From 90418334307b7c52dbed5cdeb6a4a7a772fd7a69 Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Tue, 3 Mar 2026 02:16:29 +0800 Subject: [PATCH] =?UTF-8?q?refactor(rhi):=20=E9=87=8D=E6=9E=84=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E7=A1=AC=E4=BB=B6=E6=8E=A5=E5=8F=A3=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat(rhi): 新增RHI模块抽象层 feat(opengl): 实现OpenGL后端支持 refactor(renderer): 重构材质、纹理和网格类以使用RHI接口 refactor(scene): 移除移动构造函数并禁止移动操作 fix(shader): 修复默认片段着色器纹理采样问题 refactor(module): 改进模块注册表以支持实例存储 refactor(window): 移除OpenGL上下文管理交由RHI处理 feat(assets): 为默认材质添加纹理支持 refactor(timer): 移除移动操作并简化实现 refactor(input): 移除移动操作并简化实现 --- .claude/settings.local.json | 7 + examples/scene_graph_demo/game_scene.cpp | 8 +- include/module/module_registry.h | 37 ++ include/platform/file_module.h | 160 ------ include/platform/input_module.h | 8 +- include/platform/window_module.h | 31 +- include/renderer/command_queue.h | 346 +++++++++++++ include/renderer/material.h | 74 ++- include/renderer/mesh.h | 154 +++--- include/renderer/render_graph.h | 365 +++++++++++++ include/renderer/render_types.h | 253 ++++----- include/renderer/renderer_module.h | 134 ++--- include/renderer/rhi/opengl/gl_buffer.h | 35 ++ include/renderer/rhi/opengl/gl_command_list.h | 99 ++++ include/renderer/rhi/opengl/gl_context.h | 38 ++ include/renderer/rhi/opengl/gl_framebuffer.h | 53 ++ include/renderer/rhi/opengl/gl_pipeline.h | 45 ++ include/renderer/rhi/opengl/gl_shader.h | 45 ++ include/renderer/rhi/opengl/gl_texture.h | 40 ++ include/renderer/rhi/opengl/gl_utils.h | 307 +++++++++++ include/renderer/rhi/rhi.h | 14 + include/renderer/rhi/rhi_buffer.h | 131 +++++ include/renderer/rhi/rhi_command_list.h | 184 +++++++ include/renderer/rhi/rhi_device.h | 169 ++++++ include/renderer/rhi/rhi_framebuffer.h | 76 +++ include/renderer/rhi/rhi_shader.h | 86 ++++ include/renderer/rhi/rhi_types.h | 480 ++++++++++++++++++ include/renderer/rhi_module.h | 113 +++++ include/renderer/shader.h | 210 +++----- include/renderer/texture.h | 34 +- include/renderer/uniform_buffer.h | 220 ++++---- include/scene/scene_module.h | 8 +- include/utils/timer_module.h | 8 +- shader/default.frag | 8 +- src/app/application.cpp | 5 + src/assets/assets_module.cpp | 4 +- src/module/module_registry.cpp | 19 +- src/platform/file_module.cpp | 256 ---------- src/platform/input_module.cpp | 58 --- src/platform/window_module.cpp | 95 +--- src/renderer/command_queue.cpp | 435 ++++++++++++++++ src/renderer/material.cpp | 44 +- src/renderer/mesh.cpp | 286 +++++------ src/renderer/render_graph.cpp | 334 ++++++++++++ src/renderer/renderer_module.cpp | 432 ++++++---------- src/renderer/rhi/opengl/gl_buffer.cpp | 65 +++ src/renderer/rhi/opengl/gl_command_list.cpp | 261 ++++++++++ src/renderer/rhi/opengl/gl_context.cpp | 84 +++ src/renderer/rhi/opengl/gl_device.cpp | 200 ++++++++ src/renderer/rhi/opengl/gl_framebuffer.cpp | 151 ++++++ src/renderer/rhi/opengl/gl_pipeline.cpp | 115 +++++ src/renderer/rhi/opengl/gl_shader.cpp | 137 +++++ src/renderer/rhi/opengl/gl_texture.cpp | 120 +++++ src/renderer/rhi_module.cpp | 352 +++++++++++++ src/renderer/shader.cpp | 216 +++----- src/renderer/texture.cpp | 163 +++--- src/renderer/uniform_buffer.cpp | 228 +++++---- src/scene/components/sprite_renderer.cpp | 5 +- src/scene/scene_module.cpp | 4 - src/utils/timer_module.cpp | 18 - 60 files changed, 6056 insertions(+), 2011 deletions(-) create mode 100644 .claude/settings.local.json delete mode 100644 include/platform/file_module.h create mode 100644 include/renderer/command_queue.h create mode 100644 include/renderer/render_graph.h create mode 100644 include/renderer/rhi/opengl/gl_buffer.h create mode 100644 include/renderer/rhi/opengl/gl_command_list.h create mode 100644 include/renderer/rhi/opengl/gl_context.h create mode 100644 include/renderer/rhi/opengl/gl_framebuffer.h create mode 100644 include/renderer/rhi/opengl/gl_pipeline.h create mode 100644 include/renderer/rhi/opengl/gl_shader.h create mode 100644 include/renderer/rhi/opengl/gl_texture.h create mode 100644 include/renderer/rhi/opengl/gl_utils.h create mode 100644 include/renderer/rhi/rhi.h create mode 100644 include/renderer/rhi/rhi_buffer.h create mode 100644 include/renderer/rhi/rhi_command_list.h create mode 100644 include/renderer/rhi/rhi_device.h create mode 100644 include/renderer/rhi/rhi_framebuffer.h create mode 100644 include/renderer/rhi/rhi_shader.h create mode 100644 include/renderer/rhi/rhi_types.h create mode 100644 include/renderer/rhi_module.h delete mode 100644 src/platform/file_module.cpp create mode 100644 src/renderer/command_queue.cpp create mode 100644 src/renderer/render_graph.cpp create mode 100644 src/renderer/rhi/opengl/gl_buffer.cpp create mode 100644 src/renderer/rhi/opengl/gl_command_list.cpp create mode 100644 src/renderer/rhi/opengl/gl_context.cpp create mode 100644 src/renderer/rhi/opengl/gl_device.cpp create mode 100644 src/renderer/rhi/opengl/gl_framebuffer.cpp create mode 100644 src/renderer/rhi/opengl/gl_pipeline.cpp create mode 100644 src/renderer/rhi/opengl/gl_shader.cpp create mode 100644 src/renderer/rhi/opengl/gl_texture.cpp create mode 100644 src/renderer/rhi_module.cpp diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..25655fa --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "Bash(git diff:*)" + ] + } +} diff --git a/examples/scene_graph_demo/game_scene.cpp b/examples/scene_graph_demo/game_scene.cpp index 8889581..2fae529 100644 --- a/examples/scene_graph_demo/game_scene.cpp +++ b/examples/scene_graph_demo/game_scene.cpp @@ -31,8 +31,8 @@ void PlayerNode::onUpdate(float dt) { setPosition(x, y); - // 根据移动方向旋转 - float angle = time_ * 0.5f * 57.2958f + 90.0f; + // 根据移动方向旋转(顺时针,使用负角度) + float angle = -time_ * 0.5f * 57.2958f + 90.0f; setRotation(angle); } @@ -56,8 +56,8 @@ RotatingDecoration::RotatingDecoration() { } void RotatingDecoration::onUpdate(float dt) { - // 自转 - setRotation(getRotation() + rotationSpeed_ * dt); + // 自转(顺时针,使用负角度) + setRotation(getRotation() - rotationSpeed_ * dt); } // ======================================== diff --git a/include/module/module_registry.h b/include/module/module_registry.h index 286edc1..50e8988 100644 --- a/include/module/module_registry.h +++ b/include/module/module_registry.h @@ -5,6 +5,7 @@ #include #include #include +#include #include namespace extra2d { @@ -67,9 +68,35 @@ public: return registrations_; } + /** + * @brief 存储模块实例 + * @param module 模块实例指针 + */ + void storeModuleInstance(Module* module); + + /** + * @brief 获取模块实例 + * @tparam T 模块类型 + * @return 模块实例指针,未找到返回 nullptr + */ + template + T* getModule() { + auto it = instances_.find(std::type_index(typeid(T))); + if (it != instances_.end()) { + return static_cast(it->second); + } + return nullptr; + } + + /** + * @brief 清除所有存储的模块实例 + */ + void clearInstances(); + private: ModuleRegistry() = default; std::vector registrations_; + std::unordered_map instances_; }; /** @@ -108,4 +135,14 @@ public: \ #define E2D_REGISTER_MODULE_SIMPLE(ClassName) \ E2D_REGISTER_MODULE(ClassName, #ClassName, 100) +/** + * @brief 获取模块实例的辅助函数 + * @tparam T 模块类型 + * @return 模块实例指针 + */ +template +T* getModule() { + return ModuleRegistry::instance().getModule(); +} + } // namespace extra2d diff --git a/include/platform/file_module.h b/include/platform/file_module.h deleted file mode 100644 index 7417b11..0000000 --- a/include/platform/file_module.h +++ /dev/null @@ -1,160 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace extra2d { - -/** - * @brief 文件信息 - */ -struct FileInfo { - std::string path; - std::string name; - bool isDir = false; - int64 size = 0; -}; - -/** - * @brief 文件读取结果 - */ -struct FileData { - bool ok = false; - std::vector data; - std::string error; - - operator bool() const { return ok; } - const uint8* ptr() const { return data.data(); } - size_t size() const { return data.size(); } -}; - -/** - * @brief 文件模块 - * - * 提供跨平台文件系统操作 - * 非单例设计,通过 Context 管理生命周期 - */ -class FileModule : public Module { - // 自动注册到模块系统,优先级为 20(系统模块) - E2D_REGISTER_MODULE(FileModule, "File", 20) - -public: - FileModule(); - ~FileModule() override; - - // 禁止拷贝 - FileModule(const FileModule&) = delete; - FileModule& operator=(const FileModule&) = delete; - - // 允许移动 - FileModule(FileModule&&) noexcept; - FileModule& operator=(FileModule&&) noexcept; - - // Module 接口实现 - bool init() override; - void shutdown() override; - - /** - * @brief 检查文件是否存在 - */ - bool exists(const std::string& path) const; - - /** - * @brief 检查是否为目录 - */ - bool isDir(const std::string& path) const; - - /** - * @brief 读取整个文件到内存 - */ - FileData read(const std::string& path) const; - - /** - * @brief 读取文件为字符串 - */ - std::string readString(const std::string& path) const; - - /** - * @brief 写入数据到文件 - */ - bool write(const std::string& path, const void* data, size_t size) const; - - /** - * @brief 写入字符串到文件 - */ - bool writeString(const std::string& path, const std::string& content) const; - - /** - * @brief 追加数据到文件 - */ - bool append(const std::string& path, const void* data, size_t size) const; - - /** - * @brief 删除文件 - */ - bool remove(const std::string& path) const; - - /** - * @brief 创建目录 - */ - bool mkdir(const std::string& path) const; - - /** - * @brief 列出目录内容 - */ - std::vector listDir(const std::string& path) const; - - /** - * @brief 获取文件大小 - */ - int64 fileSize(const std::string& path) const; - - /** - * @brief 获取文件扩展名 - */ - std::string ext(const std::string& path) const; - - /** - * @brief 获取文件名(不含路径) - */ - std::string fileName(const std::string& path) const; - - /** - * @brief 获取文件所在目录 - */ - std::string dirName(const std::string& path) const; - - /** - * @brief 连接路径 - */ - std::string join(const std::string& a, const std::string& b) const; - - /** - * @brief 获取可写目录(用于存档等) - */ - std::string writableDir() const; - - /** - * @brief 设置资源根目录 - */ - void setAssetRoot(const std::string& root) { assetRoot_ = root; } - - /** - * @brief 获取资源根目录 - */ - const std::string& assetRoot() const { return assetRoot_; } - - /** - * @brief 获取资源完整路径 - */ - std::string assetPath(const std::string& relPath) const; - -private: - std::string assetRoot_; - std::string writableDir_; -}; - -} // namespace extra2d diff --git a/include/platform/input_module.h b/include/platform/input_module.h index 439eadf..6f6007b 100644 --- a/include/platform/input_module.h +++ b/include/platform/input_module.h @@ -179,13 +179,11 @@ public: InputModule(); ~InputModule() override; - // 禁止拷贝 + // 禁止拷贝和移动 InputModule(const InputModule &) = delete; InputModule &operator=(const InputModule &) = delete; - - // 允许移动 - InputModule(InputModule &&) noexcept; - InputModule &operator=(InputModule &&) noexcept; + InputModule(InputModule &&) = delete; + InputModule &operator=(InputModule &&) = delete; // Module 接口实现 bool init() override; diff --git a/include/platform/window_module.h b/include/platform/window_module.h index d155b90..7f9d781 100644 --- a/include/platform/window_module.h +++ b/include/platform/window_module.h @@ -33,13 +33,11 @@ public: WindowModule(); ~WindowModule() override; - // 禁止拷贝 + // 禁止拷贝和移动 WindowModule(const WindowModule &) = delete; WindowModule &operator=(const WindowModule &) = delete; - - // 允许移动 - WindowModule(WindowModule &&) noexcept; - WindowModule &operator=(WindowModule &&) noexcept; + WindowModule(WindowModule &&) = delete; + WindowModule &operator=(WindowModule &&) = delete; // Module 接口实现 bool init() override; @@ -121,28 +119,6 @@ public: */ bool shouldClose() const { return shouldClose_; } - /** - * @brief 创建 OpenGL ES 上下文 - * @return 是否成功 - */ - bool createGLContext(); - - /** - * @brief 销毁 OpenGL ES 上下文 - */ - void destroyGLContext(); - - /** - * @brief 交换缓冲区(呈现渲染结果) - */ - void swapBuffers(); - - /** - * @brief 获取 OpenGL ES 上下文句柄 - * @return SDL_GLContext 句柄 - */ - void* getGLContext() const; - private: void handleWindowEvent(const SDL_WindowEvent &evt); @@ -162,7 +138,6 @@ private: void onRenderPresent(); SDL_Window *window_ = nullptr; - SDL_GLContext glContext_ = nullptr; bool shouldClose_ = false; CloseCb onClose_; diff --git a/include/renderer/command_queue.h b/include/renderer/command_queue.h new file mode 100644 index 0000000..14813ad --- /dev/null +++ b/include/renderer/command_queue.h @@ -0,0 +1,346 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// 前向声明 +class CommandQueue; +class Material; +class Mesh; +template class Ptr; + +/** + * @brief 绘制命令键 + * + * 64位排序键,用于高效排序绘制命令 + * 布局:| 材质ID (32位) | 深度 (16位) | 层 (8位) | 标志 (8位) | + */ +struct DrawKey { + uint64_t value; + + DrawKey() : value(0) {} + explicit DrawKey(uint64_t v) : value(v) {} + + // 构建排序键 + static DrawKey make(uint32_t materialId, uint16_t depth, uint8_t layer, + uint8_t flags = 0) { + DrawKey key; + key.value = (static_cast(materialId) << 32) | + (static_cast(depth) << 16) | + (static_cast(layer) << 8) | + static_cast(flags); + return key; + } + + uint32_t getMaterialId() const { return static_cast(value >> 32); } + uint16_t getDepth() const { + return static_cast((value >> 16) & 0xFFFF); + } + uint8_t getLayer() const { return static_cast((value >> 8) & 0xFF); } + uint8_t getFlags() const { return static_cast(value & 0xFF); } + + bool operator<(const DrawKey &other) const { return value < other.value; } + bool operator>(const DrawKey &other) const { return value > other.value; } +}; + +/** + * @brief 绘制命令 + * + * 封装一次绘制调用的所有信息 + */ +struct DrawCommand { + DrawKey key; // 排序键 + PipelineHandle pipeline; // 管线状态 + BufferHandle vertexBuffer; // 顶点缓冲区 + BufferHandle indexBuffer; // 索引缓冲区(可选) + uint32_t vertexCount; // 顶点数量 + uint32_t indexCount; // 索引数量 + uint32_t instanceCount; // 实例数量(1表示非实例化) + std::array textures; // 纹理数组 + uint32_t textureCount; // 纹理数量 + BufferHandle materialUBO; // 材质 UBO + uint32_t materialUBOSize; // 材质 UBO 大小 + + // 变换和颜色数据(用于设置 shader uniform) + Mat4 modelMatrix; // 模型矩阵 + Color color; // 颜色 + + DrawCommand() + : vertexCount(0), indexCount(0), instanceCount(1), textureCount(0), + materialUBOSize(0), modelMatrix(glm::identity()), color(Color::White) {} + + // 检查是否使用索引绘制 + bool isIndexed() const { return indexCount > 0; } + + // 检查是否实例化绘制 + bool isInstanced() const { return instanceCount > 1; } +}; + +/** + * @brief 命令批次 + * + * 合并具有相同管线和纹理的绘制命令 + */ +struct CommandBatch { + PipelineHandle pipeline; // 共享管线 + std::array textures; // 共享纹理 + uint32_t textureCount; + uint32_t startIndex; // 批次起始命令索引 + uint32_t count; // 批次命令数量 + + CommandBatch() : textureCount(0), startIndex(0), count(0) {} + + // 检查是否与命令兼容 + bool isCompatibleWith(const DrawCommand &cmd) const { + if (pipeline != cmd.pipeline) + return false; + if (textureCount != cmd.textureCount) + return false; + for (uint32_t i = 0; i < textureCount; ++i) { + if (textures[i] != cmd.textures[i]) + return false; + } + return true; + } +}; + +/** + * @brief 命令排序器 + * + * 对绘制命令进行排序以优化渲染性能 + * 排序优先级:材质(管线)> 深度 > 层 + */ +class CommandSorter { +public: + /** + * @brief 添加绘制命令 + * @param cmd 绘制命令 + * @return 命令索引 + */ + uint32_t addCommand(const DrawCommand &cmd); + + /** + * @brief 排序所有命令 + */ + void sort(); + + /** + * @brief 获取排序后的命令 + * @param index 命令索引 + * @return 命令引用 + */ + const DrawCommand &getCommand(uint32_t index) const { + return commands_[sortedIndices_[index]]; + } + + /** + * @brief 获取命令数量 + * @return 命令数量 + */ + uint32_t getCount() const { return static_cast(commands_.size()); } + + /** + * @brief 清空所有命令 + */ + void clear(); + +private: + std::vector commands_; + std::vector sortedIndices_; +}; + +/** + * @brief 命令批处理器 + * + * 将连续的兼容命令合并为批次 + * 减少状态切换开销 + */ +class CommandBatcher { +public: + /** + * @brief 处理命令列表,生成批次 + * @param sorter 已排序的命令排序器 + */ + void process(const CommandSorter &sorter); + + /** + * @brief 获取批次数量 + * @return 批次数量 + */ + uint32_t getBatchCount() const { + return static_cast(batches_.size()); + } + + /** + * @brief 获取批次 + * @param index 批次索引 + * @return 批次引用 + */ + const CommandBatch &getBatch(uint32_t index) const { return batches_[index]; } + + /** + * @brief 获取批次中的命令 + * @param batchIndex 批次索引 + * @param cmdIndex 命令在批次中的索引 + * @return 命令引用 + */ + const DrawCommand &getCommand(uint32_t batchIndex, uint32_t cmdIndex) const; + + /** + * @brief 清空所有批次 + */ + void clear(); + +private: + std::vector batches_; + const CommandSorter *sorter_ = nullptr; +}; + +/** + * @brief 命令队列 + * + * 管理一帧的所有渲染命令 + * 提供提交、排序、批处理和执行功能 + */ +class CommandQueue { +public: + /** + * @brief 默认构造函数 + */ + CommandQueue(); + + /** + * @brief 析构函数 + */ + ~CommandQueue(); + + // 禁止拷贝 + CommandQueue(const CommandQueue&) = delete; + CommandQueue& operator=(const CommandQueue&) = delete; + + // 允许移动 + CommandQueue(CommandQueue&&) noexcept; + CommandQueue& operator=(CommandQueue&&) noexcept; + + /** + * @brief 初始化命令队列 + * @return 初始化是否成功 + */ + bool initialize(); + + /** + * @brief 关闭命令队列 + */ + void shutdown(); + + /** + * @brief 开始一帧的录制 + */ + void beginFrame(); + + /** + * @brief 结束一帧的录制 + */ + void endFrame(); + + /** + * @brief 提交绘制命令 + * @param material 材质 + * @param mesh 网格 + * @param transform 变换 + * @param color 颜色 + */ + void submitDraw(Ptr material, Ptr mesh, + const struct Transform &transform, const Color &color); + + /** + * @brief 提交实例化绘制命令 + * @param material 材质 + * @param mesh 网格 + * @param instanceCount 实例数量 + */ + void submitDrawInstanced(Ptr material, Ptr mesh, + uint32_t instanceCount); + + /** + * @brief 提交清除命令 + * @param color 清除颜色 + * @param flags 清除标志 + */ + void submitClear(const Color &color, uint32_t flags); + + /** + * @brief 设置视口 + * @param x 视口 X + * @param y 视口 Y + * @param width 视口宽度 + * @param height 视口高度 + */ + void setViewport(int32_t x, int32_t y, int32_t width, int32_t height); + + /** + * @brief 执行所有命令 + * + * 排序、批处理并执行所有提交的命令 + */ + void execute(); + + /** + * @brief 获取当前命令数量 + * @return 命令数量 + */ + uint32_t getCommandCount() const { return sorter_.getCount(); } + + /** + * @brief 获取命令列表 + * @return 命令列表指针 + */ + RHICommandList* getCommandList() const { return commandList_.get(); } + +private: + CommandSorter sorter_; + CommandBatcher batcher_; + RHIContext *context_ = nullptr; + std::unique_ptr commandList_; + + // 全局 UBO 数据 + struct GlobalUBOData { + float viewProjection[16]; + float time; + float screenSize[2]; + float padding; + } globalUBOData_; + + // 材质 UBO 数据缓冲区 + std::vector materialUBOData_; + + // 当前材质 ID 计数器 + uint32_t nextMaterialId_ = 1; + + // 材质到 ID 的映射 + std::unordered_map materialIds_; + + /** + * @brief 获取或创建材质 ID + * @param material 材质指针 + * @return 材质 ID + */ + uint32_t getMaterialId(Material *material); + + /** + * @brief 执行单个批次 + * @param batchIndex 批次索引 + * @param batch 命令批次 + */ + void executeBatch(uint32_t batchIndex, const CommandBatch &batch); +}; + +} // namespace extra2d diff --git a/include/renderer/material.h b/include/renderer/material.h index 7f4daa1..e384555 100644 --- a/include/renderer/material.h +++ b/include/renderer/material.h @@ -1,12 +1,12 @@ #pragma once +#include +#include +#include #include #include #include #include -#include -#include -#include #include #include #include @@ -15,7 +15,37 @@ namespace extra2d { // 前向声明 class Material; -class Texture; + +/** + * @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(uint32_t); + default: return 0; + } +} /** * @brief 材质参数信息 @@ -30,7 +60,7 @@ struct MaterialParamInfo { * @brief 纹理槽位信息 */ struct TextureSlot { - Handle handle; + Ptr texture; uint32_t slot; std::string uniformName; }; @@ -87,7 +117,7 @@ private: /** * @brief 材质类 * - * 作为着色器和纹理的中间层,管理: + * 基于 RHI 的材质包装类,管理: * - 着色器程序 * - 材质参数(通过 UBO 上传) * - 纹理绑定 @@ -112,10 +142,7 @@ private: * material->setFloat("uOpacity", 1.0f); * * // 设置纹理 - * material->setTexture("uTexture", textureHandle, 0); - * - * // 注册到渲染器 - * MaterialHandle handle = renderer->registerMaterial(material); + * material->setTexture("uTexture", texture, 0); * @endcode */ class Material : public RefCounted { @@ -147,6 +174,12 @@ public: */ Ptr getShader() const { return shader_; } + /** + * @brief 获取 RHI 管线句柄 + * @return RHI 管线句柄 + */ + PipelineHandle getPipeline() const; + // ======================================== // 参数设置 // ======================================== @@ -168,7 +201,10 @@ public: /** * @brief 设置 vec4 参数 * @param name 参数名称 - * @param value 值 + * @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); @@ -193,10 +229,10 @@ public: /** * @brief 设置纹理 * @param uniformName 着色器中的采样器 uniform 名称 - * @param texture 纹理句柄 + * @param texture 纹理 * @param slot 纹理槽位(0-15) */ - void setTexture(const std::string& uniformName, Handle texture, uint32_t slot); + void setTexture(const std::string& uniformName, Ptr texture, uint32_t slot); /** * @brief 获取所有纹理槽位 @@ -225,17 +261,11 @@ public: */ uint32_t getDataSize() const { return static_cast(data_.size()); } - // ======================================== - // 应用材质(渲染时调用) - // ======================================== - /** - * @brief 应用材质 - * - * 绑定着色器、上传 UBO 数据 - * @param uboManager UBO 管理器 + * @brief 获取材质布局 + * @return 材质布局 */ - void apply(UniformBufferManager& uboManager); + Ptr getLayout() const { return layout_; } private: Ptr layout_; // 材质布局 diff --git a/include/renderer/mesh.h b/include/renderer/mesh.h index 4ec93f1..b5ff18c 100644 --- a/include/renderer/mesh.h +++ b/include/renderer/mesh.h @@ -1,119 +1,113 @@ #pragma once +#include #include #include #include #include #include -// 前向声明 OpenGL 类型 -typedef unsigned int GLuint; - namespace extra2d { /** * @brief 顶点结构 */ struct Vertex { - Vec2 position; // 位置 - Vec2 texCoord; // 纹理坐标 - Color color; // 颜色 + Vec2 position; // 位置 + Vec2 texCoord; // 纹理坐标 + Color color; // 颜色 - Vertex() = default; - Vertex(const Vec2 &pos, const Vec2 &uv, const Color &col) - : position(pos), texCoord(uv), color(col) {} + Vertex() = default; + Vertex(const Vec2& pos, const Vec2& uv, const Color& col) + : position(pos), texCoord(uv), color(col) {} }; /** * @brief 网格类 * - * 管理 OpenGL 顶点数组对象和缓冲区 + * 基于 RHI 的网格包装类,管理顶点缓冲区和索引缓冲区 * 支持静态和动态顶点数据 */ class Mesh : public RefCounted { public: - /** - * @brief 默认构造函数 - */ - Mesh(); + /** + * @brief 默认构造函数 + */ + Mesh(); - /** - * @brief 析构函数 - */ - ~Mesh() override; + /** + * @brief 析构函数 + */ + ~Mesh() override; - /** - * @brief 设置顶点数据 - * @param vertices 顶点数组 - * @param count 顶点数量 - */ - void setVertices(const Vertex *vertices, uint32_t count); + /** + * @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 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 更新部分顶点数据 + * @param vertices 顶点数据 + * @param count 顶点数量 + * @param offset 偏移量 + */ + void updateVertices(const Vertex* vertices, uint32_t count, uint32_t offset); - /** - * @brief 绑定网格 - */ - void bind() const; + /** + * @brief 获取顶点数量 + * @return 顶点数量 + */ + uint32_t getVertexCount() const { return vertexCount_; } - /** - * @brief 解绑网格 - */ - void unbind() const; + /** + * @brief 获取索引数量 + * @return 索引数量 + */ + uint32_t getIndexCount() const { return indexCount_; } - /** - * @brief 绘制网格 - */ - void draw() const; + /** + * @brief 获取顶点缓冲区句柄 + * @return 顶点缓冲区句柄 + */ + BufferHandle getVertexBuffer() const { return vertexBuffer_; } - /** - * @brief 实例化绘制 - * @param instanceCount 实例数量 - */ - void drawInstanced(uint32_t instanceCount) const; + /** + * @brief 获取索引缓冲区句柄 + * @return 索引缓冲区句柄 + */ + BufferHandle getIndexBuffer() const { return indexBuffer_; } - /** - * @brief 获取顶点数量 - * @return 顶点数量 - */ - uint32_t getVertexCount() const { return vertexCount_; } + /** + * @brief 检查是否有索引数据 + * @return 是否有索引数据 + */ + bool hasIndices() const { return indexCount_ > 0; } - /** - * @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)); + /** + * @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; // 索引缓冲区容量 + BufferHandle vertexBuffer_; // 顶点缓冲区句柄 + BufferHandle indexBuffer_; // 索引缓冲区句柄 + uint32_t vertexCount_ = 0; // 顶点数量 + uint32_t indexCount_ = 0; // 索引数量 + uint32_t vertexCapacity_ = 0; // 顶点缓冲区容量 + uint32_t indexCapacity_ = 0; // 索引缓冲区容量 }; } // namespace extra2d diff --git a/include/renderer/render_graph.h b/include/renderer/render_graph.h new file mode 100644 index 0000000..5a736c1 --- /dev/null +++ b/include/renderer/render_graph.h @@ -0,0 +1,365 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// 前向声明 +class RenderGraph; +class RenderPass; +class RenderGraphBuilder; + +/** + * @brief 渲染图资源类型 + */ +enum class RenderGraphResourceType : uint8_t { + Texture, // 纹理 + Buffer, // 缓冲区 + RenderTarget // 渲染目标 +}; + +/** + * @brief 渲染图资源句柄 + */ +struct RenderGraphResourceHandle { + uint32_t index = static_cast(-1); + RenderGraphResourceType type = RenderGraphResourceType::Texture; + + bool isValid() const { return index != static_cast(-1); } + static RenderGraphResourceHandle invalid() { return RenderGraphResourceHandle{}; } +}; + +/** + * @brief 纹理描述 + */ +struct RenderGraphTextureDesc { + uint32_t width = 0; + uint32_t height = 0; + TextureFormat format = TextureFormat::RGBA8; + TextureUsage usage = TextureUsage::Sampled; + bool isRenderTarget = false; + std::string name; + + static RenderGraphTextureDesc create2D(uint32_t w, uint32_t h, TextureFormat fmt, + const std::string& name = "") { + RenderGraphTextureDesc desc; + desc.width = w; + desc.height = h; + desc.format = fmt; + desc.name = name; + return desc; + } +}; + +/** + * @brief 渲染目标描述 + */ +struct RenderGraphRenderTargetDesc { + RenderGraphResourceHandle colorTarget; + RenderGraphResourceHandle depthTarget; + Rect clearRect; + Color clearColor = Color::Black; + float clearDepth = 1.0f; + uint8_t clearStencil = 0; + bool clearColorFlag = true; + bool clearDepthFlag = false; + bool clearStencilFlag = false; +}; + +/** + * @brief 渲染通道类型 + */ +enum class RenderPassType : uint8_t { + Graphics, // 图形渲染 + Compute, // 计算 + Copy // 拷贝/传输 +}; + +/** + * @brief 渲染通道执行上下文 + */ +struct RenderPassContext { + RHIContext* context = nullptr; + CommandQueue* commandQueue = nullptr; + RenderGraph* graph = nullptr; + float deltaTime = 0.0f; + uint32_t frameIndex = 0; + + // 获取纹理句柄 + TextureHandle getTexture(RenderGraphResourceHandle handle) const; +}; + +/** + * @brief 渲染通道基类 + */ +class RenderPass { +public: + virtual ~RenderPass() = default; + + /** + * @brief 获取通道名称 + */ + virtual const char* getName() const = 0; + + /** + * @brief 获取通道类型 + */ + virtual RenderPassType getType() const { return RenderPassType::Graphics; } + + /** + * @brief 声明资源依赖 + * @param builder 渲染图构建器 + */ + virtual void declareResources(RenderGraphBuilder& builder) {} + + /** + * @brief 执行渲染通道 + * @param ctx 执行上下文 + */ + virtual void execute(const RenderPassContext& ctx) = 0; + + /** + * @brief 设置是否启用 + */ + void setEnabled(bool enabled) { enabled_ = enabled; } + + /** + * @brief 检查是否启用 + */ + bool isEnabled() const { return enabled_; } + +private: + bool enabled_ = true; +}; + +/** + * @brief 渲染图构建器 + * + * 用于声明渲染通道的资源依赖 + */ +class RenderGraphBuilder { +public: + /** + * @brief 创建纹理 + * @param desc 纹理描述 + * @return 资源句柄 + */ + RenderGraphResourceHandle createTexture(const RenderGraphTextureDesc& desc); + + /** + * @brief 读取资源 + * @param handle 资源句柄 + */ + void readResource(RenderGraphResourceHandle handle); + + /** + * @brief 写入资源 + * @param handle 资源句柄 + */ + void writeResource(RenderGraphResourceHandle handle); + + /** + * @brief 设置渲染目标 + * @param desc 渲染目标描述 + */ + void setRenderTarget(const RenderGraphRenderTargetDesc& desc); + + // 内部使用 + void setCurrentPass(RenderPass* pass) { currentPass_ = pass; } + +private: + RenderPass* currentPass_ = nullptr; + std::vector textureDescs_; +}; + +/** + * @brief 渲染图 + * + * 管理渲染通道的依赖关系和执行顺序 + * 提供声明式渲染流程定义 + */ +class RenderGraph { +public: + RenderGraph(); + ~RenderGraph(); + + // 禁止拷贝 + RenderGraph(const RenderGraph&) = delete; + RenderGraph& operator=(const RenderGraph&) = delete; + + // 允许移动 + RenderGraph(RenderGraph&&) noexcept; + RenderGraph& operator=(RenderGraph&&) noexcept; + + /** + * @brief 初始化渲染图 + * @return 初始化是否成功 + */ + bool initialize(); + + /** + * @brief 关闭渲染图 + */ + void shutdown(); + + /** + * @brief 添加渲染通道 + * @param pass 渲染通道(所有权转移) + * @return 渲染通道指针 + */ + RenderPass* addPass(std::unique_ptr pass); + + /** + * @brief 创建渲染通道(便捷方法) + * @param name 通道名称 + * @param executeFunc 执行函数 + * @return 渲染通道指针 + */ + RenderPass* addLambdaPass(const std::string& name, + std::function executeFunc); + + /** + * @brief 编译渲染图 + * + * 分析依赖关系,确定执行顺序,分配资源 + */ + bool compile(); + + /** + * @brief 执行渲染图 + * @param deltaTime 帧时间 + */ + void execute(float deltaTime); + + /** + * @brief 获取纹理资源 + * @param handle 资源句柄 + * @return 纹理句柄 + */ + TextureHandle getTexture(RenderGraphResourceHandle handle) const; + + /** + * @brief 设置输出尺寸 + * @param width 宽度 + * @param height 高度 + */ + void setOutputSize(uint32_t width, uint32_t height); + + /** + * @brief 获取输出宽度 + */ + uint32_t getOutputWidth() const { return outputWidth_; } + + /** + * @brief 获取输出高度 + */ + uint32_t getOutputHeight() const { return outputHeight_; } + + /** + * @brief 获取命令队列 + */ + CommandQueue* getCommandQueue() { return &commandQueue_; } + +private: + // 渲染通道列表 + std::vector> passes_; + + // 编译后的执行顺序 + std::vector executionOrder_; + + // 资源管理 + std::unordered_map textures_; + uint32_t nextResourceId_ = 1; + + // 构建器 + RenderGraphBuilder builder_; + + // 命令队列 + CommandQueue commandQueue_; + + // 输出尺寸 + uint32_t outputWidth_ = 1280; + uint32_t outputHeight_ = 720; + + // 帧计数 + uint32_t frameIndex_ = 0; + + // 编译状态 + bool compiled_ = false; + + /** + * @brief 创建内部资源 + */ + void createResources(); + + /** + * @brief 销毁内部资源 + */ + void destroyResources(); +}; + +/** + * @brief 简单的几何渲染通道 + * + * 用于常规的 2D 几何渲染 + */ +class GeometryRenderPass : public RenderPass { +public: + GeometryRenderPass(const std::string& name); + + const char* getName() const override { return name_.c_str(); } + void declareResources(RenderGraphBuilder& builder) override; + void execute(const RenderPassContext& ctx) override; + + /** + * @brief 设置颜色目标 + */ + void setColorTarget(RenderGraphResourceHandle handle) { colorTarget_ = handle; } + + /** + * @brief 设置深度目标 + */ + void setDepthTarget(RenderGraphResourceHandle handle) { depthTarget_ = handle; } + +private: + std::string name_; + RenderGraphResourceHandle colorTarget_; + RenderGraphResourceHandle depthTarget_; +}; + +/** + * @brief 后期处理渲染通道 + */ +class PostProcessRenderPass : public RenderPass { +public: + PostProcessRenderPass(const std::string& name); + + const char* getName() const override { return name_.c_str(); } + void declareResources(RenderGraphBuilder& builder) override; + void execute(const RenderPassContext& ctx) override; + + /** + * @brief 设置输入纹理 + */ + void setInputTexture(RenderGraphResourceHandle handle) { inputTexture_ = handle; } + + /** + * @brief 设置输出目标 + */ + void setOutputTarget(RenderGraphResourceHandle handle) { outputTarget_ = handle; } + +private: + std::string name_; + RenderGraphResourceHandle inputTexture_; + RenderGraphResourceHandle outputTarget_; +}; + +} // namespace extra2d diff --git a/include/renderer/render_types.h b/include/renderer/render_types.h index eada0aa..c4f0777 100644 --- a/include/renderer/render_types.h +++ b/include/renderer/render_types.h @@ -1,12 +1,11 @@ #pragma once +#include +#include #include -#include #include #include -#include -#include -#include +#include namespace extra2d { @@ -15,174 +14,110 @@ class Material; class Mesh; class Texture; -// 资源句柄类型(使用新的 Handle) -using MaterialHandle = Handle; -using MeshHandle = Handle; -using TextureHandle = Handle; +// 注意:资源句柄直接使用 Handle,与 RHI 的 TextureHandle 等区分 /** * @brief 渲染命令类型 */ enum class RenderCommandType : uint8_t { - DrawMesh, // 绘制网格 - DrawMeshInstanced, // 实例化绘制 - SetViewport, // 设置视口 - Clear // 清除缓冲区 + 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(uint32_t) * 2; // Handle size - default: return 0; - } -} - /** * @brief 渲染命令结构 - * + * * 使用值类型设计,避免智能指针开销 * 通过资源句柄引用实际资源 */ struct RenderCommand { - RenderCommandType type; // 命令类型 - uint32_t sortKey; // 排序键(材质ID + 层) - - // 绘制网格命令数据 - struct DrawMeshData { - MeshHandle mesh; // 网格句柄 - MaterialHandle material; // 材质句柄 - Vec2 pos; // 位置 - Vec2 scale; // 缩放 - float rot; // 旋转角度 - Color color; // 顶点颜色 - }; - - // 实例化绘制命令数据 - struct DrawInstancedData { - MeshHandle mesh; // 网格句柄 - MaterialHandle material; // 材质句柄 - uint32_t instanceCount; // 实例数量 - uint32_t instanceOffset; // 实例数据偏移 - }; - - // 设置视口命令数据 - struct ViewportData { - int32_t x, y; // 视口位置 - int32_t width, height; // 视口大小 - }; - - // 清除缓冲区命令数据 - struct ClearData { - Color color; // 清除颜色 - uint32_t flags; // 清除标志(颜色/深度/模板) - }; - - union { - DrawMeshData drawMesh; - DrawInstancedData drawInstanced; - ViewportData viewport; - ClearData clear; - }; - - /** - * @brief 默认构造函数 - */ - RenderCommand() : type(RenderCommandType::DrawMesh), sortKey(0) { - drawMesh.mesh = MeshHandle::invalid(); - drawMesh.material = MaterialHandle::invalid(); - drawMesh.pos = Vec2(0.0f, 0.0f); - drawMesh.scale = Vec2(1.0f, 1.0f); - drawMesh.rot = 0.0f; - drawMesh.color = Color::White; - } - - /** - * @brief 从 Transform 设置绘制数据 - */ - void setTransform(const Transform& t) { - drawMesh.pos = t.pos; - drawMesh.scale = t.scale; - drawMesh.rot = t.rot; - } - - /** - * @brief 设置顶点颜色 - */ - void setColor(const Color& c) { - drawMesh.color = c; - } - - /** - * @brief 获取 Transform - */ - Transform getTransform() const { - return Transform(drawMesh.pos, drawMesh.scale, drawMesh.rot); - } - - /** - * @brief 获取顶点颜色 - */ - Color getColor() const { - return drawMesh.color; - } + RenderCommandType type; // 命令类型 + uint32_t sortKey; // 排序键(材质ID + 层) + + // 绘制网格命令数据 + struct DrawMeshData { + Handle mesh; // 网格句柄 + Handle material; // 材质句柄 + Vec2 pos; // 位置 + Vec2 scale; // 缩放 + float rot; // 旋转角度 + Color color; // 顶点颜色 + }; + + // 实例化绘制命令数据 + struct DrawInstancedData { + Handle mesh; // 网格句柄 + Handle material; // 材质句柄 + uint32_t instanceCount; // 实例数量 + uint32_t instanceOffset; // 实例数据偏移 + }; + + // 设置视口命令数据 + struct ViewportData { + int32_t x, y; // 视口位置 + int32_t width, height; // 视口大小 + }; + + // 清除缓冲区命令数据 + struct ClearData { + Color color; // 清除颜色 + uint32_t flags; // 清除标志(颜色/深度/模板) + }; + + union { + DrawMeshData drawMesh; + DrawInstancedData drawInstanced; + ViewportData viewport; + ClearData clear; + }; + + /** + * @brief 默认构造函数 + */ + RenderCommand() : type(RenderCommandType::DrawMesh), sortKey(0) { + drawMesh.mesh = Handle::invalid(); + drawMesh.material = Handle::invalid(); + drawMesh.pos = Vec2(0.0f, 0.0f); + drawMesh.scale = Vec2(1.0f, 1.0f); + drawMesh.rot = 0.0f; + drawMesh.color = Color::White; + } + + /** + * @brief 从 Transform 设置绘制数据 + */ + void setTransform(const Transform &t) { + drawMesh.pos = t.pos; + drawMesh.scale = t.scale; + drawMesh.rot = t.rot; + } + + /** + * @brief 设置顶点颜色 + */ + void setColor(const Color &c) { drawMesh.color = c; } + + /** + * @brief 获取 Transform + */ + Transform getTransform() const { + return Transform(drawMesh.pos, drawMesh.scale, drawMesh.rot); + } + + /** + * @brief 获取顶点颜色 + */ + Color getColor() const { return drawMesh.color; } }; // 清除标志 -constexpr uint32_t CLEAR_COLOR_FLAG = 1 << 0; -constexpr uint32_t CLEAR_DEPTH_FLAG = 1 << 1; +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 CLEAR_ALL_FLAG = + CLEAR_COLOR_FLAG | CLEAR_DEPTH_FLAG | CLEAR_STENCIL_FLAG; // 最大渲染命令数 constexpr uint32_t MAX_RENDER_COMMANDS = 10000; @@ -190,9 +125,9 @@ 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 +// UBO 绑定槽位(已移至 RHI) +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 index ab9bb87..80bbbac 100644 --- a/include/renderer/renderer_module.h +++ b/include/renderer/renderer_module.h @@ -1,15 +1,12 @@ #pragma once -#include #include #include #include -#include -#include +#include +#include #include -#include -#include -#include +#include #include namespace extra2d { @@ -20,12 +17,11 @@ class AssetsModule; /** * @brief 渲染器模块 * - * 核心渲染系统模块,负责: + * 基于新 RHI 架构的核心渲染系统模块,负责: * - 通过事件接收渲染命令 - * - 自动批处理和排序 + * - 使用 RenderGraph 管理渲染流程 + * - 使用 CommandQueue 进行命令排序和批处理 * - 执行实际渲染 - * - * 资源管理已迁移到 AssetsModule */ class RendererModule : public Module { E2D_REGISTER_MODULE(RendererModule, "Renderer", 3) @@ -41,18 +37,16 @@ public: */ ~RendererModule() override; - // 禁止拷贝 + // 禁止拷贝和移动 RendererModule(const RendererModule &) = delete; RendererModule &operator=(const RendererModule &) = delete; - - // 允许移动 - RendererModule(RendererModule &&) noexcept; - RendererModule &operator=(RendererModule &&) noexcept; + RendererModule(RendererModule &&) = delete; + RendererModule &operator=(RendererModule &&) = delete; /** * @brief 初始化模块 * - * 绑定事件监听器,等待窗口显示事件进行 GL 初始化 + * 绑定事件监听器,初始化 RenderGraph 和 CommandQueue * * @return 初始化是否成功 */ @@ -66,29 +60,7 @@ public: void shutdown() override; //=========================================================================== - // 默认资源(通过 AssetsModule 获取) - //=========================================================================== - - /** - * @brief 获取默认材质句柄 - * @return 默认材质句柄 - */ - MaterialHandle getDefaultMaterialHandle() const; - - /** - * @brief 获取默认四边形网格句柄 - * @return 默认四边形网格句柄 - */ - MeshHandle getDefaultQuadHandle() const; - - /** - * @brief 获取默认纹理句柄(1x1 白色纹理) - * @return 默认纹理句柄 - */ - TextureHandle getDefaultTextureHandle() const; - - //=========================================================================== - // 渲染状态设置 + // 渲染接口 //=========================================================================== /** @@ -120,6 +92,42 @@ public: */ const ViewportAdapter &getViewportAdapter() const { return viewportAdapter_; } + /** + * @brief 获取渲染图 + * @return 渲染图指针 + */ + RenderGraph *getRenderGraph() { return &renderGraph_; } + + /** + * @brief 获取命令队列 + * @return 命令队列指针 + */ + CommandQueue *getCommandQueue() { return renderGraph_.getCommandQueue(); } + + /** + * @brief 获取 RHI 上下文 + * @return RHI 上下文指针 + */ + RHIContext *getRHIContext() const; + + /** + * @brief 获取默认材质句柄 + * @return 默认材质句柄 + */ + Handle getDefaultMaterialHandle() const; + + /** + * @brief 获取默认四边形网格句柄 + * @return 默认四边形网格句柄 + */ + Handle getDefaultQuadHandle() const; + + /** + * @brief 获取默认纹理句柄(1x1 白色纹理) + * @return 默认纹理句柄 + */ + Handle getDefaultTextureHandle() const; + private: //=========================================================================== // 事件处理器 @@ -128,7 +136,7 @@ private: /** * @brief 渲染开始事件处理 * - * 清空命令缓冲区,重置统计信息 + * 开始渲染帧,初始化 RenderGraph */ void onRenderBegin(); @@ -147,7 +155,7 @@ private: /** * @brief 渲染结束事件处理 * - * 排序命令,批处理并执行绘制 + * 执行 RenderGraph 渲染 */ void onRenderEnd(); @@ -161,7 +169,7 @@ private: /** * @brief 窗口显示事件处理 * - * 延迟 GL 初始化到窗口显示时 + * 延迟初始化到窗口显示时 */ void onWindowShow(); @@ -169,30 +177,6 @@ private: // 渲染执行 //=========================================================================== - /** - * @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 渲染命令 @@ -200,18 +184,11 @@ private: void executeCommand(const RenderCommand &cmd); //=========================================================================== - // 命令缓冲区 + // 渲染图和命令队列 //=========================================================================== - std::array - commandBuffer_; // 预分配命令缓冲区 - uint32 commandCount_ = 0; // 当前命令数量 - - //=========================================================================== - // UBO 管理器 - //=========================================================================== - - UniformBufferManager uniformManager_; + RenderGraph renderGraph_; // 渲染图 + CommandQueue *commandQueue_ = nullptr; // 命令队列(指向 renderGraph_ 内部) //=========================================================================== // 事件监听器 @@ -228,7 +205,7 @@ private: // 状态标志 //=========================================================================== - bool glInitialized_ = false; // GL 是否已初始化 + bool initialized_ = false; // 是否已初始化 //=========================================================================== // 渲染统计 @@ -236,7 +213,6 @@ private: struct Stats { uint32 commandsSubmitted = 0; // 提交的命令数 - uint32 commandsExecuted = 0; // 执行的命令数 uint32 drawCalls = 0; // 绘制调用次数 uint32 batches = 0; // 批次数 } stats_; @@ -246,7 +222,7 @@ private: //=========================================================================== int32 viewportX_ = 0, viewportY_ = 0; - int32 viewportWidth_ = 0, viewportHeight_ = 0; + int32 viewportWidth_ = 1280, viewportHeight_ = 720; //=========================================================================== // 视口适配器 diff --git a/include/renderer/rhi/opengl/gl_buffer.h b/include/renderer/rhi/opengl/gl_buffer.h new file mode 100644 index 0000000..eefe0db --- /dev/null +++ b/include/renderer/rhi/opengl/gl_buffer.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +namespace extra2d { + +/** + * @brief OpenGL 缓冲区实现 + */ +class GLBuffer : public RHIBuffer { +public: + explicit GLBuffer(const BufferDesc &desc); + ~GLBuffer() override; + + bool create(); + void destroy(); + + bool update(const void *data, uint32_t size, uint32_t offset) override; + void *map() override; + void unmap() override; + + const BufferDesc &getDesc() const { return desc_; } + uint32_t getSize() const override { return desc_.size; } + BufferType getType() const override { return desc_.type; } + bool isValid() const override { return buffer_ != 0; } + + GLuint getGLBuffer() const { return buffer_; } + +private: + BufferDesc desc_; + GLuint buffer_; +}; + +} // namespace extra2d diff --git a/include/renderer/rhi/opengl/gl_command_list.h b/include/renderer/rhi/opengl/gl_command_list.h new file mode 100644 index 0000000..24595a0 --- /dev/null +++ b/include/renderer/rhi/opengl/gl_command_list.h @@ -0,0 +1,99 @@ +#pragma once + +#include +#include + +namespace extra2d { + +// 前向声明 +class GLPipeline; +class GLBuffer; +class GLTexture; +class GLFramebuffer; + +/** + * @brief OpenGL 命令列表实现 + */ +class GLCommandList : public RHICommandList { +public: + GLCommandList(); + ~GLCommandList() override; + + //=========================================================================== + // 命令列表生命周期 + //=========================================================================== + + void begin() override; + void end() override; + void submit() override; + + //=========================================================================== + // 渲染通道 + //=========================================================================== + + void beginRenderPass(RHIFramebuffer *framebuffer, + ClearFlags clearFlags = ClearFlags::Color, + const Color &clearColor = Color::Black, + float clearDepth = 1.0f, + uint8_t clearStencil = 0) override; + void endRenderPass() override; + + //=========================================================================== + // 状态设置 + //=========================================================================== + + void setViewport(const Viewport &viewport) override; + void setScissor(const ScissorRect &scissor) override; + void setPipeline(RHIPipeline *pipeline) override; + + //=========================================================================== + // 资源绑定 + //=========================================================================== + + void setVertexBuffer(uint32_t slot, RHIBuffer *buffer, + uint32_t offset = 0) override; + void setIndexBuffer(RHIBuffer *buffer, IndexType type, + uint32_t offset = 0) override; + void setUniformBuffer(uint32_t slot, RHIBuffer *buffer) override; + void setTexture(uint32_t slot, RHITexture *texture) override; + void setSampler(uint32_t slot, TextureFilter minFilter, + TextureFilter magFilter, TextureWrap wrapS, + TextureWrap wrapT) override; + + //=========================================================================== + // 绘制命令 + //=========================================================================== + + void draw(uint32_t vertexCount, uint32_t firstVertex = 0, + uint32_t instanceCount = 1, uint32_t firstInstance = 0) override; + + void drawIndexed(uint32_t indexCount, uint32_t firstIndex = 0, + int32_t vertexOffset = 0, uint32_t instanceCount = 1, + uint32_t firstInstance = 0) override; + + //=========================================================================== + // 工具方法 + //=========================================================================== + + void clear(ClearFlags flags, const Color &color = Color::Black, + float depth = 1.0f, uint8_t stencil = 0) override; + + bool isRecording() const override; + + // 设置 uniform 变量 + void setUniform(const std::string& name, float value); + void setUniform(const std::string& name, const Vec2& value); + void setUniform(const std::string& name, const Vec3& value); + void setUniform(const std::string& name, const Color& value); + void setUniform(const std::string& name, const Mat4& value); + +private: + bool recording_ = false; + GLPipeline *currentPipeline_ = nullptr; + GLBuffer *currentVertexBuffer_ = nullptr; + GLBuffer *currentIndexBuffer_ = nullptr; + GLFramebuffer *currentFramebuffer_ = nullptr; + GLuint currentShaderProgram_ = 0; +}; + +} // namespace extra2d diff --git a/include/renderer/rhi/opengl/gl_context.h b/include/renderer/rhi/opengl/gl_context.h new file mode 100644 index 0000000..f8338c1 --- /dev/null +++ b/include/renderer/rhi/opengl/gl_context.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +// 前向声明 +class GLDevice; +class GLFramebuffer; + +/** + * @brief OpenGL 上下文实现 + */ +class GLContext : public RHIContext { +public: + explicit GLContext(GLDevice* device); + ~GLContext() override; + + bool initialize() override; + void shutdown() override; + + void beginFrame() override; + void endFrame() override; + + void setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height) override; + void bindDefaultFramebuffer() override; + RHIFramebuffer* getDefaultFramebuffer() override; + + bool isFeatureSupported(const char* feature) const override; + +private: + GLDevice* device_; + std::unique_ptr defaultFramebuffer_; +}; + +} // namespace extra2d diff --git a/include/renderer/rhi/opengl/gl_framebuffer.h b/include/renderer/rhi/opengl/gl_framebuffer.h new file mode 100644 index 0000000..b5d855a --- /dev/null +++ b/include/renderer/rhi/opengl/gl_framebuffer.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +// 前向声明 +class GLTexture; + +/** + * @brief OpenGL 帧缓冲实现 + */ +class GLFramebuffer : public RHIFramebuffer { +public: + GLFramebuffer(); + explicit GLFramebuffer(const RenderPassDesc& desc); + ~GLFramebuffer() override; + + bool create(); + void destroy(); + + void bind() override; + void unbind() override; + + uint32_t getColorAttachmentCount() const override; + TextureHandle getColorAttachment(uint32_t index) const override; + TextureHandle getDepthStencilAttachment() const override; + + uint32_t getWidth() const override { return width_; } + uint32_t getHeight() const override { return height_; } + + bool hasDepthStencil() const override; + bool isValid() const override; + bool isDefault() const override; + + void setSize(uint32_t width, uint32_t height); + GLuint getGLFramebuffer() const { return framebuffer_; } + + const RenderPassDesc& getDesc() const { return desc_; } + void clear(ClearFlags flags, const Color& color, float depth, uint8_t stencil); + +private: + RenderPassDesc desc_; + GLuint framebuffer_; + uint32_t width_; + uint32_t height_; + std::vector> colorAttachments_; + std::unique_ptr depthStencilAttachment_; +}; + +} // namespace extra2d diff --git a/include/renderer/rhi/opengl/gl_pipeline.h b/include/renderer/rhi/opengl/gl_pipeline.h new file mode 100644 index 0000000..cba3e41 --- /dev/null +++ b/include/renderer/rhi/opengl/gl_pipeline.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +namespace extra2d { + +// 前向声明 +class GLShader; + +/** + * @brief OpenGL 管线实现 + */ +class GLPipeline : public RHIPipeline { +public: + explicit GLPipeline(const PipelineDesc& desc); + ~GLPipeline() override; + + bool create(); + void destroy(); + + void bind() override; + void unbind() override; + + ShaderHandle getVertexShader() const override; + ShaderHandle getFragmentShader() const override; + const VertexLayout& getVertexLayout() const override; + bool isValid() const override; + + const PipelineDesc& getDesc() const { return desc_; } + GLuint getGLVAO() const { return vao_; } + + // 获取 shader program ID(用于设置 uniform) + GLuint getGLProgram() const { return shaderProgram_; } + + // 设置 shader program ID(在创建时调用) + void setGLProgram(GLuint program) { shaderProgram_ = program; } + +private: + PipelineDesc desc_; + GLuint vao_; + GLuint shaderProgram_ = 0; +}; + +} // namespace extra2d diff --git a/include/renderer/rhi/opengl/gl_shader.h b/include/renderer/rhi/opengl/gl_shader.h new file mode 100644 index 0000000..d0bcb33 --- /dev/null +++ b/include/renderer/rhi/opengl/gl_shader.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +/** + * @brief OpenGL 着色器实现 + */ +class GLShader : public RHIShader { +public: + explicit GLShader(const ShaderDesc& desc); + ~GLShader() override; + + bool compile(); + void destroy(); + + ShaderType getType() const override { return ShaderType::Vertex; } // 返回 Vertex 表示这是一个程序着色器 + bool isCompiled() const override; + std::string getCompileLog() const override; + bool isValid() const override; + + void bind() const; + void unbind() const; + + void setUniform(const std::string& name, float value); + void setUniform(const std::string& name, const Vec2& value); + void setUniform(const std::string& name, const Vec3& value); + void setUniform(const std::string& name, const Color& value); + void setUniform(const std::string& name, const Mat4& value); + + const ShaderDesc& getDesc() const { return desc_; } + GLuint getGLProgram() const { return program_; } + +private: + ShaderDesc desc_; + GLuint shader_; + GLuint program_; + std::string compileLog_; + bool compiled_; +}; + +} // namespace extra2d diff --git a/include/renderer/rhi/opengl/gl_texture.h b/include/renderer/rhi/opengl/gl_texture.h new file mode 100644 index 0000000..6465d10 --- /dev/null +++ b/include/renderer/rhi/opengl/gl_texture.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +namespace extra2d { + +/** + * @brief OpenGL 纹理实现 + */ +class GLTexture : public RHITexture { +public: + explicit GLTexture(const TextureDesc& desc); + ~GLTexture() override; + + bool create(); + void destroy(); + + TextureType getType() const override { return desc_.type; } + TextureFormat getFormat() const override { return desc_.format; } + uint32_t getWidth() const override { return desc_.width; } + uint32_t getHeight() const override { return desc_.height; } + uint32_t getMipLevels() const override { return desc_.mipLevels; } + + bool update(const void* data, uint32_t mipLevel = 0) override; + void generateMipmap() override; + void bind(uint32_t slot) override; + void unbind() override; + + bool isValid() const override; + bool isRenderTarget() const override; + + GLuint getGLTexture() const { return texture_; } + +private: + TextureDesc desc_; + GLuint texture_; +}; + +} // namespace extra2d diff --git a/include/renderer/rhi/opengl/gl_utils.h b/include/renderer/rhi/opengl/gl_utils.h new file mode 100644 index 0000000..39e0344 --- /dev/null +++ b/include/renderer/rhi/opengl/gl_utils.h @@ -0,0 +1,307 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +/** + * @brief OpenGL 错误检查宏 + */ +#define GL_CHECK(call) do { \ + call; \ + GLenum err = glGetError(); \ + if (err != GL_NO_ERROR) { \ + E2D_LOG_ERROR("OpenGL error in {}: {} ({})", #call, getGLErrorString(err), err); \ + } \ +} while(0) + +/** + * @brief 获取 OpenGL 错误字符串 + * @param error OpenGL 错误码 + * @return 错误描述字符串 + */ +inline const char* getGLErrorString(GLenum error) { + switch (error) { + case GL_NO_ERROR: return "GL_NO_ERROR"; + case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; + case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW"; + case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW"; + case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; + case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION"; + default: return "Unknown Error"; + } +} + +/** + * @brief 将 BufferType 转换为 OpenGL 缓冲区目标 + */ +inline GLenum bufferTypeToGL(BufferType type) { + switch (type) { + case BufferType::Vertex: return GL_ARRAY_BUFFER; + case BufferType::Index: return GL_ELEMENT_ARRAY_BUFFER; + case BufferType::Uniform: return GL_UNIFORM_BUFFER; + case BufferType::Storage: return GL_SHADER_STORAGE_BUFFER; + case BufferType::Staging: return GL_ARRAY_BUFFER; + default: return GL_ARRAY_BUFFER; + } +} + +/** + * @brief 将 BufferUsage 转换为 OpenGL 使用模式 + */ +inline GLenum bufferUsageToGL(BufferUsage usage) { + switch (usage) { + case BufferUsage::Static: return GL_STATIC_DRAW; + case BufferUsage::Dynamic: return GL_DYNAMIC_DRAW; + case BufferUsage::Stream: return GL_STREAM_DRAW; + default: return GL_STATIC_DRAW; + } +} + +/** + * @brief 将 TextureFormat 转换为 OpenGL 内部格式 + */ +inline GLenum textureFormatToGLInternal(TextureFormat format) { + switch (format) { + case TextureFormat::R8: return GL_R8; + case TextureFormat::RG8: return GL_RG8; + case TextureFormat::RGB8: return GL_RGB8; + case TextureFormat::RGBA8: return GL_RGBA8; + case TextureFormat::RGBA8_SRGB: return GL_SRGB8_ALPHA8; + case TextureFormat::Depth16: return GL_DEPTH_COMPONENT16; + case TextureFormat::Depth24: return GL_DEPTH_COMPONENT24; + case TextureFormat::Depth32F: return GL_DEPTH_COMPONENT32F; + case TextureFormat::Depth24Stencil8: return GL_DEPTH24_STENCIL8; + case TextureFormat::Depth32FStencil8:return GL_DEPTH32F_STENCIL8; + case TextureFormat::BC1: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case TextureFormat::BC3: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case TextureFormat::BC5: return 0x8DBD; // GL_COMPRESSED_RG_RGTC2 + default: return GL_RGBA8; + } +} + +/** + * @brief 将 TextureFormat 转换为 OpenGL 格式 + */ +inline GLenum textureFormatToGLFormat(TextureFormat format) { + switch (format) { + case TextureFormat::R8: return GL_RED; + case TextureFormat::RG8: return GL_RG; + case TextureFormat::RGB8: return GL_RGB; + case TextureFormat::RGBA8: + case TextureFormat::RGBA8_SRGB: return GL_RGBA; + case TextureFormat::Depth16: + case TextureFormat::Depth24: + case TextureFormat::Depth32F: return GL_DEPTH_COMPONENT; + case TextureFormat::Depth24Stencil8: + case TextureFormat::Depth32FStencil8:return GL_DEPTH_STENCIL; + default: return GL_RGBA; + } +} + +/** + * @brief 将 TextureFormat 转换为 OpenGL 数据类型 + */ +inline GLenum textureFormatToGLType(TextureFormat format) { + switch (format) { + case TextureFormat::R8: + case TextureFormat::RG8: + case TextureFormat::RGB8: + case TextureFormat::RGBA8: + case TextureFormat::RGBA8_SRGB: return GL_UNSIGNED_BYTE; + case TextureFormat::Depth16: return GL_UNSIGNED_SHORT; + case TextureFormat::Depth24: return GL_UNSIGNED_INT; + case TextureFormat::Depth32F: return GL_FLOAT; + case TextureFormat::Depth24Stencil8: return GL_UNSIGNED_INT_24_8; + case TextureFormat::Depth32FStencil8:return GL_FLOAT_32_UNSIGNED_INT_24_8_REV; + default: return GL_UNSIGNED_BYTE; + } +} + +/** + * @brief 将 TextureFilter 转换为 OpenGL 过滤模式 + */ +inline GLenum textureFilterToGLMin(TextureFilter filter) { + switch (filter) { + case TextureFilter::Nearest: return GL_NEAREST; + case TextureFilter::Linear: return GL_LINEAR; + case TextureFilter::NearestMipmapNearest: return GL_NEAREST_MIPMAP_NEAREST; + case TextureFilter::LinearMipmapNearest: return GL_LINEAR_MIPMAP_NEAREST; + case TextureFilter::NearestMipmapLinear: return GL_NEAREST_MIPMAP_LINEAR; + case TextureFilter::LinearMipmapLinear: return GL_LINEAR_MIPMAP_LINEAR; + default: return GL_LINEAR; + } +} + +inline GLenum textureFilterToGLMag(TextureFilter filter) { + // Mag filter doesn't support mipmaps + switch (filter) { + case TextureFilter::Nearest: + case TextureFilter::NearestMipmapNearest: + case TextureFilter::NearestMipmapLinear: + return GL_NEAREST; + case TextureFilter::Linear: + case TextureFilter::LinearMipmapNearest: + case TextureFilter::LinearMipmapLinear: + default: + return GL_LINEAR; + } +} + +/** + * @brief 将 TextureWrap 转换为 OpenGL 环绕模式 + */ +inline GLenum textureWrapToGL(TextureWrap wrap) { + switch (wrap) { + case TextureWrap::Repeat: return GL_REPEAT; + case TextureWrap::ClampToEdge: return GL_CLAMP_TO_EDGE; + case TextureWrap::ClampToBorder: return GL_CLAMP_TO_BORDER; + case TextureWrap::MirroredRepeat: return GL_MIRRORED_REPEAT; + default: return GL_REPEAT; + } +} + +/** + * @brief 将 VertexFormat 转换为 OpenGL 格式 + */ +inline void vertexFormatToGL(VertexFormat format, GLint& components, GLenum& type, GLboolean& normalized) { + switch (format) { + case VertexFormat::Float1: components = 1; type = GL_FLOAT; normalized = GL_FALSE; break; + case VertexFormat::Float2: components = 2; type = GL_FLOAT; normalized = GL_FALSE; break; + case VertexFormat::Float3: components = 3; type = GL_FLOAT; normalized = GL_FALSE; break; + case VertexFormat::Float4: components = 4; type = GL_FLOAT; normalized = GL_FALSE; break; + case VertexFormat::Int1: components = 1; type = GL_INT; normalized = GL_FALSE; break; + case VertexFormat::Int2: components = 2; type = GL_INT; normalized = GL_FALSE; break; + case VertexFormat::Int3: components = 3; type = GL_INT; normalized = GL_FALSE; break; + case VertexFormat::Int4: components = 4; type = GL_INT; normalized = GL_FALSE; break; + case VertexFormat::UInt1: components = 1; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break; + case VertexFormat::UInt2: components = 2; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break; + case VertexFormat::UInt3: components = 3; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break; + case VertexFormat::UInt4: components = 4; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break; + case VertexFormat::Byte4: components = 4; type = GL_BYTE; normalized = GL_FALSE; break; + case VertexFormat::Byte4Normalized: components = 4; type = GL_BYTE; normalized = GL_TRUE; break; + case VertexFormat::UByte4: components = 4; type = GL_UNSIGNED_BYTE; normalized = GL_FALSE; break; + case VertexFormat::UByte4Normalized:components = 4; type = GL_UNSIGNED_BYTE; normalized = GL_TRUE; break; + default: components = 4; type = GL_FLOAT; normalized = GL_FALSE; break; + } +} + +/** + * @brief 将 IndexType 转换为 OpenGL 类型 + */ +inline GLenum indexTypeToGL(IndexType type) { + switch (type) { + case IndexType::UInt16: return GL_UNSIGNED_SHORT; + case IndexType::UInt32: return GL_UNSIGNED_INT; + default: return GL_UNSIGNED_SHORT; + } +} + +/** + * @brief 将 PrimitiveType 转换为 OpenGL 图元类型 + */ +inline GLenum primitiveTypeToGL(PrimitiveType type) { + switch (type) { + case PrimitiveType::Points: return GL_POINTS; + case PrimitiveType::Lines: return GL_LINES; + case PrimitiveType::LineStrip: return GL_LINE_STRIP; + case PrimitiveType::Triangles: return GL_TRIANGLES; + case PrimitiveType::TriangleStrip: return GL_TRIANGLE_STRIP; + case PrimitiveType::TriangleFan: return GL_TRIANGLE_FAN; + default: return GL_TRIANGLES; + } +} + +/** + * @brief 将 BlendFactor 转换为 OpenGL 混合因子 + */ +inline GLenum blendFactorToGL(BlendFactor factor) { + switch (factor) { + case BlendFactor::Zero: return GL_ZERO; + case BlendFactor::One: return GL_ONE; + case BlendFactor::SrcColor: return GL_SRC_COLOR; + case BlendFactor::OneMinusSrcColor: return GL_ONE_MINUS_SRC_COLOR; + case BlendFactor::DstColor: return GL_DST_COLOR; + case BlendFactor::OneMinusDstColor: return GL_ONE_MINUS_DST_COLOR; + case BlendFactor::SrcAlpha: return GL_SRC_ALPHA; + case BlendFactor::OneMinusSrcAlpha: return GL_ONE_MINUS_SRC_ALPHA; + case BlendFactor::DstAlpha: return GL_DST_ALPHA; + case BlendFactor::OneMinusDstAlpha: return GL_ONE_MINUS_DST_ALPHA; + default: return GL_ONE; + } +} + +/** + * @brief 将 BlendOp 转换为 OpenGL 混合操作 + */ +inline GLenum blendOpToGL(BlendOp op) { + switch (op) { + case BlendOp::Add: return GL_FUNC_ADD; + case BlendOp::Subtract: return GL_FUNC_SUBTRACT; + case BlendOp::ReverseSubtract: return GL_FUNC_REVERSE_SUBTRACT; + case BlendOp::Min: return GL_MIN; + case BlendOp::Max: return GL_MAX; + default: return GL_FUNC_ADD; + } +} + +/** + * @brief 将 CompareFunc 转换为 OpenGL 比较函数 + */ +inline GLenum compareFuncToGL(CompareFunc func) { + switch (func) { + case CompareFunc::Never: return GL_NEVER; + case CompareFunc::Less: return GL_LESS; + case CompareFunc::Equal: return GL_EQUAL; + case CompareFunc::LessEqual: return GL_LEQUAL; + case CompareFunc::Greater: return GL_GREATER; + case CompareFunc::NotEqual: return GL_NOTEQUAL; + case CompareFunc::GreaterEqual: return GL_GEQUAL; + case CompareFunc::Always: return GL_ALWAYS; + default: return GL_LESS; + } +} + +/** + * @brief 将 ShaderType 转换为 OpenGL 着色器类型 + */ +inline GLenum shaderTypeToGL(ShaderType type) { + switch (type) { + case ShaderType::Vertex: return GL_VERTEX_SHADER; + case ShaderType::Fragment: return GL_FRAGMENT_SHADER; + case ShaderType::Geometry: return GL_GEOMETRY_SHADER; + case ShaderType::Compute: return GL_COMPUTE_SHADER; + default: return GL_VERTEX_SHADER; + } +} + +/** + * @brief 获取 VertexFormat 的大小(字节) + */ +inline uint32_t getVertexFormatSize(VertexFormat format) { + switch (format) { + case VertexFormat::Float1: return sizeof(float) * 1; + case VertexFormat::Float2: return sizeof(float) * 2; + case VertexFormat::Float3: return sizeof(float) * 3; + case VertexFormat::Float4: return sizeof(float) * 4; + case VertexFormat::Int1: return sizeof(int32_t) * 1; + case VertexFormat::Int2: return sizeof(int32_t) * 2; + case VertexFormat::Int3: return sizeof(int32_t) * 3; + case VertexFormat::Int4: return sizeof(int32_t) * 4; + case VertexFormat::UInt1: return sizeof(uint32_t) * 1; + case VertexFormat::UInt2: return sizeof(uint32_t) * 2; + case VertexFormat::UInt3: return sizeof(uint32_t) * 3; + case VertexFormat::UInt4: return sizeof(uint32_t) * 4; + case VertexFormat::Byte4: + case VertexFormat::Byte4Normalized: return sizeof(int8_t) * 4; + case VertexFormat::UByte4: + case VertexFormat::UByte4Normalized: return sizeof(uint8_t) * 4; + default: return sizeof(float) * 4; + } +} + +} // namespace extra2d diff --git a/include/renderer/rhi/rhi.h b/include/renderer/rhi/rhi.h new file mode 100644 index 0000000..5c2370a --- /dev/null +++ b/include/renderer/rhi/rhi.h @@ -0,0 +1,14 @@ +#pragma once + +/** + * @brief RHI (Render Hardware Interface) 头文件 + * + * 包含所有 RHI 相关的接口和类型定义 + */ + +#include +#include +#include +#include +#include +#include diff --git a/include/renderer/rhi/rhi_buffer.h b/include/renderer/rhi/rhi_buffer.h new file mode 100644 index 0000000..d77aa7b --- /dev/null +++ b/include/renderer/rhi/rhi_buffer.h @@ -0,0 +1,131 @@ +#pragma once + +#include + +namespace extra2d { + +/** + * @brief RHI 缓冲区接口 + * + * 抽象 GPU 缓冲区资源 + */ +class RHIBuffer { +public: + virtual ~RHIBuffer() = default; + + /** + * @brief 获取缓冲区类型 + * @return 缓冲区类型 + */ + virtual BufferType getType() const = 0; + + /** + * @brief 获取缓冲区大小 + * @return 缓冲区大小(字节) + */ + virtual uint32_t getSize() const = 0; + + /** + * @brief 更新缓冲区数据 + * @param data 数据源指针 + * @param size 数据大小 + * @param offset 缓冲区偏移(字节) + * @return 更新是否成功 + */ + virtual bool update(const void* data, uint32_t size, uint32_t offset = 0) = 0; + + /** + * @brief 映射缓冲区到 CPU 内存 + * @return 映射的内存指针,失败返回 nullptr + */ + virtual void* map() = 0; + + /** + * @brief 解除映射 + */ + virtual void unmap() = 0; + + /** + * @brief 检查是否有效 + * @return 是否有效 + */ + virtual bool isValid() const = 0; +}; + +/** + * @brief RHI 纹理接口 + * + * 抽象 GPU 纹理资源 + */ +class RHITexture { +public: + virtual ~RHITexture() = default; + + /** + * @brief 获取纹理类型 + * @return 纹理类型 + */ + virtual TextureType getType() const = 0; + + /** + * @brief 获取纹理格式 + * @return 纹理格式 + */ + virtual TextureFormat getFormat() const = 0; + + /** + * @brief 获取纹理宽度 + * @return 宽度 + */ + virtual uint32_t getWidth() const = 0; + + /** + * @brief 获取纹理高度 + * @return 高度 + */ + virtual uint32_t getHeight() const = 0; + + /** + * @brief 获取 Mipmap 层级数 + * @return Mipmap 层级数 + */ + virtual uint32_t getMipLevels() const = 0; + + /** + * @brief 更新纹理数据 + * @param data 像素数据 + * @param mipLevel Mipmap 层级 + * @return 更新是否成功 + */ + virtual bool update(const void* data, uint32_t mipLevel = 0) = 0; + + /** + * @brief 生成 Mipmap + */ + virtual void generateMipmap() = 0; + + /** + * @brief 绑定到指定槽位 + * @param slot 纹理槽位 + */ + virtual void bind(uint32_t slot) = 0; + + /** + * @brief 解绑 + */ + virtual void unbind() = 0; + + /** + * @brief 检查是否有效 + * @return 是否有效 + */ + virtual bool isValid() const = 0; + + /** + * @brief 检查是否可作为渲染目标 + * @return 是否可作为渲染目标 + */ + virtual bool isRenderTarget() const = 0; +}; + +} // namespace extra2d diff --git a/include/renderer/rhi/rhi_command_list.h b/include/renderer/rhi/rhi_command_list.h new file mode 100644 index 0000000..2579ddc --- /dev/null +++ b/include/renderer/rhi/rhi_command_list.h @@ -0,0 +1,184 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief RHI 命令列表接口 + * + * 抽象 GPU 命令录制和执行 + */ +class RHICommandList { +public: + virtual ~RHICommandList() = default; + + //=========================================================================== + // 命令列表生命周期 + //=========================================================================== + + /** + * @brief 开始录制命令 + */ + virtual void begin() = 0; + + /** + * @brief 结束录制命令 + */ + virtual void end() = 0; + + /** + * @brief 提交命令到 GPU + */ + virtual void submit() = 0; + + //=========================================================================== + // 渲染通道 + //=========================================================================== + + /** + * @brief 开始渲染通道 + * @param framebuffer 帧缓冲 + * @param clearFlags 清除标志 + * @param clearColor 清除颜色(当包含 Color 标志时) + * @param clearDepth 清除深度值(当包含 Depth 标志时) + * @param clearStencil 清除模板值(当包含 Stencil 标志时) + */ + virtual void beginRenderPass(RHIFramebuffer* framebuffer, + ClearFlags clearFlags = ClearFlags::Color, + const Color& clearColor = Color::Black, + float clearDepth = 1.0f, + uint8_t clearStencil = 0) = 0; + + /** + * @brief 结束渲染通道 + */ + virtual void endRenderPass() = 0; + + //=========================================================================== + // 状态设置 + //=========================================================================== + + /** + * @brief 设置视口 + * @param viewport 视口 + */ + virtual void setViewport(const Viewport& viewport) = 0; + + /** + * @brief 设置裁剪矩形 + * @param scissor 裁剪矩形 + */ + virtual void setScissor(const ScissorRect& scissor) = 0; + + /** + * @brief 设置图形管线 + * @param pipeline 管线 + */ + virtual void setPipeline(RHIPipeline* pipeline) = 0; + + //=========================================================================== + // 资源绑定 + //=========================================================================== + + /** + * @brief 设置顶点缓冲区 + * @param slot 槽位 + * @param buffer 缓冲区 + * @param offset 偏移(字节) + */ + virtual void setVertexBuffer(uint32_t slot, RHIBuffer* buffer, uint32_t offset = 0) = 0; + + /** + * @brief 设置索引缓冲区 + * @param buffer 缓冲区 + * @param type 索引类型 + * @param offset 偏移(字节) + */ + virtual void setIndexBuffer(RHIBuffer* buffer, IndexType type, uint32_t offset = 0) = 0; + + /** + * @brief 设置 Uniform 缓冲区 + * @param slot 槽位 + * @param buffer 缓冲区 + */ + virtual void setUniformBuffer(uint32_t slot, RHIBuffer* buffer) = 0; + + /** + * @brief 设置纹理 + * @param slot 槽位 + * @param texture 纹理 + */ + virtual void setTexture(uint32_t slot, RHITexture* texture) = 0; + + /** + * @brief 设置采样器 + * @param slot 槽位 + * @param minFilter 最小过滤 + * @param magFilter 最大过滤 + * @param wrapS S 轴环绕 + * @param wrapT T 轴环绕 + */ + virtual void setSampler(uint32_t slot, + TextureFilter minFilter, + TextureFilter magFilter, + TextureWrap wrapS, + TextureWrap wrapT) = 0; + + //=========================================================================== + // 绘制命令 + //=========================================================================== + + /** + * @brief 绘制顶点 + * @param vertexCount 顶点数量 + * @param firstVertex 起始顶点 + * @param instanceCount 实例数量 + * @param firstInstance 起始实例 + */ + virtual void draw(uint32_t vertexCount, + uint32_t firstVertex = 0, + uint32_t instanceCount = 1, + uint32_t firstInstance = 0) = 0; + + /** + * @brief 绘制索引 + * @param indexCount 索引数量 + * @param firstIndex 起始索引 + * @param vertexOffset 顶点偏移 + * @param instanceCount 实例数量 + * @param firstInstance 起始实例 + */ + virtual void drawIndexed(uint32_t indexCount, + uint32_t firstIndex = 0, + int32_t vertexOffset = 0, + uint32_t instanceCount = 1, + uint32_t firstInstance = 0) = 0; + + //=========================================================================== + // 工具方法 + //=========================================================================== + + /** + * @brief 清除当前帧缓冲 + * @param flags 清除标志 + * @param color 清除颜色 + * @param depth 清除深度值 + * @param stencil 清除模板值 + */ + virtual void clear(ClearFlags flags, + const Color& color = Color::Black, + float depth = 1.0f, + uint8_t stencil = 0) = 0; + + /** + * @brief 检查是否在录制状态 + * @return 是否在录制 + */ + virtual bool isRecording() const = 0; +}; + +} // namespace extra2d diff --git a/include/renderer/rhi/rhi_device.h b/include/renderer/rhi/rhi_device.h new file mode 100644 index 0000000..72a2890 --- /dev/null +++ b/include/renderer/rhi/rhi_device.h @@ -0,0 +1,169 @@ +#pragma once + +#include +#include + +namespace extra2d { + +/** + * @brief RHI 设备接口 + * + * 抽象图形设备,负责资源创建和上下文管理 + */ +class RHIDevice { +public: + virtual ~RHIDevice() = default; + + /** + * @brief 初始化设备 + * @param nativeWindow 原生窗口句柄(如 SDL_Window*) + * @return 初始化是否成功 + */ + virtual bool initialize(void* nativeWindow) = 0; + + /** + * @brief 关闭设备 + */ + virtual void shutdown() = 0; + + //=========================================================================== + // 资源创建 + //=========================================================================== + + /** + * @brief 创建缓冲区 + * @param desc 缓冲区描述 + * @return 缓冲区对象 + */ + virtual std::unique_ptr createBuffer(const BufferDesc &desc) = 0; + + /** + * @brief 创建纹理 + * @param desc 纹理描述 + * @return 纹理对象 + */ + virtual std::unique_ptr + createTexture(const TextureDesc &desc) = 0; + + /** + * @brief 创建着色器 + * @param desc 着色器描述 + * @return 着色器对象 + */ + virtual std::unique_ptr createShader(const ShaderDesc &desc) = 0; + + /** + * @brief 创建图形管线 + * @param desc 管线描述 + * @return 管线对象 + */ + virtual std::unique_ptr + createPipeline(const PipelineDesc &desc) = 0; + + /** + * @brief 创建帧缓冲 + * @param desc 帧缓冲描述 + * @return 帧缓冲对象 + */ + virtual std::unique_ptr + createFramebuffer(const RenderPassDesc &desc) = 0; + + /** + * @brief 创建命令列表 + * @return 命令列表对象 + */ + virtual std::unique_ptr createCommandList() = 0; + + //=========================================================================== + // 上下文管理 + //=========================================================================== + + /** + * @brief 获取渲染上下文 + * @return 渲染上下文指针 + */ + virtual RHIContext *getContext() = 0; + + /** + * @brief 获取后端名称 + * @return 后端名称(如 "OpenGL", "Vulkan") + */ + virtual const char *getBackendName() const = 0; + + /** + * @brief 等待 GPU 完成所有操作 + */ + virtual void waitIdle() = 0; + + /** + * @brief 获取渲染统计 + * @return 渲染统计信息 + */ + virtual const RenderStats &getStats() const = 0; + + /** + * @brief 重置渲染统计 + */ + virtual void resetStats() = 0; +}; + +/** + * @brief RHI 上下文接口 + * + * 管理渲染上下文和呈现 + */ +class RHIContext { +public: + virtual ~RHIContext() = default; + + /** + * @brief 初始化上下文 + * @return 初始化是否成功 + */ + virtual bool initialize() = 0; + + /** + * @brief 关闭上下文 + */ + virtual void shutdown() = 0; + + /** + * @brief 开始一帧 + */ + virtual void beginFrame() = 0; + + /** + * @brief 结束一帧并呈现 + */ + virtual void endFrame() = 0; + + /** + * @brief 设置视口 + * @param x 视口 X 坐标 + * @param y 视口 Y 坐标 + * @param width 视口宽度 + * @param height 视口高度 + */ + virtual void setViewport(int32_t x, int32_t y, uint32_t width, + uint32_t height) = 0; + + /** + * @brief 设置默认帧缓冲 + */ + virtual void bindDefaultFramebuffer() = 0; + + /** + * @brief 获取默认帧缓冲 + * @return 默认帧缓冲 + */ + virtual RHIFramebuffer *getDefaultFramebuffer() = 0; + + /** + * @brief 检查是否支持特性 + * @param feature 特性名称 + * @return 是否支持 + */ + virtual bool isFeatureSupported(const char *feature) const = 0; +}; + +} // namespace extra2d diff --git a/include/renderer/rhi/rhi_framebuffer.h b/include/renderer/rhi/rhi_framebuffer.h new file mode 100644 index 0000000..2ba224a --- /dev/null +++ b/include/renderer/rhi/rhi_framebuffer.h @@ -0,0 +1,76 @@ +#pragma once + +#include + +namespace extra2d { + +/** + * @brief RHI 帧缓冲接口 + * + * 抽象 GPU 帧缓冲对象 (FBO) + */ +class RHIFramebuffer { +public: + virtual ~RHIFramebuffer() = default; + + /** + * @brief 绑定帧缓冲 + */ + virtual void bind() = 0; + + /** + * @brief 解绑帧缓冲 + */ + virtual void unbind() = 0; + + /** + * @brief 获取颜色附件数量 + * @return 颜色附件数量 + */ + virtual uint32_t getColorAttachmentCount() const = 0; + + /** + * @brief 获取颜色附件纹理 + * @param index 附件索引 + * @return 纹理句柄 + */ + virtual TextureHandle getColorAttachment(uint32_t index) const = 0; + + /** + * @brief 获取深度/模板附件纹理 + * @return 纹理句柄,无则返回无效句柄 + */ + virtual TextureHandle getDepthStencilAttachment() const = 0; + + /** + * @brief 获取帧缓冲宽度 + * @return 宽度 + */ + virtual uint32_t getWidth() const = 0; + + /** + * @brief 获取帧缓冲高度 + * @return 高度 + */ + virtual uint32_t getHeight() const = 0; + + /** + * @brief 检查是否有深度/模板附件 + * @return 是否有深度/模板附件 + */ + virtual bool hasDepthStencil() const = 0; + + /** + * @brief 检查是否有效 + * @return 是否有效 + */ + virtual bool isValid() const = 0; + + /** + * @brief 检查是否为默认帧缓冲(窗口帧缓冲) + * @return 是否为默认帧缓冲 + */ + virtual bool isDefault() const = 0; +}; + +} // namespace extra2d diff --git a/include/renderer/rhi/rhi_shader.h b/include/renderer/rhi/rhi_shader.h new file mode 100644 index 0000000..966fdb6 --- /dev/null +++ b/include/renderer/rhi/rhi_shader.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include + +namespace extra2d { + +/** + * @brief RHI 着色器接口 + * + * 抽象 GPU 着色器程序 + */ +class RHIShader { +public: + virtual ~RHIShader() = default; + + /** + * @brief 获取着色器类型 + * @return 着色器类型 + */ + virtual ShaderType getType() const = 0; + + /** + * @brief 检查是否已编译 + * @return 是否已编译 + */ + virtual bool isCompiled() const = 0; + + /** + * @brief 获取编译日志 + * @return 编译日志 + */ + virtual std::string getCompileLog() const = 0; + + /** + * @brief 检查是否有效 + * @return 是否有效 + */ + virtual bool isValid() const = 0; +}; + +/** + * @brief RHI 图形管线接口 + * + * 抽象 GPU 图形管线状态对象 (PSO) + */ +class RHIPipeline { +public: + virtual ~RHIPipeline() = default; + + /** + * @brief 绑定管线 + */ + virtual void bind() = 0; + + /** + * @brief 解绑管线 + */ + virtual void unbind() = 0; + + /** + * @brief 获取顶点着色器 + * @return 顶点着色器 + */ + virtual ShaderHandle getVertexShader() const = 0; + + /** + * @brief 获取片段着色器 + * @return 片段着色器 + */ + virtual ShaderHandle getFragmentShader() const = 0; + + /** + * @brief 获取顶点布局 + * @return 顶点布局 + */ + virtual const VertexLayout& getVertexLayout() const = 0; + + /** + * @brief 检查是否有效 + * @return 是否有效 + */ + virtual bool isValid() const = 0; +}; + +} // namespace extra2d diff --git a/include/renderer/rhi/rhi_types.h b/include/renderer/rhi/rhi_types.h new file mode 100644 index 0000000..14aebd5 --- /dev/null +++ b/include/renderer/rhi/rhi_types.h @@ -0,0 +1,480 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// 前向声明 +class RHIDevice; +class RHIContext; +class RHICommandList; +class RHIBuffer; +class RHITexture; +class RHIShader; +class RHIPipeline; +class RHIFramebuffer; + +/** + * @brief RHI 资源句柄基类 + */ +template class RHIHandle { +public: + RHIHandle() = default; + explicit RHIHandle(T *ptr) : ptr_(ptr) {} + + T *get() const { return ptr_; } + bool isValid() const { return ptr_ != nullptr; } + explicit operator bool() const { return isValid(); } + + bool operator==(const RHIHandle &other) const { return ptr_ == other.ptr_; } + bool operator!=(const RHIHandle &other) const { return ptr_ != other.ptr_; } + +private: + T *ptr_ = nullptr; +}; + +using BufferHandle = RHIHandle; +using TextureHandle = RHIHandle; +using ShaderHandle = RHIHandle; +using PipelineHandle = RHIHandle; +using FramebufferHandle = RHIHandle; + +/** + * @brief 缓冲区类型 + */ +enum class BufferType : uint8_t { + Vertex, // 顶点缓冲区 + Index, // 索引缓冲区 + Uniform, // Uniform 缓冲区 + Storage, // 存储缓冲区 (SSBO) + Staging // 暂存缓冲区 +}; + +/** + * @brief 缓冲区使用模式 + */ +enum class BufferUsage : uint8_t { + Static, // 静态数据,很少修改 + Dynamic, // 动态数据,每帧修改 + Stream // 流数据,每帧多次修改 +}; + +/** + * @brief 缓冲区描述 + */ +struct BufferDesc { + BufferType type = BufferType::Vertex; + BufferUsage usage = BufferUsage::Static; + uint32_t size = 0; // 缓冲区大小(字节) + const void *initialData = nullptr; // 初始数据 + + static BufferDesc vertex(uint32_t size, + BufferUsage usage = BufferUsage::Static); + static BufferDesc index(uint32_t size, + BufferUsage usage = BufferUsage::Static); + static BufferDesc uniform(uint32_t size); +}; + +/** + * @brief 纹理类型 + */ +enum class TextureType : uint8_t { + Texture2D, // 2D 纹理 + TextureCube, // 立方体贴图 + TextureArray, // 纹理数组 + RenderTarget // 渲染目标 +}; + +/** + * @brief 纹理用途 + */ +enum class TextureUsage : uint8_t { + Sampled, // 采样纹理(只读) + Storage, // 存储纹理(读写) + RenderTarget, // 渲染目标 + DepthStencil // 深度/模板目标 +}; + +/** + * @brief 纹理格式 + */ +enum class TextureFormat : uint8_t { + // 颜色格式 + R8, // 8位 红 + RG8, // 8位 RG + RGB8, // 8位 RGB + RGBA8, // 8位 RGBA + RGBA8_SRGB, // sRGB 颜色空间 + + // 深度/模板格式 + Depth16, + Depth24, + Depth32F, + Depth24Stencil8, + Depth32FStencil8, + + // 压缩格式 + BC1, // DXT1 + BC3, // DXT5 + BC5 // 3Dc +}; + +/** + * @brief 纹理过滤模式 + */ +enum class TextureFilter : uint8_t { + Nearest, // 最近邻 + Linear, // 线性 + NearestMipmapNearest, + LinearMipmapNearest, + NearestMipmapLinear, + LinearMipmapLinear +}; + +/** + * @brief 纹理环绕模式 + */ +enum class TextureWrap : uint8_t { + Repeat, // 重复 + ClampToEdge, // 边缘钳制 + ClampToBorder, // 边界钳制 + MirroredRepeat // 镜像重复 +}; + +/** + * @brief 纹理描述 + */ +struct TextureDesc { + TextureType type = TextureType::Texture2D; + TextureFormat format = TextureFormat::RGBA8; + uint32_t width = 0; + uint32_t height = 0; + uint32_t depth = 1; // 对于 3D 纹理或数组层数 + uint32_t mipLevels = 1; + TextureFilter minFilter = TextureFilter::Linear; + TextureFilter magFilter = TextureFilter::Linear; + TextureWrap wrapS = TextureWrap::Repeat; + TextureWrap wrapT = TextureWrap::Repeat; + bool renderTarget = false; // 是否可作为渲染目标 + + static TextureDesc texture2D(uint32_t width, uint32_t height, + TextureFormat format = TextureFormat::RGBA8); + static TextureDesc renderTarget2D(uint32_t width, uint32_t height, + TextureFormat format); + static TextureDesc depthStencil(uint32_t width, uint32_t height); +}; + +/** + * @brief 着色器类型 + */ +enum class ShaderType : uint8_t { + Vertex, // 顶点着色器 + Fragment, // 片段着色器 + Geometry, // 几何着色器 + Compute // 计算着色器 +}; + +/** + * @brief 着色器描述 + * + * 同时包含顶点和片段着色器源码 + */ +struct ShaderDesc { + std::string vertexSource; // 顶点着色器源码 + std::string fragmentSource; // 片段着色器源码 + std::string entryPoint = "main"; // 入口函数名 + + static ShaderDesc fromSource(const std::string &vertexSrc, + const std::string &fragmentSrc) { + ShaderDesc desc; + desc.vertexSource = vertexSrc; + desc.fragmentSource = fragmentSrc; + return desc; + } +}; + +/** + * @brief 顶点属性格式 + */ +enum class VertexFormat : uint8_t { + Float1, + Float2, + Float3, + Float4, + Int1, + Int2, + Int3, + Int4, + UInt1, + UInt2, + UInt3, + UInt4, + Byte4, + Byte4Normalized, + UByte4, + UByte4Normalized +}; + +/** + * @brief 顶点属性描述 + */ +struct VertexAttribute { + uint32_t location = 0; // 属性位置 + VertexFormat format = VertexFormat::Float3; + uint32_t offset = 0; // 在顶点结构中的偏移 + uint32_t bufferIndex = 0; // 绑定的顶点缓冲区索引 + + static uint32_t getSize(VertexFormat format); +}; + +/** + * @brief 顶点布局描述 + */ +struct VertexLayout { + std::vector attributes; + uint32_t stride = 0; // 顶点大小(字节) + + void addAttribute(uint32_t location, VertexFormat format, uint32_t offset, + uint32_t bufferIndex = 0); +}; + +/** + * @brief 索引类型 + */ +enum class IndexType : uint8_t { UInt16, UInt32 }; + +/** + * @brief 图元类型 + */ +enum class PrimitiveType : uint8_t { + Points, + Lines, + LineStrip, + Triangles, + TriangleStrip, + TriangleFan +}; + +/** + * @brief 混合因子 + */ +enum class BlendFactor : uint8_t { + Zero, + One, + SrcColor, + OneMinusSrcColor, + DstColor, + OneMinusDstColor, + SrcAlpha, + OneMinusSrcAlpha, + DstAlpha, + OneMinusDstAlpha +}; + +/** + * @brief 混合操作 + */ +enum class BlendOp : uint8_t { Add, Subtract, ReverseSubtract, Min, Max }; + +/** + * @brief 混合状态 + */ +struct BlendState { + bool enabled = false; + BlendFactor srcFactor = BlendFactor::One; + BlendFactor dstFactor = BlendFactor::Zero; + BlendOp op = BlendOp::Add; + BlendFactor srcAlphaFactor = BlendFactor::One; + BlendFactor dstAlphaFactor = BlendFactor::Zero; + BlendOp alphaOp = BlendOp::Add; + + static BlendState opaque(); + static BlendState alphaBlend(); + static BlendState additive(); +}; + +/** + * @brief 深度测试比较函数 + */ +enum class CompareFunc : uint8_t { + Never, + Less, + Equal, + LessEqual, + Greater, + NotEqual, + GreaterEqual, + Always +}; + +/** + * @brief 深度/模板状态 + */ +struct DepthStencilState { + bool depthTestEnabled = true; + bool depthWriteEnabled = true; + CompareFunc depthCompare = CompareFunc::Less; + + bool stencilEnabled = false; + // 模板测试配置(简化版) + uint8_t stencilReadMask = 0xFF; + uint8_t stencilWriteMask = 0xFF; + uint8_t stencilRef = 0; + + static DepthStencilState depthTest(); + static DepthStencilState depthTestWrite(); + static DepthStencilState depthTestNoWrite(); + static DepthStencilState noDepthTest(); +}; + +/** + * @brief 光栅化状态 + */ +struct RasterizerState { + bool cullEnabled = false; + bool cullFrontFace = false; // true = 剔除正面, false = 剔除背面 + bool frontCCW = false; // 正面是否为逆时针 + bool scissorEnabled = false; + bool wireframe = false; + + static RasterizerState cullBack(); + static RasterizerState cullFront(); + static RasterizerState noCull(); +}; + +/** + * @brief 管线描述 + */ +struct PipelineDesc { + ShaderHandle vertexShader; + ShaderHandle fragmentShader; + VertexLayout vertexLayout; + PrimitiveType primitiveType = PrimitiveType::Triangles; + BlendState blendState; + DepthStencilState depthStencilState; + RasterizerState rasterizerState; + + static PipelineDesc create(ShaderHandle vs, ShaderHandle fs, + const VertexLayout &layout); +}; + +/** + * @brief 渲染通道加载/存储操作 + */ +enum class LoadOp : uint8_t { + Load, // 加载现有内容 + Clear, // 清除为指定值 + DontCare // 不关心现有内容 +}; + +enum class StoreOp : uint8_t { + Store, // 保存结果 + DontCare // 不关心结果 +}; + +/** + * @brief 渲染通道附件描述 + */ +struct RenderPassAttachment { + TextureHandle texture; + LoadOp loadOp = LoadOp::Clear; + StoreOp storeOp = StoreOp::Store; + Color clearColor = Color::Black; + float clearDepth = 1.0f; + uint8_t clearStencil = 0; +}; + +/** + * @brief 渲染通道描述 + */ +struct RenderPassDesc { + std::vector colorAttachments; + RenderPassAttachment depthStencilAttachment; + bool hasDepthStencil = false; +}; + +/** + * @brief 视口 + */ +struct Viewport { + float x = 0.0f; + float y = 0.0f; + float width = 0.0f; + float height = 0.0f; + float minDepth = 0.0f; + float maxDepth = 1.0f; + + Viewport() = default; + Viewport(float x, float y, float w, float h) + : x(x), y(y), width(w), height(h) {} +}; + +/** + * @brief 裁剪矩形 + */ +struct ScissorRect { + int32_t x = 0; + int32_t y = 0; + int32_t width = 0; + int32_t height = 0; + + ScissorRect() = default; + ScissorRect(int32_t x, int32_t y, int32_t w, int32_t h) + : x(x), y(y), width(w), height(h) {} +}; + +/** + * @brief 清除标志 + */ +enum class ClearFlags : uint32_t { + None = 0, + Color = 1 << 0, + Depth = 1 << 1, + Stencil = 1 << 2, + All = Color | Depth | Stencil +}; + +inline ClearFlags operator|(ClearFlags a, ClearFlags b) { + return static_cast(static_cast(a) | + static_cast(b)); +} + +inline ClearFlags operator&(ClearFlags a, ClearFlags b) { + return static_cast(static_cast(a) & + static_cast(b)); +} + +inline bool hasFlag(ClearFlags flags, ClearFlags flag) { + return (static_cast(flags) & static_cast(flag)) != 0; +} + +/** + * @brief 渲染统计 + */ +struct RenderStats { + uint32_t drawCalls = 0; + uint32_t triangles = 0; + uint32_t vertices = 0; + uint32_t textureBinds = 0; + uint32_t bufferBinds = 0; + uint32_t pipelineBinds = 0; + uint32_t renderPassSwitches = 0; + + void reset() { + drawCalls = 0; + triangles = 0; + vertices = 0; + textureBinds = 0; + bufferBinds = 0; + pipelineBinds = 0; + renderPassSwitches = 0; + } +}; + +} // namespace extra2d diff --git a/include/renderer/rhi_module.h b/include/renderer/rhi_module.h new file mode 100644 index 0000000..f14f284 --- /dev/null +++ b/include/renderer/rhi_module.h @@ -0,0 +1,113 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief RHI 模块 + * + * 负责初始化和管理 RHI 后端,提供图形 API 抽象 + * 优先级:1(在 WindowModule 之后初始化) + */ +class RHIModule : public Module { + E2D_REGISTER_MODULE(RHIModule, "RHI", 1) + +public: + RHIModule(); + ~RHIModule() override; + + /** + * @brief 初始化 RHI 模块 + * @return 初始化是否成功 + */ + bool init() override; + + /** + * @brief 关闭 RHI 模块 + */ + void shutdown() override; + + /** + * @brief 获取 RHI 设备 + * @return RHI 设备指针 + */ + RHIDevice* getDevice() const { return device_.get(); } + + /** + * @brief 获取 RHI 上下文 + * @return RHI 上下文指针 + */ + RHIContext* getContext() const { return context_; } + + /** + * @brief 获取全局 RHIModule 实例 + * @return RHIModule 指针 + */ + static RHIModule* get(); + + /** + * @brief 检查 RHI 是否已初始化 + * @return 是否已初始化 + */ + bool isInitialized() const { return initialized_; } + + /** + * @brief 创建缓冲区 + * @param desc 缓冲区描述 + * @return 缓冲区对象 + */ + std::unique_ptr createBuffer(const BufferDesc& desc); + + /** + * @brief 创建纹理 + * @param desc 纹理描述 + * @return 纹理对象 + */ + std::unique_ptr createTexture(const TextureDesc& desc); + + /** + * @brief 创建着色器 + * @param desc 着色器描述 + * @return 着色器对象 + */ + std::unique_ptr createShader(const ShaderDesc& desc); + + /** + * @brief 创建图形管线 + * @param desc 管线描述 + * @return 管线对象 + */ + std::unique_ptr createPipeline(const PipelineDesc& desc); + + /** + * @brief 创建帧缓冲 + * @param desc 帧缓冲描述 + * @return 帧缓冲对象 + */ + std::unique_ptr createFramebuffer(const RenderPassDesc& desc); + + /** + * @brief 创建命令列表 + * @return 命令列表对象 + */ + std::unique_ptr createCommandList(); + +private: + /** + * @brief 窗口显示事件回调 - 在窗口显示时创建 OpenGL 上下文 + */ + void onWindowShow(); + +private: + std::unique_ptr device_; + RHIContext* context_; + bool initialized_ = false; + std::unique_ptr onShowListener_; +}; + +} // namespace extra2d diff --git a/include/renderer/shader.h b/include/renderer/shader.h index 888be6d..673bec2 100644 --- a/include/renderer/shader.h +++ b/include/renderer/shader.h @@ -1,155 +1,95 @@ #pragma once -#include -#include -#include +#include #include +#include +#include +#include #include -// 前向声明 OpenGL 类型 -typedef unsigned int GLuint; -typedef int GLint; -typedef unsigned int GLenum; - namespace extra2d { /** * @brief 着色器类 - * - * 管理 OpenGL 着色器程序的编译、链接和使用 + * + * 基于 RHI 的着色器包装类,管理着色器资源的创建和编译 * 支持从文件或源码加载 */ 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; } + /** + * @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 获取 RHI 着色器句柄 + * @return RHI 着色器句柄 + */ + ShaderHandle getHandle() const { return handle_; } + + /** + * @brief 获取 RHI 管线句柄 + * @return RHI 管线句柄 + */ + PipelineHandle getPipeline() const { return pipeline_; } + + /** + * @brief 检查是否已加载 + * @return 是否已加载 + */ + bool isLoaded() const { return handle_.isValid() && pipeline_.isValid(); } + + /** + * @brief 设置 Uniform Block 绑定槽位 + * @param name Uniform Block 名称 + * @param binding 绑定槽位 + */ + void setUniformBlock(const std::string &name, uint32_t binding); + + /** + * @brief 获取 Uniform Block 绑定槽位 + * @param name Uniform Block 名称 + * @return 绑定槽位,如果未找到返回 UINT32_MAX + */ + uint32_t getUniformBlockBinding(const std::string& name) const; 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); + /** + * @brief 添加版本声明(如果不存在) + * @param source 源码 + * @param isVertex 是否为顶点着色器 + * @return 处理后的源码 + */ + std::string addVersionIfNeeded(const std::string &source, bool isVertex); private: - GLuint program_ = 0; // OpenGL 程序对象 - std::unordered_map uniformCache_; // Uniform 位置缓存 + ShaderHandle handle_; // RHI 着色器句柄 + PipelineHandle pipeline_; // RHI 管线句柄 + + // Uniform Block 绑定映射 + std::unordered_map uniformBlockBindings_; }; } // namespace extra2d diff --git a/include/renderer/texture.h b/include/renderer/texture.h index 496e0c8..e61daa0 100644 --- a/include/renderer/texture.h +++ b/include/renderer/texture.h @@ -1,20 +1,17 @@ #pragma once +#include #include #include -#include #include #include -// 前向声明 OpenGL 类型 -typedef unsigned int GLuint; - namespace extra2d { /** * @brief 纹理类 * - * 管理 OpenGL 纹理的创建、加载和绑定 + * 基于 RHI 的纹理包装类,管理纹理资源的创建和加载 * 支持从文件或内存加载 */ class Texture : public RefCounted { @@ -55,17 +52,6 @@ public: */ bool create(int width, int height, TextureFormat format); - /** - * @brief 绑定纹理到指定槽位 - * @param slot 纹理槽位 - */ - void bind(uint32_t slot = 0) const; - - /** - * @brief 解绑纹理 - */ - void unbind() const; - /** * @brief 获取纹理宽度 * @return 宽度 @@ -85,19 +71,25 @@ public: TextureFormat getFormat() const { return format_; } /** - * @brief 获取 OpenGL 纹理对象 - * @return OpenGL 纹理对象 + * @brief 获取 RHI 纹理句柄 + * @return RHI 纹理句柄 */ - GLuint getHandle() const { return texture_; } + TextureHandle getHandle() const { return handle_; } /** * @brief 检查是否已加载 * @return 是否已加载 */ - bool isLoaded() const { return texture_ != 0; } + bool isLoaded() const { return handle_.isValid(); } private: - GLuint texture_ = 0; // OpenGL 纹理对象 + /** + * @brief 获取每个像素的字节数 + * @param format 纹理格式 + * @return 字节数 + */ + static uint32_t getBytesPerPixel(TextureFormat format); + TextureHandle handle_; // RHI 纹理句柄 int width_ = 0; // 纹理宽度 int height_ = 0; // 纹理高度 TextureFormat format_ = TextureFormat::RGBA8; // 像素格式 diff --git a/include/renderer/uniform_buffer.h b/include/renderer/uniform_buffer.h index 8e84ebd..fdfd359 100644 --- a/include/renderer/uniform_buffer.h +++ b/include/renderer/uniform_buffer.h @@ -1,8 +1,7 @@ #pragma once -#include +#include #include -#include #include namespace extra2d { @@ -10,70 +9,76 @@ namespace extra2d { /** * @brief 统一缓冲区对象 (UBO) * - * 用于高效地传递 uniform 数据到 GPU + * 基于 RHI 的 UBO 包装类,用于高效地传递 uniform 数据到 GPU * 支持 std140 布局标准 */ class UniformBuffer { public: - /** - * @brief 默认构造函数 - */ - UniformBuffer(); + /** + * @brief 默认构造函数 + */ + UniformBuffer(); - /** - * @brief 析构函数 - */ - ~UniformBuffer(); + /** + * @brief 析构函数 + */ + ~UniformBuffer(); - /** - * @brief 创建 UBO - * @param size 缓冲区大小(字节) - * @param binding 绑定槽位 - * @return 创建是否成功 - */ - bool create(uint32_t size, uint32_t binding); + /** + * @brief 创建 UBO + * @param size 缓冲区大小(字节) + * @param binding 绑定槽位 + * @return 创建是否成功 + */ + bool create(uint32_t size, uint32_t binding); - /** - * @brief 销毁 UBO - */ - void destroy(); + /** + * @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 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 绑定到指定槽位 + * @param binding 绑定槽位 + */ + void bind(uint32_t binding); - /** - * @brief 获取缓冲区大小 - * @return 缓冲区大小 - */ - uint32_t getSize() const { return size_; } + /** + * @brief 获取缓冲区大小 + * @return 缓冲区大小 + */ + uint32_t getSize() const { return size_; } - /** - * @brief 获取 OpenGL 缓冲区对象 - * @return OpenGL 缓冲区对象 - */ - GLuint getHandle() const { return ubo_; } + /** + * @brief 获取 RHI 缓冲区句柄 + * @return RHI 缓冲区句柄 + */ + BufferHandle getHandle() const { return handle_; } - /** - * @brief 检查是否有效 - * @return 是否有效 - */ - bool isValid() const { return ubo_ != 0; } + /** + * @brief 获取底层 RHI 缓冲区指针 + * @return RHI 缓冲区指针 + */ + RHIBuffer* getRHIBuffer() const; + + /** + * @brief 检查是否有效 + * @return 是否有效 + */ + bool isValid() const { return handle_.isValid(); } private: - GLuint ubo_ = 0; // OpenGL 缓冲区对象 - uint32_t size_ = 0; // 缓冲区大小 - uint32_t binding_ = 0; // 绑定槽位 + BufferHandle handle_; // RHI 缓冲区句柄 + uint32_t size_ = 0; // 缓冲区大小 + uint32_t binding_ = 0; // 绑定槽位 }; /** @@ -84,74 +89,75 @@ private: */ class UniformBufferManager { public: - /** - * @brief 默认构造函数 - */ - UniformBufferManager(); + /** + * @brief 默认构造函数 + */ + UniformBufferManager(); - /** - * @brief 析构函数 - */ - ~UniformBufferManager(); + /** + * @brief 析构函数 + */ + ~UniformBufferManager(); - // 禁止拷贝 - UniformBufferManager(const UniformBufferManager &) = delete; - UniformBufferManager &operator=(const UniformBufferManager &) = delete; + // 禁止拷贝 + UniformBufferManager(const UniformBufferManager&) = delete; + UniformBufferManager& operator=(const UniformBufferManager&) = delete; - // 允许移动 - UniformBufferManager(UniformBufferManager &&) noexcept; - UniformBufferManager &operator=(UniformBufferManager &&) noexcept; + // 允许移动 + UniformBufferManager(UniformBufferManager&&) noexcept; + UniformBufferManager& operator=(UniformBufferManager&&) noexcept; - /** - * @brief 初始化管理器 - * @return 初始化是否成功 - */ - bool initialize(); + /** + * @brief 初始化管理器 + * @return 初始化是否成功 + */ + bool initialize(); - /** - * @brief 关闭管理器 - */ - void shutdown(); + /** + * @brief 关闭管理器 + */ + void shutdown(); - /** - * @brief 获取全局 UBO - * @return 全局 UBO 指针 - */ - UniformBuffer *getGlobalUBO(); + /** + * @brief 获取全局 UBO + * @return 全局 UBO 指针 + */ + UniformBuffer* getGlobalUBO(); - /** - * @brief 获取或创建材质 UBO - * @param size 缓冲区大小 - * @return UBO 指针 - */ - UniformBuffer *acquireMaterialUBO(uint32_t size); + /** + * @brief 获取或创建材质 UBO + * @param size 缓冲区大小 + * @return UBO 指针 + */ + UniformBuffer* acquireMaterialUBO(uint32_t size); - /** - * @brief 重置材质 UBO 池 - * - * 每帧调用,重置当前索引以便复用 UBO - */ - void resetMaterialUBOs(); + /** + * @brief 重置材质 UBO 池 + * + * 每帧调用,重置当前索引以便复用 UBO + */ + void resetMaterialUBOs(); - /** - * @brief 更新全局 UBO 数据 - * @param data 数据指针 - * @param size 数据大小 - */ - void updateGlobalUBO(const void *data, uint32_t size); + /** + * @brief 更新全局 UBO 数据 + * @param data 数据指针 + * @param size 数据大小 + */ + void updateGlobalUBO(const void* data, uint32_t size); private: - // 全局 UBO(用于 viewProjection、time 等全局数据) - std::unique_ptr globalUBO_; + // 全局 UBO(用于 viewProjection、time 等全局数据) + std::unique_ptr globalUBO_; - // 材质 UBO 池 - std::vector> materialUBOPool_; - uint32_t currentUBOIndex_ = 0; + // 材质 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 + // 常量 + static constexpr uint32_t INITIAL_UBO_POOL_SIZE = 16; + static constexpr uint32_t GLOBAL_UBO_SIZE = 256; // 足够存储 viewProjection + time + screenSize + static constexpr uint32_t GLOBAL_UBO_BINDING = 0; // 全局 UBO 绑定槽位 + static constexpr uint32_t MATERIAL_UBO_BINDING = 1; // 材质 UBO 绑定槽位 }; } // namespace extra2d diff --git a/include/scene/scene_module.h b/include/scene/scene_module.h index aa6fcfc..554273a 100644 --- a/include/scene/scene_module.h +++ b/include/scene/scene_module.h @@ -20,13 +20,11 @@ public: SceneModule(); ~SceneModule() override; - // 禁止拷贝 + // 禁止拷贝和移动 SceneModule(const SceneModule &) = delete; SceneModule &operator=(const SceneModule &) = delete; - - // 允许移动 - SceneModule(SceneModule &&) noexcept; - SceneModule &operator=(SceneModule &&) noexcept; + SceneModule(SceneModule &&) = delete; + SceneModule &operator=(SceneModule &&) = delete; // Module 接口实现 bool init() override; diff --git a/include/utils/timer_module.h b/include/utils/timer_module.h index 4b7c657..8a02e83 100644 --- a/include/utils/timer_module.h +++ b/include/utils/timer_module.h @@ -51,13 +51,11 @@ public: TimerModule(); ~TimerModule() override; - // 禁止拷贝 + // 禁止拷贝和移动 TimerModule(const TimerModule &) = delete; TimerModule &operator=(const TimerModule &) = delete; - - // 允许移动 - TimerModule(TimerModule &&) noexcept; - TimerModule &operator=(TimerModule &&) noexcept; + TimerModule(TimerModule &&) = delete; + TimerModule &operator=(TimerModule &&) = delete; // Module 接口实现 bool init() override; diff --git a/shader/default.frag b/shader/default.frag index 19a4104..23bc740 100644 --- a/shader/default.frag +++ b/shader/default.frag @@ -21,9 +21,15 @@ out vec4 fragColor; * 采样纹理并与顶点颜色、色调和透明度混合 */ void main() { - // 采样纹理 + // 采样纹理(如果没有绑定纹理,texture 会返回 vec4(0,0,0,1) 或 vec4(1,1,1,1) 取决于实现) vec4 texColor = texture(uTexture, vTexCoord); + // 如果纹理采样结果是黑色或透明,使用白色作为默认值 + // 这样即使在没有纹理的情况下也能显示颜色 + if (texColor.rgb == vec3(0.0) || texColor.a < 0.01) { + texColor = vec4(1.0, 1.0, 1.0, 1.0); + } + // 混合:纹理 * 顶点颜色 * 色调 fragColor = texColor * vColor * uTintColor; diff --git a/src/app/application.cpp b/src/app/application.cpp index 1b3d2b1..5759a2d 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -63,6 +63,11 @@ void Application::initModules() { // 从注册表自动创建所有模块 modules_ = ModuleRegistry::instance().createModules(); + // 存储模块实例到注册表,以便其他模块可以通过 getModule() 获取 + for (auto &module : modules_) { + ModuleRegistry::instance().storeModuleInstance(module.get()); + } + // 初始化所有模块 for (auto &module : modules_) { if (!module->init()) { diff --git a/src/assets/assets_module.cpp b/src/assets/assets_module.cpp index b9f4bb5..eaeb614 100644 --- a/src/assets/assets_module.cpp +++ b/src/assets/assets_module.cpp @@ -410,8 +410,10 @@ bool AssetsModule::createDefaultResources() { { Ptr material = makePtr(); material->setShader(getPtr(defaultShader_)); + // 添加默认纹理到材质 + material->setTexture("uTexture", getPtr(defaultTexture_), 0); defaultMaterial_ = materials_.insert(material); - E2D_LOG_DEBUG("Created default material"); + E2D_LOG_DEBUG("Created default material with default texture"); } { diff --git a/src/module/module_registry.cpp b/src/module/module_registry.cpp index 31287ff..91b6c9a 100644 --- a/src/module/module_registry.cpp +++ b/src/module/module_registry.cpp @@ -11,7 +11,7 @@ ModuleRegistry& ModuleRegistry::instance() { std::vector> ModuleRegistry::createModules() { // 按优先级排序(值小的先初始化) std::vector sorted = registrations_; - std::sort(sorted.begin(), sorted.end(), + std::sort(sorted.begin(), sorted.end(), [](const ModuleInfo& a, const ModuleInfo& b) { return a.priority < b.priority; }); @@ -26,4 +26,21 @@ std::vector> ModuleRegistry::createModules() { return modules; } +void ModuleRegistry::storeModuleInstance(Module* module) { + if (!module) return; + + // 通过模块名称查找对应的注册信息 + for (const auto& info : registrations_) { + if (info.name == module->getName()) { + // 使用注册时存储的 type 作为键 + instances_[info.type] = module; + return; + } + } +} + +void ModuleRegistry::clearInstances() { + instances_.clear(); +} + } // namespace extra2d diff --git a/src/platform/file_module.cpp b/src/platform/file_module.cpp deleted file mode 100644 index 3c8bf2b..0000000 --- a/src/platform/file_module.cpp +++ /dev/null @@ -1,256 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#include -#define mkdir_impl(path, mode) _mkdir(path) -#elif defined(__SWITCH__) -#include -#include -// Switch 使用 ::mkdir 避免与类成员函数冲突 -#define mkdir_impl(path, mode) ::mkdir(path, mode) -#else -#include -#include -#define mkdir_impl(path, mode) mkdir(path, mode) -#endif - -#ifdef __SWITCH__ -#include -#endif - -namespace extra2d { - -FileModule::FileModule() = default; - -FileModule::~FileModule() = default; - -FileModule::FileModule(FileModule &&) noexcept = default; -FileModule &FileModule::operator=(FileModule &&) noexcept = default; - -bool FileModule::init() { - writableDir_ = SDL_GetPrefPath("Extra2D", "Extra2D"); - if (writableDir_.empty()) { - writableDir_ = "./"; - } - -#ifdef __SWITCH__ - // 初始化 Switch 的 RomFS - Result rc = romfsInit(); - if (R_SUCCEEDED(rc)) { - E2D_LOG_INFO("RomFS initialized successfully"); - } else { - E2D_LOG_WARN("romfsInit failed: {:#08X}", rc); - } -#endif - - return true; -} - -void FileModule::shutdown() { -#ifdef __SWITCH__ - // 关闭 RomFS - romfsExit(); -#endif -} - -bool FileModule::exists(const std::string &path) const { - struct stat st; - return stat(path.c_str(), &st) == 0; -} - -bool FileModule::isDir(const std::string &path) const { - struct stat st; - if (stat(path.c_str(), &st) != 0) - return false; - return (st.st_mode & S_IFDIR) != 0; -} - -FileData FileModule::read(const std::string &path) const { - FileData result; - - std::ifstream file(path, std::ios::binary | std::ios::ate); - if (!file.is_open()) { - result.error = "Cannot open file: " + path; - return result; - } - - std::streamsize size = file.tellg(); - file.seekg(0, std::ios::beg); - - result.data.resize(static_cast(size)); - if (!file.read(reinterpret_cast(result.data.data()), size)) { - result.error = "Failed to read file: " + path; - return result; - } - - result.ok = true; - return result; -} - -std::string FileModule::readString(const std::string &path) const { - std::ifstream file(path); - if (!file.is_open()) - return ""; - - std::stringstream buffer; - buffer << file.rdbuf(); - return buffer.str(); -} - -bool FileModule::write(const std::string &path, const void *data, - size_t size) const { - std::ofstream file(path, std::ios::binary); - if (!file.is_open()) - return false; - - file.write(static_cast(data), - static_cast(size)); - return file.good(); -} - -bool FileModule::writeString(const std::string &path, - const std::string &content) const { - return write(path, content.data(), content.size()); -} - -bool FileModule::append(const std::string &path, const void *data, - size_t size) const { - std::ofstream file(path, std::ios::binary | std::ios::app); - if (!file.is_open()) - return false; - - file.write(static_cast(data), - static_cast(size)); - return file.good(); -} - -bool FileModule::remove(const std::string &path) const { - return std::remove(path.c_str()) == 0; -} - -bool FileModule::mkdir(const std::string &path) const { -#ifdef _WIN32 - return mkdir_impl(path.c_str(), 0755) == 0 || errno == EEXIST; -#else - return mkdir_impl(path.c_str(), 0755) == 0 || errno == EEXIST; -#endif -} - -std::vector FileModule::listDir(const std::string &path) const { - std::vector result; - -#ifdef _WIN32 - WIN32_FIND_DATAA findData; - std::string searchPath = path + "\\*"; - HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData); - - if (hFind == INVALID_HANDLE_VALUE) - return result; - - do { - std::string name = findData.cFileName; - if (name == "." || name == "..") - continue; - - FileInfo info; - info.name = name; - info.path = join(path, name); - info.isDir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; - if (!info.isDir) { - info.size = static_cast(findData.nFileSizeLow) | - (static_cast(findData.nFileSizeHigh) << 32); - } - result.push_back(info); - } while (FindNextFileA(hFind, &findData)); - - FindClose(hFind); -#else - DIR *dir = opendir(path.c_str()); - if (!dir) - return result; - - struct dirent *entry; - while ((entry = readdir(dir)) != nullptr) { - std::string name = entry->d_name; - if (name == "." || name == "..") - continue; - - FileInfo info; - info.name = name; - info.path = join(path, name); - info.isDir = isDir(info.path); - if (!info.isDir) { - info.size = fileSize(info.path); - } - result.push_back(info); - } - - closedir(dir); -#endif - - return result; -} - -int64 FileModule::fileSize(const std::string &path) const { - struct stat st; - if (stat(path.c_str(), &st) != 0) - return -1; - return static_cast(st.st_size); -} - -std::string FileModule::ext(const std::string &path) const { - size_t pos = path.find_last_of('.'); - if (pos == std::string::npos || pos == 0) - return ""; - - size_t lastSep = path.find_last_of("/\\"); - if (lastSep != std::string::npos && pos < lastSep) - return ""; - - return path.substr(pos + 1); -} - -std::string FileModule::fileName(const std::string &path) const { - size_t pos = path.find_last_of("/\\"); - if (pos == std::string::npos) - return path; - return path.substr(pos + 1); -} - -std::string FileModule::dirName(const std::string &path) const { - size_t pos = path.find_last_of("/\\"); - if (pos == std::string::npos) - return "."; - if (pos == 0) - return "/"; - return path.substr(0, pos); -} - -std::string FileModule::join(const std::string &a, const std::string &b) const { - if (a.empty()) - return b; - if (b.empty()) - return a; - - char last = a.back(); - if (last == '/' || last == '\\') { - return a + b; - } - return a + "/" + b; -} - -std::string FileModule::writableDir() const { return writableDir_; } - -std::string FileModule::assetPath(const std::string &relPath) const { - if (assetRoot_.empty()) - return relPath; - return join(assetRoot_, relPath); -} - -} // namespace extra2d diff --git a/src/platform/input_module.cpp b/src/platform/input_module.cpp index 6e830a1..590023c 100644 --- a/src/platform/input_module.cpp +++ b/src/platform/input_module.cpp @@ -12,64 +12,6 @@ InputModule::~InputModule() { shutdown(); } -InputModule::InputModule(InputModule&& other) noexcept - : mouseX_(other.mouseX_) - , mouseY_(other.mouseY_) - , mouseWheel_(other.mouseWheel_) - , activeTouches_(std::move(other.activeTouches_)) - , endedTouches_(std::move(other.endedTouches_)) - , onKeyDown_(std::move(other.onKeyDown_)) - , onKeyUp_(std::move(other.onKeyUp_)) - , onMouseDown_(std::move(other.onMouseDown_)) - , onMouseUp_(std::move(other.onMouseUp_)) - , onTouchBegan_(std::move(other.onTouchBegan_)) - , onTouchMoved_(std::move(other.onTouchMoved_)) - , onTouchEnded_(std::move(other.onTouchEnded_)) { - std::memcpy(keyState_.data(), other.keyState_.data(), KEY_COUNT); - std::memcpy(keyPrev_.data(), other.keyPrev_.data(), KEY_COUNT); - std::memcpy(mouseState_.data(), other.mouseState_.data(), static_cast(MouseBtn::Count)); - std::memcpy(mousePrev_.data(), other.mousePrev_.data(), static_cast(MouseBtn::Count)); - std::memcpy(gamepads_, other.gamepads_, sizeof(gamepads_)); - std::memcpy(padState_, other.padState_, sizeof(padState_)); - std::memcpy(padPrev_, other.padPrev_, sizeof(padPrev_)); - - for (int32 i = 0; i < MAX_GAMEPADS; ++i) { - other.gamepads_[i] = nullptr; - } -} - -InputModule& InputModule::operator=(InputModule&& other) noexcept { - if (this != &other) { - shutdown(); - - mouseX_ = other.mouseX_; - mouseY_ = other.mouseY_; - mouseWheel_ = other.mouseWheel_; - activeTouches_ = std::move(other.activeTouches_); - endedTouches_ = std::move(other.endedTouches_); - onKeyDown_ = std::move(other.onKeyDown_); - onKeyUp_ = std::move(other.onKeyUp_); - onMouseDown_ = std::move(other.onMouseDown_); - onMouseUp_ = std::move(other.onMouseUp_); - onTouchBegan_ = std::move(other.onTouchBegan_); - onTouchMoved_ = std::move(other.onTouchMoved_); - onTouchEnded_ = std::move(other.onTouchEnded_); - - std::memcpy(keyState_.data(), other.keyState_.data(), KEY_COUNT); - std::memcpy(keyPrev_.data(), other.keyPrev_.data(), KEY_COUNT); - std::memcpy(mouseState_.data(), other.mouseState_.data(), static_cast(MouseBtn::Count)); - std::memcpy(mousePrev_.data(), other.mousePrev_.data(), static_cast(MouseBtn::Count)); - std::memcpy(gamepads_, other.gamepads_, sizeof(gamepads_)); - std::memcpy(padState_, other.padState_, sizeof(padState_)); - std::memcpy(padPrev_, other.padPrev_, sizeof(padPrev_)); - - for (int32 i = 0; i < MAX_GAMEPADS; ++i) { - other.gamepads_[i] = nullptr; - } - } - return *this; -} - bool InputModule::init() { std::memset(keyState_.data(), 0, KEY_COUNT); std::memset(keyPrev_.data(), 0, KEY_COUNT); diff --git a/src/platform/window_module.cpp b/src/platform/window_module.cpp index 6eb3d5a..0b2e316 100644 --- a/src/platform/window_module.cpp +++ b/src/platform/window_module.cpp @@ -4,39 +4,12 @@ #include #include -// OpenGL ES 3.2 with GLAD -#include - namespace extra2d { WindowModule::WindowModule() = default; WindowModule::~WindowModule() { shutdown(); } -WindowModule::WindowModule(WindowModule &&other) noexcept - : window_(other.window_), glContext_(other.glContext_), - shouldClose_(other.shouldClose_), onClose_(std::move(other.onClose_)), - onResize_(std::move(other.onResize_)), - configListener_(std::move(other.configListener_)) { - other.window_ = nullptr; - other.glContext_ = nullptr; -} - -WindowModule &WindowModule::operator=(WindowModule &&other) noexcept { - if (this != &other) { - shutdown(); - window_ = other.window_; - glContext_ = other.glContext_; - shouldClose_ = other.shouldClose_; - onClose_ = std::move(other.onClose_); - onResize_ = std::move(other.onResize_); - configListener_ = std::move(other.configListener_); - other.window_ = nullptr; - other.glContext_ = nullptr; - } - return *this; -} - bool WindowModule::init() { if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) { E2D_LOG_ERROR("Failed to initialize SDL video: {}", SDL_GetError()); @@ -58,7 +31,10 @@ bool WindowModule::init() { } void WindowModule::shutdown() { - destroyGLContext(); + // 清理事件监听器 + configListener_.reset(); + renderPresentListener_.reset(); + if (window_) { SDL_DestroyWindow(window_); window_ = nullptr; @@ -163,16 +139,10 @@ void WindowModule::onModuleConfig(const AppConfig &config) { cfg.resizable = config.resizable; if (create(cfg)) { - // 创建 OpenGL 上下文 - if (!createGLContext()) { - E2D_LOG_ERROR("Failed to create OpenGL context"); - return; - } - - // GL 上下文创建完成后,再发送窗口显示事件 - // 这样渲染模块可以在收到事件时安全地使用 GL 函数 + // 发送窗口显示事件 + // RHI 模块会监听此事件并创建 OpenGL 上下文 events::OnShow::emit(); - + setVisible(true); } } @@ -240,56 +210,9 @@ bool WindowModule::isVisible() const { return (flags & SDL_WINDOW_SHOWN) != 0; } -bool WindowModule::createGLContext() { - if (!window_) { - return false; - } - - // 创建 OpenGL ES 3.2 上下文 - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); - - glContext_ = SDL_GL_CreateContext(window_); - if (!glContext_) { - E2D_LOG_ERROR("Failed to create OpenGL ES context: {}", SDL_GetError()); - return false; - } - - // 初始化 GLAD - if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) { - E2D_LOG_ERROR("Failed to initialize GLAD"); - SDL_GL_DeleteContext(glContext_); - glContext_ = nullptr; - return false; - } - - E2D_LOG_INFO("OpenGL ES context created: {}.{}", GLVersion.major, - GLVersion.minor); - - return true; -} - -void WindowModule::destroyGLContext() { - if (glContext_) { - SDL_GL_DeleteContext(glContext_); - glContext_ = nullptr; - } -} - -void WindowModule::swapBuffers() { - if (window_) { - SDL_GL_SwapWindow(window_); - } -} - void WindowModule::onRenderPresent() { - swapBuffers(); + // 交换缓冲区由 RHI 模块处理 + // 这里可以触发呈现事件 } -void *WindowModule::getGLContext() const { return glContext_; } - } // namespace extra2d diff --git a/src/renderer/command_queue.cpp b/src/renderer/command_queue.cpp new file mode 100644 index 0000000..2ed1f08 --- /dev/null +++ b/src/renderer/command_queue.cpp @@ -0,0 +1,435 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace extra2d { + +// ======================================== +// CommandSorter 实现 +// ======================================== + +uint32_t CommandSorter::addCommand(const DrawCommand &cmd) { + uint32_t index = static_cast(commands_.size()); + commands_.push_back(cmd); + sortedIndices_.push_back(index); + return index; +} + +void CommandSorter::sort() { + // 使用稳定排序保持相同键的命令顺序 + std::stable_sort(sortedIndices_.begin(), sortedIndices_.end(), + [this](uint32_t a, uint32_t b) { + return commands_[a].key < commands_[b].key; + }); +} + +void CommandSorter::clear() { + commands_.clear(); + sortedIndices_.clear(); +} + +// ======================================== +// CommandBatcher 实现 +// ======================================== + +void CommandBatcher::process(const CommandSorter &sorter) { + clear(); + sorter_ = &sorter; + + if (sorter.getCount() == 0) { + return; + } + + CommandBatch currentBatch; + currentBatch.startIndex = 0; + currentBatch.count = 1; + + const auto &firstCmd = sorter.getCommand(0); + currentBatch.pipeline = firstCmd.pipeline; + currentBatch.textureCount = firstCmd.textureCount; + for (uint32_t i = 0; i < firstCmd.textureCount; ++i) { + currentBatch.textures[i] = firstCmd.textures[i]; + } + + // 遍历所有命令,合并兼容的命令 + for (uint32_t i = 1; i < sorter.getCount(); ++i) { + const auto &cmd = sorter.getCommand(i); + + if (currentBatch.isCompatibleWith(cmd)) { + // 兼容,加入当前批次 + currentBatch.count++; + } else { + // 不兼容,保存当前批次并创建新批次 + batches_.push_back(currentBatch); + + currentBatch.startIndex = i; + currentBatch.count = 1; + currentBatch.pipeline = cmd.pipeline; + currentBatch.textureCount = cmd.textureCount; + for (uint32_t j = 0; j < cmd.textureCount; ++j) { + currentBatch.textures[j] = cmd.textures[j]; + } + } + } + + // 保存最后一个批次 + batches_.push_back(currentBatch); +} + +const DrawCommand &CommandBatcher::getCommand(uint32_t batchIndex, + uint32_t cmdIndex) const { + const auto &batch = batches_[batchIndex]; + return sorter_->getCommand(batch.startIndex + cmdIndex); +} + +void CommandBatcher::clear() { + batches_.clear(); + sorter_ = nullptr; +} + +// ======================================== +// CommandQueue 实现 +// ======================================== + +CommandQueue::CommandQueue() = default; + +CommandQueue::~CommandQueue() { shutdown(); } + +CommandQueue::CommandQueue(CommandQueue &&other) noexcept + : sorter_(std::move(other.sorter_)), batcher_(std::move(other.batcher_)), + context_(other.context_), commandList_(std::move(other.commandList_)), + globalUBOData_(other.globalUBOData_), + materialUBOData_(std::move(other.materialUBOData_)), + nextMaterialId_(other.nextMaterialId_), + materialIds_(std::move(other.materialIds_)) { + other.context_ = nullptr; + other.globalUBOData_ = {}; + other.nextMaterialId_ = 1; +} + +CommandQueue &CommandQueue::operator=(CommandQueue &&other) noexcept { + if (this != &other) { + shutdown(); + + sorter_ = std::move(other.sorter_); + batcher_ = std::move(other.batcher_); + context_ = other.context_; + commandList_ = std::move(other.commandList_); + globalUBOData_ = other.globalUBOData_; + materialUBOData_ = std::move(other.materialUBOData_); + nextMaterialId_ = other.nextMaterialId_; + materialIds_ = std::move(other.materialIds_); + + other.context_ = nullptr; + other.globalUBOData_ = {}; + other.nextMaterialId_ = 1; + } + return *this; +} + +bool CommandQueue::initialize() { + // 获取 RHI 模块 + auto *rhiModule = RHIModule::get(); + if (!rhiModule) { + E2D_LOG_ERROR("RHIModule not available"); + return false; + } + + auto *device = rhiModule->getDevice(); + if (!device) { + E2D_LOG_ERROR("RHIDevice not available"); + return false; + } + + context_ = rhiModule->getContext(); + if (!context_) { + E2D_LOG_ERROR("RHIContext not available"); + return false; + } + + // 创建命令列表 + commandList_ = device->createCommandList(); + if (!commandList_) { + E2D_LOG_ERROR("Failed to create command list"); + return false; + } + + // 预分配材质 UBO 数据缓冲区 + materialUBOData_.reserve(1024 * 1024); // 1MB + + E2D_LOG_INFO("CommandQueue initialized"); + return true; +} + +void CommandQueue::shutdown() { + commandList_.reset(); + context_ = nullptr; + materialUBOData_.clear(); + E2D_LOG_INFO("CommandQueue shutdown"); +} + +void CommandQueue::beginFrame() { + sorter_.clear(); + batcher_.clear(); + materialIds_.clear(); + nextMaterialId_ = 1; + materialUBOData_.clear(); + + // 开始录制命令 + if (commandList_) { + commandList_->begin(); + } +} + +void CommandQueue::endFrame() { + // 结束录制 + if (commandList_) { + commandList_->end(); + } +} + +uint32_t CommandQueue::getMaterialId(Material *material) { + auto it = materialIds_.find(material); + if (it != materialIds_.end()) { + return it->second; + } + + uint32_t id = nextMaterialId_++; + materialIds_[material] = id; + return id; +} + +void CommandQueue::submitDraw(Ptr material, Ptr mesh, + const struct Transform &transform, + const Color &color) { + if (!material || !mesh || !material->getShader()) { + return; + } + + DrawCommand cmd; + + // 构建排序键 + uint32_t materialId = getMaterialId(material.get()); + cmd.key = DrawKey::make(materialId, 0, 0); + + // 设置管线 + cmd.pipeline = material->getPipeline(); + + // 设置网格数据 + cmd.vertexBuffer = mesh->getVertexBuffer(); + cmd.indexBuffer = mesh->getIndexBuffer(); + cmd.vertexCount = mesh->getVertexCount(); + cmd.indexCount = mesh->getIndexCount(); + + // 设置变换和颜色 + float matrixData[16]; + transform.toMatrix(matrixData); + cmd.modelMatrix = glm::make_mat4(matrixData); + cmd.color = color; + + // 设置纹理 + const auto &textures = material->getTextures(); + cmd.textureCount = + static_cast(std::min(textures.size(), size_t(8))); + for (uint32_t i = 0; i < cmd.textureCount; ++i) { + if (textures[i].texture) { + cmd.textures[i] = textures[i].texture->getHandle(); + } + } + + // 分配材质 UBO 数据 + uint32_t materialDataSize = material->getDataSize(); + if (materialDataSize > 0) { + uint32_t uboOffset = static_cast(materialUBOData_.size()); + materialUBOData_.resize(uboOffset + materialDataSize); + std::memcpy(materialUBOData_.data() + uboOffset, material->getData(), + materialDataSize); + cmd.materialUBOSize = materialDataSize; + // 注意:实际的 UBO 句柄需要在执行时从 UBO 管理器获取 + } + + sorter_.addCommand(cmd); +} + +void CommandQueue::submitDrawInstanced(Ptr material, Ptr mesh, + uint32_t instanceCount) { + if (!material || !mesh || !material->getShader() || instanceCount == 0) { + return; + } + + DrawCommand cmd; + + // 构建排序键 + uint32_t materialId = getMaterialId(material.get()); + cmd.key = DrawKey::make(materialId, 0, 0); + + // 设置管线 + cmd.pipeline = material->getPipeline(); + + // 设置网格数据 + cmd.vertexBuffer = mesh->getVertexBuffer(); + cmd.indexBuffer = mesh->getIndexBuffer(); + cmd.vertexCount = mesh->getVertexCount(); + cmd.indexCount = mesh->getIndexCount(); + cmd.instanceCount = instanceCount; + + // 设置纹理 + const auto &textures = material->getTextures(); + cmd.textureCount = + static_cast(std::min(textures.size(), size_t(8))); + for (uint32_t i = 0; i < cmd.textureCount; ++i) { + if (textures[i].texture) { + cmd.textures[i] = textures[i].texture->getHandle(); + } + } + + // 材质 UBO + uint32_t materialDataSize = material->getDataSize(); + if (materialDataSize > 0) { + uint32_t uboOffset = static_cast(materialUBOData_.size()); + materialUBOData_.resize(uboOffset + materialDataSize); + std::memcpy(materialUBOData_.data() + uboOffset, material->getData(), + materialDataSize); + cmd.materialUBOSize = materialDataSize; + } + + sorter_.addCommand(cmd); +} + +void CommandQueue::submitClear(const Color &color, uint32_t flags) { + // 清除命令直接执行,不加入排序队列 + if (!commandList_) { + return; + } + + commandList_->clear(static_cast(flags), color, 1.0f, 0); +} + +void CommandQueue::setViewport(int32_t x, int32_t y, int32_t width, + int32_t height) { + if (!commandList_) { + return; + } + + Viewport viewport; + viewport.x = static_cast(x); + viewport.y = static_cast(y); + viewport.width = static_cast(width); + viewport.height = static_cast(height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + commandList_->setViewport(viewport); +} + +void CommandQueue::execute() { + if (!commandList_) { + E2D_LOG_ERROR("CommandQueue::execute: commandList is null"); + return; + } + + // 排序命令 + sorter_.sort(); + + // 批处理 + batcher_.process(sorter_); + + // 执行批次 + for (uint32_t i = 0; i < batcher_.getBatchCount(); ++i) { + executeBatch(i, batcher_.getBatch(i)); + } + + // 提交命令到 GPU + commandList_->submit(); +} + +void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch) { + if (!commandList_) { + return; + } + + // 绑定管线 + if (batch.pipeline.isValid()) { + commandList_->setPipeline(batch.pipeline.get()); + } else { + E2D_LOG_WARN("Batch has no valid pipeline!"); + } + + // 绑定纹理 + if (batch.textureCount > 0) { + for (uint32_t i = 0; i < batch.textureCount; ++i) { + if (batch.textures[i].isValid()) { + commandList_->setTexture(i, batch.textures[i].get()); + } + } + } else { + // 如果没有纹理,绑定默认的白色纹理 + auto* rhiModule = RHIModule::get(); + if (rhiModule && rhiModule->getDevice()) { + // 使用默认纹理(白色像素) + // 注意:这里应该使用 AssetsModule 的默认纹理 + // 暂时跳过,因为我们需要访问 AssetsModule + } + } + + // 执行批次中的所有命令 + for (uint32_t i = 0; i < batch.count; ++i) { + const auto &cmd = batcher_.getCommand(batchIndex, i); + + // 绑定顶点缓冲区 + if (cmd.vertexBuffer.isValid()) { + commandList_->setVertexBuffer(0, cmd.vertexBuffer.get(), 0); + } else { + E2D_LOG_WARN("Draw command has no valid vertex buffer!"); + } + + // 绑定索引缓冲区(如果有) + if (cmd.isIndexed() && cmd.indexBuffer.isValid()) { + commandList_->setIndexBuffer(cmd.indexBuffer.get(), IndexType::UInt16, 0); + } + + // 设置 shader uniform 变量 + auto* glCmdList = dynamic_cast(commandList_.get()); + if (glCmdList) { + // 设置模型矩阵 + glCmdList->setUniform("uModelMatrix", cmd.modelMatrix); + // 设置颜色 + glCmdList->setUniform("uColor", cmd.color); + // 设置 view projection 矩阵 + Mat4 viewProj = glm::ortho(0.0f, 1280.0f, 0.0f, 720.0f, -1.0f, 1.0f); + glCmdList->setUniform("uViewProjection", viewProj); + // 设置色调颜色 + glCmdList->setUniform("uTintColor", Color::White); + // 设置透明度 + glCmdList->setUniform("uOpacity", 1.0f); + } + + // 绘制 + if (cmd.isInstanced()) { + if (cmd.isIndexed()) { + commandList_->drawIndexed(cmd.indexCount, 0, 0, cmd.instanceCount, 0); + } else { + commandList_->draw(cmd.vertexCount, 0, cmd.instanceCount, 0); + } + } else { + if (cmd.isIndexed()) { + commandList_->drawIndexed(cmd.indexCount, 0, 0, 1, 0); + } else { + commandList_->draw(cmd.vertexCount, 0, 1, 0); + } + } + } +} + +} // namespace extra2d diff --git a/src/renderer/material.cpp b/src/renderer/material.cpp index 02da2bd..af54c70 100644 --- a/src/renderer/material.cpp +++ b/src/renderer/material.cpp @@ -117,6 +117,17 @@ void Material::setShader(Ptr shader) { shader_ = shader; } +/** + * @brief 获取 RHI 管线句柄 + * @return RHI 管线句柄 + */ +PipelineHandle Material::getPipeline() const { + if (shader_) { + return shader_->getPipeline(); + } + return PipelineHandle{}; +} + /** * @brief 设置 float 参数 * @param name 参数名称 @@ -194,14 +205,14 @@ void Material::setMat4(const std::string& name, const float* value) { /** * @brief 设置纹理 * @param uniformName 着色器中的采样器 uniform 名称 - * @param texture 纹理句柄 + * @param texture 纹理 * @param slot 纹理槽位(0-15) */ -void Material::setTexture(const std::string& uniformName, Handle texture, uint32_t slot) { +void Material::setTexture(const std::string& uniformName, Ptr texture, uint32_t slot) { // 查找是否已存在相同名称的纹理 for (auto& texSlot : textures_) { if (texSlot.uniformName == uniformName) { - texSlot.handle = texture; + texSlot.texture = texture; texSlot.slot = slot; return; } @@ -218,31 +229,4 @@ void Material::clearTextures() { textures_.clear(); } -/** - * @brief 应用材质 - * - * 绑定着色器、上传 UBO 数据 - * @param uboManager UBO 管理器 - */ -void Material::apply(UniformBufferManager& uboManager) { - if (!shader_) return; - - // 绑定着色器 - shader_->bind(); - - // 设置 Uniform Block 绑定 - shader_->setUniformBlock("GlobalUBO", GLOBAL_UBO_BINDING); - shader_->setUniformBlock("MaterialUBO", MATERIAL_UBO_BINDING); - shader_->setUniformBlock("InstanceUBO", INSTANCE_UBO_BINDING); - - // 上传材质数据到 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); - } - } -} - } // namespace extra2d diff --git a/src/renderer/mesh.cpp b/src/renderer/mesh.cpp index ad23e7f..a2bf0c4 100644 --- a/src/renderer/mesh.cpp +++ b/src/renderer/mesh.cpp @@ -1,171 +1,165 @@ #include +#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; - } + // BufferHandle 是轻量级句柄,不需要显式释放 + // 实际的缓冲区资源由 RHI 设备管理 + vertexBuffer_ = BufferHandle(); + indexBuffer_ = BufferHandle(); } -void Mesh::setVertices(const Vertex* vertices, uint32_t count) { - if (vao_ == 0) { - glGenVertexArrays(1, &vao_); - glGenBuffers(1, &vbo_); - - E2D_LOG_INFO("Created VAO: {}, VBO: {}", vao_, vbo_); +void Mesh::setVertices(const Vertex *vertices, uint32_t count) { + if (!vertices || count == 0) + return; + + // 获取 RHI 模块 + auto *rhiModule = RHIModule::get(); + if (!rhiModule) { + E2D_LOG_ERROR("RHIModule not available"); + return; + } + + auto *device = rhiModule->getDevice(); + if (!device) { + E2D_LOG_ERROR("RHIDevice not available"); + return; + } + + uint32_t dataSize = count * sizeof(Vertex); + + // 如果缓冲区已存在且容量足够,更新数据 + if (vertexBuffer_.isValid() && vertexCapacity_ >= count) { + // 通过句柄获取缓冲区指针并更新数据 + // 注意:这里假设可以通过某种方式获取缓冲区指针 + // 实际实现可能需要通过 RHI 命令列表来更新 + E2D_LOG_WARN("Vertex buffer update not implemented yet"); + } else { + // 需要创建新缓冲区 + vertexBuffer_ = BufferHandle(); + + // 创建顶点缓冲区描述 + BufferDesc desc = BufferDesc::vertex(dataSize, BufferUsage::Dynamic); + desc.initialData = vertices; + + // 创建缓冲区 + auto buffer = device->createBuffer(desc); + if (!buffer) { + E2D_LOG_ERROR("Failed to create vertex buffer"); + return; } - - 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; - - E2D_LOG_INFO("Set {} vertices for VAO {}", count, vao_); + + // 获取缓冲区的句柄 + // 注意:createBuffer 返回的是 unique_ptr,我们需要存储句柄 + // 这里直接使用缓冲区指针作为句柄,然后释放所有权让 RHI 内部管理 + vertexBuffer_ = BufferHandle(buffer.release()); + + vertexCapacity_ = count; + } + + vertexCount_ = count; + E2D_LOG_INFO("Set {} vertices", count); } -void Mesh::setIndices(const uint16_t* indices, uint32_t count) { - if (vao_ == 0) return; - - if (ibo_ == 0) { - glGenBuffers(1, &ibo_); +void Mesh::setIndices(const uint16_t *indices, uint32_t count) { + if (!indices || count == 0) + return; + + // 获取 RHI 模块 + auto *rhiModule = RHIModule::get(); + if (!rhiModule) { + E2D_LOG_ERROR("RHIModule not available"); + return; + } + + auto *device = rhiModule->getDevice(); + if (!device) { + E2D_LOG_ERROR("RHIDevice not available"); + return; + } + + uint32_t dataSize = count * sizeof(uint16_t); + + // 如果缓冲区已存在且容量足够,更新数据 + if (indexBuffer_.isValid() && indexCapacity_ >= count) { + E2D_LOG_WARN("Index buffer update not implemented yet"); + } else { + // 需要创建新缓冲区 + indexBuffer_ = BufferHandle(); + + // 创建索引缓冲区描述 + BufferDesc desc = BufferDesc::index(dataSize, BufferUsage::Static); + desc.initialData = indices; + + // 创建缓冲区 + auto buffer = device->createBuffer(desc); + if (!buffer) { + E2D_LOG_ERROR("Failed to create index buffer"); + return; } - - 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; + + // 获取缓冲区的句柄 + indexBuffer_ = BufferHandle(buffer.release()); + + indexCapacity_ = count; + } + + indexCount_ = count; + E2D_LOG_INFO("Set {} indices", 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::updateVertices(const Vertex *vertices, uint32_t count, + uint32_t offset) { + if (!vertices || count == 0 || !vertexBuffer_.isValid()) + return; + + if (offset + count > vertexCount_) { + E2D_LOG_WARN("Vertex update out of bounds"); + return; + } + + // 获取 RHI 模块 + auto *rhiModule = RHIModule::get(); + if (!rhiModule) + return; + + auto *device = rhiModule->getDevice(); + if (!device) + return; + + // 通过句柄获取缓冲区并更新数据 + // 注意:这里需要 RHI 提供更新缓冲区的方法 + // 暂时记录为未实现 + E2D_LOG_WARN("updateVertices not fully implemented yet"); } -void Mesh::bind() const { - if (vao_ != 0) { - glBindVertexArray(vao_); - } -} +Ptr Mesh::createQuad(const Vec2 &size, const Rect &uv) { + Ptr mesh = makePtr(); -void Mesh::unbind() const { - glBindVertexArray(0); -} + float halfW = size.x * 0.5f; + float halfH = size.y * 0.5f; -void Mesh::draw() const { - if (vao_ == 0) return; - - // 检查 OpenGL 错误 - GLenum err = glGetError(); - if (err != GL_NO_ERROR) { - static bool loggedOnce = false; - if (!loggedOnce) { - E2D_LOG_ERROR("OpenGL error before draw: {}", err); - loggedOnce = true; - } - } - - if (indexCount_ > 0) { - glDrawElements(GL_TRIANGLES, indexCount_, GL_UNSIGNED_SHORT, nullptr); - } else { - glDrawArrays(GL_TRIANGLES, 0, vertexCount_); - } - - // 检查绘制后的错误 - err = glGetError(); - if (err != GL_NO_ERROR) { - static bool loggedOnce2 = false; - if (!loggedOnce2) { - E2D_LOG_ERROR("OpenGL error after draw: {}", err); - loggedOnce2 = true; - } - } -} + 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} // 左上 + }; -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); - } -} + uint16_t indices[6] = { + 0, 1, 2, // 第一个三角形 + 0, 2, 3 // 第二个三角形 + }; -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; + mesh->setVertices(vertices, 4); + mesh->setIndices(indices, 6); + + return mesh; } } // namespace extra2d diff --git a/src/renderer/render_graph.cpp b/src/renderer/render_graph.cpp new file mode 100644 index 0000000..9d6ee6e --- /dev/null +++ b/src/renderer/render_graph.cpp @@ -0,0 +1,334 @@ +#include +#include +#include + +namespace extra2d { + +// ======================================== +// RenderPassContext 实现 +// ======================================== + +TextureHandle +RenderPassContext::getTexture(RenderGraphResourceHandle handle) const { + if (graph) { + return graph->getTexture(handle); + } + return TextureHandle{}; +} + +// ======================================== +// RenderGraphBuilder 实现 +// ======================================== + +RenderGraphResourceHandle +RenderGraphBuilder::createTexture(const RenderGraphTextureDesc &desc) { + // 实际的资源创建由 RenderGraph 管理 + // 这里只记录描述,返回临时句柄 + textureDescs_.push_back(desc); + RenderGraphResourceHandle handle; + handle.index = static_cast(textureDescs_.size()) - 1; + handle.type = RenderGraphResourceType::Texture; + return handle; +} + +void RenderGraphBuilder::readResource(RenderGraphResourceHandle handle) { + // 记录资源读取依赖 + // 用于后续的依赖分析和资源生命周期管理 +} + +void RenderGraphBuilder::writeResource(RenderGraphResourceHandle handle) { + // 记录资源写入依赖 +} + +void RenderGraphBuilder::setRenderTarget( + const RenderGraphRenderTargetDesc &desc) { + // 设置渲染目标 +} + +// ======================================== +// RenderGraph 实现 +// ======================================== + +RenderGraph::RenderGraph() = default; + +RenderGraph::~RenderGraph() { shutdown(); } + +RenderGraph::RenderGraph(RenderGraph &&other) noexcept + : passes_(std::move(other.passes_)), + executionOrder_(std::move(other.executionOrder_)), + textures_(std::move(other.textures_)), + nextResourceId_(other.nextResourceId_), + builder_(std::move(other.builder_)), + commandQueue_(std::move(other.commandQueue_)), + outputWidth_(other.outputWidth_), outputHeight_(other.outputHeight_), + frameIndex_(other.frameIndex_), compiled_(other.compiled_) { + other.nextResourceId_ = 1; + other.outputWidth_ = 1280; + other.outputHeight_ = 720; + other.frameIndex_ = 0; + other.compiled_ = false; +} + +RenderGraph &RenderGraph::operator=(RenderGraph &&other) noexcept { + if (this != &other) { + shutdown(); + + passes_ = std::move(other.passes_); + executionOrder_ = std::move(other.executionOrder_); + textures_ = std::move(other.textures_); + nextResourceId_ = other.nextResourceId_; + builder_ = std::move(other.builder_); + commandQueue_ = std::move(other.commandQueue_); + outputWidth_ = other.outputWidth_; + outputHeight_ = other.outputHeight_; + frameIndex_ = other.frameIndex_; + compiled_ = other.compiled_; + + other.nextResourceId_ = 1; + other.outputWidth_ = 1280; + other.outputHeight_ = 720; + other.frameIndex_ = 0; + other.compiled_ = false; + } + return *this; +} + +bool RenderGraph::initialize() { + if (!commandQueue_.initialize()) { + E2D_LOG_ERROR("Failed to initialize CommandQueue"); + return false; + } + + E2D_LOG_INFO("RenderGraph initialized"); + return true; +} + +void RenderGraph::shutdown() { + destroyResources(); + passes_.clear(); + executionOrder_.clear(); + commandQueue_.shutdown(); + compiled_ = false; + + E2D_LOG_INFO("RenderGraph shutdown"); +} + +RenderPass *RenderGraph::addPass(std::unique_ptr pass) { + if (!pass) + return nullptr; + + RenderPass *ptr = pass.get(); + passes_.push_back(std::move(pass)); + compiled_ = false; // 添加新通道需要重新编译 + + return ptr; +} + +RenderPass *RenderGraph::addLambdaPass( + const std::string &name, + std::function executeFunc) { + // 创建一个简单的 lambda 渲染通道 + class LambdaRenderPass : public RenderPass { + public: + std::string name; + std::function func; + + LambdaRenderPass(const std::string &n, + std::function f) + : name(n), func(f) {} + + const char *getName() const override { return name.c_str(); } + + void execute(const RenderPassContext &ctx) override { + if (func) { + func(ctx); + } + } + }; + + return addPass(std::make_unique(name, executeFunc)); +} + +bool RenderGraph::compile() { + if (compiled_) + return true; + + // 清空之前的编译结果 + executionOrder_.clear(); + + // 简单的编译策略:按添加顺序执行 + // 实际实现中应该进行依赖分析和拓扑排序 + for (auto &pass : passes_) { + if (pass->isEnabled()) { + executionOrder_.push_back(pass.get()); + } + } + + // 声明资源 + for (auto *pass : executionOrder_) { + builder_.setCurrentPass(pass); + pass->declareResources(builder_); + } + + // 创建资源 + createResources(); + + compiled_ = true; + E2D_LOG_INFO("RenderGraph compiled with {} passes", executionOrder_.size()); + + return true; +} + +void RenderGraph::execute(float deltaTime) { + if (!compiled_) { + if (!compile()) { + E2D_LOG_ERROR("Failed to compile RenderGraph"); + return; + } + } + + // 注意:beginFrame 现在由 RendererModule::onRenderBegin 调用 + // 这里不再调用,以避免清空已经提交的渲染命令 + + // 获取 RHI 上下文 + auto *rhiModule = RHIModule::get(); + RHIContext *context = nullptr; + if (rhiModule && rhiModule->getDevice()) { + context = rhiModule->getDevice()->getContext(); + } + + // 创建执行上下文 + RenderPassContext ctx; + ctx.context = context; + ctx.commandQueue = &commandQueue_; + ctx.graph = this; + ctx.deltaTime = deltaTime; + ctx.frameIndex = frameIndex_; + + // 执行所有通道 + for (auto *pass : executionOrder_) { + if (pass->isEnabled()) { + pass->execute(ctx); + } + } + + // 执行命令队列 + + // 执行命令队列 + commandQueue_.execute(); + + // 结束帧 + commandQueue_.endFrame(); + + // 调用 RHI 上下文的 endFrame 进行缓冲区交换 + if (context) { + context->endFrame(); + } + + frameIndex_++; +} + +TextureHandle RenderGraph::getTexture(RenderGraphResourceHandle handle) const { + auto it = textures_.find(handle.index); + if (it != textures_.end()) { + return it->second; + } + return TextureHandle{}; +} + +void RenderGraph::setOutputSize(uint32_t width, uint32_t height) { + outputWidth_ = width; + outputHeight_ = height; +} + +void RenderGraph::createResources() { + auto *rhiModule = RHIModule::get(); + if (!rhiModule || !rhiModule->getDevice()) + return; + + auto *device = rhiModule->getDevice(); + + // 创建渲染图内部资源 + // 这里可以根据需要创建默认的后缓冲等 +} + +void RenderGraph::destroyResources() { + // 释放所有渲染图管理的资源 + textures_.clear(); +} + +// ======================================== +// GeometryRenderPass 实现 +// ======================================== + +GeometryRenderPass::GeometryRenderPass(const std::string &name) : name_(name) {} + +void GeometryRenderPass::declareResources(RenderGraphBuilder &builder) { + // 声明几何渲染通道的资源依赖 + if (colorTarget_.isValid()) { + builder.writeResource(colorTarget_); + } + if (depthTarget_.isValid()) { + builder.writeResource(depthTarget_); + } +} + +void GeometryRenderPass::execute(const RenderPassContext &ctx) { + // 几何渲染通道的执行逻辑 + // 通常由外部的渲染系统填充具体的绘制命令 + + if (!ctx.commandQueue) + return; + + // 清除颜色缓冲区 + ctx.commandQueue->submitClear(Color::Black, CLEAR_COLOR_FLAG); + + // 设置视口 + if (ctx.graph) { + ctx.commandQueue->setViewport( + 0, 0, static_cast(ctx.graph->getOutputWidth()), + static_cast(ctx.graph->getOutputHeight())); + } +} + +// ======================================== +// PostProcessRenderPass 实现 +// ======================================== + +PostProcessRenderPass::PostProcessRenderPass(const std::string &name) + : name_(name) {} + +void PostProcessRenderPass::declareResources(RenderGraphBuilder &builder) { + // 声明后期处理通道的资源依赖 + if (inputTexture_.isValid()) { + builder.readResource(inputTexture_); + } + if (outputTarget_.isValid()) { + builder.writeResource(outputTarget_); + } +} + +void PostProcessRenderPass::execute(const RenderPassContext &ctx) { + // 后期处理通道的执行逻辑 + // 这里可以实现全屏四边形绘制等后期处理效果 + + if (!ctx.context || !ctx.commandQueue) + return; + + // 获取命令列表 + RHICommandList *cmdList = ctx.commandQueue->getCommandList(); + if (!cmdList) + return; + + // 绑定输入纹理 + if (inputTexture_.isValid()) { + TextureHandle tex = ctx.getTexture(inputTexture_); + if (tex.isValid()) { + cmdList->setTexture(0, tex.get()); + } + } + + // 后期处理的具体实现由子类或外部提供 +} + +} // namespace extra2d diff --git a/src/renderer/renderer_module.cpp b/src/renderer/renderer_module.cpp index c66c461..4c9264c 100644 --- a/src/renderer/renderer_module.cpp +++ b/src/renderer/renderer_module.cpp @@ -1,82 +1,20 @@ -#include "glad/glad.h" -#include #include -#include -#include +#include +#include #include +#include #include -#include - namespace extra2d { RendererModule::RendererModule() = default; RendererModule::~RendererModule() = default; -RendererModule::RendererModule(RendererModule &&other) noexcept - : commandBuffer_(std::move(other.commandBuffer_)), - commandCount_(other.commandCount_), - uniformManager_(std::move(other.uniformManager_)), - onRenderBeginListener_(std::move(other.onRenderBeginListener_)), - onRenderSubmitListener_(std::move(other.onRenderSubmitListener_)), - onRenderSetCameraListener_(std::move(other.onRenderSetCameraListener_)), - 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_), - viewportAdapter_(std::move(other.viewportAdapter_)), - viewProjectionMatrix_(std::move(other.viewProjectionMatrix_)) { - other.commandCount_ = 0; - 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_) { - uniformManager_.shutdown(); - } - - commandBuffer_ = std::move(other.commandBuffer_); - commandCount_ = other.commandCount_; - uniformManager_ = std::move(other.uniformManager_); - onRenderBeginListener_ = std::move(other.onRenderBeginListener_); - onRenderSubmitListener_ = std::move(other.onRenderSubmitListener_); - onRenderSetCameraListener_ = std::move(other.onRenderSetCameraListener_); - 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_; - viewportAdapter_ = std::move(other.viewportAdapter_); - viewProjectionMatrix_ = std::move(other.viewProjectionMatrix_); - - other.commandCount_ = 0; - 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); }); @@ -84,69 +22,92 @@ bool RendererModule::init() { [this](const Mat4 &viewProj) { onRenderSetCamera(viewProj); }); onRenderEndListener_.bind([this]() { onRenderEnd(); }); onResizeListener_.bind([this](int32 w, int32 h) { onResize(w, h); }); - onShowListener_.bind([this]() { onWindowShow(); }); - E2D_LOG_INFO("RendererModule initialized (waiting for GL context)"); + E2D_LOG_INFO("RendererModule initialized (waiting for window show)"); return true; } void RendererModule::onWindowShow() { - if (glInitialized_) { + if (initialized_) { return; } - E2D_LOG_INFO("Initializing OpenGL context..."); + E2D_LOG_INFO("Initializing RendererModule context..."); - if (!uniformManager_.initialize()) { - E2D_LOG_ERROR("Failed to initialize UniformBufferManager"); + // 初始化 RHI 模块 + auto *rhiModule = RHIModule::get(); + if (!rhiModule) { + E2D_LOG_ERROR("RHIModule not available"); return; } - int windowWidth = 800, windowHeight = 600; - SDL_Window *sdlWindow = SDL_GL_GetCurrentWindow(); - if (sdlWindow) { - SDL_GetWindowSize(sdlWindow, &windowWidth, &windowHeight); - E2D_LOG_INFO("Setting initial viewport to window size: {}x{}", windowWidth, - windowHeight); - } else { - E2D_LOG_WARN("Could not get SDL window, using default viewport 800x600"); + if (!rhiModule->getDevice()) { + // RHI 设备尚未初始化,这是正常的时序问题 + // RHIModule 会在窗口显示后初始化设备,然后再次触发此函数 + E2D_LOG_INFO("RHIDevice not ready yet, waiting for RHI initialization..."); + return; } - setViewport(0, 0, static_cast(windowWidth), - static_cast(windowHeight)); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // 初始化渲染图 + if (!renderGraph_.initialize()) { + E2D_LOG_ERROR("Failed to initialize RenderGraph"); + return; + } - glInitialized_ = true; - E2D_LOG_INFO("OpenGL context initialized successfully"); + commandQueue_ = renderGraph_.getCommandQueue(); + + // 添加默认的几何渲染通道 + auto geometryPass = std::make_unique("Geometry"); + renderGraph_.addPass(std::move(geometryPass)); + + E2D_LOG_INFO("Added default geometry render pass"); + + auto windowModule = getModule(); + if (!windowModule) { + E2D_LOG_ERROR("WindowModule not available"); + return; + } + setViewport(0, 0, static_cast(windowModule->getSize().w), + static_cast(windowModule->getSize().h)); + + initialized_ = true; + E2D_LOG_INFO("RendererModule context initialized successfully"); } void RendererModule::shutdown() { E2D_LOG_INFO("Shutting down RendererModule..."); - if (glInitialized_) { - uniformManager_.shutdown(); - } + renderGraph_.shutdown(); + commandQueue_ = nullptr; + initialized_ = false; - glInitialized_ = false; + // 注意:事件监听器是值类型,会在析构时自动清理 E2D_LOG_INFO("RendererModule shutdown complete"); } -MaterialHandle RendererModule::getDefaultMaterialHandle() const { - auto* assets = getAssets(); - return assets ? assets->getDefaultMaterial() : MaterialHandle::invalid(); +RHIContext *RendererModule::getRHIContext() const { + auto *rhiModule = RHIModule::get(); + if (rhiModule && rhiModule->getDevice()) { + return rhiModule->getDevice()->getContext(); + } + return nullptr; } -MeshHandle RendererModule::getDefaultQuadHandle() const { - auto* assets = getAssets(); - return assets ? assets->getDefaultQuad() : MeshHandle::invalid(); +Handle RendererModule::getDefaultMaterialHandle() const { + auto *assets = getAssets(); + return assets ? assets->getDefaultMaterial() : Handle::invalid(); } -TextureHandle RendererModule::getDefaultTextureHandle() const { - auto* assets = getAssets(); - return assets ? assets->getDefaultTexture() : TextureHandle::invalid(); +Handle RendererModule::getDefaultQuadHandle() const { + auto *assets = getAssets(); + return assets ? assets->getDefaultQuad() : Handle::invalid(); +} + +Handle RendererModule::getDefaultTextureHandle() const { + auto *assets = getAssets(); + return assets ? assets->getDefaultTexture() : Handle::invalid(); } void RendererModule::setViewport(int32 x, int32 y, int32 width, int32 height) { @@ -157,216 +118,157 @@ void RendererModule::setViewport(int32 x, int32 y, int32 width, int32 height) { viewportAdapter_.update(width, height); - auto result = viewportAdapter_.getResult(); - glViewport(static_cast(result.viewport.x), - static_cast(result.viewport.y), - static_cast(result.viewport.w), - static_cast(result.viewport.h)); + // 更新渲染图输出尺寸 + renderGraph_.setOutputSize(static_cast(width), + static_cast(height)); + + // 通过 RHI 设置视口 + if (initialized_) { + auto *context = getRHIContext(); + if (context) { + auto result = viewportAdapter_.getResult(); + context->setViewport(static_cast(result.viewport.x), + static_cast(result.viewport.y), + static_cast(result.viewport.w), + static_cast(result.viewport.h)); + } + } } void RendererModule::clear(const Color &color, uint32 flags) { - GLbitfield mask = 0; + if (!initialized_) + return; - 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); + if (commandQueue_) { + commandQueue_->submitClear(color, flags); } } void RendererModule::onRenderBegin() { - if (!glInitialized_) { + if (!initialized_) { return; } - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - - commandCount_ = 0; - + // 重置统计 stats_ = {}; - uniformManager_.resetMaterialUBOs(); + // 开始渲染帧 - 清空命令队列准备接收新的渲染命令 + if (commandQueue_) { + commandQueue_->beginFrame(); + } + + // 帧开始 } void RendererModule::onRenderSubmit(const RenderCommand &cmd) { - if (!glInitialized_) { + if (!initialized_ || !commandQueue_) { + E2D_LOG_WARN( + "onRenderSubmit: RendererModule not initialized or no command queue"); return; } - if (commandCount_ >= MAX_RENDER_COMMANDS) { - E2D_LOG_WARN("Render command buffer full!"); - return; + (void)cmd; // 避免未使用警告 + + // 将渲染命令转换为绘制命令提交到队列 + switch (cmd.type) { + case RenderCommandType::DrawMesh: { + auto *assets = getAssets(); + if (!assets) + return; + + // 获取材质,如果没有指定则使用默认材质 + Material *material = assets->get(cmd.drawMesh.material); + if (!material) { + material = assets->get(assets->getDefaultMaterial()); + } + + // 获取网格,如果没有指定则使用默认四边形 + Mesh *mesh = assets->get(cmd.drawMesh.mesh); + if (!mesh) { + mesh = assets->get(assets->getDefaultQuad()); + } + + if (material && mesh) { + Transform transform(cmd.drawMesh.pos, cmd.drawMesh.scale, + cmd.drawMesh.rot); + commandQueue_->submitDraw(Ptr(material), Ptr(mesh), + transform, cmd.drawMesh.color); + stats_.commandsSubmitted++; + } else { + E2D_LOG_WARN("Failed to submit draw command: material={}, mesh={}", + material ? "valid" : "null", mesh ? "valid" : "null"); + } + break; } - commandBuffer_[commandCount_++] = cmd; - stats_.commandsSubmitted++; + case RenderCommandType::DrawMeshInstanced: { + auto *assets = getAssets(); + if (!assets) + return; + + Material *material = assets->get(cmd.drawInstanced.material); + Mesh *mesh = assets->get(cmd.drawInstanced.mesh); + + if (material && mesh) { + commandQueue_->submitDrawInstanced(Ptr(material), + Ptr(mesh), + cmd.drawInstanced.instanceCount); + stats_.commandsSubmitted++; + } + break; + } + + 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; + } + } } void RendererModule::onRenderSetCamera(const Mat4 &viewProj) { - if (!glInitialized_) { + if (!initialized_) { return; } viewProjectionMatrix_ = viewProj; - uniformManager_.updateGlobalUBO(&viewProj, sizeof(Mat4)); + // 更新全局 UBO(通过 RenderGraph 的命令队列) + // 实际的 UBO 更新会在 RenderGraph 执行时进行 } void RendererModule::onRenderEnd() { - if (!glInitialized_) { + if (!initialized_) { + E2D_LOG_WARN("onRenderEnd: RendererModule not initialized"); return; } - sortCommands(); + // 执行渲染图 - batchAndDraw(); + // 执行渲染图 + // 这里会执行所有渲染通道并提交命令 + renderGraph_.execute(0.016f); // TODO: 使用实际的 deltaTime - E2D_LOG_DEBUG("Render: {} commands, {} draw calls, {} batches", - stats_.commandsExecuted, stats_.drawCalls, stats_.batches); + // 获取统计信息 + if (commandQueue_) { + stats_.drawCalls = commandQueue_->getCommandCount(); + } + + // 渲染完成 } void RendererModule::onResize(int32 width, int32 height) { setViewport(0, 0, width, height); } -void RendererModule::sortCommands() { - std::sort(commandBuffer_.begin(), commandBuffer_.begin() + commandCount_, - [](const RenderCommand &a, const RenderCommand &b) { - return a.sortKey < b.sortKey; - }); -} - -void RendererModule::batchAndDraw() { - auto* assets = getAssets(); - if (!assets) { - E2D_LOG_ERROR("AssetsModule not available"); - return; - } - - MaterialHandle lastMaterial = MaterialHandle::invalid(); - MeshHandle lastMesh = MeshHandle::invalid(); - 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* assets = getAssets(); - if (!assets) return; - - Material* material = assets->get(materialHandle); - if (!material) { - material = assets->getDefaultMaterialPtr(); - } - - Mesh* mesh = assets->get(meshHandle); - if (!mesh) { - mesh = assets->getDefaultQuadPtr(); - } - - if (!material || !mesh) - return; - - Shader* shader = material->getShader().get(); - if (!shader) - return; - - shader->bind(); - - shader->setMat4("uViewProjection", glm::value_ptr(viewProjectionMatrix_)); - - shader->setVec4("uTintColor", 1.0f, 1.0f, 1.0f, 1.0f); - shader->setFloat("uOpacity", 1.0f); - - const auto &textureSlots = material->getTextures(); - bool hasMaterialTexture = false; - for (const auto &slot : textureSlots) { - if (slot.handle.isValid()) { - Texture* texture = assets->get(slot.handle); - if (texture) { - texture->bind(slot.slot); - shader->setInt(slot.uniformName, static_cast(slot.slot)); - hasMaterialTexture = true; - } - } - } - - if (!hasMaterialTexture) { - Texture* defaultTexture = assets->getDefaultTexturePtr(); - if (defaultTexture) { - defaultTexture->bind(0); - shader->setInt("uTexture", 0); - } - } - - mesh->bind(); - - for (uint32_t i = 0; i < count; ++i) { - Transform transform = commandBuffer_[start + i].getTransform(); - float matrix[16]; - transform.toMatrix(matrix); - - shader->setMat4("uModelMatrix", matrix); - - Color color = commandBuffer_[start + i].getColor(); - shader->setVec4("uColor", color.r, color.g, color.b, color.a); - - mesh->draw(); - stats_.drawCalls++; - } -} - void RendererModule::executeCommand(const RenderCommand &cmd) { + // 此方法保留用于直接执行特定命令 + // 大部分命令现在通过 RenderGraph 处理 switch (cmd.type) { case RenderCommandType::SetViewport: setViewport(cmd.viewport.x, cmd.viewport.y, cmd.viewport.width, diff --git a/src/renderer/rhi/opengl/gl_buffer.cpp b/src/renderer/rhi/opengl/gl_buffer.cpp new file mode 100644 index 0000000..29afdc7 --- /dev/null +++ b/src/renderer/rhi/opengl/gl_buffer.cpp @@ -0,0 +1,65 @@ +#include +#include + +namespace extra2d { + +GLBuffer::GLBuffer(const BufferDesc &desc) : desc_(desc), buffer_(0) {} + +GLBuffer::~GLBuffer() { destroy(); } + +bool GLBuffer::create() { + glGenBuffers(1, &buffer_); + if (buffer_ == 0) { + return false; + } + + GLenum target = bufferTypeToGL(desc_.type); + glBindBuffer(target, buffer_); + + GLenum usage = bufferUsageToGL(desc_.usage); + glBufferData(target, desc_.size, desc_.initialData, usage); + + glBindBuffer(target, 0); + return true; +} + +void GLBuffer::destroy() { + if (buffer_ != 0) { + glDeleteBuffers(1, &buffer_); + buffer_ = 0; + } +} + +bool GLBuffer::update(const void *data, uint32_t size, uint32_t offset) { + if (buffer_ == 0 || data == nullptr) + return false; + + GLenum target = bufferTypeToGL(desc_.type); + glBindBuffer(target, buffer_); + glBufferSubData(target, offset, size, data); + glBindBuffer(target, 0); + return true; +} + +void *GLBuffer::map() { + // OpenGL ES 3.2 不支持 glMapBuffer,使用 glMapBufferRange + if (buffer_ == 0) + return nullptr; + + GLenum target = bufferTypeToGL(desc_.type); + glBindBuffer(target, buffer_); + void *ptr = glMapBufferRange(target, 0, desc_.size, GL_MAP_WRITE_BIT); + return ptr; +} + +void GLBuffer::unmap() { + if (buffer_ == 0) + return; + + GLenum target = bufferTypeToGL(desc_.type); + glBindBuffer(target, buffer_); + glUnmapBuffer(target); + glBindBuffer(target, 0); +} + +} // namespace extra2d diff --git a/src/renderer/rhi/opengl/gl_command_list.cpp b/src/renderer/rhi/opengl/gl_command_list.cpp new file mode 100644 index 0000000..d2c6627 --- /dev/null +++ b/src/renderer/rhi/opengl/gl_command_list.cpp @@ -0,0 +1,261 @@ +#include +#include +#include +#include +#include + +#include + +namespace extra2d { + +GLCommandList::GLCommandList() = default; + +GLCommandList::~GLCommandList() = default; + +void GLCommandList::begin() { + // 开始记录命令 + recording_ = true; +} + +void GLCommandList::end() { + // 结束记录命令 + recording_ = false; +} + +void GLCommandList::submit() { + // 提交命令列表(OpenGL 是立即模式,这里不需要特殊处理) +} + +void GLCommandList::beginRenderPass(RHIFramebuffer *framebuffer, + ClearFlags clearFlags, + const Color &clearColor, + float clearDepth, + uint8_t clearStencil) { + if (framebuffer) { + auto *glFramebuffer = static_cast(framebuffer); + glFramebuffer->bind(); + currentFramebuffer_ = glFramebuffer; + } else { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + currentFramebuffer_ = nullptr; + } + + // 禁用2D渲染不需要的OpenGL状态 + glDisable(GL_DEPTH_TEST); // 禁用深度测试 + glDisable(GL_CULL_FACE); // 禁用面剔除 + glDisable(GL_STENCIL_TEST); // 禁用模板测试 + + // 启用混合(2D渲染通常需要透明度混合) + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // 禁用裁剪测试(除非需要) + glDisable(GL_SCISSOR_TEST); + + // 执行清除操作 + if (clearFlags != ClearFlags::None) { + clear(clearFlags, clearColor, clearDepth, clearStencil); + } +} + +void GLCommandList::endRenderPass() { + // 结束渲染通道 + currentFramebuffer_ = nullptr; +} + +void GLCommandList::setViewport(const Viewport &viewport) { + glViewport(static_cast(viewport.x), static_cast(viewport.y), + static_cast(viewport.width), + static_cast(viewport.height)); +} + +void GLCommandList::setScissor(const ScissorRect &scissor) { + glScissor(scissor.x, scissor.y, scissor.width, scissor.height); +} + +void GLCommandList::setPipeline(RHIPipeline *pipeline) { + if (pipeline) { + auto *glPipeline = static_cast(pipeline); + glPipeline->bind(); + currentPipeline_ = glPipeline; + + // 获取 shader program + currentShaderProgram_ = glPipeline->getGLProgram(); + } +} + +void GLCommandList::setVertexBuffer(uint32_t slot, RHIBuffer *buffer, + uint32_t offset) { + if (buffer) { + auto *glBuffer = static_cast(buffer); + glBindBuffer(GL_ARRAY_BUFFER, glBuffer->getGLBuffer()); + currentVertexBuffer_ = glBuffer; + + // 如果有当前管线,配置顶点属性 + if (currentPipeline_) { + const auto &layout = currentPipeline_->getVertexLayout(); + for (const auto &attr : layout.attributes) { + glEnableVertexAttribArray(attr.location); + + GLenum type = GL_FLOAT; + GLint size = 1; + GLboolean normalized = GL_FALSE; + + switch (attr.format) { + case VertexFormat::Float1: + size = 1; + break; + case VertexFormat::Float2: + size = 2; + break; + case VertexFormat::Float3: + size = 3; + break; + case VertexFormat::Float4: + size = 4; + break; + default: + break; + } + + glVertexAttribPointer(attr.location, size, type, normalized, layout.stride, + reinterpret_cast(attr.offset)); + } + } + } +} + +void GLCommandList::setIndexBuffer(RHIBuffer *buffer, IndexType type, + uint32_t offset) { + if (buffer) { + auto *glBuffer = static_cast(buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glBuffer->getGLBuffer()); + currentIndexBuffer_ = glBuffer; + } +} + +void GLCommandList::setUniformBuffer(uint32_t slot, RHIBuffer *buffer) { + if (buffer) { + auto *glBuffer = static_cast(buffer); + glBindBufferBase(GL_UNIFORM_BUFFER, slot, glBuffer->getGLBuffer()); + } +} + +void GLCommandList::setTexture(uint32_t slot, RHITexture *texture) { + if (texture) { + auto *glTexture = static_cast(texture); + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, glTexture->getGLTexture()); + } +} + +void GLCommandList::setSampler(uint32_t slot, TextureFilter minFilter, + TextureFilter magFilter, TextureWrap wrapS, + TextureWrap wrapT) { + // OpenGL 的采样器状态是纹理对象的一部分 + // 这里可以设置当前绑定纹理的采样参数 +} + +void GLCommandList::draw(uint32_t vertexCount, uint32_t firstVertex, + uint32_t instanceCount, uint32_t firstInstance) { + if (instanceCount > 1) { + glDrawArraysInstanced(GL_TRIANGLES, firstVertex, vertexCount, instanceCount); + } else { + glDrawArrays(GL_TRIANGLES, firstVertex, vertexCount); + } +} + +void GLCommandList::drawIndexed(uint32_t indexCount, uint32_t firstIndex, + int32_t vertexOffset, uint32_t instanceCount, + uint32_t firstInstance) { + GLenum indexType = GL_UNSIGNED_SHORT; + if (currentIndexBuffer_) { + // 根据缓冲区类型确定索引类型 + // 这里简化处理,使用 GL_UNSIGNED_SHORT + } + + if (instanceCount > 1) { + glDrawElementsInstanced( + GL_TRIANGLES, indexCount, indexType, + reinterpret_cast(firstIndex * sizeof(uint16_t)), + instanceCount); + } else { + glDrawElements(GL_TRIANGLES, indexCount, indexType, + reinterpret_cast(firstIndex * sizeof(uint16_t))); + } +} + +void GLCommandList::clear(ClearFlags flags, const Color &color, float depth, + uint8_t stencil) { + GLbitfield mask = 0; + + if (hasFlag(flags, ClearFlags::Color)) { + glClearColor(color.r, color.g, color.b, color.a); + mask |= GL_COLOR_BUFFER_BIT; + } + + if (hasFlag(flags, ClearFlags::Depth)) { + glClearDepthf(depth); + mask |= GL_DEPTH_BUFFER_BIT; + } + + if (hasFlag(flags, ClearFlags::Stencil)) { + glClearStencil(stencil); + mask |= GL_STENCIL_BUFFER_BIT; + } + + if (mask != 0) { + glClear(mask); + } +} + +bool GLCommandList::isRecording() const { + return recording_; +} + +void GLCommandList::setUniform(const std::string& name, float value) { + if (currentShaderProgram_ != 0) { + GLint location = glGetUniformLocation(currentShaderProgram_, name.c_str()); + if (location != -1) { + glUniform1f(location, value); + } + } +} + +void GLCommandList::setUniform(const std::string& name, const Vec2& value) { + if (currentShaderProgram_ != 0) { + GLint location = glGetUniformLocation(currentShaderProgram_, name.c_str()); + if (location != -1) { + glUniform2f(location, value.x, value.y); + } + } +} + +void GLCommandList::setUniform(const std::string& name, const Vec3& value) { + if (currentShaderProgram_ != 0) { + GLint location = glGetUniformLocation(currentShaderProgram_, name.c_str()); + if (location != -1) { + glUniform3f(location, value.x, value.y, value.z); + } + } +} + +void GLCommandList::setUniform(const std::string& name, const Color& value) { + if (currentShaderProgram_ != 0) { + GLint location = glGetUniformLocation(currentShaderProgram_, name.c_str()); + if (location != -1) { + glUniform4f(location, value.r, value.g, value.b, value.a); + } + } +} + +void GLCommandList::setUniform(const std::string& name, const Mat4& value) { + if (currentShaderProgram_ != 0) { + GLint location = glGetUniformLocation(currentShaderProgram_, name.c_str()); + if (location != -1) { + glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value)); + } + } +} + +} // namespace extra2d diff --git a/src/renderer/rhi/opengl/gl_context.cpp b/src/renderer/rhi/opengl/gl_context.cpp new file mode 100644 index 0000000..15e2b3f --- /dev/null +++ b/src/renderer/rhi/opengl/gl_context.cpp @@ -0,0 +1,84 @@ +#include +#include +#include +#include + +namespace extra2d { + +GLContext::GLContext(GLDevice *device) + : device_(device), defaultFramebuffer_(nullptr) {} + +GLContext::~GLContext() = default; + +bool GLContext::initialize() { + E2D_LOG_INFO("Initializing OpenGL context..."); + + // 创建默认帧缓冲(窗口帧缓冲) + defaultFramebuffer_ = std::make_unique(); + defaultFramebuffer_->setSize(800, 600); // 默认大小,会被更新 + + E2D_LOG_INFO("OpenGL context initialized"); + return true; +} + +void GLContext::shutdown() { + E2D_LOG_INFO("Shutting down OpenGL context..."); + + defaultFramebuffer_.reset(); + + E2D_LOG_INFO("OpenGL context shutdown complete"); +} + +void GLContext::beginFrame() { + // 重置每帧的统计 + // device_->resetStats(); // 需要在 GLDevice 中添加此方法 +} + +void GLContext::endFrame() { + // 交换缓冲区 + // 通过 SDL 获取当前窗口并交换缓冲区 + SDL_Window* window = SDL_GL_GetCurrentWindow(); + if (window) { + SDL_GL_SwapWindow(window); + } +} + +void GLContext::setViewport(int32_t x, int32_t y, uint32_t width, + uint32_t height) { + glViewport(x, y, static_cast(width), static_cast(height)); + + // 更新默认帧缓冲大小 + if (defaultFramebuffer_) { + defaultFramebuffer_->setSize(width, height); + } +} + +void GLContext::bindDefaultFramebuffer() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +RHIFramebuffer *GLContext::getDefaultFramebuffer() { + return defaultFramebuffer_.get(); +} + +bool GLContext::isFeatureSupported(const char *feature) const { + // 检查 OpenGL 扩展或版本 + if (std::strcmp(feature, "instancing") == 0) { + return glad_glDrawArraysInstanced != nullptr; + } + if (std::strcmp(feature, "compute") == 0) { + return glad_glDispatchCompute != nullptr; + } + if (std::strcmp(feature, "sRGB") == 0) { + return GLAD_GL_EXT_texture_sRGB_R8; + } + if (std::strcmp(feature, "anisotropic") == 0) { + return GLAD_GL_EXT_texture_filter_anisotropic; + } + if (std::strcmp(feature, "debug") == 0) { + return GLAD_GL_KHR_debug; + } + return false; +} + +} // namespace extra2d diff --git a/src/renderer/rhi/opengl/gl_device.cpp b/src/renderer/rhi/opengl/gl_device.cpp new file mode 100644 index 0000000..87bd4cc --- /dev/null +++ b/src/renderer/rhi/opengl/gl_device.cpp @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace extra2d { + +// 前向声明 +class GLDevice; + +/** + * @brief OpenGL 设备实现 + */ +class GLDevice : public RHIDevice { +public: + GLDevice() : window_(nullptr), glContext_(nullptr), context_(nullptr), stats_() {} + + ~GLDevice() override { shutdown(); } + + bool initialize(void* nativeWindow) override { + E2D_LOG_INFO("Initializing OpenGL device..."); + + if (!nativeWindow) { + E2D_LOG_ERROR("Native window handle is null"); + return false; + } + + window_ = static_cast(nativeWindow); + + // 创建 OpenGL ES 3.2 上下文 + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + + glContext_ = SDL_GL_CreateContext(window_); + if (!glContext_) { + E2D_LOG_ERROR("Failed to create OpenGL ES context: {}", SDL_GetError()); + return false; + } + + // 初始化 GLAD + if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) { + E2D_LOG_ERROR("Failed to initialize GLAD"); + SDL_GL_DeleteContext(glContext_); + glContext_ = nullptr; + return false; + } + + E2D_LOG_INFO("OpenGL ES context created: {}.{}", GLVersion.major, GLVersion.minor); + + const char* version = reinterpret_cast(glGetString(GL_VERSION)); + if (!version) { + E2D_LOG_ERROR("Failed to get OpenGL version"); + SDL_GL_DeleteContext(glContext_); + glContext_ = nullptr; + return false; + } + + E2D_LOG_INFO("OpenGL Version: {}", version); + E2D_LOG_INFO("OpenGL Vendor: {}", + reinterpret_cast(glGetString(GL_VENDOR))); + E2D_LOG_INFO("OpenGL Renderer: {}", + reinterpret_cast(glGetString(GL_RENDERER))); + + // 创建上下文管理器 + context_ = std::make_unique(this); + if (!context_->initialize()) { + E2D_LOG_ERROR("Failed to initialize GL context"); + SDL_GL_DeleteContext(glContext_); + glContext_ = nullptr; + return false; + } + + // 设置默认状态 + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + E2D_LOG_INFO("OpenGL device initialized successfully"); + return true; + } + + void shutdown() override { + E2D_LOG_INFO("Shutting down OpenGL device..."); + + if (context_) { + context_->shutdown(); + context_.reset(); + } + + if (glContext_) { + SDL_GL_DeleteContext(glContext_); + glContext_ = nullptr; + } + + window_ = nullptr; + + E2D_LOG_INFO("OpenGL device shutdown complete"); + } + + std::unique_ptr createBuffer(const BufferDesc &desc) override { + auto buffer = std::make_unique(desc); + if (!buffer->create()) { + return nullptr; + } + return buffer; + } + + std::unique_ptr createTexture(const TextureDesc &desc) override { + auto texture = std::make_unique(desc); + if (!texture->create()) { + return nullptr; + } + return texture; + } + + std::unique_ptr createShader(const ShaderDesc &desc) override { + auto shader = std::make_unique(desc); + if (!shader->compile()) { + E2D_LOG_ERROR("Shader compilation failed: {}", shader->getCompileLog()); + return nullptr; + } + return shader; + } + + std::unique_ptr + createPipeline(const PipelineDesc &desc) override { + auto pipeline = std::make_unique(desc); + if (!pipeline->create()) { + return nullptr; + } + + // 获取 vertex shader 的 program ID + if (desc.vertexShader.isValid()) { + RHIShader* shader = desc.vertexShader.get(); + if (shader) { + auto* glShader = static_cast(shader); + pipeline->setGLProgram(glShader->getGLProgram()); + } + } + + return pipeline; + } + + std::unique_ptr + createFramebuffer(const RenderPassDesc &desc) override { + auto framebuffer = std::make_unique(desc); + if (!framebuffer->create()) { + return nullptr; + } + return framebuffer; + } + + std::unique_ptr createCommandList() override { + return std::make_unique(); + } + + RHIContext *getContext() override { return context_.get(); } + + const char *getBackendName() const override { return "OpenGL"; } + + void *getNativeWindow() const { return window_; } + + void waitIdle() override { glFinish(); } + + const RenderStats &getStats() const override { return stats_; } + + void resetStats() override { stats_.reset(); } + + void incrementDrawCalls() { stats_.drawCalls++; } + void incrementTriangles(uint32_t count) { stats_.triangles += count; } + void incrementVertices(uint32_t count) { stats_.vertices += count; } + void incrementTextureBinds() { stats_.textureBinds++; } + void incrementBufferBinds() { stats_.bufferBinds++; } + void incrementPipelineBinds() { stats_.pipelineBinds++; } + void incrementRenderPassSwitches() { stats_.renderPassSwitches++; } + +private: + SDL_Window* window_; + SDL_GLContext glContext_; + std::unique_ptr context_; + RenderStats stats_; +}; + +// 创建设备的工厂函数 +std::unique_ptr CreateGLDevice() { + return std::make_unique(); +} + +} // namespace extra2d diff --git a/src/renderer/rhi/opengl/gl_framebuffer.cpp b/src/renderer/rhi/opengl/gl_framebuffer.cpp new file mode 100644 index 0000000..0c84b11 --- /dev/null +++ b/src/renderer/rhi/opengl/gl_framebuffer.cpp @@ -0,0 +1,151 @@ +#include +#include +#include + +namespace extra2d { + +/** + * @brief 检查纹理格式是否为包含模板的深度格式 + * @param format 纹理格式 + * @return 是否为深度/模板格式 + */ +static inline bool isStencilFormat(TextureFormat format) { + switch (format) { + case TextureFormat::Depth24Stencil8: + case TextureFormat::Depth32FStencil8: + return true; + default: + return false; + } +} + +GLFramebuffer::GLFramebuffer() + : desc_(), framebuffer_(0), width_(0), height_(0) {} + +GLFramebuffer::GLFramebuffer(const RenderPassDesc &desc) + : desc_(desc), framebuffer_(0), width_(0), height_(0) {} + +GLFramebuffer::~GLFramebuffer() { destroy(); } + +bool GLFramebuffer::create() { + glGenFramebuffers(1, &framebuffer_); + if (framebuffer_ == 0) { + return false; + } + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); + + // 附加颜色附件 + std::vector drawBuffers; + for (size_t i = 0; i < desc_.colorAttachments.size(); ++i) { + const auto &attachment = desc_.colorAttachments[i]; + if (attachment.texture.isValid()) { + auto *texture = static_cast(attachment.texture.get()); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, + GL_TEXTURE_2D, texture->getGLTexture(), 0); + drawBuffers.push_back(GL_COLOR_ATTACHMENT0 + i); + + if (i == 0) { + width_ = texture->getWidth(); + height_ = texture->getHeight(); + } + } + } + + // 附加深度/模板附件 + if (desc_.hasDepthStencil && desc_.depthStencilAttachment.texture.isValid()) { + auto *texture = + static_cast(desc_.depthStencilAttachment.texture.get()); + GLenum attachment = isStencilFormat(texture->getFormat()) + ? GL_DEPTH_STENCIL_ATTACHMENT + : GL_DEPTH_ATTACHMENT; + glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, + texture->getGLTexture(), 0); + } + + if (!drawBuffers.empty()) { + glDrawBuffers(static_cast(drawBuffers.size()), drawBuffers.data()); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + return status == GL_FRAMEBUFFER_COMPLETE; +} + +void GLFramebuffer::destroy() { + if (framebuffer_ != 0) { + glDeleteFramebuffers(1, &framebuffer_); + framebuffer_ = 0; + } +} + +void GLFramebuffer::bind() { + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); + glViewport(0, 0, width_, height_); +} + +void GLFramebuffer::unbind() { glBindFramebuffer(GL_FRAMEBUFFER, 0); } + +void GLFramebuffer::clear(ClearFlags flags, const Color &color, float depth, + uint8_t stencil) { + bind(); + + GLbitfield mask = 0; + + if (hasFlag(flags, ClearFlags::Color)) { + glClearColor(color.r, color.g, color.b, color.a); + mask |= GL_COLOR_BUFFER_BIT; + } + + if (hasFlag(flags, ClearFlags::Depth)) { + glClearDepthf(depth); + mask |= GL_DEPTH_BUFFER_BIT; + } + + if (hasFlag(flags, ClearFlags::Stencil)) { + glClearStencil(stencil); + mask |= GL_STENCIL_BUFFER_BIT; + } + + if (mask != 0) { + glClear(mask); + } +} + +void GLFramebuffer::setSize(uint32_t width, uint32_t height) { + width_ = width; + height_ = height; +} + +uint32_t GLFramebuffer::getColorAttachmentCount() const { + return static_cast(desc_.colorAttachments.size()); +} + +TextureHandle GLFramebuffer::getColorAttachment(uint32_t index) const { + if (index < desc_.colorAttachments.size()) { + return desc_.colorAttachments[index].texture; + } + return TextureHandle(); +} + +TextureHandle GLFramebuffer::getDepthStencilAttachment() const { + if (desc_.hasDepthStencil) { + return desc_.depthStencilAttachment.texture; + } + return TextureHandle(); +} + +bool GLFramebuffer::hasDepthStencil() const { + return desc_.hasDepthStencil; +} + +bool GLFramebuffer::isValid() const { + return framebuffer_ != 0; +} + +bool GLFramebuffer::isDefault() const { + return framebuffer_ == 0; +} + +} // namespace extra2d diff --git a/src/renderer/rhi/opengl/gl_pipeline.cpp b/src/renderer/rhi/opengl/gl_pipeline.cpp new file mode 100644 index 0000000..5009809 --- /dev/null +++ b/src/renderer/rhi/opengl/gl_pipeline.cpp @@ -0,0 +1,115 @@ +#include +#include + +namespace extra2d { + +GLPipeline::GLPipeline(const PipelineDesc &desc) : desc_(desc), vao_(0) {} + +GLPipeline::~GLPipeline() { destroy(); } + +bool GLPipeline::create() { + // 创建 VAO + glGenVertexArrays(1, &vao_); + if (vao_ == 0) { + return false; + } + + glBindVertexArray(vao_); + + // 配置顶点属性 + const auto &layout = desc_.vertexLayout; + for (const auto &attr : layout.attributes) { + glEnableVertexAttribArray(attr.location); + + GLenum type = GL_FLOAT; + GLint size = 1; + GLboolean normalized = GL_FALSE; + + switch (attr.format) { + case VertexFormat::Float1: + size = 1; + break; + case VertexFormat::Float2: + size = 2; + break; + case VertexFormat::Float3: + size = 3; + break; + case VertexFormat::Float4: + size = 4; + break; + default: + break; + } + + glVertexAttribPointer(attr.location, size, type, normalized, layout.stride, + reinterpret_cast(attr.offset)); + } + + glBindVertexArray(0); + return true; +} + +void GLPipeline::destroy() { + if (vao_ != 0) { + glDeleteVertexArrays(1, &vao_); + vao_ = 0; + } +} + +void GLPipeline::bind() { + // 绑定着色器程序 + if (shaderProgram_ != 0) { + glUseProgram(shaderProgram_); + } + + // 注意:不再绑定VAO,因为我们使用动态顶点属性配置 + // 顶点属性在 GLCommandList::setVertexBuffer 中配置 + + // 应用混合状态 + if (desc_.blendState.enabled) { + glEnable(GL_BLEND); + glBlendFunc(blendFactorToGL(desc_.blendState.srcFactor), + blendFactorToGL(desc_.blendState.dstFactor)); + } else { + glDisable(GL_BLEND); + } + + // 应用深度状态 + if (desc_.depthStencilState.depthTestEnabled) { + glEnable(GL_DEPTH_TEST); + glDepthFunc(compareFuncToGL(desc_.depthStencilState.depthCompare)); + glDepthMask(desc_.depthStencilState.depthWriteEnabled ? GL_TRUE : GL_FALSE); + } else { + glDisable(GL_DEPTH_TEST); + } + + // 应用光栅化状态 + if (desc_.rasterizerState.cullEnabled) { + glEnable(GL_CULL_FACE); + glCullFace(desc_.rasterizerState.cullFrontFace ? GL_FRONT : GL_BACK); + glFrontFace(desc_.rasterizerState.frontCCW ? GL_CCW : GL_CW); + } else { + glDisable(GL_CULL_FACE); + } +} + +void GLPipeline::unbind() { glBindVertexArray(0); } + +ShaderHandle GLPipeline::getVertexShader() const { + return desc_.vertexShader; +} + +ShaderHandle GLPipeline::getFragmentShader() const { + return desc_.fragmentShader; +} + +const VertexLayout& GLPipeline::getVertexLayout() const { + return desc_.vertexLayout; +} + +bool GLPipeline::isValid() const { + return vao_ != 0; +} + +} // namespace extra2d diff --git a/src/renderer/rhi/opengl/gl_shader.cpp b/src/renderer/rhi/opengl/gl_shader.cpp new file mode 100644 index 0000000..b3cba60 --- /dev/null +++ b/src/renderer/rhi/opengl/gl_shader.cpp @@ -0,0 +1,137 @@ +#include +#include + +namespace extra2d { + +GLShader::GLShader(const ShaderDesc &desc) + : desc_(desc), shader_(0), program_(0) {} + +GLShader::~GLShader() { destroy(); } + +bool GLShader::compile() { + // 创建程序 + program_ = glCreateProgram(); + if (program_ == 0) { + compileLog_ = "Failed to create shader program"; + return false; + } + + // 编译顶点着色器 + GLuint vs = glCreateShader(GL_VERTEX_SHADER); + const char *vsSource = desc_.vertexSource.c_str(); + glShaderSource(vs, 1, &vsSource, nullptr); + glCompileShader(vs); + + GLint success; + glGetShaderiv(vs, GL_COMPILE_STATUS, &success); + if (!success) { + char infoLog[512]; + glGetShaderInfoLog(vs, 512, nullptr, infoLog); + compileLog_ = std::string("Vertex shader compilation failed: ") + infoLog; + glDeleteShader(vs); + return false; + } + + // 编译片段着色器 + GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); + const char *fsSource = desc_.fragmentSource.c_str(); + glShaderSource(fs, 1, &fsSource, nullptr); + glCompileShader(fs); + + glGetShaderiv(fs, GL_COMPILE_STATUS, &success); + if (!success) { + char infoLog[512]; + glGetShaderInfoLog(fs, 512, nullptr, infoLog); + compileLog_ = std::string("Fragment shader compilation failed: ") + infoLog; + glDeleteShader(vs); + glDeleteShader(fs); + return false; + } + + // 链接着色器程序 + glAttachShader(program_, vs); + glAttachShader(program_, fs); + glLinkProgram(program_); + + glGetProgramiv(program_, GL_LINK_STATUS, &success); + if (!success) { + char infoLog[512]; + glGetProgramInfoLog(program_, 512, nullptr, infoLog); + compileLog_ = std::string("Shader program linking failed: ") + infoLog; + glDeleteShader(vs); + glDeleteShader(fs); + glDeleteProgram(program_); + program_ = 0; + return false; + } + + // 清理着色器对象 + glDeleteShader(vs); + glDeleteShader(fs); + + return true; +} + +void GLShader::destroy() { + if (program_ != 0) { + glDeleteProgram(program_); + program_ = 0; + } +} + +void GLShader::bind() const { + if (program_ != 0) { + glUseProgram(program_); + } +} + +void GLShader::unbind() const { glUseProgram(0); } + +void GLShader::setUniform(const std::string &name, float value) { + GLint location = glGetUniformLocation(program_, name.c_str()); + if (location != -1) { + glUniform1f(location, value); + } +} + +void GLShader::setUniform(const std::string &name, const Vec2 &value) { + GLint location = glGetUniformLocation(program_, name.c_str()); + if (location != -1) { + glUniform2f(location, value.x, value.y); + } +} + +void GLShader::setUniform(const std::string &name, const Vec3 &value) { + GLint location = glGetUniformLocation(program_, name.c_str()); + if (location != -1) { + glUniform3f(location, value.x, value.y, value.z); + } +} + +void GLShader::setUniform(const std::string &name, const Color &value) { + GLint location = glGetUniformLocation(program_, name.c_str()); + if (location != -1) { + glUniform4f(location, value.r, value.g, value.b, value.a); + } +} + +void GLShader::setUniform(const std::string &name, const Mat4 &value) { + GLint location = glGetUniformLocation(program_, name.c_str()); + if (location != -1) { + glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value)); + } +} + +bool GLShader::isCompiled() const { + return program_ != 0; +} + +std::string GLShader::getCompileLog() const { + return compileLog_; +} + +bool GLShader::isValid() const { + return program_ != 0; +} + +} // namespace extra2d diff --git a/src/renderer/rhi/opengl/gl_texture.cpp b/src/renderer/rhi/opengl/gl_texture.cpp new file mode 100644 index 0000000..ad963a5 --- /dev/null +++ b/src/renderer/rhi/opengl/gl_texture.cpp @@ -0,0 +1,120 @@ +#include +#include + +namespace extra2d { + +/** + * @brief 检查纹理格式是否为深度格式 + * @param format 纹理格式 + * @return 是否为深度格式 + */ +static inline bool isDepthFormat(TextureFormat format) { + switch (format) { + case TextureFormat::Depth16: + case TextureFormat::Depth24: + case TextureFormat::Depth32F: + case TextureFormat::Depth24Stencil8: + case TextureFormat::Depth32FStencil8: + return true; + default: + return false; + } +} + +GLTexture::GLTexture(const TextureDesc &desc) : desc_(desc), texture_(0) {} + +GLTexture::~GLTexture() { destroy(); } + +bool GLTexture::create() { + glGenTextures(1, &texture_); + if (texture_ == 0) { + return false; + } + + GLenum target = GL_TEXTURE_2D; + glBindTexture(target, texture_); + + // 设置纹理参数 + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, + textureFilterToGLMin(desc_.minFilter)); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, + textureFilterToGLMag(desc_.magFilter)); + glTexParameteri(target, GL_TEXTURE_WRAP_S, textureWrapToGL(desc_.wrapS)); + glTexParameteri(target, GL_TEXTURE_WRAP_T, textureWrapToGL(desc_.wrapT)); + + // 分配纹理存储 + GLenum format = textureFormatToGLFormat(desc_.format); + GLenum internalFormat = textureFormatToGLInternal(desc_.format); + GLenum type = GL_UNSIGNED_BYTE; + + if (isDepthFormat(desc_.format)) { + type = GL_FLOAT; + } + + glTexImage2D(target, 0, internalFormat, desc_.width, desc_.height, 0, format, + type, nullptr); + + // 生成 mipmap(如果需要) + if (desc_.mipLevels > 1) { + glGenerateMipmap(target); + } + + glBindTexture(target, 0); + return true; +} + +void GLTexture::destroy() { + if (texture_ != 0) { + glDeleteTextures(1, &texture_); + texture_ = 0; + } +} + +bool GLTexture::update(const void *data, uint32_t mipLevel) { + if (texture_ == 0 || data == nullptr) + return false; + + glBindTexture(GL_TEXTURE_2D, texture_); + GLenum format = textureFormatToGLFormat(desc_.format); + GLenum type = isDepthFormat(desc_.format) ? GL_FLOAT : GL_UNSIGNED_BYTE; + // 计算当前 mipmap 层级的尺寸 + uint32_t width = desc_.width >> mipLevel; + uint32_t height = desc_.height >> mipLevel; + if (width == 0) + width = 1; + if (height == 0) + height = 1; + glTexSubImage2D(GL_TEXTURE_2D, mipLevel, 0, 0, width, height, format, type, + data); + glBindTexture(GL_TEXTURE_2D, 0); + return true; +} + +void GLTexture::generateMipmap() { + if (texture_ == 0) + return; + + glBindTexture(GL_TEXTURE_2D, texture_); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); +} + +void GLTexture::bind(uint32_t slot) { + if (texture_ == 0) + return; + + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, texture_); +} + +void GLTexture::unbind() { glBindTexture(GL_TEXTURE_2D, 0); } + +bool GLTexture::isValid() const { + return texture_ != 0; +} + +bool GLTexture::isRenderTarget() const { + return desc_.renderTarget; +} + +} // namespace extra2d diff --git a/src/renderer/rhi_module.cpp b/src/renderer/rhi_module.cpp new file mode 100644 index 0000000..a3bd1a6 --- /dev/null +++ b/src/renderer/rhi_module.cpp @@ -0,0 +1,352 @@ +#include +#include +#include +#include + +// 包含 OpenGL 设备实现 +// 注意:GLDevice 定义在 gl_device.cpp 中,需要确保链接时包含 +namespace extra2d { +// 前向声明 GLDevice 类 +class GLDevice; +} // namespace extra2d + +namespace extra2d { + +// 全局实例指针 +static RHIModule *g_rhiModule = nullptr; + +// GLDevice 创建函数声明 - 在 gl_device.cpp 中定义 +std::unique_ptr CreateGLDevice(); + +RHIModule::RHIModule() : context_(nullptr) { g_rhiModule = this; } + +RHIModule::~RHIModule() { + shutdown(); + g_rhiModule = nullptr; +} + +bool RHIModule::init() { + E2D_LOG_INFO("Initializing RHIModule..."); + + // 注册窗口显示事件监听,在窗口显示时创建 OpenGL 上下文 + onShowListener_ = std::make_unique(); + onShowListener_->bind([this]() { this->onWindowShow(); }); + + E2D_LOG_INFO("RHIModule initialized (waiting for window show)"); + return true; +} + +void RHIModule::onWindowShow() { + if (initialized_) { + return; + } + + E2D_LOG_INFO("RHIModule: Creating OpenGL device..."); + + // 获取窗口模块 + auto *windowModule = getModule(); + if (!windowModule) { + E2D_LOG_ERROR("WindowModule not available"); + return; + } + + // 等待窗口创建完成 + if (!windowModule->handle()) { + E2D_LOG_ERROR("Window not created yet"); + return; + } + + // 创建 OpenGL 设备并传入窗口句柄 + device_ = CreateGLDevice(); + if (!device_) { + E2D_LOG_ERROR("Failed to create RHI device"); + return; + } + + if (!device_->initialize(windowModule->handle())) { + E2D_LOG_ERROR("Failed to initialize RHI device"); + device_.reset(); + return; + } + + context_ = device_->getContext(); + if (!context_) { + E2D_LOG_ERROR("Failed to get RHI context"); + device_->shutdown(); + device_.reset(); + return; + } + + // 初始化上下文 + if (!context_->initialize()) { + E2D_LOG_ERROR("Failed to initialize RHI context"); + device_->shutdown(); + device_.reset(); + context_ = nullptr; + return; + } + + initialized_ = true; + E2D_LOG_INFO("RHIModule initialized successfully (backend: {})", + device_->getBackendName()); + + // RHI 初始化完成,再次触发 OnShow 事件通知其他模块 + // 这样 RendererModule 等依赖 RHI 的模块可以完成初始化 + events::OnShow::emit(); +} + +void RHIModule::shutdown() { + E2D_LOG_INFO("Shutting down RHIModule..."); + + // 清理事件监听器 + onShowListener_.reset(); + + if (context_) { + context_->shutdown(); + context_ = nullptr; + } + + if (device_) { + device_->shutdown(); + device_.reset(); + } + + initialized_ = false; + + E2D_LOG_INFO("RHIModule shutdown complete"); +} + +RHIModule *RHIModule::get() { return g_rhiModule; } + +std::unique_ptr RHIModule::createBuffer(const BufferDesc &desc) { + if (!device_) { + E2D_LOG_ERROR("Cannot create buffer: RHI device not initialized"); + return nullptr; + } + return device_->createBuffer(desc); +} + +std::unique_ptr RHIModule::createTexture(const TextureDesc &desc) { + if (!device_) { + E2D_LOG_ERROR("Cannot create texture: RHI device not initialized"); + return nullptr; + } + return device_->createTexture(desc); +} + +std::unique_ptr RHIModule::createShader(const ShaderDesc &desc) { + if (!device_) { + E2D_LOG_ERROR("Cannot create shader: RHI device not initialized"); + return nullptr; + } + return device_->createShader(desc); +} + +std::unique_ptr +RHIModule::createPipeline(const PipelineDesc &desc) { + if (!device_) { + E2D_LOG_ERROR("Cannot create pipeline: RHI device not initialized"); + return nullptr; + } + return device_->createPipeline(desc); +} + +std::unique_ptr +RHIModule::createFramebuffer(const RenderPassDesc &desc) { + if (!device_) { + E2D_LOG_ERROR("Cannot create framebuffer: RHI device not initialized"); + return nullptr; + } + return device_->createFramebuffer(desc); +} + +std::unique_ptr RHIModule::createCommandList() { + if (!device_) { + E2D_LOG_ERROR("Cannot create command list: RHI device not initialized"); + return nullptr; + } + return device_->createCommandList(); +} + +// BufferDesc 静态方法实现 +BufferDesc BufferDesc::vertex(uint32_t size, BufferUsage usage) { + BufferDesc desc; + desc.type = BufferType::Vertex; + desc.usage = usage; + desc.size = size; + return desc; +} + +BufferDesc BufferDesc::index(uint32_t size, BufferUsage usage) { + BufferDesc desc; + desc.type = BufferType::Index; + desc.usage = usage; + desc.size = size; + return desc; +} + +BufferDesc BufferDesc::uniform(uint32_t size) { + BufferDesc desc; + desc.type = BufferType::Uniform; + desc.usage = BufferUsage::Dynamic; + desc.size = size; + return desc; +} + +// BlendState 静态方法实现 +BlendState BlendState::opaque() { + BlendState state; + state.enabled = false; + state.srcFactor = BlendFactor::One; + state.dstFactor = BlendFactor::Zero; + state.op = BlendOp::Add; + state.srcAlphaFactor = BlendFactor::One; + state.dstAlphaFactor = BlendFactor::Zero; + state.alphaOp = BlendOp::Add; + return state; +} + +BlendState BlendState::alphaBlend() { + BlendState state; + state.enabled = true; + state.srcFactor = BlendFactor::SrcAlpha; + state.dstFactor = BlendFactor::OneMinusSrcAlpha; + state.op = BlendOp::Add; + state.srcAlphaFactor = BlendFactor::One; + state.dstAlphaFactor = BlendFactor::OneMinusSrcAlpha; + state.alphaOp = BlendOp::Add; + return state; +} + +BlendState BlendState::additive() { + BlendState state; + state.enabled = true; + state.srcFactor = BlendFactor::SrcAlpha; + state.dstFactor = BlendFactor::One; + state.op = BlendOp::Add; + state.srcAlphaFactor = BlendFactor::SrcAlpha; + state.dstAlphaFactor = BlendFactor::One; + state.alphaOp = BlendOp::Add; + return state; +} + +// DepthStencilState 静态方法实现 +DepthStencilState DepthStencilState::depthTest() { + DepthStencilState state; + state.depthTestEnabled = true; + state.depthWriteEnabled = true; + state.depthCompare = CompareFunc::Less; + state.stencilEnabled = false; + return state; +} + +DepthStencilState DepthStencilState::depthTestWrite() { + DepthStencilState state; + state.depthTestEnabled = true; + state.depthWriteEnabled = true; + state.depthCompare = CompareFunc::Less; + state.stencilEnabled = false; + return state; +} + +DepthStencilState DepthStencilState::depthTestNoWrite() { + DepthStencilState state; + state.depthTestEnabled = true; + state.depthWriteEnabled = false; + state.depthCompare = CompareFunc::Less; + state.stencilEnabled = false; + return state; +} + +DepthStencilState DepthStencilState::noDepthTest() { + DepthStencilState state; + state.depthTestEnabled = false; + state.depthWriteEnabled = false; + state.depthCompare = CompareFunc::Always; + state.stencilEnabled = false; + return state; +} + +// RasterizerState 静态方法实现 +RasterizerState RasterizerState::cullBack() { + RasterizerState state; + state.cullEnabled = true; + state.cullFrontFace = false; + state.frontCCW = false; + state.scissorEnabled = false; + state.wireframe = false; + return state; +} + +RasterizerState RasterizerState::cullFront() { + RasterizerState state; + state.cullEnabled = true; + state.cullFrontFace = true; + state.frontCCW = false; + state.scissorEnabled = false; + state.wireframe = false; + return state; +} + +RasterizerState RasterizerState::noCull() { + RasterizerState state; + state.cullEnabled = false; + state.cullFrontFace = false; + state.frontCCW = false; + state.scissorEnabled = false; + state.wireframe = false; + return state; +} + +// VertexLayout 方法实现 +void VertexLayout::addAttribute(uint32_t location, VertexFormat format, + uint32_t offset, uint32_t bufferIndex) { + VertexAttribute attr; + attr.location = location; + attr.format = format; + attr.offset = offset; + attr.bufferIndex = bufferIndex; + attributes.push_back(attr); +} + +// VertexAttribute 方法实现 +uint32_t VertexAttribute::getSize(VertexFormat format) { + switch (format) { + case VertexFormat::Float1: + return 4; + case VertexFormat::Float2: + return 8; + case VertexFormat::Float3: + return 12; + case VertexFormat::Float4: + return 16; + case VertexFormat::Int1: + return 4; + case VertexFormat::Int2: + return 8; + case VertexFormat::Int3: + return 12; + case VertexFormat::Int4: + return 16; + case VertexFormat::UInt1: + return 4; + case VertexFormat::UInt2: + return 8; + case VertexFormat::UInt3: + return 12; + case VertexFormat::UInt4: + return 16; + case VertexFormat::Byte4: + return 4; + case VertexFormat::Byte4Normalized: + return 4; + case VertexFormat::UByte4: + return 4; + case VertexFormat::UByte4Normalized: + return 4; + default: + return 0; + } +} + +} // namespace extra2d diff --git a/src/renderer/shader.cpp b/src/renderer/shader.cpp index ddbb070..c7cd5b7 100644 --- a/src/renderer/shader.cpp +++ b/src/renderer/shader.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -9,10 +9,10 @@ namespace extra2d { Shader::Shader() = default; Shader::~Shader() { - if (program_ != 0) { - glDeleteProgram(program_); - program_ = 0; - } + // RHIHandle 是轻量级句柄,不需要显式释放 + // 实际的资源由 RHI 设备管理 + pipeline_ = PipelineHandle(); + handle_ = ShaderHandle(); } bool Shader::loadFromFile(const std::string &vsPath, @@ -42,170 +42,92 @@ bool Shader::loadFromFile(const std::string &vsPath, bool Shader::loadFromSource(const std::string &vsSource, const std::string &fsSource) { - // 删除旧程序 - if (program_ != 0) { - glDeleteProgram(program_); - program_ = 0; + // 释放旧资源 + pipeline_ = PipelineHandle(); + handle_ = ShaderHandle(); + + // 获取 RHI 设备 + auto *rhiModule = RHIModule::get(); + if (!rhiModule) { + E2D_LOG_ERROR("RHIModule not available"); + return false; } - uniformCache_.clear(); + auto *device = rhiModule->getDevice(); + if (!device) { + E2D_LOG_ERROR("RHIDevice not available"); + return false; + } // 处理源码(添加版本声明) - std::string processedVS = addVersionIfNeeded(vsSource, GL_VERTEX_SHADER); - std::string processedFS = addVersionIfNeeded(fsSource, GL_FRAGMENT_SHADER); + std::string processedVS = addVersionIfNeeded(vsSource, true); + std::string processedFS = addVersionIfNeeded(fsSource, false); - E2D_LOG_INFO("Compiling vertex shader..."); - // 编译顶点着色器 - GLuint vs = compileShader(GL_VERTEX_SHADER, processedVS); - if (vs == 0) { - E2D_LOG_ERROR("Vertex shader compilation failed"); + // 创建着色器描述 + ShaderDesc shaderDesc; + shaderDesc.vertexSource = processedVS; + shaderDesc.fragmentSource = processedFS; + + // 创建着色器 + auto shader = device->createShader(shaderDesc); + if (!shader) { + E2D_LOG_ERROR("Failed to create shader"); return false; } - E2D_LOG_INFO("Compiling fragment shader..."); - // 编译片段着色器 - GLuint fs = compileShader(GL_FRAGMENT_SHADER, processedFS); - if (fs == 0) { - E2D_LOG_ERROR("Fragment shader compilation failed"); - glDeleteShader(vs); + // 获取着色器句柄 + handle_ = ShaderHandle(shader.release()); + + // 创建顶点布局(2D 标准布局:位置 + 纹理坐标 + 颜色) + VertexLayout vertexLayout; + vertexLayout.stride = sizeof(float) * 8; // 2 (pos) + 2 (uv) + 4 (color) + vertexLayout.addAttribute(0, VertexFormat::Float2, 0); // 位置 + vertexLayout.addAttribute(1, VertexFormat::Float2, 8); // 纹理坐标 + vertexLayout.addAttribute(2, VertexFormat::Float4, 16); // 颜色 + + // 创建管线描述 + PipelineDesc pipelineDesc; + pipelineDesc.vertexShader = handle_; + pipelineDesc.fragmentShader = handle_; + pipelineDesc.vertexLayout = vertexLayout; + pipelineDesc.blendState = BlendState::alphaBlend(); + pipelineDesc.depthStencilState = DepthStencilState::noDepthTest(); + pipelineDesc.rasterizerState = RasterizerState::noCull(); + + // 创建管线 + auto pipeline = device->createPipeline(pipelineDesc); + if (!pipeline) { + E2D_LOG_ERROR("Failed to create pipeline"); + handle_ = ShaderHandle(); return false; } - E2D_LOG_INFO("Linking shader program..."); - // 链接程序 - if (!linkProgram(vs, fs)) { - E2D_LOG_ERROR("Shader program linking failed"); - glDeleteShader(vs); - glDeleteShader(fs); - return false; - } + // 获取管线句柄 + pipeline_ = PipelineHandle(pipeline.release()); - // 清理着色器对象 - glDeleteShader(vs); - glDeleteShader(fs); - - E2D_LOG_INFO("Shader program created successfully, program ID: {}", program_); + E2D_LOG_INFO("Shader 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; + // 存储 uniform block 绑定信息 + uniformBlockBindings_[name] = binding; - GLuint index = glGetUniformBlockIndex(program_, name.c_str()); - if (index != GL_INVALID_INDEX) { - glUniformBlockBinding(program_, index, binding); - } + // 注意:实际的 uniform block 绑定需要在渲染时通过 RHI 命令列表设置 + // 这里仅存储绑定信息,供后续渲染使用 + // 例如:commandList->setUniformBlock(binding, buffer); } -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()) { +uint32_t Shader::getUniformBlockBinding(const std::string &name) const { + auto it = uniformBlockBindings_.find(name); + if (it != uniformBlockBindings_.end()) { return it->second; } - - // 查询 uniform 位置 - GLint location = glGetUniformLocation(program_, name.c_str()); - uniformCache_[name] = location; - - return location; + return UINT32_MAX; // 未找到 } -std::string Shader::addVersionIfNeeded(const std::string &source, GLenum type) { +std::string Shader::addVersionIfNeeded(const std::string &source, + bool isVertex) { // 如果已经包含版本声明,直接返回 if (source.find("#version") != std::string::npos) { return source; @@ -215,7 +137,7 @@ std::string Shader::addVersionIfNeeded(const std::string &source, GLenum type) { std::string result = "#version 320 es\n"; // 片段着色器需要添加精度声明 - if (type == GL_FRAGMENT_SHADER) { + if (!isVertex) { result += "precision mediump float;\n"; } diff --git a/src/renderer/texture.cpp b/src/renderer/texture.cpp index bcb5a45..fd60edc 100644 --- a/src/renderer/texture.cpp +++ b/src/renderer/texture.cpp @@ -1,46 +1,17 @@ #include +#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; - } + // RHI 纹理句柄是轻量级句柄,不需要显式释放 + // 实际的纹理资源由 RHI 设备管理 + handle_ = TextureHandle(); } bool Texture::loadFromFile(const std::string& path) { @@ -75,81 +46,111 @@ bool Texture::loadFromFile(const std::string& path) { } bool Texture::loadFromMemory(const uint8_t* data, int width, int height, TextureFormat format) { - if (texture_ != 0) { - glDeleteTextures(1, &texture_); - texture_ = 0; - } + // 释放旧纹理 + handle_ = TextureHandle(); width_ = width; height_ = height; format_ = format; - // 创建纹理 - glGenTextures(1, &texture_); - glBindTexture(GL_TEXTURE_2D, texture_); + // 获取 RHI 设备 + auto* rhiModule = RHIModule::get(); + if (!rhiModule) { + E2D_LOG_ERROR("RHIModule not available"); + return false; + } - // 设置纹理参数 - 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); + auto* device = rhiModule->getDevice(); + if (!device) { + E2D_LOG_ERROR("RHIDevice not available"); + return false; + } + + // 创建纹理描述 + TextureDesc desc; + desc.width = static_cast(width); + desc.height = static_cast(height); + desc.format = format; + desc.mipLevels = 1; // 初始创建 1 层,后续生成 mipmap + + // 创建 RHI 纹理 + auto texture = device->createTexture(desc); + if (!texture) { + E2D_LOG_ERROR("Failed to create RHI texture"); + return false; + } // 上传纹理数据 - glTexImage2D(GL_TEXTURE_2D, 0, - getTextureInternalFormat(format_), - width_, height_, 0, - getTextureFormat(format_), - GL_UNSIGNED_BYTE, data); + if (data) { + // 注意:update 方法的参数需要根据实际 RHI 接口调整 + // 这里假设 update 接受数据指针 + texture->update(data, static_cast(width * height * getBytesPerPixel(format))); + // 生成 mipmap + // 注意:generateMipmap 方法需要在 RHI 接口中实现 + } - // 生成 mipmap - glGenerateMipmap(GL_TEXTURE_2D); - - glBindTexture(GL_TEXTURE_2D, 0); + // 获取纹理句柄 + handle_ = TextureHandle(texture.release()); return true; } bool Texture::create(int width, int height, TextureFormat format) { - if (texture_ != 0) { - glDeleteTextures(1, &texture_); - texture_ = 0; - } + // 释放旧纹理 + handle_ = TextureHandle(); width_ = width; height_ = height; format_ = format; - // 创建纹理 - glGenTextures(1, &texture_); - glBindTexture(GL_TEXTURE_2D, texture_); + // 获取 RHI 设备 + auto* rhiModule = RHIModule::get(); + if (!rhiModule) { + E2D_LOG_ERROR("RHIModule not available"); + return false; + } - // 设置纹理参数 - 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); + auto* device = rhiModule->getDevice(); + if (!device) { + E2D_LOG_ERROR("RHIDevice not available"); + return false; + } - // 分配纹理存储(无数据) - glTexImage2D(GL_TEXTURE_2D, 0, - getTextureInternalFormat(format_), - width_, height_, 0, - getTextureFormat(format_), - GL_UNSIGNED_BYTE, nullptr); + // 创建纹理描述 + TextureDesc desc; + desc.width = static_cast(width); + desc.height = static_cast(height); + desc.format = format; + desc.mipLevels = 1; // 不生成 mipmap - glBindTexture(GL_TEXTURE_2D, 0); + // 创建 RHI 纹理 + auto texture = device->createTexture(desc); + if (!texture) { + E2D_LOG_ERROR("Failed to create RHI texture"); + return false; + } + + // 获取纹理句柄 + handle_ = TextureHandle(texture.release()); return true; } -void Texture::bind(uint32_t slot) const { - if (texture_ != 0) { - glActiveTexture(GL_TEXTURE0 + slot); - glBindTexture(GL_TEXTURE_2D, texture_); +// 辅助函数:获取每个像素的字节数 +uint32_t Texture::getBytesPerPixel(TextureFormat format) { + switch (format) { + case TextureFormat::R8: return 1; + case TextureFormat::RG8: return 2; + case TextureFormat::RGB8: return 3; + case TextureFormat::RGBA8: + case TextureFormat::RGBA8_SRGB: return 4; + case TextureFormat::Depth16: return 2; + case TextureFormat::Depth24: return 3; + case TextureFormat::Depth32F: return 4; + case TextureFormat::Depth24Stencil8: return 4; + case TextureFormat::Depth32FStencil8: return 5; + default: return 4; } } -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 index 7463d86..a893d88 100644 --- a/src/renderer/uniform_buffer.cpp +++ b/src/renderer/uniform_buffer.cpp @@ -1,3 +1,5 @@ +#include +#include #include #include @@ -5,146 +7,162 @@ namespace extra2d { UniformBuffer::UniformBuffer() = default; -UniformBuffer::~UniformBuffer() { - destroy(); -} +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); - glBindBufferBase(GL_UNIFORM_BUFFER, binding, ubo_); - glBindBuffer(GL_UNIFORM_BUFFER, 0); - - E2D_LOG_DEBUG("UBO created: size={}, binding={}", size, binding); - return true; + destroy(); + + // 获取 RHI 设备 + auto *rhiModule = RHIModule::get(); + if (!rhiModule) { + E2D_LOG_ERROR("RHIModule not available"); + return false; + } + + auto *device = rhiModule->getDevice(); + if (!device) { + E2D_LOG_ERROR("RHIDevice not available"); + return false; + } + + size_ = size; + binding_ = binding; + + // 创建 uniform 缓冲区描述 + BufferDesc desc = BufferDesc::uniform(size); + + // 创建缓冲区 + auto buffer = device->createBuffer(desc); + if (!buffer) { + E2D_LOG_ERROR("Failed to create uniform buffer"); + return false; + } + + // 获取缓冲区句柄 + handle_ = BufferHandle(buffer.release()); + + E2D_LOG_DEBUG("UBO created: size={}, binding={}", size, binding); + return true; } void UniformBuffer::destroy() { - if (ubo_ != 0) { - glDeleteBuffers(1, &ubo_); - ubo_ = 0; - size_ = 0; - } + // RHIHandle 是轻量级句柄,不需要显式释放 + // 实际的缓冲区资源由 RHI 设备管理 + handle_ = BufferHandle(); + 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::update(const void *data, uint32_t size, uint32_t offset) { + if (!handle_.isValid() || data == nullptr) + return; + + if (offset + size > size_) { + E2D_LOG_WARN("UBO update out of bounds: offset={}, size={}, bufferSize={}", + offset, size, size_); + return; + } + + // 通过 RHI 缓冲区接口直接更新数据 + RHIBuffer *buffer = handle_.get(); + if (buffer) { + buffer->update(data, size, offset); + } } void UniformBuffer::bind(uint32_t binding) { - if (ubo_ == 0) return; - - glBindBufferBase(GL_UNIFORM_BUFFER, binding, ubo_); + if (!handle_.isValid()) + return; + + // 记录绑定槽位 + // 注意:实际的 GPU 绑定操作需要在渲染时通过 RHICommandList::setUniformBuffer + // 进行 示例:commandList->setUniformBuffer(binding, handle_.get()); + binding_ = binding; } +RHIBuffer *UniformBuffer::getRHIBuffer() const { return handle_.get(); } + UniformBufferManager::UniformBufferManager() = default; -UniformBufferManager::~UniformBufferManager() { - shutdown(); -} +UniformBufferManager::~UniformBufferManager() { shutdown(); } -UniformBufferManager::UniformBufferManager(UniformBufferManager&& other) noexcept +UniformBufferManager::UniformBufferManager( + UniformBufferManager &&other) noexcept : globalUBO_(std::move(other.globalUBO_)), materialUBOPool_(std::move(other.materialUBOPool_)), currentUBOIndex_(other.currentUBOIndex_) { - other.currentUBOIndex_ = 0; + other.currentUBOIndex_ = 0; } -UniformBufferManager& UniformBufferManager::operator=(UniformBufferManager&& other) noexcept { - if (this != &other) { - shutdown(); +UniformBufferManager & +UniformBufferManager::operator=(UniformBufferManager &&other) noexcept { + if (this != &other) { + shutdown(); - globalUBO_ = std::move(other.globalUBO_); - materialUBOPool_ = std::move(other.materialUBOPool_); - currentUBOIndex_ = other.currentUBOIndex_; + globalUBO_ = std::move(other.globalUBO_); + materialUBOPool_ = std::move(other.materialUBOPool_); + currentUBOIndex_ = other.currentUBOIndex_; - other.currentUBOIndex_ = 0; - } - return *this; + 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; + // 创建全局 UBO + globalUBO_ = std::make_unique(); + if (!globalUBO_->create(UniformBufferManager::GLOBAL_UBO_SIZE, + UniformBufferManager::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"); + materialUBOPool_.clear(); + globalUBO_.reset(); + currentUBOIndex_ = 0; + + E2D_LOG_INFO("UniformBufferManager shutdown"); } -UniformBuffer* UniformBufferManager::getGlobalUBO() { - return globalUBO_.get(); -} +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(); - } +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; + } + + // 需要创建新的 UBO + auto ubo = std::make_unique(); + if (!ubo->create(size, UniformBufferManager::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::resetMaterialUBOs() { currentUBOIndex_ = 0; } -void UniformBufferManager::updateGlobalUBO(const void* data, uint32_t size) { - if (globalUBO_) { - globalUBO_->update(data, size); - globalUBO_->bind(GLOBAL_UBO_BINDING); - } +void UniformBufferManager::updateGlobalUBO(const void *data, uint32_t size) { + if (globalUBO_) { + globalUBO_->update(data, size); + globalUBO_->bind(UniformBufferManager::GLOBAL_UBO_BINDING); + } } } // namespace extra2d diff --git a/src/scene/components/sprite_renderer.cpp b/src/scene/components/sprite_renderer.cpp index 5ddbf32..6b37f13 100644 --- a/src/scene/components/sprite_renderer.cpp +++ b/src/scene/components/sprite_renderer.cpp @@ -33,8 +33,9 @@ void SpriteRenderer::render() { uint32_t textureId = texture_.index(); cmd.sortKey = (materialId << 16) | (textureId & 0xFFFF); - cmd.drawMesh.mesh = MeshHandle::invalid(); - cmd.drawMesh.material = material_.isValid() ? material_ : MaterialHandle::invalid(); + // 如果没有指定网格,使用默认的四边形网格 + cmd.drawMesh.mesh = Handle::invalid(); // RendererModule 会使用默认网格 + cmd.drawMesh.material = material_.isValid() ? material_ : Handle::invalid(); cmd.setTransform(worldTransform); cmd.setColor(color_); diff --git a/src/scene/scene_module.cpp b/src/scene/scene_module.cpp index 4337ad8..2696be1 100644 --- a/src/scene/scene_module.cpp +++ b/src/scene/scene_module.cpp @@ -8,10 +8,6 @@ SceneModule::~SceneModule() { shutdown(); } -SceneModule::SceneModule(SceneModule &&) noexcept = default; - -SceneModule &SceneModule::operator=(SceneModule &&) noexcept = default; - bool SceneModule::init() { // 创建导演 director_ = makePtr(); diff --git a/src/utils/timer_module.cpp b/src/utils/timer_module.cpp index 4fa998c..b477796 100644 --- a/src/utils/timer_module.cpp +++ b/src/utils/timer_module.cpp @@ -7,24 +7,6 @@ TimerModule::TimerModule() = default; TimerModule::~TimerModule() { shutdown(); } -TimerModule::TimerModule(TimerModule &&other) noexcept - : timers_(std::move(other.timers_)), - pendingRemove_(std::move(other.pendingRemove_)), nextId_(other.nextId_), - timeScale_(other.timeScale_), inUpdate_(other.inUpdate_) {} - -TimerModule &TimerModule::operator=(TimerModule &&other) noexcept { - if (this != &other) { - shutdown(); - - timers_ = std::move(other.timers_); - pendingRemove_ = std::move(other.pendingRemove_); - nextId_ = other.nextId_; - timeScale_ = other.timeScale_; - inUpdate_ = other.inUpdate_; - } - return *this; -} - bool TimerModule::init() { // 初始化完成,等待 OnUpdate 事件 return true;