Browse Source

Add the skeleton for the texture viewer

Panagiotis Christopoulos Charitos 4 years ago
parent
commit
3903c9e442

+ 1 - 0
AnKi/AnKi.h

@@ -18,3 +18,4 @@
 #include <AnKi/Core.h>
 #include <AnKi/Importer.h>
 #include <AnKi/ShaderCompiler.h>
+#include <AnKi/Ui.h>

+ 29 - 28
AnKi/Core/App.cpp

@@ -41,7 +41,7 @@ namespace anki
 android_app* gAndroidApp = nullptr;
 #endif
 
-class App::StatsUi : public UiImmediateModeBuilder
+class App::StatsUi
 {
 public:
 	template<typename T>
@@ -72,6 +72,8 @@ public:
 		U32 m_count = 0;
 	};
 
+	GenericMemoryPoolAllocator<U8> m_alloc;
+
 	BufferedValue<Second> m_frameTime;
 	BufferedValue<Second> m_renderTime;
 	BufferedValue<Second> m_sceneUpdateTime;
@@ -92,12 +94,12 @@ public:
 	static const U32 BUFFERED_FRAMES = 16;
 	U32 m_bufferedFrames = 0;
 
-	StatsUi(UiManager* ui)
-		: UiImmediateModeBuilder(ui)
+	StatsUi(const GenericMemoryPoolAllocator<U8>& alloc)
+		: m_alloc(alloc)
 	{
 	}
 
-	void build(CanvasPtr canvas) override
+	void build(CanvasPtr canvas)
 	{
 		// Misc
 		++m_bufferedFrames;
@@ -173,7 +175,7 @@ public:
 
 		b = val;
 
-		StringAuto timestamp(getAllocator());
+		StringAuto timestamp(m_alloc);
 		if(gb)
 		{
 			timestamp.sprintf("%s: %4u,%04u,%04u,%04u", name.cstr(), gb, mb, kb, b);
@@ -268,7 +270,7 @@ App::~App()
 
 void App::cleanup()
 {
-	m_statsUi.reset(nullptr);
+	m_heapAlloc.deleteInstance(m_statsUi);
 	m_console.reset(nullptr);
 
 	m_heapAlloc.deleteInstance(m_scene);
@@ -477,8 +479,8 @@ Error App::initInternal(const ConfigSet& config_, AllocAlignedCallback allocCb,
 	//
 	m_scene = m_heapAlloc.newInstance<SceneGraph>();
 
-	ANKI_CHECK(m_scene->init(m_allocCb, m_allocCbData, m_threadHive, m_resources, m_input, m_script, &m_globalTimestamp,
-							 config));
+	ANKI_CHECK(m_scene->init(m_allocCb, m_allocCbData, m_threadHive, m_resources, m_input, m_script, m_ui,
+							 &m_globalTimestamp, config));
 
 	// Inform the script engine about some subsystems
 	m_script->setRenderer(m_renderer);
@@ -487,7 +489,7 @@ Error App::initInternal(const ConfigSet& config_, AllocAlignedCallback allocCb,
 	//
 	// Misc
 	//
-	ANKI_CHECK(m_ui->newInstance<StatsUi>(m_statsUi));
+	m_statsUi = m_heapAlloc.newInstance<StatsUi>(m_heapAlloc);
 	ANKI_CHECK(m_ui->newInstance<DeveloperConsole>(m_console, m_allocCb, m_allocCbData, m_script));
 
 	ANKI_CORE_LOGI("Application initialized");
@@ -622,23 +624,22 @@ Error App::mainLoop()
 			// Stats
 			if(m_displayStats)
 			{
-				StatsUi& statsUi = static_cast<StatsUi&>(*m_statsUi);
-				statsUi.m_frameTime.set(frameTime);
-				statsUi.m_renderTime.set(m_renderer->getStats().m_renderingCpuTime);
-				statsUi.m_sceneUpdateTime.set(m_scene->getStats().m_updateTime);
-				statsUi.m_visTestsTime.set(m_scene->getStats().m_visibilityTestsTime);
-				statsUi.m_physicsTime.set(m_scene->getStats().m_physicsUpdate);
-				statsUi.m_gpuTime.set(m_renderer->getStats().m_renderingGpuTime);
-				statsUi.m_allocatedCpuMem = m_memStats.m_allocatedMem.load();
-				statsUi.m_allocCount = m_memStats.m_allocCount.load();
-				statsUi.m_freeCount = m_memStats.m_freeCount.load();
+				m_statsUi->m_frameTime.set(frameTime);
+				m_statsUi->m_renderTime.set(m_renderer->getStats().m_renderingCpuTime);
+				m_statsUi->m_sceneUpdateTime.set(m_scene->getStats().m_updateTime);
+				m_statsUi->m_visTestsTime.set(m_scene->getStats().m_visibilityTestsTime);
+				m_statsUi->m_physicsTime.set(m_scene->getStats().m_physicsUpdate);
+				m_statsUi->m_gpuTime.set(m_renderer->getStats().m_renderingGpuTime);
+				m_statsUi->m_allocatedCpuMem = m_memStats.m_allocatedMem.load();
+				m_statsUi->m_allocCount = m_memStats.m_allocCount.load();
+				m_statsUi->m_freeCount = m_memStats.m_freeCount.load();
 
 				GrManagerStats grStats = m_gr->getStats();
-				statsUi.m_vkCpuMem = grStats.m_cpuMemory;
-				statsUi.m_vkGpuMem = grStats.m_gpuMemory;
-				statsUi.m_vkCmdbCount = grStats.m_commandBufferCount;
+				m_statsUi->m_vkCpuMem = grStats.m_cpuMemory;
+				m_statsUi->m_vkGpuMem = grStats.m_gpuMemory;
+				m_statsUi->m_vkCmdbCount = grStats.m_commandBufferCount;
 
-				statsUi.m_drawableCount = rqueue.countAllRenderables();
+				m_statsUi->m_drawableCount = rqueue.countAllRenderables();
 			}
 
 #if ANKI_ENABLE_TRACE
@@ -680,9 +681,9 @@ void App::injectUiElements(DynamicArrayAuto<UiQueueElement>& newUiElementArr, Re
 	U32 count = originalCount;
 	if(m_displayStats)
 	{
-		newUiElementArr[count].m_userData = m_statsUi.get();
-		newUiElementArr[count].m_drawCallback = [](CanvasPtr& canvas, void* userData) -> void {
-			static_cast<StatsUi*>(userData)->build(canvas);
+		newUiElementArr[count].m_userData = m_statsUi;
+		newUiElementArr[count].m_drawCallback = [](CanvasPtr& canvas, const void* userData) -> void {
+			static_cast<StatsUi*>(const_cast<void*>(userData))->build(canvas);
 		};
 		++count;
 	}
@@ -690,8 +691,8 @@ void App::injectUiElements(DynamicArrayAuto<UiQueueElement>& newUiElementArr, Re
 	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);
+		newUiElementArr[count].m_drawCallback = [](CanvasPtr& canvas, const void* userData) -> void {
+			static_cast<DeveloperConsole*>(const_cast<void*>(userData))->build(canvas);
 		};
 		++count;
 	}

+ 1 - 1
AnKi/Core/App.h

@@ -183,7 +183,7 @@ private:
 	ScriptManager* m_script = nullptr;
 
 	// Misc
-	UiImmediateModeBuilderPtr m_statsUi;
+	StatsUi* m_statsUi = nullptr;
 	Bool m_displayStats = false;
 	UiImmediateModeBuilderPtr m_console;
 	Bool m_consoleEnabled = false;

+ 1 - 1
AnKi/Core/DeveloperConsole.cpp

@@ -26,7 +26,7 @@ Error DeveloperConsole::init(AllocAlignedCallback allocCb, void* allocCbUserData
 	m_alloc = HeapAllocator<U8>(allocCb, allocCbUserData);
 	zeroMemory(m_inputText);
 
-	ANKI_CHECK(m_manager->newInstance(m_font, "EngineAssets/UbuntuMonoRegular.ttf", std::initializer_list<U32>{16}));
+	ANKI_CHECK(m_manager->newInstance(m_font, "EngineAssets/UbuntuMonoRegular.ttf", Array<U32, 1>{16}));
 
 	// Add a new callback to the logger
 	LoggerSingleton::get().addMessageHandler(this, loggerCallback);

+ 3 - 3
AnKi/Renderer/RenderQueue.h

@@ -309,14 +309,14 @@ public:
 static_assert(std::is_trivially_destructible<DecalQueueElement>::value == true, "Should be trivially destructible");
 
 /// Draw callback for drawing.
-using UiQueueDrawCallback = void (*)(CanvasPtr& canvas, void* userData);
+using UiQueueElementDrawCallback = void (*)(CanvasPtr& canvas, const void* userData);
 
 /// UI element render queue element.
 class UiQueueElement final
 {
 public:
-	void* m_userData;
-	UiQueueDrawCallback m_drawCallback;
+	const void* m_userData;
+	UiQueueElementDrawCallback m_drawCallback;
 
 	UiQueueElement()
 	{

+ 1 - 2
AnKi/Renderer/UiStage.cpp

@@ -24,8 +24,7 @@ UiStage::~UiStage()
 
 Error UiStage::init(const ConfigSet&)
 {
-	ANKI_CHECK(m_r->getUiManager().newInstance(m_font, "EngineAssets/UbuntuRegular.ttf",
-											   std::initializer_list<U32>{12, 16, 20}));
+	ANKI_CHECK(m_r->getUiManager().newInstance(m_font, "EngineAssets/UbuntuRegular.ttf", Array<U32, 3>{12, 16, 20}));
 	ANKI_CHECK(m_r->getUiManager().newInstance(m_canvas, m_font, 12, m_r->getWidth(), m_r->getHeight()));
 
 	return Error::NONE;

+ 1 - 1
AnKi/Resource/MeshBinaryLoader.cpp

@@ -36,7 +36,7 @@ Error MeshBinaryLoader::load(const ResourceFilename& filename)
 		// Checks
 		const U32 indicesPerFace = !!(m_header.m_flags & MeshBinaryFlag::QUAD) ? 4 : 3;
 		U idxSum = 0;
-		for(U i = 0; i < m_subMeshes.getSize(); i++)
+		for(U32 i = 0; i < m_subMeshes.getSize(); i++)
 		{
 			const MeshBinarySubMesh& sm = m_subMeshes[i];
 			if(sm.m_firstIndex != idxSum || (sm.m_indexCount % indicesPerFace) != 0)

+ 1 - 0
AnKi/Scene.h

@@ -42,6 +42,7 @@
 #include <AnKi/Scene/Components/ParticleEmitterComponent.h>
 #include <AnKi/Scene/Components/GpuParticleEmitterComponent.h>
 #include <AnKi/Scene/Components/ModelComponent.h>
+#include <AnKi/Scene/Components/UiComponent.h>
 
 #include <AnKi/Scene/Events/EventManager.h>
 #include <AnKi/Scene/Events/Event.h>

+ 2 - 1
AnKi/Scene/CameraNode.cpp

@@ -68,7 +68,8 @@ void CameraNode::initCommon(FrustumType frustumType)
 		| FrustumComponentVisibilityTestFlag::FOG_DENSITY_COMPONENTS
 		| FrustumComponentVisibilityTestFlag::GLOBAL_ILLUMINATION_PROBES | FrustumComponentVisibilityTestFlag::EARLY_Z
 		| FrustumComponentVisibilityTestFlag::ALL_SHADOWS_ENABLED
-		| FrustumComponentVisibilityTestFlag::GENERIC_COMPUTE_JOB_COMPONENTS;
+		| FrustumComponentVisibilityTestFlag::GENERIC_COMPUTE_JOB_COMPONENTS
+		| FrustumComponentVisibilityTestFlag::UI_COMPONENTS;
 	frc->setEnabledVisibilityTests(visibilityFlags);
 	frc->setLodDistance(0, getSceneGraph().getConfig().m_maxLodDistances[0]);
 	frc->setLodDistance(1, getSceneGraph().getConfig().m_maxLodDistances[1]);

+ 2 - 1
AnKi/Scene/Components/FrustumComponent.h

@@ -44,12 +44,13 @@ enum class FrustumComponentVisibilityTestFlag : U32
 	RAY_TRACING_GI = 1 << 17,
 	RAY_TRACING_REFLECTIONS = 1 << 18,
 	RAY_TRACING_PATH_TRACING = 1 << 19,
+	UI_COMPONENTS = 1 << 20,
 
 	ALL = RENDER_COMPONENTS | LIGHT_COMPONENTS | LENS_FLARE_COMPONENTS | SHADOW_CASTERS | POINT_LIGHT_SHADOWS_ENABLED
 		  | SPOT_LIGHT_SHADOWS_ENABLED | DIRECTIONAL_LIGHT_SHADOWS_ALL_CASCADES | DIRECTIONAL_LIGHT_SHADOWS_1_CASCADE
 		  | REFLECTION_PROBES | REFLECTION_PROXIES | OCCLUDERS | DECALS | FOG_DENSITY_COMPONENTS
 		  | GLOBAL_ILLUMINATION_PROBES | EARLY_Z | GENERIC_COMPUTE_JOB_COMPONENTS | RAY_TRACING_SHADOWS | RAY_TRACING_GI
-		  | RAY_TRACING_REFLECTIONS | RAY_TRACING_PATH_TRACING,
+		  | RAY_TRACING_REFLECTIONS | RAY_TRACING_PATH_TRACING | UI_COMPONENTS,
 
 	ALL_SHADOWS_ENABLED =
 		POINT_LIGHT_SHADOWS_ENABLED | SPOT_LIGHT_SHADOWS_ENABLED | DIRECTIONAL_LIGHT_SHADOWS_ALL_CASCADES,

+ 0 - 1
AnKi/Scene/Components/RenderComponent.h

@@ -5,7 +5,6 @@
 
 #pragma once
 
-#include <AnKi/Scene/Common.h>
 #include <AnKi/Scene/Components/SceneComponent.h>
 #include <AnKi/Resource/MaterialResource.h>
 #include <AnKi/Core/StagingGpuMemoryManager.h>

+ 26 - 18
AnKi/Scene/Components/SpatialComponent.cpp

@@ -18,6 +18,7 @@ SpatialComponent::SpatialComponent(SceneNode* node)
 	, m_markedForUpdate(true)
 	, m_placed(false)
 	, m_updateOctreeBounds(true)
+	, m_alwaysVisible(false)
 {
 	ANKI_ASSERT(node);
 	m_octreeInfo.m_userData = this;
@@ -62,28 +63,35 @@ Error SpatialComponent::update(SceneNode& node, Second prevTime, Second crntTime
 	updated = m_markedForUpdate;
 	if(updated)
 	{
-		// Compute the AABB
-		switch(m_collisionObjectType)
+		if(!m_alwaysVisible)
 		{
-		case CollisionShapeType::AABB:
-			m_derivedAabb = m_aabb;
-			break;
-		case CollisionShapeType::OBB:
-			m_derivedAabb = computeAabb(m_obb);
-			break;
-		case CollisionShapeType::SPHERE:
-			m_derivedAabb = computeAabb(m_sphere);
-			break;
-		case CollisionShapeType::CONVEX_HULL:
-			m_derivedAabb = computeAabb(m_hull);
-			break;
-		default:
-			ANKI_ASSERT(0);
+			// Compute the AABB
+			switch(m_collisionObjectType)
+			{
+			case CollisionShapeType::AABB:
+				m_derivedAabb = m_aabb;
+				break;
+			case CollisionShapeType::OBB:
+				m_derivedAabb = computeAabb(m_obb);
+				break;
+			case CollisionShapeType::SPHERE:
+				m_derivedAabb = computeAabb(m_sphere);
+				break;
+			case CollisionShapeType::CONVEX_HULL:
+				m_derivedAabb = computeAabb(m_hull);
+				break;
+			default:
+				ANKI_ASSERT(0);
+			}
+
+			m_node->getSceneGraph().getOctree().place(m_derivedAabb, &m_octreeInfo, m_updateOctreeBounds);
+		}
+		else
+		{
+			m_node->getSceneGraph().getOctree().placeAlwaysVisible(&m_octreeInfo);
 		}
 
 		m_markedForUpdate = false;
-
-		m_node->getSceneGraph().getOctree().place(m_derivedAabb, &m_octreeInfo, m_updateOctreeBounds);
 		m_placed = true;
 	}
 

+ 14 - 0
AnKi/Scene/Components/SpatialComponent.h

@@ -65,6 +65,7 @@ public:
 
 	const Aabb& getAabbWorldSpace() const
 	{
+		ANKI_ASSERT(!m_alwaysVisible);
 		return m_derivedAabb;
 	}
 
@@ -98,6 +99,18 @@ public:
 		m_updateOctreeBounds = update;
 	}
 
+	/// Make it or not always visible.
+	void setAlwaysVisible(Bool alwaysVisible)
+	{
+		m_alwaysVisible = alwaysVisible;
+	}
+
+	/// See if it's always visible or not.
+	Bool getAlwaysVisible() const
+	{
+		return m_alwaysVisible;
+	}
+
 	ANKI_USE_RESULT Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override;
 
 private:
@@ -124,6 +137,7 @@ private:
 	Bool m_markedForUpdate : 1;
 	Bool m_placed : 1;
 	Bool m_updateOctreeBounds : 1;
+	Bool m_alwaysVisible : 1;
 };
 /// @}
 

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

@@ -0,0 +1,13 @@
+// Copyright (C) 2009-2021, 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>
+
+namespace anki
+{
+
+ANKI_SCENE_COMPONENT_STATICS(UiComponent)
+
+} // end namespace anki

+ 50 - 0
AnKi/Scene/Components/UiComponent.h

@@ -0,0 +1,50 @@
+// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <AnKi/Scene/Components/SceneComponent.h>
+#include <AnKi/Renderer/RenderQueue.h>
+
+namespace anki
+{
+
+/// @addtogroup scene
+/// @{
+
+/// UI scene component.
+class UiComponent : public SceneComponent
+{
+	ANKI_SCENE_COMPONENT(UiComponent)
+
+public:
+	UiComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId())
+	{
+	}
+
+	void init(UiQueueElementDrawCallback callback, const void* userData)
+	{
+		ANKI_ASSERT(callback != nullptr);
+		ANKI_ASSERT(userData != nullptr);
+		m_drawCallback = callback;
+		m_userData = userData;
+	}
+
+	void setupUiQueueElement(UiQueueElement& el) const
+	{
+		ANKI_ASSERT(el.m_drawCallback != nullptr);
+		el.m_drawCallback = m_drawCallback;
+		ANKI_ASSERT(el.m_userData != nullptr);
+		el.m_userData = m_userData;
+	}
+
+private:
+	UiQueueElementDrawCallback m_drawCallback = nullptr;
+	const void* m_userData = nullptr;
+};
+/// @}
+
+} // end namespace anki

+ 2 - 6
AnKi/Scene/LightNode.cpp

@@ -315,12 +315,8 @@ DirectionalLightNode::DirectionalLightNode(SceneGraph* scene, CString name)
 
 	SpatialComponent* spatialc = newComponent<SpatialComponent>();
 
-	// Make the bounding box large enough so it will always be visible. Because of that don't update the octree bounds
-	Aabb boundingBox;
-	boundingBox.setMin(getSceneGraph().getSceneMin());
-	boundingBox.setMax(getSceneGraph().getSceneMax());
-	spatialc->setAabbWorldSpace(boundingBox);
-	spatialc->setUpdateOctreeBounds(false);
+	// Make the spatial always visible
+	spatialc->setAlwaysVisible(true);
 }
 
 } // end namespace anki

+ 28 - 5
AnKi/Scene/Octree.cpp

@@ -102,6 +102,30 @@ void Octree::place(const Aabb& volume, OctreePlaceable* placeable, Bool updateAc
 	}
 }
 
+void Octree::placeAlwaysVisible(OctreePlaceable* placeable)
+{
+	ANKI_ASSERT(placeable);
+
+	LockGuard<Mutex> lock(m_globalMtx);
+
+	// Remove the placeable from the Octree
+	removeInternal(*placeable);
+
+	// Create the root leaf
+	if(!m_rootLeaf)
+	{
+		m_rootLeaf = newLeaf();
+		m_rootLeaf->m_aabbMin = m_sceneAabbMin;
+		m_rootLeaf->m_aabbMax = m_sceneAabbMax;
+	}
+
+	// Connect placeable and leaf
+	placeable->m_leafs.pushBack(newLeafNode(m_rootLeaf));
+	m_rootLeaf->m_placeables.pushBack(newPlaceableNode(placeable));
+
+	++m_placeableCount;
+}
+
 void Octree::remove(OctreePlaceable& placeable)
 {
 	LockGuard<Mutex> lock(m_globalMtx);
@@ -295,18 +319,17 @@ void Octree::removeInternal(OctreePlaceable& placeable)
 		while(!placeable.m_leafs.isEmpty())
 		{
 			// Pop a leaf node
-			LeafNode& leafNode = placeable.m_leafs.getFront();
-			placeable.m_leafs.popFront();
+			LeafNode* leafNode = placeable.m_leafs.popFront();
 
 			// Iterate the placeables of the leaf
 			Bool found = false;
 			(void)found;
-			for(PlaceableNode& placeableNode : leafNode.m_leaf->m_placeables)
+			for(PlaceableNode& placeableNode : leafNode->m_leaf->m_placeables)
 			{
 				if(placeableNode.m_placeable == &placeable)
 				{
 					found = true;
-					leafNode.m_leaf->m_placeables.erase(&placeableNode);
+					leafNode->m_leaf->m_placeables.erase(&placeableNode);
 					releasePlaceableNode(&placeableNode);
 					break;
 				}
@@ -314,7 +337,7 @@ void Octree::removeInternal(OctreePlaceable& placeable)
 			ANKI_ASSERT(found);
 
 			// Delete the leaf node
-			releaseLeafNode(&leafNode);
+			releaseLeafNode(leafNode);
 		}
 
 		// Cleanup the tree if there are no placeables

+ 4 - 0
AnKi/Scene/Octree.h

@@ -54,6 +54,10 @@ public:
 	/// @note It's thread-safe against place and remove methods.
 	void place(const Aabb& volume, OctreePlaceable* placeable, Bool updateActualSceneBounds);
 
+	/// Place the placeable somewhere where it's always visible.
+	/// @note It's thread-safe against place and remove methods.
+	void placeAlwaysVisible(OctreePlaceable* placeable);
+
 	/// Remove an element from the tree.
 	/// @note It's thread-safe against place and remove methods.
 	void remove(OctreePlaceable& placeable);

+ 2 - 1
AnKi/Scene/SceneGraph.cpp

@@ -55,7 +55,7 @@ SceneGraph::~SceneGraph()
 }
 
 Error SceneGraph::init(AllocAlignedCallback allocCb, void* allocCbData, ThreadHive* threadHive,
-					   ResourceManager* resources, Input* input, ScriptManager* scriptManager,
+					   ResourceManager* resources, Input* input, ScriptManager* scriptManager, UiManager* uiManager,
 					   const Timestamp* globalTimestamp, const ConfigSet& config)
 {
 	m_globalTimestamp = globalTimestamp;
@@ -65,6 +65,7 @@ Error SceneGraph::init(AllocAlignedCallback allocCb, void* allocCbData, ThreadHi
 	m_physics = &m_resources->getPhysicsWorld();
 	m_input = input;
 	m_scriptManager = scriptManager;
+	m_uiManager = uiManager;
 
 	m_alloc = SceneAllocator<U8>(allocCb, allocCbData);
 	m_frameAlloc = SceneFrameAllocator<U8>(allocCb, allocCbData, 1 * 1024 * 1024);

+ 8 - 1
AnKi/Scene/SceneGraph.h

@@ -24,6 +24,7 @@ class Input;
 class ConfigSet;
 class PerspectiveCameraNode;
 class Octree;
+class UiManager;
 
 /// @addtogroup scene
 /// @{
@@ -62,7 +63,7 @@ public:
 
 	ANKI_USE_RESULT Error init(AllocAlignedCallback allocCb, void* allocCbData, ThreadHive* threadHive,
 							   ResourceManager* resources, Input* input, ScriptManager* scriptManager,
-							   const Timestamp* globalTimestamp, const ConfigSet& config);
+							   UiManager* uiManager, const Timestamp* globalTimestamp, const ConfigSet& config);
 
 	Timestamp getGlobalTimestamp() const
 	{
@@ -213,6 +214,11 @@ public:
 		return *m_input;
 	}
 
+	UiManager& getUiManager()
+	{
+		return *m_uiManager;
+	}
+
 	U64 getNewUuid()
 	{
 		return m_nodesUuid.fetchAdd(1);
@@ -242,6 +248,7 @@ private:
 	PhysicsWorld* m_physics = nullptr;
 	Input* m_input = nullptr;
 	ScriptManager* m_scriptManager = nullptr;
+	UiManager* m_uiManager = nullptr;
 
 	SceneAllocator<U8> m_alloc;
 	SceneFrameAllocator<U8> m_frameAlloc;

+ 15 - 2
AnKi/Scene/Visibility.cpp

@@ -16,6 +16,7 @@
 #include <AnKi/Scene/Components/SpatialComponent.h>
 #include <AnKi/Scene/Components/GlobalIlluminationProbeComponent.h>
 #include <AnKi/Scene/Components/GenericGpuComputeJobComponent.h>
+#include <AnKi/Scene/Components/UiComponent.h>
 #include <AnKi/Renderer/MainRenderer.h>
 #include <AnKi/Util/Logger.h>
 #include <AnKi/Util/ThreadHive.h>
@@ -325,6 +326,10 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 		wantNode |= !!(enabledVisibilityTests & FrustumComponentVisibilityTestFlag::GENERIC_COMPUTE_JOB_COMPONENTS)
 					&& (computec = node.tryGetFirstComponentOfType<GenericGpuComputeJobComponent>());
 
+		UiComponent* uic = nullptr;
+		wantNode |= !!(enabledVisibilityTests & FrustumComponentVisibilityTestFlag::UI_COMPONENTS)
+					&& (uic = node.tryGetFirstComponentOfType<UiComponent>());
+
 		if(ANKI_UNLIKELY(!wantNode))
 		{
 			// Skip node
@@ -337,7 +342,8 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 			continue;
 		}
 
-		if(!spatialInsideFrustum(testedFrc, *spatialc) || !testAgainstRasterizer(spatialc->getAabbWorldSpace()))
+		if(!spatialc->getAlwaysVisible()
+		   && (!spatialInsideFrustum(testedFrc, *spatialc) || !testAgainstRasterizer(spatialc->getAabbWorldSpace())))
 		{
 			continue;
 		}
@@ -390,7 +396,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 		{
 			// Check if it casts shadow
 			Bool castsShadow = lc->getShadowEnabled();
-			if(castsShadow)
+			if(castsShadow && lc->getLightComponentType() != LightComponentType::DIRECTIONAL)
 			{
 				// Extra check
 
@@ -586,6 +592,12 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 			computec->setupGenericGpuComputeJobQueueElement(*el);
 		}
 
+		if(uic)
+		{
+			UiQueueElement* el = result.m_uis.newElement(alloc);
+			uic->setupUiQueueElement(*el);
+		}
+
 		// Add more frustums to the list
 		if(nextQueues.getSize() > 0)
 		{
@@ -651,6 +663,7 @@ void CombineResultsTask::combine()
 	ANKI_VIS_COMBINE(GlobalIlluminationProbeQueueElement, m_giProbes);
 	ANKI_VIS_COMBINE(GenericGpuComputeJobQueueElement, m_genericGpuComputeJobs);
 	ANKI_VIS_COMBINE(RayTracingInstanceQueueElement, m_rayTracingInstances);
+	ANKI_VIS_COMBINE(UiQueueElement, m_uis);
 
 	for(U32 i = 0; i < threadCount; ++i)
 	{

+ 1 - 0
AnKi/Scene/VisibilityInternal.h

@@ -105,6 +105,7 @@ public:
 	TRenderQueueElementStorage<GlobalIlluminationProbeQueueElement> m_giProbes;
 	TRenderQueueElementStorage<GenericGpuComputeJobQueueElement> m_genericGpuComputeJobs;
 	TRenderQueueElementStorage<RayTracingInstanceQueueElement> m_rayTracingInstances;
+	TRenderQueueElementStorage<UiQueueElement> m_uis;
 
 	Timestamp m_timestamp = 0;
 

+ 2 - 2
AnKi/Ui/Font.cpp

@@ -25,7 +25,7 @@ Font::~Font()
 	m_fontData.destroy(getAllocator());
 }
 
-Error Font::init(const CString& filename, const std::initializer_list<U32>& fontHeights)
+Error Font::init(const CString& filename, ConstWeakArray<U32> fontHeights)
 {
 	setImAllocator();
 	m_imFontAtlas.init();
@@ -36,7 +36,7 @@ Error Font::init(const CString& filename, const std::initializer_list<U32>& font
 	m_fontData.create(getAllocator(), U32(file->getSize()));
 	ANKI_CHECK(file->read(&m_fontData[0], file->getSize()));
 
-	m_fonts.create(getAllocator(), U32(fontHeights.size()));
+	m_fonts.create(getAllocator(), U32(fontHeights.getSize()));
 
 	// Bake font
 	ImFontConfig cfg;

+ 2 - 2
AnKi/Ui/Font.h

@@ -8,7 +8,7 @@
 #include <AnKi/Ui/UiObject.h>
 #include <AnKi/Gr/Texture.h>
 #include <AnKi/Util/ClassWrapper.h>
-#include <initializer_list>
+#include <AnKi/Util/WeakArray.h>
 
 namespace anki
 {
@@ -28,7 +28,7 @@ public:
 	~Font();
 
 	/// Initialize the font.
-	ANKI_USE_RESULT Error init(const CString& filename, const std::initializer_list<U32>& fontHeights);
+	ANKI_USE_RESULT Error init(const CString& filename, ConstWeakArray<U32> fontHeights);
 
 	/// Get font image atlas.
 	ANKI_INTERNAL const TextureViewPtr& getTextureView() const

+ 9 - 1
AnKi/Util/StdTypes.h

@@ -193,7 +193,7 @@ private:
 	I32 m_code = NONE;
 };
 
-/// Macro to check if a method/function returned an error.
+/// Macro to check if a method/function returned an error. It will return on error.
 #define ANKI_CHECK(x_) \
 	do \
 	{ \
@@ -204,6 +204,14 @@ private:
 		} \
 	} while(0)
 
+/// Macro to check if a method/function returned an error.
+#define ANKI_CHECK_AND_IGNORE(x_) \
+	do \
+	{ \
+		const Error retError = x_; \
+		(void)retError; \
+	} while(0)
+
 #if ANKI_EXTRA_CHECKS
 #	define ANKI_DEBUG_CODE(x) x
 #else

+ 1 - 1
Tests/Ui/Ui.cpp

@@ -80,7 +80,7 @@ ANKI_TEST(Ui, Ui)
 
 	{
 		FontPtr font;
-		ANKI_TEST_EXPECT_NO_ERR(ui->newInstance(font, "UbuntuRegular.ttf", std::initializer_list<U32>{10, 20, 30, 60}));
+		ANKI_TEST_EXPECT_NO_ERR(ui->newInstance(font, "UbuntuRegular.ttf", Array<U32, 4>{10, 20, 30, 60}));
 
 		CanvasPtr canvas;
 		ANKI_TEST_EXPECT_NO_ERR(ui->newInstance(canvas, font, 20, win->getWidth(), win->getHeight()));

+ 1 - 0
Tools/CMakeLists.txt

@@ -1,2 +1,3 @@
 add_subdirectory(GltfImporter)
 add_subdirectory(Shader)
+add_subdirectory(Texture)

+ 3 - 0
Tools/Texture/CMakeLists.txt

@@ -0,0 +1,3 @@
+add_executable(TextureViewer TextureViewerMain.cpp)
+target_link_libraries(TextureViewer AnKi)
+installExecutable(TextureViewer)

+ 105 - 0
Tools/Texture/TextureViewerMain.cpp

@@ -0,0 +1,105 @@
+// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <AnKi/AnKi.h>
+
+using namespace anki;
+
+class TextureViewerUiNode : public SceneNode
+{
+public:
+	TextureViewerUiNode(SceneGraph* scene, CString name)
+		: SceneNode(scene, name)
+	{
+		SpatialComponent* spatialc = newComponent<SpatialComponent>();
+		spatialc->setAlwaysVisible(true);
+
+		UiComponent* uic = newComponent<UiComponent>();
+		uic->init([](CanvasPtr& canvas, const void* ud) { static_cast<const TextureViewerUiNode*>(ud)->draw(canvas); },
+				  this);
+
+		ANKI_CHECK_AND_IGNORE(getSceneGraph().getUiManager().newInstance(m_font, "EngineAssets/UbuntuMonoRegular.ttf",
+																		 Array<U32, 1>{32}));
+	}
+
+private:
+	FontPtr m_font;
+
+	void draw(CanvasPtr& canvas) const
+	{
+		ImGui::Begin("Console", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoTitleBar);
+
+		canvas->pushFont(m_font, 32);
+
+		ImGui::SetWindowPos(Vec2(0.0f, 0.0f));
+		ImGui::SetWindowSize(Vec2(F32(canvas->getWidth()), F32(canvas->getHeight())));
+
+		ImGui::PushStyleColor(ImGuiCol_Text, Vec4(0.0f, 0.0f, 0.0f, 1.0f));
+		ImGui::TextWrapped("Test");
+		ImGui::PopStyleColor();
+
+		canvas->popFont();
+		ImGui::End();
+	}
+};
+
+class MyApp : public App
+{
+public:
+	Error init(int argc, char** argv, CString appName)
+	{
+		HeapAllocator<U32> alloc(allocAligned, nullptr);
+		StringAuto mainDataPath(alloc, ANKI_SOURCE_DIRECTORY);
+
+		ConfigSet config = DefaultConfigSet::get();
+		config.set("window_fullscreen", false);
+		config.set("rsrc_dataPaths", mainDataPath);
+		config.set("gr_validation", 0);
+		ANKI_CHECK(config.setFromCommandLineArguments(argc - 1, argv + 1));
+
+		ANKI_CHECK(App::init(config, allocAligned, nullptr));
+
+		SceneGraph& scene = getSceneGraph();
+		TextureViewerUiNode* node;
+		ANKI_CHECK(scene.newSceneNode("TextureViewer", node));
+
+		return Error::NONE;
+	}
+
+	Error userMainLoop(Bool& quit, Second elapsedTime) override
+	{
+		Input& input = getInput();
+		if(input.getKey(KeyCode::ESCAPE))
+		{
+			quit = true;
+		}
+
+		return Error::NONE;
+	}
+};
+
+int main(int argc, char* argv[])
+{
+	Error err = Error::NONE;
+
+	MyApp* app = new MyApp;
+	err = app->init(argc, argv, "Texture Viewer");
+	if(!err)
+	{
+		err = app->mainLoop();
+	}
+
+	delete app;
+	if(err)
+	{
+		ANKI_LOGE("Error reported. Bye!!");
+	}
+	else
+	{
+		ANKI_LOGI("Bye!!");
+	}
+
+	return 0;
+}