annotations
This commit is contained in:
@@ -93,6 +93,9 @@ std::vector<RenderPassDescriptor> OpenGLRenderPass::BuildLayerPassDescriptors(
|
|||||||
const std::vector<RuntimeRenderState>& layerStates,
|
const std::vector<RuntimeRenderState>& layerStates,
|
||||||
std::vector<LayerProgram>& layerPrograms) const
|
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;
|
std::vector<RenderPassDescriptor> passes;
|
||||||
const std::size_t passCount = layerStates.size() < layerPrograms.size() ? layerStates.size() : layerPrograms.size();
|
const std::size_t passCount = layerStates.size() < layerPrograms.size() ? layerStates.size() : layerPrograms.size();
|
||||||
std::size_t descriptorCount = 0;
|
std::size_t descriptorCount = 0;
|
||||||
@@ -108,6 +111,9 @@ std::vector<RenderPassDescriptor> OpenGLRenderPass::BuildLayerPassDescriptors(
|
|||||||
LayerProgram& layerProgram = layerPrograms[index];
|
LayerProgram& layerProgram = layerPrograms[index];
|
||||||
if (layerProgram.passes.empty())
|
if (layerProgram.passes.empty())
|
||||||
continue;
|
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 std::size_t remaining = layerStates.size() - index;
|
||||||
const bool writeToMain = (remaining % 2) == 1;
|
const bool writeToMain = (remaining % 2) == 1;
|
||||||
const GLuint layerOutputTexture = writeToMain ? mRenderer.CompositeTexture() : mRenderer.LayerTempTexture();
|
const GLuint layerOutputTexture = writeToMain ? mRenderer.CompositeTexture() : mRenderer.LayerTempTexture();
|
||||||
@@ -132,6 +138,8 @@ std::vector<RenderPassDescriptor> OpenGLRenderPass::BuildLayerPassDescriptors(
|
|||||||
GLuint passSourceFramebuffer = previousPassFramebuffer;
|
GLuint passSourceFramebuffer = previousPassFramebuffer;
|
||||||
if (!passProgram.inputNames.empty())
|
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();
|
const std::string& inputName = passProgram.inputNames.front();
|
||||||
if (inputName == "layerInput")
|
if (inputName == "layerInput")
|
||||||
{
|
{
|
||||||
@@ -159,6 +167,8 @@ std::vector<RenderPassDescriptor> OpenGLRenderPass::BuildLayerPassDescriptors(
|
|||||||
RenderPassOutputTarget outputTarget = layerOutputTarget;
|
RenderPassOutputTarget outputTarget = layerOutputTarget;
|
||||||
if (!writesLayerOutput)
|
if (!writesLayerOutput)
|
||||||
{
|
{
|
||||||
|
// Temporary targets are reserved when the shader stack is
|
||||||
|
// committed, avoiding texture allocation during playback.
|
||||||
if (temporaryTargetIndex < mRenderer.TemporaryRenderTargetCount())
|
if (temporaryTargetIndex < mRenderer.TemporaryRenderTargetCount())
|
||||||
{
|
{
|
||||||
const RenderTarget& temporaryTarget = mRenderer.TemporaryRenderTarget(temporaryTargetIndex);
|
const RenderTarget& temporaryTarget = mRenderer.TemporaryRenderTarget(temporaryTargetIndex);
|
||||||
@@ -186,6 +196,8 @@ std::vector<RenderPassDescriptor> OpenGLRenderPass::BuildLayerPassDescriptors(
|
|||||||
pass.capturePreLayerHistory = passIndex == 0 && state.temporalHistorySource == TemporalHistorySource::PreLayerInput;
|
pass.capturePreLayerHistory = passIndex == 0 && state.temporalHistorySource == TemporalHistorySource::PreLayerInput;
|
||||||
passes.push_back(pass);
|
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[outputName] = std::make_pair(passDestinationTexture, passDestinationFramebuffer);
|
||||||
namedOutputs[passProgram.passId] = std::make_pair(passDestinationTexture, passDestinationFramebuffer);
|
namedOutputs[passProgram.passId] = std::make_pair(passDestinationTexture, passDestinationFramebuffer);
|
||||||
previousPassTexture = passDestinationTexture;
|
previousPassTexture = passDestinationTexture;
|
||||||
@@ -253,6 +265,8 @@ void OpenGLRenderPass::RenderShaderProgram(
|
|||||||
mTextureBindings.BindRuntimeTexturePlan(texturePlan);
|
mTextureBindings.BindRuntimeTexturePlan(texturePlan);
|
||||||
glBindVertexArray(mRenderer.FullscreenVertexArray());
|
glBindVertexArray(mRenderer.FullscreenVertexArray());
|
||||||
glUseProgram(passProgram.program);
|
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));
|
updateGlobalParams(state, mRenderer.TemporalHistory().SourceAvailableCount(), mRenderer.TemporalHistory().AvailableCountForLayer(state.layerId));
|
||||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||||
glUseProgram(0);
|
glUseProgram(0);
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ void CopyErrorMessage(const std::string& message, int errorMessageSize, char* er
|
|||||||
|
|
||||||
std::size_t RequiredTemporaryRenderTargets(const std::vector<OpenGLRenderer::LayerProgram>& layerPrograms)
|
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;
|
std::size_t requiredTargets = 0;
|
||||||
for (const OpenGLRenderer::LayerProgram& layerProgram : layerPrograms)
|
for (const OpenGLRenderer::LayerProgram& layerProgram : layerPrograms)
|
||||||
{
|
{
|
||||||
@@ -51,6 +53,8 @@ bool OpenGLShaderPrograms::CompileLayerPrograms(unsigned inputFrameWidth, unsign
|
|||||||
return false;
|
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;
|
std::vector<LayerProgram> newPrograms;
|
||||||
newPrograms.reserve(layerStates.size());
|
newPrograms.reserve(layerStates.size());
|
||||||
|
|
||||||
@@ -106,6 +110,8 @@ bool OpenGLShaderPrograms::CommitPreparedLayerPrograms(const PreparedShaderBuild
|
|||||||
return false;
|
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;
|
std::vector<LayerProgram> newPrograms;
|
||||||
newPrograms.reserve(preparedBuild.layers.size());
|
newPrograms.reserve(preparedBuild.layers.size());
|
||||||
|
|
||||||
|
|||||||
@@ -1324,6 +1324,8 @@ bool RuntimeHost::BuildLayerPassFragmentShaderSources(const std::string& layerId
|
|||||||
}
|
}
|
||||||
|
|
||||||
ShaderCompiler compiler(mRepoRoot, mWrapperPath, mGeneratedGlslPath, mPatchedGlslPath, mConfig.maxTemporalHistoryFrames);
|
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.clear();
|
||||||
passSources.reserve(shaderPackage.passes.size());
|
passSources.reserve(shaderPackage.passes.size());
|
||||||
for (const ShaderPassDefinition& pass : shaderPackage.passes)
|
for (const ShaderPassDefinition& pass : shaderPackage.passes)
|
||||||
|
|||||||
@@ -258,6 +258,8 @@ bool ParsePassDefinitions(const JsonValue& manifestJson, ShaderPackage& shaderPa
|
|||||||
|
|
||||||
if (!passesValue)
|
if (!passesValue)
|
||||||
{
|
{
|
||||||
|
// Existing shader packages are treated as a single implicit pass, so
|
||||||
|
// multipass support does not require manifest churn.
|
||||||
ShaderPassDefinition pass;
|
ShaderPassDefinition pass;
|
||||||
pass.id = "main";
|
pass.id = "main";
|
||||||
pass.entryPoint = shaderPackage.entryPoint;
|
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))
|
if (!std::filesystem::exists(pass.sourcePath))
|
||||||
{
|
{
|
||||||
error = "Shader pass source not found for package " + shaderPackage.id + ": " + pass.sourcePath.string();
|
error = "Shader pass source not found for package " + shaderPackage.id + ": " + pass.sourcePath.string();
|
||||||
|
|||||||
Reference in New Issue
Block a user