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