From 57d5fb356cf373b6c7daa562fbd58cec2b48e8d8 Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Tue, 16 Jul 2024 03:00:23 +0800 Subject: [PATCH 01/14] Initial test of presenting OpenGL with DXGI swap chain --- drivers/gles3/rasterizer_gles3.cpp | 21 +- drivers/gles3/rasterizer_gles3.h | 10 + platform/windows/display_server_windows.cpp | 7 + .../windows/gl_manager_windows_native.cpp | 705 +++++++++++++++++- platform/windows/gl_manager_windows_native.h | 11 +- 5 files changed, 744 insertions(+), 10 deletions(-) diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 37e7256d7642..39aff2fc5e42 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -82,6 +82,10 @@ #define strcpy strcpy_s #endif +#ifdef WINDOWS_ENABLED +bool RasterizerGLES3::screen_flipped_y = false; +#endif + bool RasterizerGLES3::gles_over_gl = true; void RasterizerGLES3::begin_frame(double frame_step) { @@ -380,6 +384,12 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display flip_y = false; } +#ifdef WINDOWS_ENABLED + if (screen_flipped_y) { + flip_y = !flip_y; + } +#endif + GLuint read_fbo = 0; glGenFramebuffers(1, &read_fbo); glBindFramebuffer(GL_READ_FRAMEBUFFER, read_fbo); @@ -476,9 +486,14 @@ void RasterizerGLES3::set_boot_image(const Ref &p_image, const Color &p_c screenrect.position += ((Size2(win_size.width, win_size.height) - screenrect.size) / 2.0).floor(); } - // Flip Y. - screenrect.position.y = win_size.y - screenrect.position.y; - screenrect.size.y = -screenrect.size.y; +#ifdef WINDOWS_ENABLED + if (!screen_flipped_y) +#endif + { + // Flip Y. + screenrect.position.y = win_size.y - screenrect.position.y; + screenrect.size.y = -screenrect.size.y; + } // Normalize texture coordinates to window size. screenrect.position /= win_size; diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index 80a4a792bb2d..0cf656e490fe 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -58,6 +58,10 @@ class RasterizerGLES3 : public RendererCompositor { double time_total = 0.0; bool flip_xy_workaround = false; +#ifdef WINDOWS_ENABLED + static bool screen_flipped_y; +#endif + static bool gles_over_gl; protected: @@ -117,6 +121,12 @@ class RasterizerGLES3 : public RendererCompositor { low_end = true; } +#ifdef WINDOWS_ENABLED + static void set_screen_flipped_y(bool p_flipped) { + screen_flipped_y = p_flipped; + } +#endif + _ALWAYS_INLINE_ uint64_t get_frame_number() const { return frame; } _ALWAYS_INLINE_ double get_frame_delta_time() const { return delta; } _ALWAYS_INLINE_ double get_total_time() const { return time_total; } diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 020ef0c31b31..97d9d7e10874 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -5477,6 +5477,11 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, windows.erase(id); ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create an OpenGL window."); } + if (id == MAIN_WINDOW_ID && gl_manager_native->is_using_dxgi_swap_chain()) { + // When presenting with DXGI the screen FBO is "upside down" + // w.r.t. OpenGL. + RasterizerGLES3::set_screen_flipped_y(true); + } window_set_vsync_mode(p_vsync_mode, id); } @@ -6025,6 +6030,8 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win if (rendering_driver == "opengl3") { gl_manager_native = memnew(GLManagerNative_Windows); + gl_manager_native->set_prefer_dxgi_swap_chain(true); + if (gl_manager_native->initialize() != OK) { memdelete(gl_manager_native); gl_manager_native = nullptr; diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp index 8590c46d12bd..580c8c8df64b 100644 --- a/platform/windows/gl_manager_windows_native.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -41,6 +41,26 @@ #include #include +#define OPENGL_ON_DXGI_ENABLED +// #define OPENGL_DXGI_USE_DEPTH_BUFFER +// #define OPENGL_DXGI_USE_FLIP_MODEL + +#ifdef OPENGL_ON_DXGI_ENABLED + +#include "drivers/gles3/storage/texture_storage.h" +#include "platform_gl.h" + +#include +#ifdef OPENGL_DXGI_USE_FLIP_MODEL +#include +#endif + +#include + +using Microsoft::WRL::ComPtr; + +#endif // OPENGL_ON_DXGI_ENABLED + #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 #define WGL_CONTEXT_FLAGS_ARB 0x2094 @@ -61,6 +81,59 @@ typedef BOOL(APIENTRY *PFNWGLMAKECURRENT)(HDC, HGLRC); typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *); typedef void *(APIENTRY *PFNWGLGETPROCADDRESS)(LPCSTR); +#ifdef OPENGL_ON_DXGI_ENABLED + +typedef const char *(APIENTRY *PFNWGLGETEXTENSIONSSTRINGARB)(HDC); + +#define WGL_ACCESS_READ_ONLY_NV 0x0000 +#define WGL_ACCESS_READ_WRITE_NV 0x0001 +#define WGL_ACCESS_WRITE_DISCARD_NV 0x0002 + +typedef BOOL(APIENTRY *PFNWGLDXSETRESOURCESHAREHANDLENVPROC)(void *dxObject, HANDLE shareHandle); +typedef HANDLE(APIENTRY *PFNWGLDXOPENDEVICENVPROC)(void *dxDevice); +typedef BOOL(APIENTRY *PFNWGLDXCLOSEDEVICENVPROC)(HANDLE hDevice); +typedef HANDLE(APIENTRY *PFNWGLDXREGISTEROBJECTNVPROC)(HANDLE hDevice, void *dxObject, GLuint name, GLenum type, GLenum access); +typedef BOOL(APIENTRY *PFNWGLDXUNREGISTEROBJECTNVPROC)(HANDLE hDevice, HANDLE hObject); +typedef BOOL(APIENTRY *PFNWGLDXOBJECTACCESSNVPROC)(HANDLE hObject, GLenum access); +typedef BOOL(APIENTRY *PFNWGLDXLOCKOBJECTSNVPROC)(HANDLE hDevice, GLint count, HANDLE *hObjects); +typedef BOOL(APIENTRY *PFNWGLDXUNLOCKOBJECTSNVPROC)(HANDLE hDevice, GLint count, HANDLE *hObjects); + +PFNWGLDXSETRESOURCESHAREHANDLENVPROC gd_wglDXSetResourceShareHandleNV; +PFNWGLDXOPENDEVICENVPROC gd_wglDXOpenDeviceNV; +PFNWGLDXCLOSEDEVICENVPROC gd_wglDXCloseDeviceNV; +PFNWGLDXREGISTEROBJECTNVPROC gd_wglDXRegisterObjectNV; +PFNWGLDXUNREGISTEROBJECTNVPROC gd_wglDXUnregisterObjectNV; +PFNWGLDXOBJECTACCESSNVPROC gd_wglDXObjectAccessNV; +PFNWGLDXLOCKOBJECTSNVPROC gd_wglDXLockObjectsNV; +PFNWGLDXUNLOCKOBJECTSNVPROC gd_wglDXUnlockObjectsNV; + +static bool load_nv_dx_interop(PFNWGLGETPROCADDRESS gd_wglGetProcAddress) { + gd_wglDXSetResourceShareHandleNV = (PFNWGLDXSETRESOURCESHAREHANDLENVPROC)gd_wglGetProcAddress("wglDXSetResourceShareHandleNV"); + gd_wglDXOpenDeviceNV = (PFNWGLDXOPENDEVICENVPROC)gd_wglGetProcAddress("wglDXOpenDeviceNV"); + gd_wglDXCloseDeviceNV = (PFNWGLDXCLOSEDEVICENVPROC)gd_wglGetProcAddress("wglDXCloseDeviceNV"); + gd_wglDXRegisterObjectNV = (PFNWGLDXREGISTEROBJECTNVPROC)gd_wglGetProcAddress("wglDXRegisterObjectNV"); + gd_wglDXUnregisterObjectNV = (PFNWGLDXUNREGISTEROBJECTNVPROC)gd_wglGetProcAddress("wglDXUnregisterObjectNV"); + gd_wglDXObjectAccessNV = (PFNWGLDXOBJECTACCESSNVPROC)gd_wglGetProcAddress("wglDXObjectAccessNV"); + gd_wglDXLockObjectsNV = (PFNWGLDXLOCKOBJECTSNVPROC)gd_wglGetProcAddress("wglDXLockObjectsNV"); + gd_wglDXUnlockObjectsNV = (PFNWGLDXUNLOCKOBJECTSNVPROC)gd_wglGetProcAddress("wglDXUnlockObjectsNV"); + + return gd_wglDXSetResourceShareHandleNV && + gd_wglDXOpenDeviceNV && + gd_wglDXCloseDeviceNV && + gd_wglDXRegisterObjectNV && + gd_wglDXUnregisterObjectNV && + gd_wglDXObjectAccessNV && + gd_wglDXLockObjectsNV && + gd_wglDXUnlockObjectsNV; +} + +typedef decltype(&D3D11CreateDeviceAndSwapChain) PFND3D11CREATEDEVICEANDSWAPCHAINPROC; + +static HMODULE module_d3d11 = nullptr; +PFND3D11CREATEDEVICEANDSWAPCHAINPROC fptr_D3D11CreateDeviceAndSwapChain = nullptr; + +#endif // OPENGL_ON_DXGI_ENABLED + 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, @@ -287,6 +360,128 @@ void GLManagerNative_Windows::_nvapi_setup_profile() { NvAPI_DRS_DestroySession(session_handle); } +#ifdef OPENGL_ON_DXGI_ENABLED +class GLManagerNative_Windows::DxgiSwapChain { + ComPtr device; + ComPtr device_context; + ComPtr swap_chain; + +#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER + ComPtr depth_texture; + ComPtr depth_stencil_view; +#endif + + ComPtr color_buffer; + ComPtr render_target_view; + +#ifdef OPENGL_DXGI_USE_FLIP_MODEL + HANDLE frame_latency_waitable_obj{}; +#endif + + HANDLE gldx_device{}; +#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER + HANDLE gldx_depth_texture{}; +#endif + HANDLE gldx_render_target_view{}; + + GLuint gl_fbo{}; + GLuint gl_depth_stencil_tex{}; + GLuint gl_render_target_view_tex{}; + + DxgiSwapChain() = default; + ~DxgiSwapChain() = default; + + template + friend void memdelete(T *); + +public: + static DxgiSwapChain *create(HWND p_hwnd, int p_width, int p_height); + void destroy(); + + void make_current(); + void release_current(); + + void present(bool p_use_vsync); + void resize_swap_chain(int p_width, int p_height); + void set_use_vsync(bool p_use); + +private: +#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER + bool setup_depth_buffer(int p_width, int p_height); + void release_depth_buffer(); +#endif + + bool setup_render_target(); + void release_render_target(bool p_need_unlock); + + void lock_for_opengl(); + void unlock_from_opengl(); +}; + +enum class DxgiStatus { + UNINITIALIZED, + DISABLED, + LOADED_UNTESTED, + LOADED_USABLE, +}; + +static DxgiStatus dxgi_status = DxgiStatus::UNINITIALIZED; + +static bool load_dxgi_swap_chain_functions(PFNWGLGETPROCADDRESS gd_wglGetProcAddress, HDC hDC) { + PFNWGLGETEXTENSIONSSTRINGARB gd_wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARB)gd_wglGetProcAddress("wglGetExtensionsStringARB"); + if (!gd_wglGetExtensionsStringARB) { + return false; + } + + const char *extensions = gd_wglGetExtensionsStringARB(hDC); + if (!extensions || !strstr(extensions, "WGL_NV_DX_interop2")) { + print_verbose("GLManagerNative_Windows: Extension WGL_NV_DX_interop2 not available."); + return false; + } + + if (!load_nv_dx_interop(gd_wglGetProcAddress)) { + print_verbose("GLManagerNative_Windows: Failed to load WGL_NV_DX_interop functions."); + return false; + } + + print_verbose("GLManagerNative_Windows: Loaded WGL_NV_DX_interop functions."); + + module_d3d11 = LoadLibraryW(L"d3d11.dll"); + if (!module_d3d11) { + print_verbose("GLManagerNative_Windows: Failed to load D3D11."); + return false; + } + + fptr_D3D11CreateDeviceAndSwapChain = (PFND3D11CREATEDEVICEANDSWAPCHAINPROC)(void *)GetProcAddress(module_d3d11, "D3D11CreateDeviceAndSwapChain"); + if (!fptr_D3D11CreateDeviceAndSwapChain) { + print_verbose("GLManagerNative_Windows: Failed to load D3D11 functions.") + FreeLibrary(module_d3d11); + return false; + } + + print_verbose("GLManagerNative_Windows: Loaded D3D11 functions."); + return true; +} +#endif // OPENGL_ON_DXGI_ENABLED + +void GLManagerNative_Windows::set_prefer_dxgi_swap_chain(bool p_prefer) { + prefer_dxgi = p_prefer; +} + +bool GLManagerNative_Windows::is_using_dxgi_swap_chain() { +#ifdef OPENGL_ON_DXGI_ENABLED +#ifdef DEBUG_ENABLED + if (dxgi_status == DxgiStatus::UNINITIALIZED) { + WARN_PRINT("Do not attempt to check is_using_dxgi_swap_chain before first window!"); + } +#endif + + return dxgi_status == DxgiStatus::LOADED_USABLE; +#else + return false; +#endif +} + int GLManagerNative_Windows::_find_or_create_display(GLWindow &win) { // find display NYI, only 1 supported so far if (_displays.size()) { @@ -427,6 +622,20 @@ Error GLManagerNative_Windows::_create_context(GLWindow &win, GLDisplay &gl_disp wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)gd_wglGetProcAddress("wglSwapIntervalEXT"); } +#ifdef OPENGL_ON_DXGI_ENABLED + if (dxgi_status == DxgiStatus::UNINITIALIZED) { + if (prefer_dxgi) { + if (load_dxgi_swap_chain_functions(gd_wglGetProcAddress, win.hDC)) { + dxgi_status = DxgiStatus::LOADED_UNTESTED; + } else { + dxgi_status = DxgiStatus::DISABLED; + } + } else { + dxgi_status = DxgiStatus::DISABLED; + } + } +#endif // OPENGL_ON_DXGI_ENABLED + return OK; } @@ -452,6 +661,27 @@ Error GLManagerNative_Windows::window_create(DisplayServer::WindowID p_window_id return FAILED; } +#ifdef OPENGL_ON_DXGI_ENABLED + if (dxgi_status == DxgiStatus::LOADED_UNTESTED || dxgi_status == DxgiStatus::LOADED_USABLE) { + win.dxgi = DxgiSwapChain::create(win.hwnd, p_width, p_height); + if (win.dxgi) { + if (dxgi_status == DxgiStatus::LOADED_UNTESTED) { + dxgi_status = DxgiStatus::LOADED_USABLE; + print_verbose("GLManagerNative_Windows: Presenting with D3D11 DXGI swap chain.") + } + } else { + if (dxgi_status == DxgiStatus::LOADED_UNTESTED) { + // If it failed during the first time creating a window, + // just fall back to regular OpenGL SwapBuffers. + dxgi_status = DxgiStatus::DISABLED; + WARN_PRINT("GLManagerNative_Windows: Failed to initialize D3D11 DXGI swap chain, reverting to regular OpenGL."); + } else { + return ERR_CANT_CREATE; + } + } + } +#endif // OPENGL_ON_DXGI_ENABLED + // 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; @@ -464,17 +694,40 @@ Error GLManagerNative_Windows::window_create(DisplayServer::WindowID p_window_id void GLManagerNative_Windows::window_destroy(DisplayServer::WindowID p_window_id) { GLWindow &win = get_window(p_window_id); + +#ifdef OPENGL_ON_DXGI_ENABLED + if (win.dxgi) { + win.dxgi->destroy(); + win.dxgi = nullptr; + } +#endif // OPENGL_ON_DXGI_ENABLED + if (_current_window == &win) { _current_window = nullptr; } _windows.erase(p_window_id); } +void GLManagerNative_Windows::window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) { +#ifdef OPENGL_ON_DXGI_ENABLED + GLWindow &win = get_window(p_window_id); + if (win.dxgi) { + win.dxgi->resize_swap_chain(p_width, p_height); + } +#endif // OPENGL_ON_DXGI_ENABLED +} + void GLManagerNative_Windows::release_current() { if (!_current_window) { return; } +#ifdef OPENGL_ON_DXGI_ENABLED + if (_current_window->dxgi) { + _current_window->dxgi->release_current(); + } +#endif // OPENGL_ON_DXGI_ENABLED + if (!gd_wglMakeCurrent(_current_window->hDC, nullptr)) { ERR_PRINT("Could not detach OpenGL context from window marked current: " + format_error_message(GetLastError())); } @@ -500,11 +753,26 @@ void GLManagerNative_Windows::window_make_current(DisplayServer::WindowID p_wind ERR_PRINT("Could not switch OpenGL context to other window: " + format_error_message(GetLastError())); } +#ifdef OPENGL_ON_DXGI_ENABLED + if (win.dxgi) { + win.dxgi->make_current(); + } +#endif // OPENGL_ON_DXGI_ENABLED + _current_window = &win; } void GLManagerNative_Windows::swap_buffers() { - SwapBuffers(_current_window->hDC); +#ifdef OPENGL_ON_DXGI_ENABLED + GLWindow &win = *_current_window; + if (win.dxgi) { + win.dxgi->present(win.use_vsync); + } else { +#else + { +#endif // OPENGL_ON_DXGI_ENABLED + SwapBuffers(_current_window->hDC); + } } Error GLManagerNative_Windows::initialize() { @@ -519,14 +787,23 @@ void GLManagerNative_Windows::set_use_vsync(DisplayServer::WindowID p_window_id, window_make_current(p_window_id); } - if (wglSwapIntervalEXT) { +#ifdef OPENGL_ON_DXGI_ENABLED + if (win.dxgi) { win.use_vsync = p_use; - - if (!wglSwapIntervalEXT(p_use ? 1 : 0)) { + win.dxgi->set_use_vsync(p_use); + } else { +#else + { +#endif // OPENGL_ON_DXGI_ENABLED + if (wglSwapIntervalEXT) { + win.use_vsync = p_use; + + if (!wglSwapIntervalEXT(p_use ? 1 : 0)) { + WARN_PRINT_ONCE("Could not set V-Sync mode, as changing V-Sync mode is not supported by the graphics driver."); + } + } else { WARN_PRINT_ONCE("Could not set V-Sync mode, as changing V-Sync mode is not supported by the graphics driver."); } - } else { - WARN_PRINT_ONCE("Could not set V-Sync mode, as changing V-Sync mode is not supported by the graphics driver."); } } @@ -554,4 +831,420 @@ GLManagerNative_Windows::~GLManagerNative_Windows() { release_current(); } +#ifdef OPENGL_ON_DXGI_ENABLED + +GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain::create(HWND p_hwnd, int p_width, int p_height) { + ComPtr device; + ComPtr device_context; + ComPtr swap_chain; + + DXGI_SWAP_CHAIN_DESC swap_chain_desc = {}; + swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swap_chain_desc.SampleDesc.Count = 1; +#ifdef OPENGL_DXGI_USE_FLIP_MODEL + swap_chain_desc.BufferCount = 3; +#else + swap_chain_desc.BufferCount = 2; +#endif + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.OutputWindow = p_hwnd; + swap_chain_desc.Windowed = TRUE; +#ifdef OPENGL_DXGI_USE_FLIP_MODEL + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT | DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; +#else + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; +#endif + + UINT flags = 0; + if (OS::get_singleton()->is_stdout_verbose()) { + flags |= D3D11_CREATE_DEVICE_DEBUG; + } + + // TODO: Change to use IDXGIFactory2::CreateSwapChainForHwnd so that we + // can specify DXGI_SCALING_NONE + HRESULT hr = fptr_D3D11CreateDeviceAndSwapChain(nullptr, // Adapter + D3D_DRIVER_TYPE_HARDWARE, // DriverType + nullptr, // Software + flags, // Flags + nullptr, // pFeatureLevels + 0, // FeatureLevels + D3D11_SDK_VERSION, // SDKVersion + &swap_chain_desc, // pSwapChainDesc + &swap_chain, // ppSwapChain + &device, // ppDevice + nullptr, // pFeatureLevel + &device_context); // ppImmediateContext + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("D3D11CreateDeviceAndSwapChain failed, HRESULT: 0x%08X", (unsigned)hr)); + return nullptr; + } + + { + ComPtr dxgi_device; + hr = device.As(&dxgi_device); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("Failed to get IDXGIDevice, HRESULT: 0x%08X", (unsigned)hr)); + } else { + ComPtr dxgi_adapter; + hr = dxgi_device->GetAdapter(&dxgi_adapter); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("Failed to get IDXGIAdapter, HRESULT: 0x%08X", (unsigned)hr)); + } else { + ComPtr dxgi_factory; + hr = dxgi_adapter->GetParent(__uuidof(IDXGIFactory), &dxgi_factory); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("Failed to get IDXGIFactory, HRESULT: 0x%08X", (unsigned)hr)); + } else { + hr = dxgi_factory->MakeWindowAssociation(p_hwnd, DXGI_MWA_NO_ALT_ENTER); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("Failed to get IDXGIFactory, HRESULT: 0x%08X", (unsigned)hr)); + } + } + } + } + } + + HANDLE gldx_device = gd_wglDXOpenDeviceNV(device.Get()); + if (!gldx_device) { + ERR_PRINT(vformat("Failed to connect D3D11 swap chain to WGL for interop. Error: %s", format_error_message(GetLastError()))); + gd_wglDXCloseDeviceNV(gldx_device); + return nullptr; + } + +#ifdef OPENGL_DXGI_USE_FLIP_MODEL + HANDLE frame_latency_waitable_obj{}; + { + ComPtr swap_chain_2; + hr = swap_chain.As(&swap_chain_2); + if (SUCCEEDED(hr)) { + frame_latency_waitable_obj = swap_chain_2->GetFrameLatencyWaitableObject(); + } else { + ERR_PRINT(vformat("Failed to get IDXGISwapChain2, HRESULT: 0x%08X", (unsigned)hr)); + } + } +#endif + + // HACK: We need OpenGL functions _now_ but RasterizerGLES3 might not + // have been initialized yet. + gladLoaderLoadGL(); + + // Generate the FBO we use for the window. + GLuint gl_fbo; + glGenFramebuffers(1, &gl_fbo); + + // Generate texture names for render target and depth buffers. + GLuint textures[2] = {}; + glGenTextures(2, textures); + GLuint gl_depth_stencil_tex = textures[0]; + GLuint gl_render_target_view_tex = textures[1]; + + GLManagerNative_Windows::DxgiSwapChain *dxgi = memnew(GLManagerNative_Windows::DxgiSwapChain); + dxgi->device = std::move(device); + dxgi->device_context = std::move(device_context); + dxgi->swap_chain = std::move(swap_chain); +#ifdef OPENGL_DXGI_USE_FLIP_MODEL + dxgi->frame_latency_waitable_obj = frame_latency_waitable_obj; +#endif + dxgi->gldx_device = gldx_device; + dxgi->gl_fbo = gl_fbo; + dxgi->gl_depth_stencil_tex = gl_depth_stencil_tex; + dxgi->gl_render_target_view_tex = gl_render_target_view_tex; + + GLES3::TextureStorage::system_fbo = gl_fbo; + +#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER + if (!dxgi->setup_depth_buffer(p_width, p_height)) { + GLES3::TextureStorage::system_fbo = 0; + memdelete(dxgi); + return nullptr; + } +#endif + + if (!dxgi->setup_render_target()) { + GLES3::TextureStorage::system_fbo = 0; +#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER + dxgi->release_depth_buffer(); +#endif + memdelete(dxgi); + return nullptr; + } + +#ifdef OPENGL_DXGI_USE_FLIP_MODEL + ComPtr swap_chain_2; + hr = dxgi->swap_chain.As(&swap_chain_2); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("Failed to get IDXGISwapChain2, HRESULT: 0x%08X", (unsigned)hr)); + } else { + // TODO: ??? + swap_chain_2->SetMaximumFrameLatency(1); + } +#else + ComPtr device1; + hr = dxgi->device.As(&device1); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("Failed to get IDXGIDevice1, HRESULT: 0x%08X", (unsigned)hr)); + } else { + // TODO: ??? + device1->SetMaximumFrameLatency(1); + } +#endif + + dxgi->lock_for_opengl(); + + return dxgi; +} + +void GLManagerNative_Windows::DxgiSwapChain::destroy() { + if (GLES3::TextureStorage::system_fbo == gl_fbo) { + GLES3::TextureStorage::system_fbo = 0; + } + + release_render_target(true); +#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER + release_depth_buffer(); +#endif + + // FIXME: Is this safe to do? Could our OpenGL context not be current? + glDeleteFramebuffers(1, &gl_fbo); + GLuint textures[2] = { gl_depth_stencil_tex, gl_render_target_view_tex }; + glDeleteTextures(2, textures); + + gd_wglDXCloseDeviceNV(gldx_device); + + swap_chain.Reset(); + device_context.Reset(); + device.Reset(); + + memdelete(this); +} + +void GLManagerNative_Windows::DxgiSwapChain::make_current() { + GLES3::TextureStorage::system_fbo = gl_fbo; +} + +void GLManagerNative_Windows::DxgiSwapChain::release_current() { + if (GLES3::TextureStorage::system_fbo != gl_fbo) { +#ifdef DEBUG_ENABLED + WARN_PRINT("Trying to release D3D11 target but system_fbo has changed!"); +#endif + return; + } + GLES3::TextureStorage::system_fbo = 0; +} + +#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER +bool GLManagerNative_Windows::DxgiSwapChain::setup_depth_buffer(int p_width, int p_height) { + // Godot uses 24-bit depth buffer? + ComPtr depth_texture_new; + CD3D11_TEXTURE2D_DESC depth_buffer_desc(DXGI_FORMAT_R24G8_TYPELESS, p_width, p_height, 1, 1, D3D11_BIND_DEPTH_STENCIL); + HRESULT hr = device->CreateTexture2D( + &depth_buffer_desc, + nullptr, + &depth_texture_new); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("CreateTexture2D failed, HRESULT: 0x%08X", (unsigned)hr)); + return false; + } + + D3D11_DEPTH_STENCIL_VIEW_DESC depth_stencil_view_desc{}; + depth_stencil_view_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + depth_stencil_view_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; + ComPtr depth_stencil_view_new; + hr = device->CreateDepthStencilView( + depth_texture_new.Get(), + &depth_stencil_view_desc, + &depth_stencil_view_new); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("CreateDepthStencilView failed, HRESULT: 0x%08X", (unsigned)hr)); + return false; + } + + HANDLE gldx_depth_texture_new = gd_wglDXRegisterObjectNV( + gldx_device, + depth_texture_new.Get(), + gl_depth_stencil_tex, + GL_TEXTURE_2D, + WGL_ACCESS_READ_WRITE_NV); + if (!gldx_depth_texture_new) { + ERR_PRINT(vformat("Failed to connect D3D11 depth texture to WGL for interop. Error: %s", format_error_message(GetLastError()))); + return false; + } + + depth_texture = std::move(depth_texture_new); + depth_stencil_view = std::move(depth_stencil_view_new); + gldx_depth_texture = gldx_depth_texture_new; + + return true; +} + +void GLManagerNative_Windows::DxgiSwapChain::release_depth_buffer() { + gd_wglDXUnregisterObjectNV(gldx_device, gldx_depth_texture); + + gldx_depth_texture = nullptr; + depth_stencil_view.Reset(); + depth_texture.Reset(); +} +#endif // OPENGL_DXGI_USE_DEPTH_BUFFER + +bool GLManagerNative_Windows::DxgiSwapChain::setup_render_target() { + // Get the current back buffer from the swap chain. + ComPtr color_buffer_new; + HRESULT hr = swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), &color_buffer_new); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("GetBuffer failed, HRESULT: 0x%08X", (unsigned)hr)); + return false; + } + + // TODO: Do we need to chedk OS::get_singleton()->is_layered_allowed() here? + CD3D11_RENDER_TARGET_VIEW_DESC render_target_view_desc(D3D11_RTV_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM); + ComPtr render_target_view_new; + hr = device->CreateRenderTargetView( + color_buffer_new.Get(), + &render_target_view_desc, + &render_target_view_new); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("CreateRenderTargetView failed, HRESULT: 0x%08X", (unsigned)hr)); + return false; + } + + // Register for interop. + HANDLE gldx_render_target_view_new = gd_wglDXRegisterObjectNV(gldx_device, + color_buffer_new.Get(), + gl_render_target_view_tex, + GL_TEXTURE_2D, + WGL_ACCESS_READ_WRITE_NV); + if (!gldx_render_target_view_new) { + ERR_PRINT(vformat("Failed to connect D3D11 render target to WGL for interop. Error: %s", format_error_message(GetLastError()))); + return false; + } + + // Attach back buffer and depth buffer to the render target. + device_context->OMSetRenderTargets(1, + render_target_view_new.GetAddressOf(), +#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER + depth_stencil_view.Get() +#else + nullptr +#endif + ); + + // TODO: Is it okay if we don't clear at all? + // float clear_color[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + // device_context->ClearRenderTargetView(render_target_view_new.Get(), clear_color); + + color_buffer = std::move(color_buffer_new); + render_target_view = std::move(render_target_view_new); + gldx_render_target_view = gldx_render_target_view_new; + + return true; +} + +void GLManagerNative_Windows::DxgiSwapChain::release_render_target(bool p_need_unlock) { + if (p_need_unlock) { + unlock_from_opengl(); + } + + // Release the back buffer. + gd_wglDXUnregisterObjectNV(gldx_device, gldx_render_target_view); + + gldx_render_target_view = nullptr; + render_target_view.Reset(); + color_buffer.Reset(); +} + +void GLManagerNative_Windows::DxgiSwapChain::lock_for_opengl() { + // Lock the buffers for OpenGL access. +#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER + void *handles[] = { gldx_depth_texture, gldx_render_target_view }; + gd_wglDXLockObjectsNV(gldx_device, 2, handles); +#else + gd_wglDXLockObjectsNV(gldx_device, 1, &gldx_render_target_view); +#endif + + // Attach color and depth buffers to FBO + glBindFramebuffer(GL_FRAMEBUFFER, gl_fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl_render_target_view_tex, 0); +#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, gl_depth_stencil_tex, 0); +#endif + if (GLES3::TextureStorage::system_fbo != gl_fbo) { + glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); + } +} + +void GLManagerNative_Windows::DxgiSwapChain::unlock_from_opengl() { + // Detach color and depth buffers from FBO + glBindFramebuffer(GL_FRAMEBUFFER, gl_fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); +#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0); +#endif + if (GLES3::TextureStorage::system_fbo != gl_fbo) { + glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); + } + + // Unlock from OpenGL access. +#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER + void *handles[] = { gldx_depth_texture, gldx_render_target_view }; + gd_wglDXUnlockObjectsNV(gldx_device, 2, handles); +#else + gd_wglDXUnlockObjectsNV(gldx_device, 1, &gldx_render_target_view); +#endif +} + +void GLManagerNative_Windows::DxgiSwapChain::present(bool p_use_vsync) { + unlock_from_opengl(); + +#ifdef OPENGL_DXGI_USE_FLIP_MODEL + HRESULT hr; + if (p_use_vsync) { + hr = swap_chain->Present(1, 0); + } else { + hr = swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING); + } +#else + // TODO: vsync??? + HRESULT hr = swap_chain->Present(p_use_vsync ? 1 : 0, 0); +#endif + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("Present failed, HRESULT: 0x%08X", (unsigned)hr)); + } + + release_render_target(false); + setup_render_target(); + lock_for_opengl(); +} + +void GLManagerNative_Windows::DxgiSwapChain::resize_swap_chain(int p_width, int p_height) { + release_render_target(true); +#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER + release_depth_buffer(); +#endif + + UINT flags = 0; +#ifdef OPENGL_DXGI_USE_FLIP_MODEL + flags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT | DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; +#endif + HRESULT hr = swap_chain->ResizeBuffers(0, p_width, p_height, DXGI_FORMAT_UNKNOWN, flags); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("ResizeBuffers failed, HRESULT: 0x%08X", (unsigned)hr)); + } + +#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER + if (setup_depth_buffer(p_width, p_height)) { +#else + { +#endif + setup_render_target(); + } + lock_for_opengl(); +} + +void GLManagerNative_Windows::DxgiSwapChain::set_use_vsync(bool p_use) { + // TODO: ??? +} + +#endif // OPENGL_ON_DXGI_ENABLED + #endif // WINDOWS_ENABLED && GLES3_ENABLED diff --git a/platform/windows/gl_manager_windows_native.h b/platform/windows/gl_manager_windows_native.h index 532092ae7439..535ddb3d4bbe 100644 --- a/platform/windows/gl_manager_windows_native.h +++ b/platform/windows/gl_manager_windows_native.h @@ -45,6 +45,8 @@ typedef int(APIENTRY *PFNWGLGETSWAPINTERVALEXTPROC)(void); class GLManagerNative_Windows { private: + class DxgiSwapChain; + // any data specific to the window struct GLWindow { bool use_vsync = false; @@ -54,6 +56,8 @@ class GLManagerNative_Windows { HWND hwnd; int gldisplay_id = 0; + + DxgiSwapChain *dxgi = nullptr; }; struct GLDisplay { @@ -75,6 +79,7 @@ class GLManagerNative_Windows { const GLDisplay &get_display(unsigned int id) { return _displays[id]; } bool direct_render; + bool prefer_dxgi = false; int glx_minor, glx_major; private: @@ -85,7 +90,7 @@ class GLManagerNative_Windows { public: Error window_create(DisplayServer::WindowID p_window_id, HWND p_hwnd, HINSTANCE p_hinstance, int p_width, int p_height); void window_destroy(DisplayServer::WindowID p_window_id); - void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {} + void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height); void release_current(); void swap_buffers(); @@ -100,6 +105,10 @@ class GLManagerNative_Windows { HDC get_hdc(DisplayServer::WindowID p_window_id); HGLRC get_hglrc(DisplayServer::WindowID p_window_id); + void set_prefer_dxgi_swap_chain(bool p_prefer); + // Only valid after creating the first window. + bool is_using_dxgi_swap_chain(); + GLManagerNative_Windows(); ~GLManagerNative_Windows(); }; From 24cc693e4f90ebd9daf2784451a341f90dd4fa9b Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Thu, 18 Jul 2024 03:54:01 +0800 Subject: [PATCH 02/14] Some changes for testing, try renderbuffer, fixup some names --- .../windows/gl_manager_windows_native.cpp | 179 ++++++++++++++---- 1 file changed, 144 insertions(+), 35 deletions(-) diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp index 580c8c8df64b..bfe59746c831 100644 --- a/platform/windows/gl_manager_windows_native.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -41,9 +41,32 @@ #include #include +// Enable the DXGI present feature. #define OPENGL_ON_DXGI_ENABLED -// #define OPENGL_DXGI_USE_DEPTH_BUFFER -// #define OPENGL_DXGI_USE_FLIP_MODEL + +// Create and set the D3D11 render target. +// We don't actually need to set the render target 'cause we're rendering +// everything in OpenGL, right? As long as the buffer is written to it should +// just work? +//#define OPENGL_DXGI_SET_RENDER_TARGET + +// Create a depth buffer in D3D11 and attach it to the render target. +// Requires OPENGL_DXGI_SET_RENDER_TARGET. +// For some reason Intel driver doesn't like it and gives GL_INVALID_OPERATION +// when trying to attach it with glFramebufferTexture2D. +// RasterizerGLES3 doesn't seem to actually need a depth buffer though. All +// it does is blit other FBOs onto the screen with GL_COLOR_BUFFER_BIT? +// Even if we do need a depth buffer, we could just create one in OpenGL. +//#define OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER + +// Bind the color buffer as renderbuffer instead of texture 2D. +#define OPENGL_DXGI_USE_RENDERBUFFER + +// TODO +//#define OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER + +// Use DXGI flip-discard. (WIP) +//#define OPENGL_DXGI_USE_FLIP_MODEL #ifdef OPENGL_ON_DXGI_ENABLED @@ -366,27 +389,39 @@ class GLManagerNative_Windows::DxgiSwapChain { ComPtr device_context; ComPtr swap_chain; -#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER ComPtr depth_texture; ComPtr depth_stencil_view; #endif ComPtr color_buffer; +#ifdef OPENGL_DXGI_SET_RENDER_TARGET ComPtr render_target_view; +#endif #ifdef OPENGL_DXGI_USE_FLIP_MODEL HANDLE frame_latency_waitable_obj{}; #endif HANDLE gldx_device{}; -#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER +#ifdef OPENGL_DXGI_USE_RENDERBUFFER + HANDLE gldx_color_buffer_rb{}; +#else +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER HANDLE gldx_depth_texture{}; #endif - HANDLE gldx_render_target_view{}; + HANDLE gldx_color_buffer_tex{}; +#endif GLuint gl_fbo{}; +#ifdef OPENGL_DXGI_USE_RENDERBUFFER + GLuint gl_color_buffer_rb{}; +#else +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER GLuint gl_depth_stencil_tex{}; - GLuint gl_render_target_view_tex{}; +#endif + GLuint gl_color_buffer_tex{}; +#endif DxgiSwapChain() = default; ~DxgiSwapChain() = default; @@ -406,7 +441,7 @@ class GLManagerNative_Windows::DxgiSwapChain { void set_use_vsync(bool p_use); private: -#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER bool setup_depth_buffer(int p_width, int p_height); void release_depth_buffer(); #endif @@ -697,6 +732,8 @@ void GLManagerNative_Windows::window_destroy(DisplayServer::WindowID p_window_id #ifdef OPENGL_ON_DXGI_ENABLED if (win.dxgi) { + // We need to destroy some OpenGL resources. + window_make_current(p_window_id); win.dxgi->destroy(); win.dxgi = nullptr; } @@ -896,7 +933,7 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: if (!SUCCEEDED(hr)) { ERR_PRINT(vformat("Failed to get IDXGIFactory, HRESULT: 0x%08X", (unsigned)hr)); } else { - hr = dxgi_factory->MakeWindowAssociation(p_hwnd, DXGI_MWA_NO_ALT_ENTER); + hr = dxgi_factory->MakeWindowAssociation(p_hwnd, DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES); if (!SUCCEEDED(hr)) { ERR_PRINT(vformat("Failed to get IDXGIFactory, HRESULT: 0x%08X", (unsigned)hr)); } @@ -934,10 +971,20 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: glGenFramebuffers(1, &gl_fbo); // Generate texture names for render target and depth buffers. +#ifdef OPENGL_DXGI_USE_RENDERBUFFER + GLuint gl_color_buffer_rb; + glGenRenderbuffers(1, &gl_color_buffer_rb); +#else +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER GLuint textures[2] = {}; glGenTextures(2, textures); GLuint gl_depth_stencil_tex = textures[0]; - GLuint gl_render_target_view_tex = textures[1]; + GLuint gl_color_buffer_tex = textures[1]; +#else + GLuint gl_color_buffer_tex; + glGenTextures(1, &gl_color_buffer_tex); +#endif +#endif GLManagerNative_Windows::DxgiSwapChain *dxgi = memnew(GLManagerNative_Windows::DxgiSwapChain); dxgi->device = std::move(device); @@ -948,12 +995,18 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: #endif dxgi->gldx_device = gldx_device; dxgi->gl_fbo = gl_fbo; +#ifdef OPENGL_DXGI_USE_RENDERBUFFER + dxgi->gl_color_buffer_rb = gl_color_buffer_rb; +#else +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER dxgi->gl_depth_stencil_tex = gl_depth_stencil_tex; - dxgi->gl_render_target_view_tex = gl_render_target_view_tex; +#endif + dxgi->gl_color_buffer_tex = gl_color_buffer_tex; +#endif GLES3::TextureStorage::system_fbo = gl_fbo; -#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER if (!dxgi->setup_depth_buffer(p_width, p_height)) { GLES3::TextureStorage::system_fbo = 0; memdelete(dxgi); @@ -963,7 +1016,7 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: if (!dxgi->setup_render_target()) { GLES3::TextureStorage::system_fbo = 0; -#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER dxgi->release_depth_buffer(); #endif memdelete(dxgi); @@ -1001,14 +1054,22 @@ void GLManagerNative_Windows::DxgiSwapChain::destroy() { } release_render_target(true); -#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER release_depth_buffer(); #endif // FIXME: Is this safe to do? Could our OpenGL context not be current? glDeleteFramebuffers(1, &gl_fbo); - GLuint textures[2] = { gl_depth_stencil_tex, gl_render_target_view_tex }; +#ifdef OPENGL_DXGI_USE_RENDERBUFFER + glDeleteRenderbuffers(1, &gl_color_buffer_rb); +#else +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER + GLuint textures[2] = { gl_depth_stencil_tex, gl_color_buffer_tex }; glDeleteTextures(2, textures); +#else + glDeleteTextures(1, &gl_color_buffer_tex); +#endif +#endif gd_wglDXCloseDeviceNV(gldx_device); @@ -1033,7 +1094,7 @@ void GLManagerNative_Windows::DxgiSwapChain::release_current() { GLES3::TextureStorage::system_fbo = 0; } -#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER bool GLManagerNative_Windows::DxgiSwapChain::setup_depth_buffer(int p_width, int p_height) { // Godot uses 24-bit depth buffer? ComPtr depth_texture_new; @@ -1085,7 +1146,7 @@ void GLManagerNative_Windows::DxgiSwapChain::release_depth_buffer() { depth_stencil_view.Reset(); depth_texture.Reset(); } -#endif // OPENGL_DXGI_USE_DEPTH_BUFFER +#endif // OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER bool GLManagerNative_Windows::DxgiSwapChain::setup_render_target() { // Get the current back buffer from the swap chain. @@ -1096,6 +1157,7 @@ bool GLManagerNative_Windows::DxgiSwapChain::setup_render_target() { return false; } +#ifdef OPENGL_DXGI_SET_RENDER_TARGET // TODO: Do we need to chedk OS::get_singleton()->is_layered_allowed() here? CD3D11_RENDER_TARGET_VIEW_DESC render_target_view_desc(D3D11_RTV_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM); ComPtr render_target_view_new; @@ -1107,22 +1169,36 @@ bool GLManagerNative_Windows::DxgiSwapChain::setup_render_target() { ERR_PRINT(vformat("CreateRenderTargetView failed, HRESULT: 0x%08X", (unsigned)hr)); return false; } +#endif // Register for interop. - HANDLE gldx_render_target_view_new = gd_wglDXRegisterObjectNV(gldx_device, +#ifdef OPENGL_DXGI_USE_RENDERBUFFER + HANDLE gldx_color_buffer_rb_new = gd_wglDXRegisterObjectNV(gldx_device, color_buffer_new.Get(), - gl_render_target_view_tex, + gl_color_buffer_rb, + GL_RENDERBUFFER, + WGL_ACCESS_READ_WRITE_NV); +#else + HANDLE gldx_color_buffer_tex_new = gd_wglDXRegisterObjectNV(gldx_device, + color_buffer_new.Get(), + gl_color_buffer_tex, GL_TEXTURE_2D, WGL_ACCESS_READ_WRITE_NV); - if (!gldx_render_target_view_new) { - ERR_PRINT(vformat("Failed to connect D3D11 render target to WGL for interop. Error: %s", format_error_message(GetLastError()))); +#endif +#ifdef OPENGL_DXGI_USE_RENDERBUFFER + if (!gldx_color_buffer_rb_new) { +#else + if (!gldx_color_buffer_tex_new) { +#endif + ERR_PRINT(vformat("Failed to connect D3D11 color buffer to WGL for interop. Error: %s", format_error_message(GetLastError()))); return false; } +#ifdef OPENGL_DXGI_SET_RENDER_TARGET // Attach back buffer and depth buffer to the render target. device_context->OMSetRenderTargets(1, render_target_view_new.GetAddressOf(), -#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER depth_stencil_view.Get() #else nullptr @@ -1132,10 +1208,17 @@ bool GLManagerNative_Windows::DxgiSwapChain::setup_render_target() { // TODO: Is it okay if we don't clear at all? // float clear_color[] = { 0.0f, 0.0f, 0.0f, 1.0f }; // device_context->ClearRenderTargetView(render_target_view_new.Get(), clear_color); +#endif color_buffer = std::move(color_buffer_new); +#ifdef OPENGL_DXGI_SET_RENDER_TARGET render_target_view = std::move(render_target_view_new); - gldx_render_target_view = gldx_render_target_view_new; +#endif +#ifdef OPENGL_DXGI_USE_RENDERBUFFER + gldx_color_buffer_rb = gldx_color_buffer_rb_new; +#else + gldx_color_buffer_tex = gldx_color_buffer_tex_new; +#endif return true; } @@ -1146,27 +1229,45 @@ void GLManagerNative_Windows::DxgiSwapChain::release_render_target(bool p_need_u } // Release the back buffer. - gd_wglDXUnregisterObjectNV(gldx_device, gldx_render_target_view); +#ifdef OPENGL_DXGI_USE_RENDERBUFFER + gd_wglDXUnregisterObjectNV(gldx_device, gldx_color_buffer_rb); +#else + gd_wglDXUnregisterObjectNV(gldx_device, gldx_color_buffer_tex); +#endif - gldx_render_target_view = nullptr; +#ifdef OPENGL_DXGI_USE_RENDERBUFFER + gldx_color_buffer_rb = nullptr; +#else + gldx_color_buffer_tex = nullptr; +#endif +#ifdef OPENGL_DXGI_SET_RENDER_TARGET render_target_view.Reset(); +#endif color_buffer.Reset(); } void GLManagerNative_Windows::DxgiSwapChain::lock_for_opengl() { // Lock the buffers for OpenGL access. -#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER - void *handles[] = { gldx_depth_texture, gldx_render_target_view }; +#ifdef OPENGL_DXGI_USE_RENDERBUFFER + gd_wglDXLockObjectsNV(gldx_device, 1, &gldx_color_buffer_rb); +#else +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER + void *handles[] = { gldx_depth_texture, gldx_color_buffer_tex }; gd_wglDXLockObjectsNV(gldx_device, 2, handles); #else - gd_wglDXLockObjectsNV(gldx_device, 1, &gldx_render_target_view); + gd_wglDXLockObjectsNV(gldx_device, 1, &gldx_color_buffer_tex); +#endif #endif // Attach color and depth buffers to FBO glBindFramebuffer(GL_FRAMEBUFFER, gl_fbo); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl_render_target_view_tex, 0); -#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER +#ifdef OPENGL_DXGI_USE_RENDERBUFFER + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, gl_color_buffer_rb); +#else + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl_color_buffer_tex, 0); +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, gl_depth_stencil_tex, 0); +#endif #endif if (GLES3::TextureStorage::system_fbo != gl_fbo) { glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); @@ -1176,20 +1277,28 @@ void GLManagerNative_Windows::DxgiSwapChain::lock_for_opengl() { void GLManagerNative_Windows::DxgiSwapChain::unlock_from_opengl() { // Detach color and depth buffers from FBO glBindFramebuffer(GL_FRAMEBUFFER, gl_fbo); +#ifdef OPENGL_DXGI_USE_RENDERBUFFER + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); +#else glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); -#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0); +#endif #endif if (GLES3::TextureStorage::system_fbo != gl_fbo) { glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); } // Unlock from OpenGL access. -#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER - void *handles[] = { gldx_depth_texture, gldx_render_target_view }; +#ifdef OPENGL_DXGI_USE_RENDERBUFFER + gd_wglDXUnlockObjectsNV(gldx_device, 1, &gldx_color_buffer_rb); +#else +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER + void *handles[] = { gldx_depth_texture, gldx_color_buffer_tex }; gd_wglDXUnlockObjectsNV(gldx_device, 2, handles); #else - gd_wglDXUnlockObjectsNV(gldx_device, 1, &gldx_render_target_view); + gd_wglDXUnlockObjectsNV(gldx_device, 1, &gldx_color_buffer_tex); +#endif #endif } @@ -1218,7 +1327,7 @@ void GLManagerNative_Windows::DxgiSwapChain::present(bool p_use_vsync) { void GLManagerNative_Windows::DxgiSwapChain::resize_swap_chain(int p_width, int p_height) { release_render_target(true); -#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER release_depth_buffer(); #endif @@ -1231,7 +1340,7 @@ void GLManagerNative_Windows::DxgiSwapChain::resize_swap_chain(int p_width, int ERR_PRINT(vformat("ResizeBuffers failed, HRESULT: 0x%08X", (unsigned)hr)); } -#ifdef OPENGL_DXGI_USE_DEPTH_BUFFER +#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER if (setup_depth_buffer(p_width, p_height)) { #else { From 98c426379ec5ef59ecddf67c0b15d989cda848c7 Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Thu, 18 Jul 2024 06:24:16 +0800 Subject: [PATCH 03/14] Add depth buffer renderbuffer done purely in OpenGL --- .../windows/gl_manager_windows_native.cpp | 53 +++++++++++++++---- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp index bfe59746c831..417211c64fdc 100644 --- a/platform/windows/gl_manager_windows_native.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -62,8 +62,8 @@ // Bind the color buffer as renderbuffer instead of texture 2D. #define OPENGL_DXGI_USE_RENDERBUFFER -// TODO -//#define OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER +// Create and bind a depth buffer renderbuffer in OpenGL. +#define OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER // Use DXGI flip-discard. (WIP) //#define OPENGL_DXGI_USE_FLIP_MODEL @@ -415,6 +415,9 @@ class GLManagerNative_Windows::DxgiSwapChain { GLuint gl_fbo{}; #ifdef OPENGL_DXGI_USE_RENDERBUFFER +#ifdef OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER + GLuint gl_depth_buffer_rb{}; +#endif GLuint gl_color_buffer_rb{}; #else #ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER @@ -441,7 +444,7 @@ class GLManagerNative_Windows::DxgiSwapChain { void set_use_vsync(bool p_use); private: -#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER +#if defined(OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER) || defined(OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER) bool setup_depth_buffer(int p_width, int p_height); void release_depth_buffer(); #endif @@ -972,8 +975,15 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: // Generate texture names for render target and depth buffers. #ifdef OPENGL_DXGI_USE_RENDERBUFFER +#ifdef OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER + GLuint renderbuffers[2] = {}; + glGenRenderbuffers(2, renderbuffers); + GLuint gl_depth_buffer_rb = renderbuffers[0]; + GLuint gl_color_buffer_rb = renderbuffers[1]; +#else GLuint gl_color_buffer_rb; glGenRenderbuffers(1, &gl_color_buffer_rb); +#endif #else #ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER GLuint textures[2] = {}; @@ -996,6 +1006,9 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: dxgi->gldx_device = gldx_device; dxgi->gl_fbo = gl_fbo; #ifdef OPENGL_DXGI_USE_RENDERBUFFER +#ifdef OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER + dxgi->gl_depth_buffer_rb = gl_depth_buffer_rb; +#endif dxgi->gl_color_buffer_rb = gl_color_buffer_rb; #else #ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER @@ -1006,7 +1019,7 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: GLES3::TextureStorage::system_fbo = gl_fbo; -#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER +#if defined(OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER) || defined(OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER) if (!dxgi->setup_depth_buffer(p_width, p_height)) { GLES3::TextureStorage::system_fbo = 0; memdelete(dxgi); @@ -1016,7 +1029,7 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: if (!dxgi->setup_render_target()) { GLES3::TextureStorage::system_fbo = 0; -#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER +#if defined(OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER) || defined(OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER) dxgi->release_depth_buffer(); #endif memdelete(dxgi); @@ -1054,14 +1067,19 @@ void GLManagerNative_Windows::DxgiSwapChain::destroy() { } release_render_target(true); -#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER +#if defined(OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER) || defined(OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER) release_depth_buffer(); #endif // FIXME: Is this safe to do? Could our OpenGL context not be current? glDeleteFramebuffers(1, &gl_fbo); #ifdef OPENGL_DXGI_USE_RENDERBUFFER +#ifdef OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER + GLuint renderbuffers[2] = { gl_depth_buffer_rb, gl_color_buffer_rb }; + glDeleteRenderbuffers(2, renderbuffers); +#else glDeleteRenderbuffers(1, &gl_color_buffer_rb); +#endif #else #ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER GLuint textures[2] = { gl_depth_stencil_tex, gl_color_buffer_tex }; @@ -1094,8 +1112,14 @@ void GLManagerNative_Windows::DxgiSwapChain::release_current() { GLES3::TextureStorage::system_fbo = 0; } -#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER +#if defined(OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER) || defined(OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER) bool GLManagerNative_Windows::DxgiSwapChain::setup_depth_buffer(int p_width, int p_height) { +#ifdef OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER + glBindRenderbuffer(GL_RENDERBUFFER, gl_depth_buffer_rb); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, p_width, p_height); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + return true; +#else // Godot uses 24-bit depth buffer? ComPtr depth_texture_new; CD3D11_TEXTURE2D_DESC depth_buffer_desc(DXGI_FORMAT_R24G8_TYPELESS, p_width, p_height, 1, 1, D3D11_BIND_DEPTH_STENCIL); @@ -1137,14 +1161,19 @@ bool GLManagerNative_Windows::DxgiSwapChain::setup_depth_buffer(int p_width, int gldx_depth_texture = gldx_depth_texture_new; return true; +#endif } void GLManagerNative_Windows::DxgiSwapChain::release_depth_buffer() { +#ifdef OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER + // no-op? +#else gd_wglDXUnregisterObjectNV(gldx_device, gldx_depth_texture); gldx_depth_texture = nullptr; depth_stencil_view.Reset(); depth_texture.Reset(); +#endif } #endif // OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER @@ -1263,6 +1292,9 @@ void GLManagerNative_Windows::DxgiSwapChain::lock_for_opengl() { glBindFramebuffer(GL_FRAMEBUFFER, gl_fbo); #ifdef OPENGL_DXGI_USE_RENDERBUFFER glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, gl_color_buffer_rb); +#ifdef OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gl_depth_buffer_rb); +#endif #else glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl_color_buffer_tex, 0); #ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER @@ -1279,6 +1311,9 @@ void GLManagerNative_Windows::DxgiSwapChain::unlock_from_opengl() { glBindFramebuffer(GL_FRAMEBUFFER, gl_fbo); #ifdef OPENGL_DXGI_USE_RENDERBUFFER glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); +#ifdef OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); +#endif #else glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); #ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER @@ -1327,7 +1362,7 @@ void GLManagerNative_Windows::DxgiSwapChain::present(bool p_use_vsync) { void GLManagerNative_Windows::DxgiSwapChain::resize_swap_chain(int p_width, int p_height) { release_render_target(true); -#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER +#if defined(OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER) || defined(OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER) release_depth_buffer(); #endif @@ -1340,7 +1375,7 @@ void GLManagerNative_Windows::DxgiSwapChain::resize_swap_chain(int p_width, int ERR_PRINT(vformat("ResizeBuffers failed, HRESULT: 0x%08X", (unsigned)hr)); } -#ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER +#if defined(OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER) || defined(OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER) if (setup_depth_buffer(p_width, p_height)) { #else { From d4e8e47e2ac41c932bcc58ee632f33c6b6dddc17 Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Thu, 18 Jul 2024 17:42:46 +0800 Subject: [PATCH 04/14] Better flip model test, also using frame latency waitable for vsync? --- .../windows/gl_manager_windows_native.cpp | 159 +++++++++++++++--- 1 file changed, 131 insertions(+), 28 deletions(-) diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp index 417211c64fdc..d3c669ee58b5 100644 --- a/platform/windows/gl_manager_windows_native.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -63,10 +63,11 @@ #define OPENGL_DXGI_USE_RENDERBUFFER // Create and bind a depth buffer renderbuffer in OpenGL. -#define OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER +// Again, we probably don't need this at all. +//#define OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER // Use DXGI flip-discard. (WIP) -//#define OPENGL_DXGI_USE_FLIP_MODEL +#define OPENGL_DXGI_USE_FLIP_MODEL #ifdef OPENGL_ON_DXGI_ENABLED @@ -76,6 +77,7 @@ #include #ifdef OPENGL_DXGI_USE_FLIP_MODEL #include +#include #endif #include @@ -150,10 +152,17 @@ static bool load_nv_dx_interop(PFNWGLGETPROCADDRESS gd_wglGetProcAddress) { gd_wglDXUnlockObjectsNV; } +typedef decltype(&CreateDXGIFactory1) PFNCREATEDXGIFACTORY1; + +static HMODULE module_dxgi = nullptr; +PFNCREATEDXGIFACTORY1 fptr_CreateDXGIFactory1 = nullptr; + typedef decltype(&D3D11CreateDeviceAndSwapChain) PFND3D11CREATEDEVICEANDSWAPCHAINPROC; +typedef decltype(&D3D11CreateDevice) PFND3D11CREATEDEVICEPROC; static HMODULE module_d3d11 = nullptr; PFND3D11CREATEDEVICEANDSWAPCHAINPROC fptr_D3D11CreateDeviceAndSwapChain = nullptr; +PFND3D11CREATEDEVICEPROC fptr_D3D11CreateDevice = nullptr; #endif // OPENGL_ON_DXGI_ENABLED @@ -484,14 +493,28 @@ static bool load_dxgi_swap_chain_functions(PFNWGLGETPROCADDRESS gd_wglGetProcAdd print_verbose("GLManagerNative_Windows: Loaded WGL_NV_DX_interop functions."); + module_dxgi = LoadLibraryW(L"dxgi.dll"); + if (!module_dxgi) { + print_verbose("GLManagerNative_Windows: Failed to load DXGI."); + return false; + } + + fptr_CreateDXGIFactory1 = (PFNCREATEDXGIFACTORY1)GetProcAddress(module_dxgi, "CreateDXGIFactory1"); + if (!fptr_CreateDXGIFactory1) { + print_verbose("GLManagerNative_Windows: Failed to load DXGI functions.") + FreeLibrary(module_d3d11); + return false; + } + module_d3d11 = LoadLibraryW(L"d3d11.dll"); if (!module_d3d11) { print_verbose("GLManagerNative_Windows: Failed to load D3D11."); return false; } - fptr_D3D11CreateDeviceAndSwapChain = (PFND3D11CREATEDEVICEANDSWAPCHAINPROC)(void *)GetProcAddress(module_d3d11, "D3D11CreateDeviceAndSwapChain"); - if (!fptr_D3D11CreateDeviceAndSwapChain) { + fptr_D3D11CreateDeviceAndSwapChain = (PFND3D11CREATEDEVICEANDSWAPCHAINPROC)GetProcAddress(module_d3d11, "D3D11CreateDeviceAndSwapChain"); + fptr_D3D11CreateDevice = (PFND3D11CREATEDEVICEPROC)GetProcAddress(module_d3d11, "D3D11CreateDevice"); + if (!fptr_D3D11CreateDeviceAndSwapChain || !fptr_D3D11CreateDevice) { print_verbose("GLManagerNative_Windows: Failed to load D3D11 functions.") FreeLibrary(module_d3d11); return false; @@ -873,11 +896,101 @@ GLManagerNative_Windows::~GLManagerNative_Windows() { #ifdef OPENGL_ON_DXGI_ENABLED +static ComPtr get_dxgi_factory(const ComPtr &device) { + ComPtr dxgi_device; + HRESULT hr = device.As(&dxgi_device); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("Failed to get IDXGIDevice, HRESULT: 0x%08X", (unsigned)hr)); + return {}; + } + ComPtr dxgi_adapter; + hr = dxgi_device->GetAdapter(&dxgi_adapter); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("Failed to get IDXGIAdapter, HRESULT: 0x%08X", (unsigned)hr)); + return {}; + } + ComPtr dxgi_factory; + hr = dxgi_adapter->GetParent(__uuidof(IDXGIFactory), &dxgi_factory); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("Failed to get IDXGIFactory, HRESULT: 0x%08X", (unsigned)hr)); + return {}; + } + return dxgi_factory; +} + GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain::create(HWND p_hwnd, int p_width, int p_height) { ComPtr device; ComPtr device_context; ComPtr swap_chain; + UINT flags = 0; + if (OS::get_singleton()->is_stdout_verbose()) { + flags |= D3D11_CREATE_DEVICE_DEBUG; + } + +#ifdef OPENGL_DXGI_USE_FLIP_MODEL + HRESULT hr; + +#if 0 + ComPtr dxgi_factory_2; + hr = fptr_CreateDXGIFactory1(__uuidof(IDXGIFactory2), &dxgi_factory_2); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("CreateDXGIFactory1 failed, HRESULT: 0x%08X", (unsigned)hr)); + return nullptr; + } + + ComPtr dxgi_adapter_1; + hr = dxgi_factory_2->EnumAdapters1(0, &dxgi_adapter_1); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("EnumAdapters1 failed, HRESULT: 0x%08X", (unsigned)hr)); + return nullptr; + } +#endif + + hr = fptr_D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags, nullptr, 0, D3D11_SDK_VERSION, &device, nullptr, &device_context); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("D3D11CreateDevice failed, HRESULT: 0x%08X", (unsigned)hr)); + return nullptr; + } + + ComPtr dxgi_factory = get_dxgi_factory(device); + if (!dxgi_factory) { + return nullptr; + } + + ComPtr dxgi_factory_2; + hr = dxgi_factory.As(&dxgi_factory_2); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("Failed to get IDXGIFactory2, HRESULT: 0x%08X", (unsigned)hr)); + return nullptr; + } + + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc_1 = {}; + swap_chain_desc_1.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swap_chain_desc_1.SampleDesc.Count = 1; + swap_chain_desc_1.BufferCount = 3; + swap_chain_desc_1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc_1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swap_chain_desc_1.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT | DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + swap_chain_desc_1.Scaling = DXGI_SCALING_NONE; + // TODO: ??? + swap_chain_desc_1.AlphaMode = DXGI_ALPHA_MODE_IGNORE; + + ComPtr swap_chain_1; + hr = dxgi_factory_2->CreateSwapChainForHwnd(device.Get(), p_hwnd, &swap_chain_desc_1, nullptr, nullptr, &swap_chain_1); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("CreateSwapChainForHwnd failed, HRESULT: 0x%08X", (unsigned)hr)); + return nullptr; + } + + swap_chain = swap_chain_1; + + hr = dxgi_factory_2->MakeWindowAssociation(p_hwnd, DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("MakeWindowAssociation failed, HRESULT: 0x%08X", (unsigned)hr)); + } + +#else DXGI_SWAP_CHAIN_DESC swap_chain_desc = {}; swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swap_chain_desc.SampleDesc.Count = 1; @@ -896,11 +1009,6 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; #endif - UINT flags = 0; - if (OS::get_singleton()->is_stdout_verbose()) { - flags |= D3D11_CREATE_DEVICE_DEBUG; - } - // TODO: Change to use IDXGIFactory2::CreateSwapChainForHwnd so that we // can specify DXGI_SCALING_NONE HRESULT hr = fptr_D3D11CreateDeviceAndSwapChain(nullptr, // Adapter @@ -921,29 +1029,15 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: } { - ComPtr dxgi_device; - hr = device.As(&dxgi_device); - if (!SUCCEEDED(hr)) { - ERR_PRINT(vformat("Failed to get IDXGIDevice, HRESULT: 0x%08X", (unsigned)hr)); - } else { - ComPtr dxgi_adapter; - hr = dxgi_device->GetAdapter(&dxgi_adapter); + ComPtr dxgi_factory = get_dxgi_factory(device); + if (dxgi_factory) { + hr = dxgi_factory->MakeWindowAssociation(p_hwnd, DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES); if (!SUCCEEDED(hr)) { - ERR_PRINT(vformat("Failed to get IDXGIAdapter, HRESULT: 0x%08X", (unsigned)hr)); - } else { - ComPtr dxgi_factory; - hr = dxgi_adapter->GetParent(__uuidof(IDXGIFactory), &dxgi_factory); - if (!SUCCEEDED(hr)) { - ERR_PRINT(vformat("Failed to get IDXGIFactory, HRESULT: 0x%08X", (unsigned)hr)); - } else { - hr = dxgi_factory->MakeWindowAssociation(p_hwnd, DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES); - if (!SUCCEEDED(hr)) { - ERR_PRINT(vformat("Failed to get IDXGIFactory, HRESULT: 0x%08X", (unsigned)hr)); - } - } + ERR_PRINT(vformat("MakeWindowAssociation failed, HRESULT: 0x%08X", (unsigned)hr)); } } } +#endif HANDLE gldx_device = gd_wglDXOpenDeviceNV(device.Get()); if (!gldx_device) { @@ -1344,6 +1438,15 @@ void GLManagerNative_Windows::DxgiSwapChain::present(bool p_use_vsync) { HRESULT hr; if (p_use_vsync) { hr = swap_chain->Present(1, 0); + DWORD wait = WaitForSingleObject(frame_latency_waitable_obj, 1000); + if (wait != WAIT_OBJECT_0) { + if (wait == WAIT_FAILED) { + DWORD error = GetLastError(); + ERR_PRINT(vformat("Wait for frame latency waitable failed with error: 0x%08X", (unsigned)error)); + } else { + ERR_PRINT(vformat("Wait for frame latency waitable failed, WaitForSingleObject returned 0x%08X", (unsigned)wait)); + } + } } else { hr = swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING); } From 2a70d7add58ada546f93027f071d055d681c1b58 Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Fri, 19 Jul 2024 18:20:52 +0800 Subject: [PATCH 05/14] Wait for frame latency before the first frame; more error checks --- .../windows/gl_manager_windows_native.cpp | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp index d3c669ee58b5..13aac0831840 100644 --- a/platform/windows/gl_manager_windows_native.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -966,6 +966,8 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: } DXGI_SWAP_CHAIN_DESC1 swap_chain_desc_1 = {}; + swap_chain_desc_1.Width = p_width; + swap_chain_desc_1.Height = p_height; swap_chain_desc_1.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swap_chain_desc_1.SampleDesc.Count = 1; swap_chain_desc_1.BufferCount = 3; @@ -1053,6 +1055,15 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: hr = swap_chain.As(&swap_chain_2); if (SUCCEEDED(hr)) { frame_latency_waitable_obj = swap_chain_2->GetFrameLatencyWaitableObject(); + DWORD wait = WaitForSingleObject(frame_latency_waitable_obj, 1000); + if (wait != WAIT_OBJECT_0) { + if (wait == WAIT_FAILED) { + DWORD error = GetLastError(); + ERR_PRINT(vformat("Wait for frame latency waitable failed with error: 0x%08X", (unsigned)error)); + } else { + ERR_PRINT(vformat("Wait for frame latency waitable failed, WaitForSingleObject returned 0x%08X", (unsigned)wait)); + } + } } else { ERR_PRINT(vformat("Failed to get IDXGISwapChain2, HRESULT: 0x%08X", (unsigned)hr)); } @@ -1262,7 +1273,10 @@ void GLManagerNative_Windows::DxgiSwapChain::release_depth_buffer() { #ifdef OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER // no-op? #else - gd_wglDXUnregisterObjectNV(gldx_device, gldx_depth_texture); + BOOL res = gd_wglDXUnregisterObjectNV(gldx_device, gldx_depth_texture); + if (!res) { + ERR_PRINT(vformat("Failed to unregister depth buffer for interop. Error: %s", format_error_message(GetLastError()))); + } gldx_depth_texture = nullptr; depth_stencil_view.Reset(); @@ -1353,10 +1367,13 @@ void GLManagerNative_Windows::DxgiSwapChain::release_render_target(bool p_need_u // Release the back buffer. #ifdef OPENGL_DXGI_USE_RENDERBUFFER - gd_wglDXUnregisterObjectNV(gldx_device, gldx_color_buffer_rb); + BOOL res = gd_wglDXUnregisterObjectNV(gldx_device, gldx_color_buffer_rb); #else - gd_wglDXUnregisterObjectNV(gldx_device, gldx_color_buffer_tex); + BOOL res = gd_wglDXUnregisterObjectNV(gldx_device, gldx_color_buffer_tex); #endif + if (!res) { + ERR_PRINT(vformat("Failed to unregister color buffer for interop. Error: %s", format_error_message(GetLastError()))); + } #ifdef OPENGL_DXGI_USE_RENDERBUFFER gldx_color_buffer_rb = nullptr; @@ -1372,15 +1389,18 @@ void GLManagerNative_Windows::DxgiSwapChain::release_render_target(bool p_need_u void GLManagerNative_Windows::DxgiSwapChain::lock_for_opengl() { // Lock the buffers for OpenGL access. #ifdef OPENGL_DXGI_USE_RENDERBUFFER - gd_wglDXLockObjectsNV(gldx_device, 1, &gldx_color_buffer_rb); + BOOL res = gd_wglDXLockObjectsNV(gldx_device, 1, &gldx_color_buffer_rb); #else #ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER void *handles[] = { gldx_depth_texture, gldx_color_buffer_tex }; - gd_wglDXLockObjectsNV(gldx_device, 2, handles); + BOOL res = gd_wglDXLockObjectsNV(gldx_device, 2, handles); #else - gd_wglDXLockObjectsNV(gldx_device, 1, &gldx_color_buffer_tex); + BOOL res = gd_wglDXLockObjectsNV(gldx_device, 1, &gldx_color_buffer_tex); #endif #endif + if (!res) { + ERR_PRINT(vformat("Failed to lock DX objects for interop. Error: %s", format_error_message(GetLastError()))); + } // Attach color and depth buffers to FBO glBindFramebuffer(GL_FRAMEBUFFER, gl_fbo); @@ -1420,15 +1440,18 @@ void GLManagerNative_Windows::DxgiSwapChain::unlock_from_opengl() { // Unlock from OpenGL access. #ifdef OPENGL_DXGI_USE_RENDERBUFFER - gd_wglDXUnlockObjectsNV(gldx_device, 1, &gldx_color_buffer_rb); + BOOL res = gd_wglDXUnlockObjectsNV(gldx_device, 1, &gldx_color_buffer_rb); #else #ifdef OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER void *handles[] = { gldx_depth_texture, gldx_color_buffer_tex }; - gd_wglDXUnlockObjectsNV(gldx_device, 2, handles); + BOOL res = gd_wglDXUnlockObjectsNV(gldx_device, 2, handles); #else - gd_wglDXUnlockObjectsNV(gldx_device, 1, &gldx_color_buffer_tex); + BOOL res = gd_wglDXUnlockObjectsNV(gldx_device, 1, &gldx_color_buffer_tex); #endif #endif + if (!res) { + ERR_PRINT(vformat("Failed to unlock DX objects for interop. Error: %s", format_error_message(GetLastError()))); + } } void GLManagerNative_Windows::DxgiSwapChain::present(bool p_use_vsync) { From 91e770343c41565d697a63eae26e880d23c5d1ad Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Fri, 19 Jul 2024 18:25:46 +0800 Subject: [PATCH 06/14] Release render target before presenting (may fix NVIDIA crash?) --- platform/windows/gl_manager_windows_native.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp index 13aac0831840..88d69d7029f4 100644 --- a/platform/windows/gl_manager_windows_native.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -1455,7 +1455,7 @@ void GLManagerNative_Windows::DxgiSwapChain::unlock_from_opengl() { } void GLManagerNative_Windows::DxgiSwapChain::present(bool p_use_vsync) { - unlock_from_opengl(); + release_render_target(true); #ifdef OPENGL_DXGI_USE_FLIP_MODEL HRESULT hr; @@ -1481,7 +1481,6 @@ void GLManagerNative_Windows::DxgiSwapChain::present(bool p_use_vsync) { ERR_PRINT(vformat("Present failed, HRESULT: 0x%08X", (unsigned)hr)); } - release_render_target(false); setup_render_target(); lock_for_opengl(); } From 86fb5c43fc8808ea86951cfb1cb8b892b3a324a0 Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Sat, 20 Jul 2024 00:36:41 +0800 Subject: [PATCH 07/14] Check for tearing support before actually using it --- .../windows/gl_manager_windows_native.cpp | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp index 88d69d7029f4..7ff92b77cc86 100644 --- a/platform/windows/gl_manager_windows_native.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -78,6 +78,7 @@ #ifdef OPENGL_DXGI_USE_FLIP_MODEL #include #include +#include #endif #include @@ -435,6 +436,10 @@ class GLManagerNative_Windows::DxgiSwapChain { GLuint gl_color_buffer_tex{}; #endif +#ifdef OPENGL_DXGI_USE_FLIP_MODEL + bool supports_tearing = false; +#endif + DxgiSwapChain() = default; ~DxgiSwapChain() = default; @@ -958,6 +963,23 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: return nullptr; } + bool supports_tearing = false; + { + ComPtr dxgi_factory_5; + hr = dxgi_factory.As(&dxgi_factory_5); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("Failed to get IDXGIFactory5, HRESULT: 0x%08X", (unsigned)hr)); + } else { + BOOL feature_allow_tearing = FALSE; + hr = dxgi_factory_5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &feature_allow_tearing, sizeof(feature_allow_tearing)); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("Failed to check DXGI_FEATURE_PRESENT_ALLOW_TEARING, HRESULT: 0x%08X", (unsigned)hr)); + } else { + supports_tearing = feature_allow_tearing; + } + } + } + ComPtr dxgi_factory_2; hr = dxgi_factory.As(&dxgi_factory_2); if (!SUCCEEDED(hr)) { @@ -973,7 +995,10 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: swap_chain_desc_1.BufferCount = 3; swap_chain_desc_1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swap_chain_desc_1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - swap_chain_desc_1.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT | DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + swap_chain_desc_1.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + if (supports_tearing) { + swap_chain_desc_1.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + } swap_chain_desc_1.Scaling = DXGI_SCALING_NONE; // TODO: ??? swap_chain_desc_1.AlphaMode = DXGI_ALPHA_MODE_IGNORE; @@ -1121,6 +1146,9 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: #endif dxgi->gl_color_buffer_tex = gl_color_buffer_tex; #endif +#ifdef OPENGL_DXGI_USE_FLIP_MODEL + dxgi->supports_tearing = supports_tearing; +#endif GLES3::TextureStorage::system_fbo = gl_fbo; @@ -1471,7 +1499,7 @@ void GLManagerNative_Windows::DxgiSwapChain::present(bool p_use_vsync) { } } } else { - hr = swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING); + hr = swap_chain->Present(0, supports_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0); } #else // TODO: vsync??? @@ -1493,7 +1521,10 @@ void GLManagerNative_Windows::DxgiSwapChain::resize_swap_chain(int p_width, int UINT flags = 0; #ifdef OPENGL_DXGI_USE_FLIP_MODEL - flags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT | DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + flags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + if (supports_tearing) { + flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + } #endif HRESULT hr = swap_chain->ResizeBuffers(0, p_width, p_height, DXGI_FORMAT_UNKNOWN, flags); if (!SUCCEEDED(hr)) { From 1b87a584c4f05f9da2ea4d3f98e276a46cd0cbd1 Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Sat, 20 Jul 2024 00:38:06 +0800 Subject: [PATCH 08/14] Wait for frame latency waitable object unconditionally --- platform/windows/gl_manager_windows_native.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp index 7ff92b77cc86..cdbb34e82d82 100644 --- a/platform/windows/gl_manager_windows_native.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -1489,18 +1489,18 @@ void GLManagerNative_Windows::DxgiSwapChain::present(bool p_use_vsync) { HRESULT hr; if (p_use_vsync) { hr = swap_chain->Present(1, 0); - DWORD wait = WaitForSingleObject(frame_latency_waitable_obj, 1000); - if (wait != WAIT_OBJECT_0) { - if (wait == WAIT_FAILED) { - DWORD error = GetLastError(); - ERR_PRINT(vformat("Wait for frame latency waitable failed with error: 0x%08X", (unsigned)error)); - } else { - ERR_PRINT(vformat("Wait for frame latency waitable failed, WaitForSingleObject returned 0x%08X", (unsigned)wait)); - } - } } else { hr = swap_chain->Present(0, supports_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0); } + DWORD wait = WaitForSingleObject(frame_latency_waitable_obj, 1000); + if (wait != WAIT_OBJECT_0) { + if (wait == WAIT_FAILED) { + DWORD error = GetLastError(); + ERR_PRINT(vformat("Wait for frame latency waitable failed with error: 0x%08X", (unsigned)error)); + } else { + ERR_PRINT(vformat("Wait for frame latency waitable failed, WaitForSingleObject returned 0x%08X", (unsigned)wait)); + } + } #else // TODO: vsync??? HRESULT hr = swap_chain->Present(p_use_vsync ? 1 : 0, 0); From 8e19ef720a40f2113c3dff7230e3a00f48c4df13 Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Sat, 20 Jul 2024 17:38:53 +0800 Subject: [PATCH 09/14] (Refactor) Split unlock_from_opengl call --- platform/windows/gl_manager_windows_native.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp index cdbb34e82d82..9952e588981a 100644 --- a/platform/windows/gl_manager_windows_native.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -464,7 +464,7 @@ class GLManagerNative_Windows::DxgiSwapChain { #endif bool setup_render_target(); - void release_render_target(bool p_need_unlock); + void release_render_target(); void lock_for_opengl(); void unlock_from_opengl(); @@ -1199,7 +1199,8 @@ void GLManagerNative_Windows::DxgiSwapChain::destroy() { GLES3::TextureStorage::system_fbo = 0; } - release_render_target(true); + unlock_from_opengl(); + release_render_target(); #if defined(OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER) || defined(OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER) release_depth_buffer(); #endif @@ -1388,11 +1389,7 @@ bool GLManagerNative_Windows::DxgiSwapChain::setup_render_target() { return true; } -void GLManagerNative_Windows::DxgiSwapChain::release_render_target(bool p_need_unlock) { - if (p_need_unlock) { - unlock_from_opengl(); - } - +void GLManagerNative_Windows::DxgiSwapChain::release_render_target() { // Release the back buffer. #ifdef OPENGL_DXGI_USE_RENDERBUFFER BOOL res = gd_wglDXUnregisterObjectNV(gldx_device, gldx_color_buffer_rb); @@ -1483,7 +1480,8 @@ void GLManagerNative_Windows::DxgiSwapChain::unlock_from_opengl() { } void GLManagerNative_Windows::DxgiSwapChain::present(bool p_use_vsync) { - release_render_target(true); + unlock_from_opengl(); + release_render_target(); #ifdef OPENGL_DXGI_USE_FLIP_MODEL HRESULT hr; @@ -1514,7 +1512,8 @@ void GLManagerNative_Windows::DxgiSwapChain::present(bool p_use_vsync) { } void GLManagerNative_Windows::DxgiSwapChain::resize_swap_chain(int p_width, int p_height) { - release_render_target(true); + unlock_from_opengl(); + release_render_target(); #if defined(OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER) || defined(OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER) release_depth_buffer(); #endif From 7d6e32109b4c0a5af8e42a8a0e9f3fc699e02c3f Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Sat, 20 Jul 2024 18:24:50 +0800 Subject: [PATCH 10/14] Add disabled test code to render onto an intermediate buffer --- .../windows/gl_manager_windows_native.cpp | 91 ++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp index 9952e588981a..4e4cc09c052a 100644 --- a/platform/windows/gl_manager_windows_native.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -66,6 +66,10 @@ // Again, we probably don't need this at all. //#define OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER +// Instead of rendering directly to the DXGI back buffer, render onto an +// intermediate buffer which is copied to the back buffer on present. +//#define OPENGL_DXGI_USE_INTERMEDIATE_BUFFER + // Use DXGI flip-discard. (WIP) #define OPENGL_DXGI_USE_FLIP_MODEL @@ -404,7 +408,11 @@ class GLManagerNative_Windows::DxgiSwapChain { ComPtr depth_stencil_view; #endif +#ifdef OPENGL_DXGI_USE_INTERMEDIATE_BUFFER + ComPtr intermediate_buffer; +#else ComPtr color_buffer; +#endif #ifdef OPENGL_DXGI_SET_RENDER_TARGET ComPtr render_target_view; #endif @@ -463,8 +471,13 @@ class GLManagerNative_Windows::DxgiSwapChain { void release_depth_buffer(); #endif +#ifdef OPENGL_DXGI_USE_INTERMEDIATE_BUFFER + bool setup_intermediate_buffer(int p_width, int p_height); + void release_intermediate_buffer(); +#else bool setup_render_target(); void release_render_target(); +#endif void lock_for_opengl(); void unlock_from_opengl(); @@ -1160,7 +1173,11 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: } #endif +#ifdef OPENGL_DXGI_USE_INTERMEDIATE_BUFFER + if (!dxgi->setup_intermediate_buffer(p_width, p_height)) { +#else if (!dxgi->setup_render_target()) { +#endif GLES3::TextureStorage::system_fbo = 0; #if defined(OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER) || defined(OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER) dxgi->release_depth_buffer(); @@ -1200,7 +1217,11 @@ void GLManagerNative_Windows::DxgiSwapChain::destroy() { } unlock_from_opengl(); +#ifdef OPENGL_DXGI_USE_INTERMEDIATE_BUFFER + release_intermediate_buffer(); +#else release_render_target(); +#endif #if defined(OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER) || defined(OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER) release_depth_buffer(); #endif @@ -1314,6 +1335,48 @@ void GLManagerNative_Windows::DxgiSwapChain::release_depth_buffer() { } #endif // OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER +#ifdef OPENGL_DXGI_USE_INTERMEDIATE_BUFFER +bool GLManagerNative_Windows::DxgiSwapChain::setup_intermediate_buffer(int p_width, int p_height) { + ComPtr intermediate_buffer_new; + CD3D11_TEXTURE2D_DESC intermediate_buffer_desc(DXGI_FORMAT_R8G8B8A8_UNORM, p_width, p_height, 1, 1, D3D11_BIND_RENDER_TARGET); + HRESULT hr = device->CreateTexture2D( + &intermediate_buffer_desc, + nullptr, + &intermediate_buffer_new); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("CreateTexture2D failed, HRESULT: 0x%08X", (unsigned)hr)); + return false; + } + + HANDLE gldx_color_buffer_rb_new = gd_wglDXRegisterObjectNV( + gldx_device, + intermediate_buffer_new.Get(), + gl_color_buffer_rb, + GL_RENDERBUFFER, + WGL_ACCESS_READ_WRITE_NV); + if (!gldx_color_buffer_rb_new) { + ERR_PRINT(vformat("Failed to connect D3D11 intermediate texture to WGL for interop. Error: %s", format_error_message(GetLastError()))); + return false; + } + + intermediate_buffer = std::move(intermediate_buffer_new); + gldx_color_buffer_rb = gldx_color_buffer_rb_new; + + return true; +} + +void GLManagerNative_Windows::DxgiSwapChain::release_intermediate_buffer() { + BOOL res = gd_wglDXUnregisterObjectNV(gldx_device, gldx_color_buffer_rb); + if (!res) { + ERR_PRINT(vformat("Failed to unregister color buffer for interop. Error: %s", format_error_message(GetLastError()))); + } + + gldx_color_buffer_rb = nullptr; + intermediate_buffer.Reset(); +} + +#else // OPENGL_DXGI_USE_INTERMEDIATE_BUFFER + bool GLManagerNative_Windows::DxgiSwapChain::setup_render_target() { // Get the current back buffer from the swap chain. ComPtr color_buffer_new; @@ -1410,6 +1473,7 @@ void GLManagerNative_Windows::DxgiSwapChain::release_render_target() { #endif color_buffer.Reset(); } +#endif // OPENGL_DXGI_USE_INTERMEDIATE_BUFFER void GLManagerNative_Windows::DxgiSwapChain::lock_for_opengl() { // Lock the buffers for OpenGL access. @@ -1481,10 +1545,25 @@ void GLManagerNative_Windows::DxgiSwapChain::unlock_from_opengl() { void GLManagerNative_Windows::DxgiSwapChain::present(bool p_use_vsync) { unlock_from_opengl(); +#ifndef OPENGL_DXGI_USE_INTERMEDIATE_BUFFER release_render_target(); +#endif -#ifdef OPENGL_DXGI_USE_FLIP_MODEL HRESULT hr; + +#ifdef OPENGL_DXGI_USE_INTERMEDIATE_BUFFER + // Now we copy our intermediate buffer to the back buffer. + ComPtr color_buffer_new; + hr = swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), &color_buffer_new); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("GetBuffer failed, HRESULT: 0x%08X", (unsigned)hr)); + return false; + } + + device_context->CopyResource(color_buffer_new.Get(), intermediate_buffer.Get()); +#endif + +#ifdef OPENGL_DXGI_USE_FLIP_MODEL if (p_use_vsync) { hr = swap_chain->Present(1, 0); } else { @@ -1507,13 +1586,19 @@ void GLManagerNative_Windows::DxgiSwapChain::present(bool p_use_vsync) { ERR_PRINT(vformat("Present failed, HRESULT: 0x%08X", (unsigned)hr)); } +#ifndef OPENGL_DXGI_USE_INTERMEDIATE_BUFFER setup_render_target(); +#endif lock_for_opengl(); } void GLManagerNative_Windows::DxgiSwapChain::resize_swap_chain(int p_width, int p_height) { unlock_from_opengl(); +#ifdef OPENGL_DXGI_USE_INTERMEDIATE_BUFFER + release_intermediate_buffer(); +#else release_render_target(); +#endif #if defined(OPENGL_DXGI_USE_D3D11_DEPTH_BUFFER) || defined(OPENGL_DXGI_ADD_DEPTH_RENDERBUFFER) release_depth_buffer(); #endif @@ -1535,7 +1620,11 @@ void GLManagerNative_Windows::DxgiSwapChain::resize_swap_chain(int p_width, int #else { #endif +#ifdef OPENGL_DXGI_USE_INTERMEDIATE_BUFFER + setup_intermediate_buffer(p_width, p_height); +#else setup_render_target(); +#endif } lock_for_opengl(); } From 78cba4939b88f83581d333ae9eca7ca0d9c54983 Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Sun, 21 Jul 2024 15:30:38 +0800 Subject: [PATCH 11/14] Create only one D3D11 device and reuse it --- .../windows/gl_manager_windows_native.cpp | 86 +++++++++++++++---- platform/windows/gl_manager_windows_native.h | 6 ++ 2 files changed, 75 insertions(+), 17 deletions(-) diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp index 4e4cc09c052a..75abaaaba974 100644 --- a/platform/windows/gl_manager_windows_native.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -455,7 +455,7 @@ class GLManagerNative_Windows::DxgiSwapChain { friend void memdelete(T *); public: - static DxgiSwapChain *create(HWND p_hwnd, int p_width, int p_height); + static DxgiSwapChain *create(HWND p_hwnd, int p_width, int p_height, ID3D11Device *p_d3d11_device, ID3D11DeviceContext *p_d3d11_device_context); void destroy(); void make_current(); @@ -541,6 +541,8 @@ static bool load_dxgi_swap_chain_functions(PFNWGLGETPROCADDRESS gd_wglGetProcAdd print_verbose("GLManagerNative_Windows: Loaded D3D11 functions."); return true; } + +static bool try_create_d3d11_device(ID3D11Device *&p_out_device, ID3D11DeviceContext *&p_out_device_context); #endif // OPENGL_ON_DXGI_ENABLED void GLManagerNative_Windows::set_prefer_dxgi_swap_chain(bool p_prefer) { @@ -705,7 +707,11 @@ Error GLManagerNative_Windows::_create_context(GLWindow &win, GLDisplay &gl_disp if (dxgi_status == DxgiStatus::UNINITIALIZED) { if (prefer_dxgi) { if (load_dxgi_swap_chain_functions(gd_wglGetProcAddress, win.hDC)) { - dxgi_status = DxgiStatus::LOADED_UNTESTED; + if (try_create_d3d11_device(d3d11_device, d3d11_device_context)) { + dxgi_status = DxgiStatus::LOADED_UNTESTED; + } else { + dxgi_status = DxgiStatus::DISABLED; + } } else { dxgi_status = DxgiStatus::DISABLED; } @@ -742,7 +748,7 @@ Error GLManagerNative_Windows::window_create(DisplayServer::WindowID p_window_id #ifdef OPENGL_ON_DXGI_ENABLED if (dxgi_status == DxgiStatus::LOADED_UNTESTED || dxgi_status == DxgiStatus::LOADED_USABLE) { - win.dxgi = DxgiSwapChain::create(win.hwnd, p_width, p_height); + win.dxgi = DxgiSwapChain::create(win.hwnd, p_width, p_height, d3d11_device, d3d11_device_context); if (win.dxgi) { if (dxgi_status == DxgiStatus::LOADED_UNTESTED) { dxgi_status = DxgiStatus::LOADED_USABLE; @@ -910,13 +916,25 @@ GLManagerNative_Windows::GLManagerNative_Windows() { GLManagerNative_Windows::~GLManagerNative_Windows() { release_current(); + +#ifdef OPENGL_ON_DXGI_ENABLED + if (d3d11_device_context) { + d3d11_device_context->Release(); + } + if (d3d11_device) { + d3d11_device->Release(); + } +#endif } #ifdef OPENGL_ON_DXGI_ENABLED -static ComPtr get_dxgi_factory(const ComPtr &device) { +template +static ComPtr get_dxgi_factory(ID3D11Device *device) { + static_assert(std::is_convertible::value, "Template argument must be IDXGIFactory or a derived type."); + ComPtr dxgi_device; - HRESULT hr = device.As(&dxgi_device); + HRESULT hr = device->QueryInterface(__uuidof(IDXGIDevice), &dxgi_device); if (!SUCCEEDED(hr)) { ERR_PRINT(vformat("Failed to get IDXGIDevice, HRESULT: 0x%08X", (unsigned)hr)); return {}; @@ -927,8 +945,8 @@ static ComPtr get_dxgi_factory(const ComPtr &device) ERR_PRINT(vformat("Failed to get IDXGIAdapter, HRESULT: 0x%08X", (unsigned)hr)); return {}; } - ComPtr dxgi_factory; - hr = dxgi_adapter->GetParent(__uuidof(IDXGIFactory), &dxgi_factory); + ComPtr dxgi_factory; + hr = dxgi_adapter->GetParent(__uuidof(T), &dxgi_factory); if (!SUCCEEDED(hr)) { ERR_PRINT(vformat("Failed to get IDXGIFactory, HRESULT: 0x%08X", (unsigned)hr)); return {}; @@ -936,7 +954,37 @@ static ComPtr get_dxgi_factory(const ComPtr &device) return dxgi_factory; } -GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain::create(HWND p_hwnd, int p_width, int p_height) { +static bool try_create_d3d11_device(ID3D11Device *&p_out_device, ID3D11DeviceContext *&p_out_device_context) { + ComPtr device; + ComPtr device_context; + + UINT flags = 0; + if (OS::get_singleton()->is_stdout_verbose()) { + flags |= D3D11_CREATE_DEVICE_DEBUG; + } + + HRESULT hr; + + hr = fptr_D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags, nullptr, 0, D3D11_SDK_VERSION, &device, nullptr, &device_context); + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("D3D11CreateDevice failed, HRESULT: 0x%08X", (unsigned)hr)); + return false; + } + +#ifdef OPENGL_DXGI_USE_FLIP_MODEL + ComPtr dxgi_factory_2 = get_dxgi_factory(device.Get()); + if (!dxgi_factory_2) { + ERR_PRINT("Failed to get IDXGIFactory2."); + return false; + } +#endif + + p_out_device = device.Detach(); + p_out_device_context = device_context.Detach(); + return true; +} + +GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain::create(HWND p_hwnd, int p_width, int p_height, ID3D11Device *p_d3d11_device, ID3D11DeviceContext *p_d3d11_device_context) { ComPtr device; ComPtr device_context; ComPtr swap_chain; @@ -965,21 +1013,19 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: } #endif - hr = fptr_D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags, nullptr, 0, D3D11_SDK_VERSION, &device, nullptr, &device_context); - if (!SUCCEEDED(hr)) { - ERR_PRINT(vformat("D3D11CreateDevice failed, HRESULT: 0x%08X", (unsigned)hr)); - return nullptr; - } + device = p_d3d11_device; + device_context = p_d3d11_device_context; - ComPtr dxgi_factory = get_dxgi_factory(device); - if (!dxgi_factory) { + ComPtr dxgi_factory_2 = get_dxgi_factory(device.Get()); + if (!dxgi_factory_2) { + ERR_PRINT("Failed to get IDXGIFactory2."); return nullptr; } bool supports_tearing = false; { ComPtr dxgi_factory_5; - hr = dxgi_factory.As(&dxgi_factory_5); + hr = dxgi_factory_2.As(&dxgi_factory_5); if (!SUCCEEDED(hr)) { ERR_PRINT(vformat("Failed to get IDXGIFactory5, HRESULT: 0x%08X", (unsigned)hr)); } else { @@ -1069,7 +1115,7 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: } { - ComPtr dxgi_factory = get_dxgi_factory(device); + ComPtr dxgi_factory = get_dxgi_factory(device.Get()); if (dxgi_factory) { hr = dxgi_factory->MakeWindowAssociation(p_hwnd, DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES); if (!SUCCEEDED(hr)) { @@ -1246,6 +1292,12 @@ void GLManagerNative_Windows::DxgiSwapChain::destroy() { gd_wglDXCloseDeviceNV(gldx_device); +#ifdef OPENGL_DXGI_USE_FLIP_MODEL + if (!CloseHandle(frame_latency_waitable_obj)) { + ERR_PRINT(vformat("Failed to CloseHandle on frame latency waitable object. Error: %s", format_error_message(GetLastError()))); + } +#endif + swap_chain.Reset(); device_context.Reset(); device.Reset(); diff --git a/platform/windows/gl_manager_windows_native.h b/platform/windows/gl_manager_windows_native.h index 535ddb3d4bbe..fe28eabbb5b9 100644 --- a/platform/windows/gl_manager_windows_native.h +++ b/platform/windows/gl_manager_windows_native.h @@ -43,6 +43,9 @@ typedef bool(APIENTRY *PFNWGLSWAPINTERVALEXTPROC)(int interval); typedef int(APIENTRY *PFNWGLGETSWAPINTERVALEXTPROC)(void); +class ID3D11Device; +class ID3D11DeviceContext; + class GLManagerNative_Windows { private: class DxgiSwapChain; @@ -72,6 +75,9 @@ class GLManagerNative_Windows { PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr; + ID3D11Device *d3d11_device = nullptr; + ID3D11DeviceContext *d3d11_device_context = nullptr; + GLWindow &get_window(unsigned int id) { return _windows[id]; } const GLWindow &get_window(unsigned int id) const { return _windows[id]; } From 293e68d957e9b1b5b5c3d6e0e15da6dc3fd57b8a Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Sun, 21 Jul 2024 15:58:08 +0800 Subject: [PATCH 12/14] Remove redefinition of variable --- platform/windows/gl_manager_windows_native.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp index 75abaaaba974..2c03acb1a3d5 100644 --- a/platform/windows/gl_manager_windows_native.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -1039,13 +1039,6 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: } } - ComPtr dxgi_factory_2; - hr = dxgi_factory.As(&dxgi_factory_2); - if (!SUCCEEDED(hr)) { - ERR_PRINT(vformat("Failed to get IDXGIFactory2, HRESULT: 0x%08X", (unsigned)hr)); - return nullptr; - } - DXGI_SWAP_CHAIN_DESC1 swap_chain_desc_1 = {}; swap_chain_desc_1.Width = p_width; swap_chain_desc_1.Height = p_height; From 8b6513f0b5f25ca5186f89754829691e9e6e99c6 Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Sun, 21 Jul 2024 16:48:57 +0800 Subject: [PATCH 13/14] Forward-declare COM interfaces as `struct` --- platform/windows/gl_manager_windows_native.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/windows/gl_manager_windows_native.h b/platform/windows/gl_manager_windows_native.h index fe28eabbb5b9..0a1a77032fb8 100644 --- a/platform/windows/gl_manager_windows_native.h +++ b/platform/windows/gl_manager_windows_native.h @@ -43,8 +43,8 @@ typedef bool(APIENTRY *PFNWGLSWAPINTERVALEXTPROC)(int interval); typedef int(APIENTRY *PFNWGLGETSWAPINTERVALEXTPROC)(void); -class ID3D11Device; -class ID3D11DeviceContext; +struct ID3D11Device; +struct ID3D11DeviceContext; class GLManagerNative_Windows { private: From ef37b05ab633640b9ad768568794a6bd52e1256e Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Tue, 30 Jul 2024 03:51:07 +0800 Subject: [PATCH 14/14] Wait frame latency in the main loop and only for the active window --- platform/windows/display_server_windows.cpp | 6 ++ .../windows/gl_manager_windows_native.cpp | 94 ++++++++++++++----- platform/windows/gl_manager_windows_native.h | 1 + 3 files changed, 77 insertions(+), 24 deletions(-) diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 97d9d7e10874..4bbc30fcfa2f 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -3003,6 +3003,12 @@ String DisplayServerWindows::keyboard_get_layout_name(int p_index) const { void DisplayServerWindows::process_events() { ERR_FAIL_COND(!Thread::is_main_thread()); +#if defined(GLES3_ENABLED) + if (gl_manager_native) { + gl_manager_native->wait_for_present(get_focused_window()); + } +#endif + if (!drop_events) { joypad->process_joypads(); } diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp index 2c03acb1a3d5..b6940ee15cea 100644 --- a/platform/windows/gl_manager_windows_native.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -34,6 +34,7 @@ #include "core/config/project_settings.h" #include "core/version.h" +#include "main/main.h" #include "thirdparty/nvapi/nvapi_minimal.h" @@ -446,6 +447,7 @@ class GLManagerNative_Windows::DxgiSwapChain { #ifdef OPENGL_DXGI_USE_FLIP_MODEL bool supports_tearing = false; + bool needs_wait = false; #endif DxgiSwapChain() = default; @@ -454,6 +456,8 @@ class GLManagerNative_Windows::DxgiSwapChain { template friend void memdelete(T *); + friend class GLManagerNative_Windows; + public: static DxgiSwapChain *create(HWND p_hwnd, int p_width, int p_height, ID3D11Device *p_d3d11_device, ID3D11DeviceContext *p_d3d11_device_context); void destroy(); @@ -520,7 +524,7 @@ static bool load_dxgi_swap_chain_functions(PFNWGLGETPROCADDRESS gd_wglGetProcAdd fptr_CreateDXGIFactory1 = (PFNCREATEDXGIFACTORY1)GetProcAddress(module_dxgi, "CreateDXGIFactory1"); if (!fptr_CreateDXGIFactory1) { print_verbose("GLManagerNative_Windows: Failed to load DXGI functions.") - FreeLibrary(module_d3d11); + FreeLibrary(module_dxgi); return false; } @@ -1132,15 +1136,6 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: hr = swap_chain.As(&swap_chain_2); if (SUCCEEDED(hr)) { frame_latency_waitable_obj = swap_chain_2->GetFrameLatencyWaitableObject(); - DWORD wait = WaitForSingleObject(frame_latency_waitable_obj, 1000); - if (wait != WAIT_OBJECT_0) { - if (wait == WAIT_FAILED) { - DWORD error = GetLastError(); - ERR_PRINT(vformat("Wait for frame latency waitable failed with error: 0x%08X", (unsigned)error)); - } else { - ERR_PRINT(vformat("Wait for frame latency waitable failed, WaitForSingleObject returned 0x%08X", (unsigned)wait)); - } - } } else { ERR_PRINT(vformat("Failed to get IDXGISwapChain2, HRESULT: 0x%08X", (unsigned)hr)); } @@ -1184,6 +1179,7 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: dxgi->swap_chain = std::move(swap_chain); #ifdef OPENGL_DXGI_USE_FLIP_MODEL dxgi->frame_latency_waitable_obj = frame_latency_waitable_obj; + dxgi->needs_wait = (frame_latency_waitable_obj != nullptr); #endif dxgi->gldx_device = gldx_device; dxgi->gl_fbo = gl_fbo; @@ -1232,7 +1228,7 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: ERR_PRINT(vformat("Failed to get IDXGISwapChain2, HRESULT: 0x%08X", (unsigned)hr)); } else { // TODO: ??? - swap_chain_2->SetMaximumFrameLatency(1); + swap_chain_2->SetMaximumFrameLatency(2); } #else ComPtr device1; @@ -1241,7 +1237,7 @@ GLManagerNative_Windows::DxgiSwapChain *GLManagerNative_Windows::DxgiSwapChain:: ERR_PRINT(vformat("Failed to get IDXGIDevice1, HRESULT: 0x%08X", (unsigned)hr)); } else { // TODO: ??? - device1->SetMaximumFrameLatency(1); + device1->SetMaximumFrameLatency(2); } #endif @@ -1609,27 +1605,46 @@ void GLManagerNative_Windows::DxgiSwapChain::present(bool p_use_vsync) { #endif #ifdef OPENGL_DXGI_USE_FLIP_MODEL - if (p_use_vsync) { - hr = swap_chain->Present(1, 0); - } else { - hr = swap_chain->Present(0, supports_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0); + bool skip_present = false; + if (needs_wait) { + DWORD wait = WaitForSingleObject(frame_latency_waitable_obj, 0); + if (wait != WAIT_OBJECT_0) { + skip_present = true; + if (wait == WAIT_TIMEOUT) { + // Force a redraw so that we can present the latest frame + // for this window on the next redraw. + Main::force_redraw(); + } else if (wait == WAIT_FAILED) { + DWORD error = GetLastError(); + ERR_PRINT(vformat("Wait for frame latency waitable failed with error: 0x%08X", (unsigned)error)); + } else { + ERR_PRINT(vformat("Wait for frame latency waitable failed, WaitForSingleObject returned 0x%08X", (unsigned)wait)); + } + } } - DWORD wait = WaitForSingleObject(frame_latency_waitable_obj, 1000); - if (wait != WAIT_OBJECT_0) { - if (wait == WAIT_FAILED) { - DWORD error = GetLastError(); - ERR_PRINT(vformat("Wait for frame latency waitable failed with error: 0x%08X", (unsigned)error)); + if (!skip_present) { + if (p_use_vsync) { + if (!needs_wait) { + hr = swap_chain->Present(1, 0); + } else { + // Not the main window, or not in the main loop (e.g. resizing). + hr = swap_chain->Present(0, 0); + } } else { - ERR_PRINT(vformat("Wait for frame latency waitable failed, WaitForSingleObject returned 0x%08X", (unsigned)wait)); + hr = swap_chain->Present(0, supports_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0); } + if (!SUCCEEDED(hr)) { + ERR_PRINT(vformat("Present failed, HRESULT: 0x%08X", (unsigned)hr)); + } + needs_wait = (frame_latency_waitable_obj != nullptr); } #else // TODO: vsync??? - HRESULT hr = swap_chain->Present(p_use_vsync ? 1 : 0, 0); -#endif + hr = swap_chain->Present(p_use_vsync ? 1 : 0, 0); if (!SUCCEEDED(hr)) { ERR_PRINT(vformat("Present failed, HRESULT: 0x%08X", (unsigned)hr)); } +#endif #ifndef OPENGL_DXGI_USE_INTERMEDIATE_BUFFER setup_render_target(); @@ -1678,6 +1693,37 @@ void GLManagerNative_Windows::DxgiSwapChain::set_use_vsync(bool p_use) { // TODO: ??? } +void GLManagerNative_Windows::wait_for_present(DisplayServer::WindowID p_window_id) { +#ifdef OPENGL_DXGI_USE_FLIP_MODEL + GLWindow *win = nullptr; + if (RBMap::Element *item = _windows.find(p_window_id)) { + win = &item->value(); + } else if ((item = _windows.find(DisplayServer::MAIN_WINDOW_ID))) { + p_window_id = DisplayServer::MAIN_WINDOW_ID; + win = &item->value(); + } else { +#ifdef DEBUG_ENABLED + WARN_PRINT("Cannot wait for present when there are no valid windows."); +#endif + return; + } + + if (win->dxgi && win->dxgi->needs_wait) { + DWORD wait = WaitForSingleObject(win->dxgi->frame_latency_waitable_obj, 1000); + if (wait != WAIT_OBJECT_0) { + if (wait == WAIT_FAILED) { + DWORD error = GetLastError(); + ERR_PRINT(vformat("Wait for frame latency waitable failed with error: 0x%08X", (unsigned)error)); + } else { + ERR_PRINT(vformat("Wait for frame latency waitable failed, WaitForSingleObject returned 0x%08X", (unsigned)wait)); + } + } else { + win->dxgi->needs_wait = false; + } + } +#endif +} + #endif // OPENGL_ON_DXGI_ENABLED #endif // WINDOWS_ENABLED && GLES3_ENABLED diff --git a/platform/windows/gl_manager_windows_native.h b/platform/windows/gl_manager_windows_native.h index 0a1a77032fb8..3d0d414cef04 100644 --- a/platform/windows/gl_manager_windows_native.h +++ b/platform/windows/gl_manager_windows_native.h @@ -100,6 +100,7 @@ class GLManagerNative_Windows { void release_current(); void swap_buffers(); + void wait_for_present(DisplayServer::WindowID p_window_id); void window_make_current(DisplayServer::WindowID p_window_id);