1 use crate::{conversions as conv, FastHashMap, PrivateCapabilities, MAX_COLOR_ATTACHMENTS};
2 
3 use hal::{
4     command::ClearColor,
5     format::{Aspects, ChannelType},
6     image::{Filter, NumSamples},
7     pso,
8 };
9 
10 use metal;
11 use parking_lot::{Mutex, RawRwLock};
12 use storage_map::{StorageMap, StorageMapGuard};
13 
14 use std::mem;
15 
16 pub type FastStorageMap<K, V> = StorageMap<RawRwLock, FastHashMap<K, V>>;
17 pub type FastStorageGuard<'a, V> = StorageMapGuard<'a, RawRwLock, V>;
18 
19 #[derive(Clone, Debug)]
20 pub struct ClearVertex {
21     pub pos: [f32; 4],
22 }
23 
24 #[derive(Clone, Debug)]
25 pub struct BlitVertex {
26     pub uv: [f32; 4],
27     pub pos: [f32; 4],
28 }
29 
30 #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
31 pub enum Channel {
32     Float,
33     Int,
34     Uint,
35 }
36 
37 impl From<ChannelType> for Channel {
from(channel_type: ChannelType) -> Self38     fn from(channel_type: ChannelType) -> Self {
39         match channel_type {
40             ChannelType::Unorm
41             | ChannelType::Snorm
42             | ChannelType::Ufloat
43             | ChannelType::Sfloat
44             | ChannelType::Uscaled
45             | ChannelType::Sscaled
46             | ChannelType::Srgb => Channel::Float,
47             ChannelType::Uint => Channel::Uint,
48             ChannelType::Sint => Channel::Int,
49         }
50     }
51 }
52 
53 impl Channel {
interpret(self, raw: ClearColor) -> metal::MTLClearColor54     pub fn interpret(self, raw: ClearColor) -> metal::MTLClearColor {
55         unsafe {
56             match self {
57                 Channel::Float => metal::MTLClearColor::new(
58                     raw.float32[0] as _,
59                     raw.float32[1] as _,
60                     raw.float32[2] as _,
61                     raw.float32[3] as _,
62                 ),
63                 Channel::Int => metal::MTLClearColor::new(
64                     raw.sint32[0] as _,
65                     raw.sint32[1] as _,
66                     raw.sint32[2] as _,
67                     raw.sint32[3] as _,
68                 ),
69                 Channel::Uint => metal::MTLClearColor::new(
70                     raw.uint32[0] as _,
71                     raw.uint32[1] as _,
72                     raw.uint32[2] as _,
73                     raw.uint32[3] as _,
74                 ),
75             }
76         }
77     }
78 }
79 
80 #[derive(Debug)]
81 pub struct SamplerStates {
82     nearest: metal::SamplerState,
83     linear: metal::SamplerState,
84 }
85 
86 impl SamplerStates {
new(device: &metal::DeviceRef) -> Self87     fn new(device: &metal::DeviceRef) -> Self {
88         let desc = metal::SamplerDescriptor::new();
89         desc.set_min_filter(metal::MTLSamplerMinMagFilter::Nearest);
90         desc.set_mag_filter(metal::MTLSamplerMinMagFilter::Nearest);
91         desc.set_mip_filter(metal::MTLSamplerMipFilter::Nearest);
92         let nearest = device.new_sampler(&desc);
93         desc.set_min_filter(metal::MTLSamplerMinMagFilter::Linear);
94         desc.set_mag_filter(metal::MTLSamplerMinMagFilter::Linear);
95         let linear = device.new_sampler(&desc);
96 
97         SamplerStates { nearest, linear }
98     }
99 
get(&self, filter: Filter) -> &metal::SamplerStateRef100     pub fn get(&self, filter: Filter) -> &metal::SamplerStateRef {
101         match filter {
102             Filter::Nearest => &self.nearest,
103             Filter::Linear => &self.linear,
104         }
105     }
106 }
107 
108 #[derive(Debug)]
109 pub struct DepthStencilStates {
110     map: FastStorageMap<pso::DepthStencilDesc, metal::DepthStencilState>,
111     write_none: pso::DepthStencilDesc,
112     write_depth: pso::DepthStencilDesc,
113     write_stencil: pso::DepthStencilDesc,
114     write_all: pso::DepthStencilDesc,
115 }
116 
117 impl DepthStencilStates {
new(device: &metal::DeviceRef) -> Self118     fn new(device: &metal::DeviceRef) -> Self {
119         let write_none = pso::DepthStencilDesc {
120             depth: None,
121             depth_bounds: false,
122             stencil: None,
123         };
124         let write_depth = pso::DepthStencilDesc {
125             depth: Some(pso::DepthTest {
126                 fun: pso::Comparison::Always,
127                 write: true,
128             }),
129             depth_bounds: false,
130             stencil: None,
131         };
132         let face = pso::StencilFace {
133             fun: pso::Comparison::Always,
134             op_fail: pso::StencilOp::Replace,
135             op_depth_fail: pso::StencilOp::Replace,
136             op_pass: pso::StencilOp::Replace,
137         };
138         let write_stencil = pso::DepthStencilDesc {
139             depth: None,
140             depth_bounds: false,
141             stencil: Some(pso::StencilTest {
142                 faces: pso::Sided::new(face),
143                 ..pso::StencilTest::default()
144             }),
145         };
146         let write_all = pso::DepthStencilDesc {
147             depth: Some(pso::DepthTest {
148                 fun: pso::Comparison::Always,
149                 write: true,
150             }),
151             depth_bounds: false,
152             stencil: Some(pso::StencilTest {
153                 faces: pso::Sided::new(face),
154                 ..pso::StencilTest::default()
155             }),
156         };
157 
158         let map = FastStorageMap::default();
159         for desc in &[&write_none, &write_depth, &write_stencil, &write_all] {
160             map.get_or_create_with(*desc, || {
161                 let raw_desc = Self::create_desc(desc).unwrap();
162                 device.new_depth_stencil_state(&raw_desc)
163             });
164         }
165 
166         DepthStencilStates {
167             map,
168             write_none,
169             write_depth,
170             write_stencil,
171             write_all,
172         }
173     }
174 
get_write(&self, aspects: Aspects) -> FastStorageGuard<metal::DepthStencilState>175     pub fn get_write(&self, aspects: Aspects) -> FastStorageGuard<metal::DepthStencilState> {
176         let key = if aspects.contains(Aspects::DEPTH | Aspects::STENCIL) {
177             &self.write_all
178         } else if aspects.contains(Aspects::DEPTH) {
179             &self.write_depth
180         } else if aspects.contains(Aspects::STENCIL) {
181             &self.write_stencil
182         } else {
183             &self.write_none
184         };
185         self.map.get_or_create_with(key, || unreachable!())
186     }
187 
prepare(&self, desc: &pso::DepthStencilDesc, device: &metal::DeviceRef)188     pub fn prepare(&self, desc: &pso::DepthStencilDesc, device: &metal::DeviceRef) {
189         self.map.prepare_maybe(desc, || {
190             Self::create_desc(desc).map(|raw_desc| device.new_depth_stencil_state(&raw_desc))
191         });
192     }
193 
194     // TODO: avoid locking for writes every time
get( &self, desc: pso::DepthStencilDesc, device: &Mutex<metal::Device>, ) -> FastStorageGuard<metal::DepthStencilState>195     pub fn get(
196         &self,
197         desc: pso::DepthStencilDesc,
198         device: &Mutex<metal::Device>,
199     ) -> FastStorageGuard<metal::DepthStencilState> {
200         self.map.get_or_create_with(&desc, || {
201             let raw_desc = Self::create_desc(&desc).expect("Incomplete descriptor provided");
202             device.lock().new_depth_stencil_state(&raw_desc)
203         })
204     }
205 
create_stencil( face: &pso::StencilFace, read_mask: pso::StencilValue, write_mask: pso::StencilValue, ) -> metal::StencilDescriptor206     fn create_stencil(
207         face: &pso::StencilFace,
208         read_mask: pso::StencilValue,
209         write_mask: pso::StencilValue,
210     ) -> metal::StencilDescriptor {
211         let desc = metal::StencilDescriptor::new();
212         desc.set_stencil_compare_function(conv::map_compare_function(face.fun));
213         desc.set_read_mask(read_mask);
214         desc.set_write_mask(write_mask);
215         desc.set_stencil_failure_operation(conv::map_stencil_op(face.op_fail));
216         desc.set_depth_failure_operation(conv::map_stencil_op(face.op_depth_fail));
217         desc.set_depth_stencil_pass_operation(conv::map_stencil_op(face.op_pass));
218         desc
219     }
220 
create_desc(desc: &pso::DepthStencilDesc) -> Option<metal::DepthStencilDescriptor>221     fn create_desc(desc: &pso::DepthStencilDesc) -> Option<metal::DepthStencilDescriptor> {
222         let raw = metal::DepthStencilDescriptor::new();
223 
224         if let Some(ref stencil) = desc.stencil {
225             let read_masks = match stencil.read_masks {
226                 pso::State::Static(value) => value,
227                 pso::State::Dynamic => return None,
228             };
229             let write_masks = match stencil.write_masks {
230                 pso::State::Static(value) => value,
231                 pso::State::Dynamic => return None,
232             };
233             let front_desc =
234                 Self::create_stencil(&stencil.faces.front, read_masks.front, write_masks.front);
235             raw.set_front_face_stencil(Some(&front_desc));
236             let back_desc = if stencil.faces.front == stencil.faces.back
237                 && read_masks.front == read_masks.back
238                 && write_masks.front == write_masks.back
239             {
240                 front_desc
241             } else {
242                 Self::create_stencil(&stencil.faces.back, read_masks.back, write_masks.back)
243             };
244             raw.set_back_face_stencil(Some(&back_desc));
245         }
246 
247         if let Some(ref depth) = desc.depth {
248             raw.set_depth_compare_function(conv::map_compare_function(depth.fun));
249             raw.set_depth_write_enabled(depth.write);
250         }
251 
252         Some(raw)
253     }
254 }
255 
256 #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
257 pub struct ClearKey {
258     pub framebuffer_aspects: Aspects,
259     pub color_formats: [metal::MTLPixelFormat; MAX_COLOR_ATTACHMENTS],
260     pub depth_stencil_format: metal::MTLPixelFormat,
261     pub sample_count: NumSamples,
262     pub target_index: Option<(u8, Channel)>,
263 }
264 
265 #[derive(Debug)]
266 pub struct ImageClearPipes {
267     map: FastStorageMap<ClearKey, metal::RenderPipelineState>,
268 }
269 
270 impl ImageClearPipes {
get( &self, key: ClearKey, library: &Mutex<metal::Library>, device: &Mutex<metal::Device>, private_caps: &PrivateCapabilities, ) -> FastStorageGuard<metal::RenderPipelineState>271     pub(crate) fn get(
272         &self,
273         key: ClearKey,
274         library: &Mutex<metal::Library>,
275         device: &Mutex<metal::Device>,
276         private_caps: &PrivateCapabilities,
277     ) -> FastStorageGuard<metal::RenderPipelineState> {
278         self.map.get_or_create_with(&key, || {
279             Self::create(key, &*library.lock(), &*device.lock(), private_caps)
280         })
281     }
282 
create( key: ClearKey, library: &metal::LibraryRef, device: &metal::DeviceRef, private_caps: &PrivateCapabilities, ) -> metal::RenderPipelineState283     fn create(
284         key: ClearKey,
285         library: &metal::LibraryRef,
286         device: &metal::DeviceRef,
287         private_caps: &PrivateCapabilities,
288     ) -> metal::RenderPipelineState {
289         let pipeline = metal::RenderPipelineDescriptor::new();
290         if private_caps.layered_rendering {
291             pipeline.set_input_primitive_topology(metal::MTLPrimitiveTopologyClass::Triangle);
292         }
293 
294         let vs_clear = library.get_function("vs_clear", None).unwrap();
295         pipeline.set_vertex_function(Some(&vs_clear));
296 
297         if key.framebuffer_aspects.contains(Aspects::COLOR) {
298             for (i, &format) in key.color_formats.iter().enumerate() {
299                 pipeline
300                     .color_attachments()
301                     .object_at(i as u64)
302                     .unwrap()
303                     .set_pixel_format(format);
304             }
305         }
306         if key.framebuffer_aspects.contains(Aspects::DEPTH) {
307             pipeline.set_depth_attachment_pixel_format(key.depth_stencil_format);
308         }
309         if key.framebuffer_aspects.contains(Aspects::STENCIL) {
310             pipeline.set_stencil_attachment_pixel_format(key.depth_stencil_format);
311         }
312         if key.sample_count > 1 {
313             pipeline.set_sample_count(key.sample_count as u64);
314         }
315 
316         if let Some((index, channel)) = key.target_index {
317             assert!(key.framebuffer_aspects.contains(Aspects::COLOR));
318             let s_channel = match channel {
319                 Channel::Float => "float",
320                 Channel::Int => "int",
321                 Channel::Uint => "uint",
322             };
323             let ps_name = format!("ps_clear{}_{}", index, s_channel);
324             let ps_fun = library.get_function(&ps_name, None).unwrap();
325             pipeline.set_fragment_function(Some(&ps_fun));
326         }
327 
328         // Vertex buffers
329         let vertex_descriptor = metal::VertexDescriptor::new();
330         let mtl_buffer_desc = vertex_descriptor.layouts().object_at(0).unwrap();
331         mtl_buffer_desc.set_stride(mem::size_of::<ClearVertex>() as _);
332         for i in 0..1 {
333             let mtl_attribute_desc = vertex_descriptor
334                 .attributes()
335                 .object_at(i)
336                 .expect("too many vertex attributes");
337             mtl_attribute_desc.set_buffer_index(0);
338             mtl_attribute_desc.set_offset(i * mem::size_of::<[f32; 4]>() as u64);
339             mtl_attribute_desc.set_format(metal::MTLVertexFormat::Float4);
340         }
341         pipeline.set_vertex_descriptor(Some(&vertex_descriptor));
342 
343         device.new_render_pipeline_state(&pipeline).unwrap()
344     }
345 }
346 
347 pub type BlitKey = (
348     metal::MTLTextureType,
349     metal::MTLPixelFormat,
350     Aspects,
351     Channel,
352 );
353 
354 #[derive(Debug)]
355 pub struct ImageBlitPipes {
356     map: FastStorageMap<BlitKey, metal::RenderPipelineState>,
357 }
358 
359 impl ImageBlitPipes {
get( &self, key: BlitKey, library: &Mutex<metal::Library>, device: &Mutex<metal::Device>, private_caps: &PrivateCapabilities, ) -> FastStorageGuard<metal::RenderPipelineState>360     pub(crate) fn get(
361         &self,
362         key: BlitKey,
363         library: &Mutex<metal::Library>,
364         device: &Mutex<metal::Device>,
365         private_caps: &PrivateCapabilities,
366     ) -> FastStorageGuard<metal::RenderPipelineState> {
367         self.map.get_or_create_with(&key, || {
368             Self::create(key, &*library.lock(), &*device.lock(), private_caps)
369         })
370     }
371 
create( key: BlitKey, library: &metal::LibraryRef, device: &metal::DeviceRef, private_caps: &PrivateCapabilities, ) -> metal::RenderPipelineState372     fn create(
373         key: BlitKey,
374         library: &metal::LibraryRef,
375         device: &metal::DeviceRef,
376         private_caps: &PrivateCapabilities,
377     ) -> metal::RenderPipelineState {
378         use metal::MTLTextureType as Tt;
379 
380         let pipeline = metal::RenderPipelineDescriptor::new();
381         if private_caps.layered_rendering {
382             pipeline.set_input_primitive_topology(metal::MTLPrimitiveTopologyClass::Triangle);
383         }
384 
385         let s_type = match key.0 {
386             Tt::D1 => "1d",
387             Tt::D1Array => "1d_array",
388             Tt::D2 => "2d",
389             Tt::D2Array => "2d_array",
390             Tt::D3 => "3d",
391             Tt::D2Multisample => panic!("Can't blit MSAA surfaces"),
392             Tt::Cube | Tt::CubeArray => unimplemented!(),
393         };
394         let s_channel = if key.2.contains(Aspects::COLOR) {
395             match key.3 {
396                 Channel::Float => "float",
397                 Channel::Int => "int",
398                 Channel::Uint => "uint",
399             }
400         } else {
401             "depth" //TODO: stencil
402         };
403         let ps_name = format!("ps_blit_{}_{}", s_type, s_channel);
404 
405         let vs_blit = library.get_function("vs_blit", None).unwrap();
406         let ps_blit = library.get_function(&ps_name, None).unwrap();
407         pipeline.set_vertex_function(Some(&vs_blit));
408         pipeline.set_fragment_function(Some(&ps_blit));
409 
410         if key.2.contains(Aspects::COLOR) {
411             pipeline
412                 .color_attachments()
413                 .object_at(0)
414                 .unwrap()
415                 .set_pixel_format(key.1);
416         }
417         if key.2.contains(Aspects::DEPTH) {
418             pipeline.set_depth_attachment_pixel_format(key.1);
419         }
420         if key.2.contains(Aspects::STENCIL) {
421             pipeline.set_stencil_attachment_pixel_format(key.1);
422         }
423 
424         // Vertex buffers
425         let vertex_descriptor = metal::VertexDescriptor::new();
426         let mtl_buffer_desc = vertex_descriptor.layouts().object_at(0).unwrap();
427         mtl_buffer_desc.set_stride(mem::size_of::<BlitVertex>() as _);
428         for i in 0..2 {
429             let mtl_attribute_desc = vertex_descriptor
430                 .attributes()
431                 .object_at(i)
432                 .expect("too many vertex attributes");
433             mtl_attribute_desc.set_buffer_index(0);
434             mtl_attribute_desc.set_offset(i * mem::size_of::<[f32; 4]>() as u64);
435             mtl_attribute_desc.set_format(metal::MTLVertexFormat::Float4);
436         }
437         pipeline.set_vertex_descriptor(Some(&vertex_descriptor));
438 
439         device.new_render_pipeline_state(&pipeline).unwrap()
440     }
441 }
442 
443 #[derive(Debug)]
444 pub struct ServicePipes {
445     pub library: Mutex<metal::Library>,
446     pub sampler_states: SamplerStates,
447     pub depth_stencil_states: DepthStencilStates,
448     pub clears: ImageClearPipes,
449     pub blits: ImageBlitPipes,
450     pub copy_buffer: metal::ComputePipelineState,
451     pub fill_buffer: metal::ComputePipelineState,
452 }
453 
454 impl ServicePipes {
new(device: &metal::DeviceRef) -> Self455     pub fn new(device: &metal::DeviceRef) -> Self {
456         let data = if cfg!(target_os = "macos") {
457             &include_bytes!("./../shaders/gfx-shaders-macos.metallib")[..]
458         } else if cfg!(target_arch = "aarch64") {
459             &include_bytes!("./../shaders/gfx-shaders-ios.metallib")[..]
460         } else {
461             &include_bytes!("./../shaders/gfx-shaders-ios-simulator.metallib")[..]
462         };
463         let library = device.new_library_with_data(data).unwrap();
464 
465         let copy_buffer = Self::create_copy_buffer(&library, device);
466         let fill_buffer = Self::create_fill_buffer(&library, device);
467 
468         ServicePipes {
469             library: Mutex::new(library),
470             sampler_states: SamplerStates::new(device),
471             depth_stencil_states: DepthStencilStates::new(device),
472             clears: ImageClearPipes {
473                 map: FastStorageMap::default(),
474             },
475             blits: ImageBlitPipes {
476                 map: FastStorageMap::default(),
477             },
478             copy_buffer,
479             fill_buffer,
480         }
481     }
482 
create_copy_buffer( library: &metal::LibraryRef, device: &metal::DeviceRef, ) -> metal::ComputePipelineState483     fn create_copy_buffer(
484         library: &metal::LibraryRef,
485         device: &metal::DeviceRef,
486     ) -> metal::ComputePipelineState {
487         let pipeline = metal::ComputePipelineDescriptor::new();
488 
489         let cs_copy_buffer = library.get_function("cs_copy_buffer", None).unwrap();
490         pipeline.set_compute_function(Some(&cs_copy_buffer));
491         pipeline.set_thread_group_size_is_multiple_of_thread_execution_width(true);
492 
493         /*TODO: check MacOS version
494         if let Some(buffers) = pipeline.buffers() {
495             buffers.object_at(0).unwrap().set_mutability(metal::MTLMutability::Mutable);
496             buffers.object_at(1).unwrap().set_mutability(metal::MTLMutability::Immutable);
497             buffers.object_at(2).unwrap().set_mutability(metal::MTLMutability::Immutable);
498         }*/
499 
500         device.new_compute_pipeline_state(&pipeline).unwrap()
501     }
502 
create_fill_buffer( library: &metal::LibraryRef, device: &metal::DeviceRef, ) -> metal::ComputePipelineState503     fn create_fill_buffer(
504         library: &metal::LibraryRef,
505         device: &metal::DeviceRef,
506     ) -> metal::ComputePipelineState {
507         let pipeline = metal::ComputePipelineDescriptor::new();
508 
509         let cs_fill_buffer = library.get_function("cs_fill_buffer", None).unwrap();
510         pipeline.set_compute_function(Some(&cs_fill_buffer));
511         pipeline.set_thread_group_size_is_multiple_of_thread_execution_width(true);
512 
513         /*TODO: check MacOS version
514         if let Some(buffers) = pipeline.buffers() {
515             buffers.object_at(0).unwrap().set_mutability(metal::MTLMutability::Mutable);
516             buffers.object_at(1).unwrap().set_mutability(metal::MTLMutability::Immutable);
517         }*/
518 
519         device.new_compute_pipeline_state(&pipeline).unwrap()
520     }
521 }
522