Bläddra i källkod

Merge pull request #60894 from derammo/derammo_opengl3_windows

Rémi Verschelde 3 år sedan
förälder
incheckning
349aa9c884

+ 7 - 0
drivers/gles3/rasterizer_gles3.cpp

@@ -210,6 +210,9 @@ RasterizerGLES3::RasterizerGLES3() {
 #ifdef GLAD_ENABLED
 #ifdef GLAD_ENABLED
 	if (!gladLoadGL()) {
 	if (!gladLoadGL()) {
 		ERR_PRINT("Error initializing GLAD");
 		ERR_PRINT("Error initializing GLAD");
+		// FIXME this is an early return from a constructor.  Any other code using this instance will crash or the finalizer will crash, because none of
+		// the members of this instance are initialized, so this just makes debugging harder.  It should either crash here intentionally,
+		// or we need to actually test for this situation before constructing this.
 		return;
 		return;
 	}
 	}
 #endif
 #endif
@@ -285,6 +288,9 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display
 
 
 	// TODO: do we need a keep 3d linear option?
 	// TODO: do we need a keep 3d linear option?
 
 
+	// Make sure we are drawing to the right context.
+	DisplayServer::get_singleton()->gl_window_make_current(p_screen);
+
 	if (rt->external.fbo != 0) {
 	if (rt->external.fbo != 0) {
 		glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->external.fbo);
 		glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->external.fbo);
 	} else {
 	} else {
@@ -292,6 +298,7 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display
 	}
 	}
 	glReadBuffer(GL_COLOR_ATTACHMENT0);
 	glReadBuffer(GL_COLOR_ATTACHMENT0);
 	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
 	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
+	// Flip content upside down to correct for coordinates.
 	glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, 0, p_screen_rect.size.y, p_screen_rect.size.x, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST);
 	glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, 0, p_screen_rect.size.y, p_screen_rect.size.x, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST);
 }
 }
 
 

+ 1 - 1
drivers/gles3/storage/config.cpp

@@ -44,7 +44,7 @@ Config::Config() {
 	singleton = this;
 	singleton = this;
 
 
 	{
 	{
-		int max_extensions = 0;
+		GLint max_extensions = 0;
 		glGetIntegerv(GL_NUM_EXTENSIONS, &max_extensions);
 		glGetIntegerv(GL_NUM_EXTENSIONS, &max_extensions);
 		for (int i = 0; i < max_extensions; i++) {
 		for (int i = 0; i < max_extensions; i++) {
 			const GLubyte *s = glGetStringi(GL_EXTENSIONS, i);
 			const GLubyte *s = glGetStringi(GL_EXTENSIONS, i);

+ 1 - 1
drivers/gles3/storage/material_storage.cpp

@@ -2542,7 +2542,7 @@ RS::ShaderNativeSourceCode MaterialStorage::shader_get_native_source_code(RID p_
 
 
 /* MATERIAL API */
 /* MATERIAL API */
 
 
-void MaterialStorage::_material_queue_update(Material *material, bool p_uniform, bool p_texture) {
+void MaterialStorage::_material_queue_update(GLES3::Material *material, bool p_uniform, bool p_texture) {
 	material->uniform_dirty = material->uniform_dirty || p_uniform;
 	material->uniform_dirty = material->uniform_dirty || p_uniform;
 	material->texture_dirty = material->texture_dirty || p_texture;
 	material->texture_dirty = material->texture_dirty || p_texture;
 
 

+ 24 - 2
main/main.cpp

@@ -641,6 +641,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 	bool found_project = false;
 	bool found_project = false;
 #endif
 #endif
 
 
+	String default_renderer = "";
+	String renderer_hints = "";
+
 	packed_data = PackedData::get_singleton();
 	packed_data = PackedData::get_singleton();
 	if (!packed_data) {
 	if (!packed_data) {
 		packed_data = memnew(PackedData);
 		packed_data = memnew(PackedData);
@@ -1306,14 +1309,33 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 
 
 	// possibly be worth changing the default from vulkan to something lower spec,
 	// possibly be worth changing the default from vulkan to something lower spec,
 	// for the project manager, depending on how smooth the fallback is.
 	// for the project manager, depending on how smooth the fallback is.
-	GLOBAL_DEF_RST("rendering/driver/driver_name", "vulkan");
 
 
 	// this list is hard coded, which makes it more difficult to add new backends.
 	// this list is hard coded, which makes it more difficult to add new backends.
 	// can potentially be changed to more of a plugin system at a later date.
 	// can potentially be changed to more of a plugin system at a later date.
+
+	// Start with Vulkan, which will be the default if enabled.
+#ifdef VULKAN_ENABLED
+	renderer_hints = "vulkan";
+#endif
+
+	// And OpenGL3 next, or first if Vulkan is disabled.
+#ifdef GLES3_ENABLED
+	if (!renderer_hints.is_empty()) {
+		renderer_hints += ",";
+	}
+	renderer_hints += "opengl3";
+#endif
+	if (renderer_hints.is_empty()) {
+		ERR_PRINT("No rendering driver available.");
+	}
+
+	default_renderer = renderer_hints.get_slice(",", 0);
+	GLOBAL_DEF_RST("rendering/driver/driver_name", default_renderer);
+
 	ProjectSettings::get_singleton()->set_custom_property_info("rendering/driver/driver_name",
 	ProjectSettings::get_singleton()->set_custom_property_info("rendering/driver/driver_name",
 			PropertyInfo(Variant::STRING,
 			PropertyInfo(Variant::STRING,
 					"rendering/driver/driver_name",
 					"rendering/driver/driver_name",
-					PROPERTY_HINT_ENUM, "vulkan,opengl3"));
+					PROPERTY_HINT_ENUM, renderer_hints));
 
 
 	// if not set on the command line
 	// if not set on the command line
 	if (rendering_driver.is_empty()) {
 	if (rendering_driver.is_empty()) {

+ 8 - 6
platform/windows/detect.py

@@ -269,12 +269,14 @@ def configure_msvc(env, manual_msvc_config):
         "dwmapi",
         "dwmapi",
     ]
     ]
 
 
-    env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED"])
-    if not env["use_volk"]:
-        LIBS += ["vulkan"]
-
-    env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"])
-    LIBS += ["opengl32"]
+    if env["vulkan"]:
+        env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED"])
+        if not env["use_volk"]:
+            LIBS += ["vulkan"]
+
+    if env["opengl3"]:
+        env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"])
+        LIBS += ["opengl32"]
 
 
     env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS])
     env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS])
 
 

+ 74 - 14
platform/windows/display_server_windows.cpp

@@ -607,8 +607,11 @@ void DisplayServerWindows::show_window(WindowID p_id) {
 		_update_window_style(p_id);
 		_update_window_style(p_id);
 	}
 	}
 
 
-	ShowWindow(wd.hWnd, (wd.no_focus || wd.is_popup) ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window.
-	if (!wd.no_focus && !wd.is_popup) {
+	if (wd.no_focus || wd.is_popup) {
+		// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
+		ShowWindow(wd.hWnd, SW_SHOWNA);
+	} else {
+		ShowWindow(wd.hWnd, SW_SHOW);
 		SetForegroundWindow(wd.hWnd); // Slightly higher priority.
 		SetForegroundWindow(wd.hWnd); // Slightly higher priority.
 		SetFocus(wd.hWnd); // Set keyboard focus.
 		SetFocus(wd.hWnd); // Set keyboard focus.
 	}
 	}
@@ -1798,7 +1801,9 @@ void DisplayServerWindows::make_rendering_thread() {
 
 
 void DisplayServerWindows::swap_buffers() {
 void DisplayServerWindows::swap_buffers() {
 #if defined(GLES3_ENABLED)
 #if defined(GLES3_ENABLED)
-	gl_manager->swap_buffers();
+	if (gl_manager) {
+		gl_manager->swap_buffers();
+	}
 #endif
 #endif
 }
 }
 
 
@@ -1952,14 +1957,18 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
 void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
 void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
 	_THREAD_SAFE_METHOD_
 	_THREAD_SAFE_METHOD_
 #if defined(VULKAN_ENABLED)
 #if defined(VULKAN_ENABLED)
-	context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
+	if (context_vulkan) {
+		context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
+	}
 #endif
 #endif
 }
 }
 
 
 DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_window) const {
 DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_window) const {
 	_THREAD_SAFE_METHOD_
 	_THREAD_SAFE_METHOD_
 #if defined(VULKAN_ENABLED)
 #if defined(VULKAN_ENABLED)
-	return context_vulkan->get_vsync_mode(p_window);
+	if (context_vulkan) {
+		return context_vulkan->get_vsync_mode(p_window);
+	}
 #endif
 #endif
 	return DisplayServer::VSYNC_ENABLED;
 	return DisplayServer::VSYNC_ENABLED;
 }
 }
@@ -2193,8 +2202,39 @@ LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam)
 	return ::CallNextHookEx(mouse_monitor, code, wParam, lParam);
 	return ::CallNextHookEx(mouse_monitor, code, wParam, lParam);
 }
 }
 
 
-// Our default window procedure to handle processing of window-related system messages/events.
-// Also known as DefProc or DefWindowProc.
+// Handle a single window message received while CreateWindowEx is still on the stack and our data
+// structures are not fully initialized.
+LRESULT DisplayServerWindows::_handle_early_window_message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+	switch (uMsg) {
+		case WM_GETMINMAXINFO: {
+			// We receive this during CreateWindowEx and we haven't initialized the window
+			// struct, so let Windows figure out the maximized size.
+			// Silently forward to user/default.
+		} break;
+		case WM_NCCREATE: {
+			// We tunnel an unowned pointer to our window context (WindowData) through the
+			// first possible message (WM_NCCREATE) to fix up our window context collection.
+			CREATESTRUCTW *pCreate = (CREATESTRUCTW *)lParam;
+			WindowData *pWindowData = reinterpret_cast<WindowData *>(pCreate->lpCreateParams);
+
+			// Fix this up so we can recognize the remaining messages.
+			pWindowData->hWnd = hWnd;
+		} break;
+		default: {
+			// Additional messages during window creation should happen after we fixed
+			// up the data structures on WM_NCCREATE, but this might change in the future,
+			// so report an error here and then we can implement them.
+			ERR_PRINT_ONCE(vformat("Unexpected window message 0x%x received for window we cannot recognize in our collection; sequence error.", uMsg));
+		} break;
+	}
+
+	if (user_proc) {
+		return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
+	}
+	return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+}
+
+// The window procedure for our window class "Engine", used to handle processing of window-related system messages/events.
 // See: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-procedures
 // See: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-procedures
 LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
 LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
 	if (drop_events) {
 	if (drop_events) {
@@ -2208,7 +2248,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
 	WindowID window_id = INVALID_WINDOW_ID;
 	WindowID window_id = INVALID_WINDOW_ID;
 	bool window_created = false;
 	bool window_created = false;
 
 
-	// Check whether window exists.
+	// Check whether window exists
+	// FIXME this is O(n), where n is the set of currently open windows and subwindows
+	// we should have a secondary map from HWND to WindowID or even WindowData* alias, if we want to eliminate all the map lookups below
 	for (const KeyValue<WindowID, WindowData> &E : windows) {
 	for (const KeyValue<WindowID, WindowData> &E : windows) {
 		if (E.value.hWnd == hWnd) {
 		if (E.value.hWnd == hWnd) {
 			window_id = E.key;
 			window_id = E.key;
@@ -2217,10 +2259,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
 		}
 		}
 	}
 	}
 
 
-	// Window doesn't exist or creation in progress, don't handle messages yet.
+	// WARNING: we get called with events before the window is registered in our collection
+	// specifically, even the call to CreateWindowEx already calls here while still on the stack,
+	// so there is no way to store the window handle in our collection before we get here
 	if (!window_created) {
 	if (!window_created) {
-		window_id = window_id_counter;
-		ERR_FAIL_COND_V(!windows.has(window_id), 0);
+		// don't let code below operate on incompletely initialized window objects or missing window_id
+		return _handle_early_window_message(hWnd, uMsg, wParam, lParam);
 	}
 	}
 
 
 	// Process window messages.
 	// Process window messages.
@@ -3388,11 +3432,17 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
 				WindowRect.top,
 				WindowRect.top,
 				WindowRect.right - WindowRect.left,
 				WindowRect.right - WindowRect.left,
 				WindowRect.bottom - WindowRect.top,
 				WindowRect.bottom - WindowRect.top,
-				nullptr, nullptr, hInstance, nullptr);
+				nullptr,
+				nullptr,
+				hInstance,
+				// tunnel the WindowData we need to handle creation message
+				// lifetime is ensured because we are still on the stack when this is
+				// processed in the window proc
+				reinterpret_cast<void *>(&wd));
 		if (!wd.hWnd) {
 		if (!wd.hWnd) {
 			MessageBoxW(nullptr, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);
 			MessageBoxW(nullptr, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);
 			windows.erase(id);
 			windows.erase(id);
-			return INVALID_WINDOW_ID;
+			ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Windows OS window.");
 		}
 		}
 		if (p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
 		if (p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
 			wd.pre_fs_valid = true;
 			wd.pre_fs_valid = true;
@@ -3412,7 +3462,14 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
 #ifdef GLES3_ENABLED
 #ifdef GLES3_ENABLED
 		if (gl_manager) {
 		if (gl_manager) {
 			Error err = gl_manager->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top);
 			Error err = gl_manager->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top);
-			ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Failed to create an OpenGL window.");
+
+			// shut down OpenGL, to mirror behavior of Vulkan code
+			if (err != OK) {
+				memdelete(gl_manager);
+				gl_manager = nullptr;
+				windows.erase(id);
+				ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create an OpenGL window.");
+			}
 		}
 		}
 #endif
 #endif
 
 
@@ -3457,6 +3514,8 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
 		ImmReleaseContext(wd.hWnd, wd.im_himc);
 		ImmReleaseContext(wd.hWnd, wd.im_himc);
 
 
 		wd.im_position = Vector2();
 		wd.im_position = Vector2();
+
+		// FIXME this is wrong in cases where the window coordinates were changed due to full screen mode; use WindowRect
 		wd.last_pos = p_rect.position;
 		wd.last_pos = p_rect.position;
 		wd.width = p_rect.size.width;
 		wd.width = p_rect.size.width;
 		wd.height = p_rect.size.height;
 		wd.height = p_rect.size.height;
@@ -3747,6 +3806,7 @@ DisplayServerWindows::~DisplayServerWindows() {
 
 
 #ifdef GLES3_ENABLED
 #ifdef GLES3_ENABLED
 	// destroy windows .. NYI?
 	// destroy windows .. NYI?
+	// FIXME wglDeleteContext is never called
 #endif
 #endif
 
 
 	if (windows.has(MAIN_WINDOW_ID)) {
 	if (windows.has(MAIN_WINDOW_ID)) {

+ 2 - 0
platform/windows/display_server_windows.h

@@ -448,6 +448,8 @@ class DisplayServerWindows : public DisplayServer {
 	static void _dispatch_input_events(const Ref<InputEvent> &p_event);
 	static void _dispatch_input_events(const Ref<InputEvent> &p_event);
 	void _dispatch_input_event(const Ref<InputEvent> &p_event);
 	void _dispatch_input_event(const Ref<InputEvent> &p_event);
 
 
+	LRESULT _handle_early_window_message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
 public:
 public:
 	LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 	LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 	LRESULT MouseProc(int code, WPARAM wParam, LPARAM lParam);
 	LRESULT MouseProc(int code, WPARAM wParam, LPARAM lParam);

+ 66 - 56
platform/windows/gl_manager_windows.cpp

@@ -54,6 +54,18 @@
 
 
 typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *);
 typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *);
 
 
+static String format_error_message(DWORD id) {
+	LPWSTR messageBuffer = nullptr;
+	size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+			nullptr, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr);
+
+	String msg = "Error " + itos(id) + ": " + String::utf16((const char16_t *)messageBuffer, size);
+
+	LocalFree(messageBuffer);
+
+	return msg;
+}
+
 int GLManager_Windows::_find_or_create_display(GLWindow &win) {
 int GLManager_Windows::_find_or_create_display(GLWindow &win) {
 	// find display NYI, only 1 supported so far
 	// find display NYI, only 1 supported so far
 	if (_displays.size()) {
 	if (_displays.size()) {
@@ -79,7 +91,7 @@ int GLManager_Windows::_find_or_create_display(GLWindow &win) {
 	return new_display_id;
 	return new_display_id;
 }
 }
 
 
-Error GLManager_Windows::_create_context(GLWindow &win, GLDisplay &gl_display) {
+static Error _configure_pixel_format(HDC hDC) {
 	static PIXELFORMATDESCRIPTOR pfd = {
 	static PIXELFORMATDESCRIPTOR pfd = {
 		sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
 		sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
 		1,
 		1,
@@ -101,9 +113,6 @@ Error GLManager_Windows::_create_context(GLWindow &win, GLDisplay &gl_display) {
 		0, 0, 0 // Layer Masks Ignored
 		0, 0, 0 // Layer Masks Ignored
 	};
 	};
 
 
-	// alias
-	HDC hDC = win.hDC;
-
 	int pixel_format = ChoosePixelFormat(hDC, &pfd);
 	int pixel_format = ChoosePixelFormat(hDC, &pfd);
 	if (!pixel_format) // Did Windows Find A Matching Pixel Format?
 	if (!pixel_format) // Did Windows Find A Matching Pixel Format?
 	{
 	{
@@ -116,13 +125,24 @@ Error GLManager_Windows::_create_context(GLWindow &win, GLDisplay &gl_display) {
 		return ERR_CANT_CREATE; // Return FALSE
 		return ERR_CANT_CREATE; // Return FALSE
 	}
 	}
 
 
-	gl_display.hRC = wglCreateContext(hDC);
+	return OK;
+}
+
+Error GLManager_Windows::_create_context(GLWindow &win, GLDisplay &gl_display) {
+	Error err = _configure_pixel_format(win.hDC);
+	if (err != OK) {
+		return err;
+	}
+
+	gl_display.hRC = wglCreateContext(win.hDC);
 	if (!gl_display.hRC) // Are We Able To Get A Rendering Context?
 	if (!gl_display.hRC) // Are We Able To Get A Rendering Context?
 	{
 	{
 		return ERR_CANT_CREATE; // Return FALSE
 		return ERR_CANT_CREATE; // Return FALSE
 	}
 	}
 
 
-	wglMakeCurrent(hDC, gl_display.hRC);
+	if (!wglMakeCurrent(win.hDC, gl_display.hRC)) {
+		ERR_PRINT("Could not attach OpenGL context to newly created window: " + format_error_message(GetLastError()));
+	}
 
 
 	int attribs[] = {
 	int attribs[] = {
 		WGL_CONTEXT_MAJOR_VERSION_ARB, 3, //we want a 3.3 context
 		WGL_CONTEXT_MAJOR_VERSION_ARB, 3, //we want a 3.3 context
@@ -143,57 +163,61 @@ Error GLManager_Windows::_create_context(GLWindow &win, GLDisplay &gl_display) {
 		return ERR_CANT_CREATE;
 		return ERR_CANT_CREATE;
 	}
 	}
 
 
-	HGLRC new_hRC = wglCreateContextAttribsARB(hDC, 0, attribs);
+	HGLRC new_hRC = wglCreateContextAttribsARB(win.hDC, 0, attribs);
 	if (!new_hRC) {
 	if (!new_hRC) {
 		wglDeleteContext(gl_display.hRC);
 		wglDeleteContext(gl_display.hRC);
 		gl_display.hRC = 0;
 		gl_display.hRC = 0;
-		return ERR_CANT_CREATE; // Return false
+		return ERR_CANT_CREATE;
 	}
 	}
-	wglMakeCurrent(hDC, nullptr);
+
+	if (!wglMakeCurrent(win.hDC, nullptr)) {
+		ERR_PRINT("Could not detach OpenGL context from newly created window: " + format_error_message(GetLastError()));
+	}
+
 	wglDeleteContext(gl_display.hRC);
 	wglDeleteContext(gl_display.hRC);
 	gl_display.hRC = new_hRC;
 	gl_display.hRC = new_hRC;
 
 
-	if (!wglMakeCurrent(hDC, gl_display.hRC)) // Try To Activate The Rendering Context
+	if (!wglMakeCurrent(win.hDC, gl_display.hRC)) // Try To Activate The Rendering Context
 	{
 	{
+		ERR_PRINT("Could not attach OpenGL context to newly created window with replaced OpenGL context: " + format_error_message(GetLastError()));
 		wglDeleteContext(gl_display.hRC);
 		wglDeleteContext(gl_display.hRC);
 		gl_display.hRC = 0;
 		gl_display.hRC = 0;
-		return ERR_CANT_CREATE; // Return FALSE
+		return ERR_CANT_CREATE;
 	}
 	}
 
 
 	return OK;
 	return OK;
 }
 }
 
 
 Error GLManager_Windows::window_create(DisplayServer::WindowID p_window_id, HWND p_hwnd, HINSTANCE p_hinstance, int p_width, int p_height) {
 Error GLManager_Windows::window_create(DisplayServer::WindowID p_window_id, HWND p_hwnd, HINSTANCE p_hinstance, int p_width, int p_height) {
-	HDC hdc = GetDC(p_hwnd);
-	if (!hdc) {
-		return ERR_CANT_CREATE; // Return FALSE
+	HDC hDC = GetDC(p_hwnd);
+	if (!hDC) {
+		return ERR_CANT_CREATE;
 	}
 	}
 
 
-	// make sure vector is big enough...
-	// we can mirror the external vector, it is simpler
-	// to keep the IDs identical for fast lookup
-	if (p_window_id >= (int)_windows.size()) {
-		_windows.resize(p_window_id + 1);
+	// configure the HDC to use a compatible pixel format
+	Error result = _configure_pixel_format(hDC);
+	if (result != OK) {
+		return result;
 	}
 	}
 
 
-	GLWindow &win = _windows[p_window_id];
-	win.in_use = true;
-	win.window_id = p_window_id;
+	GLWindow win;
 	win.width = p_width;
 	win.width = p_width;
 	win.height = p_height;
 	win.height = p_height;
 	win.hwnd = p_hwnd;
 	win.hwnd = p_hwnd;
-	win.hDC = hdc;
+	win.hDC = hDC;
 
 
 	win.gldisplay_id = _find_or_create_display(win);
 	win.gldisplay_id = _find_or_create_display(win);
 
 
 	if (win.gldisplay_id == -1) {
 	if (win.gldisplay_id == -1) {
-		// release DC?
-		_windows.remove_at(_windows.size() - 1);
 		return FAILED;
 		return FAILED;
 	}
 	}
 
 
+	// WARNING: p_window_id is an eternally growing integer since popup windows keep coming and going
+	// and each of them has a higher id than the previous, so it must be used in a map not a vector
+	_windows[p_window_id] = win;
+
 	// make current
 	// make current
-	window_make_current(_windows.size() - 1);
+	window_make_current(p_window_id);
 
 
 	return OK;
 	return OK;
 }
 }
@@ -217,11 +241,10 @@ int GLManager_Windows::window_get_height(DisplayServer::WindowID p_window_id) {
 
 
 void GLManager_Windows::window_destroy(DisplayServer::WindowID p_window_id) {
 void GLManager_Windows::window_destroy(DisplayServer::WindowID p_window_id) {
 	GLWindow &win = get_window(p_window_id);
 	GLWindow &win = get_window(p_window_id);
-	win.in_use = false;
-
 	if (_current_window == &win) {
 	if (_current_window == &win) {
 		_current_window = nullptr;
 		_current_window = nullptr;
 	}
 	}
+	_windows.erase(p_window_id);
 }
 }
 
 
 void GLManager_Windows::release_current() {
 void GLManager_Windows::release_current() {
@@ -229,7 +252,9 @@ void GLManager_Windows::release_current() {
 		return;
 		return;
 	}
 	}
 
 
-	wglMakeCurrent(_current_window->hDC, nullptr);
+	if (!wglMakeCurrent(_current_window->hDC, nullptr)) {
+		ERR_PRINT("Could not detach OpenGL context from window marked current: " + format_error_message(GetLastError()));
+	}
 }
 }
 
 
 void GLManager_Windows::window_make_current(DisplayServer::WindowID p_window_id) {
 void GLManager_Windows::window_make_current(DisplayServer::WindowID p_window_id) {
@@ -237,10 +262,8 @@ void GLManager_Windows::window_make_current(DisplayServer::WindowID p_window_id)
 		return;
 		return;
 	}
 	}
 
 
+	// crash if our data structures are out of sync, i.e. not found
 	GLWindow &win = _windows[p_window_id];
 	GLWindow &win = _windows[p_window_id];
-	if (!win.in_use) {
-		return;
-	}
 
 
 	// noop
 	// noop
 	if (&win == _current_window) {
 	if (&win == _current_window) {
@@ -248,7 +271,9 @@ void GLManager_Windows::window_make_current(DisplayServer::WindowID p_window_id)
 	}
 	}
 
 
 	const GLDisplay &disp = get_display(win.gldisplay_id);
 	const GLDisplay &disp = get_display(win.gldisplay_id);
-	wglMakeCurrent(win.hDC, disp.hRC);
+	if (!wglMakeCurrent(win.hDC, disp.hRC)) {
+		ERR_PRINT("Could not switch OpenGL context to other window: " + format_error_message(GetLastError()));
+	}
 
 
 	_internal_set_current_window(&win);
 	_internal_set_current_window(&win);
 }
 }
@@ -257,34 +282,19 @@ void GLManager_Windows::make_current() {
 	if (!_current_window) {
 	if (!_current_window) {
 		return;
 		return;
 	}
 	}
-	if (!_current_window->in_use) {
-		WARN_PRINT("current window not in use!");
-		return;
-	}
 	const GLDisplay &disp = get_current_display();
 	const GLDisplay &disp = get_current_display();
-	wglMakeCurrent(_current_window->hDC, disp.hRC);
+	if (!wglMakeCurrent(_current_window->hDC, disp.hRC)) {
+		ERR_PRINT("Could not switch OpenGL context to window marked current: " + format_error_message(GetLastError()));
+	}
 }
 }
 
 
 void GLManager_Windows::swap_buffers() {
 void GLManager_Windows::swap_buffers() {
-	// NO NEED TO CALL SWAP BUFFERS for each window...
-	// see https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glXSwapBuffers.xml
-
-	if (!_current_window) {
-		return;
+	// on other platforms, OpenGL swaps buffers for all windows (on all displays, really?)
+	// Windows swaps buffers on a per-window basis
+	// REVISIT: this could be structurally bad, should we have "dirty" flags then?
+	for (KeyValue<DisplayServer::WindowID, GLWindow> &entry : _windows) {
+		SwapBuffers(entry.value.hDC);
 	}
 	}
-	if (!_current_window->in_use) {
-		WARN_PRINT("current window not in use!");
-		return;
-	}
-
-	//	print_line("\tswap_buffers");
-
-	// only for debugging without drawing anything
-	//	glClearColor(Math::randf(), 0, 1, 1);
-	//glClear(GL_COLOR_BUFFER_BIT);
-
-	//	const GLDisplay &disp = get_current_display();
-	SwapBuffers(_current_window->hDC);
 }
 }
 
 
 Error GLManager_Windows::initialize() {
 Error GLManager_Windows::initialize() {

+ 1 - 5
platform/windows/gl_manager_windows.h

@@ -52,10 +52,6 @@ public:
 private:
 private:
 	// any data specific to the window
 	// any data specific to the window
 	struct GLWindow {
 	struct GLWindow {
-		bool in_use = false;
-
-		// the external ID .. should match the GL window number .. unused I think
-		DisplayServer::WindowID window_id = DisplayServer::INVALID_WINDOW_ID;
 		int width = 0;
 		int width = 0;
 		int height = 0;
 		int height = 0;
 
 
@@ -71,7 +67,7 @@ private:
 		HGLRC hRC;
 		HGLRC hRC;
 	};
 	};
 
 
-	LocalVector<GLWindow> _windows;
+	Map<DisplayServer::WindowID, GLWindow> _windows;
 	LocalVector<GLDisplay> _displays;
 	LocalVector<GLDisplay> _displays;
 
 
 	GLWindow *_current_window = nullptr;
 	GLWindow *_current_window = nullptr;

+ 29 - 0
platform/windows/os_windows.cpp

@@ -129,9 +129,34 @@ void OS_Windows::initialize_debugging() {
 	SetConsoleCtrlHandler(HandlerRoutine, TRUE);
 	SetConsoleCtrlHandler(HandlerRoutine, TRUE);
 }
 }
 
 
+#ifdef WINDOWS_DEBUG_OUTPUT_ENABLED
+static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) {
+	String err_str;
+	if (p_errorexp && p_errorexp[0]) {
+		err_str = String::utf8(p_errorexp);
+	} else {
+		err_str = String::utf8(p_file) + ":" + itos(p_line) + " - " + String::utf8(p_error);
+	}
+
+	if (p_editor_notify) {
+		err_str += " (User)\n";
+	} else {
+		err_str += "\n";
+	}
+
+	OutputDebugStringW((LPCWSTR)err_str.utf16().ptr());
+}
+#endif
+
 void OS_Windows::initialize() {
 void OS_Windows::initialize() {
 	crash_handler.initialize();
 	crash_handler.initialize();
 
 
+#ifdef WINDOWS_DEBUG_OUTPUT_ENABLED
+	error_handlers.errfunc = _error_handler;
+	error_handlers.userdata = this;
+	add_error_handler(&error_handlers);
+#endif
+
 #ifndef WINDOWS_SUBSYSTEM_CONSOLE
 #ifndef WINDOWS_SUBSYSTEM_CONSOLE
 	RedirectIOToConsole();
 	RedirectIOToConsole();
 #endif
 #endif
@@ -194,6 +219,10 @@ void OS_Windows::finalize_core() {
 
 
 	memdelete(process_map);
 	memdelete(process_map);
 	NetSocketPosix::cleanup();
 	NetSocketPosix::cleanup();
+
+#ifdef WINDOWS_DEBUG_OUTPUT_ENABLED
+	remove_error_handler(&error_handlers);
+#endif
 }
 }
 
 
 Error OS_Windows::get_entropy(uint8_t *r_buffer, int p_bytes) {
 Error OS_Windows::get_entropy(uint8_t *r_buffer, int p_bytes) {

+ 9 - 0
platform/windows/os_windows.h

@@ -57,6 +57,11 @@
 #include <windows.h>
 #include <windows.h>
 #include <windowsx.h>
 #include <windowsx.h>
 
 
+#ifdef DEBUG_ENABLED
+// forward error messages to OutputDebugString
+#define WINDOWS_DEBUG_OUTPUT_ENABLED
+#endif
+
 class JoypadWindows;
 class JoypadWindows;
 class OS_Windows : public OS {
 class OS_Windows : public OS {
 #ifdef STDOUT_FILE
 #ifdef STDOUT_FILE
@@ -81,6 +86,10 @@ class OS_Windows : public OS {
 
 
 	CrashHandler crash_handler;
 	CrashHandler crash_handler;
 
 
+#ifdef WINDOWS_DEBUG_OUTPUT_ENABLED
+	ErrorHandlerList error_handlers;
+#endif
+
 	bool force_quit;
 	bool force_quit;
 	HWND main_window;
 	HWND main_window;
 
 

+ 4 - 0
platform/windows/vulkan_context_win.cpp

@@ -28,6 +28,8 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 /*************************************************************************/
 
 
+#if defined(WINDOWS_ENABLED) && defined(VULKAN_ENABLED)
+
 #include "vulkan_context_win.h"
 #include "vulkan_context_win.h"
 #ifdef USE_VOLK
 #ifdef USE_VOLK
 #include <volk.h>
 #include <volk.h>
@@ -57,3 +59,5 @@ VulkanContextWindows::VulkanContextWindows() {
 
 
 VulkanContextWindows::~VulkanContextWindows() {
 VulkanContextWindows::~VulkanContextWindows() {
 }
 }
+
+#endif

+ 5 - 2
servers/rendering/renderer_scene_cull.cpp

@@ -609,13 +609,16 @@ void RendererSceneCull::instance_set_base(RID p_instance, RID p_base) {
 	if (p_base.is_valid()) {
 	if (p_base.is_valid()) {
 		instance->base_type = RSG::storage->get_base_type(p_base);
 		instance->base_type = RSG::storage->get_base_type(p_base);
 
 
+		// fix up a specific malfunctioning case before the switch, so it can be handled
 		if (instance->base_type == RS::INSTANCE_NONE && RendererSceneOcclusionCull::get_singleton()->is_occluder(p_base)) {
 		if (instance->base_type == RS::INSTANCE_NONE && RendererSceneOcclusionCull::get_singleton()->is_occluder(p_base)) {
 			instance->base_type = RS::INSTANCE_OCCLUDER;
 			instance->base_type = RS::INSTANCE_OCCLUDER;
 		}
 		}
 
 
-		ERR_FAIL_COND(instance->base_type == RS::INSTANCE_NONE);
-
 		switch (instance->base_type) {
 		switch (instance->base_type) {
+			case RS::INSTANCE_NONE: {
+				ERR_PRINT_ONCE("unimplemented base type encountered in renderer scene cull");
+				return;
+			}
 			case RS::INSTANCE_LIGHT: {
 			case RS::INSTANCE_LIGHT: {
 				InstanceLightData *light = memnew(InstanceLightData);
 				InstanceLightData *light = memnew(InstanceLightData);