Skip to content

Commit 4781d9d

Browse files
Merge #1607
1607: Fix Downlevel Vertex Stage Storage Buffer Check r=kvark a=cwfitzgerald **Connections** Fixes a bug in #1599. Also follows up on #1583 marking all of hello-compute as a failure. **Testing** Tests now pass on rpi4. Co-authored-by: Connor Fitzgerald <[email protected]>
2 parents d2a27e0 + 90ef814 commit 4781d9d

File tree

6 files changed

+77
-60
lines changed

6 files changed

+77
-60
lines changed

wgpu-core/src/device/mod.rs

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -976,22 +976,33 @@ impl<A: HalApi> Device<A> {
976976
label: Option<&str>,
977977
entry_map: binding_model::BindEntryMap,
978978
) -> Result<binding_model::BindGroupLayout<A>, binding_model::CreateBindGroupLayoutError> {
979+
#[derive(PartialEq)]
980+
enum WritableStorage {
981+
Yes,
982+
No,
983+
}
984+
979985
for entry in entry_map.values() {
980986
use wgt::BindingType as Bt;
981987

982988
let mut required_features = wgt::Features::empty();
983-
let mut required_downlevel_flags = wgt::DownlevelFlags::empty();
984-
let (array_feature, is_writable_storage) = match entry.ty {
989+
let (array_feature, writable_storage) = match entry.ty {
985990
Bt::Buffer {
986991
ty: wgt::BufferBindingType::Uniform,
987992
has_dynamic_offset: false,
988993
min_binding_size: _,
989-
} => (Some(wgt::Features::BUFFER_BINDING_ARRAY), false),
994+
} => (
995+
Some(wgt::Features::BUFFER_BINDING_ARRAY),
996+
WritableStorage::No,
997+
),
990998
Bt::Buffer {
991999
ty: wgt::BufferBindingType::Uniform,
9921000
has_dynamic_offset: true,
9931001
min_binding_size: _,
994-
} => (Some(wgt::Features::BUFFER_BINDING_ARRAY), false),
1002+
} => (
1003+
Some(wgt::Features::BUFFER_BINDING_ARRAY),
1004+
WritableStorage::No,
1005+
),
9951006
Bt::Buffer {
9961007
ty: wgt::BufferBindingType::Storage { read_only },
9971008
..
@@ -1000,22 +1011,28 @@ impl<A: HalApi> Device<A> {
10001011
wgt::Features::BUFFER_BINDING_ARRAY
10011012
| wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
10021013
),
1003-
!read_only,
1014+
match read_only {
1015+
true => WritableStorage::No,
1016+
false => WritableStorage::Yes,
1017+
},
1018+
),
1019+
Bt::Sampler { .. } => (None, WritableStorage::No),
1020+
Bt::Texture { .. } => (
1021+
Some(wgt::Features::TEXTURE_BINDING_ARRAY),
1022+
WritableStorage::No,
10041023
),
1005-
Bt::Sampler { .. } => (None, false),
1006-
Bt::Texture { .. } => (Some(wgt::Features::TEXTURE_BINDING_ARRAY), false),
10071024
Bt::StorageTexture { access, .. } => (
10081025
Some(
10091026
wgt::Features::TEXTURE_BINDING_ARRAY
10101027
| wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
10111028
),
10121029
match access {
1013-
wgt::StorageTextureAccess::ReadOnly => false,
1014-
wgt::StorageTextureAccess::WriteOnly => true,
1030+
wgt::StorageTextureAccess::ReadOnly => WritableStorage::No,
1031+
wgt::StorageTextureAccess::WriteOnly => WritableStorage::Yes,
10151032
wgt::StorageTextureAccess::ReadWrite => {
10161033
required_features |=
10171034
wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
1018-
true
1035+
WritableStorage::Yes
10191036
}
10201037
},
10211038
),
@@ -1030,13 +1047,14 @@ impl<A: HalApi> Device<A> {
10301047
error,
10311048
})?;
10321049
}
1033-
if entry.visibility.contains(wgt::ShaderStages::VERTEX) {
1034-
required_downlevel_flags |= wgt::DownlevelFlags::VERTEX_ACCESSABLE_STORAGE_BUFFERS;
1035-
}
1036-
if is_writable_storage && entry.visibility.contains(wgt::ShaderStages::VERTEX) {
1050+
if writable_storage == WritableStorage::Yes
1051+
&& entry.visibility.contains(wgt::ShaderStages::VERTEX)
1052+
{
10371053
required_features |= wgt::Features::VERTEX_WRITABLE_STORAGE;
10381054
}
1039-
if is_writable_storage && entry.visibility.contains(wgt::ShaderStages::FRAGMENT) {
1055+
if writable_storage == WritableStorage::Yes
1056+
&& entry.visibility.contains(wgt::ShaderStages::FRAGMENT)
1057+
{
10401058
self.require_downlevel_flags(wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE)
10411059
.map_err(binding_model::BindGroupLayoutEntryError::MissingDownlevelFlags)
10421060
.map_err(|error| binding_model::CreateBindGroupLayoutError::Entry {
@@ -1051,13 +1069,6 @@ impl<A: HalApi> Device<A> {
10511069
binding: entry.binding,
10521070
error,
10531071
})?;
1054-
1055-
self.require_downlevel_flags(required_downlevel_flags)
1056-
.map_err(binding_model::BindGroupLayoutEntryError::MissingDownlevelFlags)
1057-
.map_err(|error| binding_model::CreateBindGroupLayoutError::Entry {
1058-
binding: entry.binding,
1059-
error,
1060-
})?;
10611072
}
10621073

10631074
let mut hal_bindings = entry_map.values().cloned().collect::<Vec<_>>();

wgpu-hal/src/gles/adapter.rs

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -180,17 +180,39 @@ impl super::Adapter {
180180
naga::back::glsl::Version::Embedded(value)
181181
};
182182

183+
let vertex_shader_storage_blocks =
184+
gl.get_parameter_i32(glow::MAX_VERTEX_SHADER_STORAGE_BLOCKS) as u32;
185+
let fragment_shader_storage_blocks =
186+
gl.get_parameter_i32(glow::MAX_FRAGMENT_SHADER_STORAGE_BLOCKS) as u32;
187+
188+
let vertex_shader_storage_textures =
189+
gl.get_parameter_i32(glow::MAX_VERTEX_IMAGE_UNIFORMS) as u32;
190+
let fragment_shader_storage_textures =
191+
gl.get_parameter_i32(glow::MAX_FRAGMENT_IMAGE_UNIFORMS) as u32;
192+
193+
// WORKAROUND:
194+
// In order to work around an issue with GL on RPI4 and similar, we ignore a zero vertex ssbo count if there are vertex sstos. (more info: https://github.com/gfx-rs/wgpu/pull/1607#issuecomment-874938961)
195+
// The hardware does not want us to write to these SSBOs, but GLES cannot express that. We detect this case and disable writing to SSBOs.
196+
let vertex_ssbo_false_zero =
197+
vertex_shader_storage_blocks == 0 && vertex_shader_storage_textures != 0;
198+
199+
let max_storage_buffers_per_shader_stage = if vertex_ssbo_false_zero {
200+
// We only care about fragment here as the 0 is a lie.
201+
log::warn!("Max vertex shader SSBO == 0 and SSTO != 0. Interpreting as false zero.");
202+
fragment_shader_storage_blocks
203+
} else {
204+
vertex_shader_storage_blocks.min(fragment_shader_storage_blocks)
205+
};
206+
183207
let mut features = wgt::Features::empty() | wgt::Features::TEXTURE_COMPRESSION_ETC2;
184208
features.set(
185209
wgt::Features::DEPTH_CLAMPING,
186210
extensions.contains("GL_EXT_depth_clamp"),
187211
);
188-
features.set(wgt::Features::VERTEX_WRITABLE_STORAGE, ver >= (3, 1));
189-
190-
let vertex_shader_storage_blocks =
191-
gl.get_parameter_i32(glow::MAX_VERTEX_SHADER_STORAGE_BLOCKS);
192-
let fragment_shader_storage_blocks =
193-
gl.get_parameter_i32(glow::MAX_FRAGMENT_SHADER_STORAGE_BLOCKS);
212+
features.set(
213+
wgt::Features::VERTEX_WRITABLE_STORAGE,
214+
ver >= (3, 1) && !vertex_ssbo_false_zero,
215+
);
194216

195217
let mut downlevel_flags = wgt::DownlevelFlags::empty()
196218
| wgt::DownlevelFlags::DEVICE_LOCAL_IMAGE_COPIES
@@ -210,10 +232,6 @@ impl super::Adapter {
210232
wgt::DownlevelFlags::INDEPENDENT_BLENDING,
211233
ver >= (3, 2) || extensions.contains("GL_EXT_draw_buffers_indexed"),
212234
);
213-
downlevel_flags.set(
214-
wgt::DownlevelFlags::VERTEX_ACCESSABLE_STORAGE_BUFFERS,
215-
vertex_shader_storage_blocks > 0,
216-
);
217235

218236
let max_texture_size = gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) as u32;
219237
let max_texture_3d_size = gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) as u32;
@@ -228,14 +246,6 @@ impl super::Adapter {
228246
let max_uniform_buffers_per_shader_stage =
229247
gl.get_parameter_i32(glow::MAX_VERTEX_UNIFORM_BLOCKS)
230248
.min(gl.get_parameter_i32(glow::MAX_FRAGMENT_UNIFORM_BLOCKS)) as u32;
231-
let max_storage_buffers_per_shader_stage = if vertex_shader_storage_blocks > 0 {
232-
vertex_shader_storage_blocks.min(fragment_shader_storage_blocks)
233-
} else {
234-
fragment_shader_storage_blocks
235-
} as u32;
236-
237-
let max_storage_textures_per_shader_stage =
238-
gl.get_parameter_i32(glow::MAX_FRAGMENT_IMAGE_UNIFORMS) as u32;
239249

240250
let limits = wgt::Limits {
241251
max_texture_dimension_1d: max_texture_size,
@@ -248,7 +258,8 @@ impl super::Adapter {
248258
max_sampled_textures_per_shader_stage: super::MAX_TEXTURE_SLOTS as u32,
249259
max_samplers_per_shader_stage: super::MAX_SAMPLERS as u32,
250260
max_storage_buffers_per_shader_stage,
251-
max_storage_textures_per_shader_stage,
261+
max_storage_textures_per_shader_stage: vertex_shader_storage_textures
262+
.min(fragment_shader_storage_textures),
252263
max_uniform_buffers_per_shader_stage,
253264
max_uniform_buffer_binding_size: gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
254265
as u32,

wgpu-types/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -702,8 +702,6 @@ bitflags::bitflags! {
702702
const COMPARISON_SAMPLERS = 0x0000_0100;
703703
/// Supports different blending modes per color target.
704704
const INDEPENDENT_BLENDING = 0x0000_0200;
705-
/// Supports attaching storage buffers to vertex shaders.
706-
const VERTEX_ACCESSABLE_STORAGE_BUFFERS = 0x0000_0400;
707705

708706

709707
/// Supports samplers with anisotropic filtering. Note this isn't actually required by WebGPU,

wgpu/examples/hello-compute/tests.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use common::{initialize_test, TestParameters};
99
#[test]
1010
fn test_compute_1() {
1111
initialize_test(
12-
TestParameters::default().specific_failure(None, None, Some("V3D"), false),
12+
TestParameters::default().specific_failure(None, None, Some("V3D"), true),
1313
|ctx| {
1414
let input = &[1, 2, 3, 4];
1515

@@ -26,7 +26,7 @@ fn test_compute_1() {
2626
#[test]
2727
fn test_compute_2() {
2828
initialize_test(
29-
TestParameters::default().specific_failure(None, None, Some("V3D"), false),
29+
TestParameters::default().specific_failure(None, None, Some("V3D"), true),
3030
|ctx| {
3131
let input = &[5, 23, 10, 9];
3232

@@ -43,7 +43,7 @@ fn test_compute_2() {
4343
#[test]
4444
fn test_compute_overflow() {
4545
initialize_test(
46-
TestParameters::default().specific_failure(None, None, Some("V3D"), false),
46+
TestParameters::default().specific_failure(None, None, Some("V3D"), true),
4747
|ctx| {
4848
let input = &[77031, 837799, 8400511, 63728127];
4949
pollster::block_on(assert_execute_gpu(
@@ -61,7 +61,7 @@ fn test_multithreaded_compute() {
6161
initialize_test(
6262
TestParameters::default()
6363
.backend_failure(wgpu::Backends::GL)
64-
.specific_failure(Some(wgpu::Backends::VULKAN), None, Some("V3D"), false),
64+
.specific_failure(None, None, Some("V3D"), true),
6565
|ctx| {
6666
use std::{sync::mpsc, thread, time::Duration};
6767

wgpu/examples/shadow/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ impl framework::Example for Example {
542542
},
543543
wgpu::BindGroupLayoutEntry {
544544
binding: 1, // lights
545-
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
545+
visibility: wgpu::ShaderStages::FRAGMENT,
546546
ty: wgpu::BindingType::Buffer {
547547
ty: wgpu::BufferBindingType::Storage { read_only: true },
548548
has_dynamic_offset: false,

wgpu/tests/common/mod.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ pub struct FailureCase {
7575
backends: Option<wgpu::Backends>,
7676
vendor: Option<usize>,
7777
adapter: Option<String>,
78-
segfault: bool,
78+
skip: bool,
7979
}
8080

8181
// This information determines if a test should run.
@@ -137,7 +137,7 @@ impl TestParameters {
137137
backends: None,
138138
vendor: None,
139139
adapter: None,
140-
segfault: false,
140+
skip: false,
141141
});
142142
self
143143
}
@@ -148,7 +148,7 @@ impl TestParameters {
148148
backends: Some(backends),
149149
vendor: None,
150150
adapter: None,
151-
segfault: false,
151+
skip: false,
152152
});
153153
self
154154
}
@@ -165,13 +165,13 @@ impl TestParameters {
165165
backends: Option<Backends>,
166166
vendor: Option<usize>,
167167
device: Option<&'static str>,
168-
segfault: bool,
168+
skip: bool,
169169
) -> Self {
170170
self.failures.push(FailureCase {
171171
backends,
172172
vendor,
173173
adapter: device.as_ref().map(AsRef::as_ref).map(str::to_lowercase),
174-
segfault,
174+
skip,
175175
});
176176
self
177177
}
@@ -257,7 +257,7 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te
257257
&& expect_failure_adapter.unwrap_or(true)
258258
{
259259
if always {
260-
Some((FailureReasons::ALWAYS, failure.segfault))
260+
Some((FailureReasons::ALWAYS, failure.skip))
261261
} else {
262262
let mut reason = FailureReasons::empty();
263263
reason.set(
@@ -272,18 +272,15 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te
272272
FailureReasons::ADAPTER,
273273
expect_failure_adapter.unwrap_or(false),
274274
);
275-
Some((reason, failure.segfault))
275+
Some((reason, failure.skip))
276276
}
277277
} else {
278278
None
279279
}
280280
});
281281

282282
if let Some((reason, true)) = failure_reason {
283-
println!(
284-
"EXPECTED TEST FAILURE SKIPPED DUE TO SEGFAULT: {:?}",
285-
reason
286-
);
283+
println!("EXPECTED TEST FAILURE SKIPPED: {:?}", reason);
287284
return;
288285
}
289286

@@ -297,7 +294,7 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te
297294
// Print out reason for the failure
298295
println!("GOT EXPECTED TEST FAILURE: {:?}", reason);
299296
}
300-
} else if let Some(reason) = failure_reason {
297+
} else if let Some((reason, _)) = failure_reason {
301298
// We expected to fail, but things passed
302299
panic!("UNEXPECTED TEST PASS: {:?}", reason);
303300
} else {

0 commit comments

Comments
 (0)