Render udpates

This commit is contained in:
Aiden
2026-05-12 15:26:02 +10:00
parent c5fd8e72b4
commit 334693f28c
13 changed files with 437 additions and 23 deletions

View File

@@ -11,6 +11,7 @@
#include "SimpleMotionRenderer.h"
#include <algorithm>
#include <memory>
#include <thread>
RenderThread::RenderThread(SystemFrameExchange& frameExchange, Config config) :
@@ -83,11 +84,22 @@ void RenderThread::ThreadMain()
RenderCadenceCompositor::TryLog(RenderCadenceCompositor::LogLevel::Log, "render-thread", "Render thread starting.");
HiddenGlWindow window;
std::string error;
if (!window.Create(mConfig.width, mConfig.height, error) || !window.MakeCurrent())
if (!window.Create(mConfig.width, mConfig.height, error))
{
SignalStartupFailure(error.empty() ? "OpenGL context creation failed." : error);
return;
}
std::unique_ptr<HiddenGlWindow> prepareWindow = std::make_unique<HiddenGlWindow>();
if (!prepareWindow->CreateShared(mConfig.width, mConfig.height, window.DeviceContext(), window.Context(), error))
{
SignalStartupFailure(error.empty() ? "Runtime shader prepare shared context creation failed." : error);
return;
}
if (!window.MakeCurrent())
{
SignalStartupFailure("OpenGL context creation failed.");
return;
}
if (!ResolveGLExtensions())
{
SignalStartupFailure("OpenGL extension resolution failed.");
@@ -97,6 +109,11 @@ void RenderThread::ThreadMain()
SimpleMotionRenderer renderer;
RuntimeRenderScene runtimeRenderScene;
Bgra8ReadbackPipeline readback;
if (!runtimeRenderScene.StartPrepareWorker(std::move(prepareWindow), error))
{
SignalStartupFailure(error.empty() ? "Runtime shader prepare worker initialization failed." : error);
return;
}
if (!renderer.InitializeGl(mConfig.width, mConfig.height) || !readback.Initialize(mConfig.width, mConfig.height, mConfig.pboDepth))
{
SignalStartupFailure("Render pipeline initialization failed.");

View File

@@ -1,5 +1,7 @@
#include "RuntimeRenderScene.h"
#include "../platform/HiddenGlWindow.h"
#include <algorithm>
#include <functional>
#include <utility>
@@ -9,9 +11,17 @@ 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);
@@ -48,25 +58,38 @@ bool RuntimeRenderScene::CommitRenderLayers(const std::vector<RenderCadenceCompo
if (program->shaderId == layer.shaderId && program->sourceFingerprint == fingerprint && program->renderer && program->renderer->HasProgram())
continue;
if (program->pendingFingerprint == fingerprint)
continue;
std::unique_ptr<RuntimeShaderRenderer> nextRenderer = std::make_unique<RuntimeShaderRenderer>();
if (!nextRenderer->CommitShaderArtifact(layer.artifact, error))
return false;
if (program->renderer)
program->renderer->ShutdownGl();
program->shaderId = layer.shaderId;
program->sourceFingerprint = fingerprint;
program->renderer = std::move(nextRenderer);
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);
@@ -78,6 +101,7 @@ void RuntimeRenderScene::RenderFrame(uint64_t frameIndex, unsigned width, unsign
void RuntimeRenderScene::ShutdownGl()
{
mPrepareWorker.Stop();
for (LayerProgram& layer : mLayers)
{
if (layer.renderer)
@@ -87,6 +111,41 @@ void RuntimeRenderScene::ShutdownGl()
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)

View File

@@ -1,8 +1,11 @@
#pragma once
#include "../runtime/RuntimeLayerModel.h"
#include "RuntimeShaderPrepareWorker.h"
#include "RuntimeShaderRenderer.h"
#include <windows.h>
#include <cstdint>
#include <memory>
#include <string>
@@ -16,8 +19,9 @@ public:
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() const { return !mLayerOrder.empty(); }
bool HasLayers();
void RenderFrame(uint64_t frameIndex, unsigned width, unsigned height);
void ShutdownGl();
@@ -27,13 +31,16 @@ private:
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,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

@@ -71,6 +71,62 @@ bool RuntimeShaderRenderer::CommitShaderArtifact(const RuntimeShaderArtifact& ar
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)
@@ -175,7 +231,7 @@ bool RuntimeShaderRenderer::BuildProgram(const std::string& fragmentShaderSource
return true;
}
void RuntimeShaderRenderer::AssignSamplerUniforms(GLuint program) const
void RuntimeShaderRenderer::AssignSamplerUniforms(GLuint program)
{
glUseProgram(program);
const GLint videoInputLocation = glGetUniformLocation(program, "gVideoInput");
@@ -219,7 +275,7 @@ void RuntimeShaderRenderer::BindRuntimeTextures()
glActiveTexture(GL_TEXTURE0);
}
bool RuntimeShaderRenderer::CompileShader(GLenum shaderType, const char* source, GLuint& shader, std::string& error) const
bool RuntimeShaderRenderer::CompileShader(GLenum shaderType, const char* source, GLuint& shader, std::string& error)
{
shader = glCreateShader(shaderType);
glShaderSource(shader, 1, &source, nullptr);

View File

@@ -1,6 +1,7 @@
#pragma once
#include "GLExtensions.h"
#include "RuntimeShaderProgram.h"
#include "../runtime/RuntimeShaderArtifact.h"
#include <cstdint>
@@ -17,15 +18,22 @@ public:
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);
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;
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();