Skip to content

Commit 01af383

Browse files
authored
Fix Vulkan swapchain invalidation issue. (#3379)
* Fix Vulkan swapchain invalidation issue. * Always clamp render pass to frame buffer size. * Fix formatting.
1 parent 4372a1f commit 01af383

File tree

4 files changed

+87
-40
lines changed

4 files changed

+87
-40
lines changed

examples/common/entry/entry.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,7 @@ BX_PRAGMA_DIAGNOSTIC_POP();
767767
handle = size->m_handle;
768768
_width = size->m_width;
769769
_height = size->m_height;
770+
BX_TRACE("Window resize event: %d: %dx%d", handle, _width, _height);
770771

771772
needReset = true;
772773
}
@@ -800,6 +801,7 @@ BX_PRAGMA_DIAGNOSTIC_POP();
800801
&& needReset)
801802
{
802803
_reset = s_reset;
804+
BX_TRACE("bgfx::reset(%d, %d, 0x%x)", _width, _height, _reset)
803805
bgfx::reset(_width, _height, _reset);
804806
inputSetMouseResolution(uint16_t(_width), uint16_t(_height) );
805807
}
@@ -979,6 +981,7 @@ BX_PRAGMA_DIAGNOSTIC_POP();
979981
if (needReset)
980982
{
981983
_reset = s_reset;
984+
BX_TRACE("bgfx::reset(%d, %d, 0x%x)", s_window[0].m_width, s_window[0].m_height, _reset)
982985
bgfx::reset(s_window[0].m_width, s_window[0].m_height, _reset);
983986
inputSetMouseResolution(uint16_t(s_window[0].m_width), uint16_t(s_window[0].m_height) );
984987
}

examples/common/entry/entry_sdl.cpp

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ namespace entry
446446

447447
// Force window resolution...
448448
WindowHandle defaultWindow = { 0 };
449-
setWindowSize(defaultWindow, m_width, m_height, true);
449+
entry::setWindowSize(defaultWindow, m_width, m_height);
450450

451451
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
452452

@@ -624,7 +624,15 @@ namespace entry
624624
case SDL_WINDOWEVENT_SIZE_CHANGED:
625625
{
626626
WindowHandle handle = findHandle(wev.windowID);
627-
setWindowSize(handle, wev.data1, wev.data2);
627+
uint32_t width = wev.data1;
628+
uint32_t height = wev.data2;
629+
if (width != m_width
630+
|| height != m_height)
631+
{
632+
m_width = width;
633+
m_height = height;
634+
m_eventQueue.postSizeEvent(handle, m_width, m_height);
635+
}
628636
}
629637
break;
630638

@@ -825,7 +833,7 @@ namespace entry
825833
Msg* msg = (Msg*)uev.data2;
826834
if (isValid(handle) )
827835
{
828-
setWindowSize(handle, msg->m_width, msg->m_height);
836+
SDL_SetWindowSize(m_window[handle.idx], msg->m_width, msg->m_height);
829837
}
830838
delete msg;
831839
}
@@ -897,20 +905,6 @@ namespace entry
897905
return invalid;
898906
}
899907

900-
void setWindowSize(WindowHandle _handle, uint32_t _width, uint32_t _height, bool _force = false)
901-
{
902-
if (_width != m_width
903-
|| _height != m_height
904-
|| _force)
905-
{
906-
m_width = _width;
907-
m_height = _height;
908-
909-
SDL_SetWindowSize(m_window[_handle.idx], m_width, m_height);
910-
m_eventQueue.postSizeEvent(_handle, m_width, m_height);
911-
}
912-
}
913-
914908
GamepadHandle findGamepad(SDL_JoystickID _jid)
915909
{
916910
for (uint32_t ii = 0, num = m_gamepadAlloc.getNumHandles(); ii < num; ++ii)
@@ -1011,6 +1005,7 @@ namespace entry
10111005

10121006
void setWindowSize(WindowHandle _handle, uint32_t _width, uint32_t _height)
10131007
{
1008+
// Function to set the window size programmatically from the examples/tools.
10141009
Msg* msg = new Msg;
10151010
msg->m_width = _width;
10161011
msg->m_height = _height;

src/renderer_vk.cpp

Lines changed: 71 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2705,6 +2705,7 @@ VK_IMPORT_DEVICE
27052705
m_indexBuffers[_blitter.m_ib->handle.idx].update(m_commandBuffer, 0, _numIndices*2, _blitter.m_ib->data);
27062706
m_vertexBuffers[_blitter.m_vb->handle.idx].update(m_commandBuffer, 0, numVertices*_blitter.m_layout.m_stride, _blitter.m_vb->data, true);
27072707

2708+
27082709
VkRenderPassBeginInfo rpbi;
27092710
rpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
27102711
rpbi.pNext = NULL;
@@ -2799,27 +2800,26 @@ VK_IMPORT_DEVICE
27992800
return suspended;
28002801
}
28012802

2803+
//BX_TRACE("updateResolution(%d, %d) m_resolution=(%d, %d)"
2804+
// , _resolution.width
2805+
// , _resolution.height
2806+
// , m_resolution.width
2807+
// , m_resolution.height
2808+
// );
2809+
28022810
uint32_t flags = _resolution.reset & ~(0
28032811
| BGFX_RESET_SUSPEND
28042812
| BGFX_RESET_MAXANISOTROPY
28052813
| BGFX_RESET_DEPTH_CLAMP
28062814
);
28072815

2808-
// Note: m_needToRefreshSwapchain is deliberately ignored when deciding whether to
2809-
// recreate the swapchain because it can happen several frames before submit is called
2810-
// with the new resolution.
2811-
//
2812-
// Instead, vkAcquireNextImageKHR and all draws to the backbuffer are skipped until
2813-
// the window size is updated. That also fixes a related issue where VK_ERROR_OUT_OF_DATE_KHR
2814-
// is returned from vkQueuePresentKHR when the window doesn't exist anymore, and
2815-
// vkGetPhysicalDeviceSurfaceCapabilitiesKHR fails with VK_ERROR_SURFACE_LOST_KHR.
2816-
28172816
if (false
28182817
|| m_resolution.format != _resolution.format
28192818
|| m_resolution.width != _resolution.width
28202819
|| m_resolution.height != _resolution.height
28212820
|| m_resolution.reset != flags
2822-
|| m_backBuffer.m_swapChain.m_needToRecreateSurface)
2821+
|| m_backBuffer.m_swapChain.m_needToRecreateSurface
2822+
|| m_backBuffer.m_swapChain.m_needToRecreateSwapchain)
28232823
{
28242824
flags &= ~BGFX_RESET_INTERNAL_FORCE;
28252825

@@ -2837,6 +2837,10 @@ VK_IMPORT_DEVICE
28372837
preReset();
28382838

28392839
m_backBuffer.update(m_commandBuffer, m_resolution);
2840+
// Update the resolution again here, as the actual width and height
2841+
// is now final (as it was potentially clamped by the Vulkan driver).
2842+
m_resolution.width = m_backBuffer.m_width;
2843+
m_resolution.height = m_backBuffer.m_height;
28402844

28412845
postReset();
28422846
}
@@ -6849,6 +6853,7 @@ VK_DESTROY
68496853
;
68506854

68516855
const bool recreateSwapchain = false
6856+
|| m_needToRecreateSwapchain
68526857
|| m_resolution.format != _resolution.format
68536858
|| m_resolution.width != _resolution.width
68546859
|| m_resolution.height != _resolution.height
@@ -6962,6 +6967,7 @@ VK_DESTROY
69626967
&& NULL != vkCreateWaylandSurfaceKHR
69636968
)
69646969
{
6970+
BX_TRACE("Attempting Wayland surface creation.");
69656971
VkWaylandSurfaceCreateInfoKHR sci;
69666972
sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
69676973
sci.pNext = NULL;
@@ -6977,6 +6983,7 @@ VK_DESTROY
69776983
&& NULL != vkCreateXlibSurfaceKHR
69786984
)
69796985
{
6986+
BX_TRACE("Attempting Xlib surface creation.");
69806987
VkXlibSurfaceCreateInfoKHR sci;
69816988
sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
69826989
sci.pNext = NULL;
@@ -6996,6 +7003,7 @@ VK_DESTROY
69967003

69977004
if (NULL != xcbdll)
69987005
{
7006+
BX_TRACE("Attempting XCB surface creation.");
69997007
typedef xcb_connection_t* (*PFN_XGETXCBCONNECTION)(Display*);
70007008
PFN_XGETXCBCONNECTION XGetXCBConnection = (PFN_XGETXCBCONNECTION)bx::dlsym(xcbdll, "XGetXCBConnection");
70017009

@@ -7108,8 +7116,9 @@ VK_DESTROY
71087116
const VkAllocationCallbacks* allocatorCb = s_renderVK->m_allocatorCb;
71097117

71107118
// Waiting for the device to be idle seems to get rid of VK_DEVICE_LOST
7111-
// upon resizing the window quickly. (See https://github.com/mpv-player/mpv/issues/8360
7112-
// and https://github.com/bkaradzic/bgfx/issues/3227).
7119+
// upon resizing the window quickly. See:
7120+
// - https://github.com/mpv-player/mpv/issues/8360
7121+
// - https://github.com/bkaradzic/bgfx/issues/3227
71137122
result = vkDeviceWaitIdle(device);
71147123
BX_WARN(VK_SUCCESS == result, "Create swapchain error: vkDeviceWaitIdle() failed: %d: %s", result, getName(result));
71157124

@@ -7166,6 +7175,15 @@ VK_DESTROY
71667175
, surfaceCapabilities.minImageExtent.height
71677176
, surfaceCapabilities.maxImageExtent.height
71687177
);
7178+
if (width != m_resolution.width || height != m_resolution.height)
7179+
{
7180+
BX_TRACE("Clamped swapchain resolution from %dx%d to %dx%d"
7181+
, m_resolution.width
7182+
, m_resolution.height
7183+
, width
7184+
, height
7185+
);
7186+
}
71697187

71707188
VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
71717189

@@ -7292,6 +7310,8 @@ VK_DESTROY
72927310
m_backBufferColorImageLayout[ii] = VK_IMAGE_LAYOUT_UNDEFINED;
72937311
}
72947312

7313+
BX_TRACE("Succesfully created swapchain (%dx%d) with %d images.", width, height, m_numSwapChainImages);
7314+
72957315
VkSemaphoreCreateInfo sci;
72967316
sci.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
72977317
sci.pNext = NULL;
@@ -7314,7 +7334,7 @@ VK_DESTROY
73147334
m_currentSemaphore = 0;
73157335

73167336
m_needPresent = false;
7317-
m_needToRefreshSwapchain = false;
7337+
m_needToRecreateSwapchain = false;
73187338

73197339
return result;
73207340
}
@@ -7626,7 +7646,7 @@ VK_DESTROY
76267646
{
76277647
BGFX_PROFILER_SCOPE("SwapChainVK::acquire", kColorFrame);
76287648
if (VK_NULL_HANDLE == m_swapChain
7629-
|| m_needToRefreshSwapchain)
7649+
|| m_needToRecreateSwapchain)
76307650
{
76317651
return false;
76327652
}
@@ -7652,18 +7672,25 @@ VK_DESTROY
76527672
);
76537673
}
76547674

7675+
7676+
if (result != VK_SUCCESS)
7677+
{
7678+
BX_TRACE("vkAcquireNextImageKHR(...): result = %s", getName(result));
7679+
}
7680+
76557681
switch (result)
76567682
{
76577683
case VK_SUCCESS:
76587684
break;
76597685

76607686
case VK_ERROR_SURFACE_LOST_KHR:
76617687
m_needToRecreateSurface = true;
7662-
[[fallthrough]];
7688+
m_needToRecreateSwapchain = true;
7689+
return false;
76637690

76647691
case VK_ERROR_OUT_OF_DATE_KHR:
76657692
case VK_SUBOPTIMAL_KHR:
7666-
m_needToRefreshSwapchain = true;
7693+
m_needToRecreateSwapchain = true;
76677694
return false;
76687695

76697696
default:
@@ -7712,15 +7739,21 @@ VK_DESTROY
77127739
result = vkQueuePresentKHR(m_queue, &pi);
77137740
}
77147741

7742+
if (result != VK_SUCCESS)
7743+
{
7744+
BX_TRACE("vkQueuePresentKHR(...): result = %s", getName(result));
7745+
}
7746+
77157747
switch (result)
77167748
{
77177749
case VK_ERROR_SURFACE_LOST_KHR:
77187750
m_needToRecreateSurface = true;
7719-
[[fallthrough]];
7751+
m_needToRecreateSwapchain = true;
7752+
break;
77207753

77217754
case VK_ERROR_OUT_OF_DATE_KHR:
77227755
case VK_SUBOPTIMAL_KHR:
7723-
m_needToRefreshSwapchain = true;
7756+
m_needToRecreateSwapchain = true;
77247757
break;
77257758

77267759
default:
@@ -7883,8 +7916,10 @@ VK_DESTROY
78837916
BGFX_PROFILER_SCOPE("FrameBufferVK::update", kColorResource);
78847917
m_swapChain.update(_commandBuffer, m_nwh, _resolution);
78857918
VK_CHECK(s_renderVK->getRenderPass(m_swapChain, &m_renderPass) );
7886-
m_width = _resolution.width;
7887-
m_height = _resolution.height;
7919+
// Don't believe the passed Resolution, as the Vulkan driver might have
7920+
// specified another resolution, which we had to obey.
7921+
m_width = m_swapChain.m_sci.imageExtent.width;
7922+
m_height = m_swapChain.m_sci.imageExtent.height;
78887923
m_sampler = m_swapChain.m_sampler;
78897924
}
78907925

@@ -8563,12 +8598,26 @@ VK_DESTROY
85638598
if (isFrameBufferValid)
85648599
{
85658600
viewState.m_rect = _render->m_view[view].m_rect;
8566-
const Rect& rect = _render->m_view[view].m_rect;
8567-
const Rect& scissorRect = _render->m_view[view].m_scissor;
8601+
Rect rect = _render->m_view[view].m_rect;
8602+
Rect scissorRect = _render->m_view[view].m_scissor;
85688603
viewHasScissor = !scissorRect.isZero();
85698604
viewScissorRect = viewHasScissor ? scissorRect : rect;
85708605
restoreScissor = false;
85718606

8607+
// Clamp the rect to what's valid according to Vulkan.
8608+
rect.m_width = bx::min(rect.m_width, fb.m_width - rect.m_x);
8609+
rect.m_height = bx::min(rect.m_height, fb.m_height - rect.m_y);
8610+
if (_render->m_view[view].m_rect.m_width != rect.m_width
8611+
|| _render->m_view[view].m_rect.m_height != rect.m_height)
8612+
{
8613+
BX_TRACE("Clamp render pass from %dx%d to %dx%d"
8614+
, _render->m_view[view].m_rect.m_width
8615+
, _render->m_view[view].m_rect.m_height
8616+
, rect.m_width
8617+
, rect.m_height
8618+
);
8619+
}
8620+
85728621
rpbi.framebuffer = fb.m_currentFramebuffer;
85738622
rpbi.renderPass = fb.m_renderPass;
85748623
rpbi.renderArea.offset.x = rect.m_x;

src/renderer_vk.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,7 @@ VK_DESTROY_FUNC(DescriptorSet);
762762
VkSemaphore m_lastImageAcquiredSemaphore;
763763

764764
bool m_needPresent;
765-
bool m_needToRefreshSwapchain;
765+
bool m_needToRecreateSwapchain;
766766
bool m_needToRecreateSurface;
767767

768768
TextureVK m_backBufferDepthStencil;

0 commit comments

Comments
 (0)