来自 Vulkan 中先前子通道的输入附件

Input attachment from previous subpass in Vulkan

我试图首先在子通道 0 中将一些内容渲染到 R8_UNORM 图像视图,然后在片段着色器中使用 subpassLoad 将结果用作子通道 1 中的输入附件。然而,渲染过程的最终输出(也是呈现的交换链图像)包含垃圾(参见 https://imgur.com/a/RmNdcxg),使用以下片段着色器:

#version 420
layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput input_text;
layout(location=0) out vec4 color;
void main()
{
    float value = subpassLoad(input_text).r;
    if (value > 0.0f) {
        color = vec4(0.0f, 0.0f, 1.0f, 1.0f);
    }
}

然而,使用这个片段着色器:

#version 420
layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput input_text;
layout(location=0) out vec4 color;
void main()
{
    float value = subpassLoad(input_text).r;
    if (value > 0.0f) {
        color = vec4(0.0f, 0.0f, 1.0f, 1.0f);
    }
    else {
        color = vec4(0.0f, 1.0f, 0.0f, 1.0f);
    }
}

给出了预期的结果(参见 https://imgur.com/a/JfhuJUk),所以我怀疑在写入每个片段时不存在一些同步问题。

两个帧缓冲区附件使用的明确值如下:

VkClearColorValue black_clear_color = { 0.0f, 0.0f, 0.0f, 0.0f };
VkClearColorValue red_clear_color = { 1.0f, 0.0f, 0.0f, 1.0f };
VkClearValue clear_value[2];
clear_value[0].color = vk_black_clear_color; // R8_UNORM attachment
clear_value[1].color = vk_red_clear_color; // Swapchain attachment

子通道 0 的片段着色器始终将 1.0f 写入要着色的每个片段。

以下是我认为相关的代码:

命令缓冲区记录、提交、图片展示

// Record
vkCmdBeginRenderPass(command_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, text_graphics_pipeline);
vkCmdBindVertexBuffers(command_buffer, 0, 1, &text_vertex_buffer, &vertex_buffer_offset);
vkCmdDraw(command_buffer, 15, 1, 0, 0);
vkCmdNextSubpass(command_buffer, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, final_graphics_pipeline);
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, final_pipeline_layout, 0, 1, &final_descriptor_set, 0, NULL);
vkCmdBindVertexBuffers(command_buffer, 0, 1, &final_vertex_buffer, &vertex_buffer_offset);
vkCmdDraw(command_buffer, 6, 1, 0, 0);
vkCmdEndRenderPass(command_buffer);
CHECK_VK_RES(vkEndCommandBuffer(command_buffer));

// Submit
VkPipelineStageFlags pipeline_wait_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo submit_info;
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.pNext = NULL;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &swapchain_image_avilable_semaphore;
submit_info.pWaitDstStageMask = &pipeline_wait_stages;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffer;
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &command_buffer_finished_semaphore;
CHECK_VK_RES(vkQueueSubmit(queue, 1, &submit_info, command_buffer_finished_fence));

// Present
VkPresentInfoKHR present_info;
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.pNext = NULL;
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = &command_buffer_finished_semaphore;
present_info.swapchainCount = 1;
present_info.pSwapchains = &swapchain;
present_info.pImageIndices = &image_available_idx;
present_info.pResults = NULL;
CHECK_VK_RES(vkQueuePresentKHR(queue, &present_info));

渲染过程设置

// Render pass attachments
VkAttachmentDescription vk_render_pass_attachment_descriptions[2];
// Text attachment
render_pass_attachment_descriptions[0].flags = 0;
render_pass_attachment_descriptions[0].format = VK_FORMAT_R8_UNORM;
render_pass_attachment_descriptions[0].samples = VK_SAMPLE_COUNT_1_BIT;
render_pass_attachment_descriptions[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
render_pass_attachment_descriptions[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
render_pass_attachment_descriptions[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
render_pass_attachment_descriptions[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
render_pass_attachment_descriptions[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
render_pass_attachment_descriptions[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
// Final attachment
render_pass_attachment_descriptions[1].flags = 0;
render_pass_attachment_descriptions[1].format = swapchain_format; // BGRA8_UNORM
render_pass_attachment_descriptions[1].samples = VK_SAMPLE_COUNT_1_BIT;
render_pass_attachment_descriptions[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
render_pass_attachment_descriptions[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
render_pass_attachment_descriptions[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
render_pass_attachment_descriptions[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
render_pass_attachment_descriptions[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
render_pass_attachment_descriptions[1].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

// Subpass descriptions
VkSubpassDescription render_pass_subpass_descriptions[2];
// Subpass 0
VkAttachmentReference text_subpass_color_attachment_reference;
text_subpass_color_attachment_reference.attachment = 0;
text_subpass_color_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
render_pass_subpass_descriptions[0].flags = 0;
render_pass_subpass_descriptions[0].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
render_pass_subpass_descriptions[0].inputAttachmentCount = 0;
render_pass_subpass_descriptions[0].pInputAttachments = NULL;
render_pass_subpass_descriptions[0].colorAttachmentCount = 1;
render_pass_subpass_descriptions[0].pColorAttachments = &text_subpass_color_attachment_reference;
render_pass_subpass_descriptions[0].pResolveAttachments = NULL;
render_pass_subpass_descriptions[0].pDepthStencilAttachment = NULL;
render_pass_subpass_descriptions[0].preserveAttachmentCount = 0;
render_pass_subpass_descriptions[0].pPreserveAttachments = NULL;
// Subpass 1
VkAttachmentReference final_subpass_input_attachment_reference;
final_subpass_input_attachment_reference.attachment = 0;
final_subpass_input_attachment_reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkAttachmentReference final_subpass_color_attachment_reference;
final_subpass_color_attachment_reference.attachment = 1;
final_subpass_color_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
render_pass_subpass_descriptions[1].flags = 0;
render_pass_subpass_descriptions[1].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
render_pass_subpass_descriptions[1].inputAttachmentCount = 1;
render_pass_subpass_descriptions[1].pInputAttachments = &final_subpass_input_attachment_reference;
render_pass_subpass_descriptions[1].colorAttachmentCount = 1;
render_pass_subpass_descriptions[1].pColorAttachments = &final_subpass_color_attachment_reference;
render_pass_subpass_descriptions[1].pResolveAttachments = NULL;
render_pass_subpass_descriptions[1].pDepthStencilAttachment = NULL;
render_pass_subpass_descriptions[1].preserveAttachmentCount = 0;
render_pass_subpass_descriptions[1].pPreserveAttachments = NULL;

// Subpass dependencies
VkSubpassDependency renderpass_subpass_dependencies[2];
// Ensure subpass 1's color attachment (swapchain image) is transitioned before it's written to
// since the pWaitDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
renderpass_subpass_dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
renderpass_subpass_dependencies[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
renderpass_subpass_dependencies[0].srcAccessMask = 0;
renderpass_subpass_dependencies[0].dstSubpass = 1;
renderpass_subpass_dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
renderpass_subpass_dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
renderpass_subpass_dependencies[0].dependencyFlags = 0;
// Subpass 1 cannot read from its input attachment before subpass 0 finishes writing to its color attachment
renderpass_subpass_dependencies[1].srcSubpass = 0;
renderpass_subpass_dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
renderpass_subpass_dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
renderpass_subpass_dependencies[1].dstSubpass = 1;
renderpass_subpass_dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
renderpass_subpass_dependencies[1].dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
renderpass_subpass_dependencies[1].dependencyFlags = 0;

// Render pass
VkRenderPassCreateInfo render_pass_info;
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_info.pNext = NULL;
render_pass_info.flags = 0;
render_pass_info.attachmentCount = 2;
render_pass_info.pAttachments = render_pass_attachment_descriptions;
render_pass_info.subpassCount = 2;
render_pass_info.pSubpasses = render_pass_subpass_descriptions;
render_pass_info.dependencyCount = 2;
render_pass_info.pDependencies = renderpass_subpass_dependencies;
VkRenderPass render_pass;
CHECK_VK_RES(vkCreateRenderPass(vk_device, &render_pass_info, NULL, &render_pass));

任何帮助将不胜感激,因为我似乎看不出这里有什么问题。

同步不是这里的罪魁祸首。相反,事实上子通道 1 中的 out vec4 color 总是被写到帧缓冲区(因为几何是全屏四边形),即使如果 if-condition 没有将其初始化为任何东西满意。

通过在else-clause中给着色器添加一个discard,得到正确的输出:

#version 420
layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput input_text;
layout(location=0) out vec4 color;
void main()
{
    float value = subpassLoad(input_text).r;
    if (value > 0.0f) {
        color = vec4(0.0f, 0.0f, 1.0f, 1.0f);
    }
    else {
        discard;
    }
}

作为一个“有趣”的事实:问题中不正确的图像在左上角不是很蓝,但图像其余大部分是蓝色的原因可能是寄存器用于存储 color 中的值被重新使用。所以一开始(假设工作从左上角开始),寄存器包含垃圾,但随着实际的蓝色片段被写出,越来越多的寄存器中有旧的蓝色值。因此,当 else 子句处于活动状态时,蓝色被写出,即使它在技术上是垃圾。