Skip to content

Commit c2ec292

Browse files
committed
Adding support for GPU counters in UE4 plugin
1 parent 2ee1888 commit c2ec292

File tree

6 files changed

+241
-272
lines changed

6 files changed

+241
-272
lines changed

gui/Optick/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,5 @@
5151
// You can specify all the values or you can default the Build and Revision Numbers
5252
// by using the '*' as shown below:
5353
// [assembly: AssemblyVersion("1.0.*")]
54-
[assembly: AssemblyVersion("1.3.2.0")]
55-
[assembly: AssemblyFileVersion("1.3.2.0")]
54+
[assembly: AssemblyVersion("1.3.3.0")]
55+
[assembly: AssemblyFileVersion("1.3.3.0")]

samples/UnrealEnginePlugin/Source/OptickPlugin.Build.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,12 @@ public OptickPlugin(ReadOnlyTargetRules Target) : base(Target)
5757
}
5858
);
5959

60-
/*
61-
#if UE_4_24_OR_LATER
6260
PublicDefinitions.AddRange(
6361
new string[]
6462
{
6563
"OPTICK_UE4_GPU=1",
6664
}
6765
);
68-
#endif
69-
*/
7066

7167
if (Target.bBuildEditor == true)
7268
{

samples/UnrealEnginePlugin/Source/Private/OptickPlugin.cpp

Lines changed: 133 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include "CoreMinimal.h"
55
#include "Containers/Ticker.h"
66
#include "GenericPlatform/GenericPlatformFile.h"
7-
#include "HAL/X.h"
7+
#include "HAL/PlatformFilemanager.h"
88
#include "HAL/PlatformProcess.h"
99
#include "Misc/EngineVersion.h"
1010
#include "Misc/CoreDelegates.h"
@@ -112,10 +112,12 @@ class FOptickPlugin : public IOptickPlugin
112112
uint64 Convert32bitCPUTimestamp(int64 timestamp) const;
113113

114114
#ifdef OPTICK_UE4_GPU
115+
115116
void OnEndFrameRT();
116-
uint64 ConvertGPUTimestamp(uint64 timestamp);
117+
uint64 ConvertGPUTimestamp(uint64 timestamp, int GPUIndex);
117118

118-
FGPUTimingCalibrationTimestamp CalibrationTimestamp;
119+
bool UpdateCalibrationTimestamp(FRealtimeGPUProfilerFrameImpl* Frame, int GPUIndex);
120+
FGPUTimingCalibrationTimestamp CalibrationTimestamps[MAX_NUM_GPUS];
119121
#endif
120122

121123
public:
@@ -294,8 +296,6 @@ void FOptickPlugin::StartCapture()
294296
if (!IsCapturing)
295297
{
296298
#ifdef OPTICK_UE4_GPU
297-
CalibrationTimestamp = FGPUTiming::GetCalibrationTimestamp();
298-
299299
GPUThreadStorage.Reset();
300300
for (auto& pair : StorageMap)
301301
pair.Value->Reset();
@@ -393,7 +393,57 @@ uint64 FOptickPlugin::Convert32bitCPUTimestamp(int64 timestamp) const
393393
}
394394

395395
#ifdef OPTICK_UE4_GPU
396-
#if UE_4_24_OR_LATER
396+
397+
bool FOptickPlugin::UpdateCalibrationTimestamp(FRealtimeGPUProfilerFrameImpl* Frame, int GPUIndex)
398+
{
399+
FGPUTimingCalibrationTimestamp& CalibrationTimestamp = CalibrationTimestamps[GPUIndex];
400+
CalibrationTimestamp = FGPUTimingCalibrationTimestamp{ 0, 0 };
401+
402+
if (Frame->TimestampCalibrationQuery.IsValid())
403+
{
404+
#if UE_4_27_OR_LATER
405+
CalibrationTimestamp.GPUMicroseconds = Frame->TimestampCalibrationQuery->GPUMicroseconds[GPUIndex];
406+
CalibrationTimestamp.CPUMicroseconds = Frame->TimestampCalibrationQuery->CPUMicroseconds[GPUIndex];
407+
#else
408+
CalibrationTimestamp.GPUMicroseconds = Frame->TimestampCalibrationQuery->GPUMicroseconds;
409+
CalibrationTimestamp.CPUMicroseconds = Frame->TimestampCalibrationQuery->CPUMicroseconds;
410+
#endif
411+
}
412+
413+
if (CalibrationTimestamp.GPUMicroseconds == 0 || CalibrationTimestamp.CPUMicroseconds == 0) // Unimplemented platforms, or invalid on the first frame
414+
{
415+
if (Frame->GpuProfilerEvents.Num() > 1)
416+
{
417+
// Align CPU and GPU frames
418+
CalibrationTimestamp.GPUMicroseconds = Frame->GpuProfilerEvents[1].GetStartResultMicroseconds(0);
419+
CalibrationTimestamp.CPUMicroseconds = FPlatformTime::ToSeconds64(Frame->CPUFrameStartTimestamp) * 1000 * 1000;
420+
}
421+
else
422+
{
423+
// Fallback to legacy
424+
CalibrationTimestamp = FGPUTiming::GetCalibrationTimestamp();
425+
}
426+
}
427+
428+
return CalibrationTimestamp.GPUMicroseconds != 0 && CalibrationTimestamp.CPUMicroseconds != 0;
429+
}
430+
431+
struct TimeRange
432+
{
433+
uint64 Start;
434+
uint64 Finish;
435+
bool IsOverlap(TimeRange other) const
436+
{
437+
return !((Finish < other.Start) || (other.Finish < Start));
438+
}
439+
bool IsValid() const
440+
{
441+
return Start != 0 && Finish != 0 && Finish > Start;
442+
}
443+
TimeRange() : Start(0), Finish(0) {}
444+
TimeRange(uint64 start, uint64 finish) : Start(start), Finish(finish) {}
445+
};
446+
397447
void FOptickPlugin::OnEndFrameRT()
398448
{
399449
FScopeLock ScopeLock(&UpdateCriticalSection);
@@ -418,6 +468,7 @@ void FOptickPlugin::OnEndFrameRT()
418468

419469
if (!Event.GatherQueryResults(RHICmdList))
420470
{
471+
421472
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
422473
UE_LOG(OptickLog, Warning, TEXT("Query is not ready."));
423474
#endif
@@ -426,162 +477,106 @@ void FOptickPlugin::OnEndFrameRT()
426477
}
427478
}
428479

429-
for (int32 i = 1; i < NumEventsThisFramePlusOne; ++i)
430-
{
431-
FRealtimeGPUProfilerEventImpl& Event = Frame->GpuProfilerEvents[i];
432-
433-
const FName Name = Event.Name;
434-
435-
Optick::EventDescription* Description = nullptr;
436-
437-
if (Optick::EventDescription** ppDescription = GPUDescriptionMap.Find(Name))
438-
{
439-
Description = *ppDescription;
440-
}
441-
else
442-
{
443-
Description = Optick::EventDescription::CreateShared(TCHAR_TO_ANSI(*Name.ToString()));
444-
GPUDescriptionMap.Add(Name, Description);
445-
}
446-
447-
uint64 startTimestamp = ConvertGPUTimestamp(Event.StartResultMicroseconds);
448-
uint64 endTimestamp = ConvertGPUTimestamp(Event.EndResultMicroseconds);
480+
TArray<TimeRange> EventStack;
449481

450-
if (Name == NAME_GPU_Unaccounted)
451-
{
452-
OPTICK_FRAME_FLIP(Optick::FrameType::GPU, startTimestamp);
482+
if (NumEventsThisFramePlusOne <= 1)
483+
return;
453484

454-
if (GPUThreadStorage.LastTimestamp != 0)
455-
{
456-
OPTICK_STORAGE_POP(GPUThreadStorage.EventStorage, GPUThreadStorage.LastTimestamp);
457-
}
458-
459-
OPTICK_STORAGE_PUSH(GPUThreadStorage.EventStorage, Optick::GetFrameDescription(Optick::FrameType::GPU), startTimestamp)
460-
}
461-
else
462-
{
463-
OPTICK_STORAGE_EVENT(GPUThreadStorage.EventStorage, Description, startTimestamp, endTimestamp);
464-
}
465-
GPUThreadStorage.LastTimestamp = FMath::Max<uint64>(GPUThreadStorage.LastTimestamp, endTimestamp);
466-
}
467-
}
468-
}
469-
}
470-
#else
471-
void FOptickPlugin::OnEndFrameRT()
472-
{
473-
FScopeLock ScopeLock(&UpdateCriticalSection);
485+
// VS TODO: Add MGPU support
486+
uint32 GPUIndex = 0;
474487

475-
if (!IsCapturing || !Optick::IsActive(Optick::Mode::GPU))
476-
return;
488+
// Can't collect GPU data without valid calibration between CPU and GPU timestamps
489+
if (!UpdateCalibrationTimestamp(Frame, GPUIndex))
490+
return;
477491

478-
QUICK_SCOPE_CYCLE_COUNTER(STAT_FOptickPlugin_UpdRT);
492+
uint64 lastTimeStamp = FMath::Max(CalibrationTimestamps[GPUIndex].CPUMicroseconds, GPUThreadStorage.LastTimestamp);
479493

480-
if (FRealtimeGPUProfilerImpl* gpuProfiler = reinterpret_cast<FRealtimeGPUProfilerImpl*>(FRealtimeGPUProfiler::Get()))
481-
{
482-
if (FRealtimeGPUProfilerFrameImpl* Frame = gpuProfiler->Frames[gpuProfiler->ReadBufferIndex])
483-
{
484-
FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList();
494+
const FRealtimeGPUProfilerEventImpl& FirstEvent = Frame->GpuProfilerEvents[1];
495+
uint64 frameStartTimestamp = FMath::Max(ConvertGPUTimestamp(FirstEvent.GetStartResultMicroseconds(GPUIndex), GPUIndex), lastTimeStamp);
485496

486-
bool bAnyEventFailed = false;
487-
bool bAllQueriesAllocated = true;
497+
OPTICK_FRAME_FLIP(Optick::FrameType::GPU, frameStartTimestamp);
498+
OPTICK_STORAGE_PUSH(GPUThreadStorage.EventStorage, Optick::GetFrameDescription(Optick::FrameType::GPU), frameStartTimestamp)
488499

489-
for (int i = 0; i < Frame->GpuProfilerEvents.Num(); ++i)
500+
for (int32 Idx = 1; Idx < NumEventsThisFramePlusOne; ++Idx)
490501
{
491-
FRealtimeGPUProfilerEventImpl* Event = Frame->GpuProfilerEvents[i];
492-
check(Event != nullptr);
502+
const FRealtimeGPUProfilerEventImpl& Event = Frame->GpuProfilerEvents[Idx];
493503

494-
if (!Event->HasValidResult())
504+
if (Event.GetGPUMask().Contains(GPUIndex))
495505
{
496-
Event->GatherQueryResults(RHICmdList);
497-
}
506+
const FName Name = Event.Name;
498507

499-
if (!Event->HasValidResult())
500-
{
501-
#if UE_BUILD_DEBUG
502-
UE_LOG(OptickLog, Warning, TEXT("Query '%s' not ready."), *Event->GetName().ToString());
503-
#endif
504-
// The frame isn't ready yet. Don't update stats - we'll try again next frame.
505-
bAnyEventFailed = true;
506-
continue;
507-
}
508+
if (Name == NAME_GPU_Unaccounted)
509+
continue;
508510

509-
if (!Event->HasQueriesAllocated())
510-
{
511-
bAllQueriesAllocated = false;
512-
}
513-
}
514-
515-
if (bAnyEventFailed)
516-
{
517-
return;
518-
}
511+
Optick::EventDescription* Description = nullptr;
519512

520-
if (!bAllQueriesAllocated)
521-
{
522-
static bool bWarned = false;
513+
if (Optick::EventDescription** ppDescription = GPUDescriptionMap.Find(Name))
514+
{
515+
Description = *ppDescription;
516+
}
517+
else
518+
{
519+
Description = Optick::EventDescription::CreateShared(TCHAR_TO_ANSI(*Name.ToString()));
520+
GPUDescriptionMap.Add(Name, Description);
521+
}
523522

524-
if (!bWarned)
525-
{
526-
bWarned = true;
527-
UE_LOG(OptickLog, Warning, TEXT("Ran out of GPU queries! Results for this frame will be incomplete"));
528-
}
529-
}
523+
uint64 startTimestamp = ConvertGPUTimestamp(Event.GetStartResultMicroseconds(GPUIndex), GPUIndex);
524+
uint64 endTimestamp = ConvertGPUTimestamp(Event.GetEndResultMicroseconds(GPUIndex), GPUIndex);
530525

526+
// Fixing potential errors
527+
startTimestamp = FMath::Max(startTimestamp, lastTimeStamp);
528+
endTimestamp = FMath::Max(endTimestamp, startTimestamp);
531529

532-
for (int i = 0; i < Frame->GpuProfilerEvents.Num(); ++i)
533-
{
534-
FRealtimeGPUProfilerEventImpl* Event = Frame->GpuProfilerEvents[i];
535-
check(Event != nullptr);
530+
// Ensuring correct hierarchy
531+
while (EventStack.Num() && (EventStack.Last().Finish <= startTimestamp))
532+
EventStack.Pop();
536533

537-
const FName Name = Event->Name;
534+
// Discovered broken hierarchy, skipping event
535+
if (EventStack.Num() && (endTimestamp < EventStack.Last().Start))
536+
continue;
538537

539-
Optick::EventDescription* Description = nullptr;
538+
// Clamp range against the parent counter
539+
if (EventStack.Num())
540+
{
541+
TimeRange parent = EventStack.Last();
542+
startTimestamp = FMath::Clamp(startTimestamp, parent.Start, parent.Finish);
543+
endTimestamp = FMath::Clamp(endTimestamp, parent.Start, parent.Finish);
544+
}
540545

541-
if (Optick::EventDescription** ppDescription = GPUDescriptionMap.Find(Name))
542-
{
543-
Description = *ppDescription;
544-
}
545-
else
546-
{
547-
Description = Optick::EventDescription::CreateShared(TCHAR_TO_ANSI(*Name.ToString()));
548-
GPUDescriptionMap.Add(Name, Description);
549-
}
546+
// Ignore invalid events
547+
if (startTimestamp == endTimestamp)
548+
continue;
550549

551-
uint64 startTimestamp = ConvertGPUTimestamp(Event->StartResultMicroseconds);
552-
uint64 endTimestamp = ConvertGPUTimestamp(Event->EndResultMicroseconds);
550+
//if (Name == NAME_GPU_Unaccounted)
551+
//{
552+
// OPTICK_FRAME_FLIP(Optick::FrameType::GPU, startTimestamp);
553553

554-
if (Name == NAME_GPU_Unaccounted)
555-
{
556-
OPTICK_FRAME_FLIP(Optick::FrameType::GPU, startTimestamp);
557554

558-
if (GPUThreadStorage.LastTimestamp != 0)
555+
// OPTICK_STORAGE_PUSH(GPUThreadStorage.EventStorage, Optick::GetFrameDescription(Optick::FrameType::GPU), startTimestamp)
556+
//}
557+
//else
559558
{
560-
OPTICK_STORAGE_POP(GPUThreadStorage.EventStorage, GPUThreadStorage.LastTimestamp);
559+
EventStack.Add(TimeRange(startTimestamp, endTimestamp));
560+
OPTICK_STORAGE_EVENT(GPUThreadStorage.EventStorage, Description, startTimestamp, endTimestamp);
561561
}
562-
563-
OPTICK_STORAGE_PUSH(GPUThreadStorage.EventStorage, Optick::GetFrameDescription(Optick::FrameType::GPU), startTimestamp)
564-
}
565-
else
566-
{
567-
OPTICK_STORAGE_EVENT(GPUThreadStorage.EventStorage, Description, startTimestamp, endTimestamp);
562+
lastTimeStamp = FMath::Max<uint64>(lastTimeStamp, endTimestamp);
568563
}
569-
GPUThreadStorage.LastTimestamp = FMath::Max<uint64>(GPUThreadStorage.LastTimestamp, endTimestamp);
570564
}
565+
566+
OPTICK_STORAGE_POP(GPUThreadStorage.EventStorage, lastTimeStamp);
567+
GPUThreadStorage.LastTimestamp = lastTimeStamp;
571568
}
572569
}
573570
}
574-
#endif
575571

576-
uint64 FOptickPlugin::ConvertGPUTimestamp(uint64 timestamp)
572+
uint64 FOptickPlugin::ConvertGPUTimestamp(uint64 timestamp, int GPUIndex)
577573
{
578-
if (CalibrationTimestamp.CPUMicroseconds == 0 || CalibrationTimestamp.GPUMicroseconds == 0)
574+
if (CalibrationTimestamps[GPUIndex].CPUMicroseconds == 0 || CalibrationTimestamps[GPUIndex].GPUMicroseconds == 0)
579575
{
580-
CalibrationTimestamp.CPUMicroseconds = uint64(FPlatformTime::ToSeconds64(FPlatformTime::Cycles64()) * 1e6);
581-
CalibrationTimestamp.GPUMicroseconds = timestamp;
576+
return (uint64)-1;
582577
}
583578

584-
const uint64 cpuTimestampUs = timestamp - CalibrationTimestamp.GPUMicroseconds + CalibrationTimestamp.CPUMicroseconds;
579+
const uint64 cpuTimestampUs = timestamp - CalibrationTimestamps[GPUIndex].GPUMicroseconds + CalibrationTimestamps[GPUIndex].CPUMicroseconds;
585580
const uint64 cpuTimestamp = cpuTimestampUs * 1e-6 / FPlatformTime::GetSecondsPerCycle64();
586581
return cpuTimestamp;
587582
}
@@ -639,13 +634,22 @@ void FOptickPlugin::GetDataFromStatsThread(int64 CurrentFrame)
639634
const FName groupName = Item.NameAndInfo.GetGroupName();
640635

641636
uint32 color = 0;
637+
uint32 filter = 0;
642638

643639
if (NAME_STATGROUP_CPUStalls == groupName)
644-
color = Optick::Color::White;
640+
{
641+
color = Optick::Color::Tomato;
642+
filter = Optick::Filter::Wait;
643+
}
645644

646645
for (int i = 0; i < sizeof(NAME_Wait) / sizeof(NAME_Wait[0]); ++i)
646+
{
647647
if (NAME_Wait[i] == shortName)
648+
{
648649
color = Optick::Color::White;
650+
filter = Optick::Filter::Wait;
651+
}
652+
}
649653

650654
Description = Optick::EventDescription::CreateShared(TCHAR_TO_ANSI(*shortName.ToString()), nullptr, 0, color);
651655

0 commit comments

Comments
 (0)