Browse Source

[BUGFIX] Handle VK_ERROR_OUT_OF_DATE_KHR

Panagiotis Christopoulos Charitos 8 years ago
parent
commit
3428187d35

+ 20 - 6
src/anki/gr/vulkan/CommandBufferImpl.cpp

@@ -67,6 +67,7 @@ void CommandBufferImpl::beginRecording()
 	if(!!(m_flags & CommandBufferFlag::SECOND_LEVEL))
 	if(!!(m_flags & CommandBufferFlag::SECOND_LEVEL))
 	{
 	{
 		FramebufferImpl& impl = *m_activeFb->m_impl;
 		FramebufferImpl& impl = *m_activeFb->m_impl;
+		impl.sync();
 
 
 		// Calc the layouts
 		// Calc the layouts
 		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS> colAttLayouts;
 		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS> colAttLayouts;
@@ -90,7 +91,11 @@ void CommandBufferImpl::beginRecording()
 		}
 		}
 		else
 		else
 		{
 		{
-			inheritance.framebuffer = impl.getFramebufferHandle(getGrManagerImpl().getCurrentBackbufferIndex());
+			MicroSwapchainPtr swapchain;
+			U32 backbufferIdx;
+			impl.getDefaultFramebufferInfo(swapchain, backbufferIdx);
+
+			inheritance.framebuffer = impl.getFramebufferHandle(backbufferIdx);
 		}
 		}
 
 
 		begin.flags |= VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;
 		begin.flags |= VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;
@@ -113,6 +118,8 @@ void CommandBufferImpl::beginRenderPass(FramebufferPtr fb,
 	m_rpCommandCount = 0;
 	m_rpCommandCount = 0;
 	m_activeFb = fb;
 	m_activeFb = fb;
 
 
+	fb->m_impl->sync();
+
 	U32 fbWidth, fbHeight;
 	U32 fbWidth, fbHeight;
 	fb->m_impl->getAttachmentsSize(fbWidth, fbHeight);
 	fb->m_impl->getAttachmentsSize(fbWidth, fbHeight);
 	m_fbSize[0] = fbWidth;
 	m_fbSize[0] = fbWidth;
@@ -180,9 +187,12 @@ void CommandBufferImpl::beginRenderPassInternal()
 		// Bind the default FB
 		// Bind the default FB
 		m_renderedToDefaultFb = true;
 		m_renderedToDefaultFb = true;
 
 
-		bi.framebuffer = impl.getFramebufferHandle(getGrManagerImpl().getCurrentBackbufferIndex());
-		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS> dummy;
-		bi.renderPass = impl.getRenderPassHandle(dummy, VK_IMAGE_LAYOUT_MAX_ENUM);
+		MicroSwapchainPtr swapchain;
+		U32 backbufferIdx;
+		impl.getDefaultFramebufferInfo(swapchain, backbufferIdx);
+
+		bi.framebuffer = impl.getFramebufferHandle(backbufferIdx);
+		bi.renderPass = impl.getRenderPassHandle({}, VK_IMAGE_LAYOUT_MAX_ENUM);
 
 
 		// Perform the transition
 		// Perform the transition
 		setImageBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
 		setImageBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
@@ -191,7 +201,7 @@ void CommandBufferImpl::beginRenderPassInternal()
 			VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
 			VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
 			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
 			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
 			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
 			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
-			getGrManagerImpl().getDefaultSurfaceImage(getGrManagerImpl().getCurrentBackbufferIndex()),
+			swapchain->m_images[backbufferIdx],
 			VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1});
 			VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1});
 	}
 	}
 
 
@@ -223,13 +233,17 @@ void CommandBufferImpl::endRenderPass()
 	// Default FB barrier/transition
 	// Default FB barrier/transition
 	if(m_activeFb->m_impl->isDefaultFramebuffer())
 	if(m_activeFb->m_impl->isDefaultFramebuffer())
 	{
 	{
+		MicroSwapchainPtr swapchain;
+		U32 backbufferIdx;
+		m_activeFb->m_impl->getDefaultFramebufferInfo(swapchain, backbufferIdx);
+
 		setImageBarrier(VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
 		setImageBarrier(VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
 			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
 			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
 			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
 			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
 			VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
 			VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
 			VK_ACCESS_MEMORY_READ_BIT,
 			VK_ACCESS_MEMORY_READ_BIT,
 			VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
 			VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
-			getGrManagerImpl().getDefaultSurfaceImage(getGrManagerImpl().getCurrentBackbufferIndex()),
+			swapchain->m_images[backbufferIdx],
 			VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1});
 			VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1});
 	}
 	}
 
 

+ 139 - 150
src/anki/gr/vulkan/FramebufferImpl.cpp

@@ -13,44 +13,85 @@ namespace anki
 
 
 FramebufferImpl::~FramebufferImpl()
 FramebufferImpl::~FramebufferImpl()
 {
 {
-	for(VkFramebuffer fb : m_fbs)
+	if(m_noDflt.m_fb)
 	{
 	{
-		if(fb)
-		{
-			vkDestroyFramebuffer(getDevice(), fb, nullptr);
-		}
+		vkDestroyFramebuffer(getDevice(), m_noDflt.m_fb, nullptr);
 	}
 	}
 
 
-	for(auto it : m_rpasses)
+	for(auto it : m_noDflt.m_rpasses)
 	{
 	{
 		VkRenderPass rpass = it;
 		VkRenderPass rpass = it;
 		ANKI_ASSERT(rpass);
 		ANKI_ASSERT(rpass);
 		vkDestroyRenderPass(getDevice(), rpass, nullptr);
 		vkDestroyRenderPass(getDevice(), rpass, nullptr);
 	}
 	}
 
 
-	m_rpasses.destroy(getAllocator());
+	m_noDflt.m_rpasses.destroy(getAllocator());
 
 
-	if(m_compatibleOrDefaultRpass)
+	if(m_noDflt.m_compatibleRpass)
 	{
 	{
-		vkDestroyRenderPass(getDevice(), m_compatibleOrDefaultRpass, nullptr);
+		vkDestroyRenderPass(getDevice(), m_noDflt.m_compatibleRpass, nullptr);
 	}
 	}
 }
 }
 
 
 Error FramebufferImpl::init(const FramebufferInitInfo& init)
 Error FramebufferImpl::init(const FramebufferInitInfo& init)
 {
 {
+	// Init common
 	m_defaultFb = init.refersToDefaultFramebuffer();
 	m_defaultFb = init.refersToDefaultFramebuffer();
 	strcpy(&m_name[0], (init.getName()) ? init.getName().cstr() : "");
 	strcpy(&m_name[0], (init.getName()) ? init.getName().cstr() : "");
 
 
-	// Create a renderpass.
-	initRpassCreateInfo(init);
-	ANKI_VK_CHECK(vkCreateRenderPass(getDevice(), &m_rpassCi, nullptr, &m_compatibleOrDefaultRpass));
-	getGrManagerImpl().trySetVulkanHandleName(
-		init.getName(), VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, m_compatibleOrDefaultRpass);
+	for(U i = 0; i < init.m_colorAttachmentCount; ++i)
+	{
+		m_colorAttachmentMask.set(i);
+		m_colorAttCount = i + 1;
+	}
 
 
-	// Create the FBs
-	ANKI_CHECK(initFbs(init));
+	if(!m_defaultFb && init.m_depthStencilAttachment.m_texture)
+	{
+		const TextureImpl& tex = *init.m_depthStencilAttachment.m_texture->m_impl;
 
 
-	// Set clear values and some other stuff
+		if(!!(tex.m_workarounds & TextureImplWorkaround::S8_TO_D24S8))
+		{
+			m_aspect = DepthStencilAspectBit::STENCIL;
+		}
+		else if(tex.m_akAspect == DepthStencilAspectBit::DEPTH)
+		{
+			m_aspect = DepthStencilAspectBit::DEPTH;
+		}
+		else if(tex.m_akAspect == DepthStencilAspectBit::STENCIL)
+		{
+			m_aspect = DepthStencilAspectBit::STENCIL;
+		}
+		else
+		{
+			ANKI_ASSERT(!!init.m_depthStencilAttachment.m_aspect);
+			m_aspect = init.m_depthStencilAttachment.m_aspect;
+		}
+	}
+
+	initClearValues(init);
+
+	if(m_defaultFb)
+	{
+		m_dflt.m_swapchain = getGrManagerImpl().getSwapchain();
+		m_dflt.m_loadOp = convertLoadOp(init.m_colorAttachments[0].m_loadOperation);
+	}
+	else
+	{
+		// Create a renderpass.
+		initRpassCreateInfo(init);
+		ANKI_VK_CHECK(vkCreateRenderPass(getDevice(), &m_noDflt.m_rpassCi, nullptr, &m_noDflt.m_compatibleRpass));
+		getGrManagerImpl().trySetVulkanHandleName(
+			init.getName(), VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, m_noDflt.m_compatibleRpass);
+
+		// Create the FB
+		ANKI_CHECK(initFbs(init));
+	}
+
+	return Error::NONE;
+}
+
+void FramebufferImpl::initClearValues(const FramebufferInitInfo& init)
+{
 	for(U i = 0; i < m_colorAttCount; ++i)
 	for(U i = 0; i < m_colorAttCount; ++i)
 	{
 	{
 		if(init.m_colorAttachments[i].m_loadOperation == AttachmentLoadOperation::CLEAR)
 		if(init.m_colorAttachments[i].m_loadOperation == AttachmentLoadOperation::CLEAR)
@@ -65,8 +106,6 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 		{
 		{
 			m_clearVals[i] = {};
 			m_clearVals[i] = {};
 		}
 		}
-
-		m_attachedSurfaces[i] = init.m_colorAttachments[i].m_surface;
 	}
 	}
 
 
 	if(hasDepthStencil())
 	if(hasDepthStencil())
@@ -84,129 +123,70 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 		{
 		{
 			m_clearVals[m_colorAttCount] = {};
 			m_clearVals[m_colorAttCount] = {};
 		}
 		}
-
-		m_attachedSurfaces[MAX_COLOR_ATTACHMENTS] = init.m_depthStencilAttachment.m_surface;
 	}
 	}
-
-	return Error::NONE;
 }
 }
 
 
 Error FramebufferImpl::initFbs(const FramebufferInitInfo& init)
 Error FramebufferImpl::initFbs(const FramebufferInitInfo& init)
 {
 {
-	const Bool hasDepthStencil = init.m_depthStencilAttachment.m_texture == true;
-
 	VkFramebufferCreateInfo ci = {};
 	VkFramebufferCreateInfo ci = {};
 	ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
 	ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
-	ci.renderPass = m_compatibleOrDefaultRpass;
-	ci.attachmentCount = init.m_colorAttachmentCount + ((hasDepthStencil) ? 1 : 0);
-
+	ci.renderPass = m_noDflt.m_compatibleRpass;
+	ci.attachmentCount = init.m_colorAttachmentCount + ((hasDepthStencil()) ? 1 : 0);
 	ci.layers = 1;
 	ci.layers = 1;
 
 
-	if(m_defaultFb)
+	Array<VkImageView, MAX_COLOR_ATTACHMENTS + 1> imgViews;
+	U count = 0;
+
+	for(U i = 0; i < init.m_colorAttachmentCount; ++i)
 	{
 	{
-		for(U i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
-		{
-			VkImageView view = getGrManagerImpl().getDefaultSurfaceImageView(i);
-			ci.pAttachments = &view;
+		const FramebufferAttachmentInfo& att = init.m_colorAttachments[i];
+		TextureImpl& tex = *att.m_texture->m_impl;
 
 
-			m_width = getGrManagerImpl().getDefaultSurfaceWidth();
-			m_height = getGrManagerImpl().getDefaultSurfaceHeight();
-			ci.width = m_width;
-			ci.height = m_height;
+		imgViews[count++] = tex.getOrCreateSingleSurfaceView(att.m_surface, att.m_aspect);
 
 
-			ANKI_VK_CHECK(vkCreateFramebuffer(getDevice(), &ci, nullptr, &m_fbs[i]));
-			getGrManagerImpl().trySetVulkanHandleName(
-				init.getName(), VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT, m_fbs[i]);
+		if(m_noDflt.m_width == 0)
+		{
+			m_noDflt.m_width = tex.m_width >> att.m_surface.m_level;
+			m_noDflt.m_height = tex.m_height >> att.m_surface.m_level;
 		}
 		}
 
 
-		m_colorAttachmentMask.set(0);
-		m_colorAttCount = 1;
+		m_noDflt.m_refs[i] = att.m_texture;
 	}
 	}
-	else
-	{
-		Array<VkImageView, MAX_COLOR_ATTACHMENTS + 1> attachments;
-		U count = 0;
-
-		for(U i = 0; i < init.m_colorAttachmentCount; ++i)
-		{
-			const FramebufferAttachmentInfo& att = init.m_colorAttachments[i];
-			TextureImpl& tex = *att.m_texture->m_impl;
 
 
-			attachments[count++] = tex.getOrCreateSingleSurfaceView(att.m_surface, att.m_aspect);
-
-			if(m_width == 0)
-			{
-				m_width = tex.m_width >> att.m_surface.m_level;
-				m_height = tex.m_height >> att.m_surface.m_level;
-			}
+	if(hasDepthStencil())
+	{
+		const FramebufferAttachmentInfo& att = init.m_depthStencilAttachment;
+		TextureImpl& tex = *att.m_texture->m_impl;
 
 
-			m_refs[i] = att.m_texture;
-			m_colorAttachmentMask.set(i);
-			m_colorAttCount = i + 1;
-		}
+		imgViews[count++] = tex.getOrCreateSingleSurfaceView(att.m_surface, m_aspect);
 
 
-		if(hasDepthStencil)
+		if(m_noDflt.m_width == 0)
 		{
 		{
-			const FramebufferAttachmentInfo& att = init.m_depthStencilAttachment;
-			TextureImpl& tex = *att.m_texture->m_impl;
-
-			DepthStencilAspectBit aspect;
-			if(!!(tex.m_workarounds & TextureImplWorkaround::S8_TO_D24S8))
-			{
-				aspect = DepthStencilAspectBit::STENCIL;
-			}
-			else if(tex.m_akAspect == DepthStencilAspectBit::DEPTH)
-			{
-				aspect = DepthStencilAspectBit::DEPTH;
-			}
-			else if(tex.m_akAspect == DepthStencilAspectBit::STENCIL)
-			{
-				aspect = DepthStencilAspectBit::STENCIL;
-			}
-			else
-			{
-				ANKI_ASSERT(!!att.m_aspect);
-				aspect = att.m_aspect;
-			}
-
-			m_depthAttachment = !!(aspect & DepthStencilAspectBit::DEPTH);
-			m_stencilAttachment = !!(aspect & DepthStencilAspectBit::STENCIL);
-
-			attachments[count++] = tex.getOrCreateSingleSurfaceView(att.m_surface, aspect);
-
-			if(m_width == 0)
-			{
-				m_width = tex.m_width >> att.m_surface.m_level;
-				m_height = tex.m_height >> att.m_surface.m_level;
-			}
-
-			m_refs[MAX_COLOR_ATTACHMENTS] = att.m_texture;
+			m_noDflt.m_width = tex.m_width >> att.m_surface.m_level;
+			m_noDflt.m_height = tex.m_height >> att.m_surface.m_level;
 		}
 		}
 
 
-		ci.width = m_width;
-		ci.height = m_height;
+		m_noDflt.m_refs[MAX_COLOR_ATTACHMENTS] = att.m_texture;
+	}
 
 
-		ci.pAttachments = &attachments[0];
-		ANKI_ASSERT(count == ci.attachmentCount);
+	ci.width = m_noDflt.m_width;
+	ci.height = m_noDflt.m_height;
 
 
-		ANKI_VK_CHECK(vkCreateFramebuffer(getDevice(), &ci, nullptr, &m_fbs[0]));
-		getGrManagerImpl().trySetVulkanHandleName(
-			init.getName(), VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT, m_fbs[0]);
-	}
+	ci.pAttachments = &imgViews[0];
+	ANKI_ASSERT(count == ci.attachmentCount);
+
+	ANKI_VK_CHECK(vkCreateFramebuffer(getDevice(), &ci, nullptr, &m_noDflt.m_fb));
+	getGrManagerImpl().trySetVulkanHandleName(
+		init.getName(), VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT, m_noDflt.m_fb);
 
 
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
 void FramebufferImpl::setupAttachmentDescriptor(
 void FramebufferImpl::setupAttachmentDescriptor(
-	const FramebufferAttachmentInfo& att, VkAttachmentDescription& desc) const
+	const FramebufferAttachmentInfo& att, VkAttachmentDescription& desc, VkImageLayout layout) const
 {
 {
-	// TODO This func won't work if it's default but this is a depth attachment
-
-	const VkImageLayout layout = (m_defaultFb) ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL;
-
 	desc = {};
 	desc = {};
-	desc.format = (m_defaultFb) ? getGrManagerImpl().getDefaultFramebufferSurfaceFormat()
-								: convertFormat(att.m_texture->m_impl->m_format);
+	desc.format = convertFormat(att.m_texture->m_impl->m_format);
 	desc.samples = VK_SAMPLE_COUNT_1_BIT;
 	desc.samples = VK_SAMPLE_COUNT_1_BIT;
 	desc.loadOp = convertLoadOp(att.m_loadOperation);
 	desc.loadOp = convertLoadOp(att.m_loadOperation);
 	desc.storeOp = convertStoreOp(att.m_storeOperation);
 	desc.storeOp = convertStoreOp(att.m_storeOperation);
@@ -218,62 +198,60 @@ void FramebufferImpl::setupAttachmentDescriptor(
 
 
 void FramebufferImpl::initRpassCreateInfo(const FramebufferInitInfo& init)
 void FramebufferImpl::initRpassCreateInfo(const FramebufferInitInfo& init)
 {
 {
+	// Setup attachments and references
 	for(U i = 0; i < init.m_colorAttachmentCount; ++i)
 	for(U i = 0; i < init.m_colorAttachmentCount; ++i)
 	{
 	{
-		const FramebufferAttachmentInfo& att = init.m_colorAttachments[i];
-
-		setupAttachmentDescriptor(att, m_attachmentDescriptions[i]);
+		setupAttachmentDescriptor(
+			init.m_colorAttachments[i], m_noDflt.m_attachmentDescriptions[i], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
 
 
-		m_references[i].attachment = i;
-		m_references[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+		m_noDflt.m_references[i].attachment = i;
+		m_noDflt.m_references[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 	}
 	}
 
 
-	const Bool hasDepthStencil = init.m_depthStencilAttachment.m_texture == true;
-	if(hasDepthStencil)
+	if(hasDepthStencil())
 	{
 	{
-		VkAttachmentReference& dsReference = m_references[init.m_colorAttachmentCount];
-
-		setupAttachmentDescriptor(init.m_depthStencilAttachment, m_attachmentDescriptions[init.m_colorAttachmentCount]);
+		setupAttachmentDescriptor(init.m_depthStencilAttachment,
+			m_noDflt.m_attachmentDescriptions[init.m_colorAttachmentCount],
+			VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
 
 
+		VkAttachmentReference& dsReference = m_noDflt.m_references[init.m_colorAttachmentCount];
 		dsReference.attachment = init.m_colorAttachmentCount;
 		dsReference.attachment = init.m_colorAttachmentCount;
 		dsReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 		dsReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 	}
 	}
 
 
 	// Setup the render pass
 	// Setup the render pass
-	m_rpassCi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
-	m_rpassCi.pAttachments = &m_attachmentDescriptions[0];
-	m_rpassCi.attachmentCount = init.m_colorAttachmentCount + ((hasDepthStencil) ? 1 : 0);
+	m_noDflt.m_rpassCi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+	m_noDflt.m_rpassCi.pAttachments = &m_noDflt.m_attachmentDescriptions[0];
+	m_noDflt.m_rpassCi.attachmentCount = init.m_colorAttachmentCount + ((hasDepthStencil()) ? 1 : 0);
 
 
 	// Subpass
 	// Subpass
-	m_subpassDescr.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
-	m_subpassDescr.colorAttachmentCount = init.m_colorAttachmentCount;
-	m_subpassDescr.pColorAttachments = (init.m_colorAttachmentCount) ? &m_references[0] : nullptr;
-	m_subpassDescr.pDepthStencilAttachment = (hasDepthStencil) ? &m_references[init.m_colorAttachmentCount] : nullptr;
-
-	m_rpassCi.subpassCount = 1;
-	m_rpassCi.pSubpasses = &m_subpassDescr;
+	m_noDflt.m_subpassDescr.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+	m_noDflt.m_subpassDescr.colorAttachmentCount = init.m_colorAttachmentCount;
+	m_noDflt.m_subpassDescr.pColorAttachments = (init.m_colorAttachmentCount) ? &m_noDflt.m_references[0] : nullptr;
+	m_noDflt.m_subpassDescr.pDepthStencilAttachment =
+		(hasDepthStencil()) ? &m_noDflt.m_references[init.m_colorAttachmentCount] : nullptr;
+
+	m_noDflt.m_rpassCi.subpassCount = 1;
+	m_noDflt.m_rpassCi.pSubpasses = &m_noDflt.m_subpassDescr;
 }
 }
 
 
 VkRenderPass FramebufferImpl::getRenderPassHandle(
 VkRenderPass FramebufferImpl::getRenderPassHandle(
 	const Array<VkImageLayout, MAX_COLOR_ATTACHMENTS>& colorLayouts, VkImageLayout dsLayout)
 	const Array<VkImageLayout, MAX_COLOR_ATTACHMENTS>& colorLayouts, VkImageLayout dsLayout)
 {
 {
-	VkRenderPass out;
+	VkRenderPass out = {};
 
 
 	if(!m_defaultFb)
 	if(!m_defaultFb)
 	{
 	{
 		// Create hash
 		// Create hash
 		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS + 1> allLayouts;
 		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS + 1> allLayouts;
 		U allLayoutCount = 0;
 		U allLayoutCount = 0;
-		for(U i = 0; i < MAX_COLOR_ATTACHMENTS; ++i)
+		for(U i = 0; i < m_colorAttCount; ++i)
 		{
 		{
-			if(m_colorAttachmentMask.get(i))
-			{
-				ANKI_ASSERT(colorLayouts[i] != VK_IMAGE_LAYOUT_UNDEFINED);
-				allLayouts[allLayoutCount++] = colorLayouts[i];
-			}
+			ANKI_ASSERT(colorLayouts[i] != VK_IMAGE_LAYOUT_UNDEFINED);
+			allLayouts[allLayoutCount++] = colorLayouts[i];
 		}
 		}
 
 
-		if(m_depthAttachment || m_stencilAttachment)
+		if(hasDepthStencil())
 		{
 		{
 			ANKI_ASSERT(dsLayout != VK_IMAGE_LAYOUT_UNDEFINED);
 			ANKI_ASSERT(dsLayout != VK_IMAGE_LAYOUT_UNDEFINED);
 			allLayouts[allLayoutCount++] = dsLayout;
 			allLayouts[allLayoutCount++] = dsLayout;
@@ -282,9 +260,9 @@ VkRenderPass FramebufferImpl::getRenderPassHandle(
 		U64 hash = computeHash(&allLayouts[0], allLayoutCount * sizeof(allLayouts[0]));
 		U64 hash = computeHash(&allLayouts[0], allLayoutCount * sizeof(allLayouts[0]));
 
 
 		// Get or create
 		// Get or create
-		LockGuard<Mutex> lock(m_rpassesMtx);
-		auto it = m_rpasses.find(hash);
-		if(it != m_rpasses.getEnd())
+		LockGuard<Mutex> lock(m_noDflt.m_rpassesMtx);
+		auto it = m_noDflt.m_rpasses.find(hash);
+		if(it != m_noDflt.m_rpasses.getEnd())
 		{
 		{
 			out = *it;
 			out = *it;
 		}
 		}
@@ -292,10 +270,11 @@ VkRenderPass FramebufferImpl::getRenderPassHandle(
 		{
 		{
 			// Create
 			// Create
 
 
-			VkRenderPassCreateInfo ci = m_rpassCi;
-			Array<VkAttachmentDescription, MAX_COLOR_ATTACHMENTS + 1> attachmentDescriptions = m_attachmentDescriptions;
-			Array<VkAttachmentReference, MAX_COLOR_ATTACHMENTS + 1> references = m_references;
-			VkSubpassDescription subpassDescr = m_subpassDescr;
+			VkRenderPassCreateInfo ci = m_noDflt.m_rpassCi;
+			Array<VkAttachmentDescription, MAX_COLOR_ATTACHMENTS + 1> attachmentDescriptions =
+				m_noDflt.m_attachmentDescriptions;
+			Array<VkAttachmentReference, MAX_COLOR_ATTACHMENTS + 1> references = m_noDflt.m_references;
+			VkSubpassDescription subpassDescr = m_noDflt.m_subpassDescr;
 
 
 			// Fix pointers
 			// Fix pointers
 			subpassDescr.pColorAttachments = &references[0];
 			subpassDescr.pColorAttachments = &references[0];
@@ -313,7 +292,7 @@ VkRenderPass FramebufferImpl::getRenderPassHandle(
 				references[i].layout = lay;
 				references[i].layout = lay;
 			}
 			}
 
 
-			if(m_refs[MAX_COLOR_ATTACHMENTS])
+			if(hasDepthStencil())
 			{
 			{
 				const U i = subpassDescr.colorAttachmentCount;
 				const U i = subpassDescr.colorAttachmentCount;
 				const VkImageLayout lay = dsLayout;
 				const VkImageLayout lay = dsLayout;
@@ -329,16 +308,26 @@ VkRenderPass FramebufferImpl::getRenderPassHandle(
 			ANKI_VK_CHECKF(vkCreateRenderPass(getDevice(), &ci, nullptr, &out));
 			ANKI_VK_CHECKF(vkCreateRenderPass(getDevice(), &ci, nullptr, &out));
 			getGrManagerImpl().trySetVulkanHandleName(&m_name[0], VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, out);
 			getGrManagerImpl().trySetVulkanHandleName(&m_name[0], VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, out);
 
 
-			m_rpasses.emplace(getAllocator(), hash, out);
+			m_noDflt.m_rpasses.emplace(getAllocator(), hash, out);
 		}
 		}
 	}
 	}
 	else
 	else
 	{
 	{
-		out = m_compatibleOrDefaultRpass;
+		out = m_dflt.m_swapchain->getRenderPass(m_dflt.m_loadOp);
 	}
 	}
 
 
 	ANKI_ASSERT(out);
 	ANKI_ASSERT(out);
 	return out;
 	return out;
 }
 }
 
 
+void FramebufferImpl::sync()
+{
+	if(m_defaultFb)
+	{
+		LockGuard<SpinLock> lock(m_dflt.m_swapchainLock);
+		m_dflt.m_swapchain = getGrManagerImpl().getSwapchain();
+		m_dflt.m_currentBackbufferIndex = m_dflt.m_swapchain->m_currentBackbufferIndex;
+	}
+}
+
 } // end namespace anki
 } // end namespace anki

+ 79 - 38
src/anki/gr/vulkan/FramebufferImpl.h

@@ -6,6 +6,7 @@
 #pragma once
 #pragma once
 
 
 #include <anki/gr/vulkan/VulkanObject.h>
 #include <anki/gr/vulkan/VulkanObject.h>
+#include <anki/gr/vulkan/SwapchainFactory.h>
 #include <anki/util/HashMap.h>
 #include <anki/util/HashMap.h>
 #include <anki/util/BitSet.h>
 #include <anki/util/BitSet.h>
 
 
@@ -34,25 +35,42 @@ public:
 	/// Good for pipeline creation.
 	/// Good for pipeline creation.
 	VkRenderPass getCompatibleRenderPass() const
 	VkRenderPass getCompatibleRenderPass() const
 	{
 	{
-		ANKI_ASSERT(m_compatibleOrDefaultRpass);
-		return m_compatibleOrDefaultRpass;
+		if(!m_defaultFb)
+		{
+			ANKI_ASSERT(m_noDflt.m_compatibleRpass);
+			return m_noDflt.m_compatibleRpass;
+		}
+		else
+		{
+			return m_dflt.m_swapchain->getRenderPass(m_dflt.m_loadOp);
+		}
 	}
 	}
 
 
-	/// Use it for binding.
+	/// Sync it before you bind it. It's thread-safe
+	void sync();
+
+	/// Use it for binding. It's thread-safe
 	VkRenderPass getRenderPassHandle(
 	VkRenderPass getRenderPassHandle(
 		const Array<VkImageLayout, MAX_COLOR_ATTACHMENTS>& colorLayouts, VkImageLayout dsLayout);
 		const Array<VkImageLayout, MAX_COLOR_ATTACHMENTS>& colorLayouts, VkImageLayout dsLayout);
 
 
 	VkFramebuffer getFramebufferHandle(U frame) const
 	VkFramebuffer getFramebufferHandle(U frame) const
 	{
 	{
-		ANKI_ASSERT(m_fbs[frame]);
-		return m_fbs[frame];
+		if(!m_defaultFb)
+		{
+			ANKI_ASSERT(m_noDflt.m_fb);
+			return m_noDflt.m_fb;
+		}
+		else
+		{
+			return m_dflt.m_swapchain->m_framebuffers[frame];
+		}
 	}
 	}
 
 
 	void getAttachmentInfo(BitSet<MAX_COLOR_ATTACHMENTS, U8>& colorAttachments, Bool& depth, Bool& stencil) const
 	void getAttachmentInfo(BitSet<MAX_COLOR_ATTACHMENTS, U8>& colorAttachments, Bool& depth, Bool& stencil) const
 	{
 	{
 		colorAttachments = m_colorAttachmentMask;
 		colorAttachments = m_colorAttachmentMask;
-		depth = m_depthAttachment;
-		stencil = m_stencilAttachment;
+		depth = !!(m_aspect & DepthStencilAspectBit::DEPTH);
+		stencil = !!(m_aspect & DepthStencilAspectBit::STENCIL);
 	}
 	}
 
 
 	U getColorAttachmentCount() const
 	U getColorAttachmentCount() const
@@ -62,7 +80,7 @@ public:
 
 
 	Bool hasDepthStencil() const
 	Bool hasDepthStencil() const
 	{
 	{
-		return m_refs[MAX_COLOR_ATTACHMENTS].isCreated();
+		return !!m_aspect;
 	}
 	}
 
 
 	U getAttachmentCount() const
 	U getAttachmentCount() const
@@ -72,14 +90,14 @@ public:
 
 
 	TexturePtr getColorAttachment(U att) const
 	TexturePtr getColorAttachment(U att) const
 	{
 	{
-		ANKI_ASSERT(m_refs[att].get());
-		return m_refs[att];
+		ANKI_ASSERT(m_noDflt.m_refs[att].get());
+		return m_noDflt.m_refs[att];
 	}
 	}
 
 
 	TexturePtr getDepthStencilAttachment() const
 	TexturePtr getDepthStencilAttachment() const
 	{
 	{
-		ANKI_ASSERT(m_refs[MAX_COLOR_ATTACHMENTS].get());
-		return m_refs[MAX_COLOR_ATTACHMENTS];
+		ANKI_ASSERT(m_noDflt.m_refs[MAX_COLOR_ATTACHMENTS].get());
+		return m_noDflt.m_refs[MAX_COLOR_ATTACHMENTS];
 	}
 	}
 
 
 	const VkClearValue* getClearValues() const
 	const VkClearValue* getClearValues() const
@@ -94,50 +112,73 @@ public:
 
 
 	void getAttachmentsSize(U32& width, U32& height) const
 	void getAttachmentsSize(U32& width, U32& height) const
 	{
 	{
-		ANKI_ASSERT(m_width != 0 && m_height != 0);
-		width = m_width;
-		height = m_height;
+		if(!m_defaultFb)
+		{
+			ANKI_ASSERT(m_noDflt.m_width != 0 && m_noDflt.m_height != 0);
+			width = m_noDflt.m_width;
+			height = m_noDflt.m_height;
+		}
+		else
+		{
+			width = m_dflt.m_swapchain->m_surfaceWidth;
+			height = m_dflt.m_swapchain->m_surfaceHeight;
+		}
 	}
 	}
 
 
-	const Array<TextureSurfaceInfo, MAX_COLOR_ATTACHMENTS + 1>& getAttachedSurfaces() const
+	void getDefaultFramebufferInfo(MicroSwapchainPtr& swapchain, U32& crntBackBufferIdx)
 	{
 	{
-		return m_attachedSurfaces;
+		ANKI_ASSERT(m_defaultFb);
+		swapchain = m_dflt.m_swapchain;
+		crntBackBufferIdx = m_dflt.m_currentBackbufferIndex;
 	}
 	}
 
 
 private:
 private:
-	U32 m_width = 0;
-	U32 m_height = 0;
+	Array<char, MAX_GR_OBJECT_NAME_LENGTH + 1> m_name;
 
 
 	Bool8 m_defaultFb = false;
 	Bool8 m_defaultFb = false;
 
 
 	BitSet<MAX_COLOR_ATTACHMENTS, U8> m_colorAttachmentMask = {false};
 	BitSet<MAX_COLOR_ATTACHMENTS, U8> m_colorAttachmentMask = {false};
-	Bool8 m_depthAttachment = false;
-	Bool8 m_stencilAttachment = false;
+	DepthStencilAspectBit m_aspect = DepthStencilAspectBit::NONE;
 
 
 	U8 m_colorAttCount = 0;
 	U8 m_colorAttCount = 0;
 	Array<VkClearValue, MAX_COLOR_ATTACHMENTS + 1> m_clearVals;
 	Array<VkClearValue, MAX_COLOR_ATTACHMENTS + 1> m_clearVals;
 
 
-	Array<TexturePtr, MAX_COLOR_ATTACHMENTS + 1> m_refs; ///< @note The pos of every attachment is fixed.
-	Array<TextureSurfaceInfo, MAX_COLOR_ATTACHMENTS + 1> m_attachedSurfaces = {};
-
-	// RenderPass create info
-	VkRenderPassCreateInfo m_rpassCi = {};
-	Array<VkAttachmentDescription, MAX_COLOR_ATTACHMENTS + 1> m_attachmentDescriptions = {};
-	Array<VkAttachmentReference, MAX_COLOR_ATTACHMENTS + 1> m_references = {};
-	VkSubpassDescription m_subpassDescr = {};
-
-	// VK objects
-	VkRenderPass m_compatibleOrDefaultRpass = {}; ///< Compatible renderpass or default FB's renderpass.
-	HashMap<U64, VkRenderPass> m_rpasses;
-	Mutex m_rpassesMtx;
-	Array<VkFramebuffer, MAX_FRAMES_IN_FLIGHT> m_fbs = {};
-
-	Array<char, MAX_GR_OBJECT_NAME_LENGTH + 1> m_name;
+	class
+	{
+	public:
+		U32 m_width = 0;
+		U32 m_height = 0;
+
+		Array<TexturePtr, MAX_COLOR_ATTACHMENTS + 1> m_refs; ///< @note The pos of every attachment is fixed.
+
+		// RenderPass create info
+		VkRenderPassCreateInfo m_rpassCi = {};
+		Array<VkAttachmentDescription, MAX_COLOR_ATTACHMENTS + 1> m_attachmentDescriptions = {};
+		Array<VkAttachmentReference, MAX_COLOR_ATTACHMENTS + 1> m_references = {};
+		VkSubpassDescription m_subpassDescr = {};
+
+		// VK objects
+		VkRenderPass m_compatibleRpass = {}; ///< Compatible renderpass or default FB's renderpass.
+		HashMap<U64, VkRenderPass> m_rpasses;
+		Mutex m_rpassesMtx;
+		VkFramebuffer m_fb = {};
+	} m_noDflt; ///< Not default FB
+
+	class
+	{
+	public:
+		MicroSwapchainPtr m_swapchain;
+		SpinLock m_swapchainLock;
+		VkAttachmentLoadOp m_loadOp = {};
+		U8 m_currentBackbufferIndex = 0;
+	} m_dflt; ///< Default FB
 
 
 	// Methods
 	// Methods
 	ANKI_USE_RESULT Error initFbs(const FramebufferInitInfo& init);
 	ANKI_USE_RESULT Error initFbs(const FramebufferInitInfo& init);
 	void initRpassCreateInfo(const FramebufferInitInfo& init);
 	void initRpassCreateInfo(const FramebufferInitInfo& init);
-	void setupAttachmentDescriptor(const FramebufferAttachmentInfo& att, VkAttachmentDescription& desc) const;
+	void initClearValues(const FramebufferInitInfo& init);
+	void setupAttachmentDescriptor(
+		const FramebufferAttachmentInfo& att, VkAttachmentDescription& desc, VkImageLayout layout) const;
 };
 };
 /// @}
 /// @}
 
 

+ 32 - 163
src/anki/gr/vulkan/GrManagerImpl.cpp

@@ -30,15 +30,6 @@ GrManagerImpl::~GrManagerImpl()
 	m_cmdbFactory.destroy();
 	m_cmdbFactory.destroy();
 
 
 	// SECOND THING: The destroy everything that has a reference to GrObjects.
 	// SECOND THING: The destroy everything that has a reference to GrObjects.
-	for(auto& x : m_backbuffers)
-	{
-		if(x.m_imageView)
-		{
-			vkDestroyImageView(m_device, x.m_imageView, nullptr);
-			x.m_imageView = VK_NULL_HANDLE;
-		}
-	}
-
 	for(auto& x : m_perFrame)
 	for(auto& x : m_perFrame)
 	{
 	{
 		x.m_presentFence.reset(nullptr);
 		x.m_presentFence.reset(nullptr);
@@ -51,21 +42,19 @@ GrManagerImpl::~GrManagerImpl()
 		getAllocator().deleteInstance(m_samplerCache);
 		getAllocator().deleteInstance(m_samplerCache);
 	}
 	}
 
 
+	m_crntSwapchain.reset(nullptr);
+
 	// THIRD THING: Continue with the rest
 	// THIRD THING: Continue with the rest
 	m_gpuMemManager.destroy();
 	m_gpuMemManager.destroy();
 
 
 	m_barrierFactory.destroy(); // Destroy before fences
 	m_barrierFactory.destroy(); // Destroy before fences
 	m_semaphores.destroy(); // Destroy before fences
 	m_semaphores.destroy(); // Destroy before fences
+	m_swapchainFactory.destroy(); // Destroy before fences
 	m_fences.destroy();
 	m_fences.destroy();
 
 
 	m_pplineLayoutFactory.destroy();
 	m_pplineLayoutFactory.destroy();
 	m_descrFactory.destroy();
 	m_descrFactory.destroy();
 
 
-	if(m_swapchain)
-	{
-		vkDestroySwapchainKHR(m_device, m_swapchain, nullptr);
-	}
-
 	m_pplineCache.destroy(m_device, m_physicalDevice, getAllocator());
 	m_pplineCache.destroy(m_device, m_physicalDevice, getAllocator());
 
 
 	if(m_device)
 	if(m_device)
@@ -125,7 +114,10 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 	ANKI_CHECK(initSurface(init));
 	ANKI_CHECK(initSurface(init));
 	ANKI_CHECK(initDevice(init));
 	ANKI_CHECK(initDevice(init));
 	vkGetDeviceQueue(m_device, m_queueIdx, 0, &m_queue);
 	vkGetDeviceQueue(m_device, m_queueIdx, 0, &m_queue);
-	ANKI_CHECK(initSwapchain(init));
+
+	m_swapchainFactory.init(this, init.m_config->getNumber("window.vsync"));
+
+	m_crntSwapchain = m_swapchainFactory.newInstance();
 
 
 	ANKI_CHECK(m_pplineCache.init(m_device, m_physicalDevice, init.m_cacheDirectory, *init.m_config, getAllocator()));
 	ANKI_CHECK(m_pplineCache.init(m_device, m_physicalDevice, init.m_cacheDirectory, *init.m_config, getAllocator()));
 
 
@@ -570,147 +562,6 @@ Error GrManagerImpl::initDevice(const GrManagerInitInfo& init)
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
-Error GrManagerImpl::initSwapchain(const GrManagerInitInfo& init)
-{
-	VkSurfaceCapabilitiesKHR surfaceProperties;
-	ANKI_VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_physicalDevice, m_surface, &surfaceProperties));
-
-	if(surfaceProperties.currentExtent.width == MAX_U32 || surfaceProperties.currentExtent.height == MAX_U32)
-	{
-		ANKI_VK_LOGE("Wrong surface size");
-		return Error::FUNCTION_FAILED;
-	}
-	m_surfaceWidth = surfaceProperties.currentExtent.width;
-	m_surfaceHeight = surfaceProperties.currentExtent.height;
-
-	uint32_t formatCount;
-	ANKI_VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, nullptr));
-
-	DynamicArrayAuto<VkSurfaceFormatKHR> formats(getAllocator());
-	formats.create(formatCount);
-	ANKI_VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, &formats[0]));
-
-	VkColorSpaceKHR colorspace = VK_COLOR_SPACE_MAX_ENUM_KHR;
-	while(formatCount--)
-	{
-		if(formats[formatCount].format == VK_FORMAT_B8G8R8A8_UNORM)
-		{
-			m_surfaceFormat = formats[formatCount].format;
-			colorspace = formats[formatCount].colorSpace;
-			break;
-		}
-	}
-
-	if(m_surfaceFormat == VK_FORMAT_UNDEFINED)
-	{
-		ANKI_VK_LOGE("Surface format not found");
-		return Error::FUNCTION_FAILED;
-	}
-
-	// Chose present mode
-	uint32_t presentModeCount;
-	vkGetPhysicalDeviceSurfacePresentModesKHR(m_physicalDevice, m_surface, &presentModeCount, nullptr);
-	presentModeCount = min(presentModeCount, 4u);
-	Array<VkPresentModeKHR, 4> presentModes;
-	vkGetPhysicalDeviceSurfacePresentModesKHR(m_physicalDevice, m_surface, &presentModeCount, &presentModes[0]);
-
-	VkPresentModeKHR presentMode = VK_PRESENT_MODE_MAX_ENUM_KHR;
-	if(init.m_config->getNumber("window.vsync"))
-	{
-		ANKI_VK_LOGI("vsync is on");
-		presentMode = VK_PRESENT_MODE_FIFO_KHR;
-	}
-	else
-	{
-		ANKI_VK_LOGI("vsync is off");
-		for(U i = 0; i < presentModeCount; ++i)
-		{
-			if(presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
-			{
-				presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
-				break;
-			}
-			else if(presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)
-			{
-				presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
-				break;
-			}
-		}
-	}
-
-	if(presentMode == VK_PRESENT_MODE_MAX_ENUM_KHR)
-	{
-		ANKI_VK_LOGE("Couldn't find a present mode");
-		return Error::FUNCTION_FAILED;
-	}
-
-	// Create swapchain
-	VkSwapchainCreateInfoKHR ci = {};
-	ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
-	ci.surface = m_surface;
-	ci.minImageCount = MAX_FRAMES_IN_FLIGHT;
-	ci.imageFormat = m_surfaceFormat;
-	ci.imageColorSpace = colorspace;
-	ci.imageExtent = surfaceProperties.currentExtent;
-	ci.imageArrayLayers = 1;
-	ci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
-	ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
-	ci.queueFamilyIndexCount = 1;
-	ci.pQueueFamilyIndices = &m_queueIdx;
-	ci.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
-	ci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
-	ci.presentMode = presentMode;
-	ci.clipped = false;
-	ci.oldSwapchain = VK_NULL_HANDLE;
-
-	ANKI_VK_CHECK(vkCreateSwapchainKHR(m_device, &ci, nullptr, &m_swapchain));
-
-	// Get images
-	uint32_t count = 0;
-	ANKI_VK_CHECK(vkGetSwapchainImagesKHR(m_device, m_swapchain, &count, nullptr));
-	if(count != MAX_FRAMES_IN_FLIGHT)
-	{
-		ANKI_VK_LOGE("Requested a swapchain with %u images but got one with %u", MAX_FRAMES_IN_FLIGHT, count);
-		return Error::FUNCTION_FAILED;
-	}
-
-	ANKI_VK_LOGI("Created a swapchain. Image count: %u, present mode: %u, size %ux%u",
-		count,
-		presentMode,
-		m_surfaceWidth,
-		m_surfaceHeight);
-
-	Array<VkImage, MAX_FRAMES_IN_FLIGHT> images;
-	ANKI_VK_CHECK(vkGetSwapchainImagesKHR(m_device, m_swapchain, &count, &images[0]));
-	for(U i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
-	{
-		m_backbuffers[i].m_image = images[i];
-		ANKI_ASSERT(images[i]);
-	}
-
-	// Create img views
-	for(U i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
-	{
-		VkImageViewCreateInfo ci = {};
-		ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
-		ci.flags = 0;
-		ci.image = m_backbuffers[i].m_image;
-		ci.viewType = VK_IMAGE_VIEW_TYPE_2D;
-		ci.format = m_surfaceFormat;
-		ci.components = {
-			VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A};
-		ci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-		ci.subresourceRange.baseMipLevel = 0;
-		ci.subresourceRange.levelCount = 1;
-		ci.subresourceRange.baseArrayLayer = 0;
-		ci.subresourceRange.layerCount = 1;
-
-		ANKI_VK_CHECK(vkCreateImageView(m_device, &ci, nullptr, &m_backbuffers[i].m_imageView));
-	}
-
-	return Error::NONE;
-}
-
 Error GrManagerImpl::initMemory(const ConfigSet& cfg)
 Error GrManagerImpl::initMemory(const ConfigSet& cfg)
 {
 {
 	vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &m_memoryProperties);
 	vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &m_memoryProperties);
@@ -788,12 +639,16 @@ void GrManagerImpl::beginFrame()
 	// Get new image
 	// Get new image
 	uint32_t imageIdx;
 	uint32_t imageIdx;
 	ANKI_TRACE_START_EVENT(VK_ACQUIRE_IMAGE);
 	ANKI_TRACE_START_EVENT(VK_ACQUIRE_IMAGE);
-	ANKI_VK_CHECKF(vkAcquireNextImageKHR(
-		m_device, m_swapchain, UINT64_MAX, frame.m_acquireSemaphore->getHandle(), fence->getHandle(), &imageIdx));
+	ANKI_VK_CHECKF(vkAcquireNextImageKHR(m_device,
+		m_crntSwapchain->m_swapchain,
+		UINT64_MAX,
+		frame.m_acquireSemaphore->getHandle(),
+		fence->getHandle(),
+		&imageIdx));
 	ANKI_TRACE_STOP_EVENT(VK_ACQUIRE_IMAGE);
 	ANKI_TRACE_STOP_EVENT(VK_ACQUIRE_IMAGE);
 
 
 	ANKI_ASSERT(imageIdx < MAX_FRAMES_IN_FLIGHT);
 	ANKI_ASSERT(imageIdx < MAX_FRAMES_IN_FLIGHT);
-	m_crntBackbufferIdx = imageIdx;
+	m_crntSwapchain->m_currentBackbufferIndex = imageIdx;
 }
 }
 
 
 void GrManagerImpl::endFrame()
 void GrManagerImpl::endFrame()
@@ -824,12 +679,23 @@ void GrManagerImpl::endFrame()
 	present.waitSemaphoreCount = (frame.m_renderSemaphore) ? 1 : 0;
 	present.waitSemaphoreCount = (frame.m_renderSemaphore) ? 1 : 0;
 	present.pWaitSemaphores = (frame.m_renderSemaphore) ? &frame.m_renderSemaphore->getHandle() : nullptr;
 	present.pWaitSemaphores = (frame.m_renderSemaphore) ? &frame.m_renderSemaphore->getHandle() : nullptr;
 	present.swapchainCount = 1;
 	present.swapchainCount = 1;
-	present.pSwapchains = &m_swapchain;
-	present.pImageIndices = &m_crntBackbufferIdx;
+	present.pSwapchains = &m_crntSwapchain->m_swapchain;
+	U32 idx = m_crntSwapchain->m_currentBackbufferIndex;
+	present.pImageIndices = &idx;
 	present.pResults = &res;
 	present.pResults = &res;
 
 
-	ANKI_VK_CHECKF(vkQueuePresentKHR(m_queue, &present));
-	ANKI_VK_CHECKF(res);
+	VkResult res1 = vkQueuePresentKHR(m_queue, &present);
+	if(res1 == VK_ERROR_OUT_OF_DATE_KHR)
+	{
+		ANKI_VK_LOGW("Swapchain is out of date. Will wait for the queue and create a new one");
+		vkQueueWaitIdle(m_queue);
+		m_crntSwapchain = m_swapchainFactory.newInstance();
+	}
+	else
+	{
+		ANKI_VK_CHECKF(res1);
+		ANKI_VK_CHECKF(res);
+	}
 
 
 	m_descrFactory.endFrame();
 	m_descrFactory.endFrame();
 
 
@@ -883,6 +749,9 @@ void GrManagerImpl::flushCommandBuffer(CommandBufferPtr cmdb, FencePtr* outFence
 		submit.pSignalSemaphores = &frame.m_renderSemaphore->getHandle();
 		submit.pSignalSemaphores = &frame.m_renderSemaphore->getHandle();
 
 
 		frame.m_presentFence = fence;
 		frame.m_presentFence = fence;
+
+		// Update the swapchain's fence
+		m_crntSwapchain->setFence(fence);
 	}
 	}
 
 
 	submit.commandBufferCount = 1;
 	submit.commandBufferCount = 1;

+ 21 - 47
src/anki/gr/vulkan/GrManagerImpl.h

@@ -13,6 +13,7 @@
 #include <anki/gr/vulkan/QueryExtra.h>
 #include <anki/gr/vulkan/QueryExtra.h>
 #include <anki/gr/vulkan/DescriptorSet.h>
 #include <anki/gr/vulkan/DescriptorSet.h>
 #include <anki/gr/vulkan/CommandBufferFactory.h>
 #include <anki/gr/vulkan/CommandBufferFactory.h>
+#include <anki/gr/vulkan/SwapchainFactory.h>
 #include <anki/gr/vulkan/PipelineLayout.h>
 #include <anki/gr/vulkan/PipelineLayout.h>
 #include <anki/gr/vulkan/PipelineCache.h>
 #include <anki/gr/vulkan/PipelineCache.h>
 #include <anki/util/HashMap.h>
 #include <anki/util/HashMap.h>
@@ -85,40 +86,6 @@ public:
 	}
 	}
 	/// @}
 	/// @}
 
 
-	VkFormat getDefaultFramebufferSurfaceFormat() const
-	{
-		return m_surfaceFormat;
-	}
-
-	VkImageView getDefaultSurfaceImageView(U idx) const
-	{
-		ANKI_ASSERT(m_backbuffers[idx].m_imageView);
-		return m_backbuffers[idx].m_imageView;
-	}
-
-	VkImage getDefaultSurfaceImage(U idx) const
-	{
-		ANKI_ASSERT(m_backbuffers[idx].m_image);
-		return m_backbuffers[idx].m_image;
-	}
-
-	U getCurrentBackbufferIndex() const
-	{
-		return m_crntBackbufferIdx;
-	}
-
-	U getDefaultSurfaceWidth() const
-	{
-		ANKI_ASSERT(m_surfaceWidth);
-		return m_surfaceWidth;
-	}
-
-	U getDefaultSurfaceHeight() const
-	{
-		ANKI_ASSERT(m_surfaceHeight);
-		return m_surfaceHeight;
-	}
-
 	void flushCommandBuffer(CommandBufferPtr ptr, FencePtr* fence, Bool wait = false);
 	void flushCommandBuffer(CommandBufferPtr ptr, FencePtr* fence, Bool wait = false);
 
 
 	void finishCommandBuffer(CommandBufferPtr ptr)
 	void finishCommandBuffer(CommandBufferPtr ptr)
@@ -191,6 +158,22 @@ public:
 		return m_extensions;
 		return m_extensions;
 	}
 	}
 
 
+	MicroSwapchainPtr getSwapchain() const
+	{
+		return m_crntSwapchain;
+	}
+
+	VkSurfaceKHR getSurface() const
+	{
+		ANKI_ASSERT(m_surface);
+		return m_surface;
+	}
+
+	U32 getGraphicsQueueIndex() const
+	{
+		return m_queueIdx;
+	}
+
 	void trySetVulkanHandleName(CString name, VkDebugReportObjectTypeEXT type, U64 handle) const;
 	void trySetVulkanHandleName(CString name, VkDebugReportObjectTypeEXT type, U64 handle) const;
 
 
 	void trySetVulkanHandleName(CString name, VkDebugReportObjectTypeEXT type, void* handle) const
 	void trySetVulkanHandleName(CString name, VkDebugReportObjectTypeEXT type, void* handle) const
@@ -232,13 +215,6 @@ private:
 
 
 	/// @name Surface_related
 	/// @name Surface_related
 	/// @{
 	/// @{
-	class Backbuffer
-	{
-	public:
-		VkImage m_image = VK_NULL_HANDLE;
-		VkImageView m_imageView = VK_NULL_HANDLE;
-	};
-
 	class PerFrame
 	class PerFrame
 	{
 	{
 	public:
 	public:
@@ -250,12 +226,9 @@ private:
 	};
 	};
 
 
 	VkSurfaceKHR m_surface = VK_NULL_HANDLE;
 	VkSurfaceKHR m_surface = VK_NULL_HANDLE;
-	U32 m_surfaceWidth = 0, m_surfaceHeight = 0;
-	VkFormat m_surfaceFormat = VK_FORMAT_UNDEFINED;
-	VkSwapchainKHR m_swapchain = VK_NULL_HANDLE;
+	MicroSwapchainPtr m_crntSwapchain;
+
 	Array<PerFrame, MAX_FRAMES_IN_FLIGHT> m_perFrame;
 	Array<PerFrame, MAX_FRAMES_IN_FLIGHT> m_perFrame;
-	Array<Backbuffer, MAX_FRAMES_IN_FLIGHT> m_backbuffers;
-	U32 m_crntBackbufferIdx = 0;
 	/// @}
 	/// @}
 
 
 	/// @name Memory
 	/// @name Memory
@@ -273,6 +246,8 @@ private:
 	DeferredBarrierFactory m_barrierFactory;
 	DeferredBarrierFactory m_barrierFactory;
 	/// @}
 	/// @}
 
 
+	SwapchainFactory m_swapchainFactory;
+
 	PipelineLayoutFactory m_pplineLayoutFactory;
 	PipelineLayoutFactory m_pplineLayoutFactory;
 
 
 	DescriptorSetFactory m_descrFactory;
 	DescriptorSetFactory m_descrFactory;
@@ -294,7 +269,6 @@ private:
 	ANKI_USE_RESULT Error initInstance(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initInstance(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initSurface(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initSurface(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initDevice(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initDevice(const GrManagerInitInfo& init);
-	ANKI_USE_RESULT Error initSwapchain(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initFramebuffers(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initFramebuffers(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initMemory(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initMemory(const ConfigSet& cfg);
 
 

+ 376 - 1
src/anki/gr/vulkan/SwapchainFactory.cpp

@@ -3,4 +3,379 @@
 // Code licensed under the BSD License.
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
-#include <anki/gr/vulkan/SwapchainFactory.h>
+#include <anki/gr/vulkan/SwapchainFactory.h>
+#include <anki/gr/vulkan/GrManagerImpl.h>
+
+namespace anki
+{
+
+MicroSwapchain::MicroSwapchain(SwapchainFactory* factory)
+	: m_factory(factory)
+{
+	ANKI_ASSERT(factory);
+
+	if(initInternal())
+	{
+		ANKI_VK_LOGF("Error creating the swapchain. Will not try to recover");
+	}
+}
+
+MicroSwapchain::~MicroSwapchain()
+{
+	const VkDevice dev = m_factory->m_gr->getDevice();
+
+	for(VkFramebuffer& fb : m_framebuffers)
+	{
+		if(fb)
+		{
+			vkDestroyFramebuffer(dev, fb, nullptr);
+			fb = {};
+		}
+	}
+
+	for(VkRenderPass& rpass : m_rpasses)
+	{
+		if(rpass)
+		{
+			vkDestroyRenderPass(dev, rpass, nullptr);
+			rpass = {};
+		}
+	}
+
+	for(VkImageView& iview : m_imageViews)
+	{
+		if(iview)
+		{
+			vkDestroyImageView(dev, iview, nullptr);
+			iview = {};
+		}
+	}
+
+	if(m_swapchain)
+	{
+		vkDestroySwapchainKHR(dev, m_swapchain, nullptr);
+		m_swapchain = {};
+		m_images = {};
+	}
+}
+
+Error MicroSwapchain::initInternal()
+{
+	const VkDevice dev = m_factory->m_gr->getDevice();
+
+	// Get the surface size
+	VkSurfaceCapabilitiesKHR surfaceProperties;
+	{
+		ANKI_VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
+			m_factory->m_gr->getPhysicalDevice(), m_factory->m_gr->getSurface(), &surfaceProperties));
+
+		if(surfaceProperties.currentExtent.width == MAX_U32 || surfaceProperties.currentExtent.height == MAX_U32)
+		{
+			ANKI_VK_LOGE("Wrong surface size");
+			return Error::FUNCTION_FAILED;
+		}
+		m_surfaceWidth = surfaceProperties.currentExtent.width;
+		m_surfaceHeight = surfaceProperties.currentExtent.height;
+	}
+
+	// Get the surface format
+	VkColorSpaceKHR colorspace = VK_COLOR_SPACE_MAX_ENUM_KHR;
+	{
+		uint32_t formatCount;
+		ANKI_VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(
+			m_factory->m_gr->getPhysicalDevice(), m_factory->m_gr->getSurface(), &formatCount, nullptr));
+
+		DynamicArrayAuto<VkSurfaceFormatKHR> formats(getAllocator());
+		formats.create(formatCount);
+		ANKI_VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(
+			m_factory->m_gr->getPhysicalDevice(), m_factory->m_gr->getSurface(), &formatCount, &formats[0]));
+
+		while(formatCount--)
+		{
+			if(formats[formatCount].format == VK_FORMAT_B8G8R8A8_UNORM)
+			{
+				m_surfaceFormat = formats[formatCount].format;
+				colorspace = formats[formatCount].colorSpace;
+				break;
+			}
+		}
+
+		if(m_surfaceFormat == VK_FORMAT_UNDEFINED)
+		{
+			ANKI_VK_LOGE("Surface format not found");
+			return Error::FUNCTION_FAILED;
+		}
+	}
+
+	// Chose present mode
+	VkPresentModeKHR presentMode = VK_PRESENT_MODE_MAX_ENUM_KHR;
+	{
+		uint32_t presentModeCount;
+		vkGetPhysicalDeviceSurfacePresentModesKHR(
+			m_factory->m_gr->getPhysicalDevice(), m_factory->m_gr->getSurface(), &presentModeCount, nullptr);
+		presentModeCount = min(presentModeCount, 4u);
+		Array<VkPresentModeKHR, 4> presentModes;
+		vkGetPhysicalDeviceSurfacePresentModesKHR(
+			m_factory->m_gr->getPhysicalDevice(), m_factory->m_gr->getSurface(), &presentModeCount, &presentModes[0]);
+
+		if(m_factory->m_vsync)
+		{
+			presentMode = VK_PRESENT_MODE_FIFO_KHR;
+		}
+		else
+		{
+			for(U i = 0; i < presentModeCount; ++i)
+			{
+				if(presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
+				{
+					presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
+					break;
+				}
+				else if(presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)
+				{
+					presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
+					break;
+				}
+			}
+		}
+
+		if(presentMode == VK_PRESENT_MODE_MAX_ENUM_KHR)
+		{
+			ANKI_VK_LOGE("Couldn't find a present mode");
+			return Error::FUNCTION_FAILED;
+		}
+	}
+
+	// Create swapchain
+	{
+		VkSwapchainCreateInfoKHR ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+		ci.surface = m_factory->m_gr->getSurface();
+		ci.minImageCount = MAX_FRAMES_IN_FLIGHT;
+		ci.imageFormat = m_surfaceFormat;
+		ci.imageColorSpace = colorspace;
+		ci.imageExtent = surfaceProperties.currentExtent;
+		ci.imageArrayLayers = 1;
+		ci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+		ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+		ci.queueFamilyIndexCount = 1;
+		U32 idx = m_factory->m_gr->getGraphicsQueueIndex();
+		ci.pQueueFamilyIndices = &idx;
+		ci.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+		ci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+		ci.presentMode = presentMode;
+		ci.clipped = false;
+		ci.oldSwapchain = VK_NULL_HANDLE;
+
+		ANKI_VK_CHECK(vkCreateSwapchainKHR(dev, &ci, nullptr, &m_swapchain));
+	}
+
+	// Get images
+	{
+		uint32_t count = 0;
+		ANKI_VK_CHECK(vkGetSwapchainImagesKHR(dev, m_swapchain, &count, nullptr));
+		if(count != MAX_FRAMES_IN_FLIGHT)
+		{
+			ANKI_VK_LOGE("Requested a swapchain with %u images but got one with %u", MAX_FRAMES_IN_FLIGHT, count);
+			return Error::FUNCTION_FAILED;
+		}
+
+		ANKI_VK_LOGI("Created a swapchain. Image count: %u, present mode: %u, size: %ux%u, vsync: %u",
+			count,
+			presentMode,
+			m_surfaceWidth,
+			m_surfaceHeight,
+			U32(m_factory->m_vsync));
+
+		Array<VkImage, MAX_FRAMES_IN_FLIGHT> images;
+		ANKI_VK_CHECK(vkGetSwapchainImagesKHR(dev, m_swapchain, &count, &images[0]));
+		for(U i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
+		{
+			m_images[i] = images[i];
+			ANKI_ASSERT(images[i]);
+		}
+	}
+
+	// Create img views
+	for(U i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
+	{
+		VkImageViewCreateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+		ci.flags = 0;
+		ci.image = m_images[i];
+		ci.viewType = VK_IMAGE_VIEW_TYPE_2D;
+		ci.format = m_surfaceFormat;
+		ci.components = {
+			VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A};
+		ci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+		ci.subresourceRange.baseMipLevel = 0;
+		ci.subresourceRange.levelCount = 1;
+		ci.subresourceRange.baseArrayLayer = 0;
+		ci.subresourceRange.layerCount = 1;
+
+		ANKI_VK_CHECK(vkCreateImageView(dev, &ci, nullptr, &m_imageViews[i]));
+		m_factory->m_gr->trySetVulkanHandleName(
+			"DfldImgView", VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT, m_imageViews[i]);
+	}
+
+	// Create the render passes
+	static const Array<VkAttachmentLoadOp, RPASS_COUNT> loadOps = {
+		{VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_LOAD_OP_DONT_CARE}};
+	for(U i = 0; i < RPASS_COUNT; ++i)
+	{
+		const VkImageLayout layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+		VkAttachmentDescription desc = {};
+		desc.format = m_surfaceFormat;
+		desc.samples = VK_SAMPLE_COUNT_1_BIT;
+		desc.loadOp = loadOps[i];
+		desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+		desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+		desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+		desc.initialLayout = layout;
+		desc.finalLayout = layout;
+
+		VkAttachmentReference ref = {0, layout};
+
+		VkSubpassDescription subpass = {};
+		subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+		subpass.colorAttachmentCount = 1;
+		subpass.pColorAttachments = &ref;
+		subpass.pDepthStencilAttachment = nullptr;
+
+		VkRenderPassCreateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+		ci.attachmentCount = 1;
+		ci.pAttachments = &desc;
+		ci.subpassCount = 1;
+		ci.pSubpasses = &subpass;
+
+		ANKI_VK_CHECK(vkCreateRenderPass(dev, &ci, nullptr, &m_rpasses[i]));
+		m_factory->m_gr->trySetVulkanHandleName(
+			"Dfld Rpass", VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, m_rpasses[i]);
+	}
+
+	// Create FBs
+	for(U i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
+	{
+		VkFramebufferCreateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+		ci.renderPass = m_rpasses[0]; // Use that, it's compatible
+		ci.attachmentCount = 1;
+		ci.pAttachments = &m_imageViews[i];
+		ci.width = m_surfaceWidth;
+		ci.height = m_surfaceHeight;
+		ci.layers = 1;
+
+		ANKI_VK_CHECK(vkCreateFramebuffer(dev, &ci, nullptr, &m_framebuffers[i]));
+		m_factory->m_gr->trySetVulkanHandleName(
+			"Dfld FB", VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT, m_framebuffers[i]);
+	}
+
+	return Error::NONE;
+}
+
+GrAllocator<U8> MicroSwapchain::getAllocator() const
+{
+	return m_factory->m_gr->getAllocator();
+}
+
+void SwapchainFactory::destroy()
+{
+	LockGuard<Mutex> lock(m_mtx);
+
+	U count = m_swapchainCount;
+	while(count--)
+	{
+		if(m_swapchains[count]->m_fence)
+		{
+			ANKI_ASSERT(m_swapchains[count]->m_fence->done());
+		}
+
+		m_gr->getAllocator().deleteInstance(m_swapchains[count]);
+#if ANKI_EXTRA_CHECKS
+		--m_swapchainsInFlight;
+#endif
+	}
+
+	m_swapchains.destroy(m_gr->getAllocator());
+
+	ANKI_ASSERT(m_swapchainsInFlight == 0 && "Wrong destroy order");
+}
+
+MicroSwapchainPtr SwapchainFactory::newInstance()
+{
+	LockGuard<Mutex> lock(m_mtx);
+
+	MicroSwapchain* out = nullptr;
+
+	if(m_swapchainCount > 0)
+	{
+		releaseFences();
+
+		U count = m_swapchainCount;
+		while(count--)
+		{
+			if(!m_swapchains[count]->m_fence)
+			{
+				out = m_swapchains[count];
+
+				// Pop it
+				for(U i = count; i < m_swapchainCount - 1; ++i)
+				{
+					m_swapchains[i] = m_swapchains[i + 1];
+				}
+
+				--m_swapchainCount;
+
+				break;
+			}
+		}
+	}
+
+	if(out == nullptr)
+	{
+		// Create a new one
+		out = m_gr->getAllocator().newInstance<MicroSwapchain>(this);
+#if ANKI_EXTRA_CHECKS
+		++m_swapchainsInFlight;
+#endif
+	}
+
+	ANKI_ASSERT(out->m_refcount.get() == 0);
+	return MicroSwapchainPtr(out);
+}
+
+void SwapchainFactory::releaseFences()
+{
+	U count = m_swapchainCount;
+	while(count--)
+	{
+		MicroSwapchain& schain = *m_swapchains[count];
+		if(schain.m_fence && schain.m_fence->done())
+		{
+			schain.m_fence.reset(nullptr);
+		}
+	}
+}
+
+void SwapchainFactory::destroySwapchain(MicroSwapchain* schain)
+{
+	ANKI_ASSERT(schain);
+	ANKI_ASSERT(schain->m_refcount.get() == 0);
+
+	LockGuard<Mutex> lock(m_mtx);
+
+	releaseFences();
+
+	if(m_swapchains.getSize() <= m_swapchainCount)
+	{
+		// Grow storage
+		m_swapchains.resize(m_gr->getAllocator(), max<U>(1, m_swapchains.getSize() * 2));
+	}
+
+	m_swapchains[m_swapchainCount] = schain;
+	++m_swapchainCount;
+}
+
+} // end namespace anki

+ 81 - 4
src/anki/gr/vulkan/SwapchainFactory.h

@@ -20,11 +20,23 @@ class SwapchainFactory;
 /// A wrapper for the swapchain.
 /// A wrapper for the swapchain.
 class MicroSwapchain
 class MicroSwapchain
 {
 {
+	friend class MicroSwapchainPtrDeleter;
+	friend class SwapchainFactory;
+
 public:
 public:
-	VkSwapchainKHR m_swapchain = VK_NULL_HANDLE;
+	VkSwapchainKHR m_swapchain = {};
+
+	Array<VkImage, MAX_FRAMES_IN_FLIGHT> m_images = {};
+	Array<VkImageView, MAX_FRAMES_IN_FLIGHT> m_imageViews = {};
+
+	VkFormat m_surfaceFormat = {};
+
+	U32 m_surfaceWidth = 0;
+	U32 m_surfaceHeight = 0;
+
+	Array<VkFramebuffer, MAX_FRAMES_IN_FLIGHT> m_framebuffers = {};
 
 
-	Array<VkImage, MAX_FRAMES_IN_FLIGHT> m_image = {};
-	Array<VkImageView, MAX_FRAMES_IN_FLIGHT> m_imageView = {};
+	U8 m_currentBackbufferIndex = 0;
 
 
 	MicroSwapchain(SwapchainFactory* factory);
 	MicroSwapchain(SwapchainFactory* factory);
 
 
@@ -37,9 +49,33 @@ public:
 
 
 	GrAllocator<U8> getAllocator() const;
 	GrAllocator<U8> getAllocator() const;
 
 
+	void setFence(MicroFencePtr fence)
+	{
+		m_fence = fence;
+	}
+
+	VkRenderPass getRenderPass(VkAttachmentLoadOp loadOp) const
+	{
+		const U idx = (loadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE) ? RPASS_LOAD_DONT_CARE : RPASS_LOAD_CLEAR;
+		return m_rpasses[idx];
+	}
+
 private:
 private:
 	Atomic<U32> m_refcount = {0};
 	Atomic<U32> m_refcount = {0};
 	SwapchainFactory* m_factory = nullptr;
 	SwapchainFactory* m_factory = nullptr;
+
+	enum
+	{
+		RPASS_LOAD_CLEAR,
+		RPASS_LOAD_DONT_CARE,
+		RPASS_COUNT
+	};
+
+	Array<VkRenderPass, RPASS_COUNT> m_rpasses = {};
+
+	MicroFencePtr m_fence;
+
+	ANKI_USE_RESULT Error initInternal();
 };
 };
 
 
 /// Deleter for MicroSwapchainPtr smart pointer.
 /// Deleter for MicroSwapchainPtr smart pointer.
@@ -51,6 +87,47 @@ public:
 
 
 /// MicroSwapchain smart pointer.
 /// MicroSwapchain smart pointer.
 using MicroSwapchainPtr = IntrusivePtr<MicroSwapchain, MicroSwapchainPtrDeleter>;
 using MicroSwapchainPtr = IntrusivePtr<MicroSwapchain, MicroSwapchainPtrDeleter>;
+
+/// Swapchain factory.
+class SwapchainFactory
+{
+	friend class MicroSwapchainPtrDeleter;
+	friend class MicroSwapchain;
+
+public:
+	void init(GrManagerImpl* manager, Bool vsync)
+	{
+		ANKI_ASSERT(manager);
+		m_gr = manager;
+		m_vsync = vsync;
+	}
+
+	void destroy();
+
+	MicroSwapchainPtr newInstance();
+
+private:
+	GrManagerImpl* m_gr = nullptr;
+	Bool8 m_vsync = false;
+
+	Mutex m_mtx;
+
+	DynamicArray<MicroSwapchain*> m_swapchains;
+	U32 m_swapchainCount = 0;
+#if ANKI_EXTRA_CHECKS
+	U32 m_swapchainsInFlight = 0;
+#endif
+
+	void destroySwapchain(MicroSwapchain* schain);
+
+	void releaseFences();
+};
 /// @}
 /// @}
 
 
-} // end namespace anki
+inline void MicroSwapchainPtrDeleter::operator()(MicroSwapchain* s)
+{
+	ANKI_ASSERT(s);
+	s->m_factory->destroySwapchain(s);
+}
+
+} // end namespace anki

+ 1 - 1
thirdparty

@@ -1 +1 @@
-Subproject commit 39ffb4afb7b2671baccac854dd248b05927043ab
+Subproject commit 3b16e5508d0d280c1a6355f11ceb0d5b672b5752