Optimized vertex and index buffers

* buffers are no-longer re-created at each frame
* re-use existing buffers when updating mesh
This commit is contained in:
Steins7 2022-08-18 23:38:32 +02:00
parent 330a93fac8
commit 88078e128a
6 changed files with 114 additions and 80 deletions

10
Cargo.lock generated
View File

@ -158,6 +158,8 @@ dependencies = [
"pollster",
"raw-window-handle",
"wgpu",
"wgpu-hal",
"wgpu-types",
"winit",
]
@ -1466,9 +1468,9 @@ dependencies = [
[[package]]
name = "wgpu-hal"
version = "0.13.1"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef50e48812c7eb958fa52d28a912f8b77c96453ebab21c72b01cdda61d3e65d"
checksum = "20cbdfc3d0637dba3d5536b93adef3d26023a0b96f0e1ee5ee9560a401d9f646"
dependencies = [
"android_system_properties",
"arrayvec",
@ -1505,9 +1507,9 @@ dependencies = [
[[package]]
name = "wgpu-types"
version = "0.13.0"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f48d691b733b9d50ea8cb18f377fd1ed927c90c55ad1ec5b90f68885471977f7"
checksum = "1f762cbc08e1a51389859cf9c199c7aef544789cf3510889aab12c607f701604"
dependencies = [
"bitflags",
]

View File

@ -19,7 +19,9 @@ winit = "^0.26.1"
raw-window-handle = "^0.4.3"
# gpu API
wgpu = "^0.13.0"
wgpu = "0.13.0"
wgpu-hal = "0.13.2"
wgpu-types = "0.13.2"
bytemuck = { version = "1.4", features = [ "derive" ] }
[dependencies.image]

View File

@ -70,7 +70,11 @@ impl Canvas {
//--Create functions--
pub fn create_texture_sprite(&mut self, size: Size) -> TextureSprite {
TextureSprite::create(self.default_texture.clone(), size)
TextureSprite::create(
self.default_texture.clone(),
size,
self.renderer.create_texture_mesh(),
)
}
pub fn create_text_sprite(&mut self, _size: Size, _scale: f32) -> TextSprite {
@ -129,7 +133,7 @@ impl Canvas {
}
//--Output functions--
pub fn draw<S: Sprite>(&mut self, sprite: &S) {
pub fn draw<S: Sprite>(&mut self, sprite: &mut S) {
//update texture
self.renderer.render(sprite);

View File

@ -154,8 +154,8 @@ impl Application<ExampleState> for ExampleApp {
//canvas.draw(&txt_sprite);
canvas.draw(&state.tex_sprite);
canvas.draw(&state.sub_sprite);
canvas.draw(&mut state.tex_sprite);
canvas.draw(&mut state.sub_sprite);
canvas.update();
Ok(())

View File

@ -3,15 +3,15 @@ use log::{debug, error, info, trace, warn};
use raw_window_handle::HasRawWindowHandle;
use wgpu;
use wgpu_hal;
use crate::{
sprite::Sprite,
texture::{GpuTexture, Texture},
utils::{Pixel, Position, Size},
texture::{GpuTexture},
utils::{Pixel, Size},
};
use std::{
marker::PhantomData,
rc::Rc,
cell::RefCell,
};
@ -26,8 +26,8 @@ pub struct WgpuRenderer {
texture_bind_group_layout: wgpu::BindGroupLayout,
texture_render_pipeline: wgpu::RenderPipeline,
shape_render_pipeline: wgpu::RenderPipeline,
texture_quad: GpuMesh<TextureVertex>,
quad_mesh: GpuMesh<ColorVertex>, //TODO temporary, to be moved to shapes.rs
texture_quad: GpuMesh<TextureVertex, 4, 6>,
quad_mesh: GpuMesh<ColorVertex, 4, 6>, //TODO temporary, to be moved to shapes.rs
output: Option<wgpu::SurfaceTexture>,
}
@ -211,36 +211,36 @@ impl WgpuRenderer {
let texture_quad = {
let mesh = Mesh {
vertices: vec![
vertices: [
TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] },
TextureVertex { position: [1.0, 0.0], tex_coords: [1.0, 1.0] },
TextureVertex { position: [1.0, 1.0], tex_coords: [1.0, 0.0] },
TextureVertex { position: [0.0, 1.0], tex_coords: [0.0, 0.0] },
],
indices: vec![
indices: [
0, 1, 2,
0, 2, 3,
],
};
GpuMesh::create(&device, &mesh)
GpuMesh::create(&device, mesh)
};
let quad_mesh = {
let mesh = Mesh {
vertices: vec![
vertices: [
ColorVertex { position: [0.0, 0.0], color: [1.0, 0.0, 0.0] },
ColorVertex { position: [1.0, 0.0], color: [0.0, 1.0, 0.0] },
ColorVertex { position: [1.0, 1.0], color: [0.0, 0.0, 1.0] },
ColorVertex { position: [0.0, 1.0], color: [0.5, 0.5, 0.5] },
],
indices: vec![
indices: [
0, 1, 2,
0, 2, 3,
],
};
GpuMesh::create(&device, &mesh)
GpuMesh::create(&device, mesh)
};
let output = Some(surface.get_current_texture()
@ -267,7 +267,12 @@ impl WgpuRenderer {
GpuTexture::create(&self.device, &self.texture_bind_group_layout, buf, size)
}
pub fn render<S: Sprite>(&mut self, sprite: &S) {
pub fn create_texture_mesh(&self) -> GpuMesh<TextureVertex, 4, 6>
{
GpuMesh::create(&self.device, TEXTURE_QUAD)
}
pub fn render<S: Sprite>(&mut self, sprite: &mut S) {
let output = match &self.output {
Some(out) => out,
@ -281,15 +286,16 @@ impl WgpuRenderer {
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
match sprite.render_data() {
RenderData::Texture ((mesh, texture)) => {
RenderData::Texture ((gpu_mesh, texture)) => {
let mut texture = texture.borrow_mut();
if texture.is_synced == false {
texture.update(&mut self.queue);
}
//TODO avoid recreating buffers every time
let gpu_mesh = GpuMesh::create(&self.device, &mesh);
if gpu_mesh.is_synced() == false {
gpu_mesh.update(&mut self.queue);
}
let mut encoder = self.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
@ -389,32 +395,40 @@ impl WgpuRenderer {
}
}
pub struct GpuMesh<V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable> {
pub struct GpuMesh<V, const V_NB: usize, const I_NB: usize>
where
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
{
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
index_number: u32,
phantom: PhantomData<V>, //keep track of wich type of vertex is used
mesh: Mesh<V, V_NB, I_NB>,
is_synced: bool,
}
impl<V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable> GpuMesh<V> {
impl<V, const V_NB: usize, const I_NB: usize> GpuMesh<V, V_NB, I_NB>
where
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
{
fn create(device: &wgpu::Device, mesh: &Mesh<V>) -> Self {
use wgpu::util::DeviceExt;
pub fn create(device: &wgpu::Device, mesh: Mesh<V, V_NB, I_NB>) -> Self {
use std::mem::size_of;
use wgpu_types::BufferUsages as Usages;
let vertex_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
let vertex_buffer = device.create_buffer(
&wgpu_types::BufferDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(&mesh.vertices),
usage: wgpu::BufferUsages::VERTEX,
size: (V_NB * size_of::<V>()) as u64,
usage: Usages::VERTEX.union(Usages::COPY_DST),
mapped_at_creation: false,
}
);
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer( &wgpu_types::BufferDescriptor {
label: Some("Index Buffer"),
contents: bytemuck::cast_slice(&mesh.indices),
usage: wgpu::BufferUsages::INDEX,
size: (I_NB * size_of::<u16>()) as u64,
usage: Usages::INDEX.union(Usages::COPY_DST),
mapped_at_creation: false,
}
);
@ -422,14 +436,33 @@ impl<V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable> GpuMesh<V> {
vertex_buffer,
index_buffer,
index_number: mesh.indices.len() as u32,
phantom: PhantomData,
mesh,
is_synced: true,
}
}
pub fn set_mesh(&mut self, mesh: Mesh<V, V_NB, I_NB>) {
self.mesh = mesh;
self.is_synced = false;
}
pub fn is_synced(&self) -> bool {
self.is_synced
}
pub fn update(&mut self, queue: &wgpu::Queue) {
queue.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&self.mesh.vertices));
queue.write_buffer(&self.index_buffer, 0, bytemuck::cast_slice(&self.mesh.indices));
self.is_synced = true;
}
}
pub enum RenderData<'a> {
Texture ((&'a Mesh<TextureVertex>, &'a Rc<RefCell<GpuTexture>>)),
Shape (&'a GpuMesh<ColorVertex>),
Texture ((&'a mut GpuMesh<TextureVertex, 4, 6>, &'a Rc<RefCell<GpuTexture>>)),
Shape (&'a GpuMesh<ColorVertex, 4, 6>),
}
//--Renderer struct utils---------------------------------------------------------------------------
@ -475,23 +508,26 @@ impl ColorVertex {
}
}
pub struct Mesh<V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable> {
pub vertices: Vec<V>,
pub indices: Vec<u16>,
pub struct Mesh<V, const V_NB: usize, const I_NB: usize>
where
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
{
pub vertices: [V; V_NB],
pub indices: [u16; I_NB],
}
//pub const TEXTURE_QUAD: Mesh<TextureVertex> = Mesh {
// vertices: [
// TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] },
// TextureVertex { position: [1.0, 0.0], tex_coords: [1.0, 1.0] },
// TextureVertex { position: [1.0, 1.0], tex_coords: [1.0, 0.0] },
// TextureVertex { position: [0.0, 1.0], tex_coords: [0.0, 0.0] },
// ],
// indices: [
// 0, 1, 2,
// 0, 2, 3,
// ],
//};
pub const TEXTURE_QUAD: Mesh<TextureVertex, 4, 6> = Mesh {
vertices: [
TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] },
TextureVertex { position: [1.0, 0.0], tex_coords: [1.0, 1.0] },
TextureVertex { position: [1.0, 1.0], tex_coords: [1.0, 0.0] },
TextureVertex { position: [0.0, 1.0], tex_coords: [0.0, 0.0] },
],
indices: [
0, 1, 2,
0, 2, 3,
],
};
//
//const QUAD: Mesh<ColorVertex> = Mesh {
// vertices: [

View File

@ -2,7 +2,7 @@
use log::{debug, error, info, trace, warn};
use crate::{
renderer::{Mesh, RenderData, TextureVertex},
renderer::{Mesh, GpuMesh, RenderData, TextureVertex},
shape::Shape,
texture::Texture,
utils::{Pixel, Position, Size},
@ -23,7 +23,7 @@ pub trait Sprite {
fn set_rotation(&mut self, rot: f32);
fn set_alpha(&mut self, alpha: u8);
fn set_scale(&mut self, scale: f32);
fn render_data(&self) -> RenderData;
fn render_data(&mut self) -> RenderData;
}
//--TextureSprite struct----------------------------------------------------------------------------
@ -31,7 +31,7 @@ pub struct TextureSprite {
position: Position,
scale: f32,
matrix: Matrix4<f32>,
mesh: Mesh<TextureVertex>,
gpu_mesh: GpuMesh<TextureVertex, 4, 6>,
texture: Texture,
texture_offset: Position,
texture_size: Size,
@ -39,29 +39,17 @@ pub struct TextureSprite {
impl TextureSprite {
pub fn create(texture: Texture, size: Size) -> Self {
pub fn create(texture: Texture, size: Size, gpu_mesh: GpuMesh<TextureVertex, 4, 6>) -> Self {
let position = Position::origin();
let scale = 1.0;
let matrix = Matrix4::from_scale(scale) + Matrix4::from_translation(position.into());
let mesh = Mesh {
vertices: vec![
TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] },
TextureVertex { position: [1.0, 0.0], tex_coords: [1.0, 1.0] },
TextureVertex { position: [1.0, 1.0], tex_coords: [1.0, 0.0] },
TextureVertex { position: [0.0, 1.0], tex_coords: [0.0, 0.0] },
],
indices: vec![
0, 1, 2,
0, 2, 3,
],
};
Self {
position,
scale,
matrix,
mesh,
gpu_mesh,
texture,
texture_offset: position,
texture_size: size,
@ -82,8 +70,8 @@ impl TextureSprite {
let y_offset = self.texture_offset.y as f32 / size.h as f32;
// generate new sprite mesh
self.mesh = Mesh {
vertices: vec![
let mesh = Mesh {
vertices: [
TextureVertex {
position: [0.0, 0.0],
tex_coords: [x_offset , y_offset + y_size],
@ -101,11 +89,13 @@ impl TextureSprite {
tex_coords: [x_offset , y_offset ],
},
],
indices: vec![
indices: [
0, 1, 2,
0, 2, 3,
],
};
self.gpu_mesh.set_mesh(mesh);
}
pub fn set_pixel(&mut self, pos: Position, pix: Pixel) {
@ -137,8 +127,8 @@ impl Sprite for TextureSprite {
unimplemented!();
}
fn render_data(&self) -> RenderData {
RenderData::Texture ((&self.mesh, &self.texture.gpu_texture()))
fn render_data(&mut self) -> RenderData {
RenderData::Texture ((&mut self.gpu_mesh, &self.texture.gpu_texture()))
}
}
@ -178,7 +168,7 @@ impl Sprite for TextSprite {
unimplemented!();
}
fn render_data(&self) -> RenderData {
fn render_data(&mut self) -> RenderData {
todo!();
}
}
@ -219,7 +209,7 @@ impl Sprite for ShapeSprite {
unimplemented!();
}
fn render_data(&self) -> RenderData {
fn render_data(&mut self) -> RenderData {
todo!();
}
}