From aa2baedd97f98b0452855d803be7437972c3e814 Mon Sep 17 00:00:00 2001 From: Ricardo Garcia Date: Tue, 12 Jan 2021 22:53:21 +0100 Subject: [PATCH 1/2] Fix skybox edges being visible in Vulkan Skybox edges are sometimes visible in Vulkan, specially in lower screen resolutions or when vk_pixel_size is used to the same effect. To avoid this problem, draw the skybox using CLAMP_TO_EDGE addressing mode in samplers. In order to do that, the number of texture samplers is doubled and a second set of samplers with the new addressing mode are created, and used only when drawing the skybox. --- src/client/refresh/vk/header/qvk.h | 5 ++- src/client/refresh/vk/vk_common.c | 67 +++++++++++++++++------------- src/client/refresh/vk/vk_draw.c | 2 +- src/client/refresh/vk/vk_image.c | 12 +++--- src/client/refresh/vk/vk_rsurf.c | 4 +- 5 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/client/refresh/vk/header/qvk.h b/src/client/refresh/vk/header/qvk.h index f7361b8e..9b4613c2 100644 --- a/src/client/refresh/vk/header/qvk.h +++ b/src/client/refresh/vk/header/qvk.h @@ -86,6 +86,7 @@ typedef struct VkFormat format; VkDescriptorSet descriptorSet; uint32_t mipLevels; + qboolean clampToEdge; } qvktexture_t; #define QVVKTEXTURE_INIT { \ @@ -280,9 +281,9 @@ VkResult QVk_CreateImageView(const VkImage *image, VkImageAspectFlags aspectFlag VkResult QVk_CreateImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, qvktexture_t *texture); void QVk_CreateDepthBuffer(VkSampleCountFlagBits sampleCount, qvktexture_t *depthBuffer); void QVk_CreateColorBuffer(VkSampleCountFlagBits sampleCount, qvktexture_t *colorBuffer, int extraFlags); -void QVk_CreateTexture(qvktexture_t *texture, const unsigned char *data, uint32_t width, uint32_t height, qvksampler_t samplerType); +void QVk_CreateTexture(qvktexture_t *texture, const unsigned char *data, uint32_t width, uint32_t height, qvksampler_t samplerType, qboolean clampToEdge); void QVk_UpdateTextureData(qvktexture_t *texture, const unsigned char *data, uint32_t offset_x, uint32_t offset_y, uint32_t width, uint32_t height); -VkSampler QVk_UpdateTextureSampler(qvktexture_t *texture, qvksampler_t samplerType); +VkSampler QVk_UpdateTextureSampler(qvktexture_t *texture, qvksampler_t samplerType, qboolean clampToEdge); void QVk_ReadPixels(uint8_t *dstBuffer, uint32_t width, uint32_t height); VkResult QVk_BeginCommand(const VkCommandBuffer *commandBuffer); void QVk_SubmitCommand(const VkCommandBuffer *commandBuffer, const VkQueue *queue); diff --git a/src/client/refresh/vk/vk_common.c b/src/client/refresh/vk/vk_common.c index 3874772a..6c46f153 100644 --- a/src/client/refresh/vk/vk_common.c +++ b/src/client/refresh/vk/vk_common.c @@ -148,7 +148,7 @@ qvkpipeline_t vk_worldWarpPipeline = QVKPIPELINE_INIT; qvkpipeline_t vk_postprocessPipeline = QVKPIPELINE_INIT; // samplers -static VkSampler vk_samplers[S_SAMPLER_CNT]; +static VkSampler vk_samplers[S_SAMPLER_CNT * 2]; // Vulkan function pointers PFN_vkCreateDebugUtilsMessengerEXT qvkCreateDebugUtilsMessengerEXT; @@ -811,7 +811,7 @@ static void CreateDescriptorSetLayouts() } // internal helper -static void CreateSamplers() +static void CreateSamplersHelper(VkSampler *samplers, VkSamplerAddressMode addressMode) { VkSamplerCreateInfo samplerInfo = { .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, @@ -820,9 +820,9 @@ static void CreateSamplers() .magFilter = VK_FILTER_NEAREST, .minFilter = VK_FILTER_NEAREST, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, - .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeU = addressMode, + .addressModeV = addressMode, + .addressModeW = addressMode, .mipLodBias = 0.f, .anisotropyEnable = VK_FALSE, .maxAnisotropy = 1.f, @@ -834,22 +834,22 @@ static void CreateSamplers() .unnormalizedCoordinates = VK_FALSE }; - VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &vk_samplers[S_NEAREST])); - QVk_DebugSetObjectName((uint64_t)vk_samplers[S_NEAREST], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_NEAREST"); + VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &samplers[S_NEAREST])); + QVk_DebugSetObjectName((uint64_t)samplers[S_NEAREST], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_NEAREST"); samplerInfo.maxLod = FLT_MAX; - VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &vk_samplers[S_MIPMAP_NEAREST])); - QVk_DebugSetObjectName((uint64_t)vk_samplers[S_MIPMAP_NEAREST], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_MIPMAP_NEAREST"); + VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &samplers[S_MIPMAP_NEAREST])); + QVk_DebugSetObjectName((uint64_t)samplers[S_MIPMAP_NEAREST], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_MIPMAP_NEAREST"); samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &vk_samplers[S_MIPMAP_LINEAR])); - QVk_DebugSetObjectName((uint64_t)vk_samplers[S_MIPMAP_LINEAR], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_MIPMAP_LINEAR"); + VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &samplers[S_MIPMAP_LINEAR])); + QVk_DebugSetObjectName((uint64_t)samplers[S_MIPMAP_LINEAR], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_MIPMAP_LINEAR"); samplerInfo.maxLod = 1.f; - VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &vk_samplers[S_LINEAR])); - QVk_DebugSetObjectName((uint64_t)vk_samplers[S_LINEAR], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_LINEAR"); + VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &samplers[S_LINEAR])); + QVk_DebugSetObjectName((uint64_t)samplers[S_LINEAR], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_LINEAR"); // aniso samplers assert((vk_device.properties.limits.maxSamplerAnisotropy > 1.f) && "maxSamplerAnisotropy is 1"); @@ -864,29 +864,36 @@ static void CreateSamplers() } samplerInfo.maxLod = 1.f; - VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &vk_samplers[S_ANISO_NEAREST])); - QVk_DebugSetObjectName((uint64_t)vk_samplers[S_ANISO_NEAREST], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_ANISO_NEAREST"); + VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &samplers[S_ANISO_NEAREST])); + QVk_DebugSetObjectName((uint64_t)samplers[S_ANISO_NEAREST], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_ANISO_NEAREST"); samplerInfo.maxLod = FLT_MAX; - VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &vk_samplers[S_ANISO_MIPMAP_NEAREST])); - QVk_DebugSetObjectName((uint64_t)vk_samplers[S_ANISO_MIPMAP_NEAREST], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_ANISO_MIPMAP_NEAREST"); + VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &samplers[S_ANISO_MIPMAP_NEAREST])); + QVk_DebugSetObjectName((uint64_t)samplers[S_ANISO_MIPMAP_NEAREST], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_ANISO_MIPMAP_NEAREST"); samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &vk_samplers[S_ANISO_MIPMAP_LINEAR])); - QVk_DebugSetObjectName((uint64_t)vk_samplers[S_ANISO_MIPMAP_LINEAR], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_ANISO_MIPMAP_LINEAR"); + VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &samplers[S_ANISO_MIPMAP_LINEAR])); + QVk_DebugSetObjectName((uint64_t)samplers[S_ANISO_MIPMAP_LINEAR], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_ANISO_MIPMAP_LINEAR"); samplerInfo.maxLod = 1.f; - VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &vk_samplers[S_ANISO_LINEAR])); - QVk_DebugSetObjectName((uint64_t)vk_samplers[S_ANISO_LINEAR], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_ANISO_LINEAR"); + VK_VERIFY(vkCreateSampler(vk_device.logical, &samplerInfo, NULL, &samplers[S_ANISO_LINEAR])); + QVk_DebugSetObjectName((uint64_t)samplers[S_ANISO_LINEAR], VK_OBJECT_TYPE_SAMPLER, "Sampler: S_ANISO_LINEAR"); +} + +// internal helper +static void CreateSamplers() +{ + CreateSamplersHelper(vk_samplers, VK_SAMPLER_ADDRESS_MODE_REPEAT); + CreateSamplersHelper(vk_samplers + S_SAMPLER_CNT, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE); } // internal helper static void DestroySamplers() { int i; - for (i = 0; i < S_SAMPLER_CNT; ++i) + for (i = 0; i < S_SAMPLER_CNT * 2; ++i) { if (vk_samplers[i] != VK_NULL_HANDLE) vkDestroySampler(vk_device.logical, vk_samplers[i], NULL); @@ -1923,9 +1930,9 @@ qboolean QVk_Init(SDL_Window *window) }; VK_VERIFY(vkAllocateDescriptorSets(vk_device.logical, &dsAllocInfo, &vk_colorbuffer.descriptorSet)); - QVk_UpdateTextureSampler(&vk_colorbuffer, S_NEAREST); + QVk_UpdateTextureSampler(&vk_colorbuffer, S_NEAREST, false); VK_VERIFY(vkAllocateDescriptorSets(vk_device.logical, &dsAllocInfo, &vk_colorbufferWarp.descriptorSet)); - QVk_UpdateTextureSampler(&vk_colorbufferWarp, S_NEAREST); + QVk_UpdateTextureSampler(&vk_colorbufferWarp, S_NEAREST, false); QVk_DebugSetObjectName((uint64_t)vk_colorbuffer.descriptorSet, VK_OBJECT_TYPE_DESCRIPTOR_SET, "Descriptor Set: World Color Buffer"); @@ -2364,12 +2371,16 @@ void QVk_SubmitStagingBuffers() } } -VkSampler QVk_UpdateTextureSampler(qvktexture_t *texture, qvksampler_t samplerType) +VkSampler QVk_UpdateTextureSampler(qvktexture_t *texture, qvksampler_t samplerType, qboolean clampToEdge) { - assert((vk_samplers[samplerType] != VK_NULL_HANDLE) && "Sampler is VK_NULL_HANDLE!"); + const int samplerIndex = samplerType + (clampToEdge ? S_SAMPLER_CNT : 0); + + assert((vk_samplers[samplerIndex] != VK_NULL_HANDLE) && "Sampler is VK_NULL_HANDLE!"); + + texture->clampToEdge = clampToEdge; VkDescriptorImageInfo dImgInfo = { - .sampler = vk_samplers[samplerType], + .sampler = vk_samplers[samplerIndex], .imageView = texture->imageView, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }; @@ -2389,7 +2400,7 @@ VkSampler QVk_UpdateTextureSampler(qvktexture_t *texture, qvksampler_t samplerTy vkUpdateDescriptorSets(vk_device.logical, 1, &writeSet, 0, NULL); - return vk_samplers[samplerType]; + return vk_samplers[samplerIndex]; } void QVk_DrawColorRect(float *ubo, VkDeviceSize uboSize, qvkrenderpasstype_t rpType) diff --git a/src/client/refresh/vk/vk_draw.c b/src/client/refresh/vk/vk_draw.c index 24398467..ab18fd43 100644 --- a/src/client/refresh/vk/vk_draw.c +++ b/src/client/refresh/vk/vk_draw.c @@ -317,7 +317,7 @@ void RE_Draw_StretchRaw (int x, int y, int w, int h, int cols, int rows, byte *d { QVVKTEXTURE_CLEAR(vk_rawTexture); QVk_CreateTexture(&vk_rawTexture, (unsigned char*)raw_image32, scaled_size, scaled_size, - vk_current_sampler); + vk_current_sampler, false); QVk_DebugSetObjectName((uint64_t)vk_rawTexture.resource.image, VK_OBJECT_TYPE_IMAGE, "Image: raw texture"); QVk_DebugSetObjectName((uint64_t)vk_rawTexture.imageView, diff --git a/src/client/refresh/vk/vk_image.c b/src/client/refresh/vk/vk_image.c index 92294821..771a11cc 100644 --- a/src/client/refresh/vk/vk_image.c +++ b/src/client/refresh/vk/vk_image.c @@ -366,7 +366,7 @@ void QVk_CreateColorBuffer(VkSampleCountFlagBits sampleCount, qvktexture_t *colo VK_VERIFY(QVk_CreateImageView(&colorBuffer->resource.image, VK_IMAGE_ASPECT_COLOR_BIT, &colorBuffer->imageView, colorBuffer->format, colorBuffer->mipLevels)); } -void QVk_CreateTexture(qvktexture_t *texture, const unsigned char *data, uint32_t width, uint32_t height, qvksampler_t samplerType) +void QVk_CreateTexture(qvktexture_t *texture, const unsigned char *data, uint32_t width, uint32_t height, qvksampler_t samplerType, qboolean clampToEdge) { createTextureImage(texture, data, width, height); VK_VERIFY(QVk_CreateImageView(&texture->resource.image, VK_IMAGE_ASPECT_COLOR_BIT, &texture->imageView, texture->format, texture->mipLevels)); @@ -383,7 +383,7 @@ void QVk_CreateTexture(qvktexture_t *texture, const unsigned char *data, uint32_ VK_VERIFY(vkAllocateDescriptorSets(vk_device.logical, &dsAllocInfo, &texture->descriptorSet)); // attach sampler - QVk_UpdateTextureSampler(texture, samplerType); + QVk_UpdateTextureSampler(texture, samplerType, clampToEdge); } void QVk_UpdateTextureData(qvktexture_t *texture, const unsigned char *data, uint32_t offset_x, uint32_t offset_y, uint32_t width, uint32_t height) @@ -633,11 +633,11 @@ void Vk_TextureMode( char *string ) { // skip console characters - we want them unfiltered at all times if (image->vk_texture.resource.image != VK_NULL_HANDLE && Q_stricmp(image->name, "pics/conchars.pcx")) - QVk_UpdateTextureSampler(&image->vk_texture, i); + QVk_UpdateTextureSampler(&image->vk_texture, i, image->vk_texture.clampToEdge); } if (vk_rawTexture.resource.image != VK_NULL_HANDLE) - QVk_UpdateTextureSampler(&vk_rawTexture, i); + QVk_UpdateTextureSampler(&vk_rawTexture, i, vk_rawTexture.clampToEdge); } /* @@ -673,7 +673,7 @@ void Vk_LmapTextureMode( char *string ) for (j = 0; j < MAX_LIGHTMAPS*2; j++) { if (vk_state.lightmap_textures[j].resource.image != VK_NULL_HANDLE) - QVk_UpdateTextureSampler(&vk_state.lightmap_textures[j], i); + QVk_UpdateTextureSampler(&vk_state.lightmap_textures[j], i, vk_state.lightmap_textures[j].clampToEdge); } } @@ -1026,7 +1026,7 @@ Vk_LoadPic(char *name, byte *pic, int width, int realwidth, QVk_CreateTexture(&image->vk_texture, (unsigned char*)texBuffer, image->upload_width, image->upload_height, - nolerp ? S_NEAREST : vk_current_sampler); + nolerp ? S_NEAREST : vk_current_sampler, (type == it_sky)); QVk_DebugSetObjectName((uint64_t)image->vk_texture.resource.image, VK_OBJECT_TYPE_IMAGE, va("Image: %s", name)); QVk_DebugSetObjectName((uint64_t)image->vk_texture.imageView, diff --git a/src/client/refresh/vk/vk_rsurf.c b/src/client/refresh/vk/vk_rsurf.c index 11f0b460..985a3beb 100644 --- a/src/client/refresh/vk/vk_rsurf.c +++ b/src/client/refresh/vk/vk_rsurf.c @@ -1098,7 +1098,7 @@ static void LM_UploadBlock( qboolean dynamic ) { QVVKTEXTURE_CLEAR(vk_state.lightmap_textures[texture]); QVk_CreateTexture(&vk_state.lightmap_textures[texture], vk_lms.lightmap_buffer, - BLOCK_WIDTH, BLOCK_HEIGHT, vk_current_lmap_sampler); + BLOCK_WIDTH, BLOCK_HEIGHT, vk_current_lmap_sampler, false); QVk_DebugSetObjectName((uint64_t)vk_state.lightmap_textures[texture].resource.image, VK_OBJECT_TYPE_IMAGE, va("Image: dynamic lightmap #%d", texture)); QVk_DebugSetObjectName((uint64_t)vk_state.lightmap_textures[texture].imageView, @@ -1304,7 +1304,7 @@ void Vk_BeginBuildingLightmaps (model_t *m) QVVKTEXTURE_CLEAR(vk_state.lightmap_textures[i]); QVk_CreateTexture(&vk_state.lightmap_textures[i], (unsigned char*)dummy, - BLOCK_WIDTH, BLOCK_HEIGHT, vk_current_lmap_sampler); + BLOCK_WIDTH, BLOCK_HEIGHT, vk_current_lmap_sampler, false); QVk_DebugSetObjectName((uint64_t)vk_state.lightmap_textures[i].resource.image, VK_OBJECT_TYPE_IMAGE, va("Image: dynamic lightmap #%d", i)); QVk_DebugSetObjectName((uint64_t)vk_state.lightmap_textures[i].imageView, From 8142898b563d9fc5408177833d10e37668bdc13d Mon Sep 17 00:00:00 2001 From: Ricardo Garcia Date: Tue, 12 Jan 2021 22:59:41 +0100 Subject: [PATCH 2/2] Fix black pixels along skybox edges in Vulkan Due to the skybox geometry not always being watertight, it's sometimes possible to see instances of isolated black pixels flickering along skybox edges. This happens when the sampling coordinates for the given pixel fall outside any triangle in the skybox due to the previous problem. These pixels are usually visible when MSAA is not used and pixels are big enough on the screen, like when using lower screen resolutions or large vk_pixel_size values. If MSAA is used, normally only a few of the samples fall outside any triangle and the problem is masked a bit, being harder to spot. Instead of fixing the skybox clipping routines, which may be complicated, this commit simply preserves color buffer contents between frames. If any pixel ends up without coverage, its color will be taken from a previous draw a few frames before, depending on the swapchain size. This is usually more than enough to completely mask the problem visually. --- src/client/refresh/vk/vk_common.c | 14 +++++++------ src/client/refresh/vk/vk_image.c | 34 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/client/refresh/vk/vk_common.c b/src/client/refresh/vk/vk_common.c index 6c46f153..dab8b047 100644 --- a/src/client/refresh/vk/vk_common.c +++ b/src/client/refresh/vk/vk_common.c @@ -427,12 +427,14 @@ static VkResult CreateRenderpasses() .flags = 0, .format = vk_swapchain.format, .samples = VK_SAMPLE_COUNT_1_BIT, - .loadOp = msaaEnabled ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : vk_renderpasses[RP_WORLD].colorLoadOp, - // if MSAA is enabled, we don't need to preserve rendered texture data since it's kept by MSAA resolve attachment - .storeOp = msaaEnabled ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE, + // The color attachment is loaded from the previous frame and stored + // after the frame is drawn to mask geometry errors in the skybox + // that may leave some pixels without coverage. + .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, // depth attachment @@ -503,7 +505,7 @@ static VkResult CreateRenderpasses() .format = vk_swapchain.format, .samples = VK_SAMPLE_COUNT_1_BIT, .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, - .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, @@ -711,7 +713,7 @@ static void CreateDrawBuffers() if (vk_renderpasses[RP_WORLD].sampleCount > 1) { QVk_CreateColorBuffer(vk_renderpasses[RP_WORLD].sampleCount, &vk_msaaColorbuffer, - 0); + VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT); R_Printf(PRINT_ALL, "...created MSAAx%d color buffer\n", vk_renderpasses[RP_WORLD].sampleCount); } diff --git a/src/client/refresh/vk/vk_image.c b/src/client/refresh/vk/vk_image.c index 771a11cc..5fdb5e30 100644 --- a/src/client/refresh/vk/vk_image.c +++ b/src/client/refresh/vk/vk_image.c @@ -357,6 +357,38 @@ void QVk_CreateDepthBuffer(VkSampleCountFlagBits sampleCount, qvktexture_t *dept VK_VERIFY(QVk_CreateImageView(&depthBuffer->resource.image, getDepthStencilAspect(depthBuffer->format), &depthBuffer->imageView, depthBuffer->format, depthBuffer->mipLevels)); } +static void ChangeColorBufferLayout(VkImage image, VkImageLayout fromLayout, VkImageLayout toLayout) +{ + VkCommandBuffer commandBuffer = QVk_CreateCommandBuffer(&vk_transferCommandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY); + QVk_BeginCommand(&commandBuffer); + + const VkImageSubresourceRange subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0u, + .levelCount = 1u, + .baseArrayLayer = 0u, + .layerCount = 1u, + }; + + const VkImageMemoryBarrier imageBarrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = NULL, + .srcAccessMask = 0u, + .dstAccessMask = 0u, + .oldLayout = fromLayout, + .newLayout = toLayout, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange = subresourceRange, + }; + + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0u, 0u, NULL, 0u, NULL, 1u, &imageBarrier); + + QVk_SubmitCommand(&commandBuffer, &vk_device.transferQueue); + vkFreeCommandBuffers(vk_device.logical, vk_transferCommandPool, 1, &commandBuffer); +} + void QVk_CreateColorBuffer(VkSampleCountFlagBits sampleCount, qvktexture_t *colorBuffer, int extraFlags) { colorBuffer->format = vk_swapchain.format; @@ -364,6 +396,8 @@ void QVk_CreateColorBuffer(VkSampleCountFlagBits sampleCount, qvktexture_t *colo VK_VERIFY(QVk_CreateImage(vk_swapchain.extent.width, vk_swapchain.extent.height, colorBuffer->format, VK_IMAGE_TILING_OPTIMAL, extraFlags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, colorBuffer)); VK_VERIFY(QVk_CreateImageView(&colorBuffer->resource.image, VK_IMAGE_ASPECT_COLOR_BIT, &colorBuffer->imageView, colorBuffer->format, colorBuffer->mipLevels)); + + ChangeColorBufferLayout(colorBuffer->resource.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } void QVk_CreateTexture(qvktexture_t *texture, const unsigned char *data, uint32_t width, uint32_t height, qvksampler_t samplerType, qboolean clampToEdge)