2
0
Эх сурвалжийг харах

Record frame num in view stats (#2908)

* Add 'frameNumber' to Frame struct

Previously, the frame number returned from bgfx::frame() was tracked separately in the Context. Now,
we store that information in the Frame. This will allow us to attach the frame number to ViewStats.

* Add frame number to ViewStats

When ViewStats are enabled, we tag each timer query with the current frame number, then include
that information in the final results. In this way, clients can correlate specific work that they
submitted to specific GPU timing information.

NOTE: Some backends not implemented, yet. They will always have 0 for frame number.
The ones which are implemented are:
 * OpenGL
 * Vulkan
 * D3D 9,11,12
 * Noop
jwdevel 3 жил өмнө
parent
commit
f1f77a6cd3

+ 2 - 0
bindings/bf/bgfx.bf

@@ -2189,6 +2189,7 @@ public static class bgfx
 		public int64 cpuTimeEnd;
 		public int64 cpuTimeEnd;
 		public int64 gpuTimeBegin;
 		public int64 gpuTimeBegin;
 		public int64 gpuTimeEnd;
 		public int64 gpuTimeEnd;
+		public uint32 gpuFrameNum;
 	}
 	}
 	
 	
 	[CRepr]
 	[CRepr]
@@ -2214,6 +2215,7 @@ public static class bgfx
 		public uint32 numCompute;
 		public uint32 numCompute;
 		public uint32 numBlit;
 		public uint32 numBlit;
 		public uint32 maxGpuLatency;
 		public uint32 maxGpuLatency;
+		public uint32 gpuFrameNum;
 		public uint16 numDynamicIndexBuffers;
 		public uint16 numDynamicIndexBuffers;
 		public uint16 numDynamicVertexBuffers;
 		public uint16 numDynamicVertexBuffers;
 		public uint16 numFrameBuffers;
 		public uint16 numFrameBuffers;

+ 2 - 0
bindings/cs/bgfx.cs

@@ -2157,6 +2157,7 @@ public static partial class bgfx
 		public long cpuTimeEnd;
 		public long cpuTimeEnd;
 		public long gpuTimeBegin;
 		public long gpuTimeBegin;
 		public long gpuTimeEnd;
 		public long gpuTimeEnd;
+		public uint gpuFrameNum;
 	}
 	}
 	
 	
 	public unsafe struct EncoderStats
 	public unsafe struct EncoderStats
@@ -2180,6 +2181,7 @@ public static partial class bgfx
 		public uint numCompute;
 		public uint numCompute;
 		public uint numBlit;
 		public uint numBlit;
 		public uint maxGpuLatency;
 		public uint maxGpuLatency;
+		public uint gpuFrameNum;
 		public ushort numDynamicIndexBuffers;
 		public ushort numDynamicIndexBuffers;
 		public ushort numDynamicVertexBuffers;
 		public ushort numDynamicVertexBuffers;
 		public ushort numFrameBuffers;
 		public ushort numFrameBuffers;

+ 2 - 0
bindings/d/types.d

@@ -1030,6 +1030,7 @@ struct bgfx_view_stats_t
 	long cpuTimeEnd; /// CPU (submit) end time.
 	long cpuTimeEnd; /// CPU (submit) end time.
 	long gpuTimeBegin; /// GPU begin time.
 	long gpuTimeBegin; /// GPU begin time.
 	long gpuTimeEnd; /// GPU end time.
 	long gpuTimeEnd; /// GPU end time.
+	uint gpuFrameNum; /// Frame which generated gpuTimeBegin, gpuTimeEnd.
 }
 }
 
 
 /// Encoder stats.
 /// Encoder stats.
@@ -1059,6 +1060,7 @@ struct bgfx_stats_t
 	uint numCompute; /// Number of compute calls submitted.
 	uint numCompute; /// Number of compute calls submitted.
 	uint numBlit; /// Number of blit calls submitted.
 	uint numBlit; /// Number of blit calls submitted.
 	uint maxGpuLatency; /// GPU driver latency.
 	uint maxGpuLatency; /// GPU driver latency.
+	uint gpuFrameNum; /// Frame which generated gpuTimeBegin, gpuTimeEnd.
 	ushort numDynamicIndexBuffers; /// Number of used dynamic index buffers.
 	ushort numDynamicIndexBuffers; /// Number of used dynamic index buffers.
 	ushort numDynamicVertexBuffers; /// Number of used dynamic vertex buffers.
 	ushort numDynamicVertexBuffers; /// Number of used dynamic vertex buffers.
 	ushort numFrameBuffers; /// Number of used frame buffers.
 	ushort numFrameBuffers; /// Number of used frame buffers.

+ 2 - 0
bindings/zig/bgfx.zig

@@ -1417,6 +1417,7 @@ pub const Init = extern struct {
         cpuTimeEnd: i64,
         cpuTimeEnd: i64,
         gpuTimeBegin: i64,
         gpuTimeBegin: i64,
         gpuTimeEnd: i64,
         gpuTimeEnd: i64,
+        gpuFrameNum: u32,
     };
     };
 
 
     pub const EncoderStats = extern struct {
     pub const EncoderStats = extern struct {
@@ -1438,6 +1439,7 @@ pub const Init = extern struct {
         numCompute: u32,
         numCompute: u32,
         numBlit: u32,
         numBlit: u32,
         maxGpuLatency: u32,
         maxGpuLatency: u32,
+        gpuFrameNum: u32,
         numDynamicIndexBuffers: u16,
         numDynamicIndexBuffers: u16,
         numDynamicVertexBuffers: u16,
         numDynamicVertexBuffers: u16,
         numFrameBuffers: u16,
         numFrameBuffers: u16,

+ 8 - 6
include/bgfx/bgfx.h

@@ -942,12 +942,13 @@ namespace bgfx
 	///
 	///
 	struct ViewStats
 	struct ViewStats
 	{
 	{
-		char    name[256];      //!< View name.
-		ViewId  view;           //!< View id.
-		int64_t cpuTimeBegin;   //!< CPU (submit) begin time.
-		int64_t cpuTimeEnd;     //!< CPU (submit) end time.
-		int64_t gpuTimeBegin;   //!< GPU begin time.
-		int64_t gpuTimeEnd;     //!< GPU end time.
+		char     name[256];      //!< View name.
+		ViewId   view;           //!< View id.
+		int64_t  cpuTimeBegin;   //!< CPU (submit) begin time.
+		int64_t  cpuTimeEnd;     //!< CPU (submit) end time.
+		int64_t  gpuTimeBegin;   //!< GPU begin time.
+		int64_t  gpuTimeEnd;     //!< GPU end time.
+		uint32_t gpuFrameNum;    //!< Frame which generated gpuTimeBegin, gpuTimeEnd.
 	};
 	};
 
 
 	/// Encoder stats.
 	/// Encoder stats.
@@ -985,6 +986,7 @@ namespace bgfx
 		uint32_t numCompute;                //!< Number of compute calls submitted.
 		uint32_t numCompute;                //!< Number of compute calls submitted.
 		uint32_t numBlit;                   //!< Number of blit calls submitted.
 		uint32_t numBlit;                   //!< Number of blit calls submitted.
 		uint32_t maxGpuLatency;             //!< GPU driver latency.
 		uint32_t maxGpuLatency;             //!< GPU driver latency.
+		uint32_t gpuFrameNum;               //<! Frame which generated gpuTimeBegin, gpuTimeEnd.
 
 
 		uint16_t numDynamicIndexBuffers;    //!< Number of used dynamic index buffers.
 		uint16_t numDynamicIndexBuffers;    //!< Number of used dynamic index buffers.
 		uint16_t numDynamicVertexBuffers;   //!< Number of used dynamic vertex buffers.
 		uint16_t numDynamicVertexBuffers;   //!< Number of used dynamic vertex buffers.

+ 2 - 0
include/bgfx/c99/bgfx.h

@@ -825,6 +825,7 @@ typedef struct bgfx_view_stats_s
     int64_t              cpuTimeEnd;         /** CPU (submit) end time.                   */
     int64_t              cpuTimeEnd;         /** CPU (submit) end time.                   */
     int64_t              gpuTimeBegin;       /** GPU begin time.                          */
     int64_t              gpuTimeBegin;       /** GPU begin time.                          */
     int64_t              gpuTimeEnd;         /** GPU end time.                            */
     int64_t              gpuTimeEnd;         /** GPU end time.                            */
+    uint32_t             gpuFrameNum;        /** Frame which generated gpuTimeBegin, gpuTimeEnd. */
 
 
 } bgfx_view_stats_t;
 } bgfx_view_stats_t;
 
 
@@ -860,6 +861,7 @@ typedef struct bgfx_stats_s
     uint32_t             numCompute;         /** Number of compute calls submitted.       */
     uint32_t             numCompute;         /** Number of compute calls submitted.       */
     uint32_t             numBlit;            /** Number of blit calls submitted.          */
     uint32_t             numBlit;            /** Number of blit calls submitted.          */
     uint32_t             maxGpuLatency;      /** GPU driver latency.                      */
     uint32_t             maxGpuLatency;      /** GPU driver latency.                      */
+    uint32_t             gpuFrameNum;        /** Frame which generated gpuTimeBegin, gpuTimeEnd. */
     uint16_t             numDynamicIndexBuffers; /** Number of used dynamic index buffers.    */
     uint16_t             numDynamicIndexBuffers; /** Number of used dynamic index buffers.    */
     uint16_t             numDynamicVertexBuffers; /** Number of used dynamic vertex buffers.   */
     uint16_t             numDynamicVertexBuffers; /** Number of used dynamic vertex buffers.   */
     uint16_t             numFrameBuffers;    /** Number of used frame buffers.            */
     uint16_t             numFrameBuffers;    /** Number of used frame buffers.            */

+ 2 - 0
scripts/bgfx.idl

@@ -910,6 +910,7 @@ struct.ViewStats
 	.cpuTimeEnd     "int64_t"   --- CPU (submit) end time.
 	.cpuTimeEnd     "int64_t"   --- CPU (submit) end time.
 	.gpuTimeBegin   "int64_t"   --- GPU begin time.
 	.gpuTimeBegin   "int64_t"   --- GPU begin time.
 	.gpuTimeEnd     "int64_t"   --- GPU end time.
 	.gpuTimeEnd     "int64_t"   --- GPU end time.
+	.gpuFrameNum    "uint32_t"  --- Frame which generated gpuTimeBegin, gpuTimeEnd.
 
 
 --- Encoder stats.
 --- Encoder stats.
 struct.EncoderStats
 struct.EncoderStats
@@ -937,6 +938,7 @@ struct.Stats
 	.numCompute              "uint32_t"      --- Number of compute calls submitted.
 	.numCompute              "uint32_t"      --- Number of compute calls submitted.
 	.numBlit                 "uint32_t"      --- Number of blit calls submitted.
 	.numBlit                 "uint32_t"      --- Number of blit calls submitted.
 	.maxGpuLatency           "uint32_t"      --- GPU driver latency.
 	.maxGpuLatency           "uint32_t"      --- GPU driver latency.
+	.gpuFrameNum             "uint32_t"      --- Frame which generated gpuTimeBegin, gpuTimeEnd.
 
 
 	.numDynamicIndexBuffers  "uint16_t"      --- Number of used dynamic index buffers.
 	.numDynamicIndexBuffers  "uint16_t"      --- Number of used dynamic index buffers.
 	.numDynamicVertexBuffers "uint16_t"      --- Number of used dynamic vertex buffers.
 	.numDynamicVertexBuffers "uint16_t"      --- Number of used dynamic vertex buffers.

+ 5 - 4
src/bgfx.cpp

@@ -1894,7 +1894,6 @@ namespace bgfx
 
 
 		m_exit    = false;
 		m_exit    = false;
 		m_flipped = true;
 		m_flipped = true;
-		m_frames  = 0;
 		m_debug   = BGFX_DEBUG_NONE;
 		m_debug   = BGFX_DEBUG_NONE;
 		m_frameTimeLast = bx::getHPCounter();
 		m_frameTimeLast = bx::getHPCounter();
 
 
@@ -2296,6 +2295,8 @@ namespace bgfx
 
 
 		m_submit->m_capture = _capture;
 		m_submit->m_capture = _capture;
 
 
+		uint32_t frameNum = m_submit->m_frameNum;
+
 		BGFX_PROFILER_SCOPE("bgfx/API thread frame", 0xff2040ff);
 		BGFX_PROFILER_SCOPE("bgfx/API thread frame", 0xff2040ff);
 		// wait for render thread to finish
 		// wait for render thread to finish
 		renderSemWait();
 		renderSemWait();
@@ -2303,7 +2304,7 @@ namespace bgfx
 
 
 		m_encoder[0].begin(m_submit, 0);
 		m_encoder[0].begin(m_submit, 0);
 
 
-		return m_frames;
+		return frameNum;
 	}
 	}
 
 
 	void Context::frameNoRenderWait()
 	void Context::frameNoRenderWait()
@@ -2346,8 +2347,8 @@ namespace bgfx
 			renderFrame();
 			renderFrame();
 		}
 		}
 
 
-		m_frames++;
-		m_submit->start();
+		uint32_t nextFrameNum = m_render->m_frameNum + 1;
+		m_submit->start(nextFrameNum);
 
 
 		bx::memSet(m_seq, 0, sizeof(m_seq) );
 		bx::memSet(m_seq, 0, sizeof(m_seq) );
 
 

+ 8 - 4
src/bgfx_p.h

@@ -2076,6 +2076,7 @@ namespace bgfx
 		Frame()
 		Frame()
 			: m_waitSubmit(0)
 			: m_waitSubmit(0)
 			, m_waitRender(0)
 			, m_waitRender(0)
+			, m_frameNum(0)
 			, m_capture(false)
 			, m_capture(false)
 		{
 		{
 			SortKey term;
 			SortKey term;
@@ -2109,7 +2110,7 @@ namespace bgfx
 			}
 			}
 
 
 			reset();
 			reset();
-			start();
+			start(0);
 			m_textVideoMem = BX_NEW(g_allocator, TextVideoMem);
 			m_textVideoMem = BX_NEW(g_allocator, TextVideoMem);
 		}
 		}
 
 
@@ -2126,12 +2127,12 @@ namespace bgfx
 
 
 		void reset()
 		void reset()
 		{
 		{
-			start();
+			start(0);
 			finish();
 			finish();
 			resetFreeHandles();
 			resetFreeHandles();
 		}
 		}
 
 
-		void start()
+		void start(uint32_t frameNum)
 		{
 		{
 			m_perfStats.transientVbUsed = m_vboffset;
 			m_perfStats.transientVbUsed = m_vboffset;
 			m_perfStats.transientIbUsed = m_iboffset;
 			m_perfStats.transientIbUsed = m_iboffset;
@@ -2145,6 +2146,7 @@ namespace bgfx
 			m_cmdPost.start();
 			m_cmdPost.start();
 			m_capture = false;
 			m_capture = false;
 			m_numScreenShots = 0;
 			m_numScreenShots = 0;
+			m_frameNum = frameNum;
 		}
 		}
 
 
 		void finish()
 		void finish()
@@ -2353,6 +2355,8 @@ namespace bgfx
 		int64_t m_waitSubmit;
 		int64_t m_waitSubmit;
 		int64_t m_waitRender;
 		int64_t m_waitRender;
 
 
+		uint32_t m_frameNum;
+
 		bool m_capture;
 		bool m_capture;
 	};
 	};
 
 
@@ -4523,7 +4527,7 @@ namespace bgfx
 			cmdbuf.write(_handle);
 			cmdbuf.write(_handle);
 			cmdbuf.write(_data);
 			cmdbuf.write(_data);
 			cmdbuf.write(_mip);
 			cmdbuf.write(_mip);
-			return m_frames + 2;
+			return m_submit->m_frameNum + 2;
 		}
 		}
 
 
 		void resizeTexture(TextureHandle _handle, uint16_t _width, uint16_t _height, uint8_t _numMips, uint16_t _numLayers)
 		void resizeTexture(TextureHandle _handle, uint16_t _width, uint16_t _height, uint8_t _numMips, uint16_t _numLayers)

+ 2 - 1
src/renderer.h

@@ -501,7 +501,7 @@ namespace bgfx
 				ViewStats& viewStats = m_frame->m_perfStats.viewStats[m_numViews];
 				ViewStats& viewStats = m_frame->m_perfStats.viewStats[m_numViews];
 				viewStats.cpuTimeBegin = bx::getHPCounter();
 				viewStats.cpuTimeBegin = bx::getHPCounter();
 
 
-				m_queryIdx = m_gpuTimer.begin(_view);
+				m_queryIdx = m_gpuTimer.begin(_view, m_frame->m_frameNum);
 
 
 				viewStats.view = ViewId(_view);
 				viewStats.view = ViewId(_view);
 				bx::strCopy(viewStats.name
 				bx::strCopy(viewStats.name
@@ -524,6 +524,7 @@ namespace bgfx
 				viewStats.cpuTimeEnd = bx::getHPCounter();
 				viewStats.cpuTimeEnd = bx::getHPCounter();
 				viewStats.gpuTimeBegin = result.m_begin;
 				viewStats.gpuTimeBegin = result.m_begin;
 				viewStats.gpuTimeEnd = result.m_end;
 				viewStats.gpuTimeEnd = result.m_end;
+				viewStats.gpuFrameNum = result.m_frameNum;
 
 
 				++m_numViews;
 				++m_numViews;
 				m_queryIdx = UINT32_MAX;
 				m_queryIdx = UINT32_MAX;

+ 5 - 2
src/renderer_d3d11.cpp

@@ -5406,7 +5406,7 @@ namespace bgfx { namespace d3d11
 		}
 		}
 	}
 	}
 
 
-	uint32_t TimerQueryD3D11::begin(uint32_t _resultIdx)
+	uint32_t TimerQueryD3D11::begin(uint32_t _resultIdx, uint32_t _frameNum)
 	{
 	{
 		ID3D11DeviceContext* deviceCtx = s_renderD3D11->m_deviceCtx;
 		ID3D11DeviceContext* deviceCtx = s_renderD3D11->m_deviceCtx;
 
 
@@ -5422,6 +5422,7 @@ namespace bgfx { namespace d3d11
 		Query& query = m_query[idx];
 		Query& query = m_query[idx];
 		query.m_resultIdx = _resultIdx;
 		query.m_resultIdx = _resultIdx;
 		query.m_ready     = false;
 		query.m_ready     = false;
+		query.m_frameNum  = _frameNum;
 
 
 		deviceCtx->Begin(query.m_disjoint);
 		deviceCtx->Begin(query.m_disjoint);
 		deviceCtx->End(query.m_begin);
 		deviceCtx->End(query.m_begin);
@@ -5478,6 +5479,7 @@ namespace bgfx { namespace d3d11
 
 
 				Result& result = m_result[query.m_resultIdx];
 				Result& result = m_result[query.m_resultIdx];
 				--result.m_pending;
 				--result.m_pending;
+				result.m_frameNum = query.m_frameNum;
 
 
 				result.m_frequency = disjoint.Frequency;
 				result.m_frequency = disjoint.Frequency;
 				result.m_begin     = timeBegin;
 				result.m_begin     = timeBegin;
@@ -5664,7 +5666,7 @@ namespace bgfx { namespace d3d11
 
 
 		if (m_timerQuerySupport)
 		if (m_timerQuerySupport)
 		{
 		{
-			frameQueryIdx = m_gpuTimer.begin(BGFX_CONFIG_MAX_VIEWS);
+			frameQueryIdx = m_gpuTimer.begin(BGFX_CONFIG_MAX_VIEWS, _render->m_frameNum);
 		}
 		}
 
 
 		if (0 < _render->m_iboffset)
 		if (0 < _render->m_iboffset)
@@ -6583,6 +6585,7 @@ namespace bgfx { namespace d3d11
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.maxGpuLatency = maxGpuLatency;
 		perfStats.maxGpuLatency = maxGpuLatency;
+		perfStats.gpuFrameNum   = result.m_frameNum;
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		m_nvapi.getMemoryInfo(perfStats.gpuMemoryUsed, perfStats.gpuMemoryMax);
 		m_nvapi.getMemoryInfo(perfStats.gpuMemoryUsed, perfStats.gpuMemoryMax);
 
 

+ 4 - 1
src/renderer_d3d11.h

@@ -374,7 +374,7 @@ namespace bgfx { namespace d3d11
 
 
 		void postReset();
 		void postReset();
 		void preReset();
 		void preReset();
-		uint32_t begin(uint32_t _resultIdx);
+		uint32_t begin(uint32_t _resultIdx, uint32_t _frameNum);
 		void end(uint32_t _idx);
 		void end(uint32_t _idx);
 		bool update();
 		bool update();
 
 
@@ -384,6 +384,7 @@ namespace bgfx { namespace d3d11
 			ID3D11Query* m_begin;
 			ID3D11Query* m_begin;
 			ID3D11Query* m_end;
 			ID3D11Query* m_end;
 			uint32_t     m_resultIdx;
 			uint32_t     m_resultIdx;
+			uint32_t     m_frameNum;
 			bool         m_ready;
 			bool         m_ready;
 		};
 		};
 
 
@@ -395,12 +396,14 @@ namespace bgfx { namespace d3d11
 				m_end       = 0;
 				m_end       = 0;
 				m_frequency = 1;
 				m_frequency = 1;
 				m_pending   = 0;
 				m_pending   = 0;
+				m_frameNum  = 0;
 			}
 			}
 
 
 			uint64_t m_begin;
 			uint64_t m_begin;
 			uint64_t m_end;
 			uint64_t m_end;
 			uint64_t m_frequency;
 			uint64_t m_frequency;
 			uint32_t m_pending;
 			uint32_t m_pending;
+			uint32_t m_frameNum;
 		};
 		};
 
 
 		Result m_result[BGFX_CONFIG_MAX_VIEWS+1];
 		Result m_result[BGFX_CONFIG_MAX_VIEWS+1];

+ 5 - 2
src/renderer_d3d12.cpp

@@ -5882,7 +5882,7 @@ namespace bgfx { namespace d3d12
 		DX_RELEASE(m_readback, 0);
 		DX_RELEASE(m_readback, 0);
 	}
 	}
 
 
-	uint32_t TimerQueryD3D12::begin(uint32_t _resultIdx)
+	uint32_t TimerQueryD3D12::begin(uint32_t _resultIdx, uint32_t _frameNum)
 	{
 	{
 		while (0 == m_control.reserve(1) )
 		while (0 == m_control.reserve(1) )
 		{
 		{
@@ -5896,6 +5896,7 @@ namespace bgfx { namespace d3d12
 		Query& query = m_query[idx];
 		Query& query = m_query[idx];
 		query.m_resultIdx = _resultIdx;
 		query.m_resultIdx = _resultIdx;
 		query.m_ready     = false;
 		query.m_ready     = false;
+		query.m_frameNum  = _frameNum;
 
 
 		ID3D12GraphicsCommandList* commandList = s_renderD3D12->m_commandList;
 		ID3D12GraphicsCommandList* commandList = s_renderD3D12->m_commandList;
 
 
@@ -5957,6 +5958,7 @@ namespace bgfx { namespace d3d12
 
 
 			Result& result = m_result[query.m_resultIdx];
 			Result& result = m_result[query.m_resultIdx];
 			--result.m_pending;
 			--result.m_pending;
+			result.m_frameNum = query.m_frameNum;
 
 
 			uint32_t offset = idx * 2;
 			uint32_t offset = idx * 2;
 			result.m_begin  = m_queryResult[offset+0];
 			result.m_begin  = m_queryResult[offset+0];
@@ -6192,7 +6194,7 @@ namespace bgfx { namespace d3d12
 		int64_t timeBegin = bx::getHPCounter();
 		int64_t timeBegin = bx::getHPCounter();
 		int64_t captureElapsed = 0;
 		int64_t captureElapsed = 0;
 
 
-		uint32_t frameQueryIdx = m_gpuTimer.begin(BGFX_CONFIG_MAX_VIEWS);
+		uint32_t frameQueryIdx = m_gpuTimer.begin(BGFX_CONFIG_MAX_VIEWS, _render->m_frameNum);
 
 
 		if (0 < _render->m_iboffset)
 		if (0 < _render->m_iboffset)
 		{
 		{
@@ -7066,6 +7068,7 @@ namespace bgfx { namespace d3d12
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.maxGpuLatency = maxGpuLatency;
 		perfStats.maxGpuLatency = maxGpuLatency;
+		perfStats.gpuFrameNum   = result.m_frameNum;
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		perfStats.gpuMemoryMax  = -INT64_MAX;
 		perfStats.gpuMemoryMax  = -INT64_MAX;
 		perfStats.gpuMemoryUsed = -INT64_MAX;
 		perfStats.gpuMemoryUsed = -INT64_MAX;

+ 8 - 5
src/renderer_d3d12.h

@@ -517,29 +517,32 @@ namespace bgfx { namespace d3d12
 
 
 		void init();
 		void init();
 		void shutdown();
 		void shutdown();
-		uint32_t begin(uint32_t _resultIdx);
+		uint32_t begin(uint32_t _resultIdx, uint32_t _frameNum);
 		void end(uint32_t _idx);
 		void end(uint32_t _idx);
 		bool update();
 		bool update();
 
 
 		struct Query
 		struct Query
 		{
 		{
 			uint32_t m_resultIdx;
 			uint32_t m_resultIdx;
-			bool     m_ready;
+			uint32_t m_frameNum;
 			uint64_t m_fence;
 			uint64_t m_fence;
+			bool     m_ready;
 		};
 		};
 
 
 		struct Result
 		struct Result
 		{
 		{
 			void reset()
 			void reset()
 			{
 			{
-				m_begin     = 0;
-				m_end       = 0;
-				m_pending   = 0;
+				m_begin    = 0;
+				m_end      = 0;
+				m_pending  = 0;
+				m_frameNum = 0;
 			}
 			}
 
 
 			uint64_t m_begin;
 			uint64_t m_begin;
 			uint64_t m_end;
 			uint64_t m_end;
 			uint32_t m_pending;
 			uint32_t m_pending;
+			uint32_t m_frameNum;
 		};
 		};
 
 
 		uint64_t m_frequency;
 		uint64_t m_frequency;

+ 5 - 2
src/renderer_d3d9.cpp

@@ -3547,7 +3547,7 @@ namespace bgfx { namespace d3d9
 		}
 		}
 	}
 	}
 
 
-	uint32_t TimerQueryD3D9::begin(uint32_t _resultIdx)
+	uint32_t TimerQueryD3D9::begin(uint32_t _resultIdx, uint32_t _frameNum)
 	{
 	{
 		while (0 == m_control.reserve(1) )
 		while (0 == m_control.reserve(1) )
 		{
 		{
@@ -3561,6 +3561,7 @@ namespace bgfx { namespace d3d9
 		Query& query = m_query[idx];
 		Query& query = m_query[idx];
 		query.m_resultIdx = _resultIdx;
 		query.m_resultIdx = _resultIdx;
 		query.m_ready     = false;
 		query.m_ready     = false;
+		query.m_frameNum  = _frameNum;
 
 
 		query.m_disjoint->Issue(D3DISSUE_BEGIN);
 		query.m_disjoint->Issue(D3DISSUE_BEGIN);
 		query.m_begin->Issue(D3DISSUE_END);
 		query.m_begin->Issue(D3DISSUE_END);
@@ -3611,6 +3612,7 @@ namespace bgfx { namespace d3d9
 
 
 				Result& result = m_result[query.m_resultIdx];
 				Result& result = m_result[query.m_resultIdx];
 				--result.m_pending;
 				--result.m_pending;
+				result.m_frameNum = query.m_frameNum;
 
 
 				result.m_frequency = freq;
 				result.m_frequency = freq;
 				result.m_begin     = timeBegin;
 				result.m_begin     = timeBegin;
@@ -3761,7 +3763,7 @@ namespace bgfx { namespace d3d9
 		device->BeginScene();
 		device->BeginScene();
 		if (m_timerQuerySupport)
 		if (m_timerQuerySupport)
 		{
 		{
-			frameQueryIdx = m_gpuTimer.begin(BGFX_CONFIG_MAX_VIEWS);
+			frameQueryIdx = m_gpuTimer.begin(BGFX_CONFIG_MAX_VIEWS, _render->m_frameNum);
 		}
 		}
 
 
 		if (0 < _render->m_iboffset)
 		if (0 < _render->m_iboffset)
@@ -4456,6 +4458,7 @@ namespace bgfx { namespace d3d9
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.maxGpuLatency = maxGpuLatency;
 		perfStats.maxGpuLatency = maxGpuLatency;
+		perfStats.gpuFrameNum   = result.m_frameNum;
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		m_nvapi.getMemoryInfo(perfStats.gpuMemoryUsed, perfStats.gpuMemoryMax);
 		m_nvapi.getMemoryInfo(perfStats.gpuMemoryUsed, perfStats.gpuMemoryMax);
 
 

+ 4 - 1
src/renderer_d3d9.h

@@ -445,7 +445,7 @@ namespace bgfx { namespace d3d9
 
 
 		void postReset();
 		void postReset();
 		void preReset();
 		void preReset();
-		uint32_t begin(uint32_t _resultIdx);
+		uint32_t begin(uint32_t _resultIdx, uint32_t _frameNum);
 		void end(uint32_t _idx);
 		void end(uint32_t _idx);
 		bool update();
 		bool update();
 
 
@@ -456,6 +456,7 @@ namespace bgfx { namespace d3d9
 			IDirect3DQuery9* m_end;
 			IDirect3DQuery9* m_end;
 			IDirect3DQuery9* m_freq;
 			IDirect3DQuery9* m_freq;
 			uint32_t         m_resultIdx;
 			uint32_t         m_resultIdx;
+			uint32_t         m_frameNum;
 			bool             m_ready;
 			bool             m_ready;
 		};
 		};
 
 
@@ -467,12 +468,14 @@ namespace bgfx { namespace d3d9
 				m_end       = 0;
 				m_end       = 0;
 				m_frequency = 1;
 				m_frequency = 1;
 				m_pending   = 0;
 				m_pending   = 0;
+				m_frameNum  = 0;
 			}
 			}
 
 
 			uint64_t m_begin;
 			uint64_t m_begin;
 			uint64_t m_end;
 			uint64_t m_end;
 			uint64_t m_frequency;
 			uint64_t m_frequency;
 			uint32_t m_pending;
 			uint32_t m_pending;
+			uint32_t m_frameNum;
 		};
 		};
 
 
 		Result m_result[BGFX_CONFIG_MAX_VIEWS+1];
 		Result m_result[BGFX_CONFIG_MAX_VIEWS+1];

+ 2 - 1
src/renderer_gl.cpp

@@ -7518,7 +7518,7 @@ namespace bgfx { namespace gl
 		if (m_timerQuerySupport
 		if (m_timerQuerySupport
 		&&  !BX_ENABLED(BX_PLATFORM_OSX) )
 		&&  !BX_ENABLED(BX_PLATFORM_OSX) )
 		{
 		{
-			frameQueryIdx = m_gpuTimer.begin(BGFX_CONFIG_MAX_VIEWS);
+			frameQueryIdx = m_gpuTimer.begin(BGFX_CONFIG_MAX_VIEWS, _render->m_frameNum);
 		}
 		}
 
 
 		if (0 < _render->m_iboffset)
 		if (0 < _render->m_iboffset)
@@ -8657,6 +8657,7 @@ namespace bgfx { namespace gl
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.maxGpuLatency = maxGpuLatency;
 		perfStats.maxGpuLatency = maxGpuLatency;
+		perfStats.gpuFrameNum   = result.m_frameNum;
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		perfStats.gpuMemoryMax  = -INT64_MAX;
 		perfStats.gpuMemoryMax  = -INT64_MAX;
 		perfStats.gpuMemoryUsed = -INT64_MAX;
 		perfStats.gpuMemoryUsed = -INT64_MAX;

+ 10 - 4
src/renderer_gl.h

@@ -1529,6 +1529,7 @@ namespace bgfx { namespace gl
 			{
 			{
 				Query& query = m_query[ii];
 				Query& query = m_query[ii];
 				query.m_ready = false;
 				query.m_ready = false;
+				query.m_frameNum = 0;
 				GL_CHECK(glGenQueries(1, &query.m_begin) );
 				GL_CHECK(glGenQueries(1, &query.m_begin) );
 				GL_CHECK(glGenQueries(1, &query.m_end) );
 				GL_CHECK(glGenQueries(1, &query.m_end) );
 			}
 			}
@@ -1550,7 +1551,7 @@ namespace bgfx { namespace gl
 			}
 			}
 		}
 		}
 
 
-		uint32_t begin(uint32_t _resultIdx)
+		uint32_t begin(uint32_t _resultIdx, uint32_t _frameNum)
 		{
 		{
 			while (0 == m_control.reserve(1) )
 			while (0 == m_control.reserve(1) )
 			{
 			{
@@ -1563,6 +1564,7 @@ namespace bgfx { namespace gl
 			const uint32_t idx = m_control.m_current;
 			const uint32_t idx = m_control.m_current;
 			Query& query = m_query[idx];
 			Query& query = m_query[idx];
 			query.m_resultIdx = _resultIdx;
 			query.m_resultIdx = _resultIdx;
+			query.m_frameNum = _frameNum;
 			query.m_ready     = false;
 			query.m_ready     = false;
 
 
 			GL_CHECK(glQueryCounter(query.m_begin
 			GL_CHECK(glQueryCounter(query.m_begin
@@ -1611,6 +1613,7 @@ namespace bgfx { namespace gl
 
 
 					Result& result = m_result[query.m_resultIdx];
 					Result& result = m_result[query.m_resultIdx];
 					--result.m_pending;
 					--result.m_pending;
+					result.m_frameNum = query.m_frameNum;
 
 
 					GL_CHECK(glGetQueryObjectui64v(query.m_begin
 					GL_CHECK(glGetQueryObjectui64v(query.m_begin
 						, GL_QUERY_RESULT
 						, GL_QUERY_RESULT
@@ -1633,14 +1636,16 @@ namespace bgfx { namespace gl
 		{
 		{
 			void reset()
 			void reset()
 			{
 			{
-				m_begin   = 0;
-				m_end     = 0;
-				m_pending = 0;
+				m_begin    = 0;
+				m_end      = 0;
+				m_pending  = 0;
+				m_frameNum = 0;
 			}
 			}
 
 
 			uint64_t m_begin;
 			uint64_t m_begin;
 			uint64_t m_end;
 			uint64_t m_end;
 			uint32_t m_pending;
 			uint32_t m_pending;
+			uint32_t m_frameNum;
 		};
 		};
 
 
 		struct Query
 		struct Query
@@ -1648,6 +1653,7 @@ namespace bgfx { namespace gl
 			GLuint   m_begin;
 			GLuint   m_begin;
 			GLuint   m_end;
 			GLuint   m_end;
 			uint32_t m_resultIdx;
 			uint32_t m_resultIdx;
+			uint32_t m_frameNum;
 			bool     m_ready;
 			bool     m_ready;
 		};
 		};
 
 

+ 6 - 4
src/renderer_mtl.h

@@ -1133,7 +1133,7 @@ namespace bgfx { namespace mtl
 
 
 		void init();
 		void init();
 		void shutdown();
 		void shutdown();
-		uint32_t begin(uint32_t _resultIdx);
+		uint32_t begin(uint32_t _resultIdx, uint32_t _frameNum);
 		void end(uint32_t _idx);
 		void end(uint32_t _idx);
 		void addHandlers(CommandBuffer& _commandBuffer);
 		void addHandlers(CommandBuffer& _commandBuffer);
 		bool get();
 		bool get();
@@ -1142,14 +1142,16 @@ namespace bgfx { namespace mtl
 		{
 		{
 			void reset()
 			void reset()
 			{
 			{
-				m_begin     = 0;
-				m_end       = 0;
-				m_pending   = 0;
+				m_begin    = 0;
+				m_end      = 0;
+				m_pending  = 0;
+				m_frameNum = 0;
 			}
 			}
 
 
 			uint64_t m_begin;
 			uint64_t m_begin;
 			uint64_t m_end;
 			uint64_t m_end;
 			uint32_t m_pending;
 			uint32_t m_pending;
+			uint32_t m_frameNum; // TODO: implement (currently stays 0)
 		};
 		};
 
 
 		uint64_t m_begin;
 		uint64_t m_begin;

+ 3 - 1
src/renderer_mtl.mm

@@ -3677,9 +3677,10 @@ BX_STATIC_ASSERT(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNa
 	{
 	{
 	}
 	}
 
 
-	uint32_t TimerQueryMtl::begin(uint32_t _resultIdx)
+	uint32_t TimerQueryMtl::begin(uint32_t _resultIdx, uint32_t _frameNum)
 	{
 	{
 		BX_UNUSED(_resultIdx);
 		BX_UNUSED(_resultIdx);
+		BX_UNUSED(_frameNum);
 		return 0;
 		return 0;
 	}
 	}
 
 
@@ -4960,6 +4961,7 @@ BX_STATIC_ASSERT(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNa
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.maxGpuLatency = maxGpuLatency;
 		perfStats.maxGpuLatency = maxGpuLatency;
+		perfStats.gpuFrameNum   = 0; // TODO: take from TimerQueryMtl::Result
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		perfStats.gpuMemoryMax  = -INT64_MAX;
 		perfStats.gpuMemoryMax  = -INT64_MAX;
 		perfStats.gpuMemoryUsed = -INT64_MAX;
 		perfStats.gpuMemoryUsed = -INT64_MAX;

+ 1 - 0
src/renderer_noop.cpp

@@ -252,6 +252,7 @@ namespace bgfx { namespace noop
 			perfStats.gpuTimeBegin  = 0;
 			perfStats.gpuTimeBegin  = 0;
 			perfStats.gpuTimeEnd    = 0;
 			perfStats.gpuTimeEnd    = 0;
 			perfStats.gpuTimerFreq  = 1000000000;
 			perfStats.gpuTimerFreq  = 1000000000;
+			perfStats.gpuFrameNum   = 0;
 
 
 			bx::memSet(perfStats.numPrims, 0, sizeof(perfStats.numPrims) );
 			bx::memSet(perfStats.numPrims, 0, sizeof(perfStats.numPrims) );
 
 

+ 5 - 2
src/renderer_vk.cpp

@@ -5324,7 +5324,7 @@ VK_DESTROY
 		vkDestroy(m_readbackMemory);
 		vkDestroy(m_readbackMemory);
 	}
 	}
 
 
-	uint32_t TimerQueryVK::begin(uint32_t _resultIdx)
+	uint32_t TimerQueryVK::begin(uint32_t _resultIdx, uint32_t _frameNum)
 	{
 	{
 		while (0 == m_control.reserve(1) )
 		while (0 == m_control.reserve(1) )
 		{
 		{
@@ -5338,6 +5338,7 @@ VK_DESTROY
 		Query& query = m_query[idx];
 		Query& query = m_query[idx];
 		query.m_resultIdx = _resultIdx;
 		query.m_resultIdx = _resultIdx;
 		query.m_ready     = false;
 		query.m_ready     = false;
+		query.m_frameNum  = _frameNum;
 
 
 		const VkCommandBuffer commandBuffer = s_renderVK->m_commandBuffer;
 		const VkCommandBuffer commandBuffer = s_renderVK->m_commandBuffer;
 		const uint32_t offset = idx * 2 + 0;
 		const uint32_t offset = idx * 2 + 0;
@@ -5400,6 +5401,7 @@ VK_DESTROY
 
 
 			Result& result = m_result[query.m_resultIdx];
 			Result& result = m_result[query.m_resultIdx];
 			--result.m_pending;
 			--result.m_pending;
+			result.m_frameNum = query.m_frameNum;
 
 
 			uint32_t offset = idx * 2;
 			uint32_t offset = idx * 2;
 			result.m_begin  = m_queryResult[offset+0];
 			result.m_begin  = m_queryResult[offset+0];
@@ -8071,7 +8073,7 @@ VK_DESTROY
 
 
 		if (m_timerQuerySupport)
 		if (m_timerQuerySupport)
 		{
 		{
-			frameQueryIdx = m_gpuTimer.begin(BGFX_CONFIG_MAX_VIEWS);
+			frameQueryIdx = m_gpuTimer.begin(BGFX_CONFIG_MAX_VIEWS, _render->m_frameNum);
 		}
 		}
 
 
 		if (0 < _render->m_iboffset)
 		if (0 < _render->m_iboffset)
@@ -8939,6 +8941,7 @@ VK_DESTROY
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.maxGpuLatency = maxGpuLatency;
 		perfStats.maxGpuLatency = maxGpuLatency;
+		perfStats.gpuFrameNum   = result.m_frameNum;
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		perfStats.gpuMemoryMax  = gpuMemoryAvailable;
 		perfStats.gpuMemoryMax  = gpuMemoryAvailable;
 		perfStats.gpuMemoryUsed = gpuMemoryUsed;
 		perfStats.gpuMemoryUsed = gpuMemoryUsed;

+ 10 - 7
src/renderer_vk.h

@@ -524,7 +524,7 @@ VK_DESTROY_FUNC(DescriptorSet);
 
 
 		VkResult init();
 		VkResult init();
 		void shutdown();
 		void shutdown();
-		uint32_t begin(uint32_t _resultIdx);
+		uint32_t begin(uint32_t _resultIdx, uint32_t _frameNum);
 		void end(uint32_t _idx);
 		void end(uint32_t _idx);
 		bool update();
 		bool update();
 
 
@@ -532,21 +532,24 @@ VK_DESTROY_FUNC(DescriptorSet);
 		{
 		{
 			void reset()
 			void reset()
 			{
 			{
-				m_begin   = 0;
-				m_end     = 0;
-				m_pending = 0;
+				m_begin    = 0;
+				m_end      = 0;
+				m_pending  = 0;
+				m_frameNum = 0;
 			}
 			}
 
 
 			uint64_t m_begin;
 			uint64_t m_begin;
 			uint64_t m_end;
 			uint64_t m_end;
 			uint32_t m_pending;
 			uint32_t m_pending;
+			uint32_t m_frameNum;
 		};
 		};
 
 
 		struct Query
 		struct Query
 		{
 		{
-			uint32_t  m_resultIdx;
-			bool      m_ready;
-			uint64_t  m_completed;
+			uint32_t m_resultIdx;
+			uint32_t m_frameNum;
+			uint64_t m_completed;
+			bool     m_ready;
 		};
 		};
 
 
 		uint64_t m_frequency;
 		uint64_t m_frequency;

+ 3 - 1
src/renderer_webgpu.cpp

@@ -3987,9 +3987,10 @@ namespace bgfx { namespace webgpu
 	{
 	{
 	}
 	}
 
 
-	uint32_t TimerQueryWgpu::begin(uint32_t _resultIdx)
+	uint32_t TimerQueryWgpu::begin(uint32_t _resultIdx, uint32_t _frameNum)
 	{
 	{
 		BX_UNUSED(_resultIdx);
 		BX_UNUSED(_resultIdx);
+		BX_UNUSED(_frameNum);
 		return 0;
 		return 0;
 	}
 	}
 
 
@@ -4847,6 +4848,7 @@ namespace bgfx { namespace webgpu
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.maxGpuLatency = maxGpuLatency;
 		perfStats.maxGpuLatency = maxGpuLatency;
+		perfStats.gpuFrameNum   = result.m_frameNum;
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		perfStats.gpuMemoryMax  = -INT64_MAX;
 		perfStats.gpuMemoryMax  = -INT64_MAX;
 		perfStats.gpuMemoryUsed = -INT64_MAX;
 		perfStats.gpuMemoryUsed = -INT64_MAX;

+ 3 - 1
src/renderer_webgpu.h

@@ -509,7 +509,7 @@ namespace bgfx { namespace webgpu
 
 
 		void init();
 		void init();
 		void shutdown();
 		void shutdown();
-		uint32_t begin(uint32_t _resultIdx);
+		uint32_t begin(uint32_t _resultIdx, uint32_t _frameNum);
 		void end(uint32_t _idx);
 		void end(uint32_t _idx);
 		void addHandlers(wgpu::CommandBuffer& _commandBuffer);
 		void addHandlers(wgpu::CommandBuffer& _commandBuffer);
 		bool get();
 		bool get();
@@ -521,11 +521,13 @@ namespace bgfx { namespace webgpu
 				m_begin = 0;
 				m_begin = 0;
 				m_end = 0;
 				m_end = 0;
 				m_pending = 0;
 				m_pending = 0;
+				m_frameNum = 0;
 			}
 			}
 
 
 			uint64_t m_begin;
 			uint64_t m_begin;
 			uint64_t m_end;
 			uint64_t m_end;
 			uint32_t m_pending;
 			uint32_t m_pending;
+			uint32_t m_frameNum; // TODO: implement (currently stays 0)
 		};
 		};
 
 
 		uint64_t m_begin;
 		uint64_t m_begin;