restructure
This commit is contained in:
@@ -0,0 +1,323 @@
|
||||
#include "RuntimeShaderRenderer.h"
|
||||
|
||||
#include "RuntimeShaderParams.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr GLuint kGlobalParamsBindingPoint = 0;
|
||||
constexpr GLuint kSourceTextureUnit = 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";
|
||||
|
||||
}
|
||||
|
||||
RuntimeShaderRenderer::~RuntimeShaderRenderer()
|
||||
{
|
||||
ShutdownGl();
|
||||
}
|
||||
|
||||
bool RuntimeShaderRenderer::CommitFragmentShader(const std::string& fragmentShaderSource, std::string& error)
|
||||
{
|
||||
RuntimeShaderArtifact artifact;
|
||||
artifact.shaderId = "runtime-fragment";
|
||||
artifact.displayName = "Runtime Fragment";
|
||||
artifact.fragmentShaderSource = fragmentShaderSource;
|
||||
return CommitShaderArtifact(artifact, error);
|
||||
}
|
||||
|
||||
bool RuntimeShaderRenderer::CommitShaderArtifact(const RuntimeShaderArtifact& artifact, std::string& error)
|
||||
{
|
||||
if (artifact.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 (!BuildProgram(artifact.fragmentShaderSource, program, vertexShader, fragmentShader, error))
|
||||
return false;
|
||||
|
||||
DestroyProgram();
|
||||
mProgram = program;
|
||||
mVertexShader = vertexShader;
|
||||
mFragmentShader = fragmentShader;
|
||||
mArtifact = artifact;
|
||||
AssignSamplerUniforms(mProgram);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuntimeShaderRenderer::CommitPreparedProgram(RuntimePreparedShaderProgram& preparedProgram, std::string& error)
|
||||
{
|
||||
if (!preparedProgram.succeeded || preparedProgram.program == 0)
|
||||
{
|
||||
error = preparedProgram.error.empty() ? "Prepared runtime shader program is not valid." : preparedProgram.error;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EnsureStaticGlResources(error))
|
||||
return false;
|
||||
|
||||
DestroyProgram();
|
||||
mProgram = preparedProgram.program;
|
||||
mVertexShader = preparedProgram.vertexShader;
|
||||
mFragmentShader = preparedProgram.fragmentShader;
|
||||
mArtifact = preparedProgram.artifact;
|
||||
preparedProgram.program = 0;
|
||||
preparedProgram.vertexShader = 0;
|
||||
preparedProgram.fragmentShader = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuntimeShaderRenderer::BuildPreparedProgram(
|
||||
const std::string& layerId,
|
||||
const std::string& sourceFingerprint,
|
||||
const RuntimeShaderArtifact& artifact,
|
||||
RuntimePreparedShaderProgram& preparedProgram)
|
||||
{
|
||||
preparedProgram = RuntimePreparedShaderProgram();
|
||||
preparedProgram.layerId = layerId;
|
||||
preparedProgram.shaderId = artifact.shaderId;
|
||||
preparedProgram.sourceFingerprint = sourceFingerprint;
|
||||
preparedProgram.artifact = artifact;
|
||||
|
||||
if (artifact.fragmentShaderSource.empty())
|
||||
{
|
||||
preparedProgram.error = "Cannot prepare an empty fragment shader.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!BuildProgram(
|
||||
artifact.fragmentShaderSource,
|
||||
preparedProgram.program,
|
||||
preparedProgram.vertexShader,
|
||||
preparedProgram.fragmentShader,
|
||||
preparedProgram.error))
|
||||
{
|
||||
preparedProgram.ReleaseGl();
|
||||
return false;
|
||||
}
|
||||
|
||||
preparedProgram.succeeded = true;
|
||||
AssignSamplerUniforms(preparedProgram.program);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RuntimeShaderRenderer::RenderFrame(uint64_t frameIndex, unsigned width, unsigned height)
|
||||
{
|
||||
if (mProgram == 0)
|
||||
return;
|
||||
|
||||
glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
UpdateGlobalParams(frameIndex, width, height);
|
||||
BindRuntimeTextures();
|
||||
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, 1024, nullptr, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
if (mFallbackSourceTexture == 0)
|
||||
{
|
||||
const unsigned char pixels[] = {
|
||||
0, 0, 0, 255,
|
||||
96, 64, 32, 255,
|
||||
64, 96, 160, 255,
|
||||
255, 255, 255, 255
|
||||
};
|
||||
glGenTextures(1, &mFallbackSourceTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, mFallbackSourceTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 2, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
if (mVertexArray == 0 || mGlobalParamsBuffer == 0 || mFallbackSourceTexture == 0)
|
||||
{
|
||||
error = "Failed to create runtime shader GL resources.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuntimeShaderRenderer::BuildProgram(const std::string& fragmentShaderSource, GLuint& program, GLuint& vertexShader, GLuint& fragmentShader, std::string& error)
|
||||
{
|
||||
program = 0;
|
||||
vertexShader = 0;
|
||||
fragmentShader = 0;
|
||||
|
||||
if (!CompileShader(GL_VERTEX_SHADER, kVertexShaderSource, vertexShader, error))
|
||||
return false;
|
||||
if (!CompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource.c_str(), fragmentShader, error))
|
||||
{
|
||||
glDeleteShader(vertexShader);
|
||||
vertexShader = 0;
|
||||
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<char, 4096> log = {};
|
||||
GLsizei length = 0;
|
||||
glGetProgramInfoLog(program, static_cast<GLsizei>(log.size()), &length, log.data());
|
||||
error = std::string(log.data(), static_cast<std::size_t>(length));
|
||||
glDeleteProgram(program);
|
||||
glDeleteShader(vertexShader);
|
||||
glDeleteShader(fragmentShader);
|
||||
program = 0;
|
||||
vertexShader = 0;
|
||||
fragmentShader = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
const GLuint globalParamsIndex = glGetUniformBlockIndex(program, "GlobalParams");
|
||||
if (globalParamsIndex != GL_INVALID_INDEX)
|
||||
glUniformBlockBinding(program, globalParamsIndex, kGlobalParamsBindingPoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RuntimeShaderRenderer::AssignSamplerUniforms(GLuint program)
|
||||
{
|
||||
glUseProgram(program);
|
||||
const GLint videoInputLocation = glGetUniformLocation(program, "gVideoInput");
|
||||
if (videoInputLocation >= 0)
|
||||
glUniform1i(videoInputLocation, static_cast<GLint>(kSourceTextureUnit));
|
||||
const GLint videoInputArrayLocation = glGetUniformLocation(program, "gVideoInput_0");
|
||||
if (videoInputArrayLocation >= 0)
|
||||
glUniform1i(videoInputArrayLocation, static_cast<GLint>(kSourceTextureUnit));
|
||||
const GLint layerInputLocation = glGetUniformLocation(program, "gLayerInput");
|
||||
if (layerInputLocation >= 0)
|
||||
glUniform1i(layerInputLocation, static_cast<GLint>(kSourceTextureUnit));
|
||||
const GLint layerInputArrayLocation = glGetUniformLocation(program, "gLayerInput_0");
|
||||
if (layerInputArrayLocation >= 0)
|
||||
glUniform1i(layerInputArrayLocation, static_cast<GLint>(kSourceTextureUnit));
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
void RuntimeShaderRenderer::UpdateGlobalParams(uint64_t frameIndex, unsigned width, unsigned height)
|
||||
{
|
||||
std::vector<unsigned char>& buffer = mGlobalParamsScratch;
|
||||
buffer = BuildRuntimeShaderGlobalParamsStd140(mArtifact, frameIndex, width, height);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, mGlobalParamsBuffer);
|
||||
const GLsizeiptr bufferSize = static_cast<GLsizeiptr>(buffer.size());
|
||||
if (mGlobalParamsBufferSize != bufferSize)
|
||||
{
|
||||
glBufferData(GL_UNIFORM_BUFFER, bufferSize, buffer.data(), GL_DYNAMIC_DRAW);
|
||||
mGlobalParamsBufferSize = bufferSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
glBufferSubData(GL_UNIFORM_BUFFER, 0, bufferSize, buffer.data());
|
||||
}
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, kGlobalParamsBindingPoint, mGlobalParamsBuffer);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
|
||||
void RuntimeShaderRenderer::BindRuntimeTextures()
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + kSourceTextureUnit);
|
||||
glBindTexture(GL_TEXTURE_2D, mFallbackSourceTexture);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
bool RuntimeShaderRenderer::CompileShader(GLenum shaderType, const char* source, GLuint& shader, std::string& error)
|
||||
{
|
||||
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<char, 4096> log = {};
|
||||
GLsizei length = 0;
|
||||
glGetShaderInfoLog(shader, static_cast<GLsizei>(log.size()), &length, log.data());
|
||||
error = std::string(log.data(), static_cast<std::size_t>(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);
|
||||
if (mFallbackSourceTexture != 0)
|
||||
glDeleteTextures(1, &mFallbackSourceTexture);
|
||||
mGlobalParamsBuffer = 0;
|
||||
mGlobalParamsBufferSize = 0;
|
||||
mVertexArray = 0;
|
||||
mFallbackSourceTexture = 0;
|
||||
}
|
||||
Reference in New Issue
Block a user