Skip to content

Commit 86c41a1

Browse files
committed
Add ExternalTexture BindingType behind new Feature flag
Adds a new feature flag, `EXTERNAL_TEXTURE`, indicating device support for our implementation of WebGPU's `GPUExternalTexture` [1] which will land in upcoming patches. Conceptually this would make more sense as a downlevel flag, as it is a core part of the WebGPU spec which we do not yet support. We do not want, however, to cause applications to reject adapters because we have not finished implementing this, so for now we are making it an opt-in feature. As an initial step towards supporting this feature, this patch adds a new `BindingType` corresponding to WebGPU's `GPUExternalTextureBindingLayout` [2]. This binding type dictates that when creating a bind group the corresponding entry must be either an external texture or a texture view with certain additional requirements [3]. As of yet wgpu has no concept of an external texture (that will follow in later patches) but for now this patch ensures that texture views corresponding to an external texture binding type are validated correctly. Note that as the feature flag is not yet supported on any real backends, bind group layout creation will fail before getting the chance to attempt to create a bind group. But in the added tests using the noop backend we can see this validation taking place. [1] https://www.w3.org/TR/webgpu/#gpuexternaltexture [1] https://www.w3.org/TR/webgpu/#dictdef-gpuexternaltexturebindinglayout [2] https://gpuweb.github.io/gpuweb/#bind-group-creation
1 parent 8d3ade9 commit 86c41a1

File tree

14 files changed

+292
-19
lines changed

14 files changed

+292
-19
lines changed
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
use wgpu::*;
2+
use wgpu_test::{fail, valid};
3+
4+
/// Ensures a [`TextureView`] can be bound to a [`BindingType::ExternalTexture`]
5+
/// resource binding.
6+
#[test]
7+
fn external_texture_binding_texture_view() {
8+
let (device, _queue) = wgpu::Device::noop(&DeviceDescriptor {
9+
required_features: Features::EXTERNAL_TEXTURE,
10+
..Default::default()
11+
});
12+
13+
let bgl = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
14+
label: None,
15+
entries: &[BindGroupLayoutEntry {
16+
binding: 0,
17+
visibility: ShaderStages::FRAGMENT,
18+
ty: BindingType::ExternalTexture,
19+
count: None,
20+
}],
21+
});
22+
23+
let texture_descriptor = TextureDescriptor {
24+
label: None,
25+
size: Extent3d {
26+
width: 256,
27+
height: 256,
28+
depth_or_array_layers: 1,
29+
},
30+
mip_level_count: 1,
31+
sample_count: 1,
32+
dimension: TextureDimension::D2,
33+
format: TextureFormat::Rgba8Unorm,
34+
usage: TextureUsages::TEXTURE_BINDING,
35+
view_formats: &[],
36+
};
37+
38+
let texture = device.create_texture(&texture_descriptor);
39+
let view = texture.create_view(&TextureViewDescriptor::default());
40+
valid(&device, || {
41+
device.create_bind_group(&BindGroupDescriptor {
42+
label: None,
43+
layout: &bgl,
44+
entries: &[BindGroupEntry {
45+
binding: 0,
46+
resource: BindingResource::TextureView(&view),
47+
}],
48+
})
49+
});
50+
51+
// Invalid usages (must include TEXTURE_BINDING)
52+
let texture = device.create_texture(&TextureDescriptor {
53+
usage: TextureUsages::STORAGE_BINDING,
54+
..texture_descriptor
55+
});
56+
let view = texture.create_view(&TextureViewDescriptor::default());
57+
fail(
58+
&device,
59+
|| {
60+
device.create_bind_group(&BindGroupDescriptor {
61+
label: None,
62+
layout: &bgl,
63+
entries: &[BindGroupEntry {
64+
binding: 0,
65+
resource: BindingResource::TextureView(&view),
66+
}],
67+
})
68+
},
69+
Some("Usage flags TextureUsages(STORAGE_BINDING) of TextureView with '' label do not contain required usage flags TextureUsages(TEXTURE_BINDING"),
70+
);
71+
72+
// Invalid dimension (must be D2)
73+
let texture = device.create_texture(&TextureDescriptor {
74+
dimension: TextureDimension::D3,
75+
..texture_descriptor
76+
});
77+
let view = texture.create_view(&TextureViewDescriptor::default());
78+
fail(
79+
&device,
80+
|| {
81+
device.create_bind_group(&BindGroupDescriptor {
82+
label: None,
83+
layout: &bgl,
84+
entries: &[BindGroupEntry {
85+
binding: 0,
86+
resource: BindingResource::TextureView(&view),
87+
}],
88+
})
89+
},
90+
Some("Texture binding 0 expects dimension = D2, but given a view with dimension = D3"),
91+
);
92+
93+
// Invalid mip_level_count (must be 1)
94+
let texture = device.create_texture(&TextureDescriptor {
95+
mip_level_count: 2,
96+
..texture_descriptor
97+
});
98+
let view = texture.create_view(&TextureViewDescriptor::default());
99+
fail(
100+
&device,
101+
|| {
102+
103+
device.create_bind_group(&BindGroupDescriptor {
104+
label: None,
105+
layout: &bgl,
106+
entries: &[
107+
BindGroupEntry {
108+
binding: 0,
109+
resource: BindingResource::TextureView(&view),
110+
},
111+
],
112+
})
113+
},
114+
Some("External texture bindings must have a single mip level, but given a view with mip_level_count = 2 at binding 0")
115+
);
116+
117+
// Invalid format (must be Rgba8Unorm, Bgra8Unorm, or Rgba16float)
118+
let texture = device.create_texture(&TextureDescriptor {
119+
format: TextureFormat::Rgba8Uint,
120+
..texture_descriptor
121+
});
122+
let view = texture.create_view(&TextureViewDescriptor::default());
123+
fail(
124+
&device,
125+
|| {
126+
127+
device.create_bind_group(&BindGroupDescriptor {
128+
label: None,
129+
layout: &bgl,
130+
entries: &[
131+
BindGroupEntry {
132+
binding: 0,
133+
resource: BindingResource::TextureView(&view),
134+
},
135+
],
136+
})
137+
},
138+
Some("External texture bindings must have a format of `rgba8unorm`, `bgra8unorm`, or `rgba16float, but given a view with format = Rgba8Uint at binding 0")
139+
);
140+
141+
// Invalid sample count (must be 1)
142+
let texture = device.create_texture(&TextureDescriptor {
143+
sample_count: 4,
144+
usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING,
145+
..texture_descriptor
146+
});
147+
let view = texture.create_view(&TextureViewDescriptor::default());
148+
fail(
149+
&device,
150+
|| {
151+
device.create_bind_group(&BindGroupDescriptor {
152+
label: None,
153+
layout: &bgl,
154+
entries: &[BindGroupEntry {
155+
binding: 0,
156+
resource: BindingResource::TextureView(&view),
157+
}],
158+
})
159+
},
160+
Some("Texture binding 0 expects multisampled = false, but given a view with samples = 4"),
161+
);
162+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod binding_arrays;
22
mod buffer;
33
mod buffer_slice;
4+
mod external_texture;
45
mod instance;
56
mod texture;

wgpu-core/src/binding_model.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,13 @@ pub enum CreateBindGroupError {
173173
},
174174
#[error("Storage texture bindings must have a single mip level, but given a view with mip_level_count = {mip_level_count:?} at binding {binding}")]
175175
InvalidStorageTextureMipLevelCount { binding: u32, mip_level_count: u32 },
176+
#[error("External texture bindings must have a single mip level, but given a view with mip_level_count = {mip_level_count:?} at binding {binding}")]
177+
InvalidExternalTextureMipLevelCount { binding: u32, mip_level_count: u32 },
178+
#[error("External texture bindings must have a format of `rgba8unorm`, `bgra8unorm`, or `rgba16float, but given a view with format = {format:?} at binding {binding}")]
179+
InvalidExternalTextureFormat {
180+
binding: u32,
181+
format: wgt::TextureFormat,
182+
},
176183
#[error("Sampler binding {binding} expects comparison = {layout_cmp}, but given a sampler with comparison = {sampler_cmp}")]
177184
WrongSamplerComparison {
178185
binding: u32,
@@ -382,6 +389,19 @@ impl BindingTypeMaxCountValidator {
382389
wgt::BindingType::AccelerationStructure { .. } => {
383390
self.acceleration_structures.add(binding.visibility, count);
384391
}
392+
wgt::BindingType::ExternalTexture => {
393+
// https://www.w3.org/TR/webgpu/#gpuexternaltexture
394+
// In order to account for many possible representations,
395+
// the binding conservatively uses the following, for each
396+
// external texture:
397+
// * Three sampled textures for up to 3 planes
398+
// * One additional sampled texture for a 3D LUT
399+
// * One sampler to sample the LUT
400+
// * One uniform buffer for metadata
401+
self.sampled_textures.add(binding.visibility, count * 4);
402+
self.samplers.add(binding.visibility, count);
403+
self.uniform_buffers.add(binding.visibility, count);
404+
}
385405
}
386406
}
387407
}

wgpu-core/src/device/resource.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2010,6 +2010,14 @@ impl Device {
20102010
)
20112011
}
20122012
Bt::AccelerationStructure { .. } => (None, WritableStorage::No),
2013+
Bt::ExternalTexture => {
2014+
self.require_features(wgt::Features::EXTERNAL_TEXTURE)
2015+
.map_err(|e| binding_model::CreateBindGroupLayoutError::Entry {
2016+
binding: entry.binding,
2017+
error: e.into(),
2018+
})?;
2019+
(None, WritableStorage::No)
2020+
}
20132021
};
20142022

20152023
// Validate the count parameter
@@ -2760,6 +2768,41 @@ impl Device {
27602768
view.check_usage(wgt::TextureUsages::STORAGE_BINDING)?;
27612769
Ok(internal_use)
27622770
}
2771+
wgt::BindingType::ExternalTexture => {
2772+
if view.desc.dimension != TextureViewDimension::D2 {
2773+
return Err(Error::InvalidTextureDimension {
2774+
binding,
2775+
layout_dimension: TextureViewDimension::D2,
2776+
view_dimension: view.desc.dimension,
2777+
});
2778+
}
2779+
let mip_level_count = view.selector.mips.end - view.selector.mips.start;
2780+
if mip_level_count != 1 {
2781+
return Err(Error::InvalidExternalTextureMipLevelCount {
2782+
binding,
2783+
mip_level_count,
2784+
});
2785+
}
2786+
if view.desc.format != TextureFormat::Rgba8Unorm
2787+
&& view.desc.format != TextureFormat::Bgra8Unorm
2788+
&& view.desc.format != TextureFormat::Rgba16Float
2789+
{
2790+
return Err(Error::InvalidExternalTextureFormat {
2791+
binding,
2792+
format: view.desc.format,
2793+
});
2794+
}
2795+
if view.samples != 1 {
2796+
return Err(Error::InvalidTextureMultisample {
2797+
binding,
2798+
layout_multisampled: false,
2799+
view_samples: view.samples,
2800+
});
2801+
}
2802+
2803+
view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
2804+
Ok(wgt::TextureUses::RESOURCE)
2805+
}
27632806
_ => Err(Error::WrongBindingType {
27642807
binding,
27652808
actual: decl.ty,

wgpu-core/src/validation.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub enum BindingTypeName {
3636
Texture,
3737
Sampler,
3838
AccelerationStructure,
39+
ExternalTexture,
3940
}
4041

4142
impl From<&ResourceType> for BindingTypeName {
@@ -57,6 +58,7 @@ impl From<&BindingType> for BindingTypeName {
5758
BindingType::StorageTexture { .. } => BindingTypeName::Texture,
5859
BindingType::Sampler { .. } => BindingTypeName::Sampler,
5960
BindingType::AccelerationStructure { .. } => BindingTypeName::AccelerationStructure,
61+
BindingType::ExternalTexture => BindingTypeName::ExternalTexture,
6062
}
6163
}
6264
}
@@ -466,6 +468,7 @@ impl Resource {
466468
let view_dimension = match entry.ty {
467469
BindingType::Texture { view_dimension, .. }
468470
| BindingType::StorageTexture { view_dimension, .. } => view_dimension,
471+
BindingType::ExternalTexture => wgt::TextureViewDimension::D2,
469472
_ => {
470473
return Err(BindingError::WrongTextureViewDimension {
471474
dim,
@@ -1147,6 +1150,9 @@ impl Interface {
11471150
);
11481151
let texture_sample_type = match texture_layout.ty {
11491152
BindingType::Texture { sample_type, .. } => sample_type,
1153+
BindingType::ExternalTexture => {
1154+
wgt::TextureSampleType::Float { filterable: true }
1155+
}
11501156
_ => unreachable!(),
11511157
};
11521158

wgpu-hal/src/dx12/conv.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ pub fn map_binding_type(ty: &wgt::BindingType) -> Direct3D12::D3D12_DESCRIPTOR_R
135135
}
136136
| Bt::StorageTexture { .. } => Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_UAV,
137137
Bt::AccelerationStructure { .. } => Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
138+
Bt::ExternalTexture => unimplemented!(),
138139
}
139140
}
140141

wgpu-hal/src/dx12/device.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,7 @@ impl crate::Device for super::Device {
761761
| wgt::BindingType::StorageTexture { .. }
762762
| wgt::BindingType::AccelerationStructure { .. } => num_views += count,
763763
wgt::BindingType::Sampler { .. } => has_sampler_in_group = true,
764+
wgt::BindingType::ExternalTexture => unimplemented!(),
764765
}
765766
}
766767

@@ -1526,6 +1527,7 @@ impl crate::Device for super::Device {
15261527
inner.stage.push(handle);
15271528
}
15281529
}
1530+
wgt::BindingType::ExternalTexture => unimplemented!(),
15291531
}
15301532
}
15311533

wgpu-hal/src/gles/device.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,6 +1192,7 @@ impl crate::Device for super::Device {
11921192
..
11931193
} => &mut num_storage_buffers,
11941194
wgt::BindingType::AccelerationStructure { .. } => unimplemented!(),
1195+
wgt::BindingType::ExternalTexture => unimplemented!(),
11951196
};
11961197

11971198
binding_to_slot[entry.binding as usize] = *counter;
@@ -1302,6 +1303,7 @@ impl crate::Device for super::Device {
13021303
})
13031304
}
13041305
wgt::BindingType::AccelerationStructure { .. } => unimplemented!(),
1306+
wgt::BindingType::ExternalTexture => unimplemented!(),
13051307
};
13061308
contents.push(binding);
13071309
}

wgpu-hal/src/metal/device.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,7 @@ impl crate::Device for super::Device {
746746
};
747747
}
748748
wgt::BindingType::AccelerationStructure { .. } => unimplemented!(),
749+
wgt::BindingType::ExternalTexture => unimplemented!(),
749750
}
750751
}
751752

@@ -978,6 +979,7 @@ impl crate::Device for super::Device {
978979
counter.textures += 1;
979980
}
980981
wgt::BindingType::AccelerationStructure { .. } => unimplemented!(),
982+
wgt::BindingType::ExternalTexture => unimplemented!(),
981983
}
982984
}
983985
}

wgpu-hal/src/vulkan/conv.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,7 @@ pub fn map_binding_type(ty: wgt::BindingType) -> vk::DescriptorType {
764764
wgt::BindingType::AccelerationStructure { .. } => {
765765
vk::DescriptorType::ACCELERATION_STRUCTURE_KHR
766766
}
767+
wgt::BindingType::ExternalTexture => unimplemented!(),
767768
}
768769
}
769770

wgpu-hal/src/vulkan/device.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1482,6 +1482,7 @@ impl crate::Device for super::Device {
14821482
wgt::BindingType::AccelerationStructure { .. } => {
14831483
desc_count.acceleration_structure += count;
14841484
}
1485+
wgt::BindingType::ExternalTexture => unimplemented!(),
14851486
}
14861487
}
14871488

0 commit comments

Comments
 (0)