Browse Source

Renderer: Add object picking

Panagiotis Christopoulos Charitos 3 weeks ago
parent
commit
07894fce56

+ 79 - 5
AnKi/Editor/EditorUi.cpp

@@ -11,6 +11,7 @@
 #include <AnKi/Window/Input.h>
 #include <AnKi/Util/Filesystem.h>
 #include <AnKi/Renderer/Renderer.h>
+#include <AnKi/Renderer/Dbg.h>
 #include <filesystem>
 #include <ThirdParty/ImGui/Extra/IconsMaterialDesignIcons.h> // See all icons in https://pictogrammers.com/library/mdi/
 
@@ -147,6 +148,8 @@ void EditorUi::draw(UiCanvas& canvas)
 {
 	m_canvas = &canvas;
 
+	selectSceneNode();
+
 	if(!m_font)
 	{
 		const Array<CString, 2> fnames = {"EngineAssets/UbuntuRegular.ttf", "EngineAssets/Editor/materialdesignicons-webfont.ttf"};
@@ -258,6 +261,56 @@ void EditorUi::mainMenu()
 			ImGui::EndMenu();
 		}
 
+		if(ImGui::BeginMenu(ICON_MDI_CUBE_SCAN " Debug"))
+		{
+			Bool bBoundingBoxes = !!(Renderer::getSingleton().getDbg().getOptions() & DbgOption::kBoundingBoxes);
+			if(ImGui::Checkbox("Bounding Boxes", &bBoundingBoxes))
+			{
+				DbgOption options = Renderer::getSingleton().getDbg().getOptions();
+				if(bBoundingBoxes)
+				{
+					options |= DbgOption::kBoundingBoxes | DbgOption::kIcons;
+				}
+				else
+				{
+					options &= ~(DbgOption::kBoundingBoxes | DbgOption::kIcons);
+				}
+				Renderer::getSingleton().getDbg().setOptions(options);
+			}
+
+			Bool bPhysics = !!(Renderer::getSingleton().getDbg().getOptions() & DbgOption::kPhysics);
+			if(ImGui::Checkbox("Physics Bodies", &bPhysics))
+			{
+				DbgOption options = Renderer::getSingleton().getDbg().getOptions();
+				if(bPhysics)
+				{
+					options |= DbgOption::kPhysics;
+				}
+				else
+				{
+					options &= ~DbgOption::kPhysics;
+				}
+				Renderer::getSingleton().getDbg().setOptions(options);
+			}
+
+			Bool bDepthTest = !!(Renderer::getSingleton().getDbg().getOptions() & DbgOption::kDepthTest);
+			if(ImGui::Checkbox("Depth Test", &bDepthTest))
+			{
+				DbgOption options = Renderer::getSingleton().getDbg().getOptions();
+				if(bDepthTest)
+				{
+					options |= DbgOption::kDepthTest;
+				}
+				else
+				{
+					options &= ~DbgOption::kDepthTest;
+				}
+				Renderer::getSingleton().getDbg().setOptions(options);
+			}
+
+			ImGui::EndMenu();
+		}
+
 		// Title
 		{
 			CString text = "AnKi 3D Engine Editor";
@@ -307,7 +360,7 @@ void EditorUi::sceneNode(SceneNode& node)
 	treeFlags |= ImGuiTreeNodeFlags_SpanFullWidth; // Span full width for easier mouse reach
 	treeFlags |= ImGuiTreeNodeFlags_DrawLinesToNodes; // Always draw hierarchy outlines
 
-	if(&node == m_sceneHierarchyWindow.m_visibleNode)
+	if(&node == m_sceneHierarchyWindow.m_selectedNode)
 	{
 		treeFlags |= ImGuiTreeNodeFlags_Selected;
 	}
@@ -336,7 +389,7 @@ void EditorUi::sceneNode(SceneNode& node)
 
 	if(ImGui::IsItemFocused())
 	{
-		m_sceneHierarchyWindow.m_visibleNode = &node;
+		m_sceneHierarchyWindow.m_selectedNode = &node;
 	}
 
 	if(nodeOpen)
@@ -380,7 +433,7 @@ void EditorUi::sceneHierarchyWindow()
 					{
 						sceneNode(node);
 					}
-					return true;
+					return FunctorContinue::kContinue;
 				});
 
 				ImGui::EndTable();
@@ -410,9 +463,9 @@ void EditorUi::sceneNodePropertiesWindow()
 		ImGui::SetNextWindowSize(Vec2(initialWidth, viewportSize.y() - kConsoleHeight), ImGuiCond_FirstUseEver);
 	}
 
-	if(ImGui::Begin("SceneNode Props", &m_showSceneNodePropsWindow, ImGuiWindowFlags_NoCollapse) && m_sceneHierarchyWindow.m_visibleNode)
+	if(ImGui::Begin("SceneNode Props", &m_showSceneNodePropsWindow, ImGuiWindowFlags_NoCollapse) && m_sceneHierarchyWindow.m_selectedNode)
 	{
-		SceneNode& node = *m_sceneHierarchyWindow.m_visibleNode;
+		SceneNode& node = *m_sceneHierarchyWindow.m_selectedNode;
 		I32 id = 0;
 
 		if(state.m_currentSceneNodeUuid != node.getUuid())
@@ -1480,4 +1533,25 @@ void EditorUi::loadImageToCache(CString fname, ImageResourcePtr& img)
 	}
 }
 
+void EditorUi::selectSceneNode()
+{
+	if(!m_mouseHoveredOverAnyWindow && Input::getSingleton().getMouseButton(MouseButton::kLeft) == 1)
+	{
+		const U32 uuid = Renderer::getSingleton().getDbg().getObjectUuidAtMousePosition();
+
+		if(uuid != 0)
+		{
+			SceneGraph::getSingleton().visitNodes([this, uuid](SceneNode& node) {
+				if(node.getUuid() == uuid)
+				{
+					m_sceneHierarchyWindow.m_selectedNode = &node;
+					ANKI_LOGV("Selecting scene node: %s", node.getName().cstr());
+					return FunctorContinue::kStop;
+				}
+				return FunctorContinue::kContinue;
+			});
+		}
+	}
+}
+
 } // end namespace anki

+ 3 - 1
AnKi/Editor/EditorUi.h

@@ -8,6 +8,7 @@
 #include <AnKi/Ui.h>
 #include <AnKi/Editor/ImageViewerUi.h>
 #include <AnKi/Editor/ParticleEditorUi.h>
+#include <AnKi/Util/Function.h>
 #include <filesystem>
 
 namespace anki {
@@ -98,7 +99,7 @@ private:
 	{
 	public:
 		ImGuiTextFilter m_filter;
-		SceneNode* m_visibleNode = nullptr;
+		SceneNode* m_selectedNode = nullptr;
 	} m_sceneHierarchyWindow;
 
 	class
@@ -176,6 +177,7 @@ private:
 	static void listDir(const std::filesystem::path& rootPath, const std::filesystem::path& parentPath, AssetPath& parent, U32& id);
 	static void gatherAssets(DynamicArray<AssetPath>& paths);
 	void loadImageToCache(CString fname, ImageResourcePtr& img);
+	void selectSceneNode();
 };
 
 } // end namespace anki

+ 344 - 135
AnKi/Renderer/Dbg.cpp

@@ -18,6 +18,9 @@
 #include <AnKi/Util/CVarSet.h>
 #include <AnKi/Collision/ConvexHullShape.h>
 #include <AnKi/Physics/PhysicsWorld.h>
+#include <AnKi/GpuMemory/GpuVisibleTransientMemoryPool.h>
+#include <AnKi/Shaders/Include/GpuVisibilityTypes.h>
+#include <AnKi/Window/Input.h>
 
 namespace anki {
 
@@ -36,6 +39,14 @@ Error Dbg::init()
 															  Format::kR8G8B8A8_Unorm, "Dbg");
 	m_rtDescr.bake();
 
+	m_objectPickingRtDescr = getRenderer().create2DRenderTargetDescription(
+		getRenderer().getInternalResolution().x() / 2, getRenderer().getInternalResolution().y() / 2, Format::kR32_Uint, "ObjectPicking");
+	m_objectPickingRtDescr.bake();
+
+	m_objectPickingDepthRtDescr = getRenderer().create2DRenderTargetDescription(
+		getRenderer().getInternalResolution().x() / 2, getRenderer().getInternalResolution().y() / 2, Format::kD32_Sfloat, "ObjectPickingDepth");
+	m_objectPickingDepthRtDescr.bake();
+
 	ResourceManager& rsrcManager = ResourceManager::getSingleton();
 	ANKI_CHECK(rsrcManager.loadResource("EngineAssets/GiProbe.ankitex", m_giProbeImage));
 	ANKI_CHECK(rsrcManager.loadResource("EngineAssets/LightBulb.ankitex", m_pointLightImage));
@@ -113,7 +124,7 @@ void Dbg::drawNonRenderable(GpuSceneNonRenderableObjectType type, U32 objCount,
 							CommandBuffer& cmdb)
 {
 	ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
-	variantInitInfo.addMutation("DEPTH_FAIL_VISUALIZATION", U32(m_ditheredDepthTestOn != 0));
+	variantInitInfo.addMutation("DEPTH_FAIL_VISUALIZATION", !!(m_options & DbgOption::kDitheredDepthTest));
 	variantInitInfo.addMutation("OBJECT_TYPE", U32(type));
 	variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, "Bilboards");
 	const ShaderProgramResourceVariant* variant;
@@ -140,194 +151,392 @@ void Dbg::drawNonRenderable(GpuSceneNonRenderableObjectType type, U32 objCount,
 	cmdb.draw(PrimitiveTopology::kTriangles, 6, objCount);
 }
 
-void Dbg::run(RenderPassWorkContext& rgraphCtx, const RenderingContext& ctx)
+void Dbg::populateRenderGraph(RenderingContext& ctx)
 {
 	ANKI_TRACE_SCOPED_EVENT(Dbg);
-	ANKI_ASSERT(g_cvarRenderDbgScene || g_cvarRenderDbgPhysics);
-
-	CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
-
-	// Set common state
-	cmdb.setViewport(0, 0, getRenderer().getInternalResolution().x(), getRenderer().getInternalResolution().y());
-	cmdb.setDepthWrite(false);
 
-	cmdb.setBlendFactors(0, BlendFactor::kSrcAlpha, BlendFactor::kOneMinusSrcAlpha);
-	cmdb.setDepthCompareOperation((m_depthTestOn) ? CompareOperation::kLess : CompareOperation::kAlways);
-	cmdb.setLineWidth(2.0f);
-
-	cmdb.bindSampler(0, 0, getRenderer().getSamplers().m_nearestNearestClamp.get());
-	rgraphCtx.bindSrv(0, 0, getGBuffer().getDepthRt());
+	if(!isEnabled())
+	{
+		return;
+	}
 
-	// Common code for boxes stuff
+	// Debug visualization
+	if(!!(m_options & (DbgOption::kDbgScene)))
 	{
-		ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
-		variantInitInfo.addMutation("DEPTH_FAIL_VISUALIZATION", U32(m_ditheredDepthTestOn != 0));
-		variantInitInfo.addMutation("OBJECT_TYPE", 0);
-		variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, "Renderables");
-		const ShaderProgramResourceVariant* variant;
-		m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
-		cmdb.bindShaderProgram(&variant->getProgram());
-
-		class Constants
-		{
-		public:
-			Vec4 m_color;
-			Mat4 m_viewProjMat;
-		} consts;
-		consts.m_color = Vec4(1.0f, 0.0f, 1.0f, 1.0f);
-		consts.m_viewProjMat = ctx.m_matrices.m_viewProjection;
-
-		cmdb.setFastConstants(&consts, sizeof(consts));
-		cmdb.bindVertexBuffer(0, BufferView(m_cubeVertsBuffer.get()), sizeof(Vec3));
-		cmdb.setVertexAttribute(VertexAttributeSemantic::kPosition, 0, Format::kR32G32B32_Sfloat, 0);
-		cmdb.bindIndexBuffer(BufferView(m_cubeIndicesBuffer.get()), IndexType::kU16);
+		populateRenderGraphMain(ctx);
 	}
 
-	// GBuffer renderables
-	const U32 gbufferAllAabbCount = GpuSceneArrays::RenderableBoundingVolumeGBuffer::getSingleton().getElementCount();
-	if(g_cvarRenderDbgScene && gbufferAllAabbCount)
+	// Object picking
+	if(!!(m_options & DbgOption::kObjectPicking))
 	{
-		cmdb.bindSrv(1, 0, GpuSceneArrays::RenderableBoundingVolumeGBuffer::getSingleton().getBufferView());
+		populateRenderGraphObjectPicking(ctx);
+	}
+}
 
-		const GpuVisibilityOutput& visOut = getGBuffer().getVisibilityOutput();
-		cmdb.bindSrv(2, 0, visOut.m_visibleAaabbIndicesBuffer);
+void Dbg::populateRenderGraphMain(RenderingContext& ctx)
+{
+	RenderGraphBuilder& rgraph = ctx.m_renderGraphDescr;
 
-		cmdb.drawIndexed(PrimitiveTopology::kLines, 12 * 2, gbufferAllAabbCount);
-	}
+	m_runCtx.m_rt = rgraph.newRenderTarget(m_rtDescr);
 
-	// Forward shading renderables
-	const U32 forwardAllAabbCount = GpuSceneArrays::RenderableBoundingVolumeForward::getSingleton().getElementCount();
-	if(g_cvarRenderDbgScene && forwardAllAabbCount)
-	{
-		cmdb.bindSrv(1, 0, GpuSceneArrays::RenderableBoundingVolumeForward::getSingleton().getBufferView());
+	GraphicsRenderPass& pass = rgraph.newGraphicsRenderPass("Debug");
 
-		const GpuVisibilityOutput& visOut = getForwardShading().getGpuVisibilityOutput();
-		cmdb.bindSrv(2, 0, visOut.m_visibleAaabbIndicesBuffer);
+	GraphicsRenderPassTargetDesc colorRti(m_runCtx.m_rt);
+	colorRti.m_loadOperation = RenderTargetLoadOperation::kClear;
+	GraphicsRenderPassTargetDesc depthRti(getGBuffer().getDepthRt());
+	depthRti.m_loadOperation = RenderTargetLoadOperation::kLoad;
+	depthRti.m_subresource.m_depthStencilAspect = DepthStencilAspectBit::kDepth;
+	pass.setRenderpassInfo({colorRti}, &depthRti);
 
-		cmdb.drawIndexed(PrimitiveTopology::kLines, 12 * 2, forwardAllAabbCount);
-	}
+	pass.newTextureDependency(m_runCtx.m_rt, TextureUsageBit::kRtvDsvWrite);
+	pass.newTextureDependency(getGBuffer().getDepthRt(), TextureUsageBit::kSrvPixel | TextureUsageBit::kRtvDsvRead);
 
-	// Draw non-renderables
-	if(g_cvarRenderDbgScene)
+	const GpuVisibilityOutput& visOut = getGBuffer().getVisibilityOutput();
+	if(visOut.m_dependency.isValid())
 	{
-		drawNonRenderable(GpuSceneNonRenderableObjectType::kLight, GpuSceneArrays::Light::getSingleton().getElementCount(), ctx, *m_pointLightImage,
-						  cmdb);
-		drawNonRenderable(GpuSceneNonRenderableObjectType::kDecal, GpuSceneArrays::Decal::getSingleton().getElementCount(), ctx, *m_decalImage, cmdb);
-		drawNonRenderable(GpuSceneNonRenderableObjectType::kGlobalIlluminationProbe,
-						  GpuSceneArrays::GlobalIlluminationProbe::getSingleton().getElementCount(), ctx, *m_giProbeImage, cmdb);
-		drawNonRenderable(GpuSceneNonRenderableObjectType::kReflectionProbe, GpuSceneArrays::ReflectionProbe::getSingleton().getElementCount(), ctx,
-						  *m_reflectionImage, cmdb);
+		pass.newBufferDependency(visOut.m_dependency, BufferUsageBit::kSrvGeometry);
 	}
 
-	// Physics
-	if(g_cvarRenderDbgPhysics)
+	const GpuVisibilityOutput& fvisOut = getForwardShading().getGpuVisibilityOutput();
+	if(fvisOut.m_dependency.isValid())
 	{
-		class MyPhysicsDebugDrawerInterface final : public PhysicsDebugDrawerInterface
-		{
-		public:
-			RendererDynamicArray<HVec4> m_positions;
-			RendererDynamicArray<Array<U8, 4>> m_colors;
-
-			void drawLines(ConstWeakArray<Vec3> lines, Array<U8, 4> color) override
-			{
-				static constexpr U32 kMaxVerts = 1024 * 100;
+		pass.newBufferDependency(fvisOut.m_dependency, BufferUsageBit::kSrvGeometry);
+	}
 
-				for(const Vec3& pos : lines)
-				{
-					if(m_positions.getSize() >= kMaxVerts)
-					{
-						break;
-					}
+	pass.setWork([this, &ctx](RenderPassWorkContext& rgraphCtx) {
+		ANKI_TRACE_SCOPED_EVENT(Dbg);
+		ANKI_ASSERT(!!(m_options & DbgOption::kDbgScene));
 
-					m_positions.emplaceBack(HVec4(pos.xyz0()));
-					m_colors.emplaceBack(color);
-				}
-			}
-		} drawerInterface;
+		CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
 
-		PhysicsWorld::getSingleton().debugDraw(drawerInterface);
+		// Set common state
+		cmdb.setViewport(0, 0, getRenderer().getInternalResolution().x(), getRenderer().getInternalResolution().y());
+		cmdb.setDepthWrite(false);
 
-		const U32 vertCount = drawerInterface.m_positions.getSize();
-		if(vertCount)
-		{
-			HVec4* positions;
-			const BufferView positionBuff =
-				RebarTransientMemoryPool::getSingleton().allocate(drawerInterface.m_positions.getSizeInBytes(), sizeof(HVec4), positions);
-			memcpy(positions, drawerInterface.m_positions.getBegin(), drawerInterface.m_positions.getSizeInBytes());
+		cmdb.setBlendFactors(0, BlendFactor::kSrcAlpha, BlendFactor::kOneMinusSrcAlpha);
+		cmdb.setDepthCompareOperation(!!(m_options & DbgOption::kDepthTest) ? CompareOperation::kLess : CompareOperation::kAlways);
+		cmdb.setLineWidth(2.0f);
 
-			U8* colors;
-			const BufferView colorBuff =
-				RebarTransientMemoryPool::getSingleton().allocate(drawerInterface.m_colors.getSizeInBytes(), sizeof(U8) * 4, colors);
-			memcpy(colors, drawerInterface.m_colors.getBegin(), drawerInterface.m_colors.getSizeInBytes());
+		cmdb.bindSampler(0, 0, getRenderer().getSamplers().m_nearestNearestClamp.get());
+		rgraphCtx.bindSrv(0, 0, getGBuffer().getDepthRt());
 
+		// Common code for boxes stuff
+		{
 			ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
-			variantInitInfo.addMutation("DEPTH_FAIL_VISUALIZATION", U32(m_ditheredDepthTestOn != 0));
+			variantInitInfo.addMutation("DEPTH_FAIL_VISUALIZATION", !!(m_options & DbgOption::kDitheredDepthTest));
 			variantInitInfo.addMutation("OBJECT_TYPE", 0);
-			variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, "Lines");
+			variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, "RenderableBoxes");
 			const ShaderProgramResourceVariant* variant;
 			m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
 			cmdb.bindShaderProgram(&variant->getProgram());
 
-			cmdb.setVertexAttribute(VertexAttributeSemantic::kPosition, 0, Format::kR16G16B16A16_Sfloat, 0);
-			cmdb.setVertexAttribute(VertexAttributeSemantic::kColor, 1, Format::kR8G8B8A8_Unorm, 0);
-			cmdb.bindVertexBuffer(0, positionBuff, sizeof(HVec4));
-			cmdb.bindVertexBuffer(1, colorBuff, sizeof(U8) * 4);
+			class Constants
+			{
+			public:
+				Vec4 m_color;
+				Mat4 m_viewProjMat;
+			} consts;
+			consts.m_color = Vec4(1.0f, 0.0f, 1.0f, 1.0f);
+			consts.m_viewProjMat = ctx.m_matrices.m_viewProjection;
+
+			cmdb.setFastConstants(&consts, sizeof(consts));
+			cmdb.bindVertexBuffer(0, BufferView(m_cubeVertsBuffer.get()), sizeof(Vec3));
+			cmdb.setVertexAttribute(VertexAttributeSemantic::kPosition, 0, Format::kR32G32B32_Sfloat, 0);
+			cmdb.bindIndexBuffer(BufferView(m_cubeIndicesBuffer.get()), IndexType::kU16);
+		}
+
+		// GBuffer AABBs
+		const U32 gbufferAllAabbCount = GpuSceneArrays::RenderableBoundingVolumeGBuffer::getSingleton().getElementCount();
+		if(!!(m_options & DbgOption::kBoundingBoxes) && gbufferAllAabbCount)
+		{
+			cmdb.bindSrv(1, 0, GpuSceneArrays::RenderableBoundingVolumeGBuffer::getSingleton().getBufferView());
 
-			cmdb.setFastConstants(&ctx.m_matrices.m_viewProjection, sizeof(ctx.m_matrices.m_viewProjection));
+			const GpuVisibilityOutput& visOut = getGBuffer().getVisibilityOutput();
+			cmdb.bindSrv(2, 0, visOut.m_visibleAaabbIndicesBuffer);
 
-			cmdb.draw(PrimitiveTopology::kLines, vertCount);
+			cmdb.drawIndexed(PrimitiveTopology::kLines, 12 * 2, gbufferAllAabbCount);
+		}
+
+		// Forward shading renderables
+		const U32 forwardAllAabbCount = GpuSceneArrays::RenderableBoundingVolumeForward::getSingleton().getElementCount();
+		if(!!(m_options & DbgOption::kBoundingBoxes) && forwardAllAabbCount)
+		{
+			cmdb.bindSrv(1, 0, GpuSceneArrays::RenderableBoundingVolumeForward::getSingleton().getBufferView());
+
+			const GpuVisibilityOutput& visOut = getForwardShading().getGpuVisibilityOutput();
+			cmdb.bindSrv(2, 0, visOut.m_visibleAaabbIndicesBuffer);
+
+			cmdb.drawIndexed(PrimitiveTopology::kLines, 12 * 2, forwardAllAabbCount);
+		}
+
+		// Draw non-renderables
+		if(!!(m_options & DbgOption::kIcons))
+		{
+			drawNonRenderable(GpuSceneNonRenderableObjectType::kLight, GpuSceneArrays::Light::getSingleton().getElementCount(), ctx,
+							  *m_pointLightImage, cmdb);
+			drawNonRenderable(GpuSceneNonRenderableObjectType::kDecal, GpuSceneArrays::Decal::getSingleton().getElementCount(), ctx, *m_decalImage,
+							  cmdb);
+			drawNonRenderable(GpuSceneNonRenderableObjectType::kGlobalIlluminationProbe,
+							  GpuSceneArrays::GlobalIlluminationProbe::getSingleton().getElementCount(), ctx, *m_giProbeImage, cmdb);
+			drawNonRenderable(GpuSceneNonRenderableObjectType::kReflectionProbe, GpuSceneArrays::ReflectionProbe::getSingleton().getElementCount(),
+							  ctx, *m_reflectionImage, cmdb);
+		}
+
+		// Physics
+		if(!!(m_options & DbgOption::kPhysics))
+		{
+			class MyPhysicsDebugDrawerInterface final : public PhysicsDebugDrawerInterface
+			{
+			public:
+				RendererDynamicArray<HVec4> m_positions;
+				RendererDynamicArray<Array<U8, 4>> m_colors;
+
+				void drawLines(ConstWeakArray<Vec3> lines, Array<U8, 4> color) override
+				{
+					static constexpr U32 kMaxVerts = 1024 * 100;
+
+					for(const Vec3& pos : lines)
+					{
+						if(m_positions.getSize() >= kMaxVerts)
+						{
+							break;
+						}
+
+						m_positions.emplaceBack(HVec4(pos.xyz0()));
+						m_colors.emplaceBack(color);
+					}
+				}
+			} drawerInterface;
+
+			PhysicsWorld::getSingleton().debugDraw(drawerInterface);
+
+			const U32 vertCount = drawerInterface.m_positions.getSize();
+			if(vertCount)
+			{
+				HVec4* positions;
+				const BufferView positionBuff =
+					RebarTransientMemoryPool::getSingleton().allocate(drawerInterface.m_positions.getSizeInBytes(), sizeof(HVec4), positions);
+				memcpy(positions, drawerInterface.m_positions.getBegin(), drawerInterface.m_positions.getSizeInBytes());
+
+				U8* colors;
+				const BufferView colorBuff =
+					RebarTransientMemoryPool::getSingleton().allocate(drawerInterface.m_colors.getSizeInBytes(), sizeof(U8) * 4, colors);
+				memcpy(colors, drawerInterface.m_colors.getBegin(), drawerInterface.m_colors.getSizeInBytes());
+
+				ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
+				variantInitInfo.addMutation("DEPTH_FAIL_VISUALIZATION", !!(m_options & DbgOption::kDitheredDepthTest));
+				variantInitInfo.addMutation("OBJECT_TYPE", 0);
+				variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, "Lines");
+				const ShaderProgramResourceVariant* variant;
+				m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
+				cmdb.bindShaderProgram(&variant->getProgram());
+
+				cmdb.setVertexAttribute(VertexAttributeSemantic::kPosition, 0, Format::kR16G16B16A16_Sfloat, 0);
+				cmdb.setVertexAttribute(VertexAttributeSemantic::kColor, 1, Format::kR8G8B8A8_Unorm, 0);
+				cmdb.bindVertexBuffer(0, positionBuff, sizeof(HVec4));
+				cmdb.bindVertexBuffer(1, colorBuff, sizeof(U8) * 4);
+
+				cmdb.setFastConstants(&ctx.m_matrices.m_viewProjection, sizeof(ctx.m_matrices.m_viewProjection));
+
+				cmdb.draw(PrimitiveTopology::kLines, vertCount);
+			}
 		}
-	}
 
-	// Restore state
-	cmdb.setDepthCompareOperation(CompareOperation::kLess);
-	cmdb.setDepthWrite(true);
+		// Restore state
+		cmdb.setDepthCompareOperation(CompareOperation::kLess);
+		cmdb.setDepthWrite(true);
+	});
 }
 
-void Dbg::populateRenderGraph(RenderingContext& ctx)
+void Dbg::populateRenderGraphObjectPicking(RenderingContext& ctx)
 {
-	ANKI_TRACE_SCOPED_EVENT(Dbg);
+	RenderGraphBuilder& rgraph = ctx.m_renderGraphDescr;
+
+	const GpuVisibilityOutput& visOut = getGBuffer().getVisibilityOutput();
+	const GpuVisibilityOutput& fvisOut = getForwardShading().getGpuVisibilityOutput();
 
-	if(!g_cvarRenderDbgScene && !g_cvarRenderDbgPhysics)
+	U32 maxVisibleCount = 0;
+	if(visOut.containsDrawcalls())
 	{
-		return;
+		maxVisibleCount += U32(visOut.m_visibleAaabbIndicesBuffer.getRange() / sizeof(LodAndGpuSceneRenderableBoundingVolumeIndex));
 	}
+	if(fvisOut.containsDrawcalls())
+	{
+		maxVisibleCount += U32(fvisOut.m_visibleAaabbIndicesBuffer.getRange() / sizeof(LodAndGpuSceneRenderableBoundingVolumeIndex));
+	}
+	const BufferView drawIndirectArgsBuff =
+		GpuVisibleTransientMemoryPool::getSingleton().allocateStructuredBuffer<DrawIndexedIndirectArgs>(maxVisibleCount);
 
-	RenderGraphBuilder& rgraph = ctx.m_renderGraphDescr;
-
-	// Create RT
-	m_runCtx.m_rt = rgraph.newRenderTarget(m_rtDescr);
+	const BufferView drawCountBuff = GpuVisibleTransientMemoryPool::getSingleton().allocateStructuredBuffer<U32>(1);
+	const BufferHandle bufferHandle = rgraph.importBuffer(drawCountBuff, BufferUsageBit::kNone);
 
-	// Create pass
-	GraphicsRenderPass& pass = rgraph.newGraphicsRenderPass("Debug");
+	const BufferView lodAndRenderableIndicesBuff = GpuVisibleTransientMemoryPool::getSingleton().allocateStructuredBuffer<U32>(maxVisibleCount);
 
-	pass.setWork([this, &ctx](RenderPassWorkContext& rgraphCtx) {
-		run(rgraphCtx, ctx);
-	});
+	// Zero draw count
+	{
+		NonGraphicsRenderPass& pass = rgraph.newNonGraphicsRenderPass("Object Picking: Zero");
 
-	GraphicsRenderPassTargetDesc colorRti(m_runCtx.m_rt);
-	colorRti.m_loadOperation = RenderTargetLoadOperation::kClear;
-	GraphicsRenderPassTargetDesc depthRti(getGBuffer().getDepthRt());
-	depthRti.m_loadOperation = RenderTargetLoadOperation::kLoad;
-	depthRti.m_subresource.m_depthStencilAspect = DepthStencilAspectBit::kDepth;
-	pass.setRenderpassInfo({colorRti}, &depthRti);
+		pass.newBufferDependency(bufferHandle, BufferUsageBit::kCopyDestination);
 
-	pass.newTextureDependency(m_runCtx.m_rt, TextureUsageBit::kRtvDsvWrite);
-	pass.newTextureDependency(getGBuffer().getDepthRt(), TextureUsageBit::kSrvPixel | TextureUsageBit::kRtvDsvRead);
+		pass.setWork([drawCountBuff, lodAndRenderableIndicesBuff, drawIndirectArgsBuff](RenderPassWorkContext& rgraphCtx) {
+			CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
+			cmdb.zeroBuffer(drawCountBuff);
+			cmdb.zeroBuffer(lodAndRenderableIndicesBuff);
+			cmdb.zeroBuffer(drawIndirectArgsBuff);
+		});
+	}
 
-	if(g_cvarRenderDbgScene)
+	// Prepare pass
 	{
-		const GpuVisibilityOutput& visOut = getGBuffer().getVisibilityOutput();
+		NonGraphicsRenderPass& pass = rgraph.newNonGraphicsRenderPass("Object Picking: Prepare");
+
 		if(visOut.m_dependency.isValid())
 		{
-			pass.newBufferDependency(visOut.m_dependency, BufferUsageBit::kSrvGeometry);
+			pass.newBufferDependency(visOut.m_dependency, BufferUsageBit::kSrvCompute);
 		}
 
-		const GpuVisibilityOutput& fvisOut = getForwardShading().getGpuVisibilityOutput();
 		if(fvisOut.m_dependency.isValid())
 		{
-			pass.newBufferDependency(fvisOut.m_dependency, BufferUsageBit::kSrvGeometry);
+			pass.newBufferDependency(fvisOut.m_dependency, BufferUsageBit::kSrvCompute);
 		}
+
+		pass.newBufferDependency(bufferHandle, BufferUsageBit::kUavCompute);
+
+		pass.setWork([this, drawIndirectArgsBuff, drawCountBuff, lodAndRenderableIndicesBuff](RenderPassWorkContext& rgraphCtx) {
+			CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
+
+			ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
+			variantInitInfo.addMutation("DEPTH_FAIL_VISUALIZATION", 0);
+			variantInitInfo.addMutation("OBJECT_TYPE", 0);
+			variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kCompute, "PrepareRenderableUuids");
+			const ShaderProgramResourceVariant* variant;
+			m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
+			cmdb.bindShaderProgram(&variant->getProgram());
+
+			cmdb.bindSrv(1, 0, GpuSceneArrays::Renderable::getSingleton().getBufferView());
+			cmdb.bindSrv(2, 0, GpuSceneArrays::MeshLod::getSingleton().getBufferView());
+
+			cmdb.bindUav(0, 0, drawIndirectArgsBuff);
+			cmdb.bindUav(1, 0, drawCountBuff);
+			cmdb.bindUav(2, 0, lodAndRenderableIndicesBuff);
+
+			// Do GBuffer
+			const GpuVisibilityOutput& visOut = getGBuffer().getVisibilityOutput();
+			if(visOut.containsDrawcalls())
+			{
+				cmdb.bindSrv(0, 0, GpuSceneArrays::RenderableBoundingVolumeGBuffer::getSingleton().getBufferView());
+				cmdb.bindSrv(3, 0, visOut.m_visibleAaabbIndicesBuffer);
+
+				const U32 allAabbCount = GpuSceneArrays::RenderableBoundingVolumeGBuffer::getSingleton().getElementCount();
+				cmdb.dispatchCompute((allAabbCount + 63) / 64, 1, 1);
+			}
+
+			// Do ForwardShading
+			const GpuVisibilityOutput& fvisOut = getForwardShading().getGpuVisibilityOutput();
+			if(fvisOut.containsDrawcalls())
+			{
+				cmdb.bindSrv(0, 0, GpuSceneArrays::RenderableBoundingVolumeForward::getSingleton().getBufferView());
+				cmdb.bindSrv(3, 0, fvisOut.m_visibleAaabbIndicesBuffer);
+
+				const U32 allAabbCount = GpuSceneArrays::RenderableBoundingVolumeForward::getSingleton().getElementCount();
+				cmdb.dispatchCompute((allAabbCount + 63) / 64, 1, 1);
+			}
+		});
+	}
+
+	// The render pass that draws the UUIDs to a buffer
+	const RenderTargetHandle objectPickingRt = rgraph.newRenderTarget(m_objectPickingRtDescr);
+	const RenderTargetHandle objectPickingDepthRt = rgraph.newRenderTarget(m_objectPickingDepthRtDescr);
+	{
+		GraphicsRenderPass& pass = rgraph.newGraphicsRenderPass("Object Picking: Draw UUIDs");
+
+		pass.newBufferDependency(bufferHandle, BufferUsageBit::kIndirectDraw);
+		pass.newTextureDependency(objectPickingRt, TextureUsageBit::kRtvDsvWrite);
+		pass.newTextureDependency(objectPickingDepthRt, TextureUsageBit::kRtvDsvWrite);
+
+		GraphicsRenderPassTargetDesc colorRti(objectPickingRt);
+		colorRti.m_loadOperation = RenderTargetLoadOperation::kClear;
+		GraphicsRenderPassTargetDesc depthRti(objectPickingDepthRt);
+		depthRti.m_loadOperation = RenderTargetLoadOperation::kClear;
+		depthRti.m_clearValue.m_depthStencil.m_depth = 1.0;
+		depthRti.m_subresource.m_depthStencilAspect = DepthStencilAspectBit::kDepth;
+		pass.setRenderpassInfo({colorRti}, &depthRti);
+
+		pass.setWork(
+			[this, lodAndRenderableIndicesBuff, &ctx, drawIndirectArgsBuff, drawCountBuff, maxVisibleCount](RenderPassWorkContext& rgraphCtx) {
+				CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
+
+				// Set common state
+				cmdb.setViewport(0, 0, getRenderer().getInternalResolution().x() / 2, getRenderer().getInternalResolution().y() / 2);
+				cmdb.setDepthCompareOperation(CompareOperation::kLess);
+
+				ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
+				variantInitInfo.addMutation("DEPTH_FAIL_VISUALIZATION", 0);
+				variantInitInfo.addMutation("OBJECT_TYPE", 0);
+				variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, "RenderableUuids");
+				const ShaderProgramResourceVariant* variant;
+				m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
+				cmdb.bindShaderProgram(&variant->getProgram());
+
+				cmdb.bindIndexBuffer(UnifiedGeometryBuffer::getSingleton().getBufferView(), IndexType::kU16);
+
+				cmdb.bindSrv(0, 0, lodAndRenderableIndicesBuff);
+				cmdb.bindSrv(1, 0, GpuSceneArrays::Renderable::getSingleton().getBufferView());
+				cmdb.bindSrv(2, 0, GpuSceneArrays::MeshLod::getSingleton().getBufferView());
+				cmdb.bindSrv(3, 0, GpuSceneArrays::Transform::getSingleton().getBufferView());
+				cmdb.bindSrv(4, 0, UnifiedGeometryBuffer::getSingleton().getBufferView(), Format::kR16G16B16A16_Unorm);
+
+				cmdb.setFastConstants(&ctx.m_matrices.m_viewProjection, sizeof(ctx.m_matrices.m_viewProjection));
+
+				cmdb.drawIndexedIndirectCount(PrimitiveTopology::kTriangles, drawIndirectArgsBuff, sizeof(DrawIndexedIndirectArgs), drawCountBuff,
+											  maxVisibleCount);
+			});
+	}
+
+	// Read the UUID RT to get the UUID that is over the mouse
+	{
+		U32 uuid;
+		PtrSize dataOut;
+		getRenderer().getReadbackManager().readMostRecentData(m_readback, &uuid, sizeof(uuid), dataOut);
+		if(dataOut)
+		{
+			m_runCtx.m_objUuid = uuid;
+		}
+		else
+		{
+			m_runCtx.m_objUuid = 0;
+		}
+
+		const BufferView readbackBuff = getRenderer().getReadbackManager().allocateStructuredBuffer<U32>(m_readback, 1);
+
+		NonGraphicsRenderPass& pass = rgraph.newNonGraphicsRenderPass("Object Picking: Picking");
+
+		pass.newTextureDependency(objectPickingRt, TextureUsageBit::kSrvCompute);
+
+		pass.setWork([this, readbackBuff, objectPickingRt](RenderPassWorkContext& rgraphCtx) {
+			CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
+
+			ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
+			variantInitInfo.addMutation("DEPTH_FAIL_VISUALIZATION", 0);
+			variantInitInfo.addMutation("OBJECT_TYPE", 0);
+			variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kCompute, "RenderableUuidsPick");
+			const ShaderProgramResourceVariant* variant;
+			m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
+			cmdb.bindShaderProgram(&variant->getProgram());
+
+			rgraphCtx.bindSrv(0, 0, objectPickingRt);
+			cmdb.bindUav(0, 0, readbackBuff);
+
+			Vec2 mousePos = Input::getSingleton().getMousePositionNdc();
+			mousePos.y() = -mousePos.y();
+			mousePos = mousePos / 2.0f + 0.5f;
+			mousePos *= Vec2(getRenderer().getInternalResolution() / 2);
+
+			const UVec4 consts(UVec2(mousePos), 0u, 0u);
+			cmdb.setFastConstants(&consts, sizeof(consts));
+
+			cmdb.dispatchCompute(1, 1, 1);
+		});
 	}
 }
 

+ 42 - 26
AnKi/Renderer/Dbg.h

@@ -6,18 +6,34 @@
 #pragma once
 
 #include <AnKi/Renderer/RendererObject.h>
+#include <AnKi/Renderer/Utils/Readback.h>
 #include <AnKi/Gr.h>
 #include <AnKi/Util/Enum.h>
 
 namespace anki {
 
-/// @addtogroup renderer
-/// @{
-
-ANKI_CVAR2(BoolCVar, Render, Dbg, Scene, false, "Enable or not debug visualization of scene")
-ANKI_CVAR2(BoolCVar, Render, Dbg, Physics, false, "Enable or not physics debug visualization")
+enum class DbgOption : U8
+{
+	kNone = 0,
+
+	// Flags that draw something somewhere
+	kBoundingBoxes = 1 << 0,
+	kIcons = 1 << 1,
+	kPhysics = 1 << 2,
+	kObjectPicking = 1 << 3,
+
+	// Flags that affect how things are drawn
+	kDepthTest = 1 << 4,
+	kDitheredDepthTest = 1 << 5,
+
+	// Agregate flags
+	kGatherAabbs = kBoundingBoxes | kObjectPicking,
+	kDbgScene = kBoundingBoxes | kIcons | kPhysics,
+	kDbgEnabled = kDbgScene | kObjectPicking,
+};
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(DbgOption)
 
-/// Debugging stage
+// Debugging visualization of the scene.
 class Dbg : public RendererObject
 {
 public:
@@ -27,7 +43,6 @@ public:
 
 	Error init();
 
-	/// Populate the rendergraph.
 	void populateRenderGraph(RenderingContext& ctx);
 
 	RenderTargetHandle getRt() const
@@ -35,38 +50,36 @@ public:
 		return m_runCtx.m_rt;
 	}
 
-	Bool getDepthTestEnabled() const
-	{
-		return m_depthTestOn;
-	}
-
-	void setDepthTestEnabled(Bool enable)
+	void setOptions(DbgOption options)
 	{
-		m_depthTestOn = enable;
+		m_options = options;
 	}
 
-	void switchDepthTestEnabled()
+	void enableOptions(DbgOption options)
 	{
-		m_depthTestOn = !m_depthTestOn;
+		m_options |= options;
 	}
 
-	Bool getDitheredDepthTestEnabled() const
+	DbgOption getOptions() const
 	{
-		return m_ditheredDepthTestOn;
+		return m_options;
 	}
 
-	void setDitheredDepthTestEnabled(Bool enable)
+	Bool isEnabled() const
 	{
-		m_ditheredDepthTestOn = enable;
+		return !!(m_options & DbgOption::kDbgEnabled);
 	}
 
-	void switchDitheredDepthTestEnabled()
+	U32 getObjectUuidAtMousePosition() const
 	{
-		m_ditheredDepthTestOn = !m_ditheredDepthTestOn;
+		ANKI_ASSERT(!!(m_options & DbgOption::kObjectPicking));
+		return m_runCtx.m_objUuid;
 	}
 
 private:
 	RenderTargetDesc m_rtDescr;
+	RenderTargetDesc m_objectPickingRtDescr;
+	RenderTargetDesc m_objectPickingDepthRtDescr;
 
 	ImageResourcePtr m_giProbeImage;
 	ImageResourcePtr m_pointLightImage;
@@ -79,20 +92,23 @@ private:
 
 	ShaderProgramResourcePtr m_dbgProg;
 
-	Bool m_depthTestOn : 1 = false;
-	Bool m_ditheredDepthTestOn : 1 = false;
+	MultiframeReadbackToken m_readback;
+
+	DbgOption m_options = DbgOption::kDepthTest;
 
 	class
 	{
 	public:
 		RenderTargetHandle m_rt;
+		U32 m_objUuid = 0;
 	} m_runCtx;
 
-	void run(RenderPassWorkContext& rgraphCtx, const RenderingContext& ctx);
+	void populateRenderGraphMain(RenderingContext& ctx);
+
+	void populateRenderGraphObjectPicking(RenderingContext& ctx);
 
 	void drawNonRenderable(GpuSceneNonRenderableObjectType type, U32 objCount, const RenderingContext& ctx, const ImageResource& image,
 						   CommandBuffer& cmdb);
 };
-/// @}
 
 } // end namespace anki

+ 2 - 2
AnKi/Renderer/FinalComposite.cpp

@@ -107,7 +107,7 @@ void FinalComposite::populateRenderGraph(RenderingContext& ctx)
 
 	pass.newTextureDependency(outRt, TextureUsageBit::kRtvDsvWrite);
 
-	if(g_cvarRenderDbgScene || g_cvarRenderDbgPhysics)
+	if(!!(getDbg().getOptions() & DbgOption::kDbgScene))
 	{
 		pass.newTextureDependency(getRenderer().getDbg().getRt(), TextureUsageBit::kSrvPixel);
 	}
@@ -137,7 +137,7 @@ void FinalComposite::populateRenderGraph(RenderingContext& ctx)
 		ANKI_TRACE_SCOPED_EVENT(FinalComposite);
 
 		CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
-		const Bool dbgEnabled = g_cvarRenderDbgScene || g_cvarRenderDbgPhysics;
+		const Bool dbgEnabled = !!(getDbg().getOptions() & DbgOption::kDbgScene);
 
 		Array<RenderTargetHandle, kMaxDebugRenderTargets> dbgRts;
 		Array<DebugRenderTargetDrawStyle, kMaxDebugRenderTargets> drawStyles;

+ 1 - 1
AnKi/Renderer/ForwardShading.cpp

@@ -36,7 +36,7 @@ void ForwardShading::populateRenderGraph(RenderingContext& ctx)
 	visIn.m_lodReferencePoint = ctx.m_matrices.m_cameraTransform.getTranslationPart().xyz();
 	visIn.m_lodDistances = lodDistances;
 	visIn.m_rgraph = &rgraph;
-	visIn.m_gatherAabbIndices = g_cvarRenderDbgScene;
+	visIn.m_gatherAabbIndices = !!(getDbg().getOptions() & DbgOption::kGatherAabbs);
 	RenderTargetHandle hzb = getGBuffer().getHzbRt();
 	visIn.m_hzbRt = &hzb;
 	visIn.m_viewportSize = getRenderer().getInternalResolution();

+ 1 - 1
AnKi/Renderer/GBuffer.cpp

@@ -106,7 +106,7 @@ void GBuffer::populateRenderGraph(RenderingContext& ctx)
 		visIn.m_lodDistances = lodDistances;
 		visIn.m_rgraph = &rgraph;
 		visIn.m_hzbRt = &m_runCtx.m_hzbRt;
-		visIn.m_gatherAabbIndices = g_cvarRenderDbgScene;
+		visIn.m_gatherAabbIndices = !!(getDbg().getOptions() & DbgOption::kGatherAabbs);
 		visIn.m_viewportSize = getRenderer().getInternalResolution();
 		visIn.m_twoPhaseOcclusionCulling = getRenderer().getMeshletRenderingType() != MeshletRenderingType::kNone;
 

+ 1 - 1
AnKi/Renderer/Renderer.cpp

@@ -817,7 +817,7 @@ Error Renderer::render()
 	// First thing, reset the temp mem pool
 	m_framePool.reset();
 
-	m_uiStage->buildUiAsync();
+	m_uiStage->buildUi();
 
 	RenderingContext ctx(&m_framePool);
 	ctx.m_renderGraphDescr.setStatisticsEnabled(ANKI_STATS_ENABLED);

+ 10 - 15
AnKi/Renderer/UiStage.cpp

@@ -20,7 +20,7 @@ Error UiStage::init()
 	return Error::kNone;
 }
 
-void UiStage::buildUiAsync()
+void UiStage::buildUi()
 {
 	if(SceneGraph::getSingleton().getComponentArrays().getUis().getSize() == 0)
 	{
@@ -28,20 +28,18 @@ void UiStage::buildUiAsync()
 		return;
 	}
 
-	CoreThreadJobManager::getSingleton().dispatchTask([this]([[maybe_unused]] U32 tid) {
-		ANKI_TRACE_SCOPED_EVENT(UiBuild);
+	ANKI_TRACE_SCOPED_EVENT(UiBuild);
 
-		m_canvas->handleInput();
-		m_canvas->beginBuilding();
-		m_canvas->resize(getRenderer().getSwapchainResolution());
+	m_canvas->handleInput();
+	m_canvas->beginBuilding();
+	m_canvas->resize(getRenderer().getSwapchainResolution());
 
-		for(UiComponent& comp : SceneGraph::getSingleton().getComponentArrays().getUis())
-		{
-			comp.drawUi(*m_canvas);
-		}
+	for(UiComponent& comp : SceneGraph::getSingleton().getComponentArrays().getUis())
+	{
+		comp.drawUi(*m_canvas);
+	}
 
-		m_canvas->endBuilding();
-	});
+	m_canvas->endBuilding();
 }
 
 void UiStage::populateRenderGraph(RenderingContext& ctx)
@@ -51,9 +49,6 @@ void UiStage::populateRenderGraph(RenderingContext& ctx)
 	RenderGraphBuilder& rgraph = ctx.m_renderGraphDescr;
 	DynamicArray<RenderTargetHandle, MemoryPoolPtrWrapper<StackMemoryPool>> texHandles(&getRenderer().getFrameMemoryPool());
 
-	// Wait for the async task
-	CoreThreadJobManager::getSingleton().waitForAllTasksToFinish();
-
 	m_canvas->visitTexturesForUpdate([&](Texture& tex, Bool isNew) {
 		const RenderTargetHandle handle = rgraph.importRenderTarget(&tex, (isNew) ? TextureUsageBit::kNone : TextureUsageBit::kSrvPixel);
 		texHandles.emplaceBack(handle);

+ 1 - 2
AnKi/Renderer/UiStage.h

@@ -19,8 +19,7 @@ class UiStage : public RendererObject
 public:
 	Error init();
 
-	/// Need to wait the CoreThreadJobManager for that to finish and before calling the methods bellow.
-	void buildUiAsync();
+	void buildUi();
 
 	void populateRenderGraph(RenderingContext& ctx);
 

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

@@ -107,7 +107,8 @@ public:
 		BufferView m_firstMeshletBuffer; // For H/W meshlet rendering. Points to the first meshlet in the m_meshletInstancesBuffer. One per bucket.
 	} m_mesh; // S/W or H/W meshlet rendering.
 
-	BufferView m_visibleAaabbIndicesBuffer; // [Optional] Indices to the AABB buffer. The 1st element is the count.
+	// [Optional] Indices to the AABB buffer (LodAndGpuSceneRenderableBoundingVolumeIndex). The 1st element is the count
+	BufferView m_visibleAaabbIndicesBuffer;
 
 	BufferView m_visiblesHashBuffer; // [Optional] A hash of the visible objects. Used to conditionaly not perform shadow randering.
 

+ 1 - 0
AnKi/Scene/Components/MaterialComponent.cpp

@@ -322,6 +322,7 @@ void MaterialComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 
 		const UVec3 u3(averageDiffuse.xyz().clamp(0.0f, 1.0f) * 255.0f);
 		gpuRenderable.m_diffuseColor = ((u3.x() << 16u) | (u3.y() << 8u) | u3.z()) & 0xFFFFFFF;
+		gpuRenderable.m_sceneNodeUuid = info.m_node->getUuid();
 
 		m_gpuSceneRenderable.uploadToGpuScene(gpuRenderable);
 	}

+ 1 - 2
AnKi/Scene/SceneGraph.h

@@ -106,8 +106,7 @@ public:
 	{
 		for(SceneNode& psn : m_nodes)
 		{
-			const Bool continue_ = func(psn);
-			if(!continue_)
+			if(func(psn) == FunctorContinue::kStop)
 			{
 				break;
 			}

+ 152 - 6
AnKi/Shaders/Dbg.ankiprog

@@ -6,16 +6,27 @@
 #pragma anki mutator DEPTH_FAIL_VISUALIZATION 0 1
 #pragma anki mutator OBJECT_TYPE 0 1 2 3 4 // Same as GpuSceneNonRenderableObjectType
 
-#pragma anki technique Renderables vert pixel mutators DEPTH_FAIL_VISUALIZATION
+#pragma anki technique RenderableBoxes vert pixel mutators DEPTH_FAIL_VISUALIZATION
 #pragma anki technique Bilboards vert pixel
 #pragma anki technique Lines vert pixel mutators DEPTH_FAIL_VISUALIZATION
 
+// Prepare the indirect calls for RenderableUuids
+#pragma anki technique PrepareRenderableUuids comp mutators
+
+// Draw the renderables to output the UUID
+#pragma anki technique RenderableUuids vert pixel mutators
+
+// Pick one UUID at mouse position
+#pragma anki technique RenderableUuidsPick comp mutators
+
 #include <AnKi/Shaders/Common.hlsl>
+#include <AnKi/Shaders/Include/GpuVisibilityTypes.h>
+#include <AnKi/Shaders/Include/GpuSceneTypes.h>
 
 // ===========================================================================
-// Renderables                                                               =
+// RenderableBoxes                                                           =
 // ===========================================================================
-#if NOT_ZERO(ANKI_TECHNIQUE_Renderables)
+#if NOT_ZERO(ANKI_TECHNIQUE_RenderableBoxes)
 #	include <AnKi/Shaders/Include/GpuSceneTypes.h>
 
 struct Constants
@@ -27,7 +38,7 @@ struct Constants
 ANKI_FAST_CONSTANTS(Constants, g_consts)
 
 StructuredBuffer<GpuSceneRenderableBoundingVolume> g_renderableBoundingVolumes : register(t1);
-StructuredBuffer<U32> g_visibleRenderableBoundingVolumeIndices : register(t2);
+StructuredBuffer<LodAndGpuSceneRenderableBoundingVolumeIndex> g_visibleRenderableBoundingVolumeIndices : register(t2);
 
 struct VertOut
 {
@@ -49,7 +60,8 @@ VertOut main(VertIn input)
 
 	if(input.m_svInstanceId < bvolumeCount)
 	{
-		const GpuSceneRenderableBoundingVolume bvol = g_renderableBoundingVolumes[g_visibleRenderableBoundingVolumeIndices[input.m_svInstanceId + 1]];
+		const U32 bvolIdx = g_visibleRenderableBoundingVolumeIndices[input.m_svInstanceId + 1] & ((1u << 29u) - 1u);
+		const GpuSceneRenderableBoundingVolume bvol = g_renderableBoundingVolumes[bvolIdx];
 		const Vec3 boxCenter = (bvol.m_aabbMin + bvol.m_aabbMax) * 0.5f;
 		Vec3 localPos = input.m_position * (bvol.m_aabbMax - boxCenter) + boxCenter;
 		output.m_svPosition = mul(g_consts.m_viewProjMat, Vec4(localPos, 1.0));
@@ -94,7 +106,7 @@ Vec4 main(VertOut input) : SV_TARGET0
 	return g_consts.m_color;
 }
 #	endif // ANKI_PIXEL_SHADER
-#endif // ANKI_TECHNIQUE_Renderables
+#endif // ANKI_TECHNIQUE_RenderableBoxes
 
 // ===========================================================================
 // Bilboards                                                                 =
@@ -275,3 +287,137 @@ Vec4 main(VertOut input) : SV_TARGET0
 }
 #	endif // ANKI_PIXEL_SHADER
 #endif // ANKI_TECHNIQUE_Lines
+
+// ===========================================================================
+// PrepareRenderableUuids                                                    =
+// ===========================================================================
+#if ANKI_TECHNIQUE_PrepareRenderableUuids
+
+StructuredBuffer<GpuSceneRenderableBoundingVolume> g_renderableBoundingVolumes : register(t0);
+StructuredBuffer<GpuSceneRenderable> g_renderables : register(t1);
+StructuredBuffer<GpuSceneMeshLod> g_meshLods : register(t2);
+StructuredBuffer<LodAndGpuSceneRenderableBoundingVolumeIndex> g_visibleRenderableBoundingVolumeIndices : register(t3);
+
+RWStructuredBuffer<DrawIndexedIndirectArgs> g_drawArgs : register(u0);
+RWStructuredBuffer<U32> g_drawArgCount : register(u1);
+
+RWStructuredBuffer<U32> g_lodAndRenderableIndices : register(u2);
+
+[numthreads(64, 1, 1)] void main(COMPUTE_ARGS)
+{
+	if(svDispatchThreadId.x >= SBUFF(g_visibleRenderableBoundingVolumeIndices, 0))
+	{
+		return;
+	}
+
+	const LodAndGpuSceneRenderableBoundingVolumeIndex packed = SBUFF(g_visibleRenderableBoundingVolumeIndices, svDispatchThreadId.x + 1);
+	const U32 lod = packed >> 29u;
+	const U32 bvolIdx = packed & ((1u << 29u) - 1u);
+
+	const GpuSceneRenderableBoundingVolume bvol = SBUFF(g_renderableBoundingVolumes, bvolIdx);
+	const GpuSceneRenderable renderable = SBUFF(g_renderables, bvol.m_renderableIndex);
+
+	const Bool isParticleEmitter = renderable.m_particleEmitterIndex < kMaxU32 || renderable.m_particleEmitterIndex2 < kMaxU32;
+	if(isParticleEmitter)
+	{
+		// Can't draw particle emitters as is
+		return;
+	}
+
+	const GpuSceneMeshLod meshLod = SBUFF(g_meshLods, renderable.m_meshLodsIndex + lod);
+
+	DrawIndexedIndirectArgs indirect;
+	indirect.m_indexCount = meshLod.m_indexCount;
+	indirect.m_instanceCount = 1;
+	indirect.m_firstIndex = meshLod.m_firstIndex;
+	indirect.m_vertexOffset = 0;
+	indirect.m_firstInstance = 0;
+
+	U32 index;
+	InterlockedAdd(g_drawArgCount[0], 1, index);
+
+	SBUFF(g_drawArgs, index) = indirect;
+
+	U32 packed2 = lod << 29u;
+	packed2 |= bvol.m_renderableIndex;
+	SBUFF(g_lodAndRenderableIndices, index) = packed2;
+}
+#endif
+
+// ===========================================================================
+// RenderableUuids                                                           =
+// ===========================================================================
+#if ANKI_TECHNIQUE_RenderableUuids
+
+StructuredBuffer<U32> g_lodAndRenderableIndices : register(t0);
+StructuredBuffer<GpuSceneRenderable> g_renderables : register(t1);
+StructuredBuffer<GpuSceneMeshLod> g_meshLods : register(t2);
+StructuredBuffer<Mat3x4> g_transforms : register(t3);
+Buffer<Vec4> g_unifiedGeom_R16G16B16A16_Unorm : register(t4);
+
+ANKI_FAST_CONSTANTS(Mat4, g_mvp);
+
+struct VertOut
+{
+	Vec4 m_svPosition : SV_POSITION;
+	U32 m_sceneNodeUuid : SCENE_NODE_UUID;
+};
+
+#	if ANKI_VERTEX_SHADER
+VertOut main(U32 svVertexId : SV_VERTEXID)
+{
+	const U32 packed = SBUFF(g_lodAndRenderableIndices, gl_DrawID);
+	const U32 lod = packed >> 29u;
+	ANKI_ASSERT(lod < kMaxLodCount);
+	const U32 renderableIdx = packed & ((1u << 29u) - 1u);
+
+	const GpuSceneRenderable renderable = SBUFF(g_renderables, renderableIdx);
+
+	const GpuSceneMeshLod meshLod = SBUFF(g_meshLods, renderable.m_meshLodsIndex + lod);
+
+	Vec3 position = g_unifiedGeom_R16G16B16A16_Unorm[meshLod.m_vertexOffsets[(U32)VertexStreamId::kPosition] + svVertexId];
+	position = position * meshLod.m_positionScale + meshLod.m_positionTranslation;
+
+	const Mat3x4 trf = SBUFF(g_transforms, renderable.m_worldTransformsIndex);
+	position = mul(trf, Vec4(position, 1.0));
+
+	VertOut output;
+	output.m_svPosition = mul(g_mvp, Vec4(position, 1.0));
+	output.m_sceneNodeUuid = renderable.m_sceneNodeUuid;
+	return output;
+}
+#	endif
+
+#	if ANKI_PIXEL_SHADER
+U32 main(VertOut input) : SV_TARGET0
+{
+	return input.m_sceneNodeUuid;
+}
+#	endif
+
+#endif
+
+// ===========================================================================
+// RenderableUuidsPick                                                       =
+// ===========================================================================
+#if ANKI_TECHNIQUE_RenderableUuidsPick
+
+Texture2D<UVec4> g_uuidsTex : register(t0);
+
+RWStructuredBuffer<U32> g_outUuid : register(u0);
+
+struct Consts
+{
+	UVec2 m_mousePos;
+	UVec2 m_padding;
+};
+
+ANKI_FAST_CONSTANTS(Consts, g_consts);
+
+[numthreads(1, 1, 1)] void main()
+{
+	const U32 uuid = g_uuidsTex[g_consts.m_mousePos].x;
+	g_outUuid[0] = uuid;
+}
+
+#endif

+ 1 - 1
AnKi/Shaders/GpuVisibilityAccelerationStructures.ankiprog

@@ -84,7 +84,7 @@ ANKI_FAST_CONSTANTS(GpuVisibilityAccelerationStructuresConstants, g_consts)
 			lod = 2u;
 		}
 
-		const U32 renderableIdx = bvolume.m_renderableIndex_20bit_renderStateBucket_12bit >> 12u;
+		const U32 renderableIdx = bvolume.m_renderableIndex;
 		const GpuSceneRenderable renderable = SBUFF(g_renderables, renderableIdx);
 
 		const U32 meshLodIndex = renderable.m_meshLodsIndex + lod;

+ 20 - 4
AnKi/Shaders/GpuVisibilityStage1.ankiprog

@@ -51,7 +51,8 @@ RWStructuredBuffer<DispatchIndirectArgs> g_gpuVisIndirectDispatchArgs : register
 RWStructuredBuffer<U32> g_outOfMemoryBuffer : register(u6);
 
 #if GATHER_AABBS
-RWStructuredBuffer<U32> g_visibleAabbIndices : register(u7); // Indices of the visible AABBs. The 1st element is the count.
+// Indices of the visible AABBs. The 1st element is the count.
+RWStructuredBuffer<LodAndGpuSceneRenderableBoundingVolumeIndex> g_visibleAabbIndices : register(u7);
 #endif
 
 #if HASH_VISIBLES
@@ -175,9 +176,9 @@ Bool isVisible(GpuSceneRenderableBoundingVolume bvolume)
 
 		// Add the object
 		//
-		const U32 renderableIdx = bvolume.m_renderableIndex_20bit_renderStateBucket_12bit >> 12u;
+		const U32 renderableIdx = bvolume.m_renderableIndex;
 		const GpuSceneRenderable renderable = SBUFF(g_renderables, renderableIdx);
-		const U32 renderStateBucket = bvolume.m_renderableIndex_20bit_renderStateBucket_12bit & ((1u << 12u) - 1u);
+		const U32 renderStateBucket = bvolume.m_renderStateBucket;
 		const U32 meshLodIndex = renderable.m_meshLodsIndex + lod;
 		const GpuSceneMeshLod meshLod = SBUFF(g_meshLods, meshLodIndex);
 
@@ -278,7 +279,12 @@ Bool isVisible(GpuSceneRenderableBoundingVolume bvolume)
 #if GATHER_AABBS
 		U32 index;
 		InterlockedAdd(SBUFF(g_visibleAabbIndices, 0), 1, index);
-		SBUFF(g_visibleAabbIndices, index + 1) = bvolumeIdx;
+		const U32 maxCount = getStructuredBufferElementCount(g_visibleAabbIndices) - 1;
+		if(index < maxCount)
+		{
+			const LodAndGpuSceneRenderableBoundingVolumeIndex pack = (lod << 29u) | bvolumeIdx;
+			SBUFF(g_visibleAabbIndices, index + 1) = pack;
+		}
 #endif
 	}
 
@@ -357,6 +363,16 @@ Bool isVisible(GpuSceneRenderableBoundingVolume bvolume)
 
 			// Reset it for the next job
 			SBUFF(g_counters, (U32)GpuVisibilityCounter::kThreadgroupCount) = 0;
+
+#if GATHER_AABBS
+			// Fix the count if needed
+			const U32 count = SBUFF(g_visibleAabbIndices, 0);
+			const U32 maxCount = getStructuredBufferElementCount(g_visibleAabbIndices) - 1;
+			if(count > maxCount)
+			{
+				SBUFF(g_visibleAabbIndices, 0) = maxCount;
+			}
+#endif
 		}
 	}
 }

+ 2 - 2
AnKi/Shaders/Include/GpuSceneFunctions.h

@@ -34,10 +34,10 @@ inline GpuSceneRenderableBoundingVolume initGpuSceneRenderableBoundingVolume(Vec
 #endif
 
 	ANKI_ASSERT(renderableIndex <= (1u << 20u) - 1u);
-	gpuVolume.m_renderableIndex_20bit_renderStateBucket_12bit = renderableIndex << 12u;
+	gpuVolume.m_renderableIndex = renderableIndex;
 
 	ANKI_ASSERT(renderStateBucket <= (1u << 12u) - 1u);
-	gpuVolume.m_renderableIndex_20bit_renderStateBucket_12bit |= renderStateBucket;
+	gpuVolume.m_renderStateBucket = renderStateBucket;
 	return gpuVolume;
 }
 

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

@@ -28,7 +28,8 @@ struct GpuSceneRenderable
 	U32 m_particleEmitterIndex2; // Index to the GpuSceneParticleEmitter2 array or kMaxU32 if it's not an emitter.
 	U32 m_rtShadowsShaderHandleIndex; // The index of the shader handle in the array of library's handles.
 	U32 m_rtMaterialFetchShaderHandleIndex; // The index of the shader handle in the array of library's handles.
-	U32 m_uuid; // A UUID specific for this renderable. Don't come from some scene object
+	U32 m_uuid; // A UUID specific for this renderable. Not related to the scene object
+	U32 m_sceneNodeUuid;
 
 	U32 m_diffuseColor : 24; // The average diffuse color of the renderable. Blue is in low bits.
 	U32 m_padding : 8;
@@ -63,7 +64,8 @@ struct GpuSceneRenderableBoundingVolume
 	F32 m_sphereRadius ANKI_CPP_CODE(= 0.0f);
 
 	Vec3 m_aabbMax ANKI_CPP_CODE(= Vec3(kSomeFarDistance));
-	U32 m_renderableIndex_20bit_renderStateBucket_12bit;
+	U32 m_renderableIndex : 20;
+	U32 m_renderStateBucket : 12;
 };
 static_assert(sizeof(GpuSceneRenderableBoundingVolume) == sizeof(Vec4) * 2);
 

+ 4 - 1
AnKi/Shaders/Include/GpuVisibilityTypes.h

@@ -101,7 +101,7 @@ enum class GpuVisibilityIndirectDispatches : U32
 	kCount
 };
 
-/// Counters used in non-renderables visibility
+// Counters used in non-renderables visibility
 struct GpuVisibilityNonRenderablesCounters
 {
 	U32 m_threadgroupCount; ///< Counts the no of threadgroups
@@ -109,6 +109,9 @@ struct GpuVisibilityNonRenderablesCounters
 	U32 m_feedbackObjectCount; ///< Counts the visbile objects that need feedback
 };
 
+// Packs the LOD in the MSB 3bit and the rest it's an index to a GpuSceneRenderableBoundingVolume. It's actually U32 because of some shader logic
+typedef U32 LodAndGpuSceneRenderableBoundingVolumeIndex;
+
 struct GpuVisibilityLocalLightsConsts
 {
 	Vec3 m_cellSize;

+ 10 - 0
AnKi/Util/CVarSet.cpp

@@ -5,11 +5,21 @@
 
 #include <AnKi/Util/CVarSet.h>
 #include <AnKi/Util/File.h>
+#include <AnKi/Util/Thread.h>
 
 namespace anki {
 
+#if ANKI_ASSERTIONS_ENABLED
+void CVar::validateSetValue() const
+{
+	ANKI_ASSERT(Thread::getCurrentThreadId() == CVarSet::getSingleton().m_mainThreadHandle && "CVars can only be set by the main thread");
+}
+#endif
+
 void CVarSet::registerCVar(CVar* cvar)
 {
+	m_mainThreadHandle = Thread::getCurrentThreadId();
+
 	for([[maybe_unused]] CVar& it : m_cvars)
 	{
 		ANKI_ASSERT(it.m_name != cvar->m_name);

+ 14 - 0
AnKi/Util/CVarSet.h

@@ -68,6 +68,14 @@ protected:
 		registerSelf();
 	}
 
+	void validateSetValue() const
+#if ANKI_ASSERTIONS_ENABLED
+		;
+#else
+	{
+	}
+#endif
+
 private:
 	void registerSelf();
 };
@@ -103,6 +111,7 @@ public:
 
 	NumericCVar& operator=(TNumber val)
 	{
+		validateSetValue();
 		Bool ok = true;
 		if(!m_checkValueCallback)
 		{
@@ -176,6 +185,7 @@ public:
 
 	StringCVar& operator=(CString name)
 	{
+		validateSetValue();
 		if(m_str)
 		{
 			free(m_str);
@@ -216,6 +226,7 @@ public:
 
 	BoolCVar& operator=(Bool val)
 	{
+		validateSetValue();
 		m_val = val;
 		return *this;
 	}
@@ -273,6 +284,9 @@ public:
 
 private:
 	IntrusiveList<CVar> m_cvars;
+#if ANKI_ASSERTIONS_ENABLED
+	U64 m_mainThreadHandle = 0;
+#endif
 
 	void registerCVar(CVar* var);
 };

+ 11 - 7
Samples/Common/SampleApp.cpp

@@ -134,24 +134,28 @@ Error SampleApp::userMainLoop(Bool& quit, Second elapsedTime)
 
 	if(in.getKey(KeyCode::kF1) == 1)
 	{
+		DbgOption options = renderer.getDbg().getOptions();
+
 		static U mode = 0;
 		mode = (mode + 1) % 3;
 		if(mode == 0)
 		{
-			g_cvarRenderDbgScene = false;
+			options &= ~DbgOption::kBoundingBoxes;
 		}
 		else if(mode == 1)
 		{
-			g_cvarRenderDbgScene = true;
-			renderer.getDbg().setDepthTestEnabled(true);
-			renderer.getDbg().setDitheredDepthTestEnabled(false);
+			options |= DbgOption::kBoundingBoxes;
+			options |= DbgOption::kDepthTest;
+			options &= ~DbgOption::kDitheredDepthTest;
 		}
 		else
 		{
-			g_cvarRenderDbgScene = true;
-			renderer.getDbg().setDepthTestEnabled(false);
-			renderer.getDbg().setDitheredDepthTestEnabled(true);
+			options |= DbgOption::kBoundingBoxes;
+			options &= ~DbgOption::kDepthTest;
+			options |= DbgOption::kDitheredDepthTest;
 		}
+
+		renderer.getDbg().setOptions(options);
 	}
 
 	if(in.getKey(KeyCode::kF11) == 1 && ANKI_TRACING_ENABLED)

+ 14 - 10
Samples/PhysicsPlayground/Main.cpp

@@ -211,31 +211,35 @@ Error MyApp::userMainLoop(Bool& quit, [[maybe_unused]] Second elapsedTime)
 
 	if(Input::getSingleton().getKey(KeyCode::kF1) == 1)
 	{
+		DbgOption options = renderer.getDbg().getOptions();
+
 		static U mode = 0;
 		mode = (mode + 1) % 3;
 		if(mode == 0)
 		{
-			g_cvarRenderDbgScene = false;
+			options &= ~DbgOption::kBoundingBoxes;
 		}
 		else if(mode == 1)
 		{
-			g_cvarRenderDbgScene = true;
-			renderer.getDbg().setDepthTestEnabled(true);
-			renderer.getDbg().setDitheredDepthTestEnabled(false);
+			options |= DbgOption::kBoundingBoxes;
+			options |= DbgOption::kDepthTest;
+			options &= ~DbgOption::kDitheredDepthTest;
 		}
 		else
 		{
-			g_cvarRenderDbgScene = true;
-			renderer.getDbg().setDepthTestEnabled(false);
-			renderer.getDbg().setDitheredDepthTestEnabled(true);
+			options |= DbgOption::kBoundingBoxes;
+			options &= ~DbgOption::kDepthTest;
+			options |= DbgOption::kDitheredDepthTest;
 		}
+
+		renderer.getDbg().setOptions(options);
 	}
 
 	if(Input::getSingleton().getKey(KeyCode::kF2) == 1)
 	{
-		g_cvarRenderDbgPhysics = !g_cvarRenderDbgPhysics;
-		renderer.getDbg().setDepthTestEnabled(true);
-		renderer.getDbg().setDitheredDepthTestEnabled(false);
+		DbgOption options = renderer.getDbg().getOptions();
+		options ^= DbgOption::kPhysics;
+		renderer.getDbg().setOptions(options);
 	}
 
 	if(0)

+ 22 - 30
Sandbox/Main.cpp

@@ -124,40 +124,28 @@ Error MyApp::userMainLoop(Bool& quit, Second elapsedTime)
 
 	if(in.getKey(KeyCode::kF1) == 1)
 	{
+		DbgOption options = renderer.getDbg().getOptions();
+
 		static U mode = 0;
 		mode = (mode + 1) % 3;
 		if(mode == 0)
 		{
-			g_cvarRenderDbgScene = false;
+			options &= ~DbgOption::kBoundingBoxes;
 		}
 		else if(mode == 1)
 		{
-			g_cvarRenderDbgScene = true;
-			renderer.getDbg().setDepthTestEnabled(true);
-			renderer.getDbg().setDitheredDepthTestEnabled(false);
+			options |= DbgOption::kBoundingBoxes;
+			options |= DbgOption::kDepthTest;
+			options &= ~DbgOption::kDitheredDepthTest;
 		}
 		else
 		{
-			g_cvarRenderDbgScene = true;
-			renderer.getDbg().setDepthTestEnabled(false);
-			renderer.getDbg().setDitheredDepthTestEnabled(true);
+			options |= DbgOption::kBoundingBoxes;
+			options &= ~DbgOption::kDepthTest;
+			options |= DbgOption::kDitheredDepthTest;
 		}
-	}
-	if(in.getKey(KeyCode::kF2) == 1)
-	{
-		// renderer.getDbg().flipFlags(DbgFlag::SPATIAL_COMPONENT);
-	}
-	if(in.getKey(KeyCode::kF3) == 1)
-	{
-		// renderer.getDbg().flipFlags(DbgFlag::PHYSICS);
-	}
-	if(in.getKey(KeyCode::kF4) == 1)
-	{
-		// renderer.getDbg().flipFlags(DbgFlag::SECTOR_COMPONENT);
-	}
-	if(in.getKey(KeyCode::kF6) == 1)
-	{
-		renderer.getDbg().switchDepthTestEnabled();
+
+		renderer.getDbg().setOptions(options);
 	}
 
 	if(in.getKey(KeyCode::kF11) == 1)
@@ -187,24 +175,28 @@ Error MyApp::userMainLoop(Bool& quit, Second elapsedTime)
 
 		if(in.getKey(KeyCode::kF1) == 1)
 		{
+			DbgOption options = renderer.getDbg().getOptions();
+
 			static U mode = 0;
 			mode = (mode + 1) % 3;
 			if(mode == 0)
 			{
-				g_cvarRenderDbgScene = false;
+				options &= ~DbgOption::kBoundingBoxes;
 			}
 			else if(mode == 1)
 			{
-				g_cvarRenderDbgScene = true;
-				renderer.getDbg().setDepthTestEnabled(true);
-				renderer.getDbg().setDitheredDepthTestEnabled(false);
+				options |= DbgOption::kBoundingBoxes;
+				options |= DbgOption::kDepthTest;
+				options &= ~DbgOption::kDitheredDepthTest;
 			}
 			else
 			{
-				g_cvarRenderDbgScene = true;
-				renderer.getDbg().setDepthTestEnabled(false);
-				renderer.getDbg().setDitheredDepthTestEnabled(true);
+				options |= DbgOption::kBoundingBoxes;
+				options &= ~DbgOption::kDepthTest;
+				options |= DbgOption::kDitheredDepthTest;
 			}
+
+			renderer.getDbg().setOptions(options);
 		}
 
 		if(in.getKey(KeyCode::kUp) > 0)

+ 2 - 0
Tools/Editor/EditorMain.cpp

@@ -90,6 +90,8 @@ public:
 			ANKI_CHECK(ScriptManager::getSingleton().evalString(script->getSource()));
 		}
 
+		Renderer::getSingleton().getDbg().enableOptions(DbgOption::kObjectPicking);
+
 		return Error::kNone;
 	}