refactor(渲染): 迁移OpenGL到Vulkan并重构相关模块

- 移除OpenGL相关代码和依赖,添加Vulkan SDK支持
- 重构窗口模块为Vulkan实现,移除GL上下文和VSync相关功能
- 简化配置结构,移除不再需要的OpenGL版本设置
- 重构上下文模块,使用单例模块注册表
- 移动定时器模块到utils目录并优化实现
- 清理废弃代码和头文件引用
This commit is contained in:
ChestnutYueyue 2026-03-01 00:01:48 +08:00
parent f9be301dae
commit fb11f2a71e
13 changed files with 477 additions and 536 deletions

View File

@ -16,10 +16,7 @@ struct AppConfig {
int32 height = 720; // 窗口高度 int32 height = 720; // 窗口高度
bool fullscreen = false; // 是否全屏 bool fullscreen = false; // 是否全屏
bool resizable = true; // 是否可调整大小 bool resizable = true; // 是否可调整大小
bool vsync = true; // 是否垂直同步
int32 fpsLimit = 0; // FPS限制0表示不限制 int32 fpsLimit = 0; // FPS限制0表示不限制
int32 glMajor = 3; // OpenGL主版本
int32 glMinor = 3; // OpenGL次版本
bool enableCursors = true; // 启用光标 bool enableCursors = true; // 启用光标
bool enableDpiScale = false; // 启用DPI缩放 bool enableDpiScale = false; // 启用DPI缩放
}; };

View File

@ -16,9 +16,6 @@ struct WindowCfg {
int32 height = 720; // 窗口高度 int32 height = 720; // 窗口高度
bool fullscreen = false; // 是否全屏 bool fullscreen = false; // 是否全屏
bool resizable = true; // 是否可调整大小 bool resizable = true; // 是否可调整大小
bool vsync = true; // 是否垂直同步
int32 glMajor = 3; // OpenGL主版本
int32 glMinor = 3; // OpenGL次版本
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -87,7 +87,6 @@ public:
void stop() { running_ = false; } void stop() { running_ = false; }
private: private:
std::unique_ptr<ModuleRegistry> moduleRegistry_;
std::unique_ptr<PluginLoader> pluginLoader_; std::unique_ptr<PluginLoader> pluginLoader_;
std::unique_ptr<TimerModule> timerModule_; std::unique_ptr<TimerModule> timerModule_;

View File

@ -33,9 +33,6 @@
#include <event/event_bus_macros.h> #include <event/event_bus_macros.h>
#include <event/events.h> #include <event/events.h>
// Platform
#include <platform/sdl2.h>
// Utils // Utils
#include <utils/logger.h> #include <utils/logger.h>
#include <utils/random.h> #include <utils/random.h>
@ -47,13 +44,6 @@
// Application // Application
#include <app/application.h> #include <app/application.h>
// 兼容层 - 旧的头文件(将在后续版本中移除)
// #include <core/director.h> // 已废弃,使用 Context
// #include <module/module.h> // 已废弃,使用 imodule.h
// #include <module/module_manager.h> // 已废弃,使用 module_registry.h
// #include <plugin/plugin.h> // 已废弃,使用 iplugin.h
// #include <plugin/plugin_manager.h> // 已废弃,使用 plugin_loader.h
#ifdef __SWITCH__ #ifdef __SWITCH__
#include <switch.h> #include <switch.h>
#endif #endif

View File

@ -3,10 +3,11 @@
#include <SDL.h> #include <SDL.h>
#include <config/app_config.h> #include <config/app_config.h>
#include <config/window_config.h> #include <config/window_config.h>
#include <event/events.h>
#include <functional> #include <functional>
#include <memory>
#include <module/module.h> #include <module/module.h>
#include <module/module_registry.h> #include <module/module_registry.h>
#include <type_traits>
#include <types/math/size.h> #include <types/math/size.h>
#include <types/math/vec2.h> #include <types/math/vec2.h>
@ -19,9 +20,9 @@ using ResizeCb = std::function<void(int32 w, int32 h)>;
using CloseCb = std::function<void()>; using CloseCb = std::function<void()>;
/** /**
* @brief - * @brief - Vulkan
* *
* SDL2 OpenGL * SDL2 Vulkan
* 使 Module * 使 Module
*/ */
class WindowModule : public Module { class WindowModule : public Module {
@ -55,21 +56,11 @@ public:
*/ */
bool pollEvents(); bool pollEvents();
/**
* @brief
*/
void swapBuffers();
/** /**
* @brief SDL * @brief SDL
*/ */
SDL_Window *handle() const { return window_; } SDL_Window *handle() const { return window_; }
/**
* @brief OpenGL
*/
SDL_GLContext glContext() const { return glCtx_; }
/** /**
* @brief * @brief
*/ */
@ -100,16 +91,6 @@ public:
*/ */
bool isFullscreen() const; bool isFullscreen() const;
/**
* @brief
*/
void setVsync(bool vsync);
/**
* @brief
*/
bool isVsync() const;
/** /**
* @brief / * @brief /
*/ */
@ -140,42 +121,39 @@ public:
*/ */
bool shouldClose() const { return shouldClose_; } bool shouldClose() const { return shouldClose_; }
/**
* @brief Vulkan
* @return
*/
std::vector<const char *> getVulkanExtensions() const;
/**
* @brief Vulkan
* @param instance Vulkan
* @param surface
* @return
*/
bool createVulkanSurface(void *instance, void **surface) const;
private: private:
void handleWindowEvent(const SDL_WindowEvent &evt); void handleWindowEvent(const SDL_WindowEvent &evt);
/** /**
* @brief * @brief
* @tparam ConfigT * @param config
* @param config
* *
* AppConfig * AppConfig
*/ */
template <typename ConfigT> void onModuleConfig(const ConfigT &config) { void onModuleConfig(const AppConfig &config);
// 只处理 AppConfig 类型
if constexpr (std::is_same_v<ConfigT, AppConfig>) {
WindowCfg cfg;
cfg.title = config.title;
cfg.width = config.width;
cfg.height = config.height;
cfg.fullscreen = config.fullscreen;
cfg.resizable = config.resizable;
cfg.vsync = config.vsync;
cfg.glMajor = config.glMajor;
cfg.glMinor = config.glMinor;
if (create(cfg)) {
setVisible(true);
}
}
}
SDL_Window *window_ = nullptr; SDL_Window *window_ = nullptr;
SDL_GLContext glCtx_ = nullptr;
bool shouldClose_ = false; bool shouldClose_ = false;
bool vsync_ = true;
CloseCb onClose_; CloseCb onClose_;
ResizeCb onResize_; ResizeCb onResize_;
// 模块配置事件监听器
std::unique_ptr<events::OnModuleConfig<AppConfig>::Listener> configListener_;
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -1,10 +1,9 @@
#pragma once #pragma once
#include <plugin\iplugin.h> #include <plugin\iplugin.h>
#include <vector>
#include <unordered_map>
#include <string> #include <string>
#include <memory> #include <unordered_map>
#include <vector>
namespace extra2d { namespace extra2d {
@ -20,51 +19,51 @@ public:
~PluginLoader(); ~PluginLoader();
// 禁止拷贝 // 禁止拷贝
PluginLoader(const PluginLoader&) = delete; PluginLoader(const PluginLoader &) = delete;
PluginLoader& operator=(const PluginLoader&) = delete; PluginLoader &operator=(const PluginLoader &) = delete;
// 允许移动 // 允许移动
PluginLoader(PluginLoader&&) noexcept; PluginLoader(PluginLoader &&) noexcept;
PluginLoader& operator=(PluginLoader&&) noexcept; PluginLoader &operator=(PluginLoader &&) noexcept;
/** /**
* @brief * @brief
* @param path * @param path
* @return * @return
*/ */
bool loadFromLibrary(const char* path); bool loadFromLibrary(const char *path);
/** /**
* @brief * @brief
* @param plugin * @param plugin
*/ */
void registerPlugin(IPlugin* plugin); void registerPlugin(IPlugin *plugin);
/** /**
* @brief * @brief
* @param name * @param name
*/ */
void unloadPlugin(const char* name); void unloadPlugin(const char *name);
/** /**
* @brief * @brief
* @param name * @param name
* @return nullptr * @return nullptr
*/ */
IPlugin* getPlugin(const char* name) const; IPlugin *getPlugin(const char *name) const;
/** /**
* @brief * @brief
* @param name * @param name
* @return * @return
*/ */
bool hasPlugin(const char* name) const; bool hasPlugin(const char *name) const;
/** /**
* @brief * @brief
* @return * @return
*/ */
std::vector<IPlugin*> getAllPlugins() const; std::vector<IPlugin *> getAllPlugins() const;
/** /**
* @brief * @brief
@ -87,22 +86,22 @@ public:
* @brief * @brief
* @param path * @param path
*/ */
void addSearchPath(const char* path); void addSearchPath(const char *path);
/** /**
* @brief * @brief
* @param directory * @param directory
* @return * @return
*/ */
size_t loadPluginsFromDirectory(const char* directory); size_t loadPluginsFromDirectory(const char *directory);
private: private:
/** /**
* @brief * @brief
*/ */
struct PluginEntry { struct PluginEntry {
IPlugin* plugin = nullptr; // 插件实例 IPlugin *plugin = nullptr; // 插件实例
void* handle = nullptr; // 动态库句柄 void *handle = nullptr; // 动态库句柄
bool isDynamic = false; // 是否为动态加载 bool isDynamic = false; // 是否为动态加载
bool owned = false; // 是否由加载器管理生命周期 bool owned = false; // 是否由加载器管理生命周期
}; };
@ -112,14 +111,14 @@ private:
* @param plugin * @param plugin
* @return * @return
*/ */
bool resolveDependencies(IPlugin* plugin); bool resolveDependencies(IPlugin *plugin);
/** /**
* @brief * @brief
* @param dependencies * @param dependencies
* @return * @return
*/ */
bool checkDependencies(const std::vector<std::string>& dependencies); bool checkDependencies(const std::vector<std::string> &dependencies);
/** /**
* @brief * @brief
@ -131,13 +130,13 @@ private:
* @param path * @param path
* @return * @return
*/ */
void* loadDynamicLibrary(const char* path); void *loadDynamicLibrary(const char *path);
/** /**
* @brief * @brief
* @param handle * @param handle
*/ */
void unloadDynamicLibrary(void* handle); void unloadDynamicLibrary(void *handle);
/** /**
* @brief * @brief
@ -145,11 +144,11 @@ private:
* @param name * @param name
* @return * @return
*/ */
void* getSymbol(void* handle, const char* name); void *getSymbol(void *handle, const char *name);
std::unordered_map<std::string, PluginEntry> plugins_; std::unordered_map<std::string, PluginEntry> plugins_;
std::vector<std::string> searchPaths_; std::vector<std::string> searchPaths_;
std::vector<IPlugin*> sortedPlugins_; // 按依赖排序的插件列表 std::vector<IPlugin *> sortedPlugins_; // 按依赖排序的插件列表
bool inited_ = false; bool inited_ = false;
}; };

View File

@ -54,8 +54,8 @@ bool Application::init(const AppConfig &config) {
events::OnInit::emit(); events::OnInit::emit();
E2D_LOG_INFO("Application initialized successfully"); E2D_LOG_INFO("Application initialized successfully");
E2D_LOG_INFO("Window: {}x{}, Fullscreen: {}, VSync: {}", config.width, E2D_LOG_INFO("Window: {}x{}, Fullscreen: {}", config.width,
config.height, config.fullscreen, config.vsync); config.height, config.fullscreen);
return true; return true;
} }
@ -132,13 +132,8 @@ void Application::run() {
update(); update();
} }
// 交换缓冲区
if (window) {
window->swapBuffers();
}
// FPS 限制 - 使用 SDL_Delay // FPS 限制 - 使用 SDL_Delay
if (!config_.vsync && config_.fpsLimit > 0) { if (config_.fpsLimit > 0) {
Uint64 frameEndCounter = SDL_GetPerformanceCounter(); Uint64 frameEndCounter = SDL_GetPerformanceCounter();
float frameTime = float frameTime =
static_cast<float>(frameEndCounter - currentPerfCounter) / perfFreq; static_cast<float>(frameEndCounter - currentPerfCounter) / perfFreq;

View File

@ -1,18 +1,14 @@
#include <context/context.h> #include <context/context.h>
#include <module/module_registry.h>
#include <module/timer_module.h>
#include <plugin/plugin_loader.h>
#include <event/events.h> #include <event/events.h>
#include <algorithm> #include <module/module_registry.h>
#include <plugin/plugin_loader.h>
#include <utils/timer_module.h>
namespace extra2d { namespace extra2d {
Context::Context() Context::Context()
: moduleRegistry_(std::make_unique<ModuleRegistry>()) : pluginLoader_(std::make_unique<PluginLoader>()),
, pluginLoader_(std::make_unique<PluginLoader>()) timerModule_(std::make_unique<TimerModule>()) {}
, timerModule_(std::make_unique<TimerModule>())
{
}
Context::~Context() { Context::~Context() {
if (inited_) { if (inited_) {
@ -20,8 +16,8 @@ Context::~Context() {
} }
} }
Context::Context(Context&&) noexcept = default; Context::Context(Context &&) noexcept = default;
Context& Context::operator=(Context&&) noexcept = default; Context &Context::operator=(Context &&) noexcept = default;
std::unique_ptr<Context> Context::create() { std::unique_ptr<Context> Context::create() {
return std::make_unique<Context>(); return std::make_unique<Context>();
@ -35,11 +31,8 @@ bool Context::init() {
// 发送引擎初始化事件 // 发送引擎初始化事件
events::OnInit::emit(); events::OnInit::emit();
// 注册核心模块 // 初始化定时器模块
moduleRegistry_->registerModule(timerModule_.get()); if (!timerModule_->init()) {
// 初始化所有模块
if (!moduleRegistry_->initAll()) {
return false; return false;
} }
@ -63,8 +56,8 @@ void Context::shutdown() {
// 关闭所有插件 // 关闭所有插件
pluginLoader_->shutdownAll(); pluginLoader_->shutdownAll();
// 关闭所有模块 // 关闭定时器模块
moduleRegistry_->shutdownAll(); timerModule_->shutdown();
// 发送引擎关闭事件 // 发送引擎关闭事件
events::OnShutdown::emit(); events::OnShutdown::emit();
@ -91,16 +84,10 @@ void Context::tick(float dt) {
events::OnLateUpdate::emit(dt); events::OnLateUpdate::emit(dt);
} }
ModuleRegistry& Context::modules() { ModuleRegistry &Context::modules() { return ModuleRegistry::instance(); }
return *moduleRegistry_;
}
PluginLoader& Context::plugins() { PluginLoader &Context::plugins() { return *pluginLoader_; }
return *pluginLoader_;
}
TimerModule& Context::timer() { TimerModule &Context::timer() { return *timerModule_; }
return *timerModule_;
}
} // namespace extra2d } // namespace extra2d

View File

@ -1,221 +0,0 @@
#include <module/timer_module.h>
#include <event/events.h>
namespace extra2d {
TimerModule::TimerModule() = default;
TimerModule::~TimerModule() {
shutdown();
}
TimerModule::TimerModule(TimerModule&& other) noexcept
: timers_(std::move(other.timers_))
, pendingRemove_(std::move(other.pendingRemove_))
, nextId_(other.nextId_)
, timeScale_(other.timeScale_)
, inUpdate_(other.inUpdate_) {
}
TimerModule& TimerModule::operator=(TimerModule&& other) noexcept {
if (this != &other) {
shutdown();
timers_ = std::move(other.timers_);
pendingRemove_ = std::move(other.pendingRemove_);
nextId_ = other.nextId_;
timeScale_ = other.timeScale_;
inUpdate_ = other.inUpdate_;
}
return *this;
}
bool TimerModule::init() {
// 初始化完成,等待 OnUpdate 事件
return true;
}
void TimerModule::shutdown() {
cancelAll();
}
TimerId TimerModule::scheduleOnce(float delay, TimerCallback callback) {
if (delay < 0.0f) delay = 0.0f;
TimerId id = generateId();
auto info = std::make_unique<TimerInfo>();
info->id = id;
info->interval = delay;
info->elapsed = 0.0f;
info->repeat = 1;
info->executed = 0;
info->paused = false;
info->cancelled = false;
info->callback = std::move(callback);
timers_[id] = std::move(info);
return id;
}
TimerId TimerModule::scheduleRepeat(float interval, uint32 repeat, TimerCallback callback) {
if (interval < 0.0f) interval = 0.0f;
TimerId id = generateId();
auto info = std::make_unique<TimerInfo>();
info->id = id;
info->interval = interval;
info->elapsed = 0.0f;
info->repeat = repeat;
info->executed = 0;
info->paused = false;
info->cancelled = false;
info->callback = std::move(callback);
timers_[id] = std::move(info);
return id;
}
TimerId TimerModule::scheduleUpdate(TimerUpdateCallback callback) {
TimerId id = generateId();
auto info = std::make_unique<TimerInfo>();
info->id = id;
info->interval = 0.0f;
info->elapsed = 0.0f;
info->repeat = 0;
info->executed = 0;
info->paused = false;
info->cancelled = false;
info->updateCallback = std::move(callback);
timers_[id] = std::move(info);
return id;
}
void TimerModule::cancel(TimerId id) {
if (id == INVALID_TIMER_ID) return;
auto it = timers_.find(id);
if (it != timers_.end()) {
if (inUpdate_) {
// 如果在更新中,标记为待删除
it->second->cancelled = true;
pendingRemove_.push_back(id);
} else {
// 直接删除
timers_.erase(it);
}
}
}
void TimerModule::pause(TimerId id) {
if (id == INVALID_TIMER_ID) return;
auto it = timers_.find(id);
if (it != timers_.end()) {
it->second->paused = true;
}
}
void TimerModule::resume(TimerId id) {
if (id == INVALID_TIMER_ID) return;
auto it = timers_.find(id);
if (it != timers_.end()) {
it->second->paused = false;
}
}
void TimerModule::cancelAll() {
if (inUpdate_) {
// 如果在更新中,标记所有为待删除
for (auto& pair : timers_) {
pair.second->cancelled = true;
pendingRemove_.push_back(pair.first);
}
} else {
// 直接清空
timers_.clear();
}
}
size_t TimerModule::getActiveCount() const {
size_t count = 0;
for (const auto& pair : timers_) {
if (!pair.second->cancelled) {
++count;
}
}
return count;
}
void TimerModule::update(float dt) {
if (timers_.empty()) return;
// 应用时间缩放
dt *= timeScale_;
inUpdate_ = true;
// 收集需要执行的定时器
std::vector<TimerInfo*> toExecute;
std::vector<TimerId> toRemove;
for (auto& pair : timers_) {
TimerInfo* info = pair.second.get();
if (info->cancelled || info->paused) {
continue;
}
// 更新任务(每帧执行)
if (info->updateCallback) {
toExecute.push_back(info);
continue;
}
// 定时任务
info->elapsed += dt;
if (info->elapsed >= info->interval) {
toExecute.push_back(info);
// 重置计时器
info->elapsed = 0.0f;
info->executed++;
// 检查是否需要移除
if (info->repeat > 0 && info->executed >= info->repeat) {
toRemove.push_back(info->id);
}
}
}
// 执行回调
for (TimerInfo* info : toExecute) {
if (info->callback) {
info->callback();
}
if (info->updateCallback) {
info->updateCallback(dt);
}
}
inUpdate_ = false;
// 移除已完成的定时器
for (TimerId id : toRemove) {
timers_.erase(id);
}
// 处理待删除列表
for (TimerId id : pendingRemove_) {
timers_.erase(id);
}
pendingRemove_.clear();
}
TimerId TimerModule::generateId() {
return nextId_++;
}
} // namespace extra2d

View File

@ -1,9 +1,15 @@
#include <platform/window_module.h> #include <platform/window_module.h>
#include <platform/sdl2.h>
#include <config/app_config.h> #include <config/app_config.h>
#include <event/events.h> #include <event/events.h>
#include <memory>
#include <utils/logger.h> #include <utils/logger.h>
// SDL Vulkan support
#include <SDL_vulkan.h>
// Vulkan
#include <vulkan/vulkan.h>
namespace extra2d { namespace extra2d {
WindowModule::WindowModule() = default; WindowModule::WindowModule() = default;
@ -14,53 +20,42 @@ WindowModule::~WindowModule() {
WindowModule::WindowModule(WindowModule&& other) noexcept WindowModule::WindowModule(WindowModule&& other) noexcept
: window_(other.window_) : window_(other.window_)
, glCtx_(other.glCtx_)
, shouldClose_(other.shouldClose_) , shouldClose_(other.shouldClose_)
, vsync_(other.vsync_)
, onClose_(std::move(other.onClose_)) , onClose_(std::move(other.onClose_))
, onResize_(std::move(other.onResize_)) { , onResize_(std::move(other.onResize_))
, configListener_(std::move(other.configListener_)) {
other.window_ = nullptr; other.window_ = nullptr;
other.glCtx_ = nullptr;
} }
WindowModule& WindowModule::operator=(WindowModule&& other) noexcept { WindowModule& WindowModule::operator=(WindowModule&& other) noexcept {
if (this != &other) { if (this != &other) {
shutdown(); shutdown();
window_ = other.window_; window_ = other.window_;
glCtx_ = other.glCtx_;
shouldClose_ = other.shouldClose_; shouldClose_ = other.shouldClose_;
vsync_ = other.vsync_;
onClose_ = std::move(other.onClose_); onClose_ = std::move(other.onClose_);
onResize_ = std::move(other.onResize_); onResize_ = std::move(other.onResize_);
configListener_ = std::move(other.configListener_);
other.window_ = nullptr; other.window_ = nullptr;
other.glCtx_ = nullptr;
} }
return *this; return *this;
} }
bool WindowModule::init() { bool WindowModule::init() {
if (!Sdl2::initVideo()) { if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
E2D_LOG_ERROR("Failed to initialize SDL video: {}", SDL_GetError());
return false; return false;
} }
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
// 监听模块配置事件 // 监听模块配置事件
events::OnModuleConfig<AppConfig>::subscribe(this, &WindowModule::onModuleConfig<AppConfig>); configListener_ = std::make_unique<events::OnModuleConfig<AppConfig>::Listener>();
configListener_->bind([this](const AppConfig& config) {
this->onModuleConfig(config);
});
return true; return true;
} }
void WindowModule::shutdown() { void WindowModule::shutdown() {
if (glCtx_) {
SDL_GL_DeleteContext(glCtx_);
glCtx_ = nullptr;
}
if (window_) { if (window_) {
SDL_DestroyWindow(window_); SDL_DestroyWindow(window_);
window_ = nullptr; window_ = nullptr;
@ -71,7 +66,7 @@ bool WindowModule::create(const WindowCfg& cfg) {
if (window_) if (window_)
return true; return true;
uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN; uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN;
if (cfg.resizable) { if (cfg.resizable) {
flags |= SDL_WINDOW_RESIZABLE; flags |= SDL_WINDOW_RESIZABLE;
} }
@ -79,9 +74,6 @@ bool WindowModule::create(const WindowCfg& cfg) {
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
} }
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, cfg.glMajor);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, cfg.glMinor);
window_ = SDL_CreateWindow(cfg.title.c_str(), SDL_WINDOWPOS_CENTERED, window_ = SDL_CreateWindow(cfg.title.c_str(), SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags); SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags);
@ -90,20 +82,10 @@ bool WindowModule::create(const WindowCfg& cfg) {
return false; return false;
} }
glCtx_ = SDL_GL_CreateContext(window_);
if (!glCtx_) {
E2D_LOG_ERROR("Failed to create OpenGL context: {}", SDL_GetError());
SDL_DestroyWindow(window_);
window_ = nullptr;
return false;
}
setVsync(cfg.vsync);
vsync_ = cfg.vsync;
// 发送窗口显示事件 // 发送窗口显示事件
events::OnShow::emit(); events::OnShow::emit();
E2D_LOG_INFO("Window created: {}x{}", cfg.width, cfg.height);
return true; return true;
} }
@ -164,9 +146,16 @@ void WindowModule::handleWindowEvent(const SDL_WindowEvent& evt) {
} }
} }
void WindowModule::swapBuffers() { void WindowModule::onModuleConfig(const AppConfig &config) {
if (window_ && glCtx_) { WindowCfg cfg;
SDL_GL_SwapWindow(window_); cfg.title = config.title;
cfg.width = config.width;
cfg.height = config.height;
cfg.fullscreen = config.fullscreen;
cfg.resizable = config.resizable;
if (create(cfg)) {
setVisible(true);
} }
} }
@ -212,15 +201,6 @@ bool WindowModule::isFullscreen() const {
return (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; return (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
} }
void WindowModule::setVsync(bool vsync) {
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
vsync_ = vsync;
}
bool WindowModule::isVsync() const {
return vsync_;
}
void WindowModule::setVisible(bool visible) { void WindowModule::setVisible(bool visible) {
if (window_) { if (window_) {
if (visible) { if (visible) {
@ -242,4 +222,25 @@ bool WindowModule::isVisible() const {
return (flags & SDL_WINDOW_SHOWN) != 0; return (flags & SDL_WINDOW_SHOWN) != 0;
} }
std::vector<const char*> WindowModule::getVulkanExtensions() const {
std::vector<const char*> extensions;
if (!window_) {
return extensions;
}
uint32 count = 0;
SDL_Vulkan_GetInstanceExtensions(window_, &count, nullptr);
extensions.resize(count);
SDL_Vulkan_GetInstanceExtensions(window_, &count, extensions.data());
return extensions;
}
bool WindowModule::createVulkanSurface(void* instance, void** surface) const {
if (!window_ || !instance || !surface) {
return false;
}
return SDL_Vulkan_CreateSurface(window_, static_cast<VkInstance>(instance), reinterpret_cast<VkSurfaceKHR*>(surface));
}
} // namespace extra2d } // namespace extra2d

219
src/utils/timer_module.cpp Normal file
View File

@ -0,0 +1,219 @@
#include <event/events.h>
#include <utils/timer_module.h>
namespace extra2d {
TimerModule::TimerModule() = default;
TimerModule::~TimerModule() { shutdown(); }
TimerModule::TimerModule(TimerModule &&other) noexcept
: timers_(std::move(other.timers_)),
pendingRemove_(std::move(other.pendingRemove_)), nextId_(other.nextId_),
timeScale_(other.timeScale_), inUpdate_(other.inUpdate_) {}
TimerModule &TimerModule::operator=(TimerModule &&other) noexcept {
if (this != &other) {
shutdown();
timers_ = std::move(other.timers_);
pendingRemove_ = std::move(other.pendingRemove_);
nextId_ = other.nextId_;
timeScale_ = other.timeScale_;
inUpdate_ = other.inUpdate_;
}
return *this;
}
bool TimerModule::init() {
// 初始化完成,等待 OnUpdate 事件
return true;
}
void TimerModule::shutdown() { cancelAll(); }
TimerId TimerModule::scheduleOnce(float delay, TimerCallback callback) {
if (delay < 0.0f)
delay = 0.0f;
TimerId id = generateId();
auto info = std::make_unique<TimerInfo>();
info->id = id;
info->interval = delay;
info->elapsed = 0.0f;
info->repeat = 1;
info->executed = 0;
info->paused = false;
info->cancelled = false;
info->callback = std::move(callback);
timers_[id] = std::move(info);
return id;
}
TimerId TimerModule::scheduleRepeat(float interval, uint32 repeat,
TimerCallback callback) {
if (interval < 0.0f)
interval = 0.0f;
TimerId id = generateId();
auto info = std::make_unique<TimerInfo>();
info->id = id;
info->interval = interval;
info->elapsed = 0.0f;
info->repeat = repeat;
info->executed = 0;
info->paused = false;
info->cancelled = false;
info->callback = std::move(callback);
timers_[id] = std::move(info);
return id;
}
TimerId TimerModule::scheduleUpdate(TimerUpdateCallback callback) {
TimerId id = generateId();
auto info = std::make_unique<TimerInfo>();
info->id = id;
info->interval = 0.0f;
info->elapsed = 0.0f;
info->repeat = 0;
info->executed = 0;
info->paused = false;
info->cancelled = false;
info->updateCallback = std::move(callback);
timers_[id] = std::move(info);
return id;
}
void TimerModule::cancel(TimerId id) {
if (id == INVALID_TIMER_ID)
return;
auto it = timers_.find(id);
if (it != timers_.end()) {
if (inUpdate_) {
// 如果在更新中,标记为待删除
it->second->cancelled = true;
pendingRemove_.push_back(id);
} else {
// 直接删除
timers_.erase(it);
}
}
}
void TimerModule::pause(TimerId id) {
if (id == INVALID_TIMER_ID)
return;
auto it = timers_.find(id);
if (it != timers_.end()) {
it->second->paused = true;
}
}
void TimerModule::resume(TimerId id) {
if (id == INVALID_TIMER_ID)
return;
auto it = timers_.find(id);
if (it != timers_.end()) {
it->second->paused = false;
}
}
void TimerModule::cancelAll() {
if (inUpdate_) {
// 如果在更新中,标记所有为待删除
for (auto &pair : timers_) {
pair.second->cancelled = true;
pendingRemove_.push_back(pair.first);
}
} else {
// 直接清空
timers_.clear();
}
}
size_t TimerModule::getActiveCount() const {
size_t count = 0;
for (const auto &pair : timers_) {
if (!pair.second->cancelled) {
++count;
}
}
return count;
}
void TimerModule::update(float dt) {
if (timers_.empty())
return;
// 应用时间缩放
dt *= timeScale_;
inUpdate_ = true;
// 收集需要执行的定时器
std::vector<TimerInfo *> toExecute;
std::vector<TimerId> toRemove;
for (auto &pair : timers_) {
TimerInfo *info = pair.second.get();
if (info->cancelled || info->paused) {
continue;
}
// 更新任务(每帧执行)
if (info->updateCallback) {
toExecute.push_back(info);
continue;
}
// 定时任务
info->elapsed += dt;
if (info->elapsed >= info->interval) {
toExecute.push_back(info);
// 重置计时器
info->elapsed = 0.0f;
info->executed++;
// 检查是否需要移除
if (info->repeat > 0 && info->executed >= info->repeat) {
toRemove.push_back(info->id);
}
}
}
// 执行回调
for (TimerInfo *info : toExecute) {
if (info->callback) {
info->callback();
}
if (info->updateCallback) {
info->updateCallback(dt);
}
}
inUpdate_ = false;
// 移除已完成的定时器
for (TimerId id : toRemove) {
timers_.erase(id);
}
// 处理待删除列表
for (TimerId id : pendingRemove_) {
timers_.erase(id);
}
pendingRemove_.clear();
}
TimerId TimerModule::generateId() { return nextId_++; }
} // namespace extra2d

View File

@ -78,7 +78,7 @@ end
-- ============================================== -- ==============================================
if target_plat == "mingw" then if target_plat == "mingw" then
add_requires("glm", "libsdl2", "libsdl2_mixer") add_requires("glm", "libsdl2", "libsdl2_mixer", "vulkansdk")
end end
-- ============================================== -- ==============================================

View File

@ -14,23 +14,23 @@ function define_extra2d_engine()
set_kind("static") set_kind("static")
add_files("src/**.cpp|core/*.cpp|module/module_manager.cpp|plugin/plugin_manager.cpp|platform/window.cpp|platform/input.cpp") add_files("src/**.cpp|core/*.cpp|module/module_manager.cpp|plugin/plugin_manager.cpp|platform/window.cpp|platform/input.cpp")
add_files("third_party/glad/src/glad.c")
add_includedirs("include", {public = true}) add_includedirs("include", {public = true})
add_includedirs("third_party/glad/include", {public = true})
add_includedirs("third_party", {public = true}) add_includedirs("third_party", {public = true})
local plat = get_current_plat() local plat = get_current_plat()
if plat == "mingw" then if plat == "mingw" then
add_defines("_UNICODE", "UNICODE") add_defines("_UNICODE", "UNICODE")
add_packages("glm", "libsdl2", "libsdl2_mixer", {public = true}) -- 使用 xmake 官方包
add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi", {public = true}) add_packages("glm", "libsdl2", "libsdl2_mixer", "vulkansdk", {public = true})
add_syslinks("winmm", "imm32", "version", "setupapi", {public = true})
elseif plat == "switch" then elseif plat == "switch" then
local devkitPro = os.getenv("DEVKITPRO") or "C:/devkitPro" local devkitPro = os.getenv("DEVKITPRO") or "C:/devkitPro"
add_includedirs(devkitPro .. "/portlibs/switch/include", {public = true}) add_includedirs(devkitPro .. "/portlibs/switch/include", {public = true})
add_linkdirs(devkitPro .. "/portlibs/switch/lib") add_linkdirs(devkitPro .. "/portlibs/switch/lib")
-- Switch 平台可能需要不同的图形库
add_syslinks("SDL2_mixer", "SDL2", "opusfile", "opus", "vorbisidec", "ogg", add_syslinks("SDL2_mixer", "SDL2", "opusfile", "opus", "vorbisidec", "ogg",
"modplug", "mpg123", "FLAC", "GLESv2", "EGL", "glapi", "drm_nouveau", "modplug", "mpg123", "FLAC", "drm_nouveau",
{public = true}) {public = true})
end end