Shader ownership change
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m52s
CI / Windows Release Package (push) Successful in 2m59s

This commit is contained in:
Aiden
2026-05-12 02:15:03 +10:00
parent 4ea829af85
commit c0d7e84495
12 changed files with 370 additions and 36 deletions

View File

@@ -38,7 +38,8 @@ Included now:
- latest-N system-memory frame exchange
- rendered-frame warmup
- background Slang compile of `shaders/happy-accident`
- render-thread-only GL commit once compiled shader source is ready
- app-owned submission of a completed shader artifact
- render-thread-only GL commit once the artifact is ready
- compact telemetry
- non-GL frame-exchange tests
@@ -106,9 +107,9 @@ Healthy first-run signs:
## Runtime Slang Shader Test
On startup the app begins compiling `shaders/happy-accident` on a background thread.
On startup the app begins compiling `shaders/happy-accident` on a background thread owned by the app orchestration layer.
The render thread keeps drawing the simple motion renderer while Slang compiles. It only attempts the OpenGL shader compile/link once a complete GLSL fragment shader is ready. If either the Slang build or GL commit fails, the app keeps rendering the simple motion fallback.
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.
Successful handoff signs:

View File

@@ -1,11 +1,13 @@
#pragma once
#include "AppConfig.h"
#include "../runtime/RuntimeSlangShaderCompiler.h"
#include "../telemetry/TelemetryPrinter.h"
#include "../video/DeckLinkOutput.h"
#include "../video/DeckLinkOutputThread.h"
#include <chrono>
#include <iostream>
#include <string>
#include <thread>
#include <type_traits>
@@ -78,6 +80,7 @@ public:
Stop();
return false;
}
StartRuntimeShaderBuild();
if (!mFrameExchange.WaitForCompletedDepth(mConfig.warmupCompletedFrames, mConfig.warmupTimeout))
{
@@ -116,6 +119,7 @@ public:
mTelemetry.Stop();
mOutputThread.Stop();
mOutput.Stop();
StopRuntimeShaderBuild();
mRenderThread.Stop();
mOutput.ReleaseResources();
mStarted = false;
@@ -137,12 +141,47 @@ private:
return false;
}
void StartRuntimeShaderBuild()
{
mShaderCompiler.StartHappyAccidentBuild();
mShaderBridgeStopping = false;
mShaderBridgeThread = std::thread([this]() { ShaderBridgeMain(); });
}
void StopRuntimeShaderBuild()
{
mShaderBridgeStopping = true;
if (mShaderBridgeThread.joinable())
mShaderBridgeThread.join();
mShaderCompiler.Stop();
}
void ShaderBridgeMain()
{
while (!mShaderBridgeStopping)
{
RuntimeSlangShaderBuild build;
if (mShaderCompiler.TryConsume(build))
{
if (build.succeeded)
mRenderThread.SubmitRuntimeShaderArtifact(build.artifact);
else
std::cout << "Runtime Slang build failed: " << build.message << "\n";
return;
}
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
}
RenderThread& mRenderThread;
SystemFrameExchange& mFrameExchange;
AppConfig mConfig;
DeckLinkOutput mOutput;
DeckLinkOutputThread<SystemFrameExchange> mOutputThread;
TelemetryPrinter mTelemetry;
RuntimeSlangShaderCompiler mShaderCompiler;
std::thread mShaderBridgeThread;
std::atomic<bool> mShaderBridgeStopping{ false };
bool mStarted = false;
};
}

View File

@@ -93,7 +93,6 @@ void RenderThread::ThreadMain()
SignalStartupFailure("Render pipeline initialization failed.");
return;
}
mSlangCompiler.StartHappyAccidentBuild();
RenderCadenceClock clock(mConfig.frameDurationMilliseconds);
uint64_t frameIndex = 0;
@@ -156,7 +155,6 @@ void RenderThread::ThreadMain()
readback.Shutdown();
runtimeShaderRenderer.ShutdownGl();
renderer.ShutdownGl();
mSlangCompiler.Stop();
window.ClearCurrent();
mRunning.store(false, std::memory_order_release);
}
@@ -193,23 +191,36 @@ void RenderThread::CountAcquireMiss()
++mMetrics.acquireMisses;
}
void RenderThread::SubmitRuntimeShaderArtifact(const RuntimeShaderArtifact& artifact)
{
if (artifact.fragmentShaderSource.empty())
return;
std::lock_guard<std::mutex> lock(mShaderArtifactMutex);
mPendingShaderArtifact = artifact;
mHasPendingShaderArtifact = true;
}
bool RenderThread::TryTakePendingRuntimeShaderArtifact(RuntimeShaderArtifact& artifact)
{
std::lock_guard<std::mutex> lock(mShaderArtifactMutex);
if (!mHasPendingShaderArtifact)
return false;
artifact = std::move(mPendingShaderArtifact);
mPendingShaderArtifact = RuntimeShaderArtifact();
mHasPendingShaderArtifact = false;
return true;
}
void RenderThread::TryCommitReadyRuntimeShader(RuntimeShaderRenderer& runtimeShaderRenderer)
{
RuntimeSlangShaderBuild build;
if (!mSlangCompiler.TryConsume(build))
RuntimeShaderArtifact artifact;
if (!TryTakePendingRuntimeShaderArtifact(artifact))
return;
if (!build.succeeded)
{
std::cout << "Runtime Slang build failed: " << build.message << "\n";
OutputDebugStringA(("Runtime Slang build failed: " + build.message + "\n").c_str());
std::lock_guard<std::mutex> lock(mMetricsMutex);
++mMetrics.shaderBuildFailures;
return;
}
std::string commitError;
if (!runtimeShaderRenderer.CommitFragmentShader(build.fragmentShaderSource, commitError))
if (!runtimeShaderRenderer.CommitFragmentShader(artifact.fragmentShaderSource, commitError))
{
std::cout << "Runtime shader GL commit failed: " << commitError << "\n";
OutputDebugStringA(("Runtime shader GL commit failed: " + commitError + "\n").c_str());
@@ -218,8 +229,8 @@ void RenderThread::TryCommitReadyRuntimeShader(RuntimeShaderRenderer& runtimeSha
return;
}
std::cout << "Runtime shader committed: " << build.shaderId << ". " << build.message << "\n";
OutputDebugStringA(("Runtime shader committed: " + build.shaderId + ". " + build.message + "\n").c_str());
std::cout << "Runtime shader committed: " << artifact.shaderId << ". " << artifact.message << "\n";
OutputDebugStringA(("Runtime shader committed: " + artifact.shaderId + ". " + artifact.message + "\n").c_str());
std::lock_guard<std::mutex> lock(mMetricsMutex);
++mMetrics.shaderBuildsCommitted;
}

View File

@@ -1,7 +1,7 @@
#pragma once
#include "RenderCadenceClock.h"
#include "../runtime/RuntimeSlangShaderCompiler.h"
#include "../runtime/RuntimeShaderArtifact.h"
#include "RuntimeShaderRenderer.h"
#include <atomic>
@@ -44,6 +44,7 @@ public:
bool Start(std::string& error);
void Stop();
void SubmitRuntimeShaderArtifact(const RuntimeShaderArtifact& artifact);
Metrics GetMetrics() const;
bool IsRunning() const { return mRunning.load(std::memory_order_acquire); }
@@ -56,10 +57,10 @@ private:
void CountCompleted();
void CountAcquireMiss();
void TryCommitReadyRuntimeShader(RuntimeShaderRenderer& runtimeShaderRenderer);
bool TryTakePendingRuntimeShaderArtifact(RuntimeShaderArtifact& artifact);
SystemFrameExchange& mFrameExchange;
Config mConfig;
RuntimeSlangShaderCompiler mSlangCompiler;
std::thread mThread;
std::atomic<bool> mStopping{ false };
std::atomic<bool> mRunning{ false };
@@ -71,4 +72,8 @@ private:
mutable std::mutex mMetricsMutex;
Metrics mMetrics;
std::mutex mShaderArtifactMutex;
bool mHasPendingShaderArtifact = false;
RuntimeShaderArtifact mPendingShaderArtifact;
};

View File

@@ -0,0 +1,10 @@
#pragma once
#include <string>
struct RuntimeShaderArtifact
{
std::string shaderId;
std::string fragmentShaderSource;
std::string message;
};

View File

@@ -89,7 +89,7 @@ bool RuntimeSlangShaderCompiler::TryConsume(RuntimeSlangShaderBuild& build)
RuntimeSlangShaderBuild RuntimeSlangShaderCompiler::BuildHappyAccident() const
{
RuntimeSlangShaderBuild build;
build.shaderId = "happy-accident";
build.artifact.shaderId = "happy-accident";
try
{
@@ -126,7 +126,7 @@ RuntimeSlangShaderBuild RuntimeSlangShaderCompiler::BuildHappyAccident() const
std::string error;
const auto start = std::chrono::steady_clock::now();
if (!compiler.BuildPassFragmentShaderSource(shaderPackage, pass, build.fragmentShaderSource, error))
if (!compiler.BuildPassFragmentShaderSource(shaderPackage, pass, build.artifact.fragmentShaderSource, error))
{
build.succeeded = false;
build.message = error.empty() ? "Happy Accident Slang compile failed." : error;
@@ -136,7 +136,8 @@ RuntimeSlangShaderBuild RuntimeSlangShaderCompiler::BuildHappyAccident() const
const auto end = std::chrono::steady_clock::now();
const double milliseconds = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(end - start).count();
build.succeeded = true;
build.message = "Happy Accident Slang compile completed in " + std::to_string(milliseconds) + " ms.";
build.artifact.message = "Happy Accident Slang compile completed in " + std::to_string(milliseconds) + " ms.";
build.message = build.artifact.message;
return build;
}
catch (const std::exception& exception)

View File

@@ -1,5 +1,7 @@
#pragma once
#include "RuntimeShaderArtifact.h"
#include <atomic>
#include <mutex>
#include <string>
@@ -9,8 +11,7 @@ struct RuntimeSlangShaderBuild
{
bool available = false;
bool succeeded = false;
std::string shaderId;
std::string fragmentShaderSource;
RuntimeShaderArtifact artifact;
std::string message;
};

View File

@@ -1,8 +1,5 @@
#pragma once
#include "../video/DeckLinkOutput.h"
#include "../video/DeckLinkOutputThread.h"
#include <chrono>
#include <cstddef>
#include <cstdint>
@@ -34,10 +31,10 @@ struct CadenceTelemetrySnapshot
class CadenceTelemetry
{
public:
template <typename SystemFrameExchange, typename OutputThread>
template <typename SystemFrameExchange, typename Output, typename OutputThread>
CadenceTelemetrySnapshot Sample(
const SystemFrameExchange& exchange,
const DeckLinkOutput& output,
const Output& output,
const OutputThread& outputThread)
{
const auto now = Clock::now();
@@ -46,7 +43,7 @@ public:
: 0.0;
const auto exchangeMetrics = exchange.Metrics();
const DeckLinkOutputMetrics outputMetrics = output.Metrics();
const auto outputMetrics = output.Metrics();
const auto threadMetrics = outputThread.Metrics();
CadenceTelemetrySnapshot snapshot;
@@ -80,10 +77,10 @@ public:
return snapshot;
}
template <typename SystemFrameExchange, typename OutputThread, typename RenderThread>
template <typename SystemFrameExchange, typename Output, typename OutputThread, typename RenderThread>
CadenceTelemetrySnapshot Sample(
const SystemFrameExchange& exchange,
const DeckLinkOutput& output,
const Output& output,
const OutputThread& outputThread,
const RenderThread& renderThread)
{

View File

@@ -31,8 +31,8 @@ public:
Stop();
}
template <typename SystemFrameExchange, typename OutputThread, typename RenderThread>
void Start(const SystemFrameExchange& exchange, const DeckLinkOutput& output, const OutputThread& outputThread, const RenderThread& renderThread)
template <typename SystemFrameExchange, typename Output, typename OutputThread, typename RenderThread>
void Start(const SystemFrameExchange& exchange, const Output& output, const OutputThread& outputThread, const RenderThread& renderThread)
{
if (mRunning)
return;