Browse Source

Refactor the stats UI. Some changes in animation importing

Panagiotis Christopoulos Charitos 3 years ago
parent
commit
621e55ff11

+ 34 - 23
AnKi/Core/App.cpp

@@ -449,7 +449,7 @@ Error App::mainLoop()
 
 			// Render
 			TexturePtr presentableTex = m_gr->acquireNextPresentableTexture();
-			m_renderer->setStatsEnabled(m_config->getCoreDisplayStats()
+			m_renderer->setStatsEnabled(m_config->getCoreDisplayStats() > 0
 #if ANKI_ENABLE_TRACE
 										|| TracerSingleton::get().getEnabled()
 #endif
@@ -481,36 +481,47 @@ Error App::mainLoop()
 			}
 
 			// Stats
-			if(m_config->getCoreDisplayStats())
+			if(m_config->getCoreDisplayStats() > 0)
 			{
-				StatsUi& statsUi = *static_cast<StatsUi*>(m_statsUi.get());
+				StatsUiInput in;
+				in.m_cpuFrameTime = frameTime;
+				in.m_rendererTime = m_renderer->getStats().m_renderingCpuTime;
+				in.m_sceneUpdateTime = m_scene->getStats().m_updateTime;
+				in.m_visibilityTestsTime = m_scene->getStats().m_visibilityTestsTime;
+				in.m_physicsTime = m_scene->getStats().m_physicsUpdate;
 
-				statsUi.setFrameTime(frameTime);
-				statsUi.setRenderTime(m_renderer->getStats().m_renderingCpuTime);
-				statsUi.setSceneUpdateTime(m_scene->getStats().m_updateTime);
-				statsUi.setVisibilityTestsTime(m_scene->getStats().m_visibilityTestsTime);
-				statsUi.setPhysicsTime(m_scene->getStats().m_physicsUpdate);
+				in.m_gpuFrameTime = m_renderer->getStats().m_renderingGpuTime;
 
-				statsUi.setGpuTime(m_renderer->getStats().m_renderingGpuTime);
 				if(m_maliHwCounters)
 				{
 					MaliHwCountersOut out;
 					m_maliHwCounters->sample(out);
-
-					statsUi.setGpuActiveCycles(out.m_gpuActive);
-					statsUi.setGpuReadBandwidth(out.m_readBandwidth);
-					statsUi.setGpuWriteBandwidth(out.m_writeBandwidth);
+					in.m_gpuActiveCycles = out.m_gpuActive;
+					in.m_gpuReadBandwidth = out.m_readBandwidth;
+					in.m_gpuWriteBandwidth = out.m_writeBandwidth;
 				}
 
-				statsUi.setAllocatedCpuMemory(m_memStats.m_allocatedMem.load());
-				statsUi.setCpuAllocationCount(m_memStats.m_allocCount.load());
-				statsUi.setCpuFreeCount(m_memStats.m_freeCount.load());
-				statsUi.setGrStats(m_gr->getStats());
+				in.m_cpuAllocatedMemory = m_memStats.m_allocatedMem.load();
+				in.m_cpuAllocationCount = m_memStats.m_allocCount.load();
+				in.m_cpuFreeCount = m_memStats.m_freeCount.load();
+
+				const GrManagerStats grStats = m_gr->getStats();
 				BuddyAllocatorBuilderStats vertMemStats;
 				m_vertexMem->getMemoryStats(vertMemStats);
-				statsUi.setGlobalVertexMemoryPoolStats(vertMemStats);
 
-				statsUi.setDrawableCount(rqueue.countAllRenderables());
+				in.m_gpuDeviceMemoryAllocated = grStats.m_deviceMemoryAllocated;
+				in.m_gpuDeviceMemoryInUse = grStats.m_deviceMemoryInUse;
+				in.m_globalVertexAllocated = vertMemStats.m_realAllocatedSize;
+				in.m_globalVertexUsed = vertMemStats.m_userAllocatedSize;
+				in.m_globalVertexExternalFragmentation = vertMemStats.m_externalFragmentation;
+
+				in.m_drawableCount = rqueue.countAllRenderables();
+				in.m_vkCommandBufferCount = grStats.m_commandBufferCount;
+
+				StatsUi& statsUi = *static_cast<StatsUi*>(m_statsUi.get());
+				const StatsUiDetail detail =
+					(m_config->getCoreDisplayStats() == 1) ? StatsUiDetail::kFpsOnly : StatsUiDetail::kDetailed;
+				statsUi.setStats(in, detail);
 			}
 
 #if ANKI_ENABLE_TRACE
@@ -536,9 +547,9 @@ Error App::mainLoop()
 void App::injectUiElements(DynamicArrayAuto<UiQueueElement>& newUiElementArr, RenderQueue& rqueue)
 {
 	const U32 originalCount = rqueue.m_uis.getSize();
-	if(m_config->getCoreDisplayStats() || m_consoleEnabled)
+	if(m_config->getCoreDisplayStats() > 0 || m_consoleEnabled)
 	{
-		const U32 extraElements = m_config->getCoreDisplayStats() + (m_consoleEnabled != 0);
+		const U32 extraElements = (m_config->getCoreDisplayStats() > 0) + (m_consoleEnabled != 0);
 		newUiElementArr.create(originalCount + extraElements);
 
 		if(originalCount > 0)
@@ -550,7 +561,7 @@ void App::injectUiElements(DynamicArrayAuto<UiQueueElement>& newUiElementArr, Re
 	}
 
 	U32 count = originalCount;
-	if(m_config->getCoreDisplayStats())
+	if(m_config->getCoreDisplayStats() > 0)
 	{
 		newUiElementArr[count].m_userData = m_statsUi.get();
 		newUiElementArr[count].m_drawCallback = [](CanvasPtr& canvas, void* userData) -> void {
@@ -571,7 +582,7 @@ void App::injectUiElements(DynamicArrayAuto<UiQueueElement>& newUiElementArr, Re
 
 void App::initMemoryCallbacks(AllocAlignedCallback allocCb, void* allocCbUserData)
 {
-	if(m_config->getCoreDisplayStats())
+	if(m_config->getCoreDisplayStats() > 1)
 	{
 		m_memStats.m_originalAllocCallback = allocCb;
 		m_memStats.m_originalUserData = allocCbUserData;

+ 1 - 0
AnKi/Core/CMakeLists.txt

@@ -19,6 +19,7 @@ set(headers
 	MaliHwCounters.h
 	NativeWindow.h
 	StatsUi.h
+	StatsUi.defs.h
 	StdinListener.h)
 
 if(ANKI_HEADLESS)

+ 1 - 1
AnKi/Core/ConfigVars.defs.h

@@ -19,6 +19,6 @@ ANKI_CONFIG_VAR_U32(WindowFullscreen, 1, 0, 2, "0: windowed, 1: borderless fulls
 
 ANKI_CONFIG_VAR_U32(CoreTargetFps, 60u, 30u, MAX_U32, "Target FPS")
 ANKI_CONFIG_VAR_U32(CoreJobThreadCount, max(2u, getCpuCoresCount() / 2u), 2u, 1024u, "Number of job thread")
-ANKI_CONFIG_VAR_BOOL(CoreDisplayStats, false, "Display stats")
+ANKI_CONFIG_VAR_U32(CoreDisplayStats, 0, 0, 2, "Display stats, 0: None, 1: Simple, 2: Detailed")
 ANKI_CONFIG_VAR_BOOL(CoreClearCaches, false, "Clear all caches")
 ANKI_CONFIG_VAR_BOOL(CoreVerboseLog, false, "Verbose logging")

+ 67 - 45
AnKi/Core/StatsUi.cpp

@@ -56,18 +56,27 @@ void StatsUi::labelBytes(PtrSize val, CString name) const
 	ImGui::TextUnformatted(timestamp.cstr());
 }
 
-void StatsUi::build(CanvasPtr canvas)
+void StatsUi::setStats(const StatsUiInput& input, StatsUiDetail detail)
 {
-	// Misc
-	++m_bufferedFrames;
 	Bool flush = false;
-	if(m_bufferedFrames == BUFFERED_FRAMES)
+	if(m_bufferedFrames == kBufferedFrames)
 	{
 		flush = true;
 		m_bufferedFrames = 0;
 	}
+	++m_bufferedFrames;
+
+#define ANKI_STATS_UI_BEGIN_GROUP(x)
+#define ANKI_STATS_UI_VALUE(type, name, text, flags) m_##name.update(input.m_##name, flags, flush);
+#include <AnKi/Core/StatsUi.defs.h>
+#undef ANKI_STATS_UI_BEGIN_GROUP
+#undef ANKI_STATS_UI_VALUE
+
+	m_detail = detail;
+}
 
-	// Start drawing the UI
+void StatsUi::build(CanvasPtr canvas)
+{
 	canvas->pushFont(m_font, 24);
 
 	const Vec4 oldWindowColor = ImGui::GetStyle().Colors[ImGuiCol_WindowBg];
@@ -78,48 +87,61 @@ void StatsUi::build(CanvasPtr canvas)
 		ImGui::SetWindowPos(Vec2(5.0f, 5.0f));
 		ImGui::SetWindowSize(Vec2(230.0f, 450.0f));
 
-		ImGui::Text("CPU Time:");
-		labelTime(m_frameTime.get(flush), "Total frame");
-		labelTime(m_renderTime.get(flush), "Renderer");
-		labelTime(m_sceneUpdateTime.get(flush), "Scene update");
-		labelTime(m_visTestsTime.get(flush), "Visibility");
-		labelTime(m_physicsTime.get(flush), "Physics");
-
-		ImGui::Text("----");
-		ImGui::Text("GPU:");
-		labelTime(m_gpuTime.get(flush), "Total frame");
-		const U64 gpuActive = m_gpuActive.get(flush);
-		if(gpuActive)
+		auto writeText = [this](const Value& v, const Char* text, ValueFlag flags, Bool isFloat) {
+			if(isFloat)
+			{
+				if(!!(flags & ValueFlag::kSeconds))
+				{
+					labelTime(v.m_float, text);
+				}
+				else
+				{
+					ImGui::Text("%s: %f", text, v.m_float);
+				}
+			}
+			else
+			{
+				U64 val;
+				if(!!(flags & ValueFlag::kAverage))
+				{
+					val = U64(v.m_avg);
+				}
+				else
+				{
+					val = v.m_int;
+				}
+
+				if(!!(flags & ValueFlag::kBytes))
+				{
+					labelBytes(val, text);
+				}
+				else
+				{
+					ImGui::Text("%s: %zu", text, val);
+				}
+			}
+		};
+
+		if(m_detail == StatsUiDetail::kDetailed)
 		{
-			ImGui::Text("%s: %luK cycles", "Active Cycles", gpuActive / 1000);
-			labelBytes(m_gpuReadBandwidth.get(flush), "Read bandwidth");
-			labelBytes(m_gpuWriteBandwidth.get(flush), "Write bandwidth");
+#define ANKI_STATS_UI_BEGIN_GROUP(x) \
+	ImGui::Text("----"); \
+	ImGui::Text(x); \
+	ImGui::Text("----");
+#define ANKI_STATS_UI_VALUE(type, name, text, flags) \
+	writeText(m_##name, text, flags, std::is_floating_point<type>::value);
+#include <AnKi/Core/StatsUi.defs.h>
+#undef ANKI_STATS_UI_BEGIN_GROUP
+#undef ANKI_STATS_UI_VALUE
+		}
+		else
+		{
+			const Second maxTime = max(m_cpuFrameTime.m_float, m_gpuFrameTime.m_float);
+			const F32 fps = F32(1.0 / maxTime);
+			const Bool cpuBound = m_cpuFrameTime.m_float > m_gpuFrameTime.m_float;
+			ImGui::TextColored((cpuBound) ? Vec4(1.0f, 0.5f, 0.5f, 1.0f) : Vec4(0.5f, 1.0f, 0.5f, 1.0f), "FPS %.1f",
+							   fps);
 		}
-
-		ImGui::Text("----");
-		ImGui::Text("CPU Memory:");
-		labelBytes(m_allocatedCpuMem, "Total CPU");
-		labelUint(m_allocCount, "Total allocations");
-		labelUint(m_freeCount, "Total frees");
-
-		ImGui::Text("----");
-		ImGui::Text("GPU Memory:");
-		labelBytes(m_grStats.m_hostMemoryAllocated, "Host");
-		labelBytes(m_grStats.m_hostMemoryInUse, "Host in use");
-		labelUint(m_grStats.m_hostMemoryAllocationCount, "Host allocations");
-		labelBytes(m_grStats.m_deviceMemoryAllocated, "Device");
-		labelBytes(m_grStats.m_deviceMemoryInUse, "Device in use");
-		labelUint(m_grStats.m_deviceMemoryAllocationCount, "Device allocations");
-		labelBytes(m_globalVertexPoolStats.m_userAllocatedSize, "Vertex/Index GPU memory");
-		labelBytes(m_globalVertexPoolStats.m_realAllocatedSize, "Actual Vertex/Index GPU memory");
-
-		ImGui::Text("----");
-		ImGui::Text("Vulkan:");
-		labelUint(m_grStats.m_commandBufferCount, "Cmd buffers");
-
-		ImGui::Text("----");
-		ImGui::Text("Other:");
-		labelUint(m_drawableCount, "Drawbles");
 	}
 
 	ImGui::End();

+ 33 - 0
AnKi/Core/StatsUi.defs.h

@@ -0,0 +1,33 @@
+// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+ANKI_STATS_UI_BEGIN_GROUP("CPU")
+ANKI_STATS_UI_VALUE(Second, cpuFrameTime, "Total frame", ValueFlag::kAverage | ValueFlag::kSeconds)
+ANKI_STATS_UI_VALUE(Second, rendererTime, "Renderer", ValueFlag::kAverage | ValueFlag::kSeconds)
+ANKI_STATS_UI_VALUE(Second, sceneUpdateTime, "Scene update", ValueFlag::kAverage | ValueFlag::kSeconds)
+ANKI_STATS_UI_VALUE(Second, visibilityTestsTime, "Visibility tests", ValueFlag::kAverage | ValueFlag::kSeconds)
+ANKI_STATS_UI_VALUE(Second, physicsTime, "Physics", ValueFlag::kAverage | ValueFlag::kSeconds)
+
+ANKI_STATS_UI_BEGIN_GROUP("GPU")
+ANKI_STATS_UI_VALUE(Second, gpuFrameTime, "Total frame", ValueFlag::kAverage | ValueFlag::kSeconds)
+ANKI_STATS_UI_VALUE(U64, gpuActiveCycles, "GPU cycles", ValueFlag::kAverage)
+ANKI_STATS_UI_VALUE(U64, gpuReadBandwidth, "Read bandwidth", ValueFlag::kAverage | ValueFlag::kBytes)
+ANKI_STATS_UI_VALUE(U64, gpuWriteBandwidth, "Write bandwidth", ValueFlag::kAverage | ValueFlag::kBytes)
+
+ANKI_STATS_UI_BEGIN_GROUP("CPU memory")
+ANKI_STATS_UI_VALUE(PtrSize, cpuAllocatedMemory, "Total", ValueFlag::kNone | ValueFlag::kBytes)
+ANKI_STATS_UI_VALUE(PtrSize, cpuAllocationCount, "Number of allocations", ValueFlag::kNone)
+ANKI_STATS_UI_VALUE(PtrSize, cpuFreeCount, "Number of frees", ValueFlag::kNone)
+
+ANKI_STATS_UI_BEGIN_GROUP("GPU memory")
+ANKI_STATS_UI_VALUE(PtrSize, gpuDeviceMemoryAllocated, "Really allocated", ValueFlag::kNone | ValueFlag::kBytes)
+ANKI_STATS_UI_VALUE(PtrSize, gpuDeviceMemoryInUse, "Used", ValueFlag::kNone | ValueFlag::kBytes)
+ANKI_STATS_UI_VALUE(PtrSize, globalVertexAllocated, "Vertex really allocated", ValueFlag::kNone | ValueFlag::kBytes)
+ANKI_STATS_UI_VALUE(PtrSize, globalVertexUsed, "Vertex used", ValueFlag::kNone | ValueFlag::kBytes)
+ANKI_STATS_UI_VALUE(F32, globalVertexExternalFragmentation, "Vertex external fragmentation", ValueFlag::kNone)
+
+ANKI_STATS_UI_BEGIN_GROUP("Other")
+ANKI_STATS_UI_VALUE(U32, drawableCount, "Render queue drawbles", ValueFlag::kNone)
+ANKI_STATS_UI_VALUE(U32, vkCommandBufferCount, "VK command buffers", ValueFlag::kNone)

+ 71 - 110
AnKi/Core/StatsUi.h

@@ -15,6 +15,24 @@ namespace anki {
 /// @addtogroup core
 /// @{
 
+/// @memberof StatsUi
+class StatsUiInput
+{
+public:
+#define ANKI_STATS_UI_BEGIN_GROUP(x)
+#define ANKI_STATS_UI_VALUE(type, name, text, flags) type m_##name = {};
+#include <AnKi/Core/StatsUi.defs.h>
+#undef ANKI_STATS_UI_BEGIN_GROUP
+#undef ANKI_STATS_UI_VALUE
+};
+
+/// @memberof StatsUi
+enum class StatsUiDetail : U8
+{
+	kDetailed,
+	kFpsOnly
+};
+
 /// UI for displaying on-screen stats.
 class StatsUi : public UiImmediateModeBuilder
 {
@@ -30,136 +48,79 @@ public:
 
 	void build(CanvasPtr ctx) override;
 
-	void setFrameTime(Second v)
-	{
-		m_frameTime.set(v);
-	}
-
-	void setRenderTime(Second v)
-	{
-		m_renderTime.set(v);
-	}
-
-	void setSceneUpdateTime(Second v)
-	{
-		m_sceneUpdateTime.set(v);
-	}
-
-	void setVisibilityTestsTime(Second v)
-	{
-		m_visTestsTime.set(v);
-	}
-
-	void setPhysicsTime(Second v)
-	{
-		m_physicsTime.set(v);
-	}
+	void setStats(const StatsUiInput& input, StatsUiDetail detail);
 
-	void setGpuTime(Second v)
-	{
-		m_gpuTime.set(v);
-	}
-
-	void setGpuActiveCycles(U64 v)
-	{
-		m_gpuActive.set(v);
-	}
-
-	void setGpuReadBandwidth(PtrSize v)
-	{
-		m_gpuReadBandwidth.set(v);
-	}
-
-	void setGpuWriteBandwidth(PtrSize v)
-	{
-		m_gpuWriteBandwidth.set(v);
-	}
-
-	void setAllocatedCpuMemory(PtrSize v)
-	{
-		m_allocatedCpuMem = v;
-	}
-
-	void setCpuAllocationCount(U64 v)
-	{
-		m_allocCount = v;
-	}
-
-	void setCpuFreeCount(U64 v)
-	{
-		m_freeCount = v;
-	}
+private:
+	static constexpr U32 kBufferedFrames = 16;
 
-	void setGrStats(const GrManagerStats& stats)
+	enum class ValueFlag : U8
 	{
-		m_grStats = stats;
-	}
+		kNone = 0,
+		kAverage = 1 << 0,
+		kSeconds = 1 << 1,
+		kBytes = 1 << 2,
+	};
+	ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS_FRIEND(ValueFlag)
 
-	void setDrawableCount(U64 v)
+	class Value
 	{
-		m_drawableCount = v;
-	}
+	public:
+		union
+		{
+			U64 m_int = 0;
+			F64 m_float;
+			F64 m_avg;
+		};
 
-	void setGlobalVertexMemoryPoolStats(const BuddyAllocatorBuilderStats& stats)
-	{
-		m_globalVertexPoolStats = stats;
-	}
+		F64 m_rollingAvg = 0.0;
 
-private:
-	static constexpr U32 BUFFERED_FRAMES = 16;
+		template<typename T, ANKI_ENABLE(std::is_floating_point<T>::value)>
+		void update(T x, ValueFlag flags, Bool flush)
+		{
+			if(!!(flags & ValueFlag::kAverage))
+			{
+				setAverage(x, flush);
+			}
+			else
+			{
+				m_float = x;
+			}
+		}
 
-	template<typename T>
-	class BufferedValue
-	{
-	public:
-		void set(T x)
+		template<typename T, ANKI_ENABLE(std::is_integral<T>::value)>
+		void update(T x, ValueFlag flags, Bool flush)
 		{
-			m_rollongAvg += x / T(BUFFERED_FRAMES);
+			if(!!(flags & ValueFlag::kAverage))
+			{
+				setAverage(F64(x), flush);
+			}
+			else
+			{
+				m_int = x;
+			}
 		}
 
-		T get(Bool flush)
+		void setAverage(F64 x, Bool flush)
 		{
+			m_rollingAvg += x / F64(kBufferedFrames);
+
 			if(flush)
 			{
-				m_avg = m_rollongAvg;
-				m_rollongAvg = T(0);
+				m_avg = m_rollingAvg;
+				m_rollingAvg = 0.0;
 			}
-
-			return m_avg;
 		}
-
-	private:
-		T m_rollongAvg = T(0);
-		T m_avg = T(0);
 	};
 
+#define ANKI_STATS_UI_BEGIN_GROUP(x)
+#define ANKI_STATS_UI_VALUE(type, name, text, flags) Value m_##name;
+#include <AnKi/Core/StatsUi.defs.h>
+#undef ANKI_STATS_UI_BEGIN_GROUP
+#undef ANKI_STATS_UI_VALUE
+
 	FontPtr m_font;
 	U32 m_bufferedFrames = 0;
-
-	// CPU
-	BufferedValue<Second> m_frameTime;
-	BufferedValue<Second> m_renderTime;
-	BufferedValue<Second> m_sceneUpdateTime;
-	BufferedValue<Second> m_visTestsTime;
-	BufferedValue<Second> m_physicsTime;
-
-	// GPU
-	BufferedValue<Second> m_gpuTime;
-	BufferedValue<U64> m_gpuActive;
-	BufferedValue<PtrSize> m_gpuReadBandwidth;
-	BufferedValue<PtrSize> m_gpuWriteBandwidth;
-
-	// Memory
-	PtrSize m_allocatedCpuMem = 0;
-	U64 m_allocCount = 0;
-	U64 m_freeCount = 0;
-	BuddyAllocatorBuilderStats m_globalVertexPoolStats = {};
-
-	// GR
-	GrManagerStats m_grStats = {};
-
-	// Other
-	PtrSize m_drawableCount = 0;
+	StatsUiDetail m_detail = StatsUiDetail::kDetailed;
 
 	static void labelTime(Second val, CString name)
 	{

+ 4 - 4
AnKi/Gr/Common.h

@@ -32,10 +32,10 @@ class GrUpscalerInitInfo;
 /// @addtogroup graphics
 /// @{
 
-#define ANKI_GR_LOGI(...) ANKI_LOG("GR  ", NORMAL, __VA_ARGS__)
-#define ANKI_GR_LOGE(...) ANKI_LOG("GR  ", ERROR, __VA_ARGS__)
-#define ANKI_GR_LOGW(...) ANKI_LOG("GR  ", WARNING, __VA_ARGS__)
-#define ANKI_GR_LOGF(...) ANKI_LOG("GR  ", FATAL, __VA_ARGS__)
+#define ANKI_GR_LOGI(...) ANKI_LOG("GR", NORMAL, __VA_ARGS__)
+#define ANKI_GR_LOGE(...) ANKI_LOG("GR", ERROR, __VA_ARGS__)
+#define ANKI_GR_LOGW(...) ANKI_LOG("GR", WARNING, __VA_ARGS__)
+#define ANKI_GR_LOGF(...) ANKI_LOG("GR", FATAL, __VA_ARGS__)
 
 // Some constants
 constexpr U32 MAX_VERTEX_ATTRIBUTES = 8;

+ 5 - 5
AnKi/Gr/Vulkan/Common.h

@@ -36,11 +36,11 @@ class GrManagerImpl;
 /// @addtogroup vulkan
 /// @{
 
-#define ANKI_VK_LOGI(...) ANKI_LOG("VK  ", NORMAL, __VA_ARGS__)
-#define ANKI_VK_LOGE(...) ANKI_LOG("VK  ", ERROR, __VA_ARGS__)
-#define ANKI_VK_LOGW(...) ANKI_LOG("VK  ", WARNING, __VA_ARGS__)
-#define ANKI_VK_LOGF(...) ANKI_LOG("VK  ", FATAL, __VA_ARGS__)
-#define ANKI_VK_LOGV(...) ANKI_LOG("VK  ", VERBOSE, __VA_ARGS__)
+#define ANKI_VK_LOGI(...) ANKI_LOG("VK", NORMAL, __VA_ARGS__)
+#define ANKI_VK_LOGE(...) ANKI_LOG("VK", ERROR, __VA_ARGS__)
+#define ANKI_VK_LOGW(...) ANKI_LOG("VK", WARNING, __VA_ARGS__)
+#define ANKI_VK_LOGF(...) ANKI_LOG("VK", FATAL, __VA_ARGS__)
+#define ANKI_VK_LOGV(...) ANKI_LOG("VK", VERBOSE, __VA_ARGS__)
 
 #define ANKI_VK_SELF(class_) class_& self = *static_cast<class_*>(this)
 #define ANKI_VK_SELF_CONST(class_) const class_& self = *static_cast<const class_*>(this)

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

@@ -682,7 +682,7 @@ Error GrManagerImpl::initDevice(const GrManagerInitInfo& init)
 				extensionsToEnable[extensionsToEnableCount++] = extensionName.cstr();
 			}
 			else if(extensionName == VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME
-					&& m_config->getCoreDisplayStats())
+					&& m_config->getCoreDisplayStats() > 1)
 			{
 				m_extensions |= VulkanExtensions::KHR_PIPELINE_EXECUTABLE_PROPERTIES;
 				extensionsToEnable[extensionsToEnableCount++] = extensionName.cstr();

+ 1 - 0
AnKi/Importer/GltfImporter.cpp

@@ -173,6 +173,7 @@ Error GltfImporter::init(const GltfImporterInitInfo& initInfo)
 	m_rpath.create(initInfo.m_rpath);
 	m_texrpath.create(initInfo.m_texrpath);
 	m_optimizeMeshes = initInfo.m_optimizeMeshes;
+	m_optimizeAnimations = initInfo.m_optimizeAnimations;
 	m_comment.create(initInfo.m_comment);
 
 	m_lightIntensityScale = max(initInfo.m_lightIntensityScale, EPSILON);

+ 2 - 0
AnKi/Importer/GltfImporter.h

@@ -28,6 +28,7 @@ public:
 	CString m_rpath;
 	CString m_texrpath;
 	Bool m_optimizeMeshes = true;
+	Bool m_optimizeAnimations = true;
 	F32 m_lodFactor = 1.0f;
 	U32 m_lodCount = 1;
 	F32 m_lightIntensityScale = 1.0f;
@@ -83,6 +84,7 @@ private:
 	U32 m_lodCount = 1;
 	F32 m_lightIntensityScale = 1.0f;
 	Bool m_optimizeMeshes = false;
+	Bool m_optimizeAnimations = false;
 	StringAuto m_comment{m_alloc};
 
 	/// Don't generate LODs for meshes with less vertices than this number.

+ 78 - 56
AnKi/Importer/GltfImporterAnimation.cpp

@@ -38,50 +38,69 @@ template<typename T, typename TZeroFunc, typename TLerpFunc>
 static void optimizeChannel(DynamicArrayAuto<GltfAnimKey<T>>& arr, const T& identity, TZeroFunc isZeroFunc,
 							TLerpFunc lerpFunc)
 {
-	if(arr.getSize() < 3)
-	{
-		return;
-	}
+	constexpr F32 kMinSkippedToTotalRatio = 0.1f;
 
-	DynamicArrayAuto<GltfAnimKey<T>> newArr(arr.getAllocator());
-	newArr.emplaceBack(arr.getFront());
-	for(U32 i = 1; i < arr.getSize() - 1; ++i)
+	U32 iterationCount = 0;
+	while(true)
 	{
-		const GltfAnimKey<T>& left = arr[i - 1];
-		const GltfAnimKey<T>& middle = arr[i];
-		const GltfAnimKey<T>& right = arr[i + 1];
-
-		if(left.m_value == middle.m_value && middle.m_value == right.m_value)
+		if(arr.getSize() < 3)
 		{
-			// Skip it
+			break;
 		}
-		else
+
+		DynamicArrayAuto<GltfAnimKey<T>> newArr(arr.getAllocator());
+		for(U32 i = 0; i < arr.getSize() - 2; i += 2)
 		{
-			const F32 factor = F32((middle.m_time - left.m_time) / (right.m_time - left.m_time));
-			ANKI_ASSERT(factor > 0.0f && factor < 1.0f);
-			const T lerpRez = lerpFunc(left.m_value, right.m_value, factor);
-			if(isZeroFunc(middle.m_value - lerpRez))
+			const GltfAnimKey<T>& left = arr[i];
+			const GltfAnimKey<T>& middle = arr[i + 1];
+			const GltfAnimKey<T>& right = arr[i + 2];
+
+			newArr.emplaceBack(left);
+
+			if(left.m_value == middle.m_value && middle.m_value == right.m_value)
 			{
-				// It's redundant, skip it
+				// Skip it
 			}
 			else
 			{
-				newArr.emplaceBack(middle);
+				const F32 factor = F32((middle.m_time - left.m_time) / (right.m_time - left.m_time));
+				ANKI_ASSERT(factor > 0.0f && factor < 1.0f);
+				const T lerpRez = lerpFunc(left.m_value, right.m_value, factor);
+				if(isZeroFunc(middle.m_value - lerpRez))
+				{
+					// It's redundant, skip it
+				}
+				else
+				{
+					newArr.emplaceBack(middle);
+				}
 			}
+
+			newArr.emplaceBack(right);
 		}
-	}
-	newArr.emplaceBack(arr.getBack());
-	ANKI_ASSERT(newArr.getSize() <= arr.getSize());
+		ANKI_ASSERT(newArr.getSize() <= arr.getSize());
 
-	// Check if identity
-	if(newArr.getSize() == 2 && isZeroFunc(newArr[0].m_value - newArr[1].m_value)
-	   && isZeroFunc(newArr[0].m_value - identity))
-	{
-		newArr.destroy();
+		// Check if identity
+		if(newArr.getSize() == 2 && isZeroFunc(newArr[0].m_value - newArr[1].m_value)
+		   && isZeroFunc(newArr[0].m_value - identity))
+		{
+			newArr.destroy();
+		}
+
+		const F32 skippedToTotalRatio = 1.0f - F32(newArr.getSize()) / F32(arr.getSize());
+
+		arr.destroy();
+		arr = std::move(newArr);
+
+		++iterationCount;
+
+		if(skippedToTotalRatio <= kMinSkippedToTotalRatio)
+		{
+			break;
+		}
 	}
 
-	arr.destroy();
-	arr = std::move(newArr);
+	ANKI_IMPORTER_LOGV("Channel optimization iteration count: %u", iterationCount);
 }
 
 Error GltfImporter::writeAnimation(const cgltf_animation& anim)
@@ -239,33 +258,36 @@ Error GltfImporter::writeAnimation(const cgltf_animation& anim)
 	}
 
 	// Optimize animation
-	constexpr F32 KILL_EPSILON = 0.001f; // 1 millimiter
-	for(GltfAnimChannel& channel : tempChannels)
+	if(m_optimizeAnimations)
 	{
-		optimizeChannel(
-			channel.m_positions, Vec3(0.0f),
-			[&](const Vec3& a) -> Bool {
-				return a.abs() < KILL_EPSILON;
-			},
-			[&](const Vec3& a, const Vec3& b, F32 u) -> Vec3 {
-				return linearInterpolate(a, b, u);
-			});
-		optimizeChannel(
-			channel.m_rotations, Quat::getIdentity(),
-			[&](const Quat& a) -> Bool {
-				return a.abs() < Quat(EPSILON * 20.0f);
-			},
-			[&](const Quat& a, const Quat& b, F32 u) -> Quat {
-				return a.slerp(b, u);
-			});
-		optimizeChannel(
-			channel.m_scales, 1.0f,
-			[&](const F32& a) -> Bool {
-				return absolute(a) < KILL_EPSILON;
-			},
-			[&](const F32& a, const F32& b, F32 u) -> F32 {
-				return linearInterpolate(a, b, u);
-			});
+		constexpr F32 kKillEpsilon = 1.0_cm;
+		for(GltfAnimChannel& channel : tempChannels)
+		{
+			optimizeChannel(
+				channel.m_positions, Vec3(0.0f),
+				[&](const Vec3& a) -> Bool {
+					return a.abs() < kKillEpsilon;
+				},
+				[&](const Vec3& a, const Vec3& b, F32 u) -> Vec3 {
+					return linearInterpolate(a, b, u);
+				});
+			optimizeChannel(
+				channel.m_rotations, Quat::getIdentity(),
+				[&](const Quat& a) -> Bool {
+					return a.abs() < Quat(EPSILON * 20.0f);
+				},
+				[&](const Quat& a, const Quat& b, F32 u) -> Quat {
+					return a.slerp(b, u);
+				});
+			optimizeChannel(
+				channel.m_scales, 1.0f,
+				[&](const F32& a) -> Bool {
+					return absolute(a) < kKillEpsilon;
+				},
+				[&](const F32& a, const F32& b, F32 u) -> F32 {
+					return linearInterpolate(a, b, u);
+				});
+		}
 	}
 
 	// Write file

+ 1 - 1
AnKi/Math/Functions.h

@@ -18,7 +18,7 @@ namespace anki {
 constexpr F32 PI = 3.14159265358979323846f;
 
 /// Floating point epsilon.
-const F32 EPSILON = 1.0e-6f;
+constexpr F32 EPSILON = 1.0e-6f;
 
 template<typename T>
 inline T sin(const T rad)

+ 5 - 5
AnKi/Renderer/Common.h

@@ -13,11 +13,11 @@
 
 namespace anki {
 
-#define ANKI_R_LOGI(...) ANKI_LOG("R   ", NORMAL, __VA_ARGS__)
-#define ANKI_R_LOGE(...) ANKI_LOG("R   ", ERROR, __VA_ARGS__)
-#define ANKI_R_LOGW(...) ANKI_LOG("R   ", WARNING, __VA_ARGS__)
-#define ANKI_R_LOGF(...) ANKI_LOG("R   ", FATAL, __VA_ARGS__)
-#define ANKI_R_LOGV(...) ANKI_LOG("R   ", VERBOSE, __VA_ARGS__)
+#define ANKI_R_LOGI(...) ANKI_LOG("REND", NORMAL, __VA_ARGS__)
+#define ANKI_R_LOGE(...) ANKI_LOG("REND", ERROR, __VA_ARGS__)
+#define ANKI_R_LOGW(...) ANKI_LOG("REND", WARNING, __VA_ARGS__)
+#define ANKI_R_LOGF(...) ANKI_LOG("REND", FATAL, __VA_ARGS__)
+#define ANKI_R_LOGV(...) ANKI_LOG("REND", VERBOSE, __VA_ARGS__)
 
 // Forward
 #define ANKI_RENDERER_OBJECT_DEF(a, b) class a;

+ 1 - 1
AnKi/Renderer/ConfigVars.defs.h

@@ -74,7 +74,7 @@ ANKI_CONFIG_VAR_U32(RProbeRefectionMaxCachedProbes, 32, 4, 256, "Max cached numb
 ANKI_CONFIG_VAR_U32(RProbeReflectionShadowMapResolution, 64, 4, 2048, "Reflection probe shadow resolution")
 
 // Final composite
-ANKI_CONFIG_VAR_U32(RMotionBlurSamples, 32, 1, 2048, "Max motion blur samples")
+ANKI_CONFIG_VAR_U32(RMotionBlurSamples, 32, 0, 2048, "Max motion blur samples")
 ANKI_CONFIG_VAR_F32(RFilmGrainStrength, 16.0f, 0.0f, 250.0f, "Film grain strength")
 
 // Lens flare

+ 3 - 5
AnKi/Renderer/MainRenderer.cpp

@@ -30,8 +30,6 @@ MainRenderer::~MainRenderer()
 
 Error MainRenderer::init(const MainRendererInitInfo& inf)
 {
-	ANKI_R_LOGI("Initializing main renderer");
-
 	m_alloc = HeapAllocator<U8>(inf.m_allocCallback, inf.m_allocCallbackUserData, "MainRenderer");
 	m_frameAlloc = StackAllocator<U8>(inf.m_allocCallback, inf.m_allocCallbackUserData, 10_MB, 1.0f);
 
@@ -39,6 +37,9 @@ Error MainRenderer::init(const MainRendererInitInfo& inf)
 	m_swapchainResolution = inf.m_swapchainSize;
 	m_rDrawToDefaultFb = inf.m_config->getRRenderScaling() == 1.0f;
 
+	ANKI_R_LOGI("Initializing main renderer. Swapchain resolution %ux%u", m_swapchainResolution.x(),
+				m_swapchainResolution.y());
+
 	m_r.reset(m_alloc.newInstance<Renderer>());
 	ANKI_CHECK(m_r->init(inf.m_threadHive, inf.m_resourceManager, inf.m_gr, inf.m_stagingMemory, inf.m_ui, m_alloc,
 						 inf.m_config, inf.m_globTimestamp, m_swapchainResolution));
@@ -71,9 +72,6 @@ Error MainRenderer::init(const MainRendererInitInfo& inf)
 
 	m_rgraph = inf.m_gr->newRenderGraph();
 
-	ANKI_R_LOGI("Main renderer initialized. Swapchain resolution %ux%u", m_swapchainResolution.x(),
-				m_swapchainResolution.y());
-
 	return Error::NONE;
 }
 

+ 2 - 2
AnKi/Renderer/RenderQueue.cpp

@@ -7,9 +7,9 @@
 
 namespace anki {
 
-PtrSize RenderQueue::countAllRenderables() const
+U32 RenderQueue::countAllRenderables() const
 {
-	PtrSize drawableCount = 0;
+	U32 drawableCount = 0;
 	drawableCount += m_earlyZRenderables.getSize();
 	drawableCount += m_renderables.getSize();
 	drawableCount += m_forwardShadingRenderables.getSize();

+ 1 - 1
AnKi/Renderer/RenderQueue.h

@@ -436,7 +436,7 @@ public:
 		zeroMemory(m_skybox);
 	}
 
-	PtrSize countAllRenderables() const;
+	U32 countAllRenderables() const;
 };
 
 static_assert(std::is_trivially_destructible<RenderQueue>::value == true, "Should be trivially destructible");

+ 4 - 4
AnKi/Ui/Common.h

@@ -21,10 +21,10 @@ class UiManager;
 /// @addtogroup ui
 /// @{
 
-#define ANKI_UI_LOGI(...) ANKI_LOG("UI  ", NORMAL, __VA_ARGS__)
-#define ANKI_UI_LOGE(...) ANKI_LOG("UI  ", ERROR, __VA_ARGS__)
-#define ANKI_UI_LOGW(...) ANKI_LOG("UI  ", WARNING, __VA_ARGS__)
-#define ANKI_UI_LOGF(...) ANKI_LOG("UI  ", FATAL, __VA_ARGS__)
+#define ANKI_UI_LOGI(...) ANKI_LOG("UI", NORMAL, __VA_ARGS__)
+#define ANKI_UI_LOGE(...) ANKI_LOG("UI", ERROR, __VA_ARGS__)
+#define ANKI_UI_LOGW(...) ANKI_LOG("UI", WARNING, __VA_ARGS__)
+#define ANKI_UI_LOGF(...) ANKI_LOG("UI", FATAL, __VA_ARGS__)
 
 using UiAllocator = HeapAllocator<U8>;
 

+ 4 - 3
AnKi/Util/Logger.cpp

@@ -186,7 +186,7 @@ void Logger::defaultSystemMessageHandler(void*, const LoggerMessageInfo& info)
 			endTerminalColor);
 #elif ANKI_OS_WINDOWS
 	WORD attribs = 0;
-	FILE* out = NULL;
+	FILE* out = nullptr;
 	switch(info.m_type)
 	{
 	case LoggerMessageType::NORMAL:
@@ -211,6 +211,7 @@ void Logger::defaultSystemMessageHandler(void*, const LoggerMessageInfo& info)
 		break;
 	default:
 		ANKI_ASSERT(0);
+		out = stdout;
 	}
 
 	HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
@@ -228,8 +229,8 @@ void Logger::defaultSystemMessageHandler(void*, const LoggerMessageInfo& info)
 
 		// Print
 		static_assert(Thread::kThreadNameMaxLength == 15, "See file");
-		fprintf(out, "[%s][%s][%-15s] %s (%s:%d %s)\n", kMessageTypeTxt[info.m_type],
-				info.m_subsystem ? info.m_subsystem : "N/A ", info.m_threadName, info.m_msg, info.m_file, info.m_line,
+		fprintf(out, "[%s][%-4s][%-15s] %s (%s:%d %s)\n", kMessageTypeTxt[info.m_type],
+				info.m_subsystem ? info.m_subsystem : "N/A", info.m_threadName, info.m_msg, info.m_file, info.m_line,
 				info.m_func);
 
 		// Restore state

+ 26 - 8
Tools/GltfImporter/Main.cpp

@@ -9,14 +9,15 @@ using namespace anki;
 
 static const char* USAGE = R"(Usage: %s in_file out_dir [options]
 Options:
--rpath <string>        : Replace all absolute paths of assets with that path
--texrpath <string>     : Same as rpath but for textures
--optimize-meshes <0|1> : Optimize meshes. Default is 1
--j <thread_count>      : Number of threads. Defaults to system's max
--lod-count <1|2|3>     : The number of geometry LODs to generate. Default: 1
--lod-factor <float>    : The decimate factor for each LOD. Default 0.25
--light-scale <float>   : Multiply the light intensity with this number. Default 1.0
--v                     : Enable verbose log
+-rpath <string>            : Replace all absolute paths of assets with that path
+-texrpath <string>         : Same as rpath but for textures
+-optimize-meshes <0|1>     : Optimize meshes. Default is 1
+-optimize-animations <0|1> : Optimize animations. Default is 1
+-j <thread_count>          : Number of threads. Defaults to system's max
+-lod-count <1|2|3>         : The number of geometry LODs to generate. Default: 1
+-lod-factor <float>        : The decimate factor for each LOD. Default 0.25
+-light-scale <float>       : Multiply the light intensity with this number. Default 1.0
+-v                         : Enable verbose log
 )";
 
 class CmdLineArgs
@@ -28,6 +29,7 @@ public:
 	StringAuto m_rpath = {m_alloc};
 	StringAuto m_texRpath = {m_alloc};
 	Bool m_optimizeMeshes = true;
+	Bool m_optimizeAnimations = true;
 	U32 m_threadCount = MAX_U32;
 	U32 m_lodCount = 1;
 	F32 m_lodFactor = 0.25f;
@@ -165,6 +167,21 @@ static Error parseCommandLineArgs(int argc, char** argv, CmdLineArgs& info)
 				return Error::USER_DATA;
 			}
 		}
+		else if(strcmp(argv[i], "-optimize-animations") == 0)
+		{
+			++i;
+
+			if(i < argc)
+			{
+				I optimize = 1;
+				ANKI_CHECK(CString(argv[i]).toNumber(optimize));
+				info.m_optimizeAnimations = optimize != 0;
+			}
+			else
+			{
+				return Error::USER_DATA;
+			}
+		}
 		else
 		{
 			return Error::USER_DATA;
@@ -219,6 +236,7 @@ int myMain(int argc, char** argv)
 	initInfo.m_rpath = cmdArgs.m_rpath;
 	initInfo.m_texrpath = cmdArgs.m_texRpath;
 	initInfo.m_optimizeMeshes = cmdArgs.m_optimizeMeshes;
+	initInfo.m_optimizeAnimations = cmdArgs.m_optimizeAnimations;
 	initInfo.m_lodFactor = cmdArgs.m_lodFactor;
 	initInfo.m_lodCount = cmdArgs.m_lodCount;
 	initInfo.m_lightIntensityScale = cmdArgs.m_lightIntensityScale;