Skip to content

Commit

Permalink
vk: do not draw into empty swapchain
Browse files Browse the repository at this point in the history
On Windows we're seeing a max size = 0x0 swapchains. Those cannot be created or used. Make sure that we're not, and we're not trying to draw anything when there's no swapchain available.

Unfortunately we still have to call some rendering functions (without actually rendering anything) to make sure that various invariants hold.

fixes #463
  • Loading branch information
w23 committed Sep 12, 2023
1 parent d920796 commit 64e1a9b
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 52 deletions.
96 changes: 58 additions & 38 deletions ref/vk/vk_framectl.c
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ void VK_RenderFrame( const struct ref_viewpass_s *rvp )
APROF_SCOPE_END(render_frame);
}

static void enqueueRendering( vk_combuf_t* combuf ) {
static void enqueueRendering( vk_combuf_t* combuf, qboolean draw ) {
const VkClearValue clear_value[] = {
{.color = {{1., 0., 0., 0.}}},
{.depthStencil = {1., 0.}} // TODO reverse-z
Expand All @@ -272,8 +272,8 @@ static void enqueueRendering( vk_combuf_t* combuf ) {
if (vk_frame.rtx_enabled)
VK_RenderEndRTX( combuf, g_frame.current.framebuffer.view, g_frame.current.framebuffer.image, g_frame.current.framebuffer.width, g_frame.current.framebuffer.height );

{
VkRenderPassBeginInfo rpbi = {
if (draw) {
const VkRenderPassBeginInfo rpbi = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = vk_frame.rtx_enabled ? vk_frame.render_pass.after_ray_tracing : vk_frame.render_pass.raster,
.renderArea.extent.width = g_frame.current.framebuffer.width,
Expand All @@ -283,33 +283,34 @@ static void enqueueRendering( vk_combuf_t* combuf ) {
.framebuffer = g_frame.current.framebuffer.framebuffer,
};
vkCmdBeginRenderPass(cmdbuf, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
}

{
const VkViewport viewport[] = {
{0.f, 0.f, (float)g_frame.current.framebuffer.width, (float)g_frame.current.framebuffer.height, 0.f, 1.f},
};
const VkRect2D scissor[] = {{
{0, 0},
{g_frame.current.framebuffer.width, g_frame.current.framebuffer.height},
}};

vkCmdSetViewport(cmdbuf, 0, ARRAYSIZE(viewport), viewport);
vkCmdSetScissor(cmdbuf, 0, ARRAYSIZE(scissor), scissor);
{
const VkViewport viewport[] = {
{0.f, 0.f, (float)g_frame.current.framebuffer.width, (float)g_frame.current.framebuffer.height, 0.f, 1.f},
};
const VkRect2D scissor[] = {{
{0, 0},
{g_frame.current.framebuffer.width, g_frame.current.framebuffer.height},
}};

vkCmdSetViewport(cmdbuf, 0, ARRAYSIZE(viewport), viewport);
vkCmdSetScissor(cmdbuf, 0, ARRAYSIZE(scissor), scissor);
}
}

if (!vk_frame.rtx_enabled)
VK_RenderEnd( cmdbuf );
VK_RenderEnd( cmdbuf, draw );

R_VkOverlay_DrawAndFlip( cmdbuf );
R_VkOverlay_DrawAndFlip( cmdbuf, draw );

vkCmdEndRenderPass(cmdbuf);
if (draw)
vkCmdEndRenderPass(cmdbuf);

g_frame.current.phase = Phase_RenderingEnqueued;
}

// FIXME pass frame, not combuf (possible desync)
static void submit( vk_combuf_t* combuf, qboolean wait ) {
static void submit( vk_combuf_t* combuf, qboolean wait, qboolean draw ) {
ASSERT(g_frame.current.phase == Phase_RenderingEnqueued);

const VkCommandBuffer cmdbuf = combuf->cmdbuf;
Expand All @@ -332,33 +333,50 @@ static void submit( vk_combuf_t* combuf, qboolean wait ) {
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
};

#define BOUNDED_ARRAY(NAME, TYPE, MAX_SIZE) \
struct { \
TYPE items[MAX_SIZE]; \
int count; \
} NAME

#define BOUNDED_ARRAY_APPEND(var, item) \
do { \
ASSERT(var.count < COUNTOF(var.items)); \
var.items[var.count++] = item; \
} while(0)

// TODO for RT renderer we only touch framebuffer at the very end of rendering/cmdbuf.
// Can we postpone waitinf for framebuffer semaphore until we actually need it.
const VkSemaphore waitophores[] = {
frame->sem_framebuffer_ready,
prev_frame->sem_done2,
};
const VkSemaphore signalphores[] = {
frame->sem_done,
frame->sem_done2,
};
BOUNDED_ARRAY(waitophores, VkSemaphore, 2) = {0};
BOUNDED_ARRAY(signalphores, VkSemaphore, 2) = {0};

if (draw) {
BOUNDED_ARRAY_APPEND(waitophores, frame->sem_framebuffer_ready);
BOUNDED_ARRAY_APPEND(signalphores, frame->sem_done);
}

BOUNDED_ARRAY_APPEND(waitophores, prev_frame->sem_done2);
BOUNDED_ARRAY_APPEND(signalphores, frame->sem_done2);

const VkSubmitInfo subinfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = NULL,
.waitSemaphoreCount = waitophores.count,
.pWaitSemaphores = waitophores.items,
.pWaitDstStageMask = stageflags,
.commandBufferCount = cmdbufs[0] ? 2 : 1,
.pCommandBuffers = cmdbufs[0] ? cmdbufs : cmdbufs + 1,
.waitSemaphoreCount = COUNTOF(waitophores),
.pWaitSemaphores = waitophores,
.pWaitDstStageMask = stageflags,
.signalSemaphoreCount = COUNTOF(signalphores),
.pSignalSemaphores = signalphores,
.signalSemaphoreCount = signalphores.count,
.pSignalSemaphores = signalphores.items,
};
//gEngine.Con_Printf("SYNC: wait for semaphore %d, signal semaphore %d\n", (g_frame.current.index + 1) % MAX_CONCURRENT_FRAMES, g_frame.current.index);
XVK_CHECK(vkQueueSubmit(vk_core.queue, 1, &subinfo, frame->fence_done));
g_frame.current.phase = Phase_Submitted;
}

R_VkSwapchainPresent(g_frame.current.framebuffer.index, frame->sem_done);
if (g_frame.current.framebuffer.framebuffer != VK_NULL_HANDLE)
R_VkSwapchainPresent(g_frame.current.framebuffer.index, frame->sem_done);

g_frame.current.framebuffer = (r_vk_swapchain_framebuffer_t){0};

if (wait) {
Expand All @@ -384,9 +402,10 @@ void R_EndFrame( void )

if (g_frame.current.phase == Phase_FrameBegan) {
vk_combuf_t *const combuf = g_frame.frames[g_frame.current.index].combuf;
enqueueRendering( combuf );
submit( combuf, false );
//submit( cmdbuf, true );
const qboolean draw = g_frame.current.framebuffer.framebuffer != VK_NULL_HANDLE;
enqueueRendering( combuf, draw );
submit( combuf, false, draw );
//submit( cmdbuf, true, draw );
}

APROF_SCOPE_END(end_frame);
Expand Down Expand Up @@ -523,7 +542,8 @@ static rgbdata_t *XVK_ReadPixels( void ) {
}

// Make sure that all rendering ops are enqueued
enqueueRendering( combuf );
const qboolean draw = true;
enqueueRendering( combuf, draw );

{
// Barrier 1: dest image
Expand Down Expand Up @@ -628,7 +648,7 @@ static rgbdata_t *XVK_ReadPixels( void ) {
0, 0, NULL, 0, NULL, ARRAYSIZE(image_barrier), image_barrier);
}

submit( combuf, true );
submit( combuf, true, draw );

// copy bytes to buffer
{
Expand Down
7 changes: 6 additions & 1 deletion ref/vk/vk_overlay.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ void R_VkOverlay_Shutdown( void ) {
vkDestroyPipelineLayout(vk_core.device, g2d.pipeline_layout, NULL);
}

void R_VkOverlay_DrawAndFlip( VkCommandBuffer cmdbuf ) {
static void drawOverlay( VkCommandBuffer cmdbuf ) {
DEBUG_BEGIN(cmdbuf, "2d overlay");

{
Expand All @@ -275,6 +275,11 @@ void R_VkOverlay_DrawAndFlip( VkCommandBuffer cmdbuf ) {
}

DEBUG_END(cmdbuf);
}

void R_VkOverlay_DrawAndFlip( VkCommandBuffer cmdbuf, qboolean draw ) {
if (draw)
drawOverlay(cmdbuf);

clearAccumulated();
}
Expand Down
3 changes: 2 additions & 1 deletion ref/vk/vk_overlay.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ void CL_FillRGBABlend( float x, float y, float w, float h, int r, int g, int b,

qboolean R_VkOverlay_Init( void );
void R_VkOverlay_Shutdown( void );
void R_VkOverlay_DrawAndFlip( VkCommandBuffer cmdbuf );

void R_VkOverlay_DrawAndFlip( VkCommandBuffer cmdbuf, qboolean draw );
5 changes: 4 additions & 1 deletion ref/vk/vk_render.c
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,11 @@ void VK_Render_FIXME_Barrier( VkCommandBuffer cmdbuf ) {
}
}

void VK_RenderEnd( VkCommandBuffer cmdbuf )
void VK_RenderEnd( VkCommandBuffer cmdbuf, qboolean draw )
{
if (!draw)
return;

// TODO we can sort collected draw commands for more efficient and correct rendering
// that requires adding info about distance to camera for correct order-dependent blending

Expand Down
2 changes: 1 addition & 1 deletion ref/vk/vk_render.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ void VK_RenderDebugLabelBegin( const char *label );
void VK_RenderDebugLabelEnd( void );

void VK_RenderBegin( qboolean ray_tracing );
void VK_RenderEnd( VkCommandBuffer cmdbuf );
void VK_RenderEnd( VkCommandBuffer cmdbuf, qboolean draw );
struct vk_combuf_s;
void VK_RenderEndRTX( struct vk_combuf_s* combuf, VkImageView img_dst_view, VkImage img_dst, uint32_t w, uint32_t h );

Expand Down
10 changes: 7 additions & 3 deletions ref/vk/vk_rtx.c
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,6 @@ static void performTracing( vk_combuf_t *combuf, const perform_tracing_args_t* a

DEBUG_BEGIN(cmdbuf, "yay tracing");

// Feed tlas with dynamic data
RT_DynamicModelProcessFrame();

// TODO move this to "TLAS producer"
g_rtx.res[ExternalResource_tlas].resource = RT_VkAccelPrepareTlas(combuf);

Expand Down Expand Up @@ -542,6 +539,13 @@ void VK_RayFrameEnd(const vk_ray_frame_render_args_t* args)

ASSERT(g_rtx.mainpipe_out);

// Feed tlas with dynamic data
RT_DynamicModelProcessFrame();

// Do not draw when we have no swapchain
if (args->dst.image_view == VK_NULL_HANDLE)
return;

if (g_ray_model_state.frame.instances_count == 0) {
const r_vkimage_blit_args blit_args = {
.in_stage = VK_PIPELINE_STAGE_TRANSFER_BIT,
Expand Down
20 changes: 13 additions & 7 deletions ref/vk/vk_swapchain.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ static void destroySwapchainAndFramebuffers( VkSwapchainKHR swapchain ) {
vkDestroySwapchainKHR(vk_core.device, swapchain, NULL);
}

qboolean recreateSwapchain( qboolean force ) {
static qboolean recreateSwapchainIfNeeded( qboolean force ) {
const uint32_t prev_num_images = g_swapchain.num_images;
uint32_t new_width, new_height;

Expand All @@ -80,6 +80,10 @@ qboolean recreateSwapchain( qboolean force ) {
new_width = clamp_u32(new_width, surface_caps.minImageExtent.width, surface_caps.maxImageExtent.width);
new_height = clamp_u32(new_height, surface_caps.minImageExtent.height, surface_caps.maxImageExtent.height);

if (new_height == 0 || new_width == 0) {
return false;
}

if (new_width == g_swapchain.width && new_height == g_swapchain.height && !force)
return true;

Expand Down Expand Up @@ -187,7 +191,7 @@ qboolean R_VkSwapchainInit( VkRenderPass render_pass, VkFormat depth_format ) {
g_swapchain.render_pass = render_pass;
g_swapchain.depth_format = depth_format;

return recreateSwapchain( true );
return recreateSwapchainIfNeeded( true );
}

void R_VkSwapchainShutdown( void ) {
Expand All @@ -201,7 +205,9 @@ r_vk_swapchain_framebuffer_t R_VkSwapchainAcquire( VkSemaphore sem_image_availa

for (;;) {
// Check that swapchain has the same size
recreateSwapchain(!!g_swapchain.recreate_requested);
if (!recreateSwapchainIfNeeded(!!g_swapchain.recreate_requested)) {
goto finalize;
}

APROF_SCOPE_DECLARE_BEGIN_EX(vkAcquireNextImageKHR, "vkAcquireNextImageKHR", APROF_SCOPE_FLAG_WAIT);
const VkResult acquire_result = vkAcquireNextImageKHR(vk_core.device, g_swapchain.swapchain, UINT64_MAX, sem_image_available, VK_NULL_HANDLE, &ret.index);
Expand All @@ -223,12 +229,12 @@ r_vk_swapchain_framebuffer_t R_VkSwapchainAcquire( VkSemaphore sem_image_availa
case VK_ERROR_DEVICE_LOST:
gEngine.Host_Error("vkAcquireNextImageKHR returned %s, this is unrecoverable, crashing.\n", R_VkResultName(acquire_result));
XVK_CHECK(acquire_result);
return ret;
goto finalize;

case VK_TIMEOUT:
case VK_NOT_READY:
gEngine.Con_Printf(S_ERROR "vkAcquireNextImageKHR returned %s (%0#x), frame will be lost\n", R_VkResultName(acquire_result), acquire_result);
return ret;
goto finalize;

case VK_ERROR_OUT_OF_DATE_KHR:
case VK_ERROR_SURFACE_LOST_KHR:
Expand All @@ -238,7 +244,7 @@ r_vk_swapchain_framebuffer_t R_VkSwapchainAcquire( VkSemaphore sem_image_availa

if (g_swapchain.recreate_requested) {
gEngine.Con_Printf(S_WARN "second vkAcquireNextImageKHR failed with %s, frame will be lost\n", R_VkResultName(acquire_result));
return ret;
goto finalize;
}

++g_swapchain.recreate_requested;
Expand All @@ -254,8 +260,8 @@ r_vk_swapchain_framebuffer_t R_VkSwapchainAcquire( VkSemaphore sem_image_availa
ret.image = g_swapchain.images[ret.index];
ret.view = g_swapchain.image_views[ret.index];

finalize:
APROF_SCOPE_END(function);

return ret;
}

Expand Down

0 comments on commit 64e1a9b

Please sign in to comment.