我正在将渲染器从渲染通道移至动态渲染,并在 Linux(具体是 Nvidia 550.78)上看到验证消息,而在 Windows(具体是 AMD 24.3.1)上看不到。以下是我为便于阅读而格式化的几行:
[17:01:00.273][8314]: Swapchain image 0xcad092000000000d: 'ColorAttachmentOptimal'->'PresentSrcKHR'
[17:01:00.273][8314]:
Validation Error: [ SYNC-HAZARD-WRITE-AFTER-READ ]
Object 0: handle = 0x555555b41130, type = VK_OBJECT_TYPE_QUEUE; |
MessageID = 0x376bc9df |
vkQueueSubmit():
Hazard WRITE_AFTER_READ for entry 0,
VkCommandBuffer 0x5555582fad00[],
Submitted access info (submitted_usage: SYNC_IMAGE_LAYOUT_TRANSITION,
command: vkCmdPipelineBarrier,
seq_no: 1,
VkImage 0xcad092000000000d[],
reset_no: 30).
Access info (prior_usage: SYNC_PRESENT_ENGINE_SYNCVAL_PRESENT_ACQUIRE_READ_SYNCVAL,
read_barriers: VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT|VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT,
,
batch_tag: 677,
vkAcquireNextImageKHR aquire_tag:677
: VkSwapchainKHR 0xe88693000000000c[],
image_index: 0
image: VkImage 0xcad092000000000d[]).
[17:01:00.273][8314]:
Validation Error: [ SYNC-HAZARD-WRITE-AFTER-WRITE ]
Object 0: handle = 0x555555b41130, type = VK_OBJECT_TYPE_QUEUE; |
MessageID = 0x5c0ec5d6 |
vkQueueSubmit():
Hazard WRITE_AFTER_WRITE for entry 0,
VkCommandBuffer 0x5555582fad00[],
Submitted access info (submitted_usage: SYNC_IMAGE_LAYOUT_TRANSITION,
command: vkCmdPipelineBarrier,
seq_no: 2,
VkImage 0x2e2941000000001f[],
reset_no: 30).
Access info (prior_usage: SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE,
write_barriers: 0,
queue: VkQueue 0x555555b41130[],
submit: 88,
batch: 0,
batch_tag: 663,
command: vkCmdEndRenderingKHR,
command_buffer: VkCommandBuffer 0x555558313000[],
seq_no: 11,
reset_no: 28).
[17:01:00.289][8314]: Swapchain image 0x967dd1000000000e: 'Undefined'->'ColorAttachmentOptimal'
[17:01:00.289][8314]: Image 0x2e2941000000001f: 'Undefined'->'DepthAttachmentOptimal'
第一行和最后两行是我自己的日志记录,而“中间两行”是分解的验证消息。看起来好像有一些格式上的废话,但我保留了丢失的信息,并尽可能与我的格式保持一致。
无论如何,在我提交其中包含转换管道屏障的命令缓冲区后,我收到了读后写错误。这是实际控制何时创建障碍的代码。就在我调用
beginRenderingKHR()
之前,我转换了交换链图像和深度缓冲区:
auto &color_buffer = *Renderer::swapchain().images()[Renderer::image_index()];
color_buffer.transition_layout(Renderer::cmd_buffer(),
vk::ImageLayout::eUndefined,
vk::ImageLayout::eColorAttachmentOptimal);
_depth_buffer.transition_layout(Renderer::cmd_buffer(),
vk::ImageLayout::eUndefined,
vk::ImageLayout::eDepthAttachmentOptimal);
然后,就在之后调用
endRenderingKHR()
我转换颜色缓冲区:
auto &color_buffer = *Renderer::swapchain().images()[Renderer::image_index()];
color_buffer.transition_layout(Renderer::cmd_buffer(),
vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::ePresentSrcKHR);
这是图像转换代码(
vkSwapchainImage
不适用于深度缓冲区,但逻辑是相同的):
void vkSwapchainImage::transition_layout(vkCmdBuffer const &cmd_buffer,
vk::ImageLayout const old_layout,
vk::ImageLayout const new_layout)
{
BTX_TRACE("Swapchain image {}: '{:s}'->'{:s}'",
_handle,
vk::to_string(old_layout),
vk::to_string(new_layout));
vk::ImageMemoryBarrier barrier {
.srcAccessMask = { },
.dstAccessMask = { },
.oldLayout = old_layout,
.newLayout = new_layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = _handle,
.subresourceRange {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0u,
.levelCount = 1u,
.baseArrayLayer = 0u,
.layerCount = 1u,
}
};
vk::PipelineStageFlags src_stage = vk::PipelineStageFlagBits::eNone;
vk::PipelineStageFlags dst_stage = vk::PipelineStageFlagBits::eNone;
if(old_layout == vk::ImageLayout::eUndefined) {
barrier.srcAccessMask = vk::AccessFlagBits::eNone;
if(new_layout == vk::ImageLayout::eColorAttachmentOptimal) {
barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentRead
| vk::AccessFlagBits::eColorAttachmentWrite;
src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
dst_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
}
else if(new_layout == vk::ImageLayout::eDepthAttachmentOptimal) {
barrier.dstAccessMask =
vk::AccessFlagBits::eDepthStencilAttachmentRead
| vk::AccessFlagBits::eDepthStencilAttachmentWrite;
src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
dst_stage = vk::PipelineStageFlagBits::eEarlyFragmentTests
| vk::PipelineStageFlagBits::eLateFragmentTests;
}
else {
BTX_CRITICAL("Unsupported image layout transition");
return;
}
}
else if(old_layout == vk::ImageLayout::eColorAttachmentOptimal) {
barrier.srcAccessMask = vk::AccessFlagBits::eColorAttachmentRead
| vk::AccessFlagBits::eColorAttachmentWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eNone;
src_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
dst_stage = vk::PipelineStageFlagBits::eBottomOfPipe;
}
else {
BTX_CRITICAL("Unsupported image layout transition");
return;
}
cmd_buffer.native().pipelineBarrier(
src_stage, // Source stage
dst_stage, // Destination stage
{ }, // Dependency flags
nullptr, // Memory barriers
nullptr, // Buffer memory barriers
{ barrier } // Image memory barriers
);
_layout = barrier.newLayout;
}
我努力从官方动态渲染示例代码中准确复制图像转换,它们在 Windows/AMD 上看起来很好,但我当然想了解哪些内容不完整或有什么问题。
当然,任何能够更好地处理图像过渡的结构改进都将受到欢迎。 =)
对于交换链图像,您可能不会等待它准备好(其获取完成)。 使用栅栏在 CPU 端等待或使用带有
VkSubmitInfo::pWaitSemaphores
的信号量。
对于深度图像:
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT
在第一个同步范围(src_stage
)中指定时不指定执行阶段。
即,您没有告诉 GPU 确保其先前的使用已完成并且缓存已刷新/无效。
您可能还需要
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT
来代替 src_stage
,并带有适当的 VkAccessFlagBits
。