Improvement
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m52s
CI / Windows Release Package (push) Successful in 3m0s

This commit is contained in:
Aiden
2026-05-12 00:00:23 +10:00
parent a434a88108
commit 9e3412712c
22 changed files with 1409 additions and 34 deletions

View File

@@ -200,6 +200,34 @@ void TestOutputRenderPipelineTiming()
Expect(playout.outputCachedFallbackCount == 1, "output render timing counts cached fallbacks");
Expect(playout.outputSyncFallbackCount == 1, "output render timing counts sync fallbacks");
}
void TestSystemMemoryPlayoutStats()
{
HealthTelemetry telemetry;
telemetry.RecordSystemMemoryPlayoutStats(2, 3, 1, 4, 5, 6, 12.5, 24.0);
HealthTelemetry::BackendPlayoutSnapshot playout = telemetry.GetBackendPlayoutSnapshot();
Expect(playout.systemFramePoolFree == 2, "system-memory playout stores free frame count");
Expect(playout.systemFramePoolReady == 3, "system-memory playout stores ready frame count");
Expect(playout.systemFramePoolScheduled == 1, "system-memory playout stores scheduled frame count");
Expect(playout.systemFrameUnderrunCount == 4, "system-memory playout stores underrun count");
Expect(playout.systemFrameRepeatCount == 5, "system-memory playout stores repeat count");
Expect(playout.systemFrameDropCount == 6, "system-memory playout stores drop count");
Expect(playout.systemFrameAgeAtScheduleMilliseconds == 12.5, "system-memory playout stores schedule age");
Expect(playout.systemFrameAgeAtCompletionMilliseconds == 24.0, "system-memory playout stores completion age");
Expect(telemetry.TryRecordSystemMemoryPlayoutStats(1, 0, 2, 7, 8, 9, -1.0, -2.0),
"try system-memory playout stats succeeds when uncontended");
playout = telemetry.GetBackendPlayoutSnapshot();
Expect(playout.systemFramePoolFree == 1, "try system-memory playout stores free frame count");
Expect(playout.systemFramePoolReady == 0, "try system-memory playout stores ready frame count");
Expect(playout.systemFramePoolScheduled == 2, "try system-memory playout stores scheduled frame count");
Expect(playout.systemFrameUnderrunCount == 7, "try system-memory playout stores underrun count");
Expect(playout.systemFrameRepeatCount == 8, "try system-memory playout stores repeat count");
Expect(playout.systemFrameDropCount == 9, "try system-memory playout stores drop count");
Expect(playout.systemFrameAgeAtScheduleMilliseconds == 0.0, "system-memory playout clamps negative schedule age");
Expect(playout.systemFrameAgeAtCompletionMilliseconds == 0.0, "system-memory playout clamps negative completion age");
}
}
int main()
@@ -210,6 +238,7 @@ int main()
TestPersistenceWriteHealth();
TestBackendPlayoutHealth();
TestOutputRenderPipelineTiming();
TestSystemMemoryPlayoutStats();
if (gFailures != 0)
{

View File

@@ -5,6 +5,7 @@
namespace
{
int gFailures = 0;
int gReleasedFrames = 0;
void Expect(bool condition, const char* message)
{
@@ -23,6 +24,22 @@ RenderOutputFrame MakeFrame(uint64_t index)
return frame;
}
void CountReleasedFrame(VideoIOOutputFrame& frame)
{
if (frame.nativeFrame != nullptr)
{
++gReleasedFrames;
frame.nativeFrame = nullptr;
}
}
RenderOutputFrame MakeOwnedFrame(uint64_t index)
{
RenderOutputFrame frame = MakeFrame(index);
frame.releaseFrame = CountReleasedFrame;
return frame;
}
void TestQueuePreservesOrdering()
{
VideoPlayoutPolicy policy;
@@ -58,6 +75,25 @@ void TestBoundedQueueDropsOldestFrame()
Expect(frame.frameIndex == 2, "oldest frame was dropped when queue overflowed");
}
void TestOverflowReleasesDroppedFrame()
{
gReleasedFrames = 0;
VideoPlayoutPolicy policy;
policy.targetReadyFrames = 1;
policy.maxReadyFrames = 1;
RenderOutputQueue queue(policy);
queue.Push(MakeOwnedFrame(1));
queue.Push(MakeOwnedFrame(2));
Expect(gReleasedFrames == 1, "overflow releases dropped ready frame");
RenderOutputFrame frame;
Expect(queue.TryPop(frame), "newest owned frame remains queued");
Expect(frame.frameIndex == 2, "overflow keeps newest owned frame");
Expect(gReleasedFrames == 1, "pop transfers ownership without releasing");
}
void TestUnderrunIsCounted()
{
RenderOutputQueue queue;
@@ -90,14 +126,53 @@ void TestConfigureShrinksDepthToNewCapacity()
Expect(queue.TryPop(frame), "trimmed queue still has newest frame");
Expect(frame.frameIndex == 3, "configure keeps newest ready frame");
}
void TestConfigureReleasesTrimmedFrames()
{
gReleasedFrames = 0;
VideoPlayoutPolicy policy;
policy.maxReadyFrames = 3;
RenderOutputQueue queue(policy);
queue.Push(MakeOwnedFrame(1));
queue.Push(MakeOwnedFrame(2));
queue.Push(MakeOwnedFrame(3));
VideoPlayoutPolicy smallerPolicy;
smallerPolicy.targetReadyFrames = 1;
smallerPolicy.maxReadyFrames = 1;
queue.Configure(smallerPolicy);
Expect(gReleasedFrames == 2, "configure releases trimmed ready frames");
RenderOutputFrame frame;
Expect(queue.TryPop(frame), "trimmed owned queue still has newest frame");
Expect(frame.frameIndex == 3, "configure keeps newest owned frame after release");
}
void TestClearReleasesQueuedFrames()
{
gReleasedFrames = 0;
RenderOutputQueue queue;
queue.Push(MakeOwnedFrame(1));
queue.Push(MakeOwnedFrame(2));
queue.Clear();
RenderOutputQueueMetrics metrics = queue.GetMetrics();
Expect(metrics.depth == 0, "clear empties ready queue");
Expect(gReleasedFrames == 2, "clear releases queued ready frames");
}
}
int main()
{
TestQueuePreservesOrdering();
TestBoundedQueueDropsOldestFrame();
TestOverflowReleasesDroppedFrame();
TestUnderrunIsCounted();
TestConfigureShrinksDepthToNewCapacity();
TestConfigureReleasesTrimmedFrames();
TestClearReleasesQueuedFrames();
if (gFailures != 0)
{

View File

@@ -0,0 +1,170 @@
#include "SystemOutputFramePool.h"
#include <cstdint>
#include <iostream>
namespace
{
int gFailures = 0;
void Expect(bool condition, const char* message)
{
if (condition)
return;
std::cerr << "FAIL: " << message << "\n";
++gFailures;
}
SystemOutputFramePoolConfig MakeConfig(std::size_t capacity = 2)
{
SystemOutputFramePoolConfig config;
config.width = 4;
config.height = 3;
config.pixelFormat = VideoIOPixelFormat::Bgra8;
config.capacity = capacity;
return config;
}
void TestAcquireHonorsCapacityAndFrameShape()
{
SystemOutputFramePool pool(MakeConfig(2));
OutputFrameSlot first;
OutputFrameSlot second;
OutputFrameSlot third;
Expect(pool.AcquireFreeSlot(first), "first slot can be acquired");
Expect(pool.AcquireFreeSlot(second), "second slot can be acquired");
Expect(!pool.AcquireFreeSlot(third), "fixed capacity rejects third acquire");
Expect(first.frame.bytes != nullptr, "acquired slot has system memory");
Expect(first.frame.nativeBuffer == first.frame.bytes, "native buffer points at system memory");
Expect(first.frame.nativeFrame == nullptr, "system frame has no native frame");
Expect(first.frame.width == 4, "frame width is configured");
Expect(first.frame.height == 3, "frame height is configured");
Expect(first.frame.rowBytes == 16, "BGRA8 row bytes are inferred");
Expect(first.frame.pixelFormat == VideoIOPixelFormat::Bgra8, "BGRA8 is the default output format");
Expect(first.frame.bytes != second.frame.bytes, "each slot owns distinct memory");
SystemOutputFramePoolMetrics metrics = pool.GetMetrics();
Expect(metrics.freeCount == 0, "all slots are in use");
Expect(metrics.acquiredCount == 2, "acquired slots are counted");
Expect(metrics.acquireMissCount == 1, "capacity miss is counted");
}
void TestReadySlotsAreConsumedFifo()
{
SystemOutputFramePool pool(MakeConfig(2));
OutputFrameSlot first;
OutputFrameSlot second;
Expect(pool.AcquireFreeSlot(first), "first FIFO slot can be acquired");
Expect(pool.AcquireFreeSlot(second), "second FIFO slot can be acquired");
Expect(pool.PublishReadySlot(first), "first FIFO slot can be published");
Expect(pool.PublishReadySlot(second), "second FIFO slot can be published");
OutputFrameSlot consumed;
Expect(pool.ConsumeReadySlot(consumed), "first ready slot can be consumed");
Expect(consumed.index == first.index, "first published slot is consumed first");
Expect(pool.MarkScheduled(consumed), "consumed slot can be marked scheduled");
Expect(pool.ReleaseScheduledSlot(consumed), "scheduled slot can be released");
Expect(pool.ConsumeReadySlot(consumed), "second ready slot can be consumed");
Expect(consumed.index == second.index, "second published slot is consumed second");
Expect(pool.ReleaseSlot(consumed), "consumed slot can be released without scheduling");
SystemOutputFramePoolMetrics metrics = pool.GetMetrics();
Expect(metrics.freeCount == 2, "released slots return to free pool");
Expect(metrics.readyCount == 0, "ready queue is empty after consumption");
}
void TestReadySlotCanBeScheduledByBuffer()
{
SystemOutputFramePool pool(MakeConfig(1));
OutputFrameSlot slot;
Expect(pool.AcquireFreeSlot(slot), "buffer schedule slot can be acquired");
void* bytes = slot.frame.bytes;
Expect(pool.PublishReadySlot(slot), "buffer schedule slot can be published");
Expect(pool.MarkScheduledByBuffer(bytes), "ready slot can be marked scheduled by buffer");
SystemOutputFramePoolMetrics metrics = pool.GetMetrics();
Expect(metrics.readyCount == 0, "scheduled-by-buffer removes slot from ready queue");
Expect(metrics.scheduledCount == 1, "scheduled-by-buffer counts scheduled slot");
Expect(pool.ReleaseSlotByBuffer(bytes), "scheduled slot can be released by buffer");
metrics = pool.GetMetrics();
Expect(metrics.freeCount == 1, "released-by-buffer slot returns to free pool");
}
void TestInvalidTransitionsAreRejected()
{
SystemOutputFramePool pool(MakeConfig(1));
OutputFrameSlot slot;
Expect(pool.AcquireFreeSlot(slot), "transition slot can be acquired");
Expect(!pool.MarkScheduled(slot), "acquired slot cannot be marked scheduled");
Expect(pool.PublishReadySlot(slot), "acquired slot can be published");
Expect(!pool.PublishReadySlot(slot), "ready slot cannot be published twice");
Expect(pool.ReleaseSlot(slot), "ready slot can be released to free");
Expect(!pool.ReleaseSlot(slot), "free slot cannot be released again");
OutputFrameSlot next;
Expect(pool.AcquireFreeSlot(next), "slot can be reacquired after release");
Expect(next.index == slot.index, "same storage slot can be reused");
Expect(next.generation != slot.generation, "stale handles are invalidated on reacquire");
Expect(!pool.PublishReadySlot(slot), "stale handle cannot publish reacquired slot");
}
void TestPixelFormatAwareSizing()
{
SystemOutputFramePoolConfig config;
config.width = 7;
config.height = 2;
config.pixelFormat = VideoIOPixelFormat::V210;
config.capacity = 1;
SystemOutputFramePool pool(config);
OutputFrameSlot slot;
Expect(pool.AcquireFreeSlot(slot), "v210 slot can be acquired");
Expect(slot.frame.pixelFormat == VideoIOPixelFormat::V210, "slot keeps configured pixel format");
Expect(slot.frame.rowBytes == static_cast<long>(MinimumV210RowBytes(config.width)), "v210 row bytes are inferred");
SystemOutputFramePoolConfig explicitConfig = config;
explicitConfig.pixelFormat = VideoIOPixelFormat::Uyvy8;
explicitConfig.rowBytes = 64;
pool.Configure(explicitConfig);
Expect(pool.AcquireFreeSlot(slot), "explicit row-byte slot can be acquired");
Expect(slot.frame.pixelFormat == VideoIOPixelFormat::Uyvy8, "slot keeps reconfigured pixel format");
Expect(slot.frame.rowBytes == 64, "explicit row bytes are preserved");
}
void TestEmptyReadyQueueUnderrunIsCounted()
{
SystemOutputFramePool pool(MakeConfig(1));
OutputFrameSlot slot;
Expect(!pool.ConsumeReadySlot(slot), "empty ready queue cannot be consumed");
SystemOutputFramePoolMetrics metrics = pool.GetMetrics();
Expect(metrics.readyUnderrunCount == 1, "ready underrun is counted");
}
}
int main()
{
TestAcquireHonorsCapacityAndFrameShape();
TestReadySlotsAreConsumedFifo();
TestReadySlotCanBeScheduledByBuffer();
TestInvalidTransitionsAreRejected();
TestPixelFormatAwareSizing();
TestEmptyReadyQueueUnderrunIsCounted();
if (gFailures != 0)
{
std::cerr << gFailures << " system output frame pool test failure(s).\n";
return 1;
}
std::cout << "SystemOutputFramePool tests passed.\n";
return 0;
}