ROADMAP.md

This commit is contained in:
2026-01-09 21:54:32 +00:00
parent 35d27a44e1
commit 8a02d4985a
16 changed files with 1324 additions and 1041 deletions

View File

@@ -274,6 +274,7 @@ endif()
set(JSON_CONFIG_SOURCES
src/services/impl/json_config_service.cpp
src/services/impl/runtime_config_builder.cpp
src/services/impl/json_config_document_loader.cpp
src/services/impl/json_config_document_parser.cpp
src/services/impl/json_config_extend_resolver.cpp
@@ -296,6 +297,7 @@ set(WORKFLOW_SOURCES
src/services/impl/workflow_config_migration_step.cpp
src/services/impl/workflow_config_schema_step.cpp
src/services/impl/workflow_default_step_registrar.cpp
src/services/impl/workflow_runtime_config_step.cpp
)
set(MATERIALX_SCRIPT_SOURCES

View File

@@ -242,8 +242,8 @@ Option B: per-shader only
- [x] Template package: `config/workflows/templates/boot_default.json`.
### Next Steps
- Move RuntimeConfig parsing into a workflow step.
- Add frame workflow template (BeginFrame → RenderGraph → Capture → Validate).
- [x] Move RuntimeConfig parsing into a workflow step.
- [ ] Add frame workflow template (BeginFrame → RenderGraph → Capture → Validate).
## Feature Matrix (What You Get, When You Get It)

View File

@@ -42,6 +42,17 @@
"document": "config.document",
"path": "config.path"
}
},
{
"id": "build_runtime_config",
"plugin": "runtime.config.build",
"inputs": {
"document": "config.document",
"path": "config.path"
},
"outputs": {
"runtime": "config.runtime"
}
}
]
}

View File

@@ -0,0 +1,42 @@
{
"template": "frame.default",
"steps": [
{
"id": "begin_frame",
"plugin": "frame.begin",
"inputs": {
"delta": "frame.delta",
"elapsed": "frame.elapsed"
}
},
{
"id": "step_physics",
"plugin": "frame.physics",
"inputs": {
"delta": "frame.delta"
}
},
{
"id": "update_scene",
"plugin": "frame.scene",
"inputs": {
"delta": "frame.delta"
}
},
{
"id": "render_frame",
"plugin": "frame.render",
"inputs": {
"elapsed": "frame.elapsed"
}
},
{
"id": "update_audio",
"plugin": "frame.audio"
},
{
"id": "dispatch_gui",
"plugin": "frame.gui"
}
]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,24 @@
#pragma once
#include "../interfaces/config_types.hpp"
#include "../interfaces/i_logger.hpp"
#include <filesystem>
#include <memory>
#include <rapidjson/document.h>
namespace sdl3cpp::services::impl {
class RuntimeConfigBuilder final {
public:
explicit RuntimeConfigBuilder(std::shared_ptr<ILogger> logger);
services::RuntimeConfig Build(const rapidjson::Document& document,
const std::filesystem::path& configPath) const;
private:
std::shared_ptr<ILogger> logger_;
};
} // namespace sdl3cpp::services::impl

View File

@@ -7,11 +7,11 @@
#include "workflow_template_resolver.hpp"
#include "../interfaces/i_logger.hpp"
#include "../interfaces/i_probe_service.hpp"
#include "../interfaces/workflow_context.hpp"
#include <stdexcept>
#include <string>
#include <utility>
namespace sdl3cpp::services::impl {
WorkflowConfigPipeline::WorkflowConfigPipeline(std::shared_ptr<ILogger> logger,
@@ -19,9 +19,8 @@ WorkflowConfigPipeline::WorkflowConfigPipeline(std::shared_ptr<ILogger> logger,
: logger_(std::move(logger)),
probeService_(std::move(probeService)) {}
std::shared_ptr<rapidjson::Document> WorkflowConfigPipeline::Execute(
const std::filesystem::path& configPath,
std::optional<int>* versionOut) const {
WorkflowResult WorkflowConfigPipeline::Execute(const std::filesystem::path& configPath,
std::optional<int>* versionOut) const {
if (logger_) {
logger_->Trace("WorkflowConfigPipeline", "Execute",
"configPath=" + configPath.string(),
@@ -51,10 +50,6 @@ std::shared_ptr<rapidjson::Document> WorkflowConfigPipeline::Execute(
if (!documentHandle || !(*documentHandle)) {
throw std::runtime_error("WorkflowConfigPipeline: boot workflow did not provide config.document");
}
if (versionOut) {
const auto* versionHandle = context.TryGet<std::optional<int>>("config.version");
*versionOut = versionHandle ? *versionHandle : std::nullopt;
}
if (logger_) {
logger_->Trace("WorkflowConfigPipeline", "Execute",
@@ -62,7 +57,14 @@ std::shared_ptr<rapidjson::Document> WorkflowConfigPipeline::Execute(
"Boot workflow complete");
}
return *documentHandle;
WorkflowResult result;
result.document = *documentHandle;
if (versionOut) {
const auto* versionHandle = context.TryGet<std::optional<int>>("config.version");
*versionOut = versionHandle ? *versionHandle : std::nullopt;
}
result.context = std::move(context);
return result;
}
} // namespace sdl3cpp::services::impl

View File

@@ -4,6 +4,8 @@
#include <memory>
#include <optional>
#include "../interfaces/workflow_context.hpp"
#include <rapidjson/document.h>
namespace sdl3cpp::services {
@@ -13,13 +15,18 @@ class IProbeService;
namespace sdl3cpp::services::impl {
struct WorkflowResult {
WorkflowContext context;
std::shared_ptr<rapidjson::Document> document;
};
class WorkflowConfigPipeline {
public:
WorkflowConfigPipeline(std::shared_ptr<ILogger> logger,
std::shared_ptr<IProbeService> probeService);
std::shared_ptr<rapidjson::Document> Execute(const std::filesystem::path& configPath,
std::optional<int>* versionOut) const;
WorkflowResult Execute(const std::filesystem::path& configPath,
std::optional<int>* versionOut) const;
private:
std::shared_ptr<ILogger> logger_;

View File

@@ -3,6 +3,7 @@
#include "workflow_config_migration_step.hpp"
#include "workflow_config_schema_step.hpp"
#include "workflow_config_version_step.hpp"
#include "workflow_runtime_config_step.hpp"
#include <stdexcept>
#include <unordered_set>
@@ -39,6 +40,9 @@ void WorkflowDefaultStepRegistrar::RegisterUsedSteps(
if (plugins.contains("config.schema.validate")) {
registry->RegisterStep(std::make_shared<WorkflowConfigSchemaStep>(logger_, probeService_));
}
if (plugins.contains("runtime.config.build")) {
registry->RegisterStep(std::make_shared<WorkflowRuntimeConfigStep>(logger_));
}
}
} // namespace sdl3cpp::services::impl

View File

@@ -0,0 +1,34 @@
#include "workflow_frame_begin_step.hpp"
#include "workflow_step_io_resolver.hpp"
#include <stdexcept>
namespace sdl3cpp::services::impl {
WorkflowFrameBeginStep::WorkflowFrameBeginStep(std::shared_ptr<ILogger> logger)
: logger_(std::move(logger)) {}
std::string WorkflowFrameBeginStep::GetPluginId() const {
return "frame.begin";
}
void WorkflowFrameBeginStep::Execute(const WorkflowStepDefinition& step, WorkflowContext& context) {
WorkflowStepIoResolver resolver;
const std::string deltaKey = resolver.GetRequiredInputKey(step, "delta");
const std::string elapsedKey = resolver.GetRequiredInputKey(step, "elapsed");
const auto* delta = context.TryGet<double>(deltaKey);
const auto* elapsed = context.TryGet<double>(elapsedKey);
if (!delta || !elapsed) {
throw std::runtime_error("frame.begin requires delta and elapsed inputs");
}
if (logger_) {
logger_->Trace("WorkflowFrameBeginStep", "Execute",
"delta=" + std::to_string(*delta) +
", elapsed=" + std::to_string(*elapsed),
"Starting frame workflow");
}
}
} // namespace sdl3cpp::services::impl

View File

@@ -0,0 +1,21 @@
#pragma once
#include "../interfaces/i_workflow_step.hpp"
#include "../interfaces/i_logger.hpp"
#include <memory>
namespace sdl3cpp::services::impl {
class WorkflowFrameBeginStep final : public IWorkflowStep {
public:
explicit WorkflowFrameBeginStep(std::shared_ptr<ILogger> logger);
std::string GetPluginId() const override;
void Execute(const WorkflowStepDefinition& step, WorkflowContext& context) override;
private:
std::shared_ptr<ILogger> logger_;
};
} // namespace sdl3cpp::services::impl

View File

@@ -0,0 +1,35 @@
#include "workflow_frame_physics_step.hpp"
#include "workflow_step_io_resolver.hpp"
#include <stdexcept>
namespace sdl3cpp::services::impl {
WorkflowFramePhysicsStep::WorkflowFramePhysicsStep(std::shared_ptr<IPhysicsService> physicsService,
std::shared_ptr<ILogger> logger)
: physicsService_(std::move(physicsService)),
logger_(std::move(logger)) {}
std::string WorkflowFramePhysicsStep::GetPluginId() const {
return "frame.physics";
}
void WorkflowFramePhysicsStep::Execute(const WorkflowStepDefinition& step, WorkflowContext& context) {
if (!physicsService_) {
throw std::runtime_error("frame.physics requires an IPhysicsService");
}
WorkflowStepIoResolver resolver;
const std::string deltaKey = resolver.GetRequiredInputKey(step, "delta");
const auto* delta = context.TryGet<double>(deltaKey);
if (!delta) {
throw std::runtime_error("frame.physics missing delta input");
}
physicsService_->StepSimulation(static_cast<float>(*delta));
if (logger_) {
logger_->Trace("WorkflowFramePhysicsStep", "Execute",
"delta=" + std::to_string(*delta),
"Physics step completed");
}
}
} // namespace sdl3cpp::services::impl

View File

@@ -0,0 +1,24 @@
#pragma once
#include "../interfaces/i_workflow_step.hpp"
#include "../interfaces/i_logger.hpp"
#include "../interfaces/i_physics_service.hpp"
#include <memory>
namespace sdl3cpp::services::impl {
class WorkflowFramePhysicsStep final : public IWorkflowStep {
public:
WorkflowFramePhysicsStep(std::shared_ptr<IPhysicsService> physicsService,
std::shared_ptr<ILogger> logger);
std::string GetPluginId() const override;
void Execute(const WorkflowStepDefinition& step, WorkflowContext& context) override;
private:
std::shared_ptr<IPhysicsService> physicsService_;
std::shared_ptr<ILogger> logger_;
};
} // namespace sdl3cpp::services::impl

View File

@@ -0,0 +1,48 @@
#include "workflow_runtime_config_step.hpp"
#include "runtime_config_builder.hpp"
#include "workflow_step_io_resolver.hpp"
#include <rapidjson/document.h>
#include <filesystem>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
namespace sdl3cpp::services::impl {
WorkflowRuntimeConfigStep::WorkflowRuntimeConfigStep(std::shared_ptr<ILogger> logger)
: logger_(std::move(logger)) {}
std::string WorkflowRuntimeConfigStep::GetPluginId() const {
return "runtime.config.build";
}
void WorkflowRuntimeConfigStep::Execute(const WorkflowStepDefinition& step, WorkflowContext& context) {
WorkflowStepIoResolver resolver;
const std::string documentKey = resolver.GetRequiredInputKey(step, "document");
const std::string pathKey = resolver.GetRequiredInputKey(step, "path");
const std::string outputKey = resolver.GetRequiredOutputKey(step, "runtime");
const auto* documentHandle = context.TryGet<std::shared_ptr<rapidjson::Document>>(documentKey);
if (!documentHandle || !(*documentHandle)) {
throw std::runtime_error("Workflow runtime.config.build missing document input '" + documentKey + "'");
}
std::filesystem::path pathValue;
if (const auto* path = context.TryGet<std::filesystem::path>(pathKey)) {
pathValue = *path;
} else if (const auto* pathString = context.TryGet<std::string>(pathKey)) {
pathValue = *pathString;
} else {
throw std::runtime_error("Workflow runtime.config.build missing path input '" + pathKey + "'");
}
RuntimeConfigBuilder builder(logger_);
RuntimeConfig config = builder.Build(**documentHandle, pathValue);
context.Set(outputKey, std::move(config));
}
} // namespace sdl3cpp::services::impl

View File

@@ -0,0 +1,20 @@
#pragma once
#include "../interfaces/i_workflow_step.hpp"
#include "../interfaces/i_logger.hpp"
#include <memory>
namespace sdl3cpp::services::impl {
class WorkflowRuntimeConfigStep : public IWorkflowStep {
public:
explicit WorkflowRuntimeConfigStep(std::shared_ptr<ILogger> logger);
std::string GetPluginId() const override;
void Execute(const WorkflowStepDefinition& step, WorkflowContext& context) override;
private:
std::shared_ptr<ILogger> logger_;
};
} // namespace sdl3cpp::services::impl