restructure

This commit is contained in:
Aiden
2026-05-12 15:47:59 +10:00
parent 6a33bd02ab
commit 02b221f481
21 changed files with 40 additions and 33 deletions

View File

@@ -0,0 +1,173 @@
#include "RuntimeRenderScene.h"
#include "../../platform/HiddenGlWindow.h"
#include <algorithm>
#include <functional>
#include <utility>
RuntimeRenderScene::~RuntimeRenderScene()
{
ShutdownGl();
}
bool RuntimeRenderScene::StartPrepareWorker(std::unique_ptr<HiddenGlWindow> sharedWindow, std::string& error)
{
return mPrepareWorker.Start(std::move(sharedWindow), error);
}
bool RuntimeRenderScene::CommitRenderLayers(const std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel>& layers, std::string& error)
{
ConsumePreparedPrograms();
std::vector<std::string> nextOrder;
std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel> layersToPrepare;
nextOrder.reserve(layers.size());
for (const RenderCadenceCompositor::RuntimeRenderLayerModel& layer : layers)
nextOrder.push_back(layer.id);
for (auto layerIt = mLayers.begin(); layerIt != mLayers.end();)
{
const bool stillPresent = std::find(nextOrder.begin(), nextOrder.end(), layerIt->layerId) != nextOrder.end();
if (stillPresent)
{
++layerIt;
continue;
}
if (layerIt->renderer)
layerIt->renderer->ShutdownGl();
layerIt = mLayers.erase(layerIt);
}
for (const RenderCadenceCompositor::RuntimeRenderLayerModel& layer : layers)
{
if (layer.artifact.fragmentShaderSource.empty())
continue;
const std::string fingerprint = Fingerprint(layer.artifact);
LayerProgram* program = FindLayer(layer.id);
if (!program)
{
LayerProgram next;
next.layerId = layer.id;
next.renderer = std::make_unique<RuntimeShaderRenderer>();
mLayers.push_back(std::move(next));
program = &mLayers.back();
}
if (program->shaderId == layer.shaderId && program->sourceFingerprint == fingerprint && program->renderer && program->renderer->HasProgram())
continue;
if (program->pendingFingerprint == fingerprint)
continue;
program->shaderId = layer.shaderId;
program->pendingFingerprint = fingerprint;
layersToPrepare.push_back(layer);
}
mLayerOrder = std::move(nextOrder);
if (!layersToPrepare.empty())
mPrepareWorker.Submit(layersToPrepare);
error.clear();
return true;
}
bool RuntimeRenderScene::HasLayers()
{
ConsumePreparedPrograms();
for (const std::string& layerId : mLayerOrder)
{
const LayerProgram* layer = FindLayer(layerId);
if (layer && layer->renderer && layer->renderer->HasProgram())
return true;
}
return false;
}
void RuntimeRenderScene::RenderFrame(uint64_t frameIndex, unsigned width, unsigned height)
{
ConsumePreparedPrograms();
for (const std::string& layerId : mLayerOrder)
{
LayerProgram* layer = FindLayer(layerId);
if (!layer || !layer->renderer || !layer->renderer->HasProgram())
continue;
layer->renderer->RenderFrame(frameIndex, width, height);
}
}
void RuntimeRenderScene::ShutdownGl()
{
mPrepareWorker.Stop();
for (LayerProgram& layer : mLayers)
{
if (layer.renderer)
layer.renderer->ShutdownGl();
}
mLayers.clear();
mLayerOrder.clear();
}
void RuntimeRenderScene::ConsumePreparedPrograms()
{
RuntimePreparedShaderProgram preparedProgram;
while (mPrepareWorker.TryConsume(preparedProgram))
{
if (!preparedProgram.succeeded)
{
preparedProgram.ReleaseGl();
continue;
}
LayerProgram* layer = FindLayer(preparedProgram.layerId);
if (!layer || layer->pendingFingerprint != preparedProgram.sourceFingerprint)
{
preparedProgram.ReleaseGl();
continue;
}
std::unique_ptr<RuntimeShaderRenderer> nextRenderer = std::make_unique<RuntimeShaderRenderer>();
std::string error;
if (!nextRenderer->CommitPreparedProgram(preparedProgram, error))
{
preparedProgram.ReleaseGl();
continue;
}
if (layer->renderer)
layer->renderer->ShutdownGl();
layer->renderer = std::move(nextRenderer);
layer->shaderId = preparedProgram.shaderId;
layer->sourceFingerprint = preparedProgram.sourceFingerprint;
layer->pendingFingerprint.clear();
}
}
RuntimeRenderScene::LayerProgram* RuntimeRenderScene::FindLayer(const std::string& layerId)
{
for (LayerProgram& layer : mLayers)
{
if (layer.layerId == layerId)
return &layer;
}
return nullptr;
}
const RuntimeRenderScene::LayerProgram* RuntimeRenderScene::FindLayer(const std::string& layerId) const
{
for (const LayerProgram& layer : mLayers)
{
if (layer.layerId == layerId)
return &layer;
}
return nullptr;
}
std::string RuntimeRenderScene::Fingerprint(const RuntimeShaderArtifact& artifact)
{
const std::hash<std::string> hasher;
return artifact.shaderId + ":" + std::to_string(artifact.fragmentShaderSource.size()) + ":" + std::to_string(hasher(artifact.fragmentShaderSource));
}

View File

@@ -0,0 +1,46 @@
#pragma once
#include "../runtime/RuntimeLayerModel.h"
#include "RuntimeShaderPrepareWorker.h"
#include "RuntimeShaderRenderer.h"
#include <windows.h>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
class RuntimeRenderScene
{
public:
RuntimeRenderScene() = default;
RuntimeRenderScene(const RuntimeRenderScene&) = delete;
RuntimeRenderScene& operator=(const RuntimeRenderScene&) = delete;
~RuntimeRenderScene();
bool StartPrepareWorker(std::unique_ptr<HiddenGlWindow> sharedWindow, std::string& error);
bool CommitRenderLayers(const std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel>& layers, std::string& error);
bool HasLayers();
void RenderFrame(uint64_t frameIndex, unsigned width, unsigned height);
void ShutdownGl();
private:
struct LayerProgram
{
std::string layerId;
std::string shaderId;
std::string sourceFingerprint;
std::string pendingFingerprint;
std::unique_ptr<RuntimeShaderRenderer> renderer;
};
void ConsumePreparedPrograms();
LayerProgram* FindLayer(const std::string& layerId);
const LayerProgram* FindLayer(const std::string& layerId) const;
static std::string Fingerprint(const RuntimeShaderArtifact& artifact);
RuntimeShaderPrepareWorker mPrepareWorker;
std::vector<LayerProgram> mLayers;
std::vector<std::string> mLayerOrder;
};

View File

@@ -0,0 +1,120 @@
#include "RuntimeShaderParams.h"
#include "Std140Buffer.h"
#include <chrono>
namespace
{
ShaderParameterValue DefaultValueForDefinition(const ShaderParameterDefinition& definition)
{
ShaderParameterValue value;
switch (definition.type)
{
case ShaderParameterType::Float:
value.numberValues = definition.defaultNumbers.empty() ? std::vector<double>{ 0.0 } : definition.defaultNumbers;
break;
case ShaderParameterType::Vec2:
value.numberValues = definition.defaultNumbers.size() == 2 ? definition.defaultNumbers : std::vector<double>{ 0.0, 0.0 };
break;
case ShaderParameterType::Color:
value.numberValues = definition.defaultNumbers.size() == 4 ? definition.defaultNumbers : std::vector<double>{ 1.0, 1.0, 1.0, 1.0 };
break;
case ShaderParameterType::Boolean:
value.booleanValue = definition.defaultBoolean;
break;
case ShaderParameterType::Enum:
value.enumValue = definition.defaultEnumValue;
break;
case ShaderParameterType::Text:
value.textValue = definition.defaultTextValue;
break;
case ShaderParameterType::Trigger:
value.numberValues = { 0.0, -1000000.0 };
break;
}
return value;
}
int EnumIndexForDefault(const ShaderParameterDefinition& definition, const ShaderParameterValue& value)
{
for (std::size_t optionIndex = 0; optionIndex < definition.enumOptions.size(); ++optionIndex)
{
if (definition.enumOptions[optionIndex].value == value.enumValue)
return static_cast<int>(optionIndex);
}
return 0;
}
double UtcSecondsOfDay()
{
const auto now = std::chrono::system_clock::now();
const auto seconds = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
const long long secondsPerDay = 24 * 60 * 60;
long long daySeconds = seconds % secondsPerDay;
if (daySeconds < 0)
daySeconds += secondsPerDay;
return static_cast<double>(daySeconds);
}
}
std::vector<unsigned char> BuildRuntimeShaderGlobalParamsStd140(
const RuntimeShaderArtifact& artifact,
uint64_t frameIndex,
unsigned width,
unsigned height)
{
std::vector<unsigned char> buffer;
buffer.reserve(512);
AppendStd140Float(buffer, static_cast<float>(frameIndex) / 60.0f);
AppendStd140Vec2(buffer, static_cast<float>(width), static_cast<float>(height));
AppendStd140Vec2(buffer, static_cast<float>(width), static_cast<float>(height));
AppendStd140Float(buffer, static_cast<float>(UtcSecondsOfDay()));
AppendStd140Float(buffer, 0.0f);
AppendStd140Float(buffer, 0.37f);
AppendStd140Float(buffer, static_cast<float>(frameIndex));
AppendStd140Float(buffer, 1.0f);
AppendStd140Float(buffer, 0.0f);
AppendStd140Int(buffer, 0);
AppendStd140Int(buffer, 0);
AppendStd140Int(buffer, 0);
for (const ShaderParameterDefinition& definition : artifact.parameterDefinitions)
{
const ShaderParameterValue value = DefaultValueForDefinition(definition);
switch (definition.type)
{
case ShaderParameterType::Float:
AppendStd140Float(buffer, value.numberValues.empty() ? 0.0f : static_cast<float>(value.numberValues[0]));
break;
case ShaderParameterType::Vec2:
AppendStd140Vec2(buffer,
value.numberValues.size() > 0 ? static_cast<float>(value.numberValues[0]) : 0.0f,
value.numberValues.size() > 1 ? static_cast<float>(value.numberValues[1]) : 0.0f);
break;
case ShaderParameterType::Color:
AppendStd140Vec4(buffer,
value.numberValues.size() > 0 ? static_cast<float>(value.numberValues[0]) : 1.0f,
value.numberValues.size() > 1 ? static_cast<float>(value.numberValues[1]) : 1.0f,
value.numberValues.size() > 2 ? static_cast<float>(value.numberValues[2]) : 1.0f,
value.numberValues.size() > 3 ? static_cast<float>(value.numberValues[3]) : 1.0f);
break;
case ShaderParameterType::Boolean:
AppendStd140Int(buffer, value.booleanValue ? 1 : 0);
break;
case ShaderParameterType::Enum:
AppendStd140Int(buffer, EnumIndexForDefault(definition, value));
break;
case ShaderParameterType::Text:
break;
case ShaderParameterType::Trigger:
AppendStd140Int(buffer, 0);
AppendStd140Float(buffer, -1000000.0f);
break;
}
}
buffer.resize(AlignStd140(buffer.size(), 16), 0);
return buffer;
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include "../runtime/RuntimeShaderArtifact.h"
#include <cstdint>
#include <vector>
std::vector<unsigned char> BuildRuntimeShaderGlobalParamsStd140(
const RuntimeShaderArtifact& artifact,
uint64_t frameIndex,
unsigned width,
unsigned height);

View File

@@ -0,0 +1,158 @@
#include "RuntimeShaderPrepareWorker.h"
#include "../../platform/HiddenGlWindow.h"
#include "RuntimeShaderRenderer.h"
#include <algorithm>
#include <chrono>
#include <functional>
#include <utility>
RuntimeShaderPrepareWorker::~RuntimeShaderPrepareWorker()
{
Stop();
}
bool RuntimeShaderPrepareWorker::Start(std::unique_ptr<HiddenGlWindow> sharedWindow, std::string& error)
{
if (mThread.joinable())
return true;
if (!sharedWindow || sharedWindow->DeviceContext() == nullptr || sharedWindow->Context() == nullptr)
{
error = "Runtime shader prepare worker needs an existing shared GL context.";
return false;
}
mWindow = std::move(sharedWindow);
mStopping.store(false, std::memory_order_release);
mStarted.store(false, std::memory_order_release);
{
std::lock_guard<std::mutex> lock(mMutex);
mStartupReady = false;
mStartupError.clear();
}
mThread = std::thread([this]() { ThreadMain(); });
std::unique_lock<std::mutex> lock(mMutex);
if (!mStartupCondition.wait_for(lock, std::chrono::seconds(3), [this]() {
return mStartupReady || !mStartupError.empty();
}))
{
error = "Timed out starting runtime shader prepare worker.";
lock.unlock();
Stop();
return false;
}
if (!mStartupError.empty())
{
error = mStartupError;
lock.unlock();
Stop();
return false;
}
return true;
}
void RuntimeShaderPrepareWorker::Stop()
{
mStopping.store(true, std::memory_order_release);
mCondition.notify_all();
if (mThread.joinable())
mThread.join();
std::deque<RuntimePreparedShaderProgram> completed;
{
std::lock_guard<std::mutex> lock(mMutex);
mRequests.clear();
completed.swap(mCompleted);
}
for (RuntimePreparedShaderProgram& program : completed)
program.ReleaseGl();
mWindow.reset();
mStarted.store(false, std::memory_order_release);
}
void RuntimeShaderPrepareWorker::Submit(const std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel>& layers)
{
std::lock_guard<std::mutex> lock(mMutex);
for (const RenderCadenceCompositor::RuntimeRenderLayerModel& layer : layers)
{
if (layer.artifact.fragmentShaderSource.empty())
continue;
PrepareRequest request;
request.layerId = layer.id;
request.shaderId = layer.shaderId;
request.sourceFingerprint = Fingerprint(layer.artifact);
request.artifact = layer.artifact;
auto sameLayer = [&request](const PrepareRequest& existing) {
return existing.layerId == request.layerId;
};
mRequests.erase(std::remove_if(mRequests.begin(), mRequests.end(), sameLayer), mRequests.end());
mRequests.push_back(std::move(request));
}
mCondition.notify_one();
}
bool RuntimeShaderPrepareWorker::TryConsume(RuntimePreparedShaderProgram& preparedProgram)
{
std::lock_guard<std::mutex> lock(mMutex);
if (mCompleted.empty())
return false;
preparedProgram = std::move(mCompleted.front());
mCompleted.pop_front();
return true;
}
void RuntimeShaderPrepareWorker::ThreadMain()
{
if (!mWindow || !mWindow->MakeCurrent())
{
std::lock_guard<std::mutex> lock(mMutex);
mStartupError = "Runtime shader prepare worker could not make shared GL context current.";
mStartupCondition.notify_all();
return;
}
{
std::lock_guard<std::mutex> lock(mMutex);
mStartupReady = true;
}
mStarted.store(true, std::memory_order_release);
mStartupCondition.notify_all();
while (!mStopping.load(std::memory_order_acquire))
{
PrepareRequest request;
{
std::unique_lock<std::mutex> lock(mMutex);
mCondition.wait(lock, [this]() {
return mStopping.load(std::memory_order_acquire) || !mRequests.empty();
});
if (mStopping.load(std::memory_order_acquire))
break;
request = std::move(mRequests.front());
mRequests.pop_front();
}
RuntimePreparedShaderProgram preparedProgram;
RuntimeShaderRenderer::BuildPreparedProgram(
request.layerId,
request.sourceFingerprint,
request.artifact,
preparedProgram);
glFlush();
std::lock_guard<std::mutex> lock(mMutex);
mCompleted.push_back(std::move(preparedProgram));
}
mWindow->ClearCurrent();
}
std::string RuntimeShaderPrepareWorker::Fingerprint(const RuntimeShaderArtifact& artifact)
{
const std::hash<std::string> hasher;
return artifact.shaderId + ":" + std::to_string(artifact.fragmentShaderSource.size()) + ":" + std::to_string(hasher(artifact.fragmentShaderSource));
}

View File

@@ -0,0 +1,56 @@
#pragma once
#include "RuntimeShaderProgram.h"
#include "../../runtime/RuntimeLayerModel.h"
#include <windows.h>
#include <atomic>
#include <condition_variable>
#include <deque>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
class HiddenGlWindow;
class RuntimeShaderPrepareWorker
{
public:
RuntimeShaderPrepareWorker() = default;
RuntimeShaderPrepareWorker(const RuntimeShaderPrepareWorker&) = delete;
RuntimeShaderPrepareWorker& operator=(const RuntimeShaderPrepareWorker&) = delete;
~RuntimeShaderPrepareWorker();
bool Start(std::unique_ptr<HiddenGlWindow> sharedWindow, std::string& error);
void Stop();
void Submit(const std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel>& layers);
bool TryConsume(RuntimePreparedShaderProgram& preparedProgram);
private:
struct PrepareRequest
{
std::string layerId;
std::string shaderId;
std::string sourceFingerprint;
RuntimeShaderArtifact artifact;
};
void ThreadMain();
static std::string Fingerprint(const RuntimeShaderArtifact& artifact);
std::unique_ptr<HiddenGlWindow> mWindow;
std::mutex mMutex;
std::condition_variable mCondition;
std::deque<PrepareRequest> mRequests;
std::deque<RuntimePreparedShaderProgram> mCompleted;
std::condition_variable mStartupCondition;
std::thread mThread;
std::atomic<bool> mStopping{ false };
std::atomic<bool> mStarted{ false };
bool mStartupReady = false;
std::string mStartupError;
};

View File

@@ -0,0 +1,32 @@
#pragma once
#include "GLExtensions.h"
#include "../../runtime/RuntimeShaderArtifact.h"
#include <string>
struct RuntimePreparedShaderProgram
{
std::string layerId;
std::string shaderId;
std::string sourceFingerprint;
RuntimeShaderArtifact artifact;
GLuint program = 0;
GLuint vertexShader = 0;
GLuint fragmentShader = 0;
bool succeeded = false;
std::string error;
void ReleaseGl()
{
if (program != 0)
glDeleteProgram(program);
if (vertexShader != 0)
glDeleteShader(vertexShader);
if (fragmentShader != 0)
glDeleteShader(fragmentShader);
program = 0;
vertexShader = 0;
fragmentShader = 0;
}
};

View File

@@ -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;
}

View File

@@ -0,0 +1,51 @@
#pragma once
#include "GLExtensions.h"
#include "RuntimeShaderProgram.h"
#include "../../runtime/RuntimeShaderArtifact.h"
#include <cstdint>
#include <string>
#include <vector>
class RuntimeShaderRenderer
{
public:
RuntimeShaderRenderer() = default;
RuntimeShaderRenderer(const RuntimeShaderRenderer&) = delete;
RuntimeShaderRenderer& operator=(const RuntimeShaderRenderer&) = delete;
~RuntimeShaderRenderer();
bool CommitFragmentShader(const std::string& fragmentShaderSource, std::string& error);
bool CommitShaderArtifact(const RuntimeShaderArtifact& artifact, std::string& error);
bool CommitPreparedProgram(RuntimePreparedShaderProgram& preparedProgram, std::string& error);
bool HasProgram() const { return mProgram != 0; }
void RenderFrame(uint64_t frameIndex, unsigned width, unsigned height);
void ShutdownGl();
static bool BuildPreparedProgram(
const std::string& layerId,
const std::string& sourceFingerprint,
const RuntimeShaderArtifact& artifact,
RuntimePreparedShaderProgram& preparedProgram);
private:
bool EnsureStaticGlResources(std::string& error);
static bool CompileShader(GLenum shaderType, const char* source, GLuint& shader, std::string& error);
static bool BuildProgram(const std::string& fragmentShaderSource, GLuint& program, GLuint& vertexShader, GLuint& fragmentShader, std::string& error);
static void AssignSamplerUniforms(GLuint program);
void UpdateGlobalParams(uint64_t frameIndex, unsigned width, unsigned height);
void BindRuntimeTextures();
void DestroyProgram();
void DestroyStaticGlResources();
RuntimeShaderArtifact mArtifact;
GLuint mProgram = 0;
GLuint mVertexShader = 0;
GLuint mFragmentShader = 0;
GLuint mVertexArray = 0;
GLuint mGlobalParamsBuffer = 0;
GLsizeiptr mGlobalParamsBufferSize = 0;
GLuint mFallbackSourceTexture = 0;
std::vector<unsigned char> mGlobalParamsScratch;
};