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::NSInteger;
68 use core_graphics::base::CGFloat;
69 use core_graphics::geometry::CGRect;
70 #[cfg(feature = "dispatch")]
71 use dispatch;
72 use foreign_types::ForeignTypeRef;
73 use lazy_static::lazy_static;
74 use metal::MTLFeatureSet;
75 use metal::MTLLanguageVersion;
76 use objc::{
77     declare::ClassDecl,
78     runtime::{Class, Object, Sel, BOOL, YES},
79 };
80 use parking_lot::{Condvar, Mutex};
81 
82 use std::mem;
83 use std::os::raw::c_void;
84 use std::ptr::NonNull;
85 use std::sync::Arc;
86 
87 mod command;
88 mod conversions;
89 mod device;
90 mod internal;
91 mod native;
92 mod soft;
93 mod window;
94 
95 pub use crate::command::CommandPool;
96 pub use crate::device::{Device, LanguageVersion, PhysicalDevice};
97 pub use crate::window::{AcquireMode, CAMetalLayer, Surface, Swapchain};
98 
99 pub type GraphicsCommandPool = CommandPool;
100 
101 //TODO: investigate why exactly using `u8` here is slower (~5% total).
102 /// A type representing Metal binding's resource index.
103 type ResourceIndex = u32;
104 
105 /// Method of recording one-time-submit command buffers.
106 #[derive(Clone, Debug, Hash, PartialEq)]
107 pub enum OnlineRecording {
108     /// Record natively on-the-fly.
109     Immediate,
110     /// Store commands and only start recording at submission time.
111     Deferred,
112     #[cfg(feature = "dispatch")]
113     /// Start recording asynchronously upon finishing each pass.
114     Remote(dispatch::QueuePriority),
115 }
116 
117 impl Default for OnlineRecording {
default() -> Self118     fn default() -> Self {
119         OnlineRecording::Immediate
120     }
121 }
122 
123 const MAX_ACTIVE_COMMAND_BUFFERS: usize = 1 << 14;
124 const MAX_VISIBILITY_QUERIES: usize = 1 << 14;
125 const MAX_COLOR_ATTACHMENTS: usize = 4;
126 const MAX_BOUND_DESCRIPTOR_SETS: usize = 8;
127 
128 #[derive(Debug, Clone, Copy)]
129 pub struct QueueFamily {}
130 
131 impl hal::queue::QueueFamily for QueueFamily {
queue_type(&self) -> QueueType132     fn queue_type(&self) -> QueueType {
133         QueueType::General
134     }
max_queues(&self) -> usize135     fn max_queues(&self) -> usize {
136         1
137     }
id(&self) -> QueueFamilyId138     fn id(&self) -> QueueFamilyId {
139         QueueFamilyId(0)
140     }
141 }
142 
143 #[derive(Debug)]
144 struct VisibilityShared {
145     /// Availability buffer is in shared memory, it has N double words for
146     /// query results followed by N words for the availability.
147     buffer: metal::Buffer,
148     allocator: Mutex<RangeAllocator<hal::query::Id>>,
149     availability_offset: hal::buffer::Offset,
150     condvar: Condvar,
151 }
152 
153 #[derive(Debug)]
154 struct Shared {
155     device: Mutex<metal::Device>,
156     queue: Mutex<command::QueueInner>,
157     queue_blocker: Mutex<command::QueueBlocker>,
158     service_pipes: internal::ServicePipes,
159     disabilities: PrivateDisabilities,
160     private_caps: PrivateCapabilities,
161     visibility: VisibilityShared,
162 }
163 
164 unsafe impl Send for Shared {}
165 unsafe impl Sync for Shared {}
166 
167 impl Shared {
new(device: metal::Device, experiments: &Experiments) -> Self168     fn new(device: metal::Device, experiments: &Experiments) -> Self {
169         let private_caps = PrivateCapabilities::new(&device, experiments);
170 
171         let visibility = VisibilityShared {
172             buffer: device.new_buffer(
173                 MAX_VISIBILITY_QUERIES as u64
174                     * (mem::size_of::<u64>() + mem::size_of::<u32>()) as u64,
175                 metal::MTLResourceOptions::StorageModeShared,
176             ),
177             allocator: Mutex::new(RangeAllocator::new(
178                 0 .. MAX_VISIBILITY_QUERIES as hal::query::Id,
179             )),
180             availability_offset: (MAX_VISIBILITY_QUERIES * mem::size_of::<u64>())
181                 as hal::buffer::Offset,
182             condvar: Condvar::new(),
183         };
184         Shared {
185             queue: Mutex::new(command::QueueInner::new(
186                 &device,
187                 Some(MAX_ACTIVE_COMMAND_BUFFERS),
188             )),
189             queue_blocker: Mutex::new(command::QueueBlocker::default()),
190             service_pipes: internal::ServicePipes::new(&device),
191             disabilities: PrivateDisabilities {
192                 broken_viewport_near_depth: device.name().starts_with("Intel")
193                     && !device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v4),
194                 broken_layered_clear_image: device.name().starts_with("Intel"),
195             },
196             private_caps,
197             device: Mutex::new(device),
198             visibility,
199         }
200     }
201 }
202 
203 #[derive(Clone, Debug, Default)]
204 pub struct Experiments {
205     pub argument_buffers: bool,
206 }
207 
208 #[derive(Debug)]
209 pub struct Instance {
210     pub experiments: Experiments,
211     gfx_managed_metal_layer_delegate: GfxManagedMetalLayerDelegate,
212 }
213 
214 impl hal::Instance<Backend> for Instance {
create(_: &str, _: u32) -> Result<Self, hal::UnsupportedBackend>215     fn create(_: &str, _: u32) -> Result<Self, hal::UnsupportedBackend> {
216         Ok(Instance {
217             experiments: Experiments::default(),
218             gfx_managed_metal_layer_delegate: GfxManagedMetalLayerDelegate::new(),
219         })
220     }
221 
enumerate_adapters(&self) -> Vec<Adapter<Backend>>222     fn enumerate_adapters(&self) -> Vec<Adapter<Backend>> {
223         let devices = metal::Device::all();
224         let mut adapters: Vec<Adapter<Backend>> = devices
225             .into_iter()
226             .map(|dev| {
227                 let name = dev.name().into();
228                 let shared = Shared::new(dev, &self.experiments);
229                 let physical_device = device::PhysicalDevice::new(Arc::new(shared));
230                 Adapter {
231                     info: AdapterInfo {
232                         name,
233                         vendor: 0,
234                         device: 0,
235                         device_type: if physical_device.shared.private_caps.low_power {
236                             DeviceType::IntegratedGpu
237                         } else {
238                             DeviceType::DiscreteGpu
239                         },
240                     },
241                     physical_device,
242                     queue_families: vec![QueueFamily {}],
243                 }
244             })
245             .collect();
246         adapters.sort_by_key(|adapt| {
247             (
248                 adapt.physical_device.shared.private_caps.low_power,
249                 adapt.physical_device.shared.private_caps.headless,
250             )
251         });
252         adapters
253     }
254 
create_surface( &self, has_handle: &impl raw_window_handle::HasRawWindowHandle, ) -> Result<Surface, hal::window::InitError>255     unsafe fn create_surface(
256         &self,
257         has_handle: &impl raw_window_handle::HasRawWindowHandle,
258     ) -> Result<Surface, hal::window::InitError> {
259         match has_handle.raw_window_handle() {
260             #[cfg(target_os = "ios")]
261             raw_window_handle::RawWindowHandle::IOS(handle) => {
262                 Ok(self.create_surface_from_uiview(handle.ui_view, false))
263             }
264             #[cfg(target_os = "macos")]
265             raw_window_handle::RawWindowHandle::MacOS(handle) => {
266                 Ok(self.create_surface_from_nsview(handle.ns_view, false))
267             }
268             _ => Err(hal::window::InitError::UnsupportedWindowHandle),
269         }
270     }
271 
destroy_surface(&self, _surface: Surface)272     unsafe fn destroy_surface(&self, _surface: Surface) {
273         // TODO: Implement Surface cleanup
274     }
275 }
276 
277 lazy_static! {
278     static ref GFX_MANAGED_METAL_LAYER_DELEGATE_CLASS: &'static Class = unsafe {
279         let mut decl = ClassDecl::new("GfxManagedMetalLayerDelegate", class!(NSObject)).unwrap();
280         decl.add_method(
281             sel!(layer:shouldInheritContentsScale:fromWindow:),
282             layer_should_inherit_contents_scale_from_window
283                 as extern "C" fn(&Object, Sel, *mut Object, CGFloat, *mut Object) -> BOOL,
284         );
285         decl.register()
286     };
287 }
288 
layer_should_inherit_contents_scale_from_window( _: &Object, _: Sel, _layer: *mut Object, _new_scale: CGFloat, _from_window: *mut Object, ) -> BOOL289 extern "C" fn layer_should_inherit_contents_scale_from_window(
290     _: &Object,
291     _: Sel,
292     _layer: *mut Object,
293     _new_scale: CGFloat,
294     _from_window: *mut Object,
295 ) -> BOOL {
296     return YES;
297 }
298 
299 #[derive(Debug)]
300 struct GfxManagedMetalLayerDelegate(*mut Object);
301 
302 impl GfxManagedMetalLayerDelegate {
new() -> Self303     pub fn new() -> Self {
304         unsafe {
305             let mut delegate: *mut Object =
306                 msg_send![*GFX_MANAGED_METAL_LAYER_DELEGATE_CLASS, alloc];
307             delegate = msg_send![delegate, init];
308             Self(delegate)
309         }
310     }
311 }
312 
313 impl Drop for GfxManagedMetalLayerDelegate {
drop(&mut self)314     fn drop(&mut self) {
315         unsafe {
316             let () = msg_send![self.0, release];
317         }
318     }
319 }
320 
321 unsafe impl Send for GfxManagedMetalLayerDelegate {}
322 unsafe impl Sync for GfxManagedMetalLayerDelegate {}
323 
324 impl Instance {
325     #[cfg(target_os = "ios")]
create_from_uiview(&self, uiview: *mut c_void) -> window::SurfaceInner326     unsafe fn create_from_uiview(&self, uiview: *mut c_void) -> window::SurfaceInner {
327         let view: cocoa::base::id = mem::transmute(uiview);
328         if view.is_null() {
329             panic!("window does not have a valid contentView");
330         }
331 
332         let main_layer: CAMetalLayer = msg_send![view, layer];
333         let class = class!(CAMetalLayer);
334         let is_valid_layer: BOOL = msg_send![main_layer, isKindOfClass: class];
335         let render_layer = if is_valid_layer == YES {
336             main_layer
337         } else {
338             // If the main layer is not a CAMetalLayer, we create a CAMetalLayer sublayer and use it instead.
339             // Unlike on macOS, we cannot replace the main view as UIView does not allow it (when NSView does).
340             let new_layer: CAMetalLayer = msg_send![class, new];
341 
342             let bounds: CGRect = msg_send![main_layer, bounds];
343             let () = msg_send![new_layer, setFrame: bounds];
344 
345             let () = msg_send![main_layer, addSublayer: new_layer];
346             new_layer
347         };
348 
349         let window: cocoa::base::id = msg_send![view, window];
350         if !window.is_null() {
351             let screen: cocoa::base::id = msg_send![window, screen];
352             assert!(!screen.is_null(), "window is not attached to a screen");
353 
354             let scale_factor: CGFloat = msg_send![screen, nativeScale];
355             let () = msg_send![view, setContentScaleFactor: scale_factor];
356         }
357 
358         let _: *mut c_void = msg_send![view, retain];
359         window::SurfaceInner::new(NonNull::new(view), render_layer)
360     }
361 
362     #[cfg(target_os = "macos")]
create_from_nsview(&self, nsview: *mut c_void) -> window::SurfaceInner363     unsafe fn create_from_nsview(&self, nsview: *mut c_void) -> window::SurfaceInner {
364         let view: cocoa::base::id = mem::transmute(nsview);
365         if view.is_null() {
366             panic!("window does not have a valid contentView");
367         }
368 
369         let existing: CAMetalLayer = msg_send![view, layer];
370         let class = class!(CAMetalLayer);
371         // Deprecated! Clients should use `create_surface_from_layer` instead.
372         let is_actually_layer: BOOL = msg_send![view, isKindOfClass: class];
373         if is_actually_layer == YES {
374             return self.create_from_layer(view);
375         }
376 
377         let use_current = if existing.is_null() {
378             false
379         } else {
380             let result: BOOL = msg_send![existing, isKindOfClass: class];
381             result == YES
382         };
383 
384         let render_layer: CAMetalLayer = if use_current {
385             existing
386         } else {
387             let layer: CAMetalLayer = msg_send![class, new];
388             let () = msg_send![view, setLayer: layer];
389             let () = msg_send![view, setWantsLayer: YES];
390             let bounds: CGRect = msg_send![view, bounds];
391             let () = msg_send![layer, setBounds: bounds];
392 
393             let window: cocoa::base::id = msg_send![view, window];
394             if !window.is_null() {
395                 let scale_factor: CGFloat = msg_send![window, backingScaleFactor];
396                 let () = msg_send![layer, setContentsScale: scale_factor];
397             }
398             let () = msg_send![layer, setDelegate: self.gfx_managed_metal_layer_delegate.0];
399             layer
400         };
401 
402         let _: *mut c_void = msg_send![view, retain];
403         window::SurfaceInner::new(NonNull::new(view), render_layer)
404     }
405 
create_from_layer(&self, layer: CAMetalLayer) -> window::SurfaceInner406     unsafe fn create_from_layer(&self, layer: CAMetalLayer) -> window::SurfaceInner {
407         let class = class!(CAMetalLayer);
408         let proper_kind: BOOL = msg_send![layer, isKindOfClass: class];
409         assert_eq!(proper_kind, YES);
410         let _: *mut c_void = msg_send![layer, retain];
411         window::SurfaceInner::new(None, layer)
412     }
413 
create_surface_from_layer( &self, layer: CAMetalLayer, enable_signposts: bool, ) -> Surface414     pub fn create_surface_from_layer(
415         &self,
416         layer: CAMetalLayer,
417         enable_signposts: bool,
418     ) -> Surface {
419         unsafe { self.create_from_layer(layer) }.into_surface(enable_signposts)
420     }
421 
422     #[cfg(target_os = "macos")]
create_surface_from_nsview( &self, nsview: *mut c_void, enable_signposts: bool, ) -> Surface423     pub fn create_surface_from_nsview(
424         &self,
425         nsview: *mut c_void,
426         enable_signposts: bool,
427     ) -> Surface {
428         unsafe { self.create_from_nsview(nsview) }.into_surface(enable_signposts)
429     }
430 
431     #[cfg(target_os = "ios")]
create_surface_from_uiview( &self, uiview: *mut c_void, enable_signposts: bool, ) -> Surface432     pub fn create_surface_from_uiview(
433         &self,
434         uiview: *mut c_void,
435         enable_signposts: bool,
436     ) -> Surface {
437         unsafe { self.create_from_uiview(uiview) }.into_surface(enable_signposts)
438     }
439 }
440 
441 #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
442 pub enum Backend {}
443 impl hal::Backend for Backend {
444     type Instance = Instance;
445     type PhysicalDevice = device::PhysicalDevice;
446     type Device = device::Device;
447 
448     type Surface = window::Surface;
449     type Swapchain = window::Swapchain;
450 
451     type QueueFamily = QueueFamily;
452     type CommandQueue = command::CommandQueue;
453     type CommandBuffer = command::CommandBuffer;
454 
455     type Memory = native::Memory;
456     type CommandPool = command::CommandPool;
457 
458     type ShaderModule = native::ShaderModule;
459     type RenderPass = native::RenderPass;
460     type Framebuffer = native::Framebuffer;
461 
462     type Buffer = native::Buffer;
463     type BufferView = native::BufferView;
464     type Image = native::Image;
465     type ImageView = native::ImageView;
466     type Sampler = native::Sampler;
467 
468     type ComputePipeline = native::ComputePipeline;
469     type GraphicsPipeline = native::GraphicsPipeline;
470     type PipelineCache = native::PipelineCache;
471     type PipelineLayout = native::PipelineLayout;
472     type DescriptorSetLayout = native::DescriptorSetLayout;
473     type DescriptorPool = native::DescriptorPool;
474     type DescriptorSet = native::DescriptorSet;
475 
476     type Fence = native::Fence;
477     type Semaphore = native::Semaphore;
478     type Event = native::Event;
479     type QueryPool = native::QueryPool;
480 }
481 
482 const RESOURCE_HEAP_SUPPORT: &[MTLFeatureSet] = &[
483     MTLFeatureSet::iOS_GPUFamily1_v3,
484     MTLFeatureSet::iOS_GPUFamily2_v3,
485     MTLFeatureSet::iOS_GPUFamily3_v2,
486     MTLFeatureSet::tvOS_GPUFamily1_v2,
487 ];
488 
489 const ARGUMENT_BUFFER_SUPPORT: &[MTLFeatureSet] = &[
490     MTLFeatureSet::iOS_GPUFamily1_v4,
491     MTLFeatureSet::tvOS_GPUFamily1_v3,
492     MTLFeatureSet::macOS_GPUFamily1_v3,
493 ];
494 
495 const MUTABLE_COMPARISON_SAMPLER_SUPPORT: &[MTLFeatureSet] = &[
496     MTLFeatureSet::macOS_GPUFamily1_v1,
497     MTLFeatureSet::iOS_GPUFamily3_v1,
498 ];
499 
500 const ASTC_PIXEL_FORMAT_FEATURES: &[MTLFeatureSet] = &[
501     MTLFeatureSet::iOS_GPUFamily2_v1,
502     MTLFeatureSet::iOS_GPUFamily2_v2,
503     MTLFeatureSet::iOS_GPUFamily3_v1,
504     MTLFeatureSet::iOS_GPUFamily2_v3,
505     MTLFeatureSet::iOS_GPUFamily3_v2,
506     MTLFeatureSet::iOS_GPUFamily2_v4,
507     MTLFeatureSet::iOS_GPUFamily3_v3,
508     MTLFeatureSet::iOS_GPUFamily4_v1,
509     MTLFeatureSet::tvOS_GPUFamily1_v1,
510     MTLFeatureSet::tvOS_GPUFamily1_v2,
511     MTLFeatureSet::tvOS_GPUFamily1_v3,
512     MTLFeatureSet::tvOS_GPUFamily2_v1,
513 ];
514 
515 const R8UNORM_SRGB_ALL: &[MTLFeatureSet] = &[
516     MTLFeatureSet::iOS_GPUFamily3_v1,
517     MTLFeatureSet::iOS_GPUFamily2_v3,
518     MTLFeatureSet::iOS_GPUFamily3_v2,
519     MTLFeatureSet::iOS_GPUFamily2_v4,
520     MTLFeatureSet::iOS_GPUFamily3_v3,
521     MTLFeatureSet::iOS_GPUFamily4_v1,
522     MTLFeatureSet::tvOS_GPUFamily1_v2,
523     MTLFeatureSet::tvOS_GPUFamily1_v3,
524     MTLFeatureSet::tvOS_GPUFamily2_v1,
525 ];
526 
527 const R8SNORM_NO_RESOLVE: &[MTLFeatureSet] = &[
528     MTLFeatureSet::iOS_GPUFamily1_v1,
529     MTLFeatureSet::iOS_GPUFamily1_v2,
530     MTLFeatureSet::iOS_GPUFamily1_v3,
531     MTLFeatureSet::iOS_GPUFamily1_v4,
532 ];
533 
534 const RG8UNORM_SRGB_NO_WRITE: &[MTLFeatureSet] = &[
535     MTLFeatureSet::iOS_GPUFamily1_v1,
536     MTLFeatureSet::iOS_GPUFamily2_v1,
537     MTLFeatureSet::iOS_GPUFamily1_v2,
538     MTLFeatureSet::iOS_GPUFamily2_v2,
539     MTLFeatureSet::iOS_GPUFamily1_v3,
540     MTLFeatureSet::iOS_GPUFamily1_v4,
541     MTLFeatureSet::tvOS_GPUFamily1_v1,
542 ];
543 
544 const RG8SNORM_NO_RESOLVE: &[MTLFeatureSet] = &[
545     MTLFeatureSet::iOS_GPUFamily1_v1,
546     MTLFeatureSet::iOS_GPUFamily1_v2,
547     MTLFeatureSet::iOS_GPUFamily1_v3,
548     MTLFeatureSet::iOS_GPUFamily1_v4,
549 ];
550 
551 const RGBA8_SRGB: &[MTLFeatureSet] = &[
552     MTLFeatureSet::iOS_GPUFamily3_v1,
553     MTLFeatureSet::iOS_GPUFamily2_v3,
554     MTLFeatureSet::iOS_GPUFamily3_v2,
555     MTLFeatureSet::iOS_GPUFamily2_v4,
556     MTLFeatureSet::iOS_GPUFamily3_v3,
557     MTLFeatureSet::iOS_GPUFamily4_v1,
558     MTLFeatureSet::tvOS_GPUFamily1_v2,
559     MTLFeatureSet::tvOS_GPUFamily1_v3,
560     MTLFeatureSet::tvOS_GPUFamily2_v1,
561 ];
562 
563 const RGB10A2UNORM_ALL: &[MTLFeatureSet] = &[
564     MTLFeatureSet::iOS_GPUFamily3_v1,
565     MTLFeatureSet::iOS_GPUFamily3_v2,
566     MTLFeatureSet::iOS_GPUFamily3_v3,
567     MTLFeatureSet::iOS_GPUFamily4_v1,
568     MTLFeatureSet::tvOS_GPUFamily2_v1,
569     MTLFeatureSet::macOS_GPUFamily1_v1,
570     MTLFeatureSet::macOS_GPUFamily1_v2,
571     MTLFeatureSet::macOS_GPUFamily1_v3,
572 ];
573 
574 const RGB10A2UINT_COLOR_WRITE: &[MTLFeatureSet] = &[
575     MTLFeatureSet::iOS_GPUFamily3_v1,
576     MTLFeatureSet::iOS_GPUFamily3_v2,
577     MTLFeatureSet::iOS_GPUFamily3_v3,
578     MTLFeatureSet::iOS_GPUFamily4_v1,
579     MTLFeatureSet::tvOS_GPUFamily2_v1,
580     MTLFeatureSet::macOS_GPUFamily1_v1,
581     MTLFeatureSet::macOS_GPUFamily1_v2,
582     MTLFeatureSet::macOS_GPUFamily1_v3,
583 ];
584 
585 const RG11B10FLOAT_ALL: &[MTLFeatureSet] = &[
586     MTLFeatureSet::iOS_GPUFamily3_v1,
587     MTLFeatureSet::iOS_GPUFamily3_v2,
588     MTLFeatureSet::iOS_GPUFamily3_v3,
589     MTLFeatureSet::iOS_GPUFamily4_v1,
590     MTLFeatureSet::tvOS_GPUFamily2_v1,
591     MTLFeatureSet::macOS_GPUFamily1_v1,
592     MTLFeatureSet::macOS_GPUFamily1_v2,
593     MTLFeatureSet::macOS_GPUFamily1_v3,
594 ];
595 
596 const RGB9E5FLOAT_ALL: &[MTLFeatureSet] = &[
597     MTLFeatureSet::iOS_GPUFamily3_v1,
598     MTLFeatureSet::iOS_GPUFamily3_v2,
599     MTLFeatureSet::iOS_GPUFamily3_v3,
600     MTLFeatureSet::iOS_GPUFamily4_v1,
601     MTLFeatureSet::tvOS_GPUFamily2_v1,
602 ];
603 
604 const BGR10A2_ALL: &[MTLFeatureSet] = &[
605     MTLFeatureSet::iOS_GPUFamily1_v4,
606     MTLFeatureSet::iOS_GPUFamily2_v4,
607     MTLFeatureSet::iOS_GPUFamily3_v3,
608     MTLFeatureSet::iOS_GPUFamily4_v1,
609     MTLFeatureSet::tvOS_GPUFamily1_v3,
610     MTLFeatureSet::tvOS_GPUFamily2_v1,
611 ];
612 
613 const BASE_INSTANCE_SUPPORT: &[MTLFeatureSet] = &[
614     MTLFeatureSet::iOS_GPUFamily1_v4,
615     MTLFeatureSet::iOS_GPUFamily3_v1,
616 ];
617 
618 const DUAL_SOURCE_BLEND_SUPPORT: &[MTLFeatureSet] = &[
619     MTLFeatureSet::iOS_GPUFamily1_v4,
620     MTLFeatureSet::tvOS_GPUFamily1_v3,
621     MTLFeatureSet::macOS_GPUFamily1_v2,
622 ];
623 
624 const LAYERED_RENDERING_SUPPORT: &[MTLFeatureSet] = &[
625     MTLFeatureSet::iOS_GPUFamily5_v1,
626     MTLFeatureSet::macOS_GPUFamily1_v1,
627 ];
628 
629 const FUNCTION_SPECIALIZATION_SUPPORT: &[MTLFeatureSet] = &[
630     MTLFeatureSet::iOS_GPUFamily1_v3,
631     MTLFeatureSet::tvOS_GPUFamily1_v2,
632     MTLFeatureSet::macOS_GPUFamily1_v2,
633 ];
634 
635 const DEPTH_CLIP_MODE: &[MTLFeatureSet] = &[
636     MTLFeatureSet::iOS_GPUFamily4_v1,
637     MTLFeatureSet::tvOS_GPUFamily1_v3,
638     MTLFeatureSet::macOS_GPUFamily1_v1,
639 ];
640 
641 #[derive(Clone, Debug)]
642 struct PrivateCapabilities {
643     pub os_is_mac: bool,
644     os_version: (u32, u32),
645     msl_version: metal::MTLLanguageVersion,
646     exposed_queues: usize,
647     // if TRUE, we'll report `NON_FILL_POLYGON_MODE` feature without the points support
648     expose_line_mode: bool,
649     resource_heaps: bool,
650     argument_buffers: bool,
651     shared_textures: bool,
652     mutable_comparison_samplers: bool,
653     base_instance: bool,
654     base_vertex_instance_drawing: bool,
655     dual_source_blending: bool,
656     low_power: bool,
657     headless: bool,
658     layered_rendering: bool,
659     function_specialization: bool,
660     depth_clip_mode: bool,
661     format_depth24_stencil8: bool,
662     format_depth32_stencil8_filter: bool,
663     format_depth32_stencil8_none: bool,
664     format_min_srgb_channels: u8,
665     format_b5: bool,
666     format_bc: bool,
667     format_eac_etc: bool,
668     format_astc: bool,
669     format_r8unorm_srgb_all: bool,
670     format_r8unorm_srgb_no_write: bool,
671     format_r8snorm_all: bool,
672     format_r16_norm_all: bool,
673     format_rg8unorm_srgb_all: bool,
674     format_rg8unorm_srgb_no_write: bool,
675     format_rg8snorm_all: bool,
676     format_r32_all: bool,
677     format_r32_no_write: bool,
678     format_r32float_no_write_no_filter: bool,
679     format_r32float_no_filter: bool,
680     format_r32float_all: bool,
681     format_rgba8_srgb_all: bool,
682     format_rgba8_srgb_no_write: bool,
683     format_rgb10a2_unorm_all: bool,
684     format_rgb10a2_unorm_no_write: bool,
685     format_rgb10a2_uint_color: bool,
686     format_rgb10a2_uint_color_write: bool,
687     format_rg11b10_all: bool,
688     format_rg11b10_no_write: bool,
689     format_rgb9e5_all: bool,
690     format_rgb9e5_no_write: bool,
691     format_rgb9e5_filter_only: bool,
692     format_rg32_color: bool,
693     format_rg32_color_write: bool,
694     format_rg32float_all: bool,
695     format_rg32float_color_blend: bool,
696     format_rg32float_no_filter: bool,
697     format_rgba32int_color: bool,
698     format_rgba32int_color_write: bool,
699     format_rgba32float_color: bool,
700     format_rgba32float_color_write: bool,
701     format_rgba32float_all: bool,
702     format_depth16unorm: bool,
703     format_depth32float_filter: bool,
704     format_depth32float_none: bool,
705     format_bgr10a2_all: bool,
706     format_bgr10a2_no_write: bool,
707     max_buffers_per_stage: ResourceIndex,
708     max_textures_per_stage: ResourceIndex,
709     max_samplers_per_stage: ResourceIndex,
710     buffer_alignment: u64,
711     max_buffer_size: u64,
712     max_texture_size: u64,
713     max_texture_3d_size: u64,
714     max_texture_layers: u64,
715     max_fragment_input_components: u64,
716     sample_count_mask: u8,
717     supports_debug_markers: bool,
718 }
719 
720 impl PrivateCapabilities {
version_at_least(major: u32, minor: u32, needed_major: u32, needed_minor: u32) -> bool721     fn version_at_least(major: u32, minor: u32, needed_major: u32, needed_minor: u32) -> bool {
722         major > needed_major || (major == needed_major && minor >= needed_minor)
723     }
724 
supports_any(raw: &metal::DeviceRef, features_sets: &[MTLFeatureSet]) -> bool725     fn supports_any(raw: &metal::DeviceRef, features_sets: &[MTLFeatureSet]) -> bool {
726         features_sets
727             .iter()
728             .cloned()
729             .any(|x| raw.supports_feature_set(x))
730     }
731 
new(device: &metal::Device, experiments: &Experiments) -> Self732     fn new(device: &metal::Device, experiments: &Experiments) -> Self {
733         #[repr(C)]
734         #[derive(Clone, Copy, Debug)]
735         struct NSOperatingSystemVersion {
736             major: NSInteger,
737             minor: NSInteger,
738             patch: NSInteger,
739         }
740 
741         let version: NSOperatingSystemVersion = unsafe {
742             let process_info: *mut Object = msg_send![class!(NSProcessInfo), processInfo];
743             msg_send![process_info, operatingSystemVersion]
744         };
745 
746         let major = version.major as u32;
747         let minor = version.minor as u32;
748         let os_is_mac = device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v1);
749 
750         let mut sample_count_mask: u8 = 1 | 4; // 1 and 4 samples are supported on all devices
751         if device.supports_sample_count(2) {
752             sample_count_mask |= 2;
753         }
754         if device.supports_sample_count(8) {
755             sample_count_mask |= 8;
756         }
757 
758         PrivateCapabilities {
759             os_is_mac,
760             os_version: (major as u32, minor as u32),
761             msl_version: if os_is_mac {
762                 if Self::version_at_least(major, minor, 10, 15) {
763                     MTLLanguageVersion::V2_2
764                 } else if Self::version_at_least(major, minor, 10, 14) {
765                     MTLLanguageVersion::V2_1
766                 } else if Self::version_at_least(major, minor, 10, 13) {
767                     MTLLanguageVersion::V2_0
768                 } else if Self::version_at_least(major, minor, 10, 12) {
769                     MTLLanguageVersion::V1_2
770                 } else if Self::version_at_least(major, minor, 10, 11) {
771                     MTLLanguageVersion::V1_1
772                 } else {
773                     MTLLanguageVersion::V1_0
774                 }
775             } else if Self::version_at_least(major, minor, 13, 0) {
776                 MTLLanguageVersion::V2_2
777             } else if Self::version_at_least(major, minor, 12, 0) {
778                 MTLLanguageVersion::V2_1
779             } else if Self::version_at_least(major, minor, 11, 0) {
780                 MTLLanguageVersion::V2_0
781             } else if Self::version_at_least(major, minor, 10, 0) {
782                 MTLLanguageVersion::V1_2
783             } else if Self::version_at_least(major, minor, 9, 0) {
784                 MTLLanguageVersion::V1_1
785             } else {
786                 MTLLanguageVersion::V1_0
787             },
788             exposed_queues: 1,
789             expose_line_mode: true,
790             resource_heaps: Self::supports_any(&device, RESOURCE_HEAP_SUPPORT),
791             argument_buffers: experiments.argument_buffers
792                 && Self::supports_any(&device, ARGUMENT_BUFFER_SUPPORT),
793             shared_textures: !os_is_mac,
794             mutable_comparison_samplers: Self::supports_any(
795                 &device,
796                 MUTABLE_COMPARISON_SAMPLER_SUPPORT,
797             ),
798             base_instance: Self::supports_any(&device, BASE_INSTANCE_SUPPORT),
799             base_vertex_instance_drawing: Self::supports_any(
800                 &device,
801                 &[
802                     MTLFeatureSet::iOS_GPUFamily3_v1,
803                     MTLFeatureSet::iOS_GPUFamily4_v1,
804                     MTLFeatureSet::iOS_GPUFamily5_v1,
805                     MTLFeatureSet::tvOS_GPUFamily2_v1,
806                     MTLFeatureSet::macOS_GPUFamily1_v1,
807                     MTLFeatureSet::macOS_GPUFamily2_v1,
808                 ],
809             ),
810             dual_source_blending: Self::supports_any(&device, DUAL_SOURCE_BLEND_SUPPORT),
811             low_power: !os_is_mac || device.is_low_power(),
812             headless: os_is_mac && device.is_headless(),
813             layered_rendering: Self::supports_any(&device, LAYERED_RENDERING_SUPPORT),
814             function_specialization: Self::supports_any(&device, FUNCTION_SPECIALIZATION_SUPPORT),
815             depth_clip_mode: Self::supports_any(&device, DEPTH_CLIP_MODE),
816             format_depth24_stencil8: os_is_mac && device.d24_s8_supported(),
817             format_depth32_stencil8_filter: os_is_mac,
818             format_depth32_stencil8_none: !os_is_mac,
819             format_min_srgb_channels: if os_is_mac { 4 } else { 1 },
820             format_b5: !os_is_mac,
821             format_bc: os_is_mac,
822             format_eac_etc: !os_is_mac,
823             format_astc: Self::supports_any(&device, ASTC_PIXEL_FORMAT_FEATURES),
824             format_r8unorm_srgb_all: Self::supports_any(&device, R8UNORM_SRGB_ALL),
825             format_r8unorm_srgb_no_write: !Self::supports_any(&device, R8UNORM_SRGB_ALL)
826                 && !os_is_mac,
827             format_r8snorm_all: !Self::supports_any(&device, R8SNORM_NO_RESOLVE),
828             format_r16_norm_all: os_is_mac,
829             format_rg8unorm_srgb_all: Self::supports_any(&device, RG8UNORM_SRGB_NO_WRITE),
830             format_rg8unorm_srgb_no_write: !Self::supports_any(&device, RG8UNORM_SRGB_NO_WRITE)
831                 && !os_is_mac,
832             format_rg8snorm_all: !Self::supports_any(&device, RG8SNORM_NO_RESOLVE),
833             format_r32_all: !Self::supports_any(
834                 &device,
835                 &[
836                     MTLFeatureSet::iOS_GPUFamily1_v1,
837                     MTLFeatureSet::iOS_GPUFamily2_v1,
838                 ],
839             ),
840             format_r32_no_write: Self::supports_any(
841                 &device,
842                 &[
843                     MTLFeatureSet::iOS_GPUFamily1_v1,
844                     MTLFeatureSet::iOS_GPUFamily2_v1,
845                 ],
846             ),
847             format_r32float_no_write_no_filter: Self::supports_any(
848                 &device,
849                 &[
850                     MTLFeatureSet::iOS_GPUFamily1_v1,
851                     MTLFeatureSet::iOS_GPUFamily2_v1,
852                 ],
853             ) && !os_is_mac,
854             format_r32float_no_filter: !Self::supports_any(
855                 &device,
856                 &[
857                     MTLFeatureSet::iOS_GPUFamily1_v1,
858                     MTLFeatureSet::iOS_GPUFamily2_v1,
859                 ],
860             ) && !os_is_mac,
861             format_r32float_all: os_is_mac,
862             format_rgba8_srgb_all: Self::supports_any(&device, RGBA8_SRGB),
863             format_rgba8_srgb_no_write: !Self::supports_any(&device, RGBA8_SRGB),
864             format_rgb10a2_unorm_all: Self::supports_any(&device, RGB10A2UNORM_ALL),
865             format_rgb10a2_unorm_no_write: !Self::supports_any(&device, RGB10A2UNORM_ALL),
866             format_rgb10a2_uint_color: !Self::supports_any(&device, RGB10A2UINT_COLOR_WRITE),
867             format_rgb10a2_uint_color_write: Self::supports_any(&device, RGB10A2UINT_COLOR_WRITE),
868             format_rg11b10_all: Self::supports_any(&device, RG11B10FLOAT_ALL),
869             format_rg11b10_no_write: !Self::supports_any(&device, RG11B10FLOAT_ALL),
870             format_rgb9e5_all: Self::supports_any(&device, RGB9E5FLOAT_ALL),
871             format_rgb9e5_no_write: !Self::supports_any(&device, RGB9E5FLOAT_ALL) && !os_is_mac,
872             format_rgb9e5_filter_only: os_is_mac,
873             format_rg32_color: Self::supports_any(
874                 &device,
875                 &[
876                     MTLFeatureSet::iOS_GPUFamily1_v1,
877                     MTLFeatureSet::iOS_GPUFamily2_v1,
878                 ],
879             ),
880             format_rg32_color_write: !Self::supports_any(
881                 &device,
882                 &[
883                     MTLFeatureSet::iOS_GPUFamily1_v1,
884                     MTLFeatureSet::iOS_GPUFamily2_v1,
885                 ],
886             ),
887             format_rg32float_all: os_is_mac,
888             format_rg32float_color_blend: Self::supports_any(
889                 &device,
890                 &[
891                     MTLFeatureSet::iOS_GPUFamily1_v1,
892                     MTLFeatureSet::iOS_GPUFamily2_v1,
893                 ],
894             ),
895             format_rg32float_no_filter: !os_is_mac
896                 && !Self::supports_any(
897                     &device,
898                     &[
899                         MTLFeatureSet::iOS_GPUFamily1_v1,
900                         MTLFeatureSet::iOS_GPUFamily2_v1,
901                     ],
902                 ),
903             format_rgba32int_color: Self::supports_any(
904                 &device,
905                 &[
906                     MTLFeatureSet::iOS_GPUFamily1_v1,
907                     MTLFeatureSet::iOS_GPUFamily2_v1,
908                 ],
909             ),
910             format_rgba32int_color_write: !Self::supports_any(
911                 &device,
912                 &[
913                     MTLFeatureSet::iOS_GPUFamily1_v1,
914                     MTLFeatureSet::iOS_GPUFamily2_v1,
915                 ],
916             ),
917             format_rgba32float_color: Self::supports_any(
918                 &device,
919                 &[
920                     MTLFeatureSet::iOS_GPUFamily1_v1,
921                     MTLFeatureSet::iOS_GPUFamily2_v1,
922                 ],
923             ),
924             format_rgba32float_color_write: !Self::supports_any(
925                 &device,
926                 &[
927                     MTLFeatureSet::iOS_GPUFamily1_v1,
928                     MTLFeatureSet::iOS_GPUFamily2_v1,
929                 ],
930             ) && !os_is_mac,
931             format_rgba32float_all: os_is_mac,
932             format_depth16unorm: device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v2),
933             format_depth32float_filter: device
934                 .supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v1),
935             format_depth32float_none: !device
936                 .supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v1),
937             format_bgr10a2_all: Self::supports_any(&device, BGR10A2_ALL),
938             format_bgr10a2_no_write: !device
939                 .supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v3),
940             max_buffers_per_stage: 31,
941             max_textures_per_stage: if os_is_mac { 128 } else { 31 },
942             max_samplers_per_stage: 16,
943             buffer_alignment: if os_is_mac { 256 } else { 64 },
944             max_buffer_size: if device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v2) {
945                 1 << 30 // 1GB on macOS 1.2 and up
946             } else {
947                 1 << 28 // 256MB otherwise
948             },
949             max_texture_size: if Self::supports_any(
950                 &device,
951                 &[
952                     MTLFeatureSet::iOS_GPUFamily3_v1,
953                     MTLFeatureSet::tvOS_GPUFamily2_v1,
954                     MTLFeatureSet::macOS_GPUFamily1_v1,
955                 ],
956             ) {
957                 16384
958             } else if Self::supports_any(
959                 &device,
960                 &[
961                     MTLFeatureSet::iOS_GPUFamily1_v2,
962                     MTLFeatureSet::iOS_GPUFamily2_v2,
963                     MTLFeatureSet::tvOS_GPUFamily1_v1,
964                 ],
965             ) {
966                 8192
967             } else {
968                 4096
969             },
970             max_texture_3d_size: 2048,
971             max_texture_layers: 2048,
972             max_fragment_input_components: if os_is_mac { 128 } else { 60 },
973             sample_count_mask,
974             supports_debug_markers: Self::supports_any(
975                 &device,
976                 &[
977                     MTLFeatureSet::macOS_GPUFamily1_v2,
978                     MTLFeatureSet::macOS_GPUFamily2_v1,
979                     MTLFeatureSet::iOS_GPUFamily1_v3,
980                     MTLFeatureSet::iOS_GPUFamily2_v3,
981                     MTLFeatureSet::iOS_GPUFamily3_v2,
982                     MTLFeatureSet::iOS_GPUFamily4_v1,
983                     MTLFeatureSet::iOS_GPUFamily5_v1,
984                     MTLFeatureSet::tvOS_GPUFamily1_v2,
985                     MTLFeatureSet::tvOS_GPUFamily2_v1,
986                 ],
987             ),
988         }
989     }
990 
has_version_at_least(&self, needed_major: u32, needed_minor: u32) -> bool991     fn has_version_at_least(&self, needed_major: u32, needed_minor: u32) -> bool {
992         let (major, minor) = self.os_version;
993         Self::version_at_least(major, minor, needed_major, needed_minor)
994     }
995 }
996 
997 #[derive(Clone, Copy, Debug)]
998 struct PrivateDisabilities {
999     /// Near depth is not respected properly on some Intel GPUs.
1000     broken_viewport_near_depth: bool,
1001     /// Multi-target clears don't appear to work properly on Intel GPUs.
1002     broken_layered_clear_image: bool,
1003 }
1004 
1005 trait AsNative {
1006     type Native;
from(native: &Self::Native) -> Self1007     fn from(native: &Self::Native) -> Self;
as_native(&self) -> &Self::Native1008     fn as_native(&self) -> &Self::Native;
1009 }
1010 
1011 pub type BufferPtr = NonNull<metal::MTLBuffer>;
1012 pub type TexturePtr = NonNull<metal::MTLTexture>;
1013 pub type SamplerPtr = NonNull<metal::MTLSamplerState>;
1014 pub type ResourcePtr = NonNull<metal::MTLResource>;
1015 
1016 //TODO: make this a generic struct with a single generic implementation
1017 
1018 impl AsNative for BufferPtr {
1019     type Native = metal::BufferRef;
1020     #[inline]
from(native: &metal::BufferRef) -> Self1021     fn from(native: &metal::BufferRef) -> Self {
1022         unsafe { NonNull::new_unchecked(native.as_ptr()) }
1023     }
1024     #[inline]
as_native(&self) -> &metal::BufferRef1025     fn as_native(&self) -> &metal::BufferRef {
1026         unsafe { metal::BufferRef::from_ptr(self.as_ptr()) }
1027     }
1028 }
1029 
1030 impl AsNative for TexturePtr {
1031     type Native = metal::TextureRef;
1032     #[inline]
from(native: &metal::TextureRef) -> Self1033     fn from(native: &metal::TextureRef) -> Self {
1034         unsafe { NonNull::new_unchecked(native.as_ptr()) }
1035     }
1036     #[inline]
as_native(&self) -> &metal::TextureRef1037     fn as_native(&self) -> &metal::TextureRef {
1038         unsafe { metal::TextureRef::from_ptr(self.as_ptr()) }
1039     }
1040 }
1041 
1042 impl AsNative for SamplerPtr {
1043     type Native = metal::SamplerStateRef;
1044     #[inline]
from(native: &metal::SamplerStateRef) -> Self1045     fn from(native: &metal::SamplerStateRef) -> Self {
1046         unsafe { NonNull::new_unchecked(native.as_ptr()) }
1047     }
1048     #[inline]
as_native(&self) -> &metal::SamplerStateRef1049     fn as_native(&self) -> &metal::SamplerStateRef {
1050         unsafe { metal::SamplerStateRef::from_ptr(self.as_ptr()) }
1051     }
1052 }
1053 
1054 impl AsNative for ResourcePtr {
1055     type Native = metal::ResourceRef;
1056     #[inline]
from(native: &metal::ResourceRef) -> Self1057     fn from(native: &metal::ResourceRef) -> Self {
1058         unsafe { NonNull::new_unchecked(native.as_ptr()) }
1059     }
1060     #[inline]
as_native(&self) -> &metal::ResourceRef1061     fn as_native(&self) -> &metal::ResourceRef {
1062         unsafe { metal::ResourceRef::from_ptr(self.as_ptr()) }
1063     }
1064 }
1065