[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,
|
text_bind_group_layout: wgpu::BindGroupLayout,
|
||||||
fonts: HashMap<u32, text::FontCache>,
|
fonts: HashMap<u32, text::FontCache>,
|
||||||
|
font_textures: HashMap<u32, wgpu::BindGroup>,
|
||||||
|
|
||||||
write_textures: HashMap<u32, texture::Texture>,
|
write_textures: HashMap<u32, texture::Texture>,
|
||||||
|
|
||||||
|
|
@ -693,6 +694,30 @@ impl State {
|
||||||
multiview: None,
|
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 {
|
let sprite_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some("Sprite Vertex Buffer"),
|
label: Some("Sprite Vertex Buffer"),
|
||||||
contents: bytemuck::cast_slice(SPRITE_VERTICES),
|
contents: bytemuck::cast_slice(SPRITE_VERTICES),
|
||||||
|
|
@ -721,7 +746,8 @@ impl State {
|
||||||
text_instance_buffers: VertexBufferPool::new(),
|
text_instance_buffers: VertexBufferPool::new(),
|
||||||
|
|
||||||
text_bind_group_layout,
|
text_bind_group_layout,
|
||||||
fonts: HashMap::new(),
|
fonts,
|
||||||
|
font_textures,
|
||||||
|
|
||||||
write_textures: HashMap::new(),
|
write_textures: HashMap::new(),
|
||||||
screen_uniform,
|
screen_uniform,
|
||||||
|
|
@ -852,6 +878,7 @@ impl State {
|
||||||
enum DrawMode {
|
enum DrawMode {
|
||||||
Sprites,
|
Sprites,
|
||||||
Circles,
|
Circles,
|
||||||
|
Text,
|
||||||
}
|
}
|
||||||
|
|
||||||
trait DrawModeInstance: Sized {
|
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)]
|
#[derive(Debug)]
|
||||||
struct DrawCall {
|
struct DrawCall {
|
||||||
mode: DrawMode,
|
mode: DrawMode,
|
||||||
|
|
@ -947,6 +985,17 @@ impl DrawCall {
|
||||||
.buffer
|
.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 => {
|
DrawMode::Circles => {
|
||||||
&state
|
&state
|
||||||
.circle_instance_buffers
|
.circle_instance_buffers
|
||||||
|
|
@ -1060,7 +1109,7 @@ impl<'a> FrameBuilder<'a> {
|
||||||
GraphicsCommand::StrokeColor(c) => {
|
GraphicsCommand::StrokeColor(c) => {
|
||||||
self.stroke_color = 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::Sprite(si) => self.push_sprite(si),
|
||||||
GraphicsCommand::Circle(cc) => self.push_circle(cc),
|
GraphicsCommand::Circle(cc) => self.push_circle(cc),
|
||||||
GraphicsCommand::UseTexture(id) => self.use_texture(id),
|
GraphicsCommand::UseTexture(id) => self.use_texture(id),
|
||||||
|
|
@ -1140,6 +1189,11 @@ impl<'a> FrameBuilder<'a> {
|
||||||
.state
|
.state
|
||||||
.circle_instance_buffers
|
.circle_instance_buffers
|
||||||
.get_a_buffer(&self.state.device),
|
.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) {
|
fn flush(&mut self) {
|
||||||
if self.draw_calls.len() > 0 {
|
if self.draw_calls.len() > 0 {
|
||||||
let mut pass = self.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
let mut pass = self.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
|
@ -1220,6 +1305,7 @@ impl<'a> FrameBuilder<'a> {
|
||||||
match call.mode {
|
match call.mode {
|
||||||
DrawMode::Sprites => pass.set_pipeline(&self.state.sprite_pipeline),
|
DrawMode::Sprites => pass.set_pipeline(&self.state.sprite_pipeline),
|
||||||
DrawMode::Circles => pass.set_pipeline(&self.state.circle_pipeline),
|
DrawMode::Circles => pass.set_pipeline(&self.state.circle_pipeline),
|
||||||
|
DrawMode::Text => pass.set_pipeline(&self.state.text_pipeline),
|
||||||
}
|
}
|
||||||
last_mode = Some(call.mode);
|
last_mode = Some(call.mode);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
84
src/text.rs
84
src/text.rs
|
|
@ -45,12 +45,9 @@ enum CellCacheKey {
|
||||||
pub struct FontCache {
|
pub struct FontCache {
|
||||||
font: Font,
|
font: Font,
|
||||||
size: f32,
|
size: f32,
|
||||||
atlas_width: u16,
|
|
||||||
atlas_height: u16,
|
|
||||||
char_width: u16,
|
|
||||||
char_height: u16,
|
|
||||||
texture: wgpu::Texture,
|
texture: wgpu::Texture,
|
||||||
view: wgpu::TextureView,
|
pub view: wgpu::TextureView,
|
||||||
|
pub sampler: wgpu::Sampler,
|
||||||
|
|
||||||
cells: Vec<GlyphCell>,
|
cells: Vec<GlyphCell>,
|
||||||
cell_cache: LruCache<CellCacheKey, usize>,
|
cell_cache: LruCache<CellCacheKey, usize>,
|
||||||
|
|
@ -58,7 +55,6 @@ pub struct FontCache {
|
||||||
|
|
||||||
enum SlotState {
|
enum SlotState {
|
||||||
Empty,
|
Empty,
|
||||||
Allocated(u16, fontdue::Metrics),
|
|
||||||
Rendered(u16, fontdue::Metrics),
|
Rendered(u16, fontdue::Metrics),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,8 +66,15 @@ struct GlyphCell {
|
||||||
state: SlotState,
|
state: SlotState,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Glyph {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub w: f32,
|
||||||
|
pub h: f32,
|
||||||
|
}
|
||||||
|
|
||||||
impl FontCache {
|
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())
|
let font = fontdue::Font::from_bytes(bytes, fontdue::FontSettings::default())
|
||||||
.expect("Could not parse font");
|
.expect("Could not parse font");
|
||||||
|
|
||||||
|
|
@ -90,10 +93,21 @@ impl FontCache {
|
||||||
sample_count: 4,
|
sample_count: 4,
|
||||||
dimension: wgpu::TextureDimension::D2,
|
dimension: wgpu::TextureDimension::D2,
|
||||||
format: wgpu::TextureFormat::R8Unorm,
|
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: &[],
|
view_formats: &[],
|
||||||
});
|
});
|
||||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
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.
|
// Measure the font to figure out the size of a cell in the cache.
|
||||||
// NOTE: This metric nonsense is bad, probably.
|
// NOTE: This metric nonsense is bad, probably.
|
||||||
|
|
@ -133,28 +147,44 @@ impl FontCache {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the binding group and pipeline and whatnot.
|
|
||||||
|
|
||||||
FontCache {
|
FontCache {
|
||||||
font,
|
font,
|
||||||
size,
|
size,
|
||||||
atlas_width,
|
|
||||||
atlas_height,
|
|
||||||
char_width: char_width as u16,
|
|
||||||
char_height: char_height as u16,
|
|
||||||
texture,
|
texture,
|
||||||
view,
|
view,
|
||||||
|
sampler,
|
||||||
cells,
|
cells,
|
||||||
cell_cache,
|
cell_cache,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rasterize_character(&mut self, queue: &wgpu::Queue, index: u16, slot: &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);
|
||||||
|
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
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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);
|
let (metrics, bitmap) = self.font.rasterize_indexed(index, self.size);
|
||||||
|
|
||||||
let mut texture = self.texture.as_image_copy();
|
let mut texture = self.texture.as_image_copy();
|
||||||
texture.origin.x = slot.x.into();
|
texture.origin.x = cell.x.into();
|
||||||
texture.origin.y = slot.y.into();
|
texture.origin.y = cell.y.into();
|
||||||
|
|
||||||
queue.write_texture(
|
queue.write_texture(
|
||||||
texture,
|
texture,
|
||||||
|
|
@ -170,22 +200,18 @@ impl FontCache {
|
||||||
depth_or_array_layers: 1,
|
depth_or_array_layers: 1,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
fn get_glyph_slot(&mut self, index: u16) -> &mut GlyphCell {
|
cell.state = SlotState::Rendered(index, metrics.clone());
|
||||||
let key = CellCacheKey::GlyphIndex(index);
|
metrics
|
||||||
if let Some(cell_index) = self.cell_cache.get(&key) {
|
|
||||||
return &mut self.cells[*cell_index];
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if let Some((_, cell_index)) = self.cell_cache.pop_lru() {
|
Glyph {
|
||||||
self.cell_cache.put(key, cell_index);
|
x: cell.x as f32 + metrics.bounds.xmin,
|
||||||
let cell = &mut self.cells[cell_index];
|
y: cell.y as f32 + metrics.bounds.ymin,
|
||||||
cell.state = SlotState::Empty;
|
w: metrics.bounds.width,
|
||||||
return cell;
|
h: metrics.bounds.height,
|
||||||
}
|
}
|
||||||
|
|
||||||
panic!("Did not put enough things in the LRU cache, why is it empty here?");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue