New rules based order
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m53s
CI / Windows Release Package (push) Successful in 3m18s

This commit is contained in:
Aiden
2026-05-12 02:35:15 +10:00
parent c0d7e84495
commit 511b67c9bc
18 changed files with 772 additions and 150 deletions

View File

@@ -9,7 +9,6 @@
#include "SimpleMotionRenderer.h"
#include <algorithm>
#include <iostream>
#include <thread>
#include <windows.h>
@@ -220,16 +219,14 @@ void RenderThread::TryCommitReadyRuntimeShader(RuntimeShaderRenderer& runtimeSha
return;
std::string commitError;
if (!runtimeShaderRenderer.CommitFragmentShader(artifact.fragmentShaderSource, commitError))
if (!runtimeShaderRenderer.CommitShaderArtifact(artifact, commitError))
{
std::cout << "Runtime shader GL commit failed: " << commitError << "\n";
OutputDebugStringA(("Runtime shader GL commit failed: " + commitError + "\n").c_str());
std::lock_guard<std::mutex> lock(mMetricsMutex);
++mMetrics.shaderBuildFailures;
return;
}
std::cout << "Runtime shader committed: " << artifact.shaderId << ". " << artifact.message << "\n";
OutputDebugStringA(("Runtime shader committed: " + artifact.shaderId + ". " + artifact.message + "\n").c_str());
std::lock_guard<std::mutex> lock(mMetricsMutex);
++mMetrics.shaderBuildsCommitted;

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

@@ -1,5 +1,7 @@
#include "RuntimeShaderRenderer.h"
#include "RuntimeShaderParams.h"
#include <array>
#include <cstring>
#include <string>
@@ -7,6 +9,7 @@
namespace
{
constexpr GLuint kGlobalParamsBindingPoint = 0;
constexpr GLuint kSourceTextureUnit = 0;
const char* kVertexShaderSource = R"GLSL(
#version 430 core
@@ -26,28 +29,6 @@ void main()
}
)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()
@@ -57,7 +38,16 @@ RuntimeShaderRenderer::~RuntimeShaderRenderer()
bool RuntimeShaderRenderer::CommitFragmentShader(const std::string& fragmentShaderSource, std::string& error)
{
if (fragmentShaderSource.empty())
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;
@@ -69,50 +59,15 @@ bool RuntimeShaderRenderer::CommitFragmentShader(const std::string& fragmentShad
GLuint vertexShader = 0;
GLuint fragmentShader = 0;
GLuint program = 0;
if (!CompileShader(GL_VERTEX_SHADER, kVertexShaderSource, vertexShader, error))
if (!BuildProgram(artifact.fragmentShaderSource, program, vertexShader, fragmentShader, 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<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);
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;
mArtifact = artifact;
AssignSamplerUniforms(mProgram);
return true;
}
@@ -121,22 +76,12 @@ void RuntimeShaderRenderer::RenderFrame(uint64_t frameIndex, unsigned width, uns
if (mProgram == 0)
return;
GlobalParamsStd140 params;
params.time = static_cast<float>(frameIndex) / 60.0f;
params.inputResolution[0] = static_cast<float>(width);
params.inputResolution[1] = static_cast<float>(height);
params.outputResolution[0] = static_cast<float>(width);
params.outputResolution[1] = static_cast<float>(height);
params.frameCount = static_cast<float>(frameIndex);
glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
glDisable(GL_SCISSOR_TEST);
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
glBindBuffer(GL_UNIFORM_BUFFER, mGlobalParamsBuffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(params), &params);
glBindBufferBase(GL_UNIFORM_BUFFER, kGlobalParamsBindingPoint, mGlobalParamsBuffer);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
UpdateGlobalParams(frameIndex, width, height);
BindRuntimeTextures();
glBindVertexArray(mVertexArray);
glUseProgram(mProgram);
glDrawArrays(GL_TRIANGLES, 0, 3);
@@ -158,11 +103,28 @@ bool RuntimeShaderRenderer::EnsureStaticGlResources(std::string& error)
{
glGenBuffers(1, &mGlobalParamsBuffer);
glBindBuffer(GL_UNIFORM_BUFFER, mGlobalParamsBuffer);
glBufferData(GL_UNIFORM_BUFFER, static_cast<GLsizeiptr>(sizeof(GlobalParamsStd140)), nullptr, GL_DYNAMIC_DRAW);
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)
if (mVertexArray == 0 || mGlobalParamsBuffer == 0 || mFallbackSourceTexture == 0)
{
error = "Failed to create runtime shader GL resources.";
return false;
@@ -170,6 +132,93 @@ bool RuntimeShaderRenderer::EnsureStaticGlResources(std::string& error)
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) const
{
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) const
{
shader = glCreateShader(shaderType);
@@ -209,6 +258,10 @@ void RuntimeShaderRenderer::DestroyStaticGlResources()
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

@@ -1,9 +1,11 @@
#pragma once
#include "GLExtensions.h"
#include "../runtime/RuntimeShaderArtifact.h"
#include <cstdint>
#include <string>
#include <vector>
class RuntimeShaderRenderer
{
@@ -14,6 +16,7 @@ public:
~RuntimeShaderRenderer();
bool CommitFragmentShader(const std::string& fragmentShaderSource, std::string& error);
bool CommitShaderArtifact(const RuntimeShaderArtifact& artifact, std::string& error);
bool HasProgram() const { return mProgram != 0; }
void RenderFrame(uint64_t frameIndex, unsigned width, unsigned height);
void ShutdownGl();
@@ -21,12 +24,20 @@ public:
private:
bool EnsureStaticGlResources(std::string& error);
bool CompileShader(GLenum shaderType, const char* source, GLuint& shader, std::string& error) const;
bool BuildProgram(const std::string& fragmentShaderSource, GLuint& program, GLuint& vertexShader, GLuint& fragmentShader, std::string& error);
void AssignSamplerUniforms(GLuint program) const;
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;
};