#pragma once #include "OpenGLRenderPass.h" #include "OpenGLRenderPipeline.h" #include "OpenGLRenderer.h" #include "OpenGLShaderPrograms.h" #include "RenderCommandQueue.h" #include "RenderFrameState.h" #include "RenderFrameStateResolver.h" #include "HealthTelemetry.h" #include "RuntimeCoordinator.h" #include "RuntimeSnapshotProvider.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class RenderEngine { public: using RenderEffectCallback = std::function; using ScreenshotCallback = std::function; using PreviewPaintCallback = std::function; struct OscOverlayUpdate { std::string routeKey; std::string layerKey; std::string parameterKey; JsonValue targetValue; }; struct OscOverlayCommitCompletion { std::string routeKey; uint64_t generation = 0; }; struct OscOverlayCommitRequest { std::string routeKey; std::string layerKey; std::string parameterKey; JsonValue value; uint64_t generation = 0; }; RenderEngine( RuntimeSnapshotProvider& runtimeSnapshotProvider, HealthTelemetry& healthTelemetry, CRITICAL_SECTION& mutex, HDC hdc, HGLRC hglrc, RenderEffectCallback renderEffect, ScreenshotCallback screenshotReady, PreviewPaintCallback previewPaint); ~RenderEngine(); bool StartRenderThread(); void StopRenderThread(); bool CompileDecodeShader(int errorMessageSize, char* errorMessage); bool CompileOutputPackShader(int errorMessageSize, char* errorMessage); bool InitializeResources( unsigned inputFrameWidth, unsigned inputFrameHeight, unsigned captureTextureWidth, unsigned outputFrameWidth, unsigned outputFrameHeight, unsigned outputPackTextureWidth, std::string& error); bool CompileLayerPrograms(unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage); bool ApplyPreparedShaderBuild( const PreparedShaderBuild& preparedBuild, unsigned inputFrameWidth, unsigned inputFrameHeight, bool preserveFeedbackState, int errorMessageSize, char* errorMessage); void ResetTemporalHistoryState(); void ResetShaderFeedbackState(); void ApplyRuntimeCoordinatorRenderReset(RuntimeCoordinatorRenderResetScope resetScope); void ClearOscOverlayState(); void UpdateOscOverlayState( const std::vector& updates, const std::vector& completedCommits); void ResizeView(int width, int height); bool TryPresentPreview(bool force, unsigned previewFps, unsigned outputFrameWidth, unsigned outputFrameHeight); bool TryUploadInputFrame(const VideoIOFrame& inputFrame, const VideoIOState& videoState); bool RenderOutputFrame(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame); bool ResolveRenderFrameState( const RenderFrameInput& input, std::vector* commitRequests, RenderFrameState& frameState); void RenderPreparedFrame(const RenderFrameState& frameState); bool CaptureOutputFrameRgbaTopDown(unsigned width, unsigned height, std::vector& topDownPixels); private: static constexpr std::chrono::milliseconds kRenderThreadRequestTimeout{ 250 }; struct RenderThreadTaskState { std::atomic started = false; std::atomic cancelled = false; }; template auto InvokeOnRenderThread(Func&& func) -> decltype(func()) { using Result = decltype(func()); if (!mRenderThreadRunning || GetCurrentThreadId() == mRenderThreadId) return func(); auto task = std::make_shared>(std::forward(func)); std::future result = task->get_future(); { std::lock_guard lock(mRenderThreadMutex); mRenderThreadTasks.push([task]() { (*task)(); }); } mRenderThreadCondition.notify_one(); return result.get(); } template bool TryInvokeOnRenderThread(const char* operationName, Func&& func) { if (!mRenderThreadRunning || GetCurrentThreadId() == mRenderThreadId) return func(); auto state = std::make_shared(); auto task = std::make_shared>( [state, func = std::forward(func)]() mutable { state->started = true; if (state->cancelled) return false; return func(); }); std::future result = task->get_future(); { std::lock_guard lock(mRenderThreadMutex); if (mRenderThreadStopping) { ReportRenderThreadRequestFailure(operationName, "render thread is stopping"); return false; } mRenderThreadTasks.push([task]() { (*task)(); }); } mRenderThreadCondition.notify_one(); if (result.wait_for(kRenderThreadRequestTimeout) == std::future_status::ready) return result.get(); if (!state->started) { state->cancelled = true; ReportRenderThreadRequestFailure(operationName, "timed out before execution"); return false; } ReportRenderThreadRequestFailure(operationName, "exceeded timeout while executing; waiting for safe completion"); return result.get(); } void RenderThreadMain(std::promise ready); void ReportRenderThreadRequestFailure(const char* operationName, const char* reason); bool CommitPreparedLayerPrograms(const PreparedShaderBuild& preparedBuild, unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage); void RenderLayerStack( bool hasInputSource, const std::vector& layerStates, unsigned inputFrameWidth, unsigned inputFrameHeight, unsigned captureTextureWidth, VideoIOPixelFormat inputPixelFormat, unsigned historyCap); void ResetTemporalHistoryStateOnRenderThread(); void ResetShaderFeedbackStateOnRenderThread(); void ApplyRenderResetOnRenderThread(RenderCommandResetScope resetScope); void ProcessRenderResetCommandsOnRenderThread(); bool PresentPreviewOnRenderThread(unsigned outputFrameWidth, unsigned outputFrameHeight); bool UploadInputFrameOnRenderThread(const VideoIOFrame& inputFrame, const VideoIOState& videoState); bool RenderOutputFrameOnRenderThread(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame); bool ReadOutputFrameRgbaOnRenderThread(unsigned width, unsigned height, std::vector& bottomUpPixels); bool CaptureOutputFrameRgbaTopDownOnRenderThread(unsigned width, unsigned height, std::vector& topDownPixels); OpenGLRenderer mRenderer; OpenGLRenderPass mRenderPass; OpenGLRenderPipeline mRenderPipeline; OpenGLShaderPrograms mShaderPrograms; CRITICAL_SECTION& mMutex; HDC mHdc; HGLRC mHglrc; std::chrono::steady_clock::time_point mLastPreviewPresentTime; RenderCommandQueue mRenderCommandQueue; RenderFrameStateResolver mFrameStateResolver; RuntimeLiveState mRuntimeLiveState; std::thread mRenderThread; std::atomic mRenderThreadId = 0; std::mutex mRenderThreadMutex; std::condition_variable mRenderThreadCondition; std::queue> mRenderThreadTasks; std::atomic mRenderThreadRunning = false; bool mRenderThreadStopping = false; bool mResourcesDestroyed = false; };