#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 "RuntimeRenderScene.h" #include "RuntimeShaderRenderer.h" #include "SimpleMotionRenderer.h" #include #include 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 lock(mStartupMutex); mStarted = false; mStartupError.clear(); } mStopping.store(false, std::memory_order_release); mThread = std::thread([this]() { ThreadMain(); }); std::unique_lock 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; RuntimeRenderScene runtimeRenderScene; 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(runtimeRenderScene); if (!readback.RenderAndQueue(frameIndex, [this, &renderer, &runtimeRenderScene](uint64_t index) { if (runtimeRenderScene.HasLayers()) runtimeRenderScene.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(); runtimeRenderScene.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 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 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 lock(mShaderArtifactMutex); mPendingShaderArtifact = artifact; mHasPendingShaderArtifact = true; } void RenderThread::SubmitRuntimeRenderLayers(const std::vector& layers) { std::lock_guard lock(mRenderLayersMutex); mPendingRenderLayers = layers; mHasPendingRenderLayers = true; } bool RenderThread::TryTakePendingRuntimeShaderArtifact(RuntimeShaderArtifact& artifact) { std::lock_guard lock(mShaderArtifactMutex); if (!mHasPendingShaderArtifact) return false; artifact = std::move(mPendingShaderArtifact); mPendingShaderArtifact = RuntimeShaderArtifact(); mHasPendingShaderArtifact = false; return true; } bool RenderThread::TryTakePendingRenderLayers(std::vector& layers) { std::lock_guard lock(mRenderLayersMutex); if (!mHasPendingRenderLayers) return false; layers = std::move(mPendingRenderLayers); mPendingRenderLayers.clear(); mHasPendingRenderLayers = false; return true; } void RenderThread::TryCommitReadyRuntimeShader(RuntimeRenderScene& runtimeRenderScene) { std::vector layers; std::string commitError; if (TryTakePendingRenderLayers(layers)) { if (!runtimeRenderScene.CommitRenderLayers(layers, commitError)) { RenderCadenceCompositor::TryLog( RenderCadenceCompositor::LogLevel::Error, "render-thread", "Runtime render-layer commit failed: " + commitError); mShaderBuildFailures.fetch_add(1, std::memory_order_relaxed); return; } RenderCadenceCompositor::TryLog( RenderCadenceCompositor::LogLevel::Log, "render-thread", "Runtime render layer snapshot committed."); mShaderBuildsCommitted.fetch_add(1, std::memory_order_relaxed); return; } RuntimeShaderArtifact artifact; if (!TryTakePendingRuntimeShaderArtifact(artifact)) return; RenderCadenceCompositor::RuntimeRenderLayerModel layer; layer.id = artifact.layerId.empty() ? "runtime-layer-1" : artifact.layerId; layer.shaderId = artifact.shaderId; layer.artifact = artifact; layers.push_back(std::move(layer)); if (!runtimeRenderScene.CommitRenderLayers(layers, 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); }