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.
This commit is contained in:
Ricardo Garcia 2021-01-12 22:59:41 +01:00
parent aa2baedd97
commit 8142898b56
2 changed files with 42 additions and 6 deletions

View file

@ -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);
}

View file

@ -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)