Reputation: 145
I am writing a game for Android using Vulkan. For the game, I need to write the depth buffer information to an offscreen framebuffer. I eventually want to read this depth information with the CPU, but for now I was drawing it on a quad to debug it. I was only getting the clear depth value of 1.0 in the image, so I tried to simplify the problem. First, I decided to use a color and depth attachment in case there was an issue with just the depth attachment. Then I did the following:
(1) set up a render pass which uses color and depth attachments and clears them to green and {1.0, 0} respectively,
(2) use a framebuffer that does not use the swapchain images or normal depth buffer, but images created specifically for this offscreen framebuffer,
(3) transition the layouts so that they can be written to by the render pass,
(4) draw a blue quad which takes up the entire screen to the framebuffer,
(5) end the command buffer and wait for the graphics queue is idle,
(6) transition the layout of the color attachment so that it can be read by a shader,
(7) start a new render pass using the swapchain images in a different command buffer,
(8) draw a quad using for its texture, the results of the previous render pass (color attachment),
(9) end the command buffer and submit to the graphics queue.
What I see is a green rectangle (the clear color from the first render pass) with some blue squares inside of it (blue is the color of the quad drawn in the first render pass). I expected to see the whole screen taken up by a single blue quad.
I feel like I am missing a barrier or fence or semaphore somewhere. But I can't figure out where. I wait on the graphics queue after each command submission (for debugging), I added a semaphore to have each set of commands signal the next set that it was done. So, one would think that there was something wrong in the render pass, but I use this render pass for my normal draw opperations (that are done in the swapchain images and sent to the present queue) without any problem.
Let me know which part of the code you need to see. It is very long.
I enabled the Vulkan validation layers and am getting no complaints from it. I made sure it is working by forcing it to complain.
I did the same thing in OpenGL and it works fine.
My load and store ops are as follows:
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
For the layout transitions I am doing the following transition for the color attachment image before the draw:
vkBeginCommandBuffer(...);
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = colorImage;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
vkCmdPipelineBarrier(
cmdBuffer,
sourceStage, destinationStage,
0,
0, nullptr,
0, nullptr,
1, &barrier
);
vkEndCommandBuffer(...);
vkQueueSubmit(...);
vkQueueWaitIdle(graphicsQueue);
In the render pass, I transition the layout to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL so that I can read it from the shader.
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
The subpass and its dependencies:
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
subpass.pDepthStencilAttachment = &depthAttachmentRef;
VkSubpassDependency dependency = {};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstAccessMask =
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
My final goal is to have a render pass with no color attachment, only a depth attachment. The color attachment is there for debug purposes only.
Upvotes: 1
Views: 2902
Reputation: 145
My thought on this problem is that it is a driver bug. The code works fine on a couple of different Linux PCs and on a Pixel 4XL android device. I put a test in to see if I get the correct result for a known model. If the answer comes back incorrect, I fall back to OpenGL.
Upvotes: 0