diff --git a/src/lib.rs b/src/lib.rs index bdf3b59c..4c5f94ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -255,6 +255,65 @@ const SPRITE_VERTICES: &[Vertex] = &[ }, ]; +struct VertexBufferPool { + buffers: Vec>, + next_free_buffer: usize, +} + +impl VertexBufferPool { + fn new() -> Self { + VertexBufferPool { + buffers: Vec::new(), + next_free_buffer: 0, + } + } + + fn clear(&mut self) { + self.next_free_buffer = 0; + } + + fn new_buffer(&mut self, device: &wgpu::Device) -> VertexBufferHandle { + if self.next_free_buffer >= self.buffers.len() { + let max_sprites: usize = 4096; + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Vertex Buffer"), + size: (max_sprites * std::mem::size_of::()).try_into().unwrap(), + mapped_at_creation: false, + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + }); + self.buffers.push(VertexBuffer { + buffer, + capacity: max_sprites, + vec: Vec::with_capacity(max_sprites), + }); + } + + let index = self.next_free_buffer; + self.next_free_buffer += 1; + let vb = &mut self.buffers[index]; + vb.vec.clear(); + VertexBufferHandle { + index, + capacity: vb.capacity, + } + } + + fn get(&self, handle: &VertexBufferHandle) -> &VertexBuffer { + &self.buffers[handle.index] + } + + fn get_mut(&mut self, handle: &VertexBufferHandle) -> &mut VertexBuffer { + &mut self.buffers[handle.index] + } + + fn copy_instance_buffers(&mut self, queue: &wgpu::Queue) { + for i in 0..self.next_free_buffer { + let vb = &self.buffers[i]; + queue.write_buffer(&vb.buffer, 0, bytemuck::cast_slice(&vb.vec)); + } + } +} + struct State { surface: wgpu::Surface, device: wgpu::Device, @@ -266,15 +325,13 @@ struct State { sprite_vertex_buffer: wgpu::Buffer, sprite_pipeline: wgpu::RenderPipeline, - sprite_instance_buffers: Vec>, - next_free_sprite_instance_buffer: usize, + sprite_instance_buffers: VertexBufferPool, sprite_bind_group_layout: wgpu::BindGroupLayout, sprite_textures: HashMap, circle_pipeline: wgpu::RenderPipeline, - circle_instance_buffers: Vec>, - next_free_circle_instance_buffer: usize, + circle_instance_buffers: VertexBufferPool, write_textures: HashMap, @@ -484,15 +541,13 @@ impl State { sprite_pipeline, sprite_vertex_buffer, - sprite_instance_buffers: Vec::new(), - next_free_sprite_instance_buffer: 0, + sprite_instance_buffers: VertexBufferPool::new(), sprite_bind_group_layout, sprite_textures: HashMap::new(), circle_pipeline, - circle_instance_buffers: Vec::new(), - next_free_circle_instance_buffer: 0, + circle_instance_buffers: VertexBufferPool::new(), write_textures: HashMap::new(), screen_uniform, @@ -529,8 +584,9 @@ impl State { fn render(&mut self, commands: Vec) -> Result<(), wgpu::SurfaceError> { let _span = span!("context render"); - // Reset vertex buffers. - self.next_free_sprite_instance_buffer = 0; + // Reset instance buffers. + self.sprite_instance_buffers.clear(); + self.circle_instance_buffers.clear(); let mut builder = FrameBuilder::new(self)?; for command in commands { @@ -610,59 +666,23 @@ impl State { self.write_textures.insert(id, texture); } - fn new_vertex_buffer(&mut self) -> VertexBufferHandle { - if self.next_free_sprite_instance_buffer >= self.sprite_instance_buffers.len() { - let max_sprites: usize = 4096; - let buffer = self.device.create_buffer(&wgpu::BufferDescriptor { - label: Some("Sprite Instance Buffer"), - size: (max_sprites * std::mem::size_of::()) - .try_into() - .unwrap(), - mapped_at_creation: false, - usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - }); - self.sprite_instance_buffers.push(VertexBuffer { - buffer, - capacity: max_sprites, - vec: Vec::with_capacity(max_sprites), - }); - } - - let index = self.next_free_sprite_instance_buffer; - self.next_free_sprite_instance_buffer += 1; - let vb = &mut self.sprite_instance_buffers[index]; - vb.vec.clear(); - VertexBufferHandle { - index, - capacity: vb.capacity, - } - } - - fn get_sprite_instance_buffer( - &self, - handle: &VertexBufferHandle, - ) -> &VertexBuffer { - &self.sprite_instance_buffers[handle.index] - } - - fn get_sprite_instance_buffer_mut( - &mut self, - handle: &VertexBufferHandle, - ) -> &mut VertexBuffer { - &mut self.sprite_instance_buffers[handle.index] - } - fn copy_instance_buffers(&mut self) { - for i in 0..self.next_free_sprite_instance_buffer { - let vb = &self.sprite_instance_buffers[i]; - self.queue - .write_buffer(&vb.buffer, 0, bytemuck::cast_slice(&vb.vec)); - } + self.sprite_instance_buffers + .copy_instance_buffers(&self.queue); + self.circle_instance_buffers + .copy_instance_buffers(&self.queue); } } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +enum DrawMode { + Sprites, + Circles, +} + #[derive(Debug)] struct DrawCall { + mode: DrawMode, texture_id: Option, vertex_buffer: VertexBufferHandle, draw_start: u32, @@ -670,8 +690,9 @@ struct DrawCall { } impl DrawCall { - pub fn new(vertex_buffer: VertexBufferHandle, draw_start: u32) -> Self { + pub fn new(mode: DrawMode, vertex_buffer: VertexBufferHandle, draw_start: u32) -> Self { DrawCall { + mode, texture_id: None, vertex_buffer, draw_start, @@ -680,7 +701,7 @@ impl DrawCall { } pub fn new_at_buffer_tail(&self) -> Self { - DrawCall::new(self.vertex_buffer.clone(), self.draw_end) + DrawCall::new(self.mode, self.vertex_buffer.clone(), self.draw_end) } pub fn switch_textures(&self, id: u32) -> DrawCall { @@ -713,10 +734,23 @@ impl DrawCall { None => (), }; - let vb = state.get_sprite_instance_buffer(&self.vertex_buffer); + let vb = match self.mode { + DrawMode::Sprites => { + &state + .sprite_instance_buffers + .get(&self.vertex_buffer) + .buffer + } + DrawMode::Circles => { + &state + .circle_instance_buffers + .get(&self.vertex_buffer) + .buffer + } + }; pass.set_bind_group(1, &state.screen_uniform_bind_group, &[]); pass.set_vertex_buffer(0, state.sprite_vertex_buffer.slice(..)); - pass.set_vertex_buffer(1, vb.buffer.slice(..)); + pass.set_vertex_buffer(1, vb.slice(..)); pass.draw( 0..SPRITE_VERTICES.len() as u32, @@ -733,6 +767,7 @@ struct FrameBuilder<'a> { encoder: wgpu::CommandEncoder, output: wgpu::SurfaceTexture, + mode: DrawMode, target: Rc, color: Option<[f64; 4]>, draw_calls: Vec, @@ -762,6 +797,7 @@ impl<'a> FrameBuilder<'a> { encoder, output, + mode: DrawMode::Sprites, target: last_view, color: None, draw_calls: Vec::new(), @@ -836,6 +872,19 @@ impl<'a> FrameBuilder<'a> { self.target = target; } + fn new_instance_buffer(&mut self) -> VertexBufferHandle { + match self.mode { + DrawMode::Sprites => self + .state + .sprite_instance_buffers + .new_buffer(&self.state.device), + DrawMode::Circles => self + .state + .circle_instance_buffers + .new_buffer(&self.state.device), + } + } + fn use_texture(&mut self, texture_id: u32) { match self.draw_calls.last_mut() { Some(call) => match call.texture_id { @@ -855,26 +904,35 @@ impl<'a> FrameBuilder<'a> { } }, None => { - let mut call = DrawCall::new(self.state.new_vertex_buffer(), 0); + let mut call = DrawCall::new(self.mode, self.new_instance_buffer(), 0); call.texture_id = Some(texture_id); self.draw_calls.push(call); } } } + fn switch_mode(&mut self, mode: DrawMode) { + if self.mode != mode { + self.flush(); + self.draw_calls.clear(); + self.mode = mode; + } + } + fn get_sprite_instance_buffer(&mut self) -> &mut VertexBuffer { + self.switch_mode(DrawMode::Sprites); match self.draw_calls.last_mut() { Some(call) => match call.allocate_capacity(1) { - Some(vb) => return self.state.get_sprite_instance_buffer_mut(&vb), + Some(vb) => return self.state.sprite_instance_buffers.get_mut(&vb), None => {} }, None => {} }; - let mut call = DrawCall::new(self.state.new_vertex_buffer(), 0); + let mut call = DrawCall::new(self.mode, self.new_instance_buffer(), 0); let vb = call.allocate_capacity(1).unwrap(); self.draw_calls.push(call); - self.state.get_sprite_instance_buffer_mut(&vb) + self.state.sprite_instance_buffers.get_mut(&vb) } fn push_sprite(&mut self, sc: script::graphics::SpriteCommand) { @@ -887,10 +945,26 @@ impl<'a> FrameBuilder<'a> { }); } + fn get_circle_instance_buffer(&mut self) -> &mut VertexBuffer { + self.switch_mode(DrawMode::Circles); + match self.draw_calls.last_mut() { + Some(call) => match call.allocate_capacity(1) { + Some(vb) => return self.state.circle_instance_buffers.get_mut(&vb), + None => {} + }, + None => {} + }; + + let mut call = DrawCall::new(self.mode, self.new_instance_buffer(), 0); + let vb = call.allocate_capacity(1).unwrap(); + self.draw_calls.push(call); + self.state.circle_instance_buffers.get_mut(&vb) + } + fn flush(&mut self) { let first_call = match self.draw_calls.last() { Some(call) => call.new_at_buffer_tail(), - None => DrawCall::new(self.state.new_vertex_buffer(), 0), + None => DrawCall::new(self.mode, self.new_instance_buffer(), 0), }; if self.draw_calls.len() > 0 { @@ -916,7 +990,11 @@ impl<'a> FrameBuilder<'a> { depth_stencil_attachment: None, }); - pass.set_pipeline(&self.state.sprite_pipeline); + match self.mode { + DrawMode::Sprites => pass.set_pipeline(&self.state.sprite_pipeline), + DrawMode::Circles => pass.set_pipeline(&self.state.circle_pipeline), + } + for call in &self.draw_calls { call.draw(&self.state, &mut pass); }