diff --git a/Extra2D/include/extra2d/graphics/backends/backend_factory.h b/Extra2D/include/extra2d/graphics/backends/backend_factory.h new file mode 100644 index 0000000..2b1a2e5 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/backends/backend_factory.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include + +namespace extra2d { + +/** + * @brief 渲染后端类型枚举 + */ +enum class BackendType { + OpenGL, // OpenGL 4.x + Vulkan, // Vulkan 1.x + Metal, // Metal (macOS/iOS) + D3D11, // Direct3D 11 + D3D12, // Direct3D 12 + OpenGLES, // OpenGL ES (移动平台) + Count +}; + +/** + * @brief 后端工厂类,用于创建渲染后端实例 + */ +class BackendFactory { +public: + /** + * @brief 获取后端工厂单例 + * @return 后端工厂实例引用 + */ + static BackendFactory& getInstance(); + + /** + * @brief 创建渲染后端 + * @param type 后端类型 + * @return 渲染后端实例 + */ + UniquePtr createBackend(BackendType type); + + /** + * @brief 创建默认渲染后端 + * @return 默认渲染后端实例 + */ + UniquePtr createDefaultBackend(); + + /** + * @brief 检查后端是否可用 + * @param type 后端类型 + * @return 可用返回true,否则返回false + */ + bool isBackendAvailable(BackendType type) const; + + /** + * @brief 获取当前平台推荐的后端类型 + * @return 推荐的后端类型 + */ + BackendType getRecommendedBackend() const; + + /** + * @brief 获取后端类型名称 + * @param type 后端类型 + * @return 后端类型名称字符串 + */ + const char* getBackendName(BackendType type) const; + + /** + * @brief 从名称解析后端类型 + * @param name 后端类型名称 + * @return 后端类型,如果未知则返回OpenGL + */ + BackendType parseBackendType(const char* name) const; + +private: + BackendFactory() = default; + ~BackendFactory() = default; + BackendFactory(const BackendFactory&) = delete; + BackendFactory& operator=(const BackendFactory&) = delete; +}; + +} // 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 new file mode 100644 index 0000000..61c8288 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/backends/opengl/gl_font_atlas.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +#include +#include + +#include +#include + +namespace extra2d { + +// ============================================================================ +// OpenGL 字体图集实现 (使用 STB 库) +// ============================================================================ +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; + }; + + struct PackedCharData { + stbtt_packedchar packedChar; + bool valid; + }; + + bool useSDF_; + int fontSize_; + + Ptr texture_; + std::unordered_map glyphs_; + std::unordered_map packedChars_; + float lineHeight_; + float ascent_; + float descent_; + float lineGap_; + + // 字体数据 + std::vector fontData_; + stbtt_fontinfo fontInfo_; + float scale_; + + // 图集打包参数 + static constexpr int ATLAS_WIDTH = 512; + static constexpr int ATLAS_HEIGHT = 512; + + // 初始化字体 + bool initFont(const std::string& filepath); + // 渲染字形到图集 + bool renderGlyph(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/opengl/gl_renderer.h b/Extra2D/include/extra2d/graphics/backends/opengl/gl_renderer.h similarity index 98% rename from Extra2D/include/extra2d/graphics/opengl/gl_renderer.h rename to Extra2D/include/extra2d/graphics/backends/opengl/gl_renderer.h index 4c369ea..37f696c 100644 --- a/Extra2D/include/extra2d/graphics/opengl/gl_renderer.h +++ b/Extra2D/include/extra2d/graphics/backends/opengl/gl_renderer.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/Extra2D/include/extra2d/graphics/opengl/gl_shader.h b/Extra2D/include/extra2d/graphics/backends/opengl/gl_shader.h similarity index 100% rename from Extra2D/include/extra2d/graphics/opengl/gl_shader.h rename to Extra2D/include/extra2d/graphics/backends/opengl/gl_shader.h diff --git a/Extra2D/include/extra2d/graphics/backends/opengl/gl_sprite_batch.h b/Extra2D/include/extra2d/graphics/backends/opengl/gl_sprite_batch.h new file mode 100644 index 0000000..2679e6b --- /dev/null +++ b/Extra2D/include/extra2d/graphics/backends/opengl/gl_sprite_batch.h @@ -0,0 +1,66 @@ +#pragma once + +#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 draw(const Texture& texture, const SpriteData& data); + + // 批量绘制(用于文本渲染优化) + void drawBatch(const Texture& texture, const std::vector& sprites); + + // 获取绘制调用次数 + uint32_t getDrawCallCount() const { return drawCallCount_; } + +private: + // OpenGL 对象 + GLuint vao_; + GLuint vbo_; + GLuint 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_; + + // 内部方法 + void flush(); + void submitBatch(); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/opengl/gl_texture.h b/Extra2D/include/extra2d/graphics/backends/opengl/gl_texture.h similarity index 100% rename from Extra2D/include/extra2d/graphics/opengl/gl_texture.h rename to Extra2D/include/extra2d/graphics/backends/opengl/gl_texture.h diff --git a/Extra2D/include/extra2d/graphics/backends/vulkan/vk_renderer.h b/Extra2D/include/extra2d/graphics/backends/vulkan/vk_renderer.h new file mode 100644 index 0000000..608dd8d --- /dev/null +++ b/Extra2D/include/extra2d/graphics/backends/vulkan/vk_renderer.h @@ -0,0 +1,78 @@ +#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/sprite_batch.h b/Extra2D/include/extra2d/graphics/batch/sprite_batch.h new file mode 100644 index 0000000..838f286 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/batch/sprite_batch.h @@ -0,0 +1,121 @@ +#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/core/render_target.h b/Extra2D/include/extra2d/graphics/core/render_target.h index 47433a5..c7dca31 100644 --- a/Extra2D/include/extra2d/graphics/core/render_target.h +++ b/Extra2D/include/extra2d/graphics/core/render_target.h @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include diff --git a/Extra2D/include/extra2d/graphics/opengl/gl_font_atlas.h b/Extra2D/include/extra2d/graphics/opengl/gl_font_atlas.h deleted file mode 100644 index 5b524aa..0000000 --- a/Extra2D/include/extra2d/graphics/opengl/gl_font_atlas.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// OpenGL 字体图集实现 - 使用 stb_rect_pack 进行矩形打包 -// ============================================================================ -class GLFontAtlas : public FontAtlas { -public: - GLFontAtlas(const std::string &filepath, int fontSize, bool useSDF = false); - ~GLFontAtlas(); - - // 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 ascent_ - descent_ + lineGap_; } - Vec2 measureText(const std::string &text) override; - bool isSDF() const override { return useSDF_; } - -private: - // 图集配置 - 增大尺寸以支持更多字符 - static constexpr int ATLAS_WIDTH = 1024; - static constexpr int ATLAS_HEIGHT = 1024; - static constexpr int PADDING = 2; // 字形之间的间距 - - int fontSize_; - bool useSDF_; - mutable std::unique_ptr texture_; - mutable std::unordered_map glyphs_; - - // stb_rect_pack 上下文 - mutable stbrp_context packContext_; - mutable std::vector packNodes_; - mutable int currentY_; - - std::vector fontData_; - stbtt_fontinfo fontInfo_; - float scale_; - float ascent_; - float descent_; - float lineGap_; - - // 预分配字形位图缓冲区,避免每次动态分配 - mutable std::vector glyphBitmapCache_; - mutable std::vector glyphRgbaCache_; - - void createAtlas(); - void cacheGlyph(char32_t codepoint) const; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/opengl/gl_sprite_batch.h b/Extra2D/include/extra2d/graphics/opengl/gl_sprite_batch.h deleted file mode 100644 index 1609a2f..0000000 --- a/Extra2D/include/extra2d/graphics/opengl/gl_sprite_batch.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace extra2d { - -// ============================================================================ -// OpenGL 精灵批渲染器 - 优化版本 -// ============================================================================ -class GLSpriteBatch { -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; - - struct Vertex { - glm::vec2 position; - glm::vec2 texCoord; - glm::vec4 color; - }; - - struct SpriteData { - glm::vec2 position; - glm::vec2 size; - glm::vec2 texCoordMin; - glm::vec2 texCoordMax; - glm::vec4 color; - float rotation; - glm::vec2 anchor; - bool isSDF = false; - }; - - GLSpriteBatch(); - ~GLSpriteBatch(); - - bool init(); - void shutdown(); - - void begin(const glm::mat4 &viewProjection); - void draw(const Texture &texture, const SpriteData &data); - void end(); - - // 批量绘制接口 - 用于自动批处理 - void drawBatch(const Texture &texture, - const std::vector &sprites); - - // 立即绘制(不缓存) - void drawImmediate(const Texture &texture, const SpriteData &data); - - // 统计 - uint32_t getDrawCallCount() const { return drawCallCount_; } - uint32_t getSpriteCount() const { return spriteCount_; } - uint32_t getBatchCount() const { return batchCount_; } - - // 检查是否需要刷新 - bool needsFlush(const Texture &texture, bool isSDF) const; - -private: - GLuint vao_; - GLuint vbo_; - GLuint ibo_; - GLShader shader_; - - // 使用固定大小数组减少内存分配 - std::array vertexBuffer_; - size_t vertexCount_; - - const Texture *currentTexture_; - bool currentIsSDF_; - glm::mat4 viewProjection_; - - // 缓存上一帧的 viewProjection,避免重复设置 - glm::mat4 cachedViewProjection_; - bool viewProjectionDirty_ = true; - - uint32_t drawCallCount_; - uint32_t spriteCount_; - uint32_t batchCount_; - - void flush(); - void setupShader(); - - // 添加顶点到缓冲区 - void addVertices(const SpriteData &data); -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader/shader_hot_reloader.h b/Extra2D/include/extra2d/graphics/shader/shader_hot_reloader.h index d5ddebc..bd21d74 100644 --- a/Extra2D/include/extra2d/graphics/shader/shader_hot_reloader.h +++ b/Extra2D/include/extra2d/graphics/shader/shader_hot_reloader.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #ifdef _WIN32 @@ -17,118 +16,113 @@ namespace extra2d { // 文件变化事件 // ============================================================================ struct FileChangeEvent { - std::string filepath; - - enum class Type { - Created, - Modified, - Deleted, - Renamed - } type; - - uint64_t timestamp = 0; + std::string filepath; + + enum class Type { Created, Modified, Deleted, Renamed } type; + + uint64_t timestamp = 0; }; // ============================================================================ // 文件变化回调 // ============================================================================ -using FileChangeCallback = std::function; +using FileChangeCallback = std::function; // ============================================================================ // Shader热重载管理器 // ============================================================================ class ShaderHotReloader { public: - /** - * @brief 获取单例实例 - * @return 热重载管理器实例引用 - */ - static ShaderHotReloader& getInstance(); + /** + * @brief 获取单例实例 + * @return 热重载管理器实例引用 + */ + static ShaderHotReloader &getInstance(); - /** - * @brief 初始化热重载系统 - * @return 初始化成功返回true,失败返回false - */ - bool init(); + /** + * @brief 初始化热重载系统 + * @return 初始化成功返回true,失败返回false + */ + bool init(); - /** - * @brief 关闭热重载系统 - */ - void shutdown(); + /** + * @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 注册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 取消监视 + * @param shaderName Shader名称 + */ + void unwatch(const std::string &shaderName); - /** - * @brief 更新文件监视(在主循环中调用) - */ - void update(); + /** + * @brief 更新文件监视(在主循环中调用) + */ + void update(); - /** - * @brief 启用/禁用热重载 - * @param enabled 是否启用 - */ - void setEnabled(bool enabled); + /** + * @brief 启用/禁用热重载 + * @param enabled 是否启用 + */ + void setEnabled(bool enabled); - /** - * @brief 检查是否启用 - * @return 启用返回true,否则返回false - */ - bool isEnabled() const { return enabled_; } + /** + * @brief 检查是否启用 + * @return 启用返回true,否则返回false + */ + bool isEnabled() const { return enabled_; } - /** - * @brief 检查是否已初始化 - * @return 已初始化返回true,否则返回false - */ - bool isInitialized() const { return initialized_; } + /** + * @brief 检查是否已初始化 + * @return 已初始化返回true,否则返回false + */ + bool isInitialized() const { return initialized_; } private: - ShaderHotReloader() = default; - ~ShaderHotReloader() = default; - ShaderHotReloader(const ShaderHotReloader&) = delete; - ShaderHotReloader& operator=(const ShaderHotReloader&) = delete; + ShaderHotReloader() = default; + ~ShaderHotReloader() = default; + ShaderHotReloader(const ShaderHotReloader &) = delete; + ShaderHotReloader &operator=(const ShaderHotReloader &) = delete; - bool enabled_ = false; - bool initialized_ = false; + bool enabled_ = false; + bool initialized_ = false; - struct WatchInfo { - std::vector filePaths; - FileChangeCallback callback; - std::unordered_map modifiedTimes; - }; - std::unordered_map watchMap_; + 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; + HANDLE watchHandle_ = nullptr; + std::vector buffer_; + std::string watchDir_; + bool watching_ = false; #endif - /** - * @brief 轮询检查文件变化 - */ - void pollChanges(); + /** + * @brief 轮询检查文件变化 + */ + void pollChanges(); - /** - * @brief 获取文件修改时间 - * @param filepath 文件路径 - * @return 修改时间戳 - */ - static uint64_t getFileModifiedTime(const std::string& filepath); + /** + * @brief 获取文件修改时间 + * @param filepath 文件路径 + * @return 修改时间戳 + */ + static uint64_t getFileModifiedTime(const std::string &filepath); }; // 便捷宏 diff --git a/Extra2D/include/extra2d/graphics/shader/shader_manager.h b/Extra2D/include/extra2d/graphics/shader/shader_manager.h index 7e5a5c2..e4d6efa 100644 --- a/Extra2D/include/extra2d/graphics/shader/shader_manager.h +++ b/Extra2D/include/extra2d/graphics/shader/shader_manager.h @@ -177,6 +177,14 @@ public: */ bool loadBuiltinShaders(); + /** + * @brief 从JSON元数据文件加载Shader(多后端支持) + * @param jsonPath JSON元数据文件路径 + * @param name Shader名称 + * @return 加载的Shader实例 + */ + Ptr loadFromMetadata(const std::string& jsonPath, const std::string& name); + // ------------------------------------------------------------------------ // 工具方法 // ------------------------------------------------------------------------ diff --git a/Extra2D/include/extra2d/graphics/texture/texture_atlas.h b/Extra2D/include/extra2d/graphics/texture/texture_atlas.h index 975ef58..8fca067 100644 --- a/Extra2D/include/extra2d/graphics/texture/texture_atlas.h +++ b/Extra2D/include/extra2d/graphics/texture/texture_atlas.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/Extra2D/include/extra2d/graphics/texture/texture_pool.h b/Extra2D/include/extra2d/graphics/texture/texture_pool.h index 61edfdc..1bebf67 100644 --- a/Extra2D/include/extra2d/graphics/texture/texture_pool.h +++ b/Extra2D/include/extra2d/graphics/texture/texture_pool.h @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -13,7 +12,6 @@ #include #include #include -#include namespace extra2d { @@ -25,161 +23,153 @@ class RenderBackend; // 纹理加载选项 // ============================================================================ struct TextureLoadOptions { - bool generateMipmaps = true; // 是否生成 mipmaps - bool sRGB = true; // 是否使用 sRGB 色彩空间 - bool premultiplyAlpha = false; // 是否预乘 Alpha - PixelFormat preferredFormat = PixelFormat::RGBA8; // 首选像素格式 + bool generateMipmaps = true; // 是否生成 mipmaps + bool sRGB = true; // 是否使用 sRGB 色彩空间 + bool premultiplyAlpha = false; // 是否预乘 Alpha + PixelFormat preferredFormat = PixelFormat::RGBA8; // 首选像素格式 }; // ============================================================================ // 纹理键 - 用于唯一标识纹理缓存条目 // ============================================================================ struct TextureKey { - std::string path; // 纹理文件路径 - Rect region; // 纹理区域(用于纹理图集) + std::string path; // 纹理文件路径 + Rect region; // 纹理区域(用于纹理图集) - /** - * @brief 默认构造函数 - */ - TextureKey() = default; + /** + * @brief 默认构造函数 + */ + TextureKey() = default; - /** - * @brief 构造函数(仅路径) - * @param p 纹理文件路径 - */ - explicit TextureKey(const std::string& p) : path(p), region(Rect::Zero()) {} + /** + * @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 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 path == other.path && region == other.region; + } - /** - * @brief 不等比较运算符 - * @param other 另一个 TextureKey - * @return 是否不等 - */ - bool operator!=(const TextureKey& other) const { - return !(*this == other); - } + /** + * @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); + /** + * @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; - } + // 组合哈希值 + 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; // 最后访问时间戳 + 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 默认构造函数 + */ + 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 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 另一个条目 + */ + 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; + /** + * @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; + // 禁止拷贝 + TexturePoolEntry(const TexturePoolEntry &) = delete; + TexturePoolEntry &operator=(const TexturePoolEntry &) = delete; - /** - * @brief 更新最后访问时间 - */ - void touch() const { lastAccessTime = getCurrentTime(); } + /** + * @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()); - } + /** + * @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()); + } }; // ============================================================================ @@ -187,144 +177,143 @@ struct TexturePoolEntry { // ============================================================================ class TextureRef { public: - /** - * @brief 默认构造函数 - */ - TextureRef() : texture_(nullptr), entry_(nullptr), mutex_(nullptr) {} + /** + * @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 纹理对象 + * @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 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(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 - */ - TextureRef(TextureRef&& other) noexcept - : texture_(std::move(other.texture_)) - , entry_(other.entry_) - , mutex_(other.mutex_) { - other.entry_ = nullptr; - other.mutex_ = nullptr; + /** + * @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 析构函数 - */ - ~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 重置引用 + */ + 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 移动赋值运算符 - * @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 获取纹理对象 + * @return 纹理对象指针 + */ + Texture *get() const { return texture_.get(); } - /** - * @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 纹理对象智能指针 + */ + Ptr getPtr() const { return texture_; } - /** - * @brief 获取纹理对象 - * @return 纹理对象指针 - */ - Texture* get() const { return texture_.get(); } + /** + * @brief 检查是否有效 + * @return 是否有效 + */ + bool valid() const { return texture_ != nullptr; } - /** - * @brief 获取纹理对象(智能指针) - * @return 纹理对象智能指针 - */ - Ptr getPtr() const { return texture_; } + /** + * @brief 布尔转换运算符 + */ + explicit operator bool() const { return valid(); } - /** - * @brief 检查是否有效 - * @return 是否有效 - */ - bool valid() const { return texture_ != nullptr; } + /** + * @brief 箭头运算符 + */ + Texture *operator->() const { return texture_.get(); } - /** - * @brief 布尔转换运算符 - */ - explicit operator bool() const { return valid(); } - - /** - * @brief 箭头运算符 - */ - Texture* operator->() const { return texture_.get(); } - - /** - * @brief 解引用运算符 - */ - Texture& operator*() const { return *texture_; } + /** + * @brief 解引用运算符 + */ + Texture &operator*() const { return *texture_; } private: - Ptr texture_; - TexturePoolEntry* entry_; - std::mutex* mutex_; + Ptr texture_; + TexturePoolEntry *entry_; + std::mutex *mutex_; }; // ============================================================================ @@ -338,232 +327,235 @@ private: // ============================================================================ 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; // 淘汰次数 - }; + // ======================================================================== + // 统计信息 + // ======================================================================== + 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 默认构造函数 + */ + TexturePool(); - /** - * @brief 构造函数 - * @param scene 场景指针 - * @param maxMemoryUsage 最大内存使用量(0 表示无限制) - */ - explicit TexturePool(Scene* scene, size_t maxMemoryUsage = 0); + /** + * @brief 构造函数 + * @param scene 场景指针 + * @param maxMemoryUsage 最大内存使用量(0 表示无限制) + */ + explicit TexturePool(Scene *scene, size_t maxMemoryUsage = 0); - /** - * @brief 析构函数 - */ - ~TexturePool(); + /** + * @brief 析构函数 + */ + ~TexturePool(); - // 禁止拷贝 - TexturePool(const TexturePool&) = delete; - TexturePool& operator=(const TexturePool&) = delete; + // 禁止拷贝 + 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 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 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& region, - 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 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 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& region, - 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 是否成功 + */ + bool addRef(const TextureKey &key); - /** - * @brief 减少引用计数 - * @param key 纹理键 - * @return 减少后的引用计数 - */ - uint32_t release(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 引用计数 + */ + uint32_t getRefCount(const TextureKey &key) const; - // ======================================================================== - // 缓存管理 - // ======================================================================== + // ======================================================================== + // 缓存管理 + // ======================================================================== - /** - * @brief 检查纹理是否已缓存 - * @param key 纹理键 - * @return 是否已缓存 - */ - bool isCached(const TextureKey& key) const; + /** + * @brief 检查纹理是否已缓存 + * @param key 纹理键 + * @return 是否已缓存 + */ + bool isCached(const TextureKey &key) const; - /** - * @brief 从缓存中移除纹理 - * @param key 纹理键 - * @return 是否成功 - */ - bool removeFromCache(const TextureKey& key); + /** + * @brief 从缓存中移除纹理 + * @param key 纹理键 + * @return 是否成功 + */ + bool removeFromCache(const TextureKey &key); - /** - * @brief 垃圾回收(移除引用计数为 0 的纹理) - * @return 移除的纹理数量 - */ - size_t collectGarbage(); + /** + * @brief 垃圾回收(移除引用计数为 0 的纹理) + * @return 移除的纹理数量 + */ + size_t collectGarbage(); - /** - * @brief 清空所有缓存 - */ - void clear(); + /** + * @brief 清空所有缓存 + */ + void clear(); - // ======================================================================== - // 内存管理 - // ======================================================================== + // ======================================================================== + // 内存管理 + // ======================================================================== - /** - * @brief 获取当前内存使用量 - * @return 内存使用量(字节) - */ - size_t getMemoryUsage() const; + /** + * @brief 获取当前内存使用量 + * @return 内存使用量(字节) + */ + size_t getMemoryUsage() const; - /** - * @brief 设置最大内存使用量 - * @param maxMemory 最大内存使用量(0 表示无限制) - */ - void setMaxMemoryUsage(size_t maxMemory); + /** + * @brief 设置最大内存使用量 + * @param maxMemory 最大内存使用量(0 表示无限制) + */ + void setMaxMemoryUsage(size_t maxMemory); - /** - * @brief 获取最大内存使用量 - * @return 最大内存使用量 - */ - size_t getMaxMemoryUsage() const { return maxMemoryUsage_; } + /** + * @brief 获取最大内存使用量 + * @return 最大内存使用量 + */ + size_t getMaxMemoryUsage() const { return maxMemoryUsage_; } - /** - * @brief 执行 LRU 淘汰 - * @param targetMemory 目标内存使用量 - * @return 淘汰的纹理数量 - */ - size_t evictLRU(size_t targetMemory = 0); + /** + * @brief 执行 LRU 淘汰 + * @param targetMemory 目标内存使用量 + * @return 淘汰的纹理数量 + */ + size_t evictLRU(size_t targetMemory = 0); - // ======================================================================== - // 统计信息 - // ======================================================================== + // ======================================================================== + // 统计信息 + // ======================================================================== - /** - * @brief 获取统计信息 - * @return 统计信息 - */ - Stats getStats() const; + /** + * @brief 获取统计信息 + * @return 统计信息 + */ + Stats getStats() const; - /** - * @brief 重置统计信息 - */ - void resetStats(); + /** + * @brief 重置统计信息 + */ + void resetStats(); private: - /** - * @brief 计算纹理内存大小 - * @param texture 纹理对象 - * @return 内存大小(字节) - */ - static size_t calculateTextureMemory(const Texture* texture); + /** + * @brief 计算纹理内存大小 + * @param texture 纹理对象 + * @return 内存大小(字节) + */ + static size_t calculateTextureMemory(const Texture *texture); - /** - * @brief 检查是否需要淘汰 - * @return 是否需要淘汰 - */ - bool needsEviction() const; + /** + * @brief 检查是否需要淘汰 + * @return 是否需要淘汰 + */ + bool needsEviction() const; - /** - * @brief 尝试自动淘汰 - */ - void tryAutoEvict(); + /** + * @brief 尝试自动淘汰 + */ + void tryAutoEvict(); - Scene* scene_; // 场景指针 - mutable std::mutex mutex_; // 互斥锁 - std::unordered_map cache_; // 纹理缓存 + Scene *scene_; // 场景指针 + mutable std::mutex mutex_; // 互斥锁 + std::unordered_map + cache_; // 纹理缓存 - size_t maxMemoryUsage_; // 最大内存使用量 - size_t currentMemoryUsage_; // 当前内存使用量 + size_t maxMemoryUsage_; // 最大内存使用量 + size_t currentMemoryUsage_; // 当前内存使用量 - // 统计信息 - mutable std::atomic cacheHits_; - mutable std::atomic cacheMisses_; - mutable std::atomic evictionCount_; + // 统计信息 + mutable std::atomic cacheHits_; + mutable std::atomic cacheMisses_; + mutable std::atomic evictionCount_; }; -} // namespace extra2d +} // namespace extra2d diff --git a/Extra2D/shaders/backends/opengl/builtin/shape.frag b/Extra2D/shaders/backends/opengl/builtin/shape.frag new file mode 100644 index 0000000..1986612 --- /dev/null +++ b/Extra2D/shaders/backends/opengl/builtin/shape.frag @@ -0,0 +1,10 @@ +#version 300 es +precision highp float; + +in vec4 v_color; + +out vec4 fragColor; + +void main() { + fragColor = v_color; +} diff --git a/Extra2D/shaders/backends/opengl/builtin/shape.vert b/Extra2D/shaders/backends/opengl/builtin/shape.vert new file mode 100644 index 0000000..b010cde --- /dev/null +++ b/Extra2D/shaders/backends/opengl/builtin/shape.vert @@ -0,0 +1,14 @@ +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec4 a_color; + +uniform mat4 u_viewProjection; + +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * vec4(a_position, 0.0, 1.0); + v_color = a_color; +} diff --git a/Extra2D/shaders/backends/opengl/builtin/sprite.frag b/Extra2D/shaders/backends/opengl/builtin/sprite.frag new file mode 100644 index 0000000..0767df0 --- /dev/null +++ b/Extra2D/shaders/backends/opengl/builtin/sprite.frag @@ -0,0 +1,20 @@ +#version 300 es +precision highp float; + +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_opacity; + +out vec4 fragColor; + +void main() { + vec4 texColor = texture(u_texture, v_texCoord); + fragColor = texColor * v_color; + fragColor.a *= u_opacity; + + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/Extra2D/shaders/backends/opengl/builtin/sprite.vert b/Extra2D/shaders/backends/opengl/builtin/sprite.vert new file mode 100644 index 0000000..f7a9adb --- /dev/null +++ b/Extra2D/shaders/backends/opengl/builtin/sprite.vert @@ -0,0 +1,18 @@ +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_viewProjection; +uniform mat4 u_model; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} diff --git a/Extra2D/shaders/common/color.glsl b/Extra2D/shaders/backends/opengl/common/color.glsl similarity index 100% rename from Extra2D/shaders/common/color.glsl rename to Extra2D/shaders/backends/opengl/common/color.glsl diff --git a/Extra2D/shaders/common/math.glsl b/Extra2D/shaders/backends/opengl/common/math.glsl similarity index 100% rename from Extra2D/shaders/common/math.glsl rename to Extra2D/shaders/backends/opengl/common/math.glsl diff --git a/Extra2D/shaders/builtin/font.shader b/Extra2D/shaders/builtin/font.shader deleted file mode 100644 index c1bc8ec..0000000 --- a/Extra2D/shaders/builtin/font.shader +++ /dev/null @@ -1,56 +0,0 @@ -// ============================================ -// Extra2D Combined Shader File -// Name: font -// Category: builtin -// Version: 1.0 -// ============================================ - -#meta -{ - "name": "font", - "category": "builtin", - "author": "Extra2D Team", - "description": "字体渲染Shader,支持SDF" -} - -#vertex -#version 300 es -precision highp float; - -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; - -uniform mat4 u_viewProjection; -uniform mat4 u_model; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; - v_color = a_color; -} - -#fragment -#version 300 es -precision highp float; - -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_smoothing; - -out vec4 fragColor; - -void main() { - float dist = texture(u_texture, v_texCoord).r; - float alpha = smoothstep(0.5 - u_smoothing, 0.5 + u_smoothing, dist); - fragColor = vec4(v_color.rgb, v_color.a * alpha); - - if (fragColor.a < 0.01) { - discard; - } -} diff --git a/Extra2D/shaders/builtin/particle.shader b/Extra2D/shaders/builtin/particle.shader deleted file mode 100644 index 4b1c1d4..0000000 --- a/Extra2D/shaders/builtin/particle.shader +++ /dev/null @@ -1,53 +0,0 @@ -// ============================================ -// Extra2D Combined Shader File -// Name: particle -// Category: builtin -// Version: 1.0 -// ============================================ - -#meta -{ - "name": "particle", - "category": "builtin", - "author": "Extra2D Team", - "description": "粒子渲染Shader" -} - -#vertex -#version 300 es -precision highp float; - -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; - -uniform mat4 u_viewProjection; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; - v_color = a_color; -} - -#fragment -#version 300 es -precision highp float; - -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; - -out vec4 fragColor; - -void main() { - vec4 texColor = texture(u_texture, v_texCoord); - fragColor = texColor * v_color; - - if (fragColor.a < 0.01) { - discard; - } -} diff --git a/Extra2D/shaders/builtin/postprocess.shader b/Extra2D/shaders/builtin/postprocess.shader deleted file mode 100644 index 619f997..0000000 --- a/Extra2D/shaders/builtin/postprocess.shader +++ /dev/null @@ -1,42 +0,0 @@ -// ============================================ -// Extra2D Combined Shader File -// Name: postprocess -// Category: builtin -// Version: 1.0 -// ============================================ - -#meta -{ - "name": "postprocess", - "category": "builtin", - "author": "Extra2D Team", - "description": "后处理基础Shader" -} - -#vertex -#version 300 es -precision highp float; - -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; - -out vec2 v_texCoord; - -void main() { - gl_Position = vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; -} - -#fragment -#version 300 es -precision highp float; - -in vec2 v_texCoord; - -uniform sampler2D u_texture; - -out vec4 fragColor; - -void main() { - fragColor = texture(u_texture, v_texCoord); -} diff --git a/Extra2D/shaders/builtin/shape.shader b/Extra2D/shaders/builtin/shape.shader deleted file mode 100644 index 48809f2..0000000 --- a/Extra2D/shaders/builtin/shape.shader +++ /dev/null @@ -1,42 +0,0 @@ -// ============================================ -// Extra2D Combined Shader File -// Name: shape -// Category: builtin -// Version: 1.0 -// ============================================ - -#meta -{ - "name": "shape", - "category": "builtin", - "author": "Extra2D Team", - "description": "形状渲染Shader,支持顶点颜色批处理" -} - -#vertex -#version 300 es -precision highp float; - -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec4 a_color; - -uniform mat4 u_viewProjection; - -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * vec4(a_position, 0.0, 1.0); - v_color = a_color; -} - -#fragment -#version 300 es -precision highp float; - -in vec4 v_color; - -out vec4 fragColor; - -void main() { - fragColor = v_color; -} diff --git a/Extra2D/shaders/builtin/sprite.shader b/Extra2D/shaders/builtin/sprite.shader deleted file mode 100644 index ec30971..0000000 --- a/Extra2D/shaders/builtin/sprite.shader +++ /dev/null @@ -1,61 +0,0 @@ -// ============================================ -// Extra2D Combined Shader File -// Name: sprite -// Category: builtin -// Version: 1.0 -// ============================================ - -#meta -{ - "name": "sprite", - "category": "builtin", - "author": "Extra2D Team", - "description": "标准2D精灵渲染Shader", - "uniforms": { - "u_viewProjection": { "type": "mat4", "description": "视图投影矩阵" }, - "u_model": { "type": "mat4", "description": "模型矩阵" }, - "u_opacity": { "type": "float", "default": 1.0, "description": "透明度" } - } -} - -#vertex -#version 300 es -precision highp float; - -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; - -uniform mat4 u_viewProjection; -uniform mat4 u_model; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; - v_color = a_color; -} - -#fragment -#version 300 es -precision highp float; - -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_opacity; - -out vec4 fragColor; - -void main() { - vec4 texColor = texture(u_texture, v_texCoord); - fragColor = texColor * v_color; - fragColor.a *= u_opacity; - - if (fragColor.a < 0.01) { - discard; - } -} diff --git a/Extra2D/shaders/effects/blur.shader b/Extra2D/shaders/effects/blur.shader deleted file mode 100644 index 7f6eec5..0000000 --- a/Extra2D/shaders/effects/blur.shader +++ /dev/null @@ -1,71 +0,0 @@ -// ============================================ -// Extra2D Combined Shader File -// Name: blur -// Category: effects -// Version: 1.0 -// ============================================ - -#meta -{ - "name": "blur", - "category": "effects", - "author": "Extra2D Team", - "description": "模糊特效Shader" -} - -#vertex -#version 300 es -precision highp float; - -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; - -uniform mat4 u_viewProjection; -uniform mat4 u_model; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; - v_color = a_color; -} - -#fragment -#version 300 es -precision highp float; - -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_radius; -uniform vec2 u_textureSize; -uniform float u_opacity; - -out vec4 fragColor; - -void main() { - vec2 texel = u_radius / u_textureSize; - - vec4 sum = vec4(0.0); - sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, -1.0)); - sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, -1.0)); - sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, -1.0)); - sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, 0.0)); - sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, 0.0)); - sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, 0.0)); - sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, 1.0)); - sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, 1.0)); - sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, 1.0)); - - vec4 texColor = sum / 9.0; - fragColor = texColor * v_color; - fragColor.a *= u_opacity; - - if (fragColor.a < 0.01) { - discard; - } -} diff --git a/Extra2D/shaders/effects/distortion.shader b/Extra2D/shaders/effects/distortion.shader deleted file mode 100644 index 1d583d4..0000000 --- a/Extra2D/shaders/effects/distortion.shader +++ /dev/null @@ -1,64 +0,0 @@ -// ============================================ -// Extra2D Combined Shader File -// Name: distortion -// Category: effects -// Version: 1.0 -// ============================================ - -#meta -{ - "name": "distortion", - "category": "effects", - "author": "Extra2D Team", - "description": "扭曲特效Shader" -} - -#vertex -#version 300 es -precision highp float; - -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; - -uniform mat4 u_viewProjection; -uniform mat4 u_model; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; - v_color = a_color; -} - -#fragment -#version 300 es -precision highp float; - -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_distortionAmount; -uniform float u_time; -uniform float u_timeScale; - -out vec4 fragColor; - -void main() { - vec2 uv = v_texCoord; - - float t = u_time * u_timeScale; - float dx = sin(uv.y * 10.0 + t) * u_distortionAmount; - float dy = cos(uv.x * 10.0 + t) * u_distortionAmount; - uv += vec2(dx, dy); - - vec4 texColor = texture(u_texture, uv); - fragColor = texColor * v_color; - - if (fragColor.a < 0.01) { - discard; - } -} diff --git a/Extra2D/shaders/effects/grayscale.shader b/Extra2D/shaders/effects/grayscale.shader deleted file mode 100644 index 70748fb..0000000 --- a/Extra2D/shaders/effects/grayscale.shader +++ /dev/null @@ -1,61 +0,0 @@ -// ============================================ -// Extra2D Combined Shader File -// Name: grayscale -// Category: effects -// Version: 1.0 -// ============================================ - -#meta -{ - "name": "grayscale", - "category": "effects", - "author": "Extra2D Team", - "description": "灰度特效Shader" -} - -#vertex -#version 300 es -precision highp float; - -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; - -uniform mat4 u_viewProjection; -uniform mat4 u_model; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; - v_color = a_color; -} - -#fragment -#version 300 es -precision highp float; - -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_intensity; -uniform float u_opacity; - -out vec4 fragColor; - -void main() { - vec4 texColor = texture(u_texture, v_texCoord) * v_color; - - float gray = dot(texColor.rgb, vec3(0.299, 0.587, 0.114)); - texColor.rgb = mix(texColor.rgb, vec3(gray), u_intensity); - - fragColor = texColor; - fragColor.a *= u_opacity; - - if (fragColor.a < 0.01) { - discard; - } -} diff --git a/Extra2D/shaders/effects/grayscale_outline.shader b/Extra2D/shaders/effects/grayscale_outline.shader deleted file mode 100644 index a5b0f91..0000000 --- a/Extra2D/shaders/effects/grayscale_outline.shader +++ /dev/null @@ -1,77 +0,0 @@ -// ============================================ -// Extra2D Combined Shader File -// Name: grayscale_outline -// Category: effects -// Version: 1.0 -// ============================================ - -#meta -{ - "name": "grayscale_outline", - "category": "effects", - "author": "Extra2D Team", - "description": "灰度+描边组合效果Shader" -} - -#vertex -#version 300 es -precision highp float; - -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; - -uniform mat4 u_viewProjection; -uniform mat4 u_model; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; - v_color = a_color; -} - -#fragment -#version 300 es -precision highp float; - -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_grayIntensity; -uniform vec4 u_outlineColor; -uniform float u_thickness; -uniform vec2 u_textureSize; -uniform float u_opacity; - -out vec4 fragColor; - -void main() { - vec4 color = texture(u_texture, v_texCoord) * v_color; - - float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114)); - color.rgb = mix(color.rgb, vec3(gray), u_grayIntensity); - - float alpha = 0.0; - vec2 offset = u_thickness / u_textureSize; - - alpha += texture(u_texture, v_texCoord + vec2(-offset.x, 0.0)).a; - alpha += texture(u_texture, v_texCoord + vec2(offset.x, 0.0)).a; - alpha += texture(u_texture, v_texCoord + vec2(0.0, -offset.y)).a; - alpha += texture(u_texture, v_texCoord + vec2(0.0, offset.y)).a; - - if (color.a < 0.1 && alpha > 0.0) { - fragColor = u_outlineColor; - } else { - fragColor = color; - } - - fragColor.a *= u_opacity; - - if (fragColor.a < 0.01) { - discard; - } -} diff --git a/Extra2D/shaders/effects/invert.shader b/Extra2D/shaders/effects/invert.shader deleted file mode 100644 index 9e8cdfc..0000000 --- a/Extra2D/shaders/effects/invert.shader +++ /dev/null @@ -1,60 +0,0 @@ -// ============================================ -// Extra2D Combined Shader File -// Name: invert -// Category: effects -// Version: 1.0 -// ============================================ - -#meta -{ - "name": "invert", - "category": "effects", - "author": "Extra2D Team", - "description": "反相特效Shader" -} - -#vertex -#version 300 es -precision highp float; - -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; - -uniform mat4 u_viewProjection; -uniform mat4 u_model; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; - v_color = a_color; -} - -#fragment -#version 300 es -precision highp float; - -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_strength; -uniform float u_opacity; - -out vec4 fragColor; - -void main() { - vec4 texColor = texture(u_texture, v_texCoord) * v_color; - vec3 inverted = vec3(1.0) - texColor.rgb; - texColor.rgb = mix(texColor.rgb, inverted, u_strength); - - fragColor = texColor; - fragColor.a *= u_opacity; - - if (fragColor.a < 0.01) { - discard; - } -} diff --git a/Extra2D/shaders/effects/outline.shader b/Extra2D/shaders/effects/outline.shader deleted file mode 100644 index 6969a8b..0000000 --- a/Extra2D/shaders/effects/outline.shader +++ /dev/null @@ -1,70 +0,0 @@ -// ============================================ -// Extra2D Combined Shader File -// Name: outline -// Category: effects -// Version: 1.0 -// ============================================ - -#meta -{ - "name": "outline", - "category": "effects", - "author": "Extra2D Team", - "description": "描边特效Shader" -} - -#vertex -#version 300 es -precision highp float; - -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; - -uniform mat4 u_viewProjection; -uniform mat4 u_model; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; - v_color = a_color; -} - -#fragment -#version 300 es -precision highp float; - -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform vec4 u_outlineColor; -uniform float u_thickness; -uniform vec2 u_textureSize; - -out vec4 fragColor; - -void main() { - vec4 color = texture(u_texture, v_texCoord); - - float alpha = 0.0; - vec2 offset = u_thickness / u_textureSize; - - alpha += texture(u_texture, v_texCoord + vec2(-offset.x, 0.0)).a; - alpha += texture(u_texture, v_texCoord + vec2(offset.x, 0.0)).a; - alpha += texture(u_texture, v_texCoord + vec2(0.0, -offset.y)).a; - alpha += texture(u_texture, v_texCoord + vec2(0.0, offset.y)).a; - - if (color.a < 0.1 && alpha > 0.0) { - fragColor = u_outlineColor; - } else { - fragColor = color; - } - - if (fragColor.a < 0.01) { - discard; - } -} diff --git a/Extra2D/shaders/effects/pixelate.shader b/Extra2D/shaders/effects/pixelate.shader deleted file mode 100644 index 17eb372..0000000 --- a/Extra2D/shaders/effects/pixelate.shader +++ /dev/null @@ -1,61 +0,0 @@ -// ============================================ -// Extra2D Combined Shader File -// Name: pixelate -// Category: effects -// Version: 1.0 -// ============================================ - -#meta -{ - "name": "pixelate", - "category": "effects", - "author": "Extra2D Team", - "description": "像素化特效Shader" -} - -#vertex -#version 300 es -precision highp float; - -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; - -uniform mat4 u_viewProjection; -uniform mat4 u_model; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; - v_color = a_color; -} - -#fragment -#version 300 es -precision highp float; - -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_pixelSize; -uniform vec2 u_textureSize; -uniform float u_opacity; - -out vec4 fragColor; - -void main() { - vec2 pixel = u_pixelSize / u_textureSize; - vec2 uv = floor(v_texCoord / pixel) * pixel + pixel * 0.5; - - vec4 texColor = texture(u_texture, uv); - fragColor = texColor * v_color; - fragColor.a *= u_opacity; - - if (fragColor.a < 0.01) { - discard; - } -} diff --git a/Extra2D/shaders/effects/pixelate_invert.shader b/Extra2D/shaders/effects/pixelate_invert.shader deleted file mode 100644 index f19e31c..0000000 --- a/Extra2D/shaders/effects/pixelate_invert.shader +++ /dev/null @@ -1,66 +0,0 @@ -// ============================================ -// Extra2D Combined Shader File -// Name: pixelate_invert -// Category: effects -// Version: 1.0 -// ============================================ - -#meta -{ - "name": "pixelate_invert", - "category": "effects", - "author": "Extra2D Team", - "description": "像素化+反相组合效果Shader" -} - -#vertex -#version 300 es -precision highp float; - -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; - -uniform mat4 u_viewProjection; -uniform mat4 u_model; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; - v_color = a_color; -} - -#fragment -#version 300 es -precision highp float; - -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_pixelSize; -uniform vec2 u_textureSize; -uniform float u_invertStrength; -uniform float u_opacity; - -out vec4 fragColor; - -void main() { - vec2 pixel = u_pixelSize / u_textureSize; - vec2 uv = floor(v_texCoord / pixel) * pixel + pixel * 0.5; - - vec4 color = texture(u_texture, uv) * v_color; - - vec3 inverted = 1.0 - color.rgb; - color.rgb = mix(color.rgb, inverted, u_invertStrength); - - fragColor = color; - fragColor.a *= u_opacity; - - if (fragColor.a < 0.01) { - discard; - } -} diff --git a/Extra2D/shaders/effects/water.shader b/Extra2D/shaders/effects/water.shader deleted file mode 100644 index e729afc..0000000 --- a/Extra2D/shaders/effects/water.shader +++ /dev/null @@ -1,63 +0,0 @@ -// ============================================ -// Extra2D Combined Shader File -// Name: water -// Category: effects -// Version: 1.0 -// ============================================ - -#meta -{ - "name": "water", - "category": "effects", - "author": "Extra2D Team", - "description": "水波纹特效Shader" -} - -#vertex -#version 300 es -precision highp float; - -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; - -uniform mat4 u_viewProjection; -uniform mat4 u_model; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; - v_color = a_color; -} - -#fragment -#version 300 es -precision highp float; - -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_waveSpeed; -uniform float u_waveAmplitude; -uniform float u_waveFrequency; -uniform float u_time; - -out vec4 fragColor; - -void main() { - vec2 uv = v_texCoord; - - float wave = sin(uv.y * u_waveFrequency + u_time * u_waveSpeed) * u_waveAmplitude; - uv.x += wave; - - vec4 texColor = texture(u_texture, uv); - fragColor = texColor * v_color; - - if (fragColor.a < 0.01) { - discard; - } -} diff --git a/Extra2D/shaders/shared/builtin/shape.json b/Extra2D/shaders/shared/builtin/shape.json new file mode 100644 index 0000000..74ae28e --- /dev/null +++ b/Extra2D/shaders/shared/builtin/shape.json @@ -0,0 +1,20 @@ +{ + "name": "shape", + "category": "builtin", + "version": "1.0", + "description": "基本形状渲染Shader", + "uniforms": { + "u_viewProjection": { "type": "mat4", "description": "视图投影矩阵" } + }, + "samplers": {}, + "backends": { + "opengl": { + "vertex": "backends/opengl/builtin/shape.vert", + "fragment": "backends/opengl/builtin/shape.frag" + }, + "vulkan": { + "vertex": "backends/vulkan/builtin/shape.vert.spv", + "fragment": "backends/vulkan/builtin/shape.frag.spv" + } + } +} diff --git a/Extra2D/shaders/shared/builtin/sprite.json b/Extra2D/shaders/shared/builtin/sprite.json new file mode 100644 index 0000000..4cc2616 --- /dev/null +++ b/Extra2D/shaders/shared/builtin/sprite.json @@ -0,0 +1,24 @@ +{ + "name": "sprite", + "category": "builtin", + "version": "1.0", + "description": "标准2D精灵渲染Shader", + "uniforms": { + "u_viewProjection": { "type": "mat4", "description": "视图投影矩阵" }, + "u_model": { "type": "mat4", "description": "模型矩阵" }, + "u_opacity": { "type": "float", "default": 1.0, "description": "透明度" } + }, + "samplers": { + "u_texture": { "binding": 0, "description": "纹理采样器" } + }, + "backends": { + "opengl": { + "vertex": "backends/opengl/builtin/sprite.vert", + "fragment": "backends/opengl/builtin/sprite.frag" + }, + "vulkan": { + "vertex": "backends/vulkan/builtin/sprite.vert.spv", + "fragment": "backends/vulkan/builtin/sprite.frag.spv" + } + } +} diff --git a/Extra2D/src/graphics/backends/backend_factory.cpp b/Extra2D/src/graphics/backends/backend_factory.cpp new file mode 100644 index 0000000..27b66c5 --- /dev/null +++ b/Extra2D/src/graphics/backends/backend_factory.cpp @@ -0,0 +1,127 @@ +#include + +// 条件编译包含对应后端实现 +#ifdef E2D_BACKEND_OPENGL +#include +#endif + +#ifdef E2D_BACKEND_VULKAN +#include +#endif + +#include +#include + +namespace extra2d { + +BackendFactory& BackendFactory::getInstance() { + static BackendFactory instance; + return instance; +} + +UniquePtr BackendFactory::createBackend(BackendType type) { + switch (type) { +#ifdef E2D_BACKEND_OPENGL + case BackendType::OpenGL: + E2D_LOG_INFO("Creating OpenGL render backend"); + return makeUnique(); +#endif + +#ifdef E2D_BACKEND_VULKAN + case BackendType::Vulkan: + E2D_LOG_INFO("Creating Vulkan render backend"); + return makeUnique(); +#endif + + default: + E2D_LOG_ERROR("Unsupported render backend type: {}", static_cast(type)); + return nullptr; + } +} + +UniquePtr BackendFactory::createDefaultBackend() { + BackendType recommended = getRecommendedBackend(); + return createBackend(recommended); +} + +bool BackendFactory::isBackendAvailable(BackendType type) const { + switch (type) { +#ifdef E2D_BACKEND_OPENGL + case BackendType::OpenGL: + return true; +#endif + +#ifdef E2D_BACKEND_VULKAN + case BackendType::Vulkan: + return true; +#endif + + default: + return false; + } +} + +BackendType BackendFactory::getRecommendedBackend() const { + // 平台特定的默认后端选择 + // 优先级:Vulkan > OpenGL + +#ifdef E2D_BACKEND_VULKAN + return BackendType::Vulkan; +#endif + +#ifdef E2D_BACKEND_OPENGL + return BackendType::OpenGL; +#endif + + // 如果没有可用的后端,返回OpenGL作为默认值 + return BackendType::OpenGL; +} + +const char* BackendFactory::getBackendName(BackendType type) const { + switch (type) { + case BackendType::OpenGL: + return "OpenGL"; + case BackendType::Vulkan: + return "Vulkan"; + case BackendType::Metal: + return "Metal"; + case BackendType::D3D11: + return "D3D11"; + case BackendType::D3D12: + return "D3D12"; + case BackendType::OpenGLES: + return "OpenGL ES"; + default: + return "Unknown"; + } +} + +BackendType BackendFactory::parseBackendType(const char* name) const { + if (!name) { + return BackendType::OpenGL; + } + + if (std::strcmp(name, "opengl") == 0 || std::strcmp(name, "OpenGL") == 0) { + return BackendType::OpenGL; + } + if (std::strcmp(name, "vulkan") == 0 || std::strcmp(name, "Vulkan") == 0) { + return BackendType::Vulkan; + } + if (std::strcmp(name, "metal") == 0 || std::strcmp(name, "Metal") == 0) { + return BackendType::Metal; + } + if (std::strcmp(name, "d3d11") == 0 || std::strcmp(name, "D3D11") == 0) { + return BackendType::D3D11; + } + if (std::strcmp(name, "d3d12") == 0 || std::strcmp(name, "D3D12") == 0) { + return BackendType::D3D12; + } + if (std::strcmp(name, "opengles") == 0 || std::strcmp(name, "OpenGLES") == 0) { + return BackendType::OpenGLES; + } + + E2D_LOG_WARN("Unknown backend type '{}', defaulting to OpenGL", name); + return BackendType::OpenGL; +} + +} // namespace extra2d diff --git a/Extra2D/src/graphics/backends/opengl/gl_font_atlas.cpp b/Extra2D/src/graphics/backends/opengl/gl_font_atlas.cpp new file mode 100644 index 0000000..bb91b42 --- /dev/null +++ b/Extra2D/src/graphics/backends/opengl/gl_font_atlas.cpp @@ -0,0 +1,276 @@ +#include +#include + +#include +#include +#include +#include + +// 在实现文件中定义 STB 实现 +#define STB_TRUETYPE_IMPLEMENTATION +#include + +#define STB_RECT_PACK_IMPLEMENTATION +#include + +namespace extra2d { + +// ============================================================================ +// GLFontAtlas 构造函数 +// 使用 STB 库加载字体并创建纹理图集 +// ============================================================================ +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("Failed to initialize font: {}", filepath); + return; + } + + // 创建纹理图集 (使用单通道格式) + texture_ = std::make_unique(ATLAS_WIDTH, ATLAS_HEIGHT, nullptr, 1); + + // 预加载 ASCII 字符 + std::string asciiChars = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + for (char c : asciiChars) { + char32_t codepoint = static_cast(static_cast(c)); + renderGlyph(codepoint); + } + + // 计算字体度量 + int ascent, descent, lineGap; + stbtt_GetFontVMetrics(&fontInfo_, &ascent, &descent, &lineGap); + + scale_ = stbtt_ScaleForPixelHeight(&fontInfo_, static_cast(fontSize)); + + ascent_ = static_cast(ascent) * scale_; + descent_ = static_cast(descent) * scale_; + lineGap_ = static_cast(lineGap) * scale_; + lineHeight_ = ascent_ - descent_ + lineGap_; +} + +// ============================================================================ +// GLFontAtlas 析构函数 +// ============================================================================ +GLFontAtlas::~GLFontAtlas() { + // STB 不需要显式清理,字体数据由 vector 自动管理 +} + +// ============================================================================ +// 获取字形信息 +// ============================================================================ +const Glyph *GLFontAtlas::getGlyph(char32_t codepoint) const { + auto it = glyphs_.find(codepoint); + if (it == glyphs_.end()) { + // 尝试渲染该字符 + const_cast(this)->renderGlyph(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 width = 0.0f; + float maxWidth = 0.0f; + float height = lineHeight_; + + for (size_t i = 0; i < text.length();) { + // 处理 UTF-8 编码 + char32_t codepoint = 0; + unsigned char c = static_cast(text[i]); + + if ((c & 0x80) == 0) { + // 单字节 ASCII + codepoint = c; + i++; + } else if ((c & 0xE0) == 0xC0) { + // 2字节 UTF-8 + if (i + 1 >= text.length()) break; + codepoint = ((c & 0x1F) << 6) | (static_cast(text[i + 1]) & 0x3F); + i += 2; + } else if ((c & 0xF0) == 0xE0) { + // 3字节 UTF-8 + if (i + 2 >= text.length()) break; + codepoint = ((c & 0x0F) << 12) | + ((static_cast(text[i + 1]) & 0x3F) << 6) | + (static_cast(text[i + 2]) & 0x3F); + i += 3; + } else if ((c & 0xF8) == 0xF0) { + // 4字节 UTF-8 + if (i + 3 >= text.length()) break; + codepoint = ((c & 0x07) << 18) | + ((static_cast(text[i + 1]) & 0x3F) << 12) | + ((static_cast(text[i + 2]) & 0x3F) << 6) | + (static_cast(text[i + 3]) & 0x3F); + i += 4; + } else { + // 无效的 UTF-8,跳过 + i++; + continue; + } + + // 处理换行 + if (codepoint == '\n') { + maxWidth = std::max(maxWidth, width); + width = 0.0f; + height += lineHeight_; + continue; + } + + const Glyph* glyph = getGlyph(codepoint); + if (glyph) { + width += glyph->advance; + } + } + + maxWidth = std::max(maxWidth, width); + return Vec2(maxWidth, height); +} + +// ============================================================================ +// 初始化字体 (使用 STB) +// ============================================================================ +bool GLFontAtlas::initFont(const std::string &filepath) { + // 读取字体文件到内存 + std::ifstream file(filepath, std::ios::binary | std::ios::ate); + if (!file.is_open()) { + E2D_LOG_ERROR("Failed to open font file: {}", 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("Failed to read font file: {}", filepath); + return false; + } + + // 初始化 STB 字体 + if (!stbtt_InitFont(&fontInfo_, fontData_.data(), 0)) { + E2D_LOG_ERROR("Failed to initialize STB font: {}", filepath); + return false; + } + + return true; +} + +// ============================================================================ +// 渲染字形到图集 +// ============================================================================ +bool GLFontAtlas::renderGlyph(char32_t codepoint) { + if (glyphs_.find(codepoint) != glyphs_.end()) { + return true; // 已存在 + } + + // 获取字形索引 + int glyphIndex = stbtt_FindGlyphIndex(&fontInfo_, static_cast(codepoint)); + if (glyphIndex == 0) { + return false; // 字符不存在 + } + + // 获取字形位图尺寸 + int width, height, xoff, yoff; + unsigned char *bitmap = stbtt_GetCodepointBitmap( + &fontInfo_, 0, scale_, static_cast(codepoint), + &width, &height, &xoff, &yoff); + + if (!bitmap) { + // 空白字符(如空格) + int advance, leftSideBearing; + stbtt_GetCodepointHMetrics(&fontInfo_, static_cast(codepoint), &advance, &leftSideBearing); + + GlyphData data; + data.width = 0; + data.height = 0; + data.bearingX = 0; + data.bearingY = 0; + data.advance = static_cast(advance) * scale_; + data.u0 = data.v0 = data.u1 = data.v1 = 0; + glyphs_[codepoint] = data; + return true; + } + + // 使用 stb_rect_pack 进行图集打包 + stbrp_context context; + stbrp_node nodes[ATLAS_WIDTH]; + stbrp_init_target(&context, ATLAS_WIDTH, ATLAS_HEIGHT, nodes, ATLAS_WIDTH); + + stbrp_rect rect; + rect.id = static_cast(codepoint); + rect.w = static_cast(width); + rect.h = static_cast(height); + + if (!stbrp_pack_rects(&context, &rect, 1) || !rect.was_packed) { + E2D_LOG_WARN("Failed to pack glyph for codepoint: {}", codepoint); + stbtt_FreeBitmap(bitmap, nullptr); + return false; + } + + int x = rect.x; + int y = rect.y; + + // 更新纹理 + updateAtlas(x, y, width, height, std::vector(bitmap, bitmap + width * height)); + stbtt_FreeBitmap(bitmap, nullptr); + + // 获取字形度量 + int advance, leftSideBearing; + stbtt_GetCodepointHMetrics(&fontInfo_, static_cast(codepoint), &advance, &leftSideBearing); + + int ix0, iy0, ix1, iy1; + stbtt_GetCodepointBitmapBox(&fontInfo_, static_cast(codepoint), scale_, scale_, &ix0, &iy0, &ix1, &iy1); + + // 存储字形数据 + GlyphData data; + data.width = static_cast(width); + data.height = static_cast(height); + data.bearingX = static_cast(xoff); + data.bearingY = static_cast(-yoff); // STB 使用不同的坐标系 + data.advance = static_cast(advance) * scale_; + + // 计算 UV 坐标 + data.u0 = static_cast(x) / ATLAS_WIDTH; + data.v0 = static_cast(y) / ATLAS_HEIGHT; + data.u1 = static_cast(x + width) / ATLAS_WIDTH; + data.v1 = static_cast(y + height) / ATLAS_HEIGHT; + + glyphs_[codepoint] = data; + return true; +} + +// ============================================================================ +// 更新图集纹理 +// ============================================================================ +void GLFontAtlas::updateAtlas(int x, int y, int width, int height, + const std::vector &data) { + if (texture_) { + texture_->bind(); + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RED, + GL_UNSIGNED_BYTE, data.data()); + } +} + +} // namespace extra2d diff --git a/Extra2D/src/graphics/opengl/gl_renderer.cpp b/Extra2D/src/graphics/backends/opengl/gl_renderer.cpp similarity index 95% rename from Extra2D/src/graphics/opengl/gl_renderer.cpp rename to Extra2D/src/graphics/backends/opengl/gl_renderer.cpp index a98da62..b34097a 100644 --- a/Extra2D/src/graphics/opengl/gl_renderer.cpp +++ b/Extra2D/src/graphics/backends/opengl/gl_renderer.cpp @@ -2,9 +2,10 @@ #include #include #include -#include -#include -#include +#include +#include +#include +#include #include #include #include @@ -273,9 +274,9 @@ void GLRenderer::beginSpriteBatch() { spriteBatch_.begin(viewProjection_); } void GLRenderer::drawSprite(const Texture &texture, const Rect &destRect, const Rect &srcRect, const Color &tint, float rotation, const Vec2 &anchor) { - GLSpriteBatch::SpriteData data; - data.position = glm::vec2(destRect.origin.x, destRect.origin.y); - data.size = glm::vec2(destRect.size.width, destRect.size.height); + 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()); @@ -287,13 +288,14 @@ void GLRenderer::drawSprite(const Texture &texture, const Rect &destRect, float v1 = srcRect.origin.y / texH; float v2 = (srcRect.origin.y + srcRect.size.height) / texH; - data.texCoordMin = glm::vec2(glm::min(u1, u2), glm::min(v1, v2)); - data.texCoordMax = glm::vec2(glm::max(u1, u2), glm::max(v1, v2)); + data.uvRect = Rect( + Vec2(glm::min(u1, u2), glm::min(v1, v2)), + Size(glm::abs(u2 - u1), glm::abs(v2 - v1)) + ); - data.color = glm::vec4(tint.r, tint.g, tint.b, tint.a); + data.color = tint; data.rotation = rotation * 3.14159f / 180.0f; - data.anchor = glm::vec2(anchor.x, anchor.y); - data.isSDF = false; + data.pivot = Vec2(anchor.x, anchor.y); spriteBatch_.draw(texture, data); } @@ -580,7 +582,7 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text, float baselineY = cursorY + font.getAscent(); // 收集所有字符数据用于批处理 - std::vector sprites; + std::vector sprites; sprites.reserve(text.size()); // 预分配空间 for (char c : text) { @@ -604,15 +606,16 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text, float xPos = penX + glyph->bearingX; float yPos = baselineY + glyph->bearingY; - GLSpriteBatch::SpriteData data; - data.position = glm::vec2(xPos, yPos); - data.size = glm::vec2(glyph->width, glyph->height); - data.texCoordMin = glm::vec2(glyph->u0, glyph->v0); - data.texCoordMax = glm::vec2(glyph->u1, glyph->v1); - data.color = glm::vec4(color.r, color.g, color.b, color.a); + SpriteData data; + data.position = Vec2(xPos, yPos); + 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; - data.anchor = glm::vec2(0.0f, 0.0f); - data.isSDF = font.isSDF(); + data.pivot = Vec2(0.0f, 0.0f); sprites.push_back(data); } diff --git a/Extra2D/src/graphics/opengl/gl_shader.cpp b/Extra2D/src/graphics/backends/opengl/gl_shader.cpp similarity index 99% rename from Extra2D/src/graphics/opengl/gl_shader.cpp rename to Extra2D/src/graphics/backends/opengl/gl_shader.cpp index 16b839e..0d04452 100644 --- a/Extra2D/src/graphics/opengl/gl_shader.cpp +++ b/Extra2D/src/graphics/backends/opengl/gl_shader.cpp @@ -1,4 +1,4 @@ -#include +#include #include namespace extra2d { diff --git a/Extra2D/src/graphics/backends/opengl/gl_sprite_batch.cpp b/Extra2D/src/graphics/backends/opengl/gl_sprite_batch.cpp new file mode 100644 index 0000000..3ca1ef3 --- /dev/null +++ b/Extra2D/src/graphics/backends/opengl/gl_sprite_batch.cpp @@ -0,0 +1,189 @@ +#include +#include +#include +#include + +namespace extra2d { + +GLSpriteBatch::GLSpriteBatch() + : vao_(0), vbo_(0), ebo_(0), currentTexture_(nullptr), + drawCallCount_(0) {} + +GLSpriteBatch::~GLSpriteBatch() { shutdown(); } + +bool GLSpriteBatch::init() { + // 从ShaderManager获取精灵着色器 + shader_ = ShaderManager::getInstance().getBuiltin("sprite"); + if (!shader_) { + E2D_LOG_ERROR("Failed to get builtin sprite shader"); + return false; + } + + // 创建 VAO, VBO, EBO + glGenVertexArrays(1, &vao_); + glGenBuffers(1, &vbo_); + glGenBuffers(1, &ebo_); + + glBindVertexArray(vao_); + + // 设置 VBO + glBindBuffer(GL_ARRAY_BUFFER, vbo_); + glBufferData(GL_ARRAY_BUFFER, + SpriteBatch::MAX_VERTICES * sizeof(SpriteVertex), nullptr, + GL_DYNAMIC_DRAW); + + // 设置顶点属性 + 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(索引缓冲区)- 使用 batch 层生成的静态索引 + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + batch_.getIndices().size() * sizeof(uint16_t), + batch_.getIndices().data(), GL_STATIC_DRAW); + + glBindVertexArray(0); + + return true; +} + +void GLSpriteBatch::shutdown() { + if (ebo_ != 0) { + glDeleteBuffers(1, &ebo_); + ebo_ = 0; + } + if (vbo_ != 0) { + glDeleteBuffers(1, &vbo_); + vbo_ = 0; + } + if (vao_ != 0) { + glDeleteVertexArrays(1, &vao_); + vao_ = 0; + } +} + +void GLSpriteBatch::begin(const glm::mat4 &viewProjection) { + batch_.begin(viewProjection); + batches_.clear(); + currentTexture_ = nullptr; + drawCallCount_ = 0; +} + +void GLSpriteBatch::end() { + if (batch_.getSpriteCount() > 0) { + flush(); + } +} + +void GLSpriteBatch::draw(const Texture &texture, const SpriteData &data) { + const GLTexture *glTex = dynamic_cast(&texture); + if (!glTex) { + E2D_LOG_WARN("Invalid texture type for sprite batch"); + 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("Invalid texture type for sprite batch"); + 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(); + // 注意:batch 层不存储 viewProjection,需要外部传入 + // 这里简化处理,实际应该在 begin 时设置 + } + + // 上传顶点数据 + glBindBuffer(GL_ARRAY_BUFFER, vbo_); + glBufferSubData(GL_ARRAY_BUFFER, 0, + batch_.getVertices().size() * sizeof(SpriteVertex), + batch_.getVertices().data()); + + glBindVertexArray(vao_); + + // 绘制 + 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(); + } + + // 绑定着色器并设置uniform + if (shader_) { + shader_->bind(); + // 这里需要从某处获取 viewProjection + // 简化处理:假设 shader 已经在 begin 时设置了 viewProjection + shader_->setInt("u_texture", 0); + shader_->setFloat("u_opacity", 1.0f); + } + + // 重置状态 + batches_.clear(); + currentTexture_ = nullptr; +} + +} // namespace extra2d diff --git a/Extra2D/src/graphics/opengl/gl_texture.cpp b/Extra2D/src/graphics/backends/opengl/gl_texture.cpp similarity index 99% rename from Extra2D/src/graphics/opengl/gl_texture.cpp rename to Extra2D/src/graphics/backends/opengl/gl_texture.cpp index e68b829..b88aa31 100644 --- a/Extra2D/src/graphics/opengl/gl_texture.cpp +++ b/Extra2D/src/graphics/backends/opengl/gl_texture.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #define STB_IMAGE_IMPLEMENTATION diff --git a/Extra2D/src/graphics/backends/vulkan/vk_renderer.cpp b/Extra2D/src/graphics/backends/vulkan/vk_renderer.cpp new file mode 100644 index 0000000..fc09612 --- /dev/null +++ b/Extra2D/src/graphics/backends/vulkan/vk_renderer.cpp @@ -0,0 +1,150 @@ +#include +#include + +namespace extra2d { + +VulkanRenderer::VulkanRenderer() = default; + +VulkanRenderer::~VulkanRenderer() { + shutdown(); +} + +bool VulkanRenderer::init(IWindow* window) { + E2D_LOG_WARN("Vulkan renderer is not fully implemented yet"); + 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/sprite_batch.cpp b/Extra2D/src/graphics/batch/sprite_batch.cpp new file mode 100644 index 0000000..cd17b8b --- /dev/null +++ b/Extra2D/src/graphics/batch/sprite_batch.cpp @@ -0,0 +1,200 @@ +#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; + } + + // 设置纹理坐标 + float u1 = sprite.uvRect.origin.x; + float v1 = sprite.uvRect.origin.y; + float u2 = u1 + sprite.uvRect.size.width; + float v2 = v1 + sprite.uvRect.size.height; + + vertices_[vertexOffset + 0].texCoord = Vec2(u1, v2); // 左下 + vertices_[vertexOffset + 1].texCoord = Vec2(u2, v2); // 右下 + vertices_[vertexOffset + 2].texCoord = Vec2(u2, v1); // 右上 + vertices_[vertexOffset + 3].texCoord = Vec2(u1, 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/core/render_backend.cpp b/Extra2D/src/graphics/core/render_backend.cpp index 7d4b53a..82e05d4 100644 --- a/Extra2D/src/graphics/core/render_backend.cpp +++ b/Extra2D/src/graphics/core/render_backend.cpp @@ -1,4 +1,4 @@ -#include +#include #include namespace extra2d { diff --git a/Extra2D/src/graphics/core/render_module.cpp b/Extra2D/src/graphics/core/render_module.cpp index 7d3b19a..5043fad 100644 --- a/Extra2D/src/graphics/core/render_module.cpp +++ b/Extra2D/src/graphics/core/render_module.cpp @@ -1,6 +1,6 @@ #include -#include -#include +#include +#include #include #include #include diff --git a/Extra2D/src/graphics/core/render_target.cpp b/Extra2D/src/graphics/core/render_target.cpp index 931084d..bb6adff 100644 --- a/Extra2D/src/graphics/core/render_target.cpp +++ b/Extra2D/src/graphics/core/render_target.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include diff --git a/Extra2D/src/graphics/opengl/gl_font_atlas.cpp b/Extra2D/src/graphics/opengl/gl_font_atlas.cpp deleted file mode 100644 index d279f55..0000000 --- a/Extra2D/src/graphics/opengl/gl_font_atlas.cpp +++ /dev/null @@ -1,309 +0,0 @@ -#include -#include -#include -#define STB_TRUETYPE_IMPLEMENTATION -#include -#define STB_RECT_PACK_IMPLEMENTATION -#include -#include - - -namespace extra2d { - -// ============================================================================ -// 构造函数 - 初始化字体图集 -// ============================================================================ -/** - * @brief 构造函数,从字体文件初始化字体图集 - * @param filepath 字体文件路径 - * @param fontSize 字体大小(像素) - * @param useSDF 是否使用有符号距离场渲染 - */ -GLFontAtlas::GLFontAtlas(const std::string &filepath, int fontSize, bool useSDF) - : fontSize_(fontSize), useSDF_(useSDF), currentY_(0), scale_(0.0f), - ascent_(0.0f), descent_(0.0f), lineGap_(0.0f) { - - // 加载字体文件 - std::ifstream file(filepath, std::ios::binary | std::ios::ate); - if (!file.is_open()) { - E2D_LOG_ERROR("Failed to load font: {}", filepath); - return; - } - - std::streamsize size = file.tellg(); - file.seekg(0, std::ios::beg); - fontData_.resize(size); - if (!file.read(reinterpret_cast(fontData_.data()), size)) { - E2D_LOG_ERROR("Failed to read font file: {}", filepath); - return; - } - - // 初始化 stb_truetype - if (!stbtt_InitFont(&fontInfo_, fontData_.data(), - stbtt_GetFontOffsetForIndex(fontData_.data(), 0))) { - E2D_LOG_ERROR("Failed to init font: {}", 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_; - - createAtlas(); -} - -// ============================================================================ -// 析构函数 -// ============================================================================ -/** - * @brief 析构函数 - */ -GLFontAtlas::~GLFontAtlas() = default; - -// ============================================================================ -// 获取字形 - 如果字形不存在则缓存它 -// ============================================================================ -/** - * @brief 获取字形信息,如果字形不存在则动态缓存 - * @param codepoint Unicode码点 - * @return 字形信息指针,如果获取失败返回nullptr - */ -const Glyph *GLFontAtlas::getGlyph(char32_t codepoint) const { - auto it = glyphs_.find(codepoint); - if (it == glyphs_.end()) { - cacheGlyph(codepoint); - it = glyphs_.find(codepoint); - } - return (it != glyphs_.end()) ? &it->second : nullptr; -} - -// ============================================================================ -// 测量文本尺寸 -// ============================================================================ -/** - * @brief 测量文本渲染后的尺寸 - * @param text 要测量的文本 - * @return 文本的宽度和高度 - */ -Vec2 GLFontAtlas::measureText(const std::string &text) { - float width = 0.0f; - float height = getAscent() - getDescent(); - float currentWidth = 0.0f; - - for (char c : text) { - char32_t codepoint = static_cast(static_cast(c)); - if (codepoint == '\n') { - width = std::max(width, currentWidth); - currentWidth = 0.0f; - height += getLineHeight(); - continue; - } - - const Glyph *glyph = getGlyph(codepoint); - if (glyph) { - currentWidth += glyph->advance; - } - } - - width = std::max(width, currentWidth); - return Vec2(width, height); -} - -// ============================================================================ -// 创建图集纹理 - 初始化空白纹理和矩形打包上下文 -// ============================================================================ -/** - * @brief 创建字体图集纹理,初始化空白纹理和矩形打包上下文 - */ -void GLFontAtlas::createAtlas() { - // 统一使用 4 通道格式 - int channels = 4; - std::vector emptyData(ATLAS_WIDTH * ATLAS_HEIGHT * channels, 0); - texture_ = std::make_unique(ATLAS_WIDTH, ATLAS_HEIGHT, - emptyData.data(), channels); - 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 进行矩形打包 -// ============================================================================ -/** - * @brief 缓存字形到图集,渲染字形位图并存储字形信息 - * @param codepoint Unicode码点 - */ -void GLFontAtlas::cacheGlyph(char32_t codepoint) const { - int advance = 0; - stbtt_GetCodepointHMetrics(&fontInfo_, static_cast(codepoint), &advance, - nullptr); - float advancePx = advance * scale_; - - 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); - Glyph glyph{}; - glyph.advance = advancePx; - glyphs_[codepoint] = glyph; - return; - } - - 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("Font atlas is full, cannot cache codepoint: {}", - static_cast(codepoint)); - stbtt_FreeSDF(sdf, nullptr); - return; - } - - int atlasX = rect.x + PADDING; - int atlasY = rect.y + PADDING; - - Glyph glyph; - glyph.width = static_cast(w); - glyph.height = static_cast(h); - glyph.bearingX = static_cast(xoff); - glyph.bearingY = static_cast(yoff); - glyph.advance = advancePx; - - // stb_rect_pack 使用左上角为原点,OpenGL纹理使用左下角为原点 - // 需要翻转V坐标 - float v0 = static_cast(atlasY) / ATLAS_HEIGHT; - float v1 = static_cast(atlasY + h) / ATLAS_HEIGHT; - glyph.u0 = static_cast(atlasX) / ATLAS_WIDTH; - glyph.v0 = 1.0f - v1; // 翻转V坐标 - glyph.u1 = static_cast(atlasX + w) / ATLAS_WIDTH; - glyph.v1 = 1.0f - v0; // 翻转V坐标 - - glyphs_[codepoint] = glyph; - - // 将 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 通道 - } - - // 直接设置像素对齐为 4,无需查询当前状态 - glBindTexture(GL_TEXTURE_2D, texture_->getTextureID()); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - // OpenGL纹理坐标原点在左下角,需要将Y坐标翻转 - glTexSubImage2D(GL_TEXTURE_2D, 0, atlasX, ATLAS_HEIGHT - atlasY - h, w, h, - GL_RGBA, GL_UNSIGNED_BYTE, glyphRgbaCache_.data()); - - 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; - int yoff = y0; - - if (w <= 0 || h <= 0) { - Glyph glyph{}; - glyph.advance = advancePx; - glyphs_[codepoint] = glyph; - 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("Font atlas is full, cannot cache codepoint: {}", - static_cast(codepoint)); - return; - } - - int atlasX = rect.x + PADDING; - int atlasY = rect.y + PADDING; - - // 创建字形信息 - Glyph glyph; - glyph.width = static_cast(w); - glyph.height = static_cast(h); - glyph.bearingX = static_cast(xoff); - glyph.bearingY = static_cast(yoff); - glyph.advance = advancePx; - - // 计算纹理坐标(相对于图集) - // stb_rect_pack 使用左上角为原点,OpenGL纹理使用左下角为原点 - // 需要翻转V坐标 - float v0 = static_cast(atlasY) / ATLAS_HEIGHT; - float v1 = static_cast(atlasY + h) / ATLAS_HEIGHT; - glyph.u0 = static_cast(atlasX) / ATLAS_WIDTH; - glyph.v0 = 1.0f - v1; // 翻转V坐标 - glyph.u1 = static_cast(atlasX + w) / ATLAS_WIDTH; - glyph.v1 = 1.0f - v0; // 翻转V坐标 - - // 存储字形 - glyphs_[codepoint] = glyph; - - // 将单通道字形数据转换为 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 - } - - // 更新纹理 - 将字形数据上传到图集的指定位置 - // 直接设置像素对齐为 4,无需查询当前状态 - glBindTexture(GL_TEXTURE_2D, texture_->getTextureID()); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - // OpenGL纹理坐标原点在左下角,需要将Y坐标翻转 - glTexSubImage2D(GL_TEXTURE_2D, 0, atlasX, ATLAS_HEIGHT - atlasY - h, w, h, - GL_RGBA, GL_UNSIGNED_BYTE, glyphRgbaCache_.data()); -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/opengl/gl_sprite_batch.cpp b/Extra2D/src/graphics/opengl/gl_sprite_batch.cpp deleted file mode 100644 index 6020c7f..0000000 --- a/Extra2D/src/graphics/opengl/gl_sprite_batch.cpp +++ /dev/null @@ -1,428 +0,0 @@ -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 三角函数查表 - 避免每帧重复计算 sin/cos -// ============================================================================ -/** - * @brief 三角函数查表类,避免每帧重复计算sin/cos - */ -class TrigLookup { -public: - static constexpr size_t TABLE_SIZE = 360 * 4; // 0.25度精度 - static constexpr float INDEX_SCALE = 4.0f; // 每度4个采样点 - static constexpr float RAD_TO_INDEX = INDEX_SCALE * 180.0f / 3.14159265359f; - - /** - * @brief 查表获取sin值 - * @param radians 弧度值 - * @return sin值 - */ - static float sinRad(float radians) { - return table_.sinTable[normalizeIndexRad(radians)]; - } - - /** - * @brief 查表获取cos值 - * @param radians 弧度值 - * @return cos值 - */ - static float cosRad(float radians) { - return table_.cosTable[normalizeIndexRad(radians)]; - } - -private: - struct Tables { - std::array sinTable; - std::array cosTable; - - Tables() { - for (size_t i = 0; i < TABLE_SIZE; ++i) { - float angle = static_cast(i) / INDEX_SCALE * 3.14159265359f / 180.0f; - sinTable[i] = std::sin(angle); - cosTable[i] = std::cos(angle); - } - } - }; - - static size_t normalizeIndexRad(float radians) { - int idx = static_cast(radians * RAD_TO_INDEX) % static_cast(TABLE_SIZE); - if (idx < 0) { - idx += static_cast(TABLE_SIZE); - } - return static_cast(idx); - } - - static const Tables table_; -}; - -const TrigLookup::Tables TrigLookup::table_; - -// 静态索引生成函数 -/** - * @brief 获取静态索引数组,用于精灵绘制 - * @return 索引数组的常量引用 - */ -static const std::array& getIndices() { - static std::array indices = []() { - std::array arr{}; - for (size_t i = 0; i < GLSpriteBatch::MAX_SPRITES; ++i) { - GLuint base = static_cast(i * GLSpriteBatch::VERTICES_PER_SPRITE); - arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 0] = base + 0; - arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 1] = base + 1; - arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 2] = base + 2; - arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 3] = base + 0; - arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 4] = base + 2; - arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 5] = base + 3; - } - return arr; - }(); - return indices; -} - -// 顶点着色器 (GLES 3.2) -static const char *SPRITE_VERTEX_SHADER = R"( -#version 300 es -precision highp float; -layout(location = 0) in vec2 aPosition; -layout(location = 1) in vec2 aTexCoord; -layout(location = 2) in vec4 aColor; - -uniform mat4 uViewProjection; - -out vec2 vTexCoord; -out vec4 vColor; - -void main() { - gl_Position = uViewProjection * vec4(aPosition, 0.0, 1.0); - vTexCoord = aTexCoord; - vColor = aColor; -} -)"; - -// 片段着色器 (GLES 3.2) -// SDF 常量硬编码:ONEDGE_VALUE=128/255=0.502, PIXEL_DIST_SCALE=255/64=3.98 -// SDF 值存储在 Alpha 通道 -static const char *SPRITE_FRAGMENT_SHADER = R"( -#version 300 es -precision highp float; -in vec2 vTexCoord; -in vec4 vColor; - -uniform sampler2D uTexture; -uniform int uUseSDF; - -out vec4 fragColor; - -void main() { - if (uUseSDF == 1) { - float dist = texture(uTexture, vTexCoord).a; - float sd = (dist - 0.502) * 3.98; - float w = fwidth(sd); - float alpha = smoothstep(-w, w, sd); - fragColor = vec4(vColor.rgb, vColor.a * alpha); - } else { - fragColor = texture(uTexture, vTexCoord) * vColor; - } -} -)"; - -/** - * @brief 构造函数,初始化精灵批处理器成员变量 - */ -GLSpriteBatch::GLSpriteBatch() - : vao_(0), vbo_(0), ibo_(0), vertexCount_(0), currentTexture_(nullptr), - currentIsSDF_(false), drawCallCount_(0), spriteCount_(0), batchCount_(0) { -} - -/** - * @brief 析构函数,调用shutdown释放资源 - */ -GLSpriteBatch::~GLSpriteBatch() { shutdown(); } - -/** - * @brief 初始化精灵批处理器,创建VAO、VBO、IBO和编译着色器 - * @return 初始化成功返回true,失败返回false - */ -bool GLSpriteBatch::init() { - // 创建并编译着色器 - if (!shader_.compileFromSource(SPRITE_VERTEX_SHADER, - SPRITE_FRAGMENT_SHADER)) { - E2D_LOG_ERROR("Failed to compile sprite batch shader"); - return false; - } - - // 生成 VAO、VBO、IBO - glGenVertexArrays(1, &vao_); - glGenBuffers(1, &vbo_); - glGenBuffers(1, &ibo_); - - glBindVertexArray(vao_); - - // 设置 VBO - 使用动态绘制模式 - glBindBuffer(GL_ARRAY_BUFFER, vbo_); - glBufferData(GL_ARRAY_BUFFER, MAX_VERTICES * sizeof(Vertex), nullptr, - GL_DYNAMIC_DRAW); - - // 设置顶点属性 - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), - (void *)offsetof(Vertex, position)); - - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), - (void *)offsetof(Vertex, texCoord)); - - glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), - (void *)offsetof(Vertex, color)); - - // 使用编译期生成的静态索引缓冲区 - const auto& indices = getIndices(); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), - indices.data(), GL_STATIC_DRAW); - - glBindVertexArray(0); - - E2D_LOG_INFO("GLSpriteBatch initialized with capacity for {} sprites", - MAX_SPRITES); - return true; -} - -/** - * @brief 关闭精灵批处理器,释放OpenGL资源 - */ -void GLSpriteBatch::shutdown() { - if (vao_ != 0) { - glDeleteVertexArrays(1, &vao_); - vao_ = 0; - } - if (vbo_ != 0) { - glDeleteBuffers(1, &vbo_); - vbo_ = 0; - } - if (ibo_ != 0) { - glDeleteBuffers(1, &ibo_); - ibo_ = 0; - } -} - -/** - * @brief 开始批处理,重置状态并设置视图投影矩阵 - * @param viewProjection 视图投影矩阵 - */ -void GLSpriteBatch::begin(const glm::mat4 &viewProjection) { - viewProjection_ = viewProjection; - vertexCount_ = 0; - currentTexture_ = nullptr; - currentIsSDF_ = false; - drawCallCount_ = 0; - spriteCount_ = 0; - batchCount_ = 0; -} - -/** - * @brief 检查是否需要刷新批次 - * @param texture 当前纹理 - * @param isSDF 是否为SDF渲染 - * @return 需要刷新返回true,否则返回false - */ -bool GLSpriteBatch::needsFlush(const Texture &texture, bool isSDF) const { - if (currentTexture_ == nullptr) { - return false; - } - - // 检查是否需要刷新:纹理改变、SDF 状态改变或缓冲区已满 - return (currentTexture_ != &texture) || (currentIsSDF_ != isSDF) || - (vertexCount_ + VERTICES_PER_SPRITE > MAX_VERTICES); -} - -/** - * @brief 添加精灵顶点到顶点缓冲区 - * @param data 精灵数据 - */ -void GLSpriteBatch::addVertices(const SpriteData &data) { - // 计算锚点偏移 - float anchorOffsetX = data.size.x * data.anchor.x; - float anchorOffsetY = data.size.y * data.anchor.y; - - // 使用三角函数查表替代 cosf/sinf - float cosR = TrigLookup::cosRad(data.rotation); - float sinR = TrigLookup::sinRad(data.rotation); - - glm::vec4 color(data.color.r, data.color.g, data.color.b, data.color.a); - - // 直接计算变换后的位置 - float rx0 = -anchorOffsetX; - float ry0 = -anchorOffsetY; - float rx1 = data.size.x - anchorOffsetX; - float ry1 = data.size.y - anchorOffsetY; - - // 预计算旋转后的偏移 - float cosRx0 = rx0 * cosR, sinRx0 = rx0 * sinR; - float cosRx1 = rx1 * cosR, sinRx1 = rx1 * sinR; - float cosRy0 = ry0 * cosR, sinRy0 = ry0 * sinR; - float cosRy1 = ry1 * cosR, sinRy1 = ry1 * sinR; - - // v0: (0, 0) -> (rx0, ry0) - vertexBuffer_[vertexCount_++] = { - glm::vec2(data.position.x + cosRx0 - sinRy0, data.position.y + sinRx0 + cosRy0), - glm::vec2(data.texCoordMin.x, data.texCoordMin.y), - color - }; - - // v1: (size.x, 0) -> (rx1, ry0) - vertexBuffer_[vertexCount_++] = { - glm::vec2(data.position.x + cosRx1 - sinRy0, data.position.y + sinRx1 + cosRy0), - glm::vec2(data.texCoordMax.x, data.texCoordMin.y), - color - }; - - // v2: (size.x, size.y) -> (rx1, ry1) - vertexBuffer_[vertexCount_++] = { - glm::vec2(data.position.x + cosRx1 - sinRy1, data.position.y + sinRx1 + cosRy1), - glm::vec2(data.texCoordMax.x, data.texCoordMax.y), - color - }; - - // v3: (0, size.y) -> (rx0, ry1) - vertexBuffer_[vertexCount_++] = { - glm::vec2(data.position.x + cosRx0 - sinRy1, data.position.y + sinRx0 + cosRy1), - glm::vec2(data.texCoordMin.x, data.texCoordMax.y), - color - }; -} - -/** - * @brief 绘制单个精灵 - * @param texture 纹理引用 - * @param data 精灵数据 - */ -void GLSpriteBatch::draw(const Texture &texture, const SpriteData &data) { - // 如果需要刷新,先提交当前批次 - if (needsFlush(texture, data.isSDF)) { - flush(); - } - - currentTexture_ = &texture; - currentIsSDF_ = data.isSDF; - - addVertices(data); - spriteCount_++; -} - -/** - * @brief 批量绘制多个精灵 - * @param texture 纹理引用 - * @param sprites 精灵数据数组 - */ -void GLSpriteBatch::drawBatch(const Texture &texture, - const std::vector &sprites) { - if (sprites.empty()) { - return; - } - - // 如果当前有未提交的批次且纹理不同,先刷新 - if (currentTexture_ != nullptr && currentTexture_ != &texture) { - flush(); - } - - currentTexture_ = &texture; - currentIsSDF_ = sprites[0].isSDF; // 假设批量中的精灵 SDF 状态一致 - - // 分批处理,避免超过缓冲区大小 - size_t index = 0; - while (index < sprites.size()) { - size_t remainingSpace = (MAX_VERTICES - vertexCount_) / VERTICES_PER_SPRITE; - size_t batchSize = std::min(sprites.size() - index, remainingSpace); - - for (size_t i = 0; i < batchSize; ++i) { - addVertices(sprites[index + i]); - spriteCount_++; - } - - index += batchSize; - - // 如果还有更多精灵,刷新当前批次 - if (index < sprites.size()) { - flush(); - } - } - - batchCount_++; -} - -/** - * @brief 立即绘制精灵,不缓存 - * @param texture 纹理引用 - * @param data 精灵数据 - */ -void GLSpriteBatch::drawImmediate(const Texture &texture, - const SpriteData &data) { - // 立即绘制,不缓存 - 用于需要立即显示的情况 - flush(); // 先提交当前批次 - - currentTexture_ = &texture; - currentIsSDF_ = data.isSDF; - addVertices(data); - spriteCount_++; - - flush(); // 立即提交 -} - -/** - * @brief 结束批处理,提交所有待绘制的精灵 - */ -void GLSpriteBatch::end() { - if (vertexCount_ > 0) { - flush(); - } -} - -/** - * @brief 刷新批次,执行实际的OpenGL绘制调用 - */ -void GLSpriteBatch::flush() { - if (vertexCount_ == 0 || currentTexture_ == nullptr) { - return; - } - - // 绑定纹理 - GLuint texID = static_cast( - reinterpret_cast(currentTexture_->getNativeHandle())); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texID); - - // 使用着色器 - shader_.bind(); - shader_.setMat4("uViewProjection", viewProjection_); - shader_.setInt("uTexture", 0); - shader_.setInt("uUseSDF", currentIsSDF_ ? 1 : 0); - // SDF 常量已硬编码到着色器中 - - // 更新 VBO 数据 - 只更新实际使用的部分 - glBindBuffer(GL_ARRAY_BUFFER, vbo_); - glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount_ * sizeof(Vertex), - vertexBuffer_.data()); - - // 绘制 - glBindVertexArray(vao_); - GLsizei indexCount = static_cast( - (vertexCount_ / VERTICES_PER_SPRITE) * INDICES_PER_SPRITE); - glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, nullptr); - - drawCallCount_++; - batchCount_++; - - // 重置状态 - vertexCount_ = 0; - currentTexture_ = nullptr; - currentIsSDF_ = false; -} - -} // namespace extra2d diff --git a/Extra2D/src/graphics/shader/shader_manager.cpp b/Extra2D/src/graphics/shader/shader_manager.cpp index 22d7c26..3c0b651 100644 --- a/Extra2D/src/graphics/shader/shader_manager.cpp +++ b/Extra2D/src/graphics/shader/shader_manager.cpp @@ -1,6 +1,10 @@ #include #include +#include + +namespace nl = nlohmann; + namespace extra2d { /** @@ -450,10 +454,76 @@ Ptr ShaderManager::getBuiltin(const std::string& name) { return shader; } + // 尝试从新的多后端JSON元数据加载 + std::string jsonPath = shaderDir_ + "shared/builtin/" + name + ".json"; + if (loader_.fileExists(jsonPath)) { + return loadFromMetadata(jsonPath, name); + } + + // 回退到旧的组合文件格式 std::string path = shaderDir_ + "builtin/" + name + ".shader"; return loadFromCombinedFile(path); } +/** + * @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 not initialized"); + 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("Failed to read shader metadata: {}", jsonPath); + return nullptr; + } + + try { + // 使用nlohmann/json解析 + nl::json j = nl::json::parse(jsonContent); + + // 获取OpenGL后端路径 + if (!j.contains("backends") || !j["backends"].contains("opengl")) { + E2D_LOG_ERROR("No OpenGL backend found in shader metadata: {}", jsonPath); + return nullptr; + } + + auto& opengl = j["backends"]["opengl"]; + if (!opengl.contains("vertex") || !opengl.contains("fragment")) { + E2D_LOG_ERROR("Missing vertex or fragment path in shader metadata: {}", jsonPath); + return nullptr; + } + + std::string vertRelativePath = opengl["vertex"].get(); + std::string fragRelativePath = opengl["fragment"].get(); + + // 构建完整路径 + std::string vertPath = shaderDir_ + vertRelativePath; + std::string fragPath = shaderDir_ + fragRelativePath; + + E2D_LOG_DEBUG("Loading shader from metadata: {} -> vert: {}, frag: {}", name, vertPath, fragPath); + + // 使用分离文件加载 + return loadFromFiles(name, vertPath, fragPath); + + } catch (const nl::json::exception& e) { + E2D_LOG_ERROR("Failed to parse shader metadata {}: {}", jsonPath, e.what()); + return nullptr; + } +} + /** * @brief 加载所有内置Shader * @return 加载成功返回true,失败返回false @@ -475,17 +545,27 @@ bool ShaderManager::loadBuiltinShaders() { }; for (const char* name : builtinNames) { - std::string path = shaderDir_ + "builtin/" + name + ".shader"; + // 首先尝试新的多后端JSON格式 + std::string jsonPath = shaderDir_ + "shared/builtin/" + name + ".json"; std::string shaderName = std::string("builtin_") + name; - if (!loadFromCombinedFile(path)) { - E2D_LOG_ERROR("Failed to load builtin {} shader from: {}", name, path); + Ptr shader = nullptr; + if (loader_.fileExists(jsonPath)) { + shader = loadFromMetadata(jsonPath, name); + } else { + // 回退到旧的组合文件格式 + std::string path = shaderDir_ + "builtin/" + name + ".shader"; + shader = loadFromCombinedFile(path); + } + + if (!shader) { + E2D_LOG_ERROR("Failed to load builtin {} shader", name); allSuccess = false; } else { + // 同时注册带 builtin_ 前缀的名称 auto it = shaders_.find(name); if (it != shaders_.end()) { shaders_[shaderName] = it->second; - shaders_.erase(it); } } } diff --git a/xmake.lua b/xmake.lua index 1775063..4231541 100644 --- a/xmake.lua +++ b/xmake.lua @@ -40,6 +40,13 @@ option("window_backend") set_values("sdl2", "glfw") option_end() +option("render_backend") + set_default("opengl") + set_showmenu(true) + set_description("Render backend: opengl or vulkan") + set_values("opengl", "vulkan") +option_end() + -- ============================================== -- 平台检测与配置 -- ============================================== diff --git a/xmake/engine.lua b/xmake/engine.lua index 1cdf5c8..96981bd 100644 --- a/xmake/engine.lua +++ b/xmake/engine.lua @@ -19,14 +19,29 @@ local function get_window_backend() return get_config("window_backend") or "sdl2" end +-- 获取渲染后端 +local function get_render_backend() + return get_config("render_backend") or "opengl" +end + -- 定义 Extra2D 引擎库目标 function define_extra2d_engine() target("extra2d") set_kind("static") - -- 引擎核心源文件 - add_files("Extra2D/src/**.cpp|platform/backends/**.cpp") - add_files("Extra2D/src/glad/glad.c") + -- 引擎核心源文件(后端无关) + add_files("Extra2D/src/**.cpp|platform/backends/**.cpp|graphics/backends/**.cpp") + + -- 渲染后端源文件 + local render_backend = get_render_backend() + if render_backend == "vulkan" then + add_files("Extra2D/src/graphics/backends/vulkan/*.cpp") + add_defines("E2D_BACKEND_VULKAN") + else + add_files("Extra2D/src/graphics/backends/opengl/*.cpp") + add_files("Extra2D/src/glad/glad.c") + add_defines("E2D_BACKEND_OPENGL") + end -- 窗口后端源文件 local backend = get_window_backend()