瀏覽代碼

Vulkan: Having shader + pipeline almost working

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

+ 18 - 1
CMakeLists.txt

@@ -263,9 +263,26 @@ if(VULKAN)
 
 	ExternalProject_Get_Property(GLSLANG_PROJECT install_dir)
 	set(GLSLANG_INSTALL_DIR ${install_dir})
+	
 	add_library(GLSLANG_LIB STATIC IMPORTED)
 	set_property(TARGET GLSLANG_LIB PROPERTY IMPORTED_LOCATION ${GLSLANG_INSTALL_DIR}/lib/libglslang.a)
 	add_dependencies(GLSLANG_LIB GLSLANG_PROJECT)
+
+	add_library(SPIRV_LIB STATIC IMPORTED)
+	set_property(TARGET SPIRV_LIB PROPERTY IMPORTED_LOCATION ${GLSLANG_INSTALL_DIR}/lib/libSPIRV.a)
+	add_dependencies(SPIRV_LIB GLSLANG_PROJECT)
+
+	add_library(OSD_LIB STATIC IMPORTED)
+	set_property(TARGET OSD_LIB PROPERTY IMPORTED_LOCATION ${GLSLANG_INSTALL_DIR}/lib/libOSDependent.a)
+	add_dependencies(OSD_LIB GLSLANG_PROJECT)
+
+	add_library(GLC_LIB STATIC IMPORTED)
+	set_property(TARGET GLC_LIB PROPERTY IMPORTED_LOCATION ${GLSLANG_INSTALL_DIR}/lib/libOGLCompiler.a)
+	add_dependencies(GLC_LIB GLSLANG_PROJECT)
+
+	add_library(HLSL_LIB STATIC IMPORTED)
+	set_property(TARGET HLSL_LIB PROPERTY IMPORTED_LOCATION ${GLSLANG_INSTALL_DIR}/lib/libHLSL.a)
+	add_dependencies(HLSL_LIB GLSLANG_PROJECT)
 endif()
 
 ################################################################################
@@ -379,7 +396,7 @@ add_library(anki src/Dummy.cpp "${_SYS_SRC}")
 target_link_libraries(anki ${ANKI_LIBS} ankitinyxml2 ankilua ankiz ankinewton ${ANKI_GPERFTOOLS_LIBS} SDL2_LIB FREETYPE_LIB ${_SYS})
 
 if(VULKAN)
-	target_link_libraries(anki GLSLANG_LIB)
+#target_link_libraries(anki GLSLANG_LIB)
 endif()
 
 if(SDL)

+ 2 - 0
README.md

@@ -29,6 +29,8 @@ Prerequisites:
 
 - Cmake 2.8 and up
 - GCC 4.8 and up or Clang 3.5 and up
+- libx11-dev installed
+- [Optional] libxinerama-dev if you want proper multi-monitor support
 
 To build the release version:
 

+ 3 - 1
include/anki/gr/Shader.h

@@ -78,7 +78,9 @@ public:
 	}
 
 	/// Create shader.
-	void init(ShaderType shaderType, const void* source, PtrSize sourceSize);
+	/// @param shaderType The type of the shader.
+	/// @param source The GLSL code of the shader.
+	void init(ShaderType shaderType, const CString& source);
 
 private:
 	UniquePtr<ShaderImpl> m_impl;

+ 46 - 3
include/anki/gr/vulkan/GrManagerImpl.h

@@ -7,6 +7,8 @@
 
 #include <anki/gr/vulkan/Common.h>
 #include <anki/gr/vulkan/GpuMemoryAllocator.h>
+#include <anki/gr/vulkan/ObjectRecycler.h>
+#include <anki/util/HashMap.h>
 
 namespace anki
 {
@@ -49,6 +51,11 @@ public:
 		return m_globalPipelineLayout;
 	}
 
+	VkCommandBuffer newCommandBuffer(Thread::Id tid, Bool secondLevel);
+
+	void deleteCommandBuffer(
+		VkCommandBuffer cmdb, Bool secondLevel, Thread::Id tid);
+
 private:
 	GrManager* m_manager = nullptr;
 
@@ -74,7 +81,6 @@ private:
 
 		/// The semaphores that of those submits that render to the default FB.
 		DynamicArray<VkSemaphore> m_renderSemaphores;
-
 		U32 m_renderSemaphoresCount = 0;
 	};
 
@@ -101,14 +107,47 @@ private:
 	DynamicArray<GpuMemoryAllocator> m_gpuMemAllocs;
 	/// @}
 
+	/// @name Per_thread_cache
+	/// @{
+
+	class PerThreadHasher
+	{
+	public:
+		U64 operator()(const Thread::Id& b) const
+		{
+			return b;
+		}
+	};
+
+	class PerThreadCompare
+	{
+	public:
+		Bool operator()(const Thread::Id& a, const Thread::Id& b) const
+		{
+			return a == b;
+		}
+	};
+
+	/// Per thread cache.
+	class PerThread
+	{
+	public:
+		CommandBufferObjectRecycler m_cmdbs;
+	};
+
+	HashMap<Thread::Id, PerThread, PerThreadHasher, PerThreadCompare>
+		m_perThread;
+	SpinLock m_perThreadMtx;
+	/// @}
+
 	ANKI_USE_RESULT Error initInternal(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initInstance(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initSurface(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initDevice(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initSwapchain(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initFramebuffers(const GrManagerInitInfo& init);
-	void initGlobalDsetLayout();
-	void initGlobalPplineLayout();
+	ANKI_USE_RESULT Error initGlobalDsetLayout();
+	ANKI_USE_RESULT Error initGlobalPplineLayout();
 	void initMemory();
 
 	/// Find a suitable memory type.
@@ -128,6 +167,10 @@ private:
 		VkSystemAllocationScope allocationScope);
 
 	static void freeCallback(void* userData, void* ptr);
+
+	void resetFrame(PerFrame& frame);
+
+	PerThread& getPerThreadCache(Thread::Id tid);
 };
 /// @}
 

+ 59 - 0
include/anki/gr/vulkan/ObjectRecycler.h

@@ -0,0 +1,59 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/gr/vulkan/Common.h>
+
+namespace anki
+{
+
+/// @addtogroup vulkan
+/// @{
+
+class CommandBufferObjectRecycler
+{
+public:
+	CommandBufferObjectRecycler()
+	{
+	}
+
+	~CommandBufferObjectRecycler();
+
+	ANKI_USE_RESULT Error init(GenericMemoryPoolAllocator<U8> alloc,
+		VkDevice dev,
+		uint32_t queueFamily);
+
+	/// Request a new command buffer.
+	VkCommandBuffer newCommandBuffer(Bool secondLevel);
+
+	/// Free a command buffer.
+	void deleteCommandBuffer(VkCommandBuffer cmdb, Bool secondLevel);
+
+	void collectGarbage();
+
+	Bool isCreated() const
+	{
+		return m_dev != VK_NULL_HANDLE;
+	}
+
+private:
+	GenericMemoryPoolAllocator<U8> m_alloc;
+	VkDevice m_dev = VK_NULL_HANDLE;
+	VkCommandPool m_pool = VK_NULL_HANDLE;
+
+	class CmdbType
+	{
+	public:
+		DynamicArray<VkCommandBuffer> m_cmdbs; ///< A stack.
+		U32 m_count = 0;
+		Mutex m_mtx; ///< Lock because the allocations may happen anywhere.
+	};
+
+	Array<CmdbType, 2> m_types;
+};
+/// @}
+
+} // end namespace anki

+ 6 - 3
include/anki/gr/vulkan/PipelineImpl.h

@@ -35,12 +35,12 @@ public:
 
 	~PipelineImpl();
 
-	void init(const PipelineInitInfo& init);
+	ANKI_USE_RESULT Error init(const PipelineInitInfo& init);
 
 private:
-	void initGraphics(const PipelineInitInfo& init);
+	ANKI_USE_RESULT Error initGraphics(const PipelineInitInfo& init);
 
-	void initCompute(const PipelineInitInfo& init);
+	ANKI_USE_RESULT Error initCompute(const PipelineInitInfo& init);
 
 	void initShaders(
 		const PipelineInitInfo& init, VkGraphicsPipelineCreateInfo& ci);
@@ -73,6 +73,9 @@ private:
 
 	ANKI_USE_RESULT VkPipelineColorBlendStateCreateInfo* initColorState(
 		const ColorStateInfo& c, VkPipelineColorBlendStateCreateInfo& ci);
+
+	ANKI_USE_RESULT VkPipelineDynamicStateCreateInfo* initDynamicState(
+		VkPipelineDynamicStateCreateInfo& ci);
 };
 /// @}
 

+ 1 - 7
src/core/Config.cpp

@@ -90,13 +90,7 @@ Config::Config()
 	newOption("glmajor", 4);
 	newOption("glminor", 5);
 	newOption("fullscreenDesktopResolution", false);
-	newOption("debugContext",
-#if ANKI_DEBUG == 1
-		true
-#else
-		false
-#endif
-		);
+	newOption("debugContext", false);
 }
 
 //==============================================================================

+ 3 - 1
src/gr/CMakeLists.txt

@@ -2,8 +2,10 @@ file(GLOB ANKI_GR_SOURCES *.cpp common/*.cpp)
 
 if(GL)
 	set(GR_BACKEND "gl")
+	set(EXTRA_LIBS "")
 else()
 	set(GR_BACKEND "vulkan")
+	set(EXTRA_LIBS GLSLANG_LIB SPIRV_LIB OSD_LIB GLC_LIB HLSL_LIB)
 endif()
 
 file(GLOB ANKI_GR_BACKEND_SOURCES ${GR_BACKEND}/*.cpp)
@@ -16,4 +18,4 @@ else()
 endif()
 
 add_library(ankigr ${ANKI_GR_SOURCES} ${ANKI_GR_BACKEND_SOURCES})
-target_link_libraries(ankigr ankiutil ankicore)
+target_link_libraries(ankigr ankiutil ankicore ${EXTRA_LIBS})

+ 2 - 2
src/gr/gl/GrManagerImplSdl.cpp

@@ -40,8 +40,8 @@ public:
 		m_window = init.m_window->getNative().m_window;
 
 		ANKI_LOGI("Creating GL %u.%u context...",
-			init.m_config->getNumber("glmajor"),
-			init.m_config->getNumber("glminor"));
+			U(init.m_config->getNumber("glmajor")),
+			U(init.m_config->getNumber("glminor")));
 
 		if(init.m_config->getNumber("debugContext"))
 		{

+ 4 - 6
src/gr/gl/Shader.cpp

@@ -60,11 +60,9 @@ public:
 	}
 };
 
-void Shader::init(ShaderType shaderType, const void* source, PtrSize sourceSize)
+void Shader::init(ShaderType shaderType, const CString& source)
 {
-	ANKI_ASSERT(source);
-	ANKI_ASSERT(sourceSize
-		== CString(static_cast<const char*>(source)).getLength() + 1);
+	ANKI_ASSERT(!source.isEmpty());
 
 	m_impl.reset(getAllocator().newInstance<ShaderImpl>(&getManager()));
 
@@ -74,8 +72,8 @@ void Shader::init(ShaderType shaderType, const void* source, PtrSize sourceSize)
 	// Copy source to the command buffer
 	CommandBufferAllocator<char> alloc =
 		cmdb->getImplementation().getInternalAllocator();
-	char* src = alloc.allocate(sourceSize);
-	memcpy(src, source, sourceSize);
+	char* src = alloc.allocate(source.getLength() + 1);
+	memcpy(src, &source[0], source.getLength() + 1);
 
 	cmdb->getImplementation().pushBackNewCommand<ShaderCreateCommand>(
 		this, shaderType, src, alloc);

+ 145 - 46
src/gr/vulkan/GrManagerImpl.cpp

@@ -9,6 +9,7 @@
 #include <anki/util/HashMap.h>
 #include <anki/util/Hash.h>
 #include <anki/core/Config.h>
+#include <glslang/Public/ShaderLang.h>
 
 namespace anki
 {
@@ -75,6 +76,32 @@ public:
 //==============================================================================
 GrManagerImpl::~GrManagerImpl()
 {
+	if(m_renderPasses)
+	{
+		auto it = m_renderPasses->m_hashmap.getBegin();
+		auto end = m_renderPasses->m_hashmap.getEnd();
+		while(it != end)
+		{
+			VkRenderPass pass = (*it);
+			vkDestroyRenderPass(m_device, pass, nullptr);
+			++it;
+		}
+
+		m_renderPasses->m_hashmap.destroy(getAllocator());
+		getAllocator().deleteInstance(m_renderPasses);
+	}
+
+	if(m_globalPipelineLayout)
+	{
+		vkDestroyPipelineLayout(m_device, m_globalPipelineLayout, nullptr);
+	}
+
+	if(m_globalDescriptorSetLayout)
+	{
+		vkDestroyDescriptorSetLayout(
+			m_device, m_globalDescriptorSetLayout, nullptr);
+	}
+
 	for(auto& x : m_perFrame)
 	{
 		if(x.m_fb)
@@ -144,9 +171,14 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 	ANKI_CHECK(initSwapchain(init));
 	ANKI_CHECK(initFramebuffers(init));
 
-	/*initGlobalDsetLayout();
-	initGlobalPplineLayout();
-	initMemory();*/
+	ANKI_CHECK(initGlobalDsetLayout());
+	ANKI_CHECK(initGlobalPplineLayout());
+
+	m_renderPasses = getAllocator().newInstance<CompatibleRenderPassHashMap>();
+
+	// initMemory();
+
+	glslang::InitializeProcess();
 
 	return ErrorCode::NONE;
 }
@@ -310,7 +342,7 @@ Error GrManagerImpl::initSwapchain(const GrManagerInitInfo& init)
 	ANKI_VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(
 		m_physicalDevice, m_surface, &formatCount, &formats[0]));
 
-	VkColorSpaceKHR colorspace;
+	VkColorSpaceKHR colorspace = VK_COLOR_SPACE_MAX_ENUM_KHR;
 	while(--formatCount)
 	{
 		if(formats[formatCount].format == VK_FORMAT_B8G8R8A8_SRGB)
@@ -446,18 +478,17 @@ Error GrManagerImpl::initFramebuffers(const GrManagerInitInfo& init)
 }
 
 //==============================================================================
-void GrManagerImpl::initGlobalDsetLayout()
+Error GrManagerImpl::initGlobalDsetLayout()
 {
-	VkDescriptorSetLayoutCreateInfo ci;
+	VkDescriptorSetLayoutCreateInfo ci = {};
 	ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
-	ci.pNext = nullptr;
-	ci.flags = 0;
 
 	const U BINDING_COUNT = MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS
 		+ MAX_STORAGE_BUFFER_BINDINGS;
 	ci.bindingCount = BINDING_COUNT;
 
 	Array<VkDescriptorSetLayoutBinding, BINDING_COUNT> bindings;
+	memset(&bindings[0], 0, sizeof(bindings));
 	ci.pBindings = &bindings[0];
 
 	U count = 0;
@@ -470,7 +501,6 @@ void GrManagerImpl::initGlobalDsetLayout()
 		binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
 		binding.descriptorCount = 1;
 		binding.stageFlags = VK_SHADER_STAGE_ALL;
-		binding.pImmutableSamplers = nullptr;
 
 		++count;
 	}
@@ -483,7 +513,6 @@ void GrManagerImpl::initGlobalDsetLayout()
 		binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
 		binding.descriptorCount = 1;
 		binding.stageFlags = VK_SHADER_STAGE_ALL;
-		binding.pImmutableSamplers = nullptr;
 
 		++count;
 	}
@@ -496,19 +525,20 @@ void GrManagerImpl::initGlobalDsetLayout()
 		binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
 		binding.descriptorCount = 1;
 		binding.stageFlags = VK_SHADER_STAGE_ALL;
-		binding.pImmutableSamplers = nullptr;
 
 		++count;
 	}
 
 	ANKI_ASSERT(count == BINDING_COUNT);
 
-	ANKI_VK_CHECKF(vkCreateDescriptorSetLayout(
+	ANKI_VK_CHECK(vkCreateDescriptorSetLayout(
 		m_device, &ci, nullptr, &m_globalDescriptorSetLayout));
+
+	return ErrorCode::NONE;
 }
 
 //==============================================================================
-void GrManagerImpl::initGlobalPplineLayout()
+Error GrManagerImpl::initGlobalPplineLayout()
 {
 	Array<VkDescriptorSetLayout, MAX_RESOURCE_GROUPS> sets = {
 		{m_globalDescriptorSetLayout, m_globalDescriptorSetLayout}};
@@ -522,15 +552,18 @@ void GrManagerImpl::initGlobalPplineLayout()
 	ci.pushConstantRangeCount = 0;
 	ci.pPushConstantRanges = nullptr;
 
-	ANKI_VK_CHECKF(vkCreatePipelineLayout(
+	ANKI_VK_CHECK(vkCreatePipelineLayout(
 		m_device, &ci, nullptr, &m_globalPipelineLayout));
+
+	return ErrorCode::NONE;
 }
 
 //==============================================================================
 VkRenderPass GrManagerImpl::getOrCreateCompatibleRenderPass(
 	const PipelineInitInfo& init)
 {
-	VkRenderPass out;
+	VkRenderPass out = VK_NULL_HANDLE;
+
 	// Create the key
 	RenderPassKey key;
 	for(U i = 0; i < init.m_color.m_attachmentCount; ++i)
@@ -551,21 +584,20 @@ VkRenderPass GrManagerImpl::getOrCreateCompatibleRenderPass(
 	else
 	{
 		// Not found, create one
-		VkRenderPassCreateInfo ci;
-		ANKI_VK_MEMSET_DBG(ci);
+		VkRenderPassCreateInfo ci = {};
 		ci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
-		ci.pNext = nullptr;
-		ci.flags = 0;
 
 		Array<VkAttachmentDescription, MAX_COLOR_ATTACHMENTS + 1>
 			attachmentDescriptions;
+		memset(&attachmentDescriptions[0], 0, sizeof(attachmentDescriptions));
+
 		Array<VkAttachmentReference, MAX_COLOR_ATTACHMENTS> references;
+		memset(&references[0], 0, sizeof(references));
+
 		for(U i = 0; i < init.m_color.m_attachmentCount; ++i)
 		{
 			// We only care about samples and format
 			VkAttachmentDescription& desc = attachmentDescriptions[i];
-			ANKI_VK_MEMSET_DBG(desc);
-			desc.flags = 0;
 			desc.format = convertFormat(init.m_color.m_attachments[i].m_format);
 			desc.samples = VK_SAMPLE_COUNT_1_BIT;
 			desc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
@@ -588,8 +620,6 @@ VkRenderPass GrManagerImpl::getOrCreateCompatibleRenderPass(
 		{
 			VkAttachmentDescription& desc =
 				attachmentDescriptions[ci.attachmentCount];
-			ANKI_VK_MEMSET_DBG(desc);
-			desc.flags = 0;
 			desc.format = convertFormat(init.m_depthStencil.m_format);
 			desc.samples = VK_SAMPLE_COUNT_1_BIT;
 			desc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
@@ -605,27 +635,18 @@ VkRenderPass GrManagerImpl::getOrCreateCompatibleRenderPass(
 			++ci.attachmentCount;
 		}
 
-		VkSubpassDescription desc;
-		ANKI_VK_MEMSET_DBG(desc);
-		desc.flags = 0;
+		VkSubpassDescription desc = {};
 		desc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
-		desc.inputAttachmentCount = 0;
-		desc.pInputAttachments = nullptr;
 		desc.colorAttachmentCount = init.m_color.m_attachmentCount;
 		desc.pColorAttachments =
 			(init.m_color.m_attachmentCount) ? &references[0] : nullptr;
-		desc.pResolveAttachments = nullptr;
 		desc.pDepthStencilAttachment =
 			(hasDepthStencil) ? &dsReference : nullptr;
-		desc.preserveAttachmentCount = 0;
-		desc.pPreserveAttachments = nullptr;
 
 		ANKI_ASSERT(ci.attachmentCount);
 		ci.pAttachments = &attachmentDescriptions[0];
 		ci.subpassCount = 1;
 		ci.pSubpasses = &desc;
-		ci.dependencyCount = 0;
-		ci.pDependencies = nullptr;
 
 		VkRenderPass rpass;
 		ANKI_VK_CHECKF(vkCreateRenderPass(m_device, &ci, nullptr, &rpass));
@@ -750,35 +771,113 @@ void GrManagerImpl::endFrame()
 	// Wait for the fence of N-2 frame
 	U waitFrameIdx = (m_frame + 1) % MAX_FRAMES_IN_FLIGHT;
 	PerFrame& waitFrame = m_perFrame[waitFrameIdx];
-	VkFence& waitFence = waitFrame.m_presentFence;
-	if(waitFence)
+	if(waitFrame.m_presentFence)
 	{
 		// Wait
-		ANKI_VK_CHECKF(vkWaitForFences(m_device, 1, &waitFence, true, MAX_U64));
+		ANKI_VK_CHECKF(vkWaitForFences(
+			m_device, 1, &waitFrame.m_presentFence, true, MAX_U64));
+	}
+
+	resetFrame(waitFrame);
+
+	if(frame.m_renderSemaphoresCount == 0)
+	{
+		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.swapchainCount = 1;
+	present.pSwapchains = &m_swapchain;
+	present.pImageIndices = &imageIdx;
+	ANKI_VK_CHECKF(vkQueuePresentKHR(m_queue, &present));
+
+	// Finalize
+	++m_frame;
+}
+
+//==============================================================================
+void GrManagerImpl::resetFrame(PerFrame& frame)
+{
+	if(frame.m_presentFence)
+	{
 		// Recycle fence
-		ANKI_VK_CHECKF(vkResetFences(m_device, 1, &waitFence));
+		ANKI_VK_CHECKF(vkResetFences(m_device, 1, &frame.m_presentFence));
 	}
 
-	// Cleanup various objects from the wait frame
-	if(waitFrame.m_acquireSemaphore)
+	if(frame.m_acquireSemaphore)
 	{
 		vkDestroySemaphore(m_device, frame.m_acquireSemaphore, nullptr);
 	}
 
-	for(U i = 0; i < waitFrame.m_renderSemaphoresCount; ++i)
+	for(U i = 0; i < frame.m_renderSemaphoresCount; ++i)
 	{
-		ANKI_ASSERT(waitFrame.m_renderSemaphores[i]);
-		vkDestroySemaphore(m_device, waitFrame.m_renderSemaphores[i], nullptr);
-		waitFrame.m_renderSemaphores[i] = VK_NULL_HANDLE;
+		ANKI_ASSERT(frame.m_renderSemaphores[i]);
+		vkDestroySemaphore(m_device, frame.m_renderSemaphores[i], nullptr);
+		frame.m_renderSemaphores[i] = VK_NULL_HANDLE;
 	}
 
-	if(frame.m_renderSemaphoresCount == 0)
+	frame.m_renderSemaphoresCount = 0;
+}
+
+//==============================================================================
+GrManagerImpl::PerThread& GrManagerImpl::getPerThreadCache(Thread::Id tid)
+{
+	PerThread* thread = nullptr;
+	LockGuard<SpinLock> lock(m_perThreadMtx);
+
+	// Find or create a record
+	auto it = m_perThread.find(tid);
+	if(it != m_perThread.getEnd())
 	{
-		ANKI_LOGW("Nobody draw to the default framebuffer");
+		thread = &(*it);
+	}
+	else
+	{
+		m_perThread.emplaceBack(getAllocator(), tid);
+		it = m_perThread.find(tid);
+		thread = &(*it);
 	}
 
-	++m_frame;
+	return *thread;
+}
+
+//==============================================================================
+VkCommandBuffer GrManagerImpl::newCommandBuffer(
+	Thread::Id tid, Bool secondLevel)
+{
+	// Get the per thread cache
+	PerThread& thread = getPerThreadCache(tid);
+
+	// Try initialize the recycler
+	if(ANKI_UNLIKELY(!thread.m_cmdbs.isCreated()))
+	{
+		Error err = thread.m_cmdbs.init(getAllocator(), m_device, m_queueIdx);
+		if(err)
+		{
+			ANKI_LOGF("Cannot recover");
+		}
+	}
+
+	return thread.m_cmdbs.newCommandBuffer(secondLevel);
+}
+
+//==============================================================================
+void GrManagerImpl::deleteCommandBuffer(
+	VkCommandBuffer cmdb, Bool secondLevel, Thread::Id tid)
+{
+	// Get the per thread cache
+	PerThread& thread = getPerThreadCache(tid);
+
+	thread.m_cmdbs.deleteCommandBuffer(cmdb, secondLevel);
 }
 
 } // end namespace anki

+ 95 - 0
src/gr/vulkan/ObjectRecycler.cpp

@@ -0,0 +1,95 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/gr/vulkan/ObjectRecycler.h>
+
+namespace anki
+{
+
+//==============================================================================
+CommandBufferObjectRecycler::~CommandBufferObjectRecycler()
+{
+	if(m_pool)
+	{
+		vkDestroyCommandPool(m_dev, m_pool, nullptr);
+	}
+
+	// TODO the rest
+}
+
+//==============================================================================
+Error CommandBufferObjectRecycler::init(
+	GenericMemoryPoolAllocator<U8> alloc, VkDevice dev, uint32_t queueFamily)
+{
+	m_alloc = alloc;
+
+	VkCommandPoolCreateInfo ci = {};
+	ci.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+	ci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+	ci.queueFamilyIndex = queueFamily;
+
+	ANKI_VK_CHECK(vkCreateCommandPool(dev, &ci, nullptr, &m_pool));
+
+	m_dev = dev;
+	return ErrorCode::NONE;
+}
+
+//==============================================================================
+VkCommandBuffer CommandBufferObjectRecycler::newCommandBuffer(Bool secondLevel)
+{
+	ANKI_ASSERT(isCreated());
+
+	CmdbType& type = m_types[secondLevel];
+
+	LockGuard<Mutex> lock(type.m_mtx);
+
+	VkCommandBuffer out = VK_NULL_HANDLE;
+	if(type.m_count > 0)
+	{
+		// Recycle
+
+		--type.m_count;
+		out = type.m_cmdbs[type.m_count];
+	}
+	else
+	{
+		// Create a new one
+
+		VkCommandBufferAllocateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+		ci.commandPool = m_pool;
+		ci.level = (secondLevel) ? VK_COMMAND_BUFFER_LEVEL_SECONDARY
+								 : VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+		ci.commandBufferCount = 1;
+
+		ANKI_VK_CHECKF(vkAllocateCommandBuffers(m_dev, &ci, &out));
+	}
+
+	ANKI_ASSERT(out);
+	return out;
+}
+
+//==============================================================================
+void CommandBufferObjectRecycler::deleteCommandBuffer(
+	VkCommandBuffer cmdb, Bool secondLevel)
+{
+	ANKI_ASSERT(isCreated());
+	ANKI_ASSERT(cmdb);
+
+	CmdbType& type = m_types[secondLevel];
+
+	LockGuard<Mutex> lock(type.m_mtx);
+
+	if(type.m_cmdbs.getSize() <= type.m_count)
+	{
+		// Grow storage
+		type.m_cmdbs.resize(m_alloc, type.m_cmdbs.getSize() + 1);
+	}
+
+	type.m_cmdbs[type.m_count] = cmdb;
+	++type.m_count;
+}
+
+} // end namespace anki

+ 48 - 24
src/gr/vulkan/PipelineImpl.cpp

@@ -87,7 +87,7 @@ PipelineImpl::~PipelineImpl()
 }
 
 //==============================================================================
-void PipelineImpl::initGraphics(const PipelineInitInfo& init)
+Error PipelineImpl::initGraphics(const PipelineInitInfo& init)
 {
 	FilledGraphicsPipelineCreateInfo ci = FILLED;
 
@@ -105,38 +105,39 @@ void PipelineImpl::initGraphics(const PipelineInitInfo& init)
 	ci.pMultisampleState = initMsState(ci.m_ms);
 	ci.pDepthStencilState = initDsState(init.m_depthStencil, ci.m_ds);
 	ci.pColorBlendState = initColorState(init.m_color, ci.m_color);
-	ci.pDynamicState = nullptr; // No dynamic state as static at the moment
+	ci.pDynamicState = initDynamicState(ci.m_dyn);
 
 	// Finalize
 	ci.layout = getGrManagerImpl().getGlobalPipelineLayout();
 	ci.renderPass = getGrManagerImpl().getOrCreateCompatibleRenderPass(init);
 	ci.basePipelineHandle = VK_NULL_HANDLE;
 
-	ANKI_VK_CHECKF(vkCreateGraphicsPipelines(
+	ANKI_VK_CHECK(vkCreateGraphicsPipelines(
 		getDevice(), nullptr, 1, &ci, nullptr, &m_handle));
+
+	return ErrorCode::NONE;
 }
 
 //==============================================================================
-void PipelineImpl::initCompute(const PipelineInitInfo& init)
+Error PipelineImpl::initCompute(const PipelineInitInfo& init)
 {
-	VkComputePipelineCreateInfo ci;
+	VkComputePipelineCreateInfo ci = {};
 	ci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
-	ci.pNext = nullptr;
 	ci.layout = getGrManagerImpl().getGlobalPipelineLayout();
 	ci.basePipelineHandle = VK_NULL_HANDLE;
 
 	VkPipelineShaderStageCreateInfo& stage = ci.stage;
 	stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
-	stage.pNext = nullptr;
-	stage.flags = 0;
 	stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
 	stage.module =
 		init.m_shaders[ShaderType::COMPUTE]->getImplementation().m_handle;
 	stage.pName = "main";
 	stage.pSpecializationInfo = nullptr;
 
-	ANKI_VK_CHECKF(vkCreateComputePipelines(
+	ANKI_VK_CHECK(vkCreateComputePipelines(
 		getDevice(), nullptr, 1, &ci, nullptr, &m_handle));
+
+	return ErrorCode::NONE;
 }
 
 //==============================================================================
@@ -161,14 +162,10 @@ void PipelineImpl::initShaders(
 		VkPipelineShaderStageCreateInfo& stage =
 			const_cast<VkPipelineShaderStageCreateInfo&>(
 				ci.pStages[ci.stageCount]);
-		ANKI_VK_MEMSET_DBG(stage);
-		stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
-		stage.pNext = nullptr;
-		stage.flags = 0;
+
 		stage.stage = VkShaderStageFlagBits(1u << U(type));
 		stage.module = init.m_shaders[type]->getImplementation().m_handle;
 		stage.pName = "main";
-		stage.pSpecializationInfo = nullptr;
 
 		++ci.stageCount;
 	}
@@ -216,7 +213,7 @@ VkPipelineVertexInputStateCreateInfo* PipelineImpl::initVertexStage(
 			const_cast<VkVertexInputAttributeDescription&>(
 				ci.pVertexAttributeDescriptions[i]);
 
-		vkAttrib.location = 0;
+		vkAttrib.location = i;
 		vkAttrib.binding = vertex.m_attributes[i].m_binding;
 		vkAttrib.format = convertFormat(vertex.m_attributes[i].m_format);
 		vkAttrib.offset = vertex.m_attributes[i].m_offset;
@@ -248,8 +245,11 @@ VkPipelineTessellationStateCreateInfo* PipelineImpl::initTessellationState(
 VkPipelineViewportStateCreateInfo* PipelineImpl::initViewportState(
 	VkPipelineViewportStateCreateInfo& ci)
 {
-	// Viewport is dynamic
-	return nullptr;
+	// Viewport an scissor is dynamic
+	ci.viewportCount = 1;
+	ci.scissorCount = 1;
+
+	return &ci;
 }
 
 //==============================================================================
@@ -261,7 +261,8 @@ VkPipelineRasterizationStateCreateInfo* PipelineImpl::initRasterizerState(
 	ci.polygonMode = convertFillMode(r.m_fillMode);
 	ci.cullMode = convertCullMode(r.m_cullMode);
 	ci.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
-	ci.depthBiasEnable = VK_FALSE; // Dynamic
+	ci.depthBiasEnable = VK_FALSE; // TODO
+	ci.lineWidth = 1.0;
 
 	return &ci;
 }
@@ -270,15 +271,15 @@ VkPipelineRasterizationStateCreateInfo* PipelineImpl::initRasterizerState(
 VkPipelineMultisampleStateCreateInfo* PipelineImpl::initMsState(
 	VkPipelineMultisampleStateCreateInfo& ci)
 {
-	// No MS for now
-	return nullptr;
+	ci.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+	return &ci;
 }
 
 //==============================================================================
 VkPipelineDepthStencilStateCreateInfo* PipelineImpl::initDsState(
 	const DepthStencilStateInfo& ds, VkPipelineDepthStencilStateCreateInfo& ci)
 {
-	ci.depthTestEnable = ds.m_depthCompareFunction != CompareOperation::ALWAYS
+	ci.depthTestEnable = (ds.m_depthCompareFunction != CompareOperation::ALWAYS)
 		|| ds.m_depthWriteEnabled;
 	ci.depthWriteEnable = ds.m_depthWriteEnabled;
 	ci.depthCompareOp = convertCompareOp(ds.m_depthCompareFunction);
@@ -301,6 +302,7 @@ VkPipelineColorBlendStateCreateInfo* PipelineImpl::initColorState(
 			const_cast<VkPipelineColorBlendAttachmentState&>(
 				ci.pAttachments[i]);
 		const ColorAttachmentStateInfo& in = c.m_attachments[i];
+
 		out.blendEnable = !(in.m_srcBlendMethod == BlendMethod::ONE
 			&& in.m_dstBlendMethod == BlendMethod::ZERO);
 		out.srcColorBlendFactor = convertBlendMethod(in.m_srcBlendMethod);
@@ -317,16 +319,38 @@ VkPipelineColorBlendStateCreateInfo* PipelineImpl::initColorState(
 }
 
 //==============================================================================
-void PipelineImpl::init(const PipelineInitInfo& init)
+VkPipelineDynamicStateCreateInfo* PipelineImpl::initDynamicState(
+	VkPipelineDynamicStateCreateInfo& ci)
+{
+	// Almost all state is dynamic
+	static const Array<VkDynamicState, 8> DYN = {{VK_DYNAMIC_STATE_VIEWPORT,
+		VK_DYNAMIC_STATE_SCISSOR,
+		VK_DYNAMIC_STATE_DEPTH_BIAS,
+		VK_DYNAMIC_STATE_BLEND_CONSTANTS,
+		VK_DYNAMIC_STATE_DEPTH_BOUNDS,
+		VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
+		VK_DYNAMIC_STATE_STENCIL_WRITE_MASK,
+		VK_DYNAMIC_STATE_STENCIL_REFERENCE}};
+
+	ci.dynamicStateCount = DYN.getSize();
+	ci.pDynamicStates = &DYN[0];
+
+	return &ci;
+}
+
+//==============================================================================
+Error PipelineImpl::init(const PipelineInitInfo& init)
 {
 	if(init.m_shaders[ShaderType::COMPUTE].isCreated())
 	{
-		initCompute(init);
+		return initCompute(init);
 	}
 	else
 	{
-		initGraphics(init);
+		return initGraphics(init);
 	}
+
+	return ErrorCode::NONE;
 }
 
 } // end namespace anki

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

@@ -21,8 +21,16 @@ Shader::~Shader()
 }
 
 //==============================================================================
-void Shader::init(ShaderType shaderType, const void* source, PtrSize sourceSize)
+void Shader::init(ShaderType shaderType, const CString& source)
 {
+	ANKI_ASSERT(!source.isEmpty());
+
+	m_impl.reset(getAllocator().newInstance<ShaderImpl>(&getManager()));
+
+	if(m_impl->init(shaderType, source))
+	{
+		ANKI_LOGF("Cannot recover");
+	}
 }
 
 } // end namespace anki

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

@@ -152,6 +152,15 @@ static const TBuiltInResource GLSLANG_LIMITS = setLimits();
 // ShaderImpl                                                                  =
 //==============================================================================
 
+//==============================================================================
+ShaderImpl::~ShaderImpl()
+{
+	if(m_handle)
+	{
+		vkDestroyShaderModule(getDevice(), m_handle, nullptr);
+	}
+}
+
 //==============================================================================
 Error ShaderImpl::genSpirv(
 	const CString& source, std::vector<unsigned int>& spirv)
@@ -218,7 +227,7 @@ Error ShaderImpl::init(ShaderType shaderType, const CString& source)
 		spirv.size() * sizeof(unsigned int),
 		&spirv[0]};
 
-	ANKI_VK_CHECKF(vkCreateShaderModule(getDevice(), &ci, nullptr, &m_handle));
+	ANKI_VK_CHECK(vkCreateShaderModule(getDevice(), &ci, nullptr, &m_handle));
 
 	return ErrorCode::NONE;
 }

+ 1 - 1
src/resource/ShaderResource.cpp

@@ -55,7 +55,7 @@ Error ShaderResource::load(
 
 	// Create
 	m_shader = getManager().getGrManager().newInstance<Shader>(
-		pars.getShaderType(), &source[0], source.getLength() + 1);
+		pars.getShaderType(), source.toCString());
 
 	m_type = pars.getShaderType();
 

+ 117 - 0
tests/gr/Gr.cpp

@@ -0,0 +1,117 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <tests/framework/Framework.h>
+#include <anki/Gr.h>
+#include <anki/core/NativeWindow.h>
+#include <anki/core/Config.h>
+#include <anki/util/HighRezTimer.h>
+
+namespace anki
+{
+
+static const char* VERT_SRC = R"(out gl_PerVertex
+{
+	vec4 gl_Position;
+};
+
+void main()
+{
+	const vec2 POSITIONS[3] =
+		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);
+})";
+
+static const char* FRAG_SRC = R"(layout (location = 0) out vec4 out_color;
+
+void main()
+{
+	out_color = vec4(0.5);
+})";
+
+//==============================================================================
+static NativeWindow* createWindow()
+{
+	HeapAllocator<U8> alloc(allocAligned, nullptr);
+
+	NativeWindowInitInfo inf;
+	NativeWindow* win = new NativeWindow();
+
+	ANKI_TEST_EXPECT_NO_ERR(win->init(inf, alloc));
+
+	return win;
+}
+
+//==============================================================================
+static void createGrManager(NativeWindow*& win, GrManager*& gr)
+{
+	win = createWindow();
+	gr = new GrManager();
+
+	Config cfg;
+	GrManagerInitInfo inf;
+	inf.m_allocCallback = allocAligned;
+	inf.m_cacheDirectory = "./";
+	inf.m_config = &cfg;
+	inf.m_window = win;
+	ANKI_TEST_EXPECT_NO_ERR(gr->init(inf));
+}
+
+//==============================================================================
+ANKI_TEST(Gr, GrManager)
+{
+	NativeWindow* win = nullptr;
+	GrManager* gr = nullptr;
+	createGrManager(win, gr);
+
+	delete gr;
+	delete win;
+}
+
+//==============================================================================
+ANKI_TEST(Gr, Shader)
+{
+	NativeWindow* win = nullptr;
+	GrManager* gr = nullptr;
+	createGrManager(win, gr);
+
+	{
+		ShaderPtr shader =
+			gr->newInstance<Shader>(ShaderType::VERTEX, VERT_SRC);
+	}
+
+	delete gr;
+	delete win;
+}
+
+//==============================================================================
+ANKI_TEST(Gr, Pipeline)
+{
+	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_attachments[0].m_format =
+			PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM);
+		init.m_color.m_attachmentCount = 1;
+		init.m_depthStencil.m_depthWriteEnabled = false;
+
+		PipelinePtr ppline = gr->newInstance<Pipeline>(init);
+	}
+
+	delete gr;
+	delete win;
+}
+
+} // end namespace anki

+ 0 - 47
tests/gr/GrManager.cpp

@@ -1,47 +0,0 @@
-// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <tests/framework/Framework.h>
-#include <anki/gr/GrManager.h>
-#include <anki/core/NativeWindow.h>
-#include <anki/core/Config.h>
-#include <anki/util/HighRezTimer.h>
-
-namespace anki
-{
-
-//==============================================================================
-NativeWindow* createWindow()
-{
-	HeapAllocator<U8> alloc(allocAligned, nullptr);
-
-	NativeWindowInitInfo inf;
-	NativeWindow* win = new NativeWindow();
-
-	ANKI_TEST_EXPECT_NO_ERR(win->init(inf, alloc));
-
-	return win;
-}
-
-//==============================================================================
-ANKI_TEST(Gr, GrManager)
-{
-	NativeWindow* win = createWindow();
-
-	GrManager* gr = new GrManager();
-
-	Config cfg;
-	GrManagerInitInfo inf;
-	inf.m_allocCallback = allocAligned;
-	inf.m_cacheDirectory = "./";
-	inf.m_config = &cfg;
-	inf.m_window = win;
-	ANKI_TEST_EXPECT_NO_ERR(gr->init(inf));
-
-	delete gr;
-	delete win;
-}
-
-} // end namespace anki

+ 1 - 1
thirdparty

@@ -1 +1 @@
-Subproject commit f4bd88a88ec48298b460426af93515984e3772a2
+Subproject commit 5913d2a9ccf124bc21ed5cf227b291da491db5f8