Multiple render pass compatibility

Hello, I am trying to understand using multiple render passes. I have a chain of render passes:

std::array<VkAttachmentDescription, 4> attachments{};
// shadow map attachment
attachments[0].format = _point_light_shadow_map.image_info.getFormat();
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachments[0].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
// color attachment
attachments[1].format = VK_FORMAT_R8G8B8A8_UNORM;
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
// depth attachment
attachments[2].format = _vk_dev.get()->depth_image.getFormat();
attachments[2].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachments[2].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[2].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
// post processing attachment
attachments[3].format = _vk_dev.get()->getFormat();
attachments[3].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[3].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachments[3].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
attachments[3].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[3].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[3].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[3].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;

VkAttachmentReference shadow_attachment_reference{};
shadow_attachment_reference.attachment = 0;
shadow_attachment_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkAttachmentReference color_attachment_reference{};
color_attachment_reference.attachment = 1;
color_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference depth_attachment_reference{};
depth_attachment_reference.attachment = 2;
depth_attachment_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkAttachmentReference post_processing_attachment_reference{};
post_processing_attachment_reference.attachment = 3;
post_processing_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

//shadow map render pass dependency
std::array<VkSubpassDependency, 2> dependency{};
dependency[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependency[0].dstSubpass = 0;
dependency[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency[0].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependency[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependency[0].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependency[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

dependency[1].srcSubpass = 0;
dependency[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependency[1].srcStageMask = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependency[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependency[1].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependency[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependency[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

std::array<VkSubpassDescription, 1> shadow_subpasses{};
shadow_subpasses[0].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
shadow_subpasses[0].pDepthStencilAttachment = &shadow_attachment_reference;

VkRenderPassCreateInfo shadow_create_info{};
shadow_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
shadow_create_info.subpassCount = shadow_subpasses.size();
shadow_create_info.pSubpasses = shadow_subpasses.data();
shadow_create_info.dependencyCount = dependency.size();
shadow_create_info.pDependencies = dependency.data();
shadow_create_info.attachmentCount = attachments.size();
shadow_create_info.pAttachments = attachments.data();
VK_CHECK(vkCreateRenderPass(_vk_dev.get()->device, &shadow_create_info, nullptr, &_shadow_render_pass));

// offscreen render pass dependency
dependency[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependency[0].dstSubpass = 0;
dependency[0].srcStageMask = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependency[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependency[0].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependency[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT;
dependency[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

dependency[1].srcSubpass = 0;
dependency[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependency[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependency[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependency[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependency[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

std::array<VkSubpassDescription, 1> subpasses{};
subpasses[0].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpasses[0].colorAttachmentCount = 1;
subpasses[0].pColorAttachments = &color_attachment_reference;
subpasses[0].pDepthStencilAttachment = &depth_attachment_reference;

VkRenderPassCreateInfo create_info{};
create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
create_info.subpassCount = subpasses.size();
create_info.pSubpasses = subpasses.data();
create_info.dependencyCount = dependency.size();
create_info.pDependencies = dependency.data();
create_info.attachmentCount = attachments.size();
create_info.pAttachments = attachments.data();
VK_CHECK(vkCreateRenderPass(_vk_dev.get()->device, &create_info, nullptr, &_offscreen_render_pass));

// post processing render pass dependency
dependency[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependency[0].dstSubpass = 0;
dependency[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependency[0].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependency[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT;
dependency[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

dependency[1].srcSubpass = 0;
dependency[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependency[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependency[1].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependency[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependency[1].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependency[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

std::array<VkSubpassDescription, 1> post_processing_subpasses{};
post_processing_subpasses[0].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
post_processing_subpasses[0].pColorAttachments = &post_processing_attachment_reference;
post_processing_subpasses[0].colorAttachmentCount = 1;

VkRenderPassCreateInfo post_processing_create_info{};
post_processing_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
post_processing_create_info.subpassCount = post_processing_subpasses.size();
post_processing_create_info.pSubpasses = post_processing_subpasses.data();
post_processing_create_info.dependencyCount = dependency.size();
post_processing_create_info.pDependencies = dependency.data();
post_processing_create_info.attachmentCount = attachments.size();
post_processing_create_info.pAttachments = attachments.data();
VK_CHECK(vkCreateRenderPass(_vk_dev.get()->device, &post_processing_create_info, nullptr, &_post_processing_render_pass));

It works well but I don`t know if it is good to create attachments and bind all of them to all render passes and then create framebuffers with all the attachments. I think it would be better to use attachments that I use in the specific render pass. I have tried to experiment but Vulkan says about render pass compatibility. And when I tried to create render passes with different amount of subpasses Vulkan also complained about it. Can you tell me how to do it correctly? Thanks for help.

This question is very vague and missing information. You have “multiple render passes”, but you don’t say what those render passes are for or why you need them to be “compatible”. Are you trying to use the same pipeline with them? If so, why do you need more than one?

In order for two render passes to be compatible, they basically have to be created with identical data. And thus, they’re basically the same render pass in two different VkRenderPass objects. So why do you need two things that are the same thing?

Also:

  1. “says” what “about render pass compatibility”?
  2. Guess and check is not how you learn Vulkan. There are many things validation layers can catch, but also many things they cannot. Vulkan is not a safe API, so trying stuff and seeing what “works” is not going to give you good results.

That explains a lot of things to me.
I am trying to implement shadow mapping. I have 4 different attachments and 3 different render passes.
I first draw the scene from the perspective of the light source (first pipeline, first render pass), then I draw objects and lights from the perspective of the camera (second and third pipelines, second render pass), then I render a quad and do postprocessing (fourth pipeline, third render pass). When I tried to do that at first, I got a problem that render passes must be compatible, because there was attachment mismatch. So I set all the attachments in VkRenderPassCreateInfo, but in my subpasses I use only several attachments. With all this stuff I have different framebuffers that have all the attachments that all the render passes use. I want to know if I can use framebuffers only with attachments they use (postprocessing render pass do not use the shadow map, so I do not attach image view to the postprocessing framebuffer). But as you said, they have to be created with identical data, so I don’t think there’s any way to do that other than set all the attachments at once in VkRenderPassCreateInfo and VkFramebufferCreateInfo, isn`t it?

You “got a problem” where, exactly? Validation layer errors tend to point to the cite in the code where the problem occurred. So where was it? Were you binding a pipeline? If so, which pipeline?

I don’t know why you need compatibility between these 3 render passes. Each render pass will be using its own pipelines. So what were you doing that required compatibility between them.

Alfonse, thank you very much. I have realized that there was no mistake in my program. I really apologise for wasting your time. I have managed to create different render passes with only their attachments. The problem was in the beginning, when validation layers were talking about attachments and render pass compatibility and, because I did not know too much, I had assumed that when creating a chain of render passes, it was necessary to specify all attachments, but now I realise I don’t need to do that and the problem was different.