Skip to content

Commit 5a37c32

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 5a37c32

File tree

14 files changed

+350
-19
lines changed

14 files changed

+350
-19
lines changed
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
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 = valid(&device, || {
14+
device.create_bind_group_layout(&BindGroupLayoutDescriptor {
15+
label: None,
16+
entries: &[
17+
BindGroupLayoutEntry {
18+
binding: 0,
19+
visibility: ShaderStages::FRAGMENT,
20+
ty: BindingType::ExternalTexture,
21+
count: None,
22+
},
23+
BindGroupLayoutEntry {
24+
binding: 1,
25+
visibility: ShaderStages::FRAGMENT,
26+
ty: BindingType::Sampler(SamplerBindingType::Filtering),
27+
count: None,
28+
},
29+
],
30+
})
31+
});
32+
33+
let sampler = device.create_sampler(&SamplerDescriptor::default());
34+
35+
let texture_descriptor = TextureDescriptor {
36+
label: None,
37+
size: Extent3d {
38+
width: 256,
39+
height: 256,
40+
depth_or_array_layers: 1,
41+
},
42+
mip_level_count: 1,
43+
sample_count: 1,
44+
dimension: TextureDimension::D2,
45+
format: TextureFormat::Rgba8Unorm,
46+
usage: TextureUsages::TEXTURE_BINDING,
47+
view_formats: &[],
48+
};
49+
let view_descriptor = TextureViewDescriptor {
50+
label: None,
51+
format: None,
52+
dimension: None,
53+
usage: None,
54+
aspect: TextureAspect::All,
55+
base_mip_level: 0,
56+
mip_level_count: None,
57+
base_array_layer: 0,
58+
array_layer_count: None,
59+
};
60+
61+
valid(&device, || {
62+
let texture = device.create_texture(&texture_descriptor);
63+
let view = texture.create_view(&view_descriptor);
64+
65+
device.create_bind_group(&BindGroupDescriptor {
66+
label: None,
67+
layout: &bgl,
68+
entries: &[
69+
BindGroupEntry {
70+
binding: 0,
71+
resource: BindingResource::TextureView(&view),
72+
},
73+
BindGroupEntry {
74+
binding: 1,
75+
resource: BindingResource::Sampler(&sampler),
76+
},
77+
],
78+
})
79+
});
80+
81+
// Invalid usages (must include RESOURCE_BINDING)
82+
fail(
83+
&device,
84+
|| {
85+
let texture = device.create_texture(&TextureDescriptor {
86+
usage: TextureUsages::empty(),
87+
..texture_descriptor
88+
});
89+
let view = texture.create_view(&view_descriptor);
90+
91+
device.create_bind_group(&BindGroupDescriptor {
92+
label: None,
93+
layout: &bgl,
94+
entries: &[
95+
BindGroupEntry {
96+
binding: 0,
97+
resource: BindingResource::TextureView(&view),
98+
},
99+
BindGroupEntry {
100+
binding: 1,
101+
resource: BindingResource::Sampler(&sampler),
102+
},
103+
],
104+
})
105+
},
106+
Some("Invalid usage flags TextureUsages(0x0)"),
107+
);
108+
109+
// Invalid dimension (must be D2)
110+
fail(
111+
&device,
112+
|| {
113+
let texture = device.create_texture(&TextureDescriptor {
114+
dimension: TextureDimension::D3,
115+
..texture_descriptor
116+
});
117+
let view = texture.create_view(&view_descriptor);
118+
119+
device.create_bind_group(&BindGroupDescriptor {
120+
label: None,
121+
layout: &bgl,
122+
entries: &[
123+
BindGroupEntry {
124+
binding: 0,
125+
resource: BindingResource::TextureView(&view),
126+
},
127+
BindGroupEntry {
128+
binding: 1,
129+
resource: BindingResource::Sampler(&sampler),
130+
},
131+
],
132+
})
133+
},
134+
Some("Texture binding 0 expects dimension = D2, but given a view with dimension = D3"),
135+
);
136+
137+
// Invalid mip_level_count (must be 1)
138+
fail(
139+
&device,
140+
|| {
141+
let texture = device.create_texture(&TextureDescriptor {
142+
mip_level_count: 2,
143+
..texture_descriptor
144+
});
145+
let view = texture.create_view(&view_descriptor);
146+
147+
device.create_bind_group(&BindGroupDescriptor {
148+
label: None,
149+
layout: &bgl,
150+
entries: &[
151+
BindGroupEntry {
152+
binding: 0,
153+
resource: BindingResource::TextureView(&view),
154+
},
155+
BindGroupEntry {
156+
binding: 1,
157+
resource: BindingResource::Sampler(&sampler),
158+
},
159+
],
160+
})
161+
},
162+
Some("External texture bindings must have a single mip level, but given a view with mip_level_count = 2 at binding 0")
163+
);
164+
165+
// Invalid format (must be Rgba8Unorm, Bgra8Unorm, or Rgba16float)
166+
fail(
167+
&device,
168+
|| {
169+
let texture = device.create_texture(&TextureDescriptor {
170+
format: TextureFormat::Rgba8Uint,
171+
..texture_descriptor
172+
});
173+
let view = texture.create_view(&view_descriptor);
174+
175+
device.create_bind_group(&BindGroupDescriptor {
176+
label: None,
177+
layout: &bgl,
178+
entries: &[
179+
BindGroupEntry {
180+
binding: 0,
181+
resource: BindingResource::TextureView(&view),
182+
},
183+
BindGroupEntry {
184+
binding: 1,
185+
resource: BindingResource::Sampler(&sampler),
186+
},
187+
],
188+
})
189+
},
190+
Some("External texture bindings must have a format of `rgba8unorm`, `bgra8unorm`, or `rgba16float, but given a view with format = Rgba8Uint at binding 0")
191+
);
192+
193+
// Invalid sample count (must be 1)
194+
fail(
195+
&device,
196+
|| {
197+
let texture = device.create_texture(&TextureDescriptor {
198+
sample_count: 4,
199+
usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING,
200+
..texture_descriptor
201+
});
202+
let view = texture.create_view(&view_descriptor);
203+
204+
device.create_bind_group(&BindGroupDescriptor {
205+
label: None,
206+
layout: &bgl,
207+
entries: &[
208+
BindGroupEntry {
209+
binding: 0,
210+
resource: BindingResource::TextureView(&view),
211+
},
212+
BindGroupEntry {
213+
binding: 1,
214+
resource: BindingResource::Sampler(&sampler),
215+
},
216+
],
217+
})
218+
},
219+
Some("Texture binding 0 expects multisampled = false, but given a view with samples = 4"),
220+
);
221+
}
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: 1 addition & 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

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
}

0 commit comments

Comments
 (0)