Browse Source

Make swapchain present explicit

Panagiotis Christopoulos Charitos 7 years ago
parent
commit
ed6709fc9d
36 changed files with 637 additions and 796 deletions
  1. 2 3
      src/anki/core/App.cpp
  2. 2 1
      src/anki/gr/Enums.h
  3. 15 8
      src/anki/gr/Framebuffer.h
  4. 3 2
      src/anki/gr/GrManager.h
  5. 71 85
      src/anki/gr/RenderGraph.cpp
  6. 8 16
      src/anki/gr/RenderGraph.h
  7. 59 89
      src/anki/gr/gl/FramebufferImpl.cpp
  8. 0 1
      src/anki/gr/gl/FramebufferImpl.h
  9. 2 0
      src/anki/gr/gl/GlState.h
  10. 3 2
      src/anki/gr/gl/GrManager.cpp
  11. 31 0
      src/anki/gr/gl/GrManagerImpl.cpp
  12. 7 3
      src/anki/gr/gl/GrManagerImpl.h
  13. 10 1
      src/anki/gr/gl/RenderingThread.cpp
  14. 1 0
      src/anki/gr/gl/TextureImpl.cpp
  15. 19 74
      src/anki/gr/vulkan/CommandBufferImpl.cpp
  16. 1 1
      src/anki/gr/vulkan/CommandBufferImpl.inl.h
  17. 100 125
      src/anki/gr/vulkan/FramebufferImpl.cpp
  18. 33 79
      src/anki/gr/vulkan/FramebufferImpl.h
  19. 2 2
      src/anki/gr/vulkan/GrManager.cpp
  20. 29 6
      src/anki/gr/vulkan/GrManagerImpl.cpp
  21. 4 2
      src/anki/gr/vulkan/GrManagerImpl.h
  22. 1 1
      src/anki/gr/vulkan/Pipeline.h
  23. 1 10
      src/anki/gr/vulkan/ShaderImpl.cpp
  24. 4 1
      src/anki/gr/vulkan/ShaderProgramImpl.cpp
  25. 24 110
      src/anki/gr/vulkan/SwapchainFactory.cpp
  26. 1 11
      src/anki/gr/vulkan/SwapchainFactory.h
  27. 24 13
      src/anki/gr/vulkan/TextureImpl.cpp
  28. 11 3
      src/anki/gr/vulkan/TextureImpl.h
  29. 8 52
      src/anki/renderer/FinalComposite.cpp
  30. 0 7
      src/anki/renderer/FinalComposite.h
  31. 37 9
      src/anki/renderer/MainRenderer.cpp
  32. 15 1
      src/anki/renderer/MainRenderer.h
  33. 1 3
      src/anki/renderer/Renderer.cpp
  34. 6 12
      src/anki/renderer/Renderer.h
  35. 76 50
      tests/gr/Gr.cpp
  36. 26 13
      tests/ui/Ui.cpp

+ 2 - 3
src/anki/core/App.cpp

@@ -565,8 +565,6 @@ Error App::mainLoop()
 		prevUpdateTime = crntTime;
 		crntTime = HighRezTimer::getCurrentTime();
 
-		m_gr->beginFrame();
-
 		// Update
 		ANKI_CHECK(m_input->handleEvents());
 
@@ -583,7 +581,8 @@ Error App::mainLoop()
 		injectStatsUiElement(newUiElementArr, rqueue);
 
 		// Render
-		ANKI_CHECK(m_renderer->render(rqueue));
+		TexturePtr presentableTex = m_gr->acquireNextPresentableTexture();
+		ANKI_CHECK(m_renderer->render(rqueue, presentableTex));
 
 		// Pause and sync async loader. That will force all tasks before the pause to finish in this frame.
 		m_resources->getAsyncLoader().pause();

+ 2 - 1
src/anki/gr/Enums.h

@@ -420,9 +420,10 @@ enum class TextureUsageBit : U16
 	/// @{
 	GENERATE_MIPMAPS = 1 << 11,
 	CLEAR = 1 << 12, ///< Will be used in CommandBuffer::clearImage.
+	PRESENT = 1 << 13,
 	/// @}
 
-	/// @name Other
+	/// @name Groups
 	/// @{
 	ANY_COMPUTE = SAMPLED_COMPUTE | IMAGE_COMPUTE_READ_WRITE,
 	ANY_GRAPHICS = SAMPLED_ALL_GRAPHICS | FRAMEBUFFER_ATTACHMENT_READ_WRITE,

+ 15 - 8
src/anki/gr/Framebuffer.h

@@ -29,8 +29,7 @@ public:
 	ClearValue m_clearValue;
 };
 
-/// Framebuffer initializer. If you require the default framebuffer then set m_colorAttachmentCount to 1 and don't set a
-/// color texture.
+/// Framebuffer initializer.
 class FramebufferInitInfo : public GrBaseInitInfo
 {
 public:
@@ -70,14 +69,22 @@ public:
 		return *this;
 	}
 
-	Bool refersToDefaultFramebuffer() const
-	{
-		return m_colorAttachmentCount == 1 && !m_colorAttachments[0].m_textureView.isCreated();
-	}
-
 	Bool isValid() const
 	{
-		return m_colorAttachmentCount != 0 || m_depthStencilAttachment.m_textureView.isCreated();
+		for(U i = 0; i < m_colorAttachmentCount; ++i)
+		{
+			if(!m_colorAttachments[i].m_textureView.isCreated())
+			{
+				return false;
+			}
+		}
+
+		if(m_colorAttachmentCount == 0 && !m_depthStencilAttachment.m_textureView.isCreated())
+		{
+			return false;
+		}
+
+		return true;
 	}
 };
 

+ 3 - 2
src/anki/gr/GrManager.h

@@ -56,8 +56,9 @@ public:
 		return m_capabilities;
 	}
 
-	/// Begin frame.
-	void beginFrame();
+	/// Get next presentable image. The returned Texture is valid until the following swapBuffers. After that it might
+	/// dissapear even if you hold the reference.
+	TexturePtr acquireNextPresentableTexture();
 
 	/// Swap buffers
 	void swapBuffers();

+ 71 - 85
src/anki/gr/RenderGraph.cpp

@@ -94,6 +94,8 @@ public:
 	Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS> m_colorUsages = {}; ///< For beginRender pass
 	TextureUsageBit m_dsUsage = TextureUsageBit::NONE; ///< For beginRender pass
 
+	Bool8 m_drawsToPresentable = false;
+
 	/// WARNING: Should be the same as RenderPassDependency::TextureInfo
 	class ConsumedTextureInfo
 	{
@@ -104,8 +106,6 @@ public:
 	};
 	DynamicArray<ConsumedTextureInfo> m_consumedTextures;
 
-	Bool8 m_drawsToDefaultFb = false;
-
 	FramebufferPtr& fb()
 	{
 		return m_secondLevelCmdbInitInfo.m_framebuffer;
@@ -149,11 +149,6 @@ public:
 void FramebufferDescription::bake()
 {
 	ANKI_ASSERT(m_hash == 0 && "Already baked");
-	if(m_defaultFb)
-	{
-		m_hash = 1;
-		return;
-	}
 
 	m_hash = 0;
 	ANKI_ASSERT(m_colorAttachmentCount > 0 || !!m_depthStencilAttachment.m_aspect);
@@ -328,32 +323,34 @@ TexturePtr RenderGraph::getOrCreateRenderTarget(const TextureInitInfo& initInf,
 }
 
 FramebufferPtr RenderGraph::getOrCreateFramebuffer(
-	const FramebufferDescription& fbDescr, const RenderTargetHandle* rtHandles, CString name)
+	const FramebufferDescription& fbDescr, const RenderTargetHandle* rtHandles, CString name, Bool& drawsToPresentable)
 {
 	ANKI_ASSERT(rtHandles);
 	U64 hash = fbDescr.m_hash;
 	ANKI_ASSERT(hash > 0);
 
-	const Bool defaultFb = hash == 1;
+	drawsToPresentable = false;
 
-	if(!defaultFb)
+	// Create a hash that includes the render targets
+	Array<U64, MAX_COLOR_ATTACHMENTS + 1> uuids;
+	U count = 0;
+	for(U i = 0; i < fbDescr.m_colorAttachmentCount; ++i)
 	{
-		// Create a hash that includes the render targets
-		Array<U64, MAX_COLOR_ATTACHMENTS + 1> uuids;
-		U count = 0;
-		for(U i = 0; i < fbDescr.m_colorAttachmentCount; ++i)
-		{
-			uuids[count++] = m_ctx->m_rts[rtHandles[i].m_idx].m_texture->getUuid();
-		}
+		uuids[count++] = m_ctx->m_rts[rtHandles[i].m_idx].m_texture->getUuid();
 
-		if(!!fbDescr.m_depthStencilAttachment.m_aspect)
+		if(!!(m_ctx->m_rts[rtHandles[i].m_idx].m_texture->getTextureUsage() & TextureUsageBit::PRESENT))
 		{
-			uuids[count++] = m_ctx->m_rts[rtHandles[MAX_COLOR_ATTACHMENTS].m_idx].m_texture->getUuid();
+			drawsToPresentable = true;
 		}
+	}
 
-		hash = appendHash(&uuids[0], sizeof(U64) * count, hash);
+	if(!!fbDescr.m_depthStencilAttachment.m_aspect)
+	{
+		uuids[count++] = m_ctx->m_rts[rtHandles[MAX_COLOR_ATTACHMENTS].m_idx].m_texture->getUuid();
 	}
 
+	hash = appendHash(&uuids[0], sizeof(U64) * count, hash);
+
 	FramebufferPtr fb;
 	auto it = m_fbCache.find(hash);
 	if(it != m_fbCache.getEnd())
@@ -364,50 +361,42 @@ FramebufferPtr RenderGraph::getOrCreateFramebuffer(
 	{
 		// Create a complete fb init info
 		FramebufferInitInfo fbInit;
-		if(!defaultFb)
+		fbInit.m_colorAttachmentCount = fbDescr.m_colorAttachmentCount;
+		for(U i = 0; i < fbInit.m_colorAttachmentCount; ++i)
 		{
-			fbInit.m_colorAttachmentCount = fbDescr.m_colorAttachmentCount;
-			for(U i = 0; i < fbInit.m_colorAttachmentCount; ++i)
-			{
-				FramebufferAttachmentInfo& outAtt = fbInit.m_colorAttachments[i];
-				const FramebufferDescriptionAttachment& inAtt = fbDescr.m_colorAttachments[i];
-
-				outAtt.m_clearValue = inAtt.m_clearValue;
-				outAtt.m_loadOperation = inAtt.m_loadOperation;
-				outAtt.m_storeOperation = inAtt.m_storeOperation;
+			FramebufferAttachmentInfo& outAtt = fbInit.m_colorAttachments[i];
+			const FramebufferDescriptionAttachment& inAtt = fbDescr.m_colorAttachments[i];
 
-				// Create texture view
-				TextureViewInitInfo viewInit(
-					m_ctx->m_rts[rtHandles[i].m_idx].m_texture, TextureSubresourceInfo(inAtt.m_surface), "RenderGraph");
-				TextureViewPtr view = getManager().newTextureView(viewInit);
+			outAtt.m_clearValue = inAtt.m_clearValue;
+			outAtt.m_loadOperation = inAtt.m_loadOperation;
+			outAtt.m_storeOperation = inAtt.m_storeOperation;
 
-				outAtt.m_textureView = view;
-			}
+			// Create texture view
+			TextureViewInitInfo viewInit(
+				m_ctx->m_rts[rtHandles[i].m_idx].m_texture, TextureSubresourceInfo(inAtt.m_surface), "RenderGraph");
+			TextureViewPtr view = getManager().newTextureView(viewInit);
 
-			if(!!fbDescr.m_depthStencilAttachment.m_aspect)
-			{
-				FramebufferAttachmentInfo& outAtt = fbInit.m_depthStencilAttachment;
-				const FramebufferDescriptionAttachment& inAtt = fbDescr.m_depthStencilAttachment;
-
-				outAtt.m_clearValue = inAtt.m_clearValue;
-				outAtt.m_loadOperation = inAtt.m_loadOperation;
-				outAtt.m_storeOperation = inAtt.m_storeOperation;
-				outAtt.m_stencilLoadOperation = inAtt.m_stencilLoadOperation;
-				outAtt.m_stencilStoreOperation = inAtt.m_stencilStoreOperation;
-
-				// Create texture view
-				TextureViewInitInfo viewInit(m_ctx->m_rts[rtHandles[MAX_COLOR_ATTACHMENTS].m_idx].m_texture,
-					TextureSubresourceInfo(inAtt.m_surface, inAtt.m_aspect),
-					"RenderGraph");
-				TextureViewPtr view = getManager().newTextureView(viewInit);
-
-				outAtt.m_textureView = view;
-			}
+			outAtt.m_textureView = view;
 		}
-		else
+
+		if(!!fbDescr.m_depthStencilAttachment.m_aspect)
 		{
-			fbInit.m_colorAttachmentCount = 1;
-			fbInit.m_colorAttachments[0].m_loadOperation = fbDescr.m_colorAttachments[0].m_loadOperation;
+			FramebufferAttachmentInfo& outAtt = fbInit.m_depthStencilAttachment;
+			const FramebufferDescriptionAttachment& inAtt = fbDescr.m_depthStencilAttachment;
+
+			outAtt.m_clearValue = inAtt.m_clearValue;
+			outAtt.m_loadOperation = inAtt.m_loadOperation;
+			outAtt.m_storeOperation = inAtt.m_storeOperation;
+			outAtt.m_stencilLoadOperation = inAtt.m_stencilLoadOperation;
+			outAtt.m_stencilStoreOperation = inAtt.m_stencilStoreOperation;
+
+			// Create texture view
+			TextureViewInitInfo viewInit(m_ctx->m_rts[rtHandles[MAX_COLOR_ATTACHMENTS].m_idx].m_texture,
+				TextureSubresourceInfo(inAtt.m_surface, inAtt.m_aspect),
+				"RenderGraph");
+			TextureViewPtr view = getManager().newTextureView(viewInit);
+
+			outAtt.m_textureView = view;
 		}
 
 		// Set FB name
@@ -640,37 +629,34 @@ void RenderGraph::initRenderPassesAndSetDeps(const RenderGraphDescription& descr
 
 			if(graphicsPass.hasFramebuffer())
 			{
-				outPass.fb() =
-					getOrCreateFramebuffer(graphicsPass.m_fbDescr, &graphicsPass.m_rtHandles[0], inPass.m_name.cstr());
-				outPass.m_drawsToDefaultFb = graphicsPass.m_fbDescr.m_defaultFb;
+				Bool drawsToPresentable;
+				outPass.fb() = getOrCreateFramebuffer(
+					graphicsPass.m_fbDescr, &graphicsPass.m_rtHandles[0], inPass.m_name.cstr(), drawsToPresentable);
 
 				outPass.m_fbRenderArea = graphicsPass.m_fbRenderArea;
+				outPass.m_drawsToPresentable = drawsToPresentable;
 
 				// Init the usage bits
-				if(graphicsPass.m_fbDescr.m_hash != 1)
+				TextureUsageBit usage;
+				for(U i = 0; i < graphicsPass.m_fbDescr.m_colorAttachmentCount; ++i)
 				{
-					TextureUsageBit usage;
+					getCrntUsage(graphicsPass.m_rtHandles[i],
+						passIdx,
+						TextureSubresourceInfo(graphicsPass.m_fbDescr.m_colorAttachments[i].m_surface),
+						usage);
 
-					for(U i = 0; i < graphicsPass.m_fbDescr.m_colorAttachmentCount; ++i)
-					{
-						getCrntUsage(graphicsPass.m_rtHandles[i],
-							passIdx,
-							TextureSubresourceInfo(graphicsPass.m_fbDescr.m_colorAttachments[i].m_surface),
-							usage);
-
-						outPass.m_colorUsages[i] = usage;
-					}
+					outPass.m_colorUsages[i] = usage;
+				}
 
-					if(!!graphicsPass.m_fbDescr.m_depthStencilAttachment.m_aspect)
-					{
-						TextureSubresourceInfo subresource =
-							TextureSubresourceInfo(graphicsPass.m_fbDescr.m_depthStencilAttachment.m_surface,
-								graphicsPass.m_fbDescr.m_depthStencilAttachment.m_aspect);
+				if(!!graphicsPass.m_fbDescr.m_depthStencilAttachment.m_aspect)
+				{
+					TextureSubresourceInfo subresource =
+						TextureSubresourceInfo(graphicsPass.m_fbDescr.m_depthStencilAttachment.m_surface,
+							graphicsPass.m_fbDescr.m_depthStencilAttachment.m_aspect);
 
-						getCrntUsage(graphicsPass.m_rtHandles[MAX_COLOR_ATTACHMENTS], passIdx, subresource, usage);
+					getCrntUsage(graphicsPass.m_rtHandles[MAX_COLOR_ATTACHMENTS], passIdx, subresource, usage);
 
-						outPass.m_dsUsage = usage;
-					}
+					outPass.m_dsUsage = usage;
 				}
 
 				// Do some pre-work for the second level command buffers
@@ -717,7 +703,7 @@ void RenderGraph::initBatches()
 	while(passesInBatchCount < passCount)
 	{
 		Batch batch;
-		Bool drawsToDefaultFb = false;
+		Bool drawsToPresentable = false;
 
 		for(U i = 0; i < passCount; ++i)
 		{
@@ -727,15 +713,15 @@ void RenderGraph::initBatches()
 				++passesInBatchCount;
 				batch.m_passIndices.emplaceBack(m_ctx->m_alloc, i);
 
-				// Will batch draw to default FB?
-				drawsToDefaultFb = drawsToDefaultFb || m_ctx->m_passes[i].m_drawsToDefaultFb;
+				// Will batch draw to the swapchain?
+				drawsToPresentable = drawsToPresentable || m_ctx->m_passes[i].m_drawsToPresentable;
 			}
 		}
 
 		// Get or create cmdb for the batch.
-		// Create a new cmdb if the batch is writing to default FB. This will help Vulkan to have a dependency of the
+		// Create a new cmdb if the batch is writing to swapchain. This will help Vulkan to have a dependency of the
 		// swap chain image acquire to the 2nd command buffer instead of adding it to a single big cmdb.
-		if(m_ctx->m_graphicsCmdbs.isEmpty() || drawsToDefaultFb)
+		if(m_ctx->m_graphicsCmdbs.isEmpty() || drawsToPresentable)
 		{
 			CommandBufferInitInfo cmdbInit;
 			cmdbInit.m_flags = CommandBufferFlag::COMPUTE_WORK | CommandBufferFlag::GRAPHICS_WORK;

+ 8 - 16
src/anki/gr/RenderGraph.h

@@ -364,11 +364,6 @@ public:
 	U32 m_colorAttachmentCount = 0;
 	FramebufferDescriptionAttachment m_depthStencilAttachment;
 
-	void setDefaultFramebuffer()
-	{
-		m_defaultFb = true;
-	}
-
 	/// Calculate the hash for the framebuffer.
 	void bake();
 
@@ -378,8 +373,6 @@ public:
 	}
 
 private:
-	Bool8 m_defaultFb = false;
-
 	U64 m_hash = 0;
 };
 
@@ -404,7 +397,7 @@ public:
 		ANKI_ASSERT(fbInfo.isBacked() && "Forgot call GraphicsRenderPassFramebufferInfo::bake");
 		for(U i = 0; i < colorRenderTargetHandles.getSize(); ++i)
 		{
-			if(fbInfo.m_defaultFb || i >= fbInfo.m_colorAttachmentCount)
+			if(i >= fbInfo.m_colorAttachmentCount)
 			{
 				ANKI_ASSERT(!colorRenderTargetHandles[i].isValid());
 			}
@@ -414,7 +407,7 @@ public:
 			}
 		}
 
-		if(fbInfo.m_defaultFb || !fbInfo.m_depthStencilAttachment.m_aspect)
+		if(!fbInfo.m_depthStencilAttachment.m_aspect)
 		{
 			ANKI_ASSERT(!depthStencilRenderTargetHandle.isValid());
 		}
@@ -425,11 +418,8 @@ public:
 #endif
 
 		m_fbDescr = fbInfo;
-		if(!fbInfo.m_defaultFb)
-		{
-			memcpy(&m_rtHandles[0], &colorRenderTargetHandles[0], sizeof(colorRenderTargetHandles));
-			m_rtHandles[MAX_COLOR_ATTACHMENTS] = depthStencilRenderTargetHandle;
-		}
+		memcpy(&m_rtHandles[0], &colorRenderTargetHandles[0], sizeof(colorRenderTargetHandles));
+		m_rtHandles[MAX_COLOR_ATTACHMENTS] = depthStencilRenderTargetHandle;
 		m_fbRenderArea = {{minx, miny, maxx, maxy}};
 	}
 
@@ -675,8 +665,10 @@ private:
 	void setBatchBarriers(const RenderGraphDescription& descr);
 
 	TexturePtr getOrCreateRenderTarget(const TextureInitInfo& initInf, U64 hash);
-	FramebufferPtr getOrCreateFramebuffer(
-		const FramebufferDescription& fbDescr, const RenderTargetHandle* rtHandles, CString name);
+	FramebufferPtr getOrCreateFramebuffer(const FramebufferDescription& fbDescr,
+		const RenderTargetHandle* rtHandles,
+		CString name,
+		Bool& drawsToPresentableTex);
 
 	ANKI_HOT Bool passADependsOnB(const RenderPassDescriptionBase& a, const RenderPassDescriptionBase& b) const;
 

+ 59 - 89
src/anki/gr/gl/FramebufferImpl.cpp

@@ -20,15 +20,6 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 	ANKI_ASSERT(!isCreated());
 	m_in = init;
 
-	if(init.refersToDefaultFramebuffer())
-	{
-		m_fbSize[0] = m_fbSize[1] = MAX_U16;
-		m_bindDefault = true;
-		return Error::NONE;
-	}
-
-	m_bindDefault = false;
-
 	glGenFramebuffers(1, &m_glName);
 	ANKI_ASSERT(m_glName != 0);
 	const GLenum target = GL_FRAMEBUFFER;
@@ -185,106 +176,85 @@ void FramebufferImpl::bind(const GlState& state, U32 minx, U32 miny, U32 width,
 	height = maxy - miny;
 	glScissor(minx, miny, width, height);
 
-	if(m_bindDefault)
-	{
-		glBindFramebuffer(GL_FRAMEBUFFER, 0);
-		const FramebufferAttachmentInfo& att = m_in.m_colorAttachments[0];
+	ANKI_ASSERT(m_glName != 0);
+	glBindFramebuffer(GL_FRAMEBUFFER, m_glName);
 
-		if(att.m_loadOperation == AttachmentLoadOperation::CLEAR)
-		{
-			glClearBufferfv(GL_COLOR, 0, &att.m_clearValue.m_colorf[0]);
-		}
-		else
-		{
-			/* For some reason the driver reports error
-			ANKI_ASSERT(
-				att.m_loadOperation == AttachmentLoadOperation::DONT_CARE);
-			GLenum buff = GL_COLOR_ATTACHMENT0;
-			glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, &buff);*/
-		}
+	// Set the draw buffers
+	if(m_in.m_colorAttachmentCount)
+	{
+		glDrawBuffers(m_in.m_colorAttachmentCount, &m_drawBuffers[0]);
 	}
-	else
+
+	// Invalidate
+	if(m_invalidateBuffersCount)
 	{
-		ANKI_ASSERT(m_glName != 0);
-		glBindFramebuffer(GL_FRAMEBUFFER, m_glName);
+		glInvalidateSubFramebuffer(
+			GL_FRAMEBUFFER, m_invalidateBuffersCount, &m_invalidateBuffers[0], minx, miny, width, height);
+	}
 
-		// Set the draw buffers
-		if(m_in.m_colorAttachmentCount)
-		{
-			glDrawBuffers(m_in.m_colorAttachmentCount, &m_drawBuffers[0]);
-		}
+	// Clear buffers
+	for(U i = 0; i < m_in.m_colorAttachmentCount; i++)
+	{
+		const FramebufferAttachmentInfo& att = m_in.m_colorAttachments[i];
 
-		// Invalidate
-		if(m_invalidateBuffersCount)
+		if(att.m_loadOperation == AttachmentLoadOperation::CLEAR)
 		{
-			glInvalidateSubFramebuffer(
-				GL_FRAMEBUFFER, m_invalidateBuffersCount, &m_invalidateBuffers[0], minx, miny, width, height);
-		}
+			// Enable write mask in case a pipeline changed it (else no clear will happen) and then restore state
+			Bool restore = false;
+			if(state.m_colorWriteMasks[i][0] != true || state.m_colorWriteMasks[i][1] != true
+				|| state.m_colorWriteMasks[i][2] != true || state.m_colorWriteMasks[i][3] != true)
+			{
+				glColorMaski(i, true, true, true, true);
+				restore = true;
+			}
 
-		// Clear buffers
-		for(U i = 0; i < m_in.m_colorAttachmentCount; i++)
-		{
-			const FramebufferAttachmentInfo& att = m_in.m_colorAttachments[i];
+			glClearBufferfv(GL_COLOR, i, &att.m_clearValue.m_colorf[0]);
 
-			if(att.m_loadOperation == AttachmentLoadOperation::CLEAR)
+			if(restore)
 			{
-				// Enable write mask in case a pipeline changed it (else no clear will happen) and then restore state
-				Bool restore = false;
-				if(state.m_colorWriteMasks[i][0] != true || state.m_colorWriteMasks[i][1] != true
-					|| state.m_colorWriteMasks[i][2] != true || state.m_colorWriteMasks[i][3] != true)
-				{
-					glColorMaski(i, true, true, true, true);
-					restore = true;
-				}
-
-				glClearBufferfv(GL_COLOR, i, &att.m_clearValue.m_colorf[0]);
-
-				if(restore)
-				{
-					glColorMaski(i,
-						state.m_colorWriteMasks[i][0],
-						state.m_colorWriteMasks[i][1],
-						state.m_colorWriteMasks[i][2],
-						state.m_colorWriteMasks[i][3]);
-				}
+				glColorMaski(i,
+					state.m_colorWriteMasks[i][0],
+					state.m_colorWriteMasks[i][1],
+					state.m_colorWriteMasks[i][2],
+					state.m_colorWriteMasks[i][3]);
 			}
 		}
+	}
 
-		// Clear depth
-		if(m_clearDepth)
+	// Clear depth
+	if(m_clearDepth)
+	{
+		// Enable write mask in case a pipeline changed it (else no clear will happen) and then restore state
+		if(state.m_depthWriteMask == false)
 		{
-			// Enable write mask in case a pipeline changed it (else no clear will happen) and then restore state
-			if(state.m_depthWriteMask == false)
-			{
-				glDepthMask(true);
-			}
+			glDepthMask(true);
+		}
 
-			glClearBufferfv(GL_DEPTH, 0, &m_in.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth);
+		glClearBufferfv(GL_DEPTH, 0, &m_in.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth);
 
-			if(state.m_depthWriteMask == false)
-			{
-				glDepthMask(false);
-			}
+		if(state.m_depthWriteMask == false)
+		{
+			glDepthMask(false);
 		}
+	}
 
-		// Clear stencil
-		if(m_clearStencil)
+	// Clear stencil
+	if(m_clearStencil)
+	{
+		// Enable write mask in case a pipeline changed it (else no clear will happen) and then restore state
+		// From the spec: The clear operation always uses the front stencil write mask when clearing the stencil
+		// buffer
+		if(state.m_stencilWriteMask[0] != MAX_U32)
 		{
-			// Enable write mask in case a pipeline changed it (else no clear will happen) and then restore state
-			// From the spec: The clear operation always uses the front stencil write mask when clearing the stencil
-			// buffer
-			if(state.m_stencilWriteMask[0] != MAX_U32)
-			{
-				glStencilMaskSeparate(GL_FRONT, MAX_U32);
-			}
+			glStencilMaskSeparate(GL_FRONT, MAX_U32);
+		}
 
-			GLint clearVal = m_in.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_stencil;
-			glClearBufferiv(GL_STENCIL, 0, &clearVal);
+		GLint clearVal = m_in.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_stencil;
+		glClearBufferiv(GL_STENCIL, 0, &clearVal);
 
-			if(state.m_stencilWriteMask[0] != MAX_U32)
-			{
-				glStencilMaskSeparate(GL_FRONT, state.m_stencilWriteMask[0]);
-			}
+		if(state.m_stencilWriteMask[0] != MAX_U32)
+		{
+			glStencilMaskSeparate(GL_FRONT, state.m_stencilWriteMask[0]);
 		}
 	}
 

+ 0 - 1
src/anki/gr/gl/FramebufferImpl.h

@@ -49,7 +49,6 @@ private:
 	Array<GLenum, MAX_COLOR_ATTACHMENTS> m_drawBuffers;
 	Array<GLenum, MAX_COLOR_ATTACHMENTS + 1> m_invalidateBuffers;
 	U8 m_invalidateBuffersCount = 0;
-	Bool8 m_bindDefault = false;
 	Bool8 m_clearDepth = false;
 	Bool8 m_clearStencil = false;
 

+ 2 - 0
src/anki/gr/gl/GlState.h

@@ -8,6 +8,8 @@
 #include <anki/gr/gl/Common.h>
 #include <anki/util/DynamicArray.h>
 #include <anki/gr/ShaderProgram.h>
+#include <anki/gr/Texture.h>
+#include <anki/gr/Framebuffer.h>
 
 namespace anki
 {

+ 3 - 2
src/anki/gr/gl/GrManager.cpp

@@ -66,9 +66,10 @@ void GrManager::deleteInstance(GrManager* gr)
 	alloc.deallocate(gr, 1);
 }
 
-void GrManager::beginFrame()
+TexturePtr GrManager::acquireNextPresentableTexture()
 {
-	// Nothing for GL
+	ANKI_GL_SELF(GrManagerImpl);
+	return self.m_fakeFbTex;
 }
 
 void GrManager::swapBuffers()

+ 31 - 0
src/anki/gr/gl/GrManagerImpl.cpp

@@ -8,12 +8,20 @@
 #include <anki/gr/gl/RenderingThread.h>
 #include <anki/gr/gl/GlState.h>
 #include <anki/core/Config.h>
+#include <anki/core/NativeWindow.h>
 
 namespace anki
 {
 
+GrManagerImpl::GrManagerImpl()
+{
+}
+
 GrManagerImpl::~GrManagerImpl()
 {
+	m_fakeDefaultFb.reset(nullptr);
+	m_fakeFbTex.reset(nullptr);
+
 	if(m_thread)
 	{
 		m_thread->stop();
@@ -61,7 +69,30 @@ Error GrManagerImpl::init(GrManagerInitInfo& init, GrAllocator<U8> alloc)
 	m_capabilities.m_majorApiVersion = U(init.m_config->getNumber("gr.glmajor"));
 	m_capabilities.m_minorApiVersion = U(init.m_config->getNumber("gr.glmajor"));
 
+	initFakeDefaultFb(init);
+
 	return Error::NONE;
 }
 
+void GrManagerImpl::initFakeDefaultFb(GrManagerInitInfo& init)
+{
+	U32 defaultFbWidth = init.m_window->getWidth();
+	U32 defaultFbHeight = init.m_window->getHeight();
+
+	TextureInitInfo texinit("FB Tex");
+	texinit.m_width = defaultFbWidth;
+	texinit.m_height = defaultFbHeight;
+	texinit.m_format = Format::R8G8B8A8_UNORM;
+	texinit.m_usage =
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE | TextureUsageBit::IMAGE_COMPUTE_WRITE | TextureUsageBit::PRESENT;
+	m_fakeFbTex = newTexture(texinit);
+
+	TextureViewPtr view = newTextureView(TextureViewInitInfo(m_fakeFbTex, "FB view"));
+
+	FramebufferInitInfo fbinit("Dflt FB");
+	fbinit.m_colorAttachmentCount = 1;
+	fbinit.m_colorAttachments[0].m_textureView = view;
+	m_fakeDefaultFb = newFramebuffer(fbinit);
+}
+
 } // end namespace anki

+ 7 - 3
src/anki/gr/gl/GrManagerImpl.h

@@ -23,9 +23,11 @@ class GlState;
 class GrManagerImpl final : public GrManager
 {
 public:
-	GrManagerImpl()
-	{
-	}
+	/// Dummy framebuffer texture that emulates functionality vulkan can but GL can't
+	TexturePtr m_fakeFbTex;
+	FramebufferPtr m_fakeDefaultFb;
+
+	GrManagerImpl();
 
 	~GrManagerImpl();
 
@@ -72,6 +74,8 @@ private:
 
 	ANKI_USE_RESULT Error createBackend(GrManagerInitInfo& init);
 	void destroyBackend();
+
+	void initFakeDefaultFb(GrManagerInitInfo& init);
 };
 /// @}
 

+ 10 - 1
src/anki/gr/gl/RenderingThread.cpp

@@ -49,8 +49,17 @@ public:
 	{
 	}
 
-	ANKI_USE_RESULT Error operator()(GlState&)
+	ANKI_USE_RESULT Error operator()(GlState& state)
 	{
+		// Blit from the fake FB to the real default FB
+		const GrManagerImpl& gr = *static_cast<const GrManagerImpl*>(state.m_manager);
+		const FramebufferImpl& fb = static_cast<FramebufferImpl&>(*gr.m_fakeDefaultFb);
+		const U width = gr.m_fakeFbTex->getWidth();
+		const U height = gr.m_fakeFbTex->getHeight();
+		glBlitNamedFramebuffer(
+			fb.getGlName(), 0, 0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+		// Swap buffers
 		m_renderingThread->swapBuffersInternal();
 		return Error::NONE;
 	}

+ 1 - 0
src/anki/gr/gl/TextureImpl.cpp

@@ -124,6 +124,7 @@ void TextureImpl::preInit(const TextureInitInfo& init)
 	m_target = convertTextureType(init.m_type);
 	m_texType = init.m_type;
 	m_format = init.m_format;
+	m_usage = init.m_usage;
 
 	convertTextureInformation(init.m_format, m_compressed, m_glFormat, m_internalFormat, m_glType, m_aspect);
 

+ 19 - 74
src/anki/gr/vulkan/CommandBufferImpl.cpp

@@ -70,7 +70,6 @@ void CommandBufferImpl::beginRecording()
 	if(!!(m_flags & CommandBufferFlag::SECOND_LEVEL))
 	{
 		FramebufferImpl& impl = static_cast<FramebufferImpl&>(*m_activeFb);
-		impl.sync();
 
 		// Calc the layouts
 		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS> colAttLayouts;
@@ -90,19 +89,7 @@ void CommandBufferImpl::beginRecording()
 
 		inheritance.renderPass = impl.getRenderPassHandle(colAttLayouts, dsAttLayout);
 		inheritance.subpass = 0;
-
-		if(!impl.isDefaultFramebuffer())
-		{
-			inheritance.framebuffer = impl.getFramebufferHandle(0);
-		}
-		else
-		{
-			MicroSwapchainPtr swapchain;
-			U32 backbufferIdx;
-			impl.getDefaultFramebufferInfo(swapchain, backbufferIdx);
-
-			inheritance.framebuffer = impl.getFramebufferHandle(backbufferIdx);
-		}
+		inheritance.framebuffer = impl.getFramebufferHandle();
 
 		begin.flags |= VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;
 	}
@@ -125,7 +112,6 @@ void CommandBufferImpl::beginRenderPass(FramebufferPtr fb,
 	m_activeFb = fb;
 
 	FramebufferImpl& fbimpl = static_cast<FramebufferImpl&>(*fb);
-	fbimpl.sync();
 
 	U32 fbWidth, fbHeight;
 	fbimpl.getAttachmentsSize(fbWidth, fbHeight);
@@ -167,54 +153,25 @@ void CommandBufferImpl::beginRenderPassInternal()
 	bi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
 	bi.clearValueCount = impl.getAttachmentCount();
 	bi.pClearValues = impl.getClearValues();
+	bi.framebuffer = impl.getFramebufferHandle();
 
-	if(!impl.isDefaultFramebuffer())
+	// Calc the layouts
+	Array<VkImageLayout, MAX_COLOR_ATTACHMENTS> colAttLayouts;
+	for(U i = 0; i < impl.getColorAttachmentCount(); ++i)
 	{
-		// Bind a non-default FB
-
-		bi.framebuffer = impl.getFramebufferHandle(0);
-
-		// Calc the layouts
-		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS> colAttLayouts;
-		for(U i = 0; i < impl.getColorAttachmentCount(); ++i)
-		{
-			const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*impl.getColorAttachment(i));
-			colAttLayouts[i] =
-				static_cast<const TextureImpl&>(*view.m_tex).computeLayout(m_colorAttachmentUsages[i], 0);
-		}
-
-		VkImageLayout dsAttLayout = VK_IMAGE_LAYOUT_MAX_ENUM;
-		if(impl.hasDepthStencil())
-		{
-			const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*impl.getDepthStencilAttachment());
-			dsAttLayout = static_cast<const TextureImpl&>(*view.m_tex).computeLayout(m_depthStencilAttachmentUsage, 0);
-		}
-
-		bi.renderPass = impl.getRenderPassHandle(colAttLayouts, dsAttLayout);
+		const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*impl.getColorAttachment(i));
+		colAttLayouts[i] = static_cast<const TextureImpl&>(*view.m_tex).computeLayout(m_colorAttachmentUsages[i], 0);
 	}
-	else
-	{
-		// Bind the default FB
-		m_renderedToDefaultFb = true;
-
-		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
-		setImageBarrier(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
-			0,
-			VK_IMAGE_LAYOUT_UNDEFINED,
-			VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
-			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
-			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
-			swapchain->m_images[backbufferIdx],
-			VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1});
+	VkImageLayout dsAttLayout = VK_IMAGE_LAYOUT_MAX_ENUM;
+	if(impl.hasDepthStencil())
+	{
+		const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*impl.getDepthStencilAttachment());
+		dsAttLayout = static_cast<const TextureImpl&>(*view.m_tex).computeLayout(m_depthStencilAttachmentUsage, 0);
 	}
 
+	bi.renderPass = impl.getRenderPassHandle(colAttLayouts, dsAttLayout);
+
 	const Bool flipvp = flipViewport();
 	bi.renderArea.offset.x = m_renderArea[0];
 	if(flipvp)
@@ -227,6 +184,11 @@ void CommandBufferImpl::beginRenderPassInternal()
 
 	getGrManagerImpl().beginMarker(m_handle, impl.getName());
 	ANKI_CMD(vkCmdBeginRenderPass(m_handle, &bi, m_subpassContents), ANY_OTHER_COMMAND);
+
+	if(impl.hasPresentableTexture())
+	{
+		m_renderedToDefaultFb = true;
+	}
 }
 
 void CommandBufferImpl::endRenderPass()
@@ -242,23 +204,6 @@ void CommandBufferImpl::endRenderPass()
 	ANKI_CMD(vkCmdEndRenderPass(m_handle), ANY_OTHER_COMMAND);
 	getGrManagerImpl().endMarker(m_handle);
 
-	// Default FB barrier/transition
-	if(static_cast<const FramebufferImpl&>(*m_activeFb).isDefaultFramebuffer())
-	{
-		MicroSwapchainPtr swapchain;
-		U32 backbufferIdx;
-		static_cast<const FramebufferImpl&>(*m_activeFb).getDefaultFramebufferInfo(swapchain, backbufferIdx);
-
-		setImageBarrier(VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
-			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
-			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
-			VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
-			VK_ACCESS_MEMORY_READ_BIT,
-			VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
-			swapchain->m_images[backbufferIdx],
-			VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1});
-	}
-
 	m_activeFb.reset(nullptr);
 	m_state.endRenderPass();
 

+ 1 - 1
src/anki/gr/vulkan/CommandBufferImpl.inl.h

@@ -740,7 +740,7 @@ inline void CommandBufferImpl::copyBufferToBuffer(
 
 inline Bool CommandBufferImpl::flipViewport() const
 {
-	return static_cast<const FramebufferImpl&>(*m_activeFb).isDefaultFramebuffer()
+	return static_cast<const FramebufferImpl&>(*m_activeFb).hasPresentableTexture()
 		   && !!(getGrManagerImpl().getExtensions() & VulkanExtensions::KHR_MAINENANCE1);
 }
 

+ 100 - 125
src/anki/gr/vulkan/FramebufferImpl.cpp

@@ -13,23 +13,23 @@ namespace anki
 
 FramebufferImpl::~FramebufferImpl()
 {
-	if(m_noDflt.m_fb)
+	if(m_fb)
 	{
-		vkDestroyFramebuffer(getDevice(), m_noDflt.m_fb, nullptr);
+		vkDestroyFramebuffer(getDevice(), m_fb, nullptr);
 	}
 
-	for(auto it : m_noDflt.m_rpasses)
+	for(auto it : m_rpasses)
 	{
 		VkRenderPass rpass = it;
 		ANKI_ASSERT(rpass);
 		vkDestroyRenderPass(getDevice(), rpass, nullptr);
 	}
 
-	m_noDflt.m_rpasses.destroy(getAllocator());
+	m_rpasses.destroy(getAllocator());
 
-	if(m_noDflt.m_compatibleRpass)
+	if(m_compatibleRpass)
 	{
-		vkDestroyRenderPass(getDevice(), m_noDflt.m_compatibleRpass, nullptr);
+		vkDestroyRenderPass(getDevice(), m_compatibleRpass, nullptr);
 	}
 }
 
@@ -38,37 +38,27 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 	ANKI_ASSERT(init.isValid());
 
 	// Init common
-	m_defaultFb = init.refersToDefaultFramebuffer();
-
 	for(U i = 0; i < init.m_colorAttachmentCount; ++i)
 	{
 		m_colorAttachmentMask.set(i);
 		m_colorAttCount = i + 1;
 	}
 
-	if(!m_defaultFb && init.m_depthStencilAttachment.m_textureView)
+	if(init.m_depthStencilAttachment.m_textureView)
 	{
 		m_aspect = init.m_depthStencilAttachment.m_textureView->getSubresource().m_depthStencilAspect;
 	}
 
 	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));
-	}
+	// Create a renderpass.
+	initRpassCreateInfo(init);
+	ANKI_VK_CHECK(vkCreateRenderPass(getDevice(), &m_rpassCi, nullptr, &m_compatibleRpass));
+	getGrManagerImpl().trySetVulkanHandleName(
+		init.getName(), VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, m_compatibleRpass);
+
+	// Create the FB
+	ANKI_CHECK(initFbs(init));
 
 	return Error::NONE;
 }
@@ -113,7 +103,7 @@ Error FramebufferImpl::initFbs(const FramebufferInitInfo& init)
 {
 	VkFramebufferCreateInfo ci = {};
 	ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
-	ci.renderPass = m_noDflt.m_compatibleRpass;
+	ci.renderPass = m_compatibleRpass;
 	ci.attachmentCount = init.m_colorAttachmentCount + ((hasDepthStencil()) ? 1 : 0);
 	ci.layers = 1;
 
@@ -129,13 +119,18 @@ Error FramebufferImpl::initFbs(const FramebufferInitInfo& init)
 
 		imgViews[count++] = view.m_handle;
 
-		if(m_noDflt.m_width == 0)
+		if(m_width == 0)
 		{
-			m_noDflt.m_width = tex.getWidth() >> view.getSubresource().m_firstMipmap;
-			m_noDflt.m_height = tex.getHeight() >> view.getSubresource().m_firstMipmap;
+			m_width = tex.getWidth() >> view.getSubresource().m_firstMipmap;
+			m_height = tex.getHeight() >> view.getSubresource().m_firstMipmap;
 		}
 
-		m_noDflt.m_refs[i] = att.m_textureView;
+		m_refs[i] = att.m_textureView;
+
+		if(!!(tex.getTextureUsage() & TextureUsageBit::PRESENT))
+		{
+			m_presentableTex = true;
+		}
 	}
 
 	if(hasDepthStencil())
@@ -147,24 +142,23 @@ Error FramebufferImpl::initFbs(const FramebufferInitInfo& init)
 
 		imgViews[count++] = view.m_handle;
 
-		if(m_noDflt.m_width == 0)
+		if(m_width == 0)
 		{
-			m_noDflt.m_width = tex.getWidth() >> view.getSubresource().m_firstMipmap;
-			m_noDflt.m_height = tex.getHeight() >> view.getSubresource().m_firstMipmap;
+			m_width = tex.getWidth() >> view.getSubresource().m_firstMipmap;
+			m_height = tex.getHeight() >> view.getSubresource().m_firstMipmap;
 		}
 
-		m_noDflt.m_refs[MAX_COLOR_ATTACHMENTS] = att.m_textureView;
+		m_refs[MAX_COLOR_ATTACHMENTS] = att.m_textureView;
 	}
 
-	ci.width = m_noDflt.m_width;
-	ci.height = m_noDflt.m_height;
+	ci.width = m_width;
+	ci.height = m_height;
 
 	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);
+	ANKI_VK_CHECK(vkCreateFramebuffer(getDevice(), &ci, nullptr, &m_fb));
+	getGrManagerImpl().trySetVulkanHandleName(init.getName(), VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT, m_fb);
 
 	return Error::NONE;
 }
@@ -189,37 +183,36 @@ void FramebufferImpl::initRpassCreateInfo(const FramebufferInitInfo& init)
 	for(U i = 0; i < init.m_colorAttachmentCount; ++i)
 	{
 		setupAttachmentDescriptor(
-			init.m_colorAttachments[i], m_noDflt.m_attachmentDescriptions[i], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+			init.m_colorAttachments[i], m_attachmentDescriptions[i], 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;
+		m_references[i].attachment = i;
+		m_references[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 	}
 
 	if(hasDepthStencil())
 	{
 		setupAttachmentDescriptor(init.m_depthStencilAttachment,
-			m_noDflt.m_attachmentDescriptions[init.m_colorAttachmentCount],
+			m_attachmentDescriptions[init.m_colorAttachmentCount],
 			VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
 
-		VkAttachmentReference& dsReference = m_noDflt.m_references[init.m_colorAttachmentCount];
+		VkAttachmentReference& dsReference = m_references[init.m_colorAttachmentCount];
 		dsReference.attachment = init.m_colorAttachmentCount;
 		dsReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 	}
 
 	// Setup the render pass
-	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);
+	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);
 
 	// Subpass
-	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;
+	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;
 }
 
 VkRenderPass FramebufferImpl::getRenderPassHandle(
@@ -227,94 +220,76 @@ VkRenderPass FramebufferImpl::getRenderPassHandle(
 {
 	VkRenderPass out = {};
 
-	if(!m_defaultFb)
+	// Create hash
+	Array<VkImageLayout, MAX_COLOR_ATTACHMENTS + 1> allLayouts;
+	U allLayoutCount = 0;
+	for(U i = 0; i < m_colorAttCount; ++i)
 	{
-		// Create hash
-		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS + 1> allLayouts;
-		U allLayoutCount = 0;
-		for(U i = 0; i < m_colorAttCount; ++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(hasDepthStencil())
-		{
-			ANKI_ASSERT(dsLayout != VK_IMAGE_LAYOUT_UNDEFINED);
-			allLayouts[allLayoutCount++] = dsLayout;
-		}
+	if(hasDepthStencil())
+	{
+		ANKI_ASSERT(dsLayout != VK_IMAGE_LAYOUT_UNDEFINED);
+		allLayouts[allLayoutCount++] = dsLayout;
+	}
 
-		U64 hash = computeHash(&allLayouts[0], allLayoutCount * sizeof(allLayouts[0]));
+	U64 hash = computeHash(&allLayouts[0], allLayoutCount * sizeof(allLayouts[0]));
 
-		// Get or create
-		LockGuard<Mutex> lock(m_noDflt.m_rpassesMtx);
-		auto it = m_noDflt.m_rpasses.find(hash);
-		if(it != m_noDflt.m_rpasses.getEnd())
-		{
-			out = *it;
-		}
-		else
-		{
-			// Create
+	// Get or create
+	LockGuard<Mutex> lock(m_rpassesMtx);
+	auto it = m_rpasses.find(hash);
+	if(it != m_rpasses.getEnd())
+	{
+		out = *it;
+	}
+	else
+	{
+		// Create
 
-			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;
+		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;
 
-			// Fix pointers
-			subpassDescr.pColorAttachments = &references[0];
-			ci.pAttachments = &attachmentDescriptions[0];
-			ci.pSubpasses = &subpassDescr;
+		// Fix pointers
+		subpassDescr.pColorAttachments = &references[0];
+		ci.pAttachments = &attachmentDescriptions[0];
+		ci.pSubpasses = &subpassDescr;
 
-			for(U i = 0; i < subpassDescr.colorAttachmentCount; ++i)
-			{
-				const VkImageLayout lay = colorLayouts[i];
-				ANKI_ASSERT(lay != VK_IMAGE_LAYOUT_UNDEFINED);
+		for(U i = 0; i < subpassDescr.colorAttachmentCount; ++i)
+		{
+			const VkImageLayout lay = colorLayouts[i];
+			ANKI_ASSERT(lay != VK_IMAGE_LAYOUT_UNDEFINED);
 
-				attachmentDescriptions[i].initialLayout = lay;
-				attachmentDescriptions[i].finalLayout = lay;
+			attachmentDescriptions[i].initialLayout = lay;
+			attachmentDescriptions[i].finalLayout = lay;
 
-				references[i].layout = lay;
-			}
+			references[i].layout = lay;
+		}
 
-			if(hasDepthStencil())
-			{
-				const U i = subpassDescr.colorAttachmentCount;
-				const VkImageLayout lay = dsLayout;
-				ANKI_ASSERT(lay != VK_IMAGE_LAYOUT_UNDEFINED);
+		if(hasDepthStencil())
+		{
+			const U i = subpassDescr.colorAttachmentCount;
+			const VkImageLayout lay = dsLayout;
+			ANKI_ASSERT(lay != VK_IMAGE_LAYOUT_UNDEFINED);
 
-				attachmentDescriptions[i].initialLayout = lay;
-				attachmentDescriptions[i].finalLayout = lay;
+			attachmentDescriptions[i].initialLayout = lay;
+			attachmentDescriptions[i].finalLayout = lay;
 
-				references[subpassDescr.colorAttachmentCount].layout = lay;
-				subpassDescr.pDepthStencilAttachment = &references[subpassDescr.colorAttachmentCount];
-			}
+			references[subpassDescr.colorAttachmentCount].layout = lay;
+			subpassDescr.pDepthStencilAttachment = &references[subpassDescr.colorAttachmentCount];
+		}
 
-			ANKI_VK_CHECKF(vkCreateRenderPass(getDevice(), &ci, nullptr, &out));
-			getGrManagerImpl().trySetVulkanHandleName(getName(), VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, out);
+		ANKI_VK_CHECKF(vkCreateRenderPass(getDevice(), &ci, nullptr, &out));
+		getGrManagerImpl().trySetVulkanHandleName(getName(), VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, out);
 
-			m_noDflt.m_rpasses.emplace(getAllocator(), hash, out);
-		}
-	}
-	else
-	{
-		out = m_dflt.m_swapchain->getRenderPass(m_dflt.m_loadOp);
+		m_rpasses.emplace(getAllocator(), hash, out);
 	}
 
 	ANKI_ASSERT(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

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

@@ -36,35 +36,18 @@ public:
 	/// Good for pipeline creation.
 	VkRenderPass getCompatibleRenderPass() const
 	{
-		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);
-		}
+		ANKI_ASSERT(m_compatibleRpass);
+		return m_compatibleRpass;
 	}
 
-	/// Sync it before you bind it. It's thread-safe
-	void sync();
-
 	/// Use it for binding. It's thread-safe
 	VkRenderPass getRenderPassHandle(
 		const Array<VkImageLayout, MAX_COLOR_ATTACHMENTS>& colorLayouts, VkImageLayout dsLayout);
 
-	VkFramebuffer getFramebufferHandle(U frame) const
+	VkFramebuffer getFramebufferHandle() const
 	{
-		if(!m_defaultFb)
-		{
-			ANKI_ASSERT(m_noDflt.m_fb);
-			return m_noDflt.m_fb;
-		}
-		else
-		{
-			return m_dflt.m_swapchain->m_framebuffers[frame];
-		}
+		ANKI_ASSERT(m_fb);
+		return m_fb;
 	}
 
 	void getAttachmentInfo(BitSet<MAX_COLOR_ATTACHMENTS, U8>& colorAttachments, Bool& depth, Bool& stencil) const
@@ -89,16 +72,16 @@ public:
 		return m_colorAttCount + (hasDepthStencil() ? 1 : 0);
 	}
 
-	TextureViewPtr getColorAttachment(U att) const
+	const TextureViewPtr& getColorAttachment(U att) const
 	{
-		ANKI_ASSERT(m_noDflt.m_refs[att].get());
-		return m_noDflt.m_refs[att];
+		ANKI_ASSERT(m_refs[att].get());
+		return m_refs[att];
 	}
 
-	TextureViewPtr getDepthStencilAttachment() const
+	const TextureViewPtr& getDepthStencilAttachment() const
 	{
-		ANKI_ASSERT(m_noDflt.m_refs[MAX_COLOR_ATTACHMENTS].get());
-		return m_noDflt.m_refs[MAX_COLOR_ATTACHMENTS];
+		ANKI_ASSERT(m_refs[MAX_COLOR_ATTACHMENTS].get());
+		return m_refs[MAX_COLOR_ATTACHMENTS];
 	}
 
 	const VkClearValue* getClearValues() const
@@ -106,71 +89,42 @@ public:
 		return &m_clearVals[0];
 	}
 
-	Bool isDefaultFramebuffer() const
-	{
-		return m_defaultFb;
-	}
-
 	void getAttachmentsSize(U32& width, U32& height) const
 	{
-		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;
-		}
+		ANKI_ASSERT(m_width != 0 && m_height != 0);
+		width = m_width;
+		height = m_height;
 	}
 
-	void getDefaultFramebufferInfo(MicroSwapchainPtr& swapchain, U32& crntBackBufferIdx) const
+	Bool hasPresentableTexture() const
 	{
-		ANKI_ASSERT(m_defaultFb);
-		swapchain = m_dflt.m_swapchain;
-		crntBackBufferIdx = m_dflt.m_currentBackbufferIndex;
+		return m_presentableTex;
 	}
 
 private:
-	Bool8 m_defaultFb = false;
-
 	BitSet<MAX_COLOR_ATTACHMENTS, U8> m_colorAttachmentMask = {false};
 	DepthStencilAspectBit m_aspect = DepthStencilAspectBit::NONE;
 
 	U8 m_colorAttCount = 0;
 	Array<VkClearValue, MAX_COLOR_ATTACHMENTS + 1> m_clearVals;
 
-	class
-	{
-	public:
-		U32 m_width = 0;
-		U32 m_height = 0;
-
-		Array<TextureViewPtr, 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
+	U32 m_width = 0;
+	U32 m_height = 0;
+	Bool8 m_presentableTex = false;
+
+	Array<TextureViewPtr, 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.
+	HashMap<U64, VkRenderPass> m_rpasses;
+	Mutex m_rpassesMtx;
+	VkFramebuffer m_fb = VK_NULL_HANDLE;
 
 	// Methods
 	ANKI_USE_RESULT Error initFbs(const FramebufferInitInfo& init);

+ 2 - 2
src/anki/gr/vulkan/GrManager.cpp

@@ -66,10 +66,10 @@ void GrManager::deleteInstance(GrManager* gr)
 	alloc.deallocate(gr, 1);
 }
 
-void GrManager::beginFrame()
+TexturePtr GrManager::acquireNextPresentableTexture()
 {
 	ANKI_VK_SELF(GrManagerImpl);
-	self.beginFrame();
+	return self.acquireNextPresentableTexture();
 }
 
 void GrManager::swapBuffers()

+ 29 - 6
src/anki/gr/vulkan/GrManagerImpl.cpp

@@ -333,7 +333,7 @@ Error GrManagerImpl::initInstance(const GrManagerInitInfo& init)
 		}
 	}
 
-#if 0 && ANKI_GR_MANAGER_DEBUG_MEMMORY
+#if ANKI_GR_MANAGER_DEBUG_MEMMORY
 	m_debugAllocCbs = {};
 	m_debugAllocCbs.pUserData = this;
 	m_debugAllocCbs.pfnAllocation = allocateCallback;
@@ -674,8 +674,12 @@ void GrManagerImpl::freeCallback(void* userData, void* ptr)
 }
 #endif
 
-void GrManagerImpl::beginFrame()
+TexturePtr GrManagerImpl::acquireNextPresentableTexture()
 {
+	ANKI_TRACE_SCOPED_EVENT(VK_ACQUIRE_IMAGE);
+
+	LockGuard<Mutex> lock(m_globalMtx);
+
 	PerFrame& frame = m_perFrame[m_frame % MAX_FRAMES_IN_FLIGHT];
 
 	// Create sync objects
@@ -684,8 +688,21 @@ void GrManagerImpl::beginFrame()
 
 	// Get new image
 	uint32_t imageIdx;
+
+	VkResult res = vkAcquireNextImageKHR(m_device,
+		m_crntSwapchain->m_swapchain,
+		UINT64_MAX,
+		frame.m_acquireSemaphore->getHandle(),
+		fence->getHandle(),
+		&imageIdx);
+
+	if(res == VK_ERROR_OUT_OF_DATE_KHR)
 	{
-		ANKI_TRACE_SCOPED_EVENT(VK_ACQUIRE_IMAGE);
+		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();
+
+		// Can't fail a second time
 		ANKI_VK_CHECKF(vkAcquireNextImageKHR(m_device,
 			m_crntSwapchain->m_swapchain,
 			UINT64_MAX,
@@ -693,9 +710,14 @@ void GrManagerImpl::beginFrame()
 			fence->getHandle(),
 			&imageIdx));
 	}
+	else
+	{
+		ANKI_VK_CHECKF(res);
+	}
 
 	ANKI_ASSERT(imageIdx < MAX_FRAMES_IN_FLIGHT);
-	m_crntSwapchain->m_currentBackbufferIndex = imageIdx;
+	m_acquiredImageIdx = imageIdx;
+	return m_crntSwapchain->m_textures[imageIdx];
 }
 
 void GrManagerImpl::endFrame()
@@ -729,7 +751,7 @@ void GrManagerImpl::endFrame()
 	present.pWaitSemaphores = (frame.m_renderSemaphore) ? &frame.m_renderSemaphore->getHandle() : nullptr;
 	present.swapchainCount = 1;
 	present.pSwapchains = &m_crntSwapchain->m_swapchain;
-	U32 idx = m_crntSwapchain->m_currentBackbufferIndex;
+	U32 idx = m_acquiredImageIdx;
 	present.pImageIndices = &idx;
 	present.pResults = &res;
 
@@ -785,7 +807,8 @@ void GrManagerImpl::flushCommandBuffer(CommandBufferPtr cmdb, FencePtr* outFence
 	if(impl.renderedToDefaultFramebuffer())
 	{
 		submit.pWaitSemaphores = &frame.m_acquireSemaphore->getHandle();
-		waitFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+		waitFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
+					| VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; // TODO That depends on how we use the swapchain img
 		submit.pWaitDstStageMask = &waitFlags;
 		submit.waitSemaphoreCount = 1;
 

+ 4 - 2
src/anki/gr/vulkan/GrManagerImpl.h

@@ -23,7 +23,8 @@
 namespace anki
 {
 
-#define ANKI_GR_MANAGER_DEBUG_MEMMORY ANKI_EXTRA_CHECKS
+/// @note Disable that because it crashes Intel drivers
+#define ANKI_GR_MANAGER_DEBUG_MEMMORY ANKI_EXTRA_CHECKS && 0
 
 // Forward
 class TextureFallbackUploader;
@@ -54,7 +55,7 @@ public:
 		return m_devProps;
 	}
 
-	void beginFrame();
+	TexturePtr acquireNextPresentableTexture();
 
 	void endFrame();
 
@@ -262,6 +263,7 @@ private:
 
 	VkSurfaceKHR m_surface = VK_NULL_HANDLE;
 	MicroSwapchainPtr m_crntSwapchain;
+	U8 m_acquiredImageIdx = MAX_U8;
 
 	Array<PerFrame, MAX_FRAMES_IN_FLIGHT> m_perFrame;
 	/// @}

+ 1 - 1
src/anki/gr/vulkan/Pipeline.h

@@ -385,7 +385,7 @@ public:
 		m_fbDepth = d;
 		m_fbStencil = s;
 		m_rpass = fbimpl.getCompatibleRenderPass();
-		m_defaultFb = fbimpl.isDefaultFramebuffer();
+		m_defaultFb = fbimpl.hasPresentableTexture();
 		m_fb = fb;
 	}
 

+ 1 - 10
src/anki/gr/vulkan/ShaderImpl.cpp

@@ -57,17 +57,8 @@ Error ShaderImpl::init(const ShaderInitInfo& inf)
 
 #if ANKI_DUMP_SHADERS
 	{
-		static U32 name = 0;
-		SpinLock m_nameLock;
-
-		U32 newName;
-		{
-			LockGuard<SpinLock> lock(m_nameLock);
-			newName = name++;
-		}
-
 		StringAuto fnameSpirv(getAllocator());
-		fnameSpirv.sprintf("%s/%05u.spv", getManager().getCacheDirectory().cstr(), newName);
+		fnameSpirv.sprintf("%s/%05u.spv", getManager().getCacheDirectory().cstr(), getUuid());
 
 		File fileSpirv;
 		ANKI_CHECK(fileSpirv.open(fnameSpirv.toCString(), FileOpenFlag::BINARY | FileOpenFlag::WRITE));

+ 4 - 1
src/anki/gr/vulkan/ShaderProgramImpl.cpp

@@ -49,7 +49,6 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 
 			const ShaderImpl& simpl = *scast<const ShaderImpl*>(m_shaders[stype].get());
 
-			m_refl.m_descriptorSetMask |= simpl.m_descriptorSetMask;
 			m_refl.m_activeBindingMask[set] |= simpl.m_activeBindingMask[set];
 
 			for(U i = 0; i < simpl.m_bindings[set].getSize(); ++i)
@@ -89,6 +88,7 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 			}
 		}
 
+		// We may end up with ppline layouts with "empty" dslayouts. That's fine, we want it.
 		if(counts[set])
 		{
 			descriptorSetCount = set + 1;
@@ -104,6 +104,9 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 
 		ANKI_CHECK(
 			getGrManagerImpl().getDescriptorSetFactory().newDescriptorSetLayout(inf, m_descriptorSetLayouts[set]));
+
+		// Even if the dslayout is empty we will have to list it because we'll have to bind a DS for it.
+		m_refl.m_descriptorSetMask.set(set);
 	}
 
 	// Create the ppline layout

+ 24 - 110
src/anki/gr/vulkan/SwapchainFactory.cpp

@@ -24,38 +24,15 @@ MicroSwapchain::~MicroSwapchain()
 {
 	const VkDevice dev = m_factory->m_gr->getDevice();
 
-	for(VkFramebuffer& fb : m_framebuffers)
+	for(TexturePtr& tex : m_textures)
 	{
-		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 = {};
-		}
+		tex.reset(nullptr);
 	}
 
 	if(m_swapchain)
 	{
 		vkDestroySwapchainKHR(dev, m_swapchain, nullptr);
 		m_swapchain = {};
-		m_images = {};
 	}
 }
 
@@ -65,6 +42,7 @@ Error MicroSwapchain::initInternal()
 
 	// Get the surface size
 	VkSurfaceCapabilitiesKHR surfaceProperties;
+	U surfaceWidth = 0, surfaceHeight = 0;
 	{
 		ANKI_VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
 			m_factory->m_gr->getPhysicalDevice(), m_factory->m_gr->getSurface(), &surfaceProperties));
@@ -74,11 +52,12 @@ Error MicroSwapchain::initInternal()
 			ANKI_VK_LOGE("Wrong surface size");
 			return Error::FUNCTION_FAILED;
 		}
-		m_surfaceWidth = surfaceProperties.currentExtent.width;
-		m_surfaceHeight = surfaceProperties.currentExtent.height;
+		surfaceWidth = surfaceProperties.currentExtent.width;
+		surfaceHeight = surfaceProperties.currentExtent.height;
 	}
 
 	// Get the surface format
+	VkFormat surfaceFormat = VK_FORMAT_END_RANGE;
 	VkColorSpaceKHR colorspace = VK_COLOR_SPACE_MAX_ENUM_KHR;
 	{
 		uint32_t formatCount;
@@ -94,13 +73,13 @@ Error MicroSwapchain::initInternal()
 		{
 			if(formats[formatCount].format == VK_FORMAT_B8G8R8A8_UNORM)
 			{
-				m_surfaceFormat = formats[formatCount].format;
+				surfaceFormat = formats[formatCount].format;
 				colorspace = formats[formatCount].colorSpace;
 				break;
 			}
 		}
 
-		if(m_surfaceFormat == VK_FORMAT_UNDEFINED)
+		if(surfaceFormat == VK_FORMAT_UNDEFINED)
 		{
 			ANKI_VK_LOGE("Surface format not found");
 			return Error::FUNCTION_FAILED;
@@ -152,7 +131,7 @@ Error MicroSwapchain::initInternal()
 		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.imageFormat = surfaceFormat;
 		ci.imageColorSpace = colorspace;
 		ci.imageExtent = surfaceProperties.currentExtent;
 		ci.imageArrayLayers = 1;
@@ -183,95 +162,30 @@ Error MicroSwapchain::initInternal()
 		ANKI_VK_LOGI("Created a swapchain. Image count: %u, present mode: %u, size: %ux%u, vsync: %u",
 			count,
 			presentMode,
-			m_surfaceWidth,
-			m_surfaceHeight,
+			surfaceWidth,
+			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]);
+			TextureInitInfo init("SwapchainImg");
+			init.m_width = surfaceWidth;
+			init.m_height = surfaceHeight;
+			init.m_format = Format::B8G8R8A8_UNORM;
+			ANKI_ASSERT(surfaceFormat == VK_FORMAT_B8G8R8A8_UNORM);
+			init.m_usage = TextureUsageBit::IMAGE_COMPUTE_WRITE | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE
+						   | TextureUsageBit::PRESENT;
+			init.m_type = TextureType::_2D;
+
+			TextureImpl* tex =
+				m_factory->m_gr->getAllocator().newInstance<TextureImpl>(m_factory->m_gr, init.getName());
+			m_textures[i].reset(tex);
+			ANKI_CHECK(tex->initExternal(images[i], init));
 		}
 	}
 
-	// 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;
 }
 

+ 1 - 11
src/anki/gr/vulkan/SwapchainFactory.h

@@ -27,17 +27,7 @@ class MicroSwapchain
 public:
 	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 = {};
-
-	U8 m_currentBackbufferIndex = 0;
+	Array<TexturePtr, MAX_FRAMES_IN_FLIGHT> m_textures;
 
 	MicroSwapchain(SwapchainFactory* factory);
 

+ 24 - 13
src/anki/gr/vulkan/TextureImpl.cpp

@@ -33,7 +33,7 @@ TextureImpl::~TextureImpl()
 
 	m_viewsMap.destroy(getAllocator());
 
-	if(m_imageHandle)
+	if(m_imageHandle && !(m_usage & TextureUsageBit::PRESENT))
 	{
 		vkDestroyImage(getDevice(), m_imageHandle, nullptr);
 	}
@@ -49,10 +49,14 @@ TextureImpl::~TextureImpl()
 	}
 }
 
-Error TextureImpl::init(const TextureInitInfo& init_)
+Error TextureImpl::initInternal(VkImage externalImage, const TextureInitInfo& init_)
 {
 	TextureInitInfo init = init_;
 	ANKI_ASSERT(init.isValid());
+	if(externalImage)
+	{
+		ANKI_ASSERT(!!(init.m_usage & TextureUsageBit::PRESENT));
+	}
 
 	// Set some stuff
 	m_width = init.m_width;
@@ -77,7 +81,14 @@ Error TextureImpl::init(const TextureInitInfo& init_)
 	m_aspect = getImageAspectFromFormat(m_format);
 	m_usage = init.m_usage;
 
-	ANKI_CHECK(initImage(init));
+	if(externalImage)
+	{
+		m_imageHandle = externalImage;
+	}
+	else
+	{
+		ANKI_CHECK(initImage(init));
+	}
 
 	// Init the template
 	memset(&m_viewCreateInfoTemplate, 0, sizeof(m_viewCreateInfoTemplate)); // memset, it will be used for hashing
@@ -253,32 +264,22 @@ Error TextureImpl::initImage(const TextureInitInfo& init_)
 	case TextureType::_2D:
 		ci.extent.depth = 1;
 		ci.arrayLayers = 1;
-
-		m_surfaceOrVolumeCount = m_mipCount;
 		break;
 	case TextureType::_2D_ARRAY:
 		ci.extent.depth = 1;
 		ci.arrayLayers = init.m_layerCount;
-
-		m_surfaceOrVolumeCount = m_mipCount * m_layerCount;
 		break;
 	case TextureType::CUBE:
 		ci.extent.depth = 1;
 		ci.arrayLayers = 6;
-
-		m_surfaceOrVolumeCount = m_mipCount * 6;
 		break;
 	case TextureType::CUBE_ARRAY:
 		ci.extent.depth = 1;
 		ci.arrayLayers = 6 * init.m_layerCount;
-
-		m_surfaceOrVolumeCount = m_mipCount * 6 * m_layerCount;
 		break;
 	case TextureType::_3D:
 		ci.extent.depth = init.m_depth;
 		ci.arrayLayers = 1;
-
-		m_surfaceOrVolumeCount = m_mipCount;
 		break;
 	default:
 		ANKI_ASSERT(0);
@@ -580,6 +581,12 @@ void TextureImpl::computeBarrierInfo(TextureUsageBit before,
 		dstAccesses |= VK_ACCESS_TRANSFER_WRITE_BIT;
 	}
 
+	if(!!(after & TextureUsageBit::PRESENT))
+	{
+		dstStages |= VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+		dstAccesses |= VK_ACCESS_MEMORY_READ_BIT;
+	}
+
 	ANKI_ASSERT(dstStages);
 }
 
@@ -649,6 +656,10 @@ VkImageLayout TextureImpl::computeLayout(TextureUsageBit usage, U level) const
 	{
 		out = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
 	}
+	else if(usage == TextureUsageBit::PRESENT)
+	{
+		out = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+	}
 
 	ANKI_ASSERT(out != VK_IMAGE_LAYOUT_MAX_ENUM);
 	return out;

+ 11 - 3
src/anki/gr/vulkan/TextureImpl.h

@@ -38,8 +38,6 @@ public:
 
 	GpuMemoryHandle m_memHandle;
 
-	U32 m_surfaceOrVolumeCount = 0;
-
 	VkFormat m_vkFormat = VK_FORMAT_UNDEFINED;
 
 	TextureImplWorkaround m_workarounds = TextureImplWorkaround::NONE;
@@ -53,7 +51,15 @@ public:
 
 	~TextureImpl();
 
-	ANKI_USE_RESULT Error init(const TextureInitInfo& init);
+	ANKI_USE_RESULT Error init(const TextureInitInfo& init)
+	{
+		return initInternal(VK_NULL_HANDLE, init);
+	}
+
+	ANKI_USE_RESULT Error initExternal(VkImage image, const TextureInitInfo& init)
+	{
+		return initInternal(image, init);
+	}
 
 	Bool aspectValid(DepthStencilAspectBit aspect) const
 	{
@@ -175,6 +181,8 @@ private:
 		ANKI_ASSERT(isSubresourceValid(subresource));
 		return (textureTypeIsCube(m_texType) && subresource.m_faceCount != 6) ? TextureType::_2D : m_texType;
 	}
+
+	ANKI_USE_RESULT Error initInternal(VkImage externalImage, const TextureInitInfo& init);
 };
 /// @}
 

+ 8 - 52
src/anki/renderer/FinalComposite.cpp

@@ -37,19 +37,7 @@ Error FinalComposite::initInternal(const ConfigSet& config)
 	ANKI_CHECK(loadColorGradingTexture("engine_data/DefaultLut.ankitex"));
 	m_sharpenEnabled = config.getNumber("r.finalComposite.sharpen");
 
-	if(!m_r->getDrawToDefaultFramebuffer())
-	{
-		m_rtDescr =
-			m_r->create2DRenderTargetDescription(m_r->getWidth(), m_r->getHeight(), RT_PIXEL_FORMAT, "Final Composite");
-		m_rtDescr.bake();
-
-		m_fbDescr.m_colorAttachmentCount = 1;
-	}
-	else
-	{
-		m_fbDescr.setDefaultFramebuffer();
-	}
-
+	m_fbDescr.m_colorAttachmentCount = 1;
 	m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
 	m_fbDescr.bake();
 
@@ -103,13 +91,10 @@ Error FinalComposite::loadColorGradingTexture(CString filename)
 void FinalComposite::run(RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
 {
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-
 	const Bool dbgEnabled = m_r->getDbg().getEnabled();
-	const Bool drawToDefaultFb = m_r->getDrawToDefaultFramebuffer();
 
 	// Bind stuff
-	rgraphCtx.bindColorTextureAndSampler(
-		0, 0, m_r->getTemporalAA().getRt(), (drawToDefaultFb) ? m_r->getNearestSampler() : m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getTemporalAA().getRt(), m_r->getLinearSampler());
 
 	rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getBloom().getRt(), m_r->getLinearSampler());
 	cmdb->bindTextureAndSampler(
@@ -126,18 +111,7 @@ void FinalComposite::run(RenderingContext& ctx, RenderPassWorkContext& rgraphCtx
 	Vec4* uniforms = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
 	uniforms->x() = F32(m_r->getFrameCount() % m_blueNoise->getLayerCount());
 
-	U width, height;
-	if(drawToDefaultFb)
-	{
-		width = ctx.m_outFbWidth;
-		height = ctx.m_outFbHeight;
-	}
-	else
-	{
-		width = m_r->getWidth();
-		height = m_r->getHeight();
-	}
-	cmdb->setViewport(0, 0, width, height);
+	cmdb->setViewport(0, 0, ctx.m_outRenderTargetWidth, ctx.m_outRenderTargetHeight);
 
 	cmdb->bindShaderProgram(m_grProgs[dbgEnabled]);
 	drawQuad(cmdb);
@@ -150,39 +124,21 @@ void FinalComposite::populateRenderGraph(RenderingContext& ctx)
 {
 	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 	m_runCtx.m_ctx = &ctx;
-	const Bool drawToDefaultFb = m_r->getDrawToDefaultFramebuffer();
-	const Bool dbgEnabled = m_r->getDbg().getEnabled();
-
-	// Maybe create the RT
-	if(!drawToDefaultFb)
-	{
-		m_runCtx.m_rt = rgraph.newRenderTarget(m_rtDescr);
-	}
 
 	// Create the pass
 	GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("Final Composite");
 
 	pass.setWork(runCallback, this, 0);
+	pass.setFramebufferInfo(m_fbDescr, {{ctx.m_outRenderTarget}}, {});
 
-	if(drawToDefaultFb)
-	{
-		pass.setFramebufferInfo(m_fbDescr, {}, {});
-	}
-	else
-	{
-		pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rt}}, {});
-	}
-
-	if(!drawToDefaultFb)
-	{
-		pass.newConsumer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
-		pass.newProducer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
-	}
+	pass.newConsumer({ctx.m_outRenderTarget, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	pass.newProducer({ctx.m_outRenderTarget, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 
-	if(dbgEnabled)
+	if(m_r->getDbg().getEnabled())
 	{
 		pass.newConsumer({m_r->getDbg().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 	}
+
 	pass.newConsumer({m_r->getTemporalAA().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 	pass.newConsumer({m_r->getBloom().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 }

+ 0 - 7
src/anki/renderer/FinalComposite.h

@@ -32,16 +32,10 @@ anki_internal:
 	/// Populate the rendergraph.
 	void populateRenderGraph(RenderingContext& ctx);
 
-	RenderTargetHandle getRt() const
-	{
-		return m_runCtx.m_rt;
-	}
-
 private:
 	static const U LUT_SIZE = 16;
 
 	FramebufferDescription m_fbDescr;
-	RenderTargetDescription m_rtDescr;
 
 	ShaderProgramResourcePtr m_prog;
 	Array<ShaderProgramPtr, 2> m_grProgs; ///< One with Dbg and one without
@@ -54,7 +48,6 @@ private:
 	class
 	{
 	public:
-		RenderTargetHandle m_rt;
 		RenderingContext* m_ctx = nullptr;
 	} m_runCtx;
 

+ 37 - 9
src/anki/renderer/MainRenderer.cpp

@@ -58,8 +58,7 @@ Error MainRenderer::init(ThreadPool* threadpool,
 	m_rDrawToDefaultFb = m_renderingQuality == 1.0;
 
 	m_r.reset(m_alloc.newInstance<Renderer>());
-	ANKI_CHECK(
-		m_r->init(threadpool, resources, gr, stagingMem, ui, m_alloc, config2, globTimestamp, m_rDrawToDefaultFb));
+	ANKI_CHECK(m_r->init(threadpool, resources, gr, stagingMem, ui, m_alloc, config2, globTimestamp));
 
 	// Init other
 	if(!m_rDrawToDefaultFb)
@@ -69,6 +68,10 @@ Error MainRenderer::init(ThreadPool* threadpool,
 		m_blitProg->getOrCreateVariant(variant);
 		m_blitGrProg = variant->getProgram();
 
+		// The RT desc
+		m_tmpRtDesc = m_r->create2DRenderTargetDescription(m_width, m_height, Format::R8G8B8_UNORM, "Final Composite");
+		m_tmpRtDesc.bake();
+
 		ANKI_R_LOGI("The main renderer will have to blit the offscreen renderer's result");
 	}
 
@@ -79,7 +82,7 @@ Error MainRenderer::init(ThreadPool* threadpool,
 	return Error::NONE;
 }
 
-Error MainRenderer::render(RenderQueue& rqueue)
+Error MainRenderer::render(RenderQueue& rqueue, TexturePtr presentTex)
 {
 	ANKI_TRACE_SCOPED_EVENT(RENDER);
 
@@ -90,11 +93,25 @@ Error MainRenderer::render(RenderQueue& rqueue)
 
 	// Run renderer
 	RenderingContext ctx(m_frameAlloc);
+	m_runCtx.m_ctx = &ctx;
+
+	RenderTargetHandle presentRt = ctx.m_renderGraphDescr.importRenderTarget(presentTex, TextureUsageBit::NONE);
 
 	if(m_rDrawToDefaultFb)
 	{
-		ctx.m_outFbWidth = m_width;
-		ctx.m_outFbHeight = m_height;
+		// m_r will draw to a presentable texture
+
+		ctx.m_outRenderTarget = presentRt;
+		ctx.m_outRenderTargetWidth = presentTex->getWidth();
+		ctx.m_outRenderTargetHeight = presentTex->getHeight();
+	}
+	else
+	{
+		// m_r will draw to a temp tex
+
+		ctx.m_outRenderTarget = ctx.m_renderGraphDescr.newRenderTarget(m_tmpRtDesc);
+		ctx.m_outRenderTargetWidth = m_width;
+		ctx.m_outRenderTargetHeight = m_height;
 	}
 
 	ctx.m_renderQueue = &rqueue;
@@ -107,12 +124,23 @@ Error MainRenderer::render(RenderQueue& rqueue)
 		GraphicsRenderPassDescription& pass = ctx.m_renderGraphDescr.newGraphicsRenderPass("Final Blit");
 
 		FramebufferDescription fbDescr;
-		fbDescr.setDefaultFramebuffer();
+		fbDescr.m_colorAttachmentCount = 1;
+		fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
 		fbDescr.bake();
-		pass.setFramebufferInfo(fbDescr, {{}}, {});
+
+		pass.setFramebufferInfo(fbDescr, {{presentRt}}, {});
 		pass.setWork(runCallback, this, 0);
 
-		pass.newConsumer({m_r->getFinalComposite().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumerAndProducer({presentRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newConsumer({ctx.m_outRenderTarget, TextureUsageBit::SAMPLED_FRAGMENT});
+	}
+
+	// Create a dummy pass to transition the presentable image to present
+	{
+		ComputeRenderPassDescription& pass = ctx.m_renderGraphDescr.newComputeRenderPass("Present");
+
+		pass.setWork(presentCallback, nullptr, 0);
+		pass.newConsumerAndProducer({presentRt, TextureUsageBit::PRESENT});
 	}
 
 	// Bake the render graph
@@ -163,7 +191,7 @@ void MainRenderer::runBlit(RenderPassWorkContext& rgraphCtx)
 	cmdb->setViewport(0, 0, m_width, m_height);
 
 	cmdb->bindShaderProgram(m_blitGrProg);
-	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getFinalComposite().getRt(), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_runCtx.m_ctx->m_outRenderTarget, m_r->getLinearSampler());
 
 	cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3, 1);
 }

+ 15 - 1
src/anki/renderer/MainRenderer.h

@@ -47,7 +47,7 @@ public:
 		const ConfigSet& config,
 		Timestamp* globTimestamp);
 
-	ANKI_USE_RESULT Error render(RenderQueue& rqueue);
+	ANKI_USE_RESULT Error render(RenderQueue& rqueue, TexturePtr presentTex);
 
 	Dbg& getDbg();
 
@@ -84,10 +84,18 @@ private:
 	F32 m_renderingQuality = 1.0;
 
 	RenderGraphPtr m_rgraph;
+	RenderTargetDescription m_tmpRtDesc;
 
 	MainRendererStats m_stats;
 
+	class
+	{
+	public:
+		const RenderingContext* m_ctx = nullptr;
+	} m_runCtx;
+
 	void runBlit(RenderPassWorkContext& rgraphCtx);
+	void present(RenderPassWorkContext& rgraphCtx);
 
 	// A RenderPassWorkCallback for blit pass.
 	static void runCallback(RenderPassWorkContext& rgraphCtx)
@@ -95,6 +103,12 @@ private:
 		MainRenderer* const self = scast<MainRenderer*>(rgraphCtx.m_userData);
 		self->runBlit(rgraphCtx);
 	}
+
+	// A RenderPassWorkCallback for present.
+	static void presentCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		// Do nothing. This pass is dummy
+	}
 };
 /// @}
 

+ 1 - 3
src/anki/renderer/Renderer.cpp

@@ -47,8 +47,7 @@ Error Renderer::init(ThreadPool* threadpool,
 	UiManager* ui,
 	HeapAllocator<U8> alloc,
 	const ConfigSet& config,
-	Timestamp* globTimestamp,
-	Bool willDrawToDefaultFbo)
+	Timestamp* globTimestamp)
 {
 	ANKI_TRACE_SCOPED_EVENT(R_INIT);
 
@@ -59,7 +58,6 @@ Error Renderer::init(ThreadPool* threadpool,
 	m_stagingMem = stagingMem;
 	m_ui = ui;
 	m_alloc = alloc;
-	m_willDrawToDefaultFbo = willDrawToDefaultFbo;
 
 	Error err = initInternal(config);
 	if(err)

+ 6 - 12
src/anki/renderer/Renderer.h

@@ -36,6 +36,11 @@ public:
 
 	RenderGraphDescription m_renderGraphDescr;
 
+	/// The render target that the Renderer will populate.
+	RenderTargetHandle m_outRenderTarget;
+	U32 m_outRenderTargetWidth = 0;
+	U32 m_outRenderTargetHeight = 0;
+
 	// Extra matrices
 	Mat4 m_projMatJitter;
 	Mat4 m_viewProjMatJitter;
@@ -45,9 +50,6 @@ public:
 
 	Vec4 m_unprojParams;
 
-	U32 m_outFbWidth = 0;
-	U32 m_outFbHeight = 0;
-
 	RenderingContext(const StackAllocator<U8>& alloc)
 		: m_tempAllocator(alloc)
 		, m_renderGraphDescr(alloc)
@@ -185,8 +187,7 @@ public:
 		UiManager* ui,
 		HeapAllocator<U8> alloc,
 		const ConfigSet& config,
-		Timestamp* globTimestamp,
-		Bool willDrawToDefaultFbo);
+		Timestamp* globTimestamp);
 
 	/// This function does all the rendering stages and produces a final result.
 	ANKI_USE_RESULT Error populateRenderGraph(RenderingContext& ctx);
@@ -299,11 +300,6 @@ anki_internal:
 		return m_resourcesDirty;
 	}
 
-	Bool getDrawToDefaultFramebuffer() const
-	{
-		return m_willDrawToDefaultFbo;
-	}
-
 	TextureViewPtr getDummyTextureView() const
 	{
 		return m_dummyTexView;
@@ -378,8 +374,6 @@ private:
 	U64 m_prevAsyncTasksCompleted = 0;
 	Bool m_resourcesDirty = true;
 
-	Bool8 m_willDrawToDefaultFbo = false;
-
 	Mat4 m_prevViewProjMat = Mat4::getIdentity();
 	Mat4 m_prevCamTransform = Mat4::getIdentity();
 

+ 76 - 50
tests/gr/Gr.cpp

@@ -376,11 +376,16 @@ static ShaderProgramPtr createProgram(CString vertSrc, CString fragSrc, GrManage
 	return gr.newShaderProgram(ShaderProgramInitInfo(vert, frag));
 }
 
-static FramebufferPtr createDefaultFb(GrManager& gr)
+static FramebufferPtr createColorFb(GrManager& gr, TexturePtr tex)
 {
+	TextureViewInitInfo init;
+	init.m_texture = tex;
+	TextureViewPtr view = gr.newTextureView(init);
+
 	FramebufferInitInfo fbinit;
 	fbinit.m_colorAttachmentCount = 1;
 	fbinit.m_colorAttachments[0].m_clearValue.m_colorf = {{1.0, 0.0, 1.0, 1.0}};
+	fbinit.m_colorAttachments[0].m_textureView = view;
 
 	return gr.newFramebuffer(fbinit);
 }
@@ -405,6 +410,18 @@ static void createCube(GrManager& gr, BufferPtr& verts, BufferPtr& indices)
 	indices->unmap();
 }
 
+static void presentBarrierA(CommandBufferPtr cmdb, TexturePtr presentTex)
+{
+	cmdb->setTextureBarrier(
+		presentTex, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureSubresourceInfo());
+}
+
+static void presentBarrierB(CommandBufferPtr cmdb, TexturePtr presentTex)
+{
+	cmdb->setTextureBarrier(
+		presentTex, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureUsageBit::PRESENT, TextureSubresourceInfo());
+}
+
 ANKI_TEST(Gr, GrManager){COMMON_BEGIN() COMMON_END()}
 
 ANKI_TEST(Gr, Shader)
@@ -430,22 +447,23 @@ ANKI_TEST(Gr, ClearScreen)
 	COMMON_BEGIN()
 	ANKI_TEST_LOGI("Expect to see a magenta background");
 
-	FramebufferPtr fb = createDefaultFb(*gr);
-
 	U iterations = 100;
 	while(iterations--)
 	{
 		HighRezTimer timer;
 		timer.start();
 
-		gr->beginFrame();
+		TexturePtr presentTex = gr->acquireNextPresentableTexture();
+		FramebufferPtr fb = createColorFb(*gr, presentTex);
 
 		CommandBufferInitInfo cinit;
 		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::SMALL_BATCH;
 		CommandBufferPtr cmdb = gr->newCommandBuffer(cinit);
 
-		cmdb->beginRenderPass(fb, {}, {});
+		presentBarrierA(cmdb, presentTex);
+		cmdb->beginRenderPass(fb, {TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE}, {});
 		cmdb->endRenderPass();
+		presentBarrierB(cmdb, presentTex);
 		cmdb->flush();
 
 		gr->swapBuffers();
@@ -467,7 +485,6 @@ ANKI_TEST(Gr, SimpleDrawcall)
 
 	ANKI_TEST_LOGI("Expect to see a grey triangle");
 	ShaderProgramPtr prog = createProgram(VERT_SRC, FRAG_SRC, *gr);
-	FramebufferPtr fb = createDefaultFb(*gr);
 
 	const U ITERATIONS = 200;
 	for(U i = 0; i < ITERATIONS; ++i)
@@ -475,7 +492,8 @@ ANKI_TEST(Gr, SimpleDrawcall)
 		HighRezTimer timer;
 		timer.start();
 
-		gr->beginFrame();
+		TexturePtr presentTex = gr->acquireNextPresentableTexture();
+		FramebufferPtr fb = createColorFb(*gr, presentTex);
 
 		CommandBufferInitInfo cinit;
 		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
@@ -483,9 +501,11 @@ ANKI_TEST(Gr, SimpleDrawcall)
 
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
 		cmdb->bindShaderProgram(prog);
-		cmdb->beginRenderPass(fb, {}, {});
+		presentBarrierA(cmdb, presentTex);
+		cmdb->beginRenderPass(fb, {{TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE}}, {});
 		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
 		cmdb->endRenderPass();
+		presentBarrierB(cmdb, presentTex);
 		cmdb->flush();
 
 		gr->swapBuffers();
@@ -503,6 +523,7 @@ ANKI_TEST(Gr, SimpleDrawcall)
 
 ANKI_TEST(Gr, ViewportAndScissor)
 {
+#if 0
 	COMMON_BEGIN()
 
 	ANKI_TEST_LOGI("Expect to see a grey quad appearing in the 4 corners. The clear color will change and affect only"
@@ -567,6 +588,7 @@ ANKI_TEST(Gr, ViewportAndScissor)
 	}
 
 	COMMON_END()
+#endif
 }
 
 ANKI_TEST(Gr, ViewportAndScissorOffscreen)
@@ -613,8 +635,6 @@ ANKI_TEST(Gr, ViewportAndScissorOffscreen)
 		f = gr->newFramebuffer(fbinit);
 	}
 
-	FramebufferPtr defaultFb = createDefaultFb(*gr);
-
 	SamplerInitInfo samplerInit;
 	samplerInit.m_minMagFilter = SamplingFilter::NEAREST;
 	samplerInit.m_mipmapFilter = SamplingFilter::BASE;
@@ -633,7 +653,8 @@ ANKI_TEST(Gr, ViewportAndScissorOffscreen)
 		HighRezTimer timer;
 		timer.start();
 
-		gr->beginFrame();
+		TexturePtr presentTex = gr->acquireNextPresentableTexture();
+		FramebufferPtr dfb = createColorFb(*gr, presentTex);
 
 		if(i == 0)
 		{
@@ -688,9 +709,11 @@ ANKI_TEST(Gr, ViewportAndScissorOffscreen)
 			TextureUsageBit::SAMPLED_FRAGMENT,
 			TextureSurfaceInfo(0, 0, 0, 0));
 		cmdb->bindTextureAndSampler(0, 0, texView, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
-		cmdb->beginRenderPass(defaultFb, {}, {});
+		presentBarrierA(cmdb, presentTex);
+		cmdb->beginRenderPass(dfb, {TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE}, {});
 		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 6);
 		cmdb->endRenderPass();
+		presentBarrierB(cmdb, presentTex);
 
 		cmdb->flush();
 
@@ -749,16 +772,14 @@ ANKI_TEST(Gr, DrawWithUniforms)
 	// Progm
 	ShaderProgramPtr prog = createProgram(VERT_UBO_SRC, FRAG_UBO_SRC, *gr);
 
-	// FB
-	FramebufferPtr fb = createDefaultFb(*gr);
-
 	const U ITERATION_COUNT = 100;
 	U iterations = ITERATION_COUNT;
 	while(iterations--)
 	{
 		HighRezTimer timer;
 		timer.start();
-		gr->beginFrame();
+		TexturePtr presentTex = gr->acquireNextPresentableTexture();
+		FramebufferPtr fb = createColorFb(*gr, presentTex);
 
 		CommandBufferInitInfo cinit;
 		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
@@ -766,7 +787,8 @@ ANKI_TEST(Gr, DrawWithUniforms)
 
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
 		cmdb->bindShaderProgram(prog);
-		cmdb->beginRenderPass(fb, {}, {});
+		presentBarrierA(cmdb, presentTex);
+		cmdb->beginRenderPass(fb, {TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE}, {});
 
 		cmdb->bindUniformBuffer(0, 0, b, 0, MAX_PTR_SIZE);
 
@@ -780,6 +802,7 @@ ANKI_TEST(Gr, DrawWithUniforms)
 
 		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
 		cmdb->endRenderPass();
+		presentBarrierB(cmdb, presentTex);
 		cmdb->flush();
 
 		gr->swapBuffers();
@@ -833,16 +856,14 @@ ANKI_TEST(Gr, DrawWithVertex)
 	// Prog
 	ShaderProgramPtr prog = createProgram(VERT_INP_SRC, FRAG_INP_SRC, *gr);
 
-	// FB
-	FramebufferPtr fb = createDefaultFb(*gr);
-
 	U iterations = 100;
 	while(iterations--)
 	{
 		HighRezTimer timer;
 		timer.start();
 
-		gr->beginFrame();
+		TexturePtr presentTex = gr->acquireNextPresentableTexture();
+		FramebufferPtr fb = createColorFb(*gr, presentTex);
 
 		CommandBufferInitInfo cinit;
 		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
@@ -857,9 +878,11 @@ ANKI_TEST(Gr, DrawWithVertex)
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
 		cmdb->setPolygonOffset(0.0, 0.0);
 		cmdb->bindShaderProgram(prog);
-		cmdb->beginRenderPass(fb, {}, {});
+		presentBarrierA(cmdb, presentTex);
+		cmdb->beginRenderPass(fb, {TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE}, {});
 		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
 		cmdb->endRenderPass();
+		presentBarrierB(cmdb, presentTex);
 		cmdb->flush();
 
 		gr->swapBuffers();
@@ -1064,11 +1087,6 @@ ANKI_TEST(Gr, DrawWithTexture)
 	//
 	ShaderProgramPtr prog = createProgram(VERT_QUAD_SRC, FRAG_2TEX_SRC, *gr);
 
-	//
-	// Create FB
-	//
-	FramebufferPtr fb = createDefaultFb(*gr);
-
 	//
 	// Draw
 	//
@@ -1079,7 +1097,8 @@ ANKI_TEST(Gr, DrawWithTexture)
 		HighRezTimer timer;
 		timer.start();
 
-		gr->beginFrame();
+		TexturePtr presentTex = gr->acquireNextPresentableTexture();
+		FramebufferPtr fb = createColorFb(*gr, presentTex);
 
 		CommandBufferInitInfo cinit;
 		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::SMALL_BATCH;
@@ -1087,12 +1106,14 @@ ANKI_TEST(Gr, DrawWithTexture)
 
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
 		cmdb->bindShaderProgram(prog);
-		cmdb->beginRenderPass(fb, {}, {});
+		presentBarrierA(cmdb, presentTex);
+		cmdb->beginRenderPass(fb, {TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE}, {});
 
 		cmdb->bindTextureAndSampler(0, 0, aView, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
 		cmdb->bindTextureAndSampler(0, 1, bView, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
 		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 6);
 		cmdb->endRenderPass();
+		presentBarrierB(cmdb, presentTex);
 		cmdb->flush();
 
 		gr->swapBuffers();
@@ -1197,11 +1218,6 @@ static void drawOffscreen(GrManager& gr, Bool useSecondLevel)
 
 	FramebufferPtr fb = gr.newFramebuffer(fbinit);
 
-	//
-	// Create default FB
-	//
-	FramebufferPtr dfb = createDefaultFb(gr);
-
 	//
 	// Create buffs
 	//
@@ -1223,7 +1239,6 @@ static void drawOffscreen(GrManager& gr, Bool useSecondLevel)
 	{
 		HighRezTimer timer;
 		timer.start();
-		gr.beginFrame();
 
 		CommandBufferInitInfo cinit;
 		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
@@ -1277,13 +1292,18 @@ static void drawOffscreen(GrManager& gr, Bool useSecondLevel)
 			TextureSurfaceInfo(0, 0, 0, 0));
 
 		// Draw quad
-		cmdb->beginRenderPass(dfb, {}, {});
+		TexturePtr presentTex = gr.acquireNextPresentableTexture();
+		FramebufferPtr dfb = createColorFb(gr, presentTex);
+
+		presentBarrierA(cmdb, presentTex);
+		cmdb->beginRenderPass(dfb, {TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE}, {});
 		cmdb->bindShaderProgram(resolveProg);
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
 		cmdb->bindTextureAndSampler(0, 0, col0View, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
 		cmdb->bindTextureAndSampler(0, 1, col1View, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
 		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 6);
 		cmdb->endRenderPass();
+		presentBarrierB(cmdb, presentTex);
 
 		cmdb->flush();
 
@@ -1349,9 +1369,6 @@ ANKI_TEST(Gr, ImageLoadStore)
 	sprogInit.m_shaders[ShaderType::COMPUTE] = shader;
 	ShaderProgramPtr compProg = gr->newShaderProgram(sprogInit);
 
-	// FB
-	FramebufferPtr dfb = createDefaultFb(*gr);
-
 	// Write texture data
 	CommandBufferInitInfo cmdbinit;
 	CommandBufferPtr cmdb = gr->newCommandBuffer(cmdbinit);
@@ -1383,7 +1400,6 @@ ANKI_TEST(Gr, ImageLoadStore)
 	{
 		HighRezTimer timer;
 		timer.start();
-		gr->beginFrame();
 
 		CommandBufferInitInfo cinit;
 		cinit.m_flags =
@@ -1408,11 +1424,15 @@ ANKI_TEST(Gr, ImageLoadStore)
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
 
 		cmdb->bindShaderProgram(prog);
-		cmdb->beginRenderPass(dfb, {}, {});
+		TexturePtr presentTex = gr->acquireNextPresentableTexture();
+		FramebufferPtr dfb = createColorFb(*gr, presentTex);
+		presentBarrierA(cmdb, presentTex);
+		cmdb->beginRenderPass(dfb, {TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE}, {});
 		cmdb->bindTextureAndSampler(
 			0, 0, gr->newTextureView(TextureViewInitInfo(tex)), sampler, TextureUsageBit::SAMPLED_FRAGMENT);
 		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 6);
 		cmdb->endRenderPass();
+		presentBarrierB(cmdb, presentTex);
 
 		cmdb->flush();
 
@@ -1527,8 +1547,6 @@ ANKI_TEST(Gr, 3DTextures)
 	//
 	ShaderProgramPtr prog = createProgram(VERT_QUAD_SRC, FRAG_TEX3D_SRC, *gr);
 
-	FramebufferPtr dfb = createDefaultFb(*gr);
-
 	static Array<Vec4, 9> TEX_COORDS_LOD = {{Vec4(0, 0, 0, 0),
 		Vec4(1, 0, 0, 0),
 		Vec4(0, 1, 0, 0),
@@ -1545,14 +1563,16 @@ ANKI_TEST(Gr, 3DTextures)
 	{
 		HighRezTimer timer;
 		timer.start();
-		gr->beginFrame();
 
 		CommandBufferInitInfo cinit;
 		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::SMALL_BATCH;
 		CommandBufferPtr cmdb = gr->newCommandBuffer(cinit);
 
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
-		cmdb->beginRenderPass(dfb, {}, {});
+		TexturePtr presentTex = gr->acquireNextPresentableTexture();
+		FramebufferPtr dfb = createColorFb(*gr, presentTex);
+		presentBarrierA(cmdb, presentTex);
+		cmdb->beginRenderPass(dfb, {TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE}, {});
 
 		cmdb->bindShaderProgram(prog);
 
@@ -1566,6 +1586,7 @@ ANKI_TEST(Gr, 3DTextures)
 		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 6);
 
 		cmdb->endRenderPass();
+		presentBarrierB(cmdb, presentTex);
 
 		cmdb->flush();
 
@@ -2024,7 +2045,6 @@ void main()
 		gr->newBuffer(BufferInitInfo(sizeof(UVec4), BufferUsageBit::STORAGE_COMPUTE_WRITE, BufferMapAccessBit::READ));
 
 	// Draw
-	gr->beginFrame();
 
 	CommandBufferInitInfo cinit;
 	cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
@@ -2033,9 +2053,13 @@ void main()
 	cmdb->setViewport(0, 0, WIDTH, HEIGHT);
 	cmdb->bindShaderProgram(prog);
 	cmdb->bindStorageBuffer(0, 0, resultBuff, 0, resultBuff->getSize());
-	cmdb->beginRenderPass(createDefaultFb(*gr), {}, {});
+	TexturePtr presentTex = gr->acquireNextPresentableTexture();
+	FramebufferPtr dfb = createColorFb(*gr, presentTex);
+	presentBarrierA(cmdb, presentTex);
+	cmdb->beginRenderPass(dfb, {TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE}, {});
 	cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
 	cmdb->endRenderPass();
+	presentBarrierB(cmdb, presentTex);
 	cmdb->flush();
 
 	gr->swapBuffers();
@@ -2128,8 +2152,6 @@ void main()
 		BufferInitInfo(sizeof(UVec4), BufferUsageBit::STORAGE_ALL | BufferUsageBit::FILL, BufferMapAccessBit::READ));
 
 	// Draw
-	gr->beginFrame();
-
 	CommandBufferInitInfo cinit;
 	cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
 	CommandBufferPtr cmdb = gr->newCommandBuffer(cinit);
@@ -2152,9 +2174,13 @@ void main()
 	cmdb->setPushConstants(&pc, sizeof(pc));
 
 	cmdb->bindStorageBuffer(0, 0, resultBuff, 0, resultBuff->getSize());
-	cmdb->beginRenderPass(createDefaultFb(*gr), {}, {});
+	TexturePtr presentTex = gr->acquireNextPresentableTexture();
+	FramebufferPtr dfb = createColorFb(*gr, presentTex);
+	presentBarrierA(cmdb, presentTex);
+	cmdb->beginRenderPass(dfb, {TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE}, {});
 	cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
 	cmdb->endRenderPass();
+	presentBarrierB(cmdb, presentTex);
 	cmdb->flush();
 
 	gr->swapBuffers();

+ 26 - 13
tests/ui/Ui.cpp

@@ -13,15 +13,6 @@
 namespace anki
 {
 
-static FramebufferPtr createDefaultFb(GrManager& gr)
-{
-	FramebufferInitInfo fbinit;
-	fbinit.m_colorAttachmentCount = 1;
-	fbinit.m_colorAttachments[0].m_clearValue.m_colorf = {{1.0, 0.0, 1.0, 1.0}};
-
-	return gr.newFramebuffer(fbinit);
-}
-
 class Label : public UiImmediateModeBuilder
 {
 public:
@@ -87,8 +78,6 @@ ANKI_TEST(Ui, Ui)
 		IntrusivePtr<Label> label;
 		ANKI_TEST_EXPECT_NO_ERR(ui->newInstance(label));
 
-		FramebufferPtr fb = createDefaultFb(*gr);
-
 		Bool done = false;
 		while(!done)
 		{
@@ -106,15 +95,39 @@ ANKI_TEST(Ui, Ui)
 			label->build(canvas);
 			canvas->endBuilding();
 
-			gr->beginFrame();
+			TexturePtr presentTex = gr->acquireNextPresentableTexture();
+			FramebufferPtr fb;
+			{
+				TextureViewInitInfo init;
+				init.m_texture = presentTex;
+				TextureViewPtr view = gr->newTextureView(init);
+
+				FramebufferInitInfo fbinit;
+				fbinit.m_colorAttachmentCount = 1;
+				fbinit.m_colorAttachments[0].m_clearValue.m_colorf = {{1.0, 0.0, 1.0, 1.0}};
+				fbinit.m_colorAttachments[0].m_textureView = view;
+
+				fb = gr->newFramebuffer(fbinit);
+			}
 
 			CommandBufferInitInfo cinit;
 			cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::SMALL_BATCH;
 			CommandBufferPtr cmdb = gr->newCommandBuffer(cinit);
 
-			cmdb->beginRenderPass(fb, {{}}, {});
+			cmdb->setTextureBarrier(presentTex,
+				TextureUsageBit::NONE,
+				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+				TextureSubresourceInfo());
+
+			cmdb->beginRenderPass(fb, {{TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE}}, {});
 			canvas->appendToCommandBuffer(cmdb);
 			cmdb->endRenderPass();
+
+			cmdb->setTextureBarrier(presentTex,
+				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+				TextureUsageBit::PRESENT,
+				TextureSubresourceInfo());
+
 			cmdb->flush();
 
 			gr->swapBuffers();