From d5d36cfadce9da92a909f51c6c255997931f9dba Mon Sep 17 00:00:00 2001 From: Steins7 Date: Sat, 22 Oct 2022 00:21:41 +0200 Subject: [PATCH] Perform major code refactor The rendering is moved from the renderer to the sprites. As a result, WgpuRenderer is now more of a rendering context while the renderer in itself is the context + the sprites --- Cargo.lock | 12 +- src/canvas.rs | 35 +- src/lib.rs | 6 +- src/main.rs | 32 +- src/renderer.rs | 395 ++-------------------- src/renderer/matrix.rs | 97 ++++++ src/renderer/texture.rs | 98 ++++++ src/renderer/uniform.rs | 62 ++++ src/renderer/utils.rs | 164 +++++++++ src/sprite.rs | 232 +------------ src/{ => sprite}/shaders/shape.wgsl | 0 src/{ => sprite}/shaders/test_shader.wgsl | 0 src/{ => sprite}/shaders/texture.wgsl | 0 src/sprite/texture_sprite.rs | 271 +++++++++++++++ src/texture.rs | 145 ++------ 15 files changed, 807 insertions(+), 742 deletions(-) create mode 100644 src/renderer/matrix.rs create mode 100644 src/renderer/texture.rs create mode 100644 src/renderer/uniform.rs create mode 100644 src/renderer/utils.rs rename src/{ => sprite}/shaders/shape.wgsl (100%) rename src/{ => sprite}/shaders/test_shader.wgsl (100%) rename src/{ => sprite}/shaders/texture.wgsl (100%) create mode 100644 src/sprite/texture_sprite.rs diff --git a/Cargo.lock b/Cargo.lock index 263e1bd..631aec5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,9 +36,9 @@ dependencies = [ [[package]] name = "approx" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" dependencies = [ "num-traits", ] @@ -158,6 +158,7 @@ dependencies = [ "pollster", "raw-window-handle", "wgpu", + "wgpu-core", "wgpu-hal", "wgpu-types", "winit", @@ -190,10 +191,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] name = "cgmath" version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317" dependencies = [ "approx", + "bytemuck", "num-traits", ] @@ -1443,9 +1443,9 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266ca6be6004fd1b2a768023b1cb0afbf7af0cbffaba19af25c5792d44e74784" +checksum = "89b92788dec9d0c1bed849a1b83f01b2ee12819bf04a79c90f68e4173f7b5ba2" dependencies = [ "arrayvec", "bit-vec", diff --git a/src/canvas.rs b/src/canvas.rs index 2017259..c9216c1 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -6,9 +6,9 @@ use raw_window_handle::HasRawWindowHandle; use crate::{ io::{Key, Scroll}, sprite::{Sprite, TextureSprite, TextSprite, ShapeSprite}, - texture::Texture, + texture::TextureHandle, utils::{Size, Pixel, Position, Color}, - renderer::WgpuRenderer, + renderer::{WgpuRenderer, Texture}, }; use std::{ @@ -16,6 +16,7 @@ use std::{ cell::RefCell, }; + //--Application trait------------------------------------------------------------------------------- pub trait Application { @@ -28,13 +29,13 @@ pub trait Application { pub struct Canvas { renderer: WgpuRenderer, clear_color: Pixel, - default_texture: Texture, + default_texture: TextureHandle, } impl Canvas { - pub async fn create(window: &W, size: Size) - -> Result + pub async fn create(window: &W, size: Size) + -> Result { let mut renderer = WgpuRenderer::create(window, size).await?; renderer.clear(Color::WHITE); @@ -58,7 +59,8 @@ impl Canvas { } }; - Texture::create(renderer.create_gpu_texture(&rgba, img_size)) + let texture = Texture::create(&renderer, &rgba, img_size); + TextureHandle::from_texture(texture) }; Ok(Self { @@ -71,10 +73,10 @@ impl Canvas { //--Create functions-- pub fn create_texture_sprite(&mut self, size: Size) -> TextureSprite { TextureSprite::create( - self.default_texture.clone(), - size, + self.default_texture.clone(), + size, + &self.renderer, self.renderer.create_texture_mesh(), - self.renderer.create_matrix_uniform(), ) } @@ -86,18 +88,18 @@ impl Canvas { unimplemented!(); } - pub fn create_texture(&mut self, _size: Size, _background: Option) - -> Rc> + pub fn create_texture(&mut self, _size: Size, _background: Option) + -> Rc> { unimplemented!(); } - pub fn create_texture_from_file(&mut self, + pub fn create_texture_from_file(&mut self, file: &'static str, _offset: Option, _size: Option, - _background: Option) - -> Result + _background: Option) + -> Result { use image::{ io::Reader, @@ -117,7 +119,8 @@ impl Canvas { } }; - Ok(Texture::create(self.renderer.create_gpu_texture(&rgba, img_size))) + let texture = Texture::create(&self.renderer, &rgba, img_size); + Ok(TextureHandle::from_texture(texture)) } pub fn create_texture_from_bytes(&mut self, @@ -137,7 +140,7 @@ impl Canvas { pub fn draw(&mut self, sprite: &mut S) { //update texture - self.renderer.render(sprite); + sprite.render(&mut self.renderer); } pub fn set_clear_color(&mut self, color: Pixel) { diff --git a/src/lib.rs b/src/lib.rs index fac16f5..65ba04c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,8 +15,8 @@ mod renderer; use utils::Size; -pub fn run_canvas(title: &'static str, size: Size, _: A) -> ! -where +pub fn run_canvas(title: &'static str, size: Size, _: A) -> ! +where S: 'static + Sized, A: 'static + Application, { @@ -77,7 +77,7 @@ where canvas.clear(); let _ = canvas.update(); }, - + _ => () }; }); diff --git a/src/main.rs b/src/main.rs index c81b026..97285e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,15 +2,15 @@ use log::{debug, error, info, trace, warn}; use canvas::{ - Application, + Application, Canvas, - texture::Texture, + texture::TextureHandle, sprite::TextureSprite, utils::Position, }; use std::{ - time::Instant, + time::Instant, arch::x86_64::_mm_sub_epi8, }; fn setup_logger() -> Result<(), fern::InitError> { @@ -32,15 +32,14 @@ fn setup_logger() -> Result<(), fern::InitError> { message )) }) - .level(log::LevelFilter::Debug) + .level(log::LevelFilter::Info) .chain(std::io::stdout()) .chain(fern::log_file("output.log")?) - .apply()?; - Ok(()) + .apply()?; Ok(()) } struct ExampleState { - pub texture: Texture, + pub texture: TextureHandle, pub tex_sprite: TextureSprite, pub sub_sprite: TextureSprite, pub last_instant: Instant, @@ -54,16 +53,14 @@ struct ExampleApp {} impl Application for ExampleApp { fn init(canvas: &mut Canvas) -> Result { - use canvas::{ - utils::{Size}, - }; + use canvas::utils::Size; //// 20 x 20 sprite of a picture let texture = canvas.create_texture_from_file("assets/camel.jpg", None, None, None) .unwrap(); let mut tex_sprite = canvas.create_texture_sprite(Size {w: 1280, h: 720}); tex_sprite.set_texture(texture.clone(), Some(Position {x: 0, y: 0}), 1.0); - + let mut sub_sprite = canvas.create_texture_sprite(Size {w: 200, h: 200}); sub_sprite.set_texture(texture.clone(), Some(Position {x: 350, y: 0}), 1.0); @@ -84,9 +81,7 @@ impl Application for ExampleApp { } fn tick(state: &mut ExampleState, canvas: &mut Canvas) -> Result<(), &'static str> { - use canvas::{ - sprite::Sprite, - }; + use canvas::sprite::Sprite; let now = Instant::now(); let elapsed = now.duration_since(state.last_instant).as_millis(); @@ -99,10 +94,11 @@ impl Application for ExampleApp { 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(), + state.sub_sprite.set_texture(state.texture.clone(), Some(Position {x: state.last_offset, y: 0}), 1.0); state.sub_sprite.set_position(state.last_pos); state.sub_sprite.set_rotation(state.last_rot); + //state.sub_sprite.set_scale(state.last_rot/1000.0); // inputs @@ -136,7 +132,7 @@ impl Application for ExampleApp { //bas_sprite.set_color(Color::WHITE); //canvas.draw(&bas_sprite); - + //// scaled sprite of a manually drawed texture //let texture = canvas.create_texture(Size {w: 20, h: 20}, Some(Color::WHITE)); @@ -158,8 +154,8 @@ impl Application for ExampleApp { //txt_sprite.set_alpha(255); //txt_sprite.set_scale(0.5); //canvas.draw(&txt_sprite); - - + + canvas.draw(&mut state.tex_sprite); canvas.draw(&mut state.sub_sprite); canvas.update(); diff --git a/src/renderer.rs b/src/renderer.rs index fdfc1a0..ff8e644 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -5,30 +5,33 @@ use raw_window_handle::HasRawWindowHandle; use wgpu; use crate::{ - sprite::{Sprite, MATRIX_SIZE}, - texture::{GpuTexture}, utils::{Pixel, Size}, }; -use std::{ - rc::Rc, - cell::RefCell, -}; +pub mod utils; +use utils::{TextureVertex, Mesh, GpuMesh}; + +mod texture; +pub use texture::Texture as Texture; + +mod uniform; +pub use uniform::Uniform as Uniform; + +mod matrix; +pub use matrix::ModelMatrix as ModelMatrix; //--Renderer struct--------------------------------------------------------------------------------- pub struct WgpuRenderer { surface: wgpu::Surface, - device: wgpu::Device, - queue: wgpu::Queue, - config: wgpu::SurfaceConfiguration, + pub device: wgpu::Device, + pub queue: wgpu::Queue, + pub config: wgpu::SurfaceConfiguration, surface_size: Size, - output: Option, + pub output: Option, - matrix_bind_group_layout: wgpu::BindGroupLayout, - - texture_bind_group_layout: wgpu::BindGroupLayout, - texture_render_pipeline: wgpu::RenderPipeline, + pub matrix_layout: wgpu::BindGroupLayout, + pub texture_layout: wgpu::BindGroupLayout, #[allow(dead_code)] shape_render_pipeline: wgpu::RenderPipeline, @@ -38,8 +41,8 @@ pub struct WgpuRenderer { impl WgpuRenderer { - pub async fn create(window: &W, size: Size) - -> Result + pub async fn create(window: &W, size: Size) + -> Result { if size.w == 0 || size.h == 0 { return Err("window has zero as at least one of its dimensions"); @@ -92,118 +95,20 @@ impl WgpuRenderer { 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: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - // This should match the filterable field of the - // corresponding Texture entry above. - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - label: Some("texture_bind_group_layout"), - }); - - let texture_render_pipeline = { - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("texture shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("shaders/texture.wgsl").into()), - }); - - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("texture render pipeline layout"), - bind_group_layouts: &[ - &matrix_bind_group_layout, - &texture_bind_group_layout, - ], - push_constant_ranges: &[], - }); - - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("texture render pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[ - TextureVertex::desc(), - ], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format: config.format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - 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, - // Requires Features::DEPTH_CLIP_CONTROL - unclipped_depth: false, - // Requires Features::CONSERVATIVE_RASTERIZATION - conservative: false, - }, - depth_stencil: None, - // multisampling, we don't need it - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - }) - }; + let matrix_layout = utils::new_matrix_layout(&device); + let texture_layout = utils::new_texture_layout(&device); let shape_render_pipeline = { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("shape shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("shaders/shape.wgsl").into()), + source: wgpu::ShaderSource::Wgsl(include_str!("sprite/shaders/shape.wgsl").into()), }); let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("shape render pipeline layout"), bind_group_layouts: &[ - &matrix_bind_group_layout, + &matrix_layout, ], push_constant_ranges: &[], }); @@ -230,24 +135,24 @@ impl WgpuRenderer { primitive: wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::TriangleList, strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, + front_face: wgpu::FrontFace::Ccw, cull_mode: Some(wgpu::Face::Back), // Setting this to anything other than Fill requires - // Features::NON_FILL_POLYGON_MODE + // Features::NON_FILL_POLYGON_MODE polygon_mode: wgpu::PolygonMode::Fill, // Requires Features::DEPTH_CLIP_CONTROL unclipped_depth: false, // Requires Features::CONSERVATIVE_RASTERIZATION conservative: false, }, - depth_stencil: None, + depth_stencil: None, // multisampling, we don't need it multisample: wgpu::MultisampleState { count: 1, mask: !0, alpha_to_coverage_enabled: false, }, - multiview: None, + multiview: None, }) }; @@ -275,88 +180,15 @@ impl WgpuRenderer { config, surface_size, output, - matrix_bind_group_layout, - texture_bind_group_layout, - texture_render_pipeline, + matrix_layout, + texture_layout, shape_render_pipeline, quad_mesh, }) } - 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_matrix_uniform(&self) -> GpuUniform { - GpuUniform::create(&self.device, &self.matrix_bind_group_layout) - } - - pub fn render(&mut self, sprite: &mut S) { - - let output = match &self.output { - Some(out) => out, - None => { - self.create_output(); - &self.output.as_ref().unwrap() - }, - }; - - //TODO move that to output struct ? - let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default()); - - match sprite.render_data() { - 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); - } - - if gpu_mesh.is_synced() == false { - gpu_mesh.update(&mut self.queue); - } - - let mut encoder = self.device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Render Encoder"), - }); - - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Render Pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, store: true, }, - })], - depth_stencil_attachment: None, - }); - - render_pass.set_pipeline(&self.texture_render_pipeline); - 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, 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); - self.queue.submit(std::iter::once(encoder.finish())); - }, - _ => (), - } - + GpuMesh::create(&self.device, TEXTURE_QUAD) } pub fn clear(&mut self, color: Pixel) { @@ -396,7 +228,7 @@ impl WgpuRenderer { if size.w == 0 || size.h == 0 { panic!("window has zero as at least one of its dimensions"); } - + self.surface_size = size; //self.aspect_matrix = { // use cgmath::{Basis3, Rotation3, Deg}; @@ -421,7 +253,7 @@ impl WgpuRenderer { } } - fn create_output(&mut self) { + pub fn create_output(&mut self) { self.output = Some( self.surface.get_current_texture() .map_err(|err| match err { @@ -436,162 +268,7 @@ impl WgpuRenderer { } } -//--GpuMesh struct---------------------------------------------------------------------------------- -pub struct GpuMesh -where - V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable, -{ - vertex_buffer: wgpu::Buffer, - index_buffer: wgpu::Buffer, - index_number: u32, - - mesh: Mesh, - is_synced: bool, -} - -impl GpuMesh -where - V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable, -{ - - pub fn create(device: &wgpu::Device, mesh: Mesh) -> Self { - use std::mem::size_of; - use wgpu_types::BufferUsages as Usages; - - let vertex_buffer = device.create_buffer( - &wgpu_types::BufferDescriptor { - label: Some("Vertex Buffer"), - size: (V_NB * size_of::()) as u64, - usage: Usages::VERTEX | Usages::COPY_DST, - mapped_at_creation: false, - } - ); - - let index_buffer = device.create_buffer( &wgpu_types::BufferDescriptor { - label: Some("Index Buffer"), - size: (I_NB * size_of::()) as u64, - usage: Usages::INDEX | Usages::COPY_DST, - mapped_at_creation: false, - } - ); - - Self { - vertex_buffer, - index_buffer, - index_number: mesh.indices.len() as u32, - mesh, - is_synced: true, - } - } - - pub fn set_mesh(&mut self, mesh: Mesh) { - - 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; - } -} - -//--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 GpuUniform, - &'a mut GpuMesh, - &'a Rc>, - )), - Shape (&'a GpuMesh), -} - //--Renderer struct utils--------------------------------------------------------------------------- -#[repr(C)] -#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -pub struct TextureVertex { - pub position: [f32; 2], - pub tex_coords: [f32; 2], -} - -impl TextureVertex { - - const ATTRIBS: [wgpu::VertexAttribute; 2] = - wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2]; - - fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &Self::ATTRIBS, - } - } -} - #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] pub struct ColorVertex { @@ -613,14 +290,6 @@ impl ColorVertex { } } -pub struct Mesh -where - V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable, -{ - pub vertices: [V; V_NB], - pub indices: [u16; I_NB], -} - pub const TEXTURE_QUAD: Mesh = Mesh { vertices: [ TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] }, diff --git a/src/renderer/matrix.rs b/src/renderer/matrix.rs new file mode 100644 index 0000000..ef825fb --- /dev/null +++ b/src/renderer/matrix.rs @@ -0,0 +1,97 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use crate:: utils::Position; + +use super::{Uniform, WgpuRenderer}; + +use cgmath::{ + Deg, + Vector2, + Matrix3, +}; + +pub const MATRIX_SIZE: usize = 48;//std::mem::size_of::>(); + +//--ModelMatrix struct------------------------------------------------------------------------------ +pub struct ModelMatrix { + position: Position, + rotation: Deg, + scale: f32, + + uniform: Uniform, + is_synced: bool, +} + +impl ModelMatrix { + + pub fn new(renderer: &WgpuRenderer, + pos: Position, + rot: f32, + scale: f32) + -> Self + { + + Self { + position: pos, + rotation: Deg (rot), + scale, + + uniform: Uniform::create(renderer, &renderer.matrix_layout), + is_synced: false, + } + } + + pub fn default(renderer: &WgpuRenderer) -> Self { + Self::new(renderer, Position::origin(), 0.0, 1.0) + } + + pub fn set_position(&mut self, pos: Position) { + self.position = pos; + self.is_synced = false; + } + + pub fn set_rotation(&mut self, rot: f32) { + self.rotation = Deg (rot); + self.is_synced = false; + } + + pub fn set_scale(&mut self, scale: f32) { + self.scale = scale; + self.is_synced = false; + } + + pub fn get_uniform(&mut self) -> &mut Uniform { + use cgmath::{Basis3, Rotation3}; + + if self.is_synced == false { + + let pos_vec = Vector2 { + x: self.position.x as f32, + y: self.position.y as f32, + }; + 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); + + 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; + } + + &mut self.uniform + } +} + diff --git a/src/renderer/texture.rs b/src/renderer/texture.rs new file mode 100644 index 0000000..cefa31b --- /dev/null +++ b/src/renderer/texture.rs @@ -0,0 +1,98 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use crate::{ + utils::{Pixel, Size}, + renderer::WgpuRenderer, +}; + +pub struct Texture { + pub texture: wgpu::Texture, + pub bind_group: wgpu::BindGroup, + + pub buffer: Vec, + pub size: Size, + pub is_synced: bool, +} + +impl Texture { + + pub fn create(renderer: &WgpuRenderer, + buffer: &[u8], + size: Size) + -> Self + { + + let texture = renderer.device.create_texture( + &wgpu::TextureDescriptor { + label: None, + size: size.into(), + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + } + ); + + let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + let sampler = renderer.device.create_sampler( + &wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::Repeat, + address_mode_v: wgpu::AddressMode::Repeat, + address_mode_w: wgpu::AddressMode::Repeat, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + ..Default::default() + } + ); + + let bind_group = renderer.device.create_bind_group( + &wgpu::BindGroupDescriptor { + layout: &renderer.texture_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&sampler), + } + ], + label: Some("texture_bind_group"), + } + ); + + Self { + texture, + bind_group, + buffer: Vec::from(bytemuck::cast_slice(buffer)), + size, + is_synced: false, + } + } + + pub fn update(&mut self, queue: &wgpu::Queue) { + + queue.write_texture( + wgpu::ImageCopyTexture { + aspect: wgpu::TextureAspect::All, + texture: &self.texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + }, + bytemuck::cast_slice(&self.buffer), + wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: std::num::NonZeroU32::new(4 * self.size.w), + rows_per_image: std::num::NonZeroU32::new(self.size.h), + }, + self.size.into(), + ); + + self.is_synced = true; + } +} + diff --git a/src/renderer/uniform.rs b/src/renderer/uniform.rs new file mode 100644 index 0000000..c72f308 --- /dev/null +++ b/src/renderer/uniform.rs @@ -0,0 +1,62 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use super::WgpuRenderer; + +//--Uniform struct---------------------------------------------------------------------------------- +pub struct Uniform { + buffer: wgpu::Buffer, + bind_group: wgpu::BindGroup, + data: [u8; S], + is_synced: bool, +} + +impl Uniform { + + pub fn create(renderer: &WgpuRenderer, layout: &wgpu::BindGroupLayout) -> Self { + + let buffer = renderer.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 = renderer.device.create_bind_group(&wgpu::BindGroupDescriptor { + layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: buffer.as_entire_binding(), + } + ], + label: Some("uniform_bind_group"), + }); + + Self { + buffer, + 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.buffer, 0, &self.data); + self.is_synced = true; + } + + &self.bind_group + } +} + diff --git a/src/renderer/utils.rs b/src/renderer/utils.rs new file mode 100644 index 0000000..a62c3e2 --- /dev/null +++ b/src/renderer/utils.rs @@ -0,0 +1,164 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +//--Bind group layouts------------------------------------------------------------------------------ + +/// Creates the layout bind_group used by all matrixes of the renderer +pub fn new_matrix_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout { + + 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"), + }) +} + +/// Crates the layout bind group used by all textures of the renderer +pub fn new_texture_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout { + + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + // This should match the filterable field of the + // corresponding Texture entry above. + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + label: Some("texture_bind_group_layout"), + }) +} + +//--TextureVertex struct---------------------------------------------------------------------------- +// +#[repr(C)] +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +pub struct TextureVertex { + pub position: [f32; 2], + pub tex_coords: [f32; 2], +} + +impl TextureVertex { + + const ATTRIBS: [wgpu::VertexAttribute; 2] = + wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2]; + + pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { + wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &Self::ATTRIBS, + } + } +} + +//--Mesh struct------------------------------------------------------------------------------------- +pub struct Mesh +where + V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable, +{ + pub vertices: [V; V_NB], + pub indices: [u16; I_NB], +} + +//--GpuMesh struct---------------------------------------------------------------------------------- +pub struct GpuMesh +where + V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable, +{ + vertex_buffer: wgpu::Buffer, + index_buffer: wgpu::Buffer, + index_number: u32, + + mesh: Mesh, + is_synced: bool, +} + +impl GpuMesh +where + V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable, +{ + + pub fn create(device: &wgpu::Device, mesh: Mesh) -> Self { + use std::mem::size_of; + use wgpu_types::BufferUsages as Usages; + + let vertex_buffer = device.create_buffer( + &wgpu_types::BufferDescriptor { + label: Some("Vertex Buffer"), + size: (V_NB * size_of::()) as u64, + usage: Usages::VERTEX | Usages::COPY_DST, + mapped_at_creation: false, + } + ); + + let index_buffer = device.create_buffer( &wgpu_types::BufferDescriptor { + label: Some("Index Buffer"), + size: (I_NB * size_of::()) as u64, + usage: Usages::INDEX | Usages::COPY_DST, + mapped_at_creation: false, + } + ); + + Self { + vertex_buffer, + index_buffer, + index_number: mesh.indices.len() as u32, + mesh, + is_synced: true, + } + } + + pub fn set_mesh(&mut self, mesh: Mesh) { + + 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 fn get_vertex_buffer_slice(&self) -> wgpu::BufferSlice { + self.vertex_buffer.slice(..) + } + + pub fn get_index_buffer_slice(&self) -> wgpu::BufferSlice { + self.index_buffer.slice(..) + } + + pub fn get_index_number(&self) -> u32 { + self.index_number + } +} + diff --git a/src/sprite.rs b/src/sprite.rs index 787b956..654b736 100644 --- a/src/sprite.rs +++ b/src/sprite.rs @@ -2,232 +2,24 @@ use log::{debug, error, info, trace, warn}; use crate::{ - renderer::{Mesh, GpuMesh, RenderData, TextureVertex, GpuUniform}, + renderer::WgpuRenderer, shape::Shape, - texture::Texture, utils::{Pixel, Position, Size}, }; -use cgmath::{ - Deg, - Vector2, - Matrix3, - Matrix4, -}; +use cgmath::Matrix4; + +mod texture_sprite; +pub use texture_sprite::TextureSprite; //--Sprite trait------------------------------------------------------------------------------------ pub trait Sprite { - + fn set_position(&mut self, pos: Position); fn set_rotation(&mut self, rot: f32); fn set_alpha(&mut self, alpha: u8); fn set_scale(&mut self, scale: f32); - fn render_data(&mut self) -> RenderData; -} - -//--ModelMatrix struct------------------------------------------------------------------------------ -pub struct ModelMatrix { - position: Position, - rotation: Deg, - scale: f32, - - uniform: GpuUniform, - is_synced: bool, -} - -impl ModelMatrix { - - pub fn new(pos: Position, - rot: f32, - scale: f32, - uniform: GpuUniform) - -> Self - { - - Self { - position: pos, - rotation: Deg (rot), - scale, - - uniform, - is_synced: false, - } - } - - pub fn default(uniform: GpuUniform) -> Self { - Self::new(Position::origin(), 0.0, 1.0, uniform) - } - - pub fn set_position(&mut self, pos: Position) { - self.position = pos; - self.is_synced = false; - } - - pub fn set_rotation(&mut self, rot: f32) { - self.rotation = Deg (rot); - self.is_synced = false; - } - - pub fn set_scale(&mut self, scale: f32) { - self.scale = scale; - self.is_synced = false; - } - - pub fn get_uniform(&mut self) -> &mut GpuUniform { - use cgmath::{Basis3, Rotation3}; - - if self.is_synced == false { - - let pos_vec = Vector2 { - x: self.position.x as f32, - y: self.position.y as f32, - }; - 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); - - 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; - } - - &mut self.uniform - } -} - -pub const MATRIX_SIZE: usize = 48;//std::mem::size_of::>(); - -//--TextureSprite struct---------------------------------------------------------------------------- -pub struct TextureSprite { - matrix: ModelMatrix, - gpu_mesh: GpuMesh, - inner_size: Size, //TODO move to f32 - - texture: Texture, - texture_offset: Position, - texture_scale: f32, -} - -impl TextureSprite { - - pub fn create(texture: Texture, - size: Size, - gpu_mesh: GpuMesh, - matrix_uniform: GpuUniform) - -> Self - { - let mut sprite = Self { - matrix: ModelMatrix::default(matrix_uniform), - gpu_mesh, - inner_size: size, - - texture: texture.clone(), - texture_offset: Position::origin(), - texture_scale: 1.0, - }; - - //generate proper mesh - //TODO improve that - sprite.set_texture(texture, None, 1.0); - sprite - } - - pub fn set_texture(&mut self, texture: Texture, offset: Option, scale: f32) { - - // update texture - self.texture = texture; - self.texture_scale = scale; - let size = self.texture.get_size(); - - // compute normalized coordinates - self.texture_offset = offset.unwrap_or(Position::origin()); - let x_size = self.inner_size.w as f32 / size.w as f32 * scale; - let y_size = self.inner_size.h as f32 / size.h as f32 * scale; - let x_offset = self.texture_offset.x as f32 / size.w as f32; - let y_offset = self.texture_offset.y as f32 / size.h as f32; - - // compute mesh size - let w = self.inner_size.w as f32; - let h = self.inner_size.h as f32; - - // generate new sprite mesh - let mesh = Mesh { - vertices: [ - TextureVertex { - position: [-w, -h], - tex_coords: [x_offset , y_offset + y_size], - }, - TextureVertex { - position: [ w, -h], - tex_coords: [x_offset + x_size, y_offset + y_size], - }, - TextureVertex { - position: [ w, h], - tex_coords: [x_offset + x_size, y_offset ], - }, - TextureVertex { - position: [-w, h], - tex_coords: [x_offset , y_offset ], - }, - ], - indices: [ - 0, 1, 2, - 0, 2, 3, - ], - }; - - self.gpu_mesh.set_mesh(mesh); - } - - pub fn set_pixel(&mut self, pos: Position, pix: Pixel) { - //TODO check pos ? - self.texture.set_pixel(self.texture_offset + pos, pix); - } - - pub fn for_each(&mut self, func: F) { - //TODO check pos ? - //TODO take scale into account - self.texture.for_each_in_area(func, self.texture_offset, self.inner_size); - } -} - -impl Sprite for TextureSprite { - - fn set_position(&mut self, pos: Position) { - self.matrix.set_position(pos); - } - - fn set_rotation(&mut self, rot: f32) { - self.matrix.set_rotation(rot); - } - - fn set_alpha(&mut self, _alpha: u8) { - unimplemented!(); - } - - fn set_scale(&mut self, scale: f32) { - self.matrix.set_scale(scale); - } - - fn render_data(&mut self) -> RenderData { - RenderData::Texture (( - self.matrix.get_uniform(), - &mut self.gpu_mesh, - &self.texture.gpu_texture(), - )) - } + fn render(&mut self, renderer: &mut WgpuRenderer); } //--TextSprite struct------------------------------------------------------------------------------- @@ -250,7 +42,7 @@ impl TextSprite { } impl Sprite for TextSprite { - + fn set_position(&mut self, _pos: Position) { unimplemented!(); } @@ -267,7 +59,7 @@ impl Sprite for TextSprite { unimplemented!(); } - fn render_data(&mut self) -> RenderData { + fn render(&mut self, _renderer: &mut WgpuRenderer) { todo!(); } } @@ -292,7 +84,7 @@ impl ShapeSprite { } impl Sprite for ShapeSprite { - + fn set_position(&mut self, _pos: Position) { unimplemented!(); } @@ -308,8 +100,8 @@ impl Sprite for ShapeSprite { fn set_scale(&mut self, _scale: f32) { unimplemented!(); } - - fn render_data(&mut self) -> RenderData { + + fn render(&mut self, _renderer: &mut WgpuRenderer) { todo!(); } } diff --git a/src/shaders/shape.wgsl b/src/sprite/shaders/shape.wgsl similarity index 100% rename from src/shaders/shape.wgsl rename to src/sprite/shaders/shape.wgsl diff --git a/src/shaders/test_shader.wgsl b/src/sprite/shaders/test_shader.wgsl similarity index 100% rename from src/shaders/test_shader.wgsl rename to src/sprite/shaders/test_shader.wgsl diff --git a/src/shaders/texture.wgsl b/src/sprite/shaders/texture.wgsl similarity index 100% rename from src/shaders/texture.wgsl rename to src/sprite/shaders/texture.wgsl diff --git a/src/sprite/texture_sprite.rs b/src/sprite/texture_sprite.rs new file mode 100644 index 0000000..1b9eb0b --- /dev/null +++ b/src/sprite/texture_sprite.rs @@ -0,0 +1,271 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use wgpu; + +use crate::{ + texture::TextureHandle, + renderer::{ + utils::{TextureVertex, GpuMesh}, + WgpuRenderer, ModelMatrix, + }, + utils::{Size, Position, Pixel}, +}; + +use super::Sprite; + +use std::cell::RefCell; + +thread_local!(static PIPELINE : RefCell> = RefCell::new(None)); + +//--TextureSprite struct---------------------------------------------------------------------------- +pub struct TextureSprite { + matrix: ModelMatrix, + gpu_mesh: GpuMesh, + inner_size: Size, //TODO move to f32 + + texture: TextureHandle, + texture_offset: Position, + texture_scale: f32, +} + +impl TextureSprite { + + pub fn create(texture: TextureHandle, + size: Size, + renderer: &WgpuRenderer, + gpu_mesh: GpuMesh) + -> Self + { + // initialize pipeline if needed + PIPELINE.with(|cell| { + if cell.borrow().is_none() { + cell.replace(Some(initialize_pipeline(renderer))); + } + }); + + let mut sprite = Self { + matrix: ModelMatrix::default(renderer), + gpu_mesh, + inner_size: size, + + texture: texture.clone(), + texture_offset: Position::origin(), + texture_scale: 1.0, + }; + + //generate proper mesh + //TODO improve that + sprite.set_texture(texture, None, 1.0); + sprite + } + + pub fn set_texture(&mut self, texture: TextureHandle, offset: Option, scale: f32) { + use crate::renderer::utils::Mesh; + + // update texture + self.texture = texture; + self.texture_scale = scale; + let size = self.texture.get_size(); + + // compute normalized coordinates + self.texture_offset = offset.unwrap_or(Position::origin()); + let x_size = self.inner_size.w as f32 / size.w as f32 * scale; + let y_size = self.inner_size.h as f32 / size.h as f32 * scale; + let x_offset = self.texture_offset.x as f32 / size.w as f32; + let y_offset = self.texture_offset.y as f32 / size.h as f32; + + // compute mesh size + let w = self.inner_size.w as f32; + let h = self.inner_size.h as f32; + + // generate new sprite mesh + let mesh = Mesh { + vertices: [ + TextureVertex { + position: [-w, -h], + tex_coords: [x_offset , y_offset + y_size], + }, + TextureVertex { + position: [ w, -h], + tex_coords: [x_offset + x_size, y_offset + y_size], + }, + TextureVertex { + position: [ w, h], + tex_coords: [x_offset + x_size, y_offset ], + }, + TextureVertex { + position: [-w, h], + tex_coords: [x_offset , y_offset ], + }, + ], + indices: [ + 0, 1, 2, + 0, 2, 3, + ], + }; + + self.gpu_mesh.set_mesh(mesh); + } + + pub fn set_pixel(&mut self, pos: Position, pix: Pixel) { + //TODO check pos ? + self.texture.set_pixel(self.texture_offset + pos, pix); + } + + pub fn for_each(&mut self, func: F) { + //TODO check pos ? + //TODO take scale into account + self.texture.for_each_in_area(func, self.texture_offset, self.inner_size); + } + + pub fn render(&mut self) + { + todo!(); + } +} + +fn initialize_pipeline(renderer: &WgpuRenderer) -> wgpu::RenderPipeline { + + let shader = renderer.device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("texture shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("shaders/texture.wgsl").into()), + }); + + let render_pipeline_layout = + renderer.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("texture render pipeline layout"), + bind_group_layouts: &[ + &renderer.matrix_layout, + &renderer.texture_layout, + ], + push_constant_ranges: &[], + }); + + renderer.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("texture render pipeline"), + layout: Some(&render_pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[ + TextureVertex::desc(), + ], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + format: renderer.config.format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + 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, + // Requires Features::DEPTH_CLIP_CONTROL + unclipped_depth: false, + // Requires Features::CONSERVATIVE_RASTERIZATION + conservative: false, + }, + depth_stencil: None, + // multisampling, we don't need it + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + }) +} + +impl Sprite for TextureSprite { + + fn set_position(&mut self, pos: Position) { + self.matrix.set_position(pos); + } + + fn set_rotation(&mut self, rot: f32) { + self.matrix.set_rotation(rot); + } + + fn set_alpha(&mut self, _alpha: u8) { + unimplemented!(); + } + + fn set_scale(&mut self, scale: f32) { + self.matrix.set_scale(scale); + } + + fn render(&mut self, renderer: &mut WgpuRenderer) { + + let output = match &renderer.output { + Some(out) => out, + None => { + renderer.create_output(); + &renderer.output.as_ref().unwrap() + }, + }; + + //TODO move that to output struct ? + let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default()); + + let texture = &self.texture.texture(); + + if texture.borrow().is_synced == false { + //TODO modify that + texture.borrow_mut().update(&mut renderer.queue); + } + + if self.gpu_mesh.is_synced() == false { + self.gpu_mesh.update(&mut renderer.queue); + } + + let mut encoder = renderer.device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), + }); + + PIPELINE.with(|pipeline| { + let pipeline = pipeline.borrow(); + let texture_bind_group = &texture.borrow().bind_group; + + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, store: true, }, + })], + depth_stencil_attachment: None, + }); + + render_pass.set_pipeline(pipeline.as_ref().unwrap()); + render_pass.set_vertex_buffer(0, self.gpu_mesh.get_vertex_buffer_slice()); + render_pass.set_index_buffer(self.gpu_mesh.get_index_buffer_slice(), + wgpu::IndexFormat::Uint16); + render_pass.set_bind_group(0, + self.matrix.get_uniform().get_bind_group(&renderer.queue), + &[]); + 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..self.gpu_mesh.get_index_number(), 0, 0..1); + + drop(render_pass); + }); + + renderer.queue.submit(std::iter::once(encoder.finish())); + } +} + diff --git a/src/texture.rs b/src/texture.rs index be2fcc7..56ebf5b 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -1,160 +1,73 @@ #[allow(unused_imports)] use log::{debug, error, info, trace, warn}; -use crate::utils::{Pixel, Position, Size}; +use crate::{ + utils::{Pixel, Position, Size}, + renderer::Texture, +}; use std::{ rc::Rc, cell::RefCell, }; -//--GpuTexture struct------------------------------------------------------------------------------- -pub struct GpuTexture { - pub texture: wgpu::Texture, - pub bind_group: wgpu::BindGroup, - - pub buffer: Vec, - pub size: Size, - pub is_synced: bool, -} - -impl GpuTexture { - - pub fn create(device: &wgpu::Device, - layout: &wgpu::BindGroupLayout, - buffer: &[u8], - size: Size) - -> Self - { - let texture = device.create_texture( - &wgpu::TextureDescriptor { - label: None, - size: size.into(), - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, - } - ); - - let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); - let sampler = device.create_sampler( - &wgpu::SamplerDescriptor { - address_mode_u: wgpu::AddressMode::Repeat, - address_mode_v: wgpu::AddressMode::Repeat, - address_mode_w: wgpu::AddressMode::Repeat, - mag_filter: wgpu::FilterMode::Linear, - min_filter: wgpu::FilterMode::Nearest, - mipmap_filter: wgpu::FilterMode::Nearest, - ..Default::default() - } - ); - - let bind_group = device.create_bind_group( - &wgpu::BindGroupDescriptor { - layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&sampler), - } - ], - label: Some("texture_bind_group"), - } - ); - - Self { - texture, - bind_group, - buffer: Vec::from(bytemuck::cast_slice(buffer)), - size, - is_synced: false, - } - } - - pub fn update(&mut self, queue: &wgpu::Queue) { - - queue.write_texture( - wgpu::ImageCopyTexture { - aspect: wgpu::TextureAspect::All, - texture: &self.texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - }, - bytemuck::cast_slice(&self.buffer), - wgpu::ImageDataLayout { - offset: 0, - bytes_per_row: std::num::NonZeroU32::new(4 * self.size.w), - rows_per_image: std::num::NonZeroU32::new(self.size.h), - }, - self.size.into(), - ); - - self.is_synced = true; - } -} - -//--Texture struct---------------------------------------------------------------------------------- +//--TextureHandle struct---------------------------------------------------------------------------- #[derive(Clone)] -pub struct Texture { - gpu_texture: Rc>, +pub struct TextureHandle { + texture: Rc>, } -impl Texture { +impl TextureHandle { - pub fn create(gpu_texture: GpuTexture) -> Self { + pub fn from_texture(texture: Texture) -> Self { Self { - gpu_texture: Rc::new(RefCell::new(gpu_texture)), + texture: Rc::new(RefCell::new(texture)), } } pub fn set_pixel(&mut self, pos: Position, pix: Pixel) { //TODO check pos ? - let mut gpu_texture = self.gpu_texture.borrow_mut(); - let width = gpu_texture.size.w; - gpu_texture.buffer[(pos.x + pos.y*width) as usize] = pix; - gpu_texture.is_synced = false; + let mut texture = self.texture.borrow_mut(); + let width = texture.size.w; + texture.buffer[(pos.x + pos.y*width) as usize] = pix; + texture.is_synced = false; } pub fn for_each(&mut self, mut func: F) { - let mut gpu_texture = self.gpu_texture.borrow_mut(); - for pix in &mut gpu_texture.buffer { + let mut texture = self.texture.borrow_mut(); + for pix in &mut texture.buffer { func(pix); } - gpu_texture.is_synced = false; + texture.is_synced = false; } - pub fn for_each_in_area(&mut self, - mut func: F, - offset: Position, - size: Size) + pub fn for_each_in_area(&mut self, + mut func: F, + offset: Position, + size: Size) { //TODO check offset and pos ? - let mut gpu_texture = self.gpu_texture.borrow_mut(); - let width = gpu_texture.size.w; + let mut texture = self.texture.borrow_mut(); + let width = texture.size.w; for x in offset.x..(offset.x + size.w) { for y in offset.y..(offset.y + size.h) { - func(&mut gpu_texture.buffer[(x + y*width) as usize]); + func(&mut texture.buffer[(x + y*width) as usize]); } } - gpu_texture.is_synced = false; + texture.is_synced = false; } - pub fn gpu_texture(&self) -> &Rc> { + pub fn texture(&self) -> &Rc> { //TODO improve that - &self.gpu_texture + &self.texture } pub fn get_size(&self) -> Size { - self.gpu_texture.borrow().size + self.texture.borrow().size } } +