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 0000000..1253915 Binary files /dev/null and b/assets/camel.jpg differ 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}; }