Skip to content

Commit 488c519

Browse files
committed
fix: add opengl drift correction for gpu zones
1 parent e5aa8eb commit 488c519

1 file changed

Lines changed: 52 additions & 3 deletions

File tree

public/tracy/TracyOpenGL.hpp

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class GpuCtxScope
3434
#include <atomic>
3535
#include <assert.h>
3636
#include <stdlib.h>
37+
#include <chrono>
3738

3839
#include "Tracy.hpp"
3940
#include "../client/TracyProfiler.hpp"
@@ -106,6 +107,13 @@ class GpuCtx
106107
GLint bits;
107108
glGetQueryiv( GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, &bits );
108109

110+
// Seed periodic recalibration (see Recalibrate). Without this the single
111+
// anchor above is never refreshed and the GPU/CPU clocks drift apart
112+
// (measured ~33 ppm), so GPU zones slide off the CPU timeline over a
113+
// session. Advertising GpuContextCalibration tells the server to expect
114+
// GpuCalibration events and apply rate correction.
115+
m_prevCalibration = GetHostTimeNs();
116+
109117
const float period = 1.f;
110118
const auto thread = GetThreadHandle();
111119
TracyLfqPrepare( QueueType::GpuNewContext );
@@ -114,7 +122,7 @@ class GpuCtx
114122
MemWrite( &item->gpuNewContext.thread, thread );
115123
MemWrite( &item->gpuNewContext.period, period );
116124
MemWrite( &item->gpuNewContext.context, m_context );
117-
MemWrite( &item->gpuNewContext.flags, GpuContextFlags( 0 ) );
125+
MemWrite( &item->gpuNewContext.flags, GpuContextFlags( GpuContextCalibration ) );
118126
MemWrite( &item->gpuNewContext.type, GpuContextType::OpenGl );
119127

120128
#ifdef TRACY_ON_DEMAND
@@ -143,8 +151,6 @@ class GpuCtx
143151
{
144152
ZoneScopedC( Color::Red4 );
145153

146-
if( m_tail == m_head ) return;
147-
148154
#ifdef TRACY_ON_DEMAND
149155
if( !GetProfiler().IsConnected() )
150156
{
@@ -153,6 +159,13 @@ class GpuCtx
153159
}
154160
#endif
155161

162+
// Emit a fresh clock-pair periodically so the server can correct GPU/CPU
163+
// drift. Done before the (possibly early-returning) query drain so it
164+
// still runs on frames with no completed queries.
165+
Recalibrate();
166+
167+
if( m_tail == m_head ) return;
168+
156169
while( m_tail != m_head )
157170
{
158171
GLint available;
@@ -173,6 +186,40 @@ class GpuCtx
173186
}
174187

175188
private:
189+
// Monotonic host nanoseconds, used only for measuring the interval between
190+
// calibrations (cpuDelta). Independent of Profiler::GetTime() (raw TSC ticks)
191+
// on purpose — matches how the D3D12/Vulkan backends source cpuDelta from a
192+
// separate ns clock while reporting cpuTime from Profiler::GetTime().
193+
static tracy_force_inline int64_t GetHostTimeNs()
194+
{
195+
return std::chrono::duration_cast<std::chrono::nanoseconds>(
196+
std::chrono::steady_clock::now().time_since_epoch() ).count();
197+
}
198+
199+
// Re-anchor the GPU<->CPU mapping. OpenGL has no atomic dual-clock read
200+
// (unlike Vulkan's vkGetCalibratedTimestampsEXT), so we sample GL_TIMESTAMP
201+
// and the host clock back-to-back; the sub-microsecond gap is negligible
202+
// against a >=1s recalibration interval. The server uses (gpuTime, cpuDelta)
203+
// to derive the clock-rate ratio and continuously correct drift.
204+
tracy_force_inline void Recalibrate()
205+
{
206+
const int64_t hostNow = GetHostTimeNs();
207+
const int64_t delta = hostNow - m_prevCalibration;
208+
if( delta < 1000ll * 1000 * 1000 ) return; // throttle: ~once per second
209+
210+
int64_t tgpu;
211+
glGetInteger64v( GL_TIMESTAMP, &tgpu );
212+
const int64_t refCpu = Profiler::GetTime();
213+
m_prevCalibration = hostNow;
214+
215+
TracyLfqPrepare( QueueType::GpuCalibration );
216+
MemWrite( &item->gpuCalibration.gpuTime, tgpu );
217+
MemWrite( &item->gpuCalibration.cpuTime, refCpu );
218+
MemWrite( &item->gpuCalibration.cpuDelta, delta );
219+
MemWrite( &item->gpuCalibration.context, m_context );
220+
TracyLfqCommit;
221+
}
222+
176223
tracy_force_inline unsigned int NextQueryId()
177224
{
178225
const auto id = m_head;
@@ -196,6 +243,8 @@ class GpuCtx
196243

197244
unsigned int m_head;
198245
unsigned int m_tail;
246+
247+
int64_t m_prevCalibration; // host-ns timestamp of the last emitted calibration
199248
};
200249

201250
class GpuCtxScope

0 commit comments

Comments
 (0)