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; // 窗口高度
bool fullscreen = false; // 是否全屏
bool resizable = true; // 是否可调整大小
bool vsync = true; // 是否垂直同步
int32 fpsLimit = 0; // FPS限制0表示不限制
int32 glMajor = 3; // OpenGL主版本
int32 glMinor = 3; // OpenGL次版本
bool enableCursors = true; // 启用光标
bool enableDpiScale = false; // 启用DPI缩放
};

View File

@ -11,14 +11,11 @@ namespace extra2d {
*
*/
struct WindowCfg {
std::string title = "Extra2D"; // 窗口标题
int32 width = 1280; // 窗口宽度
int32 height = 720; // 窗口高度
bool fullscreen = false; // 是否全屏
bool resizable = true; // 是否可调整大小
bool vsync = true; // 是否垂直同步
int32 glMajor = 3; // OpenGL主版本
int32 glMinor = 3; // OpenGL次版本
std::string title = "Extra2D"; // 窗口标题
int32 width = 1280; // 窗口宽度
int32 height = 720; // 窗口高度
bool fullscreen = false; // 是否全屏
bool resizable = true; // 是否可调整大小
};
} // namespace extra2d

View File

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

View File

@ -33,9 +33,6 @@
#include <event/event_bus_macros.h>
#include <event/events.h>
// Platform
#include <platform/sdl2.h>
// Utils
#include <utils/logger.h>
#include <utils/random.h>
@ -47,13 +44,6 @@
// Application
#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__
#include <switch.h>
#endif

View File

@ -3,10 +3,11 @@
#include <SDL.h>
#include <config/app_config.h>
#include <config/window_config.h>
#include <event/events.h>
#include <functional>
#include <memory>
#include <module/module.h>
#include <module/module_registry.h>
#include <type_traits>
#include <types/math/size.h>
#include <types/math/vec2.h>
@ -19,9 +20,9 @@ using ResizeCb = std::function<void(int32 w, int32 h)>;
using CloseCb = std::function<void()>;
/**
* @brief -
* @brief - Vulkan
*
* SDL2 OpenGL
* SDL2 Vulkan
* 使 Module
*/
class WindowModule : public Module {
@ -55,21 +56,11 @@ public:
*/
bool pollEvents();
/**
* @brief
*/
void swapBuffers();
/**
* @brief SDL
*/
SDL_Window *handle() const { return window_; }
/**
* @brief OpenGL
*/
SDL_GLContext glContext() const { return glCtx_; }
/**
* @brief
*/
@ -100,16 +91,6 @@ public:
*/
bool isFullscreen() const;
/**
* @brief
*/
void setVsync(bool vsync);
/**
* @brief
*/
bool isVsync() const;
/**
* @brief /
*/
@ -140,42 +121,39 @@ public:
*/
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:
void handleWindowEvent(const SDL_WindowEvent &evt);
/**
* @brief
* @tparam ConfigT
* @param config
* @param config
*
* AppConfig
* AppConfig
*/
template <typename ConfigT> void onModuleConfig(const ConfigT &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);
}
}
}
void onModuleConfig(const AppConfig &config);
SDL_Window *window_ = nullptr;
SDL_GLContext glCtx_ = nullptr;
bool shouldClose_ = false;
bool vsync_ = true;
CloseCb onClose_;
ResizeCb onResize_;
// 模块配置事件监听器
std::unique_ptr<events::OnModuleConfig<AppConfig>::Listener> configListener_;
};
} // namespace extra2d

View File

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

View File

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

View File

@ -1,106 +1,93 @@
#include <context/context.h>
#include <module/module_registry.h>
#include <module/timer_module.h>
#include <plugin/plugin_loader.h>
#include <event/events.h>
#include <algorithm>
#include <module/module_registry.h>
#include <plugin/plugin_loader.h>
#include <utils/timer_module.h>
namespace extra2d {
Context::Context()
: moduleRegistry_(std::make_unique<ModuleRegistry>())
, pluginLoader_(std::make_unique<PluginLoader>())
, timerModule_(std::make_unique<TimerModule>())
{
}
: pluginLoader_(std::make_unique<PluginLoader>()),
timerModule_(std::make_unique<TimerModule>()) {}
Context::~Context() {
if (inited_) {
shutdown();
}
if (inited_) {
shutdown();
}
}
Context::Context(Context&&) noexcept = default;
Context& Context::operator=(Context&&) noexcept = default;
Context::Context(Context &&) noexcept = default;
Context &Context::operator=(Context &&) noexcept = default;
std::unique_ptr<Context> Context::create() {
return std::make_unique<Context>();
return std::make_unique<Context>();
}
bool Context::init() {
if (inited_) {
return true;
}
// 发送引擎初始化事件
events::OnInit::emit();
// 注册核心模块
moduleRegistry_->registerModule(timerModule_.get());
// 初始化所有模块
if (!moduleRegistry_->initAll()) {
return false;
}
// 初始化所有插件
if (!pluginLoader_->initAll()) {
return false;
}
inited_ = true;
running_ = true;
if (inited_) {
return true;
}
// 发送引擎初始化事件
events::OnInit::emit();
// 初始化定时器模块
if (!timerModule_->init()) {
return false;
}
// 初始化所有插件
if (!pluginLoader_->initAll()) {
return false;
}
inited_ = true;
running_ = true;
return true;
}
void Context::shutdown() {
if (!inited_) {
return;
}
if (!inited_) {
return;
}
running_ = false;
running_ = false;
// 关闭所有插件
pluginLoader_->shutdownAll();
// 关闭所有插件
pluginLoader_->shutdownAll();
// 关闭所有模块
moduleRegistry_->shutdownAll();
// 关闭定时器模块
timerModule_->shutdown();
// 发送引擎关闭事件
events::OnShutdown::emit();
// 发送引擎关闭事件
events::OnShutdown::emit();
inited_ = false;
inited_ = false;
}
void Context::tick(float dt) {
if (!running_) {
return;
}
if (!running_) {
return;
}
// 更新时间和帧数
totalTime_ += dt;
frameCount_++;
// 更新时间和帧数
totalTime_ += dt;
frameCount_++;
// 更新定时器模块
timerModule_->update(dt);
// 更新定时器模块
timerModule_->update(dt);
// 发送更新事件
events::OnUpdate::emit(dt);
// 发送更新事件
events::OnUpdate::emit(dt);
// 发送延迟更新事件
events::OnLateUpdate::emit(dt);
// 发送延迟更新事件
events::OnLateUpdate::emit(dt);
}
ModuleRegistry& Context::modules() {
return *moduleRegistry_;
}
ModuleRegistry &Context::modules() { return ModuleRegistry::instance(); }
PluginLoader& Context::plugins() {
return *pluginLoader_;
}
PluginLoader &Context::plugins() { return *pluginLoader_; }
TimerModule& Context::timer() {
return *timerModule_;
}
TimerModule &Context::timer() { return *timerModule_; }
} // 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/sdl2.h>
#include <config/app_config.h>
#include <event/events.h>
#include <memory>
#include <utils/logger.h>
// SDL Vulkan support
#include <SDL_vulkan.h>
// Vulkan
#include <vulkan/vulkan.h>
namespace extra2d {
WindowModule::WindowModule() = default;
@ -14,53 +20,42 @@ WindowModule::~WindowModule() {
WindowModule::WindowModule(WindowModule&& other) noexcept
: window_(other.window_)
, glCtx_(other.glCtx_)
, shouldClose_(other.shouldClose_)
, vsync_(other.vsync_)
, onClose_(std::move(other.onClose_))
, onResize_(std::move(other.onResize_)) {
, onResize_(std::move(other.onResize_))
, configListener_(std::move(other.configListener_)) {
other.window_ = nullptr;
other.glCtx_ = nullptr;
}
WindowModule& WindowModule::operator=(WindowModule&& other) noexcept {
if (this != &other) {
shutdown();
window_ = other.window_;
glCtx_ = other.glCtx_;
shouldClose_ = other.shouldClose_;
vsync_ = other.vsync_;
onClose_ = std::move(other.onClose_);
onResize_ = std::move(other.onResize_);
configListener_ = std::move(other.configListener_);
other.window_ = nullptr;
other.glCtx_ = nullptr;
}
return *this;
}
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;
}
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;
}
void WindowModule::shutdown() {
if (glCtx_) {
SDL_GL_DeleteContext(glCtx_);
glCtx_ = nullptr;
}
if (window_) {
SDL_DestroyWindow(window_);
window_ = nullptr;
@ -71,7 +66,7 @@ bool WindowModule::create(const WindowCfg& cfg) {
if (window_)
return true;
uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN;
if (cfg.resizable) {
flags |= SDL_WINDOW_RESIZABLE;
}
@ -79,9 +74,6 @@ bool WindowModule::create(const WindowCfg& cfg) {
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,
SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags);
@ -90,20 +82,10 @@ bool WindowModule::create(const WindowCfg& cfg) {
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();
E2D_LOG_INFO("Window created: {}x{}", cfg.width, cfg.height);
return true;
}
@ -164,9 +146,16 @@ void WindowModule::handleWindowEvent(const SDL_WindowEvent& evt) {
}
}
void WindowModule::swapBuffers() {
if (window_ && glCtx_) {
SDL_GL_SwapWindow(window_);
void WindowModule::onModuleConfig(const AppConfig &config) {
WindowCfg cfg;
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;
}
void WindowModule::setVsync(bool vsync) {
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
vsync_ = vsync;
}
bool WindowModule::isVsync() const {
return vsync_;
}
void WindowModule::setVisible(bool visible) {
if (window_) {
if (visible) {
@ -242,4 +222,25 @@ bool WindowModule::isVisible() const {
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

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
add_requires("glm", "libsdl2", "libsdl2_mixer")
add_requires("glm", "libsdl2", "libsdl2_mixer", "vulkansdk")
end
-- ==============================================

View File

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