Browse Source

Add GPU statistics using timestamps

Panagiotis Christopoulos Charitos 6 years ago
parent
commit
49a5453f2f

+ 9 - 2
src/anki/core/App.cpp

@@ -75,6 +75,7 @@ public:
 	BufferedValue<Second> m_sceneUpdateTime;
 	BufferedValue<Second> m_sceneUpdateTime;
 	BufferedValue<Second> m_visTestsTime;
 	BufferedValue<Second> m_visTestsTime;
 	BufferedValue<Second> m_physicsTime;
 	BufferedValue<Second> m_physicsTime;
+	BufferedValue<Second> m_gpuTime;
 
 
 	PtrSize m_allocatedCpuMem = 0;
 	PtrSize m_allocatedCpuMem = 0;
 	U64 m_allocCount = 0;
 	U64 m_allocCount = 0;
@@ -116,7 +117,7 @@ public:
 			ImGui::SetWindowPos(Vec2(5.0f, 5.0f));
 			ImGui::SetWindowPos(Vec2(5.0f, 5.0f));
 			ImGui::SetWindowSize(Vec2(230.0f, 450.0f));
 			ImGui::SetWindowSize(Vec2(230.0f, 450.0f));
 
 
-			ImGui::Text("Time:");
+			ImGui::Text("CPU Time:");
 			labelTime(m_frameTime.get(flush), "Total frame");
 			labelTime(m_frameTime.get(flush), "Total frame");
 			labelTime(m_renderTime.get(flush) - m_lightBinTime.get(flush), "Renderer");
 			labelTime(m_renderTime.get(flush) - m_lightBinTime.get(flush), "Renderer");
 			labelTime(m_lightBinTime.get(false), "Light bin");
 			labelTime(m_lightBinTime.get(false), "Light bin");
@@ -124,6 +125,10 @@ public:
 			labelTime(m_visTestsTime.get(flush), "Visibility");
 			labelTime(m_visTestsTime.get(flush), "Visibility");
 			labelTime(m_physicsTime.get(flush), "Physics");
 			labelTime(m_physicsTime.get(flush), "Physics");
 
 
+			ImGui::Text("----");
+			ImGui::Text("GPU Time:");
+			labelTime(m_gpuTime.get(flush), "Total frame");
+
 			ImGui::Text("----");
 			ImGui::Text("----");
 			ImGui::Text("Memory:");
 			ImGui::Text("Memory:");
 			labelBytes(m_allocatedCpuMem, "Total CPU");
 			labelBytes(m_allocatedCpuMem, "Total CPU");
@@ -577,6 +582,7 @@ Error App::mainLoop()
 
 
 		// Render
 		// Render
 		TexturePtr presentableTex = m_gr->acquireNextPresentableTexture();
 		TexturePtr presentableTex = m_gr->acquireNextPresentableTexture();
+		m_renderer->setStatsEnabled(m_displayStats);
 		ANKI_CHECK(m_renderer->render(rqueue, presentableTex));
 		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.
 		// Pause and sync async loader. That will force all tasks before the pause to finish in this frame.
@@ -609,11 +615,12 @@ Error App::mainLoop()
 		{
 		{
 			StatsUi& statsUi = static_cast<StatsUi&>(*m_statsUi);
 			StatsUi& statsUi = static_cast<StatsUi&>(*m_statsUi);
 			statsUi.m_frameTime.set(frameTime);
 			statsUi.m_frameTime.set(frameTime);
-			statsUi.m_renderTime.set(m_renderer->getStats().m_renderingTime);
+			statsUi.m_renderTime.set(m_renderer->getStats().m_renderingCpuTime);
 			statsUi.m_lightBinTime.set(m_renderer->getStats().m_lightBinTime);
 			statsUi.m_lightBinTime.set(m_renderer->getStats().m_lightBinTime);
 			statsUi.m_sceneUpdateTime.set(m_scene->getStats().m_updateTime);
 			statsUi.m_sceneUpdateTime.set(m_scene->getStats().m_updateTime);
 			statsUi.m_visTestsTime.set(m_scene->getStats().m_visibilityTestsTime);
 			statsUi.m_visTestsTime.set(m_scene->getStats().m_visibilityTestsTime);
 			statsUi.m_physicsTime.set(m_scene->getStats().m_physicsUpdate);
 			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_allocatedCpuMem = m_memStats.m_allocatedMem.load();
 			statsUi.m_allocCount = m_memStats.m_allocCount.load();
 			statsUi.m_allocCount = m_memStats.m_allocCount.load();
 			statsUi.m_freeCount = m_memStats.m_freeCount.load();
 			statsUi.m_freeCount = m_memStats.m_freeCount.load();

+ 3 - 0
src/anki/gr/CommandBuffer.h

@@ -413,6 +413,9 @@ public:
 	/// End query.
 	/// End query.
 	void endOcclusionQuery(OcclusionQueryPtr query);
 	void endOcclusionQuery(OcclusionQueryPtr query);
 
 
+	/// Write a timestamp.
+	void writeTimestamp(TimestampQueryPtr query);
+
 	/// Append a second level command buffer.
 	/// Append a second level command buffer.
 	void pushSecondLevelCommandBuffer(CommandBufferPtr cmdb);
 	void pushSecondLevelCommandBuffer(CommandBufferPtr cmdb);
 
 

+ 3 - 3
src/anki/gr/OcclusionQuery.h

@@ -21,6 +21,9 @@ class OcclusionQuery : public GrObject
 public:
 public:
 	static const GrObjectType CLASS_TYPE = GrObjectType::OCCLUSION_QUERY;
 	static const GrObjectType CLASS_TYPE = GrObjectType::OCCLUSION_QUERY;
 
 
+	/// Get the occlusion query result. It won't block.
+	OcclusionQueryResult getResult() const;
+
 protected:
 protected:
 	/// Construct.
 	/// Construct.
 	OcclusionQuery(GrManager* manager, CString name)
 	OcclusionQuery(GrManager* manager, CString name)
@@ -33,9 +36,6 @@ protected:
 	{
 	{
 	}
 	}
 
 
-	/// Get the occlusion query result. It won't block.
-	OcclusionQueryResult getResult() const;
-
 private:
 private:
 	/// Allocate and initialize new instance.
 	/// Allocate and initialize new instance.
 	static ANKI_USE_RESULT OcclusionQuery* newInstance(GrManager* manager);
 	static ANKI_USE_RESULT OcclusionQuery* newInstance(GrManager* manager);

+ 50 - 2
src/anki/gr/RenderGraph.cpp

@@ -148,6 +148,8 @@ public:
 
 
 	DynamicArray<CommandBufferPtr> m_graphicsCmdbs;
 	DynamicArray<CommandBufferPtr> m_graphicsCmdbs;
 
 
+	Bool m_gatherStatistics = false;
+
 	BakeContext(const StackAllocator<U8>& alloc)
 	BakeContext(const StackAllocator<U8>& alloc)
 		: m_alloc(alloc)
 		: m_alloc(alloc)
 	{
 	{
@@ -673,6 +675,8 @@ RenderGraph::BakeContext* RenderGraph::newContext(const RenderGraphDescription&
 		ctx->m_buffers[buffIdx].m_buffer = descr.m_buffers[buffIdx].m_importedBuff;
 		ctx->m_buffers[buffIdx].m_buffer = descr.m_buffers[buffIdx].m_importedBuff;
 	}
 	}
 
 
+	ctx->m_gatherStatistics = descr.m_gatherStatistics;
+
 	return ctx;
 	return ctx;
 }
 }
 
 
@@ -749,6 +753,7 @@ void RenderGraph::initBatches()
 	U passesAssignedToBatchCount = 0;
 	U passesAssignedToBatchCount = 0;
 	const U passCount = m_ctx->m_passes.getSize();
 	const U passCount = m_ctx->m_passes.getSize();
 	ANKI_ASSERT(passCount > 0);
 	ANKI_ASSERT(passCount > 0);
+	Bool setTimestamp = m_ctx->m_gatherStatistics;
 	while(passesAssignedToBatchCount < passCount)
 	while(passesAssignedToBatchCount < passCount)
 	{
 	{
 		m_ctx->m_batches.emplaceBack(m_ctx->m_alloc);
 		m_ctx->m_batches.emplaceBack(m_ctx->m_alloc);
@@ -781,6 +786,17 @@ void RenderGraph::initBatches()
 			m_ctx->m_graphicsCmdbs.emplaceBack(m_ctx->m_alloc, cmdb);
 			m_ctx->m_graphicsCmdbs.emplaceBack(m_ctx->m_alloc, cmdb);
 
 
 			batch.m_cmdb = cmdb.get();
 			batch.m_cmdb = cmdb.get();
+
+			// Maybe write a timestamp
+			if(ANKI_UNLIKELY(setTimestamp))
+			{
+				setTimestamp = false;
+				TimestampQueryPtr query = getManager().newTimestampQuery();
+				cmdb->writeTimestamp(query);
+
+				m_statistics.m_nextTimestamp = (m_statistics.m_nextTimestamp + 1) % MAX_TIMESTAMPS_BUFFERED;
+				m_statistics.m_timestamps[m_statistics.m_nextTimestamp * 2] = query;
+			}
 		}
 		}
 		else
 		else
 		{
 		{
@@ -1190,9 +1206,19 @@ void RenderGraph::flush()
 {
 {
 	ANKI_TRACE_SCOPED_EVENT(GR_RENDER_GRAPH_FLUSH);
 	ANKI_TRACE_SCOPED_EVENT(GR_RENDER_GRAPH_FLUSH);
 
 
-	for(CommandBufferPtr& cmdb : m_ctx->m_graphicsCmdbs)
+	for(U i = 0; i < m_ctx->m_graphicsCmdbs.getSize(); ++i)
 	{
 	{
-		cmdb->flush();
+		// Maybe write a timestamp before flush
+		if(ANKI_UNLIKELY(m_ctx->m_gatherStatistics && i == m_ctx->m_graphicsCmdbs.getSize() - 1))
+		{
+			TimestampQueryPtr query = getManager().newTimestampQuery();
+			m_ctx->m_graphicsCmdbs[i]->writeTimestamp(query);
+
+			m_statistics.m_timestamps[m_statistics.m_nextTimestamp * 2 + 1] = query;
+		}
+
+		// Flush
+		m_ctx->m_graphicsCmdbs[i]->flush();
 	}
 	}
 }
 }
 
 
@@ -1253,6 +1279,28 @@ void RenderGraph::periodicCleanup()
 	}
 	}
 }
 }
 
 
+void RenderGraph::getStatistics(RenderGraphStatistics& statistics) const
+{
+	const U oldFrame = (m_statistics.m_nextTimestamp + 1) % MAX_TIMESTAMPS_BUFFERED;
+
+	if(m_statistics.m_timestamps[oldFrame * 2] && m_statistics.m_timestamps[oldFrame * 2 + 1])
+	{
+		Second start, end;
+		TimestampQueryResult res = m_statistics.m_timestamps[oldFrame * 2]->getResult(start);
+		ANKI_ASSERT(res == TimestampQueryResult::AVAILABLE);
+
+		res = m_statistics.m_timestamps[oldFrame * 2 + 1]->getResult(end);
+		ANKI_ASSERT(res == TimestampQueryResult::AVAILABLE);
+
+		const Second diff = end - start;
+		statistics.m_gpuTime = diff;
+	}
+	else
+	{
+		statistics.m_gpuTime = -1.0;
+	}
+}
+
 #if ANKI_DBG_RENDER_GRAPH
 #if ANKI_DBG_RENDER_GRAPH
 StringAuto RenderGraph::textureUsageToStr(StackAllocator<U8>& alloc, TextureUsageBit usage)
 StringAuto RenderGraph::textureUsageToStr(StackAllocator<U8>& alloc, TextureUsageBit usage)
 {
 {

+ 30 - 0
src/anki/gr/RenderGraph.h

@@ -11,6 +11,7 @@
 #include <anki/gr/Buffer.h>
 #include <anki/gr/Buffer.h>
 #include <anki/gr/GrManager.h>
 #include <anki/gr/GrManager.h>
 #include <anki/gr/Framebuffer.h>
 #include <anki/gr/Framebuffer.h>
+#include <anki/gr/TimestampQuery.h>
 #include <anki/gr/CommandBuffer.h>
 #include <anki/gr/CommandBuffer.h>
 #include <anki/util/HashMap.h>
 #include <anki/util/HashMap.h>
 #include <anki/util/BitSet.h>
 #include <anki/util/BitSet.h>
@@ -473,6 +474,12 @@ public:
 	/// Import a buffer.
 	/// Import a buffer.
 	RenderPassBufferHandle importBuffer(BufferPtr buff, BufferUsageBit usage);
 	RenderPassBufferHandle importBuffer(BufferPtr buff, BufferUsageBit usage);
 
 
+	/// Gather statistics.
+	void setStatisticsEnabled(Bool gather)
+	{
+		m_gatherStatistics = gather;
+	}
+
 private:
 private:
 	class Resource
 	class Resource
 	{
 	{
@@ -509,6 +516,14 @@ private:
 	DynamicArray<RenderPassDescriptionBase*> m_passes;
 	DynamicArray<RenderPassDescriptionBase*> m_passes;
 	DynamicArray<RT> m_renderTargets;
 	DynamicArray<RT> m_renderTargets;
 	DynamicArray<Buffer> m_buffers;
 	DynamicArray<Buffer> m_buffers;
+	Bool m_gatherStatistics = false;
+};
+
+/// Statistics.
+class RenderGraphStatistics
+{
+public:
+	Second m_gpuTime; ///< Time spent in the GPU.
 };
 };
 
 
 /// Accepts a descriptor of the frame's render passes and sets the dependencies between them.
 /// Accepts a descriptor of the frame's render passes and sets the dependencies between them.
@@ -561,6 +576,13 @@ public:
 	void reset();
 	void reset();
 	/// @}
 	/// @}
 
 
+	/// @name 5th step methods [OPTIONAL]
+	/// @{
+
+	/// Get some statistics.
+	void getStatistics(RenderGraphStatistics& statistics) const;
+	/// @}
+
 private:
 private:
 	static constexpr U PERIODIC_CLEANUP_EVERY = 60; ///< How many frames between cleanups.
 	static constexpr U PERIODIC_CLEANUP_EVERY = 60; ///< How many frames between cleanups.
 
 
@@ -594,6 +616,14 @@ private:
 	BakeContext* m_ctx = nullptr;
 	BakeContext* m_ctx = nullptr;
 	U64 m_version = 0;
 	U64 m_version = 0;
 
 
+	static constexpr U MAX_TIMESTAMPS_BUFFERED = MAX_FRAMES_IN_FLIGHT + 1;
+	class
+	{
+	public:
+		Array<TimestampQueryPtr, MAX_TIMESTAMPS_BUFFERED * 2> m_timestamps;
+		U8 m_nextTimestamp = 0;
+	} m_statistics;
+
 	RenderGraph(GrManager* manager, CString name);
 	RenderGraph(GrManager* manager, CString name);
 
 
 	~RenderGraph();
 	~RenderGraph();

+ 3 - 3
src/anki/gr/TimestampQuery.h

@@ -21,6 +21,9 @@ class TimestampQuery : public GrObject
 public:
 public:
 	static const GrObjectType CLASS_TYPE = GrObjectType::TIMESTAMP_QUERY;
 	static const GrObjectType CLASS_TYPE = GrObjectType::TIMESTAMP_QUERY;
 
 
+	/// Get the result if it's available. It won't block.
+	TimestampQueryResult getResult(Second& timestamp) const;
+
 protected:
 protected:
 	/// Construct.
 	/// Construct.
 	TimestampQuery(GrManager* manager, CString name)
 	TimestampQuery(GrManager* manager, CString name)
@@ -33,9 +36,6 @@ protected:
 	{
 	{
 	}
 	}
 
 
-	/// Get the result if it's available. It won't block.
-	TimestampQueryResult getResult(U64& timestamp) const;
-
 private:
 private:
 	/// Allocate and initialize new instance.
 	/// Allocate and initialize new instance.
 	static ANKI_USE_RESULT TimestampQuery* newInstance(GrManager* manager);
 	static ANKI_USE_RESULT TimestampQuery* newInstance(GrManager* manager);

+ 6 - 0
src/anki/gr/vulkan/CommandBuffer.cpp

@@ -376,6 +376,12 @@ void CommandBuffer::pushSecondLevelCommandBuffer(CommandBufferPtr cmdb)
 	self.pushSecondLevelCommandBuffer(cmdb);
 	self.pushSecondLevelCommandBuffer(cmdb);
 }
 }
 
 
+void CommandBuffer::writeTimestamp(TimestampQueryPtr query)
+{
+	ANKI_VK_SELF(CommandBufferImpl);
+	self.writeTimestampInternal(query);
+}
+
 Bool CommandBuffer::isEmpty() const
 Bool CommandBuffer::isEmpty() const
 {
 {
 	ANKI_VK_SELF_CONST(CommandBufferImpl);
 	ANKI_VK_SELF_CONST(CommandBufferImpl);

+ 2 - 0
src/anki/gr/vulkan/CommandBufferImpl.h

@@ -312,6 +312,8 @@ public:
 
 
 	void endOcclusionQuery(OcclusionQueryPtr query);
 	void endOcclusionQuery(OcclusionQueryPtr query);
 
 
+	void writeTimestampInternal(TimestampQueryPtr& query);
+
 	void generateMipmaps2d(TextureViewPtr texView);
 	void generateMipmaps2d(TextureViewPtr texView);
 
 
 	void clearTextureView(TextureViewPtr texView, const ClearValue& clearValue);
 	void clearTextureView(TextureViewPtr texView, const ClearValue& clearValue);

+ 14 - 0
src/anki/gr/vulkan/CommandBufferImpl.inl.h

@@ -7,6 +7,8 @@
 #include <anki/gr/vulkan/TextureImpl.h>
 #include <anki/gr/vulkan/TextureImpl.h>
 #include <anki/gr/OcclusionQuery.h>
 #include <anki/gr/OcclusionQuery.h>
 #include <anki/gr/vulkan/OcclusionQueryImpl.h>
 #include <anki/gr/vulkan/OcclusionQueryImpl.h>
+#include <anki/gr/TimestampQuery.h>
+#include <anki/gr/vulkan/TimestampQueryImpl.h>
 #include <anki/core/Trace.h>
 #include <anki/core/Trace.h>
 
 
 namespace anki
 namespace anki
@@ -399,6 +401,18 @@ inline void CommandBufferImpl::endOcclusionQuery(OcclusionQueryPtr query)
 	m_microCmdb->pushObjectRef(query);
 	m_microCmdb->pushObjectRef(query);
 }
 }
 
 
+inline void CommandBufferImpl::writeTimestampInternal(TimestampQueryPtr& query)
+{
+	commandCommon();
+
+	const VkQueryPool handle = static_cast<const TimestampQueryImpl&>(*query).m_handle.getQueryPool();
+	const U32 idx = static_cast<const TimestampQueryImpl&>(*query).m_handle.getQueryIndex();
+
+	ANKI_CMD(vkCmdWriteTimestamp(m_handle, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, handle, idx), ANY_OTHER_COMMAND);
+
+	m_microCmdb->pushObjectRef(query);
+}
+
 inline void CommandBufferImpl::clearTextureView(TextureViewPtr texView, const ClearValue& clearValue)
 inline void CommandBufferImpl::clearTextureView(TextureViewPtr texView, const ClearValue& clearValue)
 {
 {
 	commandCommon();
 	commandCommon();

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

@@ -49,7 +49,7 @@ Error QueryFactory::newQuery(MicroQuery& handle)
 
 
 		VkQueryPoolCreateInfo ci = {};
 		VkQueryPoolCreateInfo ci = {};
 		ci.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
 		ci.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
-		ci.queryType = VK_QUERY_TYPE_OCCLUSION;
+		ci.queryType = m_poolType;
 		ci.queryCount = MAX_SUB_ALLOCATIONS_PER_QUERY_CHUNK;
 		ci.queryCount = MAX_SUB_ALLOCATIONS_PER_QUERY_CHUNK;
 
 
 		ANKI_VK_CHECK(vkCreateQueryPool(m_dev, &ci, nullptr, &chunk->m_pool));
 		ANKI_VK_CHECK(vkCreateQueryPool(m_dev, &ci, nullptr, &chunk->m_pool));

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

@@ -21,7 +21,7 @@ TimestampQuery* TimestampQuery::newInstance(GrManager* manager)
 	return impl;
 	return impl;
 }
 }
 
 
-TimestampQueryResult TimestampQuery::getResult(U64& timestamp) const
+TimestampQueryResult TimestampQuery::getResult(Second& timestamp) const
 {
 {
 	return static_cast<const TimestampQueryImpl*>(this)->getResultInternal(timestamp);
 	return static_cast<const TimestampQueryImpl*>(this)->getResultInternal(timestamp);
 }
 }

+ 11 - 4
src/anki/gr/vulkan/TimestampQueryImpl.cpp

@@ -20,26 +20,33 @@ TimestampQueryImpl::~TimestampQueryImpl()
 Error TimestampQueryImpl::init()
 Error TimestampQueryImpl::init()
 {
 {
 	ANKI_CHECK(getGrManagerImpl().getTimestampQueryFactory().newQuery(m_handle));
 	ANKI_CHECK(getGrManagerImpl().getTimestampQueryFactory().newQuery(m_handle));
+
+	m_timestampPeriod = getGrManagerImpl().getPhysicalDeviceProperties().limits.timestampPeriod;
+
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
-TimestampQueryResult TimestampQueryImpl::getResultInternal(U64& timestamp) const
+TimestampQueryResult TimestampQueryImpl::getResultInternal(Second& timestamp) const
 {
 {
 	ANKI_ASSERT(m_handle);
 	ANKI_ASSERT(m_handle);
+	timestamp = -1.0;
 
 
 	VkResult res;
 	VkResult res;
+	U64 value;
 	ANKI_VK_CHECKF(res = vkGetQueryPoolResults(getDevice(),
 	ANKI_VK_CHECKF(res = vkGetQueryPoolResults(getDevice(),
 					   m_handle.getQueryPool(),
 					   m_handle.getQueryPool(),
 					   m_handle.getQueryIndex(),
 					   m_handle.getQueryIndex(),
 					   1,
 					   1,
-					   sizeof(timestamp),
-					   &timestamp,
-					   sizeof(timestamp),
+					   sizeof(value),
+					   &value,
+					   sizeof(value),
 					   VK_QUERY_RESULT_64_BIT));
 					   VK_QUERY_RESULT_64_BIT));
 
 
 	TimestampQueryResult qout = TimestampQueryResult::NOT_AVAILABLE;
 	TimestampQueryResult qout = TimestampQueryResult::NOT_AVAILABLE;
 	if(res == VK_SUCCESS)
 	if(res == VK_SUCCESS)
 	{
 	{
+		value *= m_timestampPeriod;
+		timestamp = Second(value) / Second(1000000000);
 		qout = TimestampQueryResult::AVAILABLE;
 		qout = TimestampQueryResult::AVAILABLE;
 	}
 	}
 	else if(res == VK_NOT_READY)
 	else if(res == VK_NOT_READY)

+ 4 - 1
src/anki/gr/vulkan/TimestampQueryImpl.h

@@ -32,7 +32,10 @@ public:
 	ANKI_USE_RESULT Error init();
 	ANKI_USE_RESULT Error init();
 
 
 	/// Get query result.
 	/// Get query result.
-	TimestampQueryResult getResultInternal(U64& timestamp) const;
+	TimestampQueryResult getResultInternal(Second& timestamp) const;
+
+private:
+	U64 m_timestampPeriod = 0;
 };
 };
 /// @}
 /// @}
 
 

+ 12 - 3
src/anki/renderer/MainRenderer.cpp

@@ -86,7 +86,8 @@ Error MainRenderer::render(RenderQueue& rqueue, TexturePtr presentTex)
 {
 {
 	ANKI_TRACE_SCOPED_EVENT(RENDER);
 	ANKI_TRACE_SCOPED_EVENT(RENDER);
 
 
-	m_stats.m_renderingTime = HighRezTimer::getCurrentTime();
+	m_r->setStatsEnabled(m_statsEnabled);
+	m_stats.m_renderingCpuTime = (m_statsEnabled) ? HighRezTimer::getCurrentTime() : -1.0;
 
 
 	// First thing, reset the temp mem pool
 	// First thing, reset the temp mem pool
 	m_frameAlloc.getMemoryPool().reset();
 	m_frameAlloc.getMemoryPool().reset();
@@ -95,6 +96,7 @@ Error MainRenderer::render(RenderQueue& rqueue, TexturePtr presentTex)
 	RenderingContext ctx(m_frameAlloc);
 	RenderingContext ctx(m_frameAlloc);
 	m_runCtx.m_ctx = &ctx;
 	m_runCtx.m_ctx = &ctx;
 	m_runCtx.m_secondaryTaskId.set(0);
 	m_runCtx.m_secondaryTaskId.set(0);
+	ctx.m_renderGraphDescr.setStatisticsEnabled(m_statsEnabled);
 
 
 	RenderTargetHandle presentRt = ctx.m_renderGraphDescr.importRenderTarget(presentTex, TextureUsageBit::NONE);
 	RenderTargetHandle presentRt = ctx.m_renderGraphDescr.importRenderTarget(presentTex, TextureUsageBit::NONE);
 
 
@@ -183,8 +185,15 @@ Error MainRenderer::render(RenderQueue& rqueue, TexturePtr presentTex)
 	m_r->finalize(ctx);
 	m_r->finalize(ctx);
 
 
 	// Stats
 	// Stats
-	static_cast<RendererStats&>(m_stats) = m_r->getStats();
-	m_stats.m_renderingTime = HighRezTimer::getCurrentTime() - m_stats.m_renderingTime;
+	if(m_statsEnabled)
+	{
+		static_cast<RendererStats&>(m_stats) = m_r->getStats();
+		m_stats.m_renderingCpuTime = HighRezTimer::getCurrentTime() - m_stats.m_renderingGpuTime;
+
+		RenderGraphStatistics rgraphStats;
+		m_rgraph->getStatistics(rgraphStats);
+		m_stats.m_renderingGpuTime = rgraphStats.m_gpuTime;
+	}
 
 
 	return Error::NONE;
 	return Error::NONE;
 }
 }

+ 8 - 1
src/anki/renderer/MainRenderer.h

@@ -25,7 +25,8 @@ class UiManager;
 class MainRendererStats : public RendererStats
 class MainRendererStats : public RendererStats
 {
 {
 public:
 public:
-	Second m_renderingTime ANKI_DEBUG_CODE(= 0.0);
+	Second m_renderingCpuTime ANKI_DEBUG_CODE(= -1.0);
+	Second m_renderingGpuTime ANKI_DEBUG_CODE(= -1.0);
 };
 };
 
 
 /// Main onscreen renderer
 /// Main onscreen renderer
@@ -62,6 +63,11 @@ public:
 		return *m_r;
 		return *m_r;
 	}
 	}
 
 
+	void setStatsEnabled(Bool enabled)
+	{
+		m_statsEnabled = enabled;
+	}
+
 	const MainRendererStats& getStats() const
 	const MainRendererStats& getStats() const
 	{
 	{
 		return m_stats;
 		return m_stats;
@@ -86,6 +92,7 @@ private:
 	RenderTargetDescription m_tmpRtDesc;
 	RenderTargetDescription m_tmpRtDesc;
 
 
 	MainRendererStats m_stats;
 	MainRendererStats m_stats;
+	Bool m_statsEnabled = false;
 
 
 	class
 	class
 	{
 	{

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

@@ -321,7 +321,7 @@ Error Renderer::populateRenderGraph(RenderingContext& ctx)
 	m_finalComposite->populateRenderGraph(ctx);
 	m_finalComposite->populateRenderGraph(ctx);
 
 
 	// Bin lights and update uniforms
 	// Bin lights and update uniforms
-	m_stats.m_lightBinTime = HighRezTimer::getCurrentTime();
+	m_stats.m_lightBinTime = (m_statsEnabled) ? HighRezTimer::getCurrentTime() : -1.0;
 	ClusterBinIn cin;
 	ClusterBinIn cin;
 	cin.m_renderQueue = ctx.m_renderQueue;
 	cin.m_renderQueue = ctx.m_renderQueue;
 	cin.m_tempAlloc = ctx.m_tempAllocator;
 	cin.m_tempAlloc = ctx.m_tempAllocator;
@@ -335,7 +335,7 @@ Error Renderer::populateRenderGraph(RenderingContext& ctx)
 	m_prevClustererMagicValues = ctx.m_clusterBinOut.m_shaderMagicValues;
 	m_prevClustererMagicValues = ctx.m_clusterBinOut.m_shaderMagicValues;
 
 
 	updateLightShadingUniforms(ctx);
 	updateLightShadingUniforms(ctx);
-	m_stats.m_lightBinTime = HighRezTimer::getCurrentTime() - m_stats.m_lightBinTime;
+	m_stats.m_lightBinTime = (m_statsEnabled) ? (HighRezTimer::getCurrentTime() - m_stats.m_lightBinTime) : -1.0;
 
 
 	return Error::NONE;
 	return Error::NONE;
 }
 }

+ 7 - 2
src/anki/renderer/Renderer.h

@@ -75,8 +75,7 @@ public:
 class RendererStats
 class RendererStats
 {
 {
 public:
 public:
-	U32 m_drawcallCount ANKI_DEBUG_CODE(= 0);
-	Second m_lightBinTime ANKI_DEBUG_CODE(= 0.0);
+	Second m_lightBinTime ANKI_DEBUG_CODE(= -1.0);
 };
 };
 
 
 class RendererPrecreatedSamplers
 class RendererPrecreatedSamplers
@@ -226,6 +225,11 @@ public:
 
 
 	void finalize(const RenderingContext& ctx);
 	void finalize(const RenderingContext& ctx);
 
 
+	void setStatsEnabled(Bool enable)
+	{
+		m_statsEnabled = enable;
+	}
+
 	const RendererStats& getStats() const
 	const RendererStats& getStats() const
 	{
 	{
 		return m_stats;
 		return m_stats;
@@ -429,6 +433,7 @@ private:
 	ShaderProgramResourcePtr m_clearTexComputeProg;
 	ShaderProgramResourcePtr m_clearTexComputeProg;
 
 
 	RendererStats m_stats;
 	RendererStats m_stats;
+	Bool m_statsEnabled = false;
 
 
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);