removed hard coded shader start up
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m27s
CI / Windows Release Package (push) Has been skipped

This commit is contained in:
Aiden
2026-05-30 20:57:01 +10:00
parent 0f3db3ba1b
commit 067c606092
13 changed files with 62 additions and 20 deletions

View File

@@ -24,7 +24,7 @@
},
"previewEnabled": true,
"previewFps": 59.94,
"runtimeShaderId": "happy-accident",
"runtimeShaderId": "",
"serverPort": 8080,
"shaderLibrary": "shaders"
}

View File

@@ -70,8 +70,8 @@
},
"runtimeShaderId": {
"type": "string",
"default": "happy-accident",
"description": "Startup shader package id used when no saved runtime layer stack is restored."
"default": "",
"description": "Optional startup shader package id used only when no saved runtime layer stack is restored. Leave empty to keep the simple fallback renderer until layers are added or restored."
}
},
"required": [

View File

@@ -65,7 +65,7 @@ Do not move DeckLink, NDI, file I/O, shader compilation, or control handling int
Before cutting a long-lived fork, fix or decide these items:
- Remove hardcoded `happy-accident` assumptions in `src/app/AppConfig.h` and `src/runtime/shader/RuntimeSlangShaderCompiler.cpp`.
- Keep `runtimeShaderId` empty in checked-in config unless this repo intentionally wants a default startup shader again.
- Align remaining runtime third-party discovery with CMake. Font atlas generation now checks `MSDF_ATLAS_GEN_ROOT`, `THIRD_PARTY_ROOT`, `3rdParty`, and `video-io-3rdParty`; shader compiler lookup still needs the same treatment for Slang.
- Make `config/runtime-host.json` portable. Current checked-in defaults include a local NDI source name and DeckLink output.
- Decide whether the fork keeps the Slang shader package contract. If not, retire or clearly isolate `shaders/SHADER_CONTRACT.md`, shader package UI, and shader manifest tests.

View File

@@ -640,6 +640,8 @@ components:
type: number
runtimeShaderId:
type: string
description: Optional startup shader id used only when no saved runtime layer stack is restored.
default: ""
additionalProperties: false
HostVideoInputConfig:
type: object

View File

@@ -62,7 +62,7 @@ Included now:
- bounded FIFO system-memory frame exchange
- bounded completed-frame output preroll reserve before DeckLink playback, with DeckLink scheduled depth still targeted at four
- conservative DeckLink schedule-lead telemetry and recovery
- background Slang compile of `shaders/happy-accident`
- optional background Slang compile of configured or restored runtime shader layers
- app-owned display/render layer model for shader build readiness
- app-owned submission of a completed shader artifact
- render-thread-owned render-content interface with runtime shader content as the default implementation
@@ -364,7 +364,7 @@ Healthy first-run signs:
## Runtime Slang Shader Stack
On startup the app first tries to restore `runtime/runtime_state.json`. Valid saved layers are rebuilt in saved order, including shader id, bypass state, and parameter values. Missing shader packages are skipped, invalid saved parameter values fall back to manifest defaults, and if the runtime-state file is missing or unusable the app falls back to the configured shader package. The default configured shader is `shaders/happy-accident`.
On startup the app first tries to restore `runtime/runtime_state.json`. Valid saved layers are rebuilt in saved order, including shader id, bypass state, and parameter values. Missing shader packages are skipped, invalid saved parameter values fall back to manifest defaults, and if the runtime-state file is missing or unusable the app falls back to the optional configured startup shader. The checked-in config leaves `runtimeShaderId` empty so a fresh host keeps the simple fallback renderer until layers are added or a saved stack exists.
The render thread keeps drawing the simple motion renderer while Slang compiles. It does not choose packages, launch Slang, or track build lifecycle. Once a completed shader artifact is published, the render-thread-owned runtime scene queues changed layers to a shared-context GL prepare worker. That worker compiles/links runtime shader programs off the cadence thread. The render thread only swaps in an already-prepared GL program at a frame boundary. If either the Slang build or GL preparation fails, the app keeps rendering the current renderer or simple motion fallback.
@@ -405,7 +405,7 @@ When a layer becomes render-ready, the app publishes the ready render-layer snap
Successful handoff signs:
- telemetry shows `shaderCommitted=1`
- output changes from the simple motion pattern to the Happy Accident shader
- output changes from the simple motion pattern to the restored or configured runtime shader stack
- render/schedule cadence remains near 60 fps during and after the handoff
- DeckLink buffer remains stable

View File

@@ -40,7 +40,7 @@ AppConfig DefaultAppConfig()
config.warmupTimeout = std::chrono::seconds(3);
config.prerollTimeout = std::chrono::seconds(3);
config.prerollPoll = std::chrono::milliseconds(2);
config.runtimeShaderId = "happy-accident";
config.runtimeShaderId.clear();
return config;
}
}

View File

@@ -54,7 +54,7 @@ struct AppConfig
std::chrono::milliseconds warmupTimeout = std::chrono::seconds(3);
std::chrono::milliseconds prerollTimeout = std::chrono::seconds(3);
std::chrono::milliseconds prerollPoll = std::chrono::milliseconds(2);
std::string runtimeShaderId = "happy-accident";
std::string runtimeShaderId;
};
AppConfig DefaultAppConfig();

View File

@@ -36,8 +36,15 @@ void RuntimeLayerController::InitializeLayerModel(std::string& runtimeShaderId)
if (InitializeLayerModelFromRuntimeState())
return;
if (!runtimeShaderId.empty())
Log("runtime-state", "Falling back to configured runtime shader '" + runtimeShaderId + "'.");
if (runtimeShaderId.empty())
{
Log("runtime-state", "No saved runtime layer stack or startup shader configured; using fallback renderer.");
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
mRuntimeLayerModel.Clear();
return;
}
Log("runtime-state", "Falling back to configured runtime shader '" + runtimeShaderId + "'.");
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
std::string error;

View File

@@ -17,7 +17,7 @@ std::filesystem::path FindRepoRoot()
std::filesystem::path current = std::filesystem::current_path();
for (;;)
{
if (std::filesystem::exists(current / "shaders" / "happy-accident" / "shader.slang") &&
if (std::filesystem::exists(current / "shaders") &&
std::filesystem::exists(current / "runtime" / "templates" / "shader_wrapper.slang.in"))
{
return current;
@@ -49,13 +49,11 @@ RuntimeSlangShaderCompiler::~RuntimeSlangShaderCompiler()
Stop();
}
void RuntimeSlangShaderCompiler::StartHappyAccidentBuild()
{
StartShaderBuild("happy-accident");
}
void RuntimeSlangShaderCompiler::StartShaderBuild(const std::string& shaderId)
{
if (shaderId.empty())
return;
if (mRunning.load(std::memory_order_acquire))
return;

View File

@@ -23,7 +23,6 @@ public:
RuntimeSlangShaderCompiler& operator=(const RuntimeSlangShaderCompiler&) = delete;
~RuntimeSlangShaderCompiler();
void StartHappyAccidentBuild();
void StartShaderBuild(const std::string& shaderId);
void Stop();
bool TryConsume(RuntimeSlangShaderBuild& build);

View File

@@ -165,7 +165,7 @@ void TestKnownPostEndpointReturnsActionError()
HttpControlServer::HttpRequest request;
request.method = "POST";
request.path = "/api/layers/add";
request.body = "{\"shaderId\":\"happy-accident\"}";
request.body = "{\"shaderId\":\"solid\"}";
RenderCadenceHttpRouteCallbacks callbacks;
const HttpControlServer::HttpResponse response = RouteRenderCadenceHttpRequest(request, server, callbacks);

View File

@@ -180,6 +180,22 @@ void TestRejectsUnsupportedStartupShader()
std::filesystem::remove_all(root);
}
void TestEmptyStartupShaderKeepsModelEmpty()
{
std::filesystem::path root;
RenderCadenceCompositor::SupportedShaderCatalog catalog = MakeCatalog(root);
RenderCadenceCompositor::RuntimeLayerModel model;
std::string error = "unexpected";
Expect(model.InitializeSingleLayer(catalog, "", error), "empty startup shader is accepted");
Expect(error.empty(), "empty startup shader reports no error");
Expect(model.FirstLayerId().empty(), "empty startup shader creates no startup layer");
Expect(model.Snapshot().displayLayers.empty(), "empty startup shader leaves display model empty");
Expect(model.PendingLayerBuilds().empty(), "empty startup shader queues no build");
std::filesystem::remove_all(root);
}
void TestBuildFailureStaysDisplaySide()
{
std::filesystem::path root;
@@ -576,6 +592,7 @@ int main()
{
TestSingleLayerLifecycle();
TestRejectsUnsupportedStartupShader();
TestEmptyStartupShaderKeepsModelEmpty();
TestBuildFailureStaysDisplaySide();
TestAddAndRemoveLayers();
TestSnapshotCompileMessageSummarizesLayerStack();

View File

@@ -50,6 +50,19 @@ function TextField({ config, label, path, setConfig }) {
);
}
function OptionalTextField({ config, label, path, placeholder, setConfig }) {
return (
<Field label={label}>
<input
type="text"
value={readPath(config, path) ?? ""}
placeholder={placeholder}
onChange={(event) => setConfig((current) => writePath(current, path, event.target.value))}
/>
</Field>
);
}
function InputDeviceField({
config,
manualOpen,
@@ -397,7 +410,13 @@ export function ConfigEditor({ onClose }) {
<h4>Runtime</h4>
<div className="config-fields config-fields--wide">
<TextField config={draft} label="Shader library" path="shaderLibrary" setConfig={setDraft} />
<TextField config={draft} label="Startup shader" path="runtimeShaderId" setConfig={setDraft} />
<OptionalTextField
config={draft}
label="Startup shader"
path="runtimeShaderId"
placeholder="restore saved stack"
setConfig={setDraft}
/>
<NumberField config={draft} label="Server port" min={1} path="serverPort" setConfig={setDraft} />
<NumberField config={draft} label="Temporal cap" min={0} path="maxTemporalHistoryFrames" setConfig={setDraft} />
<ToggleField config={draft} label="Auto reload" path="autoReload" setConfig={setDraft} />