Added config editor in front end
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m46s
CI / Windows Release Package (push) Has been skipped

This commit is contained in:
Aiden
2026-05-30 19:33:40 +10:00
parent f0f8b080ca
commit 8ffc011ca0
26 changed files with 1201 additions and 55 deletions

View File

@@ -21,7 +21,7 @@ The app is a native C++ OpenGL compositor with:
Primary source areas:
- `src/app`: startup/shutdown orchestration, config loading, runtime layer controller
- `src/render`: cadence clock, input texture upload, simple renderer, readback, and runtime GL support
- `src/render`: cadence clock, input texture upload, render-content boundary, readback, and runtime GL support
- `src/render/thread`: render thread lifecycle, cadence loop, metrics, and runtime shader commit mailbox
- `src/render/runtime`: render-thread-owned runtime shader scene, renderer, text texture upload cache, and shared-context shader prepare worker
- `src/frames`: system-memory frame exchange
@@ -99,6 +99,8 @@ The mutation path snapshots the current layer model and hands serialized state t
OSC-driven changes are intentionally not part of this autosave path yet.
The host configuration editor is separate from runtime layer persistence. The UI reads active and saved startup config through `/api/config`, saves `config/runtime-host.json` through `/api/config/save`, and requests a native host restart through `/api/app/restart`. Render cadence, video input/output selection, resolution, frame rate, output pixel format, HTTP port, and preview settings are still startup-owned; they are not hot-swapped inside the cadence path.
## Shader Reload
`POST /api/reload` and the control UI reload button:
@@ -129,11 +131,11 @@ The render path consumes published render-layer snapshots. It does not:
- handle HTTP or OSC
- call DeckLink discovery/setup APIs
When a runtime shader build completes, the app publishes a render-layer artifact. The render thread-owned runtime scene diffs the snapshot and queues changed pass programs to the shared-context prepare worker. The render thread swaps in an already-prepared render plan at a frame boundary.
When a runtime shader build completes, the app publishes a render-layer artifact. The render thread forwards pending layer snapshots to the active render-content adapter. The default `RuntimeShaderRenderContent` owns the runtime scene, diffs the snapshot, and queues changed pass programs to the shared-context prepare worker. The render thread swaps in an already-prepared render plan at a frame boundary through that adapter.
## Video And Preview
Video input and output are optional edges. `input.backend` and `output.backend` select the concrete backend through the app-side backend factory. DeckLink is the current concrete backend, and `none` disables that edge. `input` and `output` also carry the device selector plus resolution/frame-rate settings. Configured video modes are represented in `src/video/core` and translated to DeckLink display modes only inside `src/video/decklink`.
Video input and output are optional edges. `input.backend` and `output.backend` select the concrete backend through the app-side backend factory. DeckLink and NDI are the current concrete backends, and `none` disables an edge. `input` and `output` also carry the device selector plus resolution/frame-rate settings. Configured video modes are represented in `src/video/core` and translated to backend-specific modes only inside the concrete edge.
The input edge writes CPU frames into `InputFrameMailbox`. The current DeckLink backend captures BGRA8 directly where possible, or raw UYVY8 for render-thread GPU decode. The input edge does not call GL, render, preview, screenshot, shader, or output scheduling code.

View File

@@ -6,7 +6,7 @@ This note captures the fork-readiness review for using this repository as a base
The repository is clean enough for an internal fork, but it needs a small hygiene pass before it becomes a comfortable long-lived base repo.
The important architecture is already in place: render cadence, video input/output, frame exchange, readback, preview, control, and shader build work are mostly separated by role. The main replacement point is the render-thread draw path in `src/render/thread/RenderThread.cpp`, where cadence, input upload, readback, and frame publication wrap the actual GPU rendering call.
The important architecture is already in place: render cadence, video input/output, frame exchange, readback, preview, control, and shader build work are mostly separated by role. The main replacement point is the render-content adapter in `src/render/RuntimeShaderRenderContent.*`, where the current shader-package renderer decides what to draw into the framebuffer handed to it by `RenderThread`.
For a new repo, keep the cadence and frame handoff machinery, then replace or narrow the runtime shader rendering layer.
@@ -34,22 +34,22 @@ These are most likely to change when the fork renders something other than shade
- `runtime/templates/shader_wrapper.slang.in`: only needed for the current Slang package pipeline.
- Shader-specific UI affordances in `ui`, if the new renderer has a different control model.
The cleanest first fork step is to preserve `RenderThread`'s cadence/readback shell and introduce a narrow render-content interface behind the draw call. Then the new repo can swap the implementation without touching video I/O scheduling.
The first fork step is now in place: `RenderThread` preserves the cadence/readback shell and calls a narrow render-content interface behind the draw call. A new repo can swap that implementation without touching video I/O scheduling.
## Current Swap Point
The current draw decision happens inside the readback queue call in `src/render/thread/RenderThread.cpp`:
The render cadence loop now calls `IRenderContent` inside the readback queue call in `src/render/thread/RenderThread.cpp`:
```cpp
if (runtimeRenderScene.HasLayers())
runtimeRenderScene.RenderFrame(index, mConfig.width, mConfig.height, videoInputTexture);
else if (videoInputTexture != 0)
renderer.RenderTexture(videoInputTexture);
else
renderer.RenderFrame(index);
renderContent.RenderFrame(RenderContentFrame{
index,
mConfig.width,
mConfig.height,
videoInputTexture
});
```
That is the practical boundary for a fork:
The default implementation is `RuntimeShaderRenderContent`, which owns the existing runtime shader scene plus simple fallback renderer. That is the practical boundary for a fork:
- keep the tick clock, input upload, readback queueing, and `SystemFrameExchange` publication around it
- replace what draws into the current GL framebuffer
@@ -84,9 +84,9 @@ The generated Visual Studio `RUN_TESTS` target did not build missing test execut
## Recommended Fork Sequence
1. Make the hygiene fixes above in this repo or immediately after the fork.
2. Add a small render-content abstraction behind the `RenderThread` draw call.
3. Port the existing runtime shader renderer behind that abstraction as the baseline implementation.
4. Add the new renderer beside it.
2. Keep `IRenderContent` as the boundary behind the `RenderThread` draw call.
3. Keep `RuntimeShaderRenderContent` as the baseline implementation until the fork's renderer exists.
4. Add the new renderer beside it or replace the default adapter.
5. Verify that video output still consumes completed frames and never requests rendering directly.
6. Only then remove shader package pieces that the new repo no longer needs.

View File

@@ -16,6 +16,8 @@ servers:
tags:
- name: State
description: Runtime state and status.
- name: Config
description: Startup host configuration and restart control.
- name: Static
description: Bundled control UI and static assets served by the local host.
- name: Docs
@@ -179,6 +181,52 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/RuntimeState"
/api/config:
get:
tags: [Config]
summary: Get active and saved host config
operationId: getHostConfig
responses:
"200":
description: Active startup config and the config currently saved on disk.
content:
application/json:
schema:
$ref: "#/components/schemas/HostConfigResponse"
/api/config/save:
post:
tags: [Config]
summary: Save host config
description: Saves `runtime-host.json`. Startup-owned services use the new values after app restart.
operationId: saveHostConfig
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/HostConfig"
responses:
"200":
$ref: "#/components/responses/ActionOk"
"400":
$ref: "#/components/responses/ActionError"
/api/app/restart:
post:
tags: [Config]
summary: Restart the native host
operationId: restartHost
requestBody:
required: false
content:
application/json:
schema:
type: object
additionalProperties: false
responses:
"200":
$ref: "#/components/responses/ActionOk"
"400":
$ref: "#/components/responses/ActionError"
/ws:
get:
tags: [State]
@@ -511,6 +559,90 @@ components:
type: string
example: live-show-look
additionalProperties: false
HostConfigResponse:
type: object
properties:
ok:
type: boolean
path:
type: string
active:
$ref: "#/components/schemas/HostConfig"
disk:
$ref: "#/components/schemas/HostConfig"
diskLoaded:
type: boolean
restartRequired:
type: boolean
error:
type: string
HostConfig:
type: object
properties:
$schema:
type: string
shaderLibrary:
type: string
serverPort:
type: number
oscBindAddress:
type: string
oscPort:
type: number
oscSmoothing:
type: number
input:
$ref: "#/components/schemas/HostVideoInputConfig"
output:
$ref: "#/components/schemas/HostVideoOutputConfig"
autoReload:
type: boolean
maxTemporalHistoryFrames:
type: number
previewEnabled:
type: boolean
previewFps:
type: number
runtimeShaderId:
type: string
additionalProperties: false
HostVideoInputConfig:
type: object
properties:
backend:
type: string
enum: [decklink, ndi, none]
device:
type: string
resolution:
type: string
frameRate:
type: string
additionalProperties: false
HostVideoOutputConfig:
type: object
properties:
backend:
type: string
enum: [decklink, ndi, none]
device:
type: string
resolution:
type: string
frameRate:
type: string
pixelFormat:
type: string
enum: [auto, bgra8, uyvy8]
keying:
type: object
properties:
external:
type: boolean
alphaRequired:
type: boolean
additionalProperties: false
additionalProperties: false
RuntimeState:
type: object
properties: