1 /*!
2 # Metal backend internals.
3
4 ## Pipeline Layout
5
6 In Metal, push constants, vertex buffers, and resources in the descriptor sets
7 are all placed together in the native resource bindings, which work similarly to D3D11:
8 there are tables of textures, buffers, and samplers.
9
10 We put push constants first (if any) in the table, followed by descriptor set 0
11 resource, followed by other descriptor sets. The vertex buffers are bound at the very
12 end of the VS buffer table.
13
14 When argument buffers are supported, each descriptor set becomes a buffer binding,
15 but the general placement rule is the same.
16
17 ## Command recording
18
19 One-time-submit primary command buffers are recorded "live" into `MTLCommandBuffer`.
20 Special care is taken to the recording state: active bindings are restored at the
21 start of any render or compute pass.
22
23 Multi-submit and secondary command buffers are recorded as "soft" commands into
24 `Journal`. Actual native recording is done at either `submit` or `execute_commands`
25 correspondingly. When that happens, we `enqueue` the command buffer at the start
26 of recording, which allows the driver to work on pass translation at the same time
27 as we are recording the following passes.
28
29 ## Memory
30
31 In general, "Shared" storage is used for CPU-coherent memory. "Managed" is used for
32 non-coherent CPU-visible memory. Finally, "Private" storage is backing device-local
33 memory types.
34
35 Metal doesn't have CPU-visible memory for textures. We only allow RGBA8 2D textures
36 to be allocated from it, and only for the matter of transfer operations, which is
37 the minimum required by Vulkan. In fact, these become just glorified staging buffers.
38
39 ## Events
40
41 Events are represented by just an atomic bool. When recording, a command buffer keeps
42 track of all events set or reset. Signalling within a command buffer is therefore a
43 matter of simply checking that local list. When making a submission, used events are
44 also accumulated temporarily, so that we can change their values in the completion
45 handler of the last command buffer. We also check this list in order to resolve events
46 fired in one command buffer and waited in another one within the same submission.
47
48 Waiting for an event from a different submission is accomplished similar to waiting
49 for the host. We block all the submissions until the host blockers are resolved, and
50 these are checked at certain points like setting an event by the device, or waiting
51 for a fence.
52 !*/
53
54 #[macro_use]
55 extern crate bitflags;
56 #[macro_use]
57 extern crate objc;
58 #[macro_use]
59 extern crate log;
60
61 use hal::{
62 adapter::{Adapter, AdapterInfo, DeviceType},
63 queue::{QueueFamilyId, QueueType},
64 };
65 use range_alloc::RangeAllocator;
66
67 use cocoa_foundation::foundation::NSInteger;
68 #[cfg(feature = "dispatch")]
69 use dispatch;
70 use foreign_types::ForeignTypeRef;
71 use metal::MTLFeatureSet;
72 use metal::MTLGPUFamily;
73 use metal::MTLLanguageVersion;
74 use metal::{CGFloat, CGSize, MetalLayer, MetalLayerRef};
75 use objc::{
76 declare::ClassDecl,
77 runtime::{Class, Object, Sel, BOOL, YES},
78 };
79 use parking_lot::{Condvar, Mutex};
80
81 use std::{
82 collections::HashMap,
83 hash::BuildHasherDefault,
84 mem,
85 os::raw::c_void,
86 ptr::NonNull,
87 sync::{Arc, Once},
88 };
89
90 mod command;
91 mod conversions;
92 mod device;
93 mod internal;
94 mod native;
95 #[cfg(feature = "pipeline-cache")]
96 mod pipeline_cache;
97 mod soft;
98 mod window;
99
100 pub use crate::command::CommandPool;
101 pub use crate::device::{Device, LanguageVersion, PhysicalDevice};
102 pub use crate::window::Surface;
103
104 pub type GraphicsCommandPool = CommandPool;
105 type FastHashMap<K, V> = HashMap<K, V, BuildHasherDefault<fxhash::FxHasher>>;
106
107 //TODO: investigate why exactly using `u8` here is slower (~5% total).
108 /// A type representing Metal binding's resource index.
109 type ResourceIndex = u32;
110
111 // For CALayer contentsGravity
112 #[link(name = "QuartzCore", kind = "framework")]
113 extern "C" {
114 #[allow(non_upper_case_globals)]
115 static kCAGravityTopLeft: cocoa_foundation::base::id;
116 }
117
118 #[repr(C)]
119 #[derive(Clone, Copy, Debug, Default)]
120 pub struct CGPoint {
121 pub x: CGFloat,
122 pub y: CGFloat,
123 }
124
125 impl CGPoint {
126 #[inline]
new(x: CGFloat, y: CGFloat) -> CGPoint127 pub fn new(x: CGFloat, y: CGFloat) -> CGPoint {
128 CGPoint { x, y }
129 }
130 }
131
132 #[repr(C)]
133 #[derive(Clone, Copy, Debug, Default)]
134 pub struct CGRect {
135 pub origin: CGPoint,
136 pub size: CGSize,
137 }
138
139 impl CGRect {
140 #[inline]
new(origin: CGPoint, size: CGSize) -> CGRect141 pub fn new(origin: CGPoint, size: CGSize) -> CGRect {
142 CGRect { origin, size }
143 }
144 }
145
146 /// Method of recording one-time-submit command buffers.
147 #[derive(Clone, Debug, Hash, PartialEq)]
148 pub enum OnlineRecording {
149 /// Record natively on-the-fly.
150 Immediate,
151 /// Store commands and only start recording at submission time.
152 Deferred,
153 #[cfg(feature = "dispatch")]
154 /// Start recording asynchronously upon finishing each pass.
155 Remote(dispatch::QueuePriority),
156 }
157
158 impl Default for OnlineRecording {
default() -> Self159 fn default() -> Self {
160 OnlineRecording::Immediate
161 }
162 }
163
164 const MAX_ACTIVE_COMMAND_BUFFERS: usize = 1 << 14;
165 const MAX_VISIBILITY_QUERIES: usize = 1 << 14;
166 const MAX_COLOR_ATTACHMENTS: usize = 8;
167 const MAX_BOUND_DESCRIPTOR_SETS: usize = 8;
168
169 #[derive(Debug, Clone, Copy)]
170 pub struct QueueFamily {}
171
172 impl hal::queue::QueueFamily for QueueFamily {
queue_type(&self) -> QueueType173 fn queue_type(&self) -> QueueType {
174 QueueType::General
175 }
max_queues(&self) -> usize176 fn max_queues(&self) -> usize {
177 1
178 }
id(&self) -> QueueFamilyId179 fn id(&self) -> QueueFamilyId {
180 QueueFamilyId(0)
181 }
supports_sparse_binding(&self) -> bool182 fn supports_sparse_binding(&self) -> bool {
183 false
184 }
185 }
186
187 #[derive(Debug)]
188 struct VisibilityShared {
189 /// Availability buffer is in shared memory, it has N double words for
190 /// query results followed by N words for the availability.
191 buffer: metal::Buffer,
192 allocator: Mutex<RangeAllocator<hal::query::Id>>,
193 availability_offset: hal::buffer::Offset,
194 condvar: Condvar,
195 }
196
197 #[derive(Debug)]
198 struct Shared {
199 device: Mutex<metal::Device>,
200 queue: Mutex<command::QueueInner>,
201 queue_blocker: Mutex<command::QueueBlocker>,
202 service_pipes: internal::ServicePipes,
203 disabilities: PrivateDisabilities,
204 private_caps: PrivateCapabilities,
205 visibility: VisibilityShared,
206 }
207
208 unsafe impl Send for Shared {}
209 unsafe impl Sync for Shared {}
210
211 impl Shared {
new(device: metal::Device, experiments: &Experiments) -> Self212 fn new(device: metal::Device, experiments: &Experiments) -> Self {
213 let private_caps = PrivateCapabilities::new(&device, experiments);
214 debug!("{:#?}", private_caps);
215
216 let visibility = VisibilityShared {
217 buffer: device.new_buffer(
218 MAX_VISIBILITY_QUERIES as u64
219 * (mem::size_of::<u64>() + mem::size_of::<u32>()) as u64,
220 metal::MTLResourceOptions::StorageModeShared,
221 ),
222 allocator: Mutex::new(RangeAllocator::new(
223 0..MAX_VISIBILITY_QUERIES as hal::query::Id,
224 )),
225 availability_offset: (MAX_VISIBILITY_QUERIES * mem::size_of::<u64>())
226 as hal::buffer::Offset,
227 condvar: Condvar::new(),
228 };
229 Shared {
230 queue: Mutex::new(command::QueueInner::new(
231 &device,
232 Some(MAX_ACTIVE_COMMAND_BUFFERS),
233 )),
234 queue_blocker: Mutex::new(command::QueueBlocker::default()),
235 service_pipes: internal::ServicePipes::new(&device),
236 disabilities: PrivateDisabilities {
237 broken_viewport_near_depth: device.name().starts_with("Intel")
238 && !device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v4),
239 broken_layered_clear_image: device.name().starts_with("Intel"),
240 },
241 private_caps,
242 device: Mutex::new(device),
243 visibility,
244 }
245 }
246 }
247
248 #[derive(Clone, Debug, Default)]
249 pub struct Experiments {
250 pub argument_buffers: bool,
251 }
252
253 #[derive(Debug)]
254 pub struct Instance {
255 pub experiments: Experiments,
256 gfx_managed_metal_layer_delegate: GfxManagedMetalLayerDelegate,
257 }
258
259 impl hal::Instance<Backend> for Instance {
create(_: &str, _: u32) -> Result<Self, hal::UnsupportedBackend>260 fn create(_: &str, _: u32) -> Result<Self, hal::UnsupportedBackend> {
261 Ok(Instance {
262 experiments: Experiments::default(),
263 gfx_managed_metal_layer_delegate: GfxManagedMetalLayerDelegate::new(),
264 })
265 }
266
enumerate_adapters(&self) -> Vec<Adapter<Backend>>267 fn enumerate_adapters(&self) -> Vec<Adapter<Backend>> {
268 let devices = metal::Device::all();
269 let mut adapters: Vec<Adapter<Backend>> = devices
270 .into_iter()
271 .map(|dev| {
272 let name = dev.name().into();
273 let shared = Shared::new(dev, &self.experiments);
274 let physical_device = device::PhysicalDevice::new(Arc::new(shared));
275 Adapter {
276 info: AdapterInfo {
277 name,
278 vendor: 0,
279 device: 0,
280 device_type: if physical_device.shared.private_caps.low_power {
281 DeviceType::IntegratedGpu
282 } else {
283 DeviceType::DiscreteGpu
284 },
285 },
286 physical_device,
287 queue_families: vec![QueueFamily {}],
288 }
289 })
290 .collect();
291 adapters.sort_by_key(|adapt| {
292 (
293 adapt.physical_device.shared.private_caps.low_power,
294 adapt.physical_device.shared.private_caps.headless,
295 )
296 });
297 adapters
298 }
299
create_surface( &self, has_handle: &impl raw_window_handle::HasRawWindowHandle, ) -> Result<Surface, hal::window::InitError>300 unsafe fn create_surface(
301 &self,
302 has_handle: &impl raw_window_handle::HasRawWindowHandle,
303 ) -> Result<Surface, hal::window::InitError> {
304 match has_handle.raw_window_handle() {
305 #[cfg(target_os = "ios")]
306 raw_window_handle::RawWindowHandle::IOS(handle) => {
307 Ok(self.create_surface_from_uiview(handle.ui_view))
308 }
309 #[cfg(target_os = "macos")]
310 raw_window_handle::RawWindowHandle::MacOS(handle) => {
311 Ok(self.create_surface_from_nsview(handle.ns_view))
312 }
313 _ => Err(hal::window::InitError::UnsupportedWindowHandle),
314 }
315 }
316
destroy_surface(&self, surface: Surface)317 unsafe fn destroy_surface(&self, surface: Surface) {
318 surface.dispose();
319 }
320 }
321
layer_should_inherit_contents_scale_from_window( _: &Class, _: Sel, _layer: *mut Object, _new_scale: CGFloat, _from_window: *mut Object, ) -> BOOL322 extern "C" fn layer_should_inherit_contents_scale_from_window(
323 _: &Class,
324 _: Sel,
325 _layer: *mut Object,
326 _new_scale: CGFloat,
327 _from_window: *mut Object,
328 ) -> BOOL {
329 YES
330 }
331
332 const CAML_DELEGATE_CLASS: &str = "GfxManagedMetalLayerDelegate";
333 static CAML_DELEGATE_REGISTER: Once = Once::new();
334
335 #[derive(Debug)]
336 struct GfxManagedMetalLayerDelegate(&'static Class);
337
338 impl GfxManagedMetalLayerDelegate {
new() -> Self339 pub fn new() -> Self {
340 CAML_DELEGATE_REGISTER.call_once(|| {
341 type Fun = extern "C" fn(&Class, Sel, *mut Object, CGFloat, *mut Object) -> BOOL;
342 let mut decl = ClassDecl::new(CAML_DELEGATE_CLASS, class!(NSObject)).unwrap();
343 unsafe {
344 decl.add_class_method(
345 sel!(layer:shouldInheritContentsScale:fromWindow:),
346 layer_should_inherit_contents_scale_from_window as Fun,
347 );
348 }
349 decl.register();
350 });
351 GfxManagedMetalLayerDelegate(Class::get(CAML_DELEGATE_CLASS).unwrap())
352 }
353 }
354
355 impl Instance {
356 #[cfg(target_os = "ios")]
create_from_uiview(&self, uiview: *mut c_void) -> Surface357 unsafe fn create_from_uiview(&self, uiview: *mut c_void) -> Surface {
358 let view: cocoa_foundation::base::id = mem::transmute(uiview);
359 if view.is_null() {
360 panic!("window does not have a valid contentView");
361 }
362
363 let main_layer: *mut Object = msg_send![view, layer];
364 let class = class!(CAMetalLayer);
365 let is_valid_layer: BOOL = msg_send![main_layer, isKindOfClass: class];
366 let render_layer = if is_valid_layer == YES {
367 mem::transmute::<_, &MetalLayerRef>(main_layer).to_owned()
368 } else {
369 // If the main layer is not a CAMetalLayer, we create a CAMetalLayer sublayer and use it instead.
370 // Unlike on macOS, we cannot replace the main view as UIView does not allow it (when NSView does).
371 let new_layer: MetalLayer = msg_send![class, new];
372 let bounds: CGRect = msg_send![main_layer, bounds];
373 let () = msg_send![new_layer.as_ref(), setFrame: bounds];
374 let () = msg_send![main_layer, addSublayer: new_layer.as_ref()];
375 new_layer
376 };
377
378 let window: cocoa_foundation::base::id = msg_send![view, window];
379 if !window.is_null() {
380 let screen: cocoa_foundation::base::id = msg_send![window, screen];
381 assert!(!screen.is_null(), "window is not attached to a screen");
382
383 let scale_factor: CGFloat = msg_send![screen, nativeScale];
384 let () = msg_send![view, setContentScaleFactor: scale_factor];
385 }
386
387 let _: *mut c_void = msg_send![view, retain];
388 Surface::new(NonNull::new(view), render_layer)
389 }
390
391 #[cfg(target_os = "macos")]
create_from_nsview(&self, nsview: *mut c_void) -> Surface392 unsafe fn create_from_nsview(&self, nsview: *mut c_void) -> Surface {
393 let view: cocoa_foundation::base::id = mem::transmute(nsview);
394 if view.is_null() {
395 panic!("window does not have a valid contentView");
396 }
397
398 let class = class!(CAMetalLayer);
399 // Deprecated! Clients should use `create_surface_from_layer` instead.
400 let is_actually_layer: BOOL = msg_send![view, isKindOfClass: class];
401 if is_actually_layer == YES {
402 return self.create_from_layer(mem::transmute(view));
403 }
404
405 let existing: *mut Object = msg_send![view, layer];
406 let use_current = if existing.is_null() {
407 false
408 } else {
409 let result: BOOL = msg_send![existing, isKindOfClass: class];
410 result == YES
411 };
412
413 let render_layer: MetalLayer = if use_current {
414 mem::transmute::<_, &MetalLayerRef>(existing).to_owned()
415 } else {
416 let layer: MetalLayer = msg_send![class, new];
417 let () = msg_send![view, setLayer: layer.as_ref()];
418 let () = msg_send![view, setWantsLayer: YES];
419 let bounds: CGRect = msg_send![view, bounds];
420 let () = msg_send![layer.as_ref(), setBounds: bounds];
421
422 let window: cocoa_foundation::base::id = msg_send![view, window];
423 if !window.is_null() {
424 let scale_factor: CGFloat = msg_send![window, backingScaleFactor];
425 let () = msg_send![layer, setContentsScale: scale_factor];
426 }
427 let () = msg_send![layer, setDelegate: self.gfx_managed_metal_layer_delegate.0];
428 layer
429 };
430
431 let () = msg_send![render_layer, setContentsGravity: kCAGravityTopLeft];
432
433 let _: *mut c_void = msg_send![view, retain];
434 Surface::new(NonNull::new(view), render_layer)
435 }
436
create_from_layer(&self, layer: &MetalLayerRef) -> Surface437 unsafe fn create_from_layer(&self, layer: &MetalLayerRef) -> Surface {
438 let class = class!(CAMetalLayer);
439 let proper_kind: BOOL = msg_send![layer, isKindOfClass: class];
440 assert_eq!(proper_kind, YES);
441 Surface::new(None, layer.to_owned())
442 }
443
create_surface_from_layer(&self, layer: &MetalLayerRef) -> Surface444 pub fn create_surface_from_layer(&self, layer: &MetalLayerRef) -> Surface {
445 unsafe { self.create_from_layer(layer) }
446 }
447
448 #[cfg(target_os = "macos")]
create_surface_from_nsview(&self, nsview: *mut c_void) -> Surface449 pub fn create_surface_from_nsview(&self, nsview: *mut c_void) -> Surface {
450 unsafe { self.create_from_nsview(nsview) }
451 }
452
453 #[cfg(target_os = "ios")]
create_surface_from_uiview(&self, uiview: *mut c_void) -> Surface454 pub fn create_surface_from_uiview(&self, uiview: *mut c_void) -> Surface {
455 unsafe { self.create_from_uiview(uiview) }
456 }
457 }
458
459 #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
460 pub enum Backend {}
461 impl hal::Backend for Backend {
462 type Instance = Instance;
463 type PhysicalDevice = device::PhysicalDevice;
464 type Device = device::Device;
465 type Surface = Surface;
466
467 type QueueFamily = QueueFamily;
468 type Queue = command::Queue;
469 type CommandBuffer = command::CommandBuffer;
470
471 type Memory = native::Memory;
472 type CommandPool = command::CommandPool;
473
474 type ShaderModule = native::ShaderModule;
475 type RenderPass = native::RenderPass;
476 type Framebuffer = native::Framebuffer;
477
478 type Buffer = native::Buffer;
479 type BufferView = native::BufferView;
480 type Image = native::Image;
481 type ImageView = native::ImageView;
482 type Sampler = native::Sampler;
483
484 type ComputePipeline = native::ComputePipeline;
485 type GraphicsPipeline = native::GraphicsPipeline;
486 type PipelineCache = native::PipelineCache;
487 type PipelineLayout = native::PipelineLayout;
488 type DescriptorSetLayout = native::DescriptorSetLayout;
489 type DescriptorPool = native::DescriptorPool;
490 type DescriptorSet = native::DescriptorSet;
491
492 type Fence = native::Fence;
493 type Semaphore = native::Semaphore;
494 type Event = native::Event;
495 type QueryPool = native::QueryPool;
496 }
497
498 const RESOURCE_HEAP_SUPPORT: &[MTLFeatureSet] = &[
499 MTLFeatureSet::iOS_GPUFamily1_v3,
500 MTLFeatureSet::iOS_GPUFamily2_v3,
501 MTLFeatureSet::iOS_GPUFamily3_v2,
502 MTLFeatureSet::iOS_GPUFamily4_v1,
503 MTLFeatureSet::iOS_GPUFamily5_v1,
504 MTLFeatureSet::tvOS_GPUFamily1_v2,
505 MTLFeatureSet::tvOS_GPUFamily2_v1,
506 MTLFeatureSet::macOS_GPUFamily1_v3,
507 MTLFeatureSet::macOS_GPUFamily2_v1,
508 ];
509
510 const ARGUMENT_BUFFER_SUPPORT: &[MTLFeatureSet] = &[
511 MTLFeatureSet::iOS_GPUFamily1_v4,
512 MTLFeatureSet::iOS_GPUFamily2_v4,
513 MTLFeatureSet::iOS_GPUFamily3_v3,
514 MTLFeatureSet::iOS_GPUFamily4_v1,
515 MTLFeatureSet::iOS_GPUFamily5_v1,
516 MTLFeatureSet::tvOS_GPUFamily1_v3,
517 MTLFeatureSet::macOS_GPUFamily1_v3,
518 MTLFeatureSet::macOS_GPUFamily2_v1,
519 ];
520
521 const MUTABLE_COMPARISON_SAMPLER_SUPPORT: &[MTLFeatureSet] = &[
522 MTLFeatureSet::iOS_GPUFamily3_v1,
523 MTLFeatureSet::iOS_GPUFamily4_v1,
524 MTLFeatureSet::iOS_GPUFamily5_v1,
525 MTLFeatureSet::macOS_GPUFamily1_v1,
526 MTLFeatureSet::macOS_GPUFamily2_v1,
527 ];
528
529 const SAMPLER_CLAMP_TO_BORDER_SUPPORT: &[MTLFeatureSet] = &[
530 MTLFeatureSet::macOS_GPUFamily1_v2,
531 MTLFeatureSet::macOS_GPUFamily2_v1,
532 ];
533
534 const ASTC_PIXEL_FORMAT_FEATURES: &[MTLFeatureSet] = &[
535 MTLFeatureSet::iOS_GPUFamily2_v1,
536 MTLFeatureSet::iOS_GPUFamily3_v1,
537 MTLFeatureSet::iOS_GPUFamily4_v1,
538 MTLFeatureSet::iOS_GPUFamily5_v1,
539 MTLFeatureSet::tvOS_GPUFamily1_v1,
540 MTLFeatureSet::tvOS_GPUFamily2_v1,
541 ];
542
543 const ANY8_UNORM_SRGB_ALL: &[MTLFeatureSet] = &[
544 MTLFeatureSet::iOS_GPUFamily2_v3,
545 MTLFeatureSet::iOS_GPUFamily3_v1,
546 MTLFeatureSet::iOS_GPUFamily4_v1,
547 MTLFeatureSet::iOS_GPUFamily5_v1,
548 MTLFeatureSet::tvOS_GPUFamily1_v2,
549 MTLFeatureSet::tvOS_GPUFamily2_v1,
550 ];
551
552 const ANY8_SNORM_RESOLVE: &[MTLFeatureSet] = &[
553 MTLFeatureSet::iOS_GPUFamily2_v1,
554 MTLFeatureSet::iOS_GPUFamily3_v1,
555 MTLFeatureSet::iOS_GPUFamily4_v1,
556 MTLFeatureSet::iOS_GPUFamily5_v1,
557 MTLFeatureSet::tvOS_GPUFamily1_v1,
558 MTLFeatureSet::tvOS_GPUFamily2_v1,
559 MTLFeatureSet::macOS_GPUFamily1_v1,
560 MTLFeatureSet::macOS_GPUFamily2_v1,
561 ];
562
563 const RGBA8_SRGB: &[MTLFeatureSet] = &[
564 MTLFeatureSet::iOS_GPUFamily2_v3,
565 MTLFeatureSet::iOS_GPUFamily3_v1,
566 MTLFeatureSet::iOS_GPUFamily4_v1,
567 MTLFeatureSet::iOS_GPUFamily5_v1,
568 MTLFeatureSet::tvOS_GPUFamily1_v2,
569 MTLFeatureSet::tvOS_GPUFamily2_v1,
570 ];
571
572 const RGB10A2UNORM_ALL: &[MTLFeatureSet] = &[
573 MTLFeatureSet::iOS_GPUFamily3_v1,
574 MTLFeatureSet::iOS_GPUFamily4_v1,
575 MTLFeatureSet::iOS_GPUFamily5_v1,
576 MTLFeatureSet::tvOS_GPUFamily2_v1,
577 MTLFeatureSet::macOS_GPUFamily1_v1,
578 MTLFeatureSet::macOS_GPUFamily2_v1,
579 ];
580
581 const RGB10A2UINT_COLOR_WRITE: &[MTLFeatureSet] = &[
582 MTLFeatureSet::iOS_GPUFamily3_v1,
583 MTLFeatureSet::iOS_GPUFamily4_v1,
584 MTLFeatureSet::iOS_GPUFamily5_v1,
585 MTLFeatureSet::tvOS_GPUFamily2_v1,
586 MTLFeatureSet::macOS_GPUFamily1_v1,
587 MTLFeatureSet::macOS_GPUFamily2_v1,
588 ];
589
590 const RG11B10FLOAT_ALL: &[MTLFeatureSet] = &[
591 MTLFeatureSet::iOS_GPUFamily3_v1,
592 MTLFeatureSet::iOS_GPUFamily4_v1,
593 MTLFeatureSet::iOS_GPUFamily5_v1,
594 MTLFeatureSet::tvOS_GPUFamily2_v1,
595 MTLFeatureSet::macOS_GPUFamily1_v1,
596 MTLFeatureSet::macOS_GPUFamily2_v1,
597 ];
598
599 const RGB9E5FLOAT_ALL: &[MTLFeatureSet] = &[
600 MTLFeatureSet::iOS_GPUFamily3_v1,
601 MTLFeatureSet::iOS_GPUFamily4_v1,
602 MTLFeatureSet::iOS_GPUFamily5_v1,
603 MTLFeatureSet::tvOS_GPUFamily2_v1,
604 ];
605
606 const BGR10A2_ALL: &[MTLFeatureSet] = &[
607 MTLFeatureSet::iOS_GPUFamily1_v4,
608 MTLFeatureSet::iOS_GPUFamily2_v4,
609 MTLFeatureSet::iOS_GPUFamily3_v3,
610 MTLFeatureSet::iOS_GPUFamily4_v1,
611 MTLFeatureSet::iOS_GPUFamily5_v1,
612 MTLFeatureSet::tvOS_GPUFamily1_v3,
613 MTLFeatureSet::tvOS_GPUFamily2_v1,
614 MTLFeatureSet::macOS_GPUFamily1_v3,
615 MTLFeatureSet::macOS_GPUFamily2_v1,
616 ];
617
618 const BASE_INSTANCE_SUPPORT: &[MTLFeatureSet] = &[
619 MTLFeatureSet::iOS_GPUFamily3_v1,
620 MTLFeatureSet::iOS_GPUFamily4_v1,
621 MTLFeatureSet::iOS_GPUFamily5_v1,
622 MTLFeatureSet::tvOS_GPUFamily2_v1,
623 MTLFeatureSet::macOS_GPUFamily1_v1,
624 MTLFeatureSet::macOS_GPUFamily2_v1,
625 ];
626
627 const BASE_VERTEX_INSTANCE_SUPPORT: &[MTLFeatureSet] = &[
628 MTLFeatureSet::iOS_GPUFamily3_v1,
629 MTLFeatureSet::iOS_GPUFamily4_v1,
630 MTLFeatureSet::iOS_GPUFamily5_v1,
631 MTLFeatureSet::tvOS_GPUFamily2_v1,
632 MTLFeatureSet::macOS_GPUFamily1_v1,
633 MTLFeatureSet::macOS_GPUFamily2_v1,
634 ];
635
636 const TEXTURE_CUBE_ARRAY_SUPPORT: &[MTLFeatureSet] = &[
637 MTLFeatureSet::iOS_GPUFamily4_v1,
638 MTLFeatureSet::iOS_GPUFamily5_v1,
639 MTLFeatureSet::tvOS_GPUFamily1_v2,
640 MTLFeatureSet::tvOS_GPUFamily2_v1,
641 MTLFeatureSet::macOS_GPUFamily1_v1,
642 MTLFeatureSet::macOS_GPUFamily2_v1,
643 ];
644
645 const DUAL_SOURCE_BLEND_SUPPORT: &[MTLFeatureSet] = &[
646 MTLFeatureSet::iOS_GPUFamily1_v4,
647 MTLFeatureSet::iOS_GPUFamily2_v4,
648 MTLFeatureSet::iOS_GPUFamily3_v3,
649 MTLFeatureSet::iOS_GPUFamily4_v1,
650 MTLFeatureSet::iOS_GPUFamily5_v1,
651 MTLFeatureSet::tvOS_GPUFamily1_v3,
652 MTLFeatureSet::tvOS_GPUFamily2_v1,
653 MTLFeatureSet::macOS_GPUFamily1_v2,
654 MTLFeatureSet::macOS_GPUFamily2_v1,
655 ];
656
657 const LAYERED_RENDERING_SUPPORT: &[MTLFeatureSet] = &[
658 MTLFeatureSet::iOS_GPUFamily5_v1,
659 MTLFeatureSet::macOS_GPUFamily1_v1,
660 MTLFeatureSet::macOS_GPUFamily2_v1,
661 ];
662
663 const FUNCTION_SPECIALIZATION_SUPPORT: &[MTLFeatureSet] = &[
664 MTLFeatureSet::iOS_GPUFamily1_v3,
665 MTLFeatureSet::iOS_GPUFamily2_v3,
666 MTLFeatureSet::iOS_GPUFamily3_v2,
667 MTLFeatureSet::iOS_GPUFamily4_v1,
668 MTLFeatureSet::iOS_GPUFamily5_v1,
669 MTLFeatureSet::tvOS_GPUFamily1_v2,
670 MTLFeatureSet::macOS_GPUFamily1_v2,
671 MTLFeatureSet::macOS_GPUFamily2_v1,
672 ];
673
674 const DEPTH_CLIP_MODE: &[MTLFeatureSet] = &[
675 MTLFeatureSet::iOS_GPUFamily4_v1,
676 MTLFeatureSet::iOS_GPUFamily5_v1,
677 MTLFeatureSet::tvOS_GPUFamily1_v3,
678 MTLFeatureSet::macOS_GPUFamily1_v1,
679 MTLFeatureSet::macOS_GPUFamily2_v1,
680 ];
681
682 #[derive(Clone, Debug)]
683 struct PrivateCapabilities {
684 pub os_is_mac: bool,
685 os_version: (u32, u32),
686 msl_version: metal::MTLLanguageVersion,
687 exposed_queues: usize,
688 read_write_texture_tier: metal::MTLReadWriteTextureTier,
689 // if TRUE, we'll report `NON_FILL_POLYGON_MODE` feature without the points support
690 expose_line_mode: bool,
691 resource_heaps: bool,
692 argument_buffers: bool,
693 shared_textures: bool,
694 mutable_comparison_samplers: bool,
695 sampler_clamp_to_border: bool,
696 base_instance: bool,
697 base_vertex_instance_drawing: bool,
698 dual_source_blending: bool,
699 low_power: bool,
700 headless: bool,
701 layered_rendering: bool,
702 function_specialization: bool,
703 depth_clip_mode: bool,
704 texture_cube_array: bool,
705 format_depth24_stencil8: bool,
706 format_depth32_stencil8_filter: bool,
707 format_depth32_stencil8_none: bool,
708 format_min_srgb_channels: u8,
709 format_b5: bool,
710 format_bc: bool,
711 format_eac_etc: bool,
712 format_astc: bool,
713 format_any8_unorm_srgb_all: bool,
714 format_any8_unorm_srgb_no_write: bool,
715 format_any8_snorm_all: bool,
716 format_r16_norm_all: bool,
717 format_r32_all: bool,
718 format_r32_no_write: bool,
719 format_r32float_no_write_no_filter: bool,
720 format_r32float_no_filter: bool,
721 format_r32float_all: bool,
722 format_rgba8_srgb_all: bool,
723 format_rgba8_srgb_no_write: bool,
724 format_rgb10a2_unorm_all: bool,
725 format_rgb10a2_unorm_no_write: bool,
726 format_rgb10a2_uint_color: bool,
727 format_rgb10a2_uint_color_write: bool,
728 format_rg11b10_all: bool,
729 format_rg11b10_no_write: bool,
730 format_rgb9e5_all: bool,
731 format_rgb9e5_no_write: bool,
732 format_rgb9e5_filter_only: bool,
733 format_rg32_color: bool,
734 format_rg32_color_write: bool,
735 format_rg32float_all: bool,
736 format_rg32float_color_blend: bool,
737 format_rg32float_no_filter: bool,
738 format_rgba32int_color: bool,
739 format_rgba32int_color_write: bool,
740 format_rgba32float_color: bool,
741 format_rgba32float_color_write: bool,
742 format_rgba32float_all: bool,
743 format_depth16unorm: bool,
744 format_depth32float_filter: bool,
745 format_depth32float_none: bool,
746 format_bgr10a2_all: bool,
747 format_bgr10a2_no_write: bool,
748 max_buffers_per_stage: ResourceIndex,
749 max_textures_per_stage: ResourceIndex,
750 max_samplers_per_stage: ResourceIndex,
751 buffer_alignment: u64,
752 max_buffer_size: u64,
753 max_texture_size: u64,
754 max_texture_3d_size: u64,
755 max_texture_layers: u64,
756 max_fragment_input_components: u64,
757 max_color_render_targets: u8,
758 max_total_threadgroup_memory: u32,
759 sample_count_mask: u8,
760 supports_debug_markers: bool,
761 supports_binary_archives: bool,
762 }
763
764 impl PrivateCapabilities {
version_at_least(major: u32, minor: u32, needed_major: u32, needed_minor: u32) -> bool765 fn version_at_least(major: u32, minor: u32, needed_major: u32, needed_minor: u32) -> bool {
766 major > needed_major || (major == needed_major && minor >= needed_minor)
767 }
768
supports_any(raw: &metal::DeviceRef, features_sets: &[MTLFeatureSet]) -> bool769 fn supports_any(raw: &metal::DeviceRef, features_sets: &[MTLFeatureSet]) -> bool {
770 features_sets
771 .iter()
772 .cloned()
773 .any(|x| raw.supports_feature_set(x))
774 }
775
new(device: &metal::Device, experiments: &Experiments) -> Self776 fn new(device: &metal::Device, experiments: &Experiments) -> Self {
777 #[repr(C)]
778 #[derive(Clone, Copy, Debug)]
779 struct NSOperatingSystemVersion {
780 major: NSInteger,
781 minor: NSInteger,
782 patch: NSInteger,
783 }
784
785 let version: NSOperatingSystemVersion = unsafe {
786 let process_info: *mut Object = msg_send![class!(NSProcessInfo), processInfo];
787 msg_send![process_info, operatingSystemVersion]
788 };
789
790 let major = version.major as u32;
791 let minor = version.minor as u32;
792 let os_is_mac = device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v1);
793
794 let mut sample_count_mask: u8 = 1 | 4; // 1 and 4 samples are supported on all devices
795 if device.supports_texture_sample_count(2) {
796 sample_count_mask |= 2;
797 }
798 if device.supports_texture_sample_count(8) {
799 sample_count_mask |= 8;
800 }
801
802 PrivateCapabilities {
803 os_is_mac,
804 os_version: (major as u32, minor as u32),
805 msl_version: if os_is_mac {
806 if Self::version_at_least(major, minor, 10, 15) {
807 MTLLanguageVersion::V2_2
808 } else if Self::version_at_least(major, minor, 10, 14) {
809 MTLLanguageVersion::V2_1
810 } else if Self::version_at_least(major, minor, 10, 13) {
811 MTLLanguageVersion::V2_0
812 } else if Self::version_at_least(major, minor, 10, 12) {
813 MTLLanguageVersion::V1_2
814 } else if Self::version_at_least(major, minor, 10, 11) {
815 MTLLanguageVersion::V1_1
816 } else {
817 MTLLanguageVersion::V1_0
818 }
819 } else if Self::version_at_least(major, minor, 13, 0) {
820 MTLLanguageVersion::V2_2
821 } else if Self::version_at_least(major, minor, 12, 0) {
822 MTLLanguageVersion::V2_1
823 } else if Self::version_at_least(major, minor, 11, 0) {
824 MTLLanguageVersion::V2_0
825 } else if Self::version_at_least(major, minor, 10, 0) {
826 MTLLanguageVersion::V1_2
827 } else if Self::version_at_least(major, minor, 9, 0) {
828 MTLLanguageVersion::V1_1
829 } else {
830 MTLLanguageVersion::V1_0
831 },
832 exposed_queues: 1,
833 read_write_texture_tier: device.read_write_texture_support(),
834 expose_line_mode: true,
835 resource_heaps: Self::supports_any(&device, RESOURCE_HEAP_SUPPORT),
836 argument_buffers: experiments.argument_buffers
837 && Self::supports_any(&device, ARGUMENT_BUFFER_SUPPORT),
838 shared_textures: !os_is_mac,
839 mutable_comparison_samplers: Self::supports_any(
840 &device,
841 MUTABLE_COMPARISON_SAMPLER_SUPPORT,
842 ),
843 sampler_clamp_to_border: Self::supports_any(&device, SAMPLER_CLAMP_TO_BORDER_SUPPORT),
844 base_instance: Self::supports_any(&device, BASE_INSTANCE_SUPPORT),
845 base_vertex_instance_drawing: Self::supports_any(&device, BASE_VERTEX_INSTANCE_SUPPORT),
846 dual_source_blending: Self::supports_any(&device, DUAL_SOURCE_BLEND_SUPPORT),
847 low_power: !os_is_mac || device.is_low_power(),
848 headless: os_is_mac && device.is_headless(),
849 layered_rendering: Self::supports_any(&device, LAYERED_RENDERING_SUPPORT),
850 function_specialization: Self::supports_any(&device, FUNCTION_SPECIALIZATION_SUPPORT),
851 depth_clip_mode: Self::supports_any(&device, DEPTH_CLIP_MODE),
852 texture_cube_array: Self::supports_any(&device, TEXTURE_CUBE_ARRAY_SUPPORT),
853 format_depth24_stencil8: os_is_mac && device.d24_s8_supported(),
854 format_depth32_stencil8_filter: os_is_mac,
855 format_depth32_stencil8_none: !os_is_mac,
856 format_min_srgb_channels: if os_is_mac { 4 } else { 1 },
857 format_b5: !os_is_mac,
858 format_bc: os_is_mac,
859 format_eac_etc: !os_is_mac,
860 format_astc: Self::supports_any(&device, ASTC_PIXEL_FORMAT_FEATURES),
861 format_any8_unorm_srgb_all: Self::supports_any(&device, ANY8_UNORM_SRGB_ALL),
862 format_any8_unorm_srgb_no_write: !Self::supports_any(&device, ANY8_UNORM_SRGB_ALL)
863 && !os_is_mac,
864 format_any8_snorm_all: Self::supports_any(&device, ANY8_SNORM_RESOLVE),
865 format_r16_norm_all: os_is_mac,
866 format_r32_all: !Self::supports_any(
867 &device,
868 &[
869 MTLFeatureSet::iOS_GPUFamily1_v1,
870 MTLFeatureSet::iOS_GPUFamily2_v1,
871 ],
872 ),
873 format_r32_no_write: Self::supports_any(
874 &device,
875 &[
876 MTLFeatureSet::iOS_GPUFamily1_v1,
877 MTLFeatureSet::iOS_GPUFamily2_v1,
878 ],
879 ),
880 format_r32float_no_write_no_filter: Self::supports_any(
881 &device,
882 &[
883 MTLFeatureSet::iOS_GPUFamily1_v1,
884 MTLFeatureSet::iOS_GPUFamily2_v1,
885 ],
886 ) && !os_is_mac,
887 format_r32float_no_filter: !Self::supports_any(
888 &device,
889 &[
890 MTLFeatureSet::iOS_GPUFamily1_v1,
891 MTLFeatureSet::iOS_GPUFamily2_v1,
892 ],
893 ) && !os_is_mac,
894 format_r32float_all: os_is_mac,
895 format_rgba8_srgb_all: Self::supports_any(&device, RGBA8_SRGB),
896 format_rgba8_srgb_no_write: !Self::supports_any(&device, RGBA8_SRGB),
897 format_rgb10a2_unorm_all: Self::supports_any(&device, RGB10A2UNORM_ALL),
898 format_rgb10a2_unorm_no_write: !Self::supports_any(&device, RGB10A2UNORM_ALL),
899 format_rgb10a2_uint_color: !Self::supports_any(&device, RGB10A2UINT_COLOR_WRITE),
900 format_rgb10a2_uint_color_write: Self::supports_any(&device, RGB10A2UINT_COLOR_WRITE),
901 format_rg11b10_all: Self::supports_any(&device, RG11B10FLOAT_ALL),
902 format_rg11b10_no_write: !Self::supports_any(&device, RG11B10FLOAT_ALL),
903 format_rgb9e5_all: Self::supports_any(&device, RGB9E5FLOAT_ALL),
904 format_rgb9e5_no_write: !Self::supports_any(&device, RGB9E5FLOAT_ALL) && !os_is_mac,
905 format_rgb9e5_filter_only: os_is_mac,
906 format_rg32_color: Self::supports_any(
907 &device,
908 &[
909 MTLFeatureSet::iOS_GPUFamily1_v1,
910 MTLFeatureSet::iOS_GPUFamily2_v1,
911 ],
912 ),
913 format_rg32_color_write: !Self::supports_any(
914 &device,
915 &[
916 MTLFeatureSet::iOS_GPUFamily1_v1,
917 MTLFeatureSet::iOS_GPUFamily2_v1,
918 ],
919 ),
920 format_rg32float_all: os_is_mac,
921 format_rg32float_color_blend: Self::supports_any(
922 &device,
923 &[
924 MTLFeatureSet::iOS_GPUFamily1_v1,
925 MTLFeatureSet::iOS_GPUFamily2_v1,
926 ],
927 ),
928 format_rg32float_no_filter: !os_is_mac
929 && !Self::supports_any(
930 &device,
931 &[
932 MTLFeatureSet::iOS_GPUFamily1_v1,
933 MTLFeatureSet::iOS_GPUFamily2_v1,
934 ],
935 ),
936 format_rgba32int_color: Self::supports_any(
937 &device,
938 &[
939 MTLFeatureSet::iOS_GPUFamily1_v1,
940 MTLFeatureSet::iOS_GPUFamily2_v1,
941 ],
942 ),
943 format_rgba32int_color_write: !Self::supports_any(
944 &device,
945 &[
946 MTLFeatureSet::iOS_GPUFamily1_v1,
947 MTLFeatureSet::iOS_GPUFamily2_v1,
948 ],
949 ),
950 format_rgba32float_color: Self::supports_any(
951 &device,
952 &[
953 MTLFeatureSet::iOS_GPUFamily1_v1,
954 MTLFeatureSet::iOS_GPUFamily2_v1,
955 ],
956 ),
957 format_rgba32float_color_write: !Self::supports_any(
958 &device,
959 &[
960 MTLFeatureSet::iOS_GPUFamily1_v1,
961 MTLFeatureSet::iOS_GPUFamily2_v1,
962 ],
963 ) && !os_is_mac,
964 format_rgba32float_all: os_is_mac,
965 format_depth16unorm: device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v2),
966 format_depth32float_filter: device
967 .supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v1),
968 format_depth32float_none: !device
969 .supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v1),
970 format_bgr10a2_all: Self::supports_any(&device, BGR10A2_ALL),
971 format_bgr10a2_no_write: !device
972 .supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v3),
973 max_buffers_per_stage: 31,
974 max_textures_per_stage: if os_is_mac { 128 } else { 31 },
975 max_samplers_per_stage: 16,
976 buffer_alignment: if os_is_mac { 256 } else { 64 },
977 max_buffer_size: if device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v2) {
978 1 << 30 // 1GB on macOS 1.2 and up
979 } else {
980 1 << 28 // 256MB otherwise
981 },
982 max_texture_size: if Self::supports_any(
983 &device,
984 &[
985 MTLFeatureSet::iOS_GPUFamily3_v1,
986 MTLFeatureSet::tvOS_GPUFamily2_v1,
987 MTLFeatureSet::macOS_GPUFamily1_v1,
988 ],
989 ) {
990 16384
991 } else if Self::supports_any(
992 &device,
993 &[
994 MTLFeatureSet::iOS_GPUFamily1_v2,
995 MTLFeatureSet::iOS_GPUFamily2_v2,
996 MTLFeatureSet::tvOS_GPUFamily1_v1,
997 ],
998 ) {
999 8192
1000 } else {
1001 4096
1002 },
1003 max_texture_3d_size: 2048,
1004 max_texture_layers: 2048,
1005 max_fragment_input_components: if os_is_mac { 128 } else { 60 },
1006 max_color_render_targets: if Self::supports_any(
1007 &device,
1008 &[
1009 MTLFeatureSet::iOS_GPUFamily2_v1,
1010 MTLFeatureSet::iOS_GPUFamily3_v1,
1011 MTLFeatureSet::iOS_GPUFamily4_v1,
1012 MTLFeatureSet::iOS_GPUFamily5_v1,
1013 MTLFeatureSet::tvOS_GPUFamily1_v1,
1014 MTLFeatureSet::tvOS_GPUFamily2_v1,
1015 MTLFeatureSet::macOS_GPUFamily1_v1,
1016 MTLFeatureSet::macOS_GPUFamily2_v1,
1017 ],
1018 ) {
1019 8
1020 } else {
1021 4
1022 },
1023 max_total_threadgroup_memory: if Self::supports_any(
1024 &device,
1025 &[
1026 MTLFeatureSet::iOS_GPUFamily4_v2,
1027 MTLFeatureSet::iOS_GPUFamily5_v1,
1028 ],
1029 ) {
1030 64 << 10
1031 } else if Self::supports_any(
1032 &device,
1033 &[
1034 MTLFeatureSet::iOS_GPUFamily4_v1,
1035 MTLFeatureSet::macOS_GPUFamily1_v2,
1036 MTLFeatureSet::macOS_GPUFamily2_v1,
1037 ],
1038 ) {
1039 32 << 10
1040 } else {
1041 16 << 10
1042 },
1043 sample_count_mask,
1044 supports_debug_markers: Self::supports_any(
1045 &device,
1046 &[
1047 MTLFeatureSet::macOS_GPUFamily1_v2,
1048 MTLFeatureSet::macOS_GPUFamily2_v1,
1049 MTLFeatureSet::iOS_GPUFamily1_v3,
1050 MTLFeatureSet::iOS_GPUFamily2_v3,
1051 MTLFeatureSet::iOS_GPUFamily3_v2,
1052 MTLFeatureSet::iOS_GPUFamily4_v1,
1053 MTLFeatureSet::iOS_GPUFamily5_v1,
1054 MTLFeatureSet::tvOS_GPUFamily1_v2,
1055 MTLFeatureSet::tvOS_GPUFamily2_v1,
1056 ],
1057 ),
1058 supports_binary_archives: cfg!(feature = "pipeline-cache")
1059 && (device.supports_family(MTLGPUFamily::Apple3)
1060 || device.supports_family(MTLGPUFamily::Mac1)),
1061 }
1062 }
1063
has_version_at_least(&self, needed_major: u32, needed_minor: u32) -> bool1064 fn has_version_at_least(&self, needed_major: u32, needed_minor: u32) -> bool {
1065 let (major, minor) = self.os_version;
1066 Self::version_at_least(major, minor, needed_major, needed_minor)
1067 }
1068 }
1069
1070 #[derive(Clone, Copy, Debug)]
1071 struct PrivateDisabilities {
1072 /// Near depth is not respected properly on some Intel GPUs.
1073 broken_viewport_near_depth: bool,
1074 /// Multi-target clears don't appear to work properly on Intel GPUs.
1075 broken_layered_clear_image: bool,
1076 }
1077
1078 trait AsNative {
1079 type Native;
from(native: &Self::Native) -> Self1080 fn from(native: &Self::Native) -> Self;
as_native(&self) -> &Self::Native1081 fn as_native(&self) -> &Self::Native;
1082 }
1083
1084 pub type BufferPtr = NonNull<metal::MTLBuffer>;
1085 pub type TexturePtr = NonNull<metal::MTLTexture>;
1086 pub type SamplerPtr = NonNull<metal::MTLSamplerState>;
1087 pub type ResourcePtr = NonNull<metal::MTLResource>;
1088
1089 //TODO: make this a generic struct with a single generic implementation
1090
1091 impl AsNative for BufferPtr {
1092 type Native = metal::BufferRef;
1093 #[inline]
from(native: &metal::BufferRef) -> Self1094 fn from(native: &metal::BufferRef) -> Self {
1095 unsafe { NonNull::new_unchecked(native.as_ptr()) }
1096 }
1097 #[inline]
as_native(&self) -> &metal::BufferRef1098 fn as_native(&self) -> &metal::BufferRef {
1099 unsafe { metal::BufferRef::from_ptr(self.as_ptr()) }
1100 }
1101 }
1102
1103 impl AsNative for TexturePtr {
1104 type Native = metal::TextureRef;
1105 #[inline]
from(native: &metal::TextureRef) -> Self1106 fn from(native: &metal::TextureRef) -> Self {
1107 unsafe { NonNull::new_unchecked(native.as_ptr()) }
1108 }
1109 #[inline]
as_native(&self) -> &metal::TextureRef1110 fn as_native(&self) -> &metal::TextureRef {
1111 unsafe { metal::TextureRef::from_ptr(self.as_ptr()) }
1112 }
1113 }
1114
1115 impl AsNative for SamplerPtr {
1116 type Native = metal::SamplerStateRef;
1117 #[inline]
from(native: &metal::SamplerStateRef) -> Self1118 fn from(native: &metal::SamplerStateRef) -> Self {
1119 unsafe { NonNull::new_unchecked(native.as_ptr()) }
1120 }
1121 #[inline]
as_native(&self) -> &metal::SamplerStateRef1122 fn as_native(&self) -> &metal::SamplerStateRef {
1123 unsafe { metal::SamplerStateRef::from_ptr(self.as_ptr()) }
1124 }
1125 }
1126
1127 impl AsNative for ResourcePtr {
1128 type Native = metal::ResourceRef;
1129 #[inline]
from(native: &metal::ResourceRef) -> Self1130 fn from(native: &metal::ResourceRef) -> Self {
1131 unsafe { NonNull::new_unchecked(native.as_ptr()) }
1132 }
1133 #[inline]
as_native(&self) -> &metal::ResourceRef1134 fn as_native(&self) -> &metal::ResourceRef {
1135 unsafe { metal::ResourceRef::from_ptr(self.as_ptr()) }
1136 }
1137 }
1138