refactor: Enhance service architecture by introducing IPlatformService and updating dependencies

- Removed core/platform.hpp and core/vulkan_utils.cpp, integrating their functionality into new platform_service implementations.
- Updated service registrations to utilize IPlatformService for improved modularity.
- Refactored event bus usage across services to leverage IEventBus interface.
- Enhanced buffer management in BufferService with detailed logging and error handling.
- Updated GUI rendering services to utilize buffer service for resource management.
- Cleaned up includes and improved overall code organization for better maintainability.
This commit is contained in:
2026-01-04 17:43:18 +00:00
parent 1e6be869e0
commit 00a359d85f
23 changed files with 254 additions and 220 deletions

View File

@@ -1,8 +1,10 @@
#include "service_based_app.hpp"
#include "events/event_bus.hpp"
#include "events/i_event_bus.hpp"
#include "services/interfaces/i_window_service.hpp"
#include "services/interfaces/i_graphics_service.hpp"
#include "services/impl/json_config_service.hpp"
#include "services/impl/platform_service.hpp"
#include "services/impl/sdl_window_service.hpp"
#include "services/impl/sdl_input_service.hpp"
#include "services/impl/vulkan_device_service.hpp"
@@ -24,6 +26,7 @@
#include "services/impl/bullet_physics_service.hpp"
#include "services/impl/crash_recovery_service.hpp"
#include "services/impl/logger_service.hpp"
#include "services/interfaces/i_platform_service.hpp"
#include <iostream>
#include <stdexcept>
@@ -173,8 +176,12 @@ void ServiceBasedApp::RegisterServices() {
registry_.RegisterService<services::ICrashRecoveryService, services::impl::CrashRecoveryService>(
registry_.GetService<services::ILogger>());
// Platform service (needed for SDL error enrichment)
registry_.RegisterService<services::IPlatformService, services::impl::PlatformService>(
registry_.GetService<services::ILogger>());
// Event bus (needed by window service)
registry_.RegisterService<events::EventBus, events::EventBus>();
registry_.RegisterService<events::IEventBus, events::EventBus>();
// Configuration service
services::impl::RuntimeConfig runtimeConfig;
@@ -185,11 +192,12 @@ void ServiceBasedApp::RegisterServices() {
// Window service
registry_.RegisterService<services::IWindowService, services::impl::SdlWindowService>(
registry_.GetService<services::ILogger>(),
registry_.GetService<events::EventBus>());
registry_.GetService<services::IPlatformService>(),
registry_.GetService<events::IEventBus>());
// Input service
registry_.RegisterService<services::IInputService, services::impl::SdlInputService>(
registry_.GetService<events::EventBus>(),
registry_.GetService<events::IEventBus>(),
registry_.GetService<services::ILogger>());
// Audio service (needed before script bindings execute)
@@ -237,7 +245,7 @@ void ServiceBasedApp::RegisterServices() {
// Swapchain service
registry_.RegisterService<services::ISwapchainService, services::impl::SwapchainService>(
registry_.GetService<services::IVulkanDeviceService>(),
registry_.GetService<events::EventBus>(),
registry_.GetService<events::IEventBus>(),
registry_.GetService<services::ILogger>());
// Pipeline service
@@ -273,7 +281,8 @@ void ServiceBasedApp::RegisterServices() {
// GUI service
registry_.RegisterService<services::IGuiService, services::impl::VulkanGuiService>(
registry_.GetService<services::ILogger>());
registry_.GetService<services::ILogger>(),
registry_.GetService<services::IBufferService>());
// Physics service
registry_.RegisterService<services::IPhysicsService, services::impl::BulletPhysicsService>(

View File

@@ -6,7 +6,7 @@
#include "../services/interfaces/i_physics_service.hpp"
#include "../services/interfaces/i_scene_service.hpp"
#include "../services/interfaces/i_audio_service.hpp"
#include "../events/event_bus.hpp"
#include "../events/i_event_bus.hpp"
#include "../events/event_types.hpp"
#include <chrono>
@@ -60,7 +60,7 @@ void ApplicationController::HandleEvents() {
}
// Check for quit events
auto eventBus = registry_.GetService<events::EventBus>();
auto eventBus = registry_.GetService<events::IEventBus>();
if (eventBus) {
// Process queued events
eventBus->ProcessQueue();

View File

@@ -1,27 +0,0 @@
#ifndef SDL3CPP_CORE_PLATFORM_HPP
#define SDL3CPP_CORE_PLATFORM_HPP
#include <filesystem>
#include <optional>
#include <string>
namespace sdl3cpp::platform {
// Platform detection
#ifdef _WIN32
constexpr bool IsWindows = true;
constexpr bool IsUnix = false;
#else
constexpr bool IsWindows = false;
constexpr bool IsUnix = true;
#endif
// Get user configuration directory (platform-specific)
std::optional<std::filesystem::path> GetUserConfigDirectory();
// Format platform-specific error messages
std::string GetPlatformError();
} // namespace sdl3cpp::platform
#endif // SDL3CPP_CORE_PLATFORM_HPP

View File

@@ -1,114 +0,0 @@
#include "vulkan_utils.hpp"
#include <algorithm>
#include <stdexcept>
#include <string>
namespace sdl3cpp::vulkan::utils {
namespace {
uint32_t FindMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties) {
VkPhysicalDeviceMemoryProperties memProperties;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
for (uint32_t i = 0; i < memProperties.memoryTypeCount; ++i) {
if ((typeFilter & (1 << i)) &&
(memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
return i;
}
}
throw std::runtime_error("Failed to find suitable memory type");
}
} // namespace
VkExtent2D ChooseSwapExtent(VkSurfaceCapabilitiesKHR capabilities, SDL_Window* window) {
int width = 0;
int height = 0;
SDL_GetWindowSize(window, &width, &height);
return VkExtent2D{
static_cast<uint32_t>(std::clamp(width, static_cast<int>(capabilities.minImageExtent.width),
static_cast<int>(capabilities.maxImageExtent.width))),
static_cast<uint32_t>(std::clamp(height, static_cast<int>(capabilities.minImageExtent.height),
static_cast<int>(capabilities.maxImageExtent.height)))
};
}
void CreateBuffer(VkDevice device, VkPhysicalDevice physicalDevice, VkDeviceSize size, VkBufferUsageFlags usage,
VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
// Validate buffer size
if (size == 0) {
throw std::runtime_error("Cannot create buffer with size 0");
}
// Check available memory before allocating
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps);
uint64_t totalAvailable = 0;
for (uint32_t i = 0; i < memProps.memoryHeapCount; ++i) {
if (memProps.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) {
totalAvailable += memProps.memoryHeaps[i].size;
}
}
if (size > totalAvailable) {
throw std::runtime_error("Requested buffer size (" +
std::to_string(size / (1024 * 1024)) + " MB) exceeds available GPU memory (" +
std::to_string(totalAvailable / (1024 * 1024)) + " MB)");
}
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
bufferInfo.usage = usage;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VkResult createResult = vkCreateBuffer(device, &bufferInfo, nullptr, &buffer);
if (createResult != VK_SUCCESS) {
throw std::runtime_error("Failed to create buffer (error code: " +
std::to_string(createResult) + ", size: " +
std::to_string(size / 1024) + " KB)");
}
VkMemoryRequirements memRequirements;
vkGetBufferMemoryRequirements(device, buffer, &memRequirements);
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
try {
allocInfo.memoryTypeIndex =
FindMemoryType(physicalDevice, memRequirements.memoryTypeBits, properties);
} catch (const std::exception& e) {
vkDestroyBuffer(device, buffer, nullptr);
throw std::runtime_error(std::string("Failed to find suitable memory type: ") + e.what());
}
VkResult allocResult = vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory);
if (allocResult != VK_SUCCESS) {
vkDestroyBuffer(device, buffer, nullptr);
std::string errorMsg = "Failed to allocate buffer memory.\n";
errorMsg += "Requested: " + std::to_string(memRequirements.size / (1024 * 1024)) + " MB\n";
errorMsg += "Error code: " + std::to_string(allocResult) + "\n";
if (allocResult == VK_ERROR_OUT_OF_DEVICE_MEMORY) {
errorMsg += "\nOut of GPU memory. Try:\n";
errorMsg += "- Closing other GPU-intensive applications\n";
errorMsg += "- Reducing window resolution\n";
errorMsg += "- Upgrading GPU or system memory";
} else if (allocResult == VK_ERROR_OUT_OF_HOST_MEMORY) {
errorMsg += "\nOut of system memory. Try:\n";
errorMsg += "- Closing other applications\n";
errorMsg += "- Adding more RAM to your system";
}
throw std::runtime_error(errorMsg);
}
vkBindBufferMemory(device, buffer, bufferMemory, 0);
}
} // namespace sdl3cpp::vulkan::utils

View File

@@ -1,13 +0,0 @@
#pragma once
#include <vulkan/vulkan.h>
#include <SDL3/SDL.h>
namespace sdl3cpp::vulkan::utils {
VkExtent2D ChooseSwapExtent(VkSurfaceCapabilitiesKHR capabilities, SDL_Window* window);
void CreateBuffer(VkDevice device, VkPhysicalDevice physicalDevice, VkDeviceSize size, VkBufferUsageFlags usage,
VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
} // namespace sdl3cpp::vulkan::utils

View File

@@ -3,6 +3,7 @@
#include "../di/lifecycle.hpp"
#include "event_listener.hpp"
#include "event_types.hpp"
#include "i_event_bus.hpp"
#include <mutex>
#include <queue>
#include <unordered_map>
@@ -40,7 +41,7 @@ namespace sdl3cpp::events {
* eventBus.ProcessQueue(); // Call once per frame
* @endcode
*/
class EventBus : public di::IInitializable, public di::IShutdownable {
class EventBus : public IEventBus, public di::IInitializable, public di::IShutdownable {
public:
EventBus() = default;
~EventBus() = default;
@@ -60,7 +61,7 @@ public:
* @param type The event type to subscribe to
* @param listener The callback function to invoke
*/
void Subscribe(EventType type, EventListener listener);
void Subscribe(EventType type, EventListener listener) override;
/**
* @brief Subscribe to all event types.
@@ -70,7 +71,7 @@ public:
*
* @param listener The callback function to invoke for all events
*/
void SubscribeAll(EventListener listener);
void SubscribeAll(EventListener listener) override;
/**
* @brief Publish an event synchronously.
@@ -83,7 +84,7 @@ public:
*
* @param event The event to publish
*/
void Publish(const Event& event);
void Publish(const Event& event) override;
/**
* @brief Publish an event asynchronously.
@@ -96,7 +97,7 @@ public:
*
* @param event The event to publish
*/
void PublishAsync(const Event& event);
void PublishAsync(const Event& event) override;
/**
* @brief Process all queued asynchronous events.
@@ -106,14 +107,14 @@ public:
*
* Thread-safe: Can be called while other threads are calling PublishAsync().
*/
void ProcessQueue();
void ProcessQueue() override;
/**
* @brief Remove all event listeners.
*
* Useful for testing or resetting the event bus state.
*/
void ClearListeners();
void ClearListeners() override;
/**
* @brief Get the number of listeners for a specific event type.
@@ -121,14 +122,14 @@ public:
* @param type The event type to query
* @return The number of listeners subscribed to this event type
*/
size_t GetListenerCount(EventType type) const;
size_t GetListenerCount(EventType type) const override;
/**
* @brief Get the number of global listeners.
*
* @return The number of listeners subscribed to all events
*/
size_t GetGlobalListenerCount() const;
size_t GetGlobalListenerCount() const override;
// IInitializable interface
void Initialize() override {}

View File

@@ -0,0 +1,23 @@
#pragma once
#include "event_listener.hpp"
#include "event_types.hpp"
#include <cstddef>
namespace sdl3cpp::events {
class IEventBus {
public:
virtual ~IEventBus() = default;
virtual void Subscribe(EventType type, EventListener listener) = 0;
virtual void SubscribeAll(EventListener listener) = 0;
virtual void Publish(const Event& event) = 0;
virtual void PublishAsync(const Event& event) = 0;
virtual void ProcessQueue() = 0;
virtual void ClearListeners() = 0;
virtual size_t GetListenerCount(EventType type) const = 0;
virtual size_t GetGlobalListenerCount() const = 0;
};
} // namespace sdl3cpp::events

View File

@@ -1,7 +1,5 @@
#include "gui/gui_renderer.hpp"
#include "../core/vulkan_utils.hpp"
#include <algorithm>
#include <array>
#include <cmath>
@@ -17,7 +15,6 @@
#include "../../third_party/font8x8_basic.h"
namespace script = sdl3cpp::script;
namespace vulkan_utils = sdl3cpp::vulkan::utils;
namespace sdl3cpp::gui {
namespace {
@@ -350,12 +347,14 @@ private:
};
GuiRenderer::GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFormat swapchainFormat,
const std::filesystem::path& scriptDirectory)
const std::filesystem::path& scriptDirectory,
std::shared_ptr<services::IBufferService> bufferService)
: device_(device),
physicalDevice_(physicalDevice),
swapchainFormat_(swapchainFormat),
scriptDirectory_(scriptDirectory),
canvas_(std::make_unique<Canvas>()) {
canvas_(std::make_unique<Canvas>()),
bufferService_(std::move(bufferService)) {
}
GuiRenderer::~GuiRenderer() {
@@ -516,7 +515,10 @@ GuiRenderer::GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFor
if (size == 0) {
return;
}
vulkan_utils::CreateBuffer(device_, physicalDevice_, static_cast<VkDeviceSize>(size),
if (!bufferService_) {
throw std::runtime_error("Buffer service not available for GUI staging buffer");
}
bufferService_->CreateBuffer(static_cast<VkDeviceSize>(size),
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
stagingBuffer_, stagingMemory_);

View File

@@ -9,6 +9,7 @@
#include <vulkan/vulkan.h>
#include "script/gui_types.hpp"
#include "services/interfaces/i_buffer_service.hpp"
namespace sdl3cpp::gui {
@@ -28,7 +29,8 @@ struct ParsedSvg {
class GuiRenderer {
public:
GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFormat swapchainFormat,
const std::filesystem::path& scriptDirectory);
const std::filesystem::path& scriptDirectory,
std::shared_ptr<services::IBufferService> bufferService);
~GuiRenderer();
GuiRenderer(const GuiRenderer&) = delete;
@@ -63,6 +65,7 @@ private:
uint32_t canvasHeight_ = 0;
std::unique_ptr<Canvas> canvas_;
std::unordered_map<std::string, ParsedSvg> svgCache_;
std::shared_ptr<services::IBufferService> bufferService_;
};
} // namespace sdl3cpp::gui

View File

@@ -21,7 +21,7 @@
#include "app/service_based_app.hpp"
#include <SDL3/SDL_main.h>
#include "services/interfaces/i_logger.hpp"
#include "core/platform.hpp"
#include "services/impl/platform_service.hpp"
using namespace sdl3cpp::services;
@@ -168,7 +168,8 @@ RuntimeConfig LoadRuntimeConfigFromJson(const std::filesystem::path& configPath,
}
std::optional<std::filesystem::path> GetDefaultConfigPath() {
if (auto dir = sdl3cpp::platform::GetUserConfigDirectory()) {
sdl3cpp::services::impl::PlatformService platformService;
if (auto dir = platformService.GetUserConfigDirectory()) {
return *dir / "default_runtime.json";
}
return std::nullopt;

View File

@@ -1,7 +1,7 @@
#include "buffer_service.hpp"
#include "../../core/vulkan_utils.hpp"
#include <cstring>
#include <stdexcept>
#include <string>
namespace sdl3cpp::services::impl {
@@ -97,7 +97,94 @@ void BufferService::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
auto device = deviceService_->GetDevice();
auto physicalDevice = deviceService_->GetPhysicalDevice();
vulkan::utils::CreateBuffer(device, physicalDevice, size, usage, properties, buffer, bufferMemory);
if (logger_) {
logger_->Debug("Creating buffer with size " + std::to_string(size) + " bytes");
}
if (size == 0) {
if (logger_) {
logger_->Error("Cannot create buffer with size 0");
}
throw std::runtime_error("Cannot create buffer with size 0");
}
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps);
uint64_t totalAvailable = 0;
for (uint32_t i = 0; i < memProps.memoryHeapCount; ++i) {
if (memProps.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) {
totalAvailable += memProps.memoryHeaps[i].size;
}
}
if (size > totalAvailable) {
throw std::runtime_error("Requested buffer size (" +
std::to_string(size / (1024 * 1024)) + " MB) exceeds available GPU memory (" +
std::to_string(totalAvailable / (1024 * 1024)) + " MB)");
}
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
bufferInfo.usage = usage;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VkResult createResult = vkCreateBuffer(device, &bufferInfo, nullptr, &buffer);
if (createResult != VK_SUCCESS) {
throw std::runtime_error("Failed to create buffer (error code: " +
std::to_string(createResult) + ", size: " +
std::to_string(size / 1024) + " KB)");
}
VkMemoryRequirements memRequirements;
vkGetBufferMemoryRequirements(device, buffer, &memRequirements);
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
bool foundType = false;
for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) {
if ((memRequirements.memoryTypeBits & (1 << i)) &&
(memProps.memoryTypes[i].propertyFlags & properties) == properties) {
allocInfo.memoryTypeIndex = i;
foundType = true;
if (logger_) {
logger_->Debug("Found suitable memory type: " + std::to_string(i));
}
break;
}
}
if (!foundType) {
vkDestroyBuffer(device, buffer, nullptr);
if (logger_) {
logger_->Error("Failed to find suitable memory type");
}
throw std::runtime_error("Failed to find suitable memory type");
}
VkResult allocResult = vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory);
if (allocResult != VK_SUCCESS) {
vkDestroyBuffer(device, buffer, nullptr);
std::string errorMsg = "Failed to allocate buffer memory.\n";
errorMsg += "Requested: " + std::to_string(memRequirements.size / (1024 * 1024)) + " MB\n";
errorMsg += "Error code: " + std::to_string(allocResult) + "\n";
if (allocResult == VK_ERROR_OUT_OF_DEVICE_MEMORY) {
errorMsg += "\nOut of GPU memory. Try:\n";
errorMsg += "- Closing other GPU-intensive applications\n";
errorMsg += "- Reducing window resolution\n";
errorMsg += "- Upgrading GPU or system memory";
} else if (allocResult == VK_ERROR_OUT_OF_HOST_MEMORY) {
errorMsg += "\nOut of system memory. Try:\n";
errorMsg += "- Closing other applications\n";
errorMsg += "- Adding more RAM to your system";
}
throw std::runtime_error(errorMsg);
}
vkBindBufferMemory(device, buffer, bufferMemory, 0);
}
void BufferService::CleanupBuffers() {

View File

@@ -1,14 +1,22 @@
#include "core/platform.hpp"
#include "platform_service.hpp"
#include <cstdlib>
#include <utility>
#ifdef _WIN32
#include <windows.h>
#endif
namespace sdl3cpp::platform {
namespace sdl3cpp::services::impl {
std::optional<std::filesystem::path> GetUserConfigDirectory() {
PlatformService::PlatformService(std::shared_ptr<ILogger> logger)
: logger_(std::move(logger)) {
}
std::optional<std::filesystem::path> PlatformService::GetUserConfigDirectory() const {
if (logger_) {
logger_->Trace("PlatformService", "GetUserConfigDirectory");
}
#ifdef _WIN32
if (const char* appData = std::getenv("APPDATA")) {
return std::filesystem::path(appData) / "sdl3cpp";
@@ -25,8 +33,7 @@ std::optional<std::filesystem::path> GetUserConfigDirectory() {
}
#ifdef _WIN32
namespace {
std::string FormatWin32Error(DWORD errorCode) {
std::string PlatformService::FormatWin32Error(unsigned long errorCode) const {
if (errorCode == ERROR_SUCCESS) {
return "ERROR_SUCCESS";
}
@@ -44,7 +51,6 @@ std::string FormatWin32Error(DWORD errorCode) {
std::string message;
if (length > 0 && buffer != nullptr) {
message = std::string(buffer, length);
// Remove trailing newlines
while (!message.empty() && (message.back() == '\n' || message.back() == '\r')) {
message.pop_back();
}
@@ -54,10 +60,12 @@ std::string FormatWin32Error(DWORD errorCode) {
}
return message;
}
} // anonymous namespace
#endif
std::string GetPlatformError() {
std::string PlatformService::GetPlatformError() const {
if (logger_) {
logger_->Trace("PlatformService", "GetPlatformError");
}
#ifdef _WIN32
DWORD win32Error = ::GetLastError();
if (win32Error != ERROR_SUCCESS) {
@@ -69,4 +77,4 @@ std::string GetPlatformError() {
#endif
}
} // namespace sdl3cpp::platform
} // namespace sdl3cpp::services::impl

View File

@@ -0,0 +1,24 @@
#pragma once
#include "../interfaces/i_platform_service.hpp"
#include "../interfaces/i_logger.hpp"
#include <memory>
namespace sdl3cpp::services::impl {
class PlatformService : public IPlatformService {
public:
explicit PlatformService(std::shared_ptr<ILogger> logger = nullptr);
~PlatformService() override = default;
std::optional<std::filesystem::path> GetUserConfigDirectory() const override;
std::string GetPlatformError() const override;
private:
std::shared_ptr<ILogger> logger_;
#ifdef _WIN32
std::string FormatWin32Error(unsigned long errorCode) const;
#endif
};
} // namespace sdl3cpp::services::impl

View File

@@ -12,7 +12,7 @@ const std::unordered_map<SDL_Keycode, std::string> SdlInputService::kGuiKeyNames
{SDLK_RALT, "ralt"}
};
SdlInputService::SdlInputService(std::shared_ptr<events::EventBus> eventBus, std::shared_ptr<ILogger> logger)
SdlInputService::SdlInputService(std::shared_ptr<events::IEventBus> eventBus, std::shared_ptr<ILogger> logger)
: eventBus_(std::move(eventBus)), logger_(logger) {
// Subscribe to input events

View File

@@ -3,7 +3,7 @@
#include "../interfaces/i_input_service.hpp"
#include "../interfaces/i_gui_script_service.hpp"
#include "../interfaces/i_logger.hpp"
#include "../../events/event_bus.hpp"
#include "../../events/i_event_bus.hpp"
#include <memory>
namespace sdl3cpp::services::impl {
@@ -23,7 +23,7 @@ public:
*
* @param eventBus Event bus to subscribe to
*/
explicit SdlInputService(std::shared_ptr<events::EventBus> eventBus, std::shared_ptr<ILogger> logger);
explicit SdlInputService(std::shared_ptr<events::IEventBus> eventBus, std::shared_ptr<ILogger> logger);
// IInputService interface
void ProcessEvent(const SDL_Event& event) override;
@@ -36,7 +36,7 @@ public:
void UpdateGuiInput() override;
private:
std::shared_ptr<events::EventBus> eventBus_;
std::shared_ptr<events::IEventBus> eventBus_;
std::shared_ptr<ILogger> logger_;
InputState state_;
script::GuiInputSnapshot guiInputSnapshot_;

View File

@@ -1,6 +1,5 @@
#include "sdl_window_service.hpp"
#include "../interfaces/i_logger.hpp"
#include "../../core/platform.hpp"
#include <SDL3/SDL_vulkan.h>
#include <chrono>
#include <sstream>
@@ -10,7 +9,7 @@ namespace sdl3cpp::services::impl {
namespace {
std::string BuildSdlErrorMessage(const char* context) {
std::string BuildSdlErrorMessage(const char* context, const std::shared_ptr<IPlatformService>& platformService) {
std::ostringstream oss;
oss << context;
const char* sdlError = SDL_GetError();
@@ -20,17 +19,19 @@ std::string BuildSdlErrorMessage(const char* context) {
oss << ": (SDL_GetError returned an empty string)";
}
std::string platformError = sdl3cpp::platform::GetPlatformError();
if (!platformError.empty() && platformError != "No platform error") {
oss << " [" << platformError << "]";
if (platformService) {
std::string platformError = platformService->GetPlatformError();
if (!platformError.empty() && platformError != "No platform error") {
oss << " [" << platformError << "]";
}
}
return oss.str();
}
void ThrowSdlErrorIfFailed(bool success, const char* context) {
void ThrowSdlErrorIfFailed(bool success, const char* context, const std::shared_ptr<IPlatformService>& platformService) {
if (!success) {
throw std::runtime_error(BuildSdlErrorMessage(context));
throw std::runtime_error(BuildSdlErrorMessage(context, platformService));
}
}
@@ -45,8 +46,12 @@ void ShowErrorDialog(const char* title, const std::string& message) {
} // namespace
SdlWindowService::SdlWindowService(std::shared_ptr<ILogger> logger, std::shared_ptr<events::EventBus> eventBus)
: logger_(std::move(logger)), eventBus_(std::move(eventBus)) {
SdlWindowService::SdlWindowService(std::shared_ptr<ILogger> logger,
std::shared_ptr<IPlatformService> platformService,
std::shared_ptr<events::IEventBus> eventBus)
: logger_(std::move(logger)),
platformService_(std::move(platformService)),
eventBus_(std::move(eventBus)) {
}
SdlWindowService::~SdlWindowService() {
@@ -101,7 +106,7 @@ void SdlWindowService::CreateWindow(const WindowConfig& config) {
// Initialize SDL here if not already initialized
if (SDL_WasInit(0) == 0) {
try {
ThrowSdlErrorIfFailed(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO), "SDL_Init failed");
ThrowSdlErrorIfFailed(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO), "SDL_Init failed", platformService_);
} catch (const std::exception& e) {
ShowErrorDialog("SDL Initialization Failed",
std::string("Failed to initialize SDL subsystems.\n\nError: ") + e.what());
@@ -109,7 +114,7 @@ void SdlWindowService::CreateWindow(const WindowConfig& config) {
}
try {
ThrowSdlErrorIfFailed(SDL_Vulkan_LoadLibrary(nullptr), "SDL_Vulkan_LoadLibrary failed");
ThrowSdlErrorIfFailed(SDL_Vulkan_LoadLibrary(nullptr), "SDL_Vulkan_LoadLibrary failed", platformService_);
} catch (const std::exception& e) {
ShowErrorDialog("Vulkan Library Load Failed",
std::string("Failed to load Vulkan library. Make sure Vulkan drivers are installed.\n\nError: ") + e.what());
@@ -130,7 +135,7 @@ void SdlWindowService::CreateWindow(const WindowConfig& config) {
);
if (!window_) {
std::string errorMsg = BuildSdlErrorMessage("SDL_CreateWindow failed");
std::string errorMsg = BuildSdlErrorMessage("SDL_CreateWindow failed", platformService_);
ShowErrorDialog("Window Creation Failed",
std::string("Failed to create application window.\n\nError: ") + errorMsg);
throw std::runtime_error(errorMsg);

View File

@@ -2,8 +2,9 @@
#include "../interfaces/i_window_service.hpp"
#include "../interfaces/i_logger.hpp"
#include "../interfaces/i_platform_service.hpp"
#include "../../di/lifecycle.hpp"
#include "../../events/event_bus.hpp"
#include "../../events/i_event_bus.hpp"
#include <memory>
#include <SDL3/SDL.h>
@@ -25,7 +26,9 @@ public:
* @param logger Logger service for logging
* @param eventBus Event bus for publishing window/input events
*/
SdlWindowService(std::shared_ptr<ILogger> logger, std::shared_ptr<events::EventBus> eventBus);
SdlWindowService(std::shared_ptr<ILogger> logger,
std::shared_ptr<IPlatformService> platformService,
std::shared_ptr<events::IEventBus> eventBus);
~SdlWindowService() override;
@@ -47,7 +50,8 @@ public:
private:
std::shared_ptr<ILogger> logger_;
std::shared_ptr<events::EventBus> eventBus_;
std::shared_ptr<IPlatformService> platformService_;
std::shared_ptr<events::IEventBus> eventBus_;
SDL_Window* window_ = nullptr;
bool shouldClose_ = false;
bool initialized_ = false;

View File

@@ -5,7 +5,7 @@
namespace sdl3cpp::services::impl {
SwapchainService::SwapchainService(std::shared_ptr<IVulkanDeviceService> deviceService,
std::shared_ptr<events::EventBus> eventBus,
std::shared_ptr<events::IEventBus> eventBus,
std::shared_ptr<ILogger> logger)
: deviceService_(std::move(deviceService)), eventBus_(std::move(eventBus)), logger_(logger) {
// Subscribe to window resize events

View File

@@ -4,7 +4,7 @@
#include "../interfaces/i_vulkan_device_service.hpp"
#include "../interfaces/i_logger.hpp"
#include "../../di/lifecycle.hpp"
#include "../../events/event_bus.hpp"
#include "../../events/i_event_bus.hpp"
#include <memory>
#include <vector>
@@ -21,7 +21,7 @@ class SwapchainService : public ISwapchainService,
public di::IShutdownable {
public:
explicit SwapchainService(std::shared_ptr<IVulkanDeviceService> deviceService,
std::shared_ptr<events::EventBus> eventBus,
std::shared_ptr<events::IEventBus> eventBus,
std::shared_ptr<ILogger> logger);
~SwapchainService() override;
@@ -50,7 +50,7 @@ public:
private:
std::shared_ptr<IVulkanDeviceService> deviceService_;
std::shared_ptr<events::EventBus> eventBus_;
std::shared_ptr<events::IEventBus> eventBus_;
std::shared_ptr<ILogger> logger_;
VkSwapchainKHR swapchain_ = VK_NULL_HANDLE;

View File

@@ -4,8 +4,10 @@
namespace sdl3cpp::services::impl {
VulkanGuiService::VulkanGuiService(std::shared_ptr<ILogger> logger)
: logger_(std::move(logger)) {
VulkanGuiService::VulkanGuiService(std::shared_ptr<ILogger> logger,
std::shared_ptr<IBufferService> bufferService)
: logger_(std::move(logger)),
bufferService_(std::move(bufferService)) {
}
VulkanGuiService::~VulkanGuiService() {
@@ -24,7 +26,7 @@ void VulkanGuiService::Initialize(VkDevice device,
return;
}
renderer_ = std::make_unique<gui::GuiRenderer>(device, physicalDevice, format, resourcePath);
renderer_ = std::make_unique<gui::GuiRenderer>(device, physicalDevice, format, resourcePath, bufferService_);
initialized_ = true;
logger_->Info("GUI service initialized");

View File

@@ -1,6 +1,7 @@
#pragma once
#include "../interfaces/i_gui_service.hpp"
#include "../interfaces/i_buffer_service.hpp"
#include "../interfaces/i_logger.hpp"
#include "../../gui/gui_renderer.hpp"
#include "../../di/lifecycle.hpp"
@@ -17,7 +18,8 @@ namespace sdl3cpp::services::impl {
class VulkanGuiService : public IGuiService,
public di::IShutdownable {
public:
explicit VulkanGuiService(std::shared_ptr<ILogger> logger);
VulkanGuiService(std::shared_ptr<ILogger> logger,
std::shared_ptr<IBufferService> bufferService);
~VulkanGuiService() override;
// IGuiService interface
@@ -38,6 +40,7 @@ public:
private:
std::shared_ptr<ILogger> logger_;
std::shared_ptr<IBufferService> bufferService_;
std::unique_ptr<gui::GuiRenderer> renderer_;
bool initialized_ = false;
};

View File

@@ -0,0 +1,17 @@
#pragma once
#include <filesystem>
#include <optional>
#include <string>
namespace sdl3cpp::services {
class IPlatformService {
public:
virtual ~IPlatformService() = default;
virtual std::optional<std::filesystem::path> GetUserConfigDirectory() const = 0;
virtual std::string GetPlatformError() const = 0;
};
} // namespace sdl3cpp::services