refactor: 移除事件系统、属性绑定和模块配置相关代码
重构模块系统,简化核心架构: 1. 删除EventDispatcher、EventQueue和EventContext相关代码 2. 移除PropertyBinder和模块属性绑定功能 3. 清理冗余的模块配置类(debug_config.h, resource_config.h等) 4. 合并RenderModule配置到模块头文件 5. 移除ServiceRegistry实现 6. 简化Shader系统实现,移除独立缓存和热重载组件 7. 更新文档说明模块配置方式 8. 修复TexturePool的渲染后端依赖问题
This commit is contained in:
parent
05ef543615
commit
ea1bbb891d
|
|
@ -0,0 +1,86 @@
|
||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
本文件为 Claude Code (claude.ai/code) 在此仓库中工作时提供指导。
|
||||||
|
|
||||||
|
## 项目概述
|
||||||
|
|
||||||
|
Extra2D 是一个使用 C++17 编写的轻量级跨平台 2D 游戏引擎。支持 Windows (MinGW)、Linux、macOS 和 Nintendo Switch。项目注释和文档使用中文。
|
||||||
|
|
||||||
|
## 构建系统
|
||||||
|
|
||||||
|
项目使用 **xmake**(非 CMake)。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 配置 (Windows/MinGW 示例)
|
||||||
|
xmake f -p mingw -a x86_64 -m release -y
|
||||||
|
|
||||||
|
# 配置调试构建
|
||||||
|
xmake f -p mingw -a x86_64 -m debug -y
|
||||||
|
|
||||||
|
# 构建全部
|
||||||
|
xmake build
|
||||||
|
|
||||||
|
# 构建特定目标
|
||||||
|
xmake build demo_basic
|
||||||
|
xmake build demo_hello_module
|
||||||
|
|
||||||
|
# 运行示例
|
||||||
|
xmake run demo_basic
|
||||||
|
xmake run demo_hello_module
|
||||||
|
|
||||||
|
# Nintendo Switch 构建
|
||||||
|
export DEVKITPRO=/opt/devkitpro
|
||||||
|
xmake f -p switch -m release -y
|
||||||
|
xmake build
|
||||||
|
```
|
||||||
|
|
||||||
|
构建目标:`extra2d`(引擎静态库)、`demo_basic`、`demo_hello_module`、`hello_module_lib`。
|
||||||
|
|
||||||
|
调试构建定义 `E2D_DEBUG` 和 `_DEBUG`。`debug_logs` 选项启用调试日志。
|
||||||
|
|
||||||
|
## 架构
|
||||||
|
|
||||||
|
### 双系统设计:模块 + 服务
|
||||||
|
|
||||||
|
引擎有两个由 `Application`(单例)管理的并行系统:
|
||||||
|
|
||||||
|
- **模块**(`Module` 基类):处理平台级初始化,具有生命周期钩子(`setupModule`、`destroyModule`、`onUpdate`、`onRender`、`handleEvent`)。通过 `E2D_MODULE` 宏注册,在静态初始化时自动发现。按优先级排序(数值越小越先初始化)。
|
||||||
|
- **服务**(`IService` 基类):提供通过 `ServiceLocator` 访问的运行时功能。通过 `Application` 便捷方法获取:`app.scenes()`、`app.timers()`、`app.events()`、`app.camera()`。
|
||||||
|
|
||||||
|
### 模块自动注册
|
||||||
|
|
||||||
|
模块使用 `E2D_MODULE(类名, 优先级)` 宏,放置在 `.cpp` 文件末尾、**任何命名空间之外**。这会创建一个静态的 `ModuleAutoRegister<T>` 变量,在静态初始化期间向 `ModuleRegistry` 注册。静态链接需要 `--whole-archive` 链接器标志,以防止链接器剥离这些未引用的符号。
|
||||||
|
|
||||||
|
内置模块优先级顺序:Logger(-1) → Config(0) → Platform(10) → Window(20) → Input(30) → Render(40)。用户模块应使用优先级 1000+。
|
||||||
|
|
||||||
|
### 模块上下文链
|
||||||
|
|
||||||
|
模块生命周期方法接收上下文对象(`UpdateContext`、`RenderContext`、`EventContext`)。**必须调用 `ctx.next()`** 以继续链式调用到下一个模块。
|
||||||
|
|
||||||
|
### 关键子系统
|
||||||
|
|
||||||
|
- **平台层**:统一的 SDL2 后端(`E2D_BACKEND_SDL2` 宏定义)。通过 `IWindow`/`IInput` 接口实现平台抽象。实现在 `Extra2D/src/platform/backends/sdl2/`。
|
||||||
|
- **图形**:通过 glad 使用 OpenGL ES 3.2。渲染器(`gl_renderer`)、精灵批处理(`gl_sprite_batch`)、支持热重载的着色器管理。内置着色器在 `Extra2D/shaders/builtin/`(构建后复制到构建目录)。
|
||||||
|
- **场景图**:基于树的 `Node` 层级结构,支持变换继承。`Scene` 是根节点。专用节点:`ShapeNode`、`Sprite`。场景过渡效果(淡入淡出、滑动、翻转、缩放、方块)。
|
||||||
|
- **事件系统**:`EventType` 枚举驱动。事件使用 `std::variant` 实现类型化数据(`KeyEvent`、`MouseEvent`、`GamepadEvent`)。输入使用扫描码(非键码)。
|
||||||
|
|
||||||
|
## 源码布局
|
||||||
|
|
||||||
|
- `Extra2D/include/extra2d/` — 按子系统组织的公共头文件
|
||||||
|
- `Extra2D/src/` — 实现,镜像 include 结构
|
||||||
|
- `Extra2D/shaders/` — GLSL 着色器文件(复制到构建输出)
|
||||||
|
- `examples/` — 示例程序(`basic/`、`hello_module/`)
|
||||||
|
- `xmake/engine.lua` — 引擎静态库目标定义
|
||||||
|
- `xmake.lua` — 根构建配置,包含示例目标
|
||||||
|
|
||||||
|
## 依赖
|
||||||
|
|
||||||
|
由 xmake 管理:`glm`、`nlohmann_json`、`libsdl2`。内嵌:`glad`、`stb_image`、`stb_truetype`、`stb_rect_pack`、`KHR`。
|
||||||
|
|
||||||
|
## 约定
|
||||||
|
|
||||||
|
- 命名空间:`extra2d`
|
||||||
|
- 智能指针:`Ptr<T>`(shared_ptr 别名)、`SharedPtr<T>`,通过 `makeShared<T>()` 创建
|
||||||
|
- 日志:`E2D_LOG_INFO(...)`、`E2D_LOG_WARN(...)`、`E2D_LOG_ERROR(...)`
|
||||||
|
- 导出宏:`E2D_API` 用于 DLL 可见符号
|
||||||
|
- 自定义模块直接编译到可执行文件(静态链接,推荐)或作为独立 DLL
|
||||||
|
|
@ -11,10 +11,7 @@ namespace extra2d {
|
||||||
* @brief 应用级别配置
|
* @brief 应用级别配置
|
||||||
*
|
*
|
||||||
* 本文件仅包含应用级别的配置项,不包含任何模块特定配置。
|
* 本文件仅包含应用级别的配置项,不包含任何模块特定配置。
|
||||||
* 各模块应该在自己的模块文件中定义配置结构,并实现 IModuleConfig 接口。
|
* 各模块在自己的模块头文件中定义配置结构(如 RenderModuleConfig)。
|
||||||
*
|
|
||||||
* 模块配置通过 ModuleRegistry 注册,由 ConfigManager 统一管理。
|
|
||||||
* 这种设计遵循开闭原则,新增模块无需修改引擎核心代码。
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,7 @@ namespace extra2d {
|
||||||
* @file config_loader.h
|
* @file config_loader.h
|
||||||
* @brief 配置加载器接口
|
* @brief 配置加载器接口
|
||||||
*
|
*
|
||||||
* 配置加载器只负责加载应用级别的配置(AppConfig)。
|
* 负责加载和保存应用级别的配置(AppConfig)。
|
||||||
* 模块配置通过 ModuleRegistry 和各模块的 IModuleConfig 接口加载。
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,7 @@ namespace extra2d {
|
||||||
* @file config_manager.h
|
* @file config_manager.h
|
||||||
* @brief 配置管理器
|
* @brief 配置管理器
|
||||||
*
|
*
|
||||||
* 配置管理器只管理应用级别的配置(AppConfig)。
|
* 管理应用级别的配置(AppConfig)。
|
||||||
* 模块配置通过 ModuleRegistry 管理,各模块实现 IModuleConfig 接口。
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include <extra2d/core/export.h>
|
#include <extra2d/core/export.h>
|
||||||
#include <extra2d/core/types.h>
|
#include <extra2d/core/types.h>
|
||||||
#include <extra2d/core/property.h>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -10,8 +9,6 @@ namespace extra2d {
|
||||||
class Module;
|
class Module;
|
||||||
class UpdateContext;
|
class UpdateContext;
|
||||||
class RenderContext;
|
class RenderContext;
|
||||||
class EventContext;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 模块上下文基类
|
* @brief 模块上下文基类
|
||||||
* 用于遍历模块链,支持链式调用
|
* 用于遍历模块链,支持链式调用
|
||||||
|
|
@ -112,22 +109,6 @@ private:
|
||||||
Phase phase_;
|
Phase phase_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 事件上下文
|
|
||||||
* 用于模块事件处理阶段
|
|
||||||
*/
|
|
||||||
class EventContext : public ModuleContext {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief 构造函数
|
|
||||||
* @param modules 模块列表引用
|
|
||||||
*/
|
|
||||||
EventContext(std::vector<Module*>& modules);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void handle(Module* m) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 模块基类
|
* @brief 模块基类
|
||||||
* 所有模块只需继承此类,实现需要的生命周期方法
|
* 所有模块只需继承此类,实现需要的生命周期方法
|
||||||
|
|
@ -179,13 +160,6 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void afterRender(RenderContext& ctx) { ctx.next(); }
|
virtual void afterRender(RenderContext& ctx) { ctx.next(); }
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 事件处理
|
|
||||||
* 处理系统事件
|
|
||||||
* @param ctx 事件上下文
|
|
||||||
*/
|
|
||||||
virtual void handleEvent(EventContext& ctx) { ctx.next(); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取模块名称
|
* @brief 获取模块名称
|
||||||
* @return 模块名称字符串
|
* @return 模块名称字符串
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include <extra2d/core/export.h>
|
#include <extra2d/core/export.h>
|
||||||
#include <extra2d/core/module_meta.h>
|
#include <extra2d/core/module_meta.h>
|
||||||
#include <extra2d/core/property.h>
|
|
||||||
#include <extra2d/core/types.h>
|
#include <extra2d/core/types.h>
|
||||||
|
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
|
@ -20,7 +19,6 @@ public:
|
||||||
const char* name_ = nullptr;
|
const char* name_ = nullptr;
|
||||||
int priority_ = 0;
|
int priority_ = 0;
|
||||||
std::vector<const char*> dependencies_;
|
std::vector<const char*> dependencies_;
|
||||||
std::function<void(Module*, PropertyBinder&)> bindFunc_;
|
|
||||||
|
|
||||||
const char* getName() const override { return name_; }
|
const char* getName() const override { return name_; }
|
||||||
int getPriority() const override { return priority_; }
|
int getPriority() const override { return priority_; }
|
||||||
|
|
@ -29,12 +27,6 @@ public:
|
||||||
T* create() override {
|
T* create() override {
|
||||||
return new T();
|
return new T();
|
||||||
}
|
}
|
||||||
|
|
||||||
void bindProperties(Module* instance, PropertyBinder& binder) override {
|
|
||||||
if (bindFunc_) {
|
|
||||||
bindFunc_(instance, binder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
@ -107,50 +99,3 @@ struct ModuleAutoRegister {
|
||||||
E2D_DECLARE_FORCE_LINK(ModuleClassName); \
|
E2D_DECLARE_FORCE_LINK(ModuleClassName); \
|
||||||
E2D_CALL_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<type>(#name, module->name, #name, ""); \
|
|
||||||
}; \
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 结束模块定义
|
|
||||||
*/
|
|
||||||
#define E2D_MODULE_END() \
|
|
||||||
} E2D_CONCAT(_e2d_cfg_inst_, ModuleClassName); \
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <extra2d/core/export.h>
|
#include <extra2d/core/export.h>
|
||||||
#include <extra2d/core/property.h>
|
|
||||||
#include <extra2d/core/types.h>
|
#include <extra2d/core/types.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -43,10 +42,6 @@ struct E2D_API ModuleMetaBase {
|
||||||
*/
|
*/
|
||||||
virtual Module* create() = 0;
|
virtual Module* create() = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 绑定模块属性
|
|
||||||
*/
|
|
||||||
virtual void bindProperties(Module* instance, PropertyBinder& binder) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <extra2d/core/color.h>
|
|
||||||
#include <extra2d/core/math_types.h>
|
|
||||||
#include <extra2d/core/types.h>
|
|
||||||
|
|
||||||
#include <variant>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
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<PropertyValue()> getter;
|
|
||||||
std::function<bool(const PropertyValue&)> setter;
|
|
||||||
PropertyMeta meta;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 属性绑定器基类
|
|
||||||
*
|
|
||||||
* 提供属性的绑定、查询和修改接口
|
|
||||||
*/
|
|
||||||
class PropertyBinder {
|
|
||||||
public:
|
|
||||||
virtual ~PropertyBinder() = default;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取所有属性元数据
|
|
||||||
* @return 属性元数据列表
|
|
||||||
*/
|
|
||||||
virtual std::vector<PropertyMeta> 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<typename T>
|
|
||||||
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<T>(&v)) {
|
|
||||||
value = *ptr;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
accessors_[name] = std::move(accessor);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<PropertyMeta> getProperties() const override {
|
|
||||||
std::vector<PropertyMeta> 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<std::string, PropertyAccessor> accessors_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -1,137 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <extra2d/core/service_interface.h>
|
|
||||||
#include <extra2d/core/service_locator.h>
|
|
||||||
#include <functional>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 服务注册信息
|
|
||||||
*/
|
|
||||||
struct ServiceRegistration {
|
|
||||||
std::string name;
|
|
||||||
ServicePriority priority;
|
|
||||||
std::function<SharedPtr<IService>()> factory;
|
|
||||||
bool enabled = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 服务注册表
|
|
||||||
* 管理服务的注册信息,支持延迟创建和配置
|
|
||||||
*/
|
|
||||||
class ServiceRegistry {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief 获取单例实例
|
|
||||||
* @return 服务注册表实例引用
|
|
||||||
*/
|
|
||||||
static ServiceRegistry& instance();
|
|
||||||
|
|
||||||
ServiceRegistry(const ServiceRegistry&) = delete;
|
|
||||||
ServiceRegistry& operator=(const ServiceRegistry&) = delete;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 注册服务
|
|
||||||
* @tparam T 服务接口类型
|
|
||||||
* @tparam Impl 服务实现类型
|
|
||||||
* @param name 服务名称
|
|
||||||
* @param priority 服务优先级
|
|
||||||
*/
|
|
||||||
template<typename T, typename Impl>
|
|
||||||
void registerService(const std::string& name, ServicePriority priority) {
|
|
||||||
static_assert(std::is_base_of_v<IService, T>,
|
|
||||||
"T must derive from IService");
|
|
||||||
static_assert(std::is_base_of_v<T, Impl>,
|
|
||||||
"Impl must derive from T");
|
|
||||||
|
|
||||||
ServiceRegistration reg;
|
|
||||||
reg.name = name;
|
|
||||||
reg.priority = priority;
|
|
||||||
reg.factory = []() -> SharedPtr<IService> {
|
|
||||||
return std::static_pointer_cast<IService>(makeShared<Impl>());
|
|
||||||
};
|
|
||||||
registrations_.push_back(reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 注册服务(带工厂函数)
|
|
||||||
* @tparam T 服务接口类型
|
|
||||||
* @param name 服务名称
|
|
||||||
* @param priority 服务优先级
|
|
||||||
* @param factory 工厂函数
|
|
||||||
*/
|
|
||||||
template<typename T>
|
|
||||||
void registerServiceWithFactory(
|
|
||||||
const std::string& name,
|
|
||||||
ServicePriority priority,
|
|
||||||
std::function<SharedPtr<T>()> factory) {
|
|
||||||
static_assert(std::is_base_of_v<IService, T>,
|
|
||||||
"T must derive from IService");
|
|
||||||
|
|
||||||
ServiceRegistration reg;
|
|
||||||
reg.name = name;
|
|
||||||
reg.priority = priority;
|
|
||||||
reg.factory = [factory]() -> SharedPtr<IService> {
|
|
||||||
return std::static_pointer_cast<IService>(factory());
|
|
||||||
};
|
|
||||||
registrations_.push_back(reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 启用/禁用服务
|
|
||||||
* @param name 服务名称
|
|
||||||
* @param enabled 是否启用
|
|
||||||
*/
|
|
||||||
void setServiceEnabled(const std::string& name, bool enabled);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建所有已注册的服务
|
|
||||||
* 并注册到 ServiceLocator
|
|
||||||
*/
|
|
||||||
void createAllServices();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取所有注册信息
|
|
||||||
* @return 注册信息列表
|
|
||||||
*/
|
|
||||||
const std::vector<ServiceRegistration>& getRegistrations() const {
|
|
||||||
return registrations_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 清空所有注册
|
|
||||||
*/
|
|
||||||
void clear() {
|
|
||||||
registrations_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ServiceRegistry() = default;
|
|
||||||
~ServiceRegistry() = default;
|
|
||||||
|
|
||||||
std::vector<ServiceRegistration> registrations_;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 自动服务注册器
|
|
||||||
* 在全局作用域使用,自动注册服务
|
|
||||||
*/
|
|
||||||
template<typename Interface, typename Implementation>
|
|
||||||
class AutoServiceRegistrar {
|
|
||||||
public:
|
|
||||||
AutoServiceRegistrar(const std::string& name, ServicePriority priority) {
|
|
||||||
ServiceRegistry::instance().registerService<Interface, Implementation>(
|
|
||||||
name, priority);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#define E2D_REGISTER_SERVICE_AUTO(Interface, Implementation, Name, Priority) \
|
|
||||||
namespace { \
|
|
||||||
static ::extra2d::AutoServiceRegistrar<Interface, Implementation> \
|
|
||||||
E2D_CONCAT(auto_service_registrar_, __LINE__)(Name, Priority); \
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file debug_config.h
|
|
||||||
* @brief 调试模块配置
|
|
||||||
*
|
|
||||||
* 定义调试相关的配置数据结构,由 DebugModule 管理。
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 调试配置数据结构
|
|
||||||
*/
|
|
||||||
struct DebugConfigData {
|
|
||||||
bool enabled = false;
|
|
||||||
bool showFPS = false;
|
|
||||||
bool showMemoryUsage = false;
|
|
||||||
bool showRenderStats = false;
|
|
||||||
bool showColliders = false;
|
|
||||||
bool showGrid = false;
|
|
||||||
bool logToFile = false;
|
|
||||||
bool logToConsole = true;
|
|
||||||
int logLevel = 2;
|
|
||||||
bool breakOnAssert = true;
|
|
||||||
bool enableProfiling = false;
|
|
||||||
std::string logFilePath;
|
|
||||||
std::vector<std::string> debugFlags;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查是否存在指定的调试标志
|
|
||||||
* @param flag 要检查的标志名称
|
|
||||||
* @return 如果存在返回 true
|
|
||||||
*/
|
|
||||||
bool hasDebugFlag(const std::string& flag) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 添加调试标志
|
|
||||||
* @param flag 要添加的标志名称
|
|
||||||
*/
|
|
||||||
void addDebugFlag(const std::string& flag);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 移除调试标志
|
|
||||||
* @param flag 要移除的标志名称
|
|
||||||
*/
|
|
||||||
void removeDebugFlag(const std::string& flag);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -49,8 +49,6 @@
|
||||||
|
|
||||||
// Event
|
// Event
|
||||||
#include <extra2d/event/event.h>
|
#include <extra2d/event/event.h>
|
||||||
#include <extra2d/event/event_dispatcher.h>
|
|
||||||
#include <extra2d/event/event_queue.h>
|
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
#include <extra2d/utils/logger.h>
|
#include <extra2d/utils/logger.h>
|
||||||
|
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <extra2d/core/math_types.h>
|
|
||||||
#include <extra2d/graphics/render_backend.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file render_config.h
|
|
||||||
* @brief 渲染模块配置
|
|
||||||
*
|
|
||||||
* 定义渲染相关的配置数据结构,由 RenderModule 管理。
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 渲染配置数据结构
|
|
||||||
*/
|
|
||||||
struct RenderConfigData {
|
|
||||||
BackendType backend = BackendType::OpenGL;
|
|
||||||
int targetFPS = 60;
|
|
||||||
bool vsync = true;
|
|
||||||
bool tripleBuffering = false;
|
|
||||||
int multisamples = 0;
|
|
||||||
bool sRGBFramebuffer = false;
|
|
||||||
Color clearColor{0.0f, 0.0f, 0.0f, 1.0f};
|
|
||||||
int maxTextureSize = 0;
|
|
||||||
int textureAnisotropy = 1;
|
|
||||||
bool wireframeMode = false;
|
|
||||||
bool depthTest = false;
|
|
||||||
bool blending = true;
|
|
||||||
bool dithering = false;
|
|
||||||
int spriteBatchSize = 1000;
|
|
||||||
int maxRenderTargets = 1;
|
|
||||||
bool allowShaderHotReload = false;
|
|
||||||
std::string shaderCachePath;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查是否启用多重采样
|
|
||||||
* @return 如果多重采样数大于0返回 true
|
|
||||||
*/
|
|
||||||
bool isMultisampleEnabled() const { return multisamples > 0; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查是否限制帧率
|
|
||||||
* @return 如果设置了目标帧率返回 true
|
|
||||||
*/
|
|
||||||
bool isFPSCapped() const { return targetFPS > 0; }
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,131 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <extra2d/core/types.h>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Shader缓存条目
|
|
||||||
// ============================================================================
|
|
||||||
struct ShaderCacheEntry {
|
|
||||||
std::string name;
|
|
||||||
std::string sourceHash;
|
|
||||||
uint64_t compileTime = 0;
|
|
||||||
std::vector<uint8_t> binary;
|
|
||||||
std::vector<std::string> dependencies;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Shader缓存管理器
|
|
||||||
// ============================================================================
|
|
||||||
class ShaderCache {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief 获取单例实例
|
|
||||||
* @return 缓存管理器实例引用
|
|
||||||
*/
|
|
||||||
static ShaderCache& getInstance();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 初始化缓存系统
|
|
||||||
* @param cacheDir 缓存目录路径
|
|
||||||
* @return 初始化成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool init(const std::string& cacheDir);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 关闭缓存系统
|
|
||||||
*/
|
|
||||||
void shutdown();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查缓存是否有效
|
|
||||||
* @param name Shader名称
|
|
||||||
* @param sourceHash 源码哈希值
|
|
||||||
* @return 缓存有效返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool hasValidCache(const std::string& name, const std::string& sourceHash);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 加载缓存的二进制数据
|
|
||||||
* @param name Shader名称
|
|
||||||
* @return 缓存条目指针,不存在返回nullptr
|
|
||||||
*/
|
|
||||||
Ptr<ShaderCacheEntry> loadCache(const std::string& name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 保存编译结果到缓存
|
|
||||||
* @param entry 缓存条目
|
|
||||||
* @return 保存成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool saveCache(const ShaderCacheEntry& entry);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 使缓存失效
|
|
||||||
* @param name Shader名称
|
|
||||||
*/
|
|
||||||
void invalidate(const std::string& name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 清除所有缓存
|
|
||||||
*/
|
|
||||||
void clearAll();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 计算源码哈希值
|
|
||||||
* @param vertSource 顶点着色器源码
|
|
||||||
* @param fragSource 片段着色器源码
|
|
||||||
* @return 哈希值字符串
|
|
||||||
*/
|
|
||||||
static std::string computeHash(const std::string& vertSource,
|
|
||||||
const std::string& fragSource);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查是否已初始化
|
|
||||||
* @return 已初始化返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool isInitialized() const { return initialized_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
ShaderCache() = default;
|
|
||||||
~ShaderCache() = default;
|
|
||||||
ShaderCache(const ShaderCache&) = delete;
|
|
||||||
ShaderCache& operator=(const ShaderCache&) = delete;
|
|
||||||
|
|
||||||
std::string cacheDir_;
|
|
||||||
std::unordered_map<std::string, ShaderCacheEntry> cacheMap_;
|
|
||||||
bool initialized_ = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 加载缓存索引
|
|
||||||
* @return 加载成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool loadCacheIndex();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 保存缓存索引
|
|
||||||
* @return 保存成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool saveCacheIndex();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取缓存文件路径
|
|
||||||
* @param name Shader名称
|
|
||||||
* @return 缓存文件完整路径
|
|
||||||
*/
|
|
||||||
std::string getCachePath(const std::string& name) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 确保缓存目录存在
|
|
||||||
* @return 目录存在或创建成功返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool ensureCacheDirectory();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 便捷宏
|
|
||||||
#define E2D_SHADER_CACHE() ::extra2d::ShaderCache::getInstance()
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -1,137 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <extra2d/core/types.h>
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 文件变化事件
|
|
||||||
// ============================================================================
|
|
||||||
struct FileChangeEvent {
|
|
||||||
std::string filepath;
|
|
||||||
|
|
||||||
enum class Type {
|
|
||||||
Created,
|
|
||||||
Modified,
|
|
||||||
Deleted,
|
|
||||||
Renamed
|
|
||||||
} type;
|
|
||||||
|
|
||||||
uint64_t timestamp = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 文件变化回调
|
|
||||||
// ============================================================================
|
|
||||||
using FileChangeCallback = std::function<void(const FileChangeEvent&)>;
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Shader热重载管理器
|
|
||||||
// ============================================================================
|
|
||||||
class ShaderHotReloader {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief 获取单例实例
|
|
||||||
* @return 热重载管理器实例引用
|
|
||||||
*/
|
|
||||||
static ShaderHotReloader& getInstance();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 初始化热重载系统
|
|
||||||
* @return 初始化成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool init();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 关闭热重载系统
|
|
||||||
*/
|
|
||||||
void shutdown();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 注册Shader文件监视
|
|
||||||
* @param shaderName Shader名称
|
|
||||||
* @param filePaths 要监视的文件列表
|
|
||||||
* @param callback 文件变化时的回调
|
|
||||||
*/
|
|
||||||
void watch(const std::string& shaderName,
|
|
||||||
const std::vector<std::string>& filePaths,
|
|
||||||
FileChangeCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 取消监视
|
|
||||||
* @param shaderName Shader名称
|
|
||||||
*/
|
|
||||||
void unwatch(const std::string& shaderName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 更新文件监视(在主循环中调用)
|
|
||||||
*/
|
|
||||||
void update();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 启用/禁用热重载
|
|
||||||
* @param enabled 是否启用
|
|
||||||
*/
|
|
||||||
void setEnabled(bool enabled);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查是否启用
|
|
||||||
* @return 启用返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool isEnabled() const { return enabled_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查是否已初始化
|
|
||||||
* @return 已初始化返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool isInitialized() const { return initialized_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
ShaderHotReloader() = default;
|
|
||||||
~ShaderHotReloader() = default;
|
|
||||||
ShaderHotReloader(const ShaderHotReloader&) = delete;
|
|
||||||
ShaderHotReloader& operator=(const ShaderHotReloader&) = delete;
|
|
||||||
|
|
||||||
bool enabled_ = false;
|
|
||||||
bool initialized_ = false;
|
|
||||||
|
|
||||||
struct WatchInfo {
|
|
||||||
std::vector<std::string> filePaths;
|
|
||||||
FileChangeCallback callback;
|
|
||||||
std::unordered_map<std::string, uint64_t> modifiedTimes;
|
|
||||||
};
|
|
||||||
std::unordered_map<std::string, WatchInfo> watchMap_;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
HANDLE watchHandle_ = nullptr;
|
|
||||||
std::vector<uint8_t> buffer_;
|
|
||||||
std::string watchDir_;
|
|
||||||
bool watching_ = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 轮询检查文件变化
|
|
||||||
*/
|
|
||||||
void pollChanges();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取文件修改时间
|
|
||||||
* @param filepath 文件路径
|
|
||||||
* @return 修改时间戳
|
|
||||||
*/
|
|
||||||
static uint64_t getFileModifiedTime(const std::string& filepath);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 便捷宏
|
|
||||||
#define E2D_SHADER_HOT_RELOADER() ::extra2d::ShaderHotReloader::getInstance()
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -32,136 +32,34 @@ struct ShaderMetadata {
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// ShaderLoader接口 - 支持多种文件格式加载
|
// ShaderLoader - Shader文件加载
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
class IShaderLoader {
|
class ShaderLoader {
|
||||||
public:
|
|
||||||
virtual ~IShaderLoader() = default;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从分离文件加载Shader (.vert + .frag)
|
|
||||||
* @param name Shader名称
|
|
||||||
* @param vertPath 顶点着色器文件路径
|
|
||||||
* @param fragPath 片段着色器文件路径
|
|
||||||
* @return 加载结果
|
|
||||||
*/
|
|
||||||
virtual ShaderLoadResult loadFromSeparateFiles(
|
|
||||||
const std::string& name,
|
|
||||||
const std::string& vertPath,
|
|
||||||
const std::string& fragPath) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从组合文件加载Shader (.shader)
|
|
||||||
* @param path 组合Shader文件路径
|
|
||||||
* @return 加载结果
|
|
||||||
*/
|
|
||||||
virtual ShaderLoadResult loadFromCombinedFile(const std::string& path) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从源码字符串加载Shader
|
|
||||||
* @param vertSource 顶点着色器源码
|
|
||||||
* @param fragSource 片段着色器源码
|
|
||||||
* @return 加载结果
|
|
||||||
*/
|
|
||||||
virtual ShaderLoadResult loadFromSource(
|
|
||||||
const std::string& vertSource,
|
|
||||||
const std::string& fragSource) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 处理Shader源码中的#include指令
|
|
||||||
* @param source 原始源码
|
|
||||||
* @param baseDir 基础目录
|
|
||||||
* @param outDependencies 输出依赖列表
|
|
||||||
* @return 处理后的源码
|
|
||||||
*/
|
|
||||||
virtual std::string processIncludes(
|
|
||||||
const std::string& source,
|
|
||||||
const std::string& baseDir,
|
|
||||||
std::vector<std::string>& outDependencies) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 应用预处理器定义
|
|
||||||
* @param source 原始源码
|
|
||||||
* @param defines 预处理器定义列表
|
|
||||||
* @return 处理后的源码
|
|
||||||
*/
|
|
||||||
virtual std::string applyDefines(
|
|
||||||
const std::string& source,
|
|
||||||
const std::vector<std::string>& defines) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取Shader元数据
|
|
||||||
* @param path Shader文件路径
|
|
||||||
* @return 元数据
|
|
||||||
*/
|
|
||||||
virtual ShaderMetadata getMetadata(const std::string& path) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 默认ShaderLoader实现
|
|
||||||
// ============================================================================
|
|
||||||
class ShaderLoader : public IShaderLoader {
|
|
||||||
public:
|
public:
|
||||||
ShaderLoader();
|
ShaderLoader();
|
||||||
~ShaderLoader() override = default;
|
~ShaderLoader() = default;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从分离文件加载Shader (.vert + .frag)
|
|
||||||
* @param name Shader名称
|
|
||||||
* @param vertPath 顶点着色器文件路径
|
|
||||||
* @param fragPath 片段着色器文件路径
|
|
||||||
* @return 加载结果
|
|
||||||
*/
|
|
||||||
ShaderLoadResult loadFromSeparateFiles(
|
ShaderLoadResult loadFromSeparateFiles(
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
const std::string& vertPath,
|
const std::string& vertPath,
|
||||||
const std::string& fragPath) override;
|
const std::string& fragPath);
|
||||||
|
|
||||||
/**
|
ShaderLoadResult loadFromCombinedFile(const std::string& path);
|
||||||
* @brief 从组合文件加载Shader (.shader)
|
|
||||||
* @param path 组合Shader文件路径
|
|
||||||
* @return 加载结果
|
|
||||||
*/
|
|
||||||
ShaderLoadResult loadFromCombinedFile(const std::string& path) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从源码字符串加载Shader
|
|
||||||
* @param vertSource 顶点着色器源码
|
|
||||||
* @param fragSource 片段着色器源码
|
|
||||||
* @return 加载结果
|
|
||||||
*/
|
|
||||||
ShaderLoadResult loadFromSource(
|
ShaderLoadResult loadFromSource(
|
||||||
const std::string& vertSource,
|
const std::string& vertSource,
|
||||||
const std::string& fragSource) override;
|
const std::string& fragSource);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 处理Shader源码中的#include指令
|
|
||||||
* @param source 原始源码
|
|
||||||
* @param baseDir 基础目录
|
|
||||||
* @param outDependencies 输出依赖列表
|
|
||||||
* @return 处理后的源码
|
|
||||||
*/
|
|
||||||
std::string processIncludes(
|
std::string processIncludes(
|
||||||
const std::string& source,
|
const std::string& source,
|
||||||
const std::string& baseDir,
|
const std::string& baseDir,
|
||||||
std::vector<std::string>& outDependencies) override;
|
std::vector<std::string>& outDependencies);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 应用预处理器定义
|
|
||||||
* @param source 原始源码
|
|
||||||
* @param defines 预处理器定义列表
|
|
||||||
* @return 处理后的源码
|
|
||||||
*/
|
|
||||||
std::string applyDefines(
|
std::string applyDefines(
|
||||||
const std::string& source,
|
const std::string& source,
|
||||||
const std::vector<std::string>& defines) override;
|
const std::vector<std::string>& defines);
|
||||||
|
|
||||||
/**
|
ShaderMetadata getMetadata(const std::string& path);
|
||||||
* @brief 获取Shader元数据
|
|
||||||
* @param path Shader文件路径
|
|
||||||
* @return 元数据
|
|
||||||
*/
|
|
||||||
ShaderMetadata getMetadata(const std::string& path) override;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 添加include搜索路径
|
* @brief 添加include搜索路径
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,52 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <extra2d/graphics/shader_cache.h>
|
|
||||||
#include <extra2d/graphics/shader_hot_reloader.h>
|
|
||||||
#include <extra2d/graphics/shader_interface.h>
|
#include <extra2d/graphics/shader_interface.h>
|
||||||
#include <extra2d/graphics/shader_loader.h>
|
#include <extra2d/graphics/shader_loader.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Shader缓存条目
|
||||||
|
// ============================================================================
|
||||||
|
struct ShaderCacheEntry {
|
||||||
|
std::string name;
|
||||||
|
std::string sourceHash;
|
||||||
|
uint64_t compileTime = 0;
|
||||||
|
std::vector<uint8_t> binary;
|
||||||
|
std::vector<std::string> dependencies;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 文件变化事件
|
||||||
|
// ============================================================================
|
||||||
|
struct FileChangeEvent {
|
||||||
|
std::string filepath;
|
||||||
|
|
||||||
|
enum class Type {
|
||||||
|
Created,
|
||||||
|
Modified,
|
||||||
|
Deleted,
|
||||||
|
Renamed
|
||||||
|
} type;
|
||||||
|
|
||||||
|
uint64_t timestamp = 0;
|
||||||
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Shader重载回调
|
// Shader重载回调
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
using ShaderReloadCallback = std::function<void(Ptr<IShader> newShader)>;
|
using ShaderReloadCallback = std::function<void(Ptr<IShader> newShader)>;
|
||||||
|
using FileChangeCallback = std::function<void(const FileChangeEvent&)>;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Shader管理器 - 统一入口
|
// Shader管理器 - 统一入口(加载/缓存/热重载)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
class ShaderManager {
|
class ShaderManager {
|
||||||
public:
|
public:
|
||||||
|
|
@ -31,7 +62,6 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 使用平台默认路径初始化Shader系统
|
* @brief 使用平台默认路径初始化Shader系统
|
||||||
* 自动检测平台并使用正确的路径(romfs/sdmc/相对路径)
|
|
||||||
* @param factory 渲染后端Shader工厂
|
* @param factory 渲染后端Shader工厂
|
||||||
* @param appName 应用名称(用于缓存目录)
|
* @param appName 应用名称(用于缓存目录)
|
||||||
* @return 初始化成功返回true,失败返回false
|
* @return 初始化成功返回true,失败返回false
|
||||||
|
|
@ -56,14 +86,11 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查是否已初始化
|
* @brief 检查是否已初始化
|
||||||
* @return 已初始化返回true,否则返回false
|
|
||||||
*/
|
*/
|
||||||
bool isInitialized() const { return initialized_; }
|
bool isInitialized() const { return initialized_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查当前平台是否支持热重载
|
* @brief 检查当前平台是否支持热重载
|
||||||
* Switch平台使用romfs,不支持热重载
|
|
||||||
* @return 支持热重载返回true
|
|
||||||
*/
|
*/
|
||||||
bool isHotReloadSupported() const { return hotReloadSupported_; }
|
bool isHotReloadSupported() const { return hotReloadSupported_; }
|
||||||
|
|
||||||
|
|
@ -71,134 +98,73 @@ public:
|
||||||
// Shader加载
|
// Shader加载
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从分离文件加载Shader
|
|
||||||
* @param name Shader名称
|
|
||||||
* @param vertPath 顶点着色器文件路径
|
|
||||||
* @param fragPath 片段着色器文件路径
|
|
||||||
* @return 加载的Shader实例
|
|
||||||
*/
|
|
||||||
Ptr<IShader> loadFromFiles(const std::string& name,
|
Ptr<IShader> loadFromFiles(const std::string& name,
|
||||||
const std::string& vertPath,
|
const std::string& vertPath,
|
||||||
const std::string& fragPath);
|
const std::string& fragPath);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从组合文件加载Shader
|
|
||||||
* @param path 组合Shader文件路径
|
|
||||||
* @return 加载的Shader实例
|
|
||||||
*/
|
|
||||||
Ptr<IShader> loadFromCombinedFile(const std::string& path);
|
Ptr<IShader> loadFromCombinedFile(const std::string& path);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从源码加载Shader
|
|
||||||
* @param name Shader名称
|
|
||||||
* @param vertSource 顶点着色器源码
|
|
||||||
* @param fragSource 片段着色器源码
|
|
||||||
* @return 加载的Shader实例
|
|
||||||
*/
|
|
||||||
Ptr<IShader> loadFromSource(const std::string& name,
|
Ptr<IShader> loadFromSource(const std::string& name,
|
||||||
const std::string& vertSource,
|
const std::string& vertSource,
|
||||||
const std::string& fragSource);
|
const std::string& fragSource);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取已加载的Shader
|
|
||||||
* @param name Shader名称
|
|
||||||
* @return Shader实例,不存在返回nullptr
|
|
||||||
*/
|
|
||||||
Ptr<IShader> get(const std::string& name) const;
|
Ptr<IShader> get(const std::string& name) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查Shader是否存在
|
|
||||||
* @param name Shader名称
|
|
||||||
* @return 存在返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool has(const std::string& name) const;
|
bool has(const std::string& name) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 移除Shader
|
|
||||||
* @param name Shader名称
|
|
||||||
*/
|
|
||||||
void remove(const std::string& name);
|
void remove(const std::string& name);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 清除所有Shader
|
|
||||||
*/
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 热重载
|
// 热重载
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 注册重载回调
|
|
||||||
* @param name Shader名称
|
|
||||||
* @param callback 重载回调函数
|
|
||||||
*/
|
|
||||||
void setReloadCallback(const std::string& name, ShaderReloadCallback callback);
|
void setReloadCallback(const std::string& name, ShaderReloadCallback callback);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 启用/禁用热重载
|
|
||||||
* @param enabled 是否启用
|
|
||||||
*/
|
|
||||||
void setHotReloadEnabled(bool enabled);
|
void setHotReloadEnabled(bool enabled);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查热重载是否启用
|
|
||||||
* @return 启用返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool isHotReloadEnabled() const;
|
bool isHotReloadEnabled() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 更新热重载系统(主循环调用)
|
|
||||||
*/
|
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 手动重载Shader
|
|
||||||
* @param name Shader名称
|
|
||||||
* @return 重载成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool reload(const std::string& name);
|
bool reload(const std::string& name);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// 热重载文件监视
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void watch(const std::string& shaderName,
|
||||||
|
const std::vector<std::string>& filePaths,
|
||||||
|
FileChangeCallback callback);
|
||||||
|
void unwatch(const std::string& shaderName);
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 内置Shader
|
// 内置Shader
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取内置Shader
|
|
||||||
* @param name 内置Shader名称
|
|
||||||
* @return Shader实例
|
|
||||||
*/
|
|
||||||
Ptr<IShader> getBuiltin(const std::string& name);
|
Ptr<IShader> getBuiltin(const std::string& name);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 加载所有内置Shader
|
|
||||||
* @return 加载成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool loadBuiltinShaders();
|
bool loadBuiltinShaders();
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 工具方法
|
// 工具方法
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取Shader目录
|
|
||||||
* @return Shader目录路径
|
|
||||||
*/
|
|
||||||
const std::string& getShaderDir() const { return shaderDir_; }
|
const std::string& getShaderDir() const { return shaderDir_; }
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取ShaderLoader
|
|
||||||
* @return ShaderLoader引用
|
|
||||||
*/
|
|
||||||
ShaderLoader& getLoader() { return loader_; }
|
ShaderLoader& getLoader() { return loader_; }
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// 缓存
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool hasValidCache(const std::string& name, const std::string& sourceHash);
|
||||||
|
Ptr<ShaderCacheEntry> loadCache(const std::string& name);
|
||||||
|
bool saveCache(const ShaderCacheEntry& entry);
|
||||||
|
void invalidateCache(const std::string& name);
|
||||||
|
void clearAllCache();
|
||||||
|
static std::string computeHash(const std::string& vertSource,
|
||||||
|
const std::string& fragSource);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ShaderManager() = default;
|
ShaderManager() = default;
|
||||||
~ShaderManager() = default;
|
~ShaderManager() = default;
|
||||||
ShaderManager(const ShaderManager&) = delete;
|
ShaderManager(const ShaderManager&) = delete;
|
||||||
ShaderManager& operator=(const ShaderManager&) = delete;
|
ShaderManager& operator=(const ShaderManager&) = delete;
|
||||||
|
|
||||||
|
// Shader存储
|
||||||
std::string shaderDir_;
|
std::string shaderDir_;
|
||||||
std::string cacheDir_;
|
std::string cacheDir_;
|
||||||
Ptr<IShaderFactory> factory_;
|
Ptr<IShaderFactory> factory_;
|
||||||
|
|
@ -218,29 +184,45 @@ private:
|
||||||
bool hotReloadEnabled_ = false;
|
bool hotReloadEnabled_ = false;
|
||||||
bool hotReloadSupported_ = true;
|
bool hotReloadSupported_ = true;
|
||||||
|
|
||||||
/**
|
// 缓存(原 ShaderCache)
|
||||||
* @brief 从缓存加载Shader
|
std::unordered_map<std::string, ShaderCacheEntry> cacheMap_;
|
||||||
* @param name Shader名称
|
bool cacheInitialized_ = false;
|
||||||
* @param sourceHash 源码哈希值
|
|
||||||
* @param vertSource 顶点着色器源码
|
bool initCache(const std::string& cacheDir);
|
||||||
* @param fragSource 片段着色器源码
|
void shutdownCache();
|
||||||
* @return Shader实例
|
bool loadCacheIndex();
|
||||||
*/
|
bool saveCacheIndex();
|
||||||
|
std::string getCachePath(const std::string& name) const;
|
||||||
|
bool ensureCacheDirectory();
|
||||||
|
|
||||||
|
// 热重载(原 ShaderHotReloader)
|
||||||
|
bool reloaderInitialized_ = false;
|
||||||
|
|
||||||
|
struct WatchInfo {
|
||||||
|
std::vector<std::string> filePaths;
|
||||||
|
FileChangeCallback callback;
|
||||||
|
std::unordered_map<std::string, uint64_t> modifiedTimes;
|
||||||
|
};
|
||||||
|
std::unordered_map<std::string, WatchInfo> watchMap_;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE watchHandle_ = nullptr;
|
||||||
|
std::vector<uint8_t> watchBuffer_;
|
||||||
|
std::string watchDir_;
|
||||||
|
bool watching_ = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool initReloader();
|
||||||
|
void shutdownReloader();
|
||||||
|
void pollChanges();
|
||||||
|
static uint64_t getFileModifiedTime(const std::string& filepath);
|
||||||
|
|
||||||
Ptr<IShader> loadFromCache(const std::string& name,
|
Ptr<IShader> loadFromCache(const std::string& name,
|
||||||
const std::string& sourceHash,
|
const std::string& sourceHash,
|
||||||
const std::string& vertSource,
|
const std::string& vertSource,
|
||||||
const std::string& fragSource);
|
const std::string& fragSource);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建内置Shader源码
|
|
||||||
*/
|
|
||||||
void createBuiltinShaderSources();
|
void createBuiltinShaderSources();
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 处理文件变化事件
|
|
||||||
* @param shaderName Shader名称
|
|
||||||
* @param event 文件变化事件
|
|
||||||
*/
|
|
||||||
void handleFileChange(const std::string& shaderName, const FileChangeEvent& event);
|
void handleFileChange(const std::string& shaderName, const FileChangeEvent& event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <extra2d/core/color.h>
|
|
||||||
#include <extra2d/core/types.h>
|
|
||||||
#include <extra2d/graphics/shader_interface.h>
|
|
||||||
#include <glm/vec4.hpp>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
struct WaterParams {
|
|
||||||
float waveSpeed = 1.0f;
|
|
||||||
float waveAmplitude = 0.02f;
|
|
||||||
float waveFrequency = 4.0f;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct OutlineParams {
|
|
||||||
Color color = Colors::Black;
|
|
||||||
float thickness = 2.0f;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DistortionParams {
|
|
||||||
float distortionAmount = 0.02f;
|
|
||||||
float timeScale = 1.0f;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PixelateParams {
|
|
||||||
float pixelSize = 8.0f;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InvertParams {
|
|
||||||
float strength = 1.0f;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GrayscaleParams {
|
|
||||||
float intensity = 1.0f;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BlurParams {
|
|
||||||
float radius = 5.0f;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ShaderPreset {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief 创建水波纹效果着色器
|
|
||||||
* @param params 水波纹效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
static Ptr<IShader> Water(const WaterParams& params = {});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建描边效果着色器
|
|
||||||
* @param params 描边效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
static Ptr<IShader> Outline(const OutlineParams& params = {});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建扭曲效果着色器
|
|
||||||
* @param params 扭曲效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
static Ptr<IShader> Distortion(const DistortionParams& params = {});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建像素化效果着色器
|
|
||||||
* @param params 像素化效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
static Ptr<IShader> Pixelate(const PixelateParams& params = {});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建反相效果着色器
|
|
||||||
* @param params 反相效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
static Ptr<IShader> Invert(const InvertParams& params = {});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建灰度效果着色器
|
|
||||||
* @param params 灰度效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
static Ptr<IShader> Grayscale(const GrayscaleParams& params = {});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建模糊效果着色器
|
|
||||||
* @param params 模糊效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
static Ptr<IShader> Blur(const BlurParams& params = {});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建灰度+描边组合效果着色器
|
|
||||||
* @param grayParams 灰度效果参数
|
|
||||||
* @param outlineParams 描边效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
static Ptr<IShader> GrayscaleOutline(const GrayscaleParams& grayParams,
|
|
||||||
const OutlineParams& outlineParams);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建像素化+反相组合效果着色器
|
|
||||||
* @param pixParams 像素化效果参数
|
|
||||||
* @param invParams 反相效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
static Ptr<IShader> PixelateInvert(const PixelateParams& pixParams,
|
|
||||||
const InvertParams& invParams);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -382,6 +382,12 @@ public:
|
||||||
*/
|
*/
|
||||||
void init(Scene* scene, size_t maxMemoryUsage = 0);
|
void init(Scene* scene, size_t maxMemoryUsage = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置渲染后端
|
||||||
|
* @param backend 渲染后端指针
|
||||||
|
*/
|
||||||
|
void setRenderBackend(RenderBackend* backend) { backend_ = backend; }
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// 纹理加载
|
// 纹理加载
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
@ -554,6 +560,7 @@ private:
|
||||||
void tryAutoEvict();
|
void tryAutoEvict();
|
||||||
|
|
||||||
Scene* scene_; // 场景指针
|
Scene* scene_; // 场景指针
|
||||||
|
RenderBackend* backend_ = nullptr; // 渲染后端
|
||||||
mutable std::mutex mutex_; // 互斥锁
|
mutable std::mutex mutex_; // 互斥锁
|
||||||
std::unordered_map<TextureKey, TexturePoolEntry, TextureKeyHash> cache_; // 纹理缓存
|
std::unordered_map<TextureKey, TexturePoolEntry, TextureKeyHash> cache_; // 纹理缓存
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <extra2d/core/module.h>
|
#include <extra2d/core/module.h>
|
||||||
#include <extra2d/graphics/render_config.h>
|
#include <extra2d/core/math_types.h>
|
||||||
#include <extra2d/graphics/render_backend.h>
|
#include <extra2d/graphics/render_backend.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -16,6 +17,27 @@ struct RenderModuleConfig {
|
||||||
int multisamples = 0;
|
int multisamples = 0;
|
||||||
bool sRGBFramebuffer = false;
|
bool sRGBFramebuffer = false;
|
||||||
int spriteBatchSize = 1000;
|
int spriteBatchSize = 1000;
|
||||||
|
bool tripleBuffering = false;
|
||||||
|
Color clearColor{0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
int maxTextureSize = 0;
|
||||||
|
int textureAnisotropy = 1;
|
||||||
|
bool wireframeMode = false;
|
||||||
|
bool depthTest = false;
|
||||||
|
bool blending = true;
|
||||||
|
bool dithering = false;
|
||||||
|
int maxRenderTargets = 1;
|
||||||
|
bool allowShaderHotReload = false;
|
||||||
|
std::string shaderCachePath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否启用多重采样
|
||||||
|
*/
|
||||||
|
bool isMultisampleEnabled() const { return multisamples > 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否限制帧率
|
||||||
|
*/
|
||||||
|
bool isFPSCapped() const { return targetFPS > 0; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 验证配置有效性
|
* @brief 验证配置有效性
|
||||||
|
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file resource_config.h
|
|
||||||
* @brief 资源模块配置
|
|
||||||
*
|
|
||||||
* 定义资源相关的配置数据结构,由 ResourceModule 管理。
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 资源配置数据结构
|
|
||||||
*/
|
|
||||||
struct ResourceConfigData {
|
|
||||||
std::string assetRootPath = "assets";
|
|
||||||
std::string cachePath = "cache";
|
|
||||||
std::string savePath = "saves";
|
|
||||||
std::string configPath = "config";
|
|
||||||
std::string logPath = "logs";
|
|
||||||
bool useAssetCache = true;
|
|
||||||
int maxCacheSize = 512;
|
|
||||||
bool hotReloadEnabled = false;
|
|
||||||
float hotReloadInterval = 1.0f;
|
|
||||||
bool compressTextures = false;
|
|
||||||
bool preloadCommonAssets = true;
|
|
||||||
std::vector<std::string> searchPaths;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 添加资源搜索路径
|
|
||||||
* @param path 要添加的搜索路径
|
|
||||||
*/
|
|
||||||
void addSearchPath(const std::string& path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 移除资源搜索路径
|
|
||||||
* @param path 要移除的搜索路径
|
|
||||||
*/
|
|
||||||
void removeSearchPath(const std::string& path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查是否存在指定的搜索路径
|
|
||||||
* @param path 要检查的路径
|
|
||||||
* @return 如果存在返回 true
|
|
||||||
*/
|
|
||||||
bool hasSearchPath(const std::string& path) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -60,11 +60,6 @@ public:
|
||||||
size_t getTotalListenerCount() const override;
|
size_t getTotalListenerCount() const override;
|
||||||
size_t getQueueSize() const override;
|
size_t getQueueSize() const override;
|
||||||
|
|
||||||
EventQueue& getQueue() { return queue_; }
|
|
||||||
const EventQueue& getQueue() const { return queue_; }
|
|
||||||
EventDispatcher& getDispatcher() { return dispatcher_; }
|
|
||||||
const EventDispatcher& getDispatcher() const { return dispatcher_; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EventQueue queue_;
|
EventQueue queue_;
|
||||||
EventDispatcher dispatcher_;
|
EventDispatcher dispatcher_;
|
||||||
|
|
|
||||||
|
|
@ -256,9 +256,6 @@ void Application::mainLoop() {
|
||||||
|
|
||||||
render();
|
render();
|
||||||
|
|
||||||
auto renderModule = getModule<RenderModule>();
|
|
||||||
(void)renderModule;
|
|
||||||
|
|
||||||
ConfigManager::instance().update(deltaTime_);
|
ConfigManager::instance().update(deltaTime_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -331,11 +328,8 @@ RenderBackend &Application::renderer() {
|
||||||
if (renderModule && renderModule->getRenderer()) {
|
if (renderModule && renderModule->getRenderer()) {
|
||||||
return *renderModule->getRenderer();
|
return *renderModule->getRenderer();
|
||||||
}
|
}
|
||||||
static RenderBackend *dummy = nullptr;
|
E2D_LOG_ERROR("RenderModule not initialized - renderer() called too early");
|
||||||
if (!dummy) {
|
std::abort();
|
||||||
dummy = RenderBackend::create(BackendType::OpenGL).release();
|
|
||||||
}
|
|
||||||
return *dummy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedPtr<ISceneService> Application::scenes() {
|
SharedPtr<ISceneService> Application::scenes() {
|
||||||
|
|
|
||||||
|
|
@ -54,16 +54,4 @@ void RenderContext::handle(Module* m) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// EventContext 实现
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
EventContext::EventContext(std::vector<Module*>& modules)
|
|
||||||
: ModuleContext(modules) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventContext::handle(Module* m) {
|
|
||||||
m->handleEvent(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -205,9 +205,6 @@ bool ModuleRegistry::createAndInitAll() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyBinderImpl binder;
|
|
||||||
meta->bindProperties(instance, binder);
|
|
||||||
|
|
||||||
instance->setupModule();
|
instance->setupModule();
|
||||||
|
|
||||||
instances_.emplace_back(instance);
|
instances_.emplace_back(instance);
|
||||||
|
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
#include <extra2d/core/service_registry.h>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
ServiceRegistry& ServiceRegistry::instance() {
|
|
||||||
static ServiceRegistry instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServiceRegistry::setServiceEnabled(const std::string& name, bool enabled) {
|
|
||||||
for (auto& reg : registrations_) {
|
|
||||||
if (reg.name == name) {
|
|
||||||
reg.enabled = enabled;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServiceRegistry::createAllServices() {
|
|
||||||
std::sort(registrations_.begin(), registrations_.end(),
|
|
||||||
[](const ServiceRegistration& a, const ServiceRegistration& b) {
|
|
||||||
return static_cast<int>(a.priority) < static_cast<int>(b.priority);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const auto& reg : registrations_) {
|
|
||||||
if (!reg.enabled) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto service = reg.factory();
|
|
||||||
if (service) {
|
|
||||||
ServiceLocator::instance().registerService<IService>(service);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
#include <extra2d/debug/debug_config.h>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
bool DebugConfigData::hasDebugFlag(const std::string& flag) const {
|
|
||||||
return std::find(debugFlags.begin(), debugFlags.end(), flag) != debugFlags.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebugConfigData::addDebugFlag(const std::string& flag) {
|
|
||||||
if (!hasDebugFlag(flag)) {
|
|
||||||
debugFlags.push_back(flag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebugConfigData::removeDebugFlag(const std::string& flag) {
|
|
||||||
auto it = std::find(debugFlags.begin(), debugFlags.end(), flag);
|
|
||||||
if (it != debugFlags.end()) {
|
|
||||||
debugFlags.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,286 +0,0 @@
|
||||||
#include <extra2d/graphics/shader_cache.h>
|
|
||||||
#include <extra2d/utils/logger.h>
|
|
||||||
#include <chrono>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取单例实例
|
|
||||||
* @return 缓存管理器实例引用
|
|
||||||
*/
|
|
||||||
ShaderCache& ShaderCache::getInstance() {
|
|
||||||
static ShaderCache instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 初始化缓存系统
|
|
||||||
* @param cacheDir 缓存目录路径
|
|
||||||
* @return 初始化成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool ShaderCache::init(const std::string& cacheDir) {
|
|
||||||
cacheDir_ = cacheDir;
|
|
||||||
|
|
||||||
if (!ensureCacheDirectory()) {
|
|
||||||
E2D_LOG_ERROR("Failed to create cache directory: {}", cacheDir);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!loadCacheIndex()) {
|
|
||||||
E2D_LOG_WARN("Failed to load cache index, starting fresh");
|
|
||||||
}
|
|
||||||
|
|
||||||
initialized_ = true;
|
|
||||||
E2D_LOG_INFO("Shader cache initialized at: {}", cacheDir);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 关闭缓存系统
|
|
||||||
*/
|
|
||||||
void ShaderCache::shutdown() {
|
|
||||||
if (!initialized_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
saveCacheIndex();
|
|
||||||
cacheMap_.clear();
|
|
||||||
initialized_ = false;
|
|
||||||
E2D_LOG_INFO("Shader cache shutdown");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查缓存是否有效
|
|
||||||
* @param name Shader名称
|
|
||||||
* @param sourceHash 源码哈希值
|
|
||||||
* @return 缓存有效返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool ShaderCache::hasValidCache(const std::string& name, const std::string& sourceHash) {
|
|
||||||
auto it = cacheMap_.find(name);
|
|
||||||
if (it == cacheMap_.end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return it->second.sourceHash == sourceHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 加载缓存的二进制数据
|
|
||||||
* @param name Shader名称
|
|
||||||
* @return 缓存条目指针,不存在返回nullptr
|
|
||||||
*/
|
|
||||||
Ptr<ShaderCacheEntry> ShaderCache::loadCache(const std::string& name) {
|
|
||||||
auto it = cacheMap_.find(name);
|
|
||||||
if (it == cacheMap_.end()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string cachePath = getCachePath(name);
|
|
||||||
std::ifstream file(cachePath, std::ios::binary);
|
|
||||||
if (!file.is_open()) {
|
|
||||||
E2D_LOG_WARN("Failed to open cache file: {}", cachePath);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto entry = std::make_shared<ShaderCacheEntry>(it->second);
|
|
||||||
entry->binary.clear();
|
|
||||||
|
|
||||||
file.seekg(0, std::ios::end);
|
|
||||||
size_t fileSize = static_cast<size_t>(file.tellg());
|
|
||||||
file.seekg(0, std::ios::beg);
|
|
||||||
|
|
||||||
entry->binary.resize(fileSize);
|
|
||||||
file.read(reinterpret_cast<char*>(entry->binary.data()), fileSize);
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 保存编译结果到缓存
|
|
||||||
* @param entry 缓存条目
|
|
||||||
* @return 保存成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool ShaderCache::saveCache(const ShaderCacheEntry& entry) {
|
|
||||||
if (!initialized_) {
|
|
||||||
E2D_LOG_WARN("ShaderCache not initialized, cannot save cache");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.binary.empty()) {
|
|
||||||
E2D_LOG_WARN("Shader binary is empty, skipping cache save for: {}", entry.name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string cachePath = getCachePath(entry.name);
|
|
||||||
E2D_LOG_DEBUG("Saving shader cache to: {} ({} bytes)", cachePath, entry.binary.size());
|
|
||||||
|
|
||||||
std::ofstream file(cachePath, std::ios::binary);
|
|
||||||
if (!file.is_open()) {
|
|
||||||
E2D_LOG_ERROR("Failed to create cache file: {}", cachePath);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
file.write(reinterpret_cast<const char*>(entry.binary.data()), entry.binary.size());
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
cacheMap_[entry.name] = entry;
|
|
||||||
saveCacheIndex();
|
|
||||||
|
|
||||||
E2D_LOG_INFO("Shader cache saved: {} ({} bytes)", entry.name, entry.binary.size());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 使缓存失效
|
|
||||||
* @param name Shader名称
|
|
||||||
*/
|
|
||||||
void ShaderCache::invalidate(const std::string& name) {
|
|
||||||
auto it = cacheMap_.find(name);
|
|
||||||
if (it == cacheMap_.end()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string cachePath = getCachePath(name);
|
|
||||||
fs::remove(cachePath);
|
|
||||||
|
|
||||||
cacheMap_.erase(it);
|
|
||||||
saveCacheIndex();
|
|
||||||
|
|
||||||
E2D_LOG_DEBUG("Shader cache invalidated: {}", name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 清除所有缓存
|
|
||||||
*/
|
|
||||||
void ShaderCache::clearAll() {
|
|
||||||
for (const auto& pair : cacheMap_) {
|
|
||||||
std::string cachePath = getCachePath(pair.first);
|
|
||||||
fs::remove(cachePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheMap_.clear();
|
|
||||||
saveCacheIndex();
|
|
||||||
|
|
||||||
E2D_LOG_INFO("All shader caches cleared");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 计算源码哈希值
|
|
||||||
* @param vertSource 顶点着色器源码
|
|
||||||
* @param fragSource 片段着色器源码
|
|
||||||
* @return 哈希值字符串
|
|
||||||
*/
|
|
||||||
std::string ShaderCache::computeHash(const std::string& vertSource,
|
|
||||||
const std::string& fragSource) {
|
|
||||||
std::string combined = vertSource + fragSource;
|
|
||||||
|
|
||||||
uint32_t hash = 5381;
|
|
||||||
for (char c : combined) {
|
|
||||||
hash = ((hash << 5) + hash) + static_cast<uint32_t>(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << std::hex << hash;
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 加载缓存索引
|
|
||||||
* @return 加载成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool ShaderCache::loadCacheIndex() {
|
|
||||||
std::string indexPath = cacheDir_ + "/.cache_index";
|
|
||||||
|
|
||||||
if (!fs::exists(indexPath)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ifstream file(indexPath);
|
|
||||||
if (!file.is_open()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string line;
|
|
||||||
while (std::getline(file, line)) {
|
|
||||||
if (line.empty() || line[0] == '#') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t pos = line.find('=');
|
|
||||||
if (pos == std::string::npos) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name = line.substr(0, pos);
|
|
||||||
std::string hash = line.substr(pos + 1);
|
|
||||||
|
|
||||||
std::string cachePath = getCachePath(name);
|
|
||||||
if (fs::exists(cachePath)) {
|
|
||||||
ShaderCacheEntry entry;
|
|
||||||
entry.name = name;
|
|
||||||
entry.sourceHash = hash;
|
|
||||||
entry.compileTime = static_cast<uint64_t>(
|
|
||||||
std::chrono::system_clock::now().time_since_epoch().count());
|
|
||||||
cacheMap_[name] = entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 保存缓存索引
|
|
||||||
* @return 保存成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool ShaderCache::saveCacheIndex() {
|
|
||||||
std::string indexPath = cacheDir_ + "/.cache_index";
|
|
||||||
|
|
||||||
std::ofstream file(indexPath);
|
|
||||||
if (!file.is_open()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
file << "# Extra2D Shader Cache Index\n";
|
|
||||||
file << "# Format: name=hash\n";
|
|
||||||
|
|
||||||
for (const auto& pair : cacheMap_) {
|
|
||||||
file << pair.first << "=" << pair.second.sourceHash << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取缓存文件路径
|
|
||||||
* @param name Shader名称
|
|
||||||
* @return 缓存文件完整路径
|
|
||||||
*/
|
|
||||||
std::string ShaderCache::getCachePath(const std::string& name) const {
|
|
||||||
return cacheDir_ + "/" + name + ".cache";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 确保缓存目录存在
|
|
||||||
* @return 目录存在或创建成功返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool ShaderCache::ensureCacheDirectory() {
|
|
||||||
if (cacheDir_.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::error_code ec;
|
|
||||||
if (!fs::exists(cacheDir_)) {
|
|
||||||
if (!fs::create_directories(cacheDir_, ec)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -1,164 +0,0 @@
|
||||||
#include <extra2d/graphics/shader_hot_reloader.h>
|
|
||||||
#include <extra2d/utils/logger.h>
|
|
||||||
#include <chrono>
|
|
||||||
#include <filesystem>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取单例实例
|
|
||||||
* @return 热重载管理器实例引用
|
|
||||||
*/
|
|
||||||
ShaderHotReloader& ShaderHotReloader::getInstance() {
|
|
||||||
static ShaderHotReloader instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 初始化热重载系统
|
|
||||||
* @return 初始化成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool ShaderHotReloader::init() {
|
|
||||||
if (initialized_) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
buffer_.resize(4096);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
initialized_ = true;
|
|
||||||
E2D_LOG_INFO("Shader hot reloader initialized");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 关闭热重载系统
|
|
||||||
*/
|
|
||||||
void ShaderHotReloader::shutdown() {
|
|
||||||
if (!initialized_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (watchHandle_ != nullptr) {
|
|
||||||
FindCloseChangeNotification(watchHandle_);
|
|
||||||
watchHandle_ = nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
watchMap_.clear();
|
|
||||||
initialized_ = false;
|
|
||||||
enabled_ = false;
|
|
||||||
E2D_LOG_INFO("Shader hot reloader shutdown");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 注册Shader文件监视
|
|
||||||
* @param shaderName Shader名称
|
|
||||||
* @param filePaths 要监视的文件列表
|
|
||||||
* @param callback 文件变化时的回调
|
|
||||||
*/
|
|
||||||
void ShaderHotReloader::watch(const std::string& shaderName,
|
|
||||||
const std::vector<std::string>& filePaths,
|
|
||||||
FileChangeCallback callback) {
|
|
||||||
if (!initialized_) {
|
|
||||||
E2D_LOG_WARN("Hot reloader not initialized");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
WatchInfo info;
|
|
||||||
info.filePaths = filePaths;
|
|
||||||
info.callback = callback;
|
|
||||||
|
|
||||||
for (const auto& path : filePaths) {
|
|
||||||
info.modifiedTimes[path] = getFileModifiedTime(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
watchMap_[shaderName] = std::move(info);
|
|
||||||
E2D_LOG_DEBUG("Watching shader: {} ({} files)", shaderName, filePaths.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 取消监视
|
|
||||||
* @param shaderName Shader名称
|
|
||||||
*/
|
|
||||||
void ShaderHotReloader::unwatch(const std::string& shaderName) {
|
|
||||||
auto it = watchMap_.find(shaderName);
|
|
||||||
if (it != watchMap_.end()) {
|
|
||||||
watchMap_.erase(it);
|
|
||||||
E2D_LOG_DEBUG("Stopped watching shader: {}", shaderName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 更新文件监视(在主循环中调用)
|
|
||||||
*/
|
|
||||||
void ShaderHotReloader::update() {
|
|
||||||
if (!initialized_ || !enabled_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pollChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 启用/禁用热重载
|
|
||||||
* @param enabled 是否启用
|
|
||||||
*/
|
|
||||||
void ShaderHotReloader::setEnabled(bool enabled) {
|
|
||||||
enabled_ = enabled;
|
|
||||||
E2D_LOG_DEBUG("Hot reload {}", enabled ? "enabled" : "disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 轮询检查文件变化
|
|
||||||
*/
|
|
||||||
void ShaderHotReloader::pollChanges() {
|
|
||||||
auto now = static_cast<uint64_t>(
|
|
||||||
std::chrono::system_clock::now().time_since_epoch().count());
|
|
||||||
|
|
||||||
for (auto& pair : watchMap_) {
|
|
||||||
WatchInfo& info = pair.second;
|
|
||||||
|
|
||||||
for (const auto& filePath : info.filePaths) {
|
|
||||||
uint64_t currentModTime = getFileModifiedTime(filePath);
|
|
||||||
uint64_t lastModTime = info.modifiedTimes[filePath];
|
|
||||||
|
|
||||||
if (currentModTime != 0 && lastModTime != 0 && currentModTime != lastModTime) {
|
|
||||||
info.modifiedTimes[filePath] = currentModTime;
|
|
||||||
|
|
||||||
FileChangeEvent event;
|
|
||||||
event.filepath = filePath;
|
|
||||||
event.type = FileChangeEvent::Type::Modified;
|
|
||||||
event.timestamp = now;
|
|
||||||
|
|
||||||
E2D_LOG_DEBUG("Shader file changed: {}", filePath);
|
|
||||||
|
|
||||||
if (info.callback) {
|
|
||||||
info.callback(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取文件修改时间
|
|
||||||
* @param filepath 文件路径
|
|
||||||
* @return 修改时间戳
|
|
||||||
*/
|
|
||||||
uint64_t ShaderHotReloader::getFileModifiedTime(const std::string& filepath) {
|
|
||||||
try {
|
|
||||||
auto ftime = fs::last_write_time(filepath);
|
|
||||||
auto sctp = std::chrono::time_point_cast<std::chrono::seconds>(
|
|
||||||
ftime - fs::file_time_type::clock::now() + std::chrono::system_clock::now());
|
|
||||||
return static_cast<uint64_t>(sctp.time_since_epoch().count());
|
|
||||||
} catch (...) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -1,8 +1,18 @@
|
||||||
#include <extra2d/graphics/shader_manager.h>
|
#include <extra2d/graphics/shader_manager.h>
|
||||||
#include <extra2d/utils/logger.h>
|
#include <extra2d/utils/logger.h>
|
||||||
|
#include <chrono>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// ShaderManager 核心
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
ShaderManager& ShaderManager::getInstance() {
|
ShaderManager& ShaderManager::getInstance() {
|
||||||
static ShaderManager instance;
|
static ShaderManager instance;
|
||||||
return instance;
|
return instance;
|
||||||
|
|
@ -46,18 +56,16 @@ bool ShaderManager::init(const std::string& shaderDir,
|
||||||
|
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
hotReloadSupported_ = false;
|
hotReloadSupported_ = false;
|
||||||
if (!ShaderCache::getInstance().init(cacheDir_)) {
|
|
||||||
E2D_LOG_WARN("Failed to initialize shader cache on Switch");
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
hotReloadSupported_ = true;
|
hotReloadSupported_ = true;
|
||||||
if (!ShaderCache::getInstance().init(cacheDir_)) {
|
|
||||||
E2D_LOG_WARN("Failed to initialize shader cache, caching disabled");
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!initCache(cacheDir_)) {
|
||||||
|
E2D_LOG_WARN("Failed to initialize shader cache, caching disabled");
|
||||||
|
}
|
||||||
|
|
||||||
if (hotReloadSupported_) {
|
if (hotReloadSupported_) {
|
||||||
if (!ShaderHotReloader::getInstance().init()) {
|
if (!initReloader()) {
|
||||||
E2D_LOG_WARN("Failed to initialize hot reloader");
|
E2D_LOG_WARN("Failed to initialize hot reloader");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -76,9 +84,9 @@ void ShaderManager::shutdown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hotReloadSupported_) {
|
if (hotReloadSupported_) {
|
||||||
ShaderHotReloader::getInstance().shutdown();
|
shutdownReloader();
|
||||||
}
|
}
|
||||||
ShaderCache::getInstance().shutdown();
|
shutdownCache();
|
||||||
|
|
||||||
shaders_.clear();
|
shaders_.clear();
|
||||||
factory_.reset();
|
factory_.reset();
|
||||||
|
|
@ -87,6 +95,10 @@ void ShaderManager::shutdown() {
|
||||||
E2D_LOG_INFO("ShaderManager shutdown");
|
E2D_LOG_INFO("ShaderManager shutdown");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Shader 加载
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
Ptr<IShader> ShaderManager::loadFromFiles(const std::string& name,
|
Ptr<IShader> ShaderManager::loadFromFiles(const std::string& name,
|
||||||
const std::string& vertPath,
|
const std::string& vertPath,
|
||||||
const std::string& fragPath) {
|
const std::string& fragPath) {
|
||||||
|
|
@ -177,6 +189,10 @@ void ShaderManager::clear() {
|
||||||
E2D_LOG_INFO("All shaders cleared");
|
E2D_LOG_INFO("All shaders cleared");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 热重载
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
void ShaderManager::setReloadCallback(const std::string& name, ShaderReloadCallback callback) {
|
void ShaderManager::setReloadCallback(const std::string& name, ShaderReloadCallback callback) {
|
||||||
auto it = shaders_.find(name);
|
auto it = shaders_.find(name);
|
||||||
if (it != shaders_.end()) {
|
if (it != shaders_.end()) {
|
||||||
|
|
@ -200,7 +216,9 @@ void ShaderManager::update() {
|
||||||
if (!hotReloadEnabled_ || !hotReloadSupported_) {
|
if (!hotReloadEnabled_ || !hotReloadSupported_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ShaderHotReloader::getInstance().update();
|
if (reloaderInitialized_) {
|
||||||
|
pollChanges();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderManager::reload(const std::string& name) {
|
bool ShaderManager::reload(const std::string& name) {
|
||||||
|
|
@ -226,6 +244,10 @@ bool ShaderManager::reload(const std::string& name) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 内置Shader
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
Ptr<IShader> ShaderManager::getBuiltin(const std::string& name) {
|
Ptr<IShader> ShaderManager::getBuiltin(const std::string& name) {
|
||||||
return get("builtin_" + name);
|
return get("builtin_" + name);
|
||||||
}
|
}
|
||||||
|
|
@ -274,4 +296,321 @@ void ShaderManager::handleFileChange(const std::string& shaderName, const FileCh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 缓存(原 ShaderCache)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
bool ShaderManager::initCache(const std::string& cacheDir) {
|
||||||
|
cacheDir_ = cacheDir;
|
||||||
|
|
||||||
|
if (!ensureCacheDirectory()) {
|
||||||
|
E2D_LOG_ERROR("Failed to create cache directory: {}", cacheDir);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loadCacheIndex()) {
|
||||||
|
E2D_LOG_WARN("Failed to load cache index, starting fresh");
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheInitialized_ = true;
|
||||||
|
E2D_LOG_INFO("Shader cache initialized at: {}", cacheDir);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShaderManager::shutdownCache() {
|
||||||
|
if (!cacheInitialized_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveCacheIndex();
|
||||||
|
cacheMap_.clear();
|
||||||
|
cacheInitialized_ = false;
|
||||||
|
E2D_LOG_INFO("Shader cache shutdown");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShaderManager::hasValidCache(const std::string& name, const std::string& sourceHash) {
|
||||||
|
auto it = cacheMap_.find(name);
|
||||||
|
if (it == cacheMap_.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return it->second.sourceHash == sourceHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptr<ShaderCacheEntry> ShaderManager::loadCache(const std::string& name) {
|
||||||
|
auto it = cacheMap_.find(name);
|
||||||
|
if (it == cacheMap_.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cachePath = getCachePath(name);
|
||||||
|
std::ifstream file(cachePath, std::ios::binary);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
E2D_LOG_WARN("Failed to open cache file: {}", cachePath);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto entry = std::make_shared<ShaderCacheEntry>(it->second);
|
||||||
|
entry->binary.clear();
|
||||||
|
|
||||||
|
file.seekg(0, std::ios::end);
|
||||||
|
size_t fileSize = static_cast<size_t>(file.tellg());
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
entry->binary.resize(fileSize);
|
||||||
|
file.read(reinterpret_cast<char*>(entry->binary.data()), fileSize);
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShaderManager::saveCache(const ShaderCacheEntry& entry) {
|
||||||
|
if (!cacheInitialized_) {
|
||||||
|
E2D_LOG_WARN("Shader cache not initialized, cannot save cache");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.binary.empty()) {
|
||||||
|
E2D_LOG_WARN("Shader binary is empty, skipping cache save for: {}", entry.name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cachePath = getCachePath(entry.name);
|
||||||
|
E2D_LOG_DEBUG("Saving shader cache to: {} ({} bytes)", cachePath, entry.binary.size());
|
||||||
|
|
||||||
|
std::ofstream file(cachePath, std::ios::binary);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
E2D_LOG_ERROR("Failed to create cache file: {}", cachePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.write(reinterpret_cast<const char*>(entry.binary.data()), entry.binary.size());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
cacheMap_[entry.name] = entry;
|
||||||
|
saveCacheIndex();
|
||||||
|
|
||||||
|
E2D_LOG_INFO("Shader cache saved: {} ({} bytes)", entry.name, entry.binary.size());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderManager::invalidateCache(const std::string& name) {
|
||||||
|
auto it = cacheMap_.find(name);
|
||||||
|
if (it == cacheMap_.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cachePath = getCachePath(name);
|
||||||
|
fs::remove(cachePath);
|
||||||
|
|
||||||
|
cacheMap_.erase(it);
|
||||||
|
saveCacheIndex();
|
||||||
|
|
||||||
|
E2D_LOG_DEBUG("Shader cache invalidated: {}", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderManager::clearAllCache() {
|
||||||
|
for (const auto& pair : cacheMap_) {
|
||||||
|
std::string cachePath = getCachePath(pair.first);
|
||||||
|
fs::remove(cachePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheMap_.clear();
|
||||||
|
saveCacheIndex();
|
||||||
|
|
||||||
|
E2D_LOG_INFO("All shader caches cleared");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ShaderManager::computeHash(const std::string& vertSource,
|
||||||
|
const std::string& fragSource) {
|
||||||
|
std::string combined = vertSource + fragSource;
|
||||||
|
|
||||||
|
uint32_t hash = 5381;
|
||||||
|
for (char c : combined) {
|
||||||
|
hash = ((hash << 5) + hash) + static_cast<uint32_t>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::hex << hash;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShaderManager::loadCacheIndex() {
|
||||||
|
std::string indexPath = cacheDir_ + "/.cache_index";
|
||||||
|
|
||||||
|
if (!fs::exists(indexPath)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ifstream file(indexPath);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
if (line.empty() || line[0] == '#') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pos = line.find('=');
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name = line.substr(0, pos);
|
||||||
|
std::string hash = line.substr(pos + 1);
|
||||||
|
|
||||||
|
std::string cachePath = getCachePath(name);
|
||||||
|
if (fs::exists(cachePath)) {
|
||||||
|
ShaderCacheEntry entry;
|
||||||
|
entry.name = name;
|
||||||
|
entry.sourceHash = hash;
|
||||||
|
entry.compileTime = static_cast<uint64_t>(
|
||||||
|
std::chrono::system_clock::now().time_since_epoch().count());
|
||||||
|
cacheMap_[name] = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShaderManager::saveCacheIndex() {
|
||||||
|
std::string indexPath = cacheDir_ + "/.cache_index";
|
||||||
|
|
||||||
|
std::ofstream file(indexPath);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file << "# Extra2D Shader Cache Index\n";
|
||||||
|
file << "# Format: name=hash\n";
|
||||||
|
|
||||||
|
for (const auto& pair : cacheMap_) {
|
||||||
|
file << pair.first << "=" << pair.second.sourceHash << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ShaderManager::getCachePath(const std::string& name) const {
|
||||||
|
return cacheDir_ + "/" + name + ".cache";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShaderManager::ensureCacheDirectory() {
|
||||||
|
if (cacheDir_.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::error_code ec;
|
||||||
|
if (!fs::exists(cacheDir_)) {
|
||||||
|
if (!fs::create_directories(cacheDir_, ec)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 热重载(原 ShaderHotReloader)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
bool ShaderManager::initReloader() {
|
||||||
|
if (reloaderInitialized_) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
watchBuffer_.resize(4096);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
reloaderInitialized_ = true;
|
||||||
|
E2D_LOG_INFO("Shader hot reloader initialized");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderManager::shutdownReloader() {
|
||||||
|
if (!reloaderInitialized_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (watchHandle_ != nullptr) {
|
||||||
|
FindCloseChangeNotification(watchHandle_);
|
||||||
|
watchHandle_ = nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
watchMap_.clear();
|
||||||
|
reloaderInitialized_ = false;
|
||||||
|
E2D_LOG_INFO("Shader hot reloader shutdown");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderManager::watch(const std::string& shaderName,
|
||||||
|
const std::vector<std::string>& filePaths,
|
||||||
|
FileChangeCallback callback) {
|
||||||
|
if (!reloaderInitialized_) {
|
||||||
|
E2D_LOG_WARN("Hot reloader not initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WatchInfo info;
|
||||||
|
info.filePaths = filePaths;
|
||||||
|
info.callback = callback;
|
||||||
|
|
||||||
|
for (const auto& path : filePaths) {
|
||||||
|
info.modifiedTimes[path] = getFileModifiedTime(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
watchMap_[shaderName] = std::move(info);
|
||||||
|
E2D_LOG_DEBUG("Watching shader: {} ({} files)", shaderName, filePaths.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderManager::unwatch(const std::string& shaderName) {
|
||||||
|
auto it = watchMap_.find(shaderName);
|
||||||
|
if (it != watchMap_.end()) {
|
||||||
|
watchMap_.erase(it);
|
||||||
|
E2D_LOG_DEBUG("Stopped watching shader: {}", shaderName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderManager::pollChanges() {
|
||||||
|
auto now = static_cast<uint64_t>(
|
||||||
|
std::chrono::system_clock::now().time_since_epoch().count());
|
||||||
|
|
||||||
|
for (auto& pair : watchMap_) {
|
||||||
|
WatchInfo& info = pair.second;
|
||||||
|
|
||||||
|
for (const auto& filePath : info.filePaths) {
|
||||||
|
uint64_t currentModTime = getFileModifiedTime(filePath);
|
||||||
|
uint64_t lastModTime = info.modifiedTimes[filePath];
|
||||||
|
|
||||||
|
if (currentModTime != 0 && lastModTime != 0 && currentModTime != lastModTime) {
|
||||||
|
info.modifiedTimes[filePath] = currentModTime;
|
||||||
|
|
||||||
|
FileChangeEvent event;
|
||||||
|
event.filepath = filePath;
|
||||||
|
event.type = FileChangeEvent::Type::Modified;
|
||||||
|
event.timestamp = now;
|
||||||
|
|
||||||
|
E2D_LOG_DEBUG("Shader file changed: {}", filePath);
|
||||||
|
|
||||||
|
if (info.callback) {
|
||||||
|
info.callback(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t ShaderManager::getFileModifiedTime(const std::string& filepath) {
|
||||||
|
try {
|
||||||
|
auto ftime = fs::last_write_time(filepath);
|
||||||
|
auto sctp = std::chrono::time_point_cast<std::chrono::seconds>(
|
||||||
|
ftime - fs::file_time_type::clock::now() + std::chrono::system_clock::now());
|
||||||
|
return static_cast<uint64_t>(sctp.time_since_epoch().count());
|
||||||
|
} catch (...) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
||||||
#include <extra2d/graphics/shader_manager.h>
|
|
||||||
#include <extra2d/graphics/shader_preset.h>
|
|
||||||
#include <extra2d/utils/logger.h>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建水波纹效果着色器
|
|
||||||
* @param params 水波纹效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
Ptr<IShader> ShaderPreset::Water(const WaterParams ¶ms) {
|
|
||||||
Ptr<IShader> shader = ShaderManager::getInstance().get("water");
|
|
||||||
if (!shader) {
|
|
||||||
E2D_LOG_ERROR("Failed to get water shader");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
shader->setFloat("u_waveSpeed", params.waveSpeed);
|
|
||||||
shader->setFloat("u_waveAmplitude", params.waveAmplitude);
|
|
||||||
shader->setFloat("u_waveFrequency", params.waveFrequency);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建描边效果着色器
|
|
||||||
* @param params 描边效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
Ptr<IShader> ShaderPreset::Outline(const OutlineParams ¶ms) {
|
|
||||||
Ptr<IShader> shader = ShaderManager::getInstance().get("outline");
|
|
||||||
if (!shader) {
|
|
||||||
E2D_LOG_ERROR("Failed to get outline shader");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
shader->setVec4("u_outlineColor", glm::vec4(params.color.r, params.color.g,
|
|
||||||
params.color.b, params.color.a));
|
|
||||||
shader->setFloat("u_thickness", params.thickness);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建扭曲效果着色器
|
|
||||||
* @param params 扭曲效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
Ptr<IShader> ShaderPreset::Distortion(const DistortionParams ¶ms) {
|
|
||||||
Ptr<IShader> shader = ShaderManager::getInstance().get("distortion");
|
|
||||||
if (!shader) {
|
|
||||||
E2D_LOG_ERROR("Failed to get distortion shader");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
shader->setFloat("u_distortionAmount", params.distortionAmount);
|
|
||||||
shader->setFloat("u_timeScale", params.timeScale);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建像素化效果着色器
|
|
||||||
* @param params 像素化效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
Ptr<IShader> ShaderPreset::Pixelate(const PixelateParams ¶ms) {
|
|
||||||
Ptr<IShader> shader = ShaderManager::getInstance().get("pixelate");
|
|
||||||
if (!shader) {
|
|
||||||
E2D_LOG_ERROR("Failed to get pixelate shader");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
shader->setFloat("u_pixelSize", params.pixelSize);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建反相效果着色器
|
|
||||||
* @param params 反相效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
Ptr<IShader> ShaderPreset::Invert(const InvertParams ¶ms) {
|
|
||||||
Ptr<IShader> shader = ShaderManager::getInstance().get("invert");
|
|
||||||
if (!shader) {
|
|
||||||
E2D_LOG_ERROR("Failed to get invert shader");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
shader->setFloat("u_strength", params.strength);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建灰度效果着色器
|
|
||||||
* @param params 灰度效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
Ptr<IShader> ShaderPreset::Grayscale(const GrayscaleParams ¶ms) {
|
|
||||||
Ptr<IShader> shader = ShaderManager::getInstance().get("grayscale");
|
|
||||||
if (!shader) {
|
|
||||||
E2D_LOG_ERROR("Failed to get grayscale shader");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
shader->setFloat("u_intensity", params.intensity);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建模糊效果着色器
|
|
||||||
* @param params 模糊效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
Ptr<IShader> ShaderPreset::Blur(const BlurParams ¶ms) {
|
|
||||||
Ptr<IShader> shader = ShaderManager::getInstance().get("blur");
|
|
||||||
if (!shader) {
|
|
||||||
E2D_LOG_ERROR("Failed to get blur shader");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
shader->setFloat("u_radius", params.radius);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建灰度+描边组合效果着色器
|
|
||||||
* @param grayParams 灰度效果参数
|
|
||||||
* @param outlineParams 描边效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
Ptr<IShader>
|
|
||||||
ShaderPreset::GrayscaleOutline(const GrayscaleParams &grayParams,
|
|
||||||
const OutlineParams &outlineParams) {
|
|
||||||
std::string shaderDir = ShaderManager::getInstance().getShaderDir();
|
|
||||||
std::string shaderPath = shaderDir + "effects/grayscale_outline.shader";
|
|
||||||
|
|
||||||
Ptr<IShader> shader =
|
|
||||||
ShaderManager::getInstance().loadFromCombinedFile(shaderPath);
|
|
||||||
if (!shader) {
|
|
||||||
E2D_LOG_ERROR("Failed to load grayscale_outline shader from: {}",
|
|
||||||
shaderPath);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
shader->setFloat("u_grayIntensity", grayParams.intensity);
|
|
||||||
shader->setVec4("u_outlineColor",
|
|
||||||
glm::vec4(outlineParams.color.r, outlineParams.color.g,
|
|
||||||
outlineParams.color.b, outlineParams.color.a));
|
|
||||||
shader->setFloat("u_thickness", outlineParams.thickness);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建像素化+反相组合效果着色器
|
|
||||||
* @param pixParams 像素化效果参数
|
|
||||||
* @param invParams 反相效果参数
|
|
||||||
* @return 配置好的着色器
|
|
||||||
*/
|
|
||||||
Ptr<IShader> ShaderPreset::PixelateInvert(const PixelateParams &pixParams,
|
|
||||||
const InvertParams &invParams) {
|
|
||||||
std::string shaderDir = ShaderManager::getInstance().getShaderDir();
|
|
||||||
std::string shaderPath = shaderDir + "effects/pixelate_invert.shader";
|
|
||||||
|
|
||||||
Ptr<IShader> shader =
|
|
||||||
ShaderManager::getInstance().loadFromCombinedFile(shaderPath);
|
|
||||||
if (!shader) {
|
|
||||||
E2D_LOG_ERROR("Failed to load pixelate_invert shader from: {}", shaderPath);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
shader->setFloat("u_pixelSize", pixParams.pixelSize);
|
|
||||||
shader->setFloat("u_invertStrength", invParams.strength);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -112,15 +112,10 @@ TextureRef TexturePool::load(const std::string& path, const Rect& region,
|
||||||
cacheMisses_.fetch_add(1, std::memory_order_relaxed);
|
cacheMisses_.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
|
||||||
// 获取渲染后端
|
// 获取渲染后端
|
||||||
RenderBackend* backend = nullptr;
|
RenderBackend* backend = backend_;
|
||||||
if (scene_) {
|
|
||||||
// 假设 Scene 有获取 RenderBackend 的方法
|
|
||||||
// 这里需要根据实际接口调整
|
|
||||||
backend = nullptr; // TODO: 从 Scene 获取 RenderBackend
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!backend) {
|
if (!backend) {
|
||||||
E2D_LOG_ERROR("TexturePool: RenderBackend not available");
|
E2D_LOG_ERROR("TexturePool: RenderBackend not available, call setRenderBackend() first");
|
||||||
return TextureRef();
|
return TextureRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
#include <extra2d/resource/resource_config.h>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
void ResourceConfigData::addSearchPath(const std::string& path) {
|
|
||||||
if (!hasSearchPath(path)) {
|
|
||||||
searchPaths.push_back(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResourceConfigData::removeSearchPath(const std::string& path) {
|
|
||||||
auto it = std::find(searchPaths.begin(), searchPaths.end(), path);
|
|
||||||
if (it != searchPaths.end()) {
|
|
||||||
searchPaths.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ResourceConfigData::hasSearchPath(const std::string& path) const {
|
|
||||||
return std::find(searchPaths.begin(), searchPaths.end(), path) != searchPaths.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue