Add support for Vsync capture on Linux.

This commit is contained in:
Bartosz Taudul 2022-07-30 21:29:44 +02:00
parent 91b002267e
commit e0f813d9e9
No known key found for this signature in database
GPG Key ID: B7FE2008B7575DF3

View File

@ -661,6 +661,7 @@ enum TraceEventId
EventCacheMiss,
EventBranchRetired,
EventBranchMiss,
EventVsync,
EventContextSwitch,
EventWakeup,
};
@ -751,13 +752,17 @@ bool SysTraceStart( int64_t& samplingPeriod )
TracyDebug( "perf_event_paranoid: %i\n", paranoidLevel );
#endif
int switchId = -1, wakeupId = -1;
int switchId = -1, wakeupId = -1, vsyncId = -1;
const auto switchIdStr = ReadFile( "/sys/kernel/debug/tracing/events/sched/sched_switch/id" );
if( switchIdStr ) switchId = atoi( switchIdStr );
const auto wakeupIdStr = ReadFile( "/sys/kernel/debug/tracing/events/sched/sched_wakeup/id" );
if( wakeupIdStr ) wakeupId = atoi( wakeupIdStr );
const auto vsyncIdStr = ReadFile( "/sys/kernel/debug/tracing/events/drm/drm_vblank_event/id" );
if( vsyncIdStr ) vsyncId = atoi( vsyncIdStr );
TracyDebug( "sched_switch id: %i\nsched_wakeup id: %i\n", switchId, wakeupId );
TracyDebug( "sched_switch id: %i\n", switchId );
TracyDebug( "sched_wakeup id: %i\n", wakeupId );
TracyDebug( "drm_vblank_event id: %i\n", vsyncId );
#ifdef TRACY_NO_SAMPLE_RETIREMENT
const bool noRetirement = true;
@ -787,6 +792,13 @@ bool SysTraceStart( int64_t& samplingPeriod )
const bool noCtxSwitch = noCtxSwitchEnv && noCtxSwitchEnv[0] == '1';
#endif
#ifdef TRACY_NO_VSYNC_CAPTURE
const bool noVsync = true;
#else
const char* noVsyncEnv = GetEnvVar( "TRACY_NO_VSYNC_CAPTURE" );
const bool noVsync = noVsyncEnv && noVsyncEnv[0] == '1';
#endif
samplingPeriod = GetSamplingPeriod();
uint32_t currentPid = (uint32_t)getpid();
@ -797,7 +809,8 @@ bool SysTraceStart( int64_t& samplingPeriod )
2 + // CPU cycles + instructions retired
2 + // cache reference + miss
2 + // branch retired + miss
2 // context switches + wakeups
2 + // context switches + wakeups
1 // vsync
);
s_ring = (RingBuffer*)tracy_malloc( sizeof( RingBuffer ) * maxNumBuffers );
s_numBuffers = 0;
@ -973,6 +986,37 @@ bool SysTraceStart( int64_t& samplingPeriod )
s_ctxBufferIdx = s_numBuffers;
// vsync
if( !noVsync && vsyncId != -1 )
{
pe = {};
pe.type = PERF_TYPE_TRACEPOINT;
pe.size = sizeof( perf_event_attr );
pe.sample_period = 1;
pe.sample_type = PERF_SAMPLE_TIME | PERF_SAMPLE_RAW;
pe.disabled = 1;
pe.config = vsyncId;
#if !defined TRACY_HW_TIMER || !( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 )
pe.use_clockid = 1;
pe.clockid = CLOCK_MONOTONIC_RAW;
#endif
TracyDebug( "Setup vsync capture\n" );
for( int i=0; i<s_numCpus; i++ )
{
const int fd = perf_event_open( &pe, -1, i, -1, PERF_FLAG_FD_CLOEXEC );
if( fd != -1 )
{
new( s_ring+s_numBuffers ) RingBuffer( 64*1024, fd, EventVsync, i );
if( s_ring[s_numBuffers].IsValid() )
{
s_numBuffers++;
TracyDebug( " Core %i ok\n", i );
}
}
}
}
// context switches
if( !noCtxSwitch && switchId != -1 )
{
@ -1307,7 +1351,8 @@ void SysTraceWorker( void* ptr )
t0 = ring.ConvertTimeToTsc( t0 );
#endif
if( ring.GetId() == EventContextSwitch )
const auto rid = ring.GetId();
if( rid == EventContextSwitch )
{
// Layout:
// u64 time
@ -1375,10 +1420,8 @@ void SysTraceWorker( void* ptr )
TracyLfqCommit;
}
}
else
else if( rid == EventWakeup )
{
assert( ring.GetId() == EventWakeup );
// Layout:
// u64 time
// u32 size
@ -1400,6 +1443,40 @@ void SysTraceWorker( void* ptr )
MemWrite( &item->threadWakeup.thread, pid );
TracyLfqCommit;
}
else
{
assert( rid == EventVsync );
// Layout:
// u64 time
// u32 size
// u8 data[size]
// Data (not ABI stable):
// u8 hdr[8]
// i32 crtc
// u32 seq
// i64 ktime
// u8 high precision
offset += sizeof( perf_event_header ) + sizeof( uint64_t ) + sizeof( uint32_t ) + 8;
int32_t crtc;
ring.Read( &crtc, offset, sizeof( int32_t ) );
// Note: The timestamp value t0 might be off by a number of microseconds from the
// true hardware vblank event. The ktime value should be used instead, but it is
// measured in CLOCK_MONOTONIC time. Tracy only supports the timestamp counter
// register (TSC) or CLOCK_MONOTONIC_RAW clock.
#if 0
offset += sizeof( uint32_t ) * 2;
int64_t ktime;
ring.Read( &ktime, offset, sizeof( int64_t ) );
#endif
TracyLfqPrepare( QueueType::FrameVsync );
MemWrite( &item->frameVsync.id, crtc );
MemWrite( &item->frameVsync.time, t0 );
TracyLfqCommit;
}
rbPos += hdr.size;
if( rbPos == end[sel] )