Vulkan第二个渲染通道附件被误判为输入附件问题求助
我配置了两个各包含1个子通道的Render Pass:
- 第一个Render Pass包含
depthAttachment、colorAttachment、colorResolveAttachment三个附件; - 第二个Render Pass仅包含
finalColorAttachment,其片段着色器会读取第一个Render Pass的colorResolveAttachment。
创建第二个Render Pass时,Vulkan验证层输出警告,称pSubpasses[0].pColorAttachments[0](即colorAttachmentPP)同时是输入附件,其布局VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL不符合要求,但该附件实际并非输入附件,我无法理解报错原因。
警告信息
vkCreateRenderPass(): pSubpasses[0].pColorAttachments[0] is also an input attachment so the layout (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) must not be VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL or VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR.
The Vulkan spec states: Each attachment must follow the image layout requirements specified for its attachment type
相关代码
// First render pass ------------------------- // Final color attachment VkAttachmentDescription colorAttachmentResolve{}; colorAttachmentResolve.format = swapChainImageFormat; colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT; colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE; colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentReference colorAttachmentResolveRef{}; colorAttachmentResolveRef.attachment = 0; colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // Depth attachment VkAttachmentDescription depthAttachment{}; depthAttachment.format = findDepthFormat(); depthAttachment.samples = msaaSamples; depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkAttachmentReference depthAttachmentRef{}; depthAttachmentRef.attachment = 1; depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; // Color attachment VkAttachmentDescription colorAttachment{}; colorAttachment.format = swapChainImageFormat; colorAttachment.samples = msaaSamples; 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; colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentReference colorAttachmentRef{}; colorAttachmentRef.attachment = 2; colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; std::vector<VkAttachmentDescription> attachments = std::vector<VkAttachmentDescription>{ colorAttachmentResolve, depthAttachment, colorAttachment }; // Subpass 1: VkSubpassDescription subpass{}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorAttachmentRef; subpass.pDepthStencilAttachment = &depthAttachmentRef; subpass.pResolveAttachments = &colorAttachmentResolveRef; subpass.inputAttachmentCount; subpass.pInputAttachments; subpass.preserveAttachmentCount; subpass.pPreserveAttachments; VkSubpassDependency dependency{}; dependency.srcSubpass = VK_SUBPASS_EXTERNAL; dependency.dstSubpass = 0; dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.srcAccessMask = 0; dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; // Create Render Pass 1: VkRenderPassCreateInfo renderPassInfo{}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size()); renderPassInfo.pAttachments = attachments.data(); renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass[0]); // Second render pass (Post processing) ------------------------- // Final color attachment (from previous render pass) colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // Final color Post-Processed attachment VkAttachmentDescription colorAttachmentPP{}; colorAttachmentPP.format = swapChainImageFormat; colorAttachmentPP.samples = VK_SAMPLE_COUNT_1_BIT; colorAttachmentPP.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorAttachmentPP.storeOp = VK_ATTACHMENT_STORE_OP_STORE; colorAttachmentPP.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorAttachmentPP.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; colorAttachmentPP.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; colorAttachmentPP.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; VkAttachmentReference colorAttachmentPPRef{}; colorAttachmentPPRef.attachment = 0; colorAttachmentPPRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // Subpass 2 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorAttachmentPPRef; subpass.pDepthStencilAttachment = nullptr; subpass.pResolveAttachments = nullptr; VkAttachmentReference inputAttachments[1] = { colorAttachmentResolveRef }; subpass.inputAttachmentCount = 1; subpass.pInputAttachments = inputAttachments; // <<< Set input attachments subpass.preserveAttachmentCount; subpass.pPreserveAttachments; dependency.srcSubpass = VK_SUBPASS_EXTERNAL; dependency.dstSubpass = 0; dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.srcAccessMask = 0; dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; // Create Render Pass 2: renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassInfo.attachmentCount = static_cast<uint32_t>(1); renderPassInfo.pAttachments = &colorAttachmentPP; renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass[1]);
问题出在第二个Render Pass的附件引用逻辑:你给子通道指定的输入附件colorAttachmentResolveRef,其attachment值为0,但第二个Render Pass只声明了1个附件(colorAttachmentPP),索引正好是0。Vulkan会把这个输入附件解析为引用当前Render Pass的第0个附件,也就是colorAttachmentPP,导致它同时被标记为颜色附件和输入附件,触发验证层警告。
修复步骤如下:
- 将第一个Render Pass的
colorAttachmentResolve加入第二个Render Pass的附件列表,调整其布局参数(初始布局对应第一个Render Pass的最终布局,最终布局设为着色器可读); - 修改输入附件的
attachment值为1(因为colorAttachmentPP是第0个附件,colorAttachmentResolve是第1个); - 不要复用第一个Render Pass的
subpass和dependency变量,避免残留旧数据; - 修正依赖关系,确保第一个Render Pass的输出能被第二个Render Pass的片段着色器正确读取。
修复后的第二个Render Pass代码示例:
// Second render pass (Post processing) ------------------------- // 复制并调整第一个Render Pass的colorAttachmentResolve描述 VkAttachmentDescription colorAttachmentResolvePP = colorAttachmentResolve; colorAttachmentResolvePP.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // 对应第一个Render Pass的finalLayout colorAttachmentResolvePP.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkAttachmentReference colorAttachmentResolveRefPP{}; colorAttachmentResolveRefPP.attachment = 1; // 现在是第二个附件 colorAttachmentResolveRefPP.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // Final color Post-Processed attachment VkAttachmentDescription colorAttachmentPP{}; colorAttachmentPP.format = swapChainImageFormat; colorAttachmentPP.samples = VK_SAMPLE_COUNT_1_BIT; colorAttachmentPP.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorAttachmentPP.storeOp = VK_ATTACHMENT_STORE_OP_STORE; colorAttachmentPP.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorAttachmentPP.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; colorAttachmentPP.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; colorAttachmentPP.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; VkAttachmentReference colorAttachmentPPRef{}; colorAttachmentPPRef.attachment = 0; colorAttachmentPPRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // 新建子通道描述,不复用旧变量 VkSubpassDescription subpass2{}; subpass2.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass2.colorAttachmentCount = 1; subpass2.pColorAttachments = &colorAttachmentPPRef; subpass2.pDepthStencilAttachment = nullptr; subpass2.pResolveAttachments = nullptr; VkAttachmentReference inputAttachments[1] = { colorAttachmentResolveRefPP }; subpass2.inputAttachmentCount = 1; subpass2.pInputAttachments = inputAttachments; subpass2.preserveAttachmentCount = 0; subpass2.pPreserveAttachments = nullptr; // 修正依赖关系,确保读取权限正确 VkSubpassDependency dependency2{}; dependency2.srcSubpass = VK_SUBPASS_EXTERNAL; dependency2.dstSubpass = 0; dependency2.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependency2.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; dependency2.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependency2.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; dependency2.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; // 附件列表包含两个附件 std::vector<VkAttachmentDescription> attachments2 = { colorAttachmentPP, colorAttachmentResolvePP }; // 创建Render Pass 2 VkRenderPassCreateInfo renderPassInfo2{}; renderPassInfo2.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassInfo2.attachmentCount = static_cast<uint32_t>(attachments2.size()); renderPassInfo2.pAttachments = attachments2.data(); renderPassInfo2.subpassCount = 1; renderPassInfo2.pSubpasses = &subpass2; renderPassInfo2.dependencyCount = 1; renderPassInfo2.pDependencies = &dependency2; vkCreateRenderPass(device, &renderPassInfo2, nullptr, &renderPass[1]);
内容的提问来源于stack exchange,提问作者Anselmo GPP

