From 957a5b57ddd904c5b19d476bcef6275fae33af33 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 21 Feb 2019 22:46:54 -0500 Subject: [PATCH 1/8] Rust side of the shadow example --- gfx-examples/src/cube.rs | 10 +- gfx-examples/src/shadow.rs | 678 ++++++++++++++++++++++++++++++++++++ wgpu-native/src/conv.rs | 22 +- wgpu-native/src/device.rs | 2 +- wgpu-native/src/pipeline.rs | 1 + wgpu-native/src/resource.rs | 3 +- wgpu-rs/src/lib.rs | 6 +- 7 files changed, 705 insertions(+), 17 deletions(-) create mode 100644 gfx-examples/src/shadow.rs diff --git a/gfx-examples/src/cube.rs b/gfx-examples/src/cube.rs index 13f839c6fc..09e1b079a8 100644 --- a/gfx-examples/src/cube.rs +++ b/gfx-examples/src/cube.rs @@ -83,7 +83,7 @@ fn create_texels(size: usize) -> Vec { .collect() } -struct Cube { +struct Example { vertex_buf: wgpu::Buffer, index_buf: wgpu::Buffer, index_count: usize, @@ -92,7 +92,7 @@ struct Cube { pipeline: wgpu::RenderPipeline, } -impl Cube { +impl Example { fn generate_matrix(aspect_ratio: f32) -> cgmath::Matrix4 { let mx_projection = cgmath::perspective(cgmath::Deg(45f32), aspect_ratio, 1.0, 10.0); let mx_view = cgmath::Matrix4::look_at( @@ -104,7 +104,7 @@ impl Cube { } } -impl framework::Example for Cube { +impl framework::Example for Example { fn init(device: &mut wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor) -> Self { use std::mem; @@ -293,7 +293,7 @@ impl framework::Example for Cube { // Done let init_command_buf = init_encoder.finish(); device.get_queue().submit(&[init_command_buf]); - Cube { + Example { vertex_buf, index_buf, index_count: index_data.len(), @@ -337,5 +337,5 @@ impl framework::Example for Cube { } fn main() { - framework::run::("cube"); + framework::run::("cube"); } diff --git a/gfx-examples/src/shadow.rs b/gfx-examples/src/shadow.rs new file mode 100644 index 0000000000..55b59efeb1 --- /dev/null +++ b/gfx-examples/src/shadow.rs @@ -0,0 +1,678 @@ +use std::ops::Range; +use std::rc::Rc; + +mod framework; + + +#[derive(Clone)] +struct Vertex { + pos: [i8; 4], + normal: [i8; 4], +} + +fn vertex(pos: [i8; 3], nor: [i8; 3]) -> Vertex { + Vertex { + pos: [pos[0], pos[1], pos[2], 1], + normal: [nor[0], nor[1], nor[2], 0], + } +} + +fn create_cube() -> (Vec, Vec) { + let vertex_data = [ + // top (0, 0, 1) + vertex([-1, -1, 1], [0, 0, 1]), + vertex([ 1, -1, 1], [0, 0, 1]), + vertex([ 1, 1, 1], [0, 0, 1]), + vertex([-1, 1, 1], [0, 0, 1]), + // bottom (0, 0, -1) + vertex([-1, 1, -1], [0, 0, -1]), + vertex([ 1, 1, -1], [0, 0, -1]), + vertex([ 1, -1, -1], [0, 0, -1]), + vertex([-1, -1, -1], [0, 0, -1]), + // right (1, 0, 0) + vertex([ 1, -1, -1], [1, 0, 0]), + vertex([ 1, 1, -1], [1, 0, 0]), + vertex([ 1, 1, 1], [1, 0, 0]), + vertex([ 1, -1, 1], [1, 0, 0]), + // left (-1, 0, 0) + vertex([-1, -1, 1], [-1, 0, 0]), + vertex([-1, 1, 1], [-1, 0, 0]), + vertex([-1, 1, -1], [-1, 0, 0]), + vertex([-1, -1, -1], [-1, 0, 0]), + // front (0, 1, 0) + vertex([ 1, 1, -1], [0, 1, 0]), + vertex([-1, 1, -1], [0, 1, 0]), + vertex([-1, 1, 1], [0, 1, 0]), + vertex([ 1, 1, 1], [0, 1, 0]), + // back (0, -1, 0) + vertex([ 1, -1, 1], [0, -1, 0]), + vertex([-1, -1, 1], [0, -1, 0]), + vertex([-1, -1, -1], [0, -1, 0]), + vertex([ 1, -1, -1], [0, -1, 0]), + ]; + + let index_data: &[u16] = &[ + 0, 1, 2, 2, 3, 0, // top + 4, 5, 6, 6, 7, 4, // bottom + 8, 9, 10, 10, 11, 8, // right + 12, 13, 14, 14, 15, 12, // left + 16, 17, 18, 18, 19, 16, // front + 20, 21, 22, 22, 23, 20, // back + ]; + + (vertex_data.to_vec(), index_data.to_vec()) +} + +fn create_plane(size: i8) -> (Vec, Vec) { + let vertex_data = [ + vertex([ size, -size, 0], [0, 0, 1]), + vertex([ size, size, 0], [0, 0, 1]), + vertex([-size, -size, 0], [0, 0, 1]), + vertex([-size, size, 0], [0, 0, 1]), + ]; + + let index_data: &[u16] = &[ + 0, 1, 2, + 2, 1, 3 + ]; + + (vertex_data.to_vec(), index_data.to_vec()) +} + + +struct Entity { + mx_world: cgmath::Matrix4, + vertex_buf: Rc, + index_buf: Rc, + index_count: usize, + bind_group: wgpu::BindGroup, + uniform_buf: wgpu::Buffer, +} + +struct Light { + pos: cgmath::Point3, + color: wgpu::Color, + fov: f32, + depth: Range, + target_view: wgpu::TextureView, +} + +#[repr(C)] +struct LightRaw { + pos: [f32; 4], + color: [f32; 4], + proj: [[f32; 4]; 4], +} + +impl Light { + fn to_raw(&self) -> LightRaw { + use cgmath::{EuclideanSpace, Deg, Point3, Vector3, Matrix4, PerspectiveFov}; + + let mx_view = Matrix4::look_at( + self.pos, + Point3::origin(), + Vector3::unit_z(), + ); + let projection = PerspectiveFov { + fovy: Deg(self.fov).into(), + aspect: 1.0, + near: self.depth.start, + far: self.depth.end, + }; + let mx_view_proj = cgmath::Matrix4::from(projection.to_perspective()) * mx_view; + LightRaw { + proj: *mx_view_proj.as_ref(), + pos: [self.pos.x, self.pos.y, self.pos.z, 1.0], + color: [self.color.r, self.color.g, self.color.b, 1.0], + } + } +} + +#[repr(C)] +struct ForwardUniforms { + proj: [[f32; 4]; 4], + color: [f32; 4], + num_lights: [u32; 4], +} + +#[repr(C)] +struct ShadowUniforms { + proj: [[f32; 4]; 4], +} + +struct Pass { + pipeline: wgpu::RenderPipeline, + bind_group: wgpu::BindGroup, + uniform_buf: wgpu::Buffer, +} + +struct Example { + entities: Vec, + lights: Vec, + lights_are_dirty: bool, + shadow_pass: Pass, + forward_pass: Pass, + light_uniform_buf: wgpu::Buffer, +} + +impl Example { + const MAX_LIGHTS: usize = 10; + const SHADOW_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::D32Float; + const SHADOW_SIZE: wgpu::Extent3d = wgpu::Extent3d { + width: 512, + height: 512, + depth: 1, + }; + + fn generate_matrix(aspect_ratio: f32) -> cgmath::Matrix4 { + let mx_projection = cgmath::perspective(cgmath::Deg(45f32), aspect_ratio, 1.0, 10.0); + let mx_view = cgmath::Matrix4::look_at( + cgmath::Point3::new(1.5f32, -5.0, 3.0), + cgmath::Point3::new(0f32, 0.0, 0.0), + cgmath::Vector3::unit_z(), + ); + mx_projection * mx_view + } +} + +impl framework::Example for Example { + fn init(device: &mut wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor) -> Self { + use std::mem; + + // Create the vertex and index buffers + let vertex_size = mem::size_of::(); + let (cube_vertex_data, cube_index_data) = create_cube(); + let cube_vertex_buf = Rc::new(device.create_buffer(&wgpu::BufferDescriptor { + size: (cube_vertex_data.len() * vertex_size) as u32, + usage: wgpu::BufferUsageFlags::VERTEX | wgpu::BufferUsageFlags::TRANSFER_DST, + })); + cube_vertex_buf.set_sub_data(0, framework::cast_slice(&cube_vertex_data)); + let cube_index_buf = Rc::new(device.create_buffer(&wgpu::BufferDescriptor { + size: (cube_index_data.len() * 2) as u32, + usage: wgpu::BufferUsageFlags::INDEX | wgpu::BufferUsageFlags::TRANSFER_DST, + })); + cube_index_buf.set_sub_data(0, framework::cast_slice(&cube_index_data)); + + let (plane_vertex_data, plane_index_data) = create_plane(7); + let plane_vertex_buf = device.create_buffer(&wgpu::BufferDescriptor { + size: (plane_vertex_data.len() * vertex_size) as u32, + usage: wgpu::BufferUsageFlags::VERTEX | wgpu::BufferUsageFlags::TRANSFER_DST, + }); + plane_vertex_buf.set_sub_data(0, framework::cast_slice(&plane_vertex_data)); + let plane_index_buf = device.create_buffer(&wgpu::BufferDescriptor { + size: (plane_index_data.len() * 2) as u32, + usage: wgpu::BufferUsageFlags::INDEX | wgpu::BufferUsageFlags::TRANSFER_DST, + }); + plane_index_buf.set_sub_data(0, framework::cast_slice(&plane_index_data)); + let plane_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { + size: 64, + usage: wgpu::BufferUsageFlags::UNIFORM | wgpu::BufferUsageFlags::TRANSFER_DST, + }); + + let local_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[ + wgpu::BindGroupLayoutBinding { + binding: 0, + visibility: wgpu::ShaderStageFlags::VERTEX, + ty: wgpu::BindingType::UniformBuffer, + }, + ], + }); + + let mut entities = vec![{ + use cgmath::SquareMatrix; + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &local_bind_group_layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::Buffer { + buffer: &plane_uniform_buf, + range: 0 .. 64, + }, + }, + ], + }); + Entity { + mx_world: cgmath::Matrix4::identity(), + vertex_buf: Rc::new(plane_vertex_buf), + index_buf: Rc::new(plane_index_buf), + index_count: plane_index_data.len(), + bind_group, + uniform_buf: plane_uniform_buf, + } + }]; + + struct CubeDesc { + offset: cgmath::Vector3, + angle: f32, + scale: f32, + } + let cube_descs = [ + CubeDesc { + offset: cgmath::vec3(-2.0, -2.0, 2.0), + angle: 10.0, + scale: 0.7, + }, + CubeDesc { + offset: cgmath::vec3(2.0, -2.0, 2.0), + angle: 50.0, + scale: 1.3, + }, + CubeDesc { + offset: cgmath::vec3(-2.0, 2.0, 2.0), + angle: 140.0, + scale: 1.1, + }, + CubeDesc { + offset: cgmath::vec3(2.0, 2.0, 2.0), + angle: 210.0, + scale: 0.9, + }, + ]; + + for cube in &cube_descs { + use cgmath::{Deg, Decomposed, Quaternion, Rotation3, InnerSpace}; + + let transform = Decomposed { + disp: cube.offset.clone(), + rot: Quaternion::from_axis_angle( + cube.offset.normalize(), + Deg(cube.angle), + ), + scale: cube.scale, + }; + let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { + size: 64, + usage: wgpu::BufferUsageFlags::UNIFORM | wgpu::BufferUsageFlags::TRANSFER_DST, + }); + entities.push(Entity { + mx_world: cgmath::Matrix4::from(transform), + vertex_buf: Rc::clone(&cube_vertex_buf), + index_buf: Rc::clone(&cube_index_buf), + index_count: cube_index_data.len(), + bind_group: device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &local_bind_group_layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::Buffer { + buffer: &uniform_buf, + range: 0 .. 64, + }, + }, + ], + }), + uniform_buf, + }); + } + + // Create other resources + let shadow_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + r_address_mode: wgpu::AddressMode::ClampToEdge, + s_address_mode: wgpu::AddressMode::ClampToEdge, + t_address_mode: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::FilterMode::Nearest, + lod_min_clamp: -100.0, + lod_max_clamp: 100.0, + max_anisotropy: 0, + compare_function: wgpu::CompareFunction::LessEqual, + border_color: wgpu::BorderColor::TransparentBlack, + }); + + let shadow_texture = device.create_texture(&wgpu::TextureDescriptor { + size: Self::SHADOW_SIZE, + array_size: Self::MAX_LIGHTS as u32, + dimension: wgpu::TextureDimension::D2, + format: Self::SHADOW_FORMAT, + usage: wgpu::TextureUsageFlags::OUTPUT_ATTACHMENT | wgpu::TextureUsageFlags::SAMPLED, + }); + let shadow_view = shadow_texture.create_default_view(); + + let mut shadow_target_views = (0..2) + .map(|i| Some(shadow_texture.create_view(&wgpu::TextureViewDescriptor { + format: Self::SHADOW_FORMAT, + dimension: wgpu::TextureViewDimension::D2, + aspect: wgpu::TextureAspectFlags::DEPTH, + base_mip_level: 0, + level_count: 1, + base_array_layer: i as u32, + array_count: 1, + }))) + .collect::>(); + let lights = vec![ + Light { + pos: cgmath::Point3::new(7.0, -5.0, 10.0), + color: wgpu::Color { r: 0.5, g: 1.0, b: 0.5, a: 1.0 }, + fov: 60.0, + depth: 1.0 .. 20.0, + target_view: shadow_target_views[0].take().unwrap(), + }, + Light { + pos: cgmath::Point3::new(-5.0, 7.0, 10.0), + color: wgpu::Color { r: 1.0, g: 0.5, b: 0.5, a: 1.0 }, + fov: 45.0, + depth: 1.0 .. 20.0, + target_view: shadow_target_views[1].take().unwrap(), + }, + ]; + let light_uniform_size = (Self::MAX_LIGHTS * mem::size_of::()) as u32; + let light_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { + size: light_uniform_size, + usage: wgpu::BufferUsageFlags::UNIFORM | wgpu::BufferUsageFlags::TRANSFER_DST, + }); + + let vb_desc = wgpu::VertexBufferDescriptor { + stride: vertex_size as u32, + step_mode: wgpu::InputStepMode::Vertex, + attributes: &[ + wgpu::VertexAttributeDescriptor { + attribute_index: 0, + format: wgpu::VertexFormat::IntR8G8B8A8, + offset: 0, + }, + wgpu::VertexAttributeDescriptor { + attribute_index: 1, + format: wgpu::VertexFormat::IntR8G8B8A8, + offset: 4 * 1, + }, + ], + }; + + let shadow_pass = { + // Create pipeline layout + let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[ + wgpu::BindGroupLayoutBinding { + binding: 0, // global + visibility: wgpu::ShaderStageFlags::VERTEX, + ty: wgpu::BindingType::UniformBuffer, + }, + ], + }); + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[ + &bind_group_layout, + &local_bind_group_layout, + ], + }); + + let uniform_size = mem::size_of::() as u32; + let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { + size: uniform_size, + usage: wgpu::BufferUsageFlags::UNIFORM | wgpu::BufferUsageFlags::TRANSFER_DST, + }); + + // Create bind group + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &bind_group_layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::Buffer { + buffer: &uniform_buf, + range: 0 .. uniform_size, + }, + }, + ], + }); + + // Create the render pipeline + let vs_bytes = framework::load_glsl("shadow-base.vert", framework::ShaderStage::Vertex); + let fs_bytes = framework::load_glsl("shadow-bake.frag", framework::ShaderStage::Fragment); + let vs_module = device.create_shader_module(&vs_bytes); + let fs_module = device.create_shader_module(&fs_bytes); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &pipeline_layout, + vertex_stage: wgpu::PipelineStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: wgpu::PipelineStageDescriptor { + module: &fs_module, + entry_point: "main", + }, + rasterization_state: wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Cw, + cull_mode: wgpu::CullMode::Back, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: wgpu::MAX_DEPTH_BIAS_CLAMP, + }, + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[], + depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { + format: Self::SHADOW_FORMAT, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::LessEqual, + stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE, + stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE, + stencil_read_mask: 0, + stencil_write_mask: 0, + }), + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[vb_desc.clone()], + sample_count: 1, + }); + + Pass { + pipeline, + bind_group, + uniform_buf, + } + }; + + let forward_pass = { + // Create pipeline layout + let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[ + wgpu::BindGroupLayoutBinding { + binding: 0, // global + visibility: wgpu::ShaderStageFlags::VERTEX | wgpu::ShaderStageFlags::FRAGMENT, + ty: wgpu::BindingType::UniformBuffer, + }, + wgpu::BindGroupLayoutBinding { + binding: 1, // lights + visibility: wgpu::ShaderStageFlags::VERTEX | wgpu::ShaderStageFlags::FRAGMENT, + ty: wgpu::BindingType::UniformBuffer, + }, + wgpu::BindGroupLayoutBinding { + binding: 2, + visibility: wgpu::ShaderStageFlags::FRAGMENT, + ty: wgpu::BindingType::SampledTexture, + }, + wgpu::BindGroupLayoutBinding { + binding: 3, + visibility: wgpu::ShaderStageFlags::FRAGMENT, + ty: wgpu::BindingType::Sampler, + }, + ], + }); + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[ + &bind_group_layout, + &local_bind_group_layout, + ], + }); + + let uniform_size = mem::size_of::() as u32; + let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { + size: uniform_size, + usage: wgpu::BufferUsageFlags::UNIFORM | wgpu::BufferUsageFlags::TRANSFER_DST, + }); + let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32); + let data = ForwardUniforms { + proj: *mx_total.as_ref(), + color: [1.0; 4], + num_lights: [lights.len() as u32, 0, 0, 0], + }; + uniform_buf.set_sub_data(0, framework::cast_slice(&[data])); + + // Create bind group + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &bind_group_layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::Buffer { + buffer: &uniform_buf, + range: 0 .. uniform_size, + }, + }, + wgpu::Binding { + binding: 1, + resource: wgpu::BindingResource::Buffer { + buffer: &light_uniform_buf, + range: 0 .. light_uniform_size, + }, + }, + wgpu::Binding { + binding: 2, + resource: wgpu::BindingResource::TextureView(&shadow_view), + }, + wgpu::Binding { + binding: 3, + resource: wgpu::BindingResource::Sampler(&shadow_sampler), + }, + ], + }); + + // Create the render pipeline + let vs_bytes = framework::load_glsl("shadow-forward.vert", framework::ShaderStage::Vertex); + let fs_bytes = framework::load_glsl("shadow-forward.frag", framework::ShaderStage::Fragment); + let vs_module = device.create_shader_module(&vs_bytes); + let fs_module = device.create_shader_module(&fs_bytes); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &pipeline_layout, + vertex_stage: wgpu::PipelineStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: wgpu::PipelineStageDescriptor { + module: &fs_module, + entry_point: "main", + }, + rasterization_state: wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Cw, + cull_mode: wgpu::CullMode::Back, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: wgpu::MAX_DEPTH_BIAS_CLAMP, + }, + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[ + wgpu::ColorStateDescriptor { + format: sc_desc.format, + color: wgpu::BlendDescriptor::REPLACE, + alpha: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWriteFlags::ALL, + }, + ], + depth_stencil_state: None, + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[vb_desc], + sample_count: 1, + }); + + Pass { + pipeline, + bind_group, + uniform_buf, + } + }; + + Example { + entities, + lights, + lights_are_dirty: true, + shadow_pass, + forward_pass, + light_uniform_buf, + } + } + + fn update(&mut self, event: wgpu::winit::WindowEvent) { + if let wgpu::winit::WindowEvent::Resized(size) = event { + let mx_total = Self::generate_matrix(size.width as f32 / size.height as f32); + let mx_ref: &[f32; 16] = mx_total.as_ref(); + self.forward_pass.uniform_buf.set_sub_data(0, framework::cast_slice(&mx_ref[..])); + } + } + + fn render(&mut self, frame: &wgpu::SwapChainOutput, device: &mut wgpu::Device) { + for entity in &self.entities { + let raw: &[f32; 16] = entity.mx_world.as_ref(); + entity.uniform_buf.set_sub_data(0, framework::cast_slice(&raw[..])); + } + if self.lights_are_dirty { + self.lights_are_dirty = false; + let raw = self.lights + .iter() + .map(|light| light.to_raw()) + .collect::>(); + self.light_uniform_buf.set_sub_data(0, framework::cast_slice(&raw)); + } + + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); + + for (_i, light) in self.lights.iter().enumerate() { + //TODO: update light uniforms + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { + attachment: &light.target_view, + depth_load_op: wgpu::LoadOp::Clear, + depth_store_op: wgpu::StoreOp::Store, + stencil_load_op: wgpu::LoadOp::Clear, + stencil_store_op: wgpu::StoreOp::Store, + clear_depth: 1.0, + clear_stencil: 0, + }), + }); + pass.set_pipeline(&self.shadow_pass.pipeline); + pass.set_bind_group(0, &self.shadow_pass.bind_group); + + for entity in &self.entities { + pass.set_bind_group(1, &entity.bind_group); + pass.set_index_buffer(&entity.index_buf, 0); + pass.set_vertex_buffers(&[(&entity.vertex_buf, 0)]); + pass.draw_indexed(0 .. entity.index_count as u32, 0, 0..1); + } + } + + // forward pass + { + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0 }, + }], + depth_stencil_attachment: None, + }); + pass.set_pipeline(&self.shadow_pass.pipeline); + pass.set_bind_group(0, &self.shadow_pass.bind_group); + + for entity in &self.entities { + pass.set_bind_group(1, &entity.bind_group); + pass.set_index_buffer(&entity.index_buf, 0); + pass.set_vertex_buffers(&[(&entity.vertex_buf, 0)]); + pass.draw_indexed(0 .. entity.index_count as u32, 0, 0..1); + } + } + + device + .get_queue() + .submit(&[encoder.finish()]); + } +} + +fn main() { + framework::run::("shadow"); +} diff --git a/wgpu-native/src/conv.rs b/wgpu-native/src/conv.rs index a584b4290f..182dc7b666 100644 --- a/wgpu-native/src/conv.rs +++ b/wgpu-native/src/conv.rs @@ -291,6 +291,7 @@ pub fn map_texture_format(texture_format: resource::TextureFormat) -> hal::forma R8g8b8a8Unorm => H::Rgba8Unorm, R8g8b8a8Uint => H::Rgba8Uint, B8g8r8a8Unorm => H::Bgra8Unorm, + D32Float => H::D32Float, D32FloatS8Uint => H::D32FloatS8Uint, } } @@ -303,6 +304,7 @@ pub fn map_vertex_format(vertex_format: pipeline::VertexFormat) -> hal::format:: FloatR32G32B32 => H::Rgb32Float, FloatR32G32 => H::Rg32Float, FloatR32 => H::R32Float, + IntR8G8B8A8 => H::Rgba8Int, } } @@ -313,21 +315,25 @@ fn checked_u32_as_u16(value: u32) -> u16 { pub fn map_texture_dimension_size( dimension: resource::TextureDimension, - Extent3d { - width, - height, - depth, - }: Extent3d, + Extent3d { width, height, depth }: Extent3d, + array_size: u32, ) -> hal::image::Kind { use hal::image::Kind as H; use crate::resource::TextureDimension::*; match dimension { D1 => { assert_eq!(height, 1); - H::D1(width, checked_u32_as_u16(depth)) + assert_eq!(depth, 1); + H::D1(width, checked_u32_as_u16(array_size)) + } + D2 => { + assert_eq!(depth, 1); + H::D2(width, height, checked_u32_as_u16(array_size), 1) // TODO: Samples + } + D3 => { + assert_eq!(array_size, 1); + H::D3(width, height, depth) } - D2 => H::D2(width, height, checked_u32_as_u16(depth), 1), // TODO: Samples - D3 => H::D3(width, height, depth), } } diff --git a/wgpu-native/src/device.rs b/wgpu-native/src/device.rs index 480ddd1ab6..d48d8f2e59 100644 --- a/wgpu-native/src/device.rs +++ b/wgpu-native/src/device.rs @@ -385,7 +385,7 @@ pub fn device_create_texture( device_id: DeviceId, desc: &resource::TextureDescriptor, ) -> resource::Texture { - let kind = conv::map_texture_dimension_size(desc.dimension, desc.size); + let kind = conv::map_texture_dimension_size(desc.dimension, desc.size, desc.array_size); let format = conv::map_texture_format(desc.format); let aspects = format.surface_desc().aspects; let usage = conv::map_texture_usage(desc.usage, aspects); diff --git a/wgpu-native/src/pipeline.rs b/wgpu-native/src/pipeline.rs index cfe2f2f33e..c4566e6c07 100644 --- a/wgpu-native/src/pipeline.rs +++ b/wgpu-native/src/pipeline.rs @@ -129,6 +129,7 @@ pub enum VertexFormat { FloatR32G32B32 = 1, FloatR32G32 = 2, FloatR32 = 3, + IntR8G8B8A8 = 4, } #[repr(C)] diff --git a/wgpu-native/src/resource.rs b/wgpu-native/src/resource.rs index 5e6745ee9d..9ea522f0a5 100644 --- a/wgpu-native/src/resource.rs +++ b/wgpu-native/src/resource.rs @@ -61,7 +61,8 @@ pub enum TextureFormat { R8g8b8a8Unorm = 0, R8g8b8a8Uint = 1, B8g8r8a8Unorm = 2, - D32FloatS8Uint = 3, + D32Float = 3, + D32FloatS8Uint = 4, } bitflags! { diff --git a/wgpu-rs/src/lib.rs b/wgpu-rs/src/lib.rs index d14409ba0f..1c678b88e9 100644 --- a/wgpu-rs/src/lib.rs +++ b/wgpu-rs/src/lib.rs @@ -16,12 +16,13 @@ pub use wgn::{ BufferDescriptor, BufferUsageFlags, IndexFormat, InputStepMode, ShaderAttributeIndex, VertexAttributeDescriptor, VertexFormat, Color, CommandEncoderDescriptor, - ColorStateDescriptor, DepthStencilStateDescriptor, + ColorStateDescriptor, DepthStencilStateDescriptor, StencilStateFaceDescriptor, StencilOperation, DeviceDescriptor, Extensions, Extent3d, LoadOp, Origin3d, PowerPreference, PrimitiveTopology, RenderPassColorAttachmentDescriptor, RenderPassDepthStencilAttachmentDescriptor, ShaderModuleDescriptor, ShaderStageFlags, StoreOp, SwapChainDescriptor, SamplerDescriptor, AddressMode, FilterMode, BorderColor, CompareFunction, - TextureDescriptor, TextureDimension, TextureFormat, TextureUsageFlags, TextureViewDescriptor, + TextureDescriptor, TextureDimension, TextureFormat, TextureUsageFlags, + TextureViewDescriptor, TextureViewDimension, TextureAspectFlags, }; @@ -151,6 +152,7 @@ pub struct PipelineStageDescriptor<'a> { pub entry_point: &'a str, } +#[derive(Clone, Debug)] pub struct VertexBufferDescriptor<'a> { pub stride: u32, pub step_mode: InputStepMode, From 056cc64eb4cb6616305275d9d7fe18c98d07719d Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 22 Feb 2019 10:26:29 -0500 Subject: [PATCH 2/8] Shadow example shaders and improvements --- Makefile | 6 ++-- examples/hello_compute_rust/main.rs | 4 +-- gfx-examples/data/shadow-bake.frag | 4 +++ gfx-examples/data/shadow-bake.vert | 16 +++++++++ gfx-examples/data/shadow-forward.frag | 10 ++++++ gfx-examples/data/shadow-forward.vert | 25 +++++++++++++ gfx-examples/src/framework.rs | 6 +++- gfx-examples/src/shadow.rs | 51 ++++++++++++++++++--------- wgpu-rs/src/lib.rs | 2 +- 9 files changed, 101 insertions(+), 23 deletions(-) create mode 100644 gfx-examples/data/shadow-bake.frag create mode 100644 gfx-examples/data/shadow-bake.vert create mode 100644 gfx-examples/data/shadow-forward.frag create mode 100644 gfx-examples/data/shadow-forward.vert diff --git a/Makefile b/Makefile index ef08a5b1e1..9b183aec18 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ else endif -.PHONY: all check test doc clear lib-native lib-rust examples-native examples-rust gfx-cube +.PHONY: all check test doc clear lib-native lib-rust examples-native examples-rust gfx all: examples-native examples-rust examples-gfx @@ -65,5 +65,5 @@ examples-rust: lib-rust examples/Cargo.toml $(wildcard wgpu-native/**/*.rs) examples-gfx: lib-rust gfx-examples/Cargo.toml $(wildcard gfx-examples/*.rs) cargo build --manifest-path gfx-examples/Cargo.toml --features $(FEATURE_RUST) -gfx-cube: - cargo run --manifest-path gfx-examples/Cargo.toml --bin cube --features $(FEATURE_RUST) +gfx: + cargo run --manifest-path gfx-examples/Cargo.toml --bin $(name) --features $(FEATURE_RUST) diff --git a/examples/hello_compute_rust/main.rs b/examples/hello_compute_rust/main.rs index d2e74ca8c2..310426b24d 100644 --- a/examples/hello_compute_rust/main.rs +++ b/examples/hello_compute_rust/main.rs @@ -86,14 +86,14 @@ fn main() { }); let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); - encoder.copy_buffer_tobuffer(&staging_buffer, 0, &storage_buffer, 0, size); + encoder.copy_buffer_to_buffer(&staging_buffer, 0, &storage_buffer, 0, size); { let mut cpass = encoder.begin_compute_pass(); cpass.set_pipeline(&compute_pipeline); cpass.set_bind_group(0, &bind_group); cpass.dispatch(numbers.len() as u32, 1, 1); } - encoder.copy_buffer_tobuffer(&storage_buffer, 0, &staging_buffer, 0, size); + encoder.copy_buffer_to_buffer(&storage_buffer, 0, &staging_buffer, 0, size); // TODO: read the results back out of the staging buffer diff --git a/gfx-examples/data/shadow-bake.frag b/gfx-examples/data/shadow-bake.frag new file mode 100644 index 0000000000..f0bcb49bf9 --- /dev/null +++ b/gfx-examples/data/shadow-bake.frag @@ -0,0 +1,4 @@ +#version 450 + +void main() { +} diff --git a/gfx-examples/data/shadow-bake.vert b/gfx-examples/data/shadow-bake.vert new file mode 100644 index 0000000000..e4426b7455 --- /dev/null +++ b/gfx-examples/data/shadow-bake.vert @@ -0,0 +1,16 @@ +#version 450 + +layout(location = 0) in ivec4 a_Pos; + +layout(set = 0, binding = 0) uniform Globals { + mat4 u_ViewProj; +}; + +layout(set = 1, binding = 0) uniform Entity { + mat4 u_World; + vec4 u_Color; +}; + +void main() { + gl_Position = u_ViewProj * u_World * vec4(a_Pos); +} diff --git a/gfx-examples/data/shadow-forward.frag b/gfx-examples/data/shadow-forward.frag new file mode 100644 index 0000000000..22c79b3e9c --- /dev/null +++ b/gfx-examples/data/shadow-forward.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0) out vec4 o_Target; + +layout(set = 0, binding = 2) uniform texture2D u_ShadowTexture; +layout(set = 0, binding = 3) uniform sampler u_ShadowSampler; + +void main() { + o_Target = vec4(1.0); +} diff --git a/gfx-examples/data/shadow-forward.vert b/gfx-examples/data/shadow-forward.vert new file mode 100644 index 0000000000..c100fed42a --- /dev/null +++ b/gfx-examples/data/shadow-forward.vert @@ -0,0 +1,25 @@ +#version 450 + +layout(location = 0) in ivec4 a_Pos; +layout(location = 1) in ivec4 a_Normal; + +struct Light { + mat4 proj; + vec4 color; +}; + +layout(set = 0, binding = 0) uniform Globals { + mat4 u_ViewProj; + uvec4 u_NumLights; +}; +layout(set = 0, binding = 1) uniform Lights { + Light u_Lights[]; +}; +layout(set = 1, binding = 0) uniform Entity { + mat4 u_World; + vec4 u_Color; +}; + +void main() { + gl_Position = u_ViewProj * u_World * vec4(a_Pos); +} diff --git a/gfx-examples/src/framework.rs b/gfx-examples/src/framework.rs index ec0514ef74..dec148427b 100644 --- a/gfx-examples/src/framework.rs +++ b/gfx-examples/src/framework.rs @@ -30,7 +30,11 @@ pub fn load_glsl(name: &str, stage: ShaderStage) -> Vec { let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("data") .join(name); - let code = read_to_string(path).unwrap(); + let code = match read_to_string(&path) { + Ok(code) => code, + Err(e) => panic!("Unable to read {:?}: {:?}", path, e), + }; + let mut output = glsl_to_spirv::compile(&code, ty).unwrap(); let mut spv = Vec::new(); output.read_to_end(&mut spv).unwrap(); diff --git a/gfx-examples/src/shadow.rs b/gfx-examples/src/shadow.rs index 55b59efeb1..f0dd5ce404 100644 --- a/gfx-examples/src/shadow.rs +++ b/gfx-examples/src/shadow.rs @@ -1,3 +1,4 @@ +use std::mem; use std::ops::Range; use std::rc::Rc; @@ -82,6 +83,7 @@ fn create_plane(size: i8) -> (Vec, Vec) { struct Entity { mx_world: cgmath::Matrix4, + color: wgpu::Color, vertex_buf: Rc, index_buf: Rc, index_count: usize, @@ -99,9 +101,9 @@ struct Light { #[repr(C)] struct LightRaw { + proj: [[f32; 4]; 4], pos: [f32; 4], color: [f32; 4], - proj: [[f32; 4]; 4], } impl Light { @@ -131,10 +133,15 @@ impl Light { #[repr(C)] struct ForwardUniforms { proj: [[f32; 4]; 4], - color: [f32; 4], num_lights: [u32; 4], } +#[repr(C)] +struct EntityUniforms { + model: [[f32; 4]; 4], + color: [f32; 4], +} + #[repr(C)] struct ShadowUniforms { proj: [[f32; 4]; 4], @@ -177,8 +184,6 @@ impl Example { impl framework::Example for Example { fn init(device: &mut wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor) -> Self { - use std::mem; - // Create the vertex and index buffers let vertex_size = mem::size_of::(); let (cube_vertex_data, cube_index_data) = create_cube(); @@ -204,8 +209,9 @@ impl framework::Example for Example { usage: wgpu::BufferUsageFlags::INDEX | wgpu::BufferUsageFlags::TRANSFER_DST, }); plane_index_buf.set_sub_data(0, framework::cast_slice(&plane_index_data)); + let entity_uniform_size = mem::size_of::() as u32; let plane_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { - size: 64, + size: entity_uniform_size, usage: wgpu::BufferUsageFlags::UNIFORM | wgpu::BufferUsageFlags::TRANSFER_DST, }); @@ -229,13 +235,14 @@ impl framework::Example for Example { binding: 0, resource: wgpu::BindingResource::Buffer { buffer: &plane_uniform_buf, - range: 0 .. 64, + range: 0 .. entity_uniform_size, }, }, ], }); Entity { mx_world: cgmath::Matrix4::identity(), + color: wgpu::Color::WHITE, vertex_buf: Rc::new(plane_vertex_buf), index_buf: Rc::new(plane_index_buf), index_count: plane_index_data.len(), @@ -284,11 +291,12 @@ impl framework::Example for Example { scale: cube.scale, }; let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { - size: 64, + size: entity_uniform_size, usage: wgpu::BufferUsageFlags::UNIFORM | wgpu::BufferUsageFlags::TRANSFER_DST, }); entities.push(Entity { mx_world: cgmath::Matrix4::from(transform), + color: wgpu::Color::GREEN, vertex_buf: Rc::clone(&cube_vertex_buf), index_buf: Rc::clone(&cube_index_buf), index_count: cube_index_data.len(), @@ -299,7 +307,7 @@ impl framework::Example for Example { binding: 0, resource: wgpu::BindingResource::Buffer { buffer: &uniform_buf, - range: 0 .. 64, + range: 0 .. entity_uniform_size, }, }, ], @@ -421,7 +429,7 @@ impl framework::Example for Example { }); // Create the render pipeline - let vs_bytes = framework::load_glsl("shadow-base.vert", framework::ShaderStage::Vertex); + let vs_bytes = framework::load_glsl("shadow-bake.vert", framework::ShaderStage::Vertex); let fs_bytes = framework::load_glsl("shadow-bake.frag", framework::ShaderStage::Fragment); let vs_module = device.create_shader_module(&vs_bytes); let fs_module = device.create_shader_module(&fs_bytes); @@ -507,7 +515,6 @@ impl framework::Example for Example { let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32); let data = ForwardUniforms { proj: *mx_total.as_ref(), - color: [1.0; 4], num_lights: [lights.len() as u32, 0, 0, 0], }; uniform_buf.set_sub_data(0, framework::cast_slice(&[data])); @@ -606,8 +613,11 @@ impl framework::Example for Example { fn render(&mut self, frame: &wgpu::SwapChainOutput, device: &mut wgpu::Device) { for entity in &self.entities { - let raw: &[f32; 16] = entity.mx_world.as_ref(); - entity.uniform_buf.set_sub_data(0, framework::cast_slice(&raw[..])); + let data = EntityUniforms { + model: *entity.mx_world.as_ref(), + color: [entity.color.r, entity.color.g, entity.color.b, entity.color.a], + }; + entity.uniform_buf.set_sub_data(0, framework::cast_slice(&[data])); } if self.lights_are_dirty { self.lights_are_dirty = false; @@ -620,8 +630,17 @@ impl framework::Example for Example { let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); - for (_i, light) in self.lights.iter().enumerate() { - //TODO: update light uniforms + for (i, light) in self.lights.iter().enumerate() { + // The light uniform buffer already has the projection, + // let's just copy it over to the shadow uniform buffer. + encoder.copy_buffer_to_buffer( + &self.light_uniform_buf, + (i * mem::size_of::()) as u32, + &self.shadow_pass.uniform_buf, + 0, + 64, + ); + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { @@ -656,8 +675,8 @@ impl framework::Example for Example { }], depth_stencil_attachment: None, }); - pass.set_pipeline(&self.shadow_pass.pipeline); - pass.set_bind_group(0, &self.shadow_pass.bind_group); + pass.set_pipeline(&self.forward_pass.pipeline); + pass.set_bind_group(0, &self.forward_pass.bind_group); for entity in &self.entities { pass.set_bind_group(1, &entity.bind_group); diff --git a/wgpu-rs/src/lib.rs b/wgpu-rs/src/lib.rs index 1c678b88e9..c0ad705dcf 100644 --- a/wgpu-rs/src/lib.rs +++ b/wgpu-rs/src/lib.rs @@ -535,7 +535,7 @@ impl CommandEncoder { } } - pub fn copy_buffer_tobuffer( + pub fn copy_buffer_to_buffer( &mut self, source: &Buffer, source_offset: u32, From 7386a05a0dc50a7580378725bb2d482d0853a701 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 22 Feb 2019 12:59:05 -0500 Subject: [PATCH 3/8] Shadow shaders --- gfx-examples/Cargo.toml | 5 +++ gfx-examples/data/shadow-forward.frag | 51 +++++++++++++++++++++++++-- gfx-examples/data/shadow-forward.vert | 13 +++---- gfx-examples/src/cube.rs | 2 +- gfx-examples/src/framework.rs | 1 + gfx-examples/src/shadow.rs | 12 +++---- 6 files changed, 66 insertions(+), 18 deletions(-) diff --git a/gfx-examples/Cargo.toml b/gfx-examples/Cargo.toml index ce4959801a..d8b3698e54 100644 --- a/gfx-examples/Cargo.toml +++ b/gfx-examples/Cargo.toml @@ -12,8 +12,13 @@ publish = false name = "cube" path = "src/cube.rs" +[[bin]] +name = "shadow" +path = "src/shadow.rs" + [features] default = [] +metal-auto-capture = ["wgpu/metal-auto-capture"] metal = ["wgpu/metal"] dx11 = ["wgpu/dx11"] dx12 = ["wgpu/dx12"] diff --git a/gfx-examples/data/shadow-forward.frag b/gfx-examples/data/shadow-forward.frag index 22c79b3e9c..0928adce86 100644 --- a/gfx-examples/data/shadow-forward.frag +++ b/gfx-examples/data/shadow-forward.frag @@ -1,10 +1,55 @@ #version 450 +const int MAX_LIGHTS = 10; + +layout(location = 0) in vec3 v_Normal; +layout(location = 1) in vec4 v_Position; + layout(location = 0) out vec4 o_Target; -layout(set = 0, binding = 2) uniform texture2D u_ShadowTexture; -layout(set = 0, binding = 3) uniform sampler u_ShadowSampler; +struct Light { + mat4 proj; + vec4 pos; + vec4 color; +}; + +layout(set = 0, binding = 0) uniform Globals { + mat4 u_ViewProj; + uvec4 u_NumLights; +}; +layout(set = 0, binding = 1) uniform Lights { + Light u_Lights[MAX_LIGHTS]; +}; +layout(set = 0, binding = 2) uniform texture2DArray t_Shadow; +layout(set = 0, binding = 3) uniform samplerShadow s_Shadow; + +layout(set = 1, binding = 0) uniform Entity { + mat4 u_World; + vec4 u_Color; +}; + void main() { - o_Target = vec4(1.0); + vec3 normal = normalize(v_Normal); + vec3 ambient = vec3(0.05, 0.05, 0.05); + // accumulate color + vec3 color = ambient; + for (int i=0; i(title: &str) { let frame = swap_chain.get_next_texture(); example.render(&frame, &mut device); + running &= !cfg!(feature = "metal-auto-capture"); } } diff --git a/gfx-examples/src/shadow.rs b/gfx-examples/src/shadow.rs index f0dd5ce404..5200f6da93 100644 --- a/gfx-examples/src/shadow.rs +++ b/gfx-examples/src/shadow.rs @@ -172,11 +172,11 @@ impl Example { }; fn generate_matrix(aspect_ratio: f32) -> cgmath::Matrix4 { - let mx_projection = cgmath::perspective(cgmath::Deg(45f32), aspect_ratio, 1.0, 10.0); + let mx_projection = cgmath::perspective(cgmath::Deg(45f32), aspect_ratio, 1.0, 20.0); let mx_view = cgmath::Matrix4::look_at( - cgmath::Point3::new(1.5f32, -5.0, 3.0), + cgmath::Point3::new(3.0f32, -10.0, 6.0), cgmath::Point3::new(0f32, 0.0, 0.0), - cgmath::Vector3::unit_z(), + -cgmath::Vector3::unit_z(), ); mx_projection * mx_view } @@ -219,7 +219,7 @@ impl framework::Example for Example { bindings: &[ wgpu::BindGroupLayoutBinding { binding: 0, - visibility: wgpu::ShaderStageFlags::VERTEX, + visibility: wgpu::ShaderStageFlags::VERTEX | wgpu::ShaderStageFlags::FRAGMENT, ty: wgpu::BindingType::UniformBuffer, }, ], @@ -447,8 +447,8 @@ impl framework::Example for Example { rasterization_state: wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Cw, cull_mode: wgpu::CullMode::Back, - depth_bias: 0, - depth_bias_slope_scale: 0.0, + depth_bias: 2, + depth_bias_slope_scale: 1.0, depth_bias_clamp: wgpu::MAX_DEPTH_BIAS_CLAMP, }, primitive_topology: wgpu::PrimitiveTopology::TriangleList, From c21420399a6e1552df6812bae44c9d1a9d3cafae Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 22 Feb 2019 12:59:23 -0500 Subject: [PATCH 4/8] Refactored tracking, now includes the views --- wgpu-native/Cargo.toml | 1 + wgpu-native/src/binding_model.rs | 5 +- wgpu-native/src/command/allocator.rs | 5 +- wgpu-native/src/command/compute.rs | 16 +-- wgpu-native/src/command/mod.rs | 21 ++-- wgpu-native/src/command/render.rs | 50 ++++++---- wgpu-native/src/command/transfer.rs | 16 +-- wgpu-native/src/device.rs | 141 ++++++++++++++++++--------- wgpu-native/src/lib.rs | 11 +-- wgpu-native/src/resource.rs | 3 +- wgpu-native/src/swap_chain.rs | 3 +- wgpu-native/src/track.rs | 77 ++++++++++++--- wgpu-rs/Cargo.toml | 1 + wgpu-rs/src/lib.rs | 8 ++ 14 files changed, 242 insertions(+), 116 deletions(-) diff --git a/wgpu-native/Cargo.toml b/wgpu-native/Cargo.toml index 7a5c2fb5e2..dfb72223bf 100644 --- a/wgpu-native/Cargo.toml +++ b/wgpu-native/Cargo.toml @@ -18,6 +18,7 @@ crate-type = ["lib", "cdylib", "staticlib"] [features] default = [] local = ["winit", "gfx-backend-empty/winit"] +metal-auto-capture = ["gfx-backend-metal/auto-capture"] [dependencies] bitflags = "1.0" diff --git a/wgpu-native/src/binding_model.rs b/wgpu-native/src/binding_model.rs index 79b841097e..f610bdcc58 100644 --- a/wgpu-native/src/binding_model.rs +++ b/wgpu-native/src/binding_model.rs @@ -1,4 +1,4 @@ -use crate::track::{BufferTracker, TextureTracker}; +use crate::track::TrackerSet; use crate::{ LifeGuard, WeaklyStored, BindGroupLayoutId, BufferId, SamplerId, TextureViewId, @@ -84,6 +84,5 @@ pub struct BindGroup { pub(crate) raw: B::DescriptorSet, pub(crate) layout_id: WeaklyStored, pub(crate) life_guard: LifeGuard, - pub(crate) used_buffers: BufferTracker, - pub(crate) used_textures: TextureTracker, + pub(crate) used: TrackerSet, } diff --git a/wgpu-native/src/command/allocator.rs b/wgpu-native/src/command/allocator.rs index 4eaeced48d..b09b08ea3e 100644 --- a/wgpu-native/src/command/allocator.rs +++ b/wgpu-native/src/command/allocator.rs @@ -1,5 +1,5 @@ use super::CommandBuffer; -use crate::track::Tracker; +use crate::track::TrackerSet; use crate::{DeviceId, LifeGuard, Stored, SubmissionIndex}; use hal::command::RawCommandBuffer; @@ -86,8 +86,7 @@ impl CommandAllocator { recorded_thread_id: thread_id, device_id, life_guard: LifeGuard::new(), - buffer_tracker: Tracker::new(), - texture_tracker: Tracker::new(), + trackers: TrackerSet::new(), swap_chain_links: Vec::new(), } } diff --git a/wgpu-native/src/command/compute.rs b/wgpu-native/src/command/compute.rs index a4f9322418..8560c16571 100644 --- a/wgpu-native/src/command/compute.rs +++ b/wgpu-native/src/command/compute.rs @@ -1,6 +1,6 @@ use crate::command::bind::{Binder}; use crate::hub::HUB; -use crate::track::{BufferTracker, TextureTracker}; +use crate::track::TrackerSet; use crate::{ Stored, CommandBuffer, BindGroupId, CommandBufferId, ComputePassId, ComputePipelineId, @@ -16,8 +16,7 @@ pub struct ComputePass { raw: B::CommandBuffer, cmb_id: Stored, binder: Binder, - buffer_tracker: BufferTracker, - texture_tracker: TextureTracker, + trackers: TrackerSet, } impl ComputePass { @@ -26,8 +25,7 @@ impl ComputePass { raw, cmb_id, binder: Binder::default(), - buffer_tracker: BufferTracker::new(), - texture_tracker: TextureTracker::new(), + trackers: TrackerSet::new(), } } } @@ -36,6 +34,8 @@ impl ComputePass { pub extern "C" fn wgpu_compute_pass_end_pass(pass_id: ComputePassId) -> CommandBufferId { let pass = HUB.compute_passes.unregister(pass_id); + //TODO: transitions? + HUB.command_buffers .write() .get_mut(pass.cmb_id.value) @@ -69,13 +69,15 @@ pub extern "C" fn wgpu_compute_pass_set_bind_group( //Note: currently, WebGPU compute passes have synchronization defined // at a dispatch granularity, so we insert the necessary barriers here. + //TODO: have `TrackerSet::consume()` ? CommandBuffer::insert_barriers( &mut pass.raw, - pass.buffer_tracker.consume_by_replace(&bind_group.used_buffers), - pass.texture_tracker.consume_by_replace(&bind_group.used_textures), + pass.trackers.buffers.consume_by_replace(&bind_group.used.buffers), + pass.trackers.textures.consume_by_replace(&bind_group.used.textures), &*HUB.buffers.read(), &*HUB.textures.read(), ); + pass.trackers.views.consume(&bind_group.used.views); if let Some(pipeline_layout_id) = pass.binder.provide_entry(index as usize, bind_group_id, bind_group) { let pipeline_layout_guard = HUB.pipeline_layouts.read(); diff --git a/wgpu-native/src/command/mod.rs b/wgpu-native/src/command/mod.rs index 975f9f1f32..e80cdc1bbb 100644 --- a/wgpu-native/src/command/mod.rs +++ b/wgpu-native/src/command/mod.rs @@ -15,7 +15,7 @@ use crate::device::{ }; use crate::hub::{HUB, Storage}; use crate::swap_chain::{SwapChainLink, SwapImageEpoch}; -use crate::track::{BufferTracker, TextureTracker}; +use crate::track::TrackerSet; use crate::conv; use crate::{ BufferHandle, TextureHandle, @@ -85,8 +85,7 @@ pub struct CommandBuffer { recorded_thread_id: ThreadId, device_id: Stored, pub(crate) life_guard: LifeGuard, - pub(crate) buffer_tracker: BufferTracker, - pub(crate) texture_tracker: TextureTracker, + pub(crate) trackers: TrackerSet, pub(crate) swap_chain_links: Vec>, } @@ -184,7 +183,7 @@ pub fn command_encoder_begin_render_pass( }; let rp_key = { - let tracker = &mut cmb.texture_tracker; + let trackers = &mut cmb.trackers; let swap_chain_links = &mut cmb.swap_chain_links; let depth_stencil_key = depth_stencil_attachment.map(|at| { @@ -194,7 +193,12 @@ pub fn command_encoder_begin_render_pass( } else { extent = Some(view.extent); } - let query = tracker.query(&view.texture_id, TextureUsageFlags::empty()); + trackers.views.query(at.attachment, &view.life_guard.ref_count); + let query = trackers.textures.query( + view.texture_id.value, + &view.texture_id.ref_count, + TextureUsageFlags::empty(), + ); let (_, layout) = conv::map_texture_state( query.usage, hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL, @@ -232,7 +236,12 @@ pub fn command_encoder_begin_render_pass( } else { extent = Some(view.extent); } - let query = tracker.query(&view.texture_id, TextureUsageFlags::empty()); + trackers.views.query(at.attachment, &view.life_guard.ref_count); + let query = trackers.textures.query( + view.texture_id.value, + &view.texture_id.ref_count, + TextureUsageFlags::empty(), + ); let (_, layout) = conv::map_texture_state(query.usage, hal::format::Aspects::COLOR); hal::pass::Attachment { format: Some(conv::map_texture_format(view.format)), diff --git a/wgpu-native/src/command/render.rs b/wgpu-native/src/command/render.rs index bd41a1de93..8133697840 100644 --- a/wgpu-native/src/command/render.rs +++ b/wgpu-native/src/command/render.rs @@ -1,7 +1,7 @@ use crate::command::bind::Binder; use crate::hub::HUB; use crate::resource::BufferUsageFlags; -use crate::track::{BufferTracker, TextureTracker}; +use crate::track::TrackerSet; use crate::{ CommandBuffer, Stored, BindGroupId, BufferId, CommandBufferId, RenderPassId, RenderPipelineId, @@ -16,8 +16,7 @@ pub struct RenderPass { raw: B::CommandBuffer, cmb_id: Stored, binder: Binder, - buffer_tracker: BufferTracker, - texture_tracker: TextureTracker, + trackers: TrackerSet, } impl RenderPass { @@ -26,8 +25,7 @@ impl RenderPass { raw, cmb_id, binder: Binder::default(), - buffer_tracker: BufferTracker::new(), - texture_tracker: TextureTracker::new(), + trackers: TrackerSet::new(), } } } @@ -42,16 +40,27 @@ pub extern "C" fn wgpu_render_pass_end_pass(pass_id: RenderPassId) -> CommandBuf let mut cmb_guard = HUB.command_buffers.write(); let cmb = cmb_guard.get_mut(pass.cmb_id.value); - if let Some(ref mut last) = cmb.raw.last_mut() { - CommandBuffer::insert_barriers( - last, - cmb.buffer_tracker.consume_by_replace(&pass.buffer_tracker), - cmb.texture_tracker.consume_by_replace(&pass.texture_tracker), - &*HUB.buffers.read(), - &*HUB.textures.read(), - ); - unsafe { last.finish() }; + match cmb.raw.last_mut() { + Some(ref mut last) => { + CommandBuffer::insert_barriers( + last, + cmb.trackers.buffers.consume_by_replace(&pass.trackers.buffers), + cmb.trackers.textures.consume_by_replace(&pass.trackers.textures), + &*HUB.buffers.read(), + &*HUB.textures.read(), + ); + unsafe { last.finish() }; + } + None => { + cmb.trackers.buffers + .consume_by_extend(&pass.trackers.buffers) + .unwrap(); + cmb.trackers.textures + .consume_by_extend(&pass.trackers.textures) + .unwrap(); + } } + cmb.trackers.views.consume(&pass.trackers.views); cmb.raw.push(pass.raw); pass.cmb_id.value @@ -65,7 +74,7 @@ pub extern "C" fn wgpu_render_pass_set_index_buffer( let buffer_guard = HUB.buffers.read(); let pass = pass_guard.get_mut(pass_id); - let buffer = pass.buffer_tracker + let buffer = pass.trackers.buffers .get_with_extended_usage( &*buffer_guard, buffer_id, @@ -102,7 +111,7 @@ pub extern "C" fn wgpu_render_pass_set_vertex_buffers( let pass = pass_guard.get_mut(pass_id); for &id in buffers { - pass.buffer_tracker + pass.trackers.buffers .get_with_extended_usage( &*buffer_guard, id, @@ -174,12 +183,13 @@ pub extern "C" fn wgpu_render_pass_set_bind_group( let bind_group_guard = HUB.bind_groups.read(); let bind_group = bind_group_guard.get(bind_group_id); - pass.buffer_tracker - .consume_by_extend(&bind_group.used_buffers) + pass.trackers.buffers + .consume_by_extend(&bind_group.used.buffers) .unwrap(); - pass.texture_tracker - .consume_by_extend(&bind_group.used_textures) + pass.trackers.textures + .consume_by_extend(&bind_group.used.textures) .unwrap(); + pass.trackers.views.consume(&bind_group.used.views); if let Some(pipeline_layout_id) = pass.binder.provide_entry(index as usize, bind_group_id, bind_group) { let pipeline_layout_guard = HUB.pipeline_layouts.read(); diff --git a/wgpu-native/src/command/transfer.rs b/wgpu-native/src/command/transfer.rs index e8b14216c7..5c6fee9ac3 100644 --- a/wgpu-native/src/command/transfer.rs +++ b/wgpu-native/src/command/transfer.rs @@ -45,7 +45,7 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_buffer( let cmb = cmb_guard.get_mut(command_buffer_id); let buffer_guard = HUB.buffers.read(); - let (src_buffer, src_usage) = cmb.buffer_tracker + let (src_buffer, src_usage) = cmb.trackers.buffers .get_with_replaced_usage( &*buffer_guard, src, @@ -59,7 +59,7 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_buffer( range: None .. None, }); - let (dst_buffer, dst_usage) = cmb.buffer_tracker + let (dst_buffer, dst_usage) = cmb.trackers.buffers .get_with_replaced_usage( &*buffer_guard, dst, @@ -105,7 +105,7 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_texture( let buffer_guard = HUB.buffers.read(); let texture_guard = HUB.textures.read(); - let (src_buffer, src_usage) = cmb.buffer_tracker + let (src_buffer, src_usage) = cmb.trackers.buffers .get_with_replaced_usage( &*buffer_guard, source.buffer, @@ -119,7 +119,7 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_texture( range: None .. None, }); - let (dst_texture, dst_usage) = cmb.texture_tracker + let (dst_texture, dst_usage) = cmb.trackers.textures .get_with_replaced_usage( &*texture_guard, destination.texture, @@ -188,7 +188,7 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_buffer( let buffer_guard = HUB.buffers.read(); let texture_guard = HUB.textures.read(); - let (src_texture, src_usage) = cmb.texture_tracker + let (src_texture, src_usage) = cmb.trackers.textures .get_with_replaced_usage( &*texture_guard, source.texture, @@ -205,7 +205,7 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_buffer( }); assert!(src_texture.swap_chain_link.is_none()); //TODO - let (dst_buffer, dst_usage) = cmb.buffer_tracker + let (dst_buffer, dst_usage) = cmb.trackers.buffers .get_with_replaced_usage( &*buffer_guard, destination.buffer, @@ -263,14 +263,14 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_texture( let cmb = cmb_guard.get_mut(command_buffer_id); let texture_guard = HUB.textures.read(); - let (src_texture, src_usage) = cmb.texture_tracker + let (src_texture, src_usage) = cmb.trackers.textures .get_with_replaced_usage( &*texture_guard, source.texture, TextureUsageFlags::TRANSFER_SRC, ) .unwrap(); - let (dst_texture, dst_usage) = cmb.texture_tracker + let (dst_texture, dst_usage) = cmb.trackers.textures .get_with_replaced_usage( &*texture_guard, destination.texture, diff --git a/wgpu-native/src/device.rs b/wgpu-native/src/device.rs index d48d8f2e59..b56003c2ee 100644 --- a/wgpu-native/src/device.rs +++ b/wgpu-native/src/device.rs @@ -1,6 +1,6 @@ use crate::{binding_model, command, conv, pipeline, resource, swap_chain}; use crate::hub::HUB; -use crate::track::{BufferTracker, TextureTracker, TrackPermit}; +use crate::track::{TrackerSet, TrackPermit}; use crate::{ LifeGuard, RefCount, Stored, SubmissionIndex, WeaklyStored, }; @@ -64,6 +64,7 @@ pub(crate) struct FramebufferKey { } impl Eq for FramebufferKey {} +#[derive(Debug)] enum ResourceId { Buffer(BufferId), Texture(TextureId), @@ -140,29 +141,32 @@ impl DestroyedResources { impl DestroyedResources { fn triage_referenced( &mut self, - buffer_tracker: &mut BufferTracker, - texture_tracker: &mut TextureTracker, + trackers: &mut TrackerSet, ) { for i in (0..self.referenced.len()).rev() { - // one in resource itself, and one here in this list let num_refs = self.referenced[i].1.load(); - if num_refs <= 2 { - assert_eq!(num_refs, 2); + // Before destruction, a resource is expected to have the following strong refs: + // 1. in resource itself + // 2. in the device tracker + // 3. in this list + if num_refs <= 3 { let resource_id = self.referenced.swap_remove(i).0; + assert_eq!(num_refs, 3, "Resource {:?} misses some references", resource_id); let (submit_index, resource) = match resource_id { ResourceId::Buffer(id) => { - buffer_tracker.remove(id); + trackers.buffers.remove(id); let buf = HUB.buffers.unregister(id); let si = buf.life_guard.submission_index.load(Ordering::Acquire); (si, Resource::Buffer(buf)) } ResourceId::Texture(id) => { - texture_tracker.remove(id); + trackers.textures.remove(id); let tex = HUB.textures.unregister(id); let si = tex.life_guard.submission_index.load(Ordering::Acquire); (si, Resource::Texture(tex)) } ResourceId::TextureView(id) => { + trackers.views.remove(id); let view = HUB.texture_views.unregister(id); let si = view.life_guard.submission_index.load(Ordering::Acquire); (si, Resource::TextureView(view)) @@ -189,8 +193,7 @@ pub struct Device { //mem_allocator: Heaps, pub(crate) com_allocator: command::CommandAllocator, life_guard: LifeGuard, - buffer_tracker: Mutex, - pub(crate) texture_tracker: Mutex, + pub(crate) trackers: Mutex, mem_props: hal::MemoryProperties, pub(crate) render_passes: Mutex>, pub(crate) framebuffers: Mutex>, @@ -264,8 +267,7 @@ impl Device { com_allocator: command::CommandAllocator::new(queue_group.family()), queue_group, life_guard, - buffer_tracker: Mutex::new(BufferTracker::new()), - texture_tracker: Mutex::new(TextureTracker::new()), + trackers: Mutex::new(TrackerSet::new()), mem_props, render_passes: Mutex::new(HashMap::new()), framebuffers: Mutex::new(HashMap::new()), @@ -343,12 +345,10 @@ pub fn device_track_buffer( let query = HUB.devices .read() .get(device_id) - .buffer_tracker + .trackers .lock() - .query( - &Stored { value: buffer_id, ref_count }, - resource::BufferUsageFlags::empty(), - ); + .buffers + .query(buffer_id, &ref_count, resource::BufferUsageFlags::empty()); assert!(query.initialized); } @@ -458,12 +458,10 @@ pub fn device_track_texture( let query = HUB.devices .read() .get(device_id) - .texture_tracker + .trackers .lock() - .query( - &Stored { value: texture_id, ref_count }, - resource::TextureUsageFlags::UNINITIALIZED, - ); + .textures + .query(texture_id, &ref_count, resource::TextureUsageFlags::UNINITIALIZED); assert!(query.initialized); } @@ -520,6 +518,26 @@ pub fn texture_create_view( } } +pub fn device_track_view( + texture_id: TextureId, + view_id: BufferId, + ref_count: RefCount, +) { + let device_id = HUB.textures + .read() + .get(texture_id) + .device_id + .value; + let initialized = HUB.devices + .read() + .get(device_id) + .trackers + .lock() + .views + .query(view_id, &ref_count); + assert!(initialized); +} + #[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_texture_create_view( @@ -527,7 +545,11 @@ pub extern "C" fn wgpu_texture_create_view( desc: &resource::TextureViewDescriptor, ) -> TextureViewId { let view = texture_create_view(texture_id, desc); - HUB.texture_views.register(view) + let texture_id = view.texture_id.value; + let ref_count = view.life_guard.ref_count.clone(); + let id = HUB.texture_views.register(view); + device_track_view(texture_id, id, ref_count); + id } pub fn texture_create_default_view( @@ -537,8 +559,10 @@ pub fn texture_create_default_view( let texture = texture_guard.get(texture_id); let view_kind = match texture.kind { - hal::image::Kind::D1(..) => hal::image::ViewKind::D1, - hal::image::Kind::D2(..) => hal::image::ViewKind::D2, //TODO: array + hal::image::Kind::D1(_, 1) => hal::image::ViewKind::D1, + hal::image::Kind::D1(..) => hal::image::ViewKind::D1Array, + hal::image::Kind::D2(_, _, 1, _) => hal::image::ViewKind::D2, + hal::image::Kind::D2(..) => hal::image::ViewKind::D2Array, hal::image::Kind::D3(..) => hal::image::ViewKind::D3, }; @@ -575,7 +599,11 @@ pub fn texture_create_default_view( #[no_mangle] pub extern "C" fn wgpu_texture_create_default_view(texture_id: TextureId) -> TextureViewId { let view = texture_create_default_view(texture_id); - HUB.texture_views.register(view) + let texture_id = view.texture_id.value; + let ref_count = view.life_guard.ref_count.clone(); + let id = HUB.texture_views.register(view); + device_track_view(texture_id, id, ref_count); + id } #[no_mangle] @@ -679,7 +707,7 @@ pub fn device_create_bind_group_layout( hal::pso::DescriptorSetLayoutBinding { binding: binding.binding, ty: conv::map_binding_type(binding.ty), - count: bindings.len(), + count: 1, //TODO: consolidate stage_flags: conv::map_shader_stage_flags(binding.visibility), immutable_samplers: false, // TODO } @@ -771,12 +799,11 @@ pub fn device_create_bind_group( //TODO: group writes into contiguous sections let mut writes = Vec::new(); - let mut used_buffers = BufferTracker::new(); - let mut used_textures = TextureTracker::new(); + let mut used = TrackerSet::new(); for b in bindings { let descriptor = match b.resource { binding_model::BindingResource::Buffer(ref bb) => { - let buffer = used_buffers + let buffer = used.buffers .get_with_extended_usage( &*buffer_guard, bb.buffer, @@ -792,7 +819,8 @@ pub fn device_create_bind_group( } binding_model::BindingResource::TextureView(id) => { let view = texture_view_guard.get(id); - used_textures + used.views.query(id, &view.life_guard.ref_count); + used.textures .transit( view.texture_id.value, &view.texture_id.ref_count, @@ -820,8 +848,7 @@ pub fn device_create_bind_group( raw: desc_set, layout_id: WeaklyStored(desc.layout), life_guard: LifeGuard::new(), - used_buffers, - used_textures, + used, } } @@ -916,8 +943,7 @@ pub extern "C" fn wgpu_queue_submit( .life_guard .submission_index .fetch_add(1, Ordering::Relaxed); - let mut buffer_tracker = device.buffer_tracker.lock(); - let mut texture_tracker = device.texture_tracker.lock(); + let mut trackers = device.trackers.lock(); //TODO: if multiple command buffers are submitted, we can re-use the last // native command buffer of the previous chain instead of always creating @@ -934,14 +960,14 @@ pub extern "C" fn wgpu_queue_submit( // update submission IDs comb.life_guard.submission_index .store(old_submit_index, Ordering::Release); - for id in comb.buffer_tracker.used() { + for id in comb.trackers.buffers.used() { buffer_guard .get(id) .life_guard .submission_index .store(old_submit_index, Ordering::Release); } - for id in comb.texture_tracker.used() { + for id in comb.trackers.textures.used() { texture_guard .get(id) .life_guard @@ -958,13 +984,15 @@ pub extern "C" fn wgpu_queue_submit( ); } //TODO: fix the consume + let TrackerSet { ref mut buffers, ref mut textures, ref mut views } = *trackers; command::CommandBuffer::insert_barriers( &mut transit, - buffer_tracker.consume_by_replace(&comb.buffer_tracker), - texture_tracker.consume_by_replace(&comb.texture_tracker), + buffers.consume_by_replace(&comb.trackers.buffers), + textures.consume_by_replace(&comb.trackers.textures), &*buffer_guard, &*texture_guard, ); + views.consume(&comb.trackers.views); unsafe { transit.finish(); } @@ -1014,7 +1042,7 @@ pub extern "C" fn wgpu_queue_submit( let last_done = { let mut destroyed = device.destroyed.lock(); - destroyed.triage_referenced(&mut *buffer_tracker, &mut *texture_tracker); + destroyed.triage_referenced(&mut *trackers); let last_done = destroyed.cleanup(&device.raw); destroyed.active.push(ActiveSubmission { @@ -1435,6 +1463,7 @@ fn swap_chain_populate_textures( .unwrap(); let device_guard = HUB.devices.read(); let device = device_guard.get(swap_chain.device_id.value); + let mut trackers = device.trackers.lock(); for (i, mut texture) in textures.into_iter().enumerate() { let format = texture.format; @@ -1460,9 +1489,11 @@ fn swap_chain_populate_textures( ref_count: texture.life_guard.ref_count.clone(), value: HUB.textures.register(texture), }; - device.texture_tracker - .lock() - .query(&texture_id, resource::TextureUsageFlags::UNINITIALIZED); + trackers.textures.query( + texture_id.value, + &texture_id.ref_count, + resource::TextureUsageFlags::UNINITIALIZED, + ); let view = resource::TextureView { raw: view_raw, @@ -1473,12 +1504,18 @@ fn swap_chain_populate_textures( is_owned_by_swap_chain: true, life_guard: LifeGuard::new(), }; + let view_id = Stored { + ref_count: view.life_guard.ref_count.clone(), + value: HUB.texture_views.register(view), + }; + trackers.views.query( + view_id.value, + &view_id.ref_count, + ); + swap_chain.frames.push(swap_chain::Frame { texture_id, - view_id: Stored { - ref_count: view.life_guard.ref_count.clone(), - value: HUB.texture_views.register(view), - }, + view_id, fence: device.raw.create_fence(true).unwrap(), sem_available: device.raw.create_semaphore().unwrap(), sem_present: device.raw.create_semaphore().unwrap(), @@ -1507,6 +1544,8 @@ pub extern "C" fn wgpu_device_create_swap_chain( ref_count: outdated_view.life_guard.ref_count.clone(), value: HUB.texture_views.register(outdated_view), }; + device_track_view(texture_id.value, view_id.value, view_id.ref_count.clone()); + swap_chain::OutdatedFrame { texture_id, view_id, @@ -1532,8 +1571,9 @@ pub extern "C" fn wgpu_buffer_set_sub_data( //Note: this is just doing `update_buffer`, which is limited to 64KB trace!("transit {:?} to transfer dst", buffer_id); - let barrier = device.buffer_tracker + let barrier = device.trackers .lock() + .buffers .transit( buffer_id, &buffer.life_guard.ref_count, @@ -1581,3 +1621,8 @@ pub extern "C" fn wgpu_buffer_set_sub_data( device.com_allocator.after_submit(comb); } + +#[no_mangle] +pub extern "C" fn wgpu_device_destroy(device_id: BufferId) { + HUB.devices.unregister(device_id); +} \ No newline at end of file diff --git a/wgpu-native/src/lib.rs b/wgpu-native/src/lib.rs index 2667a414ea..7564fc1ee0 100644 --- a/wgpu-native/src/lib.rs +++ b/wgpu-native/src/lib.rs @@ -51,6 +51,10 @@ type SubmissionIndex = usize; #[derive(Debug)] pub struct RefCount(ptr::NonNull); +unsafe impl Send for RefCount {} +unsafe impl Sync for RefCount {} + + impl RefCount { const MAX: usize = 1 << 24; @@ -80,10 +84,6 @@ struct LifeGuard { submission_index: AtomicUsize, } -//TODO: reconsider this -unsafe impl Send for LifeGuard {} -unsafe impl Sync for LifeGuard {} - impl LifeGuard { fn new() -> Self { let bx = Box::new(AtomicUsize::new(1)); @@ -100,9 +100,6 @@ struct Stored { ref_count: RefCount, } -unsafe impl Send for Stored {} -unsafe impl Sync for Stored {} - #[derive(Clone, Debug, Hash, PartialEq, Eq)] struct WeaklyStored(T); diff --git a/wgpu-native/src/resource.rs b/wgpu-native/src/resource.rs index 9ea522f0a5..158191e9be 100644 --- a/wgpu-native/src/resource.rs +++ b/wgpu-native/src/resource.rs @@ -138,12 +138,13 @@ pub struct TextureViewDescriptor { pub struct TextureView { pub(crate) raw: B::ImageView, pub(crate) texture_id: Stored, + //TODO: store device_id for quick access? pub(crate) format: TextureFormat, pub(crate) extent: hal::image::Extent, pub(crate) samples: hal::image::NumSamples, pub(crate) is_owned_by_swap_chain: bool, #[cfg_attr(not(feature = "local"), allow(dead_code))] - pub(crate) life_guard: LifeGuard, //TODO: use + pub(crate) life_guard: LifeGuard, } #[repr(C)] diff --git a/wgpu-native/src/swap_chain.rs b/wgpu-native/src/swap_chain.rs index 16a027f32b..ea44857f8f 100644 --- a/wgpu-native/src/swap_chain.rs +++ b/wgpu-native/src/swap_chain.rs @@ -179,8 +179,9 @@ pub extern "C" fn wgpu_swap_chain_present( //TODO: support for swapchain being sampled or read by the shader? trace!("transit {:?} to present", frame.texture_id.value); - let barrier = device.texture_tracker + let barrier = device.trackers .lock() + .textures .transit( frame.texture_id.value, &texture.life_guard.ref_count, diff --git a/wgpu-native/src/track.rs b/wgpu-native/src/track.rs index f0bd086a04..700927d246 100644 --- a/wgpu-native/src/track.rs +++ b/wgpu-native/src/track.rs @@ -1,14 +1,18 @@ use crate::hub::{Id, Storage}; use crate::resource::{BufferUsageFlags, TextureUsageFlags}; -use crate::{BufferId, RefCount, Stored, TextureId, WeaklyStored}; +use crate::{ + RefCount, WeaklyStored, + BufferId, TextureId, TextureViewId, +}; use std::borrow::Borrow; -use std::collections::hash_map::{Entry, HashMap}; +use std::collections::hash_map::Entry; use std::hash::Hash; use std::mem; use std::ops::{BitOr, Range}; use bitflags::bitflags; +use hal::backend::FastHashMap; #[derive(Clone, Debug, PartialEq)] @@ -70,20 +74,38 @@ struct Track { last: U, } -unsafe impl Send for Track {} -unsafe impl Sync for Track {} - //TODO: consider having `I` as an associated type of `U`? pub struct Tracker { - map: HashMap, Track>, + map: FastHashMap, Track>, } pub type BufferTracker = Tracker; pub type TextureTracker = Tracker; +pub struct DummyTracker { + map: FastHashMap, RefCount>, +} +pub type TextureViewTracker = DummyTracker; -impl + PartialEq> Tracker { +pub struct TrackerSet { + pub buffers: BufferTracker, + pub textures: TextureTracker, + pub views: TextureViewTracker, + //TODO: samplers +} + +impl TrackerSet { pub fn new() -> Self { - Tracker { - map: HashMap::new(), + TrackerSet { + buffers: BufferTracker::new(), + textures: TextureTracker::new(), + views: TextureViewTracker::new(), + } + } +} + +impl DummyTracker { + pub fn new() -> Self { + DummyTracker { + map: FastHashMap::default(), } } @@ -93,11 +115,42 @@ impl + PartialE } /// Get the last usage on a resource. - pub(crate) fn query(&mut self, stored: &Stored, default: U) -> Query { - match self.map.entry(WeaklyStored(stored.value.clone())) { + pub(crate) fn query(&mut self, id: I, ref_count: &RefCount) -> bool { + match self.map.entry(WeaklyStored(id)) { + Entry::Vacant(e) => { + e.insert(ref_count.clone()); + true + } + Entry::Occupied(_) => false, + } + } + + /// Consume another tacker. + pub fn consume(&mut self, other: &Self) { + for (id, ref_count) in &other.map { + self.query(id.0.clone(), ref_count); + } + } +} + +impl + PartialEq> Tracker { + pub fn new() -> Self { + Tracker { + map: FastHashMap::default(), + } + } + + /// Remove an id from the tracked map. + pub(crate) fn remove(&mut self, id: I) -> bool { + self.map.remove(&WeaklyStored(id)).is_some() + } + + /// Get the last usage on a resource. + pub(crate) fn query(&mut self, id: I, ref_count: &RefCount, default: U) -> Query { + match self.map.entry(WeaklyStored(id)) { Entry::Vacant(e) => { e.insert(Track { - ref_count: stored.ref_count.clone(), + ref_count: ref_count.clone(), init: default, last: default, }); diff --git a/wgpu-rs/Cargo.toml b/wgpu-rs/Cargo.toml index 27a3bbf615..b42dcd7e7a 100644 --- a/wgpu-rs/Cargo.toml +++ b/wgpu-rs/Cargo.toml @@ -16,6 +16,7 @@ license = "MPL-2.0" [features] default = [] +metal-auto-capture = ["wgpu-native/metal-auto-capture"] metal = ["wgpu-native/gfx-backend-metal"] dx11 = ["wgpu-native/gfx-backend-dx11"] dx12 = ["wgpu-native/gfx-backend-dx12"] diff --git a/wgpu-rs/src/lib.rs b/wgpu-rs/src/lib.rs index c0ad705dcf..5c2c5304a5 100644 --- a/wgpu-rs/src/lib.rs +++ b/wgpu-rs/src/lib.rs @@ -443,6 +443,14 @@ impl Device { } } +impl Drop for Device { + fn drop(&mut self) { + //TODO: make this work in general + #[cfg(feature = "metal-auto-capture")] + wgn::wgpu_device_destroy(self.id); + } +} + impl Buffer { pub fn set_sub_data(&self, offset: u32, data: &[u8]) { wgn::wgpu_buffer_set_sub_data(self.id, offset, data.len() as u32, data.as_ptr()); From 5ada8d994377c9fefb143e5acae2b7c28a21cd54 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 22 Feb 2019 13:53:28 -0500 Subject: [PATCH 5/8] Fix full_range of arrays --- gfx-examples/data/shadow-forward.frag | 1 - gfx-examples/src/shadow.rs | 2 +- wgpu-native/src/device.rs | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/gfx-examples/data/shadow-forward.frag b/gfx-examples/data/shadow-forward.frag index 0928adce86..9a2bd8fc67 100644 --- a/gfx-examples/data/shadow-forward.frag +++ b/gfx-examples/data/shadow-forward.frag @@ -39,7 +39,6 @@ void main() { // project into the light space vec4 light_local = light.proj * v_Position; // compute texture coordinates for shadow lookup - light_local.y *= -1.0; // difference in Vulkan target versus texture coordinates... light_local.xyw = (light_local.xyz/light_local.w + 1.0) / 2.0; light_local.z = i; // do the lookup, using HW PCF and comparison diff --git a/gfx-examples/src/shadow.rs b/gfx-examples/src/shadow.rs index 5200f6da93..56f52f059e 100644 --- a/gfx-examples/src/shadow.rs +++ b/gfx-examples/src/shadow.rs @@ -113,7 +113,7 @@ impl Light { let mx_view = Matrix4::look_at( self.pos, Point3::origin(), - Vector3::unit_z(), + -Vector3::unit_z(), ); let projection = PerspectiveFov { fovy: Deg(self.fov).into(), diff --git a/wgpu-native/src/device.rs b/wgpu-native/src/device.rs index b56003c2ee..fad6eb6342 100644 --- a/wgpu-native/src/device.rs +++ b/wgpu-native/src/device.rs @@ -442,8 +442,8 @@ pub fn device_create_texture( format: desc.format, full_range: hal::image::SubresourceRange { aspects, - levels: 0..1, //TODO: mips - layers: 0..1, //TODO + levels: 0 .. 1, //TODO: mips + layers: 0 .. desc.array_size as u16, }, swap_chain_link: None, life_guard: LifeGuard::new(), From 9df7cc4a8ee446cdf51bc10484318299c56f834f Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 22 Feb 2019 17:01:56 -0500 Subject: [PATCH 6/8] Fix depth projection in the examples, add some rotation to the shadow --- gfx-examples/data/cube.vert | 2 + gfx-examples/data/shadow-bake.vert | 2 + gfx-examples/data/shadow-forward.vert | 2 + gfx-examples/src/cube.rs | 16 +++--- gfx-examples/src/framework.rs | 7 ++- gfx-examples/src/shadow.rs | 82 +++++++++++++++++++++++---- 6 files changed, 89 insertions(+), 22 deletions(-) diff --git a/gfx-examples/data/cube.vert b/gfx-examples/data/cube.vert index 6a4028e7a1..badc7fc203 100644 --- a/gfx-examples/data/cube.vert +++ b/gfx-examples/data/cube.vert @@ -11,4 +11,6 @@ layout(set = 0, binding = 0) uniform Locals { void main() { v_TexCoord = a_TexCoord; gl_Position = u_Transform * a_Pos; + // convert from -1,1 Z to 0,1 + gl_Position.z = 0.5 * (gl_Position.z + gl_Position.w); } diff --git a/gfx-examples/data/shadow-bake.vert b/gfx-examples/data/shadow-bake.vert index e4426b7455..32e257c6b7 100644 --- a/gfx-examples/data/shadow-bake.vert +++ b/gfx-examples/data/shadow-bake.vert @@ -13,4 +13,6 @@ layout(set = 1, binding = 0) uniform Entity { void main() { gl_Position = u_ViewProj * u_World * vec4(a_Pos); + // convert from -1,1 Z to 0,1 + gl_Position.z = 0.5 * (gl_Position.z + gl_Position.w); } diff --git a/gfx-examples/data/shadow-forward.vert b/gfx-examples/data/shadow-forward.vert index 5e02939393..f12281db10 100644 --- a/gfx-examples/data/shadow-forward.vert +++ b/gfx-examples/data/shadow-forward.vert @@ -19,4 +19,6 @@ void main() { v_Normal = mat3(u_World) * vec3(a_Normal.xyz); v_Position = u_World * vec4(a_Pos); gl_Position = u_ViewProj * v_Position; + // convert from -1,1 Z to 0,1 + gl_Position.z = 0.5 * (gl_Position.z + gl_Position.w); } diff --git a/gfx-examples/src/cube.rs b/gfx-examples/src/cube.rs index b58e2a7acf..e1ef403245 100644 --- a/gfx-examples/src/cube.rs +++ b/gfx-examples/src/cube.rs @@ -105,7 +105,7 @@ impl Example { } impl framework::Example for Example { - fn init(device: &mut wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor) -> Self { + fn init(sc_desc: &wgpu::SwapChainDescriptor, device: &mut wgpu::Device) -> Self { use std::mem; let mut init_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { @@ -303,12 +303,14 @@ impl framework::Example for Example { } } - fn update(&mut self, event: wgpu::winit::WindowEvent) { - if let wgpu::winit::WindowEvent::Resized(size) = event { - let mx_total = Self::generate_matrix(size.width as f32 / size.height as f32); - let mx_ref: &[f32; 16] = mx_total.as_ref(); - self.uniform_buf.set_sub_data(0, framework::cast_slice(&mx_ref[..])); - } + fn update(&mut self, _event: wgpu::winit::WindowEvent) { + //empty + } + + fn resize(&mut self, sc_desc: &wgpu::SwapChainDescriptor, _device: &mut wgpu::Device) { + let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32); + let mx_ref: &[f32; 16] = mx_total.as_ref(); + self.uniform_buf.set_sub_data(0, framework::cast_slice(&mx_ref[..])); } fn render(&mut self, frame: &wgpu::SwapChainOutput, device: &mut wgpu::Device) { diff --git a/gfx-examples/src/framework.rs b/gfx-examples/src/framework.rs index e5cb1a319a..6d1561637f 100644 --- a/gfx-examples/src/framework.rs +++ b/gfx-examples/src/framework.rs @@ -42,7 +42,8 @@ pub fn load_glsl(name: &str, stage: ShaderStage) -> Vec { } pub trait Example { - fn init(device: &mut wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor) -> Self; + fn init(sc_desc: &wgpu::SwapChainDescriptor, device: &mut wgpu::Device) -> Self; + fn resize(&mut self, sc_desc: &wgpu::SwapChainDescriptor, device: &mut wgpu::Device); fn update(&mut self, event: wgpu::winit::WindowEvent); fn render(&mut self, frame: &wgpu::SwapChainOutput, device: &mut wgpu::Device); } @@ -83,7 +84,7 @@ pub fn run(title: &str) { let mut swap_chain = device.create_swap_chain(&surface, &sc_desc); info!("Initializing the example..."); - let mut example = E::init(&mut device, &sc_desc); + let mut example = E::init(&sc_desc, &mut device); info!("Entering render loop..."); let mut running = true; @@ -99,7 +100,7 @@ pub fn run(title: &str) { sc_desc.width = physical.width as u32; sc_desc.height = physical.height as u32; swap_chain = device.create_swap_chain(&surface, &sc_desc); - example.update(WindowEvent::Resized(size)); + example.resize(&sc_desc, &mut device); } Event::WindowEvent { event, .. } => match event { WindowEvent::KeyboardInput { diff --git a/gfx-examples/src/shadow.rs b/gfx-examples/src/shadow.rs index 56f52f059e..ca2b8fad6c 100644 --- a/gfx-examples/src/shadow.rs +++ b/gfx-examples/src/shadow.rs @@ -83,6 +83,7 @@ fn create_plane(size: i8) -> (Vec, Vec) { struct Entity { mx_world: cgmath::Matrix4, + rotation_speed: f32, color: wgpu::Color, vertex_buf: Rc, index_buf: Rc, @@ -159,6 +160,7 @@ struct Example { lights_are_dirty: bool, shadow_pass: Pass, forward_pass: Pass, + forward_depth: wgpu::TextureView, light_uniform_buf: wgpu::Buffer, } @@ -170,6 +172,7 @@ impl Example { height: 512, depth: 1, }; + const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::D32Float; fn generate_matrix(aspect_ratio: f32) -> cgmath::Matrix4 { let mx_projection = cgmath::perspective(cgmath::Deg(45f32), aspect_ratio, 1.0, 20.0); @@ -183,7 +186,7 @@ impl Example { } impl framework::Example for Example { - fn init(device: &mut wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor) -> Self { + fn init(sc_desc: &wgpu::SwapChainDescriptor, device: &mut wgpu::Device) -> Self { // Create the vertex and index buffers let vertex_size = mem::size_of::(); let (cube_vertex_data, cube_index_data) = create_cube(); @@ -242,6 +245,7 @@ impl framework::Example for Example { }); Entity { mx_world: cgmath::Matrix4::identity(), + rotation_speed: 0.0, color: wgpu::Color::WHITE, vertex_buf: Rc::new(plane_vertex_buf), index_buf: Rc::new(plane_index_buf), @@ -255,27 +259,32 @@ impl framework::Example for Example { offset: cgmath::Vector3, angle: f32, scale: f32, + rotation: f32, } let cube_descs = [ CubeDesc { offset: cgmath::vec3(-2.0, -2.0, 2.0), angle: 10.0, scale: 0.7, + rotation: 1.0, }, CubeDesc { offset: cgmath::vec3(2.0, -2.0, 2.0), angle: 50.0, scale: 1.3, + rotation: 2.0, }, CubeDesc { offset: cgmath::vec3(-2.0, 2.0, 2.0), angle: 140.0, scale: 1.1, + rotation: 3.0, }, CubeDesc { offset: cgmath::vec3(2.0, 2.0, 2.0), angle: 210.0, scale: 0.9, + rotation: 4.0, }, ]; @@ -296,6 +305,7 @@ impl framework::Example for Example { }); entities.push(Entity { mx_world: cgmath::Matrix4::from(transform), + rotation_speed: cube.rotation, color: wgpu::Color::GREEN, vertex_buf: Rc::clone(&cube_vertex_buf), index_buf: Rc::clone(&cube_index_buf), @@ -447,8 +457,8 @@ impl framework::Example for Example { rasterization_state: wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Cw, cull_mode: wgpu::CullMode::Back, - depth_bias: 2, - depth_bias_slope_scale: 1.0, + depth_bias: 2, // corresponds to bilinear filtering + depth_bias_slope_scale: 2.0, depth_bias_clamp: wgpu::MAX_DEPTH_BIAS_CLAMP, }, primitive_topology: wgpu::PrimitiveTopology::TriangleList, @@ -580,7 +590,15 @@ impl framework::Example for Example { write_mask: wgpu::ColorWriteFlags::ALL, }, ], - depth_stencil_state: None, + depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { + format: Self::DEPTH_FORMAT, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, + stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE, + stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE, + stencil_read_mask: 0, + stencil_write_mask: 0, + }), index_format: wgpu::IndexFormat::Uint16, vertex_buffers: &[vb_desc], sample_count: 1, @@ -593,26 +611,58 @@ impl framework::Example for Example { } }; + let depth_texture = device.create_texture(&wgpu::TextureDescriptor { + size: wgpu::Extent3d { + width: sc_desc.width, + height: sc_desc.height, + depth: 1, + }, + array_size: 1, + dimension: wgpu::TextureDimension::D2, + format: Self::DEPTH_FORMAT, + usage: wgpu::TextureUsageFlags::OUTPUT_ATTACHMENT, + }); + Example { entities, lights, lights_are_dirty: true, shadow_pass, forward_pass, + forward_depth: depth_texture.create_default_view(), light_uniform_buf, } } - fn update(&mut self, event: wgpu::winit::WindowEvent) { - if let wgpu::winit::WindowEvent::Resized(size) = event { - let mx_total = Self::generate_matrix(size.width as f32 / size.height as f32); - let mx_ref: &[f32; 16] = mx_total.as_ref(); - self.forward_pass.uniform_buf.set_sub_data(0, framework::cast_slice(&mx_ref[..])); - } + fn update(&mut self, _event: wgpu::winit::WindowEvent) { + //empty + } + + fn resize(&mut self, sc_desc: &wgpu::SwapChainDescriptor, device: &mut wgpu::Device) { + let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32); + let mx_ref: &[f32; 16] = mx_total.as_ref(); + self.forward_pass.uniform_buf.set_sub_data(0, framework::cast_slice(&mx_ref[..])); + + let depth_texture = device.create_texture(&wgpu::TextureDescriptor { + size: wgpu::Extent3d { + width: sc_desc.width, + height: sc_desc.height, + depth: 1, + }, + array_size: 1, + dimension: wgpu::TextureDimension::D2, + format: Self::DEPTH_FORMAT, + usage: wgpu::TextureUsageFlags::OUTPUT_ATTACHMENT, + }); + self.forward_depth = depth_texture.create_default_view(); } fn render(&mut self, frame: &wgpu::SwapChainOutput, device: &mut wgpu::Device) { - for entity in &self.entities { + for entity in &mut self.entities { + if entity.rotation_speed != 0.0 { + let rotation = cgmath::Matrix4::from_angle_x(cgmath::Deg(entity.rotation_speed)); + entity.mx_world = entity.mx_world * rotation; + } let data = EntityUniforms { model: *entity.mx_world.as_ref(), color: [entity.color.r, entity.color.g, entity.color.b, entity.color.a], @@ -673,7 +723,15 @@ impl framework::Example for Example { store_op: wgpu::StoreOp::Store, clear_color: wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0 }, }], - depth_stencil_attachment: None, + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { + attachment: &self.forward_depth, + depth_load_op: wgpu::LoadOp::Clear, + depth_store_op: wgpu::StoreOp::Store, + stencil_load_op: wgpu::LoadOp::Clear, + stencil_store_op: wgpu::StoreOp::Store, + clear_depth: 1.0, + clear_stencil: 0, + }), }); pass.set_pipeline(&self.forward_pass.pipeline); pass.set_bind_group(0, &self.forward_pass.bind_group); From cfe3da1c6f4e30af0b0239a58cd793450044dca6 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 22 Feb 2019 18:42:14 -0500 Subject: [PATCH 7/8] Remove the outdated frame concept in favor of aggressive swapchain re-creation --- wgpu-native/src/device.rs | 37 +++--------- wgpu-native/src/swap_chain.rs | 103 ++++++++++++++++++---------------- 2 files changed, 63 insertions(+), 77 deletions(-) diff --git a/wgpu-native/src/device.rs b/wgpu-native/src/device.rs index fad6eb6342..0bee3e49d3 100644 --- a/wgpu-native/src/device.rs +++ b/wgpu-native/src/device.rs @@ -22,7 +22,7 @@ use hal::{self, Device as _Device, Surface as _Surface, }; -use log::trace; +use log::{info, trace}; //use rendy_memory::{allocator, Config, Heaps}; use parking_lot::{Mutex}; @@ -1343,8 +1343,9 @@ pub fn device_create_swap_chain( device_id: DeviceId, surface_id: SurfaceId, desc: &swap_chain::SwapChainDescriptor, - outdated: swap_chain::OutdatedFrame, ) -> Vec> { + info!("creating swap chain {:?}", desc); + let device_guard = HUB.devices.read(); let device = device_guard.get(device_id); let mut surface_guard = HUB.surfaces.write(); @@ -1357,6 +1358,7 @@ pub fn device_create_swap_chain( surface.raw.compatibility(&adapter.physical_device) }; let num_frames = caps.image_count.start; //TODO: configure? + let usage = conv::map_texture_usage(desc.usage, hal::format::Aspects::COLOR); let config = hal::SwapchainConfig::new( desc.width, desc.height, @@ -1364,7 +1366,6 @@ pub fn device_create_swap_chain( num_frames, //TODO: configure? ); - let usage = conv::map_texture_usage(desc.usage, hal::format::Aspects::COLOR); if let Some(formats) = formats { assert!(formats.contains(&config.format), "Requested format {:?} is not in supported list: {:?}", @@ -1380,9 +1381,6 @@ pub fn device_create_swap_chain( let (old_raw, sem_available, command_pool) = match surface.swap_chain.take() { Some(mut old) => { assert_eq!(old.device_id.value, device_id); - let mut destroyed = device.destroyed.lock(); - destroyed.add(ResourceId::Texture(old.outdated.texture_id.value), old.outdated.texture_id.ref_count); - destroyed.add(ResourceId::TextureView(old.outdated.view_id.value), old.outdated.view_id.ref_count); unsafe { old.command_pool.reset() }; @@ -1417,10 +1415,10 @@ pub fn device_create_swap_chain( value: device_id, ref_count: device.life_guard.ref_count.clone(), }, + desc: desc.clone(), frames: Vec::with_capacity(num_frames as usize), acquired: Vec::with_capacity(1), //TODO: get it from gfx-hal? sem_available, - outdated, command_pool, }); @@ -1451,7 +1449,7 @@ pub fn device_create_swap_chain( } #[cfg(feature = "local")] -fn swap_chain_populate_textures( +pub fn swap_chain_populate_textures( swap_chain_id: SwapChainId, textures: Vec>, ) { @@ -1531,28 +1529,7 @@ pub extern "C" fn wgpu_device_create_swap_chain( surface_id: SurfaceId, desc: &swap_chain::SwapChainDescriptor, ) -> SwapChainId { - let outdated = { - let outdated_texture = device_create_texture(device_id, &desc.to_texture_desc()); - let texture_id = Stored { - ref_count: outdated_texture.life_guard.ref_count.clone(), - value: HUB.textures.register(outdated_texture), - }; - device_track_texture(device_id, texture_id.value, texture_id.ref_count.clone()); - - let outdated_view = texture_create_default_view(texture_id.value); - let view_id = Stored { - ref_count: outdated_view.life_guard.ref_count.clone(), - value: HUB.texture_views.register(outdated_view), - }; - device_track_view(texture_id.value, view_id.value, view_id.ref_count.clone()); - - swap_chain::OutdatedFrame { - texture_id, - view_id, - } - }; - - let textures = device_create_swap_chain(device_id, surface_id, desc, outdated); + let textures = device_create_swap_chain(device_id, surface_id, desc); swap_chain_populate_textures(surface_id, textures); surface_id } diff --git a/wgpu-native/src/swap_chain.rs b/wgpu-native/src/swap_chain.rs index ea44857f8f..e505cf9247 100644 --- a/wgpu-native/src/swap_chain.rs +++ b/wgpu-native/src/swap_chain.rs @@ -45,26 +45,20 @@ pub(crate) struct Frame { pub comb: hal::command::CommandBuffer, } -pub struct OutdatedFrame { - pub(crate) texture_id: Stored, - pub(crate) view_id: Stored, -} - -const OUTDATED_IMAGE_INDEX: u32 = !0; //TODO: does it need a ref-counted lifetime? - pub struct SwapChain { pub(crate) raw: B::Swapchain, pub(crate) device_id: Stored, + pub(crate) desc: SwapChainDescriptor, pub(crate) frames: Vec>, pub(crate) acquired: Vec, pub(crate) sem_available: B::Semaphore, - pub(crate) outdated: OutdatedFrame, #[cfg_attr(not(feature = "local"), allow(dead_code))] //TODO: remove pub(crate) command_pool: hal::CommandPool, } #[repr(C)] +#[derive(Clone, Debug)] pub struct SwapChainDescriptor { pub usage: resource::TextureUsageFlags, pub format: resource::TextureFormat, @@ -98,51 +92,72 @@ pub struct SwapChainOutput { pub extern "C" fn wgpu_swap_chain_get_next_texture( swap_chain_id: SwapChainId, ) -> SwapChainOutput { + let (image_index, device_id, descriptor) = { + let mut surface_guard = HUB.surfaces.write(); + let swap_chain = surface_guard + .get_mut(swap_chain_id) + .swap_chain + .as_mut() + .unwrap(); + let sync = hal::FrameSync::Semaphore(&swap_chain.sem_available); + let result = unsafe { + swap_chain.raw.acquire_image(!0, sync) + }; + (result.ok(), swap_chain.device_id.value, swap_chain.desc.clone()) + }; + + #[cfg(not(feature = "local"))] + let _ = descriptor; + #[cfg(feature = "local")] + { + use crate::device::{device_create_swap_chain, swap_chain_populate_textures}; + if image_index.is_none() { + warn!("acquire_image failed, re-creating"); + let textures = device_create_swap_chain(device_id, swap_chain_id, &descriptor); + swap_chain_populate_textures(swap_chain_id, textures); + } + } + let mut surface_guard = HUB.surfaces.write(); let swap_chain = surface_guard .get_mut(swap_chain_id) .swap_chain .as_mut() .unwrap(); + + let image_index = match image_index { + Some(index) => index, + None => { + let sync = hal::FrameSync::Semaphore(&swap_chain.sem_available); + unsafe { swap_chain.raw.acquire_image(!0, sync) }.unwrap() + } + }; + let device_guard = HUB.devices.read(); - let device = device_guard.get(swap_chain.device_id.value); + let device = device_guard.get(device_id); assert_ne!(swap_chain.acquired.len(), swap_chain.acquired.capacity(), "Unable to acquire any more swap chain images before presenting"); + swap_chain.acquired.push(image_index); - match { - let sync = hal::FrameSync::Semaphore(&swap_chain.sem_available); - unsafe { swap_chain.raw.acquire_image(!0, sync) } - } { - Ok(image_index) => { - swap_chain.acquired.push(image_index); - let frame = &mut swap_chain.frames[image_index as usize]; - unsafe { - device.raw.wait_for_fence(&frame.fence, !0).unwrap(); - } + let frame = &mut swap_chain.frames[image_index as usize]; + unsafe { + device.raw.wait_for_fence(&frame.fence, !0).unwrap(); + } + mem::swap(&mut frame.sem_available, &mut swap_chain.sem_available); - mem::swap(&mut frame.sem_available, &mut swap_chain.sem_available); + match HUB.textures + .read() + .get(frame.texture_id.value) + .swap_chain_link + { + Some(ref link) => *link.epoch.lock() += 1, + None => unreachable!(), + } - let texture_guard = HUB.textures.read(); - let texture = texture_guard.get(frame.texture_id.value); - match texture.swap_chain_link { - Some(ref link) => *link.epoch.lock() += 1, - None => unreachable!(), - } - - SwapChainOutput { - texture_id: frame.texture_id.value, - view_id: frame.view_id.value, - } - } - Err(e) => { - warn!("acquire_image failed: {:?}", e); - swap_chain.acquired.push(OUTDATED_IMAGE_INDEX); - SwapChainOutput { - texture_id: swap_chain.outdated.texture_id.value, - view_id: swap_chain.outdated.view_id.value, - } - } + SwapChainOutput { + texture_id: frame.texture_id.value, + view_id: frame.view_id.value, } } @@ -158,13 +173,7 @@ pub extern "C" fn wgpu_swap_chain_present( .unwrap(); let image_index = swap_chain.acquired.remove(0); - let frame = match swap_chain.frames.get_mut(image_index as usize) { - Some(frame) => frame, - None => { - assert_eq!(image_index, OUTDATED_IMAGE_INDEX); - return - } - }; + let frame = &mut swap_chain.frames[image_index as usize]; let mut device_guard = HUB.devices.write(); let device = device_guard.get_mut(swap_chain.device_id.value); From 616a3dd849c87db72db37d534bb21ddc700a2165 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 22 Feb 2019 22:23:52 -0500 Subject: [PATCH 8/8] Fix leaking swapchain textures --- wgpu-native/src/device.rs | 22 +++++++++++++++------- wgpu-rs/src/lib.rs | 19 ++++++++++++++++--- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/wgpu-native/src/device.rs b/wgpu-native/src/device.rs index 0bee3e49d3..a7da89d545 100644 --- a/wgpu-native/src/device.rs +++ b/wgpu-native/src/device.rs @@ -15,9 +15,11 @@ use crate::{ }; use back; +use hal::backend::FastHashMap; use hal::command::RawCommandBuffer; use hal::queue::RawCommandQueue; -use hal::{self, +use hal::{ + self, DescriptorPool as _DescriptorPool, Device as _Device, Surface as _Surface, @@ -27,7 +29,7 @@ use log::{info, trace}; use parking_lot::{Mutex}; use std::{ffi, iter, slice}; -use std::collections::hash_map::{Entry, HashMap}; +use std::collections::hash_map::Entry; use std::sync::atomic::Ordering; @@ -64,7 +66,7 @@ pub(crate) struct FramebufferKey { } impl Eq for FramebufferKey {} -#[derive(Debug)] +#[derive(Debug, PartialEq)] enum ResourceId { Buffer(BufferId), Texture(TextureId), @@ -100,6 +102,7 @@ unsafe impl Sync for DestroyedResources {} impl DestroyedResources { fn add(&mut self, resource_id: ResourceId, ref_count: RefCount) { + debug_assert!(!self.referenced.iter().any(|r| r.0 == resource_id)); self.referenced.push((resource_id, ref_count)); } @@ -195,8 +198,8 @@ pub struct Device { life_guard: LifeGuard, pub(crate) trackers: Mutex, mem_props: hal::MemoryProperties, - pub(crate) render_passes: Mutex>, - pub(crate) framebuffers: Mutex>, + pub(crate) render_passes: Mutex>, + pub(crate) framebuffers: Mutex>, desc_pool: Mutex, destroyed: Mutex>, } @@ -269,8 +272,8 @@ impl Device { life_guard, trackers: Mutex::new(TrackerSet::new()), mem_props, - render_passes: Mutex::new(HashMap::new()), - framebuffers: Mutex::new(HashMap::new()), + render_passes: Mutex::new(FastHashMap::default()), + framebuffers: Mutex::new(FastHashMap::default()), desc_pool, destroyed: Mutex::new(DestroyedResources { referenced: Vec::new(), @@ -1380,7 +1383,12 @@ pub fn device_create_swap_chain( let (old_raw, sem_available, command_pool) = match surface.swap_chain.take() { Some(mut old) => { + let mut destroyed = device.destroyed.lock(); assert_eq!(old.device_id.value, device_id); + for frame in old.frames { + destroyed.add(ResourceId::Texture(frame.texture_id.value), frame.texture_id.ref_count); + destroyed.add(ResourceId::TextureView(frame.view_id.value), frame.view_id.ref_count); + } unsafe { old.command_pool.reset() }; diff --git a/wgpu-rs/src/lib.rs b/wgpu-rs/src/lib.rs index 5c2c5304a5..036c64d1c1 100644 --- a/wgpu-rs/src/lib.rs +++ b/wgpu-rs/src/lib.rs @@ -54,10 +54,12 @@ pub struct Buffer { pub struct Texture { id: wgn::TextureId, + owned: bool, } pub struct TextureView { id: wgn::TextureViewId, + owned: bool, } pub struct Sampler { @@ -427,6 +429,7 @@ impl Device { pub fn create_texture(&self, desc: &TextureDescriptor) -> Texture { Texture { id: wgn::wgpu_device_create_texture(self.id, desc), + owned: true, } } @@ -467,25 +470,31 @@ impl Texture { pub fn create_view(&self, desc: &TextureViewDescriptor) -> TextureView { TextureView { id: wgn::wgpu_texture_create_view(self.id, desc), + owned: true, } } pub fn create_default_view(&self) -> TextureView { TextureView { id: wgn::wgpu_texture_create_default_view(self.id), + owned: true, } } } impl Drop for Texture { fn drop(&mut self) { - wgn::wgpu_texture_destroy(self.id); + if self.owned { + wgn::wgpu_texture_destroy(self.id); + } } } impl Drop for TextureView { fn drop(&mut self) { - wgn::wgpu_texture_view_destroy(self.id); + if self.owned { + wgn::wgpu_texture_view_destroy(self.id); + } } } @@ -707,8 +716,12 @@ impl SwapChain { SwapChainOutput { texture: Texture { id: output.texture_id, + owned: false, + }, + view: TextureView { + id: output.view_id, + owned: false, }, - view: TextureView { id: output.view_id }, swap_chain_id: &self.id, } }