Browse Source

Some refactoring

Panagiotis Christopoulos Charitos 9 years ago
parent
commit
b7ecf1ed39

+ 3 - 1
CMakeLists.txt

@@ -220,7 +220,9 @@ if(SDL)
 		DOWNLOAD_COMMAND ""
 		DOWNLOAD_COMMAND ""
 		PREFIX "${CMAKE_CURRENT_BINARY_DIR}/sdl2_build"
 		PREFIX "${CMAKE_CURRENT_BINARY_DIR}/sdl2_build"
 		SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/SDL2"
 		SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/SDL2"
-		CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/sdl2_build -DSDL_SHARED=OFF -DSDL_STATIC=ON -DCMAKE_BUILD_TYPE=Release -DDIRECTX=OFF -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DVIDEO_MIR=OFF -DSNDIO=OFF -DVIDEO_OPENGLES=OFF)
+		CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/sdl2_build -DSDL_SHARED=OFF -DSDL_STATIC=ON 
+		-DCMAKE_BUILD_TYPE=Release -DDIRECTX=OFF -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DVIDEO_MIR=OFF 
+		-DVIDEO_WAYLAND=OFF -DSNDIO=OFF -DVIDEO_OPENGLES=OFF)
 
 
 	ExternalProject_Get_Property(SDL2_PROJECT install_dir)
 	ExternalProject_Get_Property(SDL2_PROJECT install_dir)
 	set(SDL2_INSTALL_DIR ${install_dir})
 	set(SDL2_INSTALL_DIR ${install_dir})

+ 3 - 106
src/anki/gr/vulkan/GrManagerImpl.cpp

@@ -60,16 +60,7 @@ GrManagerImpl::~GrManagerImpl()
 		vkDestroyPipelineLayout(m_device, m_globalPipelineLayout, nullptr);
 		vkDestroyPipelineLayout(m_device, m_globalPipelineLayout, nullptr);
 	}
 	}
 
 
-	if(m_globalDescriptorPool)
-	{
-		vkDestroyDescriptorPool(m_device, m_globalDescriptorPool, nullptr);
-	}
-
-	if(m_globalDescriptorSetLayout)
-	{
-		vkDestroyDescriptorSetLayout(m_device, m_globalDescriptorSetLayout, nullptr);
-	}
-
+	m_dsetAlloc.destroy();
 	m_transientMem.destroy();
 	m_transientMem.destroy();
 	m_gpuAlloc.destroy();
 	m_gpuAlloc.destroy();
 
 
@@ -124,8 +115,7 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 	ANKI_CHECK(initSwapchain(init));
 	ANKI_CHECK(initSwapchain(init));
 
 
 	ANKI_CHECK(initMemory(*init.m_config));
 	ANKI_CHECK(initMemory(*init.m_config));
-	ANKI_CHECK(initGlobalDsetLayout());
-	ANKI_CHECK(initGlobalDsetPool());
+	ANKI_CHECK(m_dsetAlloc.init(m_device));
 	ANKI_CHECK(initGlobalPplineLayout());
 	ANKI_CHECK(initGlobalPplineLayout());
 
 
 	for(PerFrame& f : m_perFrame)
 	for(PerFrame& f : m_perFrame)
@@ -423,103 +413,10 @@ Error GrManagerImpl::initSwapchain(const GrManagerInitInfo& init)
 	return ErrorCode::NONE;
 	return ErrorCode::NONE;
 }
 }
 
 
-Error GrManagerImpl::initGlobalDsetLayout()
-{
-	VkDescriptorSetLayoutCreateInfo ci = {};
-	ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
-
-	const U BINDING_COUNT =
-		MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS + MAX_IMAGE_BINDINGS;
-	ci.bindingCount = BINDING_COUNT;
-
-	Array<VkDescriptorSetLayoutBinding, BINDING_COUNT> bindings;
-	memset(&bindings[0], 0, sizeof(bindings));
-	ci.pBindings = &bindings[0];
-
-	U count = 0;
-
-	// Combined image samplers
-	for(U i = 0; i < MAX_TEXTURE_BINDINGS; ++i)
-	{
-		VkDescriptorSetLayoutBinding& binding = bindings[count];
-		binding.binding = count;
-		binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-		binding.descriptorCount = 1;
-		binding.stageFlags = VK_SHADER_STAGE_ALL;
-
-		++count;
-	}
-
-	// Uniform buffers
-	for(U i = 0; i < MAX_UNIFORM_BUFFER_BINDINGS; ++i)
-	{
-		VkDescriptorSetLayoutBinding& binding = bindings[count];
-		binding.binding = count;
-		binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
-		binding.descriptorCount = 1;
-		binding.stageFlags = VK_SHADER_STAGE_ALL;
-
-		++count;
-	}
-
-	// Storage buffers
-	for(U i = 0; i < MAX_STORAGE_BUFFER_BINDINGS; ++i)
-	{
-		VkDescriptorSetLayoutBinding& binding = bindings[count];
-		binding.binding = count;
-		binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
-		binding.descriptorCount = 1;
-		binding.stageFlags = VK_SHADER_STAGE_ALL;
-
-		++count;
-	}
-
-	// Images
-	for(U i = 0; i < MAX_IMAGE_BINDINGS; ++i)
-	{
-		VkDescriptorSetLayoutBinding& binding = bindings[count];
-		binding.binding = count;
-		binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
-		binding.descriptorCount = 1;
-		binding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
-
-		++count;
-	}
-
-	ANKI_ASSERT(count == BINDING_COUNT);
-
-	ANKI_VK_CHECK(vkCreateDescriptorSetLayout(m_device, &ci, nullptr, &m_globalDescriptorSetLayout));
-
-	return ErrorCode::NONE;
-}
-
-Error GrManagerImpl::initGlobalDsetPool()
-{
-	Array<VkDescriptorPoolSize, 4> pools = {{}};
-	pools[0] =
-		VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, MAX_TEXTURE_BINDINGS * MAX_RESOURCE_GROUPS};
-	pools[1] = VkDescriptorPoolSize{
-		VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, MAX_UNIFORM_BUFFER_BINDINGS * MAX_RESOURCE_GROUPS};
-	pools[2] = VkDescriptorPoolSize{
-		VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, MAX_STORAGE_BUFFER_BINDINGS * MAX_RESOURCE_GROUPS};
-	pools[3] = VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, MAX_IMAGE_BINDINGS * MAX_RESOURCE_GROUPS};
-
-	VkDescriptorPoolCreateInfo ci = {};
-	ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
-	ci.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
-	ci.maxSets = MAX_RESOURCE_GROUPS;
-	ci.poolSizeCount = pools.getSize();
-	ci.pPoolSizes = &pools[0];
-
-	ANKI_VK_CHECK(vkCreateDescriptorPool(m_device, &ci, nullptr, &m_globalDescriptorPool));
-
-	return ErrorCode::NONE;
-}
-
 Error GrManagerImpl::initGlobalPplineLayout()
 Error GrManagerImpl::initGlobalPplineLayout()
 {
 {
 	Array<VkDescriptorSetLayout, MAX_BOUND_RESOURCE_GROUPS> sets = {
 	Array<VkDescriptorSetLayout, MAX_BOUND_RESOURCE_GROUPS> sets = {
-		{m_globalDescriptorSetLayout, m_globalDescriptorSetLayout}};
+		{m_dsetAlloc.getGlobalDescriptorSetLayout(), m_dsetAlloc.getGlobalDescriptorSetLayout()}};
 
 
 	VkPipelineLayoutCreateInfo ci;
 	VkPipelineLayoutCreateInfo ci;
 	ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
 	ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;

+ 7 - 33
src/anki/gr/vulkan/GrManagerImpl.h

@@ -13,6 +13,7 @@
 #include <anki/gr/vulkan/TransientMemoryManager.h>
 #include <anki/gr/vulkan/TransientMemoryManager.h>
 #include <anki/gr/vulkan/QueryAllocator.h>
 #include <anki/gr/vulkan/QueryAllocator.h>
 #include <anki/gr/vulkan/CompatibleRenderPassCreator.h>
 #include <anki/gr/vulkan/CompatibleRenderPassCreator.h>
+#include <anki/gr/vulkan/ResourceGroupMisc.h>
 #include <anki/util/HashMap.h>
 #include <anki/util/HashMap.h>
 
 
 namespace anki
 namespace anki
@@ -77,33 +78,6 @@ public:
 	/// @name object_creation
 	/// @name object_creation
 	/// @{
 	/// @{
 
 
-	ANKI_USE_RESULT Error allocateDescriptorSet(VkDescriptorSet& out)
-	{
-		out = VK_NULL_HANDLE;
-		VkDescriptorSetAllocateInfo ci = {};
-		ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
-		ci.descriptorPool = m_globalDescriptorPool;
-		ci.descriptorSetCount = 1;
-		ci.pSetLayouts = &m_globalDescriptorSetLayout;
-
-		LockGuard<Mutex> lock(m_globalDescriptorPoolMtx);
-		if(++m_descriptorSetAllocationCount > MAX_RESOURCE_GROUPS)
-		{
-			ANKI_LOGE("Exceeded the MAX_RESOURCE_GROUPS");
-			return ErrorCode::OUT_OF_MEMORY;
-		}
-		ANKI_VK_CHECK(vkAllocateDescriptorSets(m_device, &ci, &out));
-		return ErrorCode::NONE;
-	}
-
-	void freeDescriptorSet(VkDescriptorSet ds)
-	{
-		ANKI_ASSERT(ds);
-		LockGuard<Mutex> lock(m_globalDescriptorPoolMtx);
-		--m_descriptorSetAllocationCount;
-		ANKI_VK_CHECKF(vkFreeDescriptorSets(m_device, m_globalDescriptorPool, 1, &ds));
-	}
-
 	VkCommandBuffer newCommandBuffer(ThreadId tid, Bool secondLevel);
 	VkCommandBuffer newCommandBuffer(ThreadId tid, Bool secondLevel);
 
 
 	void deleteCommandBuffer(VkCommandBuffer cmdb, Bool secondLevel, ThreadId tid);
 	void deleteCommandBuffer(VkCommandBuffer cmdb, Bool secondLevel, ThreadId tid);
@@ -202,6 +176,11 @@ public:
 		return m_rpCreator;
 		return m_rpCreator;
 	}
 	}
 
 
+	DescriptorSetAllocator& getDescriptorSetAllocator()
+	{
+		return m_dsetAlloc;
+	}
+
 private:
 private:
 	GrManager* m_manager = nullptr;
 	GrManager* m_manager = nullptr;
 
 
@@ -243,10 +222,7 @@ private:
 	Array<PerFrame, MAX_FRAMES_IN_FLIGHT> m_perFrame;
 	Array<PerFrame, MAX_FRAMES_IN_FLIGHT> m_perFrame;
 	/// @}
 	/// @}
 
 
-	VkDescriptorSetLayout m_globalDescriptorSetLayout = VK_NULL_HANDLE;
-	VkDescriptorPool m_globalDescriptorPool = VK_NULL_HANDLE;
-	Mutex m_globalDescriptorPoolMtx;
-	U32 m_descriptorSetAllocationCount = 0;
+	DescriptorSetAllocator m_dsetAlloc;
 	VkPipelineLayout m_globalPipelineLayout = VK_NULL_HANDLE;
 	VkPipelineLayout m_globalPipelineLayout = VK_NULL_HANDLE;
 
 
 	/// Map for compatible render passes.
 	/// Map for compatible render passes.
@@ -309,8 +285,6 @@ private:
 	ANKI_USE_RESULT Error initDevice(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initDevice(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initSwapchain(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initSwapchain(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initFramebuffers(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initFramebuffers(const GrManagerInitInfo& init);
-	ANKI_USE_RESULT Error initGlobalDsetLayout();
-	ANKI_USE_RESULT Error initGlobalDsetPool();
 	ANKI_USE_RESULT Error initGlobalPplineLayout();
 	ANKI_USE_RESULT Error initGlobalPplineLayout();
 	ANKI_USE_RESULT Error initMemory(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initMemory(const ConfigSet& cfg);
 
 

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

@@ -17,7 +17,7 @@ ResourceGroupImpl::~ResourceGroupImpl()
 {
 {
 	if(m_handle)
 	if(m_handle)
 	{
 	{
-		getGrManagerImpl().freeDescriptorSet(m_handle);
+		getGrManagerImpl().getDescriptorSetAllocator().free(m_handle);
 	}
 	}
 
 
 	m_refs.destroy(getAllocator());
 	m_refs.destroy(getAllocator());
@@ -191,7 +191,7 @@ Error ResourceGroupImpl::init(const ResourceGroupInitInfo& init)
 	//
 	//
 	if(needsDSet)
 	if(needsDSet)
 	{
 	{
-		ANKI_CHECK(getGrManagerImpl().allocateDescriptorSet(m_handle));
+		ANKI_CHECK(getGrManagerImpl().getDescriptorSetAllocator().allocate(m_handle));
 		ANKI_ASSERT(m_bindPoint != VK_PIPELINE_BIND_POINT_MAX_ENUM);
 		ANKI_ASSERT(m_bindPoint != VK_PIPELINE_BIND_POINT_MAX_ENUM);
 	}
 	}
 
 

+ 128 - 0
src/anki/gr/vulkan/ResourceGroupMisc.cpp

@@ -0,0 +1,128 @@
+// 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/ResourceGroupMisc.h>
+
+namespace anki
+{
+
+Error DescriptorSetAllocator::init(VkDevice dev)
+{
+	ANKI_ASSERT(dev);
+	m_dev = dev;
+	ANKI_CHECK(initGlobalDsetLayout());
+	ANKI_CHECK(initGlobalDsetPool());
+	return ErrorCode::NONE;
+}
+
+void DescriptorSetAllocator::destroy()
+{
+	if(m_globalDPool)
+	{
+		vkDestroyDescriptorPool(m_dev, m_globalDPool, nullptr);
+		m_globalDPool = VK_NULL_HANDLE;
+	}
+
+	if(m_globalDsetLayout)
+	{
+		vkDestroyDescriptorSetLayout(m_dev, m_globalDsetLayout, nullptr);
+		m_globalDsetLayout = VK_NULL_HANDLE;
+	}
+}
+
+Error DescriptorSetAllocator::initGlobalDsetLayout()
+{
+	VkDescriptorSetLayoutCreateInfo ci = {};
+	ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+
+	const U BINDING_COUNT =
+		MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS + MAX_IMAGE_BINDINGS;
+	ci.bindingCount = BINDING_COUNT;
+
+	Array<VkDescriptorSetLayoutBinding, BINDING_COUNT> bindings;
+	memset(&bindings[0], 0, sizeof(bindings));
+	ci.pBindings = &bindings[0];
+
+	U count = 0;
+
+	// Combined image samplers
+	for(U i = 0; i < MAX_TEXTURE_BINDINGS; ++i)
+	{
+		VkDescriptorSetLayoutBinding& binding = bindings[count];
+		binding.binding = count;
+		binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+		binding.descriptorCount = 1;
+		binding.stageFlags = VK_SHADER_STAGE_ALL;
+
+		++count;
+	}
+
+	// Uniform buffers
+	for(U i = 0; i < MAX_UNIFORM_BUFFER_BINDINGS; ++i)
+	{
+		VkDescriptorSetLayoutBinding& binding = bindings[count];
+		binding.binding = count;
+		binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+		binding.descriptorCount = 1;
+		binding.stageFlags = VK_SHADER_STAGE_ALL;
+
+		++count;
+	}
+
+	// Storage buffers
+	for(U i = 0; i < MAX_STORAGE_BUFFER_BINDINGS; ++i)
+	{
+		VkDescriptorSetLayoutBinding& binding = bindings[count];
+		binding.binding = count;
+		binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
+		binding.descriptorCount = 1;
+		binding.stageFlags = VK_SHADER_STAGE_ALL;
+
+		++count;
+	}
+
+	// Images
+	for(U i = 0; i < MAX_IMAGE_BINDINGS; ++i)
+	{
+		VkDescriptorSetLayoutBinding& binding = bindings[count];
+		binding.binding = count;
+		binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
+		binding.descriptorCount = 1;
+		binding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+
+		++count;
+	}
+
+	ANKI_ASSERT(count == BINDING_COUNT);
+
+	ANKI_VK_CHECK(vkCreateDescriptorSetLayout(m_dev, &ci, nullptr, &m_globalDsetLayout));
+
+	return ErrorCode::NONE;
+}
+
+Error DescriptorSetAllocator::initGlobalDsetPool()
+{
+	Array<VkDescriptorPoolSize, 4> pools = {{}};
+	pools[0] =
+		VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, MAX_TEXTURE_BINDINGS * MAX_RESOURCE_GROUPS};
+	pools[1] = VkDescriptorPoolSize{
+		VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, MAX_UNIFORM_BUFFER_BINDINGS * MAX_RESOURCE_GROUPS};
+	pools[2] = VkDescriptorPoolSize{
+		VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, MAX_STORAGE_BUFFER_BINDINGS * MAX_RESOURCE_GROUPS};
+	pools[3] = VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, MAX_IMAGE_BINDINGS * MAX_RESOURCE_GROUPS};
+
+	VkDescriptorPoolCreateInfo ci = {};
+	ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+	ci.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
+	ci.maxSets = MAX_RESOURCE_GROUPS;
+	ci.poolSizeCount = pools.getSize();
+	ci.pPoolSizes = &pools[0];
+
+	ANKI_VK_CHECK(vkCreateDescriptorPool(m_dev, &ci, nullptr, &m_globalDPool));
+
+	return ErrorCode::NONE;
+}
+
+} // end namespace anki

+ 80 - 0
src/anki/gr/vulkan/ResourceGroupMisc.h

@@ -0,0 +1,80 @@
+// 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
+/// @{
+
+/// Allocator of descriptor sets.
+class DescriptorSetAllocator : public NonCopyable
+{
+public:
+	DescriptorSetAllocator()
+	{
+	}
+
+	~DescriptorSetAllocator()
+	{
+		ANKI_ASSERT(m_globalDPool == VK_NULL_HANDLE);
+		ANKI_ASSERT(m_globalDsetLayout == VK_NULL_HANDLE);
+	}
+
+	ANKI_USE_RESULT Error init(VkDevice dev);
+
+	void destroy();
+
+	ANKI_USE_RESULT Error allocate(VkDescriptorSet& out)
+	{
+		out = VK_NULL_HANDLE;
+		VkDescriptorSetAllocateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+		ci.descriptorPool = m_globalDPool;
+		ci.descriptorSetCount = 1;
+		ci.pSetLayouts = &m_globalDsetLayout;
+
+		LockGuard<Mutex> lock(m_mtx);
+		if(++m_descriptorSetAllocationCount > MAX_RESOURCE_GROUPS)
+		{
+			ANKI_LOGE("Exceeded the MAX_RESOURCE_GROUPS");
+			return ErrorCode::OUT_OF_MEMORY;
+		}
+		ANKI_VK_CHECK(vkAllocateDescriptorSets(m_dev, &ci, &out));
+		return ErrorCode::NONE;
+	}
+
+	void free(VkDescriptorSet ds)
+	{
+		ANKI_ASSERT(ds);
+		LockGuard<Mutex> lock(m_mtx);
+		ANKI_ASSERT(m_descriptorSetAllocationCount > 0);
+		--m_descriptorSetAllocationCount;
+		ANKI_VK_CHECKF(vkFreeDescriptorSets(m_dev, m_globalDPool, 1, &ds));
+	}
+
+	VkDescriptorSetLayout getGlobalDescriptorSetLayout() const
+	{
+		ANKI_ASSERT(m_globalDsetLayout);
+		return m_globalDsetLayout;
+	}
+
+private:
+	VkDevice m_dev = VK_NULL_HANDLE;
+	VkDescriptorSetLayout m_globalDsetLayout = VK_NULL_HANDLE;
+	VkDescriptorPool m_globalDPool = VK_NULL_HANDLE;
+	Mutex m_mtx;
+	U32 m_descriptorSetAllocationCount = 0;
+
+	Error initGlobalDsetLayout();
+	Error initGlobalDsetPool();
+};
+/// @}
+
+} // end namespace anki