Files
video-shader-toys/tests/RenderCadenceCompositorFrameExchangeTests.cpp
Aiden 5c1fc2a6cf
All checks were successful
CI / React UI Build (push) Successful in 10s
CI / Native Windows Build And Tests (push) Successful in 2m58s
CI / Windows Release Package (push) Has been skipped
telemetry and timing updates
2026-05-13 00:21:28 +10:00

229 lines
8.2 KiB
C++

#include "SystemFrameExchange.h"
#include <chrono>
#include <cstdint>
#include <iostream>
namespace
{
int gFailures = 0;
void Expect(bool condition, const char* message)
{
if (condition)
return;
std::cerr << "FAIL: " << message << "\n";
++gFailures;
}
SystemFrameExchangeConfig MakeConfig(std::size_t capacity = 2)
{
SystemFrameExchangeConfig config;
config.width = 4;
config.height = 3;
config.pixelFormat = VideoIOPixelFormat::Bgra8;
config.capacity = capacity;
return config;
}
SystemFrameExchangeConfig MakeBoundedCompletedConfig(std::size_t capacity = 4, std::size_t maxCompletedFrames = 2)
{
SystemFrameExchangeConfig config = MakeConfig(capacity);
config.maxCompletedFrames = maxCompletedFrames;
return config;
}
void TestAcquirePublishesAndSchedules()
{
SystemFrameExchange exchange(MakeConfig(1));
SystemFrame frame;
Expect(exchange.AcquireForRender(frame), "frame can be acquired for render");
Expect(frame.bytes != nullptr, "acquired frame has storage");
Expect(frame.width == 4, "frame width is configured");
Expect(frame.height == 3, "frame height is configured");
Expect(frame.rowBytes == 16, "BGRA8 row bytes are inferred");
Expect(frame.pixelFormat == VideoIOPixelFormat::Bgra8, "pixel format is configured");
frame.frameIndex = 42;
Expect(exchange.PublishCompleted(frame), "rendering frame can be completed");
Expect(exchange.WaitForCompletedDepth(1, std::chrono::milliseconds(0)), "completed depth can be observed");
SystemFrame scheduled;
Expect(exchange.ConsumeCompletedForSchedule(scheduled), "completed frame can be scheduled");
Expect(scheduled.index == frame.index, "scheduled frame uses completed slot");
Expect(scheduled.generation == frame.generation, "scheduled frame keeps generation");
Expect(scheduled.frameIndex == 42, "frame index is preserved");
Expect(exchange.ReleaseScheduledByBytes(scheduled.bytes), "scheduled frame can be released by bytes");
SystemFrameExchangeMetrics metrics = exchange.Metrics();
Expect(metrics.freeCount == 1, "released slot returns to free");
Expect(metrics.completedFrames == 1, "completed metric is counted");
Expect(metrics.scheduledFrames == 1, "scheduled metric is counted");
}
void TestAcquirePreservesCompletedFrames()
{
SystemFrameExchange exchange(MakeConfig(2));
SystemFrame first;
SystemFrame second;
SystemFrame third;
Expect(exchange.AcquireForRender(first), "first preserving frame can be acquired");
first.frameIndex = 1;
Expect(exchange.PublishCompleted(first), "first preserving frame can be completed");
Expect(exchange.AcquireForRender(second), "second preserving frame can be acquired");
second.frameIndex = 2;
Expect(exchange.PublishCompleted(second), "second preserving frame can be completed");
Expect(!exchange.AcquireForRender(third), "render acquire refuses to drop completed frames");
SystemFrame scheduled;
Expect(exchange.ConsumeCompletedForSchedule(scheduled), "oldest completed frame survives acquire miss");
Expect(scheduled.frameIndex == 1, "preserving acquire keeps FIFO output continuity");
SystemFrameExchangeMetrics metrics = exchange.Metrics();
Expect(metrics.completedDrops == 0, "preserving acquire does not count completed drops");
Expect(metrics.acquireMisses == 1, "preserving acquire miss is counted");
}
void TestCompletedReserveIsBoundedFifo()
{
SystemFrameExchange exchange(MakeBoundedCompletedConfig(4, 2));
for (uint64_t frameIndex = 1; frameIndex <= 3; ++frameIndex)
{
SystemFrame frame;
Expect(exchange.AcquireForRender(frame), "bounded reserve frame can be acquired");
frame.frameIndex = frameIndex;
Expect(exchange.PublishCompleted(frame), "bounded reserve frame can be completed");
}
SystemFrame firstScheduled;
Expect(exchange.ConsumeCompletedForSchedule(firstScheduled), "bounded reserve oldest retained frame can be scheduled");
Expect(firstScheduled.frameIndex == 2, "bounded reserve drops oldest overflow and keeps FIFO order");
SystemFrame secondScheduled;
Expect(exchange.ConsumeCompletedForSchedule(secondScheduled), "bounded reserve second retained frame can be scheduled");
Expect(secondScheduled.frameIndex == 3, "bounded reserve schedules next retained frame");
SystemFrameExchangeMetrics metrics = exchange.Metrics();
Expect(metrics.completedDrops == 1, "bounded completed reserve records oldest overflow drop");
Expect(metrics.scheduledFrames == 2, "bounded reserve schedules retained frames");
}
void TestScheduledFramesAreNotDropped()
{
SystemFrameExchange exchange(MakeConfig(1));
SystemFrame frame;
Expect(exchange.AcquireForRender(frame), "single frame can be acquired");
Expect(exchange.PublishCompleted(frame), "single frame can be completed");
SystemFrame scheduled;
Expect(exchange.ConsumeCompletedForSchedule(scheduled), "single frame can be scheduled");
SystemFrame extra;
Expect(!exchange.AcquireForRender(extra), "scheduled frame is not dropped for render acquire");
SystemFrameExchangeMetrics metrics = exchange.Metrics();
Expect(metrics.acquireMisses == 1, "blocked acquire miss is counted");
Expect(metrics.completedDrops == 0, "scheduled frame is not counted as a completed drop");
}
void TestGenerationValidationRejectsStaleFrames()
{
SystemFrameExchange exchange(MakeConfig(1));
SystemFrame first;
Expect(exchange.AcquireForRender(first), "frame can be acquired");
Expect(exchange.PublishCompleted(first), "frame can be completed");
SystemFrame scheduled;
Expect(exchange.ConsumeCompletedForSchedule(scheduled), "frame can be scheduled");
Expect(exchange.ReleaseScheduledByBytes(scheduled.bytes), "frame can be released");
SystemFrame second;
Expect(exchange.AcquireForRender(second), "slot can be reacquired");
Expect(second.index == first.index, "same slot is reused");
Expect(second.generation != first.generation, "reacquire invalidates stale generation");
Expect(!exchange.PublishCompleted(first), "stale frame cannot be completed");
}
void TestPixelFormatAwareSizing()
{
SystemFrameExchangeConfig config;
config.width = 7;
config.height = 2;
config.pixelFormat = VideoIOPixelFormat::V210;
config.capacity = 1;
SystemFrameExchange exchange(config);
SystemFrame frame;
Expect(exchange.AcquireForRender(frame), "v210 frame can be acquired");
Expect(frame.pixelFormat == VideoIOPixelFormat::V210, "v210 pixel format is preserved");
Expect(frame.rowBytes == static_cast<long>(VideoIORowBytes(VideoIOPixelFormat::V210, 7)), "v210 row bytes are inferred");
config.pixelFormat = VideoIOPixelFormat::Uyvy8;
config.rowBytes = 64;
exchange.Configure(config);
Expect(exchange.AcquireForRender(frame), "explicit row-byte frame can be acquired");
Expect(frame.pixelFormat == VideoIOPixelFormat::Uyvy8, "reconfigured pixel format is preserved");
Expect(frame.rowBytes == 64, "explicit row bytes are preserved");
}
void TestCompletedPollMissIsCounted()
{
SystemFrameExchange exchange(MakeConfig(1));
SystemFrame frame;
Expect(!exchange.ConsumeCompletedForSchedule(frame), "empty completed queue cannot be consumed");
SystemFrameExchangeMetrics metrics = exchange.Metrics();
Expect(metrics.completedPollMisses == 1, "completed poll miss is counted");
}
void TestStableCompletedDepthCanBeObserved()
{
SystemFrameExchange exchange(MakeConfig(1));
SystemFrame frame;
Expect(exchange.AcquireForRender(frame), "stable-depth frame can be acquired");
Expect(exchange.PublishCompleted(frame), "stable-depth frame can be completed");
Expect(
exchange.WaitForStableCompletedDepth(1, std::chrono::milliseconds(1), std::chrono::milliseconds(50)),
"stable completed depth can be observed");
}
void TestStableCompletedDepthTimesOut()
{
SystemFrameExchange exchange(MakeConfig(1));
Expect(
!exchange.WaitForStableCompletedDepth(1, std::chrono::milliseconds(1), std::chrono::milliseconds(1)),
"missing stable completed depth times out");
}
}
int main()
{
TestAcquirePublishesAndSchedules();
TestAcquirePreservesCompletedFrames();
TestCompletedReserveIsBoundedFifo();
TestScheduledFramesAreNotDropped();
TestGenerationValidationRejectsStaleFrames();
TestPixelFormatAwareSizing();
TestCompletedPollMissIsCounted();
TestStableCompletedDepthCanBeObserved();
TestStableCompletedDepthTimesOut();
if (gFailures != 0)
{
std::cerr << gFailures << " frame exchange test failure(s).\n";
return 1;
}
std::cout << "RenderCadenceCompositor frame exchange tests passed.\n";
return 0;
}