My setup is like this:
- Render a small scene (ie. a single 3D model) into a framebuffer with 1 color and 1 depth attachment.
- Render the color buffer into a fullscreen quad
Most code examples online use render passes and subpass dependencies, however I'm using dynamic rendering extension and therefore VkRenderingInfo, VkRenderingAttachmentInfo, and vkCmdBeginRendering. The setup is identical to dynamic rendering with swapchain and swapchain depth buffer, which I have got working fine. So I was thinking to extend this system and try deferred. However, it would seem the depth testing is not being performed, because every fragment passes the depth test (something that is clear upon inspection by visualizing the depth buffer, and from Renderdoc).
This is my command buffer recording for each frame:
// 1) begin recording on the command buffer
// 2) begin dynamic rendering on the framebuffer
// 3) bind main pipeline and set dynamic states
// 4) bind and render model, including descriptor sets and push constants
// 5) end dynamic rendering on the framebuffer
// 6) begin dynamic rendering on the swapchain
// 7) bind the quad pipeline
// 8) bind framebuffer color attachment for reading (descriptor set)
// 9) draw fullscreen quad
// 10) end dynamic rendering on the swapchain
// 11) end recording on the command buffer
Steps 6-11 are working fine (checked with Renderdoc). Also, since the problem is depth testing not working correctly, I expect the problem is either:
- How the main pipeline is configured; or
- How dynamic rendering is configured for the framebuffer
The pipeline is configured as it should be - the setup was copy/pasted from other projects where depth buffering is enabled and working. Therefore, this is what happens in step 2:
// color attachment pipeline barrier, omitted here.
// depth attachment pipeline barrier, shown below:
VkImageMemoryBarrier depthBarrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.pNext = nullptr;
barrier.srcAccessMask = 0;
barrier.dstAccessMask
= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
barrier.srcQueueFamilyIndex = graphicsQueueFamilyIndex;
barrier.dstQueueFamilyIndex = graphicsQueueFamilyIndex;
barrier.image = framebuffer->depthAttachment.image;
barrier.subresourceRange.aspectMask
= VK_IMAGE_ASPECT_DEPTH_BIT
| VK_IMAGE_ASPECT_STENCIL_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
const VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
const VkPipelineStageFlags dstStageMask
= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
| VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
vkCmdPipelineBarrier(
cmdBuf, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &depthBarrier);
VkRenderingAttachmentInfo colorInfo{};
colorInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
colorInfo.pNext = nullptr;
colorInfo.imageView = framebuffer->colorAttachment.imageView;
colorInfo.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
colorInfo.resolveMode = VK_RESOLVE_MODE_NONE;
colorInfo.resolveImageView = VK_NULL_HANDLE;
colorInfo.resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorInfo.clearValue = { .color = { .float32 = {0.1f, 0.1f, 0.1f, 1.0f} } }
VkRenderingAttachmentInfo depthInfo{};
depthInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
depthInfo.pNext = nullptr;
depthInfo.imageView = framebuffer->depthAttachment.imageView;
depthInfo.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depthInfo.resolveMode = VK_RESOLVE_MODE_NONE;
depthInfo.resolveImageView = VK_NULL_HANDLE;
depthInfo.resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
depthInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depthInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
depthInfo.clearValue = { .depthStencil = { 1.0f, 0 } };
VkRenderingInfo renderingInfo{};
renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
renderingInfo.pNext = nullptr;
renderingInfo.flags = 0;
renderingInfo.renderArea = { 0U, 0U, framebuffer->width, framebuffer->height };
renderingInfo.layerCount = 1;
renderingInfo.viewMask = 0;
renderingInfo.colorAttachmentCount = 1;
renderingInfo.pColorAttachments = &colorInfo;
renderingInfo.pDepthAttachment = &depthInfo;
renderingInfo.pStencilAttachment = nullptr;
vkCmdBeginRendering(cmdBuf, &renderingInfo);
Step 3 and 4 have been tested before and should work fine. Step 5 is ending dynamic rendering on the framebuffer, and it only transitions the color attachment to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL so it can be sampled from in the fullscreen quad shader. I don't get any validation messages here, and I can see the final image on-screen - only without proper depth testing.
What could I be doing wrong? I hope I'm presenting the problem properly, otherwise let me know if I'm missing an important step.
Turns out my problem was nowhere I suspected - I accidentally forgot to set the depth range for the
vkCmdSetViewportduring command buffer recording:Even with depth buffer, depth testing+writing enabled, correct barriers, the viewport transform will force all depth values to
depthRange- I'm not sure if my understand here is correct..For anyone wondering, the barriers that I set up work just fine.
It's quite difficult to post Vulkan-questions, because several 100 lines may be relevant to spot where the error lies. If this helps someone else I'm happy!