From 0ea4cac04bddaa438c55f52c4c4f4833953ef7f3 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sat, 5 Jun 2021 01:44:21 -0400 Subject: [PATCH] Rework shader and pipeline creation --- Cargo.lock | 2 + Cargo.toml | 2 +- wgpu-core/src/binding_model.rs | 10 +- wgpu-core/src/device/mod.rs | 711 +++++++++++---------------------- wgpu-core/src/hub.rs | 6 +- wgpu-core/src/instance.rs | 27 +- wgpu-core/src/pipeline.rs | 9 +- wgpu-core/src/resource.rs | 51 +-- wgpu-core/src/track/mod.rs | 45 +-- wgpu-hal/Cargo.toml | 5 + wgpu-hal/src/empty.rs | 149 ++++--- wgpu-hal/src/lib.rs | 389 +++++++++++++++--- 12 files changed, 716 insertions(+), 690 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f3daee6a..91b986562 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1890,6 +1890,8 @@ name = "wgpu-hal" version = "0.1.0" dependencies = [ "bitflags", + "naga", + "smallvec", "wgpu-types", ] diff --git a/Cargo.toml b/Cargo.toml index fa966a3a0..1fbd783c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ members = [ "wgpu-hal", "wgpu-types", ] -default-members = ["wgpu"] +default-members = ["wgpu", "player"] [patch."https://github.com/gfx-rs/naga"] #naga = { path = "../naga" } diff --git a/wgpu-core/src/binding_model.rs b/wgpu-core/src/binding_model.rs index 4e1037f96..6864e0f12 100644 --- a/wgpu-core/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -3,10 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::{ - device::{ - descriptor::{DescriptorSet, DescriptorTotalCount}, - DeviceError, MissingFeatures, SHADER_STAGE_COUNT, - }, + device::{descriptor::DescriptorSet, DeviceError, MissingFeatures, SHADER_STAGE_COUNT}, hub::Resource, id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureViewId, Valid}, memory_init_tracker::MemoryInitTrackerAction, @@ -390,11 +387,10 @@ pub(crate) type BindEntryMap = FastHashMap; #[derive(Debug)] pub struct BindGroupLayout { - pub(crate) raw: B::DescriptorSetLayout, + pub(crate) raw: A::BindGroupLayout, pub(crate) device_id: Stored, pub(crate) multi_ref_count: MultiRefCount, pub(crate) entries: BindEntryMap, - pub(crate) desc_count: DescriptorTotalCount, pub(crate) dynamic_count: usize, pub(crate) count_validator: BindingTypeMaxCountValidator, #[cfg(debug_assertions)] @@ -499,7 +495,7 @@ pub struct PipelineLayoutDescriptor<'a> { #[derive(Debug)] pub struct PipelineLayout { - pub(crate) raw: B::PipelineLayout, + pub(crate) raw: A::PipelineLayout, pub(crate) device_id: Stored, pub(crate) life_guard: LifeGuard, pub(crate) bind_group_layout_ids: ArrayVec<[Valid; hal::MAX_BIND_GROUPS]>, diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 5c1f4445f..e86a9f213 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -569,52 +569,30 @@ impl Device { .as_ref() .ok_or(resource::CreateTextureViewError::InvalidTexture)?; - let view_dim = - match desc.dimension { - Some(dim) => { - let required_tex_dim = dim.compatible_texture_dimension(); - - if required_tex_dim != texture.dimension { - return Err( - resource::CreateTextureViewError::InvalidTextureViewDimension { - view: dim, - image: texture.dimension, - }, - ); - } - - if let Kind::D2(_, _, depth, _) = texture.kind { - match dim { - TextureViewDimension::Cube if depth != 6 => { - return Err( - resource::CreateTextureViewError::InvalidCubemapTextureDepth { - depth, - }, - ) - } - TextureViewDimension::CubeArray if depth % 6 != 0 => return Err( - resource::CreateTextureViewError::InvalidCubemapArrayTextureDepth { - depth, - }, - ), - _ => {} - } - } - - dim + let view_dim = match desc.dimension { + Some(dim) => { + if texture.dimension != dim.compatible_texture_dimension() { + return Err( + resource::CreateTextureViewError::InvalidTextureViewDimension { + view: dim, + image: texture.dimension, + }, + ); } - None => match texture.desc.dimension { - wgt::TextureDimension::D1 => wgt::TextureViewDimension::D1, - wgt::TextureDimension::D2 - if texture.desc.array_layer_count > 1 - && desc.range.array_layer_count.is_none() => - { - wgt::TextureViewDimension::D2Array - } - wgt::TextureDimension::D2 => wgt::TextureViewDimension::D2, - wgt::TextureDimension::D3 => wgt::TextureViewDimension::D3, - }, - }; + dim + } + None => match texture.desc.dimension { + wgt::TextureDimension::D1 => wgt::TextureViewDimension::D1, + wgt::TextureDimension::D2 + if texture.desc.array_layer_count > 1 + && desc.range.array_layer_count.is_none() => + { + wgt::TextureViewDimension::D2Array + } + wgt::TextureDimension::D2 => wgt::TextureViewDimension::D2, + wgt::TextureDimension::D3 => wgt::TextureViewDimension::D3, + }, + }; let required_level_count = desc.range.base_mip_level + desc.range.mip_level_count.map_or(1, |count| count.get()); @@ -635,6 +613,24 @@ impl Device { }); }; + match view_dim { + TextureViewDimension::Cube if required_layer_count != 6 => { + return Err( + resource::CreateTextureViewError::InvalidCubemapTextureDepth { + required_layer_count, + }, + ) + } + TextureViewDimension::CubeArray if required_layer_count % 6 != 0 => { + return Err( + resource::CreateTextureViewError::InvalidCubemapArrayTextureDepth { + required_layer_count, + }, + ) + } + _ => {} + } + let full_aspect = hal::FormatAspect::from(texture.desc.format); let aspect = match desc.range.aspect { wgt::TextureAspect::All => full_aspect, @@ -845,20 +841,20 @@ impl Device { let info = naga::valid::Validator::new(naga::valid::ValidationFlags::all(), caps) .validate(&module)?; let interface = validation::Interface::new(&module, &info); - let shader = hal::device::NagaShader { module, info }; + let shader = hal::NagaShader { module, info }; let naga_result = if desc .flags .contains(wgt::ShaderFlags::EXPERIMENTAL_TRANSLATION) || !cfg!(feature = "cross") { - match unsafe { self.raw.create_shader_module_from_naga(shader) } { + let hal_desc = hal::ShaderModuleDescriptor { label: desc.label }; + match unsafe { self.raw.create_shader_module(&hal_desc, shader) } { Ok(raw) => Ok(raw), - Err((hal::device::ShaderError::CompilationFailed(msg), shader)) => { - log::warn!("Shader module compilation failed: {}", msg); + Err((error, shader)) => { + log::warn!("Shader module compilation failed: {}", error); Err(Some(shader)) } - Err((_, shader)) => Err(Some(shader)), } } else { Err(Some(shader)) @@ -884,10 +880,7 @@ impl Device { }; match spv { Ok(data) => unsafe { self.raw.create_shader_module(&data) }, - Err(e) => Err(hal::device::ShaderError::CompilationFailed(format!( - "{}", - e - ))), + Err(e) => Err(hal::ShaderError::Compilation(format!("{}", e))), } } }; @@ -895,11 +888,11 @@ impl Device { Ok(pipeline::ShaderModule { raw: match spv_result { Ok(raw) => raw, - Err(hal::device::ShaderError::OutOfMemory(_)) => { - return Err(DeviceError::OutOfMemory.into()); + Err(hal::ShaderError::Device(error)) => { + return Err(DeviceError::from(error)); } - Err(error) => { - log::error!("Shader error: {}", error); + Err(hal::ShaderError::Compilation(ref msg)) => { + log::error!("Shader error: {}", msg); return Err(pipeline::CreateShaderModuleError::Generation); } }, @@ -913,41 +906,6 @@ impl Device { }) } - /// Create a compatible render pass with a given key. - /// - /// This functions doesn't consider the following aspects for compatibility: - /// - image layouts - /// - resolve attachments - fn create_compatible_render_pass( - &self, - key: &RenderPassKey, - ) -> Result { - let mut color_ids = [(0, hal::image::Layout::ColorAttachmentOptimal); MAX_COLOR_TARGETS]; - for (index, color) in color_ids[..key.colors.len()].iter_mut().enumerate() { - color.0 = index; - } - let depth_id = key.depth_stencil.as_ref().map(|_| { - ( - key.colors.len(), - hal::image::Layout::DepthStencilAttachmentOptimal, - ) - }); - - let subpass = hal::pass::SubpassDesc { - colors: &color_ids[..key.colors.len()], - depth_stencil: depth_id.as_ref(), - inputs: &[], - resolves: &[], - preserves: &[], - }; - let all = key.all().map(|&(ref at, _)| at.clone()); - - unsafe { - self.raw - .create_render_pass(all, iter::once(subpass), iter::empty()) - } - } - fn deduplicate_bind_group_layout( self_id: id::DeviceId, entry_map: &binding_model::BindEntryMap, @@ -1062,27 +1020,15 @@ impl Device { })?; } - let raw_bindings = entry_map - .values() - .map(|entry| hal::pso::DescriptorSetLayoutBinding { - binding: entry.binding, - ty: conv::map_binding_type(entry), - count: entry - .count - .map_or(1, |v| v.get() as hal::pso::DescriptorArrayIndex), //TODO: consolidate - stage_flags: conv::map_shader_stage_flags(entry.visibility), - immutable_samplers: false, // TODO - }); + let hal_bindings = entry_map.values().collect::>(); + let hal_desc = hal::BindGroupLayoutDescriptor { + label, + entries: Cow::Owned(hal_bindings), + }; let raw = unsafe { - let mut raw_layout = self - .raw - .create_descriptor_set_layout(raw_bindings, iter::empty()) - .or(Err(DeviceError::OutOfMemory))?; - if let Some(label) = label { - self.raw - .set_descriptor_set_layout_name(&mut raw_layout, label); - } - raw_layout + self.raw + .create_bind_group_layout(&hal_bindings) + .map_err(DeviceError::from)? }; let mut count_validator = binding_model::BindingTypeMaxCountValidator::default(); @@ -1115,7 +1061,7 @@ impl Device { } #[allow(clippy::too_many_arguments)] - fn create_buffer_descriptor<'a>( + fn create_buffer_binding<'a>( bb: &binding_model::BufferBinding, binding: u32, decl: &wgt::BindGroupLayoutEntry, @@ -1124,7 +1070,7 @@ impl Device { used: &mut TrackerSet, storage: &'a Storage, id::BufferId>, limits: &wgt::Limits, - ) -> Result, binding_model::CreateBindGroupError> { + ) -> Result, binding_model::CreateBindGroupError> { use crate::binding_model::CreateBindGroupError as Error; let (binding_ty, dynamic, min_size) = match decl.ty { @@ -1167,7 +1113,7 @@ impl Device { .use_extend(storage, bb.buffer_id, (), internal_use) .map_err(|_| Error::InvalidBuffer(bb.buffer_id))?; check_buffer_usage(buffer.usage, pub_usage)?; - let &(ref buffer_raw, _) = buffer + let &(ref raw_buffer, _) = buffer .raw .as_ref() .ok_or(Error::InvalidBuffer(bb.buffer_id))?; @@ -1221,11 +1167,11 @@ impl Device { kind: MemoryInitKind::NeedsInitializedMemory, }); - let sub_range = hal::buffer::SubRange { + Ok(hal::BufferBinding { + buffer: raw_buffer, offset: bb.offset, - size: Some(bind_size), - }; - Ok(hal::pso::Descriptor::Buffer(buffer_raw, sub_range)) + size: bb.size, + }) } fn create_bind_group( @@ -1258,10 +1204,8 @@ impl Device { let (texture_view_guard, mut token) = hub.texture_views.read(&mut token); let (sampler_guard, _) = hub.samplers.read(&mut token); - // `BTreeMap` has ordered bindings as keys, which allows us to coalesce - // the descriptor writes into a single transaction. - let mut write_map = BTreeMap::new(); let mut used_buffer_ranges = Vec::new(); + let mut hal_entries = Vec::with_capacity(desc.entries.len()); for entry in desc.entries.iter() { let binding = entry.binding; // Find the corresponding declaration in the layout @@ -1269,9 +1213,9 @@ impl Device { .entries .get(&binding) .ok_or(Error::MissingBindingDeclaration(binding))?; - let descriptors: SmallVec<[_; 1]> = match entry.resource { + let hal_resource = match entry.resource { Br::Buffer(ref bb) => { - let buffer_desc = Self::create_buffer_descriptor( + let bb = Self::create_buffer_binding( &bb, binding, &decl, @@ -1281,7 +1225,7 @@ impl Device { &*buffer_guard, &self.limits, )?; - SmallVec::from([buffer_desc]) + hal::BindingResource::Buffers([bb].into()) } Br::BufferArray(ref bindings_array) => { if let Some(count) = decl.count { @@ -1297,21 +1241,21 @@ impl Device { return Err(Error::SingleBindingExpected); } - bindings_array - .iter() - .map(|bb| { - Self::create_buffer_descriptor( - &bb, - binding, - &decl, - &mut used_buffer_ranges, - &mut dynamic_binding_info, - &mut used, - &*buffer_guard, - &self.limits, - ) - }) - .collect::>()? + let mut hal_bindings = SmallVec::with_capacity(bindings_array.len()); + for bb in bindings_array { + let bb = Self::create_buffer_binding( + bb, + binding, + &decl, + &mut used_buffer_ranges, + &mut dynamic_binding_info, + &mut used, + &*buffer_guard, + &self.limits, + )?; + hal_bindings.push(bb); + } + hal::BindingResource::Buffers(hal_bindings) } Br::Sampler(id) => { match decl.ty { @@ -1341,7 +1285,7 @@ impl Device { }); } - SmallVec::from([hal::pso::Descriptor::Sampler(&sampler.raw)]) + hal::BindingResource::Sampler(&sampler.raw) } _ => { return Err(Error::WrongBindingType { @@ -1387,11 +1331,11 @@ impl Device { (Tst::Float { .. }, Tst::Depth, ..) => {} _ => { return Err(Error::InvalidTextureSampleType { - binding, - layout_sample_type: sample_type, - view_format: view.format, - }) - }, + binding, + layout_sample_type: sample_type, + view_format: view.format, + }) + } } if view_dimension != view.dimension { return Err(Error::InvalidTextureDimension { @@ -1450,6 +1394,7 @@ impl Device { "SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture", }), }; + if view .aspects .contains(hal::FormatAspect::DEPTH | hal::FormatAspect::STENCIL) @@ -1473,9 +1418,7 @@ impl Device { ) .map_err(UsageConflict::from)?; check_texture_usage(texture.usage, pub_usage)?; - let image_layout = - conv::map_texture_state(internal_use, view.aspects).1; - SmallVec::from([hal::pso::Descriptor::Image(raw, image_layout)]) + hal::BindingResource::TextureViews([raw].into(), internal_use) } resource::TextureViewInner::SwapChain { .. } => { return Err(Error::SwapChainImage); @@ -1496,87 +1439,82 @@ impl Device { return Err(Error::SingleBindingExpected); } - bindings_array - .iter() - .map(|&id| { - let view = used - .views - .use_extend(&*texture_view_guard, id, (), ()) - .map_err(|_| Error::InvalidTextureView(id))?; - let (pub_usage, internal_use) = match decl.ty { - wgt::BindingType::Texture { .. } => { - (wgt::TextureUsage::SAMPLED, view.sampled_internal_use) - } - _ => { - return Err(Error::WrongBindingType { - binding, - actual: decl.ty, - expected: "SampledTextureArray", - }) - } - }; - match view.inner { - resource::TextureViewInner::Native { - ref raw, - ref source_id, - } => { - // Careful here: the texture may no longer have its own ref count, - // if it was deleted by the user. - let texture = &texture_guard[source_id.value]; - used.textures - .change_extend( - source_id.value, - &source_id.ref_count, - view.selector.clone(), - internal_use, - ) - .map_err(UsageConflict::from)?; - check_texture_usage(texture.usage, pub_usage)?; - let image_layout = - conv::map_texture_state(internal_use, view.aspects).1; - Ok(hal::pso::Descriptor::Image(raw, image_layout)) - } - resource::TextureViewInner::SwapChain { .. } => { - Err(Error::SwapChainImage) - } + let mut hal_bindings = SmallVec::with_capacity(bindings_array.len()); + let mut common_internal_use = None; + for &id in bindings_array { + let view = used + .views + .use_extend(&*texture_view_guard, id, (), ()) + .map_err(|_| Error::InvalidTextureView(id))?; + let (pub_usage, internal_use) = match decl.ty { + wgt::BindingType::Texture { .. } => { + (wgt::TextureUsage::SAMPLED, view.sampled_internal_use) } - }) - .collect::>()? + _ => { + return Err(Error::WrongBindingType { + binding, + actual: decl.ty, + expected: "SampledTextureArray", + }) + } + }; + //TODO: ensure these match across the whole array + common_internal_use = Some(internal_use); + + match view.inner { + resource::TextureViewInner::Native { + ref raw, + ref source_id, + } => { + // Careful here: the texture may no longer have its own ref count, + // if it was deleted by the user. + let texture = &texture_guard[source_id.value]; + used.textures + .change_extend( + source_id.value, + &source_id.ref_count, + view.selector.clone(), + internal_use, + ) + .map_err(UsageConflict::from)?; + check_texture_usage(texture.usage, pub_usage)?; + hal_bindings.push(raw); + } + resource::TextureViewInner::SwapChain { .. } => { + Err(Error::SwapChainImage) + } + } + } + + hal::BindingResource::TextureViews(hal_bindings, common_internal_use.unwrap()) } }; - if write_map.insert(binding, descriptors).is_some() { - return Err(Error::DuplicateBinding(binding)); + + hal_entries.push(hal::BindGroupEntry { + binding, + resource: hal_resource, + }); + } + + hal_entries.sort_by_key(|entry| entry.binding); + for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) { + if a.binding == b.binding { + return Err(Error::DuplicateBinding(a.binding)); } } - let mut desc_sets = - self.desc_allocator - .lock() - .allocate(&self.raw, &layout.raw, &layout.desc_count, 1)?; - let mut desc_set = desc_sets.pop().unwrap(); - - // Set the descriptor set's label for easier debugging. - if let Some(label) = desc.label.as_ref() { - unsafe { - self.raw.set_descriptor_set_name(desc_set.raw_mut(), &label); - } - } - - if let Some(start_binding) = write_map.keys().next().cloned() { - let descriptors = write_map.into_iter().flat_map(|(_, list)| list); - unsafe { - let write = hal::pso::DescriptorSetWrite { - set: desc_set.raw_mut(), - binding: start_binding, - array_offset: 0, - descriptors, - }; - self.raw.write_descriptor_set(write); - } - } + let hal_desc = hal::BindGroupDescriptor { + name: desc.name, + entries: hal_entries.into(), + }; + let raw = unsafe { + self.raw + .create_bind_group(&hal_desc) + .map_err(DeviceError::from)? + }; Ok(binding_model::BindGroup { - raw: desc_set, + raw, device_id: Stored { value: id::Valid(self_id), ref_count: self.life_guard.add_ref(), @@ -1657,25 +1595,20 @@ impl Device { .validate(&self.limits) .map_err(Error::TooManyBindings)?; - let descriptor_set_layouts = desc - .bind_group_layouts - .iter() - .map(|&id| &bgl_guard.get(id).unwrap().raw); - let push_constants = desc - .push_constant_ranges - .iter() - .map(|pc| (conv::map_shader_stage_flags(pc.stages), pc.range.clone())); + let hal_desc = hal::PipelineLayoutDescriptor { + label: desc.label, + bind_group_layouts: desc + .bind_group_layouts + .iter() + .map(|&id| &bgl_guard.get(id).unwrap().raw) + .collect(), + push_constant_ranges: desc.push_constant_ranges.as_ref(), + }; let raw = unsafe { - let raw_layout = self - .raw - .create_pipeline_layout(descriptor_set_layouts, push_constants) - .or(Err(DeviceError::OutOfMemory))?; - if let Some(_) = desc.label { - //TODO-0.6: needs gfx changes published - //self.raw.set_pipeline_layout_name(&mut raw_layout, label); - } - raw_layout + self.raw + .create_pipeline_layout(&hal_desc) + .map_err(DeviceError::from)? }; Ok(binding_model::PipelineLayout { @@ -1813,17 +1746,6 @@ impl Device { return Err(pipeline::ImplicitLayoutError::ReflectionError(flag).into()); } - let shader = hal::pso::EntryPoint:: { - entry: &entry_point_name, // TODO - module: &shader_module.raw, - specialization: hal::pso::Specialization::EMPTY, - }; - - // TODO - let flags = hal::pso::PipelineCreationFlags::empty(); - // TODO - let parent = hal::pso::BasePipeline::None; - let pipeline_layout_id = match desc.layout { Some(id) => id, None => self.derive_pipeline_layout( @@ -1838,29 +1760,26 @@ impl Device { .get(pipeline_layout_id) .map_err(|_| pipeline::CreateComputePipelineError::InvalidLayout)?; - let pipeline_desc = hal::pso::ComputePipelineDesc { - label: desc.label.as_ref().map(AsRef::as_ref), - shader, + let pipeline_desc = hal::ComputePipelineDescriptor { + label: desc.label, layout: &layout.raw, - flags, - parent, + stage: hal::ProgrammableStage { + entry: &entry_point_name, + module: &shader_module.raw, + }, }; let raw = - unsafe { self.raw.create_compute_pipeline(&pipeline_desc, None) }.map_err(|err| { - match err { - hal::pso::CreationError::OutOfMemory(_) => { - pipeline::CreateComputePipelineError::Device(DeviceError::OutOfMemory) + unsafe { self.raw.create_compute_pipeline(&pipeline_desc) }.map_err( + |err| match err { + hal::PipelineError::Device(error) => { + pipeline::CreateComputePipelineError::Device(error.into()) } - hal::pso::CreationError::ShaderCreationError(_, error) => { - pipeline::CreateComputePipelineError::Internal(error) + hal::PipelineError::Linkage(msg) => { + pipeline::CreateComputePipelineError::Internal(msg) } - _ => { - log::error!("failed to create compute pipeline: {}", err); - pipeline::CreateComputePipelineError::Device(DeviceError::OutOfMemory) - } - } - })?; + }, + )?; let pipeline = pipeline::ComputePipeline { raw, @@ -1901,22 +1820,19 @@ impl Device { let mut derived_group_layouts = ArrayVec::<[binding_model::BindEntryMap; hal::MAX_BIND_GROUPS]>::new(); - let color_states = desc + let color_targets = desc .fragment .as_ref() .map_or(&[][..], |fragment| &fragment.targets); let depth_stencil_state = desc.depth_stencil.as_ref(); - let rasterizer = - conv::map_primitive_state_to_rasterizer(&desc.primitive, depth_stencil_state); let mut io = validation::StageIo::default(); let mut validated_stages = wgt::ShaderStage::empty(); - let desc_vbs = &desc.vertex.buffers; - let mut vertex_strides = Vec::with_capacity(desc_vbs.len()); - let mut vertex_buffers = Vec::with_capacity(desc_vbs.len()); - let mut attributes = Vec::new(); - for (i, vb_state) in desc_vbs.iter().enumerate() { + let mut vertex_strides = Vec::with_capacity(desc.vertex.buffers.len()); + let mut vertex_buffers = Vec::with_capacity(desc.vertex.buffers.len()); + let mut total_attributes = 0; + for (i, vb_state) in desc.vertex.buffers.iter().enumerate() { vertex_strides .alloc() .init((vb_state.array_stride, vb_state.step_mode)); @@ -1936,16 +1852,13 @@ impl Device { stride: vb_state.array_stride, }); } - vertex_buffers.alloc().init(hal::pso::VertexBufferDesc { - binding: i as u32, - stride: vb_state.array_stride as u32, - rate: match vb_state.step_mode { - InputStepMode::Vertex => hal::pso::VertexInputRate::Vertex, - InputStepMode::Instance => hal::pso::VertexInputRate::Instance(1), - }, + vertex_buffers.alloc().init(hal::VertexBufferLayout { + array_stride: vb_state.array_stride as u32, + step_mode: vb_state.state_mode, + attributs: Cow::Borrowed(vb_state.attributes.as_ref()), }); - let desc_atts = &vb_state.attributes; - for attribute in desc_atts.iter() { + + for attribute in vb_state.attributes.iter() { if attribute.offset >= 0x10000000 { return Err( pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset { @@ -1963,19 +1876,12 @@ impl Device { self.require_features(wgt::Features::VERTEX_ATTRIBUTE_64BIT)?; } - attributes.alloc().init(hal::pso::AttributeDesc { - location: attribute.shader_location, - binding: i as u32, - element: hal::pso::Element { - format: conv::map_vertex_format(attribute.format), - offset: attribute.offset as u32, - }, - }); io.insert( attribute.shader_location, validation::InterfaceVar::vertex_attribute(attribute.format), ); } + total_attributes += vb_state.attributes.len(); } if vertex_buffers.len() > self.limits.max_vertex_buffers as usize { @@ -1984,10 +1890,10 @@ impl Device { limit: self.limits.max_vertex_buffers, }); } - if attributes.len() > self.limits.max_vertex_attributes as usize { + if total_attributes > self.limits.max_vertex_attributes as usize { return Err( pipeline::CreateRenderPipelineError::TooManyVertexAttributes { - given: attributes.len() as u32, + given: total_attributes as u32, limit: self.limits.max_vertex_attributes, }, ); @@ -2022,13 +1928,7 @@ impl Device { ); } - let input_assembler = conv::map_primitive_state_to_input_assembler(&desc.primitive); - - let mut blender = hal::pso::BlendDesc { - logic_op: None, - targets: Vec::with_capacity(color_states.len()), - }; - for (i, cs) in color_states.iter().enumerate() { + for (i, cs) in color_targets.iter().enumerate() { let error = loop { let format_desc = cs.format.describe(); self.require_features(format_desc.required_features)?; @@ -2042,19 +1942,10 @@ impl Device { if cs.blend.is_some() && !format_desc.guaranteed_format_features.filterable { break Some(pipeline::ColorStateError::FormatNotBlendable(cs.format)); } - let hal_format = conv::map_texture_format(cs.format, self.private_features); - if !hal_format - .surface_desc() - .aspects - .contains(hal::format::Aspects::COLOR) - { + if !hal::FormatAspect::from(cs.format).contains(hal::FormatAspect::COLOR) { break Some(pipeline::ColorStateError::FormatNotColor(cs.format)); } - match conv::map_color_target_state(cs) { - Ok(bt) => blender.targets.push(bt), - Err(e) => break Some(e), - } break None; }; if let Some(e) = error { @@ -2075,12 +1966,11 @@ impl Device { ds.format, )); } - let hal_format = conv::map_texture_format(ds.format, self.private_features); - let aspects = hal_format.surface_desc().aspects; - if ds.is_depth_enabled() && !aspects.contains(hal::format::Aspects::DEPTH) { + let aspect = hal::FormatAspect::from(ds.format); + if ds.is_depth_enabled() && !aspect.contains(hal::FormatAspect::DEPTH) { break Some(pipeline::DepthStencilStateError::FormatNotDepth(ds.format)); } - if ds.stencil.is_enabled() && !aspects.contains(hal::format::Aspects::STENCIL) { + if ds.stencil.is_enabled() && !aspect.contains(hal::FormatAspect::STENCIL) { break Some(pipeline::DepthStencilStateError::FormatNotStencil( ds.format, )); @@ -2091,16 +1981,6 @@ impl Device { return Err(pipeline::CreateRenderPipelineError::DepthStencilState(e)); } } - let depth_stencil = depth_stencil_state - .map(conv::map_depth_stencil_state) - .unwrap_or_default(); - - let baked_states = hal::pso::BakedStates { - viewport: None, - scissor: None, - blend_constants: None, - depth_bounds: None, - }; if desc.layout.is_none() { for _ in 0..self.limits.max_bind_groups { @@ -2115,52 +1995,10 @@ impl Device { } sc as u8 }; - let multisampling = if samples == 1 { - None - } else { - Some(conv::map_multisample_state(&desc.multisample)) - }; - - let rp_key = RenderPassKey { - colors: color_states - .iter() - .map(|state| { - let at = hal::pass::Attachment { - format: Some(conv::map_texture_format( - state.format, - self.private_features, - )), - samples, - ops: hal::pass::AttachmentOps::PRESERVE, - stencil_ops: hal::pass::AttachmentOps::DONT_CARE, - layouts: hal::image::Layout::General..hal::image::Layout::General, - }; - (at, hal::image::Layout::ColorAttachmentOptimal) - }) - .collect(), - // We can ignore the resolves as the vulkan specs says: - // As an additional special case, if two render passes have a single subpass, - // they are compatible even if they have different resolve attachment references - // or depth/stencil resolve modes but satisfy the other compatibility conditions. - resolves: ArrayVec::new(), - depth_stencil: depth_stencil_state.map(|state| { - let at = hal::pass::Attachment { - format: Some(conv::map_texture_format( - state.format, - self.private_features, - )), - samples, - ops: hal::pass::AttachmentOps::PRESERVE, - stencil_ops: hal::pass::AttachmentOps::PRESERVE, - layouts: hal::image::Layout::General..hal::image::Layout::General, - }; - (at, hal::image::Layout::DepthStencilAttachmentOptimal) - }), - }; let (shader_module_guard, _) = hub.shader_modules.read(&mut token); - let vertex = { + let vertex_stage = { let stage = &desc.vertex.stage; let flag = wgt::ShaderStage::VERTEX; @@ -2200,14 +2038,13 @@ impl Device { validated_stages |= flag; } - hal::pso::EntryPoint:: { - entry: &stage.entry_point, + hal::ProgrammableStage { module: &shader_module.raw, - specialization: hal::pso::Specialization::EMPTY, + entry_point: &stage.entry_point, } }; - let fragment = match desc.fragment { + let fragment_stage = match desc.fragment { Some(ref fragment) => { let entry_point_name = &fragment.stage.entry_point; let flag = wgt::ShaderStage::FRAGMENT; @@ -2248,17 +2085,16 @@ impl Device { } } - Some(hal::pso::EntryPoint:: { - entry: &entry_point_name, + Some(hal::ProgrammableStage { module: &shader_module.raw, - specialization: hal::pso::Specialization::EMPTY, + entry_point: &entry_point_name, }) } None => None, }; if validated_stages.contains(wgt::ShaderStage::FRAGMENT) { - for (i, state) in color_states.iter().enumerate() { + for (i, state) in color_targets.iter().enumerate() { match io.get(&(i as wgt::ShaderLocation)) { Some(ref output) => { validation::check_texture_format(state.format, &output.ty).map_err( @@ -2292,20 +2128,6 @@ impl Device { return Err(pipeline::ImplicitLayoutError::ReflectionError(last_stage).into()); } - let primitive_assembler = hal::pso::PrimitiveAssemblerDesc::Vertex { - buffers: &vertex_buffers, - attributes: &attributes, - input_assembler, - vertex, - tessellation: None, - geometry: None, - }; - - // TODO - let flags = hal::pso::PipelineCreationFlags::empty(); - // TODO - let parent = hal::pso::BasePipeline::None; - let pipeline_layout_id = match desc.layout { Some(id) => id, None => self.derive_pipeline_layout( @@ -2320,55 +2142,31 @@ impl Device { .get(pipeline_layout_id) .map_err(|_| pipeline::CreateRenderPipelineError::InvalidLayout)?; - let mut rp_lock = self.render_passes.lock(); - let pipeline_desc = hal::pso::GraphicsPipelineDesc { - label: desc.label.as_ref().map(AsRef::as_ref), - primitive_assembler, - rasterizer, - fragment, - blender, - depth_stencil, - multisampling, - baked_states, + let pipeline_desc = hal::RenderPipelineDescriptor { + label: desc.label, layout: &layout.raw, - subpass: hal::pass::Subpass { - index: 0, - main_pass: match rp_lock.render_passes.entry(rp_key) { - Entry::Occupied(e) => e.into_mut(), - Entry::Vacant(e) => { - let pass = self - .create_compatible_render_pass(e.key()) - .or(Err(DeviceError::OutOfMemory))?; - e.insert(pass) + vertex_buffers: vertex_buffers.into(), + vertex_stage, + fragment_stage, + color_targets, + depth_stencil: desc.depth_stencil, + multisample: desc.multisample, + }; + let raw = + unsafe { self.raw.create_render_pipeline(&pipeline_desc) }.map_err( + |err| match err { + hal::PipelineError::Device(error) => { + pipeline::CreateRenderPipelineError::Device(error.into()) + } + hal::PipelineError::Linkage(stage, msg) => { + pipeline::CreateRenderPipelineError::Internal { stage, error: msg } } }, - }, - flags, - parent, - }; - // TODO: cache - let raw = - unsafe { self.raw.create_graphics_pipeline(&pipeline_desc, None) }.map_err(|err| { - match err { - hal::pso::CreationError::OutOfMemory(_) => { - pipeline::CreateRenderPipelineError::Device(DeviceError::OutOfMemory) - } - hal::pso::CreationError::ShaderCreationError(stage, error) => { - pipeline::CreateRenderPipelineError::Internal { - stage: conv::map_hal_flags_to_shader_stage(stage), - error, - } - } - _ => { - log::error!("failed to create graphics pipeline: {}", err); - pipeline::CreateRenderPipelineError::Device(DeviceError::OutOfMemory) - } - } - })?; + )?; let pass_context = RenderPassContext { attachments: AttachmentData { - colors: color_states.iter().map(|state| state.format).collect(), + colors: color_targets.iter().map(|state| state.format).collect(), resolves: ArrayVec::new(), depth_stencil: depth_stencil_state.as_ref().map(|state| state.format), }, @@ -2376,7 +2174,7 @@ impl Device { }; let mut flags = pipeline::PipelineFlags::empty(); - for state in color_states.iter() { + for state in color_targets.iter() { if let Some(ref bs) = state.blend { if bs.color.uses_constant() | bs.alpha.uses_constant() { flags |= pipeline::PipelineFlags::BLEND_CONSTANT; @@ -2544,35 +2342,6 @@ pub enum DeviceError { OutOfMemory, } -impl From for DeviceError { - fn from(err: hal::device::WaitError) -> Self { - match err { - hal::device::WaitError::OutOfMemory(_) => Self::OutOfMemory, - hal::device::WaitError::DeviceLost(_) => Self::Lost, - } - } -} - -impl From for DeviceError { - fn from(err: gpu_alloc::MapError) -> Self { - match err { - gpu_alloc::MapError::OutOfDeviceMemory | gpu_alloc::MapError::OutOfHostMemory => { - DeviceError::OutOfMemory - } - _ => panic!("failed to map buffer: {}", err), - } - } -} - -impl DeviceError { - fn from_bind(err: hal::device::BindError) -> Self { - match err { - hal::device::BindError::OutOfMemory(_) => Self::OutOfMemory, - _ => panic!("failed to bind memory: {}", err), - } - } -} - #[derive(Clone, Debug, Error)] #[error("Features {0:?} are required but not enabled on the device")] pub struct MissingFeatures(pub wgt::Features); @@ -2715,7 +2484,7 @@ impl Global { }; buffer.map_state = resource::BufferMapState::Active { ptr, - sub_range: hal::buffer::SubRange::WHOLE, + range: 0..map_size, host: HostMap::Write, }; resource::BufferUse::MAP_WRITE @@ -4271,7 +4040,7 @@ impl Global { }; let (caps, formats) = { - let surface = B::get_surface_mut(surface); + let surface = A::get_surface_mut(surface); let adapter = &adapter_guard[device.adapter_id.value]; let queue_family = &adapter.raw.queue_families[0]; if !surface.supports_queue_family(queue_family) { @@ -4303,14 +4072,8 @@ impl Global { } let framebuffer_attachment = config.framebuffer_attachment(); - match unsafe { B::get_surface_mut(surface).configure_swapchain(&device.raw, config) } { - Ok(()) => (), - Err(hal::window::SwapchainError::OutOfMemory(_)) => { - break DeviceError::OutOfMemory.into() - } - Err(hal::window::SwapchainError::DeviceLost(_)) => break DeviceError::Lost.into(), - Err(err) => panic!("failed to configure swap chain on creation: {}", err), - } + unsafe { A::get_surface_mut(surface).configure_swapchain(&device.raw, config) } + .map_err(DeviceError::from)?; if let Some(sc) = swap_chain_guard.try_remove(sc_id) { if sc.acquired_view_id.is_some() { diff --git a/wgpu-core/src/hub.rs b/wgpu-core/src/hub.rs index 2d48b30bb..e18a81598 100644 --- a/wgpu-core/src/hub.rs +++ b/wgpu-core/src/hub.rs @@ -555,7 +555,7 @@ pub struct Hub { pub samplers: Registry, SamplerId, F>, } -impl Hub { +impl Hub { fn new(factory: &F) -> Self { Self { adapters: Registry::new(A::VARIANT, factory), @@ -578,7 +578,7 @@ impl Hub { } } -impl Hub { +impl Hub { //TODO: instead of having a hacky `with_adapters` parameter, // we should have `clear_device(device_id)` that specifically destroys // everything related to a logical device. @@ -696,7 +696,7 @@ impl Hub { //TODO: hold the surface alive by the swapchain if surface_guard.contains(suf_id) { let surface = surface_guard.get_mut(suf_id).unwrap(); - let suf = B::get_surface_mut(surface); + let suf = A::get_surface_mut(surface); unsafe { suf.unconfigure_swapchain(&device.raw); } diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index 6a39e7d28..722450c0f 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::{ - conv, device::{Device, DeviceDescriptor}, hub::{Global, GlobalIdentityHandlerFactory, HalApi, Input, Token}, id::{AdapterId, DeviceId, SurfaceId, Valid}, @@ -85,21 +84,21 @@ impl Instance { } } -type GfxSurface = ::Surface; +type HalSurface = ::Surface; #[derive(Debug)] pub struct Surface { /* #[cfg(vulkan)] -pub vulkan: Option>, +pub vulkan: Option>, #[cfg(metal)] -pub metal: Option>, +pub metal: Option>, #[cfg(dx12)] -pub dx12: Option>, +pub dx12: Option>, #[cfg(dx11)] -pub dx11: Option>, +pub dx11: Option>, #[cfg(gl)] -pub gl: Option>, +pub gl: Option>, */} impl crate::hub::Resource for Surface { @@ -142,7 +141,7 @@ impl Adapter { wgt::TextureFormat::Rgba8Unorm, ]; - let formats = B::get_surface(surface).supported_formats(&self.raw.adapter); + let formats = A::get_surface(surface).supported_formats(&self.raw.adapter); preferred_formats .iter() .cloned() @@ -487,35 +486,35 @@ impl Global { /* #[cfg(vulkan)] let adapters_vk = map((&instance.vulkan, &id_vulkan, { - fn surface_vulkan(surf: &Surface) -> Option<&GfxSurface> { + fn surface_vulkan(surf: &Surface) -> Option<&HalSurface> { surf.vulkan.as_ref() } surface_vulkan })); #[cfg(metal)] let adapters_mtl = map((&instance.metal, &id_metal, { - fn surface_metal(surf: &Surface) -> Option<&GfxSurface> { + fn surface_metal(surf: &Surface) -> Option<&HalSurface> { surf.metal.as_ref() } surface_metal })); #[cfg(dx12)] let adapters_dx12 = map((&instance.dx12, &id_dx12, { - fn surface_dx12(surf: &Surface) -> Option<&GfxSurface> { + fn surface_dx12(surf: &Surface) -> Option<&HalSurface> { surf.dx12.as_ref() } surface_dx12 })); #[cfg(dx11)] let adapters_dx11 = map((&instance.dx11, &id_dx11, { - fn surface_dx11(surf: &Surface) -> Option<&GfxSurface> { + fn surface_dx11(surf: &Surface) -> Option<&HalSurface> { surf.dx11.as_ref() } surface_dx11 })); #[cfg(gl)] let adapters_gl = map((&instance.gl, &id_gl, { - fn surface_gl(surf: &Surface) -> Option<&GfxSurface> { + fn surface_gl(surf: &Surface) -> Option<&HalSurface> { surf.gl.as_ref() } surface_gl @@ -604,7 +603,7 @@ impl Global { let (adapter_guard, _) = hub.adapters.read(&mut token); adapter_guard .get(adapter_id) - .map(|adapter| conv::map_adapter_info(adapter.raw.info.clone(), adapter_id.backend())) + .map(|adapter| adapter.raw.info.clone()) .map_err(|_| InvalidAdapter) } diff --git a/wgpu-core/src/pipeline.rs b/wgpu-core/src/pipeline.rs index 53eeb652d..f7ddf6c73 100644 --- a/wgpu-core/src/pipeline.rs +++ b/wgpu-core/src/pipeline.rs @@ -12,11 +12,10 @@ use crate::{ use std::borrow::Cow; use thiserror::Error; -#[derive(Debug)] pub enum ShaderModuleSource<'a> { SpirV(Cow<'a, [u32]>), Wgsl(Cow<'a, str>), - Naga(naga::Module), + Naga(&'a naga::Module), } #[derive(Clone, Debug)] @@ -30,7 +29,7 @@ pub struct ShaderModuleDescriptor<'a> { #[derive(Debug)] pub struct ShaderModule { - pub(crate) raw: B::ShaderModule, + pub(crate) raw: A::ShaderModule, pub(crate) device_id: Stored, pub(crate) interface: Option, #[cfg(debug_assertions)] @@ -126,7 +125,7 @@ pub enum CreateComputePipelineError { #[derive(Debug)] pub struct ComputePipeline { - pub(crate) raw: B::ComputePipeline, + pub(crate) raw: A::ComputePipeline, pub(crate) layout_id: Stored, pub(crate) device_id: Stored, pub(crate) life_guard: LifeGuard, @@ -290,7 +289,7 @@ bitflags::bitflags! { #[derive(Debug)] pub struct RenderPipeline { - pub(crate) raw: B::GraphicsPipeline, + pub(crate) raw: A::RenderPipeline, pub(crate) layout_id: Stored, pub(crate) device_id: Stored, pub(crate) pass_context: RenderPassContext, diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index 20e365b0f..52703373e 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -16,55 +16,8 @@ use thiserror::Error; use std::{borrow::Borrow, num::NonZeroU8, ops::Range, ptr::NonNull}; -bitflags::bitflags! { - /// The internal enum mirrored from `BufferUsage`. The values don't have to match! - pub struct BufferUse: u32 { - const EMPTY = 0; - const MAP_READ = 1; - const MAP_WRITE = 2; - const COPY_SRC = 4; - const COPY_DST = 8; - const INDEX = 16; - const VERTEX = 32; - const UNIFORM = 64; - const STORAGE_LOAD = 128; - const STORAGE_STORE = 256; - const INDIRECT = 512; - /// The combination of all read-only usages. - const READ_ALL = Self::MAP_READ.bits | Self::COPY_SRC.bits | - Self::INDEX.bits | Self::VERTEX.bits | Self::UNIFORM.bits | - Self::STORAGE_LOAD.bits | Self::INDIRECT.bits; - /// The combination of all write-only and read-write usages. - const WRITE_ALL = Self::MAP_WRITE.bits | Self::COPY_DST.bits | Self::STORAGE_STORE.bits; - /// The combination of all usages that the are guaranteed to be be ordered by the hardware. - /// If a usage is not ordered, then even if it doesn't change between draw calls, there - /// still need to be pipeline barriers inserted for synchronization. - const ORDERED = Self::READ_ALL.bits | Self::MAP_WRITE.bits | Self::COPY_DST.bits; - } -} - -bitflags::bitflags! { - /// The internal enum mirrored from `TextureUsage`. The values don't have to match! - pub struct TextureUse: u32 { - const EMPTY = 0; - const COPY_SRC = 1; - const COPY_DST = 2; - const SAMPLED = 4; - const ATTACHMENT_READ = 8; - const ATTACHMENT_WRITE = 16; - const STORAGE_LOAD = 32; - const STORAGE_STORE = 48; - /// The combination of all read-only usages. - const READ_ALL = Self::COPY_SRC.bits | Self::SAMPLED.bits | Self::ATTACHMENT_READ.bits | Self::STORAGE_LOAD.bits; - /// The combination of all write-only and read-write usages. - const WRITE_ALL = Self::COPY_DST.bits | Self::ATTACHMENT_WRITE.bits | Self::STORAGE_STORE.bits; - /// The combination of all usages that the are guaranteed to be be ordered by the hardware. - /// If a usage is not ordered, then even if it doesn't change between draw calls, there - /// still need to be pipeline barriers inserted for synchronization. - const ORDERED = Self::READ_ALL.bits | Self::COPY_DST.bits | Self::ATTACHMENT_WRITE.bits; - const UNINITIALIZED = 0xFFFF; - } -} +//TODO: remove the alias, just use it from `hal` +pub(crate) use hal::{BufferUse, TextureUse}; #[repr(C)] #[derive(Debug)] diff --git a/wgpu-core/src/track/mod.rs b/wgpu-core/src/track/mod.rs index a314ddf24..798d4362d 100644 --- a/wgpu-core/src/track/mod.rs +++ b/wgpu-core/src/track/mod.rs @@ -7,7 +7,7 @@ mod range; mod texture; use crate::{ - conv, hub, + hub, id::{self, TypedId, Valid}, resource, Epoch, FastHashMap, Index, RefCount, }; @@ -124,24 +124,6 @@ pub(crate) struct PendingTransition { pub usage: ops::Range, } -impl PendingTransition { - /// Produce the gfx-hal barrier corresponding to the transition. - pub fn into_hal<'a, A: hal::Api>( - self, - buf: &'a resource::Buffer, - ) -> hal::memory::Barrier<'a, B> { - log::trace!("\tbuffer -> {:?}", self); - let &(ref target, _) = buf.raw.as_ref().expect("Buffer is destroyed"); - hal::memory::Barrier::Buffer { - states: conv::map_buffer_state(self.usage.start) - ..conv::map_buffer_state(self.usage.end), - target, - range: hal::buffer::SubRange::WHOLE, - families: None, - } - } -} - impl From> for UsageConflict { fn from(e: PendingTransition) -> Self { Self::Buffer { @@ -151,31 +133,6 @@ impl From> for UsageConflict { } } -impl PendingTransition { - /// Produce the gfx-hal barrier corresponding to the transition. - pub fn into_hal<'a, A: hal::Api>( - self, - tex: &'a resource::Texture, - ) -> hal::memory::Barrier<'a, B> { - log::trace!("\ttexture -> {:?}", self); - let &(ref target, _) = tex.raw.as_ref().expect("Texture is destroyed"); - let aspects = tex.aspects; - hal::memory::Barrier::Image { - states: conv::map_texture_state(self.usage.start, aspects) - ..conv::map_texture_state(self.usage.end, aspects), - target, - range: hal::image::SubresourceRange { - aspects, - level_start: self.selector.levels.start, - level_count: Some(self.selector.levels.end - self.selector.levels.start), - layer_start: self.selector.layers.start, - layer_count: Some(self.selector.layers.end - self.selector.layers.start), - }, - families: None, - } - } -} - impl From> for UsageConflict { fn from(e: PendingTransition) -> Self { Self::Texture { diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index 1874bc9cb..8b242b0a5 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -15,4 +15,9 @@ license = "MPL-2.0" [dependencies] bitflags = "1.0" +smallvec = "1" wgt = { package = "wgpu-types", path = "../wgpu-types" } + +[dependencies.naga] +git = "https://github.com/gfx-rs/naga" +tag = "gfx-25" diff --git a/wgpu-hal/src/empty.rs b/wgpu-hal/src/empty.rs index 65a8f11c4..a6b668ed8 100644 --- a/wgpu-hal/src/empty.rs +++ b/wgpu-hal/src/empty.rs @@ -1,39 +1,68 @@ +#[derive(Clone)] pub struct Api; -pub struct Surface; -pub struct Adapter; -pub struct Queue; -pub struct Device; -pub struct CommandBuffer; -pub struct RenderPass; -pub struct ComputePass; +pub struct Context; +pub struct Encoder; +#[derive(Debug)] pub struct Resource; -impl crate::Surface for Surface {} +impl crate::Api for Api { + type Instance = Context; + type Surface = Context; + type Adapter = Context; + type Queue = Context; + type Device = Context; -impl crate::Adapter for Adapter { + type CommandBuffer = Encoder; + type RenderPass = Encoder; + type ComputePass = Encoder; + + type Buffer = Resource; + type QuerySet = Resource; + type Texture = Resource; + type SwapChainTexture = Resource; + type TextureView = Resource; + type Sampler = Resource; + + type BindGroupLayout = Resource; + type BindGroup = Resource; + type PipelineLayout = Resource; + type ShaderModule = Resource; + type RenderPipeline = Resource; + type ComputePipeline = Resource; +} + +impl crate::Instance for Context { + unsafe fn enumerate_adapters(&self) -> Vec> { + Vec::new() + } +} + +impl crate::Surface for Context {} + +impl crate::Adapter for Context { unsafe fn open( &self, _features: wgt::Features, ) -> Result, crate::Error> { Err(crate::Error::DeviceLost) } - unsafe fn close(&self, _device: Device) {} + unsafe fn close(&self, _device: Context) {} unsafe fn texture_format_capabilities( &self, _format: wgt::TextureFormat, ) -> crate::TextureFormatCapability { crate::TextureFormatCapability::empty() } - unsafe fn surface_formats(&self, _surface: &Surface) -> Vec { + unsafe fn surface_formats(&self, _surface: &Context) -> Vec { Vec::new() } } -impl crate::Queue for Queue { - unsafe fn submit>(&mut self, _command_buffers: I) {} +impl crate::Queue for Context { + unsafe fn submit>(&mut self, _command_buffers: I) {} } -impl crate::Device for Device { +impl crate::Device for Context { unsafe fn create_buffer( &self, _desc: &wgt::BufferDescriptor, @@ -79,52 +108,76 @@ impl crate::Device for Device { unsafe fn destroy_texture_view(&self, _view: Resource) {} unsafe fn create_sampler( &self, - desc: &crate::SamplerDescriptor, + _desc: &crate::SamplerDescriptor, ) -> Result { Ok(Resource) } unsafe fn destroy_sampler(&self, _sampler: Resource) {} - unsafe fn create_command_buffer(&self) -> Result { - Ok(CommandBuffer) + unsafe fn create_command_buffer(&self) -> Result { + Ok(Encoder) } + unsafe fn destroy_command_buffer(&self, _cmd_buf: Encoder) {} + + unsafe fn create_bind_group_layout( + &self, + _desc: &crate::BindGroupLayoutDescriptor, + ) -> Result { + Ok(Resource) + } + unsafe fn destroy_bind_group_layout(&self, _bg_layout: Resource) {} + unsafe fn create_pipeline_layout( + &self, + _desc: &crate::PipelineLayoutDescriptor, + ) -> Result { + Ok(Resource) + } + unsafe fn destroy_pipeline_layout(&self, _pipeline_layout: Resource) {} + unsafe fn create_bind_group( + &self, + _desc: &crate::BindGroupDescriptor, + ) -> Result { + Ok(Resource) + } + unsafe fn destroy_bind_group(&self, _group: Resource) {} + + unsafe fn create_shader_module( + &self, + _desc: &crate::ShaderModuleDescriptor, + _shader: crate::NagaShader, + ) -> Result { + Ok(Resource) + } + unsafe fn destroy_shader_module(&self, _module: Resource) {} + unsafe fn create_render_pipeline( + &self, + _desc: &crate::RenderPipelineDescriptor, + ) -> Result { + Ok(Resource) + } + unsafe fn destroy_render_pipeline(&self, _pipeline: Resource) {} + unsafe fn create_compute_pipeline( + &self, + _desc: &crate::ComputePipelineDescriptor, + ) -> Result { + Ok(Resource) + } + unsafe fn destroy_compute_pipeline(&self, _pipeline: Resource) {} } -impl crate::CommandBuffer for CommandBuffer { +impl crate::CommandBuffer for Encoder { unsafe fn begin(&mut self) {} unsafe fn end(&mut self) {} - unsafe fn begin_render_pass(&mut self) -> RenderPass { - RenderPass + unsafe fn begin_render_pass(&mut self) -> Encoder { + Encoder } - unsafe fn end_render_pass(&mut self, _pass: RenderPass) {} - unsafe fn begin_compute_pass(&mut self) -> ComputePass { - ComputePass + unsafe fn end_render_pass(&mut self, _pass: Encoder) {} + unsafe fn begin_compute_pass(&mut self) -> Encoder { + Encoder } - unsafe fn end_compute_pass(&mut self, _pass: ComputePass) {} + unsafe fn end_compute_pass(&mut self, _pass: Encoder) {} } -impl crate::RenderPass for RenderPass {} -impl crate::ComputePass for ComputePass {} - -impl crate::Api for Api { - type Surface = Surface; - type Adapter = Adapter; - type Queue = Queue; - type Device = Device; - - type CommandBuffer = CommandBuffer; - type RenderPass = RenderPass; - type ComputePass = ComputePass; - - type Buffer = Resource; - type QuerySet = Resource; - type Texture = Resource; - type SwapChainTexture = Resource; - type TextureView = Resource; - type Sampler = Resource; - - unsafe fn enumerate_adapters(&self) -> Vec> { - Vec::new() - } -} +impl crate::RenderPass for Encoder {} +impl crate::ComputePass for Encoder {} diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index 715900bab..9124192aa 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -5,11 +5,33 @@ /*! This library describes the internal unsafe graphics abstraction API. */ +#![allow( + // We use loops for getting early-out of scope without closures. + clippy::never_loop, + // We don't use syntax sugar where it's not necessary. + clippy::match_like_matches_macro, + // Redundant matching is more explicit. + clippy::redundant_pattern_matching, + // Explicit lifetimes are often easier to reason about. + clippy::needless_lifetimes, + // No need for defaults in the internal types. + clippy::new_without_default, +)] +#![warn( + trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_qualifications, + // We don't match on a reference, unless required. + clippy::pattern_type_mismatch, +)] + pub mod empty; -use std::{fmt, num::NonZeroU8, ops::Range, ptr::NonNull}; +use std::{borrow::Cow, fmt, num::NonZeroU8, ops::Range, ptr::NonNull}; use bitflags::bitflags; +use smallvec::SmallVec; pub const MAX_ANISOTROPY: u8 = 16; pub const MAX_BIND_GROUPS: usize = 8; @@ -35,7 +57,56 @@ impl fmt::Display for Error { } impl std::error::Error for Error {} -pub trait Api: Sized { +#[derive(Debug)] +pub enum ShaderError { + Compilation(String), + Device(Error), +} + +impl fmt::Display for ShaderError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::Compilation(ref message) => write!(f, "compilation failed: {}", message), + Self::Device(_) => Ok(()), + } + } +} +impl std::error::Error for ShaderError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match *self { + Self::Compilation(..) => None, + Self::Device(ref parent) => Some(parent), + } + } +} + +#[derive(Debug)] +pub enum PipelineError { + Linkage(wgt::ShaderStage, String), + Device(Error), +} + +impl fmt::Display for PipelineError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::Linkage(stage, ref message) => { + write!(f, "linkage failed for stage {:?}: {}", stage, message) + } + Self::Device(_) => Ok(()), + } + } +} +impl std::error::Error for PipelineError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match *self { + Self::Linkage(..) => None, + Self::Device(ref parent) => Some(parent), + } + } +} + +pub trait Api: Clone + Sized { + type Instance: Instance; type Surface: Surface; type Adapter: Adapter; type Device: Device; @@ -45,14 +116,23 @@ pub trait Api: Sized { type RenderPass: RenderPass; type ComputePass: ComputePass; - type Buffer; - type QuerySet; - type Texture; - type SwapChainTexture; - type TextureView; - type Sampler; + type Buffer: fmt::Debug + Send + Sync; + type QuerySet: fmt::Debug + Send + Sync; + type Texture: fmt::Debug + Send + Sync; + type SwapChainTexture: fmt::Debug + Send + Sync; + type TextureView: fmt::Debug + Send + Sync; + type Sampler: fmt::Debug + Send + Sync; - unsafe fn enumerate_adapters(&self) -> Vec>; + type BindGroupLayout; + type BindGroup: fmt::Debug + Send + Sync; + type PipelineLayout; + type ShaderModule: fmt::Debug + Send + Sync; + type RenderPipeline; + type ComputePipeline; +} + +pub trait Instance { + unsafe fn enumerate_adapters(&self) -> Vec>; } pub trait Surface {} @@ -102,10 +182,44 @@ pub trait Device { desc: &TextureViewDescriptor, + ) -> Result; + unsafe fn destroy_pipeline_layout(&self, pipeline_layout: A::PipelineLayout); + unsafe fn create_bind_group( + &self, + desc: &BindGroupDescriptor, + ) -> Result; + unsafe fn destroy_bind_group(&self, group: A::BindGroup); + + unsafe fn create_shader_module( + &self, + desc: &ShaderModuleDescriptor, + shader: NagaShader, + ) -> Result; + unsafe fn destroy_shader_module(&self, module: A::ShaderModule); + unsafe fn create_render_pipeline( + &self, + desc: &RenderPipelineDescriptor, + ) -> Result; + unsafe fn destroy_render_pipeline(&self, pipeline: A::RenderPipeline); + unsafe fn create_compute_pipeline( + &self, + desc: &ComputePipelineDescriptor, + ) -> Result; + unsafe fn destroy_compute_pipeline(&self, pipeline: A::ComputePipeline); } pub trait Queue { @@ -125,38 +239,6 @@ pub trait CommandBuffer { pub trait RenderPass {} pub trait ComputePass {} -#[derive(Debug)] -pub struct Alignments { - /// The alignment of the start of the buffer used as a GPU copy source. - pub buffer_copy_offset: wgt::BufferSize, - /// The alignment of the row pitch of the texture data stored in a buffer that is - /// used in a GPU copy operation. - pub buffer_copy_pitch: wgt::BufferSize, - pub storage_buffer_offset: wgt::BufferSize, - pub uniform_buffer_offset: wgt::BufferSize, -} - -#[derive(Debug)] -pub struct Capabilities { - pub limits: wgt::Limits, - pub alignments: Alignments, - pub downlevel: wgt::DownlevelCapabilities, -} - -#[derive(Debug)] -pub struct ExposedAdapter { - pub adapter: A::Adapter, - pub info: wgt::AdapterInfo, - pub features: wgt::Features, - pub capabilities: Capabilities, -} - -#[derive(Debug)] -pub struct OpenDevice { - pub device: A::Device, - pub queue: A::Queue, -} - bitflags!( /// Texture format capability flags. pub struct TextureFormatCapability: u32 { @@ -207,6 +289,87 @@ impl From for FormatAspect { } } +bitflags::bitflags! { + /// Similar to `wgt::BufferUsage` but for internal use. + pub struct BufferUse: u32 { + const MAP_READ = 1; + const MAP_WRITE = 2; + const COPY_SRC = 4; + const COPY_DST = 8; + const INDEX = 16; + const VERTEX = 32; + const UNIFORM = 64; + const STORAGE_LOAD = 128; + const STORAGE_STORE = 256; + const INDIRECT = 512; + /// The combination of all read-only usages. + const READ_ALL = Self::MAP_READ.bits | Self::COPY_SRC.bits | + Self::INDEX.bits | Self::VERTEX.bits | Self::UNIFORM.bits | + Self::STORAGE_LOAD.bits | Self::INDIRECT.bits; + /// The combination of all write-only and read-write usages. + const WRITE_ALL = Self::MAP_WRITE.bits | Self::COPY_DST.bits | Self::STORAGE_STORE.bits; + /// The combination of all usages that the are guaranteed to be be ordered by the hardware. + /// If a usage is not ordered, then even if it doesn't change between draw calls, there + /// still need to be pipeline barriers inserted for synchronization. + const ORDERED = Self::READ_ALL.bits | Self::MAP_WRITE.bits | Self::COPY_DST.bits; + } +} + +bitflags::bitflags! { + /// Similar to `wgt::TextureUsage` but for internal use. + pub struct TextureUse: u32 { + const COPY_SRC = 1; + const COPY_DST = 2; + const SAMPLED = 4; + const COLOR_TARGET = 8; + const DEPTH_STENCIL_READ = 16; + const DEPTH_STENCIL_WRITE = 32; + const STORAGE_LOAD = 64; + const STORAGE_STORE = 128; + /// The combination of all read-only usages. + const READ_ALL = Self::COPY_SRC.bits | Self::SAMPLED.bits | Self::DEPTH_STENCIL_READ.bits | Self::STORAGE_LOAD.bits; + /// The combination of all write-only and read-write usages. + const WRITE_ALL = Self::COPY_DST.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_STORE.bits; + /// The combination of all usages that the are guaranteed to be be ordered by the hardware. + /// If a usage is not ordered, then even if it doesn't change between draw calls, there + /// still need to be pipeline barriers inserted for synchronization. + const ORDERED = Self::READ_ALL.bits | Self::COPY_DST.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits; + const UNINITIALIZED = 0xFFFF; + } +} + +#[derive(Debug)] +pub struct Alignments { + /// The alignment of the start of the buffer used as a GPU copy source. + pub buffer_copy_offset: wgt::BufferSize, + /// The alignment of the row pitch of the texture data stored in a buffer that is + /// used in a GPU copy operation. + pub buffer_copy_pitch: wgt::BufferSize, + pub storage_buffer_offset: wgt::BufferSize, + pub uniform_buffer_offset: wgt::BufferSize, +} + +#[derive(Debug)] +pub struct Capabilities { + pub limits: wgt::Limits, + pub alignments: Alignments, + pub downlevel: wgt::DownlevelCapabilities, +} + +#[derive(Debug)] +pub struct ExposedAdapter { + pub adapter: A::Adapter, + pub info: wgt::AdapterInfo, + pub features: wgt::Features, + pub capabilities: Capabilities, +} + +#[derive(Debug)] +pub struct OpenDevice { + pub device: A::Device, + pub queue: A::Queue, +} + #[derive(Clone, Debug)] pub struct TextureViewDescriptor { pub label: L, @@ -227,10 +390,9 @@ impl TextureViewDescriptor { } } -/// Describes a [`Sampler`] #[derive(Clone, Debug)] -pub struct SamplerDescriptor { - pub label: L, +pub struct SamplerDescriptor<'a> { + pub label: Label<'a>, pub address_modes: [wgt::AddressMode; 3], pub mag_filter: wgt::FilterMode, pub min_filter: wgt::FilterMode, @@ -241,6 +403,143 @@ pub struct SamplerDescriptor { pub border_color: Option, } +#[derive(Clone, Debug)] +pub struct BindGroupLayoutDescriptor<'a> { + pub label: Label<'a>, + pub entries: Cow<'a, [wgt::BindGroupLayoutEntry]>, +} + +#[derive(Clone, Debug)] +pub struct PipelineLayoutDescriptor<'a, A: Api> { + pub label: Label<'a>, + pub bind_group_layouts: Cow<'a, [&'a A::BindGroupLayout]>, + pub push_constant_ranges: Cow<'a, [wgt::PushConstantRange]>, +} + +#[derive(Debug)] +pub struct BufferBinding<'a, A: Api> { + pub buffer: &'a A::Buffer, + pub offset: wgt::BufferAddress, + pub size: Option, +} + +// Rust gets confused about the impl requirements for `A` +impl Clone for BufferBinding<'_, A> { + fn clone(&self) -> Self { + Self { + buffer: self.buffer, + offset: self.offset, + size: self.size, + } + } +} + +#[derive(Debug)] +pub enum BindingResource<'a, A: Api> { + Buffers(SmallVec<[BufferBinding<'a, A>; 1]>), + Sampler(&'a A::Sampler), + TextureViews(SmallVec<[&'a A::TextureView; 1]>, TextureUse), +} + +// Rust gets confused about the impl requirements for `A` +impl Clone for BindingResource<'_, A> { + fn clone(&self) -> Self { + match *self { + Self::Buffers(ref slice) => Self::Buffers(slice.clone()), + Self::Sampler(sampler) => Self::Sampler(sampler), + Self::TextureViews(ref slice, usage) => Self::TextureViews(slice.clone(), usage), + } + } +} + +#[derive(Clone, Debug)] +pub struct BindGroupEntry<'a, A: Api> { + pub binding: u32, + pub resource: BindingResource<'a, A>, +} + +#[derive(Clone, Debug)] +pub struct BindGroupDescriptor<'a, A: Api> { + pub label: Label<'a>, + pub layout: &'a A::BindGroupLayout, + pub entries: Cow<'a, [BindGroupEntry<'a, A>]>, +} + +/// Naga shader module. +pub struct NagaShader { + /// Shader module IR. + pub module: naga::Module, + /// Analysis information of the module. + pub info: naga::valid::ModuleInfo, +} + +pub struct ShaderModuleDescriptor<'a> { + pub label: Label<'a>, +} + +/// Describes a programmable pipeline stage. +#[derive(Debug)] +pub struct ProgrammableStage<'a, A: Api> { + /// The compiled shader module for this stage. + pub module: &'a A::ShaderModule, + /// The name of the entry point in the compiled shader. There must be a function that returns + /// void with this name in the shader. + pub entry_point: Cow<'a, str>, +} + +// Rust gets confused about the impl requirements for `A` +impl Clone for ProgrammableStage<'_, A> { + fn clone(&self) -> Self { + Self { + module: self.module, + entry_point: self.entry_point.clone(), + } + } +} + +/// Describes a compute pipeline. +#[derive(Clone, Debug)] +pub struct ComputePipelineDescriptor<'a, A: Api> { + pub label: Label<'a>, + /// The layout of bind groups for this pipeline. + pub layout: &'a A::PipelineLayout, + /// The compiled compute stage and its entry point. + pub stage: ProgrammableStage<'a, A>, +} + +/// Describes how the vertex buffer is interpreted. +#[derive(Clone, Debug)] +pub struct VertexBufferLayout<'a> { + /// The stride, in bytes, between elements of this buffer. + pub array_stride: wgt::BufferAddress, + /// How often this vertex buffer is "stepped" forward. + pub step_mode: wgt::InputStepMode, + /// The list of attributes which comprise a single vertex. + pub attributes: Cow<'a, [wgt::VertexAttribute]>, +} + +/// Describes a render (graphics) pipeline. +#[derive(Clone, Debug)] +pub struct RenderPipelineDescriptor<'a, A: Api> { + pub label: Label<'a>, + /// The layout of bind groups for this pipeline. + pub layout: &'a A::PipelineLayout, + /// The format of any vertex buffers used with this pipeline. + pub vertex_buffers: Cow<'a, [VertexBufferLayout<'a>]>, + /// The vertex stage for this pipeline. + pub vertex_stage: ProgrammableStage<'a, A>, + /// The properties of the pipeline at the primitive assembly and rasterization level. + pub primitive: wgt::PrimitiveState, + /// The effect of draw calls on the depth and stencil aspects of the output target, if any. + pub depth_stencil: Option, + /// The multi-sampling properties of the pipeline. + pub multisample: wgt::MultisampleState, + /// The fragment stage for this pipeline. + pub fragment_stage: ProgrammableStage<'a, A>, + /// The effect of draw calls on the color aspect of the output target. + pub color_targets: Cow<'a, [wgt::ColorTargetState]>, +} + #[test] fn test_default_limits() { let limits = wgt::Limits::default();