#include "ShaderProgramCompiler.h" #include "GlRenderConstants.h" #include "GlScopedObjects.h" #include "GlShaderSources.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); } } ShaderProgramCompiler::ShaderProgramCompiler(OpenGLRenderer& renderer, RuntimeHost& runtimeHost, ShaderTextureBindings& textureBindings) : mRenderer(renderer), mRuntimeHost(runtimeHost), mTextureBindings(textureBindings) { } bool ShaderProgramCompiler::CompileLayerProgram(const RuntimeRenderState& state, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage) { std::vector> passSources; std::string loadError; if (!mRuntimeHost.BuildLayerPassFragmentShaderSources(state.layerId, passSources, loadError)) { CopyErrorMessage(loadError, errorMessageSize, errorMessage); return false; } 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> passSources; passSources.push_back(std::make_pair(std::string("main"), fragmentShaderSource)); return CompilePreparedLayerProgram(state, passSources, layerProgram, errorMessageSize, errorMessage); } bool ShaderProgramCompiler::CompilePreparedLayerProgram(const RuntimeRenderState& state, const std::vector>& passSources, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage) { GLsizei errorBufferSize = 0; std::string loadError; const char* vertexSource = kFullscreenTriangleVertexShaderSource; layerProgram.layerId = state.layerId; layerProgram.shaderId = state.shaderId; layerProgram.passes.clear(); for (const auto& passSource : passSources) { GLint compileResult = GL_FALSE; GLint linkResult = GL_FALSE; const char* fragmentSource = passSource.second.c_str(); ScopedGlShader newVertexShader(glCreateShader(GL_VERTEX_SHADER)); glShaderSource(newVertexShader.get(), 1, (const GLchar**)&vertexSource, NULL); glCompileShader(newVertexShader.get()); glGetShaderiv(newVertexShader.get(), GL_COMPILE_STATUS, &compileResult); if (compileResult == GL_FALSE) { glGetShaderInfoLog(newVertexShader.get(), errorMessageSize, &errorBufferSize, errorMessage); mRenderer.DestroySingleLayerProgram(layerProgram); return false; } ScopedGlShader newFragmentShader(glCreateShader(GL_FRAGMENT_SHADER)); glShaderSource(newFragmentShader.get(), 1, (const GLchar**)&fragmentSource, NULL); glCompileShader(newFragmentShader.get()); glGetShaderiv(newFragmentShader.get(), GL_COMPILE_STATUS, &compileResult); if (compileResult == GL_FALSE) { glGetShaderInfoLog(newFragmentShader.get(), errorMessageSize, &errorBufferSize, errorMessage); mRenderer.DestroySingleLayerProgram(layerProgram); return false; } ScopedGlProgram newProgram(glCreateProgram()); glAttachShader(newProgram.get(), newVertexShader.get()); glAttachShader(newProgram.get(), newFragmentShader.get()); glLinkProgram(newProgram.get()); glGetProgramiv(newProgram.get(), GL_LINK_STATUS, &linkResult); if (linkResult == GL_FALSE) { glGetProgramInfoLog(newProgram.get(), errorMessageSize, &errorBufferSize, errorMessage); mRenderer.DestroySingleLayerProgram(layerProgram); return false; } std::vector textureBindings; for (const ShaderTextureAsset& textureAsset : state.textureAssets) { LayerProgram::TextureBinding textureBinding; textureBinding.samplerName = textureAsset.id; textureBinding.sourcePath = textureAsset.path; if (!mTextureBindings.LoadTextureAsset(textureAsset, textureBinding.texture, loadError)) { for (LayerProgram::TextureBinding& loadedTexture : textureBindings) { if (loadedTexture.texture != 0) glDeleteTextures(1, &loadedTexture.texture); } CopyErrorMessage(loadError, errorMessageSize, errorMessage); mRenderer.DestroySingleLayerProgram(layerProgram); return false; } textureBindings.push_back(textureBinding); } std::vector textBindings; mTextureBindings.CreateTextBindings(state, textBindings); PassProgram passProgram; passProgram.passId = passSource.first; passProgram.shaderTextureBase = mTextureBindings.ResolveShaderTextureBase(state, mRuntimeHost.GetMaxTemporalHistoryFrames()); passProgram.textureBindings.swap(textureBindings); passProgram.textBindings.swap(textBindings); const GLuint globalParamsIndex = glGetUniformBlockIndex(newProgram.get(), "GlobalParams"); if (globalParamsIndex != GL_INVALID_INDEX) glUniformBlockBinding(newProgram.get(), globalParamsIndex, kGlobalParamsBindingPoint); const unsigned historyCap = mRuntimeHost.GetMaxTemporalHistoryFrames(); glUseProgram(newProgram.get()); mTextureBindings.AssignLayerSamplerUniforms(newProgram.get(), state, passProgram, historyCap); glUseProgram(0); passProgram.program = newProgram.release(); passProgram.vertexShader = newVertexShader.release(); passProgram.fragmentShader = newFragmentShader.release(); layerProgram.passes.push_back(std::move(passProgram)); } return true; } bool ShaderProgramCompiler::CompileDecodeShader(int errorMessageSize, char* errorMessage) { GLsizei errorBufferSize = 0; GLint compileResult = GL_FALSE; GLint linkResult = GL_FALSE; const char* vertexSource = kFullscreenTriangleVertexShaderSource; const char* fragmentSource = kDecodeFragmentShaderSource; ScopedGlShader newVertexShader(glCreateShader(GL_VERTEX_SHADER)); glShaderSource(newVertexShader.get(), 1, (const GLchar**)&vertexSource, NULL); glCompileShader(newVertexShader.get()); glGetShaderiv(newVertexShader.get(), GL_COMPILE_STATUS, &compileResult); if (compileResult == GL_FALSE) { glGetShaderInfoLog(newVertexShader.get(), errorMessageSize, &errorBufferSize, errorMessage); return false; } ScopedGlShader newFragmentShader(glCreateShader(GL_FRAGMENT_SHADER)); glShaderSource(newFragmentShader.get(), 1, (const GLchar**)&fragmentSource, NULL); glCompileShader(newFragmentShader.get()); glGetShaderiv(newFragmentShader.get(), GL_COMPILE_STATUS, &compileResult); if (compileResult == GL_FALSE) { glGetShaderInfoLog(newFragmentShader.get(), errorMessageSize, &errorBufferSize, errorMessage); return false; } ScopedGlProgram newProgram(glCreateProgram()); glAttachShader(newProgram.get(), newVertexShader.get()); glAttachShader(newProgram.get(), newFragmentShader.get()); glLinkProgram(newProgram.get()); glGetProgramiv(newProgram.get(), GL_LINK_STATUS, &linkResult); if (linkResult == GL_FALSE) { glGetProgramInfoLog(newProgram.get(), errorMessageSize, &errorBufferSize, errorMessage); return false; } mRenderer.DestroyDecodeShaderProgram(); mRenderer.SetDecodeShaderProgram(newProgram.release(), newVertexShader.release(), newFragmentShader.release()); return true; } bool ShaderProgramCompiler::CompileOutputPackShader(int errorMessageSize, char* errorMessage) { GLsizei errorBufferSize = 0; GLint compileResult = GL_FALSE; GLint linkResult = GL_FALSE; const char* vertexSource = kFullscreenTriangleVertexShaderSource; const char* fragmentSource = kOutputPackFragmentShaderSource; ScopedGlShader newVertexShader(glCreateShader(GL_VERTEX_SHADER)); glShaderSource(newVertexShader.get(), 1, (const GLchar**)&vertexSource, NULL); glCompileShader(newVertexShader.get()); glGetShaderiv(newVertexShader.get(), GL_COMPILE_STATUS, &compileResult); if (compileResult == GL_FALSE) { glGetShaderInfoLog(newVertexShader.get(), errorMessageSize, &errorBufferSize, errorMessage); return false; } ScopedGlShader newFragmentShader(glCreateShader(GL_FRAGMENT_SHADER)); glShaderSource(newFragmentShader.get(), 1, (const GLchar**)&fragmentSource, NULL); glCompileShader(newFragmentShader.get()); glGetShaderiv(newFragmentShader.get(), GL_COMPILE_STATUS, &compileResult); if (compileResult == GL_FALSE) { glGetShaderInfoLog(newFragmentShader.get(), errorMessageSize, &errorBufferSize, errorMessage); return false; } ScopedGlProgram newProgram(glCreateProgram()); glAttachShader(newProgram.get(), newVertexShader.get()); glAttachShader(newProgram.get(), newFragmentShader.get()); glLinkProgram(newProgram.get()); glGetProgramiv(newProgram.get(), GL_LINK_STATUS, &linkResult); if (linkResult == GL_FALSE) { glGetProgramInfoLog(newProgram.get(), errorMessageSize, &errorBufferSize, errorMessage); return false; } glUseProgram(newProgram.get()); const GLint outputSamplerLocation = glGetUniformLocation(newProgram.get(), "uOutputRgb"); if (outputSamplerLocation >= 0) glUniform1i(outputSamplerLocation, 0); glUseProgram(0); mRenderer.DestroyOutputPackShaderProgram(); mRenderer.SetOutputPackShaderProgram(newProgram.release(), newVertexShader.release(), newFragmentShader.release()); return true; }