Browse Source

Almost feature complete

Panagiotis Christopoulos Charitos 6 years ago
parent
commit
84469ab10c
42 changed files with 326 additions and 1115 deletions
  1. 1 1
      samples/common/Framework.cpp
  2. 2 2
      sandbox/Main.cpp
  3. 1 1
      src/anki/Core.h
  4. 75 80
      src/anki/core/App.cpp
  5. 4 0
      src/anki/core/App.h
  6. 16 16
      src/anki/core/CoreTracer.cpp
  7. 1 1
      src/anki/core/CoreTracer.h
  8. 1 1
      src/anki/core/StagingGpuMemoryManager.cpp
  9. 1 0
      src/anki/core/StagingGpuMemoryManager.h
  10. 0 119
      src/anki/core/Trace.h
  11. 1 1
      src/anki/gr/RenderGraph.cpp
  12. 2 2
      src/anki/gr/ShaderCompiler.cpp
  13. 1 1
      src/anki/gr/vulkan/CommandBufferFactory.cpp
  14. 1 1
      src/anki/gr/vulkan/CommandBufferImpl.inl.h
  15. 1 1
      src/anki/gr/vulkan/DescriptorSet.cpp
  16. 1 1
      src/anki/gr/vulkan/FenceFactory.inl.h
  17. 1 0
      src/anki/gr/vulkan/GrManagerImpl.h
  18. 1 1
      src/anki/gr/vulkan/Pipeline.cpp
  19. 1 1
      src/anki/gr/vulkan/SemaphoreFactory.inl.h
  20. 1 1
      src/anki/renderer/ClusterBin.cpp
  21. 1 1
      src/anki/renderer/Drawer.cpp
  22. 1 1
      src/anki/renderer/GBuffer.cpp
  23. 1 1
      src/anki/renderer/GlobalIllumination.cpp
  24. 1 2
      src/anki/renderer/MainRenderer.cpp
  25. 1 1
      src/anki/renderer/ProbeReflections.cpp
  26. 1 1
      src/anki/renderer/Renderer.cpp
  27. 1 2
      src/anki/renderer/ShadowMapping.cpp
  28. 1 1
      src/anki/renderer/UiStage.cpp
  29. 1 1
      src/anki/resource/AsyncLoader.cpp
  30. 1 1
      src/anki/resource/ResourceFilesystem.cpp
  31. 1 1
      src/anki/resource/TransferGpuAllocator.cpp
  32. 1 1
      src/anki/scene/Octree.h
  33. 1 1
      src/anki/scene/SceneGraph.cpp
  34. 1 1
      src/anki/scene/SoftwareRasterizer.cpp
  35. 1 1
      src/anki/scene/VisibilityInternal.h
  36. 1 1
      src/anki/script/LuaBinder.cpp
  37. 1 1
      src/anki/util/CMakeLists.txt
  38. 70 482
      src/anki/util/Tracer.cpp
  39. 98 44
      src/anki/util/Tracer.h
  40. 0 159
      src/anki/util/Tracer2.cpp
  41. 0 155
      src/anki/util/Tracer2.h
  42. 28 25
      tests/util/Tracer.cpp

+ 1 - 1
samples/common/Framework.cpp

@@ -162,7 +162,7 @@ Error SampleApp::userMainLoop(Bool& quit)
 
 
 		if(in.getKey(KeyCode::F12) == 1)
 		if(in.getKey(KeyCode::F12) == 1)
 		{
 		{
-			CoreTracerSingleton::get().m_enabled = !CoreTracerSingleton::get().m_enabled;
+			TracerSingleton::get().setEnabled(!TracerSingleton::get().getEnabled());
 		}
 		}
 
 
 		if(in.getMousePosition() != Vec2(0.0))
 		if(in.getMousePosition() != Vec2(0.0))

+ 2 - 2
sandbox/Main.cpp

@@ -52,7 +52,7 @@ Error MyApp::init(int argc, char* argv[])
 	{
 	{
 		m_profile = true;
 		m_profile = true;
 		setTimerTick(0.0);
 		setTimerTick(0.0);
-		CoreTracerSingleton::get().m_enabled = true;
+		TracerSingleton::get().setEnabled(true);
 	}
 	}
 
 
 // Input
 // Input
@@ -165,7 +165,7 @@ Error MyApp::userMainLoop(Bool& quit)
 
 
 	if(in.getKey(KeyCode::F12) == 1)
 	if(in.getKey(KeyCode::F12) == 1)
 	{
 	{
-		CoreTracerSingleton::get().m_enabled = !CoreTracerSingleton::get().m_enabled;
+		TracerSingleton::get().setEnabled(!TracerSingleton::get().getEnabled());
 	}
 	}
 
 
 #if !PLAYER
 #if !PLAYER

+ 1 - 1
src/anki/Core.h

@@ -8,4 +8,4 @@
 #include <anki/core/App.h>
 #include <anki/core/App.h>
 #include <anki/core/Config.h>
 #include <anki/core/Config.h>
 #include <anki/core/NativeWindow.h>
 #include <anki/core/NativeWindow.h>
-#include <anki/core/Trace.h>
+#include <anki/core/CoreTracer.h>

+ 75 - 80
src/anki/core/App.cpp

@@ -10,7 +10,8 @@
 #include <anki/util/Filesystem.h>
 #include <anki/util/Filesystem.h>
 #include <anki/util/System.h>
 #include <anki/util/System.h>
 #include <anki/util/ThreadHive.h>
 #include <anki/util/ThreadHive.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
+#include <anki/core/CoreTracer.h>
 #include <anki/core/DeveloperConsole.h>
 #include <anki/core/DeveloperConsole.h>
 #include <anki/core/NativeWindow.h>
 #include <anki/core/NativeWindow.h>
 #include <anki/input/Input.h>
 #include <anki/input/Input.h>
@@ -284,17 +285,7 @@ void App::cleanup()
 	m_heapAlloc.deleteInstance(m_window);
 	m_heapAlloc.deleteInstance(m_window);
 
 
 #if ANKI_ENABLE_TRACE
 #if ANKI_ENABLE_TRACE
-	if(CoreTracerSingleton::get().isInitialized())
-	{
-		StringAuto fname(m_heapAlloc);
-		fname.sprintf("%s/trace", m_settingsDir.cstr());
-		ANKI_CORE_LOGI("Will dump trace files: %s", fname.cstr());
-		if(CoreTracerSingleton::get().flush(fname.toCString()))
-		{
-			ANKI_CORE_LOGE("Ignoring error from the tracer");
-		}
-		CoreTracerSingleton::destroy();
-	}
+	m_heapAlloc.deleteInstance(m_coreTracer);
 #endif
 #endif
 
 
 	m_settingsDir.destroy(m_heapAlloc);
 	m_settingsDir.destroy(m_heapAlloc);
@@ -321,11 +312,6 @@ Error App::initInternal(const ConfigSet& config_, AllocAlignedCallback allocCb,
 	initMemoryCallbacks(allocCb, allocCbUserData);
 	initMemoryCallbacks(allocCb, allocCbUserData);
 	m_heapAlloc = HeapAllocator<U8>(m_allocCb, m_allocCbData);
 	m_heapAlloc = HeapAllocator<U8>(m_allocCb, m_allocCbData);
 
 
-#if ANKI_ENABLE_TRACE
-	CoreTracerSingleton::get().init(m_heapAlloc);
-	CoreTracerSingleton::get().newFrame(0);
-#endif
-
 	ANKI_CHECK(initDirs(config));
 	ANKI_CHECK(initDirs(config));
 
 
 	// Print a message
 	// Print a message
@@ -372,6 +358,14 @@ Error App::initInternal(const ConfigSet& config_, AllocAlignedCallback allocCb,
 
 
 	ANKI_CORE_LOGI("Number of main threads: %u", U(config.getNumberU32("core.mainThreadCount")));
 	ANKI_CORE_LOGI("Number of main threads: %u", U(config.getNumberU32("core.mainThreadCount")));
 
 
+	//
+	// Core tracer
+	//
+#if ANKI_ENABLE_TRACE
+	m_coreTracer = m_heapAlloc.newInstance<CoreTracer>();
+	ANKI_CHECK(m_coreTracer->init(m_heapAlloc, m_settingsDir));
+#endif
+
 	//
 	//
 	// Window
 	// Window
 	//
 	//
@@ -561,85 +555,86 @@ Error App::mainLoop()
 
 
 	while(!quit)
 	while(!quit)
 	{
 	{
-#if ANKI_ENABLE_TRACE
-		static U64 frame = 1;
-		CoreTracerSingleton::get().newFrame(frame++);
-#endif
-		ANKI_TRACE_START_EVENT(FRAME);
-		const Second startTime = HighRezTimer::getCurrentTime();
+		{
+			ANKI_TRACE_SCOPED_EVENT(FRAME);
+			const Second startTime = HighRezTimer::getCurrentTime();
 
 
-		prevUpdateTime = crntTime;
-		crntTime = HighRezTimer::getCurrentTime();
+			prevUpdateTime = crntTime;
+			crntTime = HighRezTimer::getCurrentTime();
 
 
-		// Update
-		ANKI_CHECK(m_input->handleEvents());
+			// Update
+			ANKI_CHECK(m_input->handleEvents());
 
 
-		// User update
-		ANKI_CHECK(userMainLoop(quit));
+			// User update
+			ANKI_CHECK(userMainLoop(quit));
 
 
-		ANKI_CHECK(m_scene->update(prevUpdateTime, crntTime));
+			ANKI_CHECK(m_scene->update(prevUpdateTime, crntTime));
 
 
-		RenderQueue rqueue;
-		m_scene->doVisibilityTests(rqueue);
+			RenderQueue rqueue;
+			m_scene->doVisibilityTests(rqueue);
 
 
-		// Inject stats UI
-		DynamicArrayAuto<UiQueueElement> newUiElementArr(m_heapAlloc);
-		injectUiElements(newUiElementArr, rqueue);
+			// Inject stats UI
+			DynamicArrayAuto<UiQueueElement> newUiElementArr(m_heapAlloc);
+			injectUiElements(newUiElementArr, rqueue);
 
 
-		// Render
-		TexturePtr presentableTex = m_gr->acquireNextPresentableTexture();
-		m_renderer->setStatsEnabled(m_displayStats);
-		ANKI_CHECK(m_renderer->render(rqueue, presentableTex));
+			// Render
+			TexturePtr presentableTex = m_gr->acquireNextPresentableTexture();
+			m_renderer->setStatsEnabled(m_displayStats);
+			ANKI_CHECK(m_renderer->render(rqueue, presentableTex));
 
 
-		// Pause and sync async loader. That will force all tasks before the pause to finish in this frame.
-		m_resources->getAsyncLoader().pause();
+			// Pause and sync async loader. That will force all tasks before the pause to finish in this frame.
+			m_resources->getAsyncLoader().pause();
 
 
-		m_gr->swapBuffers();
-		m_stagingMem->endFrame();
+			m_gr->swapBuffers();
+			m_stagingMem->endFrame();
 
 
-		// Update the trace info with some async loader stats
-		U64 asyncTaskCount = m_resources->getAsyncLoader().getCompletedTaskCount();
-		ANKI_TRACE_INC_COUNTER(RESOURCE_ASYNC_TASKS, asyncTaskCount - m_resourceCompletedAsyncTaskCount);
-		m_resourceCompletedAsyncTaskCount = asyncTaskCount;
+			// Update the trace info with some async loader stats
+			U64 asyncTaskCount = m_resources->getAsyncLoader().getCompletedTaskCount();
+			ANKI_TRACE_INC_COUNTER(RESOURCE_ASYNC_TASKS, asyncTaskCount - m_resourceCompletedAsyncTaskCount);
+			m_resourceCompletedAsyncTaskCount = asyncTaskCount;
 
 
-		// Now resume the loader
-		m_resources->getAsyncLoader().resume();
+			// Now resume the loader
+			m_resources->getAsyncLoader().resume();
 
 
-		ANKI_TRACE_STOP_EVENT(FRAME);
+			// Sleep
+			const Second endTime = HighRezTimer::getCurrentTime();
+			const Second frameTime = endTime - startTime;
+			if(frameTime < m_timerTick)
+			{
+				ANKI_TRACE_SCOPED_EVENT(TIMER_TICK_SLEEP);
+				HighRezTimer::sleep(m_timerTick - frameTime);
+			}
 
 
-		// Sleep
-		const Second endTime = HighRezTimer::getCurrentTime();
-		const Second frameTime = endTime - startTime;
-		if(frameTime < m_timerTick)
-		{
-			ANKI_TRACE_SCOPED_EVENT(TIMER_TICK_SLEEP);
-			HighRezTimer::sleep(m_timerTick - frameTime);
-		}
+			// 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_lightBinTime.set(m_renderer->getStats().m_lightBinTime);
+				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();
+
+				GrManagerStats grStats = m_gr->getStats();
+				statsUi.m_vkCpuMem = grStats.m_cpuMemory;
+				statsUi.m_vkGpuMem = grStats.m_gpuMemory;
+				statsUi.m_vkCmdbCount = grStats.m_commandBufferCount;
+
+				statsUi.m_drawableCount = rqueue.countAllRenderables();
+			}
 
 
-		// 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_lightBinTime.set(m_renderer->getStats().m_lightBinTime);
-			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();
-
-			GrManagerStats grStats = m_gr->getStats();
-			statsUi.m_vkCpuMem = grStats.m_cpuMemory;
-			statsUi.m_vkGpuMem = grStats.m_gpuMemory;
-			statsUi.m_vkCmdbCount = grStats.m_commandBufferCount;
-
-			statsUi.m_drawableCount = rqueue.countAllRenderables();
+			++m_globalTimestamp;
 		}
 		}
 
 
-		++m_globalTimestamp;
+#if ANKI_ENABLE_TRACE
+		static U64 frame = 1;
+		m_coreTracer->flushFrame(frame++);
+#endif
 	}
 	}
 
 
 	return Error::NONE;
 	return Error::NONE;

+ 4 - 0
src/anki/core/App.h

@@ -22,6 +22,7 @@ extern android_app* gAndroidApp;
 #endif
 #endif
 
 
 // Forward
 // Forward
+class CoreTracer;
 class ConfigSet;
 class ConfigSet;
 class ThreadHive;
 class ThreadHive;
 class NativeWindow;
 class NativeWindow;
@@ -166,6 +167,9 @@ private:
 	HeapAllocator<U8> m_heapAlloc;
 	HeapAllocator<U8> m_heapAlloc;
 
 
 	// Sybsystems
 	// Sybsystems
+#if ANKI_ENABLE_TRACE
+	CoreTracer* m_coreTracer = nullptr;
+#endif
 	NativeWindow* m_window = nullptr;
 	NativeWindow* m_window = nullptr;
 	Input* m_input = nullptr;
 	Input* m_input = nullptr;
 	GrManager* m_gr = nullptr;
 	GrManager* m_gr = nullptr;

+ 16 - 16
src/anki/core/CoreTracer.cpp

@@ -5,7 +5,7 @@
 
 
 #include <anki/core/CoreTracer.h>
 #include <anki/core/CoreTracer.h>
 #include <anki/util/DynamicArray.h>
 #include <anki/util/DynamicArray.h>
-#include <anki/util/Tracer2.h>
+#include <anki/util/Tracer.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -32,8 +32,8 @@ static void getSpreadsheetColumnName(U32 column, Array<char, 3>& arr)
 class CoreTracer::ThreadWorkItem : public IntrusiveListEnabled<ThreadWorkItem>
 class CoreTracer::ThreadWorkItem : public IntrusiveListEnabled<ThreadWorkItem>
 {
 {
 public:
 public:
-	DynamicArrayAuto<Tracer2Event> m_events;
-	DynamicArrayAuto<Tracer2Counter> m_counters;
+	DynamicArrayAuto<TracerEvent> m_events;
+	DynamicArrayAuto<TracerCounter> m_counters;
 	ThreadId m_tid;
 	ThreadId m_tid;
 	U64 m_frame;
 	U64 m_frame;
 
 
@@ -47,7 +47,7 @@ public:
 class CoreTracer::PerFrameCounters : public IntrusiveListEnabled<PerFrameCounters>
 class CoreTracer::PerFrameCounters : public IntrusiveListEnabled<PerFrameCounters>
 {
 {
 public:
 public:
-	DynamicArrayAuto<Tracer2Counter> m_counters;
+	DynamicArrayAuto<TracerCounter> m_counters;
 	U64 m_frame;
 	U64 m_frame;
 
 
 	PerFrameCounters(GenericMemoryPoolAllocator<U8>& alloc)
 	PerFrameCounters(GenericMemoryPoolAllocator<U8>& alloc)
@@ -165,7 +165,7 @@ Error CoreTracer::threadWorker()
 Error CoreTracer::writeEvents(const ThreadWorkItem& item)
 Error CoreTracer::writeEvents(const ThreadWorkItem& item)
 {
 {
 	// Write events
 	// Write events
-	for(const Tracer2Event& event : item.m_events)
+	for(const TracerEvent& event : item.m_events)
 	{
 	{
 		const U64 startMicroSec = U64(event.m_start * 1000000.0);
 		const U64 startMicroSec = U64(event.m_start * 1000000.0);
 		const U64 durMicroSec = U64(event.m_duration * 1000000.0);
 		const U64 durMicroSec = U64(event.m_duration * 1000000.0);
@@ -187,12 +187,12 @@ Error CoreTracer::writeEvents(const ThreadWorkItem& item)
 void CoreTracer::gatherCounters(ThreadWorkItem& item)
 void CoreTracer::gatherCounters(ThreadWorkItem& item)
 {
 {
 	// Sort
 	// Sort
-	std::sort(item.m_counters.getBegin(),
-		item.m_counters.getEnd(),
-		[](const Tracer2Counter& a, const Tracer2Counter& b) { return a.m_name < b.m_name; });
+	std::sort(item.m_counters.getBegin(), item.m_counters.getEnd(), [](const TracerCounter& a, const TracerCounter& b) {
+		return a.m_name < b.m_name;
+	});
 
 
 	// Merge same
 	// Merge same
-	DynamicArrayAuto<Tracer2Counter> mergedCounters(m_alloc);
+	DynamicArrayAuto<TracerCounter> mergedCounters(m_alloc);
 	for(U32 i = 0; i < item.m_counters.getSize(); ++i)
 	for(U32 i = 0; i < item.m_counters.getSize(); ++i)
 	{
 	{
 		if(mergedCounters.getBack().m_name != item.m_counters[i].m_name)
 		if(mergedCounters.getBack().m_name != item.m_counters[i].m_name)
@@ -212,7 +212,7 @@ void CoreTracer::gatherCounters(ThreadWorkItem& item)
 	Bool addedCounterName = false;
 	Bool addedCounterName = false;
 	for(U32 i = 0; i < mergedCounters.getSize(); ++i)
 	for(U32 i = 0; i < mergedCounters.getSize(); ++i)
 	{
 	{
-		const Tracer2Counter& counter = mergedCounters[i];
+		const TracerCounter& counter = mergedCounters[i];
 
 
 		Bool found = false;
 		Bool found = false;
 		for(const String& name : m_counterNames)
 		for(const String& name : m_counterNames)
@@ -250,10 +250,10 @@ void CoreTracer::gatherCounters(ThreadWorkItem& item)
 		// Merge counters to existing frame
 		// Merge counters to existing frame
 		PerFrameCounters& frame = m_frameCounters.getBack();
 		PerFrameCounters& frame = m_frameCounters.getBack();
 		ANKI_ASSERT(frame.m_frame == item.m_frame);
 		ANKI_ASSERT(frame.m_frame == item.m_frame);
-		for(const Tracer2Counter& newCounter : mergedCounters)
+		for(const TracerCounter& newCounter : mergedCounters)
 		{
 		{
 			Bool found = false;
 			Bool found = false;
-			for(Tracer2Counter& existingCounter : frame.m_counters)
+			for(TracerCounter& existingCounter : frame.m_counters)
 			{
 			{
 				if(newCounter.m_name == existingCounter.m_name)
 				if(newCounter.m_name == existingCounter.m_name)
 				{
 				{
@@ -270,7 +270,7 @@ void CoreTracer::gatherCounters(ThreadWorkItem& item)
 	}
 	}
 }
 }
 
 
-void CoreTracer::newFrame(U64 frame)
+void CoreTracer::flushFrame(U64 frame)
 {
 {
 	struct Ctx
 	struct Ctx
 	{
 	{
@@ -282,8 +282,8 @@ void CoreTracer::newFrame(U64 frame)
 	ctx.m_frame = frame;
 	ctx.m_frame = frame;
 	ctx.m_self = this;
 	ctx.m_self = this;
 
 
-	Tracer2Singleton::get().flush(
-		[](void* ud, ThreadId tid, ConstWeakArray<Tracer2Event> events, ConstWeakArray<Tracer2Counter> counters) {
+	TracerSingleton::get().flush(
+		[](void* ud, ThreadId tid, ConstWeakArray<TracerEvent> events, ConstWeakArray<TracerCounter> counters) {
 			Ctx& ctx = *static_cast<Ctx*>(ud);
 			Ctx& ctx = *static_cast<Ctx*>(ud);
 			CoreTracer& self = *ctx.m_self;
 			CoreTracer& self = *ctx.m_self;
 
 
@@ -334,7 +334,7 @@ Error CoreTracer::writeCountersForReal()
 		{
 		{
 			// Find value
 			// Find value
 			U64 value = 0;
 			U64 value = 0;
-			for(const Tracer2Counter& counter : frame.m_counters)
+			for(const TracerCounter& counter : frame.m_counters)
 			{
 			{
 				if(counter.m_name == m_counterNames[j])
 				if(counter.m_name == m_counterNames[j])
 				{
 				{

+ 1 - 1
src/anki/core/CoreTracer.h

@@ -29,7 +29,7 @@ public:
 	ANKI_USE_RESULT Error init(GenericMemoryPoolAllocator<U8> alloc, CString directory);
 	ANKI_USE_RESULT Error init(GenericMemoryPoolAllocator<U8> alloc, CString directory);
 
 
 	/// It will flush everything.
 	/// It will flush everything.
-	void newFrame(U64 frame);
+	void flushFrame(U64 frame);
 
 
 private:
 private:
 	class ThreadWorkItem;
 	class ThreadWorkItem;

+ 1 - 1
src/anki/core/StagingGpuMemoryManager.cpp

@@ -6,7 +6,7 @@
 #include <anki/core/StagingGpuMemoryManager.h>
 #include <anki/core/StagingGpuMemoryManager.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/gr/GrManager.h>
 #include <anki/gr/GrManager.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 
 
 namespace anki
 namespace anki
 {
 {

+ 1 - 0
src/anki/core/StagingGpuMemoryManager.h

@@ -5,6 +5,7 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <anki/core/Common.h>
 #include <anki/gr/Buffer.h>
 #include <anki/gr/Buffer.h>
 #include <anki/gr/common/FrameGpuAllocator.h>
 #include <anki/gr/common/FrameGpuAllocator.h>
 
 

+ 0 - 119
src/anki/core/Trace.h

@@ -1,119 +0,0 @@
-// Copyright (C) 2009-2019, 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/util/Tracer.h>
-
-namespace anki
-{
-
-/// @addtogroup core
-/// @{
-
-/// Core tracer.
-class CoreTracer
-{
-public:
-	Tracer m_tracer;
-	Bool m_enabled = false;
-
-	/// @copydoc Tracer::init
-	void init(GenericMemoryPoolAllocator<U8> alloc)
-	{
-		m_tracer.init(alloc);
-	}
-
-	/// @copydoc Tracer::isInitialized
-	Bool isInitialized() const
-	{
-		return m_tracer.isInitialized();
-	}
-
-	/// @copydoc Tracer::beginEvent
-	ANKI_USE_RESULT TracerEventHandle beginEvent()
-	{
-		if(m_enabled)
-		{
-			return m_tracer.beginEvent();
-		}
-
-		return nullptr;
-	}
-
-	/// @copydoc Tracer::endEvent
-	void endEvent(const char* eventName, TracerEventHandle event)
-	{
-		if(event != nullptr)
-		{
-			m_tracer.endEvent(eventName, event);
-		}
-	}
-
-	/// @copydoc Tracer::increaseCounter
-	void increaseCounter(const char* counterName, U64 value)
-	{
-		if(m_enabled)
-		{
-			m_tracer.increaseCounter(counterName, value);
-		}
-	}
-
-	/// @copydoc Tracer::newFrame
-	void newFrame(U64 frame)
-	{
-		if(m_enabled)
-		{
-			m_tracer.newFrame(frame);
-		}
-	}
-
-	/// @copydoc Tracer::flush
-	ANKI_USE_RESULT Error flush(CString filename)
-	{
-		return m_tracer.flush(filename);
-	}
-};
-
-using CoreTracerSingleton = Singleton<CoreTracer>;
-
-class CoreTraceScopedEvent
-{
-public:
-	CoreTraceScopedEvent(const char* name)
-		: m_name(name)
-		, m_tracer(&CoreTracerSingleton::get())
-	{
-		m_handle = m_tracer->beginEvent();
-	}
-
-	~CoreTraceScopedEvent()
-	{
-		m_tracer->endEvent(m_name, m_handle);
-	}
-
-private:
-	const char* m_name;
-	TracerEventHandle m_handle;
-	CoreTracer* m_tracer;
-};
-
-/// @name Trace macros.
-/// @{
-#if ANKI_ENABLE_TRACE
-#	define ANKI_TRACE_START_EVENT(name_) TracerEventHandle _teh##name_ = CoreTracerSingleton::get().beginEvent()
-#	define ANKI_TRACE_STOP_EVENT(name_) CoreTracerSingleton::get().endEvent(#	name_, _teh##name_)
-#	define ANKI_TRACE_SCOPED_EVENT(name_) CoreTraceScopedEvent _tse##name_(#	name_)
-#	define ANKI_TRACE_INC_COUNTER(name_, val_) CoreTracerSingleton::get().increaseCounter(#	name_, val_)
-#else
-#	define ANKI_TRACE_START_EVENT(name_) ((void)0)
-#	define ANKI_TRACE_STOP_EVENT(name_) ((void)0)
-#	define ANKI_TRACE_SCOPED_EVENT(name_) ((void)0)
-#	define ANKI_TRACE_INC_COUNTER(name_, val_) ((void)0)
-#endif
-/// @}
-
-} // end namespace anki

+ 1 - 1
src/anki/gr/RenderGraph.cpp

@@ -9,7 +9,7 @@
 #include <anki/gr/Sampler.h>
 #include <anki/gr/Sampler.h>
 #include <anki/gr/Framebuffer.h>
 #include <anki/gr/Framebuffer.h>
 #include <anki/gr/CommandBuffer.h>
 #include <anki/gr/CommandBuffer.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 #include <anki/util/BitSet.h>
 #include <anki/util/BitSet.h>
 #include <anki/util/File.h>
 #include <anki/util/File.h>
 #include <anki/util/StringList.h>
 #include <anki/util/StringList.h>

+ 2 - 2
src/anki/gr/ShaderCompiler.cpp

@@ -5,10 +5,10 @@
 
 
 #include <anki/gr/ShaderCompiler.h>
 #include <anki/gr/ShaderCompiler.h>
 #include <anki/gr/GrManager.h>
 #include <anki/gr/GrManager.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 #include <anki/util/StringList.h>
 #include <anki/util/StringList.h>
 #include <anki/util/Filesystem.h>
 #include <anki/util/Filesystem.h>
-#include <anki/core/Trace.h>
+#include <anki/util/File.h>
 
 
 #if ANKI_COMPILER_GCC_COMPATIBLE
 #if ANKI_COMPILER_GCC_COMPATIBLE
 #	pragma GCC diagnostic push
 #	pragma GCC diagnostic push

+ 1 - 1
src/anki/gr/vulkan/CommandBufferFactory.cpp

@@ -4,7 +4,7 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <anki/gr/vulkan/CommandBufferFactory.h>
 #include <anki/gr/vulkan/CommandBufferFactory.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 
 
 namespace anki
 namespace anki
 {
 {

+ 1 - 1
src/anki/gr/vulkan/CommandBufferImpl.inl.h

@@ -9,7 +9,7 @@
 #include <anki/gr/vulkan/OcclusionQueryImpl.h>
 #include <anki/gr/vulkan/OcclusionQueryImpl.h>
 #include <anki/gr/TimestampQuery.h>
 #include <anki/gr/TimestampQuery.h>
 #include <anki/gr/vulkan/TimestampQueryImpl.h>
 #include <anki/gr/vulkan/TimestampQueryImpl.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 
 
 namespace anki
 namespace anki
 {
 {

+ 1 - 1
src/anki/gr/vulkan/DescriptorSet.cpp

@@ -8,7 +8,7 @@
 #include <anki/gr/vulkan/BufferImpl.h>
 #include <anki/gr/vulkan/BufferImpl.h>
 #include <anki/util/List.h>
 #include <anki/util/List.h>
 #include <anki/util/HashMap.h>
 #include <anki/util/HashMap.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 #include <algorithm>
 #include <algorithm>
 
 
 namespace anki
 namespace anki

+ 1 - 1
src/anki/gr/vulkan/FenceFactory.inl.h

@@ -4,7 +4,7 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <anki/gr/vulkan/FenceFactory.h>
 #include <anki/gr/vulkan/FenceFactory.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 
 
 namespace anki
 namespace anki
 {
 {

+ 1 - 0
src/anki/gr/vulkan/GrManagerImpl.h

@@ -20,6 +20,7 @@
 #include <anki/gr/vulkan/PipelineCache.h>
 #include <anki/gr/vulkan/PipelineCache.h>
 #include <anki/gr/vulkan/DescriptorSet.h>
 #include <anki/gr/vulkan/DescriptorSet.h>
 #include <anki/util/HashMap.h>
 #include <anki/util/HashMap.h>
+#include <anki/util/File.h>
 
 
 namespace anki
 namespace anki
 {
 {

+ 1 - 1
src/anki/gr/vulkan/Pipeline.cpp

@@ -6,7 +6,7 @@
 #include <anki/gr/vulkan/Pipeline.h>
 #include <anki/gr/vulkan/Pipeline.h>
 #include <anki/gr/vulkan/GrManagerImpl.h>
 #include <anki/gr/vulkan/GrManagerImpl.h>
 #include <anki/gr/common/Misc.h>
 #include <anki/gr/common/Misc.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 
 
 namespace anki
 namespace anki
 {
 {

+ 1 - 1
src/anki/gr/vulkan/SemaphoreFactory.inl.h

@@ -4,7 +4,7 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <anki/gr/vulkan/SemaphoreFactory.h>
 #include <anki/gr/vulkan/SemaphoreFactory.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 
 
 namespace anki
 namespace anki
 {
 {

+ 1 - 1
src/anki/renderer/ClusterBin.cpp

@@ -7,8 +7,8 @@
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/Collision.h>
 #include <anki/Collision.h>
 #include <anki/util/ThreadHive.h>
 #include <anki/util/ThreadHive.h>
+#include <anki/util/Tracer.h>
 #include <anki/core/Config.h>
 #include <anki/core/Config.h>
-#include <anki/core/Trace.h>
 
 
 namespace anki
 namespace anki
 {
 {

+ 1 - 1
src/anki/renderer/Drawer.cpp

@@ -8,7 +8,7 @@
 #include <anki/resource/MaterialResource.h>
 #include <anki/resource/MaterialResource.h>
 #include <anki/resource/TextureResource.h>
 #include <anki/resource/TextureResource.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/Renderer.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 #include <anki/util/Logger.h>
 #include <anki/util/Logger.h>
 
 
 namespace anki
 namespace anki

+ 1 - 1
src/anki/renderer/GBuffer.cpp

@@ -8,8 +8,8 @@
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/LensFlare.h>
 #include <anki/renderer/LensFlare.h>
 #include <anki/util/Logger.h>
 #include <anki/util/Logger.h>
+#include <anki/util/Tracer.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/misc/ConfigSet.h>
-#include <anki/core/Trace.h>
 
 
 namespace anki
 namespace anki
 {
 {

+ 1 - 1
src/anki/renderer/GlobalIllumination.cpp

@@ -7,7 +7,7 @@
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/misc/ConfigSet.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 #include <anki/collision/Aabb.h>
 #include <anki/collision/Aabb.h>
 #include <anki/collision/Functions.h>
 #include <anki/collision/Functions.h>
 
 

+ 1 - 2
src/anki/renderer/MainRenderer.cpp

@@ -12,8 +12,7 @@
 #include <anki/util/Logger.h>
 #include <anki/util/Logger.h>
 #include <anki/util/File.h>
 #include <anki/util/File.h>
 #include <anki/util/Filesystem.h>
 #include <anki/util/Filesystem.h>
-#include <anki/core/Trace.h>
-#include <anki/core/App.h>
+#include <anki/util/Tracer.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/util/HighRezTimer.h>
 #include <anki/util/HighRezTimer.h>
 #include <anki/util/ThreadHive.h>
 #include <anki/util/ThreadHive.h>

+ 1 - 1
src/anki/renderer/ProbeReflections.cpp

@@ -9,7 +9,7 @@
 #include <anki/renderer/GBuffer.h>
 #include <anki/renderer/GBuffer.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/core/Config.h>
 #include <anki/core/Config.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 #include <anki/resource/MeshResource.h>
 #include <anki/resource/MeshResource.h>
 #include <shaders/glsl_cpp_common/TraditionalDeferredShading.h>
 #include <shaders/glsl_cpp_common/TraditionalDeferredShading.h>
 
 

+ 1 - 1
src/anki/renderer/Renderer.cpp

@@ -5,7 +5,7 @@
 
 
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/RenderQueue.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/util/HighRezTimer.h>
 #include <anki/util/HighRezTimer.h>
 #include <anki/collision/Aabb.h>
 #include <anki/collision/Aabb.h>

+ 1 - 2
src/anki/renderer/ShadowMapping.cpp

@@ -6,10 +6,9 @@
 #include <anki/renderer/ShadowMapping.h>
 #include <anki/renderer/ShadowMapping.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/RenderQueue.h>
-#include <anki/core/App.h>
-#include <anki/core/Trace.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/util/ThreadHive.h>
 #include <anki/util/ThreadHive.h>
+#include <anki/util/Tracer.h>
 
 
 namespace anki
 namespace anki
 {
 {

+ 1 - 1
src/anki/renderer/UiStage.cpp

@@ -8,7 +8,7 @@
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/ui/Font.h>
 #include <anki/ui/Font.h>
 #include <anki/ui/UiManager.h>
 #include <anki/ui/UiManager.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 
 
 namespace anki
 namespace anki
 {
 {

+ 1 - 1
src/anki/resource/AsyncLoader.cpp

@@ -5,7 +5,7 @@
 
 
 #include <anki/resource/AsyncLoader.h>
 #include <anki/resource/AsyncLoader.h>
 #include <anki/util/Logger.h>
 #include <anki/util/Logger.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 
 
 namespace anki
 namespace anki
 {
 {

+ 1 - 1
src/anki/resource/ResourceFilesystem.cpp

@@ -6,7 +6,7 @@
 #include <anki/resource/ResourceFilesystem.h>
 #include <anki/resource/ResourceFilesystem.h>
 #include <anki/util/Filesystem.h>
 #include <anki/util/Filesystem.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/misc/ConfigSet.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 #include <contrib/minizip/unzip.h>
 #include <contrib/minizip/unzip.h>
 
 
 namespace anki
 namespace anki

+ 1 - 1
src/anki/resource/TransferGpuAllocator.cpp

@@ -7,7 +7,7 @@
 #include <anki/gr/Fence.h>
 #include <anki/gr/Fence.h>
 #include <anki/gr/Buffer.h>
 #include <anki/gr/Buffer.h>
 #include <anki/gr/GrManager.h>
 #include <anki/gr/GrManager.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 
 
 namespace anki
 namespace anki
 {
 {

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

@@ -12,7 +12,7 @@
 #include <anki/util/Enum.h>
 #include <anki/util/Enum.h>
 #include <anki/util/ObjectAllocator.h>
 #include <anki/util/ObjectAllocator.h>
 #include <anki/util/List.h>
 #include <anki/util/List.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 
 
 namespace anki
 namespace anki
 {
 {

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

@@ -9,12 +9,12 @@
 #include <anki/scene/ModelNode.h>
 #include <anki/scene/ModelNode.h>
 #include <anki/scene/Octree.h>
 #include <anki/scene/Octree.h>
 #include <anki/scene/components/FrustumComponent.h>
 #include <anki/scene/components/FrustumComponent.h>
-#include <anki/core/Trace.h>
 #include <anki/physics/PhysicsWorld.h>
 #include <anki/physics/PhysicsWorld.h>
 #include <anki/resource/ResourceManager.h>
 #include <anki/resource/ResourceManager.h>
 #include <anki/renderer/MainRenderer.h>
 #include <anki/renderer/MainRenderer.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/util/ThreadHive.h>
 #include <anki/util/ThreadHive.h>
+#include <anki/util/Tracer.h>
 
 
 namespace anki
 namespace anki
 {
 {

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

@@ -6,7 +6,7 @@
 #include <anki/scene/SoftwareRasterizer.h>
 #include <anki/scene/SoftwareRasterizer.h>
 #include <anki/collision/Aabb.h>
 #include <anki/collision/Aabb.h>
 #include <anki/collision/Functions.h>
 #include <anki/collision/Functions.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 
 
 namespace anki
 namespace anki
 {
 {

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

@@ -10,7 +10,7 @@
 #include <anki/scene/components/FrustumComponent.h>
 #include <anki/scene/components/FrustumComponent.h>
 #include <anki/scene/Octree.h>
 #include <anki/scene/Octree.h>
 #include <anki/util/Thread.h>
 #include <anki/util/Thread.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/RenderQueue.h>
 
 
 namespace anki
 namespace anki

+ 1 - 1
src/anki/script/LuaBinder.cpp

@@ -5,7 +5,7 @@
 
 
 #include <anki/script/LuaBinder.h>
 #include <anki/script/LuaBinder.h>
 #include <anki/util/Logger.h>
 #include <anki/util/Logger.h>
-#include <anki/core/Trace.h>
+#include <anki/util/Tracer.h>
 
 
 namespace anki
 namespace anki
 {
 {

+ 1 - 1
src/anki/util/CMakeLists.txt

@@ -1,5 +1,5 @@
 set(SOURCES Assert.cpp Functions.cpp File.cpp Filesystem.cpp Memory.cpp System.cpp HighRezTimer.cpp ThreadPool.cpp
 set(SOURCES Assert.cpp Functions.cpp File.cpp Filesystem.cpp Memory.cpp System.cpp HighRezTimer.cpp ThreadPool.cpp
-	ThreadHive.cpp Hash.cpp Logger.cpp String.cpp StringList.cpp Tracer.cpp Tracer2.cpp)
+	ThreadHive.cpp Hash.cpp Logger.cpp String.cpp StringList.cpp Tracer.cpp)
 
 
 if(LINUX OR ANDROID OR MACOS)
 if(LINUX OR ANDROID OR MACOS)
 	set(SOURCES ${SOURCES} HighRezTimerPosix.cpp FilesystemPosix.cpp ThreadPosix.cpp)
 	set(SOURCES ${SOURCES} HighRezTimerPosix.cpp FilesystemPosix.cpp ThreadPosix.cpp)

+ 70 - 482
src/anki/util/Tracer.cpp

@@ -6,143 +6,36 @@
 #include <anki/util/Tracer.h>
 #include <anki/util/Tracer.h>
 #include <anki/util/HighRezTimer.h>
 #include <anki/util/HighRezTimer.h>
 #include <anki/util/HashMap.h>
 #include <anki/util/HashMap.h>
+#include <anki/util/List.h>
 
 
 namespace anki
 namespace anki
 {
 {
 
 
-/// Lightweight event storage.
-class Tracer::Event
+class Tracer::Chunk : public IntrusiveListEnabled<Chunk>
 {
 {
 public:
 public:
-	const char* m_name;
-	Second m_timestamp;
-	Second m_duration;
-};
-
-/// Event batch allocation.
-class Tracer::EventsChunk : public IntrusiveListEnabled<EventsChunk>
-{
-public:
-	Array<Event, EVENTS_PER_CHUNK> m_events;
+	Array<TracerEvent, EVENTS_PER_CHUNK> m_events;
 	U32 m_eventCount = 0;
 	U32 m_eventCount = 0;
-};
-
-/// A heavyweight event with more info.
-class Tracer::GatherEvent
-{
-public:
-	CString m_name;
-	Second m_timestamp;
-	Second m_duration;
-	ThreadId m_tid;
-};
-
-/// Lightweight counter storage.
-class Tracer::Counter
-{
-public:
-	const char* m_name;
-	U64 m_value;
-};
-
-/// Counter batch allocation.
-class Tracer::CountersChunk : public IntrusiveListEnabled<CountersChunk>
-{
-public:
-	U64 m_frame;
-	Second m_startFrameTime;
-	Array<Counter, COUNTERS_PER_CHUNK> m_counters;
+	Array<TracerCounter, COUNTERS_PER_CHUNK> m_counters;
 	U32 m_counterCount = 0;
 	U32 m_counterCount = 0;
 };
 };
 
 
-/// Heavyweight counter storage.
-class Tracer::GatherCounter
-{
-public:
-	CString m_name;
-	U64 m_value;
-};
-
 /// Thread local storage.
 /// Thread local storage.
-class Tracer::ThreadLocal
+class alignas(ANKI_CACHE_LINE_SIZE) Tracer::ThreadLocal
 {
 {
 public:
 public:
 	ThreadId m_tid ANKI_DEBUG_CODE(= 0);
 	ThreadId m_tid ANKI_DEBUG_CODE(= 0);
 
 
-	IntrusiveList<CountersChunk> m_counterChunks;
-	IntrusiveList<EventsChunk> m_eventChunks;
+	Chunk* m_currentChunk ANKI_DEBUG_CODE(= nullptr);
+	IntrusiveList<Chunk> m_allChunks;
+	SpinLock m_currentChunkLock;
 };
 };
 
 
 thread_local Tracer::ThreadLocal* Tracer::m_threadLocal = nullptr;
 thread_local Tracer::ThreadLocal* Tracer::m_threadLocal = nullptr;
 
 
-/// Storage of counters per frame.
-class Tracer::PerFrameCounters
-{
-public:
-	DynamicArrayAuto<GatherCounter> m_counters;
-	DynamicArrayAuto<GatherCounter> m_tempCounters; ///< A temp storage.
-	U64 m_frame;
-	Second m_startFrameTime;
-
-	PerFrameCounters(GenericMemoryPoolAllocator<U8> alloc)
-		: m_counters(alloc)
-		, m_tempCounters(alloc)
-	{
-	}
-};
-
-/// Context for Tracer::flush().
-class Tracer::FlushCtx
-{
-public:
-	GenericMemoryPoolAllocator<U8> m_alloc;
-	CString m_filename;
-	DynamicArrayAuto<CString> m_counterNames;
-	DynamicArrayAuto<PerFrameCounters> m_counters;
-	DynamicArrayAuto<GatherEvent> m_events;
-
-	FlushCtx(GenericMemoryPoolAllocator<U8> alloc, const CString& filename)
-		: m_alloc(alloc)
-		, m_filename(filename)
-		, m_counterNames(alloc)
-		, m_counters(alloc)
-		, m_events(alloc)
-	{
-	}
-};
-
 Tracer::~Tracer()
 Tracer::~Tracer()
 {
 {
-	for(ThreadLocal* threadLocal : m_allThreadLocal)
-	{
-		while(!threadLocal->m_counterChunks.isEmpty())
-		{
-			CountersChunk& chunk = threadLocal->m_counterChunks.getFront();
-			threadLocal->m_counterChunks.popFront();
-			m_alloc.deleteInstance(&chunk);
-		}
-
-		while(!threadLocal->m_eventChunks.isEmpty())
-		{
-			EventsChunk& chunk = threadLocal->m_eventChunks.getFront();
-			threadLocal->m_eventChunks.popFront();
-			m_alloc.deleteInstance(&chunk);
-		}
-
-		m_alloc.deleteInstance(threadLocal);
-	}
-
-	m_allThreadLocal.destroy(m_alloc);
-}
-
-void Tracer::newFrame(U64 frame)
-{
-	ANKI_ASSERT(frame == 0 || frame > m_frame);
-
-	LockGuard<SpinLock> lock(m_frameMtx);
-
-	m_startFrameTime = HighRezTimer::getCurrentTime();
-	m_frame = frame;
+	// TODO
 }
 }
 
 
 Tracer::ThreadLocal& Tracer::getThreadLocal()
 Tracer::ThreadLocal& Tracer::getThreadLocal()
@@ -152,420 +45,115 @@ Tracer::ThreadLocal& Tracer::getThreadLocal()
 	{
 	{
 		out = m_alloc.newInstance<ThreadLocal>();
 		out = m_alloc.newInstance<ThreadLocal>();
 		out->m_tid = Thread::getCurrentThreadId();
 		out->m_tid = Thread::getCurrentThreadId();
+		out->m_currentChunk = nullptr;
 		m_threadLocal = out;
 		m_threadLocal = out;
 
 
 		// Store it
 		// Store it
-		LockGuard<Mutex> lock(m_threadLocalMtx);
+		LockGuard<Mutex> lock(m_allThreadLocalMtx);
 		m_allThreadLocal.emplaceBack(m_alloc, out);
 		m_allThreadLocal.emplaceBack(m_alloc, out);
 	}
 	}
 
 
 	return *out;
 	return *out;
 }
 }
 
 
-TracerEventHandle Tracer::beginEvent()
+Tracer::Chunk& Tracer::getOrCreateChunk(ThreadLocal& tlocal)
 {
 {
-	ThreadLocal& threadLocal = getThreadLocal();
+	Chunk* out;
 
 
-	// Allocate new chunk
-	if(threadLocal.m_eventChunks.isEmpty() || threadLocal.m_eventChunks.getBack().m_eventCount >= EVENTS_PER_CHUNK)
+	if(tlocal.m_currentChunk && tlocal.m_currentChunk->m_eventCount + 1 < EVENTS_PER_CHUNK
+		&& tlocal.m_currentChunk->m_counterCount + 1 < COUNTERS_PER_CHUNK)
 	{
 	{
-		EventsChunk* chunk = m_alloc.newInstance<EventsChunk>();
-		threadLocal.m_eventChunks.pushBack(chunk);
+		// There is a chunk and it has enough space
+		out = tlocal.m_currentChunk;
 	}
 	}
-
-	EventsChunk& chunk = threadLocal.m_eventChunks.getBack();
-	Event* event = &chunk.m_events[chunk.m_eventCount++];
-	event->m_timestamp = HighRezTimer::getCurrentTime();
-
-	return event;
-}
-
-void Tracer::endEvent(const char* eventName, TracerEventHandle eventHandle)
-{
-	ANKI_ASSERT(eventName);
-	ANKI_ASSERT(eventHandle);
-
-	Event* event = static_cast<Event*>(eventHandle);
-	event->m_name = eventName;
-	event->m_duration = HighRezTimer::getCurrentTime() - event->m_timestamp;
-
-	// Store a counter as well. In ns
-	increaseCounter(eventName, U64(event->m_duration * 1000000000.0));
-}
-
-void Tracer::increaseCounter(const char* counterName, U64 value)
-{
-	ANKI_ASSERT(counterName);
-
-	ThreadLocal& threadLocal = getThreadLocal();
-
-	// Create chunk
-	if(threadLocal.m_counterChunks.isEmpty() || threadLocal.m_counterChunks.getBack().m_frame != m_frame
-		|| threadLocal.m_counterChunks.getBack().m_counterCount >= COUNTERS_PER_CHUNK)
+	else
 	{
 	{
-		CountersChunk* newChunk = m_alloc.newInstance<CountersChunk>();
-		threadLocal.m_counterChunks.pushBack(newChunk);
-
-		{
-			LockGuard<SpinLock> lock(m_frameMtx);
-			newChunk->m_frame = m_frame;
-			newChunk->m_startFrameTime = m_startFrameTime;
-		}
+		// Create a new
+		out = m_alloc.newInstance<Chunk>();
+		tlocal.m_currentChunk = out;
+		tlocal.m_allChunks.pushBack(out);
 	}
 	}
 
 
-	CountersChunk& chunk = threadLocal.m_counterChunks.getBack();
-
-	Counter& counter = chunk.m_counters[chunk.m_counterCount++];
-	counter.m_name = counterName;
-	counter.m_value = value;
+	return *out;
 }
 }
 
 
-void Tracer::gatherCounters(FlushCtx& ctx)
+TracerEventHandle Tracer::beginEvent()
 {
 {
-	// Iterate all the chunks and create the PerFrameCounters
-	for(ThreadLocal* threadLocal : m_allThreadLocal)
-	{
-		while(!threadLocal->m_counterChunks.isEmpty())
-		{
-			// Pop chunk
-			CountersChunk& chunk = threadLocal->m_counterChunks.getFront();
-			threadLocal->m_counterChunks.popFront();
-
-			// Iterate the PerFrameCounters to find if the frame is present
-			PerFrameCounters* perFrame = nullptr;
-			for(PerFrameCounters& pf : ctx.m_counters)
-			{
-				if(pf.m_frame == chunk.m_frame)
-				{
-					perFrame = &pf;
-					break;
-				}
-			}
-
-			if(!perFrame)
-			{
-				ctx.m_counters.emplaceBack(m_alloc);
-
-				perFrame = &ctx.m_counters.getBack();
-				perFrame->m_frame = chunk.m_frame;
-				perFrame->m_startFrameTime = chunk.m_startFrameTime;
-			}
-
-			ANKI_ASSERT(chunk.m_frame == perFrame->m_frame);
-
-			// Copy the counters
-			for(U i = 0; i < chunk.m_counterCount; ++i)
-			{
-				const Counter& inCounter = chunk.m_counters[i];
-
-				GatherCounter outCounter;
-				outCounter.m_name = inCounter.m_name;
-				outCounter.m_value = inCounter.m_value;
-
-				perFrame->m_tempCounters.emplaceBack(outCounter);
-			}
-
-			// Delete chunk
-			m_alloc.deleteInstance(&chunk);
-		}
-	}
+	TracerEventHandle out;
+	out.m_start = HighRezTimer::getCurrentTime();
+	return out;
+}
 
 
-	if(ctx.m_counters.getSize() == 0)
+void Tracer::endEvent(const char* eventName, TracerEventHandle event)
+{
+	if(!m_enabled)
 	{
 	{
-		// Early exit
 		return;
 		return;
 	}
 	}
 
 
-	// Compact the counters and get all counter names
-	for(PerFrameCounters& perFrame : ctx.m_counters)
-	{
-		if(perFrame.m_tempCounters.getSize() == 0)
-		{
-			continue;
-		}
-
-		// Sort counters
-		std::sort(perFrame.m_tempCounters.getBegin(),
-			perFrame.m_tempCounters.getEnd(),
-			[](const GatherCounter& a, const GatherCounter& b) { return a.m_name < b.m_name; });
-
-		// Compact counters
-		for(const GatherCounter& tmpCounter : perFrame.m_tempCounters)
-		{
-			if(perFrame.m_counters.getSize() == 0 || perFrame.m_counters.getBack().m_name != tmpCounter.m_name)
-			{
-				// Create new counter
-				perFrame.m_counters.emplaceBack(tmpCounter);
-
-				// Update the counter names
-				Bool found = false;
-				for(const CString& counterName : ctx.m_counterNames)
-				{
-					if(counterName == tmpCounter.m_name)
-					{
-						found = true;
-						break;
-					}
-				}
-
-				if(!found)
-				{
-					ctx.m_counterNames.emplaceBack(tmpCounter.m_name);
-				}
-			}
-			else
-			{
-				// Merge counters
-				GatherCounter& mergeTo = perFrame.m_counters.getBack();
-				ANKI_ASSERT(mergeTo.m_name == tmpCounter.m_name);
-				mergeTo.m_value += tmpCounter.m_value;
-			}
-		}
-
-		// Free some memory
-		perFrame.m_tempCounters.destroy();
-	}
-
-	// Sort the counter names
-	ANKI_ASSERT(ctx.m_counterNames.getSize() > 0);
-	std::sort(ctx.m_counterNames.getBegin(), ctx.m_counterNames.getEnd(), [](CString a, CString b) { return a < b; });
-
-	// Fill the gaps. Some counters might have not appeared in some frames. Those counters need to have a zero value
-	// because the CSV wants all counters present on all rows
-	for(PerFrameCounters& perFrame : ctx.m_counters)
-	{
-		ANKI_ASSERT(perFrame.m_counters.getSize() <= ctx.m_counterNames.getSize());
-
-		for(U i = 0; i < ctx.m_counterNames.getSize(); ++i)
-		{
-			const CString& counterName = ctx.m_counterNames[i];
+	// Get the time before the lock and everything
+	const Second timestamp = HighRezTimer::getCurrentTime();
 
 
-			// Try to find the counter
-			Bool found = false;
-			for(const GatherCounter& c : perFrame.m_counters)
-			{
-				if(counterName == c.m_name)
-				{
-					found = true;
-					break;
-				}
-			}
+	ThreadLocal& tlocal = getThreadLocal();
 
 
-			if(!found)
-			{
-				// Counter is missing
-				GatherCounter missingCounter;
-				missingCounter.m_name = counterName;
-				missingCounter.m_value = 0;
-				perFrame.m_counters.emplaceBack(missingCounter);
-			}
-		}
+	// Write the event
+	LockGuard<SpinLock> lock(tlocal.m_currentChunkLock);
+	Chunk& chunk = getOrCreateChunk(tlocal);
 
 
-		// Sort again
-		std::sort(perFrame.m_counters.getBegin(),
-			perFrame.m_counters.getEnd(),
-			[](const GatherCounter& a, const GatherCounter& b) { return a.m_name < b.m_name; });
+	ANKI_ASSERT(chunk.m_eventCount + 1 < EVENTS_PER_CHUNK);
+	TracerEvent& writeEvent = chunk.m_events[chunk.m_eventCount++];
+	writeEvent.m_name = eventName;
+	writeEvent.m_start = event.m_start;
+	writeEvent.m_duration = timestamp - event.m_start;
 
 
-		ANKI_ASSERT(perFrame.m_counters.getSize() == ctx.m_counterNames.getSize());
-	}
+	// Write counter as well. In ns
+	ANKI_ASSERT(chunk.m_counterCount + 1 < COUNTERS_PER_CHUNK);
+	TracerCounter& writeCounter = chunk.m_counters[chunk.m_counterCount++];
+	writeCounter.m_name = eventName;
+	writeCounter.m_value = U64(writeEvent.m_duration * 1000000000.0);
 }
 }
 
 
-void Tracer::gatherEvents(FlushCtx& ctx)
+void Tracer::incrementCounter(const char* counterName, U64 value)
 {
 {
-	for(ThreadLocal* threadLocal : m_allThreadLocal)
+	if(!m_enabled)
 	{
 	{
-		while(!threadLocal->m_eventChunks.isEmpty())
-		{
-			// Pop chunk
-			EventsChunk& chunk = threadLocal->m_eventChunks.getFront();
-			threadLocal->m_eventChunks.popFront();
-
-			// Copy
-			for(U i = 0; i < chunk.m_eventCount; ++i)
-			{
-				const Event& inEvent = chunk.m_events[i];
-
-				GatherEvent outEvent;
-				outEvent.m_duration = inEvent.m_duration;
-				outEvent.m_name = inEvent.m_name;
-				outEvent.m_timestamp = inEvent.m_timestamp;
-				outEvent.m_tid = threadLocal->m_tid;
-
-				ctx.m_events.emplaceBack(outEvent);
-			}
-
-			// Delete poped chunk
-			m_alloc.deleteInstance(&chunk);
-		}
+		return;
 	}
 	}
 
 
-	// Sort them
-	std::sort(ctx.m_events.getBegin(), ctx.m_events.getEnd(), [](const GatherEvent& a, const GatherEvent& b) {
-		if(a.m_timestamp != b.m_timestamp)
-		{
-			return a.m_timestamp < b.m_timestamp;
-		}
+	ThreadLocal& tlocal = getThreadLocal();
 
 
-		if(a.m_duration != b.m_duration)
-		{
-			return a.m_duration < b.m_duration;
-		}
+	LockGuard<SpinLock> lock(tlocal.m_currentChunkLock);
+	Chunk& chunk = getOrCreateChunk(tlocal);
 
 
-		return a.m_name < b.m_name;
-	});
+	ANKI_ASSERT(chunk.m_counterCount + 1 < COUNTERS_PER_CHUNK);
+	TracerCounter& writeTo = chunk.m_counters[chunk.m_counterCount++];
+	writeTo.m_name = counterName;
+	writeTo.m_value = value;
 }
 }
 
 
-Error Tracer::writeTraceJson(const FlushCtx& ctx)
+void Tracer::flush(TracerFlushCallback callback, void* callbackUserData)
 {
 {
-	// Open the file
-	StringAuto newFname(m_alloc);
-	newFname.sprintf("%s.trace.json", ctx.m_filename.cstr());
-	File file;
-	ANKI_CHECK(file.open(newFname.toCString(), FileOpenFlag::WRITE));
-
-	if(ctx.m_events.getSize() == 0)
-	{
-		// Early exit
-		return Error::NONE;
-	}
-
-	ANKI_CHECK(file.writeText("[\n"));
-
-	// Write the events to the file
-	for(const GatherEvent& event : ctx.m_events)
-	{
-		const U64 startMicroSec = U64(event.m_timestamp * 1000000.0);
-		const U64 durMicroSec = U64(event.m_duration * 1000000.0);
-
-		if(durMicroSec == 0)
-		{
-			continue;
-		}
-
-		ANKI_CHECK(file.writeText("{\"name\": \"%s\", \"cat\": \"PERF\", \"ph\": \"X\", "
-								  "\"pid\": 1, \"tid\": %llu, \"ts\": %llu, \"dur\": %llu},\n",
-			event.m_name.cstr(),
-			event.m_tid,
-			startMicroSec,
-			durMicroSec));
-	}
+	ANKI_ASSERT(callback);
 
 
-	// Write the counters
-	for(U i = 0; i < ctx.m_counters.getSize(); ++i)
+	LockGuard<Mutex> lock(m_allThreadLocalMtx);
+	for(ThreadLocal* tlocal : m_allThreadLocal)
 	{
 	{
-		const PerFrameCounters& frame = ctx.m_counters[i];
-		const Second startFrameTime = frame.m_startFrameTime;
-
-		// The counters need a range in order to appear. Add a dummy counter for the last frame
-		const Array<Second, 2> timestamps = {{startFrameTime, startFrameTime + 1.0}};
-		const U timestampCount = (i < ctx.m_counters.getSize() - 1) ? 1 : 2;
+		LockGuard<SpinLock> lock2(tlocal->m_currentChunkLock);
 
 
-		for(const GatherCounter& counter : frame.m_counters)
+		while(!tlocal->m_allChunks.isEmpty())
 		{
 		{
-			for(U j = 0; j < timestampCount; ++j)
-			{
-				ANKI_CHECK(file.writeText("{\"name\": \"%s\", \"cat\": \"PERF\", \"ph\": \"C\", "
-										  "\"pid\": 1, \"ts\": %llu, \"args\": {\"val\": %llu}},\n",
-					counter.m_name.cstr(),
-					U64(timestamps[j] * 1000000.0),
-					counter.m_value));
-			}
-		}
-	}
-
-	ANKI_CHECK(file.writeText("{}\n]\n"));
-
-	return Error::NONE;
-}
-
-Error Tracer::writeCounterCsv(const FlushCtx& ctx)
-{
-	// Open the file
-	StringAuto fname(m_alloc);
-	fname.sprintf("%s.counters.csv", ctx.m_filename.cstr());
-	File file;
-	ANKI_CHECK(file.open(fname.toCString(), FileOpenFlag::WRITE));
-
-	if(ctx.m_counters.getSize() == 0)
-	{
-		// If there are no counters leave the file empty and exit
-		return Error::NONE;
-	}
-
-	// Write the counter names
-	ANKI_CHECK(file.writeText("Frame"));
-	for(CString counterName : ctx.m_counterNames)
-	{
-		ANKI_CHECK(file.writeText(",%s", counterName.cstr()));
-	}
-	ANKI_CHECK(file.writeText("\n"));
+			Chunk* chunk = tlocal->m_allChunks.popFront();
 
 
-	// Dump the frames
-	U rowCount = 0;
-	for(const PerFrameCounters& frame : ctx.m_counters)
-	{
-		ANKI_CHECK(file.writeText("%llu", frame.m_frame));
+			callback(callbackUserData,
+				tlocal->m_tid,
+				WeakArray<TracerEvent>(&chunk->m_events[0], chunk->m_eventCount),
+				WeakArray<TracerCounter>(&chunk->m_counters[0], chunk->m_counterCount));
 
 
-		for(const GatherCounter& c : frame.m_counters)
-		{
-			ANKI_CHECK(file.writeText(",%llu", c.m_value));
+			m_alloc.deleteInstance(chunk);
 		}
 		}
 
 
-		ANKI_CHECK(file.writeText("\n"));
-		++rowCount;
-	}
-
-	// Dump some spreadsheet functions
-	ANKI_CHECK(file.writeText("SUM"));
-	for(U i = 0; i < ctx.m_counterNames.getSize(); ++i)
-	{
-		Array<char, 3> columnName;
-		getSpreadsheetColumnName(i + 1, columnName);
-		ANKI_CHECK(file.writeText(",=SUM(%s2:%s%u)", &columnName[0], &columnName[0], rowCount + 1u));
-	}
-	ANKI_CHECK(file.writeText("\n"));
-
-	ANKI_CHECK(file.writeText("AVG"));
-	for(U i = 0; i < ctx.m_counterNames.getSize(); ++i)
-	{
-		Array<char, 3> columnName;
-		getSpreadsheetColumnName(i + 1, columnName);
-		ANKI_CHECK(file.writeText(",=AVERAGE(%s2:%s%u)", &columnName[0], &columnName[0], rowCount + 1u));
-	}
-
-	return Error::NONE;
-}
-
-Error Tracer::flush(CString filename)
-{
-	FlushCtx ctx(m_alloc, filename);
-
-	gatherCounters(ctx);
-	gatherEvents(ctx);
-
-	ANKI_CHECK(writeTraceJson(ctx));
-	ANKI_CHECK(writeCounterCsv(ctx));
-
-	return Error::NONE;
-}
-
-void Tracer::getSpreadsheetColumnName(U column, Array<char, 3>& arr)
-{
-	U major = column / 26;
-	U minor = column % 26;
-
-	if(major)
-	{
-		arr[0] = char('A' + (major - 1));
-		arr[1] = char('A' + minor);
+		tlocal->m_currentChunk = nullptr;
 	}
 	}
-	else
-	{
-		arr[0] = char('A' + minor);
-		arr[1] = '\0';
-	}
-
-	arr[2] = '\0';
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 98 - 44
src/anki/util/Tracer.h

@@ -5,9 +5,11 @@
 
 
 #pragma once
 #pragma once
 
 
-#include <anki/util/File.h>
-#include <anki/util/List.h>
-#include <anki/util/ObjectAllocator.h>
+#include <anki/util/Thread.h>
+#include <anki/util/WeakArray.h>
+#include <anki/util/DynamicArray.h>
+#include <anki/util/Singleton.h>
+#include <anki/util/String.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -16,86 +18,138 @@ namespace anki
 /// @{
 /// @{
 
 
 /// @memberof Tracer
 /// @memberof Tracer
-using TracerEventHandle = void*;
+class TracerEventHandle
+{
+	friend class Tracer;
 
 
-/// Tracer.
-class Tracer : public NonCopyable
+private:
+	Second m_start;
+};
+
+/// @memberof Tracer
+class TracerEvent
 {
 {
 public:
 public:
-	Tracer()
+	CString m_name;
+	Second m_start;
+	Second m_duration;
+
+	TracerEvent()
 	{
 	{
+		// No init
 	}
 	}
+};
 
 
-	~Tracer();
+/// @memberof Tracer
+class TracerCounter
+{
+public:
+	CString m_name;
+	U64 m_value;
 
 
-	void init(GenericMemoryPoolAllocator<U8> alloc)
+	TracerCounter()
 	{
 	{
-		m_alloc = alloc;
+		// No init
 	}
 	}
+};
+
+/// Tracer flush callback.
+/// @memberof Tracer
+using TracerFlushCallback = void (*)(
+	void* userData, ThreadId tid, ConstWeakArray<TracerEvent> events, ConstWeakArray<TracerCounter> counters);
 
 
-	Bool isInitialized() const
+/// Tracer.
+class Tracer : public NonCopyable
+{
+public:
+	Tracer(GenericMemoryPoolAllocator<U8> alloc)
+		: m_alloc(alloc)
 	{
 	{
-		return !!m_alloc;
 	}
 	}
 
 
+	~Tracer();
+
 	/// Begin a new event.
 	/// Begin a new event.
+	/// @note It's thread-safe.
 	ANKI_USE_RESULT TracerEventHandle beginEvent();
 	ANKI_USE_RESULT TracerEventHandle beginEvent();
 
 
 	/// End the event that got started with beginEvent().
 	/// End the event that got started with beginEvent().
+	/// @note It's thread-safe.
 	void endEvent(const char* eventName, TracerEventHandle event);
 	void endEvent(const char* eventName, TracerEventHandle event);
 
 
-	/// Increase a counter.
-	void increaseCounter(const char* counterName, U64 value);
-
-	/// Begin a new frame.
-	void newFrame(U64 frame);
+	/// Increment a counter.
+	/// @note It's thread-safe.
+	void incrementCounter(const char* counterName, U64 value);
 
 
-	/// Flush all results to a file. Don't call that more than once.
-	ANKI_USE_RESULT Error flush(CString filename);
+	/// Flush all counters and events and start clean. The callback will be called multiple times.
+	/// @note It's thread-safe.
+	void flush(TracerFlushCallback callback, void* callbackUserData);
 
 
-private:
-	static const U32 EVENTS_PER_CHUNK = 256;
-	static const U32 COUNTERS_PER_CHUNK = 512;
+	Bool getEnabled() const
+	{
+		return m_enabled;
+	}
 
 
-	class Event;
-	class EventsChunk;
-	class GatherEvent;
+	void setEnabled(Bool enabled)
+	{
+		m_enabled = enabled;
+	}
 
 
-	class Counter;
-	class CountersChunk;
-	class GatherCounter;
+private:
+	static constexpr U32 EVENTS_PER_CHUNK = 256;
+	static constexpr U32 COUNTERS_PER_CHUNK = 512;
 
 
 	class ThreadLocal;
 	class ThreadLocal;
-	class PerFrameCounters;
-	class FlushCtx;
+	class Chunk;
 
 
 	GenericMemoryPoolAllocator<U8> m_alloc;
 	GenericMemoryPoolAllocator<U8> m_alloc;
 
 
-	Second m_startFrameTime = 0.0;
-	U64 m_frame = 0;
-	SpinLock m_frameMtx; ///< Protect m_startFrameTime and m_frame.
-
 	static thread_local ThreadLocal* m_threadLocal;
 	static thread_local ThreadLocal* m_threadLocal;
 	DynamicArray<ThreadLocal*> m_allThreadLocal; ///< The Tracer should know about all the ThreadLocal.
 	DynamicArray<ThreadLocal*> m_allThreadLocal; ///< The Tracer should know about all the ThreadLocal.
-	Mutex m_threadLocalMtx;
+	Mutex m_allThreadLocalMtx;
+
+	Bool m_enabled = false;
 
 
 	/// Get the thread local ThreadLocal structure.
 	/// Get the thread local ThreadLocal structure.
+	/// @note Thread-safe.
 	ThreadLocal& getThreadLocal();
 	ThreadLocal& getThreadLocal();
 
 
-	/// Gather all counters from all the threads.
-	void gatherCounters(FlushCtx& ctx);
+	/// Get or create a new chunk.
+	Chunk& getOrCreateChunk(ThreadLocal& tlocal);
+};
 
 
-	/// Gather the events from all the threads.
-	void gatherEvents(FlushCtx& ctx);
+/// The global tracer.
+using TracerSingleton = SingletonInit<Tracer>;
 
 
-	/// Dump the counters to a CSV file
-	Error writeCounterCsv(const FlushCtx& ctx);
+/// Scoped tracer event.
+class TracerScopedEvent
+{
+public:
+	TracerScopedEvent(const char* name)
+		: m_name(name)
+		, m_tracer(&TracerSingleton::get())
+	{
+		m_handle = m_tracer->beginEvent();
+	}
 
 
-	/// Dump the events and the counters to a chrome trace file.
-	Error writeTraceJson(const FlushCtx& ctx);
+	~TracerScopedEvent()
+	{
+		m_tracer->endEvent(m_name, m_handle);
+	}
 
 
-	static void getSpreadsheetColumnName(U column, Array<char, 3>& arr);
+private:
+	const char* m_name;
+	TracerEventHandle m_handle;
+	Tracer* m_tracer;
 };
 };
+
+#if ANKI_ENABLE_TRACE
+#	define ANKI_TRACE_SCOPED_EVENT(name_) TracerScopedEvent _tse##name_(#	name_)
+#	define ANKI_TRACE_INC_COUNTER(name_, val_) TracerSingleton::get().incrementCounter(#	name_, val_)
+#else
+#	define ANKI_TRACE_SCOPED_EVENT(name_) ((void)0)
+#	define ANKI_TRACE_INC_COUNTER(name_, val_) ((void)0)
+#endif
 /// @}
 /// @}
 
 
 } // end namespace anki
 } // end namespace anki

+ 0 - 159
src/anki/util/Tracer2.cpp

@@ -1,159 +0,0 @@
-// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <anki/util/Tracer2.h>
-#include <anki/util/HighRezTimer.h>
-#include <anki/util/HashMap.h>
-#include <anki/util/List.h>
-
-namespace anki
-{
-
-class Tracer2::Chunk : public IntrusiveListEnabled<Chunk>
-{
-public:
-	Array<Tracer2Event, EVENTS_PER_CHUNK> m_events;
-	U32 m_eventCount = 0;
-	Array<Tracer2Counter, COUNTERS_PER_CHUNK> m_counters;
-	U32 m_counterCount = 0;
-};
-
-/// Thread local storage.
-class alignas(ANKI_CACHE_LINE_SIZE) Tracer2::ThreadLocal
-{
-public:
-	ThreadId m_tid ANKI_DEBUG_CODE(= 0);
-
-	Chunk* m_currentChunk ANKI_DEBUG_CODE(= nullptr);
-	IntrusiveList<Chunk> m_allChunks;
-	SpinLock m_currentChunkLock;
-};
-
-thread_local Tracer2::ThreadLocal* Tracer2::m_threadLocal = nullptr;
-
-Tracer2::~Tracer2()
-{
-	// TODO
-}
-
-Tracer2::ThreadLocal& Tracer2::getThreadLocal()
-{
-	ThreadLocal* out = m_threadLocal;
-	if(ANKI_UNLIKELY(out == nullptr))
-	{
-		out = m_alloc.newInstance<ThreadLocal>();
-		out->m_tid = Thread::getCurrentThreadId();
-		out->m_currentChunk = nullptr;
-		m_threadLocal = out;
-
-		// Store it
-		LockGuard<Mutex> lock(m_allThreadLocalMtx);
-		m_allThreadLocal.emplaceBack(m_alloc, out);
-	}
-
-	return *out;
-}
-
-Tracer2::Chunk& Tracer2::getOrCreateChunk(ThreadLocal& tlocal)
-{
-	Chunk* out;
-
-	if(tlocal.m_currentChunk && tlocal.m_currentChunk->m_eventCount + 1 < EVENTS_PER_CHUNK
-		&& tlocal.m_currentChunk->m_counterCount + 1 < COUNTERS_PER_CHUNK)
-	{
-		// There is a chunk and it has enough space
-		out = tlocal.m_currentChunk;
-	}
-	else
-	{
-		// Create a new
-		out = m_alloc.newInstance<Chunk>();
-		tlocal.m_currentChunk = out;
-		tlocal.m_allChunks.pushBack(out);
-	}
-
-	return *out;
-}
-
-Tracer2EventHandle Tracer2::beginEvent()
-{
-	Tracer2EventHandle out;
-	out.m_start = HighRezTimer::getCurrentTime();
-	return out;
-}
-
-void Tracer2::endEvent(const char* eventName, Tracer2EventHandle event)
-{
-	if(!m_enabled)
-	{
-		return;
-	}
-
-	// Get the time before the lock and everything
-	const Second timestamp = HighRezTimer::getCurrentTime();
-
-	ThreadLocal& tlocal = getThreadLocal();
-
-	// Write the event
-	LockGuard<SpinLock> lock(tlocal.m_currentChunkLock);
-	Chunk& chunk = getOrCreateChunk(tlocal);
-
-	ANKI_ASSERT(chunk.m_eventCount + 1 < EVENTS_PER_CHUNK);
-	Tracer2Event& writeEvent = chunk.m_events[chunk.m_eventCount++];
-	writeEvent.m_name = eventName;
-	writeEvent.m_start = event.m_start;
-	writeEvent.m_duration = timestamp - event.m_start;
-
-	// Write counter as well. In ns
-	ANKI_ASSERT(chunk.m_counterCount + 1 < COUNTERS_PER_CHUNK);
-	Tracer2Counter& writeCounter = chunk.m_counters[chunk.m_counterCount++];
-	writeCounter.m_name = eventName;
-	writeCounter.m_value = U64(writeEvent.m_duration * 1000000000.0);
-}
-
-void Tracer2::incrementCounter(const char* counterName, U64 value)
-{
-	if(!m_enabled)
-	{
-		return;
-	}
-
-	ThreadLocal& tlocal = getThreadLocal();
-
-	LockGuard<SpinLock> lock(tlocal.m_currentChunkLock);
-	Chunk& chunk = getOrCreateChunk(tlocal);
-
-	ANKI_ASSERT(chunk.m_counterCount + 1 < COUNTERS_PER_CHUNK);
-	Tracer2Counter& writeTo = chunk.m_counters[chunk.m_counterCount++];
-	writeTo.m_name = counterName;
-	writeTo.m_value = value;
-}
-
-void Tracer2::flush(Tracer2FlushCallback callback, void* callbackUserData)
-{
-	ANKI_ASSERT(callback);
-
-	LockGuard<Mutex> lock(m_allThreadLocalMtx);
-	for(ThreadLocal* tlocal : m_allThreadLocal)
-	{
-		LockGuard<SpinLock> lock2(tlocal->m_currentChunkLock);
-
-		while(!tlocal->m_allChunks.isEmpty())
-		{
-			Chunk* chunk = tlocal->m_allChunks.popFront();
-
-			callback(callbackUserData,
-				tlocal->m_tid,
-				WeakArray<Tracer2Event>(&chunk->m_events[0], chunk->m_eventCount),
-				WeakArray<Tracer2Counter>(&chunk->m_counters[0], chunk->m_counterCount));
-
-			m_alloc.deleteInstance(chunk);
-		}
-
-		tlocal->m_currentChunk = nullptr;
-	}
-}
-
-} // end namespace anki

+ 0 - 155
src/anki/util/Tracer2.h

@@ -1,155 +0,0 @@
-// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <anki/util/Thread.h>
-#include <anki/util/WeakArray.h>
-#include <anki/util/DynamicArray.h>
-#include <anki/util/Singleton.h>
-#include <anki/util/String.h>
-
-namespace anki
-{
-
-/// @addtogroup util_other
-/// @{
-
-/// @memberof Tracer
-class Tracer2EventHandle
-{
-	friend class Tracer2;
-
-private:
-	Second m_start;
-};
-
-/// @memberof Tracer
-class Tracer2Event
-{
-public:
-	CString m_name;
-	Second m_start;
-	Second m_duration;
-
-	Tracer2Event()
-	{
-		// No init
-	}
-};
-
-/// @memberof Tracer
-class Tracer2Counter
-{
-public:
-	CString m_name;
-	U64 m_value;
-
-	Tracer2Counter()
-	{
-		// No init
-	}
-};
-
-/// Tracer flush callback.
-/// @memberof Tracer
-using Tracer2FlushCallback = void (*)(
-	void* userData, ThreadId tid, ConstWeakArray<Tracer2Event> events, ConstWeakArray<Tracer2Counter> counters);
-
-/// Tracer.
-class Tracer2 : public NonCopyable
-{
-public:
-	Tracer2(GenericMemoryPoolAllocator<U8> alloc)
-		: m_alloc(alloc)
-	{
-	}
-
-	~Tracer2();
-
-	/// Begin a new event.
-	/// @note It's thread-safe.
-	ANKI_USE_RESULT Tracer2EventHandle beginEvent();
-
-	/// End the event that got started with beginEvent().
-	/// @note It's thread-safe.
-	void endEvent(const char* eventName, Tracer2EventHandle event);
-
-	/// Increment a counter.
-	/// @note It's thread-safe.
-	void incrementCounter(const char* counterName, U64 value);
-
-	/// Flush all counters and events and start clean. The callback will be called multiple times.
-	/// @note It's thread-safe.
-	void flush(Tracer2FlushCallback callback, void* callbackUserData);
-
-	Bool getEnabled() const
-	{
-		return m_enabled;
-	}
-
-	void setEnabled(Bool enabled)
-	{
-		m_enabled = enabled;
-	}
-
-private:
-	static constexpr U32 EVENTS_PER_CHUNK = 256;
-	static constexpr U32 COUNTERS_PER_CHUNK = 512;
-
-	class ThreadLocal;
-	class Chunk;
-
-	GenericMemoryPoolAllocator<U8> m_alloc;
-
-	static thread_local ThreadLocal* m_threadLocal;
-	DynamicArray<ThreadLocal*> m_allThreadLocal; ///< The Tracer should know about all the ThreadLocal.
-	Mutex m_allThreadLocalMtx;
-
-	Bool m_enabled = false;
-
-	/// Get the thread local ThreadLocal structure.
-	/// @note Thread-safe.
-	ThreadLocal& getThreadLocal();
-
-	/// Get or create a new chunk.
-	Chunk& getOrCreateChunk(ThreadLocal& tlocal);
-};
-
-/// The global tracer.
-using Tracer2Singleton = SingletonInit<Tracer2>;
-
-/// Scoped tracer event.
-class Tracer2ScopedEvent
-{
-public:
-	Tracer2ScopedEvent(const char* name)
-		: m_name(name)
-		, m_tracer(&Tracer2Singleton::get())
-	{
-		m_handle = m_tracer->beginEvent();
-	}
-
-	~Tracer2ScopedEvent()
-	{
-		m_tracer->endEvent(m_name, m_handle);
-	}
-
-private:
-	const char* m_name;
-	Tracer2EventHandle m_handle;
-	Tracer2* m_tracer;
-};
-
-#if ANKI_ENABLE_TRACE
-#	define ANKI_TRACE2_SCOPED_EVENT(name_) Tracer2ScopedEvent _tse##name_(#	name_)
-#	define ANKI_TRACE2_INC_COUNTER(name_, val_) Tracer2Singleton::get().increaseCounter(#	name_, val_)
-#else
-#	define ANKI_TRACE2_SCOPED_EVENT(name_) ((void)0)
-#	define ANKI_TRACE2_INC_COUNTER(name_) ((void)0)
-#endif
-/// @}
-
-} // end namespace anki

+ 28 - 25
tests/util/Tracer.cpp

@@ -5,48 +5,51 @@
 
 
 #include <tests/framework/Framework.h>
 #include <tests/framework/Framework.h>
 #include <anki/util/Tracer.h>
 #include <anki/util/Tracer.h>
+#include <anki/core/CoreTracer.h>
 #include <anki/util/HighRezTimer.h>
 #include <anki/util/HighRezTimer.h>
 
 
+#if ANKI_ENABLE_TRACE
 ANKI_TEST(Util, Tracer)
 ANKI_TEST(Util, Tracer)
 {
 {
 	HeapAllocator<U8> alloc(allocAligned, nullptr);
 	HeapAllocator<U8> alloc(allocAligned, nullptr);
-	Tracer tracer;
-	tracer.init(alloc);
+	CoreTracer tracer;
+	ANKI_TEST_EXPECT_NO_ERR(tracer.init(alloc, "./"));
 
 
 	// 1st frame
 	// 1st frame
-	tracer.newFrame(0);
-	ANKI_TEST_EXPECT_NO_ERR(tracer.flush("./0"));
+	tracer.flushFrame(0);
 
 
 	// 2nd frame
 	// 2nd frame
-	// 2 same events
-	tracer.newFrame(1);
+	// 2 events
+	{
+		ANKI_TRACE_SCOPED_EVENT(EVENT);
+		HighRezTimer::sleep(0.5);
+	}
 
 
-	auto handle0 = tracer.beginEvent();
-	HighRezTimer::sleep(0.5);
-	tracer.endEvent("event", handle0);
+	{
+		ANKI_TRACE_SCOPED_EVENT(EVENT);
+		HighRezTimer::sleep(0.25);
+	}
 
 
-	auto handle1 = tracer.beginEvent();
-	HighRezTimer::sleep(0.5);
-	tracer.endEvent("event", handle1);
+	tracer.flushFrame(1);
 
 
 	// 4rd frame
 	// 4rd frame
 	// 2 different events & non zero counter
 	// 2 different events & non zero counter
-	tracer.newFrame(3);
+	{
+		ANKI_TRACE_SCOPED_EVENT(EVENT);
+		HighRezTimer::sleep(0.5);
+	}
 
 
-	auto handle2 = tracer.beginEvent();
-	HighRezTimer::sleep(0.5);
-	tracer.endEvent("event", handle2);
+	{
+		ANKI_TRACE_SCOPED_EVENT(EVENT2);
+		HighRezTimer::sleep(0.25);
+	}
 
 
-	auto handle3 = tracer.beginEvent();
-	HighRezTimer::sleep(0.5);
-	tracer.endEvent("event2", handle3);
+	ANKI_TRACE_INC_COUNTER(COUNTER, 100);
 
 
-	tracer.increaseCounter("counter", 100);
+	tracer.flushFrame(3);
 
 
 	// 5th frame
 	// 5th frame
-	tracer.newFrame(4);
-	tracer.increaseCounter("counter", 150);
-	HighRezTimer::sleep(0.1);
-
-	ANKI_TEST_EXPECT_NO_ERR(tracer.flush("./1"));
+	ANKI_TRACE_INC_COUNTER(COUNTER, 150);
+	tracer.flushFrame(4);
 }
 }
+#endif