Clock updates
This commit is contained in:
@@ -19,7 +19,8 @@ RenderThread
|
|||||||
|
|
||||||
InputFrameMailbox
|
InputFrameMailbox
|
||||||
owns latest disposable CPU input slots
|
owns latest disposable CPU input slots
|
||||||
drops older unsampled input frames when newer frames arrive
|
keeps a bounded three-ready-frame input buffer for render
|
||||||
|
trims frames beyond that bound to avoid runaway input latency
|
||||||
protects the one frame currently being uploaded by render
|
protects the one frame currently being uploaded by render
|
||||||
uses a single contiguous copy when capture row stride matches mailbox row stride
|
uses a single contiguous copy when capture row stride matches mailbox row stride
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ DeckLinkOutputThread
|
|||||||
never renders
|
never renders
|
||||||
```
|
```
|
||||||
|
|
||||||
Startup warms up real rendered frames before DeckLink scheduled playback starts. When DeckLink input is available, startup also waits briefly for two ready input frames before the render thread starts so the first render ticks are deliberate rather than lucky.
|
Startup warms up real rendered frames before DeckLink scheduled playback starts. When DeckLink input is available, startup also waits briefly for three ready input frames before the render thread starts so the first render ticks are deliberate rather than lucky.
|
||||||
|
|
||||||
## Current Scope
|
## Current Scope
|
||||||
|
|
||||||
@@ -46,9 +47,9 @@ Included now:
|
|||||||
- hidden render-thread-owned OpenGL context
|
- hidden render-thread-owned OpenGL context
|
||||||
- simple smooth-motion renderer
|
- simple smooth-motion renderer
|
||||||
- BGRA8-only output
|
- BGRA8-only output
|
||||||
- non-blocking latest-frame input mailbox
|
- non-blocking three-frame FIFO input mailbox for render
|
||||||
- fast contiguous mailbox copy path for matching input row strides
|
- fast contiguous mailbox copy path for matching input row strides
|
||||||
- bounded two-frame input warmup before render cadence starts
|
- bounded three-frame input warmup before render cadence starts
|
||||||
- render-thread-owned input texture upload
|
- render-thread-owned input texture upload
|
||||||
- async PBO readback
|
- async PBO readback
|
||||||
- latest-N system-memory frame exchange
|
- latest-N system-memory frame exchange
|
||||||
@@ -122,9 +123,9 @@ This tracks parity with `apps/LoopThroughWithOpenGLCompositing`.
|
|||||||
- [x] Trigger parameter pulse count/time for latest trigger events
|
- [x] Trigger parameter pulse count/time for latest trigger events
|
||||||
- [x] Optional DeckLink input capture
|
- [x] Optional DeckLink input capture
|
||||||
- [x] UYVY8 input capture with render-thread GPU decode to shader input texture
|
- [x] UYVY8 input capture with render-thread GPU decode to shader input texture
|
||||||
- [x] Latest-frame CPU input mailbox
|
- [x] Three-frame FIFO CPU input mailbox for render
|
||||||
- [x] Fast contiguous input mailbox copy when source/destination stride matches
|
- [x] Fast contiguous input mailbox copy when source/destination stride matches
|
||||||
- [x] Bounded two-frame input warmup before render cadence starts
|
- [x] Bounded three-frame input warmup before render cadence starts
|
||||||
- [x] Render-owned input texture upload
|
- [x] Render-owned input texture upload
|
||||||
- [x] Runtime shaders receive input through `gVideoInput`
|
- [x] Runtime shaders receive input through `gVideoInput`
|
||||||
- [x] Live DeckLink input bound to `gVideoInput`
|
- [x] Live DeckLink input bound to `gVideoInput`
|
||||||
@@ -254,10 +255,10 @@ Startup order is:
|
|||||||
2. try to attach DeckLink input for the configured input mode
|
2. try to attach DeckLink input for the configured input mode
|
||||||
3. prefer BGRA8 capture, otherwise accept raw UYVY8 capture and configure the mailbox for UYVY8 bytes
|
3. prefer BGRA8 capture, otherwise accept raw UYVY8 capture and configure the mailbox for UYVY8 bytes
|
||||||
4. start `DeckLinkInputThread`
|
4. start `DeckLinkInputThread`
|
||||||
5. wait briefly for two ready input warmup frames before starting render cadence
|
5. wait briefly for three ready input warmup frames before starting render cadence
|
||||||
6. leave input absent if discovery, setup, format support, or stream startup fails
|
6. leave input absent if discovery, setup, format support, or stream startup fails
|
||||||
|
|
||||||
`DeckLinkInput` and `DeckLinkInputThread` are deliberately narrow. They capture BGRA8 frames directly or raw UYVY8 frames into `InputFrameMailbox`; they do not call GL, render, preview, screenshot, shader, or output scheduling code. UYVY8-to-RGBA decode happens later inside the render-thread-owned input texture upload path, so the DeckLink callback stays a capture/copy edge only. The mailbox uses one contiguous copy when the capture row stride matches the configured mailbox row stride, and falls back to row-by-row copy only for padded or mismatched frames. Unsupported input modes or formats outside BGRA8/UYVY8 are reported explicitly and treated as an unavailable edge rather than silently converted.
|
`DeckLinkInput` and `DeckLinkInputThread` are deliberately narrow. They capture BGRA8 frames directly or raw UYVY8 frames into `InputFrameMailbox`; they do not call GL, render, preview, screenshot, shader, or output scheduling code. UYVY8-to-RGBA decode happens later inside the render-thread-owned input texture upload path, so the DeckLink callback stays a capture/copy edge only. The render upload path consumes the oldest ready input frame from a bounded three-ready-frame queue, so the input behaves like a small jitter buffer instead of a latest-frame preview mailbox. The mailbox trims older frames beyond that bound to avoid runaway latency, uses one contiguous copy when the capture row stride matches the configured mailbox row stride, and falls back to row-by-row copy only for padded or mismatched frames. Unsupported input modes or formats outside BGRA8/UYVY8 are reported explicitly and treated as an unavailable edge rather than silently converted.
|
||||||
|
|
||||||
Input warmup is startup-only and bounded. It may delay render-thread startup for a short window, but it does not add waits to the steady-state render cadence loop.
|
Input warmup is startup-only and bounded. It may delay render-thread startup for a short window, but it does not add waits to the steady-state render cadence loop.
|
||||||
|
|
||||||
@@ -269,6 +270,12 @@ Normal cadence samples are available through `GET /api/state` and are not printe
|
|||||||
- warning when schedule failures increase
|
- warning when schedule failures increase
|
||||||
- error when the app/DeckLink output buffer is starved
|
- error when the app/DeckLink output buffer is starved
|
||||||
|
|
||||||
|
Render cadence telemetry:
|
||||||
|
|
||||||
|
- `clockOverruns`: render cadence overruns where missed time was detected
|
||||||
|
- `clockSkippedFrames`: selected-cadence frame intervals skipped instead of catch-up rendering
|
||||||
|
- `clockOveruns` / `clockSkipped`: compatibility aliases for quick polling scripts
|
||||||
|
|
||||||
Input telemetry:
|
Input telemetry:
|
||||||
|
|
||||||
- `inputFramesReceived`: frames accepted into `InputFrameMailbox`
|
- `inputFramesReceived`: frames accepted into `InputFrameMailbox`
|
||||||
@@ -391,7 +398,7 @@ This app keeps the same core behavior but splits it into modules that can grow:
|
|||||||
- `frames/`: system-memory handoff
|
- `frames/`: system-memory handoff
|
||||||
- `platform/`: COM/Win32/hidden GL context support
|
- `platform/`: COM/Win32/hidden GL context support
|
||||||
- `render/`: cadence thread, clock, and simple renderer
|
- `render/`: cadence thread, clock, and simple renderer
|
||||||
- `frames/InputFrameMailbox`: non-blocking latest-frame CPU input handoff with contiguous-copy fast path for matching row strides
|
- `frames/InputFrameMailbox`: non-blocking bounded FIFO CPU input handoff with contiguous-copy fast path for matching row strides
|
||||||
- `render/InputFrameTexture`: render-thread-owned upload of the latest CPU input frame into GL, including raw UYVY8 decode into the shader-visible input texture
|
- `render/InputFrameTexture`: render-thread-owned upload of the latest CPU input frame into GL, including raw UYVY8 decode into the shader-visible input texture
|
||||||
- `render/readback/`: PBO-backed BGRA8 readback and completed-frame publication
|
- `render/readback/`: PBO-backed BGRA8 readback and completed-frame publication
|
||||||
- `render/runtime/RuntimeRenderScene`: render-thread-owned GL scene for ready runtime shader layers
|
- `render/runtime/RuntimeRenderScene`: render-thread-owned GL scene for ready runtime shader layers
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ int main(int argc, char** argv)
|
|||||||
inputMailboxConfig.pixelFormat = VideoIOPixelFormat::Bgra8;
|
inputMailboxConfig.pixelFormat = VideoIOPixelFormat::Bgra8;
|
||||||
inputMailboxConfig.rowBytes = VideoIORowBytes(inputMailboxConfig.pixelFormat, inputMailboxConfig.width);
|
inputMailboxConfig.rowBytes = VideoIORowBytes(inputMailboxConfig.pixelFormat, inputMailboxConfig.width);
|
||||||
inputMailboxConfig.capacity = 4;
|
inputMailboxConfig.capacity = 4;
|
||||||
|
inputMailboxConfig.maxReadyFrames = 3;
|
||||||
InputFrameMailbox inputMailbox(inputMailboxConfig);
|
InputFrameMailbox inputMailbox(inputMailboxConfig);
|
||||||
|
|
||||||
VideoFormat inputVideoMode;
|
VideoFormat inputVideoMode;
|
||||||
@@ -139,7 +140,7 @@ int main(int argc, char** argv)
|
|||||||
deckLinkInputStarted = true;
|
deckLinkInputStarted = true;
|
||||||
RenderCadenceCompositor::Log("app", "DeckLink input edge started for " + inputVideoMode.displayName + ".");
|
RenderCadenceCompositor::Log("app", "DeckLink input edge started for " + inputVideoMode.displayName + ".");
|
||||||
RenderCadenceCompositor::Log("app", "Waiting for DeckLink input warmup frames.");
|
RenderCadenceCompositor::Log("app", "Waiting for DeckLink input warmup frames.");
|
||||||
constexpr std::size_t kInputStartupBufferedFrames = 2;
|
constexpr std::size_t kInputStartupBufferedFrames = 3;
|
||||||
constexpr std::chrono::milliseconds kInputWarmupTimeout(1000);
|
constexpr std::chrono::milliseconds kInputWarmupTimeout(1000);
|
||||||
if (WaitForInputWarmup(inputMailbox, kInputStartupBufferedFrames, kInputWarmupTimeout))
|
if (WaitForInputWarmup(inputMailbox, kInputStartupBufferedFrames, kInputWarmupTimeout))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ bool InputFrameMailbox::SubmitFrame(const void* bytes, unsigned rowBytes, uint64
|
|||||||
slot.frameIndex = frameIndex;
|
slot.frameIndex = frameIndex;
|
||||||
++slot.generation;
|
++slot.generation;
|
||||||
mReadyIndices.push_back(slotIndex);
|
mReadyIndices.push_back(slotIndex);
|
||||||
|
TrimReadyFramesLocked();
|
||||||
++mCounters.submittedFrames;
|
++mCounters.submittedFrames;
|
||||||
mCounters.latestFrameIndex = frameIndex;
|
mCounters.latestFrameIndex = frameIndex;
|
||||||
mCounters.hasSubmittedFrame = true;
|
mCounters.hasSubmittedFrame = true;
|
||||||
@@ -151,6 +152,27 @@ bool InputFrameMailbox::TryAcquireLatest(InputFrame& frame)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InputFrameMailbox::TryAcquireOldest(InputFrame& frame)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
while (!mReadyIndices.empty())
|
||||||
|
{
|
||||||
|
const std::size_t index = mReadyIndices.front();
|
||||||
|
mReadyIndices.pop_front();
|
||||||
|
if (index >= mSlots.size() || mSlots[index].state != InputFrameSlotState::Ready)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
mSlots[index].state = InputFrameSlotState::Reading;
|
||||||
|
FillFrameLocked(index, frame);
|
||||||
|
++mCounters.consumedFrames;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame = InputFrame();
|
||||||
|
++mCounters.consumeMisses;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool InputFrameMailbox::Release(const InputFrame& frame)
|
bool InputFrameMailbox::Release(const InputFrame& frame)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
@@ -246,6 +268,14 @@ bool InputFrameMailbox::DropOldestReadyLocked()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputFrameMailbox::TrimReadyFramesLocked()
|
||||||
|
{
|
||||||
|
if (mConfig.maxReadyFrames == 0)
|
||||||
|
return;
|
||||||
|
while (mReadyIndices.size() > mConfig.maxReadyFrames)
|
||||||
|
DropOldestReadyLocked();
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t InputFrameMailbox::FrameByteCount() const
|
std::size_t InputFrameMailbox::FrameByteCount() const
|
||||||
{
|
{
|
||||||
return static_cast<std::size_t>(mConfig.rowBytes) * static_cast<std::size_t>(mConfig.height);
|
return static_cast<std::size_t>(mConfig.rowBytes) * static_cast<std::size_t>(mConfig.height);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ struct InputFrameMailboxConfig
|
|||||||
VideoIOPixelFormat pixelFormat = VideoIOPixelFormat::Bgra8;
|
VideoIOPixelFormat pixelFormat = VideoIOPixelFormat::Bgra8;
|
||||||
unsigned rowBytes = 0;
|
unsigned rowBytes = 0;
|
||||||
std::size_t capacity = 0;
|
std::size_t capacity = 0;
|
||||||
|
std::size_t maxReadyFrames = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InputFrame
|
struct InputFrame
|
||||||
@@ -64,6 +65,7 @@ public:
|
|||||||
|
|
||||||
bool SubmitFrame(const void* bytes, unsigned rowBytes, uint64_t frameIndex);
|
bool SubmitFrame(const void* bytes, unsigned rowBytes, uint64_t frameIndex);
|
||||||
bool TryAcquireLatest(InputFrame& frame);
|
bool TryAcquireLatest(InputFrame& frame);
|
||||||
|
bool TryAcquireOldest(InputFrame& frame);
|
||||||
bool Release(const InputFrame& frame);
|
bool Release(const InputFrame& frame);
|
||||||
void Clear();
|
void Clear();
|
||||||
InputFrameMailboxMetrics Metrics() const;
|
InputFrameMailboxMetrics Metrics() const;
|
||||||
@@ -80,6 +82,7 @@ private:
|
|||||||
bool IsValidLocked(const InputFrame& frame) const;
|
bool IsValidLocked(const InputFrame& frame) const;
|
||||||
void FillFrameLocked(std::size_t index, InputFrame& frame) const;
|
void FillFrameLocked(std::size_t index, InputFrame& frame) const;
|
||||||
bool DropOldestReadyLocked();
|
bool DropOldestReadyLocked();
|
||||||
|
void TrimReadyFramesLocked();
|
||||||
std::size_t FrameByteCount() const;
|
std::size_t FrameByteCount() const;
|
||||||
|
|
||||||
mutable std::mutex mMutex;
|
mutable std::mutex mMutex;
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ GLuint InputFrameTexture::PollAndUpload(InputFrameMailbox* mailbox)
|
|||||||
return mTexture;
|
return mTexture;
|
||||||
|
|
||||||
InputFrame frame;
|
InputFrame frame;
|
||||||
if (!mailbox->TryAcquireLatest(frame))
|
if (!mailbox->TryAcquireOldest(frame))
|
||||||
{
|
{
|
||||||
++mUploadMisses;
|
++mUploadMisses;
|
||||||
mLastUploadMilliseconds = 0.0;
|
mLastUploadMilliseconds = 0.0;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ RenderCadenceClock::RenderCadenceClock(double frameDurationMilliseconds)
|
|||||||
void RenderCadenceClock::Reset(TimePoint now)
|
void RenderCadenceClock::Reset(TimePoint now)
|
||||||
{
|
{
|
||||||
mNextRenderTime = now;
|
mNextRenderTime = now;
|
||||||
|
mPendingFrameAdvance = 1;
|
||||||
mOverrunCount = 0;
|
mOverrunCount = 0;
|
||||||
mSkippedFrameCount = 0;
|
mSkippedFrameCount = 0;
|
||||||
}
|
}
|
||||||
@@ -27,10 +28,12 @@ RenderCadenceClock::Tick RenderCadenceClock::Poll(TimePoint now)
|
|||||||
}
|
}
|
||||||
|
|
||||||
tick.due = true;
|
tick.due = true;
|
||||||
|
mPendingFrameAdvance = 1;
|
||||||
const Duration lateBy = now - mNextRenderTime;
|
const Duration lateBy = now - mNextRenderTime;
|
||||||
if (lateBy > mFrameDuration)
|
if (lateBy > mFrameDuration)
|
||||||
{
|
{
|
||||||
tick.skippedFrames = static_cast<uint64_t>(lateBy / mFrameDuration);
|
tick.skippedFrames = static_cast<uint64_t>(lateBy / mFrameDuration);
|
||||||
|
mPendingFrameAdvance += tick.skippedFrames;
|
||||||
++mOverrunCount;
|
++mOverrunCount;
|
||||||
mSkippedFrameCount += tick.skippedFrames;
|
mSkippedFrameCount += tick.skippedFrames;
|
||||||
}
|
}
|
||||||
@@ -39,7 +42,8 @@ RenderCadenceClock::Tick RenderCadenceClock::Poll(TimePoint now)
|
|||||||
|
|
||||||
void RenderCadenceClock::MarkRendered(TimePoint now)
|
void RenderCadenceClock::MarkRendered(TimePoint now)
|
||||||
{
|
{
|
||||||
mNextRenderTime += mFrameDuration;
|
mNextRenderTime += mFrameDuration * mPendingFrameAdvance;
|
||||||
|
mPendingFrameAdvance = 1;
|
||||||
if (now - mNextRenderTime > mFrameDuration * 4)
|
if (now - mNextRenderTime > mFrameDuration * 4)
|
||||||
mNextRenderTime = now + mFrameDuration;
|
mNextRenderTime = now + mFrameDuration;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
Duration mFrameDuration;
|
Duration mFrameDuration;
|
||||||
TimePoint mNextRenderTime = Clock::now();
|
TimePoint mNextRenderTime = Clock::now();
|
||||||
|
uint64_t mPendingFrameAdvance = 1;
|
||||||
uint64_t mOverrunCount = 0;
|
uint64_t mOverrunCount = 0;
|
||||||
uint64_t mSkippedFrameCount = 0;
|
uint64_t mSkippedFrameCount = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -60,6 +60,27 @@ void TestLatePollRecordsSkippedFrames()
|
|||||||
Expect(cadence.SkippedFrameCount() == 3, "late poll accumulates skipped frames");
|
Expect(cadence.SkippedFrameCount() == 3, "late poll accumulates skipped frames");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestLatePollSkipsMissedIntervalsInsteadOfCatchingUp()
|
||||||
|
{
|
||||||
|
using Clock = RenderCadenceClock::Clock;
|
||||||
|
RenderCadenceClock cadence(10.0);
|
||||||
|
const auto start = Clock::now();
|
||||||
|
cadence.Reset(start);
|
||||||
|
|
||||||
|
const auto late = start + std::chrono::milliseconds(35);
|
||||||
|
const auto tick = cadence.Poll(late);
|
||||||
|
Expect(tick.due, "late skipped-interval poll is due");
|
||||||
|
Expect(tick.skippedFrames == 3, "late skipped-interval poll counts missed frames");
|
||||||
|
|
||||||
|
cadence.MarkRendered(late);
|
||||||
|
Expect(cadence.NextRenderTime() > late, "late render schedules the next tick in the future");
|
||||||
|
Expect(cadence.NextRenderTime() - late <= std::chrono::milliseconds(6), "late render does not leave catch-up frames due immediately");
|
||||||
|
|
||||||
|
const auto immediateFollowup = cadence.Poll(late);
|
||||||
|
Expect(!immediateFollowup.due, "cadence does not allow an immediate catch-up render after a late frame");
|
||||||
|
Expect(immediateFollowup.sleepFor > RenderCadenceClock::Duration::zero(), "cadence reports wait time after skipping missed intervals");
|
||||||
|
}
|
||||||
|
|
||||||
void TestMarkRenderedRebasesAfterLargeStall()
|
void TestMarkRenderedRebasesAfterLargeStall()
|
||||||
{
|
{
|
||||||
using Clock = RenderCadenceClock::Clock;
|
using Clock = RenderCadenceClock::Clock;
|
||||||
@@ -81,6 +102,7 @@ int main()
|
|||||||
TestEarlyPollWaitsWithoutAdvancing();
|
TestEarlyPollWaitsWithoutAdvancing();
|
||||||
TestDuePollRendersWithoutSkipping();
|
TestDuePollRendersWithoutSkipping();
|
||||||
TestLatePollRecordsSkippedFrames();
|
TestLatePollRecordsSkippedFrames();
|
||||||
|
TestLatePollSkipsMissedIntervalsInsteadOfCatchingUp();
|
||||||
TestMarkRenderedRebasesAfterLargeStall();
|
TestMarkRenderedRebasesAfterLargeStall();
|
||||||
|
|
||||||
if (gFailures != 0)
|
if (gFailures != 0)
|
||||||
|
|||||||
@@ -29,6 +29,13 @@ InputFrameMailboxConfig MakeConfig(std::size_t capacity = 2)
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InputFrameMailboxConfig MakeBufferedConfig(std::size_t capacity = 4, std::size_t maxReadyFrames = 2)
|
||||||
|
{
|
||||||
|
InputFrameMailboxConfig config = MakeConfig(capacity);
|
||||||
|
config.maxReadyFrames = maxReadyFrames;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<unsigned char> MakeFrame(unsigned char value)
|
std::vector<unsigned char> MakeFrame(unsigned char value)
|
||||||
{
|
{
|
||||||
return std::vector<unsigned char>(16, value);
|
return std::vector<unsigned char>(16, value);
|
||||||
@@ -90,6 +97,48 @@ void TestReadingFrameIsProtected()
|
|||||||
Expect(mailbox.Release(acquired), "protected frame releases");
|
Expect(mailbox.Release(acquired), "protected frame releases");
|
||||||
Expect(mailbox.SubmitFrame(frame2.data(), 8, 2), "producer can submit after release");
|
Expect(mailbox.SubmitFrame(frame2.data(), 8, 2), "producer can submit after release");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestAcquireOldestConsumesFifoWithoutDroppingReadyFrames()
|
||||||
|
{
|
||||||
|
InputFrameMailbox mailbox(MakeConfig(3));
|
||||||
|
const std::vector<unsigned char> frame1 = MakeFrame(1);
|
||||||
|
const std::vector<unsigned char> frame2 = MakeFrame(2);
|
||||||
|
|
||||||
|
Expect(mailbox.SubmitFrame(frame1.data(), 8, 1), "fifo first frame submits");
|
||||||
|
Expect(mailbox.SubmitFrame(frame2.data(), 8, 2), "fifo second frame submits");
|
||||||
|
|
||||||
|
InputFrame acquired;
|
||||||
|
Expect(mailbox.TryAcquireOldest(acquired), "fifo oldest frame acquired");
|
||||||
|
Expect(acquired.frameIndex == 1, "fifo acquire returns oldest frame");
|
||||||
|
Expect(mailbox.Release(acquired), "fifo acquired frame releases");
|
||||||
|
|
||||||
|
const InputFrameMailboxMetrics metrics = mailbox.Metrics();
|
||||||
|
Expect(metrics.readyCount == 1, "fifo acquire leaves newer frame ready");
|
||||||
|
Expect(metrics.droppedReadyFrames == 0, "fifo acquire does not drop newer ready frame");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestMaxReadyFramesKeepsConfiguredInputBuffer()
|
||||||
|
{
|
||||||
|
InputFrameMailbox mailbox(MakeBufferedConfig(4, 3));
|
||||||
|
const std::vector<unsigned char> frame1 = MakeFrame(1);
|
||||||
|
const std::vector<unsigned char> frame2 = MakeFrame(2);
|
||||||
|
const std::vector<unsigned char> frame3 = MakeFrame(3);
|
||||||
|
const std::vector<unsigned char> frame4 = MakeFrame(4);
|
||||||
|
|
||||||
|
Expect(mailbox.SubmitFrame(frame1.data(), 8, 1), "bounded first frame submits");
|
||||||
|
Expect(mailbox.SubmitFrame(frame2.data(), 8, 2), "bounded second frame submits");
|
||||||
|
Expect(mailbox.SubmitFrame(frame3.data(), 8, 3), "bounded third frame submits");
|
||||||
|
Expect(mailbox.SubmitFrame(frame4.data(), 8, 4), "bounded fourth frame submits");
|
||||||
|
|
||||||
|
InputFrame acquired;
|
||||||
|
Expect(mailbox.TryAcquireOldest(acquired), "bounded oldest available frame acquired");
|
||||||
|
Expect(acquired.frameIndex == 2, "bounded buffer trims oldest beyond configured ready frame limit");
|
||||||
|
Expect(mailbox.Release(acquired), "bounded acquired frame releases");
|
||||||
|
|
||||||
|
const InputFrameMailboxMetrics metrics = mailbox.Metrics();
|
||||||
|
Expect(metrics.readyCount == 2, "bounded acquire leaves remaining configured ready frames");
|
||||||
|
Expect(metrics.droppedReadyFrames == 1, "bounded buffer records trimmed frame");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
@@ -97,6 +146,8 @@ int main()
|
|||||||
TestAcquireLatestDropsOlderReadyFrames();
|
TestAcquireLatestDropsOlderReadyFrames();
|
||||||
TestSubmitDropsOldestWhenFull();
|
TestSubmitDropsOldestWhenFull();
|
||||||
TestReadingFrameIsProtected();
|
TestReadingFrameIsProtected();
|
||||||
|
TestAcquireOldestConsumesFifoWithoutDroppingReadyFrames();
|
||||||
|
TestMaxReadyFramesKeepsConfiguredInputBuffer();
|
||||||
|
|
||||||
if (gFailures != 0)
|
if (gFailures != 0)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user