#pragma once #include "RuntimeJson.h" #include #include #include #include #include #include enum class ShaderParameterType { Float, Vec2, Color, Boolean, Enum }; struct ShaderParameterOption { std::string value; std::string label; }; struct ShaderParameterDefinition { std::string id; std::string label; ShaderParameterType type = ShaderParameterType::Float; std::vector defaultNumbers; std::vector minNumbers; std::vector maxNumbers; std::vector stepNumbers; bool defaultBoolean = false; std::string defaultEnumValue; std::vector enumOptions; }; struct ShaderParameterValue { std::vector numberValues; bool booleanValue = false; std::string enumValue; }; enum class TemporalHistorySource { None, Source, PreLayerInput }; struct TemporalSettings { bool enabled = false; TemporalHistorySource historySource = TemporalHistorySource::None; unsigned requestedHistoryLength = 0; unsigned effectiveHistoryLength = 0; }; struct ShaderPackage { std::string id; std::string displayName; std::string description; std::string category; std::string entryPoint; std::filesystem::path directoryPath; std::filesystem::path shaderPath; std::filesystem::path manifestPath; std::vector parameters; TemporalSettings temporal; std::filesystem::file_time_type shaderWriteTime; std::filesystem::file_time_type manifestWriteTime; }; struct RuntimeRenderState { std::string layerId; std::string shaderId; std::vector parameterDefinitions; std::map parameterValues; double timeSeconds = 0.0; double frameCount = 0.0; double mixAmount = 1.0; double bypass = 0.0; unsigned inputWidth = 0; unsigned inputHeight = 0; unsigned outputWidth = 0; unsigned outputHeight = 0; bool isTemporal = false; TemporalHistorySource temporalHistorySource = TemporalHistorySource::None; unsigned requestedTemporalHistoryLength = 0; unsigned effectiveTemporalHistoryLength = 0; }; class RuntimeHost { public: RuntimeHost(); bool Initialize(std::string& error); bool PollFileChanges(bool& registryChanged, bool& reloadRequested, std::string& error); bool ManualReloadRequested(); void ClearReloadRequest(); bool AddLayer(const std::string& shaderId, std::string& error); bool RemoveLayer(const std::string& layerId, std::string& error); bool MoveLayer(const std::string& layerId, int direction, std::string& error); bool MoveLayerToIndex(const std::string& layerId, std::size_t targetIndex, std::string& error); bool SetLayerBypass(const std::string& layerId, bool bypassed, std::string& error); bool SetLayerShader(const std::string& layerId, const std::string& shaderId, std::string& error); bool UpdateLayerParameter(const std::string& layerId, const std::string& parameterId, const JsonValue& newValue, std::string& error); bool ResetLayerParameters(const std::string& layerId, std::string& error); bool SaveStackPreset(const std::string& presetName, std::string& error) const; bool LoadStackPreset(const std::string& presetName, std::string& error); void SetCompileStatus(bool succeeded, const std::string& message); void SetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName); void SetDeckLinkOutputStatus(const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying, bool keyerInterfaceAvailable, bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage); void SetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds); void AdvanceFrame(); bool BuildLayerFragmentShaderSource(const std::string& layerId, std::string& fragmentShaderSource, std::string& error); std::vector GetLayerRenderStates(unsigned outputWidth, unsigned outputHeight) const; std::string BuildStateJson() const; const std::filesystem::path& GetRepoRoot() const { return mRepoRoot; } const std::filesystem::path& GetUiRoot() const { return mUiRoot; } const std::filesystem::path& GetRuntimeRoot() const { return mRuntimeRoot; } unsigned short GetServerPort() const { return mServerPort; } unsigned GetMaxTemporalHistoryFrames() const { return mConfig.maxTemporalHistoryFrames; } bool ExternalKeyingEnabled() const { return mConfig.enableExternalKeying; } void SetServerPort(unsigned short port); bool AutoReloadEnabled() const { return mAutoReloadEnabled; } private: struct AppConfig { std::string shaderLibrary = "shaders"; unsigned short serverPort = 8080; bool autoReload = true; unsigned maxTemporalHistoryFrames = 4; bool enableExternalKeying = false; }; struct DeckLinkOutputStatus { std::string modelName; bool supportsInternalKeying = false; bool supportsExternalKeying = false; bool keyerInterfaceAvailable = false; bool externalKeyingRequested = false; bool externalKeyingActive = false; std::string statusMessage; }; struct LayerPersistentState { std::string id; std::string shaderId; bool bypass = false; std::map parameterValues; }; struct PersistentState { std::vector layers; }; bool LoadConfig(std::string& error); bool LoadPersistentState(std::string& error); bool SavePersistentState(std::string& error) const; bool ScanShaderPackages(std::string& error); bool ParseShaderManifest(const std::filesystem::path& manifestPath, ShaderPackage& shaderPackage, std::string& error) const; bool NormalizeAndValidateValue(const ShaderParameterDefinition& definition, const JsonValue& value, ShaderParameterValue& normalizedValue, std::string& error) const; ShaderParameterValue DefaultValueForDefinition(const ShaderParameterDefinition& definition) const; void EnsureLayerDefaultsLocked(LayerPersistentState& layerState, const ShaderPackage& shaderPackage) const; std::string BuildWrapperSlangSource(const ShaderPackage& shaderPackage) const; bool FindSlangCompiler(std::filesystem::path& compilerPath, std::string& error) const; bool RunSlangCompiler(const std::filesystem::path& wrapperPath, const std::filesystem::path& outputPath, std::string& error) const; bool PatchGeneratedGlsl(std::string& shaderText, std::string& error) const; std::string ReadTextFile(const std::filesystem::path& path, std::string& error) const; bool WriteTextFile(const std::filesystem::path& path, const std::string& contents, std::string& error) const; bool ResolvePaths(std::string& error); JsonValue BuildStateValue() const; JsonValue SerializeLayerStackLocked() const; bool DeserializeLayerStackLocked(const JsonValue& layersValue, std::vector& layers, std::string& error); std::vector GetStackPresetNamesLocked() const; std::string MakeSafePresetFileStem(const std::string& presetName) const; JsonValue SerializeParameterValue(const ShaderParameterDefinition& definition, const ShaderParameterValue& value) const; std::string TemporalHistorySourceToString(TemporalHistorySource source) const; LayerPersistentState* FindLayerById(const std::string& layerId); const LayerPersistentState* FindLayerById(const std::string& layerId) const; std::string GenerateLayerId(); private: mutable std::mutex mMutex; AppConfig mConfig; PersistentState mPersistentState; std::filesystem::path mRepoRoot; std::filesystem::path mUiRoot; std::filesystem::path mShaderRoot; std::filesystem::path mRuntimeRoot; std::filesystem::path mPresetRoot; std::filesystem::path mRuntimeStatePath; std::filesystem::path mConfigPath; std::filesystem::path mWrapperPath; std::filesystem::path mGeneratedGlslPath; std::filesystem::path mPatchedGlslPath; std::map mPackagesById; std::vector mPackageOrder; bool mReloadRequested; bool mCompileSucceeded; std::string mCompileMessage; bool mHasSignal; unsigned mSignalWidth; unsigned mSignalHeight; std::string mSignalModeName; double mFrameBudgetMilliseconds; double mRenderMilliseconds; double mSmoothedRenderMilliseconds; DeckLinkOutputStatus mDeckLinkOutputStatus; unsigned short mServerPort; bool mAutoReloadEnabled; std::chrono::steady_clock::time_point mStartTime; std::chrono::steady_clock::time_point mLastScanTime; uint64_t mFrameCounter; uint64_t mNextLayerId; };