Browse Source

Vulkan: do some work to fix the SimpleDraw test

Panagiotis Christopoulos Charitos 9 years ago
parent
commit
fe959a9bbc
43 changed files with 387 additions and 224 deletions
  1. 2 2
      src/anki/gr/gl/CommandBufferImpl.cpp
  2. 5 0
      src/anki/gr/gl/Common.h
  3. 1 1
      src/anki/gr/gl/Error.cpp
  4. 1 1
      src/anki/gr/gl/FramebufferImpl.cpp
  5. 4 4
      src/anki/gr/gl/GlState.cpp
  6. 5 5
      src/anki/gr/gl/GrManagerImplSdl.cpp
  7. 3 3
      src/anki/gr/gl/RenderingThread.cpp
  8. 1 1
      src/anki/gr/gl/ShaderProgramImpl.cpp
  9. 1 1
      src/anki/gr/vulkan/Buffer.cpp
  10. 2 2
      src/anki/gr/vulkan/BufferImpl.cpp
  11. 6 6
      src/anki/gr/vulkan/CommandBuffer.cpp
  12. 39 2
      src/anki/gr/vulkan/CommandBufferImpl.cpp
  13. 21 18
      src/anki/gr/vulkan/CommandBufferImpl.h
  14. 26 62
      src/anki/gr/vulkan/CommandBufferImpl.inl.h
  15. 1 1
      src/anki/gr/vulkan/Common.cpp
  16. 7 2
      src/anki/gr/vulkan/Common.h
  17. 46 0
      src/anki/gr/vulkan/DescriptorSet.cpp
  18. 1 4
      src/anki/gr/vulkan/DescriptorSet.h
  19. 1 1
      src/anki/gr/vulkan/Fence.inl.h
  20. 1 1
      src/anki/gr/vulkan/Framebuffer.cpp
  21. 22 4
      src/anki/gr/vulkan/FramebufferImpl.cpp
  22. 3 3
      src/anki/gr/vulkan/FramebufferImpl.h
  23. 1 1
      src/anki/gr/vulkan/GpuMemoryManager.cpp
  24. 40 34
      src/anki/gr/vulkan/GrManagerImpl.cpp
  25. 6 0
      src/anki/gr/vulkan/GrManagerImpl.h
  26. 1 1
      src/anki/gr/vulkan/GrManagerImplSdl.cpp
  27. 1 1
      src/anki/gr/vulkan/OcclusionQuery.cpp
  28. 39 27
      src/anki/gr/vulkan/Pipeline.cpp
  29. 17 3
      src/anki/gr/vulkan/Pipeline.h
  30. 7 17
      src/anki/gr/vulkan/PipelineLayout.cpp
  31. 1 2
      src/anki/gr/vulkan/PipelineLayout.h
  32. 1 1
      src/anki/gr/vulkan/QueryExtra.cpp
  33. 1 1
      src/anki/gr/vulkan/Sampler.cpp
  34. 1 1
      src/anki/gr/vulkan/Shader.cpp
  35. 1 1
      src/anki/gr/vulkan/ShaderImpl.cpp
  36. 39 3
      src/anki/gr/vulkan/ShaderProgram.cpp
  37. 1 1
      src/anki/gr/vulkan/ShaderProgramImpl.h
  38. 1 1
      src/anki/gr/vulkan/Texture.cpp
  39. 1 1
      src/anki/gr/vulkan/TextureImpl.cpp
  40. 11 1
      src/anki/misc/ConfigSet.cpp
  41. 11 3
      src/anki/util/Logger.cpp
  42. 6 0
      tests/framework/Framework.h
  43. 1 0
      tests/gr/Gr.cpp

+ 2 - 2
src/anki/gr/gl/CommandBufferImpl.cpp

@@ -38,8 +38,8 @@ void CommandBufferImpl::destroy()
 #if ANKI_DEBUG
 	if(!m_executed && m_firstCommand)
 	{
-		ANKI_LOGW("Chain contains commands but never executed. "
-				  "This should only happen on exceptions");
+		ANKI_GL_LOGW("Chain contains commands but never executed. "
+					 "This should only happen on exceptions");
 	}
 #endif
 

+ 5 - 0
src/anki/gr/gl/Common.h

@@ -31,6 +31,11 @@ class RenderingThread;
 /// @addtogroup opengl
 /// @{
 
+#define ANKI_GL_LOGI(...) ANKI_LOGI("GR/GL: " __VA_ARGS__)
+#define ANKI_GL_LOGE(...) ANKI_LOGE("GR/GL: " __VA_ARGS__)
+#define ANKI_GL_LOGW(...) ANKI_LOGW("GR/GL: " __VA_ARGS__)
+#define ANKI_GL_LOGF(...) ANKI_LOGF("GR/GL: " __VA_ARGS__)
+
 // Spec limits
 const U MAX_UNIFORM_BLOCK_SIZE = 16384;
 const U MAX_STORAGE_BLOCK_SIZE = 2 << 27;

+ 1 - 1
src/anki/gr/gl/Error.cpp

@@ -45,7 +45,7 @@ void glConditionalCheckError(const char* file, int line, const char* func)
 	memcpy(errStr, tmp, sizeof(tmp));
 	strcat(errStr, glerr);
 
-	ANKI_LOGF("GL error: %s (%s:%d %s)", errStr, file, line, func);
+	ANKI_GL_LOGF("GL error: %s (%s:%d %s)", errStr, file, line, func);
 }
 
 } // end namespace

+ 1 - 1
src/anki/gr/gl/FramebufferImpl.cpp

@@ -103,7 +103,7 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 	GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
 	if(status != GL_FRAMEBUFFER_COMPLETE)
 	{
-		ANKI_LOGE("FBO is incomplete: 0x%x", status);
+		ANKI_GL_LOGE("FBO is incomplete: 0x%x", status);
 		return ErrorCode::FUNCTION_FAILED;
 	}
 

+ 4 - 4
src/anki/gr/gl/GlState.cpp

@@ -69,13 +69,13 @@ __stdcall
 	switch(severity)
 	{
 	case GL_DEBUG_SEVERITY_LOW:
-		ANKI_LOGI("GL: %s, %s: %s", sourced->str, typed->str, message);
+		ANKI_GL_LOGI("GL: %s, %s: %s", sourced->str, typed->str, message);
 		break;
 	case GL_DEBUG_SEVERITY_MEDIUM:
-		ANKI_LOGW("GL: %s, %s: %s", sourced->str, typed->str, message);
+		ANKI_GL_LOGW("GL: %s, %s: %s", sourced->str, typed->str, message);
 		break;
 	case GL_DEBUG_SEVERITY_HIGH:
-		ANKI_LOGE("GL: %s, %s: %s", sourced->str, typed->str, message);
+		ANKI_GL_LOGE("GL: %s, %s: %s", sourced->str, typed->str, message);
 		break;
 	}
 }
@@ -105,7 +105,7 @@ void GlState::initRenderThread()
 	{
 		m_gpu = GpuVendor::NVIDIA;
 	}
-	ANKI_LOGI("GPU vendor is %s", &GPU_VENDOR_STR[m_gpu][0]);
+	ANKI_GL_LOGI("GPU vendor is %s", &GPU_VENDOR_STR[m_gpu][0]);
 
 // Enable debug messages
 #if ANKI_GL == ANKI_GL_DESKTOP

+ 5 - 5
src/anki/gr/gl/GrManagerImplSdl.cpp

@@ -35,7 +35,7 @@ public:
 	{
 		m_window = init.m_window->getNative().m_window;
 
-		ANKI_LOGI("Creating GL %u.%u context...",
+		ANKI_GL_LOGI("Creating GL %u.%u context...",
 			U(init.m_config->getNumber("glmajor")),
 			U(init.m_config->getNumber("glminor")));
 
@@ -43,7 +43,7 @@ public:
 		{
 			if(SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG))
 			{
-				ANKI_LOGE("SDL_GL_SetAttribute() failed");
+				ANKI_GL_LOGE("SDL_GL_SetAttribute() failed");
 				return ErrorCode::FUNCTION_FAILED;
 			}
 		}
@@ -52,7 +52,7 @@ public:
 			|| SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, init.m_config->getNumber("glminor"))
 			|| SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE))
 		{
-			ANKI_LOGE("SDL_GL_SetAttribute() failed");
+			ANKI_GL_LOGE("SDL_GL_SetAttribute() failed");
 			return ErrorCode::FUNCTION_FAILED;
 		}
 
@@ -60,7 +60,7 @@ public:
 		m_context = SDL_GL_CreateContext(m_window);
 		if(m_context == nullptr)
 		{
-			ANKI_LOGE("SDL_GL_CreateContext() failed");
+			ANKI_GL_LOGE("SDL_GL_CreateContext() failed");
 			return ErrorCode::FUNCTION_FAILED;
 		}
 
@@ -68,7 +68,7 @@ public:
 		glewExperimental = GL_TRUE;
 		if(glewInit() != GLEW_OK)
 		{
-			ANKI_LOGE("GLEW initialization failed");
+			ANKI_GL_LOGE("GLEW initialization failed");
 			return ErrorCode::FUNCTION_FAILED;
 		}
 		glGetError();

+ 3 - 3
src/anki/gr/gl/RenderingThread.cpp

@@ -95,7 +95,7 @@ void RenderingThread::flushCommandBuffer(CommandBufferPtr cmdb)
 		}
 		else
 		{
-			ANKI_LOGW("Rendering queue too small");
+			ANKI_GL_LOGW("Rendering queue too small");
 		}
 
 		m_condVar.notifyOne(); // Wake the thread
@@ -150,7 +150,7 @@ void RenderingThread::prepare()
 	// Ignore the first error
 	glGetError();
 
-	ANKI_LOGI("OpenGL async thread started: OpenGL version %s, GLSL version %s",
+	ANKI_GL_LOGI("OpenGL async thread started: OpenGL version %s, GLSL version %s",
 		reinterpret_cast<const char*>(glGetString(GL_VERSION)),
 		reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)));
 
@@ -228,7 +228,7 @@ void RenderingThread::threadLoop()
 
 		if(err)
 		{
-			ANKI_LOGE("Error in rendering thread. Aborting");
+			ANKI_GL_LOGE("Error in rendering thread. Aborting");
 			abort();
 		}
 	}

+ 1 - 1
src/anki/gr/gl/ShaderProgramImpl.cpp

@@ -67,7 +67,7 @@ Error ShaderProgramImpl::link(GLuint vert, GLuint frag)
 
 		glGetProgramInfoLog(m_glName, infoLen, &charsWritten, &infoLogTxt[0]);
 
-		ANKI_LOGE("Link error log follows (vs:%u, fs:%u):\n%s", vert, frag, &infoLogTxt[0]);
+		ANKI_GL_LOGE("Link error log follows (vs:%u, fs:%u):\n%s", vert, frag, &infoLogTxt[0]);
 
 		err = ErrorCode::USER_DATA;
 	}

+ 1 - 1
src/anki/gr/vulkan/Buffer.cpp

@@ -24,7 +24,7 @@ void Buffer::init(PtrSize size, BufferUsageBit usage, BufferMapAccessBit access)
 
 	if(m_impl->init(size, usage, access))
 	{
-		ANKI_LOGF("Cannot recover");
+		ANKI_VK_LOGF("Cannot recover");
 	}
 }
 

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

@@ -68,7 +68,7 @@ Error BufferImpl::init(PtrSize size, BufferUsageBit usage, BufferMapAccessBit ac
 		// Fallback: any host
 		if(memIdx == MAX_U32)
 		{
-			ANKI_LOGW("Vulkan: Using a fallback mode for write-only buffer");
+			ANKI_VK_LOGW("Vulkan: Using a fallback mode for write-only buffer");
 			memIdx = getGrManagerImpl().getGpuMemoryManager().findMemoryType(
 				req.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, 0);
 		}
@@ -93,7 +93,7 @@ Error BufferImpl::init(PtrSize size, BufferUsageBit usage, BufferMapAccessBit ac
 		// Fallback: Just host
 		if(memIdx == MAX_U32)
 		{
-			ANKI_LOGW("Vulkan: Using a fallback mode for read/write buffer");
+			ANKI_VK_LOGW("Vulkan: Using a fallback mode for read/write buffer");
 			memIdx = getGrManagerImpl().getGpuMemoryManager().findMemoryType(
 				req.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, 0);
 		}

+ 6 - 6
src/anki/gr/vulkan/CommandBuffer.cpp

@@ -25,7 +25,7 @@ void CommandBuffer::init(CommandBufferInitInfo& inf)
 
 	if(m_impl->init(inf))
 	{
-		ANKI_LOGF("Cannot recover");
+		ANKI_VK_LOGF("Cannot recover");
 	}
 }
 
@@ -193,7 +193,7 @@ void CommandBuffer::bindImage(U32 set, U32 binding, TexturePtr img, U32 level)
 
 void CommandBuffer::bindShaderProgram(ShaderProgramPtr prog)
 {
-	ANKI_ASSERT(!"TODO");
+	m_impl->bindShaderProgram(prog);
 }
 
 void CommandBuffer::beginRenderPass(FramebufferPtr fb)
@@ -209,22 +209,22 @@ void CommandBuffer::endRenderPass()
 void CommandBuffer::drawElements(
 	PrimitiveTopology topology, U32 count, U32 instanceCount, U32 firstIndex, U32 baseVertex, U32 baseInstance)
 {
-	ANKI_ASSERT(!"TODO");
+	m_impl->drawElements(topology, count, instanceCount, firstIndex, baseVertex, baseInstance);
 }
 
 void CommandBuffer::drawArrays(PrimitiveTopology topology, U32 count, U32 instanceCount, U32 first, U32 baseInstance)
 {
-	ANKI_ASSERT(!"TODO");
+	m_impl->drawArrays(topology, count, instanceCount, first, baseInstance);
 }
 
 void CommandBuffer::drawArraysIndirect(PrimitiveTopology topology, U32 drawCount, PtrSize offset, BufferPtr buff)
 {
-	ANKI_ASSERT(!"TODO");
+	m_impl->drawArraysIndirect(topology, drawCount, offset, buff);
 }
 
 void CommandBuffer::drawElementsIndirect(PrimitiveTopology topology, U32 drawCount, PtrSize offset, BufferPtr buff)
 {
-	ANKI_ASSERT(!"TODO");
+	m_impl->drawElementsIndirect(topology, drawCount, offset, buff);
 }
 
 void CommandBuffer::dispatchCompute(U32 groupCountX, U32 groupCountY, U32 groupCountZ)

+ 39 - 2
src/anki/gr/vulkan/CommandBufferImpl.cpp

@@ -23,12 +23,12 @@ CommandBufferImpl::~CommandBufferImpl()
 {
 	if(m_empty)
 	{
-		ANKI_LOGW("Command buffer was empty");
+		ANKI_VK_LOGW("Command buffer was empty");
 	}
 
 	if(!m_finalized)
 	{
-		ANKI_LOGW("Command buffer was not flushed");
+		ANKI_VK_LOGW("Command buffer was not flushed");
 	}
 
 	if(m_handle)
@@ -41,6 +41,7 @@ CommandBufferImpl::~CommandBufferImpl()
 	m_queryList.destroy(m_alloc);
 	m_bufferList.destroy(m_alloc);
 	m_cmdbList.destroy(m_alloc);
+	m_progs.destroy(m_alloc);
 
 	m_imgBarriers.destroy(m_alloc);
 	m_buffBarriers.destroy(m_alloc);
@@ -109,6 +110,8 @@ void CommandBufferImpl::beginRenderPass(FramebufferPtr fb)
 
 void CommandBufferImpl::beginRenderPassInternal()
 {
+	m_state.beginRenderPass(m_activeFb);
+
 	FramebufferImpl& impl = *m_activeFb->m_impl;
 
 	VkRenderPassBeginInfo bi = {};
@@ -778,4 +781,38 @@ void CommandBufferImpl::flushWriteQueryResults()
 	m_writeQueryAtomCount = 0;
 }
 
+void CommandBufferImpl::setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
+{
+	ANKI_ASSERT(minx < maxx && miny < maxy);
+	commandCommon();
+
+	if(m_viewport[0] != minx || m_viewport[1] != miny || m_viewport[2] != maxx || m_viewport[3] != maxy)
+	{
+		VkViewport s;
+		s.x = minx;
+		s.y = miny;
+		s.width = maxx - minx;
+		s.height = maxy - miny;
+		s.minDepth = 0.0;
+		s.maxDepth = 1.0;
+		ANKI_CMD(vkCmdSetViewport(m_handle, 0, 1, &s), ANY_OTHER_COMMAND);
+
+		VkRect2D scissor = {};
+		scissor.extent.width = maxx - minx;
+		scissor.extent.height = maxy - miny;
+		scissor.offset.x = minx;
+		scissor.offset.y = miny;
+		ANKI_CMD(vkCmdSetScissor(m_handle, 0, 1, &scissor), ANY_OTHER_COMMAND);
+
+		m_viewport[0] = minx;
+		m_viewport[1] = miny;
+		m_viewport[2] = maxx;
+		m_viewport[3] = maxy;
+	}
+	else
+	{
+		// Skip
+	}
+}
+
 } // end namespace anki

+ 21 - 18
src/anki/gr/vulkan/CommandBufferImpl.h

@@ -9,6 +9,7 @@
 #include <anki/gr/CommandBuffer.h>
 #include <anki/gr/Texture.h>
 #include <anki/gr/vulkan/TextureImpl.h>
+#include <anki/gr/vulkan/Pipeline.h>
 #include <anki/util/List.h>
 
 namespace anki
@@ -63,6 +64,14 @@ public:
 		return !!(m_flags & CommandBufferFlag::SECOND_LEVEL);
 	}
 
+	void bindVertexBuffer(U32 binding, BufferPtr buff, PtrSize offset, PtrSize stride);
+
+	void setVertexAttribute(U32 location, U32 buffBinding, const PixelFormat& fmt, PtrSize relativeOffset);
+
+	void bindIndexBuffer(BufferPtr buff, PtrSize offset, IndexType type);
+
+	void setPrimitiveRestart(Bool enable);
+
 	void setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy);
 
 	void setPolygonOffset(F32 factor, F32 units);
@@ -77,13 +86,14 @@ public:
 
 	void endRenderPass();
 
-	void drawArrays(U32 count, U32 instanceCount, U32 first, U32 baseInstance);
+	void drawArrays(PrimitiveTopology topology, U32 count, U32 instanceCount, U32 first, U32 baseInstance);
 
-	void drawElements(U32 count, U32 instanceCount, U32 firstIndex, U32 baseVertex, U32 baseInstance);
+	void drawElements(
+		PrimitiveTopology topology, U32 count, U32 instanceCount, U32 firstIndex, U32 baseVertex, U32 baseInstance);
 
-	void drawArraysIndirect(U32 drawCount, PtrSize offset, BufferPtr buff);
+	void drawArraysIndirect(PrimitiveTopology topology, U32 drawCount, PtrSize offset, BufferPtr& buff);
 
-	void drawElementsIndirect(U32 drawCount, PtrSize offset, BufferPtr buff);
+	void drawElementsIndirect(PrimitiveTopology topology, U32 drawCount, PtrSize offset, BufferPtr& buff);
 
 	void dispatchCompute(U32 groupCountX, U32 groupCountY, U32 groupCountZ);
 
@@ -128,6 +138,8 @@ public:
 
 	void writeOcclusionQueryResultToBuffer(OcclusionQueryPtr query, PtrSize offset, BufferPtr buff);
 
+	void bindShaderProgram(ShaderProgramPtr& prog);
+
 private:
 	StackAllocator<U8> m_alloc;
 
@@ -141,6 +153,10 @@ private:
 	U m_rpCommandCount = 0; ///< Number of drawcalls or pushed cmdbs in rp.
 	FramebufferPtr m_activeFb;
 
+	ShaderProgramImpl* m_graphicsProg ANKI_DBG_NULLIFY; ///< Last bound graphics program
+
+	PipelineStateTracker m_state;
+
 	/// @name cleanup_references
 	/// @{
 	List<FramebufferPtr> m_fbList;
@@ -148,6 +164,7 @@ private:
 	List<OcclusionQueryPtr> m_queryList;
 	List<BufferPtr> m_bufferList;
 	List<CommandBufferPtr> m_cmdbList;
+	List<ShaderProgramPtr> m_progs;
 /// @}
 
 #if ANKI_ASSERTIONS
@@ -206,18 +223,6 @@ private:
 	U16 m_writeQueryAtomCount = 0;
 	/// @}
 
-	class DeferredDsetBinding
-	{
-	public:
-		Array<U32, MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS> m_dynOffsets;
-		U8 m_dynOffsetCount;
-		VkPipelineBindPoint m_bindPoint;
-		VkDescriptorSet m_dset;
-	};
-	Array<DeferredDsetBinding, MAX_DESCRIPTOR_SETS> m_deferredDsetBindings;
-	U8 m_deferredDsetBindingMask = 0;
-	VkPipelineLayout m_crntPplineLayout = VK_NULL_HANDLE;
-
 	/// Track texture usage.
 	class TextureUsageTracker
 	{
@@ -292,8 +297,6 @@ private:
 
 	void flushWriteQueryResults();
 
-	void flushDsetBindings();
-
 	void clearTextureInternal(TexturePtr tex, const ClearValue& clearValue, const VkImageSubresourceRange& range);
 
 	void setImageBarrier(VkPipelineStageFlags srcStage,

+ 26 - 62
src/anki/gr/vulkan/CommandBufferImpl.inl.h

@@ -15,40 +15,6 @@
 namespace anki
 {
 
-inline void CommandBufferImpl::setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
-{
-	ANKI_ASSERT(minx < maxx && miny < maxy);
-	commandCommon();
-
-	if(m_viewport[0] != minx || m_viewport[1] != miny || m_viewport[2] != maxx || m_viewport[3] != maxy)
-	{
-		VkViewport s;
-		s.x = minx;
-		s.y = miny;
-		s.width = maxx - minx;
-		s.height = maxy - miny;
-		s.minDepth = 0.0;
-		s.maxDepth = 1.0;
-		ANKI_CMD(vkCmdSetViewport(m_handle, 0, 1, &s), ANY_OTHER_COMMAND);
-
-		VkRect2D scissor = {};
-		scissor.extent.width = maxx - minx;
-		scissor.extent.height = maxy - miny;
-		scissor.offset.x = minx;
-		scissor.offset.y = miny;
-		ANKI_CMD(vkCmdSetScissor(m_handle, 0, 1, &scissor), ANY_OTHER_COMMAND);
-
-		m_viewport[0] = minx;
-		m_viewport[1] = miny;
-		m_viewport[2] = maxx;
-		m_viewport[3] = maxy;
-	}
-	else
-	{
-		// Skip
-	}
-}
-
 inline void CommandBufferImpl::setPolygonOffset(F32 factor, F32 units)
 {
 	commandCommon();
@@ -290,21 +256,26 @@ inline void CommandBufferImpl::setBufferBarrier(
 	m_bufferList.pushBack(m_alloc, buff);
 }
 
-inline void CommandBufferImpl::drawArrays(U32 count, U32 instanceCount, U32 first, U32 baseInstance)
+inline void CommandBufferImpl::drawArrays(
+	PrimitiveTopology topology, U32 count, U32 instanceCount, U32 first, U32 baseInstance)
 {
+	m_state.setPrimitiveTopology(topology);
 	drawcallCommon();
 	ANKI_CMD(vkCmdDraw(m_handle, count, instanceCount, first, baseInstance), ANY_OTHER_COMMAND);
 }
 
 inline void CommandBufferImpl::drawElements(
-	U32 count, U32 instanceCount, U32 firstIndex, U32 baseVertex, U32 baseInstance)
+	PrimitiveTopology topology, U32 count, U32 instanceCount, U32 firstIndex, U32 baseVertex, U32 baseInstance)
 {
+	m_state.setPrimitiveTopology(topology);
 	drawcallCommon();
 	ANKI_CMD(vkCmdDrawIndexed(m_handle, count, instanceCount, firstIndex, baseVertex, baseInstance), ANY_OTHER_COMMAND);
 }
 
-inline void CommandBufferImpl::drawArraysIndirect(U32 drawCount, PtrSize offset, BufferPtr buff)
+inline void CommandBufferImpl::drawArraysIndirect(
+	PrimitiveTopology topology, U32 drawCount, PtrSize offset, BufferPtr& buff)
 {
+	m_state.setPrimitiveTopology(topology);
 	drawcallCommon();
 	const BufferImpl& impl = *buff->m_impl;
 	ANKI_ASSERT(impl.usageValid(BufferUsageBit::INDIRECT));
@@ -315,8 +286,10 @@ inline void CommandBufferImpl::drawArraysIndirect(U32 drawCount, PtrSize offset,
 		ANY_OTHER_COMMAND);
 }
 
-inline void CommandBufferImpl::drawElementsIndirect(U32 drawCount, PtrSize offset, BufferPtr buff)
+inline void CommandBufferImpl::drawElementsIndirect(
+	PrimitiveTopology topology, U32 drawCount, PtrSize offset, BufferPtr& buff)
 {
+	m_state.setPrimitiveTopology(topology);
 	drawcallCommon();
 	const BufferImpl& impl = *buff->m_impl;
 	ANKI_ASSERT(impl.usageValid(BufferUsageBit::INDIRECT));
@@ -330,7 +303,6 @@ inline void CommandBufferImpl::drawElementsIndirect(U32 drawCount, PtrSize offse
 inline void CommandBufferImpl::dispatchCompute(U32 groupCountX, U32 groupCountY, U32 groupCountZ)
 {
 	commandCommon();
-	flushDsetBindings();
 	ANKI_CMD(vkCmdDispatch(m_handle, groupCountX, groupCountY, groupCountZ), ANY_OTHER_COMMAND);
 }
 
@@ -469,10 +441,19 @@ inline void CommandBufferImpl::drawcallCommon()
 		beginRenderPassInternal();
 	}
 
-	flushDsetBindings();
-
 	++m_rpCommandCount;
 
+	// Get or create ppline
+	ANKI_ASSERT(m_graphicsProg);
+	Pipeline ppline;
+	Bool stateDirty;
+	m_graphicsProg->getPipelineFactory().newPipeline(m_state, ppline, stateDirty);
+
+	if(stateDirty)
+	{
+		ANKI_CMD(vkCmdBindPipeline(m_handle, VK_PIPELINE_BIND_POINT_GRAPHICS, ppline.getHandle()), ANY_OTHER_COMMAND);
+	}
+
 	ANKI_TRACE_INC_COUNTER(GR_DRAWCALLS, 1);
 }
 
@@ -574,28 +555,11 @@ inline void CommandBufferImpl::writeOcclusionQueryResultToBuffer(
 	m_bufferList.pushBack(m_alloc, buff);
 }
 
-inline void CommandBufferImpl::flushDsetBindings()
+inline void CommandBufferImpl::bindShaderProgram(ShaderProgramPtr& prog)
 {
-	for(U i = 0; i < MAX_DESCRIPTOR_SETS; ++i)
-	{
-		if(m_deferredDsetBindingMask & (1 << i))
-		{
-			ANKI_ASSERT(m_crntPplineLayout);
-			const DeferredDsetBinding& binding = m_deferredDsetBindings[i];
-
-			ANKI_CMD(vkCmdBindDescriptorSets(m_handle,
-						 binding.m_bindPoint,
-						 m_crntPplineLayout,
-						 i,
-						 1,
-						 &binding.m_dset,
-						 binding.m_dynOffsetCount,
-						 &binding.m_dynOffsets[0]),
-				ANY_OTHER_COMMAND);
-		}
-	}
-
-	m_deferredDsetBindingMask = 0;
+	m_graphicsProg = prog->m_impl.get();
+	m_state.bindShaderProgram(prog);
+	m_progs.pushBack(m_alloc, prog);
 }
 
 } // end namespace anki

+ 1 - 1
src/anki/gr/vulkan/Common.cpp

@@ -436,7 +436,7 @@ VkBlendFactor convertBlendFactor(BlendFactor ak)
 	return out;
 }
 
-VkBlendOp convertBlendFunc(BlendOperation ak)
+VkBlendOp convertBlendOperation(BlendOperation ak)
 {
 	VkBlendOp out = VK_BLEND_OP_MAX_ENUM;
 

+ 7 - 2
src/anki/gr/vulkan/Common.h

@@ -46,7 +46,7 @@ const U DESCRIPTOR_FRAME_BUFFERING = 60 * 5; ///< How many frames worth of descr
 		VkResult rez;                                                                                                  \
 		if((rez = (x)) < 0)                                                                                            \
 		{                                                                                                              \
-			ANKI_LOGF("Vulkan function failed (VkResult: %d): %s", rez, #x);                                           \
+			ANKI_VK_LOGF("Vulkan function failed (VkResult: %d): %s", rez, #x);                                        \
 		}                                                                                                              \
 	} while(0)
 
@@ -57,11 +57,16 @@ const U DESCRIPTOR_FRAME_BUFFERING = 60 * 5; ///< How many frames worth of descr
 		VkResult rez;                                                                                                  \
 		if((rez = (x)) < 0)                                                                                            \
 		{                                                                                                              \
-			ANKI_LOGE("Vulkan function failed (VkResult: %d): %s", rez, #x);                                           \
+			ANKI_VK_LOGE("Vulkan function failed (VkResult: %d): %s", rez, #x);                                        \
 			return ErrorCode::FUNCTION_FAILED;                                                                         \
 		}                                                                                                              \
 	} while(0)
 
+#define ANKI_VK_LOGI(...) ANKI_LOGI("GR/VK: " __VA_ARGS__)
+#define ANKI_VK_LOGE(...) ANKI_LOGE("GR/VK: " __VA_ARGS__)
+#define ANKI_VK_LOGW(...) ANKI_LOGW("GR/VK: " __VA_ARGS__)
+#define ANKI_VK_LOGF(...) ANKI_LOGF("GR/VK: " __VA_ARGS__)
+
 /// Convert compare op.
 ANKI_USE_RESULT VkCompareOp convertCompareOp(CompareOperation ak);
 

+ 46 - 0
src/anki/gr/vulkan/DescriptorSet.cpp

@@ -97,6 +97,25 @@ public:
 	ANKI_USE_RESULT Error getOrCreateThreadAllocator(ThreadId tid, DSThreadAllocator*& alloc);
 };
 
+DSThreadAllocator::~DSThreadAllocator()
+{
+	auto alloc = m_layoutEntry->m_factory->m_alloc;
+
+	while(!m_list.isEmpty())
+	{
+		DS* ds = &m_list.getFront();
+		m_list.popFront();
+
+		alloc.deleteInstance(ds);
+	}
+
+	for(VkDescriptorPool pool : m_pools)
+	{
+		vkDestroyDescriptorPool(m_layoutEntry->m_factory->m_dev, pool, nullptr);
+	}
+	m_pools.destroy(alloc);
+}
+
 Error DSThreadAllocator::init()
 {
 	ANKI_CHECK(createNewPool());
@@ -286,6 +305,23 @@ void DSThreadAllocator::writeSet(const Array<AnyBinding, MAX_BINDINGS_PER_DESCRI
 	vkUpdateDescriptorSets(m_layoutEntry->m_factory->m_dev, writeCount, &writes[0], 0, nullptr);
 }
 
+DSLayoutCacheEntry::~DSLayoutCacheEntry()
+{
+	auto alloc = m_factory->m_alloc;
+
+	for(DSThreadAllocator* a : m_threadAllocs)
+	{
+		alloc.deleteInstance(a);
+	}
+
+	m_threadAllocs.destroy(alloc);
+
+	if(m_layoutHandle)
+	{
+		vkDestroyDescriptorSetLayout(m_factory->m_dev, m_layoutHandle, nullptr);
+	}
+}
+
 Error DSLayoutCacheEntry::init(const DescriptorBinding* bindings, U bindingCount, U64 hash)
 {
 	ANKI_ASSERT(bindings);
@@ -426,6 +462,16 @@ void DescriptorSetFactory::init(const GrAllocator<U8>& alloc, VkDevice dev)
 	m_dev = dev;
 }
 
+void DescriptorSetFactory::destroy()
+{
+	for(DSLayoutCacheEntry* l : m_caches)
+	{
+		m_alloc.deleteInstance(l);
+	}
+
+	m_caches.destroy(m_alloc);
+}
+
 Error DescriptorSetFactory::newDescriptorSetLayout(const DescriptorSetLayoutInitInfo& init, DescriptorSetLayout& layout)
 {
 	// Compute the hash for the layout

+ 1 - 4
src/anki/gr/vulkan/DescriptorSet.h

@@ -196,10 +196,7 @@ public:
 
 	void init(const GrAllocator<U8>& alloc, VkDevice dev);
 
-	void destroy()
-	{
-		ANKI_ASSERT(!"TODO");
-	}
+	void destroy();
 
 	/// @note It's thread-safe.
 	ANKI_USE_RESULT Error newDescriptorSetLayout(const DescriptorSetLayoutInitInfo& init, DescriptorSetLayout& layout);

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

@@ -49,7 +49,7 @@ inline Bool Fence::done() const
 	}
 	else if(status != VK_NOT_READY)
 	{
-		ANKI_LOGF("vkGetFenceStatus() failed");
+		ANKI_VK_LOGF("vkGetFenceStatus() failed");
 	}
 
 	return false;

+ 1 - 1
src/anki/gr/vulkan/Framebuffer.cpp

@@ -23,7 +23,7 @@ void Framebuffer::init(const FramebufferInitInfo& init)
 	m_impl.reset(getAllocator().newInstance<FramebufferImpl>(&getManager()));
 	if(m_impl->init(init))
 	{
-		ANKI_LOGF("Cannot recover");
+		ANKI_VK_LOGF("Cannot recover");
 	}
 }
 

+ 22 - 4
src/anki/gr/vulkan/FramebufferImpl.cpp

@@ -13,7 +13,25 @@ namespace anki
 
 FramebufferImpl::~FramebufferImpl()
 {
-	// TODO
+	for(VkFramebuffer fb : m_fbs)
+	{
+		if(fb)
+		{
+			vkDestroyFramebuffer(getDevice(), fb, nullptr);
+		}
+	}
+
+	for(auto it : m_rpasses)
+	{
+		VkRenderPass rpass = it;
+		ANKI_ASSERT(rpass);
+		vkDestroyRenderPass(getDevice(), rpass, nullptr);
+	}
+
+	if(m_compatibleOrDefaultRpass)
+	{
+		vkDestroyRenderPass(getDevice(), m_compatibleOrDefaultRpass, nullptr);
+	}
 }
 
 Error FramebufferImpl::init(const FramebufferInitInfo& init)
@@ -22,7 +40,7 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 
 	// Create a renderpass.
 	initRpassCreateInfo(init);
-	ANKI_VK_CHECK(vkCreateRenderPass(getDevice(), &m_rpassCi, nullptr, &m_rpass));
+	ANKI_VK_CHECK(vkCreateRenderPass(getDevice(), &m_rpassCi, nullptr, &m_compatibleOrDefaultRpass));
 
 	// Create the FBs
 	ANKI_CHECK(initFbs(init));
@@ -70,7 +88,7 @@ Error FramebufferImpl::initFbs(const FramebufferInitInfo& init)
 
 	VkFramebufferCreateInfo ci = {};
 	ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
-	ci.renderPass = m_rpass;
+	ci.renderPass = m_compatibleOrDefaultRpass;
 	ci.attachmentCount = init.m_colorAttachmentCount + ((hasDepthStencil) ? 1 : 0);
 
 	ci.layers = 1;
@@ -291,7 +309,7 @@ VkRenderPass FramebufferImpl::getRenderPassHandle(
 	}
 	else
 	{
-		out = m_rpass;
+		out = m_compatibleOrDefaultRpass;
 	}
 
 	ANKI_ASSERT(out);

+ 3 - 3
src/anki/gr/vulkan/FramebufferImpl.h

@@ -34,8 +34,8 @@ public:
 	/// Good for pipeline creation.
 	VkRenderPass getCompatibleRenderPass() const
 	{
-		ANKI_ASSERT(m_rpass);
-		return m_rpass;
+		ANKI_ASSERT(m_compatibleOrDefaultRpass);
+		return m_compatibleOrDefaultRpass;
 	}
 
 	/// Use it for binding.
@@ -121,7 +121,7 @@ private:
 	VkSubpassDescription m_subpassDescr = {};
 
 	// VK objects
-	VkRenderPass m_rpass = {}; ///< Compatible renderpass or default FB's renderpass.
+	VkRenderPass m_compatibleOrDefaultRpass = {}; ///< Compatible renderpass or default FB's renderpass.
 	HashMap<U64, VkRenderPass> m_rpasses;
 	Mutex m_rpassesMtx;
 	Array<VkFramebuffer, MAX_FRAMES_IN_FLIGHT> m_fbs = {};

+ 1 - 1
src/anki/gr/vulkan/GpuMemoryManager.cpp

@@ -195,7 +195,7 @@ void GpuMemoryManager::init(VkPhysicalDevice pdev, VkDevice dev, GrAllocator<U8>
 	// Print some info
 	for(const ClassInf& c : CLASSES)
 	{
-		ANKI_LOGI("VK: GPU mem class. Chunk size: %u, slotSize: %u, allocsPerChunk %u",
+		ANKI_VK_LOGI("GPU mem class. Chunk size: %u, slotSize: %u, allocsPerChunk %u",
 			c.m_chunkSize,
 			c.m_slotSize,
 			c.m_chunkSize / c.m_slotSize);

+ 40 - 34
src/anki/gr/vulkan/GrManagerImpl.cpp

@@ -16,8 +16,6 @@
 namespace anki
 {
 
-#define ANKI_GR_MANAGER_DEBUG_MEMMORY ANKI_DEBUG
-
 GrManagerImpl::~GrManagerImpl()
 {
 	// FIRST THING: wait for the GPU
@@ -60,6 +58,9 @@ GrManagerImpl::~GrManagerImpl()
 	m_semaphores.destroy(); // Destroy before fences
 	m_fences.destroy();
 
+	m_pplineLayoutFactory.destroy();
+	m_descrFactory.destroy();
+
 	if(m_swapchain)
 	{
 		vkDestroySwapchainKHR(m_device, m_swapchain, nullptr);
@@ -82,11 +83,14 @@ GrManagerImpl::~GrManagerImpl()
 
 	if(m_instance)
 	{
-		vkDestroyInstance(m_instance, nullptr);
-	}
+#if ANKI_GR_MANAGER_DEBUG_MEMMORY
+		VkAllocationCallbacks* pallocCbs = &m_debugAllocCbs;
+#else
+		VkAllocationCallbacks* pallocCbs = nullptr;
+#endif
 
-	m_descrFactory.destroy();
-	m_pplineLayoutFactory.destroy();
+		vkDestroyInstance(m_instance, pallocCbs);
+	}
 }
 
 GrAllocator<U8> GrManagerImpl::getAllocator() const
@@ -99,7 +103,7 @@ Error GrManagerImpl::init(const GrManagerInitInfo& init)
 	Error err = initInternal(init);
 	if(err)
 	{
-		ANKI_LOGE("Vulkan initialization failed");
+		ANKI_VK_LOGE("Vulkan initialization failed");
 		return ErrorCode::FUNCTION_FAILED;
 	}
 
@@ -108,7 +112,7 @@ Error GrManagerImpl::init(const GrManagerInitInfo& init)
 
 Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 {
-	ANKI_LOGI("Initializing Vulkan backend");
+	ANKI_VK_LOGI("Initializing Vulkan backend");
 	ANKI_CHECK(initInstance(init));
 	ANKI_CHECK(initSurface(init));
 	ANKI_CHECK(initDevice(init));
@@ -149,13 +153,13 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 
 		if(res == VK_ERROR_FORMAT_NOT_SUPPORTED)
 		{
-			ANKI_LOGI("R8G8B8 Images are not supported. Will workaround this");
+			ANKI_VK_LOGI("R8G8B8 Images are not supported. Will workaround this");
 			m_r8g8b8ImagesSupported = false;
 		}
 		else
 		{
 			ANKI_ASSERT(res == VK_SUCCESS);
-			ANKI_LOGI("R8G8B8 Images are supported");
+			ANKI_VK_LOGI("R8G8B8 Images are supported");
 			m_r8g8b8ImagesSupported = true;
 		}
 	}
@@ -173,13 +177,13 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 
 		if(res == VK_ERROR_FORMAT_NOT_SUPPORTED)
 		{
-			ANKI_LOGI("S8 Images are not supported. Will workaround this");
+			ANKI_VK_LOGI("S8 Images are not supported. Will workaround this");
 			m_s8ImagesSupported = false;
 		}
 		else
 		{
 			ANKI_ASSERT(res == VK_SUCCESS);
-			ANKI_LOGI("S8 Images are supported");
+			ANKI_VK_LOGI("S8 Images are supported");
 			m_s8ImagesSupported = true;
 		}
 	}
@@ -197,19 +201,20 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 
 		if(res == VK_ERROR_FORMAT_NOT_SUPPORTED)
 		{
-			ANKI_LOGI("D24S8 Images are not supported. Will workaround this");
+			ANKI_VK_LOGI("D24S8 Images are not supported. Will workaround this");
 			m_d24S8ImagesSupported = false;
 		}
 		else
 		{
 			ANKI_ASSERT(res == VK_SUCCESS);
-			ANKI_LOGI("D24S8 Images are supported");
+			ANKI_VK_LOGI("D24S8 Images are supported");
 			m_d24S8ImagesSupported = true;
 		}
 	}
 
 	m_descrGen.init(getAllocator());
 	m_descrFactory.init(getAllocator(), m_device);
+	m_pplineLayoutFactory.init(getAllocator(), m_device);
 
 	return ErrorCode::NONE;
 }
@@ -251,7 +256,7 @@ Error GrManagerImpl::initInstance(const GrManagerInitInfo& init)
 
 	if(init.m_config->getNumber("debugContext"))
 	{
-		ANKI_LOGI("VK: Will enable debug layers");
+		ANKI_VK_LOGI("Will enable debug layers");
 		ci.enabledLayerCount = LAYERS.getSize();
 		ci.ppEnabledLayerNames = &LAYERS[0];
 	}
@@ -260,12 +265,13 @@ Error GrManagerImpl::initInstance(const GrManagerInitInfo& init)
 	ci.ppEnabledExtensionNames = &EXTENSIONS[0];
 
 #if ANKI_GR_MANAGER_DEBUG_MEMMORY
-	VkAllocationCallbacks allocCbs = {};
-	VkAllocationCallbacks* pallocCbs = &allocCbs;
-	allocCbs.pUserData = this;
-	allocCbs.pfnAllocation = allocateCallback;
-	allocCbs.pfnReallocation = reallocateCallback;
-	allocCbs.pfnFree = freeCallback;
+	m_debugAllocCbs = {};
+	m_debugAllocCbs.pUserData = this;
+	m_debugAllocCbs.pfnAllocation = allocateCallback;
+	m_debugAllocCbs.pfnReallocation = reallocateCallback;
+	m_debugAllocCbs.pfnFree = freeCallback;
+
+	VkAllocationCallbacks* pallocCbs = &m_debugAllocCbs;
 #else
 	VkAllocationCallbacks* pallocCbs = nullptr;
 #endif
@@ -276,10 +282,10 @@ Error GrManagerImpl::initInstance(const GrManagerInitInfo& init)
 	//
 	uint32_t count = 0;
 	ANKI_VK_CHECK(vkEnumeratePhysicalDevices(m_instance, &count, nullptr));
-	ANKI_LOGI("VK: Number of physical devices: %u", count);
+	ANKI_VK_LOGI("Number of physical devices: %u", count);
 	if(count < 1)
 	{
-		ANKI_LOGE("Wrong number of physical devices");
+		ANKI_VK_LOGE("Wrong number of physical devices");
 		return ErrorCode::FUNCTION_FAILED;
 	}
 
@@ -302,7 +308,7 @@ Error GrManagerImpl::initInstance(const GrManagerInitInfo& init)
 		m_vendor = GpuVendor::AMD;
 		break;
 	}
-	ANKI_LOGI("GPU vendor is %s", &GPU_VENDOR_STR[m_vendor][0]);
+	ANKI_VK_LOGI("GPU vendor is %s", &GPU_VENDOR_STR[m_vendor][0]);
 
 	vkGetPhysicalDeviceFeatures(m_physicalDevice, &m_devFeatures);
 
@@ -313,7 +319,7 @@ Error GrManagerImpl::initDevice(const GrManagerInitInfo& init)
 {
 	uint32_t count = 0;
 	vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &count, nullptr);
-	ANKI_LOGI("VK: Number of queue families: %u", count);
+	ANKI_VK_LOGI("Number of queue families: %u", count);
 
 	DynamicArrayAuto<VkQueueFamilyProperties> queueInfos(getAllocator());
 	queueInfos.create(count);
@@ -338,8 +344,8 @@ Error GrManagerImpl::initDevice(const GrManagerInitInfo& init)
 
 	if(desiredFamilyIdx == MAX_U32)
 	{
-		ANKI_LOGE("Couldn't find a queue family with graphics+compute+transfer+present."
-				  "The assumption was wrong. The code needs to be reworked");
+		ANKI_VK_LOGE("Couldn't find a queue family with graphics+compute+transfer+present."
+					 "The assumption was wrong. The code needs to be reworked");
 		return ErrorCode::FUNCTION_FAILED;
 	}
 
@@ -374,7 +380,7 @@ Error GrManagerImpl::initSwapchain(const GrManagerInitInfo& init)
 
 	if(surfaceProperties.currentExtent.width == MAX_U32 || surfaceProperties.currentExtent.height == MAX_U32)
 	{
-		ANKI_LOGE("Wrong surface size");
+		ANKI_VK_LOGE("Wrong surface size");
 		return ErrorCode::FUNCTION_FAILED;
 	}
 	m_surfaceWidth = surfaceProperties.currentExtent.width;
@@ -400,7 +406,7 @@ Error GrManagerImpl::initSwapchain(const GrManagerInitInfo& init)
 
 	if(m_surfaceFormat == VK_FORMAT_UNDEFINED)
 	{
-		ANKI_LOGE("Surface format not found");
+		ANKI_VK_LOGE("Surface format not found");
 		return ErrorCode::FUNCTION_FAILED;
 	}
 
@@ -435,7 +441,7 @@ Error GrManagerImpl::initSwapchain(const GrManagerInitInfo& init)
 
 	if(presentMode == VK_PRESENT_MODE_MAX_ENUM_KHR)
 	{
-		ANKI_LOGE("VK: Couldn't find a present mode");
+		ANKI_VK_LOGE("Couldn't find a present mode");
 		return ErrorCode::FUNCTION_FAILED;
 	}
 
@@ -465,11 +471,11 @@ Error GrManagerImpl::initSwapchain(const GrManagerInitInfo& init)
 	ANKI_VK_CHECK(vkGetSwapchainImagesKHR(m_device, m_swapchain, &count, nullptr));
 	if(count != MAX_FRAMES_IN_FLIGHT)
 	{
-		ANKI_LOGE("Requested a swapchain with %u images but got one with %u", MAX_FRAMES_IN_FLIGHT, count);
+		ANKI_VK_LOGE("Requested a swapchain with %u images but got one with %u", MAX_FRAMES_IN_FLIGHT, count);
 		return ErrorCode::FUNCTION_FAILED;
 	}
 
-	ANKI_LOGI("VK: Created a swapchain. Image count: %u, present mode: %u", count, presentMode);
+	ANKI_VK_LOGI("Created a swapchain. Image count: %u, present mode: %u", count, presentMode);
 
 	Array<VkImage, MAX_FRAMES_IN_FLIGHT> images;
 	ANKI_VK_CHECK(vkGetSwapchainImagesKHR(m_device, m_swapchain, &count, &images[0]));
@@ -572,7 +578,7 @@ void GrManagerImpl::endFrame()
 
 	if(!frame.m_renderSemaphore)
 	{
-		ANKI_LOGW("Nobody draw to the default framebuffer");
+		ANKI_VK_LOGW("Nobody draw to the default framebuffer");
 	}
 
 	// Present
@@ -634,7 +640,7 @@ VkCommandBuffer GrManagerImpl::newCommandBuffer(ThreadId tid, CommandBufferFlag
 		Error err = thread.m_cmdbs.init(getAllocator(), m_device, m_queueIdx);
 		if(err)
 		{
-			ANKI_LOGF("Cannot recover");
+			ANKI_VK_LOGF("Cannot recover");
 		}
 	}
 

+ 6 - 0
src/anki/gr/vulkan/GrManagerImpl.h

@@ -19,6 +19,8 @@
 namespace anki
 {
 
+#define ANKI_GR_MANAGER_DEBUG_MEMMORY ANKI_DEBUG
+
 // Forward
 class TextureFallbackUploader;
 class ConfigSet;
@@ -192,6 +194,10 @@ private:
 
 	U64 m_frame = 0;
 
+#if ANKI_GR_MANAGER_DEBUG_MEMMORY
+	VkAllocationCallbacks m_debugAllocCbs;
+#endif
+
 	VkInstance m_instance = VK_NULL_HANDLE;
 	VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
 	GpuVendor m_vendor = GpuVendor::UNKNOWN;

+ 1 - 1
src/anki/gr/vulkan/GrManagerImplSdl.cpp

@@ -25,7 +25,7 @@ Error GrManagerImpl::initSurface(const GrManagerInitInfo& init)
 	SDL_VERSION(&wminfo.version);
 	if(!SDL_GetWindowWMInfo(init.m_window->getNative().m_window, &wminfo))
 	{
-		ANKI_LOGE("SDL_GetWindowWMInfo() failed");
+		ANKI_VK_LOGE("SDL_GetWindowWMInfo() failed");
 		return ErrorCode::NONE;
 	}
 

+ 1 - 1
src/anki/gr/vulkan/OcclusionQuery.cpp

@@ -24,7 +24,7 @@ void OcclusionQuery::init()
 
 	if(m_impl->init())
 	{
-		ANKI_LOGF("Cannot recover");
+		ANKI_VK_LOGF("Cannot recover");
 	}
 }
 

+ 39 - 27
src/anki/gr/vulkan/Pipeline.cpp

@@ -190,41 +190,37 @@ const VkGraphicsPipelineCreateInfo& PipelineStateTracker::updatePipelineCreateIn
 	ci.pStages = m_state.m_prog->m_impl->getShaderCreateInfos(ci.stageCount);
 
 	// Vert
-	if(!!m_shaderAttributeMask)
+	VkPipelineVertexInputStateCreateInfo& vertCi = m_ci.m_vert;
+	vertCi = {};
+	vertCi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+
+	for(U i = 0; i < MAX_VERTEX_ATTRIBUTES; ++i)
 	{
-		VkPipelineVertexInputStateCreateInfo& vertCi = m_ci.m_vert;
-		vertCi = {};
-		vertCi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+		BitSet<MAX_VERTEX_ATTRIBUTES, U8> bindingSet = {false};
 
-		for(U i = 0; i < MAX_VERTEX_ATTRIBUTES; ++i)
+		if(m_shaderAttributeMask.get(i))
 		{
-			BitSet<MAX_VERTEX_ATTRIBUTES, U8> bindingSet = {false};
+			VkVertexInputAttributeDescription& attrib = m_ci.m_attribs[vertCi.vertexAttributeDescriptionCount++];
+			attrib.binding = m_state.m_vertex.m_attributes[i].m_binding;
+			attrib.format = convertFormat(m_state.m_vertex.m_attributes[i].m_format);
+			attrib.location = i;
+			attrib.offset = m_state.m_vertex.m_attributes[i].m_offset;
 
-			if(m_shaderAttributeMask.get(i))
+			if(!bindingSet.get(i))
 			{
-				VkVertexInputAttributeDescription& attrib = m_ci.m_attribs[vertCi.vertexAttributeDescriptionCount++];
-				attrib.binding = m_state.m_vertex.m_attributes[i].m_binding;
-				attrib.format = convertFormat(m_state.m_vertex.m_attributes[i].m_format);
-				attrib.location = i;
-				attrib.offset = m_state.m_vertex.m_attributes[i].m_offset;
+				bindingSet.set(i);
 
-				if(!bindingSet.get(i))
-				{
-					bindingSet.set(i);
-
-					VkVertexInputBindingDescription& binding =
-						m_ci.m_vertBindings[vertCi.vertexBindingDescriptionCount++];
+				VkVertexInputBindingDescription& binding = m_ci.m_vertBindings[vertCi.vertexBindingDescriptionCount++];
 
-					binding.binding = attrib.binding;
-					binding.inputRate = convertVertexStepRate(m_state.m_vertex.m_bindings[attrib.binding].m_stepRate);
-					binding.stride = m_state.m_vertex.m_bindings[attrib.binding].m_stride;
-				}
+				binding.binding = attrib.binding;
+				binding.inputRate = convertVertexStepRate(m_state.m_vertex.m_bindings[attrib.binding].m_stepRate);
+				binding.stride = m_state.m_vertex.m_bindings[attrib.binding].m_stride;
 			}
 		}
-
-		ci.pVertexInputState = &vertCi;
 	}
 
+	ci.pVertexInputState = &vertCi;
+
 	// IA
 	VkPipelineInputAssemblyStateCreateInfo& iaCi = m_ci.m_ia;
 	iaCi = {};
@@ -233,6 +229,14 @@ const VkGraphicsPipelineCreateInfo& PipelineStateTracker::updatePipelineCreateIn
 	iaCi.topology = convertTopology(m_state.m_inputAssembler.m_topology);
 	ci.pInputAssemblyState = &iaCi;
 
+	// Viewport
+	VkPipelineViewportStateCreateInfo& vpCi = m_ci.m_vp;
+	vpCi = {};
+	vpCi.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+	vpCi.scissorCount = 1;
+	vpCi.viewportCount = 1;
+	ci.pViewportState = &vpCi;
+
 	// Raster
 	VkPipelineRasterizationStateCreateInfo& rastCi = m_ci.m_rast;
 	rastCi = {};
@@ -331,6 +335,8 @@ const VkGraphicsPipelineCreateInfo& PipelineStateTracker::updatePipelineCreateIn
 
 	// Dyn state
 	VkPipelineDynamicStateCreateInfo& dynCi = m_ci.m_dyn;
+	dynCi = {};
+	dynCi.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
 
 	// Almost all state is dynamic. Depth bias is static
 	static const Array<VkDynamicState, 7> DYN = {{VK_DYNAMIC_STATE_VIEWPORT,
@@ -381,10 +387,16 @@ void PipelineFactory::destroy()
 	m_pplines.destroy(m_alloc);
 }
 
-void PipelineFactory::newPipeline(PipelineStateTracker& state, Pipeline& ppline)
+void PipelineFactory::newPipeline(PipelineStateTracker& state, Pipeline& ppline, Bool& stateDirty)
 {
 	U64 hash;
-	state.flush(hash);
+	state.flush(hash, stateDirty);
+
+	if(ANKI_UNLIKELY(!stateDirty))
+	{
+		ppline.m_handle = VK_NULL_HANDLE;
+		return;
+	}
 
 	LockGuard<Mutex> lock(m_pplinesMtx);
 
@@ -395,7 +407,7 @@ void PipelineFactory::newPipeline(PipelineStateTracker& state, Pipeline& ppline)
 	}
 	else
 	{
-		PipelineFactory::PipelineInternal pp;
+		PipelineInternal pp;
 		const VkGraphicsPipelineCreateInfo& ci = state.updatePipelineCreateInfo();
 
 		ANKI_VK_CHECKF(vkCreateGraphicsPipelines(m_dev, m_pplineCache, 1, &ci, nullptr, &pp.m_handle));

+ 17 - 3
src/anki/gr/vulkan/Pipeline.h

@@ -333,6 +333,7 @@ public:
 			const ShaderProgramImpl& impl = *prog->m_impl;
 			m_shaderColorAttachmentWritemask = impl.getReflectionInfo().m_colorAttachmentWritemask;
 			m_shaderAttributeMask = impl.getReflectionInfo().m_attributeMask;
+			m_state.m_prog = prog;
 			m_dirty.m_prog = true;
 		}
 	}
@@ -363,13 +364,24 @@ public:
 	}
 
 	/// Flush state
-	void flush(U64& pipelineHash)
+	void flush(U64& pipelineHash, Bool& stateDirty)
 	{
 		Bool dirtyHashes = updateHashes();
 		if(dirtyHashes)
 		{
 			updateSuperHash();
 		}
+
+		if(m_hashes.m_superHash != m_hashes.m_lastSuperHash)
+		{
+			m_hashes.m_lastSuperHash = m_hashes.m_superHash;
+			stateDirty = true;
+		}
+		else
+		{
+			stateDirty = false;
+		}
+
 		pipelineHash = m_hashes.m_superHash;
 		ANKI_ASSERT(pipelineHash);
 	}
@@ -393,6 +405,7 @@ private:
 		Array<U64, MAX_COLOR_ATTACHMENTS> m_colAttachments = {};
 
 		U64 m_superHash = 0;
+		U64 m_lastSuperHash = 0;
 	} m_hashes;
 
 	class DirtyBits
@@ -435,6 +448,7 @@ private:
 		Array<VkVertexInputAttributeDescription, MAX_VERTEX_ATTRIBUTES> m_attribs;
 		VkPipelineVertexInputStateCreateInfo m_vert;
 		VkPipelineInputAssemblyStateCreateInfo m_ia;
+		VkPipelineViewportStateCreateInfo m_vp;
 		VkPipelineTessellationStateCreateInfo m_tess;
 		VkPipelineRasterizationStateCreateInfo m_rast;
 		VkPipelineMultisampleStateCreateInfo m_ms;
@@ -462,7 +476,7 @@ public:
 	}
 
 private:
-	VkPipeline m_handle = VK_NULL_HANDLE;
+	VkPipeline m_handle ANKI_DBG_NULLIFY;
 };
 
 /// Given some state it creates/hashes pipelines.
@@ -482,7 +496,7 @@ public:
 	void destroy();
 
 	/// @note Thread-safe.
-	void newPipeline(PipelineStateTracker& state, Pipeline& ppline);
+	void newPipeline(PipelineStateTracker& state, Pipeline& ppline, Bool& stateDirty);
 
 private:
 	class PipelineInternal;

+ 7 - 17
src/anki/gr/vulkan/PipelineLayout.cpp

@@ -8,32 +8,22 @@
 namespace anki
 {
 
-class PipelineLayoutFactory::Layout
+class PipelineLayoutFactory::Layout : public IntrusiveHashMapEnabled<PipelineLayoutFactory::Layout>
 {
 public:
-	U64 m_hash;
 	VkPipelineLayout m_handle;
 };
 
-class PipelineLayoutFactory::Hasher
-{
-public:
-	U64 operator()(U64 k) const
-	{
-		return k;
-	}
-};
-
 void PipelineLayoutFactory::destroy()
 {
-	for(auto it : m_layouts)
+	while(!m_layouts.isEmpty())
 	{
-		Layout* l = it;
+		Layout* l = &(*m_layouts.getBegin());
+		m_layouts.erase(l);
+
 		vkDestroyPipelineLayout(m_dev, l->m_handle, nullptr);
 		m_alloc.deleteInstance(l);
 	}
-
-	m_layouts.destroy(m_alloc);
 }
 
 Error PipelineLayoutFactory::newPipelineLayout(
@@ -59,14 +49,13 @@ Error PipelineLayoutFactory::newPipelineLayout(
 	{
 		// Found it
 
-		layout.m_handle = (*it)->m_handle;
+		layout.m_handle = (*it).m_handle;
 	}
 	else
 	{
 		// Not found, create new
 
 		Layout* lay = m_alloc.newInstance<Layout>();
-		lay->m_hash = hash;
 
 		VkPipelineLayoutCreateInfo ci = {};
 		ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
@@ -74,6 +63,7 @@ Error PipelineLayoutFactory::newPipelineLayout(
 		ci.setLayoutCount = dsetLayoutCount;
 
 		ANKI_VK_CHECK(vkCreatePipelineLayout(m_dev, &ci, nullptr, &lay->m_handle));
+		m_layouts.pushBack(hash, lay);
 
 		layout.m_handle = lay->m_handle;
 	}

+ 1 - 2
src/anki/gr/vulkan/PipelineLayout.h

@@ -49,12 +49,11 @@ public:
 
 private:
 	class Layout;
-	class Hasher;
 
 	GrAllocator<U8> m_alloc;
 	VkDevice m_dev = VK_NULL_HANDLE;
 
-	HashMap<U64, Layout*, Hasher> m_layouts;
+	IntrusiveHashMap<U64, Layout> m_layouts;
 	Mutex m_layoutsMtx;
 };
 /// @}

+ 1 - 1
src/anki/gr/vulkan/QueryExtra.cpp

@@ -12,7 +12,7 @@ QueryAllocator::~QueryAllocator()
 {
 	if(!m_chunks.isEmpty())
 	{
-		ANKI_LOGW("Forgot the delete some queries");
+		ANKI_VK_LOGW("Forgot the delete some queries");
 	}
 }
 

+ 1 - 1
src/anki/gr/vulkan/Sampler.cpp

@@ -24,7 +24,7 @@ void Sampler::init(const SamplerInitInfo& init)
 
 	if(m_impl->init(init))
 	{
-		ANKI_LOGF("Cannot recover");
+		ANKI_VK_LOGF("Cannot recover");
 	}
 }
 

+ 1 - 1
src/anki/gr/vulkan/Shader.cpp

@@ -26,7 +26,7 @@ void Shader::init(ShaderType shaderType, const CString& source)
 
 	if(m_impl->init(shaderType, source))
 	{
-		ANKI_LOGF("Cannot recover");
+		ANKI_VK_LOGF("Cannot recover");
 	}
 }
 

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

@@ -205,7 +205,7 @@ Error ShaderImpl::genSpirv(const CString& source, std::vector<unsigned int>& spi
 
 	if(!program.link(messages))
 	{
-		ANKI_LOGE("glslang failed to link a shader");
+		ANKI_VK_LOGE("glslang failed to link a shader");
 		return ErrorCode::USER_DATA;
 	}
 

+ 39 - 3
src/anki/gr/vulkan/ShaderProgram.cpp

@@ -5,6 +5,7 @@
 
 #include <anki/gr/ShaderProgram.h>
 #include <anki/gr/vulkan/ShaderProgramImpl.h>
+#include <anki/gr/Shader.h>
 
 namespace anki
 {
@@ -20,17 +21,52 @@ ShaderProgram::~ShaderProgram()
 
 void ShaderProgram::init(ShaderPtr vert, ShaderPtr frag)
 {
-	ANKI_ASSERT(!"TODO");
+	ANKI_ASSERT(vert && frag);
+
+	Array<ShaderPtr, U(ShaderType::COUNT)> shaders = {};
+	shaders[ShaderType::VERTEX] = vert;
+	shaders[ShaderType::FRAGMENT] = frag;
+
+	m_impl.reset(getAllocator().newInstance<ShaderProgramImpl>(&getManager()));
+
+	if(m_impl->init(shaders))
+	{
+		ANKI_VK_LOGF("Cannot recover");
+	}
 }
 
 void ShaderProgram::init(ShaderPtr comp)
 {
-	ANKI_ASSERT(!"TODO");
+	ANKI_ASSERT(comp);
+
+	Array<ShaderPtr, U(ShaderType::COUNT)> shaders = {};
+	shaders[ShaderType::COMPUTE] = comp;
+
+	m_impl.reset(getAllocator().newInstance<ShaderProgramImpl>(&getManager()));
+
+	if(m_impl->init(shaders))
+	{
+		ANKI_VK_LOGF("Cannot recover");
+	}
 }
 
 void ShaderProgram::init(ShaderPtr vert, ShaderPtr tessc, ShaderPtr tesse, ShaderPtr geom, ShaderPtr frag)
 {
-	ANKI_ASSERT(!"TODO");
+	ANKI_ASSERT(vert && frag);
+
+	Array<ShaderPtr, U(ShaderType::COUNT)> shaders = {};
+	shaders[ShaderType::VERTEX] = vert;
+	shaders[ShaderType::TESSELLATION_CONTROL] = tessc;
+	shaders[ShaderType::TESSELLATION_EVALUATION] = tesse;
+	shaders[ShaderType::GEOMETRY] = geom;
+	shaders[ShaderType::FRAGMENT] = frag;
+
+	m_impl.reset(getAllocator().newInstance<ShaderProgramImpl>(&getManager()));
+
+	if(m_impl->init(shaders))
+	{
+		ANKI_VK_LOGF("Cannot recover");
+	}
 }
 
 } // end namespace anki

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

@@ -67,7 +67,7 @@ private:
 	Array<VkPipelineShaderStageCreateInfo, U(ShaderType::COUNT) - 1> m_shaderCreateInfos;
 	U32 m_shaderCreateInfoCount = 0;
 
-	PipelineLayout m_pplineLayout;
+	PipelineLayout m_pplineLayout = {};
 	Array<DescriptorSetLayout, MAX_DESCRIPTOR_SETS> m_descriptorSetLayouts;
 
 	ShaderProgramReflectionInfo m_refl;

+ 1 - 1
src/anki/gr/vulkan/Texture.cpp

@@ -24,7 +24,7 @@ void Texture::init(const TextureInitInfo& init)
 
 	if(m_impl->init(init, this))
 	{
-		ANKI_LOGF("Cannot recover");
+		ANKI_VK_LOGF("Cannot recover");
 	}
 }
 

+ 1 - 1
src/anki/gr/vulkan/TextureImpl.cpp

@@ -240,7 +240,7 @@ Error TextureImpl::initImage(const TextureInitInfo& init_)
 
 	if(!supported)
 	{
-		ANKI_LOGE("Unsupported texture format: %u %u", U(init.m_format.m_components), U(init.m_format.m_transform));
+		ANKI_VK_LOGE("Unsupported texture format: %u %u", U(init.m_format.m_components), U(init.m_format.m_transform));
 		return ErrorCode::FUNCTION_FAILED;
 	}
 

+ 11 - 1
src/anki/misc/ConfigSet.cpp

@@ -163,7 +163,17 @@ Error ConfigSet::loadFromFile(CString filename)
 		}
 		else
 		{
-			ANKI_LOGW("Missing option for \"%s\". Will use the default value", &option.m_name[0]);
+			if(option.m_type == 0)
+			{
+				ANKI_LOGW("Missing option for \"%s\". Will use the default value: %s",
+					&option.m_name[0],
+					&option.m_strVal[0]);
+			}
+			else
+			{
+				ANKI_LOGW(
+					"Missing option for \"%s\". Will use the default value: %f", &option.m_name[0], option.m_fVal);
+			}
 		}
 	}
 

+ 11 - 3
src/anki/util/Logger.cpp

@@ -92,33 +92,41 @@ void Logger::defaultSystemMessageHandler(void*, const Info& info)
 #if ANKI_OS == ANKI_OS_LINUX
 	FILE* out = nullptr;
 	const char* terminalColor = nullptr;
+	const char* terminalColorBg = nullptr;
+
+#define ANKI_END_TERMINAL_FMT "\033[0m"
 
 	switch(info.m_type)
 	{
 	case Logger::MessageType::NORMAL:
 		out = stdout;
 		terminalColor = "\033[0;32m";
+		terminalColorBg = "\033[1;42;37m";
 		break;
 	case Logger::MessageType::ERROR:
 		out = stderr;
 		terminalColor = "\033[0;31m";
+		terminalColorBg = "\033[1;41;37m";
 		break;
 	case Logger::MessageType::WARNING:
 		out = stderr;
-		terminalColor = "\033[0;33m";
+		terminalColor = "\033[2;33m";
+		terminalColorBg = "\033[1;43;37m";
 		break;
 	case Logger::MessageType::FATAL:
 		out = stderr;
 		terminalColor = "\033[0;31m";
+		terminalColorBg = "\033[1;41;37m";
 		break;
 	default:
 		ANKI_ASSERT(0);
 	}
 
 	fprintf(out,
-		"%s[%s] %s (%s:%d %s)\033[0m\n",
-		terminalColor,
+		"%s[%s]" ANKI_END_TERMINAL_FMT "%s %s (%s:%d %s)" ANKI_END_TERMINAL_FMT "\n",
+		terminalColorBg,
 		MSG_TEXT[static_cast<U>(info.m_type)],
+		terminalColor,
 		info.m_msg,
 		info.m_file,
 		info.m_line,

+ 6 - 0
tests/framework/Framework.h

@@ -7,6 +7,7 @@
 
 #include <anki/util/Singleton.h>
 #include <anki/Math.h>
+#include <anki/util/Logger.h>
 #include <stdexcept>
 #include <vector>
 #include <string>
@@ -20,6 +21,11 @@ class TestSuite;
 class Test;
 class Tester;
 
+#define ANKI_TEST_LOGI(...) ANKI_LOGI("TESTS: " __VA_ARGS__)
+#define ANKI_TEST_LOGE(...) ANKI_LOGE("TESTS: " __VA_ARGS__)
+#define ANKI_TEST_LOGW(...) ANKI_LOGW("TESTS: " __VA_ARGS__)
+#define ANKI_TEST_LOGF(...) ANKI_LOGF("TESTS: " __VA_ARGS__)
+
 /// The actual test
 using TestCallback = void (*)(Test&);
 

+ 1 - 0
tests/gr/Gr.cpp

@@ -443,6 +443,7 @@ ANKI_TEST(Gr, SimpleDrawcall)
 {
 	COMMON_BEGIN()
 
+	ANKI_TEST_LOGI("Expect to see a grey triangle");
 	ShaderProgramPtr prog = createProgram(VERT_SRC, FRAG_SRC, *gr);
 	FramebufferPtr fb = createDefaultFb(*gr);