Files
video-shader-toys/src/RenderCadenceCompositor.cpp
Aiden e006fcc6ee
Some checks failed
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Failing after 2m30s
CI / Windows Release Package (push) Has been skipped
config cleanup
2026-05-22 16:05:40 +10:00

177 lines
5.7 KiB
C++

#include "app/AppConfig.h"
#include "app/AppConfigProvider.h"
#include "app/RenderCadenceApp.h"
#include "app/VideoBackendFactory.h"
#include "frames/InputFrameMailbox.h"
#include "frames/SystemFrameExchange.h"
#include "logging/Logger.h"
#include "render/thread/RenderThread.h"
#include "video/core/VideoIOFormat.h"
#include "video/core/VideoMode.h"
#include <windows.h>
#include <chrono>
#include <iostream>
#include <sstream>
#include <string>
namespace
{
constexpr std::size_t kDeckLinkTargetBufferedFrames = 4;
constexpr std::size_t kReadbackDepth = 6;
constexpr std::size_t kWritableOutputReserveFrames = kReadbackDepth + 2;
class ComInitGuard
{
public:
~ComInitGuard()
{
if (mInitialized)
CoUninitialize();
}
bool Initialize()
{
const HRESULT result = CoInitialize(nullptr);
mInitialized = SUCCEEDED(result);
mResult = result;
return mInitialized;
}
HRESULT Result() const { return mResult; }
private:
bool mInitialized = false;
HRESULT mResult = S_OK;
};
}
int main(int argc, char** argv)
{
RenderCadenceCompositor::AppConfigProvider configProvider;
std::string configError;
if (!configProvider.LoadDefault(configError))
{
RenderCadenceCompositor::Logger::Instance().Start(RenderCadenceCompositor::DefaultAppConfig().logging);
RenderCadenceCompositor::LogError("app", "Config load failed: " + configError);
RenderCadenceCompositor::Logger::Instance().Stop();
return 1;
}
configProvider.ApplyCommandLine(argc, argv);
RenderCadenceCompositor::AppConfig appConfig = configProvider.Config();
RenderCadenceCompositor::Logger::Instance().Start(appConfig.logging);
RenderCadenceCompositor::Log(
"app",
"RenderCadenceCompositor starting. Starts render cadence, configured video I/O backends, and telemetry. Press Enter to stop.");
RenderCadenceCompositor::Log("app", "Loaded config from " + configProvider.SourcePath().string());
ComInitGuard com;
if (RenderCadenceCompositor::VideoBackendsRequireCom(appConfig) && !com.Initialize())
{
std::ostringstream message;
message << "COM initialization failed: 0x" << std::hex << com.Result();
RenderCadenceCompositor::LogError("app", message.str());
RenderCadenceCompositor::Logger::Instance().Stop();
return 1;
}
SystemFrameExchangeConfig frameExchangeConfig;
VideoFormatDimensions(
appConfig.output.resolution,
frameExchangeConfig.width,
frameExchangeConfig.height);
frameExchangeConfig.pixelFormat = VideoIOPixelFormat::Bgra8;
frameExchangeConfig.rowBytes = VideoIORowBytes(frameExchangeConfig.pixelFormat, frameExchangeConfig.width);
frameExchangeConfig.capacity =
appConfig.warmupCompletedFrames +
kDeckLinkTargetBufferedFrames +
kWritableOutputReserveFrames;
frameExchangeConfig.maxCompletedFrames = appConfig.warmupCompletedFrames;
SystemFrameExchange frameExchange(frameExchangeConfig);
InputFrameMailboxConfig inputMailboxConfig;
VideoFormatDimensions(
appConfig.input.resolution,
inputMailboxConfig.width,
inputMailboxConfig.height);
inputMailboxConfig.pixelFormat = VideoIOPixelFormat::Bgra8;
inputMailboxConfig.rowBytes = VideoIORowBytes(inputMailboxConfig.pixelFormat, inputMailboxConfig.width);
inputMailboxConfig.capacity = 4;
inputMailboxConfig.maxReadyFrames = 3;
InputFrameMailbox inputMailbox(inputMailboxConfig);
VideoFormat inputVideoMode;
VideoFormat outputVideoMode;
std::string inputVideoModeError;
const bool inputVideoModeResolved = ResolveConfiguredVideoFormat(appConfig.input.resolution, appConfig.input.frameRate, inputVideoMode);
const bool outputVideoModeResolved = ResolveConfiguredVideoFormat(appConfig.output.resolution, appConfig.output.frameRate, outputVideoMode);
if (!inputVideoModeResolved)
{
inputVideoModeError = "Unsupported input resolution/frameRate in config/runtime-host.json: " +
appConfig.input.resolution + " / " + appConfig.input.frameRate;
RenderCadenceCompositor::LogWarning("app", inputVideoModeError);
}
if (!outputVideoModeResolved)
{
RenderCadenceCompositor::LogWarning(
"app",
"Unsupported output resolution/frameRate in config/runtime-host.json; render cadence will use parsed frame-rate fallback: " +
appConfig.output.resolution + " / " + appConfig.output.frameRate);
}
else
{
appConfig.output.videoMode = outputVideoMode;
}
auto inputBackend = RenderCadenceCompositor::StartVideoInputBackend(
appConfig,
inputMailbox,
inputMailboxConfig,
inputVideoMode,
inputVideoModeResolved);
RenderThread::Config renderConfig;
renderConfig.width = frameExchangeConfig.width;
renderConfig.height = frameExchangeConfig.height;
const double fallbackFrameDurationMilliseconds = FrameDurationMillisecondsFromRateString(appConfig.output.frameRate);
renderConfig.frameDurationMilliseconds = outputVideoModeResolved
? outputVideoMode.frameDurationMilliseconds
: fallbackFrameDurationMilliseconds;
renderConfig.pboDepth = kReadbackDepth;
RenderThread renderThread(frameExchange, &inputMailbox, renderConfig);
auto outputBackend = RenderCadenceCompositor::CreateVideoOutputBackend(appConfig);
RenderCadenceCompositor::RenderCadenceApp<RenderThread, SystemFrameExchange> app(
renderThread,
frameExchange,
appConfig,
std::move(outputBackend));
app.SetVideoInputMetricsProvider([inputBackend = inputBackend.get()]() {
return inputBackend ? inputBackend->Metrics() : RenderCadenceCompositor::VideoInputEdgeMetrics();
});
std::string error;
if (!app.Start(error))
{
RenderCadenceCompositor::LogError("app", "RenderCadenceCompositor start failed: " + error);
if (inputBackend)
inputBackend->Stop();
RenderCadenceCompositor::Logger::Instance().Stop();
return 1;
}
std::string line;
std::getline(std::cin, line);
app.Stop();
if (inputBackend)
inputBackend->Stop();
RenderCadenceCompositor::Log("app", "RenderCadenceCompositor stopped.");
RenderCadenceCompositor::Logger::Instance().Stop();
return 0;
}