#pragma once #include "RenderCadenceClock.h" #include "../runtime/RuntimeLayerModel.h" #include "../runtime/RuntimeShaderArtifact.h" #include "runtime/RuntimeRenderScene.h" #include #include #include #include #include #include #include class SystemFrameExchange; class InputFrameMailbox; class InputFrameTexture; class Bgra8ReadbackPipeline; class RenderThread { public: struct Config { unsigned width = 1920; unsigned height = 1080; double frameDurationMilliseconds = 1000.0 / 59.94; std::size_t pboDepth = 6; }; struct Metrics { uint64_t renderedFrames = 0; uint64_t completedReadbacks = 0; uint64_t acquireMisses = 0; uint64_t pboQueueMisses = 0; uint64_t clockOverruns = 0; uint64_t skippedFrames = 0; uint64_t shaderBuildsCommitted = 0; uint64_t shaderBuildFailures = 0; double renderFrameMilliseconds = 0.0; double renderFrameBudgetUsedPercent = 0.0; double renderFrameMaxMilliseconds = 0.0; double readbackQueueMilliseconds = 0.0; double completedReadbackCopyMilliseconds = 0.0; uint64_t inputFramesReceived = 0; uint64_t inputFramesDropped = 0; uint64_t inputConsumeMisses = 0; uint64_t inputUploadMisses = 0; std::size_t inputReadyFrames = 0; std::size_t inputReadingFrames = 0; double inputLatestAgeMilliseconds = 0.0; double inputUploadMilliseconds = 0.0; bool inputFormatSupported = true; bool inputSignalPresent = false; }; RenderThread(SystemFrameExchange& frameExchange, Config config); RenderThread(SystemFrameExchange& frameExchange, InputFrameMailbox* inputMailbox, Config config); RenderThread(const RenderThread&) = delete; RenderThread& operator=(const RenderThread&) = delete; ~RenderThread(); bool Start(std::string& error); void Stop(); void SubmitRuntimeShaderArtifact(const RuntimeShaderArtifact& artifact); void SubmitRuntimeRenderLayers(const std::vector& layers); Metrics GetMetrics() const; bool IsRunning() const { return mRunning.load(std::memory_order_acquire); } private: void ThreadMain(); void SignalStarted(); void SignalStartupFailure(const std::string& error); void CountRendered(); void CountCompleted(); void CountAcquireMiss(); void PublishReadbackMetrics(const Bgra8ReadbackPipeline& readback); void PublishInputMetrics(const InputFrameTexture& inputTexture); void TryCommitReadyRuntimeShader(RuntimeRenderScene& runtimeRenderScene); bool TryTakePendingRuntimeShaderArtifact(RuntimeShaderArtifact& artifact); bool TryTakePendingRenderLayers(std::vector& layers); SystemFrameExchange& mFrameExchange; InputFrameMailbox* mInputMailbox = nullptr; Config mConfig; std::thread mThread; std::atomic mStopping{ false }; std::atomic mRunning{ false }; mutable std::mutex mStartupMutex; std::condition_variable mStartupCondition; bool mStarted = false; std::string mStartupError; std::atomic mRenderedFrames{ 0 }; std::atomic mCompletedReadbacks{ 0 }; std::atomic mAcquireMisses{ 0 }; std::atomic mPboQueueMisses{ 0 }; std::atomic mClockOverruns{ 0 }; std::atomic mSkippedFrames{ 0 }; std::atomic mShaderBuildsCommitted{ 0 }; std::atomic mShaderBuildFailures{ 0 }; std::atomic mRenderFrameMilliseconds{ 0.0 }; std::atomic mRenderFrameBudgetUsedPercent{ 0.0 }; std::atomic mRenderFrameMaxMilliseconds{ 0.0 }; std::atomic mReadbackQueueMilliseconds{ 0.0 }; std::atomic mCompletedReadbackCopyMilliseconds{ 0.0 }; std::atomic mInputFramesReceived{ 0 }; std::atomic mInputFramesDropped{ 0 }; std::atomic mInputConsumeMisses{ 0 }; std::atomic mInputUploadMisses{ 0 }; std::atomic mInputReadyFrames{ 0 }; std::atomic mInputReadingFrames{ 0 }; std::atomic mInputLatestAgeMilliseconds{ 0.0 }; std::atomic mInputUploadMilliseconds{ 0.0 }; std::atomic mInputFormatSupported{ true }; std::atomic mInputSignalPresent{ false }; std::mutex mShaderArtifactMutex; bool mHasPendingShaderArtifact = false; RuntimeShaderArtifact mPendingShaderArtifact; std::mutex mRenderLayersMutex; bool mHasPendingRenderLayers = false; std::vector mPendingRenderLayers; };