diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index 80c982d7..819d65c7 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -152,6 +152,7 @@ impl Backend { self.image_pipeline.draw( device, + staging_belt, encoder, &layer.images, scaled, diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs index 49f1d29c..cd756c1e 100644 --- a/wgpu/src/image.rs +++ b/wgpu/src/image.rs @@ -42,6 +42,8 @@ pub struct Pipeline { impl Pipeline { pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self { + use wgpu::util::DeviceExt; + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, @@ -49,24 +51,29 @@ impl Pipeline { mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear, - lod_min_clamp: -100.0, - lod_max_clamp: 100.0, - compare: wgpu::CompareFunction::Always, + ..Default::default() }); let constant_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: None, - bindings: &[ + entries: &[ wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStage::VERTEX, - ty: wgpu::BindingType::UniformBuffer { dynamic: false }, + ty: wgpu::BindingType::UniformBuffer { + dynamic: false, + min_binding_size: wgpu::BufferSize::new( + mem::size_of::() as u64, + ), + }, + count: None, }, wgpu::BindGroupLayoutEntry { binding: 1, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler { comparison: false }, + count: None, }, ], }); @@ -75,24 +82,25 @@ impl Pipeline { transform: Transformation::identity().into(), }; - let uniforms_buffer = device.create_buffer_with_data( - uniforms.as_bytes(), - wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, - ); + let uniforms_buffer = + device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: uniforms.as_bytes(), + usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, + }); let constant_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: None, layout: &constant_layout, - bindings: &[ - wgpu::Binding { + entries: &[ + wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::Buffer { - buffer: &uniforms_buffer, - range: 0..std::mem::size_of::() as u64, - }, + resource: wgpu::BindingResource::Buffer( + uniforms_buffer.slice(..), + ), }, - wgpu::Binding { + wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler), }, @@ -102,7 +110,7 @@ impl Pipeline { let texture_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: None, - bindings: &[wgpu::BindGroupLayoutEntry { + entries: &[wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { @@ -110,29 +118,29 @@ impl Pipeline { component_type: wgpu::TextureComponentType::Float, multisampled: false, }, + count: None, }], }); let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + push_constant_ranges: &[], bind_group_layouts: &[&constant_layout, &texture_layout], }); - let vs = include_bytes!("shader/image.vert.spv"); - let vs_module = device.create_shader_module( - &wgpu::read_spirv(std::io::Cursor::new(&vs[..])) - .expect("Read image vertex shader as SPIR-V"), - ); + let vs_module = device.create_shader_module(wgpu::include_spirv!( + "shader/image.vert.spv" + )); - let fs = include_bytes!("shader/image.frag.spv"); - let fs_module = device.create_shader_module( - &wgpu::read_spirv(std::io::Cursor::new(&fs[..])) - .expect("Read image fragment shader as SPIR-V"), - ); + let fs_module = device.create_shader_module(wgpu::include_spirv!( + "shader/image.frag.spv" + )); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - layout: &layout, + label: None, + layout: Some(&layout), vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", @@ -147,6 +155,7 @@ impl Pipeline { depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, + ..Default::default() }), primitive_topology: wgpu::PrimitiveTopology::TriangleList, color_states: &[wgpu::ColorStateDescriptor { @@ -214,20 +223,25 @@ impl Pipeline { alpha_to_coverage_enabled: false, }); - let vertices = device.create_buffer_with_data( - QUAD_VERTS.as_bytes(), - wgpu::BufferUsage::VERTEX, - ); + let vertices = + device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: QUAD_VERTS.as_bytes(), + usage: wgpu::BufferUsage::VERTEX, + }); - let indices = device.create_buffer_with_data( - QUAD_INDICES.as_bytes(), - wgpu::BufferUsage::INDEX, - ); + let indices = + device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: QUAD_INDICES.as_bytes(), + usage: wgpu::BufferUsage::INDEX, + }); let instances = device.create_buffer(&wgpu::BufferDescriptor { label: None, size: mem::size_of::() as u64 * Instance::MAX as u64, usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST, + mapped_at_creation: false, }); let texture_atlas = Atlas::new(device); @@ -235,7 +249,7 @@ impl Pipeline { let texture = device.create_bind_group(&wgpu::BindGroupDescriptor { label: None, layout: &texture_layout, - bindings: &[wgpu::Binding { + entries: &[wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView( &texture_atlas.view(), @@ -282,6 +296,7 @@ impl Pipeline { pub fn draw( &mut self, device: &wgpu::Device, + staging_belt: &mut wgpu::util::StagingBelt, encoder: &mut wgpu::CommandEncoder, images: &[layer::Image], transformation: Transformation, @@ -356,7 +371,7 @@ impl Pipeline { device.create_bind_group(&wgpu::BindGroupDescriptor { label: None, layout: &self.texture_layout, - bindings: &[wgpu::Binding { + entries: &[wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView( &self.texture_atlas.view(), @@ -367,26 +382,23 @@ impl Pipeline { self.texture_version = texture_version; } - let uniforms_buffer = device.create_buffer_with_data( - Uniforms { - transform: transformation.into(), - } - .as_bytes(), - wgpu::BufferUsage::COPY_SRC, - ); + { + let mut uniforms_buffer = staging_belt.write_buffer( + encoder, + &self.uniforms, + 0, + wgpu::BufferSize::new(mem::size_of::() as u64) + .unwrap(), + device, + ); - encoder.copy_buffer_to_buffer( - &uniforms_buffer, - 0, - &self.uniforms, - 0, - std::mem::size_of::() as u64, - ); - - let instances_buffer = device.create_buffer_with_data( - instances.as_bytes(), - wgpu::BufferUsage::COPY_SRC, - ); + uniforms_buffer.copy_from_slice( + Uniforms { + transform: transformation.into(), + } + .as_bytes(), + ); + } let mut i = 0; let total = instances.len(); @@ -395,27 +407,29 @@ impl Pipeline { let end = (i + Instance::MAX).min(total); let amount = end - i; - encoder.copy_buffer_to_buffer( - &instances_buffer, - (i * std::mem::size_of::()) as u64, + let mut instances_buffer = staging_belt.write_buffer( + encoder, &self.instances, 0, - (amount * std::mem::size_of::()) as u64, + wgpu::BufferSize::new( + (amount * std::mem::size_of::()) as u64, + ) + .unwrap(), + device, ); + instances_buffer + .copy_from_slice(instances[i..i + amount].as_bytes()); + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[ wgpu::RenderPassColorAttachmentDescriptor { attachment: target, resolve_target: None, - load_op: wgpu::LoadOp::Load, - store_op: wgpu::StoreOp::Store, - clear_color: wgpu::Color { - r: 0.0, - g: 0.0, - b: 0.0, - a: 0.0, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: true, }, }, ], @@ -425,9 +439,9 @@ impl Pipeline { render_pass.set_pipeline(&self.pipeline); render_pass.set_bind_group(0, &self.constants, &[]); render_pass.set_bind_group(1, &self.texture, &[]); - render_pass.set_index_buffer(&self.indices, 0, 0); - render_pass.set_vertex_buffer(0, &self.vertices, 0, 0); - render_pass.set_vertex_buffer(1, &self.instances, 0, 0); + render_pass.set_index_buffer(self.indices.slice(..)); + render_pass.set_vertex_buffer(0, self.vertices.slice(..)); + render_pass.set_vertex_buffer(1, self.instances.slice(..)); render_pass.set_scissor_rect( bounds.x, diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs index 3ce3ce8b..99e9995f 100644 --- a/wgpu/src/image/atlas.rs +++ b/wgpu/src/image/atlas.rs @@ -30,7 +30,6 @@ impl Atlas { let texture = device.create_texture(&wgpu::TextureDescriptor { label: None, size: extent, - array_layer_count: 2, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, @@ -40,7 +39,10 @@ impl Atlas { | wgpu::TextureUsage::SAMPLED, }); - let texture_view = texture.create_default_view(); + let texture_view = texture.create_view(&wgpu::TextureViewDescriptor { + dimension: Some(wgpu::TextureViewDimension::D2Array), + ..Default::default() + }); Atlas { texture, @@ -65,6 +67,8 @@ impl Atlas { device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, ) -> Option { + use wgpu::util::DeviceExt; + let entry = { let current_size = self.layers.len(); let entry = self.allocate(width, height)?; @@ -78,8 +82,31 @@ impl Atlas { log::info!("Allocated atlas entry: {:?}", entry); + // It is a webgpu requirement that: + // BufferCopyView.layout.bytes_per_row % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT == 0 + // So we calculate padded_width by rounding width up to the next + // multiple of wgpu::COPY_BYTES_PER_ROW_ALIGNMENT. + let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT; + let padding = (align - (4 * width) % align) % align; + let padded_width = (4 * width + padding) as usize; + let padded_data_size = padded_width * height as usize; + + let mut padded_data = vec![0; padded_data_size]; + + for row in 0..height as usize { + let offset = row * padded_width; + + padded_data[offset..offset + 4 * width as usize].copy_from_slice( + &data[row * 4 * width as usize..(row + 1) * 4 * width as usize], + ) + } + let buffer = - device.create_buffer_with_data(data, wgpu::BufferUsage::COPY_SRC); + device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: &padded_data, + usage: wgpu::BufferUsage::COPY_SRC, + }); match &entry { Entry::Contiguous(allocation) => { @@ -87,6 +114,7 @@ impl Atlas { &buffer, width, height, + padding, 0, &allocation, encoder, @@ -95,12 +123,13 @@ impl Atlas { Entry::Fragmented { fragments, .. } => { for fragment in fragments { let (x, y) = fragment.position; - let offset = (y * width + x) as usize * 4; + let offset = (y * padded_width as u32 + x) as usize * 4; self.upload_allocation( &buffer, width, height, + padding, offset, &fragment.allocation, encoder, @@ -253,6 +282,7 @@ impl Atlas { buffer: &wgpu::Buffer, image_width: u32, image_height: u32, + padding: u32, offset: usize, allocation: &Allocation, encoder: &mut wgpu::CommandEncoder, @@ -270,15 +300,20 @@ impl Atlas { encoder.copy_buffer_to_texture( wgpu::BufferCopyView { buffer, - offset: offset as u64, - bytes_per_row: 4 * image_width, - rows_per_image: image_height, + layout: wgpu::TextureDataLayout { + offset: offset as u64, + bytes_per_row: 4 * image_width + padding, + rows_per_image: image_height, + }, }, wgpu::TextureCopyView { texture: &self.texture, - array_layer: layer as u32, mip_level: 0, - origin: wgpu::Origin3d { x, y, z: 0 }, + origin: wgpu::Origin3d { + x, + y, + z: layer as u32, + }, }, extent, ); @@ -299,9 +334,8 @@ impl Atlas { size: wgpu::Extent3d { width: SIZE, height: SIZE, - depth: 1, + depth: self.layers.len() as u32, }, - array_layer_count: self.layers.len() as u32, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, @@ -323,15 +357,21 @@ impl Atlas { encoder.copy_texture_to_texture( wgpu::TextureCopyView { texture: &self.texture, - array_layer: i as u32, mip_level: 0, - origin: wgpu::Origin3d { x: 0, y: 0, z: 0 }, + origin: wgpu::Origin3d { + x: 0, + y: 0, + z: i as u32, + }, }, wgpu::TextureCopyView { texture: &new_texture, - array_layer: i as u32, mip_level: 0, - origin: wgpu::Origin3d { x: 0, y: 0, z: 0 }, + origin: wgpu::Origin3d { + x: 0, + y: 0, + z: i as u32, + }, }, wgpu::Extent3d { width: SIZE, @@ -342,6 +382,10 @@ impl Atlas { } self.texture = new_texture; - self.texture_view = self.texture.create_default_view(); + self.texture_view = + self.texture.create_view(&wgpu::TextureViewDescriptor { + dimension: Some(wgpu::TextureViewDimension::D2Array), + ..Default::default() + }); } }