Browse Source

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 years ago
parent
commit
f1f77a6cd3

+ 2 - 0
bindings/bf/bgfx.bf

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

+ 2 - 0
bindings/cs/bgfx.cs

@@ -2157,6 +2157,7 @@ public static partial class bgfx
 		public long cpuTimeEnd;
 		public long gpuTimeBegin;
 		public long gpuTimeEnd;
+		public uint gpuFrameNum;
 	}
 	
 	public unsafe struct EncoderStats
@@ -2180,6 +2181,7 @@ public static partial class bgfx
 		public uint numCompute;
 		public uint numBlit;
 		public uint maxGpuLatency;
+		public uint gpuFrameNum;
 		public ushort numDynamicIndexBuffers;
 		public ushort numDynamicVertexBuffers;
 		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 gpuTimeBegin; /// GPU begin time.
 	long gpuTimeEnd; /// GPU end time.
+	uint gpuFrameNum; /// Frame which generated gpuTimeBegin, gpuTimeEnd.
 }
 
 /// Encoder stats.
@@ -1059,6 +1060,7 @@ struct bgfx_stats_t
 	uint numCompute; /// Number of compute calls submitted.
 	uint numBlit; /// Number of blit calls submitted.
 	uint maxGpuLatency; /// GPU driver latency.
+	uint gpuFrameNum; /// Frame which generated gpuTimeBegin, gpuTimeEnd.
 	ushort numDynamicIndexBuffers; /// Number of used dynamic index buffers.
 	ushort numDynamicVertexBuffers; /// Number of used dynamic vertex 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,
         gpuTimeBegin: i64,
         gpuTimeEnd: i64,
+        gpuFrameNum: u32,
     };
 
     pub const EncoderStats = extern struct {
@@ -1438,6 +1439,7 @@ pub const Init = extern struct {
         numCompute: u32,
         numBlit: u32,
         maxGpuLatency: u32,
+        gpuFrameNum: u32,
         numDynamicIndexBuffers: u16,
         numDynamicVertexBuffers: u16,
         numFrameBuffers: u16,

+ 8 - 6
include/bgfx/bgfx.h

@@ -942,12 +942,13 @@ namespace bgfx
 	///
 	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.
@@ -985,6 +986,7 @@ namespace bgfx
 		uint32_t numCompute;                //!< Number of compute calls submitted.
 		uint32_t numBlit;                   //!< Number of blit calls submitted.
 		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 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              gpuTimeBegin;       /** GPU begin time.                          */
     int64_t              gpuTimeEnd;         /** GPU end time.                            */
+    uint32_t             gpuFrameNum;        /** Frame which generated gpuTimeBegin, gpuTimeEnd. */
 
 } bgfx_view_stats_t;
 
@@ -860,6 +861,7 @@ typedef struct bgfx_stats_s
     uint32_t             numCompute;         /** Number of compute calls submitted.       */
     uint32_t             numBlit;            /** Number of blit calls submitted.          */
     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             numDynamicVertexBuffers; /** Number of used dynamic vertex 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.
 	.gpuTimeBegin   "int64_t"   --- GPU begin time.
 	.gpuTimeEnd     "int64_t"   --- GPU end time.
+	.gpuFrameNum    "uint32_t"  --- Frame which generated gpuTimeBegin, gpuTimeEnd.
 
 --- Encoder stats.
 struct.EncoderStats
@@ -937,6 +938,7 @@ struct.Stats
 	.numCompute              "uint32_t"      --- Number of compute calls submitted.
 	.numBlit                 "uint32_t"      --- Number of blit calls submitted.
 	.maxGpuLatency           "uint32_t"      --- GPU driver latency.
+	.gpuFrameNum             "uint32_t"      --- Frame which generated gpuTimeBegin, gpuTimeEnd.
 
 	.numDynamicIndexBuffers  "uint16_t"      --- Number of used dynamic index 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_flipped = true;
-		m_frames  = 0;
 		m_debug   = BGFX_DEBUG_NONE;
 		m_frameTimeLast = bx::getHPCounter();
 
@@ -2296,6 +2295,8 @@ namespace bgfx
 
 		m_submit->m_capture = _capture;
 
+		uint32_t frameNum = m_submit->m_frameNum;
+
 		BGFX_PROFILER_SCOPE("bgfx/API thread frame", 0xff2040ff);
 		// wait for render thread to finish
 		renderSemWait();
@@ -2303,7 +2304,7 @@ namespace bgfx
 
 		m_encoder[0].begin(m_submit, 0);
 
-		return m_frames;
+		return frameNum;
 	}
 
 	void Context::frameNoRenderWait()
@@ -2346,8 +2347,8 @@ namespace bgfx
 			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) );
 

+ 8 - 4
src/bgfx_p.h

@@ -2076,6 +2076,7 @@ namespace bgfx
 		Frame()
 			: m_waitSubmit(0)
 			, m_waitRender(0)
+			, m_frameNum(0)
 			, m_capture(false)
 		{
 			SortKey term;
@@ -2109,7 +2110,7 @@ namespace bgfx
 			}
 
 			reset();
-			start();
+			start(0);
 			m_textVideoMem = BX_NEW(g_allocator, TextVideoMem);
 		}
 
@@ -2126,12 +2127,12 @@ namespace bgfx
 
 		void reset()
 		{
-			start();
+			start(0);
 			finish();
 			resetFreeHandles();
 		}
 
-		void start()
+		void start(uint32_t frameNum)
 		{
 			m_perfStats.transientVbUsed = m_vboffset;
 			m_perfStats.transientIbUsed = m_iboffset;
@@ -2145,6 +2146,7 @@ namespace bgfx
 			m_cmdPost.start();
 			m_capture = false;
 			m_numScreenShots = 0;
+			m_frameNum = frameNum;
 		}
 
 		void finish()
@@ -2353,6 +2355,8 @@ namespace bgfx
 		int64_t m_waitSubmit;
 		int64_t m_waitRender;
 
+		uint32_t m_frameNum;
+
 		bool m_capture;
 	};
 
@@ -4523,7 +4527,7 @@ namespace bgfx
 			cmdbuf.write(_handle);
 			cmdbuf.write(_data);
 			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)

+ 2 - 1
src/renderer.h

@@ -501,7 +501,7 @@ namespace bgfx
 				ViewStats& viewStats = m_frame->m_perfStats.viewStats[m_numViews];
 				viewStats.cpuTimeBegin = bx::getHPCounter();
 
-				m_queryIdx = m_gpuTimer.begin(_view);
+				m_queryIdx = m_gpuTimer.begin(_view, m_frame->m_frameNum);
 
 				viewStats.view = ViewId(_view);
 				bx::strCopy(viewStats.name
@@ -524,6 +524,7 @@ namespace bgfx
 				viewStats.cpuTimeEnd = bx::getHPCounter();
 				viewStats.gpuTimeBegin = result.m_begin;
 				viewStats.gpuTimeEnd = result.m_end;
+				viewStats.gpuFrameNum = result.m_frameNum;
 
 				++m_numViews;
 				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;
 
@@ -5422,6 +5422,7 @@ namespace bgfx { namespace d3d11
 		Query& query = m_query[idx];
 		query.m_resultIdx = _resultIdx;
 		query.m_ready     = false;
+		query.m_frameNum  = _frameNum;
 
 		deviceCtx->Begin(query.m_disjoint);
 		deviceCtx->End(query.m_begin);
@@ -5478,6 +5479,7 @@ namespace bgfx { namespace d3d11
 
 				Result& result = m_result[query.m_resultIdx];
 				--result.m_pending;
+				result.m_frameNum = query.m_frameNum;
 
 				result.m_frequency = disjoint.Frequency;
 				result.m_begin     = timeBegin;
@@ -5664,7 +5666,7 @@ namespace bgfx { namespace d3d11
 
 		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)
@@ -6583,6 +6585,7 @@ namespace bgfx { namespace d3d11
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.maxGpuLatency = maxGpuLatency;
+		perfStats.gpuFrameNum   = result.m_frameNum;
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		m_nvapi.getMemoryInfo(perfStats.gpuMemoryUsed, perfStats.gpuMemoryMax);
 

+ 4 - 1
src/renderer_d3d11.h

@@ -374,7 +374,7 @@ namespace bgfx { namespace d3d11
 
 		void postReset();
 		void preReset();
-		uint32_t begin(uint32_t _resultIdx);
+		uint32_t begin(uint32_t _resultIdx, uint32_t _frameNum);
 		void end(uint32_t _idx);
 		bool update();
 
@@ -384,6 +384,7 @@ namespace bgfx { namespace d3d11
 			ID3D11Query* m_begin;
 			ID3D11Query* m_end;
 			uint32_t     m_resultIdx;
+			uint32_t     m_frameNum;
 			bool         m_ready;
 		};
 
@@ -395,12 +396,14 @@ namespace bgfx { namespace d3d11
 				m_end       = 0;
 				m_frequency = 1;
 				m_pending   = 0;
+				m_frameNum  = 0;
 			}
 
 			uint64_t m_begin;
 			uint64_t m_end;
 			uint64_t m_frequency;
 			uint32_t m_pending;
+			uint32_t m_frameNum;
 		};
 
 		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);
 	}
 
-	uint32_t TimerQueryD3D12::begin(uint32_t _resultIdx)
+	uint32_t TimerQueryD3D12::begin(uint32_t _resultIdx, uint32_t _frameNum)
 	{
 		while (0 == m_control.reserve(1) )
 		{
@@ -5896,6 +5896,7 @@ namespace bgfx { namespace d3d12
 		Query& query = m_query[idx];
 		query.m_resultIdx = _resultIdx;
 		query.m_ready     = false;
+		query.m_frameNum  = _frameNum;
 
 		ID3D12GraphicsCommandList* commandList = s_renderD3D12->m_commandList;
 
@@ -5957,6 +5958,7 @@ namespace bgfx { namespace d3d12
 
 			Result& result = m_result[query.m_resultIdx];
 			--result.m_pending;
+			result.m_frameNum = query.m_frameNum;
 
 			uint32_t offset = idx * 2;
 			result.m_begin  = m_queryResult[offset+0];
@@ -6192,7 +6194,7 @@ namespace bgfx { namespace d3d12
 		int64_t timeBegin = bx::getHPCounter();
 		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)
 		{
@@ -7066,6 +7068,7 @@ namespace bgfx { namespace d3d12
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.maxGpuLatency = maxGpuLatency;
+		perfStats.gpuFrameNum   = result.m_frameNum;
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		perfStats.gpuMemoryMax  = -INT64_MAX;
 		perfStats.gpuMemoryUsed = -INT64_MAX;

+ 8 - 5
src/renderer_d3d12.h

@@ -517,29 +517,32 @@ namespace bgfx { namespace d3d12
 
 		void init();
 		void shutdown();
-		uint32_t begin(uint32_t _resultIdx);
+		uint32_t begin(uint32_t _resultIdx, uint32_t _frameNum);
 		void end(uint32_t _idx);
 		bool update();
 
 		struct Query
 		{
 			uint32_t m_resultIdx;
-			bool     m_ready;
+			uint32_t m_frameNum;
 			uint64_t m_fence;
+			bool     m_ready;
 		};
 
 		struct Result
 		{
 			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_end;
 			uint32_t m_pending;
+			uint32_t m_frameNum;
 		};
 
 		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) )
 		{
@@ -3561,6 +3561,7 @@ namespace bgfx { namespace d3d9
 		Query& query = m_query[idx];
 		query.m_resultIdx = _resultIdx;
 		query.m_ready     = false;
+		query.m_frameNum  = _frameNum;
 
 		query.m_disjoint->Issue(D3DISSUE_BEGIN);
 		query.m_begin->Issue(D3DISSUE_END);
@@ -3611,6 +3612,7 @@ namespace bgfx { namespace d3d9
 
 				Result& result = m_result[query.m_resultIdx];
 				--result.m_pending;
+				result.m_frameNum = query.m_frameNum;
 
 				result.m_frequency = freq;
 				result.m_begin     = timeBegin;
@@ -3761,7 +3763,7 @@ namespace bgfx { namespace d3d9
 		device->BeginScene();
 		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)
@@ -4456,6 +4458,7 @@ namespace bgfx { namespace d3d9
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.maxGpuLatency = maxGpuLatency;
+		perfStats.gpuFrameNum   = result.m_frameNum;
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		m_nvapi.getMemoryInfo(perfStats.gpuMemoryUsed, perfStats.gpuMemoryMax);
 

+ 4 - 1
src/renderer_d3d9.h

@@ -445,7 +445,7 @@ namespace bgfx { namespace d3d9
 
 		void postReset();
 		void preReset();
-		uint32_t begin(uint32_t _resultIdx);
+		uint32_t begin(uint32_t _resultIdx, uint32_t _frameNum);
 		void end(uint32_t _idx);
 		bool update();
 
@@ -456,6 +456,7 @@ namespace bgfx { namespace d3d9
 			IDirect3DQuery9* m_end;
 			IDirect3DQuery9* m_freq;
 			uint32_t         m_resultIdx;
+			uint32_t         m_frameNum;
 			bool             m_ready;
 		};
 
@@ -467,12 +468,14 @@ namespace bgfx { namespace d3d9
 				m_end       = 0;
 				m_frequency = 1;
 				m_pending   = 0;
+				m_frameNum  = 0;
 			}
 
 			uint64_t m_begin;
 			uint64_t m_end;
 			uint64_t m_frequency;
 			uint32_t m_pending;
+			uint32_t m_frameNum;
 		};
 
 		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
 		&&  !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)
@@ -8657,6 +8657,7 @@ namespace bgfx { namespace gl
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.maxGpuLatency = maxGpuLatency;
+		perfStats.gpuFrameNum   = result.m_frameNum;
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		perfStats.gpuMemoryMax  = -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.m_ready = false;
+				query.m_frameNum = 0;
 				GL_CHECK(glGenQueries(1, &query.m_begin) );
 				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) )
 			{
@@ -1563,6 +1564,7 @@ namespace bgfx { namespace gl
 			const uint32_t idx = m_control.m_current;
 			Query& query = m_query[idx];
 			query.m_resultIdx = _resultIdx;
+			query.m_frameNum = _frameNum;
 			query.m_ready     = false;
 
 			GL_CHECK(glQueryCounter(query.m_begin
@@ -1611,6 +1613,7 @@ namespace bgfx { namespace gl
 
 					Result& result = m_result[query.m_resultIdx];
 					--result.m_pending;
+					result.m_frameNum = query.m_frameNum;
 
 					GL_CHECK(glGetQueryObjectui64v(query.m_begin
 						, GL_QUERY_RESULT
@@ -1633,14 +1636,16 @@ namespace bgfx { namespace gl
 		{
 			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_end;
 			uint32_t m_pending;
+			uint32_t m_frameNum;
 		};
 
 		struct Query
@@ -1648,6 +1653,7 @@ namespace bgfx { namespace gl
 			GLuint   m_begin;
 			GLuint   m_end;
 			uint32_t m_resultIdx;
+			uint32_t m_frameNum;
 			bool     m_ready;
 		};
 

+ 6 - 4
src/renderer_mtl.h

@@ -1133,7 +1133,7 @@ namespace bgfx { namespace mtl
 
 		void init();
 		void shutdown();
-		uint32_t begin(uint32_t _resultIdx);
+		uint32_t begin(uint32_t _resultIdx, uint32_t _frameNum);
 		void end(uint32_t _idx);
 		void addHandlers(CommandBuffer& _commandBuffer);
 		bool get();
@@ -1142,14 +1142,16 @@ namespace bgfx { namespace mtl
 		{
 			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_end;
 			uint32_t m_pending;
+			uint32_t m_frameNum; // TODO: implement (currently stays 0)
 		};
 
 		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(_frameNum);
 		return 0;
 	}
 
@@ -4960,6 +4961,7 @@ BX_STATIC_ASSERT(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNa
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.maxGpuLatency = maxGpuLatency;
+		perfStats.gpuFrameNum   = 0; // TODO: take from TimerQueryMtl::Result
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		perfStats.gpuMemoryMax  = -INT64_MAX;
 		perfStats.gpuMemoryUsed = -INT64_MAX;

+ 1 - 0
src/renderer_noop.cpp

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

+ 5 - 2
src/renderer_vk.cpp

@@ -5324,7 +5324,7 @@ VK_DESTROY
 		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) )
 		{
@@ -5338,6 +5338,7 @@ VK_DESTROY
 		Query& query = m_query[idx];
 		query.m_resultIdx = _resultIdx;
 		query.m_ready     = false;
+		query.m_frameNum  = _frameNum;
 
 		const VkCommandBuffer commandBuffer = s_renderVK->m_commandBuffer;
 		const uint32_t offset = idx * 2 + 0;
@@ -5400,6 +5401,7 @@ VK_DESTROY
 
 			Result& result = m_result[query.m_resultIdx];
 			--result.m_pending;
+			result.m_frameNum = query.m_frameNum;
 
 			uint32_t offset = idx * 2;
 			result.m_begin  = m_queryResult[offset+0];
@@ -8071,7 +8073,7 @@ VK_DESTROY
 
 		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)
@@ -8939,6 +8941,7 @@ VK_DESTROY
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.maxGpuLatency = maxGpuLatency;
+		perfStats.gpuFrameNum   = result.m_frameNum;
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		perfStats.gpuMemoryMax  = gpuMemoryAvailable;
 		perfStats.gpuMemoryUsed = gpuMemoryUsed;

+ 10 - 7
src/renderer_vk.h

@@ -524,7 +524,7 @@ VK_DESTROY_FUNC(DescriptorSet);
 
 		VkResult init();
 		void shutdown();
-		uint32_t begin(uint32_t _resultIdx);
+		uint32_t begin(uint32_t _resultIdx, uint32_t _frameNum);
 		void end(uint32_t _idx);
 		bool update();
 
@@ -532,21 +532,24 @@ VK_DESTROY_FUNC(DescriptorSet);
 		{
 			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_end;
 			uint32_t m_pending;
+			uint32_t m_frameNum;
 		};
 
 		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;

+ 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(_frameNum);
 		return 0;
 	}
 
@@ -4847,6 +4848,7 @@ namespace bgfx { namespace webgpu
 		perfStats.numCompute    = statsKeyType[1];
 		perfStats.numBlit       = _render->m_numBlitItems;
 		perfStats.maxGpuLatency = maxGpuLatency;
+		perfStats.gpuFrameNum   = result.m_frameNum;
 		bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );
 		perfStats.gpuMemoryMax  = -INT64_MAX;
 		perfStats.gpuMemoryUsed = -INT64_MAX;

+ 3 - 1
src/renderer_webgpu.h

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