[oden] It doesn't crash but it doesn't work either
This commit is contained in:
parent
71aa3c39f7
commit
8914b1795f
2 changed files with 162 additions and 50 deletions
90
src/lib.rs
90
src/lib.rs
|
|
@ -411,6 +411,7 @@ struct State {
|
|||
|
||||
text_bind_group_layout: wgpu::BindGroupLayout,
|
||||
fonts: HashMap<u32, text::FontCache>,
|
||||
font_textures: HashMap<u32, wgpu::BindGroup>,
|
||||
|
||||
write_textures: HashMap<u32, texture::Texture>,
|
||||
|
||||
|
|
@ -693,6 +694,30 @@ impl State {
|
|||
multiview: None,
|
||||
});
|
||||
|
||||
let inconsolata = include_bytes!("./Inconsolata-Regular.ttf") as &[u8];
|
||||
let inconsolata = text::FontCache::new(&device, inconsolata, 16.0);
|
||||
|
||||
let mut font_textures = HashMap::new();
|
||||
{
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &text_bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(&inconsolata.view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(&inconsolata.sampler),
|
||||
},
|
||||
],
|
||||
label: Some("inconsolata"),
|
||||
});
|
||||
font_textures.insert(0, bind_group);
|
||||
}
|
||||
let mut fonts = HashMap::new();
|
||||
fonts.insert(0, inconsolata);
|
||||
|
||||
let sprite_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Sprite Vertex Buffer"),
|
||||
contents: bytemuck::cast_slice(SPRITE_VERTICES),
|
||||
|
|
@ -721,7 +746,8 @@ impl State {
|
|||
text_instance_buffers: VertexBufferPool::new(),
|
||||
|
||||
text_bind_group_layout,
|
||||
fonts: HashMap::new(),
|
||||
fonts,
|
||||
font_textures,
|
||||
|
||||
write_textures: HashMap::new(),
|
||||
screen_uniform,
|
||||
|
|
@ -852,6 +878,7 @@ impl State {
|
|||
enum DrawMode {
|
||||
Sprites,
|
||||
Circles,
|
||||
Text,
|
||||
}
|
||||
|
||||
trait DrawModeInstance: Sized {
|
||||
|
|
@ -885,6 +912,17 @@ impl DrawModeInstance for CircleInstance {
|
|||
}
|
||||
}
|
||||
|
||||
impl DrawModeInstance for GlyphInstance {
|
||||
const MODE: DrawMode = DrawMode::Text;
|
||||
|
||||
fn get_vertex_buffer<'a>(
|
||||
state: &'a mut State,
|
||||
vb: &VertexBufferHandle,
|
||||
) -> &'a mut VertexBuffer<Self> {
|
||||
state.text_instance_buffers.get_mut(&vb)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DrawCall {
|
||||
mode: DrawMode,
|
||||
|
|
@ -947,6 +985,17 @@ impl DrawCall {
|
|||
.buffer
|
||||
}
|
||||
|
||||
DrawMode::Text => {
|
||||
match self.texture_id {
|
||||
Some(id) => {
|
||||
let bind_group = state.font_textures.get(&id).unwrap();
|
||||
pass.set_bind_group(1, bind_group, &[]);
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
&state.text_instance_buffers.get(&self.vertex_buffer).buffer
|
||||
}
|
||||
|
||||
DrawMode::Circles => {
|
||||
&state
|
||||
.circle_instance_buffers
|
||||
|
|
@ -1060,7 +1109,7 @@ impl<'a> FrameBuilder<'a> {
|
|||
GraphicsCommand::StrokeColor(c) => {
|
||||
self.stroke_color = c;
|
||||
}
|
||||
GraphicsCommand::Print(pc) => println!("{}", pc.text),
|
||||
GraphicsCommand::Print(pc) => self.push_text(pc),
|
||||
GraphicsCommand::Sprite(si) => self.push_sprite(si),
|
||||
GraphicsCommand::Circle(cc) => self.push_circle(cc),
|
||||
GraphicsCommand::UseTexture(id) => self.use_texture(id),
|
||||
|
|
@ -1140,6 +1189,11 @@ impl<'a> FrameBuilder<'a> {
|
|||
.state
|
||||
.circle_instance_buffers
|
||||
.get_a_buffer(&self.state.device),
|
||||
|
||||
DrawMode::Text => self
|
||||
.state
|
||||
.text_instance_buffers
|
||||
.get_a_buffer(&self.state.device),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1186,6 +1240,37 @@ impl<'a> FrameBuilder<'a> {
|
|||
});
|
||||
}
|
||||
|
||||
fn push_text(&mut self, pc: script::graphics::PrintCommand) {
|
||||
println!("{}", pc.text);
|
||||
let mut cursor_x = 0.0;
|
||||
let cursor_y = 0.0;
|
||||
|
||||
self.mode = DrawMode::Text; // ?
|
||||
self.use_texture(0); // ?
|
||||
|
||||
let font_id: u32 = 0;
|
||||
let font = self.state.fonts.get_mut(&font_id).unwrap();
|
||||
let mut glyphs = vec![];
|
||||
for char in pc.text.chars() {
|
||||
let color = self.fill_color.clone();
|
||||
let glyph = font.get_char(&self.state.queue, char);
|
||||
glyphs.push(GlyphInstance {
|
||||
src_top_left: [glyph.x, glyph.y],
|
||||
src_dims: [glyph.w, glyph.h],
|
||||
dest_top_left: [cursor_x, cursor_y],
|
||||
dest_dims: [glyph.w, glyph.h],
|
||||
color,
|
||||
});
|
||||
cursor_x += glyph.w;
|
||||
}
|
||||
|
||||
for glyph in glyphs {
|
||||
// TODO: Get new instance buffer when we don't have space for
|
||||
// this glyph instead of doing this over and over.
|
||||
self.get_instance_buffer().vec.push(glyph);
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) {
|
||||
if self.draw_calls.len() > 0 {
|
||||
let mut pass = self.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
|
|
@ -1220,6 +1305,7 @@ impl<'a> FrameBuilder<'a> {
|
|||
match call.mode {
|
||||
DrawMode::Sprites => pass.set_pipeline(&self.state.sprite_pipeline),
|
||||
DrawMode::Circles => pass.set_pipeline(&self.state.circle_pipeline),
|
||||
DrawMode::Text => pass.set_pipeline(&self.state.text_pipeline),
|
||||
}
|
||||
last_mode = Some(call.mode);
|
||||
}
|
||||
|
|
|
|||
122
src/text.rs
122
src/text.rs
|
|
@ -45,12 +45,9 @@ enum CellCacheKey {
|
|||
pub struct FontCache {
|
||||
font: Font,
|
||||
size: f32,
|
||||
atlas_width: u16,
|
||||
atlas_height: u16,
|
||||
char_width: u16,
|
||||
char_height: u16,
|
||||
texture: wgpu::Texture,
|
||||
view: wgpu::TextureView,
|
||||
pub view: wgpu::TextureView,
|
||||
pub sampler: wgpu::Sampler,
|
||||
|
||||
cells: Vec<GlyphCell>,
|
||||
cell_cache: LruCache<CellCacheKey, usize>,
|
||||
|
|
@ -58,7 +55,6 @@ pub struct FontCache {
|
|||
|
||||
enum SlotState {
|
||||
Empty,
|
||||
Allocated(u16, fontdue::Metrics),
|
||||
Rendered(u16, fontdue::Metrics),
|
||||
}
|
||||
|
||||
|
|
@ -70,8 +66,15 @@ struct GlyphCell {
|
|||
state: SlotState,
|
||||
}
|
||||
|
||||
pub struct Glyph {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub w: f32,
|
||||
pub h: f32,
|
||||
}
|
||||
|
||||
impl FontCache {
|
||||
fn new(device: &wgpu::Device, bytes: &[u8], size: f32) -> Self {
|
||||
pub fn new(device: &wgpu::Device, bytes: &[u8], size: f32) -> Self {
|
||||
let font = fontdue::Font::from_bytes(bytes, fontdue::FontSettings::default())
|
||||
.expect("Could not parse font");
|
||||
|
||||
|
|
@ -90,10 +93,21 @@ impl FontCache {
|
|||
sample_count: 4,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::R8Unorm,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING
|
||||
| wgpu::TextureUsages::COPY_DST
|
||||
| wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
view_formats: &[],
|
||||
});
|
||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
mag_filter: wgpu::FilterMode::Nearest,
|
||||
min_filter: wgpu::FilterMode::Nearest,
|
||||
mipmap_filter: wgpu::FilterMode::Nearest,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// Measure the font to figure out the size of a cell in the cache.
|
||||
// NOTE: This metric nonsense is bad, probably.
|
||||
|
|
@ -133,59 +147,71 @@ impl FontCache {
|
|||
);
|
||||
}
|
||||
|
||||
// Set up the binding group and pipeline and whatnot.
|
||||
|
||||
FontCache {
|
||||
font,
|
||||
size,
|
||||
atlas_width,
|
||||
atlas_height,
|
||||
char_width: char_width as u16,
|
||||
char_height: char_height as u16,
|
||||
texture,
|
||||
view,
|
||||
sampler,
|
||||
cells,
|
||||
cell_cache,
|
||||
}
|
||||
}
|
||||
|
||||
fn rasterize_character(&mut self, queue: &wgpu::Queue, index: u16, slot: &GlyphCell) {
|
||||
let (metrics, bitmap) = self.font.rasterize_indexed(index, self.size);
|
||||
|
||||
let mut texture = self.texture.as_image_copy();
|
||||
texture.origin.x = slot.x.into();
|
||||
texture.origin.y = slot.y.into();
|
||||
|
||||
queue.write_texture(
|
||||
texture,
|
||||
&bitmap,
|
||||
wgpu::ImageDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: Some(metrics.width as u32),
|
||||
rows_per_image: None,
|
||||
},
|
||||
wgpu::Extent3d {
|
||||
width: metrics.width as u32,
|
||||
height: metrics.height as u32,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn get_glyph_slot(&mut self, index: u16) -> &mut GlyphCell {
|
||||
pub fn get_char(&mut self, queue: &wgpu::Queue, c: char) -> Glyph {
|
||||
let index = self.font.lookup_glyph_index(c);
|
||||
let key = CellCacheKey::GlyphIndex(index);
|
||||
if let Some(cell_index) = self.cell_cache.get(&key) {
|
||||
return &mut self.cells[*cell_index];
|
||||
}
|
||||
let cell = match self.cell_cache.get(&key) {
|
||||
Some(cell_index) => &mut self.cells[*cell_index],
|
||||
None => {
|
||||
let (_, cell_index) = self
|
||||
.cell_cache
|
||||
.pop_lru()
|
||||
.expect("did not put all available things in the LRU cache");
|
||||
self.cell_cache.put(key, cell_index);
|
||||
let cell = &mut self.cells[cell_index];
|
||||
cell.state = SlotState::Empty; // This isn't what it used to be.
|
||||
cell
|
||||
}
|
||||
};
|
||||
|
||||
if let Some((_, cell_index)) = self.cell_cache.pop_lru() {
|
||||
self.cell_cache.put(key, cell_index);
|
||||
let cell = &mut self.cells[cell_index];
|
||||
cell.state = SlotState::Empty;
|
||||
return cell;
|
||||
}
|
||||
// I mean, technically if we got an LRU hit here it's rendered, but
|
||||
// convincing the compiler of that is a pain.
|
||||
let metrics = match cell.state {
|
||||
SlotState::Rendered(_, metrics) => metrics,
|
||||
SlotState::Empty => {
|
||||
let (metrics, bitmap) = self.font.rasterize_indexed(index, self.size);
|
||||
|
||||
panic!("Did not put enough things in the LRU cache, why is it empty here?");
|
||||
let mut texture = self.texture.as_image_copy();
|
||||
texture.origin.x = cell.x.into();
|
||||
texture.origin.y = cell.y.into();
|
||||
|
||||
queue.write_texture(
|
||||
texture,
|
||||
&bitmap,
|
||||
wgpu::ImageDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: Some(metrics.width as u32),
|
||||
rows_per_image: None,
|
||||
},
|
||||
wgpu::Extent3d {
|
||||
width: metrics.width as u32,
|
||||
height: metrics.height as u32,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
);
|
||||
|
||||
cell.state = SlotState::Rendered(index, metrics.clone());
|
||||
metrics
|
||||
}
|
||||
};
|
||||
|
||||
Glyph {
|
||||
x: cell.x as f32 + metrics.bounds.xmin,
|
||||
y: cell.y as f32 + metrics.bounds.ymin,
|
||||
w: metrics.bounds.width,
|
||||
h: metrics.bounds.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue