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..dab8b047 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; @@ -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); } @@ -811,7 +813,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 +822,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 +836,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 +866,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 +1932,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 +2373,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 +2402,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..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,9 +396,11 @@ 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) +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 +417,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 +667,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 +707,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 +1060,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,