How to overlay swapchain image with new renderpass with MSAA

I have 2 graphical pipelines. One pipeline draws firstly and second pipeline uses result of swapchain image from first pipeline and overlays with new data. It works correctly if resolve attachment for MSAA is not used in the renderpass. But first pipeline result clears if MSAA is used in second pipeline. Second pipeline renderpass simple executes loadOp = overlay ? VK_ATTACHMENT_LOAD_OP_LOAD: VK_ATTACHMENT_LOAD_OP_CLEAR. As I understand rasterization state fills initially MSAA image and after that it resolves to framebuffer attachment. And previous swapchain image cannot be loaded. Does someone know the way to load previous swapchain image when MSAA is used?

renderpass creation looks like

{
	bool use_msaa = numSamples != VK_SAMPLE_COUNT_1_BIT;

	std::vector<VkAttachmentDescription> renderPassAttachments;
	uint32_t attachmentReferenceCounter = 0;

	std::vector<VkAttachmentReference> subPassColorAttachments;
	std::vector<VkAttachmentReference> subPassDepthStencilAttachments;
	std::vector<VkAttachmentReference> subPassResolveAttachments;
	std::vector<VkAttachmentReference> subPassInputAttachments;

	VkAttachmentDescription colorAttachment = {};
	colorAttachment.format = format; //The format of the color attachment should match the format of the swap chain images
	colorAttachment.samples = numSamples;
	colorAttachment.loadOp = overlay ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR ; //VK_ATTACHMENT_LOAD_OP_LOAD//clear on init or load
	colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; //store stage result
	colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; //don't care - it is only for depth buffer
	colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;//don't care - it is only for depth buffer

	if (initialLayout == VK_IMAGE_LAYOUT_UNDEFINED && finalLayout == VK_IMAGE_LAYOUT_UNDEFINED)
	{
		if (overlay)
		{
			initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
			if (use_msaa)
			{
				finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
			}
			else
			{
				finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
			}
			
		}
		else
		{
			initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
			if (use_msaa)
			{
				finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
			}
			else
			{
				finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
			}
		}
	}

	colorAttachment.initialLayout = initialLayout; //undefined image layout in the memory
	colorAttachment.finalLayout = finalLayout; //VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - presentation


	colorAttachment.flags = 0;

	renderPassAttachments.push_back(colorAttachment);

	VkAttachmentReference color_reference = {};
	color_reference.attachment = attachmentReferenceCounter++;
	color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
	subPassColorAttachments.push_back(color_reference);

	/////////////////////////////////////////////////////////////
	//2nd
	if (include_depth)
	{
		VkAttachmentDescription depthAttachment = {};
		depthAttachment.format = depth_format;
		depthAttachment.samples = numSamples;
		depthAttachment.loadOp = overlay ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR; //VK_ATTACHMENT_LOAD_OP_CLEAR;
		depthAttachment.storeOp = overlay ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE;// VK_ATTACHMENT_STORE_OP_STORE; // 
		depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;// VK_ATTACHMENT_LOAD_OP_LOAD; // 
		depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;// VK_ATTACHMENT_STORE_OP_STORE; // 
		depthAttachment.initialLayout = overlay_depth ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED ;// 
		depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
		depthAttachment.flags = 0;

		renderPassAttachments.push_back(depthAttachment);

		VkAttachmentReference depth_reference = {};
		depth_reference.attachment = attachmentReferenceCounter++;
		depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
		subPassDepthStencilAttachments.push_back(depth_reference);
	}

	/////////////////////////////////////////////////////////////
	//3d
	if (use_msaa) //framebuffer image == resolve attachment
	{
		VkAttachmentDescription colorAttachmentResolve = {};
		colorAttachmentResolve.format = format;
		colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT;
		colorAttachmentResolve.loadOp = overlay ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR;// VK_ATTACHMENT_LOAD_OP_DONT_CARE;// 
		colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE;// VK_ATTACHMENT_STORE_OP_DONT_CARE;// ;
		colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
		colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
		colorAttachmentResolve.initialLayout = overlay ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; 
		colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

		renderPassAttachments.push_back(colorAttachmentResolve);

		VkAttachmentReference colorAttachmentResolveRef = {};
		colorAttachmentResolveRef.attachment = attachmentReferenceCounter;
		colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
		subPassResolveAttachments.push_back(colorAttachmentResolveRef);

		attachmentReferenceCounter++;
	}

	VkSubpassDescription subpass = {};
	subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
	subpass.flags = 0;
	subpass.inputAttachmentCount = static_cast<uint32_t>(subPassInputAttachments.size());
	subpass.pInputAttachments = subPassInputAttachments.data();
	subpass.colorAttachmentCount = static_cast<uint32_t>(subPassColorAttachments.size()); 
	subpass.pColorAttachments = subPassColorAttachments.data();
	subpass.pResolveAttachments = subPassResolveAttachments.data();
	subpass.pDepthStencilAttachment = subPassDepthStencilAttachments.data();
	subpass.preserveAttachmentCount = 0;
	subpass.pPreserveAttachments = nullptr;
es in the dependency graph.*/

	std::vector<VkSubpassDependency> dependencies;

	VkSubpassDependency colorAttachmentDependency = {};


	colorAttachmentDependency.srcSubpass = VK_SUBPASS_EXTERNAL;
	colorAttachmentDependency.dstSubpass = 0;
	colorAttachmentDependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
	colorAttachmentDependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
	colorAttachmentDependency.srcAccessMask = 0;
	colorAttachmentDependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
	colorAttachmentDependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

	dependencies.push_back(colorAttachmentDependency);

	VkRenderPassCreateInfo rp_info = {};
	rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
	rp_info.pNext = nullptr;
	rp_info.attachmentCount = static_cast<uint32_t>(renderPassAttachments.size());
	rp_info.pAttachments = renderPassAttachments.data();
	rp_info.subpassCount = 1;
	rp_info.pSubpasses = &subpass;
	rp_info.dependencyCount = static_cast<uint32_t>(dependencies.size());
	rp_info.pDependencies = dependencies.data();

	if (vkCreateRenderPass(m_vk_logical_device, &rp_info, nullptr, &m_renderPass) != VK_SUCCESS)
	{
		throw std::runtime_error("failed to create render pass!");
	}

}


It’s not really clear what you’re doing here, as your code doesn’t really conform to what you’ve described.

You’re describing a situation where you’re rendering to a multisampled image, then resolving that to another image (your “swap chain image”, but the fact that it’s part of the “swap chain” is essentially irrelevant). And then you want to render more stuff to the resolved image more.

I see where you’re creating a renderpass that renders to a multisampled image, and then resolves it to another image. But I don’t see the part where you have the next subpass come along and render to the resolved image. Indeed, you only have a single subpass.

So… where’s the code that is supposed to use the resolved image? Also, your renderpass has a source external dependency, but not a destination one. So, it’s not clear what’s really going on here.

I just want to overlay actual framebuffer image with new layer. (framebuffer image is assigned to swapchain image) And second overlay drawing should use MSAA and draws data atop of framebuffer image. It is main purpose. (both pipelines cmd buffers are submitted to queue together)

Initial image creates by first pipeline with own renderpass. After that second pipeline loads image in renderpass and draws data.

>> I see where you’re creating a renderpass that renders to a multisampled image, and then resolves it to another image. But I don’t see the part where you have the next subpass come along and render to the resolved image. Indeed, you only have a single subpass.

Actually the mentioned code is called twice for different pipilenes with different conditions. I don’t know how to use subpasses to impelement overlay.

Now I achive second pipeline overlay with mentioned code. But it is only works if the one MSAA image is used for both pipelines. But I don’t need to use MSAA for first pipeline because it reduces performance very much.

So I need to know how to get back image from framebuffer in renderpass with MSAA

And second overlay drawing should use MSAA and draws data atop of framebuffer image.

A multisample resolve operation overwrites the image it is resolving into. So you can’t directly resolve “atop” of anything. If you want to do some multisampled rendering and have that appear over some other image via transparency, you must resolve to a separate image (with your multisampled image cleared initially to transparent) and then do blended rendering of a quad sampling from that image (presumably as an input attachment, since you’re doing a 1:1 copy) onto the final image.

I don’t know how to use subpasses to impelement overlay.

This is not particularly complicated. You have one subpass that does the primary rendering. Then you have a second subpass that renders to the same image for your “overlay”. If both renderings are multisampled with the same sample count, you don’t even need to do the resolve until you’ve done both.