2 Commits

Author SHA1 Message Date
5ae43513a7 annotations
Some checks failed
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m16s
CI / Windows Release Package (push) Has been cancelled
2026-05-08 17:35:48 +10:00
cc23e73d51 Removed uneeded code 2026-05-08 17:33:57 +10:00
9 changed files with 26 additions and 38 deletions

View File

@@ -93,6 +93,9 @@ std::vector<RenderPassDescriptor> OpenGLRenderPass::BuildLayerPassDescriptors(
const std::vector<RuntimeRenderState>& layerStates,
std::vector<LayerProgram>& layerPrograms) const
{
// Flatten the layer stack into concrete GL passes. A layer may now contain
// several shader passes, but the outer stack still sees one visible output
// per layer.
std::vector<RenderPassDescriptor> passes;
const std::size_t passCount = layerStates.size() < layerPrograms.size() ? layerStates.size() : layerPrograms.size();
std::size_t descriptorCount = 0;
@@ -108,6 +111,9 @@ std::vector<RenderPassDescriptor> OpenGLRenderPass::BuildLayerPassDescriptors(
LayerProgram& layerProgram = layerPrograms[index];
if (layerProgram.passes.empty())
continue;
// Preserve the original two-target layer ping-pong. Intermediate passes
// inside this layer are routed through pooled temporary targets instead.
const std::size_t remaining = layerStates.size() - index;
const bool writeToMain = (remaining % 2) == 1;
const GLuint layerOutputTexture = writeToMain ? mRenderer.CompositeTexture() : mRenderer.LayerTempTexture();
@@ -132,6 +138,8 @@ std::vector<RenderPassDescriptor> OpenGLRenderPass::BuildLayerPassDescriptors(
GLuint passSourceFramebuffer = previousPassFramebuffer;
if (!passProgram.inputNames.empty())
{
// v1 multipass uses the first declared input as gVideoInput.
// Later inputs are parsed for forward compatibility.
const std::string& inputName = passProgram.inputNames.front();
if (inputName == "layerInput")
{
@@ -159,6 +167,8 @@ std::vector<RenderPassDescriptor> OpenGLRenderPass::BuildLayerPassDescriptors(
RenderPassOutputTarget outputTarget = layerOutputTarget;
if (!writesLayerOutput)
{
// Temporary targets are reserved when the shader stack is
// committed, avoiding texture allocation during playback.
if (temporaryTargetIndex < mRenderer.TemporaryRenderTargetCount())
{
const RenderTarget& temporaryTarget = mRenderer.TemporaryRenderTarget(temporaryTargetIndex);
@@ -186,6 +196,8 @@ std::vector<RenderPassDescriptor> OpenGLRenderPass::BuildLayerPassDescriptors(
pass.capturePreLayerHistory = passIndex == 0 && state.temporalHistorySource == TemporalHistorySource::PreLayerInput;
passes.push_back(pass);
// A later pass can reference either the explicit output name or the
// pass id, which keeps small manifests pleasant to write.
namedOutputs[outputName] = std::make_pair(passDestinationTexture, passDestinationFramebuffer);
namedOutputs[passProgram.passId] = std::make_pair(passDestinationTexture, passDestinationFramebuffer);
previousPassTexture = passDestinationTexture;
@@ -253,6 +265,8 @@ void OpenGLRenderPass::RenderShaderProgram(
mTextureBindings.BindRuntimeTexturePlan(texturePlan);
glBindVertexArray(mRenderer.FullscreenVertexArray());
glUseProgram(passProgram.program);
// The UBO is shared by every pass in a layer; texture routing is what
// changes from pass to pass.
updateGlobalParams(state, mRenderer.TemporalHistory().SourceAvailableCount(), mRenderer.TemporalHistory().AvailableCountForLayer(state.layerId));
glDrawArrays(GL_TRIANGLES, 0, 3);
glUseProgram(0);

View File

@@ -16,6 +16,8 @@ void CopyErrorMessage(const std::string& message, int errorMessageSize, char* er
std::size_t RequiredTemporaryRenderTargets(const std::vector<OpenGLRenderer::LayerProgram>& layerPrograms)
{
// Only one layer renders at a time, so the pool needs to cover the widest
// layer, not the sum of every intermediate pass in the stack.
std::size_t requiredTargets = 0;
for (const OpenGLRenderer::LayerProgram& layerProgram : layerPrograms)
{
@@ -51,6 +53,8 @@ bool OpenGLShaderPrograms::CompileLayerPrograms(unsigned inputFrameWidth, unsign
return false;
}
// Initial startup still compiles synchronously; auto-reload uses the build
// queue so Slang/file work stays off the playback path.
std::vector<LayerProgram> newPrograms;
newPrograms.reserve(layerStates.size());
@@ -106,6 +110,8 @@ bool OpenGLShaderPrograms::CommitPreparedLayerPrograms(const PreparedShaderBuild
return false;
}
// The prepared build already contains GLSL text for each pass. This commit
// step performs the short GL work on the render thread.
std::vector<LayerProgram> newPrograms;
newPrograms.reserve(preparedBuild.layers.size());

View File

@@ -40,17 +40,6 @@ bool ShaderProgramCompiler::CompileLayerProgram(const RuntimeRenderState& state,
return CompilePreparedLayerProgram(state, passSources, layerProgram, errorMessageSize, errorMessage);
}
bool ShaderProgramCompiler::CompilePreparedLayerProgram(const RuntimeRenderState& state, const std::string& fragmentShaderSource, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage)
{
std::vector<ShaderPassBuildSource> passSources;
ShaderPassBuildSource passSource;
passSource.passId = "main";
passSource.fragmentShaderSource = fragmentShaderSource;
passSource.outputName = "layerOutput";
passSources.push_back(std::move(passSource));
return CompilePreparedLayerProgram(state, passSources, layerProgram, errorMessageSize, errorMessage);
}
bool ShaderProgramCompiler::CompilePreparedLayerProgram(const RuntimeRenderState& state, const std::vector<ShaderPassBuildSource>& passSources, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage)
{
GLsizei errorBufferSize = 0;

View File

@@ -16,7 +16,6 @@ public:
ShaderProgramCompiler(OpenGLRenderer& renderer, RuntimeHost& runtimeHost, ShaderTextureBindings& textureBindings);
bool CompileLayerProgram(const RuntimeRenderState& state, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage);
bool CompilePreparedLayerProgram(const RuntimeRenderState& state, const std::string& fragmentShaderSource, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage);
bool CompilePreparedLayerProgram(const RuntimeRenderState& state, const std::vector<ShaderPassBuildSource>& passSources, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage);
bool CompileDecodeShader(int errorMessageSize, char* errorMessage);
bool CompileOutputPackShader(int errorMessageSize, char* errorMessage);

View File

@@ -1300,20 +1300,6 @@ bool RuntimeHost::TryAdvanceFrame()
return true;
}
bool RuntimeHost::BuildLayerFragmentShaderSource(const std::string& layerId, std::string& fragmentShaderSource, std::string& error)
{
std::vector<ShaderPassBuildSource> passSources;
if (!BuildLayerPassFragmentShaderSources(layerId, passSources, error))
return false;
if (passSources.empty())
{
error = "Shader layer produced no compiled passes: " + layerId;
return false;
}
fragmentShaderSource = passSources.front().fragmentShaderSource;
return true;
}
bool RuntimeHost::BuildLayerPassFragmentShaderSources(const std::string& layerId, std::vector<ShaderPassBuildSource>& passSources, std::string& error)
{
try
@@ -1338,6 +1324,8 @@ bool RuntimeHost::BuildLayerPassFragmentShaderSources(const std::string& layerId
}
ShaderCompiler compiler(mRepoRoot, mWrapperPath, mGeneratedGlslPath, mPatchedGlslPath, mConfig.maxTemporalHistoryFrames);
// Compile every declared pass while the caller remains backend-neutral.
// The GL layer decides how the resulting pass sources are routed.
passSources.clear();
passSources.reserve(shaderPackage.passes.size());
for (const ShaderPassDefinition& pass : shaderPackage.passes)

View File

@@ -51,7 +51,6 @@ public:
void AdvanceFrame();
bool TryAdvanceFrame();
bool BuildLayerFragmentShaderSource(const std::string& layerId, std::string& fragmentShaderSource, std::string& error);
bool BuildLayerPassFragmentShaderSources(const std::string& layerId, std::vector<ShaderPassBuildSource>& passSources, std::string& error);
std::vector<RuntimeRenderState> GetLayerRenderStates(unsigned outputWidth, unsigned outputHeight) const;
bool TryGetLayerRenderStates(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const;

View File

@@ -143,16 +143,6 @@ ShaderCompiler::ShaderCompiler(
{
}
bool ShaderCompiler::BuildLayerFragmentShaderSource(const ShaderPackage& shaderPackage, std::string& fragmentShaderSource, std::string& error) const
{
if (shaderPackage.passes.empty())
{
error = "Shader package has no render passes: " + shaderPackage.id;
return false;
}
return BuildPassFragmentShaderSource(shaderPackage, shaderPackage.passes.front(), fragmentShaderSource, error);
}
bool ShaderCompiler::BuildPassFragmentShaderSource(const ShaderPackage& shaderPackage, const ShaderPassDefinition& pass, std::string& fragmentShaderSource, std::string& error) const
{
std::string wrapperSource;

View File

@@ -15,7 +15,6 @@ public:
const std::filesystem::path& patchedGlslPath,
unsigned maxTemporalHistoryFrames);
bool BuildLayerFragmentShaderSource(const ShaderPackage& shaderPackage, std::string& fragmentShaderSource, std::string& error) const;
bool BuildPassFragmentShaderSource(const ShaderPackage& shaderPackage, const ShaderPassDefinition& pass, std::string& fragmentShaderSource, std::string& error) const;
private:

View File

@@ -258,6 +258,8 @@ bool ParsePassDefinitions(const JsonValue& manifestJson, ShaderPackage& shaderPa
if (!passesValue)
{
// Existing shader packages are treated as a single implicit pass, so
// multipass support does not require manifest churn.
ShaderPassDefinition pass;
pass.id = "main";
pass.entryPoint = shaderPackage.entryPoint;
@@ -334,6 +336,8 @@ bool ParsePassDefinitions(const JsonValue& manifestJson, ShaderPackage& shaderPa
}
}
// Keep source validation in the registry. Bad pass declarations then
// appear as unavailable shaders instead of failing at render time.
if (!std::filesystem::exists(pass.sourcePath))
{
error = "Shader pass source not found for package " + shaderPackage.id + ": " + pass.sourcePath.string();