From 8fc3b794d2cd63c872d7638550ac04835d968c98 Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Mon, 16 Feb 2026 09:29:11 +0800 Subject: [PATCH] =?UTF-8?q?refactor(engine):=20=E9=87=8D=E6=9E=84=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E7=B3=BB=E7=BB=9F=E4=B8=8E=E5=B9=B3=E5=8F=B0=E5=90=8E?= =?UTF-8?q?=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除PlatformModule和LoggerModule,改为使用E2D_MODULE宏自动注册模块 - 新增ModuleRegistry和ModuleMeta系统实现模块自发现 - 将BackendFactory从PlatformModule移至独立文件 - 添加export.h统一管理导出宏 - 更新README.md添加模块自发现流程图 - 修复SDL2Input初始化状态管理问题 - 清理不再使用的平台配置相关代码 - 示例项目改为静态链接确保模块自动注册 - 添加属性绑定系统支持运行时反射 --- Extra2D/include/extra2d/app/application.h | 397 +++++----- Extra2D/include/extra2d/audio/audio_config.h | 44 -- Extra2D/include/extra2d/config/app_config.h | 5 +- .../include/extra2d/config/config_manager.h | 14 - .../include/extra2d/config/platform_config.h | 87 --- .../extra2d/config/platform_detector.h | 210 ------ Extra2D/include/extra2d/core/export.h | 23 + Extra2D/include/extra2d/core/module.h | 4 +- Extra2D/include/extra2d/core/module_macros.h | 156 ++++ Extra2D/include/extra2d/core/module_meta.h | 146 ++++ Extra2D/include/extra2d/core/property.h | 184 +++++ Extra2D/include/extra2d/extra2d.h | 11 +- .../include/extra2d/graphics/shader_manager.h | 1 - .../include/extra2d/modules/input_module.h | 7 +- .../include/extra2d/modules/logger_module.h | 75 -- .../include/extra2d/modules/platform_module.h | 81 --- .../include/extra2d/modules/render_module.h | 7 +- .../{platform_module.h => backend_factory.h} | 12 +- Extra2D/include/extra2d/scene/node.h | 3 +- Extra2D/include/extra2d/scene/scene.h | 3 +- Extra2D/include/extra2d/scene/shape_node.h | 3 +- Extra2D/include/extra2d/utils/logger.h | 3 +- Extra2D/src/app/application.cpp | 298 ++------ Extra2D/src/config/app_config.cpp | 4 - Extra2D/src/config/config_loader_ini.cpp | 45 +- Extra2D/src/config/config_loader_json.cpp | 5 - Extra2D/src/config/config_manager.cpp | 21 +- Extra2D/src/config/platform_config.cpp | 224 ------ Extra2D/src/config/platform_detector.cpp | 678 ------------------ Extra2D/src/core/module_registry.cpp | 242 +++++++ Extra2D/src/graphics/opengl/gl_renderer.cpp | 2 +- Extra2D/src/graphics/shader_manager.cpp | 426 +++-------- Extra2D/src/modules/config_module.cpp | 3 + Extra2D/src/modules/input_module.cpp | 28 +- Extra2D/src/modules/logger_module.cpp | 47 -- Extra2D/src/modules/platform_module.cpp | 101 --- Extra2D/src/modules/render_module.cpp | 28 +- Extra2D/src/modules/window_module.cpp | 5 +- ...latform_module.cpp => backend_factory.cpp} | 14 +- .../platform/backends/sdl2/sdl2_backend.cpp | 2 +- .../src/platform/backends/sdl2/sdl2_input.cpp | 9 +- .../src/platform/backends/sdl2/sdl2_input.h | 1 + README.md | 24 + docs/module_system.md | 529 +++++++++----- examples/basic/main.cpp | 7 +- examples/hello_module/hello_module.cpp | 3 + examples/hello_module/hello_module.h | 87 +-- examples/hello_module/main.cpp | 14 +- xmake.lua | 35 +- xmake/engine.lua | 2 +- 50 files changed, 1661 insertions(+), 2699 deletions(-) delete mode 100644 Extra2D/include/extra2d/audio/audio_config.h delete mode 100644 Extra2D/include/extra2d/config/platform_config.h delete mode 100644 Extra2D/include/extra2d/config/platform_detector.h create mode 100644 Extra2D/include/extra2d/core/export.h create mode 100644 Extra2D/include/extra2d/core/module_macros.h create mode 100644 Extra2D/include/extra2d/core/module_meta.h create mode 100644 Extra2D/include/extra2d/core/property.h delete mode 100644 Extra2D/include/extra2d/modules/logger_module.h delete mode 100644 Extra2D/include/extra2d/modules/platform_module.h rename Extra2D/include/extra2d/platform/{platform_module.h => backend_factory.h} (91%) delete mode 100644 Extra2D/src/config/platform_config.cpp delete mode 100644 Extra2D/src/config/platform_detector.cpp create mode 100644 Extra2D/src/core/module_registry.cpp delete mode 100644 Extra2D/src/modules/logger_module.cpp delete mode 100644 Extra2D/src/modules/platform_module.cpp rename Extra2D/src/platform/{platform_module.cpp => backend_factory.cpp} (75%) diff --git a/Extra2D/include/extra2d/app/application.h b/Extra2D/include/extra2d/app/application.h index 0d130a3..6d4773e 100644 --- a/Extra2D/include/extra2d/app/application.h +++ b/Extra2D/include/extra2d/app/application.h @@ -1,14 +1,14 @@ #pragma once -#include -#include -#include #include #include +#include +#include +#include +#include +#include #include #include -#include -#include namespace extra2d { @@ -17,240 +17,237 @@ class RenderBackend; /** * @brief 应用程序类 - * 使用服务定位器模式管理模块依赖,支持依赖注入和测试Mock + * + * 模块现在通过 E2D_MODULE 宏自动注册,无需手动调用 use() + * Application 只负责协调初始化和生命周期管理 */ -class Application { +class E2D_API Application { public: - /** - * @brief 获取单例实例 - * @return 应用程序实例引用 - */ - static Application& get(); + /** + * @brief 获取单例实例 + * @return 应用程序实例引用 + */ + static Application &get(); - Application(const Application&) = delete; - Application& operator=(const Application&) = delete; + Application(const Application &) = delete; + Application &operator=(const Application &) = delete; - /** - * @brief 添加模块 - * @param m 模块引用 - */ - void use(Module& m); + /** + * @brief 使用默认配置初始化 + * @return 初始化成功返回 true + */ + bool init(); - /** - * @brief 批量添加模块 - * @param modules 模块指针列表 - */ - void use(std::initializer_list modules); + /** + * @brief 使用指定配置初始化 + * @param config 应用配置 + * @return 初始化成功返回 true + */ + bool init(const AppConfig &config); - /** - * @brief 使用默认配置初始化 - * @return 初始化成功返回 true - */ - bool init(); + /** + * @brief 使用配置文件初始化 + * @param configPath 配置文件路径 + * @return 初始化成功返回 true + */ + bool init(const std::string &configPath); - /** - * @brief 使用指定配置初始化 - * @param config 应用配置 - * @return 初始化成功返回 true - */ - bool init(const AppConfig& config); + /** + * @brief 关闭应用程序 + */ + void shutdown(); - /** - * @brief 使用配置文件初始化 - * @param configPath 配置文件路径 - * @return 初始化成功返回 true - */ - bool init(const std::string& configPath); + /** + * @brief 运行主循环 + */ + void run(); - /** - * @brief 关闭应用程序 - */ - void shutdown(); + /** + * @brief 请求退出 + */ + void quit(); - /** - * @brief 运行主循环 - */ - void run(); + /** + * @brief 暂停应用程序 + */ + void pause(); - /** - * @brief 请求退出 - */ - void quit(); + /** + * @brief 恢复应用程序 + */ + void resume(); - /** - * @brief 暂停应用程序 - */ - void pause(); + /** + * @brief 检查是否暂停 + * @return 暂停状态返回 true + */ + bool isPaused() const { return paused_; } - /** - * @brief 恢复应用程序 - */ - void resume(); + /** + * @brief 检查是否运行中 + * @return 运行中返回 true + */ + bool isRunning() const { return running_; } - /** - * @brief 检查是否暂停 - * @return 暂停状态返回 true - */ - bool isPaused() const { return paused_; } + /** + * @brief 获取窗口 + * @return 窗口引用 + */ + IWindow &window() { return *window_; } - /** - * @brief 检查是否运行中 - * @return 运行中返回 true - */ - bool isRunning() const { return running_; } + /** + * @brief 获取渲染器 + * @return 渲染器引用 + */ + RenderBackend &renderer(); - /** - * @brief 获取窗口 - * @return 窗口引用 - */ - IWindow& window() { return *window_; } + /** + * @brief 获取输入接口 + * @return 输入接口引用 + */ + IInput &input(); - /** - * @brief 获取渲染器 - * @return 渲染器引用 - */ - RenderBackend& renderer(); + /** + * @brief 获取场景服务 + * @return 场景服务共享指针 + */ + SharedPtr scenes(); - /** - * @brief 获取输入接口 - * @return 输入接口引用 - */ - IInput& input(); + /** + * @brief 获取计时器服务 + * @return 计时器服务共享指针 + */ + SharedPtr timers(); - /** - * @brief 获取场景服务 - * @return 场景服务共享指针 - */ - SharedPtr scenes(); + /** + * @brief 获取事件服务 + * @return 事件服务共享指针 + */ + SharedPtr events(); - /** - * @brief 获取计时器服务 - * @return 计时器服务共享指针 - */ - SharedPtr timers(); + /** + * @brief 获取相机服务 + * @return 相机服务共享指针 + */ + SharedPtr camera(); - /** - * @brief 获取事件服务 - * @return 事件服务共享指针 - */ - SharedPtr events(); + /** + * @brief 进入场景 + * @param scene 场景指针 + */ + void enterScene(Ptr scene); - /** - * @brief 获取相机服务 - * @return 相机服务共享指针 - */ - SharedPtr camera(); + /** + * @brief 获取帧间隔时间 + * @return 帧间隔时间(秒) + */ + float deltaTime() const { return deltaTime_; } - /** - * @brief 进入场景 - * @param scene 场景指针 - */ - void enterScene(Ptr scene); + /** + * @brief 获取总运行时间 + * @return 总运行时间(秒) + */ + float totalTime() const { return totalTime_; } - /** - * @brief 获取帧间隔时间 - * @return 帧间隔时间(秒) - */ - float deltaTime() const { return deltaTime_; } + /** + * @brief 获取当前帧率 + * @return 帧率 + */ + int fps() const { return currentFps_; } - /** - * @brief 获取总运行时间 - * @return 总运行时间(秒) - */ - float totalTime() const { return totalTime_; } + /** + * @brief 获取配置管理器 + * @return 配置管理器引用 + */ + ConfigManager &config(); - /** - * @brief 获取当前帧率 - * @return 帧率 - */ - int fps() const { return currentFps_; } + /** + * @brief 获取应用配置 + * @return 应用配置常量引用 + */ + const AppConfig &getConfig() const; - /** - * @brief 获取配置管理器 - * @return 配置管理器引用 - */ - ConfigManager& config(); + /** + * @brief 获取模块实例(按类型) + * @tparam T 模块类型 + * @return 模块指针,不存在返回 nullptr + */ + template T *getModule() { + return ModuleRegistry::instance().getModule(); + } - /** - * @brief 获取应用配置 - * @return 应用配置常量引用 - */ - const AppConfig& getConfig() const; + /** + * @brief 获取模块实例(按名称) + * @param name 模块名称 + * @return 模块指针,不存在返回 nullptr + */ + Module *getModule(const char *name) { + return ModuleRegistry::instance().getModule(name); + } - /** - * @brief 注册自定义服务 - * @tparam T 服务接口类型 - * @param service 服务实例 - */ - template - void registerService(SharedPtr service) { - ServiceLocator::instance().registerService(service); - } + /** + * @brief 注册自定义服务 + * @tparam T 服务接口类型 + * @param service 服务实例 + */ + template void registerService(SharedPtr service) { + ServiceLocator::instance().registerService(service); + } - /** - * @brief 获取服务 - * @tparam T 服务接口类型 - * @return 服务共享指针 - */ - template - SharedPtr getService() { - return ServiceLocator::instance().getService(); - } + /** + * @brief 获取服务 + * @tparam T 服务接口类型 + * @return 服务共享指针 + */ + template SharedPtr getService() { + return ServiceLocator::instance().getService(); + } private: - Application() = default; - ~Application(); + Application() = default; + ~Application(); - /** - * @brief 初始化核心模块 - * @return 初始化成功返回 true - */ - bool initCoreModules(); + /** + * @brief 初始化模块(从注册器创建并初始化) + * @param config 应用配置 + * @return 初始化成功返回 true + */ + bool initModules(const AppConfig &config); - /** - * @brief 设置所有模块 - */ - void setupAllModules(); + /** + * @brief 注册核心服务 + */ + void registerCoreServices(); + void registerCameraService(); - /** - * @brief 销毁所有模块 - */ - void destroyAllModules(); + /** + * @brief 主循环 + */ + void mainLoop(); - /** - * @brief 注册核心服务 - */ - void registerCoreServices(); + /** + * @brief 更新 + */ + void update(); - /** - * @brief 主循环 - */ - void mainLoop(); + /** + * @brief 渲染 + */ + void render(); - /** - * @brief 更新 - */ - void update(); + IWindow *window_ = nullptr; - /** - * @brief 渲染 - */ - void render(); + bool initialized_ = false; + bool running_ = false; + bool paused_ = false; + bool shouldQuit_ = false; - std::vector modules_; - IWindow* window_ = nullptr; - - bool initialized_ = false; - bool running_ = false; - bool paused_ = false; - bool shouldQuit_ = false; - - float deltaTime_ = 0.0f; - float totalTime_ = 0.0f; - double lastFrameTime_ = 0.0; - int frameCount_ = 0; - float fpsTimer_ = 0.0f; - int currentFps_ = 0; + float deltaTime_ = 0.0f; + float totalTime_ = 0.0f; + double lastFrameTime_ = 0.0; + int frameCount_ = 0; + float fpsTimer_ = 0.0f; + int currentFps_ = 0; }; -} +} // namespace extra2d diff --git a/Extra2D/include/extra2d/audio/audio_config.h b/Extra2D/include/extra2d/audio/audio_config.h deleted file mode 100644 index 719e97f..0000000 --- a/Extra2D/include/extra2d/audio/audio_config.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -namespace extra2d { - -/** - * @file audio_config.h - * @brief 音频模块配置 - * - * 定义音频相关的配置数据结构,由 AudioModule 管理。 - */ - -/** - * @brief 音频配置数据结构 - */ -struct AudioConfigData { - bool enabled = true; - int masterVolume = 100; - int musicVolume = 100; - int sfxVolume = 100; - int voiceVolume = 100; - int ambientVolume = 100; - int frequency = 44100; - int channels = 2; - int chunkSize = 2048; - int maxChannels = 16; - bool spatialAudio = false; - float listenerPosition[3] = {0.0f, 0.0f, 0.0f}; - - /** - * @brief 验证音量值是否有效 - * @param volume 要验证的音量值 - * @return 如果音量在0-100范围内返回 true - */ - bool isValidVolume(int volume) const { return volume >= 0 && volume <= 100; } - - /** - * @brief 将音量值转换为浮点数 - * @param volume 音量值(0-100) - * @return 浮点数音量值(0.0-1.0) - */ - float volumeToFloat(int volume) const { return static_cast(volume) / 100.0f; } -}; - -} diff --git a/Extra2D/include/extra2d/config/app_config.h b/Extra2D/include/extra2d/config/app_config.h index a7a3812..a8cbde7 100644 --- a/Extra2D/include/extra2d/config/app_config.h +++ b/Extra2D/include/extra2d/config/app_config.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -21,12 +21,11 @@ namespace extra2d { * @brief 应用配置结构体 * 仅包含应用级别的配置项,模块配置由各模块自行管理 */ -struct AppConfig { +struct E2D_API AppConfig { std::string appName = "Extra2D App"; std::string appVersion = "1.0.0"; std::string organization = ""; std::string configFile = "config.json"; - PlatformType targetPlatform = PlatformType::Auto; /** * @brief 创建默认配置 diff --git a/Extra2D/include/extra2d/config/config_manager.h b/Extra2D/include/extra2d/config/config_manager.h index 18da0c2..6b44658 100644 --- a/Extra2D/include/extra2d/config/config_manager.h +++ b/Extra2D/include/extra2d/config/config_manager.h @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -114,18 +113,6 @@ public: */ void setAppConfig(const AppConfig &config); - /** - * @brief 获取平台配置 - * @return 平台配置接口指针 - */ - PlatformConfig *platformConfig(); - - /** - * @brief 获取平台配置(常量版本) - * @return 平台配置接口常量指针 - */ - const PlatformConfig *platformConfig() const; - /** * @brief 注册配置变更回调 * @param callback 回调函数 @@ -273,7 +260,6 @@ private: void notifyChangeCallbacks(const ConfigChangeEvent &event); AppConfig m_appConfig; - UniquePtr m_platformConfig; UniquePtr m_loader; std::string m_configPath; bool m_initialized = false; diff --git a/Extra2D/include/extra2d/config/platform_config.h b/Extra2D/include/extra2d/config/platform_config.h deleted file mode 100644 index 2ac7648..0000000 --- a/Extra2D/include/extra2d/config/platform_config.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#include -#include - -namespace extra2d { - -/** - * @file platform_config.h - * @brief 平台配置接口 - * - * 平台配置只提供平台能力信息,不再直接修改应用配置。 - * 各模块通过 IModuleConfig::applyPlatformConstraints() 处理平台约束。 - */ - -/** - * @brief 平台类型枚举 - */ -enum class PlatformType { - Auto, - Windows, - Switch, - Linux, - macOS -}; - -/** - * @brief 平台能力结构 - */ -struct PlatformCapabilities { - bool supportsWindowed = true; - bool supportsFullscreen = true; - bool supportsBorderless = true; - bool supportsCursor = true; - bool supportsCursorHide = true; - bool supportsDPIAwareness = true; - bool supportsVSync = true; - bool supportsMultiMonitor = true; - bool supportsClipboard = true; - bool supportsGamepad = true; - bool supportsTouch = false; - bool supportsKeyboard = true; - bool supportsMouse = true; - bool supportsResize = true; - bool supportsHighDPI = true; - int maxTextureSize = 16384; - int preferredScreenWidth = 1920; - int preferredScreenHeight = 1080; - float defaultDPI = 96.0f; - - bool hasWindowSupport() const { return supportsWindowed || supportsFullscreen || supportsBorderless; } - bool hasInputSupport() const { return supportsKeyboard || supportsMouse || supportsGamepad || supportsTouch; } - bool isDesktop() const { return supportsKeyboard && supportsMouse && supportsWindowed; } - bool isConsole() const { return !supportsWindowed && supportsGamepad; } -}; - -/** - * @brief 平台配置抽象接口 - */ -class PlatformConfig { -public: - virtual ~PlatformConfig() = default; - - virtual PlatformType platformType() const = 0; - virtual const char* platformName() const = 0; - virtual const PlatformCapabilities& capabilities() const = 0; - - virtual int getRecommendedWidth() const = 0; - virtual int getRecommendedHeight() const = 0; - virtual bool isResolutionSupported(int width, int height) const = 0; -}; - -/** - * @brief 创建平台配置实例 - * @param type 平台类型,默认为 Auto(自动检测) - * @return 平台配置的智能指针 - */ -UniquePtr createPlatformConfig(PlatformType type = PlatformType::Auto); - -/** - * @brief 获取平台类型名称 - * @param type 平台类型枚举值 - * @return 平台名称字符串 - */ -const char* getPlatformTypeName(PlatformType type); - -} diff --git a/Extra2D/include/extra2d/config/platform_detector.h b/Extra2D/include/extra2d/config/platform_detector.h deleted file mode 100644 index 9507c29..0000000 --- a/Extra2D/include/extra2d/config/platform_detector.h +++ /dev/null @@ -1,210 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 平台检测器工具类 -// ============================================================================ -class PlatformDetector { -public: - /** - * @brief 检测当前运行平台 - * @return 当前平台的类型 - */ - static PlatformType detect(); - - /** - * @brief 获取平台名称字符串 - * @return 平台名称(如 "Windows", "Linux", "macOS", "Switch") - */ - static const char* platformName(); - - /** - * @brief 获取指定平台类型的名称 - * @param type 平台类型 - * @return 平台名称字符串 - */ - static const char* platformName(PlatformType type); - - /** - * @brief 检查当前平台是否为桌面平台 - * @return 如果是桌面平台返回 true - */ - static bool isDesktopPlatform(); - - /** - * @brief 检查当前平台是否为游戏主机平台 - * @return 如果是游戏主机平台返回 true - */ - static bool isConsolePlatform(); - - /** - * @brief 检查当前平台是否为移动平台 - * @return 如果是移动平台返回 true - */ - static bool isMobilePlatform(); - - /** - * @brief 获取当前平台的能力 - * @return 平台能力结构 - */ - static PlatformCapabilities capabilities(); - - /** - * @brief 获取指定平台的能力 - * @param type 平台类型 - * @return 平台能力结构 - */ - static PlatformCapabilities capabilities(PlatformType type); - - /** - * @brief 获取当前平台的默认配置 - * @return 平台默认的应用配置 - */ - static AppConfig platformDefaults(); - - /** - * @brief 获取指定平台的默认配置 - * @param type 平台类型 - * @return 平台默认的应用配置 - */ - static AppConfig platformDefaults(PlatformType type); - - /** - * @brief 获取当前平台的推荐分辨率 - * @param width 输出宽度 - * @param height 输出高度 - */ - static void getRecommendedResolution(int& width, int& height); - - /** - * @brief 获取当前平台的默认 DPI - * @return 默认 DPI 值 - */ - static float getDefaultDPI(); - - /** - * @brief 检查当前平台是否支持指定功能 - * @param feature 功能名称 - * @return 如果支持返回 true - */ - static bool supportsFeature(const std::string& feature); - - /** - * @brief 获取系统内存大小 - * @return 系统内存大小(MB),如果无法获取返回 0 - */ - static int getSystemMemoryMB(); - - /** - * @brief 获取 CPU 核心数 - * @return CPU 核心数 - */ - static int getCPUCoreCount(); - - /** - * @brief 检查是否支持多线程渲染 - * @return 如果支持返回 true - */ - static bool supportsMultithreadedRendering(); - - /** - * @brief 获取平台特定的配置路径 - * @param appName 应用名称 - * @return 配置文件目录路径 - */ - static std::string getConfigPath(const std::string& appName); - - /** - * @brief 获取平台特定的存档路径 - * @param appName 应用名称 - * @return 存档文件目录路径 - */ - static std::string getSavePath(const std::string& appName); - - /** - * @brief 获取平台特定的缓存路径 - * @param appName 应用名称 - * @return 缓存文件目录路径 - */ - static std::string getCachePath(const std::string& appName); - - /** - * @brief 获取平台特定的日志路径 - * @param appName 应用名称 - * @return 日志文件目录路径 - */ - static std::string getLogPath(const std::string& appName); - - /** - * @brief 获取平台特定的资源路径(Shader、纹理等) - * Switch平台使用romfs,其他平台使用相对路径 - * @param appName 应用名称 - * @return 资源目录路径 - */ - static std::string getResourcePath(const std::string& appName = ""); - - /** - * @brief 获取平台特定的Shader路径 - * @param appName 应用名称 - * @return Shader目录路径 - */ - static std::string getShaderPath(const std::string& appName = ""); - - /** - * @brief 获取平台特定的Shader缓存路径 - * Switch平台使用sdmc,其他平台使用系统缓存目录 - * @param appName 应用名称 - * @return Shader缓存目录路径 - */ - static std::string getShaderCachePath(const std::string& appName = ""); - - /** - * @brief 检查平台是否使用romfs(只读文件系统) - * @return 使用romfs返回true - */ - static bool usesRomfs(); - - /** - * @brief 检查平台是否支持热重载 - * Switch平台不支持热重载(romfs只读) - * @return 支持热重载返回true - */ - static bool supportsHotReload(); - - /** - * @brief 检查平台是否为小端字节序 - * @return 如果是小端字节序返回 true - */ - static bool isLittleEndian(); - - /** - * @brief 检查平台是否为大端字节序 - * @return 如果是大端字节序返回 true - */ - static bool isBigEndian(); - - /** - * @brief 获取平台信息摘要 - * @return 平台信息字符串 - */ - static std::string getPlatformSummary(); - -private: - static PlatformCapabilities getWindowsCapabilities(); - static PlatformCapabilities getLinuxCapabilities(); - static PlatformCapabilities getMacOSCapabilities(); - static PlatformCapabilities getSwitchCapabilities(); - - static AppConfig getWindowsDefaults(); - static AppConfig getLinuxDefaults(); - static AppConfig getMacOSDefaults(); - static AppConfig getSwitchDefaults(); -}; - -} diff --git a/Extra2D/include/extra2d/core/export.h b/Extra2D/include/extra2d/core/export.h new file mode 100644 index 0000000..79f1afa --- /dev/null +++ b/Extra2D/include/extra2d/core/export.h @@ -0,0 +1,23 @@ +#pragma once + +// 动态库导出宏 +// 静态库时 E2D_API 为空 +#ifndef E2D_BUILDING_DLL + #define E2D_API +#else + #if defined(_WIN32) || defined(__CYGWIN__) + #define E2D_API __declspec(dllexport) + #else + #define E2D_API __attribute__((visibility("default"))) + #endif +#endif + +// 模板类导出(不需要导出) +#define E2D_TEMPLATE_API + +// 内联函数导出 +#if defined(_WIN32) || defined(__CYGWIN__) + #define E2D_INLINE __forceinline +#else + #define E2D_INLINE inline +#endif diff --git a/Extra2D/include/extra2d/core/module.h b/Extra2D/include/extra2d/core/module.h index 1453943..31f46de 100644 --- a/Extra2D/include/extra2d/core/module.h +++ b/Extra2D/include/extra2d/core/module.h @@ -1,6 +1,8 @@ #pragma once +#include #include +#include #include namespace extra2d { @@ -14,7 +16,7 @@ class EventContext; * @brief 模块上下文基类 * 用于遍历模块链,支持链式调用 */ -class ModuleContext { +class E2D_API ModuleContext { public: /** * @brief 析构函数 diff --git a/Extra2D/include/extra2d/core/module_macros.h b/Extra2D/include/extra2d/core/module_macros.h new file mode 100644 index 0000000..dbb3dc0 --- /dev/null +++ b/Extra2D/include/extra2d/core/module_macros.h @@ -0,0 +1,156 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace extra2d { + +/** + * @brief 模块元数据模板实现 + */ +template +class ModuleMeta : public ModuleMetaBase { +public: + using ModuleType = T; + + const char* name_ = nullptr; + int priority_ = 0; + std::vector dependencies_; + std::function bindFunc_; + + const char* getName() const override { return name_; } + int getPriority() const override { return priority_; } + std::vector getDependencies() const override { return dependencies_; } + + T* create() override { + return new T(); + } + + void bindProperties(Module* instance, PropertyBinder& binder) override { + if (bindFunc_) { + bindFunc_(instance, binder); + } + } +}; + +namespace detail { + +/** + * @brief 模块注册辅助类(静态自动注册) + */ +template +struct ModuleAutoRegister { + ModuleMeta meta; + + ModuleAutoRegister(const char* name, int priority, std::initializer_list deps) { + meta.name_ = name; + meta.priority_ = priority; + meta.dependencies_ = std::vector(deps); + ModuleRegistry::instance().registerMeta(&meta); + } +}; + +} // namespace detail + +} // namespace extra2d + +// ============================================================================ +// 模块定义宏 - 静态自动注册 +// ============================================================================ + +/** + * @brief 简化版模块定义(静态自动注册) + * + * 用于引擎内部模块,在动态库加载时自动注册 + */ +#define E2D_MODULE(ModuleClassName, priorityValue, ...) \ + __attribute__((used)) \ + static ::extra2d::detail::ModuleAutoRegister< ::extra2d::ModuleClassName> \ + E2D_CONCAT(_e2d_auto_reg_, ModuleClassName)( \ + #ModuleClassName, priorityValue, { __VA_ARGS__ }); + +/** + * @brief 外部模块定义(自动生成 force_link 函数) + * + * 用于编译为单独 DLL 的自定义模块 + * + * 使用示例(在模块 cpp 文件末尾): + * } // namespace extra2d + * E2D_MODULE_EXPORT(HelloModule, 1000) + */ +#define E2D_MODULE_EXPORT(ModuleClassName, priorityValue, ...) \ + E2D_MODULE(ModuleClassName, priorityValue, __VA_ARGS__) \ + extern "C" E2D_API void E2D_CONCAT(e2d_force_link_, ModuleClassName)() {} + +/** + * @brief 声明外部模块的 force_link 函数 + */ +#define E2D_DECLARE_FORCE_LINK(ModuleClassName) \ + extern "C" void E2D_CONCAT(e2d_force_link_, ModuleClassName)() + +/** + * @brief 调用 force_link 函数 + */ +#define E2D_CALL_FORCE_LINK(ModuleClassName) \ + E2D_CONCAT(e2d_force_link_, ModuleClassName)() + +/** + * @brief 强制链接外部模块(声明 + 调用) + * + * 在 main.cpp 开头调用,触发 DLL 静态初始化 + */ +#define E2D_FORCE_LINK(ModuleClassName) \ + E2D_DECLARE_FORCE_LINK(ModuleClassName); \ + E2D_CALL_FORCE_LINK(ModuleClassName) + +/** + * @brief 带属性的模块定义开始 + */ +#define E2D_MODULE_BEGIN(ModuleClassName) \ + namespace { \ + static ::extra2d::ModuleMeta< ::extra2d::ModuleClassName>& E2D_CONCAT(_e2d_get_meta_, ModuleClassName)() { \ + static ::extra2d::ModuleMeta< ::extra2d::ModuleClassName> meta; \ + return meta; \ + } \ + struct E2D_CONCAT(_E2D_ModuleCfg_, ModuleClassName) { \ + E2D_CONCAT(_E2D_ModuleCfg_, ModuleClassName)() + +/** + * @brief 定义模块优先级 + */ +#define E2D_PRIORITY(value) \ + { auto& m = E2D_CONCAT(_e2d_get_meta_, ModuleClassName)(); m.priority_ = value; } + +/** + * @brief 定义模块依赖 + */ +#define E2D_DEPENDENCIES(...) \ + { \ + auto& m = E2D_CONCAT(_e2d_get_meta_, ModuleClassName)(); \ + m.dependencies_ = { __VA_ARGS__ }; \ + } + +/** + * @brief 定义属性 + */ +#define E2D_PROPERTY(name, type) \ + { \ + auto& m = E2D_CONCAT(_e2d_get_meta_, ModuleClassName)(); \ + auto oldFunc = m.bindFunc_; \ + m.bindFunc_ = [oldFunc](::extra2d::Module* inst, ::extra2d::PropertyBinder& binder) { \ + if (oldFunc) oldFunc(inst, binder); \ + auto* module = static_cast< ::extra2d::ModuleClassName*>(inst); \ + binder.bind(#name, module->name, #name, ""); \ + }; \ + } + +/** + * @brief 结束模块定义 + */ +#define E2D_MODULE_END() \ + } E2D_CONCAT(_e2d_cfg_inst_, ModuleClassName); \ + } diff --git a/Extra2D/include/extra2d/core/module_meta.h b/Extra2D/include/extra2d/core/module_meta.h new file mode 100644 index 0000000..3088166 --- /dev/null +++ b/Extra2D/include/extra2d/core/module_meta.h @@ -0,0 +1,146 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +class Module; + +/** + * @brief 模块元数据基类 + * + * 提供模块的类型信息、依赖关系和创建工厂 + */ +struct E2D_API ModuleMetaBase { + virtual ~ModuleMetaBase() = default; + + /** + * @brief 获取模块名称 + */ + virtual const char* getName() const = 0; + + /** + * @brief 获取模块优先级 + */ + virtual int getPriority() const { return 0; } + + /** + * @brief 获取模块依赖 + */ + virtual std::vector getDependencies() const { return {}; } + + /** + * @brief 创建模块实例 + */ + virtual Module* create() = 0; + + /** + * @brief 绑定模块属性 + */ + virtual void bindProperties(Module* instance, PropertyBinder& binder) = 0; +}; + +/** + * @brief 模块注册器(自动发现) + * + * 单例类,管理所有模块的注册、创建和生命周期 + */ +class E2D_API ModuleRegistry { +public: + /** + * @brief 获取单例实例 + */ + static ModuleRegistry& instance(); + + /** + * @brief 注册模块元数据 + * @param meta 模块元数据指针 + */ + void registerMeta(ModuleMetaBase* meta); + + /** + * @brief 获取所有已注册模块元数据 + */ + std::vector getAllMetas() const; + + /** + * @brief 按名称获取模块元数据 + */ + ModuleMetaBase* getMeta(const char* name) const; + + /** + * @brief 创建并初始化所有模块 + * @return 成功返回 true + */ + bool createAndInitAll(); + + /** + * @brief 销毁所有模块 + */ + void destroyAll(); + + /** + * @brief 获取模块实例(按类型) + */ + template + T* getModule() const { + for (const auto& [name, ptr] : instanceMap_) { + if (auto* derived = dynamic_cast(ptr)) { + return derived; + } + } + return nullptr; + } + + /** + * @brief 获取模块实例(按名称) + */ + Module* getModule(const char* name) const; + + /** + * @brief 检查模块是否存在 + */ + bool hasModule(const char* name) const; + + /** + * @brief 获取所有模块实例 + */ + std::vector getAllModules() const; + + /** + * @brief 检查是否已初始化 + */ + bool isInitialized() const { return initialized_; } + +private: + ModuleRegistry() = default; + ~ModuleRegistry() = default; + ModuleRegistry(const ModuleRegistry&) = delete; + ModuleRegistry& operator=(const ModuleRegistry&) = delete; + + /** + * @brief 按依赖关系拓扑排序 + */ + std::vector sortByDependency(); + + /** + * @brief 检测循环依赖 + */ + bool hasCircularDependency() const; + + std::vector metas_; + std::vector> instances_; + std::unordered_map instanceMap_; + bool initialized_ = false; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/core/property.h b/Extra2D/include/extra2d/core/property.h new file mode 100644 index 0000000..f4e2bc0 --- /dev/null +++ b/Extra2D/include/extra2d/core/property.h @@ -0,0 +1,184 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 属性值类型 + * + * 支持多种基础类型的运行时存储和查询 + */ +using PropertyValue = std::variant< + std::monostate, + bool, + int, + float, + double, + std::string, + Vec2, + Vec3, + Color, + Rect, + Size +>; + +/** + * @brief 属性元数据 + * + * 包含属性的描述信息,用于编辑器和序列化 + */ +struct PropertyMeta { + const char* name = nullptr; + const char* displayName = nullptr; + const char* description = nullptr; + PropertyValue defaultValue{}; + bool editable = true; + bool serializable = true; + + PropertyMeta() = default; + + PropertyMeta(const char* n, const char* dn, const char* desc, + PropertyValue def, bool edit = true, bool ser = true) + : name(n), displayName(dn), description(desc), + defaultValue(def), editable(edit), serializable(ser) {} +}; + +/** + * @brief 属性访问器 + * + * 用于运行时读写属性值 + */ +struct PropertyAccessor { + std::function getter; + std::function setter; + PropertyMeta meta; +}; + +/** + * @brief 属性绑定器基类 + * + * 提供属性的绑定、查询和修改接口 + */ +class PropertyBinder { +public: + virtual ~PropertyBinder() = default; + + /** + * @brief 获取所有属性元数据 + * @return 属性元数据列表 + */ + virtual std::vector getProperties() const = 0; + + /** + * @brief 获取属性值 + * @param name 属性名 + * @return 属性值 + */ + virtual PropertyValue getProperty(const char* name) const = 0; + + /** + * @brief 设置属性值 + * @param name 属性名 + * @param value 属性值 + * @return 设置成功返回 true + */ + virtual bool setProperty(const char* name, const PropertyValue& value) = 0; + + /** + * @brief 检查是否有指定属性 + * @param name 属性名 + * @return 存在返回 true + */ + virtual bool hasProperty(const char* name) const = 0; + + /** + * @brief 获取属性元数据 + * @param name 属性名 + * @return 属性元数据指针,不存在返回 nullptr + */ + virtual const PropertyMeta* getPropertyMeta(const char* name) const = 0; +}; + +/** + * @brief 属性绑定器实现 + * + * 使用函数指针实现属性的运行时访问 + */ +class PropertyBinderImpl : public PropertyBinder { +public: + /** + * @brief 绑定属性 + * @tparam T 属性类型 + * @param name 属性名 + * @param value 属性引用 + * @param displayName 显示名 + * @param description 描述 + */ + template + void bind(const char* name, T& value, + const char* displayName = nullptr, + const char* description = nullptr) { + PropertyAccessor accessor; + accessor.meta = PropertyMeta(name, displayName ? displayName : name, + description ? description : "", T{}); + accessor.getter = [&value]() -> PropertyValue { return value; }; + accessor.setter = [&value](const PropertyValue& v) -> bool { + if (auto* ptr = std::get_if(&v)) { + value = *ptr; + return true; + } + return false; + }; + accessors_[name] = std::move(accessor); + } + + std::vector getProperties() const override { + std::vector result; + result.reserve(accessors_.size()); + for (const auto& [name, accessor] : accessors_) { + result.push_back(accessor.meta); + } + return result; + } + + PropertyValue getProperty(const char* name) const override { + auto it = accessors_.find(name); + if (it != accessors_.end() && it->second.getter) { + return it->second.getter(); + } + return {}; + } + + bool setProperty(const char* name, const PropertyValue& value) override { + auto it = accessors_.find(name); + if (it != accessors_.end() && it->second.setter) { + return it->second.setter(value); + } + return false; + } + + bool hasProperty(const char* name) const override { + return accessors_.find(name) != accessors_.end(); + } + + const PropertyMeta* getPropertyMeta(const char* name) const override { + auto it = accessors_.find(name); + if (it != accessors_.end()) { + return &it->second.meta; + } + return nullptr; + } + +private: + std::unordered_map accessors_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/extra2d.h b/Extra2D/include/extra2d/extra2d.h index c9e2342..37031b2 100644 --- a/Extra2D/include/extra2d/extra2d.h +++ b/Extra2D/include/extra2d/extra2d.h @@ -6,23 +6,20 @@ // Core #include #include -#include #include +#include +#include // Config #include #include #include -#include -#include // Modules #include -#include -#include -#include #include #include +#include // Platform #include @@ -61,10 +58,10 @@ #include // Services +#include #include #include #include -#include // Application #include diff --git a/Extra2D/include/extra2d/graphics/shader_manager.h b/Extra2D/include/extra2d/graphics/shader_manager.h index ce34937..b7a5761 100644 --- a/Extra2D/include/extra2d/graphics/shader_manager.h +++ b/Extra2D/include/extra2d/graphics/shader_manager.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include diff --git a/Extra2D/include/extra2d/modules/input_module.h b/Extra2D/include/extra2d/modules/input_module.h index 26c3456..30864f2 100644 --- a/Extra2D/include/extra2d/modules/input_module.h +++ b/Extra2D/include/extra2d/modules/input_module.h @@ -67,9 +67,14 @@ public: * @brief 设置窗口 * @param window 窗口接口指针 */ - void setWindow(IWindow* window) { window_ = window; } + void setWindow(IWindow* window); private: + /** + * @brief 使用窗口初始化输入 + */ + void initializeWithWindow(); + IWindow* window_ = nullptr; IInput* input_ = nullptr; InputConfigData config_; diff --git a/Extra2D/include/extra2d/modules/logger_module.h b/Extra2D/include/extra2d/modules/logger_module.h deleted file mode 100644 index ba9533d..0000000 --- a/Extra2D/include/extra2d/modules/logger_module.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace extra2d { - -/** - * @brief 日志模块 - * 管理日志系统的初始化和配置 - */ -class LoggerModule : public Module { -public: - /** - * @brief 构造函数 - */ - LoggerModule(); - - /** - * @brief 析构函数 - */ - ~LoggerModule() override; - - /** - * @brief 获取模块名称 - * @return 模块名称 - */ - const char* getName() const override { return "Logger"; } - - /** - * @brief 获取模块优先级 - * @return 优先级(核心模块,最先初始化) - */ - int getPriority() const override { return -1; } - - /** - * @brief 设置模块 - */ - void setupModule() override; - - /** - * @brief 销毁模块 - */ - void destroyModule() override; - - /** - * @brief 设置日志级别 - * @param level 日志级别 - */ - void setLogLevel(LogLevel level) { logLevel_ = level; } - - /** - * @brief 设置控制台输出 - * @param enabled 是否启用 - */ - void setConsoleOutput(bool enabled) { consoleOutput_ = enabled; } - - /** - * @brief 设置文件输出 - * @param filePath 日志文件路径 - */ - void setFileOutput(const std::string& filePath) { - fileOutput_ = true; - logFilePath_ = filePath; - } - -private: - LogLevel logLevel_ = LogLevel::Info; - bool consoleOutput_ = true; - bool fileOutput_ = false; - std::string logFilePath_; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/modules/platform_module.h b/Extra2D/include/extra2d/modules/platform_module.h deleted file mode 100644 index bbf2025..0000000 --- a/Extra2D/include/extra2d/modules/platform_module.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -#include -#include - -namespace extra2d { - -/** - * @brief 平台模块 - * 管理平台相关的初始化和配置 - */ -class PlatformModule : public Module { -public: - /** - * @brief 构造函数 - */ - PlatformModule(); - - /** - * @brief 析构函数 - */ - ~PlatformModule() override; - - /** - * @brief 获取模块名称 - * @return 模块名称 - */ - const char* getName() const override { return "Platform"; } - - /** - * @brief 获取模块优先级 - * @return 优先级 - */ - int getPriority() const override { return 10; } - - /** - * @brief 设置模块 - */ - void setupModule() override; - - /** - * @brief 销毁模块 - */ - void destroyModule() override; - - /** - * @brief 设置目标平台 - * @param platform 目标平台类型 - */ - void setTargetPlatform(PlatformType platform) { targetPlatform_ = platform; } - - /** - * @brief 获取当前平台 - * @return 当前平台类型 - */ - PlatformType getPlatform() const { return resolvedPlatform_; } - - /** - * @brief 获取平台配置 - * @return 平台配置指针 - */ - PlatformConfig* getPlatformConfig() const { return platformConfig_.get(); } - -private: - /** - * @brief 初始化 Switch 平台 - * @return 初始化成功返回 true - */ - bool initSwitch(); - - /** - * @brief 关闭 Switch 平台 - */ - void shutdownSwitch(); - - PlatformType targetPlatform_ = PlatformType::Auto; - PlatformType resolvedPlatform_ = PlatformType::Windows; - UniquePtr platformConfig_; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/modules/render_module.h b/Extra2D/include/extra2d/modules/render_module.h index 575412d..07be5ff 100644 --- a/Extra2D/include/extra2d/modules/render_module.h +++ b/Extra2D/include/extra2d/modules/render_module.h @@ -99,7 +99,7 @@ public: * @brief 设置窗口 * @param window 窗口接口指针 */ - void setWindow(IWindow* window) { window_ = window; } + void setWindow(IWindow* window); /** * @brief 获取渲染器 @@ -108,6 +108,11 @@ public: RenderBackend* getRenderer() const { return renderer_.get(); } private: + /** + * @brief 使用窗口初始化渲染器 + */ + void initializeWithWindow(); + IWindow* window_ = nullptr; UniquePtr renderer_; RenderModuleConfig config_; diff --git a/Extra2D/include/extra2d/platform/platform_module.h b/Extra2D/include/extra2d/platform/backend_factory.h similarity index 91% rename from Extra2D/include/extra2d/platform/platform_module.h rename to Extra2D/include/extra2d/platform/backend_factory.h index faeb144..0716de8 100644 --- a/Extra2D/include/extra2d/platform/platform_module.h +++ b/Extra2D/include/extra2d/platform/backend_factory.h @@ -10,16 +10,6 @@ namespace extra2d { -/** - * @brief 平台模块配置 - */ -struct PlatformModuleConfig { - std::string backend = "sdl2"; - bool gamepad = true; - bool touch = true; - float deadzone = 0.15f; -}; - /** * @brief 平台后端工厂 * 用于注册和创建平台后端 @@ -95,4 +85,4 @@ private: } e2d_backend_reg_##name; \ } -} // namespace extra2d +} diff --git a/Extra2D/include/extra2d/scene/node.h b/Extra2D/include/extra2d/scene/node.h index 702106a..10433c6 100644 --- a/Extra2D/include/extra2d/scene/node.h +++ b/Extra2D/include/extra2d/scene/node.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -19,7 +20,7 @@ struct RenderCommand; // ============================================================================ // 节点基类 - 场景图的基础 // ============================================================================ -class Node : public std::enable_shared_from_this { +class E2D_API Node : public std::enable_shared_from_this { public: Node(); virtual ~Node(); diff --git a/Extra2D/include/extra2d/scene/scene.h b/Extra2D/include/extra2d/scene/scene.h index 89b8b65..04c3a70 100644 --- a/Extra2D/include/extra2d/scene/scene.h +++ b/Extra2D/include/extra2d/scene/scene.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -13,7 +14,7 @@ struct RenderCommand; // ============================================================================ // 场景类 - 节点容器,管理整个场景图 // ============================================================================ -class Scene : public Node { +class E2D_API Scene : public Node { public: Scene(); ~Scene() override = default; diff --git a/Extra2D/include/extra2d/scene/shape_node.h b/Extra2D/include/extra2d/scene/shape_node.h index e0af481..ee1a6ba 100644 --- a/Extra2D/include/extra2d/scene/shape_node.h +++ b/Extra2D/include/extra2d/scene/shape_node.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -15,7 +16,7 @@ enum class ShapeType { Point, Line, Rect, Circle, Triangle, Polygon }; // ============================================================================ // 形状节点 - 用于绘制几何形状 // ============================================================================ -class ShapeNode : public Node { +class E2D_API ShapeNode : public Node { public: ShapeNode(); ~ShapeNode() override = default; diff --git a/Extra2D/include/extra2d/utils/logger.h b/Extra2D/include/extra2d/utils/logger.h index 43a91f4..cff4a7e 100644 --- a/Extra2D/include/extra2d/utils/logger.h +++ b/Extra2D/include/extra2d/utils/logger.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -128,7 +129,7 @@ inline std::string e2d_format(const char *fmt, const Args &...args) { inline std::string e2d_format(const char *fmt) { return std::string(fmt); } -class Logger { +class E2D_API Logger { public: static void init(); static void shutdown(); diff --git a/Extra2D/src/app/application.cpp b/Extra2D/src/app/application.cpp index 7bc771c..8b93f1b 100644 --- a/Extra2D/src/app/application.cpp +++ b/Extra2D/src/app/application.cpp @@ -1,8 +1,8 @@ #include +#include #include #include #include -#include #include #include #include @@ -11,9 +11,7 @@ #include #include -#include #include -#include namespace extra2d { @@ -36,23 +34,6 @@ Application &Application::get() { return instance; } -void Application::use(Module &m) { - for (auto *existing : modules_) { - if (existing == &m) { - return; - } - } - modules_.push_back(&m); -} - -void Application::use(std::initializer_list modules) { - for (auto *m : modules) { - if (m) { - use(*m); - } - } -} - bool Application::init() { AppConfig cfg; return init(cfg); @@ -63,49 +44,14 @@ bool Application::init(const AppConfig &config) { return true; } - if (!initCoreModules()) { + // 先注册核心服务,因为模块初始化时可能需要它们 + registerCoreServices(); + + if (!initModules(config)) { return false; } - ConfigModule *configModule = nullptr; - for (auto *m : modules_) { - if (auto *cm = dynamic_cast(m)) { - configModule = cm; - break; - } - } - - if (configModule) { - configModule->setAppConfig(config); - } - - std::sort(modules_.begin(), modules_.end(), modulePriorityCompare); - - WindowModule *windowModule = nullptr; - InputModule *inputModule = nullptr; - RenderModule *renderModule = nullptr; - - for (auto *m : modules_) { - if (auto *wm = dynamic_cast(m)) { - windowModule = wm; - } - if (auto *im = dynamic_cast(m)) { - inputModule = im; - } - if (auto *rm = dynamic_cast(m)) { - renderModule = rm; - } - } - - for (auto *m : modules_) { - if (m == inputModule || m == renderModule) { - continue; - } - if (!m->isInitialized()) { - m->setupModule(); - } - } - + auto windowModule = getModule(); if (!windowModule || !windowModule->isInitialized()) { E2D_LOG_ERROR("Window module not initialized"); return false; @@ -117,18 +63,19 @@ bool Application::init(const AppConfig &config) { return false; } - registerCoreServices(); - + auto inputModule = getModule(); if (inputModule) { inputModule->setWindow(window_); - inputModule->setupModule(); } + auto renderModule = getModule(); if (renderModule) { renderModule->setWindow(window_); - renderModule->setupModule(); } + // 注册 CameraService(需要 window) + registerCameraService(); + if (!ServiceLocator::instance().initializeAll()) { return false; } @@ -162,168 +109,29 @@ bool Application::init(const std::string &configPath) { return true; } - if (!initCoreModules()) { - return false; - } - - ConfigModule *configModule = nullptr; - for (auto *m : modules_) { - if (auto *cm = dynamic_cast(m)) { - configModule = cm; - break; - } - } - + auto configModule = getModule(); if (configModule) { configModule->setConfigPath(configPath); } - std::sort(modules_.begin(), modules_.end(), modulePriorityCompare); - - WindowModule *windowModule = nullptr; - InputModule *inputModule = nullptr; - RenderModule *renderModule = nullptr; - - for (auto *m : modules_) { - if (auto *wm = dynamic_cast(m)) { - windowModule = wm; - } - if (auto *im = dynamic_cast(m)) { - inputModule = im; - } - if (auto *rm = dynamic_cast(m)) { - renderModule = rm; - } - } - - for (auto *m : modules_) { - if (m == inputModule || m == renderModule) { - continue; - } - if (!m->isInitialized()) { - m->setupModule(); - } - } - - if (!windowModule || !windowModule->isInitialized()) { - E2D_LOG_ERROR("Window module not initialized"); - return false; - } - - window_ = windowModule->getWindow(); - if (!window_) { - E2D_LOG_ERROR("Window not created"); - return false; - } - - registerCoreServices(); - - if (inputModule) { - inputModule->setWindow(window_); - inputModule->setupModule(); - } - - if (renderModule) { - renderModule->setWindow(window_); - renderModule->setupModule(); - } - - if (!ServiceLocator::instance().initializeAll()) { - return false; - } - - auto cameraService = ServiceLocator::instance().getService(); - if (cameraService && window_) { - window_->onResize([cameraService](int width, int height) { - cameraService->updateViewport(width, height); - cameraService->applyViewportAdapter(); - - auto sceneService = - ServiceLocator::instance().getService(); - if (sceneService) { - auto currentScene = sceneService->getCurrentScene(); - if (currentScene) { - currentScene->setViewportSize(static_cast(width), - static_cast(height)); - } - } - }); - } - - initialized_ = true; - running_ = true; - - return true; + AppConfig cfg; + return init(cfg); } -bool Application::initCoreModules() { - bool hasConfig = false; - bool hasPlatform = false; - bool hasWindow = false; - bool hasInput = false; - bool hasRender = false; - - for (auto *m : modules_) { - if (dynamic_cast(m)) - hasConfig = true; - if (dynamic_cast(m)) - hasPlatform = true; - if (dynamic_cast(m)) - hasWindow = true; - if (dynamic_cast(m)) - hasInput = true; - if (dynamic_cast(m)) - hasRender = true; +bool Application::initModules(const AppConfig &config) { + auto configModule = getModule(); + if (configModule) { + configModule->setAppConfig(config); } - if (!hasConfig) { - static ConfigModule defaultConfigModule; - use(defaultConfigModule); - } - - if (!hasPlatform) { - static PlatformModule defaultPlatformModule; - use(defaultPlatformModule); - } - - if (!hasWindow) { - static WindowModule defaultWindowModule; - use(defaultWindowModule); - } - - if (!hasInput) { - static InputModule defaultInputModule; - use(defaultInputModule); - } - - if (!hasRender) { - static RenderModule defaultRenderModule; - use(defaultRenderModule); + if (!ModuleRegistry::instance().createAndInitAll()) { + E2D_LOG_ERROR("Failed to initialize modules"); + return false; } return true; } -void Application::setupAllModules() { - std::sort(modules_.begin(), modules_.end(), modulePriorityCompare); - - for (auto *m : modules_) { - if (!m->isInitialized()) { - m->setupModule(); - } - } -} - -void Application::destroyAllModules() { - for (auto it = modules_.rbegin(); it != modules_.rend(); ++it) { - Module *m = *it; - if (m->isInitialized()) { - m->destroyModule(); - } - } - modules_.clear(); -} - void Application::registerCoreServices() { auto &locator = ServiceLocator::instance(); @@ -338,6 +146,10 @@ void Application::registerCoreServices() { if (!locator.hasService()) { locator.registerService(makeShared()); } +} + +void Application::registerCameraService() { + auto &locator = ServiceLocator::instance(); if (!locator.hasService()) { auto cameraService = makeShared(); @@ -359,16 +171,20 @@ void Application::shutdown() { if (!initialized_) return; + E2D_LOG_INFO("Application shutting down..."); + VRAMMgr::get().printStats(); + // 先销毁模块(它们可能需要服务和窗口) + ModuleRegistry::instance().destroyAll(); + + // 最后清除服务 ServiceLocator::instance().clear(); - window_ = nullptr; - - destroyAllModules(); - initialized_ = false; running_ = false; + + E2D_LOG_INFO("Application shutdown complete"); } Application::~Application() { @@ -387,6 +203,9 @@ void Application::run() { while (running_ && !window_->shouldClose()) { mainLoop(); } + + // 主循环结束后自动清理 + shutdown(); } void Application::quit() { @@ -437,14 +256,7 @@ void Application::mainLoop() { render(); - RenderModule *renderModule = nullptr; - for (auto *m : modules_) { - if (auto *rm = dynamic_cast(m)) { - renderModule = rm; - break; - } - } - + auto renderModule = getModule(); (void)renderModule; ConfigManager::instance().update(deltaTime_); @@ -453,19 +265,18 @@ void Application::mainLoop() { void Application::update() { ServiceLocator::instance().updateAll(deltaTime_); - auto ctx = UpdateContext(modules_, deltaTime_); - if (!modules_.empty()) { + auto modules = ModuleRegistry::instance().getAllModules(); + auto ctx = UpdateContext(modules, deltaTime_); + if (!modules.empty()) { ctx.next(); } } void Application::render() { RenderBackend *renderer = nullptr; - for (auto *m : modules_) { - if (auto *rm = dynamic_cast(m)) { - renderer = rm->getRenderer(); - break; - } + auto renderModule = getModule(); + if (renderModule) { + renderer = renderModule->getRenderer(); } if (!renderer) { @@ -484,16 +295,18 @@ void Application::render() { renderer->setViewport(0, 0, window_->width(), window_->height()); } + auto modules = ModuleRegistry::instance().getAllModules(); + { - auto ctx = RenderContext(modules_, RenderContext::Phase::Before); - if (!modules_.empty()) { + auto ctx = RenderContext(modules, RenderContext::Phase::Before); + if (!modules.empty()) { ctx.next(); } } { - auto ctx = RenderContext(modules_, RenderContext::Phase::On); - if (!modules_.empty()) { + auto ctx = RenderContext(modules, RenderContext::Phase::On); + if (!modules.empty()) { ctx.next(); } } @@ -504,8 +317,8 @@ void Application::render() { } { - auto ctx = RenderContext(modules_, RenderContext::Phase::After); - if (!modules_.empty()) { + auto ctx = RenderContext(modules, RenderContext::Phase::After); + if (!modules.empty()) { ctx.next(); } } @@ -514,12 +327,9 @@ void Application::render() { IInput &Application::input() { return *window_->input(); } RenderBackend &Application::renderer() { - for (auto *m : modules_) { - if (auto *rm = dynamic_cast(m)) { - if (rm->getRenderer()) { - return *rm->getRenderer(); - } - } + auto renderModule = getModule(); + if (renderModule && renderModule->getRenderer()) { + return *renderModule->getRenderer(); } static RenderBackend *dummy = nullptr; if (!dummy) { diff --git a/Extra2D/src/config/app_config.cpp b/Extra2D/src/config/app_config.cpp index 9531559..7e7d385 100644 --- a/Extra2D/src/config/app_config.cpp +++ b/Extra2D/src/config/app_config.cpp @@ -10,7 +10,6 @@ AppConfig AppConfig::createDefault() { config.appVersion = "1.0.0"; config.organization = ""; config.configFile = "config.json"; - config.targetPlatform = PlatformType::Auto; return config; } @@ -50,9 +49,6 @@ void AppConfig::merge(const AppConfig& other) { if (other.configFile != "config.json") { configFile = other.configFile; } - if (other.targetPlatform != PlatformType::Auto) { - targetPlatform = other.targetPlatform; - } E2D_LOG_INFO("Merged app config"); } diff --git a/Extra2D/src/config/config_loader_ini.cpp b/Extra2D/src/config/config_loader_ini.cpp index 10a0b7d..ae0942b 100644 --- a/Extra2D/src/config/config_loader_ini.cpp +++ b/Extra2D/src/config/config_loader_ini.cpp @@ -9,11 +9,6 @@ namespace extra2d { -/** - * @brief 去除字符串首尾空白字符 - * @param str 输入字符串 - * @return 去除空白后的字符串 - */ static std::string trim(const std::string &str) { size_t start = 0; while (start < str.length() && @@ -28,11 +23,6 @@ static std::string trim(const std::string &str) { return str.substr(start, end - start); } -/** - * @brief 将字符串转换为小写 - * @param str 输入字符串 - * @return 小写字符串 - */ static std::string toLower(const std::string &str) { std::string result = str; for (char &c : result) { @@ -41,17 +31,8 @@ static std::string toLower(const std::string &str) { return result; } -/** - * @brief INI 数据存储结构 - */ using IniData = std::map>; -/** - * @brief 解析 INI 内容到数据结构 - * @param content INI 内容字符串 - * @param data 输出的 INI 数据 - * @return 解析结果 - */ static ConfigLoadResult parseIniContent(const std::string &content, IniData &data) { std::istringstream stream(content); @@ -97,14 +78,6 @@ static ConfigLoadResult parseIniContent(const std::string &content, return ConfigLoadResult::ok(); } -/** - * @brief 获取 INI 值(带默认值) - * @param data INI 数据 - * @param section 节名 - * @param key 键名 - * @param defaultValue 默认值 - * @return 值字符串 - */ static std::string getIniValue(const IniData &data, const std::string §ion, const std::string &key, const std::string &defaultValue = "") { @@ -119,13 +92,6 @@ static std::string getIniValue(const IniData &data, const std::string §ion, return keyIt->second; } -/** - * @brief 检查 INI 值是否存在 - * @param data INI 数据 - * @param section 节名 - * @param key 键名 - * @return 是否存在 - */ static bool hasIniValue(const IniData &data, const std::string §ion, const std::string &key) { auto sectionIt = data.find(section); @@ -192,14 +158,6 @@ ConfigLoadResult IniConfigLoader::loadFromString(const std::string &content, if (hasIniValue(data, "app", "configFile")) { config.configFile = getIniValue(data, "app", "configFile"); } - if (hasIniValue(data, "app", "targetPlatform")) { - int value; - auto res = parseInt(getIniValue(data, "app", "targetPlatform"), value, - "app.targetPlatform"); - if (res.isOk()) { - config.targetPlatform = static_cast(value); - } - } E2D_LOG_INFO("INI 应用配置加载成功"); return ConfigLoadResult::ok(); @@ -213,7 +171,6 @@ std::string IniConfigLoader::saveToString(const AppConfig &config) { oss << "version=" << config.appVersion << "\n"; oss << "organization=" << config.organization << "\n"; oss << "configFile=" << config.configFile << "\n"; - oss << "targetPlatform=" << static_cast(config.targetPlatform) << "\n"; return oss.str(); } @@ -329,4 +286,4 @@ ConfigLoadResult IniConfigLoader::parseBool(const std::string &value, return ConfigLoadResult::error("无法解析布尔值: " + value, -1, fieldName); } -} // namespace extra2d +} diff --git a/Extra2D/src/config/config_loader_json.cpp b/Extra2D/src/config/config_loader_json.cpp index ae59f8b..cceaf28 100644 --- a/Extra2D/src/config/config_loader_json.cpp +++ b/Extra2D/src/config/config_loader_json.cpp @@ -71,10 +71,6 @@ ConfigLoadResult JsonConfigLoader::loadFromString(const std::string& content, Ap config.configFile = root["configFile"].get(); } - if (root.contains("targetPlatform") && root["targetPlatform"].is_number_integer()) { - config.targetPlatform = static_cast(root["targetPlatform"].get()); - } - E2D_LOG_INFO("JSON 应用配置加载成功"); return ConfigLoadResult::ok(); } @@ -86,7 +82,6 @@ std::string JsonConfigLoader::saveToString(const AppConfig& config) { root["appVersion"] = config.appVersion; root["organization"] = config.organization; root["configFile"] = config.configFile; - root["targetPlatform"] = static_cast(config.targetPlatform); return root.dump(4); } diff --git a/Extra2D/src/config/config_manager.cpp b/Extra2D/src/config/config_manager.cpp index 2094b22..3d2c072 100644 --- a/Extra2D/src/config/config_manager.cpp +++ b/Extra2D/src/config/config_manager.cpp @@ -1,6 +1,4 @@ #include -#include -#include #include #include @@ -36,12 +34,6 @@ bool ConfigManager::initialize(const std::string& configPath) { m_configPath = configPath; - m_platformConfig = createPlatformConfig(PlatformType::Auto); - if (!m_platformConfig) { - E2D_LOG_ERROR("Failed to create platform config"); - return false; - } - m_loader = makeUnique(); if (!m_loader) { E2D_LOG_ERROR("Failed to create config loader"); @@ -49,13 +41,11 @@ bool ConfigManager::initialize(const std::string& configPath) { } m_appConfig = AppConfig::createDefault(); - m_appConfig.targetPlatform = PlatformDetector::detect(); m_initialized = true; m_modified = false; - E2D_LOG_INFO("ConfigManager initialized for platform: {}", - m_platformConfig->platformName()); + E2D_LOG_INFO("ConfigManager initialized"); return true; } @@ -73,7 +63,6 @@ void ConfigManager::shutdown() { m_changeCallbacks.clear(); m_rawValues.clear(); m_loader.reset(); - m_platformConfig.reset(); m_initialized = false; m_modified = false; @@ -217,14 +206,6 @@ void ConfigManager::setAppConfig(const AppConfig& config) { E2D_LOG_INFO("App config updated"); } -PlatformConfig* ConfigManager::platformConfig() { - return m_platformConfig.get(); -} - -const PlatformConfig* ConfigManager::platformConfig() const { - return m_platformConfig.get(); -} - int ConfigManager::registerChangeCallback(ConfigChangeCallback callback) { std::lock_guard lock(m_mutex); diff --git a/Extra2D/src/config/platform_config.cpp b/Extra2D/src/config/platform_config.cpp deleted file mode 100644 index 85cac6e..0000000 --- a/Extra2D/src/config/platform_config.cpp +++ /dev/null @@ -1,224 +0,0 @@ -#include -#include -#include - -#ifdef _WIN32 -#include -#endif - -#ifdef __SWITCH__ -#include -#endif - -namespace extra2d { - -namespace { - -class WindowsPlatformConfig : public PlatformConfig { -public: - WindowsPlatformConfig() { - caps_.supportsWindowed = true; - caps_.supportsFullscreen = true; - caps_.supportsBorderless = true; - caps_.supportsCursor = true; - caps_.supportsCursorHide = true; - caps_.supportsDPIAwareness = true; - caps_.supportsVSync = true; - caps_.supportsMultiMonitor = true; - caps_.supportsClipboard = true; - caps_.supportsGamepad = true; - caps_.supportsTouch = false; - caps_.supportsKeyboard = true; - caps_.supportsMouse = true; - caps_.supportsResize = true; - caps_.supportsHighDPI = true; - caps_.maxTextureSize = 16384; - caps_.preferredScreenWidth = 1920; - caps_.preferredScreenHeight = 1080; - caps_.defaultDPI = 96.0f; - } - - PlatformType platformType() const override { return PlatformType::Windows; } - const char *platformName() const override { return "Windows"; } - const PlatformCapabilities &capabilities() const override { return caps_; } - - int getRecommendedWidth() const override { return 1920; } - int getRecommendedHeight() const override { return 1080; } - bool isResolutionSupported(int width, int height) const override { - return width >= 320 && height >= 240 && width <= caps_.maxTextureSize && - height <= caps_.maxTextureSize; - } - -private: - PlatformCapabilities caps_; -}; - -class LinuxPlatformConfig : public PlatformConfig { -public: - LinuxPlatformConfig() { - caps_.supportsWindowed = true; - caps_.supportsFullscreen = true; - caps_.supportsBorderless = true; - caps_.supportsCursor = true; - caps_.supportsCursorHide = true; - caps_.supportsDPIAwareness = true; - caps_.supportsVSync = true; - caps_.supportsMultiMonitor = true; - caps_.supportsClipboard = true; - caps_.supportsGamepad = true; - caps_.supportsTouch = false; - caps_.supportsKeyboard = true; - caps_.supportsMouse = true; - caps_.supportsResize = true; - caps_.supportsHighDPI = true; - caps_.maxTextureSize = 16384; - caps_.preferredScreenWidth = 1920; - caps_.preferredScreenHeight = 1080; - caps_.defaultDPI = 96.0f; - } - - PlatformType platformType() const override { return PlatformType::Linux; } - const char *platformName() const override { return "Linux"; } - const PlatformCapabilities &capabilities() const override { return caps_; } - - int getRecommendedWidth() const override { return 1920; } - int getRecommendedHeight() const override { return 1080; } - bool isResolutionSupported(int width, int height) const override { - return width >= 320 && height >= 240; - } - -private: - PlatformCapabilities caps_; -}; - -class MacOSPlatformConfig : public PlatformConfig { -public: - MacOSPlatformConfig() { - caps_.supportsWindowed = true; - caps_.supportsFullscreen = true; - caps_.supportsBorderless = true; - caps_.supportsCursor = true; - caps_.supportsCursorHide = true; - caps_.supportsDPIAwareness = true; - caps_.supportsVSync = true; - caps_.supportsMultiMonitor = true; - caps_.supportsClipboard = true; - caps_.supportsGamepad = true; - caps_.supportsTouch = false; - caps_.supportsKeyboard = true; - caps_.supportsMouse = true; - caps_.supportsResize = true; - caps_.supportsHighDPI = true; - caps_.maxTextureSize = 16384; - caps_.preferredScreenWidth = 1920; - caps_.preferredScreenHeight = 1080; - caps_.defaultDPI = 144.0f; - } - - PlatformType platformType() const override { return PlatformType::macOS; } - const char *platformName() const override { return "macOS"; } - const PlatformCapabilities &capabilities() const override { return caps_; } - - int getRecommendedWidth() const override { return 1920; } - int getRecommendedHeight() const override { return 1080; } - bool isResolutionSupported(int width, int height) const override { - return width >= 320 && height >= 240; - } - -private: - PlatformCapabilities caps_; -}; - -class SwitchPlatformConfig : public PlatformConfig { -public: - SwitchPlatformConfig() { - caps_.supportsWindowed = false; - caps_.supportsFullscreen = true; - caps_.supportsBorderless = false; - caps_.supportsCursor = false; - caps_.supportsCursorHide = false; - caps_.supportsDPIAwareness = false; - caps_.supportsVSync = true; - caps_.supportsMultiMonitor = false; - caps_.supportsClipboard = false; - caps_.supportsGamepad = true; - caps_.supportsTouch = true; - caps_.supportsKeyboard = false; - caps_.supportsMouse = false; - caps_.supportsResize = false; - caps_.supportsHighDPI = false; - caps_.maxTextureSize = 8192; - caps_.preferredScreenWidth = 1920; - caps_.preferredScreenHeight = 1080; - caps_.defaultDPI = 96.0f; - } - - PlatformType platformType() const override { return PlatformType::Switch; } - const char *platformName() const override { return "Nintendo Switch"; } - const PlatformCapabilities &capabilities() const override { return caps_; } - - int getRecommendedWidth() const override { return 1920; } - int getRecommendedHeight() const override { return 1080; } - bool isResolutionSupported(int width, int height) const override { - return (width == 1920 && height == 1080) || - (width == 1280 && height == 720); - } - -private: - PlatformCapabilities caps_; -}; - -} - -UniquePtr createPlatformConfig(PlatformType type) { - if (type == PlatformType::Auto) { -#ifdef _WIN32 - type = PlatformType::Windows; -#elif defined(__SWITCH__) - type = PlatformType::Switch; -#elif defined(__linux__) - type = PlatformType::Linux; -#elif defined(__APPLE__) - type = PlatformType::macOS; -#else - type = PlatformType::Windows; -#endif - } - - switch (type) { - case PlatformType::Windows: - E2D_LOG_INFO("Creating Windows platform config"); - return makeUnique(); - case PlatformType::Switch: - E2D_LOG_INFO("Creating Nintendo Switch platform config"); - return makeUnique(); - case PlatformType::Linux: - E2D_LOG_INFO("Creating Linux platform config"); - return makeUnique(); - case PlatformType::macOS: - E2D_LOG_INFO("Creating macOS platform config"); - return makeUnique(); - default: - E2D_LOG_WARN("Unknown platform type, defaulting to Windows"); - return makeUnique(); - } -} - -const char *getPlatformTypeName(PlatformType type) { - switch (type) { - case PlatformType::Auto: - return "Auto"; - case PlatformType::Windows: - return "Windows"; - case PlatformType::Switch: - return "Switch"; - case PlatformType::Linux: - return "Linux"; - case PlatformType::macOS: - return "macOS"; - default: - return "Unknown"; - } -} - -} diff --git a/Extra2D/src/config/platform_detector.cpp b/Extra2D/src/config/platform_detector.cpp deleted file mode 100644 index 32d1ddb..0000000 --- a/Extra2D/src/config/platform_detector.cpp +++ /dev/null @@ -1,678 +0,0 @@ -#include -#include -#include - -#ifdef _WIN32 -#include -#include -#include -#elif defined(__linux__) -#include -#include -#include -#include -#elif defined(__APPLE__) -#include -#include -#include -#include -#endif - -#ifdef __SWITCH__ -#include -#endif - -namespace extra2d { - -/** - * @brief 检测当前运行平台 - * 使用编译时宏判断当前平台类型 - * @return 当前平台的类型枚举值 - */ -PlatformType PlatformDetector::detect() { -#ifdef _WIN32 - return PlatformType::Windows; -#elif defined(__SWITCH__) - return PlatformType::Switch; -#elif defined(__linux__) - return PlatformType::Linux; -#elif defined(__APPLE__) - return PlatformType::macOS; -#else - return PlatformType::Windows; -#endif -} - -/** - * @brief 获取当前平台名称字符串 - * @return 平台名称(如 "Windows", "Linux", "macOS", "Switch") - */ -const char* PlatformDetector::platformName() { - return platformName(detect()); -} - -/** - * @brief 获取指定平台类型的名称 - * @param type 平台类型 - * @return 平台名称字符串 - */ -const char* PlatformDetector::platformName(PlatformType type) { - switch (type) { - case PlatformType::Windows: return "Windows"; - case PlatformType::Switch: return "Nintendo Switch"; - case PlatformType::Linux: return "Linux"; - case PlatformType::macOS: return "macOS"; - case PlatformType::Auto: return "Auto"; - default: return "Unknown"; - } -} - -/** - * @brief 检查当前平台是否为桌面平台 - * @return 如果是桌面平台返回 true - */ -bool PlatformDetector::isDesktopPlatform() { - PlatformType type = detect(); - return type == PlatformType::Windows || - type == PlatformType::Linux || - type == PlatformType::macOS; -} - -/** - * @brief 检查当前平台是否为游戏主机平台 - * @return 如果是游戏主机平台返回 true - */ -bool PlatformDetector::isConsolePlatform() { - return detect() == PlatformType::Switch; -} - -/** - * @brief 检查当前平台是否为移动平台 - * @return 如果是移动平台返回 true - */ -bool PlatformDetector::isMobilePlatform() { - return false; -} - -/** - * @brief 获取当前平台的能力 - * @return 平台能力结构 - */ -PlatformCapabilities PlatformDetector::capabilities() { - return capabilities(detect()); -} - -/** - * @brief 获取指定平台的能力 - * @param type 平台类型 - * @return 平台能力结构 - */ -PlatformCapabilities PlatformDetector::capabilities(PlatformType type) { - switch (type) { - case PlatformType::Windows: - return getWindowsCapabilities(); - case PlatformType::Switch: - return getSwitchCapabilities(); - case PlatformType::Linux: - return getLinuxCapabilities(); - case PlatformType::macOS: - return getMacOSCapabilities(); - default: - return getWindowsCapabilities(); - } -} - -/** - * @brief 获取当前平台的默认配置 - * @return 平台默认的应用配置 - */ -AppConfig PlatformDetector::platformDefaults() { - return platformDefaults(detect()); -} - -/** - * @brief 获取指定平台的默认配置 - * @param type 平台类型 - * @return 平台默认的应用配置 - */ -AppConfig PlatformDetector::platformDefaults(PlatformType type) { - switch (type) { - case PlatformType::Windows: - return getWindowsDefaults(); - case PlatformType::Switch: - return getSwitchDefaults(); - case PlatformType::Linux: - return getLinuxDefaults(); - case PlatformType::macOS: - return getMacOSDefaults(); - default: - return AppConfig::createDefault(); - } -} - -/** - * @brief 获取当前平台的推荐分辨率 - * @param width 输出宽度 - * @param height 输出高度 - */ -void PlatformDetector::getRecommendedResolution(int& width, int& height) { - PlatformCapabilities caps = capabilities(); - width = caps.preferredScreenWidth; - height = caps.preferredScreenHeight; -} - -/** - * @brief 获取当前平台的默认 DPI - * @return 默认 DPI 值 - */ -float PlatformDetector::getDefaultDPI() { - return capabilities().defaultDPI; -} - -/** - * @brief 检查当前平台是否支持指定功能 - * @param feature 功能名称 - * @return 如果支持返回 true - */ -bool PlatformDetector::supportsFeature(const std::string& feature) { - PlatformCapabilities caps = capabilities(); - - if (feature == "windowed") return caps.supportsWindowed; - if (feature == "fullscreen") return caps.supportsFullscreen; - if (feature == "borderless") return caps.supportsBorderless; - if (feature == "cursor") return caps.supportsCursor; - if (feature == "cursor_hide") return caps.supportsCursorHide; - if (feature == "dpi_awareness") return caps.supportsDPIAwareness; - if (feature == "vsync") return caps.supportsVSync; - if (feature == "multi_monitor") return caps.supportsMultiMonitor; - if (feature == "clipboard") return caps.supportsClipboard; - if (feature == "gamepad") return caps.supportsGamepad; - if (feature == "touch") return caps.supportsTouch; - if (feature == "keyboard") return caps.supportsKeyboard; - if (feature == "mouse") return caps.supportsMouse; - if (feature == "resize") return caps.supportsResize; - if (feature == "high_dpi") return caps.supportsHighDPI; - - return false; -} - -/** - * @brief 获取系统内存大小 - * @return 系统内存大小(MB),如果无法获取返回 0 - */ -int PlatformDetector::getSystemMemoryMB() { -#ifdef _WIN32 - MEMORYSTATUSEX status; - status.dwLength = sizeof(status); - if (GlobalMemoryStatusEx(&status)) { - return static_cast(status.ullTotalPhys / (1024 * 1024)); - } - return 0; -#elif defined(__SWITCH__) - return 4096; -#elif defined(__linux__) - struct sysinfo info; - if (sysinfo(&info) == 0) { - return static_cast(info.totalram * info.mem_unit / (1024 * 1024)); - } - return 0; -#elif defined(__APPLE__) - int mib[2] = {CTL_HW, HW_MEMSIZE}; - int64_t memSize = 0; - size_t length = sizeof(memSize); - if (sysctl(mib, 2, &memSize, &length, nullptr, 0) == 0) { - return static_cast(memSize / (1024 * 1024)); - } - return 0; -#else - return 0; -#endif -} - -/** - * @brief 获取 CPU 核心数 - * @return CPU 核心数 - */ -int PlatformDetector::getCPUCoreCount() { -#ifdef _WIN32 - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - return static_cast(sysinfo.dwNumberOfProcessors); -#elif defined(__SWITCH__) - return 4; -#elif defined(__linux__) || defined(__APPLE__) - long cores = sysconf(_SC_NPROCESSORS_ONLN); - return static_cast(cores > 0 ? cores : 1); -#else - return 1; -#endif -} - -/** - * @brief 检查是否支持多线程渲染 - * @return 如果支持返回 true - */ -bool PlatformDetector::supportsMultithreadedRendering() { -#ifdef __SWITCH__ - return false; -#else - return getCPUCoreCount() >= 2; -#endif -} - -/** - * @brief 获取平台特定的配置路径 - * @param appName 应用名称 - * @return 配置文件目录路径 - */ -std::string PlatformDetector::getConfigPath(const std::string& appName) { -#ifdef _WIN32 - char path[MAX_PATH]; - if (SUCCEEDED(SHGetFolderPathA(nullptr, CSIDL_APPDATA, nullptr, 0, path))) { - return std::string(path) + "\\" + appName + "\\config"; - } - return ".\\config"; -#elif defined(__SWITCH__) - return "sdmc:/config/" + appName; -#elif defined(__linux__) - const char* configHome = getenv("XDG_CONFIG_HOME"); - if (configHome && configHome[0] != '\0') { - return std::string(configHome) + "/" + appName; - } - const char* home = getenv("HOME"); - if (!home) { - struct passwd* pwd = getpwuid(getuid()); - if (pwd) home = pwd->pw_dir; - } - if (home) { - return std::string(home) + "/.config/" + appName; - } - return "./config"; -#elif defined(__APPLE__) - const char* home = getenv("HOME"); - if (!home) { - struct passwd* pwd = getpwuid(getuid()); - if (pwd) home = pwd->pw_dir; - } - if (home) { - return std::string(home) + "/Library/Application Support/" + appName + "/config"; - } - return "./config"; -#else - return "./config"; -#endif -} - -/** - * @brief 获取平台特定的存档路径 - * @param appName 应用名称 - * @return 存档文件目录路径 - */ -std::string PlatformDetector::getSavePath(const std::string& appName) { -#ifdef _WIN32 - char path[MAX_PATH]; - if (SUCCEEDED(SHGetFolderPathA(nullptr, CSIDL_APPDATA, nullptr, 0, path))) { - return std::string(path) + "\\" + appName + "\\saves"; - } - return ".\\saves"; -#elif defined(__SWITCH__) - return "sdmc:/saves/" + appName; -#elif defined(__linux__) - const char* dataHome = getenv("XDG_DATA_HOME"); - if (dataHome && dataHome[0] != '\0') { - return std::string(dataHome) + "/" + appName + "/saves"; - } - const char* home = getenv("HOME"); - if (!home) { - struct passwd* pwd = getpwuid(getuid()); - if (pwd) home = pwd->pw_dir; - } - if (home) { - return std::string(home) + "/.local/share/" + appName + "/saves"; - } - return "./saves"; -#elif defined(__APPLE__) - const char* home = getenv("HOME"); - if (!home) { - struct passwd* pwd = getpwuid(getuid()); - if (pwd) home = pwd->pw_dir; - } - if (home) { - return std::string(home) + "/Library/Application Support/" + appName + "/saves"; - } - return "./saves"; -#else - return "./saves"; -#endif -} - -/** - * @brief 获取平台特定的缓存路径 - * @param appName 应用名称 - * @return 缓存文件目录路径 - */ -std::string PlatformDetector::getCachePath(const std::string& appName) { -#ifdef _WIN32 - char path[MAX_PATH]; - if (SUCCEEDED(SHGetFolderPathA(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, path))) { - return std::string(path) + "\\" + appName + "\\cache"; - } - return ".\\cache"; -#elif defined(__SWITCH__) - return "sdmc:/cache/" + appName; -#elif defined(__linux__) - const char* cacheHome = getenv("XDG_CACHE_HOME"); - if (cacheHome && cacheHome[0] != '\0') { - return std::string(cacheHome) + "/" + appName; - } - const char* home = getenv("HOME"); - if (!home) { - struct passwd* pwd = getpwuid(getuid()); - if (pwd) home = pwd->pw_dir; - } - if (home) { - return std::string(home) + "/.cache/" + appName; - } - return "./cache"; -#elif defined(__APPLE__) - const char* home = getenv("HOME"); - if (!home) { - struct passwd* pwd = getpwuid(getuid()); - if (pwd) home = pwd->pw_dir; - } - if (home) { - return std::string(home) + "/Library/Caches/" + appName; - } - return "./cache"; -#else - return "./cache"; -#endif -} - -/** - * @brief 获取平台特定的日志路径 - * @param appName 应用名称 - * @return 日志文件目录路径 - */ -std::string PlatformDetector::getLogPath(const std::string& appName) { -#ifdef _WIN32 - char path[MAX_PATH]; - if (SUCCEEDED(SHGetFolderPathA(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, path))) { - return std::string(path) + "\\" + appName + "\\logs"; - } - return ".\\logs"; -#elif defined(__SWITCH__) - return "sdmc:/logs/" + appName; -#elif defined(__linux__) - const char* cacheHome = getenv("XDG_CACHE_HOME"); - if (cacheHome && cacheHome[0] != '\0') { - return std::string(cacheHome) + "/" + appName + "/logs"; - } - const char* home = getenv("HOME"); - if (!home) { - struct passwd* pwd = getpwuid(getuid()); - if (pwd) home = pwd->pw_dir; - } - if (home) { - return std::string(home) + "/.cache/" + appName + "/logs"; - } - return "./logs"; -#elif defined(__APPLE__) - const char* home = getenv("HOME"); - if (!home) { - struct passwd* pwd = getpwuid(getuid()); - if (pwd) home = pwd->pw_dir; - } - if (home) { - return std::string(home) + "/Library/Logs/" + appName; - } - return "./logs"; -#else - return "./logs"; -#endif -} - -/** - * @brief 获取平台特定的资源路径(Shader、纹理等) - * Switch平台使用romfs,其他平台使用相对路径 - * @param appName 应用名称 - * @return 资源目录路径 - */ -std::string PlatformDetector::getResourcePath(const std::string& appName) { -#ifdef __SWITCH__ - (void)appName; - return "romfs:/"; -#else - (void)appName; - return "./resources/"; -#endif -} - -/** - * @brief 获取平台特定的Shader路径 - * @param appName 应用名称 - * @return Shader目录路径 - */ -std::string PlatformDetector::getShaderPath(const std::string& appName) { -#ifdef __SWITCH__ - (void)appName; - return "romfs:/shaders/"; -#else - (void)appName; - return "./shaders/"; -#endif -} - -/** - * @brief 获取平台特定的Shader缓存路径 - * Switch平台使用sdmc,其他平台使用系统缓存目录 - * @param appName 应用名称 - * @return Shader缓存目录路径 - */ -std::string PlatformDetector::getShaderCachePath(const std::string& appName) { -#ifdef __SWITCH__ - std::string name = appName.empty() ? "extra2d" : appName; - return "sdmc:/cache/" + name + "/shaders/"; -#else - return getCachePath(appName.empty() ? "extra2d" : appName) + "/shaders/"; -#endif -} - -/** - * @brief 检查平台是否使用romfs(只读文件系统) - * @return 使用romfs返回true - */ -bool PlatformDetector::usesRomfs() { -#ifdef __SWITCH__ - return true; -#else - return false; -#endif -} - -/** - * @brief 检查平台是否支持热重载 - * Switch平台不支持热重载(romfs只读) - * @return 支持热重载返回true - */ -bool PlatformDetector::supportsHotReload() { -#ifdef __SWITCH__ - return false; -#else - return true; -#endif -} - -/** - * @brief 检查平台是否为小端字节序 - * @return 如果是小端字节序返回 true - */ -bool PlatformDetector::isLittleEndian() { - union { - uint32_t i; - char c[4]; - } test = {0x01020304}; - return test.c[0] == 0x04; -} - -/** - * @brief 检查平台是否为大端字节序 - * @return 如果是大端字节序返回 true - */ -bool PlatformDetector::isBigEndian() { - return !isLittleEndian(); -} - -/** - * @brief 获取平台信息摘要 - * @return 平台信息字符串 - */ -std::string PlatformDetector::getPlatformSummary() { - std::string summary; - summary += "Platform: "; - summary += platformName(); - summary += "\n"; - summary += "Memory: "; - summary += std::to_string(getSystemMemoryMB()); - summary += " MB\n"; - summary += "CPU Cores: "; - summary += std::to_string(getCPUCoreCount()); - summary += "\n"; - summary += "Endianness: "; - summary += isLittleEndian() ? "Little Endian" : "Big Endian"; - summary += "\n"; - summary += "Desktop Platform: "; - summary += isDesktopPlatform() ? "Yes" : "No"; - summary += "\n"; - summary += "Console Platform: "; - summary += isConsolePlatform() ? "Yes" : "No"; - summary += "\n"; - summary += "Recommended Resolution: "; - int width, height; - getRecommendedResolution(width, height); - summary += std::to_string(width); - summary += "x"; - summary += std::to_string(height); - summary += "\n"; - summary += "Default DPI: "; - summary += std::to_string(static_cast(getDefaultDPI())); - return summary; -} - -PlatformCapabilities PlatformDetector::getWindowsCapabilities() { - PlatformCapabilities caps; - caps.supportsWindowed = true; - caps.supportsFullscreen = true; - caps.supportsBorderless = true; - caps.supportsCursor = true; - caps.supportsCursorHide = true; - caps.supportsDPIAwareness = true; - caps.supportsVSync = true; - caps.supportsMultiMonitor = true; - caps.supportsClipboard = true; - caps.supportsGamepad = true; - caps.supportsTouch = false; - caps.supportsKeyboard = true; - caps.supportsMouse = true; - caps.supportsResize = true; - caps.supportsHighDPI = true; - caps.maxTextureSize = 16384; - caps.preferredScreenWidth = 1920; - caps.preferredScreenHeight = 1080; - caps.defaultDPI = 96.0f; - return caps; -} - -PlatformCapabilities PlatformDetector::getLinuxCapabilities() { - PlatformCapabilities caps; - caps.supportsWindowed = true; - caps.supportsFullscreen = true; - caps.supportsBorderless = true; - caps.supportsCursor = true; - caps.supportsCursorHide = true; - caps.supportsDPIAwareness = true; - caps.supportsVSync = true; - caps.supportsMultiMonitor = true; - caps.supportsClipboard = true; - caps.supportsGamepad = true; - caps.supportsTouch = false; - caps.supportsKeyboard = true; - caps.supportsMouse = true; - caps.supportsResize = true; - caps.supportsHighDPI = true; - caps.maxTextureSize = 16384; - caps.preferredScreenWidth = 1920; - caps.preferredScreenHeight = 1080; - caps.defaultDPI = 96.0f; - return caps; -} - -PlatformCapabilities PlatformDetector::getMacOSCapabilities() { - PlatformCapabilities caps; - caps.supportsWindowed = true; - caps.supportsFullscreen = true; - caps.supportsBorderless = true; - caps.supportsCursor = true; - caps.supportsCursorHide = true; - caps.supportsDPIAwareness = true; - caps.supportsVSync = true; - caps.supportsMultiMonitor = true; - caps.supportsClipboard = true; - caps.supportsGamepad = true; - caps.supportsTouch = false; - caps.supportsKeyboard = true; - caps.supportsMouse = true; - caps.supportsResize = true; - caps.supportsHighDPI = true; - caps.maxTextureSize = 16384; - caps.preferredScreenWidth = 1920; - caps.preferredScreenHeight = 1080; - caps.defaultDPI = 144.0f; - return caps; -} - -PlatformCapabilities PlatformDetector::getSwitchCapabilities() { - PlatformCapabilities caps; - caps.supportsWindowed = false; - caps.supportsFullscreen = true; - caps.supportsBorderless = false; - caps.supportsCursor = false; - caps.supportsCursorHide = false; - caps.supportsDPIAwareness = false; - caps.supportsVSync = true; - caps.supportsMultiMonitor = false; - caps.supportsClipboard = false; - caps.supportsGamepad = true; - caps.supportsTouch = true; - caps.supportsKeyboard = false; - caps.supportsMouse = false; - caps.supportsResize = false; - caps.supportsHighDPI = false; - caps.maxTextureSize = 8192; - caps.preferredScreenWidth = 1920; - caps.preferredScreenHeight = 1080; - caps.defaultDPI = 96.0f; - return caps; -} - -AppConfig PlatformDetector::getWindowsDefaults() { - AppConfig config = AppConfig::createDefault(); - return config; -} - -AppConfig PlatformDetector::getLinuxDefaults() { - AppConfig config = AppConfig::createDefault(); - return config; -} - -AppConfig PlatformDetector::getMacOSDefaults() { - AppConfig config = AppConfig::createDefault(); - return config; -} - -AppConfig PlatformDetector::getSwitchDefaults() { - AppConfig config = AppConfig::createDefault(); - return config; -} - -} diff --git a/Extra2D/src/core/module_registry.cpp b/Extra2D/src/core/module_registry.cpp new file mode 100644 index 0000000..fa7d671 --- /dev/null +++ b/Extra2D/src/core/module_registry.cpp @@ -0,0 +1,242 @@ +#include +#include +#include +#include + +#include +#include + +namespace extra2d { + +ModuleRegistry &ModuleRegistry::instance() { + static ModuleRegistry instance; + return instance; +} + +void ModuleRegistry::registerMeta(ModuleMetaBase *meta) { + if (!meta) + return; + + for (const auto *existing : metas_) { + if (existing && std::string(existing->getName()) == meta->getName()) { + return; + } + } + + metas_.push_back(meta); +} + +std::vector ModuleRegistry::getAllMetas() const { + return metas_; +} + +ModuleMetaBase *ModuleRegistry::getMeta(const char *name) const { + if (!name) + return nullptr; + + for (auto *meta : metas_) { + if (meta && std::string(meta->getName()) == name) { + return meta; + } + } + return nullptr; +} + +Module *ModuleRegistry::getModule(const char *name) const { + auto it = instanceMap_.find(name ? name : ""); + return it != instanceMap_.end() ? it->second : nullptr; +} + +bool ModuleRegistry::hasModule(const char *name) const { + return instanceMap_.find(name ? name : "") != instanceMap_.end(); +} + +std::vector ModuleRegistry::getAllModules() const { + std::vector result; + result.reserve(instances_.size()); + for (const auto &inst : instances_) { + result.push_back(inst.get()); + } + return result; +} + +bool ModuleRegistry::hasCircularDependency() const { + std::unordered_map> graph; + + for (auto *meta : metas_) { + if (!meta) + continue; + std::string name = meta->getName(); + for (const auto *dep : meta->getDependencies()) { + graph[name].insert(dep); + } + } + + std::unordered_set visited; + std::unordered_set recStack; + + std::function hasCycle = + [&](const std::string &node) -> bool { + visited.insert(node); + recStack.insert(node); + + for (const auto &neighbor : graph[node]) { + if (visited.find(neighbor) == visited.end()) { + if (hasCycle(neighbor)) + return true; + } else if (recStack.find(neighbor) != recStack.end()) { + return true; + } + } + + recStack.erase(node); + return false; + }; + + for (auto *meta : metas_) { + if (!meta) + continue; + std::string name = meta->getName(); + if (visited.find(name) == visited.end()) { + if (hasCycle(name)) + return true; + } + } + + return false; +} + +std::vector ModuleRegistry::sortByDependency() { + if (hasCircularDependency()) { + E2D_LOG_ERROR("Circular dependency detected in modules!"); + return {}; + } + + std::unordered_map inDegree; + std::unordered_map> graph; + std::unordered_map nameToMeta; + + for (auto *meta : metas_) { + if (!meta) + continue; + std::string name = meta->getName(); + nameToMeta[name] = meta; + if (inDegree.find(name) == inDegree.end()) { + inDegree[name] = 0; + } + + for (const auto *dep : meta->getDependencies()) { + std::string depName = dep; + graph[depName].push_back(name); + inDegree[name]++; + } + } + + std::vector sorted; + std::vector currentLevel; + + for (const auto &[name, degree] : inDegree) { + if (degree == 0) { + currentLevel.push_back(name); + } + } + + while (!currentLevel.empty()) { + std::sort(currentLevel.begin(), currentLevel.end(), + [&nameToMeta](const std::string &a, const std::string &b) { + auto *metaA = nameToMeta[a]; + auto *metaB = nameToMeta[b]; + if (!metaA || !metaB) + return a < b; + return metaA->getPriority() < metaB->getPriority(); + }); + + std::vector nextLevel; + + for (const auto &name : currentLevel) { + if (nameToMeta.find(name) != nameToMeta.end()) { + sorted.push_back(nameToMeta[name]); + } + + for (const auto &neighbor : graph[name]) { + inDegree[neighbor]--; + if (inDegree[neighbor] == 0) { + nextLevel.push_back(neighbor); + } + } + } + + currentLevel = std::move(nextLevel); + } + + return sorted; +} + +bool ModuleRegistry::createAndInitAll() { + if (initialized_) { + return true; + } + + // 动态库中模块已通过 E2D_MODULE 宏自动注册 + // 无需手动调用 registerCoreModules() + + E2D_LOG_INFO("ModuleRegistry: {} modules registered", metas_.size()); + for (auto *meta : metas_) { + if (meta) { + E2D_LOG_INFO(" - {} (priority: {})", meta->getName(), + meta->getPriority()); + } + } + + auto sorted = sortByDependency(); + if (sorted.empty() && !metas_.empty()) { + E2D_LOG_ERROR( + "Failed to sort modules by dependency - possible circular dependency"); + return false; + } + + for (auto *meta : sorted) { + if (!meta) + continue; + + Module *instance = meta->create(); + if (!instance) { + E2D_LOG_ERROR("Failed to create module: {}", meta->getName()); + continue; + } + + PropertyBinderImpl binder; + meta->bindProperties(instance, binder); + + instance->setupModule(); + + instances_.emplace_back(instance); + instanceMap_[meta->getName()] = instance; + } + + initialized_ = true; + return true; +} + +void ModuleRegistry::destroyAll() { + if (!initialized_) { + return; + } + + E2D_LOG_INFO("Destroying {} modules in reverse order", instances_.size()); + + // 先销毁所有模块(逆序) + for (auto it = instances_.rbegin(); it != instances_.rend(); ++it) { + if (*it) { + E2D_LOG_INFO("Destroying module: {}", (*it)->getName()); + (*it)->destroyModule(); + } + } + + // 然后清理实例 + instances_.clear(); + instanceMap_.clear(); + initialized_ = false; +} + +} // namespace extra2d diff --git a/Extra2D/src/graphics/opengl/gl_renderer.cpp b/Extra2D/src/graphics/opengl/gl_renderer.cpp index 6452568..efdb694 100644 --- a/Extra2D/src/graphics/opengl/gl_renderer.cpp +++ b/Extra2D/src/graphics/opengl/gl_renderer.cpp @@ -633,7 +633,7 @@ void GLRenderer::resetStats() { stats_ = Stats{}; } */ void GLRenderer::initShapeRendering() { // 从ShaderManager获取形状着色器 - shapeShader_ = ShaderManager::getInstance().getBuiltin("builtin_shape"); + shapeShader_ = ShaderManager::getInstance().getBuiltin("shape"); if (!shapeShader_) { E2D_LOG_WARN("Failed to get builtin shape shader, loading from manager"); if (!ShaderManager::getInstance().isInitialized()) { diff --git a/Extra2D/src/graphics/shader_manager.cpp b/Extra2D/src/graphics/shader_manager.cpp index b15552d..ac90f3f 100644 --- a/Extra2D/src/graphics/shader_manager.cpp +++ b/Extra2D/src/graphics/shader_manager.cpp @@ -3,42 +3,30 @@ namespace extra2d { -/** - * @brief 获取单例实例 - * @return Shader管理器实例引用 - */ ShaderManager& ShaderManager::getInstance() { static ShaderManager instance; return instance; } -/** - * @brief 使用平台默认路径初始化Shader系统 - * 自动检测平台并使用正确的路径(romfs/sdmc/相对路径) - * @param factory 渲染后端Shader工厂 - * @param appName 应用名称(用于缓存目录) - * @return 初始化成功返回true,失败返回false - */ bool ShaderManager::init(Ptr factory, const std::string& appName) { - std::string shaderDir = PlatformDetector::getShaderPath(appName); - std::string cacheDir = PlatformDetector::getShaderCachePath(appName); + std::string shaderDir; + std::string cacheDir; - hotReloadSupported_ = PlatformDetector::supportsHotReload(); - - E2D_LOG_INFO("Platform: {} (HotReload: {})", - PlatformDetector::platformName(), - hotReloadSupported_ ? "supported" : "not supported"); +#ifdef __SWITCH__ + shaderDir = "romfs:/shaders/"; + cacheDir = "sdmc:/config/" + appName + "/shader_cache/"; + hotReloadSupported_ = false; + E2D_LOG_INFO("Platform: Switch (HotReload: not supported)"); +#else + shaderDir = "shaders/"; + cacheDir = "shader_cache/"; + hotReloadSupported_ = true; + E2D_LOG_INFO("Platform: Desktop (HotReload: supported)"); +#endif return init(shaderDir, cacheDir, factory); } -/** - * @brief 初始化Shader系统 - * @param shaderDir Shader文件目录 - * @param cacheDir 缓存目录 - * @param factory 渲染后端Shader工厂 - * @return 初始化成功返回true,失败返回false - */ bool ShaderManager::init(const std::string& shaderDir, const std::string& cacheDir, Ptr factory) { @@ -56,13 +44,13 @@ bool ShaderManager::init(const std::string& shaderDir, cacheDir_ = cacheDir; factory_ = factory; - hotReloadSupported_ = PlatformDetector::supportsHotReload(); - #ifdef __SWITCH__ + hotReloadSupported_ = false; if (!ShaderCache::getInstance().init(cacheDir_)) { E2D_LOG_WARN("Failed to initialize shader cache on Switch"); } #else + hotReloadSupported_ = true; if (!ShaderCache::getInstance().init(cacheDir_)) { E2D_LOG_WARN("Failed to initialize shader cache, caching disabled"); } @@ -78,16 +66,10 @@ bool ShaderManager::init(const std::string& shaderDir, initialized_ = true; E2D_LOG_INFO("ShaderManager initialized"); - E2D_LOG_INFO(" Shader directory: {}", shaderDir_); - E2D_LOG_INFO(" Cache directory: {}", cacheDir_); - E2D_LOG_INFO(" Hot reload: {}", hotReloadSupported_ ? "supported" : "not supported"); - + return true; } -/** - * @brief 关闭Shader系统 - */ void ShaderManager::shutdown() { if (!initialized_) { return; @@ -105,175 +87,57 @@ void ShaderManager::shutdown() { E2D_LOG_INFO("ShaderManager shutdown"); } -/** - * @brief 从分离文件加载Shader - * @param name Shader名称 - * @param vertPath 顶点着色器文件路径 - * @param fragPath 片段着色器文件路径 - * @return 加载的Shader实例 - */ Ptr ShaderManager::loadFromFiles(const std::string& name, const std::string& vertPath, const std::string& fragPath) { - if (!initialized_) { - E2D_LOG_ERROR("ShaderManager not initialized"); + if (!factory_) { + E2D_LOG_ERROR("Shader factory not initialized"); return nullptr; } - auto it = shaders_.find(name); - if (it != shaders_.end()) { - return it->second.shader; - } - - ShaderLoadResult result = loader_.loadFromSeparateFiles(name, vertPath, fragPath); - if (!result.success) { - E2D_LOG_ERROR("Failed to load shader files: {} - {}", vertPath, fragPath); + auto vertSource = loader_.readFile(vertPath); + auto fragSource = loader_.readFile(fragPath); + + if (vertSource.empty() || fragSource.empty()) { + E2D_LOG_ERROR("Failed to load shader sources: {} / {}", vertPath, fragPath); return nullptr; } - std::string sourceHash = ShaderCache::computeHash(result.vertSource, result.fragSource); - Ptr shader = loadFromCache(name, sourceHash, result.vertSource, result.fragSource); - - if (!shader) { - E2D_LOG_DEBUG("No valid cache found, compiling shader from source: {}", name); - shader = factory_->createFromSource(name, result.vertSource, result.fragSource); - if (!shader) { - E2D_LOG_ERROR("Failed to create shader from source: {}", name); - return nullptr; - } - - std::vector binary; - if (factory_->getShaderBinary(*shader, binary)) { - E2D_LOG_DEBUG("Got shader binary, size: {} bytes", binary.size()); - ShaderCacheEntry entry; - entry.name = name; - entry.sourceHash = sourceHash; - entry.binary = binary; - entry.dependencies = result.dependencies; - ShaderCache::getInstance().saveCache(entry); - } else { - E2D_LOG_WARN("Failed to get shader binary for: {}", name); - } - } - - ShaderInfo info; - info.shader = shader; - info.vertSource = result.vertSource; - info.fragSource = result.fragSource; - info.filePaths = {vertPath, fragPath}; - info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(), result.dependencies.end()); - - info.metadata.name = name; - info.metadata.vertPath = vertPath; - info.metadata.fragPath = fragPath; - - shaders_[name] = std::move(info); - - if (hotReloadEnabled_ && hotReloadSupported_) { - auto callback = [this, name](const FileChangeEvent& event) { - this->handleFileChange(name, event); - }; - ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths, callback); - } - - E2D_LOG_DEBUG("Shader loaded: {}", name); - return shader; + return loadFromSource(name, vertSource, fragSource); } -/** - * @brief 从组合文件加载Shader - * @param path 组合Shader文件路径 - * @return 加载的Shader实例 - */ Ptr ShaderManager::loadFromCombinedFile(const std::string& path) { - if (!initialized_) { - E2D_LOG_ERROR("ShaderManager not initialized"); + if (!factory_) { + E2D_LOG_ERROR("Shader factory not initialized"); return nullptr; } - ShaderMetadata metadata = loader_.getMetadata(path); - std::string name = metadata.name.empty() ? path : metadata.name; - - auto it = shaders_.find(name); - if (it != shaders_.end()) { - return it->second.shader; - } - - ShaderLoadResult result = loader_.loadFromCombinedFile(path); + auto result = loader_.loadFromCombinedFile(path); if (!result.success) { - E2D_LOG_ERROR("Failed to load combined shader file: {}", path); + E2D_LOG_ERROR("Failed to load combined shader: {}", path); return nullptr; } - std::string sourceHash = ShaderCache::computeHash(result.vertSource, result.fragSource); - Ptr shader = loadFromCache(name, sourceHash, result.vertSource, result.fragSource); - - if (!shader) { - E2D_LOG_DEBUG("No valid cache found, compiling shader from source: {}", name); - shader = factory_->createFromSource(name, result.vertSource, result.fragSource); - if (!shader) { - E2D_LOG_ERROR("Failed to create shader from source: {}", name); - return nullptr; - } - - std::vector binary; - if (factory_->getShaderBinary(*shader, binary)) { - E2D_LOG_DEBUG("Got shader binary, size: {} bytes", binary.size()); - ShaderCacheEntry entry; - entry.name = name; - entry.sourceHash = sourceHash; - entry.binary = binary; - entry.dependencies = result.dependencies; - ShaderCache::getInstance().saveCache(entry); - } else { - E2D_LOG_WARN("Failed to get shader binary for: {}", name); - } - } - - ShaderInfo info; - info.shader = shader; - info.vertSource = result.vertSource; - info.fragSource = result.fragSource; - info.filePaths = {path}; - info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(), result.dependencies.end()); - info.metadata = metadata; - - shaders_[name] = std::move(info); - - if (hotReloadEnabled_ && hotReloadSupported_) { - auto callback = [this, name](const FileChangeEvent& event) { - this->handleFileChange(name, event); - }; - ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths, callback); - } - - E2D_LOG_DEBUG("Shader loaded from combined file: {}", name); - return shader; + std::string name = path.substr(path.find_last_of("/\\") + 1); + return loadFromSource(name, result.vertSource, result.fragSource); } -/** - * @brief 从源码加载Shader - * @param name Shader名称 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 加载的Shader实例 - */ Ptr ShaderManager::loadFromSource(const std::string& name, const std::string& vertSource, const std::string& fragSource) { - if (!initialized_) { - E2D_LOG_ERROR("ShaderManager not initialized"); + if (!factory_) { + E2D_LOG_ERROR("Shader factory not initialized"); return nullptr; } - auto it = shaders_.find(name); - if (it != shaders_.end()) { - return it->second.shader; + if (has(name)) { + E2D_LOG_WARN("Shader already exists: {}", name); + return get(name); } - Ptr shader = factory_->createFromSource(name, vertSource, fragSource); + auto shader = factory_->createFromSource(name, vertSource, fragSource); if (!shader) { - E2D_LOG_ERROR("Failed to create shader from source: {}", name); + E2D_LOG_ERROR("Failed to create shader: {}", name); return nullptr; } @@ -281,19 +145,13 @@ Ptr ShaderManager::loadFromSource(const std::string& name, info.shader = shader; info.vertSource = vertSource; info.fragSource = fragSource; - info.metadata.name = name; - + shaders_[name] = std::move(info); - E2D_LOG_DEBUG("Shader loaded from source: {}", name); + E2D_LOG_INFO("Shader loaded: {}", name); return shader; } -/** - * @brief 获取已加载的Shader - * @param name Shader名称 - * @return Shader实例,不存在返回nullptr - */ Ptr ShaderManager::get(const std::string& name) const { auto it = shaders_.find(name); if (it != shaders_.end()) { @@ -302,232 +160,118 @@ Ptr ShaderManager::get(const std::string& name) const { return nullptr; } -/** - * @brief 检查Shader是否存在 - * @param name Shader名称 - * @return 存在返回true,否则返回false - */ bool ShaderManager::has(const std::string& name) const { return shaders_.find(name) != shaders_.end(); } -/** - * @brief 移除Shader - * @param name Shader名称 - */ void ShaderManager::remove(const std::string& name) { auto it = shaders_.find(name); if (it != shaders_.end()) { - ShaderHotReloader::getInstance().unwatch(name); shaders_.erase(it); - E2D_LOG_DEBUG("Shader removed: {}", name); + E2D_LOG_INFO("Shader removed: {}", name); } } -/** - * @brief 清除所有Shader - */ void ShaderManager::clear() { - if (hotReloadSupported_) { - for (const auto& pair : shaders_) { - ShaderHotReloader::getInstance().unwatch(pair.first); - } - } shaders_.clear(); - E2D_LOG_DEBUG("All shaders cleared"); + E2D_LOG_INFO("All shaders cleared"); } -/** - * @brief 注册重载回调 - * @param name Shader名称 - * @param callback 重载回调函数 - */ void ShaderManager::setReloadCallback(const std::string& name, ShaderReloadCallback callback) { auto it = shaders_.find(name); if (it != shaders_.end()) { - it->second.reloadCallback = callback; + it->second.reloadCallback = std::move(callback); } } -/** - * @brief 启用/禁用热重载 - * @param enabled 是否启用 - */ void ShaderManager::setHotReloadEnabled(bool enabled) { - if (!hotReloadSupported_) { + if (!hotReloadSupported_ && enabled) { E2D_LOG_WARN("Hot reload not supported on this platform"); return; } hotReloadEnabled_ = enabled; - ShaderHotReloader::getInstance().setEnabled(enabled); - E2D_LOG_INFO("Hot reload {}", enabled ? "enabled" : "disabled"); } -/** - * @brief 检查热重载是否启用 - * @return 启用返回true,否则返回false - */ bool ShaderManager::isHotReloadEnabled() const { - return hotReloadEnabled_ && hotReloadSupported_; + return hotReloadEnabled_; } -/** - * @brief 更新热重载系统(主循环调用) - */ void ShaderManager::update() { - if (hotReloadEnabled_ && hotReloadSupported_) { - ShaderHotReloader::getInstance().update(); + if (!hotReloadEnabled_ || !hotReloadSupported_) { + return; } + ShaderHotReloader::getInstance().update(); } -/** - * @brief 手动重载Shader - * @param name Shader名称 - * @return 重载成功返回true,失败返回false - */ bool ShaderManager::reload(const std::string& name) { auto it = shaders_.find(name); if (it == shaders_.end()) { - E2D_LOG_WARN("Shader not found for reload: {}", name); + E2D_LOG_ERROR("Shader not found: {}", name); return false; } - ShaderInfo& info = it->second; - - std::string vertSource = info.vertSource; - std::string fragSource = info.fragSource; - - if (!info.metadata.vertPath.empty() && !info.metadata.fragPath.empty()) { - ShaderLoadResult result = loader_.loadFromSeparateFiles(name, info.metadata.vertPath, info.metadata.fragPath); - if (result.success) { - vertSource = result.vertSource; - fragSource = result.fragSource; - } - } else if (!info.metadata.combinedPath.empty()) { - ShaderLoadResult result = loader_.loadFromCombinedFile(info.metadata.combinedPath); - if (result.success) { - vertSource = result.vertSource; - fragSource = result.fragSource; - } - } - - Ptr newShader = factory_->createFromSource(name, vertSource, fragSource); - if (!newShader) { + auto shader = factory_->createFromSource(name, it->second.vertSource, it->second.fragSource); + if (!shader) { E2D_LOG_ERROR("Failed to reload shader: {}", name); return false; } - info.shader = newShader; - info.vertSource = vertSource; - info.fragSource = fragSource; - - if (info.reloadCallback) { - info.reloadCallback(newShader); + it->second.shader = shader; + + if (it->second.reloadCallback) { + it->second.reloadCallback(shader); } E2D_LOG_INFO("Shader reloaded: {}", name); return true; } -/** - * @brief 获取内置Shader - * @param name 内置Shader名称 - * @return Shader实例 - */ Ptr ShaderManager::getBuiltin(const std::string& name) { - Ptr shader = get(name); - if (shader) { - return shader; - } - - std::string path = shaderDir_ + "builtin/" + name + ".shader"; - return loadFromCombinedFile(path); + return get("builtin_" + name); } -/** - * @brief 加载所有内置Shader - * @return 加载成功返回true,失败返回false - */ bool ShaderManager::loadBuiltinShaders() { - if (!initialized_) { - E2D_LOG_ERROR("ShaderManager not initialized"); - return false; - } - - bool allSuccess = true; - - const char* builtinNames[] = { + E2D_LOG_INFO("Loading builtin shaders..."); + + const std::vector builtinShaders = { "sprite", - "particle", "shape", - "postprocess", - "font" + "font", + "particle", + "postprocess" }; - - for (const char* name : builtinNames) { + + int loaded = 0; + for (const auto& name : builtinShaders) { std::string path = shaderDir_ + "builtin/" + name + ".shader"; - std::string shaderName = std::string("builtin_") + name; - - if (!loadFromCombinedFile(path)) { - E2D_LOG_ERROR("Failed to load builtin {} shader from: {}", name, path); - allSuccess = false; - } else { - auto it = shaders_.find(name); - if (it != shaders_.end()) { - shaders_[shaderName] = it->second; - shaders_.erase(it); + if (ShaderLoader::fileExists(path)) { + auto result = loader_.loadFromCombinedFile(path); + if (result.success) { + auto shader = factory_->createFromSource("builtin_" + name, result.vertSource, result.fragSource); + if (shader) { + ShaderInfo info; + info.shader = shader; + info.vertSource = result.vertSource; + info.fragSource = result.fragSource; + shaders_["builtin_" + name] = std::move(info); + loaded++; + E2D_LOG_DEBUG("Loaded builtin shader: {}", name); + } } } } - - if (allSuccess) { - E2D_LOG_INFO("All builtin shaders loaded"); - } - - return allSuccess; + + E2D_LOG_INFO("Builtin shaders loaded: {}/{}", loaded, builtinShaders.size()); + return loaded > 0; } -/** - * @brief 从缓存加载Shader - * @param name Shader名称 - * @param sourceHash 源码哈希值 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return Shader实例 - */ -Ptr ShaderManager::loadFromCache(const std::string& name, - const std::string& sourceHash, - const std::string& vertSource, - const std::string& fragSource) { - if (!ShaderCache::getInstance().isInitialized()) { - return nullptr; - } - - if (!ShaderCache::getInstance().hasValidCache(name, sourceHash)) { - return nullptr; - } - - Ptr entry = ShaderCache::getInstance().loadCache(name); - if (!entry || entry->binary.empty()) { - return nullptr; - } - - Ptr shader = factory_->createFromBinary(name, entry->binary); - if (shader) { - E2D_LOG_DEBUG("Shader loaded from cache: {}", name); - } - - return shader; +void ShaderManager::createBuiltinShaderSources() { } -/** - * @brief 处理文件变化事件 - * @param shaderName Shader名称 - * @param event 文件变化事件 - */ void ShaderManager::handleFileChange(const std::string& shaderName, const FileChangeEvent& event) { - E2D_LOG_DEBUG("Shader file changed: {} -> {}", shaderName, event.filepath); - reload(shaderName); + if (hotReloadEnabled_) { + reload(shaderName); + } } -} // namespace extra2d +} diff --git a/Extra2D/src/modules/config_module.cpp b/Extra2D/src/modules/config_module.cpp index f17630e..5ac6247 100644 --- a/Extra2D/src/modules/config_module.cpp +++ b/Extra2D/src/modules/config_module.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace extra2d { @@ -51,3 +52,5 @@ void ConfigModule::destroyModule() { } } // namespace extra2d + +E2D_MODULE(ConfigModule, 0) diff --git a/Extra2D/src/modules/input_module.cpp b/Extra2D/src/modules/input_module.cpp index 13d7f60..706de65 100644 --- a/Extra2D/src/modules/input_module.cpp +++ b/Extra2D/src/modules/input_module.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -24,8 +25,18 @@ void InputModule::setupModule() { return; } + // 如果 window 还没设置,延迟初始化 + if (!window_) { + E2D_LOG_INFO("Input module waiting for window"); + setInitialized(true); + return; + } + + initializeWithWindow(); +} + +void InputModule::initializeWithWindow() { if (!window_) { - E2D_LOG_ERROR("Window not set - cannot initialize input module"); return; } @@ -49,13 +60,24 @@ void InputModule::setupModule() { } } - setInitialized(true); E2D_LOG_INFO("Input module initialized"); E2D_LOG_INFO(" Deadzone: {}", config_.deadzone); E2D_LOG_INFO(" Mouse sensitivity: {}", config_.mouseSensitivity); E2D_LOG_INFO(" Vibration: {}", config_.enableVibration ? "enabled" : "disabled"); } +void InputModule::setWindow(IWindow* window) { + if (window_ == window) { + return; + } + window_ = window; + + // 如果模块已初始化但还没初始化输入,现在初始化 + if (isInitialized() && window_ && !input_) { + initializeWithWindow(); + } +} + void InputModule::destroyModule() { if (!isInitialized()) { return; @@ -78,3 +100,5 @@ void InputModule::onUpdate(UpdateContext& ctx) { } } // namespace extra2d + +E2D_MODULE(InputModule, 30, "WindowModule") diff --git a/Extra2D/src/modules/logger_module.cpp b/Extra2D/src/modules/logger_module.cpp deleted file mode 100644 index 35bd9ea..0000000 --- a/Extra2D/src/modules/logger_module.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include - -namespace extra2d { - -LoggerModule::LoggerModule() - : Module() - , logLevel_(LogLevel::Info) - , consoleOutput_(true) - , fileOutput_(false) { -} - -LoggerModule::~LoggerModule() { - if (isInitialized()) { - destroyModule(); - } -} - -void LoggerModule::setupModule() { - if (isInitialized()) { - return; - } - - Logger::init(); - - Logger::setLevel(logLevel_); - Logger::setConsoleOutput(consoleOutput_); - - if (fileOutput_ && !logFilePath_.empty()) { - Logger::setFileOutput(logFilePath_); - } - - setInitialized(true); - E2D_LOG_INFO("Logger module initialized"); -} - -void LoggerModule::destroyModule() { - if (!isInitialized()) { - return; - } - - E2D_LOG_INFO("Logger module shutting down"); - Logger::shutdown(); - setInitialized(false); -} - -} // namespace extra2d diff --git a/Extra2D/src/modules/platform_module.cpp b/Extra2D/src/modules/platform_module.cpp deleted file mode 100644 index 102b3da..0000000 --- a/Extra2D/src/modules/platform_module.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include -#include -#include - -#ifdef __SWITCH__ -#include -#endif - -namespace extra2d { - -PlatformModule::PlatformModule() - : Module() - , targetPlatform_(PlatformType::Auto) - , resolvedPlatform_(PlatformType::Windows) { -} - -PlatformModule::~PlatformModule() { - if (isInitialized()) { - destroyModule(); - } -} - -void PlatformModule::setupModule() { - if (isInitialized()) { - return; - } - - resolvedPlatform_ = targetPlatform_; - if (resolvedPlatform_ == PlatformType::Auto) { -#ifdef __SWITCH__ - resolvedPlatform_ = PlatformType::Switch; -#else -#ifdef _WIN32 - resolvedPlatform_ = PlatformType::Windows; -#elif defined(__linux__) - resolvedPlatform_ = PlatformType::Linux; -#elif defined(__APPLE__) - resolvedPlatform_ = PlatformType::macOS; -#else - resolvedPlatform_ = PlatformType::Windows; -#endif -#endif - } - - platformConfig_ = createPlatformConfig(resolvedPlatform_); - if (!platformConfig_) { - E2D_LOG_ERROR("Failed to create platform config"); - return; - } - - if (resolvedPlatform_ == PlatformType::Switch) { - if (!initSwitch()) { - return; - } - } - - setInitialized(true); - E2D_LOG_INFO("Platform module initialized ({})", getPlatformTypeName(resolvedPlatform_)); -} - -void PlatformModule::destroyModule() { - if (!isInitialized()) { - return; - } - - E2D_LOG_INFO("Platform module shutting down"); - - if (resolvedPlatform_ == PlatformType::Switch) { - shutdownSwitch(); - } - - platformConfig_.reset(); - setInitialized(false); -} - -bool PlatformModule::initSwitch() { -#ifdef __SWITCH__ - Result rc; - rc = romfsInit(); - if (R_SUCCEEDED(rc)) { - E2D_LOG_INFO("RomFS initialized successfully"); - } else { - E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem", rc); - } - - rc = socketInitializeDefault(); - if (R_FAILED(rc)) { - E2D_LOG_WARN("socketInitializeDefault failed, nxlink will not be available"); - } -#endif - return true; -} - -void PlatformModule::shutdownSwitch() { -#ifdef __SWITCH__ - romfsExit(); - socketExit(); -#endif -} - -} // namespace extra2d diff --git a/Extra2D/src/modules/render_module.cpp b/Extra2D/src/modules/render_module.cpp index af25840..c8e13e6 100644 --- a/Extra2D/src/modules/render_module.cpp +++ b/Extra2D/src/modules/render_module.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -29,8 +30,18 @@ void RenderModule::setupModule() { return; } + // 如果 window 还没设置,延迟初始化 + if (!window_) { + E2D_LOG_INFO("Render module waiting for window"); + setInitialized(true); + return; + } + + initializeWithWindow(); +} + +void RenderModule::initializeWithWindow() { if (!window_) { - E2D_LOG_ERROR("Render module requires window to be set"); return; } @@ -55,10 +66,21 @@ void RenderModule::setupModule() { return; } - setInitialized(true); E2D_LOG_INFO("Render module initialized"); } +void RenderModule::setWindow(IWindow* window) { + if (window_ == window) { + return; + } + window_ = window; + + // 如果模块已初始化但还没初始化渲染器,现在初始化 + if (isInitialized() && window_ && !renderer_) { + initializeWithWindow(); + } +} + void RenderModule::destroyModule() { if (!isInitialized()) { return; @@ -96,3 +118,5 @@ void RenderModule::afterRender(RenderContext& ctx) { } } // namespace extra2d + +E2D_MODULE(RenderModule, 40, "WindowModule") diff --git a/Extra2D/src/modules/window_module.cpp b/Extra2D/src/modules/window_module.cpp index 1852837..4f79c67 100644 --- a/Extra2D/src/modules/window_module.cpp +++ b/Extra2D/src/modules/window_module.cpp @@ -1,5 +1,6 @@ #include -#include +#include +#include #include #include @@ -119,3 +120,5 @@ bool WindowModule::createWindow(const WindowConfigData& config) { } } // namespace extra2d + +E2D_MODULE(WindowModule, 20, "ConfigModule") diff --git a/Extra2D/src/platform/platform_module.cpp b/Extra2D/src/platform/backend_factory.cpp similarity index 75% rename from Extra2D/src/platform/platform_module.cpp rename to Extra2D/src/platform/backend_factory.cpp index 93a704c..ad872f0 100644 --- a/Extra2D/src/platform/platform_module.cpp +++ b/Extra2D/src/platform/backend_factory.cpp @@ -1,4 +1,4 @@ -#include +#include namespace extra2d { @@ -12,18 +12,16 @@ void BackendFactory::reg(const std::string& name, WindowFn win, InputFn in) { } UniquePtr BackendFactory::createWindow(const std::string& name) { - auto& reg = registry(); - auto it = reg.find(name); - if (it != reg.end() && it->second.windowFn) { + auto it = registry().find(name); + if (it != registry().end() && it->second.windowFn) { return it->second.windowFn(); } return nullptr; } UniquePtr BackendFactory::createInput(const std::string& name) { - auto& reg = registry(); - auto it = reg.find(name); - if (it != reg.end() && it->second.inputFn) { + auto it = registry().find(name); + if (it != registry().end() && it->second.inputFn) { return it->second.inputFn(); } return nullptr; @@ -41,4 +39,4 @@ bool BackendFactory::has(const std::string& name) { return registry().find(name) != registry().end(); } -} // namespace extra2d +} diff --git a/Extra2D/src/platform/backends/sdl2/sdl2_backend.cpp b/Extra2D/src/platform/backends/sdl2/sdl2_backend.cpp index 3e82a54..483aecf 100644 --- a/Extra2D/src/platform/backends/sdl2/sdl2_backend.cpp +++ b/Extra2D/src/platform/backends/sdl2/sdl2_backend.cpp @@ -1,6 +1,6 @@ #include "sdl2_window.h" #include "sdl2_input.h" -#include +#include namespace extra2d { diff --git a/Extra2D/src/platform/backends/sdl2/sdl2_input.cpp b/Extra2D/src/platform/backends/sdl2/sdl2_input.cpp index 27bbd3b..a2f2e0d 100644 --- a/Extra2D/src/platform/backends/sdl2/sdl2_input.cpp +++ b/Extra2D/src/platform/backends/sdl2/sdl2_input.cpp @@ -4,7 +4,8 @@ namespace extra2d { -SDL2Input::SDL2Input() { +SDL2Input::SDL2Input() + : initialized_(false) { keyCurrent_.fill(false); keyPrevious_.fill(false); mouseCurrent_.fill(false); @@ -18,6 +19,8 @@ SDL2Input::~SDL2Input() { } void SDL2Input::init() { + if (initialized_) return; + E2D_LOG_INFO("SDL2Input initialized"); if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0) { @@ -25,10 +28,14 @@ void SDL2Input::init() { } openGamepad(); + initialized_ = true; } void SDL2Input::shutdown() { + if (!initialized_) return; + closeGamepad(); + initialized_ = false; E2D_LOG_INFO("SDL2Input shutdown"); } diff --git a/Extra2D/src/platform/backends/sdl2/sdl2_input.h b/Extra2D/src/platform/backends/sdl2/sdl2_input.h index df52122..8a6a346 100644 --- a/Extra2D/src/platform/backends/sdl2/sdl2_input.h +++ b/Extra2D/src/platform/backends/sdl2/sdl2_input.h @@ -98,6 +98,7 @@ private: float rightTrigger_ = 0.0f; float deadzone_ = 0.15f; + bool initialized_ = false; EventCallback eventCallback_; }; diff --git a/README.md b/README.md index 1d1827c..c993b25 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,30 @@ flowchart TB | Input | 输入处理 | 30 | | Render | 渲染系统 | 40 | +### 模块自发现原理 + +```mermaid +sequenceDiagram + participant C as 编译时 + participant R as 运行时 + participant MR as ModuleRegistry + participant App as Application + + Note over C: E2D_MODULE 宏展开 + C->>C: 创建静态变量 ModuleAutoRegister + C->>C: 添加 __attribute__((used)) 防止优化 + + Note over R: 程序启动 + R->>MR: 静态初始化阶段 + MR->>MR: 构造函数自动调用 registerModule() + MR->>MR: 模块信息存入注册表 + + Note over App: Application::init() + App->>MR: getModules() + MR->>App: 返回按优先级排序的模块列表 + App->>App: 按顺序调用 setupModule() +``` + ### 服务系统 | 服务 | 职责 | 优先级 | diff --git a/docs/module_system.md b/docs/module_system.md index f2943f0..0eac41e 100644 --- a/docs/module_system.md +++ b/docs/module_system.md @@ -4,33 +4,267 @@ Extra2D 采用模块化架构设计(参考 Kiwano),所有核心功能通过模块系统和服务系统管理。系统提供: +- **自动发现注册**:模块定义即注册,无需手动调用 - **统一的生命周期管理**:初始化、更新、渲染、关闭 - **优先级排序**:确保模块按正确顺序初始化 -- **显式注册**:通过 `Application::use()` 注册模块 - **Context 模式**:使用上下文对象遍历模块链 - **依赖注入**:通过服务定位器解耦模块间依赖 ## 架构图 +```mermaid +graph TB + Application["Application
(协调模块和服务)"] + + Application --> ModuleRegistry + Application --> ServiceLocator + + ModuleRegistry["ModuleRegistry
(模块注册器)"] + ServiceLocator["ServiceLocator
(服务定位器)"] + + ModuleRegistry --> ConfigModule["ConfigModule"] + ModuleRegistry --> WindowModule["WindowModule"] + + ServiceLocator --> SceneService["SceneService"] + ServiceLocator --> TimerService["TimerService"] ``` -┌─────────────────────────────────────────────────────────────┐ -│ Application │ -│ (协调模块和服务,通过服务定位器获取依赖) │ -└─────────────────────────────────────────────────────────────┘ - │ - ┌───────────────┴───────────────┐ - ▼ ▼ -┌─────────────────────────────┐ ┌─────────────────────────────┐ -│ Modules │ │ ServiceLocator │ -│ (模块列表,按优先级排序) │ │ (服务定位器,管理运行时服务) │ -└─────────────────────────────┘ └─────────────────────────────┘ - │ │ - ┌─────┴─────┐ ┌───────┴───────┐ - ▼ ▼ ▼ ▼ -┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ -│ Config │ │ Window │ │ Scene │ │ Timer │ -│ Module │ │ Module │ │ Service │ │ Service │ -└───────────┘ └───────────┘ └───────────┘ └───────────┘ + +--- + +## 模块自动注册 + +### E2D_MODULE 宏 + +模块通过 `E2D_MODULE` 宏自动注册,无需手动调用任何注册函数: + +```cpp +// config_module.cpp +#include "config_module.h" + +namespace extra2d { +// 模块实现... +} + +// 在文件末尾,namespace 外部 +E2D_MODULE(ConfigModule, 0) // 名称, 优先级 +``` + +### 带依赖的模块 + +```cpp +// window_module.cpp +E2D_MODULE(WindowModule, 20, "ConfigModule") // 依赖 ConfigModule + +// render_module.cpp +E2D_MODULE(RenderModule, 40, "WindowModule", "ConfigModule") // 多个依赖 +``` + +### 带属性的模块 + +```cpp +// render_module.cpp +E2D_MODULE_BEGIN(RenderModule) +E2D_PRIORITY(40) +E2D_DEPENDENCIES("WindowModule") + E2D_PROPERTY(vsync, bool) + E2D_PROPERTY(targetFPS, int) +E2D_MODULE_END() +``` + +--- + +## 链接方式详解 + +### 静态链接 vs 动态链接 + +| 特性 | 静态链接 | 动态链接 | +|------|---------|---------| +| **引擎库** | `.a` / `.lib` | `.dll` / `.so` | +| **模块注册** | 需要 `--whole-archive` 或直接编译 | 自动注册 | +| **自定义模块** | 直接编译到可执行文件 | 编译为独立 DLL | +| **额外代码** | 无 | 需要 `E2D_FORCE_LINK` | +| **分发** | 单一可执行文件 | 需要 DLL 文件 | +| **热更新** | 不支持 | 支持(重新加载 DLL) | + +### 静态链接方案(推荐) + +#### 原理 + +静态链接时,链接器会优化掉未引用的代码。解决方案: +1. **引擎库**:使用 `--whole-archive` 强制链接所有符号 +2. **自定义模块**:直接编译到可执行文件 + +#### xmake 配置 + +```lua +-- 引擎库(静态库) +target("extra2d") + set_kind("static") + -- ... 其他配置 + +-- 可执行文件 +target("demo_basic") + set_kind("binary") + + -- 强制链接引擎静态库 + add_ldflags("-Wl,--whole-archive", {force = true}) + add_deps("extra2d") + add_ldflags("-Wl,--no-whole-archive", {force = true}) + + add_files("main.cpp") +``` + +#### 自定义模块配置 + +```lua +target("demo_hello_module") + set_kind("binary") + + -- 强制链接引擎静态库 + add_ldflags("-Wl,--whole-archive", {force = true}) + add_deps("extra2d") + add_ldflags("-Wl,--no-whole-archive", {force = true}) + + -- 直接编译模块源文件到可执行文件 + add_files("main.cpp", "hello_module.cpp") + add_includedirs("hello_module") +``` + +#### 模块定义 + +```cpp +// hello_module.cpp +#include "hello_module.h" + +namespace extra2d { +// 模块实现... +} + +// 在文件末尾,namespace 外部 +E2D_MODULE(HelloModule, 1000) +``` + +#### main.cpp + +```cpp +#include "hello_module.h" +#include + +int main() { + extra2d::Application& app = extra2d::Application::get(); + + // 无需任何注册代码! + // 模块在静态初始化时自动注册 + + extra2d::AppConfig config; + config.appName = "My App"; + + if (!app.init(config)) return 1; + app.run(); + return 0; +} +``` + +### 动态链接方案 + +#### xmake 配置 + +```lua +-- 引擎库(动态库) +target("extra2d") + set_kind("shared") + add_defines("E2D_BUILDING_DLL", {public = false}) + -- ... 其他配置 + +-- 自定义模块 DLL +target("hello_module_lib") + set_kind("shared") + add_defines("E2D_BUILDING_DLL", {public = false}) + add_deps("extra2d") + add_files("hello_module.cpp") + add_includedirs("hello_module", {public = true}) + +-- 可执行文件 +target("demo_hello_module") + set_kind("binary") + add_deps("hello_module_lib") + add_files("main.cpp") +``` + +#### 模块定义 + +```cpp +// hello_module.cpp +#include "hello_module.h" + +namespace extra2d { +// 模块实现... +} + +// 使用 E2D_MODULE_EXPORT(自动生成导出函数) +E2D_MODULE_EXPORT(HelloModule, 1000) +``` + +#### main.cpp + +```cpp +#include "hello_module.h" +#include + +// 声明外部模块的导出函数 +E2D_DECLARE_FORCE_LINK(HelloModule); + +int main() { + // 触发 DLL 加载 + E2D_CALL_FORCE_LINK(HelloModule); + + extra2d::Application& app = extra2d::Application::get(); + if (!app.init(config)) return 1; + app.run(); + return 0; +} +``` + +--- + +## 平台兼容性 + +### Linux + +```lua +add_ldflags("-Wl,--whole-archive", {force = true}) +add_deps("extra2d") +add_ldflags("-Wl,--no-whole-archive", {force = true}) +``` + +### macOS + +macOS 使用 `-force_load` 代替 `--whole-archive`: + +```lua +if is_plat("macosx") then + add_ldflags("-force_load", {force = true}) +end +add_deps("extra2d") +``` + +### Windows (MSVC) + +MSVC 使用 `/WHOLEARCHIVE`: + +```lua +if is_plat("windows") and is_toolchain("msvc") then + add_ldflags("/WHOLEARCHIVE:extra2d", {force = true}) +end +add_deps("extra2d") +``` + +### Windows (MinGW) + +```lua +add_ldflags("-Wl,--whole-archive", {force = true}) +add_deps("extra2d") +add_ldflags("-Wl,--no-whole-archive", {force = true}) ``` --- @@ -139,7 +373,7 @@ class EventContext : public ModuleContext { |-----|--------------|---------------| | 用途 | 平台级初始化 | 运行时功能 | | 生命周期 | Application 管理 | ServiceLocator 管理 | -| 注册方式 | `app.use(module)` | `locator.registerService()` | +| 注册方式 | 自动发现 | `locator.registerService()` | | 可替换性 | 编译时确定 | 运行时可替换 | | 示例 | Window, Render, Input | Scene, Timer, Event, Camera | @@ -164,116 +398,78 @@ class EventContext : public ModuleContext { ## 创建新模块 -### 简单示例 +### 完整示例(静态链接) +**hello_module.h:** ```cpp -#include - -class MyModule : public extra2d::Module { -public: - const char* getName() const override { return "MyModule"; } - - int getPriority() const override { return 1000; } - - void setupModule() override { - // 初始化资源 - extra2d::E2D_LOG_INFO("MyModule initialized"); - } - - void destroyModule() override { - // 清理资源 - extra2d::E2D_LOG_INFO("MyModule destroyed"); - } - - void onUpdate(extra2d::UpdateContext& ctx) override { - // 更新逻辑 - ctx.next(); // 继续下一个模块 - } -}; -``` - -### 注册模块 - -```cpp -int main() { - auto& app = extra2d::Application::get(); - - MyModule myModule; - app.use(myModule); // 显式注册 - - app.init(); - app.run(); - return 0; -} -``` - -### 带配置的模块 - -```cpp -// my_module.h #pragma once - #include #include -struct MyModuleConfig { +namespace extra2d { + +struct HelloModuleConfig { std::string greeting = "Hello!"; int repeatCount = 1; }; -class MyModule : public extra2d::Module { +class HelloModule : public Module { public: - MyModule(); - ~MyModule() override; + HelloModule(); + ~HelloModule() override; - const char* getName() const override { return "MyModule"; } + const char* getName() const override { return "HelloModule"; } int getPriority() const override { return 1000; } void setupModule() override; void destroyModule() override; - void onUpdate(extra2d::UpdateContext& ctx) override; + void onUpdate(UpdateContext& ctx) override; - void setConfig(const MyModuleConfig& config) { config_ = config; } + void setConfig(const HelloModuleConfig& config) { config_ = config; } void sayHello() const; private: - MyModuleConfig config_; + HelloModuleConfig config_; float time_ = 0.0f; }; + +} // namespace extra2d ``` +**hello_module.cpp:** ```cpp -// my_module.cpp -#include "my_module.h" +#include "hello_module.h" #include -MyModule::MyModule() : Module() {} +namespace extra2d { -MyModule::~MyModule() { +HelloModule::HelloModule() = default; + +HelloModule::~HelloModule() { if (isInitialized()) { destroyModule(); } } -void MyModule::setupModule() { +void HelloModule::setupModule() { if (isInitialized()) return; setInitialized(true); - E2D_LOG_INFO("MyModule initialized"); + E2D_LOG_INFO("HelloModule initialized"); E2D_LOG_INFO(" Greeting: {}", config_.greeting); E2D_LOG_INFO(" Repeat Count: {}", config_.repeatCount); sayHello(); } -void MyModule::destroyModule() { +void HelloModule::destroyModule() { if (!isInitialized()) return; - E2D_LOG_INFO("MyModule shutdown"); + E2D_LOG_INFO("HelloModule shutdown"); setInitialized(false); } -void MyModule::onUpdate(extra2d::UpdateContext& ctx) { +void HelloModule::onUpdate(UpdateContext& ctx) { if (!isInitialized()) { ctx.next(); return; @@ -289,11 +485,49 @@ void MyModule::onUpdate(extra2d::UpdateContext& ctx) { ctx.next(); } -void MyModule::sayHello() const { +void HelloModule::sayHello() const { for (int i = 0; i < config_.repeatCount; ++i) { - E2D_LOG_INFO("[MyModule] {}", config_.greeting); + E2D_LOG_INFO("[HelloModule] {}", config_.greeting); } } + +} // namespace extra2d + +// 自动注册(在 namespace 外部) +E2D_MODULE(HelloModule, 1000) +``` + +**main.cpp:** +```cpp +#include "hello_module.h" +#include + +int main() { + extra2d::Application& app = extra2d::Application::get(); + + extra2d::AppConfig config; + config.appName = "Hello Module Demo"; + + // 无需手动注册!模块已自动注册 + + if (!app.init(config)) return 1; + app.run(); + return 0; +} +``` + +**xmake.lua:** +```lua +target("demo_hello_module") + set_kind("binary") + + -- 强制链接引擎静态库 + add_ldflags("-Wl,--whole-archive", {force = true}) + add_deps("extra2d") + add_ldflags("-Wl,--no-whole-archive", {force = true}) + + -- 直接编译模块源文件 + add_files("main.cpp", "hello_module.cpp") ``` --- @@ -314,35 +548,6 @@ config.appVersion = "1.0.0"; --- -### Logger 模块 - -**职责**:日志系统初始化 - -**优先级**:-1(最先初始化) - -```cpp -extra2d::LoggerModule loggerModule; -loggerModule.setLogLevel(extra2d::LogLevel::Debug); -loggerModule.setFileOutput("app.log"); -app.use(loggerModule); -``` - ---- - -### Platform 模块 - -**职责**:平台检测和平台特定初始化 - -**优先级**:10 - -**支持平台**: -- Windows -- Linux -- macOS -- Nintendo Switch - ---- - ### Window 模块 **职责**:窗口创建和管理 @@ -351,17 +556,6 @@ app.use(loggerModule); **后端**:统一使用 SDL2 -```cpp -extra2d::WindowModule windowModule; -extra2d::WindowConfigData windowConfig; -windowConfig.title = "My App"; -windowConfig.width = 1280; -windowConfig.height = 720; -windowConfig.vsync = true; -windowModule.setWindowConfig(windowConfig); -app.use(windowModule); -``` - --- ### Input 模块 @@ -370,15 +564,6 @@ app.use(windowModule); **优先级**:30 -```cpp -extra2d::InputModule inputModule; -extra2d::InputConfigData inputConfig; -inputConfig.deadzone = 0.15f; -inputConfig.enableVibration = true; -inputModule.setInputConfig(inputConfig); -app.use(inputModule); -``` - --- ### Render 模块 @@ -387,15 +572,6 @@ app.use(inputModule); **优先级**:40 -```cpp -extra2d::RenderModule renderModule; -extra2d::RenderModuleConfig renderConfig; -renderConfig.vsync = true; -renderConfig.multisamples = 4; -renderModule.setRenderConfig(renderConfig); -app.use(renderModule); -``` - --- ## 服务系统 @@ -558,48 +734,37 @@ auto triangle = ShapeNode::createFilledTriangle( ); ``` -### 变换继承 - -```cpp -auto parent = makeShared(); -parent->setPos(100, 100); -parent->setRotation(45); - -auto child = makeShared(); -child->setPos(50, 0); // 相对于父节点 -parent->addChild(child); - -// child 会随 parent 一起旋转 -``` - --- -## 视口适配系统 +## 常见问题 -### ViewportAdapter +### Q: 模块没有被注册? -```cpp -enum class ViewportMode { - AspectRatio, // 保持宽高比,可能有黑边 - Stretch, // 拉伸填满整个窗口 - Center, // 居中显示,不缩放 - Custom // 自定义缩放和偏移 -}; +**静态链接:** +- 确保使用了 `--whole-archive` +- 自定义模块要直接编译到可执行文件 + +**动态链接:** +- 确保使用了 `E2D_MODULE_EXPORT` +- 确保调用了 `E2D_CALL_FORCE_LINK` + +### Q: 链接错误 "undefined reference"? + +检查链接顺序,`--whole-archive` 要在 `add_deps` 之前。 + +### Q: 模块初始化顺序错误? + +使用 `getPriority()` 控制顺序,数字小的先初始化。 + +### Q: 如何调试模块注册? + +查看日志输出: ``` - -### 使用 CameraService 配置视口 - -```cpp -auto cameraService = app.camera(); -if (cameraService) { - extra2d::ViewportConfig vpConfig; - vpConfig.logicWidth = 1280.0f; - vpConfig.logicHeight = 720.0f; - vpConfig.mode = extra2d::ViewportMode::AspectRatio; - - cameraService->setViewportConfig(vpConfig); - cameraService->updateViewport(windowWidth, windowHeight); -} +[INFO ] ModuleRegistry: 4 modules registered +[INFO ] - ConfigModule (priority: 0) +[INFO ] - WindowModule (priority: 20) +[INFO ] - InputModule (priority: 30) +[INFO ] - RenderModule (priority: 40) ``` --- @@ -653,3 +818,7 @@ void onUpdate(UpdateContext& ctx) override { ctx.next(); } ``` + +### 4. 静态链接优先 + +静态链接更简单,无需额外配置,推荐用于大多数场景。 diff --git a/examples/basic/main.cpp b/examples/basic/main.cpp index 5de72a1..04b7f72 100644 --- a/examples/basic/main.cpp +++ b/examples/basic/main.cpp @@ -7,6 +7,8 @@ * - 变换(位置、旋转、缩放) * - 形状节点渲染 * - 输入事件处理 + * + * 模块现在通过 E2D_MODULE 宏自动注册,无需手动调用 app.use() */ #include @@ -111,6 +113,8 @@ int main(int argc, char *argv[]) { Application &app = Application::get(); + // 模块已通过 E2D_MODULE 宏自动注册,无需调用 app.use() + if (!app.init(config)) { std::cerr << "Failed to initialize application!" << std::endl; return -1; @@ -166,9 +170,6 @@ int main(int argc, char *argv[]) { app.run(); - std::cout << "Shutting down..." << std::endl; - app.shutdown(); - std::cout << "Goodbye!" << std::endl; return 0; } diff --git a/examples/hello_module/hello_module.cpp b/examples/hello_module/hello_module.cpp index a85c669..f91896f 100644 --- a/examples/hello_module/hello_module.cpp +++ b/examples/hello_module/hello_module.cpp @@ -1,4 +1,5 @@ #include "hello_module.h" +#include #include namespace extra2d { @@ -77,3 +78,5 @@ void HelloModule::sayHello() const { } } // namespace extra2d + +E2D_MODULE(HelloModule, 1000) diff --git a/examples/hello_module/hello_module.h b/examples/hello_module/hello_module.h index 797e8df..4f87fb0 100644 --- a/examples/hello_module/hello_module.h +++ b/examples/hello_module/hello_module.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -9,69 +10,69 @@ namespace extra2d { * @brief Hello模块配置数据结构 */ struct HelloModuleConfigData { - std::string greeting = "Hello, Extra2D!"; - int repeatCount = 1; - bool enableLogging = true; + std::string greeting = "Hello, Extra2D!"; + int repeatCount = 1; + bool enableLogging = true; }; /** * @brief Hello模块 - * + * * 这是一个简单的自定义模块示例,展示如何: * 1. 继承 Module 基类 * 2. 实现生命周期方法 - * 3. 使用 Application::use() 注册模块 + * 3. 使用 E2D_MODULE 宏自动注册模块 */ class HelloModule : public Module { public: - /** - * @brief 构造函数 - */ - HelloModule(); + /** + * @brief 构造函数 + */ + HelloModule(); - /** - * @brief 析构函数 - */ - ~HelloModule() override; + /** + * @brief 析构函数 + */ + ~HelloModule() override; - /** - * @brief 获取模块名称 - */ - const char* getName() const override { return "HelloModule"; } + /** + * @brief 获取模块名称 + */ + const char *getName() const override { return "HelloModule"; } - /** - * @brief 获取模块优先级 - */ - int getPriority() const override { return 1000; } + /** + * @brief 获取模块优先级 + */ + int getPriority() const override { return 1000; } - /** - * @brief 设置模块 - */ - void setupModule() override; + /** + * @brief 设置模块 + */ + void setupModule() override; - /** - * @brief 销毁模块 - */ - void destroyModule() override; + /** + * @brief 销毁模块 + */ + void destroyModule() override; - /** - * @brief 更新时 - */ - void onUpdate(UpdateContext& ctx) override; + /** + * @brief 更新时 + */ + void onUpdate(UpdateContext &ctx) override; - /** - * @brief 设置配置 - */ - void setConfig(const HelloModuleConfigData& config) { config_ = config; } + /** + * @brief 设置配置 + */ + void setConfig(const HelloModuleConfigData &config) { config_ = config; } - /** - * @brief 执行问候操作 - */ - void sayHello() const; + /** + * @brief 执行问候操作 + */ + void sayHello() const; private: - HelloModuleConfigData config_; - float time_ = 0.0f; + HelloModuleConfigData config_; + float time_ = 0.0f; }; } // namespace extra2d diff --git a/examples/hello_module/main.cpp b/examples/hello_module/main.cpp index 83fcc53..37b5f16 100644 --- a/examples/hello_module/main.cpp +++ b/examples/hello_module/main.cpp @@ -16,10 +16,11 @@ public: Scene::onEnter(); addListener(EventType::KeyPress, [](Event &e) { auto &keyEvent = std::get(e.data); - + auto app = Application::get().getModule(); + E2D_LOG_INFO("Module {}", app->getName()); if (keyEvent.scancode == static_cast(Key::Escape)) { e.handled = true; - E2D_LOG_INFO("ESC !!!exit"); + E2D_LOG_INFO("ESC pressed, exiting..."); Application::get().quit(); } }); @@ -33,14 +34,15 @@ private: /** * @brief 应用程序入口 + * + * 静态链接时模块直接编译到可执行文件,自动注册,无需任何额外代码! */ int main(int argc, char *argv[]) { + (void)argc; + (void)argv; Application &app = Application::get(); - HelloModule helloModule; - app.use(helloModule); - AppConfig appConfig; appConfig.appName = "HelloModule Example"; appConfig.appVersion = "1.0.0"; @@ -55,7 +57,5 @@ int main(int argc, char *argv[]) { app.run(); - app.shutdown(); - return 0; } diff --git a/xmake.lua b/xmake.lua index f629793..a0258de 100644 --- a/xmake.lua +++ b/xmake.lua @@ -145,7 +145,11 @@ target("demo_basic") set_kind("binary") set_default(false) + -- 强制链接整个静态库(保留静态初始化的模块注册变量) + add_ldflags("-Wl,--whole-archive", {force = true}) add_deps("extra2d") + add_ldflags("-Wl,--no-whole-archive", {force = true}) + add_files("examples/basic/main.cpp") -- 平台配置 @@ -165,13 +169,42 @@ target("demo_basic") after_build(install_shaders) target_end() +-- Hello Module 静态库 - 自定义模块示例 +target("hello_module_lib") + set_kind("static") + set_default(false) + + add_deps("extra2d") + add_files("examples/hello_module/hello_module.cpp") + add_includedirs("examples/hello_module", {public = true}) + + -- 平台配置 + local plat = get_config("plat") or os.host() + if plat == "mingw" or plat == "windows" then + add_packages("glm", "nlohmann_json", "libsdl2") + add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi") + elseif plat == "linux" then + add_packages("glm", "nlohmann_json", "libsdl2") + add_syslinks("GL", "dl", "pthread") + elseif plat == "macosx" then + add_packages("glm", "nlohmann_json", "libsdl2") + add_frameworks("OpenGL", "Cocoa", "IOKit", "CoreVideo") + end +target_end() + -- Hello Module 示例 - 展示如何创建自定义模块 +-- 注意:静态链接时,自定义模块需要直接编译到可执行文件中 target("demo_hello_module") set_kind("binary") set_default(false) + -- 强制链接引擎静态库 + add_ldflags("-Wl,--whole-archive", {force = true}) add_deps("extra2d") - add_files("examples/hello_module/*.cpp") + add_ldflags("-Wl,--no-whole-archive", {force = true}) + + -- 直接编译模块源文件(静态链接时的推荐方式) + add_files("examples/hello_module/main.cpp", "examples/hello_module/hello_module.cpp") add_includedirs("examples/hello_module") -- 平台配置 diff --git a/xmake/engine.lua b/xmake/engine.lua index 8dcc08c..c9f1f3b 100644 --- a/xmake/engine.lua +++ b/xmake/engine.lua @@ -17,7 +17,7 @@ end -- 定义 Extra2D 引擎库目标 function define_extra2d_engine() target("extra2d") - set_kind("static") + set_kind("static") -- 改回静态库 -- 引擎核心源文件 add_files("Extra2D/src/**.cpp")