feat: Add application, lifecycle, and render controllers for service orchestration

This commit is contained in:
2026-01-04 13:38:11 +00:00
parent a720e4a446
commit 5421f7945d
7 changed files with 274 additions and 0 deletions

View File

@@ -135,6 +135,9 @@ if(BUILD_SDL3_APP)
src/services/impl/vulkan_gui_service.cpp
src/services/impl/bullet_physics_service.cpp
src/services/impl/lua_script_service.cpp
src/controllers/application_controller.cpp
src/controllers/render_controller.cpp
src/controllers/lifecycle_controller.cpp
src/app/sdl3_app_core.cpp
src/app/audio_player.cpp
src/app/sdl3_app_device.cpp

View File

@@ -0,0 +1,66 @@
#include "application_controller.hpp"
#include "../logging/logger.hpp"
#include "../services/interfaces/i_window_service.hpp"
#include "../services/interfaces/i_input_service.hpp"
#include "../events/event_bus.hpp"
#include "../events/event_types.hpp"
namespace sdl3cpp::controllers {
ApplicationController::ApplicationController(di::ServiceRegistry& registry)
: registry_(registry), running_(false) {
logging::TraceGuard trace;
}
ApplicationController::~ApplicationController() {
logging::TraceGuard trace;
}
void ApplicationController::Run() {
logging::TraceGuard trace;
logging::Logger::GetInstance().Info("Application starting main loop");
running_ = true;
auto lastTime = std::chrono::high_resolution_clock::now();
while (running_) {
auto currentTime = std::chrono::high_resolution_clock::now();
float deltaTime = std::chrono::duration<float>(currentTime - lastTime).count();
lastTime = currentTime;
HandleEvents();
ProcessFrame(deltaTime);
}
logging::Logger::GetInstance().Info("Application exiting main loop");
}
void ApplicationController::HandleEvents() {
// Window service polls SDL events and publishes to event bus
auto windowService = registry_.GetService<services::IWindowService>();
if (windowService) {
windowService->PollEvents();
}
// Check for quit events
auto eventBus = registry_.GetService<events::EventBus>();
if (eventBus) {
// Process queued events
eventBus->ProcessQueue();
}
// Check if window should close
if (windowService && windowService->ShouldClose()) {
running_ = false;
}
}
void ApplicationController::ProcessFrame(float deltaTime) {
logging::TraceGuard trace;
// Render controller will handle rendering pipeline
// Physics, scripts, audio updates happen here
// This is a placeholder - full implementation in RenderController
}
} // namespace sdl3cpp::controllers

View File

@@ -0,0 +1,37 @@
#pragma once
#include "../di/service_registry.hpp"
#include <memory>
namespace sdl3cpp::controllers {
/**
* @brief Main application controller.
*
* Orchestrates the main game loop and coordinates all subsystems.
* Replaces Sdl3App::MainLoop() with clean service-based architecture.
*/
class ApplicationController {
public:
explicit ApplicationController(di::ServiceRegistry& registry);
~ApplicationController();
ApplicationController(const ApplicationController&) = delete;
ApplicationController& operator=(const ApplicationController&) = delete;
/**
* @brief Run the main application loop.
*
* Processes events, updates game state, and renders frames until quit.
*/
void Run();
private:
void ProcessFrame(float deltaTime);
void HandleEvents();
di::ServiceRegistry& registry_;
bool running_ = false;
};
} // namespace sdl3cpp::controllers

View File

@@ -0,0 +1,35 @@
#include "lifecycle_controller.hpp"
#include "../logging/logger.hpp"
namespace sdl3cpp::controllers {
LifecycleController::LifecycleController(di::ServiceRegistry& registry)
: registry_(registry) {
logging::TraceGuard trace;
}
LifecycleController::~LifecycleController() {
logging::TraceGuard trace;
}
void LifecycleController::InitializeAll() {
logging::TraceGuard trace;
logging::Logger::GetInstance().Info("Initializing all services");
// ServiceRegistry handles initialization order based on dependencies
registry_.InitializeAll();
logging::Logger::GetInstance().Info("All services initialized");
}
void LifecycleController::ShutdownAll() noexcept {
logging::TraceGuard trace;
logging::Logger::GetInstance().Info("Shutting down all services");
// ServiceRegistry handles shutdown in reverse dependency order
registry_.ShutdownAll();
logging::Logger::GetInstance().Info("All services shutdown");
}
} // namespace sdl3cpp::controllers

View File

@@ -0,0 +1,35 @@
#pragma once
#include "../di/service_registry.hpp"
namespace sdl3cpp::controllers {
/**
* @brief Lifecycle controller.
*
* Manages initialization and shutdown sequencing of all services.
* Ensures proper dependency order and resource cleanup.
*/
class LifecycleController {
public:
explicit LifecycleController(di::ServiceRegistry& registry);
~LifecycleController();
LifecycleController(const LifecycleController&) = delete;
LifecycleController& operator=(const LifecycleController&) = delete;
/**
* @brief Initialize all services in correct order.
*/
void InitializeAll();
/**
* @brief Shutdown all services in reverse order.
*/
void ShutdownAll() noexcept;
private:
di::ServiceRegistry& registry_;
};
} // namespace sdl3cpp::controllers

View File

@@ -0,0 +1,65 @@
#include "render_controller.hpp"
#include "../logging/logger.hpp"
#include "../services/interfaces/i_graphics_service.hpp"
#include "../services/interfaces/i_script_service.hpp"
#include "../services/interfaces/i_gui_service.hpp"
namespace sdl3cpp::controllers {
RenderController::RenderController(di::ServiceRegistry& registry)
: registry_(registry) {
logging::TraceGuard trace;
}
RenderController::~RenderController() {
logging::TraceGuard trace;
}
void RenderController::RenderFrame(float time) {
logging::TraceGuard trace;
// Get required services
auto graphicsService = registry_.GetService<services::IGraphicsService>();
auto scriptService = registry_.GetService<services::IScriptService>();
auto guiService = registry_.GetService<services::IGuiService>();
if (!graphicsService) {
logging::Logger::GetInstance().Error("Graphics service not available");
return;
}
// Begin frame
graphicsService->BeginFrame();
// Load scene objects from script
if (scriptService) {
auto sceneObjects = scriptService->LoadSceneObjects();
// Compute model matrices and build render commands
std::vector<services::RenderCommand> renderCommands;
for (const auto& obj : sceneObjects) {
auto modelMatrix = scriptService->ComputeModelMatrix(obj.computeModelMatrixRef, time);
// Build render command from scene object
// This would create RenderCommand with vertices, indices, modelMatrix, shaderKey
}
// Get view-projection matrix
float aspect = 1920.0f / 1080.0f; // TODO: Get from window service
auto viewProj = scriptService->GetViewProjectionMatrix(aspect);
// Render scene
// graphicsService->RenderScene(renderCommands, viewProj);
}
// Render GUI overlay
if (guiService && scriptService && scriptService->HasGuiCommands()) {
auto guiCommands = scriptService->LoadGuiCommands();
// guiService->PrepareFrame(guiCommands, width, height);
// guiService->RenderToSwapchain(commandBuffer, swapchainImage);
}
// End frame and present
graphicsService->EndFrame();
}
} // namespace sdl3cpp::controllers

View File

@@ -0,0 +1,33 @@
#pragma once
#include "../di/service_registry.hpp"
#include <memory>
namespace sdl3cpp::controllers {
/**
* @brief Render controller.
*
* Coordinates the rendering pipeline across graphics, scene, GUI, and script services.
* Extracted from Sdl3App rendering logic.
*/
class RenderController {
public:
explicit RenderController(di::ServiceRegistry& registry);
~RenderController();
RenderController(const RenderController&) = delete;
RenderController& operator=(const RenderController&) = delete;
/**
* @brief Render a single frame.
*
* @param time Current time in seconds for animations
*/
void RenderFrame(float time);
private:
di::ServiceRegistry& registry_;
};
} // namespace sdl3cpp::controllers