diff --git a/player/tests/data/bind-group.ron b/player/tests/data/bind-group.ron index 0c16741963..8a4ecb3c5f 100644 --- a/player/tests/data/bind-group.ron +++ b/player/tests/data/bind-group.ron @@ -34,11 +34,9 @@ ( binding: 0, visibility: (bits: 0x3), - ty: UniformBuffer( - dynamic: false, - min_binding_size: None, + ty: Buffer( + ty: Uniform, ), - count: None, ), ], )), diff --git a/wgpu-core/src/binding_model.rs b/wgpu-core/src/binding_model.rs index cc5d270a5e..569f3b8b72 100644 --- a/wgpu-core/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -193,22 +193,30 @@ impl BindingTypeMaxCountValidator { pub(crate) fn add_binding(&mut self, binding: &wgt::BindGroupLayoutEntry) { let count = binding.count.map_or(1, |count| count.get()); match binding.ty { - wgt::BindingType::UniformBuffer { dynamic, .. } => { + wgt::BindingType::Buffer { + ty: wgt::BufferBindingType::Uniform, + has_dynamic_offset, + .. + } => { self.uniform_buffers.add(binding.visibility, count); - if dynamic { + if has_dynamic_offset { self.dynamic_uniform_buffers += count; } } - wgt::BindingType::StorageBuffer { dynamic, .. } => { + wgt::BindingType::Buffer { + ty: wgt::BufferBindingType::Storage { .. }, + has_dynamic_offset, + .. + } => { self.storage_buffers.add(binding.visibility, count); - if dynamic { + if has_dynamic_offset { self.dynamic_storage_buffers += count; } } wgt::BindingType::Sampler { .. } => { self.samplers.add(binding.visibility, count); } - wgt::BindingType::SampledTexture { .. } => { + wgt::BindingType::Texture { .. } => { self.sampled_textures.add(binding.visibility, count); } wgt::BindingType::StorageTexture { .. } => { diff --git a/wgpu-core/src/conv.rs b/wgpu-core/src/conv.rs index f134c8322a..58093b37e6 100644 --- a/wgpu-core/src/conv.rs +++ b/wgpu-core/src/conv.rs @@ -84,36 +84,33 @@ pub fn map_binding_type(binding: &wgt::BindGroupLayoutEntry) -> hal::pso::Descri use hal::pso; use wgt::BindingType as Bt; match binding.ty { - Bt::UniformBuffer { - dynamic, + Bt::Buffer { + ty, + has_dynamic_offset, min_binding_size: _, } => pso::DescriptorType::Buffer { - ty: pso::BufferDescriptorType::Uniform, - format: pso::BufferDescriptorFormat::Structured { - dynamic_offset: dynamic, - }, - }, - Bt::StorageBuffer { - readonly, - dynamic, - min_binding_size: _, - } => pso::DescriptorType::Buffer { - ty: pso::BufferDescriptorType::Storage { - read_only: readonly, + ty: match ty { + wgt::BufferBindingType::Uniform => pso::BufferDescriptorType::Uniform, + wgt::BufferBindingType::Storage { read_only } => { + pso::BufferDescriptorType::Storage { read_only } + } }, format: pso::BufferDescriptorFormat::Structured { - dynamic_offset: dynamic, + dynamic_offset: has_dynamic_offset, }, }, - Bt::Sampler { comparison: _ } => pso::DescriptorType::Sampler, - Bt::SampledTexture { .. } => pso::DescriptorType::Image { + Bt::Sampler { .. } => pso::DescriptorType::Sampler, + Bt::Texture { .. } => pso::DescriptorType::Image { ty: pso::ImageDescriptorType::Sampled { with_sampler: false, }, }, - Bt::StorageTexture { readonly, .. } => pso::DescriptorType::Image { + Bt::StorageTexture { access, .. } => pso::DescriptorType::Image { ty: pso::ImageDescriptorType::Storage { - read_only: readonly, + read_only: match access { + wgt::StorageTextureAccess::ReadOnly => true, + _ => false, + }, }, }, } diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 4f047825ff..c464fe4ca4 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -989,7 +989,7 @@ impl Device { for binding in entry_map.values() { if binding.count.is_some() { match binding.ty { - wgt::BindingType::SampledTexture { .. } => { + wgt::BindingType::Texture { .. } => { if !self .features .contains(wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY) @@ -1100,30 +1100,12 @@ impl Device { .ok_or(Error::MissingBindingDeclaration(binding))?; let descriptors: SmallVec<[_; 1]> = match entry.resource { Br::Buffer(ref bb) => { - let (pub_usage, internal_use, min_size, dynamic) = match decl.ty { - wgt::BindingType::UniformBuffer { - dynamic, + let (binding_ty, dynamic, min_size) = match decl.ty { + wgt::BindingType::Buffer { + ty, + has_dynamic_offset, min_binding_size, - } => ( - wgt::BufferUsage::UNIFORM, - resource::BufferUse::UNIFORM, - min_binding_size, - dynamic, - ), - wgt::BindingType::StorageBuffer { - dynamic, - min_binding_size, - readonly, - } => ( - wgt::BufferUsage::STORAGE, - if readonly { - resource::BufferUse::STORAGE_LOAD - } else { - resource::BufferUse::STORAGE_STORE - }, - min_binding_size, - dynamic, - ), + } => (ty, has_dynamic_offset, min_binding_size), _ => { return Err(Error::WrongBindingType { binding, @@ -1132,6 +1114,19 @@ impl Device { }) } }; + let (pub_usage, internal_use) = match binding_ty { + wgt::BufferBindingType::Uniform => { + (wgt::BufferUsage::UNIFORM, resource::BufferUse::UNIFORM) + } + wgt::BufferBindingType::Storage { read_only } => ( + wgt::BufferUsage::STORAGE, + if read_only { + resource::BufferUse::STORAGE_LOAD + } else { + resource::BufferUse::STORAGE_STORE + }, + ), + }; if bb.offset % wgt::BIND_BUFFER_ALIGNMENT != 0 { return Err(Error::UnalignedBufferOffset(bb.offset)); @@ -1161,7 +1156,7 @@ impl Device { None => (buffer.size - bb.offset, buffer.size), }; - if pub_usage == wgt::BufferUsage::UNIFORM + if binding_ty == wgt::BufferBindingType::Uniform && (self.limits.max_uniform_buffer_binding_size as u64) < bind_size { return Err(Error::UniformBufferRangeTooLarge); @@ -1192,7 +1187,10 @@ impl Device { } Br::Sampler(id) => { match decl.ty { - wgt::BindingType::Sampler { comparison } => { + wgt::BindingType::Sampler { + filtering: _, + comparison, + } => { let sampler = used .samplers .use_extend(&*sampler_guard, id, (), ()) @@ -1220,15 +1218,18 @@ impl Device { .use_extend(&*texture_view_guard, id, (), ()) .map_err(|_| Error::InvalidTextureView(id))?; let (pub_usage, internal_use) = match decl.ty { - wgt::BindingType::SampledTexture { .. } => { + wgt::BindingType::Texture { .. } => { (wgt::TextureUsage::SAMPLED, resource::TextureUse::SAMPLED) } - wgt::BindingType::StorageTexture { readonly, .. } => ( + wgt::BindingType::StorageTexture { access, .. } => ( wgt::TextureUsage::STORAGE, - if readonly { - resource::TextureUse::STORAGE_LOAD - } else { - resource::TextureUse::STORAGE_STORE + match access { + wgt::StorageTextureAccess::ReadOnly => { + resource::TextureUse::STORAGE_LOAD + } + wgt::StorageTextureAccess::WriteOnly => { + resource::TextureUse::STORAGE_STORE + } }, ), _ => return Err(Error::WrongBindingType { @@ -1290,7 +1291,7 @@ impl Device { } let (pub_usage, internal_use) = match decl.ty { - wgt::BindingType::SampledTexture { .. } => { + wgt::BindingType::Texture { .. } => { (wgt::TextureUsage::SAMPLED, resource::TextureUse::SAMPLED) } _ => { diff --git a/wgpu-core/src/validation.rs b/wgpu-core/src/validation.rs index d7d7034f5e..6cfe12d53f 100644 --- a/wgpu-core/src/validation.rs +++ b/wgpu-core/src/validation.rs @@ -240,19 +240,17 @@ fn check_binding_use( match module.types[var.ty].inner { naga::TypeInner::Struct { ref members } => { let (allowed_usage, min_size) = match entry.ty { - BindingType::UniformBuffer { - dynamic: _, + BindingType::Buffer { + ty, + has_dynamic_offset: _, min_binding_size, - } => (naga::GlobalUse::LOAD, min_binding_size), - BindingType::StorageBuffer { - dynamic: _, - min_binding_size, - readonly, } => { - let global_use = if readonly { - naga::GlobalUse::LOAD - } else { - naga::GlobalUse::all() + let global_use = match ty { + wgt::BufferBindingType::Uniform + | wgt::BufferBindingType::Storage { read_only: true } => { + naga::GlobalUse::LOAD + } + wgt::BufferBindingType::Storage { read_only: _ } => naga::GlobalUse::all(), }; (global_use, min_binding_size) } @@ -271,7 +269,10 @@ fn check_binding_use( Ok(allowed_usage) } naga::TypeInner::Sampler { comparison } => match entry.ty { - BindingType::Sampler { comparison: cmp } => { + BindingType::Sampler { + filtering: _, + comparison: cmp, + } => { if cmp == comparison { Ok(naga::GlobalUse::LOAD) } else { @@ -286,8 +287,8 @@ fn check_binding_use( class, } => { let view_dimension = match entry.ty { - BindingType::SampledTexture { dimension, .. } - | BindingType::StorageTexture { dimension, .. } => dimension, + BindingType::Texture { view_dimension, .. } + | BindingType::StorageTexture { view_dimension, .. } => view_dimension, _ => { return Err(BindingError::WrongTextureViewDimension { dim, @@ -321,40 +322,38 @@ fn check_binding_use( } } let (expected_class, usage) = match entry.ty { - BindingType::SampledTexture { - dimension: _, - component_type, - multisampled, + BindingType::Texture { + sample_type, + view_dimension: _, + multisampled: multi, } => { - let (kind, comparison) = match component_type { - wgt::TextureComponentType::Float => (naga::ScalarKind::Float, false), - wgt::TextureComponentType::Sint => (naga::ScalarKind::Sint, false), - wgt::TextureComponentType::Uint => (naga::ScalarKind::Uint, false), - wgt::TextureComponentType::DepthComparison => { - (naga::ScalarKind::Float, true) - } - }; - let class = if comparison { - naga::ImageClass::Depth - } else { - naga::ImageClass::Sampled { - kind, - multi: multisampled, - } + let class = match sample_type { + wgt::TextureSampleType::Float { .. } => naga::ImageClass::Sampled { + kind: naga::ScalarKind::Float, + multi, + }, + wgt::TextureSampleType::Sint => naga::ImageClass::Sampled { + kind: naga::ScalarKind::Sint, + multi, + }, + wgt::TextureSampleType::Uint => naga::ImageClass::Sampled { + kind: naga::ScalarKind::Uint, + multi, + }, + wgt::TextureSampleType::Depth => naga::ImageClass::Depth, }; (class, naga::GlobalUse::LOAD) } BindingType::StorageTexture { - readonly, + access, format, - dimension: _, + view_dimension: _, } => { let naga_format = map_storage_format_to_naga(format) .ok_or(BindingError::BadStorageFormat(format))?; - let usage = if readonly { - naga::GlobalUse::LOAD - } else { - naga::GlobalUse::STORE + let usage = match access { + wgt::StorageTextureAccess::ReadOnly => naga::GlobalUse::LOAD, + wgt::StorageTextureAccess::WriteOnly => naga::GlobalUse::STORE, }; (naga::ImageClass::Storage(naga_format), usage) } @@ -780,31 +779,37 @@ fn derive_binding_type( let ty = &module.types[var.ty]; Ok(match ty.inner { naga::TypeInner::Struct { ref members } => { - let dynamic = false; + let has_dynamic_offset = false; let mut actual_size = 0; for (i, member) in members.iter().enumerate() { actual_size += get_aligned_type_size(module, member.ty, i + 1 == members.len()); } match var.class { - naga::StorageClass::Uniform => BindingType::UniformBuffer { - dynamic, + naga::StorageClass::Uniform => BindingType::Buffer { + ty: wgt::BufferBindingType::Uniform, + has_dynamic_offset, min_binding_size: wgt::BufferSize::new(actual_size), }, - naga::StorageClass::Storage => BindingType::StorageBuffer { - dynamic, + naga::StorageClass::Storage => BindingType::Buffer { + ty: wgt::BufferBindingType::Storage { + read_only: !usage.contains(naga::GlobalUse::STORE), + }, + has_dynamic_offset, min_binding_size: wgt::BufferSize::new(actual_size), - readonly: !usage.contains(naga::GlobalUse::STORE), }, _ => return Err(BindingError::WrongType), } } - naga::TypeInner::Sampler { comparison } => BindingType::Sampler { comparison }, + naga::TypeInner::Sampler { comparison } => BindingType::Sampler { + filtering: true, + comparison, + }, naga::TypeInner::Image { dim, arrayed, class, } => { - let dimension = match dim { + let view_dimension = match dim { naga::ImageDimension::D1 => wgt::TextureViewDimension::D1, naga::ImageDimension::D2 if arrayed => wgt::TextureViewDimension::D2Array, naga::ImageDimension::D2 => wgt::TextureViewDimension::D2, @@ -813,23 +818,30 @@ fn derive_binding_type( naga::ImageDimension::Cube => wgt::TextureViewDimension::Cube, }; match class { - naga::ImageClass::Sampled { multi, kind } => BindingType::SampledTexture { - dimension, - component_type: match kind { - naga::ScalarKind::Float => wgt::TextureComponentType::Float, - naga::ScalarKind::Sint => wgt::TextureComponentType::Sint, - naga::ScalarKind::Uint => wgt::TextureComponentType::Uint, + naga::ImageClass::Sampled { multi, kind } => BindingType::Texture { + sample_type: match kind { + naga::ScalarKind::Float => { + wgt::TextureSampleType::Float { filterable: true } + } + naga::ScalarKind::Sint => wgt::TextureSampleType::Sint, + naga::ScalarKind::Uint => wgt::TextureSampleType::Uint, naga::ScalarKind::Bool => unreachable!(), }, + view_dimension, multisampled: multi, }, - naga::ImageClass::Depth => BindingType::SampledTexture { - dimension, - component_type: wgt::TextureComponentType::DepthComparison, + naga::ImageClass::Depth => BindingType::Texture { + sample_type: wgt::TextureSampleType::Depth, + view_dimension, multisampled: false, }, naga::ImageClass::Storage(format) => BindingType::StorageTexture { - dimension, + access: if usage.contains(naga::GlobalUse::STORE) { + wgt::StorageTextureAccess::WriteOnly + } else { + wgt::StorageTextureAccess::ReadOnly + }, + view_dimension, format: { let f = map_storage_format_from_naga(format); let original = map_storage_format_to_naga(f) @@ -837,7 +849,6 @@ fn derive_binding_type( debug_assert_eq!(format, original); f }, - readonly: !usage.contains(naga::GlobalUse::STORE), }, } } diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index b150b53e13..a3a29c8c8c 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -447,6 +447,12 @@ pub enum TextureViewDimension { D3, } +impl Default for TextureViewDimension { + fn default() -> Self { + Self::D2 + } +} + impl TextureViewDimension { /// Get the texture dimension required fo this texture view dimension. pub fn compatible_texture_dimension(self) -> TextureDimension { @@ -1225,9 +1231,9 @@ bitflags::bitflags! { const INDEX = 16; /// Allow a buffer to be the vertex buffer in a draw operation. const VERTEX = 32; - /// Allow a buffer to be a [`BindingType::UniformBuffer`] inside a bind group. + /// Allow a buffer to be a [`BufferBindingType::Uniform`] inside a bind group. const UNIFORM = 64; - /// Allow a buffer to be a [`BindingType::StorageBuffer`] inside a bind group. + /// Allow a buffer to be a [`BufferBindingType::Storage`] inside a bind group. const STORAGE = 128; /// Allow a buffer to be the indirect buffer in an indirect draw call. const INDIRECT = 256; @@ -1324,7 +1330,7 @@ bitflags::bitflags! { /// Allows a texture to be the destination in a [`CommandEncoder::copy_texture_to_buffer`], /// [`CommandEncoder::copy_texture_to_texture`], or [`Queue::write_texture`] operation. const COPY_DST = 2; - /// Allows a texture to be a [`BindingType::SampledTexture`] in a bind group. + /// Allows a texture to be a [`BindingType::Texture`] in a bind group. const SAMPLED = 4; /// Allows a texture to be a [`BindingType::StorageTexture`] in a bind group. const STORAGE = 8; @@ -1485,7 +1491,7 @@ pub struct TextureDescriptor { pub size: Extent3d, /// Mip count of texture. For a texture with no extra mips, this must be 1. pub mip_level_count: u32, - /// Sample count of texture. If this is not 1, texture must have [`BindingType::SampledTexture::multisampled`] set to true. + /// Sample count of texture. If this is not 1, texture must have [`BindingType::Texture::multisampled`] set to true. pub sample_count: u32, /// Dimensions of the texture. pub dimension: TextureDimension, @@ -1640,85 +1646,6 @@ impl Default for RenderBundleDescriptor> { } } -/// Type of data shaders will read from a texture. -/// -/// Only relevant for [`BindingType::SampledTexture`] bindings. See [`TextureFormat`] for more information. -#[repr(C)] -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -#[cfg_attr(feature = "trace", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum TextureComponentType { - /// They see it as a floating point number `texture1D`, `texture2D` etc - Float, - /// They see it as a signed integer `itexture1D`, `itexture2D` etc - Sint, - /// They see it as a unsigned integer `utexture1D`, `utexture2D` etc - Uint, - /// They see it as a floating point 0-1 result of comparison, i.e. `shadowTexture2D` - DepthComparison, -} - -impl From for TextureComponentType { - fn from(format: TextureFormat) -> Self { - match format { - TextureFormat::R8Uint - | TextureFormat::R16Uint - | TextureFormat::Rg8Uint - | TextureFormat::R32Uint - | TextureFormat::Rg16Uint - | TextureFormat::Rgba8Uint - | TextureFormat::Rg32Uint - | TextureFormat::Rgba16Uint - | TextureFormat::Rgba32Uint => Self::Uint, - - TextureFormat::R8Sint - | TextureFormat::R16Sint - | TextureFormat::Rg8Sint - | TextureFormat::R32Sint - | TextureFormat::Rg16Sint - | TextureFormat::Rgba8Sint - | TextureFormat::Rg32Sint - | TextureFormat::Rgba16Sint - | TextureFormat::Rgba32Sint => Self::Sint, - - TextureFormat::R8Unorm - | TextureFormat::R8Snorm - | TextureFormat::R16Float - | TextureFormat::R32Float - | TextureFormat::Rg8Unorm - | TextureFormat::Rg8Snorm - | TextureFormat::Rg16Float - | TextureFormat::Rg11b10Float - | TextureFormat::Rg32Float - | TextureFormat::Rgba8Snorm - | TextureFormat::Rgba16Float - | TextureFormat::Rgba32Float - | TextureFormat::Rgba8Unorm - | TextureFormat::Rgba8UnormSrgb - | TextureFormat::Bgra8Unorm - | TextureFormat::Bgra8UnormSrgb - | TextureFormat::Rgb10a2Unorm - | TextureFormat::Depth32Float - | TextureFormat::Depth24Plus - | TextureFormat::Depth24PlusStencil8 - | TextureFormat::Bc1RgbaUnorm - | TextureFormat::Bc1RgbaUnormSrgb - | TextureFormat::Bc2RgbaUnorm - | TextureFormat::Bc2RgbaUnormSrgb - | TextureFormat::Bc3RgbaUnorm - | TextureFormat::Bc3RgbaUnormSrgb - | TextureFormat::Bc4RUnorm - | TextureFormat::Bc4RSnorm - | TextureFormat::Bc5RgUnorm - | TextureFormat::Bc5RgSnorm - | TextureFormat::Bc6hRgbSfloat - | TextureFormat::Bc6hRgbUfloat - | TextureFormat::Bc7RgbaUnorm - | TextureFormat::Bc7RgbaUnormSrgb => Self::Float, - } - } -} - /// Layout of a texture in a buffer's memory. #[repr(C)] #[derive(Clone, Debug, Default)] @@ -1743,13 +1670,13 @@ pub struct TextureDataLayout { pub rows_per_image: u32, } -/// Specific type of a binding. +/// Specific type of a buffer binding. /// -/// WebGPU spec: https://gpuweb.github.io/gpuweb/#dictdef-gpubindgrouplayoutentry -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +/// WebGPU spec: https://gpuweb.github.io/gpuweb/#enumdef-gpubufferbindingtype +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] #[cfg_attr(feature = "trace", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum BindingType { +pub enum BufferBindingType { /// A buffer for uniform values. /// /// Example GLSL syntax: @@ -1760,16 +1687,7 @@ pub enum BindingType { /// vec2 anotherUniform; /// }; /// ``` - UniformBuffer { - /// Indicates that the binding has a dynamic offset. - /// One offset must be passed to [`RenderPass::set_bind_group`] for each dynamic binding in increasing order of binding number. - dynamic: bool, - /// Minimum size of the corresponding `BufferBinding` required to match this entry. - /// When pipeline is created, the size has to cover at least the corresponding structure in the shader - /// plus one element of the unbound array, which can only be last in the structure. - /// If `None`, the check is performed at draw call time instead of pipeline and bind group creation. - min_binding_size: Option, - }, + Uniform, /// A storage buffer. /// /// Example GLSL syntax: @@ -1778,16 +1696,9 @@ pub enum BindingType { /// vec4 myElement[]; /// }; /// ``` - StorageBuffer { - /// Indicates that the binding has a dynamic offset. - /// One offset must be passed to [`RenderPass::set_bind_group`] for each dynamic binding in increasing order of binding number. - dynamic: bool, - /// Minimum size of the corresponding `BufferBinding` required to match this entry. - /// When pipeline is created, the size has to cover at least the corresponding structure in the shader - /// plus one element of the unbound array, which can only be last in the structure. - /// If `None`, the check is performed at draw call time instead of pipeline and bind group creation. - min_binding_size: Option, - /// The buffer can only be read in the shader and it must be annotated with `readonly`. + Storage { + /// If `true`, the buffer can only be read in the shader, + /// and it must be annotated with `readonly`. /// /// Example GLSL syntax: /// ```cpp,ignore @@ -1795,7 +1706,176 @@ pub enum BindingType { /// vec4 myElement[]; /// }; /// ``` - readonly: bool, + read_only: bool, + }, +} + +impl Default for BufferBindingType { + fn default() -> Self { + Self::Uniform + } +} + +/// Specific type of a sample in a texture binding. +/// +/// WebGPU spec: https://gpuweb.github.io/gpuweb/#enumdef-gputexturesampletype +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "trace", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub enum TextureSampleType { + /// Sampling returns floats. + /// + /// If `filterable` is false, the texture can't be sampled with + /// a filtering sampler. + /// + /// Example GLSL syntax: + /// ```cpp,ignore + /// layout(binding = 0) + /// uniform texture2D t; + /// ``` + Float { filterable: bool }, + /// Sampling does the depth reference comparison. + /// + /// Example GLSL syntax: + /// ```cpp,ignore + /// layout(binding = 0) + /// uniform texture2DShadow t; + /// ``` + Depth, + /// Sampling returns signed integers. + /// + /// Example GLSL syntax: + /// ```cpp,ignore + /// layout(binding = 0) + /// uniform itexture2D t; + /// ``` + Sint, + /// Sampling returns unsigned integers. + /// + /// Example GLSL syntax: + /// ```cpp,ignore + /// layout(binding = 0) + /// uniform utexture2D t; + /// ``` + Uint, +} + +impl Default for TextureSampleType { + fn default() -> Self { + Self::Float { filterable: true } + } +} + +impl From for TextureSampleType { + fn from(format: TextureFormat) -> Self { + match format { + TextureFormat::R8Uint + | TextureFormat::R16Uint + | TextureFormat::Rg8Uint + | TextureFormat::R32Uint + | TextureFormat::Rg16Uint + | TextureFormat::Rgba8Uint + | TextureFormat::Rg32Uint + | TextureFormat::Rgba16Uint + | TextureFormat::Rgba32Uint => Self::Uint, + + TextureFormat::R8Sint + | TextureFormat::R16Sint + | TextureFormat::Rg8Sint + | TextureFormat::R32Sint + | TextureFormat::Rg16Sint + | TextureFormat::Rgba8Sint + | TextureFormat::Rg32Sint + | TextureFormat::Rgba16Sint + | TextureFormat::Rgba32Sint => Self::Sint, + + TextureFormat::R32Float | TextureFormat::Rg32Float | TextureFormat::Rgba32Float => { + Self::Float { filterable: false } + } + + TextureFormat::R8Unorm + | TextureFormat::R8Snorm + | TextureFormat::R16Float + | TextureFormat::Rg8Unorm + | TextureFormat::Rg8Snorm + | TextureFormat::Rg16Float + | TextureFormat::Rg11b10Float + | TextureFormat::Rgba8Snorm + | TextureFormat::Rgba16Float + | TextureFormat::Rgba8Unorm + | TextureFormat::Rgba8UnormSrgb + | TextureFormat::Bgra8Unorm + | TextureFormat::Bgra8UnormSrgb + | TextureFormat::Rgb10a2Unorm + | TextureFormat::Bc1RgbaUnorm + | TextureFormat::Bc1RgbaUnormSrgb + | TextureFormat::Bc2RgbaUnorm + | TextureFormat::Bc2RgbaUnormSrgb + | TextureFormat::Bc3RgbaUnorm + | TextureFormat::Bc3RgbaUnormSrgb + | TextureFormat::Bc4RUnorm + | TextureFormat::Bc4RSnorm + | TextureFormat::Bc5RgUnorm + | TextureFormat::Bc5RgSnorm + | TextureFormat::Bc6hRgbSfloat + | TextureFormat::Bc6hRgbUfloat + | TextureFormat::Bc7RgbaUnorm + | TextureFormat::Bc7RgbaUnormSrgb => Self::Float { filterable: true }, + + TextureFormat::Depth32Float + | TextureFormat::Depth24Plus + | TextureFormat::Depth24PlusStencil8 => Self::Depth, + } + } +} + +/// Specific type of a sample in a texture binding. +/// +/// WebGPU spec: https://gpuweb.github.io/gpuweb/#enumdef-gpustoragetextureaccess +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "trace", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub enum StorageTextureAccess { + /// The texture can only be read in the shader and it must be annotated with `readonly`. + /// + /// Example GLSL syntax: + /// ```cpp,ignore + /// layout(set=0, binding=0, r32f) readonly uniform image2D myStorageImage; + /// ``` + ReadOnly, + /// The texture can only be read in the shader and it must be annotated with `writeonly`. + /// + /// Example GLSL syntax: + /// ```cpp,ignore + /// layout(set=0, binding=0, r32f) writeonly uniform image2D myStorageImage; + /// ``` + WriteOnly, +} + +/// Specific type of a binding. +/// +/// WebGPU spec: the enum of +/// - https://gpuweb.github.io/gpuweb/#dictdef-gpubufferbindinglayout +/// - https://gpuweb.github.io/gpuweb/#dictdef-gpusamplerbindinglayout +/// - https://gpuweb.github.io/gpuweb/#dictdef-gputexturebindinglayout +/// - https://gpuweb.github.io/gpuweb/#dictdef-gpustoragetexturebindinglayout +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "trace", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub enum BindingType { + /// A buffer binding. + Buffer { + ty: BufferBindingType, + /// Indicates that the binding has a dynamic offset. + /// One offset must be passed to [`RenderPass::set_bind_group`] for each dynamic binding in increasing order of binding number. + #[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))] + has_dynamic_offset: bool, + /// Minimum size of the corresponding `BufferBinding` required to match this entry. + /// When pipeline is created, the size has to cover at least the corresponding structure in the shader + /// plus one element of the unbound array, which can only be last in the structure. + /// If `None`, the check is performed at draw call time instead of pipeline and bind group creation. + #[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))] + min_binding_size: Option, }, /// A sampler that can be used to sample a texture. /// @@ -1805,23 +1885,27 @@ pub enum BindingType { /// uniform sampler s; /// ``` Sampler { + /// The sampling result is produced based on more than a single color sample from a texture, + /// e.g. when bilinear interpolation is enabled. + /// + /// A filtering sampler can only be used with a filterable texture. + filtering: bool, /// Use as a comparison sampler instead of a normal sampler. /// For more info take a look at the analogous functionality in OpenGL: https://www.khronos.org/opengl/wiki/Sampler_Object#Comparison_mode. comparison: bool, }, - /// A texture. + /// A texture binding. /// /// Example GLSL syntax: /// ```cpp,ignore /// layout(binding = 0) /// uniform texture2D t; /// ``` - SampledTexture { + Texture { + /// Sample type of the texture binding. + sample_type: TextureSampleType, /// Dimension of the texture view that is going to be sampled. - dimension: TextureViewDimension, - /// Component type of the texture. - /// This must be compatible with the format of the texture. - component_type: TextureComponentType, + view_dimension: TextureViewDimension, /// True if the texture has a sample count greater than 1. If this is true, /// the texture must be read from shaders with `texture1DMS`, `texture2DMS`, or `texture3DMS`, /// depending on `dimension`. @@ -1836,24 +1920,21 @@ pub enum BindingType { /// Note that the texture format must be specified in the shader as well. /// A list of valid formats can be found in the specification here: https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.html#layout-qualifiers StorageTexture { - /// Dimension of the texture view that is going to be sampled. - dimension: TextureViewDimension, + /// Allowed access to this texture. + access: StorageTextureAccess, /// Format of the texture. format: TextureFormat, - /// The texture can only be read in the shader and it must be annotated with `readonly`. - /// - /// Example GLSL syntax: - /// ```cpp,ignore - /// layout(set=0, binding=0, r32f) readonly uniform image2D myStorageImage; - /// ``` - readonly: bool, + /// Dimension of the texture view that is going to be sampled. + view_dimension: TextureViewDimension, }, } impl BindingType { pub fn has_dynamic_offset(&self) -> bool { match *self { - Self::UniformBuffer { dynamic, .. } | Self::StorageBuffer { dynamic, .. } => dynamic, + Self::Buffer { + has_dynamic_offset, .. + } => has_dynamic_offset, _ => false, } } @@ -1873,9 +1954,10 @@ pub struct BindGroupLayoutEntry { pub ty: BindingType, /// If this value is Some, indicates this entry is an array. Array size must be 1 or greater. /// - /// If this value is Some and `ty` is `BindingType::SampledTexture`, [`Capabilities::SAMPLED_TEXTURE_BINDING_ARRAY`] must be supported. + /// If this value is Some and `ty` is `BindingType::Texture`, [`Features::SAMPLED_TEXTURE_BINDING_ARRAY`] must be supported. /// /// If this value is Some and `ty` is any other variant, bind group creation will fail. + #[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))] pub count: Option, }