From b4be0d84f8d890ccf6001ceb55842437aaf491bd Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Mon, 2 Mar 2026 00:25:14 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=B8=B2=E6=9F=93=E5=99=A8):=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E6=A0=B8=E5=BF=83=E6=B8=B2=E6=9F=93=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加渲染器模块及相关组件,包括材质、网格、纹理、着色器和统一缓冲区管理。主要变更包括: - 新增渲染器模块,负责接收渲染命令、批处理和排序 - 实现材质系统支持参数和着色器管理 - 添加网格类管理顶点和索引数据 - 实现纹理加载和绑定功能 - 添加着色器编译和链接功能 - 实现统一缓冲区对象(UBO)管理系统 - 提供默认资源(材质、网格、纹理) - 支持实例化渲染和命令批处理 - 添加渲染事件系统(OnRenderBegin/Submit/End) - 完善资源句柄管理机制 - 优化GL资源初始化和清理流程 移除不再使用的IModule接口,调整窗口模块事件触发时机确保GL上下文安全 --- examples/hello_world/xmake.lua | 15 +- include/context/context.h | 7 +- include/event/events.h | 27 ++ include/extra2d.h | 10 +- include/module/imodule.h | 69 ----- include/renderer/material.h | 178 +++++++++++ include/renderer/mesh.h | 119 +++++++ include/renderer/render_types.h | 161 ++++++++++ include/renderer/renderer_module.h | 404 ++++++++++++++++++++++++ include/renderer/shader.h | 155 ++++++++++ include/renderer/texture.h | 106 +++++++ include/renderer/uniform_buffer.h | 157 ++++++++++ include/types/math/transform.h | 16 + shader/default.frag | 32 ++ shader/default.vert | 34 ++ src/context/context.cpp | 27 +- src/platform/window_module.cpp | 8 +- src/renderer/material.cpp | 158 ++++++++++ src/renderer/mesh.cpp | 147 +++++++++ src/renderer/renderer_module.cpp | 481 +++++++++++++++++++++++++++++ src/renderer/shader.cpp | 217 +++++++++++++ src/renderer/texture.cpp | 155 ++++++++++ src/renderer/uniform_buffer.cpp | 148 +++++++++ xmake.lua | 1 - 24 files changed, 2740 insertions(+), 92 deletions(-) delete mode 100644 include/module/imodule.h create mode 100644 include/renderer/material.h create mode 100644 include/renderer/mesh.h create mode 100644 include/renderer/render_types.h create mode 100644 include/renderer/renderer_module.h create mode 100644 include/renderer/shader.h create mode 100644 include/renderer/texture.h create mode 100644 include/renderer/uniform_buffer.h create mode 100644 shader/default.frag create mode 100644 shader/default.vert create mode 100644 src/renderer/material.cpp create mode 100644 src/renderer/mesh.cpp create mode 100644 src/renderer/renderer_module.cpp create mode 100644 src/renderer/shader.cpp create mode 100644 src/renderer/texture.cpp create mode 100644 src/renderer/uniform_buffer.cpp diff --git a/examples/hello_world/xmake.lua b/examples/hello_world/xmake.lua index b2b074a..eeb9e89 100644 --- a/examples/hello_world/xmake.lua +++ b/examples/hello_world/xmake.lua @@ -56,9 +56,11 @@ target("hello_world") -- 复制资源到输出目录 after_build(function (target) + local target_dir = path.directory(target:targetfile()) + + -- 复制 romfs 资源 local romfs = path.join(example_dir, "romfs") if os.isdir(romfs) then - local target_dir = path.directory(target:targetfile()) local assets_dir = path.join(target_dir, "assets") -- 创建 assets 目录 @@ -72,6 +74,17 @@ target("hello_world") else print("Warning: romfs directory not found at " .. romfs) end + + -- 复制着色器文件 + local shader_source = path.join(example_dir, "../../shader") + local shader_target = path.join(target_dir, "shader") + if os.isdir(shader_source) then + if not os.isdir(shader_target) then + os.mkdir(shader_target) + end + os.cp(path.join(shader_source, "*"), shader_target) + print("Copied shaders from " .. shader_source .. " to " .. shader_target) + end end) end target_end() diff --git a/include/context/context.h b/include/context/context.h index 5547976..fef0b0a 100644 --- a/include/context/context.h +++ b/include/context/context.h @@ -9,6 +9,7 @@ namespace extra2d { class ModuleRegistry; class PluginLoader; class TimerModule; +class RendererModule; /** * @brief 引擎上下文 @@ -66,6 +67,11 @@ public: */ TimerModule& timer(); + /** + * @brief 获取渲染器模块 + */ + RendererModule& renderer(); + private: /** * @brief 获取总运行时间 @@ -89,7 +95,6 @@ private: private: std::unique_ptr pluginLoader_; - std::unique_ptr timerModule_; float totalTime_ = 0.0f; uint64 frameCount_ = 0; diff --git a/include/event/events.h b/include/event/events.h index 1343c42..a0beecf 100644 --- a/include/event/events.h +++ b/include/event/events.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace extra2d::events { @@ -223,4 +224,30 @@ DECLARE_EVENT_0(OnEnterBackground, Engine) */ DECLARE_EVENT_0(OnEnterForeground, Engine) +// ============================================================================ +// 渲染事件 +// ============================================================================ + +/** + * @brief 渲染开始事件 + * + * 在渲染帧开始时触发,渲染器应清空命令缓冲区 + */ +DECLARE_EVENT_0(OnRenderBegin, Engine) + +/** + * @brief 渲染提交事件 + * + * 场景图模块通过此事件提交渲染命令 + * @param cmd 渲染命令 + */ +DECLARE_EVENT_1(OnRenderSubmit, Engine, extra2d::RenderCommand) + +/** + * @brief 渲染结束事件 + * + * 在渲染帧结束时触发,渲染器执行所有收集的命令 + */ +DECLARE_EVENT_0(OnRenderEnd, Engine) + } // namespace extra2d::events diff --git a/include/extra2d.h b/include/extra2d.h index 356dbd2..559d463 100644 --- a/include/extra2d.h +++ b/include/extra2d.h @@ -21,7 +21,6 @@ #include // Module System (新架构 - 基于事件总线) -#include #include // Plugin System (新架构 - 基于事件总线) @@ -41,6 +40,15 @@ #include #include +// Renderer System +#include +#include +#include +#include +#include +#include +#include + // Application #include diff --git a/include/module/imodule.h b/include/module/imodule.h deleted file mode 100644 index 08214f6..0000000 --- a/include/module/imodule.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include - -namespace extra2d { - -/** - * @brief 模块类型枚举 - */ -enum class ModuleType : uint8 { - Core, // 核心模块(必须) - System, // 系统模块(平台相关) - Feature, // 功能模块 - Extension // 扩展模块 -}; - -/** - * @brief 模块接口 - 基于事件总线 - * - * 模块不再通过虚函数被调用,而是通过监听事件响应 - * 模块可以发送事件,但不应直接调用其他模块 - * - * 设计理念: - * - 模块是引擎的内置组件,在编译时确定 - * - 模块之间通过事件总线通信,零直接依赖 - * - 模块的生命周期由 ModuleRegistry 管理 - */ -class IModule { -public: - virtual ~IModule() = default; - - /** - * @brief 获取模块名称 - * @return 模块唯一标识名 - */ - virtual const char* name() const = 0; - - /** - * @brief 获取模块类型 - * @return 模块类型,用于分类和优先级管理 - */ - virtual ModuleType type() const { return ModuleType::Feature; } - - /** - * @brief 获取模块优先级 - * @return 优先级数值,越小优先级越高 - */ - virtual int priority() const { return 0; } - - /** - * @brief 初始化模块 - * - * 在此注册事件监听器 - * 返回 false 表示初始化失败,引擎将停止 - * - * @return 初始化是否成功 - */ - virtual bool init() { return true; } - - /** - * @brief 关闭模块 - * - * 在此执行清理工作 - * 事件监听器会自动注销(RAII) - */ - virtual void shutdown() {} -}; - -} // namespace extra2d diff --git a/include/renderer/material.h b/include/renderer/material.h new file mode 100644 index 0000000..53ad2f4 --- /dev/null +++ b/include/renderer/material.h @@ -0,0 +1,178 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// 前向声明 +class Material; + +/** + * @brief 材质参数信息 + */ +struct MaterialParamInfo { + MaterialParamType type; + uint32_t offset; + uint32_t size; +}; + +/** + * @brief 材质布局类 + * + * 定义材质参数的布局,可被多个材质共享 + */ +class MaterialLayout : public RefCounted { +public: + /** + * @brief 默认构造函数 + */ + MaterialLayout(); + + /** + * @brief 添加参数 + * @param name 参数名称 + * @param type 参数类型 + */ + void addParam(const std::string& name, MaterialParamType type); + + /** + * @brief 完成布局定义,计算偏移和总大小 + */ + void finalize(); + + /** + * @brief 获取参数信息 + * @param name 参数名称 + * @return 参数信息指针,不存在返回 nullptr + */ + const MaterialParamInfo* getParam(const std::string& name) const; + + /** + * @brief 获取缓冲区大小 + * @return 缓冲区大小 + */ + uint32_t getBufferSize() const { return bufferSize_; } + + /** + * @brief 检查是否已最终化 + * @return 是否已最终化 + */ + bool isFinalized() const { return finalized_; } + +private: + std::unordered_map params_; + uint32_t bufferSize_ = 0; + bool finalized_ = false; +}; + +/** + * @brief 材质类 + * + * 管理着色器和材质参数 + * 支持通过 UBO 高效上传材质数据到 GPU + */ +class Material : public RefCounted { +public: + /** + * @brief 默认构造函数 + */ + Material(); + + /** + * @brief 设置材质布局 + * @param layout 材质布局 + */ + void setLayout(Ptr layout); + + /** + * @brief 设置着色器 + * @param shader 着色器 + */ + void setShader(Ptr shader); + + /** + * @brief 设置 float 参数 + * @param name 参数名称 + * @param value 值 + */ + void setFloat(const std::string& name, float value); + + /** + * @brief 设置 vec2 参数 + * @param name 参数名称 + * @param value 值 + */ + void setVec2(const std::string& name, const Vec2& value); + + /** + * @brief 设置 vec4 参数 + * @param name 参数名称 + * @param value 值 + */ + void setVec4(const std::string& name, float x, float y, float z, float w); + + /** + * @brief 设置颜色参数 + * @param name 参数名称 + * @param value 颜色值 + */ + void setColor(const std::string& name, const Color& value); + + /** + * @brief 设置 mat4 参数 + * @param name 参数名称 + * @param value 矩阵数据指针 + */ + void setMat4(const std::string& name, const float* value); + + /** + * @brief 设置纹理 + * @param name 参数名称 + * @param texture 纹理句柄 + * @param slot 纹理槽位 + */ + void setTexture(const std::string& name, TextureHandle texture, uint32_t slot); + + /** + * @brief 应用材质 + * + * 绑定着色器、上传 UBO 数据、绑定纹理 + * @param uboManager UBO 管理器 + */ + void apply(UniformBufferManager& uboManager); + + /** + * @brief 获取着色器 + * @return 着色器 + */ + Ptr getShader() const { return shader_; } + + /** + * @brief 获取材质数据指针 + * @return 数据指针 + */ + const void* getData() const { return data_.data(); } + + /** + * @brief 获取材质数据大小 + * @return 数据大小 + */ + uint32_t getDataSize() const { return static_cast(data_.size()); } + +private: + Ptr layout_; // 材质布局 + Ptr shader_; // 着色器 + std::vector data_; // 材质数据 + std::vector> textures_; // 纹理列表 +}; + +} // namespace extra2d diff --git a/include/renderer/mesh.h b/include/renderer/mesh.h new file mode 100644 index 0000000..4ec93f1 --- /dev/null +++ b/include/renderer/mesh.h @@ -0,0 +1,119 @@ +#pragma once + +#include +#include +#include +#include +#include + +// 前向声明 OpenGL 类型 +typedef unsigned int GLuint; + +namespace extra2d { + +/** + * @brief 顶点结构 + */ +struct Vertex { + Vec2 position; // 位置 + Vec2 texCoord; // 纹理坐标 + Color color; // 颜色 + + Vertex() = default; + Vertex(const Vec2 &pos, const Vec2 &uv, const Color &col) + : position(pos), texCoord(uv), color(col) {} +}; + +/** + * @brief 网格类 + * + * 管理 OpenGL 顶点数组对象和缓冲区 + * 支持静态和动态顶点数据 + */ +class Mesh : public RefCounted { +public: + /** + * @brief 默认构造函数 + */ + Mesh(); + + /** + * @brief 析构函数 + */ + ~Mesh() override; + + /** + * @brief 设置顶点数据 + * @param vertices 顶点数组 + * @param count 顶点数量 + */ + void setVertices(const Vertex *vertices, uint32_t count); + + /** + * @brief 设置索引数据 + * @param indices 索引数组 + * @param count 索引数量 + */ + void setIndices(const uint16_t *indices, uint32_t count); + + /** + * @brief 更新部分顶点数据 + * @param vertices 顶点数据 + * @param count 顶点数量 + * @param offset 偏移量 + */ + void updateVertices(const Vertex *vertices, uint32_t count, uint32_t offset); + + /** + * @brief 绑定网格 + */ + void bind() const; + + /** + * @brief 解绑网格 + */ + void unbind() const; + + /** + * @brief 绘制网格 + */ + void draw() const; + + /** + * @brief 实例化绘制 + * @param instanceCount 实例数量 + */ + void drawInstanced(uint32_t instanceCount) const; + + /** + * @brief 获取顶点数量 + * @return 顶点数量 + */ + uint32_t getVertexCount() const { return vertexCount_; } + + /** + * @brief 获取索引数量 + * @return 索引数量 + */ + uint32_t getIndexCount() const { return indexCount_; } + + /** + * @brief 创建标准四边形 + * @param size 四边形大小 + * @param uv 纹理坐标范围 + * @return 网格对象 + */ + static Ptr createQuad(const Vec2 &size, + const Rect &uv = Rect(0, 0, 1, 1)); + +private: + GLuint vao_ = 0; // 顶点数组对象 + GLuint vbo_ = 0; // 顶点缓冲区 + GLuint ibo_ = 0; // 索引缓冲区 + uint32_t vertexCount_ = 0; // 顶点数量 + uint32_t indexCount_ = 0; // 索引数量 + uint32_t vertexCapacity_ = 0; // 顶点缓冲区容量 + uint32_t indexCapacity_ = 0; // 索引缓冲区容量 +}; + +} // namespace extra2d diff --git a/include/renderer/render_types.h b/include/renderer/render_types.h new file mode 100644 index 0000000..bd816ef --- /dev/null +++ b/include/renderer/render_types.h @@ -0,0 +1,161 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// 前向声明 +class Material; +class Mesh; +class Texture; + +// 资源句柄类型(64位,替代智能指针) +using MaterialHandle = uint64_t; +using MeshHandle = uint64_t; +using TextureHandle = uint64_t; + +// 无效句柄值 +constexpr MaterialHandle INVALID_MATERIAL_HANDLE = 0; +constexpr MeshHandle INVALID_MESH_HANDLE = 0; +constexpr TextureHandle INVALID_TEXTURE_HANDLE = 0; + +/** + * @brief 渲染命令类型 + */ +enum class RenderCommandType : uint8_t { + DrawMesh, // 绘制网格 + DrawMeshInstanced, // 实例化绘制 + SetViewport, // 设置视口 + Clear // 清除缓冲区 +}; + +/** + * @brief 纹理格式 + */ +enum class TextureFormat : uint8_t { + RGBA8, // 32位 RGBA + RGB8, // 24位 RGB + RGBA4, // 16位 RGBA + R8, // 8位 灰度 + RG8 // 16位 RG +}; + +/** + * @brief 获取纹理格式的像素大小(字节) + * @param format 纹理格式 + * @return 像素大小 + */ +inline uint32_t getTexturePixelSize(TextureFormat format) { + switch (format) { + case TextureFormat::RGBA8: return 4; + case TextureFormat::RGB8: return 3; + case TextureFormat::RGBA4: return 2; + case TextureFormat::R8: return 1; + case TextureFormat::RG8: return 2; + default: return 4; + } +} + +/** + * @brief 材质参数类型 + */ +enum class MaterialParamType : uint8_t { + Float, // 浮点数 + Vec2, // 二维向量 + Vec3, // 三维向量 + Vec4, // 四维向量 + Color, // 颜色 + Mat4, // 4x4 矩阵 + Texture // 纹理 +}; + +/** + * @brief 获取材质参数类型的大小 + * @param type 参数类型 + * @return 参数大小(字节) + */ +inline uint32_t getMaterialParamSize(MaterialParamType type) { + switch (type) { + case MaterialParamType::Float: return sizeof(float); + case MaterialParamType::Vec2: return sizeof(float) * 2; + case MaterialParamType::Vec3: return sizeof(float) * 3; + case MaterialParamType::Vec4: return sizeof(float) * 4; + case MaterialParamType::Color: return sizeof(float) * 4; + case MaterialParamType::Mat4: return sizeof(float) * 16; + case MaterialParamType::Texture: return sizeof(TextureHandle); + default: return 0; + } +} + +/** + * @brief 渲染命令结构(32字节对齐) + * + * 使用值类型设计,避免智能指针开销 + * 通过资源句柄引用实际资源 + */ +struct alignas(32) RenderCommand { + RenderCommandType type; // 命令类型 + uint32_t sortKey; // 排序键(材质ID + 层) + + union { + // 绘制网格命令 + struct { + MeshHandle mesh; // 网格句柄 + MaterialHandle material; // 材质句柄 + Transform transform; // 世界变换 + } drawMesh; + + // 实例化绘制命令 + struct { + MeshHandle mesh; // 网格句柄 + MaterialHandle material; // 材质句柄 + uint32_t instanceCount; // 实例数量 + uint32_t instanceOffset; // 实例数据偏移 + } drawInstanced; + + // 设置视口命令 + struct { + int32_t x, y; // 视口位置 + int32_t width, height; // 视口大小 + } viewport; + + // 清除缓冲区命令 + struct { + Color color; // 清除颜色 + uint32_t flags; // 清除标志(颜色/深度/模板) + } clear; + }; + + /** + * @brief 默认构造函数 + */ + RenderCommand() : type(RenderCommandType::DrawMesh), sortKey(0) { + drawMesh.mesh = INVALID_MESH_HANDLE; + drawMesh.material = INVALID_MATERIAL_HANDLE; + drawMesh.transform = Transform(); + } +}; + +// 清除标志 +constexpr uint32_t CLEAR_COLOR_FLAG = 1 << 0; +constexpr uint32_t CLEAR_DEPTH_FLAG = 1 << 1; +constexpr uint32_t CLEAR_STENCIL_FLAG = 1 << 2; +constexpr uint32_t CLEAR_ALL_FLAG = CLEAR_COLOR_FLAG | CLEAR_DEPTH_FLAG | CLEAR_STENCIL_FLAG; + +// 最大渲染命令数 +constexpr uint32_t MAX_RENDER_COMMANDS = 10000; + +// 最大实例数 +constexpr uint32_t MAX_INSTANCES = 256; + +// UBO 绑定槽位 +constexpr uint32_t GLOBAL_UBO_BINDING = 0; // 全局 UBO +constexpr uint32_t MATERIAL_UBO_BINDING = 1; // 材质 UBO +constexpr uint32_t INSTANCE_UBO_BINDING = 2; // 实例 UBO + +} // namespace extra2d diff --git a/include/renderer/renderer_module.h b/include/renderer/renderer_module.h new file mode 100644 index 0000000..b2a6d83 --- /dev/null +++ b/include/renderer/renderer_module.h @@ -0,0 +1,404 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 渲染器模块 + * + * 核心渲染系统模块,负责: + * - 通过事件接收渲染命令 + * - 自动批处理和排序 + * - GPU 资源管理 + * - 执行实际渲染 + */ +class RendererModule : public Module { + // 自动注册到模块系统,优先级为 3(在 Window、Timer、Input 之后) + E2D_REGISTER_MODULE(RendererModule, "Renderer", 3) + +public: + /** + * @brief 默认构造函数 + */ + RendererModule(); + + /** + * @brief 析构函数 + */ + ~RendererModule() override; + + // 禁止拷贝 + RendererModule(const RendererModule &) = delete; + RendererModule &operator=(const RendererModule &) = delete; + + // 允许移动 + RendererModule(RendererModule &&) noexcept; + RendererModule &operator=(RendererModule &&) noexcept; + + /** + * @brief 初始化模块 + * + * 绑定事件监听器,等待窗口显示事件进行 GL 初始化 + * + * @return 初始化是否成功 + */ + bool init() override; + + /** + * @brief 关闭模块 + * + * 清理所有渲染资源 + */ + void shutdown() override; + + //=========================================================================== + // 资源注册接口(供其他模块使用) + //=========================================================================== + + /** + * @brief 注册材质 + * @param material 材质对象 + * @return 材质句柄 + */ + MaterialHandle registerMaterial(Ptr material); + + /** + * @brief 注册网格 + * @param mesh 网格对象 + * @return 网格句柄 + */ + MeshHandle registerMesh(Ptr mesh); + + /** + * @brief 注册纹理 + * @param texture 纹理对象 + * @return 纹理句柄 + */ + TextureHandle registerTexture(Ptr texture); + + /** + * @brief 注销材质 + * @param handle 材质句柄 + */ + void unregisterMaterial(MaterialHandle handle); + + /** + * @brief 注销网格 + * @param handle 网格句柄 + */ + void unregisterMesh(MeshHandle handle); + + /** + * @brief 注销纹理 + * @param handle 纹理句柄 + */ + void unregisterTexture(TextureHandle handle); + + /** + * @brief 获取材质 + * @param handle 材质句柄 + * @return 材质对象,无效句柄返回 nullptr + */ + Ptr getMaterial(MaterialHandle handle); + + /** + * @brief 获取网格 + * @param handle 网格句柄 + * @return 网格对象,无效句柄返回 nullptr + */ + Ptr getMesh(MeshHandle handle); + + /** + * @brief 获取纹理 + * @param handle 纹理句柄 + * @return 纹理对象,无效句柄返回 nullptr + */ + Ptr getTexture(TextureHandle handle); + + //=========================================================================== + // 默认资源 + //=========================================================================== + + /** + * @brief 获取默认材质句柄 + * @return 默认材质句柄 + */ + MaterialHandle getDefaultMaterialHandle() const { + return defaultMaterialHandle_; + } + + /** + * @brief 获取默认四边形网格句柄 + * @return 默认四边形网格句柄 + */ + MeshHandle getDefaultQuadHandle() const { return defaultQuadHandle_; } + + /** + * @brief 获取默认纹理句柄(1x1 白色纹理) + * @return 默认纹理句柄 + */ + TextureHandle getDefaultTextureHandle() const { + return defaultTextureHandle_; + } + + //=========================================================================== + // 渲染状态设置 + //=========================================================================== + + /** + * @brief 设置视口 + * @param x 视口左上角 X 坐标 + * @param y 视口左上角 Y 坐标 + * @param width 视口宽度 + * @param height 视口高度 + */ + void setViewport(int32 x, int32 y, int32 width, int32 height); + + /** + * @brief 清除缓冲区 + * @param color 清除颜色 + * @param flags 清除标志(组合使用 CLEAR_COLOR_FLAG, CLEAR_DEPTH_FLAG, CLEAR_STENCIL_FLAG) + */ + void clear(const Color &color, uint32 flags = CLEAR_COLOR_FLAG); + +private: + //=========================================================================== + // 事件处理器 + //=========================================================================== + + /** + * @brief 渲染开始事件处理 + * + * 清空命令缓冲区,重置统计信息 + */ + void onRenderBegin(); + + /** + * @brief 渲染提交事件处理 + * @param cmd 渲染命令 + */ + void onRenderSubmit(const RenderCommand &cmd); + + /** + * @brief 渲染结束事件处理 + * + * 排序命令,批处理并执行绘制 + */ + void onRenderEnd(); + + /** + * @brief 窗口大小改变事件处理 + * @param width 新宽度 + * @param height 新高度 + */ + void onResize(int32 width, int32 height); + + /** + * @brief 窗口显示事件处理 + * + * 延迟 GL 初始化到窗口显示时 + */ + void onWindowShow(); + + //=========================================================================== + // 渲染执行 + //=========================================================================== + + /** + * @brief 排序渲染命令 + * + * 根据 sortKey 对命令进行排序以优化绘制顺序 + */ + void sortCommands(); + + /** + * @brief 批处理并绘制 + * + * 将相同材质和网格的命令合并批次绘制 + */ + void batchAndDraw(); + + /** + * @brief 绘制批次 + * @param start 批次起始索引 + * @param count 批次命令数量 + * @param materialHandle 材质句柄 + * @param meshHandle 网格句柄 + */ + void drawBatch(uint32 start, uint32 count, MaterialHandle materialHandle, + MeshHandle meshHandle); + + /** + * @brief 执行单个渲染命令 + * @param cmd 渲染命令 + */ + void executeCommand(const RenderCommand &cmd); + + //=========================================================================== + // 默认资源创建与销毁 + //=========================================================================== + + /** + * @brief 创建默认资源 + * @return 创建是否成功 + */ + bool createDefaultResources(); + + /** + * @brief 销毁默认资源 + */ + void destroyDefaultResources(); + + //=========================================================================== + // 资源句柄管理 + //=========================================================================== + + /** + * @brief 资源槽位结构 + * + * 用于管理资源对象和句柄生命周期 + */ + struct ResourceSlot { + Ptr material; + Ptr mesh; + Ptr texture; + uint32 generation = 0; + bool active = false; + }; + + /** + * @brief 句柄池模板类 + * + * 管理资源句柄的分配和回收 + */ + template struct HandlePool { + std::vector slots; + std::queue freeIndices; + uint32 nextGeneration = 1; + + /** + * @brief 分配新句柄 + * @return 编码后的句柄(高32位索引,低32位世代) + */ + uint64 acquire() { + uint32 index; + if (!freeIndices.empty()) { + index = freeIndices.front(); + freeIndices.pop(); + } else { + index = static_cast(slots.size()); + slots.emplace_back(); + } + + slots[index].active = true; + slots[index].generation = nextGeneration++; + + // 编码句柄:高32位是索引,低32位是世代 + return (static_cast(index) << 32) | slots[index].generation; + } + + /** + * @brief 释放句柄 + * @param handle 要释放的句柄 + */ + void release(uint64 handle) { + uint32 index = static_cast(handle >> 32); + if (index < slots.size() && slots[index].active) { + slots[index].active = false; + slots[index].material.reset(); + slots[index].mesh.reset(); + slots[index].texture.reset(); + freeIndices.push(index); + } + } + + /** + * @brief 获取资源槽位 + * @param handle 资源句柄 + * @return 资源槽位指针,无效句柄返回 nullptr + */ + ResourceSlot *get(uint64 handle) { + uint32 index = static_cast(handle >> 32); + uint32 generation = static_cast(handle & 0xFFFFFFFF); + + if (index < slots.size() && slots[index].active && + slots[index].generation == generation) { + return &slots[index]; + } + return nullptr; + } + }; + + HandlePool materialPool_; // 材质资源池 + HandlePool meshPool_; // 网格资源池 + HandlePool texturePool_; // 纹理资源池 + + //=========================================================================== + // 命令缓冲区 + //=========================================================================== + + std::array commandBuffer_; // 预分配命令缓冲区 + uint32 commandCount_ = 0; // 当前命令数量 + + //=========================================================================== + // UBO 管理器 + //=========================================================================== + + UniformBufferManager uniformManager_; + + //=========================================================================== + // 默认资源句柄 + //=========================================================================== + + MaterialHandle defaultMaterialHandle_ = INVALID_MATERIAL_HANDLE; + MeshHandle defaultQuadHandle_ = INVALID_MESH_HANDLE; + TextureHandle defaultTextureHandle_ = INVALID_TEXTURE_HANDLE; + + //=========================================================================== + // 事件监听器 + //=========================================================================== + + events::OnRenderBegin::Listener onRenderBeginListener_; + events::OnRenderSubmit::Listener onRenderSubmitListener_; + events::OnRenderEnd::Listener onRenderEndListener_; + events::OnResize::Listener onResizeListener_; + events::OnShow::Listener onShowListener_; + + //=========================================================================== + // 状态标志 + //=========================================================================== + + bool glInitialized_ = false; // GL 是否已初始化 + + //=========================================================================== + // 渲染统计 + //=========================================================================== + + struct Stats { + uint32 commandsSubmitted = 0; // 提交的命令数 + uint32 commandsExecuted = 0; // 执行的命令数 + uint32 drawCalls = 0; // 绘制调用次数 + uint32 batches = 0; // 批次数 + } stats_; + + //=========================================================================== + // 视口状态 + //=========================================================================== + + int32 viewportX_ = 0, viewportY_ = 0; + int32 viewportWidth_ = 0, viewportHeight_ = 0; +}; + +} // namespace extra2d diff --git a/include/renderer/shader.h b/include/renderer/shader.h new file mode 100644 index 0000000..888be6d --- /dev/null +++ b/include/renderer/shader.h @@ -0,0 +1,155 @@ +#pragma once + +#include +#include +#include +#include +#include + +// 前向声明 OpenGL 类型 +typedef unsigned int GLuint; +typedef int GLint; +typedef unsigned int GLenum; + +namespace extra2d { + +/** + * @brief 着色器类 + * + * 管理 OpenGL 着色器程序的编译、链接和使用 + * 支持从文件或源码加载 + */ +class Shader : public RefCounted { +public: + /** + * @brief 默认构造函数 + */ + Shader(); + + /** + * @brief 析构函数 + */ + ~Shader() override; + + /** + * @brief 从文件加载着色器 + * @param vsPath 顶点着色器文件路径 + * @param fsPath 片段着色器文件路径 + * @return 加载是否成功 + */ + bool loadFromFile(const std::string& vsPath, const std::string& fsPath); + + /** + * @brief 从源码加载着色器 + * @param vsSource 顶点着色器源码 + * @param fsSource 片段着色器源码 + * @return 加载是否成功 + */ + bool loadFromSource(const std::string& vsSource, const std::string& fsSource); + + /** + * @brief 绑定着色器程序 + */ + void bind() const; + + /** + * @brief 解绑着色器程序 + */ + void unbind() const; + + /** + * @brief 设置 Uniform Block 绑定槽位 + * @param name Uniform Block 名称 + * @param binding 绑定槽位 + */ + void setUniformBlock(const std::string& name, uint32_t binding); + + /** + * @brief 设置 int 类型 uniform + * @param name uniform 名称 + * @param value 值 + */ + void setInt(const std::string& name, int value); + + /** + * @brief 设置 float 类型 uniform + * @param name uniform 名称 + * @param value 值 + */ + void setFloat(const std::string& name, float value); + + /** + * @brief 设置 vec2 类型 uniform + * @param name uniform 名称 + * @param x X 分量 + * @param y Y 分量 + */ + void setVec2(const std::string& name, float x, float y); + + /** + * @brief 设置 vec4 类型 uniform + * @param name uniform 名称 + * @param x X 分量 + * @param y Y 分量 + * @param z Z 分量 + * @param w W 分量 + */ + void setVec4(const std::string& name, float x, float y, float z, float w); + + /** + * @brief 设置 mat4 类型 uniform + * @param name uniform 名称 + * @param value 矩阵数据指针(16个float) + */ + void setMat4(const std::string& name, const float* value); + + /** + * @brief 获取着色器程序 ID + * @return OpenGL 程序 ID + */ + GLuint getProgram() const { return program_; } + + /** + * @brief 检查是否已加载 + * @return 是否已加载 + */ + bool isLoaded() const { return program_ != 0; } + +private: + /** + * @brief 编译着色器 + * @param type 着色器类型 + * @param source 源码 + * @return 着色器对象,失败返回 0 + */ + GLuint compileShader(GLenum type, const std::string& source); + + /** + * @brief 链接着色器程序 + * @param vertexShader 顶点着色器 + * @param fragmentShader 片段着色器 + * @return 链接是否成功 + */ + bool linkProgram(GLuint vertexShader, GLuint fragmentShader); + + /** + * @brief 获取 uniform 位置 + * @param name uniform 名称 + * @return uniform 位置,不存在返回 -1 + */ + GLint getUniformLocation(const std::string& name); + + /** + * @brief 添加版本声明(如果不存在) + * @param source 源码 + * @param type 着色器类型 + * @return 处理后的源码 + */ + std::string addVersionIfNeeded(const std::string& source, GLenum type); + +private: + GLuint program_ = 0; // OpenGL 程序对象 + std::unordered_map uniformCache_; // Uniform 位置缓存 +}; + +} // namespace extra2d diff --git a/include/renderer/texture.h b/include/renderer/texture.h new file mode 100644 index 0000000..496e0c8 --- /dev/null +++ b/include/renderer/texture.h @@ -0,0 +1,106 @@ +#pragma once + +#include +#include +#include +#include +#include + +// 前向声明 OpenGL 类型 +typedef unsigned int GLuint; + +namespace extra2d { + +/** + * @brief 纹理类 + * + * 管理 OpenGL 纹理的创建、加载和绑定 + * 支持从文件或内存加载 + */ +class Texture : public RefCounted { +public: + /** + * @brief 默认构造函数 + */ + Texture(); + + /** + * @brief 析构函数 + */ + ~Texture() override; + + /** + * @brief 从文件加载纹理 + * @param path 文件路径 + * @return 加载是否成功 + */ + bool loadFromFile(const std::string& path); + + /** + * @brief 从内存加载纹理 + * @param data 像素数据 + * @param width 宽度 + * @param height 高度 + * @param format 像素格式 + * @return 加载是否成功 + */ + bool loadFromMemory(const uint8_t* data, int width, int height, TextureFormat format); + + /** + * @brief 创建空纹理 + * @param width 宽度 + * @param height 高度 + * @param format 像素格式 + * @return 创建是否成功 + */ + bool create(int width, int height, TextureFormat format); + + /** + * @brief 绑定纹理到指定槽位 + * @param slot 纹理槽位 + */ + void bind(uint32_t slot = 0) const; + + /** + * @brief 解绑纹理 + */ + void unbind() const; + + /** + * @brief 获取纹理宽度 + * @return 宽度 + */ + int getWidth() const { return width_; } + + /** + * @brief 获取纹理高度 + * @return 高度 + */ + int getHeight() const { return height_; } + + /** + * @brief 获取纹理格式 + * @return 像素格式 + */ + TextureFormat getFormat() const { return format_; } + + /** + * @brief 获取 OpenGL 纹理对象 + * @return OpenGL 纹理对象 + */ + GLuint getHandle() const { return texture_; } + + /** + * @brief 检查是否已加载 + * @return 是否已加载 + */ + bool isLoaded() const { return texture_ != 0; } + +private: + GLuint texture_ = 0; // OpenGL 纹理对象 + int width_ = 0; // 纹理宽度 + int height_ = 0; // 纹理高度 + TextureFormat format_ = TextureFormat::RGBA8; // 像素格式 +}; + +} // namespace extra2d diff --git a/include/renderer/uniform_buffer.h b/include/renderer/uniform_buffer.h new file mode 100644 index 0000000..8e84ebd --- /dev/null +++ b/include/renderer/uniform_buffer.h @@ -0,0 +1,157 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 统一缓冲区对象 (UBO) + * + * 用于高效地传递 uniform 数据到 GPU + * 支持 std140 布局标准 + */ +class UniformBuffer { +public: + /** + * @brief 默认构造函数 + */ + UniformBuffer(); + + /** + * @brief 析构函数 + */ + ~UniformBuffer(); + + /** + * @brief 创建 UBO + * @param size 缓冲区大小(字节) + * @param binding 绑定槽位 + * @return 创建是否成功 + */ + bool create(uint32_t size, uint32_t binding); + + /** + * @brief 销毁 UBO + */ + void destroy(); + + /** + * @brief 更新缓冲区数据 + * @param data 数据源指针 + * @param size 数据大小 + * @param offset 缓冲区偏移(字节) + */ + void update(const void *data, uint32_t size, uint32_t offset = 0); + + /** + * @brief 绑定到指定槽位 + * @param binding 绑定槽位 + */ + void bind(uint32_t binding); + + /** + * @brief 获取缓冲区大小 + * @return 缓冲区大小 + */ + uint32_t getSize() const { return size_; } + + /** + * @brief 获取 OpenGL 缓冲区对象 + * @return OpenGL 缓冲区对象 + */ + GLuint getHandle() const { return ubo_; } + + /** + * @brief 检查是否有效 + * @return 是否有效 + */ + bool isValid() const { return ubo_ != 0; } + +private: + GLuint ubo_ = 0; // OpenGL 缓冲区对象 + uint32_t size_ = 0; // 缓冲区大小 + uint32_t binding_ = 0; // 绑定槽位 +}; + +/** + * @brief UBO 管理器 + * + * 管理多个 UBO 的分配和回收 + * 支持每帧重置和对象池复用 + */ +class UniformBufferManager { +public: + /** + * @brief 默认构造函数 + */ + UniformBufferManager(); + + /** + * @brief 析构函数 + */ + ~UniformBufferManager(); + + // 禁止拷贝 + UniformBufferManager(const UniformBufferManager &) = delete; + UniformBufferManager &operator=(const UniformBufferManager &) = delete; + + // 允许移动 + UniformBufferManager(UniformBufferManager &&) noexcept; + UniformBufferManager &operator=(UniformBufferManager &&) noexcept; + + /** + * @brief 初始化管理器 + * @return 初始化是否成功 + */ + bool initialize(); + + /** + * @brief 关闭管理器 + */ + void shutdown(); + + /** + * @brief 获取全局 UBO + * @return 全局 UBO 指针 + */ + UniformBuffer *getGlobalUBO(); + + /** + * @brief 获取或创建材质 UBO + * @param size 缓冲区大小 + * @return UBO 指针 + */ + UniformBuffer *acquireMaterialUBO(uint32_t size); + + /** + * @brief 重置材质 UBO 池 + * + * 每帧调用,重置当前索引以便复用 UBO + */ + void resetMaterialUBOs(); + + /** + * @brief 更新全局 UBO 数据 + * @param data 数据指针 + * @param size 数据大小 + */ + void updateGlobalUBO(const void *data, uint32_t size); + +private: + // 全局 UBO(用于 viewProjection、time 等全局数据) + std::unique_ptr globalUBO_; + + // 材质 UBO 池 + std::vector> materialUBOPool_; + uint32_t currentUBOIndex_ = 0; + + // 常量 + static constexpr uint32_t INITIAL_UBO_POOL_SIZE = 16; + static constexpr uint32_t GLOBAL_UBO_SIZE = + 256; // 足够存储 viewProjection + time + screenSize +}; + +} // namespace extra2d diff --git a/include/types/math/transform.h b/include/types/math/transform.h index f7e34f9..e3c11a3 100644 --- a/include/types/math/transform.h +++ b/include/types/math/transform.h @@ -2,6 +2,7 @@ #include #include +#include namespace extra2d { @@ -25,6 +26,21 @@ struct Transform { } bool operator!=(const Transform& o) const { return !(*this == o); } + /** + * @brief 转换为 4x4 矩阵(列主序) + * @param out 输出矩阵(16个float) + */ + void toMatrix(float* out) const { + float c = std::cos(rot); + float s = std::sin(rot); + + // 列主序矩阵 + out[0] = scale.x * c; out[1] = scale.x * s; out[2] = 0.0f; out[3] = 0.0f; + out[4] = -scale.y * s; out[5] = scale.y * c; out[6] = 0.0f; out[7] = 0.0f; + out[8] = 0.0f; out[9] = 0.0f; out[10] = 1.0f; out[11] = 0.0f; + out[12] = pos.x; out[13] = pos.y; out[14] = 0.0f; out[15] = 1.0f; + } + static const Transform Identity; }; diff --git a/shader/default.frag b/shader/default.frag new file mode 100644 index 0000000..77d0444 --- /dev/null +++ b/shader/default.frag @@ -0,0 +1,32 @@ +#version 320 es +precision mediump float; + +// 材质 UBO +layout(std140, binding = 1) uniform MaterialUBO { + vec4 tintColor; + float opacity; +} uMaterial; + +// 输入从顶点着色器 +in vec2 vTexCoord; +in vec4 vColor; + +// 纹理采样器 +uniform sampler2D uTexture; + +// 输出颜色 +out vec4 fragColor; + +void main() { + // 采样纹理 + vec4 texColor = texture(uTexture, vTexCoord); + + // 应用顶点颜色、材质颜色和透明度 + fragColor = texColor * vColor * uMaterial.tintColor; + fragColor.a *= uMaterial.opacity; + + // 丢弃完全透明的像素 + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/shader/default.vert b/shader/default.vert new file mode 100644 index 0000000..9f9dc46 --- /dev/null +++ b/shader/default.vert @@ -0,0 +1,34 @@ +#version 320 es + +// 全局 UBO +layout(std140, binding = 0) uniform GlobalUBO { + mat4 viewProjection; + vec2 screenSize; + float time; +} uGlobal; + +// 实例 UBO +layout(std140, binding = 2) uniform InstanceUBO { + mat4 transforms[256]; +} uInstances; + +// 顶点属性 +layout(location = 0) in vec2 aPosition; +layout(location = 1) in vec2 aTexCoord; +layout(location = 2) in vec4 aColor; + +// 输出到片段着色器 +out vec2 vTexCoord; +out vec4 vColor; + +void main() { + // 获取实例变换矩阵 + mat4 instanceTransform = uInstances.transforms[gl_InstanceID]; + + // 计算最终位置 + gl_Position = uGlobal.viewProjection * instanceTransform * vec4(aPosition, 0.0, 1.0); + + // 传递纹理坐标和颜色 + vTexCoord = aTexCoord; + vColor = aColor; +} diff --git a/src/context/context.cpp b/src/context/context.cpp index db45e08..a343994 100644 --- a/src/context/context.cpp +++ b/src/context/context.cpp @@ -2,13 +2,12 @@ #include #include #include +#include #include namespace extra2d { -Context::Context() - : pluginLoader_(std::make_unique()), - timerModule_(std::make_unique()) {} +Context::Context() : pluginLoader_(std::make_unique()) {} Context::~Context() { if (inited_) { @@ -31,11 +30,6 @@ bool Context::init() { // 发送引擎初始化事件 events::OnInit::emit(); - // 初始化定时器模块 - if (!timerModule_->init()) { - return false; - } - // 初始化所有插件 if (!pluginLoader_->initAll()) { return false; @@ -56,9 +50,6 @@ void Context::shutdown() { // 关闭所有插件 pluginLoader_->shutdownAll(); - // 关闭定时器模块 - timerModule_->shutdown(); - // 发送引擎关闭事件 events::OnShutdown::emit(); @@ -74,20 +65,24 @@ void Context::tick(float dt) { totalTime_ += dt; frameCount_++; - // 更新定时器模块 - timerModule_->update(dt); - // 发送更新事件 events::OnUpdate::emit(dt); // 发送延迟更新事件 events::OnLateUpdate::emit(dt); + + // 渲染阶段 + // 1. 渲染开始 + events::OnRenderBegin::emit(); + + // 2. 场景图提交渲染命令(通过 OnRenderSubmit 事件) + + // 3. 渲染结束并执行绘制 + events::OnRenderEnd::emit(); } ModuleRegistry &Context::modules() { return ModuleRegistry::instance(); } PluginLoader &Context::plugins() { return *pluginLoader_; } -TimerModule &Context::timer() { return *timerModule_; } - } // namespace extra2d diff --git a/src/platform/window_module.cpp b/src/platform/window_module.cpp index 71ee943..0981073 100644 --- a/src/platform/window_module.cpp +++ b/src/platform/window_module.cpp @@ -81,9 +81,6 @@ bool WindowModule::create(const WindowCfg &cfg) { return false; } - // 发送窗口显示事件 - events::OnShow::emit(); - E2D_LOG_INFO("Window created: {}x{}", cfg.width, cfg.height); return true; } @@ -165,6 +162,11 @@ void WindowModule::onModuleConfig(const AppConfig &config) { E2D_LOG_ERROR("Failed to create OpenGL context"); return; } + + // GL 上下文创建完成后,再发送窗口显示事件 + // 这样渲染模块可以在收到事件时安全地使用 GL 函数 + events::OnShow::emit(); + setVisible(true); } } diff --git a/src/renderer/material.cpp b/src/renderer/material.cpp new file mode 100644 index 0000000..cf95de2 --- /dev/null +++ b/src/renderer/material.cpp @@ -0,0 +1,158 @@ +#include +#include +#include + +namespace extra2d { + +// MaterialLayout 实现 + +MaterialLayout::MaterialLayout() = default; + +void MaterialLayout::addParam(const std::string& name, MaterialParamType type) { + if (finalized_) { + E2D_LOG_WARN("Cannot add param to finalized MaterialLayout"); + return; + } + + MaterialParamInfo info; + info.type = type; + info.size = getMaterialParamSize(type); + info.offset = 0; // 将在 finalize 时计算 + + params_[name] = info; +} + +void MaterialLayout::finalize() { + if (finalized_) return; + + // 计算 std140 布局的偏移 + // std140 规则: + // - 标量和向量:偏移必须是其大小的倍数 + // - 数组和结构体:偏移必须是 16 的倍数 + // - vec3 后面需要填充到 16 字节边界 + + uint32_t offset = 0; + for (auto& pair : params_) { + auto& info = pair.second; + + // 对齐到参数大小的倍数 + uint32_t alignment = info.size; + if (info.type == MaterialParamType::Mat4) { + alignment = 16; // mat4 需要 16 字节对齐 + } else if (info.type == MaterialParamType::Vec3) { + alignment = 16; // vec3 在 std140 中占 16 字节 + } else if (alignment < 4) { + alignment = 4; // 最小 4 字节对齐 + } + + // 对齐偏移 + offset = (offset + alignment - 1) & ~(alignment - 1); + + info.offset = offset; + offset += info.size; + } + + // 最终大小对齐到 16 字节 + bufferSize_ = (offset + 15) & ~15; + finalized_ = true; +} + +const MaterialParamInfo* MaterialLayout::getParam(const std::string& name) const { + auto it = params_.find(name); + if (it != params_.end()) { + return &it->second; + } + return nullptr; +} + +// Material 实现 + +Material::Material() = default; + +void Material::setLayout(Ptr layout) { + layout_ = layout; + if (layout_ && layout_->isFinalized()) { + data_.resize(layout_->getBufferSize(), 0); + } +} + +void Material::setShader(Ptr shader) { + shader_ = shader; +} + +void Material::setFloat(const std::string& name, float value) { + if (!layout_) return; + + const auto* param = layout_->getParam(name); + if (param && param->type == MaterialParamType::Float) { + std::memcpy(data_.data() + param->offset, &value, sizeof(float)); + } +} + +void Material::setVec2(const std::string& name, const Vec2& value) { + if (!layout_) return; + + const auto* param = layout_->getParam(name); + if (param && param->type == MaterialParamType::Vec2) { + std::memcpy(data_.data() + param->offset, &value.x, sizeof(float) * 2); + } +} + +void Material::setVec4(const std::string& name, float x, float y, float z, float w) { + if (!layout_) return; + + const auto* param = layout_->getParam(name); + if (param && param->type == MaterialParamType::Vec4) { + float values[4] = {x, y, z, w}; + std::memcpy(data_.data() + param->offset, values, sizeof(float) * 4); + } +} + +void Material::setColor(const std::string& name, const Color& value) { + if (!layout_) return; + + const auto* param = layout_->getParam(name); + if (param && param->type == MaterialParamType::Color) { + std::memcpy(data_.data() + param->offset, &value.r, sizeof(float) * 4); + } +} + +void Material::setMat4(const std::string& name, const float* value) { + if (!layout_) return; + + const auto* param = layout_->getParam(name); + if (param && param->type == MaterialParamType::Mat4) { + std::memcpy(data_.data() + param->offset, value, sizeof(float) * 16); + } +} + +void Material::setTexture(const std::string& name, TextureHandle texture, uint32_t slot) { + // 纹理存储在单独的列表中 + textures_.push_back({texture, slot}); +} + +void Material::apply(UniformBufferManager& uboManager) { + if (!shader_) return; + + // 绑定着色器 + shader_->bind(); + + // 上传材质数据到 UBO + if (!data_.empty()) { + auto* ubo = uboManager.acquireMaterialUBO(static_cast(data_.size())); + if (ubo) { + ubo->update(data_.data(), static_cast(data_.size())); + ubo->bind(MATERIAL_UBO_BINDING); + } + } + + // 设置 Uniform Block 绑定(如果着色器支持) + shader_->setUniformBlock("MaterialUBO", MATERIAL_UBO_BINDING); + + // TODO: 绑定纹理 + // for (const auto& [texture, slot] : textures_) { + // // 通过纹理句柄获取纹理并绑定 + // } +} + +} // namespace extra2d diff --git a/src/renderer/mesh.cpp b/src/renderer/mesh.cpp new file mode 100644 index 0000000..25ce619 --- /dev/null +++ b/src/renderer/mesh.cpp @@ -0,0 +1,147 @@ +#include +#include +#include + +namespace extra2d { + +Mesh::Mesh() = default; + +Mesh::~Mesh() { + if (vao_ != 0) { + glDeleteVertexArrays(1, &vao_); + vao_ = 0; + } + if (vbo_ != 0) { + glDeleteBuffers(1, &vbo_); + vbo_ = 0; + } + if (ibo_ != 0) { + glDeleteBuffers(1, &ibo_); + ibo_ = 0; + } +} + +void Mesh::setVertices(const Vertex* vertices, uint32_t count) { + if (vao_ == 0) { + glGenVertexArrays(1, &vao_); + glGenBuffers(1, &vbo_); + } + + glBindVertexArray(vao_); + glBindBuffer(GL_ARRAY_BUFFER, vbo_); + + // 如果容量不足,重新分配 + if (count > vertexCapacity_) { + glBufferData(GL_ARRAY_BUFFER, count * sizeof(Vertex), vertices, GL_DYNAMIC_DRAW); + vertexCapacity_ = count; + } else { + glBufferSubData(GL_ARRAY_BUFFER, 0, count * sizeof(Vertex), vertices); + } + + // 设置顶点属性 + // 位置 (location = 0) + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), + reinterpret_cast(offsetof(Vertex, position))); + + // 纹理坐标 (location = 1) + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), + reinterpret_cast(offsetof(Vertex, texCoord))); + + // 颜色 (location = 2) + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), + reinterpret_cast(offsetof(Vertex, color))); + + glBindVertexArray(0); + vertexCount_ = count; +} + +void Mesh::setIndices(const uint16_t* indices, uint32_t count) { + if (vao_ == 0) return; + + if (ibo_ == 0) { + glGenBuffers(1, &ibo_); + } + + glBindVertexArray(vao_); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_); + + // 如果容量不足,重新分配 + if (count > indexCapacity_) { + glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(uint16_t), indices, GL_STATIC_DRAW); + indexCapacity_ = count; + } else { + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, count * sizeof(uint16_t), indices); + } + + glBindVertexArray(0); + indexCount_ = count; +} + +void Mesh::updateVertices(const Vertex* vertices, uint32_t count, uint32_t offset) { + if (vao_ == 0 || vbo_ == 0 || vertices == nullptr) return; + + glBindBuffer(GL_ARRAY_BUFFER, vbo_); + glBufferSubData(GL_ARRAY_BUFFER, offset * sizeof(Vertex), + count * sizeof(Vertex), vertices); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void Mesh::bind() const { + if (vao_ != 0) { + glBindVertexArray(vao_); + } +} + +void Mesh::unbind() const { + glBindVertexArray(0); +} + +void Mesh::draw() const { + if (vao_ == 0) return; + + if (indexCount_ > 0) { + glDrawElements(GL_TRIANGLES, indexCount_, GL_UNSIGNED_SHORT, nullptr); + } else { + glDrawArrays(GL_TRIANGLES, 0, vertexCount_); + } +} + +void Mesh::drawInstanced(uint32_t instanceCount) const { + if (vao_ == 0) return; + + if (indexCount_ > 0) { + glDrawElementsInstanced(GL_TRIANGLES, indexCount_, GL_UNSIGNED_SHORT, + nullptr, instanceCount); + } else { + glDrawArraysInstanced(GL_TRIANGLES, 0, vertexCount_, instanceCount); + } +} + +Ptr Mesh::createQuad(const Vec2& size, const Rect& uv) { + Ptr mesh = makePtr(); + + float halfW = size.x * 0.5f; + float halfH = size.y * 0.5f; + + Vertex vertices[4] = { + {Vec2(-halfW, -halfH), Vec2(uv.x, uv.y + uv.h), Color::White}, // 左下 + {Vec2( halfW, -halfH), Vec2(uv.x + uv.w, uv.y + uv.h), Color::White}, // 右下 + {Vec2( halfW, halfH), Vec2(uv.x + uv.w, uv.y), Color::White}, // 右上 + {Vec2(-halfW, halfH), Vec2(uv.x, uv.y), Color::White} // 左上 + }; + + uint16_t indices[6] = { + 0, 1, 2, // 第一个三角形 + 0, 2, 3 // 第二个三角形 + }; + + mesh->setVertices(vertices, 4); + mesh->setIndices(indices, 6); + + return mesh; +} + +} // namespace extra2d diff --git a/src/renderer/renderer_module.cpp b/src/renderer/renderer_module.cpp new file mode 100644 index 0000000..0032677 --- /dev/null +++ b/src/renderer/renderer_module.cpp @@ -0,0 +1,481 @@ +#include +#include +#include +#include + +namespace extra2d { + +RendererModule::RendererModule() = default; + +RendererModule::~RendererModule() = default; + +RendererModule::RendererModule(RendererModule &&other) noexcept + : materialPool_(std::move(other.materialPool_)), + meshPool_(std::move(other.meshPool_)), + texturePool_(std::move(other.texturePool_)), + commandBuffer_(std::move(other.commandBuffer_)), + commandCount_(other.commandCount_), + uniformManager_(std::move(other.uniformManager_)), + defaultMaterialHandle_(other.defaultMaterialHandle_), + defaultQuadHandle_(other.defaultQuadHandle_), + defaultTextureHandle_(other.defaultTextureHandle_), + onRenderBeginListener_(std::move(other.onRenderBeginListener_)), + onRenderSubmitListener_(std::move(other.onRenderSubmitListener_)), + onRenderEndListener_(std::move(other.onRenderEndListener_)), + onResizeListener_(std::move(other.onResizeListener_)), + onShowListener_(std::move(other.onShowListener_)), + glInitialized_(other.glInitialized_), stats_(other.stats_), + viewportX_(other.viewportX_), viewportY_(other.viewportY_), + viewportWidth_(other.viewportWidth_), + viewportHeight_(other.viewportHeight_) { + // 重置源对象状态 + other.commandCount_ = 0; + other.defaultMaterialHandle_ = INVALID_MATERIAL_HANDLE; + other.defaultQuadHandle_ = INVALID_MESH_HANDLE; + other.defaultTextureHandle_ = INVALID_TEXTURE_HANDLE; + other.glInitialized_ = false; + other.stats_ = {}; + other.viewportX_ = 0; + other.viewportY_ = 0; + other.viewportWidth_ = 0; + other.viewportHeight_ = 0; +} + +RendererModule &RendererModule::operator=(RendererModule &&other) noexcept { + if (this != &other) { + // 清理当前资源 + if (glInitialized_) { + destroyDefaultResources(); + uniformManager_.shutdown(); + } + + // 移动资源 + materialPool_ = std::move(other.materialPool_); + meshPool_ = std::move(other.meshPool_); + texturePool_ = std::move(other.texturePool_); + commandBuffer_ = std::move(other.commandBuffer_); + commandCount_ = other.commandCount_; + uniformManager_ = std::move(other.uniformManager_); + defaultMaterialHandle_ = other.defaultMaterialHandle_; + defaultQuadHandle_ = other.defaultQuadHandle_; + defaultTextureHandle_ = other.defaultTextureHandle_; + onRenderBeginListener_ = std::move(other.onRenderBeginListener_); + onRenderSubmitListener_ = std::move(other.onRenderSubmitListener_); + onRenderEndListener_ = std::move(other.onRenderEndListener_); + onResizeListener_ = std::move(other.onResizeListener_); + onShowListener_ = std::move(other.onShowListener_); + glInitialized_ = other.glInitialized_; + stats_ = other.stats_; + viewportX_ = other.viewportX_; + viewportY_ = other.viewportY_; + viewportWidth_ = other.viewportWidth_; + viewportHeight_ = other.viewportHeight_; + + // 重置源对象状态 + other.commandCount_ = 0; + other.defaultMaterialHandle_ = INVALID_MATERIAL_HANDLE; + other.defaultQuadHandle_ = INVALID_MESH_HANDLE; + other.defaultTextureHandle_ = INVALID_TEXTURE_HANDLE; + other.glInitialized_ = false; + other.stats_ = {}; + other.viewportX_ = 0; + other.viewportY_ = 0; + other.viewportWidth_ = 0; + other.viewportHeight_ = 0; + } + return *this; +} + +bool RendererModule::init() { + E2D_LOG_INFO("Initializing RendererModule..."); + + // 绑定事件监听器 + onRenderBeginListener_.bind([this]() { onRenderBegin(); }); + onRenderSubmitListener_.bind( + [this](const RenderCommand &cmd) { onRenderSubmit(cmd); }); + onRenderEndListener_.bind([this]() { onRenderEnd(); }); + onResizeListener_.bind([this](int32_t w, int32_t h) { onResize(w, h); }); + + // 延迟 GL 初始化到窗口显示时 + onShowListener_.bind([this]() { onWindowShow(); }); + + E2D_LOG_INFO("RendererModule initialized (waiting for GL context)"); + return true; +} + +void RendererModule::onWindowShow() { + if (glInitialized_) { + return; + } + + E2D_LOG_INFO("Initializing OpenGL context..."); + + // 初始化 UBO 管理器(需要 GL 上下文) + if (!uniformManager_.initialize()) { + E2D_LOG_ERROR("Failed to initialize UniformBufferManager"); + return; + } + + // 创建默认资源(需要 GL 上下文) + if (!createDefaultResources()) { + E2D_LOG_ERROR("Failed to create default resources"); + return; + } + + // 设置默认视口 + setViewport(0, 0, 800, 600); + + // 启用深度测试和混合 + glEnable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glInitialized_ = true; + E2D_LOG_INFO("OpenGL context initialized successfully"); +} + +void RendererModule::shutdown() { + E2D_LOG_INFO("Shutting down RendererModule..."); + + // 只有在 GL 初始化后才销毁 GL 相关资源 + if (glInitialized_) { + // 销毁默认资源 + destroyDefaultResources(); + + // 关闭 UBO 管理器 + uniformManager_.shutdown(); + } + + // 清理资源池 + materialPool_.slots.clear(); + while (!materialPool_.freeIndices.empty()) + materialPool_.freeIndices.pop(); + + meshPool_.slots.clear(); + while (!meshPool_.freeIndices.empty()) + meshPool_.freeIndices.pop(); + + texturePool_.slots.clear(); + while (!texturePool_.freeIndices.empty()) + texturePool_.freeIndices.pop(); + + glInitialized_ = false; + + E2D_LOG_INFO("RendererModule shutdown complete"); +} + +MaterialHandle RendererModule::registerMaterial(Ptr material) { + uint64_t handle = materialPool_.acquire(); + auto *slot = materialPool_.get(handle); + if (slot) { + slot->material = material; + } + return handle; +} + +MeshHandle RendererModule::registerMesh(Ptr mesh) { + uint64_t handle = meshPool_.acquire(); + auto *slot = meshPool_.get(handle); + if (slot) { + slot->mesh = mesh; + } + return handle; +} + +TextureHandle RendererModule::registerTexture(Ptr texture) { + uint64_t handle = texturePool_.acquire(); + auto *slot = texturePool_.get(handle); + if (slot) { + slot->texture = texture; + } + return handle; +} + +void RendererModule::unregisterMaterial(MaterialHandle handle) { + materialPool_.release(handle); +} + +void RendererModule::unregisterMesh(MeshHandle handle) { + meshPool_.release(handle); +} + +void RendererModule::unregisterTexture(TextureHandle handle) { + texturePool_.release(handle); +} + +Ptr RendererModule::getMaterial(MaterialHandle handle) { + auto *slot = materialPool_.get(handle); + return slot ? slot->material : nullptr; +} + +Ptr RendererModule::getMesh(MeshHandle handle) { + auto *slot = meshPool_.get(handle); + return slot ? slot->mesh : nullptr; +} + +Ptr RendererModule::getTexture(TextureHandle handle) { + auto *slot = texturePool_.get(handle); + return slot ? slot->texture : nullptr; +} + +void RendererModule::setViewport(int32 x, int32 y, int32 width, int32 height) { + viewportX_ = x; + viewportY_ = y; + viewportWidth_ = width; + viewportHeight_ = height; + glViewport(x, y, width, height); +} + +void RendererModule::clear(const Color &color, uint32_t flags) { + GLbitfield mask = 0; + + if (flags & CLEAR_COLOR_FLAG) { + glClearColor(color.r, color.g, color.b, color.a); + mask |= GL_COLOR_BUFFER_BIT; + } + + if (flags & CLEAR_DEPTH_FLAG) { + mask |= GL_DEPTH_BUFFER_BIT; + } + + if (flags & CLEAR_STENCIL_FLAG) { + mask |= GL_STENCIL_BUFFER_BIT; + } + + if (mask != 0) { + glClear(mask); + } +} + +void RendererModule::onRenderBegin() { + // 如果 GL 未初始化,跳过渲染 + if (!glInitialized_) { + return; + } + + // 清空命令缓冲区 + commandCount_ = 0; + + // 重置统计 + stats_ = {}; + + // 重置 UBO 管理器 + uniformManager_.resetMaterialUBOs(); +} + +void RendererModule::onRenderSubmit(const RenderCommand &cmd) { + // 如果 GL 未初始化,跳过提交 + if (!glInitialized_) { + return; + } + + // 检查缓冲区是否已满 + if (commandCount_ >= MAX_RENDER_COMMANDS) { + E2D_LOG_WARN("Render command buffer full!"); + return; + } + + // 直接存入预分配缓冲区(无动态分配) + commandBuffer_[commandCount_++] = cmd; + stats_.commandsSubmitted++; +} + +void RendererModule::onRenderEnd() { + // 如果 GL 未初始化,跳过渲染 + if (!glInitialized_) { + return; + } + + // 排序命令 + sortCommands(); + + // 批处理并绘制 + batchAndDraw(); + + // 输出统计 + E2D_LOG_DEBUG("Render: {} commands, {} draw calls, {} batches", + stats_.commandsExecuted, stats_.drawCalls, stats_.batches); +} + +void RendererModule::onResize(int32 width, int32 height) { + setViewport(0, 0, width, height); +} + +void RendererModule::sortCommands() { + // 使用 std::sort 对命令进行排序 + std::sort(commandBuffer_.begin(), commandBuffer_.begin() + commandCount_, + [](const RenderCommand &a, const RenderCommand &b) { + return a.sortKey < b.sortKey; + }); +} + +void RendererModule::batchAndDraw() { + MaterialHandle lastMaterial = INVALID_MATERIAL_HANDLE; + MeshHandle lastMesh = INVALID_MESH_HANDLE; + uint32_t batchStart = 0; + uint32_t batchCount = 0; + + for (uint32_t i = 0; i < commandCount_; ++i) { + const auto &cmd = commandBuffer_[i]; + + if (cmd.type != RenderCommandType::DrawMesh) { + // 处理非绘制命令 + if (batchCount > 0) { + drawBatch(batchStart, batchCount, lastMaterial, lastMesh); + stats_.batches++; + batchCount = 0; + } + executeCommand(cmd); + continue; + } + + // 检查是否需要刷新批次 + if (cmd.drawMesh.material != lastMaterial || + cmd.drawMesh.mesh != lastMesh) { + + // 刷新上一批次 + if (batchCount > 0) { + drawBatch(batchStart, batchCount, lastMaterial, lastMesh); + stats_.batches++; + } + + lastMaterial = cmd.drawMesh.material; + lastMesh = cmd.drawMesh.mesh; + batchStart = i; + batchCount = 1; + } else { + ++batchCount; + } + + stats_.commandsExecuted++; + } + + // 刷新最后一批 + if (batchCount > 0) { + drawBatch(batchStart, batchCount, lastMaterial, lastMesh); + stats_.batches++; + } +} + +void RendererModule::drawBatch(uint32_t start, uint32_t count, + MaterialHandle materialHandle, + MeshHandle meshHandle) { + auto material = getMaterial(materialHandle); + auto mesh = getMesh(meshHandle); + + if (!material || !mesh) + return; + + // 应用材质(绑定着色器、上传 UBO、绑定纹理) + material->apply(uniformManager_); + + // 绑定网格 + mesh->bind(); + + if (count == 1) { + // 单绘制 + mesh->draw(); + } else { + // 批量绘制 - 使用实例化渲染 + // 上传变换矩阵到 UBO + std::vector transforms; + transforms.reserve(count * 16); + + for (uint32_t i = 0; i < count; ++i) { + const auto &transform = commandBuffer_[start + i].drawMesh.transform; + // 将 Transform 转换为 4x4 矩阵并添加到 transforms + // 这里简化处理,实际需要完整的矩阵转换 + float matrix[16]; + transform.toMatrix(matrix); + transforms.insert(transforms.end(), matrix, matrix + 16); + } + + // 更新实例 UBO + auto *instanceUBO = uniformManager_.acquireMaterialUBO( + static_cast(transforms.size() * sizeof(float))); + if (instanceUBO) { + instanceUBO->update( + transforms.data(), + static_cast(transforms.size() * sizeof(float))); + instanceUBO->bind(INSTANCE_UBO_BINDING); + } + + // 实例化渲染 + mesh->drawInstanced(count); + } + + stats_.drawCalls++; +} + +void RendererModule::executeCommand(const RenderCommand &cmd) { + switch (cmd.type) { + case RenderCommandType::SetViewport: + setViewport(cmd.viewport.x, cmd.viewport.y, cmd.viewport.width, + cmd.viewport.height); + break; + + case RenderCommandType::Clear: + clear(cmd.clear.color, cmd.clear.flags); + break; + + default: + break; + } +} + +bool RendererModule::createDefaultResources() { + // 创建默认着色器 + auto defaultShader = makePtr(); + if (!defaultShader->loadFromFile("shader/default.vert", + "shader/default.frag")) { + E2D_LOG_ERROR("Failed to load default shader"); + return false; + } + + // 创建默认材质布局 + auto defaultLayout = makePtr(); + defaultLayout->addParam("tintColor", MaterialParamType::Color); + defaultLayout->addParam("opacity", MaterialParamType::Float); + defaultLayout->finalize(); + + // 创建默认材质 + auto defaultMaterial = makePtr(); + defaultMaterial->setLayout(defaultLayout); + defaultMaterial->setShader(defaultShader); + defaultMaterial->setColor("tintColor", Color::White); + defaultMaterial->setFloat("opacity", 1.0f); + + defaultMaterialHandle_ = registerMaterial(defaultMaterial); + + // 创建默认四边形 + auto defaultQuad = Mesh::createQuad(Vec2(1.0f, 1.0f)); + defaultQuadHandle_ = registerMesh(defaultQuad); + + // 创建默认纹理(1x1 白色) + auto defaultTexture = makePtr(); + uint8_t whitePixel[4] = {255, 255, 255, 255}; + defaultTexture->loadFromMemory(whitePixel, 1, 1, TextureFormat::RGBA8); + defaultTextureHandle_ = registerTexture(defaultTexture); + + E2D_LOG_INFO("Default resources created"); + return true; +} + +void RendererModule::destroyDefaultResources() { + if (defaultMaterialHandle_ != INVALID_MATERIAL_HANDLE) { + unregisterMaterial(defaultMaterialHandle_); + defaultMaterialHandle_ = INVALID_MATERIAL_HANDLE; + } + + if (defaultQuadHandle_ != INVALID_MESH_HANDLE) { + unregisterMesh(defaultQuadHandle_); + defaultQuadHandle_ = INVALID_MESH_HANDLE; + } + + if (defaultTextureHandle_ != INVALID_TEXTURE_HANDLE) { + unregisterTexture(defaultTextureHandle_); + defaultTextureHandle_ = INVALID_TEXTURE_HANDLE; + } +} + +} // namespace extra2d diff --git a/src/renderer/shader.cpp b/src/renderer/shader.cpp new file mode 100644 index 0000000..1dc0b8f --- /dev/null +++ b/src/renderer/shader.cpp @@ -0,0 +1,217 @@ +#include +#include +#include +#include +#include + +namespace extra2d { + +Shader::Shader() = default; + +Shader::~Shader() { + if (program_ != 0) { + glDeleteProgram(program_); + program_ = 0; + } +} + +bool Shader::loadFromFile(const std::string& vsPath, const std::string& fsPath) { + // 读取顶点着色器 + std::ifstream vsFile(vsPath); + if (!vsFile.is_open()) { + E2D_LOG_ERROR("Failed to open vertex shader: {}", vsPath); + return false; + } + std::stringstream vsStream; + vsStream << vsFile.rdbuf(); + std::string vsSource = vsStream.str(); + + // 读取片段着色器 + std::ifstream fsFile(fsPath); + if (!fsFile.is_open()) { + E2D_LOG_ERROR("Failed to open fragment shader: {}", fsPath); + return false; + } + std::stringstream fsStream; + fsStream << fsFile.rdbuf(); + std::string fsSource = fsStream.str(); + + return loadFromSource(vsSource, fsSource); +} + +bool Shader::loadFromSource(const std::string& vsSource, const std::string& fsSource) { + // 删除旧程序 + if (program_ != 0) { + glDeleteProgram(program_); + program_ = 0; + } + + uniformCache_.clear(); + + // 处理源码(添加版本声明) + std::string processedVS = addVersionIfNeeded(vsSource, GL_VERTEX_SHADER); + std::string processedFS = addVersionIfNeeded(fsSource, GL_FRAGMENT_SHADER); + + // 编译顶点着色器 + GLuint vs = compileShader(GL_VERTEX_SHADER, processedVS); + if (vs == 0) { + return false; + } + + // 编译片段着色器 + GLuint fs = compileShader(GL_FRAGMENT_SHADER, processedFS); + if (fs == 0) { + glDeleteShader(vs); + return false; + } + + // 链接程序 + if (!linkProgram(vs, fs)) { + glDeleteShader(vs); + glDeleteShader(fs); + return false; + } + + // 清理着色器对象 + glDeleteShader(vs); + glDeleteShader(fs); + + E2D_LOG_DEBUG("Shader program created successfully"); + return true; +} + +void Shader::bind() const { + if (program_ != 0) { + glUseProgram(program_); + } +} + +void Shader::unbind() const { + glUseProgram(0); +} + +void Shader::setUniformBlock(const std::string& name, uint32_t binding) { + if (program_ == 0) return; + + GLuint index = glGetUniformBlockIndex(program_, name.c_str()); + if (index != GL_INVALID_INDEX) { + glUniformBlockBinding(program_, index, binding); + } +} + +void Shader::setInt(const std::string& name, int value) { + GLint location = getUniformLocation(name); + if (location != -1) { + glUniform1i(location, value); + } +} + +void Shader::setFloat(const std::string& name, float value) { + GLint location = getUniformLocation(name); + if (location != -1) { + glUniform1f(location, value); + } +} + +void Shader::setVec2(const std::string& name, float x, float y) { + GLint location = getUniformLocation(name); + if (location != -1) { + glUniform2f(location, x, y); + } +} + +void Shader::setVec4(const std::string& name, float x, float y, float z, float w) { + GLint location = getUniformLocation(name); + if (location != -1) { + glUniform4f(location, x, y, z, w); + } +} + +void Shader::setMat4(const std::string& name, const float* value) { + GLint location = getUniformLocation(name); + if (location != -1) { + glUniformMatrix4fv(location, 1, GL_FALSE, value); + } +} + +GLuint Shader::compileShader(GLenum type, const std::string& source) { + GLuint shader = glCreateShader(type); + + const char* src = source.c_str(); + glShaderSource(shader, 1, &src, nullptr); + glCompileShader(shader); + + // 检查编译状态 + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + char infoLog[512]; + glGetShaderInfoLog(shader, 512, nullptr, infoLog); + + const char* typeStr = (type == GL_VERTEX_SHADER) ? "vertex" : "fragment"; + E2D_LOG_ERROR("{} shader compilation failed: {}", typeStr, infoLog); + + glDeleteShader(shader); + return 0; + } + + return shader; +} + +bool Shader::linkProgram(GLuint vertexShader, GLuint fragmentShader) { + program_ = glCreateProgram(); + glAttachShader(program_, vertexShader); + glAttachShader(program_, fragmentShader); + glLinkProgram(program_); + + // 检查链接状态 + GLint success; + glGetProgramiv(program_, GL_LINK_STATUS, &success); + if (!success) { + char infoLog[512]; + glGetProgramInfoLog(program_, 512, nullptr, infoLog); + E2D_LOG_ERROR("Shader program linking failed: {}", infoLog); + + glDeleteProgram(program_); + program_ = 0; + return false; + } + + return true; +} + +GLint Shader::getUniformLocation(const std::string& name) { + if (program_ == 0) return -1; + + // 检查缓存 + auto it = uniformCache_.find(name); + if (it != uniformCache_.end()) { + return it->second; + } + + // 查询 uniform 位置 + GLint location = glGetUniformLocation(program_, name.c_str()); + uniformCache_[name] = location; + + return location; +} + +std::string Shader::addVersionIfNeeded(const std::string& source, GLenum type) { + // 如果已经包含版本声明,直接返回 + if (source.find("#version") != std::string::npos) { + return source; + } + + // 添加 OpenGL ES 3.2 版本声明 + std::string result = "#version 320 es\n"; + + // 片段着色器需要添加精度声明 + if (type == GL_FRAGMENT_SHADER) { + result += "precision mediump float;\n"; + } + + result += source; + return result; +} + +} // namespace extra2d diff --git a/src/renderer/texture.cpp b/src/renderer/texture.cpp new file mode 100644 index 0000000..bcb5a45 --- /dev/null +++ b/src/renderer/texture.cpp @@ -0,0 +1,155 @@ +#include +#include +#include +#define STB_IMAGE_IMPLEMENTATION +#include + +namespace extra2d { + +// OpenGL 格式转换函数 +static GLint getTextureInternalFormat(TextureFormat format) { + switch (format) { + case TextureFormat::RGBA8: return GL_RGBA8; + case TextureFormat::RGB8: return GL_RGB8; + case TextureFormat::RGBA4: return GL_RGBA4; + case TextureFormat::R8: return GL_R8; + case TextureFormat::RG8: return GL_RG8; + default: return GL_RGBA8; + } +} + +static GLenum getTextureFormat(TextureFormat format) { + switch (format) { + case TextureFormat::RGBA8: + case TextureFormat::RGBA4: + return GL_RGBA; + case TextureFormat::RGB8: + return GL_RGB; + case TextureFormat::R8: + return GL_RED; + case TextureFormat::RG8: + return GL_RG; + default: + return GL_RGBA; + } +} + +Texture::Texture() = default; + +Texture::~Texture() { + if (texture_ != 0) { + glDeleteTextures(1, &texture_); + texture_ = 0; + } +} + +bool Texture::loadFromFile(const std::string& path) { + // 加载图片 + int channels; + stbi_set_flip_vertically_on_load(true); + unsigned char* data = stbi_load(path.c_str(), &width_, &height_, &channels, 0); + + if (!data) { + E2D_LOG_ERROR("Failed to load texture: {}", path); + return false; + } + + // 根据通道数确定格式 + TextureFormat format; + switch (channels) { + case 1: format = TextureFormat::R8; break; + case 2: format = TextureFormat::RG8; break; + case 3: format = TextureFormat::RGB8; break; + case 4: format = TextureFormat::RGBA8; break; + default: format = TextureFormat::RGBA8; break; + } + + bool result = loadFromMemory(data, width_, height_, format); + stbi_image_free(data); + + if (result) { + E2D_LOG_DEBUG("Texture loaded: {} ({}x{})", path, width_, height_); + } + + return result; +} + +bool Texture::loadFromMemory(const uint8_t* data, int width, int height, TextureFormat format) { + if (texture_ != 0) { + glDeleteTextures(1, &texture_); + texture_ = 0; + } + + width_ = width; + height_ = height; + format_ = format; + + // 创建纹理 + glGenTextures(1, &texture_); + glBindTexture(GL_TEXTURE_2D, texture_); + + // 设置纹理参数 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // 上传纹理数据 + glTexImage2D(GL_TEXTURE_2D, 0, + getTextureInternalFormat(format_), + width_, height_, 0, + getTextureFormat(format_), + GL_UNSIGNED_BYTE, data); + + // 生成 mipmap + glGenerateMipmap(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, 0); + + return true; +} + +bool Texture::create(int width, int height, TextureFormat format) { + if (texture_ != 0) { + glDeleteTextures(1, &texture_); + texture_ = 0; + } + + width_ = width; + height_ = height; + format_ = format; + + // 创建纹理 + glGenTextures(1, &texture_); + glBindTexture(GL_TEXTURE_2D, texture_); + + // 设置纹理参数 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // 分配纹理存储(无数据) + glTexImage2D(GL_TEXTURE_2D, 0, + getTextureInternalFormat(format_), + width_, height_, 0, + getTextureFormat(format_), + GL_UNSIGNED_BYTE, nullptr); + + glBindTexture(GL_TEXTURE_2D, 0); + + return true; +} + +void Texture::bind(uint32_t slot) const { + if (texture_ != 0) { + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, texture_); + } +} + +void Texture::unbind() const { + glBindTexture(GL_TEXTURE_2D, 0); +} + +} // namespace extra2d diff --git a/src/renderer/uniform_buffer.cpp b/src/renderer/uniform_buffer.cpp new file mode 100644 index 0000000..d10c923 --- /dev/null +++ b/src/renderer/uniform_buffer.cpp @@ -0,0 +1,148 @@ +#include +#include + +namespace extra2d { + +UniformBuffer::UniformBuffer() = default; + +UniformBuffer::~UniformBuffer() { + destroy(); +} + +bool UniformBuffer::create(uint32_t size, uint32_t binding) { + if (ubo_ != 0) { + destroy(); + } + + size_ = size; + binding_ = binding; + + glGenBuffers(1, &ubo_); + if (ubo_ == 0) { + E2D_LOG_ERROR("Failed to generate UBO"); + return false; + } + + glBindBuffer(GL_UNIFORM_BUFFER, ubo_); + glBufferData(GL_UNIFORM_BUFFER, size, nullptr, GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + E2D_LOG_DEBUG("UBO created: size={}, binding={}", size, binding); + return true; +} + +void UniformBuffer::destroy() { + if (ubo_ != 0) { + glDeleteBuffers(1, &ubo_); + ubo_ = 0; + size_ = 0; + } +} + +void UniformBuffer::update(const void* data, uint32_t size, uint32_t offset) { + if (ubo_ == 0 || data == nullptr) return; + + if (offset + size > size_) { + E2D_LOG_WARN("UBO update out of bounds: offset={}, size={}, bufferSize={}", + offset, size, size_); + return; + } + + glBindBuffer(GL_UNIFORM_BUFFER, ubo_); + glBufferSubData(GL_UNIFORM_BUFFER, offset, size, data); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +void UniformBuffer::bind(uint32_t binding) { + if (ubo_ == 0) return; + + glBindBufferBase(GL_UNIFORM_BUFFER, binding, ubo_); +} + +UniformBufferManager::UniformBufferManager() = default; + +UniformBufferManager::~UniformBufferManager() { + shutdown(); +} + +UniformBufferManager::UniformBufferManager(UniformBufferManager&& other) noexcept + : globalUBO_(std::move(other.globalUBO_)), + materialUBOPool_(std::move(other.materialUBOPool_)), + currentUBOIndex_(other.currentUBOIndex_) { + other.currentUBOIndex_ = 0; +} + +UniformBufferManager& UniformBufferManager::operator=(UniformBufferManager&& other) noexcept { + if (this != &other) { + shutdown(); + + globalUBO_ = std::move(other.globalUBO_); + materialUBOPool_ = std::move(other.materialUBOPool_); + currentUBOIndex_ = other.currentUBOIndex_; + + other.currentUBOIndex_ = 0; + } + return *this; +} + +bool UniformBufferManager::initialize() { + // 创建全局 UBO + globalUBO_ = std::make_unique(); + if (!globalUBO_->create(GLOBAL_UBO_SIZE, GLOBAL_UBO_BINDING)) { + E2D_LOG_ERROR("Failed to create global UBO"); + return false; + } + + // 预分配材质 UBO 池 + materialUBOPool_.reserve(INITIAL_UBO_POOL_SIZE); + + E2D_LOG_INFO("UniformBufferManager initialized"); + return true; +} + +void UniformBufferManager::shutdown() { + materialUBOPool_.clear(); + globalUBO_.reset(); + currentUBOIndex_ = 0; + + E2D_LOG_INFO("UniformBufferManager shutdown"); +} + +UniformBuffer* UniformBufferManager::getGlobalUBO() { + return globalUBO_.get(); +} + +UniformBuffer* UniformBufferManager::acquireMaterialUBO(uint32_t size) { + // 查找或创建合适大小的 UBO + for (uint32_t i = currentUBOIndex_; i < materialUBOPool_.size(); ++i) { + if (materialUBOPool_[i]->getSize() >= size) { + currentUBOIndex_ = i + 1; + return materialUBOPool_[i].get(); + } + } + + // 需要创建新的 UBO + auto ubo = std::make_unique(); + if (!ubo->create(size, MATERIAL_UBO_BINDING)) { + E2D_LOG_ERROR("Failed to create material UBO"); + return nullptr; + } + + UniformBuffer* result = ubo.get(); + materialUBOPool_.push_back(std::move(ubo)); + currentUBOIndex_ = materialUBOPool_.size(); + + return result; +} + +void UniformBufferManager::resetMaterialUBOs() { + currentUBOIndex_ = 0; +} + +void UniformBufferManager::updateGlobalUBO(const void* data, uint32_t size) { + if (globalUBO_) { + globalUBO_->update(data, size); + } +} + +} // namespace extra2d diff --git a/xmake.lua b/xmake.lua index e6ff1d2..a4e19a6 100644 --- a/xmake.lua +++ b/xmake.lua @@ -94,7 +94,6 @@ define_extra2d_engine() -- 示例程序目标(作为子项目) if is_config("examples","true") then includes("examples/hello_world", {rootdir = "examples/hello_world"}) - includes("examples/resource_loading", {rootdir = "examples/resource_loading"}) end