From 3a7799bc763b74287d991702a663450f26223977 Mon Sep 17 00:00:00 2001 From: Steins7 Date: Wed, 20 Jul 2022 23:54:41 +0200 Subject: [PATCH] Started implementing TextureSprite * reworked Texture API + implemented basic TextureSprite functions * minor modifications --- src/canvas.rs | 39 +++++++++++---- src/main.rs | 24 ++++----- src/renderer.rs | 93 +++++++++++++++++++++-------------- src/sprite.rs | 44 ++++++++++++++--- src/texture.rs | 128 +++++++++++++++++++++++++++++++----------------- src/utils.rs | 38 +++++++++++++- 6 files changed, 255 insertions(+), 111 deletions(-) diff --git a/src/canvas.rs b/src/canvas.rs index ce2630a..8683d81 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -28,6 +28,7 @@ pub trait Application { pub struct Canvas { renderer: WgpuRenderer, clear_color: Pixel, + default_texture: Texture, } impl Canvas { @@ -38,15 +39,38 @@ impl Canvas { let mut renderer = WgpuRenderer::create(window, size).await?; renderer.clear(Color::WHITE); + let default_texture = { + use image::{ + io::Reader, + GenericImageView, + }; + + let img = Reader::open("assets/camel.jpg") + .map_err(|_| "Failed to open default texture")? + .decode() + .map_err(|_| "Failed to decode default texture")?; + let rgba = img.to_rgba8(); + let img_size = { + let dimensions = img.dimensions(); + Size { + w: dimensions.0, + h: dimensions.1, + } + }; + + Texture::create(renderer.create_gpu_texture(&rgba, img_size)) + }; + Ok(Self { renderer, clear_color: Color::WHITE, + default_texture, }) } //--Create functions-- - pub fn create_texture_sprite(&mut self, _size: Size, _scale: f32) -> TextureSprite { - unimplemented!(); + pub fn create_texture_sprite(&mut self, _size: Size) -> TextureSprite { + TextureSprite::create(self.default_texture.clone()) } pub fn create_text_sprite(&mut self, _size: Size, _scale: f32) -> TextSprite { @@ -68,7 +92,7 @@ impl Canvas { _offset: Option, _size: Option, _background: Option) - -> Result>, &'static str> + -> Result { use image::{ io::Reader, @@ -88,9 +112,7 @@ impl Canvas { } }; - Ok(Rc::new(RefCell::new( - self.renderer.create_texture(&rgba, img_size) - ))) + Ok(Texture::create(self.renderer.create_gpu_texture(&rgba, img_size))) } pub fn create_texture_from_bytes(&mut self, @@ -107,11 +129,10 @@ impl Canvas { } //--Output functions-- -// pub fn draw(&mut self, sprite: &S) { - pub fn draw(&mut self, texture: &Texture) { + pub fn draw(&mut self, sprite: &S) { //update texture - self.renderer.render(texture); + self.renderer.render(sprite); } pub fn set_clear_color(&mut self, color: Pixel) { diff --git a/src/main.rs b/src/main.rs index 820347c..a2c7135 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use canvas::{ Application, Canvas, texture::Texture, + sprite::TextureSprite, }; use std::{ @@ -36,7 +37,8 @@ fn setup_logger() -> Result<(), fern::InitError> { } struct ExampleState { - pub texture: Rc>, + pub texture: Texture, + pub tex_sprite: TextureSprite, } struct ExampleApp {} @@ -44,15 +46,22 @@ struct ExampleApp {} impl Application for ExampleApp { fn init(canvas: &mut Canvas) -> Result { + use canvas::{ + utils::{Position, 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: 20, h: 20}); + tex_sprite.set_texture(texture.clone(), Some(Position {x: 0, y: 0})); canvas.clear(); canvas.update(); Ok(ExampleState { texture, + tex_sprite, }) } @@ -95,15 +104,7 @@ impl Application for ExampleApp { //bas_sprite.set_color(Color::WHITE); //canvas.draw(&bas_sprite); - //// 20 x 20 sprite of a picture - //let texture = canvas.create_texture_from_file( - // Position {x: 0, y: 0}, - // "texture.png", - // Some(Color::WHITE) - //); - //let mut pix_sprite = canvas.create_texture_sprite(Size {w: 20, h: 20}, 1.0); - //pix_sprite.set_texture(texture, Some(Position {x: 0, y: 0})); - //canvas.draw(&pix_sprite); + //// scaled sprite of a manually drawed texture //let texture = canvas.create_texture(Size {w: 20, h: 20}, Some(Color::WHITE)); @@ -125,7 +126,8 @@ impl Application for ExampleApp { //txt_sprite.set_alpha(255); //txt_sprite.set_scale(0.5); //canvas.draw(&txt_sprite); - canvas.draw(&state.texture.borrow()); + + canvas.draw(&state.tex_sprite); canvas.update(); Ok(()) diff --git a/src/renderer.rs b/src/renderer.rs index 36401ca..657e081 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -6,11 +6,15 @@ use wgpu; use crate::{ sprite::Sprite, - texture::Texture, + texture::{GpuTexture, Texture}, utils::{Pixel, Position, Size}, }; -use std::marker::PhantomData; +use std::{ + marker::PhantomData, + rc::Rc, + cell::RefCell, +}; //--Renderer struct--------------------------------------------------------------------------------- pub struct WgpuRenderer { @@ -37,7 +41,7 @@ impl WgpuRenderer { } let instance = wgpu::Instance::new(wgpu::Backends::all()); let surface = unsafe { instance.create_surface(window) }; - + let adapter = instance.request_adapter( &wgpu::RequestAdapterOptions { //for now, integrated GPU is enough, may be revised later @@ -226,14 +230,13 @@ impl WgpuRenderer { }) } - pub fn create_texture(&mut self, buf: &[u8], size: Size) - -> Texture + pub fn create_gpu_texture(&mut self, buf: &[u8], size: Size) + -> GpuTexture { - Texture::create(&self.device, &self.queue, &self.texture_bind_group_layout, buf, size) + GpuTexture::create(&self.device, &self.texture_bind_group_layout, buf, size) } -// pub fn render(&mut self, _sprite: &S) { - pub fn render(&mut self, texture: &Texture) { + pub fn render(&mut self, sprite: &S) { let output = match &self.output { Some(out) => out, @@ -243,34 +246,47 @@ impl WgpuRenderer { }, }; + //TODO move that to output struct ? let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default()); - 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, - }); + match sprite.render_data() { + RenderData::Texture (texture) => { + let mut texture = texture.borrow_mut(); - render_pass.set_pipeline(&self.texture_render_pipeline); - render_pass.set_vertex_buffer(0, self.texture_quad.vertex_buffer.slice(..)); - render_pass.set_index_buffer(self.texture_quad.index_buffer.slice(..), - wgpu::IndexFormat::Uint16); - render_pass.set_bind_group(0, texture.bind_group(), &[]); - render_pass.draw_indexed(0..self.texture_quad.index_number, 0, 0..1); + if texture.is_synced() == false { + texture.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, self.texture_quad.vertex_buffer.slice(..)); + render_pass.set_index_buffer(self.texture_quad.index_buffer.slice(..), + wgpu::IndexFormat::Uint16); + render_pass.set_bind_group(0, texture.bind_group(), &[]); + render_pass.draw_indexed(0..self.texture_quad.index_number, 0, 0..1); + + drop(render_pass); + self.queue.submit(std::iter::once(encoder.finish())); + }, + _ => (), } - self.queue.submit(std::iter::once(encoder.finish())); } pub fn clear(&mut self, color: Pixel) { @@ -340,7 +356,7 @@ impl WgpuRenderer { } } -struct GpuMesh { +pub struct GpuMesh { vertex_buffer: wgpu::Buffer, index_buffer: wgpu::Buffer, index_number: u32, @@ -378,10 +394,15 @@ impl GpuMesh { } } +pub enum RenderData<'a> { + Texture (&'a Rc>), + Shape (&'a GpuMesh), +} + //--Renderer struct utils--------------------------------------------------------------------------- #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -struct TextureVertex { +pub struct TextureVertex { pub position: [f32; 2], pub tex_coords: [f32; 2], } @@ -402,7 +423,7 @@ impl TextureVertex { #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -struct ColorVertex { +pub struct ColorVertex { pub position: [f32; 2], pub color: [f32; 3], } @@ -421,12 +442,12 @@ impl ColorVertex { } } -struct Mesh<'a, V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable> { +pub struct Mesh<'a, V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable> { pub vertices: &'a[V], pub indices: &'a[u16], } -const TEXTURE_QUAD: Mesh<'static, TextureVertex> = Mesh { +pub const TEXTURE_QUAD: Mesh<'static, TextureVertex> = Mesh { vertices: &[ TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] }, TextureVertex { position: [1.0, 0.0], tex_coords: [1.0, 1.0] }, diff --git a/src/sprite.rs b/src/sprite.rs index 2c81cca..5c50470 100644 --- a/src/sprite.rs +++ b/src/sprite.rs @@ -2,6 +2,7 @@ use log::{debug, error, info, trace, warn}; use crate::{ + renderer::{GpuMesh, RenderData}, shape::Shape, texture::Texture, utils::{Pixel, Position, Size}, @@ -22,31 +23,46 @@ pub trait Sprite { fn set_rotation(&mut self, rot: f32); fn set_alpha(&mut self, alpha: u8); fn set_scale(&mut self, scale: f32); + fn render_data(&self) -> RenderData; } //--TextureSprite struct---------------------------------------------------------------------------- pub struct TextureSprite { - matrix: Matrix4, - size: Size, - texture: Rc>, + position: Position, + scale: f32, + matrix: Matrix4, + texture: Texture, } impl TextureSprite { - pub fn set_texture(&mut self, _texture: Rc>, _offset: Option) { - unimplemented!(); + pub fn create(texture: Texture) -> Self { + + let position = Position::default(); + let scale = 1.0; + let matrix = Matrix4::from_scale(scale) + Matrix4::from_translation(position.into()); + Self { + position, + scale, + matrix, + texture, + } + } + + pub fn set_texture(&mut self, texture: Texture, _offset: Option) { + self.texture = texture; } pub fn set_pixel(&mut self, _pos: Position, _pix: Pixel) { - unimplemented!(); + todo!(); } pub fn iter(&self) -> Iter<'_, Pixel> { - unimplemented!(); + todo!(); } pub fn iter_mut(&mut self) -> IterMut<'_, Pixel> { - unimplemented!(); + todo!(); } } @@ -67,6 +83,10 @@ impl Sprite for TextureSprite { fn set_scale(&mut self, _scale: f32) { unimplemented!(); } + + fn render_data(&self) -> RenderData { + RenderData::Texture (&self.texture.gpu_texture()) + } } //--TextSprite struct------------------------------------------------------------------------------- @@ -104,6 +124,10 @@ impl Sprite for TextSprite { fn set_scale(&mut self, _scale: f32) { unimplemented!(); } + + fn render_data(&self) -> RenderData { + todo!(); + } } //--ShapeSprite struct------------------------------------------------------------------------------- @@ -141,5 +165,9 @@ impl Sprite for ShapeSprite { fn set_scale(&mut self, _scale: f32) { unimplemented!(); } + + fn render_data(&self) -> RenderData { + todo!(); + } } diff --git a/src/texture.rs b/src/texture.rs index 473e1c4..c85803f 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -3,32 +3,34 @@ use log::{debug, error, info, trace, warn}; use crate::utils::{Pixel, Position, Size}; -use std::slice::{Iter, IterMut}; +use std::{ + slice::{Iter, IterMut}, + rc::Rc, + cell::RefCell, +}; -//--Texture struct---------------------------------------------------------------------------------- -pub struct Texture { - texture: wgpu::Texture, - bind_group: wgpu::BindGroup, +//--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 Texture { +impl GpuTexture { pub fn create(device: &wgpu::Device, - queue: &wgpu::Queue, layout: &wgpu::BindGroupLayout, - buf: &[u8], - buf_size: Size) + buffer: &[u8], + size: Size) -> Self { - let size = wgpu::Extent3d { - width: buf_size.w, - height: buf_size.h, - depth_or_array_layers: 1, - }; let texture = device.create_texture( &wgpu::TextureDescriptor { label: None, - size, + size: size.into(), mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, @@ -37,22 +39,6 @@ impl Texture { } ); - queue.write_texture( - wgpu::ImageCopyTexture { - aspect: wgpu::TextureAspect::All, - texture: &texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - }, - &buf, - wgpu::ImageDataLayout { - offset: 0, - bytes_per_row: std::num::NonZeroU32::new(4 * buf_size.w), - rows_per_image: std::num::NonZeroU32::new(buf_size.h), - }, - size, - ); - let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let sampler = device.create_sampler( &wgpu::SamplerDescriptor { @@ -79,27 +65,79 @@ impl Texture { resource: wgpu::BindingResource::Sampler(&sampler), } ], - label: Some("diffuse_bind_group"), + label: Some("texture_bind_group"), } ); - Self { texture, bind_group } - } - - pub fn set_pixel(&mut self, _pos: Position, _pix: Pixel) { - unimplemented!(); - } - - pub fn iter(&self) -> Iter<'_, Pixel> { - unimplemented!(); - } - - pub fn iter_mut(&mut self) -> IterMut<'_, Pixel> { - unimplemented!(); + Self { + texture, + bind_group, + buffer: Vec::from(bytemuck::cast_slice(buffer)), + size, + is_synced: false, + } } pub fn bind_group(&self) -> &wgpu::BindGroup { &self.bind_group } + + pub fn is_synced(&self) -> bool { + self.is_synced + } + + 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---------------------------------------------------------------------------------- +#[derive(Clone)] +pub struct Texture { + gpu_texture: Rc>, +} + +impl Texture { + + pub fn create(gpu_texture: GpuTexture) -> Self { + + Self { + gpu_texture: Rc::new(RefCell::new(gpu_texture)), + } + } + + pub fn set_pixel(&mut self, _pos: Position, _pix: Pixel) { + todo!(); + } + + pub fn iter(&self) -> Iter<'_, Pixel> { + todo!(); + } + + pub fn iter_mut(&mut self) -> IterMut<'_, Pixel> { + todo!(); + } + + pub fn gpu_texture(&self) -> &Rc> { + //TODO improve that + &self.gpu_texture + } } diff --git a/src/utils.rs b/src/utils.rs index e3ab014..d4d628c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,12 +3,32 @@ use log::{debug, error, info, trace, warn}; use winit; +use cgmath::Vector3; + //--Position struct--------------------------------------------------------------------------------- +#[derive(Copy, Clone)] pub struct Position { pub x: u32, pub y: u32, } +impl Position { + + pub fn default() -> Self { + Position { + x: 0, + y: 0, + } + } +} + +impl From for Vector3 { + + fn from(pos: Position) -> Self { + Self::new(pos.x as f32, pos.y as f32, 0.0) + } +} + //--Size struct------------------------------------------------------------------------------------- #[derive(Copy, Clone)] pub struct Size { @@ -27,6 +47,17 @@ impl From for winit::dpi::Size { } } +impl From for wgpu::Extent3d { + + fn from(size: Size) -> Self { + wgpu::Extent3d { + width: size.w, + height: size.h, + depth_or_array_layers: 1, + } + } +} + impl From> for Size { fn from(size: winit::dpi::PhysicalSize) -> Self { @@ -38,7 +69,8 @@ impl From> for Size { } //--Pixel struct------------------------------------------------------------------------------------ -#[derive(Copy, Clone)] +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] pub struct Rgba { pub r: u8, pub g: u8, @@ -46,12 +78,14 @@ pub struct Rgba { pub a: u8, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, bytemuck::Zeroable)] pub union Pixel { flat: u32, rgba: Rgba, } +unsafe impl bytemuck::Pod for Pixel {} + impl Pixel { pub fn rgb(r: u8, g: u8, b: u8) -> Pixel {