From e362e0280e61a1b383d677b76ab6fa700dd3020e Mon Sep 17 00:00:00 2001 From: Ricardo Garcia Date: Sun, 21 Mar 2021 00:26:25 +0100 Subject: [PATCH 1/3] Restart Vulkan renderer at EndFrame instead of BeginFrame This brings yquake2 closer to vkQuake2 regarding renderer restarts when the swapchain is out of date, among other situations that trigger a Vulkan renderer restart. Basically, the current behavior has the problem that when the renderer is restarted at the beginning of the frame, the models are lost and we end up with "ERROR: Mod_PointInLeaf: bad model" when attempting to render anything after that restart. To solve this, we move the restart logic to EndFrame and add a twist to it: we use a vid_refresh variable to signal the server that the client needs re-registration before starting the next frame cleanly, which will trigger the registration logic to prepare the models again. --- src/client/refresh/vk/header/local.h | 1 + src/client/refresh/vk/vk_common.c | 4 +++- src/client/refresh/vk/vk_rmain.c | 34 ++++++++++++++-------------- src/client/vid/vid.c | 8 +++++++ 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/client/refresh/vk/header/local.h b/src/client/refresh/vk/header/local.h index b1ffc7d9..b2760ddc 100644 --- a/src/client/refresh/vk/header/local.h +++ b/src/client/refresh/vk/header/local.h @@ -152,6 +152,7 @@ extern cvar_t *vk_pixel_size; extern cvar_t *r_fixsurfsky; extern cvar_t *vid_fullscreen; +extern cvar_t *vid_refresh; extern cvar_t *vid_gamma; extern int c_visible_lightmaps; diff --git a/src/client/refresh/vk/vk_common.c b/src/client/refresh/vk/vk_common.c index 08ffb225..81535598 100644 --- a/src/client/refresh/vk/vk_common.c +++ b/src/client/refresh/vk/vk_common.c @@ -1664,6 +1664,7 @@ void QVk_Restart(void) if (!QVk_Init()) ri.Sys_Error(ERR_FATAL, "Unable to restart Vulkan renderer"); QVk_PostInit(); + vid_refresh->modified = true; } void QVk_PostInit(void) @@ -2097,7 +2098,8 @@ VkResult QVk_EndFrame(qboolean force) { // continue only if QVk_BeginFrame() had been previously issued if (!vk_frameStarted) - return VK_NOT_READY; + return VK_SUCCESS; + // this may happen if Sys_Error is issued mid-frame, so we need to properly advance the draw pipeline if (force) { diff --git a/src/client/refresh/vk/vk_rmain.c b/src/client/refresh/vk/vk_rmain.c index 80abeb80..41322e39 100644 --- a/src/client/refresh/vk/vk_rmain.c +++ b/src/client/refresh/vk/vk_rmain.c @@ -130,6 +130,7 @@ cvar_t *vk_nolerp_list; cvar_t *r_fixsurfsky; cvar_t *vid_fullscreen; +cvar_t *vid_refresh; cvar_t *vid_gamma; static cvar_t *viewsize; @@ -1194,6 +1195,7 @@ R_Register( void ) ri.Cvar_Set("r_msaa_samples", "0"); vid_fullscreen = ri.Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); + vid_refresh = ri.Cvar_Get("vid_refresh", "0", CVAR_NOSET); vid_gamma = ri.Cvar_Get("vid_gamma", "1.0", CVAR_ARCHIVE); viewsize = ri.Cvar_Get("viewsize", "100", CVAR_ARCHIVE); @@ -1376,7 +1378,8 @@ RE_BeginFrame( float camera_separation ) world_rendered = false; // if ri.Sys_Error() had been issued mid-frame, we might end up here without properly submitting the image, so call QVk_EndFrame to be safe - QVk_EndFrame(true); + if (QVk_EndFrame(true) != VK_SUCCESS) + vk_restartNeeded = true; /* ** change modes if necessary @@ -1407,21 +1410,10 @@ RE_BeginFrame( float camera_separation ) } } - if (vk_restartNeeded) - { - QVk_Restart(); - vk_restartNeeded = false; - } - - for (;;) - { - VkResult swapChainValid = QVk_BeginFrame(&vk_viewport, &vk_scissor); - if (swapChainValid == VK_SUCCESS) - break; - QVk_Restart(); - } - - QVk_BeginRenderpass(RP_WORLD); + if (QVk_BeginFrame(&vk_viewport, &vk_scissor) != VK_SUCCESS) + vk_restartNeeded = true; + else + QVk_BeginRenderpass(RP_WORLD); } /* @@ -1432,9 +1424,17 @@ RE_EndFrame static void RE_EndFrame( void ) { - QVk_EndFrame(false); + if (QVk_EndFrame(false) != VK_SUCCESS) + vk_restartNeeded = true; + // world has not rendered yet world_rendered = false; + + if (vk_restartNeeded) + { + QVk_Restart(); + vk_restartNeeded = false; + } } /* diff --git a/src/client/vid/vid.c b/src/client/vid/vid.c index 0857e573..c2c6f61f 100644 --- a/src/client/vid/vid.c +++ b/src/client/vid/vid.c @@ -286,6 +286,7 @@ VID_GetModeInfo(int *width, int *height, int mode) // Global console variables. cvar_t *vid_gamma; cvar_t *vid_fullscreen; +cvar_t *vid_refresh; cvar_t *vid_renderer; // Global video state, used throughout the client. @@ -489,6 +490,12 @@ VID_CheckChanges(void) // Unblock the client. cls.disable_screen = false; } + + if (vid_refresh->modified) + { + vid_refresh->modified = false; + cl.refresh_prepped = false; + } } /* @@ -500,6 +507,7 @@ VID_Init(void) // Console variables vid_gamma = Cvar_Get("vid_gamma", "1.0", CVAR_ARCHIVE); vid_fullscreen = Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); + vid_refresh = Cvar_Get("vid_refresh", "0", CVAR_NOSET); vid_renderer = Cvar_Get("vid_renderer", "gl1", CVAR_ARCHIVE); // Commands From 17c2c94f078542202c7db35c94350a351e1bbae5 Mon Sep 17 00:00:00 2001 From: Ricardo Garcia Date: Sun, 21 Mar 2021 00:20:12 +0100 Subject: [PATCH 2/3] Fix memory leak when restarting the Vulkan renderer models_known needs to be freed after freeing its contents. --- src/client/refresh/vk/header/model.h | 1 + src/client/refresh/vk/vk_common.c | 1 + src/client/refresh/vk/vk_model.c | 13 ++++++++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/client/refresh/vk/header/model.h b/src/client/refresh/vk/header/model.h index b3484f85..e38cc33f 100644 --- a/src/client/refresh/vk/header/model.h +++ b/src/client/refresh/vk/header/model.h @@ -248,3 +248,4 @@ int Hunk_End (void); void Hunk_Free (void *base); void Mod_FreeAll (void); +void Mod_FreeModelsKnown (void); diff --git a/src/client/refresh/vk/vk_common.c b/src/client/refresh/vk/vk_common.c index 81535598..6038a0ea 100644 --- a/src/client/refresh/vk/vk_common.c +++ b/src/client/refresh/vk/vk_common.c @@ -1654,6 +1654,7 @@ void QVk_WaitAndShutdownAll (void) } Mod_FreeAll(); + Mod_FreeModelsKnown(); Vk_ShutdownImages(); QVk_Shutdown(); } diff --git a/src/client/refresh/vk/vk_model.c b/src/client/refresh/vk/vk_model.c index 7ba4900c..831c7407 100644 --- a/src/client/refresh/vk/vk_model.c +++ b/src/client/refresh/vk/vk_model.c @@ -101,7 +101,7 @@ void Mod_Reallocate (void) models_known_max *= 2; // free up Mod_FreeAll(); - free(models_known); + Mod_FreeModelsKnown(); } if (models_known_max < (mod_max * 4)) @@ -177,6 +177,17 @@ void Mod_FreeAll (void) } } +/* +================ +Mod_FreeModelsKnown +================ +*/ +void Mod_FreeModelsKnown (void) +{ + free(models_known); + models_known = NULL; +} + /* =============================================================================== From 6c3261180b467df640d952d9ac170633d2a6a6e0 Mon Sep 17 00:00:00 2001 From: Ricardo Garcia Date: Mon, 29 Mar 2021 22:15:28 +0200 Subject: [PATCH 3/3] Use vid_renderer instead of vid_refresh to re-register clients --- src/client/refresh/vk/header/local.h | 2 +- src/client/refresh/vk/vk_common.c | 2 +- src/client/refresh/vk/vk_rmain.c | 4 ++-- src/client/vid/vid.c | 9 +++++---- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/client/refresh/vk/header/local.h b/src/client/refresh/vk/header/local.h index b2760ddc..30b2c558 100644 --- a/src/client/refresh/vk/header/local.h +++ b/src/client/refresh/vk/header/local.h @@ -152,7 +152,7 @@ extern cvar_t *vk_pixel_size; extern cvar_t *r_fixsurfsky; extern cvar_t *vid_fullscreen; -extern cvar_t *vid_refresh; +extern cvar_t *vid_renderer; extern cvar_t *vid_gamma; extern int c_visible_lightmaps; diff --git a/src/client/refresh/vk/vk_common.c b/src/client/refresh/vk/vk_common.c index 6038a0ea..ee64bf2d 100644 --- a/src/client/refresh/vk/vk_common.c +++ b/src/client/refresh/vk/vk_common.c @@ -1665,7 +1665,7 @@ void QVk_Restart(void) if (!QVk_Init()) ri.Sys_Error(ERR_FATAL, "Unable to restart Vulkan renderer"); QVk_PostInit(); - vid_refresh->modified = true; + vid_renderer->modified = true; } void QVk_PostInit(void) diff --git a/src/client/refresh/vk/vk_rmain.c b/src/client/refresh/vk/vk_rmain.c index 41322e39..3c0e2958 100644 --- a/src/client/refresh/vk/vk_rmain.c +++ b/src/client/refresh/vk/vk_rmain.c @@ -130,7 +130,7 @@ cvar_t *vk_nolerp_list; cvar_t *r_fixsurfsky; cvar_t *vid_fullscreen; -cvar_t *vid_refresh; +cvar_t *vid_renderer; cvar_t *vid_gamma; static cvar_t *viewsize; @@ -1195,7 +1195,7 @@ R_Register( void ) ri.Cvar_Set("r_msaa_samples", "0"); vid_fullscreen = ri.Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); - vid_refresh = ri.Cvar_Get("vid_refresh", "0", CVAR_NOSET); + vid_renderer = ri.Cvar_Get("vid_renderer", "gl1", CVAR_ARCHIVE); vid_gamma = ri.Cvar_Get("vid_gamma", "1.0", CVAR_ARCHIVE); viewsize = ri.Cvar_Get("viewsize", "100", CVAR_ARCHIVE); diff --git a/src/client/vid/vid.c b/src/client/vid/vid.c index c2c6f61f..e6da7d5b 100644 --- a/src/client/vid/vid.c +++ b/src/client/vid/vid.c @@ -286,7 +286,6 @@ VID_GetModeInfo(int *width, int *height, int mode) // Global console variables. cvar_t *vid_gamma; cvar_t *vid_fullscreen; -cvar_t *vid_refresh; cvar_t *vid_renderer; // Global video state, used throughout the client. @@ -487,13 +486,16 @@ VID_CheckChanges(void) } } + // Ignore possible changes in vid_renderer above. + vid_renderer->modified = false; + // Unblock the client. cls.disable_screen = false; } - if (vid_refresh->modified) + if (vid_renderer->modified) { - vid_refresh->modified = false; + vid_renderer->modified = false; cl.refresh_prepped = false; } } @@ -507,7 +509,6 @@ VID_Init(void) // Console variables vid_gamma = Cvar_Get("vid_gamma", "1.0", CVAR_ARCHIVE); vid_fullscreen = Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); - vid_refresh = Cvar_Get("vid_refresh", "0", CVAR_NOSET); vid_renderer = Cvar_Get("vid_renderer", "gl1", CVAR_ARCHIVE); // Commands