|
1 | 1 | use alloc::{borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec, vec::Vec}; |
| 2 | +use core::fmt; |
2 | 3 |
|
3 | 4 | use hashbrown::HashMap; |
4 | 5 | use thiserror::Error; |
@@ -414,64 +415,13 @@ impl Instance { |
414 | 415 | }) |
415 | 416 | } |
416 | 417 |
|
417 | | - /// This function checks that the adapter obeys WebGPU's adapter capability |
418 | | - /// guarantees. Most of the limits are adjusted in wgpu-hal's |
419 | | - /// `adjust_raw_limits` fn. So we only check the remaining properties here. |
420 | | - /// See <https://gpuweb.github.io/gpuweb/#adapter-capability-guarantees>. |
421 | 418 | fn adapter_allowed(&self, raw: &hal::DynExposedAdapter) -> bool { |
422 | | - // Check "All alignment-class limits must be powers of 2." |
423 | | - // |
424 | | - // Even if the application has not requested strict WebGPU compliance, |
425 | | - // non-power-of-two alignment limits are nonsensical, so don't attempt |
426 | | - // to use such a device. |
427 | | - let min_uniform_buffer_offset_alignment = |
428 | | - raw.capabilities.limits.min_uniform_buffer_offset_alignment; |
429 | | - if !min_uniform_buffer_offset_alignment.is_power_of_two() { |
430 | | - log::error!( |
431 | | - "Adapter {:?} min_uniform_buffer_offset_alignment limit is not a power of 2: {:?}", |
432 | | - raw.info, |
433 | | - min_uniform_buffer_offset_alignment |
434 | | - ); |
435 | | - return false; |
436 | | - } |
437 | | - let min_storage_buffer_offset_alignment = |
438 | | - raw.capabilities.limits.min_storage_buffer_offset_alignment; |
439 | | - if !min_storage_buffer_offset_alignment.is_power_of_two() { |
440 | | - log::error!( |
441 | | - "Adapter {:?} min_storage_buffer_offset_alignment limit is not a power of 2: {:?}", |
442 | | - raw.info, |
443 | | - min_storage_buffer_offset_alignment |
444 | | - ); |
445 | | - return false; |
446 | | - } |
447 | | - |
448 | | - // Following checks are only enabled if `STRICT_WEBGPU_COMPLIANCE` is set. |
449 | | - if !self.flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) { |
450 | | - return true; |
451 | | - } |
452 | | - |
453 | | - // Check "All supported limits must be either the default value or better." |
454 | | - let failed_limits = check_limits(&wgt::Limits::defaults(), &raw.capabilities.limits); |
455 | | - if !failed_limits.is_empty() { |
456 | | - log::debug!( |
457 | | - "Adapter {:?} is not WebGPU compliant due to limits: {:?}", |
458 | | - raw.info, |
459 | | - failed_limits |
460 | | - ); |
461 | | - return false; |
462 | | - } |
463 | | - |
464 | | - if !raw.capabilities.downlevel.is_webgpu_compliant() { |
465 | | - let missing_flags = wgt::DownlevelFlags::compliant() - raw.capabilities.downlevel.flags; |
466 | | - log::debug!( |
467 | | - "Adapter {:?} is not WebGPU compliant due to missing downlevel flags: {:?}", |
468 | | - raw.info, |
469 | | - missing_flags |
470 | | - ); |
471 | | - return false; |
472 | | - } |
473 | | - |
474 | | - true |
| 419 | + adapter_allowed( |
| 420 | + self.flags, |
| 421 | + &raw.info, |
| 422 | + &raw.capabilities.limits, |
| 423 | + &raw.capabilities.downlevel, |
| 424 | + ) |
475 | 425 | } |
476 | 426 |
|
477 | 427 | pub fn enumerate_adapters( |
@@ -1315,3 +1265,170 @@ impl Global { |
1315 | 1265 | Ok((device_id, queue_id)) |
1316 | 1266 | } |
1317 | 1267 | } |
| 1268 | + |
| 1269 | +/// This function checks that the adapter obeys WebGPU's adapter capability |
| 1270 | +/// guarantees. Most of the limits are adjusted in wgpu-hal's |
| 1271 | +/// `adjust_raw_limits` fn. So we only check the remaining properties here. |
| 1272 | +/// See <https://gpuweb.github.io/gpuweb/#adapter-capability-guarantees>. |
| 1273 | +fn adapter_allowed( |
| 1274 | + flags: InstanceFlags, |
| 1275 | + info: &impl fmt::Debug, |
| 1276 | + limits: &wgt::Limits, |
| 1277 | + downlevel: &wgt::DownlevelCapabilities, |
| 1278 | +) -> bool { |
| 1279 | + // Check "All alignment-class limits must be powers of 2." |
| 1280 | + // |
| 1281 | + // Even if the application has not requested strict WebGPU compliance, |
| 1282 | + // non-power-of-two alignment limits are nonsensical, so don't attempt |
| 1283 | + // to use such a device. |
| 1284 | + let min_uniform_buffer_offset_alignment = limits.min_uniform_buffer_offset_alignment; |
| 1285 | + if !min_uniform_buffer_offset_alignment.is_power_of_two() { |
| 1286 | + log::error!( |
| 1287 | + "Adapter {:?} min_uniform_buffer_offset_alignment limit is not a power of 2: {:?}", |
| 1288 | + info, |
| 1289 | + min_uniform_buffer_offset_alignment |
| 1290 | + ); |
| 1291 | + return false; |
| 1292 | + } |
| 1293 | + let min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment; |
| 1294 | + if !min_storage_buffer_offset_alignment.is_power_of_two() { |
| 1295 | + log::error!( |
| 1296 | + "Adapter {:?} min_storage_buffer_offset_alignment limit is not a power of 2: {:?}", |
| 1297 | + info, |
| 1298 | + min_storage_buffer_offset_alignment |
| 1299 | + ); |
| 1300 | + return false; |
| 1301 | + } |
| 1302 | + |
| 1303 | + // Following checks are only enabled if `STRICT_WEBGPU_COMPLIANCE` is set. |
| 1304 | + if !flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) { |
| 1305 | + return true; |
| 1306 | + } |
| 1307 | + |
| 1308 | + // Check "All supported limits must be either the default value or better." |
| 1309 | + let failed_limits = check_limits(&wgt::Limits::defaults(), limits); |
| 1310 | + if !failed_limits.is_empty() { |
| 1311 | + log::debug!( |
| 1312 | + "Adapter {:?} is not WebGPU compliant due to limits: {:?}", |
| 1313 | + info, |
| 1314 | + failed_limits |
| 1315 | + ); |
| 1316 | + return false; |
| 1317 | + } |
| 1318 | + |
| 1319 | + if !downlevel.is_webgpu_compliant() { |
| 1320 | + let missing_flags = wgt::DownlevelFlags::compliant() - downlevel.flags; |
| 1321 | + log::debug!( |
| 1322 | + "Adapter {:?} is not WebGPU compliant due to missing downlevel flags: {:?}", |
| 1323 | + info, |
| 1324 | + missing_flags |
| 1325 | + ); |
| 1326 | + return false; |
| 1327 | + } |
| 1328 | + |
| 1329 | + true |
| 1330 | +} |
| 1331 | + |
| 1332 | +#[cfg(test)] |
| 1333 | +mod tests { |
| 1334 | + use super::*; |
| 1335 | + |
| 1336 | + fn compliant_downlevel() -> wgt::DownlevelCapabilities { |
| 1337 | + wgt::DownlevelCapabilities { |
| 1338 | + flags: wgt::DownlevelFlags::compliant(), |
| 1339 | + ..Default::default() |
| 1340 | + } |
| 1341 | + } |
| 1342 | + |
| 1343 | + #[test] |
| 1344 | + fn non_power_of_two_uniform_alignment_always_rejected() { |
| 1345 | + let limits = wgt::Limits { |
| 1346 | + min_uniform_buffer_offset_alignment: 3, |
| 1347 | + ..wgt::Limits::defaults() |
| 1348 | + }; |
| 1349 | + assert!(!adapter_allowed( |
| 1350 | + InstanceFlags::empty(), |
| 1351 | + &"", |
| 1352 | + &limits, |
| 1353 | + &compliant_downlevel() |
| 1354 | + )); |
| 1355 | + assert!(!adapter_allowed( |
| 1356 | + InstanceFlags::STRICT_WEBGPU_COMPLIANCE, |
| 1357 | + &"", |
| 1358 | + &limits, |
| 1359 | + &compliant_downlevel() |
| 1360 | + )); |
| 1361 | + } |
| 1362 | + |
| 1363 | + #[test] |
| 1364 | + fn non_power_of_two_storage_alignment_always_rejected() { |
| 1365 | + let limits = wgt::Limits { |
| 1366 | + min_storage_buffer_offset_alignment: 96, |
| 1367 | + ..wgt::Limits::defaults() |
| 1368 | + }; |
| 1369 | + assert!(!adapter_allowed( |
| 1370 | + InstanceFlags::empty(), |
| 1371 | + &"", |
| 1372 | + &limits, |
| 1373 | + &compliant_downlevel() |
| 1374 | + )); |
| 1375 | + assert!(!adapter_allowed( |
| 1376 | + InstanceFlags::STRICT_WEBGPU_COMPLIANCE, |
| 1377 | + &"", |
| 1378 | + &limits, |
| 1379 | + &compliant_downlevel() |
| 1380 | + )); |
| 1381 | + } |
| 1382 | + |
| 1383 | + #[test] |
| 1384 | + fn low_limits_allowed_without_strict_compliance() { |
| 1385 | + let limits = wgt::Limits { |
| 1386 | + max_texture_dimension_1d: 1, |
| 1387 | + ..wgt::Limits::defaults() |
| 1388 | + }; |
| 1389 | + assert!(adapter_allowed( |
| 1390 | + InstanceFlags::empty(), |
| 1391 | + &"", |
| 1392 | + &limits, |
| 1393 | + &wgt::DownlevelCapabilities::default() |
| 1394 | + )); |
| 1395 | + } |
| 1396 | + |
| 1397 | + #[test] |
| 1398 | + fn low_limits_rejected_with_strict_compliance() { |
| 1399 | + let limits = wgt::Limits { |
| 1400 | + max_texture_dimension_1d: 1, |
| 1401 | + ..wgt::Limits::defaults() |
| 1402 | + }; |
| 1403 | + assert!(!adapter_allowed( |
| 1404 | + InstanceFlags::STRICT_WEBGPU_COMPLIANCE, |
| 1405 | + &"", |
| 1406 | + &limits, |
| 1407 | + &compliant_downlevel() |
| 1408 | + )); |
| 1409 | + } |
| 1410 | + |
| 1411 | + #[test] |
| 1412 | + fn missing_downlevel_flags_rejected_with_strict_compliance() { |
| 1413 | + let downlevel = wgt::DownlevelCapabilities { |
| 1414 | + flags: wgt::DownlevelFlags::empty(), |
| 1415 | + ..Default::default() |
| 1416 | + }; |
| 1417 | + assert!(!adapter_allowed( |
| 1418 | + InstanceFlags::STRICT_WEBGPU_COMPLIANCE, |
| 1419 | + &"", |
| 1420 | + &wgt::Limits::defaults(), |
| 1421 | + &downlevel |
| 1422 | + )); |
| 1423 | + } |
| 1424 | + |
| 1425 | + #[test] |
| 1426 | + fn fully_compliant_adapter_always_allowed() { |
| 1427 | + assert!(adapter_allowed( |
| 1428 | + InstanceFlags::STRICT_WEBGPU_COMPLIANCE, |
| 1429 | + &"", |
| 1430 | + &wgt::Limits::defaults(), |
| 1431 | + &compliant_downlevel() |
| 1432 | + )); |
| 1433 | + } |
| 1434 | +} |
0 commit comments