#include "OpenGLShaderPrograms.h" #include #include #include namespace { void CopyErrorMessage(const std::string& message, int errorMessageSize, char* errorMessage) { if (!errorMessage || errorMessageSize <= 0) return; strncpy_s(errorMessage, errorMessageSize, message.c_str(), _TRUNCATE); } std::size_t RequiredTemporaryRenderTargets(const std::vector& 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) { const std::size_t internalPasses = layerProgram.passes.size() > 0 ? layerProgram.passes.size() - 1 : 0; if (internalPasses > requiredTargets) requiredTargets = internalPasses; } return requiredTargets; } } OpenGLShaderPrograms::OpenGLShaderPrograms(OpenGLRenderer& renderer, RuntimeHost& runtimeHost, RuntimeSnapshotProvider& runtimeSnapshotProvider) : mRenderer(renderer), mRuntimeHost(runtimeHost), mRuntimeSnapshotProvider(runtimeSnapshotProvider), mGlobalParamsBuffer(renderer), mCompiler(renderer, runtimeHost, mTextureBindings) { } bool OpenGLShaderPrograms::CompileLayerPrograms(unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage) { const RuntimeRenderStateSnapshot renderSnapshot = mRuntimeSnapshotProvider.GetRenderStateSnapshot(inputFrameWidth, inputFrameHeight); const std::vector& layerStates = renderSnapshot.states; std::string temporalError; const unsigned historyCap = mRuntimeHost.GetMaxTemporalHistoryFrames(); if (!mRenderer.TemporalHistory().ValidateTextureUnitBudget(layerStates, historyCap, temporalError)) { CopyErrorMessage(temporalError, errorMessageSize, errorMessage); return false; } if (!mRenderer.TemporalHistory().EnsureResources(layerStates, historyCap, inputFrameWidth, inputFrameHeight, temporalError)) { CopyErrorMessage(temporalError, errorMessageSize, errorMessage); return false; } if (mRenderer.ResourcesInitialized() && !mRenderer.FeedbackBuffers().EnsureResources(layerStates, inputFrameWidth, inputFrameHeight, temporalError)) { CopyErrorMessage(temporalError, errorMessageSize, errorMessage); return false; } // Initial startup still compiles synchronously; auto-reload uses the build // queue so Slang/file work stays off the playback path. std::vector newPrograms; newPrograms.reserve(layerStates.size()); for (const RuntimeRenderState& state : layerStates) { LayerProgram layerProgram; if (!mCompiler.CompileLayerProgram(state, layerProgram, errorMessageSize, errorMessage)) { for (LayerProgram& program : newPrograms) DestroySingleLayerProgram(program); return false; } newPrograms.push_back(layerProgram); } std::string targetError; if (!mRenderer.ReserveTemporaryRenderTargets(RequiredTemporaryRenderTargets(newPrograms), inputFrameWidth, inputFrameHeight, targetError)) { for (LayerProgram& program : newPrograms) DestroySingleLayerProgram(program); CopyErrorMessage(targetError, errorMessageSize, errorMessage); return false; } DestroyLayerPrograms(); mRenderer.ReplaceLayerPrograms(newPrograms); mCommittedLayerStates = renderSnapshot.states; mRuntimeHost.SetCompileStatus(true, "Shader layers compiled successfully."); mRuntimeHost.ClearReloadRequest(); return true; } bool OpenGLShaderPrograms::CommitPreparedLayerPrograms(const PreparedShaderBuild& preparedBuild, unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage) { if (!preparedBuild.succeeded) { CopyErrorMessage(preparedBuild.message, errorMessageSize, errorMessage); return false; } std::string temporalError; const unsigned historyCap = mRuntimeHost.GetMaxTemporalHistoryFrames(); if (!mRenderer.TemporalHistory().ValidateTextureUnitBudget(preparedBuild.renderSnapshot.states, historyCap, temporalError)) { CopyErrorMessage(temporalError, errorMessageSize, errorMessage); return false; } if (!mRenderer.TemporalHistory().EnsureResources(preparedBuild.renderSnapshot.states, historyCap, inputFrameWidth, inputFrameHeight, temporalError)) { CopyErrorMessage(temporalError, errorMessageSize, errorMessage); return false; } if (mRenderer.ResourcesInitialized() && !mRenderer.FeedbackBuffers().EnsureResources(preparedBuild.renderSnapshot.states, inputFrameWidth, inputFrameHeight, temporalError)) { CopyErrorMessage(temporalError, errorMessageSize, errorMessage); 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 newPrograms; newPrograms.reserve(preparedBuild.layers.size()); for (const PreparedLayerShader& preparedLayer : preparedBuild.layers) { LayerProgram layerProgram; if (!mCompiler.CompilePreparedLayerProgram(preparedLayer.state, preparedLayer.passes, layerProgram, errorMessageSize, errorMessage)) { for (LayerProgram& program : newPrograms) DestroySingleLayerProgram(program); return false; } newPrograms.push_back(layerProgram); } std::string targetError; if (!mRenderer.ReserveTemporaryRenderTargets(RequiredTemporaryRenderTargets(newPrograms), inputFrameWidth, inputFrameHeight, targetError)) { for (LayerProgram& program : newPrograms) DestroySingleLayerProgram(program); CopyErrorMessage(targetError, errorMessageSize, errorMessage); return false; } DestroyLayerPrograms(); mRenderer.ReplaceLayerPrograms(newPrograms); mCommittedLayerStates = preparedBuild.renderSnapshot.states; mRuntimeHost.SetCompileStatus(true, "Shader layers compiled successfully."); mRuntimeHost.ClearReloadRequest(); return true; } bool OpenGLShaderPrograms::CompileDecodeShader(int errorMessageSize, char* errorMessage) { return mCompiler.CompileDecodeShader(errorMessageSize, errorMessage); } bool OpenGLShaderPrograms::CompileOutputPackShader(int errorMessageSize, char* errorMessage) { return mCompiler.CompileOutputPackShader(errorMessageSize, errorMessage); } void OpenGLShaderPrograms::DestroySingleLayerProgram(LayerProgram& layerProgram) { mRenderer.DestroySingleLayerProgram(layerProgram); } void OpenGLShaderPrograms::DestroyLayerPrograms() { mRenderer.DestroyLayerPrograms(); } void OpenGLShaderPrograms::DestroyDecodeShaderProgram() { mRenderer.DestroyDecodeShaderProgram(); } void OpenGLShaderPrograms::ResetTemporalHistoryState() { mRenderer.TemporalHistory().ResetState(); } void OpenGLShaderPrograms::ResetShaderFeedbackState() { mRenderer.FeedbackBuffers().ResetState(); } bool OpenGLShaderPrograms::UpdateTextBindingTexture(const RuntimeRenderState& state, LayerProgram::TextBinding& textBinding, std::string& error) { return mTextureBindings.UpdateTextBindingTexture(state, textBinding, error); } bool OpenGLShaderPrograms::UpdateGlobalParamsBuffer(const RuntimeRenderState& state, unsigned availableSourceHistoryLength, unsigned availableTemporalHistoryLength, bool feedbackAvailable) { return mGlobalParamsBuffer.Update(state, availableSourceHistoryLength, availableTemporalHistoryLength, feedbackAvailable); }