DeckLink Render Cadence Probe
This is a deliberately small architecture probe for the Phase 7.7 playout model.
It is not the main app and does not use the main runtime, shader stack, preview path, input upload path, or render engine.
What It Tests
The probe validates the clean playout spine:
single OpenGL render thread
owns its own hidden GL context
renders a simple moving BGRA8 pattern at output cadence
queues GPU readback through a PBO ring
copies completed readbacks into latest-N system-memory slots
system-memory frame store
owns free / rendering / completed / scheduled slots
drops old completed unscheduled frames when render cadence needs space
protects scheduled frames until DeckLink completion
DeckLink playout thread
consumes completed system-memory frames
keeps a small scheduled buffer filled
does not render
Startup warms up rendered frames before starting DeckLink scheduled playback.
How To Build
cmake --build --preset build-debug --target DeckLinkRenderCadenceProbe -- /m:1
The executable is:
build\vs2022-x64-debug\Debug\DeckLinkRenderCadenceProbe.exe
How To Run
Run it from a terminal so you can see the telemetry:
build\vs2022-x64-debug\Debug\DeckLinkRenderCadenceProbe.exe
Press Enter to stop.
The first version assumes 1080p59.94 / 1920x1080 output and BGRA8 system-memory frames.
What To Watch
The probe prints one line per second:
renderFps: cadence render throughputscheduleFps: DeckLink scheduling throughputfree: free system-memory slotscompleted: rendered, unscheduled slotsscheduled: slots currently owned by DeckLinkdrops: old completed unscheduled frames recycled by the latest-N cachepboMiss: PBO ring was full when trying to queue readbacklate: DeckLink displayed-late completionsdropped: DeckLink dropped completionsdecklinkBuffered: actual DeckLink buffered-frame count when available
For a healthy architecture proof, expect:
renderFpsclose to the selected output cadencescheduleFpsclose to the selected output cadence after warmupscheduledhovering near the target buffer depthlateanddroppednot increasing continuously- visible motion that is smooth on the DeckLink output
Interpretation
If this probe is smooth at 59.94/60, the broad architecture is viable and the main app's remaining stutters are likely caused by integration details such as input upload, shared render-thread work, preview/screenshot work, or runtime/render-state coupling.
If this probe is not smooth, the problem is lower level: DeckLink scheduling, OpenGL readback, Windows scheduling, or hardware/driver behavior.
Initial Result
Date: 2026-05-12
User-visible result:
- output looked smooth
Representative telemetry:
renderFps=59.9 scheduleFps=59.9 free=7 completed=1 scheduled=4 drops=0 pboMiss=0 completions=119 late=0 dropped=0 decklinkBuffered=4
renderFps=59.9 scheduleFps=59.9 free=7 completed=1 scheduled=4 drops=0 pboMiss=0 completions=179 late=0 dropped=0 decklinkBuffered=4
renderFps=59.8 scheduleFps=59.8 free=7 completed=1 scheduled=4 drops=0 pboMiss=0 completions=239 late=0 dropped=0 decklinkBuffered=4
renderFps=60.8 scheduleFps=59.8 free=7 completed=1 scheduled=4 drops=0 pboMiss=0 completions=299 late=0 dropped=0 decklinkBuffered=4
renderFps=59.9 scheduleFps=59.9 free=7 completed=1 scheduled=4 drops=0 pboMiss=0 completions=360 late=0 dropped=0 decklinkBuffered=4
renderFps=59.8 scheduleFps=60.8 free=8 completed=0 scheduled=4 drops=0 pboMiss=0 completions=420 late=0 dropped=0 decklinkBuffered=4
Read:
- the clean architecture can sustain the selected output cadence on the test machine
- BGRA8 PBO readback is viable when isolated from the main app's other render-thread work
- latest-N system-memory buffering stayed stable
- DeckLink actual buffered depth stayed at 4
- there were no late frames, dropped frames, completed-frame drops, or PBO misses in the sampled output
Implication:
The main app's remaining stutters are likely integration/ownership issues rather than a fundamental DeckLink/OpenGL/BGRA8 readback limit. The highest-value suspects are input upload before output render, shared render-thread queue contention, preview/screenshot work, and runtime/render-state work on the output path.