103 lines
3.0 KiB
C++
103 lines
3.0 KiB
C++
#include "RenderCadenceController.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
void RenderCadenceController::Configure(Duration targetFrameDuration, TimePoint firstRenderTime, const RenderCadencePolicy& policy)
|
|
{
|
|
mTargetFrameDuration = IsPositive(targetFrameDuration) ? targetFrameDuration : std::chrono::milliseconds(1);
|
|
mPolicy = policy;
|
|
if (mPolicy.skipThresholdFrames < 1.0)
|
|
mPolicy.skipThresholdFrames = 1.0;
|
|
Reset(firstRenderTime);
|
|
}
|
|
|
|
void RenderCadenceController::Reset(TimePoint firstRenderTime)
|
|
{
|
|
mNextRenderTime = firstRenderTime;
|
|
mNextFrameIndex = 0;
|
|
mMetrics = RenderCadenceMetrics();
|
|
}
|
|
|
|
RenderCadenceDecision RenderCadenceController::Tick(TimePoint now)
|
|
{
|
|
RenderCadenceDecision decision;
|
|
decision.frameIndex = mNextFrameIndex;
|
|
decision.renderTargetTime = mNextRenderTime;
|
|
decision.nextRenderTime = mNextRenderTime;
|
|
|
|
if (now < mNextRenderTime)
|
|
{
|
|
decision.action = RenderCadenceAction::Wait;
|
|
decision.waitDuration = mNextRenderTime - now;
|
|
decision.reason = "waiting-for-next-render-tick";
|
|
return decision;
|
|
}
|
|
|
|
const Duration lateness = now - mNextRenderTime;
|
|
const uint64_t skippedTicks = SkippedTicksForLateness(lateness);
|
|
if (skippedTicks > 0)
|
|
{
|
|
decision.skippedTicks = skippedTicks;
|
|
decision.frameIndex = mNextFrameIndex + skippedTicks;
|
|
decision.renderTargetTime = mNextRenderTime + (mTargetFrameDuration * skippedTicks);
|
|
decision.reason = "late-skip-render-ticks";
|
|
mMetrics.skippedTickCount += skippedTicks;
|
|
}
|
|
else
|
|
{
|
|
decision.reason = IsPositive(lateness) ? "late-render-now" : "on-time-render";
|
|
}
|
|
|
|
decision.action = RenderCadenceAction::Render;
|
|
decision.lateness = now > decision.renderTargetTime
|
|
? now - decision.renderTargetTime
|
|
: Duration::zero();
|
|
mNextFrameIndex = decision.frameIndex + 1;
|
|
mNextRenderTime = decision.renderTargetTime + mTargetFrameDuration;
|
|
decision.nextRenderTime = mNextRenderTime;
|
|
|
|
++mMetrics.renderedFrameCount;
|
|
mMetrics.nextFrameIndex = mNextFrameIndex;
|
|
mMetrics.lastLateness = decision.lateness;
|
|
if (IsPositive(decision.lateness))
|
|
{
|
|
++mMetrics.lateFrameCount;
|
|
mMetrics.maxLateness = (std::max)(mMetrics.maxLateness, decision.lateness);
|
|
}
|
|
|
|
return decision;
|
|
}
|
|
|
|
uint64_t RenderCadenceController::SkippedTicksForLateness(Duration lateness) const
|
|
{
|
|
if (!mPolicy.skipLateTicks || !IsPositive(lateness) || !IsPositive(mTargetFrameDuration))
|
|
return 0;
|
|
|
|
const double lateFrames = static_cast<double>(lateness.count()) / static_cast<double>(mTargetFrameDuration.count());
|
|
if (lateFrames < mPolicy.skipThresholdFrames)
|
|
return 0;
|
|
|
|
const uint64_t elapsedTicks = static_cast<uint64_t>(std::floor(lateFrames));
|
|
if (elapsedTicks == 0)
|
|
return 0;
|
|
return (std::min)(elapsedTicks, mPolicy.maxSkippedTicksPerDecision);
|
|
}
|
|
|
|
bool RenderCadenceController::IsPositive(Duration duration)
|
|
{
|
|
return duration > Duration::zero();
|
|
}
|
|
|
|
const char* RenderCadenceActionName(RenderCadenceAction action)
|
|
{
|
|
switch (action)
|
|
{
|
|
case RenderCadenceAction::Render:
|
|
return "Render";
|
|
case RenderCadenceAction::Wait:
|
|
default:
|
|
return "Wait";
|
|
}
|
|
}
|