From 89d16123360f6d49d9e0a815b4f5a6ef3d9c4dd4 Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Thu, 19 Feb 2026 21:23:13 +0800 Subject: [PATCH] =?UTF-8?q?refactor(render):=20=E9=87=8D=E6=9E=84=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E7=B3=BB=E7=BB=9F=EF=BC=8C=E7=A7=BB=E9=99=A4=E6=97=A7?= =?UTF-8?q?=E5=9B=BE=E5=BD=A2=E6=A8=A1=E5=9D=97=E5=B9=B6=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=96=B0=E6=B8=B2=E6=9F=93=E6=A0=B8=E5=BF=83=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除旧的图形模块,包括GPU上下文、纹理、字体、帧缓冲等 - 添加新的渲染核心组件:RenderStats和Component - 将渲染命令相关代码迁移到render/core目录 - 引入新的渲染队列系统替代旧批处理机制 - 清理不再使用的OpenGL和Vulkan后端实现 --- Extra2D/include/extra2d/extra2d.h | 9 + .../graphics/backends/backend_factory.h | 124 -- .../graphics/backends/opengl/gl_buffer.h | 83 -- .../graphics/backends/opengl/gl_context.h | 138 --- .../graphics/backends/opengl/gl_font_atlas.h | 84 -- .../graphics/backends/opengl/gl_framebuffer.h | 105 -- .../graphics/backends/opengl/gl_pipeline.h | 131 -- .../graphics/backends/opengl/gl_renderer.h | 183 --- .../graphics/backends/opengl/gl_shader.h | 196 --- .../backends/opengl/gl_sprite_batch.h | 85 -- .../graphics/backends/opengl/gl_texture.h | 72 -- .../graphics/backends/vulkan/vk_renderer.h | 78 -- .../extra2d/graphics/batch/shape_batch.h | 157 --- .../extra2d/graphics/batch/sprite_batch.h | 121 -- .../include/extra2d/graphics/camera/camera.h | 111 -- .../graphics/camera/viewport_adapter.h | 332 ----- .../extra2d/graphics/core/render_backend.h | 125 -- .../extra2d/graphics/core/render_command.h | 223 ---- .../extra2d/graphics/core/render_module.h | 80 -- .../extra2d/graphics/core/render_target.h | 313 ----- .../extra2d/graphics/memory/gpu_context.h | 36 - .../extra2d/graphics/memory/vram_manager.h | 62 - .../extra2d/graphics/resources/buffer.h | 111 -- .../extra2d/graphics/resources/font_atlas.h | 131 -- .../extra2d/graphics/resources/framebuffer.h | 140 --- .../extra2d/graphics/resources/pipeline.h | 162 --- .../extra2d/graphics/resources/shader.h | 134 -- .../extra2d/graphics/shader/shader_cache.h | 131 -- .../graphics/shader/shader_hot_reloader.h | 131 -- .../graphics/shader/shader_interface.h | 152 --- .../extra2d/graphics/shader/shader_loader.h | 294 ----- .../extra2d/graphics/shader/shader_manager.h | 291 ----- .../extra2d/graphics/shader/shader_preset.h | 112 -- .../extra2d/graphics/texture/alpha_mask.h | 49 - .../include/extra2d/graphics/texture/font.h | 50 - .../extra2d/graphics/texture/texture.h | 64 - .../extra2d/graphics/texture/texture_atlas.h | 184 --- .../extra2d/graphics/texture/texture_pool.h | 562 --------- .../render/backends/opengl/gl_rhi_buffer.h | 177 +++ .../render/backends/opengl/gl_rhi_device.h | 200 +++ .../render/backends/opengl/gl_rhi_pipeline.h | 140 +++ .../render/backends/opengl/gl_rhi_shader.h | 160 +++ .../render/backends/opengl/gl_rhi_texture.h | 235 ++++ .../extra2d/render/batch/shape_batcher.h | 327 +++++ .../extra2d/render/batch/sprite_batcher.h | 273 +++++ .../extra2d/render/batch/text_batcher.h | 342 ++++++ .../extra2d/render/core/render_command.h | 225 ++++ .../extra2d/render/core/render_context.h | 219 ++++ .../extra2d/render/core/render_queue.h | 168 +++ .../extra2d/render/core/render_stats.h | 149 +++ .../include/extra2d/render/rhi/rhi_buffer.h | 184 +++ .../include/extra2d/render/rhi/rhi_device.h | 481 ++++++++ .../extra2d/render/rhi/rhi_framebuffer.h | 337 +++++ .../include/extra2d/render/rhi/rhi_pipeline.h | 353 ++++++ .../include/extra2d/render/rhi/rhi_shader.h | 301 +++++ .../include/extra2d/render/rhi/rhi_texture.h | 251 ++++ .../include/extra2d/render/rhi/rhi_types.h | 685 +++++++++++ .../include/extra2d/resources/font_atlas.h | 207 ++++ .../include/extra2d/resources/font_manager.h | 240 ++++ .../extra2d/resources/material_manager.h | 207 ++++ .../include/extra2d/resources/mesh_manager.h | 233 ++++ .../extra2d/resources/shader_manager.h | 227 ++++ .../extra2d/resources/texture_manager.h | 204 ++++ Extra2D/include/extra2d/scene/component.h | 154 +++ .../extra2d/scene/components/shape_renderer.h | 284 +++++ .../scene/components/sprite_renderer.h | 257 ++++ .../extra2d/scene/components/text_renderer.h | 299 +++++ .../scene/components/transform_component.h | 244 ++++ Extra2D/include/extra2d/scene/node.h | 723 ++++++++--- Extra2D/include/extra2d/scene/scene.h | 224 +++- .../src/graphics/backends/backend_factory.cpp | 141 --- .../graphics/backends/opengl/gl_backend.cpp | 39 - .../graphics/backends/opengl/gl_buffer.cpp | 174 --- .../graphics/backends/opengl/gl_context.cpp | 170 --- .../backends/opengl/gl_font_atlas.cpp | 362 ------ .../backends/opengl/gl_framebuffer.cpp | 272 ----- .../graphics/backends/opengl/gl_pipeline.cpp | 235 ---- .../graphics/backends/opengl/gl_renderer.cpp | 1086 ----------------- .../graphics/backends/opengl/gl_shader.cpp | 350 ------ .../backends/opengl/gl_sprite_batch.cpp | 230 ---- .../graphics/backends/opengl/gl_texture.cpp | 506 -------- .../graphics/backends/vulkan/vk_backend.cpp | 39 - .../graphics/backends/vulkan/vk_renderer.cpp | 150 --- Extra2D/src/graphics/batch/shape_batch.cpp | 72 -- Extra2D/src/graphics/batch/sprite_batch.cpp | 206 ---- Extra2D/src/graphics/camera/camera.cpp | 351 ------ .../src/graphics/camera/viewport_adapter.cpp | 451 ------- Extra2D/src/graphics/core/render_command.cpp | 266 ---- Extra2D/src/graphics/core/render_module.cpp | 100 -- Extra2D/src/graphics/core/render_target.cpp | 780 ------------ Extra2D/src/graphics/memory/gpu_context.cpp | 44 - Extra2D/src/graphics/memory/vram_manager.cpp | 194 --- Extra2D/src/graphics/shader/shader_cache.cpp | 293 ----- .../graphics/shader/shader_hot_reloader.cpp | 167 --- Extra2D/src/graphics/shader/shader_loader.cpp | 452 ------- .../src/graphics/shader/shader_manager.cpp | 855 ------------- Extra2D/src/graphics/shader/shader_preset.cpp | 186 --- Extra2D/src/graphics/texture/alpha_mask.cpp | 103 -- .../src/graphics/texture/texture_atlas.cpp | 386 ------ Extra2D/src/graphics/texture/texture_pool.cpp | 652 ---------- .../render/backends/opengl/gl_rhi_buffer.cpp | 239 ++++ .../render/backends/opengl/gl_rhi_device.cpp | 610 +++++++++ .../backends/opengl/gl_rhi_pipeline.cpp | 436 +++++++ .../render/backends/opengl/gl_rhi_shader.cpp | 468 +++++++ .../render/backends/opengl/gl_rhi_texture.cpp | 510 ++++++++ Extra2D/src/render/batch/shape_batcher.cpp | 562 +++++++++ Extra2D/src/render/batch/sprite_batcher.cpp | 410 +++++++ Extra2D/src/render/batch/text_batcher.cpp | 464 +++++++ Extra2D/src/render/core/render_command.cpp | 241 ++++ Extra2D/src/render/core/render_context.cpp | 285 +++++ Extra2D/src/render/core/render_queue.cpp | 207 ++++ Extra2D/src/render/core/render_stats.cpp | 89 ++ Extra2D/src/resources/font_atlas.cpp | 385 ++++++ Extra2D/src/resources/font_manager.cpp | 269 ++++ Extra2D/src/resources/material_manager.cpp | 294 +++++ Extra2D/src/resources/mesh_manager.cpp | 292 +++++ Extra2D/src/resources/shader_manager.cpp | 384 ++++++ Extra2D/src/resources/texture_manager.cpp | 302 +++++ Extra2D/src/scene/component.cpp | 18 + .../src/scene/components/shape_renderer.cpp | 376 ++++++ .../src/scene/components/sprite_renderer.cpp | 189 +++ .../src/scene/components/text_renderer.cpp | 202 +++ .../scene/components/transform_component.cpp | 251 ++++ Extra2D/src/scene/node.cpp | 843 +++++++------ Extra2D/src/scene/scene.cpp | 20 +- 125 files changed, 16451 insertions(+), 15463 deletions(-) delete mode 100644 Extra2D/include/extra2d/graphics/backends/backend_factory.h delete mode 100644 Extra2D/include/extra2d/graphics/backends/opengl/gl_buffer.h delete mode 100644 Extra2D/include/extra2d/graphics/backends/opengl/gl_context.h delete mode 100644 Extra2D/include/extra2d/graphics/backends/opengl/gl_font_atlas.h delete mode 100644 Extra2D/include/extra2d/graphics/backends/opengl/gl_framebuffer.h delete mode 100644 Extra2D/include/extra2d/graphics/backends/opengl/gl_pipeline.h delete mode 100644 Extra2D/include/extra2d/graphics/backends/opengl/gl_renderer.h delete mode 100644 Extra2D/include/extra2d/graphics/backends/opengl/gl_shader.h delete mode 100644 Extra2D/include/extra2d/graphics/backends/opengl/gl_sprite_batch.h delete mode 100644 Extra2D/include/extra2d/graphics/backends/opengl/gl_texture.h delete mode 100644 Extra2D/include/extra2d/graphics/backends/vulkan/vk_renderer.h delete mode 100644 Extra2D/include/extra2d/graphics/batch/shape_batch.h delete mode 100644 Extra2D/include/extra2d/graphics/batch/sprite_batch.h delete mode 100644 Extra2D/include/extra2d/graphics/camera/camera.h delete mode 100644 Extra2D/include/extra2d/graphics/camera/viewport_adapter.h delete mode 100644 Extra2D/include/extra2d/graphics/core/render_backend.h delete mode 100644 Extra2D/include/extra2d/graphics/core/render_command.h delete mode 100644 Extra2D/include/extra2d/graphics/core/render_module.h delete mode 100644 Extra2D/include/extra2d/graphics/core/render_target.h delete mode 100644 Extra2D/include/extra2d/graphics/memory/gpu_context.h delete mode 100644 Extra2D/include/extra2d/graphics/memory/vram_manager.h delete mode 100644 Extra2D/include/extra2d/graphics/resources/buffer.h delete mode 100644 Extra2D/include/extra2d/graphics/resources/font_atlas.h delete mode 100644 Extra2D/include/extra2d/graphics/resources/framebuffer.h delete mode 100644 Extra2D/include/extra2d/graphics/resources/pipeline.h delete mode 100644 Extra2D/include/extra2d/graphics/resources/shader.h delete mode 100644 Extra2D/include/extra2d/graphics/shader/shader_cache.h delete mode 100644 Extra2D/include/extra2d/graphics/shader/shader_hot_reloader.h delete mode 100644 Extra2D/include/extra2d/graphics/shader/shader_interface.h delete mode 100644 Extra2D/include/extra2d/graphics/shader/shader_loader.h delete mode 100644 Extra2D/include/extra2d/graphics/shader/shader_manager.h delete mode 100644 Extra2D/include/extra2d/graphics/shader/shader_preset.h delete mode 100644 Extra2D/include/extra2d/graphics/texture/alpha_mask.h delete mode 100644 Extra2D/include/extra2d/graphics/texture/font.h delete mode 100644 Extra2D/include/extra2d/graphics/texture/texture.h delete mode 100644 Extra2D/include/extra2d/graphics/texture/texture_atlas.h delete mode 100644 Extra2D/include/extra2d/graphics/texture/texture_pool.h create mode 100644 Extra2D/include/extra2d/render/backends/opengl/gl_rhi_buffer.h create mode 100644 Extra2D/include/extra2d/render/backends/opengl/gl_rhi_device.h create mode 100644 Extra2D/include/extra2d/render/backends/opengl/gl_rhi_pipeline.h create mode 100644 Extra2D/include/extra2d/render/backends/opengl/gl_rhi_shader.h create mode 100644 Extra2D/include/extra2d/render/backends/opengl/gl_rhi_texture.h create mode 100644 Extra2D/include/extra2d/render/batch/shape_batcher.h create mode 100644 Extra2D/include/extra2d/render/batch/sprite_batcher.h create mode 100644 Extra2D/include/extra2d/render/batch/text_batcher.h create mode 100644 Extra2D/include/extra2d/render/core/render_command.h create mode 100644 Extra2D/include/extra2d/render/core/render_context.h create mode 100644 Extra2D/include/extra2d/render/core/render_queue.h create mode 100644 Extra2D/include/extra2d/render/core/render_stats.h create mode 100644 Extra2D/include/extra2d/render/rhi/rhi_buffer.h create mode 100644 Extra2D/include/extra2d/render/rhi/rhi_device.h create mode 100644 Extra2D/include/extra2d/render/rhi/rhi_framebuffer.h create mode 100644 Extra2D/include/extra2d/render/rhi/rhi_pipeline.h create mode 100644 Extra2D/include/extra2d/render/rhi/rhi_shader.h create mode 100644 Extra2D/include/extra2d/render/rhi/rhi_texture.h create mode 100644 Extra2D/include/extra2d/render/rhi/rhi_types.h create mode 100644 Extra2D/include/extra2d/resources/font_atlas.h create mode 100644 Extra2D/include/extra2d/resources/font_manager.h create mode 100644 Extra2D/include/extra2d/resources/material_manager.h create mode 100644 Extra2D/include/extra2d/resources/mesh_manager.h create mode 100644 Extra2D/include/extra2d/resources/shader_manager.h create mode 100644 Extra2D/include/extra2d/resources/texture_manager.h create mode 100644 Extra2D/include/extra2d/scene/component.h create mode 100644 Extra2D/include/extra2d/scene/components/shape_renderer.h create mode 100644 Extra2D/include/extra2d/scene/components/sprite_renderer.h create mode 100644 Extra2D/include/extra2d/scene/components/text_renderer.h create mode 100644 Extra2D/include/extra2d/scene/components/transform_component.h delete mode 100644 Extra2D/src/graphics/backends/backend_factory.cpp delete mode 100644 Extra2D/src/graphics/backends/opengl/gl_backend.cpp delete mode 100644 Extra2D/src/graphics/backends/opengl/gl_buffer.cpp delete mode 100644 Extra2D/src/graphics/backends/opengl/gl_context.cpp delete mode 100644 Extra2D/src/graphics/backends/opengl/gl_font_atlas.cpp delete mode 100644 Extra2D/src/graphics/backends/opengl/gl_framebuffer.cpp delete mode 100644 Extra2D/src/graphics/backends/opengl/gl_pipeline.cpp delete mode 100644 Extra2D/src/graphics/backends/opengl/gl_renderer.cpp delete mode 100644 Extra2D/src/graphics/backends/opengl/gl_shader.cpp delete mode 100644 Extra2D/src/graphics/backends/opengl/gl_sprite_batch.cpp delete mode 100644 Extra2D/src/graphics/backends/opengl/gl_texture.cpp delete mode 100644 Extra2D/src/graphics/backends/vulkan/vk_backend.cpp delete mode 100644 Extra2D/src/graphics/backends/vulkan/vk_renderer.cpp delete mode 100644 Extra2D/src/graphics/batch/shape_batch.cpp delete mode 100644 Extra2D/src/graphics/batch/sprite_batch.cpp delete mode 100644 Extra2D/src/graphics/camera/camera.cpp delete mode 100644 Extra2D/src/graphics/camera/viewport_adapter.cpp delete mode 100644 Extra2D/src/graphics/core/render_command.cpp delete mode 100644 Extra2D/src/graphics/core/render_module.cpp delete mode 100644 Extra2D/src/graphics/core/render_target.cpp delete mode 100644 Extra2D/src/graphics/memory/gpu_context.cpp delete mode 100644 Extra2D/src/graphics/memory/vram_manager.cpp delete mode 100644 Extra2D/src/graphics/shader/shader_cache.cpp delete mode 100644 Extra2D/src/graphics/shader/shader_hot_reloader.cpp delete mode 100644 Extra2D/src/graphics/shader/shader_loader.cpp delete mode 100644 Extra2D/src/graphics/shader/shader_manager.cpp delete mode 100644 Extra2D/src/graphics/shader/shader_preset.cpp delete mode 100644 Extra2D/src/graphics/texture/alpha_mask.cpp delete mode 100644 Extra2D/src/graphics/texture/texture_atlas.cpp delete mode 100644 Extra2D/src/graphics/texture/texture_pool.cpp create mode 100644 Extra2D/src/render/backends/opengl/gl_rhi_buffer.cpp create mode 100644 Extra2D/src/render/backends/opengl/gl_rhi_device.cpp create mode 100644 Extra2D/src/render/backends/opengl/gl_rhi_pipeline.cpp create mode 100644 Extra2D/src/render/backends/opengl/gl_rhi_shader.cpp create mode 100644 Extra2D/src/render/backends/opengl/gl_rhi_texture.cpp create mode 100644 Extra2D/src/render/batch/shape_batcher.cpp create mode 100644 Extra2D/src/render/batch/sprite_batcher.cpp create mode 100644 Extra2D/src/render/batch/text_batcher.cpp create mode 100644 Extra2D/src/render/core/render_command.cpp create mode 100644 Extra2D/src/render/core/render_context.cpp create mode 100644 Extra2D/src/render/core/render_queue.cpp create mode 100644 Extra2D/src/render/core/render_stats.cpp create mode 100644 Extra2D/src/resources/font_atlas.cpp create mode 100644 Extra2D/src/resources/font_manager.cpp create mode 100644 Extra2D/src/resources/material_manager.cpp create mode 100644 Extra2D/src/resources/mesh_manager.cpp create mode 100644 Extra2D/src/resources/shader_manager.cpp create mode 100644 Extra2D/src/resources/texture_manager.cpp create mode 100644 Extra2D/src/scene/component.cpp create mode 100644 Extra2D/src/scene/components/shape_renderer.cpp create mode 100644 Extra2D/src/scene/components/sprite_renderer.cpp create mode 100644 Extra2D/src/scene/components/text_renderer.cpp create mode 100644 Extra2D/src/scene/components/transform_component.cpp diff --git a/Extra2D/include/extra2d/extra2d.h b/Extra2D/include/extra2d/extra2d.h index d7468b0..937b28c 100644 --- a/Extra2D/include/extra2d/extra2d.h +++ b/Extra2D/include/extra2d/extra2d.h @@ -35,6 +35,15 @@ #include #include +// RHI (Render Hardware Interface) +#include +#include +#include +#include +#include +#include +#include + // Scene #include #include diff --git a/Extra2D/include/extra2d/graphics/backends/backend_factory.h b/Extra2D/include/extra2d/graphics/backends/backend_factory.h deleted file mode 100644 index 2b52214..0000000 --- a/Extra2D/include/extra2d/graphics/backends/backend_factory.h +++ /dev/null @@ -1,124 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include - -namespace extra2d { -namespace graphics { - -/** - * @brief 图形后端工厂 - * 用于注册和创建图形渲染后端 - */ -class BackendFactory { -public: - using BackendFn = std::function()>; - - /** - * @brief 注册图形后端 - * @param name 后端名称 - * @param backend 后端创建函数 - * @param windowBackends 支持的窗口后端名称列表 - */ - static void reg(const std::string &name, BackendFn backend, - const std::vector &windowBackends = {}); - - /** - * @brief 创建渲染后端实例 - * @param name 后端名称 - * @return 渲染后端实例,如果后端不存在返回 nullptr - */ - static UniquePtr createBackend(const std::string &name); - - /** - * @brief 创建默认渲染后端 - * @return 默认渲染后端实例 - */ - static UniquePtr createDefaultBackend(); - - /** - * @brief 创建与指定窗口后端兼容的渲染后端 - * @param windowBackend 窗口后端名称 - * @return 兼容的渲染后端实例 - */ - static UniquePtr - createBackendForWindow(const std::string &windowBackend); - - /** - * @brief 获取所有已注册的后端名称 - */ - static std::vector backends(); - - /** - * @brief 检查后端是否存在 - */ - static bool has(const std::string &name); - - /** - * @brief 获取推荐的后端名称 - * @return 推荐的后端名称 - */ - static std::string getRecommendedBackend(); - - /** - * @brief 获取与指定窗口后端兼容的推荐图形后端 - * @param windowBackend 窗口后端名称 - * @return 推荐的图形后端名称 - */ - static std::string - getRecommendedBackendForWindow(const std::string &windowBackend); - - /** - * @brief 检查图形后端是否支持指定窗口后端 - * @param graphicsBackend 图形后端名称 - * @param windowBackend 窗口后端名称 - * @return 支持返回 true - */ - static bool isCompatible(const std::string &graphicsBackend, - const std::string &windowBackend); - - /** - * @brief 获取图形后端支持的窗口后端列表 - * @param graphicsBackend 图形后端名称 - * @return 支持的窗口后端列表 - */ - static std::vector - getSupportedWindowBackends(const std::string &graphicsBackend); - -private: - struct BackendEntry { - BackendFn createFn; - std::vector windowBackends; - }; - - static std::unordered_map ®istry(); -}; - -/** - * @brief 图形后端注册宏 - * 在全局作用域使用此宏注册图形后端 - * - * @example - * E2D_REG_GRAPHICS_BACKEND(opengl, GLRenderer, {"sdl2", "glfw"}) - */ -#define E2D_REG_GRAPHICS_BACKEND(name, RendererClass, windowBackends) \ - namespace { \ - __attribute__((used)) static struct E2D_GRAPHICS_BACKEND_REG_##name { \ - E2D_GRAPHICS_BACKEND_REG_##name() { \ - ::extra2d::graphics::BackendFactory::reg( \ - #name, \ - []() -> ::extra2d::UniquePtr<::extra2d::RenderBackend> { \ - return ::extra2d::makeUnique(); \ - }, \ - windowBackends); \ - } \ - } e2d_graphics_backend_reg_##name; \ - } - -} // namespace graphics -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/backends/opengl/gl_buffer.h b/Extra2D/include/extra2d/graphics/backends/opengl/gl_buffer.h deleted file mode 100644 index 7138b4c..0000000 --- a/Extra2D/include/extra2d/graphics/backends/opengl/gl_buffer.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// OpenGL 缓冲区实现 -// ============================================================================ -class GLBuffer : public Buffer { -public: - /** - * @brief 构造函数 - */ - GLBuffer(); - - /** - * @brief 析构函数 - */ - ~GLBuffer() override; - - /** - * @brief 初始化缓冲区 - * @param desc 缓冲区描述 - * @return 成功返回 true - */ - bool init(const BufferDesc& desc); - - /** - * @brief 关闭缓冲区,释放资源 - */ - void shutdown(); - - // Buffer 接口实现 - void bind() override; - void unbind() override; - void setData(const void* data, size_t size) override; - void updateData(const void* data, size_t offset, size_t size) override; - void* map() override; - void unmap() override; - size_t getSize() const override { return size_; } - BufferType getType() const override { return type_; } - BufferUsage getUsage() const override { return usage_; } - bool isValid() const override { return bufferID_ != 0; } - uintptr_t getNativeHandle() const override { return static_cast(bufferID_); } - - /** - * @brief 获取 OpenGL 缓冲区 ID - * @return 缓冲区 ID - */ - GLuint getBufferID() const { return bufferID_; } - - /** - * @brief 获取 OpenGL 缓冲区目标类型 - * @return 缓冲区目标类型 - */ - GLenum getTarget() const { return target_; } - -private: - GLuint bufferID_ = 0; - GLenum target_ = GL_ARRAY_BUFFER; - size_t size_ = 0; - BufferType type_ = BufferType::Vertex; - BufferUsage usage_ = BufferUsage::Static; - GLenum glUsage_ = GL_STATIC_DRAW; - bool mapped_ = false; - void* mappedPtr_ = nullptr; - - /** - * @brief 转换使用模式到 OpenGL 枚举 - */ - static GLenum convertUsage(BufferUsage usage); - - /** - * @brief 转换缓冲区类型到 OpenGL 目标 - */ - static GLenum convertType(BufferType type); -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/backends/opengl/gl_context.h b/Extra2D/include/extra2d/graphics/backends/opengl/gl_context.h deleted file mode 100644 index f0da492..0000000 --- a/Extra2D/include/extra2d/graphics/backends/opengl/gl_context.h +++ /dev/null @@ -1,138 +0,0 @@ -#pragma once - -#include -#include - -namespace extra2d { - -// ============================================================================ -// OpenGL 版本信息 -// ============================================================================ -struct GLVersion { - int major = 0; - int minor = 0; - bool es = false; // 是否为 ES 版本 -}; - -// ============================================================================ -// OpenGL 上下文管理类 -// ============================================================================ -class GLContext { -public: - /** - * @brief 获取全局 GLContext 实例 - */ - static GLContext &get(); - - /** - * @brief 初始化 OpenGL 上下文 - * @return 成功返回 true - */ - bool init(); - - /** - * @brief 关闭 OpenGL 上下文 - */ - void shutdown(); - - /** - * @brief 检查上下文是否有效 - * @return 有效返回 true - */ - bool isValid() const { return initialized_; } - - /** - * @brief 获取 OpenGL 版本信息 - */ - const GLVersion &getVersion() const { return version_; } - - /** - * @brief 获取 OpenGL 版本字符串 - */ - std::string getVersionString() const; - - /** - * @brief 获取 GPU 厂商信息 - */ - std::string getVendor() const; - - /** - * @brief 获取 GPU 渲染器信息 - */ - std::string getRenderer() const; - - /** - * @brief 检查是否支持指定扩展 - * @param extension 扩展名称 - * @return 支持返回 true - */ - bool hasExtension(const std::string &extension) const; - - /** - * @brief 获取最大纹理尺寸 - */ - int getMaxTextureSize() const; - - /** - * @brief 获取最大纹理单元数 - */ - int getMaxTextureUnits() const; - - /** - * @brief 获取最大顶点属性数 - */ - int getMaxVertexAttribs() const; - - /** - * @brief 获取最大 uniform 缓冲区绑定点数 - */ - int getMaxUniformBufferBindings() const; - - /** - * @brief 检查是否为 OpenGL ES - */ - bool isGLES() const { return version_.es; } - - /** - * @brief 检查是否支持 VAO - */ - bool hasVAO() const; - - /** - * @brief 检查是否支持 FBO - */ - bool hasFBO() const; - - /** - * @brief 检查是否支持 Shader - */ - bool hasShader() const; - -private: - GLContext() = default; - ~GLContext() = default; - - GLContext(const GLContext &) = delete; - GLContext &operator=(const GLContext &) = delete; - - bool initialized_ = false; - GLVersion version_; - - // 缓存的限制值 - mutable int maxTextureSize_ = -1; - mutable int maxTextureUnits_ = -1; - mutable int maxVertexAttribs_ = -1; - mutable int maxUniformBufferBindings_ = -1; - - /** - * @brief 解析 OpenGL 版本 - */ - void parseVersion(); - - /** - * @brief 加载 OpenGL 扩展 - */ - bool loadExtensions(); -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/backends/opengl/gl_font_atlas.h b/Extra2D/include/extra2d/graphics/backends/opengl/gl_font_atlas.h deleted file mode 100644 index 72d286c..0000000 --- a/Extra2D/include/extra2d/graphics/backends/opengl/gl_font_atlas.h +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -#include -#include - -namespace extra2d { - -// ============================================================================ -// OpenGL 字体图集实现 (使用 STB 库) -// 使用 stb_rect_pack 进行动态矩形打包,支持动态缓存字形 -// ============================================================================ -class GLFontAtlas : public FontAtlas { -public: - GLFontAtlas(const std::string& filepath, int fontSize, bool useSDF = false); - ~GLFontAtlas() override; - - // FontAtlas 接口实现 - const Glyph* getGlyph(char32_t codepoint) const override; - Texture* getTexture() const override { return texture_.get(); } - int getFontSize() const override { return fontSize_; } - float getAscent() const override { return ascent_; } - float getDescent() const override { return descent_; } - float getLineGap() const override { return lineGap_; } - float getLineHeight() const override { return lineHeight_; } - bool isSDF() const override { return useSDF_; } - Vec2 measureText(const std::string& text) override; - -private: - // 字形数据内部结构 - struct GlyphData { - float width; - float height; - float bearingX; - float bearingY; - float advance; - float u0, v0, u1, v1; - }; - - // 图集配置 - 增大尺寸以支持更多字符 - static constexpr int ATLAS_WIDTH = 1024; - static constexpr int ATLAS_HEIGHT = 1024; - static constexpr int PADDING = 2; // 字形之间的间距 - - bool useSDF_; - int fontSize_; - - Ptr texture_; - std::unordered_map glyphs_; - float lineHeight_; - float ascent_; - float descent_; - float lineGap_; - - // 字体数据 - std::vector fontData_; - stbtt_fontinfo fontInfo_; - float scale_; - - // stb_rect_pack 上下文 - 持久化以支持增量打包 - mutable stbrp_context packContext_; - mutable std::vector packNodes_; - - // 预分配缓冲区,避免每次动态分配 - mutable std::vector glyphBitmapCache_; - mutable std::vector glyphRgbaCache_; - - // 初始化字体 - bool initFont(const std::string& filepath); - // 创建空白图集纹理 - void createAtlas(); - // 缓存字形到图集 - void cacheGlyph(char32_t codepoint); - // 更新图集纹理区域 - void updateAtlas(int x, int y, int width, int height, - const std::vector& data); -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/backends/opengl/gl_framebuffer.h b/Extra2D/include/extra2d/graphics/backends/opengl/gl_framebuffer.h deleted file mode 100644 index f5286d5..0000000 --- a/Extra2D/include/extra2d/graphics/backends/opengl/gl_framebuffer.h +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// OpenGL 帧缓冲实现 -// ============================================================================ -class GLFramebuffer : public Framebuffer { -public: - // 最大颜色附件数 - static constexpr int MAX_COLOR_ATTACHMENTS = 8; - - /** - * @brief 构造函数 - */ - GLFramebuffer(); - - /** - * @brief 析构函数 - */ - ~GLFramebuffer() override; - - /** - * @brief 初始化帧缓冲 - * @param desc 帧缓冲描述 - * @return 成功返回 true - */ - bool init(const FramebufferDesc& desc); - - /** - * @brief 关闭帧缓冲,释放资源 - */ - void shutdown(); - - // Framebuffer 接口实现 - void bind() override; - void unbind() override; - void attachColorTexture(Ptr texture, int attachment = 0) override; - void attachDepthTexture(Ptr texture) override; - void attachDepthStencilTexture(Ptr texture) override; - bool isComplete() override; - Ptr getColorTexture(int attachment = 0) const override; - Ptr getDepthTexture() const override; - int getWidth() const override { return width_; } - int getHeight() const override { return height_; } - Size getSize() const override { return Size(static_cast(width_), static_cast(height_)); } - bool isValid() const override { return fboID_ != 0; } - uintptr_t getNativeHandle() const override { return static_cast(fboID_); } - void clear(const Color& color, bool clearColor = true, - bool clearDepth = true, bool clearStencil = false) override; - void setViewport(int x, int y, int width, int height) override; - bool readPixels(int x, int y, int width, int height, - std::vector& outData) override; - - /** - * @brief 获取 OpenGL FBO ID - * @return FBO ID - */ - GLuint getFboID() const { return fboID_; } - - /** - * @brief 创建带内置纹理的帧缓冲(便捷方法) - * @param width 宽度 - * @param height 高度 - * @param colorFormat 颜色格式 - * @param depthFormat 深度格式(可选) - * @return 成功返回 true - */ - bool createWithTextures(int width, int height, - PixelFormat colorFormat = PixelFormat::RGBA8, - PixelFormat depthFormat = PixelFormat::Depth24); - -private: - GLuint fboID_ = 0; - int width_ = 0; - int height_ = 0; - int numColorAttachments_ = 1; - bool hasDepth_ = false; - bool hasStencil_ = false; - - // 附件纹理 - std::array, MAX_COLOR_ATTACHMENTS> colorTextures_; - Ptr depthTexture_; - Ptr depthStencilTexture_; - - // 是否为内置纹理(需要自动清理) - bool hasInternalTextures_ = false; - - /** - * @brief 检查并更新完整状态 - */ - bool checkStatus(); - - /** - * @brief 获取 OpenGL 附件枚举 - */ - static GLenum getColorAttachment(int index); -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/backends/opengl/gl_pipeline.h b/Extra2D/include/extra2d/graphics/backends/opengl/gl_pipeline.h deleted file mode 100644 index ca0f439..0000000 --- a/Extra2D/include/extra2d/graphics/backends/opengl/gl_pipeline.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// OpenGL 管线状态实现 -// ============================================================================ -class GLPipeline : public Pipeline { -public: - /** - * @brief 构造函数 - */ - GLPipeline(); - - /** - * @brief 析构函数 - */ - ~GLPipeline() override; - - /** - * @brief 初始化管线 - * @param desc 管线描述 - * @return 成功返回 true - */ - bool init(const PipelineDesc& desc); - - /** - * @brief 关闭管线,释放资源 - */ - void shutdown(); - - // Pipeline 接口实现 - void bind() override; - void unbind() override; - void setBlendMode(BlendMode mode) override; - BlendMode getBlendMode() const override { return blendMode_; } - void setDepthTest(bool enabled) override; - void setDepthWrite(bool enabled) override; - void setDepthFunc(DepthFunc func) override; - void setCullMode(CullMode mode) override; - bool isValid() const override { return initialized_; } - uintptr_t getNativeHandle() const override { return 0; } // OpenGL 管线没有单一句柄 - - /** - * @brief 设置视口 - * @param x 视口左下角X坐标 - * @param y 视口左下角Y坐标 - * @param width 视口宽度 - * @param height 视口高度 - */ - void setViewport(int x, int y, int width, int height); - - /** - * @brief 获取当前视口 - * @param x 输出X坐标 - * @param y 输出Y坐标 - * @param width 输出宽度 - * @param height 输出高度 - */ - void getViewport(int& x, int& y, int& width, int& height) const; - - /** - * @brief 应用所有状态(用于初始化或重置) - */ - void applyAllStates(); - -private: - bool initialized_ = false; - - // 当前状态 - BlendMode blendMode_ = BlendMode::Alpha; - bool blendEnabled_ = true; - bool depthTest_ = false; - bool depthWrite_ = false; - DepthFunc depthFunc_ = DepthFunc::Less; - CullMode cullMode_ = CullMode::None; - - // 视口 - int viewportX_ = 0; - int viewportY_ = 0; - int viewportWidth_ = 0; - int viewportHeight_ = 0; - - // 状态缓存(避免冗余 GL 调用) - BlendMode cachedBlendMode_ = BlendMode::None; - bool cachedBlendEnabled_ = false; - bool cachedDepthTest_ = false; - bool cachedDepthWrite_ = false; - DepthFunc cachedDepthFunc_ = DepthFunc::Less; - CullMode cachedCullMode_ = CullMode::None; - int cachedViewportX_ = -1; - int cachedViewportY_ = -1; - int cachedViewportWidth_ = -1; - int cachedViewportHeight_ = -1; - - /** - * @brief 应用混合状态 - */ - void applyBlendState(); - - /** - * @brief 应用深度状态 - */ - void applyDepthState(); - - /** - * @brief 应用裁剪状态 - */ - void applyCullState(); - - /** - * @brief 转换混合模式到 OpenGL 枚举 - */ - static void getBlendFactors(BlendMode mode, GLenum& srcFactor, GLenum& dstFactor); - - /** - * @brief 转换深度函数到 OpenGL 枚举 - */ - static GLenum convertDepthFunc(DepthFunc func); - - /** - * @brief 转换裁剪模式到 OpenGL 枚举 - */ - static GLenum convertCullMode(CullMode mode); -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/backends/opengl/gl_renderer.h b/Extra2D/include/extra2d/graphics/backends/opengl/gl_renderer.h deleted file mode 100644 index 67869f1..0000000 --- a/Extra2D/include/extra2d/graphics/backends/opengl/gl_renderer.h +++ /dev/null @@ -1,183 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace extra2d { - -// 前向声明 -class IWindow; -class GLContext; -class GLFramebuffer; - -// ============================================================================ -// OpenGL 渲染器实现 -// ============================================================================ -class GLRenderer : public RenderBackend { -public: - GLRenderer(); - ~GLRenderer() override; - - // RenderBackend 接口实现 - bool init(IWindow* window) override; - void shutdown() override; - - void beginFrame(const Color &clearColor) override; - void endFrame() override; - void setViewport(int x, int y, int width, int height) override; - void setVSync(bool enabled) override; - - void setBlendMode(BlendMode mode) override; - void setViewProjection(const glm::mat4 &matrix) override; - - // 变换矩阵栈 - void pushTransform(const glm::mat4 &transform) override; - void popTransform() override; - glm::mat4 getCurrentTransform() const override; - - Ptr createTexture(int width, int height, const uint8_t *pixels, - int channels) override; - Ptr loadTexture(const std::string &filepath) override; - - void beginSpriteBatch() override; - void drawSprite(const Texture &texture, const Rect &destRect, - const Rect &srcRect, const Color &tint, float rotation, - const Vec2 &anchor) override; - void drawSprite(const Texture &texture, const Vec2 &position, - const Color &tint) override; - void endSpriteBatch() override; - void flush() override; - - void drawLine(const Vec2 &start, const Vec2 &end, const Color &color, - float width) override; - void drawRect(const Rect &rect, const Color &color, float width) override; - void fillRect(const Rect &rect, const Color &color) override; - void drawCircle(const Vec2 ¢er, float radius, const Color &color, - int segments, float width) override; - void fillCircle(const Vec2 ¢er, float radius, const Color &color, - int segments) override; - void drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, - const Color &color, float width) override; - void fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, - const Color &color) override; - void drawPolygon(const std::vector &points, const Color &color, - float width) override; - void fillPolygon(const std::vector &points, - const Color &color) override; - - Ptr createFontAtlas(const std::string &filepath, int fontSize, - bool useSDF = false) override; - void drawText(const FontAtlas &font, const std::string &text, - const Vec2 &position, const Color &color) override; - void drawText(const FontAtlas &font, const std::string &text, float x, - float y, const Color &color) override; - - Stats getStats() const override { return stats_; } - void resetStats() override; - - // GLFramebuffer 相关方法 - - /** - * @brief 创建帧缓冲对象 - * @param desc 帧缓冲描述 - * @return 创建的帧缓冲智能指针 - */ - Ptr createFramebuffer(const FramebufferDesc& desc); - - /** - * @brief 绑定帧缓冲(作为渲染目标) - * @param framebuffer 帧缓冲对象指针,传入 nullptr 则绑定默认帧缓冲 - */ - void bindFramebuffer(GLFramebuffer* framebuffer); - - /** - * @brief 解绑帧缓冲(恢复到默认帧缓冲) - */ - void unbindFramebuffer(); - - /** - * @brief 获取默认帧缓冲 - * @return 默认帧缓冲智能指针 - */ - Ptr getDefaultFramebuffer() const; - - /** - * @brief 清除当前绑定的帧缓冲 - * @param color 清除颜色 - * @param clearColor 是否清除颜色缓冲 - * @param clearDepth 是否清除深度缓冲 - * @param clearStencil 是否清除模板缓冲 - */ - void clearFramebuffer(const Color& color, bool clearColor = true, - bool clearDepth = true, bool clearStencil = false); - -private: - // 形状批处理常量 - static constexpr size_t MAX_CIRCLE_SEGMENTS = 128; - static constexpr size_t MAX_SHAPE_VERTICES = 8192; // 最大形状顶点数 - static constexpr size_t MAX_LINE_VERTICES = 16384; // 最大线条顶点数 - - // 形状顶点结构(包含颜色) - struct ShapeVertex { - float x, y; - float r, g, b, a; - }; - - IWindow* window_; - GLSpriteBatch spriteBatch_; - Ptr shapeShader_; - Ptr sdfFontShader_; // SDF字体专用着色器 - - GLuint shapeVao_; // 形状 VAO(手动管理,用于顶点属性配置) - GLBuffer shapeBuffer_; // 形状 VBO(使用 GLBuffer 管理) - GLuint lineVao_; // 线条 VAO(手动管理,用于顶点属性配置) - GLBuffer lineBuffer_; // 线条 VBO(使用 GLBuffer 管理) - - glm::mat4 viewProjection_; - std::vector transformStack_; - Stats stats_; - bool vsync_; - - // 形状批处理缓冲区(预分配,避免每帧内存分配) - std::array shapeVertexCache_; - size_t shapeVertexCount_ = 0; - GLenum currentShapeMode_ = GL_TRIANGLES; - - // 线条批处理缓冲区 - std::array lineVertexCache_; - size_t lineVertexCount_ = 0; - float currentLineWidth_ = 1.0f; - - // OpenGL 管线状态管理 - GLPipeline pipeline_; - - // 自动批处理状态 - bool batchActive_ = false; // 批处理是否激活 - bool autoBatchEnabled_ = true; // 是否启用自动批处理 - const Texture* currentBatchTexture_ = nullptr; // 当前批处理的纹理 - std::vector pendingSprites_; // 待提交的精灵 - static constexpr size_t MAX_BATCH_SPRITES = 1000; // 最大批处理精灵数 - - // 帧缓冲管理 - mutable Ptr defaultFramebuffer_; // 默认帧缓冲(延迟创建) - GLFramebuffer* currentFramebuffer_ = nullptr; // 当前绑定的帧缓冲 - - void initShapeRendering(); - void ensureBatchActive(); // 确保批处理已激活 - void submitPendingSprites(); // 提交待处理的精灵 - void flushShapeBatch(); - void flushLineBatch(); - void addShapeVertex(float x, float y, const Color &color); - void addLineVertex(float x, float y, const Color &color); - void submitShapeBatch(GLenum mode); -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/backends/opengl/gl_shader.h b/Extra2D/include/extra2d/graphics/backends/opengl/gl_shader.h deleted file mode 100644 index f61c7b5..0000000 --- a/Extra2D/include/extra2d/graphics/backends/opengl/gl_shader.h +++ /dev/null @@ -1,196 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace extra2d { - -class GLShader : public IShader { -public: - /** - * @brief 构造函数 - */ - GLShader(); - - /** - * @brief 析构函数 - */ - ~GLShader() override; - - /** - * @brief 绑定Shader程序 - */ - void bind() const override; - - /** - * @brief 解绑Shader程序 - */ - void unbind() const override; - - /** - * @brief 设置布尔类型uniform变量 - * @param name uniform变量名 - * @param value 布尔值 - */ - void setBool(const std::string& name, bool value) override; - - /** - * @brief 设置整数类型uniform变量 - * @param name uniform变量名 - * @param value 整数值 - */ - void setInt(const std::string& name, int value) override; - - /** - * @brief 设置浮点类型uniform变量 - * @param name uniform变量名 - * @param value 浮点值 - */ - void setFloat(const std::string& name, float value) override; - - /** - * @brief 设置二维向量类型uniform变量 - * @param name uniform变量名 - * @param value 二维向量值 - */ - void setVec2(const std::string& name, const glm::vec2& value) override; - - /** - * @brief 设置三维向量类型uniform变量 - * @param name uniform变量名 - * @param value 三维向量值 - */ - void setVec3(const std::string& name, const glm::vec3& value) override; - - /** - * @brief 设置四维向量类型uniform变量 - * @param name uniform变量名 - * @param value 四维向量值 - */ - void setVec4(const std::string& name, const glm::vec4& value) override; - - /** - * @brief 设置4x4矩阵类型uniform变量 - * @param name uniform变量名 - * @param value 4x4矩阵值 - */ - void setMat4(const std::string& name, const glm::mat4& value) override; - - /** - * @brief 设置颜色类型uniform变量 - * @param name uniform变量名 - * @param color 颜色值 - */ - void setColor(const std::string& name, const Color& color) override; - - /** - * @brief 检查Shader是否有效 - * @return 有效返回true,否则返回false - */ - bool isValid() const override { return programID_ != 0; } - - /** - * @brief 获取原生句柄(OpenGL程序ID) - * @return OpenGL程序ID - */ - uint32_t getNativeHandle() const override { return programID_; } - - /** - * @brief 获取Shader名称 - * @return Shader名称 - */ - const std::string& getName() const override { return name_; } - - /** - * @brief 设置Shader名称 - * @param name Shader名称 - */ - void setName(const std::string& name) override { name_ = name; } - - /** - * @brief 从源码编译Shader - * @param vertexSource 顶点着色器源码 - * @param fragmentSource 片段着色器源码 - * @return 编译成功返回true,失败返回false - */ - bool compileFromSource(const char* vertexSource, const char* fragmentSource); - - /** - * @brief 从二进制数据创建Shader - * @param binary 二进制数据 - * @return 创建成功返回true,失败返回false - */ - bool compileFromBinary(const std::vector& binary); - - /** - * @brief 获取Shader二进制数据 - * @param outBinary 输出的二进制数据 - * @return 成功返回true,失败返回false - */ - bool getBinary(std::vector& outBinary); - - /** - * @brief 获取OpenGL程序ID - * @return OpenGL程序ID - */ - GLuint getProgramID() const { return programID_; } - -private: - GLuint programID_ = 0; - std::string name_; - std::unordered_map uniformCache_; - - /** - * @brief 编译单个着色器 - * @param type 着色器类型 - * @param source 着色器源码 - * @return 着色器ID,失败返回0 - */ - GLuint compileShader(GLenum type, const char* source); - - /** - * @brief 获取uniform位置 - * @param name uniform变量名 - * @return uniform位置 - */ - GLint getUniformLocation(const std::string& name); -}; - -class GLShaderFactory : public IShaderFactory { -public: - /** - * @brief 从源码创建Shader - * @param name Shader名称 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 创建的Shader实例 - */ - Ptr createFromSource( - const std::string& name, - const std::string& vertSource, - const std::string& fragSource) override; - - /** - * @brief 从缓存二进制创建Shader - * @param name Shader名称 - * @param binary 编译后的二进制数据 - * @return 创建的Shader实例 - */ - Ptr createFromBinary( - const std::string& name, - const std::vector& binary) override; - - /** - * @brief 获取Shader的二进制数据 - * @param shader Shader实例 - * @param outBinary 输出的二进制数据 - * @return 成功返回true,失败返回false - */ - bool getShaderBinary(const IShader& shader, - std::vector& outBinary) override; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/backends/opengl/gl_sprite_batch.h b/Extra2D/include/extra2d/graphics/backends/opengl/gl_sprite_batch.h deleted file mode 100644 index e2b5650..0000000 --- a/Extra2D/include/extra2d/graphics/backends/opengl/gl_sprite_batch.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include -#include - -namespace extra2d { - -// ============================================================================ -// OpenGL 精灵批处理渲染器 -// 使用 batch/sprite_batch 作为后端无关的批处理层 -// ============================================================================ -class GLSpriteBatch { -public: - GLSpriteBatch(); - ~GLSpriteBatch(); - - // 初始化/关闭 - bool init(); - void shutdown(); - - // 批处理生命周期 - void begin(const glm::mat4 &viewProjection); - void end(); - - // 使用指定着色器开始批处理 - void begin(const glm::mat4 &viewProjection, Ptr shader); - - // 绘制单个精灵 - void draw(const Texture &texture, const SpriteData &data); - - // 批量绘制(用于文本渲染优化) - void drawBatch(const Texture &texture, - const std::vector &sprites); - - // 获取绘制调用次数 - uint32_t getDrawCallCount() const { return drawCallCount_; } - - // 设置自定义着色器(用于SDF字体等特殊渲染) - void setShader(Ptr shader); - - // 获取当前着色器 - Ptr getShader() const { return shader_; } - - // 设置额外的uniform值(用于SDF字体等特殊渲染) - void setExtraUniforms(const UniformValueMap &uniforms) { extraUniforms_ = uniforms; } - void clearExtraUniforms() { extraUniforms_.clear(); } - -private: - // OpenGL 对象 - GLuint vao_; - GLBuffer vbo_; // 顶点缓冲区(动态) - GLBuffer ebo_; // 索引缓冲区(静态) - - // 后端无关的批处理层 - SpriteBatch batch_; - - // 批次管理 - struct Batch { - const GLTexture *texture; - size_t startVertex; - size_t vertexCount; - }; - std::vector batches_; - const GLTexture *currentTexture_; - - // 着色器和矩阵 - Ptr shader_; - uint32_t drawCallCount_; - glm::mat4 viewProjection_; - - // 额外的uniform值(用于SDF字体等特殊渲染) - UniformValueMap extraUniforms_; - - // 内部方法 - void flush(); - void submitBatch(); -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/backends/opengl/gl_texture.h b/Extra2D/include/extra2d/graphics/backends/opengl/gl_texture.h deleted file mode 100644 index df9cd6d..0000000 --- a/Extra2D/include/extra2d/graphics/backends/opengl/gl_texture.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include -#include - -#include - -#include -#include - -namespace extra2d { - -// ============================================================================ -// OpenGL 纹理实现 -// ============================================================================ -class GLTexture : public Texture { -public: - GLTexture(int width, int height, const uint8_t* pixels, int channels); - GLTexture(const std::string& filepath); - ~GLTexture(); - - // Texture 接口实现 - int getWidth() const override { return width_; } - int getHeight() const override { return height_; } - Size getSize() const override { return Size(static_cast(width_), static_cast(height_)); } - int getChannels() const override { return channels_; } - PixelFormat getFormat() const override; - void* getNativeHandle() const override { return reinterpret_cast(static_cast(textureID_)); } - bool isValid() const override { return textureID_ != 0; } - void setFilter(bool linear) override; - void setWrap(bool repeat) override; - - // 从参数创建纹理的工厂方法 - static Ptr create(int width, int height, PixelFormat format); - - // 加载压缩纹理(KTX/DDS 格式) - bool loadCompressed(const std::string& filepath); - - // OpenGL 特定 - GLuint getTextureID() const { return textureID_; } - void bind(unsigned int slot = 0) const; - void unbind() const; - - // 获取纹理数据大小(字节),用于 VRAM 跟踪 - size_t getDataSize() const { return dataSize_; } - - // Alpha 遮罩 - bool hasAlphaMask() const { return alphaMask_ != nullptr && alphaMask_->isValid(); } - const AlphaMask* getAlphaMask() const { return alphaMask_.get(); } - void generateAlphaMask(); // 从当前纹理数据生成遮罩 - -private: - GLuint textureID_; - int width_; - int height_; - int channels_; - PixelFormat format_; - size_t dataSize_; - - // 原始像素数据(用于生成遮罩) - std::vector pixelData_; - std::unique_ptr alphaMask_; - - void createTexture(const uint8_t* pixels); - - // KTX 文件加载 - bool loadKTX(const std::string& filepath); - // DDS 文件加载 - bool loadDDS(const std::string& filepath); -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/backends/vulkan/vk_renderer.h b/Extra2D/include/extra2d/graphics/backends/vulkan/vk_renderer.h deleted file mode 100644 index 608dd8d..0000000 --- a/Extra2D/include/extra2d/graphics/backends/vulkan/vk_renderer.h +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include - -namespace extra2d { - -/** - * @brief Vulkan 渲染器实现(占位) - * - * 这是一个占位实现,用于展示Vulkan后端应该包含的内容。 - * 完整的实现需要包含Vulkan上下文、设备、交换链、管线等。 - */ -class VulkanRenderer : public RenderBackend { -public: - VulkanRenderer(); - ~VulkanRenderer() override; - - // RenderBackend 接口实现 - bool init(IWindow* window) override; - void shutdown() override; - - void beginFrame(const Color &clearColor) override; - void endFrame() override; - void setViewport(int x, int y, int width, int height) override; - void setVSync(bool enabled) override; - - void setBlendMode(BlendMode mode) override; - void setViewProjection(const glm::mat4 &matrix) override; - - void pushTransform(const glm::mat4 &transform) override; - void popTransform() override; - glm::mat4 getCurrentTransform() const override; - - Ptr createTexture(int width, int height, const uint8_t *pixels, - int channels) override; - Ptr loadTexture(const std::string &filepath) override; - - void beginSpriteBatch() override; - void drawSprite(const Texture &texture, const Rect &destRect, - const Rect &srcRect, const Color &tint, float rotation, - const Vec2 &anchor) override; - void drawSprite(const Texture &texture, const Vec2 &position, - const Color &tint) override; - void endSpriteBatch() override; - - void drawLine(const Vec2 &start, const Vec2 &end, const Color &color, - float width) override; - void drawRect(const Rect &rect, const Color &color, float width) override; - void fillRect(const Rect &rect, const Color &color) override; - void drawCircle(const Vec2 ¢er, float radius, const Color &color, - int segments, float width) override; - void fillCircle(const Vec2 ¢er, float radius, const Color &color, - int segments) override; - void drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, - const Color &color, float width) override; - void fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, - const Color &color) override; - void drawPolygon(const std::vector &points, const Color &color, - float width) override; - void fillPolygon(const std::vector &points, - const Color &color) override; - - Ptr createFontAtlas(const std::string &filepath, int fontSize, - bool useSDF = false) override; - void drawText(const FontAtlas &font, const std::string &text, - const Vec2 &position, const Color &color) override; - void drawText(const FontAtlas &font, const std::string &text, float x, - float y, const Color &color) override; - - Stats getStats() const override { return stats_; } - void resetStats() override; - -private: - Stats stats_; - bool initialized_ = false; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/batch/shape_batch.h b/Extra2D/include/extra2d/graphics/batch/shape_batch.h deleted file mode 100644 index 091f16b..0000000 --- a/Extra2D/include/extra2d/graphics/batch/shape_batch.h +++ /dev/null @@ -1,157 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 形状顶点结构 -// ============================================================================ -struct ShapeVertex { - float x, y; // 位置 - float r, g, b, a; // 颜色 - - ShapeVertex() = default; - ShapeVertex(float px, float py, const Color& c) - : x(px), y(py), r(c.r), g(c.g), b(c.b), a(c.a) {} -}; - -// ============================================================================ -// 形状批处理抽象接口 - 后端无关 -// ============================================================================ -class ShapeBatch { -public: - virtual ~ShapeBatch() = default; - - /** - * @brief 初始化形状批处理 - * @return 成功返回 true - */ - virtual bool init() = 0; - - /** - * @brief 关闭形状批处理,释放资源 - */ - virtual void shutdown() = 0; - - /** - * @brief 开始批处理 - * @param viewProjection 视图投影矩阵 - */ - virtual void begin(const glm::mat4& viewProjection) = 0; - - /** - * @brief 结束批处理并提交绘制 - */ - virtual void end() = 0; - - /** - * @brief 绘制线段 - * @param start 起点 - * @param end 终点 - * @param color 颜色 - * @param width 线宽 - */ - virtual void drawLine(const Vec2& start, const Vec2& end, - const Color& color, float width = 1.0f) = 0; - - /** - * @brief 绘制矩形边框 - * @param rect 矩形区域 - * @param color 颜色 - * @param width 边框宽度 - */ - virtual void drawRect(const Rect& rect, const Color& color, - float width = 1.0f) = 0; - - /** - * @brief 填充矩形 - * @param rect 矩形区域 - * @param color 颜色 - */ - virtual void fillRect(const Rect& rect, const Color& color) = 0; - - /** - * @brief 绘制圆形边框 - * @param center 圆心 - * @param radius 半径 - * @param color 颜色 - * @param segments 分段数 - * @param width 边框宽度 - */ - virtual void drawCircle(const Vec2& center, float radius, - const Color& color, int segments = 32, - float width = 1.0f) = 0; - - /** - * @brief 填充圆形 - * @param center 圆心 - * @param radius 半径 - * @param color 颜色 - * @param segments 分段数 - */ - virtual void fillCircle(const Vec2& center, float radius, - const Color& color, int segments = 32) = 0; - - /** - * @brief 绘制三角形边框 - * @param p1 顶点1 - * @param p2 顶点2 - * @param p3 顶点3 - * @param color 颜色 - * @param width 边框宽度 - */ - virtual void drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3, - const Color& color, float width = 1.0f) = 0; - - /** - * @brief 填充三角形 - * @param p1 顶点1 - * @param p2 顶点2 - * @param p3 顶点3 - * @param color 颜色 - */ - virtual void fillTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3, - const Color& color) = 0; - - /** - * @brief 绘制多边形边框 - * @param points 顶点数组 - * @param color 颜色 - * @param width 边框宽度 - */ - virtual void drawPolygon(const std::vector& points, - const Color& color, float width = 1.0f) = 0; - - /** - * @brief 填充多边形 - * @param points 顶点数组 - * @param color 颜色 - */ - virtual void fillPolygon(const std::vector& points, - const Color& color) = 0; - - /** - * @brief 获取绘制调用次数 - * @return 绘制调用次数 - */ - virtual uint32_t getDrawCallCount() const = 0; - - /** - * @brief 重置绘制调用计数 - */ - virtual void resetDrawCallCount() = 0; - - /** - * @brief 检查是否有效 - * @return 有效返回 true - */ - virtual bool isValid() const = 0; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/batch/sprite_batch.h b/Extra2D/include/extra2d/graphics/batch/sprite_batch.h deleted file mode 100644 index 838f286..0000000 --- a/Extra2D/include/extra2d/graphics/batch/sprite_batch.h +++ /dev/null @@ -1,121 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include - -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 三角函数查表 - 避免每帧计算 sin/cos -// ============================================================================ -class TrigLookup { -public: - TrigLookup(); - - // 通过角度(0-360)获取 sin/cos - float sin(int angle) const; - float cos(int angle) const; - - // 通过弧度获取 sin/cos - float sinRad(float rad) const; - float cosRad(float rad) const; - -private: - static constexpr int TABLE_SIZE = 360 * 4; // 0.25度精度 - std::array sinTable_; - std::array cosTable_; -}; - -// ============================================================================ -// 精灵批次数据 - 后端无关 -// ============================================================================ -struct SpriteVertex { - Vec2 position; - Vec2 texCoord; - Color color; -}; - -struct SpriteData { - Vec2 position; - Vec2 size; - float rotation; - Vec2 pivot; - Color color; - const Texture* texture; - Rect uvRect; -}; - -// ============================================================================ -// 通用精灵批处理 - 后端无关 -// 负责:顶点生成、批次管理、三角函数查表 -// ============================================================================ -class SpriteBatch { -public: - static constexpr size_t MAX_SPRITES = 10000; - static constexpr size_t VERTICES_PER_SPRITE = 4; - static constexpr size_t INDICES_PER_SPRITE = 6; - static constexpr size_t MAX_VERTICES = MAX_SPRITES * VERTICES_PER_SPRITE; - static constexpr size_t MAX_INDICES = MAX_SPRITES * INDICES_PER_SPRITE; - - SpriteBatch(); - ~SpriteBatch() = default; - - // 开始批次 - void begin(const glm::mat4& viewProjection); - - // 结束批次 - 返回需要绘制的批次列表 - void end(); - - // 绘制单个精灵 - void draw(const SpriteData& sprite); - - // 批量绘制 - 一次性处理多个精灵 - void drawBatch(const std::vector& sprites); - - // 立即绘制 - 不缓存,直接提交 - void drawImmediate(const SpriteData& sprite); - - // 获取当前批次数据 - const std::vector& getVertices() const { return vertices_; } - const std::vector& getIndices() const { return indices_; } - size_t getSpriteCount() const { return spriteCount_; } - - // 检查是否需要刷新 - bool needsFlush() const { return spriteCount_ >= MAX_SPRITES; } - - // 清空批次 - void clear(); - -private: - // 三角函数查表 - TrigLookup trigLookup_; - - // 顶点数据 - 使用固定大小数组避免动态分配 - std::vector vertices_; - std::vector indices_; - size_t spriteCount_; - - // 变换矩阵 - glm::mat4 viewProjection_; - glm::mat4 cachedVP_; - bool vpDirty_; - - // 生成索引 - void generateIndices(); - - // 生成顶点 - void generateVertices(const SpriteData& sprite, size_t vertexOffset); - - // 刷新批次 - void flush(); -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/camera/camera.h b/Extra2D/include/extra2d/graphics/camera/camera.h deleted file mode 100644 index 110b9b5..0000000 --- a/Extra2D/include/extra2d/graphics/camera/camera.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace extra2d { - -class ViewportAdapter; - -// ============================================================================ -// 2D 正交相机 -// ============================================================================ -class Camera { -public: - Camera(); - Camera(float left, float right, float bottom, float top); - Camera(const Size &viewport); - ~Camera() = default; - - // ------------------------------------------------------------------------ - // 位置和变换 - // ------------------------------------------------------------------------ - void setPos(const Vec2 &position); - void setPos(float x, float y); - Vec2 getPosition() const { return position_; } - - void setRotation(float degrees); - float getRotation() const { return rotation_; } - - void setZoom(float zoom); - float getZoom() const { return zoom_; } - - // ------------------------------------------------------------------------ - // 视口设置 - // ------------------------------------------------------------------------ - void setViewport(float left, float right, float bottom, float top); - void setViewport(const Rect &rect); - Rect getViewport() const; - - // ------------------------------------------------------------------------ - // 矩阵获取 - // ------------------------------------------------------------------------ - glm::mat4 getViewMatrix() const; - glm::mat4 getProjectionMatrix() const; - glm::mat4 getViewProjectionMatrix() const; - - // ------------------------------------------------------------------------ - // 坐标转换 - // ------------------------------------------------------------------------ - Vec2 screenToWorld(const Vec2 &screenPos) const; - Vec2 worldToScreen(const Vec2 &worldPos) const; - Vec2 screenToWorld(float x, float y) const; - Vec2 worldToScreen(float x, float y) const; - - // ------------------------------------------------------------------------ - // 移动相机 - // ------------------------------------------------------------------------ - void move(const Vec2 &offset); - void move(float x, float y); - - // ------------------------------------------------------------------------ - // 边界限制 - // ------------------------------------------------------------------------ - void setBounds(const Rect &bounds); - void clearBounds(); - void clampToBounds(); - - // ------------------------------------------------------------------------ - // 视口适配器 - // ------------------------------------------------------------------------ - /** - * @brief 设置视口适配器 - * @param adapter 视口适配器指针 - */ - void setViewportAdapter(ViewportAdapter *adapter); - - /** - * @brief 根据视口适配器自动设置视口 - */ - void applyViewportAdapter(); - - // ------------------------------------------------------------------------ - // 快捷方法:看向某点 - // ------------------------------------------------------------------------ - void lookAt(const Vec2 &target); - -private: - Vec2 position_ = Vec2::Zero(); - float rotation_ = 0.0f; - float zoom_ = 1.0f; - - float left_ = -1.0f; - float right_ = 1.0f; - float bottom_ = -1.0f; - float top_ = 1.0f; - - Rect bounds_; - bool hasBounds_ = false; - - ViewportAdapter *viewportAdapter_ = nullptr; - - mutable glm::mat4 viewMatrix_; - mutable glm::mat4 projMatrix_; - mutable glm::mat4 vpMatrix_; - mutable bool viewDirty_ = true; - mutable bool projDirty_ = true; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/camera/viewport_adapter.h b/Extra2D/include/extra2d/graphics/camera/viewport_adapter.h deleted file mode 100644 index 23db783..0000000 --- a/Extra2D/include/extra2d/graphics/camera/viewport_adapter.h +++ /dev/null @@ -1,332 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 视口适配模式枚举 -// ============================================================================ -enum class ViewportMode { - AspectRatio, - Stretch, - Center, - Custom -}; - -// ============================================================================ -// 黑边位置枚举 -// ============================================================================ -enum class LetterboxPosition { - Center, - LeftTop, - RightTop, - LeftBottom, - RightBottom -}; - -// ============================================================================ -// 视口配置结构体 -// ============================================================================ -struct ViewportConfig { - float logicWidth = 1920.0f; - float logicHeight = 1080.0f; - ViewportMode mode = ViewportMode::AspectRatio; - LetterboxPosition letterboxPosition = LetterboxPosition::Center; - Color letterboxColor = Colors::Black; - bool autoScaleInCenterMode = true; - float customScale = 1.0f; - Vec2 customOffset = Vec2::Zero(); - Rect customViewport = Rect::Zero(); -}; - -// ============================================================================ -// 视口计算结果结构体 -// ============================================================================ -struct ViewportResult { - Rect viewport; - float scaleX = 1.0f; - float scaleY = 1.0f; - float uniformScale = 1.0f; - Vec2 offset; - bool hasLetterbox = false; - - struct Letterbox { - Rect top; - Rect bottom; - Rect left; - Rect right; - } letterbox; -}; - -// ============================================================================ -// 视口适配器类 -// ============================================================================ -class ViewportAdapter { -public: - ViewportAdapter(); - ViewportAdapter(float logicWidth, float logicHeight); - ~ViewportAdapter() = default; - - // ------------------------------------------------------------------------ - // 配置设置 - // ------------------------------------------------------------------------ - - /** - * @brief 设置视口配置 - * @param config 视口配置结构体 - */ - void setConfig(const ViewportConfig &config); - - /** - * @brief 获取当前视口配置 - * @return 当前视口配置 - */ - const ViewportConfig &getConfig() const { return config_; } - - /** - * @brief 设置逻辑分辨率 - * @param width 逻辑宽度 - * @param height 逻辑高度 - */ - void setLogicSize(float width, float height); - - /** - * @brief 设置视口适配模式 - * @param mode 适配模式 - */ - void setMode(ViewportMode mode); - - /** - * @brief 设置黑边位置 - * @param position 黑边位置 - */ - void setLetterboxPosition(LetterboxPosition position); - - /** - * @brief 设置黑边颜色 - * @param color 黑边颜色 - */ - void setLetterboxColor(const Color &color); - - // ------------------------------------------------------------------------ - // 更新和计算 - // ------------------------------------------------------------------------ - - /** - * @brief 更新视口适配计算 - * @param screenWidth 屏幕宽度 - * @param screenHeight 屏幕高度 - */ - void update(int screenWidth, int screenHeight); - - /** - * @brief 获取计算结果 - * @return 视口计算结果 - */ - const ViewportResult &getResult() const { return result_; } - - // ------------------------------------------------------------------------ - // 坐标转换 - // ------------------------------------------------------------------------ - - /** - * @brief 屏幕坐标转逻辑坐标 - * @param screenPos 屏幕坐标 - * @return 逻辑坐标 - */ - Vec2 screenToLogic(const Vec2 &screenPos) const; - - /** - * @brief 逻辑坐标转屏幕坐标 - * @param logicPos 逻辑坐标 - * @return 屏幕坐标 - */ - Vec2 logicToScreen(const Vec2 &logicPos) const; - - /** - * @brief 屏幕坐标转逻辑坐标(分量形式) - * @param x 屏幕X坐标 - * @param y 屏幕Y坐标 - * @return 逻辑坐标 - */ - Vec2 screenToLogic(float x, float y) const; - - /** - * @brief 逻辑坐标转屏幕坐标(分量形式) - * @param x 逻辑X坐标 - * @param y 逻辑Y坐标 - * @return 屏幕坐标 - */ - Vec2 logicToScreen(float x, float y) const; - - // ------------------------------------------------------------------------ - // 矩阵获取 - // ------------------------------------------------------------------------ - - /** - * @brief 获取视口变换矩阵 - * @return 视口变换矩阵(从逻辑坐标到屏幕坐标) - */ - glm::mat4 getMatrix() const; - - /** - * @brief 获取反向视口变换矩阵 - * @return 反向视口变换矩阵(从屏幕坐标到逻辑坐标) - */ - glm::mat4 getInvMatrix() const; - - // ------------------------------------------------------------------------ - // 区域检测 - // ------------------------------------------------------------------------ - - /** - * @brief 检查屏幕坐标是否在视口内 - * @param screenPos 屏幕坐标 - * @return 如果在视口内返回 true - */ - bool isInViewport(const Vec2 &screenPos) const; - - /** - * @brief 检查屏幕坐标是否在黑边区域 - * @param screenPos 屏幕坐标 - * @return 如果在黑边区域返回 true - */ - bool isInLetterbox(const Vec2 &screenPos) const; - - // ------------------------------------------------------------------------ - // Getter 方法 - // ------------------------------------------------------------------------ - - /** - * @brief 获取逻辑宽度 - * @return 逻辑宽度 - */ - float getLogicWidth() const { return config_.logicWidth; } - - /** - * @brief 获取逻辑高度 - * @return 逻辑高度 - */ - float getLogicHeight() const { return config_.logicHeight; } - - /** - * @brief 获取逻辑尺寸 - * @return 逻辑尺寸 - */ - Size getLogicSize() const { - return Size(config_.logicWidth, config_.logicHeight); - } - - /** - * @brief 获取屏幕宽度 - * @return 屏幕宽度 - */ - int getScreenWidth() const { return screenWidth_; } - - /** - * @brief 获取屏幕高度 - * @return 屏幕高度 - */ - int getScreenHeight() const { return screenHeight_; } - - /** - * @brief 获取屏幕尺寸 - * @return 屏幕尺寸 - */ - Size getScreenSize() const { - return Size(static_cast(screenWidth_), - static_cast(screenHeight_)); - } - - /** - * @brief 获取X方向缩放比例 - * @return X方向缩放比例 - */ - float getScaleX() const { return result_.scaleX; } - - /** - * @brief 获取Y方向缩放比例 - * @return Y方向缩放比例 - */ - float getScaleY() const { return result_.scaleY; } - - /** - * @brief 获取统一缩放比例 - * @return 统一缩放比例 - */ - float getUniformScale() const { return result_.uniformScale; } - - /** - * @brief 获取视口偏移 - * @return 视口偏移 - */ - Vec2 getOffset() const { return result_.offset; } - - /** - * @brief 获取视口矩形 - * @return 视口矩形 - */ - Rect getViewport() const { return result_.viewport; } - - /** - * @brief 检查是否有黑边 - * @return 如果有黑边返回 true - */ - bool hasLetterbox() const { return result_.hasLetterbox; } - - /** - * @brief 获取黑边信息 - * @return 黑边信息结构体 - */ - const ViewportResult::Letterbox &getLetterbox() const { - return result_.letterbox; - } - -private: - /** - * @brief 计算宽高比适配模式 - */ - void calculateAspectRatio(); - - /** - * @brief 计算拉伸适配模式 - */ - void calculateStretch(); - - /** - * @brief 计算居中适配模式 - */ - void calculateCenter(); - - /** - * @brief 计算自定义适配模式 - */ - void calculateCustom(); - - /** - * @brief 计算黑边区域 - */ - void calculateLetterbox(); - - /** - * @brief 根据黑边位置调整偏移 - * @param extraWidth 额外宽度 - * @param extraHeight 额外高度 - */ - void applyLetterboxPosition(float extraWidth, float extraHeight); - - ViewportConfig config_; - ViewportResult result_; - int screenWidth_ = 0; - int screenHeight_ = 0; - - mutable glm::mat4 viewportMatrix_; - mutable glm::mat4 inverseViewportMatrix_; - mutable bool matrixDirty_ = true; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/core/render_backend.h b/Extra2D/include/extra2d/graphics/core/render_backend.h deleted file mode 100644 index b12bcb4..0000000 --- a/Extra2D/include/extra2d/graphics/core/render_backend.h +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace extra2d { - -// 前向声明 -class IWindow; -class Texture; -class FontAtlas; -class Shader; - -// BlendMode 定义在 pipeline.h 中 - -// ============================================================================ -// 渲染后端抽象接口 -// ============================================================================ -class RenderBackend { -public: - virtual ~RenderBackend() = default; - - // ------------------------------------------------------------------------ - // 生命周期 - // ------------------------------------------------------------------------ - virtual bool init(IWindow* window) = 0; - virtual void shutdown() = 0; - - // ------------------------------------------------------------------------ - // 帧管理 - // ------------------------------------------------------------------------ - virtual void beginFrame(const Color &clearColor) = 0; - virtual void endFrame() = 0; - virtual void setViewport(int x, int y, int width, int height) = 0; - virtual void setVSync(bool enabled) = 0; - - // ------------------------------------------------------------------------ - // 状态设置 - // ------------------------------------------------------------------------ - virtual void setBlendMode(BlendMode mode) = 0; - virtual void setViewProjection(const glm::mat4 &matrix) = 0; - - // ------------------------------------------------------------------------ - // 变换矩阵栈 - // ------------------------------------------------------------------------ - virtual void pushTransform(const glm::mat4 &transform) = 0; - virtual void popTransform() = 0; - virtual glm::mat4 getCurrentTransform() const = 0; - - // ------------------------------------------------------------------------ - // 纹理 - // ------------------------------------------------------------------------ - virtual Ptr createTexture(int width, int height, - const uint8_t *pixels, int channels) = 0; - virtual Ptr loadTexture(const std::string &filepath) = 0; - - // ------------------------------------------------------------------------ - // 精灵批渲染 - // ------------------------------------------------------------------------ - /** - * @brief 开始手动精灵批处理(高级用法) - * @note 一般情况下不需要调用,drawSprite/drawText 会自动管理批处理 - */ - virtual void beginSpriteBatch() = 0; - virtual void drawSprite(const Texture &texture, const Rect &destRect, - const Rect &srcRect, const Color &tint, - float rotation, const Vec2 &anchor) = 0; - virtual void drawSprite(const Texture &texture, const Vec2 &position, - const Color &tint) = 0; - virtual void endSpriteBatch() = 0; - - /** - * @brief 立即提交当前批处理 - * @note 手动控制批处理提交时机,一般情况下不需要调用 - */ - virtual void flush() = 0; - - // ------------------------------------------------------------------------ - // 形状渲染 - // ------------------------------------------------------------------------ - virtual void drawLine(const Vec2 &start, const Vec2 &end, const Color &color, - float width = 1.0f) = 0; - virtual void drawRect(const Rect &rect, const Color &color, - float width = 1.0f) = 0; - virtual void fillRect(const Rect &rect, const Color &color) = 0; - virtual void drawCircle(const Vec2 ¢er, float radius, const Color &color, - int segments = 32, float width = 1.0f) = 0; - virtual void fillCircle(const Vec2 ¢er, float radius, const Color &color, - int segments = 32) = 0; - virtual void drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, - const Color &color, float width = 1.0f) = 0; - virtual void fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, - const Color &color) = 0; - virtual void drawPolygon(const std::vector &points, const Color &color, - float width = 1.0f) = 0; - virtual void fillPolygon(const std::vector &points, - const Color &color) = 0; - - // ------------------------------------------------------------------------ - // 文字渲染 - // ------------------------------------------------------------------------ - virtual Ptr createFontAtlas(const std::string &filepath, - int fontSize, bool useSDF = false) = 0; - virtual void drawText(const FontAtlas &font, const std::string &text, - const Vec2 &position, const Color &color) = 0; - virtual void drawText(const FontAtlas &font, const std::string &text, float x, - float y, const Color &color) = 0; - - // ------------------------------------------------------------------------ - // 统计信息 - // ------------------------------------------------------------------------ - struct Stats { - uint32_t drawCalls = 0; - uint32_t triangleCount = 0; - uint32_t textureBinds = 0; - uint32_t shaderBinds = 0; - }; - virtual Stats getStats() const = 0; - virtual void resetStats() = 0; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/core/render_command.h b/Extra2D/include/extra2d/graphics/core/render_command.h deleted file mode 100644 index d2c3aa0..0000000 --- a/Extra2D/include/extra2d/graphics/core/render_command.h +++ /dev/null @@ -1,223 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// 前向声明 -class Texture; -class FontAtlas; - -/** - * @brief 渲染命令类型枚举 - */ -enum class RenderCommandType : uint8_t { - None = 0, - Sprite, // 精灵绘制 - Line, // 线条绘制 - Rect, // 矩形绘制 - FilledRect, // 填充矩形 - Circle, // 圆形绘制 - FilledCircle, // 填充圆形 - Triangle, // 三角形绘制 - FilledTriangle, // 填充三角形 - Polygon, // 多边形绘制 - FilledPolygon, // 填充多边形 - Text, // 文本绘制 - Custom // 自定义绘制 -}; - -/** - * @brief 精灵渲染命令数据 - */ -struct SpriteCommandData { - const Texture* texture; - Rect destRect; - Rect srcRect; - Color tint; - float rotation; - Vec2 anchor; - uint32_t sortKey; // 用于自动排序的键值 - - SpriteCommandData() - : texture(nullptr), destRect(), srcRect(), tint(Colors::White), - rotation(0.0f), anchor(0.0f, 0.0f), sortKey(0) {} - SpriteCommandData(const Texture* tex, const Rect& dest, const Rect& src, - const Color& t, float rot, const Vec2& anc, uint32_t key) - : texture(tex), destRect(dest), srcRect(src), tint(t), - rotation(rot), anchor(anc), sortKey(key) {} -}; - -/** - * @brief 线条渲染命令数据 - */ -struct LineCommandData { - Vec2 start; - Vec2 end; - Color color; - float width; - - LineCommandData() : start(), end(), color(Colors::White), width(1.0f) {} - LineCommandData(const Vec2& s, const Vec2& e, const Color& c, float w) - : start(s), end(e), color(c), width(w) {} -}; - -/** - * @brief 矩形渲染命令数据 - */ -struct RectCommandData { - Rect rect; - Color color; - float width; - bool filled; - - RectCommandData() : rect(), color(Colors::White), width(1.0f), filled(false) {} - RectCommandData(const Rect& r, const Color& c, float w, bool f) - : rect(r), color(c), width(w), filled(f) {} -}; - -/** - * @brief 圆形渲染命令数据 - */ -struct CircleCommandData { - Vec2 center; - float radius; - Color color; - int segments; - float width; - bool filled; - - CircleCommandData() : center(), radius(0.0f), color(Colors::White), - segments(32), width(1.0f), filled(false) {} - CircleCommandData(const Vec2& c, float r, const Color& col, int seg, float w, bool f) - : center(c), radius(r), color(col), segments(seg), width(w), filled(f) {} -}; - -/** - * @brief 三角形渲染命令数据 - */ -struct TriangleCommandData { - Vec2 p1, p2, p3; - Color color; - float width; - bool filled; - - TriangleCommandData() : p1(), p2(), p3(), color(Colors::White), - width(1.0f), filled(false) {} - TriangleCommandData(const Vec2& a, const Vec2& b, const Vec2& c, const Color& col, float w, bool f) - : p1(a), p2(b), p3(c), color(col), width(w), filled(f) {} -}; - -/** - * @brief 多边形渲染命令数据 - */ -struct PolygonCommandData { - std::vector points; - Color color; - float width; - bool filled; - - PolygonCommandData() : color(Colors::White), width(1.0f), filled(false) {} - PolygonCommandData(std::vector pts, const Color& col, float w, bool f) - : points(std::move(pts)), color(col), width(w), filled(f) {} -}; - -/** - * @brief 文本渲染命令数据 - */ -struct TextCommandData { - const FontAtlas* font; - std::string text; - Vec2 position; - Color color; - - TextCommandData() : font(nullptr), text(), position(), color(Colors::White) {} -}; - -/** - * @brief 统一渲染命令结构 - * 使用 variant 存储不同类型的命令数据,减少内存分配 - */ -struct RenderCommand { - RenderCommandType type; - uint32_t layer; // 渲染层级,用于排序 - uint32_t order; // 提交顺序,保证同层级内稳定排序 - glm::mat4 transform; // 变换矩阵 - - // 使用 variant 存储具体数据 - std::variant< - SpriteCommandData, - LineCommandData, - RectCommandData, - CircleCommandData, - TriangleCommandData, - PolygonCommandData, - TextCommandData - > data; - - RenderCommand() : type(RenderCommandType::None), layer(0), order(0), - transform(1.0f) {} - - // 便捷构造函数 - static RenderCommand makeSprite(const Texture* tex, const Rect& dest, - const Rect& src, const Color& tint, - float rot = 0.0f, const Vec2& anc = Vec2(0, 0), - uint32_t lyr = 0); - static RenderCommand makeLine(const Vec2& s, const Vec2& e, const Color& c, - float w = 1.0f, uint32_t lyr = 0); - static RenderCommand makeRect(const Rect& r, const Color& c, - float w = 1.0f, bool fill = false, uint32_t lyr = 0); -}; - -/** - * @brief 渲染命令缓冲区 - * 用于收集和批量处理渲染命令 - */ -class RenderCommandBuffer { -public: - static constexpr size_t INITIAL_CAPACITY = 1024; - static constexpr size_t MAX_CAPACITY = 65536; - - RenderCommandBuffer(); - ~RenderCommandBuffer(); - - // 添加渲染命令 - void addCommand(const RenderCommand& cmd); - void addCommand(RenderCommand&& cmd); - - // 批量添加(预留空间后使用) - RenderCommand& emplaceCommand(); - - // 排序命令(按纹理、层级等) - void sortCommands(); - - // 清空缓冲区 - void clear(); - - // 获取命令列表 - const std::vector& getCommands() const { return commands_; } - std::vector& getCommands() { return commands_; } - - // 统计 - size_t size() const { return commands_.size(); } - bool empty() const { return commands_.empty(); } - size_t capacity() const { return commands_.capacity(); } - - // 预分配空间 - void reserve(size_t capacity); - -private: - std::vector commands_; - uint32_t nextOrder_; - - // 排序比较函数 - static bool compareCommands(const RenderCommand& a, const RenderCommand& b); -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/core/render_module.h b/Extra2D/include/extra2d/graphics/core/render_module.h deleted file mode 100644 index 4f636a3..0000000 --- a/Extra2D/include/extra2d/graphics/core/render_module.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace extra2d { - -/** - * @brief 渲染模块配置结构 - */ -struct RenderCfg { - std::string backend; - int targetFPS; - bool vsync; - int multisamples; - int priority; - - RenderCfg() - : backend("") - , targetFPS(60) - , vsync(true) - , multisamples(0) - , priority(10) - {} -}; - -/** - * @brief 渲染模块 - * 管理渲染后端,自动根据窗口后端选择兼容的渲染器 - */ -class RenderModule : public Module { -public: - /** - * @brief 构造函数(Lambda 配置) - * @param configFn 配置函数 - */ - explicit RenderModule(std::function configFn); - - /** - * @brief 析构函数 - */ - ~RenderModule() override; - - bool init() override; - void shutdown() override; - bool ok() const override { return initialized_; } - const char* name() const override { return "render"; } - int priority() const override { return cfg_.priority; } - - /** - * @brief 获取依赖 - * @return 依赖模块类型列表 - */ - std::vector deps() const override { - return {std::type_index(typeid(WindowModule))}; - } - - /** - * @brief 是否允许并行初始化 - * RenderModule 需要 OpenGL 上下文,必须在主线程初始化 - * @return 不允许并行初始化返回 false - */ - bool allowParallelInit() const override { return false; } - - /** - * @brief 获取渲染器 - * @return 渲染后端指针 - */ - RenderBackend* renderer() const { return renderer_.get(); } - -private: - RenderCfg cfg_; - UniquePtr renderer_; - bool initialized_ = false; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/core/render_target.h b/Extra2D/include/extra2d/graphics/core/render_target.h deleted file mode 100644 index c7dca31..0000000 --- a/Extra2D/include/extra2d/graphics/core/render_target.h +++ /dev/null @@ -1,313 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 渲染目标配置 -// ============================================================================ -struct RenderTargetConfig { - int width = 800; // 宽度 - int height = 600; // 高度 - PixelFormat colorFormat = PixelFormat::RGBA8; // 颜色格式 - bool hasDepth = true; // 是否包含深度缓冲 - bool hasStencil = false; // 是否包含模板缓冲 - int samples = 1; // 多重采样数 (1 = 无MSAA) - bool autoResize = true; // 是否自动调整大小 -}; - -// ============================================================================ -// 渲染目标 - 基于FBO的离屏渲染 -// ============================================================================ -class RenderTarget { -public: - RenderTarget(); - ~RenderTarget(); - - // 禁止拷贝 - RenderTarget(const RenderTarget &) = delete; - RenderTarget &operator=(const RenderTarget &) = delete; - - // 允许移动 - RenderTarget(RenderTarget &&other) noexcept; - RenderTarget &operator=(RenderTarget &&other) noexcept; - - // ------------------------------------------------------------------------ - // 创建和销毁 - // ------------------------------------------------------------------------ - - /** - * @brief 创建渲染目标 - */ - bool create(const RenderTargetConfig &config); - - /** - * @brief 从现有纹理创建渲染目标 - */ - bool createFromTexture(Ptr texture, bool hasDepth = false); - - /** - * @brief 销毁渲染目标 - */ - void destroy(); - - /** - * @brief 检查是否有效 - */ - bool isValid() const { return fbo_ != 0; } - - // ------------------------------------------------------------------------ - // 尺寸和格式 - // ------------------------------------------------------------------------ - - int getWidth() const { return width_; } - int getHeight() const { return height_; } - Vec2 getSize() const { - return Vec2(static_cast(width_), static_cast(height_)); - } - PixelFormat getColorFormat() const { return colorFormat_; } - - // ------------------------------------------------------------------------ - // 绑定和解绑 - // ------------------------------------------------------------------------ - - /** - * @brief 绑定为当前渲染目标 - */ - void bind(); - - /** - * @brief 解绑(恢复默认渲染目标) - */ - void unbind(); - - /** - * @brief 清除渲染目标 - */ - void clear(const Color &color = Colors::Transparent); - - // ------------------------------------------------------------------------ - // 纹理访问 - // ------------------------------------------------------------------------ - - /** - * @brief 获取颜色纹理 - */ - Ptr getColorTexture() const { return colorTexture_; } - - /** - * @brief 获取深度纹理(如果有) - */ - Ptr getDepthTexture() const { return depthTexture_; } - - // ------------------------------------------------------------------------ - // 视口和裁剪 - // ------------------------------------------------------------------------ - - /** - * @brief 设置视口(相对于渲染目标) - */ - void setViewport(int x, int y, int width, int height); - - /** - * @brief 获取完整视口 - */ - void getFullViewport(int &x, int &y, int &width, int &height) const; - - // ------------------------------------------------------------------------ - // 工具方法 - // ------------------------------------------------------------------------ - - /** - * @brief 调整大小(会销毁并重新创建) - */ - bool resize(int width, int height); - - /** - * @brief 复制到另一个渲染目标 - */ - void copyTo(RenderTarget &target); - - /** - * @brief 复制到屏幕 - */ - void copyToScreen(int screenWidth, int screenHeight); - - /** - * @brief 保存为图像文件 - */ - bool saveToFile(const std::string &filepath); - - // ------------------------------------------------------------------------ - // 静态方法 - // ------------------------------------------------------------------------ - - /** - * @brief 创建渲染目标的静态工厂方法 - */ - static Ptr createFromConfig(const RenderTargetConfig &config); - - /** - * @brief 获取当前绑定的渲染目标ID - */ - static GLuint getCurrentFBO(); - - /** - * @brief 绑定默认渲染目标(屏幕) - */ - static void bindDefault(); - - /** - * @brief 获取FBO ID(供内部使用) - */ - GLuint getFBO() const { return fbo_; } - -protected: - GLuint fbo_ = 0; // 帧缓冲对象 - GLuint rbo_ = 0; // 渲染缓冲对象(深度/模板) - - Ptr colorTexture_; // 颜色纹理 - Ptr depthTexture_; // 深度纹理(可选) - - int width_ = 0; - int height_ = 0; - PixelFormat colorFormat_ = PixelFormat::RGBA8; - bool hasDepth_ = false; - bool hasStencil_ = false; - int samples_ = 1; - - bool createFBO(); - void deleteFBO(); -}; - -// ============================================================================ -// 多重采样渲染目标(用于MSAA) -// ============================================================================ -class MultisampleRenderTarget : public RenderTarget { -public: - /** - * @brief 创建多重采样渲染目标 - */ - bool create(int width, int height, int samples = 4); - - /** - * @brief 解析到普通渲染目标(用于显示) - */ - void resolveTo(RenderTarget &target); - - /** - * @brief 销毁渲染目标 - */ - void destroy(); - -private: - GLuint colorRBO_ = 0; // 多重采样颜色渲染缓冲 -}; - -// ============================================================================ -// 渲染目标栈(用于嵌套渲染) -// ============================================================================ -class RenderTargetStack { -public: - static RenderTargetStack &get(); - - /** - * @brief 压入渲染目标 - */ - void push(RenderTarget *target); - - /** - * @brief 弹出渲染目标 - */ - void pop(); - - /** - * @brief 获取当前渲染目标 - */ - RenderTarget *getCurrent() const; - - /** - * @brief 获取栈大小 - */ - size_t size() const; - - /** - * @brief 清空栈 - */ - void clear(); - -private: - RenderTargetStack() = default; - ~RenderTargetStack() = default; - - std::vector stack_; - mutable std::mutex mutex_; -}; - -// ============================================================================ -// 渲染目标管理器 - 全局渲染目标管理 -// ============================================================================ -class RenderTargetMgr { -public: - /** - * @brief 获取单例实例 - */ - static RenderTargetMgr& get(); - - /** - * @brief 初始化渲染目标管理器 - * @param width 默认宽度 - * @param height 默认高度 - */ - bool init(int width, int height); - - /** - * @brief 关闭渲染目标管理器 - */ - void shutdown(); - - /** - * @brief 创建新的渲染目标 - */ - Ptr createRenderTarget(const RenderTargetConfig &config); - - /** - * @brief 获取默认渲染目标 - */ - RenderTarget *getDefaultRenderTarget() const { - return defaultRenderTarget_.get(); - } - - /** - * @brief 调整所有受管渲染目标的大小 - */ - void resize(int width, int height); - - /** - * @brief 检查是否已初始化 - */ - bool isInitialized() const { return initialized_; } - -private: - RenderTargetMgr() = default; - ~RenderTargetMgr() = default; - RenderTargetMgr(const RenderTargetMgr &) = delete; - RenderTargetMgr &operator=(const RenderTargetMgr &) = delete; - - Ptr defaultRenderTarget_; - std::vector> renderTargets_; - bool initialized_ = false; -}; - -// ============================================================================ -// 便捷宏 -// ============================================================================ -#define E2D_RENDER_TARGET_STACK() ::extra2d::RenderTargetStack::get() -#define E2D_RENDER_TARGET_MGR() ::extra2d::RenderTargetMgr::get() - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/memory/gpu_context.h b/Extra2D/include/extra2d/graphics/memory/gpu_context.h deleted file mode 100644 index 759a56a..0000000 --- a/Extra2D/include/extra2d/graphics/memory/gpu_context.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include - -namespace extra2d { - -// ============================================================================ -// GPU 上下文状态管理器 -// 用于跟踪 OpenGL/Vulkan 等 GPU 上下文的生命周期状态 -// 确保在 GPU 资源析构时能安全地检查上下文是否有效 -// ============================================================================ - -class GPUContext { -public: - /// 获取单例实例 - static GPUContext& get(); - - /// 标记 GPU 上下文为有效(在初始化完成后调用) - void markValid(); - - /// 标记 GPU 上下文为无效(在销毁前调用) - void markInvalid(); - - /// 检查 GPU 上下文是否有效 - bool isValid() const; - -private: - GPUContext() = default; - ~GPUContext() = default; - GPUContext(const GPUContext&) = delete; - GPUContext& operator=(const GPUContext&) = delete; - - std::atomic valid_{false}; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/memory/vram_manager.h b/Extra2D/include/extra2d/graphics/memory/vram_manager.h deleted file mode 100644 index 9f5d74b..0000000 --- a/Extra2D/include/extra2d/graphics/memory/vram_manager.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// VRAM 管理器 - 跟踪显存使用情况 -// ============================================================================ -class VRAMMgr { -public: - static VRAMMgr& get(); - - // 纹理显存跟踪 - void allocTexture(size_t size); - void freeTexture(size_t size); - - // VBO/FBO 显存跟踪 - void allocBuffer(size_t size); - void freeBuffer(size_t size); - - // 查询显存使用情况 - size_t getUsedVRAM() const; - size_t getTextureVRAM() const; - size_t getBufferVRAM() const; - size_t getAvailableVRAM() const; - - // 显存预算管理 - void setVRAMBudget(size_t budget); - size_t getVRAMBudget() const; - bool isOverBudget() const; - - // 统计信息 - void printStats() const; - - // 重置计数器 - void reset(); - -private: - VRAMMgr(); - ~VRAMMgr() = default; - VRAMMgr(const VRAMMgr&) = delete; - VRAMMgr& operator=(const VRAMMgr&) = delete; - - mutable std::mutex mutex_; - - size_t textureVRAM_; - size_t bufferVRAM_; - size_t vramBudget_; - - // 统计 - uint32_t textureAllocCount_; - uint32_t textureFreeCount_; - uint32_t bufferAllocCount_; - uint32_t bufferFreeCount_; - size_t peakTextureVRAM_; - size_t peakBufferVRAM_; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/resources/buffer.h b/Extra2D/include/extra2d/graphics/resources/buffer.h deleted file mode 100644 index 8e3ab29..0000000 --- a/Extra2D/include/extra2d/graphics/resources/buffer.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 缓冲区类型枚举 -// ============================================================================ -enum class BufferType { - Vertex, // 顶点缓冲 - Index, // 索引缓冲 - Uniform // 统一缓冲 -}; - -// ============================================================================ -// 缓冲区使用模式枚举 -// ============================================================================ -enum class BufferUsage { - Static, // 静态数据,很少更新 - Dynamic, // 动态数据,频繁更新 - Stream // 流式数据,每帧更新 -}; - -// ============================================================================ -// 缓冲区描述结构 -// ============================================================================ -struct BufferDesc { - BufferType type = BufferType::Vertex; - BufferUsage usage = BufferUsage::Static; - size_t size = 0; // 缓冲区大小(字节) - const void* initialData = nullptr; // 初始数据 -}; - -// ============================================================================ -// 缓冲区抽象接口 - 渲染后端无关 -// ============================================================================ -class Buffer { -public: - virtual ~Buffer() = default; - - /** - * @brief 绑定缓冲区 - */ - virtual void bind() = 0; - - /** - * @brief 解绑缓冲区 - */ - virtual void unbind() = 0; - - /** - * @brief 设置缓冲区数据(完全替换) - * @param data 数据指针 - * @param size 数据大小(字节) - */ - virtual void setData(const void* data, size_t size) = 0; - - /** - * @brief 更新缓冲区部分数据 - * @param data 数据指针 - * @param offset 偏移量(字节) - * @param size 数据大小(字节) - */ - virtual void updateData(const void* data, size_t offset, size_t size) = 0; - - /** - * @brief 映射缓冲区到内存(用于直接写入) - * @return 映射后的内存指针,失败返回 nullptr - */ - virtual void* map() = 0; - - /** - * @brief 解除缓冲区映射 - */ - virtual void unmap() = 0; - - /** - * @brief 获取缓冲区大小 - * @return 缓冲区大小(字节) - */ - virtual size_t getSize() const = 0; - - /** - * @brief 获取缓冲区类型 - * @return 缓冲区类型 - */ - virtual BufferType getType() const = 0; - - /** - * @brief 获取缓冲区使用模式 - * @return 使用模式 - */ - virtual BufferUsage getUsage() const = 0; - - /** - * @brief 检查缓冲区是否有效 - * @return 有效返回 true - */ - virtual bool isValid() const = 0; - - /** - * @brief 获取原生句柄(后端特定) - * @return 原生句柄值 - */ - virtual uintptr_t getNativeHandle() const = 0; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/resources/font_atlas.h b/Extra2D/include/extra2d/graphics/resources/font_atlas.h deleted file mode 100644 index 008e2f2..0000000 --- a/Extra2D/include/extra2d/graphics/resources/font_atlas.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 字形信息结构 -// ============================================================================ -struct Glyph { - float width = 0; // 字形宽度 - float height = 0; // 字形高度 - float bearingX = 0; // 水平偏移 - float bearingY = 0; // 垂直偏移(从基线到字形顶部) - float advance = 0; // 水平步进 - float u0 = 0, v0 = 0; // 纹理坐标左下角 - float u1 = 0, v1 = 0; // 纹理坐标右上角 -}; - -// ============================================================================ -// 字体图集描述结构 -// ============================================================================ -struct FontAtlasDesc { - std::string filepath; // 字体文件路径 - int fontSize = 16; // 字体大小 - bool useSDF = false; // 是否使用SDF渲染 - int atlasSize = 512; // 图集大小 - int padding = 2; // 字形间距 -}; - -// ============================================================================ -// 字体图集抽象接口 - 渲染后端无关 -// ============================================================================ -class FontAtlas { -public: - virtual ~FontAtlas() = default; - - /** - * @brief 初始化字体图集 - * @param desc 字体图集描述 - * @return 成功返回 true - */ - virtual bool init(const FontAtlasDesc& desc) = 0; - - /** - * @brief 关闭字体图集,释放资源 - */ - virtual void shutdown() = 0; - - /** - * @brief 获取字形信息 - * @param codepoint Unicode 码点 - * @return 字形信息指针,未找到返回 nullptr - */ - virtual const Glyph* getGlyph(char32_t codepoint) const = 0; - - /** - * @brief 获取纹理 - * @return 纹理对象 - */ - virtual Ptr getTexture() const = 0; - - /** - * @brief 获取字体大小 - * @return 字体大小 - */ - virtual int getFontSize() const = 0; - - /** - * @brief 获取行高 - * @return 行高 - */ - virtual float getLineHeight() const = 0; - - /** - * @brief 获取基线到顶部的距离 - * @return 上升高度 - */ - virtual float getAscent() const = 0; - - /** - * @brief 获取基线到底部的距离 - * @return 下降高度 - */ - virtual float getDescent() const = 0; - - /** - * @brief 计算文本宽度 - * @param text 文本内容 - * @return 文本宽度 - */ - virtual float measureText(const std::string& text) const = 0; - - /** - * @brief 计算文本尺寸 - * @param text 文本内容 - * @return 文本尺寸 - */ - virtual Size measureTextSize(const std::string& text) const = 0; - - /** - * @brief 是否使用SDF渲染 - * @return 使用SDF返回 true - */ - virtual bool isSDF() const = 0; - - /** - * @brief 检查字体图集是否有效 - * @return 有效返回 true - */ - virtual bool isValid() const = 0; - - /** - * @brief 预加载字符到图集 - * @param text 需要预加载的文本 - * @return 成功加载的字符数 - */ - virtual int preloadGlyphs(const std::string& text) = 0; - - /** - * @brief 清空已加载的字形缓存 - */ - virtual void clearCache() = 0; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/resources/framebuffer.h b/Extra2D/include/extra2d/graphics/resources/framebuffer.h deleted file mode 100644 index 5f715e2..0000000 --- a/Extra2D/include/extra2d/graphics/resources/framebuffer.h +++ /dev/null @@ -1,140 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 帧缓冲描述结构 -// ============================================================================ -struct FramebufferDesc { - int width = 0; // 帧缓冲宽度 - int height = 0; // 帧缓冲高度 - int colorAttachments = 1; // 颜色附件数量 - bool hasDepth = false; // 是否有深度附件 - bool hasStencil = false; // 是否有模板附件 - bool multisample = false; // 是否多重采样 - int samples = 4; // 采样数(多重采样时有效) -}; - -// ============================================================================ -// 帧缓冲抽象接口 - 渲染后端无关 -// ============================================================================ -class Framebuffer { -public: - virtual ~Framebuffer() = default; - - /** - * @brief 绑定帧缓冲(作为渲染目标) - */ - virtual void bind() = 0; - - /** - * @brief 解绑帧缓冲(恢复到默认帧缓冲) - */ - virtual void unbind() = 0; - - /** - * @brief 附加颜色纹理 - * @param texture 纹理对象 - * @param attachment 附件索引(0-7) - */ - virtual void attachColorTexture(Ptr texture, int attachment = 0) = 0; - - /** - * @brief 附加深度纹理 - * @param texture 纹理对象 - */ - virtual void attachDepthTexture(Ptr texture) = 0; - - /** - * @brief 附加深度模板纹理 - * @param texture 纹理对象 - */ - virtual void attachDepthStencilTexture(Ptr texture) = 0; - - /** - * @brief 检查帧缓冲是否完整 - * @return 完整返回 true - */ - virtual bool isComplete() = 0; - - /** - * @brief 获取颜色附件纹理 - * @param attachment 附件索引 - * @return 纹理对象 - */ - virtual Ptr getColorTexture(int attachment = 0) const = 0; - - /** - * @brief 获取深度附件纹理 - * @return 纹理对象 - */ - virtual Ptr getDepthTexture() const = 0; - - /** - * @brief 获取帧缓冲宽度 - * @return 宽度 - */ - virtual int getWidth() const = 0; - - /** - * @brief 获取帧缓冲高度 - * @return 高度 - */ - virtual int getHeight() const = 0; - - /** - * @brief 获取尺寸 - * @return 尺寸 - */ - virtual Size getSize() const = 0; - - /** - * @brief 检查帧缓冲是否有效 - * @return 有效返回 true - */ - virtual bool isValid() const = 0; - - /** - * @brief 获取原生句柄(后端特定) - * @return 原生句柄值 - */ - virtual uintptr_t getNativeHandle() const = 0; - - /** - * @brief 清除帧缓冲 - * @param color 清除颜色 - * @param clearColor 是否清除颜色缓冲 - * @param clearDepth 是否清除深度缓冲 - * @param clearStencil 是否清除模板缓冲 - */ - virtual void clear(const Color& color, bool clearColor = true, - bool clearDepth = true, bool clearStencil = false) = 0; - - /** - * @brief 设置视口 - * @param x 视口左下角X坐标 - * @param y 视口左下角Y坐标 - * @param width 视口宽度 - * @param height 视口高度 - */ - virtual void setViewport(int x, int y, int width, int height) = 0; - - /** - * @brief 读取像素数据 - * @param x 起始X坐标 - * @param y 起始Y坐标 - * @param width 宽度 - * @param height 高度 - * @param outData 输出数据缓冲区 - * @return 成功返回 true - */ - virtual bool readPixels(int x, int y, int width, int height, - std::vector& outData) = 0; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/resources/pipeline.h b/Extra2D/include/extra2d/graphics/resources/pipeline.h deleted file mode 100644 index 996499c..0000000 --- a/Extra2D/include/extra2d/graphics/resources/pipeline.h +++ /dev/null @@ -1,162 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 混合模式枚举 -// ============================================================================ -enum class BlendMode { - None, // 不混合 - Alpha, // 标准 Alpha 混合 - Additive, // 加法混合 - Multiply // 乘法混合 -}; - -// ============================================================================ -// 深度测试函数枚举 -// ============================================================================ -enum class DepthFunc { - Never, // 永不通过 - Less, // 小于 - Equal, // 等于 - LessEqual, // 小于等于 - Greater, // 大于 - NotEqual, // 不等于 - GreaterEqual,// 大于等于 - Always // 总是通过 -}; - -// ============================================================================ -// 裁剪模式枚举 -// ============================================================================ -enum class CullMode { - None, // 不裁剪 - Front, // 裁剪正面 - Back, // 裁剪背面 - Both // 裁剪双面 -}; - -// ============================================================================ -// 顶点属性格式枚举 -// ============================================================================ -enum class VertexFormat { - Float1, // 1个float - Float2, // 2个float - Float3, // 3个float - Float4, // 4个float - Byte4, // 4个byte - UByte4, // 4个ubyte - Short2, // 2个short - Short4 // 4个short -}; - -// ============================================================================ -// 顶点属性描述 -// ============================================================================ -struct VertexAttribute { - uint32_t location = 0; // 属性位置 - VertexFormat format = VertexFormat::Float3; // 数据格式 - uint32_t offset = 0; // 在顶点结构中的偏移 - uint32_t stride = 0; // 顶点结构大小 - bool normalized = false; // 是否归一化 - - VertexAttribute() = default; - VertexAttribute(uint32_t loc, VertexFormat fmt, uint32_t off, uint32_t str, bool norm = false) - : location(loc), format(fmt), offset(off), stride(str), normalized(norm) {} -}; - -// ============================================================================ -// 管线描述结构 -// ============================================================================ -struct PipelineDesc { - // 混合状态 - BlendMode blendMode = BlendMode::Alpha; - bool blendEnabled = true; - - // 深度状态 - bool depthTest = false; - bool depthWrite = false; - DepthFunc depthFunc = DepthFunc::Less; - - // 裁剪状态 - CullMode cullMode = CullMode::None; - - // 顶点布局 - std::vector vertexAttributes; - - // 着色器(由后端特定实现设置) - void* vertexShader = nullptr; - void* fragmentShader = nullptr; -}; - -// ============================================================================ -// 渲染管线抽象接口 - 渲染后端无关 -// ============================================================================ -class Pipeline { -public: - virtual ~Pipeline() = default; - - /** - * @brief 绑定管线 - */ - virtual void bind() = 0; - - /** - * @brief 解绑管线 - */ - virtual void unbind() = 0; - - /** - * @brief 设置混合模式 - * @param mode 混合模式 - */ - virtual void setBlendMode(BlendMode mode) = 0; - - /** - * @brief 获取当前混合模式 - * @return 混合模式 - */ - virtual BlendMode getBlendMode() const = 0; - - /** - * @brief 设置深度测试 - * @param enabled 是否启用 - */ - virtual void setDepthTest(bool enabled) = 0; - - /** - * @brief 设置深度写入 - * @param enabled 是否启用 - */ - virtual void setDepthWrite(bool enabled) = 0; - - /** - * @brief 设置深度测试函数 - * @param func 深度测试函数 - */ - virtual void setDepthFunc(DepthFunc func) = 0; - - /** - * @brief 设置裁剪模式 - * @param mode 裁剪模式 - */ - virtual void setCullMode(CullMode mode) = 0; - - /** - * @brief 检查管线是否有效 - * @return 有效返回 true - */ - virtual bool isValid() const = 0; - - /** - * @brief 获取原生句柄(后端特定) - * @return 原生句柄值 - */ - virtual uintptr_t getNativeHandle() const = 0; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/resources/shader.h b/Extra2D/include/extra2d/graphics/resources/shader.h deleted file mode 100644 index 64f38a8..0000000 --- a/Extra2D/include/extra2d/graphics/resources/shader.h +++ /dev/null @@ -1,134 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 着色器类型枚举 -// ============================================================================ -enum class ShaderType { - Vertex, // 顶点着色器 - Fragment, // 片段着色器 - Geometry, // 几何着色器 - Compute // 计算着色器 -}; - -// ============================================================================ -// 着色器描述结构 -// ============================================================================ -struct ShaderDesc { - std::string name; // 着色器名称 - std::string vertexSource; // 顶点着色器源码 - std::string fragmentSource; // 片段着色器源码 - std::string geometrySource; // 几何着色器源码(可选) - std::vector binaryData; // 预编译二进制数据(可选) -}; - -// ============================================================================ -// 着色器抽象接口 - 渲染后端无关 -// ============================================================================ -class Shader { -public: - virtual ~Shader() = default; - - /** - * @brief 绑定着色器程序 - */ - virtual void bind() = 0; - - /** - * @brief 解绑着色器程序 - */ - virtual void unbind() = 0; - - /** - * @brief 设置布尔类型 uniform 变量 - * @param name uniform 变量名 - * @param value 布尔值 - */ - virtual void setBool(const std::string& name, bool value) = 0; - - /** - * @brief 设置整数类型 uniform 变量 - * @param name uniform 变量名 - * @param value 整数值 - */ - virtual void setInt(const std::string& name, int value) = 0; - - /** - * @brief 设置浮点类型 uniform 变量 - * @param name uniform 变量名 - * @param value 浮点值 - */ - virtual void setFloat(const std::string& name, float value) = 0; - - /** - * @brief 设置二维向量类型 uniform 变量 - * @param name uniform 变量名 - * @param value 二维向量值 - */ - virtual void setVec2(const std::string& name, const glm::vec2& value) = 0; - - /** - * @brief 设置三维向量类型 uniform 变量 - * @param name uniform 变量名 - * @param value 三维向量值 - */ - virtual void setVec3(const std::string& name, const glm::vec3& value) = 0; - - /** - * @brief 设置四维向量类型 uniform 变量 - * @param name uniform 变量名 - * @param value 四维向量值 - */ - virtual void setVec4(const std::string& name, const glm::vec4& value) = 0; - - /** - * @brief 设置 4x4 矩阵类型 uniform 变量 - * @param name uniform 变量名 - * @param value 4x4 矩阵值 - */ - virtual void setMat4(const std::string& name, const glm::mat4& value) = 0; - - /** - * @brief 设置颜色类型 uniform 变量 - * @param name uniform 变量名 - * @param color 颜色值 - */ - virtual void setColor(const std::string& name, const Color& color) = 0; - - /** - * @brief 设置纹理采样器 - * @param name uniform 变量名 - * @param slot 纹理槽位 - */ - virtual void setTexture(const std::string& name, int slot) = 0; - - /** - * @brief 获取着色器名称 - * @return 着色器名称 - */ - virtual const std::string& getName() const = 0; - - /** - * @brief 检查着色器是否有效 - * @return 有效返回 true - */ - virtual bool isValid() const = 0; - - /** - * @brief 获取原生句柄(后端特定) - * @return 原生句柄值 - */ - virtual uintptr_t getNativeHandle() const = 0; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader/shader_cache.h b/Extra2D/include/extra2d/graphics/shader/shader_cache.h deleted file mode 100644 index 1ab93f5..0000000 --- a/Extra2D/include/extra2d/graphics/shader/shader_cache.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// Shader缓存条目 -// ============================================================================ -struct ShaderCacheEntry { - std::string name; - std::string sourceHash; - uint64_t compileTime = 0; - std::vector binary; - std::vector dependencies; -}; - -// ============================================================================ -// Shader缓存管理器 -// ============================================================================ -class ShaderCache { -public: - /** - * @brief 获取单例实例 - * @return 缓存管理器实例引用 - */ - static ShaderCache& getInstance(); - - /** - * @brief 初始化缓存系统 - * @param cacheDir 缓存目录路径 - * @return 初始化成功返回true,失败返回false - */ - bool init(const std::string& cacheDir); - - /** - * @brief 关闭缓存系统 - */ - void shutdown(); - - /** - * @brief 检查缓存是否有效 - * @param name Shader名称 - * @param sourceHash 源码哈希值 - * @return 缓存有效返回true,否则返回false - */ - bool hasValidCache(const std::string& name, const std::string& sourceHash); - - /** - * @brief 加载缓存的二进制数据 - * @param name Shader名称 - * @return 缓存条目指针,不存在返回nullptr - */ - Ptr loadCache(const std::string& name); - - /** - * @brief 保存编译结果到缓存 - * @param entry 缓存条目 - * @return 保存成功返回true,失败返回false - */ - bool saveCache(const ShaderCacheEntry& entry); - - /** - * @brief 使缓存失效 - * @param name Shader名称 - */ - void invalidate(const std::string& name); - - /** - * @brief 清除所有缓存 - */ - void clearAll(); - - /** - * @brief 计算源码哈希值 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 哈希值字符串 - */ - static std::string computeHash(const std::string& vertSource, - const std::string& fragSource); - - /** - * @brief 检查是否已初始化 - * @return 已初始化返回true,否则返回false - */ - bool isInitialized() const { return initialized_; } - -private: - ShaderCache() = default; - ~ShaderCache() = default; - ShaderCache(const ShaderCache&) = delete; - ShaderCache& operator=(const ShaderCache&) = delete; - - std::string cacheDir_; - std::unordered_map cacheMap_; - bool initialized_ = false; - - /** - * @brief 加载缓存索引 - * @return 加载成功返回true,失败返回false - */ - bool loadCacheIndex(); - - /** - * @brief 保存缓存索引 - * @return 保存成功返回true,失败返回false - */ - bool saveCacheIndex(); - - /** - * @brief 获取缓存文件路径 - * @param name Shader名称 - * @return 缓存文件完整路径 - */ - std::string getCachePath(const std::string& name) const; - - /** - * @brief 确保缓存目录存在 - * @return 目录存在或创建成功返回true,否则返回false - */ - bool ensureCacheDirectory(); -}; - -// 便捷宏 -#define E2D_SHADER_CACHE() ::extra2d::ShaderCache::getInstance() - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader/shader_hot_reloader.h b/Extra2D/include/extra2d/graphics/shader/shader_hot_reloader.h deleted file mode 100644 index bd21d74..0000000 --- a/Extra2D/include/extra2d/graphics/shader/shader_hot_reloader.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#endif - -namespace extra2d { - -// ============================================================================ -// 文件变化事件 -// ============================================================================ -struct FileChangeEvent { - std::string filepath; - - enum class Type { Created, Modified, Deleted, Renamed } type; - - uint64_t timestamp = 0; -}; - -// ============================================================================ -// 文件变化回调 -// ============================================================================ -using FileChangeCallback = std::function; - -// ============================================================================ -// Shader热重载管理器 -// ============================================================================ -class ShaderHotReloader { -public: - /** - * @brief 获取单例实例 - * @return 热重载管理器实例引用 - */ - static ShaderHotReloader &getInstance(); - - /** - * @brief 初始化热重载系统 - * @return 初始化成功返回true,失败返回false - */ - bool init(); - - /** - * @brief 关闭热重载系统 - */ - void shutdown(); - - /** - * @brief 注册Shader文件监视 - * @param shaderName Shader名称 - * @param filePaths 要监视的文件列表 - * @param callback 文件变化时的回调 - */ - void watch(const std::string &shaderName, - const std::vector &filePaths, - FileChangeCallback callback); - - /** - * @brief 取消监视 - * @param shaderName Shader名称 - */ - void unwatch(const std::string &shaderName); - - /** - * @brief 更新文件监视(在主循环中调用) - */ - void update(); - - /** - * @brief 启用/禁用热重载 - * @param enabled 是否启用 - */ - void setEnabled(bool enabled); - - /** - * @brief 检查是否启用 - * @return 启用返回true,否则返回false - */ - bool isEnabled() const { return enabled_; } - - /** - * @brief 检查是否已初始化 - * @return 已初始化返回true,否则返回false - */ - bool isInitialized() const { return initialized_; } - -private: - ShaderHotReloader() = default; - ~ShaderHotReloader() = default; - ShaderHotReloader(const ShaderHotReloader &) = delete; - ShaderHotReloader &operator=(const ShaderHotReloader &) = delete; - - bool enabled_ = false; - bool initialized_ = false; - - struct WatchInfo { - std::vector filePaths; - FileChangeCallback callback; - std::unordered_map modifiedTimes; - }; - std::unordered_map watchMap_; - -#ifdef _WIN32 - HANDLE watchHandle_ = nullptr; - std::vector buffer_; - std::string watchDir_; - bool watching_ = false; -#endif - - /** - * @brief 轮询检查文件变化 - */ - void pollChanges(); - - /** - * @brief 获取文件修改时间 - * @param filepath 文件路径 - * @return 修改时间戳 - */ - static uint64_t getFileModifiedTime(const std::string &filepath); -}; - -// 便捷宏 -#define E2D_SHADER_HOT_RELOADER() ::extra2d::ShaderHotReloader::getInstance() - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader/shader_interface.h b/Extra2D/include/extra2d/graphics/shader/shader_interface.h deleted file mode 100644 index e343d1d..0000000 --- a/Extra2D/include/extra2d/graphics/shader/shader_interface.h +++ /dev/null @@ -1,152 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -class Color; - -// ============================================================================ -// Shader抽象接口 - 渲染后端无关 -// ============================================================================ -class IShader { -public: - virtual ~IShader() = default; - - /** - * @brief 绑定Shader程序 - */ - virtual void bind() const = 0; - - /** - * @brief 解绑Shader程序 - */ - virtual void unbind() const = 0; - - /** - * @brief 设置布尔类型uniform变量 - * @param name uniform变量名 - * @param value 布尔值 - */ - virtual void setBool(const std::string& name, bool value) = 0; - - /** - * @brief 设置整数类型uniform变量 - * @param name uniform变量名 - * @param value 整数值 - */ - virtual void setInt(const std::string& name, int value) = 0; - - /** - * @brief 设置浮点类型uniform变量 - * @param name uniform变量名 - * @param value 浮点值 - */ - virtual void setFloat(const std::string& name, float value) = 0; - - /** - * @brief 设置二维向量类型uniform变量 - * @param name uniform变量名 - * @param value 二维向量值 - */ - virtual void setVec2(const std::string& name, const glm::vec2& value) = 0; - - /** - * @brief 设置三维向量类型uniform变量 - * @param name uniform变量名 - * @param value 三维向量值 - */ - virtual void setVec3(const std::string& name, const glm::vec3& value) = 0; - - /** - * @brief 设置四维向量类型uniform变量 - * @param name uniform变量名 - * @param value 四维向量值 - */ - virtual void setVec4(const std::string& name, const glm::vec4& value) = 0; - - /** - * @brief 设置4x4矩阵类型uniform变量 - * @param name uniform变量名 - * @param value 4x4矩阵值 - */ - virtual void setMat4(const std::string& name, const glm::mat4& value) = 0; - - /** - * @brief 设置颜色类型uniform变量 - * @param name uniform变量名 - * @param color 颜色值 - */ - virtual void setColor(const std::string& name, const Color& color) = 0; - - /** - * @brief 检查Shader是否有效 - * @return 有效返回true,否则返回false - */ - virtual bool isValid() const = 0; - - /** - * @brief 获取原生句柄(如OpenGL程序ID) - * @return 原生句柄值 - */ - virtual uint32_t getNativeHandle() const = 0; - - /** - * @brief 获取Shader名称 - * @return Shader名称 - */ - virtual const std::string& getName() const = 0; - - /** - * @brief 设置Shader名称 - * @param name Shader名称 - */ - virtual void setName(const std::string& name) = 0; -}; - -// ============================================================================ -// Shader工厂接口 - 用于创建渲染后端特定的Shader实例 -// ============================================================================ -class IShaderFactory { -public: - virtual ~IShaderFactory() = default; - - /** - * @brief 从源码创建Shader - * @param name Shader名称 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 创建的Shader实例 - */ - virtual Ptr createFromSource( - const std::string& name, - const std::string& vertSource, - const std::string& fragSource) = 0; - - /** - * @brief 从缓存二进制创建Shader - * @param name Shader名称 - * @param binary 编译后的二进制数据 - * @return 创建的Shader实例 - */ - virtual Ptr createFromBinary( - const std::string& name, - const std::vector& binary) = 0; - - /** - * @brief 获取Shader的二进制数据(用于缓存) - * @param shader Shader实例 - * @param outBinary 输出的二进制数据 - * @return 成功返回true,失败返回false - */ - virtual bool getShaderBinary(const IShader& shader, - std::vector& outBinary) = 0; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader/shader_loader.h b/Extra2D/include/extra2d/graphics/shader/shader_loader.h deleted file mode 100644 index 4e7e826..0000000 --- a/Extra2D/include/extra2d/graphics/shader/shader_loader.h +++ /dev/null @@ -1,294 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// Shader加载结果 -// ============================================================================ -struct ShaderLoadResult { - bool success = false; - std::string errorMessage; - std::string vertSource; - std::string fragSource; - std::vector dependencies; -}; - -// ============================================================================ -// Shader Uniform 定义 -// ============================================================================ -struct ShaderUniformDef { - std::string type; - std::string description; - float defaultValue = 0.0f; // 默认值(用于float类型) - float defaultVec2[2] = {0, 0}; // 默认值(用于vec2类型) - float defaultVec3[3] = {0, 0, 0}; // 默认值(用于vec3类型) - float defaultVec4[4] = {0, 0, 0, 0}; // 默认值(用于vec4类型) - float defaultMat4[16] = { - 1, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 1}; // 默认值(用于mat4类型,默认单位矩阵) - int defaultInt = 0; // 默认值(用于int类型) - bool defaultBool = false; // 默认值(用于bool类型) - bool hasDefault = false; // 是否有默认值 -}; - -// ============================================================================ -// Shader Uniform 值类型 -// ============================================================================ -struct ShaderUniformValue { - enum class Type { Float, Int, Bool, Vec2, Vec3, Vec4, Mat4 } type; - - union { - float f[16]; // 足够存储 mat4 - int i; - bool b; - } data; - - // 构造函数 - ShaderUniformValue() : type(Type::Float) { data.f[0] = 0; } - ShaderUniformValue(float v) : type(Type::Float) { data.f[0] = v; } - ShaderUniformValue(int v) : type(Type::Int) { data.i = v; } - ShaderUniformValue(bool v) : type(Type::Bool) { data.b = v; } - ShaderUniformValue(const glm::vec2 &v) : type(Type::Vec2) { - data.f[0] = v.x; - data.f[1] = v.y; - } - ShaderUniformValue(const glm::vec3 &v) : type(Type::Vec3) { - data.f[0] = v.x; - data.f[1] = v.y; - data.f[2] = v.z; - } - ShaderUniformValue(const glm::vec4 &v) : type(Type::Vec4) { - data.f[0] = v.x; - data.f[1] = v.y; - data.f[2] = v.z; - data.f[3] = v.w; - } - ShaderUniformValue(const glm::mat4 &m) : type(Type::Mat4) { - const float *ptr = glm::value_ptr(m); - for (int i = 0; i < 16; ++i) - data.f[i] = ptr[i]; - } -}; - -// Uniform 值映射表 -using UniformValueMap = std::unordered_map; - -// ============================================================================ -// Shader Sampler 定义 -// ============================================================================ -struct ShaderSamplerDef { - std::string type; - std::string description; -}; - -// ============================================================================ -// Shader元数据 -// ============================================================================ -struct ShaderMetadata { - std::string name; - std::string category; - std::string version; - std::string description; - std::string vertPath; - std::string fragPath; - std::string combinedPath; - uint64_t lastModified = 0; - std::vector defines; - std::unordered_map uniforms; - std::unordered_map uniformDefs; - std::unordered_map samplerDefs; -}; - -// ============================================================================ -// ShaderLoader接口 - 支持多种文件格式加载 -// ============================================================================ -class IShaderLoader { -public: - virtual ~IShaderLoader() = default; - - /** - * @brief 从分离文件加载Shader (.vert + .frag) - * @param name Shader名称 - * @param vertPath 顶点着色器文件路径 - * @param fragPath 片段着色器文件路径 - * @return 加载结果 - */ - virtual ShaderLoadResult - loadFromSeparateFiles(const std::string &name, const std::string &vertPath, - const std::string &fragPath) = 0; - - /** - * @brief 从组合文件加载Shader (.shader) - * @param path 组合Shader文件路径 - * @return 加载结果 - */ - virtual ShaderLoadResult loadFromCombinedFile(const std::string &path) = 0; - - /** - * @brief 从源码字符串加载Shader - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 加载结果 - */ - virtual ShaderLoadResult loadFromSource(const std::string &vertSource, - const std::string &fragSource) = 0; - - /** - * @brief 处理Shader源码中的#include指令 - * @param source 原始源码 - * @param baseDir 基础目录 - * @param outDependencies 输出依赖列表 - * @return 处理后的源码 - */ - virtual std::string - processIncludes(const std::string &source, const std::string &baseDir, - std::vector &outDependencies) = 0; - - /** - * @brief 应用预处理器定义 - * @param source 原始源码 - * @param defines 预处理器定义列表 - * @return 处理后的源码 - */ - virtual std::string applyDefines(const std::string &source, - const std::vector &defines) = 0; - - /** - * @brief 获取Shader元数据 - * @param path Shader文件路径 - * @return 元数据 - */ - virtual ShaderMetadata getMetadata(const std::string &path) = 0; -}; - -// ============================================================================ -// 默认ShaderLoader实现 -// ============================================================================ -class ShaderLoader : public IShaderLoader { -public: - ShaderLoader(); - ~ShaderLoader() override = default; - - /** - * @brief 从分离文件加载Shader (.vert + .frag) - * @param name Shader名称 - * @param vertPath 顶点着色器文件路径 - * @param fragPath 片段着色器文件路径 - * @return 加载结果 - */ - ShaderLoadResult loadFromSeparateFiles(const std::string &name, - const std::string &vertPath, - const std::string &fragPath) override; - - /** - * @brief 从组合文件加载Shader (.shader) - * @param path 组合Shader文件路径 - * @return 加载结果 - */ - ShaderLoadResult loadFromCombinedFile(const std::string &path) override; - - /** - * @brief 从源码字符串加载Shader - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 加载结果 - */ - ShaderLoadResult loadFromSource(const std::string &vertSource, - const std::string &fragSource) override; - - /** - * @brief 处理Shader源码中的#include指令 - * @param source 原始源码 - * @param baseDir 基础目录 - * @param outDependencies 输出依赖列表 - * @return 处理后的源码 - */ - std::string - processIncludes(const std::string &source, const std::string &baseDir, - std::vector &outDependencies) override; - - /** - * @brief 应用预处理器定义 - * @param source 原始源码 - * @param defines 预处理器定义列表 - * @return 处理后的源码 - */ - std::string applyDefines(const std::string &source, - const std::vector &defines) override; - - /** - * @brief 获取Shader元数据 - * @param path Shader文件路径 - * @return 元数据 - */ - ShaderMetadata getMetadata(const std::string &path) override; - - /** - * @brief 添加include搜索路径 - * @param path 搜索路径 - */ - void addIncludePath(const std::string &path); - - /** - * @brief 读取文件内容 - * @param filepath 文件路径 - * @return 文件内容字符串 - */ - static std::string readFile(const std::string &filepath); - - /** - * @brief 获取文件修改时间 - * @param filepath 文件路径 - * @return 修改时间戳 - */ - static uint64_t getFileModifiedTime(const std::string &filepath); - - /** - * @brief 检查文件是否存在 - * @param filepath 文件路径 - * @return 存在返回true,否则返回false - */ - static bool fileExists(const std::string &filepath); - -private: - std::vector includePaths_; - std::unordered_map includeCache_; - - /** - * @brief 解析组合Shader文件 - * @param content 文件内容 - * @param outVert 输出顶点着色器源码 - * @param outFrag 输出片段着色器源码 - * @param outMetadata 输出元数据 - * @return 解析成功返回true,失败返回false - */ - bool parseCombinedFile(const std::string &content, std::string &outVert, - std::string &outFrag, ShaderMetadata &outMetadata); - - /** - * @brief 解析元数据JSON块 - * @param jsonContent JSON内容 - * @param outMetadata 输出元数据 - * @return 解析成功返回true,失败返回false - */ - bool parseMetadata(const std::string &jsonContent, - ShaderMetadata &outMetadata); - - /** - * @brief 查找include文件路径 - * @param includeName include文件名 - * @param baseDir 基础目录 - * @return 找到的完整路径,未找到返回空字符串 - */ - std::string findIncludeFile(const std::string &includeName, - const std::string &baseDir); -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader/shader_manager.h b/Extra2D/include/extra2d/graphics/shader/shader_manager.h deleted file mode 100644 index d5dadc2..0000000 --- a/Extra2D/include/extra2d/graphics/shader/shader_manager.h +++ /dev/null @@ -1,291 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// Shader重载回调 -// ============================================================================ -using ShaderReloadCallback = std::function newShader)>; - -// ============================================================================ -// Shader管理器 - 统一入口 -// ============================================================================ -class ShaderManager { -public: - /** - * @brief 获取单例实例 - * @return Shader管理器实例引用 - */ - static ShaderManager& getInstance(); - - // ------------------------------------------------------------------------ - // 初始化和关闭 - // ------------------------------------------------------------------------ - - /** - * @brief 使用平台默认路径初始化Shader系统 - * 自动检测平台并使用正确的路径(romfs/sdmc/相对路径) - * @param factory 渲染后端Shader工厂 - * @param appName 应用名称(用于缓存目录) - * @return 初始化成功返回true,失败返回false - */ - bool init(Ptr factory, const std::string& appName = "extra2d"); - - /** - * @brief 初始化Shader系统 - * @param shaderDir Shader文件目录 - * @param cacheDir 缓存目录 - * @param factory 渲染后端Shader工厂 - * @return 初始化成功返回true,失败返回false - */ - bool init(const std::string& shaderDir, - const std::string& cacheDir, - Ptr factory); - - /** - * @brief 关闭Shader系统 - */ - void shutdown(); - - /** - * @brief 检查是否已初始化 - * @return 已初始化返回true,否则返回false - */ - bool isInitialized() const { return initialized_; } - - /** - * @brief 检查当前平台是否支持热重载 - * Switch平台使用romfs,不支持热重载 - * @return 支持热重载返回true - */ - bool isHotReloadSupported() const { return hotReloadSupported_; } - - // ------------------------------------------------------------------------ - // Shader加载 - // ------------------------------------------------------------------------ - - /** - * @brief 从分离文件加载Shader - * @param name Shader名称 - * @param vertPath 顶点着色器文件路径 - * @param fragPath 片段着色器文件路径 - * @return 加载的Shader实例 - */ - Ptr loadFromFiles(const std::string& name, - const std::string& vertPath, - const std::string& fragPath); - - /** - * @brief 从组合文件加载Shader - * @param path 组合Shader文件路径 - * @return 加载的Shader实例 - */ - Ptr loadFromCombinedFile(const std::string& path); - - /** - * @brief 从源码加载Shader - * @param name Shader名称 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 加载的Shader实例 - */ - Ptr loadFromSource(const std::string& name, - const std::string& vertSource, - const std::string& fragSource); - - /** - * @brief 获取已加载的Shader - * @param name Shader名称 - * @return Shader实例,不存在返回nullptr - */ - Ptr get(const std::string& name) const; - - /** - * @brief 检查Shader是否存在 - * @param name Shader名称 - * @return 存在返回true,否则返回false - */ - bool has(const std::string& name) const; - - /** - * @brief 移除Shader - * @param name Shader名称 - */ - void remove(const std::string& name); - - /** - * @brief 清除所有Shader - */ - void clear(); - - // ------------------------------------------------------------------------ - // 热重载 - // ------------------------------------------------------------------------ - - /** - * @brief 注册重载回调 - * @param name Shader名称 - * @param callback 重载回调函数 - */ - void setReloadCallback(const std::string& name, ShaderReloadCallback callback); - - /** - * @brief 启用/禁用热重载 - * @param enabled 是否启用 - */ - void setHotReloadEnabled(bool enabled); - - /** - * @brief 检查热重载是否启用 - * @return 启用返回true,否则返回false - */ - bool isHotReloadEnabled() const; - - /** - * @brief 更新热重载系统(主循环调用) - */ - void update(); - - /** - * @brief 手动重载Shader - * @param name Shader名称 - * @return 重载成功返回true,失败返回false - */ - bool reload(const std::string& name); - - // ------------------------------------------------------------------------ - // 内置Shader - // ------------------------------------------------------------------------ - - /** - * @brief 获取内置Shader - * @param name 内置Shader名称 - * @return Shader实例 - */ - Ptr getBuiltin(const std::string& name); - - /** - * @brief 加载所有内置Shader - * @return 加载成功返回true,失败返回false - */ - bool loadBuiltinShaders(); - - /** - * @brief 从JSON元数据文件加载Shader(多后端支持) - * @param jsonPath JSON元数据文件路径 - * @param name Shader名称 - * @return 加载的Shader实例 - */ - Ptr loadFromMetadata(const std::string& jsonPath, const std::string& name); - - // ------------------------------------------------------------------------ - // 工具方法 - // ------------------------------------------------------------------------ - - /** - * @brief 获取Shader目录 - * @return Shader目录路径 - */ - const std::string& getShaderDir() const { return shaderDir_; } - - /** - * @brief 获取ShaderLoader - * @return ShaderLoader引用 - */ - ShaderLoader& getLoader() { return loader_; } - - /** - * @brief 获取Shader元数据 - * @param name Shader名称 - * @return Shader元数据,不存在返回空元数据 - */ - ShaderMetadata getMetadata(const std::string& name) const; - - /** - * @brief 获取Shader的uniform定义 - * @param name Shader名称 - * @return uniform定义映射,不存在返回空映射 - */ - std::unordered_map getUniformDefs(const std::string& name) const; - - /** - * @brief 获取Shader的sampler定义 - * @param name Shader名称 - * @return sampler定义映射,不存在返回空映射 - */ - std::unordered_map getSamplerDefs(const std::string& name) const; - - /** - * @brief 自动应用uniform值到着色器 - * 根据JSON元数据中的uniform定义,自动设置对应的uniform值 - * @param shader 目标着色器 - * @param shaderName Shader名称(用于查找元数据) - * @param values uniform值映射表 - */ - void applyUniforms(Ptr shader, const std::string& shaderName, const UniformValueMap& values); - - /** - * @brief 自动应用sampler绑定到着色器 - * 根据JSON元数据中的sampler定义,自动设置对应的纹理单元 - * @param shader 目标着色器 - * @param shaderName Shader名称(用于查找元数据) - */ - void applySamplers(Ptr shader, const std::string& shaderName); - -private: - ShaderManager() = default; - ~ShaderManager() = default; - ShaderManager(const ShaderManager&) = delete; - ShaderManager& operator=(const ShaderManager&) = delete; - - std::string shaderDir_; - std::string cacheDir_; - Ptr factory_; - ShaderLoader loader_; - - struct ShaderInfo { - Ptr shader; - ShaderMetadata metadata; - ShaderReloadCallback reloadCallback; - std::string vertSource; - std::string fragSource; - std::vector filePaths; - }; - std::unordered_map shaders_; - - bool initialized_ = false; - bool hotReloadEnabled_ = false; - bool hotReloadSupported_ = true; - - /** - * @brief 从缓存加载Shader - * @param name Shader名称 - * @param sourceHash 源码哈希值 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return Shader实例 - */ - Ptr loadFromCache(const std::string& name, - const std::string& sourceHash, - const std::string& vertSource, - const std::string& fragSource); - - /** - * @brief 处理文件变化事件 - * @param shaderName Shader名称 - * @param event 文件变化事件 - */ - void handleFileChange(const std::string& shaderName, const FileChangeEvent& event); -}; - -// 便捷宏 -#define E2D_SHADER_MANAGER() ::extra2d::ShaderManager::getInstance() - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader/shader_preset.h b/Extra2D/include/extra2d/graphics/shader/shader_preset.h deleted file mode 100644 index 5014861..0000000 --- a/Extra2D/include/extra2d/graphics/shader/shader_preset.h +++ /dev/null @@ -1,112 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace extra2d { - -struct WaterParams { - float waveSpeed = 1.0f; - float waveAmplitude = 0.02f; - float waveFrequency = 4.0f; -}; - -struct OutlineParams { - Color color = Colors::Black; - float thickness = 2.0f; -}; - -struct DistortionParams { - float distortionAmount = 0.02f; - float timeScale = 1.0f; -}; - -struct PixelateParams { - float pixelSize = 8.0f; -}; - -struct InvertParams { - float strength = 1.0f; -}; - -struct GrayscaleParams { - float intensity = 1.0f; -}; - -struct BlurParams { - float radius = 5.0f; -}; - -class ShaderPreset { -public: - /** - * @brief 创建水波纹效果着色器 - * @param params 水波纹效果参数 - * @return 配置好的着色器 - */ - static Ptr Water(const WaterParams& params = {}); - - /** - * @brief 创建描边效果着色器 - * @param params 描边效果参数 - * @return 配置好的着色器 - */ - static Ptr Outline(const OutlineParams& params = {}); - - /** - * @brief 创建扭曲效果着色器 - * @param params 扭曲效果参数 - * @return 配置好的着色器 - */ - static Ptr Distortion(const DistortionParams& params = {}); - - /** - * @brief 创建像素化效果着色器 - * @param params 像素化效果参数 - * @return 配置好的着色器 - */ - static Ptr Pixelate(const PixelateParams& params = {}); - - /** - * @brief 创建反相效果着色器 - * @param params 反相效果参数 - * @return 配置好的着色器 - */ - static Ptr Invert(const InvertParams& params = {}); - - /** - * @brief 创建灰度效果着色器 - * @param params 灰度效果参数 - * @return 配置好的着色器 - */ - static Ptr Grayscale(const GrayscaleParams& params = {}); - - /** - * @brief 创建模糊效果着色器 - * @param params 模糊效果参数 - * @return 配置好的着色器 - */ - static Ptr Blur(const BlurParams& params = {}); - - /** - * @brief 创建灰度+描边组合效果着色器 - * @param grayParams 灰度效果参数 - * @param outlineParams 描边效果参数 - * @return 配置好的着色器 - */ - static Ptr GrayscaleOutline(const GrayscaleParams& grayParams, - const OutlineParams& outlineParams); - - /** - * @brief 创建像素化+反相组合效果着色器 - * @param pixParams 像素化效果参数 - * @param invParams 反相效果参数 - * @return 配置好的着色器 - */ - static Ptr PixelateInvert(const PixelateParams& pixParams, - const InvertParams& invParams); -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/texture/alpha_mask.h b/Extra2D/include/extra2d/graphics/texture/alpha_mask.h deleted file mode 100644 index 98cdd6b..0000000 --- a/Extra2D/include/extra2d/graphics/texture/alpha_mask.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// Alpha 遮罩 - 存储图片的非透明区域信息 -// ============================================================================ -class AlphaMask { -public: - AlphaMask() = default; - AlphaMask(int width, int height); - - /// 从像素数据创建遮罩 - static AlphaMask createFromPixels(const uint8_t *pixels, int width, - int height, int channels); - - /// 获取指定位置的透明度(0-255) - uint8_t getAlpha(int x, int y) const; - - /// 检查指定位置是否不透明 - bool isOpaque(int x, int y, uint8_t threshold = 128) const; - - /// 检查指定位置是否在遮罩范围内 - bool isValid(int x, int y) const; - - /// 获取遮罩尺寸 - int getWidth() const { return width_; } - int getHeight() const { return height_; } - Size getSize() const { - return Size(static_cast(width_), static_cast(height_)); - } - - /// 获取原始数据 - const std::vector &getData() const { return data_; } - - /// 检查遮罩是否有效 - bool isValid() const { return !data_.empty() && width_ > 0 && height_ > 0; } - -private: - int width_ = 0; - int height_ = 0; - std::vector data_; // Alpha值数组 -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/texture/font.h b/Extra2D/include/extra2d/graphics/texture/font.h deleted file mode 100644 index c3f06d8..0000000 --- a/Extra2D/include/extra2d/graphics/texture/font.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 字形信息 -// ============================================================================ -struct Glyph { - float u0, v0; // 纹理坐标左下角 - float u1, v1; // 纹理坐标右上角 - float width; // 字形宽度(像素) - float height; // 字形高度(像素) - float bearingX; // 水平偏移 - float bearingY; // 垂直偏移 - float advance; // 前进距离 -}; - -// ============================================================================ -// 字体图集接口 -// ============================================================================ -class FontAtlas { -public: - virtual ~FontAtlas() = default; - - // 获取字形信息 - virtual const Glyph *getGlyph(char32_t codepoint) const = 0; - - // 获取纹理 - virtual class Texture *getTexture() const = 0; - - // 获取字体大小 - virtual int getFontSize() const = 0; - - virtual float getAscent() const = 0; - virtual float getDescent() const = 0; - virtual float getLineGap() const = 0; - virtual float getLineHeight() const = 0; - - // 计算文字尺寸 - virtual Vec2 measureText(const std::string &text) = 0; - - // 是否支持 SDF 渲染 - virtual bool isSDF() const = 0; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/texture/texture.h b/Extra2D/include/extra2d/graphics/texture/texture.h deleted file mode 100644 index 712915e..0000000 --- a/Extra2D/include/extra2d/graphics/texture/texture.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include -#include - -namespace extra2d { - -// ============================================================================ -// 像素格式枚举 -// ============================================================================ -enum class PixelFormat { - R8, // 单通道灰度 - RG8, // 双通道 - RGB8, // RGB 24位 - RGBA8, // RGBA 32位(默认) - RGB16F, // RGB 半精度浮点 - RGBA16F, // RGBA 半精度浮点 - RGB32F, // RGB 全精度浮点 - RGBA32F, // RGBA 全精度浮点 - Depth16, // 16位深度 - Depth24, // 24位深度 - Depth32F, // 32位浮点深度 - Depth24Stencil8, // 24位深度 + 8位模板 - - // 压缩纹理格式 - ETC2_RGB8, // ETC2 RGB 压缩 - ETC2_RGBA8, // ETC2 RGBA 压缩 - ASTC_4x4, // ASTC 4x4 压缩 - ASTC_6x6, // ASTC 6x6 压缩 - ASTC_8x8 // ASTC 8x8 压缩 -}; - -// ============================================================================ -// 纹理接口 -// ============================================================================ -class Texture { -public: - virtual ~Texture() = default; - - // 获取尺寸 - virtual int getWidth() const = 0; - virtual int getHeight() const = 0; - virtual Size getSize() const = 0; - - // 获取通道数 - virtual int getChannels() const = 0; - - // 获取像素格式 - virtual PixelFormat getFormat() const = 0; - - // 获取原始句柄(用于底层渲染) - virtual void* getNativeHandle() const = 0; - - // 是否有效 - virtual bool isValid() const = 0; - - // 设置过滤模式 - virtual void setFilter(bool linear) = 0; - - // 设置环绕模式 - virtual void setWrap(bool repeat) = 0; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/texture/texture_atlas.h b/Extra2D/include/extra2d/graphics/texture/texture_atlas.h deleted file mode 100644 index 8fca067..0000000 --- a/Extra2D/include/extra2d/graphics/texture/texture_atlas.h +++ /dev/null @@ -1,184 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 纹理图集 - 自动将小纹理合并到大图集以减少 DrawCall -// ============================================================================ - -/** - * @brief 图集中的单个纹理条目 - */ -struct AtlasEntry { - std::string name; // 原始纹理名称/路径 - Rect uvRect; // 在图集中的 UV 坐标范围 - Vec2 originalSize; // 原始纹理尺寸 - uint32_t padding; // 边距(用于避免纹理 bleeding) - - AtlasEntry() : uvRect(), originalSize(), padding(2) {} -}; - -/** - * @brief 纹理图集页面 - * 当单个图集放不下时,创建多个页面 - */ -class TextureAtlasPage { -public: - static constexpr int DEFAULT_SIZE = 2048; - static constexpr int MAX_SIZE = 4096; - static constexpr int MIN_TEXTURE_SIZE = 32; // 小于此大小的纹理才考虑合并 - static constexpr int PADDING = 2; // 纹理间边距 - - TextureAtlasPage(int width = DEFAULT_SIZE, int height = DEFAULT_SIZE); - ~TextureAtlasPage(); - - // 尝试添加纹理到图集 - // 返回是否成功,如果成功则输出 uvRect - bool tryAddTexture(const std::string& name, int texWidth, int texHeight, - const uint8_t* pixels, Rect& outUvRect); - - // 获取图集纹理 - Ptr getTexture() const { return texture_; } - - // 获取条目 - const AtlasEntry* getEntry(const std::string& name) const; - - // 获取使用率 - float getUsageRatio() const; - - // 获取尺寸 - int getWidth() const { return width_; } - int getHeight() const { return height_; } - - // 是否已满 - bool isFull() const { return isFull_; } - -private: - int width_, height_; - Ptr texture_; - std::unordered_map entries_; - - // 矩形打包数据 - struct PackNode { - int x, y, width, height; - bool used; - std::unique_ptr left; - std::unique_ptr right; - - PackNode(int x_, int y_, int w, int h) - : x(x_), y(y_), width(w), height(h), used(false) {} - }; - - std::unique_ptr root_; - bool isFull_; - int usedArea_; - - // 递归插入 - PackNode* insert(PackNode* node, int width, int height); - void writePixels(int x, int y, int w, int h, const uint8_t* pixels); -}; - -/** - * @brief 纹理图集管理器 - * 自动管理多个图集页面,提供统一的纹理查询接口 - */ -class TextureAtlas { -public: - TextureAtlas(); - ~TextureAtlas(); - - // 初始化 - void init(int pageSize = TextureAtlasPage::DEFAULT_SIZE); - - // 添加纹理到图集 - // 如果纹理太大,返回 false,应该作为独立纹理加载 - bool addTexture(const std::string& name, int width, int height, - const uint8_t* pixels); - - // 查询纹理是否在图集中 - bool contains(const std::string& name) const; - - // 获取纹理在图集中的信息 - // 返回图集纹理和 UV 坐标 - const Texture* getAtlasTexture(const std::string& name) const; - Rect getUVRect(const std::string& name) const; - - // 获取原始纹理尺寸 - Vec2 getOriginalSize(const std::string& name) const; - - // 获取所有图集页面 - const std::vector>& getPages() const { return pages_; } - - // 获取总使用率 - float getTotalUsageRatio() const; - - // 清空所有图集 - void clear(); - - // 设置是否启用自动图集 - void setEnabled(bool enabled) { enabled_ = enabled; } - bool isEnabled() const { return enabled_; } - - // 设置纹理大小阈值(小于此大小的纹理才进入图集) - void setSizeThreshold(int threshold) { sizeThreshold_ = threshold; } - int getSizeThreshold() const { return sizeThreshold_; } - -private: - std::vector> pages_; - std::unordered_map entryToPage_; - - int pageSize_; - int sizeThreshold_; - bool enabled_; - bool initialized_; -}; - -/** - * @brief 全局图集管理器(单例) - */ -class TextureAtlasMgr { -public: - static TextureAtlasMgr& get(); - - // 获取主图集 - TextureAtlas& getAtlas() { return atlas_; } - - // 快捷方法 - bool addTexture(const std::string& name, int width, int height, - const uint8_t* pixels) { - return atlas_.addTexture(name, width, height, pixels); - } - - bool contains(const std::string& name) const { - return atlas_.contains(name); - } - - const Texture* getAtlasTexture(const std::string& name) const { - return atlas_.getAtlasTexture(name); - } - - Rect getUVRect(const std::string& name) const { - return atlas_.getUVRect(name); - } - -private: - TextureAtlasMgr() = default; - ~TextureAtlasMgr() = default; - - TextureAtlasMgr(const TextureAtlasMgr&) = delete; - TextureAtlasMgr& operator=(const TextureAtlasMgr&) = delete; - - TextureAtlas atlas_; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/texture/texture_pool.h b/Extra2D/include/extra2d/graphics/texture/texture_pool.h deleted file mode 100644 index e2f9178..0000000 --- a/Extra2D/include/extra2d/graphics/texture/texture_pool.h +++ /dev/null @@ -1,562 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace extra2d { - -// 前向声明 -class Scene; -class RenderBackend; - -// ============================================================================ -// 纹理加载选项 -// ============================================================================ -struct TextureLoadOptions { - bool generateMipmaps = true; // 是否生成 mipmaps - bool sRGB = true; // 是否使用 sRGB 色彩空间 - bool premultiplyAlpha = false; // 是否预乘 Alpha - PixelFormat preferredFormat = PixelFormat::RGBA8; // 首选像素格式 -}; - -// ============================================================================ -// 纹理键 - 用于唯一标识纹理缓存条目 -// ============================================================================ -struct TextureKey { - std::string path; // 纹理文件路径 - Rect region; // 纹理区域(用于纹理图集) - - /** - * @brief 默认构造函数 - */ - TextureKey() = default; - - /** - * @brief 构造函数(仅路径) - * @param p 纹理文件路径 - */ - explicit TextureKey(const std::string &p) : path(p), region(Rect::Zero()) {} - - /** - * @brief 构造函数(路径 + 区域) - * @param p 纹理文件路径 - * @param r 纹理区域 - */ - TextureKey(const std::string &p, const Rect &r) : path(p), region(r) {} - - /** - * @brief 相等比较运算符 - * @param other 另一个 TextureKey - * @return 是否相等 - */ - bool operator==(const TextureKey &other) const { - return path == other.path && region == other.region; - } - - /** - * @brief 不等比较运算符 - * @param other 另一个 TextureKey - * @return 是否不等 - */ - bool operator!=(const TextureKey &other) const { return !(*this == other); } -}; - -// ============================================================================ -// TextureKey 哈希函子 -// ============================================================================ -struct TextureKeyHash { - /** - * @brief 计算 TextureKey 的哈希值 - * @param key 纹理键 - * @return 哈希值 - */ - size_t operator()(const TextureKey &key) const { - size_t h1 = std::hash{}(key.path); - size_t h2 = std::hash{}(key.region.origin.x); - size_t h3 = std::hash{}(key.region.origin.y); - size_t h4 = std::hash{}(key.region.size.width); - size_t h5 = std::hash{}(key.region.size.height); - - // 组合哈希值 - size_t result = h1; - result ^= h2 + 0x9e3779b9 + (result << 6) + (result >> 2); - result ^= h3 + 0x9e3779b9 + (result << 6) + (result >> 2); - result ^= h4 + 0x9e3779b9 + (result << 6) + (result >> 2); - result ^= h5 + 0x9e3779b9 + (result << 6) + (result >> 2); - return result; - } -}; - -// ============================================================================ -// 纹理池条目 -// ============================================================================ -struct TexturePoolEntry { - Ptr texture; // 纹理对象 - mutable std::atomic refCount; // 引用计数 - TextureKey key; // 纹理键 - size_t memorySize; // 内存占用(字节) - mutable uint64_t lastAccessTime; // 最后访问时间戳 - - /** - * @brief 默认构造函数 - */ - TexturePoolEntry() - : texture(nullptr), refCount(0), key(), memorySize(0), lastAccessTime(0) { - } - - /** - * @brief 构造函数 - * @param tex 纹理对象 - * @param k 纹理键 - * @param memSize 内存占用 - */ - TexturePoolEntry(Ptr tex, const TextureKey &k, size_t memSize) - : texture(tex), refCount(1), key(k), memorySize(memSize), - lastAccessTime(getCurrentTime()) {} - - /** - * @brief 移动构造函数 - * @param other 另一个条目 - */ - TexturePoolEntry(TexturePoolEntry &&other) noexcept - : texture(std::move(other.texture)), - refCount(other.refCount.load(std::memory_order_relaxed)), - key(std::move(other.key)), memorySize(other.memorySize), - lastAccessTime(other.lastAccessTime) {} - - /** - * @brief 移动赋值运算符 - * @param other 另一个条目 - * @return 引用 - */ - TexturePoolEntry &operator=(TexturePoolEntry &&other) noexcept { - if (this != &other) { - texture = std::move(other.texture); - refCount.store(other.refCount.load(std::memory_order_relaxed), - std::memory_order_relaxed); - key = std::move(other.key); - memorySize = other.memorySize; - lastAccessTime = other.lastAccessTime; - } - return *this; - } - - // 禁止拷贝 - TexturePoolEntry(const TexturePoolEntry &) = delete; - TexturePoolEntry &operator=(const TexturePoolEntry &) = delete; - - /** - * @brief 更新最后访问时间 - */ - void touch() const { lastAccessTime = getCurrentTime(); } - - /** - * @brief 获取当前时间戳 - * @return 时间戳(毫秒) - */ - static uint64_t getCurrentTime() { - auto now = std::chrono::steady_clock::now(); - auto duration = std::chrono::duration_cast( - now.time_since_epoch()); - return static_cast(duration.count()); - } -}; - -// ============================================================================ -// 纹理引用智能指针 - 自动管理纹理池引用计数 -// ============================================================================ -class TextureRef { -public: - /** - * @brief 默认构造函数 - */ - TextureRef() : texture_(nullptr), entry_(nullptr), mutex_(nullptr) {} - - /** - * @brief 构造函数 - * @param texture 纹理对象 - * @param entry 纹理池条目 - * @param mutex 互斥锁 - */ - TextureRef(Ptr texture, TexturePoolEntry *entry, std::mutex *mutex) - : texture_(texture), entry_(entry), mutex_(mutex) {} - - /** - * @brief 创建独立的纹理引用(不管理引用计数) - * @param texture 纹理对象 - * @return 独立的纹理引用 - */ - static TextureRef fromTexture(Ptr texture) { - return TextureRef(texture, nullptr, nullptr); - } - - /** - * @brief 拷贝构造函数 - * @param other 另一个 TextureRef - */ - TextureRef(const TextureRef &other) - : texture_(other.texture_), entry_(other.entry_), mutex_(other.mutex_) { - if (entry_ && entry_->refCount.load(std::memory_order_relaxed) > 0) { - entry_->refCount.fetch_add(1, std::memory_order_relaxed); - } - } - - /** - * @brief 移动构造函数 - * @param other 另一个 TextureRef - */ - TextureRef(TextureRef &&other) noexcept - : texture_(std::move(other.texture_)), entry_(other.entry_), - mutex_(other.mutex_) { - other.entry_ = nullptr; - other.mutex_ = nullptr; - } - - /** - * @brief 析构函数 - */ - ~TextureRef() { reset(); } - - /** - * @brief 拷贝赋值运算符 - * @param other 另一个 TextureRef - * @return 引用 - */ - TextureRef &operator=(const TextureRef &other) { - if (this != &other) { - reset(); - texture_ = other.texture_; - entry_ = other.entry_; - mutex_ = other.mutex_; - if (entry_ && entry_->refCount.load(std::memory_order_relaxed) > 0) { - entry_->refCount.fetch_add(1, std::memory_order_relaxed); - } - } - return *this; - } - - /** - * @brief 移动赋值运算符 - * @param other 另一个 TextureRef - * @return 引用 - */ - TextureRef &operator=(TextureRef &&other) noexcept { - if (this != &other) { - reset(); - texture_ = std::move(other.texture_); - entry_ = other.entry_; - mutex_ = other.mutex_; - other.entry_ = nullptr; - other.mutex_ = nullptr; - } - return *this; - } - - /** - * @brief 重置引用 - */ - void reset() { - if (entry_ && mutex_) { - std::lock_guard lock(*mutex_); - if (entry_->refCount.load(std::memory_order_relaxed) > 0) { - entry_->refCount.fetch_sub(1, std::memory_order_relaxed); - } - } - texture_.reset(); - entry_ = nullptr; - mutex_ = nullptr; - } - - /** - * @brief 获取纹理对象 - * @return 纹理对象指针 - */ - Texture *get() const { return texture_.get(); } - - /** - * @brief 获取纹理对象(智能指针) - * @return 纹理对象智能指针 - */ - Ptr getPtr() const { return texture_; } - - /** - * @brief 检查是否有效 - * @return 是否有效 - */ - bool valid() const { return texture_ != nullptr; } - - /** - * @brief 布尔转换运算符 - */ - explicit operator bool() const { return valid(); } - - /** - * @brief 箭头运算符 - */ - Texture *operator->() const { return texture_.get(); } - - /** - * @brief 解引用运算符 - */ - Texture &operator*() const { return *texture_; } - -private: - Ptr texture_; - TexturePoolEntry *entry_; - std::mutex *mutex_; -}; - -// ============================================================================ -// 纹理池 - 纹理缓存和内存管理系统 -// 特性: -// - 纹理缓存和复用 -// - 引用计数管理 -// - 内存使用限制 -// - LRU 淘汰策略 -// - 线程安全 -// ============================================================================ -class TexturePool { -public: - // ======================================================================== - // 统计信息 - // ======================================================================== - struct Stats { - size_t textureCount = 0; // 纹理数量 - size_t memoryUsage = 0; // 内存使用量(字节) - size_t maxMemoryUsage = 0; // 最大内存使用量 - size_t cacheHits = 0; // 缓存命中次数 - size_t cacheMisses = 0; // 缓存未命中次数 - size_t evictionCount = 0; // 淘汰次数 - }; - - // ======================================================================== - // 构造和析构 - // ======================================================================== - - /** - * @brief 默认构造函数 - */ - TexturePool(); - - /** - * @brief 构造函数 - * @param scene 场景指针 - * @param maxMemoryUsage 最大内存使用量(0 表示无限制) - */ - explicit TexturePool(Scene *scene, size_t maxMemoryUsage = 0); - - /** - * @brief 析构函数 - */ - ~TexturePool(); - - // 禁止拷贝 - TexturePool(const TexturePool &) = delete; - TexturePool &operator=(const TexturePool &) = delete; - - /** - * @brief 初始化纹理池 - * @param scene 场景指针 - * @param maxMemoryUsage 最大内存使用量(0 表示无限制) - */ - void init(Scene *scene, size_t maxMemoryUsage = 0); - - // ======================================================================== - // 纹理加载 - // ======================================================================== - - /** - * @brief 从文件加载纹理 - * @param path 文件路径 - * @param options 加载选项 - * @return 纹理引用 - */ - TextureRef load(const std::string &path, - const TextureLoadOptions &options = TextureLoadOptions()); - - /** - * @brief 从文件加载纹理区域 - * @param path 文件路径 - * @param region 纹理区域 - * @param options 加载选项 - * @return 纹理引用 - */ - TextureRef load(const std::string &path, const Rect ®ion, - const TextureLoadOptions &options = TextureLoadOptions()); - - /** - * @brief 从内存加载纹理 - * @param data 像素数据 - * @param width 宽度 - * @param height 高度 - * @param channels 通道数 - * @param key 缓存键 - * @return 纹理引用 - */ - TextureRef loadFromMemory(const uint8_t *data, int width, int height, - int channels, const std::string &key); - - /** - * @brief 获取或加载纹理 - * @param path 文件路径 - * @param options 加载选项 - * @return 纹理引用 - */ - TextureRef - getOrLoad(const std::string &path, - const TextureLoadOptions &options = TextureLoadOptions()); - - /** - * @brief 获取或加载纹理区域 - * @param path 文件路径 - * @param region 纹理区域 - * @param options 加载选项 - * @return 纹理引用 - */ - TextureRef - getOrLoad(const std::string &path, const Rect ®ion, - const TextureLoadOptions &options = TextureLoadOptions()); - - // ======================================================================== - // 引用计数管理 - // ======================================================================== - - /** - * @brief 增加引用计数 - * @param key 纹理键 - * @return 是否成功 - */ - bool addRef(const TextureKey &key); - - /** - * @brief 减少引用计数 - * @param key 纹理键 - * @return 减少后的引用计数 - */ - uint32_t release(const TextureKey &key); - - /** - * @brief 获取引用计数 - * @param key 纹理键 - * @return 引用计数 - */ - uint32_t getRefCount(const TextureKey &key) const; - - // ======================================================================== - // 缓存管理 - // ======================================================================== - - /** - * @brief 检查纹理是否已缓存 - * @param key 纹理键 - * @return 是否已缓存 - */ - bool isCached(const TextureKey &key) const; - - /** - * @brief 从缓存中移除纹理 - * @param key 纹理键 - * @return 是否成功 - */ - bool removeFromCache(const TextureKey &key); - - /** - * @brief 垃圾回收(移除引用计数为 0 的纹理) - * @return 移除的纹理数量 - */ - size_t collectGarbage(); - - /** - * @brief 清空所有缓存 - */ - void clear(); - - // ======================================================================== - // 内存管理 - // ======================================================================== - - /** - * @brief 获取当前内存使用量 - * @return 内存使用量(字节) - */ - size_t getMemoryUsage() const; - - /** - * @brief 设置最大内存使用量 - * @param maxMemory 最大内存使用量(0 表示无限制) - */ - void setMaxMemoryUsage(size_t maxMemory); - - /** - * @brief 获取最大内存使用量 - * @return 最大内存使用量 - */ - size_t getMaxMemoryUsage() const { return maxMemoryUsage_; } - - /** - * @brief 执行 LRU 淘汰 - * @param targetMemory 目标内存使用量 - * @return 淘汰的纹理数量 - */ - size_t evictLRU(size_t targetMemory = 0); - - // ======================================================================== - // 统计信息 - // ======================================================================== - - /** - * @brief 获取统计信息 - * @return 统计信息 - */ - Stats getStats() const; - - /** - * @brief 重置统计信息 - */ - void resetStats(); - -private: - /** - * @brief 计算纹理内存大小 - * @param texture 纹理对象 - * @return 内存大小(字节) - */ - static size_t calculateTextureMemory(const Texture *texture); - - /** - * @brief 检查是否需要淘汰 - * @return 是否需要淘汰 - */ - bool needsEviction() const; - - /** - * @brief 尝试自动淘汰 - */ - void tryAutoEvict(); - - Scene *scene_; // 场景指针 - mutable std::mutex mutex_; // 互斥锁 - std::unordered_map - cache_; // 纹理缓存 - - size_t maxMemoryUsage_; // 最大内存使用量 - size_t currentMemoryUsage_; // 当前内存使用量 - - // 统计信息 - mutable std::atomic cacheHits_; - mutable std::atomic cacheMisses_; - mutable std::atomic evictionCount_; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_buffer.h b/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_buffer.h new file mode 100644 index 0000000..9b32be4 --- /dev/null +++ b/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_buffer.h @@ -0,0 +1,177 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { +namespace rhi { + +/** + * @brief OpenGL 缓冲区实现 + * + * 使用 OpenGL 4.5 DSA (Direct State Access) 特性 + */ +class GLRHIBuffer : public RHIBuffer { +public: + /** + * @brief 构造函数 + */ + GLRHIBuffer(); + + /** + * @brief 析构函数 + */ + ~GLRHIBuffer() override; + + /** + * @brief 初始化缓冲区 + * @param desc 缓冲区描述 + * @return 成功返回 true + */ + bool init(const BufferDesc& desc); + + /** + * @brief 释放资源 + */ + void shutdown(); + + // RHIBuffer 接口实现 + BufferType getType() const override { return type_; } + size_t getSize() const override { return size_; } + BufferUsage getUsage() const override { return usage_; } + void setData(const void* data, size_t size, size_t offset = 0) override; + void getData(void* data, size_t size, size_t offset = 0) const override; + void* map(size_t offset, size_t size, bool read = false, bool write = true) override; + void unmap() override; + bool isValid() const override { return bufferID_ != 0; } + uintptr_t getNativeHandle() const override { return static_cast(bufferID_); } + const std::string& getDebugName() const override { return debugName_; } + void setDebugName(const std::string& name) override { debugName_ = name; } + + /** + * @brief 获取 OpenGL 缓冲区 ID + */ + GLuint getBufferID() const { return bufferID_; } + + /** + * @brief 获取 OpenGL 目标类型 + */ + GLenum getTarget() const { return target_; } + + /** + * @brief 绑定到指定绑定点 + */ + void bindBase(uint32_t index); + +private: + GLuint bufferID_ = 0; + GLenum target_ = GL_ARRAY_BUFFER; + size_t size_ = 0; + BufferType type_ = BufferType::Vertex; + BufferUsage usage_ = BufferUsage::Static; + std::string debugName_; + bool mapped_ = false; + void* mappedPtr_ = nullptr; + + /** + * @brief 转换缓冲区类型到 OpenGL 目标 + */ + static GLenum convertType(BufferType type); + + /** + * @brief 转换使用方式到 OpenGL 枚举 + */ + static GLenum convertUsage(BufferUsage usage); +}; + +/** + * @brief OpenGL 顶点缓冲区实现 + */ +class GLRHIVertexBuffer : public RHIVertexBuffer { +public: + GLRHIVertexBuffer() = default; + ~GLRHIVertexBuffer() override = default; + + bool init(size_t size, BufferUsage usage, const void* data = nullptr); + void shutdown(); + + BufferType getType() const override { return BufferType::Vertex; } + size_t getSize() const override { return buffer_.getSize(); } + BufferUsage getUsage() const override { return buffer_.getUsage(); } + void setData(const void* data, size_t size, size_t offset = 0) override { buffer_.setData(data, size, offset); } + void getData(void* data, size_t size, size_t offset = 0) const override { buffer_.getData(data, size, offset); } + void* map(size_t offset, size_t size, bool read = false, bool write = true) override { return buffer_.map(offset, size, read, write); } + void unmap() override { buffer_.unmap(); } + bool isValid() const override { return buffer_.isValid(); } + uintptr_t getNativeHandle() const override { return buffer_.getNativeHandle(); } + const std::string& getDebugName() const override { return buffer_.getDebugName(); } + void setDebugName(const std::string& name) override { buffer_.setDebugName(name); } + + GLuint getBufferID() const { return buffer_.getBufferID(); } + +private: + GLRHIBuffer buffer_; +}; + +/** + * @brief OpenGL 索引缓冲区实现 + */ +class GLRHIIndexBuffer : public RHIIndexBuffer { +public: + GLRHIIndexBuffer() = default; + ~GLRHIIndexBuffer() override = default; + + bool init(size_t size, BufferUsage usage, const void* data = nullptr); + void shutdown(); + + BufferType getType() const override { return BufferType::Index; } + size_t getSize() const override { return buffer_.getSize(); } + BufferUsage getUsage() const override { return buffer_.getUsage(); } + void setData(const void* data, size_t size, size_t offset = 0) override { buffer_.setData(data, size, offset); } + void getData(void* data, size_t size, size_t offset = 0) const override { buffer_.getData(data, size, offset); } + void* map(size_t offset, size_t size, bool read = false, bool write = true) override { return buffer_.map(offset, size, read, write); } + void unmap() override { buffer_.unmap(); } + bool isValid() const override { return buffer_.isValid(); } + uintptr_t getNativeHandle() const override { return buffer_.getNativeHandle(); } + const std::string& getDebugName() const override { return buffer_.getDebugName(); } + void setDebugName(const std::string& name) override { buffer_.setDebugName(name); } + + GLuint getBufferID() const { return buffer_.getBufferID(); } + +private: + GLRHIBuffer buffer_; +}; + +/** + * @brief OpenGL Uniform 缓冲区实现 + */ +class GLRHIUniformBuffer : public RHIUniformBuffer { +public: + GLRHIUniformBuffer() = default; + ~GLRHIUniformBuffer() override = default; + + bool init(size_t size, BufferUsage usage); + void shutdown(); + + BufferType getType() const override { return BufferType::Uniform; } + size_t getSize() const override { return buffer_.getSize(); } + BufferUsage getUsage() const override { return buffer_.getUsage(); } + void setData(const void* data, size_t size, size_t offset = 0) override { buffer_.setData(data, size, offset); } + void getData(void* data, size_t size, size_t offset = 0) const override { buffer_.getData(data, size, offset); } + void* map(size_t offset, size_t size, bool read = false, bool write = true) override { return buffer_.map(offset, size, read, write); } + void unmap() override { buffer_.unmap(); } + bool isValid() const override { return buffer_.isValid(); } + uintptr_t getNativeHandle() const override { return buffer_.getNativeHandle(); } + const std::string& getDebugName() const override { return buffer_.getDebugName(); } + void setDebugName(const std::string& name) override { buffer_.setDebugName(name); } + + GLuint getBufferID() const { return buffer_.getBufferID(); } + void bindBase(uint32_t index) { buffer_.bindBase(index); } + +private: + GLRHIBuffer buffer_; +}; + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_device.h b/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_device.h new file mode 100644 index 0000000..4638b85 --- /dev/null +++ b/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_device.h @@ -0,0 +1,200 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +class IWindow; + +namespace rhi { + +/** + * @brief OpenGL RHI 设备实现 + * + * 使用 OpenGL 4.5 Core Profile + DSA 特性 + */ +class GLRHIDevice : public RHIDevice { +public: + /** + * @brief 构造函数 + */ + GLRHIDevice(); + + /** + * @brief 析构函数 + */ + ~GLRHIDevice() override; + + // ======================================================================== + // 生命周期 + // ======================================================================== + + bool init(IWindow* window) override; + void shutdown() override; + bool isValid() const override { return initialized_; } + GraphicsAPI getAPI() const override { return GraphicsAPI::OpenGL; } + const DeviceCaps& getCaps() const override { return caps_; } + + // ======================================================================== + // 帧管理 + // ======================================================================== + + void beginFrame() override; + void endFrame() override; + void present() override; + void setVSync(bool enabled) override; + bool isVSyncEnabled() const override { return vsyncEnabled_; } + void setDefaultFramebufferSize(uint32_t width, uint32_t height) override; + + // ======================================================================== + // 资源创建 - 缓冲区 + // ======================================================================== + + Ptr createBuffer(const BufferDesc& desc) override; + Ptr createVertexBuffer(size_t size, BufferUsage usage = BufferUsage::Static, + const void* data = nullptr) override; + Ptr createIndexBuffer(size_t size, BufferUsage usage = BufferUsage::Static, + const void* data = nullptr) override; + Ptr createUniformBuffer(size_t size, BufferUsage usage = BufferUsage::Dynamic) override; + + // ======================================================================== + // 资源创建 - 纹理 + // ======================================================================== + + Ptr createTexture(const TextureDesc& desc) override; + Ptr createTexture2D(uint32_t width, uint32_t height, Format format, + const void* data = nullptr) override; + Ptr createSampler(const SamplerDesc& desc) override; + Ptr createTextureView(Ptr texture, const TextureViewDesc& desc) override; + + // ======================================================================== + // 资源创建 - 着色器 + // ======================================================================== + + Ptr createShader(const ShaderDesc& desc) override; + Ptr createShaderFromSource(const std::string& name, + const std::string& vertexSource, + const std::string& fragmentSource) override; + Ptr createShaderFromFile(const std::string& vertexPath, + const std::string& fragmentPath) override; + + // ======================================================================== + // 资源创建 - 管线 + // ======================================================================== + + Ptr createPipeline(const PipelineDesc& desc, Ptr shader) override; + Ptr createPipeline(const PipelineBuilder& builder) override; + + // ======================================================================== + // 资源创建 - 帧缓冲 + // ======================================================================== + + Ptr createFramebuffer(const FramebufferDesc& desc) override; + Ptr createFramebuffer(const FramebufferBuilder& builder) override; + + // ======================================================================== + // 渲染状态设置 + // ======================================================================== + + void setViewport(const Viewport& viewport) override; + void setScissorRect(const ScissorRect& rect) override; + void setScissorEnabled(bool enabled) override; + + // ======================================================================== + // 渲染命令 - 绑定资源 + // ======================================================================== + + void setPipeline(Ptr pipeline) override; + void setShader(Ptr shader) override; + void setVertexBuffer(uint32_t slot, Ptr buffer, uint32_t offset = 0) override; + void setIndexBuffer(Ptr buffer, IndexFormat format) override; + void setTexture(uint32_t slot, Ptr texture, Ptr sampler = nullptr) override; + void setUniformBuffer(uint32_t slot, Ptr buffer) override; + void setFramebuffer(Ptr framebuffer) override; + void setDefaultFramebuffer() override; + + // ======================================================================== + // 渲染命令 - Uniform 设置 + // ======================================================================== + + void setUniformFloat(const std::string& name, float value) override; + void setUniformVec2(const std::string& name, const glm::vec2& value) override; + void setUniformVec3(const std::string& name, const glm::vec3& value) override; + void setUniformVec4(const std::string& name, const glm::vec4& value) override; + void setUniformMat4(const std::string& name, const glm::mat4& value) override; + void setUniformInt(const std::string& name, int value) override; + + // ======================================================================== + // 渲染命令 - 绘制 + // ======================================================================== + + void draw(const DrawCommand& cmd) override; + void drawIndexed(const DrawIndexedCommand& cmd) override; + void drawIndirect(Ptr indirectBuffer, size_t offset) override; + void drawIndexedIndirect(Ptr indirectBuffer, size_t offset) override; + + // ======================================================================== + // 渲染命令 - 清除 + // ======================================================================== + + void clearColor(const ColorValue& color) override; + void clearDepth(float depth = 1.0f) override; + void clearStencil(uint8_t stencil = 0) override; + void clear(const ColorValue& color, float depth = 1.0f, uint8_t stencil = 0) override; + + // ======================================================================== + // 统计与调试 + // ======================================================================== + + const DeviceStats& getStats() const override { return stats_; } + void resetStats() override; + bool checkError() override; + const std::string& getDebugName() const override { return debugName_; } + void setDebugName(const std::string& name) override { debugName_ = name; } + +private: + bool initialized_ = false; + bool vsyncEnabled_ = true; + IWindow* window_ = nullptr; + DeviceCaps caps_; + DeviceStats stats_; + std::string debugName_; + + // 当前绑定的资源 + Ptr currentPipeline_; + Ptr currentShader_; + Ptr currentFramebuffer_; + IndexFormat currentIndexFormat_ = IndexFormat::UInt16; + + // 默认帧缓冲尺寸 + uint32_t defaultFBWidth_ = 0; + uint32_t defaultFBHeight_ = 0; + + // 裁剪测试状态 + bool scissorEnabled_ = false; + + /** + * @brief 初始化设备能力 + */ + void initCaps(); + + /** + * @brief 设置默认状态 + */ + void setDefaultState(); + + /** + * @brief 应用管线状态 + */ + void applyPipelineState(const BlendState& blend, + const DepthStencilState& depthStencil, + const RasterizerState& rasterizer); +}; + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_pipeline.h b/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_pipeline.h new file mode 100644 index 0000000..e24f6a1 --- /dev/null +++ b/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_pipeline.h @@ -0,0 +1,140 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { +namespace rhi { + +/** + * @brief OpenGL 渲染管线实现 + * + * 封装 OpenGL VAO 和管线状态 + */ +class GLRHIPipeline : public RHIPipeline { +public: + /** + * @brief 构造函数 + */ + GLRHIPipeline(); + + /** + * @brief 析构函数 + */ + ~GLRHIPipeline() override; + + /** + * @brief 初始化管线 + */ + bool init(const PipelineDesc& desc, Ptr shader); + + /** + * @brief 释放资源 + */ + void shutdown(); + + // RHIPipeline 接口实现 + void bind() override; + void unbind() override; + + // 状态访问 + const BlendState& getBlendState() const override { return blendState_; } + const DepthStencilState& getDepthStencilState() const override { return depthStencilState_; } + const RasterizerState& getRasterizerState() const override { return rasterizerState_; } + PrimitiveType getPrimitiveType() const override { return primitiveType_; } + Ptr getShader() const override { return shader_; } + + // 动态状态设置 + void setBlendMode(BlendFactor srcFactor, BlendFactor dstFactor, BlendOp op = BlendOp::Add) override; + void setBlendState(const BlendState& state) override; + void setBlendEnabled(bool enabled) override; + void setDepthTestEnabled(bool enabled) override; + void setDepthWriteEnabled(bool enabled) override; + void setDepthCompareFunc(CompareFunc func) override; + void setCullMode(CullMode mode) override; + void setFrontFace(FrontFace face) override; + void setWireframe(bool enabled) override; + + // 顶点布局 + const std::vector& getVertexAttributes() const override { return vertexAttributes_; } + const std::vector& getVertexBufferBindings() const override { return vertexBindings_; } + uint32_t calculateVertexStride() const override; + + // 有效性检查 + bool isValid() const override { return vaoID_ != 0; } + uintptr_t getNativeHandle() const override { return static_cast(vaoID_); } + const std::string& getDebugName() const override { return debugName_; } + void setDebugName(const std::string& name) override { debugName_ = name; } + + /** + * @brief 获取 OpenGL VAO ID + */ + GLuint getVAOID() const { return vaoID_; } + + /** + * @brief 获取 OpenGL 图元类型 + */ + GLenum getGLPrimitiveType() const { return glPrimitiveType_; } + +private: + GLuint vaoID_ = 0; + Ptr shader_; + BlendState blendState_; + DepthStencilState depthStencilState_; + RasterizerState rasterizerState_; + PrimitiveType primitiveType_ = PrimitiveType::Triangles; + GLenum glPrimitiveType_ = GL_TRIANGLES; + std::vector vertexAttributes_; + std::vector vertexBindings_; + std::string debugName_; + + /** + * @brief 设置 VAO 顶点属性 + */ + void setupVertexAttributes(); + + /** + * @brief 转换图元类型到 OpenGL 枚举 + */ + static GLenum convertPrimitiveType(PrimitiveType type); + + /** + * @brief 转换混合因子到 OpenGL 枚举 + */ + static GLenum convertBlendFactor(BlendFactor factor); + + /** + * @brief 转换混合操作到 OpenGL 枚举 + */ + static GLenum convertBlendOp(BlendOp op); + + /** + * @brief 转换比较函数到 OpenGL 枚举 + */ + static GLenum convertCompareFunc(CompareFunc func); + + /** + * @brief 转换裁剪模式到 OpenGL 枚举 + */ + static GLenum convertCullMode(CullMode mode); + + /** + * @brief 转换正面方向到 OpenGL 枚举 + */ + static GLenum convertFrontFace(FrontFace face); + + /** + * @brief 转换顶点格式到 OpenGL 类型 + */ + static GLenum convertVertexFormat(Format format, bool& normalized); + + /** + * @brief 获取顶点格式分量数 + */ + static GLint getFormatComponents(Format format); +}; + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_shader.h b/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_shader.h new file mode 100644 index 0000000..0d1739a --- /dev/null +++ b/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_shader.h @@ -0,0 +1,160 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { +namespace rhi { + +/** + * @brief OpenGL 着色器实现 + * + * 封装 OpenGL 着色器程序 + */ +class GLRHIShader : public RHIShader { +public: + /** + * @brief 构造函数 + */ + GLRHIShader(); + + /** + * @brief 析构函数 + */ + ~GLRHIShader() override; + + /** + * @brief 从描述初始化着色器 + */ + bool init(const ShaderDesc& desc); + + /** + * @brief 从源码编译着色器 + */ + bool compileFromSource(const std::string& name, + const std::string& vertexSource, + const std::string& fragmentSource, + const std::string& geometrySource = ""); + + /** + * @brief 释放资源 + */ + void shutdown(); + + // RHIShader 接口实现 + void bind() override; + void unbind() override; + const std::string& getName() const override { return name_; } + bool isValid() const override { return programID_ != 0; } + uintptr_t getNativeHandle() const override { return static_cast(programID_); } + + // Uniform 设置 + void setBool(const std::string& name, bool value) override; + void setInt(const std::string& name, int value) override; + void setUInt(const std::string& name, uint32_t value) override; + void setFloat(const std::string& name, float value) override; + void setVec2(const std::string& name, const glm::vec2& value) override; + void setVec3(const std::string& name, const glm::vec3& value) override; + void setVec4(const std::string& name, const glm::vec4& value) override; + void setMat2(const std::string& name, const glm::mat2& value) override; + void setMat3(const std::string& name, const glm::mat3& value) override; + void setMat4(const std::string& name, const glm::mat4& value) override; + void setTexture(const std::string& name, uint32_t slot) override; + void setIntArray(const std::string& name, const int* values, uint32_t count) override; + void setFloatArray(const std::string& name, const float* values, uint32_t count) override; + void setVec2Array(const std::string& name, const glm::vec2* values, uint32_t count) override; + void setVec3Array(const std::string& name, const glm::vec3* values, uint32_t count) override; + void setVec4Array(const std::string& name, const glm::vec4* values, uint32_t count) override; + void setMat4Array(const std::string& name, const glm::mat4* values, uint32_t count) override; + + // Uniform 查询 + bool hasUniform(const std::string& name) const override; + int getUniformLocation(const std::string& name) const override; + const std::vector& getUniforms() const override { return uniforms_; } + const std::vector& getAttributes() const override { return attributes_; } + + /** + * @brief 获取 OpenGL 程序 ID + */ + GLuint getProgramID() const { return programID_; } + +private: + GLuint programID_ = 0; + std::string name_; + mutable std::unordered_map uniformCache_; + std::vector uniforms_; + std::vector attributes_; + + /** + * @brief 编译单个着色器 + */ + GLuint compileShader(GLenum type, const char* source); + + /** + * @brief 链接着色器程序 + */ + bool linkProgram(GLuint vertexShader, GLuint fragmentShader, GLuint geometryShader); + + /** + * @brief 反射获取 Uniform 信息 + */ + void reflectUniforms(); + + /** + * @brief 反射获取属性信息 + */ + void reflectAttributes(); + + /** + * @brief 获取或缓存 Uniform 位置 + */ + GLint getOrCacheUniformLocation(const std::string& name) const; +}; + +/** + * @brief OpenGL 着色器模块实现 + */ +class GLRHIShaderModule : public RHIShaderModule { +public: + /** + * @brief 构造函数 + */ + GLRHIShaderModule(); + + /** + * @brief 析构函数 + */ + ~GLRHIShaderModule() override; + + /** + * @brief 初始化着色器模块 + */ + bool init(ShaderStage stage, const std::string& source, const std::string& entryPoint = "main"); + + /** + * @brief 释放资源 + */ + void shutdown(); + + // RHIShaderModule 接口实现 + ShaderStage getStage() const override { return stage_; } + const std::string& getEntryPoint() const override { return entryPoint_; } + bool isValid() const override { return shaderID_ != 0; } + uintptr_t getNativeHandle() const override { return static_cast(shaderID_); } + + /** + * @brief 获取 OpenGL 着色器 ID + */ + GLuint getShaderID() const { return shaderID_; } + +private: + GLuint shaderID_ = 0; + ShaderStage stage_ = ShaderStage::Vertex; + std::string entryPoint_; +}; + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_texture.h b/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_texture.h new file mode 100644 index 0000000..fa137b5 --- /dev/null +++ b/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_texture.h @@ -0,0 +1,235 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { +namespace rhi { + +/** + * @brief OpenGL 纹理实现 + * + * 使用 OpenGL 4.5 DSA 特性 + */ +class GLRHITexture : public RHITexture { +public: + /** + * @brief 构造函数 + */ + GLRHITexture(); + + /** + * @brief 析构函数 + */ + ~GLRHITexture() override; + + /** + * @brief 初始化纹理 + * @param desc 纹理描述 + * @return 成功返回 true + */ + bool init(const TextureDesc& desc); + + /** + * @brief 释放资源 + */ + void shutdown(); + + // RHITexture 接口实现 + TextureType getType() const override { return type_; } + Format getFormat() const override { return format_; } + uint32_t getWidth() const override { return width_; } + uint32_t getHeight() const override { return height_; } + uint32_t getDepth() const override { return depth_; } + uint32_t getMipLevels() const override { return mipLevels_; } + uint32_t getArrayLayers() const override { return arrayLayers_; } + uint32_t getSampleCount() const override { return sampleCount_; } + void setData(const void* data, uint32_t mipLevel = 0, uint32_t arrayLayer = 0) override; + void setSubData(const void* data, uint32_t x, uint32_t y, + uint32_t width, uint32_t height, uint32_t mipLevel = 0) override; + void generateMipmaps() override; + bool isValid() const override { return textureID_ != 0; } + uintptr_t getNativeHandle() const override { return static_cast(textureID_); } + const std::string& getDebugName() const override { return debugName_; } + void setDebugName(const std::string& name) override { debugName_ = name; } + + /** + * @brief 获取 OpenGL 纹理 ID + */ + GLuint getTextureID() const { return textureID_; } + + /** + * @brief 绑定到纹理单元 + */ + void bind(uint32_t unit); + + /** + * @brief 转换纹理类型到 OpenGL 目标 + */ + static GLenum convertType(TextureType type); + + /** + * @brief 转换格式到 OpenGL 内部格式 + */ + static GLenum convertInternalFormat(Format format); + + /** + * @brief 转换格式到 OpenGL 格式 + */ + static GLenum convertFormat(Format format); + + /** + * @brief 转换格式到 OpenGL 类型 + */ + static GLenum convertDataType(Format format); + + /** + * @brief 计算格式字节大小 + */ + static uint32_t getFormatBytes(Format format); + +private: + GLuint textureID_ = 0; + TextureType type_ = TextureType::Texture2D; + Format format_ = Format::RGBA8_UNORM; + uint32_t width_ = 0; + uint32_t height_ = 0; + uint32_t depth_ = 1; + uint32_t mipLevels_ = 1; + uint32_t arrayLayers_ = 1; + uint32_t sampleCount_ = 1; + std::string debugName_; +}; + +/** + * @brief OpenGL 采样器实现 + */ +class GLRHISampler : public RHISampler { +public: + /** + * @brief 构造函数 + */ + GLRHISampler(); + + /** + * @brief 析构函数 + */ + ~GLRHISampler() override; + + /** + * @brief 初始化采样器 + * @param desc 采样器描述 + * @return 成功返回 true + */ + bool init(const SamplerDesc& desc); + + /** + * @brief 释放资源 + */ + void shutdown(); + + // RHISampler 接口实现 + FilterMode getMinFilter() const override { return minFilter_; } + FilterMode getMagFilter() const override { return magFilter_; } + FilterMode getMipFilter() const override { return mipFilter_; } + AddressMode getAddressModeU() const override { return addressU_; } + AddressMode getAddressModeV() const override { return addressV_; } + AddressMode getAddressModeW() const override { return addressW_; } + float getMaxAnisotropy() const override { return maxAnisotropy_; } + ColorValue getBorderColor() const override { return borderColor_; } + bool isValid() const override { return samplerID_ != 0; } + uintptr_t getNativeHandle() const override { return static_cast(samplerID_); } + const std::string& getDebugName() const override { return debugName_; } + void setDebugName(const std::string& name) override { debugName_ = name; } + + /** + * @brief 获取 OpenGL 采样器 ID + */ + GLuint getSamplerID() const { return samplerID_; } + + /** + * @brief 绑定到纹理单元 + */ + void bind(uint32_t unit); + +private: + GLuint samplerID_ = 0; + FilterMode minFilter_ = FilterMode::Linear; + FilterMode magFilter_ = FilterMode::Linear; + FilterMode mipFilter_ = FilterMode::LinearMipmapLinear; + AddressMode addressU_ = AddressMode::ClampToEdge; + AddressMode addressV_ = AddressMode::ClampToEdge; + AddressMode addressW_ = AddressMode::ClampToEdge; + float maxAnisotropy_ = 1.0f; + ColorValue borderColor_; + std::string debugName_; + + /** + * @brief 转换过滤模式到 OpenGL 枚举 + */ + static GLenum convertFilter(FilterMode filter, FilterMode mipFilter); + + /** + * @brief 转换寻址模式到 OpenGL 枚举 + */ + static GLenum convertAddressMode(AddressMode mode); + + /** + * @brief 转换比较函数到 OpenGL 枚举 + */ + static GLenum convertCompareFunc(CompareFunc func); +}; + +/** + * @brief OpenGL 纹理视图实现 + */ +class GLRHITextureView : public RHITextureView { +public: + /** + * @brief 构造函数 + */ + GLRHITextureView(); + + /** + * @brief 析构函数 + */ + ~GLRHITextureView() override; + + /** + * @brief 初始化纹理视图 + */ + bool init(Ptr texture, const TextureViewDesc& desc); + + /** + * @brief 释放资源 + */ + void shutdown(); + + // RHITextureView 接口实现 + Ptr getTexture() const override { return texture_; } + Format getFormat() const override { return format_; } + uint32_t getBaseMipLevel() const override { return baseMipLevel_; } + uint32_t getMipLevels() const override { return mipLevels_; } + uint32_t getBaseArrayLayer() const override { return baseArrayLayer_; } + uint32_t getArrayLayers() const override { return arrayLayers_; } + bool isValid() const override { return viewID_ != 0; } + uintptr_t getNativeHandle() const override { return static_cast(viewID_); } + + /** + * @brief 获取 OpenGL 纹理视图 ID + */ + GLuint getViewID() const { return viewID_; } + +private: + GLuint viewID_ = 0; + Ptr texture_; + Format format_ = Format::RGBA8_UNORM; + uint32_t baseMipLevel_ = 0; + uint32_t mipLevels_ = 1; + uint32_t baseArrayLayer_ = 0; + uint32_t arrayLayers_ = 1; +}; + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/batch/shape_batcher.h b/Extra2D/include/extra2d/render/batch/shape_batcher.h new file mode 100644 index 0000000..66325d8 --- /dev/null +++ b/Extra2D/include/extra2d/render/batch/shape_batcher.h @@ -0,0 +1,327 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 形状顶点结构 + */ +struct ShapeVertex { + Vec2 position; + Color color; + + ShapeVertex() : position(), color(Colors::White) {} + + ShapeVertex(const Vec2& pos, const Color& col) + : position(pos), color(col) {} +}; + +/** + * @brief 形状批次数据 + */ +struct ShapeBatch { + rhi::PrimitiveType primitiveType; + uint32_t startIndex; + uint32_t indexCount; + rhi::BlendState blendState; + float lineWidth; + + ShapeBatch() + : primitiveType(rhi::PrimitiveType::Triangles), startIndex(0), + indexCount(0), blendState(rhi::BlendState::alphaBlend()), lineWidth(1.0f) {} +}; + +/** + * @brief 形状批处理器配置 + */ +struct ShapeBatcherConfig { + uint32_t maxBatchSize; + uint32_t circleSegments; + bool antialiasLines; + + ShapeBatcherConfig() + : maxBatchSize(5000), + circleSegments(32), + antialiasLines(true) {} +}; + +/** + * @brief 形状批处理器 + * + * 支持绘制基本形状:线条、矩形、圆形、三角形、多边形 + * 自动合并相同渲染状态的形状,减少 Draw Call + */ +class ShapeBatcher { +public: + static constexpr uint32_t DEFAULT_MAX_BATCH_SIZE = 5000; + static constexpr uint32_t DEFAULT_CIRCLE_SEGMENTS = 32; + + ShapeBatcher(); + explicit ShapeBatcher(const ShapeBatcherConfig& config); + ~ShapeBatcher(); + + /** + * @brief 初始化批处理器 + * @param device RHI 设备 + * @return 成功返回 true + */ + bool init(Ptr device); + + /** + * @brief 关闭批处理器 + */ + void shutdown(); + + /** + * @brief 检查是否已初始化 + */ + bool isInitialized() const { return initialized_; } + + /** + * @brief 开始批处理 + */ + void begin(); + + /** + * @brief 开始批处理(指定视图投影矩阵) + */ + void begin(const glm::mat4& viewProj); + + /** + * @brief 结束批处理 + */ + void end(); + + /** + * @brief 绘制线条 + */ + void drawLine(const Vec2& start, const Vec2& end, + const Color& color = Colors::White, + float width = 1.0f); + + /** + * @brief 绘制多条线段 + */ + void drawLines(const std::vector& points, + const Color& color = Colors::White, + float width = 1.0f); + + /** + * @brief 绘制折线 + */ + void drawPolyline(const std::vector& points, + const Color& color = Colors::White, + float width = 1.0f); + + /** + * @brief 绘制矩形(轮廓) + */ + void drawRect(const Rect& rect, + const Color& color = Colors::White, + float width = 1.0f); + + /** + * @brief 绘制填充矩形 + */ + void drawFilledRect(const Rect& rect, + const Color& color = Colors::White); + + /** + * @brief 绘制圆形(轮廓) + */ + void drawCircle(const Vec2& center, float radius, + const Color& color = Colors::White, + int segments = 0, + float width = 1.0f); + + /** + * @brief 绘制填充圆形 + */ + void drawFilledCircle(const Vec2& center, float radius, + const Color& color = Colors::White, + int segments = 0); + + /** + * @brief 绘制椭圆(轮廓) + */ + void drawEllipse(const Vec2& center, float radiusX, float radiusY, + const Color& color = Colors::White, + int segments = 0, + float width = 1.0f); + + /** + * @brief 绘制填充椭圆 + */ + void drawFilledEllipse(const Vec2& center, float radiusX, float radiusY, + const Color& color = Colors::White, + int segments = 0); + + /** + * @brief 绘制三角形(轮廓) + */ + void drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3, + const Color& color = Colors::White, + float width = 1.0f); + + /** + * @brief 绘制填充三角形 + */ + void drawFilledTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3, + const Color& color = Colors::White); + + /** + * @brief 绘制多边形(轮廓) + */ + void drawPolygon(const std::vector& points, + const Color& color = Colors::White, + float width = 1.0f); + + /** + * @brief 绘制填充多边形 + */ + void drawFilledPolygon(const std::vector& points, + const Color& color = Colors::White); + + /** + * @brief 绘制圆弧 + */ + void drawArc(const Vec2& center, float radius, + float startAngle, float endAngle, + const Color& color = Colors::White, + int segments = 0, + float width = 1.0f); + + /** + * @brief 绘制渲染命令 + */ + void draw(const RenderCommand& command); + + /** + * @brief 设置视图投影矩阵 + */ + void setViewProjection(const glm::mat4& viewProj); + + /** + * @brief 获取当前视图投影矩阵 + */ + const glm::mat4& getViewProjection() const { return viewProj_; } + + /** + * @brief 获取统计信息 + */ + uint32_t getDrawCallCount() const { return drawCallCount_; } + uint32_t getShapeCount() const { return shapeCount_; } + uint32_t getBatchCount() const { return batchCount_; } + + /** + * @brief 重置统计信息 + */ + void resetStats(); + + /** + * @brief 获取配置 + */ + const ShapeBatcherConfig& getConfig() const { return config_; } + + /** + * @brief 设置配置 + */ + void setConfig(const ShapeBatcherConfig& config); + + /** + * @brief 获取 RHI 设备 + */ + Ptr getDevice() const { return device_; } + + /** + * @brief 获取着色器 + */ + Ptr getShader() const { return shader_; } + + /** + * @brief 设置自定义着色器 + */ + void setShader(Ptr shader); + +private: + /** + * @brief 创建着色器 + */ + bool createShader(); + + /** + * @brief 创建管线 + */ + bool createPipeline(); + + /** + * @brief 创建缓冲区 + */ + bool createBuffers(); + + /** + * @brief 刷新当前批次 + */ + void flushBatch(rhi::PrimitiveType primitiveType, float lineWidth = 1.0f); + + /** + * @brief 添加顶点 + */ + uint32_t addVertex(const Vec2& position, const Color& color); + + /** + * @brief 添加索引 + */ + void addIndex(uint32_t index); + + /** + * @brief 生成圆形顶点 + */ + void generateCircleVertices(const Vec2& center, float radius, + int segments, std::vector& vertices) const; + + /** + * @brief 生成椭圆顶点 + */ + void generateEllipseVertices(const Vec2& center, float radiusX, float radiusY, + int segments, std::vector& vertices) const; + + Ptr device_; + Ptr shader_; + Ptr pipeline_; + Ptr linePipeline_; + Ptr vertexBuffer_; + Ptr indexBuffer_; + Ptr uniformBuffer_; + + std::vector vertices_; + std::vector indices_; + std::vector batches_; + + ShapeBatcherConfig config_; + glm::mat4 viewProj_; + + rhi::BlendState currentBlend_; + uint32_t currentVertexIndex_; + uint32_t currentIndexIndex_; + + uint32_t drawCallCount_; + uint32_t shapeCount_; + uint32_t batchCount_; + + bool initialized_; + bool inBatch_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/batch/sprite_batcher.h b/Extra2D/include/extra2d/render/batch/sprite_batcher.h new file mode 100644 index 0000000..2545701 --- /dev/null +++ b/Extra2D/include/extra2d/render/batch/sprite_batcher.h @@ -0,0 +1,273 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 精灵顶点结构 + */ +struct SpriteVertex { + Vec2 position; + Vec2 texCoord; + Color color; + + SpriteVertex() : position(), texCoord(), color(Colors::White) {} + + SpriteVertex(const Vec2& pos, const Vec2& uv, const Color& col) + : position(pos), texCoord(uv), color(col) {} +}; + +/** + * @brief 精灵批次数据 + */ +struct SpriteBatch { + Ptr texture; + uint32_t startIndex; + uint32_t indexCount; + rhi::BlendState blendState; + glm::mat4 transform; + + SpriteBatch() + : texture(nullptr), startIndex(0), indexCount(0), + blendState(rhi::BlendState::alphaBlend()), transform(1.0f) {} +}; + +/** + * @brief 精灵批处理器配置 + */ +struct SpriteBatcherConfig { + uint32_t maxBatchSize; + uint32_t maxTextureSlots; + bool enableInstancing; + bool dynamicVertices; + + SpriteBatcherConfig() + : maxBatchSize(10000), + maxTextureSlots(8), + enableInstancing(false), + dynamicVertices(true) {} +}; + +/** + * @brief 精灵批处理器 + * + * 自动合并相同纹理和混合状态的精灵,减少 Draw Call + * 支持动态顶点缓冲区,适合频繁更新的 2D 场景 + */ +class SpriteBatcher { +public: + static constexpr uint32_t DEFAULT_MAX_BATCH_SIZE = 10000; + static constexpr uint32_t VERTEX_PER_SPRITE = 4; + static constexpr uint32_t INDEX_PER_SPRITE = 6; + + SpriteBatcher(); + explicit SpriteBatcher(const SpriteBatcherConfig& config); + ~SpriteBatcher(); + + /** + * @brief 初始化批处理器 + * @param device RHI 设备 + * @return 成功返回 true + */ + bool init(Ptr device); + + /** + * @brief 关闭批处理器 + */ + void shutdown(); + + /** + * @brief 检查是否已初始化 + */ + bool isInitialized() const { return initialized_; } + + /** + * @brief 开始批处理 + */ + void begin(); + + /** + * @brief 开始批处理(指定视图投影矩阵) + */ + void begin(const glm::mat4& viewProj); + + /** + * @brief 结束批处理 + */ + void end(); + + /** + * @brief 绘制精灵 + * @param texture 纹理 + * @param destRect 目标矩形 + * @param srcRect 源矩形(纹理坐标) + * @param color 颜色调制 + * @param rotation 旋转角度(度) + * @param anchor 锚点 + * @param blend 混合状态 + */ + void draw(Ptr texture, + const Rect& destRect, + const Rect& srcRect, + const Color& color = Colors::White, + float rotation = 0.0f, + const Vec2& anchor = Vec2(0.5f, 0.5f), + const rhi::BlendState& blend = rhi::BlendState::alphaBlend()); + + /** + * @brief 绘制精灵(使用变换矩阵) + */ + void draw(Ptr texture, + const Rect& srcRect, + const glm::mat4& transform, + const Color& color = Colors::White, + const rhi::BlendState& blend = rhi::BlendState::alphaBlend()); + + /** + * @brief 绘制精灵(使用渲染命令) + */ + void draw(const RenderCommand& command); + + /** + * @brief 绘制精灵序列 + */ + void drawBatch(const std::vector& sprites); + + /** + * @brief 设置视图投影矩阵 + */ + void setViewProjection(const glm::mat4& viewProj); + + /** + * @brief 获取当前视图投影矩阵 + */ + const glm::mat4& getViewProjection() const { return viewProj_; } + + /** + * @brief 获取统计信息 + */ + uint32_t getDrawCallCount() const { return drawCallCount_; } + uint32_t getSpriteCount() const { return spriteCount_; } + uint32_t getBatchCount() const { return batchCount_; } + + /** + * @brief 重置统计信息 + */ + void resetStats(); + + /** + * @brief 获取配置 + */ + const SpriteBatcherConfig& getConfig() const { return config_; } + + /** + * @brief 设置配置 + */ + void setConfig(const SpriteBatcherConfig& config); + + /** + * @brief 获取最大批次大小 + */ + uint32_t getMaxBatchSize() const { return config_.maxBatchSize; } + + /** + * @brief 获取 RHI 设备 + */ + Ptr getDevice() const { return device_; } + + /** + * @brief 获取着色器 + */ + Ptr getShader() const { return shader_; } + + /** + * @brief 设置自定义着色器 + */ + void setShader(Ptr shader); + +private: + /** + * @brief 创建着色器 + */ + bool createShader(); + + /** + * @brief 创建管线 + */ + bool createPipeline(); + + /** + * @brief 创建缓冲区 + */ + bool createBuffers(); + + /** + * @brief 刷新当前批次 + */ + void flushBatch(); + + /** + * @brief 检查是否可以添加到当前批次 + */ + bool canAddToBatch(Ptr texture, const rhi::BlendState& blend) const; + + /** + * @brief 添加精灵顶点 + */ + void addSpriteVertices(const Rect& destRect, + const Rect& srcRect, + const Color& color, + float rotation, + const Vec2& anchor); + + /** + * @brief 计算纹理坐标 + */ + Vec2 calcTexCoord(const Vec2& pos, uint32_t texWidth, uint32_t texHeight) const; + + /** + * @brief 计算旋转后的顶点 + */ + void rotatePoint(Vec2& point, float rotation, const Vec2& center) const; + + Ptr device_; + Ptr shader_; + Ptr pipeline_; + Ptr vertexBuffer_; + Ptr indexBuffer_; + Ptr uniformBuffer_; + + std::vector vertices_; + std::vector indices_; + std::vector batches_; + + SpriteBatcherConfig config_; + glm::mat4 viewProj_; + glm::mat4 currentTransform_; + + Ptr currentTexture_; + rhi::BlendState currentBlend_; + uint32_t currentVertexIndex_; + uint32_t currentIndexIndex_; + + uint32_t drawCallCount_; + uint32_t spriteCount_; + uint32_t batchCount_; + + bool initialized_; + bool inBatch_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/batch/text_batcher.h b/Extra2D/include/extra2d/render/batch/text_batcher.h new file mode 100644 index 0000000..6c89ab8 --- /dev/null +++ b/Extra2D/include/extra2d/render/batch/text_batcher.h @@ -0,0 +1,342 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 文本顶点结构 + */ +struct TextVertex { + Vec2 position; + Vec2 texCoord; + Color color; + + TextVertex() : position(), texCoord(), color(Colors::White) {} + + TextVertex(const Vec2& pos, const Vec2& uv, const Color& col) + : position(pos), texCoord(uv), color(col) {} +}; + +/** + * @brief 文本批次数据 + */ +struct TextBatch { + Ptr texture; + uint32_t startIndex; + uint32_t indexCount; + rhi::BlendState blendState; + + TextBatch() + : texture(nullptr), startIndex(0), indexCount(0), + blendState(rhi::BlendState::alphaBlend()) {} +}; + +/** + * @brief 文本对齐方式 + */ +enum class TextAlignment { + Left, + Center, + Right +}; + +/** + * @brief 文本垂直对齐方式 + */ +enum class TextVerticalAlignment { + Top, + Middle, + Bottom +}; + +/** + * @brief 文本绘制选项 + */ +struct TextDrawOptions { + Color color = Colors::White; + float scale = 1.0f; + TextAlignment alignment = TextAlignment::Left; + TextVerticalAlignment verticalAlignment = TextVerticalAlignment::Top; + float maxWidth = 0.0f; + float lineHeight = 0.0f; + bool wordWrap = false; + rhi::BlendState blend = rhi::BlendState::alphaBlend(); + + TextDrawOptions() = default; +}; + +/** + * @brief 文本批处理器配置 + */ +struct TextBatcherConfig { + uint32_t maxBatchSize; + bool enableSDF; + + TextBatcherConfig() + : maxBatchSize(5000), + enableSDF(true) {} +}; + +/** + * @brief 文本批处理器 + * + * 支持使用字体图集渲染文本 + * 自动缓存字形,支持动态加载 + */ +class TextBatcher { +public: + static constexpr uint32_t DEFAULT_MAX_BATCH_SIZE = 5000; + static constexpr uint32_t VERTEX_PER_GLYPH = 4; + static constexpr uint32_t INDEX_PER_GLYPH = 6; + + TextBatcher(); + explicit TextBatcher(const TextBatcherConfig& config); + ~TextBatcher(); + + /** + * @brief 初始化批处理器 + * @param device RHI 设备 + * @return 成功返回 true + */ + bool init(Ptr device); + + /** + * @brief 关闭批处理器 + */ + void shutdown(); + + /** + * @brief 检查是否已初始化 + */ + bool isInitialized() const { return initialized_; } + + /** + * @brief 开始批处理 + */ + void begin(); + + /** + * @brief 开始批处理(指定视图投影矩阵) + */ + void begin(const glm::mat4& viewProj); + + /** + * @brief 结束批处理 + */ + void end(); + + /** + * @brief 绘制文本 + * @param text 文本(UTF-32) + * @param position 位置 + * @param font 字体数据 + * @param fontSize 字体大小 + * @param options 绘制选项 + */ + void drawText(const std::u32string& text, + const Vec2& position, + FontData* font, + int fontSize, + const TextDrawOptions& options = {}); + + /** + * @brief 绘制文本(UTF-8) + */ + void drawTextUTF8(const std::string& text, + const Vec2& position, + FontData* font, + int fontSize, + const TextDrawOptions& options = {}); + + /** + * @brief 绘制文本(使用资源句柄) + */ + void drawText(const std::u32string& text, + const Vec2& position, + Ptr font, + int fontSize, + const TextDrawOptions& options = {}) { + drawText(text, position, font.get(), fontSize, options); + } + + /** + * @brief 绘制文本(使用资源句柄,UTF-8) + */ + void drawTextUTF8(const std::string& text, + const Vec2& position, + Ptr font, + int fontSize, + const TextDrawOptions& options = {}) { + drawTextUTF8(text, position, font.get(), fontSize, options); + } + + /** + * @brief 绘制渲染命令 + */ + void draw(const RenderCommand& command); + + /** + * @brief 计算文本尺寸 + */ + Size measureText(const std::u32string& text, + FontData* font, + int fontSize, + float scale = 1.0f); + + /** + * @brief 计算文本宽度 + */ + float measureTextWidth(const std::u32string& text, + FontData* font, + int fontSize, + float scale = 1.0f); + + /** + * @brief 计算文本尺寸(使用资源句柄) + */ + Size measureText(const std::u32string& text, + Ptr font, + int fontSize, + float scale = 1.0f) { + return measureText(text, font.get(), fontSize, scale); + } + + /** + * @brief 计算文本宽度(使用资源句柄) + */ + float measureTextWidth(const std::u32string& text, + Ptr font, + int fontSize, + float scale = 1.0f) { + return measureTextWidth(text, font.get(), fontSize, scale); + } + + /** + * @brief 设置视图投影矩阵 + */ + void setViewProjection(const glm::mat4& viewProj); + + /** + * @brief 获取当前视图投影矩阵 + */ + const glm::mat4& getViewProjection() const { return viewProj_; } + + /** + * @brief 获取统计信息 + */ + uint32_t getDrawCallCount() const { return drawCallCount_; } + uint32_t getGlyphCount() const { return glyphCount_; } + uint32_t getBatchCount() const { return batchCount_; } + + /** + * @brief 重置统计信息 + */ + void resetStats(); + + /** + * @brief 获取配置 + */ + const TextBatcherConfig& getConfig() const { return config_; } + + /** + * @brief 设置配置 + */ + void setConfig(const TextBatcherConfig& config); + + /** + * @brief 获取 RHI 设备 + */ + Ptr getDevice() const { return device_; } + + /** + * @brief 获取着色器 + */ + Ptr getShader() const { return shader_; } + + /** + * @brief 设置自定义着色器 + */ + void setShader(Ptr shader); + +private: + /** + * @brief 创建着色器 + */ + bool createShader(); + + /** + * @brief 创建管线 + */ + bool createPipeline(); + + /** + * @brief 创建缓冲区 + */ + bool createBuffers(); + + /** + * @brief 刷新当前批次 + */ + void flushBatch(); + + /** + * @brief 检查是否可以添加到当前批次 + */ + bool canAddToBatch(Ptr texture, const rhi::BlendState& blend) const; + + /** + * @brief 添加字形顶点 + */ + void addGlyphVertices(const Vec2& position, + const GlyphInfo* glyph, + const Color& color, + float scale); + + /** + * @brief 获取字体图集 + */ + Ptr getFontAtlas(FontData* font, int fontSize); + + Ptr device_; + Ptr shader_; + Ptr pipeline_; + Ptr vertexBuffer_; + Ptr indexBuffer_; + Ptr uniformBuffer_; + + std::vector vertices_; + std::vector indices_; + std::vector batches_; + + TextBatcherConfig config_; + glm::mat4 viewProj_; + + Ptr currentTexture_; + rhi::BlendState currentBlend_; + uint32_t currentVertexIndex_; + uint32_t currentIndexIndex_; + + uint32_t drawCallCount_; + uint32_t glyphCount_; + uint32_t batchCount_; + + bool initialized_; + bool inBatch_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/core/render_command.h b/Extra2D/include/extra2d/render/core/render_command.h new file mode 100644 index 0000000..2f62a9b --- /dev/null +++ b/Extra2D/include/extra2d/render/core/render_command.h @@ -0,0 +1,225 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +namespace rhi { +class RHITexture; +} + +/** + * @brief 渲染命令类型枚举 + */ +enum class RenderCommandType : uint8_t { + None = 0, + Sprite, + Text, + Shape, + Custom +}; + +/** + * @brief 形状类型枚举 + */ +enum class ShapeType : uint8_t { + Line, + Rect, + FilledRect, + Circle, + FilledCircle, + Triangle, + FilledTriangle, + Polygon, + FilledPolygon +}; + +/** + * @brief 精灵渲染数据 + */ +struct SpriteRenderData { + Ptr texture; + Rect destRect; + Rect srcRect; + Color tint; + float rotation; + Vec2 anchor; + rhi::BlendState blend; + uint64_t sortKey; + + SpriteRenderData() + : texture(nullptr), destRect(), srcRect(), tint(Colors::White), + rotation(0.0f), anchor(0.0f, 0.0f), blend(rhi::BlendState::alphaBlend()), sortKey(0) {} + + SpriteRenderData(Ptr tex, const Rect& dest, const Rect& src, + const Color& t, float rot, const Vec2& anc, + const rhi::BlendState& b = rhi::BlendState::alphaBlend(), + uint64_t key = 0) + : texture(tex), destRect(dest), srcRect(src), tint(t), + rotation(rot), anchor(anc), blend(b), sortKey(key) {} +}; + +/** + * @brief 文本渲染数据 + */ +struct TextRenderData { + std::u32string text; + Vec2 position; + Color color; + float fontSize; + std::string fontName; + rhi::BlendState blend; + + TextRenderData() + : text(), position(), color(Colors::White), + fontSize(16.0f), fontName(), blend(rhi::BlendState::alphaBlend()) {} +}; + +/** + * @brief 形状渲染数据 + */ +struct ShapeRenderData { + ShapeType shapeType; + std::vector points; + Color color; + float lineWidth; + float radius; + int segments; + rhi::BlendState blend; + + ShapeRenderData() + : shapeType(ShapeType::Rect), points(), color(Colors::White), + lineWidth(1.0f), radius(0.0f), segments(32), + blend(rhi::BlendState::alphaBlend()) {} +}; + +/** + * @brief 自定义渲染数据 + */ +struct CustomRenderData { + uint32_t callbackId; + void* userData; + rhi::BlendState blend; + + CustomRenderData() + : callbackId(0), userData(nullptr), blend(rhi::BlendState::alphaBlend()) {} +}; + +/** + * @brief 统一渲染命令结构 + */ +struct RenderCommand { + RenderCommandType type; + int layer; + int zOrder; + glm::mat4 transform; + std::variant data; + + RenderCommand() + : type(RenderCommandType::None), layer(0), zOrder(0), transform(1.0f) {} + + /** + * @brief 创建精灵渲染命令 + */ + static RenderCommand makeSprite(Ptr texture, + const Rect& destRect, + const Rect& srcRect, + const Color& tint = Colors::White, + float rotation = 0.0f, + const Vec2& anchor = Vec2(0.0f, 0.0f), + const rhi::BlendState& blend = rhi::BlendState::alphaBlend(), + int layer = 0, + int zOrder = 0); + + /** + * @brief 创建文本渲染命令 + */ + static RenderCommand makeText(const std::u32string& text, + const Vec2& position, + const Color& color = Colors::White, + float fontSize = 16.0f, + const std::string& fontName = "", + int layer = 0, + int zOrder = 0); + + /** + * @brief 创建线条渲染命令 + */ + static RenderCommand makeLine(const Vec2& start, + const Vec2& end, + const Color& color = Colors::White, + float width = 1.0f, + int layer = 0, + int zOrder = 0); + + /** + * @brief 创建矩形渲染命令 + */ + static RenderCommand makeRect(const Rect& rect, + const Color& color = Colors::White, + float width = 1.0f, + bool filled = false, + int layer = 0, + int zOrder = 0); + + /** + * @brief 创建圆形渲染命令 + */ + static RenderCommand makeCircle(const Vec2& center, + float radius, + const Color& color = Colors::White, + int segments = 32, + bool filled = false, + int layer = 0, + int zOrder = 0); + + /** + * @brief 创建三角形渲染命令 + */ + static RenderCommand makeTriangle(const Vec2& p1, + const Vec2& p2, + const Vec2& p3, + const Color& color = Colors::White, + bool filled = false, + int layer = 0, + int zOrder = 0); + + /** + * @brief 创建多边形渲染命令 + */ + static RenderCommand makePolygon(const std::vector& points, + const Color& color = Colors::White, + bool filled = false, + int layer = 0, + int zOrder = 0); + + /** + * @brief 创建自定义渲染命令 + */ + static RenderCommand makeCustom(uint32_t callbackId, + void* userData = nullptr, + const rhi::BlendState& blend = rhi::BlendState::alphaBlend(), + int layer = 0, + int zOrder = 0); + + /** + * @brief 计算排序键 + */ + uint64_t calculateSortKey() const; +}; + +/** + * @brief 渲染命令比较器 + */ +struct RenderCommandComparator { + bool operator()(const RenderCommand& a, const RenderCommand& b) const; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/core/render_context.h b/Extra2D/include/extra2d/render/core/render_context.h new file mode 100644 index 0000000..69ab206 --- /dev/null +++ b/Extra2D/include/extra2d/render/core/render_context.h @@ -0,0 +1,219 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +class IWindow; + +/** + * @brief 渲染统计信息 + */ +struct RenderStats { + uint32_t drawCalls; + uint32_t batchCount; + uint32_t vertexCount; + uint32_t triangleCount; + uint32_t spriteCount; + uint32_t textCount; + uint32_t shapeCount; + float frameTime; + float renderTime; + + RenderStats() + : drawCalls(0), batchCount(0), vertexCount(0), triangleCount(0), + spriteCount(0), textCount(0), shapeCount(0), frameTime(0.0f), renderTime(0.0f) {} + + void reset() { + drawCalls = 0; + batchCount = 0; + vertexCount = 0; + triangleCount = 0; + spriteCount = 0; + textCount = 0; + shapeCount = 0; + frameTime = 0.0f; + renderTime = 0.0f; + } +}; + +/** + * @brief 渲染上下文配置 + */ +struct RenderContextConfig { + uint32_t maxBatchSize; + bool enableBatching; + bool enableScissor; + bool enableStats; + rhi::GraphicsAPI preferredAPI; + + RenderContextConfig() + : maxBatchSize(10000), + enableBatching(true), + enableScissor(true), + enableStats(true), + preferredAPI(rhi::GraphicsAPI::OpenGL) {} +}; + +/** + * @brief 渲染上下文 + * + * 管理渲染状态、命令队列和 RHI 设备 + */ +class RenderContext { +public: + static constexpr uint32_t DEFAULT_MAX_BATCH_SIZE = 10000; + + RenderContext(); + explicit RenderContext(const RenderContextConfig& config); + ~RenderContext(); + + /** + * @brief 初始化渲染上下文 + */ + bool init(IWindow* window); + bool init(IWindow* window, rhi::GraphicsAPI api); + + /** + * @brief 关闭渲染上下文 + */ + void shutdown(); + + /** + * @brief 检查是否有效 + */ + bool isValid() const; + + /** + * @brief 开始帧 + */ + void beginFrame(); + + /** + * @brief 结束帧 + */ + void endFrame(); + + /** + * @brief 呈现帧 + */ + void present(); + + /** + * @brief 清除屏幕 + */ + void clear(const Color& color = Colors::Black); + void clear(const Color& color, float depth, uint8_t stencil = 0); + + /** + * @brief 获取/设置视口 + */ + void setViewport(const rhi::Viewport& viewport); + void setViewport(float x, float y, float width, float height); + rhi::Viewport getViewport() const; + + /** + * @brief 获取/设置裁剪区域 + */ + void setScissorRect(const rhi::ScissorRect& rect); + void setScissorRect(int32_t x, int32_t y, uint32_t width, uint32_t height); + void setScissorEnabled(bool enabled); + rhi::ScissorRect getScissorRect() const; + + /** + * @brief 变换矩阵栈操作 + */ + void pushTransform(const glm::mat4& transform); + void popTransform(); + void resetTransform(); + glm::mat4 getCurrentTransform() const; + + /** + * @brief 混合状态操作 + */ + void setBlendState(const rhi::BlendState& blend); + rhi::BlendState getBlendState() const; + void pushBlendState(const rhi::BlendState& blend); + void popBlendState(); + + /** + * @brief 渲染队列操作 + */ + RenderQueue& getQueue() { return queue_; } + const RenderQueue& getQueue() const { return queue_; } + void clearQueue(); + + /** + * @brief 提交渲染队列 + */ + void flushQueue(); + + /** + * @brief 获取 RHI 设备 + */ + Ptr getDevice() const { return device_; } + + /** + * @brief 获取窗口 + */ + IWindow* getWindow() const { return window_; } + + /** + * @brief 获取统计信息 + */ + const RenderStats& getStats() const { return stats_; } + void resetStats(); + + /** + * @brief 获取配置 + */ + const RenderContextConfig& getConfig() const { return config_; } + void setConfig(const RenderContextConfig& config); + + /** + * @brief 获取帧缓冲尺寸 + */ + uint32_t getFramebufferWidth() const { return fbWidth_; } + uint32_t getFramebufferHeight() const { return fbHeight_; } + + /** + * @brief 设置垂直同步 + */ + void setVSync(bool enabled); + bool isVSyncEnabled() const; + + /** + * @brief 设置默认帧缓冲尺寸 + */ + void setDefaultFramebufferSize(uint32_t width, uint32_t height); + +private: + void initDefaultState(); + + Ptr device_; + IWindow* window_; + RenderQueue queue_; + RenderStats stats_; + RenderContextConfig config_; + + std::stack transformStack_; + std::stack blendStack_; + rhi::BlendState currentBlend_; + rhi::Viewport viewport_; + rhi::ScissorRect scissorRect_; + bool scissorEnabled_; + + uint32_t fbWidth_; + uint32_t fbHeight_; + bool initialized_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/core/render_queue.h b/Extra2D/include/extra2d/render/core/render_queue.h new file mode 100644 index 0000000..1350ba9 --- /dev/null +++ b/Extra2D/include/extra2d/render/core/render_queue.h @@ -0,0 +1,168 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 渲染队列排序模式 + */ +enum class RenderSortMode { + None, + BackToFront, + FrontToBack, + ByTexture, + ByLayer, + Custom +}; + +/** + * @brief 渲染队列统计信息 + */ +struct RenderQueueStats { + uint32_t totalCommands; + uint32_t spriteCommands; + uint32_t textCommands; + uint32_t shapeCommands; + uint32_t customCommands; + uint32_t batchCount; + uint32_t drawCalls; + + RenderQueueStats() + : totalCommands(0), spriteCommands(0), textCommands(0), + shapeCommands(0), customCommands(0), batchCount(0), drawCalls(0) {} + + void reset() { + totalCommands = 0; + spriteCommands = 0; + textCommands = 0; + shapeCommands = 0; + customCommands = 0; + batchCount = 0; + drawCalls = 0; + } +}; + +/** + * @brief 渲染队列 + * + * 收集、排序和管理渲染命令 + */ +class RenderQueue { +public: + using CustomSortFunc = std::function; + + static constexpr size_t DEFAULT_CAPACITY = 1024; + static constexpr size_t MAX_CAPACITY = 65536; + + RenderQueue(); + explicit RenderQueue(size_t initialCapacity); + ~RenderQueue(); + + /** + * @brief 添加渲染命令 + */ + void addCommand(const RenderCommand& cmd); + void addCommand(RenderCommand&& cmd); + + /** + * @brief 批量添加渲染命令 + */ + void addCommands(const std::vector& commands); + + /** + * @brief 原地构造渲染命令 + */ + template + RenderCommand& emplaceCommand(Args&&... args) { + commands_.emplace_back(std::forward(args)...); + stats_.totalCommands++; + updateStatsForType(commands_.back().type); + return commands_.back(); + } + + /** + * @brief 排序渲染命令 + */ + void sort(RenderSortMode mode = RenderSortMode::ByTexture); + void sortCustom(CustomSortFunc sortFunc); + + /** + * @brief 清空队列 + */ + void clear(); + + /** + * @brief 预分配空间 + */ + void reserve(size_t capacity); + + /** + * @brief 获取命令列表 + */ + const std::vector& getCommands() const { return commands_; } + std::vector& getCommands() { return commands_; } + + /** + * @brief 获取统计信息 + */ + const RenderQueueStats& getStats() const { return stats_; } + + /** + * @brief 获取命令数量 + */ + size_t size() const { return commands_.size(); } + bool empty() const { return commands_.empty(); } + size_t capacity() const { return commands_.capacity(); } + + /** + * @brief 设置排序模式 + */ + void setSortMode(RenderSortMode mode) { sortMode_ = mode; } + RenderSortMode getSortMode() const { return sortMode_; } + + /** + * @brief 过滤命令 + */ + template + void filter(Predicate pred) { + auto it = std::remove_if(commands_.begin(), commands_.end(), + [&pred](const RenderCommand& cmd) { return !pred(cmd); }); + commands_.erase(it, commands_.end()); + recalculateStats(); + } + + /** + * @brief 按类型获取命令 + */ + std::vector getCommandsByType(RenderCommandType type) const; + + /** + * @brief 按层级获取命令 + */ + std::vector getCommandsByLayer(int layer) const; + + /** + * @brief 合并队列 + */ + void merge(const RenderQueue& other); + void merge(RenderQueue&& other); + +private: + void updateStatsForType(RenderCommandType type); + void recalculateStats(); + void sortBackToFront(); + void sortFrontToBack(); + void sortByTexture(); + void sortByLayer(); + + std::vector commands_; + RenderQueueStats stats_; + RenderSortMode sortMode_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/core/render_stats.h b/Extra2D/include/extra2d/render/core/render_stats.h new file mode 100644 index 0000000..0267d46 --- /dev/null +++ b/Extra2D/include/extra2d/render/core/render_stats.h @@ -0,0 +1,149 @@ +#pragma once + +#include +#include + +namespace extra2d { + +/** + * @brief 渲染统计信息 + */ +struct RenderStatsData { + uint32_t drawCalls; + uint32_t batchCount; + uint32_t vertexCount; + uint32_t triangleCount; + uint32_t spriteCount; + uint32_t textCount; + uint32_t shapeCount; + uint32_t textureBinds; + uint32_t shaderBinds; + uint32_t bufferBinds; + float frameTimeMs; + float renderTimeMs; + float fps; + + RenderStatsData() + : drawCalls(0), batchCount(0), vertexCount(0), triangleCount(0), + spriteCount(0), textCount(0), shapeCount(0), textureBinds(0), + shaderBinds(0), bufferBinds(0), frameTimeMs(0.0f), renderTimeMs(0.0f), fps(0.0f) {} + + void reset() { + drawCalls = 0; + batchCount = 0; + vertexCount = 0; + triangleCount = 0; + spriteCount = 0; + textCount = 0; + shapeCount = 0; + textureBinds = 0; + shaderBinds = 0; + bufferBinds = 0; + frameTimeMs = 0.0f; + renderTimeMs = 0.0f; + fps = 0.0f; + } +}; + +/** + * @brief 渲染统计收集器 + */ +class RenderStatsCollector { +public: + RenderStatsCollector(); + ~RenderStatsCollector() = default; + + /** + * @brief 开始帧计时 + */ + void beginFrame(); + + /** + * @brief 结束帧计时 + */ + void endFrame(); + + /** + * @brief 开始渲染计时 + */ + void beginRender(); + + /** + * @brief 结束渲染计时 + */ + void endRender(); + + /** + * @brief 记录绘制调用 + */ + void recordDrawCall(uint32_t vertexCount = 0, uint32_t triangleCount = 0); + + /** + * @brief 记录批处理 + */ + void recordBatch(); + + /** + * @brief 记录精灵 + */ + void recordSprite(uint32_t count = 1); + + /** + * @brief 记录文本 + */ + void recordText(uint32_t count = 1); + + /** + * @brief 记录形状 + */ + void recordShape(uint32_t count = 1); + + /** + * @brief 记录纹理绑定 + */ + void recordTextureBind(); + + /** + * @brief 记录着色器绑定 + */ + void recordShaderBind(); + + /** + * @brief 记录缓冲区绑定 + */ + void recordBufferBind(); + + /** + * @brief 获取统计信息 + */ + const RenderStatsData& getStats() const { return stats_; } + + /** + * @brief 重置统计 + */ + void reset(); + + /** + * @brief 获取平均帧时间 + */ + float getAverageFrameTime() const { return avgFrameTime_; } + + /** + * @brief 获取 FPS + */ + float getFPS() const { return fps_; } + +private: + using Clock = std::chrono::high_resolution_clock; + using TimePoint = std::chrono::time_point; + + RenderStatsData stats_; + TimePoint frameStartTime_; + TimePoint renderStartTime_; + float frameTimeAccum_; + uint32_t frameCount_; + float avgFrameTime_; + float fps_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/rhi/rhi_buffer.h b/Extra2D/include/extra2d/render/rhi/rhi_buffer.h new file mode 100644 index 0000000..97ea802 --- /dev/null +++ b/Extra2D/include/extra2d/render/rhi/rhi_buffer.h @@ -0,0 +1,184 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { +namespace rhi { + +/** + * @brief RHI 缓冲区接口 + * + * 抽象 GPU 缓冲区,支持顶点、索引、Uniform 等类型 + */ +class RHIBuffer { +public: + virtual ~RHIBuffer() = default; + + /** + * @brief 获取缓冲区类型 + */ + virtual BufferType getType() const = 0; + + /** + * @brief 获取缓冲区大小(字节) + */ + virtual size_t getSize() const = 0; + + /** + * @brief 获取缓冲区使用方式 + */ + virtual BufferUsage getUsage() const = 0; + + /** + * @brief 上传数据到缓冲区 + * @param data 数据指针 + * @param size 数据大小 + * @param offset 偏移量 + */ + virtual void setData(const void* data, size_t size, size_t offset = 0) = 0; + + /** + * @brief 读取缓冲区数据 + * @param data 目标缓冲区 + * @param size 读取大小 + * @param offset 偏移量 + */ + virtual void getData(void* data, size_t size, size_t offset = 0) const = 0; + + /** + * @brief 映射缓冲区到内存 + * @param offset 偏移量 + * @param size 映射大小 + * @param read 是否可读 + * @param write 是否可写 + * @return 映射的内存指针 + */ + virtual void* map(size_t offset, size_t size, bool read = false, bool write = true) = 0; + + /** + * @brief 取消映射缓冲区 + */ + virtual void unmap() = 0; + + /** + * @brief 检查缓冲区是否有效 + */ + virtual bool isValid() const = 0; + + /** + * @brief 获取原生句柄 + */ + virtual uintptr_t getNativeHandle() const = 0; + + /** + * @brief 获取调试名称 + */ + virtual const std::string& getDebugName() const = 0; + + /** + * @brief 设置调试名称 + */ + virtual void setDebugName(const std::string& name) = 0; +}; + +/** + * @brief 顶点缓冲区便捷类 + */ +class RHIVertexBuffer : public RHIBuffer { +public: + BufferType getType() const override { return BufferType::Vertex; } + + /** + * @brief 设置顶点数据 + * @tparam T 顶点类型 + * @param vertices 顶点数组 + */ + template + void setVertices(const std::vector& vertices) { + setData(vertices.data(), vertices.size() * sizeof(T)); + } + + /** + * @brief 获取顶点数量 + * @param stride 顶点步长 + */ + uint32_t getVertexCount(uint32_t stride) const { + return static_cast(getSize() / stride); + } +}; + +/** + * @brief 索引缓冲区便捷类 + */ +class RHIIndexBuffer : public RHIBuffer { +public: + BufferType getType() const override { return BufferType::Index; } + + /** + * @brief 设置 16 位索引数据 + */ + void setIndices16(const std::vector& indices) { + setData(indices.data(), indices.size() * sizeof(uint16_t)); + indexFormat_ = IndexFormat::UInt16; + } + + /** + * @brief 设置 32 位索引数据 + */ + void setIndices32(const std::vector& indices) { + setData(indices.data(), indices.size() * sizeof(uint32_t)); + indexFormat_ = IndexFormat::UInt32; + } + + /** + * @brief 获取索引格式 + */ + IndexFormat getIndexFormat() const { return indexFormat_; } + + /** + * @brief 获取索引数量 + */ + uint32_t getIndexCount() const { + return static_cast(getSize() / getIndexFormatSize(indexFormat_)); + } + +protected: + IndexFormat indexFormat_ = IndexFormat::UInt16; +}; + +/** + * @brief Uniform 缓冲区便捷类 + */ +class RHIUniformBuffer : public RHIBuffer { +public: + BufferType getType() const override { return BufferType::Uniform; } + + /** + * @brief 设置单个值 + * @tparam T 值类型 + * @param value 值 + * @param offset 偏移量 + */ + template + void setValue(const T& value, size_t offset = 0) { + setData(&value, sizeof(T), offset); + } + + /** + * @brief 获取单个值 + * @tparam T 值类型 + * @param offset 偏移量 + */ + template + T getValue(size_t offset = 0) const { + T value; + getData(&value, sizeof(T), offset); + return value; + } +}; + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/rhi/rhi_device.h b/Extra2D/include/extra2d/render/rhi/rhi_device.h new file mode 100644 index 0000000..a564019 --- /dev/null +++ b/Extra2D/include/extra2d/render/rhi/rhi_device.h @@ -0,0 +1,481 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +class IWindow; + +namespace rhi { + +/** + * @brief RHI 设备能力信息 + */ +struct DeviceCaps { + GraphicsAPI api = GraphicsAPI::OpenGL; + std::string apiVersion; + std::string deviceName; + std::string vendorName; + uint32_t maxTextureSize = 0; + uint32_t maxTextureUnits = 0; + uint32_t maxVertexAttributes = 0; + uint32_t maxUniformBufferBindings = 0; + uint32_t maxColorAttachments = 0; + float maxAnisotropy = 1.0f; + bool supportsCompute = false; + bool supportsGeometry = false; + bool supportsTessellation = false; + bool supportsInstancing = false; + bool supportsIndirectDraw = false; + bool supportsMultiDrawIndirect = false; + bool supportsPersistentMapping = false; + bool supportsBindlessTextures = false; +}; + +/** + * @brief RHI 设备统计信息 + */ +struct DeviceStats { + uint32_t drawCalls = 0; + uint32_t triangleCount = 0; + uint32_t vertexCount = 0; + uint32_t textureBinds = 0; + uint32_t shaderBinds = 0; + uint32_t bufferBinds = 0; + uint32_t framebufferBinds = 0; + uint64_t textureMemory = 0; + uint64_t bufferMemory = 0; +}; + +/** + * @brief RHI 设备接口 + * + * 核心渲染硬件接口抽象层 + * 提供资源创建、渲染命令提交等功能 + */ +class RHIDevice { +public: + virtual ~RHIDevice() = default; + + // ======================================================================== + // 生命周期 + // ======================================================================== + + /** + * @brief 初始化设备 + * @param window 窗口接口 + * @return 成功返回 true + */ + virtual bool init(IWindow* window) = 0; + + /** + * @brief 关闭设备 + */ + virtual void shutdown() = 0; + + /** + * @brief 检查设备是否有效 + */ + virtual bool isValid() const = 0; + + /** + * @brief 获取图形 API 类型 + */ + virtual GraphicsAPI getAPI() const = 0; + + /** + * @brief 获取设备能力 + */ + virtual const DeviceCaps& getCaps() const = 0; + + // ======================================================================== + // 帧管理 + // ======================================================================== + + /** + * @brief 开始帧 + */ + virtual void beginFrame() = 0; + + /** + * @brief 结束帧 + */ + virtual void endFrame() = 0; + + /** + * @brief 呈现帧 + */ + virtual void present() = 0; + + /** + * @brief 设置垂直同步 + */ + virtual void setVSync(bool enabled) = 0; + + /** + * @brief 获取垂直同步状态 + */ + virtual bool isVSyncEnabled() const = 0; + + /** + * @brief 设置默认帧缓冲尺寸 + */ + virtual void setDefaultFramebufferSize(uint32_t width, uint32_t height) = 0; + + // ======================================================================== + // 资源创建 - 缓冲区 + // ======================================================================== + + /** + * @brief 创建缓冲区 + */ + virtual Ptr createBuffer(const BufferDesc& desc) = 0; + + /** + * @brief 创建顶点缓冲区 + */ + virtual Ptr createVertexBuffer(size_t size, BufferUsage usage = BufferUsage::Static, + const void* data = nullptr) = 0; + + /** + * @brief 创建索引缓冲区 + */ + virtual Ptr createIndexBuffer(size_t size, BufferUsage usage = BufferUsage::Static, + const void* data = nullptr) = 0; + + /** + * @brief 创建 Uniform 缓冲区 + */ + virtual Ptr createUniformBuffer(size_t size, BufferUsage usage = BufferUsage::Dynamic) = 0; + + // ======================================================================== + // 资源创建 - 纹理 + // ======================================================================== + + /** + * @brief 创建纹理 + */ + virtual Ptr createTexture(const TextureDesc& desc) = 0; + + /** + * @brief 创建 2D 纹理 + */ + virtual Ptr createTexture2D(uint32_t width, uint32_t height, Format format, + const void* data = nullptr) = 0; + + /** + * @brief 创建采样器 + */ + virtual Ptr createSampler(const SamplerDesc& desc) = 0; + + /** + * @brief 创建纹理视图 + */ + virtual Ptr createTextureView(Ptr texture, + const TextureViewDesc& desc) = 0; + + // ======================================================================== + // 资源创建 - 着色器 + // ======================================================================== + + /** + * @brief 创建着色器 + */ + virtual Ptr createShader(const ShaderDesc& desc) = 0; + + /** + * @brief 从源码创建着色器 + */ + virtual Ptr createShaderFromSource(const std::string& name, + const std::string& vertexSource, + const std::string& fragmentSource) = 0; + + /** + * @brief 从文件创建着色器 + */ + virtual Ptr createShaderFromFile(const std::string& vertexPath, + const std::string& fragmentPath) = 0; + + // ======================================================================== + // 资源创建 - 管线 + // ======================================================================== + + /** + * @brief 创建渲染管线 + */ + virtual Ptr createPipeline(const PipelineDesc& desc, Ptr shader) = 0; + + /** + * @brief 使用构建器创建渲染管线 + */ + virtual Ptr createPipeline(const PipelineBuilder& builder) = 0; + + // ======================================================================== + // 资源创建 - 帧缓冲 + // ======================================================================== + + /** + * @brief 创建帧缓冲 + */ + virtual Ptr createFramebuffer(const FramebufferDesc& desc) = 0; + + /** + * @brief 使用构建器创建帧缓冲 + */ + virtual Ptr createFramebuffer(const FramebufferBuilder& builder) = 0; + + // ======================================================================== + // 渲染状态设置 + // ======================================================================== + + /** + * @brief 设置视口 + */ + virtual void setViewport(const Viewport& viewport) = 0; + + /** + * @brief 设置视口(简化版) + */ + void setViewport(float x, float y, float width, float height, + float minDepth = 0.0f, float maxDepth = 1.0f) { + setViewport(Viewport(x, y, width, height, minDepth, maxDepth)); + } + + /** + * @brief 设置裁剪矩形 + */ + virtual void setScissorRect(const ScissorRect& rect) = 0; + + /** + * @brief 设置裁剪矩形(简化版) + */ + void setScissorRect(int32_t x, int32_t y, uint32_t width, uint32_t height) { + setScissorRect(ScissorRect(x, y, width, height)); + } + + /** + * @brief 启用/禁用裁剪测试 + */ + virtual void setScissorEnabled(bool enabled) = 0; + + // ======================================================================== + // 渲染命令 - 绑定资源 + // ======================================================================== + + /** + * @brief 绑定管线 + */ + virtual void setPipeline(Ptr pipeline) = 0; + + /** + * @brief 绑定着色器 + */ + virtual void setShader(Ptr shader) = 0; + + /** + * @brief 绑定顶点缓冲区 + */ + virtual void setVertexBuffer(uint32_t slot, Ptr buffer, uint32_t offset = 0) = 0; + + /** + * @brief 绑定索引缓冲区 + */ + virtual void setIndexBuffer(Ptr buffer, IndexFormat format) = 0; + + /** + * @brief 绑定纹理 + */ + virtual void setTexture(uint32_t slot, Ptr texture, Ptr sampler = nullptr) = 0; + + /** + * @brief 绑定 Uniform 缓冲区 + */ + virtual void setUniformBuffer(uint32_t slot, Ptr buffer) = 0; + + /** + * @brief 绑定帧缓冲 + */ + virtual void setFramebuffer(Ptr framebuffer) = 0; + + /** + * @brief 解绑帧缓冲(恢复默认) + */ + virtual void setDefaultFramebuffer() = 0; + + // ======================================================================== + // 渲染命令 - Uniform 设置 + // ======================================================================== + + /** + * @brief 设置浮点 Uniform + */ + virtual void setUniformFloat(const std::string& name, float value) = 0; + + /** + * @brief 设置向量 Uniform + */ + virtual void setUniformVec2(const std::string& name, const glm::vec2& value) = 0; + virtual void setUniformVec3(const std::string& name, const glm::vec3& value) = 0; + virtual void setUniformVec4(const std::string& name, const glm::vec4& value) = 0; + + /** + * @brief 设置矩阵 Uniform + */ + virtual void setUniformMat4(const std::string& name, const glm::mat4& value) = 0; + + /** + * @brief 设置整数 Uniform + */ + virtual void setUniformInt(const std::string& name, int value) = 0; + + // ======================================================================== + // 渲染命令 - 绘制 + // ======================================================================== + + /** + * @brief 绘制 + */ + virtual void draw(const DrawCommand& cmd) = 0; + + /** + * @brief 绘制(简化版) + */ + void draw(uint32_t vertexCount, uint32_t firstVertex = 0, uint32_t instanceCount = 1) { + DrawCommand cmd; + cmd.vertexCount = vertexCount; + cmd.firstVertex = firstVertex; + cmd.instanceCount = instanceCount; + draw(cmd); + } + + /** + * @brief 索引绘制 + */ + virtual void drawIndexed(const DrawIndexedCommand& cmd) = 0; + + /** + * @brief 索引绘制(简化版) + */ + void drawIndexed(uint32_t indexCount, uint32_t firstIndex = 0, int32_t vertexOffset = 0, + uint32_t instanceCount = 1) { + DrawIndexedCommand cmd; + cmd.indexCount = indexCount; + cmd.firstIndex = firstIndex; + cmd.vertexOffset = vertexOffset; + cmd.instanceCount = instanceCount; + drawIndexed(cmd); + } + + /** + * @brief 间接绘制 + */ + virtual void drawIndirect(Ptr indirectBuffer, size_t offset) = 0; + + /** + * @brief 索引间接绘制 + */ + virtual void drawIndexedIndirect(Ptr indirectBuffer, size_t offset) = 0; + + // ======================================================================== + // 渲染命令 - 清除 + // ======================================================================== + + /** + * @brief 清除颜色缓冲 + */ + virtual void clearColor(const ColorValue& color) = 0; + + /** + * @brief 清除深度缓冲 + */ + virtual void clearDepth(float depth = 1.0f) = 0; + + /** + * @brief 清除模板缓冲 + */ + virtual void clearStencil(uint8_t stencil = 0) = 0; + + /** + * @brief 清除所有缓冲 + */ + virtual void clear(const ColorValue& color, float depth = 1.0f, uint8_t stencil = 0) = 0; + + // ======================================================================== + // 统计与调试 + // ======================================================================== + + /** + * @brief 获取统计信息 + */ + virtual const DeviceStats& getStats() const = 0; + + /** + * @brief 重置统计信息 + */ + virtual void resetStats() = 0; + + /** + * @brief 检查错误 + */ + virtual bool checkError() = 0; + + /** + * @brief 获取调试名称 + */ + virtual const std::string& getDebugName() const = 0; + + /** + * @brief 设置调试名称 + */ + virtual void setDebugName(const std::string& name) = 0; + + // ======================================================================== + // 静态工厂方法 + // ======================================================================== + + /** + * @brief 创建 RHI 设备 + * @param api 图形 API 类型 + * @return 设备实例 + */ + static Ptr create(GraphicsAPI api); +}; + +/** + * @brief RHI 设备作用域绑定器 + */ +class RHIDeviceScope { +public: + explicit RHIDeviceScope(Ptr device) : device_(device) { + if (device_) { + device_->beginFrame(); + } + } + + ~RHIDeviceScope() { + if (device_) { + device_->endFrame(); + } + } + + RHIDeviceScope(const RHIDeviceScope&) = delete; + RHIDeviceScope& operator=(const RHIDeviceScope&) = delete; + + RHIDevice* operator->() { return device_.get(); } + RHIDevice* get() { return device_.get(); } + +private: + Ptr device_; +}; + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/rhi/rhi_framebuffer.h b/Extra2D/include/extra2d/render/rhi/rhi_framebuffer.h new file mode 100644 index 0000000..d2f4262 --- /dev/null +++ b/Extra2D/include/extra2d/render/rhi/rhi_framebuffer.h @@ -0,0 +1,337 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { +namespace rhi { + +/** + * @brief RHI 帧缓冲接口 + * + * 抽象渲染目标,支持多渲染目标(MRT) + */ +class RHIFramebuffer { +public: + virtual ~RHIFramebuffer() = default; + + /** + * @brief 绑定帧缓冲 + */ + virtual void bind() = 0; + + /** + * @brief 解绑帧缓冲 + */ + virtual void unbind() = 0; + + /** + * @brief 获取帧缓冲宽度 + */ + virtual uint32_t getWidth() const = 0; + + /** + * @brief 获取帧缓冲高度 + */ + virtual uint32_t getHeight() const = 0; + + /** + * @brief 获取颜色附件数量 + */ + virtual uint32_t getColorAttachmentCount() const = 0; + + /** + * @brief 获取颜色附件纹理 + * @param index 附件索引 + */ + virtual Ptr getColorAttachment(uint32_t index = 0) const = 0; + + /** + * @brief 获取深度模板附件纹理 + */ + virtual Ptr getDepthStencilAttachment() const = 0; + + /** + * @brief 检查是否有深度附件 + */ + virtual bool hasDepthAttachment() const = 0; + + /** + * @brief 检查是否有模板附件 + */ + virtual bool hasStencilAttachment() const = 0; + + /** + * @brief 检查帧缓冲是否完整 + */ + virtual bool isComplete() const = 0; + + /** + * @brief 检查帧缓冲是否有效 + */ + virtual bool isValid() const = 0; + + /** + * @brief 获取原生句柄 + */ + virtual uintptr_t getNativeHandle() const = 0; + + /** + * @brief 获取调试名称 + */ + virtual const std::string& getDebugName() const = 0; + + /** + * @brief 设置调试名称 + */ + virtual void setDebugName(const std::string& name) = 0; + + /** + * @brief 获取完整描述 + */ + virtual FramebufferDesc getDesc() const = 0; + + // ======================================================================== + // 清除操作 + // ======================================================================== + + /** + * @brief 清除颜色附件 + * @param index 附件索引 + * @param color 清除颜色 + */ + virtual void clearColor(uint32_t index, const ColorValue& color) = 0; + + /** + * @brief 清除深度缓冲 + * @param depth 深度值 + */ + virtual void clearDepth(float depth = 1.0f) = 0; + + /** + * @brief 清除模板缓冲 + * @param stencil 模板值 + */ + virtual void clearStencil(uint8_t stencil = 0) = 0; + + /** + * @brief 清除深度和模板缓冲 + */ + virtual void clearDepthStencil(float depth = 1.0f, uint8_t stencil = 0) = 0; + + /** + * @brief 清除所有附件 + */ + virtual void clearAll(const ColorValue& color, float depth = 1.0f, uint8_t stencil = 0) = 0; + + // ======================================================================== + // Blit 操作 + // ======================================================================== + + /** + * @brief 将帧缓冲内容复制到另一个帧缓冲 + * @param dst 目标帧缓冲 + * @param srcRect 源矩形 + * @param dstRect 目标矩形 + * @param mask 要复制的缓冲区掩码 + * @param filter 过滤模式 + */ + virtual void blitTo(RHIFramebuffer* dst, + const ScissorRect& srcRect, const ScissorRect& dstRect, + bool color = true, bool depth = false, bool stencil = false, + FilterMode filter = FilterMode::Nearest) = 0; + + /** + * @brief 将帧缓冲内容复制到屏幕 + */ + virtual void blitToScreen(const ScissorRect& srcRect, const ScissorRect& dstRect, + FilterMode filter = FilterMode::Nearest) = 0; +}; + +/** + * @brief 渲染通道描述 + */ +struct RenderPassDesc { + struct ColorAttachment { + Ptr framebuffer; + ColorValue clearColor = ColorValue::black(); + bool load = false; + bool store = true; + }; + + struct DepthStencilAttachment { + Ptr framebuffer; + float clearDepth = 1.0f; + uint8_t clearStencil = 0; + bool loadDepth = false; + bool storeDepth = true; + bool loadStencil = false; + bool storeStencil = true; + }; + + std::vector colorAttachments; + DepthStencilAttachment depthStencilAttachment; +}; + +/** + * @brief RHI 渲染通道接口 + * + * 表示一次渲染过程,管理渲染目标的状态 + */ +class RHIRenderPass { +public: + virtual ~RHIRenderPass() = default; + + /** + * @brief 开始渲染通道 + */ + virtual void begin() = 0; + + /** + * @brief 结束渲染通道 + */ + virtual void end() = 0; + + /** + * @brief 检查渲染通道是否正在进行 + */ + virtual bool isActive() const = 0; + + /** + * @brief 获取渲染通道描述 + */ + virtual const RenderPassDesc& getDesc() const = 0; + + /** + * @brief 获取当前帧缓冲 + */ + virtual Ptr getFramebuffer() const = 0; +}; + +/** + * @brief 帧缓冲构建器 + */ +class FramebufferBuilder { +public: + FramebufferBuilder& setSize(uint32_t width, uint32_t height) { + width_ = width; + height_ = height; + return *this; + } + + FramebufferBuilder& addColorAttachment(Format format, uint32_t sampleCount = 1) { + AttachmentDesc desc; + desc.format = format; + desc.sampleCount = sampleCount; + desc.isDepthStencil = false; + colorAttachments_.push_back(desc); + return *this; + } + + FramebufferBuilder& addColorTexture(Ptr texture) { + colorTextures_.push_back(texture); + return *this; + } + + FramebufferBuilder& setDepthStencilAttachment(Format format, uint32_t sampleCount = 1) { + depthStencilAttachment_.format = format; + depthStencilAttachment_.sampleCount = sampleCount; + depthStencilAttachment_.isDepthStencil = true; + hasDepthStencil_ = true; + return *this; + } + + FramebufferBuilder& setDepthTexture(Ptr texture) { + depthTexture_ = texture; + return *this; + } + + FramebufferBuilder& setDebugName(const std::string& name) { + debugName_ = name; + return *this; + } + + uint32_t getWidth() const { return width_; } + uint32_t getHeight() const { return height_; } + const std::vector& getColorAttachments() const { return colorAttachments_; } + const AttachmentDesc& getDepthStencilAttachment() const { return depthStencilAttachment_; } + bool hasDepthStencil() const { return hasDepthStencil_; } + const std::string& getDebugName() const { return debugName_; } + const std::vector>& getColorTextures() const { return colorTextures_; } + Ptr getDepthTexture() const { return depthTexture_; } + + FramebufferDesc build() const { + FramebufferDesc desc; + desc.width = width_; + desc.height = height_; + desc.colorAttachments = colorAttachments_; + if (hasDepthStencil_) { + desc.depthStencilAttachment = depthStencilAttachment_; + } + desc.debugName = debugName_; + return desc; + } + +private: + uint32_t width_ = 0; + uint32_t height_ = 0; + std::vector colorAttachments_; + AttachmentDesc depthStencilAttachment_; + bool hasDepthStencil_ = false; + std::string debugName_; + std::vector> colorTextures_; + Ptr depthTexture_; +}; + +/** + * @brief 常用帧缓冲预设 + */ +namespace FramebufferPresets { + +/** + * @brief 创建标准颜色帧缓冲 + */ +inline FramebufferBuilder colorOnly(uint32_t width, uint32_t height) { + return FramebufferBuilder() + .setSize(width, height) + .addColorAttachment(Format::RGBA8_UNORM); +} + +/** + * @brief 创建带深度的帧缓冲 + */ +inline FramebufferBuilder withDepth(uint32_t width, uint32_t height) { + return FramebufferBuilder() + .setSize(width, height) + .addColorAttachment(Format::RGBA8_UNORM) + .setDepthStencilAttachment(Format::D24_UNORM_S8_UINT); +} + +/** + * @brief 创建 HDR 帧缓冲 + */ +inline FramebufferBuilder hdr(uint32_t width, uint32_t height) { + return FramebufferBuilder() + .setSize(width, height) + .addColorAttachment(Format::RGBA16_FLOAT) + .setDepthStencilAttachment(Format::D24_UNORM_S8_UINT); +} + +/** + * @brief 创建多目标帧缓冲(MRT) + */ +inline FramebufferBuilder mrt2(uint32_t width, uint32_t height) { + return FramebufferBuilder() + .setSize(width, height) + .addColorAttachment(Format::RGBA8_UNORM) + .addColorAttachment(Format::RGBA8_UNORM) + .setDepthStencilAttachment(Format::D24_UNORM_S8_UINT); +} + +} // namespace FramebufferPresets + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/rhi/rhi_pipeline.h b/Extra2D/include/extra2d/render/rhi/rhi_pipeline.h new file mode 100644 index 0000000..78c7c37 --- /dev/null +++ b/Extra2D/include/extra2d/render/rhi/rhi_pipeline.h @@ -0,0 +1,353 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { +namespace rhi { + +/** + * @brief RHI 渲染管线接口 + * + * 抽象 GPU 渲染管线状态对象(PSO) + * 封装着色器、混合状态、深度状态、光栅化状态等 + */ +class RHIPipeline { +public: + virtual ~RHIPipeline() = default; + + /** + * @brief 绑定管线 + */ + virtual void bind() = 0; + + /** + * @brief 解绑管线 + */ + virtual void unbind() = 0; + + // ======================================================================== + // 状态访问 + // ======================================================================== + + /** + * @brief 获取混合状态 + */ + virtual const BlendState& getBlendState() const = 0; + + /** + * @brief 获取深度模板状态 + */ + virtual const DepthStencilState& getDepthStencilState() const = 0; + + /** + * @brief 获取光栅化状态 + */ + virtual const RasterizerState& getRasterizerState() const = 0; + + /** + * @brief 获取图元类型 + */ + virtual PrimitiveType getPrimitiveType() const = 0; + + /** + * @brief 获取关联的着色器 + */ + virtual Ptr getShader() const = 0; + + // ======================================================================== + // 状态设置(动态状态) + // ======================================================================== + + /** + * @brief 设置混合模式 + */ + virtual void setBlendMode(BlendFactor srcFactor, BlendFactor dstFactor, + BlendOp op = BlendOp::Add) = 0; + + /** + * @brief 设置混合模式(预设) + */ + virtual void setBlendState(const BlendState& state) = 0; + + /** + * @brief 启用/禁用混合 + */ + virtual void setBlendEnabled(bool enabled) = 0; + + /** + * @brief 设置深度测试 + */ + virtual void setDepthTestEnabled(bool enabled) = 0; + + /** + * @brief 设置深度写入 + */ + virtual void setDepthWriteEnabled(bool enabled) = 0; + + /** + * @brief 设置深度比较函数 + */ + virtual void setDepthCompareFunc(CompareFunc func) = 0; + + /** + * @brief 设置裁剪模式 + */ + virtual void setCullMode(CullMode mode) = 0; + + /** + * @brief 设置正面方向 + */ + virtual void setFrontFace(FrontFace face) = 0; + + /** + * @brief 设置线框模式 + */ + virtual void setWireframe(bool enabled) = 0; + + // ======================================================================== + // 顶点布局 + // ======================================================================== + + /** + * @brief 获取顶点属性列表 + */ + virtual const std::vector& getVertexAttributes() const = 0; + + /** + * @brief 获取顶点缓冲区绑定 + */ + virtual const std::vector& getVertexBufferBindings() const = 0; + + /** + * @brief 计算顶点步长 + */ + virtual uint32_t calculateVertexStride() const = 0; + + // ======================================================================== + // 有效性检查 + // ======================================================================== + + /** + * @brief 检查管线是否有效 + */ + virtual bool isValid() const = 0; + + /** + * @brief 获取原生句柄 + */ + virtual uintptr_t getNativeHandle() const = 0; + + /** + * @brief 获取调试名称 + */ + virtual const std::string& getDebugName() const = 0; + + /** + * @brief 设置调试名称 + */ + virtual void setDebugName(const std::string& name) = 0; +}; + +/** + * @brief 管线构建器 + */ +class PipelineBuilder { +public: + PipelineBuilder& setShader(Ptr shader) { + shader_ = shader; + return *this; + } + + PipelineBuilder& setBlendState(const BlendState& state) { + blendState_ = state; + return *this; + } + + PipelineBuilder& setDepthStencilState(const DepthStencilState& state) { + depthStencilState_ = state; + return *this; + } + + PipelineBuilder& setRasterizerState(const RasterizerState& state) { + rasterizerState_ = state; + return *this; + } + + PipelineBuilder& setPrimitiveType(PrimitiveType type) { + primitiveType_ = type; + return *this; + } + + PipelineBuilder& addVertexAttribute(const VertexAttribute& attr) { + vertexAttributes_.push_back(attr); + return *this; + } + + PipelineBuilder& addVertexAttributes(const std::vector& attrs) { + vertexAttributes_.insert(vertexAttributes_.end(), attrs.begin(), attrs.end()); + return *this; + } + + PipelineBuilder& addVertexBufferBinding(const VertexBufferBinding& binding) { + vertexBindings_.push_back(binding); + return *this; + } + + PipelineBuilder& setColorFormat(Format format) { + colorFormat_ = format; + return *this; + } + + PipelineBuilder& setDepthStencilFormat(Format format) { + depthStencilFormat_ = format; + return *this; + } + + PipelineBuilder& setDebugName(const std::string& name) { + debugName_ = name; + return *this; + } + + PipelineBuilder& enableBlend(bool enabled = true) { + blendState_.enabled = enabled; + return *this; + } + + PipelineBuilder& enableDepthTest(bool enabled = true) { + depthStencilState_.depthTestEnabled = enabled; + return *this; + } + + PipelineBuilder& enableDepthWrite(bool enabled = true) { + depthStencilState_.depthWriteEnabled = enabled; + return *this; + } + + PipelineBuilder& setCullMode(CullMode mode) { + rasterizerState_.cullMode = mode; + return *this; + } + + Ptr getShader() const { return shader_; } + const BlendState& getBlendState() const { return blendState_; } + const DepthStencilState& getDepthStencilState() const { return depthStencilState_; } + const RasterizerState& getRasterizerState() const { return rasterizerState_; } + PrimitiveType getPrimitiveType() const { return primitiveType_; } + const std::vector& getVertexAttributes() const { return vertexAttributes_; } + const std::vector& getVertexBufferBindings() const { return vertexBindings_; } + Format getColorFormat() const { return colorFormat_; } + Format getDepthStencilFormat() const { return depthStencilFormat_; } + const std::string& getDebugName() const { return debugName_; } + + PipelineDesc build() const { + PipelineDesc desc; + desc.blendState = blendState_; + desc.depthStencilState = depthStencilState_; + desc.rasterizerState = rasterizerState_; + desc.primitiveType = primitiveType_; + desc.vertexAttributes = vertexAttributes_; + desc.vertexBindings = vertexBindings_; + desc.colorFormat = colorFormat_; + desc.depthStencilFormat = depthStencilFormat_; + desc.debugName = debugName_; + return desc; + } + +private: + Ptr shader_; + BlendState blendState_; + DepthStencilState depthStencilState_; + RasterizerState rasterizerState_; + PrimitiveType primitiveType_ = PrimitiveType::Triangles; + std::vector vertexAttributes_; + std::vector vertexBindings_; + Format colorFormat_ = Format::RGBA8_UNORM; + Format depthStencilFormat_ = Format::D24_UNORM_S8_UINT; + std::string debugName_; +}; + +/** + * @brief 输入布局描述 + */ +struct InputLayoutDesc { + std::vector attributes; + std::vector bindings; + + /** + * @brief 添加顶点属性 + */ + InputLayoutDesc& addAttribute(uint32_t location, Format format, uint32_t offset, + uint32_t bufferSlot = 0, bool normalized = false) { + attributes.emplace_back(location, format, offset, bufferSlot, normalized); + return *this; + } + + /** + * @brief 添加顶点缓冲区绑定 + */ + InputLayoutDesc& addBinding(uint32_t slot, uint32_t stride, uint32_t offset = 0, + uint32_t divisor = 0) { + bindings.emplace_back(slot, stride, offset, divisor); + return *this; + } +}; + +/** + * @brief 常用顶点布局 + */ +namespace VertexLayouts { + +/** + * @brief 位置 + 纹理坐标 + */ +inline InputLayoutDesc positionTexcoord() { + InputLayoutDesc desc; + desc.addAttribute(0, Format::RG32_FLOAT, 0); + desc.addAttribute(1, Format::RG32_FLOAT, 8); + desc.addBinding(0, 16); + return desc; +} + +/** + * @brief 位置 + 纹理坐标 + 颜色 + */ +inline InputLayoutDesc positionTexcoordColor() { + InputLayoutDesc desc; + desc.addAttribute(0, Format::RG32_FLOAT, 0); + desc.addAttribute(1, Format::RG32_FLOAT, 8); + desc.addAttribute(2, Format::RGBA8_UNORM, 16, 0, true); + desc.addBinding(0, 20); + return desc; +} + +/** + * @brief 位置 + 法线 + 纹理坐标 + */ +inline InputLayoutDesc positionNormalTexcoord() { + InputLayoutDesc desc; + desc.addAttribute(0, Format::RGB32_FLOAT, 0); + desc.addAttribute(1, Format::RGB32_FLOAT, 12); + desc.addAttribute(2, Format::RG32_FLOAT, 24); + desc.addBinding(0, 32); + return desc; +} + +/** + * @brief 位置 + 颜色 + */ +inline InputLayoutDesc positionColor() { + InputLayoutDesc desc; + desc.addAttribute(0, Format::RG32_FLOAT, 0); + desc.addAttribute(1, Format::RGBA8_UNORM, 8, 0, true); + desc.addBinding(0, 12); + return desc; +} + +} // namespace VertexLayouts + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/rhi/rhi_shader.h b/Extra2D/include/extra2d/render/rhi/rhi_shader.h new file mode 100644 index 0000000..fcf2659 --- /dev/null +++ b/Extra2D/include/extra2d/render/rhi/rhi_shader.h @@ -0,0 +1,301 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { +namespace rhi { + +/** + * @brief Uniform 变量信息 + */ +struct UniformInfo { + std::string name; + uint32_t location = 0; + uint32_t size = 0; + uint32_t offset = 0; + ShaderStage stage = ShaderStage::Vertex; +}; + +/** + * @brief 顶点属性信息 + */ +struct AttributeInfo { + std::string name; + uint32_t location = 0; + Format format = Format::RGBA32_FLOAT; + uint32_t offset = 0; +}; + +/** + * @brief RHI 着色器接口 + * + * 抽象 GPU 着色器程序 + */ +class RHIShader { +public: + virtual ~RHIShader() = default; + + /** + * @brief 绑定着色器 + */ + virtual void bind() = 0; + + /** + * @brief 解绑着色器 + */ + virtual void unbind() = 0; + + /** + * @brief 获取着色器名称 + */ + virtual const std::string& getName() const = 0; + + /** + * @brief 检查着色器是否有效 + */ + virtual bool isValid() const = 0; + + /** + * @brief 获取原生句柄 + */ + virtual uintptr_t getNativeHandle() const = 0; + + // ======================================================================== + // Uniform 设置方法 + // ======================================================================== + + /** + * @brief 设置布尔值 + */ + virtual void setBool(const std::string& name, bool value) = 0; + + /** + * @brief 设置整数值 + */ + virtual void setInt(const std::string& name, int value) = 0; + + /** + * @brief 设置无符号整数值 + */ + virtual void setUInt(const std::string& name, uint32_t value) = 0; + + /** + * @brief 设置浮点值 + */ + virtual void setFloat(const std::string& name, float value) = 0; + + /** + * @brief 设置二维向量 + */ + virtual void setVec2(const std::string& name, const glm::vec2& value) = 0; + + /** + * @brief 设置三维向量 + */ + virtual void setVec3(const std::string& name, const glm::vec3& value) = 0; + + /** + * @brief 设置四维向量 + */ + virtual void setVec4(const std::string& name, const glm::vec4& value) = 0; + + /** + * @brief 设置 2x2 矩阵 + */ + virtual void setMat2(const std::string& name, const glm::mat2& value) = 0; + + /** + * @brief 设置 3x3 矩阵 + */ + virtual void setMat3(const std::string& name, const glm::mat3& value) = 0; + + /** + * @brief 设置 4x4 矩阵 + */ + virtual void setMat4(const std::string& name, const glm::mat4& value) = 0; + + /** + * @brief 设置纹理采样器 + */ + virtual void setTexture(const std::string& name, uint32_t slot) = 0; + + /** + * @brief 设置整数数组 + */ + virtual void setIntArray(const std::string& name, const int* values, uint32_t count) = 0; + + /** + * @brief 设置浮点数组 + */ + virtual void setFloatArray(const std::string& name, const float* values, uint32_t count) = 0; + + /** + * @brief 设置向量数组 + */ + virtual void setVec2Array(const std::string& name, const glm::vec2* values, uint32_t count) = 0; + virtual void setVec3Array(const std::string& name, const glm::vec3* values, uint32_t count) = 0; + virtual void setVec4Array(const std::string& name, const glm::vec4* values, uint32_t count) = 0; + + /** + * @brief 设置矩阵数组 + */ + virtual void setMat4Array(const std::string& name, const glm::mat4* values, uint32_t count) = 0; + + // ======================================================================== + // Uniform 查询方法 + // ======================================================================== + + /** + * @brief 检查是否有指定的 Uniform + */ + virtual bool hasUniform(const std::string& name) const = 0; + + /** + * @brief 获取 Uniform 位置 + */ + virtual int getUniformLocation(const std::string& name) const = 0; + + /** + * @brief 获取所有 Uniform 信息 + */ + virtual const std::vector& getUniforms() const = 0; + + /** + * @brief 获取所有顶点属性信息 + */ + virtual const std::vector& getAttributes() const = 0; + + // ======================================================================== + // 便捷方法 + // ======================================================================== + + /** + * @brief 设置二维向量(分离参数) + */ + void setVec2(const std::string& name, float x, float y) { + setVec2(name, glm::vec2(x, y)); + } + + /** + * @brief 设置三维向量(分离参数) + */ + void setVec3(const std::string& name, float x, float y, float z) { + setVec3(name, glm::vec3(x, y, z)); + } + + /** + * @brief 设置四维向量(分离参数) + */ + void setVec4(const std::string& name, float x, float y, float z, float w) { + setVec4(name, glm::vec4(x, y, z, w)); + } + + /** + * @brief 设置颜色(RGBA) + */ + void setColor(const std::string& name, float r, float g, float b, float a = 1.0f) { + setVec4(name, glm::vec4(r, g, b, a)); + } +}; + +/** + * @brief 着色器模块接口 + * + * 表示单个着色器阶段(如顶点着色器、片段着色器) + */ +class RHIShaderModule { +public: + virtual ~RHIShaderModule() = default; + + /** + * @brief 获取着色器阶段 + */ + virtual ShaderStage getStage() const = 0; + + /** + * @brief 获取入口点名称 + */ + virtual const std::string& getEntryPoint() const = 0; + + /** + * @brief 检查模块是否有效 + */ + virtual bool isValid() const = 0; + + /** + * @brief 获取原生句柄 + */ + virtual uintptr_t getNativeHandle() const = 0; +}; + +/** + * @brief 着色器程序构建器 + */ +class ShaderProgramBuilder { +public: + ShaderProgramBuilder& setName(const std::string& name) { + name_ = name; + return *this; + } + + ShaderProgramBuilder& setVertexSource(const std::string& source) { + vertexSource_ = source; + return *this; + } + + ShaderProgramBuilder& setFragmentSource(const std::string& source) { + fragmentSource_ = source; + return *this; + } + + ShaderProgramBuilder& setGeometrySource(const std::string& source) { + geometrySource_ = source; + return *this; + } + + ShaderProgramBuilder& setBinaryData(const std::vector& data) { + binaryData_ = data; + return *this; + } + + ShaderProgramBuilder& addDefine(const std::string& name, const std::string& value = "") { + defines_[name] = value; + return *this; + } + + const std::string& getName() const { return name_; } + const std::string& getVertexSource() const { return vertexSource_; } + const std::string& getFragmentSource() const { return fragmentSource_; } + const std::string& getGeometrySource() const { return geometrySource_; } + const std::vector& getBinaryData() const { return binaryData_; } + const std::unordered_map& getDefines() const { return defines_; } + + ShaderDesc build() const { + ShaderDesc desc; + desc.name = name_; + desc.vertexSource = vertexSource_; + desc.fragmentSource = fragmentSource_; + desc.geometrySource = geometrySource_; + desc.binaryData = binaryData_; + return desc; + } + +private: + std::string name_; + std::string vertexSource_; + std::string fragmentSource_; + std::string geometrySource_; + std::vector binaryData_; + std::unordered_map defines_; +}; + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/rhi/rhi_texture.h b/Extra2D/include/extra2d/render/rhi/rhi_texture.h new file mode 100644 index 0000000..6f940c2 --- /dev/null +++ b/Extra2D/include/extra2d/render/rhi/rhi_texture.h @@ -0,0 +1,251 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { +namespace rhi { + +/** + * @brief RHI 纹理接口 + * + * 抽象 GPU 纹理资源,支持 2D、3D、立方体纹理等 + */ +class RHITexture { +public: + virtual ~RHITexture() = default; + + /** + * @brief 获取纹理类型 + */ + virtual TextureType getType() const = 0; + + /** + * @brief 获取纹理格式 + */ + virtual Format getFormat() const = 0; + + /** + * @brief 获取纹理宽度 + */ + virtual uint32_t getWidth() const = 0; + + /** + * @brief 获取纹理高度 + */ + virtual uint32_t getHeight() const = 0; + + /** + * @brief 获取纹理深度(3D 纹理) + */ + virtual uint32_t getDepth() const = 0; + + /** + * @brief 获取 Mip 级别数 + */ + virtual uint32_t getMipLevels() const = 0; + + /** + * @brief 获取数组层数 + */ + virtual uint32_t getArrayLayers() const = 0; + + /** + * @brief 获取采样数(多重采样纹理) + */ + virtual uint32_t getSampleCount() const = 0; + + /** + * @brief 上传纹理数据 + * @param data 数据指针 + * @param mipLevel Mip 级别 + * @param arrayLayer 数组层 + */ + virtual void setData(const void* data, uint32_t mipLevel = 0, uint32_t arrayLayer = 0) = 0; + + /** + * @brief 上传纹理子区域数据 + * @param data 数据指针 + * @param x X 偏移 + * @param y Y 偏移 + * @param width 宽度 + * @param height 高度 + * @param mipLevel Mip 级别 + */ + virtual void setSubData(const void* data, uint32_t x, uint32_t y, + uint32_t width, uint32_t height, uint32_t mipLevel = 0) = 0; + + /** + * @brief 生成 Mipmaps + */ + virtual void generateMipmaps() = 0; + + /** + * @brief 检查纹理是否有效 + */ + virtual bool isValid() const = 0; + + /** + * @brief 获取原生句柄 + */ + virtual uintptr_t getNativeHandle() const = 0; + + /** + * @brief 获取调试名称 + */ + virtual const std::string& getDebugName() const = 0; + + /** + * @brief 设置调试名称 + */ + virtual void setDebugName(const std::string& name) = 0; + + /** + * @brief 计算数据大小 + */ + size_t calculateDataSize(uint32_t mipLevel = 0) const { + uint32_t w = getWidth() >> mipLevel; + uint32_t h = getHeight() >> mipLevel; + if (w == 0) w = 1; + if (h == 0) h = 1; + return static_cast(w) * h * getFormatSize(getFormat()); + } +}; + +/** + * @brief RHI 采样器接口 + * + * 抽象纹理采样器状态 + */ +class RHISampler { +public: + virtual ~RHISampler() = default; + + /** + * @brief 获取最小化过滤模式 + */ + virtual FilterMode getMinFilter() const = 0; + + /** + * @brief 获取最大化过滤模式 + */ + virtual FilterMode getMagFilter() const = 0; + + /** + * @brief 获取 Mip 过滤模式 + */ + virtual FilterMode getMipFilter() const = 0; + + /** + * @brief 获取 U 方向寻址模式 + */ + virtual AddressMode getAddressModeU() const = 0; + + /** + * @brief 获取 V 方向寻址模式 + */ + virtual AddressMode getAddressModeV() const = 0; + + /** + * @brief 获取 W 方向寻址模式 + */ + virtual AddressMode getAddressModeW() const = 0; + + /** + * @brief 获取最大各向异性 + */ + virtual float getMaxAnisotropy() const = 0; + + /** + * @brief 获取边框颜色 + */ + virtual ColorValue getBorderColor() const = 0; + + /** + * @brief 检查采样器是否有效 + */ + virtual bool isValid() const = 0; + + /** + * @brief 获取原生句柄 + */ + virtual uintptr_t getNativeHandle() const = 0; + + /** + * @brief 获取调试名称 + */ + virtual const std::string& getDebugName() const = 0; + + /** + * @brief 设置调试名称 + */ + virtual void setDebugName(const std::string& name) = 0; +}; + +/** + * @brief 纹理视图描述 + */ +struct TextureViewDesc { + Format format = Format::Undefined; + uint32_t baseMipLevel = 0; + uint32_t mipLevels = 0; + uint32_t baseArrayLayer = 0; + uint32_t arrayLayers = 0; + TextureType viewType = TextureType::Texture2D; +}; + +/** + * @brief RHI 纹理视图接口 + * + * 用于创建纹理的不同视图 + */ +class RHITextureView { +public: + virtual ~RHITextureView() = default; + + /** + * @brief 获取源纹理 + */ + virtual Ptr getTexture() const = 0; + + /** + * @brief 获取视图格式 + */ + virtual Format getFormat() const = 0; + + /** + * @brief 获取基础 Mip 级别 + */ + virtual uint32_t getBaseMipLevel() const = 0; + + /** + * @brief 获取 Mip 级别数 + */ + virtual uint32_t getMipLevels() const = 0; + + /** + * @brief 获取基础数组层 + */ + virtual uint32_t getBaseArrayLayer() const = 0; + + /** + * @brief 获取数组层数 + */ + virtual uint32_t getArrayLayers() const = 0; + + /** + * @brief 检查视图是否有效 + */ + virtual bool isValid() const = 0; + + /** + * @brief 获取原生句柄 + */ + virtual uintptr_t getNativeHandle() const = 0; +}; + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/rhi/rhi_types.h b/Extra2D/include/extra2d/render/rhi/rhi_types.h new file mode 100644 index 0000000..123792c --- /dev/null +++ b/Extra2D/include/extra2d/render/rhi/rhi_types.h @@ -0,0 +1,685 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { +namespace rhi { + +/** + * @brief 图形 API 类型 + */ +enum class GraphicsAPI { + OpenGL, + Vulkan, + DirectX11, + DirectX12, + Metal, + WebGPU +}; + +/** + * @brief 纹理格式 + */ +enum class Format { + Undefined, + // 8-bit 无符号归一化 + R8_UNORM, + RG8_UNORM, + RGB8_UNORM, + RGBA8_UNORM, + // 8-bit 有符号归一化 + R8_SNORM, + RG8_SNORM, + RGB8_SNORM, + RGBA8_SNORM, + // 16-bit 浮点 + R16_FLOAT, + RG16_FLOAT, + RGBA16_FLOAT, + // 32-bit 浮点 + R32_FLOAT, + RG32_FLOAT, + RGB32_FLOAT, + RGBA32_FLOAT, + // 32-bit 整数 + R32_UINT, + RG32_UINT, + RGBA32_UINT, + // 深度/模板 + D16_UNORM, + D24_UNORM_S8_UINT, + D32_FLOAT, + D32_FLOAT_S8_UINT, + // 压缩格式 + BC1_RGB_UNORM, + BC1_RGBA_UNORM, + BC3_RGBA_UNORM, + BC5_RG_UNORM, + BC7_RGBA_UNORM, + ETC2_RGB8_UNORM, + ETC2_RGBA8_UNORM, + ASTC_4x4_UNORM, + ASTC_8x8_UNORM +}; + +/** + * @brief 缓冲区类型 + */ +enum class BufferType { + Vertex, + Index, + Uniform, + Storage, + Staging, + Indirect +}; + +/** + * @brief 缓冲区使用方式 + */ +enum class BufferUsage { + Static, + Dynamic, + Stream +}; + +/** + * @brief 图元类型 + */ +enum class PrimitiveType { + Points, + Lines, + LineStrip, + LineLoop, + Triangles, + TriangleStrip, + TriangleFan +}; + +/** + * @brief 混合因子 + */ +enum class BlendFactor { + Zero, + One, + SrcColor, + OneMinusSrcColor, + DstColor, + OneMinusDstColor, + SrcAlpha, + OneMinusSrcAlpha, + DstAlpha, + OneMinusDstAlpha, + ConstantColor, + OneMinusConstantColor, + ConstantAlpha, + OneMinusConstantAlpha, + SrcAlphaSaturate +}; + +/** + * @brief 混合操作 + */ +enum class BlendOp { + Add, + Subtract, + ReverseSubtract, + Min, + Max +}; + +/** + * @brief 比较函数 + */ +enum class CompareFunc { + Never, + Less, + Equal, + LessEqual, + Greater, + NotEqual, + GreaterEqual, + Always +}; + +/** + * @brief 裁剪模式 + */ +enum class CullMode { + None, + Front, + Back +}; + +/** + * @brief 正面方向 + */ +enum class FrontFace { + CounterClockwise, + Clockwise +}; + +/** + * @brief 纹理过滤模式 + */ +enum class FilterMode { + Nearest, + Linear, + NearestMipmapNearest, + LinearMipmapNearest, + NearestMipmapLinear, + LinearMipmapLinear +}; + +/** + * @brief 纹理寻址模式 + */ +enum class AddressMode { + Repeat, + MirroredRepeat, + ClampToEdge, + ClampToBorder, + MirrorClampToEdge +}; + +/** + * @brief 纹理类型 + */ +enum class TextureType { + Texture2D, + Texture2DArray, + Texture3D, + TextureCube, + TextureCubeArray +}; + +/** + * @brief 索引格式 + */ +enum class IndexFormat { + UInt16, + UInt32 +}; + +/** + * @brief 混合状态 + */ +struct BlendState { + bool enabled = true; + BlendFactor srcColorFactor = BlendFactor::SrcAlpha; + BlendFactor dstColorFactor = BlendFactor::OneMinusSrcAlpha; + BlendOp colorOp = BlendOp::Add; + BlendFactor srcAlphaFactor = BlendFactor::One; + BlendFactor dstAlphaFactor = BlendFactor::OneMinusSrcAlpha; + BlendOp alphaOp = BlendOp::Add; + + /** + * @brief 创建不透明混合状态 + */ + static BlendState opaque() { + BlendState state; + state.enabled = false; + return state; + } + + /** + * @brief 创建标准 Alpha 混合状态 + */ + static BlendState alphaBlend() { + return BlendState{}; + } + + /** + * @brief 创建加法混合状态 + */ + static BlendState additive() { + BlendState state; + state.srcColorFactor = BlendFactor::SrcAlpha; + state.dstColorFactor = BlendFactor::One; + state.srcAlphaFactor = BlendFactor::One; + state.dstAlphaFactor = BlendFactor::One; + return state; + } + + /** + * @brief 创建乘法混合状态 + */ + static BlendState multiply() { + BlendState state; + state.srcColorFactor = BlendFactor::DstColor; + state.dstColorFactor = BlendFactor::OneMinusSrcAlpha; + state.srcAlphaFactor = BlendFactor::DstAlpha; + state.dstAlphaFactor = BlendFactor::OneMinusSrcAlpha; + return state; + } + + /** + * @brief 创建预乘 Alpha 混合状态 + */ + static BlendState premultiplied() { + BlendState state; + state.srcColorFactor = BlendFactor::One; + state.dstColorFactor = BlendFactor::OneMinusSrcAlpha; + state.srcAlphaFactor = BlendFactor::One; + state.dstAlphaFactor = BlendFactor::OneMinusSrcAlpha; + return state; + } +}; + +/** + * @brief 深度模板状态 + */ +struct DepthStencilState { + bool depthTestEnabled = false; + bool depthWriteEnabled = true; + CompareFunc depthCompareFunc = CompareFunc::Less; + bool stencilEnabled = false; + uint8_t stencilReadMask = 0xFF; + uint8_t stencilWriteMask = 0xFF; + uint8_t stencilRef = 0; + + /** + * @brief 创建禁用深度测试的状态 + */ + static DepthStencilState disabled() { + return DepthStencilState{}; + } + + /** + * @brief 创建标准深度测试状态 + */ + static DepthStencilState defaultDepth() { + DepthStencilState state; + state.depthTestEnabled = true; + state.depthWriteEnabled = true; + return state; + } + + /** + * @brief 创建只读深度测试状态 + */ + static DepthStencilState readOnly() { + DepthStencilState state; + state.depthTestEnabled = true; + state.depthWriteEnabled = false; + return state; + } +}; + +/** + * @brief 光栅化状态 + */ +struct RasterizerState { + CullMode cullMode = CullMode::None; + FrontFace frontFace = FrontFace::CounterClockwise; + bool wireframe = false; + float lineWidth = 1.0f; + + /** + * @brief 创建无裁剪状态 + */ + static RasterizerState noCull() { + return RasterizerState{}; + } + + /** + * @brief 创建背面裁剪状态 + */ + static RasterizerState cullBack() { + RasterizerState state; + state.cullMode = CullMode::Back; + return state; + } + + /** + * @brief 创建正面裁剪状态 + */ + static RasterizerState cullFront() { + RasterizerState state; + state.cullMode = CullMode::Front; + return state; + } +}; + +/** + * @brief 顶点属性 + */ +struct VertexAttribute { + uint32_t location = 0; + Format format = Format::RGBA32_FLOAT; + uint32_t offset = 0; + uint32_t bufferSlot = 0; + bool normalized = false; + + VertexAttribute() = default; + + VertexAttribute(uint32_t loc, Format fmt, uint32_t off, uint32_t slot = 0, bool norm = false) + : location(loc), format(fmt), offset(off), bufferSlot(slot), normalized(norm) {} +}; + +/** + * @brief 顶点缓冲区绑定 + */ +struct VertexBufferBinding { + uint32_t slot = 0; + uint32_t stride = 0; + uint32_t offset = 0; + uint32_t divisor = 0; + + VertexBufferBinding() = default; + + VertexBufferBinding(uint32_t s, uint32_t str, uint32_t off = 0, uint32_t div = 0) + : slot(s), stride(str), offset(off), divisor(div) {} +}; + +/** + * @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, float minD = 0.0f, float maxD = 1.0f) + : x(x_), y(y_), width(w), height(h), minDepth(minD), maxDepth(maxD) {} +}; + +/** + * @brief 裁剪矩形 + */ +struct ScissorRect { + int32_t x = 0; + int32_t y = 0; + uint32_t width = 0; + uint32_t height = 0; + + ScissorRect() = default; + + ScissorRect(int32_t x_, int32_t y_, uint32_t w, uint32_t h) + : x(x_), y(y_), width(w), height(h) {} +}; + +/** + * @brief 颜色 + */ +struct ColorValue { + float r = 0.0f; + float g = 0.0f; + float b = 0.0f; + float a = 1.0f; + + ColorValue() = default; + + ColorValue(float r_, float g_, float b_, float a_ = 1.0f) + : r(r_), g(g_), b(b_), a(a_) {} + + static ColorValue black() { return ColorValue(0.0f, 0.0f, 0.0f, 1.0f); } + static ColorValue white() { return ColorValue(1.0f, 1.0f, 1.0f, 1.0f); } + static ColorValue red() { return ColorValue(1.0f, 0.0f, 0.0f, 1.0f); } + static ColorValue green() { return ColorValue(0.0f, 1.0f, 0.0f, 1.0f); } + static ColorValue blue() { return ColorValue(0.0f, 0.0f, 1.0f, 1.0f); } + static ColorValue transparent() { return ColorValue(0.0f, 0.0f, 0.0f, 0.0f); } +}; + +/** + * @brief 缓冲区描述 + */ +struct BufferDesc { + BufferType type = BufferType::Vertex; + BufferUsage usage = BufferUsage::Static; + size_t size = 0; + const void* data = nullptr; + std::string debugName; +}; + +/** + * @brief 纹理描述 + */ +struct TextureDesc { + TextureType type = TextureType::Texture2D; + Format format = Format::RGBA8_UNORM; + uint32_t width = 0; + uint32_t height = 0; + uint32_t depth = 1; + uint32_t mipLevels = 1; + uint32_t arrayLayers = 1; + uint32_t sampleCount = 1; + const void* data = nullptr; + std::string debugName; + + /** + * @brief 创建 2D 纹理描述 + */ + static TextureDesc texture2D(uint32_t w, uint32_t h, Format fmt, const void* data = nullptr) { + TextureDesc desc; + desc.type = TextureType::Texture2D; + desc.format = fmt; + desc.width = w; + desc.height = h; + desc.data = data; + return desc; + } + + /** + * @brief创建立方体纹理描述 + */ + static TextureDesc textureCube(uint32_t size, Format fmt) { + TextureDesc desc; + desc.type = TextureType::TextureCube; + desc.format = fmt; + desc.width = size; + desc.height = size; + desc.arrayLayers = 6; + return desc; + } +}; + +/** + * @brief 采样器描述 + */ +struct SamplerDesc { + FilterMode minFilter = FilterMode::Linear; + FilterMode magFilter = FilterMode::Linear; + FilterMode mipFilter = FilterMode::LinearMipmapLinear; + AddressMode addressU = AddressMode::ClampToEdge; + AddressMode addressV = AddressMode::ClampToEdge; + AddressMode addressW = AddressMode::ClampToEdge; + float maxAnisotropy = 1.0f; + float minLOD = 0.0f; + float maxLOD = 1000.0f; + float lodBias = 0.0f; + ColorValue borderColor = ColorValue::black(); + bool compareEnabled = false; + CompareFunc compareFunc = CompareFunc::Less; + + /** + * @brief 创建点采样器 + */ + static SamplerDesc point() { + SamplerDesc desc; + desc.minFilter = FilterMode::Nearest; + desc.magFilter = FilterMode::Nearest; + desc.mipFilter = FilterMode::Nearest; + return desc; + } + + /** + * @brief 创建线性采样器 + */ + static SamplerDesc linear() { + return SamplerDesc{}; + } + + /** + * @brief 创建三线性采样器 + */ + static SamplerDesc trilinear() { + SamplerDesc desc; + desc.minFilter = FilterMode::LinearMipmapLinear; + desc.magFilter = FilterMode::Linear; + desc.mipFilter = FilterMode::LinearMipmapLinear; + return desc; + } + + /** + * @brief 创建重复采样器 + */ + static SamplerDesc repeat() { + SamplerDesc desc; + desc.addressU = AddressMode::Repeat; + desc.addressV = AddressMode::Repeat; + desc.addressW = AddressMode::Repeat; + return desc; + } +}; + +/** + * @brief 着色器阶段 + */ +enum class ShaderStage { + Vertex, + Fragment, + Geometry, + TessControl, + TessEvaluation, + Compute +}; + +/** + * @brief 着色器描述 + */ +struct ShaderDesc { + std::string name; + std::string vertexSource; + std::string fragmentSource; + std::string geometrySource; + std::vector binaryData; + std::string entryPoint = "main"; +}; + +/** + * @brief 管线描述 + */ +struct PipelineDesc { + BlendState blendState; + DepthStencilState depthStencilState; + RasterizerState rasterizerState; + PrimitiveType primitiveType = PrimitiveType::Triangles; + std::vector vertexAttributes; + std::vector vertexBindings; + Format colorFormat = Format::RGBA8_UNORM; + Format depthStencilFormat = Format::D24_UNORM_S8_UINT; + std::string debugName; +}; + +/** + * @brief 绘制命令 + */ +struct DrawCommand { + uint32_t vertexCount = 0; + uint32_t instanceCount = 1; + uint32_t firstVertex = 0; + uint32_t firstInstance = 0; +}; + +/** + * @brief 索引绘制命令 + */ +struct DrawIndexedCommand { + uint32_t indexCount = 0; + uint32_t instanceCount = 1; + uint32_t firstIndex = 0; + int32_t vertexOffset = 0; + uint32_t firstInstance = 0; +}; + +/** + * @brief 帧缓冲附件描述 + */ +struct AttachmentDesc { + Format format = Format::RGBA8_UNORM; + uint32_t sampleCount = 1; + bool isDepthStencil = false; + + AttachmentDesc() = default; + + AttachmentDesc(Format fmt, uint32_t samples = 1, bool depthStencil = false) + : format(fmt), sampleCount(samples), isDepthStencil(depthStencil) {} +}; + +/** + * @brief 帧缓冲描述 + */ +struct FramebufferDesc { + uint32_t width = 0; + uint32_t height = 0; + std::vector colorAttachments; + AttachmentDesc depthStencilAttachment; + std::string debugName; +}; + +/** + * @brief 获取格式字节大小 + */ +inline uint32_t getFormatSize(Format format) { + switch (format) { + case Format::R8_UNORM: + case Format::R8_SNORM: + return 1; + case Format::RG8_UNORM: + case Format::RG8_SNORM: + case Format::R16_FLOAT: + return 2; + case Format::RGB8_UNORM: + case Format::RGB8_SNORM: + return 3; + case Format::RGBA8_UNORM: + case Format::RGBA8_SNORM: + case Format::RG16_FLOAT: + case Format::R32_FLOAT: + case Format::R32_UINT: + return 4; + case Format::RGBA16_FLOAT: + case Format::RG32_FLOAT: + case Format::RG32_UINT: + return 8; + case Format::RGBA32_FLOAT: + case Format::RGBA32_UINT: + return 16; + case Format::D16_UNORM: + return 2; + case Format::D24_UNORM_S8_UINT: + return 4; + case Format::D32_FLOAT: + return 4; + case Format::D32_FLOAT_S8_UINT: + return 8; + default: + return 0; + } +} + +/** + * @brief 获取索引格式字节大小 + */ +inline uint32_t getIndexFormatSize(IndexFormat format) { + switch (format) { + case IndexFormat::UInt16: + return 2; + case IndexFormat::UInt32: + return 4; + default: + return 0; + } +} + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/include/extra2d/resources/font_atlas.h b/Extra2D/include/extra2d/resources/font_atlas.h new file mode 100644 index 0000000..651369e --- /dev/null +++ b/Extra2D/include/extra2d/resources/font_atlas.h @@ -0,0 +1,207 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 字形信息结构 + */ +struct GlyphInfo { + uint32_t codepoint = 0; + float width = 0; + float height = 0; + float bearingX = 0; + float bearingY = 0; + float advance = 0; + float u0 = 0, v0 = 0; + float u1 = 0, v1 = 0; + bool isSDF = false; +}; + +/** + * @brief 字体图集描述 + */ +struct FontAtlasDesc { + std::string filepath; + std::vector fontData; + int fontSize = 16; + bool useSDF = false; + int atlasWidth = 1024; + int atlasHeight = 1024; + int padding = 2; + float sdfEdgeValue = 0.5f; + float sdfPixelDistScale = 4.0f; +}; + +/** + * @brief 字体图集 + * + * 使用 stb_truetype 解析字体,stb_rect_pack 进行图集打包 + * 支持 Unicode 字符集,动态扩展,SDF 渲染 + */ +class FontAtlas { +public: + FontAtlas(); + ~FontAtlas(); + + /** + * @brief 初始化字体图集 + * @param device RHI 设备 + * @param desc 字体图集描述 + * @return 成功返回 true + */ + bool init(Ptr device, const FontAtlasDesc& desc); + + /** + * @brief 关闭字体图集 + */ + void shutdown(); + + /** + * @brief 检查是否有效 + */ + bool isValid() const { return valid_; } + + /** + * @brief 获取字形信息 + * @param codepoint Unicode 码点 + * @return 字形信息指针,未找到返回 nullptr + */ + const GlyphInfo* getGlyph(uint32_t codepoint); + + /** + * @brief 预加载字符 + * @param chars 字符列表(UTF-32) + * @return 成功加载的字符数 + */ + int preloadChars(const std::u32string& chars); + + /** + * @brief 预加载字符(UTF-8) + * @param chars 字符列表(UTF-8) + * @return 成功加载的字符数 + */ + int preloadCharsUTF8(const std::string& chars); + + /** + * @brief 获取纹理 + */ + Ptr getTexture() const { return texture_; } + + /** + * @brief 获取采样器 + */ + Ptr getSampler() const { return sampler_; } + + /** + * @brief 获取字体大小 + */ + int getFontSize() const { return fontSize_; } + + /** + * @brief 获取行高 + */ + float getLineHeight() const { return lineHeight_; } + + /** + * @brief 获取上升高度 + */ + float getAscent() const { return ascent_; } + + /** + * @brief 获取下降高度 + */ + float getDescent() const { return descent_; } + + /** + * @brief 是否使用 SDF + */ + bool isSDF() const { return useSDF_; } + + /** + * @brief 计算文本宽度 + * @param text 文本(UTF-32) + * @param scale 缩放比例 + * @return 文本宽度 + */ + float measureText(const std::u32string& text, float scale = 1.0f); + + /** + * @brief 计算文本尺寸 + * @param text 文本(UTF-32) + * @param scale 缩放比例 + * @return 文本尺寸 + */ + Size measureTextSize(const std::u32string& text, float scale = 1.0f); + + /** + * @brief 获取已缓存字形数量 + */ + size_t getGlyphCount() const { return glyphs_.size(); } + + /** + * @brief 获取图集使用率 + */ + float getUsageRatio() const; + +private: + /** + * @brief 缓存单个字形 + */ + bool cacheGlyph(uint32_t codepoint); + + /** + * @brief 更新纹理区域 + */ + void updateTextureRegion(int x, int y, int width, int height, const uint8_t* data); + + /** + * @brief 初始化打包上下文 + */ + bool initPackContext(); + + /** + * @brief 在图集中分配空间 + */ + bool allocateRect(int width, int height, int& outX, int& outY); + + Ptr device_; + Ptr texture_; + Ptr sampler_; + + std::vector fontData_; + std::vector atlasData_; + std::unordered_map glyphs_; + + int atlasWidth_ = 0; + int atlasHeight_ = 0; + int fontSize_ = 0; + int padding_ = 2; + bool useSDF_ = false; + bool valid_ = false; + + float lineHeight_ = 0; + float ascent_ = 0; + float descent_ = 0; + float sdfEdgeValue_ = 0.5f; + float sdfPixelDistScale_ = 4.0f; + + void* stbFontInfo_ = nullptr; + void* stbPackContext_ = nullptr; + std::vector packNodes_; + std::mutex mutex_; + + int usedArea_ = 0; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/resources/font_manager.h b/Extra2D/include/extra2d/resources/font_manager.h new file mode 100644 index 0000000..7224aac --- /dev/null +++ b/Extra2D/include/extra2d/resources/font_manager.h @@ -0,0 +1,240 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 字体数据结构 + */ +struct FontData { + std::string name; + std::string path; + std::vector fontFileData; + std::unordered_map> atlasBySize; + int defaultFontSize = 16; + bool useSDF = false; + size_t memorySize = 0; +}; + +/** + * @brief 字体加载选项 + */ +struct FontLoadOptions { + int fontSize = 16; + bool useSDF = false; + int atlasWidth = 1024; + int atlasHeight = 1024; + int padding = 2; + float sdfEdgeValue = 0.5f; + float sdfPixelDistScale = 4.0f; + std::u32string preloadChars; +}; + +/** + * @brief 字体管理器 + * + * 管理多个字体,支持不同字号和 SDF 渲染 + */ +class FontManager : public BaseResourceManager { +public: + /** + * @brief 获取单例实例 + */ + static FontManager& getInstance(); + + /** + * @brief 初始化字体管理器 + * @param device RHI 设备 + * @return 成功返回 true + */ + bool init(Ptr device); + + /** + * @brief 关闭字体管理器 + */ + void shutdown(); + + /** + * @brief 检查是否已初始化 + */ + bool isInitialized() const { return initialized_; } + + /** + * @brief 从文件加载字体 + * @param path 字体文件路径 + * @param options 加载选项 + * @return 字体句柄 + */ + ResourceHandle load(const std::string& path, + const FontLoadOptions& options = {}); + + /** + * @brief 从内存数据加载字体 + * @param name 字体名称 + * @param data 字体数据 + * @param options 加载选项 + * @return 字体句柄 + */ + ResourceHandle loadFromMemory(const std::string& name, + const std::vector& data, + const FontLoadOptions& options = {}); + + /** + * @brief 获取字体 + * @param path 字体路径或名称 + * @return 字体句柄 + */ + ResourceHandle get(const std::string& path); + + /** + * @brief 检查字体是否存在 + * @param path 字体路径 + */ + bool has(const std::string& path) const; + + /** + * @brief 移除字体 + * @param path 字体路径 + */ + void remove(const std::string& path); + + /** + * @brief 清除所有字体 + */ + void clear(); + + /** + * @brief 获取字体的图集 + * @param font 字体数据 + * @param fontSize 字体大小 + * @return 字体图集 + */ + Ptr getAtlas(Ptr font, int fontSize); + + /** + * @brief 获取字体的图集(原始指针版本) + */ + Ptr getAtlas(FontData* font, int fontSize); + + /** + * @brief 获取字体的图集(句柄版本) + */ + Ptr getAtlas(const FontHandle& font, int fontSize) { + return getAtlas(font.get(), fontSize); + } + + /** + * @brief 获取字形信息 + * @param font 字体数据 + * @param fontSize 字体大小 + * @param codepoint Unicode 码点 + * @return 字形信息 + */ + const GlyphInfo* getGlyph(Ptr font, int fontSize, uint32_t codepoint); + + /** + * @brief 获取字形信息(原始指针版本) + */ + const GlyphInfo* getGlyph(FontData* font, int fontSize, uint32_t codepoint); + + /** + * @brief 获取字形信息(句柄版本) + */ + const GlyphInfo* getGlyph(const FontHandle& font, int fontSize, uint32_t codepoint) { + return getGlyph(font.get(), fontSize, codepoint); + } + + /** + * @brief 计算文本宽度 + * @param font 字体数据 + * @param fontSize 字体大小 + * @param text 文本(UTF-32) + * @return 文本宽度 + */ + float measureText(Ptr font, int fontSize, const std::u32string& text); + + /** + * @brief 计算文本宽度(原始指针版本) + */ + float measureText(FontData* font, int fontSize, const std::u32string& text); + + /** + * @brief 计算文本宽度(句柄版本) + */ + float measureText(const FontHandle& font, int fontSize, const std::u32string& text) { + return measureText(font.get(), fontSize, text); + } + + /** + * @brief 计算文本尺寸 + * @param font 字体数据 + * @param fontSize 字体大小 + * @param text 文本(UTF-32) + * @return 文本尺寸 + */ + Size measureTextSize(Ptr font, int fontSize, const std::u32string& text); + + /** + * @brief 计算文本尺寸(原始指针版本) + */ + Size measureTextSize(FontData* font, int fontSize, const std::u32string& text); + + /** + * @brief 计算文本尺寸(句柄版本) + */ + Size measureTextSize(const FontHandle& font, int fontSize, const std::u32string& text) { + return measureTextSize(font.get(), fontSize, text); + } + + /** + * @brief 获取字体内存使用量 + */ + size_t getFontMemory() const; + + /** + * @brief 获取字体数量 + */ + size_t getFontCount() const; + + /** + * @brief 设置默认加载选项 + */ + void setDefaultOptions(const FontLoadOptions& options) { defaultOptions_ = options; } + + /** + * @brief 获取默认加载选项 + */ + const FontLoadOptions& getDefaultOptions() const { return defaultOptions_; } + +private: + FontManager() = default; + ~FontManager() = default; + FontManager(const FontManager&) = delete; + FontManager& operator=(const FontManager&) = delete; + + /** + * @brief 创建字体图集 + */ + Ptr createAtlas(FontData* fontData, int fontSize); + + Ptr device_; + bool initialized_ = false; + FontLoadOptions defaultOptions_; +}; + +/** + * @brief 字体句柄类型别名 + */ +using FontHandle = ResourceHandle; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/resources/material_manager.h b/Extra2D/include/extra2d/resources/material_manager.h new file mode 100644 index 0000000..391c4d5 --- /dev/null +++ b/Extra2D/include/extra2d/resources/material_manager.h @@ -0,0 +1,207 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 材质属性值 + */ +struct MaterialProperty { + enum class Type { + Float, + Vec2, + Vec3, + Vec4, + Mat4, + Int, + Texture + }; + + Type type = Type::Float; + union { + float floatValue = 0.0f; + float vec2Value[2]; + float vec3Value[3]; + float vec4Value[4]; + float mat4Value[16]; + int intValue; + }; + std::string texturePath; + uint32_t textureSlot = 0; + + MaterialProperty() { floatValue = 0.0f; } + explicit MaterialProperty(float v) : type(Type::Float) { floatValue = v; } + MaterialProperty(float x, float y) : type(Type::Vec2) { + vec2Value[0] = x; vec2Value[1] = y; + } + MaterialProperty(float x, float y, float z) : type(Type::Vec3) { + vec3Value[0] = x; vec3Value[1] = y; vec3Value[2] = z; + } + MaterialProperty(float x, float y, float z, float w) : type(Type::Vec4) { + vec4Value[0] = x; vec4Value[1] = y; vec4Value[2] = z; vec4Value[3] = w; + } + explicit MaterialProperty(int v) : type(Type::Int) { intValue = v; } + explicit MaterialProperty(const std::string& texPath, uint32_t slot = 0) + : type(Type::Texture), texturePath(texPath), textureSlot(slot) {} +}; + +/** + * @brief 材质数据结构 + */ +struct MaterialData { + std::string name; + std::string path; + std::string shaderName; + ResourceHandle shader; + std::unordered_map properties; + rhi::BlendState blendState = rhi::BlendState::alphaBlend(); + rhi::DepthStencilState depthState = rhi::DepthStencilState::disabled(); + rhi::RasterizerState rasterizerState = rhi::RasterizerState::noCull(); + size_t memorySize = 0; +}; + +/** + * @brief 材质加载选项 + */ +struct MaterialLoadOptions { + std::string shaderName; + rhi::BlendState blendState = rhi::BlendState::alphaBlend(); + rhi::DepthStencilState depthState = rhi::DepthStencilState::disabled(); + rhi::RasterizerState rasterizerState = rhi::RasterizerState::noCull(); +}; + +/** + * @brief 材质管理器 + * + * 管理材质资源,支持属性设置、纹理绑定 + */ +class MaterialManager : public BaseResourceManager { +public: + /** + * @brief 获取单例实例 + */ + static MaterialManager& getInstance(); + + /** + * @brief 初始化材质管理器 + * @param device RHI 设备 + * @return 成功返回 true + */ + bool init(Ptr device); + + /** + * @brief 关闭材质管理器 + */ + void shutdown(); + + /** + * @brief 检查是否已初始化 + */ + bool isInitialized() const { return initialized_; } + + /** + * @brief 创建材质 + * @param name 材质名称 + * @param shaderName 着色器名称 + * @param options 加载选项 + * @return 材质句柄 + */ + ResourceHandle create(const std::string& name, + const std::string& shaderName, + const MaterialLoadOptions& options = {}); + + /** + * @brief 从 JSON 文件加载材质 + * @param path JSON 文件路径 + * @return 材质句柄 + */ + ResourceHandle load(const std::string& path); + + /** + * @brief 获取材质 + * @param name 材质名称 + * @return 材质句柄 + */ + ResourceHandle get(const std::string& name); + + /** + * @brief 检查材质是否存在 + * @param name 材质名称 + */ + bool has(const std::string& name) const; + + /** + * @brief 移除材质 + * @param name 材质名称 + */ + void remove(const std::string& name); + + /** + * @brief 清除所有材质 + */ + void clear(); + + /** + * @brief 设置材质属性 + * @param material 材质数据 + * @param name 属性名称 + * @param value 属性值 + */ + void setProperty(MaterialData* material, const std::string& name, + const MaterialProperty& value); + + /** + * @brief 设置材质纹理 + * @param material 材质数据 + * @param name 属性名称 + * @param texturePath 纹理路径 + * @param slot 纹理槽 + */ + void setTexture(MaterialData* material, const std::string& name, + const std::string& texturePath, uint32_t slot = 0); + + /** + * @brief 应用材质到设备 + * @param material 材质数据 + * @param device RHI 设备 + */ + void apply(MaterialData* material, rhi::RHIDevice* device); + + /** + * @brief 获取材质数量 + */ + size_t getMaterialCount() const; + +private: + MaterialManager() = default; + ~MaterialManager() = default; + MaterialManager(const MaterialManager&) = delete; + MaterialManager& operator=(const MaterialManager&) = delete; + + /** + * @brief 从 JSON 解析材质 + */ + bool parseFromJson(const std::string& jsonContent, MaterialData* material); + + Ptr device_; + bool initialized_ = false; +}; + +/** + * @brief 材质句柄类型别名 + */ +using MaterialHandle = ResourceHandle; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/resources/mesh_manager.h b/Extra2D/include/extra2d/resources/mesh_manager.h new file mode 100644 index 0000000..87e77e5 --- /dev/null +++ b/Extra2D/include/extra2d/resources/mesh_manager.h @@ -0,0 +1,233 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 顶点格式 + */ +struct Vertex { + Vec2 position; + Vec2 texCoord; + Color color; + + Vertex() = default; + Vertex(const Vec2& pos, const Vec2& uv, const Color& col = Colors::White) + : position(pos), texCoord(uv), color(col) {} + Vertex(float x, float y, float u = 0, float v = 0, const Color& col = Colors::White) + : position(x, y), texCoord(u, v), color(col) {} +}; + +/** + * @brief 网格数据结构 + */ +struct MeshData { + std::string name; + std::string path; + std::vector vertices; + std::vector indices16; + std::vector indices32; + Ptr vertexBuffer; + Ptr indexBuffer; + rhi::IndexFormat indexFormat = rhi::IndexFormat::UInt16; + uint32_t vertexCount = 0; + uint32_t indexCount = 0; + rhi::PrimitiveType primitiveType = rhi::PrimitiveType::Triangles; + bool dynamic = false; + size_t memorySize = 0; +}; + +/** + * @brief 网格加载选项 + */ +struct MeshLoadOptions { + rhi::BufferUsage usage = rhi::BufferUsage::Static; + rhi::PrimitiveType primitiveType = rhi::PrimitiveType::Triangles; + bool use32BitIndices = false; +}; + +/** + * @brief 网格构建器 + */ +class MeshBuilder { +public: + MeshBuilder& addVertex(const Vertex& vertex); + MeshBuilder& addVertex(const Vec2& pos, const Vec2& texCoord, const Color& color = Colors::White); + MeshBuilder& addIndex(uint32_t index); + MeshBuilder& addTriangle(uint32_t i0, uint32_t i1, uint32_t i2); + MeshBuilder& addQuad(const Vec2& pos, const Vec2& size, const Vec2& uv0, const Vec2& uv1, const Color& color = Colors::White); + + const std::vector& getVertices() const { return vertices_; } + const std::vector& getIndices() const { return indices_; } + size_t getVertexCount() const { return vertices_.size(); } + size_t getIndexCount() const { return indices_.size(); } + void clear() { vertices_.clear(); indices_.clear(); } + +private: + std::vector vertices_; + std::vector indices_; +}; + +/** + * @brief 网格管理器 + * + * 管理网格资源,支持动态和静态网格 + */ +class MeshManager : public BaseResourceManager { +public: + /** + * @brief 获取单例实例 + */ + static MeshManager& getInstance(); + + /** + * @brief 初始化网格管理器 + * @param device RHI 设备 + * @return 成功返回 true + */ + bool init(Ptr device); + + /** + * @brief 关闭网格管理器 + */ + void shutdown(); + + /** + * @brief 检查是否已初始化 + */ + bool isInitialized() const { return initialized_; } + + /** + * @brief 创建网格 + * @param name 网格名称 + * @param vertices 顶点数据 + * @param indices 索引数据 + * @param options 加载选项 + * @return 网格句柄 + */ + ResourceHandle create(const std::string& name, + const std::vector& vertices, + const std::vector& indices, + const MeshLoadOptions& options = {}); + + /** + * @brief 从构建器创建网格 + * @param name 网格名称 + * @param builder 网格构建器 + * @param options 加载选项 + * @return 网格句柄 + */ + ResourceHandle create(const std::string& name, + const MeshBuilder& builder, + const MeshLoadOptions& options = {}); + + /** + * @brief 创建四边形网格 + * @param name 网格名称 + * @param width 宽度 + * @param height 高度 + * @return 网格句柄 + */ + ResourceHandle createQuad(const std::string& name, + float width = 1.0f, float height = 1.0f); + + /** + * @brief 创建动态网格 + * @param name 网格名称 + * @param maxVertices 最大顶点数 + * @param maxIndices 最大索引数 + * @param options 加载选项 + * @return 网格句柄 + */ + ResourceHandle createDynamic(const std::string& name, + uint32_t maxVertices, + uint32_t maxIndices, + const MeshLoadOptions& options = {}); + + /** + * @brief 更新动态网格数据 + * @param mesh 网格数据 + * @param vertices 顶点数据 + * @param vertexCount 顶点数量 + * @param indices 索引数据 + * @param indexCount 索引数量 + */ + void updateDynamic(MeshData* mesh, + const Vertex* vertices, uint32_t vertexCount, + const uint32_t* indices, uint32_t indexCount); + + /** + * @brief 获取网格 + * @param name 网格名称 + * @return 网格句柄 + */ + ResourceHandle get(const std::string& name); + + /** + * @brief 检查网格是否存在 + * @param name 网格名称 + */ + bool has(const std::string& name) const; + + /** + * @brief 移除网格 + * @param name 网格名称 + */ + void remove(const std::string& name); + + /** + * @brief 清除所有网格 + */ + void clear(); + + /** + * @brief 获取网格内存使用量 + */ + size_t getMeshMemory() const; + + /** + * @brief 获取网格数量 + */ + size_t getMeshCount() const; + +private: + MeshManager() = default; + ~MeshManager() = default; + MeshManager(const MeshManager&) = delete; + MeshManager& operator=(const MeshManager&) = delete; + + /** + * @brief 创建顶点缓冲区 + */ + Ptr createVertexBuffer(const std::vector& vertices, + rhi::BufferUsage usage); + + /** + * @brief 创建索引缓冲区 + */ + Ptr createIndexBuffer(const std::vector& indices, + bool use32Bit, rhi::BufferUsage usage); + + Ptr device_; + bool initialized_ = false; +}; + +/** + * @brief 网格句柄类型别名 + */ +using MeshHandle = ResourceHandle; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/resources/shader_manager.h b/Extra2D/include/extra2d/resources/shader_manager.h new file mode 100644 index 0000000..4631482 --- /dev/null +++ b/Extra2D/include/extra2d/resources/shader_manager.h @@ -0,0 +1,227 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 着色器数据结构 + */ +struct ShaderData { + std::string name; + std::string path; + Ptr shader; + std::string vertexSource; + std::string fragmentSource; + std::vector dependencies; + std::unordered_map uniforms; + size_t memorySize = 0; +}; + +/** + * @brief 着色器加载选项 + */ +struct ShaderLoadOptions { + std::vector defines; + bool enableCache = true; + bool enableHotReload = true; +}; + +/** + * @brief 着色器 Uniform 定义 + */ +struct ShaderUniformDef { + std::string type; + std::string description; + float defaultValue = 0.0f; + float defaultVec2[2] = {0, 0}; + float defaultVec3[3] = {0, 0, 0}; + float defaultVec4[4] = {0, 0, 0, 0}; + int defaultInt = 0; + bool defaultBool = false; + bool hasDefault = false; +}; + +/** + * @brief 着色器 Sampler 定义 + */ +struct ShaderSamplerDef { + std::string type; + std::string description; +}; + +/** + * @brief 着色器元数据 + */ +struct ShaderMetadata { + std::string name; + std::string category; + std::string version; + std::string description; + std::string vertPath; + std::string fragPath; + std::unordered_map uniforms; + std::unordered_map uniformDefs; + std::unordered_map samplerDefs; +}; + +/** + * @brief 着色器管理器 + * + * 使用 RHI 接口创建着色器,保留 JSON 元数据加载功能 + * 支持同步加载、缓存管理、引用计数 + */ +class ShaderManager : public BaseResourceManager { +public: + /** + * @brief 获取单例实例 + */ + static ShaderManager& getInstance(); + + /** + * @brief 初始化着色器管理器 + * @param device RHI 设备 + * @param shaderDir 着色器目录 + * @return 成功返回 true + */ + bool init(Ptr device, const std::string& shaderDir = "shaders"); + + /** + * @brief 关闭着色器管理器 + */ + void shutdown(); + + /** + * @brief 检查是否已初始化 + */ + bool isInitialized() const { return initialized_; } + + /** + * @brief 从文件加载着色器 + * @param name 着色器名称 + * @param vertPath 顶点着色器路径 + * @param fragPath 片段着色器路径 + * @param options 加载选项 + * @return 着色器句柄 + */ + ResourceHandle loadFromFiles(const std::string& name, + const std::string& vertPath, + const std::string& fragPath, + const ShaderLoadOptions& options = {}); + + /** + * @brief 从源码加载着色器 + * @param name 着色器名称 + * @param vertSource 顶点着色器源码 + * @param fragSource 片段着色器源码 + * @return 着色器句柄 + */ + ResourceHandle loadFromSource(const std::string& name, + const std::string& vertSource, + const std::string& fragSource); + + /** + * @brief 从 JSON 元数据文件加载着色器 + * @param jsonPath JSON 文件路径 + * @param name 着色器名称 + * @return 着色器句柄 + */ + ResourceHandle loadFromMetadata(const std::string& jsonPath, + const std::string& name); + + /** + * @brief 获取着色器 + * @param name 着色器名称 + * @return 着色器句柄 + */ + ResourceHandle get(const std::string& name); + + /** + * @brief 检查着色器是否存在 + * @param name 着色器名称 + */ + bool has(const std::string& name) const; + + /** + * @brief 移除着色器 + * @param name 着色器名称 + */ + void remove(const std::string& name); + + /** + * @brief 清除所有着色器 + */ + void clear(); + + /** + * @brief 获取着色器元数据 + * @param name 着色器名称 + * @return 元数据 + */ + ShaderMetadata getMetadata(const std::string& name) const; + + /** + * @brief 获取着色器目录 + */ + const std::string& getShaderDir() const { return shaderDir_; } + + /** + * @brief 加载内置着色器 + * @return 成功返回 true + */ + bool loadBuiltinShaders(); + + /** + * @brief 获取内置着色器 + * @param name 内置着色器名称 + * @return 着色器句柄 + */ + ResourceHandle getBuiltin(const std::string& name); + +private: + ShaderManager() = default; + ~ShaderManager() = default; + ShaderManager(const ShaderManager&) = delete; + ShaderManager& operator=(const ShaderManager&) = delete; + + /** + * @brief 读取文件内容 + */ + static std::string readFile(const std::string& path); + + /** + * @brief 处理 #include 指令 + */ + std::string processIncludes(const std::string& source, + const std::string& baseDir, + std::vector& dependencies); + + /** + * @brief 解析 JSON 元数据 + */ + bool parseMetadata(const std::string& jsonContent, ShaderMetadata& metadata, + std::string& vertPath, std::string& fragPath); + + Ptr device_; + std::string shaderDir_; + bool initialized_ = false; + + std::unordered_map metadataCache_; +}; + +/** + * @brief 着色器句柄类型别名 + */ +using ShaderHandle = ResourceHandle; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/resources/texture_manager.h b/Extra2D/include/extra2d/resources/texture_manager.h new file mode 100644 index 0000000..1590b22 --- /dev/null +++ b/Extra2D/include/extra2d/resources/texture_manager.h @@ -0,0 +1,204 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 纹理数据结构 + */ +struct TextureData { + std::string name; + std::string path; + uint32_t width = 0; + uint32_t height = 0; + uint32_t channels = 0; + rhi::Format format = rhi::Format::RGBA8_UNORM; + Ptr texture; + Ptr sampler; + bool isRenderTarget = false; + bool hasMipmaps = false; + size_t memorySize = 0; +}; + +/** + * @brief 纹理加载选项 + */ +struct TextureLoadOptions { + bool generateMipmaps = true; + bool sRGB = true; + rhi::FilterMode minFilter = rhi::FilterMode::LinearMipmapLinear; + rhi::FilterMode magFilter = rhi::FilterMode::Linear; + rhi::AddressMode addressU = rhi::AddressMode::ClampToEdge; + rhi::AddressMode addressV = rhi::AddressMode::ClampToEdge; + bool premultiplyAlpha = false; + bool flipVertically = true; + int desiredChannels = 4; +}; + +/** + * @brief 纹理管理器 + * + * 使用 stb_image 加载图像,通过 RHI 创建 GPU 纹理 + * 支持同步/异步加载、缓存管理、引用计数 + */ +class TextureManager : public BaseResourceManager { +public: + /** + * @brief 获取单例实例 + */ + static TextureManager& getInstance(); + + /** + * @brief 初始化纹理管理器 + * @param device RHI 设备 + * @return 成功返回 true + */ + bool init(Ptr device); + + /** + * @brief 关闭纹理管理器 + */ + void shutdown(); + + /** + * @brief 检查是否已初始化 + */ + bool isInitialized() const { return initialized_; } + + /** + * @brief 从文件加载纹理 + * @param path 文件路径 + * @param options 加载选项 + * @return 纹理句柄 + */ + ResourceHandle load(const std::string& path, + const TextureLoadOptions& options = {}); + + /** + * @brief 从内存数据加载纹理 + * @param name 纹理名称 + * @param data 图像数据 + * @param dataSize 数据大小 + * @param options 加载选项 + * @return 纹理句柄 + */ + ResourceHandle loadFromMemory(const std::string& name, + const uint8_t* data, + size_t dataSize, + const TextureLoadOptions& options = {}); + + /** + * @brief 从像素数据创建纹理 + * @param name 纹理名称 + * @param pixels 像素数据 + * @param width 宽度 + * @param height 高度 + * @param channels 通道数 + * @param options 加载选项 + * @return 纹理句柄 + */ + ResourceHandle create(const std::string& name, + const uint8_t* pixels, + uint32_t width, + uint32_t height, + uint32_t channels, + const TextureLoadOptions& options = {}); + + /** + * @brief 创建空纹理(用于渲染目标) + * @param name 纹理名称 + * @param width 宽度 + * @param height 高度 + * @param format 格式 + * @param generateMipmaps 是否生成 Mipmap + * @return 纹理句柄 + */ + ResourceHandle createEmpty(const std::string& name, + uint32_t width, + uint32_t height, + rhi::Format format = rhi::Format::RGBA8_UNORM, + bool generateMipmaps = false); + + /** + * @brief 获取纹理 + * @param path 纹理路径或名称 + * @return 纹理句柄 + */ + ResourceHandle get(const std::string& path); + + /** + * @brief 检查纹理是否存在 + * @param path 纹理路径 + */ + bool has(const std::string& path) const; + + /** + * @brief 移除纹理 + * @param path 纹理路径 + */ + void remove(const std::string& path); + + /** + * @brief 清除所有纹理 + */ + void clear(); + + /** + * @brief 获取纹理内存使用量 + */ + size_t getTextureMemory() const; + + /** + * @brief 获取纹理数量 + */ + size_t getTextureCount() const; + + /** + * @brief 设置默认加载选项 + */ + void setDefaultOptions(const TextureLoadOptions& options) { defaultOptions_ = options; } + + /** + * @brief 获取默认加载选项 + */ + const TextureLoadOptions& getDefaultOptions() const { return defaultOptions_; } + +private: + TextureManager() = default; + ~TextureManager() = default; + TextureManager(const TextureManager&) = delete; + TextureManager& operator=(const TextureManager&) = delete; + + /** + * @brief 创建采样器 + */ + Ptr createSampler(const TextureLoadOptions& options); + + /** + * @brief 计算纹理内存大小 + */ + size_t calculateMemorySize(uint32_t width, uint32_t height, + rhi::Format format, bool hasMipmaps); + + Ptr device_; + bool initialized_ = false; + TextureLoadOptions defaultOptions_; +}; + +/** + * @brief 纹理句柄类型别名 + */ +using TextureHandle = ResourceHandle; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/component.h b/Extra2D/include/extra2d/scene/component.h new file mode 100644 index 0000000..6b63011 --- /dev/null +++ b/Extra2D/include/extra2d/scene/component.h @@ -0,0 +1,154 @@ +#pragma once + +#include +#include + +namespace extra2d { + +class Node; +class Scene; +class RenderQueue; + +namespace detail { + +/** + * @brief 组件类型ID生成器 + * + * 使用静态计数器为每种组件类型生成唯一ID + */ +inline uint32_t getNextComponentTypeId() { + static uint32_t nextId = 0; + return nextId++; +} + +/** + * @brief 获取组件类型的唯一ID + * @tparam T 组件类型 + * @return 组件类型的唯一ID + */ +template +uint32_t getComponentTypeId() { + static uint32_t id = getNextComponentTypeId(); + return id; +} + +} // namespace detail + +/** + * @brief 组件类型声明宏 + * + * 在组件类中使用此宏声明类型ID和类型名称 + */ +#define E2D_COMPONENT_TYPE_DECL(ComponentClass) \ +public: \ + uint32_t getTypeId() const override { \ + return ::extra2d::detail::getComponentTypeId(); \ + } \ + const char* getTypeName() const override { return #ComponentClass; } \ + static uint32_t getStaticTypeId() { \ + return ::extra2d::detail::getComponentTypeId(); \ + } + +/** + * @brief 组件基类 + * + * 所有场景节点组件的基类,提供组件生命周期管理和节点关联 + * 组件可以添加到节点上以扩展节点的功能 + */ +class Component : public std::enable_shared_from_this { +public: + virtual ~Component() = default; + + /** + * @brief 初始化组件 + * + * 在组件添加到节点后调用,用于执行初始化逻辑 + */ + virtual void init() {} + + /** + * @brief 销毁组件 + * + * 在组件从节点移除前调用,用于清理资源 + */ + virtual void destroy() {} + + /** + * @brief 更新组件 + * @param dt 帧间隔时间(秒) + * + * 每帧调用,用于更新组件状态 + */ + virtual void update(float dt) { (void)dt; } + + /** + * @brief 收集渲染命令 + * @param queue 渲染队列引用 + * + * 将组件的渲染命令添加到渲染队列中 + */ + virtual void collectRenderCommands(RenderQueue& queue) { (void)queue; } + + /** + * @brief 获取关联的节点 + * @return 关联的节点指针,未关联返回nullptr + */ + Node* getNode() const { return node_; } + + /** + * @brief 获取关联的场景 + * @return 关联的场景指针,未关联返回nullptr + */ + Scene* getScene() const; + + /** + * @brief 检查组件是否启用 + * @return 如果组件启用返回true + */ + bool isEnabled() const { return enabled_; } + + /** + * @brief 设置组件启用状态 + * @param enabled 是否启用 + */ + void setEnabled(bool enabled) { enabled_ = enabled; } + + /** + * @brief 获取组件类型ID + * @return 组件类型的唯一ID + */ + virtual uint32_t getTypeId() const = 0; + + /** + * @brief 获取组件类型名称 + * @return 组件类型的名称字符串 + */ + virtual const char* getTypeName() const = 0; + + /** + * @brief 检查组件是否已初始化 + * @return 如果组件已初始化返回true + */ + bool isInitialized() const { return initialized_; } + +protected: + friend class Node; + + /** + * @brief 设置关联的节点 + * @param node 节点指针 + */ + void setNode(Node* node) { node_ = node; } + + /** + * @brief 设置初始化状态 + * @param initialized 是否已初始化 + */ + void setInitialized(bool initialized) { initialized_ = initialized; } + + Node* node_ = nullptr; + bool enabled_ = true; + bool initialized_ = false; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/components/shape_renderer.h b/Extra2D/include/extra2d/scene/components/shape_renderer.h new file mode 100644 index 0000000..19d05e0 --- /dev/null +++ b/Extra2D/include/extra2d/scene/components/shape_renderer.h @@ -0,0 +1,284 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 形状渲染组件 + * + * 负责渲染基本形状:线条、矩形、圆形、三角形、多边形等 + * 支持填充和轮廓两种渲染模式 + */ +class ShapeRenderer : public Component { + E2D_COMPONENT_TYPE_DECL(ShapeRenderer) + +public: + /** + * @brief 默认构造函数 + */ + ShapeRenderer(); + + /** + * @brief 析构函数 + */ + ~ShapeRenderer() override; + + // ------------------------------------------------------------------------- + // 颜色和混合 + // ------------------------------------------------------------------------- + + /** + * @brief 设置颜色 + * @param color 颜色值 + */ + void setColor(const Color& color); + + /** + * @brief 获取颜色 + * @return 颜色值 + */ + Color getColor() const { return color_; } + + /** + * @brief 设置混合状态 + * @param blend 混合状态 + */ + void setBlendState(const rhi::BlendState& blend); + + /** + * @brief 获取混合状态 + * @return 混合状态 + */ + rhi::BlendState getBlendState() const { return blend_; } + + // ------------------------------------------------------------------------- + // 线条宽度 + // ------------------------------------------------------------------------- + + /** + * @brief 设置线条宽度 + * @param width 线条宽度 + */ + void setLineWidth(float width); + + /** + * @brief 获取线条宽度 + * @return 线条宽度 + */ + float getLineWidth() const { return lineWidth_; } + + // ------------------------------------------------------------------------- + // 渲染层级 + // ------------------------------------------------------------------------- + + /** + * @brief 设置渲染层级 + * @param layer 层级值 + */ + void setLayer(int layer); + + /** + * @brief 获取渲染层级 + * @return 层级值 + */ + int getLayer() const { return layer_; } + + // ------------------------------------------------------------------------- + // 形状绘制方法 + // ------------------------------------------------------------------------- + + /** + * @brief 绘制线条 + * @param start 起点坐标 + * @param end 终点坐标 + */ + void drawLine(const Vec2& start, const Vec2& end); + + /** + * @brief 绘制多条线段 + * @param points 点列表 + */ + void drawLines(const std::vector& points); + + /** + * @brief 绘制折线 + * @param points 点列表 + */ + void drawPolyline(const std::vector& points); + + /** + * @brief 绘制矩形(轮廓) + * @param rect 矩形区域 + */ + void drawRect(const Rect& rect); + + /** + * @brief 绘制填充矩形 + * @param rect 矩形区域 + */ + void drawFilledRect(const Rect& rect); + + /** + * @brief 绘制圆形(轮廓) + * @param center 圆心坐标 + * @param radius 半径 + * @param segments 分段数(0表示使用默认值) + */ + void drawCircle(const Vec2& center, float radius, int segments = 0); + + /** + * @brief 绘制填充圆形 + * @param center 圆心坐标 + * @param radius 半径 + * @param segments 分段数(0表示使用默认值) + */ + void drawFilledCircle(const Vec2& center, float radius, int segments = 0); + + /** + * @brief 绘制椭圆(轮廓) + * @param center 圆心坐标 + * @param radiusX X轴半径 + * @param radiusY Y轴半径 + * @param segments 分段数(0表示使用默认值) + */ + void drawEllipse(const Vec2& center, float radiusX, float radiusY, int segments = 0); + + /** + * @brief 绘制填充椭圆 + * @param center 圆心坐标 + * @param radiusX X轴半径 + * @param radiusY Y轴半径 + * @param segments 分段数(0表示使用默认值) + */ + void drawFilledEllipse(const Vec2& center, float radiusX, float radiusY, int segments = 0); + + /** + * @brief 绘制三角形(轮廓) + * @param p1 第一个顶点 + * @param p2 第二个顶点 + * @param p3 第三个顶点 + */ + void drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3); + + /** + * @brief 绘制填充三角形 + * @param p1 第一个顶点 + * @param p2 第二个顶点 + * @param p3 第三个顶点 + */ + void drawFilledTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3); + + /** + * @brief 绘制多边形(轮廓) + * @param points 顶点列表 + */ + void drawPolygon(const std::vector& points); + + /** + * @brief 绘制填充多边形 + * @param points 顶点列表 + */ + void drawFilledPolygon(const std::vector& points); + + /** + * @brief 绘制圆弧 + * @param center 圆心坐标 + * @param radius 半径 + * @param startAngle 起始角度(度) + * @param endAngle 结束角度(度) + * @param segments 分段数(0表示使用默认值) + */ + void drawArc(const Vec2& center, float radius, float startAngle, float endAngle, int segments = 0); + + // ------------------------------------------------------------------------- + // 形状管理 + // ------------------------------------------------------------------------- + + /** + * @brief 清除所有形状 + */ + void clearShapes(); + + /** + * @brief 检查是否有形状 + * @return 如果有形状返回true + */ + bool hasShapes() const { return !shapes_.empty(); } + + /** + * @brief 获取形状数量 + * @return 形状数量 + */ + size_t getShapeCount() const { return shapes_.size(); } + + // ------------------------------------------------------------------------- + // 组件生命周期 + // ------------------------------------------------------------------------- + + /** + * @brief 初始化组件 + */ + void init() override; + + /** + * @brief 销毁组件 + */ + void destroy() override; + + /** + * @brief 收集渲染命令 + * @param queue 渲染队列 + */ + void collectRenderCommands(RenderQueue& queue) override; + + // ------------------------------------------------------------------------- + // 边界框 + // ------------------------------------------------------------------------- + + /** + * @brief 获取边界矩形 + * @return 边界矩形 + */ + Rect getBounds() const; + +private: + /** + * @brief 内部形状数据结构 + */ + struct ShapeData { + ShapeType type; + std::vector points; + Color color; + float lineWidth; + float radius; + int segments; + rhi::BlendState blend; + bool filled; + + ShapeData() + : type(ShapeType::Rect), points(), color(Colors::White), + lineWidth(1.0f), radius(0.0f), segments(32), + blend(rhi::BlendState::alphaBlend()), filled(false) {} + }; + + /** + * @brief 添加形状 + * @param shape 形状数据 + */ + void addShape(const ShapeData& shape); + + std::vector shapes_; + Color color_; + rhi::BlendState blend_; + float lineWidth_; + int layer_; + int defaultSegments_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/components/sprite_renderer.h b/Extra2D/include/extra2d/scene/components/sprite_renderer.h new file mode 100644 index 0000000..baee21e --- /dev/null +++ b/Extra2D/include/extra2d/scene/components/sprite_renderer.h @@ -0,0 +1,257 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 精灵渲染组件 + * + * 负责渲染2D精灵图像,支持纹理、颜色调制、混合模式等功能 + * 附加到节点上时,会自动收集渲染命令到渲染队列 + */ +class SpriteRenderer : public Component { + E2D_COMPONENT_TYPE_DECL(SpriteRenderer) + +public: + /** + * @brief 默认构造函数 + */ + SpriteRenderer(); + + /** + * @brief 析构函数 + */ + ~SpriteRenderer() override; + + // ------------------------------------------------------------------------- + // 纹理设置 + // ------------------------------------------------------------------------- + + /** + * @brief 设置纹理(使用纹理句柄) + * @param handle 纹理句柄 + */ + void setTexture(const TextureHandle& handle); + + /** + * @brief 设置纹理(通过路径加载) + * @param path 纹理文件路径 + */ + void setTexture(const std::string& path); + + /** + * @brief 获取纹理句柄 + * @return 纹理句柄 + */ + const TextureHandle& getTexture() const { return texture_; } + + /** + * @brief 获取纹理宽度 + * @return 纹理宽度,无纹理返回0 + */ + uint32_t getTextureWidth() const; + + /** + * @brief 获取纹理高度 + * @return 纹理高度,无纹理返回0 + */ + uint32_t getTextureHeight() const; + + // ------------------------------------------------------------------------- + // 源矩形设置(纹理区域) + // ------------------------------------------------------------------------- + + /** + * @brief 设置源矩形(纹理坐标区域) + * @param rect 源矩形(像素坐标) + */ + void setSrcRect(const Rect& rect); + + /** + * @brief 设置源矩形 + * @param x 起始X坐标 + * @param y 起始Y坐标 + * @param width 宽度 + * @param height 高度 + */ + void setSrcRect(float x, float y, float width, float height); + + /** + * @brief 获取源矩形 + * @return 源矩形 + */ + Rect getSrcRect() const { return srcRect_; } + + /** + * @brief 重置源矩形为整个纹理 + */ + void resetSrcRect(); + + // ------------------------------------------------------------------------- + // 目标尺寸设置 + // ------------------------------------------------------------------------- + + /** + * @brief 设置内容尺寸 + * @param size 尺寸 + */ + void setContentSize(const Size& size); + + /** + * @brief 设置内容尺寸 + * @param width 宽度 + * @param height 高度 + */ + void setContentSize(float width, float height); + + /** + * @brief 获取内容尺寸 + * @return 内容尺寸 + */ + Size getContentSize() const { return contentSize_; } + + /** + * @brief 重置内容尺寸为纹理原始尺寸 + */ + void resetContentSize(); + + // ------------------------------------------------------------------------- + // 颜色和混合 + // ------------------------------------------------------------------------- + + /** + * @brief 设置颜色调制 + * @param color 颜色值 + */ + void setColor(const Color& color); + + /** + * @brief 获取颜色 + * @return 颜色值 + */ + Color getColor() const { return color_; } + + /** + * @brief 设置混合状态 + * @param blend 混合状态 + */ + void setBlendState(const rhi::BlendState& blend); + + /** + * @brief 获取混合状态 + * @return 混合状态 + */ + rhi::BlendState getBlendState() const { return blend_; } + + // ------------------------------------------------------------------------- + // 翻转 + // ------------------------------------------------------------------------- + + /** + * @brief 设置X轴翻转 + * @param flip 是否翻转 + */ + void setFlipX(bool flip); + + /** + * @brief 获取X轴翻转状态 + * @return 是否翻转 + */ + bool isFlipX() const { return flipX_; } + + /** + * @brief 设置Y轴翻转 + * @param flip 是否翻转 + */ + void setFlipY(bool flip); + + /** + * @brief 获取Y轴翻转状态 + * @return 是否翻转 + */ + bool isFlipY() const { return flipY_; } + + // ------------------------------------------------------------------------- + // 渲染层级 + // ------------------------------------------------------------------------- + + /** + * @brief 设置渲染层级 + * @param layer 层级值 + */ + void setLayer(int layer); + + /** + * @brief 获取渲染层级 + * @return 层级值 + */ + int getLayer() const { return layer_; } + + // ------------------------------------------------------------------------- + // 组件生命周期 + // ------------------------------------------------------------------------- + + /** + * @brief 初始化组件 + */ + void init() override; + + /** + * @brief 销毁组件 + */ + void destroy() override; + + /** + * @brief 收集渲染命令 + * @param queue 渲染队列 + */ + void collectRenderCommands(RenderQueue& queue) override; + + // ------------------------------------------------------------------------- + // 边界框 + // ------------------------------------------------------------------------- + + /** + * @brief 获取边界矩形 + * @return 边界矩形 + */ + Rect getBounds() const; + +private: + /** + * @brief 计算目标矩形 + * @return 目标矩形 + */ + Rect calculateDestRect() const; + + /** + * @brief 计算翻转后的源矩形 + * @return 翻转后的源矩形 + */ + Rect calculateFlippedSrcRect() const; + + /** + * @brief 更新内容尺寸 + */ + void updateContentSizeFromTexture(); + + TextureHandle texture_; + Rect srcRect_; + Size contentSize_; + Color color_; + rhi::BlendState blend_; + int layer_; + bool flipX_; + bool flipY_; + bool useTextureSize_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/components/text_renderer.h b/Extra2D/include/extra2d/scene/components/text_renderer.h new file mode 100644 index 0000000..af8a9d1 --- /dev/null +++ b/Extra2D/include/extra2d/scene/components/text_renderer.h @@ -0,0 +1,299 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 文本渲染组件 + * + * 负责渲染文本,支持多种编码、字体、对齐方式等 + * 使用UnicodeUtils处理编码转换,FontManager管理字体 + */ +class TextRenderer : public Component { + E2D_COMPONENT_TYPE_DECL(TextRenderer) + +public: + /** + * @brief 默认构造函数 + */ + TextRenderer(); + + /** + * @brief 析构函数 + */ + ~TextRenderer() override; + + // ------------------------------------------------------------------------- + // 文本设置 + // ------------------------------------------------------------------------- + + /** + * @brief 设置文本(UTF-32) + * @param text UTF-32编码的文本 + */ + void setText(const std::u32string& text); + + /** + * @brief 设置文本(UTF-8) + * @param text UTF-8编码的文本 + */ + void setTextUTF8(const std::string& text); + + /** + * @brief 设置文本(指定编码) + * @param text 文本数据 + * @param encoding 编码类型 + */ + void setText(const std::string& text, TextEncoding encoding); + + /** + * @brief 获取文本(UTF-32) + * @return UTF-32编码的文本 + */ + const std::u32string& getText() const { return text_; } + + /** + * @brief 获取文本(UTF-8) + * @return UTF-8编码的文本 + */ + std::string getTextUTF8() const; + + /** + * @brief 清空文本 + */ + void clearText(); + + // ------------------------------------------------------------------------- + // 字体设置 + // ------------------------------------------------------------------------- + + /** + * @brief 设置字体(使用字体句柄) + * @param handle 字体句柄 + */ + void setFont(const FontHandle& handle); + + /** + * @brief 设置字体(通过路径加载) + * @param path 字体文件路径 + */ + void setFont(const std::string& path); + + /** + * @brief 获取字体句柄 + * @return 字体句柄 + */ + const FontHandle& getFont() const { return font_; } + + // ------------------------------------------------------------------------- + // 字体大小 + // ------------------------------------------------------------------------- + + /** + * @brief 设置字体大小 + * @param size 字体大小(像素) + */ + void setFontSize(int size); + + /** + * @brief 获取字体大小 + * @return 字体大小 + */ + int getFontSize() const { return fontSize_; } + + // ------------------------------------------------------------------------- + // 颜色和混合 + // ------------------------------------------------------------------------- + + /** + * @brief 设置颜色 + * @param color 颜色值 + */ + void setColor(const Color& color); + + /** + * @brief 获取颜色 + * @return 颜色值 + */ + Color getColor() const { return color_; } + + /** + * @brief 设置混合状态 + * @param blend 混合状态 + */ + void setBlendState(const rhi::BlendState& blend); + + /** + * @brief 获取混合状态 + * @return 混合状态 + */ + rhi::BlendState getBlendState() const { return blend_; } + + // ------------------------------------------------------------------------- + // 对齐方式 + // ------------------------------------------------------------------------- + + /** + * @brief 设置水平对齐 + * @param alignment 水平对齐方式 + */ + void setAlignment(TextAlignment alignment); + + /** + * @brief 获取水平对齐 + * @return 水平对齐方式 + */ + TextAlignment getAlignment() const { return alignment_; } + + /** + * @brief 设置垂直对齐 + * @param alignment 垂直对齐方式 + */ + void setVerticalAlignment(TextVerticalAlignment alignment); + + /** + * @brief 获取垂直对齐 + * @return 垂直对齐方式 + */ + TextVerticalAlignment getVerticalAlignment() const { return verticalAlignment_; } + + // ------------------------------------------------------------------------- + // 布局设置 + // ------------------------------------------------------------------------- + + /** + * @brief 设置最大宽度(0表示无限制) + * @param width 最大宽度 + */ + void setMaxWidth(float width); + + /** + * @brief 获取最大宽度 + * @return 最大宽度 + */ + float getMaxWidth() const { return maxWidth_; } + + /** + * @brief 设置行高(0表示使用字体默认行高) + * @param height 行高 + */ + void setLineHeight(float height); + + /** + * @brief 获取行高 + * @return 行高 + */ + float getLineHeight() const { return lineHeight_; } + + /** + * @brief 设置是否自动换行 + * @param wrap 是否自动换行 + */ + void setWordWrap(bool wrap); + + /** + * @brief 获取是否自动换行 + * @return 是否自动换行 + */ + bool isWordWrap() const { return wordWrap_; } + + /** + * @brief 设置缩放比例 + * @param scale 缩放比例 + */ + void setScale(float scale); + + /** + * @brief 获取缩放比例 + * @return 缩放比例 + */ + float getScale() const { return scale_; } + + // ------------------------------------------------------------------------- + // 渲染层级 + // ------------------------------------------------------------------------- + + /** + * @brief 设置渲染层级 + * @param layer 层级值 + */ + void setLayer(int layer); + + /** + * @brief 获取渲染层级 + * @return 层级值 + */ + int getLayer() const { return layer_; } + + // ------------------------------------------------------------------------- + // 组件生命周期 + // ------------------------------------------------------------------------- + + /** + * @brief 初始化组件 + */ + void init() override; + + /** + * @brief 销毁组件 + */ + void destroy() override; + + /** + * @brief 收集渲染命令 + * @param queue 渲染队列 + */ + void collectRenderCommands(RenderQueue& queue) override; + + // ------------------------------------------------------------------------- + // 文本测量 + // ------------------------------------------------------------------------- + + /** + * @brief 计算文本尺寸 + * @return 文本尺寸 + */ + Size measureText() const; + + /** + * @brief 计算文本宽度 + * @return 文本宽度 + */ + float measureTextWidth() const; + + /** + * @brief 获取边界矩形 + * @return 边界矩形 + */ + Rect getBounds() const; + +private: + /** + * @brief 计算文本起始位置(根据对齐方式) + * @return 起始位置 + */ + Vec2 calculateStartPosition() const; + + std::u32string text_; + FontHandle font_; + int fontSize_; + Color color_; + rhi::BlendState blend_; + TextAlignment alignment_; + TextVerticalAlignment verticalAlignment_; + float maxWidth_; + float lineHeight_; + float scale_; + int layer_; + bool wordWrap_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/components/transform_component.h b/Extra2D/include/extra2d/scene/components/transform_component.h new file mode 100644 index 0000000..d6f3bab --- /dev/null +++ b/Extra2D/include/extra2d/scene/components/transform_component.h @@ -0,0 +1,244 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +/** + * @brief 变换组件 + * + * 管理节点的位置、旋转、缩放等变换属性 + * 支持本地坐标系和世界坐标系的变换计算 + */ +class TransformComponent : public Component { + E2D_COMPONENT_TYPE_DECL(TransformComponent) + +public: + /** + * @brief 默认构造函数 + */ + TransformComponent() = default; + + /** + * @brief 析构函数 + */ + ~TransformComponent() override = default; + + // ------------------------------------------------------------------------- + // 位置属性 + // ------------------------------------------------------------------------- + + /** + * @brief 设置本地位置 + * @param pos 新的位置坐标 + */ + void setPosition(const Vec2& pos); + + /** + * @brief 设置本地位置 + * @param x X坐标 + * @param y Y坐标 + */ + void setPosition(float x, float y) { setPosition(Vec2(x, y)); } + + /** + * @brief 获取本地位置 + * @return 本地位置坐标 + */ + Vec2 getPosition() const { return position_; } + + /** + * @brief 设置世界位置 + * @param pos 世界坐标位置 + */ + void setWorldPosition(const Vec2& pos); + + /** + * @brief 获取世界位置 + * @return 世界坐标位置 + */ + Vec2 getWorldPosition() const; + + // ------------------------------------------------------------------------- + // 旋转属性 + // ------------------------------------------------------------------------- + + /** + * @brief 设置旋转角度 + * @param degrees 旋转角度(度数) + */ + void setRotation(float degrees); + + /** + * @brief 获取旋转角度 + * @return 旋转角度(度数) + */ + float getRotation() const { return rotation_; } + + // ------------------------------------------------------------------------- + // 缩放属性 + // ------------------------------------------------------------------------- + + /** + * @brief 设置缩放 + * @param scale 缩放向量 + */ + void setScale(const Vec2& scale); + + /** + * @brief 设置统一缩放 + * @param scale 统一缩放值 + */ + void setScale(float scale) { setScale(Vec2(scale, scale)); } + + /** + * @brief 设置缩放 + * @param x X轴缩放值 + * @param y Y轴缩放值 + */ + void setScale(float x, float y) { setScale(Vec2(x, y)); } + + /** + * @brief 获取缩放 + * @return 缩放向量 + */ + Vec2 getScale() const { return scale_; } + + // ------------------------------------------------------------------------- + // 锚点属性 + // ------------------------------------------------------------------------- + + /** + * @brief 设置锚点 + * @param anchor 锚点位置(0-1范围) + */ + void setAnchor(const Vec2& anchor); + + /** + * @brief 设置锚点 + * @param x 锚点X坐标(0-1范围) + * @param y 锚点Y坐标(0-1范围) + */ + void setAnchor(float x, float y) { setAnchor(Vec2(x, y)); } + + /** + * @brief 获取锚点 + * @return 锚点位置 + */ + Vec2 getAnchor() const { return anchor_; } + + // ------------------------------------------------------------------------- + // 斜切属性 + // ------------------------------------------------------------------------- + + /** + * @brief 设置斜切 + * @param skew 斜切角度向量 + */ + void setSkew(const Vec2& skew); + + /** + * @brief 设置斜切 + * @param x X轴斜切角度 + * @param y Y轴斜切角度 + */ + void setSkew(float x, float y) { setSkew(Vec2(x, y)); } + + /** + * @brief 获取斜切 + * @return 斜切角度向量 + */ + Vec2 getSkew() const { return skew_; } + + // ------------------------------------------------------------------------- + // 变换矩阵 + // ------------------------------------------------------------------------- + + /** + * @brief 获取本地变换矩阵 + * @return 本地变换矩阵 + */ + glm::mat4 getLocalTransform() const; + + /** + * @brief 获取世界变换矩阵 + * @return 世界变换矩阵 + */ + glm::mat4 getWorldTransform() const; + + // ------------------------------------------------------------------------- + // 坐标转换 + // ------------------------------------------------------------------------- + + /** + * @brief 将本地坐标转换为世界坐标 + * @param localPos 本地坐标位置 + * @return 世界坐标位置 + */ + Vec2 localToWorld(const Vec2& localPos) const; + + /** + * @brief 将世界坐标转换为本地坐标 + * @param worldPos 世界坐标位置 + * @return 本地坐标位置 + */ + Vec2 worldToLocal(const Vec2& worldPos) const; + + // ------------------------------------------------------------------------- + // 脏标记 + // ------------------------------------------------------------------------- + + /** + * @brief 标记变换为脏 + * + * 标记本地变换和世界变换需要重新计算 + */ + void markTransformDirty(); + + /** + * @brief 标记世界变换为脏 + * + * 仅标记世界变换需要重新计算 + */ + void markWorldTransformDirty(); + + /** + * @brief 检查本地变换是否脏 + * @return 如果本地变换需要重新计算返回true + */ + bool isLocalTransformDirty() const { return localDirty_; } + + /** + * @brief 检查世界变换是否脏 + * @return 如果世界变换需要重新计算返回true + */ + bool isWorldTransformDirty() const { return worldDirty_; } + + /** + * @brief 批量更新变换 + * + * 从父节点到子节点依次更新世界变换矩阵 + */ + void batchTransforms(); + +private: + /** + * @brief 计算本地变换矩阵 + */ + void computeLocalTransform() const; + + Vec2 position_ = Vec2::Zero(); + Vec2 scale_ = Vec2::One(); + Vec2 anchor_ = Vec2(0.5f, 0.5f); + Vec2 skew_ = Vec2::Zero(); + float rotation_ = 0.0f; + + mutable glm::mat4 localTransform_ = glm::mat4(1.0f); + mutable glm::mat4 worldTransform_ = glm::mat4(1.0f); + mutable bool localDirty_ = true; + mutable bool worldDirty_ = true; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/node.h b/Extra2D/include/extra2d/scene/node.h index 45ec042..495798c 100644 --- a/Extra2D/include/extra2d/scene/node.h +++ b/Extra2D/include/extra2d/scene/node.h @@ -5,235 +5,614 @@ #include #include #include +#include +#include #include #include +#include #include namespace extra2d { -// 前向声明 class Scene; class RenderBackend; struct RenderCommand; +class RenderQueue; -// ============================================================================ -// 节点基类 - 场景图的基础 -// ============================================================================ +/** + * @brief 节点基类 - 场景图的基础 + * + * 节点是场景图的基本单元,支持层级管理、变换继承和组件系统 + */ class Node : public std::enable_shared_from_this { public: - Node(); - virtual ~Node(); + /** + * @brief 创建节点 + * @param name 节点名称 + * @return 创建的节点智能指针 + */ + static Ptr create(const std::string& name = ""); - // ------------------------------------------------------------------------ - // 层级管理 - // ------------------------------------------------------------------------ - void addChild(Ptr child); + Node(); + virtual ~Node(); - /** - * @brief 批量添加子节点 - * @param children 子节点列表 - */ - void addChildren(std::vector> &&children); + // ------------------------------------------------------------------------- + // 层级管理 + // ------------------------------------------------------------------------- - void removeChild(Ptr child); - void removeChildByName(const std::string &name); - void detach(); - void clearChildren(); + /** + * @brief 添加子节点 + * @param child 要添加的子节点智能指针 + */ + void addChild(Ptr child); - Ptr getParent() const { return parent_.lock(); } - const std::vector> &getChildren() const { return children_; } - Ptr findChild(const std::string &name) const; - Ptr findChildByTag(int tag) const; + /** + * @brief 批量添加子节点 + * @param children 子节点列表 + */ + void addChildren(std::vector>&& children); - // ------------------------------------------------------------------------ - // 变换属性 - // ------------------------------------------------------------------------ - void setPos(const Vec2 &pos); - void setPos(float x, float y); - Vec2 getPosition() const { return position_; } + /** + * @brief 移除子节点 + * @param child 要移除的子节点智能指针 + */ + void removeChild(Ptr child); - void setRotation(float degrees); - float getRotation() const { return rotation_; } + /** + * @brief 通过名称移除子节点 + * @param name 子节点的名称 + */ + void removeChildByName(const std::string& name); - void setScale(const Vec2 &scale); - void setScale(float scale); - void setScale(float x, float y); - Vec2 getScale() const { return scale_; } + /** + * @brief 从父节点分离 + */ + void detach(); - void setAnchor(const Vec2 &anchor); - void setAnchor(float x, float y); - Vec2 getAnchor() const { return anchor_; } + /** + * @brief 清除所有子节点 + */ + void clearChildren(); - void setSkew(const Vec2 &skew); - void setSkew(float x, float y); - Vec2 getSkew() const { return skew_; } + /** + * @brief 获取父节点 + * @return 父节点智能指针 + */ + Ptr getParent() const { return parent_.lock(); } - void setOpacity(float opacity); - float getOpacity() const { return opacity_; } + /** + * @brief 获取所有子节点 + * @return 子节点列表的常量引用 + */ + const std::vector>& getChildren() const { return children_; } - void setVisible(bool visible); - bool isVisible() const { return visible_; } + /** + * @brief 通过名称查找子节点 + * @param name 子节点的名称 + * @return 找到的子节点智能指针,未找到返回nullptr + */ + Ptr findChild(const std::string& name) const; - /** - * @brief 设置颜色 - * @param color RGB颜色 - */ - void setColor(const Color3B &color); - Color3B getColor() const { return color_; } + /** + * @brief 通过标签查找子节点 + * @param tag 子节点的标签值 + * @return 找到的子节点智能指针,未找到返回nullptr + */ + Ptr findChildByTag(int tag) const; - /** - * @brief 设置X轴翻转 - */ - void setFlipX(bool flipX); - bool isFlipX() const { return flipX_; } + // ------------------------------------------------------------------------- + // 组件系统 + // ------------------------------------------------------------------------- - /** - * @brief 设置Y轴翻转 - */ - void setFlipY(bool flipY); - bool isFlipY() const { return flipY_; } + /** + * @brief 添加组件 + * @tparam T 组件类型 + * @tparam Args 构造函数参数类型 + * @param args 构造函数参数 + * @return 创建的组件指针 + */ + template + T* addComponent(Args&&... args) { + static_assert(std::is_base_of::value, + "T must derive from Component"); + + uint32_t typeId = T::getStaticTypeId(); + + // 检查是否已存在相同类型的组件 + auto it = components_.find(typeId); + if (it != components_.end()) { + return static_cast(it->second.get()); + } - void setZOrder(int zOrder); - int getZOrder() const { return zOrder_; } + // 创建新组件 + auto component = makePtr(std::forward(args)...); + component->setNode(this); + component->init(); + component->setInitialized(true); + + components_[typeId] = component; + + return component.get(); + } - // ------------------------------------------------------------------------ - // 世界变换 - // ------------------------------------------------------------------------ - Vec2 toWorld(const Vec2 &localPos) const; - Vec2 toLocal(const Vec2 &worldPos) const; + /** + * @brief 获取组件 + * @tparam T 组件类型 + * @return 组件指针,不存在返回nullptr + */ + template + T* getComponent() const { + static_assert(std::is_base_of::value, + "T must derive from Component"); + + uint32_t typeId = T::getStaticTypeId(); + auto it = components_.find(typeId); + if (it != components_.end()) { + return static_cast(it->second.get()); + } + return nullptr; + } - glm::mat4 getLocalTransform() const; - glm::mat4 getWorldTransform() const; + /** + * @brief 检查是否有组件 + * @tparam T 组件类型 + * @return 如果存在返回true + */ + template + bool hasComponent() const { + return getComponent() != nullptr; + } - /** - * @brief 标记变换矩阵为脏状态,并传播到所有子节点 - */ - void markTransformDirty(); + /** + * @brief 移除组件 + * @tparam T 组件类型 + */ + template + void removeComponent() { + static_assert(std::is_base_of::value, + "T must derive from Component"); + + uint32_t typeId = T::getStaticTypeId(); + auto it = components_.find(typeId); + if (it != components_.end()) { + it->second->destroy(); + it->second->setInitialized(false); + it->second->setNode(nullptr); + components_.erase(it); + } + } - /** - * @brief 批量更新变换矩阵 - * 在渲染前统一计算所有脏节点的变换矩阵,避免逐节点计算时的重复递归 - */ - void batchTransforms(); + /** + * @brief 获取变换组件 + * @return 变换组件指针 + */ + TransformComponent* getTransform() const { return transform_; } - /** - * @brief 获取变换脏标记状态 - */ - bool isTransformDirty() const { return transformDirty_; } - bool isWorldTransformDirty() const { return worldTransformDirty_; } + // ------------------------------------------------------------------------- + // 变换属性(便捷方法,委托给TransformComponent) + // ------------------------------------------------------------------------- - // ------------------------------------------------------------------------ - // 名称和标签 - // ------------------------------------------------------------------------ - void setName(const std::string &name) { name_ = name; } - const std::string &getName() const { return name_; } + /** + * @brief 设置节点位置 + * @param pos 新的位置坐标 + */ + void setPos(const Vec2& pos); - void setTag(int tag) { tag_ = tag; } - int getTag() const { return tag_; } + /** + * @brief 设置节点位置 + * @param x X坐标 + * @param y Y坐标 + */ + void setPos(float x, float y); - // ------------------------------------------------------------------------ - // 生命周期回调 - // ------------------------------------------------------------------------ - virtual void onEnter(); - virtual void onExit(); - virtual void onUpdate(float dt); - virtual void onRender(RenderBackend &renderer); - virtual void onAttachToScene(Scene *scene); - virtual void onDetachFromScene(); + /** + * @brief 获取节点位置 + * @return 位置坐标 + */ + Vec2 getPosition() const; - // ------------------------------------------------------------------------ - // 边界框 - // ------------------------------------------------------------------------ - virtual Rect getBounds() const; + /** + * @brief 设置节点旋转角度 + * @param degrees 旋转角度(度数) + */ + void setRotation(float degrees); - // ------------------------------------------------------------------------ - // 事件系统 - // ------------------------------------------------------------------------ - EventDispatcher &getEventDispatcher() { return eventDispatcher_; } + /** + * @brief 获取节点旋转角度 + * @return 旋转角度(度数) + */ + float getRotation() const; - // ------------------------------------------------------------------------ - // 内部方法 - // ------------------------------------------------------------------------ - void update(float dt); - void render(RenderBackend &renderer); - void sortChildren(); + /** + * @brief 设置节点缩放 + * @param scale 缩放向量 + */ + void setScale(const Vec2& scale); - bool isRunning() const { return running_; } - Scene *getScene() const { return scene_; } + /** + * @brief 设置节点统一缩放 + * @param scale 统一缩放值 + */ + void setScale(float scale); - // 多线程渲染命令收集 - virtual void collectRenderCommands(std::vector &commands, - int parentZOrder = 0); + /** + * @brief 设置节点缩放 + * @param x X轴缩放值 + * @param y Y轴缩放值 + */ + void setScale(float x, float y); + + /** + * @brief 获取节点缩放 + * @return 缩放向量 + */ + Vec2 getScale() const; + + /** + * @brief 设置节点锚点 + * @param anchor 锚点位置(0-1范围) + */ + void setAnchor(const Vec2& anchor); + + /** + * @brief 设置节点锚点 + * @param x 锚点X坐标(0-1范围) + * @param y 锚点Y坐标(0-1范围) + */ + void setAnchor(float x, float y); + + /** + * @brief 获取节点锚点 + * @return 锚点位置 + */ + Vec2 getAnchor() const; + + /** + * @brief 设置节点斜切 + * @param skew 斜切角度向量 + */ + void setSkew(const Vec2& skew); + + /** + * @brief 设置节点斜切 + * @param x X轴斜切角度 + * @param y Y轴斜切角度 + */ + void setSkew(float x, float y); + + /** + * @brief 获取节点斜切 + * @return 斜切角度向量 + */ + Vec2 getSkew() const; + + // ------------------------------------------------------------------------- + // 世界变换 + // ------------------------------------------------------------------------- + + /** + * @brief 将本地坐标转换为世界坐标 + * @param localPos 本地坐标位置 + * @return 世界坐标位置 + */ + Vec2 toWorld(const Vec2& localPos) const; + + /** + * @brief 将世界坐标转换为本地坐标 + * @param worldPos 世界坐标位置 + * @return 本地坐标位置 + */ + Vec2 toLocal(const Vec2& worldPos) const; + + /** + * @brief 获取本地变换矩阵 + * @return 本地变换矩阵 + */ + glm::mat4 getLocalTransform() const; + + /** + * @brief 获取世界变换矩阵 + * @return 世界变换矩阵 + */ + glm::mat4 getWorldTransform() const; + + /** + * @brief 标记变换矩阵为脏状态,并传播到所有子节点 + */ + void markTransformDirty(); + + /** + * @brief 批量更新变换矩阵 + */ + void batchTransforms(); + + /** + * @brief 获取变换脏标记状态 + */ + bool isTransformDirty() const; + bool isWorldTransformDirty() const; + + // ------------------------------------------------------------------------- + // 可见性和外观 + // ------------------------------------------------------------------------- + + /** + * @brief 设置透明度 + * @param opacity 透明度值(0.0-1.0范围) + */ + void setOpacity(float opacity); + + /** + * @brief 获取透明度 + * @return 透明度值 + */ + float getOpacity() const { return opacity_; } + + /** + * @brief 设置可见性 + * @param visible 是否可见 + */ + void setVisible(bool visible); + + /** + * @brief 获取可见性 + * @return 是否可见 + */ + bool isVisible() const { return visible_; } + + /** + * @brief 设置颜色 + * @param color RGB颜色 + */ + void setColor(const Color3B& color); + + /** + * @brief 获取颜色 + * @return RGB颜色 + */ + Color3B getColor() const { return color_; } + + /** + * @brief 设置X轴翻转 + */ + void setFlipX(bool flipX); + + /** + * @brief 获取X轴翻转状态 + */ + bool isFlipX() const { return flipX_; } + + /** + * @brief 设置Y轴翻转 + */ + void setFlipY(bool flipY); + + /** + * @brief 获取Y轴翻转状态 + */ + bool isFlipY() const { return flipY_; } + + /** + * @brief 设置Z序 + * @param zOrder 渲染层级顺序 + */ + void setZOrder(int zOrder); + + /** + * @brief 获取Z序 + * @return 渲染层级顺序 + */ + int getZOrder() const { return zOrder_; } + + // ------------------------------------------------------------------------- + // 名称和标签 + // ------------------------------------------------------------------------- + + /** + * @brief 设置名称 + * @param name 节点名称 + */ + void setName(const std::string& name) { name_ = name; } + + /** + * @brief 获取名称 + * @return 节点名称 + */ + const std::string& getName() const { return name_; } + + /** + * @brief 设置标签 + * @param tag 标签值 + */ + void setTag(int tag) { tag_ = tag; } + + /** + * @brief 获取标签 + * @return 标签值 + */ + int getTag() const { return tag_; } + + // ------------------------------------------------------------------------- + // 生命周期回调 + // ------------------------------------------------------------------------- + + /** + * @brief 节点进入时的回调 + */ + virtual void onEnter(); + + /** + * @brief 节点退出时的回调 + */ + virtual void onExit(); + + /** + * @brief 更新回调 + * @param dt 帧间隔时间(秒) + */ + virtual void onUpdate(float dt); + + /** + * @brief 渲染回调 + * @param renderer 渲染后端引用 + */ + virtual void onRender(RenderBackend& renderer); + + /** + * @brief 附加到场景时的回调 + * @param scene 所属场景指针 + */ + virtual void onAttachToScene(Scene* scene); + + /** + * @brief 从场景分离时的回调 + */ + virtual void onDetachFromScene(); + + // ------------------------------------------------------------------------- + // 边界框 + // ------------------------------------------------------------------------- + + /** + * @brief 获取节点边界矩形 + * @return 节点的边界矩形 + */ + virtual Rect getBounds() const; + + // ------------------------------------------------------------------------- + // 事件系统 + // ------------------------------------------------------------------------- + + /** + * @brief 获取事件分发器 + * @return 事件分发器引用 + */ + EventDispatcher& getEventDispatcher() { return eventDispatcher_; } + + // ------------------------------------------------------------------------- + // 内部方法 + // ------------------------------------------------------------------------- + + /** + * @brief 更新节点 + * @param dt 帧间隔时间(秒) + */ + void update(float dt); + + /** + * @brief 渲染节点 + * @param renderer 渲染后端引用 + */ + void render(RenderBackend& renderer); + + /** + * @brief 对子节点排序 + */ + void sortChildren(); + + /** + * @brief 检查是否正在运行 + * @return 是否正在运行 + */ + bool isRunning() const { return running_; } + + /** + * @brief 获取所属场景 + * @return 所属场景指针 + */ + Scene* getScene() const { return scene_; } + + /** + * @brief 收集渲染命令 + * @param commands 渲染命令输出向量 + * @param parentZOrder 父节点的Z序 + */ + virtual void collectRenderCommands(std::vector& commands, + int parentZOrder = 0); + + /** + * @brief 收集渲染命令到队列 + * @param queue 渲染队列引用 + */ + virtual void collectRenderCommandsToQueue(RenderQueue& queue); protected: - // 子类重写 - virtual void onDraw(RenderBackend &renderer) {} - virtual void onUpdateNode(float dt) {} - virtual void generateRenderCommand(std::vector &commands, - int zOrder) {}; + /** + * @brief 绘制回调(子类重写) + * @param renderer 渲染后端引用 + */ + virtual void onDraw(RenderBackend& renderer) {} - // 供子类访问的内部状态 - Vec2 &getPositionRef() { return position_; } - Vec2 &getScaleRef() { return scale_; } - Vec2 &getAnchorRef() { return anchor_; } - float getRotationRef() { return rotation_; } - float getOpacityRef() { return opacity_; } + /** + * @brief 节点更新回调(子类重写) + * @param dt 帧间隔时间(秒) + */ + virtual void onUpdateNode(float dt) {} + + /** + * @brief 生成渲染命令(子类重写) + * @param commands 渲染命令输出向量 + * @param zOrder Z序 + */ + virtual void generateRenderCommand(std::vector& commands, + int zOrder) {} + + /** + * @brief 生成渲染命令到队列(子类重写) + * @param queue 渲染队列引用 + */ + virtual void generateRenderCommandToQueue(RenderQueue& queue) { (void)queue; } private: - // ========================================================================== - // 成员变量按类型大小降序排列,减少内存对齐填充 - // 64位系统对齐:std::string(32) > glm::mat4(64) > std::vector(24) > - // double(8) > float(4) > int(4) > bool(1) - // ========================================================================== + // ========================================================================== + // 成员变量按类型大小降序排列,减少内存对齐填充 + // ========================================================================== - // 1. 大块内存(64字节) - mutable glm::mat4 localTransform_; // 64 bytes - mutable glm::mat4 worldTransform_; // 64 bytes + // 1. 大块内存(64字节) + mutable glm::mat4 localTransform_ = glm::mat4(1.0f); + mutable glm::mat4 worldTransform_ = glm::mat4(1.0f); - // 2. 字符串和容器(24-32字节) - std::string name_; // 32 bytes - std::vector> children_; // 24 bytes + // 2. 字符串和容器(24-32字节) + std::string name_; + std::vector> children_; + std::unordered_map> components_; - // 3. 子节点索引(加速查找) - std::unordered_map> nameIndex_; // 56 bytes - std::unordered_map> tagIndex_; // 56 bytes + // 3. 子节点索引(加速查找) + std::unordered_map> nameIndex_; + std::unordered_map> tagIndex_; - // 4. 事件分发器 - EventDispatcher eventDispatcher_; // 大小取决于实现 + // 4. 事件分发器 + EventDispatcher eventDispatcher_; - // 5. 父节点引用 - WeakPtr parent_; // 16 bytes + // 5. 父节点引用 + WeakPtr parent_; - // 7. 变换属性(按访问频率分组) - Vec2 position_ = Vec2::Zero(); // 8 bytes - Vec2 scale_ = Vec2(1.0f, 1.0f); // 8 bytes - Vec2 anchor_ = Vec2(0.5f, 0.5f); // 8 bytes - Vec2 skew_ = Vec2::Zero(); // 8 bytes + // 6. 变换组件(内置) + TransformComponent* transform_ = nullptr; - // 8. 浮点属性 - float rotation_ = 0.0f; // 4 bytes - float opacity_ = 1.0f; // 4 bytes + // 7. 浮点属性 + float opacity_ = 1.0f; - // 10. 颜色属性 - Color3B color_ = Color3B(255, 255, 255); // 3 bytes + // 8. 颜色属性 + Color3B color_ = Color3B(255, 255, 255); - // 11. 整数属性 - int zOrder_ = 0; // 4 bytes - int tag_ = -1; // 4 bytes + // 9. 整数属性 + int zOrder_ = 0; + int tag_ = -1; - // 12. 布尔属性 - bool flipX_ = false; // 1 byte - bool flipY_ = false; // 1 byte + // 10. 布尔属性 + bool flipX_ = false; + bool flipY_ = false; - // 13. 场景指针 - Scene *scene_ = nullptr; // 8 bytes + // 11. 场景指针 + Scene* scene_ = nullptr; - // 14. 布尔标志(打包在一起) - mutable bool transformDirty_ = true; // 1 byte - mutable bool worldTransformDirty_ = true; // 1 byte - bool childrenOrderDirty_ = false; // 1 byte - bool visible_ = true; // 1 byte - bool running_ = false; // 1 byte + // 12. 布尔标志(打包在一起) + mutable bool transformDirty_ = true; + mutable bool worldTransformDirty_ = true; + bool childrenOrderDirty_ = false; + bool visible_ = true; + bool running_ = false; }; } // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/scene.h b/Extra2D/include/extra2d/scene/scene.h index aad87cf..95abfa9 100644 --- a/Extra2D/include/extra2d/scene/scene.h +++ b/Extra2D/include/extra2d/scene/scene.h @@ -7,83 +7,191 @@ namespace extra2d { -// 前向声明 -struct RenderCommand; +class RenderQueue; -// ============================================================================ -// 场景类 - 节点容器,管理整个场景图 -// ============================================================================ +/** + * @brief 场景类 - 节点容器,管理整个场景图 + * + * 场景是场景图的根节点,管理相机、视口和渲染流程 + */ class Scene : public Node { public: - Scene(); - ~Scene() override = default; + Scene(); + ~Scene() override = default; - // ------------------------------------------------------------------------ - // 场景属性 - // ------------------------------------------------------------------------ - void setBackgroundColor(const Color &color) { backgroundColor_ = color; } - Color getBackgroundColor() const { return backgroundColor_; } + // ------------------------------------------------------------------------- + // 场景属性 + // ------------------------------------------------------------------------- - // ------------------------------------------------------------------------ - // 摄像机 - // ------------------------------------------------------------------------ - void setCamera(Ptr camera); - Ptr getCamera() const { return camera_; } + /** + * @brief 设置背景颜色 + * @param color 背景颜色 + */ + void setBackgroundColor(const Color& color) { backgroundColor_ = color; } - Camera *getActiveCamera() const { - return camera_ ? camera_.get() : defaultCamera_.get(); - } + /** + * @brief 获取背景颜色 + * @return 背景颜色 + */ + Color getBackgroundColor() const { return backgroundColor_; } - // ------------------------------------------------------------------------ - // 视口和尺寸 - // ------------------------------------------------------------------------ - void setViewportSize(float width, float height); - void setViewportSize(const Size &size); - Size getViewportSize() const { return viewportSize_; } + // ------------------------------------------------------------------------- + // 摄像机 + // ------------------------------------------------------------------------- - float getWidth() const { return viewportSize_.width; } - float getHeight() const { return viewportSize_.height; } + /** + * @brief 设置场景相机 + * @param camera 相机智能指针 + */ + void setCamera(Ptr camera); - // ------------------------------------------------------------------------ - // 场景状态 - // ------------------------------------------------------------------------ - bool isPaused() const { return paused_; } - void pause() { paused_ = true; } - void resume() { paused_ = false; } + /** + * @brief 获取场景相机 + * @return 相机智能指针 + */ + Ptr getCamera() const { return camera_; } - // ------------------------------------------------------------------------ - // 渲染和更新 - // ------------------------------------------------------------------------ - void renderScene(RenderBackend &renderer); - virtual void renderContent(RenderBackend &renderer); - void updateScene(float dt); - void collectRenderCommands(std::vector &commands, - int parentZOrder = 0) override; + /** + * @brief 获取活动相机 + * @return 活动相机指针(优先返回设置的相机,否则返回默认相机) + */ + Camera* getActiveCamera() const { + return camera_ ? camera_.get() : defaultCamera_.get(); + } - // ------------------------------------------------------------------------ - // 静态创建方法 - // ------------------------------------------------------------------------ - static Ptr create(); + // ------------------------------------------------------------------------- + // 视口和尺寸 + // ------------------------------------------------------------------------- + + /** + * @brief 设置视口大小 + * @param width 视口宽度 + * @param height 视口高度 + */ + void setViewportSize(float width, float height); + + /** + * @brief 设置视口大小 + * @param size 视口尺寸结构体 + */ + void setViewportSize(const Size& size); + + /** + * @brief 获取视口大小 + * @return 视口尺寸 + */ + Size getViewportSize() const { return viewportSize_; } + + /** + * @brief 获取视口宽度 + * @return 视口宽度 + */ + float getWidth() const { return viewportSize_.width; } + + /** + * @brief 获取视口高度 + * @return 视口高度 + */ + float getHeight() const { return viewportSize_.height; } + + // ------------------------------------------------------------------------- + // 场景状态 + // ------------------------------------------------------------------------- + + /** + * @brief 检查场景是否暂停 + * @return 如果场景暂停返回true + */ + bool isPaused() const { return paused_; } + + /** + * @brief 暂停场景 + */ + void pause() { paused_ = true; } + + /** + * @brief 恢复场景 + */ + void resume() { paused_ = false; } + + // ------------------------------------------------------------------------- + // 渲染和更新 + // ------------------------------------------------------------------------- + + /** + * @brief 渲染场景 + * @param renderer 渲染后端引用 + */ + void renderScene(RenderBackend& renderer); + + /** + * @brief 渲染场景内容 + * @param renderer 渲染后端引用 + */ + virtual void renderContent(RenderBackend& renderer); + + /** + * @brief 更新场景 + * @param dt 帧间隔时间(秒) + */ + void updateScene(float dt); + + /** + * @brief 收集渲染命令 + * @param commands 渲染命令输出向量 + * @param parentZOrder 父节点的Z序 + */ + void collectRenderCommands(std::vector& commands, + int parentZOrder = 0) override; + + /** + * @brief 收集渲染命令到队列 + * @param queue 渲染队列引用 + */ + void collectRenderCommandsToQueue(RenderQueue& queue) override; + + // ------------------------------------------------------------------------- + // 静态创建方法 + // ------------------------------------------------------------------------- + + /** + * @brief 创建场景对象 + * @return 新创建的场景智能指针 + */ + static Ptr create(); protected: - void onEnter() override; - void onExit() override; + /** + * @brief 场景进入时的回调 + */ + void onEnter() override; - // 过渡场景生命周期回调(供 TransitionScene 使用) - virtual void onExitTransitionDidStart() {} - virtual void onEnterTransitionDidFinish() {} + /** + * @brief 场景退出时的回调 + */ + void onExit() override; - friend class SceneManager; - friend class TransitionScene; + /** + * @brief 过渡场景开始时的回调(供 TransitionScene 使用) + */ + virtual void onExitTransitionDidStart() {} + + /** + * @brief 过渡场景结束时的回调(供 TransitionScene 使用) + */ + virtual void onEnterTransitionDidFinish() {} + + friend class SceneManager; + friend class TransitionScene; private: - Color backgroundColor_ = Colors::Black; - Size viewportSize_ = Size::Zero(); + Color backgroundColor_ = Colors::Black; + Size viewportSize_ = Size::Zero(); - Ptr camera_; - Ptr defaultCamera_; + Ptr camera_; + Ptr defaultCamera_; - bool paused_ = false; + bool paused_ = false; }; } // namespace extra2d diff --git a/Extra2D/src/graphics/backends/backend_factory.cpp b/Extra2D/src/graphics/backends/backend_factory.cpp deleted file mode 100644 index 1b22365..0000000 --- a/Extra2D/src/graphics/backends/backend_factory.cpp +++ /dev/null @@ -1,141 +0,0 @@ -#include -#include -#include - -namespace extra2d { -namespace graphics { - -std::unordered_map & -BackendFactory::registry() { - static std::unordered_map reg; - return reg; -} - -void BackendFactory::reg(const std::string &name, BackendFn backend, - const std::vector &windowBackends) { - registry()[name] = {backend, windowBackends}; - E2D_LOG_DEBUG("已注册图形后端: {} (窗口后端数量: {})", name, - windowBackends.size()); -} - -UniquePtr -BackendFactory::createBackend(const std::string &name) { - auto ® = registry(); - auto it = reg.find(name); - if (it != reg.end() && it->second.createFn) { - E2D_LOG_INFO("正在创建图形后端: {}", name); - return it->second.createFn(); - } - E2D_LOG_ERROR("未找到图形后端 '{}'", name); - return nullptr; -} - -UniquePtr BackendFactory::createDefaultBackend() { - std::string recommended = getRecommendedBackend(); - if (recommended.empty()) { - E2D_LOG_ERROR("无可用的图形后端"); - return nullptr; - } - return createBackend(recommended); -} - -UniquePtr -BackendFactory::createBackendForWindow(const std::string &windowBackend) { - std::string recommended = getRecommendedBackendForWindow(windowBackend); - if (recommended.empty()) { - E2D_LOG_ERROR("未找到与窗口后端 '{}' 兼容的图形后端", windowBackend); - return nullptr; - } - return createBackend(recommended); -} - -std::vector BackendFactory::backends() { - std::vector result; - for (const auto &pair : registry()) { - result.push_back(pair.first); - } - return result; -} - -bool BackendFactory::has(const std::string &name) { - return registry().find(name) != registry().end(); -} - -std::string BackendFactory::getRecommendedBackend() { - auto ® = registry(); - - static const std::vector priority = { - "vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles"}; - - for (const auto &name : priority) { - if (reg.find(name) != reg.end()) { - return name; - } - } - - if (!reg.empty()) { - return reg.begin()->first; - } - - E2D_LOG_WARN("未注册任何图形后端"); - return ""; -} - -std::string BackendFactory::getRecommendedBackendForWindow( - const std::string &windowBackend) { - auto ® = registry(); - - static const std::vector priority = { - "vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles"}; - - for (const auto &name : priority) { - auto it = reg.find(name); - if (it != reg.end() && isCompatible(name, windowBackend)) { - return name; - } - } - - for (const auto &pair : reg) { - if (isCompatible(pair.first, windowBackend)) { - return pair.first; - } - } - - E2D_LOG_WARN("未找到与窗口后端 '{}' 兼容的图形后端", windowBackend); - return ""; -} - -bool BackendFactory::isCompatible(const std::string &graphicsBackend, - const std::string &windowBackend) { - auto ® = registry(); - auto it = reg.find(graphicsBackend); - if (it == reg.end()) { - return false; - } - - const auto &windowBackends = it->second.windowBackends; - if (windowBackends.empty()) { - return true; - } - - for (const auto &wb : windowBackends) { - if (wb == windowBackend) { - return true; - } - } - - return false; -} - -std::vector -BackendFactory::getSupportedWindowBackends(const std::string &graphicsBackend) { - auto ® = registry(); - auto it = reg.find(graphicsBackend); - if (it != reg.end()) { - return it->second.windowBackends; - } - return {}; -} - -} // namespace graphics -} // namespace extra2d diff --git a/Extra2D/src/graphics/backends/opengl/gl_backend.cpp b/Extra2D/src/graphics/backends/opengl/gl_backend.cpp deleted file mode 100644 index d34b6e5..0000000 --- a/Extra2D/src/graphics/backends/opengl/gl_backend.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include - -namespace extra2d { -namespace graphics { - -namespace { - static bool s_openglBackendRegistered = false; -} - -/** - * @brief 初始化 OpenGL 后端注册 - */ -void initOpenGLBackend() { - if (s_openglBackendRegistered) { - return; - } - s_openglBackendRegistered = true; - - BackendFactory::reg( - "opengl", - []() -> UniquePtr { - return makeUnique(); - }, - {"sdl2", "glfw"} - ); -} - -namespace { - struct OpenGLBackendAutoReg { - OpenGLBackendAutoReg() { - initOpenGLBackend(); - } - }; - static OpenGLBackendAutoReg s_openglAutoReg; -} - -} // namespace graphics -} // namespace extra2d diff --git a/Extra2D/src/graphics/backends/opengl/gl_buffer.cpp b/Extra2D/src/graphics/backends/opengl/gl_buffer.cpp deleted file mode 100644 index 71d3eaa..0000000 --- a/Extra2D/src/graphics/backends/opengl/gl_buffer.cpp +++ /dev/null @@ -1,174 +0,0 @@ -#include -#include -#include -#include -#include - - -namespace extra2d { - -// ============================================================================ -// GLBuffer 实现 -// ============================================================================ - -GLBuffer::GLBuffer() = default; - -GLBuffer::~GLBuffer() { shutdown(); } - -bool GLBuffer::init(const BufferDesc &desc) { - if (bufferID_ != 0) { - shutdown(); - } - - type_ = desc.type; - usage_ = desc.usage; - size_ = desc.size; - target_ = convertType(type_); - glUsage_ = convertUsage(usage_); - - // 生成缓冲区 - glGenBuffers(1, &bufferID_); - if (bufferID_ == 0) { - E2D_LOG_ERROR("生成 OpenGL 缓冲区失败"); - return false; - } - - // 绑定并分配缓冲区 - glBindBuffer(target_, bufferID_); - glBufferData(target_, static_cast(size_), desc.initialData, - glUsage_); - glBindBuffer(target_, 0); - - // 追踪显存使用 - VRAMMgr::get().allocBuffer(size_); - - E2D_LOG_DEBUG("GLBuffer 已创建: ID={}, 大小={}, 类型={}, 用途={}", - bufferID_, size_, static_cast(type_), - static_cast(usage_)); - - return true; -} - -void GLBuffer::shutdown() { - if (bufferID_ != 0) { - if (mapped_) { - unmap(); - } - // 释放显存追踪 - VRAMMgr::get().freeBuffer(size_); - glDeleteBuffers(1, &bufferID_); - E2D_LOG_DEBUG("GLBuffer 已销毁: ID={}", bufferID_); - bufferID_ = 0; - } - size_ = 0; - mapped_ = false; - mappedPtr_ = nullptr; -} - -void GLBuffer::bind() { - if (bufferID_ != 0) { - glBindBuffer(target_, bufferID_); - } -} - -void GLBuffer::unbind() { glBindBuffer(target_, 0); } - -void GLBuffer::setData(const void *data, size_t size) { - if (bufferID_ == 0) { - return; - } - - bind(); - - // 如果大小相同,使用 glBufferSubData 更高效 - if (size == size_) { - glBufferSubData(target_, 0, static_cast(size), data); - } else { - // 大小不同,重新分配 - size_ = size; - glBufferData(target_, static_cast(size_), data, glUsage_); - } - - unbind(); -} - -void GLBuffer::updateData(const void *data, size_t offset, size_t size) { - if (bufferID_ == 0 || data == nullptr || size == 0) { - return; - } - - if (offset + size > size_) { - E2D_LOG_WARN( - "GLBuffer updateData out of bounds: offset={}, size={}, bufferSize={}", - offset, size, size_); - return; - } - - bind(); - glBufferSubData(target_, static_cast(offset), - static_cast(size), data); - unbind(); -} - -void *GLBuffer::map() { - if (bufferID_ == 0 || mapped_) { - return nullptr; - } - - bind(); - - // 使用 glMapBufferRange 替代 glMapBuffer,更现代且安全 - GLbitfield access = GL_MAP_WRITE_BIT; - if (usage_ == BufferUsage::Dynamic || usage_ == BufferUsage::Stream) { - access |= GL_MAP_INVALIDATE_BUFFER_BIT; // 暗示驱动可以丢弃旧数据 - } - - mappedPtr_ = - glMapBufferRange(target_, 0, static_cast(size_), access); - if (mappedPtr_) { - mapped_ = true; - } else { - E2D_LOG_ERROR("映射 GLBuffer 失败"); - } - - return mappedPtr_; -} - -void GLBuffer::unmap() { - if (!mapped_ || bufferID_ == 0) { - return; - } - - glUnmapBuffer(target_); - mapped_ = false; - mappedPtr_ = nullptr; - unbind(); -} - -GLenum GLBuffer::convertUsage(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; - } -} - -GLenum GLBuffer::convertType(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; - default: - return GL_ARRAY_BUFFER; - } -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/backends/opengl/gl_context.cpp b/Extra2D/src/graphics/backends/opengl/gl_context.cpp deleted file mode 100644 index fc7d2b3..0000000 --- a/Extra2D/src/graphics/backends/opengl/gl_context.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// GLContext 实现 -// ============================================================================ - -GLContext &GLContext::get() { - static GLContext instance; - return instance; -} - -bool GLContext::init() { - if (initialized_) { - return true; - } - - // 解析 OpenGL 版本 - parseVersion(); - - // 加载扩展(GLAD 已在 glad.c 中完成) - if (!loadExtensions()) { - E2D_LOG_ERROR("加载 OpenGL 扩展失败"); - return false; - } - - initialized_ = true; - - // 标记 GPU 上下文为有效 - GPUContext::get().markValid(); - - E2D_LOG_INFO("OpenGL 上下文已初始化"); - E2D_LOG_INFO(" 版本: {}", getVersionString()); - E2D_LOG_INFO(" 供应商: {}", getVendor()); - E2D_LOG_INFO(" 渲染器: {}", getRenderer()); - E2D_LOG_INFO(" 最大纹理大小: {}", getMaxTextureSize()); - E2D_LOG_INFO(" 最大纹理单元数: {}", getMaxTextureUnits()); - - return true; -} - -void GLContext::shutdown() { - // 标记 GPU 上下文为无效 - GPUContext::get().markInvalid(); - - initialized_ = false; - version_ = GLVersion{}; - maxTextureSize_ = -1; - maxTextureUnits_ = -1; - maxVertexAttribs_ = -1; - maxUniformBufferBindings_ = -1; -} - -std::string GLContext::getVersionString() const { - const char *version = reinterpret_cast(glGetString(GL_VERSION)); - return version ? version : "Unknown"; -} - -std::string GLContext::getVendor() const { - const char *vendor = reinterpret_cast(glGetString(GL_VENDOR)); - return vendor ? vendor : "Unknown"; -} - -std::string GLContext::getRenderer() const { - const char *renderer = - reinterpret_cast(glGetString(GL_RENDERER)); - return renderer ? renderer : "Unknown"; -} - -bool GLContext::hasExtension(const std::string &extension) const { - GLint numExtensions = 0; - glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); - - for (GLint i = 0; i < numExtensions; ++i) { - const char *ext = - reinterpret_cast(glGetStringi(GL_EXTENSIONS, i)); - if (ext && extension == ext) { - return true; - } - } - - return false; -} - -int GLContext::getMaxTextureSize() const { - if (maxTextureSize_ < 0) { - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize_); - } - return maxTextureSize_; -} - -int GLContext::getMaxTextureUnits() const { - if (maxTextureUnits_ < 0) { - glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits_); - } - return maxTextureUnits_; -} - -int GLContext::getMaxVertexAttribs() const { - if (maxVertexAttribs_ < 0) { - glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs_); - } - return maxVertexAttribs_; -} - -int GLContext::getMaxUniformBufferBindings() const { - if (maxUniformBufferBindings_ < 0) { - glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings_); - } - return maxUniformBufferBindings_; -} - -bool GLContext::hasVAO() const { - // OpenGL 3.0+ 或 OpenGL ES 3.0+ 原生支持 VAO - if (version_.es) { - return version_.major >= 3; - } - return version_.major > 3 || (version_.major == 3 && version_.minor >= 0); -} - -bool GLContext::hasFBO() const { - // OpenGL 3.0+ 或 OpenGL ES 2.0+ 原生支持 FBO - if (version_.es) { - return version_.major >= 2; - } - return version_.major >= 3; -} - -bool GLContext::hasShader() const { - // OpenGL 2.0+ 或 OpenGL ES 2.0+ 原生支持 Shader - if (version_.es) { - return version_.major >= 2; - } - return version_.major >= 2; -} - -void GLContext::parseVersion() { - const char *versionStr = - reinterpret_cast(glGetString(GL_VERSION)); - if (!versionStr) { - version_ = GLVersion{0, 0, false}; - return; - } - - std::string version(versionStr); - - // 检查是否为 OpenGL ES - if (version.find("OpenGL ES") != std::string::npos) { - version_.es = true; - // 解析 ES 版本号,格式如 "OpenGL ES 3.0" - std::sscanf(version.c_str(), "OpenGL ES %d.%d", &version_.major, - &version_.minor); - } else { - version_.es = false; - // 解析桌面版本号,格式如 "3.3.0 NVIDIA" - std::sscanf(version.c_str(), "%d.%d", &version_.major, &version_.minor); - } -} - -bool GLContext::loadExtensions() { - // GLAD 已经在 glad.c 中加载了所有扩展 - // 这里可以添加额外的扩展检查 - return true; -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/backends/opengl/gl_font_atlas.cpp b/Extra2D/src/graphics/backends/opengl/gl_font_atlas.cpp deleted file mode 100644 index 3a17c24..0000000 --- a/Extra2D/src/graphics/backends/opengl/gl_font_atlas.cpp +++ /dev/null @@ -1,362 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include - -// 在实现文件中定义 STB 实现 -#define STB_TRUETYPE_IMPLEMENTATION -#include - -#define STB_RECT_PACK_IMPLEMENTATION -#include - -namespace extra2d { - -// ============================================================================ -// GLFontAtlas 构造函数 -// 加载字体文件并初始化图集 -// ============================================================================ -GLFontAtlas::GLFontAtlas(const std::string &filepath, int fontSize, bool useSDF) - : useSDF_(useSDF), fontSize_(fontSize), lineHeight_(0.0f), ascent_(0.0f), - descent_(0.0f), lineGap_(0.0f), scale_(0.0f) { - - // 加载字体文件 - if (!initFont(filepath)) { - E2D_LOG_ERROR("初始化字体失败: {}", filepath); - return; - } - - // 计算字体缩放比例和度量 - scale_ = stbtt_ScaleForPixelHeight(&fontInfo_, static_cast(fontSize_)); - - int ascent, descent, lineGap; - stbtt_GetFontVMetrics(&fontInfo_, &ascent, &descent, &lineGap); - - ascent_ = static_cast(ascent) * scale_; - descent_ = static_cast(descent) * scale_; - lineGap_ = static_cast(lineGap) * scale_; - lineHeight_ = ascent_ - descent_ + lineGap_; - - // 创建图集纹理和打包上下文 - createAtlas(); - - // 预加载常用 ASCII 字符 - std::string asciiChars = " !\"#$%&'()*+,-./" - "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" - "abcdefghijklmnopqrstuvwxyz{|}~"; - for (char c : asciiChars) { - char32_t codepoint = static_cast(static_cast(c)); - cacheGlyph(codepoint); - } - - E2D_LOG_INFO("字体图集已创建: {} ({}px, {}x{})", filepath, fontSize_, - ATLAS_WIDTH, ATLAS_HEIGHT); -} - -// ============================================================================ -// GLFontAtlas 析构函数 -// ============================================================================ -GLFontAtlas::~GLFontAtlas() { - // 智能指针自动管理纹理资源 -} - -// ============================================================================ -// 获取字形信息 - 如果字形不存在则动态缓存 -// ============================================================================ -const Glyph *GLFontAtlas::getGlyph(char32_t codepoint) const { - auto it = glyphs_.find(codepoint); - if (it == glyphs_.end()) { - // 动态缓存新字形 - const_cast(this)->cacheGlyph(codepoint); - it = glyphs_.find(codepoint); - if (it == glyphs_.end()) { - return nullptr; - } - } - - // 返回静态存储的 Glyph 数据 - static Glyph glyph; - const auto &data = it->second; - glyph.width = data.width; - glyph.height = data.height; - glyph.bearingX = data.bearingX; - glyph.bearingY = data.bearingY; - glyph.advance = data.advance; - glyph.u0 = data.u0; - glyph.v0 = data.v0; - glyph.u1 = data.u1; - glyph.v1 = data.v1; - - return &glyph; -} - -// ============================================================================ -// 测量文本尺寸 - 支持多行文本 -// ============================================================================ -Vec2 GLFontAtlas::measureText(const std::string &text) { - float maxWidth = 0.0f; - float height = lineHeight_; - float currentWidth = 0.0f; - - size_t utf32Length = simdutf::count_utf8(text.data(), text.size()); - std::vector utf32Buffer(utf32Length); - size_t converted = simdutf::convert_utf8_to_utf32(text.data(), text.size(), - utf32Buffer.data()); - - for (size_t i = 0; i < converted; ++i) { - char32_t codepoint = utf32Buffer[i]; - - if (codepoint == '\n') { - maxWidth = std::max(maxWidth, currentWidth); - currentWidth = 0.0f; - height += lineHeight_; - continue; - } - - const Glyph *glyph = getGlyph(codepoint); - if (glyph) { - currentWidth += glyph->advance; - } - } - - maxWidth = std::max(maxWidth, currentWidth); - return Vec2(maxWidth, height); -} - -// ============================================================================ -// 初始化字体 - 加载字体文件到内存 -// ============================================================================ -bool GLFontAtlas::initFont(const std::string &filepath) { - // 读取字体文件到内存 - std::ifstream file(filepath, std::ios::binary | std::ios::ate); - if (!file.is_open()) { - E2D_LOG_ERROR("打开字体文件失败: {}", filepath); - return false; - } - - std::streamsize size = file.tellg(); - file.seekg(0, std::ios::beg); - - fontData_.resize(static_cast(size)); - if (!file.read(reinterpret_cast(fontData_.data()), size)) { - E2D_LOG_ERROR("读取字体文件失败: {}", filepath); - return false; - } - - // 初始化 STB 字体 - if (!stbtt_InitFont(&fontInfo_, fontData_.data(), 0)) { - E2D_LOG_ERROR("初始化 STB 字体失败: {}", filepath); - return false; - } - - return true; -} - -// ============================================================================ -// 创建图集纹理 - 初始化空白纹理和矩形打包上下文 -// ============================================================================ -void GLFontAtlas::createAtlas() { - // 统一使用 4 通道格式 (RGBA) - int channels = 4; - std::vector emptyData(ATLAS_WIDTH * ATLAS_HEIGHT * channels, 0); - texture_ = std::make_unique(ATLAS_WIDTH, ATLAS_HEIGHT, - emptyData.data(), channels); - // 所有字体都使用线性过滤,SDF的抗锯齿由着色器处理 - texture_->setFilter(true); - - // 初始化矩形打包上下文 - 持久化以支持增量打包 - packNodes_.resize(ATLAS_WIDTH); - stbrp_init_target(&packContext_, ATLAS_WIDTH, ATLAS_HEIGHT, packNodes_.data(), - ATLAS_WIDTH); - - // 预分配字形缓冲区 - // 假设最大字形尺寸为 fontSize * fontSize * 4 (RGBA) - size_t maxGlyphSize = static_cast(fontSize_ * fontSize_ * 4 * 4); - glyphBitmapCache_.reserve(maxGlyphSize); - glyphRgbaCache_.reserve(maxGlyphSize); -} - -// ============================================================================ -// 缓存字形 - 渲染字形到图集并存储信息 -// 使用 stb_rect_pack 进行矩形打包 -// ============================================================================ -void GLFontAtlas::cacheGlyph(char32_t codepoint) { - // 检查是否已存在 - if (glyphs_.find(codepoint) != glyphs_.end()) { - return; - } - - // 获取字形水平度量 - int advance, leftSideBearing; - stbtt_GetCodepointHMetrics(&fontInfo_, static_cast(codepoint), &advance, - &leftSideBearing); - float advancePx = static_cast(advance) * scale_; - - // SDF 渲染模式 - if (useSDF_) { - constexpr int SDF_PADDING = 8; - constexpr unsigned char ONEDGE_VALUE = 128; - constexpr float PIXEL_DIST_SCALE = 64.0f; - - int w = 0, h = 0, xoff = 0, yoff = 0; - unsigned char *sdf = stbtt_GetCodepointSDF( - &fontInfo_, scale_, static_cast(codepoint), SDF_PADDING, - ONEDGE_VALUE, PIXEL_DIST_SCALE, &w, &h, &xoff, &yoff); - - if (!sdf || w <= 0 || h <= 0) { - if (sdf) - stbtt_FreeSDF(sdf, nullptr); - // 创建空白字形(如空格) - GlyphData data{}; - data.advance = advancePx; - glyphs_[codepoint] = data; - return; - } - - // 使用 stb_rect_pack 打包矩形 - stbrp_rect rect; - rect.id = static_cast(codepoint); - rect.w = w + PADDING * 2; - rect.h = h + PADDING * 2; - - stbrp_pack_rects(&packContext_, &rect, 1); - if (!rect.was_packed) { - E2D_LOG_WARN("字体图集已满,无法缓存字符码点: {}", codepoint); - stbtt_FreeSDF(sdf, nullptr); - return; - } - - int atlasX = rect.x + PADDING; - int atlasY = rect.y + PADDING; - - // 创建字形数据 - GlyphData data; - data.width = static_cast(w); - data.height = static_cast(h); - data.bearingX = static_cast(xoff); - data.bearingY = static_cast(yoff); - data.advance = advancePx; - - // 计算 UV 坐标 - // stb_rect_pack 使用左上角为原点,OpenGL纹理使用左下角为原点 - // 需要翻转V坐标 - float v0 = static_cast(atlasY) / ATLAS_HEIGHT; - float v1 = static_cast(atlasY + h) / ATLAS_HEIGHT; - data.u0 = static_cast(atlasX) / ATLAS_WIDTH; - data.v0 = 1.0f - v1; // 翻转V坐标 - data.u1 = static_cast(atlasX + w) / ATLAS_WIDTH; - data.v1 = 1.0f - v0; // 翻转V坐标 - - glyphs_[codepoint] = data; - - // 将 SDF 单通道数据转换为 RGBA 格式(统一格式) - size_t pixelCount = static_cast(w) * static_cast(h); - glyphRgbaCache_.resize(pixelCount * 4); - for (size_t i = 0; i < pixelCount; ++i) { - uint8_t alpha = sdf[i]; - glyphRgbaCache_[i * 4 + 0] = 255; // R - glyphRgbaCache_[i * 4 + 1] = 255; // G - glyphRgbaCache_[i * 4 + 2] = 255; // B - glyphRgbaCache_[i * 4 + 3] = alpha; // A - SDF 值存储在 Alpha 通道 - } - - // 更新纹理 - OpenGL纹理坐标原点在左下角,需要将Y坐标翻转 - updateAtlas(atlasX, ATLAS_HEIGHT - atlasY - h, w, h, glyphRgbaCache_); - - stbtt_FreeSDF(sdf, nullptr); - return; - } - - // 普通位图渲染模式 - int x0 = 0, y0 = 0, x1 = 0, y1 = 0; - stbtt_GetCodepointBitmapBox(&fontInfo_, static_cast(codepoint), scale_, - scale_, &x0, &y0, &x1, &y1); - int w = x1 - x0; - int h = y1 - y0; - int xoff = x0; - // y0 是相对于基线的偏移(通常为负值,表示在基线上方) - // bearingY 应该是字形顶部相对于基线的偏移 - int yoff = y0; - - if (w <= 0 || h <= 0) { - // 空白字符(如空格) - GlyphData data{}; - data.advance = advancePx; - glyphs_[codepoint] = data; - return; - } - - // 使用预分配缓冲区渲染字形 - size_t pixelCount = static_cast(w) * static_cast(h); - glyphBitmapCache_.resize(pixelCount); - stbtt_MakeCodepointBitmap(&fontInfo_, glyphBitmapCache_.data(), w, h, w, - scale_, scale_, static_cast(codepoint)); - - // 使用 stb_rect_pack 打包矩形 - stbrp_rect rect; - rect.id = static_cast(codepoint); - rect.w = w + PADDING * 2; - rect.h = h + PADDING * 2; - - stbrp_pack_rects(&packContext_, &rect, 1); - - if (!rect.was_packed) { - E2D_LOG_WARN("字体图集已满,无法缓存字符码点: {}", codepoint); - return; - } - - int atlasX = rect.x + PADDING; - int atlasY = rect.y + PADDING; - - // 创建字形数据 - GlyphData data; - data.width = static_cast(w); - data.height = static_cast(h); - data.bearingX = static_cast(xoff); - data.bearingY = static_cast(yoff); - data.advance = advancePx; - - // 计算 UV 坐标 - // stb_rect_pack 使用左上角为原点,OpenGL纹理使用左下角为原点 - // 需要翻转V坐标 - float v0 = static_cast(atlasY) / ATLAS_HEIGHT; - float v1 = static_cast(atlasY + h) / ATLAS_HEIGHT; - data.u0 = static_cast(atlasX) / ATLAS_WIDTH; - data.v0 = 1.0f - v1; // 翻转V坐标 - data.u1 = static_cast(atlasX + w) / ATLAS_WIDTH; - data.v1 = 1.0f - v0; // 翻转V坐标 - - glyphs_[codepoint] = data; - - // 将单通道字形数据转换为 RGBA 格式(白色字形,Alpha 通道存储灰度) - glyphRgbaCache_.resize(pixelCount * 4); - for (size_t i = 0; i < pixelCount; ++i) { - uint8_t alpha = glyphBitmapCache_[i]; - glyphRgbaCache_[i * 4 + 0] = 255; // R - glyphRgbaCache_[i * 4 + 1] = 255; // G - glyphRgbaCache_[i * 4 + 2] = 255; // B - glyphRgbaCache_[i * 4 + 3] = alpha; // A - } - - // 更新纹理 - OpenGL纹理坐标原点在左下角,需要将Y坐标翻转 - updateAtlas(atlasX, ATLAS_HEIGHT - atlasY - h, w, h, glyphRgbaCache_); -} - -// ============================================================================ -// 更新图集纹理区域 -// ============================================================================ -void GLFontAtlas::updateAtlas(int x, int y, int width, int height, - const std::vector &data) { - if (texture_) { - texture_->bind(); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, - GL_UNSIGNED_BYTE, data.data()); - } -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/backends/opengl/gl_framebuffer.cpp b/Extra2D/src/graphics/backends/opengl/gl_framebuffer.cpp deleted file mode 100644 index a8cbb56..0000000 --- a/Extra2D/src/graphics/backends/opengl/gl_framebuffer.cpp +++ /dev/null @@ -1,272 +0,0 @@ -#include -#include -#include -#include - - -namespace extra2d { - -// ============================================================================ -// GLFramebuffer 实现 -// ============================================================================ - -GLFramebuffer::GLFramebuffer() = default; - -GLFramebuffer::~GLFramebuffer() { shutdown(); } - -bool GLFramebuffer::init(const FramebufferDesc &desc) { - if (fboID_ != 0) { - shutdown(); - } - - width_ = desc.width; - height_ = desc.height; - numColorAttachments_ = desc.colorAttachments; - hasDepth_ = desc.hasDepth; - hasStencil_ = desc.hasStencil; - - // 限制颜色附件数 - if (numColorAttachments_ > MAX_COLOR_ATTACHMENTS) { - numColorAttachments_ = MAX_COLOR_ATTACHMENTS; - } - - // 生成 FBO - glGenFramebuffers(1, &fboID_); - if (fboID_ == 0) { - E2D_LOG_ERROR("生成 OpenGL 帧缓冲区失败"); - return false; - } - - E2D_LOG_DEBUG("GLFramebuffer 已创建: ID={}, 大小={}x{}, 颜色附件={}", - fboID_, width_, height_, numColorAttachments_); - - return true; -} - -void GLFramebuffer::shutdown() { - if (fboID_ != 0) { - glDeleteFramebuffers(1, &fboID_); - E2D_LOG_DEBUG("GLFramebuffer 已销毁: ID={}", fboID_); - fboID_ = 0; - } - - // 清理纹理引用 - for (auto &tex : colorTextures_) { - tex.reset(); - } - depthTexture_.reset(); - depthStencilTexture_.reset(); - - hasInternalTextures_ = false; -} - -void GLFramebuffer::bind() { - if (fboID_ != 0) { - glBindFramebuffer(GL_FRAMEBUFFER, fboID_); - } -} - -void GLFramebuffer::unbind() { glBindFramebuffer(GL_FRAMEBUFFER, 0); } - -void GLFramebuffer::attachColorTexture(Ptr texture, int attachment) { - if (fboID_ == 0 || !texture || attachment < 0 || - attachment >= MAX_COLOR_ATTACHMENTS) { - return; - } - - bind(); - - // 获取 OpenGL 纹理 ID - GLuint texID = static_cast( - reinterpret_cast(texture->getNativeHandle())); - - glFramebufferTexture2D(GL_FRAMEBUFFER, getColorAttachment(attachment), - GL_TEXTURE_2D, texID, 0); - - colorTextures_[attachment] = texture; - - unbind(); -} - -void GLFramebuffer::attachDepthTexture(Ptr texture) { - if (fboID_ == 0 || !texture) { - return; - } - - bind(); - - // 获取 OpenGL 纹理 ID - GLuint texID = static_cast( - reinterpret_cast(texture->getNativeHandle())); - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - texID, 0); - - depthTexture_ = texture; - hasDepth_ = true; - hasStencil_ = false; - - unbind(); -} - -void GLFramebuffer::attachDepthStencilTexture(Ptr texture) { - if (fboID_ == 0 || !texture) { - return; - } - - bind(); - - // 获取 OpenGL 纹理 ID - GLuint texID = static_cast( - reinterpret_cast(texture->getNativeHandle())); - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, - GL_TEXTURE_2D, texID, 0); - - depthStencilTexture_ = texture; - hasDepth_ = true; - hasStencil_ = true; - - unbind(); -} - -bool GLFramebuffer::isComplete() { - if (fboID_ == 0) { - return false; - } - - bind(); - bool complete = checkStatus(); - unbind(); - - return complete; -} - -Ptr GLFramebuffer::getColorTexture(int attachment) const { - if (attachment >= 0 && attachment < MAX_COLOR_ATTACHMENTS) { - return colorTextures_[attachment]; - } - return nullptr; -} - -Ptr GLFramebuffer::getDepthTexture() const { return depthTexture_; } - -void GLFramebuffer::clear(const Color &color, bool clearColor, bool clearDepth, - bool clearStencil) { - if (fboID_ == 0) { - return; - } - - bind(); - - GLbitfield mask = 0; - - if (clearColor) { - mask |= GL_COLOR_BUFFER_BIT; - glClearColor(color.r, color.g, color.b, color.a); - } - - if (clearDepth) { - mask |= GL_DEPTH_BUFFER_BIT; - glClearDepthf(1.0f); - } - - if (clearStencil) { - mask |= GL_STENCIL_BUFFER_BIT; - glClearStencil(0); - } - - if (mask != 0) { - glClear(mask); - } - - unbind(); -} - -void GLFramebuffer::setViewport(int x, int y, int width, int height) { - glViewport(x, y, width, height); -} - -bool GLFramebuffer::readPixels(int x, int y, int width, int height, - std::vector &outData) { - if (fboID_ == 0 || width <= 0 || height <= 0) { - return false; - } - - // 计算需要的缓冲区大小 (RGBA8) - size_t dataSize = width * height * 4; - outData.resize(dataSize); - - bind(); - glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, outData.data()); - unbind(); - - return true; -} - -bool GLFramebuffer::createWithTextures(int width, int height, - PixelFormat colorFormat, - PixelFormat depthFormat) { - FramebufferDesc desc; - desc.width = width; - desc.height = height; - desc.colorAttachments = 1; - desc.hasDepth = (depthFormat != PixelFormat::RGBA8); - desc.hasStencil = (depthFormat == PixelFormat::Depth24Stencil8); - - if (!init(desc)) { - return false; - } - - hasInternalTextures_ = true; - - // 创建颜色纹理 - // 注意:这里简化处理,实际应该通过纹理工厂创建 - // 暂时返回 true,实际纹理创建由调用者处理 - - return true; -} - -bool GLFramebuffer::checkStatus() { - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - - switch (status) { - case GL_FRAMEBUFFER_COMPLETE: - return true; - case GL_FRAMEBUFFER_UNDEFINED: - E2D_LOG_ERROR("帧缓冲区不完整: GL_FRAMEBUFFER_UNDEFINED"); - break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - E2D_LOG_ERROR( - "帧缓冲区不完整: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); - break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - E2D_LOG_ERROR( - "帧缓冲区不完整: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"); - break; -#ifndef GL_ES_VERSION_2_0 - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - E2D_LOG_ERROR( - "帧缓冲区不完整: GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"); - break; - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - E2D_LOG_ERROR( - "帧缓冲区不完整: GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"); - break; -#endif - case GL_FRAMEBUFFER_UNSUPPORTED: - E2D_LOG_ERROR("帧缓冲区不完整: GL_FRAMEBUFFER_UNSUPPORTED"); - break; - default: - E2D_LOG_ERROR("帧缓冲区不完整: 未知错误 {}", status); - break; - } - - return false; -} - -GLenum GLFramebuffer::getColorAttachment(int index) { - return GL_COLOR_ATTACHMENT0 + index; -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/backends/opengl/gl_pipeline.cpp b/Extra2D/src/graphics/backends/opengl/gl_pipeline.cpp deleted file mode 100644 index cc7f168..0000000 --- a/Extra2D/src/graphics/backends/opengl/gl_pipeline.cpp +++ /dev/null @@ -1,235 +0,0 @@ -#include -#include -#include - - -namespace extra2d { - -// ============================================================================ -// GLPipeline 实现 -// ============================================================================ - -GLPipeline::GLPipeline() = default; - -GLPipeline::~GLPipeline() { shutdown(); } - -bool GLPipeline::init(const PipelineDesc &desc) { - if (initialized_) { - shutdown(); - } - - blendMode_ = desc.blendMode; - blendEnabled_ = desc.blendEnabled; - depthTest_ = desc.depthTest; - depthWrite_ = desc.depthWrite; - depthFunc_ = desc.depthFunc; - cullMode_ = desc.cullMode; - - initialized_ = true; - - E2D_LOG_DEBUG( - "GLPipeline initialized: blendMode={}, depthTest={}, cullMode={}", - static_cast(blendMode_), depthTest_, static_cast(cullMode_)); - - return true; -} - -void GLPipeline::shutdown() { initialized_ = false; } - -void GLPipeline::bind() { - if (!initialized_) { - return; - } - - applyAllStates(); -} - -void GLPipeline::unbind() { - // OpenGL 不需要显式解绑管线 -} - -void GLPipeline::setBlendMode(BlendMode mode) { - blendMode_ = mode; - applyBlendState(); -} - -void GLPipeline::setDepthTest(bool enabled) { - depthTest_ = enabled; - applyDepthState(); -} - -void GLPipeline::setDepthWrite(bool enabled) { - depthWrite_ = enabled; - applyDepthState(); -} - -void GLPipeline::setDepthFunc(DepthFunc func) { - depthFunc_ = func; - applyDepthState(); -} - -void GLPipeline::setCullMode(CullMode mode) { - cullMode_ = mode; - applyCullState(); -} - -void GLPipeline::setViewport(int x, int y, int width, int height) { - viewportX_ = x; - viewportY_ = y; - viewportWidth_ = width; - viewportHeight_ = height; - - // 检查缓存,避免冗余调用 - if (x != cachedViewportX_ || y != cachedViewportY_ || - width != cachedViewportWidth_ || height != cachedViewportHeight_) { - glViewport(x, y, width, height); - cachedViewportX_ = x; - cachedViewportY_ = y; - cachedViewportWidth_ = width; - cachedViewportHeight_ = height; - } -} - -void GLPipeline::getViewport(int &x, int &y, int &width, int &height) const { - x = viewportX_; - y = viewportY_; - width = viewportWidth_; - height = viewportHeight_; -} - -void GLPipeline::applyAllStates() { - applyBlendState(); - applyDepthState(); - applyCullState(); - - // 应用视口 - if (viewportWidth_ > 0 && viewportHeight_ > 0) { - setViewport(viewportX_, viewportY_, viewportWidth_, viewportHeight_); - } -} - -void GLPipeline::applyBlendState() { - // 检查是否需要启用/禁用混合 - if (blendEnabled_ != cachedBlendEnabled_) { - if (blendEnabled_) { - glEnable(GL_BLEND); - } else { - glDisable(GL_BLEND); - } - cachedBlendEnabled_ = blendEnabled_; - } - - // 如果禁用了混合,不需要设置混合函数 - if (!blendEnabled_) { - return; - } - - // 检查混合模式是否改变 - if (blendMode_ != cachedBlendMode_) { - GLenum srcFactor, dstFactor; - getBlendFactors(blendMode_, srcFactor, dstFactor); - glBlendFunc(srcFactor, dstFactor); - cachedBlendMode_ = blendMode_; - } -} - -void GLPipeline::applyDepthState() { - // 深度测试 - if (depthTest_ != cachedDepthTest_) { - if (depthTest_) { - glEnable(GL_DEPTH_TEST); - } else { - glDisable(GL_DEPTH_TEST); - } - cachedDepthTest_ = depthTest_; - } - - // 深度写入 - if (depthWrite_ != cachedDepthWrite_) { - glDepthMask(depthWrite_ ? GL_TRUE : GL_FALSE); - cachedDepthWrite_ = depthWrite_; - } - - // 深度函数 - if (depthFunc_ != cachedDepthFunc_) { - glDepthFunc(convertDepthFunc(depthFunc_)); - cachedDepthFunc_ = depthFunc_; - } -} - -void GLPipeline::applyCullState() { - // 检查裁剪模式是否改变 - if (cullMode_ != cachedCullMode_) { - if (cullMode_ == CullMode::None) { - glDisable(GL_CULL_FACE); - } else { - glEnable(GL_CULL_FACE); - glCullFace(convertCullMode(cullMode_)); - } - cachedCullMode_ = cullMode_; - } -} - -void GLPipeline::getBlendFactors(BlendMode mode, GLenum &srcFactor, - GLenum &dstFactor) { - switch (mode) { - case BlendMode::None: - srcFactor = GL_ONE; - dstFactor = GL_ZERO; - break; - case BlendMode::Alpha: - srcFactor = GL_SRC_ALPHA; - dstFactor = GL_ONE_MINUS_SRC_ALPHA; - break; - case BlendMode::Additive: - srcFactor = GL_SRC_ALPHA; - dstFactor = GL_ONE; - break; - case BlendMode::Multiply: - srcFactor = GL_DST_COLOR; - dstFactor = GL_ONE_MINUS_SRC_ALPHA; - break; - default: - srcFactor = GL_SRC_ALPHA; - dstFactor = GL_ONE_MINUS_SRC_ALPHA; - break; - } -} - -GLenum GLPipeline::convertDepthFunc(DepthFunc func) { - switch (func) { - case DepthFunc::Never: - return GL_NEVER; - case DepthFunc::Less: - return GL_LESS; - case DepthFunc::Equal: - return GL_EQUAL; - case DepthFunc::LessEqual: - return GL_LEQUAL; - case DepthFunc::Greater: - return GL_GREATER; - case DepthFunc::NotEqual: - return GL_NOTEQUAL; - case DepthFunc::GreaterEqual: - return GL_GEQUAL; - case DepthFunc::Always: - return GL_ALWAYS; - default: - return GL_LESS; - } -} - -GLenum GLPipeline::convertCullMode(CullMode mode) { - switch (mode) { - case CullMode::Front: - return GL_FRONT; - case CullMode::Back: - return GL_BACK; - case CullMode::Both: - return GL_FRONT_AND_BACK; - default: - return GL_BACK; - } -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/backends/opengl/gl_renderer.cpp b/Extra2D/src/graphics/backends/opengl/gl_renderer.cpp deleted file mode 100644 index f4ded47..0000000 --- a/Extra2D/src/graphics/backends/opengl/gl_renderer.cpp +++ /dev/null @@ -1,1086 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// VBO 初始大小(用于 VRAM 跟踪) -static constexpr size_t SHAPE_VBO_SIZE = 1024 * sizeof(float); - -/** - * @brief 构造函数,初始化OpenGL渲染器成员变量 - */ -GLRenderer::GLRenderer() - : window_(nullptr), shapeVao_(0), lineVao_(0), vsync_(true), - shapeVertexCount_(0), currentShapeMode_(GL_TRIANGLES), - lineVertexCount_(0), currentLineWidth_(1.0f) { - resetStats(); - for (auto &v : shapeVertexCache_) { - v = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; - } - for (auto &v : lineVertexCache_) { - v = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; - } -} - -/** - * @brief 析构函数,调用shutdown释放资源 - */ -GLRenderer::~GLRenderer() { shutdown(); } - -/** - * @brief 初始化OpenGL渲染器 - * @param window 窗口指针 - * @return 初始化成功返回true,失败返回false - */ -bool GLRenderer::init(IWindow *window) { - window_ = window; - - // 初始化 OpenGL 上下文(Switch 平台已通过 SDL2 + EGL 初始化,GLContext - // 会处理兼容性) - if (!GLContext::get().init()) { - E2D_LOG_ERROR("初始化 OpenGL 上下文失败"); - return false; - } - - // 初始化精灵批渲染器 - if (!spriteBatch_.init()) { - E2D_LOG_ERROR("初始化精灵批处理失败"); - return false; - } - - // 初始化形状渲染 - initShapeRendering(); - - // 初始化管线状态管理 - PipelineDesc pipelineDesc; - pipelineDesc.blendMode = BlendMode::Alpha; - pipelineDesc.depthTest = false; - pipelineDesc.depthWrite = false; - if (!pipeline_.init(pipelineDesc)) { - E2D_LOG_ERROR("初始化 GLPipeline 失败"); - return false; - } - - // 应用初始管线状态 - pipeline_.applyAllStates(); - - // 标记 GPU 上下文为有效 - GPUContext::get().markValid(); - - E2D_LOG_INFO("OpenGL 渲染器初始化成功"); - E2D_LOG_INFO("OpenGL 版本: {}", GLContext::get().getVersionString()); - - return true; -} - -/** - * @brief 关闭渲染器,释放所有GPU资源 - */ -void GLRenderer::shutdown() { - // 标记 GPU 上下文为无效 - // 这会在销毁 OpenGL 上下文之前通知所有 GPU 资源 - GPUContext::get().markInvalid(); - - spriteBatch_.shutdown(); - - // 关闭 GLBuffer(自动释放 VBO) - lineBuffer_.shutdown(); - shapeBuffer_.shutdown(); - - // 删除 VAO(VAO 仍然手动管理) - if (lineVao_ != 0) { - glDeleteVertexArrays(1, &lineVao_); - lineVao_ = 0; - } - if (shapeVao_ != 0) { - glDeleteVertexArrays(1, &shapeVao_); - shapeVao_ = 0; - } - - // 关闭 OpenGL 上下文 - GLContext::get().shutdown(); -} - -/** - * @brief 开始新帧,清除颜色缓冲区并重置统计信息 - * @param clearColor 清屏颜色 - */ -void GLRenderer::beginFrame(const Color &clearColor) { - // 应用管线状态 - pipeline_.applyAllStates(); - - glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); - glClear(GL_COLOR_BUFFER_BIT); - resetStats(); -} - -/** - * @brief 结束当前帧,刷新所有待处理的渲染批次 - */ -void GLRenderer::endFrame() { - // 刷新所有待处理的精灵批次(自动批处理) - if (autoBatchEnabled_ && batchActive_) { - flush(); - } - // 刷新所有待处理的形状批次 - flushShapeBatch(); - // 刷新所有待处理的线条批次 - flushLineBatch(); -} - -/** - * @brief 设置视口区域 - * @param x 视口左下角X坐标 - * @param y 视口左下角Y坐标 - * @param width 视口宽度 - * @param height 视口高度 - */ -void GLRenderer::setViewport(int x, int y, int width, int height) { - // 使用 GLPipeline 管理视口状态 - pipeline_.setViewport(x, y, width, height); -} - -/** - * @brief 设置垂直同步 - * @param enabled true启用垂直同步,false禁用 - */ -void GLRenderer::setVSync(bool enabled) { - vsync_ = enabled; - // 通过窗口接口设置垂直同步 - if (window_) { - window_->setVSync(enabled); - } -} - -/** - * @brief 设置混合模式 - * @param mode 混合模式枚举值 - */ -void GLRenderer::setBlendMode(BlendMode mode) { - // 使用 GLPipeline 管理混合状态 - pipeline_.setBlendMode(mode); -} - -/** - * @brief 设置视图投影矩阵 - * @param matrix 4x4视图投影矩阵 - */ -void GLRenderer::setViewProjection(const glm::mat4 &matrix) { - viewProjection_ = matrix; -} - -/** - * @brief 压入变换矩阵到变换栈 - * @param transform 变换矩阵 - */ -void GLRenderer::pushTransform(const glm::mat4 &transform) { - if (transformStack_.empty()) { - transformStack_.push_back(transform); - } else { - transformStack_.push_back(transformStack_.back() * transform); - } -} - -/** - * @brief 从变换栈弹出顶部变换矩阵 - */ -void GLRenderer::popTransform() { - if (!transformStack_.empty()) { - transformStack_.pop_back(); - } -} - -/** - * @brief 获取当前累积的变换矩阵 - * @return 当前变换矩阵,如果栈为空则返回单位矩阵 - */ -glm::mat4 GLRenderer::getCurrentTransform() const { - if (transformStack_.empty()) { - return glm::mat4(1.0f); - } - return transformStack_.back(); -} - -/** - * @brief 创建纹理对象 - * @param width 纹理宽度 - * @param height 纹理高度 - * @param pixels 像素数据指针 - * @param channels 颜色通道数 - * @return 创建的纹理智能指针 - */ -Ptr GLRenderer::createTexture(int width, int height, - const uint8_t *pixels, int channels) { - return makePtr(width, height, pixels, channels); -} - -/** - * @brief 从文件加载纹理 - * @param filepath 纹理文件路径 - * @return 加载的纹理智能指针 - */ -Ptr GLRenderer::loadTexture(const std::string &filepath) { - return makePtr(filepath); -} - -/** - * @brief 确保批处理已激活(自动批处理内部使用) - */ -void GLRenderer::ensureBatchActive() { - if (!batchActive_) { - spriteBatch_.begin(viewProjection_); - batchActive_ = true; - currentBatchTexture_ = nullptr; - pendingSprites_.clear(); - } -} - -/** - * @brief 提交待处理的精灵(自动批处理内部使用) - */ -void GLRenderer::submitPendingSprites() { - if (pendingSprites_.empty()) { - return; - } - - // 提交所有待处理的精灵 - spriteBatch_.drawBatch(*currentBatchTexture_, pendingSprites_); - pendingSprites_.clear(); - currentBatchTexture_ = nullptr; -} - -/** - * @brief 开始手动精灵批处理(高级用法) - * @note 一般情况下不需要调用,drawSprite/drawText 会自动管理批处理 - */ -void GLRenderer::beginSpriteBatch() { - // 如果自动批处理已激活,先提交 - if (autoBatchEnabled_ && batchActive_) { - flush(); - } - // 禁用自动批处理,进入手动模式 - autoBatchEnabled_ = false; - spriteBatch_.begin(viewProjection_); - batchActive_ = true; -} - -/** - * @brief 绘制精灵(带完整参数) - * @param texture 纹理引用 - * @param destRect 目标矩形(屏幕坐标) - * @param srcRect 源矩形(纹理坐标) - * @param tint 着色颜色 - * @param rotation 旋转角度(度) - * @param anchor 锚点位置(0-1范围) - */ -void GLRenderer::drawSprite(const Texture &texture, const Rect &destRect, - const Rect &srcRect, const Color &tint, - float rotation, const Vec2 &anchor) { - // 自动批处理模式 - if (autoBatchEnabled_) { - ensureBatchActive(); - - // 如果纹理变化或缓冲区满,先提交当前批次 - if (currentBatchTexture_ != &texture || - pendingSprites_.size() >= MAX_BATCH_SPRITES) { - submitPendingSprites(); - currentBatchTexture_ = &texture; - } - - // 创建精灵数据 - SpriteData data; - data.position = Vec2(destRect.origin.x, destRect.origin.y); - data.size = Vec2(destRect.size.width, destRect.size.height); - - Texture *tex = const_cast(&texture); - float texW = static_cast(tex->getWidth()); - float texH = static_cast(tex->getHeight()); - - // 纹理坐标计算 - float u1 = srcRect.origin.x / texW; - float u2 = (srcRect.origin.x + srcRect.size.width) / texW; - float v1 = srcRect.origin.y / texH; - float v2 = (srcRect.origin.y + srcRect.size.height) / texH; - - data.uvRect = Rect(Vec2(glm::min(u1, u2), glm::min(v1, v2)), - Size(glm::abs(u2 - u1), glm::abs(v2 - v1))); - - data.color = tint; - data.rotation = rotation * 3.14159f / 180.0f; - data.pivot = Vec2(anchor.x, anchor.y); - - // 添加到待处理列表 - pendingSprites_.push_back(data); - } else { - // 手动批处理模式 - SpriteData data; - data.position = Vec2(destRect.origin.x, destRect.origin.y); - data.size = Vec2(destRect.size.width, destRect.size.height); - - Texture *tex = const_cast(&texture); - float texW = static_cast(tex->getWidth()); - float texH = static_cast(tex->getHeight()); - - float u1 = srcRect.origin.x / texW; - float u2 = (srcRect.origin.x + srcRect.size.width) / texW; - float v1 = srcRect.origin.y / texH; - float v2 = (srcRect.origin.y + srcRect.size.height) / texH; - - data.uvRect = Rect(Vec2(glm::min(u1, u2), glm::min(v1, v2)), - Size(glm::abs(u2 - u1), glm::abs(v2 - v1))); - - data.color = tint; - data.rotation = rotation * 3.14159f / 180.0f; - data.pivot = Vec2(anchor.x, anchor.y); - - spriteBatch_.draw(texture, data); - } -} - -/** - * @brief 绘制精灵(简化版本) - * @param texture 纹理引用 - * @param position 绘制位置 - * @param tint 着色颜色 - */ -void GLRenderer::drawSprite(const Texture &texture, const Vec2 &position, - const Color &tint) { - Rect destRect(position.x, position.y, static_cast(texture.getWidth()), - static_cast(texture.getHeight())); - Rect srcRect(0, 0, static_cast(texture.getWidth()), - static_cast(texture.getHeight())); - drawSprite(texture, destRect, srcRect, tint, 0.0f, Vec2(0, 0)); -} - -/** - * @brief 结束手动精灵批处理并提交绘制 - * @note 一般情况下不需要调用 - */ -void GLRenderer::endSpriteBatch() { - if (autoBatchEnabled_) { - // 自动模式下,只是标记批处理结束 - flush(); - } else { - // 手动模式下,提交批处理并恢复自动模式 - spriteBatch_.end(); - stats_.drawCalls += spriteBatch_.getDrawCallCount(); - batchActive_ = false; - autoBatchEnabled_ = true; - } -} - -/** - * @brief 立即提交当前批处理 - * @note 手动控制批处理提交时机,一般情况下不需要调用 - */ -void GLRenderer::flush() { - if (autoBatchEnabled_ && batchActive_) { - submitPendingSprites(); - spriteBatch_.end(); - stats_.drawCalls += spriteBatch_.getDrawCallCount(); - batchActive_ = false; - currentBatchTexture_ = nullptr; - } -} - -/** - * @brief 绘制线段 - * @param start 起点坐标 - * @param end 终点坐标 - * @param color 线条颜色 - * @param width 线条宽度 - */ -void GLRenderer::drawLine(const Vec2 &start, const Vec2 &end, - const Color &color, float width) { - // 如果线宽改变,需要先刷新线条批次 - if (width != currentLineWidth_) { - flushLineBatch(); - currentLineWidth_ = width; - } - - // 添加两个顶点到线条缓冲区 - addLineVertex(start.x, start.y, color); - addLineVertex(end.x, end.y, color); -} - -void GLRenderer::drawRect(const Rect &rect, const Color &color, float width) { - // 如果线宽改变,需要先刷新线条批次 - if (width != currentLineWidth_) { - flushLineBatch(); - currentLineWidth_ = width; - } - - float x1 = rect.origin.x; - float y1 = rect.origin.y; - float x2 = rect.origin.x + rect.size.width; - float y2 = rect.origin.y + rect.size.height; - - // 4条线段 = 8个顶点 - // 上边 - addLineVertex(x1, y1, color); - addLineVertex(x2, y1, color); - // 右边 - addLineVertex(x2, y1, color); - addLineVertex(x2, y2, color); - // 下边 - addLineVertex(x2, y2, color); - addLineVertex(x1, y2, color); - // 左边 - addLineVertex(x1, y2, color); - addLineVertex(x1, y1, color); -} - -/** - * @brief 填充矩形 - * @param rect 矩形区域 - * @param color 填充颜色 - */ -void GLRenderer::fillRect(const Rect &rect, const Color &color) { - // 提交当前批次(如果模式不同) - submitShapeBatch(GL_TRIANGLES); - - // 添加两个三角形组成矩形(6个顶点) - float x1 = rect.origin.x; - float y1 = rect.origin.y; - float x2 = rect.origin.x + rect.size.width; - float y2 = rect.origin.y + rect.size.height; - - // 三角形1: (x1,y1), (x2,y1), (x2,y2) - addShapeVertex(x1, y1, color); - addShapeVertex(x2, y1, color); - addShapeVertex(x2, y2, color); - - // 三角形2: (x1,y1), (x2,y2), (x1,y2) - addShapeVertex(x1, y1, color); - addShapeVertex(x2, y2, color); - addShapeVertex(x1, y2, color); -} - -/** - * @brief 绘制圆形边框 - * @param center 圆心坐标 - * @param radius 半径 - * @param color 边框颜色 - * @param segments 分段数 - * @param width 线条宽度 - */ -void GLRenderer::drawCircle(const Vec2 ¢er, float radius, - const Color &color, int segments, float width) { - // 限制段数不超过缓存大小 - if (segments > static_cast(MAX_CIRCLE_SEGMENTS)) { - segments = static_cast(MAX_CIRCLE_SEGMENTS); - } - - // 如果线宽改变,需要先刷新线条批次 - if (width != currentLineWidth_) { - flushLineBatch(); - currentLineWidth_ = width; - } - - // 使用线条批处理绘制圆形 - for (int i = 0; i < segments; ++i) { - float angle1 = - 2.0f * 3.14159f * static_cast(i) / static_cast(segments); - float angle2 = 2.0f * 3.14159f * static_cast(i + 1) / - static_cast(segments); - - addLineVertex(center.x + radius * cosf(angle1), - center.y + radius * sinf(angle1), color); - addLineVertex(center.x + radius * cosf(angle2), - center.y + radius * sinf(angle2), color); - } -} - -/** - * @brief 填充圆形 - * @param center 圆心坐标 - * @param radius 半径 - * @param color 填充颜色 - * @param segments 分段数 - */ -void GLRenderer::fillCircle(const Vec2 ¢er, float radius, - const Color &color, int segments) { - // 限制段数不超过缓存大小 - if (segments > static_cast(MAX_CIRCLE_SEGMENTS)) { - segments = static_cast(MAX_CIRCLE_SEGMENTS); - } - - // 提交当前批次(如果模式不同) - submitShapeBatch(GL_TRIANGLES); - - // 使用三角形扇形填充圆 - // 中心点 + 边缘点 - for (int i = 0; i < segments; ++i) { - float angle1 = - 2.0f * 3.14159f * static_cast(i) / static_cast(segments); - float angle2 = 2.0f * 3.14159f * static_cast(i + 1) / - static_cast(segments); - - // 每个三角形:中心 -> 边缘点1 -> 边缘点2 - addShapeVertex(center.x, center.y, color); - addShapeVertex(center.x + radius * cosf(angle1), - center.y + radius * sinf(angle1), color); - addShapeVertex(center.x + radius * cosf(angle2), - center.y + radius * sinf(angle2), color); - } -} - -/** - * @brief 绘制三角形边框 - * @param p1 第一个顶点 - * @param p2 第二个顶点 - * @param p3 第三个顶点 - * @param color 边框颜色 - * @param width 线条宽度 - */ -void GLRenderer::drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, - const Color &color, float width) { - drawLine(p1, p2, color, width); - drawLine(p2, p3, color, width); - drawLine(p3, p1, color, width); -} - -/** - * @brief 填充三角形 - * @param p1 第一个顶点 - * @param p2 第二个顶点 - * @param p3 第三个顶点 - * @param color 填充颜色 - */ -void GLRenderer::fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, - const Color &color) { - submitShapeBatch(GL_TRIANGLES); - - addShapeVertex(p1.x, p1.y, color); - addShapeVertex(p2.x, p2.y, color); - addShapeVertex(p3.x, p3.y, color); -} - -/** - * @brief 绘制多边形边框 - * @param points 顶点数组 - * @param color 边框颜色 - * @param width 线条宽度 - */ -void GLRenderer::drawPolygon(const std::vector &points, - const Color &color, float width) { - if (points.size() < 2) - return; - - // 如果线宽改变,需要先刷新线条批次 - if (width != currentLineWidth_) { - flushLineBatch(); - currentLineWidth_ = width; - } - - // 绘制所有边 - for (size_t i = 0; i < points.size(); ++i) { - const Vec2 &p1 = points[i]; - const Vec2 &p2 = points[(i + 1) % points.size()]; - addLineVertex(p1.x, p1.y, color); - addLineVertex(p2.x, p2.y, color); - } -} - -/** - * @brief 填充多边形 - * @param points 顶点数组 - * @param color 填充颜色 - */ -void GLRenderer::fillPolygon(const std::vector &points, - const Color &color) { - if (points.size() < 3) - return; - - submitShapeBatch(GL_TRIANGLES); - - // 使用三角形扇形填充 - // 从第一个点开始,每两个相邻点组成一个三角形 - for (size_t i = 1; i < points.size() - 1; ++i) { - addShapeVertex(points[0].x, points[0].y, color); - addShapeVertex(points[i].x, points[i].y, color); - addShapeVertex(points[i + 1].x, points[i + 1].y, color); - } -} - -/** - * @brief 创建字体图集 - * @param filepath 字体文件路径 - * @param fontSize 字体大小 - * @param useSDF 是否使用SDF渲染 - * @return 创建的字体图集智能指针 - */ -Ptr GLRenderer::createFontAtlas(const std::string &filepath, - int fontSize, bool useSDF) { - return makePtr(filepath, fontSize, useSDF); -} - -/** - * @brief 绘制文本(使用Vec2位置) - * @param font 字体图集引用 - * @param text 文本内容 - * @param position 绘制位置 - * @param color 文本颜色 - */ -void GLRenderer::drawText(const FontAtlas &font, const std::string &text, - const Vec2 &position, const Color &color) { - drawText(font, text, position.x, position.y, color); -} - -/** - * @brief 绘制文本(使用浮点坐标) - * @param font 字体图集引用 - * @param text 文本内容 - * @param x X坐标 - * @param y Y坐标 - * @param color 文本颜色 - */ -void GLRenderer::drawText(const FontAtlas &font, const std::string &text, - float x, float y, const Color &color) { - float cursorX = x; - float cursorY = y; - float baselineY = cursorY + font.getAscent(); - - // 检查是否为SDF字体 - bool isSDF = font.isSDF(); - - // 如果是SDF字体,切换到SDF着色器 - if (isSDF && sdfFontShader_) { - // 先提交当前普通批处理 - if (autoBatchEnabled_ && !pendingSprites_.empty()) { - submitPendingSprites(); - } - - // 使用SDF着色器开始新的批处理 - // 设置需要动态计算的uniform值 - UniformValueMap sdfUniformValues; - sdfUniformValues["u_viewProjection"] = viewProjection_; - if (font.getTexture()) { - sdfUniformValues["u_textureSize"] = ShaderUniformValue( - glm::vec2(static_cast(font.getTexture()->getWidth()), - static_cast(font.getTexture()->getHeight()))); - } - // 设置额外的uniform值,让submitBatch使用 - spriteBatch_.setExtraUniforms(sdfUniformValues); - spriteBatch_.begin(viewProjection_, sdfFontShader_); - } else { - // 确保批处理已激活(自动批处理) - if (autoBatchEnabled_) { - ensureBatchActive(); - } - - // 检查纹理变化,如果纹理不同则先提交当前批次 - if (autoBatchEnabled_ && currentBatchTexture_ != nullptr && - currentBatchTexture_ != font.getTexture()) { - submitPendingSprites(); - } - if (autoBatchEnabled_) { - currentBatchTexture_ = font.getTexture(); - } - } - - // 收集所有字符数据用于批处理 - std::vector sprites; - sprites.reserve(text.size()); // 预分配空间 - - for (char c : text) { - char32_t codepoint = static_cast(static_cast(c)); - if (codepoint == '\n') { - // 换行时,将当前行添加到待处理列表 - if (!sprites.empty()) { - if (isSDF && sdfFontShader_) { - // SDF模式直接提交 - spriteBatch_.drawBatch(*font.getTexture(), sprites); - } else if (autoBatchEnabled_) { - pendingSprites_.insert(pendingSprites_.end(), sprites.begin(), - sprites.end()); - } else { - // 手动模式直接提交 - spriteBatch_.drawBatch(*font.getTexture(), sprites); - } - sprites.clear(); - } - cursorX = x; - cursorY += font.getLineHeight(); - baselineY = cursorY + font.getAscent(); - continue; - } - - const Glyph *glyph = font.getGlyph(codepoint); - if (glyph) { - float penX = cursorX; - cursorX += glyph->advance; - - // 使用 epsilon 比较浮点数,避免精度问题 - constexpr float EPSILON = 0.001f; - if (glyph->width < EPSILON || glyph->height < EPSILON) { - continue; - } - - // 计算字形位置 - // bearingX: 水平偏移(从左边缘到字形左边缘) - // bearingY: 垂直偏移(从基线到字形顶部,通常为负值) - float xPos = penX + glyph->bearingX; - float yPos = baselineY + glyph->bearingY; - - SpriteData data; - // 设置精灵中心位置(精灵批处理使用中心点) - data.position = - Vec2(xPos + glyph->width * 0.5f, yPos + glyph->height * 0.5f); - data.size = Vec2(glyph->width, glyph->height); - data.uvRect = Rect(Vec2(glyph->u0, glyph->v0), - Size(glyph->u1 - glyph->u0, glyph->v1 - glyph->v0)); - data.color = color; - data.rotation = 0.0f; - // pivot (0.5, 0.5) 表示中心点,这样 position 就是精灵中心 - data.pivot = Vec2(0.5f, 0.5f); - - sprites.push_back(data); - - // 自动批处理:如果缓冲区满,先提交当前批次 - if (!isSDF && autoBatchEnabled_ && sprites.size() >= MAX_BATCH_SPRITES) { - pendingSprites_.insert(pendingSprites_.end(), sprites.begin(), - sprites.end()); - sprites.clear(); - } - } - } - - // 提交剩余的字符 - if (!sprites.empty()) { - if (isSDF && sdfFontShader_) { - // SDF模式直接提交 - spriteBatch_.drawBatch(*font.getTexture(), sprites); - } else if (autoBatchEnabled_) { - pendingSprites_.insert(pendingSprites_.end(), sprites.begin(), - sprites.end()); - } else { - // 手动模式下直接提交 - spriteBatch_.drawBatch(*font.getTexture(), sprites); - } - } - - // 如果是SDF字体,结束批处理并恢复普通着色器 - if (isSDF && sdfFontShader_) { - spriteBatch_.end(); - // 清除额外的uniform值 - spriteBatch_.clearExtraUniforms(); - // 恢复默认的sprite着色器 - auto defaultShader = ShaderManager::getInstance().getBuiltin("sprite"); - if (defaultShader) { - spriteBatch_.setShader(defaultShader); - } - } -} - -/** - * @brief 重置渲染统计信息 - */ -void GLRenderer::resetStats() { stats_ = Stats{}; } - -/** - * @brief 初始化形状渲染所需的OpenGL资源(VAO、VBO、着色器) - */ -void GLRenderer::initShapeRendering() { - // 从ShaderManager获取形状着色器 - shapeShader_ = ShaderManager::getInstance().getBuiltin("shape"); - if (!shapeShader_) { - E2D_LOG_WARN("获取内置形状着色器失败,尝试从管理器加载"); - if (!ShaderManager::getInstance().isInitialized()) { - E2D_LOG_ERROR("ShaderManager 未初始化,形状渲染可能失败"); - } - } - - // 加载SDF字体着色器 - sdfFontShader_ = ShaderManager::getInstance().getBuiltin("sdf_font"); - if (!sdfFontShader_) { - E2D_LOG_WARN("获取SDF字体着色器失败,SDF字体将使用普通渲染"); - } else { - E2D_LOG_INFO("SDF字体着色器加载成功"); - } - - // 初始化形状 GLBuffer(使用 Dynamic 使用模式,因为每帧都会更新) - BufferDesc shapeBufferDesc; - shapeBufferDesc.type = BufferType::Vertex; - shapeBufferDesc.usage = BufferUsage::Dynamic; - shapeBufferDesc.size = MAX_SHAPE_VERTICES * sizeof(ShapeVertex); - if (!shapeBuffer_.init(shapeBufferDesc)) { - E2D_LOG_ERROR("初始化形状缓冲区失败"); - } - - // 创建形状 VAO(手动管理,用于顶点属性配置) - glGenVertexArrays(1, &shapeVao_); - glBindVertexArray(shapeVao_); - shapeBuffer_.bind(); - - // 位置属性 (location = 0) - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex), - reinterpret_cast(offsetof(ShapeVertex, x))); - - // 颜色属性 (location = 1) - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex), - reinterpret_cast(offsetof(ShapeVertex, r))); - - glBindVertexArray(0); - - // 初始化线条 GLBuffer(使用 Dynamic 使用模式,因为每帧都会更新) - BufferDesc lineBufferDesc; - lineBufferDesc.type = BufferType::Vertex; - lineBufferDesc.usage = BufferUsage::Dynamic; - lineBufferDesc.size = MAX_LINE_VERTICES * sizeof(ShapeVertex); - if (!lineBuffer_.init(lineBufferDesc)) { - E2D_LOG_ERROR("初始化线条缓冲区失败"); - } - - // 创建线条 VAO(手动管理,用于顶点属性配置) - glGenVertexArrays(1, &lineVao_); - glBindVertexArray(lineVao_); - lineBuffer_.bind(); - - // 位置属性 (location = 0) - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex), - reinterpret_cast(offsetof(ShapeVertex, x))); - - // 颜色属性 (location = 1) - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex), - reinterpret_cast(offsetof(ShapeVertex, r))); - - glBindVertexArray(0); -} - -/** - * @brief 添加形状顶点到缓存 - * @param x X坐标 - * @param y Y坐标 - * @param color 顶点颜色 - */ -void GLRenderer::addShapeVertex(float x, float y, const Color &color) { - if (shapeVertexCount_ >= MAX_SHAPE_VERTICES) { - flushShapeBatch(); - } - - glm::vec4 pos(x, y, 0.0f, 1.0f); - if (!transformStack_.empty()) { - pos = transformStack_.back() * pos; - } - - ShapeVertex &v = shapeVertexCache_[shapeVertexCount_++]; - v.x = pos.x; - v.y = pos.y; - v.r = color.r; - v.g = color.g; - v.b = color.b; - v.a = color.a; -} - -/** - * @brief 添加线条顶点到缓存 - * @param x X坐标 - * @param y Y坐标 - * @param color 顶点颜色 - */ -void GLRenderer::addLineVertex(float x, float y, const Color &color) { - if (lineVertexCount_ >= MAX_LINE_VERTICES) { - flushLineBatch(); - } - - glm::vec4 pos(x, y, 0.0f, 1.0f); - if (!transformStack_.empty()) { - pos = transformStack_.back() * pos; - } - - ShapeVertex &v = lineVertexCache_[lineVertexCount_++]; - v.x = pos.x; - v.y = pos.y; - v.r = color.r; - v.g = color.g; - v.b = color.b; - v.a = color.a; -} - -/** - * @brief 提交形状批次(如果需要切换绘制模式) - * @param mode OpenGL绘制模式 - */ -void GLRenderer::submitShapeBatch(GLenum mode) { - if (shapeVertexCount_ == 0) - return; - - // 如果模式改变,先刷新 - if (currentShapeMode_ != mode && shapeVertexCount_ > 0) { - flushShapeBatch(); - } - currentShapeMode_ = mode; -} - -/** - * @brief 刷新形状批次,执行实际的OpenGL绘制调用 - */ -void GLRenderer::flushShapeBatch() { - if (shapeVertexCount_ == 0) - return; - - if (shapeShader_) { - shapeShader_->bind(); - - // 只提供需要动态计算的值,其他值使用JSON中定义的默认值 - UniformValueMap uniformValues; - uniformValues["u_viewProjection"] = viewProjection_; - - // 使用ShaderManager自动应用uniform值(未提供的值使用JSON中的默认值) - // 使用着色器自己的名称(从JSON中解析的name字段) - ShaderManager::getInstance().applyUniforms( - shapeShader_, shapeShader_->getName(), uniformValues); - } - - // 使用 GLBuffer::updateData() 更新缓冲区数据 - shapeBuffer_.updateData(shapeVertexCache_.data(), 0, - shapeVertexCount_ * sizeof(ShapeVertex)); - - glBindVertexArray(shapeVao_); - glDrawArrays(currentShapeMode_, 0, static_cast(shapeVertexCount_)); - - stats_.drawCalls++; - stats_.triangleCount += static_cast(shapeVertexCount_ / 3); - - shapeVertexCount_ = 0; -} - -/** - * @brief 刷新线条批次,执行实际的OpenGL绘制调用 - */ -void GLRenderer::flushLineBatch() { - if (lineVertexCount_ == 0) - return; - - // 先刷新形状批次 - flushShapeBatch(); - - glLineWidth(currentLineWidth_); - if (shapeShader_) { - shapeShader_->bind(); - - // 只提供需要动态计算的值,其他值使用JSON中定义的默认值 - UniformValueMap uniformValues; - uniformValues["u_viewProjection"] = viewProjection_; - - // 使用ShaderManager自动应用uniform值(未提供的值使用JSON中的默认值) - // 使用着色器自己的名称(从JSON中解析的name字段) - ShaderManager::getInstance().applyUniforms( - shapeShader_, shapeShader_->getName(), uniformValues); - } - - // 使用 GLBuffer::updateData() 更新缓冲区数据 - lineBuffer_.updateData(lineVertexCache_.data(), 0, - lineVertexCount_ * sizeof(ShapeVertex)); - - glBindVertexArray(lineVao_); - glDrawArrays(GL_LINES, 0, static_cast(lineVertexCount_)); - - stats_.drawCalls++; - - lineVertexCount_ = 0; -} - -/** - * @brief 创建帧缓冲对象 - * @param desc 帧缓冲描述 - * @return 创建的帧缓冲智能指针 - */ -Ptr GLRenderer::createFramebuffer(const FramebufferDesc &desc) { - auto framebuffer = makePtr(); - if (!framebuffer->init(desc)) { - E2D_LOG_ERROR("创建帧缓冲区失败"); - return nullptr; - } - return framebuffer; -} - -/** - * @brief 绑定帧缓冲(作为渲染目标) - * @param framebuffer 帧缓冲对象指针,传入 nullptr 则绑定默认帧缓冲 - */ -void GLRenderer::bindFramebuffer(GLFramebuffer *framebuffer) { - // 先刷新所有待处理的渲染批次 - flush(); - flushShapeBatch(); - flushLineBatch(); - - if (framebuffer == nullptr) { - // 绑定默认帧缓冲(ID 为 0) - glBindFramebuffer(GL_FRAMEBUFFER, 0); - currentFramebuffer_ = nullptr; - E2D_LOG_TRACE("绑定默认帧缓冲区 (0)"); - } else { - // 绑定自定义帧缓冲 - framebuffer->bind(); - currentFramebuffer_ = framebuffer; - E2D_LOG_TRACE("绑定自定义帧缓冲区 (ID: {})", framebuffer->getFboID()); - } -} - -/** - * @brief 解绑帧缓冲(恢复到默认帧缓冲) - */ -void GLRenderer::unbindFramebuffer() { bindFramebuffer(nullptr); } - -/** - * @brief 获取默认帧缓冲 - * @return 默认帧缓冲智能指针 - */ -Ptr GLRenderer::getDefaultFramebuffer() const { - if (!defaultFramebuffer_) { - // 延迟创建默认帧缓冲对象(代表系统默认帧缓冲,ID 为 0) - defaultFramebuffer_ = makePtr(); - // 注意:默认帧缓冲不需要显式初始化,它的 FBO ID 为 0 - } - return defaultFramebuffer_; -} - -/** - * @brief 清除当前绑定的帧缓冲 - * @param color 清除颜色 - * @param clearColor 是否清除颜色缓冲 - * @param clearDepth 是否清除深度缓冲 - * @param clearStencil 是否清除模板缓冲 - */ -void GLRenderer::clearFramebuffer(const Color &color, bool clearColor, - bool clearDepth, bool clearStencil) { - GLbitfield mask = 0; - - if (clearColor) { - glClearColor(color.r, color.g, color.b, color.a); - mask |= GL_COLOR_BUFFER_BIT; - } - - if (clearDepth) { - mask |= GL_DEPTH_BUFFER_BIT; - } - - if (clearStencil) { - mask |= GL_STENCIL_BUFFER_BIT; - } - - if (mask != 0) { - glClear(mask); - } -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/backends/opengl/gl_shader.cpp b/Extra2D/src/graphics/backends/opengl/gl_shader.cpp deleted file mode 100644 index 6ea4bfc..0000000 --- a/Extra2D/src/graphics/backends/opengl/gl_shader.cpp +++ /dev/null @@ -1,350 +0,0 @@ -#include -#include -#include - - -namespace extra2d { - -/** - * @brief 构造函数,初始化着色器程序ID为0 - */ -GLShader::GLShader() : programID_(0) {} - -/** - * @brief 析构函数,删除OpenGL着色器程序 - */ -GLShader::~GLShader() { - if (programID_ != 0) { - glDeleteProgram(programID_); - programID_ = 0; - } -} - -/** - * @brief 绑定Shader程序 - */ -void GLShader::bind() const { glUseProgram(programID_); } - -/** - * @brief 解绑Shader程序 - */ -void GLShader::unbind() const { glUseProgram(0); } - -/** - * @brief 设置布尔类型uniform变量 - * @param name uniform变量名 - * @param value 布尔值 - */ -void GLShader::setBool(const std::string &name, bool value) { - glUniform1i(getUniformLocation(name), value ? 1 : 0); -} - -/** - * @brief 设置整数类型uniform变量 - * @param name uniform变量名 - * @param value 整数值 - */ -void GLShader::setInt(const std::string &name, int value) { - glUniform1i(getUniformLocation(name), value); -} - -/** - * @brief 设置浮点类型uniform变量 - * @param name uniform变量名 - * @param value 浮点值 - */ -void GLShader::setFloat(const std::string &name, float value) { - glUniform1f(getUniformLocation(name), value); -} - -/** - * @brief 设置二维向量类型uniform变量 - * @param name uniform变量名 - * @param value 二维向量值 - */ -void GLShader::setVec2(const std::string &name, const glm::vec2 &value) { - glUniform2fv(getUniformLocation(name), 1, &value[0]); -} - -/** - * @brief 设置三维向量类型uniform变量 - * @param name uniform变量名 - * @param value 三维向量值 - */ -void GLShader::setVec3(const std::string &name, const glm::vec3 &value) { - glUniform3fv(getUniformLocation(name), 1, &value[0]); -} - -/** - * @brief 设置四维向量类型uniform变量 - * @param name uniform变量名 - * @param value 四维向量值 - */ -void GLShader::setVec4(const std::string &name, const glm::vec4 &value) { - glUniform4fv(getUniformLocation(name), 1, &value[0]); -} - -/** - * @brief 设置4x4矩阵类型uniform变量 - * @param name uniform变量名 - * @param value 4x4矩阵值 - */ -void GLShader::setMat4(const std::string &name, const glm::mat4 &value) { - glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, &value[0][0]); -} - -/** - * @brief 设置颜色类型uniform变量 - * @param name uniform变量名 - * @param color 颜色值 - */ -void GLShader::setColor(const std::string &name, const Color &color) { - glUniform4f(getUniformLocation(name), color.r, color.g, color.b, color.a); -} - -/** - * @brief 从源码编译Shader - * @param vertexSource 顶点着色器源码 - * @param fragmentSource 片段着色器源码 - * @return 编译成功返回true,失败返回false - */ -bool GLShader::compileFromSource(const char *vertexSource, - const char *fragmentSource) { - GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSource); - if (vertexShader == 0) { - return false; - } - - GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentSource); - if (fragmentShader == 0) { - glDeleteShader(vertexShader); - return false; - } - - if (programID_ != 0) { - glDeleteProgram(programID_); - uniformCache_.clear(); - } - - programID_ = glCreateProgram(); - glAttachShader(programID_, vertexShader); - glAttachShader(programID_, fragmentShader); - glLinkProgram(programID_); - - GLint success; - glGetProgramiv(programID_, GL_LINK_STATUS, &success); - if (!success) { - char infoLog[512]; - glGetProgramInfoLog(programID_, 512, nullptr, infoLog); - E2D_LOG_ERROR("着色器程序链接失败: {}", infoLog); - glDeleteProgram(programID_); - programID_ = 0; - } - - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); - - return success == GL_TRUE; -} - -/** - * @brief 从二进制数据创建Shader - * @param binary 二进制数据 - * @return 创建成功返回true,失败返回false - */ -bool GLShader::compileFromBinary(const std::vector &binary) { - if (binary.empty()) { - E2D_LOG_ERROR("二进制数据为空"); - return false; - } - - GLint numFormats = 0; - glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numFormats); - if (numFormats == 0) { - E2D_LOG_ERROR("不支持程序二进制格式"); - return false; - } - - if (programID_ != 0) { - glDeleteProgram(programID_); - uniformCache_.clear(); - } - - programID_ = glCreateProgram(); - - GLenum binaryFormat = 0; - glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, - reinterpret_cast(&binaryFormat)); - - glProgramBinary(programID_, binaryFormat, binary.data(), - static_cast(binary.size())); - - GLint success = 0; - glGetProgramiv(programID_, GL_LINK_STATUS, &success); - if (!success) { - char infoLog[512]; - glGetProgramInfoLog(programID_, 512, nullptr, infoLog); - E2D_LOG_ERROR("从二进制加载着色器失败: {}", infoLog); - glDeleteProgram(programID_); - programID_ = 0; - return false; - } - - return true; -} - -/** - * @brief 获取Shader二进制数据 - * @param outBinary 输出的二进制数据 - * @return 成功返回true,失败返回false - */ -bool GLShader::getBinary(std::vector &outBinary) { - if (programID_ == 0) { - E2D_LOG_WARN("无法获取二进制数据: 着色器程序为 0"); - return false; - } - - GLint binaryLength = 0; - glGetProgramiv(programID_, GL_PROGRAM_BINARY_LENGTH, &binaryLength); - - E2D_LOG_DEBUG("着色器二进制数据长度: {}", binaryLength); - - if (binaryLength <= 0) { - E2D_LOG_WARN("着色器二进制数据长度为 0 或负数"); - return false; - } - - outBinary.resize(binaryLength); - - GLenum binaryFormat = 0; - GLsizei actualLength = 0; - glGetProgramBinary(programID_, binaryLength, &actualLength, &binaryFormat, - outBinary.data()); - - GLenum err = glGetError(); - if (err != GL_NO_ERROR) { - E2D_LOG_ERROR("glGetProgramBinary 失败,错误码: {}", err); - outBinary.clear(); - return false; - } - - if (actualLength == 0) { - E2D_LOG_WARN("glGetProgramBinary 返回 0 字节"); - outBinary.clear(); - return false; - } - - if (actualLength != binaryLength) { - outBinary.resize(actualLength); - } - - E2D_LOG_DEBUG("着色器二进制数据已获取: {} 字节, 格式: {}", actualLength, - binaryFormat); - return true; -} - -/** - * @brief 编译单个着色器 - * @param type 着色器类型 - * @param source 着色器源码 - * @return 着色器ID,失败返回0 - */ -GLuint GLShader::compileShader(GLenum type, const char *source) { - GLuint shader = glCreateShader(type); - glShaderSource(shader, 1, &source, nullptr); - glCompileShader(shader); - - GLint success; - glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - if (!success) { - char infoLog[512]; - glGetShaderInfoLog(shader, 512, nullptr, infoLog); - E2D_LOG_ERROR("着色器编译失败: {}", infoLog); - glDeleteShader(shader); - return 0; - } - - return shader; -} - -/** - * @brief 获取uniform位置 - * @param name uniform变量名 - * @return uniform位置 - */ -GLint GLShader::getUniformLocation(const std::string &name) { - auto it = uniformCache_.find(name); - if (it != uniformCache_.end()) { - return it->second; - } - - GLint location = glGetUniformLocation(programID_, name.c_str()); - uniformCache_[name] = location; - return location; -} - -// ============================================================================ -// GLShaderFactory 实现 -// ============================================================================ - -/** - * @brief 从源码创建Shader - * @param name Shader名称 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 创建的Shader实例 - */ -Ptr GLShaderFactory::createFromSource(const std::string &name, - const std::string &vertSource, - const std::string &fragSource) { - - auto shader = std::make_shared(); - shader->setName(name); - - if (!shader->compileFromSource(vertSource.c_str(), fragSource.c_str())) { - E2D_LOG_ERROR("从源码编译着色器失败: {}", name); - return nullptr; - } - - return shader; -} - -/** - * @brief 从缓存二进制创建Shader - * @param name Shader名称 - * @param binary 编译后的二进制数据 - * @return 创建的Shader实例 - */ -Ptr -GLShaderFactory::createFromBinary(const std::string &name, - const std::vector &binary) { - - auto shader = std::make_shared(); - shader->setName(name); - - if (!shader->compileFromBinary(binary)) { - E2D_LOG_ERROR("从二进制创建着色器失败: {}", name); - return nullptr; - } - - return shader; -} - -/** - * @brief 获取Shader的二进制数据 - * @param shader Shader实例 - * @param outBinary 输出的二进制数据 - * @return 成功返回true,失败返回false - */ -bool GLShaderFactory::getShaderBinary(const IShader &shader, - std::vector &outBinary) { - const GLShader *glShader = dynamic_cast(&shader); - if (!glShader) { - E2D_LOG_ERROR("着色器不是 GLShader 实例"); - return false; - } - - return const_cast(glShader)->getBinary(outBinary); -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/backends/opengl/gl_sprite_batch.cpp b/Extra2D/src/graphics/backends/opengl/gl_sprite_batch.cpp deleted file mode 100644 index fb48994..0000000 --- a/Extra2D/src/graphics/backends/opengl/gl_sprite_batch.cpp +++ /dev/null @@ -1,230 +0,0 @@ -#include -#include -#include -#include -#include - -namespace extra2d { - -GLSpriteBatch::GLSpriteBatch() - : vao_(0), currentTexture_(nullptr), drawCallCount_(0) {} - -GLSpriteBatch::~GLSpriteBatch() { shutdown(); } - -bool GLSpriteBatch::init() { - // 从ShaderManager获取精灵着色器 - shader_ = ShaderManager::getInstance().getBuiltin("sprite"); - if (!shader_) { - E2D_LOG_ERROR("获取内置精灵着色器失败"); - return false; - } - - // 创建 VAO - glGenVertexArrays(1, &vao_); - glBindVertexArray(vao_); - - // 初始化 VBO(顶点缓冲区)- 动态使用模式 - BufferDesc vboDesc; - vboDesc.type = BufferType::Vertex; - vboDesc.usage = BufferUsage::Dynamic; - vboDesc.size = SpriteBatch::MAX_VERTICES * sizeof(SpriteVertex); - vboDesc.initialData = nullptr; - if (!vbo_.init(vboDesc)) { - E2D_LOG_ERROR("初始化精灵批处理 VBO 失败"); - return false; - } - vbo_.bind(); - - // 设置顶点属性 - glEnableVertexAttribArray(0); - glVertexAttribPointer( - 0, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex), - reinterpret_cast(offsetof(SpriteVertex, position))); - - glEnableVertexAttribArray(1); - glVertexAttribPointer( - 1, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex), - reinterpret_cast(offsetof(SpriteVertex, texCoord))); - - glEnableVertexAttribArray(2); - glVertexAttribPointer( - 2, 4, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex), - reinterpret_cast(offsetof(SpriteVertex, color))); - - // 初始化 EBO(索引缓冲区)- 静态使用模式 - BufferDesc eboDesc; - eboDesc.type = BufferType::Index; - eboDesc.usage = BufferUsage::Static; - eboDesc.size = batch_.getIndices().size() * sizeof(uint16_t); - eboDesc.initialData = batch_.getIndices().data(); - if (!ebo_.init(eboDesc)) { - E2D_LOG_ERROR("初始化精灵批处理 EBO 失败"); - return false; - } - ebo_.bind(); - - glBindVertexArray(0); - - return true; -} - -void GLSpriteBatch::shutdown() { - // 使用 GLBuffer::shutdown() 释放缓冲区资源 - vbo_.shutdown(); - ebo_.shutdown(); - - if (vao_ != 0) { - glDeleteVertexArrays(1, &vao_); - vao_ = 0; - } -} - -void GLSpriteBatch::begin(const glm::mat4 &viewProjection) { - batch_.begin(viewProjection); - batches_.clear(); - currentTexture_ = nullptr; - drawCallCount_ = 0; - // 保存 viewProjection 矩阵供后续使用 - viewProjection_ = viewProjection; - - // 绑定 VAO 和缓冲区 - glBindVertexArray(vao_); - vbo_.bind(); - ebo_.bind(); -} - -void GLSpriteBatch::begin(const glm::mat4 &viewProjection, Ptr shader) { - // 设置自定义着色器 - if (shader) { - shader_ = shader; - } - begin(viewProjection); -} - -void GLSpriteBatch::setShader(Ptr shader) { - // 如果当前有未提交的批次,先提交 - if (batch_.getSpriteCount() > 0) { - flush(); - } - shader_ = shader; -} - -void GLSpriteBatch::end() { - if (batch_.getSpriteCount() > 0) { - flush(); - } - - // 解绑缓冲区 - vbo_.unbind(); - ebo_.unbind(); - glBindVertexArray(0); -} - -void GLSpriteBatch::draw(const Texture &texture, const SpriteData &data) { - const GLTexture *glTex = dynamic_cast(&texture); - if (!glTex) { - E2D_LOG_WARN("精灵批处理纹理类型无效"); - return; - } - - // 如果纹理改变或批次已满,先提交当前批次 - if (currentTexture_ != glTex || batch_.needsFlush()) { - if (batch_.getSpriteCount() > 0) { - submitBatch(); - } - currentTexture_ = glTex; - } - - // 使用 batch 层生成顶点 - batch_.draw(data); -} - -void GLSpriteBatch::drawBatch(const Texture &texture, - const std::vector &sprites) { - const GLTexture *glTex = dynamic_cast(&texture); - if (!glTex) { - E2D_LOG_WARN("精灵批处理纹理类型无效"); - return; - } - - // 批量处理精灵 - for (const auto &data : sprites) { - // 如果纹理改变或批次已满,先提交当前批次 - if (currentTexture_ != glTex || batch_.needsFlush()) { - if (batch_.getSpriteCount() > 0) { - submitBatch(); - } - currentTexture_ = glTex; - } - - // 使用 batch 层生成顶点 - batch_.draw(data); - } -} - -void GLSpriteBatch::submitBatch() { - if (batch_.getSpriteCount() == 0) { - return; - } - - // 记录批次信息 - Batch batchInfo; - batchInfo.texture = currentTexture_; - batchInfo.startVertex = 0; // 每次提交都是新的缓冲区 - batchInfo.vertexCount = batch_.getSpriteCount() * 4; - batches_.push_back(batchInfo); - - // 绑定着色器并设置uniform - if (shader_) { - shader_->bind(); - - // 只提供需要动态计算的值,其他值使用JSON中定义的默认值 - UniformValueMap uniformValues; - uniformValues["u_viewProjection"] = viewProjection_; - - // 合并额外的uniform值(如SDF字体的u_textureSize) - for (const auto& [name, value] : extraUniforms_) { - uniformValues[name] = value; - } - - // 使用ShaderManager自动应用uniform值(未提供的值使用JSON中的默认值) - // 使用着色器自己的名称(从JSON中解析的name字段) - ShaderManager::getInstance().applyUniforms(shader_, shader_->getName(), - uniformValues); - - // 设置纹理采样器 - shader_->setInt("u_texture", 0); - } - - // 上传顶点数据 - 使用 orphaning 策略优化动态缓冲区 - // 通过传入 nullptr 进行 orphaning,告诉驱动器可以丢弃旧缓冲区并分配新内存 - // 这样可以避免 GPU 等待,提高性能 - size_t vertexDataSize = batch_.getVertices().size() * sizeof(SpriteVertex); - vbo_.setData(nullptr, vertexDataSize); // orphaning - vbo_.updateData(batch_.getVertices().data(), 0, vertexDataSize); - - // 绘制 - currentTexture_->bind(0); - - size_t indexCount = batch_.getSpriteCount() * 6; - glDrawElements(GL_TRIANGLES, static_cast(indexCount), - GL_UNSIGNED_SHORT, nullptr); - - drawCallCount_++; - - // 清空 batch 层,准备下一批 - batch_.clear(); -} - -void GLSpriteBatch::flush() { - // 提交最后的批次 - if (batch_.getSpriteCount() > 0) { - submitBatch(); - } - - // 重置状态 - batches_.clear(); - currentTexture_ = nullptr; -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/backends/opengl/gl_texture.cpp b/Extra2D/src/graphics/backends/opengl/gl_texture.cpp deleted file mode 100644 index 722b793..0000000 --- a/Extra2D/src/graphics/backends/opengl/gl_texture.cpp +++ /dev/null @@ -1,506 +0,0 @@ -#include -#include -#include -#define STB_IMAGE_IMPLEMENTATION -#include -#include -#include -#include -#include - - -namespace extra2d { - -// ============================================================================ -// KTX 文件头结构 -// ============================================================================ -#pragma pack(push, 1) -struct KTXHeader { - uint8_t identifier[12]; - uint32_t endianness; - uint32_t glType; - uint32_t glTypeSize; - uint32_t glFormat; - uint32_t glInternalFormat; - uint32_t glBaseInternalFormat; - uint32_t pixelWidth; - uint32_t pixelHeight; - uint32_t pixelDepth; - uint32_t numberOfArrayElements; - uint32_t numberOfFaces; - uint32_t numberOfMipmapLevels; - uint32_t bytesOfKeyValueData; -}; -#pragma pack(pop) - -// KTX 文件标识符 -static const uint8_t KTX_IDENTIFIER[12] = {0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, - 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A}; - -// ============================================================================ -// DDS 文件头结构 -// ============================================================================ -#pragma pack(push, 1) -struct DDSPixelFormat { - uint32_t size; - uint32_t flags; - uint32_t fourCC; - uint32_t rgbBitCount; - uint32_t rBitMask; - uint32_t gBitMask; - uint32_t bBitMask; - uint32_t aBitMask; -}; - -struct DDSHeader { - uint32_t magic; - uint32_t size; - uint32_t flags; - uint32_t height; - uint32_t width; - uint32_t pitchOrLinearSize; - uint32_t depth; - uint32_t mipMapCount; - uint32_t reserved1[11]; - DDSPixelFormat pixelFormat; - uint32_t caps; - uint32_t caps2; - uint32_t caps3; - uint32_t caps4; - uint32_t reserved2; -}; - -struct DDSHeaderDXT10 { - uint32_t dxgiFormat; - uint32_t resourceDimension; - uint32_t miscFlag; - uint32_t arraySize; - uint32_t miscFlags2; -}; -#pragma pack(pop) - -static constexpr uint32_t DDS_MAGIC = 0x20534444; // "DDS " -static constexpr uint32_t DDPF_FOURCC = 0x04; - -/** - * @brief 生成四字符代码(FourCC) - * @param a 第一个字符 - * @param b 第二个字符 - * @param c 第三个字符 - * @param d 第四个字符 - * @return 组合后的32位无符号整数 - */ -static uint32_t makeFourCC(char a, char b, char c, char d) { - return static_cast(a) | (static_cast(b) << 8) | - (static_cast(c) << 16) | (static_cast(d) << 24); -} - -// ============================================================================ -// GLTexture 实现 -// ============================================================================ - -/** - * @brief 从像素数据构造纹理对象 - * @param width 纹理宽度(像素) - * @param height 纹理高度(像素) - * @param pixels 像素数据指针,可为nullptr创建空纹理 - * @param channels 颜色通道数(1=R, 3=RGB, 4=RGBA) - */ -GLTexture::GLTexture(int width, int height, const uint8_t *pixels, int channels) - : textureID_(0), width_(width), height_(height), channels_(channels), - format_(PixelFormat::RGBA8), dataSize_(0) { - // 保存像素数据用于生成遮罩 - if (pixels) { - pixelData_.resize(width * height * channels); - std::memcpy(pixelData_.data(), pixels, pixelData_.size()); - } - createTexture(pixels); -} - -/** - * @brief 从文件路径构造纹理对象 - * @param filepath 纹理文件路径,支持普通图片格式和压缩格式(KTX/DDS) - */ -GLTexture::GLTexture(const std::string &filepath) - : textureID_(0), width_(0), height_(0), channels_(0), - format_(PixelFormat::RGBA8), dataSize_(0) { - // 检查是否为压缩纹理格式 - std::string ext = filepath.substr(filepath.find_last_of('.') + 1); - if (ext == "ktx" || ext == "KTX") { - loadCompressed(filepath); - return; - } - if (ext == "dds" || ext == "DDS") { - loadCompressed(filepath); - return; - } - - // 不翻转图片,保持原始方向 - stbi_set_flip_vertically_on_load(false); - uint8_t *data = stbi_load(filepath.c_str(), &width_, &height_, &channels_, 0); - if (data) { - // 保存像素数据用于生成遮罩 - pixelData_.resize(width_ * height_ * channels_); - std::memcpy(pixelData_.data(), data, pixelData_.size()); - - createTexture(data); - stbi_image_free(data); - } else { - E2D_LOG_ERROR("加载纹理失败: {}", filepath); - } -} - -GLTexture::~GLTexture() { - if (textureID_ != 0) { - // 检查 GPU 上下文是否仍然有效 - // 如果 OpenGL 上下文已销毁,则跳过 glDeleteTextures 调用 - if (GPUContext::get().isValid()) { - glDeleteTextures(1, &textureID_); - } - // VRAM 跟踪: 释放纹理显存(无论上下文是否有效都需要更新统计) - if (dataSize_ > 0) { - VRAMMgr::get().freeTexture(dataSize_); - } - } -} - -void GLTexture::setFilter(bool linear) { - bind(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - linear ? GL_LINEAR : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, - linear ? GL_LINEAR : GL_NEAREST); -} - -void GLTexture::setWrap(bool repeat) { - bind(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, - repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, - repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); -} - -void GLTexture::bind(unsigned int slot) const { - glActiveTexture(GL_TEXTURE0 + slot); - glBindTexture(GL_TEXTURE_2D, textureID_); -} - -/** - * @brief 解绑当前纹理 - */ -void GLTexture::unbind() const { glBindTexture(GL_TEXTURE_2D, 0); } - -/** - * @brief 创建OpenGL纹理对象并上传像素数据 - * @param pixels 像素数据指针 - */ -void GLTexture::createTexture(const uint8_t *pixels) { - GLenum format = GL_RGBA; - GLenum internalFormat = GL_RGBA8; - int unpackAlignment = 4; - if (channels_ == 1) { - format = GL_RED; - internalFormat = GL_R8; - unpackAlignment = 1; - format_ = PixelFormat::R8; - } else if (channels_ == 3) { - format = GL_RGB; - internalFormat = GL_RGB8; - unpackAlignment = 1; - format_ = PixelFormat::RGB8; - } else if (channels_ == 4) { - format = GL_RGBA; - internalFormat = GL_RGBA8; - unpackAlignment = 4; - format_ = PixelFormat::RGBA8; - } - - glGenTextures(1, &textureID_); - bind(); - - GLint prevUnpackAlignment = 4; - glGetIntegerv(GL_UNPACK_ALIGNMENT, &prevUnpackAlignment); - glPixelStorei(GL_UNPACK_ALIGNMENT, unpackAlignment); - - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width_, height_, 0, format, - GL_UNSIGNED_BYTE, pixels); - glPixelStorei(GL_UNPACK_ALIGNMENT, prevUnpackAlignment); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - // 使用 NEAREST 过滤器,更适合像素艺术风格的精灵 - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glGenerateMipmap(GL_TEXTURE_2D); - - // VRAM 跟踪 - dataSize_ = static_cast(width_ * height_ * channels_); - VRAMMgr::get().allocTexture(dataSize_); -} - -// ============================================================================ -// 压缩纹理加载 -// ============================================================================ - -bool GLTexture::loadCompressed(const std::string &filepath) { - std::string ext = filepath.substr(filepath.find_last_of('.') + 1); - if (ext == "ktx" || ext == "KTX") { - return loadKTX(filepath); - } - if (ext == "dds" || ext == "DDS") { - return loadDDS(filepath); - } - E2D_LOG_ERROR("不支持的压缩纹理格式: {}", filepath); - return false; -} - -/** - * @brief 加载KTX格式压缩纹理 - * @param filepath KTX文件路径 - * @return 加载成功返回true,失败返回false - */ -bool GLTexture::loadKTX(const std::string &filepath) { - std::ifstream file(filepath, std::ios::binary); - if (!file.is_open()) { - E2D_LOG_ERROR("打开 KTX 文件失败: {}", filepath); - return false; - } - - KTXHeader header; - file.read(reinterpret_cast(&header), sizeof(header)); - if (!file) { - E2D_LOG_ERROR("读取 KTX 文件头失败: {}", filepath); - return false; - } - - // 验证标识符 - if (std::memcmp(header.identifier, KTX_IDENTIFIER, 12) != 0) { - E2D_LOG_ERROR("无效的 KTX 标识符: {}", filepath); - return false; - } - - width_ = static_cast(header.pixelWidth); - height_ = static_cast(header.pixelHeight); - channels_ = 4; // 压缩纹理通常解压为 RGBA - - // 确定压缩格式 - GLenum glInternalFormat = header.glInternalFormat; - switch (glInternalFormat) { - case GL_COMPRESSED_RGB8_ETC2: - format_ = PixelFormat::ETC2_RGB8; - channels_ = 3; - break; - case GL_COMPRESSED_RGBA8_ETC2_EAC: - format_ = PixelFormat::ETC2_RGBA8; - break; - case GL_COMPRESSED_RGBA_ASTC_4x4: - format_ = PixelFormat::ASTC_4x4; - break; - case GL_COMPRESSED_RGBA_ASTC_6x6: - format_ = PixelFormat::ASTC_6x6; - break; - case GL_COMPRESSED_RGBA_ASTC_8x8: - format_ = PixelFormat::ASTC_8x8; - break; - default: - E2D_LOG_ERROR("不支持的 KTX 内部格式: {:#06x}", glInternalFormat); - return false; - } - - // 跳过 key-value 数据 - file.seekg(header.bytesOfKeyValueData, std::ios::cur); - - // 读取第一个 mipmap level - uint32_t imageSize = 0; - file.read(reinterpret_cast(&imageSize), sizeof(imageSize)); - if (!file || imageSize == 0) { - E2D_LOG_ERROR("读取 KTX 图像大小失败: {}", filepath); - return false; - } - - std::vector compressedData(imageSize); - file.read(reinterpret_cast(compressedData.data()), imageSize); - if (!file) { - E2D_LOG_ERROR("读取 KTX 图像数据失败: {}", filepath); - return false; - } - - // 创建 GL 纹理 - glGenTextures(1, &textureID_); - bind(); - - glCompressedTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, width_, height_, 0, - static_cast(imageSize), - compressedData.data()); - - GLenum err = glGetError(); - if (err != GL_NO_ERROR) { - E2D_LOG_ERROR("KTX 纹理上传失败: {:#06x}", err); - glDeleteTextures(1, &textureID_); - textureID_ = 0; - 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); - - // VRAM 跟踪 - dataSize_ = imageSize; - VRAMMgr::get().allocTexture(dataSize_); - - E2D_LOG_INFO("已加载 KTX 压缩纹理: {} ({}x{}, 格式={:#06x})", - filepath, width_, height_, glInternalFormat); - return true; -} - -/** - * @brief 加载DDS格式压缩纹理 - * @param filepath DDS文件路径 - * @return 加载成功返回true,失败返回false - */ -bool GLTexture::loadDDS(const std::string &filepath) { - std::ifstream file(filepath, std::ios::binary); - if (!file.is_open()) { - E2D_LOG_ERROR("打开 DDS 文件失败: {}", filepath); - return false; - } - - DDSHeader header; - file.read(reinterpret_cast(&header), sizeof(header)); - if (!file) { - E2D_LOG_ERROR("读取 DDS 文件头失败: {}", filepath); - return false; - } - - if (header.magic != DDS_MAGIC) { - E2D_LOG_ERROR("无效的 DDS 魔数: {}", filepath); - return false; - } - - width_ = static_cast(header.width); - height_ = static_cast(header.height); - channels_ = 4; - - GLenum glInternalFormat = 0; - - // 检查 DX10 扩展头 - if ((header.pixelFormat.flags & DDPF_FOURCC) && - header.pixelFormat.fourCC == makeFourCC('D', 'X', '1', '0')) { - DDSHeaderDXT10 dx10Header; - file.read(reinterpret_cast(&dx10Header), sizeof(dx10Header)); - if (!file) { - E2D_LOG_ERROR("读取 DDS DX10 文件头失败: {}", filepath); - return false; - } - - // DXGI_FORMAT 映射到 GL 格式 - switch (dx10Header.dxgiFormat) { - case 147: // DXGI_FORMAT_ETC2_RGB8 - glInternalFormat = GL_COMPRESSED_RGB8_ETC2; - format_ = PixelFormat::ETC2_RGB8; - channels_ = 3; - break; - case 148: // DXGI_FORMAT_ETC2_RGBA8 - glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; - format_ = PixelFormat::ETC2_RGBA8; - break; - default: - E2D_LOG_ERROR("不支持的 DDS DX10 格式: {}", dx10Header.dxgiFormat); - return false; - } - } else { - E2D_LOG_ERROR("DDS 文件未使用 DX10 扩展,不支持: {}", - filepath); - return false; - } - - // 计算压缩数据大小 - size_t blockSize = (glInternalFormat == GL_COMPRESSED_RGB8_ETC2) ? 8 : 16; - size_t blocksWide = (width_ + 3) / 4; - size_t blocksHigh = (height_ + 3) / 4; - size_t imageSize = blocksWide * blocksHigh * blockSize; - - std::vector compressedData(imageSize); - file.read(reinterpret_cast(compressedData.data()), imageSize); - if (!file) { - E2D_LOG_ERROR("读取 DDS 图像数据失败: {}", filepath); - return false; - } - - // 创建 GL 纹理 - glGenTextures(1, &textureID_); - bind(); - - glCompressedTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, width_, height_, 0, - static_cast(imageSize), - compressedData.data()); - - GLenum err = glGetError(); - if (err != GL_NO_ERROR) { - E2D_LOG_ERROR("DDS 纹理上传失败: {:#06x}", err); - glDeleteTextures(1, &textureID_); - textureID_ = 0; - 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); - - // VRAM 跟踪 - dataSize_ = imageSize; - VRAMMgr::get().allocTexture(dataSize_); - - E2D_LOG_INFO("已加载 DDS 压缩纹理: {} ({}x{})", filepath, width_, - height_); - return true; -} - -void GLTexture::generateAlphaMask() { - if (pixelData_.empty() || width_ <= 0 || height_ <= 0) { - E2D_LOG_WARN("无法生成透明遮罩: 没有可用的像素数据"); - return; - } - - alphaMask_ = std::make_unique(AlphaMask::createFromPixels( - pixelData_.data(), width_, height_, channels_)); - - E2D_LOG_DEBUG("已为纹理生成透明遮罩: {}x{}", width_, height_); -} - -PixelFormat GLTexture::getFormat() const { return format_; } - -/** - * @brief 静态工厂方法,创建指定格式的空纹理 - * @param width 纹理宽度 - * @param height 纹理高度 - * @param format 像素格式 - * @return 创建的纹理智能指针 - */ -Ptr GLTexture::create(int width, int height, PixelFormat format) { - int channels = 4; - switch (format) { - case PixelFormat::R8: - channels = 1; - break; - case PixelFormat::RG8: - channels = 2; - break; - case PixelFormat::RGB8: - channels = 3; - break; - case PixelFormat::RGBA8: - channels = 4; - break; - default: - channels = 4; - break; - } - return makePtr(width, height, nullptr, channels); -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/backends/vulkan/vk_backend.cpp b/Extra2D/src/graphics/backends/vulkan/vk_backend.cpp deleted file mode 100644 index 095e234..0000000 --- a/Extra2D/src/graphics/backends/vulkan/vk_backend.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include - -namespace extra2d { -namespace graphics { - -namespace { - static bool s_vulkanBackendRegistered = false; -} - -/** - * @brief 初始化 Vulkan 后端注册 - */ -void initVulkanBackend() { - if (s_vulkanBackendRegistered) { - return; - } - s_vulkanBackendRegistered = true; - - BackendFactory::reg( - "vulkan", - []() -> UniquePtr { - return makeUnique(); - }, - {"sdl2", "glfw"} - ); -} - -namespace { - struct VulkanBackendAutoReg { - VulkanBackendAutoReg() { - initVulkanBackend(); - } - }; - static VulkanBackendAutoReg s_vulkanAutoReg; -} - -} // namespace graphics -} // namespace extra2d diff --git a/Extra2D/src/graphics/backends/vulkan/vk_renderer.cpp b/Extra2D/src/graphics/backends/vulkan/vk_renderer.cpp deleted file mode 100644 index 8e7e200..0000000 --- a/Extra2D/src/graphics/backends/vulkan/vk_renderer.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include -#include -#include - - -namespace extra2d { - -VulkanRenderer::VulkanRenderer() = default; - -VulkanRenderer::~VulkanRenderer() { shutdown(); } - -bool VulkanRenderer::init(IWindow *window) { - E2D_LOG_WARN("Vulkan 渲染器尚未完全实现"); - initialized_ = true; - return true; -} - -void VulkanRenderer::shutdown() { initialized_ = false; } - -void VulkanRenderer::beginFrame(const Color &clearColor) { - // TODO: 实现Vulkan帧开始 -} - -void VulkanRenderer::endFrame() { - // TODO: 实现Vulkan帧结束 -} - -void VulkanRenderer::setViewport(int x, int y, int width, int height) { - // TODO: 实现视口设置 -} - -void VulkanRenderer::setVSync(bool enabled) { - // TODO: 实现垂直同步设置 -} - -void VulkanRenderer::setBlendMode(BlendMode mode) { - // TODO: 实现混合模式设置 -} - -void VulkanRenderer::setViewProjection(const glm::mat4 &matrix) { - // TODO: 实现视图投影矩阵设置 -} - -void VulkanRenderer::pushTransform(const glm::mat4 &transform) { - // TODO: 实现变换矩阵入栈 -} - -void VulkanRenderer::popTransform() { - // TODO: 实现变换矩阵出栈 -} - -glm::mat4 VulkanRenderer::getCurrentTransform() const { - return glm::mat4(1.0f); -} - -Ptr VulkanRenderer::createTexture(int width, int height, - const uint8_t *pixels, - int channels) { - // TODO: 实现Vulkan纹理创建 - return nullptr; -} - -Ptr VulkanRenderer::loadTexture(const std::string &filepath) { - // TODO: 实现Vulkan纹理加载 - return nullptr; -} - -void VulkanRenderer::beginSpriteBatch() { - // TODO: 实现精灵批处理开始 -} - -void VulkanRenderer::drawSprite(const Texture &texture, const Rect &destRect, - const Rect &srcRect, const Color &tint, - float rotation, const Vec2 &anchor) { - // TODO: 实现精灵绘制 -} - -void VulkanRenderer::drawSprite(const Texture &texture, const Vec2 &position, - const Color &tint) { - // TODO: 实现简化精灵绘制 -} - -void VulkanRenderer::endSpriteBatch() { - // TODO: 实现精灵批处理结束 -} - -void VulkanRenderer::drawLine(const Vec2 &start, const Vec2 &end, - const Color &color, float width) { - // TODO: 实现线条绘制 -} - -void VulkanRenderer::drawRect(const Rect &rect, const Color &color, - float width) { - // TODO: 实现矩形边框绘制 -} - -void VulkanRenderer::fillRect(const Rect &rect, const Color &color) { - // TODO: 实现矩形填充 -} - -void VulkanRenderer::drawCircle(const Vec2 ¢er, float radius, - const Color &color, int segments, float width) { - // TODO: 实现圆形边框绘制 -} - -void VulkanRenderer::fillCircle(const Vec2 ¢er, float radius, - const Color &color, int segments) { - // TODO: 实现圆形填充 -} - -void VulkanRenderer::drawTriangle(const Vec2 &p1, const Vec2 &p2, - const Vec2 &p3, const Color &color, - float width) { - // TODO: 实现三角形边框绘制 -} - -void VulkanRenderer::fillTriangle(const Vec2 &p1, const Vec2 &p2, - const Vec2 &p3, const Color &color) { - // TODO: 实现三角形填充 -} - -void VulkanRenderer::drawPolygon(const std::vector &points, - const Color &color, float width) { - // TODO: 实现多边形边框绘制 -} - -void VulkanRenderer::fillPolygon(const std::vector &points, - const Color &color) { - // TODO: 实现多边形填充 -} - -Ptr VulkanRenderer::createFontAtlas(const std::string &filepath, - int fontSize, bool useSDF) { - // TODO: 实现字体图集创建 - return nullptr; -} - -void VulkanRenderer::drawText(const FontAtlas &font, const std::string &text, - const Vec2 &position, const Color &color) { - // TODO: 实现文本绘制 -} - -void VulkanRenderer::drawText(const FontAtlas &font, const std::string &text, - float x, float y, const Color &color) { - // TODO: 实现文本绘制 -} - -void VulkanRenderer::resetStats() { stats_ = Stats{}; } - -} // namespace extra2d diff --git a/Extra2D/src/graphics/batch/shape_batch.cpp b/Extra2D/src/graphics/batch/shape_batch.cpp deleted file mode 100644 index 6958818..0000000 --- a/Extra2D/src/graphics/batch/shape_batch.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include - -namespace extra2d { - -// ============================================================================ -// ShapeBatch 基础实现(后端无关部分) -// ============================================================================ - -// 这里可以添加后端无关的工具函数 -// 例如:计算圆形的顶点、三角化多边形等 - -// 计算圆形顶点 -void calculateCircleVertices(std::vector& outVertices, - const Vec2& center, float radius, - int segments, bool fill) { - outVertices.clear(); - outVertices.reserve(fill ? segments + 1 : segments); - - if (fill) { - // 填充圆形:中心点 + 边缘点 - outVertices.push_back(center); - for (int i = 0; i <= segments; ++i) { - float angle = 2.0f * 3.14159265359f * static_cast(i) / static_cast(segments); - outVertices.emplace_back( - center.x + radius * cosf(angle), - center.y + radius * sinf(angle) - ); - } - } else { - // 圆形边框:只保留边缘点 - for (int i = 0; i < segments; ++i) { - float angle = 2.0f * 3.14159265359f * static_cast(i) / static_cast(segments); - outVertices.emplace_back( - center.x + radius * cosf(angle), - center.y + radius * sinf(angle) - ); - } - } -} - -// 计算矩形顶点 -void calculateRectVertices(std::vector& outVertices, const Rect& rect) { - outVertices.clear(); - outVertices.reserve(4); - - float x1 = rect.origin.x; - float y1 = rect.origin.y; - float x2 = rect.origin.x + rect.size.width; - float y2 = rect.origin.y + rect.size.height; - - outVertices.emplace_back(x1, y1); // 左上 - outVertices.emplace_back(x2, y1); // 右上 - outVertices.emplace_back(x2, y2); // 右下 - outVertices.emplace_back(x1, y2); // 左下 -} - -// 简单的多边形三角化(扇形三角化,适用于凸多边形) -void triangulatePolygon(std::vector& outIndices, int vertexCount) { - outIndices.clear(); - if (vertexCount < 3) { - return; - } - - // 扇形三角化:以第一个顶点为扇形中心 - for (int i = 1; i < vertexCount - 1; ++i) { - outIndices.push_back(0); - outIndices.push_back(i); - outIndices.push_back(i + 1); - } -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/batch/sprite_batch.cpp b/Extra2D/src/graphics/batch/sprite_batch.cpp deleted file mode 100644 index ee3c667..0000000 --- a/Extra2D/src/graphics/batch/sprite_batch.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include -#include -#include - - -#include -#include - -namespace extra2d { - -// ============================================================================ -// TrigLookup 实现 - 三角函数查表 -// ============================================================================ -TrigLookup::TrigLookup() { - constexpr float PI = 3.14159265359f; - constexpr float DEG2RAD = PI / 180.0f; - - for (int i = 0; i < TABLE_SIZE; ++i) { - float angle = static_cast(i) * (360.0f / TABLE_SIZE) * DEG2RAD; - sinTable_[i] = std::sin(angle); - cosTable_[i] = std::cos(angle); - } -} - -float TrigLookup::sin(int angle) const { - // 规范化角度到 0-360 - angle = ((angle % 360) + 360) % 360; - return sinTable_[angle * 4]; -} - -float TrigLookup::cos(int angle) const { - // 规范化角度到 0-360 - angle = ((angle % 360) + 360) % 360; - return cosTable_[angle * 4]; -} - -float TrigLookup::sinRad(float rad) const { - constexpr float RAD2DEG = 180.0f / 3.14159265359f; - int angle = static_cast(rad * RAD2DEG); - return sin(angle); -} - -float TrigLookup::cosRad(float rad) const { - constexpr float RAD2DEG = 180.0f / 3.14159265359f; - int angle = static_cast(rad * RAD2DEG); - return cos(angle); -} - -// ============================================================================ -// SpriteBatch 实现 -// ============================================================================ -SpriteBatch::SpriteBatch() : spriteCount_(0), vpDirty_(true) { - // 预分配顶点缓冲区 - vertices_.reserve(MAX_VERTICES); - indices_.reserve(MAX_INDICES); - - // 生成静态索引缓冲区 - generateIndices(); -} - -void SpriteBatch::generateIndices() { - indices_.clear(); - for (size_t i = 0; i < MAX_SPRITES; ++i) { - uint16_t base = static_cast(i * 4); - // 两个三角形: (0,1,2) 和 (0,2,3) - indices_.push_back(base + 0); - indices_.push_back(base + 1); - indices_.push_back(base + 2); - indices_.push_back(base + 0); - indices_.push_back(base + 2); - indices_.push_back(base + 3); - } -} - -void SpriteBatch::begin(const glm::mat4 &viewProjection) { - viewProjection_ = viewProjection; - vpDirty_ = true; - spriteCount_ = 0; - vertices_.clear(); -} - -void SpriteBatch::end() { - // 批次结束,数据已准备好供后端使用 -} - -void SpriteBatch::draw(const SpriteData &sprite) { - if (spriteCount_ >= MAX_SPRITES) { - // 缓冲区已满,需要刷新 - flush(); - } - - generateVertices(sprite, spriteCount_ * VERTICES_PER_SPRITE); - spriteCount_++; -} - -void SpriteBatch::drawBatch(const std::vector &sprites) { - size_t index = 0; - while (index < sprites.size()) { - // 计算剩余空间 - size_t remainingSpace = MAX_SPRITES - spriteCount_; - size_t batchSize = std::min(remainingSpace, sprites.size() - index); - - // 批量生成顶点 - for (size_t i = 0; i < batchSize; ++i) { - generateVertices(sprites[index + i], spriteCount_ * VERTICES_PER_SPRITE); - spriteCount_++; - } - - index += batchSize; - - // 如果缓冲区已满,需要刷新(由后端处理) - if (spriteCount_ >= MAX_SPRITES && index < sprites.size()) { - // 通知后端刷新,然后继续 - // 注意:这里只是准备数据,实际 GPU 提交由后端决定 - break; - } - } -} - -void SpriteBatch::drawImmediate(const SpriteData &sprite) { - // 立即绘制模式:清空当前批次,只绘制这一个精灵 - clear(); - draw(sprite); -} - -void SpriteBatch::generateVertices(const SpriteData &sprite, - size_t vertexOffset) { - // 确保顶点缓冲区足够 - if (vertices_.size() < vertexOffset + VERTICES_PER_SPRITE) { - vertices_.resize(vertexOffset + VERTICES_PER_SPRITE); - } - - // 计算旋转(使用查表) - float c = 1.0f; - float s = 0.0f; - if (sprite.rotation != 0.0f) { - c = trigLookup_.cosRad(sprite.rotation); - s = trigLookup_.sinRad(sprite.rotation); - } - - // 计算精灵的四个角(相对于中心点) - float halfWidth = sprite.size.x * 0.5f; - float halfHeight = sprite.size.y * 0.5f; - - // 考虑 pivot 偏移 - float pivotOffsetX = (sprite.pivot.x - 0.5f) * sprite.size.x; - float pivotOffsetY = (sprite.pivot.y - 0.5f) * sprite.size.y; - - // 四个角的本地坐标 - Vec2 localCorners[4] = { - Vec2(-halfWidth - pivotOffsetX, -halfHeight - pivotOffsetY), // 左下 - Vec2(halfWidth - pivotOffsetX, -halfHeight - pivotOffsetY), // 右下 - Vec2(halfWidth - pivotOffsetX, halfHeight - pivotOffsetY), // 右上 - Vec2(-halfWidth - pivotOffsetX, halfHeight - pivotOffsetY) // 左上 - }; - - // 应用旋转和平移 - Vec2 worldPos = sprite.position; - - for (int i = 0; i < 4; ++i) { - Vec2 rotated; - if (sprite.rotation != 0.0f) { - rotated.x = localCorners[i].x * c - localCorners[i].y * s; - rotated.y = localCorners[i].x * s + localCorners[i].y * c; - } else { - rotated = localCorners[i]; - } - - vertices_[vertexOffset + i].position = worldPos + rotated; - } - - // 设置纹理坐标 - // uvRect.origin = (u0, v0) - 左下 - // uvRect.size = (width, height) - 从左上到右下的尺寸 - float u0 = sprite.uvRect.origin.x; - float v0 = sprite.uvRect.origin.y; - float u1 = u0 + sprite.uvRect.size.width; - float v1 = v0 + sprite.uvRect.size.height; - - // 顶点顺序: 左下, 右下, 右上, 左上 - // 注意: 在 gl_font_atlas 中 v0 > v1 (因为翻转了V坐标) - // 所以 v0 对应底部,v1 对应顶部 - vertices_[vertexOffset + 0].texCoord = Vec2(u0, v0); // 左下 - vertices_[vertexOffset + 1].texCoord = Vec2(u1, v0); // 右下 - vertices_[vertexOffset + 2].texCoord = Vec2(u1, v1); // 右上 - vertices_[vertexOffset + 3].texCoord = Vec2(u0, v1); // 左上 - - // 设置颜色 - for (int i = 0; i < 4; ++i) { - vertices_[vertexOffset + i].color = sprite.color; - } -} - -void SpriteBatch::flush() { - // 标记需要刷新 - 实际刷新由后端处理 - // 这里只是重置计数器,让后端知道需要提交当前批次 - spriteCount_ = 0; - vertices_.clear(); -} - -void SpriteBatch::clear() { - spriteCount_ = 0; - vertices_.clear(); -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/camera/camera.cpp b/Extra2D/src/graphics/camera/camera.cpp deleted file mode 100644 index 23e074c..0000000 --- a/Extra2D/src/graphics/camera/camera.cpp +++ /dev/null @@ -1,351 +0,0 @@ -#include -#include -#include -#include -#include - - -namespace extra2d { - -/** - * @brief 默认构造函数 - * - * 创建一个默认的正交相机,视口范围为 (-1, -1) 到 (1, 1) - */ -Camera::Camera() : left_(-1.0f), right_(1.0f), bottom_(-1.0f), top_(1.0f) {} - -/** - * @brief 构造函数 - * @param left 视口左边界 - * @param right 视口右边界 - * @param bottom 视口底边界 - * @param top 视口顶边界 - * - * 创建一个指定视口范围的正交相机 - */ -Camera::Camera(float left, float right, float bottom, float top) - : left_(left), right_(right), bottom_(bottom), top_(top) {} - -/** - * @brief 构造函数 - * @param viewport 视口尺寸 - * - * 根据视口尺寸创建相机,视口原点在左上角 - */ -Camera::Camera(const Size &viewport) - : left_(0.0f), right_(viewport.width), bottom_(viewport.height), - top_(0.0f) {} - -/** - * @brief 设置相机位置 - * @param position 新的位置坐标 - * - * 设置相机在世界空间中的位置,会标记视图矩阵为脏 - */ -void Camera::setPos(const Vec2 &position) { - position_ = position; - viewDirty_ = true; -} - -/** - * @brief 设置相机位置 - * @param x X坐标 - * @param y Y坐标 - * - * 设置相机在世界空间中的位置,会标记视图矩阵为脏 - */ -void Camera::setPos(float x, float y) { - position_.x = x; - position_.y = y; - viewDirty_ = true; -} - -/** - * @brief 设置相机旋转角度 - * @param degrees 旋转角度(度数) - * - * 设置相机的旋转角度,会标记视图矩阵为脏 - */ -void Camera::setRotation(float degrees) { - rotation_ = degrees; - viewDirty_ = true; -} - -/** - * @brief 设置相机缩放级别 - * @param zoom 缩放值(1.0为正常大小) - * - * 设置相机的缩放级别,会同时标记视图矩阵和投影矩阵为脏 - */ -void Camera::setZoom(float zoom) { - zoom_ = zoom; - viewDirty_ = true; - projDirty_ = true; -} - -/** - * @brief 设置视口范围 - * @param left 左边界 - * @param right 右边界 - * @param bottom 底边界 - * @param top 顶边界 - * - * 设置相机的正交投影视口范围,会标记投影矩阵为脏 - */ -void Camera::setViewport(float left, float right, float bottom, float top) { - left_ = left; - right_ = right; - bottom_ = bottom; - top_ = top; - projDirty_ = true; -} - -/** - * @brief 设置视口范围 - * @param rect 视口矩形 - * - * 使用矩形设置相机的正交投影视口范围,会标记投影矩阵为脏 - */ -void Camera::setViewport(const Rect &rect) { - left_ = rect.left(); - right_ = rect.right(); - bottom_ = rect.bottom(); - top_ = rect.top(); - projDirty_ = true; -} - -/** - * @brief 获取视口矩形 - * @return 当前视口的矩形表示 - * - * 返回当前相机的视口范围 - */ -Rect Camera::getViewport() const { - return Rect(left_, top_, right_ - left_, bottom_ - top_); -} - -/** - * @brief 获取视图矩阵 - * @return 视图矩阵 - * - * 变换顺序:平移 -> 旋转 -> 缩放(逆序应用) - * View = T(-position) × R(-rotation) × S(1/zoom) - */ -glm::mat4 Camera::getViewMatrix() const { - if (viewDirty_) { - viewMatrix_ = glm::mat4(1.0f); - - // 1. 平移(最后应用) - viewMatrix_ = glm::translate(viewMatrix_, - glm::vec3(-position_.x, -position_.y, 0.0f)); - - // 2. 旋转(中间应用) - if (rotation_ != 0.0f) { - viewMatrix_ = glm::rotate(viewMatrix_, -rotation_ * DEG_TO_RAD, - glm::vec3(0.0f, 0.0f, 1.0f)); - } - - // 3. 缩放(最先应用) - if (zoom_ != 1.0f) { - viewMatrix_ = - glm::scale(viewMatrix_, glm::vec3(1.0f / zoom_, 1.0f / zoom_, 1.0f)); - } - - viewDirty_ = false; - } - return viewMatrix_; -} - -/** - * @brief 获取投影矩阵 - * @return 正交投影矩阵 - * - * 对于2D游戏,Y轴向下增长(屏幕坐标系) - * OpenGL默认Y轴向上,所以需要反转Y轴 - */ -glm::mat4 Camera::getProjectionMatrix() const { - if (projDirty_) { - // 对于2D游戏,Y轴向下增长(屏幕坐标系) - // OpenGL默认Y轴向上,所以需要反转Y轴 - // glm::ortho(left, right, bottom, top) - // 为了Y轴向下:传入 (bottom=height, top=0),这样Y轴翻转 - projMatrix_ = glm::ortho( - left_, right_, // X轴:从左到右 - bottom_, top_, // Y轴:从下到上(传入bottom>top,实现Y轴向下增长) - -1.0f, 1.0f); - projDirty_ = false; - } - return projMatrix_; -} - -/** - * @brief 获取视图-投影矩阵 - * @return 视图-投影矩阵 - */ -glm::mat4 Camera::getViewProjectionMatrix() const { - return getProjectionMatrix() * getViewMatrix(); -} - -/** - * @brief 将屏幕坐标转换为世界坐标 - * @param screenPos 屏幕坐标 - * @return 世界坐标 - */ -Vec2 Camera::screenToWorld(const Vec2 &screenPos) const { - Vec2 logicPos = screenPos; - - // 如果有视口适配器,先转换到逻辑坐标 - if (viewportAdapter_) { - logicPos = viewportAdapter_->screenToLogic(screenPos); - } - - // 使用逆视图-投影矩阵转换 - glm::mat4 invVP = glm::inverse(getViewProjectionMatrix()); - glm::vec4 ndc(logicPos.x, logicPos.y, 0.0f, 1.0f); - glm::vec4 world = invVP * ndc; - return Vec2(world.x, world.y); -} - -/** - * @brief 将世界坐标转换为屏幕坐标 - * @param worldPos 世界坐标 - * @return 屏幕坐标 - */ -Vec2 Camera::worldToScreen(const Vec2 &worldPos) const { - glm::vec4 world(worldPos.x, worldPos.y, 0.0f, 1.0f); - glm::vec4 screen = getViewProjectionMatrix() * world; - Vec2 logicPos(screen.x, screen.y); - - // 如果有视口适配器,转换到屏幕坐标 - if (viewportAdapter_) { - return viewportAdapter_->logicToScreen(logicPos); - } - return logicPos; -} - -/** - * @brief 将屏幕坐标转换为世界坐标 - * @param x 屏幕X坐标 - * @param y 屏幕Y坐标 - * @return 世界坐标 - */ -Vec2 Camera::screenToWorld(float x, float y) const { - return screenToWorld(Vec2(x, y)); -} - -/** - * @brief 将世界坐标转换为屏幕坐标 - * @param x 世界X坐标 - * @param y 世界Y坐标 - * @return 屏幕坐标 - */ -Vec2 Camera::worldToScreen(float x, float y) const { - return worldToScreen(Vec2(x, y)); -} - -/** - * @brief 移动相机位置 - * @param offset 位置偏移量 - * - * 按指定偏移量移动相机位置,会标记视图矩阵为脏 - */ -void Camera::move(const Vec2 &offset) { - position_ += offset; - viewDirty_ = true; -} - -/** - * @brief 移动相机位置 - * @param x X方向偏移量 - * @param y Y方向偏移量 - * - * 按指定偏移量移动相机位置,会标记视图矩阵为脏 - */ -void Camera::move(float x, float y) { - position_.x += x; - position_.y += y; - viewDirty_ = true; -} - -/** - * @brief 设置相机边界限制 - * @param bounds 边界矩形 - * - * 设置相机的移动边界,相机位置将被限制在此边界内 - */ -void Camera::setBounds(const Rect &bounds) { - bounds_ = bounds; - hasBounds_ = true; -} - -/** - * @brief 清除相机边界限制 - * - * 移除相机的移动边界限制 - */ -void Camera::clearBounds() { hasBounds_ = false; } - -/** - * @brief 将相机位置限制在边界内 - * - * 如果设置了边界,将相机位置限制在边界矩形内 - */ -void Camera::clampToBounds() { - if (!hasBounds_) - return; - - float viewportWidth = (right_ - left_) / zoom_; - float viewportHeight = (bottom_ - top_) / zoom_; - - float minX = bounds_.left() + viewportWidth * 0.5f; - float maxX = bounds_.right() - viewportWidth * 0.5f; - float minY = bounds_.top() + viewportHeight * 0.5f; - float maxY = bounds_.bottom() - viewportHeight * 0.5f; - - if (minX > maxX) { - position_.x = bounds_.center().x; - } else { - position_.x = std::clamp(position_.x, minX, maxX); - } - - if (minY > maxY) { - position_.y = bounds_.center().y; - } else { - position_.y = std::clamp(position_.y, minY, maxY); - } - - viewDirty_ = true; -} - -/** - * @brief 将相机移动到目标位置 - * @param target 目标位置 - * - * 设置相机位置到指定的世界坐标 - */ -void Camera::lookAt(const Vec2 &target) { - position_ = target; - viewDirty_ = true; -} - -/** - * @brief 设置视口适配器 - * @param adapter 视口适配器指针 - */ -void Camera::setViewportAdapter(ViewportAdapter *adapter) { - viewportAdapter_ = adapter; -} - -/** - * @brief 根据视口适配器自动设置视口 - * - * 如果设置了视口适配器,根据其配置自动设置相机的视口范围 - */ -void Camera::applyViewportAdapter() { - if (viewportAdapter_) { - const auto &config = viewportAdapter_->getConfig(); - setViewport(0.0f, config.logicWidth, config.logicHeight, 0.0f); - } -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/camera/viewport_adapter.cpp b/Extra2D/src/graphics/camera/viewport_adapter.cpp deleted file mode 100644 index fe6752a..0000000 --- a/Extra2D/src/graphics/camera/viewport_adapter.cpp +++ /dev/null @@ -1,451 +0,0 @@ -#include -#include -#include - -namespace extra2d { - -/** - * @brief 默认构造函数 - * - * 创建一个未配置的视口适配器 - */ -ViewportAdapter::ViewportAdapter() = default; - -/** - * @brief 构造函数 - * @param logicWidth 逻辑宽度 - * @param logicHeight 逻辑高度 - * - * 创建一个指定逻辑尺寸的视口适配器 - */ -ViewportAdapter::ViewportAdapter(float logicWidth, float logicHeight) { - config_.logicWidth = logicWidth; - config_.logicHeight = logicHeight; -} - -/** - * @brief 设置视口配置 - * @param config 视口配置 - * - * 设置完整的视口配置并标记矩阵为脏 - */ -void ViewportAdapter::setConfig(const ViewportConfig &config) { - config_ = config; - matrixDirty_ = true; -} - -/** - * @brief 设置逻辑尺寸 - * @param width 逻辑宽度 - * @param height 逻辑高度 - * - * 设置视口的逻辑尺寸并标记矩阵为脏 - */ -void ViewportAdapter::setLogicSize(float width, float height) { - config_.logicWidth = width; - config_.logicHeight = height; - matrixDirty_ = true; -} - -/** - * @brief 设置视口模式 - * @param mode 视口模式 - * - * 设置视口适配模式(宽高比、拉伸、居中等) - */ -void ViewportAdapter::setMode(ViewportMode mode) { - config_.mode = mode; - matrixDirty_ = true; -} - -/** - * @brief 设置黑边位置 - * @param position 黑边位置 - * - * 设置黑边相对于视口的位置 - */ -void ViewportAdapter::setLetterboxPosition(LetterboxPosition position) { - config_.letterboxPosition = position; - matrixDirty_ = true; -} - -/** - * @brief 设置黑边颜色 - * @param color 黑边颜色 - * - * 设置黑边区域的填充颜色 - */ -void ViewportAdapter::setLetterboxColor(const Color &color) { - config_.letterboxColor = color; -} - -/** - * @brief 更新视口适配 - * @param screenWidth 屏幕宽度 - * @param screenHeight 屏幕高度 - * - * 根据屏幕尺寸和配置计算视口参数 - */ -void ViewportAdapter::update(int screenWidth, int screenHeight) { - if (screenWidth_ == screenWidth && screenHeight_ == screenHeight && - !matrixDirty_) { - return; - } - - screenWidth_ = screenWidth; - screenHeight_ = screenHeight; - matrixDirty_ = true; - - result_.hasLetterbox = false; - result_.letterbox.top = Rect::Zero(); - result_.letterbox.bottom = Rect::Zero(); - result_.letterbox.left = Rect::Zero(); - result_.letterbox.right = Rect::Zero(); - - switch (config_.mode) { - case ViewportMode::AspectRatio: - calculateAspectRatio(); - break; - case ViewportMode::Stretch: - calculateStretch(); - break; - case ViewportMode::Center: - calculateCenter(); - break; - case ViewportMode::Custom: - calculateCustom(); - break; - } -} - -/** - * @brief 计算宽高比适配模式 - * - * 保持逻辑宽高比,根据屏幕尺寸计算缩放和偏移 - */ -void ViewportAdapter::calculateAspectRatio() { - if (config_.logicHeight <= 0.0f || screenHeight_ <= 0) { - result_ = ViewportResult(); - return; - } - - float logicAspect = config_.logicWidth / config_.logicHeight; - float screenAspect = static_cast(screenWidth_) / screenHeight_; - - if (screenAspect > logicAspect) { - result_.uniformScale = static_cast(screenHeight_) / config_.logicHeight; - result_.scaleX = result_.uniformScale; - result_.scaleY = result_.uniformScale; - result_.viewport.size.width = config_.logicWidth * result_.uniformScale; - result_.viewport.size.height = static_cast(screenHeight_); - result_.offset.x = (screenWidth_ - result_.viewport.size.width) / 2.0f; - result_.offset.y = 0.0f; - } else { - result_.uniformScale = static_cast(screenWidth_) / config_.logicWidth; - result_.scaleX = result_.uniformScale; - result_.scaleY = result_.uniformScale; - result_.viewport.size.width = static_cast(screenWidth_); - result_.viewport.size.height = config_.logicHeight * result_.uniformScale; - result_.offset.x = 0.0f; - result_.offset.y = (screenHeight_ - result_.viewport.size.height) / 2.0f; - } - - result_.viewport.origin = result_.offset; - - applyLetterboxPosition( - static_cast(screenWidth_) - result_.viewport.size.width, - static_cast(screenHeight_) - result_.viewport.size.height); - - calculateLetterbox(); -} - -/** - * @brief 计算拉伸模式 - * - * 拉伸逻辑视口以填满整个屏幕 - */ -void ViewportAdapter::calculateStretch() { - result_.scaleX = static_cast(screenWidth_) / config_.logicWidth; - result_.scaleY = static_cast(screenHeight_) / config_.logicHeight; - result_.uniformScale = std::min(result_.scaleX, result_.scaleY); - - result_.viewport.origin = Vec2::Zero(); - result_.viewport.size.width = static_cast(screenWidth_); - result_.viewport.size.height = static_cast(screenHeight_); - - result_.offset = Vec2::Zero(); - result_.hasLetterbox = false; -} - -/** - * @brief 计算居中模式 - * - * 将逻辑视口居中显示,可选自动缩放 - */ -void ViewportAdapter::calculateCenter() { - float displayWidth = config_.logicWidth; - float displayHeight = config_.logicHeight; - - if (config_.autoScaleInCenterMode) { - float scaleX = static_cast(screenWidth_) / config_.logicWidth; - float scaleY = static_cast(screenHeight_) / config_.logicHeight; - result_.uniformScale = std::min(scaleX, scaleY); - - if (result_.uniformScale < 1.0f) { - displayWidth = config_.logicWidth * result_.uniformScale; - displayHeight = config_.logicHeight * result_.uniformScale; - } else { - result_.uniformScale = 1.0f; - } - - result_.scaleX = result_.uniformScale; - result_.scaleY = result_.uniformScale; - } else { - result_.scaleX = 1.0f; - result_.scaleY = 1.0f; - result_.uniformScale = 1.0f; - } - - result_.offset.x = (screenWidth_ - displayWidth) / 2.0f; - result_.offset.y = (screenHeight_ - displayHeight) / 2.0f; - - result_.viewport.origin = result_.offset; - result_.viewport.size.width = displayWidth; - result_.viewport.size.height = displayHeight; - - applyLetterboxPosition( - static_cast(screenWidth_) - displayWidth, - static_cast(screenHeight_) - displayHeight); - - calculateLetterbox(); -} - -/** - * @brief 计算自定义模式 - * - * 使用自定义缩放和偏移参数 - */ -void ViewportAdapter::calculateCustom() { - result_.scaleX = config_.customScale; - result_.scaleY = config_.customScale; - result_.uniformScale = config_.customScale; - - if (config_.customViewport.empty()) { - float displayWidth = config_.logicWidth * config_.customScale; - float displayHeight = config_.logicHeight * config_.customScale; - - result_.offset = config_.customOffset; - result_.viewport.origin = result_.offset; - result_.viewport.size.width = displayWidth; - result_.viewport.size.height = displayHeight; - } else { - result_.viewport = config_.customViewport; - result_.offset = config_.customViewport.origin; - } - - calculateLetterbox(); -} - -/** - * @brief 计算黑边区域 - * - * 根据视口偏移计算上下左右黑边矩形 - */ -void ViewportAdapter::calculateLetterbox() { - result_.hasLetterbox = false; - - float screenW = static_cast(screenWidth_); - float screenH = static_cast(screenHeight_); - - if (result_.offset.y > 0.0f) { - result_.hasLetterbox = true; - result_.letterbox.top = - Rect(0.0f, 0.0f, screenW, result_.offset.y); - result_.letterbox.bottom = - Rect(0.0f, result_.offset.y + result_.viewport.size.height, screenW, - result_.offset.y); - } - - if (result_.offset.x > 0.0f) { - result_.hasLetterbox = true; - result_.letterbox.left = - Rect(0.0f, 0.0f, result_.offset.x, screenH); - result_.letterbox.right = - Rect(result_.offset.x + result_.viewport.size.width, 0.0f, - result_.offset.x, screenH); - } -} - -/** - * @brief 应用黑边位置 - * @param extraWidth 额外宽度 - * @param extraHeight 额外高度 - * - * 根据配置调整视口偏移以实现不同的黑边位置 - */ -void ViewportAdapter::applyLetterboxPosition(float extraWidth, - float extraHeight) { - if (extraWidth <= 0.0f && extraHeight <= 0.0f) { - return; - } - - switch (config_.letterboxPosition) { - case LetterboxPosition::Center: - break; - - case LetterboxPosition::LeftTop: - if (extraWidth > 0.0f) { - result_.offset.x = 0.0f; - } - if (extraHeight > 0.0f) { - result_.offset.y = 0.0f; - } - break; - - case LetterboxPosition::RightTop: - if (extraWidth > 0.0f) { - result_.offset.x = extraWidth; - } - if (extraHeight > 0.0f) { - result_.offset.y = 0.0f; - } - break; - - case LetterboxPosition::LeftBottom: - if (extraWidth > 0.0f) { - result_.offset.x = 0.0f; - } - if (extraHeight > 0.0f) { - result_.offset.y = extraHeight; - } - break; - - case LetterboxPosition::RightBottom: - if (extraWidth > 0.0f) { - result_.offset.x = extraWidth; - } - if (extraHeight > 0.0f) { - result_.offset.y = extraHeight; - } - break; - } - - result_.viewport.origin = result_.offset; -} - -/** - * @brief 将屏幕坐标转换为逻辑坐标 - * @param screenPos 屏幕坐标 - * @return 逻辑坐标 - * - * 根据当前缩放和偏移计算对应的逻辑坐标 - */ -Vec2 ViewportAdapter::screenToLogic(const Vec2 &screenPos) const { - return Vec2((screenPos.x - result_.offset.x) / result_.scaleX, - (screenPos.y - result_.offset.y) / result_.scaleY); -} - -/** - * @brief 将逻辑坐标转换为屏幕坐标 - * @param logicPos 逻辑坐标 - * @return 屏幕坐标 - * - * 根据当前缩放和偏移计算对应的屏幕坐标 - */ -Vec2 ViewportAdapter::logicToScreen(const Vec2 &logicPos) const { - return Vec2(logicPos.x * result_.scaleX + result_.offset.x, - logicPos.y * result_.scaleY + result_.offset.y); -} - -/** - * @brief 将屏幕坐标转换为逻辑坐标 - * @param x 屏幕X坐标 - * @param y 屏幕Y坐标 - * @return 逻辑坐标 - */ -Vec2 ViewportAdapter::screenToLogic(float x, float y) const { - return screenToLogic(Vec2(x, y)); -} - -/** - * @brief 将逻辑坐标转换为屏幕坐标 - * @param x 逻辑X坐标 - * @param y 逻辑Y坐标 - * @return 屏幕坐标 - */ -Vec2 ViewportAdapter::logicToScreen(float x, float y) const { - return logicToScreen(Vec2(x, y)); -} - -/** - * @brief 获取视口变换矩阵 - * @return 4x4变换矩阵 - * - * 返回用于将逻辑坐标转换为屏幕坐标的变换矩阵 - */ -glm::mat4 ViewportAdapter::getMatrix() const { - if (matrixDirty_) { - viewportMatrix_ = glm::mat4(1.0f); - viewportMatrix_ = glm::translate(viewportMatrix_, - glm::vec3(result_.offset.x, result_.offset.y, 0.0f)); - viewportMatrix_ = glm::scale(viewportMatrix_, - glm::vec3(result_.scaleX, result_.scaleY, 1.0f)); - matrixDirty_ = false; - } - return viewportMatrix_; -} - -/** - * @brief 获取视口逆变换矩阵 - * @return 4x4逆变换矩阵 - * - * 返回用于将屏幕坐标转换为逻辑坐标的逆变换矩阵 - */ -glm::mat4 ViewportAdapter::getInvMatrix() const { - if (matrixDirty_) { - getMatrix(); - } - inverseViewportMatrix_ = glm::inverse(viewportMatrix_); - return inverseViewportMatrix_; -} - -/** - * @brief 检查屏幕坐标是否在视口内 - * @param screenPos 屏幕坐标 - * @return 在视口内返回true,否则返回false - */ -bool ViewportAdapter::isInViewport(const Vec2 &screenPos) const { - return result_.viewport.containsPoint(screenPos); -} - -/** - * @brief 检查屏幕坐标是否在黑边区域内 - * @param screenPos 屏幕坐标 - * @return 在黑边区域内返回true,否则返回false - */ -bool ViewportAdapter::isInLetterbox(const Vec2 &screenPos) const { - if (!result_.hasLetterbox) { - return false; - } - - if (!result_.letterbox.top.empty() && - result_.letterbox.top.containsPoint(screenPos)) { - return true; - } - if (!result_.letterbox.bottom.empty() && - result_.letterbox.bottom.containsPoint(screenPos)) { - return true; - } - if (!result_.letterbox.left.empty() && - result_.letterbox.left.containsPoint(screenPos)) { - return true; - } - if (!result_.letterbox.right.empty() && - result_.letterbox.right.containsPoint(screenPos)) { - return true; - } - - return false; -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/core/render_command.cpp b/Extra2D/src/graphics/core/render_command.cpp deleted file mode 100644 index dbfc6ba..0000000 --- a/Extra2D/src/graphics/core/render_command.cpp +++ /dev/null @@ -1,266 +0,0 @@ -#include -#include - -namespace extra2d { - -// ============================================================================ -// RenderCommand 便捷构造函数 -// ============================================================================ - -/** - * @brief 创建精灵渲染命令 - * - * 创建一个用于渲染2D精灵的RenderCommand对象,包含纹理、目标区域、源区域、 - * 着色、旋转、锚点和层级等信息 - * - * @param tex 指向纹理对象的指针 - * @param dest 目标渲染区域 - * @param src 源纹理区域 - * @param tint 着色颜色 - * @param rot 旋转角度(弧度) - * @param anc 锚点位置(0.0-1.0范围) - * @param lyr 渲染层级 - * @return 配置好的RenderCommand对象 - */ -RenderCommand RenderCommand::makeSprite(const Texture* tex, const Rect& dest, - const Rect& src, const Color& tint, - float rot, const Vec2& anc, - uint32_t lyr) { - RenderCommand cmd; - cmd.type = RenderCommandType::Sprite; - cmd.layer = lyr; - cmd.transform = glm::mat4(1.0f); - - SpriteCommandData data; - data.texture = tex; - data.destRect = dest; - data.srcRect = src; - data.tint = tint; - data.rotation = rot; - data.anchor = anc; - // 生成排序键:纹理指针的高位 + 层级的低位 - data.sortKey = (reinterpret_cast(tex) >> 4) & 0xFFFFFFF0; - data.sortKey |= (lyr & 0xF); - - cmd.data = data; - return cmd; -} - -/** - * @brief 创建线段渲染命令 - * - * 创建一个用于渲染线段的RenderCommand对象 - * - * @param s 线段起点坐标 - * @param e 线段终点坐标 - * @param c 线段颜色 - * @param w 线段宽度 - * @param lyr 渲染层级 - * @return 配置好的RenderCommand对象 - */ -RenderCommand RenderCommand::makeLine(const Vec2& s, const Vec2& e, const Color& c, - float w, uint32_t lyr) { - RenderCommand cmd; - cmd.type = RenderCommandType::Line; - cmd.layer = lyr; - cmd.transform = glm::mat4(1.0f); - - LineCommandData data; - data.start = s; - data.end = e; - data.color = c; - data.width = w; - - cmd.data = data; - return cmd; -} - -/** - * @brief 创建矩形渲染命令 - * - * 创建一个用于渲染矩形的RenderCommand对象,可选择填充或描边模式 - * - * @param r 矩形区域 - * @param c 矩形颜色 - * @param w 线条宽度(仅描边模式有效) - * @param fill 是否填充矩形 - * @param lyr 渲染层级 - * @return 配置好的RenderCommand对象 - */ -RenderCommand RenderCommand::makeRect(const Rect& r, const Color& c, - float w, bool fill, uint32_t lyr) { - RenderCommand cmd; - cmd.type = fill ? RenderCommandType::FilledRect : RenderCommandType::Rect; - cmd.layer = lyr; - cmd.transform = glm::mat4(1.0f); - - RectCommandData data; - data.rect = r; - data.color = c; - data.width = w; - data.filled = fill; - - cmd.data = data; - return cmd; -} - -// ============================================================================ -// RenderCommandBuffer 实现 -// ============================================================================ - -/** - * @brief 默认构造函数 - * - * 初始化渲染命令缓冲区,预留初始容量 - */ -RenderCommandBuffer::RenderCommandBuffer() : nextOrder_(0) { - commands_.reserve(INITIAL_CAPACITY); -} - -/** - * @brief 析构函数 - * - * 释放渲染命令缓冲区资源 - */ -RenderCommandBuffer::~RenderCommandBuffer() = default; - -/** - * @brief 添加渲染命令(左值引用版本) - * - * 将渲染命令以拷贝方式添加到缓冲区,自动分配顺序号 - * - * @param cmd 要添加的渲染命令 - */ -void RenderCommandBuffer::addCommand(const RenderCommand& cmd) { - if (commands_.size() >= MAX_CAPACITY) { - // 缓冲区已满,可能需要立即刷新 - return; - } - - RenderCommand copy = cmd; - copy.order = nextOrder_++; - commands_.push_back(std::move(copy)); -} - -/** - * @brief 添加渲染命令(右值引用版本) - * - * 将渲染命令以移动方式添加到缓冲区,自动分配顺序号 - * - * @param cmd 要添加的渲染命令(右值引用) - */ -void RenderCommandBuffer::addCommand(RenderCommand&& cmd) { - if (commands_.size() >= MAX_CAPACITY) { - return; - } - - cmd.order = nextOrder_++; - commands_.push_back(std::move(cmd)); -} - -/** - * @brief 原地构造渲染命令 - * - * 在缓冲区中直接构造一个渲染命令对象,避免额外的拷贝或移动操作 - * - * @return 新构造的渲染命令对象的引用 - */ -RenderCommand& RenderCommandBuffer::emplaceCommand() { - if (commands_.size() >= MAX_CAPACITY) { - // 如果已满,返回一个虚拟命令(不应该发生) - static RenderCommand dummy; - return dummy; - } - - commands_.emplace_back(); - commands_.back().order = nextOrder_++; - return commands_.back(); -} - -/** - * @brief 对渲染命令进行排序 - * - * 按层级、命令类型、纹理/材质和提交顺序对命令进行排序, - * 以优化渲染性能和批处理效率 - */ -void RenderCommandBuffer::sortCommands() { - // 按以下优先级排序: - // 1. 层级 (layer) - 低层级先渲染 - // 2. 命令类型 - 精灵类命令优先批处理 - // 3. 纹理/材质 - 相同纹理的精灵连续渲染 - // 4. 提交顺序 - 保证稳定性 - - std::sort(commands_.begin(), commands_.end(), compareCommands); -} - -/** - * @brief 清空缓冲区 - * - * 移除所有渲染命令并重置顺序计数器 - */ -void RenderCommandBuffer::clear() { - commands_.clear(); - nextOrder_ = 0; -} - -/** - * @brief 预留缓冲区容量 - * - * 预先分配缓冲区内存以减少动态分配开销 - * - * @param capacity 要预留的容量大小 - */ -void RenderCommandBuffer::reserve(size_t capacity) { - if (capacity <= MAX_CAPACITY) { - commands_.reserve(capacity); - } -} - -/** - * @brief 渲染命令比较函数 - * - * 用于排序的比较函数,按层级、类型、纹理和顺序进行比较 - * - * @param a 第一个渲染命令 - * @param b 第二个渲染命令 - * @return 如果a应排在b前面返回true,否则返回false - */ -bool RenderCommandBuffer::compareCommands(const RenderCommand& a, const RenderCommand& b) { - // 首先按层级排序 - if (a.layer != b.layer) { - return a.layer < b.layer; - } - - // 然后按类型排序(精灵类命令放在一起以便批处理) - if (a.type != b.type) { - // 精灵和文本命令优先(需要纹理) - bool aIsSprite = (a.type == RenderCommandType::Sprite || - a.type == RenderCommandType::Text); - bool bIsSprite = (b.type == RenderCommandType::Sprite || - b.type == RenderCommandType::Text); - - if (aIsSprite != bIsSprite) { - return aIsSprite > bIsSprite; // 精灵类命令在前 - } - - return static_cast(a.type) < static_cast(b.type); - } - - // 对于精灵命令,按纹理排序 - if (a.type == RenderCommandType::Sprite && b.type == RenderCommandType::Sprite) { - const auto& dataA = std::get(a.data); - const auto& dataB = std::get(b.data); - if (dataA.texture != dataB.texture) { - return dataA.texture < dataB.texture; - } - // 相同纹理时按 sortKey 排序 - if (dataA.sortKey != dataB.sortKey) { - return dataA.sortKey < dataB.sortKey; - } - } - - // 最后按提交顺序排序(保证稳定性) - return a.order < b.order; -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/core/render_module.cpp b/Extra2D/src/graphics/core/render_module.cpp deleted file mode 100644 index e5b4177..0000000 --- a/Extra2D/src/graphics/core/render_module.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// 前向声明后端初始化函数 -namespace graphics { -#ifdef E2D_BACKEND_OPENGL -void initOpenGLBackend(); -#endif -#ifdef E2D_BACKEND_VULKAN -void initVulkanBackend(); -#endif -} // namespace graphics - -RenderModule::RenderModule(std::function configFn) { - configFn(cfg_); -} - -RenderModule::~RenderModule() { - if (initialized_) { - shutdown(); - } -} - -bool RenderModule::init() { - if (initialized_) - return true; - - auto *winMod = Registry::instance().get(); - if (!winMod || !winMod->win()) { - E2D_LOG_ERROR("窗口模块不可用"); - return false; - } - - // 初始化图形后端(注册到工厂) -#ifdef E2D_BACKEND_OPENGL - graphics::initOpenGLBackend(); -#endif -#ifdef E2D_BACKEND_VULKAN - graphics::initVulkanBackend(); -#endif - - // 使用ShaderManager的默认路径初始化 - if (!ShaderManager::getInstance().isInitialized()) { - auto factory = makeShared(); - if (!ShaderManager::getInstance().init(factory)) { - E2D_LOG_WARN("使用默认路径初始化 ShaderManager 失败"); - } - } - - std::string windowBackend = winMod->getWindowBackend(); - - if (cfg_.backend.empty()) { - E2D_LOG_INFO("未指定图形后端,正在为窗口后端自动选择:{}", windowBackend); - renderer_ = graphics::BackendFactory::createBackendForWindow(windowBackend); - } else { - if (!graphics::BackendFactory::isCompatible(cfg_.backend, windowBackend)) { - E2D_LOG_WARN("图形后端 '{}' 与窗口后端 '{}' 不兼容", cfg_.backend, - windowBackend); - } - renderer_ = graphics::BackendFactory::createBackend(cfg_.backend); - } - - if (!renderer_) { - E2D_LOG_ERROR("创建渲染后端失败"); - return false; - } - - if (!renderer_->init(winMod->win())) { - E2D_LOG_ERROR("初始化渲染后端失败"); - renderer_.reset(); - return false; - } - - E2D_LOG_INFO("渲染模块初始化成功"); - initialized_ = true; - return true; -} - -void RenderModule::shutdown() { - if (!initialized_) - return; - - if (renderer_) { - renderer_->shutdown(); - renderer_.reset(); - } - - initialized_ = false; -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/core/render_target.cpp b/Extra2D/src/graphics/core/render_target.cpp deleted file mode 100644 index d7abeaf..0000000 --- a/Extra2D/src/graphics/core/render_target.cpp +++ /dev/null @@ -1,780 +0,0 @@ -#include -#include -#include -#include -#include - - -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include - -namespace extra2d { - -// ============================================================================ -// RenderTarget实现 -// ============================================================================ - -/** - * @brief 默认构造函数 - * - * 创建一个空的渲染目标对象 - */ -RenderTarget::RenderTarget() = default; - -/** - * @brief 析构函数 - * - * 销毁渲染目标并释放相关资源 - */ -RenderTarget::~RenderTarget() { destroy(); } - -/** - * @brief 移动构造函数 - * @param other 源渲染目标对象 - * - * 将其他渲染目标的资源转移到新对象 - */ -RenderTarget::RenderTarget(RenderTarget &&other) noexcept - : fbo_(other.fbo_), rbo_(other.rbo_), - colorTexture_(std::move(other.colorTexture_)), - depthTexture_(std::move(other.depthTexture_)), width_(other.width_), - height_(other.height_), colorFormat_(other.colorFormat_), - hasDepth_(other.hasDepth_), hasStencil_(other.hasStencil_), - samples_(other.samples_) { - other.fbo_ = 0; - other.rbo_ = 0; - other.width_ = 0; - other.height_ = 0; -} - -/** - * @brief 移动赋值运算符 - * @param other 源渲染目标对象 - * @return 当前对象引用 - * - * 将其他渲染目标的资源转移到当前对象 - */ -RenderTarget &RenderTarget::operator=(RenderTarget &&other) noexcept { - if (this != &other) { - destroy(); - - fbo_ = other.fbo_; - rbo_ = other.rbo_; - colorTexture_ = std::move(other.colorTexture_); - depthTexture_ = std::move(other.depthTexture_); - width_ = other.width_; - height_ = other.height_; - colorFormat_ = other.colorFormat_; - hasDepth_ = other.hasDepth_; - hasStencil_ = other.hasStencil_; - samples_ = other.samples_; - - other.fbo_ = 0; - other.rbo_ = 0; - other.width_ = 0; - other.height_ = 0; - } - return *this; -} - -/** - * @brief 根据配置创建渲染目标 - * @param config 渲染目标配置 - * @return 创建成功返回true,失败返回false - * - * 根据指定的配置参数创建帧缓冲对象 - */ -bool RenderTarget::create(const RenderTargetConfig &config) { - destroy(); - - width_ = config.width; - height_ = config.height; - colorFormat_ = config.colorFormat; - hasDepth_ = config.hasDepth; - hasStencil_ = config.hasStencil; - samples_ = config.samples; - - if (!createFBO()) { - E2D_ERROR("创建渲染目标失败: {}x{}", width_, height_); - return false; - } - - E2D_INFO("创建渲染目标: {}x{} (深度:{}, 模板:{}, 采样:{})", width_, height_, - hasDepth_, hasStencil_, samples_); - - return true; -} - -/** - * @brief 从现有纹理创建渲染目标 - * @param texture 颜色纹理 - * @param hasDepth 是否创建深度缓冲 - * @return 创建成功返回true,失败返回false - * - * 使用现有纹理作为颜色附件创建帧缓冲对象 - */ -bool RenderTarget::createFromTexture(Ptr texture, bool hasDepth) { - if (!texture || !texture->isValid()) { - E2D_ERROR("无效的颜色纹理"); - return false; - } - - destroy(); - - width_ = texture->getWidth(); - height_ = texture->getHeight(); - colorFormat_ = texture->getFormat(); - hasDepth_ = hasDepth; - hasStencil_ = false; - samples_ = 1; - - colorTexture_ = texture; - - // 创建FBO - glGenFramebuffers(1, &fbo_); - glBindFramebuffer(GL_FRAMEBUFFER, fbo_); - - // 附加颜色纹理 - GLuint texId = static_cast( - reinterpret_cast(texture->getNativeHandle())); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - texId, 0); - - // 创建深度缓冲(如果需要) - if (hasDepth_) { - glGenRenderbuffers(1, &rbo_); - glBindRenderbuffer(GL_RENDERBUFFER, rbo_); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width_, - height_); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, rbo_); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - } - - // 检查完整性 - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - E2D_ERROR("FBO不完整: {:#x}", status); - destroy(); - return false; - } - - E2D_INFO("从纹理创建渲染目标: {}x{}", width_, height_); - return true; -} - -/** - * @brief 销毁渲染目标 - * - * 释放帧缓冲对象和相关资源 - */ -void RenderTarget::destroy() { - deleteFBO(); - - colorTexture_.reset(); - depthTexture_.reset(); - - width_ = 0; - height_ = 0; -} - -/** - * @brief 绑定渲染目标 - * - * 将此渲染目标绑定为当前渲染目标 - */ -void RenderTarget::bind() { - if (!isValid()) { - return; - } - - glBindFramebuffer(GL_FRAMEBUFFER, fbo_); - glViewport(0, 0, width_, height_); -} - -/** - * @brief 解绑渲染目标 - * - * 恢复默认帧缓冲 - */ -void RenderTarget::unbind() { bindDefault(); } - -/** - * @brief 清除渲染目标 - * @param color 清除颜色 - * - * 使用指定颜色清除颜色缓冲,如有深度/模板缓冲也会清除 - */ -void RenderTarget::clear(const Color &color) { - if (!isValid()) { - return; - } - - bind(); - - GLbitfield mask = GL_COLOR_BUFFER_BIT; - if (hasDepth_) { - mask |= GL_DEPTH_BUFFER_BIT; - glClearDepthf(1.0f); // GLES 使用 glClearDepthf - } - if (hasStencil_) { - mask |= GL_STENCIL_BUFFER_BIT; - glClearStencil(0); - } - - glClearColor(color.r, color.g, color.b, color.a); - glClear(mask); -} - -/** - * @brief 设置视口区域 - * @param x 视口左下角X坐标 - * @param y 视口左下角Y坐标 - * @param width 视口宽度 - * @param height 视口高度 - * - * 设置渲染目标的视口区域 - */ -void RenderTarget::setViewport(int x, int y, int width, int height) { - if (!isValid()) { - return; - } - - bind(); - glViewport(x, y, width, height); -} - -/** - * @brief 获取完整视口区域 - * @param[out] x 视口左下角X坐标 - * @param[out] y 视口左下角Y坐标 - * @param[out] width 视口宽度 - * @param[out] height 视口高度 - * - * 获取渲染目标的完整视口区域 - */ -void RenderTarget::getFullViewport(int &x, int &y, int &width, - int &height) const { - x = 0; - y = 0; - width = width_; - height = height_; -} - -/** - * @brief 调整渲染目标大小 - * @param width 新宽度 - * @param height 新高度 - * @return 调整成功返回true,失败返回false - * - * 重新创建指定大小的渲染目标 - */ -bool RenderTarget::resize(int width, int height) { - if (!isValid()) { - return false; - } - - RenderTargetConfig config; - config.width = width; - config.height = height; - config.colorFormat = colorFormat_; - config.hasDepth = hasDepth_; - config.hasStencil = hasStencil_; - config.samples = samples_; - - return create(config); -} - -/** - * @brief 复制到另一个渲染目标 - * @param target 目标渲染目标 - * - * 使用glBlitFramebuffer将内容复制到目标渲染目标 - */ -void RenderTarget::copyTo(RenderTarget &target) { - if (!isValid() || !target.isValid()) { - return; - } - - // 使用glBlitFramebuffer复制 - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target.fbo_); - - GLbitfield mask = GL_COLOR_BUFFER_BIT; - if (hasDepth_ && target.hasDepth_) { - mask |= GL_DEPTH_BUFFER_BIT; - } - - glBlitFramebuffer(0, 0, width_, height_, 0, 0, target.width_, target.height_, - mask, GL_LINEAR); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); -} - -/** - * @brief 复制到屏幕 - * @param screenWidth 屏幕宽度 - * @param screenHeight 屏幕高度 - * - * 将渲染目标内容复制到默认帧缓冲(屏幕) - */ -void RenderTarget::copyToScreen(int screenWidth, int screenHeight) { - if (!isValid()) { - return; - } - - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - - glBlitFramebuffer(0, 0, width_, height_, 0, 0, screenWidth, screenHeight, - GL_COLOR_BUFFER_BIT, GL_LINEAR); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); -} - -/** - * @brief 保存渲染目标到文件 - * @param filepath 文件路径 - * @return 保存成功返回true,失败返回false - * - * 将渲染目标内容保存为PNG图片文件 - */ -bool RenderTarget::saveToFile(const std::string &filepath) { - if (!isValid() || !colorTexture_) { - return false; - } - - // 读取像素数据 - std::vector pixels(width_ * height_ * 4); - - bind(); - glReadPixels(0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); - unbind(); - - // 翻转Y轴(OpenGL坐标系原点在左下角,PNG需要左上角原点) - std::vector flipped(width_ * height_ * 4); - for (int y = 0; y < height_; ++y) { - for (int x = 0; x < width_; ++x) { - int srcIdx = ((height_ - 1 - y) * width_ + x) * 4; - int dstIdx = (y * width_ + x) * 4; - for (int c = 0; c < 4; ++c) { - flipped[dstIdx + c] = pixels[srcIdx + c]; - } - } - } - - // 使用stb_image_write保存为PNG - int result = stbi_write_png(filepath.c_str(), width_, height_, 4, - flipped.data(), width_ * 4); - - if (result == 0) { - E2D_ERROR("保存渲染目标到PNG失败: {}", filepath); - return false; - } - - E2D_INFO("保存渲染目标到: {}", filepath); - return true; -} - -/** - * @brief 根据配置创建渲染目标 - * @param config 渲染目标配置 - * @return 创建成功返回渲染目标指针,失败返回nullptr - * - * 静态工厂方法,创建并初始化渲染目标 - */ -Ptr -RenderTarget::createFromConfig(const RenderTargetConfig &config) { - auto rt = std::make_shared(); - if (rt->create(config)) { - return rt; - } - return nullptr; -} - -/** - * @brief 获取当前绑定的帧缓冲对象ID - * @return 当前绑定的FBO ID - * - * 查询OpenGL当前绑定的帧缓冲对象 - */ -GLuint RenderTarget::getCurrentFBO() { - GLint fbo = 0; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo); - return static_cast(fbo); -} - -/** - * @brief 绑定默认帧缓冲 - * - * 将默认帧缓冲(屏幕)绑定为当前渲染目标 - */ -void RenderTarget::bindDefault() { glBindFramebuffer(GL_FRAMEBUFFER, 0); } - -// ============================================================================ -// 内部方法 -// ============================================================================ - -/** - * @brief 创建帧缓冲对象 - * @return 创建成功返回true,失败返回false - * - * 内部方法,创建FBO及相关附件 - */ -bool RenderTarget::createFBO() { - // 创建颜色纹理 - colorTexture_ = GLTexture::create(width_, height_, colorFormat_); - if (!colorTexture_ || !colorTexture_->isValid()) { - E2D_ERROR("创建颜色纹理失败"); - return false; - } - - // 创建FBO - glGenFramebuffers(1, &fbo_); - glBindFramebuffer(GL_FRAMEBUFFER, fbo_); - - // 附加颜色纹理 - GLuint colorTexId = - static_cast(colorTexture_.get())->getTextureID(); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - colorTexId, 0); - - // 创建深度/模板缓冲 - if (hasDepth_ || hasStencil_) { - glGenRenderbuffers(1, &rbo_); - glBindRenderbuffer(GL_RENDERBUFFER, rbo_); - - if (hasDepth_ && hasStencil_) { - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width_, - height_); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, rbo_); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, rbo_); - } else if (hasDepth_) { - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width_, - height_); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, rbo_); - } - - glBindRenderbuffer(GL_RENDERBUFFER, 0); - } - - // 检查完整性 - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - E2D_ERROR("FBO创建失败,状态: {:#x}", status); - deleteFBO(); - return false; - } - - return true; -} - -/** - * @brief 删除帧缓冲对象 - * - * 内部方法,删除FBO和渲染缓冲对象 - */ -void RenderTarget::deleteFBO() { - if (rbo_ != 0) { - glDeleteRenderbuffers(1, &rbo_); - rbo_ = 0; - } - - if (fbo_ != 0) { - glDeleteFramebuffers(1, &fbo_); - fbo_ = 0; - } -} - -// ============================================================================ -// MultisampleRenderTarget实现 -// ============================================================================ - -/** - * @brief 创建多重采样渲染目标 - * @param width 宽度 - * @param height 高度 - * @param samples 采样数 - * @return 创建成功返回true,失败返回false - * - * 创建支持多重采样抗锯齿的渲染目标 - */ -bool MultisampleRenderTarget::create(int width, int height, int samples) { - // 先销毁现有的 - destroy(); - - width_ = width; - height_ = height; - samples_ = samples; - hasDepth_ = true; - hasStencil_ = false; - colorFormat_ = PixelFormat::RGBA8; - - // 创建FBO - glGenFramebuffers(1, &fbo_); - glBindFramebuffer(GL_FRAMEBUFFER, fbo_); - - // 创建多重采样颜色渲染缓冲 - glGenRenderbuffers(1, &colorRBO_); - glBindRenderbuffer(GL_RENDERBUFFER, colorRBO_); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA8, width, - height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_RENDERBUFFER, colorRBO_); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - - // 创建多重采样深度渲染缓冲 - glGenRenderbuffers(1, &rbo_); - glBindRenderbuffer(GL_RENDERBUFFER, rbo_); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, - GL_DEPTH_COMPONENT24, width, height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, rbo_); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - - // 检查完整性 - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - E2D_ERROR("多重采样FBO创建失败,状态: {:#x}", status); - destroy(); - return false; - } - - E2D_INFO("创建多重采样渲染目标: {}x{} (采样数: {})", width, height, samples); - return true; -} - -/** - * @brief 销毁多重采样渲染目标 - * - * 释放多重采样渲染缓冲和帧缓冲对象 - */ -void MultisampleRenderTarget::destroy() { - // 删除颜色渲染缓冲 - if (colorRBO_ != 0) { - glDeleteRenderbuffers(1, &colorRBO_); - colorRBO_ = 0; - } - - // 调用基类destroy - RenderTarget::destroy(); -} - -/** - * @brief 解析多重采样到目标渲染目标 - * @param target 目标渲染目标 - * - * 将多重采样渲染目标解析到普通渲染目标 - */ -void MultisampleRenderTarget::resolveTo(RenderTarget &target) { - if (!isValid() || !target.isValid()) { - return; - } - - // 使用glBlitFramebuffer解析多重采样 - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target.getFBO()); - - glBlitFramebuffer(0, 0, width_, height_, 0, 0, target.getWidth(), - target.getHeight(), GL_COLOR_BUFFER_BIT, GL_LINEAR); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); -} - -// ============================================================================ -// RenderTargetStack实现 -// ============================================================================ - -/** - * @brief 获取RenderTargetStack单例实例 - * @return RenderTargetStack单例的引用 - * - * 使用静态局部变量实现线程安全的单例模式 - */ -RenderTargetStack &RenderTargetStack::get() { - static RenderTargetStack instance; - return instance; -} - -/** - * @brief 压入渲染目标到栈 - * @param target 渲染目标指针 - * - * 将渲染目标压入栈并绑定为当前渲染目标 - */ -void RenderTargetStack::push(RenderTarget *target) { - std::lock_guard lock(mutex_); - - if (target) { - stack_.push_back(target); - target->bind(); - } -} - -/** - * @brief 弹出栈顶渲染目标 - * - * 弹出当前渲染目标并恢复前一个渲染目标 - */ -void RenderTargetStack::pop() { - std::lock_guard lock(mutex_); - - if (!stack_.empty()) { - stack_.pop_back(); - } - - // 绑定新的当前渲染目标 - if (!stack_.empty()) { - stack_.back()->bind(); - } else { - RenderTarget::bindDefault(); - } -} - -/** - * @brief 获取当前渲染目标 - * @return 当前渲染目标指针,栈为空返回nullptr - */ -RenderTarget *RenderTargetStack::getCurrent() const { - std::lock_guard lock(mutex_); - - if (stack_.empty()) { - return nullptr; - } - return stack_.back(); -} - -/** - * @brief 获取栈大小 - * @return 栈中渲染目标的数量 - */ -size_t RenderTargetStack::size() const { - std::lock_guard lock(mutex_); - return stack_.size(); -} - -/** - * @brief 清空渲染目标栈 - * - * 清空栈并恢复默认帧缓冲 - */ -void RenderTargetStack::clear() { - std::lock_guard lock(mutex_); - stack_.clear(); - RenderTarget::bindDefault(); -} - -// ============================================================================ -// RenderTargetMgr实现 -// ============================================================================ - -/** - * @brief 获取RenderTargetMgr单例实例 - * @return RenderTargetMgr单例的引用 - * - * 使用静态局部变量实现线程安全的单例模式 - */ -RenderTargetMgr &RenderTargetMgr::get() { - static RenderTargetMgr instance; - return instance; -} - -/** - * @brief 初始化渲染目标管理器 - * @param width 默认渲染目标宽度 - * @param height 默认渲染目标高度 - * @return 初始化成功返回true,失败返回false - * - * 创建默认渲染目标 - */ -bool RenderTargetMgr::init(int width, int height) { - if (initialized_) { - return true; - } - - // 创建默认渲染目标 - RenderTargetConfig config; - config.width = width; - config.height = height; - config.hasDepth = true; - config.hasStencil = false; - - defaultRenderTarget_ = RenderTarget::createFromConfig(config); - if (!defaultRenderTarget_) { - E2D_ERROR("创建默认渲染目标失败"); - return false; - } - - initialized_ = true; - E2D_INFO("渲染目标管理器初始化完成: {}x{}", width, height); - return true; -} - -/** - * @brief 关闭渲染目标管理器 - * - * 清理所有渲染目标资源 - */ -void RenderTargetMgr::shutdown() { - if (!initialized_) { - return; - } - - renderTargets_.clear(); - defaultRenderTarget_.reset(); - initialized_ = false; - - E2D_INFO("渲染目标管理器已关闭"); -} - -/** - * @brief 创建渲染目标 - * @param config 渲染目标配置 - * @return 创建成功返回渲染目标指针,失败返回nullptr - * - * 创建新的渲染目标并由管理器管理 - */ -Ptr -RenderTargetMgr::createRenderTarget(const RenderTargetConfig &config) { - if (!initialized_) { - E2D_ERROR("渲染目标管理器未初始化"); - return nullptr; - } - - auto rt = RenderTarget::createFromConfig(config); - if (rt) { - renderTargets_.push_back(rt); - } - return rt; -} - -/** - * @brief 调整所有渲染目标大小 - * @param width 新宽度 - * @param height 新高度 - * - * 调整默认渲染目标的大小 - */ -void RenderTargetMgr::resize(int width, int height) { - if (!initialized_) { - return; - } - - // 调整默认渲染目标大小 - if (defaultRenderTarget_) { - defaultRenderTarget_->resize(width, height); - } - - E2D_INFO("渲染目标管理器调整大小: {}x{}", width, height); -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/memory/gpu_context.cpp b/Extra2D/src/graphics/memory/gpu_context.cpp deleted file mode 100644 index 93684b7..0000000 --- a/Extra2D/src/graphics/memory/gpu_context.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include - -namespace extra2d { - -/** - * @brief 获取GPUContext单例实例 - * @return GPUContext单例的引用 - * - * 使用静态局部变量实现线程安全的单例模式 - */ -GPUContext& GPUContext::get() { - static GPUContext instance; - return instance; -} - -/** - * @brief 标记GPU上下文为有效状态 - * - * 使用原子操作设置有效标志为true,使用release内存序 - */ -void GPUContext::markValid() { - valid_.store(true, std::memory_order_release); -} - -/** - * @brief 标记GPU上下文为无效状态 - * - * 使用原子操作设置有效标志为false,使用release内存序 - */ -void GPUContext::markInvalid() { - valid_.store(false, std::memory_order_release); -} - -/** - * @brief 检查GPU上下文是否有效 - * @return 如果GPU上下文有效返回true,否则返回false - * - * 使用原子操作读取有效标志,使用acquire内存序 - */ -bool GPUContext::isValid() const { - return valid_.load(std::memory_order_acquire); -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/memory/vram_manager.cpp b/Extra2D/src/graphics/memory/vram_manager.cpp deleted file mode 100644 index 768ae52..0000000 --- a/Extra2D/src/graphics/memory/vram_manager.cpp +++ /dev/null @@ -1,194 +0,0 @@ -#include -#include -#include -#include - - -namespace extra2d { - -// Switch 推荐 VRAM 预算 ~400MB -static constexpr size_t DEFAULT_VRAM_BUDGET = 400 * 1024 * 1024; - -/** - * @brief 默认构造函数 - * - * 初始化VRAM管理器,设置默认预算为400MB - */ -VRAMMgr::VRAMMgr() - : textureVRAM_(0), bufferVRAM_(0), vramBudget_(DEFAULT_VRAM_BUDGET), - textureAllocCount_(0), textureFreeCount_(0), bufferAllocCount_(0), - bufferFreeCount_(0), peakTextureVRAM_(0), peakBufferVRAM_(0) {} - -/** - * @brief 获取VRAMMgr单例实例 - * @return VRAMMgr单例的引用 - * - * 使用静态局部变量实现线程安全的单例模式 - */ -VRAMMgr &VRAMMgr::get() { - static VRAMMgr instance; - return instance; -} - -/** - * @brief 分配纹理VRAM - * @param size 分配的字节数 - * - * 增加纹理VRAM使用量并更新峰值,如果超出预算则输出警告 - */ -void VRAMMgr::allocTexture(size_t size) { - std::lock_guard lock(mutex_); - textureVRAM_ += size; - textureAllocCount_++; - peakTextureVRAM_ = std::max(peakTextureVRAM_, textureVRAM_); - - if (isOverBudget()) { - E2D_LOG_WARN("显存超出预算! 已使用: {} MB / 预算: {} MB", - getUsedVRAM() / (1024 * 1024), vramBudget_ / (1024 * 1024)); - } -} - -/** - * @brief 释放纹理VRAM - * @param size 释放的字节数 - * - * 减少纹理VRAM使用量 - */ -void VRAMMgr::freeTexture(size_t size) { - std::lock_guard lock(mutex_); - if (size <= textureVRAM_) { - textureVRAM_ -= size; - } else { - textureVRAM_ = 0; - } - textureFreeCount_++; -} - -/** - * @brief 分配缓冲VRAM - * @param size 分配的字节数 - * - * 增加缓冲VRAM使用量并更新峰值,如果超出预算则输出警告 - */ -void VRAMMgr::allocBuffer(size_t size) { - std::lock_guard lock(mutex_); - bufferVRAM_ += size; - bufferAllocCount_++; - peakBufferVRAM_ = std::max(peakBufferVRAM_, bufferVRAM_); - - if (isOverBudget()) { - E2D_LOG_WARN("显存超出预算! 已使用: {} MB / 预算: {} MB", - getUsedVRAM() / (1024 * 1024), vramBudget_ / (1024 * 1024)); - } -} - -/** - * @brief 释放缓冲VRAM - * @param size 释放的字节数 - * - * 减少缓冲VRAM使用量 - */ -void VRAMMgr::freeBuffer(size_t size) { - std::lock_guard lock(mutex_); - if (size <= bufferVRAM_) { - bufferVRAM_ -= size; - } else { - bufferVRAM_ = 0; - } - bufferFreeCount_++; -} - -/** - * @brief 获取总VRAM使用量 - * @return 总使用字节数 - * - * 返回纹理和缓冲VRAM使用量的总和 - */ -size_t VRAMMgr::getUsedVRAM() const { return textureVRAM_ + bufferVRAM_; } - -/** - * @brief 获取纹理VRAM使用量 - * @return 纹理使用字节数 - */ -size_t VRAMMgr::getTextureVRAM() const { return textureVRAM_; } - -/** - * @brief 获取缓冲VRAM使用量 - * @return 缓冲使用字节数 - */ -size_t VRAMMgr::getBufferVRAM() const { return bufferVRAM_; } - -/** - * @brief 获取可用VRAM - * @return 可用字节数 - * - * 返回预算内剩余的VRAM空间 - */ -size_t VRAMMgr::getAvailableVRAM() const { - size_t used = getUsedVRAM(); - return (used < vramBudget_) ? (vramBudget_ - used) : 0; -} - -/** - * @brief 设置VRAM预算 - * @param budget 预算字节数 - * - * 设置VRAM使用上限 - */ -void VRAMMgr::setVRAMBudget(size_t budget) { - std::lock_guard lock(mutex_); - vramBudget_ = budget; - E2D_LOG_INFO("显存预算设置为 {} MB", budget / (1024 * 1024)); -} - -/** - * @brief 获取VRAM预算 - * @return 预算字节数 - */ -size_t VRAMMgr::getVRAMBudget() const { return vramBudget_; } - -/** - * @brief 检查是否超出预算 - * @return 超出预算返回true,否则返回false - */ -bool VRAMMgr::isOverBudget() const { return getUsedVRAM() > vramBudget_; } - -/** - * @brief 打印VRAM统计信息 - * - * 输出纹理和缓冲的VRAM使用情况、峰值和分配/释放次数 - */ -void VRAMMgr::printStats() const { - std::lock_guard lock(mutex_); - E2D_LOG_INFO("=== 显存统计 ==="); - E2D_LOG_INFO(" 纹理显存: {} MB (峰值: {} MB)", - textureVRAM_ / (1024 * 1024), peakTextureVRAM_ / (1024 * 1024)); - E2D_LOG_INFO(" 缓冲显存: {} MB (峰值: {} MB)", - bufferVRAM_ / (1024 * 1024), peakBufferVRAM_ / (1024 * 1024)); - E2D_LOG_INFO(" 总计使用: {} MB / {} MB 预算", - (textureVRAM_ + bufferVRAM_) / (1024 * 1024), - vramBudget_ / (1024 * 1024)); - E2D_LOG_INFO(" 纹理分配/释放次数: {} / {}", textureAllocCount_, - textureFreeCount_); - E2D_LOG_INFO(" 缓冲分配/释放次数: {} / {}", bufferAllocCount_, - bufferFreeCount_); -} - -/** - * @brief 重置所有统计信息 - * - * 清零所有VRAM计数器和峰值记录 - */ -void VRAMMgr::reset() { - std::lock_guard lock(mutex_); - textureVRAM_ = 0; - bufferVRAM_ = 0; - textureAllocCount_ = 0; - textureFreeCount_ = 0; - bufferAllocCount_ = 0; - bufferFreeCount_ = 0; - peakTextureVRAM_ = 0; - peakBufferVRAM_ = 0; -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/shader/shader_cache.cpp b/Extra2D/src/graphics/shader/shader_cache.cpp deleted file mode 100644 index f63e421..0000000 --- a/Extra2D/src/graphics/shader/shader_cache.cpp +++ /dev/null @@ -1,293 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - - -namespace extra2d { - -namespace fs = std::filesystem; - -/** - * @brief 获取单例实例 - * @return 缓存管理器实例引用 - */ -ShaderCache &ShaderCache::getInstance() { - static ShaderCache instance; - return instance; -} - -/** - * @brief 初始化缓存系统 - * @param cacheDir 缓存目录路径 - * @return 初始化成功返回true,失败返回false - */ -bool ShaderCache::init(const std::string &cacheDir) { - cacheDir_ = cacheDir; - - if (!ensureCacheDirectory()) { - E2D_LOG_ERROR("创建缓存目录失败: {}", cacheDir); - return false; - } - - if (!loadCacheIndex()) { - E2D_LOG_WARN("加载缓存索引失败,重新开始"); - } - - initialized_ = true; - E2D_LOG_INFO("着色器缓存已初始化,位置: {}", cacheDir); - return true; -} - -/** - * @brief 关闭缓存系统 - */ -void ShaderCache::shutdown() { - if (!initialized_) { - return; - } - - saveCacheIndex(); - cacheMap_.clear(); - initialized_ = false; - E2D_LOG_INFO("着色器缓存已关闭"); -} - -/** - * @brief 检查缓存是否有效 - * @param name Shader名称 - * @param sourceHash 源码哈希值 - * @return 缓存有效返回true,否则返回false - */ -bool ShaderCache::hasValidCache(const std::string &name, - const std::string &sourceHash) { - auto it = cacheMap_.find(name); - if (it == cacheMap_.end()) { - return false; - } - - return it->second.sourceHash == sourceHash; -} - -/** - * @brief 加载缓存的二进制数据 - * @param name Shader名称 - * @return 缓存条目指针,不存在返回nullptr - */ -Ptr ShaderCache::loadCache(const std::string &name) { - auto it = cacheMap_.find(name); - if (it == cacheMap_.end()) { - return nullptr; - } - - std::string cachePath = getCachePath(name); - std::ifstream file(cachePath, std::ios::binary); - if (!file.is_open()) { - E2D_LOG_WARN("打开缓存文件失败: {}", cachePath); - return nullptr; - } - - auto entry = std::make_shared(it->second); - entry->binary.clear(); - - file.seekg(0, std::ios::end); - size_t fileSize = static_cast(file.tellg()); - file.seekg(0, std::ios::beg); - - entry->binary.resize(fileSize); - file.read(reinterpret_cast(entry->binary.data()), fileSize); - - return entry; -} - -/** - * @brief 保存编译结果到缓存 - * @param entry 缓存条目 - * @return 保存成功返回true,失败返回false - */ -bool ShaderCache::saveCache(const ShaderCacheEntry &entry) { - if (!initialized_) { - E2D_LOG_WARN("着色器缓存未初始化,无法保存缓存"); - return false; - } - - if (entry.binary.empty()) { - E2D_LOG_WARN("着色器二进制数据为空,跳过缓存保存: {}", - entry.name); - return false; - } - - std::string cachePath = getCachePath(entry.name); - E2D_LOG_DEBUG("正在保存着色器缓存到: {} ({} 字节)", cachePath, - entry.binary.size()); - - std::ofstream file(cachePath, std::ios::binary); - if (!file.is_open()) { - E2D_LOG_ERROR("创建缓存文件失败: {}", cachePath); - return false; - } - - file.write(reinterpret_cast(entry.binary.data()), - entry.binary.size()); - file.close(); - - cacheMap_[entry.name] = entry; - saveCacheIndex(); - - E2D_LOG_INFO("着色器缓存已保存: {} ({} 字节)", entry.name, - entry.binary.size()); - return true; -} - -/** - * @brief 使缓存失效 - * @param name Shader名称 - */ -void ShaderCache::invalidate(const std::string &name) { - auto it = cacheMap_.find(name); - if (it == cacheMap_.end()) { - return; - } - - std::string cachePath = getCachePath(name); - fs::remove(cachePath); - - cacheMap_.erase(it); - saveCacheIndex(); - - E2D_LOG_DEBUG("着色器缓存已失效: {}", name); -} - -/** - * @brief 清除所有缓存 - */ -void ShaderCache::clearAll() { - for (const auto &pair : cacheMap_) { - std::string cachePath = getCachePath(pair.first); - fs::remove(cachePath); - } - - cacheMap_.clear(); - saveCacheIndex(); - - E2D_LOG_INFO("所有着色器缓存已清除"); -} - -/** - * @brief 计算源码哈希值 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 哈希值字符串 - */ -std::string ShaderCache::computeHash(const std::string &vertSource, - const std::string &fragSource) { - std::string combined = vertSource + fragSource; - - uint32_t hash = 5381; - for (char c : combined) { - hash = ((hash << 5) + hash) + static_cast(c); - } - - std::stringstream ss; - ss << std::hex << hash; - return ss.str(); -} - -/** - * @brief 加载缓存索引 - * @return 加载成功返回true,失败返回false - */ -bool ShaderCache::loadCacheIndex() { - std::string indexPath = cacheDir_ + "/.cache_index"; - - if (!fs::exists(indexPath)) { - return true; - } - - std::ifstream file(indexPath); - if (!file.is_open()) { - return false; - } - - std::string line; - while (std::getline(file, line)) { - if (line.empty() || line[0] == '#') { - continue; - } - - size_t pos = line.find('='); - if (pos == std::string::npos) { - continue; - } - - std::string name = line.substr(0, pos); - std::string hash = line.substr(pos + 1); - - std::string cachePath = getCachePath(name); - if (fs::exists(cachePath)) { - ShaderCacheEntry entry; - entry.name = name; - entry.sourceHash = hash; - entry.compileTime = static_cast( - std::chrono::system_clock::now().time_since_epoch().count()); - cacheMap_[name] = entry; - } - } - - return true; -} - -/** - * @brief 保存缓存索引 - * @return 保存成功返回true,失败返回false - */ -bool ShaderCache::saveCacheIndex() { - std::string indexPath = cacheDir_ + "/.cache_index"; - - std::ofstream file(indexPath); - if (!file.is_open()) { - return false; - } - - file << "# Extra2D Shader Cache Index\n"; - file << "# Format: name=hash\n"; - - for (const auto &pair : cacheMap_) { - file << pair.first << "=" << pair.second.sourceHash << "\n"; - } - - return true; -} - -/** - * @brief 获取缓存文件路径 - * @param name Shader名称 - * @return 缓存文件完整路径 - */ -std::string ShaderCache::getCachePath(const std::string &name) const { - return cacheDir_ + "/" + name + ".cache"; -} - -/** - * @brief 确保缓存目录存在 - * @return 目录存在或创建成功返回true,否则返回false - */ -bool ShaderCache::ensureCacheDirectory() { - if (cacheDir_.empty()) { - return false; - } - - std::error_code ec; - if (!fs::exists(cacheDir_)) { - if (!fs::create_directories(cacheDir_, ec)) { - return false; - } - } - - return true; -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/shader/shader_hot_reloader.cpp b/Extra2D/src/graphics/shader/shader_hot_reloader.cpp deleted file mode 100644 index 847e8d8..0000000 --- a/Extra2D/src/graphics/shader/shader_hot_reloader.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include -#include -#include -#include -#include - -namespace extra2d { - -namespace fs = std::filesystem; - -/** - * @brief 获取单例实例 - * @return 热重载管理器实例引用 - */ -ShaderHotReloader &ShaderHotReloader::getInstance() { - static ShaderHotReloader instance; - return instance; -} - -/** - * @brief 初始化热重载系统 - * @return 初始化成功返回true,失败返回false - */ -bool ShaderHotReloader::init() { - if (initialized_) { - return true; - } - -#ifdef _WIN32 - buffer_.resize(4096); -#endif - - initialized_ = true; - E2D_LOG_INFO("着色器热重载器已初始化"); - return true; -} - -/** - * @brief 关闭热重载系统 - */ -void ShaderHotReloader::shutdown() { - if (!initialized_) { - return; - } - -#ifdef _WIN32 - if (watchHandle_ != nullptr) { - FindCloseChangeNotification(watchHandle_); - watchHandle_ = nullptr; - } -#endif - - watchMap_.clear(); - initialized_ = false; - enabled_ = false; - E2D_LOG_INFO("着色器热重载器已关闭"); -} - -/** - * @brief 注册Shader文件监视 - * @param shaderName Shader名称 - * @param filePaths 要监视的文件列表 - * @param callback 文件变化时的回调 - */ -void ShaderHotReloader::watch(const std::string &shaderName, - const std::vector &filePaths, - FileChangeCallback callback) { - if (!initialized_) { - E2D_LOG_WARN("热重载器未初始化"); - return; - } - - WatchInfo info; - info.filePaths = filePaths; - info.callback = callback; - - for (const auto &path : filePaths) { - info.modifiedTimes[path] = getFileModifiedTime(path); - } - - watchMap_[shaderName] = std::move(info); - E2D_LOG_DEBUG("正在监视着色器: {} ({} 个文件)", shaderName, filePaths.size()); -} - -/** - * @brief 取消监视 - * @param shaderName Shader名称 - */ -void ShaderHotReloader::unwatch(const std::string &shaderName) { - auto it = watchMap_.find(shaderName); - if (it != watchMap_.end()) { - watchMap_.erase(it); - E2D_LOG_DEBUG("停止监视着色器: {}", shaderName); - } -} - -/** - * @brief 更新文件监视(在主循环中调用) - */ -void ShaderHotReloader::update() { - if (!initialized_ || !enabled_) { - return; - } - - pollChanges(); -} - -/** - * @brief 启用/禁用热重载 - * @param enabled 是否启用 - */ -void ShaderHotReloader::setEnabled(bool enabled) { - enabled_ = enabled; - E2D_LOG_DEBUG("热重载已{}", enabled ? "Enable" : "Disable"); -} - -/** - * @brief 轮询检查文件变化 - */ -void ShaderHotReloader::pollChanges() { - auto now = static_cast( - std::chrono::system_clock::now().time_since_epoch().count()); - - for (auto &pair : watchMap_) { - WatchInfo &info = pair.second; - - for (const auto &filePath : info.filePaths) { - uint64_t currentModTime = getFileModifiedTime(filePath); - uint64_t lastModTime = info.modifiedTimes[filePath]; - - if (currentModTime != 0 && lastModTime != 0 && - currentModTime != lastModTime) { - info.modifiedTimes[filePath] = currentModTime; - - FileChangeEvent event; - event.filepath = filePath; - event.type = FileChangeEvent::Type::Modified; - event.timestamp = now; - - E2D_LOG_DEBUG("着色器文件已更改: {}", filePath); - - if (info.callback) { - info.callback(event); - } - } - } - } -} - -/** - * @brief 获取文件修改时间 - * @param filepath 文件路径 - * @return 修改时间戳 - */ -uint64_t ShaderHotReloader::getFileModifiedTime(const std::string &filepath) { - try { - auto ftime = fs::last_write_time(filepath); - auto sctp = std::chrono::time_point_cast( - ftime - fs::file_time_type::clock::now() + - std::chrono::system_clock::now()); - return static_cast(sctp.time_since_epoch().count()); - } catch (...) { - return 0; - } -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/shader/shader_loader.cpp b/Extra2D/src/graphics/shader/shader_loader.cpp deleted file mode 100644 index 21ffcf6..0000000 --- a/Extra2D/src/graphics/shader/shader_loader.cpp +++ /dev/null @@ -1,452 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -namespace fs = std::filesystem; - -/** - * @brief 构造函数,初始化Shader加载器 - */ -ShaderLoader::ShaderLoader() {} - -/** - * @brief 从分离文件加载Shader (.vert + .frag) - * @param name Shader名称 - * @param vertPath 顶点着色器文件路径 - * @param fragPath 片段着色器文件路径 - * @return 加载结果 - */ -ShaderLoadResult -ShaderLoader::loadFromSeparateFiles(const std::string &name, - const std::string &vertPath, - const std::string &fragPath) { - - ShaderLoadResult result; - - if (!fileExists(vertPath)) { - result.errorMessage = "Vertex shader file not found: " + vertPath; - E2D_LOG_ERROR("{}", result.errorMessage); - return result; - } - - if (!fileExists(fragPath)) { - result.errorMessage = "Fragment shader file not found: " + fragPath; - E2D_LOG_ERROR("{}", result.errorMessage); - return result; - } - - std::string vertSource = readFile(vertPath); - std::string fragSource = readFile(fragPath); - - if (vertSource.empty()) { - result.errorMessage = "Failed to read vertex shader file: " + vertPath; - E2D_LOG_ERROR("{}", result.errorMessage); - return result; - } - - if (fragSource.empty()) { - result.errorMessage = "Failed to read fragment shader file: " + fragPath; - E2D_LOG_ERROR("{}", result.errorMessage); - return result; - } - - fs::path vertDir = fs::path(vertPath).parent_path(); - fs::path fragDir = fs::path(fragPath).parent_path(); - - vertSource = - processIncludes(vertSource, vertDir.string(), result.dependencies); - fragSource = - processIncludes(fragSource, fragDir.string(), result.dependencies); - - result.vertSource = vertSource; - result.fragSource = fragSource; - result.success = true; - - return result; -} - -/** - * @brief 从组合文件加载Shader (.shader) - * @param path 组合Shader文件路径 - * @return 加载结果 - */ -ShaderLoadResult ShaderLoader::loadFromCombinedFile(const std::string &path) { - ShaderLoadResult result; - - if (!fileExists(path)) { - result.errorMessage = "Shader file not found: " + path; - E2D_LOG_ERROR("{}", result.errorMessage); - return result; - } - - std::string content = readFile(path); - if (content.empty()) { - result.errorMessage = "Failed to read shader file: " + path; - E2D_LOG_ERROR("{}", result.errorMessage); - return result; - } - - ShaderMetadata metadata; - std::string vertSource, fragSource; - - if (!parseCombinedFile(content, vertSource, fragSource, metadata)) { - result.errorMessage = "Failed to parse combined shader file: " + path; - E2D_LOG_ERROR("{}", result.errorMessage); - return result; - } - - fs::path baseDir = fs::path(path).parent_path(); - vertSource = - processIncludes(vertSource, baseDir.string(), result.dependencies); - fragSource = - processIncludes(fragSource, baseDir.string(), result.dependencies); - - result.vertSource = vertSource; - result.fragSource = fragSource; - result.success = true; - - return result; -} - -/** - * @brief 从源码字符串加载Shader - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 加载结果 - */ -ShaderLoadResult ShaderLoader::loadFromSource(const std::string &vertSource, - const std::string &fragSource) { - - ShaderLoadResult result; - result.vertSource = vertSource; - result.fragSource = fragSource; - result.success = true; - return result; -} - -/** - * @brief 处理Shader源码中的#include指令 - * @param source 原始源码 - * @param baseDir 基础目录 - * @param outDependencies 输出依赖列表 - * @return 处理后的源码 - */ -std::string -ShaderLoader::processIncludes(const std::string &source, - const std::string &baseDir, - std::vector &outDependencies) { - - std::string result; - std::istringstream stream(source); - std::string line; - - while (std::getline(stream, line)) { - size_t includePos = line.find("#include"); - if (includePos != std::string::npos) { - size_t startQuote = line.find('"', includePos); - size_t endQuote = line.find('"', startQuote + 1); - - if (startQuote != std::string::npos && endQuote != std::string::npos) { - std::string includeName = - line.substr(startQuote + 1, endQuote - startQuote - 1); - std::string includePath = findIncludeFile(includeName, baseDir); - - if (!includePath.empty()) { - auto cacheIt = includeCache_.find(includePath); - std::string includeContent; - - if (cacheIt != includeCache_.end()) { - includeContent = cacheIt->second; - } else { - includeContent = readFile(includePath); - includeCache_[includePath] = includeContent; - } - - outDependencies.push_back(includePath); - result += includeContent; - result += "\n"; - continue; - } else { - E2D_LOG_WARN("未找到包含文件: {}", includeName); - } - } - } - - result += line; - result += "\n"; - } - - return result; -} - -/** - * @brief 应用预处理器定义 - * @param source 原始源码 - * @param defines 预处理器定义列表 - * @return 处理后的源码 - */ -std::string -ShaderLoader::applyDefines(const std::string &source, - const std::vector &defines) { - - if (defines.empty()) { - return source; - } - - std::string defineBlock; - for (const auto &def : defines) { - defineBlock += "#define " + def + "\n"; - } - - std::string result; - std::istringstream stream(source); - std::string line; - bool inserted = false; - - while (std::getline(stream, line)) { - if (!inserted && (line.find("#version") != std::string::npos || - line.find("precision") != std::string::npos)) { - result += line + "\n"; - continue; - } - - if (!inserted) { - result += defineBlock; - inserted = true; - } - - result += line + "\n"; - } - - return result; -} - -/** - * @brief 获取Shader元数据 - * @param path Shader文件路径 - * @return 元数据 - */ -ShaderMetadata ShaderLoader::getMetadata(const std::string &path) { - ShaderMetadata metadata; - - if (!fileExists(path)) { - return metadata; - } - - metadata.combinedPath = path; - metadata.lastModified = getFileModifiedTime(path); - - fs::path p(path); - metadata.name = p.stem().string(); - - return metadata; -} - -/** - * @brief 添加include搜索路径 - * @param path 搜索路径 - */ -void ShaderLoader::addIncludePath(const std::string &path) { - if (std::find(includePaths_.begin(), includePaths_.end(), path) == - includePaths_.end()) { - includePaths_.push_back(path); - } -} - -/** - * @brief 读取文件内容 - * @param filepath 文件路径 - * @return 文件内容字符串 - */ -std::string ShaderLoader::readFile(const std::string &filepath) { - std::ifstream file(filepath, std::ios::binary); - if (!file.is_open()) { - return ""; - } - - std::ostringstream content; - content << file.rdbuf(); - return content.str(); -} - -/** - * @brief 获取文件修改时间 - * @param filepath 文件路径 - * @return 修改时间戳 - */ -uint64_t ShaderLoader::getFileModifiedTime(const std::string &filepath) { -#ifdef __SWITCH__ - (void)filepath; - return 1; -#else - try { - auto ftime = fs::last_write_time(filepath); - auto sctp = std::chrono::time_point_cast( - ftime - fs::file_time_type::clock::now() + - std::chrono::system_clock::now()); - return static_cast(sctp.time_since_epoch().count()); - } catch (...) { - return 0; - } -#endif -} - -/** - * @brief 检查文件是否存在 - * @param filepath 文件路径 - * @return 存在返回true,否则返回false - */ -bool ShaderLoader::fileExists(const std::string &filepath) { - return fs::exists(filepath); -} - -/** - * @brief 解析组合Shader文件 - * @param content 文件内容 - * @param outVert 输出顶点着色器源码 - * @param outFrag 输出片段着色器源码 - * @param outMetadata 输出元数据 - * @return 解析成功返回true,失败返回false - */ -bool ShaderLoader::parseCombinedFile(const std::string &content, - std::string &outVert, std::string &outFrag, - ShaderMetadata &outMetadata) { - enum class Section { None, Meta, Vertex, Fragment }; - - Section currentSection = Section::None; - std::string metaContent; - std::string vertContent; - std::string fragContent; - - std::istringstream stream(content); - std::string line; - - while (std::getline(stream, line)) { - std::string trimmedLine = line; - size_t start = trimmedLine.find_first_not_of(" \t\r\n"); - if (start != std::string::npos) { - trimmedLine = trimmedLine.substr(start); - } - size_t end = trimmedLine.find_last_not_of(" \t\r\n"); - if (end != std::string::npos) { - trimmedLine = trimmedLine.substr(0, end + 1); - } - - if (trimmedLine == "#meta") { - currentSection = Section::Meta; - continue; - } else if (trimmedLine == "#vertex") { - currentSection = Section::Vertex; - continue; - } else if (trimmedLine == "#fragment") { - currentSection = Section::Fragment; - continue; - } - - switch (currentSection) { - case Section::Meta: - metaContent += line + "\n"; - break; - case Section::Vertex: - vertContent += line + "\n"; - break; - case Section::Fragment: - fragContent += line + "\n"; - break; - default: - break; - } - } - - if (vertContent.empty() || fragContent.empty()) { - return false; - } - - if (!metaContent.empty()) { - parseMetadata(metaContent, outMetadata); - } - - outVert = vertContent; - outFrag = fragContent; - return true; -} - -/** - * @brief 解析元数据JSON块 - * @param jsonContent JSON内容 - * @param outMetadata 输出元数据 - * @return 解析成功返回true,失败返回false - */ -bool ShaderLoader::parseMetadata(const std::string &jsonContent, - ShaderMetadata &outMetadata) { - std::string content = jsonContent; - - size_t start = content.find('{'); - size_t end = content.rfind('}'); - if (start == std::string::npos || end == std::string::npos || end <= start) { - return false; - } - - content = content.substr(start, end - start + 1); - - auto extractString = [&content](const std::string &key) -> std::string { - std::string searchKey = "\"" + key + "\""; - size_t keyPos = content.find(searchKey); - if (keyPos == std::string::npos) { - return ""; - } - - size_t colonPos = content.find(':', keyPos); - if (colonPos == std::string::npos) { - return ""; - } - - size_t quoteStart = content.find('"', colonPos); - if (quoteStart == std::string::npos) { - return ""; - } - - size_t quoteEnd = content.find('"', quoteStart + 1); - if (quoteEnd == std::string::npos) { - return ""; - } - - return content.substr(quoteStart + 1, quoteEnd - quoteStart - 1); - }; - - outMetadata.name = extractString("name"); - return true; -} - -/** - * @brief 查找include文件路径 - * @param includeName include文件名 - * @param baseDir 基础目录 - * @return 找到的完整路径,未找到返回空字符串 - */ -std::string ShaderLoader::findIncludeFile(const std::string &includeName, - const std::string &baseDir) { - fs::path basePath(baseDir); - fs::path includePath = basePath / includeName; - - if (fs::exists(includePath)) { - return includePath.string(); - } - - for (const auto &searchPath : includePaths_) { - includePath = fs::path(searchPath) / includeName; - if (fs::exists(includePath)) { - return includePath.string(); - } - } - - return ""; -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/shader/shader_manager.cpp b/Extra2D/src/graphics/shader/shader_manager.cpp deleted file mode 100644 index 01fcf55..0000000 --- a/Extra2D/src/graphics/shader/shader_manager.cpp +++ /dev/null @@ -1,855 +0,0 @@ -#include -#include -#include - -#include -#include - -namespace nl = nlohmann; -namespace fs = std::filesystem; - -namespace extra2d { - -/** - * @brief 获取单例实例 - * @return Shader管理器实例引用 - */ -ShaderManager &ShaderManager::getInstance() { - static ShaderManager instance; - return instance; -} - -/** - * @brief 使用平台默认路径初始化Shader系统 - * 自动检测平台并使用正确的路径(romfs/sdmc/相对路径) - * @param factory 渲染后端Shader工厂 - * @param appName 应用名称(用于缓存目录) - * @return 初始化成功返回true,失败返回false - */ -bool ShaderManager::init(Ptr factory, - const std::string &appName) { - // 使用相对路径作为Shader目录 - fs::path shaderDir = "shaders"; - fs::path cacheDir = "cache/shaders"; - - // 非Switch平台支持热重载 -#ifndef __SWITCH__ - hotReloadSupported_ = true; -#else - hotReloadSupported_ = false; -#endif - - E2D_LOG_INFO("ShaderManager 初始化 (热重载: {})", - hotReloadSupported_ ? "supported" : "not supported"); - - return init(shaderDir.string(), cacheDir.string(), factory); -} - -/** - * @brief 初始化Shader系统 - * @param shaderDir Shader文件目录 - * @param cacheDir 缓存目录 - * @param factory 渲染后端Shader工厂 - * @return 初始化成功返回true,失败返回false - */ -bool ShaderManager::init(const std::string &shaderDir, - const std::string &cacheDir, - Ptr factory) { - if (initialized_) { - E2D_LOG_WARN("ShaderManager 已初始化"); - return true; - } - - if (!factory) { - E2D_LOG_ERROR("Shader 工厂为空"); - return false; - } - - shaderDir_ = shaderDir; - cacheDir_ = cacheDir; - factory_ = factory; - - // 非Switch平台支持热重载 -#ifndef __SWITCH__ - hotReloadSupported_ = true; -#else - hotReloadSupported_ = false; -#endif - -#ifdef __SWITCH__ - if (!ShaderCache::getInstance().init(cacheDir_)) { - E2D_LOG_WARN("Switch 平台初始化着色器缓存失败"); - } -#else - if (!ShaderCache::getInstance().init(cacheDir_)) { - E2D_LOG_WARN("初始化着色器缓存失败,已禁用缓存"); - } -#endif - - if (hotReloadSupported_) { - if (!ShaderHotReloader::getInstance().init()) { - E2D_LOG_WARN("初始化热重载器失败"); - } - } - - loader_.addIncludePath(shaderDir_ + "common"); - - initialized_ = true; - E2D_LOG_INFO("ShaderManager 初始化成功"); - E2D_LOG_INFO(" 着色器目录: {}", shaderDir_); - E2D_LOG_INFO(" 缓存目录: {}", cacheDir_); - E2D_LOG_INFO(" 热重载: {}", hotReloadSupported_ ? "支持" : "不支持"); - - return true; -} - -/** - * @brief 关闭Shader系统 - */ -void ShaderManager::shutdown() { - if (!initialized_) { - return; - } - - if (hotReloadSupported_) { - ShaderHotReloader::getInstance().shutdown(); - } - ShaderCache::getInstance().shutdown(); - - shaders_.clear(); - factory_.reset(); - initialized_ = false; - - E2D_LOG_INFO("ShaderManager 已关闭"); -} - -/** - * @brief 从分离文件加载Shader - * @param name Shader名称 - * @param vertPath 顶点着色器文件路径 - * @param fragPath 片段着色器文件路径 - * @return 加载的Shader实例 - */ -Ptr ShaderManager::loadFromFiles(const std::string &name, - const std::string &vertPath, - const std::string &fragPath) { - if (!initialized_) { - E2D_LOG_ERROR("ShaderManager 未初始化"); - return nullptr; - } - - auto it = shaders_.find(name); - if (it != shaders_.end()) { - return it->second.shader; - } - - ShaderLoadResult result = - loader_.loadFromSeparateFiles(name, vertPath, fragPath); - if (!result.success) { - E2D_LOG_ERROR("加载着色器文件失败: {} - {}", vertPath, fragPath); - return nullptr; - } - - std::string sourceHash = - ShaderCache::computeHash(result.vertSource, result.fragSource); - Ptr shader = - loadFromCache(name, sourceHash, result.vertSource, result.fragSource); - - if (!shader) { - E2D_LOG_DEBUG("未找到有效缓存,从源码编译着色器: {}", name); - shader = - factory_->createFromSource(name, result.vertSource, result.fragSource); - if (!shader) { - E2D_LOG_ERROR("从源码创建着色器失败: {}", name); - return nullptr; - } - - std::vector binary; - if (factory_->getShaderBinary(*shader, binary)) { - E2D_LOG_DEBUG("获取到着色器二进制数据,大小: {} 字节", binary.size()); - ShaderCacheEntry entry; - entry.name = name; - entry.sourceHash = sourceHash; - entry.binary = binary; - entry.dependencies = result.dependencies; - ShaderCache::getInstance().saveCache(entry); - } else { - E2D_LOG_WARN("获取着色器二进制数据失败: {}", name); - } - } - - ShaderInfo info; - info.shader = shader; - info.vertSource = result.vertSource; - info.fragSource = result.fragSource; - info.filePaths = {vertPath, fragPath}; - info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(), - result.dependencies.end()); - - info.metadata.name = name; - info.metadata.vertPath = vertPath; - info.metadata.fragPath = fragPath; - - shaders_[name] = std::move(info); - - if (hotReloadEnabled_ && hotReloadSupported_) { - auto callback = [this, name](const FileChangeEvent &event) { - this->handleFileChange(name, event); - }; - ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths, - callback); - } - - E2D_LOG_DEBUG("着色器已加载: {}", name); - return shader; -} - -/** - * @brief 从组合文件加载Shader - * @param path 组合Shader文件路径 - * @return 加载的Shader实例 - */ -Ptr ShaderManager::loadFromCombinedFile(const std::string &path) { - if (!initialized_) { - E2D_LOG_ERROR("ShaderManager 未初始化"); - return nullptr; - } - - ShaderMetadata metadata = loader_.getMetadata(path); - std::string name = metadata.name.empty() ? path : metadata.name; - - auto it = shaders_.find(name); - if (it != shaders_.end()) { - return it->second.shader; - } - - ShaderLoadResult result = loader_.loadFromCombinedFile(path); - if (!result.success) { - E2D_LOG_ERROR("加载组合着色器文件失败: {}", path); - return nullptr; - } - - std::string sourceHash = - ShaderCache::computeHash(result.vertSource, result.fragSource); - Ptr shader = - loadFromCache(name, sourceHash, result.vertSource, result.fragSource); - - if (!shader) { - E2D_LOG_DEBUG("No valid cache found, compiling shader from source: {}", - name); - shader = - factory_->createFromSource(name, result.vertSource, result.fragSource); - if (!shader) { - E2D_LOG_ERROR("从源码创建着色器失败: {}", name); - return nullptr; - } - - std::vector binary; - if (factory_->getShaderBinary(*shader, binary)) { - E2D_LOG_DEBUG("获取到着色器二进制数据,大小: {} 字节", binary.size()); - ShaderCacheEntry entry; - entry.name = name; - entry.sourceHash = sourceHash; - entry.binary = binary; - entry.dependencies = result.dependencies; - ShaderCache::getInstance().saveCache(entry); - } else { - E2D_LOG_WARN("获取着色器二进制数据失败: {}", name); - } - } - - ShaderInfo info; - info.shader = shader; - info.vertSource = result.vertSource; - info.fragSource = result.fragSource; - info.filePaths = {path}; - info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(), - result.dependencies.end()); - info.metadata = metadata; - - shaders_[name] = std::move(info); - - if (hotReloadEnabled_ && hotReloadSupported_) { - auto callback = [this, name](const FileChangeEvent &event) { - this->handleFileChange(name, event); - }; - ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths, - callback); - } - - E2D_LOG_DEBUG("从组合文件加载着色器成功: {}", name); - return shader; -} - -/** - * @brief 从源码加载Shader - * @param name Shader名称 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 加载的Shader实例 - */ -Ptr ShaderManager::loadFromSource(const std::string &name, - const std::string &vertSource, - const std::string &fragSource) { - if (!initialized_) { - E2D_LOG_ERROR("ShaderManager 未初始化"); - return nullptr; - } - - auto it = shaders_.find(name); - if (it != shaders_.end()) { - return it->second.shader; - } - - Ptr shader = - factory_->createFromSource(name, vertSource, fragSource); - if (!shader) { - E2D_LOG_ERROR("从源码创建着色器失败: {}", name); - return nullptr; - } - - ShaderInfo info; - info.shader = shader; - info.vertSource = vertSource; - info.fragSource = fragSource; - info.metadata.name = name; - - shaders_[name] = std::move(info); - - E2D_LOG_DEBUG("从源码加载着色器成功: {}", name); - return shader; -} - -/** - * @brief 获取已加载的Shader - * @param name Shader名称 - * @return Shader实例,不存在返回nullptr - */ -Ptr ShaderManager::get(const std::string &name) const { - auto it = shaders_.find(name); - if (it != shaders_.end()) { - return it->second.shader; - } - return nullptr; -} - -/** - * @brief 检查Shader是否存在 - * @param name Shader名称 - * @return 存在返回true,否则返回false - */ -bool ShaderManager::has(const std::string &name) const { - return shaders_.find(name) != shaders_.end(); -} - -/** - * @brief 移除Shader - * @param name Shader名称 - */ -void ShaderManager::remove(const std::string &name) { - auto it = shaders_.find(name); - if (it != shaders_.end()) { - ShaderHotReloader::getInstance().unwatch(name); - shaders_.erase(it); - E2D_LOG_DEBUG("着色器已移除: {}", name); - } -} - -/** - * @brief 清除所有Shader - */ -void ShaderManager::clear() { - if (hotReloadSupported_) { - for (const auto &pair : shaders_) { - ShaderHotReloader::getInstance().unwatch(pair.first); - } - } - shaders_.clear(); - E2D_LOG_DEBUG("所有着色器已清除"); -} - -/** - * @brief 注册重载回调 - * @param name Shader名称 - * @param callback 重载回调函数 - */ -void ShaderManager::setReloadCallback(const std::string &name, - ShaderReloadCallback callback) { - auto it = shaders_.find(name); - if (it != shaders_.end()) { - it->second.reloadCallback = callback; - } -} - -/** - * @brief 启用/禁用热重载 - * @param enabled 是否启用 - */ -void ShaderManager::setHotReloadEnabled(bool enabled) { - if (!hotReloadSupported_) { - E2D_LOG_WARN("当前平台不支持热重载"); - return; - } - hotReloadEnabled_ = enabled; - ShaderHotReloader::getInstance().setEnabled(enabled); - E2D_LOG_INFO("热重载已{}", enabled ? "Enable" : "Disable"); -} - -/** - * @brief 检查热重载是否启用 - * @return 启用返回true,否则返回false - */ -bool ShaderManager::isHotReloadEnabled() const { - return hotReloadEnabled_ && hotReloadSupported_; -} - -/** - * @brief 更新热重载系统(主循环调用) - */ -void ShaderManager::update() { - if (hotReloadEnabled_ && hotReloadSupported_) { - ShaderHotReloader::getInstance().update(); - } -} - -/** - * @brief 手动重载Shader - * @param name Shader名称 - * @return 重载成功返回true,失败返回false - */ -bool ShaderManager::reload(const std::string &name) { - auto it = shaders_.find(name); - if (it == shaders_.end()) { - E2D_LOG_WARN("未找到要重载的着色器: {}", name); - return false; - } - - ShaderInfo &info = it->second; - - std::string vertSource = info.vertSource; - std::string fragSource = info.fragSource; - - if (!info.metadata.vertPath.empty() && !info.metadata.fragPath.empty()) { - ShaderLoadResult result = loader_.loadFromSeparateFiles( - name, info.metadata.vertPath, info.metadata.fragPath); - if (result.success) { - vertSource = result.vertSource; - fragSource = result.fragSource; - } - } else if (!info.metadata.combinedPath.empty()) { - ShaderLoadResult result = - loader_.loadFromCombinedFile(info.metadata.combinedPath); - if (result.success) { - vertSource = result.vertSource; - fragSource = result.fragSource; - } - } - - Ptr newShader = - factory_->createFromSource(name, vertSource, fragSource); - if (!newShader) { - E2D_LOG_ERROR("重载着色器失败: {}", name); - return false; - } - - info.shader = newShader; - info.vertSource = vertSource; - info.fragSource = fragSource; - - if (info.reloadCallback) { - info.reloadCallback(newShader); - } - - E2D_LOG_INFO("着色器重载成功: {}", name); - return true; -} - -/** - * @brief 获取内置Shader - * @param name 内置Shader名称 - * @return Shader实例 - */ -Ptr ShaderManager::getBuiltin(const std::string &name) { - Ptr shader = get(name); - if (shader) { - return shader; - } - - // 从JSON元数据文件加载内置着色器 - fs::path jsonPath = - fs::path(shaderDir_) / "shared" / "builtin" / (name + ".json"); - if (loader_.fileExists(jsonPath.string())) { - return loadFromMetadata(jsonPath.string(), name); - } - - E2D_LOG_ERROR("未找到内置着色器: {}", jsonPath.string()); - return nullptr; -} - -/** - * @brief 从JSON元数据文件加载Shader(多后端支持) - * @param jsonPath JSON元数据文件路径 - * @param name Shader名称 - * @return 加载的Shader实例 - */ -Ptr ShaderManager::loadFromMetadata(const std::string &jsonPath, - const std::string &name) { - if (!initialized_) { - E2D_LOG_ERROR("ShaderManager 未初始化"); - return nullptr; - } - - // 检查是否已加载 - auto it = shaders_.find(name); - if (it != shaders_.end()) { - return it->second.shader; - } - - // 读取JSON文件 - std::string jsonContent = loader_.readFile(jsonPath); - if (jsonContent.empty()) { - E2D_LOG_ERROR("读取着色器元数据失败: {}", jsonPath); - return nullptr; - } - - try { - // 使用nlohmann/json解析 - nl::json j = nl::json::parse(jsonContent); - - // 解析基本元数据 - ShaderMetadata metadata; - metadata.name = name; - - if (j.contains("category")) { - metadata.category = j["category"].get(); - } - if (j.contains("version")) { - metadata.version = j["version"].get(); - } - if (j.contains("description")) { - metadata.description = j["description"].get(); - } - - // 解析 uniforms 详细定义 - if (j.contains("uniforms")) { - for (auto &[key, value] : j["uniforms"].items()) { - ShaderUniformDef def; - if (value.contains("type")) { - def.type = value["type"].get(); - } - if (value.contains("description")) { - def.description = value["description"].get(); - } - // 解析默认值 - if (value.contains("default")) { - def.hasDefault = true; - if (def.type == "float") { - def.defaultValue = value["default"].get(); - } else if (def.type == "int") { - def.defaultInt = value["default"].get(); - } else if (def.type == "bool") { - def.defaultBool = value["default"].get(); - } else if (def.type == "vec2" && value["default"].is_array()) { - auto arr = value["default"].get>(); - for (size_t i = 0; i < arr.size() && i < 2; ++i) { - def.defaultVec2[i] = arr[i]; - } - } else if (def.type == "vec3" && value["default"].is_array()) { - auto arr = value["default"].get>(); - for (size_t i = 0; i < arr.size() && i < 3; ++i) { - def.defaultVec3[i] = arr[i]; - } - } else if (def.type == "vec4" && value["default"].is_array()) { - auto arr = value["default"].get>(); - for (size_t i = 0; i < arr.size() && i < 4; ++i) { - def.defaultVec4[i] = arr[i]; - } - } else if (def.type == "mat4" && value["default"].is_array()) { - auto arr = value["default"].get>(); - for (size_t i = 0; i < arr.size() && i < 16; ++i) { - def.defaultMat4[i] = arr[i]; - } - } - } - metadata.uniformDefs[key] = def; - // 同时填充旧的uniforms映射以保持兼容性 - metadata.uniforms[key] = def.type; - } - } - - // 解析 samplers 定义 - if (j.contains("samplers")) { - for (auto &[key, value] : j["samplers"].items()) { - ShaderSamplerDef def; - if (value.contains("type")) { - def.type = value["type"].get(); - } - if (value.contains("description")) { - def.description = value["description"].get(); - } - metadata.samplerDefs[key] = def; - } - } - - // 获取OpenGL后端路径 - if (!j.contains("backends") || !j["backends"].contains("opengl")) { - E2D_LOG_ERROR("着色器元数据中未找到 OpenGL 后端: {}", jsonPath); - return nullptr; - } - - auto &opengl = j["backends"]["opengl"]; - if (!opengl.contains("vertex") || !opengl.contains("fragment")) { - E2D_LOG_ERROR("着色器元数据中缺少顶点或片段路径: {}", jsonPath); - return nullptr; - } - - std::string vertRelativePath = opengl["vertex"].get(); - std::string fragRelativePath = opengl["fragment"].get(); - - // 使用C++17文件系统构建完整路径 - fs::path vertPath = fs::path(shaderDir_) / vertRelativePath; - fs::path fragPath = fs::path(shaderDir_) / fragRelativePath; - - E2D_LOG_DEBUG("从元数据加载着色器: {} -> 顶点: {}, 片段: {}", name, - vertPath.string(), fragPath.string()); - - // 使用分离文件加载 - Ptr shader = - loadFromFiles(name, vertPath.string(), fragPath.string()); - - // 更新着色器元数据 - if (shader && shaders_.find(name) != shaders_.end()) { - shaders_[name].metadata = metadata; - } - - return shader; - - } catch (const nl::json::exception &e) { - E2D_LOG_ERROR("解析着色器元数据 {} 失败: {}", jsonPath, e.what()); - return nullptr; - } -} - -/** - * @brief 加载所有内置Shader - * @return 加载成功返回true,失败返回false - */ -bool ShaderManager::loadBuiltinShaders() { - if (!initialized_) { - E2D_LOG_ERROR("ShaderManager 未初始化"); - return false; - } - - bool allSuccess = true; - - const char *builtinNames[] = {"sprite", "particle", "shape", "postprocess", - "font"}; - - for (const char *name : builtinNames) { - fs::path jsonPath = fs::path(shaderDir_) / "shared" / "builtin" / - (std::string(name) + ".json"); - std::string shaderName = std::string("builtin_") + name; - - Ptr shader = nullptr; - if (loader_.fileExists(jsonPath.string())) { - shader = loadFromMetadata(jsonPath.string(), name); - } - - if (!shader) { - E2D_LOG_ERROR("加载内置 {} 着色器失败", name); - allSuccess = false; - } else { - // 同时注册带 builtin_ 前缀的名称 - auto it = shaders_.find(name); - if (it != shaders_.end()) { - shaders_[shaderName] = it->second; - } - } - } - - if (allSuccess) { - E2D_LOG_INFO("所有内置着色器加载成功"); - } - - return allSuccess; -} - -/** - * @brief 从缓存加载Shader - * @param name Shader名称 - * @param sourceHash 源码哈希值 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return Shader实例 - */ -Ptr ShaderManager::loadFromCache(const std::string &name, - const std::string &sourceHash, - const std::string &vertSource, - const std::string &fragSource) { - if (!ShaderCache::getInstance().isInitialized()) { - return nullptr; - } - - if (!ShaderCache::getInstance().hasValidCache(name, sourceHash)) { - return nullptr; - } - - Ptr entry = ShaderCache::getInstance().loadCache(name); - if (!entry || entry->binary.empty()) { - return nullptr; - } - - Ptr shader = factory_->createFromBinary(name, entry->binary); - if (shader) { - E2D_LOG_DEBUG("从缓存加载着色器成功: {}", name); - } - - return shader; -} - -/** - * @brief 处理文件变化事件 - * @param shaderName Shader名称 - * @param event 文件变化事件 - */ -void ShaderManager::handleFileChange(const std::string &shaderName, - const FileChangeEvent &event) { - E2D_LOG_DEBUG("着色器文件已更改: {} -> {}", shaderName, event.filepath); - reload(shaderName); -} - -/** - * @brief 获取Shader元数据 - * @param name Shader名称 - * @return Shader元数据,不存在返回空元数据 - */ -ShaderMetadata ShaderManager::getMetadata(const std::string &name) const { - auto it = shaders_.find(name); - if (it != shaders_.end()) { - return it->second.metadata; - } - return ShaderMetadata{}; -} - -/** - * @brief 获取Shader的uniform定义 - * @param name Shader名称 - * @return uniform定义映射,不存在返回空映射 - */ -std::unordered_map -ShaderManager::getUniformDefs(const std::string &name) const { - auto it = shaders_.find(name); - if (it != shaders_.end()) { - return it->second.metadata.uniformDefs; - } - return {}; -} - -/** - * @brief 获取Shader的sampler定义 - * @param name Shader名称 - * @return sampler定义映射,不存在返回空映射 - */ -std::unordered_map -ShaderManager::getSamplerDefs(const std::string &name) const { - auto it = shaders_.find(name); - if (it != shaders_.end()) { - return it->second.metadata.samplerDefs; - } - return {}; -} - -/** - * @brief 自动应用uniform值到着色器 - * 根据JSON元数据中的uniform定义,自动设置对应的uniform值 - * 如果值映射表中不存在该uniform,则使用JSON中定义的默认值 - * @param shader 目标着色器 - * @param shaderName Shader名称(用于查找元数据) - * @param values uniform值映射表(可选,用于覆盖默认值) - */ -void ShaderManager::applyUniforms(Ptr shader, - const std::string &shaderName, - const UniformValueMap &values) { - if (!shader) - return; - - auto uniformDefs = getUniformDefs(shaderName); - for (const auto &[name, def] : uniformDefs) { - auto it = values.find(name); - - if (it != values.end()) { - // 使用提供的值 - const auto &value = it->second; - if (def.type == "float" && - value.type == ShaderUniformValue::Type::Float) { - shader->setFloat(name, value.data.f[0]); - } else if (def.type == "int" && - value.type == ShaderUniformValue::Type::Int) { - shader->setInt(name, value.data.i); - } else if (def.type == "bool" && - value.type == ShaderUniformValue::Type::Bool) { - shader->setBool(name, value.data.b); - } else if (def.type == "vec2" && - value.type == ShaderUniformValue::Type::Vec2) { - shader->setVec2(name, glm::vec2(value.data.f[0], value.data.f[1])); - } else if (def.type == "vec3" && - value.type == ShaderUniformValue::Type::Vec3) { - shader->setVec3( - name, glm::vec3(value.data.f[0], value.data.f[1], value.data.f[2])); - } else if (def.type == "vec4" && - value.type == ShaderUniformValue::Type::Vec4) { - shader->setVec4(name, glm::vec4(value.data.f[0], value.data.f[1], - value.data.f[2], value.data.f[3])); - } else if (def.type == "mat4" && - value.type == ShaderUniformValue::Type::Mat4) { - glm::mat4 mat; - for (int i = 0; i < 4; ++i) - for (int j = 0; j < 4; ++j) - mat[i][j] = value.data.f[i * 4 + j]; - shader->setMat4(name, mat); - } - } else if (def.hasDefault) { - // 使用JSON中定义的默认值 - if (def.type == "float") { - shader->setFloat(name, def.defaultValue); - } else if (def.type == "int") { - shader->setInt(name, def.defaultInt); - } else if (def.type == "bool") { - shader->setBool(name, def.defaultBool); - } else if (def.type == "vec2") { - shader->setVec2(name, - glm::vec2(def.defaultVec2[0], def.defaultVec2[1])); - } else if (def.type == "vec3") { - shader->setVec3(name, glm::vec3(def.defaultVec3[0], def.defaultVec3[1], - def.defaultVec3[2])); - } else if (def.type == "vec4") { - shader->setVec4(name, - glm::vec4(def.defaultVec4[0], def.defaultVec4[1], - def.defaultVec4[2], def.defaultVec4[3])); - } else if (def.type == "mat4") { - glm::mat4 mat; - for (int i = 0; i < 4; ++i) - for (int j = 0; j < 4; ++j) - mat[i][j] = def.defaultMat4[i * 4 + j]; - shader->setMat4(name, mat); - } - } - } -} - -/** - * @brief 自动应用sampler绑定到着色器 - * 根据JSON元数据中的sampler定义,自动设置对应的纹理单元 - * @param shader 目标着色器 - * @param shaderName Shader名称(用于查找元数据) - */ -void ShaderManager::applySamplers(Ptr shader, - const std::string &shaderName) { - if (!shader) - return; - - // TODO: 从JSON中解析binding并设置 - // 目前sampler绑定在submitBatch中通过setInt设置 -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/shader/shader_preset.cpp b/Extra2D/src/graphics/shader/shader_preset.cpp deleted file mode 100644 index 9f44d71..0000000 --- a/Extra2D/src/graphics/shader/shader_preset.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include -#include -#include -#include - - -namespace extra2d { - -/** - * @brief 创建水波纹效果着色器 - * @param params 水波纹效果参数 - * @return 配置好的着色器 - */ -Ptr ShaderPreset::Water(const WaterParams ¶ms) { - Ptr shader = ShaderManager::getInstance().get("water"); - if (!shader) { - E2D_LOG_ERROR("获取水波纹着色器失败"); - return nullptr; - } - - shader->setFloat("u_waveSpeed", params.waveSpeed); - shader->setFloat("u_waveAmplitude", params.waveAmplitude); - shader->setFloat("u_waveFrequency", params.waveFrequency); - - return shader; -} - -/** - * @brief 创建描边效果着色器 - * @param params 描边效果参数 - * @return 配置好的着色器 - */ -Ptr ShaderPreset::Outline(const OutlineParams ¶ms) { - Ptr shader = ShaderManager::getInstance().get("outline"); - if (!shader) { - E2D_LOG_ERROR("获取描边着色器失败"); - return nullptr; - } - - shader->setVec4("u_outlineColor", glm::vec4(params.color.r, params.color.g, - params.color.b, params.color.a)); - shader->setFloat("u_thickness", params.thickness); - - return shader; -} - -/** - * @brief 创建扭曲效果着色器 - * @param params 扭曲效果参数 - * @return 配置好的着色器 - */ -Ptr ShaderPreset::Distortion(const DistortionParams ¶ms) { - Ptr shader = ShaderManager::getInstance().get("distortion"); - if (!shader) { - E2D_LOG_ERROR("获取扭曲效果着色器失败"); - return nullptr; - } - - shader->setFloat("u_distortionAmount", params.distortionAmount); - shader->setFloat("u_timeScale", params.timeScale); - - return shader; -} - -/** - * @brief 创建像素化效果着色器 - * @param params 像素化效果参数 - * @return 配置好的着色器 - */ -Ptr ShaderPreset::Pixelate(const PixelateParams ¶ms) { - Ptr shader = ShaderManager::getInstance().get("pixelate"); - if (!shader) { - E2D_LOG_ERROR("获取像素化着色器失败"); - return nullptr; - } - - shader->setFloat("u_pixelSize", params.pixelSize); - - return shader; -} - -/** - * @brief 创建反相效果着色器 - * @param params 反相效果参数 - * @return 配置好的着色器 - */ -Ptr ShaderPreset::Invert(const InvertParams ¶ms) { - Ptr shader = ShaderManager::getInstance().get("invert"); - if (!shader) { - E2D_LOG_ERROR("获取反相着色器失败"); - return nullptr; - } - - shader->setFloat("u_strength", params.strength); - - return shader; -} - -/** - * @brief 创建灰度效果着色器 - * @param params 灰度效果参数 - * @return 配置好的着色器 - */ -Ptr ShaderPreset::Grayscale(const GrayscaleParams ¶ms) { - Ptr shader = ShaderManager::getInstance().get("grayscale"); - if (!shader) { - E2D_LOG_ERROR("获取灰度着色器失败"); - return nullptr; - } - - shader->setFloat("u_intensity", params.intensity); - - return shader; -} - -/** - * @brief 创建模糊效果着色器 - * @param params 模糊效果参数 - * @return 配置好的着色器 - */ -Ptr ShaderPreset::Blur(const BlurParams ¶ms) { - Ptr shader = ShaderManager::getInstance().get("blur"); - if (!shader) { - E2D_LOG_ERROR("获取模糊着色器失败"); - return nullptr; - } - - shader->setFloat("u_radius", params.radius); - - return shader; -} - -/** - * @brief 创建灰度+描边组合效果着色器 - * @param grayParams 灰度效果参数 - * @param outlineParams 描边效果参数 - * @return 配置好的着色器 - */ -Ptr -ShaderPreset::GrayscaleOutline(const GrayscaleParams &grayParams, - const OutlineParams &outlineParams) { - std::string shaderDir = ShaderManager::getInstance().getShaderDir(); - std::string jsonPath = shaderDir + "effects/grayscale_outline.json"; - - Ptr shader = - ShaderManager::getInstance().loadFromMetadata(jsonPath, "grayscale_outline"); - if (!shader) { - E2D_LOG_ERROR("从 {} 加载灰度描边组合着色器失败", - jsonPath); - return nullptr; - } - - shader->setFloat("u_grayIntensity", grayParams.intensity); - shader->setVec4("u_outlineColor", - glm::vec4(outlineParams.color.r, outlineParams.color.g, - outlineParams.color.b, outlineParams.color.a)); - shader->setFloat("u_thickness", outlineParams.thickness); - - return shader; -} - -/** - * @brief 创建像素化+反相组合效果着色器 - * @param pixParams 像素化效果参数 - * @param invParams 反相效果参数 - * @return 配置好的着色器 - */ -Ptr ShaderPreset::PixelateInvert(const PixelateParams &pixParams, - const InvertParams &invParams) { - std::string shaderDir = ShaderManager::getInstance().getShaderDir(); - std::string jsonPath = shaderDir + "effects/pixelate_invert.json"; - - Ptr shader = - ShaderManager::getInstance().loadFromMetadata(jsonPath, "pixelate_invert"); - if (!shader) { - E2D_LOG_ERROR("从 {} 加载像素化反相组合着色器失败", jsonPath); - return nullptr; - } - - shader->setFloat("u_pixelSize", pixParams.pixelSize); - shader->setFloat("u_invertStrength", invParams.strength); - - return shader; -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/texture/alpha_mask.cpp b/Extra2D/src/graphics/texture/alpha_mask.cpp deleted file mode 100644 index bedb0c8..0000000 --- a/Extra2D/src/graphics/texture/alpha_mask.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include - -namespace extra2d { - -/** - * @brief 构造函数 - * - * 创建指定尺寸的Alpha遮罩,初始时所有像素均为不透明(值为255) - * - * @param width 遮罩宽度(像素) - * @param height 遮罩高度(像素) - */ -AlphaMask::AlphaMask(int width, int height) - : width_(width), height_(height), data_(width * height, 255) {} - -/** - * @brief 从像素数据创建Alpha遮罩 - * - * 根据输入的像素数据提取Alpha通道创建遮罩, - * 支持RGBA(4通道)、RGB(3通道)和灰度(1通道)格式 - * - * @param pixels 像素数据指针 - * @param width 图像宽度 - * @param height 图像高度 - * @param channels 通道数量(1、3或4) - * @return 创建的Alpha遮罩对象 - */ -AlphaMask AlphaMask::createFromPixels(const uint8_t *pixels, int width, - int height, int channels) { - AlphaMask mask(width, height); - - if (!pixels || width <= 0 || height <= 0) { - return mask; - } - - // 根据通道数提取Alpha值 - for (int y = 0; y < height; ++y) { - for (int x = 0; x < width; ++x) { - int pixelIndex = (y * width + x) * channels; - uint8_t alpha = 255; - - if (channels == 4) { - // RGBA格式,Alpha在第四个通道 - alpha = pixels[pixelIndex + 3]; - } else if (channels == 1) { - // 灰度图,直接作为Alpha - alpha = pixels[pixelIndex]; - } else if (channels == 3) { - // RGB格式,没有Alpha通道,视为不透明 - alpha = 255; - } - - mask.data_[y * width + x] = alpha; - } - } - - return mask; -} - -/** - * @brief 获取指定位置的Alpha值 - * - * 返回指定坐标处的Alpha值,如果坐标无效则返回0 - * - * @param x X坐标 - * @param y Y坐标 - * @return Alpha值(0-255),坐标无效时返回0 - */ -uint8_t AlphaMask::getAlpha(int x, int y) const { - if (!isValid(x, y)) { - return 0; - } - return data_[y * width_ + x]; -} - -/** - * @brief 检查指定位置是否不透明 - * - * 判断指定坐标处的Alpha值是否大于等于给定的阈值 - * - * @param x X坐标 - * @param y Y坐标 - * @param threshold 不透明度阈值(默认为255,即完全不透明) - * @return 如果Alpha值大于等于阈值返回true,否则返回false - */ -bool AlphaMask::isOpaque(int x, int y, uint8_t threshold) const { - return getAlpha(x, y) >= threshold; -} - -/** - * @brief 检查坐标是否有效 - * - * 判断给定的坐标是否在遮罩的有效范围内 - * - * @param x X坐标 - * @param y Y坐标 - * @return 如果坐标在有效范围内返回true,否则返回false - */ -bool AlphaMask::isValid(int x, int y) const { - return x >= 0 && x < width_ && y >= 0 && y < height_; -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/texture/texture_atlas.cpp b/Extra2D/src/graphics/texture/texture_atlas.cpp deleted file mode 100644 index 677cba4..0000000 --- a/Extra2D/src/graphics/texture/texture_atlas.cpp +++ /dev/null @@ -1,386 +0,0 @@ -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// TextureAtlasPage 实现 -// ============================================================================ - -/** - * @brief 构造函数 - * @param width 页面宽度 - * @param height 页面高度 - * - * 创建指定尺寸的纹理图集页面,初始化空白纹理和打包根节点 - */ -TextureAtlasPage::TextureAtlasPage(int width, int height) - : width_(width), height_(height), isFull_(false), usedArea_(0) { - // 创建空白纹理 - std::vector emptyData(width * height * 4, 0); - texture_ = makePtr(width, height, emptyData.data(), 4); - - // 初始化矩形打包根节点 - root_ = std::make_unique(0, 0, width, height); - - E2D_LOG_INFO("已创建纹理图集页面: {}x{}", width, height); -} - -/** - * @brief 析构函数 - * - * 释放纹理图集页面资源 - */ -TextureAtlasPage::~TextureAtlasPage() = default; - -/** - * @brief 尝试添加纹理到图集页面 - * @param name 纹理名称 - * @param texWidth 纹理宽度 - * @param texHeight 纹理高度 - * @param pixels 像素数据 - * @param[out] outUvRect 输出的UV坐标矩形 - * @return 添加成功返回true,失败返回false - * - * 尝试将纹理添加到图集页面中,使用矩形打包算法找到合适位置 - */ -bool TextureAtlasPage::tryAddTexture(const std::string &name, int texWidth, - int texHeight, const uint8_t *pixels, - Rect &outUvRect) { - if (isFull_) { - return false; - } - - // 添加边距 - int paddedWidth = texWidth + 2 * PADDING; - int paddedHeight = texHeight + 2 * PADDING; - - // 如果纹理太大,无法放入 - if (paddedWidth > width_ || paddedHeight > height_) { - return false; - } - - // 尝试插入 - PackNode *node = insert(root_.get(), paddedWidth, paddedHeight); - if (node == nullptr) { - // 无法放入,标记为满 - isFull_ = true; - return false; - } - - // 写入像素数据(跳过边距区域) - writePixels(node->x + PADDING, node->y + PADDING, texWidth, texHeight, - pixels); - - // 创建条目 - AtlasEntry entry; - entry.name = name; - entry.originalSize = - Vec2(static_cast(texWidth), static_cast(texHeight)); - entry.padding = PADDING; - - // 计算 UV 坐标(考虑边距) - float u1 = static_cast(node->x + PADDING) / width_; - float v1 = static_cast(node->y + PADDING) / height_; - float u2 = static_cast(node->x + PADDING + texWidth) / width_; - float v2 = static_cast(node->y + PADDING + texHeight) / height_; - - entry.uvRect = Rect(u1, v1, u2 - u1, v2 - v1); - outUvRect = entry.uvRect; - - entries_[name] = std::move(entry); - usedArea_ += paddedWidth * paddedHeight; - - E2D_LOG_DEBUG("已将纹理 '{}' 添加到图集: {}x{} 位置 ({}, {})", name, - texWidth, texHeight, node->x, node->y); - - return true; -} - -/** - * @brief 插入纹理到打包树 - * @param node 当前节点 - * @param width 纹理宽度 - * @param height 纹理高度 - * @return 找到合适的节点返回节点指针,否则返回nullptr - * - * 使用二叉树算法递归查找合适的空间位置 - */ -TextureAtlasPage::PackNode *TextureAtlasPage::insert(PackNode *node, int width, - int height) { - if (node == nullptr) { - return nullptr; - } - - // 如果节点已被使用,尝试子节点 - if (node->used) { - PackNode *result = insert(node->left.get(), width, height); - if (result != nullptr) { - return result; - } - return insert(node->right.get(), width, height); - } - - // 检查是否适合 - if (width > node->width || height > node->height) { - return nullptr; - } - - // 如果刚好合适,使用此节点 - if (width == node->width && height == node->height) { - node->used = true; - return node; - } - - // 需要分割节点 - int dw = node->width - width; - int dh = node->height - height; - - if (dw > dh) { - // 水平分割 - node->left = - std::make_unique(node->x, node->y, width, node->height); - node->right = - std::make_unique(node->x + width, node->y, dw, node->height); - } else { - // 垂直分割 - node->left = - std::make_unique(node->x, node->y, node->width, height); - node->right = - std::make_unique(node->x, node->y + height, node->width, dh); - } - - // 递归插入到左子节点 - return insert(node->left.get(), width, height); -} - -/** - * @brief 写入像素数据到纹理 - * @param x 起始X坐标 - * @param y 起始Y坐标 - * @param w 宽度 - * @param h 高度 - * @param pixels 像素数据 - * - * 使用glTexSubImage2D更新纹理的指定区域 - */ -void TextureAtlasPage::writePixels(int x, int y, int w, int h, - const uint8_t *pixels) { - if (texture_ == nullptr || pixels == nullptr) { - return; - } - - // 使用 glTexSubImage2D 更新纹理数据 - GLuint texID = static_cast( - reinterpret_cast(texture_->getNativeHandle())); - - glBindTexture(GL_TEXTURE_2D, texID); - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, - pixels); - glBindTexture(GL_TEXTURE_2D, 0); -} - -/** - * @brief 获取图集中的纹理条目信息 - * @param name 纹理名称 - * @return 找到返回条目指针,未找到返回nullptr - */ -const AtlasEntry *TextureAtlasPage::getEntry(const std::string &name) const { - auto it = entries_.find(name); - if (it != entries_.end()) { - return &it->second; - } - return nullptr; -} - -/** - * @brief 获取页面使用率 - * @return 使用率(0.0到1.0之间) - * - * 计算已使用面积占总面积的比例 - */ -float TextureAtlasPage::getUsageRatio() const { - return static_cast(usedArea_) / (width_ * height_); -} - -// ============================================================================ -// TextureAtlas 实现 -// ============================================================================ - -/** - * @brief 默认构造函数 - * - * 创建一个使用默认页面大小的纹理图集 - */ -TextureAtlas::TextureAtlas() - : pageSize_(TextureAtlasPage::DEFAULT_SIZE), sizeThreshold_(256), - enabled_(true), initialized_(false) {} - -/** - * @brief 析构函数 - * - * 释放纹理图集资源 - */ -TextureAtlas::~TextureAtlas() = default; - -/** - * @brief 初始化纹理图集 - * @param pageSize 页面大小 - * - * 设置图集页面大小并标记为已初始化 - */ -void TextureAtlas::init(int pageSize) { - pageSize_ = pageSize; - initialized_ = true; - E2D_LOG_INFO("纹理图集已初始化,页面大小: {}", pageSize); -} - -/** - * @brief 添加纹理到图集 - * @param name 纹理名称 - * @param width 纹理宽度 - * @param height 纹理高度 - * @param pixels 像素数据 - * @return 添加成功返回true,失败返回false - * - * 尝试将纹理添加到现有页面,如空间不足则创建新页面 - */ -bool TextureAtlas::addTexture(const std::string &name, int width, int height, - const uint8_t *pixels) { - if (!enabled_ || !initialized_) { - return false; - } - - // 检查是否已存在 - if (contains(name)) { - return true; - } - - // 检查纹理大小 - if (width > sizeThreshold_ || height > sizeThreshold_) { - E2D_LOG_DEBUG("纹理 '{}' 太大无法加入图集 ({}x{} > {}),跳过", - name, width, height, sizeThreshold_); - return false; - } - - // 尝试添加到现有页面 - Rect uvRect; - for (auto &page : pages_) { - if (page->tryAddTexture(name, width, height, pixels, uvRect)) { - entryToPage_[name] = page.get(); - return true; - } - } - - // 创建新页面 - auto newPage = std::make_unique(pageSize_, pageSize_); - if (newPage->tryAddTexture(name, width, height, pixels, uvRect)) { - entryToPage_[name] = newPage.get(); - pages_.push_back(std::move(newPage)); - return true; - } - - E2D_LOG_WARN("添加纹理 '{}' 到图集失败", name); - return false; -} - -/** - * @brief 检查纹理是否已存在于图集中 - * @param name 纹理名称 - * @return 存在返回true,不存在返回false - */ -bool TextureAtlas::contains(const std::string &name) const { - return entryToPage_.find(name) != entryToPage_.end(); -} - -/** - * @brief 获取纹理所在的图集纹理 - * @param name 纹理名称 - * @return 找到返回纹理指针,未找到返回nullptr - */ -const Texture *TextureAtlas::getAtlasTexture(const std::string &name) const { - auto it = entryToPage_.find(name); - if (it != entryToPage_.end()) { - return it->second->getTexture().get(); - } - return nullptr; -} - -/** - * @brief 获取纹理在图集中的UV坐标矩形 - * @param name 纹理名称 - * @return UV坐标矩形,未找到返回默认值 - */ -Rect TextureAtlas::getUVRect(const std::string &name) const { - auto it = entryToPage_.find(name); - if (it != entryToPage_.end()) { - const AtlasEntry *entry = it->second->getEntry(name); - if (entry != nullptr) { - return entry->uvRect; - } - } - return Rect(0, 0, 1, 1); // 默认 UV -} - -/** - * @brief 获取纹理的原始尺寸 - * @param name 纹理名称 - * @return 原始尺寸,未找到返回零向量 - */ -Vec2 TextureAtlas::getOriginalSize(const std::string &name) const { - auto it = entryToPage_.find(name); - if (it != entryToPage_.end()) { - const AtlasEntry *entry = it->second->getEntry(name); - if (entry != nullptr) { - return entry->originalSize; - } - } - return Vec2(0, 0); -} - -/** - * @brief 获取总使用率 - * @return 所有页面的平均使用率 - * - * 计算所有页面的平均空间使用率 - */ -float TextureAtlas::getTotalUsageRatio() const { - if (pages_.empty()) { - return 0.0f; - } - - float total = 0.0f; - for (const auto &page : pages_) { - total += page->getUsageRatio(); - } - return total / pages_.size(); -} - -/** - * @brief 清空图集 - * - * 移除所有页面和条目映射 - */ -void TextureAtlas::clear() { - pages_.clear(); - entryToPage_.clear(); - E2D_LOG_INFO("纹理图集已清空"); -} - -// ============================================================================ -// TextureAtlasMgr 单例实现 -// ============================================================================ - -/** - * @brief 获取TextureAtlasMgr单例实例 - * @return TextureAtlasMgr单例的引用 - * - * 使用静态局部变量实现线程安全的单例模式 - */ -TextureAtlasMgr &TextureAtlasMgr::get() { - static TextureAtlasMgr instance; - return instance; -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/texture/texture_pool.cpp b/Extra2D/src/graphics/texture/texture_pool.cpp deleted file mode 100644 index d56a8df..0000000 --- a/Extra2D/src/graphics/texture/texture_pool.cpp +++ /dev/null @@ -1,652 +0,0 @@ -#include -#include -#include - -#include -#include - -namespace extra2d { - -// ============================================================================ -// TexturePool 实现 -// ============================================================================ - -/** - * @brief 默认构造函数 - * - * 创建一个未初始化的纹理池 - */ -TexturePool::TexturePool() - : scene_(nullptr) - , maxMemoryUsage_(0) - , currentMemoryUsage_(0) - , cacheHits_(0) - , cacheMisses_(0) - , evictionCount_(0) { -} - -/** - * @brief 构造函数 - * @param scene 场景指针 - * @param maxMemoryUsage 最大内存使用量(0 表示无限制) - * - * 创建一个指定场景和内存限制的纹理池 - */ -TexturePool::TexturePool(Scene* scene, size_t maxMemoryUsage) - : scene_(scene) - , maxMemoryUsage_(maxMemoryUsage) - , currentMemoryUsage_(0) - , cacheHits_(0) - , cacheMisses_(0) - , evictionCount_(0) { - E2D_LOG_INFO("TexturePool 已创建,最大内存: {} 字节", maxMemoryUsage); -} - -/** - * @brief 初始化纹理池 - * @param scene 场景指针 - * @param maxMemoryUsage 最大内存使用量(0 表示无限制) - * - * 设置纹理池的场景和内存限制 - */ -void TexturePool::init(Scene* scene, size_t maxMemoryUsage) { - scene_ = scene; - maxMemoryUsage_ = maxMemoryUsage; - E2D_LOG_INFO("TexturePool 已初始化,最大内存: {} 字节", maxMemoryUsage); -} - -/** - * @brief 析构函数 - * - * 清理纹理池并释放所有资源 - */ -TexturePool::~TexturePool() { - clear(); - E2D_LOG_INFO("TexturePool 已销毁"); -} - -// ============================================================================ -// 纹理加载 -// ============================================================================ - -/** - * @brief 从文件加载纹理 - * @param path 文件路径 - * @param options 加载选项 - * @return 纹理引用 - * - * 加载完整纹理文件到纹理池 - */ -TextureRef TexturePool::load(const std::string& path, const TextureLoadOptions& options) { - return load(path, Rect::Zero(), options); -} - -/** - * @brief 从文件加载纹理区域 - * @param path 文件路径 - * @param region 纹理区域 - * @param options 加载选项 - * @return 纹理引用 - * - * 加载纹理文件的指定区域到纹理池 - */ -TextureRef TexturePool::load(const std::string& path, const Rect& region, - const TextureLoadOptions& options) { - TextureKey key(path, region); - - std::lock_guard lock(mutex_); - - // 检查缓存 - auto it = cache_.find(key); - if (it != cache_.end()) { - // 缓存命中 - it->second.touch(); - it->second.refCount.fetch_add(1, std::memory_order_relaxed); - cacheHits_.fetch_add(1, std::memory_order_relaxed); - - E2D_LOG_DEBUG("纹理缓存命中: {}", path); - return TextureRef(it->second.texture, &it->second, &mutex_); - } - - // 缓存未命中 - cacheMisses_.fetch_add(1, std::memory_order_relaxed); - - // 获取渲染后端 - RenderBackend* backend = nullptr; - if (scene_) { - // 假设 Scene 有获取 RenderBackend 的方法 - // 这里需要根据实际接口调整 - backend = nullptr; // TODO: 从 Scene 获取 RenderBackend - } - - if (!backend) { - E2D_LOG_ERROR("TexturePool: 渲染后端不可用"); - return TextureRef(); - } - - // 加载纹理 - Ptr texture = backend->loadTexture(path); - if (!texture) { - E2D_LOG_ERROR("TexturePool: 加载纹理失败: {}", path); - return TextureRef(); - } - - // 计算内存大小 - size_t memorySize = calculateTextureMemory(texture.get()); - - // 检查内存限制 - if (maxMemoryUsage_ > 0 && currentMemoryUsage_ + memorySize > maxMemoryUsage_) { - // 尝试淘汰 - evictLRU(currentMemoryUsage_ + memorySize - maxMemoryUsage_); - - // 再次检查 - if (currentMemoryUsage_ + memorySize > maxMemoryUsage_) { - E2D_LOG_WARN("TexturePool: 内存限制超出,无法加载纹理: {}", path); - return TextureRef(); - } - } - - // 创建缓存条目 - auto result = cache_.emplace(key, TexturePoolEntry(nullptr, key, 0)); - if (result.second) { - result.first->second.texture = texture; - result.first->second.memorySize = memorySize; - result.first->second.refCount.store(1, std::memory_order_relaxed); - result.first->second.touch(); - currentMemoryUsage_ += memorySize; - E2D_LOG_INFO("TexturePool: 已加载纹理: {} ({} 字节)", path, memorySize); - return TextureRef(texture, &result.first->second, &mutex_); - } - - return TextureRef(); -} - -/** - * @brief 从内存加载纹理 - * @param data 像素数据 - * @param width 宽度 - * @param height 高度 - * @param channels 通道数 - * @param key 缓存键 - * @return 纹理引用 - * - * 从内存中的像素数据创建纹理并加入纹理池 - */ -TextureRef TexturePool::loadFromMemory(const uint8_t* data, int width, int height, - int channels, const std::string& key) { - TextureKey textureKey(key); - - std::lock_guard lock(mutex_); - - // 检查缓存 - auto it = cache_.find(textureKey); - if (it != cache_.end()) { - it->second.touch(); - it->second.refCount.fetch_add(1, std::memory_order_relaxed); - cacheHits_.fetch_add(1, std::memory_order_relaxed); - return TextureRef(it->second.texture, &it->second, &mutex_); - } - - cacheMisses_.fetch_add(1, std::memory_order_relaxed); - - // 获取渲染后端 - RenderBackend* backend = nullptr; - if (scene_) { - backend = nullptr; // TODO: 从 Scene 获取 RenderBackend - } - - if (!backend) { - E2D_LOG_ERROR("TexturePool: 渲染后端不可用"); - return TextureRef(); - } - - // 创建纹理 - Ptr texture = backend->createTexture(width, height, data, channels); - if (!texture) { - E2D_LOG_ERROR("TexturePool: 从内存创建纹理失败"); - return TextureRef(); - } - - // 计算内存大小 - size_t memorySize = calculateTextureMemory(texture.get()); - - // 检查内存限制 - if (maxMemoryUsage_ > 0 && currentMemoryUsage_ + memorySize > maxMemoryUsage_) { - evictLRU(currentMemoryUsage_ + memorySize - maxMemoryUsage_); - - if (currentMemoryUsage_ + memorySize > maxMemoryUsage_) { - E2D_LOG_WARN("TexturePool: 内存限制超出"); - return TextureRef(); - } - } - - // 创建缓存条目 - auto result = cache_.emplace(textureKey, TexturePoolEntry(nullptr, textureKey, 0)); - if (result.second) { - result.first->second.texture = texture; - result.first->second.memorySize = memorySize; - result.first->second.refCount.store(1, std::memory_order_relaxed); - result.first->second.touch(); - currentMemoryUsage_ += memorySize; - E2D_LOG_INFO("TexturePool: 已从内存创建纹理 ({} 字节)", memorySize); - return TextureRef(texture, &result.first->second, &mutex_); - } - - return TextureRef(); -} - -/** - * @brief 获取或加载纹理 - * @param path 文件路径 - * @param options 加载选项 - * @return 纹理引用 - * - * 如果纹理已缓存则返回缓存,否则加载纹理 - */ -TextureRef TexturePool::getOrLoad(const std::string& path, const TextureLoadOptions& options) { - return getOrLoad(path, Rect::Zero(), options); -} - -/** - * @brief 获取或加载纹理区域 - * @param path 文件路径 - * @param region 纹理区域 - * @param options 加载选项 - * @return 纹理引用 - * - * 如果纹理区域已缓存则返回缓存,否则加载纹理区域 - */ -TextureRef TexturePool::getOrLoad(const std::string& path, const Rect& region, - const TextureLoadOptions& options) { - TextureKey key(path, region); - - std::lock_guard lock(mutex_); - - // 检查缓存 - auto it = cache_.find(key); - if (it != cache_.end()) { - it->second.touch(); - it->second.refCount.fetch_add(1, std::memory_order_relaxed); - cacheHits_.fetch_add(1, std::memory_order_relaxed); - return TextureRef(it->second.texture, &it->second, &mutex_); - } - - // 释放锁后调用 load - // 注意:这里需要重新设计以避免死锁 - // 简化处理:直接在这里加载 - - cacheMisses_.fetch_add(1, std::memory_order_relaxed); - - RenderBackend* backend = nullptr; - if (scene_) { - backend = nullptr; // TODO: 从 Scene 获取 RenderBackend - } - - if (!backend) { - E2D_LOG_ERROR("TexturePool: 渲染后端不可用"); - return TextureRef(); - } - - Ptr texture = backend->loadTexture(path); - if (!texture) { - E2D_LOG_ERROR("TexturePool: 加载纹理失败: {}", path); - return TextureRef(); - } - - size_t memorySize = calculateTextureMemory(texture.get()); - - if (maxMemoryUsage_ > 0 && currentMemoryUsage_ + memorySize > maxMemoryUsage_) { - evictLRU(currentMemoryUsage_ + memorySize - maxMemoryUsage_); - - if (currentMemoryUsage_ + memorySize > maxMemoryUsage_) { - E2D_LOG_WARN("TexturePool: 内存限制超出"); - return TextureRef(); - } - } - - auto result = cache_.emplace(key, TexturePoolEntry(nullptr, key, 0)); - if (result.second) { - result.first->second.texture = texture; - result.first->second.memorySize = memorySize; - result.first->second.refCount.store(1, std::memory_order_relaxed); - result.first->second.touch(); - currentMemoryUsage_ += memorySize; - return TextureRef(texture, &result.first->second, &mutex_); - } - - return TextureRef(); -} - -// ============================================================================ -// 引用计数管理 -// ============================================================================ - -/** - * @brief 增加引用计数 - * @param key 纹理键 - * @return 是否成功 - * - * 增加指定纹理的引用计数 - */ -bool TexturePool::addRef(const TextureKey& key) { - std::lock_guard lock(mutex_); - - auto it = cache_.find(key); - if (it != cache_.end()) { - it->second.touch(); - it->second.refCount.fetch_add(1, std::memory_order_relaxed); - return true; - } - return false; -} - -/** - * @brief 减少引用计数 - * @param key 纹理键 - * @return 减少后的引用计数 - * - * 减少指定纹理的引用计数并返回新值 - */ -uint32_t TexturePool::release(const TextureKey& key) { - std::lock_guard lock(mutex_); - - auto it = cache_.find(key); - if (it != cache_.end()) { - uint32_t count = it->second.refCount.fetch_sub(1, std::memory_order_relaxed); - return count > 0 ? count - 1 : 0; - } - return 0; -} - -/** - * @brief 获取引用计数 - * @param key 纹理键 - * @return 引用计数 - * - * 获取指定纹理的当前引用计数 - */ -uint32_t TexturePool::getRefCount(const TextureKey& key) const { - std::lock_guard lock(mutex_); - - auto it = cache_.find(key); - if (it != cache_.end()) { - return it->second.refCount.load(std::memory_order_relaxed); - } - return 0; -} - -// ============================================================================ -// 缓存管理 -// ============================================================================ - -/** - * @brief 检查纹理是否已缓存 - * @param key 纹理键 - * @return 是否已缓存 - * - * 检查指定纹理是否存在于缓存中 - */ -bool TexturePool::isCached(const TextureKey& key) const { - std::lock_guard lock(mutex_); - return cache_.find(key) != cache_.end(); -} - -/** - * @brief 从缓存中移除纹理 - * @param key 纹理键 - * @return 是否成功 - * - * 从缓存中移除指定的纹理 - */ -bool TexturePool::removeFromCache(const TextureKey& key) { - std::lock_guard lock(mutex_); - - auto it = cache_.find(key); - if (it != cache_.end()) { - currentMemoryUsage_ -= it->second.memorySize; - cache_.erase(it); - E2D_LOG_DEBUG("TexturePool: 已从缓存移除纹理"); - return true; - } - return false; -} - -/** - * @brief 垃圾回收(移除引用计数为 0 的纹理) - * @return 移除的纹理数量 - * - * 清理所有引用计数为0的纹理,释放内存 - */ -size_t TexturePool::collectGarbage() { - std::lock_guard lock(mutex_); - - size_t removed = 0; - for (auto it = cache_.begin(); it != cache_.end(); ) { - if (it->second.refCount.load(std::memory_order_relaxed) == 0) { - currentMemoryUsage_ -= it->second.memorySize; - it = cache_.erase(it); - ++removed; - } else { - ++it; - } - } - - if (removed > 0) { - E2D_LOG_INFO("TexturePool: 垃圾回收 {} 个纹理", removed); - } - - return removed; -} - -/** - * @brief 清空所有缓存 - * - * 移除纹理池中的所有纹理 - */ -void TexturePool::clear() { - std::lock_guard lock(mutex_); - - cache_.clear(); - currentMemoryUsage_ = 0; - - E2D_LOG_INFO("TexturePool: 已清除所有纹理"); -} - -// ============================================================================ -// 内存管理 -// ============================================================================ - -/** - * @brief 获取当前内存使用量 - * @return 内存使用量(字节) - * - * 返回纹理池当前的内存使用量 - */ -size_t TexturePool::getMemoryUsage() const { - std::lock_guard lock(mutex_); - return currentMemoryUsage_; -} - -/** - * @brief 设置最大内存使用量 - * @param maxMemory 最大内存使用量(0 表示无限制) - * - * 设置纹理池的内存上限,如果当前使用量超过新上限则执行淘汰 - */ -void TexturePool::setMaxMemoryUsage(size_t maxMemory) { - std::lock_guard lock(mutex_); - maxMemoryUsage_ = maxMemory; - - // 如果当前内存超过新的限制,执行淘汰 - if (maxMemoryUsage_ > 0 && currentMemoryUsage_ > maxMemoryUsage_) { - evictLRU(maxMemoryUsage_); - } - - E2D_LOG_INFO("TexturePool: 最大内存设置为 {} 字节", maxMemory); -} - -/** - * @brief 执行 LRU 淘汰 - * @param targetMemory 目标内存使用量 - * @return 淘汰的纹理数量 - * - * 根据LRU算法淘汰最少使用的纹理以达到目标内存使用量 - */ -size_t TexturePool::evictLRU(size_t targetMemory) { - // 注意:调用者应该已持有锁 - - if (cache_.empty()) { - return 0; - } - - // 收集所有条目并按最后访问时间排序 - std::vector> entries; - entries.reserve(cache_.size()); - - for (const auto& pair : cache_) { - // 只淘汰引用计数为 0 的纹理 - if (pair.second.refCount.load(std::memory_order_relaxed) == 0) { - entries.emplace_back(pair.first, pair.second.lastAccessTime); - } - } - - // 按访问时间升序排序(最旧的在前) - std::sort(entries.begin(), entries.end(), - [](const auto& a, const auto& b) { return a.second < b.second; }); - - size_t evicted = 0; - size_t target = targetMemory > 0 ? targetMemory : 0; - - for (const auto& entry : entries) { - if (targetMemory > 0 && currentMemoryUsage_ <= target) { - break; - } - - auto it = cache_.find(entry.first); - if (it != cache_.end()) { - currentMemoryUsage_ -= it->second.memorySize; - cache_.erase(it); - ++evicted; - } - } - - if (evicted > 0) { - evictionCount_.fetch_add(evicted, std::memory_order_relaxed); - E2D_LOG_INFO("TexturePool: LRU 淘汰 {} 个纹理", evicted); - } - - return evicted; -} - -// ============================================================================ -// 统计信息 -// ============================================================================ - -/** - * @brief 获取统计信息 - * @return 统计信息结构体 - * - * 返回纹理池的统计信息,包括纹理数量、内存使用、缓存命中率等 - */ -TexturePool::Stats TexturePool::getStats() const { - std::lock_guard lock(mutex_); - - Stats stats; - stats.textureCount = cache_.size(); - stats.memoryUsage = currentMemoryUsage_; - stats.maxMemoryUsage = maxMemoryUsage_; - stats.cacheHits = cacheHits_.load(std::memory_order_relaxed); - stats.cacheMisses = cacheMisses_.load(std::memory_order_relaxed); - stats.evictionCount = evictionCount_.load(std::memory_order_relaxed); - - return stats; -} - -/** - * @brief 重置统计信息 - * - * 清零缓存命中、未命中和淘汰计数 - */ -void TexturePool::resetStats() { - cacheHits_.store(0, std::memory_order_relaxed); - cacheMisses_.store(0, std::memory_order_relaxed); - evictionCount_.store(0, std::memory_order_relaxed); -} - -// ============================================================================ -// 私有方法 -// ============================================================================ - -/** - * @brief 计算纹理内存大小 - * @param texture 纹理对象 - * @return 内存大小(字节) - * - * 根据纹理的尺寸、通道数和像素格式计算内存占用 - */ -size_t TexturePool::calculateTextureMemory(const Texture* texture) { - if (!texture) { - return 0; - } - - int width = texture->getWidth(); - int height = texture->getHeight(); - int channels = texture->getChannels(); - - // 基础内存计算 - size_t baseSize = static_cast(width) * height * channels; - - // 根据像素格式调整 - PixelFormat format = texture->getFormat(); - switch (format) { - case PixelFormat::RGB16F: - case PixelFormat::RGBA16F: - baseSize *= 2; // 半精度浮点 - break; - case PixelFormat::RGB32F: - case PixelFormat::RGBA32F: - baseSize *= 4; // 全精度浮点 - break; - case PixelFormat::Depth16: - baseSize = static_cast(width) * height * 2; - break; - case PixelFormat::Depth24: - case PixelFormat::Depth24Stencil8: - baseSize = static_cast(width) * height * 4; - break; - case PixelFormat::Depth32F: - baseSize = static_cast(width) * height * 4; - break; - default: - break; - } - - // 考虑 Mipmaps(大约增加 33% 内存) - // 注意:这里假设生成了 mipmaps,实际应该根据 TextureLoadOptions 判断 - // baseSize = baseSize * 4 / 3; - - return baseSize; -} - -/** - * @brief 检查是否需要淘汰 - * @return 是否需要淘汰 - * - * 检查当前内存使用量是否超过限制 - */ -bool TexturePool::needsEviction() const { - return maxMemoryUsage_ > 0 && currentMemoryUsage_ > maxMemoryUsage_; -} - -/** - * @brief 尝试自动淘汰 - * - * 如果内存使用量超过限制,执行LRU淘汰 - */ -void TexturePool::tryAutoEvict() { - if (needsEviction()) { - evictLRU(maxMemoryUsage_); - } -} - -} // namespace extra2d diff --git a/Extra2D/src/render/backends/opengl/gl_rhi_buffer.cpp b/Extra2D/src/render/backends/opengl/gl_rhi_buffer.cpp new file mode 100644 index 0000000..bb34c7e --- /dev/null +++ b/Extra2D/src/render/backends/opengl/gl_rhi_buffer.cpp @@ -0,0 +1,239 @@ +#include +#include +#include + +namespace extra2d { +namespace rhi { + +// ============================================================================ +// GLRHIBuffer 实现 +// ============================================================================ + +GLRHIBuffer::GLRHIBuffer() = default; + +GLRHIBuffer::~GLRHIBuffer() { + shutdown(); +} + +bool GLRHIBuffer::init(const BufferDesc& desc) { + if (bufferID_ != 0) { + shutdown(); + } + + type_ = desc.type; + usage_ = desc.usage; + size_ = desc.size; + debugName_ = desc.debugName; + target_ = convertType(type_); + + // 使用 OpenGL 4.5 DSA 创建缓冲区 + glCreateBuffers(1, &bufferID_); + if (bufferID_ == 0) { + E2D_LOG_ERROR("创建 OpenGL 缓冲区失败"); + return false; + } + + // 使用 DSA 直接设置缓冲区数据 + GLbitfield flags = 0; + if (usage_ == BufferUsage::Dynamic || usage_ == BufferUsage::Stream) { + flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT; + } + + if (flags != 0 && desc.data == nullptr) { + // 使用持久映射 + glNamedBufferStorage(bufferID_, static_cast(size_), nullptr, flags); + } else { + // 传统方式 + GLenum glUsage = convertUsage(usage_); + glNamedBufferData(bufferID_, static_cast(size_), desc.data, glUsage); + } + + E2D_LOG_DEBUG("GLRHIBuffer 已创建: ID={}, 大小={}, 类型={}", + bufferID_, size_, static_cast(type_)); + + return true; +} + +void GLRHIBuffer::shutdown() { + if (bufferID_ != 0) { + if (mapped_) { + unmap(); + } + glDeleteBuffers(1, &bufferID_); + E2D_LOG_DEBUG("GLRHIBuffer 已销毁: ID={}", bufferID_); + bufferID_ = 0; + } + size_ = 0; + mapped_ = false; + mappedPtr_ = nullptr; +} + +void GLRHIBuffer::setData(const void* data, size_t size, size_t offset) { + if (bufferID_ == 0 || data == nullptr) { + return; + } + + if (offset + size > size_) { + E2D_LOG_WARN("GLRHIBuffer setData 越界: offset={}, size={}, bufferSize={}", + offset, size, size_); + return; + } + + if (offset == 0 && size == size_) { + // 完整更新 + GLenum glUsage = convertUsage(usage_); + glNamedBufferData(bufferID_, static_cast(size_), data, glUsage); + size_ = size; + } else { + // 部分更新 + glNamedBufferSubData(bufferID_, static_cast(offset), + static_cast(size), data); + } +} + +void GLRHIBuffer::getData(void* data, size_t size, size_t offset) const { + if (bufferID_ == 0 || data == nullptr) { + return; + } + + if (offset + size > size_) { + E2D_LOG_WARN("GLRHIBuffer getData 越界: offset={}, size={}, bufferSize={}", + offset, size, size_); + return; + } + + glGetNamedBufferSubData(bufferID_, static_cast(offset), + static_cast(size), data); +} + +void* GLRHIBuffer::map(size_t offset, size_t size, bool read, bool write) { + if (bufferID_ == 0 || mapped_) { + return nullptr; + } + + if (offset + size > size_) { + E2D_LOG_WARN("GLRHIBuffer map 越界: offset={}, size={}, bufferSize={}", + offset, size, size_); + return nullptr; + } + + GLbitfield access = 0; + if (read) access |= GL_MAP_READ_BIT; + if (write) access |= GL_MAP_WRITE_BIT; + + if (usage_ == BufferUsage::Dynamic || usage_ == BufferUsage::Stream) { + if (write) { + access |= GL_MAP_INVALIDATE_RANGE_BIT; + } + } + + mappedPtr_ = glMapNamedBufferRange(bufferID_, static_cast(offset), + static_cast(size), access); + + if (mappedPtr_) { + mapped_ = true; + } else { + E2D_LOG_ERROR("映射 GLRHIBuffer 失败"); + } + + return mappedPtr_; +} + +void GLRHIBuffer::unmap() { + if (!mapped_ || bufferID_ == 0) { + return; + } + + glUnmapNamedBuffer(bufferID_); + mapped_ = false; + mappedPtr_ = nullptr; +} + +void GLRHIBuffer::bindBase(uint32_t index) { + if (bufferID_ != 0) { + glBindBufferBase(target_, index, bufferID_); + } +} + +GLenum GLRHIBuffer::convertType(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::Indirect: + return GL_DRAW_INDIRECT_BUFFER; + default: + return GL_ARRAY_BUFFER; + } +} + +GLenum GLRHIBuffer::convertUsage(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; + } +} + +// ============================================================================ +// GLRHIVertexBuffer 实现 +// ============================================================================ + +bool GLRHIVertexBuffer::init(size_t size, BufferUsage usage, const void* data) { + BufferDesc desc; + desc.type = BufferType::Vertex; + desc.usage = usage; + desc.size = size; + desc.data = data; + return buffer_.init(desc); +} + +void GLRHIVertexBuffer::shutdown() { + buffer_.shutdown(); +} + +// ============================================================================ +// GLRHIIndexBuffer 实现 +// ============================================================================ + +bool GLRHIIndexBuffer::init(size_t size, BufferUsage usage, const void* data) { + BufferDesc desc; + desc.type = BufferType::Index; + desc.usage = usage; + desc.size = size; + desc.data = data; + return buffer_.init(desc); +} + +void GLRHIIndexBuffer::shutdown() { + buffer_.shutdown(); +} + +// ============================================================================ +// GLRHIUniformBuffer 实现 +// ============================================================================ + +bool GLRHIUniformBuffer::init(size_t size, BufferUsage usage) { + BufferDesc desc; + desc.type = BufferType::Uniform; + desc.usage = usage; + desc.size = size; + return buffer_.init(desc); +} + +void GLRHIUniformBuffer::shutdown() { + buffer_.shutdown(); +} + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/src/render/backends/opengl/gl_rhi_device.cpp b/Extra2D/src/render/backends/opengl/gl_rhi_device.cpp new file mode 100644 index 0000000..8896805 --- /dev/null +++ b/Extra2D/src/render/backends/opengl/gl_rhi_device.cpp @@ -0,0 +1,610 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { +namespace rhi { + +// ============================================================================ +// GLRHIDevice 实现 +// ============================================================================ + +GLRHIDevice::GLRHIDevice() = default; + +GLRHIDevice::~GLRHIDevice() { + shutdown(); +} + +bool GLRHIDevice::init(IWindow* window) { + if (initialized_) { + return true; + } + + window_ = window; + + // 初始化 OpenGL 上下文 + if (!GLContext::get().init()) { + E2D_LOG_ERROR("初始化 OpenGL 上下文失败"); + return false; + } + + // 初始化设备能力 + initCaps(); + + // 设置默认状态 + setDefaultState(); + + initialized_ = true; + + E2D_LOG_INFO("OpenGL RHI 设备已初始化"); + E2D_LOG_INFO(" API: OpenGL {}.{}", GLContext::get().getVersion().major, + GLContext::get().getVersion().minor); + E2D_LOG_INFO(" 设备: {}", caps_.deviceName); + E2D_LOG_INFO(" 供应商: {}", caps_.vendorName); + + return true; +} + +void GLRHIDevice::shutdown() { + if (!initialized_) { + return; + } + + // 清理当前绑定的资源 + currentPipeline_.reset(); + currentShader_.reset(); + currentFramebuffer_.reset(); + + GLContext::get().shutdown(); + initialized_ = false; + + E2D_LOG_INFO("OpenGL RHI 设备已关闭"); +} + +void GLRHIDevice::initCaps() { + caps_.api = GraphicsAPI::OpenGL; + caps_.apiVersion = GLContext::get().getVersionString(); + caps_.deviceName = GLContext::get().getRenderer(); + caps_.vendorName = GLContext::get().getVendor(); + caps_.maxTextureSize = static_cast(GLContext::get().getMaxTextureSize()); + caps_.maxTextureUnits = static_cast(GLContext::get().getMaxTextureUnits()); + caps_.maxVertexAttributes = static_cast(GLContext::get().getMaxVertexAttribs()); + caps_.maxUniformBufferBindings = static_cast(GLContext::get().getMaxUniformBufferBindings()); + + // 查询最大颜色附件数 + GLint maxColorAttachments; + glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachments); + caps_.maxColorAttachments = static_cast(maxColorAttachments); + + // 查询最大各向异性 + GLfloat maxAnisotropy; + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &maxAnisotropy); + caps_.maxAnisotropy = maxAnisotropy; + + // 检查特性支持 + caps_.supportsCompute = GLContext::get().getVersion().major >= 4; + caps_.supportsGeometry = true; + caps_.supportsTessellation = GLContext::get().getVersion().major >= 4; + caps_.supportsInstancing = true; + caps_.supportsIndirectDraw = GLContext::get().getVersion().major >= 4; + caps_.supportsMultiDrawIndirect = GLContext::get().getVersion().major >= 4; + caps_.supportsPersistentMapping = GLContext::get().getVersion().major >= 4; + caps_.supportsBindlessTextures = GLContext::get().hasExtension("GL_ARB_bindless_texture"); +} + +void GLRHIDevice::setDefaultState() { + glEnable(GL_TEXTURE_2D); + glDisable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClearDepth(1.0f); + glClearStencil(0); +} + +// ============================================================================ +// 帧管理 +// ============================================================================ + +void GLRHIDevice::beginFrame() { + resetStats(); +} + +void GLRHIDevice::endFrame() { + // OpenGL 的帧结束不需要特殊处理 +} + +void GLRHIDevice::present() { + if (window_) { + window_->swap(); + } +} + +void GLRHIDevice::setVSync(bool enabled) { + vsyncEnabled_ = enabled; + if (window_) { + window_->setVSync(enabled); + } +} + +void GLRHIDevice::setDefaultFramebufferSize(uint32_t width, uint32_t height) { + defaultFBWidth_ = width; + defaultFBHeight_ = height; +} + +// ============================================================================ +// 资源创建 - 缓冲区 +// ============================================================================ + +Ptr GLRHIDevice::createBuffer(const BufferDesc& desc) { + auto buffer = std::make_shared(); + if (!buffer->init(desc)) { + E2D_LOG_ERROR("创建缓冲区失败"); + return nullptr; + } + return buffer; +} + +Ptr GLRHIDevice::createVertexBuffer(size_t size, BufferUsage usage, const void* data) { + auto buffer = std::make_shared(); + if (!buffer->init(size, usage, data)) { + E2D_LOG_ERROR("创建顶点缓冲区失败"); + return nullptr; + } + return buffer; +} + +Ptr GLRHIDevice::createIndexBuffer(size_t size, BufferUsage usage, const void* data) { + auto buffer = std::make_shared(); + if (!buffer->init(size, usage, data)) { + E2D_LOG_ERROR("创建索引缓冲区失败"); + return nullptr; + } + return buffer; +} + +Ptr GLRHIDevice::createUniformBuffer(size_t size, BufferUsage usage) { + auto buffer = std::make_shared(); + if (!buffer->init(size, usage)) { + E2D_LOG_ERROR("创建 Uniform 缓冲区失败"); + return nullptr; + } + return buffer; +} + +// ============================================================================ +// 资源创建 - 纹理 +// ============================================================================ + +Ptr GLRHIDevice::createTexture(const TextureDesc& desc) { + auto texture = std::make_shared(); + if (!texture->init(desc)) { + E2D_LOG_ERROR("创建纹理失败"); + return nullptr; + } + return texture; +} + +Ptr GLRHIDevice::createTexture2D(uint32_t width, uint32_t height, Format format, const void* data) { + TextureDesc desc = TextureDesc::texture2D(width, height, format, data); + return createTexture(desc); +} + +Ptr GLRHIDevice::createSampler(const SamplerDesc& desc) { + auto sampler = std::make_shared(); + if (!sampler->init(desc)) { + E2D_LOG_ERROR("创建采样器失败"); + return nullptr; + } + return sampler; +} + +Ptr GLRHIDevice::createTextureView(Ptr texture, const TextureViewDesc& desc) { + auto view = std::make_shared(); + if (!view->init(texture, desc)) { + E2D_LOG_ERROR("创建纹理视图失败"); + return nullptr; + } + return view; +} + +// ============================================================================ +// 资源创建 - 着色器 +// ============================================================================ + +Ptr GLRHIDevice::createShader(const ShaderDesc& desc) { + auto shader = std::make_shared(); + if (!shader->init(desc)) { + E2D_LOG_ERROR("创建着色器失败: {}", desc.name); + return nullptr; + } + return shader; +} + +Ptr GLRHIDevice::createShaderFromSource(const std::string& name, + const std::string& vertexSource, + const std::string& fragmentSource) { + auto shader = std::make_shared(); + if (!shader->compileFromSource(name, vertexSource, fragmentSource)) { + E2D_LOG_ERROR("从源码创建着色器失败: {}", name); + return nullptr; + } + return shader; +} + +Ptr GLRHIDevice::createShaderFromFile(const std::string& vertexPath, + const std::string& fragmentPath) { + // 读取顶点着色器文件 + std::ifstream vertFile(vertexPath); + if (!vertFile.is_open()) { + E2D_LOG_ERROR("无法打开顶点着色器文件: {}", vertexPath); + return nullptr; + } + std::stringstream vertStream; + vertStream << vertFile.rdbuf(); + std::string vertexSource = vertStream.str(); + + // 读取片段着色器文件 + std::ifstream fragFile(fragmentPath); + if (!fragFile.is_open()) { + E2D_LOG_ERROR("无法打开片段着色器文件: {}", fragmentPath); + return nullptr; + } + std::stringstream fragStream; + fragStream << fragFile.rdbuf(); + std::string fragmentSource = fragStream.str(); + + // 提取着色器名称 + std::string name = vertexPath; + size_t lastSlash = name.find_last_of("/\\"); + if (lastSlash != std::string::npos) { + name = name.substr(lastSlash + 1); + } + + return createShaderFromSource(name, vertexSource, fragmentSource); +} + +// ============================================================================ +// 资源创建 - 管线 +// ============================================================================ + +Ptr GLRHIDevice::createPipeline(const PipelineDesc& desc, Ptr shader) { + auto pipeline = std::make_shared(); + if (!pipeline->init(desc, shader)) { + E2D_LOG_ERROR("创建渲染管线失败"); + return nullptr; + } + return pipeline; +} + +Ptr GLRHIDevice::createPipeline(const PipelineBuilder& builder) { + return createPipeline(builder.build(), builder.getShader()); +} + +// ============================================================================ +// 资源创建 - 帧缓冲 +// ============================================================================ + +Ptr GLRHIDevice::createFramebuffer(const FramebufferDesc& desc) { + // 帧缓冲实现将在后续添加 + E2D_LOG_WARN("createFramebuffer 尚未实现"); + return nullptr; +} + +Ptr GLRHIDevice::createFramebuffer(const FramebufferBuilder& builder) { + return createFramebuffer(builder.build()); +} + +// ============================================================================ +// 渲染状态设置 +// ============================================================================ + +void GLRHIDevice::setViewport(const Viewport& viewport) { + glViewport(static_cast(viewport.x), static_cast(viewport.y), + static_cast(viewport.width), static_cast(viewport.height)); + glDepthRangef(viewport.minDepth, viewport.maxDepth); +} + +void GLRHIDevice::setScissorRect(const ScissorRect& rect) { + glScissor(rect.x, rect.y, static_cast(rect.width), static_cast(rect.height)); +} + +void GLRHIDevice::setScissorEnabled(bool enabled) { + scissorEnabled_ = enabled; + if (enabled) { + glEnable(GL_SCISSOR_TEST); + } else { + glDisable(GL_SCISSOR_TEST); + } +} + +// ============================================================================ +// 渲染命令 - 绑定资源 +// ============================================================================ + +void GLRHIDevice::setPipeline(Ptr pipeline) { + currentPipeline_ = pipeline; + if (pipeline) { + pipeline->bind(); + stats_.shaderBinds++; + } +} + +void GLRHIDevice::setShader(Ptr shader) { + currentShader_ = shader; + if (shader) { + shader->bind(); + stats_.shaderBinds++; + } +} + +void GLRHIDevice::setVertexBuffer(uint32_t slot, Ptr buffer, uint32_t offset) { + if (buffer && currentPipeline_) { + GLuint vao = static_cast(currentPipeline_->getNativeHandle()); + GLuint vbo = static_cast(buffer->getNativeHandle()); + + // 使用 DSA 绑定顶点缓冲区 + glVertexArrayVertexBuffer(vao, slot, vbo, static_cast(offset), + static_cast(currentPipeline_->calculateVertexStride())); + stats_.bufferBinds++; + } +} + +void GLRHIDevice::setIndexBuffer(Ptr buffer, IndexFormat format) { + if (buffer && currentPipeline_) { + GLuint vao = static_cast(currentPipeline_->getNativeHandle()); + GLuint ibo = static_cast(buffer->getNativeHandle()); + + // 绑定索引缓冲区到 VAO + glVertexArrayElementBuffer(vao, ibo); + currentIndexFormat_ = format; + stats_.bufferBinds++; + } +} + +void GLRHIDevice::setTexture(uint32_t slot, Ptr texture, Ptr sampler) { + if (texture) { + GLuint texID = static_cast(texture->getNativeHandle()); + glBindTextureUnit(slot, texID); + + if (sampler) { + GLuint samplerID = static_cast(sampler->getNativeHandle()); + glBindSampler(slot, samplerID); + } + + stats_.textureBinds++; + } else { + glBindTextureUnit(slot, 0); + } +} + +void GLRHIDevice::setUniformBuffer(uint32_t slot, Ptr buffer) { + if (buffer) { + GLuint ubo = static_cast(buffer->getNativeHandle()); + glBindBufferBase(GL_UNIFORM_BUFFER, slot, ubo); + stats_.bufferBinds++; + } +} + +void GLRHIDevice::setFramebuffer(Ptr framebuffer) { + currentFramebuffer_ = framebuffer; + if (framebuffer) { + GLuint fbo = static_cast(framebuffer->getNativeHandle()); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + stats_.framebufferBinds++; + } else { + setDefaultFramebuffer(); + } +} + +void GLRHIDevice::setDefaultFramebuffer() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + currentFramebuffer_.reset(); +} + +// ============================================================================ +// 渲染命令 - Uniform 设置 +// ============================================================================ + +void GLRHIDevice::setUniformFloat(const std::string& name, float value) { + if (currentShader_) { + currentShader_->setFloat(name, value); + } +} + +void GLRHIDevice::setUniformVec2(const std::string& name, const glm::vec2& value) { + if (currentShader_) { + currentShader_->setVec2(name, value); + } +} + +void GLRHIDevice::setUniformVec3(const std::string& name, const glm::vec3& value) { + if (currentShader_) { + currentShader_->setVec3(name, value); + } +} + +void GLRHIDevice::setUniformVec4(const std::string& name, const glm::vec4& value) { + if (currentShader_) { + currentShader_->setVec4(name, value); + } +} + +void GLRHIDevice::setUniformMat4(const std::string& name, const glm::mat4& value) { + if (currentShader_) { + currentShader_->setMat4(name, value); + } +} + +void GLRHIDevice::setUniformInt(const std::string& name, int value) { + if (currentShader_) { + currentShader_->setInt(name, value); + } +} + +// ============================================================================ +// 渲染命令 - 绘制 +// ============================================================================ + +void GLRHIDevice::draw(const DrawCommand& cmd) { + GLenum mode = currentPipeline_ ? + static_cast(currentPipeline_.get())->getGLPrimitiveType() : + GL_TRIANGLES; + + if (cmd.instanceCount > 1) { + glDrawArraysInstanced(mode, static_cast(cmd.firstVertex), + static_cast(cmd.vertexCount), + static_cast(cmd.instanceCount)); + } else { + glDrawArrays(mode, static_cast(cmd.firstVertex), + static_cast(cmd.vertexCount)); + } + + stats_.drawCalls++; + stats_.vertexCount += cmd.vertexCount * cmd.instanceCount; + stats_.triangleCount += (cmd.vertexCount / 3) * cmd.instanceCount; +} + +void GLRHIDevice::drawIndexed(const DrawIndexedCommand& cmd) { + GLenum mode = currentPipeline_ ? + static_cast(currentPipeline_.get())->getGLPrimitiveType() : + GL_TRIANGLES; + GLenum type = (currentIndexFormat_ == IndexFormat::UInt32) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; + const void* indices = reinterpret_cast( + static_cast(cmd.firstIndex * getIndexFormatSize(currentIndexFormat_))); + + if (cmd.instanceCount > 1) { + glDrawElementsInstanced(mode, static_cast(cmd.indexCount), type, + indices, static_cast(cmd.instanceCount)); + } else { + glDrawElements(mode, static_cast(cmd.indexCount), type, indices); + } + + stats_.drawCalls++; + stats_.triangleCount += (cmd.indexCount / 3) * cmd.instanceCount; +} + +void GLRHIDevice::drawIndirect(Ptr indirectBuffer, size_t offset) { + if (!indirectBuffer || !caps_.supportsIndirectDraw) { + return; + } + + GLenum mode = currentPipeline_ ? + static_cast(currentPipeline_.get())->getGLPrimitiveType() : + GL_TRIANGLES; + + GLuint bufferID = static_cast(indirectBuffer->getNativeHandle()); + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, bufferID); + glDrawArraysIndirect(mode, reinterpret_cast(offset)); + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); + + stats_.drawCalls++; +} + +void GLRHIDevice::drawIndexedIndirect(Ptr indirectBuffer, size_t offset) { + if (!indirectBuffer || !caps_.supportsIndirectDraw) { + return; + } + + GLenum mode = currentPipeline_ ? + static_cast(currentPipeline_.get())->getGLPrimitiveType() : + GL_TRIANGLES; + GLenum type = (currentIndexFormat_ == IndexFormat::UInt32) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; + + GLuint bufferID = static_cast(indirectBuffer->getNativeHandle()); + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, bufferID); + glDrawElementsIndirect(mode, type, reinterpret_cast(offset)); + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); + + stats_.drawCalls++; +} + +// ============================================================================ +// 渲染命令 - 清除 +// ============================================================================ + +void GLRHIDevice::clearColor(const ColorValue& color) { + glClearColor(color.r, color.g, color.b, color.a); + glClear(GL_COLOR_BUFFER_BIT); +} + +void GLRHIDevice::clearDepth(float depth) { + glClearDepth(depth); + glClear(GL_DEPTH_BUFFER_BIT); +} + +void GLRHIDevice::clearStencil(uint8_t stencil) { + glClearStencil(stencil); + glClear(GL_STENCIL_BUFFER_BIT); +} + +void GLRHIDevice::clear(const ColorValue& color, float depth, uint8_t stencil) { + glClearColor(color.r, color.g, color.b, color.a); + glClearDepth(depth); + glClearStencil(stencil); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + +// ============================================================================ +// 统计与调试 +// ============================================================================ + +void GLRHIDevice::resetStats() { + stats_ = DeviceStats{}; +} + +bool GLRHIDevice::checkError() { + GLenum error = glGetError(); + if (error != GL_NO_ERROR) { + const char* errorStr; + switch (error) { + case GL_INVALID_ENUM: + errorStr = "GL_INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + errorStr = "GL_INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + errorStr = "GL_INVALID_OPERATION"; + break; + case GL_OUT_OF_MEMORY: + errorStr = "GL_OUT_OF_MEMORY"; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + errorStr = "GL_INVALID_FRAMEBUFFER_OPERATION"; + break; + default: + errorStr = "Unknown error"; + break; + } + E2D_LOG_ERROR("OpenGL 错误: {}", errorStr); + return false; + } + return true; +} + +// ============================================================================ +// 静态工厂方法实现 +// ============================================================================ + +Ptr RHIDevice::create(GraphicsAPI api) { + switch (api) { + case GraphicsAPI::OpenGL: + return std::make_shared(); + default: + E2D_LOG_ERROR("不支持的图形 API: {}", static_cast(api)); + return nullptr; + } +} + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/src/render/backends/opengl/gl_rhi_pipeline.cpp b/Extra2D/src/render/backends/opengl/gl_rhi_pipeline.cpp new file mode 100644 index 0000000..e6da474 --- /dev/null +++ b/Extra2D/src/render/backends/opengl/gl_rhi_pipeline.cpp @@ -0,0 +1,436 @@ +#include +#include +#include + +namespace extra2d { +namespace rhi { + +// ============================================================================ +// GLRHIPipeline 实现 +// ============================================================================ + +GLRHIPipeline::GLRHIPipeline() = default; + +GLRHIPipeline::~GLRHIPipeline() { + shutdown(); +} + +bool GLRHIPipeline::init(const PipelineDesc& desc, Ptr shader) { + if (vaoID_ != 0) { + shutdown(); + } + + shader_ = shader; + blendState_ = desc.blendState; + depthStencilState_ = desc.depthStencilState; + rasterizerState_ = desc.rasterizerState; + primitiveType_ = desc.primitiveType; + vertexAttributes_ = desc.vertexAttributes; + vertexBindings_ = desc.vertexBindings; + debugName_ = desc.debugName; + + glPrimitiveType_ = convertPrimitiveType(primitiveType_); + + // 创建 VAO + glCreateVertexArrays(1, &vaoID_); + if (vaoID_ == 0) { + E2D_LOG_ERROR("创建 OpenGL VAO 失败"); + return false; + } + + // 设置顶点属性 + setupVertexAttributes(); + + E2D_LOG_DEBUG("GLRHIPipeline 已创建: VAO ID={}", vaoID_); + + return true; +} + +void GLRHIPipeline::shutdown() { + if (vaoID_ != 0) { + glDeleteVertexArrays(1, &vaoID_); + E2D_LOG_DEBUG("GLRHIPipeline 已销毁: VAO ID={}", vaoID_); + vaoID_ = 0; + } + shader_.reset(); +} + +void GLRHIPipeline::bind() { + if (vaoID_ != 0) { + glBindVertexArray(vaoID_); + } + + if (shader_) { + shader_->bind(); + } + + // 应用混合状态 + if (blendState_.enabled) { + glEnable(GL_BLEND); + glBlendFuncSeparate( + convertBlendFactor(blendState_.srcColorFactor), + convertBlendFactor(blendState_.dstColorFactor), + convertBlendFactor(blendState_.srcAlphaFactor), + convertBlendFactor(blendState_.dstAlphaFactor) + ); + glBlendEquationSeparate( + convertBlendOp(blendState_.colorOp), + convertBlendOp(blendState_.alphaOp) + ); + } else { + glDisable(GL_BLEND); + } + + // 应用深度模板状态 + if (depthStencilState_.depthTestEnabled) { + glEnable(GL_DEPTH_TEST); + glDepthMask(depthStencilState_.depthWriteEnabled ? GL_TRUE : GL_FALSE); + glDepthFunc(convertCompareFunc(depthStencilState_.depthCompareFunc)); + } else { + glDisable(GL_DEPTH_TEST); + } + + // 应用光栅化状态 + if (rasterizerState_.cullMode != CullMode::None) { + glEnable(GL_CULL_FACE); + glCullFace(convertCullMode(rasterizerState_.cullMode)); + glFrontFace(convertFrontFace(rasterizerState_.frontFace)); + } else { + glDisable(GL_CULL_FACE); + } + + if (rasterizerState_.wireframe) { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + } else { + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + + glLineWidth(rasterizerState_.lineWidth); +} + +void GLRHIPipeline::unbind() { + glBindVertexArray(0); +} + +void GLRHIPipeline::setBlendMode(BlendFactor srcFactor, BlendFactor dstFactor, BlendOp op) { + blendState_.srcColorFactor = srcFactor; + blendState_.dstColorFactor = dstFactor; + blendState_.srcAlphaFactor = srcFactor; + blendState_.dstAlphaFactor = dstFactor; + blendState_.colorOp = op; + blendState_.alphaOp = op; + + if (blendState_.enabled) { + glBlendFunc(convertBlendFactor(srcFactor), convertBlendFactor(dstFactor)); + glBlendEquation(convertBlendOp(op)); + } +} + +void GLRHIPipeline::setBlendState(const BlendState& state) { + blendState_ = state; + + if (state.enabled) { + glEnable(GL_BLEND); + glBlendFuncSeparate( + convertBlendFactor(state.srcColorFactor), + convertBlendFactor(state.dstColorFactor), + convertBlendFactor(state.srcAlphaFactor), + convertBlendFactor(state.dstAlphaFactor) + ); + glBlendEquationSeparate( + convertBlendOp(state.colorOp), + convertBlendOp(state.alphaOp) + ); + } else { + glDisable(GL_BLEND); + } +} + +void GLRHIPipeline::setBlendEnabled(bool enabled) { + blendState_.enabled = enabled; + if (enabled) { + glEnable(GL_BLEND); + } else { + glDisable(GL_BLEND); + } +} + +void GLRHIPipeline::setDepthTestEnabled(bool enabled) { + depthStencilState_.depthTestEnabled = enabled; + if (enabled) { + glEnable(GL_DEPTH_TEST); + } else { + glDisable(GL_DEPTH_TEST); + } +} + +void GLRHIPipeline::setDepthWriteEnabled(bool enabled) { + depthStencilState_.depthWriteEnabled = enabled; + glDepthMask(enabled ? GL_TRUE : GL_FALSE); +} + +void GLRHIPipeline::setDepthCompareFunc(CompareFunc func) { + depthStencilState_.depthCompareFunc = func; + glDepthFunc(convertCompareFunc(func)); +} + +void GLRHIPipeline::setCullMode(CullMode mode) { + rasterizerState_.cullMode = mode; + if (mode != CullMode::None) { + glEnable(GL_CULL_FACE); + glCullFace(convertCullMode(mode)); + } else { + glDisable(GL_CULL_FACE); + } +} + +void GLRHIPipeline::setFrontFace(FrontFace face) { + rasterizerState_.frontFace = face; + glFrontFace(convertFrontFace(face)); +} + +void GLRHIPipeline::setWireframe(bool enabled) { + rasterizerState_.wireframe = enabled; + glPolygonMode(GL_FRONT_AND_BACK, enabled ? GL_LINE : GL_FILL); +} + +uint32_t GLRHIPipeline::calculateVertexStride() const { + if (vertexBindings_.empty()) { + return 0; + } + return vertexBindings_[0].stride; +} + +void GLRHIPipeline::setupVertexAttributes() { + for (const auto& attr : vertexAttributes_) { + bool normalized = attr.normalized; + GLenum glType = convertVertexFormat(attr.format, normalized); + GLint components = getFormatComponents(attr.format); + + glEnableVertexArrayAttrib(vaoID_, attr.location); + + switch (attr.format) { + case Format::R32_UINT: + case Format::RG32_UINT: + case Format::RGBA32_UINT: + glVertexArrayAttribIFormat(vaoID_, attr.location, components, glType, attr.offset); + break; + default: + glVertexArrayAttribFormat(vaoID_, attr.location, components, glType, + normalized ? GL_TRUE : GL_FALSE, attr.offset); + break; + } + + glVertexArrayAttribBinding(vaoID_, attr.location, attr.bufferSlot); + } + + // 设置顶点缓冲区绑定 + for (const auto& binding : vertexBindings_) { + glVertexArrayBindingDivisor(vaoID_, binding.slot, binding.divisor); + } +} + +GLenum GLRHIPipeline::convertPrimitiveType(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::LineLoop: + return GL_LINE_LOOP; + case PrimitiveType::Triangles: + return GL_TRIANGLES; + case PrimitiveType::TriangleStrip: + return GL_TRIANGLE_STRIP; + case PrimitiveType::TriangleFan: + return GL_TRIANGLE_FAN; + default: + return GL_TRIANGLES; + } +} + +GLenum GLRHIPipeline::convertBlendFactor(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; + case BlendFactor::ConstantColor: + return GL_CONSTANT_COLOR; + case BlendFactor::OneMinusConstantColor: + return GL_ONE_MINUS_CONSTANT_COLOR; + case BlendFactor::ConstantAlpha: + return GL_CONSTANT_ALPHA; + case BlendFactor::OneMinusConstantAlpha: + return GL_ONE_MINUS_CONSTANT_ALPHA; + case BlendFactor::SrcAlphaSaturate: + return GL_SRC_ALPHA_SATURATE; + default: + return GL_ONE; + } +} + +GLenum GLRHIPipeline::convertBlendOp(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; + } +} + +GLenum GLRHIPipeline::convertCompareFunc(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; + } +} + +GLenum GLRHIPipeline::convertCullMode(CullMode mode) { + switch (mode) { + case CullMode::Front: + return GL_FRONT; + case CullMode::Back: + return GL_BACK; + default: + return GL_BACK; + } +} + +GLenum GLRHIPipeline::convertFrontFace(FrontFace face) { + switch (face) { + case FrontFace::Clockwise: + return GL_CW; + case FrontFace::CounterClockwise: + return GL_CCW; + default: + return GL_CCW; + } +} + +GLenum GLRHIPipeline::convertVertexFormat(Format format, bool& normalized) { + normalized = false; + + switch (format) { + case Format::R8_UNORM: + normalized = true; + return GL_UNSIGNED_BYTE; + case Format::RG8_UNORM: + normalized = true; + return GL_UNSIGNED_BYTE; + case Format::RGB8_UNORM: + normalized = true; + return GL_UNSIGNED_BYTE; + case Format::RGBA8_UNORM: + normalized = true; + return GL_UNSIGNED_BYTE; + case Format::R8_SNORM: + normalized = true; + return GL_BYTE; + case Format::RG8_SNORM: + normalized = true; + return GL_BYTE; + case Format::RGB8_SNORM: + normalized = true; + return GL_BYTE; + case Format::RGBA8_SNORM: + normalized = true; + return GL_BYTE; + case Format::R16_FLOAT: + return GL_HALF_FLOAT; + case Format::RG16_FLOAT: + return GL_HALF_FLOAT; + case Format::RGBA16_FLOAT: + return GL_HALF_FLOAT; + case Format::R32_FLOAT: + return GL_FLOAT; + case Format::RG32_FLOAT: + return GL_FLOAT; + case Format::RGB32_FLOAT: + return GL_FLOAT; + case Format::RGBA32_FLOAT: + return GL_FLOAT; + case Format::R32_UINT: + return GL_UNSIGNED_INT; + case Format::RG32_UINT: + return GL_UNSIGNED_INT; + case Format::RGBA32_UINT: + return GL_UNSIGNED_INT; + default: + return GL_FLOAT; + } +} + +GLint GLRHIPipeline::getFormatComponents(Format format) { + switch (format) { + case Format::R8_UNORM: + case Format::R8_SNORM: + case Format::R16_FLOAT: + case Format::R32_FLOAT: + case Format::R32_UINT: + return 1; + case Format::RG8_UNORM: + case Format::RG8_SNORM: + case Format::RG16_FLOAT: + case Format::RG32_FLOAT: + case Format::RG32_UINT: + return 2; + case Format::RGB8_UNORM: + case Format::RGB8_SNORM: + case Format::RGB32_FLOAT: + return 3; + case Format::RGBA8_UNORM: + case Format::RGBA8_SNORM: + case Format::RGBA16_FLOAT: + case Format::RGBA32_FLOAT: + case Format::RGBA32_UINT: + return 4; + default: + return 4; + } +} + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/src/render/backends/opengl/gl_rhi_shader.cpp b/Extra2D/src/render/backends/opengl/gl_rhi_shader.cpp new file mode 100644 index 0000000..a6f7167 --- /dev/null +++ b/Extra2D/src/render/backends/opengl/gl_rhi_shader.cpp @@ -0,0 +1,468 @@ +#include +#include +#include + +namespace extra2d { +namespace rhi { + +// ============================================================================ +// GLRHIShader 实现 +// ============================================================================ + +GLRHIShader::GLRHIShader() = default; + +GLRHIShader::~GLRHIShader() { + shutdown(); +} + +bool GLRHIShader::init(const ShaderDesc& desc) { + if (!desc.vertexSource.empty() && !desc.fragmentSource.empty()) { + return compileFromSource(desc.name, desc.vertexSource, desc.fragmentSource, desc.geometrySource); + } + return false; +} + +bool GLRHIShader::compileFromSource(const std::string& name, + const std::string& vertexSource, + const std::string& fragmentSource, + const std::string& geometrySource) { + if (programID_ != 0) { + shutdown(); + } + + name_ = name; + + // 编译顶点着色器 + GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSource.c_str()); + if (vertexShader == 0) { + E2D_LOG_ERROR("编译顶点着色器失败: {}", name_); + return false; + } + + // 编译片段着色器 + GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentSource.c_str()); + if (fragmentShader == 0) { + E2D_LOG_ERROR("编译片段着色器失败: {}", name_); + glDeleteShader(vertexShader); + return false; + } + + // 编译几何着色器(可选) + GLuint geometryShader = 0; + if (!geometrySource.empty()) { + geometryShader = compileShader(GL_GEOMETRY_SHADER, geometrySource.c_str()); + if (geometryShader == 0) { + E2D_LOG_WARN("编译几何着色器失败: {}", name_); + } + } + + // 链接程序 + bool success = linkProgram(vertexShader, fragmentShader, geometryShader); + + // 清理着色器对象 + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + if (geometryShader != 0) { + glDeleteShader(geometryShader); + } + + if (success) { + // 反射获取 Uniform 和属性信息 + reflectUniforms(); + reflectAttributes(); + + E2D_LOG_DEBUG("GLRHIShader 已创建: {}, ID={}", name_, programID_); + } + + return success; +} + +void GLRHIShader::shutdown() { + if (programID_ != 0) { + glDeleteProgram(programID_); + E2D_LOG_DEBUG("GLRHIShader 已销毁: {}, ID={}", name_, programID_); + programID_ = 0; + } + uniformCache_.clear(); + uniforms_.clear(); + attributes_.clear(); +} + +void GLRHIShader::bind() { + if (programID_ != 0) { + glUseProgram(programID_); + } +} + +void GLRHIShader::unbind() { + glUseProgram(0); +} + +void GLRHIShader::setBool(const std::string& name, bool value) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniform1i(programID_, location, value ? 1 : 0); + } +} + +void GLRHIShader::setInt(const std::string& name, int value) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniform1i(programID_, location, value); + } +} + +void GLRHIShader::setUInt(const std::string& name, uint32_t value) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniform1ui(programID_, location, value); + } +} + +void GLRHIShader::setFloat(const std::string& name, float value) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniform1f(programID_, location, value); + } +} + +void GLRHIShader::setVec2(const std::string& name, const glm::vec2& value) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniform2fv(programID_, location, 1, &value[0]); + } +} + +void GLRHIShader::setVec3(const std::string& name, const glm::vec3& value) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniform3fv(programID_, location, 1, &value[0]); + } +} + +void GLRHIShader::setVec4(const std::string& name, const glm::vec4& value) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniform4fv(programID_, location, 1, &value[0]); + } +} + +void GLRHIShader::setMat2(const std::string& name, const glm::mat2& value) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniformMatrix2fv(programID_, location, 1, GL_FALSE, &value[0][0]); + } +} + +void GLRHIShader::setMat3(const std::string& name, const glm::mat3& value) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniformMatrix3fv(programID_, location, 1, GL_FALSE, &value[0][0]); + } +} + +void GLRHIShader::setMat4(const std::string& name, const glm::mat4& value) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniformMatrix4fv(programID_, location, 1, GL_FALSE, &value[0][0]); + } +} + +void GLRHIShader::setTexture(const std::string& name, uint32_t slot) { + setInt(name, static_cast(slot)); +} + +void GLRHIShader::setIntArray(const std::string& name, const int* values, uint32_t count) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniform1iv(programID_, location, count, values); + } +} + +void GLRHIShader::setFloatArray(const std::string& name, const float* values, uint32_t count) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniform1fv(programID_, location, count, values); + } +} + +void GLRHIShader::setVec2Array(const std::string& name, const glm::vec2* values, uint32_t count) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniform2fv(programID_, location, count, &values[0][0]); + } +} + +void GLRHIShader::setVec3Array(const std::string& name, const glm::vec3* values, uint32_t count) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniform3fv(programID_, location, count, &values[0][0]); + } +} + +void GLRHIShader::setVec4Array(const std::string& name, const glm::vec4* values, uint32_t count) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniform4fv(programID_, location, count, &values[0][0]); + } +} + +void GLRHIShader::setMat4Array(const std::string& name, const glm::mat4* values, uint32_t count) { + GLint location = getOrCacheUniformLocation(name); + if (location >= 0) { + glProgramUniformMatrix4fv(programID_, location, count, GL_FALSE, &values[0][0][0]); + } +} + +bool GLRHIShader::hasUniform(const std::string& name) const { + return getUniformLocation(name) >= 0; +} + +int GLRHIShader::getUniformLocation(const std::string& name) const { + auto it = uniformCache_.find(name); + if (it != uniformCache_.end()) { + return it->second; + } + GLint location = glGetUniformLocation(programID_, name.c_str()); + uniformCache_[name] = location; + return location; +} + +GLuint GLRHIShader::compileShader(GLenum type, const char* source) { + GLuint shader = glCreateShader(type); + if (shader == 0) { + E2D_LOG_ERROR("创建着色器对象失败"); + return 0; + } + + glShaderSource(shader, 1, &source, nullptr); + glCompileShader(shader); + + // 检查编译错误 + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar infoLog[1024]; + glGetShaderInfoLog(shader, sizeof(infoLog), nullptr, infoLog); + + const char* shaderTypeStr = ""; + switch (type) { + case GL_VERTEX_SHADER: shaderTypeStr = "顶点"; break; + case GL_FRAGMENT_SHADER: shaderTypeStr = "片段"; break; + case GL_GEOMETRY_SHADER: shaderTypeStr = "几何"; break; + default: shaderTypeStr = "未知"; break; + } + + E2D_LOG_ERROR("着色器编译错误 ({}): {}", shaderTypeStr, infoLog); + glDeleteShader(shader); + return 0; + } + + return shader; +} + +bool GLRHIShader::linkProgram(GLuint vertexShader, GLuint fragmentShader, GLuint geometryShader) { + programID_ = glCreateProgram(); + if (programID_ == 0) { + E2D_LOG_ERROR("创建着色器程序失败"); + return false; + } + + glAttachShader(programID_, vertexShader); + glAttachShader(programID_, fragmentShader); + if (geometryShader != 0) { + glAttachShader(programID_, geometryShader); + } + + glLinkProgram(programID_); + + // 检查链接错误 + GLint success; + glGetProgramiv(programID_, GL_LINK_STATUS, &success); + if (!success) { + GLchar infoLog[1024]; + glGetProgramInfoLog(programID_, sizeof(infoLog), nullptr, infoLog); + E2D_LOG_ERROR("着色器程序链接错误: {}", infoLog); + glDeleteProgram(programID_); + programID_ = 0; + return false; + } + + return true; +} + +void GLRHIShader::reflectUniforms() { + uniforms_.clear(); + + GLint numUniforms; + glGetProgramiv(programID_, GL_ACTIVE_UNIFORMS, &numUniforms); + + GLint maxNameLength; + glGetProgramiv(programID_, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxNameLength); + + std::vector nameBuffer(maxNameLength); + + for (GLint i = 0; i < numUniforms; ++i) { + GLsizei nameLength; + GLint size; + GLenum type; + + glGetActiveUniform(programID_, i, maxNameLength, &nameLength, &size, &type, nameBuffer.data()); + + UniformInfo info; + info.name = std::string(nameBuffer.data(), nameLength); + info.location = glGetUniformLocation(programID_, nameBuffer.data()); + info.size = size; + + // 根据类型推断着色器阶段 + info.stage = ShaderStage::Vertex; // 默认 + + uniforms_.push_back(info); + uniformCache_[info.name] = info.location; + } +} + +void GLRHIShader::reflectAttributes() { + attributes_.clear(); + + GLint numAttributes; + glGetProgramiv(programID_, GL_ACTIVE_ATTRIBUTES, &numAttributes); + + GLint maxNameLength; + glGetProgramiv(programID_, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxNameLength); + + std::vector nameBuffer(maxNameLength); + + for (GLint i = 0; i < numAttributes; ++i) { + GLsizei nameLength; + GLint size; + GLenum type; + + glGetActiveAttrib(programID_, i, maxNameLength, &nameLength, &size, &type, nameBuffer.data()); + + AttributeInfo info; + info.name = std::string(nameBuffer.data(), nameLength); + info.location = glGetAttribLocation(programID_, nameBuffer.data()); + + // 根据类型推断格式 + switch (type) { + case GL_FLOAT: + info.format = Format::R32_FLOAT; + break; + case GL_FLOAT_VEC2: + info.format = Format::RG32_FLOAT; + break; + case GL_FLOAT_VEC3: + info.format = Format::RGB32_FLOAT; + break; + case GL_FLOAT_VEC4: + info.format = Format::RGBA32_FLOAT; + break; + case GL_INT: + info.format = Format::R32_UINT; + break; + case GL_INT_VEC2: + info.format = Format::RG32_UINT; + break; + case GL_INT_VEC3: + case GL_INT_VEC4: + info.format = Format::RGBA32_UINT; + break; + default: + info.format = Format::RGBA32_FLOAT; + break; + } + + attributes_.push_back(info); + } +} + +GLint GLRHIShader::getOrCacheUniformLocation(const std::string& name) const { + auto it = uniformCache_.find(name); + if (it != uniformCache_.end()) { + return it->second; + } + GLint location = glGetUniformLocation(programID_, name.c_str()); + uniformCache_[name] = location; + return location; +} + +// ============================================================================ +// GLRHIShaderModule 实现 +// ============================================================================ + +GLRHIShaderModule::GLRHIShaderModule() = default; + +GLRHIShaderModule::~GLRHIShaderModule() { + shutdown(); +} + +bool GLRHIShaderModule::init(ShaderStage stage, const std::string& source, const std::string& entryPoint) { + if (shaderID_ != 0) { + shutdown(); + } + + stage_ = stage; + entryPoint_ = entryPoint; + + GLenum glStage; + switch (stage) { + case ShaderStage::Vertex: + glStage = GL_VERTEX_SHADER; + break; + case ShaderStage::Fragment: + glStage = GL_FRAGMENT_SHADER; + break; + case ShaderStage::Geometry: + glStage = GL_GEOMETRY_SHADER; + break; + case ShaderStage::TessControl: + glStage = GL_TESS_CONTROL_SHADER; + break; + case ShaderStage::TessEvaluation: + glStage = GL_TESS_EVALUATION_SHADER; + break; + case ShaderStage::Compute: + glStage = GL_COMPUTE_SHADER; + break; + default: + E2D_LOG_ERROR("未知的着色器阶段"); + return false; + } + + shaderID_ = glCreateShader(glStage); + if (shaderID_ == 0) { + E2D_LOG_ERROR("创建着色器对象失败"); + return false; + } + + const char* src = source.c_str(); + glShaderSource(shaderID_, 1, &src, nullptr); + glCompileShader(shaderID_); + + GLint success; + glGetShaderiv(shaderID_, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar infoLog[1024]; + glGetShaderInfoLog(shaderID_, sizeof(infoLog), nullptr, infoLog); + E2D_LOG_ERROR("着色器编译错误: {}", infoLog); + glDeleteShader(shaderID_); + shaderID_ = 0; + return false; + } + + E2D_LOG_DEBUG("GLRHIShaderModule 已创建: ID={}, 阶段={}", shaderID_, static_cast(stage)); + + return true; +} + +void GLRHIShaderModule::shutdown() { + if (shaderID_ != 0) { + glDeleteShader(shaderID_); + E2D_LOG_DEBUG("GLRHIShaderModule 已销毁: ID={}", shaderID_); + shaderID_ = 0; + } +} + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/src/render/backends/opengl/gl_rhi_texture.cpp b/Extra2D/src/render/backends/opengl/gl_rhi_texture.cpp new file mode 100644 index 0000000..0c14e86 --- /dev/null +++ b/Extra2D/src/render/backends/opengl/gl_rhi_texture.cpp @@ -0,0 +1,510 @@ +#include +#include +#include + +namespace extra2d { +namespace rhi { + +// ============================================================================ +// GLRHITexture 实现 +// ============================================================================ + +GLRHITexture::GLRHITexture() = default; + +GLRHITexture::~GLRHITexture() { + shutdown(); +} + +bool GLRHITexture::init(const TextureDesc& desc) { + if (textureID_ != 0) { + shutdown(); + } + + type_ = desc.type; + format_ = desc.format; + width_ = desc.width; + height_ = desc.height; + depth_ = desc.depth; + mipLevels_ = desc.mipLevels; + arrayLayers_ = desc.arrayLayers; + sampleCount_ = desc.sampleCount; + debugName_ = desc.debugName; + + GLenum target = convertType(type_); + + // 使用 OpenGL 4.5 DSA 创建纹理 + glCreateTextures(target, 1, &textureID_); + if (textureID_ == 0) { + E2D_LOG_ERROR("创建 OpenGL 纹理失败"); + return false; + } + + // 分配纹理存储 + GLenum internalFormat = convertInternalFormat(format_); + + switch (type_) { + case TextureType::Texture2D: + glTextureStorage2D(textureID_, mipLevels_, internalFormat, width_, height_); + break; + case TextureType::Texture2DArray: + glTextureStorage3D(textureID_, mipLevels_, internalFormat, width_, height_, arrayLayers_); + break; + case TextureType::Texture3D: + glTextureStorage3D(textureID_, mipLevels_, internalFormat, width_, height_, depth_); + break; + case TextureType::TextureCube: + glTextureStorage2D(textureID_, mipLevels_, internalFormat, width_, height_); + break; + case TextureType::TextureCubeArray: + glTextureStorage3D(textureID_, mipLevels_, internalFormat, width_, height_, arrayLayers_ * 6); + break; + } + + // 设置默认纹理参数 + glTextureParameteri(textureID_, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(textureID_, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTextureParameteri(textureID_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTextureParameteri(textureID_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTextureParameteri(textureID_, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + // 上传初始数据 + if (desc.data != nullptr) { + setData(desc.data, 0, 0); + } + + E2D_LOG_DEBUG("GLRHITexture 已创建: ID={}, 尺寸={}x{}, 格式={}", + textureID_, width_, height_, static_cast(format_)); + + return true; +} + +void GLRHITexture::shutdown() { + if (textureID_ != 0) { + glDeleteTextures(1, &textureID_); + E2D_LOG_DEBUG("GLRHITexture 已销毁: ID={}", textureID_); + textureID_ = 0; + } +} + +void GLRHITexture::setData(const void* data, uint32_t mipLevel, uint32_t arrayLayer) { + if (textureID_ == 0 || data == nullptr) { + return; + } + + GLenum format = convertFormat(format_); + GLenum dataType = convertDataType(format_); + + switch (type_) { + case TextureType::Texture2D: + glTextureSubImage2D(textureID_, mipLevel, 0, 0, + width_ >> mipLevel, height_ >> mipLevel, + format, dataType, data); + break; + case TextureType::Texture2DArray: + case TextureType::TextureCube: + case TextureType::TextureCubeArray: + glTextureSubImage3D(textureID_, mipLevel, 0, 0, arrayLayer, + width_ >> mipLevel, height_ >> mipLevel, 1, + format, dataType, data); + break; + case TextureType::Texture3D: + glTextureSubImage3D(textureID_, mipLevel, 0, 0, 0, + width_ >> mipLevel, height_ >> mipLevel, depth_, + format, dataType, data); + break; + } +} + +void GLRHITexture::setSubData(const void* data, uint32_t x, uint32_t y, + uint32_t width, uint32_t height, uint32_t mipLevel) { + if (textureID_ == 0 || data == nullptr) { + return; + } + + GLenum format = convertFormat(format_); + GLenum dataType = convertDataType(format_); + + switch (type_) { + case TextureType::Texture2D: + glTextureSubImage2D(textureID_, mipLevel, x, y, width, height, + format, dataType, data); + break; + default: + E2D_LOG_WARN("setSubData 仅支持 Texture2D"); + break; + } +} + +void GLRHITexture::generateMipmaps() { + if (textureID_ != 0) { + glGenerateTextureMipmap(textureID_); + } +} + +void GLRHITexture::bind(uint32_t unit) { + if (textureID_ != 0) { + glBindTextureUnit(unit, textureID_); + } +} + +GLenum GLRHITexture::convertType(TextureType type) { + switch (type) { + case TextureType::Texture2D: + return GL_TEXTURE_2D; + case TextureType::Texture2DArray: + return GL_TEXTURE_2D_ARRAY; + case TextureType::Texture3D: + return GL_TEXTURE_3D; + case TextureType::TextureCube: + return GL_TEXTURE_CUBE_MAP; + case TextureType::TextureCubeArray: + return GL_TEXTURE_CUBE_MAP_ARRAY; + default: + return GL_TEXTURE_2D; + } +} + +GLenum GLRHITexture::convertInternalFormat(Format format) { + switch (format) { + case Format::R8_UNORM: + return GL_R8; + case Format::RG8_UNORM: + return GL_RG8; + case Format::RGB8_UNORM: + return GL_RGB8; + case Format::RGBA8_UNORM: + return GL_RGBA8; + case Format::R8_SNORM: + return GL_R8_SNORM; + case Format::RG8_SNORM: + return GL_RG8_SNORM; + case Format::RGB8_SNORM: + return GL_RGB8_SNORM; + case Format::RGBA8_SNORM: + return GL_RGBA8_SNORM; + case Format::R16_FLOAT: + return GL_R16F; + case Format::RG16_FLOAT: + return GL_RG16F; + case Format::RGBA16_FLOAT: + return GL_RGBA16F; + case Format::R32_FLOAT: + return GL_R32F; + case Format::RG32_FLOAT: + return GL_RG32F; + case Format::RGB32_FLOAT: + return GL_RGB32F; + case Format::RGBA32_FLOAT: + return GL_RGBA32F; + case Format::R32_UINT: + return GL_R32UI; + case Format::RG32_UINT: + return GL_RG32UI; + case Format::RGBA32_UINT: + return GL_RGBA32UI; + case Format::D16_UNORM: + return GL_DEPTH_COMPONENT16; + case Format::D24_UNORM_S8_UINT: + return GL_DEPTH24_STENCIL8; + case Format::D32_FLOAT: + return GL_DEPTH_COMPONENT32F; + case Format::D32_FLOAT_S8_UINT: + return GL_DEPTH32F_STENCIL8; + case Format::BC1_RGB_UNORM: + return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + case Format::BC1_RGBA_UNORM: + return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case Format::BC3_RGBA_UNORM: + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case Format::BC5_RG_UNORM: + return GL_COMPRESSED_RG_RGTC2; + case Format::BC7_RGBA_UNORM: + return GL_COMPRESSED_RGBA_BPTC_UNORM; + case Format::ETC2_RGB8_UNORM: + return GL_COMPRESSED_RGB8_ETC2; + case Format::ETC2_RGBA8_UNORM: + return GL_COMPRESSED_RGBA8_ETC2_EAC; + default: + return GL_RGBA8; + } +} + +GLenum GLRHITexture::convertFormat(Format format) { + switch (format) { + case Format::R8_UNORM: + case Format::R8_SNORM: + case Format::R16_FLOAT: + case Format::R32_FLOAT: + case Format::R32_UINT: + return GL_RED; + case Format::RG8_UNORM: + case Format::RG8_SNORM: + case Format::RG16_FLOAT: + case Format::RG32_FLOAT: + case Format::RG32_UINT: + return GL_RG; + case Format::RGB8_UNORM: + case Format::RGB8_SNORM: + case Format::RGB32_FLOAT: + return GL_RGB; + case Format::RGBA8_UNORM: + case Format::RGBA8_SNORM: + case Format::RGBA16_FLOAT: + case Format::RGBA32_FLOAT: + case Format::RGBA32_UINT: + return GL_RGBA; + case Format::D16_UNORM: + case Format::D32_FLOAT: + return GL_DEPTH_COMPONENT; + case Format::D24_UNORM_S8_UINT: + case Format::D32_FLOAT_S8_UINT: + return GL_DEPTH_STENCIL; + default: + return GL_RGBA; + } +} + +GLenum GLRHITexture::convertDataType(Format format) { + switch (format) { + case Format::R8_UNORM: + case Format::RG8_UNORM: + case Format::RGB8_UNORM: + case Format::RGBA8_UNORM: + return GL_UNSIGNED_BYTE; + case Format::R8_SNORM: + case Format::RG8_SNORM: + case Format::RGB8_SNORM: + case Format::RGBA8_SNORM: + return GL_BYTE; + case Format::R16_FLOAT: + case Format::RG16_FLOAT: + case Format::RGBA16_FLOAT: + return GL_HALF_FLOAT; + case Format::R32_FLOAT: + case Format::RG32_FLOAT: + case Format::RGB32_FLOAT: + case Format::RGBA32_FLOAT: + return GL_FLOAT; + case Format::R32_UINT: + case Format::RG32_UINT: + case Format::RGBA32_UINT: + return GL_UNSIGNED_INT; + case Format::D16_UNORM: + return GL_UNSIGNED_SHORT; + case Format::D24_UNORM_S8_UINT: + return GL_UNSIGNED_INT_24_8; + case Format::D32_FLOAT: + return GL_FLOAT; + case Format::D32_FLOAT_S8_UINT: + return GL_FLOAT_32_UNSIGNED_INT_24_8_REV; + default: + return GL_UNSIGNED_BYTE; + } +} + +uint32_t GLRHITexture::getFormatBytes(Format format) { + return getFormatSize(format); +} + +// ============================================================================ +// GLRHISampler 实现 +// ============================================================================ + +GLRHISampler::GLRHISampler() = default; + +GLRHISampler::~GLRHISampler() { + shutdown(); +} + +bool GLRHISampler::init(const SamplerDesc& desc) { + if (samplerID_ != 0) { + shutdown(); + } + + minFilter_ = desc.minFilter; + magFilter_ = desc.magFilter; + mipFilter_ = desc.mipFilter; + addressU_ = desc.addressU; + addressV_ = desc.addressV; + addressW_ = desc.addressW; + maxAnisotropy_ = desc.maxAnisotropy; + borderColor_ = desc.borderColor; + + glCreateSamplers(1, &samplerID_); + if (samplerID_ == 0) { + E2D_LOG_ERROR("创建 OpenGL 采样器失败"); + return false; + } + + GLenum minFilter = convertFilter(minFilter_, mipFilter_); + GLenum magFilter = (magFilter_ == FilterMode::Nearest) ? GL_NEAREST : GL_LINEAR; + + glSamplerParameteri(samplerID_, GL_TEXTURE_MIN_FILTER, minFilter); + glSamplerParameteri(samplerID_, GL_TEXTURE_MAG_FILTER, magFilter); + + glSamplerParameteri(samplerID_, GL_TEXTURE_WRAP_S, convertAddressMode(addressU_)); + glSamplerParameteri(samplerID_, GL_TEXTURE_WRAP_T, convertAddressMode(addressV_)); + glSamplerParameteri(samplerID_, GL_TEXTURE_WRAP_R, convertAddressMode(addressW_)); + + glSamplerParameterf(samplerID_, GL_TEXTURE_MAX_ANISOTROPY, maxAnisotropy_); + + glSamplerParameterf(samplerID_, GL_TEXTURE_MIN_LOD, desc.minLOD); + glSamplerParameterf(samplerID_, GL_TEXTURE_MAX_LOD, desc.maxLOD); + glSamplerParameterf(samplerID_, GL_TEXTURE_LOD_BIAS, desc.lodBias); + + glSamplerParameterfv(samplerID_, GL_TEXTURE_BORDER_COLOR, &borderColor_.r); + + if (desc.compareEnabled) { + glSamplerParameteri(samplerID_, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glSamplerParameteri(samplerID_, GL_TEXTURE_COMPARE_FUNC, + convertCompareFunc(desc.compareFunc)); + } + + E2D_LOG_DEBUG("GLRHISampler 已创建: ID={}", samplerID_); + + return true; +} + +void GLRHISampler::shutdown() { + if (samplerID_ != 0) { + glDeleteSamplers(1, &samplerID_); + E2D_LOG_DEBUG("GLRHISampler 已销毁: ID={}", samplerID_); + samplerID_ = 0; + } +} + +void GLRHISampler::bind(uint32_t unit) { + if (samplerID_ != 0) { + glBindSampler(unit, samplerID_); + } +} + +GLenum GLRHISampler::convertFilter(FilterMode filter, FilterMode mipFilter) { + switch (filter) { + case FilterMode::Nearest: + switch (mipFilter) { + case FilterMode::Nearest: + return GL_NEAREST_MIPMAP_NEAREST; + case FilterMode::Linear: + return GL_NEAREST_MIPMAP_LINEAR; + default: + return GL_NEAREST; + } + case FilterMode::Linear: + switch (mipFilter) { + case FilterMode::Nearest: + return GL_LINEAR_MIPMAP_NEAREST; + case FilterMode::Linear: + return GL_LINEAR_MIPMAP_LINEAR; + default: + return GL_LINEAR; + } + case FilterMode::NearestMipmapNearest: + return GL_NEAREST_MIPMAP_NEAREST; + case FilterMode::LinearMipmapNearest: + return GL_LINEAR_MIPMAP_NEAREST; + case FilterMode::NearestMipmapLinear: + return GL_NEAREST_MIPMAP_LINEAR; + case FilterMode::LinearMipmapLinear: + return GL_LINEAR_MIPMAP_LINEAR; + default: + return GL_LINEAR; + } +} + +GLenum GLRHISampler::convertAddressMode(AddressMode mode) { + switch (mode) { + case AddressMode::Repeat: + return GL_REPEAT; + case AddressMode::MirroredRepeat: + return GL_MIRRORED_REPEAT; + case AddressMode::ClampToEdge: + return GL_CLAMP_TO_EDGE; + case AddressMode::ClampToBorder: + return GL_CLAMP_TO_BORDER; + case AddressMode::MirrorClampToEdge: + return GL_MIRROR_CLAMP_TO_EDGE; + default: + return GL_CLAMP_TO_EDGE; + } +} + +GLenum GLRHISampler::convertCompareFunc(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; + } +} + +// ============================================================================ +// GLRHITextureView 实现 +// ============================================================================ + +GLRHITextureView::GLRHITextureView() = default; + +GLRHITextureView::~GLRHITextureView() { + shutdown(); +} + +bool GLRHITextureView::init(Ptr texture, const TextureViewDesc& desc) { + if (!texture || !texture->isValid()) { + E2D_LOG_ERROR("创建纹理视图失败: 无效的源纹理"); + return false; + } + + texture_ = texture; + format_ = desc.format == Format::Undefined ? texture->getFormat() : desc.format; + baseMipLevel_ = desc.baseMipLevel; + mipLevels_ = desc.mipLevels == 0 ? texture->getMipLevels() : desc.mipLevels; + baseArrayLayer_ = desc.baseArrayLayer; + arrayLayers_ = desc.arrayLayers == 0 ? texture->getArrayLayers() : desc.arrayLayers; + + // 获取源纹理的 OpenGL ID + GLuint srcTextureID = static_cast(texture->getNativeHandle()); + + // 创建纹理视图 + glGenTextures(1, &viewID_); + if (viewID_ == 0) { + E2D_LOG_ERROR("创建 OpenGL 纹理视图失败"); + return false; + } + + GLenum target = GLRHITexture::convertType(desc.viewType); + GLenum internalFormat = GLRHITexture::convertInternalFormat(format_); + + glTextureView(viewID_, target, srcTextureID, internalFormat, + baseMipLevel_, mipLevels_, baseArrayLayer_, arrayLayers_); + + E2D_LOG_DEBUG("GLRHITextureView 已创建: ID={}, 源纹理ID={}", + viewID_, srcTextureID); + + return true; +} + +void GLRHITextureView::shutdown() { + if (viewID_ != 0) { + glDeleteTextures(1, &viewID_); + E2D_LOG_DEBUG("GLRHITextureView 已销毁: ID={}", viewID_); + viewID_ = 0; + } + texture_.reset(); +} + +} // namespace rhi +} // namespace extra2d diff --git a/Extra2D/src/render/batch/shape_batcher.cpp b/Extra2D/src/render/batch/shape_batcher.cpp new file mode 100644 index 0000000..20788c5 --- /dev/null +++ b/Extra2D/src/render/batch/shape_batcher.cpp @@ -0,0 +1,562 @@ +#include +#include +#include +#include + +namespace extra2d { + +ShapeBatcher::ShapeBatcher() + : device_(nullptr), shader_(nullptr), pipeline_(nullptr), linePipeline_(nullptr), + vertexBuffer_(nullptr), indexBuffer_(nullptr), uniformBuffer_(nullptr), + viewProj_(1.0f), currentBlend_(rhi::BlendState::alphaBlend()), + currentVertexIndex_(0), currentIndexIndex_(0), + drawCallCount_(0), shapeCount_(0), batchCount_(0), + initialized_(false), inBatch_(false) { +} + +ShapeBatcher::ShapeBatcher(const ShapeBatcherConfig& config) + : ShapeBatcher() { + config_ = config; +} + +ShapeBatcher::~ShapeBatcher() { + shutdown(); +} + +bool ShapeBatcher::init(Ptr device) { + if (initialized_) { + return true; + } + + if (!device) { + return false; + } + + device_ = device; + + if (!createShader()) { + return false; + } + + if (!createPipeline()) { + return false; + } + + if (!createBuffers()) { + return false; + } + + viewProj_ = glm::ortho(0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f); + + initialized_ = true; + return true; +} + +void ShapeBatcher::shutdown() { + if (!initialized_) { + return; + } + + vertices_.clear(); + indices_.clear(); + batches_.clear(); + + uniformBuffer_.reset(); + indexBuffer_.reset(); + vertexBuffer_.reset(); + linePipeline_.reset(); + pipeline_.reset(); + shader_.reset(); + device_.reset(); + + initialized_ = false; + inBatch_ = false; +} + +void ShapeBatcher::begin() { + begin(viewProj_); +} + +void ShapeBatcher::begin(const glm::mat4& viewProj) { + if (!initialized_ || inBatch_) { + return; + } + + viewProj_ = viewProj; + currentVertexIndex_ = 0; + currentIndexIndex_ = 0; + currentBlend_ = rhi::BlendState::alphaBlend(); + + vertices_.clear(); + indices_.clear(); + batches_.clear(); + + vertices_.reserve(config_.maxBatchSize * 4); + indices_.reserve(config_.maxBatchSize * 6); + + inBatch_ = true; +} + +void ShapeBatcher::end() { + if (!initialized_ || !inBatch_) { + return; + } + + if (!vertices_.empty()) { + flushBatch(rhi::PrimitiveType::Triangles); + } + + inBatch_ = false; +} + +void ShapeBatcher::drawLine(const Vec2& start, const Vec2& end, + const Color& color, float width) { + if (!initialized_ || !inBatch_) { + return; + } + + uint32_t i0 = addVertex(start, color); + uint32_t i1 = addVertex(end, color); + + addIndex(i0); + addIndex(i1); + + shapeCount_++; +} + +void ShapeBatcher::drawLines(const std::vector& points, + const Color& color, float width) { + if (!initialized_ || !inBatch_ || points.size() < 2) { + return; + } + + for (size_t i = 0; i + 1 < points.size(); i += 2) { + drawLine(points[i], points[i + 1], color, width); + } +} + +void ShapeBatcher::drawPolyline(const std::vector& points, + const Color& color, float width) { + if (!initialized_ || !inBatch_ || points.size() < 2) { + return; + } + + for (size_t i = 0; i + 1 < points.size(); ++i) { + drawLine(points[i], points[i + 1], color, width); + } +} + +void ShapeBatcher::drawRect(const Rect& rect, const Color& color, float width) { + if (!initialized_ || !inBatch_) { + return; + } + + Vec2 p1(rect.origin.x, rect.origin.y); + Vec2 p2(rect.origin.x + rect.size.width, rect.origin.y); + Vec2 p3(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height); + Vec2 p4(rect.origin.x, rect.origin.y + rect.size.height); + + drawLine(p1, p2, color, width); + drawLine(p2, p3, color, width); + drawLine(p3, p4, color, width); + drawLine(p4, p1, color, width); +} + +void ShapeBatcher::drawFilledRect(const Rect& rect, const Color& color) { + if (!initialized_ || !inBatch_) { + return; + } + + uint32_t i0 = addVertex(Vec2(rect.origin.x, rect.origin.y), color); + uint32_t i1 = addVertex(Vec2(rect.origin.x + rect.size.width, rect.origin.y), color); + uint32_t i2 = addVertex(Vec2(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height), color); + uint32_t i3 = addVertex(Vec2(rect.origin.x, rect.origin.y + rect.size.height), color); + + addIndex(i0); + addIndex(i1); + addIndex(i2); + addIndex(i0); + addIndex(i2); + addIndex(i3); + + shapeCount_++; +} + +void ShapeBatcher::drawCircle(const Vec2& center, float radius, + const Color& color, int segments, float width) { + if (!initialized_ || !inBatch_ || radius <= 0.0f) { + return; + } + + int segs = segments > 0 ? segments : static_cast(config_.circleSegments); + std::vector vertices; + generateCircleVertices(center, radius, segs, vertices); + + for (int i = 0; i < segs; ++i) { + int next = (i + 1) % segs; + drawLine(vertices[i], vertices[next], color, width); + } +} + +void ShapeBatcher::drawFilledCircle(const Vec2& center, float radius, + const Color& color, int segments) { + if (!initialized_ || !inBatch_ || radius <= 0.0f) { + return; + } + + int segs = segments > 0 ? segments : static_cast(config_.circleSegments); + std::vector vertices; + generateCircleVertices(center, radius, segs, vertices); + + uint32_t centerIdx = addVertex(center, color); + + for (int i = 0; i < segs; ++i) { + uint32_t v0 = addVertex(vertices[i], color); + uint32_t v1 = addVertex(vertices[(i + 1) % segs], color); + + addIndex(centerIdx); + addIndex(v0); + addIndex(v1); + } + + shapeCount_++; +} + +void ShapeBatcher::drawEllipse(const Vec2& center, float radiusX, float radiusY, + const Color& color, int segments, float width) { + if (!initialized_ || !inBatch_ || radiusX <= 0.0f || radiusY <= 0.0f) { + return; + } + + int segs = segments > 0 ? segments : static_cast(config_.circleSegments); + std::vector vertices; + generateEllipseVertices(center, radiusX, radiusY, segs, vertices); + + for (int i = 0; i < segs; ++i) { + int next = (i + 1) % segs; + drawLine(vertices[i], vertices[next], color, width); + } +} + +void ShapeBatcher::drawFilledEllipse(const Vec2& center, float radiusX, float radiusY, + const Color& color, int segments) { + if (!initialized_ || !inBatch_ || radiusX <= 0.0f || radiusY <= 0.0f) { + return; + } + + int segs = segments > 0 ? segments : static_cast(config_.circleSegments); + std::vector vertices; + generateEllipseVertices(center, radiusX, radiusY, segs, vertices); + + uint32_t centerIdx = addVertex(center, color); + + for (int i = 0; i < segs; ++i) { + uint32_t v0 = addVertex(vertices[i], color); + uint32_t v1 = addVertex(vertices[(i + 1) % segs], color); + + addIndex(centerIdx); + addIndex(v0); + addIndex(v1); + } + + shapeCount_++; +} + +void ShapeBatcher::drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3, + const Color& color, float width) { + if (!initialized_ || !inBatch_) { + return; + } + + drawLine(p1, p2, color, width); + drawLine(p2, p3, color, width); + drawLine(p3, p1, color, width); +} + +void ShapeBatcher::drawFilledTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3, + const Color& color) { + if (!initialized_ || !inBatch_) { + return; + } + + uint32_t i0 = addVertex(p1, color); + uint32_t i1 = addVertex(p2, color); + uint32_t i2 = addVertex(p3, color); + + addIndex(i0); + addIndex(i1); + addIndex(i2); + + shapeCount_++; +} + +void ShapeBatcher::drawPolygon(const std::vector& points, + const Color& color, float width) { + if (!initialized_ || !inBatch_ || points.size() < 3) { + return; + } + + for (size_t i = 0; i < points.size(); ++i) { + size_t next = (i + 1) % points.size(); + drawLine(points[i], points[next], color, width); + } +} + +void ShapeBatcher::drawFilledPolygon(const std::vector& points, + const Color& color) { + if (!initialized_ || !inBatch_ || points.size() < 3) { + return; + } + + uint32_t i0 = addVertex(points[0], color); + + for (size_t i = 1; i + 1 < points.size(); ++i) { + uint32_t i1 = addVertex(points[i], color); + uint32_t i2 = addVertex(points[i + 1], color); + + addIndex(i0); + addIndex(i1); + addIndex(i2); + } + + shapeCount_++; +} + +void ShapeBatcher::drawArc(const Vec2& center, float radius, + float startAngle, float endAngle, + const Color& color, int segments, float width) { + if (!initialized_ || !inBatch_ || radius <= 0.0f) { + return; + } + + int segs = segments > 0 ? segments : static_cast(config_.circleSegments); + float angleRange = endAngle - startAngle; + float angleStep = angleRange / static_cast(segs); + + for (int i = 0; i < segs; ++i) { + float a0 = startAngle + angleStep * static_cast(i); + float a1 = startAngle + angleStep * static_cast(i + 1); + + Vec2 p0(center.x + radius * std::cos(a0), center.y + radius * std::sin(a0)); + Vec2 p1(center.x + radius * std::cos(a1), center.y + radius * std::sin(a1)); + + drawLine(p0, p1, color, width); + } +} + +void ShapeBatcher::draw(const RenderCommand& command) { + if (command.type != RenderCommandType::Shape) { + return; + } + + const auto& data = std::get(command.data); + + switch (data.shapeType) { + case ShapeType::Line: + if (data.points.size() >= 2) { + drawLine(data.points[0], data.points[1], data.color, data.lineWidth); + } + break; + case ShapeType::Rect: + if (data.points.size() >= 2) { + Rect rect(data.points[0].x, data.points[0].y, + data.points[1].x - data.points[0].x, + data.points[1].y - data.points[0].y); + drawRect(rect, data.color, data.lineWidth); + } + break; + case ShapeType::FilledRect: + if (data.points.size() >= 2) { + Rect rect(data.points[0].x, data.points[0].y, + data.points[1].x - data.points[0].x, + data.points[1].y - data.points[0].y); + drawFilledRect(rect, data.color); + } + break; + case ShapeType::Circle: + if (data.points.size() >= 1) { + drawCircle(data.points[0], data.radius, data.color, data.segments, data.lineWidth); + } + break; + case ShapeType::FilledCircle: + if (data.points.size() >= 1) { + drawFilledCircle(data.points[0], data.radius, data.color, data.segments); + } + break; + case ShapeType::Triangle: + if (data.points.size() >= 3) { + drawTriangle(data.points[0], data.points[1], data.points[2], + data.color, data.lineWidth); + } + break; + case ShapeType::FilledTriangle: + if (data.points.size() >= 3) { + drawFilledTriangle(data.points[0], data.points[1], data.points[2], data.color); + } + break; + case ShapeType::Polygon: + drawPolygon(data.points, data.color, data.lineWidth); + break; + case ShapeType::FilledPolygon: + drawFilledPolygon(data.points, data.color); + break; + } +} + +void ShapeBatcher::setViewProjection(const glm::mat4& viewProj) { + viewProj_ = viewProj; +} + +void ShapeBatcher::resetStats() { + drawCallCount_ = 0; + shapeCount_ = 0; + batchCount_ = 0; +} + +void ShapeBatcher::setConfig(const ShapeBatcherConfig& config) { + config_ = config; +} + +void ShapeBatcher::setShader(Ptr shader) { + shader_ = shader; +} + +bool ShapeBatcher::createShader() { + ShaderManager& shaderManager = ShaderManager::getInstance(); + + auto shaderHandle = shaderManager.getBuiltin("shape"); + if (!shaderHandle) { + return false; + } + + shader_ = shaderHandle->shader; + return shader_ != nullptr && shader_->isValid(); +} + +bool ShapeBatcher::createPipeline() { + if (!shader_) { + return false; + } + + rhi::PipelineBuilder builder; + builder.setShader(shader_) + .setBlendState(rhi::BlendState::alphaBlend()) + .setPrimitiveType(rhi::PrimitiveType::Triangles) + .addVertexAttribute(rhi::VertexAttribute(0, rhi::Format::RG32_FLOAT, 0, 0)) + .addVertexAttribute(rhi::VertexAttribute(1, rhi::Format::RGBA32_FLOAT, 8, 0)) + .addVertexBufferBinding(rhi::VertexBufferBinding(0, sizeof(ShapeVertex), 0)); + + pipeline_ = device_->createPipeline(builder); + if (!pipeline_) { + return false; + } + + rhi::PipelineBuilder lineBuilder; + lineBuilder.setShader(shader_) + .setBlendState(rhi::BlendState::alphaBlend()) + .setPrimitiveType(rhi::PrimitiveType::Lines) + .addVertexAttribute(rhi::VertexAttribute(0, rhi::Format::RG32_FLOAT, 0, 0)) + .addVertexAttribute(rhi::VertexAttribute(1, rhi::Format::RGBA32_FLOAT, 8, 0)) + .addVertexBufferBinding(rhi::VertexBufferBinding(0, sizeof(ShapeVertex), 0)); + + linePipeline_ = device_->createPipeline(lineBuilder); + return linePipeline_ != nullptr; +} + +bool ShapeBatcher::createBuffers() { + size_t vertexBufferSize = config_.maxBatchSize * 4 * sizeof(ShapeVertex); + size_t indexBufferSize = config_.maxBatchSize * 6 * sizeof(uint32_t); + + vertexBuffer_ = device_->createVertexBuffer(vertexBufferSize, rhi::BufferUsage::Dynamic); + if (!vertexBuffer_) { + return false; + } + + indexBuffer_ = device_->createIndexBuffer(indexBufferSize, rhi::BufferUsage::Dynamic); + if (!indexBuffer_) { + return false; + } + + uniformBuffer_ = device_->createUniformBuffer(sizeof(glm::mat4)); + if (!uniformBuffer_) { + return false; + } + + return true; +} + +void ShapeBatcher::flushBatch(rhi::PrimitiveType primitiveType, float lineWidth) { + if (vertices_.empty()) { + return; + } + + vertexBuffer_->setData(vertices_.data(), vertices_.size() * sizeof(ShapeVertex)); + uniformBuffer_->setValue(viewProj_); + + Ptr activePipeline = (primitiveType == rhi::PrimitiveType::Lines) + ? linePipeline_ : pipeline_; + + device_->setPipeline(activePipeline); + device_->setShader(shader_); + device_->setVertexBuffer(0, vertexBuffer_); + device_->setUniformBuffer(0, uniformBuffer_); + device_->setUniformMat4("uViewProj", viewProj_); + + if (!indices_.empty() && primitiveType == rhi::PrimitiveType::Triangles) { + indexBuffer_->setData(indices_.data(), indices_.size() * sizeof(uint32_t)); + device_->setIndexBuffer(indexBuffer_, rhi::IndexFormat::UInt32); + device_->drawIndexed(static_cast(indices_.size())); + } else { + device_->draw(static_cast(vertices_.size())); + } + + drawCallCount_++; + batchCount_++; + + vertices_.clear(); + indices_.clear(); + currentVertexIndex_ = 0; + currentIndexIndex_ = 0; +} + +uint32_t ShapeBatcher::addVertex(const Vec2& position, const Color& color) { + vertices_.push_back(ShapeVertex(position, color)); + return currentVertexIndex_++; +} + +void ShapeBatcher::addIndex(uint32_t index) { + indices_.push_back(index); + currentIndexIndex_++; +} + +void ShapeBatcher::generateCircleVertices(const Vec2& center, float radius, + int segments, std::vector& vertices) const { + vertices.clear(); + vertices.reserve(segments); + + float angleStep = 2.0f * 3.14159265358979323846f / static_cast(segments); + + for (int i = 0; i < segments; ++i) { + float angle = angleStep * static_cast(i); + float x = center.x + radius * std::cos(angle); + float y = center.y + radius * std::sin(angle); + vertices.push_back(Vec2(x, y)); + } +} + +void ShapeBatcher::generateEllipseVertices(const Vec2& center, float radiusX, float radiusY, + int segments, std::vector& vertices) const { + vertices.clear(); + vertices.reserve(segments); + + float angleStep = 2.0f * 3.14159265358979323846f / static_cast(segments); + + for (int i = 0; i < segments; ++i) { + float angle = angleStep * static_cast(i); + float x = center.x + radiusX * std::cos(angle); + float y = center.y + radiusY * std::sin(angle); + vertices.push_back(Vec2(x, y)); + } +} + +} // namespace extra2d diff --git a/Extra2D/src/render/batch/sprite_batcher.cpp b/Extra2D/src/render/batch/sprite_batcher.cpp new file mode 100644 index 0000000..157c19b --- /dev/null +++ b/Extra2D/src/render/batch/sprite_batcher.cpp @@ -0,0 +1,410 @@ +#include +#include +#include +#include + +namespace extra2d { + +SpriteBatcher::SpriteBatcher() + : device_(nullptr), shader_(nullptr), pipeline_(nullptr), + vertexBuffer_(nullptr), indexBuffer_(nullptr), uniformBuffer_(nullptr), + viewProj_(1.0f), currentTransform_(1.0f), + currentTexture_(nullptr), currentBlend_(rhi::BlendState::alphaBlend()), + currentVertexIndex_(0), currentIndexIndex_(0), + drawCallCount_(0), spriteCount_(0), batchCount_(0), + initialized_(false), inBatch_(false) { +} + +SpriteBatcher::SpriteBatcher(const SpriteBatcherConfig& config) + : SpriteBatcher() { + config_ = config; +} + +SpriteBatcher::~SpriteBatcher() { + shutdown(); +} + +bool SpriteBatcher::init(Ptr device) { + if (initialized_) { + return true; + } + + if (!device) { + return false; + } + + device_ = device; + + if (!createShader()) { + return false; + } + + if (!createPipeline()) { + return false; + } + + if (!createBuffers()) { + return false; + } + + viewProj_ = glm::ortho(0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f); + + initialized_ = true; + return true; +} + +void SpriteBatcher::shutdown() { + if (!initialized_) { + return; + } + + vertices_.clear(); + indices_.clear(); + batches_.clear(); + + uniformBuffer_.reset(); + indexBuffer_.reset(); + vertexBuffer_.reset(); + pipeline_.reset(); + shader_.reset(); + device_.reset(); + + initialized_ = false; + inBatch_ = false; +} + +void SpriteBatcher::begin() { + begin(viewProj_); +} + +void SpriteBatcher::begin(const glm::mat4& viewProj) { + if (!initialized_ || inBatch_) { + return; + } + + viewProj_ = viewProj; + currentVertexIndex_ = 0; + currentIndexIndex_ = 0; + currentTexture_ = nullptr; + currentBlend_ = rhi::BlendState::alphaBlend(); + currentTransform_ = glm::mat4(1.0f); + + vertices_.clear(); + indices_.clear(); + batches_.clear(); + + vertices_.reserve(config_.maxBatchSize * VERTEX_PER_SPRITE); + indices_.reserve(config_.maxBatchSize * INDEX_PER_SPRITE); + + inBatch_ = true; +} + +void SpriteBatcher::end() { + if (!initialized_ || !inBatch_) { + return; + } + + flushBatch(); + + inBatch_ = false; +} + +void SpriteBatcher::draw(Ptr texture, + const Rect& destRect, + const Rect& srcRect, + const Color& color, + float rotation, + const Vec2& anchor, + const rhi::BlendState& blend) { + if (!initialized_ || !inBatch_ || !texture) { + return; + } + + if (!canAddToBatch(texture, blend)) { + flushBatch(); + } + + if (currentTexture_ == nullptr) { + currentTexture_ = texture; + currentBlend_ = blend; + } + + addSpriteVertices(destRect, srcRect, color, rotation, anchor); + spriteCount_++; +} + +void SpriteBatcher::draw(Ptr texture, + const Rect& srcRect, + const glm::mat4& transform, + const Color& color, + const rhi::BlendState& blend) { + if (!initialized_ || !inBatch_ || !texture) { + return; + } + + if (!canAddToBatch(texture, blend)) { + flushBatch(); + } + + if (currentTexture_ == nullptr) { + currentTexture_ = texture; + currentBlend_ = blend; + } + + uint32_t texWidth = texture->getWidth(); + uint32_t texHeight = texture->getHeight(); + + float u0 = srcRect.origin.x / static_cast(texWidth); + float v0 = srcRect.origin.y / static_cast(texHeight); + float u1 = (srcRect.origin.x + srcRect.size.width) / static_cast(texWidth); + float v1 = (srcRect.origin.y + srcRect.size.height) / static_cast(texHeight); + + float w = srcRect.size.width; + float h = srcRect.size.height; + + glm::vec4 corners[4] = { + transform * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f), + transform * glm::vec4(w, 0.0f, 0.0f, 1.0f), + transform * glm::vec4(w, h, 0.0f, 1.0f), + transform * glm::vec4(0.0f, h, 0.0f, 1.0f) + }; + + uint32_t baseIndex = currentVertexIndex_; + + vertices_.push_back(SpriteVertex(Vec2(corners[0].x, corners[0].y), Vec2(u0, v0), color)); + vertices_.push_back(SpriteVertex(Vec2(corners[1].x, corners[1].y), Vec2(u1, v0), color)); + vertices_.push_back(SpriteVertex(Vec2(corners[2].x, corners[2].y), Vec2(u1, v1), color)); + vertices_.push_back(SpriteVertex(Vec2(corners[3].x, corners[3].y), Vec2(u0, v1), color)); + + indices_.push_back(baseIndex); + indices_.push_back(baseIndex + 1); + indices_.push_back(baseIndex + 2); + indices_.push_back(baseIndex); + indices_.push_back(baseIndex + 2); + indices_.push_back(baseIndex + 3); + + currentVertexIndex_ += 4; + currentIndexIndex_ += 6; + spriteCount_++; +} + +void SpriteBatcher::draw(const RenderCommand& command) { + if (command.type != RenderCommandType::Sprite) { + return; + } + + const auto& data = std::get(command.data); + + glm::mat4 transform = command.transform; + if (data.rotation != 0.0f) { + float centerX = data.destRect.origin.x + data.destRect.size.width * data.anchor.x; + float centerY = data.destRect.origin.y + data.destRect.size.height * data.anchor.y; + glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), + glm::radians(data.rotation), + glm::vec3(0.0f, 0.0f, 1.0f)); + glm::mat4 translation = glm::translate(glm::mat4(1.0f), + glm::vec3(centerX, centerY, 0.0f)); + glm::mat4 inverseTranslation = glm::translate(glm::mat4(1.0f), + glm::vec3(-centerX, -centerY, 0.0f)); + transform = transform * translation * rotation * inverseTranslation; + } + + draw(data.texture, data.srcRect, transform, data.tint, data.blend); +} + +void SpriteBatcher::drawBatch(const std::vector& sprites) { + for (const auto& sprite : sprites) { + draw(sprite.texture, sprite.destRect, sprite.srcRect, + sprite.tint, sprite.rotation, sprite.anchor, sprite.blend); + } +} + +void SpriteBatcher::setViewProjection(const glm::mat4& viewProj) { + viewProj_ = viewProj; +} + +void SpriteBatcher::resetStats() { + drawCallCount_ = 0; + spriteCount_ = 0; + batchCount_ = 0; +} + +void SpriteBatcher::setConfig(const SpriteBatcherConfig& config) { + config_ = config; +} + +void SpriteBatcher::setShader(Ptr shader) { + shader_ = shader; +} + +bool SpriteBatcher::createShader() { + ShaderManager& shaderManager = ShaderManager::getInstance(); + + auto shaderHandle = shaderManager.getBuiltin("sprite"); + if (!shaderHandle) { + return false; + } + + shader_ = shaderHandle->shader; + return shader_ != nullptr && shader_->isValid(); +} + +bool SpriteBatcher::createPipeline() { + if (!shader_) { + return false; + } + + rhi::PipelineBuilder builder; + builder.setShader(shader_) + .setBlendState(rhi::BlendState::alphaBlend()) + .setPrimitiveType(rhi::PrimitiveType::Triangles) + .addVertexAttribute(rhi::VertexAttribute(0, rhi::Format::RG32_FLOAT, 0, 0)) + .addVertexAttribute(rhi::VertexAttribute(1, rhi::Format::RG32_FLOAT, 8, 0)) + .addVertexAttribute(rhi::VertexAttribute(2, rhi::Format::RGBA32_FLOAT, 16, 0)) + .addVertexBufferBinding(rhi::VertexBufferBinding(0, sizeof(SpriteVertex), 0)); + + pipeline_ = device_->createPipeline(builder); + return pipeline_ != nullptr; +} + +bool SpriteBatcher::createBuffers() { + size_t vertexBufferSize = config_.maxBatchSize * VERTEX_PER_SPRITE * sizeof(SpriteVertex); + size_t indexBufferSize = config_.maxBatchSize * INDEX_PER_SPRITE * sizeof(uint32_t); + + vertexBuffer_ = device_->createVertexBuffer(vertexBufferSize, rhi::BufferUsage::Dynamic); + if (!vertexBuffer_) { + return false; + } + + indexBuffer_ = device_->createIndexBuffer(indexBufferSize, rhi::BufferUsage::Dynamic); + if (!indexBuffer_) { + return false; + } + + uniformBuffer_ = device_->createUniformBuffer(sizeof(glm::mat4)); + if (!uniformBuffer_) { + return false; + } + + return true; +} + +void SpriteBatcher::flushBatch() { + if (vertices_.empty() || indices_.empty() || !currentTexture_) { + return; + } + + vertexBuffer_->setData(vertices_.data(), vertices_.size() * sizeof(SpriteVertex)); + indexBuffer_->setData(indices_.data(), indices_.size() * sizeof(uint32_t)); + uniformBuffer_->setValue(viewProj_); + + device_->setPipeline(pipeline_); + device_->setShader(shader_); + device_->setVertexBuffer(0, vertexBuffer_); + device_->setIndexBuffer(indexBuffer_, rhi::IndexFormat::UInt32); + device_->setTexture(0, currentTexture_); + device_->setUniformBuffer(0, uniformBuffer_); + device_->setUniformMat4("uViewProj", viewProj_); + + device_->drawIndexed(static_cast(indices_.size())); + + drawCallCount_++; + batchCount_++; + + vertices_.clear(); + indices_.clear(); + currentVertexIndex_ = 0; + currentIndexIndex_ = 0; + currentTexture_ = nullptr; +} + +bool SpriteBatcher::canAddToBatch(Ptr texture, const rhi::BlendState& blend) const { + if (currentTexture_ == nullptr) { + return true; + } + + if (currentTexture_ != texture) { + return false; + } + + bool blendEqual = (currentBlend_.enabled == blend.enabled && + currentBlend_.srcColorFactor == blend.srcColorFactor && + currentBlend_.dstColorFactor == blend.dstColorFactor && + currentBlend_.colorOp == blend.colorOp); + + if (!blendEqual) { + return false; + } + + uint32_t currentSpriteCount = currentVertexIndex_ / VERTEX_PER_SPRITE; + return currentSpriteCount < config_.maxBatchSize; +} + +void SpriteBatcher::addSpriteVertices(const Rect& destRect, + const Rect& srcRect, + const Color& color, + float rotation, + const Vec2& anchor) { + uint32_t texWidth = currentTexture_->getWidth(); + uint32_t texHeight = currentTexture_->getHeight(); + + float u0 = srcRect.origin.x / static_cast(texWidth); + float v0 = srcRect.origin.y / static_cast(texHeight); + float u1 = (srcRect.origin.x + srcRect.size.width) / static_cast(texWidth); + float v1 = (srcRect.origin.y + srcRect.size.height) / static_cast(texHeight); + + float x = destRect.origin.x; + float y = destRect.origin.y; + float w = destRect.size.width; + float h = destRect.size.height; + + Vec2 corners[4] = { + Vec2(x, y), + Vec2(x + w, y), + Vec2(x + w, y + h), + Vec2(x, y + h) + }; + + if (rotation != 0.0f) { + Vec2 center(x + w * anchor.x, y + h * anchor.y); + for (int i = 0; i < 4; ++i) { + rotatePoint(corners[i], rotation, center); + } + } + + uint32_t baseIndex = currentVertexIndex_; + + vertices_.push_back(SpriteVertex(corners[0], Vec2(u0, v0), color)); + vertices_.push_back(SpriteVertex(corners[1], Vec2(u1, v0), color)); + vertices_.push_back(SpriteVertex(corners[2], Vec2(u1, v1), color)); + vertices_.push_back(SpriteVertex(corners[3], Vec2(u0, v1), color)); + + indices_.push_back(baseIndex); + indices_.push_back(baseIndex + 1); + indices_.push_back(baseIndex + 2); + indices_.push_back(baseIndex); + indices_.push_back(baseIndex + 2); + indices_.push_back(baseIndex + 3); + + currentVertexIndex_ += 4; + currentIndexIndex_ += 6; +} + +Vec2 SpriteBatcher::calcTexCoord(const Vec2& pos, uint32_t texWidth, uint32_t texHeight) const { + return Vec2(pos.x / static_cast(texWidth), + pos.y / static_cast(texHeight)); +} + +void SpriteBatcher::rotatePoint(Vec2& point, float rotation, const Vec2& center) const { + float rad = glm::radians(rotation); + float cosR = std::cos(rad); + float sinR = std::sin(rad); + + float dx = point.x - center.x; + float dy = point.y - center.y; + + point.x = center.x + dx * cosR - dy * sinR; + point.y = center.y + dx * sinR + dy * cosR; +} + +} // namespace extra2d diff --git a/Extra2D/src/render/batch/text_batcher.cpp b/Extra2D/src/render/batch/text_batcher.cpp new file mode 100644 index 0000000..3d877e4 --- /dev/null +++ b/Extra2D/src/render/batch/text_batcher.cpp @@ -0,0 +1,464 @@ +#include +#include +#include +#include + +namespace extra2d { + +TextBatcher::TextBatcher() + : device_(nullptr), shader_(nullptr), pipeline_(nullptr), + vertexBuffer_(nullptr), indexBuffer_(nullptr), uniformBuffer_(nullptr), + viewProj_(1.0f), currentTexture_(nullptr), + currentBlend_(rhi::BlendState::alphaBlend()), + currentVertexIndex_(0), currentIndexIndex_(0), + drawCallCount_(0), glyphCount_(0), batchCount_(0), + initialized_(false), inBatch_(false) { +} + +TextBatcher::TextBatcher(const TextBatcherConfig& config) + : TextBatcher() { + config_ = config; +} + +TextBatcher::~TextBatcher() { + shutdown(); +} + +bool TextBatcher::init(Ptr device) { + if (initialized_) { + return true; + } + + if (!device) { + return false; + } + + device_ = device; + + if (!createShader()) { + return false; + } + + if (!createPipeline()) { + return false; + } + + if (!createBuffers()) { + return false; + } + + viewProj_ = glm::ortho(0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f); + + initialized_ = true; + return true; +} + +void TextBatcher::shutdown() { + if (!initialized_) { + return; + } + + vertices_.clear(); + indices_.clear(); + batches_.clear(); + + uniformBuffer_.reset(); + indexBuffer_.reset(); + vertexBuffer_.reset(); + pipeline_.reset(); + shader_.reset(); + device_.reset(); + + initialized_ = false; + inBatch_ = false; +} + +void TextBatcher::begin() { + begin(viewProj_); +} + +void TextBatcher::begin(const glm::mat4& viewProj) { + if (!initialized_ || inBatch_) { + return; + } + + viewProj_ = viewProj; + currentVertexIndex_ = 0; + currentIndexIndex_ = 0; + currentTexture_ = nullptr; + currentBlend_ = rhi::BlendState::alphaBlend(); + + vertices_.clear(); + indices_.clear(); + batches_.clear(); + + vertices_.reserve(config_.maxBatchSize * VERTEX_PER_GLYPH); + indices_.reserve(config_.maxBatchSize * INDEX_PER_GLYPH); + + inBatch_ = true; +} + +void TextBatcher::end() { + if (!initialized_ || !inBatch_) { + return; + } + + flushBatch(); + + inBatch_ = false; +} + +void TextBatcher::drawText(const std::u32string& text, + const Vec2& position, + FontData* font, + int fontSize, + const TextDrawOptions& options) { + if (!initialized_ || !inBatch_ || !font || text.empty()) { + return; + } + + Ptr atlas = getFontAtlas(font, fontSize); + if (!atlas) { + return; + } + + if (!canAddToBatch(atlas->getTexture(), options.blend)) { + flushBatch(); + } + + if (currentTexture_ == nullptr) { + currentTexture_ = atlas->getTexture(); + currentBlend_ = options.blend; + } + + float x = position.x; + float y = position.y; + + float textWidth = measureTextWidth(text, font, fontSize, options.scale); + + switch (options.alignment) { + case TextAlignment::Center: + x -= textWidth * 0.5f; + break; + case TextAlignment::Right: + x -= textWidth; + break; + default: + break; + } + + float lineHeight = options.lineHeight > 0.0f + ? options.lineHeight + : atlas->getLineHeight() * options.scale; + + switch (options.verticalAlignment) { + case TextVerticalAlignment::Middle: + y -= lineHeight * 0.5f; + break; + case TextVerticalAlignment::Bottom: + y -= lineHeight; + break; + default: + break; + } + + float baseY = y + atlas->getAscent() * options.scale; + + for (char32_t c : text) { + if (c == '\n') { + x = position.x; + baseY += lineHeight; + continue; + } + + if (c == '\r') { + continue; + } + + if (c == ' ') { + const GlyphInfo* spaceGlyph = atlas->getGlyph(' '); + if (spaceGlyph) { + x += spaceGlyph->advance * options.scale; + } else { + x += fontSize * 0.25f * options.scale; + } + continue; + } + + const GlyphInfo* glyph = atlas->getGlyph(static_cast(c)); + if (!glyph) { + continue; + } + + Vec2 glyphPos(x + glyph->bearingX * options.scale, + baseY - glyph->bearingY * options.scale); + + addGlyphVertices(glyphPos, glyph, options.color, options.scale); + + x += glyph->advance * options.scale; + glyphCount_++; + } +} + +void TextBatcher::drawTextUTF8(const std::string& text, + const Vec2& position, + FontData* font, + int fontSize, + const TextDrawOptions& options) { + std::u32string utf32Text = UnicodeUtils::utf8ToUtf32(text); + drawText(utf32Text, position, font, fontSize, options); +} + +void TextBatcher::draw(const RenderCommand& command) { + if (command.type != RenderCommandType::Text) { + return; + } + + const auto& data = std::get(command.data); + + TextDrawOptions options; + options.color = data.color; + options.scale = data.fontSize / 16.0f; + options.blend = data.blend; + + FontManager& fontManager = FontManager::getInstance(); + auto fontHandle = fontManager.get(data.fontName); + + if (fontHandle) { + drawText(data.text, data.position, fontHandle.get(), static_cast(data.fontSize), options); + } +} + +Size TextBatcher::measureText(const std::u32string& text, + FontData* font, + int fontSize, + float scale) { + if (!font || text.empty()) { + return Size(); + } + + Ptr atlas = getFontAtlas(font, fontSize); + if (!atlas) { + return Size(); + } + + float width = 0.0f; + float maxWidth = 0.0f; + float height = atlas->getLineHeight() * scale; + float maxAscent = atlas->getAscent() * scale; + float maxDescent = atlas->getDescent() * scale; + + for (char32_t c : text) { + if (c == '\n') { + maxWidth = std::max(maxWidth, width); + width = 0.0f; + height += atlas->getLineHeight() * scale; + continue; + } + + if (c == '\r') { + continue; + } + + const GlyphInfo* glyph = atlas->getGlyph(static_cast(c)); + if (glyph) { + width += glyph->advance * scale; + maxAscent = std::max(maxAscent, glyph->bearingY * scale); + maxDescent = std::max(maxDescent, (glyph->height - glyph->bearingY) * scale); + } + } + + maxWidth = std::max(maxWidth, width); + + return Size(maxWidth, height); +} + +float TextBatcher::measureTextWidth(const std::u32string& text, + FontData* font, + int fontSize, + float scale) { + if (!font || text.empty()) { + return 0.0f; + } + + Ptr atlas = getFontAtlas(font, fontSize); + if (!atlas) { + return 0.0f; + } + + float width = 0.0f; + + for (char32_t c : text) { + if (c == '\n' || c == '\r') { + continue; + } + + const GlyphInfo* glyph = atlas->getGlyph(static_cast(c)); + if (glyph) { + width += glyph->advance * scale; + } + } + + return width; +} + +void TextBatcher::setViewProjection(const glm::mat4& viewProj) { + viewProj_ = viewProj; +} + +void TextBatcher::resetStats() { + drawCallCount_ = 0; + glyphCount_ = 0; + batchCount_ = 0; +} + +void TextBatcher::setConfig(const TextBatcherConfig& config) { + config_ = config; +} + +void TextBatcher::setShader(Ptr shader) { + shader_ = shader; +} + +bool TextBatcher::createShader() { + ShaderManager& shaderManager = ShaderManager::getInstance(); + + auto shaderHandle = shaderManager.getBuiltin("sdf_font"); + if (!shaderHandle) { + return false; + } + + shader_ = shaderHandle->shader; + return shader_ != nullptr && shader_->isValid(); +} + +bool TextBatcher::createPipeline() { + if (!shader_) { + return false; + } + + rhi::PipelineBuilder builder; + builder.setShader(shader_) + .setBlendState(rhi::BlendState::alphaBlend()) + .setPrimitiveType(rhi::PrimitiveType::Triangles) + .addVertexAttribute(rhi::VertexAttribute(0, rhi::Format::RG32_FLOAT, 0, 0)) + .addVertexAttribute(rhi::VertexAttribute(1, rhi::Format::RG32_FLOAT, 8, 0)) + .addVertexAttribute(rhi::VertexAttribute(2, rhi::Format::RGBA32_FLOAT, 16, 0)) + .addVertexBufferBinding(rhi::VertexBufferBinding(0, sizeof(TextVertex), 0)); + + pipeline_ = device_->createPipeline(builder); + return pipeline_ != nullptr; +} + +bool TextBatcher::createBuffers() { + size_t vertexBufferSize = config_.maxBatchSize * VERTEX_PER_GLYPH * sizeof(TextVertex); + size_t indexBufferSize = config_.maxBatchSize * INDEX_PER_GLYPH * sizeof(uint32_t); + + vertexBuffer_ = device_->createVertexBuffer(vertexBufferSize, rhi::BufferUsage::Dynamic); + if (!vertexBuffer_) { + return false; + } + + indexBuffer_ = device_->createIndexBuffer(indexBufferSize, rhi::BufferUsage::Dynamic); + if (!indexBuffer_) { + return false; + } + + uniformBuffer_ = device_->createUniformBuffer(sizeof(glm::mat4) + sizeof(int) + sizeof(float)); + if (!uniformBuffer_) { + return false; + } + + return true; +} + +void TextBatcher::flushBatch() { + if (vertices_.empty() || indices_.empty() || !currentTexture_) { + return; + } + + vertexBuffer_->setData(vertices_.data(), vertices_.size() * sizeof(TextVertex)); + indexBuffer_->setData(indices_.data(), indices_.size() * sizeof(uint32_t)); + uniformBuffer_->setValue(viewProj_); + + device_->setPipeline(pipeline_); + device_->setShader(shader_); + device_->setVertexBuffer(0, vertexBuffer_); + device_->setIndexBuffer(indexBuffer_, rhi::IndexFormat::UInt32); + device_->setTexture(0, currentTexture_); + device_->setUniformBuffer(0, uniformBuffer_); + device_->setUniformMat4("uViewProj", viewProj_); + device_->setUniformInt("uUseSDF", config_.enableSDF ? 1 : 0); + device_->setUniformFloat("uSDFEdge", 0.5f); + + device_->drawIndexed(static_cast(indices_.size())); + + drawCallCount_++; + batchCount_++; + + vertices_.clear(); + indices_.clear(); + currentVertexIndex_ = 0; + currentIndexIndex_ = 0; + currentTexture_ = nullptr; +} + +bool TextBatcher::canAddToBatch(Ptr texture, const rhi::BlendState& blend) const { + if (currentTexture_ == nullptr) { + return true; + } + + if (currentTexture_ != texture) { + return false; + } + + uint32_t currentGlyphCount = currentVertexIndex_ / VERTEX_PER_GLYPH; + return currentGlyphCount < config_.maxBatchSize; +} + +void TextBatcher::addGlyphVertices(const Vec2& position, + const GlyphInfo* glyph, + const Color& color, + float scale) { + if (!glyph) { + return; + } + + float x = position.x; + float y = position.y; + float w = glyph->width * scale; + float h = glyph->height * scale; + + float u0 = glyph->u0; + float v0 = glyph->v0; + float u1 = glyph->u1; + float v1 = glyph->v1; + + uint32_t baseIndex = currentVertexIndex_; + + vertices_.push_back(TextVertex(Vec2(x, y), Vec2(u0, v0), color)); + vertices_.push_back(TextVertex(Vec2(x + w, y), Vec2(u1, v0), color)); + vertices_.push_back(TextVertex(Vec2(x + w, y + h), Vec2(u1, v1), color)); + vertices_.push_back(TextVertex(Vec2(x, y + h), Vec2(u0, v1), color)); + + indices_.push_back(baseIndex); + indices_.push_back(baseIndex + 1); + indices_.push_back(baseIndex + 2); + indices_.push_back(baseIndex); + indices_.push_back(baseIndex + 2); + indices_.push_back(baseIndex + 3); + + currentVertexIndex_ += 4; + currentIndexIndex_ += 6; +} + +Ptr TextBatcher::getFontAtlas(FontData* font, int fontSize) { + if (!font) { + return nullptr; + } + + FontManager& fontManager = FontManager::getInstance(); + return fontManager.getAtlas(font, fontSize); +} + +} // namespace extra2d diff --git a/Extra2D/src/render/core/render_command.cpp b/Extra2D/src/render/core/render_command.cpp new file mode 100644 index 0000000..dd3e448 --- /dev/null +++ b/Extra2D/src/render/core/render_command.cpp @@ -0,0 +1,241 @@ +#include + +namespace extra2d { + +RenderCommand RenderCommand::makeSprite(Ptr texture, + const Rect& destRect, + const Rect& srcRect, + const Color& tint, + float rotation, + const Vec2& anchor, + const rhi::BlendState& blend, + int layer, + int zOrder) { + RenderCommand cmd; + cmd.type = RenderCommandType::Sprite; + cmd.layer = layer; + cmd.zOrder = zOrder; + cmd.transform = glm::mat4(1.0f); + + SpriteRenderData data; + data.texture = texture; + data.destRect = destRect; + data.srcRect = srcRect; + data.tint = tint; + data.rotation = rotation; + data.anchor = anchor; + data.blend = blend; + data.sortKey = reinterpret_cast(texture.get()); + + cmd.data = std::move(data); + return cmd; +} + +RenderCommand RenderCommand::makeText(const std::u32string& text, + const Vec2& position, + const Color& color, + float fontSize, + const std::string& fontName, + int layer, + int zOrder) { + RenderCommand cmd; + cmd.type = RenderCommandType::Text; + cmd.layer = layer; + cmd.zOrder = zOrder; + cmd.transform = glm::mat4(1.0f); + + TextRenderData data; + data.text = text; + data.position = position; + data.color = color; + data.fontSize = fontSize; + data.fontName = fontName; + + cmd.data = std::move(data); + return cmd; +} + +RenderCommand RenderCommand::makeLine(const Vec2& start, + const Vec2& end, + const Color& color, + float width, + int layer, + int zOrder) { + RenderCommand cmd; + cmd.type = RenderCommandType::Shape; + cmd.layer = layer; + cmd.zOrder = zOrder; + cmd.transform = glm::mat4(1.0f); + + ShapeRenderData data; + data.shapeType = ShapeType::Line; + data.points = {start, end}; + data.color = color; + data.lineWidth = width; + + cmd.data = std::move(data); + return cmd; +} + +RenderCommand RenderCommand::makeRect(const Rect& rect, + const Color& color, + float width, + bool filled, + int layer, + int zOrder) { + RenderCommand cmd; + cmd.type = RenderCommandType::Shape; + cmd.layer = layer; + cmd.zOrder = zOrder; + cmd.transform = glm::mat4(1.0f); + + ShapeRenderData data; + data.shapeType = filled ? ShapeType::FilledRect : ShapeType::Rect; + data.points = { + rect.origin, + Vec2(rect.right(), rect.top()), + Vec2(rect.right(), rect.bottom()), + Vec2(rect.left(), rect.bottom()) + }; + data.color = color; + data.lineWidth = width; + + cmd.data = std::move(data); + return cmd; +} + +RenderCommand RenderCommand::makeCircle(const Vec2& center, + float radius, + const Color& color, + int segments, + bool filled, + int layer, + int zOrder) { + RenderCommand cmd; + cmd.type = RenderCommandType::Shape; + cmd.layer = layer; + cmd.zOrder = zOrder; + cmd.transform = glm::mat4(1.0f); + + ShapeRenderData data; + data.shapeType = filled ? ShapeType::FilledCircle : ShapeType::Circle; + data.points = {center}; + data.color = color; + data.radius = radius; + data.segments = segments; + + cmd.data = std::move(data); + return cmd; +} + +RenderCommand RenderCommand::makeTriangle(const Vec2& p1, + const Vec2& p2, + const Vec2& p3, + const Color& color, + bool filled, + int layer, + int zOrder) { + RenderCommand cmd; + cmd.type = RenderCommandType::Shape; + cmd.layer = layer; + cmd.zOrder = zOrder; + cmd.transform = glm::mat4(1.0f); + + ShapeRenderData data; + data.shapeType = filled ? ShapeType::FilledTriangle : ShapeType::Triangle; + data.points = {p1, p2, p3}; + data.color = color; + + cmd.data = std::move(data); + return cmd; +} + +RenderCommand RenderCommand::makePolygon(const std::vector& points, + const Color& color, + bool filled, + int layer, + int zOrder) { + RenderCommand cmd; + cmd.type = RenderCommandType::Shape; + cmd.layer = layer; + cmd.zOrder = zOrder; + cmd.transform = glm::mat4(1.0f); + + ShapeRenderData data; + data.shapeType = filled ? ShapeType::FilledPolygon : ShapeType::Polygon; + data.points = points; + data.color = color; + + cmd.data = std::move(data); + return cmd; +} + +RenderCommand RenderCommand::makeCustom(uint32_t callbackId, + void* userData, + const rhi::BlendState& blend, + int layer, + int zOrder) { + RenderCommand cmd; + cmd.type = RenderCommandType::Custom; + cmd.layer = layer; + cmd.zOrder = zOrder; + cmd.transform = glm::mat4(1.0f); + + CustomRenderData data; + data.callbackId = callbackId; + data.userData = userData; + data.blend = blend; + + cmd.data = std::move(data); + return cmd; +} + +uint64_t RenderCommand::calculateSortKey() const { + uint64_t key = 0; + + key = (static_cast(layer & 0xFFFF) << 48); + + switch (type) { + case RenderCommandType::Sprite: { + const auto& sprite = std::get(data); + key |= (sprite.sortKey & 0x0000FFFFFFFFFFFF); + break; + } + case RenderCommandType::Text: { + key |= (static_cast(RenderCommandType::Text) << 40); + break; + } + case RenderCommandType::Shape: { + key |= (static_cast(RenderCommandType::Shape) << 40); + break; + } + default: + break; + } + + key |= (static_cast(zOrder & 0xFFFF) << 16); + + return key; +} + +bool RenderCommandComparator::operator()(const RenderCommand& a, const RenderCommand& b) const { + if (a.layer != b.layer) { + return a.layer < b.layer; + } + + if (a.type != b.type) { + return static_cast(a.type) < static_cast(b.type); + } + + if (a.type == RenderCommandType::Sprite) { + const auto& spriteA = std::get(a.data); + const auto& spriteB = std::get(b.data); + if (spriteA.texture != spriteB.texture) { + return spriteA.texture < spriteB.texture; + } + } + + return a.zOrder < b.zOrder; +} + +} // namespace extra2d diff --git a/Extra2D/src/render/core/render_context.cpp b/Extra2D/src/render/core/render_context.cpp new file mode 100644 index 0000000..ca4bdb2 --- /dev/null +++ b/Extra2D/src/render/core/render_context.cpp @@ -0,0 +1,285 @@ +#include +#include + +namespace extra2d { + +RenderContext::RenderContext() + : device_(nullptr), + window_(nullptr), + queue_(), + stats_(), + config_(), + currentBlend_(rhi::BlendState::alphaBlend()), + viewport_(), + scissorRect_(), + scissorEnabled_(false), + fbWidth_(0), + fbHeight_(0), + initialized_(false) { + initDefaultState(); +} + +RenderContext::RenderContext(const RenderContextConfig& config) + : device_(nullptr), + window_(nullptr), + queue_(), + stats_(), + config_(config), + currentBlend_(rhi::BlendState::alphaBlend()), + viewport_(), + scissorRect_(), + scissorEnabled_(false), + fbWidth_(0), + fbHeight_(0), + initialized_(false) { + initDefaultState(); +} + +RenderContext::~RenderContext() { + shutdown(); +} + +bool RenderContext::init(IWindow* window) { + return init(window, config_.preferredAPI); +} + +bool RenderContext::init(IWindow* window, rhi::GraphicsAPI api) { + if (initialized_) { + return true; + } + + if (!window) { + return false; + } + + window_ = window; + + device_ = rhi::RHIDevice::create(api); + if (!device_) { + return false; + } + + if (!device_->init(window)) { + device_.reset(); + return false; + } + + fbWidth_ = static_cast(window->width()); + fbHeight_ = static_cast(window->height()); + + viewport_ = rhi::Viewport(0.0f, 0.0f, static_cast(fbWidth_), static_cast(fbHeight_)); + + initialized_ = true; + return true; +} + +void RenderContext::shutdown() { + if (!initialized_) { + return; + } + + queue_.clear(); + transformStack_ = std::stack(); + blendStack_ = std::stack(); + + if (device_) { + device_->shutdown(); + device_.reset(); + } + + window_ = nullptr; + initialized_ = false; +} + +bool RenderContext::isValid() const { + return initialized_ && device_ && device_->isValid(); +} + +void RenderContext::beginFrame() { + if (!isValid()) { + return; + } + + stats_.reset(); + device_->beginFrame(); + device_->resetStats(); + + device_->setViewport(viewport_); + if (scissorEnabled_) { + device_->setScissorRect(scissorRect_); + } + device_->setScissorEnabled(scissorEnabled_); +} + +void RenderContext::endFrame() { + if (!isValid()) { + return; + } + + flushQueue(); + device_->endFrame(); +} + +void RenderContext::present() { + if (!isValid()) { + return; + } + + device_->present(); +} + +void RenderContext::clear(const Color& color) { + if (!isValid()) { + return; + } + + rhi::ColorValue cv(color.r, color.g, color.b, color.a); + device_->clearColor(cv); +} + +void RenderContext::clear(const Color& color, float depth, uint8_t stencil) { + if (!isValid()) { + return; + } + + rhi::ColorValue cv(color.r, color.g, color.b, color.a); + device_->clear(cv, depth, stencil); +} + +void RenderContext::setViewport(const rhi::Viewport& viewport) { + viewport_ = viewport; + if (isValid()) { + device_->setViewport(viewport); + } +} + +void RenderContext::setViewport(float x, float y, float width, float height) { + setViewport(rhi::Viewport(x, y, width, height)); +} + +rhi::Viewport RenderContext::getViewport() const { + return viewport_; +} + +void RenderContext::setScissorRect(const rhi::ScissorRect& rect) { + scissorRect_ = rect; + if (isValid() && scissorEnabled_) { + device_->setScissorRect(rect); + } +} + +void RenderContext::setScissorRect(int32_t x, int32_t y, uint32_t width, uint32_t height) { + setScissorRect(rhi::ScissorRect(x, y, width, height)); +} + +void RenderContext::setScissorEnabled(bool enabled) { + scissorEnabled_ = enabled; + if (isValid()) { + device_->setScissorEnabled(enabled); + } +} + +rhi::ScissorRect RenderContext::getScissorRect() const { + return scissorRect_; +} + +void RenderContext::pushTransform(const glm::mat4& transform) { + if (transformStack_.empty()) { + transformStack_.push(transform); + } else { + transformStack_.push(transformStack_.top() * transform); + } +} + +void RenderContext::popTransform() { + if (!transformStack_.empty()) { + transformStack_.pop(); + } +} + +void RenderContext::resetTransform() { + while (!transformStack_.empty()) { + transformStack_.pop(); + } +} + +glm::mat4 RenderContext::getCurrentTransform() const { + if (transformStack_.empty()) { + return glm::mat4(1.0f); + } + return transformStack_.top(); +} + +void RenderContext::setBlendState(const rhi::BlendState& blend) { + currentBlend_ = blend; +} + +rhi::BlendState RenderContext::getBlendState() const { + return currentBlend_; +} + +void RenderContext::pushBlendState(const rhi::BlendState& blend) { + blendStack_.push(currentBlend_); + currentBlend_ = blend; +} + +void RenderContext::popBlendState() { + if (!blendStack_.empty()) { + currentBlend_ = blendStack_.top(); + blendStack_.pop(); + } +} + +void RenderContext::clearQueue() { + queue_.clear(); +} + +void RenderContext::flushQueue() { + if (!isValid() || queue_.empty()) { + return; + } + + queue_.sort(config_.enableBatching ? RenderSortMode::ByTexture : RenderSortMode::None); + + const auto& commands = queue_.getCommands(); + const RenderQueueStats& queueStats = queue_.getStats(); + + stats_.spriteCount = queueStats.spriteCommands; + stats_.textCount = queueStats.textCommands; + stats_.shapeCount = queueStats.shapeCommands; + + stats_.batchCount = queueStats.batchCount; + stats_.drawCalls = queueStats.drawCalls; + + queue_.clear(); +} + +void RenderContext::setConfig(const RenderContextConfig& config) { + config_ = config; +} + +void RenderContext::setVSync(bool enabled) { + if (isValid()) { + device_->setVSync(enabled); + } +} + +bool RenderContext::isVSyncEnabled() const { + return isValid() ? device_->isVSyncEnabled() : false; +} + +void RenderContext::setDefaultFramebufferSize(uint32_t width, uint32_t height) { + fbWidth_ = width; + fbHeight_ = height; + if (isValid()) { + device_->setDefaultFramebufferSize(width, height); + } +} + +void RenderContext::initDefaultState() { + transformStack_.push(glm::mat4(1.0f)); + currentBlend_ = rhi::BlendState::alphaBlend(); + scissorEnabled_ = false; +} + +} // namespace extra2d diff --git a/Extra2D/src/render/core/render_queue.cpp b/Extra2D/src/render/core/render_queue.cpp new file mode 100644 index 0000000..d2fc6f7 --- /dev/null +++ b/Extra2D/src/render/core/render_queue.cpp @@ -0,0 +1,207 @@ +#include + +namespace extra2d { + +RenderQueue::RenderQueue() + : commands_(), stats_(), sortMode_(RenderSortMode::ByTexture) { + commands_.reserve(DEFAULT_CAPACITY); +} + +RenderQueue::RenderQueue(size_t initialCapacity) + : commands_(), stats_(), sortMode_(RenderSortMode::ByTexture) { + commands_.reserve(std::min(initialCapacity, MAX_CAPACITY)); +} + +RenderQueue::~RenderQueue() = default; + +void RenderQueue::addCommand(const RenderCommand& cmd) { + if (commands_.size() >= MAX_CAPACITY) { + return; + } + commands_.push_back(cmd); + stats_.totalCommands++; + updateStatsForType(cmd.type); +} + +void RenderQueue::addCommand(RenderCommand&& cmd) { + if (commands_.size() >= MAX_CAPACITY) { + return; + } + updateStatsForType(cmd.type); + stats_.totalCommands++; + commands_.push_back(std::move(cmd)); +} + +void RenderQueue::addCommands(const std::vector& commands) { + size_t available = MAX_CAPACITY - commands_.size(); + size_t toAdd = std::min(commands.size(), available); + + for (size_t i = 0; i < toAdd; ++i) { + commands_.push_back(commands[i]); + stats_.totalCommands++; + updateStatsForType(commands[i].type); + } +} + +void RenderQueue::sort(RenderSortMode mode) { + switch (mode) { + case RenderSortMode::BackToFront: + sortBackToFront(); + break; + case RenderSortMode::FrontToBack: + sortFrontToBack(); + break; + case RenderSortMode::ByTexture: + sortByTexture(); + break; + case RenderSortMode::ByLayer: + sortByLayer(); + break; + case RenderSortMode::None: + default: + break; + } +} + +void RenderQueue::sortCustom(CustomSortFunc sortFunc) { + std::sort(commands_.begin(), commands_.end(), sortFunc); +} + +void RenderQueue::clear() { + commands_.clear(); + stats_.reset(); +} + +void RenderQueue::reserve(size_t capacity) { + commands_.reserve(std::min(capacity, MAX_CAPACITY)); +} + +std::vector RenderQueue::getCommandsByType(RenderCommandType type) const { + std::vector result; + for (const auto& cmd : commands_) { + if (cmd.type == type) { + result.push_back(cmd); + } + } + return result; +} + +std::vector RenderQueue::getCommandsByLayer(int layer) const { + std::vector result; + for (const auto& cmd : commands_) { + if (cmd.layer == layer) { + result.push_back(cmd); + } + } + return result; +} + +void RenderQueue::merge(const RenderQueue& other) { + size_t available = MAX_CAPACITY - commands_.size(); + size_t toAdd = std::min(other.commands_.size(), available); + + for (size_t i = 0; i < toAdd; ++i) { + commands_.push_back(other.commands_[i]); + stats_.totalCommands++; + updateStatsForType(other.commands_[i].type); + } +} + +void RenderQueue::merge(RenderQueue&& other) { + size_t available = MAX_CAPACITY - commands_.size(); + size_t toAdd = std::min(other.commands_.size(), available); + + for (size_t i = 0; i < toAdd; ++i) { + commands_.push_back(std::move(other.commands_[i])); + stats_.totalCommands++; + updateStatsForType(commands_.back().type); + } + other.clear(); +} + +void RenderQueue::updateStatsForType(RenderCommandType type) { + switch (type) { + case RenderCommandType::Sprite: + stats_.spriteCommands++; + break; + case RenderCommandType::Text: + stats_.textCommands++; + break; + case RenderCommandType::Shape: + stats_.shapeCommands++; + break; + case RenderCommandType::Custom: + stats_.customCommands++; + break; + default: + break; + } +} + +void RenderQueue::recalculateStats() { + stats_.reset(); + for (const auto& cmd : commands_) { + stats_.totalCommands++; + updateStatsForType(cmd.type); + } +} + +void RenderQueue::sortBackToFront() { + std::sort(commands_.begin(), commands_.end(), + [](const RenderCommand& a, const RenderCommand& b) { + if (a.layer != b.layer) { + return a.layer > b.layer; + } + return a.zOrder > b.zOrder; + }); +} + +void RenderQueue::sortFrontToBack() { + std::sort(commands_.begin(), commands_.end(), + [](const RenderCommand& a, const RenderCommand& b) { + if (a.layer != b.layer) { + return a.layer < b.layer; + } + return a.zOrder < b.zOrder; + }); +} + +void RenderQueue::sortByTexture() { + std::sort(commands_.begin(), commands_.end(), + [](const RenderCommand& a, const RenderCommand& b) { + if (a.layer != b.layer) { + return a.layer < b.layer; + } + + if (a.type != b.type) { + return static_cast(a.type) < static_cast(b.type); + } + + if (a.type == RenderCommandType::Sprite) { + const auto& spriteA = std::get(a.data); + const auto& spriteB = std::get(b.data); + if (spriteA.texture != spriteB.texture) { + return spriteA.texture < spriteB.texture; + } + } + + return a.zOrder < b.zOrder; + }); +} + +void RenderQueue::sortByLayer() { + std::sort(commands_.begin(), commands_.end(), + [](const RenderCommand& a, const RenderCommand& b) { + if (a.layer != b.layer) { + return a.layer < b.layer; + } + + if (a.type != b.type) { + return static_cast(a.type) < static_cast(b.type); + } + + return a.zOrder < b.zOrder; + }); +} + +} // namespace extra2d diff --git a/Extra2D/src/render/core/render_stats.cpp b/Extra2D/src/render/core/render_stats.cpp new file mode 100644 index 0000000..de128e2 --- /dev/null +++ b/Extra2D/src/render/core/render_stats.cpp @@ -0,0 +1,89 @@ +#include + +namespace extra2d { + +RenderStatsCollector::RenderStatsCollector() + : stats_(), + frameStartTime_(), + renderStartTime_(), + frameTimeAccum_(0.0f), + frameCount_(0), + avgFrameTime_(0.0f), + fps_(0.0f) { +} + +void RenderStatsCollector::beginFrame() { + frameStartTime_ = Clock::now(); +} + +void RenderStatsCollector::endFrame() { + auto now = Clock::now(); + auto duration = std::chrono::duration_cast(now - frameStartTime_); + float frameTime = duration.count() / 1000.0f; + + stats_.frameTimeMs = frameTime; + + frameTimeAccum_ += frameTime; + frameCount_++; + + if (frameCount_ >= 60) { + avgFrameTime_ = frameTimeAccum_ / frameCount_; + fps_ = 1000.0f / avgFrameTime_; + frameTimeAccum_ = 0.0f; + frameCount_ = 0; + } + + stats_.fps = fps_; +} + +void RenderStatsCollector::beginRender() { + renderStartTime_ = Clock::now(); +} + +void RenderStatsCollector::endRender() { + auto now = Clock::now(); + auto duration = std::chrono::duration_cast(now - renderStartTime_); + stats_.renderTimeMs = duration.count() / 1000.0f; +} + +void RenderStatsCollector::recordDrawCall(uint32_t vertexCount, uint32_t triangleCount) { + stats_.drawCalls++; + stats_.vertexCount += vertexCount; + stats_.triangleCount += triangleCount; +} + +void RenderStatsCollector::recordBatch() { + stats_.batchCount++; +} + +void RenderStatsCollector::recordSprite(uint32_t count) { + stats_.spriteCount += count; +} + +void RenderStatsCollector::recordText(uint32_t count) { + stats_.textCount += count; +} + +void RenderStatsCollector::recordShape(uint32_t count) { + stats_.shapeCount += count; +} + +void RenderStatsCollector::recordTextureBind() { + stats_.textureBinds++; +} + +void RenderStatsCollector::recordShaderBind() { + stats_.shaderBinds++; +} + +void RenderStatsCollector::recordBufferBind() { + stats_.bufferBinds++; +} + +void RenderStatsCollector::reset() { + stats_.reset(); + frameTimeAccum_ = 0.0f; + frameCount_ = 0; +} + +} // namespace extra2d diff --git a/Extra2D/src/resources/font_atlas.cpp b/Extra2D/src/resources/font_atlas.cpp new file mode 100644 index 0000000..277727d --- /dev/null +++ b/Extra2D/src/resources/font_atlas.cpp @@ -0,0 +1,385 @@ +#include + +#include + +#include + +#define STB_TRUETYPE_IMPLEMENTATION +#include + +namespace extra2d { + +FontAtlas::FontAtlas() = default; + +FontAtlas::~FontAtlas() { + shutdown(); +} + +bool FontAtlas::init(Ptr device, const FontAtlasDesc& desc) { + if (valid_) { + return true; + } + + if (!device) { + E2D_ERROR("FontAtlas: device is null"); + return false; + } + + device_ = device; + atlasWidth_ = desc.atlasWidth; + atlasHeight_ = desc.atlasHeight; + fontSize_ = desc.fontSize; + padding_ = desc.padding; + useSDF_ = desc.useSDF; + sdfEdgeValue_ = desc.sdfEdgeValue; + sdfPixelDistScale_ = desc.sdfPixelDistScale; + + if (!desc.fontData.empty()) { + fontData_ = desc.fontData; + } else if (!desc.filepath.empty()) { + std::ifstream file(desc.filepath, std::ios::binary); + if (!file.is_open()) { + E2D_ERROR("FontAtlas: Failed to open font file: {}", desc.filepath); + return false; + } + fontData_ = std::vector((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + } else { + E2D_ERROR("FontAtlas: No font data provided"); + return false; + } + + stbFontInfo_ = new stbtt_fontinfo(); + stbtt_fontinfo* fontInfo = static_cast(stbFontInfo_); + + if (!stbtt_InitFont(fontInfo, fontData_.data(), 0)) { + E2D_ERROR("FontAtlas: Failed to initialize font"); + delete static_cast(stbFontInfo_); + stbFontInfo_ = nullptr; + return false; + } + + float scale = stbtt_ScaleForPixelHeight(fontInfo, static_cast(fontSize_)); + + int ascent, descent, lineGap; + stbtt_GetFontVMetrics(fontInfo, &ascent, &descent, &lineGap); + ascent_ = static_cast(ascent) * scale; + descent_ = static_cast(descent) * scale; + lineHeight_ = static_cast(ascent - descent + lineGap) * scale; + + atlasData_.resize(static_cast(atlasWidth_) * atlasHeight_, 0); + + if (!initPackContext()) { + E2D_ERROR("FontAtlas: Failed to initialize pack context"); + delete static_cast(stbFontInfo_); + stbFontInfo_ = nullptr; + return false; + } + + rhi::TextureDesc texDesc; + texDesc.type = rhi::TextureType::Texture2D; + texDesc.format = rhi::Format::R8_UNORM; + texDesc.width = static_cast(atlasWidth_); + texDesc.height = static_cast(atlasHeight_); + texDesc.data = atlasData_.data(); + texDesc.debugName = "FontAtlas"; + + texture_ = device_->createTexture(texDesc); + if (!texture_ || !texture_->isValid()) { + E2D_ERROR("FontAtlas: Failed to create texture"); + shutdown(); + return false; + } + + rhi::SamplerDesc samplerDesc; + samplerDesc.minFilter = useSDF_ ? rhi::FilterMode::Linear : rhi::FilterMode::Linear; + samplerDesc.magFilter = rhi::FilterMode::Linear; + samplerDesc.addressU = rhi::AddressMode::ClampToEdge; + samplerDesc.addressV = rhi::AddressMode::ClampToEdge; + + sampler_ = device_->createSampler(samplerDesc); + + valid_ = true; + E2D_DEBUG("FontAtlas: Initialized ({}x{}, fontSize={}, SDF={})", + atlasWidth_, atlasHeight_, fontSize_, useSDF_); + + return true; +} + +void FontAtlas::shutdown() { + if (!valid_) { + return; + } + + glyphs_.clear(); + atlasData_.clear(); + fontData_.clear(); + + if (stbPackContext_) { + delete static_cast(stbPackContext_); + stbPackContext_ = nullptr; + } + + if (stbFontInfo_) { + delete static_cast(stbFontInfo_); + stbFontInfo_ = nullptr; + } + + packNodes_.clear(); + texture_.reset(); + sampler_.reset(); + device_.reset(); + + valid_ = false; +} + +const GlyphInfo* FontAtlas::getGlyph(uint32_t codepoint) { + if (!valid_) { + return nullptr; + } + + { + std::lock_guard lock(mutex_); + auto it = glyphs_.find(codepoint); + if (it != glyphs_.end()) { + return &it->second; + } + } + + if (!cacheGlyph(codepoint)) { + return nullptr; + } + + std::lock_guard lock(mutex_); + auto it = glyphs_.find(codepoint); + return it != glyphs_.end() ? &it->second : nullptr; +} + +int FontAtlas::preloadChars(const std::u32string& chars) { + if (!valid_) { + return 0; + } + + int count = 0; + for (char32_t c : chars) { + if (cacheGlyph(static_cast(c))) { + count++; + } + } + return count; +} + +int FontAtlas::preloadCharsUTF8(const std::string& chars) { + std::u32string utf32; + utf32.reserve(chars.size()); + + const uint8_t* ptr = reinterpret_cast(chars.data()); + const uint8_t* end = ptr + chars.size(); + + while (ptr < end) { + uint32_t codepoint = 0; + if ((*ptr & 0x80) == 0) { + codepoint = *ptr++; + } else if ((*ptr & 0xE0) == 0xC0) { + codepoint = (*ptr++ & 0x1F) << 6; + if (ptr < end) codepoint |= (*ptr++ & 0x3F); + } else if ((*ptr & 0xF0) == 0xE0) { + codepoint = (*ptr++ & 0x0F) << 12; + if (ptr < end) codepoint |= (*ptr++ & 0x3F) << 6; + if (ptr < end) codepoint |= (*ptr++ & 0x3F); + } else if ((*ptr & 0xF8) == 0xF0) { + codepoint = (*ptr++ & 0x07) << 18; + if (ptr < end) codepoint |= (*ptr++ & 0x3F) << 12; + if (ptr < end) codepoint |= (*ptr++ & 0x3F) << 6; + if (ptr < end) codepoint |= (*ptr++ & 0x3F); + } else { + ptr++; + } + if (codepoint > 0) { + utf32.push_back(static_cast(codepoint)); + } + } + + return preloadChars(utf32); +} + +float FontAtlas::measureText(const std::u32string& text, float scale) { + if (!valid_ || text.empty()) { + return 0; + } + + float width = 0; + for (char32_t c : text) { + const GlyphInfo* glyph = getGlyph(static_cast(c)); + if (glyph) { + width += glyph->advance * scale; + } + } + return width; +} + +Size FontAtlas::measureTextSize(const std::u32string& text, float scale) { + if (!valid_ || text.empty()) { + return Size{0, 0}; + } + + float width = 0; + float maxAscent = 0; + float maxDescent = 0; + + for (char32_t c : text) { + const GlyphInfo* glyph = getGlyph(static_cast(c)); + if (glyph) { + width += glyph->advance * scale; + maxAscent = std::max(maxAscent, glyph->bearingY * scale); + maxDescent = std::max(maxDescent, (glyph->height - glyph->bearingY) * scale); + } + } + + return Size{width, maxAscent + maxDescent}; +} + +float FontAtlas::getUsageRatio() const { + if (atlasWidth_ == 0 || atlasHeight_ == 0) { + return 0; + } + return static_cast(usedArea_) / (atlasWidth_ * atlasHeight_); +} + +bool FontAtlas::cacheGlyph(uint32_t codepoint) { + if (!valid_) { + return false; + } + + stbtt_fontinfo* fontInfo = static_cast(stbFontInfo_); + float scale = stbtt_ScaleForPixelHeight(fontInfo, static_cast(fontSize_)); + + int glyphIndex = stbtt_FindGlyphIndex(fontInfo, static_cast(codepoint)); + if (glyphIndex == 0) { + return false; + } + + int advanceWidth, leftSideBearing; + stbtt_GetGlyphHMetrics(fontInfo, glyphIndex, &advanceWidth, &leftSideBearing); + + int width, height, xoff, yoff; + uint8_t* bitmap = nullptr; + + if (useSDF_) { + bitmap = stbtt_GetGlyphSDF(fontInfo, scale, glyphIndex, + static_cast(sdfPixelDistScale_), + static_cast(sdfEdgeValue_ * 255), + static_cast(sdfEdgeValue_ * 255), + &width, &height, &xoff, &yoff); + } else { + bitmap = stbtt_GetGlyphBitmap(fontInfo, scale, scale, glyphIndex, + &width, &height, &xoff, &yoff); + } + + if (!bitmap || width <= 0 || height <= 0) { + if (bitmap) stbtt_FreeSDF(bitmap, nullptr); + return false; + } + + int paddedWidth = width + padding_ * 2; + int paddedHeight = height + padding_ * 2; + + int packX = 0, packY = 0; + if (!allocateRect(paddedWidth, paddedHeight, packX, packY)) { + stbtt_FreeSDF(bitmap, nullptr); + E2D_WARN("FontAtlas: No space for glyph U+{:04X}", codepoint); + return false; + } + + int destX = packX + padding_; + int destY = packY + padding_; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int srcIdx = y * width + x; + int dstIdx = (destY + y) * atlasWidth_ + (destX + x); + if (dstIdx < static_cast(atlasData_.size())) { + atlasData_[dstIdx] = bitmap[srcIdx]; + } + } + } + + updateTextureRegion(packX, packY, paddedWidth, paddedHeight, + &atlasData_[packY * atlasWidth_ + packX]); + + stbtt_FreeSDF(bitmap, nullptr); + + GlyphInfo glyph; + glyph.codepoint = codepoint; + glyph.width = static_cast(width); + glyph.height = static_cast(height); + glyph.bearingX = static_cast(xoff); + glyph.bearingY = static_cast(-yoff); + glyph.advance = static_cast(advanceWidth) * scale; + glyph.u0 = static_cast(destX) / atlasWidth_; + glyph.v0 = static_cast(destY) / atlasHeight_; + glyph.u1 = static_cast(destX + width) / atlasWidth_; + glyph.v1 = static_cast(destY + height) / atlasHeight_; + glyph.isSDF = useSDF_; + + { + std::lock_guard lock(mutex_); + glyphs_[codepoint] = glyph; + } + + usedArea_ += paddedWidth * paddedHeight; + + return true; +} + +void FontAtlas::updateTextureRegion(int x, int y, int width, int height, const uint8_t* data) { + if (!texture_) { + return; + } + + std::vector regionData(static_cast(width) * height); + for (int row = 0; row < height; row++) { + std::copy(data + row * atlasWidth_, + data + row * atlasWidth_ + width, + regionData.begin() + row * width); + } + + texture_->setSubData(regionData.data(), static_cast(x), static_cast(y), + static_cast(width), static_cast(height)); +} + +bool FontAtlas::initPackContext() { + stbrp_context* ctx = new stbrp_context(); + int numNodes = atlasWidth_ / 2; + packNodes_.resize(sizeof(stbrp_node) * numNodes); + + stbrp_init_target(ctx, atlasWidth_, atlasHeight_, + reinterpret_cast(packNodes_.data()), numNodes); + + stbPackContext_ = ctx; + return true; +} + +bool FontAtlas::allocateRect(int width, int height, int& outX, int& outY) { + if (!stbPackContext_) { + return false; + } + + stbrp_context* ctx = static_cast(stbPackContext_); + stbrp_rect rect; + rect.id = 0; + rect.w = width; + rect.h = height; + + stbrp_pack_rects(ctx, &rect, 1); + + if (rect.was_packed) { + outX = rect.x; + outY = rect.y; + return true; + } + + return false; +} + +} // namespace extra2d diff --git a/Extra2D/src/resources/font_manager.cpp b/Extra2D/src/resources/font_manager.cpp new file mode 100644 index 0000000..7586753 --- /dev/null +++ b/Extra2D/src/resources/font_manager.cpp @@ -0,0 +1,269 @@ +#include + +#include + +#include + +namespace extra2d { + +FontManager& FontManager::getInstance() { + static FontManager instance; + return instance; +} + +bool FontManager::init(Ptr device) { + if (initialized_) { + return true; + } + + if (!device) { + E2D_ERROR("FontManager: device is null"); + return false; + } + + device_ = device; + initialized_ = true; + + E2D_INFO("FontManager initialized"); + return true; +} + +void FontManager::shutdown() { + if (!initialized_) { + return; + } + + clear(); + device_.reset(); + initialized_ = false; + + E2D_INFO("FontManager shutdown"); +} + +ResourceHandle FontManager::load(const std::string& path, + const FontLoadOptions& options) { + if (!initialized_) { + E2D_ERROR("FontManager not initialized"); + return ResourceHandle(); + } + + auto cached = getFromCache(path); + if (cached) { + return ResourceHandle(cached.get(), this); + } + + std::ifstream file(path, std::ios::binary); + if (!file.is_open()) { + E2D_ERROR("FontManager: Failed to open font file: {}", path); + return ResourceHandle(); + } + + std::vector data((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + + return loadFromMemory(path, data, options); +} + +ResourceHandle FontManager::loadFromMemory(const std::string& name, + const std::vector& data, + const FontLoadOptions& options) { + if (!initialized_) { + E2D_ERROR("FontManager not initialized"); + return ResourceHandle(); + } + + auto cached = getFromCache(name); + if (cached) { + return ResourceHandle(cached.get(), this); + } + + if (data.empty()) { + E2D_ERROR("FontManager: Empty font data: {}", name); + return ResourceHandle(); + } + + auto fontData = std::make_shared(); + fontData->name = name; + fontData->path = name; + fontData->fontFileData = data; + fontData->defaultFontSize = options.fontSize; + fontData->useSDF = options.useSDF; + fontData->memorySize = data.size(); + + FontAtlasDesc atlasDesc; + atlasDesc.fontData = data; + atlasDesc.fontSize = options.fontSize; + atlasDesc.useSDF = options.useSDF; + atlasDesc.atlasWidth = options.atlasWidth; + atlasDesc.atlasHeight = options.atlasHeight; + atlasDesc.padding = options.padding; + atlasDesc.sdfEdgeValue = options.sdfEdgeValue; + atlasDesc.sdfPixelDistScale = options.sdfPixelDistScale; + + auto atlas = createAtlas(fontData.get(), options.fontSize); + if (!atlas) { + E2D_ERROR("FontManager: Failed to create font atlas: {}", name); + return ResourceHandle(); + } + + if (!options.preloadChars.empty()) { + atlas->preloadChars(options.preloadChars); + } + + fontData->atlasBySize[options.fontSize] = atlas; + fontData->memorySize += options.atlasWidth * options.atlasHeight; + + cache(name, fontData, fontData->memorySize); + + E2D_DEBUG("FontManager: Loaded font '{}' ({} bytes, size={})", + name, fontData->memorySize, options.fontSize); + + return ResourceHandle(fontData.get(), this); +} + +ResourceHandle FontManager::get(const std::string& path) { + auto cached = getFromCache(path); + if (cached) { + return ResourceHandle(cached.get(), this); + } + return ResourceHandle(); +} + +bool FontManager::has(const std::string& path) const { + return hasInCache(path); +} + +void FontManager::remove(const std::string& path) { + std::lock_guard lock(mutex_); + auto it = pathIndex_.find(path); + if (it != pathIndex_.end()) { + auto cacheIt = cache_.find(it->second); + if (cacheIt != cache_.end()) { + totalMemory_ -= cacheIt->second.memorySize; + cache_.erase(cacheIt); + } + pathIndex_.erase(it); + } +} + +void FontManager::clear() { + BaseResourceManager::clearAll(); +} + +Ptr FontManager::getAtlas(Ptr font, int fontSize) { + if (!font || !initialized_) { + return nullptr; + } + + auto it = font->atlasBySize.find(fontSize); + if (it != font->atlasBySize.end()) { + return it->second; + } + + return createAtlas(font.get(), fontSize); +} + +Ptr FontManager::getAtlas(FontData* font, int fontSize) { + if (!font || !initialized_) { + return nullptr; + } + + auto it = font->atlasBySize.find(fontSize); + if (it != font->atlasBySize.end()) { + return it->second; + } + + return createAtlas(font, fontSize); +} + +const GlyphInfo* FontManager::getGlyph(Ptr font, int fontSize, uint32_t codepoint) { + auto atlas = getAtlas(font, fontSize); + if (!atlas) { + return nullptr; + } + + return atlas->getGlyph(codepoint); +} + +const GlyphInfo* FontManager::getGlyph(FontData* font, int fontSize, uint32_t codepoint) { + auto atlas = getAtlas(font, fontSize); + if (!atlas) { + return nullptr; + } + + return atlas->getGlyph(codepoint); +} + +float FontManager::measureText(Ptr font, int fontSize, const std::u32string& text) { + auto atlas = getAtlas(font, fontSize); + if (!atlas) { + return 0; + } + + return atlas->measureText(text); +} + +float FontManager::measureText(FontData* font, int fontSize, const std::u32string& text) { + auto atlas = getAtlas(font, fontSize); + if (!atlas) { + return 0; + } + + return atlas->measureText(text); +} + +Size FontManager::measureTextSize(Ptr font, int fontSize, const std::u32string& text) { + auto atlas = getAtlas(font, fontSize); + if (!atlas) { + return Size{0, 0}; + } + + return atlas->measureTextSize(text); +} + +Size FontManager::measureTextSize(FontData* font, int fontSize, const std::u32string& text) { + auto atlas = getAtlas(font, fontSize); + if (!atlas) { + return Size{0, 0}; + } + + return atlas->measureTextSize(text); +} + +size_t FontManager::getFontMemory() const { + return getTotalMemory(); +} + +size_t FontManager::getFontCount() const { + return getCount(); +} + +Ptr FontManager::createAtlas(FontData* fontData, int fontSize) { + if (!fontData || !device_ || fontData->fontFileData.empty()) { + return nullptr; + } + + FontAtlasDesc desc; + desc.fontData = fontData->fontFileData; + desc.fontSize = fontSize; + desc.useSDF = fontData->useSDF; + desc.atlasWidth = defaultOptions_.atlasWidth; + desc.atlasHeight = defaultOptions_.atlasHeight; + desc.padding = defaultOptions_.padding; + desc.sdfEdgeValue = defaultOptions_.sdfEdgeValue; + desc.sdfPixelDistScale = defaultOptions_.sdfPixelDistScale; + + auto atlas = std::make_shared(); + if (!atlas->init(device_, desc)) { + return nullptr; + } + + fontData->atlasBySize[fontSize] = atlas; + fontData->memorySize += desc.atlasWidth * desc.atlasHeight; + + E2D_DEBUG("FontManager: Created atlas for '{}' at size {}", fontData->name, fontSize); + + return atlas; +} + +} // namespace extra2d diff --git a/Extra2D/src/resources/material_manager.cpp b/Extra2D/src/resources/material_manager.cpp new file mode 100644 index 0000000..047e4ca --- /dev/null +++ b/Extra2D/src/resources/material_manager.cpp @@ -0,0 +1,294 @@ +#include + +#include + +#include +#include + +namespace nl = nlohmann; + +namespace extra2d { + +MaterialManager& MaterialManager::getInstance() { + static MaterialManager instance; + return instance; +} + +bool MaterialManager::init(Ptr device) { + if (initialized_) { + return true; + } + + if (!device) { + E2D_ERROR("MaterialManager: device is null"); + return false; + } + + device_ = device; + initialized_ = true; + + E2D_INFO("MaterialManager initialized"); + return true; +} + +void MaterialManager::shutdown() { + if (!initialized_) { + return; + } + + clear(); + device_.reset(); + initialized_ = false; + + E2D_INFO("MaterialManager shutdown"); +} + +ResourceHandle MaterialManager::create(const std::string& name, + const std::string& shaderName, + const MaterialLoadOptions& options) { + if (!initialized_) { + E2D_ERROR("MaterialManager not initialized"); + return ResourceHandle(); + } + + auto cached = getFromCache(name); + if (cached) { + return ResourceHandle(cached.get(), this); + } + + auto materialData = std::make_shared(); + materialData->name = name; + materialData->path = name; + materialData->shaderName = shaderName; + materialData->blendState = options.blendState; + materialData->depthState = options.depthState; + materialData->rasterizerState = options.rasterizerState; + + auto& shaderMgr = ShaderManager::getInstance(); + materialData->shader = shaderMgr.get(shaderName); + if (!materialData->shader) { + E2D_WARN("MaterialManager: Shader '{}' not found for material '{}'", + shaderName, name); + } + + materialData->memorySize = sizeof(MaterialData); + + cache(name, materialData, materialData->memorySize); + + E2D_DEBUG("MaterialManager: Created material '{}'", name); + + return ResourceHandle(materialData.get(), this); +} + +ResourceHandle MaterialManager::load(const std::string& path) { + if (!initialized_) { + E2D_ERROR("MaterialManager not initialized"); + return ResourceHandle(); + } + + auto cached = getFromCache(path); + if (cached) { + return ResourceHandle(cached.get(), this); + } + + std::ifstream file(path); + if (!file.is_open()) { + E2D_ERROR("MaterialManager: Failed to open material file: {}", path); + return ResourceHandle(); + } + + std::string content((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + + auto materialData = std::make_shared(); + materialData->name = path; + materialData->path = path; + + if (!parseFromJson(content, materialData.get())) { + E2D_ERROR("MaterialManager: Failed to parse material: {}", path); + return ResourceHandle(); + } + + materialData->memorySize = sizeof(MaterialData) + + materialData->properties.size() * sizeof(MaterialProperty); + + cache(path, materialData, materialData->memorySize); + + E2D_DEBUG("MaterialManager: Loaded material '{}'", path); + + return ResourceHandle(materialData.get(), this); +} + +ResourceHandle MaterialManager::get(const std::string& name) { + auto cached = getFromCache(name); + if (cached) { + return ResourceHandle(cached.get(), this); + } + return ResourceHandle(); +} + +bool MaterialManager::has(const std::string& name) const { + return hasInCache(name); +} + +void MaterialManager::remove(const std::string& name) { + std::lock_guard lock(mutex_); + auto it = pathIndex_.find(name); + if (it != pathIndex_.end()) { + auto cacheIt = cache_.find(it->second); + if (cacheIt != cache_.end()) { + totalMemory_ -= cacheIt->second.memorySize; + cache_.erase(cacheIt); + } + pathIndex_.erase(it); + } +} + +void MaterialManager::clear() { + BaseResourceManager::clearAll(); +} + +void MaterialManager::setProperty(MaterialData* material, const std::string& name, + const MaterialProperty& value) { + if (!material) { + return; + } + + std::lock_guard lock(mutex_); + material->properties[name] = value; +} + +void MaterialManager::setTexture(MaterialData* material, const std::string& name, + const std::string& texturePath, uint32_t slot) { + if (!material) { + return; + } + + std::lock_guard lock(mutex_); + MaterialProperty prop(texturePath, slot); + material->properties[name] = prop; +} + +void MaterialManager::apply(MaterialData* material, rhi::RHIDevice* device) { + if (!material || !device || !material->shader) { + return; + } + + device->setShader(material->shader->shader); + + auto& textureMgr = TextureManager::getInstance(); + + for (const auto& [name, prop] : material->properties) { + switch (prop.type) { + case MaterialProperty::Type::Float: + device->setUniformFloat(name, prop.floatValue); + break; + case MaterialProperty::Type::Vec2: + device->setUniformVec2(name, glm::vec2(prop.vec2Value[0], prop.vec2Value[1])); + break; + case MaterialProperty::Type::Vec3: + device->setUniformVec3(name, glm::vec3(prop.vec3Value[0], prop.vec3Value[1], prop.vec3Value[2])); + break; + case MaterialProperty::Type::Vec4: + device->setUniformVec4(name, glm::vec4(prop.vec4Value[0], prop.vec4Value[1], + prop.vec4Value[2], prop.vec4Value[3])); + break; + case MaterialProperty::Type::Int: + device->setUniformInt(name, prop.intValue); + break; + case MaterialProperty::Type::Texture: { + auto texHandle = textureMgr.get(prop.texturePath); + if (texHandle && texHandle->texture) { + device->setTexture(prop.textureSlot, texHandle->texture, texHandle->sampler); + device->setUniformInt(name, static_cast(prop.textureSlot)); + } + break; + } + default: + break; + } + } +} + +size_t MaterialManager::getMaterialCount() const { + return getCount(); +} + +bool MaterialManager::parseFromJson(const std::string& jsonContent, MaterialData* material) { + try { + nl::json j = nl::json::parse(jsonContent); + + if (j.contains("shader")) { + material->shaderName = j["shader"].get(); + auto& shaderMgr = ShaderManager::getInstance(); + material->shader = shaderMgr.get(material->shaderName); + } + + if (j.contains("blend")) { + auto& blend = j["blend"]; + if (blend.contains("enabled")) { + material->blendState.enabled = blend["enabled"].get(); + } + if (blend.contains("srcFactor")) { + std::string factor = blend["srcFactor"].get(); + if (factor == "SrcAlpha") material->blendState.srcColorFactor = rhi::BlendFactor::SrcAlpha; + else if (factor == "One") material->blendState.srcColorFactor = rhi::BlendFactor::One; + else if (factor == "Zero") material->blendState.srcColorFactor = rhi::BlendFactor::Zero; + } + if (blend.contains("dstFactor")) { + std::string factor = blend["dstFactor"].get(); + if (factor == "OneMinusSrcAlpha") material->blendState.dstColorFactor = rhi::BlendFactor::OneMinusSrcAlpha; + else if (factor == "One") material->blendState.dstColorFactor = rhi::BlendFactor::One; + else if (factor == "Zero") material->blendState.dstColorFactor = rhi::BlendFactor::Zero; + } + } + + if (j.contains("depth")) { + auto& depth = j["depth"]; + if (depth.contains("enabled")) { + material->depthState.depthTestEnabled = depth["enabled"].get(); + } + if (depth.contains("write")) { + material->depthState.depthWriteEnabled = depth["write"].get(); + } + } + + if (j.contains("properties")) { + for (auto& [key, value] : j["properties"].items()) { + if (value.is_number_float()) { + material->properties[key] = MaterialProperty(value.get()); + } else if (value.is_number_integer()) { + material->properties[key] = MaterialProperty(value.get()); + } else if (value.is_array()) { + auto arr = value.get>(); + if (arr.size() == 2) { + material->properties[key] = MaterialProperty(arr[0], arr[1]); + } else if (arr.size() == 3) { + material->properties[key] = MaterialProperty(arr[0], arr[1], arr[2]); + } else if (arr.size() == 4) { + material->properties[key] = MaterialProperty(arr[0], arr[1], arr[2], arr[3]); + } + } else if (value.is_string()) { + material->properties[key] = MaterialProperty(value.get()); + } + } + } + + if (j.contains("textures")) { + uint32_t slot = 0; + for (auto& [key, value] : j["textures"].items()) { + if (value.is_string()) { + material->properties[key] = MaterialProperty(value.get(), slot++); + } + } + } + + return true; + + } catch (const nl::json::exception& e) { + E2D_ERROR("MaterialManager: JSON parse error: {}", e.what()); + return false; + } +} + +} // namespace extra2d diff --git a/Extra2D/src/resources/mesh_manager.cpp b/Extra2D/src/resources/mesh_manager.cpp new file mode 100644 index 0000000..4b0fec3 --- /dev/null +++ b/Extra2D/src/resources/mesh_manager.cpp @@ -0,0 +1,292 @@ +#include + +#include + +namespace extra2d { + +MeshBuilder& MeshBuilder::addVertex(const Vertex& vertex) { + vertices_.push_back(vertex); + return *this; +} + +MeshBuilder& MeshBuilder::addVertex(const Vec2& pos, const Vec2& texCoord, const Color& color) { + vertices_.emplace_back(pos, texCoord, color); + return *this; +} + +MeshBuilder& MeshBuilder::addIndex(uint32_t index) { + indices_.push_back(index); + return *this; +} + +MeshBuilder& MeshBuilder::addTriangle(uint32_t i0, uint32_t i1, uint32_t i2) { + indices_.push_back(i0); + indices_.push_back(i1); + indices_.push_back(i2); + return *this; +} + +MeshBuilder& MeshBuilder::addQuad(const Vec2& pos, const Vec2& size, + const Vec2& uv0, const Vec2& uv1, const Color& color) { + uint32_t baseIndex = static_cast(vertices_.size()); + + addVertex(Vec2(pos.x, pos.y), Vec2(uv0.x, uv0.y), color); + addVertex(Vec2(pos.x + size.x, pos.y), Vec2(uv1.x, uv0.y), color); + addVertex(Vec2(pos.x + size.x, pos.y + size.y), Vec2(uv1.x, uv1.y), color); + addVertex(Vec2(pos.x, pos.y + size.y), Vec2(uv0.x, uv1.y), color); + + addTriangle(baseIndex, baseIndex + 1, baseIndex + 2); + addTriangle(baseIndex, baseIndex + 2, baseIndex + 3); + + return *this; +} + +MeshManager& MeshManager::getInstance() { + static MeshManager instance; + return instance; +} + +bool MeshManager::init(Ptr device) { + if (initialized_) { + return true; + } + + if (!device) { + E2D_ERROR("MeshManager: device is null"); + return false; + } + + device_ = device; + initialized_ = true; + + E2D_INFO("MeshManager initialized"); + return true; +} + +void MeshManager::shutdown() { + if (!initialized_) { + return; + } + + clear(); + device_.reset(); + initialized_ = false; + + E2D_INFO("MeshManager shutdown"); +} + +ResourceHandle MeshManager::create(const std::string& name, + const std::vector& vertices, + const std::vector& indices, + const MeshLoadOptions& options) { + if (!initialized_) { + E2D_ERROR("MeshManager not initialized"); + return ResourceHandle(); + } + + auto cached = getFromCache(name); + if (cached) { + return ResourceHandle(cached.get(), this); + } + + auto meshData = std::make_shared(); + meshData->name = name; + meshData->path = name; + meshData->vertices = vertices; + meshData->vertexCount = static_cast(vertices.size()); + meshData->indexCount = static_cast(indices.size()); + meshData->primitiveType = options.primitiveType; + meshData->dynamic = (options.usage == rhi::BufferUsage::Dynamic || + options.usage == rhi::BufferUsage::Stream); + + if (options.use32BitIndices || vertices.size() > 65535) { + meshData->indexFormat = rhi::IndexFormat::UInt32; + meshData->indices32.assign(indices.begin(), indices.end()); + } else { + meshData->indexFormat = rhi::IndexFormat::UInt16; + meshData->indices16.reserve(indices.size()); + for (uint32_t idx : indices) { + meshData->indices16.push_back(static_cast(idx)); + } + } + + meshData->vertexBuffer = createVertexBuffer(vertices, options.usage); + if (!meshData->vertexBuffer) { + E2D_ERROR("MeshManager: Failed to create vertex buffer: {}", name); + return ResourceHandle(); + } + + meshData->indexBuffer = createIndexBuffer(indices, + meshData->indexFormat == rhi::IndexFormat::UInt32, options.usage); + if (!meshData->indexBuffer) { + E2D_ERROR("MeshManager: Failed to create index buffer: {}", name); + return ResourceHandle(); + } + + meshData->memorySize = vertices.size() * sizeof(Vertex) + indices.size() * + (meshData->indexFormat == rhi::IndexFormat::UInt32 ? 4 : 2); + + cache(name, meshData, meshData->memorySize); + + E2D_DEBUG("MeshManager: Created mesh '{}' ({} vertices, {} indices, {} bytes)", + name, meshData->vertexCount, meshData->indexCount, meshData->memorySize); + + return ResourceHandle(meshData.get(), this); +} + +ResourceHandle MeshManager::create(const std::string& name, + const MeshBuilder& builder, + const MeshLoadOptions& options) { + return create(name, builder.getVertices(), builder.getIndices(), options); +} + +ResourceHandle MeshManager::createQuad(const std::string& name, + float width, float height) { + MeshBuilder builder; + builder.addQuad(Vec2(0, 0), Vec2(width, height), Vec2(0, 0), Vec2(1, 1)); + return create(name, builder); +} + +ResourceHandle MeshManager::createDynamic(const std::string& name, + uint32_t maxVertices, + uint32_t maxIndices, + const MeshLoadOptions& options) { + if (!initialized_) { + E2D_ERROR("MeshManager not initialized"); + return ResourceHandle(); + } + + auto cached = getFromCache(name); + if (cached) { + return ResourceHandle(cached.get(), this); + } + + auto meshData = std::make_shared(); + meshData->name = name; + meshData->path = name; + meshData->vertices.resize(maxVertices); + meshData->indices32.resize(maxIndices); + meshData->indexFormat = rhi::IndexFormat::UInt32; + meshData->primitiveType = options.primitiveType; + meshData->dynamic = true; + + rhi::BufferDesc vbDesc; + vbDesc.type = rhi::BufferType::Vertex; + vbDesc.usage = rhi::BufferUsage::Dynamic; + vbDesc.size = maxVertices * sizeof(Vertex); + vbDesc.debugName = name + "_VB"; + + meshData->vertexBuffer = device_->createBuffer(vbDesc); + if (!meshData->vertexBuffer) { + E2D_ERROR("MeshManager: Failed to create dynamic vertex buffer: {}", name); + return ResourceHandle(); + } + + rhi::BufferDesc ibDesc; + ibDesc.type = rhi::BufferType::Index; + ibDesc.usage = rhi::BufferUsage::Dynamic; + ibDesc.size = maxIndices * sizeof(uint32_t); + ibDesc.debugName = name + "_IB"; + + meshData->indexBuffer = device_->createBuffer(ibDesc); + if (!meshData->indexBuffer) { + E2D_ERROR("MeshManager: Failed to create dynamic index buffer: {}", name); + return ResourceHandle(); + } + + meshData->memorySize = vbDesc.size + ibDesc.size; + + cache(name, meshData, meshData->memorySize); + + E2D_DEBUG("MeshManager: Created dynamic mesh '{}' (max {} vertices, {} indices)", + name, maxVertices, maxIndices); + + return ResourceHandle(meshData.get(), this); +} + +void MeshManager::updateDynamic(MeshData* mesh, + const Vertex* vertices, uint32_t vertexCount, + const uint32_t* indices, uint32_t indexCount) { + if (!mesh || !mesh->dynamic || !mesh->vertexBuffer || !mesh->indexBuffer) { + return; + } + + if (vertexCount > mesh->vertices.size() || indexCount > mesh->indices32.size()) { + E2D_WARN("MeshManager: Dynamic mesh overflow: {}", mesh->name); + return; + } + + mesh->vertexBuffer->setData(vertices, vertexCount * sizeof(Vertex)); + mesh->indexBuffer->setData(indices, indexCount * sizeof(uint32_t)); + + mesh->vertexCount = vertexCount; + mesh->indexCount = indexCount; +} + +ResourceHandle MeshManager::get(const std::string& name) { + auto cached = getFromCache(name); + if (cached) { + return ResourceHandle(cached.get(), this); + } + return ResourceHandle(); +} + +bool MeshManager::has(const std::string& name) const { + return hasInCache(name); +} + +void MeshManager::remove(const std::string& name) { + std::lock_guard lock(mutex_); + auto it = pathIndex_.find(name); + if (it != pathIndex_.end()) { + auto cacheIt = cache_.find(it->second); + if (cacheIt != cache_.end()) { + totalMemory_ -= cacheIt->second.memorySize; + cache_.erase(cacheIt); + } + pathIndex_.erase(it); + } +} + +void MeshManager::clear() { + BaseResourceManager::clearAll(); +} + +size_t MeshManager::getMeshMemory() const { + return getTotalMemory(); +} + +size_t MeshManager::getMeshCount() const { + return getCount(); +} + +Ptr MeshManager::createVertexBuffer(const std::vector& vertices, + rhi::BufferUsage usage) { + rhi::BufferDesc desc; + desc.type = rhi::BufferType::Vertex; + desc.usage = usage; + desc.size = vertices.size() * sizeof(Vertex); + desc.data = vertices.data(); + + return device_->createBuffer(desc); +} + +Ptr MeshManager::createIndexBuffer(const std::vector& indices, + bool use32Bit, rhi::BufferUsage usage) { + rhi::BufferDesc desc; + desc.type = rhi::BufferType::Index; + desc.usage = usage; + + if (use32Bit) { + desc.size = indices.size() * sizeof(uint32_t); + desc.data = indices.data(); + } else { + std::vector indices16(indices.begin(), indices.end()); + desc.size = indices16.size() * sizeof(uint16_t); + desc.data = indices16.data(); + } + + return device_->createBuffer(desc); +} + +} // namespace extra2d diff --git a/Extra2D/src/resources/shader_manager.cpp b/Extra2D/src/resources/shader_manager.cpp new file mode 100644 index 0000000..67e7e9d --- /dev/null +++ b/Extra2D/src/resources/shader_manager.cpp @@ -0,0 +1,384 @@ +#include + +#include + +#include +#include +#include +#include + +namespace nl = nlohmann; +namespace fs = std::filesystem; + +namespace extra2d { + +ShaderManager& ShaderManager::getInstance() { + static ShaderManager instance; + return instance; +} + +bool ShaderManager::init(Ptr device, const std::string& shaderDir) { + if (initialized_) { + return true; + } + + if (!device) { + E2D_ERROR("ShaderManager: device is null"); + return false; + } + + device_ = device; + shaderDir_ = shaderDir; + initialized_ = true; + + E2D_INFO("ShaderManager initialized with directory: {}", shaderDir_); + return true; +} + +void ShaderManager::shutdown() { + if (!initialized_) { + return; + } + + clear(); + metadataCache_.clear(); + device_.reset(); + initialized_ = false; + + E2D_INFO("ShaderManager shutdown"); +} + +ResourceHandle ShaderManager::loadFromFiles(const std::string& name, + const std::string& vertPath, + const std::string& fragPath, + const ShaderLoadOptions& options) { + if (!initialized_) { + E2D_ERROR("ShaderManager not initialized"); + return ResourceHandle(); + } + + auto cached = getFromCache(name); + if (cached) { + return ResourceHandle(cached.get(), this); + } + + std::string vertSource = readFile(vertPath); + if (vertSource.empty()) { + E2D_ERROR("ShaderManager: Failed to read vertex shader: {}", vertPath); + return ResourceHandle(); + } + + std::string fragSource = readFile(fragPath); + if (fragSource.empty()) { + E2D_ERROR("ShaderManager: Failed to read fragment shader: {}", fragPath); + return ResourceHandle(); + } + + std::vector dependencies; + fs::path baseDir = fs::path(vertPath).parent_path(); + vertSource = processIncludes(vertSource, baseDir.string(), dependencies); + fragSource = processIncludes(fragSource, baseDir.string(), dependencies); + + for (const auto& define : options.defines) { + std::string defineStr = "#define " + define + "\n"; + vertSource = defineStr + vertSource; + fragSource = defineStr + fragSource; + } + + return loadFromSource(name, vertSource, fragSource); +} + +ResourceHandle ShaderManager::loadFromSource(const std::string& name, + const std::string& vertSource, + const std::string& fragSource) { + if (!initialized_) { + E2D_ERROR("ShaderManager not initialized"); + return ResourceHandle(); + } + + auto cached = getFromCache(name); + if (cached) { + return ResourceHandle(cached.get(), this); + } + + rhi::ShaderDesc desc; + desc.name = name; + desc.vertexSource = vertSource; + desc.fragmentSource = fragSource; + + Ptr shader = device_->createShader(desc); + if (!shader || !shader->isValid()) { + E2D_ERROR("ShaderManager: Failed to create shader: {}", name); + return ResourceHandle(); + } + + auto shaderData = std::make_shared(); + shaderData->name = name; + shaderData->path = name; + shaderData->shader = shader; + shaderData->vertexSource = vertSource; + shaderData->fragmentSource = fragSource; + shaderData->memorySize = vertSource.size() + fragSource.size(); + + cache(name, shaderData, shaderData->memorySize); + + E2D_DEBUG("ShaderManager: Loaded shader '{}' ({} bytes)", name, shaderData->memorySize); + + return ResourceHandle(shaderData.get(), this); +} + +ResourceHandle ShaderManager::loadFromMetadata(const std::string& jsonPath, + const std::string& name) { + if (!initialized_) { + E2D_ERROR("ShaderManager not initialized"); + return ResourceHandle(); + } + + auto cached = getFromCache(name); + if (cached) { + return ResourceHandle(cached.get(), this); + } + + std::string jsonContent = readFile(jsonPath); + if (jsonContent.empty()) { + E2D_ERROR("ShaderManager: Failed to read shader metadata: {}", jsonPath); + return ResourceHandle(); + } + + ShaderMetadata metadata; + std::string vertPath, fragPath; + + if (!parseMetadata(jsonContent, metadata, vertPath, fragPath)) { + E2D_ERROR("ShaderManager: Failed to parse shader metadata: {}", jsonPath); + return ResourceHandle(); + } + + metadata.name = name; + metadataCache_[name] = metadata; + + fs::path jsonDir = fs::path(jsonPath).parent_path(); + fs::path fullVertPath = jsonDir / vertPath; + fs::path fullFragPath = jsonDir / fragPath; + + auto handle = loadFromFiles(name, fullVertPath.string(), fullFragPath.string()); + if (handle) { + metadataCache_[name] = metadata; + } + + return handle; +} + +ResourceHandle ShaderManager::get(const std::string& name) { + auto cached = getFromCache(name); + if (cached) { + return ResourceHandle(cached.get(), this); + } + return ResourceHandle(); +} + +bool ShaderManager::has(const std::string& name) const { + return hasInCache(name); +} + +void ShaderManager::remove(const std::string& name) { + std::lock_guard lock(mutex_); + auto it = pathIndex_.find(name); + if (it != pathIndex_.end()) { + auto cacheIt = cache_.find(it->second); + if (cacheIt != cache_.end()) { + totalMemory_ -= cacheIt->second.memorySize; + cache_.erase(cacheIt); + } + pathIndex_.erase(it); + } + metadataCache_.erase(name); +} + +void ShaderManager::clear() { + BaseResourceManager::clearAll(); + metadataCache_.clear(); +} + +ShaderMetadata ShaderManager::getMetadata(const std::string& name) const { + auto it = metadataCache_.find(name); + if (it != metadataCache_.end()) { + return it->second; + } + return ShaderMetadata{}; +} + +bool ShaderManager::loadBuiltinShaders() { + if (!initialized_) { + E2D_ERROR("ShaderManager not initialized"); + return false; + } + + bool allSuccess = true; + const char* builtinNames[] = {"sprite", "particle", "shape", "postprocess", "font"}; + + for (const char* name : builtinNames) { + fs::path jsonPath = fs::path(shaderDir_) / "shared" / "builtin" / (std::string(name) + ".json"); + + if (fs::exists(jsonPath)) { + auto handle = loadFromMetadata(jsonPath.string(), name); + if (!handle) { + E2D_ERROR("ShaderManager: Failed to load builtin shader: {}", name); + allSuccess = false; + } + } + } + + if (allSuccess) { + E2D_INFO("ShaderManager: All builtin shaders loaded"); + } + + return allSuccess; +} + +ResourceHandle ShaderManager::getBuiltin(const std::string& name) { + auto handle = get(name); + if (handle) { + return handle; + } + + fs::path jsonPath = fs::path(shaderDir_) / "shared" / "builtin" / (name + ".json"); + if (fs::exists(jsonPath)) { + return loadFromMetadata(jsonPath.string(), name); + } + + E2D_ERROR("ShaderManager: Builtin shader not found: {}", name); + return ResourceHandle(); +} + +std::string ShaderManager::readFile(const std::string& path) { + std::ifstream file(path, std::ios::binary); + if (!file.is_open()) { + return ""; + } + + std::string content((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + return content; +} + +std::string ShaderManager::processIncludes(const std::string& source, + const std::string& baseDir, + std::vector& dependencies) { + std::string result = source; + std::regex includeRegex(R"(#include\s+[<"]([^>"]+)[>"])"); + + std::smatch match; + size_t offset = 0; + + while (std::regex_search(result.cbegin() + offset, result.cend(), match, includeRegex)) { + std::string includeName = match[1].str(); + std::string includePath = (fs::path(baseDir) / includeName).string(); + + std::string includeContent = readFile(includePath); + if (!includeContent.empty()) { + dependencies.push_back(includePath); + includeContent = processIncludes(includeContent, fs::path(includePath).parent_path().string(), dependencies); + + size_t pos = match.position() + offset; + result.replace(pos, match.length(), includeContent); + offset = pos + includeContent.length(); + } else { + offset = match.position() + offset + match.length(); + } + } + + return result; +} + +bool ShaderManager::parseMetadata(const std::string& jsonContent, + ShaderMetadata& metadata, + std::string& vertPath, + std::string& fragPath) { + try { + nl::json j = nl::json::parse(jsonContent); + + if (j.contains("category")) { + metadata.category = j["category"].get(); + } + if (j.contains("version")) { + metadata.version = j["version"].get(); + } + if (j.contains("description")) { + metadata.description = j["description"].get(); + } + + if (j.contains("uniforms")) { + for (auto& [key, value] : j["uniforms"].items()) { + ShaderUniformDef def; + if (value.contains("type")) { + def.type = value["type"].get(); + } + if (value.contains("description")) { + def.description = value["description"].get(); + } + if (value.contains("default")) { + def.hasDefault = true; + if (def.type == "float") { + def.defaultValue = value["default"].get(); + } else if (def.type == "int") { + def.defaultInt = value["default"].get(); + } else if (def.type == "bool") { + def.defaultBool = value["default"].get(); + } else if (def.type == "vec2" && value["default"].is_array()) { + auto arr = value["default"].get>(); + for (size_t i = 0; i < arr.size() && i < 2; ++i) { + def.defaultVec2[i] = arr[i]; + } + } else if (def.type == "vec3" && value["default"].is_array()) { + auto arr = value["default"].get>(); + for (size_t i = 0; i < arr.size() && i < 3; ++i) { + def.defaultVec3[i] = arr[i]; + } + } else if (def.type == "vec4" && value["default"].is_array()) { + auto arr = value["default"].get>(); + for (size_t i = 0; i < arr.size() && i < 4; ++i) { + def.defaultVec4[i] = arr[i]; + } + } + } + metadata.uniformDefs[key] = def; + metadata.uniforms[key] = def.type; + } + } + + if (j.contains("samplers")) { + for (auto& [key, value] : j["samplers"].items()) { + ShaderSamplerDef def; + if (value.contains("type")) { + def.type = value["type"].get(); + } + if (value.contains("description")) { + def.description = value["description"].get(); + } + metadata.samplerDefs[key] = def; + } + } + + if (!j.contains("backends") || !j["backends"].contains("opengl")) { + E2D_ERROR("ShaderManager: No OpenGL backend in metadata"); + return false; + } + + auto& opengl = j["backends"]["opengl"]; + if (!opengl.contains("vertex") || !opengl.contains("fragment")) { + E2D_ERROR("ShaderManager: Missing vertex or fragment path in metadata"); + return false; + } + + vertPath = opengl["vertex"].get(); + fragPath = opengl["fragment"].get(); + + return true; + + } catch (const nl::json::exception& e) { + E2D_ERROR("ShaderManager: JSON parse error: {}", e.what()); + return false; + } +} + +} // namespace extra2d diff --git a/Extra2D/src/resources/texture_manager.cpp b/Extra2D/src/resources/texture_manager.cpp new file mode 100644 index 0000000..34f75b5 --- /dev/null +++ b/Extra2D/src/resources/texture_manager.cpp @@ -0,0 +1,302 @@ +#include + +#define STB_IMAGE_IMPLEMENTATION +#include + +#include + +namespace extra2d { + +TextureManager& TextureManager::getInstance() { + static TextureManager instance; + return instance; +} + +bool TextureManager::init(Ptr device) { + if (initialized_) { + return true; + } + + if (!device) { + E2D_ERROR("TextureManager: device is null"); + return false; + } + + device_ = device; + initialized_ = true; + + E2D_INFO("TextureManager initialized"); + return true; +} + +void TextureManager::shutdown() { + if (!initialized_) { + return; + } + + clear(); + device_.reset(); + initialized_ = false; + + E2D_INFO("TextureManager shutdown"); +} + +ResourceHandle TextureManager::load(const std::string& path, + const TextureLoadOptions& options) { + if (!initialized_) { + E2D_ERROR("TextureManager not initialized"); + return ResourceHandle(); + } + + auto cached = getFromCache(path); + if (cached) { + return ResourceHandle(cached.get(), this); + } + + auto data = ResourceSystem::readData(path); + if (data.empty()) { + E2D_ERROR("TextureManager: Failed to read texture file: {}", path); + return ResourceHandle(); + } + + return loadFromMemory(path, data.data(), data.size(), options); +} + +ResourceHandle TextureManager::loadFromMemory(const std::string& name, + const uint8_t* data, + size_t dataSize, + const TextureLoadOptions& options) { + if (!initialized_) { + E2D_ERROR("TextureManager not initialized"); + return ResourceHandle(); + } + + auto cached = getFromCache(name); + if (cached) { + return ResourceHandle(cached.get(), this); + } + + stbi_set_flip_vertically_on_load(options.flipVertically ? 1 : 0); + + int width, height, channels; + int desiredChannels = options.desiredChannels; + if (desiredChannels <= 0) { + desiredChannels = 4; + } + + uint8_t* pixels = stbi_load_from_memory(data, static_cast(dataSize), + &width, &height, &channels, desiredChannels); + if (!pixels) { + E2D_ERROR("TextureManager: Failed to load image: {}, error: {}", + name, stbi_failure_reason()); + return ResourceHandle(); + } + + auto handle = create(name, pixels, static_cast(width), + static_cast(height), + static_cast(desiredChannels), options); + + stbi_image_free(pixels); + return handle; +} + +ResourceHandle TextureManager::create(const std::string& name, + const uint8_t* pixels, + uint32_t width, + uint32_t height, + uint32_t channels, + const TextureLoadOptions& options) { + if (!initialized_) { + E2D_ERROR("TextureManager not initialized"); + return ResourceHandle(); + } + + auto cached = getFromCache(name); + if (cached) { + return ResourceHandle(cached.get(), this); + } + + rhi::Format format; + switch (channels) { + case 1: + format = rhi::Format::R8_UNORM; + break; + case 2: + format = rhi::Format::RG8_UNORM; + break; + case 3: + format = rhi::Format::RGB8_UNORM; + break; + case 4: + default: + format = options.sRGB ? rhi::Format::RGBA8_UNORM : rhi::Format::RGBA8_UNORM; + break; + } + + rhi::TextureDesc desc; + desc.type = rhi::TextureType::Texture2D; + desc.format = format; + desc.width = width; + desc.height = height; + desc.mipLevels = options.generateMipmaps ? 0 : 1; + desc.data = pixels; + desc.debugName = name; + + auto texture = device_->createTexture(desc); + if (!texture || !texture->isValid()) { + E2D_ERROR("TextureManager: Failed to create texture: {}", name); + return ResourceHandle(); + } + + if (options.generateMipmaps) { + texture->generateMipmaps(); + } + + auto textureData = std::make_shared(); + textureData->name = name; + textureData->path = name; + textureData->width = width; + textureData->height = height; + textureData->channels = channels; + textureData->format = format; + textureData->texture = texture; + textureData->sampler = createSampler(options); + textureData->hasMipmaps = options.generateMipmaps; + textureData->memorySize = calculateMemorySize(width, height, format, + options.generateMipmaps); + + cache(name, textureData, textureData->memorySize); + + E2D_DEBUG("TextureManager: Created texture '{}' ({}x{}, {} channels, {} bytes)", + name, width, height, channels, textureData->memorySize); + + return ResourceHandle(textureData.get(), this); +} + +ResourceHandle TextureManager::createEmpty(const std::string& name, + uint32_t width, + uint32_t height, + rhi::Format format, + bool generateMipmaps) { + if (!initialized_) { + E2D_ERROR("TextureManager not initialized"); + return ResourceHandle(); + } + + auto cached = getFromCache(name); + if (cached) { + return ResourceHandle(cached.get(), this); + } + + rhi::TextureDesc desc; + desc.type = rhi::TextureType::Texture2D; + desc.format = format; + desc.width = width; + desc.height = height; + desc.mipLevels = generateMipmaps ? 0 : 1; + desc.data = nullptr; + desc.debugName = name; + + auto texture = device_->createTexture(desc); + if (!texture || !texture->isValid()) { + E2D_ERROR("TextureManager: Failed to create empty texture: {}", name); + return ResourceHandle(); + } + + auto textureData = std::make_shared(); + textureData->name = name; + textureData->path = name; + textureData->width = width; + textureData->height = height; + textureData->channels = rhi::getFormatSize(format) / (format == rhi::Format::R8_UNORM ? 1 : + format == rhi::Format::RG8_UNORM ? 2 : 4); + textureData->format = format; + textureData->texture = texture; + textureData->isRenderTarget = true; + textureData->hasMipmaps = generateMipmaps; + textureData->memorySize = calculateMemorySize(width, height, format, generateMipmaps); + + cache(name, textureData, textureData->memorySize); + + E2D_DEBUG("TextureManager: Created empty texture '{}' ({}x{})", name, width, height); + + return ResourceHandle(textureData.get(), this); +} + +ResourceHandle TextureManager::get(const std::string& path) { + auto cached = getFromCache(path); + if (cached) { + return ResourceHandle(cached.get(), this); + } + return ResourceHandle(); +} + +bool TextureManager::has(const std::string& path) const { + return hasInCache(path); +} + +void TextureManager::remove(const std::string& path) { + std::lock_guard lock(mutex_); + auto it = pathIndex_.find(path); + if (it != pathIndex_.end()) { + auto cacheIt = cache_.find(it->second); + if (cacheIt != cache_.end()) { + totalMemory_ -= cacheIt->second.memorySize; + cache_.erase(cacheIt); + } + pathIndex_.erase(it); + } +} + +void TextureManager::clear() { + BaseResourceManager::clearAll(); +} + +size_t TextureManager::getTextureMemory() const { + return getTotalMemory(); +} + +size_t TextureManager::getTextureCount() const { + return getCount(); +} + +Ptr TextureManager::createSampler(const TextureLoadOptions& options) { + rhi::SamplerDesc desc; + desc.minFilter = options.minFilter; + desc.magFilter = options.magFilter; + desc.mipFilter = options.generateMipmaps ? + rhi::FilterMode::LinearMipmapLinear : + rhi::FilterMode::Linear; + desc.addressU = options.addressU; + desc.addressV = options.addressV; + desc.addressW = rhi::AddressMode::ClampToEdge; + + return device_->createSampler(desc); +} + +size_t TextureManager::calculateMemorySize(uint32_t width, uint32_t height, + rhi::Format format, bool hasMipmaps) { + size_t baseSize = static_cast(width) * height * rhi::getFormatSize(format); + + if (hasMipmaps) { + size_t totalSize = 0; + uint32_t w = width; + uint32_t h = height; + while (w > 0 && h > 0) { + totalSize += static_cast(w) * h * rhi::getFormatSize(format); + w >>= 1; + h >>= 1; + if (w == 0) w = 1; + if (h == 0) h = 1; + if (w == 1 && h == 1) { + totalSize += rhi::getFormatSize(format); + break; + } + } + return totalSize; + } + + return baseSize; +} + +} // namespace extra2d diff --git a/Extra2D/src/scene/component.cpp b/Extra2D/src/scene/component.cpp new file mode 100644 index 0000000..6b26cf2 --- /dev/null +++ b/Extra2D/src/scene/component.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + +namespace extra2d { + +/** + * @brief 获取关联的场景 + * @return 关联的场景指针,未关联返回nullptr + */ +Scene* Component::getScene() const { + if (node_) { + return node_->getScene(); + } + return nullptr; +} + +} // namespace extra2d diff --git a/Extra2D/src/scene/components/shape_renderer.cpp b/Extra2D/src/scene/components/shape_renderer.cpp new file mode 100644 index 0000000..ad013c2 --- /dev/null +++ b/Extra2D/src/scene/components/shape_renderer.cpp @@ -0,0 +1,376 @@ +#include +#include +#include +#include + +namespace extra2d { + +ShapeRenderer::ShapeRenderer() + : shapes_() + , color_(Colors::White) + , blend_(rhi::BlendState::alphaBlend()) + , lineWidth_(1.0f) + , layer_(0) + , defaultSegments_(32) { +} + +ShapeRenderer::~ShapeRenderer() { + shapes_.clear(); +} + +void ShapeRenderer::setColor(const Color& color) { + color_ = color; +} + +void ShapeRenderer::setBlendState(const rhi::BlendState& blend) { + blend_ = blend; +} + +void ShapeRenderer::setLineWidth(float width) { + lineWidth_ = width; +} + +void ShapeRenderer::setLayer(int layer) { + layer_ = layer; +} + +void ShapeRenderer::drawLine(const Vec2& start, const Vec2& end) { + ShapeData shape; + shape.type = ShapeType::Line; + shape.points = {start, end}; + shape.color = color_; + shape.lineWidth = lineWidth_; + shape.blend = blend_; + shape.filled = false; + addShape(shape); +} + +void ShapeRenderer::drawLines(const std::vector& points) { + if (points.size() < 2) { + return; + } + + for (size_t i = 0; i + 1 < points.size(); i += 2) { + drawLine(points[i], points[i + 1]); + } +} + +void ShapeRenderer::drawPolyline(const std::vector& points) { + if (points.size() < 2) { + return; + } + + for (size_t i = 0; i + 1 < points.size(); ++i) { + drawLine(points[i], points[i + 1]); + } +} + +void ShapeRenderer::drawRect(const Rect& rect) { + ShapeData shape; + shape.type = ShapeType::Rect; + shape.points = { + Vec2(rect.origin.x, rect.origin.y), + Vec2(rect.origin.x + rect.size.width, rect.origin.y), + Vec2(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height), + Vec2(rect.origin.x, rect.origin.y + rect.size.height) + }; + shape.color = color_; + shape.lineWidth = lineWidth_; + shape.blend = blend_; + shape.filled = false; + addShape(shape); +} + +void ShapeRenderer::drawFilledRect(const Rect& rect) { + ShapeData shape; + shape.type = ShapeType::FilledRect; + shape.points = { + Vec2(rect.origin.x, rect.origin.y), + Vec2(rect.origin.x + rect.size.width, rect.origin.y), + Vec2(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height), + Vec2(rect.origin.x, rect.origin.y + rect.size.height) + }; + shape.color = color_; + shape.blend = blend_; + shape.filled = true; + addShape(shape); +} + +void ShapeRenderer::drawCircle(const Vec2& center, float radius, int segments) { + ShapeData shape; + shape.type = ShapeType::Circle; + shape.points = {center}; + shape.color = color_; + shape.lineWidth = lineWidth_; + shape.radius = radius; + shape.segments = segments > 0 ? segments : defaultSegments_; + shape.blend = blend_; + shape.filled = false; + addShape(shape); +} + +void ShapeRenderer::drawFilledCircle(const Vec2& center, float radius, int segments) { + ShapeData shape; + shape.type = ShapeType::FilledCircle; + shape.points = {center}; + shape.color = color_; + shape.radius = radius; + shape.segments = segments > 0 ? segments : defaultSegments_; + shape.blend = blend_; + shape.filled = true; + addShape(shape); +} + +void ShapeRenderer::drawEllipse(const Vec2& center, float radiusX, float radiusY, int segments) { + ShapeData shape; + shape.type = ShapeType::Circle; + shape.points = {center, Vec2(radiusX, radiusY)}; + shape.color = color_; + shape.lineWidth = lineWidth_; + shape.segments = segments > 0 ? segments : defaultSegments_; + shape.blend = blend_; + shape.filled = false; + addShape(shape); +} + +void ShapeRenderer::drawFilledEllipse(const Vec2& center, float radiusX, float radiusY, int segments) { + ShapeData shape; + shape.type = ShapeType::FilledCircle; + shape.points = {center, Vec2(radiusX, radiusY)}; + shape.color = color_; + shape.segments = segments > 0 ? segments : defaultSegments_; + shape.blend = blend_; + shape.filled = true; + addShape(shape); +} + +void ShapeRenderer::drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3) { + ShapeData shape; + shape.type = ShapeType::Triangle; + shape.points = {p1, p2, p3}; + shape.color = color_; + shape.lineWidth = lineWidth_; + shape.blend = blend_; + shape.filled = false; + addShape(shape); +} + +void ShapeRenderer::drawFilledTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3) { + ShapeData shape; + shape.type = ShapeType::FilledTriangle; + shape.points = {p1, p2, p3}; + shape.color = color_; + shape.blend = blend_; + shape.filled = true; + addShape(shape); +} + +void ShapeRenderer::drawPolygon(const std::vector& points) { + if (points.size() < 3) { + return; + } + + ShapeData shape; + shape.type = ShapeType::Polygon; + shape.points = points; + shape.color = color_; + shape.lineWidth = lineWidth_; + shape.blend = blend_; + shape.filled = false; + addShape(shape); +} + +void ShapeRenderer::drawFilledPolygon(const std::vector& points) { + if (points.size() < 3) { + return; + } + + ShapeData shape; + shape.type = ShapeType::FilledPolygon; + shape.points = points; + shape.color = color_; + shape.blend = blend_; + shape.filled = true; + addShape(shape); +} + +void ShapeRenderer::drawArc(const Vec2& center, float radius, float startAngle, float endAngle, int segments) { + ShapeData shape; + shape.type = ShapeType::Circle; + shape.points = {center, Vec2(startAngle, endAngle)}; + shape.color = color_; + shape.lineWidth = lineWidth_; + shape.radius = radius; + shape.segments = segments > 0 ? segments : defaultSegments_; + shape.blend = blend_; + shape.filled = false; + addShape(shape); +} + +void ShapeRenderer::clearShapes() { + shapes_.clear(); +} + +void ShapeRenderer::init() { + Component::init(); +} + +void ShapeRenderer::destroy() { + shapes_.clear(); + Component::destroy(); +} + +void ShapeRenderer::collectRenderCommands(RenderQueue& queue) { + if (!enabled_ || shapes_.empty()) { + return; + } + + if (!node_) { + return; + } + + auto transform = node_->getTransform(); + if (!transform) { + return; + } + + int zOrder = node_->getZOrder(); + + for (const auto& shape : shapes_) { + RenderCommand cmd; + + switch (shape.type) { + case ShapeType::Line: + if (shape.points.size() >= 2) { + cmd = RenderCommand::makeLine( + shape.points[0], + shape.points[1], + shape.color, + shape.lineWidth, + layer_, + zOrder + ); + } + break; + + case ShapeType::Rect: + if (shape.points.size() >= 4) { + Rect rect(shape.points[0].x, shape.points[0].y, + shape.points[2].x - shape.points[0].x, + shape.points[2].y - shape.points[0].y); + cmd = RenderCommand::makeRect( + rect, + shape.color, + shape.lineWidth, + false, + layer_, + zOrder + ); + } + break; + + case ShapeType::FilledRect: + if (shape.points.size() >= 4) { + Rect rect(shape.points[0].x, shape.points[0].y, + shape.points[2].x - shape.points[0].x, + shape.points[2].y - shape.points[0].y); + cmd = RenderCommand::makeRect( + rect, + shape.color, + 0.0f, + true, + layer_, + zOrder + ); + } + break; + + case ShapeType::Circle: + case ShapeType::FilledCircle: + if (!shape.points.empty()) { + cmd = RenderCommand::makeCircle( + shape.points[0], + shape.radius, + shape.color, + shape.segments, + shape.filled, + layer_, + zOrder + ); + } + break; + + case ShapeType::Triangle: + case ShapeType::FilledTriangle: + if (shape.points.size() >= 3) { + cmd = RenderCommand::makeTriangle( + shape.points[0], + shape.points[1], + shape.points[2], + shape.color, + shape.filled, + layer_, + zOrder + ); + } + break; + + case ShapeType::Polygon: + case ShapeType::FilledPolygon: + if (shape.points.size() >= 3) { + cmd = RenderCommand::makePolygon( + shape.points, + shape.color, + shape.filled, + layer_, + zOrder + ); + } + break; + + default: + break; + } + + cmd.transform = transform->getWorldTransform(); + queue.addCommand(std::move(cmd)); + } +} + +Rect ShapeRenderer::getBounds() const { + if (shapes_.empty()) { + return Rect(); + } + + float minX = std::numeric_limits::max(); + float minY = std::numeric_limits::max(); + float maxX = std::numeric_limits::lowest(); + float maxY = std::numeric_limits::lowest(); + + for (const auto& shape : shapes_) { + for (const auto& point : shape.points) { + minX = std::min(minX, point.x); + minY = std::min(minY, point.y); + maxX = std::max(maxX, point.x); + maxY = std::max(maxY, point.y); + } + + if (shape.type == ShapeType::Circle || shape.type == ShapeType::FilledCircle) { + if (!shape.points.empty()) { + const Vec2& center = shape.points[0]; + minX = std::min(minX, center.x - shape.radius); + minY = std::min(minY, center.y - shape.radius); + maxX = std::max(maxX, center.x + shape.radius); + maxY = std::max(maxY, center.y + shape.radius); + } + } + } + + return Rect(minX, minY, maxX - minX, maxY - minY); +} + +void ShapeRenderer::addShape(const ShapeData& shape) { + shapes_.push_back(shape); +} + +} // namespace extra2d diff --git a/Extra2D/src/scene/components/sprite_renderer.cpp b/Extra2D/src/scene/components/sprite_renderer.cpp new file mode 100644 index 0000000..0d1948a --- /dev/null +++ b/Extra2D/src/scene/components/sprite_renderer.cpp @@ -0,0 +1,189 @@ +#include +#include +#include + +namespace extra2d { + +SpriteRenderer::SpriteRenderer() + : texture_() + , srcRect_() + , contentSize_() + , color_(Colors::White) + , blend_(rhi::BlendState::alphaBlend()) + , layer_(0) + , flipX_(false) + , flipY_(false) + , useTextureSize_(true) { +} + +SpriteRenderer::~SpriteRenderer() { + texture_.reset(); +} + +void SpriteRenderer::setTexture(const TextureHandle& handle) { + texture_ = handle; + if (texture_ && useTextureSize_) { + updateContentSizeFromTexture(); + resetSrcRect(); + } +} + +void SpriteRenderer::setTexture(const std::string& path) { + auto& texManager = TextureManager::getInstance(); + auto handle = texManager.load(path); + setTexture(handle); +} + +uint32_t SpriteRenderer::getTextureWidth() const { + return texture_ ? texture_->width : 0; +} + +uint32_t SpriteRenderer::getTextureHeight() const { + return texture_ ? texture_->height : 0; +} + +void SpriteRenderer::setSrcRect(const Rect& rect) { + srcRect_ = rect; +} + +void SpriteRenderer::setSrcRect(float x, float y, float width, float height) { + srcRect_ = Rect(x, y, width, height); +} + +void SpriteRenderer::resetSrcRect() { + if (texture_) { + srcRect_ = Rect(0.0f, 0.0f, + static_cast(texture_->width), + static_cast(texture_->height)); + } else { + srcRect_ = Rect(); + } +} + +void SpriteRenderer::setContentSize(const Size& size) { + contentSize_ = size; + useTextureSize_ = false; +} + +void SpriteRenderer::setContentSize(float width, float height) { + setContentSize(Size(width, height)); +} + +void SpriteRenderer::resetContentSize() { + useTextureSize_ = true; + updateContentSizeFromTexture(); +} + +void SpriteRenderer::setColor(const Color& color) { + color_ = color; +} + +void SpriteRenderer::setBlendState(const rhi::BlendState& blend) { + blend_ = blend; +} + +void SpriteRenderer::setFlipX(bool flip) { + flipX_ = flip; +} + +void SpriteRenderer::setFlipY(bool flip) { + flipY_ = flip; +} + +void SpriteRenderer::setLayer(int layer) { + layer_ = layer; +} + +void SpriteRenderer::init() { + Component::init(); +} + +void SpriteRenderer::destroy() { + texture_.reset(); + Component::destroy(); +} + +void SpriteRenderer::collectRenderCommands(RenderQueue& queue) { + if (!enabled_ || !texture_ || !texture_->texture) { + return; + } + + if (!node_) { + return; + } + + auto transform = node_->getTransform(); + if (!transform) { + return; + } + + Rect destRect = calculateDestRect(); + Rect srcRect = calculateFlippedSrcRect(); + float rotation = transform->getRotation(); + Vec2 anchor = transform->getAnchor(); + + RenderCommand cmd = RenderCommand::makeSprite( + texture_->texture, + destRect, + srcRect, + color_, + rotation, + anchor, + blend_, + layer_, + node_->getZOrder() + ); + + cmd.transform = transform->getWorldTransform(); + queue.addCommand(std::move(cmd)); +} + +Rect SpriteRenderer::getBounds() const { + return calculateDestRect(); +} + +Rect SpriteRenderer::calculateDestRect() const { + float width = contentSize_.width; + float height = contentSize_.height; + + if (width <= 0.0f || height <= 0.0f) { + if (texture_) { + width = static_cast(texture_->width); + height = static_cast(texture_->height); + } + } + + return Rect(0.0f, 0.0f, width, height); +} + +Rect SpriteRenderer::calculateFlippedSrcRect() const { + Rect result = srcRect_; + + if (!texture_) { + return result; + } + + float texWidth = static_cast(texture_->width); + float texHeight = static_cast(texture_->height); + + if (flipX_) { + result.origin.x = texWidth - srcRect_.origin.x - srcRect_.size.width; + } + + if (flipY_) { + result.origin.y = texHeight - srcRect_.origin.y - srcRect_.size.height; + } + + return result; +} + +void SpriteRenderer::updateContentSizeFromTexture() { + if (texture_) { + contentSize_.width = static_cast(texture_->width); + contentSize_.height = static_cast(texture_->height); + } else { + contentSize_ = Size(); + } +} + +} // namespace extra2d diff --git a/Extra2D/src/scene/components/text_renderer.cpp b/Extra2D/src/scene/components/text_renderer.cpp new file mode 100644 index 0000000..bd87e58 --- /dev/null +++ b/Extra2D/src/scene/components/text_renderer.cpp @@ -0,0 +1,202 @@ +#include +#include +#include + +namespace extra2d { + +TextRenderer::TextRenderer() + : text_() + , font_() + , fontSize_(16) + , color_(Colors::White) + , blend_(rhi::BlendState::alphaBlend()) + , alignment_(TextAlignment::Left) + , verticalAlignment_(TextVerticalAlignment::Top) + , maxWidth_(0.0f) + , lineHeight_(0.0f) + , scale_(1.0f) + , layer_(0) + , wordWrap_(false) { +} + +TextRenderer::~TextRenderer() { + font_.reset(); +} + +void TextRenderer::setText(const std::u32string& text) { + text_ = text; +} + +void TextRenderer::setTextUTF8(const std::string& text) { + text_ = UnicodeUtils::utf8ToUtf32(text); +} + +void TextRenderer::setText(const std::string& text, TextEncoding encoding) { + text_ = UnicodeUtils::toUtf32(text, encoding); +} + +std::string TextRenderer::getTextUTF8() const { + return UnicodeUtils::utf32ToUtf8(text_); +} + +void TextRenderer::clearText() { + text_.clear(); +} + +void TextRenderer::setFont(const FontHandle& handle) { + font_ = handle; +} + +void TextRenderer::setFont(const std::string& path) { + auto& fontManager = FontManager::getInstance(); + auto handle = fontManager.load(path); + setFont(handle); +} + +void TextRenderer::setFontSize(int size) { + fontSize_ = size; +} + +void TextRenderer::setColor(const Color& color) { + color_ = color; +} + +void TextRenderer::setBlendState(const rhi::BlendState& blend) { + blend_ = blend; +} + +void TextRenderer::setAlignment(TextAlignment alignment) { + alignment_ = alignment; +} + +void TextRenderer::setVerticalAlignment(TextVerticalAlignment alignment) { + verticalAlignment_ = alignment; +} + +void TextRenderer::setMaxWidth(float width) { + maxWidth_ = width; +} + +void TextRenderer::setLineHeight(float height) { + lineHeight_ = height; +} + +void TextRenderer::setWordWrap(bool wrap) { + wordWrap_ = wrap; +} + +void TextRenderer::setScale(float scale) { + scale_ = scale; +} + +void TextRenderer::setLayer(int layer) { + layer_ = layer; +} + +void TextRenderer::init() { + Component::init(); +} + +void TextRenderer::destroy() { + font_.reset(); + text_.clear(); + Component::destroy(); +} + +void TextRenderer::collectRenderCommands(RenderQueue& queue) { + if (!enabled_ || text_.empty() || !font_) { + return; + } + + if (!node_) { + return; + } + + auto transform = node_->getTransform(); + if (!transform) { + return; + } + + Vec2 position = calculateStartPosition(); + + RenderCommand cmd = RenderCommand::makeText( + text_, + position, + color_, + static_cast(fontSize_), + font_->name, + layer_, + node_->getZOrder() + ); + + cmd.transform = transform->getWorldTransform(); + queue.addCommand(std::move(cmd)); +} + +Size TextRenderer::measureText() const { + if (!font_ || text_.empty()) { + return Size(); + } + + auto& fontManager = FontManager::getInstance(); + return fontManager.measureTextSize(font_, fontSize_, text_); +} + +float TextRenderer::measureTextWidth() const { + if (!font_ || text_.empty()) { + return 0.0f; + } + + auto& fontManager = FontManager::getInstance(); + return fontManager.measureText(font_, fontSize_, text_); +} + +Rect TextRenderer::getBounds() const { + Size textSize = measureText(); + Vec2 position = calculateStartPosition(); + return Rect(position.x, position.y, textSize.width, textSize.height); +} + +Vec2 TextRenderer::calculateStartPosition() const { + Vec2 position; + + if (!node_) { + return position; + } + + auto transform = node_->getTransform(); + if (!transform) { + return position; + } + + Size textSize = measureText(); + Vec2 anchor = transform->getAnchor(); + + switch (alignment_) { + case TextAlignment::Left: + position.x = -textSize.width * anchor.x; + break; + case TextAlignment::Center: + position.x = -textSize.width * 0.5f; + break; + case TextAlignment::Right: + position.x = -textSize.width * (1.0f - anchor.x); + break; + } + + switch (verticalAlignment_) { + case TextVerticalAlignment::Top: + position.y = -textSize.height * anchor.y; + break; + case TextVerticalAlignment::Middle: + position.y = -textSize.height * 0.5f; + break; + case TextVerticalAlignment::Bottom: + position.y = -textSize.height * (1.0f - anchor.y); + break; + } + + return position; +} + +} // namespace extra2d diff --git a/Extra2D/src/scene/components/transform_component.cpp b/Extra2D/src/scene/components/transform_component.cpp new file mode 100644 index 0000000..57de56c --- /dev/null +++ b/Extra2D/src/scene/components/transform_component.cpp @@ -0,0 +1,251 @@ +#include +#include +#include + +namespace extra2d { + +/** + * @brief 设置本地位置 + * @param pos 新的位置坐标 + */ +void TransformComponent::setPosition(const Vec2& pos) { + position_ = pos; + markTransformDirty(); +} + +/** + * @brief 设置世界位置 + * @param pos 世界坐标位置 + */ +void TransformComponent::setWorldPosition(const Vec2& pos) { + Node* node = getNode(); + if (!node) { + setPosition(pos); + return; + } + + auto parent = node->getParent(); + if (parent) { + auto parentTransform = parent->getTransform(); + if (parentTransform) { + Vec2 localPos = parentTransform->worldToLocal(pos); + setPosition(localPos); + return; + } + } + setPosition(pos); +} + +/** + * @brief 获取世界位置 + * @return 世界坐标位置 + */ +Vec2 TransformComponent::getWorldPosition() const { + glm::mat4 worldTransform = getWorldTransform(); + return Vec2(worldTransform[3][0], worldTransform[3][1]); +} + +/** + * @brief 设置旋转角度 + * @param degrees 旋转角度(度数) + */ +void TransformComponent::setRotation(float degrees) { + rotation_ = degrees; + markTransformDirty(); +} + +/** + * @brief 设置缩放 + * @param scale 缩放向量 + */ +void TransformComponent::setScale(const Vec2& scale) { + scale_ = scale; + markTransformDirty(); +} + +/** + * @brief 设置锚点 + * @param anchor 锚点位置(0-1范围) + */ +void TransformComponent::setAnchor(const Vec2& anchor) { + anchor_ = anchor; + markTransformDirty(); +} + +/** + * @brief 设置斜切 + * @param skew 斜切角度向量 + */ +void TransformComponent::setSkew(const Vec2& skew) { + skew_ = skew; + markTransformDirty(); +} + +/** + * @brief 计算本地变换矩阵 + */ +void TransformComponent::computeLocalTransform() const { + localTransform_ = glm::mat4(1.0f); + + // T - R - Skew - S order + localTransform_ = glm::translate(localTransform_, + glm::vec3(position_.x, position_.y, 0.0f)); + + if (rotation_ != 0.0f) { + localTransform_ = glm::rotate(localTransform_, rotation_ * DEG_TO_RAD, + glm::vec3(0.0f, 0.0f, 1.0f)); + } + + if (skew_.x != 0.0f || skew_.y != 0.0f) { + glm::mat4 skewMatrix(1.0f); + skewMatrix[1][0] = std::tan(skew_.x * DEG_TO_RAD); + skewMatrix[0][1] = std::tan(skew_.y * DEG_TO_RAD); + localTransform_ *= skewMatrix; + } + + localTransform_ = glm::scale(localTransform_, glm::vec3(scale_.x, scale_.y, 1.0f)); + + localDirty_ = false; +} + +/** + * @brief 获取本地变换矩阵 + * @return 本地变换矩阵 + */ +glm::mat4 TransformComponent::getLocalTransform() const { + if (localDirty_) { + computeLocalTransform(); + } + return localTransform_; +} + +/** + * @brief 获取世界变换矩阵 + * @return 世界变换矩阵 + */ +glm::mat4 TransformComponent::getWorldTransform() const { + if (worldDirty_) { + Node* node = getNode(); + auto parent = node ? node->getParent() : nullptr; + + if (parent) { + auto parentTransform = parent->getTransform(); + if (parentTransform) { + worldTransform_ = parentTransform->getWorldTransform() * getLocalTransform(); + } else { + worldTransform_ = getLocalTransform(); + } + } else { + worldTransform_ = getLocalTransform(); + } + worldDirty_ = false; + } + return worldTransform_; +} + +/** + * @brief 将本地坐标转换为世界坐标 + * @param localPos 本地坐标位置 + * @return 世界坐标位置 + */ +Vec2 TransformComponent::localToWorld(const Vec2& localPos) const { + glm::mat4 worldTransform = getWorldTransform(); + glm::vec4 result = worldTransform * glm::vec4(localPos.x, localPos.y, 0.0f, 1.0f); + return Vec2(result.x, result.y); +} + +/** + * @brief 将世界坐标转换为本地坐标 + * @param worldPos 世界坐标位置 + * @return 本地坐标位置 + */ +Vec2 TransformComponent::worldToLocal(const Vec2& worldPos) const { + glm::mat4 invWorld = glm::inverse(getWorldTransform()); + glm::vec4 result = invWorld * glm::vec4(worldPos.x, worldPos.y, 0.0f, 1.0f); + return Vec2(result.x, result.y); +} + +/** + * @brief 标记变换为脏 + */ +void TransformComponent::markTransformDirty() { + if (!localDirty_ || !worldDirty_) { + localDirty_ = true; + worldDirty_ = true; + + // 通知子节点世界变换已失效 + Node* node = getNode(); + if (node) { + const auto& children = node->getChildren(); + for (const auto& child : children) { + auto childTransform = child->getTransform(); + if (childTransform) { + childTransform->markWorldTransformDirty(); + } + } + } + } +} + +/** + * @brief 标记世界变换为脏 + */ +void TransformComponent::markWorldTransformDirty() { + if (!worldDirty_) { + worldDirty_ = true; + + // 递归标记所有子节点 + Node* node = getNode(); + if (node) { + const auto& children = node->getChildren(); + for (const auto& child : children) { + auto childTransform = child->getTransform(); + if (childTransform) { + childTransform->markWorldTransformDirty(); + } + } + } + } +} + +/** + * @brief 批量更新变换 + */ +void TransformComponent::batchTransforms() { + // 如果本地变换脏了,先计算本地变换 + if (localDirty_) { + computeLocalTransform(); + } + + // 如果世界变换脏了,需要重新计算 + if (worldDirty_) { + Node* node = getNode(); + auto parent = node ? node->getParent() : nullptr; + + if (parent) { + auto parentTransform = parent->getTransform(); + if (parentTransform) { + worldTransform_ = parentTransform->getWorldTransform() * localTransform_; + } else { + worldTransform_ = localTransform_; + } + } else { + worldTransform_ = localTransform_; + } + worldDirty_ = false; + } + + // 递归更新子节点 + Node* node = getNode(); + if (node) { + const auto& children = node->getChildren(); + for (const auto& child : children) { + auto childTransform = child->getTransform(); + if (childTransform) { + childTransform->batchTransforms(); + } + } + } +} + +} // namespace extra2d diff --git a/Extra2D/src/scene/node.cpp b/Extra2D/src/scene/node.cpp index c216a15..446175e 100644 --- a/Extra2D/src/scene/node.cpp +++ b/Extra2D/src/scene/node.cpp @@ -1,222 +1,235 @@ #include #include #include -#include +#include #include #include #include - namespace extra2d { +/** + * @brief 创建节点 + * @param name 节点名称 + * @return 创建的节点智能指针 + */ +Ptr Node::create(const std::string& name) { + auto node = makePtr(); + node->setName(name); + return node; +} + /** * @brief 默认构造函数 * - * 创建一个空的节点对象 + * 创建一个空的节点对象,并自动添加变换组件 */ -Node::Node() = default; +Node::Node() { + // 自动添加变换组件 + auto transform = makePtr(); + transform->setNode(this); + transform_ = transform.get(); + components_[TransformComponent::getStaticTypeId()] = transform; +} /** * @brief 析构函数 * - * 清除所有子节点 + * 清除所有子节点和组件 */ -Node::~Node() { clearChildren(); } +Node::~Node() { + clearChildren(); + + // 销毁所有组件 + for (auto& pair : components_) { + pair.second->destroy(); + pair.second->setNode(nullptr); + } + components_.clear(); +} + +// ------------------------------------------------------------------------- +// 层级管理 +// ------------------------------------------------------------------------- /** * @brief 添加子节点 * @param child 要添加的子节点智能指针 - * - * 将子节点添加到当前节点的子节点列表中,自动从原父节点分离 */ void Node::addChild(Ptr child) { - if (!child || child.get() == this) { - return; - } - - child->detach(); - child->parent_ = weak_from_this(); - children_.push_back(child); - childrenOrderDirty_ = true; - - // 更新索引 - if (!child->getName().empty()) { - nameIndex_[child->getName()] = child; - } - if (child->getTag() != -1) { - tagIndex_[child->getTag()] = child; - } - - if (running_) { - child->onEnter(); - if (scene_) { - child->onAttachToScene(scene_); - } - } -} - -/** - * @brief 批量添加子节点 - * @param children 要添加的子节点数组(右值引用) - * - * 高效地批量添加多个子节点,预分配内存以减少扩容次数 - */ -void Node::addChildren(std::vector> &&children) { - // 预留空间,避免多次扩容 - size_t newSize = children_.size() + children.size(); - if (newSize > children_.capacity()) { - children_.reserve(newSize); - } - - for (auto &child : children) { if (!child || child.get() == this) { - continue; + return; } child->detach(); child->parent_ = weak_from_this(); children_.push_back(child); + childrenOrderDirty_ = true; // 更新索引 if (!child->getName().empty()) { - nameIndex_[child->getName()] = child; + nameIndex_[child->getName()] = child; } if (child->getTag() != -1) { - tagIndex_[child->getTag()] = child; + tagIndex_[child->getTag()] = child; } if (running_) { - child->onEnter(); - if (scene_) { - child->onAttachToScene(scene_); - } + child->onEnter(); + if (scene_) { + child->onAttachToScene(scene_); + } } - } +} - if (!children.empty()) { - childrenOrderDirty_ = true; - } +/** + * @brief 批量添加子节点 + * @param children 要添加的子节点数组(右值引用) + */ +void Node::addChildren(std::vector>&& children) { + size_t newSize = children_.size() + children.size(); + if (newSize > children_.capacity()) { + children_.reserve(newSize); + } + + for (auto& child : children) { + if (!child || child.get() == this) { + continue; + } + + child->detach(); + child->parent_ = weak_from_this(); + children_.push_back(child); + + if (!child->getName().empty()) { + nameIndex_[child->getName()] = child; + } + if (child->getTag() != -1) { + tagIndex_[child->getTag()] = child; + } + + if (running_) { + child->onEnter(); + if (scene_) { + child->onAttachToScene(scene_); + } + } + } + + if (!children.empty()) { + childrenOrderDirty_ = true; + } } /** * @brief 移除子节点 * @param child 要移除的子节点智能指针 - * - * 从子节点列表中移除指定节点,并触发相应的退出回调 */ void Node::removeChild(Ptr child) { - if (!child) - return; + if (!child) + return; - auto it = std::find(children_.begin(), children_.end(), child); - if (it != children_.end()) { - (*it)->onDetachFromScene(); + auto it = std::find(children_.begin(), children_.end(), child); + if (it != children_.end()) { + (*it)->onDetachFromScene(); - if (running_) { - (*it)->onExit(); + if (running_) { + (*it)->onExit(); + } + if (!(*it)->getName().empty()) { + nameIndex_.erase((*it)->getName()); + } + if ((*it)->getTag() != -1) { + tagIndex_.erase((*it)->getTag()); + } + (*it)->parent_.reset(); + children_.erase(it); } - if (!(*it)->getName().empty()) { - nameIndex_.erase((*it)->getName()); - } - if ((*it)->getTag() != -1) { - tagIndex_.erase((*it)->getTag()); - } - (*it)->parent_.reset(); - children_.erase(it); - } } /** * @brief 通过名称移除子节点 * @param name 子节点的名称 - * - * 查找并移除具有指定名称的子节点 */ -void Node::removeChildByName(const std::string &name) { - auto child = findChild(name); - if (child) { - removeChild(child); - } +void Node::removeChildByName(const std::string& name) { + auto child = findChild(name); + if (child) { + removeChild(child); + } } /** * @brief 从父节点分离 - * - * 将当前节点从其父节点的子节点列表中移除 */ void Node::detach() { - auto p = parent_.lock(); - if (p) { - // 安全获取 shared_ptr,避免在对象未由 shared_ptr 管理时崩溃 - Ptr self; - try { - self = shared_from_this(); - } catch (const std::bad_weak_ptr &) { - // 对象不是由 shared_ptr 管理的,直接重置父节点引用 - parent_.reset(); - return; + auto p = parent_.lock(); + if (p) { + Ptr self; + try { + self = shared_from_this(); + } catch (const std::bad_weak_ptr&) { + parent_.reset(); + return; + } + p->removeChild(self); } - p->removeChild(self); - } } /** * @brief 清除所有子节点 - * - * 移除所有子节点并触发相应的退出回调 */ void Node::clearChildren() { - for (auto &child : children_) { - if (running_) { - child->onDetachFromScene(); - child->onExit(); + for (auto& child : children_) { + if (running_) { + child->onDetachFromScene(); + child->onExit(); + } + child->parent_.reset(); } - child->parent_.reset(); - } - children_.clear(); - nameIndex_.clear(); - tagIndex_.clear(); + children_.clear(); + nameIndex_.clear(); + tagIndex_.clear(); } /** * @brief 通过名称查找子节点 * @param name 子节点的名称 * @return 找到的子节点智能指针,未找到返回nullptr - * - * 使用哈希索引进行O(1)时间复杂度查找 */ -Ptr Node::findChild(const std::string &name) const { - // 使用哈希索引,O(1) 查找 - auto it = nameIndex_.find(name); - if (it != nameIndex_.end()) { - return it->second.lock(); - } - return nullptr; +Ptr Node::findChild(const std::string& name) const { + auto it = nameIndex_.find(name); + if (it != nameIndex_.end()) { + return it->second.lock(); + } + return nullptr; } /** * @brief 通过标签查找子节点 * @param tag 子节点的标签值 * @return 找到的子节点智能指针,未找到返回nullptr - * - * 使用哈希索引进行O(1)时间复杂度查找 */ Ptr Node::findChildByTag(int tag) const { - // 使用哈希索引,O(1) 查找 - auto it = tagIndex_.find(tag); - if (it != tagIndex_.end()) { - return it->second.lock(); - } - return nullptr; + auto it = tagIndex_.find(tag); + if (it != tagIndex_.end()) { + return it->second.lock(); + } + return nullptr; } +// ------------------------------------------------------------------------- +// 变换属性(委托给TransformComponent) +// ------------------------------------------------------------------------- + /** * @brief 设置节点位置 * @param pos 新的位置坐标 */ -void Node::setPos(const Vec2 &pos) { - position_ = pos; - markTransformDirty(); +void Node::setPos(const Vec2& pos) { + if (transform_) { + transform_->setPosition(pos); + } + markTransformDirty(); } /** @@ -224,46 +237,91 @@ void Node::setPos(const Vec2 &pos) { * @param x X坐标 * @param y Y坐标 */ -void Node::setPos(float x, float y) { setPos(Vec2(x, y)); } +void Node::setPos(float x, float y) { + setPos(Vec2(x, y)); +} + +/** + * @brief 获取节点位置 + * @return 位置坐标 + */ +Vec2 Node::getPosition() const { + if (transform_) { + return transform_->getPosition(); + } + return Vec2::Zero(); +} /** * @brief 设置节点旋转角度 * @param degrees 旋转角度(度数) */ void Node::setRotation(float degrees) { - rotation_ = degrees; - markTransformDirty(); + if (transform_) { + transform_->setRotation(degrees); + } + markTransformDirty(); +} + +/** + * @brief 获取节点旋转角度 + * @return 旋转角度(度数) + */ +float Node::getRotation() const { + if (transform_) { + return transform_->getRotation(); + } + return 0.0f; } /** * @brief 设置节点缩放 * @param scale 缩放向量 */ -void Node::setScale(const Vec2 &scale) { - scale_ = scale; - markTransformDirty(); +void Node::setScale(const Vec2& scale) { + if (transform_) { + transform_->setScale(scale); + } + markTransformDirty(); } /** * @brief 设置节点统一缩放 * @param scale 统一缩放值 */ -void Node::setScale(float scale) { setScale(Vec2(scale, scale)); } +void Node::setScale(float scale) { + setScale(Vec2(scale, scale)); +} /** * @brief 设置节点缩放 * @param x X轴缩放值 * @param y Y轴缩放值 */ -void Node::setScale(float x, float y) { setScale(Vec2(x, y)); } +void Node::setScale(float x, float y) { + setScale(Vec2(x, y)); +} + +/** + * @brief 获取节点缩放 + * @return 缩放向量 + */ +Vec2 Node::getScale() const { + if (transform_) { + return transform_->getScale(); + } + return Vec2::One(); +} /** * @brief 设置节点锚点 * @param anchor 锚点位置(0-1范围) */ -void Node::setAnchor(const Vec2 &anchor) { - anchor_ = anchor; - markTransformDirty(); +void Node::setAnchor(const Vec2& anchor) { + if (transform_) { + transform_->setAnchor(anchor); + } + markTransformDirty(); } /** @@ -271,15 +329,30 @@ void Node::setAnchor(const Vec2 &anchor) { * @param x 锚点X坐标(0-1范围) * @param y 锚点Y坐标(0-1范围) */ -void Node::setAnchor(float x, float y) { setAnchor(Vec2(x, y)); } +void Node::setAnchor(float x, float y) { + setAnchor(Vec2(x, y)); +} + +/** + * @brief 获取节点锚点 + * @return 锚点位置 + */ +Vec2 Node::getAnchor() const { + if (transform_) { + return transform_->getAnchor(); + } + return Vec2(0.5f, 0.5f); +} /** * @brief 设置节点斜切 * @param skew 斜切角度向量 */ -void Node::setSkew(const Vec2 &skew) { - skew_ = skew; - markTransformDirty(); +void Node::setSkew(const Vec2& skew) { + if (transform_) { + transform_->setSkew(skew); + } + markTransformDirty(); } /** @@ -287,62 +360,35 @@ void Node::setSkew(const Vec2 &skew) { * @param x X轴斜切角度 * @param y Y轴斜切角度 */ -void Node::setSkew(float x, float y) { setSkew(Vec2(x, y)); } - -/** - * @brief 设置节点透明度 - * @param opacity 透明度值(0.0-1.0范围) - */ -void Node::setOpacity(float opacity) { - opacity_ = std::clamp(opacity, 0.0f, 1.0f); +void Node::setSkew(float x, float y) { + setSkew(Vec2(x, y)); } /** - * @brief 设置节点可见性 - * @param visible 是否可见 + * @brief 获取节点斜切 + * @return 斜切角度向量 */ -void Node::setVisible(bool visible) { visible_ = visible; } - -/** - * @brief 设置节点颜色 - * @param color RGB颜色值 - */ -void Node::setColor(const Color3B &color) { color_ = color; } - -/** - * @brief 设置水平翻转 - * @param flipX 是否水平翻转 - */ -void Node::setFlipX(bool flipX) { flipX_ = flipX; } - -/** - * @brief 设置垂直翻转 - * @param flipY 是否垂直翻转 - */ -void Node::setFlipY(bool flipY) { flipY_ = flipY; } - -/** - * @brief 设置Z序 - * @param zOrder 渲染层级顺序 - * - * 较大的Z序值会在上层渲染 - */ -void Node::setZOrder(int zOrder) { - if (zOrder_ != zOrder) { - zOrder_ = zOrder; - childrenOrderDirty_ = true; - } +Vec2 Node::getSkew() const { + if (transform_) { + return transform_->getSkew(); + } + return Vec2::Zero(); } +// ------------------------------------------------------------------------- +// 世界变换 +// ------------------------------------------------------------------------- + /** * @brief 将本地坐标转换为世界坐标 * @param localPos 本地坐标位置 * @return 世界坐标位置 */ -Vec2 Node::toWorld(const Vec2 &localPos) const { - glm::vec4 worldPos = - getWorldTransform() * glm::vec4(localPos.x, localPos.y, 0.0f, 1.0f); - return Vec2(worldPos.x, worldPos.y); +Vec2 Node::toWorld(const Vec2& localPos) const { + if (transform_) { + return transform_->localToWorld(localPos); + } + return localPos; } /** @@ -350,306 +396,345 @@ Vec2 Node::toWorld(const Vec2 &localPos) const { * @param worldPos 世界坐标位置 * @return 本地坐标位置 */ -Vec2 Node::toLocal(const Vec2 &worldPos) const { - glm::mat4 invWorld = glm::inverse(getWorldTransform()); - glm::vec4 localPos = invWorld * glm::vec4(worldPos.x, worldPos.y, 0.0f, 1.0f); - return Vec2(localPos.x, localPos.y); +Vec2 Node::toLocal(const Vec2& worldPos) const { + if (transform_) { + return transform_->worldToLocal(worldPos); + } + return worldPos; } /** * @brief 获取本地变换矩阵 * @return 本地变换矩阵 - * - * 计算包含位置、旋转、斜切和缩放的本地变换矩阵 */ glm::mat4 Node::getLocalTransform() const { - if (transformDirty_) { - localTransform_ = glm::mat4(1.0f); - - // T - R - S order - localTransform_ = glm::translate(localTransform_, - glm::vec3(position_.x, position_.y, 0.0f)); - - if (rotation_ != 0.0f) { - localTransform_ = glm::rotate(localTransform_, rotation_ * DEG_TO_RAD, - glm::vec3(0.0f, 0.0f, 1.0f)); + if (transform_) { + return transform_->getLocalTransform(); } - - if (skew_.x != 0.0f || skew_.y != 0.0f) { - glm::mat4 skewMatrix(1.0f); - skewMatrix[1][0] = std::tan(skew_.x * DEG_TO_RAD); - skewMatrix[0][1] = std::tan(skew_.y * DEG_TO_RAD); - localTransform_ *= skewMatrix; - } - - localTransform_ = - glm::scale(localTransform_, glm::vec3(scale_.x, scale_.y, 1.0f)); - - // 注意:锚点偏移在渲染时处理,不在本地变换中处理 - // 这样可以避免锚点偏移被父节点的缩放影响 - - transformDirty_ = false; - } - return localTransform_; + return glm::mat4(1.0f); } /** * @brief 获取世界变换矩阵 * @return 世界变换矩阵 - * - * 计算从根节点到当前节点的累积变换矩阵 */ glm::mat4 Node::getWorldTransform() const { - if (worldTransformDirty_) { - // 使用线程局部存储的固定数组,避免每帧内存分配 - // 限制最大深度为 256 层,足以覆盖绝大多数场景 - thread_local std::array nodeChainCache; - thread_local size_t chainCount = 0; - - chainCount = 0; - const Node *current = this; - while (current && chainCount < nodeChainCache.size()) { - nodeChainCache[chainCount++] = current; - auto p = current->parent_.lock(); - current = p.get(); + if (transform_) { + return transform_->getWorldTransform(); } - - // 从根节点开始计算 - glm::mat4 transform = glm::mat4(1.0f); - for (size_t i = chainCount; i > 0; --i) { - transform = transform * nodeChainCache[i - 1]->getLocalTransform(); - } - worldTransform_ = transform; - worldTransformDirty_ = false; - } - return worldTransform_; + return glm::mat4(1.0f); } /** - * @brief 标记变换为脏 - * - * 标记本地变换和世界变换需要重新计算,并递归标记所有子节点 + * @brief 标记变换矩阵为脏状态 */ void Node::markTransformDirty() { - // 避免重复标记,提高性能 - if (!transformDirty_ || !worldTransformDirty_) { - transformDirty_ = true; - worldTransformDirty_ = true; + if (!transformDirty_ || !worldTransformDirty_) { + transformDirty_ = true; + worldTransformDirty_ = true; - // 递归标记所有子节点 - for (auto &child : children_) { - child->markTransformDirty(); + // 递归标记所有子节点 + for (auto& child : children_) { + child->markTransformDirty(); + } } - } } /** - * @brief 批量更新变换 - * - * 从父节点到子节点依次更新世界变换矩阵 + * @brief 批量更新变换矩阵 */ void Node::batchTransforms() { - // 如果本地变换脏了,先计算本地变换 - if (transformDirty_) { - (void)getLocalTransform(); // 这会计算并缓存本地变换 - } - - // 如果世界变换脏了,需要重新计算 - if (worldTransformDirty_) { - auto parent = parent_.lock(); - if (parent) { - // 使用父节点的世界变换(确保父节点已经更新) - worldTransform_ = parent->getWorldTransform() * localTransform_; - } else { - // 根节点 - worldTransform_ = localTransform_; + if (transform_) { + transform_->batchTransforms(); } + + transformDirty_ = false; worldTransformDirty_ = false; - } - // 递归更新子节点 - for (auto &child : children_) { - child->batchTransforms(); - } + // 递归更新子节点 + for (auto& child : children_) { + child->batchTransforms(); + } } +/** + * @brief 获取变换脏标记状态 + */ +bool Node::isTransformDirty() const { + if (transform_) { + return transform_->isLocalTransformDirty(); + } + return transformDirty_; +} + +/** + * @brief 获取世界变换脏标记状态 + */ +bool Node::isWorldTransformDirty() const { + if (transform_) { + return transform_->isWorldTransformDirty(); + } + return worldTransformDirty_; +} + +// ------------------------------------------------------------------------- +// 可见性和外观 +// ------------------------------------------------------------------------- + +/** + * @brief 设置透明度 + * @param opacity 透明度值(0.0-1.0范围) + */ +void Node::setOpacity(float opacity) { + opacity_ = std::clamp(opacity, 0.0f, 1.0f); +} + +/** + * @brief 设置可见性 + * @param visible 是否可见 + */ +void Node::setVisible(bool visible) { + visible_ = visible; +} + +/** + * @brief 设置颜色 + * @param color RGB颜色值 + */ +void Node::setColor(const Color3B& color) { + color_ = color; +} + +/** + * @brief 设置水平翻转 + * @param flipX 是否水平翻转 + */ +void Node::setFlipX(bool flipX) { + flipX_ = flipX; +} + +/** + * @brief 设置垂直翻转 + * @param flipY 是否垂直翻转 + */ +void Node::setFlipY(bool flipY) { + flipY_ = flipY; +} + +/** + * @brief 设置Z序 + * @param zOrder 渲染层级顺序 + */ +void Node::setZOrder(int zOrder) { + if (zOrder_ != zOrder) { + zOrder_ = zOrder; + childrenOrderDirty_ = true; + } +} + +// ------------------------------------------------------------------------- +// 生命周期回调 +// ------------------------------------------------------------------------- + /** * @brief 节点进入时的回调 - * - * 标记节点为运行状态,并递归调用所有子节点的onEnter */ void Node::onEnter() { - running_ = true; - for (auto &child : children_) { - child->onEnter(); - } + running_ = true; + + // 初始化所有组件 + for (auto& pair : components_) { + if (!pair.second->isInitialized()) { + pair.second->init(); + pair.second->setInitialized(true); + } + } + + for (auto& child : children_) { + child->onEnter(); + } } /** * @brief 节点退出时的回调 - * - * 标记节点为非运行状态,并递归调用所有子节点的onExit */ void Node::onExit() { - running_ = false; - for (auto &child : children_) { - child->onExit(); - } + running_ = false; + for (auto& child : children_) { + child->onExit(); + } } /** * @brief 更新回调 * @param dt 帧间隔时间(秒) - * - * 先调用节点自身的更新逻辑,再更新所有子节点 */ void Node::onUpdate(float dt) { - onUpdateNode(dt); + onUpdateNode(dt); - // Update children - for (auto &child : children_) { - child->onUpdate(dt); - } + // 更新所有启用的组件 + for (auto& pair : components_) { + if (pair.second->isEnabled()) { + pair.second->update(dt); + } + } + + // 更新子节点 + for (auto& child : children_) { + child->onUpdate(dt); + } } /** * @brief 渲染回调 * @param renderer 渲染后端引用 - * - * 如果可见则绘制自身,然后递归渲染所有子节点 */ -void Node::onRender(RenderBackend &renderer) { - if (!visible_) - return; +void Node::onRender(RenderBackend& renderer) { + if (!visible_) + return; - renderer.pushTransform(getLocalTransform()); + renderer.pushTransform(getLocalTransform()); - onDraw(renderer); + onDraw(renderer); - for (auto &child : children_) { - child->onRender(renderer); - } + for (auto& child : children_) { + child->onRender(renderer); + } - renderer.popTransform(); + renderer.popTransform(); } /** * @brief 附加到场景时的回调 * @param scene 所属场景指针 - * - * 设置场景引用并递归通知所有子节点 */ -void Node::onAttachToScene(Scene *scene) { - scene_ = scene; +void Node::onAttachToScene(Scene* scene) { + scene_ = scene; - for (auto &child : children_) { - child->onAttachToScene(scene); - } + for (auto& child : children_) { + child->onAttachToScene(scene); + } } /** * @brief 从场景分离时的回调 - * - * 清除场景引用并递归通知所有子节点 */ void Node::onDetachFromScene() { - scene_ = nullptr; - for (auto &child : children_) { - child->onDetachFromScene(); - } + scene_ = nullptr; + for (auto& child : children_) { + child->onDetachFromScene(); + } } /** * @brief 获取节点边界矩形 * @return 节点的边界矩形 - * - * 默认返回以位置为中心的空矩形,子类应重写此方法 */ -Rect Node::getBounds() const { return Rect(position_.x, position_.y, 0, 0); } +Rect Node::getBounds() const { + Vec2 pos = getPosition(); + return Rect(pos.x, pos.y, 0, 0); +} /** * @brief 更新节点 * @param dt 帧间隔时间(秒) - * - * 调用onUpdate进行更新 */ -void Node::update(float dt) { onUpdate(dt); } +void Node::update(float dt) { + onUpdate(dt); +} /** * @brief 渲染节点 * @param renderer 渲染后端引用 - * - * 如果需要则对子节点排序,然后调用onRender进行渲染 */ -void Node::render(RenderBackend &renderer) { - if (childrenOrderDirty_) { - sortChildren(); - } - onRender(renderer); +void Node::render(RenderBackend& renderer) { + if (childrenOrderDirty_) { + sortChildren(); + } + onRender(renderer); } /** * @brief 对子节点按Z序排序 - * - * 小数组使用插入排序,大数组使用标准排序以优化性能 */ void Node::sortChildren() { - // 使用插入排序优化小范围更新场景 - // 插入排序在大部分已有序的情况下性能接近O(n) - size_t n = children_.size(); - if (n <= 1) { - childrenOrderDirty_ = false; - return; - } - - // 小数组使用插入排序,大数组使用std::sort - if (n < 32) { - // 插入排序 - for (size_t i = 1; i < n; ++i) { - auto key = children_[i]; - int keyZOrder = key->getZOrder(); - int j = static_cast(i) - 1; - - while (j >= 0 && children_[j]->getZOrder() > keyZOrder) { - children_[j + 1] = children_[j]; - --j; - } - children_[j + 1] = key; + size_t n = children_.size(); + if (n <= 1) { + childrenOrderDirty_ = false; + return; } - } else { - // 大数组使用标准排序 - std::sort(children_.begin(), children_.end(), - [](const Ptr &a, const Ptr &b) { - return a->getZOrder() < b->getZOrder(); - }); - } - childrenOrderDirty_ = false; + if (n < 32) { + for (size_t i = 1; i < n; ++i) { + auto key = children_[i]; + int keyZOrder = key->getZOrder(); + int j = static_cast(i) - 1; + + while (j >= 0 && children_[j]->getZOrder() > keyZOrder) { + children_[j + 1] = children_[j]; + --j; + } + children_[j + 1] = key; + } + } else { + std::sort(children_.begin(), children_.end(), + [](const Ptr& a, const Ptr& b) { + return a->getZOrder() < b->getZOrder(); + }); + } + + childrenOrderDirty_ = false; } /** * @brief 收集渲染命令 * @param commands 渲染命令输出向量 * @param parentZOrder 父节点的Z序 - * - * 递归收集当前节点和所有子节点的渲染命令 */ -void Node::collectRenderCommands(std::vector &commands, +void Node::collectRenderCommands(std::vector& commands, int parentZOrder) { - if (!visible_) - return; + if (!visible_) + return; - // 计算累积 Z 序 - int accumulatedZOrder = parentZOrder + zOrder_; + int accumulatedZOrder = parentZOrder + zOrder_; - // 生成当前节点的渲染命令 - generateRenderCommand(commands, accumulatedZOrder); + generateRenderCommand(commands, accumulatedZOrder); - // 递归收集子节点的渲染命令 - // 注意:这里假设子节点已经按 Z 序排序 - for (auto &child : children_) { - child->collectRenderCommands(commands, accumulatedZOrder); - } + // 让组件也生成渲染命令 + for (auto& pair : components_) { + if (pair.second->isEnabled()) { + // 组件的渲染命令收集需要通过 RenderQueue + // 这里暂时跳过,使用 collectRenderCommandsToQueue 方法 + } + } + + for (auto& child : children_) { + child->collectRenderCommands(commands, accumulatedZOrder); + } +} + +/** + * @brief 收集渲染命令到队列 + * @param queue 渲染队列引用 + */ +void Node::collectRenderCommandsToQueue(RenderQueue& queue) { + if (!visible_) + return; + + // 生成当前节点的渲染命令 + generateRenderCommandToQueue(queue); + + // 让组件也生成渲染命令 + for (auto& pair : components_) { + if (pair.second->isEnabled()) { + pair.second->collectRenderCommands(queue); + } + } + + // 递归收集子节点的渲染命令 + for (auto& child : children_) { + child->collectRenderCommandsToQueue(queue); + } } } // namespace extra2d diff --git a/Extra2D/src/scene/scene.cpp b/Extra2D/src/scene/scene.cpp index db7572a..ed9014a 100644 --- a/Extra2D/src/scene/scene.cpp +++ b/Extra2D/src/scene/scene.cpp @@ -1,10 +1,10 @@ #include #include -#include +#include +#include #include #include - namespace extra2d { /** @@ -54,7 +54,6 @@ void Scene::renderScene(RenderBackend &renderer) { if (!isVisible()) return; - // Begin frame with background color renderer.beginFrame(backgroundColor_); renderContent(renderer); renderer.endFrame(); @@ -65,7 +64,6 @@ void Scene::renderScene(RenderBackend &renderer) { * @param renderer 渲染后端引用 * * 批量更新节点变换,开始精灵批处理并渲染 - * 注意:视图投影矩阵由 Application 通过 CameraService 设置 */ void Scene::renderContent(RenderBackend &renderer) { if (!isVisible()) @@ -116,10 +114,22 @@ void Scene::collectRenderCommands(std::vector &commands, if (!isVisible()) return; - // 从场景的子节点开始收集渲染命令 Node::collectRenderCommands(commands, parentZOrder); } +/** + * @brief 收集渲染命令到队列 + * @param queue 渲染队列引用 + * + * 如果场景不可见则直接返回,否则从场景的子节点开始收集渲染命令 + */ +void Scene::collectRenderCommandsToQueue(RenderQueue &queue) { + if (!isVisible()) + return; + + Node::collectRenderCommandsToQueue(queue); +} + /** * @brief 创建场景对象 * @return 新创建的场景智能指针