Files
video-shader-toys/tests/SystemOutputFramePoolTests.cpp
Aiden 9e3412712c
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
Improvement
2026-05-12 00:00:23 +10:00

171 lines
6.3 KiB
C++

#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;
}