我正在一台集成显卡的笔记本电脑上开发一款 Vulkan 应用(只有一个 VkQueue),启动时我需要运行一个非常昂贵的片段着色器,其输出稍后会被读取为纹理。问题是,如果耗时太长,操作系统(我相信)有时会将其杀死,从而导致 VK_ERROR_DEVICE_LOST。
为了解决这个问题,我分解了渲染工作,将绘制调用细分为不同的剪刀/渲染区域,每个剪刀/渲染区域都有自己的命令缓冲区和 VkSubmitInfo,即共享帧缓冲区。这已经修复了设备丢失错误,但是除了最后一个剪刀外,所有剪刀上都有噪音/垃圾。它不是完全随机的:可以看到已经进行了绘制,但似乎驱动程序认为在完成剪刀的渲染后内存可供使用。
我需要告诉驱动程序“不要触碰这块内存”,我认为这是帧缓冲区渲染区域的要点。
如果我将附件的 initialLayout 设置为 READ_ONLY_OPTIMAL(与 finalLayout 相同)而不是 UNDEFINED,则一切正常,但当然我会收到验证错误,提示图像的布局错误。尝试将图像转换为正确的布局会再次中断渲染。
因此,我认为我错误地设置了管道屏障:
VkImageMemoryBarrier barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.image = image,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.subresourceRange.levelCount = 1,
.subresourceRange.layerCount = 1,
};
}
vkCmdPipelineBarrier(cmd[i],
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0, // dependencyFlags
0, NULL, 0, NULL, 1, &barrier);
我做错了什么?
解决方案:
renderpass = {.initialLayout=COLOR_ATTACHMENT, .finalLayout=.initialLayout}
if batch == 0
vkCmdPipelineBarrier(TOP_OF_PIPE, COLOR_ATTACHMENT_OUTPUT,
barrier={.oldLayout=UNDEFINED, .newLayout=COLOR_ATTACHMENT})
renderpass begin, ..., renderpass end
if batch == last
vkCmdPipelineBarrier(COLOR_ATTACHMENT_OUTPUT, COLOR_ATTACHMENT_OUTPUT,
barrier={.oldLayout=COLOR_ATTACHMENT, .newLayout=READ_ONLY})
布局未定义是..未定义..它告诉实现中的数据
image
可以被丢弃,因为它无法解释内容。您只能在要覆盖中的所有数据时使用它image
,并且在您的例子中,这仅适用于第一个渲染过程。对于后面的渲染过程,您需要保留数据,我会将其保留在COLOR_ATTACHMENT_OPTIMAL
鉴于您正在写入,这似乎是渲染通道附件的错误选择。预期用于
COLOR_ATTACHMENT_OPTIMAL
附件。