فهرست منبع

3rd step towards the render queue: Add the functionality needed to lights and probes

Panagiotis Christopoulos Charitos 8 سال پیش
والد
کامیت
1f8a553440

+ 4 - 2
src/anki/core/App.cpp

@@ -194,6 +194,8 @@ Error App::initInternal(const ConfigSet& config_, AllocAlignedCallback allocCb,
 	ANKI_CHECK(TraceManagerSingleton::get().create(m_heapAlloc, m_settingsDir.toCString()));
 #endif
 
+	ANKI_CORE_LOGI("Number of main threads: %u", U(config.getNumber("core.mainThreadCount")));
+
 	//
 	// Window
 	//
@@ -217,8 +219,8 @@ Error App::initInternal(const ConfigSet& config_, AllocAlignedCallback allocCb,
 	//
 	// ThreadPool
 	//
-	m_threadpool = m_heapAlloc.newInstance<ThreadPool>(getCpuCoresCount());
-	m_threadHive = m_heapAlloc.newInstance<ThreadHive>(getCpuCoresCount(), m_heapAlloc);
+	m_threadpool = m_heapAlloc.newInstance<ThreadPool>(config.getNumber("core.mainThreadCount"));
+	m_threadHive = m_heapAlloc.newInstance<ThreadHive>(config.getNumber("core.mainThreadCount"), m_heapAlloc);
 
 	//
 	// Graphics API

+ 2 - 0
src/anki/core/Config.cpp

@@ -4,6 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/core/Config.h>
+#include <anki/util/System.h>
 
 namespace anki
 {
@@ -91,6 +92,7 @@ Config::Config()
 	newOption("core.storagePerFrameMemorySize", 16_MB);
 	newOption("core.vertexPerFrameMemorySize", 10_MB);
 	newOption("core.textureBufferPerFrameMemorySize", 1_MB);
+	newOption("core.mainThreadCount", getCpuCoresCount());
 }
 
 Config::~Config()

+ 1 - 1
src/anki/math/Transform.h

@@ -32,7 +32,7 @@ public:
 		checkW();
 	}
 
-	explicit TTransform(const Mat4& m4)
+	explicit TTransform(const TMat4<T>& m4)
 	{
 		m_rotation = TMat3x4<T>(m4.getRotationPart());
 		m_origin = m4.getTranslationPart().xyz0();

+ 4 - 2
src/anki/renderer/Drawer.cpp

@@ -28,6 +28,7 @@ public:
 
 	Array<RenderQueueElement, MAX_INSTANCES> m_cachedRenderElements;
 	Array<U8, MAX_INSTANCES> m_cachedRenderElementLods;
+	Array<const void*, MAX_INSTANCES> m_userData;
 	U m_cachedRenderElementCount = 0;
 };
 
@@ -75,8 +76,8 @@ void RenderableDrawer::flushDrawcall(DrawContext& ctx)
 	ctx.m_queueCtx.m_key.m_lod = ctx.m_cachedRenderElementLods[0];
 	ctx.m_queueCtx.m_key.m_instanceCount = ctx.m_cachedRenderElementCount;
 
-	ctx.m_cachedRenderElements[0].m_callback(ctx.m_queueCtx,
-		WeakArray<const RenderQueueElement>(&ctx.m_cachedRenderElements[0], ctx.m_cachedRenderElementCount));
+	ctx.m_cachedRenderElements[0].m_callback(
+		ctx.m_queueCtx, WeakArray<const void*>(&ctx.m_userData[0], ctx.m_cachedRenderElementCount));
 
 	// Rendered something, reset the cached transforms
 	if(ctx.m_cachedRenderElementCount > 1)
@@ -113,6 +114,7 @@ void RenderableDrawer::drawSingle(DrawContext& ctx)
 	// Cache the new one
 	ctx.m_cachedRenderElements[ctx.m_cachedRenderElementCount] = rqel;
 	ctx.m_cachedRenderElementLods[ctx.m_cachedRenderElementCount] = lod;
+	ctx.m_userData[ctx.m_cachedRenderElementCount] = rqel.m_userData;
 	++ctx.m_cachedRenderElementCount;
 }
 

+ 50 - 57
src/anki/renderer/Indirect.cpp

@@ -405,20 +405,19 @@ void Indirect::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U f
 	while(it != end)
 	{
 		const LightComponent& lightc = it->m_node->getComponent<LightComponent>();
-		const MoveComponent& movec = it->m_node->getComponent<MoveComponent>();
 
 		// Update uniforms
 		IrVertex* vert = allocateAndBindUniforms<IrVertex*>(sizeof(IrVertex), cmdb, 0, 0);
 
-		Mat4 modelM(movec.getWorldTransform().getOrigin().xyz1(),
-			movec.getWorldTransform().getRotation().getRotationPart(),
+		Mat4 modelM(lightc.getWorldTransform().getOrigin().xyz1(),
+			lightc.getWorldTransform().getRotation().getRotationPart(),
 			lightc.getRadius());
 
 		vert->m_mvp = vpMat * modelM;
 
 		IrPointLight* light = allocateAndBindUniforms<IrPointLight*>(sizeof(IrPointLight), cmdb, 0, 1);
 
-		Vec4 pos = vMat * movec.getWorldTransform().getOrigin().xyz1();
+		Vec4 pos = vMat * lightc.getWorldTransform().getOrigin().xyz1();
 
 		light->m_projectionParams = frc.getProjectionMatrix().extractPerspectiveUnprojectionParams();
 		light->m_posRadius = Vec4(pos.xyz(), 1.0 / (lightc.getRadius() * lightc.getRadius()));
@@ -440,12 +439,11 @@ void Indirect::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U f
 	while(it != end)
 	{
 		const LightComponent& lightc = it->m_node->getComponent<LightComponent>();
-		const MoveComponent& movec = it->m_node->getComponent<MoveComponent>();
 
 		// Compute the model matrix
 		//
-		Mat4 modelM(movec.getWorldTransform().getOrigin().xyz1(),
-			movec.getWorldTransform().getRotation().getRotationPart(),
+		Mat4 modelM(lightc.getWorldTransform().getOrigin().xyz1(),
+			lightc.getWorldTransform().getRotation().getRotationPart(),
 			1.0);
 
 		// Calc the scale of the cone
@@ -465,14 +463,14 @@ void Indirect::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U f
 
 		light->m_projectionParams = frc.getProjectionMatrix().extractPerspectiveUnprojectionParams();
 
-		Vec4 pos = vMat * movec.getWorldTransform().getOrigin().xyz1();
+		Vec4 pos = vMat * lightc.getWorldTransform().getOrigin().xyz1();
 		light->m_posRadius = Vec4(pos.xyz(), 1.0 / (lightc.getDistance() * lightc.getDistance()));
 
 		light->m_diffuseColorOuterCos = Vec4(lightc.getDiffuseColor().xyz(), lightc.getOuterAngleCos());
 
 		light->m_specularColorInnerCos = Vec4(lightc.getSpecularColor().xyz(), lightc.getInnerAngleCos());
 
-		Vec3 lightDir = -movec.getWorldTransform().getRotation().getZAxis();
+		Vec3 lightDir = -lightc.getWorldTransform().getRotation().getZAxis();
 		lightDir = vMat.getRotationPart() * lightDir;
 		light->m_lightDirPad1 = lightDir.xyz0();
 
@@ -630,68 +628,63 @@ void Indirect::renderReflection(RenderingContext& ctx, SceneNode& node, U cubema
 
 void Indirect::findCacheEntry(SceneNode& node, U& entry, Bool& render)
 {
-	CacheEntry* it = m_cacheEntries.getBegin();
-	const CacheEntry* const end = m_cacheEntries.getEnd();
+	U64 uuid = node.getUuid();
 
-	CacheEntry* canditate = nullptr;
-	CacheEntry* empty = nullptr;
-	CacheEntry* kick = nullptr;
-	Timestamp kickTime = MAX_TIMESTAMP;
+	// Try find a candidate cache entry
+	CacheEntry* candidate = nullptr;
+	auto it = m_uuidToCacheEntry.find(uuid);
+	if(it != m_uuidToCacheEntry.getEnd())
+	{
+		// Found
 
-	while(it != end)
+		render = m_r->resourcesLoaded();
+		candidate = &(*it);
+	}
+	else
 	{
-		if(it->m_nodeUuid == node.getUuid())
+		// Not found
+
+		render = true;
+
+		// Iterate to find an empty or someone to kick
+		CacheEntry* empty = nullptr;
+		CacheEntry* kick = nullptr;
+		for(auto& entry : m_cacheEntries)
 		{
-			// Already there
-			ANKI_ASSERT(it->m_node == &node);
-			canditate = it;
-			break;
+			if(entry.m_nodeUuid == 0)
+			{
+				empty = &entry;
+				break;
+			}
+			else if(kick == nullptr || entry.m_timestamp < kick->m_timestamp)
+			{
+				kick = &entry;
+			}
 		}
-		else if(empty == nullptr && it->m_node == nullptr)
+
+		if(empty)
 		{
-			// Found empty
-			empty = it;
+			candidate = empty;
 		}
-		else if(it->m_timestamp < kickTime)
+		else
 		{
-			// Found one to kick
-			kick = it;
-			kickTime = it->m_timestamp;
-		}
+			ANKI_ASSERT(kick);
 
-		++it;
-	}
+			candidate = kick;
 
-	if(canditate)
-	{
-		// Update timestamp
-		canditate->m_timestamp = m_r->getFrameCount();
-		it = canditate;
-		render = m_r->resourcesLoaded();
-	}
-	else if(empty)
-	{
-		ANKI_ASSERT(empty->m_node == nullptr);
-		empty->m_node = &node;
-		empty->m_nodeUuid = node.getUuid();
-		empty->m_timestamp = m_r->getFrameCount();
+			// Remove from the map
+			m_uuidToCacheEntry.erase(kick);
+		}
 
-		it = empty;
-		render = true;
+		candidate->m_nodeUuid = uuid;
+		m_uuidToCacheEntry.pushBack(uuid, candidate);
 	}
-	else
-	{
-		ANKI_ASSERT(kick);
-
-		kick->m_node = &node;
-		kick->m_nodeUuid = node.getUuid();
-		kick->m_timestamp = m_r->getFrameCount();
 
-		it = kick;
-		render = true;
-	}
+	ANKI_ASSERT(candidate);
+	ANKI_ASSERT(candidate->m_nodeUuid == uuid);
+	candidate->m_timestamp = m_r->getFrameCount();
 
-	entry = it - m_cacheEntries.getBegin();
+	entry = candidate - &m_cacheEntries[0];
 }
 
 } // end namespace anki

+ 2 - 2
src/anki/renderer/Indirect.h

@@ -82,10 +82,9 @@ private:
 		}
 	};
 
-	class CacheEntry
+	class CacheEntry : public IntrusiveHashMapEnabled<CacheEntry>
 	{
 	public:
-		const SceneNode* m_node = nullptr;
 		U64 m_nodeUuid;
 		Timestamp m_timestamp = 0; ///< When last accessed.
 
@@ -129,6 +128,7 @@ private:
 	} m_irradiance;
 
 	DynamicArray<CacheEntry> m_cacheEntries;
+	IntrusiveHashMap<U64, CacheEntry> m_uuidToCacheEntry;
 
 	// Other
 	TextureResourcePtr m_integrationLut;

+ 7 - 12
src/anki/renderer/LightBin.cpp

@@ -556,12 +556,11 @@ void LightBin::binLights(U32 threadId, PtrSize threadsCount, LightBinContext& ct
 				U i = j - (ctx.m_vPointLights.getSize() + ctx.m_vDecals.getSize());
 
 				SceneNode& snode = *ctx.m_vSpotLights[i].m_node;
-				MoveComponent& move = snode.getComponent<MoveComponent>();
 				LightComponent& light = snode.getComponent<LightComponent>();
 				SpatialComponent& sp = snode.getComponent<SpatialComponent>();
 				const FrustumComponent* frc = snode.tryGetComponent<FrustumComponent>();
 
-				I pos = writeSpotLight(light, move, frc, ctx);
+				I pos = writeSpotLight(light, frc, ctx);
 				binLight(sp, light, pos, 1, ctx, testResult);
 			}
 			else if(j >= ctx.m_vDecals.getSize())
@@ -569,11 +568,10 @@ void LightBin::binLights(U32 threadId, PtrSize threadsCount, LightBinContext& ct
 				U i = j - ctx.m_vDecals.getSize();
 
 				SceneNode& snode = *ctx.m_vPointLights[i].m_node;
-				MoveComponent& move = snode.getComponent<MoveComponent>();
 				LightComponent& light = snode.getComponent<LightComponent>();
 				SpatialComponent& sp = snode.getComponent<SpatialComponent>();
 
-				I pos = writePointLight(light, move, ctx);
+				I pos = writePointLight(light, ctx);
 				binLight(sp, light, pos, 0, ctx, testResult);
 			}
 			else
@@ -677,14 +675,14 @@ void LightBin::binLights(U32 threadId, PtrSize threadsCount, LightBinContext& ct
 	ANKI_TRACE_STOP_EVENT(RENDERER_LIGHT_BINNING);
 }
 
-I LightBin::writePointLight(const LightComponent& lightc, const MoveComponent& lightMove, LightBinContext& ctx)
+I LightBin::writePointLight(const LightComponent& lightc, LightBinContext& ctx)
 {
 	// Get GPU light
 	I i = ctx.m_pointLightsCount.fetchAdd(1);
 
 	ShaderPointLight& slight = ctx.m_pointLights[i];
 
-	Vec4 pos = ctx.m_viewMat * lightMove.getWorldTransform().getOrigin().xyz1();
+	Vec4 pos = ctx.m_viewMat * lightc.getWorldTransform().getOrigin().xyz1();
 
 	slight.m_posRadius = Vec4(pos.xyz(), 1.0 / (lightc.getRadius() * lightc.getRadius()));
 	slight.m_diffuseColorShadowmapId = lightc.getDiffuseColor();
@@ -703,10 +701,7 @@ I LightBin::writePointLight(const LightComponent& lightc, const MoveComponent& l
 	return i;
 }
 
-I LightBin::writeSpotLight(const LightComponent& lightc,
-	const MoveComponent& lightMove,
-	const FrustumComponent* lightFrc,
-	LightBinContext& ctx)
+I LightBin::writeSpotLight(const LightComponent& lightc, const FrustumComponent* lightFrc, LightBinContext& ctx)
 {
 	I i = ctx.m_spotLightsCount.fetchAdd(1);
 
@@ -724,7 +719,7 @@ I LightBin::writeSpotLight(const LightComponent& lightc,
 	}
 
 	// Pos & dist
-	Vec4 pos = ctx.m_viewMat * lightMove.getWorldTransform().getOrigin().xyz1();
+	Vec4 pos = ctx.m_viewMat * lightc.getWorldTransform().getOrigin().xyz1();
 	light.m_posRadius = Vec4(pos.xyz(), 1.0 / (lightc.getDistance() * lightc.getDistance()));
 
 	// Diff color and shadowmap ID now
@@ -734,7 +729,7 @@ I LightBin::writeSpotLight(const LightComponent& lightc,
 	light.m_specularColorRadius = Vec4(lightc.getSpecularColor().xyz(), lightc.getDistance());
 
 	// Light dir
-	Vec3 lightDir = -lightMove.getWorldTransform().getRotation().getZAxis();
+	Vec3 lightDir = -lightc.getWorldTransform().getRotation().getZAxis();
 	lightDir = ctx.m_viewMat.getRotationPart() * lightDir;
 	light.m_lightDir = Vec4(lightDir, 0.0);
 

+ 2 - 5
src/anki/renderer/LightBin.h

@@ -67,12 +67,9 @@ private:
 
 	void binLights(U32 threadId, PtrSize threadsCount, LightBinContext& ctx);
 
-	I writePointLight(const LightComponent& light, const MoveComponent& move, LightBinContext& ctx);
+	I writePointLight(const LightComponent& light, LightBinContext& ctx);
 
-	I writeSpotLight(const LightComponent& lightc,
-		const MoveComponent& lightMove,
-		const FrustumComponent* lightFrc,
-		LightBinContext& ctx);
+	I writeSpotLight(const LightComponent& lightc, const FrustumComponent* lightFrc, LightBinContext& ctx);
 
 	void binLight(const SpatialComponent& sp,
 		const LightComponent& lightc,

+ 12 - 3
src/anki/renderer/RenderQueue.h

@@ -47,8 +47,7 @@ public:
 };
 
 /// Draw callback.
-using RenderQueueElementDrawCallback = void (*)(
-	RenderQueueDrawContext& ctx, WeakArray<const RenderQueueElement> elements);
+using RenderQueueElementDrawCallback = void (*)(RenderQueueDrawContext& ctx, WeakArray<const void*> userData);
 
 class RenderQueueElement final
 {
@@ -68,28 +67,38 @@ public:
 	Vec3 m_diffuseColor;
 	Vec3 m_specularColor;
 	Array<RenderQueue*, 6> m_shadowRenderQueues;
+	U32 m_textureArrayIndex; ///< Renderer internal.
 };
 
 class SpotLightQueueElement final
 {
 public:
 	U64 m_uuid;
-	Vec3 m_worldPosition;
+	Mat4 m_worldTransform;
 	F32 m_distance;
 	F32 m_outerAngleCos;
 	F32 m_innerAngleCos;
 	Vec3 m_diffuseColor;
 	Vec3 m_specularColor;
 	RenderQueue* m_shadowRenderQueue;
+	U32 m_textureArrayIndex; ///< Renderer internal.
 };
 
+/// Normally the visibility tests don't perform tests on the reflection probes because probes dont's change that often.
+/// This callback will be used by the renderer to inform a reflection probe that on the next frame it will be rendererd.
+/// In that case the probe should fill the render queues.
+using ReflectionProbeQueueElementFeedbackCallback = void (*)(Bool fillRenderQueuesOnNextFrame, void* userData);
+
 class ReflectionProbeQueueElement final
 {
 public:
+	ReflectionProbeQueueElementFeedbackCallback m_feedbackCallback;
+	void* m_userData;
 	U64 m_uuid;
 	Vec3 m_worldPosition;
 	F32 m_radius;
 	Array<RenderQueue*, 6> m_renderQueues;
+	U32 m_textureArrayIndex; ///< Renderer internal.
 };
 
 template<typename T>

+ 3 - 0
src/anki/scene/Light.cpp

@@ -104,6 +104,9 @@ void Light::onMoveUpdateCommon(MoveComponent& move)
 	{
 		lf->setWorldPosition(move.getWorldTransform().getOrigin());
 	}
+
+	// Update light component
+	getComponent<LightComponent>().setWorldTransform(move.getWorldTransform());
 }
 
 void Light::onShapeUpdateCommon(LightComponent& light)

+ 44 - 2
src/anki/scene/LightComponent.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/scene/SceneComponent.h>
+#include <anki/renderer/RenderQueue.h>
 #include <anki/Math.h>
 
 namespace anki
@@ -38,6 +39,16 @@ public:
 		return m_type;
 	}
 
+	const Transform& getWorldTransform() const
+	{
+		return m_worldTrf;
+	}
+
+	void setWorldTransform(const Transform& trf)
+	{
+		m_worldTrf = trf;
+	}
+
 	const Vec4& getDiffuseColor() const
 	{
 		return m_diffColor;
@@ -137,10 +148,41 @@ public:
 
 	ANKI_USE_RESULT Error update(SceneNode&, F32, F32, Bool& updated) override;
 
+	void setupPointLightQueueElement(PointLightQueueElement& el) const
+	{
+		ANKI_ASSERT(m_type == LightComponentType::POINT);
+		el.m_uuid = getUuid();
+		el.m_worldPosition = m_worldTrf.getOrigin().xyz();
+		el.m_radius = m_radius;
+		el.m_diffuseColor = m_diffColor.xyz();
+		el.m_specularColor = m_specColor.xyz();
+#if ANKI_EXTRA_CHECKS
+		el.m_shadowRenderQueues = {{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+		el.m_textureArrayIndex = MAX_U32;
+#endif
+	}
+
+	void setupSpotLightQueueElement(SpotLightQueueElement& el) const
+	{
+		ANKI_ASSERT(m_type == LightComponentType::SPOT);
+		el.m_uuid = getUuid();
+		el.m_worldTransform = Mat4(m_worldTrf);
+		el.m_distance = m_distance;
+		el.m_outerAngleCos = m_outerAngleCos;
+		el.m_innerAngleCos = m_innerAngleCos;
+		el.m_diffuseColor = m_diffColor.xyz();
+		el.m_specularColor = m_specColor.xyz();
+#if ANKI_EXTRA_CHECKS
+		el.m_shadowRenderQueue = nullptr;
+		el.m_textureArrayIndex = MAX_U32;
+#endif
+	}
+
 private:
 	LightComponentType m_type;
-	Vec4 m_diffColor = Vec4(0.5);
-	Vec4 m_specColor = Vec4(0.5);
+	Transform m_worldTrf = Transform(Vec4(0.0f), Mat3x4::getIdentity(), 1.0f);
+	Vec4 m_diffColor = Vec4(0.5f);
+	Vec4 m_specColor = Vec4(0.5f);
 	union
 	{
 		F32 m_radius;

+ 8 - 8
src/anki/scene/ModelNode.cpp

@@ -65,12 +65,12 @@ Error ModelPatchNode::init(const ModelPatch* modelPatch, U idx, const ModelNode&
 	return ErrorCode::NONE;
 }
 
-void ModelPatchNode::drawCallback(RenderQueueDrawContext& ctx, WeakArray<const RenderQueueElement> elements)
+void ModelPatchNode::drawCallback(RenderQueueDrawContext& ctx, WeakArray<const void*> userData)
 {
-	ANKI_ASSERT(elements.getSize() > 0 && elements.getSize() <= MAX_INSTANCES);
-	ANKI_ASSERT(ctx.m_key.m_instanceCount == elements.getSize());
+	ANKI_ASSERT(userData.getSize() > 0 && userData.getSize() <= MAX_INSTANCES);
+	ANKI_ASSERT(ctx.m_key.m_instanceCount == userData.getSize());
 
-	const ModelPatchNode& self = *static_cast<const ModelPatchNode*>(elements[0].m_userData);
+	const ModelPatchNode& self = *static_cast<const ModelPatchNode*>(userData[0]);
 
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
@@ -103,21 +103,21 @@ void ModelPatchNode::drawCallback(RenderQueueDrawContext& ctx, WeakArray<const R
 	// Uniforms
 	Array<Mat4, MAX_INSTANCES> trfs;
 	trfs[0] = Mat4(self.getParent()->getComponent<MoveComponent>().getWorldTransform());
-	for(U i = 1; i < elements.getSize(); ++i)
+	for(U i = 1; i < userData.getSize(); ++i)
 	{
-		const ModelPatchNode& self2 = *static_cast<const ModelPatchNode*>(elements[i].m_userData);
+		const ModelPatchNode& self2 = *static_cast<const ModelPatchNode*>(userData[i]);
 		trfs[i] = Mat4(self2.getParent()->getComponent<MoveComponent>().getWorldTransform());
 	}
 
 	StagingGpuMemoryToken token;
 	self.getComponent<RenderComponent>().allocateAndSetupUniforms(
-		ctx, WeakArray<const Mat4>(&trfs[0], elements.getSize()), *ctx.m_stagingGpuAllocator, token);
+		ctx, WeakArray<const Mat4>(&trfs[0], userData.getSize()), *ctx.m_stagingGpuAllocator, token);
 	cmdb->bindUniformBuffer(0, 0, token.m_buffer, token.m_offset, token.m_range);
 
 	// Draw
 	cmdb->drawElements(PrimitiveTopology::TRIANGLES,
 		modelInf.m_indicesCountArray[0],
-		elements.getSize(),
+		userData.getSize(),
 		modelInf.m_indicesOffsetArray[0] / sizeof(U16),
 		0,
 		0);

+ 1 - 1
src/anki/scene/ModelNode.h

@@ -48,7 +48,7 @@ private:
 		el.m_mergeKey = m_mergeKey;
 	}
 
-	static void drawCallback(RenderQueueDrawContext& ctx, WeakArray<const RenderQueueElement> elements);
+	static void drawCallback(RenderQueueDrawContext& ctx, WeakArray<const void*> userData);
 };
 
 /// The model scene node

+ 3 - 3
src/anki/scene/ParticleEmitter.cpp

@@ -266,11 +266,11 @@ Error ParticleEmitter::init(const CString& filename)
 	return ErrorCode::NONE;
 }
 
-void ParticleEmitter::drawCallback(RenderQueueDrawContext& ctx, WeakArray<const RenderQueueElement> elements)
+void ParticleEmitter::drawCallback(RenderQueueDrawContext& ctx, WeakArray<const void*> userData)
 {
-	ANKI_ASSERT(elements.getSize() == 1);
+	ANKI_ASSERT(userData.getSize() == 1);
 
-	const ParticleEmitter& self = *static_cast<const ParticleEmitter*>(elements[0].m_userData);
+	const ParticleEmitter& self = *static_cast<const ParticleEmitter*>(userData[0]);
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
 	// Program

+ 1 - 1
src/anki/scene/ParticleEmitter.h

@@ -207,7 +207,7 @@ private:
 		el.m_userData = this;
 	}
 
-	static void drawCallback(RenderQueueDrawContext& ctx, WeakArray<const RenderQueueElement> elements);
+	static void drawCallback(RenderQueueDrawContext& ctx, WeakArray<const void*> userData);
 };
 /// @}
 

+ 20 - 0
src/anki/scene/ReflectionProbeComponent.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/scene/SceneComponent.h>
+#include <anki/renderer/RenderQueue.h>
 
 namespace anki
 {
@@ -67,12 +68,31 @@ public:
 		return m_textureArrayIndex;
 	}
 
+	void setupReflectionProbeQueueElement(ReflectionProbeQueueElement& el) const
+	{
+		el.m_feedbackCallback = reflectionProbeQueueElementFeedbackCallback;
+		el.m_userData = const_cast<ReflectionProbeComponent*>(this);
+		el.m_uuid = getUuid();
+		el.m_worldPosition = m_pos.xyz();
+		el.m_radius = m_radius;
+#if ANKI_EXTRA_CHECKS
+		el.m_renderQueues = {{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+		el.m_textureArrayIndex = MAX_U32;
+#endif
+	}
+
 private:
 	Vec4 m_pos = Vec4(0.0);
 	F32 m_radius = 0.0;
 	Bool8 m_markedForRendering = false;
 
 	U16 m_textureArrayIndex = MAX_U16; ///< Used by the renderer
+
+	static void reflectionProbeQueueElementFeedbackCallback(Bool fillRenderQueuesOnNextFrame, void* userData)
+	{
+		ANKI_ASSERT(userData);
+		static_cast<ReflectionProbeComponent*>(userData)->m_markedForRendering = fillRenderQueuesOnNextFrame;
+	}
 };
 /// @}
 

+ 1 - 0
src/anki/scene/SceneComponent.cpp

@@ -13,6 +13,7 @@ namespace anki
 SceneComponent::SceneComponent(SceneComponentType type, SceneNode* node)
 	: m_node(node)
 	, m_type(type)
+	, m_uuid(node->getSceneGraph().getNewUuid())
 {
 	if(m_type != SceneComponentType::NONE)
 	{

+ 7 - 0
src/anki/scene/SceneComponent.h

@@ -81,6 +81,11 @@ public:
 	/// Called only by the SceneGraph
 	ANKI_USE_RESULT Error updateReal(SceneNode& node, F32 prevTime, F32 crntTime, Bool& updated);
 
+	U64 getUuid() const
+	{
+		return m_uuid;
+	}
+
 	SceneNode& getSceneNode()
 	{
 		return *m_node;
@@ -96,6 +101,7 @@ public:
 	SceneFrameAllocator<U8> getFrameAllocator() const;
 
 	SceneGraph& getSceneGraph();
+
 	const SceneGraph& getSceneGraph() const;
 
 protected:
@@ -104,6 +110,7 @@ protected:
 
 private:
 	SceneComponentType m_type;
+	U64 m_uuid;
 };
 
 /// Multiple lists of all types of components.

+ 1 - 1
src/anki/scene/SceneGraph.h

@@ -198,7 +198,7 @@ anki_internal:
 		return m_maxReflectionProxyDistance;
 	}
 
-	U64 getNewSceneNodeUuid()
+	U64 getNewUuid()
 	{
 		return m_nodesUuid++;
 	}

+ 1 - 1
src/anki/scene/SceneNode.cpp

@@ -11,7 +11,7 @@ namespace anki
 
 SceneNode::SceneNode(SceneGraph* scene, CString name)
 	: m_scene(scene)
-	, m_uuid(scene->getNewSceneNodeUuid())
+	, m_uuid(scene->getNewUuid())
 {
 	if(name)
 	{