refactor(renderer): 优化文本渲染的批处理逻辑并调整代码格式
重构文本渲染的批处理逻辑,添加纹理变化检查并优化换行处理 同时调整部分代码格式以提高可读性
This commit is contained in:
parent
32e12b8c99
commit
c8a6ea19e3
|
|
@ -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()) {
|
||||||
spriteBatch_.drawBatch(*font.getTexture(), sprites);
|
if (autoBatchEnabled_) {
|
||||||
|
pendingSprites_.insert(pendingSprites_.end(), sprites.begin(),
|
||||||
|
sprites.end());
|
||||||
|
} else {
|
||||||
|
// 手动模式直接提交
|
||||||
|
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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,53 +6,46 @@
|
||||||
* 此示例不依赖任何特定渲染后端(如 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;
|
||||||
|
|
||||||
class ImageDisplayScene : public Scene {
|
class ImageDisplayScene : public Scene {
|
||||||
public:
|
public:
|
||||||
void onEnter() override {
|
void onEnter() override {
|
||||||
// 加载图片
|
// 加载图片
|
||||||
// 注意:请确保有图片文件在 assets/images/ 目录下
|
// 注意:请确保有图片文件在 assets/images/ 目录下
|
||||||
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"
|
||||||
} catch (...) {
|
<< texture_->getHeight() << std::endl;
|
||||||
std::cerr << "Failed to load image from assets/images/demo.jpg" << std::endl;
|
} catch (...) {
|
||||||
// 尝试使用备用路径
|
std::cerr << "Failed to load image from assets/images/demo.jpg"
|
||||||
try {
|
<< std::endl;
|
||||||
texture_ = renderer_->loadTexture("examples/image_display/assets/images/demo.jpg");
|
// 尝试使用备用路径
|
||||||
std::cout << "Image loaded from alternate path!" << std::endl;
|
try {
|
||||||
} catch (...) {
|
texture_ = renderer_->loadTexture(
|
||||||
std::cerr << "Failed to load image from alternate path!" << std::endl;
|
"examples/image_display/assets/images/demo.jpg");
|
||||||
texture_ = nullptr;
|
std::cout << "Image loaded from alternate path!" << std::endl;
|
||||||
}
|
} catch (...) {
|
||||||
}
|
std::cerr << "Failed to load image from alternate path!" << std::endl;
|
||||||
|
texture_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onExit() override { texture_.reset(); }
|
||||||
|
|
||||||
|
void onRender(RenderBackend &renderer) override {
|
||||||
|
Scene::onRender(renderer);
|
||||||
|
|
||||||
|
if (!texture_) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onExit() override {
|
// 使用 RenderBackend 的抽象接口绘制图片
|
||||||
texture_.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void onRender(RenderBackend& renderer) override {
|
|
||||||
Scene::onRender(renderer);
|
|
||||||
|
|
||||||
if (!texture_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用 RenderBackend 的抽象接口绘制图片
|
|
||||||
// 不依赖任何特定后端(如 OpenGL)
|
// 不依赖任何特定后端(如 OpenGL)
|
||||||
// 自动批处理:无需手动调用 begin/endSpriteBatch
|
// 自动批处理:无需手动调用 begin/endSpriteBatch
|
||||||
|
|
||||||
|
|
@ -79,102 +72,102 @@ 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";
|
||||||
cfg.priority = 0;
|
cfg.priority = 0;
|
||||||
cfg.backend = "glfw";
|
cfg.backend = "glfw";
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use<RenderModule>([](auto &cfg) { cfg.priority = 10; });
|
||||||
|
|
||||||
|
app.use<InputModule>([](auto &cfg) { cfg.priority = 20; });
|
||||||
|
|
||||||
|
std::cout << "Initializing application..." << std::endl;
|
||||||
|
if (!app.init()) {
|
||||||
|
std::cerr << "Failed to initialize application!" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Application initialized successfully!" << std::endl;
|
||||||
|
|
||||||
|
auto *win = app.window();
|
||||||
|
if (win) {
|
||||||
|
std::cout << "Window: " << win->width() << "x" << win->height()
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置事件监听
|
||||||
|
auto eventService = ServiceLocator::instance().getService<IEventService>();
|
||||||
|
if (eventService) {
|
||||||
|
eventService->addListener(EventType::KeyPressed, [](Event &e) {
|
||||||
|
auto &keyEvent = std::get<KeyEvent>(e.data);
|
||||||
|
if (keyEvent.keyCode == static_cast<int>(Key::Escape)) {
|
||||||
|
e.handled = true;
|
||||||
|
Application::get().quit();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
app.use<RenderModule>([](auto& cfg) { cfg.priority = 10; });
|
// 获取渲染器
|
||||||
|
RenderBackend *renderer = app.renderer();
|
||||||
app.use<InputModule>([](auto& cfg) { cfg.priority = 20; });
|
|
||||||
|
|
||||||
std::cout << "Initializing application..." << std::endl;
|
|
||||||
if (!app.init()) {
|
|
||||||
std::cerr << "Failed to initialize application!" << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Application initialized successfully!" << std::endl;
|
|
||||||
|
|
||||||
auto* win = app.window();
|
|
||||||
if (win) {
|
|
||||||
std::cout << "Window: " << win->width() << "x" << win->height() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置事件监听
|
|
||||||
auto eventService = ServiceLocator::instance().getService<IEventService>();
|
|
||||||
if (eventService) {
|
|
||||||
eventService->addListener(EventType::KeyPressed, [](Event& e) {
|
|
||||||
auto& keyEvent = std::get<KeyEvent>(e.data);
|
|
||||||
if (keyEvent.keyCode == static_cast<int>(Key::Escape)) {
|
|
||||||
e.handled = true;
|
|
||||||
Application::get().quit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取渲染器
|
|
||||||
RenderBackend* renderer = app.renderer();
|
|
||||||
|
|
||||||
// 创建并配置场景
|
// 创建并配置场景
|
||||||
auto scene = makeShared<ImageDisplayScene>();
|
auto scene = makeShared<ImageDisplayScene>();
|
||||||
scene->setRenderer(renderer);
|
scene->setRenderer(renderer);
|
||||||
scene->setBackgroundColor(Color(0.1f, 0.1f, 0.15f, 1.0f));
|
scene->setBackgroundColor(Color(0.1f, 0.1f, 0.15f, 1.0f));
|
||||||
|
|
||||||
if (win) {
|
if (win) {
|
||||||
scene->setViewportSize(static_cast<float>(win->width()),
|
scene->setViewportSize(static_cast<float>(win->width()),
|
||||||
static_cast<float>(win->height()));
|
static_cast<float>(win->height()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 配置相机
|
// 配置相机
|
||||||
auto cameraService = ServiceLocator::instance().getService<ICameraService>();
|
auto cameraService = ServiceLocator::instance().getService<ICameraService>();
|
||||||
if (cameraService && win) {
|
if (cameraService && win) {
|
||||||
ViewportConfig vpConfig;
|
ViewportConfig vpConfig;
|
||||||
vpConfig.logicWidth = static_cast<float>(win->width());
|
vpConfig.logicWidth = static_cast<float>(win->width());
|
||||||
vpConfig.logicHeight = static_cast<float>(win->height());
|
vpConfig.logicHeight = static_cast<float>(win->height());
|
||||||
vpConfig.mode = ViewportMode::AspectRatio;
|
vpConfig.mode = ViewportMode::AspectRatio;
|
||||||
cameraService->setViewportConfig(vpConfig);
|
cameraService->setViewportConfig(vpConfig);
|
||||||
cameraService->updateViewport(win->width(), win->height());
|
cameraService->updateViewport(win->width(), win->height());
|
||||||
cameraService->applyViewportAdapter();
|
cameraService->applyViewportAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.enterScene(scene);
|
app.enterScene(scene);
|
||||||
|
|
||||||
std::cout << "\nControls:" << std::endl;
|
std::cout << "\nControls:" << std::endl;
|
||||||
std::cout << " ESC - Exit" << std::endl;
|
std::cout << " ESC - Exit" << std::endl;
|
||||||
std::cout << "\nRunning main loop...\n" << std::endl;
|
std::cout << "\nRunning main loop...\n" << std::endl;
|
||||||
|
|
||||||
app.run();
|
app.run();
|
||||||
|
|
||||||
std::cout << "Shutting down..." << std::endl;
|
std::cout << "Shutting down..." << std::endl;
|
||||||
app.shutdown();
|
app.shutdown();
|
||||||
|
|
||||||
std::cout << "Goodbye!" << std::endl;
|
std::cout << "Goodbye!" << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue