1047: Update bind group layout API to match upstream r=cwfitzgerald a=kvark

**Connections**
Follows https://github.com/gpuweb/gpuweb/pull/1076, https://github.com/gpuweb/gpuweb/pull/1223 (https://github.com/gpuweb/gpuweb/issues/1164), https://github.com/gpuweb/gpuweb/pull/1255, and https://github.com/gpuweb/gpuweb/pull/1256

**Description**
Aligns our API closer to the latest changes in WebGPU upstream. We technically don't have to do this, but I believe in the end it would be best if our API gets close to upstream.

Note: this is a sensitive change for the users, everybody will get their code broken. So please take a look at the API and see if something is missing or needs improvement, so that we don't have to go through the changes again afterwards.

**Testing**
Doesn't really need testing. Partially covered by the existing playtest.

Co-authored-by: Dzmitry Malyshau <kvarkus@gmail.com>
This commit is contained in:
bors[bot] 2020-11-30 22:31:49 +00:00 committed by GitHub
commit 67e652f471
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 341 additions and 244 deletions

View File

@ -34,11 +34,9 @@
(
binding: 0,
visibility: (bits: 0x3),
ty: UniformBuffer(
dynamic: false,
min_binding_size: None,
ty: Buffer(
ty: Uniform,
),
count: None,
),
],
)),

View File

@ -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 { .. } => {

View File

@ -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,
},
},
},
}

View File

@ -989,7 +989,7 @@ impl<B: GfxBackend> Device<B> {
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<B: GfxBackend> Device<B> {
.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<B: GfxBackend> Device<B> {
})
}
};
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<B: GfxBackend> Device<B> {
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<B: GfxBackend> Device<B> {
}
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<B: GfxBackend> Device<B> {
.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<B: GfxBackend> Device<B> {
}
let (pub_usage, internal_use) = match decl.ty {
wgt::BindingType::SampledTexture { .. } => {
wgt::BindingType::Texture { .. } => {
(wgt::TextureUsage::SAMPLED, resource::TextureUse::SAMPLED)
}
_ => {

View File

@ -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),
},
}
}

View File

@ -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<L> {
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<T> Default for RenderBundleDescriptor<Option<T>> {
}
}
/// 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<TextureFormat> 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<BufferSize>,
},
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<BufferSize>,
/// 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<TextureFormat> 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<BufferSize>,
},
/// 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<NonZeroU32>,
}