mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2024-11-10 07:12:07 +00:00
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:
parent
aa2baedd97
commit
8142898b56
2 changed files with 42 additions and 6 deletions
|
@ -427,12 +427,14 @@ static VkResult CreateRenderpasses()
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.format = vk_swapchain.format,
|
.format = vk_swapchain.format,
|
||||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||||
.loadOp = msaaEnabled ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : vk_renderpasses[RP_WORLD].colorLoadOp,
|
// The color attachment is loaded from the previous frame and stored
|
||||||
// if MSAA is enabled, we don't need to preserve rendered texture data since it's kept by MSAA resolve attachment
|
// after the frame is drawn to mask geometry errors in the skybox
|
||||||
.storeOp = msaaEnabled ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE,
|
// 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,
|
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||||
.stencilStoreOp = VK_ATTACHMENT_STORE_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
|
.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
|
||||||
},
|
},
|
||||||
// depth attachment
|
// depth attachment
|
||||||
|
@ -503,7 +505,7 @@ static VkResult CreateRenderpasses()
|
||||||
.format = vk_swapchain.format,
|
.format = vk_swapchain.format,
|
||||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||||
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
|
.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,
|
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||||
.initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
.initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||||
|
@ -711,7 +713,7 @@ static void CreateDrawBuffers()
|
||||||
if (vk_renderpasses[RP_WORLD].sampleCount > 1)
|
if (vk_renderpasses[RP_WORLD].sampleCount > 1)
|
||||||
{
|
{
|
||||||
QVk_CreateColorBuffer(vk_renderpasses[RP_WORLD].sampleCount, &vk_msaaColorbuffer,
|
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",
|
R_Printf(PRINT_ALL, "...created MSAAx%d color buffer\n",
|
||||||
vk_renderpasses[RP_WORLD].sampleCount);
|
vk_renderpasses[RP_WORLD].sampleCount);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
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)
|
void QVk_CreateColorBuffer(VkSampleCountFlagBits sampleCount, qvktexture_t *colorBuffer, int extraFlags)
|
||||||
{
|
{
|
||||||
colorBuffer->format = vk_swapchain.format;
|
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_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));
|
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)
|
void QVk_CreateTexture(qvktexture_t *texture, const unsigned char *data, uint32_t width, uint32_t height, qvksampler_t samplerType, qboolean clampToEdge)
|
||||||
|
|
Loading…
Reference in a new issue