Merge pull request #5 from Spirrwell/master

[WIP] Fixes for Vulkan backend
This commit is contained in:
Yamagi 2021-06-14 17:14:31 +02:00 committed by GitHub
commit a7e53dd827
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 131 additions and 99 deletions

View file

@ -253,8 +253,8 @@ extern qvktexture_t vk_colorbuffer;
extern qvktexture_t vk_colorbufferWarp;
// indicator if the frame is currently being rendered
extern qboolean vk_frameStarted;
// Indicates if the renderer needs to be restarted.
extern qboolean vk_restartNeeded;
// Indicates if the swap chain needs to be rebuilt.
extern qboolean vk_recreateSwapchainNeeded;
// is QVk initialized?
extern qboolean vk_initialized;
@ -295,6 +295,7 @@ const char* QVk_GetError(VkResult errorCode);
VkResult QVk_BeginFrame(const VkViewport* viewport, const VkRect2D* scissor);
VkResult QVk_EndFrame(qboolean force);
void QVk_BeginRenderpass(qvkrenderpasstype_t rpType);
qboolean QVk_RecreateSwapchain();
void QVk_FreeStagingBuffer(qvkstagingbuffer_t *buffer);
VkResult QVk_CreateBuffer(VkDeviceSize size, qvkbuffer_t *dstBuffer, const qvkbufferopts_t options);
void QVk_FreeBuffer(qvkbuffer_t *buffer);

View file

@ -123,8 +123,8 @@ static uint32_t vk_imageIndex = 0;
static int vk_activeStagingBuffer = 0;
// started rendering frame?
qboolean vk_frameStarted = false;
// the renderer needs to be restarted.
qboolean vk_restartNeeded = false;
// the swap chain needs to be rebuilt.
qboolean vk_recreateSwapchainNeeded = false;
// is QVk initialized?
qboolean vk_initialized = false;
@ -1363,7 +1363,6 @@ static void CreatePipelines()
for (int i = 0; i < RP_COUNT; ++i)
{
vk_drawModelPipelineFan[i].topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
vk_drawModelPipelineFan[i].blendOpts.blendEnable = VK_TRUE;
QVk_CreatePipeline(samplerUboDsLayouts, 2, &vertInfoRGB_RGBA_RG, &vk_drawModelPipelineFan[i], &vk_renderpasses[i], shaders, 2);
QVk_DebugSetObjectName((uint64_t)vk_drawModelPipelineFan[i].layout, VK_OBJECT_TYPE_PIPELINE_LAYOUT,
va("Pipeline Layout: draw model: fan (%s)", renderpassObjectNames[i]));
@ -2071,18 +2070,10 @@ VkResult QVk_BeginFrame(const VkViewport* viewport, const VkRect2D* scissor)
ReleaseSwapBuffers();
static int restartcount;
VkResult result = vkAcquireNextImageKHR(vk_device.logical, vk_swapchain.sc, 500000000, vk_imageAvailableSemaphores[vk_activeBufferIdx], VK_NULL_HANDLE, &vk_imageIndex);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_SURFACE_LOST_KHR || result == VK_TIMEOUT)
{
if (restartcount > 2)
{
Sys_Error("%s(): tried to restart 3 times after vkAcquireNextImageKHR: %s", __func__, QVk_GetError(result));
}
else
{
restartcount++;
}
vk_recreateSwapchainNeeded = true;
// for VK_OUT_OF_DATE_KHR and VK_SUBOPTIMAL_KHR it'd be fine to just rebuild the swapchain but let's take the easy way out and restart Vulkan.
R_Printf(PRINT_ALL, "%s(): received %s after vkAcquireNextImageKHR - restarting video!\n", __func__, QVk_GetError(result));
@ -2093,7 +2084,6 @@ VkResult QVk_BeginFrame(const VkViewport* viewport, const VkRect2D* scissor)
Sys_Error("%s(): unexpected error after vkAcquireNextImageKHR: %s", __func__, QVk_GetError(result));
}
restartcount = 0;
vk_activeCmdbuffer = vk_commandbuffers[vk_activeBufferIdx];
// swap dynamic buffers
@ -2185,7 +2175,7 @@ VkResult QVk_EndFrame(qboolean force)
if (renderResult == VK_ERROR_OUT_OF_DATE_KHR || renderResult == VK_SUBOPTIMAL_KHR || renderResult == VK_ERROR_SURFACE_LOST_KHR)
{
R_Printf(PRINT_ALL, "%s(): received %s after vkQueuePresentKHR - will restart video!\n", __func__, QVk_GetError(renderResult));
vk_restartNeeded = true;
vk_recreateSwapchainNeeded = true;
}
else if (renderResult != VK_SUCCESS)
{
@ -2266,22 +2256,45 @@ void QVk_BeginRenderpass(qvkrenderpasstype_t rpType)
vk_state.current_renderpass = rpType;
}
#if 0
void QVk_RecreateSwapchain()
qboolean QVk_RecreateSwapchain()
{
VkResult result = VK_SUCCESS;
vkDeviceWaitIdle( vk_device.logical );
DestroyFramebuffers();
DestroyImageViews();
VK_VERIFY(QVk_CreateSwapchain());
if (!QVk_CheckExtent())
return false;
VK_VERIFY(result = QVk_CreateSwapchain());
if (result != VK_SUCCESS)
return false;
vk_viewport.width = (float)vid.width;
vk_viewport.height = (float)vid.height;
vk_scissor.extent = vk_swapchain.extent;
DestroyDrawBuffers();
CreateDrawBuffers();
VK_VERIFY(CreateImageViews());
VK_VERIFY(CreateFramebuffers());
VK_VERIFY(result = CreateImageViews());
if (result != VK_SUCCESS)
return false;
VK_VERIFY(result = CreateFramebuffers());
if (result != VK_SUCCESS)
return false;
QVk_UpdateTextureSampler(&vk_colorbuffer, S_NEAREST_UNNORMALIZED, false);
QVk_UpdateTextureSampler(&vk_colorbufferWarp, S_NEAREST_UNNORMALIZED, false);
vk_recreateSwapchainNeeded = false;
return true;
}
#endif
uint8_t *QVk_GetVertexBuffer(VkDeviceSize size, VkBuffer *dstBuffer, VkDeviceSize *dstOffset)
{

View file

@ -270,13 +270,11 @@ RE_Draw_StretchRaw
*/
extern unsigned r_rawpalette[256];
extern qvktexture_t vk_rawTexture;
static int scaled_size = 512;
void RE_Draw_StretchRaw (int x, int y, int w, int h, int cols, int rows, byte *data)
{
int i, j;
int hscale, vscale;
unsigned *dest;
byte *source;
byte *image_scaled;
@ -313,45 +311,36 @@ void RE_Draw_StretchRaw (int x, int y, int w, int h, int cols, int rows, byte *d
image_scaled = data;
}
if (vk_rawTexture.resource.image == VK_NULL_HANDLE)
{
// get power of two image size,
// could be updated only size only after recreate texture
for (scaled_size = 512; scaled_size < cols && scaled_size < rows; scaled_size <<= 1)
;
}
raw_image32 = malloc(scaled_size * scaled_size * sizeof(unsigned));
hscale = cols * 0x10000 / scaled_size;
vscale = rows * 0x10000 / scaled_size;
raw_image32 = malloc(cols * rows * sizeof(unsigned));
source = image_scaled;
dest = raw_image32;
for (i = 0; i < scaled_size; i++)
for (i = 0; i < rows; ++i)
{
for (j = 0; j < scaled_size; j++)
int rowOffset = i * cols;
for (j = 0; j < cols; ++j)
{
*dest = r_rawpalette[*(source + ((j * hscale) >> 16))];
dest ++;
byte palIdx = source[rowOffset + j];
dest[rowOffset + j] = r_rawpalette[palIdx];
}
source = image_scaled + (((i * vscale) >> 16) * cols);
}
if (vk_retexturing->value)
{
free(image_scaled);
SmoothColorImage(raw_image32, scaled_size * scaled_size, scaled_size >> 7);
int scaled_size = cols * rows;
SmoothColorImage(raw_image32, scaled_size, (scaled_size) >> 7);
}
if (vk_rawTexture.resource.image != VK_NULL_HANDLE)
{
QVk_UpdateTextureData(&vk_rawTexture, (unsigned char*)raw_image32, 0, 0, scaled_size, scaled_size);
QVk_UpdateTextureData(&vk_rawTexture, (unsigned char*)raw_image32, 0, 0, cols, rows);
}
else
{
QVVKTEXTURE_CLEAR(vk_rawTexture);
QVk_CreateTexture(&vk_rawTexture, (unsigned char*)raw_image32, scaled_size, scaled_size,
QVk_CreateTexture(&vk_rawTexture, (unsigned char*)raw_image32, cols, rows,
vk_current_sampler, false);
QVk_DebugSetObjectName((uint64_t)vk_rawTexture.resource.image,
VK_OBJECT_TYPE_IMAGE, "Image: raw texture");

View file

@ -846,11 +846,78 @@ static void Vk_LightScaleTexture (byte *in, int inwidth, int inheight)
/*
===============
Vk_Upload32
Vk_Upload32Native
Returns number of mip levels
Returns number of mip levels and scales native resolution
if vk_picmip is set. Does not use power of 2 scaling.
===============
*/
static uint32_t Vk_Upload32Native (byte *data, int width, int height, imagetype_t type,
byte **texBuffer, int *upload_width, int *upload_height)
{
int scaled_width = width, scaled_height = height;
int miplevel = 1;
*texBuffer = NULL;
if (type != it_pic)
{
// let people sample down the world textures for speed
scaled_width >>= (int)vk_picmip->value;
scaled_height >>= (int)vk_picmip->value;
}
if (scaled_width < 1)
scaled_width = 1;
if (scaled_height < 1)
scaled_height = 1;
if (scaled_width == width && scaled_height == height)
{
// We can just send the data back and avoid an extra allocation/copy
*texBuffer = data;
}
else
{
*texBuffer = malloc(scaled_width * scaled_height * 4);
if (!*texBuffer)
ri.Sys_Error(ERR_DROP, "%s: too big", __func__);
ResizeSTB(data, width, height,
*texBuffer, scaled_width, scaled_height);
}
*upload_width = scaled_width;
*upload_height = scaled_height;
// world textures
if (type != it_pic && type != it_sky)
{
Vk_LightScaleTexture(*texBuffer, scaled_width, scaled_height);
}
while (scaled_width > 1 || scaled_height > 1)
{
scaled_width >>= 1;
scaled_height >>= 1;
if (scaled_width < 1)
scaled_width = 1;
if (scaled_height < 1)
scaled_height = 1;
miplevel++;
}
return miplevel;
}
/*
===============
Vk_Upload32
Returns number of mip levels and scales to nearest power of 2.
===============
*/
#if 0
static uint32_t Vk_Upload32 (byte *data, int width, int height, imagetype_t type,
byte **texBuffer, int *upload_width, int *upload_height)
{
@ -911,6 +978,7 @@ static uint32_t Vk_Upload32 (byte *data, int width, int height, imagetype_t type
return miplevel;
}
#endif
/*
===============
@ -977,8 +1045,12 @@ static uint32_t Vk_Upload8 (byte *data, int width, int height, imagetype_t type,
SmoothColorImage(trans, s, s >> 7);
}
miplevel = Vk_Upload32((byte *)trans, width, height, type, texBuffer, upload_width, upload_height);
free(trans);
miplevel = Vk_Upload32Native((byte *)trans, width, height, type, texBuffer, upload_width, upload_height);
// Only free if *texBuffer isn't the image data we sent
if (!texBuffer || *texBuffer != (byte *)trans)
free(trans);
return miplevel;
}
@ -1058,7 +1130,7 @@ Vk_LoadPic(char *name, byte *pic, int width, int realwidth,
}
}
else
image->vk_texture.mipLevels = Vk_Upload32(pic, width, height, image->type, &texBuffer, &upload_width, &upload_height);
image->vk_texture.mipLevels = Vk_Upload32Native(pic, width, height, image->type, &texBuffer, &upload_width, &upload_height);
image->upload_width = upload_width; // after power of 2 and scales
image->upload_height = upload_height;
@ -1075,7 +1147,7 @@ Vk_LoadPic(char *name, byte *pic, int width, int realwidth,
QVk_DebugSetObjectName((uint64_t)image->vk_texture.resource.memory,
VK_OBJECT_TYPE_DEVICE_MEMORY, "Memory: game textures");
if (texBuffer)
if (texBuffer && texBuffer != pic)
{
free(texBuffer);
}

View file

@ -1385,36 +1385,9 @@ RE_BeginFrame( float camera_separation )
return;
}
/* Some GPU drivers set a maximum extent size of 0x0 when
the window is minimized. But a swapchain with an extent
size of 0x0 is invalid. This leads to the following
problem:
1. The window is minimized, the extent size changes and
the Vulkan state get's corrupted. QVk_EndFrame()
above or QVk_BeginFrame() below detect the Vulkan
state as corrupted, set vk_frameStated to false and
request a restart by setting vk_restartNeeded to
true.
2. RE_EndFrame() triggers the restart. QVk_Shutdown()
is successfull, but QVk_Init() can't create a valid
swapchains and errors out. An incomplete internal
renderer state is left behind. The only way out is
to trigger a full render restart.
3. The full renderer restart would lead to a restart
loop: Restart -> QVk_Init() fails -> restart -> ...
The only alternative is not to restart. Instead the
renderer could error out, that would print an error
message and quit the client.
Work around this by not starting the frame or restarting
the renderer, as long as the maximum extent size is 0x0.
This is part 1 (the state corruption war detect in the
last frame), part 2 (the state corruption was detected in
the current frame) is in RE_EndFrame(). */
if (vk_restartNeeded)
if (vk_recreateSwapchainNeeded)
{
if (!QVk_CheckExtent())
if (QVk_RecreateSwapchain() != true)
{
vk_frameStarted = false;
return;
@ -1422,8 +1395,7 @@ RE_BeginFrame( float camera_separation )
}
// 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
if (QVk_EndFrame(true) != VK_SUCCESS)
vk_restartNeeded = true;
QVk_EndFrame(true);
/*
** change modes if necessary
@ -1450,9 +1422,7 @@ RE_BeginFrame( float camera_separation )
}
}
if (QVk_BeginFrame(&vk_viewport, &vk_scissor) != VK_SUCCESS)
vk_restartNeeded = true;
else
if (QVk_BeginFrame(&vk_viewport, &vk_scissor) == VK_SUCCESS)
QVk_BeginRenderpass(RP_WORLD);
}
@ -1464,23 +1434,10 @@ RE_EndFrame
static void
RE_EndFrame( void )
{
if (QVk_EndFrame(false) != VK_SUCCESS)
vk_restartNeeded = true;
QVk_EndFrame(false);
// world has not rendered yet
world_rendered = false;
/* Part two of the 'maximum extent size may be 0x0'
* work around. See the explanation in RE_BeginFrame()
* for details. */
if (vk_restartNeeded)
{
if (QVk_CheckExtent())
{
QVk_Restart();
vk_restartNeeded = false;
}
}
}
/*

View file

@ -239,7 +239,7 @@ VkResult QVk_CreateSwapchain()
return res;
VK_VERIFY(vkGetSwapchainImagesKHR(vk_device.logical, vk_swapchain.sc, &imageCount, NULL));
vk_swapchain.images = (VkImage *)malloc(imageCount * sizeof(VkImage));
vk_swapchain.images = (VkImage *)realloc(vk_swapchain.images, imageCount * sizeof(VkImage));
vk_swapchain.imageCount = imageCount;
res = vkGetSwapchainImagesKHR(vk_device.logical, vk_swapchain.sc, &imageCount, vk_swapchain.images);