Files
UnrealEngine/Engine/Plugins/ChaosInsights/Source/ChaosInsightsAnalysis/Private/Model/LockRegions.cpp
2025-05-18 13:04:45 +08:00

300 lines
6.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ChaosInsightsAnalysis/Model/LockRegions.h"
#include "Model/LockRegions.h"
#include "Algo/ForEach.h"
#include "Internationalization/Internationalization.h"
#include "TraceServices/Model/Threads.h"
#define LOCTEXT_NAMESPACE "LockRegionProvider"
DEFINE_LOG_CATEGORY(LogChaosInsights)
namespace ChaosInsightsAnalysis
{
thread_local TraceServices::FProviderLock::FThreadLocalState GLockRegionsProviderLockState;
FLockRegionProvider::FLockRegionProvider(TraceServices::IAnalysisSession& InSession)
: Session(InSession)
{
}
FLockRegionProvider::~FLockRegionProvider()
{
}
void FLockRegionProvider::BeginRead() const
{
Lock.BeginRead(GLockRegionsProviderLockState);
}
void FLockRegionProvider::EndRead() const
{
Lock.EndRead(GLockRegionsProviderLockState);
}
void FLockRegionProvider::ReadAccessCheck() const
{
Lock.ReadAccessCheck(GLockRegionsProviderLockState);
}
uint64 FLockRegionProvider::GetRegionCount() const
{
ReadAccessCheck();
uint64 RegionCount = 0;
for(const FLockRegionLane& Lane : Lanes)
{
RegionCount += Lane.Num();
}
return RegionCount;
}
int32 FLockRegionProvider::GetLaneCount() const
{
ReadAccessCheck(); return Lanes.Num();
}
const FLockRegionLane* FLockRegionProvider::GetLane(int32 index) const
{
ReadAccessCheck();
if(index < Lanes.Num())
{
return &(Lanes[index]);
}
return nullptr;
}
void FLockRegionProvider::AppendRegionBegin(double Time, uint64 ThreadId, bool bWrite)
{
EditAccessCheck();
FLockRegion** FoundRegion = OpenRegionsByThread.Find(ThreadId);
int32 InitialLockDepth = 0;
if(FoundRegion)
{
(*FoundRegion)->LockCount++;
(*FoundRegion)->LockDepth++;
}
else
{
int32 Depth = CalculateRegionDepth(Time);
while(Depth >= Lanes.Num())
{
Lanes.Emplace(Session.GetLinearAllocator());
}
TraceServices::FAnalysisSessionReadScope SessionReadScope(Session);
const TraceServices::IThreadProvider& ThreadProvider = TraceServices::ReadThreadProvider(Session);
Lanes[Depth].Regions.EmplaceBack(FLockRegion
{
.BeginTime = Time,
.Text = Session.StoreString(ThreadProvider.GetThreadName(ThreadId)),
.Thread = ThreadId,
.Depth = Depth,
.LockCount = 1,
.LockDepth = 1,
.bIsWrite = bWrite
});
OpenRegionsByThread.Add(ThreadId, &Lanes[Depth].Regions.Last());
}
UpdateSession(Time);
}
void FLockRegionProvider::AppendRegionAcquired(double Time, uint64 ThreadId)
{
EditAccessCheck();
FLockRegion* Region = OpenRegionsByThread.FindChecked(ThreadId);
if(Time < Region->AcquireTime)
{
Region->AcquireTime = Time;
}
UpdateSession(Time);
}
void FLockRegionProvider::AppendRegionEnd(double Time, uint64 ThreadId)
{
EditAccessCheck();
TMap<uint64_t, FLockRegion*>::TKeyIterator RegionIt = OpenRegionsByThread.CreateKeyIterator(ThreadId);
FLockRegion& Region = *RegionIt->Value;
if(--Region.LockDepth == 0)
{
RegionIt->Value->EndTime = Time;
RegionIt.RemoveCurrent();
}
UpdateSession(Time);
}
void FLockRegionProvider::OnAnalysisSessionEnded()
{
EditAccessCheck();
if(OpenRegionsByThread.Num() > 0)
{
UE_LOG(LogChaosInsights, Warning, TEXT("A physics lock event was never closed."));
}
}
int32 FLockRegionProvider::CalculateRegionDepth(double NewBeginTime) const
{
constexpr int32 DepthLimit = 100;
int32 NewDepth = 0;
// Find first free lane/depth
while(NewDepth < DepthLimit)
{
if(!Lanes.IsValidIndex(NewDepth))
{
break;
}
const FLockRegion& LastRegion = Lanes[NewDepth].Regions.Last();
if(LastRegion.EndTime <= NewBeginTime)
{
break;
}
NewDepth++;
}
ensureMsgf(NewDepth < DepthLimit, TEXT("Regions are nested too deep."));
return NewDepth;
}
void FLockRegionProvider::UpdateSession(double InTime)
{
TraceServices::FAnalysisSessionEditScope EditScope(Session);
Session.UpdateDurationSeconds(InTime);
}
void FLockRegionProvider::ForEachLane(TFunctionRef<void(const FLockRegionLane&, int32)> Callback) const
{
ReadAccessCheck();
for(int32 LaneIndex = 0; LaneIndex < Lanes.Num(); ++LaneIndex)
{
Callback(Lanes[LaneIndex], LaneIndex);
}
}
void FLockRegionProvider::BeginEdit() const
{
Lock.BeginWrite(GLockRegionsProviderLockState);
}
void FLockRegionProvider::EndEdit() const
{
Lock.EndWrite(GLockRegionsProviderLockState);
}
void FLockRegionProvider::EditAccessCheck() const
{
Lock.WriteAccessCheck(GLockRegionsProviderLockState);
}
bool FLockRegionProvider::ForEachRegionInRange(double IntervalStart, double IntervalEnd, TFunctionRef<bool(const FLockRegion&)> Callback) const
{
ReadAccessCheck();
if(IntervalStart > IntervalEnd)
{
return false;
}
for(const FLockRegionLane& Lane : Lanes)
{
if(!Lane.ForEachRegionInRange(IntervalStart, IntervalEnd, Callback))
{
return false;
}
}
return true;
}
FLockRegionLane::FLockRegionLane(TraceServices::ILinearAllocator& InAllocator)
: Regions(InAllocator, 512)
{
}
int32 FLockRegionLane::Num() const
{
return static_cast<int32>(Regions.Num());
}
bool FLockRegionLane::ForEachRegionInRange(double IntervalStart, double IntervalEnd, TFunctionRef<bool(const FLockRegion&)> Callback) const
{
struct FTimeProjections
{
static double Begin(const FLockRegion& InRegion)
{
return InRegion.BeginTime;
}
static double End(const FLockRegion& InRegion)
{
return InRegion.EndTime;
}
};
const FInt32Interval OverlapRange = GetElementRangeOverlappingGivenRange<FLockRegion>(Regions, IntervalStart, IntervalEnd, &FTimeProjections::Begin, &FTimeProjections::End);
if(OverlapRange.Min == -1)
{
return true;
}
for(int32 Index = OverlapRange.Min; Index <= OverlapRange.Max; ++Index)
{
if(!Callback(Regions[Index]))
{
return false;
}
}
return true;
}
FName GetLockRegionProviderName()
{
static const FName Name("LockRegionProvider");
return Name;
}
ILockRegionProvider::~ILockRegionProvider() = default;
const ILockRegionProvider& ReadRegionProvider(const TraceServices::IAnalysisSession& Session)
{
return *Session.ReadProvider<ILockRegionProvider>(GetLockRegionProviderName());
}
IEditableLockRegionProvider::~IEditableLockRegionProvider() = default;
IEditableLockRegionProvider& EditRegionProvider(TraceServices::IAnalysisSession& Session)
{
return *Session.EditProvider<IEditableLockRegionProvider>(GetLockRegionProviderName());
}
}
#undef LOCTEXT_NAMESPACE