diff --git a/dotrix_core/src/assets/mesh.rs b/dotrix_core/src/assets/mesh.rs index c12c8d1c..035e7234 100644 --- a/dotrix_core/src/assets/mesh.rs +++ b/dotrix_core/src/assets/mesh.rs @@ -354,7 +354,7 @@ mod tests { [-width, width, width], ]; - width = width / 2.0; + width /= 2.0; let verticies_test_original_2: Vec<[f32; 3]> = vec![ [-width, -width, -width], @@ -367,7 +367,7 @@ mod tests { [-width, width, width], ]; - width = width / 2.0; + width /= 2.0; let verticies_test_original_3: Vec<[u32; 3]> = vec![ [-width as u32, -width as u32, -width as u32], diff --git a/dotrix_core/src/assets/shader.rs b/dotrix_core/src/assets/shader.rs index c4e31f32..bbe61c2d 100644 --- a/dotrix_core/src/assets/shader.rs +++ b/dotrix_core/src/assets/shader.rs @@ -16,6 +16,7 @@ impl Shader { /// Loads the shader to GPU pub fn load(&mut self, renderer: &Renderer) { if !self.module.loaded() { + self.module.label = self.name.clone(); renderer.load_shader(&mut self.module, &self.code); } } diff --git a/dotrix_core/src/assets/texture.rs b/dotrix_core/src/assets/texture.rs index cee7ab0d..3315271e 100644 --- a/dotrix_core/src/assets/texture.rs +++ b/dotrix_core/src/assets/texture.rs @@ -45,4 +45,17 @@ impl Texture { pub fn unload(&mut self) { self.buffer.unload(); } + + /// Fetch data from the gpu + /// + /// This is useful textures that are altered on the gpu + /// + /// This operation is slow and should mostly be + /// used for debugging + pub fn fetch_from_gpu( + &mut self, + renderer: &mut Renderer, + ) -> impl std::future::Future, wgpu::BufferAsyncError>> { + renderer.fetch_texture(&self.buffer, [self.width, self.height, self.depth]) + } } diff --git a/dotrix_core/src/cubemap.rs b/dotrix_core/src/cubemap.rs index 39d0b075..80d0348a 100644 --- a/dotrix_core/src/cubemap.rs +++ b/dotrix_core/src/cubemap.rs @@ -32,7 +32,7 @@ impl Default for CubeMap { bottom: Id::default(), back: Id::default(), front: Id::default(), - buffer: TextureBuffer::new("CubeMap Texture Buffer"), + buffer: TextureBuffer::new_cube("CubeMap Texture Buffer"), } } } diff --git a/dotrix_core/src/renderer.rs b/dotrix_core/src/renderer.rs index 43f20d19..1bc23a10 100644 --- a/dotrix_core/src/renderer.rs +++ b/dotrix_core/src/renderer.rs @@ -84,7 +84,9 @@ impl Renderer { buffer.load(self.context(), attributes, indices, count as u32); }*/ - /// Loads the texture buffer to GPU + /// Loads the texture buffer to GPU. + /// This will recreate the texture, as a result it must be rebound on any pipelines for changes + /// to take effect pub fn load_texture<'a>( &self, texture: &mut Texture, @@ -95,11 +97,42 @@ impl Renderer { texture.load(self.context(), width, height, layers); } + /// Load data from cpu to a texture buffer on GPU + /// This is a noop if texture has not been loaded with `load_texture` + /// Unexpected results/errors occur if the dimensions differs from it dimensions at load time + pub fn update_texture<'a>( + &self, + texture: &mut Texture, + width: u32, + height: u32, + layers: &'a [&'a [u8]], + ) { + texture.update(self.context(), width, height, layers); + } + + /// This will `[update_texture]` if texture has been loaded or `[load_texture]` if not + /// the same cavets of `[update_texture]` apply in that care must be taken not to change + /// the dimensions between `load` and `update` + pub fn update_or_load_texture<'a>( + &self, + texture: &mut Texture, + width: u32, + height: u32, + layers: &'a [&'a [u8]], + ) { + texture.update_or_load(self.context(), width, height, layers); + } + /// Loads the buffer to GPU pub fn load_buffer<'a>(&self, buffer: &mut Buffer, data: &'a [u8]) { buffer.load(self.context(), data); } + /// Create a buffer on GPU without data + pub fn create_buffer(&self, buffer: &mut Buffer, size: u32, mapped: bool) { + buffer.create(self.context(), size, mapped); + } + /// Loads the sampler to GPU pub fn load_sampler(&self, sampler: &mut Sampler) { sampler.load(self.context()); @@ -110,6 +143,27 @@ impl Renderer { shader_module.load(self.context(), code); } + /// Copy a texture to a buffer + pub fn copy_texture_to_buffer( + &mut self, + texture: &Texture, + buffer: &Buffer, + extent: [u32; 3], + bytes_per_pixel: u32, + ) { + self.context_mut() + .run_copy_texture_to_buffer(texture, buffer, extent, bytes_per_pixel); + } + + /// Fetch texture from GPU + pub fn fetch_texture( + &mut self, + texture: &Texture, + dimensions: [u32; 3], + ) -> impl std::future::Future, wgpu::BufferAsyncError>> { + texture.fetch_from_gpu(dimensions, self.context_mut()) + } + /// Forces engine to reload shaders pub fn reload(&mut self) { self.dirty = true; @@ -263,6 +317,10 @@ pub fn release(mut renderer: Mut) { if renderer.cycle == 0 { renderer.cycle = 1; } + // Check for resource cleanups and mapping callbacks + if let Some(context) = renderer.context.as_ref() { + context.device.poll(wgpu::Maintain::Poll); + } } /// Resize handling system diff --git a/dotrix_core/src/renderer/bindings.rs b/dotrix_core/src/renderer/bindings.rs index 5e554636..be28b2ff 100644 --- a/dotrix_core/src/renderer/bindings.rs +++ b/dotrix_core/src/renderer/bindings.rs @@ -30,10 +30,20 @@ pub enum Binding<'a> { Uniform(&'a str, Stage, &'a Buffer), /// Texture binding Texture(&'a str, Stage, &'a Texture), + /// Cube Texture binding + TextureCube(&'a str, Stage, &'a Texture), + /// 2D Texture Array binding + TextureArray(&'a str, Stage, &'a Texture), /// 3D Texture binding Texture3D(&'a str, Stage, &'a Texture), /// Storage texture binding StorageTexture(&'a str, Stage, &'a Texture, Access), + /// Storage texture cube binding + StorageTextureCube(&'a str, Stage, &'a Texture, Access), + /// Storage 2D texture array binding + StorageTextureArray(&'a str, Stage, &'a Texture, Access), + /// Storage texture binding 3D + StorageTexture3D(&'a str, Stage, &'a Texture, Access), /// Texture sampler binding Sampler(&'a str, Stage, &'a Sampler), /// Storage binding @@ -76,25 +86,41 @@ impl<'a> BindGroup<'a> { visibility: stage.into(), ty: wgpu::BindingType::Texture { multisampled: false, - sample_type: wgpu::TextureSampleType::Float { - filterable: texture.is_filterable(), - }, + sample_type: texture.sample_type(), view_dimension: wgpu::TextureViewDimension::D2, }, count: None, }, - Binding::Texture3D(_, stage, texture) => wgpu::BindGroupLayoutEntry { + Binding::TextureCube(_, stage, texture) => wgpu::BindGroupLayoutEntry { binding: index as u32, visibility: stage.into(), ty: wgpu::BindingType::Texture { multisampled: false, - sample_type: wgpu::TextureSampleType::Float { - filterable: texture.is_filterable(), - }, + sample_type: texture.sample_type(), view_dimension: wgpu::TextureViewDimension::Cube, }, count: None, }, + Binding::TextureArray(_, stage, texture) => wgpu::BindGroupLayoutEntry { + binding: index as u32, + visibility: stage.into(), + ty: wgpu::BindingType::Texture { + multisampled: false, + sample_type: texture.sample_type(), + view_dimension: wgpu::TextureViewDimension::D2Array, + }, + count: None, + }, + Binding::Texture3D(_, stage, texture) => wgpu::BindGroupLayoutEntry { + binding: index as u32, + visibility: stage.into(), + ty: wgpu::BindingType::Texture { + multisampled: false, + sample_type: texture.sample_type(), + view_dimension: wgpu::TextureViewDimension::D3, + }, + count: None, + }, Binding::StorageTexture(_, stage, texture, access) => wgpu::BindGroupLayoutEntry { binding: index as u32, visibility: stage.into(), @@ -105,6 +131,42 @@ impl<'a> BindGroup<'a> { }, count: None, }, + Binding::StorageTextureCube(_, stage, texture, access) => { + wgpu::BindGroupLayoutEntry { + binding: index as u32, + visibility: stage.into(), + ty: wgpu::BindingType::StorageTexture { + access: access.into(), + format: texture.format, + view_dimension: wgpu::TextureViewDimension::Cube, + }, + count: None, + } + } + Binding::StorageTextureArray(_, stage, texture, access) => { + wgpu::BindGroupLayoutEntry { + binding: index as u32, + visibility: stage.into(), + ty: wgpu::BindingType::StorageTexture { + access: access.into(), + format: texture.format, + view_dimension: wgpu::TextureViewDimension::D2Array, + }, + count: None, + } + } + Binding::StorageTexture3D(_, stage, texture, access) => { + wgpu::BindGroupLayoutEntry { + binding: index as u32, + visibility: stage.into(), + ty: wgpu::BindingType::StorageTexture { + access: access.into(), + format: texture.format, + view_dimension: wgpu::TextureViewDimension::D3, + }, + count: None, + } + } Binding::Sampler(_, stage, _) => wgpu::BindGroupLayoutEntry { binding: index as u32, visibility: stage.into(), @@ -169,8 +231,13 @@ impl Bindings { uniform.get().as_entire_binding() } Binding::Texture(_, _, texture) + | Binding::TextureCube(_, _, texture) + | Binding::TextureArray(_, _, texture) | Binding::Texture3D(_, _, texture) - | Binding::StorageTexture(_, _, texture, _) => { + | Binding::StorageTexture(_, _, texture, _) + | Binding::StorageTextureCube(_, _, texture, _) + | Binding::StorageTextureArray(_, _, texture, _) + | Binding::StorageTexture3D(_, _, texture, _) => { wgpu::BindingResource::TextureView(texture.get()) } Binding::Sampler(_, _, sampler) => { diff --git a/dotrix_core/src/renderer/buffer.rs b/dotrix_core/src/renderer/buffer.rs index c7ecf578..1325b722 100644 --- a/dotrix_core/src/renderer/buffer.rs +++ b/dotrix_core/src/renderer/buffer.rs @@ -47,6 +47,16 @@ impl Buffer { Self::new(label).use_as_indirect() } + /// Construct new Map Read buffer + pub fn map_read(label: &str) -> Self { + Self::new(label).use_as_map_read() + } + + /// Construct new Map Write buffer + pub fn map_write(label: &str) -> Self { + Self::new(label).use_as_map_write() + } + /// Allow to use as Vertex Buffer #[must_use] pub fn use_as_vertex(mut self) -> Self { @@ -82,6 +92,20 @@ impl Buffer { self } + /// Allow to use as Map Read Buffer + #[must_use] + pub fn use_as_map_read(mut self) -> Self { + self.usage |= wgpu::BufferUsages::MAP_READ; + self + } + + /// Allow to use as Map Write Buffer + #[must_use] + pub fn use_as_map_write(mut self) -> Self { + self.usage |= wgpu::BufferUsages::MAP_WRITE; + self + } + /// Allow reading from buffer #[must_use] pub fn allow_read(mut self) -> Self { @@ -116,6 +140,18 @@ impl Buffer { } } + /// Create buffer of size without data + /// + /// Typically used for staging buffers + pub fn create(&mut self, ctx: &Context, size: u32, mapped: bool) { + self.wgpu_buffer = Some(ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: Some(self.label.as_str()), + size: size as wgpu::BufferAddress, + usage: self.usage, + mapped_at_creation: mapped, + })); + } + /// Check if buffer isloaded pub fn loaded(&self) -> bool { self.wgpu_buffer.is_some() diff --git a/dotrix_core/src/renderer/context.rs b/dotrix_core/src/renderer/context.rs index 2617576b..a23d639b 100644 --- a/dotrix_core/src/renderer/context.rs +++ b/dotrix_core/src/renderer/context.rs @@ -254,6 +254,48 @@ impl Context { cpass.dispatch(args.work_groups.x, args.work_groups.y, args.work_groups.z); } } + + pub(crate) fn run_copy_texture_to_buffer( + &mut self, + texture: &super::Texture, + buffer: &super::Buffer, + extent: [u32; 3], + bytes_per_pixel: u32, + ) { + let encoder = self.encoder.as_mut().expect("WGPU encoder must be set"); + let unpadded_bytes_per_row: u32 = + std::num::NonZeroU32::new(bytes_per_pixel as u32 * extent[0]) + .unwrap() + .into(); + let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as u32; + let padded_bytes_per_row_padding = (align - unpadded_bytes_per_row % align) % align; + let padded_bytes_per_row = unpadded_bytes_per_row + padded_bytes_per_row_padding; + + encoder.copy_texture_to_buffer( + wgpu::ImageCopyTexture { + texture: texture + .wgpu_texture + .as_ref() + .expect("Texture must be loaded"), + mip_level: 0, + origin: wgpu::Origin3d { x: 0, y: 0, z: 0 }, + aspect: wgpu::TextureAspect::All, + }, + wgpu::ImageCopyBuffer { + buffer: buffer.wgpu_buffer.as_ref().expect("Buffer must be ready"), + layout: wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(std::num::NonZeroU32::new(padded_bytes_per_row).unwrap()), + rows_per_image: Some(std::num::NonZeroU32::new(extent[1]).unwrap()), + }, + }, + wgpu::Extent3d { + width: extent[0], + height: extent[1], + depth_or_array_layers: extent[2], + }, + ); + } } pub(crate) async fn init(window: &winit::window::Window, sample_count: u32) -> Context { diff --git a/dotrix_core/src/renderer/texture.rs b/dotrix_core/src/renderer/texture.rs index 003ca48b..0679fae0 100644 --- a/dotrix_core/src/renderer/texture.rs +++ b/dotrix_core/src/renderer/texture.rs @@ -1,4 +1,4 @@ -use super::Context; +use super::{Buffer, Context}; use wgpu; /// GPU Texture Implementation @@ -7,8 +7,12 @@ pub struct Texture { pub label: String, /// WGPU Texture view pub wgpu_texture_view: Option, + /// WGPU Texture + pub wgpu_texture: Option, /// Texture usage pub usage: wgpu::TextureUsages, + /// Texture kind + pub kind: wgpu::TextureViewDimension, /// Texture format pub format: wgpu::TextureFormat, } @@ -18,8 +22,10 @@ impl Default for Texture { Self { label: String::from("Noname Texture"), wgpu_texture_view: None, + wgpu_texture: None, usage: wgpu::TextureUsages::empty(), format: wgpu::TextureFormat::Rgba8UnormSrgb, + kind: wgpu::TextureViewDimension::D2, } } } @@ -34,6 +40,36 @@ impl Texture { } } + /// Constructs a CubeMap GPU Texture + pub fn new_cube(label: &str) -> Self { + Self { + label: String::from(label), + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + kind: wgpu::TextureViewDimension::Cube, + ..Default::default() + } + } + + /// Constructs a 2D Array GPU Texture + pub fn new_array(label: &str) -> Self { + Self { + label: String::from(label), + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + kind: wgpu::TextureViewDimension::D2Array, + ..Default::default() + } + } + + /// Constructs a 3D GPU Texture + pub fn new_3d(label: &str) -> Self { + Self { + label: String::from(label), + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + kind: wgpu::TextureViewDimension::D3, + ..Default::default() + } + } + /// Constructs GPU Storage Texture pub fn storage(label: &str) -> Self { Self { @@ -102,7 +138,18 @@ impl Texture { } /// Loads data into the texture buffer + /// + /// This will recreate the texture backend on the gpu. Which means it must be rebound + /// in the pipelines for changes to take effect. + /// + /// If you want to update the values without recreating and therefore rebinding the texture + /// see `[update]` pub(crate) fn load<'a>(&mut self, ctx: &Context, width: u32, height: u32, layers: &[&'a [u8]]) { + let dimension = self.kind; + if let wgpu::TextureViewDimension::Cube = dimension { + assert_eq!(layers.len(), 6); + }; + let format = self.format; let usage = self.usage; let depth_or_array_layers = layers.len() as u32; @@ -111,18 +158,23 @@ impl Texture { height, depth_or_array_layers, }; - let layer_size = wgpu::Extent3d { - depth_or_array_layers: 1, - ..size - }; + let max_mips = 1; + let tex_dimension: wgpu::TextureDimension = match self.kind { + wgpu::TextureViewDimension::D2 => wgpu::TextureDimension::D2, + wgpu::TextureViewDimension::Cube => wgpu::TextureDimension::D2, + wgpu::TextureViewDimension::D2Array => wgpu::TextureDimension::D2, + wgpu::TextureViewDimension::D3 => wgpu::TextureDimension::D3, + _ => unimplemented!(), + }; + let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { label: Some(&self.label), size, mip_level_count: max_mips as u32, sample_count: 1, - dimension: wgpu::TextureDimension::D2, + dimension: tex_dimension, format, usage, }); @@ -130,35 +182,81 @@ impl Texture { self.wgpu_texture_view = Some(texture.create_view(&wgpu::TextureViewDescriptor { label: None, format: Some(format), - dimension: Some(if depth_or_array_layers == 6 { - wgpu::TextureViewDimension::Cube - } else { - wgpu::TextureViewDimension::D2 - }), + dimension: Some(dimension), ..wgpu::TextureViewDescriptor::default() })); - for (i, data) in layers.iter().enumerate() { - let bytes_per_row = std::num::NonZeroU32::new(data.len() as u32 / height).unwrap(); - ctx.queue.write_texture( - wgpu::ImageCopyTexture { - texture: &texture, - mip_level: 0, - origin: wgpu::Origin3d { - x: 0, - y: 0, - z: i as u32, + self.wgpu_texture = Some(texture); + + self.update(ctx, width, height, layers) + } + + /// This will write to a texture but not create it + /// This can be used to update a texture's value with out recreating and therefore without the + /// need to rebind it + /// however if the size of the texture is changed it will behave oddly or even panic + /// + /// This is a no op if the texture has not been loaded + pub(crate) fn update<'a>( + &mut self, + ctx: &Context, + width: u32, + height: u32, + layers: &[&'a [u8]], + ) { + if let Some(texture) = self.wgpu_texture.as_ref() { + let depth_or_array_layers = layers.len() as u32; + let size = wgpu::Extent3d { + width, + height, + depth_or_array_layers, + }; + let layer_size = wgpu::Extent3d { + depth_or_array_layers: 1, + ..size + }; + + for (i, data) in layers.iter().enumerate() { + let bytes_per_row = std::num::NonZeroU32::new(data.len() as u32 / height).unwrap(); + ctx.queue.write_texture( + wgpu::ImageCopyTexture { + texture, + mip_level: 0, + origin: wgpu::Origin3d { + x: 0, + y: 0, + z: i as u32, + }, + aspect: wgpu::TextureAspect::All, + }, + data, + wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(bytes_per_row), + rows_per_image: Some(std::num::NonZeroU32::new(height).unwrap()), }, - aspect: wgpu::TextureAspect::All, - }, - data, - wgpu::ImageDataLayout { - offset: 0, - bytes_per_row: Some(bytes_per_row), - rows_per_image: Some(std::num::NonZeroU32::new(height).unwrap()), - }, - layer_size, - ); + layer_size, + ); + } + } + } + + /// This method will update a gpu texture if it exists with new data or + /// load a new texture onto the gpu if it does not. + /// + /// The same cavets of [`update`] apply in that care must be taken to not + /// change the size of the texture between [`load`] and [`update`] + pub(crate) fn update_or_load<'a>( + &mut self, + ctx: &Context, + width: u32, + height: u32, + layers: &[&'a [u8]], + ) { + if self.wgpu_texture.is_none() { + self.load(ctx, width, height, layers); + } else { + self.update(ctx, width, height, layers); } } @@ -169,6 +267,7 @@ impl Texture { /// Release all resources used by the texture pub fn unload(&mut self) { + self.wgpu_texture.take(); self.wgpu_texture_view.take(); } @@ -183,4 +282,90 @@ impl Texture { pub fn is_filterable(&self) -> bool { self.format.describe().guaranteed_format_features.filterable } + + /// Get the texture bytes per pixels + pub fn pixel_bytes(&self) -> u8 { + self.format.describe().block_size + } + + /// Get the number of channels + pub fn num_channels(&self) -> u8 { + self.format.describe().components + } + + /// Get the texture sample type (float/uint etc) + pub fn sample_type(&self) -> wgpu::TextureSampleType { + self.format.describe().sample_type + } + + /// Fetch data from the gpu + /// + /// This is useful textures that are altered on the gpu + /// + /// This operation is slow and should mostly be + /// used for debugging + pub fn fetch_from_gpu( + &self, + dimensions: [u32; 3], + ctx: &mut Context, + ) -> impl std::future::Future, wgpu::BufferAsyncError>> { + let bytes_per_pixel: u32 = self.pixel_bytes() as u32; + let mut staging_buffer = Buffer::map_read("Texture Fetch Staging buffer"); + let unpadded_bytes_per_row: u32 = + std::num::NonZeroU32::new(bytes_per_pixel as u32 * dimensions[0]) + .unwrap() + .into(); + let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as u32; + let padded_bytes_per_row_padding = (align - unpadded_bytes_per_row % align) % align; + let padded_bytes_per_row = unpadded_bytes_per_row + padded_bytes_per_row_padding; + + staging_buffer.create( + ctx, + padded_bytes_per_row * dimensions[0] * dimensions[1], + false, + ); + ctx.run_copy_texture_to_buffer(self, &staging_buffer, dimensions, bytes_per_pixel); + + async move { + // TODO: Urgently work out a better way to await the next frame. + std::thread::sleep(std::time::Duration::from_secs(1)); + + let wgpu_buffer = staging_buffer.wgpu_buffer.expect("Buffer must be loaded"); + let buffer_slice = wgpu_buffer.slice(..); + // Gets the future representing when `staging_buffer` can be read from + let buffer_future = buffer_slice.map_async(wgpu::MapMode::Read); + + match buffer_future.await { + Ok(()) => { + // Gets contents of buffer + let data = buffer_slice.get_mapped_range(); + // This strips the padding on each row + let result: Vec = data + .chunks_exact((padded_bytes_per_row * dimensions[1]) as usize) + .flat_map(|img| { + let rows: Vec> = img + .chunks_exact(padded_bytes_per_row as usize) + .map(|row| row[0..(unpadded_bytes_per_row as usize)].to_vec()) + .collect(); + rows + }) + .flatten() + .collect(); + + // With the current interface, we have to make sure all mapped views are + // dropped before we unmap the buffer. + drop(data); + wgpu_buffer.unmap(); // Unmaps buffer from memory + // If you are familiar with C++ these 2 lines can be thought of similarly to: + // delete myPointer; + // myPointer = NULL; + // It effectively frees the memory + // + Ok(result) + } + + Err(e) => Err(e), + } + } + } } diff --git a/dotrix_core/src/world.rs b/dotrix_core/src/world.rs index 96cdc3e6..abe8e492 100644 --- a/dotrix_core/src/world.rs +++ b/dotrix_core/src/world.rs @@ -604,7 +604,7 @@ mod tests { let world = spawn(); let entity = Entity::from(0); let query = world.get::<(&Armor, &Health)>(entity); - assert_eq!(query.is_some(), true); + assert!(query.is_some()); if let Some((&armor, &health)) = query { assert_eq!(armor, Armor(100)); assert_eq!(health, Health(100)); @@ -630,8 +630,8 @@ mod tests { assert_eq!(spawned.len(), 3); - for i in 0..3 { - assert_eq!(spawned[i], Entity::from(i as u64 + 1)); + for (i, ent) in spawned.iter().enumerate() { + assert_eq!(ent, &Entity::from(i as u64 + 1)); } } } diff --git a/dotrix_sky/src/skybox.rs b/dotrix_sky/src/skybox.rs index 41c10013..02d99591 100644 --- a/dotrix_sky/src/skybox.rs +++ b/dotrix_sky/src/skybox.rs @@ -160,7 +160,7 @@ pub fn render( ), BindGroup::new( "Locals", - vec![Binding::Texture3D( + vec![Binding::TextureCube( "CubeMap", Stage::Fragment, &cubemap.buffer, diff --git a/examples/compute/main.rs b/examples/compute/main.rs index 663dbf8d..e3ebba79 100644 --- a/examples/compute/main.rs +++ b/examples/compute/main.rs @@ -184,10 +184,7 @@ fn compute( Binding::Storage("Particles", Stage::Compute, &spawner.particles), ], )], - options: ComputeOptions { - cs_main: "main", - ..Default::default() - }, + options: ComputeOptions { cs_main: "main" }, }, ); } diff --git a/src/lib.rs b/src/lib.rs index 180947b7..3f5677d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -128,6 +128,7 @@ impl Dotrix { } } + #[must_use] /// Adds system, service or extension to the application pub fn with(mut self, engine_unit: T) -> Self where @@ -137,14 +138,15 @@ impl Dotrix { self } + #[must_use] /// Adds a system to the application pub fn with_system(mut self, system: System) -> Self { self.app.as_mut().unwrap().add_system(system); self } + #[must_use] /// Adds a service to the application - pub fn with_service(mut self, service: T) -> Self { self.app.as_mut().unwrap().add_service(service); self