From a04e75687a3929136046aaf3e0fe14afba4b5414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Sat, 17 Oct 2026 14:15:13 -0700 Subject: [PATCH] Fixed D3D11 multi-window vsync and Vulkan GPU selection. Closes #3468, #3628, #1186, #1267. (#4062) --- examples/common/entry/entry.cpp | 16 +++ examples/common/entry/entry_windows.cpp | 16 +++ examples/common/entry/entry_x11.cpp | 16 ++- examples/common/entry/input.cpp | 5 + examples/common/entry/input.h | 3 + src/bgfx.cpp | 9 +- src/bgfx_p.h | 4 +- src/renderer_d3d11.cpp | 24 +++++ src/renderer_d3d11.h | 2 + src/renderer_vk.cpp | 127 +++++++++++++++++++----- 10 files changed, 188 insertions(+), 34 deletions(-) diff --git a/examples/common/entry/entry.cpp b/examples/common/entry/entry.cpp index 7bfa461d4..3ab66d5c8 100644 --- a/examples/common/entry/entry.cpp +++ b/examples/common/entry/entry.cpp @@ -923,6 +923,14 @@ restart: break; case Event::Suspend: + { + const SuspendEvent* suspend = static_cast(ev); + + if (Suspend::DidSuspend == suspend->m_state) + { + inputReleaseKeys(); + } + } break; case Event::DropFile: @@ -1087,6 +1095,14 @@ restart: break; case Event::Suspend: + { + const SuspendEvent* suspend = static_cast(ev); + + if (Suspend::DidSuspend == suspend->m_state) + { + inputReleaseKeys(); + } + } break; case Event::DropFile: diff --git a/examples/common/entry/entry_windows.cpp b/examples/common/entry/entry_windows.cpp index 22a291f20..a93be5377 100644 --- a/examples/common/entry/entry_windows.cpp +++ b/examples/common/entry/entry_windows.cpp @@ -742,6 +742,22 @@ namespace entry } break; + case WM_SETFOCUS: + { + WindowHandle handle = findHandle(_hwnd); + m_eventQueue.postSuspendEvent(handle, Suspend::WillResume); + m_eventQueue.postSuspendEvent(handle, Suspend::DidResume); + } + break; + + case WM_KILLFOCUS: + { + WindowHandle handle = findHandle(_hwnd); + m_eventQueue.postSuspendEvent(handle, Suspend::WillSuspend); + m_eventQueue.postSuspendEvent(handle, Suspend::DidSuspend); + } + break; + case WM_SYSCOMMAND: switch (_wparam) { diff --git a/examples/common/entry/entry_x11.cpp b/examples/common/entry/entry_x11.cpp index f6769ec83..b85cc47b7 100644 --- a/examples/common/entry/entry_x11.cpp +++ b/examples/common/entry/entry_x11.cpp @@ -559,8 +559,22 @@ namespace entry } break; + case FocusIn: + { + WindowHandle handle = findHandle(event.xfocus.window); + m_eventQueue.postSuspendEvent(handle, Suspend::WillResume); + m_eventQueue.postSuspendEvent(handle, Suspend::DidResume); + } + break; + case FocusOut: - m_modifiers = Modifier::None; + { + m_modifiers = Modifier::None; + + WindowHandle handle = findHandle(event.xfocus.window); + m_eventQueue.postSuspendEvent(handle, Suspend::WillSuspend); + m_eventQueue.postSuspendEvent(handle, Suspend::DidSuspend); + } break; case MappingNotify: diff --git a/examples/common/entry/input.cpp b/examples/common/entry/input.cpp index bffb1720d..9816a922f 100644 --- a/examples/common/entry/input.cpp +++ b/examples/common/entry/input.cpp @@ -325,6 +325,11 @@ bool inputGetKeyState(entry::Key::Enum _key, uint8_t* _modifiers) return s_input->m_keyboard.getKeyState(_key, _modifiers); } +void inputReleaseKeys() +{ + s_input->m_keyboard.reset(); +} + uint8_t inputGetModifiersState() { return s_input->m_keyboard.getModifiersState(); diff --git a/examples/common/entry/input.h b/examples/common/entry/input.h index 787f6374d..f7252ff7e 100644 --- a/examples/common/entry/input.h +++ b/examples/common/entry/input.h @@ -60,6 +60,9 @@ void inputSetKeyState(entry::Key::Enum _key, uint8_t _modifiers, bool _down); /// bool inputGetKeyState(entry::Key::Enum _key, uint8_t* _modifiers = NULL); +/// +void inputReleaseKeys(); + /// uint8_t inputGetModifiersState(); diff --git a/src/bgfx.cpp b/src/bgfx.cpp index 4d48653d2..100db3929 100644 --- a/src/bgfx.cpp +++ b/src/bgfx.cpp @@ -2536,8 +2536,8 @@ namespace bgfx else { BX_TRACE("Creating rendering thread."); - m_thread.init(renderThread, this, 0, "bgfx - renderer backend thread"); m_singleThreaded = false; + m_thread.init(renderThread, this, 0, "bgfx - renderer backend thread"); } #else BX_TRACE("Multithreaded renderer is disabled."); @@ -3485,18 +3485,19 @@ namespace bgfx rendererExecCommands(m_render->m_cmdPost); } + m_flipAfterRender = 0 != (m_render->m_reset & BGFX_RESET_FLIP_AFTER_RENDER); + m_flushPrevFrame = m_render->m_flush; + renderSemPost(); if (m_flipAfterRender) { - if (!m_render->m_flush) + if (!m_flushPrevFrame) { BGFX_PROFILER_SCOPE("bgfx/flip", kColorSubmit); flip(); } } - - m_flushPrevFrame = m_render->m_flush; } else { diff --git a/src/bgfx_p.h b/src/bgfx_p.h index a9fe5dace..e637dede2 100644 --- a/src/bgfx_p.h +++ b/src/bgfx_p.h @@ -6240,8 +6240,6 @@ namespace bgfx dump(m_init.swapChain, m_init.reset); - m_flipAfterRender = !!(resetFlags & BGFX_RESET_FLIP_AFTER_RENDER); - for (uint32_t ii = 0; ii < BGFX_CONFIG_MAX_VIEWS; ++ii) { View* view = m_view.find(ii); @@ -8782,7 +8780,7 @@ namespace bgfx if (ok) { m_render->m_waitSubmit = bx::getHPCounter()-start; - m_submit->m_perfStats.waitSubmit = m_submit->m_waitSubmit; + m_render->m_perfStats.waitSubmit = m_render->m_waitSubmit; return true; } diff --git a/src/renderer_d3d11.cpp b/src/renderer_d3d11.cpp index 6cff3c225..822c66c4e 100644 --- a/src/renderer_d3d11.cpp +++ b/src/renderer_d3d11.cpp @@ -5944,6 +5944,11 @@ namespace bgfx { namespace d3d11 } #if BX_PLATFORM_WINDOWS + if (scd.waitable) + { + m_frameLatencyWaitableObject = m_swapChain->GetFrameLatencyWaitableObject(); + } + DX_CHECK(s_renderD3D11->m_dxgi.m_factory->MakeWindowAssociation( (HWND)_desc.nwh , 0 @@ -6006,6 +6011,10 @@ namespace bgfx { namespace d3d11 scd.ndt = m_desc.ndt; scd.sampleDesc = s_msaa[(m_desc.flags&BGFX_SWAP_CHAIN_MSAA_MASK)>>BGFX_SWAP_CHAIN_MSAA_SHIFT]; scd.hdr10 = 0 != (m_desc.flags & BGFX_SWAP_CHAIN_HDR10); + scd.waitable = false + || DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL == scd.swapEffect + || DXGI_SWAP_EFFECT_FLIP_DISCARD == scd.swapEffect + ; return scd; } @@ -6120,6 +6129,14 @@ namespace bgfx { namespace d3d11 uint16_t FrameBufferD3D11::destroy() { +#if BX_PLATFORM_WINDOWS + if (NULL != m_frameLatencyWaitableObject) + { + CloseHandle( (HANDLE)m_frameLatencyWaitableObject); + m_frameLatencyWaitableObject = NULL; + } +#endif // BX_PLATFORM_WINDOWS + if (isSwapChain() ) { destroySwapChainViews(); @@ -6493,6 +6510,13 @@ namespace bgfx { namespace d3d11 hr = !isLost(hr) ? S_OK : hr; m_needPresent = false; +#if BX_PLATFORM_WINDOWS + if (NULL != m_frameLatencyWaitableObject) + { + WaitForSingleObjectEx( (HANDLE)m_frameLatencyWaitableObject, 1000, TRUE); + } +#endif // BX_PLATFORM_WINDOWS + return hr; } diff --git a/src/renderer_d3d11.h b/src/renderer_d3d11.h index 01130bb72..7ae932873 100644 --- a/src/renderer_d3d11.h +++ b/src/renderer_d3d11.h @@ -415,6 +415,7 @@ namespace bgfx { namespace d3d11 , m_swapChain(NULL) , m_msaaRt(NULL) , m_nwh(NULL) + , m_frameLatencyWaitableObject(NULL) , m_width(0) , m_height(0) , m_denseIdx(UINT16_MAX) @@ -462,6 +463,7 @@ namespace bgfx { namespace d3d11 SwapChain m_desc; SwapChain m_descPending; void* m_nwh; + void* m_frameLatencyWaitableObject; uint32_t m_width; uint32_t m_height; diff --git a/src/renderer_vk.cpp b/src/renderer_vk.cpp index b6ba2579b..c1f425abc 100644 --- a/src/renderer_vk.cpp +++ b/src/renderer_vk.cpp @@ -1317,6 +1317,36 @@ VK_IMPORT_DEVICE ); } + VkResult createSurface(VkInstance _instance, const VkAllocationCallbacks* _allocatorCb, void* _nwh, void* _ndt, uint32_t _flags, ::VkSurfaceKHR* _surface); + + bool canPresent(VkPhysicalDevice _physicalDevice, VkSurfaceKHR _surface) + { + VkQueueFamilyProperties queueFamilyProperties[16]; + uint32_t queueFamilyPropertyCount = BX_COUNTOF(queueFamilyProperties); + vkGetPhysicalDeviceQueueFamilyProperties(_physicalDevice, &queueFamilyPropertyCount, queueFamilyProperties); + + constexpr VkQueueFlags requiredFlags = 0 + | VK_QUEUE_GRAPHICS_BIT + | VK_QUEUE_COMPUTE_BIT + ; + + for (uint32_t ii = 0; ii < queueFamilyPropertyCount; ++ii) + { + if (requiredFlags == (requiredFlags & queueFamilyProperties[ii].queueFlags) ) + { + VkBool32 surfaceSupported = VK_FALSE; + const VkResult result = vkGetPhysicalDeviceSurfaceSupportKHR(_physicalDevice, ii, _surface, &surfaceSupported); + + return true + && VK_SUCCESS == result + && VK_FALSE != surfaceSupported + ; + } + } + + return false; + } + struct TextureVK; struct RendererContextVK : public RendererContextI @@ -1677,6 +1707,26 @@ VK_IMPORT_INSTANCE uint32_t physicalDeviceIdx = UINT32_MAX; uint32_t fallbackPhysicalDeviceIdx = UINT32_MAX; + bool matchedPhysicalDevice = false; + + VkSurfaceKHR surface = VK_NULL_HANDLE; + + if (!headless + && BX_ENABLED(BX_PLATFORM_WINDOWS || BX_PLATFORM_LINUX) ) + { + const VkResult surfaceResult = createSurface(m_instance + , m_allocatorCb + , _init.swapChain.nwh + , _init.swapChain.ndt + , _init.swapChain.flags + , &surface + ); + + if (VK_SUCCESS != surfaceResult) + { + surface = VK_NULL_HANDLE; + } + } for (uint32_t ii = 0; ii < numPhysicalDevices; ++ii) { @@ -1718,7 +1768,13 @@ VK_IMPORT_INSTANCE { BX_TRACE("\tSkipped: Vulkan 1.1 or newer is required."); } - else if ( (BGFX_PCI_ID_NONE != g_caps.vendorId || 0 != g_caps.deviceId) + else if (VK_NULL_HANDLE != surface + && !canPresent(physicalDevices[ii], surface) ) + { + BX_TRACE("\tSkipped: Can't present to the window."); + } + else if (!matchedPhysicalDevice + && (BGFX_PCI_ID_NONE != g_caps.vendorId || 0 != g_caps.deviceId) && (BGFX_PCI_ID_NONE == g_caps.vendorId || pdp.vendorID == g_caps.vendorId) && ( 0 == g_caps.deviceId || pdp.deviceID == g_caps.deviceId) ) { @@ -1728,7 +1784,8 @@ VK_IMPORT_INSTANCE fallbackPhysicalDeviceIdx = ii; } - physicalDeviceIdx = ii; + physicalDeviceIdx = ii; + matchedPhysicalDevice = true; } else if (UINT32_MAX == physicalDeviceIdx) { @@ -1771,6 +1828,11 @@ VK_IMPORT_INSTANCE dumpExtensions(physicalDevices[ii], physicalDeviceExtensions[ii]); } + if (VK_NULL_HANDLE != surface) + { + vkDestroySurfaceKHR(m_instance, surface, m_allocatorCb); + } + if (UINT32_MAX == physicalDeviceIdx) { physicalDeviceIdx = UINT32_MAX == fallbackPhysicalDeviceIdx @@ -9056,14 +9118,11 @@ VK_DESTROY } } - VkResult SwapChainVK::createSurface() + VkResult createSurface(VkInstance _instance, const VkAllocationCallbacks* _allocatorCb, void* _nwh, void* _ndt, uint32_t _flags, ::VkSurfaceKHR* _surface) { - BGFX_PROFILER_SCOPE("SwapChainVK::createSurface", kColorFrame); - VkResult result = VK_ERROR_EXTENSION_NOT_PRESENT; - const VkInstance instance = s_renderVK->m_instance; - const VkAllocationCallbacks* allocatorCb = s_renderVK->m_allocatorCb; + BX_UNUSED(_ndt, _flags); #if BX_PLATFORM_WINDOWS { @@ -9074,8 +9133,8 @@ VK_DESTROY sci.pNext = NULL; sci.flags = 0; sci.hinstance = (HINSTANCE)GetModuleHandle(NULL); - sci.hwnd = (HWND)m_nwh; - result = vkCreateWin32SurfaceKHR(instance, &sci, allocatorCb, &m_surface); + sci.hwnd = (HWND)_nwh; + result = vkCreateWin32SurfaceKHR(_instance, &sci, _allocatorCb, _surface); BX_WARN(VK_SUCCESS == result, "vkCreateWin32SurfaceKHR failed %d: %s.", result, getName(result) ); } } @@ -9087,8 +9146,8 @@ VK_DESTROY sci.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; sci.pNext = NULL; sci.flags = 0; - sci.window = (ANativeWindow*)m_nwh; - result = vkCreateAndroidSurfaceKHR(instance, &sci, allocatorCb, &m_surface); + sci.window = (ANativeWindow*)_nwh; + result = vkCreateAndroidSurfaceKHR(_instance, &sci, _allocatorCb, _surface); BX_WARN(VK_SUCCESS == result, "vkCreateAndroidSurfaceKHR failed %d: %s.", result, getName(result) ); } } @@ -9105,9 +9164,9 @@ VK_DESTROY sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; sci.pNext = NULL; sci.flags = 0; - sci.display = (wl_display*)m_desc.ndt; - sci.surface = (wl_surface*)m_nwh; - result = vkCreateWaylandSurfaceKHR(instance, &sci, allocatorCb, &m_surface); + sci.display = (wl_display*)_ndt; + sci.surface = (wl_surface*)_nwh; + result = vkCreateWaylandSurfaceKHR(_instance, &sci, _allocatorCb, _surface); BX_WARN(VK_SUCCESS == result, "vkCreateWaylandSurfaceKHR failed %d: %s.", result, getName(result) ); } } @@ -9122,9 +9181,9 @@ VK_DESTROY sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; sci.pNext = NULL; sci.flags = 0; - sci.dpy = (Display*)m_desc.ndt; - sci.window = (Window)m_nwh; - result = vkCreateXlibSurfaceKHR(instance, &sci, allocatorCb, &m_surface); + sci.dpy = (Display*)_ndt; + sci.window = (Window)_nwh; + result = vkCreateXlibSurfaceKHR(_instance, &sci, _allocatorCb, _surface); BX_WARN(VK_SUCCESS == result, "vkCreateXlibSurfaceKHR failed %d: %s.", result, getName(result) ); } @@ -9147,11 +9206,11 @@ VK_DESTROY sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; sci.pNext = NULL; sci.flags = 0; - sci.connection = XGetXCBConnection( (Display*)m_desc.ndt); + sci.connection = XGetXCBConnection( (Display*)_ndt); if (NULL != sci.connection) { - sci.window = bx::narrowCast(uintptr_t(m_nwh) ); - result = vkCreateXcbSurfaceKHR(instance, &sci, allocatorCb, &m_surface); + sci.window = bx::narrowCast(uintptr_t(_nwh) ); + result = vkCreateXcbSurfaceKHR(_instance, &sci, _allocatorCb, _surface); BX_WARN(VK_SUCCESS == result, "vkCreateXcbSurfaceKHR failed %d: %s.", result, getName(result) ); } } @@ -9165,8 +9224,8 @@ VK_DESTROY { if (NULL != vkCreateMacOSSurfaceMVK) { - NSWindow* window = (NSWindow*)(m_nwh); - CAMetalLayer* layer = (CAMetalLayer*)(m_nwh); + NSWindow* window = (NSWindow*)(_nwh); + CAMetalLayer* layer = (CAMetalLayer*)(_nwh); if ([window isKindOfClass:[NSWindow class]]) { @@ -9187,7 +9246,7 @@ VK_DESTROY return result; } - if (m_desc.flags & BGFX_SWAP_CHAIN_HIDPI) + if (_flags & BGFX_SWAP_CHAIN_HIDPI) { layer.contentsScale = [window backingScaleFactor]; } @@ -9197,7 +9256,7 @@ VK_DESTROY sci.pNext = NULL; sci.flags = 0; sci.pView = (__bridge void*)layer; - result = vkCreateMacOSSurfaceMVK(instance, &sci, allocatorCb, &m_surface); + result = vkCreateMacOSSurfaceMVK(_instance, &sci, _allocatorCb, _surface); BX_WARN(VK_SUCCESS == result, "vkCreateMacOSSurfaceMVK failed %d: %s.", result, getName(result) ); } } @@ -9208,14 +9267,30 @@ VK_DESTROY sci.sType = VK_STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN; sci.pNext = NULL; sci.flags = 0; - sci.window = m_nwh; - result = vkCreateViSurfaceNN(instance, &sci, allocatorCb, &m_surface); + sci.window = _nwh; + result = vkCreateViSurfaceNN(_instance, &sci, _allocatorCb, _surface); BX_WARN(VK_SUCCESS == result, "vkCreateViSurfaceNN failed %d: %s.", result, getName(result) ); } #else # error "Figure out KHR surface..." #endif // BX_PLATFORM_ + return result; + } + + VkResult SwapChainVK::createSurface() + { + BGFX_PROFILER_SCOPE("SwapChainVK::createSurface", kColorFrame); + + VkResult result = bgfx::vk::createSurface( + s_renderVK->m_instance + , s_renderVK->m_allocatorCb + , m_nwh + , m_desc.ndt + , m_desc.flags + , &m_surface + ); + m_needToRecreateSurface = false; if (VK_SUCCESS != result)