Browse Source

Fix GL for MESA. Fix vulkan RADV

Panagiotis Christopoulos Charitos 7 years ago
parent
commit
218e6f245a
41 changed files with 140 additions and 133 deletions
  1. BIN
      engine_data/Plight.ankimesh
  2. BIN
      engine_data/Slight.ankimesh
  3. BIN
      samples/sponza/assets/arch_support_big.ankimesh
  4. BIN
      samples/sponza/assets/arch_support_med.ankimesh
  5. BIN
      samples/sponza/assets/arch_support_tiny.ankimesh
  6. BIN
      samples/sponza/assets/column_a.001.ankimesh
  7. BIN
      samples/sponza/assets/column_a.ankimesh
  8. BIN
      samples/sponza/assets/column_b.ankimesh
  9. BIN
      samples/sponza/assets/column_b_top.ankimesh
  10. BIN
      samples/sponza/assets/column_c.ankimesh
  11. BIN
      samples/sponza/assets/column_c_small_top.ankimesh
  12. BIN
      samples/sponza/assets/column_c_square.ankimesh
  13. BIN
      samples/sponza/assets/flag_pole.ankimesh
  14. BIN
      samples/sponza/assets/hanging_vase.ankimesh
  15. BIN
      samples/sponza/assets/lion.ankimesh
  16. BIN
      samples/sponza/assets/rod_end.ankimesh
  17. BIN
      samples/sponza/assets/round_window.ankimesh
  18. BIN
      samples/sponza/assets/small_window_inner.ankimesh
  19. BIN
      samples/sponza/assets/small_window_outter.ankimesh
  20. BIN
      samples/sponza/assets/sponza_00.ankimesh
  21. BIN
      samples/sponza/assets/sponza_257.ankimesh
  22. BIN
      samples/sponza/assets/sponza_277.ankimesh
  23. BIN
      samples/sponza/assets/sponza_278.ankimesh
  24. BIN
      samples/sponza/assets/sponza_279.ankimesh
  25. BIN
      samples/sponza/assets/sponza_281.ankimesh
  26. BIN
      samples/sponza/assets/vase.ankimesh
  27. BIN
      samples/sponza/assets/vase_chains.ankimesh
  28. BIN
      samples/sponza/assets/vase_hanger.ankimesh
  29. BIN
      samples/sponza/assets/window.ankimesh
  30. 1 1
      src/anki/core/Config.cpp
  31. 2 3
      src/anki/gr/ShaderCompiler.cpp
  32. 1 1
      src/anki/gr/gl/CommandBuffer.cpp
  33. 5 0
      src/anki/gr/gl/Common.cpp
  34. 11 9
      src/anki/gr/gl/ShaderProgramImpl.cpp
  35. 2 1
      src/anki/gr/vulkan/GrManagerImpl.cpp
  36. 6 3
      src/anki/resource/MeshLoader.cpp
  37. 0 64
      src/anki/scene/Octree.cpp
  38. 13 14
      src/anki/scene/Octree.h
  39. 53 13
      src/anki/util/ObjectAllocator.h
  40. 26 18
      src/anki/util/ObjectAllocator.inl.h
  41. 20 6
      tools/scene/ExporterMesh.cpp

BIN
engine_data/Plight.ankimesh


BIN
engine_data/Slight.ankimesh


BIN
samples/sponza/assets/arch_support_big.ankimesh


BIN
samples/sponza/assets/arch_support_med.ankimesh


BIN
samples/sponza/assets/arch_support_tiny.ankimesh


BIN
samples/sponza/assets/column_a.001.ankimesh


BIN
samples/sponza/assets/column_a.ankimesh


BIN
samples/sponza/assets/column_b.ankimesh


BIN
samples/sponza/assets/column_b_top.ankimesh


BIN
samples/sponza/assets/column_c.ankimesh


BIN
samples/sponza/assets/column_c_small_top.ankimesh


BIN
samples/sponza/assets/column_c_square.ankimesh


BIN
samples/sponza/assets/flag_pole.ankimesh


BIN
samples/sponza/assets/hanging_vase.ankimesh


BIN
samples/sponza/assets/lion.ankimesh


BIN
samples/sponza/assets/rod_end.ankimesh


BIN
samples/sponza/assets/round_window.ankimesh


BIN
samples/sponza/assets/small_window_inner.ankimesh


BIN
samples/sponza/assets/small_window_outter.ankimesh


BIN
samples/sponza/assets/sponza_00.ankimesh


BIN
samples/sponza/assets/sponza_257.ankimesh


BIN
samples/sponza/assets/sponza_277.ankimesh


BIN
samples/sponza/assets/sponza_278.ankimesh


BIN
samples/sponza/assets/sponza_279.ankimesh


BIN
samples/sponza/assets/sponza_281.ankimesh


BIN
samples/sponza/assets/vase.ankimesh


BIN
samples/sponza/assets/vase_chains.ankimesh


BIN
samples/sponza/assets/vase_hanger.ankimesh


BIN
samples/sponza/assets/window.ankimesh


+ 1 - 1
src/anki/core/Config.cpp

@@ -70,7 +70,7 @@ Config::Config()
 	newOption("core.storagePerFrameMemorySize", 16_MB);
 	newOption("core.vertexPerFrameMemorySize", 10_MB);
 	newOption("core.textureBufferPerFrameMemorySize", 1_MB);
-	newOption("core.mainThreadCount", getCpuCoresCount());
+	newOption("core.mainThreadCount", getCpuCoresCount() / 2);
 	newOption("core.displayStats", false);
 }
 

+ 2 - 3
src/anki/gr/ShaderCompiler.cpp

@@ -37,8 +37,8 @@ static const char* SHADER_HEADER = R"(#version 450 core
 #if defined(ANKI_BACKEND_GL) 
 #	define ANKI_UBO_BINDING(set_, binding_) binding = (set_) * (%u) + (binding_)
 #	define ANKI_SS_BINDING(set_, binding_) binding = (set_) * (%u) + (binding_)
-#	define ANKI_TEX_BINDING(set_, binding_) location = (set_) * (%u) + (binding_)
-#	define ANKI_IMAGE_BINDING(set_, binding_) location = (set_) * (%u) + (binding_) + (%u)
+#	define ANKI_TEX_BINDING(set_, binding_) binding = (set_) * (%u) + (binding_)
+#	define ANKI_IMAGE_BINDING(set_, binding_) binding = (set_) * (%u) + (binding_)
 #	define ANKI_SPEC_CONST(binding_, type_, name_) const type_ name_ = _anki_spec_const_ ## binding_
 #	define ANKI_PUSH_CONSTANTS(struct_, name_) layout(location = (%u)) uniform struct_ name_
 #else
@@ -296,7 +296,6 @@ Error ShaderCompiler::compile(CString source, const ShaderCompilerOptions& optio
 		MAX_STORAGE_BUFFER_BINDINGS,
 		MAX_TEXTURE_BINDINGS,
 		MAX_IMAGE_BINDINGS, // Images
-		MAX_TEXTURE_BINDINGS * MAX_DESCRIPTOR_SETS, // Images offset
 		(MAX_TEXTURE_BINDINGS + MAX_IMAGE_BINDINGS) * MAX_DESCRIPTOR_SETS, // Push constant location
 		// VK bindings
 		0,

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

@@ -757,7 +757,7 @@ void CommandBuffer::bindImage(U32 set, U32 binding, TextureViewPtr img)
 
 	if(self.m_state.bindImage(set, binding, img))
 	{
-		binding = binding + set * MAX_IMAGE_BINDINGS + MAX_TEXTURE_BINDINGS * MAX_DESCRIPTOR_SETS;
+		binding = binding + set * MAX_IMAGE_BINDINGS;
 		self.pushBackNewCommand<Cmd>(binding, img);
 	}
 }

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

@@ -170,6 +170,11 @@ void convertVertexFormat(Format fmt, U& compCount, GLenum& type, Bool& normalize
 		type = GL_UNSIGNED_SHORT;
 		normalized = true;
 		break;
+	case Format::R16G16B16A16_SFLOAT:
+		compCount = 4;
+		type = GL_HALF_FLOAT;
+		normalized = false;
+		break;
 	case Format::A2B10G10R10_SNORM_PACK32:
 		compCount = 4;
 		type = GL_INT_2_10_10_10_REV;

+ 11 - 9
src/anki/gr/gl/ShaderProgramImpl.cpp

@@ -122,14 +122,7 @@ const ShaderProgramImplReflection& ShaderProgramImpl::getReflection()
 				continue;
 			}
 
-			GLint location = glGetUniformLocation(getGlName(), &name[0]);
-			if(location < I((MAX_TEXTURE_BINDINGS + MAX_IMAGE_BINDINGS) * MAX_DESCRIPTOR_SETS))
-			{
-				// It must be a sampled image, skip it
-				continue;
-			}
-
-			// Store those info
+			// Set type
 			ShaderVariableDataType akType = ShaderVariableDataType::NONE;
 			switch(type)
 			{
@@ -149,9 +142,18 @@ const ShaderProgramImplReflection& ShaderProgramImpl::getReflection()
 				akType = ShaderVariableDataType::MAT3;
 				break;
 			default:
-				ANKI_ASSERT(!"Unsupported type");
+				// Unsupported type, skip as well
+				continue;
+			}
+
+			const GLint location = glGetUniformLocation(getGlName(), &name[0]);
+			if(location < 0)
+			{
+				// Uniform block maybe, skip
+				continue;
 			}
 
+			// Store
 			ShaderProgramImplReflection::Uniform uni;
 			uni.m_location = location;
 			uni.m_type = akType;

+ 2 - 1
src/anki/gr/vulkan/GrManagerImpl.cpp

@@ -402,7 +402,8 @@ Error GrManagerImpl::initInstance(const GrManagerInitInfo& init)
 	default:
 		m_capabilities.m_gpuVendor = GpuVendor::UNKNOWN;
 	}
-	ANKI_VK_LOGI("GPU vendor is %s", &GPU_VENDOR_STR[m_capabilities.m_gpuVendor][0]);
+	ANKI_VK_LOGI(
+		"GPU is %s. Vendor identified a %s", m_devProps.deviceName, &GPU_VENDOR_STR[m_capabilities.m_gpuVendor][0]);
 
 	vkGetPhysicalDeviceFeatures(m_physicalDevice, &m_devFeatures);
 

+ 6 - 3
src/anki/resource/MeshLoader.cpp

@@ -173,7 +173,7 @@ Error MeshLoader::checkHeader() const
 
 	// Attributes
 	ANKI_CHECK(checkFormat(
-		VertexAttributeLocation::POSITION, Array<Format, 2>{{Format::R16G16B16_SFLOAT, Format::R32G32B32_SFLOAT}}));
+		VertexAttributeLocation::POSITION, Array<Format, 2>{{Format::R16G16B16A16_SFLOAT, Format::R32G32B32_SFLOAT}}));
 	ANKI_CHECK(checkFormat(VertexAttributeLocation::NORMAL, Array<Format, 1>{{Format::A2B10G10R10_SNORM_PACK32}}));
 	ANKI_CHECK(checkFormat(VertexAttributeLocation::TANGENT, Array<Format, 1>{{Format::A2B10G10R10_SNORM_PACK32}}));
 	ANKI_CHECK(
@@ -315,15 +315,18 @@ Error MeshLoader::storeIndicesAndPosition(DynamicArrayAuto<U32>& indices, Dynami
 			{
 				vert = *reinterpret_cast<Vec3*>(&staging[i * buffInfo.m_vertexStride + attrib.m_relativeOffset]);
 			}
-			else
+			else if(attrib.m_format == Format::R16G16B16A16_SFLOAT)
 			{
-				ANKI_ASSERT(attrib.m_format == Format::R16G16B16_SFLOAT);
 				F16* f16 = reinterpret_cast<F16*>(&staging[i * buffInfo.m_vertexStride + attrib.m_relativeOffset]);
 
 				vert[0] = f16[0].toF32();
 				vert[1] = f16[1].toF32();
 				vert[2] = f16[2].toF32();
 			}
+			else
+			{
+				ANKI_ASSERT(0);
+			}
 
 			positions[i] = vert;
 		}

+ 0 - 64
src/anki/scene/Octree.cpp

@@ -93,68 +93,4 @@ void Octree::placeElementRecursive(
 	// TODO
 }
 
-OctreeLeaf& Octree::newLeaf()
-{
-	OctreeLeaf* out = nullptr;
-
-	// Try find one in the storages
-	for(LeafStorage& storage : m_leafStorages)
-	{
-		if(storage.m_unusedLeafCount > 0)
-		{
-			// Pop an element
-			--storage.m_unusedLeafCount;
-			out = &storage.m_leafs[storage.m_unusedLeafCount];
-			break;
-		}
-	}
-
-	if(out == nullptr)
-	{
-		// Need to create a new storage
-
-		m_leafStorages.resize(m_alloc, m_leafStorages.getSize() + 1);
-		LeafStorage& storage = m_leafStorages.getBack();
-
-		storage.m_leafs = {m_alloc.newArray<OctreeLeaf>(LEAFES_PER_STORAGE), LEAFES_PER_STORAGE};
-		storage.m_unusedLeafCount = LEAFES_PER_STORAGE;
-
-		storage.m_unusedLeafsStack = {m_alloc.newArray<U16>(LEAFES_PER_STORAGE), LEAFES_PER_STORAGE};
-		for(U i = 0; i < LEAFES_PER_STORAGE; ++i)
-		{
-			storage.m_unusedLeafsStack[i] = LEAFES_PER_STORAGE - (i + 1);
-		}
-
-		// Allocate one
-		out = &storage.m_leafs[0];
-		--storage.m_unusedLeafCount;
-	}
-
-	// Init the leaf
-	zeroMemory(*out);
-
-	ANKI_ASSERT(out);
-	return *out;
-}
-
-void Octree::releaseLeaf(OctreeLeaf* leaf)
-{
-	ANKI_ASSERT(leaf);
-
-	for(LeafStorage& storage : m_leafStorages)
-	{
-		if(leaf >= &storage.m_leafs.getFront() && leaf <= &storage.m_leafs.getBack())
-		{
-			// Found its storage
-
-			const U idx = leaf - storage.m_leafs.getBegin();
-			storage.m_unusedLeafsStack[storage.m_unusedLeafCount] = idx;
-			++storage.m_unusedLeafCount;
-			return;
-		}
-	}
-
-	ANKI_ASSERT(!"Not found");
-}
-
 } // end namespace anki

+ 13 - 14
src/anki/scene/Octree.h

@@ -10,6 +10,7 @@
 #include <anki/collision/Forward.h>
 #include <anki/util/WeakArray.h>
 #include <anki/util/Enum.h>
+#include <anki/util/ObjectAllocator.h>
 
 namespace anki
 {
@@ -65,18 +66,7 @@ private:
 	Vec3 m_sceneAabbMin = Vec3(0.0f);
 	Vec3 m_sceneAabbMax = Vec3(0.0f);
 
-	/// Keep the allocations of leafes tight because we want quite alot of them.
-	class LeafStorage
-	{
-	public:
-		WeakArray<OctreeLeaf> m_leafs;
-		WeakArray<U16> m_unusedLeafsStack;
-		U16 m_unusedLeafCount = MAX_U16;
-	};
-
-	static constexpr U LEAFES_PER_STORAGE = 256;
-
-	DynamicArray<LeafStorage> m_leafStorages;
+	ObjectAllocatorSameType<OctreeLeaf, 256> m_leafAlloc;
 
 	OctreeLeaf* m_rootLeaf = nullptr;
 
@@ -97,8 +87,17 @@ private:
 	};
 	ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(LeafMask, friend)
 
-	OctreeLeaf& newLeaf();
-	void releaseLeaf(OctreeLeaf* leaf);
+	OctreeLeaf& newLeaf()
+	{
+		OctreeLeaf* out = m_leafAlloc.newInstance(m_alloc);
+		zeroMemory(*out);
+		return *out;
+	}
+
+	void releaseLeaf(OctreeLeaf* leaf)
+	{
+		m_leafAlloc.deleteInstance(m_alloc, leaf);
+	}
 
 	void placeElementRecursive(
 		const Aabb& volume, OctreeElement* element, OctreeLeaf& parent, const Vec3& aabbMin, const Vec3& aabbMax);

+ 53 - 13
src/anki/util/ObjectAllocator.h

@@ -13,35 +13,51 @@ namespace anki
 /// @addtogroup util
 /// @{
 
-/// A simple allocator of objects of the same type.
-template<typename T, U32 T_CHUNK_SIZE = 64>
+/// A simple allocator for objects of similar types.
+/// @tparam T_OBJECT_SIZE       The maximum size of the objects.
+/// @tparam T_OBJECT_ALIGNMENT  The maximum alignment of the objects.
+/// @tparam T_OBJECTS_PER_CHUNK How much memory (in objects) will be allocated at once.
+/// @tparam TIndexType          If T_OBJECTS_PER_CHUNK>0xFF make it U16. If T_OBJECTS_PER_CHUNK>0xFFFF make it U32.
+template<PtrSize T_OBJECT_SIZE, U32 T_OBJECT_ALIGNMENT, U32 T_OBJECTS_PER_CHUNK = 64, typename TIndexType = U8>
 class ObjectAllocator
 {
 public:
-	using Value = T;
-	static constexpr U32 CHUNK_SIZE = T_CHUNK_SIZE;
+	static constexpr PtrSize OBJECT_SIZE = T_OBJECT_SIZE;
+	static constexpr U32 OBJECT_ALIGNMENT = T_OBJECT_ALIGNMENT;
+	static constexpr U32 OBJECTS_PER_CHUNK = T_OBJECTS_PER_CHUNK;
+
+	ObjectAllocator()
+	{
+	}
+
+	~ObjectAllocator()
+	{
+		ANKI_ASSERT(m_chunksHead == nullptr && m_chunksTail == nullptr && "Forgot to deallocate");
+	}
 
 	/// Allocate and construct a new object instance.
-	template<typename TAlloc, typename... TArgs>
-	Value* newInstance(TAlloc& alloc, TArgs&&... args);
+	/// @note Not thread-safe.
+	template<typename T, typename TAlloc, typename... TArgs>
+	T* newInstance(TAlloc& alloc, TArgs&&... args);
 
 	/// Delete an object.
-	template<typename TAlloc>
-	void deleteInstance(TAlloc& alloc, Value* obj);
+	/// @note Not thread-safe.
+	template<typename T, typename TAlloc>
+	void deleteInstance(TAlloc& alloc, T* obj);
 
 private:
-	/// Storage with equal properties as the Value.
-	struct alignas(alignof(Value)) Object
+	/// Storage with equal properties as the object.
+	struct alignas(OBJECT_ALIGNMENT) Object
 	{
-		U8 m_storage[sizeof(Value)];
+		U8 m_storage[OBJECT_SIZE];
 	};
 
 	/// A  single allocation.
 	class Chunk
 	{
 	public:
-		Array<Object, CHUNK_SIZE> m_objects;
-		Array<U32, CHUNK_SIZE> m_unusedStack;
+		Array<Object, OBJECTS_PER_CHUNK> m_objects;
+		Array<U32, OBJECTS_PER_CHUNK> m_unusedStack;
 		U32 m_unusedCount;
 
 		Chunk* m_next = nullptr;
@@ -51,6 +67,30 @@ private:
 	Chunk* m_chunksHead = nullptr;
 	Chunk* m_chunksTail = nullptr;
 };
+
+/// Convenience wrapper for ObjectAllocator.
+template<typename T, U32 T_OBJECTS_PER_CHUNK = 64, typename TIndexType = U8>
+class ObjectAllocatorSameType : public ObjectAllocator<sizeof(T), alignof(T), T_OBJECTS_PER_CHUNK, TIndexType>
+{
+public:
+	using Base = ObjectAllocator<sizeof(T), alignof(T), T_OBJECTS_PER_CHUNK, TIndexType>;
+
+	/// Allocate and construct a new object instance.
+	/// @note Not thread-safe.
+	template<typename TAlloc, typename... TArgs>
+	T* newInstance(TAlloc& alloc, TArgs&&... args)
+	{
+		return Base::template newInstance<T>(alloc, std::forward(args)...);
+	}
+
+	/// Delete an object.
+	/// @note Not thread-safe.
+	template<typename TAlloc>
+	void deleteInstance(TAlloc& alloc, T* obj)
+	{
+		Base::deleteInstance(alloc, obj);
+	}
+};
 /// @}
 
 } // end namespace anki

+ 26 - 18
src/anki/util/ObjectAllocator.inl.h

@@ -8,11 +8,15 @@
 namespace anki
 {
 
-template<typename T, U32 T_CHUNK_SIZE>
-template<typename TAlloc, typename... TArgs>
-T* ObjectAllocator<T, T_CHUNK_SIZE>::newInstance(TAlloc& alloc, TArgs&&... args)
+template<PtrSize T_OBJECT_SIZE, U32 T_OBJECT_ALIGNMENT, U32 T_OBJECTS_PER_CHUNK, typename TIndexType>
+template<typename T, typename TAlloc, typename... TArgs>
+T* ObjectAllocator<T_OBJECT_SIZE, T_OBJECT_ALIGNMENT, T_OBJECTS_PER_CHUNK, TIndexType>::newInstance(
+	TAlloc& alloc, TArgs&&... args)
 {
-	Value* out = nullptr;
+	static_assert(alignof(T) <= OBJECT_ALIGNMENT, "Wrong object alignment");
+	static_assert(sizeof(T) <= OBJECT_SIZE, "Wrong object size");
+
+	T* out = nullptr;
 
 	// Try find one in the chunks
 	Chunk* chunk = m_chunksHead;
@@ -22,7 +26,7 @@ T* ObjectAllocator<T, T_CHUNK_SIZE>::newInstance(TAlloc& alloc, TArgs&&... args)
 		{
 			// Pop an element
 			--chunk->m_unusedCount;
-			out = &chunk->m_objects[chunk->m_unusedStack[chunk->m_unusedCount]];
+			out = reinterpret_cast<T*>(&chunk->m_objects[chunk->m_unusedStack[chunk->m_unusedCount]]);
 			break;
 		}
 
@@ -35,11 +39,11 @@ T* ObjectAllocator<T, T_CHUNK_SIZE>::newInstance(TAlloc& alloc, TArgs&&... args)
 
 		// Create the chunk
 		Chunk* newChunk = alloc.template newInstance<Chunk>();
-		newChunk->m_unusedCount = CHUNK_SIZE;
+		newChunk->m_unusedCount = OBJECTS_PER_CHUNK;
 
-		for(U i = 0; i < CHUNK_SIZE; ++i)
+		for(U i = 0; i < OBJECTS_PER_CHUNK; ++i)
 		{
-			newChunk->m_unusedStack[i] = CHUNK_SIZE - (i + 1);
+			newChunk->m_unusedStack[i] = OBJECTS_PER_CHUNK - (i + 1);
 		}
 
 		if(m_chunksTail)
@@ -55,22 +59,26 @@ T* ObjectAllocator<T, T_CHUNK_SIZE>::newInstance(TAlloc& alloc, TArgs&&... args)
 		}
 
 		// Allocate one object
-		out = reinterpret_cast<Value*>(&chunk->m_objects[0].m_storage);
+		out = reinterpret_cast<T*>(&chunk->m_objects[0]);
 		--chunk->m_unusedCount;
 	}
 
 	ANKI_ASSERT(out);
 
 	// Construct it
-	::new(out) Value(std::forward<TArgs>(args)...);
+	::new(out) T(std::forward<TArgs>(args)...);
 
 	return out;
 }
 
-template<typename T, U32 T_CHUNK_SIZE>
-template<typename TAlloc>
-void ObjectAllocator<T, T_CHUNK_SIZE>::deleteInstance(TAlloc& alloc, Value* obj)
+template<PtrSize T_OBJECT_SIZE, U32 T_OBJECT_ALIGNMENT, U32 T_OBJECTS_PER_CHUNK, typename TIndexType>
+template<typename T, typename TAlloc>
+void ObjectAllocator<T_OBJECT_SIZE, T_OBJECT_ALIGNMENT, T_OBJECTS_PER_CHUNK, TIndexType>::deleteInstance(
+	TAlloc& alloc, T* obj)
 {
+	static_assert(alignof(T) <= OBJECT_ALIGNMENT, "Wrong object alignment");
+	static_assert(sizeof(T) <= OBJECT_SIZE, "Wrong object size");
+
 	ANKI_ASSERT(obj);
 
 	// Find the chunk the obj is in
@@ -82,20 +90,20 @@ void ObjectAllocator<T, T_CHUNK_SIZE>::deleteInstance(TAlloc& alloc, Value* obj)
 		const Object* const end = chunk->m_objects.getEnd();
 		if(mem >= begin && mem < end)
 		{
-			// Found it
+			// Found it, remove it from the chunk and maybe delete the chunk
 
-			ANKI_ASSERT(chunk->m_unusedCount < CHUNK_SIZE);
+			ANKI_ASSERT(chunk->m_unusedCount < OBJECTS_PER_CHUNK);
 			const U idx = mem - begin;
 
 			// Destroy the object
-			obj->~Value();
+			obj->~T();
 
 			// Remove from the chunk
 			chunk->m_unusedStack[chunk->m_unusedCount] = idx;
 			++chunk->m_unusedCount;
 
-			// If chunk is empty delete it
-			if(chunk->m_unusedCount == CHUNK_SIZE)
+			// Delete the chunk if it's empty
+			if(chunk->m_unusedCount == OBJECTS_PER_CHUNK)
 			{
 				if(chunk == m_chunksTail)
 				{

+ 20 - 6
tools/scene/ExporterMesh.cpp

@@ -185,7 +185,8 @@ void Exporter::exportMesh(const aiMesh& mesh, const aiMatrix4x4* transform, unsi
 		// Positions
 		auto& posa = header.m_vertexAttributes[anki::VertexAttributeLocation::POSITION];
 		posa.m_bufferBinding = 0;
-		posa.m_format = (maxPositionDistance < 2.0) ? anki::Format::R16G16B16_SFLOAT : anki::Format::R32G32B32_SFLOAT;
+		posa.m_format =
+			(maxPositionDistance < 2.0) ? anki::Format::R16G16B16A16_SFLOAT : anki::Format::R32G32B32_SFLOAT;
 		posa.m_relativeOffset = 0;
 		posa.m_scale = 1.0;
 
@@ -244,9 +245,13 @@ void Exporter::exportMesh(const aiMesh& mesh, const aiMatrix4x4* transform, unsi
 		{
 			header.m_vertexBuffers[0].m_vertexStride = sizeof(float) * 3;
 		}
+		else if(posa.m_format == anki::Format::R16G16B16A16_SFLOAT)
+		{
+			header.m_vertexBuffers[0].m_vertexStride = sizeof(uint16_t) * 4;
+		}
 		else
 		{
-			header.m_vertexBuffers[0].m_vertexStride = sizeof(uint16_t) * 3;
+			assert(0);
 		}
 
 		// 2nd buff has normal + tangent + texcoords
@@ -322,14 +327,23 @@ void Exporter::exportMesh(const aiMesh& mesh, const aiMatrix4x4* transform, unsi
 		{
 			file.write(reinterpret_cast<char*>(&positions[0]), positions.size() * sizeof(positions[0]));
 		}
-		else if(posa.m_format == anki::Format::R16G16B16_SFLOAT)
+		else if(posa.m_format == anki::Format::R16G16B16A16_SFLOAT)
 		{
 			std::vector<uint16_t> pos16;
-			pos16.resize(positions.size());
+			pos16.resize(mesh.mNumVertices * 4);
 
-			for(unsigned i = 0; i < positions.size(); ++i)
+			const float* p32 = &positions[0];
+			const float* p32end = p32 + positions.size();
+			uint16_t* p16 = &pos16[0];
+			while(p32 != p32end)
 			{
-				pos16[i] = anki::F16(positions[i]).toU16();
+				p16[0] = anki::F16(p32[0]).toU16();
+				p16[1] = anki::F16(p32[1]).toU16();
+				p16[2] = anki::F16(p32[2]).toU16();
+				p16[3] = anki::F16(0.0f).toU16();
+
+				p32 += 3;
+				p16 += 4;
 			}
 
 			file.write(reinterpret_cast<char*>(&pos16[0]), pos16.size() * sizeof(pos16[0]));