Skip to content

Commit f76801d

Browse files
fix(metal): Check respondsToSelector before feature detection calls (gfx-rs#9284)
These methods are not implemented by `CaptureMTLDevice`. The actual calls succeed, because the exception is caught and handled by a fallback mechanism, but the valid-method checks by objc2 in debug builds are not aware of the fallback and panic. Fixes gfx-rs#9282
1 parent 4e47084 commit f76801d

2 files changed

Lines changed: 27 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ Bottom level categories:
6868

6969
- Fixed overflow detection and argument domain validation for `acosh`, `length`, `normalize`, and `pow` in constant evaluation. By @ecoricemon in [#9249](https://github.com/gfx-rs/wgpu/pull/9249).
7070

71+
#### Metal
72+
73+
- Added guards to avoid calling some feature detection methods that are not implemented on `CaptureMTLDevice`. By @andyleiserson in [#9284](https://github.com/gfx-rs/wgpu/pull/9284).
74+
7175
## v29.0.0 (2026-03-18)
7276

7377
### Major Changes

wgpu-hal/src/metal/adapter.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use objc2::{available, runtime::ProtocolObject};
1+
use objc2::runtime::{AnyObject, ProtocolObject, Sel};
2+
use objc2::{available, sel};
23
use objc2_foundation::{NSOperatingSystemVersion, NSProcessInfo};
34
use objc2_metal::{
45
MTLArgumentBuffersTier, MTLCounterSamplingPoint, MTLDevice, MTLFeatureSet, MTLGPUFamily,
@@ -14,6 +15,16 @@ use crate::metal::QueueShared;
1415

1516
use super::{OsFeatures, TimestampQuerySupport};
1617

18+
/// Check if a device's class has a given method in its method table.
19+
///
20+
/// This mirrors the check that `objc2` performs internally (in debug builds)
21+
/// before sending a message. We use it to skip method calls that would panic
22+
/// on proxy objects like Apple's `CaptureMTLDevice`, which forwards messages
23+
/// at runtime but doesn't declare the methods in its class.
24+
fn device_class_responds_to(device: &ProtocolObject<dyn MTLDevice>, sel: Sel) -> bool {
25+
AnyObject::class(device.as_ref()).responds_to(sel)
26+
}
27+
1728
/// Maximum number of command buffers for `MTLCommandQueue`s that we create.
1829
///
1930
/// If a [new command buffer] is requested when Metal has run out of command
@@ -711,6 +722,7 @@ impl super::CapabilitiesQuery {
711722
texture_cube_array: Self::supports_any(device, TEXTURE_CUBE_ARRAY_SUPPORT),
712723
supports_float_filtering: os_type == super::OsType::Macos
713724
|| (available!(macos = 11.0, ios = 14.0, tvos = 16.0, visionos = 1.0)
725+
&& device_class_responds_to(device, sel!(supports32BitFloatFiltering))
714726
&& device.supports32BitFloatFiltering()),
715727
format_depth24_stencil8: os_type == super::OsType::Macos
716728
&& device.isDepth24Stencil8PixelFormatSupported(),
@@ -983,7 +995,12 @@ impl super::CapabilitiesQuery {
983995
|| device.supportsFamily(MTLGPUFamily::Apple7)
984996
|| device.supportsFamily(MTLGPUFamily::Mac2)),
985997
// https://developer.apple.com/documentation/metal/mtldevice/hasunifiedmemory
986-
has_unified_memory: if available!(macos = 15.0, ios = 13.0, tvos = 13.0, visionos = 1.0)
998+
has_unified_memory: if available!(
999+
macos = 10.15,
1000+
ios = 13.0,
1001+
tvos = 13.0,
1002+
visionos = 1.0
1003+
) && device_class_responds_to(device, sel!(hasUnifiedMemory))
9871004
{
9881005
Some(device.hasUnifiedMemory())
9891006
} else {
@@ -1057,7 +1074,10 @@ impl super::CapabilitiesQuery {
10571074
tvos = 18.0,
10581075
visionos = 2.0,
10591076
) {
1060-
device.supportsRaytracing() && device.supportsRaytracingFromRender()
1077+
device_class_responds_to(device, sel!(supportsRaytracing))
1078+
&& device.supportsRaytracing()
1079+
&& device_class_responds_to(device, sel!(supportsRaytracingFromRender))
1080+
&& device.supportsRaytracingFromRender()
10611081
} else {
10621082
false
10631083
},

0 commit comments

Comments
 (0)