Skip to content
93 changes: 72 additions & 21 deletions IntelPresentMon/CommonUtilities/mc/MetricsCalculatorAnimation.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2025 Intel Corporation
// Copyright (C) 2025 Intel Corporation
// SPDX-License-Identifier: MIT
#include "MetricsCalculator.h"
#include "MetricsCalculatorInternal.h"
Expand All @@ -10,8 +10,53 @@ namespace pmon::util::metrics
{
namespace
{
struct AnimationSourceContext
{
AnimationErrorSource effectiveSource = AnimationErrorSource::CpuStart;
uint64_t currentSimStart = 0;
bool isTransitionFrame = false;
};

AnimationSourceContext ResolveAnimationSourceContext(
const SwapChainCoreState& chain,
const FrameData& present)
{
AnimationSourceContext context{};
context.effectiveSource = chain.animationErrorSource;

switch (chain.animationErrorSource) {
case AnimationErrorSource::AppProvider:
break;

case AnimationErrorSource::PCLatency:
if (present.appSimStartTime != 0) {
context.effectiveSource = AnimationErrorSource::AppProvider;
context.isTransitionFrame = true;
}
break;

case AnimationErrorSource::CpuStart:
if (present.appSimStartTime != 0) {
context.effectiveSource = AnimationErrorSource::AppProvider;
context.isTransitionFrame = true;
}
else if (present.pclSimStartTime != 0) {
context.effectiveSource = AnimationErrorSource::PCLatency;
context.isTransitionFrame = true;
}
break;
}

context.currentSimStart = CalculateAnimationErrorSimStartTime(
chain,
present,
context.effectiveSource);

return context;
}

// ---- Animation metrics ----
std::optional<double> ComputeAnimationError(
double ComputeAnimationError(
const QpcConverter& qpc,
const SwapChainCoreState& chain,
const FrameData& present,
Expand All @@ -20,56 +65,62 @@ namespace pmon::util::metrics
uint64_t screenTime)
{
if (!isDisplayed || !isAppFrame) {
return std::nullopt;
return MissingFrameMetricValue();
}

uint64_t currentSimStart = CalculateAnimationErrorSimStartTime(chain, present, chain.animationErrorSource);
const auto sourceContext = ResolveAnimationSourceContext(chain, present);

if (sourceContext.isTransitionFrame) {
return MissingFrameMetricValue();
}

if (currentSimStart == 0 ||
if (sourceContext.currentSimStart == 0 ||
chain.lastDisplayedSimStartTime == 0 ||
currentSimStart <= chain.lastDisplayedSimStartTime ||
sourceContext.currentSimStart <= chain.lastDisplayedSimStartTime ||
chain.lastDisplayedAppScreenTime == 0) {
return std::nullopt;
return MissingFrameMetricValue();
}

double simElapsed = qpc.DeltaUnsignedMilliSeconds(chain.lastDisplayedSimStartTime, currentSimStart);
double simElapsed = qpc.DeltaUnsignedMilliSeconds(
chain.lastDisplayedSimStartTime,
sourceContext.currentSimStart);
double displayElapsed = qpc.DeltaUnsignedMilliSeconds(chain.lastDisplayedAppScreenTime, screenTime);

if (simElapsed == 0.0 || displayElapsed == 0.0) {
return std::nullopt;
return MissingFrameMetricValue();
}

return simElapsed - displayElapsed;
}


std::optional<double> ComputeAnimationTime(
double ComputeAnimationTime(
const QpcConverter& qpc,
const SwapChainCoreState& chain,
const FrameData& present,
bool isDisplayed,
bool isAppFrame)
{
if (!isDisplayed || !isAppFrame) {
return std::nullopt;
return MissingFrameMetricValue();
}

bool isFirstProviderSimTime =
chain.animationErrorSource == AnimationErrorSource::CpuStart &&
chain.firstAppSimStartTime == 0 &&
(present.appSimStartTime != 0 || present.pclSimStartTime != 0);
if (isFirstProviderSimTime) {
// Seed only: no animation time yet. UpdateAfterPresent will flip us
// into AppProvider/PCL and latch firstAppSimStartTime.
const auto sourceContext = ResolveAnimationSourceContext(chain, present);

if (sourceContext.isTransitionFrame) {
return 0.0;
}

uint64_t currentSimStart = CalculateAnimationErrorSimStartTime(chain, present, chain.animationErrorSource);
if (currentSimStart == 0) {
if (sourceContext.effectiveSource != AnimationErrorSource::CpuStart &&
sourceContext.currentSimStart == 0) {
return MissingFrameMetricValue();
}

if (sourceContext.currentSimStart == 0) {
return 0.0;
}

return CalculateAnimationTime(qpc, chain.firstAppSimStartTime, currentSimStart);
return CalculateAnimationTime(qpc, chain.firstAppSimStartTime, sourceContext.currentSimStart);
}

double ComputePresentStartTimeMs(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2025 Intel Corporation
// Copyright (C) 2025 Intel Corporation
// SPDX-License-Identifier: MIT
#include "MetricsCalculator.h"
#include "MetricsCalculatorInternal.h"
Expand All @@ -18,7 +18,7 @@ namespace pmon::util::metrics
bool isAppPresent)
{
if (!isAppPresent) {
return 0.0;
return MissingFrameMetricValue();
}

const auto cpuStart = CalculateCPUStart(swapChain, present);
Expand All @@ -42,7 +42,7 @@ namespace pmon::util::metrics
bool isAppPresent)
{
if (!isAppPresent) {
return 0.0;
return MissingFrameMetricValue();
}

if (present.appPropagatedTimeInPresent != 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ namespace pmon::util::metrics
}


std::optional<double> ComputeMsFlipDelay(
double ComputeMsFlipDelay(
const QpcConverter& qpc,
const FrameData& present,
bool isDisplayed)
{
if (isDisplayed && present.flipDelay != 0) {
return qpc.DurationMilliSeconds(present.flipDelay);
}
return std::nullopt;
return MissingFrameMetricValue();
}


Expand All @@ -75,7 +75,7 @@ namespace pmon::util::metrics
}


std::optional<double> ComputeMsReadyTimeToDisplayLatency(
double ComputeMsReadyTimeToDisplayLatency(
const QpcConverter& qpc,
const FrameData& present,
bool isDisplayed,
Expand All @@ -84,7 +84,7 @@ namespace pmon::util::metrics
if (isDisplayed && present.readyTime != 0) {
return qpc.DeltaUnsignedMilliSeconds(present.readyTime, screenTime);
}
return std::nullopt;
return MissingFrameMetricValue();
}


Expand Down
26 changes: 13 additions & 13 deletions IntelPresentMon/CommonUtilities/mc/MetricsCalculatorInput.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2025 Intel Corporation
// Copyright (C) 2025 Intel Corporation
// SPDX-License-Identifier: MIT
#include "MetricsCalculator.h"
#include "MetricsCalculatorInternal.h"
Expand All @@ -11,7 +11,7 @@ namespace pmon::util::metrics
namespace
{
// ---- Input latency metrics ----
std::optional<double> ComputeClickToPhotonLatency(
double ComputeClickToPhotonLatency(
const QpcConverter& qpc,
const SwapChainCoreState& chain,
const FrameData& present,
Expand All @@ -22,7 +22,7 @@ namespace pmon::util::metrics
{
// Only app frames participate in click-to-photon.
if (!isAppFrame) {
return std::nullopt;
return MissingFrameMetricValue();
}

uint64_t inputTime = 0;
Expand All @@ -34,7 +34,7 @@ namespace pmon::util::metrics
if (!isDisplayed) {
// Not displayed: stash the click for a future displayed frame.
stateDeltas.lastReceivedNotDisplayedMouseClickTime = inputTime;
return std::nullopt;
return MissingFrameMetricValue();
}
else {
stateDeltas.shouldResetInputTimes = true;
Expand All @@ -49,14 +49,14 @@ namespace pmon::util::metrics

// If we still have no inputTime, nothing to compute.
if (inputTime == 0) {
return std::nullopt;
return MissingFrameMetricValue();
}

return qpc.DeltaUnsignedMilliSeconds(inputTime, screenTime);
}


std::optional<double> ComputeAllInputToPhotonLatency(
double ComputeAllInputToPhotonLatency(
const QpcConverter& qpc,
const SwapChainCoreState& chain,
const FrameData& present,
Expand All @@ -67,7 +67,7 @@ namespace pmon::util::metrics
{
// Only app frames participate in click-to-photon.
if (!isAppFrame) {
return std::nullopt;
return MissingFrameMetricValue();
}

uint64_t inputTime = 0;
Expand All @@ -79,7 +79,7 @@ namespace pmon::util::metrics
if (!isDisplayed) {
// Not displayed: stash the click for a future displayed frame.
stateDeltas.lastReceivedNotDisplayedAllInputTime = inputTime;
return std::nullopt;
return MissingFrameMetricValue();
}
else {
stateDeltas.shouldResetInputTimes = true;
Expand All @@ -94,13 +94,13 @@ namespace pmon::util::metrics

// If we still have no inputTime, nothing to compute.
if (inputTime == 0) {
return std::nullopt;
return MissingFrameMetricValue();
}

return qpc.DeltaUnsignedMilliSeconds(inputTime, screenTime);
}

std::optional<double> ComputeInstrumentedInputToPhotonLatency(
double ComputeInstrumentedInputToPhotonLatency(
const QpcConverter& qpc,
const SwapChainCoreState& chain,
const FrameData& present,
Expand All @@ -111,7 +111,7 @@ namespace pmon::util::metrics
{
// Only app frames participate in click-to-photon.
if (!isAppFrame) {
return std::nullopt;
return MissingFrameMetricValue();
}

uint64_t inputTime = 0;
Expand All @@ -123,7 +123,7 @@ namespace pmon::util::metrics
if (!isDisplayed) {
// Not displayed: stash the click for a future displayed frame.
stateDeltas.lastReceivedNotDisplayedAppProviderInputTime = inputTime;
return std::nullopt;
return MissingFrameMetricValue();
}
else {
stateDeltas.shouldResetInputTimes = true;
Expand All @@ -138,7 +138,7 @@ namespace pmon::util::metrics

// If we still have no inputTime, nothing to compute.
if (inputTime == 0) {
return std::nullopt;
return MissingFrameMetricValue();
}

return qpc.DeltaUnsignedMilliSeconds(inputTime, screenTime);
Expand Down
Loading