Moved from push constants to uniforms

This move also fixed display issues on Vulkan since the matrix's data
alignement was not properly handled
This commit is contained in:
Steins7 2022-08-21 20:35:02 +02:00
parent 2591b60a5d
commit d3bc7ecc7f
5 changed files with 176 additions and 54 deletions

View File

@ -74,6 +74,7 @@ impl Canvas {
self.default_texture.clone(), self.default_texture.clone(),
size, size,
self.renderer.create_texture_mesh(), self.renderer.create_texture_mesh(),
self.renderer.create_matrix_uniform(),
) )
} }

View File

@ -58,6 +58,7 @@ impl Application<ExampleState> for ExampleApp {
fn init(canvas: &mut Canvas) -> Result<ExampleState, &'static str> { fn init(canvas: &mut Canvas) -> Result<ExampleState, &'static str> {
use canvas::{ use canvas::{
utils::{Position, Size}, utils::{Position, Size},
sprite::Sprite,
}; };
//// 20 x 20 sprite of a picture //// 20 x 20 sprite of a picture
@ -101,7 +102,7 @@ impl Application<ExampleState> for ExampleApp {
//state.sub_sprite.for_each(|pix| unsafe {pix.flat = pix.flat.wrapping_add(1);}); //state.sub_sprite.for_each(|pix| unsafe {pix.flat = pix.flat.wrapping_add(1);});
state.last_offset += 1; state.last_offset += 1;
state.last_pos.x += 1; state.last_pos.y += 1;
state.last_rot += 1.0; state.last_rot += 1.0;
state.tex_sprite.set_texture(state.texture.clone(), None, state.last_rot/100.0); state.tex_sprite.set_texture(state.texture.clone(), None, state.last_rot/100.0);
state.sub_sprite.set_texture(state.texture.clone(), state.sub_sprite.set_texture(state.texture.clone(),

View File

@ -7,7 +7,7 @@ use wgpu_hal;
use cgmath::Matrix3; use cgmath::Matrix3;
use crate::{ use crate::{
sprite::{Sprite, ModelMatrix}, sprite::{Sprite, ModelMatrix, MATRIX_SIZE},
texture::{GpuTexture}, texture::{GpuTexture},
utils::{Pixel, Size}, utils::{Pixel, Size},
}; };
@ -25,9 +25,10 @@ pub struct WgpuRenderer {
config: wgpu::SurfaceConfiguration, config: wgpu::SurfaceConfiguration,
surface_size: Size, surface_size: Size,
aspect_matrix: Matrix3<f32>,
output: Option<wgpu::SurfaceTexture>, output: Option<wgpu::SurfaceTexture>,
matrix_bind_group_layout: wgpu::BindGroupLayout,
texture_bind_group_layout: wgpu::BindGroupLayout, texture_bind_group_layout: wgpu::BindGroupLayout,
texture_render_pipeline: wgpu::RenderPipeline, texture_render_pipeline: wgpu::RenderPipeline,
@ -57,14 +58,11 @@ impl WgpuRenderer {
}, },
).await.unwrap(); ).await.unwrap();
let mut limits = wgpu::Limits::downlevel_defaults();
limits.max_push_constant_size = 36;
let (device, queue) = adapter.request_device( let (device, queue) = adapter.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
//using minimum requirements possible since 2D isn't very demanding anyway //using minimum requirements possible since 2D isn't very demanding anyway
features: wgpu::Features::PUSH_CONSTANTS, features: wgpu::Features::PUSH_CONSTANTS,
limits, limits: wgpu::Limits::downlevel_webgl2_defaults(),
label: None, label: None,
}, },
None, // Trace path None, // Trace path
@ -82,12 +80,35 @@ impl WgpuRenderer {
surface.configure(&device, &config); surface.configure(&device, &config);
let surface_size = size; let surface_size = size;
let aspect_matrix //let aspect_matrix = {
= Matrix3::from_nonuniform_scale(1.0 / size.w as f32, 1.0 / size.h as f32); // 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() let output = Some(surface.get_current_texture()
.map_err(|_| "Failed to create SurfaceTexture")?); .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 = let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
@ -113,11 +134,6 @@ impl WgpuRenderer {
label: Some("texture_bind_group_layout"), label: Some("texture_bind_group_layout"),
}); });
let mvp_push_constant = wgpu::PushConstantRange {
stages: wgpu::ShaderStages::VERTEX,
range: 0..36,
};
let texture_render_pipeline = { let texture_render_pipeline = {
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("texture shader"), label: Some("texture shader"),
@ -127,8 +143,11 @@ impl WgpuRenderer {
let render_pipeline_layout = let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("texture render pipeline layout"), label: Some("texture render pipeline layout"),
bind_group_layouts: &[&texture_bind_group_layout], bind_group_layouts: &[
push_constant_ranges: &[mvp_push_constant], &matrix_bind_group_layout,
&texture_bind_group_layout,
],
push_constant_ranges: &[],
}); });
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
@ -154,7 +173,7 @@ impl WgpuRenderer {
topology: wgpu::PrimitiveTopology::TriangleList, topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None, strip_index_format: None,
front_face: wgpu::FrontFace::Ccw, 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 // Setting this to anything other than Fill requires
// Features::NON_FILL_POLYGON_MODE // Features::NON_FILL_POLYGON_MODE
polygon_mode: wgpu::PolygonMode::Fill, polygon_mode: wgpu::PolygonMode::Fill,
@ -183,7 +202,9 @@ impl WgpuRenderer {
let render_pipeline_layout = let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("shape render pipeline layout"), label: Some("shape render pipeline layout"),
bind_group_layouts: &[], bind_group_layouts: &[
&matrix_bind_group_layout,
],
push_constant_ranges: &[], push_constant_ranges: &[],
}); });
@ -253,8 +274,8 @@ impl WgpuRenderer {
queue, queue,
config, config,
surface_size, surface_size,
aspect_matrix,
output, output,
matrix_bind_group_layout,
texture_bind_group_layout, texture_bind_group_layout,
texture_render_pipeline, texture_render_pipeline,
shape_render_pipeline, shape_render_pipeline,
@ -262,15 +283,16 @@ impl WgpuRenderer {
}) })
} }
pub fn create_gpu_texture(&mut self, buf: &[u8], size: Size) pub fn create_gpu_texture(&mut self, buf: &[u8], size: Size) -> GpuTexture {
-> GpuTexture GpuTexture::create(&self.device, &self.texture_bind_group_layout, buf, size)
{
GpuTexture::create(&self.device, &self.texture_bind_group_layout, buf, size)
} }
pub fn create_texture_mesh(&self) -> GpuMesh<TextureVertex, 4, 6> pub fn create_texture_mesh(&self) -> GpuMesh<TextureVertex, 4, 6> {
{ GpuMesh::create(&self.device, TEXTURE_QUAD)
GpuMesh::create(&self.device, TEXTURE_QUAD) }
pub fn create_matrix_uniform(&self) -> GpuUniform<MATRIX_SIZE> {
GpuUniform::create(&self.device, &self.matrix_bind_group_layout)
} }
pub fn render<S: Sprite>(&mut self, sprite: &mut S) { pub fn render<S: Sprite>(&mut self, sprite: &mut S) {
@ -290,6 +312,8 @@ impl WgpuRenderer {
RenderData::Texture ((matrix, gpu_mesh, texture)) => { RenderData::Texture ((matrix, gpu_mesh, texture)) => {
let mut texture = texture.borrow_mut(); let mut texture = texture.borrow_mut();
let matrix_bind_group = matrix.get_bind_group(&mut self.queue);
if texture.is_synced == false { if texture.is_synced == false {
texture.update(&mut self.queue); 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_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
render_pass.set_index_buffer(gpu_mesh.index_buffer.slice(..), render_pass.set_index_buffer(gpu_mesh.index_buffer.slice(..),
wgpu::IndexFormat::Uint16); wgpu::IndexFormat::Uint16);
render_pass.set_bind_group(0, &texture.bind_group, &[]); render_pass.set_bind_group(0, matrix_bind_group, &[]);
render_pass.set_push_constants( render_pass.set_bind_group(1, &texture.bind_group, &[]);
wgpu::ShaderStages::VERTEX, //render_pass.set_push_constants(
0, // wgpu::ShaderStages::VERTEX,
bytemuck::bytes_of(&(self.aspect_matrix * matrix.get_matrix())) // 0,
); // bytemuck::bytes_of(&(self.aspect_matrix))// * matrix.get_matrix()))
//);
render_pass.draw_indexed(0..gpu_mesh.index_number, 0, 0..1); render_pass.draw_indexed(0..gpu_mesh.index_number, 0, 0..1);
drop(render_pass); drop(render_pass);
@ -373,8 +398,15 @@ impl WgpuRenderer {
} }
self.surface_size = size; self.surface_size = size;
self.aspect_matrix //self.aspect_matrix = {
= Matrix3::from_nonuniform_scale(1.0 / size.w as f32, 1.0 / size.h as f32); // 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.width = size.w;
self.config.height = size.h; self.config.height = size.h;
self.surface.configure(&self.device, &self.config); self.surface.configure(&self.device, &self.config);
@ -404,6 +436,7 @@ impl WgpuRenderer {
} }
} }
//--GpuMesh struct----------------------------------------------------------------------------------
pub struct GpuMesh<V, const V_NB: usize, const I_NB: usize> pub struct GpuMesh<V, const V_NB: usize, const I_NB: usize>
where where
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable, V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
@ -429,7 +462,7 @@ where
&wgpu_types::BufferDescriptor { &wgpu_types::BufferDescriptor {
label: Some("Vertex Buffer"), label: Some("Vertex Buffer"),
size: (V_NB * size_of::<V>()) as u64, size: (V_NB * size_of::<V>()) as u64,
usage: Usages::VERTEX.union(Usages::COPY_DST), usage: Usages::VERTEX | Usages::COPY_DST,
mapped_at_creation: false, mapped_at_creation: false,
} }
); );
@ -437,7 +470,7 @@ where
let index_buffer = device.create_buffer( &wgpu_types::BufferDescriptor { let index_buffer = device.create_buffer( &wgpu_types::BufferDescriptor {
label: Some("Index Buffer"), label: Some("Index Buffer"),
size: (I_NB * size_of::<u16>()) as u64, size: (I_NB * size_of::<u16>()) as u64,
usage: Usages::INDEX.union(Usages::COPY_DST), usage: Usages::INDEX | Usages::COPY_DST,
mapped_at_creation: false, mapped_at_creation: false,
} }
); );
@ -470,9 +503,67 @@ where
} }
} }
//--GpuUniform struct-------------------------------------------------------------------------------
pub struct GpuUniform<const S: usize> {
uniform: wgpu::Buffer,
bind_group: wgpu::BindGroup,
data: [u8; S],
is_synced: bool,
}
impl<const S: usize> GpuUniform<S> {
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> { pub enum RenderData<'a> {
Texture (( Texture ((
&'a mut ModelMatrix, &'a mut GpuUniform<MATRIX_SIZE>,
&'a mut GpuMesh<TextureVertex, 4, 6>, &'a mut GpuMesh<TextureVertex, 4, 6>,
&'a Rc<RefCell<GpuTexture>>, &'a Rc<RefCell<GpuTexture>>,
)), )),

View File

@ -1,7 +1,5 @@
// Vertex shader // Vertex shader
var<push_constant> model_matrix: mat3x3<f32>;
struct VertexInput { struct VertexInput {
@location(0) position: vec2<f32>, @location(0) position: vec2<f32>,
@location(1) tex_coords: vec2<f32>, @location(1) tex_coords: vec2<f32>,
@ -12,6 +10,9 @@ struct VertexOutput {
@location(0) tex_coords: vec2<f32>, @location(0) tex_coords: vec2<f32>,
} }
@group(0)@binding(0)
var<uniform> model_matrix: mat3x3<f32>;
@vertex @vertex
fn vs_main(model: VertexInput) -> VertexOutput { fn vs_main(model: VertexInput) -> VertexOutput {
@ -24,9 +25,9 @@ fn vs_main(model: VertexInput) -> VertexOutput {
// Fragment shader // Fragment shader
@group(0) @binding(0) @group(1)@binding(0)
var t_diffuse: texture_2d<f32>; var t_diffuse: texture_2d<f32>;
@group(0)@binding(1) @group(1)@binding(1)
var s_diffuse: sampler; var s_diffuse: sampler;
@fragment @fragment

View File

@ -2,7 +2,7 @@
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use crate::{ use crate::{
renderer::{Mesh, GpuMesh, RenderData, TextureVertex}, renderer::{Mesh, GpuMesh, RenderData, TextureVertex, GpuUniform},
shape::Shape, shape::Shape,
texture::Texture, texture::Texture,
utils::{Pixel, Position, Size, NormalizedSize}, utils::{Pixel, Position, Size, NormalizedSize},
@ -37,13 +37,18 @@ pub struct ModelMatrix {
rotation: Deg<f32>, rotation: Deg<f32>,
scale: f32, scale: f32,
matrix: Matrix3<f32>, uniform: GpuUniform<MATRIX_SIZE>,
is_synced: bool, is_synced: bool,
} }
impl ModelMatrix { impl ModelMatrix {
pub fn new(pos: Position, rot: f32, scale: f32) -> Self { pub fn new(pos: Position,
rot: f32,
scale: f32,
uniform: GpuUniform<MATRIX_SIZE>)
-> Self
{
use cgmath::SquareMatrix; use cgmath::SquareMatrix;
Self { Self {
@ -51,13 +56,13 @@ impl ModelMatrix {
rotation: Deg (rot), rotation: Deg (rot),
scale, scale,
matrix: Matrix3::identity(), uniform,
is_synced: false, is_synced: false,
} }
} }
pub fn default() -> Self { pub fn default(uniform: GpuUniform<MATRIX_SIZE>) -> Self {
Self::new(Position::origin(), 0.0, 1.0) Self::new(Position::origin(), 0.0, 1.0, uniform)
} }
pub fn set_position(&mut self, pos: Position) { pub fn set_position(&mut self, pos: Position) {
@ -75,7 +80,7 @@ impl ModelMatrix {
self.is_synced = false; self.is_synced = false;
} }
pub fn get_matrix(&mut self) -> Matrix3<f32> { pub fn get_uniform(&mut self) -> &mut GpuUniform<MATRIX_SIZE> {
use cgmath::{Basis3, Rotation3, SquareMatrix}; use cgmath::{Basis3, Rotation3, SquareMatrix};
if self.is_synced == false { if self.is_synced == false {
@ -87,15 +92,30 @@ impl ModelMatrix {
let rotation_mat = Matrix3::from(Basis3::from_angle_z(self.rotation)); let rotation_mat = Matrix3::from(Basis3::from_angle_z(self.rotation));
let scale_mat = Matrix3::from_scale(self.scale); let scale_mat = Matrix3::from_scale(self.scale);
let translation_mat = Matrix3::from_translation(pos_vec); 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.is_synced = true;
} }
self.matrix &mut self.uniform
} }
} }
pub const MATRIX_SIZE: usize = 48;//std::mem::size_of::<Matrix3<f32>>();
//--TextureSprite struct---------------------------------------------------------------------------- //--TextureSprite struct----------------------------------------------------------------------------
pub struct TextureSprite { pub struct TextureSprite {
matrix: ModelMatrix, matrix: ModelMatrix,
@ -109,10 +129,14 @@ pub struct TextureSprite {
impl TextureSprite { impl TextureSprite {
pub fn create(texture: Texture, size: Size, gpu_mesh: GpuMesh<TextureVertex, 4, 6>) -> Self { pub fn create(texture: Texture,
size: Size,
gpu_mesh: GpuMesh<TextureVertex, 4, 6>,
matrix_uniform: GpuUniform<MATRIX_SIZE>)
-> Self
{
let mut sprite = Self { let mut sprite = Self {
matrix: ModelMatrix::default(), matrix: ModelMatrix::default(matrix_uniform),
gpu_mesh, gpu_mesh,
inner_size: size, inner_size: size,
@ -205,7 +229,11 @@ impl Sprite for TextureSprite {
} }
fn render_data(&mut self) -> RenderData { 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(),
))
} }
} }