perf: 优化渲染和计算性能

- 使用线程局部存储和固定数组优化节点世界变换计算,避免内存分配
- 实现快速随机数生成器替代 std::mt19937 提升粒子系统性能
- 添加三角函数查表优化精灵旋转计算
- 使用工厂函数指针数组替代 switch 语句优化场景过渡创建
- 实现形状生成函数查找表优化粒子发射器
- 重构渲染器实现形状批处理和状态缓存,减少 GL 调用
- 添加预分配缓冲区和顶点缓存优化形状渲染性能
This commit is contained in:
ChestnutYueyue 2026-02-13 10:00:39 +08:00
parent 4107e0b12b
commit ca41af0dbd
7 changed files with 523 additions and 226 deletions

View File

@ -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<float>(next()) / static_cast<float>(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);

View File

@ -1,9 +1,10 @@
#pragma once
#include <extra2d/graphics/render_backend.h>
#include <extra2d/graphics/opengl/gl_shader.h>
#include <extra2d/graphics/opengl/gl_sprite_batch.h>
#include <extra2d/graphics/render_backend.h>
#include <array>
#include <glad/glad.h>
#include <vector>
@ -36,33 +37,56 @@ public:
void popTransform() override;
glm::mat4 getCurrentTransform() const override;
Ptr<Texture> createTexture(int width, int height, const uint8_t* pixels, int channels) override;
Ptr<Texture> createTexture(int width, int height, const uint8_t *pixels,
int channels) override;
Ptr<Texture> 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 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 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<Vec2>& points, const Color& color, float width) override;
void fillPolygon(const std::vector<Vec2>& points, 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<Vec2> &points, const Color &color,
float width) override;
void fillPolygon(const std::vector<Vec2> &points,
const Color &color) override;
Ptr<FontAtlas> 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<FontAtlas> 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:
// 形状批处理常量
static constexpr size_t MAX_CIRCLE_SEGMENTS = 128;
static constexpr size_t MAX_SHAPE_VERTICES = 8192; // 最大形状顶点数
// 形状顶点结构(包含颜色)
struct ShapeVertex {
float x, y;
float r, g, b, a;
};
Window *window_;
GLSpriteBatch spriteBatch_;
GLShader shapeShader_;
@ -75,8 +99,23 @@ private:
Stats stats_;
bool vsync_;
// 形状批处理缓冲区(预分配,避免每帧内存分配)
std::array<ShapeVertex, MAX_SHAPE_VERTICES> 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 setupBlendMode(BlendMode mode);
void flushShapeBatch();
void addShapeVertex(float x, float y, const Color &color);
void submitShapeBatch(GLenum mode);
};
} // namespace extra2d

View File

@ -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<uint32_t>(std::random_device{}())) {}
bool ParticleEmitter::init(size_t maxParticles) {
particles_.resize(maxParticles);
@ -187,37 +202,41 @@ void ParticleEmitter::emitParticle() {
}
float ParticleEmitter::randomFloat(float min, float max) {
std::uniform_real_distribution<float> dist(min, max);
return dist(rng_);
return rng_.nextFloat(min, max);
}
Vec2 ParticleEmitter::randomPointInShape() {
switch (config_.shape) {
case EmitterConfig::Shape::Point:
// 使用查找表替代 switch
size_t shapeIndex = static_cast<size_t>(config_.shape);
if (shapeIndex < SHAPE_GENERATOR_COUNT) {
return (this->*SHAPE_GENERATORS[shapeIndex])();
}
return Vec2::Zero();
}
case EmitterConfig::Shape::Circle: {
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);
}
case EmitterConfig::Shape::Rectangle:
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));
}
case EmitterConfig::Shape::Cone: {
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);
}
}
return Vec2::Zero();
}
Vec2 ParticleEmitter::randomVelocity() {
return Vec2(randomFloat(config_.minVelocity.x, config_.maxVelocity.x),

View File

@ -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:
// 状态缓存检查,避免冗余 GL 调用
if (cachedBlendMode_ == mode) {
return;
}
cachedBlendMode_ = mode;
// 使用查找表替代 switch
size_t index = static_cast<size_t>(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);
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;
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 &center, float radius,
const Color &color, int segments, float width) {
std::vector<float> 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<int>(MAX_CIRCLE_SEGMENTS)) {
segments = static_cast<int>(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<size_t>(segments + 1);
for (int i = 0; i <= segments; ++i) {
float angle =
2.0f * 3.14159f * static_cast<float>(i) / static_cast<float>(segments);
size_t idx = static_cast<size_t>(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<GLsizei>(vertexCount));
stats_.drawCalls++;
}
void GLRenderer::fillCircle(const Vec2 &center, float radius,
const Color &color, int segments) {
std::vector<float> 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<int>(MAX_CIRCLE_SEGMENTS)) {
segments = static_cast<int>(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<float>(i) / static_cast<float>(segments);
float angle2 = 2.0f * 3.14159f * static_cast<float>(i + 1) /
static_cast<float>(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<Vec2> &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<GLsizei>(pointCount));
stats_.drawCalls++;
}
void GLRenderer::fillPolygon(const std::vector<Vec2> &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<float> 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<GLsizei>(points.size()));
stats_.drawCalls++;
stats_.triangleCount += static_cast<uint32_t>(points.size() - 2);
}
Ptr<FontAtlas> 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<void *>(offsetof(ShapeVertex, x)));
// 颜色属性 (location = 1)
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex),
reinterpret_cast<void *>(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<GLsizei>(shapeVertexCount_));
stats_.drawCalls++;
stats_.triangleCount += static_cast<uint32_t>(shapeVertexCount_ / 3);
shapeVertexCount_ = 0;
}
} // namespace extra2d

View File

@ -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<float, TABLE_SIZE> sinTable;
std::array<float, TABLE_SIZE> cosTable;
Tables() {
for (size_t i = 0; i < TABLE_SIZE; ++i) {
float angle = static_cast<float>(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<int>(radians * RAD_TO_INDEX) % static_cast<int>(TABLE_SIZE);
if (idx < 0) {
idx += static_cast<int>(TABLE_SIZE);
}
return static_cast<size_t>(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;

View File

@ -253,24 +253,23 @@ glm::mat4 Node::getLocalTransform() const {
glm::mat4 Node::getWorldTransform() const {
if (worldTransformDirty_) {
// 迭代计算世界变换,避免深层级时的栈溢出
// 收集父节点链
std::vector<const Node *> nodeChain;
// 使用线程局部存储的固定数组,避免每帧内存分配
// 限制最大深度为 256 层,足以覆盖绝大多数场景
thread_local std::array<const Node*, 256> 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;

View File

@ -9,6 +9,52 @@
namespace extra2d {
// ============================================================================
// Transition 工厂映射 - 使用函数指针数组替代 switch
// ============================================================================
using TransitionFactory = Ptr<Transition> (*)(float);
static Ptr<Transition> createFadeTransition(float duration) {
return makePtr<FadeTransition>(duration);
}
static Ptr<Transition> createSlideLeftTransition(float duration) {
return makePtr<SlideTransition>(duration, TransitionDirection::Left);
}
static Ptr<Transition> createSlideRightTransition(float duration) {
return makePtr<SlideTransition>(duration, TransitionDirection::Right);
}
static Ptr<Transition> createSlideUpTransition(float duration) {
return makePtr<SlideTransition>(duration, TransitionDirection::Up);
}
static Ptr<Transition> createSlideDownTransition(float duration) {
return makePtr<SlideTransition>(duration, TransitionDirection::Down);
}
static Ptr<Transition> createScaleTransition(float duration) {
return makePtr<ScaleTransition>(duration);
}
static Ptr<Transition> createFlipTransition(float duration) {
return makePtr<FlipTransition>(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> &node, const Vec2 &worldPos) {
@ -509,32 +555,14 @@ void SceneManager::startTransition(Ptr<Scene> from, Ptr<Scene> to,
return;
}
// 使用工厂映射替代 switch
Ptr<Transition> transition;
switch (type) {
case TransitionType::Fade:
transition = makePtr<FadeTransition>(duration);
break;
case TransitionType::SlideLeft:
transition = makePtr<SlideTransition>(duration, TransitionDirection::Left);
break;
case TransitionType::SlideRight:
transition = makePtr<SlideTransition>(duration, TransitionDirection::Right);
break;
case TransitionType::SlideUp:
transition = makePtr<SlideTransition>(duration, TransitionDirection::Up);
break;
case TransitionType::SlideDown:
transition = makePtr<SlideTransition>(duration, TransitionDirection::Down);
break;
case TransitionType::Scale:
transition = makePtr<ScaleTransition>(duration);
break;
case TransitionType::Flip:
transition = makePtr<FlipTransition>(duration);
break;
default:
transition = makePtr<FadeTransition>(duration);
break;
size_t typeIndex = static_cast<size_t>(type);
if (typeIndex < TRANSITION_FACTORY_COUNT) {
transition = TRANSITION_FACTORIES[typeIndex](duration);
} else {
// 默认使用 Fade 过渡
transition = TRANSITION_FACTORIES[0](duration);
}
transition->start(from, to);