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