【问题标题】:Vulkan: Problems rendering to an offscreen framebuffer and reading the resultsVulkan:渲染到屏幕外帧缓冲区和读取结果时出现问题
【发布时间】:2020-04-05 09:35:42
【问题描述】:

我正在使用 VulkanAndroid 编写游戏。对于游戏,我需要将深度缓冲区信息写入屏幕外帧缓冲区。我最终想用 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


【解决方案1】:

我对这个问题的想法是它是一个驱动程序错误。该代码在几台不同的 Linux PC 和 Pixel 4XL android 设备上运行良好。我进行了测试,看看我是否得到了已知模型的正确结果。如果答案不正确,我会退回到 OpenGL。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-23
    • 2012-04-26
    • 1970-01-01
    • 2021-02-16
    相关资源
    最近更新 更多