From 79b86afc2abe15e5770970915d446989b64d1a9f Mon Sep 17 00:00:00 2001 From: Steins7 Date: Fri, 15 Jul 2022 11:59:52 +0200 Subject: [PATCH] Got texture to display on quad + implemented Texture * tweaked Application API ! some work need on Texture API to mask Rc> --- Cargo.lock | 165 +++++++++++++++++--- Cargo.toml | 5 + assets/camel.jpg | Bin 0 -> 67955 bytes src/canvas.rs | 59 ++++++-- src/lib.rs | 9 +- src/main.rs | 34 ++++- src/renderer.rs | 317 +++++++++++++++++++++++++++++++-------- src/shaders/texture.wgsl | 33 ++++ src/texture.rs | 84 ++++++++++- src/utils.rs | 23 ++- 10 files changed, 620 insertions(+), 109 deletions(-) create mode 100644 assets/camel.jpg create mode 100644 src/shaders/texture.wgsl diff --git a/Cargo.lock b/Cargo.lock index 75b01df..e45b364 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "ahash" version = "0.7.6" @@ -141,6 +153,7 @@ dependencies = [ "cgmath", "chrono", "fern", + "image", "log", "pollster", "raw-window-handle", @@ -236,6 +249,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "colored" version = "1.9.3" @@ -335,6 +354,15 @@ dependencies = [ "objc", ] +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "cty" version = "0.2.2" @@ -387,6 +415,15 @@ dependencies = [ "syn", ] +[[package]] +name = "deflate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" +dependencies = [ + "adler32", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -521,9 +558,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022" [[package]] name = "hermit-abi" @@ -546,6 +583,22 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "image" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28edd9d7bc256be2502e325ac0628bde30b7001b9b52e0abe31a1a9dc2701212" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "jpeg-decoder", + "num-iter", + "num-rational", + "num-traits", + "png", +] + [[package]] name = "indexmap" version = "1.9.1" @@ -553,7 +606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", - "hashbrown 0.12.1", + "hashbrown 0.12.2", ] [[package]] @@ -580,6 +633,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jpeg-decoder" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9478aa10f73e7528198d75109c8be5cd7d15fb530238040148d5f9a22d4c5b3b" + [[package]] name = "js-sys" version = "0.3.58" @@ -694,6 +753,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.4" @@ -812,6 +880,28 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -863,9 +953,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" [[package]] name = "parking_lot" @@ -875,7 +965,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core", + "parking_lot_core 0.8.5", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.3", ] [[package]] @@ -892,6 +992,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -904,6 +1017,18 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +[[package]] +name = "png" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide", +] + [[package]] name = "pollster" version = "0.2.5" @@ -994,9 +1119,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.137" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" [[package]] name = "slotmap" @@ -1009,9 +1134,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "smithay-client-toolkit" @@ -1295,15 +1420,15 @@ dependencies = [ [[package]] name = "wgpu" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd28e7c69ffd19c02e609322e4170738ac3340e699d8adfa16f5745625e4aa8c" +checksum = "277e967bf8b7820a76852645a6bce8bbd31c32fda2042e82d8e3ea75fda8892d" dependencies = [ "arrayvec", "js-sys", "log", "naga", - "parking_lot", + "parking_lot 0.12.1", "raw-window-handle", "smallvec", "wasm-bindgen", @@ -1316,9 +1441,9 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb155661d02bf104303589fbf9206fa971c80dbb6d4763e66879253bd0a072c" +checksum = "266ca6be6004fd1b2a768023b1cb0afbf7af0cbffaba19af25c5792d44e74784" dependencies = [ "arrayvec", "bit-vec", @@ -1329,7 +1454,7 @@ dependencies = [ "fxhash", "log", "naga", - "parking_lot", + "parking_lot 0.12.1", "profiling", "raw-window-handle", "smallvec", @@ -1341,9 +1466,9 @@ dependencies = [ [[package]] name = "wgpu-hal" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9f9cb367209e2ad214afa8d823348334994dc1579f4a521d53a3bc4d0848e73" +checksum = "bef50e48812c7eb958fa52d28a912f8b77c96453ebab21c72b01cdda61d3e65d" dependencies = [ "android_system_properties", "arrayvec", @@ -1366,7 +1491,7 @@ dependencies = [ "metal", "naga", "objc", - "parking_lot", + "parking_lot 0.12.1", "profiling", "range-alloc", "raw-window-handle", @@ -1482,7 +1607,7 @@ dependencies = [ "ndk-glue", "ndk-sys", "objc", - "parking_lot", + "parking_lot 0.11.2", "percent-encoding", "raw-window-handle", "smithay-client-toolkit", diff --git a/Cargo.toml b/Cargo.toml index 0b70a6d..ab3be54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,8 @@ raw-window-handle = "^0.4.3" wgpu = "^0.13.0" bytemuck = { version = "1.4", features = [ "derive" ] } +[dependencies.image] +version = "^0.24.2" +default-features = false +features = ["png", "jpeg"] + diff --git a/assets/camel.jpg b/assets/camel.jpg new file mode 100644 index 0000000000000000000000000000000000000000..12539158ab26f949c0890a8687efa39ee1824187 GIT binary patch literal 67955 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 z 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

h0kbL!fC2!@*!NpQk1&+pFEeYt)WzvJCtHVDI- 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 literal 0 HcmV?d00001 diff --git a/src/canvas.rs b/src/canvas.rs index 3ec1c01..ce2630a 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -17,11 +17,11 @@ use std::{ }; //--Application trait------------------------------------------------------------------------------- -pub trait Application { +pub trait Application { - fn init(&mut self, canvas: &mut Canvas) -> Result<(), &'static str>; + fn init(canvas: &mut Canvas) -> Result; - fn tick(&mut self, canvas: &mut Canvas) -> Result<(), &'static str>; + fn tick(state: &mut S, canvas: &mut Canvas) -> Result<(), &'static str>; } //--Canvas struct----------------------------------------------------------------------------------- @@ -35,7 +35,8 @@ impl Canvas { pub async fn create(window: &W, size: Size) -> Result { - let renderer = WgpuRenderer::create(window, size).await?; + let mut renderer = WgpuRenderer::create(window, size).await?; + renderer.clear(Color::WHITE); Ok(Self { renderer, @@ -63,18 +64,54 @@ impl Canvas { } pub fn create_texture_from_file(&mut self, - _pos: Position, - _file: &'static str, + file: &'static str, + _offset: Option, + _size: Option, _background: Option) - -> Rc> + -> Result>, &'static str> { - unimplemented!(); + use image::{ + io::Reader, + GenericImageView, + }; + + let img = Reader::open(file) + .map_err(|_| "Failed to open file")? + .decode() + .map_err(|_| "Failed to decode picture")?; + let rgba = img.to_rgba8(); + let img_size = { + let dimensions = img.dimensions(); + Size { + w: dimensions.0, + h: dimensions.1, + } + }; + + Ok(Rc::new(RefCell::new( + self.renderer.create_texture(&rgba, img_size) + ))) + } + + pub fn create_texture_from_bytes(&mut self, + _buf: &[u8], + _offset: Option, + _size: Option, + _background: Option) + -> Rc> + { + todo!() + // Rc::new(RefCell::new( + // self.renderer.create_texture_from_bytes(pos, buf, background).unwrap() + // )) } //--Output functions-- - pub fn draw(&mut self, sprite: &S) { +// pub fn draw(&mut self, sprite: &S) { + pub fn draw(&mut self, texture: &Texture) { + //update texture - self.renderer.render(sprite); + self.renderer.render(texture); } pub fn set_clear_color(&mut self, color: Pixel) { @@ -86,7 +123,7 @@ impl Canvas { } pub fn clear(&mut self) { - todo!(); + self.renderer.clear(self.clear_color); } //--Input functions-- diff --git a/src/lib.rs b/src/lib.rs index 9267869..63b0111 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ mod renderer; use utils::Size; -pub fn run_canvas(title: &'static str, size: Size, mut app: A) -> ! { +pub fn run_canvas>(title: &'static str, size: Size, mut app: A) -> ! { use winit::{ event_loop::EventLoop, window::WindowBuilder, @@ -35,7 +35,7 @@ pub fn run_canvas(title: &'static str, size: Size, mut let mut canvas = Canvas::create(&window, size.into()).block_on().unwrap(); // init application - app.init(&mut canvas).unwrap(); + let mut state = A::init(&mut canvas).unwrap(); canvas.update(); window.set_visible(true); @@ -66,9 +66,10 @@ pub fn run_canvas(title: &'static str, size: Size, mut }, Event::MainEventsCleared => { - let _ = app.tick(&mut canvas); - }, + let _ = A::tick(&mut state, &mut canvas); + }, Event::RedrawRequested(_) => { + canvas.clear(); let _ = canvas.update(); }, diff --git a/src/main.rs b/src/main.rs index 3c2243e..820347c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,13 @@ -use canvas::{Application, Canvas}; +use canvas::{ + Application, + Canvas, + texture::Texture, +}; + +use std::{ + rc::Rc, + cell::RefCell, +}; fn setup_logger() -> Result<(), fern::InitError> { use fern::colors::{Color, ColoredLevelConfig}; @@ -26,15 +35,28 @@ fn setup_logger() -> Result<(), fern::InitError> { Ok(()) } +struct ExampleState { + pub texture: Rc>, +} + struct ExampleApp {} -impl Application for ExampleApp { +impl Application for ExampleApp { - fn init(&mut self, _canvas: &mut Canvas) -> Result<(), &'static str> { - Ok(()) + fn init(canvas: &mut Canvas) -> Result { + + let texture = canvas.create_texture_from_file("assets/camel.jpg", None, None, None) + .unwrap(); + + canvas.clear(); + canvas.update(); + + Ok(ExampleState { + texture, + }) } - fn tick(&mut self, canvas: &mut Canvas) -> Result<(), &'static str> { + fn tick(state: &mut ExampleState, canvas: &mut Canvas) -> Result<(), &'static str> { use canvas::{ io::Key, sprite::Sprite, @@ -103,7 +125,7 @@ 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.update(); Ok(()) diff --git a/src/renderer.rs b/src/renderer.rs index 938c29f..36401ca 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -6,9 +6,12 @@ use wgpu; use crate::{ sprite::Sprite, - utils::Size, + texture::Texture, + utils::{Pixel, Position, Size}, }; +use std::marker::PhantomData; + //--Renderer struct--------------------------------------------------------------------------------- pub struct WgpuRenderer { surface: wgpu::Surface, @@ -16,8 +19,12 @@ pub struct WgpuRenderer { queue: wgpu::Queue, config: wgpu::SurfaceConfiguration, size: Size, - render_pipeline: wgpu::RenderPipeline, - quad_mesh: GpuMesh, + texture_bind_group_layout: wgpu::BindGroupLayout, + texture_render_pipeline: wgpu::RenderPipeline, + shape_render_pipeline: wgpu::RenderPipeline, + texture_quad: GpuMesh, + quad_mesh: GpuMesh, //TODO temporary, to be moved to shapes.rs + output: Option, } impl WgpuRenderer { @@ -61,27 +68,52 @@ impl WgpuRenderer { }; surface.configure(&device, &config); - let render_pipeline = { + 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("shape shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("shaders/shape.wgsl").into()), + 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("shape render pipeline layout"), - bind_group_layouts: &[], + label: Some("texture render pipeline layout"), + bind_group_layouts: &[&texture_bind_group_layout], push_constant_ranges: &[], }); device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("shape render pipeline"), + label: Some("texture render pipeline"), layout: Some(&render_pipeline_layout), vertex: wgpu::VertexState { module: &shader, entry_point: "vs_main", buffers: &[ - Vertex::desc(), + TextureVertex::desc(), ], }, fragment: Some(wgpu::FragmentState { @@ -117,7 +149,67 @@ impl WgpuRenderer { }) }; + 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()), + }); + + let render_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("shape render pipeline layout"), + bind_group_layouts: &[], + push_constant_ranges: &[], + }); + + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("shape render pipeline"), + layout: Some(&render_pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[ + ColorVertex::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: 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 texture_quad = GpuMesh::create(&device, &TEXTURE_QUAD); let quad_mesh = GpuMesh::create(&device, &QUAD); + + let output = Some(surface.get_current_texture() + .map_err(|_| "Failed to create SurfaceTexture")?); Ok(Self { surface, @@ -125,35 +217,31 @@ impl WgpuRenderer { queue, config, size, - render_pipeline, + texture_bind_group_layout, + texture_render_pipeline, + shape_render_pipeline, + texture_quad, quad_mesh, + output, }) } - pub fn render(&mut self, _sprite: &S) { - unimplemented!(); + pub fn create_texture(&mut self, buf: &[u8], size: Size) + -> Texture + { + Texture::create(&self.device, &self.queue, &self.texture_bind_group_layout, buf, size) } - pub fn resize(&mut self, size: Size) { - if size.w == 0 || size.h == 0 { - panic!("window has zero as at least one of its dimensions"); - } - self.size = size; - self.config.width = size.w; - self.config.height = size.h; - self.surface.configure(&self.device, &self.config); - } +// pub fn render(&mut self, _sprite: &S) { + pub fn render(&mut self, texture: &Texture) { - pub fn present(&mut self) { - let output = self.surface.get_current_texture() - .map_err(|err| match err { - wgpu::SurfaceError::Lost => { - warn!("Lost surface, trying resizing"); - self.resize(self.size); - self.surface.get_current_texture() - }, - _ => Err(err) - }).unwrap(); + let output = match &self.output { + Some(out) => out, + None => { + self.create_output(); + &self.output.as_ref().unwrap() + }, + }; let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default()); let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { @@ -167,39 +255,102 @@ impl WgpuRenderer { view: &view, resolve_target: None, ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.1, - g: 0.2, - b: 0.3, - a: 1.0, - }), + load: wgpu::LoadOp::Load, store: true, }, })], depth_stencil_attachment: None, }); - render_pass.set_pipeline(&self.render_pipeline); - render_pass.set_vertex_buffer(0, self.quad_mesh.vertex_buffer.slice(..)); - render_pass.set_index_buffer(self.quad_mesh.index_buffer.slice(..), + 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.draw_indexed(0..self.quad_mesh.index_number, 0, 0..1); + render_pass.set_bind_group(0, texture.bind_group(), &[]); + render_pass.draw_indexed(0..self.texture_quad.index_number, 0, 0..1); } self.queue.submit(std::iter::once(encoder.finish())); - output.present(); + } + + pub fn clear(&mut self, color: Pixel) { + + let output = match &self.output { + Some(out) => out, + None => { + self.create_output(); + &self.output.as_ref().unwrap() + }, + }; + + let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Clear Encoder"), + }); + + { + let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Clear Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(color.into()), + store: true, + }, + })], + depth_stencil_attachment: None, + }); + } + + self.queue.submit(std::iter::once(encoder.finish())); + } + + pub fn resize(&mut self, size: Size) { + if size.w == 0 || size.h == 0 { + panic!("window has zero as at least one of its dimensions"); + } + self.size = size; + self.config.width = size.w; + self.config.height = size.h; + self.surface.configure(&self.device, &self.config); + } + + pub fn present(&mut self) { + + match self.output.take() { + Some(out) => out.present(), + None => { + }//nothing to do + } + } + + fn create_output(&mut self) { + self.output = Some( + self.surface.get_current_texture() + .map_err(|err| match err { + wgpu::SurfaceError::Lost => { + warn!("Lost surface, trying resizing"); + self.resize(self.size); + self.surface.get_current_texture() + }, + _ => Err(err) + }).unwrap() + ); } } -struct GpuMesh { +struct GpuMesh { vertex_buffer: wgpu::Buffer, index_buffer: wgpu::Buffer, index_number: u32, + + phantom: PhantomData, //keep track of wich type of vertex is used } -impl GpuMesh { +impl GpuMesh { - pub fn create(device: &wgpu::Device, mesh: &Mesh) -> Self { + fn create(device: &wgpu::Device, mesh: &Mesh) -> Self { use wgpu::util::DeviceExt; let vertex_buffer = device.create_buffer_init( @@ -222,6 +373,7 @@ impl GpuMesh { vertex_buffer, index_buffer, index_number: mesh.indices.len() as u32, + phantom: PhantomData, } } } @@ -229,40 +381,87 @@ impl GpuMesh { //--Renderer struct utils--------------------------------------------------------------------------- #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -struct Vertex { +struct TextureVertex { pub position: [f32; 2], - pub color: [f32; 3], + pub tex_coords: [f32; 2], } -// lib.rs -impl Vertex { +impl TextureVertex { const ATTRIBS: [wgpu::VertexAttribute; 2] = - wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x3]; + 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, + array_stride: std::mem::size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, attributes: &Self::ATTRIBS, } } } -struct Mesh<'a> { - pub vertices: &'a[Vertex], +#[repr(C)] +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +struct ColorVertex { + pub position: [f32; 2], + pub color: [f32; 3], +} + +impl ColorVertex { + + const ATTRIBS: [wgpu::VertexAttribute; 2] = + wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x3]; + + 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, + } + } +} + +struct Mesh<'a, V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable> { + pub vertices: &'a[V], pub indices: &'a[u16], } -const QUAD: Mesh<'static> = Mesh { +const TEXTURE_QUAD: Mesh<'static, TextureVertex> = Mesh { vertices: &[ - Vertex { position: [0.0, 0.0], color: [1.0, 0.0, 0.0] }, - Vertex { position: [1.0, 0.0], color: [0.0, 1.0, 0.0] }, - Vertex { position: [1.0, 1.0], color: [0.0, 0.0, 1.0] }, - Vertex { position: [0.0, 1.0], color: [0.5, 0.5, 0.5] }, + TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] }, + TextureVertex { position: [1.0, 0.0], tex_coords: [1.0, 1.0] }, + TextureVertex { position: [1.0, 1.0], tex_coords: [1.0, 0.0] }, + TextureVertex { position: [0.0, 1.0], tex_coords: [0.0, 0.0] }, ], indices: &[ 0, 1, 2, 0, 2, 3, ], }; + +const QUAD: Mesh<'static, ColorVertex> = Mesh { + vertices: &[ + ColorVertex { position: [0.0, 0.0], color: [1.0, 0.0, 0.0] }, + ColorVertex { position: [1.0, 0.0], color: [0.0, 1.0, 0.0] }, + ColorVertex { position: [1.0, 1.0], color: [0.0, 0.0, 1.0] }, + ColorVertex { position: [0.0, 1.0], color: [0.5, 0.5, 0.5] }, + ], + indices: &[ + 0, 1, 2, + 0, 2, 3, + ], +}; + +impl From for wgpu::Color { + + fn from(pix: Pixel) -> Self { + let rgba = pix.to_rgba(); + Self { + r: (rgba.r as f64) / 255.0, + g: (rgba.g as f64) / 255.0, + b: (rgba.b as f64) / 255.0, + a: (rgba.a as f64) / 255.0, + } + } +} + diff --git a/src/shaders/texture.wgsl b/src/shaders/texture.wgsl new file mode 100644 index 0000000..7bd28b3 --- /dev/null +++ b/src/shaders/texture.wgsl @@ -0,0 +1,33 @@ +// Vertex shader + +struct VertexInput { + @location(0) position: vec2, + @location(1) tex_coords: vec2, +} + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) tex_coords: vec2, +} + +@vertex +fn vs_main(model: VertexInput) -> VertexOutput { + + var out: VertexOutput; + out.tex_coords = model.tex_coords; + out.clip_position = vec4(model.position, 0.0, 1.0); + return out; +} + +// Fragment shader + +@group(0) @binding(0) +var t_diffuse: texture_2d; +@group(0)@binding(1) +var s_diffuse: sampler; + +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4 { + return textureSample(t_diffuse, s_diffuse, in.tex_coords); +} + diff --git a/src/texture.rs b/src/texture.rs index 9b0256a..473e1c4 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -1,15 +1,91 @@ #[allow(unused_imports)] use log::{debug, error, info, trace, warn}; -use crate::utils::{Pixel, Position}; +use crate::utils::{Pixel, Position, Size}; use std::slice::{Iter, IterMut}; //--Texture struct---------------------------------------------------------------------------------- -pub struct Texture {} +pub struct Texture { + texture: wgpu::Texture, + bind_group: wgpu::BindGroup, +} impl Texture { + pub fn create(device: &wgpu::Device, + queue: &wgpu::Queue, + layout: &wgpu::BindGroupLayout, + buf: &[u8], + buf_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, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + } + ); + + 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 { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + 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("diffuse_bind_group"), + } + ); + + Self { texture, bind_group } + } + pub fn set_pixel(&mut self, _pos: Position, _pix: Pixel) { unimplemented!(); } @@ -21,5 +97,9 @@ impl Texture { pub fn iter_mut(&mut self) -> IterMut<'_, Pixel> { unimplemented!(); } + + pub fn bind_group(&self) -> &wgpu::BindGroup { + &self.bind_group + } } diff --git a/src/utils.rs b/src/utils.rs index f1c7639..e3ab014 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -39,13 +39,14 @@ impl From> for Size { //--Pixel struct------------------------------------------------------------------------------------ #[derive(Copy, Clone)] -struct Rgba { - r: u8, - g: u8, - b: u8, - a: u8, +pub struct Rgba { + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8, } +#[derive(Copy, Clone)] pub union Pixel { flat: u32, rgba: Rgba, @@ -58,6 +59,10 @@ impl Pixel { rgba: Rgba{r, g, b, a: 255}, } } + + pub fn to_rgba(&self) -> Rgba { + unsafe { self.rgba } + } } //--Color values------------------------------------------------------------------------------------ @@ -65,8 +70,12 @@ impl Pixel { pub struct Color; impl Color { - pub const WHITE: Pixel = Pixel {flat: 0x000000ff}; - pub const BLACK: Pixel = Pixel {flat: 0xffffffff}; + pub const NONE: Pixel = Pixel {flat: 0x00000000}; + pub const WHITE: Pixel = Pixel {flat: 0xffffffff}; + pub const BLACK: Pixel = Pixel {flat: 0x000000ff}; + pub const RED: Pixel = Pixel {flat: 0xff0000ff}; + pub const GREEN: Pixel = Pixel {flat: 0x00ff00ff}; + pub const BLUE: Pixel = Pixel {flat: 0x0000ffff}; }