#include "RuntimeShaderRenderer.h" #include #include #include namespace { constexpr GLuint kGlobalParamsBindingPoint = 0; const char* kVertexShaderSource = R"GLSL( #version 430 core out vec2 vTexCoord; void main() { vec2 positions[3] = vec2[3]( vec2(-1.0, -1.0), vec2( 3.0, -1.0), vec2(-1.0, 3.0)); vec2 texCoords[3] = vec2[3]( vec2(0.0, 0.0), vec2(2.0, 0.0), vec2(0.0, 2.0)); gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0); vTexCoord = texCoords[gl_VertexID]; } )GLSL"; struct GlobalParamsStd140 { float time = 0.0f; float pad0 = 0.0f; float inputResolution[2] = {}; float outputResolution[2] = {}; float utcTimeSeconds = 0.0f; float utcOffsetSeconds = 0.0f; float startupRandom = 0.37f; float frameCount = 0.0f; float mixAmount = 1.0f; float bypass = 0.0f; int sourceHistoryLength = 0; int temporalHistoryLength = 0; int feedbackAvailable = 0; float speed = 1.0f; float scale = 1.0f; float raySteps = 77.0f; float intensity = 1.0f; float sourceMix = 0.0f; float pad1[3] = {}; }; } RuntimeShaderRenderer::~RuntimeShaderRenderer() { ShutdownGl(); } bool RuntimeShaderRenderer::CommitFragmentShader(const std::string& fragmentShaderSource, std::string& error) { if (fragmentShaderSource.empty()) { error = "Cannot commit an empty fragment shader."; return false; } if (!EnsureStaticGlResources(error)) return false; GLuint vertexShader = 0; GLuint fragmentShader = 0; GLuint program = 0; if (!CompileShader(GL_VERTEX_SHADER, kVertexShaderSource, vertexShader, error)) return false; if (!CompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource.c_str(), fragmentShader, error)) { glDeleteShader(vertexShader); return false; } program = glCreateProgram(); glAttachShader(program, vertexShader); glAttachShader(program, fragmentShader); glLinkProgram(program); GLint linkResult = GL_FALSE; glGetProgramiv(program, GL_LINK_STATUS, &linkResult); if (linkResult == GL_FALSE) { std::array log = {}; GLsizei length = 0; glGetProgramInfoLog(program, static_cast(log.size()), &length, log.data()); error = std::string(log.data(), static_cast(length)); glDeleteProgram(program); glDeleteShader(vertexShader); glDeleteShader(fragmentShader); return false; } const GLuint globalParamsIndex = glGetUniformBlockIndex(program, "GlobalParams"); if (globalParamsIndex != GL_INVALID_INDEX) glUniformBlockBinding(program, globalParamsIndex, kGlobalParamsBindingPoint); glUseProgram(program); const GLint videoInputLocation = glGetUniformLocation(program, "gVideoInput"); if (videoInputLocation >= 0) glUniform1i(videoInputLocation, 0); const GLint layerInputLocation = glGetUniformLocation(program, "gLayerInput"); if (layerInputLocation >= 0) glUniform1i(layerInputLocation, 0); glUseProgram(0); DestroyProgram(); mProgram = program; mVertexShader = vertexShader; mFragmentShader = fragmentShader; return true; } void RuntimeShaderRenderer::RenderFrame(uint64_t frameIndex, unsigned width, unsigned height) { if (mProgram == 0) return; GlobalParamsStd140 params; params.time = static_cast(frameIndex) / 60.0f; params.inputResolution[0] = static_cast(width); params.inputResolution[1] = static_cast(height); params.outputResolution[0] = static_cast(width); params.outputResolution[1] = static_cast(height); params.frameCount = static_cast(frameIndex); glViewport(0, 0, static_cast(width), static_cast(height)); glDisable(GL_SCISSOR_TEST); glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); glBindBuffer(GL_UNIFORM_BUFFER, mGlobalParamsBuffer); glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(params), ¶ms); glBindBufferBase(GL_UNIFORM_BUFFER, kGlobalParamsBindingPoint, mGlobalParamsBuffer); glBindBuffer(GL_UNIFORM_BUFFER, 0); glBindVertexArray(mVertexArray); glUseProgram(mProgram); glDrawArrays(GL_TRIANGLES, 0, 3); glUseProgram(0); glBindVertexArray(0); } void RuntimeShaderRenderer::ShutdownGl() { DestroyProgram(); DestroyStaticGlResources(); } bool RuntimeShaderRenderer::EnsureStaticGlResources(std::string& error) { if (mVertexArray == 0) glGenVertexArrays(1, &mVertexArray); if (mGlobalParamsBuffer == 0) { glGenBuffers(1, &mGlobalParamsBuffer); glBindBuffer(GL_UNIFORM_BUFFER, mGlobalParamsBuffer); glBufferData(GL_UNIFORM_BUFFER, static_cast(sizeof(GlobalParamsStd140)), nullptr, GL_DYNAMIC_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); } if (mVertexArray == 0 || mGlobalParamsBuffer == 0) { error = "Failed to create runtime shader GL resources."; return false; } return true; } bool RuntimeShaderRenderer::CompileShader(GLenum shaderType, const char* source, GLuint& shader, std::string& error) const { shader = glCreateShader(shaderType); glShaderSource(shader, 1, &source, nullptr); glCompileShader(shader); GLint compileResult = GL_FALSE; glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult); if (compileResult != GL_FALSE) return true; std::array log = {}; GLsizei length = 0; glGetShaderInfoLog(shader, static_cast(log.size()), &length, log.data()); error = std::string(log.data(), static_cast(length)); glDeleteShader(shader); shader = 0; return false; } void RuntimeShaderRenderer::DestroyProgram() { if (mProgram != 0) glDeleteProgram(mProgram); if (mVertexShader != 0) glDeleteShader(mVertexShader); if (mFragmentShader != 0) glDeleteShader(mFragmentShader); mProgram = 0; mVertexShader = 0; mFragmentShader = 0; } void RuntimeShaderRenderer::DestroyStaticGlResources() { if (mGlobalParamsBuffer != 0) glDeleteBuffers(1, &mGlobalParamsBuffer); if (mVertexArray != 0) glDeleteVertexArrays(1, &mVertexArray); mGlobalParamsBuffer = 0; mVertexArray = 0; }