From d3bc7ecc7f9b5e6b9307f5bb2825298db917d087 Mon Sep 17 00:00:00 2001 From: Steins7 Date: Sun, 21 Aug 2022 20:35:02 +0200 Subject: [PATCH] Moved from push constants to uniforms This move also fixed display issues on Vulkan since the matrix's data alignement was not properly handled --- src/canvas.rs | 1 + src/main.rs | 3 +- src/renderer.rs | 163 ++++++++++++++++++++++++++++++--------- src/shaders/texture.wgsl | 9 ++- src/sprite.rs | 54 +++++++++---- 5 files changed, 176 insertions(+), 54 deletions(-) diff --git a/src/canvas.rs b/src/canvas.rs index 24cebc8..2017259 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -74,6 +74,7 @@ impl Canvas { self.default_texture.clone(), size, self.renderer.create_texture_mesh(), + self.renderer.create_matrix_uniform(), ) } diff --git a/src/main.rs b/src/main.rs index 5ffab97..7ed6dfc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,6 +58,7 @@ impl Application for ExampleApp { fn init(canvas: &mut Canvas) -> Result { use canvas::{ utils::{Position, Size}, + sprite::Sprite, }; //// 20 x 20 sprite of a picture @@ -101,7 +102,7 @@ impl Application for ExampleApp { //state.sub_sprite.for_each(|pix| unsafe {pix.flat = pix.flat.wrapping_add(1);}); state.last_offset += 1; - state.last_pos.x += 1; + state.last_pos.y += 1; state.last_rot += 1.0; state.tex_sprite.set_texture(state.texture.clone(), None, state.last_rot/100.0); state.sub_sprite.set_texture(state.texture.clone(), diff --git a/src/renderer.rs b/src/renderer.rs index 4817e30..5e10146 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -7,7 +7,7 @@ use wgpu_hal; use cgmath::Matrix3; use crate::{ - sprite::{Sprite, ModelMatrix}, + sprite::{Sprite, ModelMatrix, MATRIX_SIZE}, texture::{GpuTexture}, utils::{Pixel, Size}, }; @@ -25,9 +25,10 @@ pub struct WgpuRenderer { config: wgpu::SurfaceConfiguration, surface_size: Size, - aspect_matrix: Matrix3, output: Option, + matrix_bind_group_layout: wgpu::BindGroupLayout, + texture_bind_group_layout: wgpu::BindGroupLayout, texture_render_pipeline: wgpu::RenderPipeline, @@ -57,14 +58,11 @@ impl WgpuRenderer { }, ).await.unwrap(); - let mut limits = wgpu::Limits::downlevel_defaults(); - limits.max_push_constant_size = 36; - let (device, queue) = adapter.request_device( &wgpu::DeviceDescriptor { //using minimum requirements possible since 2D isn't very demanding anyway features: wgpu::Features::PUSH_CONSTANTS, - limits, + limits: wgpu::Limits::downlevel_webgl2_defaults(), label: None, }, None, // Trace path @@ -82,12 +80,35 @@ impl WgpuRenderer { surface.configure(&device, &config); let surface_size = size; - let aspect_matrix - = Matrix3::from_nonuniform_scale(1.0 / size.w as f32, 1.0 / size.h as f32); + //let aspect_matrix = { + // use cgmath::{Basis3, Rotation3, Deg}; + + // //Matrix3::from(Basis3::from_angle_x(Deg(-0.0))) + // //* Matrix3::identity() + // //* Matrix3::from_nonuniform_scale(1.0 / size.w as f32, 1.0 / size.h as f32) + // Matrix3::from_nonuniform_scale(1.0, 1.0) + //}; let output = Some(surface.get_current_texture() .map_err(|_| "Failed to create SurfaceTexture")?); + let matrix_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + } + ], + label: Some("matrix_bind_group_layout"), + }); + let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { entries: &[ @@ -113,11 +134,6 @@ impl WgpuRenderer { label: Some("texture_bind_group_layout"), }); - let mvp_push_constant = wgpu::PushConstantRange { - stages: wgpu::ShaderStages::VERTEX, - range: 0..36, - }; - let texture_render_pipeline = { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("texture shader"), @@ -127,8 +143,11 @@ impl WgpuRenderer { let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("texture render pipeline layout"), - bind_group_layouts: &[&texture_bind_group_layout], - push_constant_ranges: &[mvp_push_constant], + bind_group_layouts: &[ + &matrix_bind_group_layout, + &texture_bind_group_layout, + ], + push_constant_ranges: &[], }); device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -154,7 +173,7 @@ impl WgpuRenderer { topology: wgpu::PrimitiveTopology::TriangleList, strip_index_format: None, front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), + cull_mode: None,//Some(wgpu::Face::Back), // Setting this to anything other than Fill requires // Features::NON_FILL_POLYGON_MODE polygon_mode: wgpu::PolygonMode::Fill, @@ -183,7 +202,9 @@ impl WgpuRenderer { let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("shape render pipeline layout"), - bind_group_layouts: &[], + bind_group_layouts: &[ + &matrix_bind_group_layout, + ], push_constant_ranges: &[], }); @@ -253,8 +274,8 @@ impl WgpuRenderer { queue, config, surface_size, - aspect_matrix, output, + matrix_bind_group_layout, texture_bind_group_layout, texture_render_pipeline, shape_render_pipeline, @@ -262,15 +283,16 @@ impl WgpuRenderer { }) } - pub fn create_gpu_texture(&mut self, buf: &[u8], size: Size) - -> GpuTexture - { - GpuTexture::create(&self.device, &self.texture_bind_group_layout, buf, size) + pub fn create_gpu_texture(&mut self, buf: &[u8], size: Size) -> GpuTexture { + GpuTexture::create(&self.device, &self.texture_bind_group_layout, buf, size) } - pub fn create_texture_mesh(&self) -> GpuMesh - { - GpuMesh::create(&self.device, TEXTURE_QUAD) + pub fn create_texture_mesh(&self) -> GpuMesh { + GpuMesh::create(&self.device, TEXTURE_QUAD) + } + + pub fn create_matrix_uniform(&self) -> GpuUniform { + GpuUniform::create(&self.device, &self.matrix_bind_group_layout) } pub fn render(&mut self, sprite: &mut S) { @@ -290,6 +312,8 @@ impl WgpuRenderer { RenderData::Texture ((matrix, gpu_mesh, texture)) => { let mut texture = texture.borrow_mut(); + let matrix_bind_group = matrix.get_bind_group(&mut self.queue); + if texture.is_synced == false { texture.update(&mut self.queue); } @@ -318,12 +342,13 @@ impl WgpuRenderer { render_pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..)); render_pass.set_index_buffer(gpu_mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint16); - render_pass.set_bind_group(0, &texture.bind_group, &[]); - render_pass.set_push_constants( - wgpu::ShaderStages::VERTEX, - 0, - bytemuck::bytes_of(&(self.aspect_matrix * matrix.get_matrix())) - ); + render_pass.set_bind_group(0, matrix_bind_group, &[]); + render_pass.set_bind_group(1, &texture.bind_group, &[]); + //render_pass.set_push_constants( + // wgpu::ShaderStages::VERTEX, + // 0, + // bytemuck::bytes_of(&(self.aspect_matrix))// * matrix.get_matrix())) + //); render_pass.draw_indexed(0..gpu_mesh.index_number, 0, 0..1); drop(render_pass); @@ -373,8 +398,15 @@ impl WgpuRenderer { } self.surface_size = size; - self.aspect_matrix - = Matrix3::from_nonuniform_scale(1.0 / size.w as f32, 1.0 / size.h as f32); + //self.aspect_matrix = { + // use cgmath::{Basis3, Rotation3, Deg}; + + // //Matrix3::from(Basis3::from_angle_x(Deg(-0.0))) + // //* Matrix3::identity() + // // * Matrix3::from_nonuniform_scale(1.0 / size.w as f32, 1.0 / size.h as f32) + // Matrix3::from_nonuniform_scale(1.0, 1.0) + + //}; self.config.width = size.w; self.config.height = size.h; self.surface.configure(&self.device, &self.config); @@ -404,6 +436,7 @@ impl WgpuRenderer { } } +//--GpuMesh struct---------------------------------------------------------------------------------- pub struct GpuMesh where V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable, @@ -429,7 +462,7 @@ where &wgpu_types::BufferDescriptor { label: Some("Vertex Buffer"), size: (V_NB * size_of::()) as u64, - usage: Usages::VERTEX.union(Usages::COPY_DST), + usage: Usages::VERTEX | Usages::COPY_DST, mapped_at_creation: false, } ); @@ -437,7 +470,7 @@ where let index_buffer = device.create_buffer( &wgpu_types::BufferDescriptor { label: Some("Index Buffer"), size: (I_NB * size_of::()) as u64, - usage: Usages::INDEX.union(Usages::COPY_DST), + usage: Usages::INDEX | Usages::COPY_DST, mapped_at_creation: false, } ); @@ -470,9 +503,67 @@ where } } +//--GpuUniform struct------------------------------------------------------------------------------- +pub struct GpuUniform { + uniform: wgpu::Buffer, + bind_group: wgpu::BindGroup, + data: [u8; S], + is_synced: bool, +} + +impl GpuUniform { + + pub fn create(device: &wgpu::Device, layout: &wgpu::BindGroupLayout) -> Self { + + let uniform = device.create_buffer( + &wgpu_types::BufferDescriptor { + label: Some("Uniform Buffer"), + size: S as u64, + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + } + ); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: uniform.as_entire_binding(), + } + ], + label: Some("uniform_bind_group"), + }); + + Self { + uniform, + bind_group, + data: [0; S], + is_synced: false, + } + } + + pub fn update(&mut self, data: [u8; S]) { + + self.data = data; + self.is_synced = false; + } + + pub fn get_bind_group(&mut self, queue: &wgpu::Queue) -> &wgpu::BindGroup { + + if self.is_synced == false { + queue.write_buffer(&self.uniform, 0, &self.data); + self.is_synced = true; + } + + &self.bind_group + } +} + + pub enum RenderData<'a> { Texture (( - &'a mut ModelMatrix, + &'a mut GpuUniform, &'a mut GpuMesh, &'a Rc>, )), diff --git a/src/shaders/texture.wgsl b/src/shaders/texture.wgsl index 9955b9f..db20088 100644 --- a/src/shaders/texture.wgsl +++ b/src/shaders/texture.wgsl @@ -1,7 +1,5 @@ // Vertex shader -var model_matrix: mat3x3; - struct VertexInput { @location(0) position: vec2, @location(1) tex_coords: vec2, @@ -12,6 +10,9 @@ struct VertexOutput { @location(0) tex_coords: vec2, } +@group(0)@binding(0) +var model_matrix: mat3x3; + @vertex fn vs_main(model: VertexInput) -> VertexOutput { @@ -24,9 +25,9 @@ fn vs_main(model: VertexInput) -> VertexOutput { // Fragment shader -@group(0) @binding(0) +@group(1)@binding(0) var t_diffuse: texture_2d; -@group(0)@binding(1) +@group(1)@binding(1) var s_diffuse: sampler; @fragment diff --git a/src/sprite.rs b/src/sprite.rs index a96b40b..3e09108 100644 --- a/src/sprite.rs +++ b/src/sprite.rs @@ -2,7 +2,7 @@ use log::{debug, error, info, trace, warn}; use crate::{ - renderer::{Mesh, GpuMesh, RenderData, TextureVertex}, + renderer::{Mesh, GpuMesh, RenderData, TextureVertex, GpuUniform}, shape::Shape, texture::Texture, utils::{Pixel, Position, Size, NormalizedSize}, @@ -37,13 +37,18 @@ pub struct ModelMatrix { rotation: Deg, scale: f32, - matrix: Matrix3, + uniform: GpuUniform, is_synced: bool, } impl ModelMatrix { - pub fn new(pos: Position, rot: f32, scale: f32) -> Self { + pub fn new(pos: Position, + rot: f32, + scale: f32, + uniform: GpuUniform) + -> Self + { use cgmath::SquareMatrix; Self { @@ -51,13 +56,13 @@ impl ModelMatrix { rotation: Deg (rot), scale, - matrix: Matrix3::identity(), + uniform, is_synced: false, } } - pub fn default() -> Self { - Self::new(Position::origin(), 0.0, 1.0) + pub fn default(uniform: GpuUniform) -> Self { + Self::new(Position::origin(), 0.0, 1.0, uniform) } pub fn set_position(&mut self, pos: Position) { @@ -75,7 +80,7 @@ impl ModelMatrix { self.is_synced = false; } - pub fn get_matrix(&mut self) -> Matrix3 { + pub fn get_uniform(&mut self) -> &mut GpuUniform { use cgmath::{Basis3, Rotation3, SquareMatrix}; if self.is_synced == false { @@ -87,15 +92,30 @@ impl ModelMatrix { let rotation_mat = Matrix3::from(Basis3::from_angle_z(self.rotation)); let scale_mat = Matrix3::from_scale(self.scale); let translation_mat = Matrix3::from_translation(pos_vec); + let aspect_mat = Matrix3::from_nonuniform_scale(1.0/1280.0, 1.0/720.0); - self.matrix = translation_mat * rotation_mat * scale_mat; + let matrix = aspect_mat * translation_mat * rotation_mat * scale_mat; + let mat_bytes: [u8; 36] = bytemuck::bytes_of(&matrix).try_into().unwrap(); + let mut bytes = [0; 48]; + for i in 0..12 { + bytes[i] = mat_bytes[i]; + } + for i in 0..12 { + bytes[i+16] = mat_bytes[i+12]; + } + for i in 0..12 { + bytes[i+32] = mat_bytes[i+24]; + } + self.uniform.update(bytes); self.is_synced = true; } - self.matrix + &mut self.uniform } } +pub const MATRIX_SIZE: usize = 48;//std::mem::size_of::>(); + //--TextureSprite struct---------------------------------------------------------------------------- pub struct TextureSprite { matrix: ModelMatrix, @@ -109,10 +129,14 @@ pub struct TextureSprite { impl TextureSprite { - pub fn create(texture: Texture, size: Size, gpu_mesh: GpuMesh) -> Self { - + pub fn create(texture: Texture, + size: Size, + gpu_mesh: GpuMesh, + matrix_uniform: GpuUniform) + -> Self + { let mut sprite = Self { - matrix: ModelMatrix::default(), + matrix: ModelMatrix::default(matrix_uniform), gpu_mesh, inner_size: size, @@ -205,7 +229,11 @@ impl Sprite for TextureSprite { } fn render_data(&mut self) -> RenderData { - RenderData::Texture ((&mut self.matrix, &mut self.gpu_mesh, &self.texture.gpu_texture())) + RenderData::Texture (( + self.matrix.get_uniform(), + &mut self.gpu_mesh, + &self.texture.gpu_texture(), + )) } }