Files
video-shader-toys/apps/RenderCadenceCompositor/README.md
Aiden 9938a6cc26
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m54s
CI / Windows Release Package (push) Successful in 3m2s
http
2026-05-12 12:38:54 +10:00

254 lines
8.7 KiB
Markdown

# RenderCadenceCompositor
This app is the modular version of the working DeckLink render-cadence probe.
Its job is to prove the production-facing foundation before the current compositor's shader/runtime/control features are ported over.
Before adding features here, read the guardrails in [Render Cadence Golden Rules](../../docs/RENDER_CADENCE_GOLDEN_RULES.md).
## Architecture
```text
RenderThread
owns a hidden OpenGL context
renders simple BGRA8 motion at selected cadence
queues async PBO readback
publishes completed frames into SystemFrameExchange
SystemFrameExchange
owns Free / Rendering / Completed / Scheduled slots
drops old completed unscheduled frames when render needs space
protects scheduled frames until DeckLink completion
DeckLinkOutputThread
consumes completed system-memory frames
schedules them into DeckLink up to target depth
never renders
```
Startup warms up real rendered frames before DeckLink scheduled playback starts.
## Current Scope
Included now:
- output-only DeckLink
- hidden render-thread-owned OpenGL context
- simple smooth-motion renderer
- BGRA8-only output
- async PBO readback
- latest-N system-memory frame exchange
- rendered-frame warmup
- background Slang compile of `shaders/happy-accident`
- app-owned submission of a completed shader artifact
- render-thread-only GL commit once the artifact is ready
- manifest-driven stateless single-pass shader packages
- default float, vec2, color, boolean, enum, and trigger parameters
- small JSON writer for future HTTP/WebSocket payloads
- JSON serialization for cadence telemetry snapshots
- background logging with `log`, `warning`, and `error` levels
- local HTTP control server matching the OpenAPI route surface
- startup config provider for `config/runtime-host.json`
- compact telemetry
- non-GL frame-exchange tests
Intentionally not included yet:
- DeckLink input
- multipass shader rendering
- temporal/history/feedback shader storage
- texture/LUT asset upload
- text-parameter rasterization
- runtime state
- OSC/API control
- preview
- screenshots
- persistence
Those features should be ported only after the cadence spine is stable.
## Build
```powershell
cmake --build --preset build-debug --target RenderCadenceCompositor -- /m:1
```
The executable is:
```text
build\vs2022-x64-debug\Debug\RenderCadenceCompositor.exe
```
## Run
Run from VS Code with:
```text
Debug RenderCadenceCompositor
```
Or from a terminal:
```powershell
build\vs2022-x64-debug\Debug\RenderCadenceCompositor.exe
```
Press Enter to stop.
To test a different compatible shader package:
```powershell
build\vs2022-x64-debug\Debug\RenderCadenceCompositor.exe --shader solid-color
```
Use `--no-shader` to keep the simple motion fallback only.
## Startup Config
On startup the app loads `config/runtime-host.json` through `AppConfigProvider`, then applies explicit CLI overrides.
Currently consumed fields:
- `serverPort`
- `shaderLibrary`
- `oscBindAddress`
- `oscPort`
- `oscSmoothing`
- `inputVideoFormat`
- `inputFrameRate`
- `outputVideoFormat`
- `outputFrameRate`
- `autoReload`
- `maxTemporalHistoryFrames`
- `previewFps`
- `enableExternalKeying`
The loaded config is treated as a read-only startup snapshot. Subsystems that need config should receive this snapshot or a narrowed config struct from app orchestration; they should not reload files independently.
Supported CLI overrides:
- `--shader <shader-id>`
- `--no-shader`
- `--port <port>`
## Expected Telemetry
Startup, shutdown, shader-build, and render-thread event messages are written through the app logger. Telemetry is intentionally separate and remains a compact once-per-second cadence line.
The logger writes to the console, `OutputDebugStringA`, and `logs/render-cadence-compositor.log` by default. Render-thread log calls use the non-blocking path so diagnostics do not become cadence blockers.
## HTTP Control Server
The app starts a local HTTP control server on `http://127.0.0.1:8080` by default, searching nearby ports if that one is busy.
Current endpoints:
- `GET /api/state`: returns an OpenAPI-shaped state scaffold with cadence telemetry under `performance.cadence`
- `GET /docs/openapi.yaml` and `GET /openapi.yaml`: serves the OpenAPI document
- `GET /docs`: serves Swagger UI
- OpenAPI POST routes are present but return `{ "ok": false, "error": "Endpoint is not implemented in RenderCadenceCompositor yet." }`
The HTTP server runs on its own thread. It samples/copies telemetry through callbacks and does not call render work or DeckLink scheduling.
The app prints one line per second:
```text
renderFps=59.9 scheduleFps=59.9 free=7 completed=1 scheduled=4 completedPollMisses=0 scheduleFailures=0 completions=119 late=0 dropped=0 shaderCommitted=1 shaderFailures=0 decklinkBuffered=4 scheduleCallMs=0.0
```
Healthy first-run signs:
- visible DeckLink output is smooth
- `renderFps` is close to the selected cadence
- `scheduleFps` is close to the selected cadence after warmup
- `scheduled` stays near 4
- `decklinkBuffered` stays near 4 when available
- `late` and `dropped` do not increase continuously
- `scheduleFailures` does not increase
- `shaderCommitted` becomes `1` after the background Happy Accident compile completes
- `shaderFailures` remains `0`
`completedPollMisses` means the DeckLink scheduling thread woke up before a completed frame was available. It is not a DeckLink playout underrun by itself. Treat it as healthy polling noise when `scheduled`, `decklinkBuffered`, `late`, `dropped`, and `scheduleFailures` remain stable.
## Runtime Slang Shader Test
On startup the app begins compiling the selected shader package on a background thread owned by the app orchestration layer. The default is `shaders/happy-accident`.
The render thread keeps drawing the simple motion renderer while Slang compiles. It does not choose packages, launch Slang, or track build lifecycle. It only receives a completed shader artifact and attempts the OpenGL shader compile/link at a frame boundary. If either the Slang build or GL commit fails, the app keeps rendering the simple motion fallback.
Current runtime shader support is deliberately limited to stateless single-pass packages:
- one pass only
- no temporal history
- no feedback storage
- no texture/LUT assets yet
- no text parameters yet
- manifest defaults are used for parameters
- `gVideoInput` and `gLayerInput` are bound to a small fallback source texture until DeckLink input is added
Successful handoff signs:
- telemetry shows `shaderCommitted=1`
- output changes from the simple motion pattern to the Happy Accident shader
- render/schedule cadence remains near 60 fps during and after the handoff
- DeckLink buffer remains stable
## Baseline Result
Date: 2026-05-12
User-visible result:
- output was smooth
- DeckLink held a 4-frame buffer
Representative telemetry:
```text
renderFps=59.9 scheduleFps=59.9 free=8 completed=0 scheduled=4 completedPollMisses=30 scheduleFailures=0 completions=720 late=0 dropped=0 decklinkBuffered=4 scheduleCallMs=1.2
renderFps=59.8 scheduleFps=59.8 free=7 completed=1 scheduled=4 completedPollMisses=36 scheduleFailures=0 completions=1080 late=0 dropped=0 decklinkBuffered=4 scheduleCallMs=4.7
renderFps=59.9 scheduleFps=59.9 free=7 completed=1 scheduled=4 completedPollMisses=86 scheduleFailures=0 completions=1381 late=0 dropped=0 decklinkBuffered=4 scheduleCallMs=2.1
```
Read:
- render cadence and DeckLink schedule cadence both held roughly 60 fps
- app scheduled depth stayed at 4
- actual DeckLink buffered depth stayed at 4
- no late frames, dropped frames, or schedule failures were observed
- completed poll misses were benign because playout remained fully fed
## Tests
```powershell
cmake --build --preset build-debug --target RenderCadenceCompositorFrameExchangeTests -- /m:1
ctest --test-dir build\vs2022-x64-debug -C Debug -R RenderCadenceCompositorFrameExchangeTests --output-on-failure
```
## Relationship To The Probe
`apps/DeckLinkRenderCadenceProbe` proved the timing model in one compact file.
This app keeps the same core behavior but splits it into modules that can grow:
- `frames/`: system-memory handoff
- `platform/`: COM/Win32/hidden GL context support
- `render/`: cadence, simple rendering, PBO readback
- `control/`: local HTTP API edge
- `json/`: compact JSON serialization helpers
- `video/`: DeckLink output wrapper and scheduling thread
- `telemetry/`: cadence telemetry
- `app/`: startup/shutdown orchestration
- `app/AppConfigProvider`: startup config loading and CLI overrides
## Next Porting Steps
Only after this app matches the probe's smooth output:
1. replace `SimpleMotionRenderer` with a render-scene interface
2. port shader package rendering
3. port runtime snapshots/live state
4. add control services
5. add preview/screenshot from system-memory frames
6. add DeckLink input as a CPU latest-frame mailbox