Browse Source

Add meshlet code up to the scene

Panagiotis Christopoulos Charitos 2 years ago
parent
commit
8f601148b4

+ 7 - 0
AnKi/Core/GpuMemory/UnifiedGeometryBuffer.h

@@ -44,6 +44,8 @@ public:
 		return *this;
 		return *this;
 	}
 	}
 
 
+	operator BufferOffsetRange() const;
+
 	Bool isValid() const
 	Bool isValid() const
 	{
 	{
 		return m_token.m_offset != kMaxPtrSize;
 		return m_token.m_offset != kMaxPtrSize;
@@ -142,6 +144,11 @@ inline UnifiedGeometryBufferAllocation::~UnifiedGeometryBufferAllocation()
 {
 {
 	UnifiedGeometryBuffer::getSingleton().deferredFree(*this);
 	UnifiedGeometryBuffer::getSingleton().deferredFree(*this);
 }
 }
+
+inline UnifiedGeometryBufferAllocation::operator BufferOffsetRange() const
+{
+	return {&UnifiedGeometryBuffer::getSingleton().getBuffer(), getOffset(), getAllocatedSize()};
+}
 /// @}
 /// @}
 
 
 } // end namespace anki
 } // end namespace anki

+ 8 - 0
AnKi/Gr/CommandBuffer.h

@@ -379,6 +379,14 @@ public:
 	/// @param value The value to fill the buffer with.
 	/// @param value The value to fill the buffer with.
 	void fillBuffer(Buffer* buff, PtrSize offset, PtrSize size, U32 value);
 	void fillBuffer(Buffer* buff, PtrSize offset, PtrSize size, U32 value);
 
 
+	/// Fill a buffer with some value.
+	/// @param[in,out] buff The buffer to fill.
+	/// @param value The value to fill the buffer with.
+	void fillBuffer(const BufferOffsetRange& buff, U32 value)
+	{
+		fillBuffer(buff.m_buffer, buff.m_offset, buff.m_range, value);
+	}
+
 	/// Write the occlusion result to buffer.
 	/// Write the occlusion result to buffer.
 	/// @param[in] queries The queries to write the result of.
 	/// @param[in] queries The queries to write the result of.
 	/// @param offset The offset inside the buffer to write the result.
 	/// @param offset The offset inside the buffer to write the result.

+ 52 - 1
AnKi/Resource/MeshBinaryLoader.cpp

@@ -201,7 +201,7 @@ Error MeshBinaryLoader::checkHeader() const
 		return Error::kUserData;
 		return Error::kUserData;
 	}
 	}
 
 
-	if(h.m_meshletPrimitiveFormat != Format::kR8G8B8A8_Uint)
+	if(h.m_meshletPrimitiveFormat != kMeshletPrimitiveFormat)
 	{
 	{
 		ANKI_RESOURCE_LOGE("Wrong format for meshlet primitives");
 		ANKI_RESOURCE_LOGE("Wrong format for meshlet primitives");
 		return Error::kUserData;
 		return Error::kUserData;
@@ -317,6 +317,57 @@ Error MeshBinaryLoader::storeVertexBuffer(U32 lod, U32 bufferIdx, void* ptr, Ptr
 	return Error::kNone;
 	return Error::kNone;
 }
 }
 
 
+Error MeshBinaryLoader::storeMeshletIndicesBuffer(U32 lod, void* ptr, PtrSize size)
+{
+	ANKI_ASSERT(ptr);
+	ANKI_ASSERT(isLoaded());
+	ANKI_ASSERT(size == getMeshletPrimitivesBufferSize(lod));
+	ANKI_ASSERT(lod < m_header.m_lodCount);
+
+	PtrSize seek = sizeof(m_header) + m_subMeshes.getSizeInBytes();
+
+	for(U32 l = lod + 1; l < m_header.m_lodCount; ++l)
+	{
+		seek += getLodBuffersSize(l);
+	}
+
+	for(U32 i = 0; i < m_header.m_vertexBuffers.getSize(); ++i)
+	{
+		seek += getVertexBufferSize(lod, i);
+	}
+
+	seek += getMeshletsBufferSize(lod);
+
+	ANKI_CHECK(m_file->seek(seek, FileSeekOrigin::kBeginning));
+	ANKI_CHECK(m_file->read(ptr, size));
+
+	return Error::kNone;
+}
+
+Error MeshBinaryLoader::storeMeshletBuffer(U32 lod, WeakArray<MeshBinaryMeshlet> out)
+{
+	ANKI_ASSERT(isLoaded());
+	ANKI_ASSERT(out.getSizeInBytes() == getMeshletsBufferSize(lod));
+	ANKI_ASSERT(lod < m_header.m_lodCount);
+
+	PtrSize seek = sizeof(m_header) + m_subMeshes.getSizeInBytes();
+
+	for(U32 l = lod + 1; l < m_header.m_lodCount; ++l)
+	{
+		seek += getLodBuffersSize(l);
+	}
+
+	for(U32 i = 0; i < m_header.m_vertexBuffers.getSize(); ++i)
+	{
+		seek += getVertexBufferSize(lod, i);
+	}
+
+	ANKI_CHECK(m_file->seek(seek, FileSeekOrigin::kBeginning));
+	ANKI_CHECK(m_file->read(&out[0], out.getSizeInBytes()));
+
+	return Error::kNone;
+}
+
 Error MeshBinaryLoader::storeIndicesAndPosition(U32 lod, ResourceDynamicArray<U32>& indices, ResourceDynamicArray<Vec3>& positions)
 Error MeshBinaryLoader::storeIndicesAndPosition(U32 lod, ResourceDynamicArray<U32>& indices, ResourceDynamicArray<Vec3>& positions)
 {
 {
 	ANKI_ASSERT(isLoaded());
 	ANKI_ASSERT(isLoaded());

+ 5 - 1
AnKi/Resource/MeshBinaryLoader.h

@@ -45,6 +45,10 @@ public:
 
 
 	Error storeVertexBuffer(U32 lod, U32 bufferIdx, void* ptr, PtrSize size);
 	Error storeVertexBuffer(U32 lod, U32 bufferIdx, void* ptr, PtrSize size);
 
 
+	Error storeMeshletIndicesBuffer(U32 lod, void* ptr, PtrSize size);
+
+	Error storeMeshletBuffer(U32 lod, WeakArray<MeshBinaryMeshlet> out);
+
 	/// Instead of calling storeIndexBuffer and storeVertexBuffer use this method to get those buffers into the CPU.
 	/// Instead of calling storeIndexBuffer and storeVertexBuffer use this method to get those buffers into the CPU.
 	Error storeIndicesAndPosition(U32 lod, ResourceDynamicArray<U32>& indices, ResourceDynamicArray<Vec3>& positions);
 	Error storeIndicesAndPosition(U32 lod, ResourceDynamicArray<U32>& indices, ResourceDynamicArray<Vec3>& positions);
 
 
@@ -96,7 +100,7 @@ private:
 	{
 	{
 		ANKI_ASSERT(isLoaded());
 		ANKI_ASSERT(isLoaded());
 		ANKI_ASSERT(lod < m_header.m_lodCount);
 		ANKI_ASSERT(lod < m_header.m_lodCount);
-		return PtrSize(m_header.m_meshletPrimitiveCounts[lod]) * sizeof(U8Vec4);
+		return PtrSize(m_header.m_meshletPrimitiveCounts[lod]) * getFormatInfo(kMeshletPrimitiveFormat).m_texelSize;
 	}
 	}
 
 
 	PtrSize getLodBuffersSize(U32 lod) const;
 	PtrSize getLodBuffersSize(U32 lod) const;

+ 73 - 5
AnKi/Resource/MeshResource.cpp

@@ -139,6 +139,18 @@ Error MeshResource::load(const ResourceFilename& filename, Bool async)
 				UnifiedGeometryBuffer::getSingleton().allocateFormat(kMeshRelatedVertexStreamFormats[stream], lod.m_vertexCount);
 				UnifiedGeometryBuffer::getSingleton().allocateFormat(kMeshRelatedVertexStreamFormats[stream], lod.m_vertexCount);
 		}
 		}
 
 
+		// Meshlet
+		if(GrManager::getSingleton().getDeviceCapabilities().m_meshShaders)
+		{
+			const PtrSize meshletIndicesSize = header.m_meshletPrimitiveCounts[l] * sizeof(U8Vec4);
+			lod.m_meshletIndices = UnifiedGeometryBuffer::getSingleton().allocate(meshletIndicesSize, sizeof(U8Vec4));
+
+			const PtrSize meshletsSize = header.m_meshletCounts[l] * sizeof(Meshlet);
+			lod.m_meshlets = UnifiedGeometryBuffer::getSingleton().allocate(meshletsSize, sizeof(Meshlet));
+
+			lod.m_meshletCount = header.m_meshletCounts[l];
+		}
+
 		// BLAS
 		// BLAS
 		if(rayTracingEnabled)
 		if(rayTracingEnabled)
 		{
 		{
@@ -168,17 +180,21 @@ Error MeshResource::load(const ResourceFilename& filename, Bool async)
 
 
 		for(const Lod& lod : m_lods)
 		for(const Lod& lod : m_lods)
 		{
 		{
-			cmdb->fillBuffer(&UnifiedGeometryBuffer::getSingleton().getBuffer(), lod.m_indexBufferAllocationToken.getOffset(),
-							 PtrSize(lod.m_indexCount) * getIndexSize(m_indexType), 0);
+			cmdb->fillBuffer(lod.m_indexBufferAllocationToken, 0);
 
 
 			for(VertexStreamId stream : EnumIterable(VertexStreamId::kMeshRelatedFirst, VertexStreamId::kMeshRelatedCount))
 			for(VertexStreamId stream : EnumIterable(VertexStreamId::kMeshRelatedFirst, VertexStreamId::kMeshRelatedCount))
 			{
 			{
 				if(header.m_vertexAttributes[stream].m_format != Format::kNone)
 				if(header.m_vertexAttributes[stream].m_format != Format::kNone)
 				{
 				{
-					cmdb->fillBuffer(&UnifiedGeometryBuffer::getSingleton().getBuffer(), lod.m_vertexBuffersAllocationToken[stream].getOffset(),
-									 lod.m_vertexBuffersAllocationToken[stream].getAllocatedSize(), 0);
+					cmdb->fillBuffer(lod.m_vertexBuffersAllocationToken[stream], 0);
 				}
 				}
 			}
 			}
+
+			if(lod.m_meshletIndices.isValid())
+			{
+				cmdb->fillBuffer(lod.m_meshletIndices, 0);
+				cmdb->fillBuffer(lod.m_meshlets, 0);
+			}
 		}
 		}
 
 
 		const BufferBarrierInfo barrier = {&UnifiedGeometryBuffer::getSingleton().getBuffer(), BufferUsageBit::kTransferDestination,
 		const BufferBarrierInfo barrier = {&UnifiedGeometryBuffer::getSingleton().getBuffer(), BufferUsageBit::kTransferDestination,
@@ -209,7 +225,7 @@ Error MeshResource::loadAsync(MeshBinaryLoader& loader) const
 	GrManager& gr = GrManager::getSingleton();
 	GrManager& gr = GrManager::getSingleton();
 	TransferGpuAllocator& transferAlloc = ResourceManager::getSingleton().getTransferGpuAllocator();
 	TransferGpuAllocator& transferAlloc = ResourceManager::getSingleton().getTransferGpuAllocator();
 
 
-	Array<TransferGpuAllocatorHandle, kMaxLodCount*(U32(VertexStreamId::kMeshRelatedCount) + 1)> handles;
+	Array<TransferGpuAllocatorHandle, kMaxLodCount*(U32(VertexStreamId::kMeshRelatedCount) + 1 + 2)> handles;
 	U32 handleCount = 0;
 	U32 handleCount = 0;
 
 
 	Buffer* unifiedGeometryBuffer = &UnifiedGeometryBuffer::getSingleton().getBuffer();
 	Buffer* unifiedGeometryBuffer = &UnifiedGeometryBuffer::getSingleton().getBuffer();
@@ -266,6 +282,58 @@ Error MeshResource::loadAsync(MeshBinaryLoader& loader) const
 			cmdb->copyBufferToBuffer(&handle.getBuffer(), handle.getOffset(), unifiedGeometryBuffer,
 			cmdb->copyBufferToBuffer(&handle.getBuffer(), handle.getOffset(), unifiedGeometryBuffer,
 									 lod.m_vertexBuffersAllocationToken[stream].getOffset(), handle.getRange());
 									 lod.m_vertexBuffersAllocationToken[stream].getOffset(), handle.getRange());
 		}
 		}
+
+		if(lod.m_meshlets.isValid())
+		{
+			// Indices
+			TransferGpuAllocatorHandle& handle = handles[handleCount++];
+			const PtrSize primitivesSize = lod.m_meshletIndices.getAllocatedSize();
+			ANKI_CHECK(transferAlloc.allocate(primitivesSize, handle));
+			ANKI_CHECK(loader.storeMeshletIndicesBuffer(lodIdx, handle.getMappedMemory(), primitivesSize));
+
+			cmdb->copyBufferToBuffer(&handle.getBuffer(), handle.getOffset(), unifiedGeometryBuffer, lod.m_meshletIndices.getOffset(),
+									 handle.getRange());
+
+			// Meshlets
+			ResourceDynamicArray<MeshBinaryMeshlet> binaryMeshlets;
+			binaryMeshlets.resize(loader.getHeader().m_meshletCounts[lodIdx]);
+			ANKI_CHECK(loader.storeMeshletBuffer(lodIdx, WeakArray(binaryMeshlets)));
+
+			TransferGpuAllocatorHandle& handle2 = handles[handleCount++];
+			ANKI_CHECK(transferAlloc.allocate(lod.m_meshlets.getAllocatedSize(), handle2));
+			WeakArray<Meshlet> outMeshlets(static_cast<Meshlet*>(handle2.getMappedMemory()), loader.getHeader().m_meshletCounts[lodIdx]);
+
+			for(U32 i = 0; i < binaryMeshlets.getSize(); ++i)
+			{
+				const MeshBinaryMeshlet& inMeshlet = binaryMeshlets[i];
+				Meshlet& outMeshlet = outMeshlets[i];
+
+				outMeshlet = {};
+				for(VertexStreamId stream : EnumIterable(VertexStreamId::kMeshRelatedFirst, VertexStreamId::kMeshRelatedCount))
+				{
+					if(!(m_presentVertStreams & VertexStreamMask(1u << stream)))
+					{
+						continue;
+					}
+
+					outMeshlet.m_vertexOffsets[U32(stream)] =
+						lod.m_vertexBuffersAllocationToken[stream].getOffset()
+						+ inMeshlet.m_firstVertex * getFormatInfo(kMeshRelatedVertexStreamFormats[stream]).m_texelSize;
+				}
+
+				outMeshlet.m_firstPrimitive =
+					lod.m_meshletIndices.getOffset() + inMeshlet.m_firstPrimitive * getFormatInfo(kMeshletPrimitiveFormat).m_texelSize;
+				outMeshlet.m_primitiveCount_R16_Uint_vertexCount_R16_Uint = (inMeshlet.m_primitiveCount << 16u) & inMeshlet.m_vertexCount;
+				outMeshlet.m_sphereCenter = inMeshlet.m_boundingVolume.m_sphereCenter;
+				outMeshlet.m_sphereRadius = inMeshlet.m_boundingVolume.m_sphereRadius;
+				outMeshlet.m_coneApex = inMeshlet.m_coneApex;
+				outMeshlet.m_coneDirection_R8G8B8_Snorm_coneCosOfHalfAngle_R8_Snorm =
+					packSnorm4x8(Vec4(inMeshlet.m_coneDirection, cos(inMeshlet.m_coneAngle / 2.0f)));
+			}
+
+			cmdb->copyBufferToBuffer(&handle2.getBuffer(), handle2.getOffset(), unifiedGeometryBuffer, lod.m_meshlets.getOffset(),
+									 handle2.getRange());
+		}
 	}
 	}
 
 
 	if(gr.getDeviceCapabilities().m_rayTracingEnabled)
 	if(gr.getDeviceCapabilities().m_rayTracingEnabled)

+ 11 - 0
AnKi/Resource/MeshResource.h

@@ -68,6 +68,13 @@ public:
 		vertexCount = m_lods[lod].m_vertexCount;
 		vertexCount = m_lods[lod].m_vertexCount;
 	}
 	}
 
 
+	void getMeshletInfo(U32 lod, PtrSize& meshletOffset, U32& meshletCount)
+	{
+		meshletOffset = m_lods[lod].m_meshlets.getOffset();
+		ANKI_ASSERT(m_lods[lod].m_meshletCount);
+		meshletCount = m_lods[lod].m_meshletCount;
+	}
+
 	const AccelerationStructurePtr& getBottomLevelAccelerationStructure(U32 lod) const
 	const AccelerationStructurePtr& getBottomLevelAccelerationStructure(U32 lod) const
 	{
 	{
 		ANKI_ASSERT(m_lods[lod].m_blas);
 		ANKI_ASSERT(m_lods[lod].m_blas);
@@ -105,8 +112,12 @@ private:
 		UnifiedGeometryBufferAllocation m_indexBufferAllocationToken;
 		UnifiedGeometryBufferAllocation m_indexBufferAllocationToken;
 		Array<UnifiedGeometryBufferAllocation, U32(VertexStreamId::kMeshRelatedCount)> m_vertexBuffersAllocationToken;
 		Array<UnifiedGeometryBufferAllocation, U32(VertexStreamId::kMeshRelatedCount)> m_vertexBuffersAllocationToken;
 
 
+		UnifiedGeometryBufferAllocation m_meshletIndices;
+		UnifiedGeometryBufferAllocation m_meshlets;
+
 		U32 m_indexCount = 0;
 		U32 m_indexCount = 0;
 		U32 m_vertexCount = 0;
 		U32 m_vertexCount = 0;
+		U32 m_meshletCount = 0;
 
 
 		AccelerationStructurePtr m_blas;
 		AccelerationStructurePtr m_blas;
 	};
 	};

+ 17 - 23
AnKi/Resource/ModelResource.cpp

@@ -11,29 +11,6 @@
 
 
 namespace anki {
 namespace anki {
 
 
-void ModelPatch::getRenderingInfo(const RenderingKey& key, ModelRenderingInfo& inf) const
-{
-	ANKI_ASSERT(!(!supportsSkinning() && key.getSkinned()));
-	const U32 meshLod = min<U32>(key.getLod(), m_meshLodCount - 1);
-
-	// Vertex attributes & bindings
-	{
-		inf.m_indexBufferOffset = m_lodInfos[meshLod].m_indexBufferOffset;
-		inf.m_indexType = IndexType::kU16;
-		inf.m_firstIndex = m_lodInfos[meshLod].m_firstIndex;
-		inf.m_indexCount = m_lodInfos[meshLod].m_indexCount;
-
-		for(VertexStreamId stream : EnumIterable(VertexStreamId::kMeshRelatedFirst, VertexStreamId::kMeshRelatedCount))
-		{
-			inf.m_vertexBufferOffsets[stream] = m_lodInfos[meshLod].m_vertexBufferOffsets[stream];
-		}
-	}
-
-	// Get program
-	const MaterialVariant& variant = m_mtl->getOrCreateVariant(key);
-	inf.m_program = variant.getShaderProgram();
-}
-
 void ModelPatch::getGeometryInfo(U32 lod, ModelPatchGeometryInfo& inf) const
 void ModelPatch::getGeometryInfo(U32 lod, ModelPatchGeometryInfo& inf) const
 {
 {
 	lod = min<U32>(lod, m_meshLodCount - 1);
 	lod = min<U32>(lod, m_meshLodCount - 1);
@@ -51,6 +28,18 @@ void ModelPatch::getGeometryInfo(U32 lod, ModelPatchGeometryInfo& inf) const
 	{
 	{
 		inf.m_blas = m_mesh->getBottomLevelAccelerationStructure(lod);
 		inf.m_blas = m_mesh->getBottomLevelAccelerationStructure(lod);
 	}
 	}
+
+	if(m_lodInfos[lod].m_meshletCount != kMaxU32)
+	{
+		ANKI_ASSERT(m_lodInfos[lod].m_meshletsOffset != kMaxPtrSize);
+		inf.m_meshletCount = m_lodInfos[lod].m_meshletCount;
+		inf.m_meshletsOffset = m_lodInfos[lod].m_meshletsOffset;
+	}
+	else
+	{
+		inf.m_meshletCount = 0;
+		inf.m_meshletsOffset = kMaxPtrSize;
+	}
 }
 }
 
 
 void ModelPatch::getRayTracingInfo(const RenderingKey& key, ModelRayTracingInfo& info) const
 void ModelPatch::getRayTracingInfo(const RenderingKey& key, ModelRayTracingInfo& info) const
@@ -121,6 +110,11 @@ Error ModelPatch::init([[maybe_unused]] ModelResource* model, CString meshFName,
 				lod.m_vertexBufferOffsets[stream] = kMaxPtrSize;
 				lod.m_vertexBufferOffsets[stream] = kMaxPtrSize;
 			}
 			}
 		}
 		}
+
+		if(GrManager::getSingleton().getDeviceCapabilities().m_meshShaders)
+		{
+			m_mesh->getMeshletInfo(l, lod.m_meshletsOffset, lod.m_meshletCount);
+		}
 	}
 	}
 
 
 	return Error::kNone;
 	return Error::kNone;

+ 6 - 19
AnKi/Resource/ModelResource.h

@@ -17,22 +17,6 @@ namespace anki {
 /// @addtogroup resource
 /// @addtogroup resource
 /// @{
 /// @{
 
 
-/// @memberof ModelResource
-/// Part of the information required render the model.
-class ModelRenderingInfo
-{
-public:
-	ShaderProgramPtr m_program;
-
-	PtrSize m_indexBufferOffset;
-	IndexType m_indexType;
-	U32 m_firstIndex;
-	U32 m_indexCount;
-
-	/// Offset to the vertex buffer or kMaxPtrSize if stream is not present.
-	Array<PtrSize, U32(VertexStreamId::kMeshRelatedCount)> m_vertexBufferOffsets;
-};
-
 /// Part of the information required to create a TLAS and a SBT.
 /// Part of the information required to create a TLAS and a SBT.
 /// @memberof ModelResource
 /// @memberof ModelResource
 class ModelRayTracingInfo
 class ModelRayTracingInfo
@@ -56,6 +40,9 @@ public:
 	/// Offset to the vertex buffer or kMaxPtrSize if stream is not present.
 	/// Offset to the vertex buffer or kMaxPtrSize if stream is not present.
 	Array<PtrSize, U32(VertexStreamId::kMeshRelatedCount)> m_vertexBufferOffsets;
 	Array<PtrSize, U32(VertexStreamId::kMeshRelatedCount)> m_vertexBufferOffsets;
 
 
+	PtrSize m_meshletsOffset;
+	U32 m_meshletCount;
+
 	AccelerationStructurePtr m_blas;
 	AccelerationStructurePtr m_blas;
 };
 };
 
 
@@ -80,9 +67,6 @@ public:
 		return m_aabb;
 		return m_aabb;
 	}
 	}
 
 
-	/// Get information for rendering.
-	void getRenderingInfo(const RenderingKey& key, ModelRenderingInfo& inf) const;
-
 	void getGeometryInfo(U32 lod, ModelPatchGeometryInfo& inf) const;
 	void getGeometryInfo(U32 lod, ModelPatchGeometryInfo& inf) const;
 
 
 	/// Get the ray tracing info.
 	/// Get the ray tracing info.
@@ -97,6 +81,9 @@ private:
 		U32 m_indexCount = kMaxU32;
 		U32 m_indexCount = kMaxU32;
 
 
 		Array<PtrSize, U32(VertexStreamId::kMeshRelatedCount)> m_vertexBufferOffsets = {};
 		Array<PtrSize, U32(VertexStreamId::kMeshRelatedCount)> m_vertexBufferOffsets = {};
+
+		PtrSize m_meshletsOffset = kMaxPtrSize;
+		U32 m_meshletCount = kMaxU32;
 	};
 	};
 
 
 #if ANKI_ASSERTIONS_ENABLED
 #if ANKI_ASSERTIONS_ENABLED

+ 5 - 0
AnKi/Resource/TransferGpuAllocator.h

@@ -48,6 +48,11 @@ public:
 		return *this;
 		return *this;
 	}
 	}
 
 
+	operator BufferOffsetRange() const
+	{
+		return {m_buffer.get(), m_offsetInBuffer, m_range};
+	}
+
 	Buffer& getBuffer() const
 	Buffer& getBuffer() const
 	{
 	{
 		return *m_buffer;
 		return *m_buffer;

+ 7 - 0
AnKi/Scene/Components/ModelComponent.cpp

@@ -180,6 +180,13 @@ Error ModelComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 					memcpy(&meshLod.m_blasAddress, &address, sizeof(meshLod.m_blasAddress));
 					memcpy(&meshLod.m_blasAddress, &address, sizeof(meshLod.m_blasAddress));
 					meshLod.m_tlasInstanceMask = 0xFFFFFFFF;
 					meshLod.m_tlasInstanceMask = 0xFFFFFFFF;
 				}
 				}
+
+				if(inf.m_meshletCount)
+				{
+					ANKI_ASSERT((inf.m_meshletsOffset % sizeof(Meshlet)) == 0);
+					meshLod.m_firstMeshlet = U32(inf.m_meshletsOffset / sizeof(Meshlet));
+					meshLod.m_meshletCount = inf.m_meshletCount;
+				}
 			}
 			}
 
 
 			// Copy the last LOD to the rest just in case
 			// Copy the last LOD to the rest just in case

+ 6 - 2
AnKi/Shaders/Include/GpuSceneTypes.h

@@ -56,14 +56,18 @@ struct GpuSceneMeshLod
 	U32 m_indexCount;
 	U32 m_indexCount;
 	U32 m_firstIndex; // In sizeof(indexType)
 	U32 m_firstIndex; // In sizeof(indexType)
 
 
+	UVec2 m_padding1;
+	U32 m_firstMeshlet; // In sizeof(Meshlet)
+	U32 m_meshletCount;
+
 	Vec3 m_positionTranslation;
 	Vec3 m_positionTranslation;
 	F32 m_positionScale;
 	F32 m_positionScale;
 
 
 	UVec2 m_blasAddress;
 	UVec2 m_blasAddress;
 	U32 m_tlasInstanceMask; ///< Mask that goes to AccelerationStructureInstance::m_instanceCustomIndex24_mask8
 	U32 m_tlasInstanceMask; ///< Mask that goes to AccelerationStructureInstance::m_instanceCustomIndex24_mask8
-	U32 m_padding;
+	U32 m_padding2;
 };
 };
-static_assert(sizeof(GpuSceneMeshLod) == sizeof(Vec4) * 4);
+static_assert(sizeof(GpuSceneMeshLod) == sizeof(Vec4) * 5);
 
 
 struct GpuSceneParticleEmitter
 struct GpuSceneParticleEmitter
 {
 {

+ 17 - 0
AnKi/Shaders/Include/MeshTypes.h

@@ -73,6 +73,8 @@ inline constexpr Array<Format, U32(VertexStreamId::kMeshRelatedCount)> kMeshRela
 inline constexpr Array<Format, U32(VertexStreamId::kParticleRelatedCount)> kParticleRelatedVertexStreamFormats = {
 inline constexpr Array<Format, U32(VertexStreamId::kParticleRelatedCount)> kParticleRelatedVertexStreamFormats = {
 	Format::kR32G32B32_Sfloat, Format::kR32G32B32_Sfloat, Format::kR32_Sfloat,      Format::kR32G32B32A32_Sfloat,
 	Format::kR32G32B32_Sfloat, Format::kR32G32B32_Sfloat, Format::kR32_Sfloat,      Format::kR32G32B32A32_Sfloat,
 	Format::kR32_Sfloat,       Format::kR32_Sfloat,       Format::kR32G32B32_Sfloat};
 	Format::kR32_Sfloat,       Format::kR32_Sfloat,       Format::kR32G32B32_Sfloat};
+
+constexpr Format kMeshletPrimitiveFormat = Format::kR8G8B8A8_Uint;
 #endif
 #endif
 
 
 struct UnpackedMeshVertex
 struct UnpackedMeshVertex
@@ -85,4 +87,19 @@ struct UnpackedMeshVertex
 	RVec4 m_boneWeights;
 	RVec4 m_boneWeights;
 };
 };
 
 
+struct Meshlet
+{
+	U32 m_vertexOffsets[(U32)VertexStreamId::kMeshRelatedCount];
+	U32 m_firstPrimitive; // In size of kMeshletPrimitiveFormat
+	U32 m_primitiveCount_R16_Uint_vertexCount_R16_Uint;
+
+	Vec3 m_sphereCenter;
+	F32 m_sphereRadius;
+
+	Vec3 m_coneApex;
+	U32 m_coneDirection_R8G8B8_Snorm_coneCosOfHalfAngle_R8_Snorm;
+};
+// Power of 2 because the sizeof will be used as allocation alignment and allocation alignments need to be power of 2
+static_assert(isPowerOfTwo(sizeof(Meshlet)));
+
 ANKI_END_NAMESPACE
 ANKI_END_NAMESPACE