瀏覽代碼

Vulkan: Drawing to the default FB works

Panagiotis Christopoulos Charitos 9 年之前
父節點
當前提交
6467413387

+ 7 - 1
CMakeLists.txt

@@ -40,6 +40,7 @@ endif()
 option(ANKI_OPTIMIZE "Optimize" ON)
 option(ANKI_DEBUG_SYMBOLS "Debug symbols" OFF)
 option(ANKI_DEBUG "Debugging checks" OFF)
+option(ANKI_LTO "LTO compilation" OFF)
 
 option(ANKI_BUILD_TOOLS "Build tools" OFF)
 option(ANKI_BUILD_TESTS "Build unit tests" OFF)
@@ -150,7 +151,6 @@ endif()
 
 # Add optimization flags
 if(ANKI_OPTIMIZE)
-	# -flto ?
 	set(COMPILER_FLAGS "${COMPILER_FLAGS} -ffast-math -O3 ")
 
 	# Add this because Android compiler complains
@@ -161,6 +161,12 @@ else()
 	set(COMPILER_FLAGS "${COMPILER_FLAGS} -O0 ")
 endif()
 
+# LTO
+if(ANKI_LTO)
+	set(COMPILER_FLAGS "${COMPILER_FLAGS} -flto ")
+	set(LINKER_FLAGS "${LINKER_FLAGS} -flto ")
+endif()
+
 # Valgrind hacks
 if(ANKI_VALGRIND_HAPPY)
 	add_definitions("-DGLIBCXX_FORCE_NEW")

+ 6 - 0
include/anki/gr/CommandBuffer.h

@@ -85,6 +85,12 @@ public:
 	Bool m_secondLevel = false;
 	FramebufferPtr m_framebuffer; ///< For second level command buffers.
 	CommandBufferInitHints m_hints;
+
+	/// Set it to true if this command buffer is the frame's first.
+	Bool m_frameFirstCommandBuffer = false;
+
+	/// Set it to true if this command buffer is the frame's last.
+	Bool m_frameLastCommandBuffer = false;
 };
 
 /// Command buffer.

+ 14 - 1
include/anki/gr/Framebuffer.h

@@ -53,7 +53,8 @@ public:
 	}
 };
 
-/// Framebuffer initializer.
+/// Framebuffer initializer. If you require the default framebuffer then set
+/// m_colorAttachmentCount to 1 and don't set a color texture.
 class FramebufferInitInfo
 {
 public:
@@ -81,6 +82,18 @@ public:
 		m_depthStencilAttachment = b.m_depthStencilAttachment;
 		return *this;
 	}
+
+	Bool refersToDefaultFramebuffer() const
+	{
+		return m_colorAttachmentCount == 1
+			&& !m_colorAttachments[0].m_texture.isCreated();
+	}
+
+	Bool isValid() const
+	{
+		return m_colorAttachmentCount != 0
+			|| m_depthStencilAttachment.m_texture.isCreated();
+	}
 };
 
 /// GPU framebuffer.

+ 3 - 0
include/anki/gr/GrManager.h

@@ -50,6 +50,9 @@ public:
 	/// Create.
 	ANKI_USE_RESULT Error init(GrManagerInitInfo& init);
 
+	/// Begin frame.
+	void beginFrame();
+
 	/// Swap buffers
 	void swapBuffers();
 

+ 9 - 0
include/anki/gr/Pipeline.h

@@ -71,12 +71,19 @@ public:
 	Bool8 m_depthWriteEnabled = true;
 	CompareOperation m_depthCompareFunction = CompareOperation::LESS;
 	PixelFormat m_format;
+
+	Bool isInUse() const
+	{
+		return m_format.m_components != ComponentFormat::NONE;
+	}
 };
 
 class ColorAttachmentStateInfo
 {
 public:
+	/// Ignored if ColorStateInfo::m_drawsToDefaultFramebuffer is true.
 	PixelFormat m_format;
+
 	BlendMethod m_srcBlendMethod = BlendMethod::ONE;
 	BlendMethod m_dstBlendMethod = BlendMethod::ZERO;
 	BlendFunction m_blendFunction = BlendFunction::ADD;
@@ -89,6 +96,8 @@ public:
 	Bool8 m_alphaToCoverageEnabled = false;
 	U8 m_attachmentCount = 0;
 	Array<ColorAttachmentStateInfo, MAX_COLOR_ATTACHMENTS> m_attachments;
+
+	Bool8 m_drawsToDefaultFramebuffer = false;
 };
 
 enum class PipelineSubStateBit : U16

+ 126 - 2
include/anki/gr/vulkan/CommandBufferImpl.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/gr/vulkan/VulkanObject.h>
+#include <anki/util/List.h>
 
 namespace anki
 {
@@ -27,13 +28,136 @@ public:
 
 	ANKI_USE_RESULT Error init(const CommandBufferInitInfo& init);
 
+	VkCommandBuffer getHandle() const
+	{
+		ANKI_ASSERT(m_handle);
+		return m_handle;
+	}
+
+	Bool renderedToDefaultFramebuffer() const
+	{
+		return m_renderedToDefaultFb;
+	}
+
+	Bool isTheFirstFramebufferOfTheFrame() const
+	{
+		return m_frameFirst;
+	}
+
+	Bool isTheLastFramebufferOfTheFrame() const
+	{
+		return m_frameLast;
+	}
+
+	void setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
+	{
+		commandCommon();
+		ANKI_ASSERT(minx < maxx && miny < maxy);
+		VkViewport s;
+		s.x = minx;
+		s.y = miny;
+		s.width = maxx - minx;
+		s.height = maxy - miny;
+		vkCmdSetViewport(m_handle, 0, 1, &s);
+
+		VkRect2D scissor = {};
+		scissor.extent.width = maxx - minx;
+		scissor.extent.height = maxy - miny;
+		scissor.offset.x = minx;
+		scissor.offset.y = miny;
+		vkCmdSetScissor(m_handle, 0, 1, &scissor);
+	}
+
+	void bindPipeline(PipelinePtr ppline);
+
+	void beginRenderPass(FramebufferPtr fb);
+
+	void endRenderPass();
+
+	void drawArrays(U32 count, U32 instanceCount, U32 first, U32 baseInstance)
+	{
+		drawcallCommon();
+		vkCmdDraw(m_handle, count, instanceCount, first, baseInstance);
+	}
+
+	void flush(CommandBuffer* cmdb);
+
 private:
+	StackAllocator<U8> m_alloc;
+
 	VkCommandBuffer m_handle = VK_NULL_HANDLE;
 	Bool8 m_secondLevel = false;
+	Bool8 m_frameFirst = false;
+	Bool8 m_frameLast = false;
+	Bool8 m_renderedToDefaultFb = false;
+	Bool8 m_flushed = false;
+	Bool8 m_empty = true;
 	Thread::Id m_tid = 0;
 
-	/// Some common checks that happen on every command.
-	void commandChecks();
+	Bool m_firstRpassDrawcall = true; ///< First drawcall in a renderpass.
+	FramebufferPtr m_activeFb;
+
+	/// @name cleanup_lists
+	/// @{
+	List<PipelinePtr> m_pplineList;
+	List<FramebufferPtr> m_fbList;
+/// @}
+
+#if ANKI_ASSERTIONS
+	// Debug stuff
+	Bool8 m_insideRenderPass = false;
+	VkSubpassContents m_subpassContents = VK_SUBPASS_CONTENTS_MAX_ENUM;
+#endif
+
+	/// Some common operations per command.
+	void commandCommon();
+
+	void drawcallCommon();
+
+	Bool insideRenderPass() const
+	{
+		return m_activeFb.isCreated();
+	}
+
+	void setImageBarrier(VkPipelineStageFlags srcStage,
+		VkAccessFlags srcAccess,
+		VkImageLayout prevLayout,
+		VkPipelineStageFlags dstStage,
+		VkAccessFlags dstAccess,
+		VkImageLayout newLayout,
+		VkImage img,
+		VkImageAspectFlagBits aspect,
+		TextureType type,
+		const TextureSurfaceInfo& surface)
+	{
+		ANKI_ASSERT(img);
+		VkImageMemoryBarrier inf = {};
+		inf.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+		inf.srcAccessMask = srcAccess;
+		inf.dstAccessMask = dstAccess;
+		inf.oldLayout = prevLayout;
+		inf.newLayout = newLayout;
+		inf.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+		inf.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+		inf.image = img;
+		inf.subresourceRange.aspectMask = aspect;
+		inf.subresourceRange.baseMipLevel = surface.m_level;
+		inf.subresourceRange.levelCount = 1;
+
+		switch(type)
+		{
+		case TextureType::_2D:
+			inf.subresourceRange.baseArrayLayer = 0;
+			inf.subresourceRange.layerCount = 1;
+			break;
+		default:
+			ANKI_ASSERT(0 && "TODO");
+			break;
+		}
+
+		vkCmdPipelineBarrier(
+			m_handle, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &inf);
+	}
 };
 /// @}
 

+ 36 - 4
include/anki/gr/vulkan/FramebufferImpl.h

@@ -20,10 +20,6 @@ class Attachment;
 class FramebufferImpl : public VulkanObject
 {
 public:
-	VkFramebuffer m_framebuffer = VK_NULL_HANDLE;
-	VkRenderPass m_renderPass = VK_NULL_HANDLE;
-	Bool8 m_defaultFramebuffer = false;
-
 	FramebufferImpl(GrManager* manager)
 		: VulkanObject(manager)
 	{
@@ -33,7 +29,43 @@ public:
 
 	ANKI_USE_RESULT Error init(const FramebufferInitInfo& init);
 
+	VkFramebuffer getFramebufferHandle(U idx) const
+	{
+		ANKI_ASSERT(m_framebuffers[idx]);
+		return m_framebuffers[idx];
+	}
+
+	VkRenderPass getRenderPassHandle() const
+	{
+		ANKI_ASSERT(m_renderPass);
+		return m_renderPass;
+	}
+
+	Bool isDefaultFramebuffer() const
+	{
+		return m_defaultFramebuffer;
+	}
+
+	U getAttachmentCount() const
+	{
+		ANKI_ASSERT(m_attachmentCount > 0);
+		return m_attachmentCount;
+	}
+
+	const VkClearValue* getClearValues() const
+	{
+		return &m_clearVals[0];
+	}
+
 private:
+	Array<VkFramebuffer, MAX_FRAMES_IN_FLIGHT> m_framebuffers = {{
+		0,
+	}};
+	VkRenderPass m_renderPass = VK_NULL_HANDLE;
+	Array<VkClearValue, MAX_COLOR_ATTACHMENTS + 1> m_clearVals;
+	Bool8 m_defaultFramebuffer = false;
+	U8 m_attachmentCount = 0;
+
 	ANKI_USE_RESULT Error initRenderPass(const FramebufferInitInfo& init);
 
 	void setupAttachmentDescriptor(

+ 48 - 6
include/anki/gr/vulkan/GrManagerImpl.h

@@ -56,6 +56,42 @@ public:
 	void deleteCommandBuffer(
 		VkCommandBuffer cmdb, Bool secondLevel, Thread::Id tid);
 
+	VkFormat getDefaultSurfaceFormat() const
+	{
+		return m_surfaceFormat;
+	}
+
+	VkImageView getDefaultSurfaceImageView(U idx) const
+	{
+		ANKI_ASSERT(m_perFrame[idx].m_imageView);
+		return m_perFrame[idx].m_imageView;
+	}
+
+	VkImage getDefaultSurfaceImage(U idx) const
+	{
+		ANKI_ASSERT(m_perFrame[idx].m_image);
+		return m_perFrame[idx].m_image;
+	}
+
+	U getDefaultSurfaceWidth() const
+	{
+		ANKI_ASSERT(m_surfaceWidth);
+		return m_surfaceWidth;
+	}
+
+	U getDefaultSurfaceHeight() const
+	{
+		ANKI_ASSERT(m_surfaceHeight);
+		return m_surfaceHeight;
+	}
+
+	U64 getFrame() const
+	{
+		return m_frame;
+	}
+
+	void flushCommandBuffer(CommandBufferImpl& impl, CommandBufferPtr ptr);
+
 private:
 	GrManager* m_manager = nullptr;
 
@@ -66,6 +102,7 @@ private:
 	VkDevice m_device = VK_NULL_HANDLE;
 	U32 m_queueIdx = MAX_U32;
 	VkQueue m_queue = VK_NULL_HANDLE;
+	Mutex m_queueSubmitMtx;
 
 	/// @name Surface_related
 	/// @{
@@ -74,21 +111,21 @@ private:
 	public:
 		VkImage m_image = VK_NULL_HANDLE;
 		VkImageView m_imageView = VK_NULL_HANDLE;
-		VkFramebuffer m_fb = VK_NULL_HANDLE;
 
 		VkFence m_presentFence = VK_NULL_HANDLE;
 		VkSemaphore m_acquireSemaphore = VK_NULL_HANDLE;
 
-		/// The semaphores that of those submits that render to the default FB.
-		DynamicArray<VkSemaphore> m_renderSemaphores;
-		U32 m_renderSemaphoresCount = 0;
+		/// The semaphore that the submit that renders to the default FB.
+		VkSemaphore m_renderSemaphore = VK_NULL_HANDLE;
+
+		/// Keep it here for deferred cleanup.
+		List<CommandBufferPtr> m_cmdbsSubmitted;
 	};
 
 	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;
-	VkRenderPass m_renderPass = VK_NULL_HANDLE;
 	Array<PerFrame, MAX_FRAMES_IN_FLIGHT> m_perFrame;
 	/// @}
 
@@ -138,7 +175,12 @@ private:
 	HashMap<Thread::Id, PerThread, PerThreadHasher, PerThreadCompare>
 		m_perThread;
 	SpinLock m_perThreadMtx;
-	/// @}
+/// @}
+
+#if ANKI_ASSERTIONS
+	Bool8 m_cmdbWithIndicationThatIsFirstSubmitted = false;
+	Bool8 m_cmdbWithIndicationThatIsLastSubmitted = false;
+#endif
 
 	ANKI_USE_RESULT Error initInternal(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initInstance(const GrManagerInitInfo& init);

+ 15 - 2
include/anki/gr/vulkan/PipelineImpl.h

@@ -26,8 +26,6 @@ class ColorStateInfo;
 class PipelineImpl : public VulkanObject
 {
 public:
-	VkPipeline m_handle = VK_NULL_HANDLE;
-
 	PipelineImpl(GrManager* manager)
 		: VulkanObject(manager)
 	{
@@ -37,7 +35,22 @@ public:
 
 	ANKI_USE_RESULT Error init(const PipelineInitInfo& init);
 
+	VkPipeline getHandle() const
+	{
+		ANKI_ASSERT(m_handle);
+		return m_handle;
+	}
+
+	VkPipelineBindPoint getBindPoint() const
+	{
+		ANKI_ASSERT(m_bindPoint != VK_PIPELINE_BIND_POINT_MAX_ENUM);
+		return m_bindPoint;
+	}
+
 private:
+	VkPipeline m_handle = VK_NULL_HANDLE;
+	VkPipelineBindPoint m_bindPoint = VK_PIPELINE_BIND_POINT_MAX_ENUM;
+
 	ANKI_USE_RESULT Error initGraphics(const PipelineInitInfo& init);
 
 	ANKI_USE_RESULT Error initCompute(const PipelineInitInfo& init);

+ 16 - 2
src/gr/gl/FramebufferImpl.cpp

@@ -21,8 +21,8 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 	ANKI_ASSERT(!isCreated());
 	m_in = init;
 
-	if(m_in.m_colorAttachmentCount == 0
-		&& !m_in.m_depthStencilAttachment.m_texture.isCreated())
+	if(m_in.m_colorAttachmentCount == 1
+		&& !m_in.m_colorAttachments[0].m_texture.isCreated())
 	{
 		m_bindDefault = true;
 		return ErrorCode::NONE;
@@ -150,6 +150,20 @@ void FramebufferImpl::bind(const GlState& state)
 	if(m_bindDefault)
 	{
 		glBindFramebuffer(GL_FRAMEBUFFER, 0);
+		const Attachment& att = m_in.m_colorAttachments[0];
+
+		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);*/
+		}
 	}
 	else
 	{

+ 6 - 0
src/gr/gl/GrManager.cpp

@@ -42,6 +42,12 @@ Error GrManager::init(GrManagerInitInfo& init)
 	return ErrorCode::NONE;
 }
 
+//==============================================================================
+void GrManager::beginFrame()
+{
+	// Nothing for GL
+}
+
 //==============================================================================
 void GrManager::swapBuffers()
 {

+ 9 - 1
src/gr/vulkan/CommandBuffer.cpp

@@ -6,6 +6,8 @@
 #include <anki/gr/CommandBuffer.h>
 #include <anki/gr/vulkan/CommandBufferImpl.h>
 
+#include <anki/gr/Pipeline.h>
+
 namespace anki
 {
 
@@ -24,7 +26,7 @@ CommandBuffer::~CommandBuffer()
 void CommandBuffer::init(CommandBufferInitInfo& inf)
 {
 	m_impl.reset(getAllocator().newInstance<CommandBufferImpl>(&getManager()));
-	
+
 	if(m_impl->init(inf))
 	{
 		ANKI_LOGF("Cannot recover");
@@ -42,6 +44,7 @@ CommandBufferInitHints CommandBuffer::computeInitHints() const
 //==============================================================================
 void CommandBuffer::flush()
 {
+	m_impl->flush(this);
 }
 
 //==============================================================================
@@ -52,6 +55,7 @@ void CommandBuffer::finish()
 //==============================================================================
 void CommandBuffer::setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
 {
+	m_impl->setViewport(minx, miny, maxx, maxy);
 }
 
 //==============================================================================
@@ -62,16 +66,19 @@ void CommandBuffer::setPolygonOffset(F32 offset, F32 units)
 //==============================================================================
 void CommandBuffer::bindPipeline(PipelinePtr ppline)
 {
+	m_impl->bindPipeline(ppline);
 }
 
 //==============================================================================
 void CommandBuffer::beginRenderPass(FramebufferPtr fb)
 {
+	m_impl->beginRenderPass(fb);
 }
 
 //==============================================================================
 void CommandBuffer::endRenderPass()
 {
+	m_impl->endRenderPass();
 }
 
 //==============================================================================
@@ -93,6 +100,7 @@ void CommandBuffer::drawElements(U32 count,
 void CommandBuffer::drawArrays(
 	U32 count, U32 instanceCount, U32 first, U32 baseInstance)
 {
+	m_impl->drawArrays(count, instanceCount, first, baseInstance);
 }
 
 //==============================================================================

+ 181 - 1
src/gr/vulkan/CommandBufferImpl.cpp

@@ -7,6 +7,11 @@
 #include <anki/gr/CommandBuffer.h>
 #include <anki/gr/vulkan/GrManagerImpl.h>
 
+#include <anki/gr/Pipeline.h>
+#include <anki/gr/vulkan/PipelineImpl.h>
+#include <anki/gr/Framebuffer.h>
+#include <anki/gr/vulkan/FramebufferImpl.h>
+
 namespace anki
 {
 
@@ -19,30 +24,205 @@ CommandBufferImpl::CommandBufferImpl(GrManager* manager)
 //==============================================================================
 CommandBufferImpl::~CommandBufferImpl()
 {
+	if(m_empty)
+	{
+		ANKI_LOGW("Command buffer was empty");
+	}
+
+	if(!m_flushed)
+	{
+		ANKI_LOGW("Command buffer was not flushed");
+	}
+
 	if(m_handle)
 	{
 		getGrManagerImpl().deleteCommandBuffer(m_handle, m_secondLevel, m_tid);
 	}
+
+	m_pplineList.destroy(m_alloc);
+	m_fbList.destroy(m_alloc);
 }
 
 //==============================================================================
 Error CommandBufferImpl::init(const CommandBufferInitInfo& init)
 {
+	auto& pool = getGrManagerImpl().getAllocator().getMemoryPool();
+	m_alloc = StackAllocator<U8>(pool.getAllocationCallback(),
+		pool.getAllocationCallbackUserData(),
+		init.m_hints.m_chunkSize,
+		1.0,
+		0,
+		false);
+
 	m_secondLevel = init.m_secondLevel;
+	m_frameLast = init.m_frameLastCommandBuffer;
+	m_frameFirst = init.m_frameFirstCommandBuffer;
 	m_tid = Thread::getCurrentThreadId();
 
 	m_handle = getGrManagerImpl().newCommandBuffer(m_tid, m_secondLevel);
 	ANKI_ASSERT(m_handle);
 
+	// Begin recording
+	VkCommandBufferInheritanceInfo inheritance = {};
+	inheritance.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
+
+	if(init.m_secondLevel)
+	{
+		ANKI_ASSERT(0 && "TODO");
+	}
+
+	VkCommandBufferBeginInfo begin = {};
+	begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+	begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+	begin.pInheritanceInfo = &inheritance;
+
+	vkBeginCommandBuffer(m_handle, &begin);
+
+	// If it's the frame's first command buffer then do the default fb image
+	// transition
+	if(init.m_frameFirstCommandBuffer)
+	{
+		// Default FB barrier/transition
+		setImageBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+			VK_ACCESS_MEMORY_READ_BIT,
+			VK_IMAGE_LAYOUT_UNDEFINED,
+			VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+			getGrManagerImpl().getDefaultSurfaceImage(
+				getGrManagerImpl().getFrame() % MAX_FRAMES_IN_FLIGHT),
+			VK_IMAGE_ASPECT_COLOR_BIT,
+			TextureType::_2D,
+			TextureSurfaceInfo(0, 0, 0));
+	}
+
 	return ErrorCode::NONE;
 }
 
 //==============================================================================
-void CommandBufferImpl::commandChecks()
+void CommandBufferImpl::commandCommon()
 {
 	ANKI_ASSERT(Thread::getCurrentThreadId() == m_tid
 		&& "Commands must be recorder by the thread this command buffer was "
 		   "created");
+
+	ANKI_ASSERT(!m_flushed);
+	ANKI_ASSERT(m_handle);
+}
+
+//==============================================================================
+void CommandBufferImpl::bindPipeline(PipelinePtr ppline)
+{
+	commandCommon();
+	vkCmdBindPipeline(m_handle,
+		ppline->getImplementation().getBindPoint(),
+		ppline->getImplementation().getHandle());
+
+	m_pplineList.pushBack(m_alloc, ppline);
+}
+
+//==============================================================================
+void CommandBufferImpl::beginRenderPass(FramebufferPtr fb)
+{
+	commandCommon();
+	ANKI_ASSERT(!insideRenderPass());
+	m_empty = false;
+
+	m_firstRpassDrawcall = true;
+	m_activeFb = fb;
+
+	m_fbList.pushBack(m_alloc, fb);
+
+#if ANKI_ASSERTIONS
+	m_subpassContents = VK_SUBPASS_CONTENTS_MAX_ENUM;
+#endif
+}
+
+//==============================================================================
+void CommandBufferImpl::endRenderPass()
+{
+	commandCommon();
+	ANKI_ASSERT(insideRenderPass());
+
+	vkCmdEndRenderPass(m_handle);
+	m_activeFb.reset(nullptr);
+}
+
+//==============================================================================
+void CommandBufferImpl::drawcallCommon()
+{
+	// Preconditions
+	commandCommon();
+	ANKI_ASSERT(insideRenderPass());
+	ANKI_ASSERT(m_subpassContents == VK_SUBPASS_CONTENTS_MAX_ENUM
+		|| m_subpassContents == VK_SUBPASS_CONTENTS_INLINE);
+#if ANKI_ASSERTIONS
+	m_subpassContents = VK_SUBPASS_CONTENTS_INLINE;
+#endif
+
+	if(ANKI_UNLIKELY(m_firstRpassDrawcall))
+	{
+		m_firstRpassDrawcall = false;
+
+		// Bind the framebuffer
+		VkRenderPassBeginInfo bi = {};
+		bi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+		FramebufferImpl& impl = m_activeFb->getImplementation();
+		bi.renderPass = impl.getRenderPassHandle();
+		bi.clearValueCount = impl.getAttachmentCount();
+		bi.pClearValues = impl.getClearValues();
+
+		if(!impl.isDefaultFramebuffer())
+		{
+			bi.framebuffer = impl.getFramebufferHandle(0);
+
+			ANKI_ASSERT(0);
+			// TODO Get the render arrea from one of the attachments
+		}
+		else
+		{
+			// Bind the default FB
+			m_renderedToDefaultFb = true;
+
+			bi.framebuffer = impl.getFramebufferHandle(
+				getGrManagerImpl().getFrame() % MAX_FRAMES_IN_FLIGHT);
+
+			bi.renderArea.extent.width =
+				getGrManagerImpl().getDefaultSurfaceWidth();
+			bi.renderArea.extent.height =
+				getGrManagerImpl().getDefaultSurfaceHeight();
+		}
+
+		vkCmdBeginRenderPass(m_handle, &bi, VK_SUBPASS_CONTENTS_INLINE);
+	}
+}
+
+//==============================================================================
+void CommandBufferImpl::flush(CommandBuffer* cmdb)
+{
+	ANKI_ASSERT(cmdb);
+	ANKI_ASSERT(!m_flushed);
+	ANKI_ASSERT(!m_empty);
+
+	if(m_frameLast)
+	{
+		// Default FB barrier/transition
+		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,
+			getGrManagerImpl().getDefaultSurfaceImage(
+				getGrManagerImpl().getFrame() % MAX_FRAMES_IN_FLIGHT),
+			VK_IMAGE_ASPECT_COLOR_BIT,
+			TextureType::_2D,
+			TextureSurfaceInfo(0, 0, 0));
+	}
+
+	ANKI_VK_CHECKF(vkEndCommandBuffer(m_handle));
+	getGrManagerImpl().flushCommandBuffer(*this, CommandBufferPtr(cmdb));
+	m_flushed = true;
 }
 
 } // end namespace anki

+ 75 - 37
src/gr/vulkan/FramebufferImpl.cpp

@@ -5,6 +5,7 @@
 
 #include <anki/gr/vulkan/FramebufferImpl.h>
 #include <anki/gr/Framebuffer.h>
+#include <anki/gr/vulkan/GrManagerImpl.h>
 
 namespace anki
 {
@@ -17,25 +18,60 @@ FramebufferImpl::~FramebufferImpl()
 		vkDestroyRenderPass(getDevice(), m_renderPass, nullptr);
 	}
 
-	if(m_framebuffer)
+	for(VkFramebuffer fb : m_framebuffers)
 	{
-		vkDestroyFramebuffer(getDevice(), m_framebuffer, nullptr);
+		if(fb)
+		{
+			vkDestroyFramebuffer(getDevice(), fb, nullptr);
+		}
 	}
 }
 
 //==============================================================================
 Error FramebufferImpl::init(const FramebufferInitInfo& init)
 {
-	if(init.m_colorAttachmentCount == 0
-		&& !init.m_depthStencilAttachment.m_texture.isCreated())
+	ANKI_ASSERT(init.isValid());
+	m_defaultFramebuffer = init.refersToDefaultFramebuffer();
+
+	ANKI_CHECK(initRenderPass(init));
+	ANKI_CHECK(initFramebuffer(init));
+
+	// Set clear values
+	m_attachmentCount = 0;
+	for(U i = 0; i < init.m_colorAttachmentCount; ++i)
 	{
-		m_defaultFramebuffer = true;
+		if(init.m_colorAttachments[i].m_loadOperation
+			== AttachmentLoadOperation::CLEAR)
+		{
+			F32* col = &m_clearVals[i].color.float32[0];
+			col[0] = init.m_colorAttachments[i].m_clearValue.m_colorf[0];
+			col[1] = init.m_colorAttachments[i].m_clearValue.m_colorf[1];
+			col[2] = init.m_colorAttachments[i].m_clearValue.m_colorf[2];
+			col[3] = init.m_colorAttachments[i].m_clearValue.m_colorf[3];
+		}
+		else
+		{
+			m_clearVals[i] = {};
+		}
+
+		++m_attachmentCount;
 	}
-	else
+
+	if(init.m_depthStencilAttachment.m_texture.isCreated())
 	{
-		m_defaultFramebuffer = false;
-		ANKI_CHECK(initRenderPass(init));
-		ANKI_CHECK(initFramebuffer(init));
+		if(init.m_depthStencilAttachment.m_loadOperation
+			== AttachmentLoadOperation::CLEAR)
+		{
+			m_clearVals[m_attachmentCount].depthStencil.depth =
+				init.m_depthStencilAttachment.m_clearValue.m_depthStencil
+					.m_depth;
+		}
+		else
+		{
+			m_clearVals[m_attachmentCount] = {};
+		}
+
+		++m_attachmentCount;
 	}
 
 	return ErrorCode::NONE;
@@ -45,24 +81,21 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 void FramebufferImpl::setupAttachmentDescriptor(
 	const Attachment& att, VkAttachmentDescription& desc, Bool depthStencil)
 {
-	VkImageLayout initLayout = (depthStencil)
+	VkImageLayout layout = (depthStencil)
 		? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
 		: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 
-	ANKI_VK_MEMSET_DBG(desc);
-	desc.flags = 0;
-	desc.format = convertFormat(att.m_format);
+	desc = {};
+	desc.format = (m_defaultFramebuffer)
+		? getGrManagerImpl().getDefaultSurfaceFormat()
+		: convertFormat(att.m_format);
 	desc.samples = VK_SAMPLE_COUNT_1_BIT;
 	desc.loadOp = convertLoadOp(att.m_loadOperation);
 	desc.storeOp = convertStoreOp(att.m_storeOperation);
 	desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
 	desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
-	desc.initialLayout = (att.m_loadOperation == AttachmentLoadOperation::LOAD)
-		? initLayout
-		: VK_IMAGE_LAYOUT_UNDEFINED;
-	desc.finalLayout = (att.m_storeOperation == AttachmentStoreOperation::STORE)
-		? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
-		: VK_IMAGE_LAYOUT_UNDEFINED;
+	desc.initialLayout = layout;
+	desc.finalLayout = layout;
 }
 
 //==============================================================================
@@ -90,7 +123,8 @@ Error FramebufferImpl::initRenderPass(const FramebufferInitInfo& init)
 		++ci.attachmentCount;
 	}
 
-	VkAttachmentReference dsReference = {0, VK_IMAGE_LAYOUT_UNDEFINED};
+	VkAttachmentReference dsReference = {
+		0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL};
 	if(hasDepthStencil)
 	{
 		setupAttachmentDescriptor(init.m_depthStencilAttachment,
@@ -107,24 +141,15 @@ Error FramebufferImpl::initRenderPass(const FramebufferInitInfo& init)
 	ci.pAttachments = &attachmentDescriptions[0];
 
 	// Subpass
-	VkSubpassDescription spass;
-	ANKI_VK_MEMSET_DBG(spass);
-	spass.flags = 0;
+	VkSubpassDescription spass = {};
 	spass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
-	spass.inputAttachmentCount = 0;
-	spass.pInputAttachments = nullptr;
 	spass.colorAttachmentCount = init.m_colorAttachmentCount;
 	spass.pColorAttachments =
 		(init.m_colorAttachmentCount) ? &references[0] : nullptr;
-	spass.pResolveAttachments = nullptr;
 	spass.pDepthStencilAttachment = (hasDepthStencil) ? &dsReference : nullptr;
-	spass.preserveAttachmentCount = 0;
-	spass.pPreserveAttachments = nullptr;
 
 	ci.subpassCount = 1;
 	ci.pSubpasses = &spass;
-	ci.dependencyCount = 0;
-	ci.pDependencies = nullptr;
 
 	ANKI_VK_CHECK(vkCreateRenderPass(getDevice(), &ci, nullptr, &m_renderPass));
 
@@ -137,19 +162,32 @@ Error FramebufferImpl::initFramebuffer(const FramebufferInitInfo& init)
 	Bool hasDepthStencil = init.m_depthStencilAttachment.m_format.m_components
 		!= ComponentFormat::NONE;
 
-	VkFramebufferCreateInfo ci;
+	VkFramebufferCreateInfo ci = {};
 	ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
-	ci.pNext = nullptr;
-	ci.flags = 0;
 	ci.renderPass = m_renderPass;
 	ci.attachmentCount =
 		init.m_colorAttachmentCount + ((hasDepthStencil) ? 1 : 0);
 
-	// TODO set views
-	// TODO set size and the rest
+	ci.layers = 1;
+
+	if(m_defaultFramebuffer)
+	{
+		for(U i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
+		{
+			VkImageView view = getGrManagerImpl().getDefaultSurfaceImageView(i);
+			ci.pAttachments = &view;
+
+			ci.width = getGrManagerImpl().getDefaultSurfaceWidth();
+			ci.height = getGrManagerImpl().getDefaultSurfaceHeight();
 
-	ANKI_VK_CHECK(
-		vkCreateFramebuffer(getDevice(), &ci, nullptr, &m_framebuffer));
+			ANKI_VK_CHECK(vkCreateFramebuffer(
+				getDevice(), &ci, nullptr, &m_framebuffers[i]));
+		}
+	}
+	else
+	{
+		ANKI_ASSERT(0 && "TODO");
+	}
 
 	return ErrorCode::NONE;
 }

+ 7 - 0
src/gr/vulkan/GrManager.cpp

@@ -31,9 +31,16 @@ Error GrManager::init(GrManagerInitInfo& init)
 	return ErrorCode::NONE;
 }
 
+//==============================================================================
+void GrManager::beginFrame()
+{
+	m_impl->beginFrame();
+}
+
 //==============================================================================
 void GrManager::swapBuffers()
 {
+	m_impl->endFrame();
 }
 
 //==============================================================================

+ 146 - 84
src/gr/vulkan/GrManagerImpl.cpp

@@ -5,7 +5,11 @@
 
 #include <anki/gr/vulkan/GrManagerImpl.h>
 #include <anki/gr/GrManager.h>
+
 #include <anki/gr/Pipeline.h>
+#include <anki/gr/vulkan/CommandBufferImpl.h>
+#include <anki/gr/CommandBuffer.h>
+
 #include <anki/util/HashMap.h>
 #include <anki/util/Hash.h>
 #include <anki/core/Config.h>
@@ -76,6 +80,14 @@ public:
 //==============================================================================
 GrManagerImpl::~GrManagerImpl()
 {
+	// First thing: wait for the GPU
+	if(m_queue)
+	{
+		LockGuard<Mutex> lock(m_queueSubmitMtx);
+		vkQueueWaitIdle(m_queue);
+		m_queue = VK_NULL_HANDLE;
+	}
+
 	if(m_renderPasses)
 	{
 		auto it = m_renderPasses->m_hashmap.getBegin();
@@ -104,22 +116,35 @@ GrManagerImpl::~GrManagerImpl()
 
 	for(auto& x : m_perFrame)
 	{
-		if(x.m_fb)
+		if(x.m_imageView)
 		{
-			vkDestroyFramebuffer(m_device, x.m_fb, nullptr);
+			vkDestroyImageView(m_device, x.m_imageView, nullptr);
+			x.m_imageView = VK_NULL_HANDLE;
 		}
 
-		if(x.m_imageView)
+		if(x.m_presentFence)
 		{
-			vkDestroyImageView(m_device, x.m_imageView, nullptr);
+			vkDestroyFence(m_device, x.m_presentFence, nullptr);
+			x.m_presentFence = VK_NULL_HANDLE;
 		}
-	}
 
-	if(m_renderPass)
-	{
-		vkDestroyRenderPass(m_device, m_renderPass, nullptr);
+		if(x.m_acquireSemaphore)
+		{
+			vkDestroySemaphore(m_device, x.m_acquireSemaphore, nullptr);
+			x.m_acquireSemaphore = VK_NULL_HANDLE;
+		}
+
+		if(x.m_renderSemaphore)
+		{
+			vkDestroySemaphore(m_device, x.m_renderSemaphore, nullptr);
+			x.m_renderSemaphore = VK_NULL_HANDLE;
+		}
+
+		x.m_cmdbsSubmitted.destroy(getAllocator());
 	}
 
+	m_perThread.destroy(getAllocator());
+
 	if(m_swapchain)
 	{
 		vkDestroySwapchainKHR(m_device, m_swapchain, nullptr);
@@ -169,14 +194,16 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 	ANKI_CHECK(initDevice(init));
 	vkGetDeviceQueue(m_device, m_queueIdx, 0, &m_queue);
 	ANKI_CHECK(initSwapchain(init));
-	ANKI_CHECK(initFramebuffers(init));
 
 	ANKI_CHECK(initGlobalDsetLayout());
 	ANKI_CHECK(initGlobalPplineLayout());
 
 	m_renderPasses = getAllocator().newInstance<CompatibleRenderPassHashMap>();
 
-	// initMemory();
+	for(PerFrame& f : m_perFrame)
+	{
+		resetFrame(f);
+	}
 
 	glslang::InitializeProcess();
 
@@ -272,7 +299,7 @@ Error GrManagerImpl::initDevice(const GrManagerInitInfo& init)
 	for(U i = 0; i < count; ++i)
 	{
 		if((queueInfos[i].queueFlags & (DESITED_QUEUE_FLAGS))
-			!= DESITED_QUEUE_FLAGS)
+			== DESITED_QUEUE_FLAGS)
 		{
 			VkBool32 supportsPresent = false;
 			ANKI_VK_CHECK(vkGetPhysicalDeviceSurfaceSupportKHR(
@@ -347,7 +374,7 @@ Error GrManagerImpl::initSwapchain(const GrManagerInitInfo& init)
 	{
 		if(formats[formatCount].format == VK_FORMAT_B8G8R8A8_SRGB)
 		{
-			m_surfaceFormat = VK_FORMAT_B8G8R8A8_SRGB;
+			m_surfaceFormat = formats[formatCount].format;
 			colorspace = formats[formatCount].colorSpace;
 			break;
 		}
@@ -402,45 +429,11 @@ Error GrManagerImpl::initSwapchain(const GrManagerInitInfo& init)
 		ANKI_ASSERT(images[i]);
 	}
 
-	return ErrorCode::NONE;
-}
-
-//==============================================================================
-Error GrManagerImpl::initFramebuffers(const GrManagerInitInfo& init)
-{
-	VkAttachmentDescription attachment = {};
-	attachment.format = m_surfaceFormat;
-	attachment.samples = VK_SAMPLE_COUNT_1_BIT;
-	attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-	attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
-	attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-	attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
-	attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-	attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-
-	VkAttachmentReference colorRef = {};
-	colorRef.attachment = 0;
-	colorRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-
-	VkSubpassDescription subpass = {};
-	subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
-	subpass.colorAttachmentCount = 1;
-	subpass.pColorAttachments = &colorRef;
-
-	VkRenderPassCreateInfo ci = {};
-	ci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
-	ci.attachmentCount = 1;
-	ci.pAttachments = &attachment;
-	ci.subpassCount = 1;
-	ci.pSubpasses = &subpass;
-
-	ANKI_VK_CHECK(vkCreateRenderPass(m_device, &ci, nullptr, &m_renderPass));
-
+	// Create img views
 	for(U i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
 	{
 		PerFrame& perFrame = m_perFrame[i];
 
-		// Create the image view
 		VkImageViewCreateInfo ci = {};
 		ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
 		ci.flags = 0;
@@ -459,19 +452,6 @@ Error GrManagerImpl::initFramebuffers(const GrManagerInitInfo& init)
 
 		ANKI_VK_CHECK(
 			vkCreateImageView(m_device, &ci, nullptr, &perFrame.m_imageView));
-
-		// Create FB
-		VkFramebufferCreateInfo fbci = {};
-		fbci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
-		fbci.renderPass = m_renderPass;
-		fbci.attachmentCount = 1;
-		fbci.pAttachments = &perFrame.m_imageView;
-		fbci.width = m_surfaceWidth;
-		fbci.height = m_surfaceHeight;
-		fbci.layers = 1;
-
-		ANKI_VK_CHECK(
-			vkCreateFramebuffer(m_device, &fbci, nullptr, &perFrame.m_fb));
 	}
 
 	return ErrorCode::NONE;
@@ -598,24 +578,26 @@ VkRenderPass GrManagerImpl::getOrCreateCompatibleRenderPass(
 		{
 			// We only care about samples and format
 			VkAttachmentDescription& desc = attachmentDescriptions[i];
-			desc.format = convertFormat(init.m_color.m_attachments[i].m_format);
+			desc.format = (!init.m_color.m_drawsToDefaultFramebuffer)
+				? convertFormat(init.m_color.m_attachments[i].m_format)
+				: m_surfaceFormat;
 			desc.samples = VK_SAMPLE_COUNT_1_BIT;
 			desc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
 			desc.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
 			desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
 			desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
-			desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-			desc.finalLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+			desc.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
+			desc.finalLayout = VK_IMAGE_LAYOUT_GENERAL;
 
 			references[i].attachment = i;
-			references[i].layout = VK_IMAGE_LAYOUT_UNDEFINED;
+			references[i].layout = VK_IMAGE_LAYOUT_GENERAL;
 		}
 
 		ci.attachmentCount = init.m_color.m_attachmentCount;
 
 		Bool hasDepthStencil =
 			init.m_depthStencil.m_format.m_components != ComponentFormat::NONE;
-		VkAttachmentReference dsReference = {0, VK_IMAGE_LAYOUT_UNDEFINED};
+		VkAttachmentReference dsReference = {0, VK_IMAGE_LAYOUT_GENERAL};
 		if(hasDepthStencil)
 		{
 			VkAttachmentDescription& desc =
@@ -626,11 +608,11 @@ VkRenderPass GrManagerImpl::getOrCreateCompatibleRenderPass(
 			desc.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
 			desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
 			desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
-			desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-			desc.finalLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+			desc.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
+			desc.finalLayout = VK_IMAGE_LAYOUT_GENERAL;
 
 			dsReference.attachment = ci.attachmentCount;
-			dsReference.layout = VK_IMAGE_LAYOUT_UNDEFINED;
+			dsReference.layout = VK_IMAGE_LAYOUT_GENERAL;
 
 			++ci.attachmentCount;
 		}
@@ -652,8 +634,10 @@ VkRenderPass GrManagerImpl::getOrCreateCompatibleRenderPass(
 		ANKI_VK_CHECKF(vkCreateRenderPass(m_device, &ci, nullptr, &rpass));
 
 		m_renderPasses->m_hashmap.pushBack(getAllocator(), key, rpass);
+		out = rpass;
 	}
 
+	ANKI_ASSERT(out);
 	return out;
 }
 
@@ -742,7 +726,7 @@ void GrManagerImpl::freeCallback(void* userData, void* ptr)
 //==============================================================================
 void GrManagerImpl::beginFrame()
 {
-	PerFrame& frame = m_perFrame[m_frame];
+	PerFrame& frame = m_perFrame[m_frame % MAX_FRAMES_IN_FLIGHT];
 
 	// Create a semaphore
 	VkSemaphoreCreateInfo semaphoreCi = {};
@@ -760,13 +744,14 @@ void GrManagerImpl::beginFrame()
 		frame.m_acquireSemaphore,
 		0,
 		&imageIdx));
-	ANKI_ASSERT(imageIdx == m_frame && "Wrong assumption");
+	ANKI_ASSERT(
+		imageIdx == (m_frame % MAX_FRAMES_IN_FLIGHT) && "Wrong assumption");
 }
 
 //==============================================================================
 void GrManagerImpl::endFrame()
 {
-	PerFrame& frame = m_perFrame[m_frame];
+	PerFrame& frame = m_perFrame[m_frame % MAX_FRAMES_IN_FLIGHT];
 
 	// Wait for the fence of N-2 frame
 	U waitFrameIdx = (m_frame + 1) % MAX_FRAMES_IN_FLIGHT;
@@ -780,28 +765,37 @@ void GrManagerImpl::endFrame()
 
 	resetFrame(waitFrame);
 
-	if(frame.m_renderSemaphoresCount == 0)
+	if(frame.m_renderSemaphore == VK_NULL_HANDLE)
 	{
 		ANKI_LOGW("Nobody draw to the default framebuffer");
 	}
 
-	// TODO Transition image
-
 	// Present
 	uint32_t imageIdx = m_frame % MAX_FRAMES_IN_FLIGHT;
 	VkPresentInfoKHR present = {};
 	present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
-	present.waitSemaphoreCount = frame.m_renderSemaphoresCount;
-	present.pWaitSemaphores = (frame.m_renderSemaphoresCount > 0)
-		? &frame.m_renderSemaphores[0]
-		: nullptr;
+	present.waitSemaphoreCount = (frame.m_renderSemaphore) ? 1 : 0;
+	present.pWaitSemaphores =
+		(frame.m_renderSemaphore) ? &frame.m_renderSemaphore : nullptr;
 	present.swapchainCount = 1;
 	present.pSwapchains = &m_swapchain;
 	present.pImageIndices = &imageIdx;
-	ANKI_VK_CHECKF(vkQueuePresentKHR(m_queue, &present));
+
+	{
+		LockGuard<Mutex> lock(m_queueSubmitMtx);
+		ANKI_VK_CHECKF(vkQueuePresentKHR(m_queue, &present));
+	}
 
 	// Finalize
 	++m_frame;
+
+#if ANKI_ASSERTIONS
+	ANKI_ASSERT(m_cmdbWithIndicationThatIsFirstSubmitted
+		&& m_cmdbWithIndicationThatIsLastSubmitted
+		&& "Forgot to set some command buffer flags");
+	m_cmdbWithIndicationThatIsFirstSubmitted = false;
+	m_cmdbWithIndicationThatIsLastSubmitted = false;
+#endif
 }
 
 //==============================================================================
@@ -812,20 +806,26 @@ void GrManagerImpl::resetFrame(PerFrame& frame)
 		// Recycle fence
 		ANKI_VK_CHECKF(vkResetFences(m_device, 1, &frame.m_presentFence));
 	}
+	else
+	{
+		VkFenceCreateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+		vkCreateFence(m_device, &ci, nullptr, &frame.m_presentFence);
+	}
 
 	if(frame.m_acquireSemaphore)
 	{
 		vkDestroySemaphore(m_device, frame.m_acquireSemaphore, nullptr);
+		frame.m_acquireSemaphore = VK_NULL_HANDLE;
 	}
 
-	for(U i = 0; i < frame.m_renderSemaphoresCount; ++i)
+	if(frame.m_renderSemaphore)
 	{
-		ANKI_ASSERT(frame.m_renderSemaphores[i]);
-		vkDestroySemaphore(m_device, frame.m_renderSemaphores[i], nullptr);
-		frame.m_renderSemaphores[i] = VK_NULL_HANDLE;
+		vkDestroySemaphore(m_device, frame.m_renderSemaphore, nullptr);
+		frame.m_renderSemaphore = VK_NULL_HANDLE;
 	}
 
-	frame.m_renderSemaphoresCount = 0;
+	frame.m_cmdbsSubmitted.destroy(getAllocator());
 }
 
 //==============================================================================
@@ -880,4 +880,66 @@ void GrManagerImpl::deleteCommandBuffer(
 	thread.m_cmdbs.deleteCommandBuffer(cmdb, secondLevel);
 }
 
+//==============================================================================
+void GrManagerImpl::flushCommandBuffer(
+	CommandBufferImpl& impl, CommandBufferPtr ptr)
+{
+	VkPipelineStageFlags waitStage =
+		VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+
+	VkCommandBuffer handle = impl.getHandle();
+
+	VkSubmitInfo submit = {};
+	submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+
+	VkFence fenceToTrigger = VK_NULL_HANDLE;
+	PerFrame& frame = m_perFrame[m_frame % MAX_FRAMES_IN_FLIGHT];
+
+	// Do some special stuff for the last command buffer
+	if(impl.renderedToDefaultFramebuffer())
+	{
+		submit.waitSemaphoreCount = 1;
+		submit.pWaitDstStageMask = &waitStage;
+		submit.pWaitSemaphores = &frame.m_acquireSemaphore;
+
+		// Create the semaphore to signal
+		ANKI_ASSERT(frame.m_renderSemaphore == VK_NULL_HANDLE
+			&& "Only one begin/end render pass is allowed with the default fb");
+		VkSemaphoreCreateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+		ANKI_VK_CHECKF(vkCreateSemaphore(
+			m_device, &ci, nullptr, &frame.m_renderSemaphore));
+
+		submit.signalSemaphoreCount = 1;
+		submit.pSignalSemaphores = &frame.m_renderSemaphore;
+
+		fenceToTrigger = frame.m_presentFence;
+		ANKI_ASSERT(fenceToTrigger);
+	}
+
+	submit.commandBufferCount = 1;
+	submit.pCommandBuffers = &handle;
+
+	// Lock to submit
+	LockGuard<Mutex> lock(m_queueSubmitMtx);
+
+	frame.m_cmdbsSubmitted.pushBack(getAllocator(), ptr);
+
+#if ANKI_ASSERTIONS
+	if(impl.isTheFirstFramebufferOfTheFrame())
+	{
+		ANKI_ASSERT(m_cmdbWithIndicationThatIsFirstSubmitted == false);
+		m_cmdbWithIndicationThatIsFirstSubmitted = true;
+	}
+
+	if(impl.isTheLastFramebufferOfTheFrame())
+	{
+		ANKI_ASSERT(m_cmdbWithIndicationThatIsLastSubmitted == false);
+		m_cmdbWithIndicationThatIsLastSubmitted = true;
+	}
+#endif
+
+	ANKI_VK_CHECKF(vkQueueSubmit(m_queue, 1, &submit, fenceToTrigger));
+}
+
 } // end namespace anki

+ 10 - 2
src/gr/vulkan/ObjectRecycler.cpp

@@ -11,12 +11,20 @@ namespace anki
 //==============================================================================
 CommandBufferObjectRecycler::~CommandBufferObjectRecycler()
 {
+	for(CmdbType& type : m_types)
+	{
+		if(type.m_count)
+		{
+			vkFreeCommandBuffers(m_dev, m_pool, type.m_count, &type.m_cmdbs[0]);
+		}
+
+		type.m_cmdbs.destroy(m_alloc);
+	}
+
 	if(m_pool)
 	{
 		vkDestroyCommandPool(m_dev, m_pool, nullptr);
 	}
-
-	// TODO the rest
 }
 
 //==============================================================================

+ 18 - 14
src/gr/vulkan/PipelineImpl.cpp

@@ -175,12 +175,6 @@ void PipelineImpl::initShaders(
 VkPipelineVertexInputStateCreateInfo* PipelineImpl::initVertexStage(
 	const VertexStateInfo& vertex, VkPipelineVertexInputStateCreateInfo& ci)
 {
-	if(vertex.m_bindingCount == 0 && vertex.m_attributeCount == 0)
-	{
-		// Early out
-		return nullptr;
-	}
-
 	// First the bindings
 	ci.vertexBindingDescriptionCount = vertex.m_bindingCount;
 	for(U i = 0; i < ci.vertexBindingDescriptionCount; ++i)
@@ -279,14 +273,22 @@ VkPipelineMultisampleStateCreateInfo* PipelineImpl::initMsState(
 VkPipelineDepthStencilStateCreateInfo* PipelineImpl::initDsState(
 	const DepthStencilStateInfo& ds, VkPipelineDepthStencilStateCreateInfo& ci)
 {
-	ci.depthTestEnable = (ds.m_depthCompareFunction != CompareOperation::ALWAYS)
-		|| ds.m_depthWriteEnabled;
-	ci.depthWriteEnable = ds.m_depthWriteEnabled;
-	ci.depthCompareOp = convertCompareOp(ds.m_depthCompareFunction);
-	ci.depthBoundsTestEnable = VK_FALSE;
-	ci.stencilTestEnable = VK_FALSE; // For now no stencil
-
-	return &ci;
+	if(ds.isInUse())
+	{
+		ci.depthTestEnable =
+			(ds.m_depthCompareFunction != CompareOperation::ALWAYS)
+			|| ds.m_depthWriteEnabled;
+		ci.depthWriteEnable = ds.m_depthWriteEnabled;
+		ci.depthCompareOp = convertCompareOp(ds.m_depthCompareFunction);
+		ci.depthBoundsTestEnable = VK_FALSE;
+		ci.stencilTestEnable = VK_FALSE; // For now no stencil
+
+		return &ci;
+	}
+	else
+	{
+		return nullptr;
+	}
 }
 
 //==============================================================================
@@ -343,10 +345,12 @@ Error PipelineImpl::init(const PipelineInitInfo& init)
 {
 	if(init.m_shaders[ShaderType::COMPUTE].isCreated())
 	{
+		m_bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
 		return initCompute(init);
 	}
 	else
 	{
+		m_bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
 		return initGraphics(init);
 	}
 

+ 3 - 0
src/renderer/MainRenderer.cpp

@@ -53,6 +53,9 @@ Error MainRenderer::create(ThreadPool* threadpool,
 	m_width = config.getNumber("width");
 	m_height = config.getNumber("height");
 	FramebufferInitInfo fbInit;
+	fbInit.m_colorAttachmentCount = 1;
+	fbInit.m_colorAttachments[0].m_loadOperation =
+		AttachmentLoadOperation::DONT_CARE;
 	m_defaultFb = gr->newInstance<Framebuffer>(fbInit);
 
 	// Init renderer and manipulate the width/height

+ 83 - 1
tests/gr/Gr.cpp

@@ -12,7 +12,20 @@
 namespace anki
 {
 
-static const char* VERT_SRC = R"(out gl_PerVertex
+const U WIDTH = 1024;
+const U HEIGHT = 768;
+
+static const char* VERT_SRC = R"(
+
+#if defined(ANKI_GL)
+#define gl_VertexIndex gl_VertexID
+#elif defined(ANKI_VK)
+// Do nothing
+#else
+#error See file
+#endif
+
+out gl_PerVertex
 {
 	vec4 gl_Position;
 };
@@ -23,6 +36,9 @@ void main()
 		vec2[](vec2(-1.0, 1.0), vec2(0.0, -1.0), vec2(1.0, 1.0));
 
 	gl_Position = vec4(POSITIONS[gl_VertexIndex % 3], 0.0, 1.0);
+#if defined(ANKI_VK)
+	gl_Position.y = -gl_Position.y;
+#endif
 })";
 
 static const char* FRAG_SRC = R"(layout (location = 0) out vec4 out_color;
@@ -38,6 +54,8 @@ static NativeWindow* createWindow()
 	HeapAllocator<U8> alloc(allocAligned, nullptr);
 
 	NativeWindowInitInfo inf;
+	inf.m_width = WIDTH;
+	inf.m_height = HEIGHT;
 	NativeWindow* win = new NativeWindow();
 
 	ANKI_TEST_EXPECT_NO_ERR(win->init(inf, alloc));
@@ -52,6 +70,7 @@ static void createGrManager(NativeWindow*& win, GrManager*& gr)
 	gr = new GrManager();
 
 	Config cfg;
+	cfg.set("debugContext", 1);
 	GrManagerInitInfo inf;
 	inf.m_allocCallback = allocAligned;
 	inf.m_cacheDirectory = "./";
@@ -114,4 +133,67 @@ ANKI_TEST(Gr, Pipeline)
 	delete win;
 }
 
+//==============================================================================
+ANKI_TEST(Gr, SimpleDrawcall)
+{
+	NativeWindow* win = nullptr;
+	GrManager* gr = nullptr;
+	createGrManager(win, gr);
+
+	{
+		ShaderPtr vert = gr->newInstance<Shader>(ShaderType::VERTEX, VERT_SRC);
+		ShaderPtr frag =
+			gr->newInstance<Shader>(ShaderType::FRAGMENT, FRAG_SRC);
+
+		PipelineInitInfo init;
+		init.m_shaders[ShaderType::VERTEX] = vert;
+		init.m_shaders[ShaderType::FRAGMENT] = frag;
+		init.m_color.m_drawsToDefaultFramebuffer = true;
+		init.m_color.m_attachmentCount = 1;
+		init.m_depthStencil.m_depthWriteEnabled = false;
+
+		PipelinePtr ppline = gr->newInstance<Pipeline>(init);
+
+		FramebufferInitInfo fbinit;
+		fbinit.m_colorAttachmentCount = 1;
+		fbinit.m_colorAttachments[0].m_clearValue.m_colorf = {
+			1.0, 0.0, 1.0, 1.0};
+		FramebufferPtr fb = gr->newInstance<Framebuffer>(fbinit);
+
+		U iterations = 100;
+		while(--iterations)
+		{
+			HighRezTimer timer;
+			timer.start();
+
+			gr->beginFrame();
+
+			CommandBufferInitInfo cinit;
+			cinit.m_frameFirstCommandBuffer = true;
+			cinit.m_frameLastCommandBuffer = true;
+			CommandBufferPtr cmdb = gr->newInstance<CommandBuffer>(cinit);
+
+			cmdb->setViewport(0, 0, WIDTH, HEIGHT);
+			cmdb->setPolygonOffset(0.0, 0.0);
+			cmdb->bindPipeline(ppline);
+			cmdb->beginRenderPass(fb);
+			cmdb->drawArrays(3);
+			cmdb->endRenderPass();
+			cmdb->flush();
+
+			gr->swapBuffers();
+
+			timer.stop();
+			const F32 TICK = 1.0 / 30.0;
+			if(timer.getElapsedTime() < TICK)
+			{
+				HighRezTimer::sleep(TICK - timer.getElapsedTime());
+			}
+		}
+	}
+
+	delete gr;
+	delete win;
+}
+
 } // end namespace anki