【发布时间】:2020-04-05 09:35:42
【问题描述】:
我正在使用 Vulkan 为 Android 编写游戏。对于游戏,我需要将深度缓冲区信息写入屏幕外帧缓冲区。我最终想用 CPU 读取这个深度信息,但现在我把它画在一个四边形上来调试它。我只在图像中获得了 1.0 的清晰深度值,所以我试图简化问题。首先,我决定使用颜色和深度附件,以防仅深度附件出现问题。然后我做了以下事情:
(1) 设置使用颜色和深度附件的渲染通道,并将它们分别清除为绿色和 {1.0, 0},
(2) 使用不使用交换链图像或正常深度缓冲区的帧缓冲区,而是专门为此离屏帧缓冲区创建的图像,
(3) 转换布局,以便它们可以被渲染通道写入,
(4) 将占据整个屏幕的蓝色四边形绘制到帧缓冲区,
(5)结束命令缓冲区,等待图形队列空闲,
(6) 转换颜色附件的布局,使其可以被着色器读取,
(7) 使用不同命令缓冲区中的交换链图像开始新的渲染过程,
(8) 使用其纹理绘制一个四边形,之前渲染过程的结果(颜色附件),
(9) 结束命令缓冲区并提交到图形队列。
我看到的是一个绿色矩形(第一个渲染通道的清晰颜色),里面有一些蓝色方块(蓝色是第一个渲染通道中绘制的四边形的颜色)。我希望看到整个屏幕被一个蓝色四边形占据。
我觉得我在某处缺少屏障、栅栏或信号灯。但我不知道在哪里。在每次命令提交后(用于调试),我在图形队列上等待,我添加了一个信号量,让每组命令发出下一组命令它已完成的信号。所以,有人会认为渲染通道有问题,但我将这个渲染通道用于我的正常绘制操作(在交换链图像中完成并发送到当前队列)没有任何问题。
让我知道您需要查看代码的哪一部分。很长。
我启用了 Vulkan 验证层,没有收到任何投诉。我通过强迫它抱怨来确保它正常工作。
我在 OpenGL 中做了同样的事情,它运行良好。
我的加载和存储操作如下:
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;
对于布局过渡,我在绘制前对颜色附件图像进行以下过渡:
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);
在渲染过程中,我将布局转换为 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,以便我可以从着色器中读取它。
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;
子通道及其依赖项:
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;
我的最终目标是有一个没有颜色附件的渲染通道,只有一个深度附件。颜色附件仅用于调试目的。
【问题讨论】:
-
"将它们分别清除为绿色和 {1.0, 0}"
{1.0, 0}是什么意思?这不是深度图像的合适值。这是深度/模板图像吗? "normal depth buffer" 这是什么?它与你以前使用的有什么不同? “等待图形队列空闲” 你为什么这样做而不是使用同步?另外,您真的在等待某事之前提交 CB吗? -
对不起,我的意思是深度/模板图像。它是用 VkImageCreateInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | 创建的。 VK_IMAGE_USAGE_SAMPLED_BIT。通过正常的深度缓冲区,我的意思是我在第二个渲染过程中使用了不同的深度/模板缓冲区。我正在等待图形队列空闲以进行调试。我想确保事情正在等待上一个阶段完成。在提交下一个命令缓冲区之前,我总是在等待图形队列空闲。
-
渲染通道的加载和存储操作是什么?当你转换布局时,你是否也在做适当的执行和内存屏障(可用性/可见性)?
-
oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;诡异的。所以也要显示渲染通道依赖和布局转换。 -
你的例子似乎不是最小的。第一深度和模板似乎在这里混为一谈,但与预期的结果无关。只需禁用深度测试。然后你有图形和当前队列。那是不必要的复杂化。他们是同一个队列吗?另外,为什么不只是
vkCopy?
标签: vulkan