Files
video-shader-toys/apps/RenderCadenceCompositor/render/RenderThread.cpp
Aiden 4b62627479
Some checks failed
CI / React UI Build (push) Successful in 11s
CI / Windows Release Package (push) Has been cancelled
CI / Native Windows Build And Tests (push) Has been cancelled
removed render thread touching
2026-05-12 13:52:04 +10:00

242 lines
6.8 KiB
C++

#include "RenderThread.h"
#include "../frames/SystemFrameExchange.h"
#include "../frames/SystemFrameTypes.h"
#include "../logging/Logger.h"
#include "../platform/HiddenGlWindow.h"
#include "Bgra8ReadbackPipeline.h"
#include "GLExtensions.h"
#include "RuntimeShaderRenderer.h"
#include "SimpleMotionRenderer.h"
#include <algorithm>
#include <thread>
RenderThread::RenderThread(SystemFrameExchange& frameExchange, Config config) :
mFrameExchange(frameExchange),
mConfig(config)
{
}
RenderThread::~RenderThread()
{
Stop();
}
bool RenderThread::Start(std::string& error)
{
if (mThread.joinable())
return true;
{
std::lock_guard<std::mutex> lock(mStartupMutex);
mStarted = false;
mStartupError.clear();
}
mStopping.store(false, std::memory_order_release);
mThread = std::thread([this]() { ThreadMain(); });
std::unique_lock<std::mutex> lock(mStartupMutex);
if (!mStartupCondition.wait_for(lock, std::chrono::seconds(3), [this]() {
return mStarted || !mStartupError.empty();
}))
{
error = "Timed out starting render thread.";
return false;
}
if (!mStartupError.empty())
{
error = mStartupError;
lock.unlock();
if (mThread.joinable())
mThread.join();
return false;
}
return true;
}
void RenderThread::Stop()
{
mStopping.store(true, std::memory_order_release);
if (mThread.joinable())
mThread.join();
}
RenderThread::Metrics RenderThread::GetMetrics() const
{
Metrics metrics;
metrics.renderedFrames = mRenderedFrames.load(std::memory_order_relaxed);
metrics.completedReadbacks = mCompletedReadbacks.load(std::memory_order_relaxed);
metrics.acquireMisses = mAcquireMisses.load(std::memory_order_relaxed);
metrics.pboQueueMisses = mPboQueueMisses.load(std::memory_order_relaxed);
metrics.clockOverruns = mClockOverruns.load(std::memory_order_relaxed);
metrics.skippedFrames = mSkippedFrames.load(std::memory_order_relaxed);
metrics.shaderBuildsCommitted = mShaderBuildsCommitted.load(std::memory_order_relaxed);
metrics.shaderBuildFailures = mShaderBuildFailures.load(std::memory_order_relaxed);
return metrics;
}
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())
{
SignalStartupFailure(error.empty() ? "OpenGL context creation failed." : error);
return;
}
if (!ResolveGLExtensions())
{
SignalStartupFailure("OpenGL extension resolution failed.");
return;
}
SimpleMotionRenderer renderer;
RuntimeShaderRenderer runtimeShaderRenderer;
Bgra8ReadbackPipeline readback;
if (!renderer.InitializeGl(mConfig.width, mConfig.height) || !readback.Initialize(mConfig.width, mConfig.height, mConfig.pboDepth))
{
SignalStartupFailure("Render pipeline initialization failed.");
return;
}
RenderCadenceClock clock(mConfig.frameDurationMilliseconds);
uint64_t frameIndex = 0;
mRunning.store(true, std::memory_order_release);
SignalStarted();
while (!mStopping.load(std::memory_order_acquire))
{
readback.ConsumeCompleted(
[this](SystemFrame& frame) { return mFrameExchange.AcquireForRender(frame); },
[this](const SystemFrame& frame) { return mFrameExchange.PublishCompleted(frame); },
[this]() {
CountAcquireMiss();
},
[this]() { CountCompleted(); });
const auto now = RenderCadenceClock::Clock::now();
const RenderCadenceClock::Tick tick = clock.Poll(now);
if (!tick.due)
{
if (tick.sleepFor > RenderCadenceClock::Duration::zero())
std::this_thread::sleep_for(tick.sleepFor);
continue;
}
TryCommitReadyRuntimeShader(runtimeShaderRenderer);
if (!readback.RenderAndQueue(frameIndex, [this, &renderer, &runtimeShaderRenderer](uint64_t index) {
if (runtimeShaderRenderer.HasProgram())
runtimeShaderRenderer.RenderFrame(index, mConfig.width, mConfig.height);
else
renderer.RenderFrame(index);
}))
{
mPboQueueMisses.fetch_add(1, std::memory_order_relaxed);
}
CountRendered();
++frameIndex;
clock.MarkRendered(RenderCadenceClock::Clock::now());
mClockOverruns.store(clock.OverrunCount(), std::memory_order_relaxed);
mSkippedFrames.store(clock.SkippedFrameCount(), std::memory_order_relaxed);
}
for (std::size_t i = 0; i < mConfig.pboDepth * 2; ++i)
{
readback.ConsumeCompleted(
[this](SystemFrame& frame) { return mFrameExchange.AcquireForRender(frame); },
[this](const SystemFrame& frame) { return mFrameExchange.PublishCompleted(frame); },
[this]() {
CountAcquireMiss();
},
[this]() { CountCompleted(); });
}
readback.Shutdown();
runtimeShaderRenderer.ShutdownGl();
renderer.ShutdownGl();
window.ClearCurrent();
mRunning.store(false, std::memory_order_release);
RenderCadenceCompositor::TryLog(RenderCadenceCompositor::LogLevel::Log, "render-thread", "Render thread stopped.");
}
void RenderThread::SignalStarted()
{
std::lock_guard<std::mutex> lock(mStartupMutex);
mStarted = true;
mStartupCondition.notify_all();
}
void RenderThread::SignalStartupFailure(const std::string& error)
{
RenderCadenceCompositor::TryLog(RenderCadenceCompositor::LogLevel::Error, "render-thread", error);
std::lock_guard<std::mutex> lock(mStartupMutex);
mStartupError = error;
mStartupCondition.notify_all();
}
void RenderThread::CountRendered()
{
mRenderedFrames.fetch_add(1, std::memory_order_relaxed);
}
void RenderThread::CountCompleted()
{
mCompletedReadbacks.fetch_add(1, std::memory_order_relaxed);
}
void RenderThread::CountAcquireMiss()
{
mAcquireMisses.fetch_add(1, std::memory_order_relaxed);
}
void RenderThread::SubmitRuntimeShaderArtifact(const RuntimeShaderArtifact& artifact)
{
if (artifact.fragmentShaderSource.empty())
return;
std::lock_guard<std::mutex> lock(mShaderArtifactMutex);
mPendingShaderArtifact = artifact;
mHasPendingShaderArtifact = true;
}
bool RenderThread::TryTakePendingRuntimeShaderArtifact(RuntimeShaderArtifact& artifact)
{
std::lock_guard<std::mutex> lock(mShaderArtifactMutex);
if (!mHasPendingShaderArtifact)
return false;
artifact = std::move(mPendingShaderArtifact);
mPendingShaderArtifact = RuntimeShaderArtifact();
mHasPendingShaderArtifact = false;
return true;
}
void RenderThread::TryCommitReadyRuntimeShader(RuntimeShaderRenderer& runtimeShaderRenderer)
{
RuntimeShaderArtifact artifact;
if (!TryTakePendingRuntimeShaderArtifact(artifact))
return;
std::string commitError;
if (!runtimeShaderRenderer.CommitShaderArtifact(artifact, commitError))
{
RenderCadenceCompositor::TryLog(
RenderCadenceCompositor::LogLevel::Error,
"render-thread",
"Runtime shader GL commit failed: " + commitError);
mShaderBuildFailures.fetch_add(1, std::memory_order_relaxed);
return;
}
RenderCadenceCompositor::TryLog(
RenderCadenceCompositor::LogLevel::Log,
"render-thread",
"Runtime shader committed: " + artifact.shaderId + ". " + artifact.message);
mShaderBuildsCommitted.fetch_add(1, std::memory_order_relaxed);
}