#include "SystemFrameExchange.h" #include #include #include #include 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(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 TestLatestPublishedFrameCanBePreviewedWithoutConsuming() { SystemFrameExchange exchange(MakeConfig(2)); SystemFrame frame; Expect(exchange.AcquireForRender(frame), "copy snapshot frame can be acquired"); frame.frameIndex = 77; const unsigned char marker = 0x42; std::memset(frame.bytes, marker, static_cast(frame.rowBytes) * frame.height); Expect(exchange.PublishCompleted(frame), "copy snapshot frame can be completed"); SystemFrame preview; Expect(exchange.TryAcquireLatestForPreview(preview), "latest published frame can be acquired for preview"); Expect(preview.frameIndex == 77, "preview frame keeps frame index"); Expect(preview.width == 4 && preview.height == 3, "preview frame keeps frame dimensions"); Expect(preview.bytes != nullptr && static_cast(preview.bytes)[0] == marker, "preview frame points at frame bytes"); Expect(exchange.ReleasePreviewFrame(preview), "preview frame can be released"); SystemFrame scheduled; Expect(exchange.ConsumeCompletedForSchedule(scheduled), "previewing frame does not consume completed frame"); } void TestLatestPublishedFrameCanPreviewScheduledFrame() { SystemFrameExchange exchange(MakeConfig(1)); SystemFrame frame; Expect(exchange.AcquireForRender(frame), "scheduled snapshot frame can be acquired"); frame.frameIndex = 88; Expect(exchange.PublishCompleted(frame), "scheduled snapshot frame can be completed"); SystemFrame scheduled; Expect(exchange.ConsumeCompletedForSchedule(scheduled), "snapshot test frame can be scheduled"); SystemFrame preview; Expect(exchange.TryAcquireLatestForPreview(preview), "latest scheduled frame can be acquired for preview"); Expect(preview.frameIndex == 88, "scheduled preview keeps frame index"); Expect(exchange.ReleasePreviewFrame(preview), "scheduled preview frame can be released"); } void TestPreviewFramePinsReleasedSlotUntilPreviewRelease() { SystemFrameExchange exchange(MakeConfig(1)); SystemFrame frame; Expect(exchange.AcquireForRender(frame), "preview pin frame can be acquired"); frame.frameIndex = 99; Expect(exchange.PublishCompleted(frame), "preview pin frame can be completed"); SystemFrame preview; Expect(exchange.TryAcquireLatestForPreview(preview), "preview can acquire frame before schedule"); SystemFrame scheduled; Expect(exchange.ConsumeCompletedForSchedule(scheduled), "previewed frame can still be scheduled"); Expect(exchange.ReleaseScheduledByBytes(scheduled.bytes), "scheduled frame can be released while preview holds it"); SystemFrame blocked; Expect(!exchange.AcquireForRender(blocked), "preview reader prevents immediate slot reuse"); Expect(exchange.ReleasePreviewFrame(preview), "preview pin can be released"); Expect(exchange.AcquireForRender(blocked), "slot can be reused after preview release"); } void TestMultiplePreviewReadersPinReleasedSlotUntilAllRelease() { SystemFrameExchange exchange(MakeConfig(1)); SystemFrame frame; Expect(exchange.AcquireForRender(frame), "multi-preview frame can be acquired"); frame.frameIndex = 100; Expect(exchange.PublishCompleted(frame), "multi-preview frame can be completed"); SystemFrame firstPreview; SystemFrame secondPreview; Expect(exchange.TryAcquireLatestForPreview(firstPreview), "first preview reader can acquire"); Expect(exchange.TryAcquireLatestForPreview(secondPreview), "second preview reader can acquire"); SystemFrame scheduled; Expect(exchange.ConsumeCompletedForSchedule(scheduled), "multi-preview frame can be scheduled"); Expect(exchange.ReleaseScheduledByBytes(scheduled.bytes), "multi-preview scheduled frame can be released"); SystemFrame blocked; Expect(!exchange.AcquireForRender(blocked), "slot remains pinned while two preview readers exist"); Expect(exchange.ReleasePreviewFrame(firstPreview), "first preview reader can release"); Expect(!exchange.AcquireForRender(blocked), "slot remains pinned until last preview reader releases"); Expect(exchange.ReleasePreviewFrame(secondPreview), "second preview reader can release"); Expect(exchange.AcquireForRender(blocked), "slot is reusable after all preview readers release"); } void TestInvalidPreviewReleaseIsRejected() { SystemFrameExchange exchange(MakeConfig(1)); SystemFrame invalid; Expect(!exchange.ReleasePreviewFrame(invalid), "empty preview frame release is rejected"); SystemFrame frame; Expect(exchange.AcquireForRender(frame), "invalid release source frame can be acquired"); frame.frameIndex = 101; Expect(exchange.PublishCompleted(frame), "invalid release source frame can be completed"); SystemFrame preview; Expect(exchange.TryAcquireLatestForPreview(preview), "preview frame can be acquired for invalid release test"); Expect(exchange.ReleasePreviewFrame(preview), "valid preview release succeeds"); Expect(!exchange.ReleasePreviewFrame(preview), "double preview release is rejected"); } void TestStalePreviewReleaseIsRejectedAfterReconfigure() { SystemFrameExchange exchange(MakeConfig(1)); SystemFrame frame; Expect(exchange.AcquireForRender(frame), "stale preview source frame can be acquired"); frame.frameIndex = 102; Expect(exchange.PublishCompleted(frame), "stale preview source frame can be completed"); SystemFrame preview; Expect(exchange.TryAcquireLatestForPreview(preview), "preview frame can be acquired before reconfigure"); exchange.Configure(MakeConfig(1)); Expect(!exchange.ReleasePreviewFrame(preview), "preview release after reconfigure is rejected as stale"); } 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(); TestLatestPublishedFrameCanBePreviewedWithoutConsuming(); TestLatestPublishedFrameCanPreviewScheduledFrame(); TestPreviewFramePinsReleasedSlotUntilPreviewRelease(); TestMultiplePreviewReadersPinReleasedSlotUntilAllRelease(); TestInvalidPreviewReleaseIsRejected(); TestStalePreviewReleaseIsRejectedAfterReconfigure(); 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; }