Browse Source

Move the components into arrays

Panagiotis Christopoulos Charitos 2 years ago
parent
commit
ef26f4de3c
67 changed files with 515 additions and 648 deletions
  1. 2 2
      AnKi/Gr/RenderGraph.cpp
  2. 2 2
      AnKi/Gr/Vulkan/Pipeline.cpp
  3. 1 1
      AnKi/Gr/Vulkan/QueryFactory.cpp
  4. 1 1
      AnKi/Gr/Vulkan/ShaderProgramImpl.cpp
  5. 1 1
      AnKi/Renderer/PrimaryNonRenderableVisibility.cpp
  6. 0 5
      AnKi/Renderer/PrimaryNonRenderableVisibility.h
  7. 3 6
      AnKi/Renderer/Utils/GpuVisibility.cpp
  8. 1 1
      AnKi/Renderer/Utils/GpuVisibility.h
  9. 2 2
      AnKi/Resource/MaterialResource.cpp
  10. 2 2
      AnKi/Resource/ShaderProgramResource.cpp
  11. 1 1
      AnKi/Resource/ShaderProgramResourceSystem.cpp
  12. 0 1
      AnKi/Scene/Common.h
  13. 2 2
      AnKi/Scene/Components/BodyComponent.cpp
  14. 2 2
      AnKi/Scene/Components/BodyComponent.h
  15. 1 1
      AnKi/Scene/Components/CameraComponent.cpp
  16. 1 1
      AnKi/Scene/Components/CameraComponent.h
  17. 1 1
      AnKi/Scene/Components/DecalComponent.cpp
  18. 1 1
      AnKi/Scene/Components/DecalComponent.h
  19. 1 1
      AnKi/Scene/Components/FogDensityComponent.cpp
  20. 1 1
      AnKi/Scene/Components/FogDensityComponent.h
  21. 3 3
      AnKi/Scene/Components/GlobalIlluminationProbeComponent.cpp
  22. 4 2
      AnKi/Scene/Components/GlobalIlluminationProbeComponent.h
  23. 1 1
      AnKi/Scene/Components/JointComponent.cpp
  24. 3 3
      AnKi/Scene/Components/JointComponent.h
  25. 1 1
      AnKi/Scene/Components/LensFlareComponent.cpp
  26. 2 2
      AnKi/Scene/Components/LensFlareComponent.h
  27. 8 8
      AnKi/Scene/Components/LightComponent.cpp
  28. 6 4
      AnKi/Scene/Components/LightComponent.h
  29. 2 2
      AnKi/Scene/Components/ModelComponent.cpp
  30. 2 2
      AnKi/Scene/Components/ModelComponent.h
  31. 2 2
      AnKi/Scene/Components/MoveComponent.h
  32. 1 1
      AnKi/Scene/Components/ParticleEmitterComponent.cpp
  33. 1 1
      AnKi/Scene/Components/ParticleEmitterComponent.h
  34. 1 1
      AnKi/Scene/Components/PlayerControllerComponent.cpp
  35. 1 1
      AnKi/Scene/Components/PlayerControllerComponent.h
  36. 5 5
      AnKi/Scene/Components/ReflectionProbeComponent.cpp
  37. 3 2
      AnKi/Scene/Components/ReflectionProbeComponent.h
  38. 0 112
      AnKi/Scene/Components/SceneComponent.cpp
  39. 45 187
      AnKi/Scene/Components/SceneComponent.h
  40. 20 20
      AnKi/Scene/Components/SceneComponentClasses.def.h
  41. 0 30
      AnKi/Scene/Components/SceneComponentStatics.cpp
  42. 0 21
      AnKi/Scene/Components/SceneComponentVirtuals.defs.h
  43. 1 1
      AnKi/Scene/Components/ScriptComponent.cpp
  44. 1 1
      AnKi/Scene/Components/ScriptComponent.h
  45. 1 1
      AnKi/Scene/Components/SkinComponent.cpp
  46. 1 1
      AnKi/Scene/Components/SkinComponent.h
  47. 1 1
      AnKi/Scene/Components/SkyboxComponent.cpp
  48. 1 1
      AnKi/Scene/Components/SkyboxComponent.h
  49. 1 1
      AnKi/Scene/Components/TriggerComponent.cpp
  50. 1 1
      AnKi/Scene/Components/TriggerComponent.h
  51. 2 2
      AnKi/Scene/Components/UiComponent.h
  52. 1 5
      AnKi/Scene/Forward.h
  53. 2 2
      AnKi/Scene/GpuSceneArray.inl.h
  54. 20 2
      AnKi/Scene/SceneGraph.cpp
  55. 23 0
      AnKi/Scene/SceneGraph.h
  56. 46 9
      AnKi/Scene/SceneNode.cpp
  57. 12 17
      AnKi/Scene/SceneNode.h
  58. 5 4
      AnKi/Scene/Spatial.h
  59. 22 22
      AnKi/Scene/Visibility.cpp
  60. 4 0
      AnKi/Shaders/Intellisense.hlsl
  61. 4 4
      AnKi/Util/BitSet.h
  62. 60 92
      AnKi/Util/BlockArray.h
  63. 106 25
      AnKi/Util/BlockArray.inl.h
  64. 6 1
      AnKi/Util/Forward.h
  65. 2 2
      AnKi/Util/ThreadPosix.cpp
  66. 2 2
      AnKi/Util/ThreadWindows.cpp
  67. 53 5
      Tests/Util/BlockArray.cpp

+ 2 - 2
AnKi/Gr/RenderGraph.cpp

@@ -546,7 +546,7 @@ Bool RenderGraph::passADependsOnB(const RenderPassDescriptionBase& a, const Rend
 
 		const BitSet<kMaxRenderGraphRenderTargets, U64> fullDep = aReadBWrite | aWriteBRead | aWriteBWrite;
 
-		if(fullDep.getAny())
+		if(fullDep.getAnySet())
 		{
 			// There might be an overlap
 
@@ -588,7 +588,7 @@ Bool RenderGraph::passADependsOnB(const RenderPassDescriptionBase& a, const Rend
 
 		const BitSet<kMaxRenderGraphBuffers, U64> fullDep = aReadBWrite | aWriteBRead | aWriteBWrite;
 
-		if(fullDep.getAny())
+		if(fullDep.getAnySet())
 		{
 			// There might be an overlap
 

+ 2 - 2
AnKi/Gr/Vulkan/Pipeline.cpp

@@ -45,7 +45,7 @@ Bool PipelineStateTracker::updateHashes()
 	}
 
 	// Vertex
-	if(m_dirty.m_attribs.getAny() || m_dirty.m_vertBindings.getAny())
+	if(m_dirty.m_attribs.getAnySet() || m_dirty.m_vertBindings.getAnySet())
 	{
 		for(U i = 0; i < kMaxVertexAttributes; ++i)
 		{
@@ -340,7 +340,7 @@ const VkGraphicsPipelineCreateInfo& PipelineStateTracker::updatePipelineCreateIn
 		VkPipelineColorBlendStateCreateInfo& colCi = m_ci.m_color;
 		colCi = {};
 		colCi.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
-		colCi.attachmentCount = m_fbColorAttachmentMask.getEnabledBitCount();
+		colCi.attachmentCount = m_fbColorAttachmentMask.getSetBitCount();
 		colCi.pAttachments = &m_ci.m_colAttachments[0];
 
 		for(U i = 0; i < colCi.attachmentCount; ++i)

+ 1 - 1
AnKi/Gr/Vulkan/QueryFactory.cpp

@@ -90,7 +90,7 @@ void QueryFactory::deleteQuery(MicroQuery& handle)
 	{
 		// Delete the chunk
 
-		ANKI_ASSERT(chunk->m_allocatedMask.getAny());
+		ANKI_ASSERT(chunk->m_allocatedMask.getAnySet());
 		vkDestroyQueryPool(getVkDevice(), chunk->m_pool, nullptr);
 
 		m_chunks.erase(chunk);

+ 1 - 1
AnKi/Gr/Vulkan/ShaderProgramImpl.cpp

@@ -182,7 +182,7 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 
 		m_refl.m_colorAttachmentWritemask = static_cast<const ShaderImpl&>(*inf.m_graphicsShaders[ShaderType::kFragment]).m_colorAttachmentWritemask;
 
-		const U32 attachmentCount = m_refl.m_colorAttachmentWritemask.getEnabledBitCount();
+		const U32 attachmentCount = m_refl.m_colorAttachmentWritemask.getSetBitCount();
 		for(U32 i = 0; i < attachmentCount; ++i)
 		{
 			ANKI_ASSERT(m_refl.m_colorAttachmentWritemask.get(i) && "Should write to all attachments");

+ 1 - 1
AnKi/Renderer/PrimaryNonRenderableVisibility.cpp

@@ -83,7 +83,7 @@ void PrimaryNonRenderableVisibility::populateRenderGraph(RenderingContext& ctx)
 		GpuVisibilityNonRenderablesOutput out;
 		getRenderer().getGpuVisibilityNonRenderables().populateRenderGraph(in, out);
 
-		m_runCtx.m_visOutBufferHandle[type] = out.m_bufferHandle;
+		m_runCtx.m_visOutBufferHandle[type] = out.m_visiblesBufferHandle;
 	}
 }
 

+ 0 - 5
AnKi/Renderer/PrimaryNonRenderableVisibility.h

@@ -32,12 +32,7 @@ private:
 	class
 	{
 	public:
-		Array<Buffer*, U32(GpuSceneNonRenderableObjectType::kCount)> m_visOutBuffers = {};
-		Array<PtrSize, U32(GpuSceneNonRenderableObjectType::kCount)> m_visOutBufferOffsets = {};
-		Array<PtrSize, U32(GpuSceneNonRenderableObjectType::kCount)> m_visOutBufferRanges = {};
-
 		Array<BufferHandle, U32(GpuSceneNonRenderableObjectType::kCount)> m_visOutBufferHandle;
-
 		Array<WeakArray<U32>, U32(GpuSceneNonRenderableObjectTypeWithFeedback::kCount)> m_uuids;
 	} m_runCtx;
 };

+ 3 - 6
AnKi/Renderer/Utils/GpuVisibility.cpp

@@ -315,20 +315,17 @@ void GpuVisibilityNonRenderables::populateRenderGraph(GpuVisibilityNonRenderable
 
 	// Allocate memory for the result
 	GpuVisibleTransientMemoryAllocation visibleIndicesAlloc = GpuVisibleTransientMemoryPool::getSingleton().allocate((objCount + 1) * sizeof(U32));
-
 	out.m_visiblesBuffer.m_buffer = visibleIndicesAlloc.m_buffer;
 	out.m_visiblesBuffer.m_offset = visibleIndicesAlloc.m_offset;
 	out.m_visiblesBuffer.m_range = visibleIndicesAlloc.m_size;
-
-	// Import buffers
-	out.m_bufferHandle =
+	out.m_visiblesBufferHandle =
 		rgraph.importBuffer(out.m_visiblesBuffer.m_buffer, BufferUsageBit::kNone, out.m_visiblesBuffer.m_offset, out.m_visiblesBuffer.m_range);
 
 	// Create the renderpass
 	ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass(in.m_passesName);
 
 	pass.newBufferDependency(getRenderer().getGpuSceneBufferHandle(), BufferUsageBit::kStorageComputeRead);
-	pass.newBufferDependency(out.m_bufferHandle, BufferUsageBit::kStorageComputeWrite);
+	pass.newBufferDependency(out.m_visiblesBufferHandle, BufferUsageBit::kStorageComputeWrite);
 
 	if(in.m_hzbRt)
 	{
@@ -341,7 +338,7 @@ void GpuVisibilityNonRenderables::populateRenderGraph(GpuVisibilityNonRenderable
 	}
 
 	pass.setWork([this, objType = in.m_objectType, feedbackBuffer = in.m_cpuFeedbackBuffer, viewProjectionMat = in.m_viewProjectionMat,
-				  visibleIndicesBuffHandle = out.m_bufferHandle, counterBuffer = m_counterBuffer, counterBufferOffset = m_counterBufferOffset,
+				  visibleIndicesBuffHandle = out.m_visiblesBufferHandle, counterBuffer = m_counterBuffer, counterBufferOffset = m_counterBufferOffset,
 				  objCount](RenderPassWorkContext& rgraph) {
 		CommandBuffer& cmdb = *rgraph.m_commandBuffer;
 

+ 1 - 1
AnKi/Renderer/Utils/GpuVisibility.h

@@ -74,7 +74,7 @@ public:
 class GpuVisibilityNonRenderablesOutput
 {
 public:
-	BufferHandle m_bufferHandle; ///< Some buffer handle to be used for tracking. No need to track all buffers.
+	BufferHandle m_visiblesBufferHandle; ///< Buffer handle holding the visible objects. Used for tracking. No need to track all buffers.
 	BufferOffsetRange m_visiblesBuffer;
 };
 

+ 2 - 2
AnKi/Resource/MaterialResource.cpp

@@ -172,9 +172,9 @@ Error MaterialResource::load(const ResourceFilename& filename, Bool async)
 		} while(inputEl);
 	}
 
-	if(varsSet.getEnabledBitCount() != m_vars.getSize())
+	if(varsSet.getSetBitCount() != m_vars.getSize())
 	{
-		ANKI_RESOURCE_LOGE("Forgot to set a default value in %u input variables", U32(m_vars.getSize() - varsSet.getEnabledBitCount()));
+		ANKI_RESOURCE_LOGE("Forgot to set a default value in %u input variables", U32(m_vars.getSize() - varsSet.getSetBitCount()));
 		return Error::kUserData;
 	}
 

+ 2 - 2
AnKi/Resource/ShaderProgramResource.cpp

@@ -196,8 +196,8 @@ Error ShaderProgramResource::parseConst(CString constName, U32& componentIdx, U3
 void ShaderProgramResource::getOrCreateVariant(const ShaderProgramResourceVariantInitInfo& info, const ShaderProgramResourceVariant*& variant) const
 {
 	// Sanity checks
-	ANKI_ASSERT(info.m_setMutators.getEnabledBitCount() == m_mutators.getSize());
-	ANKI_ASSERT(info.m_setConstants.getEnabledBitCount() == m_consts.getSize());
+	ANKI_ASSERT(info.m_setMutators.getSetBitCount() == m_mutators.getSize());
+	ANKI_ASSERT(info.m_setConstants.getSetBitCount() == m_consts.getSize());
 
 	// Compute variant hash
 	U64 hash = 0;

+ 1 - 1
AnKi/Resource/ShaderProgramResourceSystem.cpp

@@ -365,7 +365,7 @@ Error ShaderProgramResourceSystem::createRayTracingPrograms(ResourceDynamicArray
 				return Error::kUserData;
 			}
 
-			if(inLib.m_rayTypeCount != inLib.m_rayTypeMask.getEnabledBitCount())
+			if(inLib.m_rayTypeCount != inLib.m_rayTypeMask.getSetBitCount())
 			{
 				ANKI_RESOURCE_LOGE("Ray types are not contiguous for library: %s", inLib.m_name.cstr());
 				return Error::kUserData;

+ 0 - 1
AnKi/Scene/Common.h

@@ -8,7 +8,6 @@
 #include <AnKi/Util/String.h>
 #include <AnKi/Scene/Forward.h>
 #include <AnKi/Shaders/Include/GpuSceneTypes.h>
-#include <functional>
 
 namespace anki {
 

+ 2 - 2
AnKi/Scene/Components/BodyComponent.cpp

@@ -15,7 +15,7 @@
 namespace anki {
 
 BodyComponent::BodyComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
+	: SceneComponent(node, kClassType)
 	, m_node(node)
 {
 	node->setIgnoreParentTransform(true);
@@ -123,7 +123,7 @@ Error BodyComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 
 void BodyComponent::onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added)
 {
-	if(other->getClassId() != ModelComponent::getStaticClassId())
+	if(other->getType() != SceneComponentType::kModel)
 	{
 		return;
 	}

+ 2 - 2
AnKi/Scene/Components/BodyComponent.h

@@ -56,9 +56,9 @@ private:
 	PhysicsBodyPtr m_body;
 	Bool m_dirty = true;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 
-	void onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added);
+	void onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added) override;
 };
 /// @}
 

+ 1 - 1
AnKi/Scene/Components/CameraComponent.cpp

@@ -29,7 +29,7 @@ static NumericCVar<F32>
 											"Every object that its distance from the camera is bellow that value will take part in ray tracing");
 
 CameraComponent::CameraComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
+	: SceneComponent(node, kClassType)
 {
 	// Init main frustum
 	m_frustum.init(FrustumType::kPerspective);

+ 1 - 1
AnKi/Scene/Components/CameraComponent.h

@@ -137,7 +137,7 @@ private:
 
 	Bool m_usesExtendedFrustum : 1 = false;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 
 	Transform computeExtendedFrustumTransform(const Transform& cameraTransform) const;
 };

+ 1 - 1
AnKi/Scene/Components/DecalComponent.cpp

@@ -12,7 +12,7 @@
 namespace anki {
 
 DecalComponent::DecalComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
+	: SceneComponent(node, kClassType)
 	, m_spatial(this)
 {
 	m_gpuSceneDecal.allocate();

+ 1 - 1
AnKi/Scene/Components/DecalComponent.h

@@ -100,7 +100,7 @@ private:
 
 	void setLayer(CString fname, F32 blendFactor, LayerType type);
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 1 - 1
AnKi/Scene/Components/FogDensityComponent.cpp

@@ -11,7 +11,7 @@
 namespace anki {
 
 FogDensityComponent::FogDensityComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
+	: SceneComponent(node, kClassType)
 	, m_spatial(this)
 {
 	m_gpuSceneVolume.allocate();

+ 1 - 1
AnKi/Scene/Components/FogDensityComponent.h

@@ -115,7 +115,7 @@ private:
 	Bool m_isBox = true;
 	Bool m_dirty = true;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 
 } // end namespace anki

+ 3 - 3
AnKi/Scene/Components/GlobalIlluminationProbeComponent.cpp

@@ -13,7 +13,7 @@
 namespace anki {
 
 GlobalIlluminationProbeComponent::GlobalIlluminationProbeComponent(SceneNode* node)
-	: QueryableSceneComponent<GlobalIlluminationProbeComponent>(node, getStaticClassId())
+	: SceneComponent(node, kClassType)
 	, m_spatial(this)
 {
 	for(U32 i = 0; i < 6; ++i)
@@ -108,7 +108,7 @@ Error GlobalIlluminationProbeComponent::update(SceneComponentUpdateInfo& info, B
 		m_spatial.setBoundingShape(aabb);
 
 		// New UUID
-		refreshUuid();
+		m_uuid = SceneGraph::getSingleton().getNewUuid();
 
 		// Upload to the GPU scene
 		GpuSceneGlobalIlluminationProbe gpuProbe;
@@ -117,7 +117,7 @@ Error GlobalIlluminationProbeComponent::update(SceneComponentUpdateInfo& info, B
 		gpuProbe.m_volumeTexture = m_volTexBindlessIdx;
 		gpuProbe.m_halfTexelSizeU = 1.0f / (F32(m_cellCounts.y()) * 6.0f) / 2.0f;
 		gpuProbe.m_fadeDistance = m_fadeDistance;
-		gpuProbe.m_uuid = getUuid();
+		gpuProbe.m_uuid = m_uuid;
 		m_gpuSceneProbe.uploadToGpuScene(gpuProbe);
 	}
 

+ 4 - 2
AnKi/Scene/Components/GlobalIlluminationProbeComponent.h

@@ -17,7 +17,7 @@ namespace anki {
 /// @{
 
 /// Global illumination probe component. It's an axis aligned box divided into cells.
-class GlobalIlluminationProbeComponent : public QueryableSceneComponent<GlobalIlluminationProbeComponent>
+class GlobalIlluminationProbeComponent : public SceneComponent
 {
 	ANKI_SCENE_COMPONENT(GlobalIlluminationProbeComponent)
 
@@ -121,6 +121,8 @@ private:
 
 	U32 m_cellIdxToRefresh = 0;
 
+	U32 m_uuid = 0;
+
 	Bool m_shapeDirty = true;
 
 	/// Recalc come values.
@@ -132,7 +134,7 @@ private:
 		m_totalCellCount = m_cellCounts.x() * m_cellCounts.y() * m_cellCounts.z();
 	}
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 1 - 1
AnKi/Scene/Components/JointComponent.cpp

@@ -139,7 +139,7 @@ Error JointComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 
 void JointComponent::onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added)
 {
-	if(other->getClassId() != BodyComponent::getStaticClassId())
+	if(other->getType() != SceneComponentType::kBody)
 	{
 		return;
 	}

+ 3 - 3
AnKi/Scene/Components/JointComponent.h

@@ -20,7 +20,7 @@ class JointComponent : public SceneComponent
 
 public:
 	JointComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId())
+		: SceneComponent(node, kClassType)
 		, m_node(node)
 	{
 	}
@@ -51,9 +51,9 @@ private:
 	template<typename TJoint, typename... TArgs>
 	void newJoint(const Vec3& relPosFactor, F32 brakingImpulse, TArgs&&... args);
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 
-	void onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added);
+	void onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added) override;
 };
 /// @}
 

+ 1 - 1
AnKi/Scene/Components/LensFlareComponent.cpp

@@ -11,7 +11,7 @@
 namespace anki {
 
 LensFlareComponent::LensFlareComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
+	: SceneComponent(node, kClassType)
 	, m_spatial(this)
 {
 }

+ 2 - 2
AnKi/Scene/Components/LensFlareComponent.h

@@ -91,9 +91,9 @@ private:
 
 	Bool m_dirty = true;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 
-	void onDestroy(SceneNode& node);
+	void onDestroy(SceneNode& node) override;
 };
 /// @}
 

+ 8 - 8
AnKi/Scene/Components/LightComponent.cpp

@@ -17,7 +17,7 @@
 namespace anki {
 
 LightComponent::LightComponent(SceneNode* node)
-	: QueryableSceneComponent<LightComponent>(node, getStaticClassId())
+	: SceneComponent(node, kClassType)
 	, m_spatial(this)
 	, m_type(LightComponentType::kPoint)
 {
@@ -106,11 +106,11 @@ Error LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 
 		if(m_shadow && shapeUpdated)
 		{
-			refreshUuid();
+			m_uuid = SceneGraph::getSingleton().getNewUuid();
 		}
 		else if(!m_shadow)
 		{
-			releaseUuid();
+			m_uuid = 0;
 		}
 
 		// Upload to the GPU scene
@@ -120,7 +120,7 @@ Error LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 		gpuLight.m_diffuseColor = m_diffColor.xyz();
 		gpuLight.m_squareRadiusOverOne = 1.0f / (m_point.m_radius * m_point.m_radius);
 		gpuLight.m_shadow = m_shadow;
-		gpuLight.m_uuid = (m_shadow) ? getUuid() : 0;
+		gpuLight.m_uuid = (m_shadow) ? m_uuid : 0;
 		if(!m_gpuSceneLight.isValid())
 		{
 			m_gpuSceneLight.allocate();
@@ -177,11 +177,11 @@ Error LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 
 		if(m_shadow && shapeUpdated)
 		{
-			refreshUuid();
+			m_uuid = SceneGraph::getSingleton().getNewUuid();
 		}
 		else if(!m_shadow)
 		{
-			releaseUuid();
+			m_uuid = 0;
 		}
 
 		// Upload to the GPU scene
@@ -198,7 +198,7 @@ Error LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 		gpuLight.m_shadow = m_shadow;
 		gpuLight.m_outerCos = cos(m_spot.m_outerAngle / 2.0f);
 		gpuLight.m_innerCos = cos(m_spot.m_innerAngle / 2.0f);
-		gpuLight.m_uuid = (m_shadow) ? getUuid() : 0;
+		gpuLight.m_uuid = (m_shadow) ? m_uuid : 0;
 		if(!m_gpuSceneLight.isValid())
 		{
 			m_gpuSceneLight.allocate();
@@ -236,7 +236,7 @@ void LightComponent::setupDirectionalLightQueueElement(const Frustum& primaryFru
 
 	const U32 shadowCascadeCount = cascadeFrustums.getSize();
 
-	el.m_uuid = hasUuid() ? getUuid() : 0;
+	el.m_uuid = m_uuid;
 	el.m_diffuseColor = m_diffColor.xyz();
 	el.m_direction = -m_worldTransform.getRotation().getZAxis().xyz();
 	for(U32 i = 0; i < shadowCascadeCount; ++i)

+ 6 - 4
AnKi/Scene/Components/LightComponent.h

@@ -29,7 +29,7 @@ enum class LightComponentType : U8
 };
 
 /// Light component. Contains all the info of lights.
-class LightComponent : public QueryableSceneComponent<LightComponent>
+class LightComponent : public SceneComponent
 {
 	ANKI_SCENE_COMPONENT(LightComponent)
 
@@ -119,7 +119,7 @@ public:
 	void setupPointLightQueueElement(PointLightQueueElement& el) const
 	{
 		ANKI_ASSERT(m_type == LightComponentType::kPoint);
-		el.m_uuid = getUuid();
+		el.m_uuid = m_uuid;
 		el.m_worldPosition = m_worldTransform.getOrigin().xyz();
 		el.m_radius = m_point.m_radius;
 		el.m_diffuseColor = m_diffColor.xyz();
@@ -130,7 +130,7 @@ public:
 	void setupSpotLightQueueElement(SpotLightQueueElement& el) const
 	{
 		ANKI_ASSERT(m_type == LightComponentType::kSpot);
-		el.m_uuid = getUuid();
+		el.m_uuid = m_uuid;
 		el.m_worldTransform = Mat4(m_worldTransform);
 		el.m_textureMatrix = m_spot.m_textureMat;
 		el.m_distance = m_spot.m_distance;
@@ -185,6 +185,8 @@ private:
 
 	GpuSceneArrays::Light::Allocation m_gpuSceneLight;
 
+	U32 m_uuid = 0;
+
 	LightComponentType m_type;
 
 	U8 m_shadow : 1 = false;
@@ -192,7 +194,7 @@ private:
 	U8 m_typeChanged : 1 = true;
 	U8 m_frustumCount : 4 = 0; ///< The size of m_frustums array.
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 2 - 2
AnKi/Scene/Components/ModelComponent.cpp

@@ -15,7 +15,7 @@
 namespace anki {
 
 ModelComponent::ModelComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
+	: SceneComponent(node, kClassType)
 	, m_node(node)
 	, m_spatial(this)
 {
@@ -459,7 +459,7 @@ void ModelComponent::onOtherComponentRemovedOrAdded(SceneComponent* other, Bool
 {
 	ANKI_ASSERT(other);
 
-	if(other->getClassId() != SkinComponent::getStaticClassId())
+	if(other->getType() != SceneComponentType::kSkin)
 	{
 		return;
 	}

+ 2 - 2
AnKi/Scene/Components/ModelComponent.h

@@ -83,9 +83,9 @@ private:
 
 	void freeGpuScene();
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 
-	void onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added);
+	void onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added) override;
 };
 /// @}
 

+ 2 - 2
AnKi/Scene/Components/MoveComponent.h

@@ -19,14 +19,14 @@ class MoveComponent : public SceneComponent
 
 public:
 	MoveComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId())
+		: SceneComponent(node, kClassType)
 	{
 	}
 
 	~MoveComponent() = default;
 
 private:
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 1 - 1
AnKi/Scene/Components/ParticleEmitterComponent.cpp

@@ -194,7 +194,7 @@ public:
 };
 
 ParticleEmitterComponent::ParticleEmitterComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
+	: SceneComponent(node, kClassType)
 	, m_spatial(this)
 {
 }

+ 1 - 1
AnKi/Scene/Components/ParticleEmitterComponent.h

@@ -77,7 +77,7 @@ private:
 	Bool m_resourceUpdated = true;
 	SimulationType m_simulationType = SimulationType::kUndefined;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 
 	template<typename TParticle>
 	void simulate(Second prevUpdateTime, Second crntTime, const Transform& worldTransform, WeakArray<TParticle> particles, Vec3*& positions,

+ 1 - 1
AnKi/Scene/Components/PlayerControllerComponent.cpp

@@ -11,7 +11,7 @@
 namespace anki {
 
 PlayerControllerComponent::PlayerControllerComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
+	: SceneComponent(node, kClassType)
 {
 	PhysicsPlayerControllerInitInfo init;
 	init.m_position = node->getWorldTransform().getOrigin().xyz();

+ 1 - 1
AnKi/Scene/Components/PlayerControllerComponent.h

@@ -40,7 +40,7 @@ private:
 	PhysicsPlayerControllerPtr m_player;
 	Vec3 m_worldPos = Vec3(0.0f);
 
-	Error update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated);
+	Error update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 5 - 5
AnKi/Scene/Components/ReflectionProbeComponent.cpp

@@ -15,7 +15,7 @@ NumericCVar<U32> g_reflectionProbeResolutionCVar(CVarSubsystem::kScene, "Reflect
 												 "The resolution of the reflection probe's reflection");
 
 ReflectionProbeComponent::ReflectionProbeComponent(SceneNode* node)
-	: QueryableSceneComponent<ReflectionProbeComponent>(node, getStaticClassId())
+	: SceneComponent(node, kClassType)
 	, m_spatial(this)
 {
 	m_worldPos = node->getWorldTransform().getOrigin().xyz();
@@ -83,8 +83,8 @@ Error ReflectionProbeComponent::update(SceneComponentUpdateInfo& info, Bool& upd
 			m_frustums[i].setFar(effectiveDistance);
 			m_frustums[i].setShadowCascadeDistance(0, shadowCascadeDistance);
 
-			// Add something really far to force LOD 0 to be used. The importing tools create LODs with holes some times
-			// and that causes the sky to bleed to GI rendering
+			// Add something really far to force LOD 0 to be used. The importing tools create LODs with holes some times and that causes the sky to
+			// bleed to GI rendering
 			m_frustums[i].setLodDistances(
 				{effectiveDistance - 3.0f * kEpsilonf, effectiveDistance - 2.0f * kEpsilonf, effectiveDistance - 1.0f * kEpsilonf});
 		}
@@ -93,7 +93,7 @@ Error ReflectionProbeComponent::update(SceneComponentUpdateInfo& info, Bool& upd
 		m_spatial.setBoundingShape(aabbWorld);
 
 		// New UUID
-		refreshUuid();
+		m_uuid = SceneGraph::getSingleton().getNewUuid();
 
 		// Upload to the GPU scene
 		GpuSceneReflectionProbe gpuProbe;
@@ -101,7 +101,7 @@ Error ReflectionProbeComponent::update(SceneComponentUpdateInfo& info, Bool& upd
 		gpuProbe.m_cubeTexture = m_reflectionTexBindlessIndex;
 		gpuProbe.m_aabbMin = aabbWorld.getMin().xyz();
 		gpuProbe.m_aabbMax = aabbWorld.getMax().xyz();
-		gpuProbe.m_uuid = getUuid();
+		gpuProbe.m_uuid = m_uuid;
 		m_gpuSceneProbe.uploadToGpuScene(gpuProbe);
 	}
 

+ 3 - 2
AnKi/Scene/Components/ReflectionProbeComponent.h

@@ -19,7 +19,7 @@ extern NumericCVar<U32> g_reflectionProbeResolutionCVar;
 /// @{
 
 /// Reflection probe component.
-class ReflectionProbeComponent : public QueryableSceneComponent<ReflectionProbeComponent>
+class ReflectionProbeComponent : public SceneComponent
 {
 	ANKI_SCENE_COMPONENT(ReflectionProbeComponent)
 
@@ -87,11 +87,12 @@ private:
 	TexturePtr m_reflectionTex;
 	TextureViewPtr m_reflectionView;
 	U32 m_reflectionTexBindlessIndex = kMaxU32;
+	U32 m_uuid = 0;
 
 	Bool m_dirty = true;
 	Bool m_reflectionNeedsRefresh = true;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 0 - 112
AnKi/Scene/Components/SceneComponent.cpp

@@ -1,112 +0,0 @@
-// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/Components/SceneComponent.h>
-#include <AnKi/Scene/SceneNode.h>
-
-namespace anki {
-
-static SceneComponentRtti* g_rttis[kMaxSceneComponentClasses] = {};
-SceneComponentCallbacks g_sceneComponentCallbacks;
-static U32 g_sceneComponentClassCount = 0;
-
-SceneComponentRtti::SceneComponentRtti(const char* name, F32 updateWeight, U32 size, U32 alignment, SceneComponentVtable vtable)
-{
-	if(g_sceneComponentClassCount >= kMaxSceneComponentClasses)
-	{
-		// No special logging because this function is called before main
-		printf("Reached maximum component count. Increase kMaxSceneComponentClasses\n");
-		exit(-1);
-	}
-
-	m_updateWeight = updateWeight;
-
-	m_className = name;
-	ANKI_ASSERT(size < getMaxNumericLimit<decltype(m_classSize)>());
-	m_classSize = decltype(m_classSize)(size);
-
-	ANKI_ASSERT(alignment < getMaxNumericLimit<decltype(m_classAlignment)>());
-	m_classAlignment = decltype(m_classAlignment)(alignment);
-
-	m_classId = kMaxU8;
-
-	g_rttis[g_sceneComponentClassCount] = this;
-
-#define ANKI_SCENE_COMPONENT_VIRTUAL(name, type) g_sceneComponentCallbacks.m_##name[g_sceneComponentClassCount] = vtable.m_##name;
-#include <AnKi/Scene/Components/SceneComponentVirtuals.defs.h>
-
-	++g_sceneComponentClassCount;
-
-	// Sort everything because the IDs should be consistend between platforms and compilation builds
-	{
-		// Copy to a temp array
-		class Temp
-		{
-		public:
-			SceneComponentRtti* m_rrti;
-			SceneComponentVtable m_vtable;
-		};
-
-		Temp temps[kMaxSceneComponentClasses];
-		for(U32 i = 0; i < g_sceneComponentClassCount; ++i)
-		{
-			temps[i].m_rrti = g_rttis[i];
-			temps[i].m_vtable = {
-#define ANKI_SCENE_COMPONENT_VIRTUAL(name, type) g_sceneComponentCallbacks.m_##name[i]
-#define ANKI_SCENE_COMPONENT_VIRTUAL_SEPERATOR ,
-#include <AnKi/Scene/Components/SceneComponentVirtuals.defs.h>
-			};
-		}
-
-		std::sort(&temps[0], &temps[g_sceneComponentClassCount], [](const Temp& a, const Temp& b) {
-			return std::strcmp(a.m_rrti->m_className, b.m_rrti->m_className) < 0;
-		});
-
-		// Re-calculate the glass IDs
-		for(U32 i = 0; i < g_sceneComponentClassCount; ++i)
-		{
-			temps[i].m_rrti->m_classId = U8(i);
-		}
-
-		// Copy back
-		for(U32 i = 0; i < g_sceneComponentClassCount; ++i)
-		{
-			g_rttis[i] = temps[i].m_rrti;
-#define ANKI_SCENE_COMPONENT_VIRTUAL(name, type) g_sceneComponentCallbacks.m_##name[i] = temps[i].m_vtable.m_##name;
-#include <AnKi/Scene/Components/SceneComponentVirtuals.defs.h>
-		}
-	}
-}
-
-SceneComponent::SceneComponent([[maybe_unused]] SceneNode* node, U8 classId)
-	: m_classId(classId)
-{
-	ANKI_ASSERT(classId < g_sceneComponentClassCount);
-}
-
-const SceneComponentRtti& SceneComponent::findClassRtti(CString className)
-{
-	for(U32 i = 0; i < g_sceneComponentClassCount; ++i)
-	{
-		ANKI_ASSERT(g_rttis[i]);
-		ANKI_ASSERT(g_rttis[i]->m_className);
-		if(g_rttis[i]->m_className == className)
-		{
-			return *g_rttis[i];
-		}
-	}
-
-	ANKI_ASSERT(0);
-	return *g_rttis[0];
-}
-
-const SceneComponentRtti& SceneComponent::getClassRtti(U8 classId)
-{
-	ANKI_ASSERT(classId < g_sceneComponentClassCount);
-	ANKI_ASSERT(g_rttis[classId]);
-	return *g_rttis[classId];
-}
-
-} // namespace anki

+ 45 - 187
AnKi/Scene/Components/SceneComponent.h

@@ -8,89 +8,47 @@
 #include <AnKi/Scene/Common.h>
 #include <AnKi/Util/Functions.h>
 #include <AnKi/Util/BitMask.h>
+#include <AnKi/Util/Enum.h>
 
 namespace anki {
 
-// Forward
-class SceneComponentUpdateInfo;
-
 /// @addtogroup scene
 /// @{
 
-constexpr U32 kMaxSceneComponentClasses = 64;
-static_assert(kMaxSceneComponentClasses < 128, "It can oly be 7 bits because of SceneComponent::m_classId");
-
-#define ANKI_SCENE_COMPONENT_VIRTUAL(name, type) using SceneComponentCallback_##name = type;
-#include <AnKi/Scene/Components/SceneComponentVirtuals.defs.h>
-
-class SceneComponentVtable
+/// @memberof SceneComponent
+enum class SceneComponentType : U8
 {
-public:
-#define ANKI_SCENE_COMPONENT_VIRTUAL(name, type) SceneComponentCallback_##name m_##name;
-#include <AnKi/Scene/Components/SceneComponentVirtuals.defs.h>
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight) k##name,
+#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
+
+	kCount,
+	kFirst = 0
 };
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(SceneComponentType)
 
-/// Callbacks live in their own arrays for better caching.
-class SceneComponentCallbacks
+/// @memberof SceneComponent
+enum class SceneComponentTypeMask : U32
 {
-public:
-#define ANKI_SCENE_COMPONENT_VIRTUAL(name, type) SceneComponentCallback_##name m_##name[kMaxSceneComponentClasses];
-#include <AnKi/Scene/Components/SceneComponentVirtuals.defs.h>
-};
+	kNone = 0,
 
-extern SceneComponentCallbacks g_sceneComponentCallbacks;
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight) k##name = 1 << U32(SceneComponentType::k##name),
+#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
+};
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(SceneComponentTypeMask)
 
-/// Scene component class info.
-class SceneComponentRtti
+class SceneComponentType2
 {
 public:
-	const char* m_className;
-	F32 m_updateWeight; ///< It give the order it will get updated compared to other components.
-	U16 m_classSize;
-	U8 m_classAlignment;
-	U8 m_classId;
-
-	SceneComponentRtti(const char* name, F32 updateWeight, U32 size, U32 alignment, SceneComponentVtable vtable);
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight) static constexpr SceneComponentType k##name##Component = SceneComponentType::k##name;
+#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
 };
 
-/// Define a scene component.
 #define ANKI_SCENE_COMPONENT(className) \
-	static SceneComponentRtti _m_rtti; \
-	static void _construct(SceneComponent& self, SceneNode& node) \
-	{ \
-		callConstructor(static_cast<className&>(self), &node); \
-	} \
-	static void _destruct(SceneComponent& self) \
-	{ \
-		callDestructor(static_cast<className&>(self)); \
-	} \
-	static void _onDestroy(SceneComponent& self, SceneNode& node) \
-	{ \
-		static_cast<className&>(self).onDestroy(node); \
-	} \
-	static Error _update(SceneComponent& self, SceneComponentUpdateInfo& info, Bool& updated) \
-	{ \
-		return static_cast<className&>(self).update(info, updated); \
-	} \
-	static void _onOtherComponentRemovedOrAdded(SceneComponent& self, SceneComponent* other, Bool added) \
-	{ \
-		static_cast<className&>(self).onOtherComponentRemovedOrAdded(other, added); \
-	} \
-\
 public: \
-	static U8 getStaticClassId() \
-	{ \
-		return _m_rtti.m_classId; \
-	} \
+	static constexpr SceneComponentType kClassType = SceneComponentType2::k##className; \
 \
 private:
 
-/// Define the statics of a scene component.
-#define ANKI_SCENE_COMPONENT_STATICS(className, updateWeight) \
-	SceneComponentRtti className::_m_rtti( \
-		ANKI_STRINGIZE(className), updateWeight, sizeof(className), alignof(className), \
-		{className::_construct, className::_destruct, className::_onDestroy, className::_update, className::_onOtherComponentRemovedOrAdded});
-
 /// Passed to SceneComponent::update.
 /// @memberof SceneComponent
 class SceneComponentUpdateInfo
@@ -116,11 +74,14 @@ class SceneComponent
 {
 public:
 	/// Construct the scene component.
-	SceneComponent(SceneNode* node, U8 classId);
+	SceneComponent([[maybe_unused]] SceneNode* node, SceneComponentType type)
+		: m_type(type)
+	{
+	}
 
-	U8 getClassId() const
+	SceneComponentType getType() const
 	{
-		return m_classId;
+		return m_type;
 	}
 
 	Timestamp getTimestamp() const
@@ -128,29 +89,25 @@ public:
 		return m_timestamp;
 	}
 
-	static const SceneComponentRtti& findClassRtti(CString className);
-
-	static const SceneComponentRtti& getClassRtti(U8 classId);
-
-	const SceneComponentRtti& getClassRtti() const
+	ANKI_INTERNAL U32 getArrayIndex() const
 	{
-		return getClassRtti(m_classId);
+		ANKI_ASSERT(m_arrayIdx != kMaxU32);
+		return m_arrayIdx;
 	}
 
-	ANKI_INTERNAL void onDestroyReal(SceneNode& node)
+	ANKI_INTERNAL void setArrayIndex(U32 idx)
 	{
-		g_sceneComponentCallbacks.m_onDestroy[m_classId](*this, node);
+		m_arrayIdx = idx;
 	}
 
-	ANKI_INTERNAL Error updateReal(SceneComponentUpdateInfo& info, Bool& updated)
+	ANKI_INTERNAL virtual void onDestroy([[maybe_unused]] SceneNode& node)
 	{
-		return g_sceneComponentCallbacks.m_update[m_classId](*this, info, updated);
 	}
 
-	ANKI_INTERNAL void onOtherComponentRemovedOrAddedReal(SceneComponent* other, Bool added)
+	ANKI_INTERNAL virtual Error update(SceneComponentUpdateInfo& info, Bool& updated) = 0;
+
+	ANKI_INTERNAL virtual void onOtherComponentRemovedOrAdded([[maybe_unused]] SceneComponent* other, [[maybe_unused]] Bool added)
 	{
-		ANKI_ASSERT(other);
-		g_sceneComponentCallbacks.m_onOtherComponentRemovedOrAdded[m_classId](*this, other, added);
 	}
 
 	/// Don't call it directly.
@@ -161,120 +118,21 @@ public:
 		m_timestamp = timestamp;
 	}
 
-protected:
-	/// Pseudo-virtual
-	void onDestroy([[maybe_unused]] SceneNode& node)
-	{
-		// Do nothing
-	}
-
-	/// Pseudo-virtual to update the component.
-	/// @param[in,out] info Update info.
-	/// @param[out] updated true if an update happened.
-	Error update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-		return Error::kNone;
-	}
-
-	/// Pseudo-virtual. Called when a component is added or removed in the SceneNode.
-	/// @param other The component that was inserted.
-	/// @param added Was it inserted or removed?
-	void onOtherComponentRemovedOrAdded([[maybe_unused]] SceneComponent* other, [[maybe_unused]] Bool added)
+	static constexpr F32 getUpdateOrderWeight(SceneComponentType type)
 	{
+		return m_updateOrderWeights[type];
 	}
 
 private:
 	Timestamp m_timestamp = 1; ///< Indicates when an update happened
-	U8 m_classId; ///< Cache the type ID.
-};
-
-/// Scene component that has a UUID and a static method that can be used to fetch the component by using the UUID.
-template<typename T>
-class QueryableSceneComponent : public SceneComponent
-{
-public:
-	QueryableSceneComponent(SceneNode* node, U8 classId)
-		: SceneComponent(node, classId)
-	{
-	}
-
-	~QueryableSceneComponent()
-	{
-		releaseUuid();
-	}
-
-	U32 getUuid() const
-	{
-		ANKI_ASSERT(m_uuid);
-		return m_uuid;
-	}
-
-	static T* tryFindComponent(U32 uuid)
-	{
-		ANKI_ASSERT(uuid != 0);
-		auto it = m_uuidToSceneComponent.find(uuid);
-		return (it != m_uuidToSceneComponent.getEnd()) ? *it : nullptr;
-	}
-
-	static T* tryFindComponentThreadSafe(U32 uuid)
-	{
-		LockGuard lock(m_uuidToSceneComponentLock);
-		return tryFindComponent(uuid);
-	}
-
-protected:
-	/// @note Not thread-safe.
-	void refreshUuid()
-	{
-		refreshUuidCustom(m_nextUuid.fetchAdd(1));
-	}
-
-	/// @note Not thread-safe.
-	void refreshUuidCustom(U32 customUuid)
-	{
-		ANKI_ASSERT(customUuid != 0);
-
-		const U32 oldUuid = m_uuid;
-		m_uuid = customUuid;
-
-		LockGuard lock(m_uuidToSceneComponentLock);
-		if(oldUuid != 0)
-		{
-			auto it = m_uuidToSceneComponent.find(oldUuid);
-			ANKI_ASSERT(it != m_uuidToSceneComponent.getEnd());
-			m_uuidToSceneComponent.erase(it);
-		}
-
-		ANKI_ASSERT(m_uuidToSceneComponent.find(m_uuid) == m_uuidToSceneComponent.getEnd());
-		m_uuidToSceneComponent.emplace(m_uuid, static_cast<T*>(this));
-	}
-
-	/// @note Not thread-safe.
-	void releaseUuid()
-	{
-		if(m_uuid != 0)
-		{
-			LockGuard lock(m_uuidToSceneComponentLock);
-			auto it = m_uuidToSceneComponent.find(m_uuid);
-			ANKI_ASSERT(it != m_uuidToSceneComponent.getEnd());
-			m_uuidToSceneComponent.erase(it);
-
-			m_uuid = 0;
-		}
-	}
-
-	Bool hasUuid() const
-	{
-		return m_uuid != 0;
-	}
-
-private:
-	U32 m_uuid = 0;
-
-	inline static SceneHashMap<U32, T*> m_uuidToSceneComponent;
-	inline static SpinLock m_uuidToSceneComponentLock;
-	inline static Atomic<U32> m_nextUuid = {1};
+	U32 m_arrayIdx = kMaxU32;
+	SceneComponentType m_type; ///< Cache the type ID.
+
+	static constexpr Array<F32, U32(SceneComponentType::kCount)> m_updateOrderWeights = {
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight) weight
+#define ANKI_SCENE_COMPONENT_SEPARATOR ,
+#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
+	};
 };
 /// @}
 

+ 20 - 20
AnKi/Scene/Components/SceneComponentClasses.defs.h → AnKi/Scene/Components/SceneComponentClasses.def.h

@@ -3,50 +3,50 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#if !defined(ANKI_SCENE_COMPONENT_SEPERATOR)
-#	define ANKI_SCENE_COMPONENT_SEPERATOR
+#if !defined(ANKI_SCENE_COMPONENT_SEPARATOR)
+#	define ANKI_SCENE_COMPONENT_SEPARATOR
 #endif
 
 ANKI_DEFINE_SCENE_COMPONENT(Script, 0.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 
 ANKI_DEFINE_SCENE_COMPONENT(Body, 10.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(PlayerController, 10.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 
 ANKI_DEFINE_SCENE_COMPONENT(Joint, 10.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 
 ANKI_DEFINE_SCENE_COMPONENT(Move, 30.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(Skin, 30.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 
 ANKI_DEFINE_SCENE_COMPONENT(Trigger, 40.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 
 ANKI_DEFINE_SCENE_COMPONENT(Model, 100.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(ParticleEmitter, 100.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(Decal, 100.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(Camera, 100.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(FogDensity, 100.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(GlobalIlluminationProbe, 100.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(ReflectionProbe, 100.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(Skybox, 100.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(Ui, 100.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(LensFlare, 100.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
+ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(Light, 100.0f)
 
 #undef ANKI_DEFINE_SCENE_COMPONENT
-#undef ANKI_SCENE_COMPONENT_SEPERATOR
+#undef ANKI_SCENE_COMPONENT_SEPARATOR

+ 0 - 30
AnKi/Scene/Components/SceneComponentStatics.cpp

@@ -1,30 +0,0 @@
-// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/Components/BodyComponent.h>
-#include <AnKi/Scene/Components/CameraComponent.h>
-#include <AnKi/Scene/Components/DecalComponent.h>
-#include <AnKi/Scene/Components/FogDensityComponent.h>
-#include <AnKi/Scene/Components/GlobalIlluminationProbeComponent.h>
-#include <AnKi/Scene/Components/JointComponent.h>
-#include <AnKi/Scene/Components/LensFlareComponent.h>
-#include <AnKi/Scene/Components/LightComponent.h>
-#include <AnKi/Scene/Components/ModelComponent.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
-#include <AnKi/Scene/Components/ParticleEmitterComponent.h>
-#include <AnKi/Scene/Components/PlayerControllerComponent.h>
-#include <AnKi/Scene/Components/ReflectionProbeComponent.h>
-#include <AnKi/Scene/Components/ScriptComponent.h>
-#include <AnKi/Scene/Components/SkinComponent.h>
-#include <AnKi/Scene/Components/SkyboxComponent.h>
-#include <AnKi/Scene/Components/TriggerComponent.h>
-#include <AnKi/Scene/Components/UiComponent.h>
-
-namespace anki {
-
-#define ANKI_DEFINE_SCENE_COMPONENT(className, updateOrder) ANKI_SCENE_COMPONENT_STATICS(className##Component, updateOrder)
-#include <AnKi/Scene/Components/SceneComponentClasses.defs.h>
-
-} // end namespace anki

+ 0 - 21
AnKi/Scene/Components/SceneComponentVirtuals.defs.h

@@ -1,21 +0,0 @@
-// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#if !defined(ANKI_SCENE_COMPONENT_VIRTUAL_SEPERATOR)
-#	define ANKI_SCENE_COMPONENT_VIRTUAL_SEPERATOR
-#endif
-
-ANKI_SCENE_COMPONENT_VIRTUAL(constructor, void (*)(SceneComponent& self, SceneNode& owner))
-ANKI_SCENE_COMPONENT_VIRTUAL_SEPERATOR
-ANKI_SCENE_COMPONENT_VIRTUAL(destructor, void (*)(SceneComponent& self))
-ANKI_SCENE_COMPONENT_VIRTUAL_SEPERATOR
-ANKI_SCENE_COMPONENT_VIRTUAL(onDestroy, void (*)(SceneComponent& self, SceneNode& owner))
-ANKI_SCENE_COMPONENT_VIRTUAL_SEPERATOR
-ANKI_SCENE_COMPONENT_VIRTUAL(update, Error (*)(SceneComponent& self, SceneComponentUpdateInfo& info, Bool& updated))
-ANKI_SCENE_COMPONENT_VIRTUAL_SEPERATOR
-ANKI_SCENE_COMPONENT_VIRTUAL(onOtherComponentRemovedOrAdded, void (*)(SceneComponent& self, SceneComponent* other, Bool added))
-
-#undef ANKI_SCENE_COMPONENT_VIRTUAL
-#undef ANKI_SCENE_COMPONENT_VIRTUAL_SEPERATOR

+ 1 - 1
AnKi/Scene/Components/ScriptComponent.cpp

@@ -13,7 +13,7 @@
 namespace anki {
 
 ScriptComponent::ScriptComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
+	: SceneComponent(node, kClassType)
 {
 	ANKI_ASSERT(node);
 }

+ 1 - 1
AnKi/Scene/Components/ScriptComponent.h

@@ -35,7 +35,7 @@ private:
 	ScriptResourcePtr m_script;
 	ScriptEnvironment* m_env = nullptr;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 1 - 1
AnKi/Scene/Components/SkinComponent.cpp

@@ -14,7 +14,7 @@
 namespace anki {
 
 SkinComponent::SkinComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
+	: SceneComponent(node, kClassType)
 {
 }
 

+ 1 - 1
AnKi/Scene/Components/SkinComponent.h

@@ -115,7 +115,7 @@ private:
 
 	GpuSceneBufferAllocation m_gpuSceneBoneTransforms;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 
 	void visitBones(const Bone& bone, const Mat3x4& parentTrf, const BitSet<128, U8>& bonesAnimated, Vec4& minExtend, Vec4& maxExtend);
 };

+ 1 - 1
AnKi/Scene/Components/SkyboxComponent.cpp

@@ -13,7 +13,7 @@
 namespace anki {
 
 SkyboxComponent::SkyboxComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
+	: SceneComponent(node, kClassType)
 	, m_spatial(this)
 {
 	m_spatial.setAlwaysVisible(true);

+ 1 - 1
AnKi/Scene/Components/SkyboxComponent.h

@@ -136,7 +136,7 @@ private:
 		Vec3 m_diffuseColor = Vec3(1.0f);
 	} m_fog;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 1 - 1
AnKi/Scene/Components/TriggerComponent.cpp

@@ -65,7 +65,7 @@ public:
 };
 
 TriggerComponent::TriggerComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
+	: SceneComponent(node, kClassType)
 	, m_node(node)
 {
 	ANKI_ASSERT(node);

+ 1 - 1
AnKi/Scene/Components/TriggerComponent.h

@@ -54,7 +54,7 @@ private:
 	SceneDynamicArray<BodyComponent*> m_bodiesExit;
 	MyPhysicsTriggerProcessContactCallback* m_callbacks = nullptr;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 2 - 2
AnKi/Scene/Components/UiComponent.h

@@ -21,7 +21,7 @@ class UiComponent : public SceneComponent
 
 public:
 	UiComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId())
+		: SceneComponent(node, kClassType)
 		, m_spatial(this)
 	{
 		m_spatial.setAlwaysVisible(true);
@@ -51,7 +51,7 @@ private:
 	void* m_userData = nullptr;
 	Spatial m_spatial;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 1 - 5
AnKi/Scene/Forward.h

@@ -10,14 +10,10 @@ namespace anki {
 // Components
 class SceneComponent;
 #define ANKI_DEFINE_SCENE_COMPONENT(name, updateOrder) class name##Component;
-#include <AnKi/Scene/Components/SceneComponentClasses.defs.h>
+#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
 
 // Nodes
 class SceneNode;
-class LightNode;
-class PointLightNode;
-class SpotLightNode;
-class CameraNode;
 
 // Events
 class EventManager;

+ 2 - 2
AnKi/Scene/GpuSceneArray.inl.h

@@ -46,7 +46,7 @@ GpuSceneArrayAllocation<TGpuSceneObject, kId> GpuSceneArray<TGpuSceneObject, kId
 	{
 		// No freed indices this frame, get a new one
 
-		for(U maskGroup = 0; maskGroup < m_inUseIndicesMask.getSize(); ++maskGroup)
+		for(U32 maskGroup = 0; maskGroup < m_inUseIndicesMask.getSize(); ++maskGroup)
 		{
 			SubMask submask = m_inUseIndicesMask[maskGroup];
 			submask = ~submask;
@@ -143,7 +143,7 @@ void GpuSceneArray<TGpuSceneObject, kId>::validate() const
 	U32 maskGroupCount = 0;
 	for(const SubMask& mask : m_inUseIndicesMask)
 	{
-		count += mask.getEnabledBitCount();
+		count += mask.getSetBitCount();
 		maxIdx = max(maxIdx, (mask.getMostSignificantBit() != kMaxU32) ? (mask.getMostSignificantBit() + maskGroupCount * 64) : 0);
 		++maskGroupCount;
 	}

+ 20 - 2
AnKi/Scene/SceneGraph.cpp

@@ -6,7 +6,6 @@
 #include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Scene/Octree.h>
 #include <AnKi/Scene/RenderStateBucket.h>
-#include <AnKi/Scene/Components/CameraComponent.h>
 #include <AnKi/Physics/PhysicsWorld.h>
 #include <AnKi/Resource/ResourceManager.h>
 #include <AnKi/Renderer/MainRenderer.h>
@@ -16,6 +15,25 @@
 #include <AnKi/Util/Tracer.h>
 #include <AnKi/Util/HighRezTimer.h>
 
+#include <AnKi/Scene/Components/BodyComponent.h>
+#include <AnKi/Scene/Components/CameraComponent.h>
+#include <AnKi/Scene/Components/DecalComponent.h>
+#include <AnKi/Scene/Components/FogDensityComponent.h>
+#include <AnKi/Scene/Components/GlobalIlluminationProbeComponent.h>
+#include <AnKi/Scene/Components/JointComponent.h>
+#include <AnKi/Scene/Components/LensFlareComponent.h>
+#include <AnKi/Scene/Components/LightComponent.h>
+#include <AnKi/Scene/Components/ModelComponent.h>
+#include <AnKi/Scene/Components/MoveComponent.h>
+#include <AnKi/Scene/Components/ParticleEmitterComponent.h>
+#include <AnKi/Scene/Components/PlayerControllerComponent.h>
+#include <AnKi/Scene/Components/ReflectionProbeComponent.h>
+#include <AnKi/Scene/Components/ScriptComponent.h>
+#include <AnKi/Scene/Components/SkinComponent.h>
+#include <AnKi/Scene/Components/SkyboxComponent.h>
+#include <AnKi/Scene/Components/TriggerComponent.h>
+#include <AnKi/Scene/Components/UiComponent.h>
+
 namespace anki {
 
 static StatCounter g_sceneUpdateTime(StatCategory::kTime, "All scene update", StatFlag::kMilisecond | StatFlag::kShowAverage);
@@ -283,7 +301,7 @@ Error SceneGraph::updateNode(Second prevTime, Second crntTime, SceneNode& node)
 
 		componentUpdateInfo.m_node = &node;
 		Bool updated = false;
-		err = comp.updateReal(componentUpdateInfo, updated);
+		err = comp.update(componentUpdateInfo, updated);
 
 		if(updated)
 		{

+ 23 - 0
AnKi/Scene/SceneGraph.h

@@ -9,6 +9,7 @@
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Math.h>
 #include <AnKi/Util/HashMap.h>
+#include <AnKi/Util/BlockArray.h>
 #include <AnKi/Scene/Events/EventManager.h>
 #include <AnKi/Resource/Common.h>
 #include <AnKi/Core/CVarSet.h>
@@ -24,6 +25,21 @@ extern NumericCVar<F32> g_probeShadowEffectiveDistanceCVar;
 /// @addtogroup scene
 /// @{
 
+class SceneComponentArrays
+{
+public:
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight) \
+	SceneBlockArray<name##Component>& get##name##s() \
+	{ \
+		return m_##name##Array; \
+	}
+#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
+
+private:
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight) SceneBlockArray<name##Component> m_##name##Array;
+#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
+};
+
 /// The scene graph that  all the scene entities
 class SceneGraph : public MakeSingleton<SceneGraph>
 {
@@ -140,6 +156,11 @@ public:
 		return *m_octree;
 	}
 
+	SceneComponentArrays& getComponentArrays()
+	{
+		return m_componentArrays;
+	}
+
 private:
 	class UpdateSceneNodesCtx;
 
@@ -173,6 +194,8 @@ private:
 
 	Atomic<U32> m_nodesUuid = {1};
 
+	SceneComponentArrays m_componentArrays;
+
 	SceneGraph();
 
 	~SceneGraph();

+ 46 - 9
AnKi/Scene/SceneNode.cpp

@@ -8,8 +8,39 @@
 #include <AnKi/Scene/Components/SceneComponent.h>
 #include <AnKi/Scene/Components/MoveComponent.h>
 
+#include <AnKi/Scene/Components/BodyComponent.h>
+#include <AnKi/Scene/Components/CameraComponent.h>
+#include <AnKi/Scene/Components/DecalComponent.h>
+#include <AnKi/Scene/Components/FogDensityComponent.h>
+#include <AnKi/Scene/Components/GlobalIlluminationProbeComponent.h>
+#include <AnKi/Scene/Components/JointComponent.h>
+#include <AnKi/Scene/Components/LensFlareComponent.h>
+#include <AnKi/Scene/Components/LightComponent.h>
+#include <AnKi/Scene/Components/ModelComponent.h>
+#include <AnKi/Scene/Components/MoveComponent.h>
+#include <AnKi/Scene/Components/ParticleEmitterComponent.h>
+#include <AnKi/Scene/Components/PlayerControllerComponent.h>
+#include <AnKi/Scene/Components/ReflectionProbeComponent.h>
+#include <AnKi/Scene/Components/ScriptComponent.h>
+#include <AnKi/Scene/Components/SkinComponent.h>
+#include <AnKi/Scene/Components/SkyboxComponent.h>
+#include <AnKi/Scene/Components/TriggerComponent.h>
+#include <AnKi/Scene/Components/UiComponent.h>
+
 namespace anki {
 
+// Specialize newComponent(). Do that first
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight) \
+	template<> \
+	name##Component* SceneNode::newComponent<name##Component>() \
+	{ \
+		auto it = SceneGraph::getSingleton().getComponentArrays().get##name##s().emplace(this); \
+		it->setArrayIndex(it.getArrayIndex()); \
+		newComponentInternal(&(*it)); \
+		return &(*it); \
+	}
+#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
+
 SceneNode::SceneNode(CString name)
 	: m_uuid(SceneGraph::getSingleton().getNewUuid())
 {
@@ -26,10 +57,16 @@ SceneNode::~SceneNode()
 {
 	for(SceneComponent* comp : m_components)
 	{
-		comp->onDestroyReal(*this);
-		g_sceneComponentCallbacks.m_destructor[comp->getClassId()](*comp);
+		comp->onDestroy(*this);
 
-		SceneMemoryPool::getSingleton().free(comp);
+		switch(comp->getType())
+		{
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight) \
+	case SceneComponentType::k##name: \
+		SceneGraph::getSingleton().getComponentArrays().get##name##s().erase(comp->getArrayIndex()); \
+		break;
+#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
+		}
 	}
 }
 
@@ -50,33 +87,33 @@ void SceneNode::setMarkedForDeletion()
 
 void SceneNode::newComponentInternal(SceneComponent* newc)
 {
-	m_componentTypeMask |= 1 << newc->getClassId();
+	m_componentTypeMask |= 1 << SceneComponentTypeMask(newc->getType());
 
 	// Inform all other components that some component was added
 	for(SceneComponent* other : m_components)
 	{
-		other->onOtherComponentRemovedOrAddedReal(newc, true);
+		other->onOtherComponentRemovedOrAdded(newc, true);
 	}
 
 	// Inform the current component about others
 	for(SceneComponent* other : m_components)
 	{
-		newc->onOtherComponentRemovedOrAddedReal(other, true);
+		newc->onOtherComponentRemovedOrAdded(other, true);
 	}
 
 	m_components.emplaceBack(newc);
 
 	// Sort based on update weight
 	std::sort(m_components.getBegin(), m_components.getEnd(), [](const SceneComponent* a, const SceneComponent* b) {
-		const F32 weightA = a->getClassRtti().m_updateWeight;
-		const F32 weightB = b->getClassRtti().m_updateWeight;
+		const F32 weightA = SceneComponent::getUpdateOrderWeight(a->getType());
+		const F32 weightB = SceneComponent::getUpdateOrderWeight(b->getType());
 		if(weightA != weightB)
 		{
 			return weightA < weightB;
 		}
 		else
 		{
-			return a->getClassId() < b->getClassId();
+			return a->getType() < b->getType();
 		}
 	});
 }

+ 12 - 17
AnKi/Scene/SceneNode.h

@@ -105,11 +105,11 @@ public:
 	template<typename TComponent, typename TFunct>
 	void iterateComponentsOfType(TFunct func) const
 	{
-		if(m_componentTypeMask & (1 << TComponent::getStaticClassId()))
+		if(!!(m_componentTypeMask & (1 << SceneComponentTypeMask(TComponent::kClassType))))
 		{
 			for(U32 i = 0; i < m_components.getSize(); ++i)
 			{
-				if(m_components[i]->getClassId() == TComponent::getStaticClassId())
+				if(m_components[i]->getType() == TComponent::kClassType)
 				{
 					func(static_cast<const TComponent&>(*m_components[i]));
 				}
@@ -121,11 +121,11 @@ public:
 	template<typename TComponent, typename TFunct>
 	void iterateComponentsOfType(TFunct func)
 	{
-		if(m_componentTypeMask & (1 << TComponent::getStaticClassId()))
+		if(!!(m_componentTypeMask & (1 << SceneComponentTypeMask(TComponent::kClassType))))
 		{
 			for(U32 i = 0; i < m_components.getSize(); ++i)
 			{
-				if(m_components[i]->getClassId() == TComponent::getStaticClassId())
+				if(m_components[i]->getType() == TComponent::kClassType)
 				{
 					func(static_cast<TComponent&>(*m_components[i]));
 				}
@@ -137,11 +137,11 @@ public:
 	template<typename TComponent>
 	const TComponent* tryGetFirstComponentOfType() const
 	{
-		if(m_componentTypeMask & (1 << TComponent::getStaticClassId()))
+		if(!!(m_componentTypeMask & (1 << SceneComponentTypeMask(TComponent::kClassType))))
 		{
 			for(U32 i = 0; i < m_components.getSize(); ++i)
 			{
-				if(m_components[i]->getClassId() == TComponent::getStaticClassId())
+				if(m_components[i]->getType() == TComponent::kClassType)
 				{
 					return static_cast<const TComponent*>(m_components[i]);
 				}
@@ -179,12 +179,12 @@ public:
 	template<typename TComponent>
 	const TComponent* tryGetNthComponentOfType(U32 nth) const
 	{
-		if(m_componentTypeMask & (1 << TComponent::getStaticClassId()))
+		if(!!(m_componentTypeMask & (1 << SceneComponentTypeMask(TComponent::kClassType))))
 		{
 			I32 inth = I32(nth);
 			for(U32 i = 0; i < m_components.getSize(); ++i)
 			{
-				if(m_components[i]->getClassId() == TComponent::getStaticClassId() && inth-- == 0)
+				if(m_components[i]->getType() == TComponent::kClassType && inth-- == 0)
 				{
 					return static_cast<const TComponent*>(m_components[i]);
 				}
@@ -221,7 +221,7 @@ public:
 	template<typename TComponent>
 	TComponent& getComponentAt(U32 idx)
 	{
-		ANKI_ASSERT(m_components[idx]->getClassId() == TComponent::getStaticClassId());
+		ANKI_ASSERT(m_components[idx]->getType() == TComponent::kClassType);
 		SceneComponent* c = m_components[idx];
 		return *static_cast<TComponent*>(c);
 	}
@@ -230,7 +230,7 @@ public:
 	template<typename TComponent>
 	const TComponent& getComponentAt(U32 idx) const
 	{
-		ANKI_ASSERT(m_components[idx]->getClassId() == TComponent::getStaticClassId());
+		ANKI_ASSERT(m_components[idx]->getType() == TComponent::kClassType);
 		const SceneComponent* c = m_components[idx];
 		return *static_cast<const TComponent*>(c);
 	}
@@ -367,12 +367,7 @@ public:
 
 	/// Create and append a component to the components container. The SceneNode has the ownership.
 	template<typename TComponent>
-	TComponent* newComponent()
-	{
-		TComponent* comp = newInstance<TComponent>(SceneMemoryPool::getSingleton(), this);
-		newComponentInternal(comp);
-		return comp;
-	}
+	TComponent* newComponent();
 
 private:
 	U32 m_uuid;
@@ -380,7 +375,7 @@ private:
 
 	GrDynamicArray<SceneComponent*> m_components;
 
-	U32 m_componentTypeMask = 0;
+	SceneComponentTypeMask m_componentTypeMask = SceneComponentTypeMask::kNone;
 
 	Timestamp m_maxComponentTimestamp = 0;
 

+ 5 - 4
AnKi/Scene/Spatial.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <AnKi/Scene/Octree.h>
+#include <AnKi/Scene/Components/SceneComponent.h>
 #include <AnKi/Collision.h>
 
 namespace anki {
@@ -19,7 +20,7 @@ class Spatial
 public:
 	Spatial(SceneComponent* owner)
 		: m_owner(owner)
-		, m_sceneComponentClassId(owner->getClassId())
+		, m_sceneComponentType(owner->getType())
 	{
 		ANKI_ASSERT(owner);
 		m_octreeInfo.m_userData = this;
@@ -44,9 +45,9 @@ public:
 		return *m_owner;
 	}
 
-	U8 getSceneComponentClassId() const
+	SceneComponentType getSceneComponentClassId() const
 	{
-		return m_sceneComponentClassId;
+		return m_sceneComponentType;
 	}
 
 	const Aabb& getAabbWorldSpace() const
@@ -137,7 +138,7 @@ private:
 	OctreePlaceable m_octreeInfo;
 
 	SceneComponent* m_owner;
-	U8 m_sceneComponentClassId; ///< Cache it.
+	SceneComponentType m_sceneComponentType; ///< Cache it.
 
 	Bool m_placed : 1 = false;
 	Bool m_updatesOctreeBounds : 1 = true;

+ 22 - 22
AnKi/Scene/Visibility.cpp

@@ -228,49 +228,49 @@ void GatherVisiblesFromOctreeTask::gather(ThreadHive& hive)
 		[&](void* placeableUserData) {
 			ANKI_ASSERT(placeableUserData);
 			Spatial* spatial = static_cast<Spatial*>(placeableUserData);
-			const U8 compClassId = spatial->getSceneComponentClassId();
+			const SceneComponentType compType = spatial->getSceneComponentClassId();
 			const FrustumFlags frustumFlags = m_frcCtx->m_frustum;
 
 			Bool gather = false;
 
-			if(compClassId == ModelComponent::getStaticClassId())
+			if(compType == ModelComponent::kClassType)
 			{
 				gather = frustumFlags.m_gatherModelComponents || frustumFlags.m_gatherShadowCasterModelComponents
 						 || frustumFlags.m_gatherRayTracingModelComponents;
 			}
-			else if(compClassId == ParticleEmitterComponent::getStaticClassId())
+			else if(compType == ParticleEmitterComponent::kClassType)
 			{
 				gather = frustumFlags.m_gatherParticleComponents;
 			}
-			else if(compClassId == LightComponent::getStaticClassId())
+			else if(compType == LightComponent::kClassType)
 			{
 				gather = frustumFlags.m_gatherLightComponents;
 			}
-			else if(compClassId == LensFlareComponent::getStaticClassId())
+			else if(compType == LensFlareComponent::kClassType)
 			{
 				gather = frustumFlags.m_gatherLensFlareComponents;
 			}
-			else if(compClassId == ReflectionProbeComponent::getStaticClassId())
+			else if(compType == ReflectionProbeComponent::kClassType)
 			{
 				gather = frustumFlags.m_gatherProbeComponents;
 			}
-			else if(compClassId == DecalComponent::getStaticClassId())
+			else if(compType == DecalComponent::kClassType)
 			{
 				gather = frustumFlags.m_gatherDecalComponents;
 			}
-			else if(compClassId == FogDensityComponent::getStaticClassId())
+			else if(compType == FogDensityComponent::kClassType)
 			{
 				gather = frustumFlags.m_gatherFogDensityComponents;
 			}
-			else if(compClassId == GlobalIlluminationProbeComponent::getStaticClassId())
+			else if(compType == GlobalIlluminationProbeComponent::kClassType)
 			{
 				gather = frustumFlags.m_gatherProbeComponents;
 			}
-			else if(compClassId == UiComponent::getStaticClassId())
+			else if(compType == UiComponent::kClassType)
 			{
 				gather = frustumFlags.m_gatherUiComponents;
 			}
-			else if(compClassId == SkyboxComponent::getStaticClassId())
+			else if(compType == SkyboxComponent::kClassType)
 			{
 				gather = frustumFlags.m_gatherSkyComponents;
 			}
@@ -344,14 +344,14 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 		Spatial* spatial = m_spatialsToTest[i];
 		ANKI_ASSERT(spatial);
 		SceneComponent& comp = spatial->getSceneComponent();
-		const U8 compClassId = spatial->getSceneComponentClassId();
+		const SceneComponentType compType = spatial->getSceneComponentClassId();
 		const Aabb& aabb = spatial->getAabbWorldSpace();
 
 		auto isInside = [&] {
 			return spatial->getAlwaysVisible() || (testedFrustum.insideFrustum(aabb) && testAgainstRasterizer(aabb));
 		};
 
-		if(compClassId == ModelComponent::getStaticClassId())
+		if(compType == ModelComponent::kClassType)
 		{
 			const ModelComponent& modelc = static_cast<ModelComponent&>(comp);
 			const Bool isShadowFrustum = frustumFlags.m_gatherShadowCasterModelComponents;
@@ -394,7 +394,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 			ANKI_ASSERT(comp.getTimestamp() > 0);
 			m_frcCtx->m_queueViews[taskId].m_timestamp = max(m_frcCtx->m_queueViews[taskId].m_timestamp, comp.getTimestamp());
 		}
-		else if(compClassId == ParticleEmitterComponent::getStaticClassId())
+		else if(compType == ParticleEmitterComponent::kClassType)
 		{
 			const ParticleEmitterComponent& partemitc = static_cast<ParticleEmitterComponent&>(comp);
 			if(!partemitc.isEnabled() || !isInside())
@@ -429,7 +429,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 				m_frcCtx->m_queueViews[taskId].m_timestamp = max(m_frcCtx->m_queueViews[taskId].m_timestamp, comp.getTimestamp());
 			}
 		}
-		else if(compClassId == LightComponent::getStaticClassId())
+		else if(compType == LightComponent::kClassType)
 		{
 			const LightComponent& lightc = static_cast<LightComponent&>(comp);
 			if(lightc.getLightComponentType() != LightComponentType::kDirectional && !isInside())
@@ -543,7 +543,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 				ANKI_ASSERT(0);
 			}
 		}
-		else if(compClassId == LensFlareComponent::getStaticClassId())
+		else if(compType == LensFlareComponent::kClassType)
 		{
 			const LensFlareComponent& flarec = static_cast<LensFlareComponent&>(comp);
 
@@ -555,7 +555,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 			LensFlareQueueElement* el = result.m_lensFlares.newElement();
 			flarec.setupLensFlareQueueElement(*el);
 		}
-		else if(compClassId == ReflectionProbeComponent::getStaticClassId())
+		else if(compType == ReflectionProbeComponent::kClassType)
 		{
 			if(!isInside())
 			{
@@ -589,7 +589,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 				reflc.setupReflectionProbeQueueElement(*el);
 			}
 		}
-		else if(compClassId == DecalComponent::getStaticClassId())
+		else if(compType == DecalComponent::kClassType)
 		{
 			const DecalComponent& decalc = static_cast<DecalComponent&>(comp);
 
@@ -601,7 +601,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 			DecalQueueElement* el = result.m_decals.newElement();
 			decalc.setupDecalQueueElement(*el);
 		}
-		else if(compClassId == FogDensityComponent::getStaticClassId())
+		else if(compType == FogDensityComponent::kClassType)
 		{
 			if(!isInside())
 			{
@@ -613,7 +613,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 			FogDensityQueueElement* el = result.m_fogDensityVolumes.newElement();
 			fogc.setupFogDensityQueueElement(*el);
 		}
-		else if(compClassId == GlobalIlluminationProbeComponent::getStaticClassId())
+		else if(compType == GlobalIlluminationProbeComponent::kClassType)
 		{
 			if(!isInside())
 			{
@@ -645,7 +645,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 			GlobalIlluminationProbeQueueElement* el = result.m_giProbes.newElement();
 			giprobec.setupGlobalIlluminationProbeQueueElement(*el);
 		}
-		else if(compClassId == UiComponent::getStaticClassId())
+		else if(compType == UiComponent::kClassType)
 		{
 			if(!isInside())
 			{
@@ -656,7 +656,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 			UiQueueElement* el = result.m_uis.newElement();
 			uic.setupUiQueueElement(*el);
 		}
-		else if(compClassId == SkyboxComponent::getStaticClassId())
+		else if(compType == SkyboxComponent::kClassType)
 		{
 			if(!isInside())
 			{

+ 4 - 0
AnKi/Shaders/Intellisense.hlsl

@@ -102,6 +102,10 @@ struct SamplerState
 {
 };
 
+struct SamplerComparisonState
+{
+};
+
 template<typename T>
 struct Texture2D
 {

+ 4 - 4
AnKi/Util/BitSet.h

@@ -149,12 +149,12 @@ public:
 
 	Bool operator!() const
 	{
-		return !getAny();
+		return !getAnySet();
 	}
 
 	explicit operator Bool() const
 	{
-		return getAny();
+		return getAnySet();
 	}
 
 	/// Set or unset a bit at the given position.
@@ -230,14 +230,14 @@ public:
 	}
 
 	/// Any are enabled.
-	Bool getAny() const
+	Bool getAnySet() const
 	{
 		const BitSet kZero(false);
 		return *this != kZero;
 	}
 
 	/// Count bits.
-	U32 getEnabledBitCount() const
+	U32 getSetBitCount() const
 	{
 		U32 count = 0;
 		for(U i = 0; i < kChunkCount; ++i)

+ 60 - 92
AnKi/Util/BlockArray.h

@@ -23,7 +23,7 @@ class BlockArrayDefaultConfig
 public:
 	static constexpr U32 getElementCountPerBlock()
 	{
-		return U32(getAlignedRoundDown(ANKI_CACHE_LINE_SIZE, sizeof(T) * 64) / sizeof(T));
+		return 64;
 	}
 };
 
@@ -42,9 +42,6 @@ public:
 	BlockArrayIterator()
 		: m_array(nullptr)
 		, m_elementIdx(kMaxU32)
-#if ANKI_EXTRA_CHECKS
-		, m_iteratorVer(kMaxU32)
-#endif
 	{
 	}
 
@@ -52,10 +49,8 @@ public:
 	BlockArrayIterator(const BlockArrayIterator& b)
 		: m_array(b.m_array)
 		, m_elementIdx(b.m_elementIdx)
-#if ANKI_EXTRA_CHECKS
-		, m_iteratorVer(b.m_iteratorVer)
-#endif
 	{
+		check();
 	}
 
 	/// Allow conversion from iterator to const iterator.
@@ -63,34 +58,22 @@ public:
 	BlockArrayIterator(const BlockArrayIterator<YValuePointer, YValueReference, YBlockArrayPtr>& b)
 		: m_array(b.m_array)
 		, m_elementIdx(b.m_elementIdx)
-#if ANKI_EXTRA_CHECKS
-		, m_iteratorVer(b.m_iteratorVer)
-#endif
 	{
+		check();
 	}
 
-	BlockArrayIterator(TBlockArrayPtr arr, U32 idx
-#if ANKI_EXTRA_CHECKS
-					   ,
-					   U32 ver
-#endif
-					   )
+	BlockArrayIterator(TBlockArrayPtr arr, U32 idx)
 		: m_array(arr)
 		, m_elementIdx(idx)
-#if ANKI_EXTRA_CHECKS
-		, m_iteratorVer(ver)
-#endif
 	{
-		ANKI_ASSERT(arr);
+		check();
 	}
 
 	BlockArrayIterator& operator=(const BlockArrayIterator& b)
 	{
 		m_array = b.m_array;
 		m_elementIdx = b.m_elementIdx;
-#if ANKI_EXTRA_CHECKS
-		m_iteratorVer = b.m_iteratorVer;
-#endif
+		check();
 		return *this;
 	}
 
@@ -109,7 +92,7 @@ public:
 	BlockArrayIterator& operator++()
 	{
 		check();
-		m_elementIdx = (m_elementIdx != kMaxU32) ? (m_array->getNextElementIndex(m_elementIdx)) : kMaxU32;
+		m_elementIdx = m_array->getNextElementIndex(m_elementIdx);
 		return *this;
 	}
 
@@ -121,25 +104,9 @@ public:
 		return out;
 	}
 
-	BlockArrayIterator operator+(U32 n) const
-	{
-		check();
-		BlockArrayIterator out = *this;
-		out.m_elementIdx = (out.m_elementIdx != kMaxU32) ? (out.m_elementIdx + n) : kMaxU32;
-		return out;
-	}
-
-	BlockArrayIterator& operator+=(U32 n)
-	{
-		check();
-		m_elementIdx = (m_elementIdx != kMaxU32) ? (m_elementIdx + n) : kMaxU32;
-		return *this;
-	}
-
 	Bool operator==(const BlockArrayIterator& b) const
 	{
 		ANKI_ASSERT(m_array == b.m_array);
-		ANKI_ASSERT(m_iteratorVer == b.m_iteratorVer);
 		return m_elementIdx == b.m_elementIdx;
 	}
 
@@ -151,20 +118,18 @@ public:
 	/// Returns the imaginary index inside the BlockArray.
 	U32 getArrayIndex() const
 	{
-		ANKI_ASSERT(m_elementIdx != kMaxU32);
+		check();
 		return m_elementIdx;
 	}
 
 private:
 	TBlockArrayPtr m_array;
 	U32 m_elementIdx;
-#if ANKI_EXTRA_CHECKS
-	U32 m_iteratorVer; ///< See BlockArray::m_iteratorVer.
-#endif
 
 	void check() const
 	{
 		ANKI_ASSERT(m_array);
+		ANKI_ASSERT(m_array->indexExists(m_elementIdx));
 	}
 };
 
@@ -224,6 +189,10 @@ public:
 		m_blockMetadatas = std::move(b.m_blockMetadatas);
 		m_elementCount = b.m_elementCount;
 		b.m_elementCount = 0;
+		m_firstIndex = b.m_firstIndex;
+		b.m_firstIndex = 0;
+		m_endIndex = b.m_endIndex;
+		b.m_endIndex = 0;
 		return *this;
 	}
 
@@ -246,45 +215,25 @@ public:
 	/// Get begin.
 	Iterator getBegin()
 	{
-		return Iterator(this, getFirstElementIndex()
-#if ANKI_EXTRA_CHECKS
-								  ,
-						m_iteratorVer
-#endif
-		);
+		return Iterator(this, m_firstIndex);
 	}
 
 	/// Get begin.
 	ConstIterator getBegin() const
 	{
-		return ConstIterator(this, getFirstElementIndex()
-#if ANKI_EXTRA_CHECKS
-									   ,
-							 m_iteratorVer
-#endif
-		);
+		return ConstIterator(this, m_firstIndex);
 	}
 
 	/// Get end.
 	Iterator getEnd()
 	{
-		return Iterator(this, kMaxU32
-#if ANKI_EXTRA_CHECKS
-						,
-						m_iteratorVer
-#endif
-		);
+		return Iterator(this, m_endIndex);
 	}
 
 	/// Get end.
 	ConstIterator getEnd() const
 	{
-		return ConstIterator(this, kMaxU32
-#if ANKI_EXTRA_CHECKS
-							 ,
-							 m_iteratorVer
-#endif
-		);
+		return ConstIterator(this, m_endIndex);
 	}
 
 	/// Get begin.
@@ -330,13 +279,41 @@ public:
 
 	/// Removes one element.
 	/// @param at Points to the position of the element to remove.
-	void erase(ConstIterator at);
+	void erase(Iterator idx);
+
+	/// Removes one element.
+	/// @param at Points to the position of the element to remove.
+	void erase(U32 index)
+	{
+		erase(indexToIterator(index));
+	}
+
+	Iterator indexToIterator(U32 idx)
+	{
+		ANKI_ASSERT(indexExists(idx));
+		return Iterator(this, idx);
+	}
+
+	ConstIterator indexToIterator(U32 idx) const
+	{
+		ANKI_ASSERT(indexExists(idx));
+		return ConstIterator(this, idx);
+	}
+
+	Bool indexExists(U32 idx) const
+	{
+		const U32 localIdx = idx % kElementCountPerBlock;
+		const U32 blockIdx = idx / kElementCountPerBlock;
+		return blockIdx < m_blockMetadatas.getSize() && m_blockMetadatas[blockIdx].m_elementsInUseMask.get(localIdx) == true;
+	}
 
 	TMemoryPool& getMemoryPool()
 	{
 		return m_blockStorages.getMemoryPool();
 	}
 
+	void validate() const;
+
 private:
 	class alignas(alignof(T)) BlockStorage
 	{
@@ -355,9 +332,8 @@ private:
 	DynamicArray<BlockStorage*, TMemoryPool> m_blockStorages;
 	DynamicArray<BlockMetadata, TMemoryPool> m_blockMetadatas;
 	U32 m_elementCount = 0;
-#if ANKI_EXTRA_CHECKS
-	U32 m_iteratorVer = 1;
-#endif
+	U32 m_firstIndex = 0;
+	U32 m_endIndex = 0; ///< The index after the last.
 
 	U32 getFirstElementIndex() const
 	{
@@ -370,35 +346,27 @@ private:
 			}
 		}
 
-		return kMaxU32;
+		ANKI_ASSERT(0);
+		return 0;
 	}
 
-	U32 getNextElementIndex(U32 crnt) const
+	U32 getLastElementIndex() const
 	{
-		ANKI_ASSERT(crnt < kMaxU32);
-		const U32 localIdx = crnt % kElementCountPerBlock;
-		U32 blockIdx = crnt / kElementCountPerBlock;
-		ANKI_ASSERT(blockIdx < m_blockMetadatas.getSize());
-
-		Mask mask = m_blockMetadatas[blockIdx].m_elementsInUseMask;
-		mask.unsetNLeastSignificantBits(localIdx + 1);
-		U32 locIdx;
-		if((locIdx = mask.getLeastSignificantBit()) != kMaxU32)
-		{
-			return blockIdx * kElementCountPerBlock + locIdx;
-		}
-
-		++blockIdx;
-		for(; blockIdx < m_blockMetadatas.getSize(); ++blockIdx)
+		U32 blockIdx = m_blockMetadatas.getSize();
+		while(blockIdx--)
 		{
-			if((locIdx = m_blockMetadatas[blockIdx].m_elementsInUseMask.getLeastSignificantBit()) != kMaxU32)
+			U32 localIdx;
+			if((localIdx = m_blockMetadatas[blockIdx].m_elementsInUseMask.getMostSignificantBit()) != kMaxU32)
 			{
-				return blockIdx * kElementCountPerBlock + locIdx;
+				return localIdx + blockIdx * kElementCountPerBlock;
 			}
 		}
 
-		return kMaxU32;
+		ANKI_ASSERT(0);
+		return 0;
 	}
+
+	U32 getNextElementIndex(U32 crnt) const;
 };
 /// @}
 

+ 106 - 25
AnKi/Util/BlockArray.inl.h

@@ -28,11 +28,13 @@ void BlockArray<T, TMemoryPool, TConfig>::destroy()
 	m_blockMetadatas.destroy();
 	m_blockStorages.destroy();
 	m_elementCount = 0;
+	m_firstIndex = 0;
+	m_endIndex = 0;
 }
 
 template<typename T, typename TMemoryPool, typename TConfig>
 template<typename... TArgs>
-BlockArray<T, TMemoryPool, TConfig>::Iterator BlockArray<T, TMemoryPool, TConfig>::emplace(TArgs&&... args)
+typename BlockArray<T, TMemoryPool, TConfig>::Iterator BlockArray<T, TMemoryPool, TConfig>::emplace(TArgs&&... args)
 {
 	U32 localIdx = kMaxU32;
 	U32 blockIdx = kMaxU32;
@@ -40,7 +42,7 @@ BlockArray<T, TMemoryPool, TConfig>::Iterator BlockArray<T, TMemoryPool, TConfig
 	// Search for a block with free elements
 	for(U32 i = 0; i < m_blockStorages.getSize(); ++i)
 	{
-		if(m_blockMetadatas[i].m_elementsInUseMask.getEnabledBitCount() < kElementCountPerBlock)
+		if(m_blockMetadatas[i].m_elementsInUseMask.getSetBitCount() < kElementCountPerBlock)
 		{
 			// Found a block, allocate from it
 			auto unsetBits = ~m_blockMetadatas[i].m_elementsInUseMask;
@@ -74,36 +76,32 @@ BlockArray<T, TMemoryPool, TConfig>::Iterator BlockArray<T, TMemoryPool, TConfig
 	ANKI_ASSERT(m_blockMetadatas[blockIdx].m_elementsInUseMask.get(localIdx) == false);
 	m_blockMetadatas[blockIdx].m_elementsInUseMask.set(localIdx);
 
-#if ANKI_EXTRA_CHECKS
-	++m_iteratorVer;
-#endif
 	++m_elementCount;
+	const U32 idx = blockIdx * kElementCountPerBlock + localIdx;
+	m_firstIndex = min(m_firstIndex, idx);
+	m_endIndex = max(m_endIndex, idx + 1);
 
-	return Iterator(this, blockIdx * kElementCountPerBlock + localIdx
-#if ANKI_EXTRA_CHECKS
-					,
-					m_iteratorVer
-#endif
-	);
+	return Iterator(this, idx);
 }
 
 template<typename T, typename TMemoryPool, typename TConfig>
-void BlockArray<T, TMemoryPool, TConfig>::erase(ConstIterator at)
+void BlockArray<T, TMemoryPool, TConfig>::erase(Iterator it)
 {
-	const U32 idx = at.m_elementIdx;
+	const U32 idx = it.getArrayIndex();
 	const U32 localIdx = idx % kElementCountPerBlock;
 	const U32 blockIdx = idx / kElementCountPerBlock;
+
 	ANKI_ASSERT(blockIdx < m_blockStorages.getSize());
+	BlockStorage* block = m_blockStorages[blockIdx];
+	ANKI_ASSERT(block);
 
 	Mask& inUseMask = m_blockMetadatas[blockIdx].m_elementsInUseMask;
 	ANKI_ASSERT(inUseMask.get(localIdx) == true);
-	BlockStorage* block = m_blockStorages[blockIdx];
-	ANKI_ASSERT(block);
 
 	reinterpret_cast<T*>(&block->m_storage[localIdx * sizeof(T)])->~T();
 
 	inUseMask.unset(localIdx);
-	if(inUseMask.getEnabledBitCount() == 0)
+	if(inUseMask.getSetBitCount() == 0)
 	{
 		// Block is empty, delete it
 		getMemoryPool().free(block);
@@ -112,6 +110,24 @@ void BlockArray<T, TMemoryPool, TConfig>::erase(ConstIterator at)
 
 	ANKI_ASSERT(m_elementCount > 0);
 	--m_elementCount;
+
+	if(m_elementCount == 0)
+	{
+		m_firstIndex = 0;
+		m_endIndex = 0;
+	}
+	else
+	{
+		if(idx == m_firstIndex)
+		{
+			m_firstIndex = getFirstElementIndex();
+		}
+
+		if(idx + 1 == m_endIndex)
+		{
+			m_endIndex = getLastElementIndex() + 1;
+		}
+	}
 }
 
 template<typename T, typename TMemoryPool, typename TConfig>
@@ -125,25 +141,90 @@ BlockArray<T, TMemoryPool, TConfig>& BlockArray<T, TMemoryPool, TConfig>::operat
 	}
 
 	m_elementCount = b.m_elementCount;
+	m_firstIndex = b.m_firstIndex;
+	m_endIndex = b.m_endIndex;
+	m_blockMetadatas = b.m_blockMetadatas;
 	m_blockStorages.resize(b.m_blockStorages.getSize());
-	m_blockMetadatas.resize(b.m_blockMetadatas.getSize());
-#if ANKI_EXTRA_CHECKS
-	++m_iteratorVer;
-#endif
 
 	for(U32 blockIdx = 0; blockIdx < b.m_blockMetadatas.getSize(); ++blockIdx)
 	{
 		Mask mask = b.m_blockMetadatas[blockIdx].m_elementsInUseMask;
-		U32 localIdx;
-		while((localIdx = mask.getLeastSignificantBit()) != kMaxU32)
+		if(mask.getAnySet())
 		{
-			const T& other = b[blockIdx * kElementCountPerBlock + localIdx];
-			::new(&b.m_blockStorages[blockIdx].m_storage[localIdx * sizeof(T)]) T(other);
-			mask.unset(localIdx);
+			m_blockStorages[blockIdx] = newInstance<BlockStorage>(getMemoryPool());
+
+			U32 localIdx;
+			while((localIdx = mask.getLeastSignificantBit()) != kMaxU32)
+			{
+				const T& other = b[blockIdx * kElementCountPerBlock + localIdx];
+				::new(&m_blockStorages[blockIdx]->m_storage[localIdx * sizeof(T)]) T(other);
+				mask.unset(localIdx);
+			}
+		}
+		else
+		{
+			m_blockStorages[blockIdx] = nullptr;
 		}
 	}
 
 	return *this;
 }
 
+template<typename T, typename TMemoryPool, typename TConfig>
+U32 BlockArray<T, TMemoryPool, TConfig>::getNextElementIndex(U32 crnt) const
+{
+	ANKI_ASSERT(crnt < kMaxU32);
+	const U32 localIdx = crnt % kElementCountPerBlock;
+	U32 blockIdx = crnt / kElementCountPerBlock;
+	ANKI_ASSERT(blockIdx < m_blockMetadatas.getSize());
+
+	Mask mask = m_blockMetadatas[blockIdx].m_elementsInUseMask;
+	mask.unsetNLeastSignificantBits(localIdx + 1);
+	U32 locIdx;
+	if((locIdx = mask.getLeastSignificantBit()) != kMaxU32)
+	{
+		return blockIdx * kElementCountPerBlock + locIdx;
+	}
+
+	++blockIdx;
+	for(; blockIdx < m_blockMetadatas.getSize(); ++blockIdx)
+	{
+		if((locIdx = m_blockMetadatas[blockIdx].m_elementsInUseMask.getLeastSignificantBit()) != kMaxU32)
+		{
+			return blockIdx * kElementCountPerBlock + locIdx;
+		}
+	}
+
+	return m_endIndex;
+}
+
+template<typename T, typename TMemoryPool, typename TConfig>
+void BlockArray<T, TMemoryPool, TConfig>::validate() const
+{
+	ANKI_ASSERT(m_blockStorages.getSize() == m_blockMetadatas.getSize());
+
+	U32 count = 0;
+	U32 first = 0;
+	U32 end = 0;
+	for(U32 i = 0; i < m_blockStorages.getSize(); ++i)
+	{
+		const Mask& mask = m_blockMetadatas[i].m_elementsInUseMask;
+		const U32 lcount = mask.getSetBitCount();
+		if(lcount == 0)
+		{
+			ANKI_ASSERT(m_blockStorages[i] == nullptr);
+		}
+		else
+		{
+			count += lcount;
+			first = min(first, mask.getLeastSignificantBit() + i * kElementCountPerBlock);
+			end = max(end, mask.getMostSignificantBit() + i * kElementCountPerBlock + 1);
+		}
+	}
+
+	ANKI_ASSERT(count == m_elementCount);
+	ANKI_ASSERT(first == m_firstIndex);
+	ANKI_ASSERT(end == m_endIndex);
+}
+
 } // end namespace anki

+ 6 - 1
AnKi/Util/Forward.h

@@ -39,6 +39,9 @@ class List;
 template<typename T, typename TMemoryPool, typename TConfig>
 class SparseArray;
 
+template<typename T>
+class BlockArrayDefaultConfig;
+
 template<typename T, typename TMemoryPool, typename TConfig>
 class BlockArray;
 
@@ -86,6 +89,8 @@ class XmlDocument;
 	using submoduleName##StringList = BaseStringList<submoduleName##MemPoolWrapper>; \
 	using submoduleName##XmlDocument = XmlDocument<submoduleName##MemPoolWrapper>; \
 	template<typename T> \
-	using submoduleName##Hierarchy = Hierarchy<T, submoduleName##MemPoolWrapper>;
+	using submoduleName##Hierarchy = Hierarchy<T, submoduleName##MemPoolWrapper>; \
+	template<typename T, typename TConfig = BlockArrayDefaultConfig<T>> \
+	using submoduleName##BlockArray = BlockArray<T, submoduleName##MemPoolWrapper, TConfig>;
 
 } // end namespace anki

+ 2 - 2
AnKi/Util/ThreadPosix.cpp

@@ -45,7 +45,7 @@ void Thread::start(void* userData, ThreadCallback callback, const ThreadCoreAffi
 
 	pthread_attr_destroy(&attr);
 
-	if(coreAffintyMask.getEnabledBitCount())
+	if(coreAffintyMask.getSetBitCount())
 	{
 		pinToCores(coreAffintyMask);
 	}
@@ -75,7 +75,7 @@ void Thread::pinToCores(const ThreadCoreAffinityMask& coreAffintyMask)
 	CPU_ZERO(&cpus);
 
 	ThreadCoreAffinityMask affinity = coreAffintyMask;
-	while(affinity.getEnabledBitCount() > 0)
+	while(affinity.getSetBitCount() > 0)
 	{
 		const U32 msb = affinity.getMostSignificantBit();
 		ANKI_ASSERT(msb != kMaxU32);

+ 2 - 2
AnKi/Util/ThreadWindows.cpp

@@ -45,7 +45,7 @@ void Thread::start(void* userData, ThreadCallback callback, const ThreadCoreAffi
 		ANKI_UTIL_LOGF("CreateThread() failed");
 	}
 
-	if(coreAffintyMask.getAny())
+	if(coreAffintyMask.getAnySet())
 	{
 		pinToCores(coreAffintyMask);
 	}
@@ -94,7 +94,7 @@ void Thread::pinToCores(const ThreadCoreAffinityMask& coreAffintyMask)
 		ANKI_UTIL_LOGF("SetThreadAffinityMask() failed");
 	}
 
-	if(affinityTest.getEnabledBitCount() > 0)
+	if(affinityTest.getSetBitCount() > 0)
 	{
 		ANKI_UTIL_LOGE("Couldn't set affinity for all cores. Need to refactor the code");
 	}

+ 53 - 5
Tests/Util/BlockArray.cpp

@@ -26,7 +26,7 @@ ANKI_TEST(Util, BlockArray)
 		auto it3 = arr.emplace(666);
 		ANKI_TEST_EXPECT_EQ(it3->m_x, 666);
 
-		arr.erase(arr.getBegin() + 1);
+		arr.erase(it2);
 		ANKI_TEST_EXPECT_EQ(arr.getSize(), 2);
 
 		int sum = 0;
@@ -39,6 +39,52 @@ ANKI_TEST(Util, BlockArray)
 	ANKI_TEST_EXPECT_EQ(TestFoo::m_constructorCount, TestFoo::m_destructorCount);
 	ANKI_TEST_EXPECT_EQ(TestFoo::m_copyCount, 0);
 
+	// Copy
+	TestFoo::reset();
+	{
+		constexpr U32 kInsertionCount = 100;
+		BlockArray<TestFoo> arr;
+
+		// Add some
+		for(U32 i = 0; i < kInsertionCount; ++i)
+		{
+			arr.emplace(rand());
+		}
+
+		// Remove some
+		for(U32 i = 0; i < kInsertionCount / 2; ++i)
+		{
+			const U32 idx = U32(rand()) % kInsertionCount;
+			if(arr.indexExists(idx))
+			{
+				arr.erase(arr.indexToIterator(idx));
+			}
+			arr.validate();
+		}
+
+		// Copy
+		BlockArray<TestFoo> arr2 = arr;
+		arr2.validate();
+
+		// Test
+		I64 sum = 0;
+		for(auto it : arr)
+		{
+			sum += it.m_x;
+		}
+
+		I64 sum2 = 0;
+		for(auto it : arr2)
+		{
+			sum2 += it.m_x;
+		}
+
+		ANKI_TEST_EXPECT_EQ(sum, sum2);
+		ANKI_TEST_EXPECT_EQ(arr.getSize(), arr2.getSize());
+	}
+	ANKI_TEST_EXPECT_EQ(TestFoo::m_constructorCount, TestFoo::m_destructorCount);
+	ANKI_TEST_EXPECT_EQ(TestFoo::m_copyCount, 0);
+
 	// Fuzzy
 	TestFoo::reset();
 	{
@@ -56,8 +102,7 @@ ANKI_TEST(Util, BlockArray)
 				const int randPos = rand() % int(vec.size());
 
 				ANKI_TEST_EXPECT_EQ(arr[vec[randPos].first].m_x, vec[randPos].second);
-				auto it = arr.getBegin();
-				arr.erase(it + (vec[randPos].first - it.getArrayIndex()));
+				arr.erase(arr.indexToIterator(vec[randPos].first));
 
 				vec.erase(vec.begin() + randPos);
 			}
@@ -65,8 +110,8 @@ ANKI_TEST(Util, BlockArray)
 			{
 				// Add something
 				const int randVal = rand();
-				auto it = arr.emplace(randVal);
-				vec.push_back({it.getArrayIndex(), randVal});
+				auto idx = arr.emplace(randVal);
+				vec.push_back({idx.getArrayIndex(), randVal});
 			}
 			else if(vec.size())
 			{
@@ -78,6 +123,8 @@ ANKI_TEST(Util, BlockArray)
 				arr[vec[randPos].first].m_x = 1234;
 				vec[randPos].second = 1234;
 			}
+
+			arr.validate();
 		}
 
 		ANKI_TEST_EXPECT_EQ(vec.size(), arr.getSize());
@@ -90,6 +137,7 @@ ANKI_TEST(Util, BlockArray)
 	ANKI_TEST_EXPECT_EQ(TestFoo::m_copyCount, 0);
 
 	// Performance
+	if(ANKI_OPTIMIZE)
 	{
 		// Insertion
 		std::vector<U64> vec;