Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/texture updates #159

Merged
merged 9 commits into from
Apr 20, 2022
4 changes: 2 additions & 2 deletions dotrix_core/src/assets/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -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],
Expand Down
1 change: 1 addition & 0 deletions dotrix_core/src/assets/shader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
voxelias marked this conversation as resolved.
Show resolved Hide resolved
renderer.load_shader(&mut self.module, &self.code);
}
}
Expand Down
13 changes: 13 additions & 0 deletions dotrix_core/src/assets/texture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Output = Result<Vec<u8>, wgpu::BufferAsyncError>> {
renderer.fetch_texture(&self.buffer, [self.width, self.height, self.depth])
}
}
2 changes: 1 addition & 1 deletion dotrix_core/src/cubemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
}
}
}
Expand Down
60 changes: 59 additions & 1 deletion dotrix_core/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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());
Expand All @@ -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<Output = Result<Vec<u8>, wgpu::BufferAsyncError>> {
texture.fetch_from_gpu(dimensions, self.context_mut())
}

/// Forces engine to reload shaders
pub fn reload(&mut self) {
self.dirty = true;
Expand Down Expand Up @@ -263,6 +317,10 @@ pub fn release(mut renderer: Mut<Renderer>) {
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);
}
voxelias marked this conversation as resolved.
Show resolved Hide resolved
}

/// Resize handling system
Expand Down
83 changes: 75 additions & 8 deletions dotrix_core/src/renderer/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(),
voxelias marked this conversation as resolved.
Show resolved Hide resolved
view_dimension: wgpu::TextureViewDimension::Cube,
},
count: None,
},
Binding::TextureArray(_, stage, texture) => wgpu::BindGroupLayoutEntry {
voxelias marked this conversation as resolved.
Show resolved Hide resolved
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(),
Expand All @@ -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(),
Expand Down Expand Up @@ -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) => {
Expand Down
36 changes: 36 additions & 0 deletions dotrix_core/src/renderer/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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()
Expand Down
42 changes: 42 additions & 0 deletions dotrix_core/src/renderer/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading