Browse Source

Move UI stuff to the new design

Panagiotis Christopoulos Charitos 2 years ago
parent
commit
17b0a2c9cb

+ 19 - 69
AnKi/Core/App.cpp

@@ -18,8 +18,6 @@
 #include <AnKi/Core/GpuMemory/RebarTransientMemoryPool.h>
 #include <AnKi/Core/GpuMemory/GpuVisibleTransientMemoryPool.h>
 #include <AnKi/Core/GpuMemory/GpuReadbackMemoryPool.h>
-#include <AnKi/Core/DeveloperConsole.h>
-#include <AnKi/Core/StatsUi.h>
 #include <AnKi/Core/StatsSet.h>
 #include <AnKi/Window/NativeWindow.h>
 #include <AnKi/Core/MaliHwCounters.h>
@@ -34,6 +32,7 @@
 #include <AnKi/Resource/AsyncLoader.h>
 #include <AnKi/Ui/UiManager.h>
 #include <AnKi/Ui/Canvas.h>
+#include <AnKi/Scene/DeveloperConsoleUiNode.h>
 #include <csignal>
 
 #if ANKI_OS_ANDROID
@@ -151,9 +150,6 @@ App::~App()
 
 void App::cleanup()
 {
-	m_statsUi.reset(nullptr);
-	m_console.reset(nullptr);
-
 	SceneGraph::freeSingleton();
 	ScriptManager::freeSingleton();
 	MainRenderer::freeSingleton();
@@ -374,12 +370,6 @@ Error App::initInternal()
 	//
 	ANKI_CHECK(SceneGraph::allocateSingleton().init(allocCb, allocCbUserData));
 
-	//
-	// Misc
-	//
-	ANKI_CHECK(UiManager::getSingleton().newInstance<StatsUi>(m_statsUi));
-	ANKI_CHECK(UiManager::getSingleton().newInstance<DeveloperConsole>(m_console));
-
 	ANKI_CORE_LOGI("Application initialized");
 
 	return Error::kNone;
@@ -469,16 +459,11 @@ Error App::mainLoop()
 			RenderQueue rqueue;
 			SceneGraph::getSingleton().doVisibilityTests(rqueue);
 
-			// Inject stats UI
-			CoreDynamicArray<UiQueueElement> newUiElementArr;
-			injectUiElements(newUiElementArr, rqueue);
-
 			// Render
 			TexturePtr presentableTex = GrManager::getSingleton().acquireNextPresentableTexture();
 			ANKI_CHECK(MainRenderer::getSingleton().render(rqueue, presentableTex.get()));
 
-			// If we get stats exclude the time of GR because it forces some GPU-CPU serialization. We don't want to
-			// count that
+			// If we get stats exclude the time of GR because it forces some GPU-CPU serialization. We don't want to count that
 			Second grTime = 0.0;
 			if(benchmarkMode || g_displayStatsCVar.get() > 0) [[unlikely]]
 			{
@@ -530,25 +515,18 @@ Error App::mainLoop()
 			}
 
 			// Stats
-			if(g_displayStatsCVar.get() > 0)
-			{
 #if ANKI_PLATFORM_MOBILE
-				if(MaliHwCounters::isAllocated())
-				{
-					MaliHwCountersOut out;
-					MaliHwCounters::getSingleton().sample(out);
-					g_maliGpuActive.set(out.m_gpuActive);
-					g_maliGpuReadBandwidth.set(out.m_readBandwidth);
-					g_maliGpuWriteBandwidth.set(out.m_writeBandwidth);
-				}
+			if(MaliHwCounters::isAllocated())
+			{
+				MaliHwCountersOut out;
+				MaliHwCounters::getSingleton().sample(out);
+				g_maliGpuActive.set(out.m_gpuActive);
+				g_maliGpuReadBandwidth.set(out.m_readBandwidth);
+				g_maliGpuWriteBandwidth.set(out.m_writeBandwidth);
+			}
 #endif
 
-				StatsUi& statsUi = *static_cast<StatsUi*>(m_statsUi.get());
-				const StatsUiDetail detail = (g_displayStatsCVar.get() == 1) ? StatsUiDetail::kFpsOnly : StatsUiDetail::kDetailed;
-				statsUi.setStatsDetail(detail);
-
-				StatsSet::getSingleton().endFrame();
-			}
+			StatsSet::getSingleton().endFrame();
 
 			++GlobalFrameIndex::getSingleton().m_value;
 
@@ -575,42 +553,6 @@ Error App::mainLoop()
 	return Error::kNone;
 }
 
-void App::injectUiElements(CoreDynamicArray<UiQueueElement>& newUiElementArr, RenderQueue& rqueue)
-{
-	const U32 originalCount = rqueue.m_uis.getSize();
-	if(g_displayStatsCVar.get() > 0 || m_consoleEnabled)
-	{
-		const U32 extraElements = (g_displayStatsCVar.get() > 0) + (m_consoleEnabled != 0);
-		newUiElementArr.resize(originalCount + extraElements);
-
-		if(originalCount > 0)
-		{
-			memcpy(&newUiElementArr[0], &rqueue.m_uis[0], rqueue.m_uis.getSizeInBytes());
-		}
-
-		rqueue.m_uis = WeakArray<UiQueueElement>(newUiElementArr);
-	}
-
-	U32 count = originalCount;
-	if(g_displayStatsCVar.get() > 0)
-	{
-		newUiElementArr[count].m_userData = m_statsUi.get();
-		newUiElementArr[count].m_drawCallback = [](CanvasPtr& canvas, void* userData) -> void {
-			static_cast<StatsUi*>(userData)->build(canvas);
-		};
-		++count;
-	}
-
-	if(m_consoleEnabled)
-	{
-		newUiElementArr[count].m_userData = m_console.get();
-		newUiElementArr[count].m_drawCallback = [](CanvasPtr& canvas, void* userData) -> void {
-			static_cast<DeveloperConsole*>(userData)->build(canvas);
-		};
-		++count;
-	}
-}
-
 void App::initMemoryCallbacks(AllocAlignedCallback& allocCb, void*& allocCbUserData)
 {
 	if(ANKI_STATS_ENABLED && g_displayStatsCVar.get() > 1)
@@ -672,4 +614,12 @@ void App::setSignalHandlers()
 	// Ignore for now: signal(SIGABRT, handler);
 }
 
+Bool App::toggleDeveloperConsole()
+{
+	SceneNode& node = SceneGraph::getSingleton().findSceneNode("_DevConsole");
+	static_cast<DeveloperConsoleUiNode&>(node).toggleConsole();
+	m_consoleEnabled = static_cast<DeveloperConsoleUiNode&>(node).isConsoleEnabled();
+	return m_consoleEnabled;
+}
+
 } // end namespace anki

+ 5 - 11
AnKi/Core/App.h

@@ -15,6 +15,7 @@ namespace anki {
 // Forward
 class UiQueueElement;
 class RenderQueue;
+class StatCounter;
 extern NumericCVar<U32> g_windowWidthCVar;
 extern NumericCVar<U32> g_windowHeightCVar;
 extern NumericCVar<U32> g_windowFullscreenCVar;
@@ -22,6 +23,8 @@ extern NumericCVar<U32> g_targetFpsCVar;
 extern NumericCVar<F32> g_lod0MaxDistanceCVar;
 extern NumericCVar<F32> g_lod1MaxDistanceCVar;
 extern NumericCVar<U32> g_displayStatsCVar;
+extern StatCounter g_cpuTotalTime;
+extern StatCounter g_rendererGpuTime;
 
 /// The core class of the engine.
 class App
@@ -54,20 +57,14 @@ public:
 		return Error::kNone;
 	}
 
-	void setDisplayDeveloperConsole(Bool display)
-	{
-		m_consoleEnabled = display;
-	}
+	Bool toggleDeveloperConsole();
 
-	Bool getDisplayDeveloperConsole() const
+	Bool getDeveloperConsoleEnabled() const
 	{
 		return m_consoleEnabled;
 	}
 
 private:
-	// Misc
-	UiImmediateModeBuilderPtr m_statsUi;
-	UiImmediateModeBuilderPtr m_console;
 	Bool m_consoleEnabled = false;
 	CoreString m_settingsDir; ///< The path that holds the configuration
 	CoreString m_cacheDir; ///< This is used as a cache
@@ -84,9 +81,6 @@ private:
 	Error initDirs();
 	void cleanup();
 
-	/// Inject a new UI element in the render queue for displaying various stuff.
-	void injectUiElements(CoreDynamicArray<UiQueueElement>& elements, RenderQueue& rqueue);
-
 	void setSignalHandlers();
 };
 

+ 0 - 4
AnKi/Core/CMakeLists.txt

@@ -1,10 +1,8 @@
 set(sources
 	App.cpp
 	CVarSet.cpp
-	DeveloperConsole.cpp
 	CoreTracer.cpp
 	MaliHwCounters.cpp
-	StatsUi.cpp
 	StatsSet.cpp
 	GpuMemory/UnifiedGeometryBuffer.cpp
 	GpuMemory/GpuSceneBuffer.cpp
@@ -16,9 +14,7 @@ set(headers
 	Common.h
 	CVarSet.h
 	CoreTracer.h
-	DeveloperConsole.h
 	MaliHwCounters.h
-	StatsUi.h
 	StatsSet.h
 	StdinListener.h
 	GpuMemory/UnifiedGeometryBuffer.h

+ 0 - 67
AnKi/Core/StatsUi.h

@@ -1,67 +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
-
-#pragma once
-
-#include <AnKi/Core/Common.h>
-#include <AnKi/Ui/UiImmediateModeBuilder.h>
-#include <AnKi/Util/BuddyAllocatorBuilder.h>
-#include <AnKi/Gr/GrManager.h>
-
-namespace anki {
-
-/// @addtogroup core
-/// @{
-
-/// @memberof StatsUi
-enum class StatsUiDetail : U8
-{
-	kDetailed,
-	kFpsOnly
-};
-
-/// UI for displaying on-screen stats.
-class StatsUi : public UiImmediateModeBuilder
-{
-public:
-	StatsUi();
-
-	~StatsUi();
-
-	Error init();
-
-	void build(CanvasPtr ctx) override;
-
-	void setStatsDetail(StatsUiDetail detail)
-	{
-		m_detail = detail;
-	}
-
-private:
-	class Value;
-
-	static constexpr U32 kBufferedFrames = 30;
-
-	FontPtr m_font;
-	StatsUiDetail m_detail = StatsUiDetail::kDetailed;
-
-	CoreDynamicArray<Value> m_averageValues;
-	U32 m_bufferedFrames = 0;
-
-	static void labelTime(Second val, CString name)
-	{
-		ImGui::Text("%s: %fms", name.cstr(), val * 1000.0);
-	}
-
-	static void labelUint(U64 val, CString name)
-	{
-		ImGui::Text("%s: %lu", name.cstr(), val);
-	}
-
-	static void labelBytes(PtrSize val, CString name);
-};
-/// @}
-
-} // end namespace anki

+ 4 - 4
AnKi/Renderer/FinalComposite.cpp

@@ -89,8 +89,8 @@ void FinalComposite::populateRenderGraph(RenderingContext& ctx)
 	// Create the pass
 	GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("Final Composite");
 
-	pass.setWork(1, [this, &ctx](RenderPassWorkContext& rgraphCtx) {
-		run(ctx, rgraphCtx);
+	pass.setWork(1, [this](RenderPassWorkContext& rgraphCtx) {
+		run(rgraphCtx);
 	});
 	pass.setFramebufferInfo(m_fbDescr, {ctx.m_outRenderTarget});
 
@@ -121,7 +121,7 @@ void FinalComposite::populateRenderGraph(RenderingContext& ctx)
 	}
 }
 
-void FinalComposite::run(RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
+void FinalComposite::run(RenderPassWorkContext& rgraphCtx)
 {
 	ANKI_TRACE_SCOPED_EVENT(RFinalComposite);
 
@@ -186,7 +186,7 @@ void FinalComposite::run(RenderingContext& ctx, RenderPassWorkContext& rgraphCtx
 	drawQuad(cmdb);
 
 	// Draw UI
-	getRenderer().getUiStage().draw(getRenderer().getPostProcessResolution().x(), getRenderer().getPostProcessResolution().y(), ctx, cmdb);
+	getRenderer().getUiStage().draw(getRenderer().getPostProcessResolution().x(), getRenderer().getPostProcessResolution().y(), cmdb);
 }
 
 } // end namespace anki

+ 1 - 1
AnKi/Renderer/FinalComposite.h

@@ -44,7 +44,7 @@ private:
 
 	Error initInternal();
 
-	void run(RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
+	void run(RenderPassWorkContext& rgraphCtx);
 };
 /// @}
 

+ 7 - 5
AnKi/Renderer/UiStage.cpp

@@ -8,6 +8,8 @@
 #include <AnKi/Renderer/RenderQueue.h>
 #include <AnKi/Ui/Font.h>
 #include <AnKi/Ui/UiManager.h>
+#include <AnKi/Scene/Components/UiComponent.h>
+#include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Util/Tracer.h>
 
 namespace anki {
@@ -21,11 +23,11 @@ Error UiStage::init()
 	return Error::kNone;
 }
 
-void UiStage::draw(U32 width, U32 height, RenderingContext& ctx, CommandBuffer& cmdb)
+void UiStage::draw(U32 width, U32 height, CommandBuffer& cmdb)
 {
-	// Early exit
-	if(ctx.m_renderQueue->m_uis.getSize() == 0)
+	if(SceneGraph::getSingleton().getComponentArrays().getUis().getSize() == 0)
 	{
+		// Early exit
 		return;
 	}
 
@@ -35,9 +37,9 @@ void UiStage::draw(U32 width, U32 height, RenderingContext& ctx, CommandBuffer&
 	m_canvas->beginBuilding();
 	m_canvas->resize(width, height);
 
-	for(UiQueueElement& el : ctx.m_renderQueue->m_uis)
+	for(UiComponent& comp : SceneGraph::getSingleton().getComponentArrays().getUis())
 	{
-		el.m_drawCallback(m_canvas, el.m_userData);
+		comp.drawUi(m_canvas);
 	}
 
 	m_canvas->appendToCommandBuffer(cmdb);

+ 1 - 1
AnKi/Renderer/UiStage.h

@@ -20,7 +20,7 @@ class UiStage : public RendererObject
 public:
 	Error init();
 
-	void draw(U32 width, U32 height, RenderingContext& ctx, CommandBuffer& cmdb);
+	void draw(U32 width, U32 height, CommandBuffer& cmdb);
 
 private:
 	FontPtr m_font;

+ 0 - 23
AnKi/Scene/Components/UiComponent.cpp

@@ -1,23 +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/UiComponent.h>
-#include <AnKi/Scene/SceneNode.h>
-#include <AnKi/Scene/SceneGraph.h>
-
-namespace anki {
-
-UiComponent ::~UiComponent()
-{
-	m_spatial.removeFromOctree(SceneGraph::getSingleton().getOctree());
-}
-
-Error UiComponent::update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
-{
-	updated = m_spatial.update(SceneGraph::getSingleton().getOctree());
-	return Error::kNone;
-}
-
-} // end namespace anki

+ 24 - 11
AnKi/Scene/Components/UiComponent.h

@@ -22,13 +22,12 @@ class UiComponent : public SceneComponent
 public:
 	UiComponent(SceneNode* node)
 		: SceneComponent(node, kClassType)
-		, m_spatial(this)
 	{
-		m_spatial.setAlwaysVisible(true);
-		m_spatial.setUpdatesOctreeBounds(false);
 	}
 
-	~UiComponent();
+	~UiComponent()
+	{
+	}
 
 	void init(UiQueueElementDrawCallback callback, void* userData)
 	{
@@ -38,20 +37,34 @@ public:
 		m_userData = userData;
 	}
 
-	void setupUiQueueElement(UiQueueElement& el) const
+	void drawUi(CanvasPtr& canvas)
+	{
+		if(m_drawCallback && m_enabled)
+		{
+			m_drawCallback(canvas, m_userData);
+		}
+	}
+
+	Bool isEnabled() const
 	{
-		ANKI_ASSERT(el.m_drawCallback != nullptr);
-		el.m_drawCallback = m_drawCallback;
-		ANKI_ASSERT(el.m_userData != nullptr);
-		el.m_userData = m_userData;
+		return m_enabled;
+	}
+
+	void setEnabled(Bool enabled)
+	{
+		m_enabled = enabled;
 	}
 
 private:
 	UiQueueElementDrawCallback m_drawCallback = nullptr;
 	void* m_userData = nullptr;
-	Spatial m_spatial;
+	Bool m_enabled = true;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	Error update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated) override
+	{
+		updated = false;
+		return Error::kNone;
+	}
 };
 /// @}
 

+ 76 - 51
AnKi/Core/DeveloperConsole.cpp → AnKi/Scene/DeveloperConsoleUiNode.cpp

@@ -3,44 +3,107 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#include <AnKi/Core/DeveloperConsole.h>
+#include <AnKi/Scene/DeveloperConsoleUiNode.h>
+#include <AnKi/Scene/Components/UiComponent.h>
+#include <AnKi/Ui.h>
 
 namespace anki {
 
-DeveloperConsole::~DeveloperConsole()
+DeveloperConsoleUiNode::DeveloperConsoleUiNode(CString name)
+	: SceneNode(name)
 {
+	UiComponent* uic = newComponent<UiComponent>();
+	uic->init(
+		[](CanvasPtr& canvas, void* ud) {
+			static_cast<DeveloperConsoleUiNode*>(ud)->draw(canvas);
+		},
+		this);
+	uic->setEnabled(false);
+}
+
+DeveloperConsoleUiNode::~DeveloperConsoleUiNode()
+{
+	// Do that first
 	Logger::getSingleton().removeMessageHandler(this, loggerCallback);
 
 	while(!m_logItems.isEmpty())
 	{
 		LogItem* item = &m_logItems.getFront();
 		m_logItems.popFront();
-		deleteInstance(UiMemoryPool::getSingleton(), item);
+		deleteInstance(SceneMemoryPool::getSingleton(), item);
 	}
 }
 
-Error DeveloperConsole::init()
+Error DeveloperConsoleUiNode::init()
 {
-	zeroMemory(m_inputText);
-
 	ANKI_CHECK(UiManager::getSingleton().newInstance(m_font, "EngineAssets/UbuntuMonoRegular.ttf", Array<U32, 1>{16}));
+	return Error::kNone;
+}
 
-	// Add a new callback to the logger
-	Logger::getSingleton().addMessageHandler(this, loggerCallback);
+void DeveloperConsoleUiNode::toggleConsole()
+{
+	m_enabled = !m_enabled;
 
-	return Error::kNone;
+	if(m_enabled)
+	{
+		getFirstComponentOfType<UiComponent>().setEnabled(true);
+		Logger::getSingleton().addMessageHandler(this, loggerCallback);
+	}
+	else
+	{
+		getFirstComponentOfType<UiComponent>().setEnabled(false);
+		Logger::getSingleton().removeMessageHandler(this, loggerCallback);
+	}
 }
 
-void DeveloperConsole::build(CanvasPtr ctx)
+void DeveloperConsoleUiNode::newLogItem(const LoggerMessageInfo& inf)
+{
+	LogItem* newLogItem;
+
+	// Pop first
+	if(m_logItemCount + 1 > kMaxLogItems)
+	{
+		LogItem* first = &m_logItems.getFront();
+		m_logItems.popFront();
+
+		first->m_msg.destroy();
+		first->m_threadName.destroy();
+
+		// Re-use the log item
+		newLogItem = first;
+		--m_logItemCount;
+	}
+	else
+	{
+		newLogItem = newInstance<LogItem>(SceneMemoryPool::getSingleton());
+	}
+
+	// Create the new item
+	newLogItem->m_file = inf.m_file;
+	newLogItem->m_func = inf.m_func;
+	newLogItem->m_subsystem = inf.m_subsystem;
+	newLogItem->m_threadName = inf.m_threadName;
+	newLogItem->m_msg = inf.m_msg;
+	newLogItem->m_line = inf.m_line;
+	newLogItem->m_type = inf.m_type;
+
+	// Push it back
+	m_logItems.pushBack(newLogItem);
+	++m_logItemCount;
+
+	m_logItemsTimestamp.fetchAdd(1);
+}
+
+void DeveloperConsoleUiNode::draw(CanvasPtr& canvas)
 {
 	const Vec4 oldWindowColor = ImGui::GetStyle().Colors[ImGuiCol_WindowBg];
 	ImGui::GetStyle().Colors[ImGuiCol_WindowBg].w = 0.3f;
-	ctx->pushFont(m_font, 16);
+	canvas->pushFont(m_font, 16);
 
 	ImGui::Begin("Console", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoTitleBar);
 
 	ImGui::SetWindowPos(Vec2(0.0f, 0.0f));
-	ImGui::SetWindowSize(Vec2(F32(ctx->getWidth()), F32(ctx->getHeight()) * (2.0f / 3.0f)));
+	ImGui::SetWindowSize(Vec2(F32(canvas->getWidth()), F32(canvas->getHeight()) * (2.0f / 3.0f)));
 
 	// Push the items
 	const F32 footerHeightToPreserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
@@ -103,45 +166,7 @@ void DeveloperConsole::build(CanvasPtr ctx)
 
 	ImGui::End();
 	ImGui::GetStyle().Colors[ImGuiCol_WindowBg] = oldWindowColor;
-	ctx->popFont();
-}
-
-void DeveloperConsole::newLogItem(const LoggerMessageInfo& inf)
-{
-	LogItem* newLogItem;
-
-	// Pop first
-	if(m_logItemCount + 1 > kMaxLogItems)
-	{
-		LogItem* first = &m_logItems.getFront();
-		m_logItems.popFront();
-
-		first->m_msg.destroy();
-		first->m_threadName.destroy();
-
-		// Re-use the log item
-		newLogItem = first;
-		--m_logItemCount;
-	}
-	else
-	{
-		newLogItem = newInstance<LogItem>(UiMemoryPool::getSingleton());
-	}
-
-	// Create the new item
-	newLogItem->m_file = inf.m_file;
-	newLogItem->m_func = inf.m_func;
-	newLogItem->m_subsystem = inf.m_subsystem;
-	newLogItem->m_threadName = inf.m_threadName;
-	newLogItem->m_msg = inf.m_msg;
-	newLogItem->m_line = inf.m_line;
-	newLogItem->m_type = inf.m_type;
-
-	// Push it back
-	m_logItems.pushBack(newLogItem);
-	++m_logItemCount;
-
-	m_logItemsTimestamp.fetchAdd(1);
+	canvas->popFont();
 }
 
 } // end namespace anki

+ 21 - 13
AnKi/Core/DeveloperConsole.h → AnKi/Scene/DeveloperConsoleUiNode.h

@@ -5,27 +5,31 @@
 
 #pragma once
 
-#include <AnKi/Ui.h>
-#include <AnKi/Core/Common.h>
-#include <AnKi/Util/List.h>
+#include <AnKi/Scene/SceneNode.h>
+#include <AnKi/Ui/Canvas.h>
 #include <AnKi/Script/ScriptEnvironment.h>
 
 namespace anki {
 
-/// @addtogroup core
+/// @addtogroup scene
 /// @{
 
-/// Developer console UI.
-class DeveloperConsole : public UiImmediateModeBuilder
+/// A node that draws the developer console.
+class DeveloperConsoleUiNode : public SceneNode
 {
 public:
-	DeveloperConsole() = default;
+	DeveloperConsoleUiNode(CString name);
 
-	~DeveloperConsole();
+	~DeveloperConsoleUiNode();
 
 	Error init();
 
-	void build(CanvasPtr ctx) override;
+	void toggleConsole();
+
+	Bool isConsoleEnabled() const
+	{
+		return m_enabled;
+	}
 
 private:
 	static constexpr U kMaxLogItems = 64;
@@ -36,8 +40,8 @@ private:
 		const Char* m_file;
 		const Char* m_func;
 		const Char* m_subsystem;
-		UiString m_threadName;
-		UiString m_msg;
+		SceneString m_threadName;
+		SceneString m_msg;
 		I32 m_line;
 		LoggerMessageType m_type;
 	};
@@ -45,19 +49,23 @@ private:
 	FontPtr m_font;
 	IntrusiveList<LogItem> m_logItems;
 	U32 m_logItemCount = 0;
-	Array<char, 256> m_inputText;
+	Array<Char, 256> m_inputText = {};
 
 	Atomic<U32> m_logItemsTimestamp = {1};
 	U32 m_logItemsTimestampConsumed = 0;
 
 	ScriptEnvironment m_scriptEnv;
 
+	Bool m_enabled = false;
+
 	void newLogItem(const LoggerMessageInfo& inf);
 
 	static void loggerCallback(void* userData, const LoggerMessageInfo& info)
 	{
-		static_cast<DeveloperConsole*>(userData)->newLogItem(info);
+		static_cast<DeveloperConsoleUiNode*>(userData)->newLogItem(info);
 	}
+
+	void draw(CanvasPtr& canvas);
 };
 /// @}
 

+ 14 - 0
AnKi/Scene/SceneGraph.cpp

@@ -14,6 +14,9 @@
 #include <AnKi/Util/ThreadHive.h>
 #include <AnKi/Util/Tracer.h>
 #include <AnKi/Util/HighRezTimer.h>
+#include <AnKi/Core/App.h>
+#include <AnKi/Scene/StatsUiNode.h>
+#include <AnKi/Scene/DeveloperConsoleUiNode.h>
 
 #include <AnKi/Scene/Components/BodyComponent.h>
 #include <AnKi/Scene/Components/CameraComponent.h>
@@ -125,6 +128,17 @@ Error SceneGraph::init(AllocAlignedCallback allocCallback, void* allocCallbackDa
 
 	RenderStateBucketContainer::allocateSingleton();
 
+	// Construct a few common nodex
+	if(g_displayStatsCVar.get() > 0)
+	{
+		StatsUiNode* statsNode;
+		ANKI_CHECK(newSceneNode("_StatsUi", statsNode));
+		statsNode->setFpsOnly(g_displayStatsCVar.get() == 1);
+	}
+
+	DeveloperConsoleUiNode* consoleNode;
+	ANKI_CHECK(newSceneNode("_DevConsole", consoleNode));
+
 	return Error::kNone;
 }
 

+ 50 - 46
AnKi/Core/StatsUi.cpp → AnKi/Scene/StatsUiNode.cpp

@@ -3,56 +3,16 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#include <AnKi/Core/StatsUi.h>
+#include <AnKi/Scene/StatsUiNode.h>
+#include <AnKi/Scene/Components/UiComponent.h>
 #include <AnKi/Core/StatsSet.h>
+#include <AnKi/Core/App.h>
 #include <AnKi/Ui/UiManager.h>
 #include <AnKi/Ui/Font.h>
-#include <AnKi/Ui/Canvas.h>
 
 namespace anki {
 
-extern StatCounter g_cpuTotalTime;
-extern StatCounter g_rendererGpuTime;
-
-class StatsUi::Value
-{
-public:
-	F64 m_avg = 0.0;
-	F64 m_rollingAvg = 0.0;
-
-	void update(F64 x, Bool flush)
-	{
-		m_rollingAvg += x / F64(kBufferedFrames);
-
-		if(flush)
-		{
-			m_avg = m_rollingAvg;
-			m_rollingAvg = 0.0;
-		}
-	}
-};
-
-StatsUi::StatsUi()
-{
-}
-
-StatsUi::~StatsUi()
-{
-}
-
-Error StatsUi::init()
-{
-	ANKI_CHECK(UiManager::getSingleton().newInstance(m_font, "EngineAssets/UbuntuMonoRegular.ttf", Array<U32, 1>{24}));
-
-	if(StatsSet::getSingleton().getCounterCount())
-	{
-		m_averageValues.resize(StatsSet::getSingleton().getCounterCount());
-	}
-
-	return Error::kNone;
-}
-
-void StatsUi::labelBytes(PtrSize val, CString name)
+static void labelBytes(PtrSize val, CString name)
 {
 	PtrSize gb, mb, kb, b;
 
@@ -87,7 +47,51 @@ void StatsUi::labelBytes(PtrSize val, CString name)
 	ImGui::TextUnformatted(timestamp.cstr());
 }
 
-void StatsUi::build(CanvasPtr canvas)
+class StatsUiNode::Value
+{
+public:
+	F64 m_avg = 0.0;
+	F64 m_rollingAvg = 0.0;
+
+	void update(F64 x, Bool flush)
+	{
+		m_rollingAvg += x / F64(kBufferedFrames);
+
+		if(flush)
+		{
+			m_avg = m_rollingAvg;
+			m_rollingAvg = 0.0;
+		}
+	}
+};
+
+StatsUiNode::StatsUiNode(CString name)
+	: SceneNode(name)
+{
+	UiComponent* uic = newComponent<UiComponent>();
+	uic->init(
+		[](CanvasPtr& canvas, void* ud) {
+			static_cast<StatsUiNode*>(ud)->draw(canvas);
+		},
+		this);
+
+	if(StatsSet::getSingleton().getCounterCount())
+	{
+		m_averageValues.resize(StatsSet::getSingleton().getCounterCount());
+	}
+}
+
+StatsUiNode::~StatsUiNode()
+{
+}
+
+Error StatsUiNode::init()
+{
+	ANKI_CHECK(UiManager::getSingleton().newInstance(m_font, "EngineAssets/UbuntuMonoRegular.ttf", Array<U32, 1>{24}));
+	return Error::kNone;
+}
+
+void StatsUiNode::draw(CanvasPtr& canvas)
 {
 	Bool flush = false;
 	if(m_bufferedFrames == kBufferedFrames)
@@ -107,7 +111,7 @@ void StatsUi::build(CanvasPtr canvas)
 		ImGui::SetWindowPos(Vec2(5.0f, 5.0f));
 		ImGui::SetWindowSize(Vec2(230.0f, 450.0f));
 
-		if(m_detail == StatsUiDetail::kDetailed)
+		if(!m_fpsOnly)
 		{
 			StatCategory category = StatCategory::kCount;
 			U32 count = 0;

+ 46 - 0
AnKi/Scene/StatsUiNode.h

@@ -0,0 +1,46 @@
+// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <AnKi/Scene/SceneNode.h>
+#include <AnKi/Ui/Canvas.h>
+
+namespace anki {
+
+/// @addtogroup scene
+/// @{
+
+/// A node that draws a UI with stats.
+class StatsUiNode : public SceneNode
+{
+public:
+	StatsUiNode(CString name);
+
+	~StatsUiNode();
+
+	Error init();
+
+	void setFpsOnly(Bool fpsOnly)
+	{
+		m_fpsOnly = fpsOnly;
+	}
+
+private:
+	class Value;
+
+	static constexpr U32 kBufferedFrames = 30;
+
+	FontPtr m_font;
+	Bool m_fpsOnly = false;
+
+	SceneDynamicArray<Value> m_averageValues;
+	U32 m_bufferedFrames = 0;
+
+	void draw(CanvasPtr& canvas);
+};
+/// @}
+
+} // end namespace anki

+ 1 - 8
AnKi/Scene/Visibility.cpp

@@ -385,14 +385,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 		}
 		else if(compType == UiComponent::kClassType)
 		{
-			if(!isInside())
-			{
-				continue;
-			}
-
-			const UiComponent& uic = static_cast<UiComponent&>(comp);
-			UiQueueElement* el = result.m_uis.newElement();
-			uic.setupUiQueueElement(*el);
+			ANKI_ASSERT(0);
 		}
 		else if(compType == SkyboxComponent::kClassType)
 		{

+ 0 - 4
AnKi/Util/BlockArray.h

@@ -50,7 +50,6 @@ public:
 		: m_array(b.m_array)
 		, m_elementIdx(b.m_elementIdx)
 	{
-		check();
 	}
 
 	/// Allow conversion from iterator to const iterator.
@@ -59,21 +58,18 @@ public:
 		: m_array(b.m_array)
 		, m_elementIdx(b.m_elementIdx)
 	{
-		check();
 	}
 
 	BlockArrayIterator(TBlockArrayPtr arr, U32 idx)
 		: m_array(arr)
 		, m_elementIdx(idx)
 	{
-		check();
 	}
 
 	BlockArrayIterator& operator=(const BlockArrayIterator& b)
 	{
 		m_array = b.m_array;
 		m_elementIdx = b.m_elementIdx;
-		check();
 		return *this;
 	}
 

+ 7 - 0
AnKi/Util/StdTypes.h

@@ -212,6 +212,13 @@ private:
 		} \
 	} while(0)
 
+/// Macro to check if a method/function returned an error. It will abort on error.
+#define ANKI_CHECKF(x_) \
+	if(x_) \
+	{ \
+		ANKI_LOGF("Failed and won't recover"); \
+	}
+
 /// Macro to check if a method/function returned an error.
 #define ANKI_CHECK_AND_IGNORE(x_) \
 	do \

+ 6 - 1
Samples/Common/SampleApp.cpp

@@ -56,7 +56,12 @@ Error SampleApp::userMainLoop(Bool& quit, Second elapsedTime)
 
 	if(in.getKey(KeyCode::kBackquote) == 1)
 	{
-		setDisplayDeveloperConsole(!getDisplayDeveloperConsole());
+		toggleDeveloperConsole();
+	}
+
+	if(getDeveloperConsoleEnabled())
+	{
+		return Error::kNone;
 	}
 
 	if(in.getKey(KeyCode::kY) == 1)

+ 6 - 0
Tests/Util/BlockArray.cpp

@@ -17,6 +17,12 @@ ANKI_TEST(Util, BlockArray)
 	TestFoo::reset();
 	{
 		BlockArray<TestFoo> arr;
+
+		for(TestFoo& f : arr) // Iterate empty
+		{
+			f.m_x = 1;
+		}
+
 		auto it = arr.emplace(123);
 		ANKI_TEST_EXPECT_EQ(it->m_x, 123);