232 lines
7.7 KiB
C++
232 lines
7.7 KiB
C++
#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 <windows.h>
|
|
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#include <chrono>
|
|
#include <condition_variable>
|
|
#include <functional>
|
|
#include <future>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <queue>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
class RenderEngine
|
|
{
|
|
public:
|
|
using RenderEffectCallback = std::function<void()>;
|
|
using ScreenshotCallback = std::function<void()>;
|
|
using ScreenshotCaptureCallback = std::function<void(unsigned, unsigned, std::vector<unsigned char>)>;
|
|
using PreviewPaintCallback = std::function<void()>;
|
|
|
|
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,
|
|
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 ClearOscOverlayStateForLayerKey(const std::string& layerKey);
|
|
void UpdateOscOverlayState(
|
|
const std::vector<OscOverlayUpdate>& updates,
|
|
const std::vector<OscOverlayCommitCompletion>& completedCommits);
|
|
void ResizeView(int width, int height);
|
|
bool TryPresentPreview(bool force, unsigned previewFps, unsigned outputFrameWidth, unsigned outputFrameHeight);
|
|
bool RequestScreenshotCapture(unsigned width, unsigned height, ScreenshotCaptureCallback completion);
|
|
bool QueueInputFrame(const VideoIOFrame& inputFrame, const VideoIOState& videoState);
|
|
bool RequestOutputFrame(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame);
|
|
bool ResolveRenderFrameState(
|
|
const RenderFrameInput& input,
|
|
std::vector<OscOverlayCommitRequest>* commitRequests,
|
|
RenderFrameState& frameState);
|
|
void RenderPreparedFrame(const RenderFrameState& frameState);
|
|
|
|
private:
|
|
static constexpr std::chrono::milliseconds kRenderThreadRequestTimeout{ 250 };
|
|
|
|
struct RenderThreadTaskState
|
|
{
|
|
std::atomic<bool> started = false;
|
|
std::atomic<bool> cancelled = false;
|
|
};
|
|
|
|
template<typename Func>
|
|
auto InvokeOnRenderThread(Func&& func) -> decltype(func())
|
|
{
|
|
using Result = decltype(func());
|
|
if (!mRenderThreadRunning || GetCurrentThreadId() == mRenderThreadId)
|
|
return func();
|
|
|
|
auto task = std::make_shared<std::packaged_task<Result()>>(std::forward<Func>(func));
|
|
std::future<Result> result = task->get_future();
|
|
{
|
|
std::lock_guard<std::mutex> lock(mRenderThreadMutex);
|
|
mRenderThreadTasks.push([task]() { (*task)(); });
|
|
}
|
|
mRenderThreadCondition.notify_one();
|
|
return result.get();
|
|
}
|
|
|
|
template<typename Func>
|
|
bool TryInvokeOnRenderThread(const char* operationName, Func&& func)
|
|
{
|
|
if (!mRenderThreadRunning || GetCurrentThreadId() == mRenderThreadId)
|
|
return func();
|
|
|
|
auto state = std::make_shared<RenderThreadTaskState>();
|
|
auto task = std::make_shared<std::packaged_task<bool()>>(
|
|
[state, func = std::forward<Func>(func)]() mutable {
|
|
state->started = true;
|
|
if (state->cancelled)
|
|
return false;
|
|
|
|
return func();
|
|
});
|
|
std::future<bool> result = task->get_future();
|
|
{
|
|
std::lock_guard<std::mutex> 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<bool> ready);
|
|
void ReportRenderThreadRequestFailure(const char* operationName, const char* reason);
|
|
bool IsRenderThreadAccessExpected() const;
|
|
void ReportWrongThreadRenderAccess(const char* operationName) const;
|
|
bool CommitPreparedLayerPrograms(const PreparedShaderBuild& preparedBuild, unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
|
|
void RenderLayerStack(
|
|
bool hasInputSource,
|
|
const std::vector<RuntimeRenderState>& layerStates,
|
|
unsigned inputFrameWidth,
|
|
unsigned inputFrameHeight,
|
|
unsigned captureTextureWidth,
|
|
VideoIOPixelFormat inputPixelFormat,
|
|
unsigned historyCap);
|
|
void ResetTemporalHistoryStateOnRenderThread();
|
|
void ResetShaderFeedbackStateOnRenderThread();
|
|
void ApplyRenderResetOnRenderThread(RenderCommandResetScope resetScope);
|
|
void ProcessRenderResetCommandsOnRenderThread();
|
|
void EnqueuePreviewPresentWake();
|
|
void ProcessPreviewPresentCommandsOnRenderThread();
|
|
void EnqueueInputUploadWake();
|
|
void ProcessInputUploadCommandsOnRenderThread();
|
|
void EnqueueScreenshotCaptureWake();
|
|
void ProcessScreenshotCaptureCommandsOnRenderThread();
|
|
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<unsigned char>& bottomUpPixels);
|
|
bool CaptureOutputFrameRgbaTopDownOnRenderThread(unsigned width, unsigned height, std::vector<unsigned char>& topDownPixels);
|
|
|
|
OpenGLRenderer mRenderer;
|
|
OpenGLRenderPass mRenderPass;
|
|
OpenGLRenderPipeline mRenderPipeline;
|
|
OpenGLShaderPrograms mShaderPrograms;
|
|
HDC mHdc;
|
|
HGLRC mHglrc;
|
|
|
|
std::chrono::steady_clock::time_point mLastPreviewPresentTime;
|
|
RenderCommandQueue mRenderCommandQueue;
|
|
RenderFrameStateResolver mFrameStateResolver;
|
|
RuntimeLiveState mRuntimeLiveState;
|
|
std::thread mRenderThread;
|
|
std::atomic<DWORD> mRenderThreadId = 0;
|
|
std::mutex mRenderThreadMutex;
|
|
std::condition_variable mRenderThreadCondition;
|
|
std::queue<std::function<void()>> mRenderThreadTasks;
|
|
std::atomic<bool> mRenderThreadRunning = false;
|
|
bool mRenderThreadStopping = false;
|
|
bool mPreviewPresentWakePending = false;
|
|
bool mInputUploadWakePending = false;
|
|
bool mScreenshotCaptureWakePending = false;
|
|
ScreenshotCaptureCallback mScreenshotCaptureCompletion;
|
|
bool mResourcesDestroyed = false;
|
|
};
|