From ca41af0dbdee5659fadc2a630385fdbc802ed993 Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Fri, 13 Feb 2026 10:00:39 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=B8=B2=E6=9F=93?= =?UTF-8?q?=E5=92=8C=E8=AE=A1=E7=AE=97=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用线程局部存储和固定数组优化节点世界变换计算,避免内存分配 - 实现快速随机数生成器替代 std::mt19937 提升粒子系统性能 - 添加三角函数查表优化精灵旋转计算 - 使用工厂函数指针数组替代 switch 语句优化场景过渡创建 - 实现形状生成函数查找表优化粒子发射器 - 重构渲染器实现形状批处理和状态缓存,减少 GL 调用 - 添加预分配缓冲区和顶点缓存优化形状渲染性能 --- .../include/extra2d/effects/particle_system.h | 36 +- .../extra2d/graphics/opengl/gl_renderer.h | 139 ++++--- Extra2D/src/effects/particle_system.cpp | 71 ++-- Extra2D/src/graphics/opengl/gl_renderer.cpp | 355 ++++++++++++------ .../src/graphics/opengl/gl_sprite_batch.cpp | 49 ++- Extra2D/src/scene/node.cpp | 21 +- Extra2D/src/scene/scene_manager.cpp | 78 ++-- 7 files changed, 523 insertions(+), 226 deletions(-) diff --git a/Extra2D/include/extra2d/effects/particle_system.h b/Extra2D/include/extra2d/effects/particle_system.h index 8c56158..05bad4a 100644 --- a/Extra2D/include/extra2d/effects/particle_system.h +++ b/Extra2D/include/extra2d/effects/particle_system.h @@ -10,6 +10,34 @@ namespace extra2d { +// ============================================================================ +// 快速随机数生成器 - 使用 xorshift 算法,比 std::mt19937 更快 +// ============================================================================ +class FastRNG { +public: + explicit FastRNG(uint32_t seed = 0) : state_(seed ? seed : 0x853c49e67) {} + + float nextFloat() { + return static_cast(next()) / static_cast(UINT32_MAX); + } + + float nextFloat(float min, float max) { + return min + (max - min) * nextFloat(); + } + +private: + uint32_t state_; + + uint32_t next() { + uint32_t x = state_; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + state_ = x; + return x; + } +}; + // ============================================================================ // 粒子数据 // ============================================================================ @@ -97,6 +125,12 @@ public: ParticleEmitter(); ~ParticleEmitter() = default; + // 形状生成函数(公有,用于查找表) + Vec2 randomPointShape(); + Vec2 randomCircleShape(); + Vec2 randomRectangleShape(); + Vec2 randomConeShape(); + // ------------------------------------------------------------------------ // 初始化和关闭 // ------------------------------------------------------------------------ @@ -192,7 +226,7 @@ private: float emissionTimer_ = 0.0f; float emissionTime_ = 0.0f; - std::mt19937 rng_; + FastRNG rng_; // 使用快速 RNG 替代 std::mt19937 void emitParticle(); float randomFloat(float min, float max); diff --git a/Extra2D/include/extra2d/graphics/opengl/gl_renderer.h b/Extra2D/include/extra2d/graphics/opengl/gl_renderer.h index 6775e9c..072db18 100644 --- a/Extra2D/include/extra2d/graphics/opengl/gl_renderer.h +++ b/Extra2D/include/extra2d/graphics/opengl/gl_renderer.h @@ -1,9 +1,10 @@ #pragma once -#include #include #include +#include +#include #include #include @@ -16,67 +17,105 @@ class Window; // ============================================================================ class GLRenderer : public RenderBackend { public: - GLRenderer(); - ~GLRenderer() override; + GLRenderer(); + ~GLRenderer() override; - // RenderBackend 接口实现 - bool init(Window* window) override; - void shutdown() override; + // RenderBackend 接口实现 + bool init(Window *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 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 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; + // 变换矩阵栈 + 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; + 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 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& center, float radius, const Color& color, int segments, float width) override; - void fillCircle(const Vec2& center, 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; + 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; + 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; + Stats getStats() const override { return stats_; } + void resetStats() override; private: - Window* window_; - GLSpriteBatch spriteBatch_; - GLShader shapeShader_; - - GLuint shapeVao_; - GLuint shapeVbo_; - - glm::mat4 viewProjection_; - std::vector transformStack_; - Stats stats_; - bool vsync_; + // 形状批处理常量 + static constexpr size_t MAX_CIRCLE_SEGMENTS = 128; + static constexpr size_t MAX_SHAPE_VERTICES = 8192; // 最大形状顶点数 - void initShapeRendering(); - void setupBlendMode(BlendMode mode); + // 形状顶点结构(包含颜色) + struct ShapeVertex { + float x, y; + float r, g, b, a; + }; + + Window *window_; + GLSpriteBatch spriteBatch_; + GLShader shapeShader_; + + GLuint shapeVao_; + GLuint shapeVbo_; + + glm::mat4 viewProjection_; + std::vector transformStack_; + Stats stats_; + bool vsync_; + + // 形状批处理缓冲区(预分配,避免每帧内存分配) + std::array shapeVertexCache_; + size_t shapeVertexCount_ = 0; + GLenum currentShapeMode_ = GL_TRIANGLES; + + // OpenGL 状态缓存 + BlendMode cachedBlendMode_ = BlendMode::None; + bool blendEnabled_ = false; + int cachedViewportX_ = 0; + int cachedViewportY_ = 0; + int cachedViewportWidth_ = 0; + int cachedViewportHeight_ = 0; + + void initShapeRendering(); + void flushShapeBatch(); + void addShapeVertex(float x, float y, const Color &color); + void submitShapeBatch(GLenum mode); }; } // namespace extra2d diff --git a/Extra2D/src/effects/particle_system.cpp b/Extra2D/src/effects/particle_system.cpp index 5c9b18a..e9bdd9c 100644 --- a/Extra2D/src/effects/particle_system.cpp +++ b/Extra2D/src/effects/particle_system.cpp @@ -5,11 +5,26 @@ namespace extra2d { +// ============================================================================ +// 形状生成函数类型和查找表 +// ============================================================================ +using ShapeGenerator = Vec2 (ParticleEmitter::*)(); + +// 形状生成函数指针数组,索引对应 EmitterConfig::Shape 枚举值 +static constexpr ShapeGenerator SHAPE_GENERATORS[] = { + &ParticleEmitter::randomPointShape, // Shape::Point = 0 + &ParticleEmitter::randomCircleShape, // Shape::Circle = 1 + &ParticleEmitter::randomRectangleShape, // Shape::Rectangle = 2 + &ParticleEmitter::randomConeShape // Shape::Cone = 3 +}; + +static constexpr size_t SHAPE_GENERATOR_COUNT = sizeof(SHAPE_GENERATORS) / sizeof(SHAPE_GENERATORS[0]); + // ============================================================================ // ParticleEmitter实现 // ============================================================================ -ParticleEmitter::ParticleEmitter() : rng_(std::random_device{}()) {} +ParticleEmitter::ParticleEmitter() : rng_(static_cast(std::random_device{}())) {} bool ParticleEmitter::init(size_t maxParticles) { particles_.resize(maxParticles); @@ -187,38 +202,42 @@ void ParticleEmitter::emitParticle() { } float ParticleEmitter::randomFloat(float min, float max) { - std::uniform_real_distribution dist(min, max); - return dist(rng_); + return rng_.nextFloat(min, max); } Vec2 ParticleEmitter::randomPointInShape() { - switch (config_.shape) { - case EmitterConfig::Shape::Point: - return Vec2::Zero(); - - case EmitterConfig::Shape::Circle: { - float angle = randomFloat(0.0f, 2.0f * 3.14159265359f); - float radius = randomFloat(0.0f, config_.shapeRadius); - return Vec2(std::cos(angle) * radius, std::sin(angle) * radius); + // 使用查找表替代 switch + size_t shapeIndex = static_cast(config_.shape); + if (shapeIndex < SHAPE_GENERATOR_COUNT) { + return (this->*SHAPE_GENERATORS[shapeIndex])(); } - - case EmitterConfig::Shape::Rectangle: - return Vec2( - randomFloat(-config_.shapeSize.x * 0.5f, config_.shapeSize.x * 0.5f), - randomFloat(-config_.shapeSize.y * 0.5f, config_.shapeSize.y * 0.5f)); - - case EmitterConfig::Shape::Cone: { - float angle = - randomFloat(-config_.coneAngle * 0.5f, config_.coneAngle * 0.5f); - float radius = randomFloat(0.0f, config_.shapeRadius); - float rad = angle * 3.14159265359f / 180.0f; - return Vec2(std::cos(rad) * radius, std::sin(rad) * radius); - } - } - return Vec2::Zero(); } +Vec2 ParticleEmitter::randomPointShape() { + return Vec2::Zero(); +} + +Vec2 ParticleEmitter::randomCircleShape() { + float angle = randomFloat(0.0f, 2.0f * 3.14159265359f); + float radius = randomFloat(0.0f, config_.shapeRadius); + return Vec2(std::cos(angle) * radius, std::sin(angle) * radius); +} + +Vec2 ParticleEmitter::randomRectangleShape() { + return Vec2( + randomFloat(-config_.shapeSize.x * 0.5f, config_.shapeSize.x * 0.5f), + randomFloat(-config_.shapeSize.y * 0.5f, config_.shapeSize.y * 0.5f)); +} + +Vec2 ParticleEmitter::randomConeShape() { + float angle = + randomFloat(-config_.coneAngle * 0.5f, config_.coneAngle * 0.5f); + float radius = randomFloat(0.0f, config_.shapeRadius); + float rad = angle * 3.14159265359f / 180.0f; + return Vec2(std::cos(rad) * radius, std::sin(rad) * radius); +} + Vec2 ParticleEmitter::randomVelocity() { return Vec2(randomFloat(config_.minVelocity.x, config_.maxVelocity.x), randomFloat(config_.minVelocity.y, config_.maxVelocity.y)); diff --git a/Extra2D/src/graphics/opengl/gl_renderer.cpp b/Extra2D/src/graphics/opengl/gl_renderer.cpp index e457323..4b7dfac 100644 --- a/Extra2D/src/graphics/opengl/gl_renderer.cpp +++ b/Extra2D/src/graphics/opengl/gl_renderer.cpp @@ -14,33 +14,59 @@ namespace extra2d { -// 形状渲染着色器 (GLES 3.2) +// 形状渲染着色器 - 支持顶点颜色批处理 (GLES 3.2) static const char *SHAPE_VERTEX_SHADER = R"( #version 300 es precision highp float; layout(location = 0) in vec2 aPosition; +layout(location = 1) in vec4 aColor; uniform mat4 uViewProjection; +out vec4 vColor; void main() { gl_Position = uViewProjection * vec4(aPosition, 0.0, 1.0); + vColor = aColor; } )"; static const char *SHAPE_FRAGMENT_SHADER = R"( #version 300 es precision highp float; -uniform vec4 uColor; +in vec4 vColor; out vec4 fragColor; void main() { - fragColor = uColor; + fragColor = vColor; } )"; // VBO 初始大小(用于 VRAM 跟踪) static constexpr size_t SHAPE_VBO_SIZE = 1024 * sizeof(float); +// ============================================================================ +// BlendMode 查找表 - 编译期构建,运行时 O(1) 查找 +// ============================================================================ +struct BlendState { + bool enable; + GLenum srcFactor; + GLenum dstFactor; +}; + +static constexpr BlendState BLEND_STATES[] = { + {false, 0, 0}, // BlendMode::None + {true, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}, // BlendMode::Alpha + {true, GL_SRC_ALPHA, GL_ONE}, // BlendMode::Additive + {true, GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA} // BlendMode::Multiply +}; + +static constexpr size_t BLEND_STATE_COUNT = + sizeof(BLEND_STATES) / sizeof(BLEND_STATES[0]); + GLRenderer::GLRenderer() - : window_(nullptr), shapeVao_(0), shapeVbo_(0), vsync_(true) { + : window_(nullptr), shapeVao_(0), shapeVbo_(0), vsync_(true), + shapeVertexCount_(0), currentShapeMode_(GL_TRIANGLES) { resetStats(); + for (auto &v : shapeVertexCache_) { + v = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; + } } GLRenderer::~GLRenderer() { shutdown(); } @@ -98,7 +124,8 @@ void GLRenderer::beginFrame(const Color &clearColor) { } void GLRenderer::endFrame() { - // 交换缓冲区在 Window 类中处理 + // 刷新所有待处理的形状批次 + flushShapeBatch(); } void GLRenderer::setViewport(int x, int y, int width, int height) { @@ -112,22 +139,30 @@ void GLRenderer::setVSync(bool enabled) { } void GLRenderer::setBlendMode(BlendMode mode) { - switch (mode) { - case BlendMode::None: - glDisable(GL_BLEND); - break; - case BlendMode::Alpha: - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - break; - case BlendMode::Additive: - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - break; - case BlendMode::Multiply: - glEnable(GL_BLEND); - glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); - break; + // 状态缓存检查,避免冗余 GL 调用 + if (cachedBlendMode_ == mode) { + return; + } + cachedBlendMode_ = mode; + + // 使用查找表替代 switch + size_t index = static_cast(mode); + if (index >= BLEND_STATE_COUNT) { + index = 0; + } + + const BlendState &state = BLEND_STATES[index]; + if (state.enable) { + if (!blendEnabled_) { + glEnable(GL_BLEND); + blendEnabled_ = true; + } + glBlendFunc(state.srcFactor, state.dstFactor); + } else { + if (blendEnabled_) { + glDisable(GL_BLEND); + blendEnabled_ = false; + } } } @@ -211,13 +246,18 @@ void GLRenderer::endSpriteBatch() { void GLRenderer::drawLine(const Vec2 &start, const Vec2 &end, const Color &color, float width) { + // 线条需要立即绘制,因为需要设置线宽 + // 先刷新之前的批处理 + flushShapeBatch(); + glLineWidth(width); shapeShader_.bind(); shapeShader_.setMat4("uViewProjection", viewProjection_); - shapeShader_.setVec4("uColor", glm::vec4(color.r, color.g, color.b, color.a)); - float vertices[] = {start.x, start.y, end.x, end.y}; + ShapeVertex vertices[] = { + {start.x, start.y, color.r, color.g, color.b, color.a}, + {end.x, end.y, color.r, color.g, color.b, color.a}}; glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); @@ -230,95 +270,122 @@ void GLRenderer::drawLine(const Vec2 &start, const Vec2 &end, } void GLRenderer::drawRect(const Rect &rect, const Color &color, float width) { + // 矩形边框需要立即绘制(因为有线宽参数) + flushShapeBatch(); + float x1 = rect.origin.x; float y1 = rect.origin.y; float x2 = rect.origin.x + rect.size.width; float y2 = rect.origin.y + rect.size.height; - drawLine(Vec2(x1, y1), Vec2(x2, y1), color, width); - drawLine(Vec2(x2, y1), Vec2(x2, y2), color, width); - drawLine(Vec2(x2, y2), Vec2(x1, y2), color, width); - drawLine(Vec2(x1, y2), Vec2(x1, y1), color, width); -} - -void GLRenderer::fillRect(const Rect &rect, const Color &color) { + glLineWidth(width); shapeShader_.bind(); shapeShader_.setMat4("uViewProjection", viewProjection_); - shapeShader_.setVec4("uColor", glm::vec4(color.r, color.g, color.b, color.a)); - float vertices[] = {rect.origin.x, - rect.origin.y, - rect.origin.x + rect.size.width, - rect.origin.y, - rect.origin.x + rect.size.width, - rect.origin.y + rect.size.height, - rect.origin.x, - rect.origin.y + rect.size.height}; + // 4条线段 = 8个顶点 + ShapeVertex vertices[] = {{x1, y1, color.r, color.g, color.b, color.a}, + {x2, y1, color.r, color.g, color.b, color.a}, + {x2, y1, color.r, color.g, color.b, color.a}, + {x2, y2, color.r, color.g, color.b, color.a}, + {x2, y2, color.r, color.g, color.b, color.a}, + {x1, y2, color.r, color.g, color.b, color.a}, + {x1, y2, color.r, color.g, color.b, color.a}, + {x1, y1, color.r, color.g, color.b, color.a}}; glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); glBindVertexArray(shapeVao_); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDrawArrays(GL_LINES, 0, 8); stats_.drawCalls++; - stats_.triangleCount += 2; +} + +void GLRenderer::fillRect(const Rect &rect, const Color &color) { + // 提交当前批次(如果模式不同) + submitShapeBatch(GL_TRIANGLES); + + // 添加两个三角形组成矩形(6个顶点) + float x1 = rect.origin.x; + float y1 = rect.origin.y; + float x2 = rect.origin.x + rect.size.width; + float y2 = rect.origin.y + rect.size.height; + + // 三角形1: (x1,y1), (x2,y1), (x2,y2) + addShapeVertex(x1, y1, color); + addShapeVertex(x2, y1, color); + addShapeVertex(x2, y2, color); + + // 三角形2: (x1,y1), (x2,y2), (x1,y2) + addShapeVertex(x1, y1, color); + addShapeVertex(x2, y2, color); + addShapeVertex(x1, y2, color); } void GLRenderer::drawCircle(const Vec2 ¢er, float radius, const Color &color, int segments, float width) { - std::vector vertices; - vertices.reserve((segments + 1) * 2); - - for (int i = 0; i <= segments; ++i) { - float angle = 2.0f * 3.14159f * i / segments; - vertices.push_back(center.x + radius * cosf(angle)); - vertices.push_back(center.y + radius * sinf(angle)); + // 限制段数不超过缓存大小 + if (segments > static_cast(MAX_CIRCLE_SEGMENTS)) { + segments = static_cast(MAX_CIRCLE_SEGMENTS); } + // 圆形轮廓需要立即绘制(因为有线宽参数) + flushShapeBatch(); + glLineWidth(width); shapeShader_.bind(); shapeShader_.setMat4("uViewProjection", viewProjection_); - shapeShader_.setVec4("uColor", glm::vec4(color.r, color.g, color.b, color.a)); + + // 使用预分配缓冲区,避免每帧内存分配 + const size_t vertexCount = static_cast(segments + 1); + for (int i = 0; i <= segments; ++i) { + float angle = + 2.0f * 3.14159f * static_cast(i) / static_cast(segments); + size_t idx = static_cast(i); + shapeVertexCache_[idx].x = center.x + radius * cosf(angle); + shapeVertexCache_[idx].y = center.y + radius * sinf(angle); + shapeVertexCache_[idx].r = color.r; + shapeVertexCache_[idx].g = color.g; + shapeVertexCache_[idx].b = color.b; + shapeVertexCache_[idx].a = color.a; + } glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_); - glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), - vertices.data(), GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(ShapeVertex), + shapeVertexCache_.data()); glBindVertexArray(shapeVao_); - glDrawArrays(GL_LINE_STRIP, 0, segments + 1); + glDrawArrays(GL_LINE_STRIP, 0, static_cast(vertexCount)); stats_.drawCalls++; } void GLRenderer::fillCircle(const Vec2 ¢er, float radius, const Color &color, int segments) { - std::vector vertices; - vertices.reserve((segments + 2) * 2); - - vertices.push_back(center.x); - vertices.push_back(center.y); - - for (int i = 0; i <= segments; ++i) { - float angle = 2.0f * 3.14159f * i / segments; - vertices.push_back(center.x + radius * cosf(angle)); - vertices.push_back(center.y + radius * sinf(angle)); + // 限制段数不超过缓存大小 + if (segments > static_cast(MAX_CIRCLE_SEGMENTS)) { + segments = static_cast(MAX_CIRCLE_SEGMENTS); } - shapeShader_.bind(); - shapeShader_.setMat4("uViewProjection", viewProjection_); - shapeShader_.setVec4("uColor", glm::vec4(color.r, color.g, color.b, color.a)); + // 提交当前批次(如果模式不同) + submitShapeBatch(GL_TRIANGLES); - glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_); - glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), - vertices.data(), GL_DYNAMIC_DRAW); + // 使用三角形扇形填充圆 + // 中心点 + 边缘点 + for (int i = 0; i < segments; ++i) { + float angle1 = + 2.0f * 3.14159f * static_cast(i) / static_cast(segments); + float angle2 = 2.0f * 3.14159f * static_cast(i + 1) / + static_cast(segments); - glBindVertexArray(shapeVao_); - glDrawArrays(GL_TRIANGLE_FAN, 0, segments + 2); - - stats_.drawCalls++; - stats_.triangleCount += segments; + // 每个三角形:中心 -> 边缘点1 -> 边缘点2 + addShapeVertex(center.x, center.y, color); + addShapeVertex(center.x + radius * cosf(angle1), + center.y + radius * sinf(angle1), color); + addShapeVertex(center.x + radius * cosf(angle2), + center.y + radius * sinf(angle2), color); + } } void GLRenderer::drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, @@ -330,54 +397,66 @@ void GLRenderer::drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, void GLRenderer::fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, const Color &color) { - shapeShader_.bind(); - shapeShader_.setMat4("uViewProjection", viewProjection_); - shapeShader_.setVec4("uColor", glm::vec4(color.r, color.g, color.b, color.a)); + submitShapeBatch(GL_TRIANGLES); - float vertices[] = {p1.x, p1.y, p2.x, p2.y, p3.x, p3.y}; - - glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); - - glBindVertexArray(shapeVao_); - glDrawArrays(GL_TRIANGLES, 0, 3); - - stats_.drawCalls++; - stats_.triangleCount += 1; + addShapeVertex(p1.x, p1.y, color); + addShapeVertex(p2.x, p2.y, color); + addShapeVertex(p3.x, p3.y, color); } void GLRenderer::drawPolygon(const std::vector &points, const Color &color, float width) { - for (size_t i = 0; i < points.size(); ++i) { - drawLine(points[i], points[(i + 1) % points.size()], color, width); + flushShapeBatch(); + + glLineWidth(width); + shapeShader_.bind(); + shapeShader_.setMat4("uViewProjection", viewProjection_); + + size_t pointCount = std::min(points.size(), MAX_CIRCLE_SEGMENTS); + + for (size_t i = 0; i < pointCount; ++i) { + size_t idx = i; + shapeVertexCache_[idx].x = points[i].x; + shapeVertexCache_[idx].y = points[i].y; + shapeVertexCache_[idx].r = color.r; + shapeVertexCache_[idx].g = color.g; + shapeVertexCache_[idx].b = color.b; + shapeVertexCache_[idx].a = color.a; } + + // 闭合多边形 + shapeVertexCache_[pointCount].x = points[0].x; + shapeVertexCache_[pointCount].y = points[0].y; + shapeVertexCache_[pointCount].r = color.r; + shapeVertexCache_[pointCount].g = color.g; + shapeVertexCache_[pointCount].b = color.b; + shapeVertexCache_[pointCount].a = color.a; + pointCount++; + + glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_); + glBufferSubData(GL_ARRAY_BUFFER, 0, pointCount * sizeof(ShapeVertex), + shapeVertexCache_.data()); + + glBindVertexArray(shapeVao_); + glDrawArrays(GL_LINE_STRIP, 0, static_cast(pointCount)); + + stats_.drawCalls++; } void GLRenderer::fillPolygon(const std::vector &points, const Color &color) { - // 简化的三角形扇形填充 if (points.size() < 3) return; - shapeShader_.bind(); - shapeShader_.setMat4("uViewProjection", viewProjection_); - shapeShader_.setVec4("uColor", glm::vec4(color.r, color.g, color.b, color.a)); + submitShapeBatch(GL_TRIANGLES); - std::vector vertices; - for (const auto &p : points) { - vertices.push_back(p.x); - vertices.push_back(p.y); + // 使用三角形扇形填充 + // 从第一个点开始,每两个相邻点组成一个三角形 + for (size_t i = 1; i < points.size() - 1; ++i) { + addShapeVertex(points[0].x, points[0].y, color); + addShapeVertex(points[i].x, points[i].y, color); + addShapeVertex(points[i + 1].x, points[i + 1].y, color); } - - glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_); - glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), - vertices.data(), GL_DYNAMIC_DRAW); - - glBindVertexArray(shapeVao_); - glDrawArrays(GL_TRIANGLE_FAN, 0, static_cast(points.size())); - - stats_.drawCalls++; - stats_.triangleCount += static_cast(points.size() - 2); } Ptr GLRenderer::createFontAtlas(const std::string &filepath, @@ -390,8 +469,8 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text, drawText(font, text, position.x, position.y, color); } -void GLRenderer::drawText(const FontAtlas &font, const std::string &text, float x, - float y, const Color &color) { +void GLRenderer::drawText(const FontAtlas &font, const std::string &text, + float x, float y, const Color &color) { float cursorX = x; float cursorY = y; float baselineY = cursorY + font.getAscent(); @@ -444,15 +523,69 @@ void GLRenderer::initShapeRendering() { glBindVertexArray(shapeVao_); glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_); - glBufferData(GL_ARRAY_BUFFER, SHAPE_VBO_SIZE, nullptr, GL_DYNAMIC_DRAW); + // 使用更大的缓冲区以支持形状批处理 + glBufferData(GL_ARRAY_BUFFER, MAX_SHAPE_VERTICES * sizeof(ShapeVertex), + nullptr, GL_DYNAMIC_DRAW); + // 位置属性 (location = 0) glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), nullptr); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex), + reinterpret_cast(offsetof(ShapeVertex, x))); + + // 颜色属性 (location = 1) + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex), + reinterpret_cast(offsetof(ShapeVertex, r))); glBindVertexArray(0); // VRAM 跟踪 - VRAMManager::getInstance().allocBuffer(SHAPE_VBO_SIZE); + VRAMManager::getInstance().allocBuffer(MAX_SHAPE_VERTICES * + sizeof(ShapeVertex)); +} + +void GLRenderer::addShapeVertex(float x, float y, const Color &color) { + if (shapeVertexCount_ >= MAX_SHAPE_VERTICES) { + flushShapeBatch(); + } + ShapeVertex &v = shapeVertexCache_[shapeVertexCount_++]; + v.x = x; + v.y = y; + v.r = color.r; + v.g = color.g; + v.b = color.b; + v.a = color.a; +} + +void GLRenderer::submitShapeBatch(GLenum mode) { + if (shapeVertexCount_ == 0) + return; + + // 如果模式改变,先刷新 + if (currentShapeMode_ != mode && shapeVertexCount_ > 0) { + flushShapeBatch(); + } + currentShapeMode_ = mode; +} + +void GLRenderer::flushShapeBatch() { + if (shapeVertexCount_ == 0) + return; + + shapeShader_.bind(); + shapeShader_.setMat4("uViewProjection", viewProjection_); + + glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_); + glBufferSubData(GL_ARRAY_BUFFER, 0, shapeVertexCount_ * sizeof(ShapeVertex), + shapeVertexCache_.data()); + + glBindVertexArray(shapeVao_); + glDrawArrays(currentShapeMode_, 0, static_cast(shapeVertexCount_)); + + stats_.drawCalls++; + stats_.triangleCount += static_cast(shapeVertexCount_ / 3); + + shapeVertexCount_ = 0; } } // namespace extra2d diff --git a/Extra2D/src/graphics/opengl/gl_sprite_batch.cpp b/Extra2D/src/graphics/opengl/gl_sprite_batch.cpp index bcbd9af..f61579b 100644 --- a/Extra2D/src/graphics/opengl/gl_sprite_batch.cpp +++ b/Extra2D/src/graphics/opengl/gl_sprite_batch.cpp @@ -5,6 +5,50 @@ namespace extra2d { +// ============================================================================ +// 三角函数查表 - 避免每帧重复计算 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; + + static float sinRad(float radians) { + return table_.sinTable[normalizeIndexRad(radians)]; + } + + 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_; + // 顶点着色器 (GLES 3.2) static const char *SPRITE_VERTEX_SHADER = R"( #version 300 es @@ -156,8 +200,9 @@ void GLSpriteBatch::addVertices(const SpriteData &data) { glm::vec2 anchorOffset(data.size.x * data.anchor.x, data.size.y * data.anchor.y); - float cosR = cosf(data.rotation); - float sinR = sinf(data.rotation); + // 使用三角函数查表替代 cosf/sinf + float cosR = TrigLookup::cosRad(data.rotation); + float sinR = TrigLookup::sinRad(data.rotation); auto transform = [&](float x, float y) -> glm::vec2 { float rx = x - anchorOffset.x; diff --git a/Extra2D/src/scene/node.cpp b/Extra2D/src/scene/node.cpp index c566eff..0dfae1a 100644 --- a/Extra2D/src/scene/node.cpp +++ b/Extra2D/src/scene/node.cpp @@ -253,24 +253,23 @@ glm::mat4 Node::getLocalTransform() const { glm::mat4 Node::getWorldTransform() const { if (worldTransformDirty_) { - // 迭代计算世界变换,避免深层级时的栈溢出 - // 收集父节点链 - std::vector nodeChain; + // 使用线程局部存储的固定数组,避免每帧内存分配 + // 限制最大深度为 256 层,足以覆盖绝大多数场景 + thread_local std::array nodeChainCache; + thread_local size_t chainCount = 0; + + chainCount = 0; const Node *current = this; - while (current) { - nodeChain.push_back(current); + while (current && chainCount < nodeChainCache.size()) { + nodeChainCache[chainCount++] = current; auto p = current->parent_.lock(); current = p.get(); - // 限制最大深度,防止异常循环 - if (nodeChain.size() > 1000) { - break; - } } // 从根节点开始计算 glm::mat4 transform = glm::mat4(1.0f); - for (auto it = nodeChain.rbegin(); it != nodeChain.rend(); ++it) { - transform = transform * (*it)->getLocalTransform(); + for (size_t i = chainCount; i > 0; --i) { + transform = transform * nodeChainCache[i - 1]->getLocalTransform(); } worldTransform_ = transform; worldTransformDirty_ = false; diff --git a/Extra2D/src/scene/scene_manager.cpp b/Extra2D/src/scene/scene_manager.cpp index a936697..8d7b1c8 100644 --- a/Extra2D/src/scene/scene_manager.cpp +++ b/Extra2D/src/scene/scene_manager.cpp @@ -9,6 +9,52 @@ namespace extra2d { +// ============================================================================ +// Transition 工厂映射 - 使用函数指针数组替代 switch +// ============================================================================ +using TransitionFactory = Ptr (*)(float); + +static Ptr createFadeTransition(float duration) { + return makePtr(duration); +} + +static Ptr createSlideLeftTransition(float duration) { + return makePtr(duration, TransitionDirection::Left); +} + +static Ptr createSlideRightTransition(float duration) { + return makePtr(duration, TransitionDirection::Right); +} + +static Ptr createSlideUpTransition(float duration) { + return makePtr(duration, TransitionDirection::Up); +} + +static Ptr createSlideDownTransition(float duration) { + return makePtr(duration, TransitionDirection::Down); +} + +static Ptr createScaleTransition(float duration) { + return makePtr(duration); +} + +static Ptr createFlipTransition(float duration) { + return makePtr(duration); +} + +// 工厂函数指针数组,索引对应 TransitionType 枚举值 +static constexpr TransitionFactory TRANSITION_FACTORIES[] = { + createFadeTransition, // TransitionType::Fade = 0 + createSlideLeftTransition, // TransitionType::SlideLeft = 1 + createSlideRightTransition,// TransitionType::SlideRight = 2 + createSlideUpTransition, // TransitionType::SlideUp = 3 + createSlideDownTransition, // TransitionType::SlideDown = 4 + createScaleTransition, // TransitionType::Scale = 5 + createFlipTransition // TransitionType::Flip = 6 +}; + +static constexpr size_t TRANSITION_FACTORY_COUNT = sizeof(TRANSITION_FACTORIES) / sizeof(TRANSITION_FACTORIES[0]); + namespace { Node *hitTestTopmost(const Ptr &node, const Vec2 &worldPos) { @@ -509,32 +555,14 @@ void SceneManager::startTransition(Ptr from, Ptr to, return; } + // 使用工厂映射替代 switch Ptr transition; - switch (type) { - case TransitionType::Fade: - transition = makePtr(duration); - break; - case TransitionType::SlideLeft: - transition = makePtr(duration, TransitionDirection::Left); - break; - case TransitionType::SlideRight: - transition = makePtr(duration, TransitionDirection::Right); - break; - case TransitionType::SlideUp: - transition = makePtr(duration, TransitionDirection::Up); - break; - case TransitionType::SlideDown: - transition = makePtr(duration, TransitionDirection::Down); - break; - case TransitionType::Scale: - transition = makePtr(duration); - break; - case TransitionType::Flip: - transition = makePtr(duration); - break; - default: - transition = makePtr(duration); - break; + size_t typeIndex = static_cast(type); + if (typeIndex < TRANSITION_FACTORY_COUNT) { + transition = TRANSITION_FACTORIES[typeIndex](duration); + } else { + // 默认使用 Fade 过渡 + transition = TRANSITION_FACTORIES[0](duration); } transition->start(from, to);