refactor(renderer): 优化文本渲染的批处理逻辑并调整代码格式

重构文本渲染的批处理逻辑,添加纹理变化检查并优化换行处理
同时调整部分代码格式以提高可读性
This commit is contained in:
ChestnutYueyue 2026-02-17 20:44:33 +08:00
parent 32e12b8c99
commit c8a6ea19e3
2 changed files with 140 additions and 139 deletions

View File

@ -1,13 +1,13 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#include <extra2d/graphics/memory/gpu_context.h>
#include <extra2d/graphics/backends/opengl/gl_font_atlas.h> #include <extra2d/graphics/backends/opengl/gl_font_atlas.h>
#include <extra2d/graphics/backends/opengl/gl_renderer.h> #include <extra2d/graphics/backends/opengl/gl_renderer.h>
#include <extra2d/graphics/backends/opengl/gl_texture.h> #include <extra2d/graphics/backends/opengl/gl_texture.h>
#include <extra2d/graphics/batch/sprite_batch.h> #include <extra2d/graphics/batch/sprite_batch.h>
#include <extra2d/graphics/shader/shader_manager.h> #include <extra2d/graphics/memory/gpu_context.h>
#include <extra2d/graphics/memory/vram_manager.h> #include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/platform/iwindow.h> #include <extra2d/platform/iwindow.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
#include <vector> #include <vector>
@ -340,10 +340,8 @@ void GLRenderer::drawSprite(const Texture &texture, const Rect &destRect,
float v1 = srcRect.origin.y / texH; float v1 = srcRect.origin.y / texH;
float v2 = (srcRect.origin.y + srcRect.size.height) / texH; float v2 = (srcRect.origin.y + srcRect.size.height) / texH;
data.uvRect = Rect( data.uvRect = Rect(Vec2(glm::min(u1, u2), glm::min(v1, v2)),
Vec2(glm::min(u1, u2), glm::min(v1, v2)), Size(glm::abs(u2 - u1), glm::abs(v2 - v1)));
Size(glm::abs(u2 - u1), glm::abs(v2 - v1))
);
data.color = tint; data.color = tint;
data.rotation = rotation * 3.14159f / 180.0f; data.rotation = rotation * 3.14159f / 180.0f;
@ -366,10 +364,8 @@ void GLRenderer::drawSprite(const Texture &texture, const Rect &destRect,
float v1 = srcRect.origin.y / texH; float v1 = srcRect.origin.y / texH;
float v2 = (srcRect.origin.y + srcRect.size.height) / texH; float v2 = (srcRect.origin.y + srcRect.size.height) / texH;
data.uvRect = Rect( data.uvRect = Rect(Vec2(glm::min(u1, u2), glm::min(v1, v2)),
Vec2(glm::min(u1, u2), glm::min(v1, v2)), Size(glm::abs(u2 - u1), glm::abs(v2 - v1)));
Size(glm::abs(u2 - u1), glm::abs(v2 - v1))
);
data.color = tint; data.color = tint;
data.rotation = rotation * 3.14159f / 180.0f; data.rotation = rotation * 3.14159f / 180.0f;
@ -688,6 +684,15 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text,
ensureBatchActive(); ensureBatchActive();
} }
// 检查纹理变化,如果纹理不同则先提交当前批次
if (autoBatchEnabled_ && currentBatchTexture_ != nullptr &&
currentBatchTexture_ != font.getTexture()) {
submitPendingSprites();
}
if (autoBatchEnabled_) {
currentBatchTexture_ = font.getTexture();
}
// 收集所有字符数据用于批处理 // 收集所有字符数据用于批处理
std::vector<SpriteData> sprites; std::vector<SpriteData> sprites;
sprites.reserve(text.size()); // 预分配空间 sprites.reserve(text.size()); // 预分配空间
@ -695,9 +700,15 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text,
for (char c : text) { for (char c : text) {
char32_t codepoint = static_cast<char32_t>(static_cast<unsigned char>(c)); char32_t codepoint = static_cast<char32_t>(static_cast<unsigned char>(c));
if (codepoint == '\n') { if (codepoint == '\n') {
// 提交当前批次(换行时) // 换行时,将当前行添加到待处理列表
if (autoBatchEnabled_ && !sprites.empty()) { if (!sprites.empty()) {
if (autoBatchEnabled_) {
pendingSprites_.insert(pendingSprites_.end(), sprites.begin(),
sprites.end());
} else {
// 手动模式直接提交
spriteBatch_.drawBatch(*font.getTexture(), sprites); spriteBatch_.drawBatch(*font.getTexture(), sprites);
}
sprites.clear(); sprites.clear();
} }
cursorX = x; cursorX = x;
@ -725,12 +736,11 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text,
SpriteData data; SpriteData data;
// 设置精灵中心位置(精灵批处理使用中心点) // 设置精灵中心位置(精灵批处理使用中心点)
data.position = Vec2(xPos + glyph->width * 0.5f, yPos + glyph->height * 0.5f); data.position =
Vec2(xPos + glyph->width * 0.5f, yPos + glyph->height * 0.5f);
data.size = Vec2(glyph->width, glyph->height); data.size = Vec2(glyph->width, glyph->height);
data.uvRect = Rect( data.uvRect = Rect(Vec2(glyph->u0, glyph->v0),
Vec2(glyph->u0, glyph->v0), Size(glyph->u1 - glyph->u0, glyph->v1 - glyph->v0));
Size(glyph->u1 - glyph->u0, glyph->v1 - glyph->v0)
);
data.color = color; data.color = color;
data.rotation = 0.0f; data.rotation = 0.0f;
// pivot (0.5, 0.5) 表示中心点,这样 position 就是精灵中心 // pivot (0.5, 0.5) 表示中心点,这样 position 就是精灵中心
@ -740,7 +750,8 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text,
// 自动批处理:如果缓冲区满,先提交当前批次 // 自动批处理:如果缓冲区满,先提交当前批次
if (autoBatchEnabled_ && sprites.size() >= MAX_BATCH_SPRITES) { if (autoBatchEnabled_ && sprites.size() >= MAX_BATCH_SPRITES) {
spriteBatch_.drawBatch(*font.getTexture(), sprites); pendingSprites_.insert(pendingSprites_.end(), sprites.begin(),
sprites.end());
sprites.clear(); sprites.clear();
} }
} }
@ -749,14 +760,11 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text,
// 提交剩余的字符 // 提交剩余的字符
if (!sprites.empty()) { if (!sprites.empty()) {
if (autoBatchEnabled_) { if (autoBatchEnabled_) {
spriteBatch_.drawBatch(*font.getTexture(), sprites); pendingSprites_.insert(pendingSprites_.end(), sprites.begin(),
sprites.end());
} else { } else {
// 手动模式下,添加到待处理列表 // 手动模式下直接提交
if (currentBatchTexture_ != font.getTexture()) { spriteBatch_.drawBatch(*font.getTexture(), sprites);
submitPendingSprites();
currentBatchTexture_ = font.getTexture();
}
pendingSprites_.insert(pendingSprites_.end(), sprites.begin(), sprites.end());
} }
} }
} }

View File

@ -6,15 +6,7 @@
* OpenGL * OpenGL
*/ */
#include <extra2d/core/service_locator.h>
#include <extra2d/extra2d.h> #include <extra2d/extra2d.h>
#include <extra2d/graphics/core/render_backend.h>
#include <extra2d/graphics/core/render_module.h>
#include <extra2d/graphics/texture/texture.h>
#include <extra2d/platform/input_module.h>
#include <extra2d/platform/window_module.h>
#include <extra2d/services/camera_service.h>
#include <extra2d/services/event_service.h>
#include <iostream> #include <iostream>
using namespace extra2d; using namespace extra2d;
@ -27,12 +19,15 @@ public:
try { try {
texture_ = renderer_->loadTexture("assets/images/demo.jpg"); texture_ = renderer_->loadTexture("assets/images/demo.jpg");
std::cout << "Image loaded successfully!" << std::endl; std::cout << "Image loaded successfully!" << std::endl;
std::cout << " Size: " << texture_->getWidth() << "x" << texture_->getHeight() << std::endl; std::cout << " Size: " << texture_->getWidth() << "x"
<< texture_->getHeight() << std::endl;
} catch (...) { } catch (...) {
std::cerr << "Failed to load image from assets/images/demo.jpg" << std::endl; std::cerr << "Failed to load image from assets/images/demo.jpg"
<< std::endl;
// 尝试使用备用路径 // 尝试使用备用路径
try { try {
texture_ = renderer_->loadTexture("examples/image_display/assets/images/demo.jpg"); texture_ = renderer_->loadTexture(
"examples/image_display/assets/images/demo.jpg");
std::cout << "Image loaded from alternate path!" << std::endl; std::cout << "Image loaded from alternate path!" << std::endl;
} catch (...) { } catch (...) {
std::cerr << "Failed to load image from alternate path!" << std::endl; std::cerr << "Failed to load image from alternate path!" << std::endl;
@ -41,11 +36,9 @@ public:
} }
} }
void onExit() override { void onExit() override { texture_.reset(); }
texture_.reset();
}
void onRender(RenderBackend& renderer) override { void onRender(RenderBackend &renderer) override {
Scene::onRender(renderer); Scene::onRender(renderer);
if (!texture_) { if (!texture_) {
@ -79,30 +72,29 @@ public:
// 参数:纹理、目标矩形、源矩形、颜色、旋转角度、锚点 // 参数:纹理、目标矩形、源矩形、颜色、旋转角度、锚点
Rect destRect(x, y, displayWidth, displayHeight); Rect destRect(x, y, displayWidth, displayHeight);
Rect srcRect(0, 0, imgWidth, imgHeight); Rect srcRect(0, 0, imgWidth, imgHeight);
renderer.drawSprite(*texture_, destRect, srcRect, Colors::White, 0.0f, Vec2(0, 0)); renderer.drawSprite(*texture_, destRect, srcRect, Colors::White, 0.0f,
Vec2(0, 0));
// 注意:无需手动调用 renderer.endSpriteBatch(),帧结束时会自动刷新 // 注意:无需手动调用 renderer.endSpriteBatch(),帧结束时会自动刷新
} }
void setRenderer(RenderBackend* renderer) { void setRenderer(RenderBackend *renderer) { renderer_ = renderer; }
renderer_ = renderer;
}
private: private:
Ptr<Texture> texture_; Ptr<Texture> texture_;
RenderBackend* renderer_ = nullptr; RenderBackend *renderer_ = nullptr;
}; };
int main(int argc, char* argv[]) { int main(int argc, char *argv[]) {
(void)argc; (void)argc;
(void)argv; (void)argv;
std::cout << "Extra2D Image Display Demo - Starting..." << std::endl; std::cout << "Extra2D Image Display Demo - Starting..." << std::endl;
Application& app = Application::get(); Application &app = Application::get();
// 注册模块 // 注册模块
app.use<WindowModule>([](auto& cfg) { app.use<WindowModule>([](auto &cfg) {
cfg.w = 1280; cfg.w = 1280;
cfg.h = 720; cfg.h = 720;
cfg.title = "Extra2D Image Display Demo"; cfg.title = "Extra2D Image Display Demo";
@ -110,9 +102,9 @@ int main(int argc, char* argv[]) {
cfg.backend = "glfw"; cfg.backend = "glfw";
}); });
app.use<RenderModule>([](auto& cfg) { cfg.priority = 10; }); app.use<RenderModule>([](auto &cfg) { cfg.priority = 10; });
app.use<InputModule>([](auto& cfg) { cfg.priority = 20; }); app.use<InputModule>([](auto &cfg) { cfg.priority = 20; });
std::cout << "Initializing application..." << std::endl; std::cout << "Initializing application..." << std::endl;
if (!app.init()) { if (!app.init()) {
@ -122,16 +114,17 @@ int main(int argc, char* argv[]) {
std::cout << "Application initialized successfully!" << std::endl; std::cout << "Application initialized successfully!" << std::endl;
auto* win = app.window(); auto *win = app.window();
if (win) { if (win) {
std::cout << "Window: " << win->width() << "x" << win->height() << std::endl; std::cout << "Window: " << win->width() << "x" << win->height()
<< std::endl;
} }
// 设置事件监听 // 设置事件监听
auto eventService = ServiceLocator::instance().getService<IEventService>(); auto eventService = ServiceLocator::instance().getService<IEventService>();
if (eventService) { if (eventService) {
eventService->addListener(EventType::KeyPressed, [](Event& e) { eventService->addListener(EventType::KeyPressed, [](Event &e) {
auto& keyEvent = std::get<KeyEvent>(e.data); auto &keyEvent = std::get<KeyEvent>(e.data);
if (keyEvent.keyCode == static_cast<int>(Key::Escape)) { if (keyEvent.keyCode == static_cast<int>(Key::Escape)) {
e.handled = true; e.handled = true;
Application::get().quit(); Application::get().quit();
@ -140,7 +133,7 @@ int main(int argc, char* argv[]) {
} }
// 获取渲染器 // 获取渲染器
RenderBackend* renderer = app.renderer(); RenderBackend *renderer = app.renderer();
// 创建并配置场景 // 创建并配置场景
auto scene = makeShared<ImageDisplayScene>(); auto scene = makeShared<ImageDisplayScene>();