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(),
size,
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> {
use canvas::{
utils::{Position, Size},
sprite::Sprite,
};
//// 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.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(),

View File

@ -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<f32>,
output: Option<wgpu::SurfaceTexture>,
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,17 +283,18 @@ impl WgpuRenderer {
})
}
pub fn create_gpu_texture(&mut self, buf: &[u8], size: Size)
-> GpuTexture
{
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<TextureVertex, 4, 6>
{
pub fn create_texture_mesh(&self) -> GpuMesh<TextureVertex, 4, 6> {
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) {
let output = match &self.output {
@ -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<V, const V_NB: usize, const I_NB: usize>
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::<V>()) 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::<u16>()) 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<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> {
Texture ((
&'a mut ModelMatrix,
&'a mut GpuUniform<MATRIX_SIZE>,
&'a mut GpuMesh<TextureVertex, 4, 6>,
&'a Rc<RefCell<GpuTexture>>,
)),

View File

@ -1,7 +1,5 @@
// Vertex shader
var<push_constant> model_matrix: mat3x3<f32>;
struct VertexInput {
@location(0) position: vec2<f32>,
@location(1) tex_coords: vec2<f32>,
@ -12,6 +10,9 @@ struct VertexOutput {
@location(0) tex_coords: vec2<f32>,
}
@group(0)@binding(0)
var<uniform> model_matrix: mat3x3<f32>;
@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<f32>;
@group(0)@binding(1)
@group(1)@binding(1)
var s_diffuse: sampler;
@fragment

View File

@ -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<f32>,
scale: f32,
matrix: Matrix3<f32>,
uniform: GpuUniform<MATRIX_SIZE>,
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<MATRIX_SIZE>)
-> 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<MATRIX_SIZE>) -> 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<f32> {
pub fn get_uniform(&mut self) -> &mut GpuUniform<MATRIX_SIZE> {
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::<Matrix3<f32>>();
//--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<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 {
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(),
))
}
}