Files
watrbx-game-engine/App/util/Profiling.cpp
T
2025-09-18 17:55:52 -04:00

283 lines
6.2 KiB
C++

/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
#include "stdafx.h"
#include "Util/Profiling.h"
#include "G3D/System.h"
#include "rbx/Debug.h"
#include "v8datamodel/DebugSettings.h"
#ifndef _WIN32
#include <pthread.h>
static pthread_key_t tsd_key;
#endif
static long markTlsIndex = 0;
static bool profilingEnabled = false;
void RBX::Profiling::init(bool enabled)
{
profilingEnabled = enabled;
#ifdef _WIN32
markTlsIndex = TlsAlloc();
#else
/* with full error check EL
if( pthread_key_create(&tsd_key, NULL) ) err_abort(status, Error creating key);
if( pthread_setspecific( tsd_key, value))
err_abort(status, Error in pthread_setspecific);
*/
pthread_key_create(&tsd_key, NULL);
markTlsIndex = reinterpret_cast<uintptr_t>(pthread_getspecific( tsd_key ));
#endif
}
void RBX::Profiling::setEnabled(bool enabled)
{
profilingEnabled = enabled;
#ifdef _WIN32
// This is only valid in windows. For mac, the markTlsIndex
// is always 0 unless profiling is init()ed inside the context
// of a Mark.
RBXASSERT(markTlsIndex);
#endif
}
bool RBX::Profiling::isEnabled()
{
return profilingEnabled || DebugSettings::singleton().enableProfiling;
}
using namespace RBX;
using namespace Profiling;
Bucket::Bucket()
:sampleTimeElapsed(0)
,wallTimeSpan(0)
,frames(0)
{
}
double Profiler::profilingWindow = 3.0;
Profiler::Profiler(const char* name)
:name(name)
,currentBucket(0)
,bucketTimeSpan(0.003 * profilingWindow) // in second
{
}
CodeProfiler::CodeProfiler(const char* name)
:Profiler(name)
{
}
void CodeProfiler::unlog(double wallTimeElapsed)
{
buckets[currentBucket].wallTimeSpan -= wallTimeElapsed;
}
void CodeProfiler::log(bool frameTick, double wallTimeElapsed)
{
// TODO: Do we need to modify our tick code to be monotonically increasing???
// Sometimes the wall time can be slightly < 0!
//RBXASSERT(wallTimeElapsed>=-0.001);
Time time = Time::now<Time::Benchmark>();
Time::Interval sampleTimeElapsed = time - lastSampleTime;
if (sampleTimeElapsed >= bucketTimeSpan)
{
// We're done with the current bucket. Create a new one
currentBucket++;
currentBucket %= buckets.size();
buckets[currentBucket].frames = frameTick ? 1 : 0;
buckets[currentBucket].sampleTimeElapsed = sampleTimeElapsed.seconds();
buckets[currentBucket].wallTimeSpan = wallTimeElapsed;
}
else
{
if (frameTick)
buckets[currentBucket].frames++;
buckets[currentBucket].sampleTimeElapsed += sampleTimeElapsed.seconds();
buckets[currentBucket].wallTimeSpan += wallTimeElapsed;
}
lastSampleTime = time;
}
Bucket Profiler::getWindow(double window) const
{
Bucket result;
if (!Profiling::isEnabled() && !DebugSettings::singleton().enableProfiling)
return result;
const int firstBucket = currentBucket - 1 + buckets.size(); // We add bucketCount to ensure the % operator wraps to >= 0 index
// Iterate backwards through previous buckets
const size_t count = std::min((size_t)std::max((int)(window / bucketTimeSpan.seconds()), 1), buckets.size()-2);
for (size_t i=0; i<count; ++i)
{
int index = (firstBucket-i) % buckets.size();
RBXASSERT(index>=0);
result += buckets[index];
if (result.sampleTimeElapsed>=window)
break;
}
return result;
}
Bucket Profiler::getFrames(int frames) const
{
Bucket result;
if (!Profiling::isEnabled() && !DebugSettings::singleton().enableProfiling)
return result;
const int firstBucket = currentBucket - 1 + buckets.size(); // We add bucketCount to ensure the % operator wraps to >= 0 index
// Iterate backwards through previous buckets
const size_t count = std::min((size_t)frames, buckets.size()-2);
for (size_t i=0; i<count; ++i)
{
int index = (firstBucket-i) % buckets.size();
RBXASSERT(index>=0);
result += buckets[index];
}
return result;
}
Bucket& Bucket::operator+=(const Bucket& b)
{
sampleTimeElapsed += b.sampleTimeElapsed;
wallTimeSpan += b.wallTimeSpan;
frames += b.frames;
return *this;
}
double Bucket::getActualFPS() const
{
return sampleTimeElapsed>0.0 ? frames / sampleTimeElapsed : 0;
}
double Bucket::getNominalFPS() const
{
return wallTimeSpan>0.0 ? frames / wallTimeSpan : 0;
}
double Bucket::getNominalFramePeriod() const
{
return frames>0 ? wallTimeSpan / frames : 0;
}
Mark::Mark(CodeProfiler& section, bool frameTick, bool logInclusive):section(section),frameTick(frameTick),enabled(profilingEnabled || DebugSettings::singleton().enableProfiling), logInclusive(logInclusive)
{
#if 1
if (enabled)
{
startTime = Time::now<Time::Benchmark>();
#ifdef _WIN32
enclosingMark = (Mark*) TlsGetValue(markTlsIndex);
TlsSetValue(markTlsIndex, this);
#else
enclosingMark = (Mark *) pthread_getspecific( tsd_key );
pthread_setspecific( tsd_key, (void *)(this));
#endif
}
#endif
}
Mark::~Mark()
{
#if 1
if (enabled)
{
#ifdef _WIN32
TlsSetValue(markTlsIndex, enclosingMark);
#else
pthread_setspecific( tsd_key, (void *)(enclosingMark));
#endif
Time now = Time::now<Time::Benchmark>();
Time::Interval wallTimeInclusive = now - startTime;
double logtime = wallTimeInclusive.seconds();
if(!logInclusive)
{
logtime = wallTimeInclusive.seconds() - childrenElapsed.seconds();
}
section.log(frameTick, logtime);
// Subtract times from enclosing Mark
if (enclosingMark)
enclosingMark->childrenElapsed += wallTimeInclusive;
}
#endif
}
BucketProfile::BucketProfile(const int* bucketLimits, int size) : bucketLimits(bucketLimits), total(0)
{
data.resize(size+1, 0);
}
BucketProfile::BucketProfile(const BucketProfile& orig) : data(orig.data), bucketLimits(orig.bucketLimits), total(0)
{
}
BucketProfile::BucketProfile() : bucketLimits(NULL), total(0)
{}
unsigned BucketProfile::findBucket(int v)
{
unsigned numBuckets = data.size();
for(unsigned i = 0; i < numBuckets-1; i++)
{
if (v < bucketLimits[i])
return i;
}
return numBuckets-1;
}
const BucketProfile& BucketProfile::operator = (const BucketProfile& rhs)
{
data = rhs.data;
bucketLimits = rhs.bucketLimits;
total = rhs.total;
return *this;
}
void BucketProfile::clear()
{
for(std::vector<int>::iterator it = data.begin(); it != data.end(); ++it)
{
*it = 0;
}
total = 0;
}
void BucketProfile::addValue(int v)
{
data[findBucket(v)]++;
total += v;
}
void BucketProfile::removeValue(int v)
{
unsigned bucket = findBucket(v);
data[bucket]--;
RBXASSERT(data[bucket] >= 0);
}