Rust文本渲染库glyph_brush的使用,高效处理2D文本和字形布局的Rust插件库
Rust文本渲染库glyph_brush的使用,高效处理2D文本和字形布局的Rust插件库
glyph_brush是一个使用ab_glyph的快速缓存文本渲染库。它提供了与渲染API无关的栅格化和绘制缓存逻辑。
该库广泛使用缓存来优化帧性能:
- GPU纹理缓存逻辑,动态维护已渲染字形的GPU纹理
- 缓存字形布局输出,避免在连续帧上重复渲染相同文本的成本
- 重用布局以优化变更后相似布局的计算
- 每个部分的顶点生成都被缓存,并在变更时重新组装成总顶点数组
- 当相同文本在连续帧上渲染时,避免任何布局或顶点计算
该库设计为可以轻松包装,以创建特定渲染API的便捷版本,例如gfx-glyph。
基本用法示例
use glyph_brush::{ab_glyph::FontArc, BrushAction, BrushError, GlyphBrushBuilder, Section, Text};
let dejavu = FontArc::try_from_slice(include_bytes!("../../fonts/DejaVuSans.ttf"))?;
let mut glyph_brush = GlyphBrushBuilder::using_font(dejavu).build();
glyph_brush.queue(Section::default().add_text(Text::new("Hello glyph_brush")));
glyph_brush.queue(some_other_section);
match glyph_brush.process_queued(
|rect, tex_data| update_texture(rect, tex_data),
|vertex_data| into_vertex(vertex_data),
) {
Ok(BrushAction::Draw(vertices)) => {
// 绘制新顶点
}
Ok(BrushAction::ReDraw) => {
// 重新绘制上一帧未修改的顶点
}
Err(BrushError::TextureTooSmall { suggested }) => {
// 扩大纹理+glyph_brush纹理缓存并重试
}
}
完整示例DEMO
以下是一个更完整的示例,展示了如何使用glyph_brush库与wgpu渲染器结合使用:
use glyph_brush::{
ab_glyph::{FontArc, PxScale},
GlyphBrushBuilder, Section, Text
};
use wgpu::{util::DeviceExt, *};
struct TextRenderer {
glyph_brush: GlyphBrush<()>,
texture: Texture,
vertex_buffer: Option<Buffer>,
texture_size: u32,
device: Device,
queue: Queue,
}
impl TextRenderer {
pub async fn new() -> Self {
// 1. 加载字体
let font_data = include_bytes!("DejaVuSans.ttf");
let font = FontArc::try_from_slice(font_data).unwrap();
// 2. 创建WGPU实例
let instance = Instance::new(Backends::PRIMARY);
let adapter = instance.request_adapter(&RequestAdapterOptions::default())
.await
.unwrap();
let (device, queue) = adapter.request_device(&DeviceDescriptor::default(), None)
.await
.unwrap();
// 3. 初始化glyph_brush
let glyph_brush = GlyphBrushBuilder::using_font(font).build();
// 4. 创建初始纹理
let texture_size = 1024;
let texture = device.create_texture(&TextureDescriptor {
size: Extent3d {
width: texture_size,
height: texture_size,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::R8Unorm,
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
label: Some("glyph_brush_texture"),
});
Self {
glyph_brush,
texture,
vertex_buffer: None,
texture_size,
device,
queue,
}
}
pub fn draw_text(&mut self, text: &str, position: (f32, f32), color: [f32; 4], scale: f32) {
self.glyph_brush.queue(Section {
screen_position: position,
bounds: (f32::INFINITY, f32::INFINITY),
text: vec![
Text::new(text)
.with_scale(PxScale::from(scale))
.with_color(color),
],
..Section::default()
});
}
pub fn render(&mut self) {
let result = self.glyph_brush.process_queued(
|rect, data| {
// 更新纹理数据
self.queue.write_texture(
ImageCopyTexture {
texture: &self.texture,
mip_level: 0,
origin: Origin3d {
x: rect.min[0],
y: rect.min[1],
z: 0,
},
aspect: TextureAspect::All,
},
data,
ImageDataLayout {
offset: 0,
bytes_per_row: Some(rect.width()),
rows_per_image: Some(rect.height()),
},
Extent3d {
width: rect.width(),
height: rect.height(),
depth_or_array_layers: 1,
},
);
},
|vertices| {
// 创建顶点缓冲区
self.vertex_buffer = Some(
self.device.create_buffer_init(&util::BufferInitDescriptor {
label: Some("Text Vertex Buffer"),
contents: bytemuck::cast_slice(vertices),
usage: BufferUsages::VERTEX,
})
);
},
);
if let Err(BrushError::TextureTooSmall { suggested }) = result {
// 纹理太小,需要调整大小
self.texture_size = suggested.width.max(suggested.height);
self.texture = self.device.create_texture(&TextureDescriptor {
size: Extent3d {
width: self.texture_size,
height: self.texture_size,
depth_or_array_layers: 1,
},
..TextureDescriptor::default()
});
// 重试渲染
self.render();
}
}
}
示例运行
可以通过运行以下命令查看opengl示例:
cargo run --example opengl --release
这个库提供了高效的2D文本渲染和字形布局功能,特别适合需要频繁更新文本内容的图形应用程序。
1 回复
Rust文本渲染库glyph_brush的使用指南
简介
glyph_brush
是一个高效的2D文本和字形布局处理库,专为Rust设计。它建立在glyph_brush_layout
和底层图形API(如gfx、wgpu等)之上,提供了高性能的文本渲染解决方案。
主要特点:
- 高效的文本缓存和批处理
- 自动布局和重排
- 支持多行文本
- 自定义字体支持
- 与多种图形后端兼容
安装
在Cargo.toml中添加依赖:
[dependencies]
glyph_brush = "0.7"
glyph_brush_layout = "0.2"
基本使用方法
1. 初始化
use glyph_brush::{GlyphBrush, GlyphBrushBuilder};
use glyph_brush_layout::{FontId, Section, Text};
// 加载字体
let font_data = include_bytes!("path/to/font.ttf");
let font = glyph_brush::ab_glyph::FontRef::try_from_slice(font_data).unwrap();
// 创建GlyphBrush
let mut glyph_brush = GlyphBrushBuilder::using_font(font).build();
2. 渲染文本
// 定义要渲染的文本部分
let section = Section {
screen_position: (30.0, 30.0),
bounds: (300.0, 200.0),
text: vec![
Text::new("Hello, glyph_brush!")
.with_color([1.0, 1.0, 1.0, 1.0])
.with_scale(40.0),
],
..Section::default()
};
// 队列文本
glyph_brush.queue(section);
// 绘制(需要提供图形后端的上下文)
// glyph_brush.draw_queued(...);
3. 多字体支持
// 加载多个字体
let font1 = ...;
let font2 = ...;
let mut glyph_brush = GlyphBrushBuilder::using_fonts(vec![font1, font2]).build();
// 使用不同字体
let section = Section {
text: vec![
Text::new("Font 1").with_font_id(FontId(0)),
Text::new(" and Font 2").with_font_id(FontId(1)),
],
..Section::default()
};
高级用法
自定义布局
use glyph_brush_layout::{HorizontalAlign, VerticalAlign, Layout};
let section = Section {
layout: Layout::Wrap {
line_breaker: Default::default(),
h_align: HorizontalAlign::Center,
v_align: VerticalAlign::Center,
},
..section
};
性能优化
对于频繁更新的文本,可以使用GlyphBrush::keep_cached
来保留缓存:
glyph_brush.keep_cached(|_| true); // 保留所有缓存
与wgpu集成示例
// 创建wgpu渲染器后
let mut glyph_brush = GlyphBrushBuilder::using_font(font)
.initial_cache_size((2048, 2048))
.build(wgpu_device, wgpu_swap_chain_format);
// 渲染循环中
glyph_brush.queue(section);
glyph_brush
.draw_queued(
wgpu_device,
&mut staging_belt,
&mut encoder,
wgpu_texture_view,
wgpu_screen_width,
wgpu_screen_height,
)
.unwrap();
完整示例代码
下面是一个完整的wgpu集成示例,展示如何使用glyph_brush渲染文本:
use wgpu::{Device, Queue, SurfaceConfiguration, CommandEncoder, TextureView};
use wgpu::util::DeviceExt;
use glyph_brush::{GlyphBrush, GlyphBrushBuilder};
use glyph_brush_layout::{FontId, Section, Text};
async fn run() {
// 初始化wgpu
let instance = wgpu::Instance::new(wgpu::Backends::all());
let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
compatible_surface: None,
}).await.unwrap();
let (device, queue) = adapter.request_device(
&wgpu::DeviceDescriptor {
features: wgpu::Features::empty(),
limits: wgpu::Limits::default(),
label: None,
},
None
).await.unwrap();
// 加载字体
let font_data = include_bytes!("path/to/font.ttf");
let font = glyph_brush::ab_glyph::FontRef::try_from_slice(font_data).unwrap();
// 创建glyph_brush实例
let mut glyph_brush = GlyphBrushBuilder::using_font(font)
.initial_cache_size((2048, 2048))
.build(&device, wgpu::TextureFormat::Bgra8UnormSrgb);
// 渲染循环
loop {
// 创建命令编码器
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"),
});
// 定义要渲染的文本
let section = Section {
screen_position: (100.0, 100.0),
bounds: (500.0, 300.0),
text: vec![
Text::new("Hello wgpu + glyph_brush!")
.with_color([1.0, 1.0, 1.0, 1.0])
.with_scale(40.0),
Text::new("\n高性能文本渲染")
.with_color([0.5, 0.8, 1.0, 1.0])
.with_scale(30.0),
],
..Section::default()
};
// 队列并绘制文本
glyph_brush.queue(section);
// 假设我们已经获取了texture_view
let texture_view = ...; // 实际应用中需要从surface获取
glyph_brush.draw_queued(
&device,
&mut staging_belt,
&mut encoder,
&texture_view,
800, // 屏幕宽度
600, // 屏幕高度
).unwrap();
// 提交命令缓冲区
queue.submit(std::iter::once(encoder.finish()));
}
}
注意事项
glyph_brush
需要与图形API配合使用,单独使用时只能处理布局- 频繁修改文本内容会影响性能,尽量重用Section对象
- 对于静态文本,考虑缓存渲染结果
- 大文本量时注意调整初始缓存大小
通过合理使用glyph_brush
,可以在Rust应用中实现高性能的2D文本渲染效果。