Skip to main content

mtl_gpu/mtl4/compiler/
compiler.rs

1//! MTL4 Compiler implementation.
2
3use std::ffi::c_void;
4use std::ptr::NonNull;
5
6use mtl_foundation::Referencing;
7use mtl_sys::{msg_send_0, msg_send_2, msg_send_3, msg_send_4, sel};
8
9use crate::mtl4::{
10    BinaryFunction, BinaryFunctionDescriptor, CompilerTask, ComputePipelineDescriptor,
11    LibraryDescriptor, MachineLearningPipelineDescriptor, MachineLearningPipelineState,
12    PipelineDataSetSerializer, PipelineDescriptor, PipelineStageDynamicLinkingDescriptor,
13    RenderPipelineDynamicLinkingDescriptor,
14};
15use crate::{ComputePipelineState, Device, DynamicLibrary, Library, RenderPipelineState};
16
17use super::CompilerTaskOptions;
18
19/// Helper to create a generic error.
20fn generic_error() -> mtl_foundation::Error {
21    mtl_foundation::Error::error(std::ptr::null_mut(), -1, std::ptr::null_mut())
22        .expect("failed to create error object")
23}
24
25/// MTL4 shader compiler.
26///
27/// C++ equivalent: `MTL4::Compiler`
28///
29/// Compiler provides methods for compiling shaders, creating pipelines,
30/// and managing binary functions.
31#[repr(transparent)]
32pub struct Compiler(NonNull<c_void>);
33
34impl Compiler {
35    /// Create a Compiler from a raw pointer.
36    #[inline]
37    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
38        NonNull::new(ptr).map(Self)
39    }
40
41    /// Get the raw pointer.
42    #[inline]
43    pub fn as_raw(&self) -> *mut c_void {
44        self.0.as_ptr()
45    }
46
47    /// Get the device.
48    ///
49    /// C++ equivalent: `MTL::Device* device() const`
50    pub fn device(&self) -> Option<Device> {
51        unsafe {
52            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(device));
53            Device::from_raw(ptr)
54        }
55    }
56
57    /// Get the label.
58    ///
59    /// C++ equivalent: `NS::String* label() const`
60    pub fn label(&self) -> Option<String> {
61        unsafe {
62            let ns_string: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
63            if ns_string.is_null() {
64                return None;
65            }
66            let c_str: *const i8 = msg_send_0(ns_string, sel!(UTF8String));
67            if c_str.is_null() {
68                return None;
69            }
70            Some(
71                std::ffi::CStr::from_ptr(c_str)
72                    .to_string_lossy()
73                    .into_owned(),
74            )
75        }
76    }
77
78    /// Get the pipeline data set serializer.
79    ///
80    /// C++ equivalent: `PipelineDataSetSerializer* pipelineDataSetSerializer() const`
81    pub fn pipeline_data_set_serializer(&self) -> Option<PipelineDataSetSerializer> {
82        unsafe {
83            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(pipelineDataSetSerializer));
84            PipelineDataSetSerializer::from_raw(ptr)
85        }
86    }
87
88    // ========== Library Creation ==========
89
90    /// Create a new library synchronously.
91    ///
92    /// C++ equivalent: `MTL::Library* newLibrary(const MTL4::LibraryDescriptor*, NS::Error**)`
93    pub fn new_library(
94        &self,
95        descriptor: &LibraryDescriptor,
96    ) -> Result<Library, mtl_foundation::Error> {
97        unsafe {
98            let mut error: *mut c_void = std::ptr::null_mut();
99            let ptr: *mut c_void = msg_send_2(
100                self.as_ptr(),
101                sel!(newLibraryWithDescriptor:error:),
102                descriptor.as_ptr(),
103                &mut error as *mut _,
104            );
105            if !error.is_null() {
106                if let Some(err) = mtl_foundation::Error::from_ptr(error) {
107                    return Err(err);
108                }
109            }
110            Library::from_raw(ptr).ok_or_else(|| generic_error())
111        }
112    }
113
114    // ========== Binary Function Creation ==========
115
116    /// Create a new binary function synchronously.
117    ///
118    /// C++ equivalent: `BinaryFunction* newBinaryFunction(..., NS::Error**)`
119    pub fn new_binary_function(
120        &self,
121        descriptor: &BinaryFunctionDescriptor,
122        options: Option<&CompilerTaskOptions>,
123    ) -> Result<BinaryFunction, mtl_foundation::Error> {
124        unsafe {
125            let mut error: *mut c_void = std::ptr::null_mut();
126            let options_ptr = options.map_or(std::ptr::null(), |o| o.as_ptr());
127            let ptr: *mut c_void = msg_send_3(
128                self.as_ptr(),
129                sel!(newBinaryFunctionWithDescriptor:compilerTaskOptions:error:),
130                descriptor.as_ptr(),
131                options_ptr,
132                &mut error as *mut _,
133            );
134            if !error.is_null() {
135                if let Some(err) = mtl_foundation::Error::from_ptr(error) {
136                    return Err(err);
137                }
138            }
139            BinaryFunction::from_raw(ptr).ok_or_else(|| generic_error())
140        }
141    }
142
143    // ========== Compute Pipeline Creation ==========
144
145    /// Create a new compute pipeline state synchronously.
146    ///
147    /// C++ equivalent: `MTL::ComputePipelineState* newComputePipelineState(..., NS::Error**)`
148    pub fn new_compute_pipeline_state(
149        &self,
150        descriptor: &ComputePipelineDescriptor,
151        options: Option<&CompilerTaskOptions>,
152    ) -> Result<ComputePipelineState, mtl_foundation::Error> {
153        unsafe {
154            let mut error: *mut c_void = std::ptr::null_mut();
155            let options_ptr = options.map_or(std::ptr::null(), |o| o.as_ptr());
156            let ptr: *mut c_void = msg_send_3(
157                self.as_ptr(),
158                sel!(newComputePipelineStateWithDescriptor:compilerTaskOptions:error:),
159                descriptor.as_ptr(),
160                options_ptr,
161                &mut error as *mut _,
162            );
163            if !error.is_null() {
164                if let Some(err) = mtl_foundation::Error::from_ptr(error) {
165                    return Err(err);
166                }
167            }
168            ComputePipelineState::from_raw(ptr).ok_or_else(|| generic_error())
169        }
170    }
171
172    /// Create a new compute pipeline state with dynamic linking synchronously.
173    ///
174    /// C++ equivalent: `MTL::ComputePipelineState* newComputePipelineState(..., dynamicLinkingDescriptor, ..., NS::Error**)`
175    pub fn new_compute_pipeline_state_with_dynamic_linking(
176        &self,
177        descriptor: &ComputePipelineDescriptor,
178        dynamic_linking: &PipelineStageDynamicLinkingDescriptor,
179        options: Option<&CompilerTaskOptions>,
180    ) -> Result<ComputePipelineState, mtl_foundation::Error> {
181        unsafe {
182            let mut error: *mut c_void = std::ptr::null_mut();
183            let options_ptr = options.map_or(std::ptr::null(), |o| o.as_ptr());
184            let ptr: *mut c_void = msg_send_4(
185                self.as_ptr(),
186                sel!(newComputePipelineStateWithDescriptor:dynamicLinkingDescriptor:compilerTaskOptions:error:),
187                descriptor.as_ptr(),
188                dynamic_linking.as_ptr(),
189                options_ptr,
190                &mut error as *mut _,
191            );
192            if !error.is_null() {
193                if let Some(err) = mtl_foundation::Error::from_ptr(error) {
194                    return Err(err);
195                }
196            }
197            ComputePipelineState::from_raw(ptr).ok_or_else(|| generic_error())
198        }
199    }
200
201    // ========== Render Pipeline Creation ==========
202
203    /// Create a new render pipeline state synchronously.
204    ///
205    /// C++ equivalent: `MTL::RenderPipelineState* newRenderPipelineState(..., NS::Error**)`
206    pub fn new_render_pipeline_state(
207        &self,
208        descriptor: &PipelineDescriptor,
209        options: Option<&CompilerTaskOptions>,
210    ) -> Result<RenderPipelineState, mtl_foundation::Error> {
211        unsafe {
212            let mut error: *mut c_void = std::ptr::null_mut();
213            let options_ptr = options.map_or(std::ptr::null(), |o| o.as_ptr());
214            let ptr: *mut c_void = msg_send_3(
215                self.as_ptr(),
216                sel!(newRenderPipelineStateWithDescriptor:compilerTaskOptions:error:),
217                descriptor.as_ptr(),
218                options_ptr,
219                &mut error as *mut _,
220            );
221            if !error.is_null() {
222                if let Some(err) = mtl_foundation::Error::from_ptr(error) {
223                    return Err(err);
224                }
225            }
226            RenderPipelineState::from_raw(ptr).ok_or_else(|| generic_error())
227        }
228    }
229
230    /// Create a new render pipeline state with dynamic linking synchronously.
231    ///
232    /// C++ equivalent: `MTL::RenderPipelineState* newRenderPipelineState(..., dynamicLinkingDescriptor, ..., NS::Error**)`
233    pub fn new_render_pipeline_state_with_dynamic_linking(
234        &self,
235        descriptor: &PipelineDescriptor,
236        dynamic_linking: &RenderPipelineDynamicLinkingDescriptor,
237        options: Option<&CompilerTaskOptions>,
238    ) -> Result<RenderPipelineState, mtl_foundation::Error> {
239        unsafe {
240            let mut error: *mut c_void = std::ptr::null_mut();
241            let options_ptr = options.map_or(std::ptr::null(), |o| o.as_ptr());
242            let ptr: *mut c_void = msg_send_4(
243                self.as_ptr(),
244                sel!(newRenderPipelineStateWithDescriptor:dynamicLinkingDescriptor:compilerTaskOptions:error:),
245                descriptor.as_ptr(),
246                dynamic_linking.as_ptr(),
247                options_ptr,
248                &mut error as *mut _,
249            );
250            if !error.is_null() {
251                if let Some(err) = mtl_foundation::Error::from_ptr(error) {
252                    return Err(err);
253                }
254            }
255            RenderPipelineState::from_raw(ptr).ok_or_else(|| generic_error())
256        }
257    }
258
259    /// Create a new render pipeline state by specialization.
260    ///
261    /// C++ equivalent: `MTL::RenderPipelineState* newRenderPipelineStateBySpecialization(...)`
262    pub fn new_render_pipeline_state_by_specialization(
263        &self,
264        descriptor: &PipelineDescriptor,
265        pipeline: &RenderPipelineState,
266    ) -> Result<RenderPipelineState, mtl_foundation::Error> {
267        unsafe {
268            let mut error: *mut c_void = std::ptr::null_mut();
269            let ptr: *mut c_void = msg_send_3(
270                self.as_ptr(),
271                sel!(newRenderPipelineStateBySpecializationWithDescriptor:pipeline:error:),
272                descriptor.as_ptr(),
273                pipeline.as_ptr(),
274                &mut error as *mut _,
275            );
276            if !error.is_null() {
277                if let Some(err) = mtl_foundation::Error::from_ptr(error) {
278                    return Err(err);
279                }
280            }
281            RenderPipelineState::from_raw(ptr).ok_or_else(|| generic_error())
282        }
283    }
284
285    // ========== Dynamic Library Creation ==========
286
287    /// Create a new dynamic library from a library.
288    ///
289    /// C++ equivalent: `MTL::DynamicLibrary* newDynamicLibrary(const MTL::Library*, NS::Error**)`
290    pub fn new_dynamic_library_from_library(
291        &self,
292        library: &Library,
293    ) -> Result<*mut c_void, mtl_foundation::Error> {
294        unsafe {
295            let mut error: *mut c_void = std::ptr::null_mut();
296            let ptr: *mut c_void = msg_send_2(
297                self.as_ptr(),
298                sel!(newDynamicLibrary:error:),
299                library.as_ptr(),
300                &mut error as *mut _,
301            );
302            if !error.is_null() {
303                if let Some(err) = mtl_foundation::Error::from_ptr(error) {
304                    return Err(err);
305                }
306            }
307            if ptr.is_null() {
308                return Err(generic_error());
309            }
310            Ok(ptr)
311        }
312    }
313
314    /// Create a new dynamic library from a URL.
315    ///
316    /// C++ equivalent: `MTL::DynamicLibrary* newDynamicLibrary(const NS::URL*, NS::Error**)`
317    pub fn new_dynamic_library_from_url(
318        &self,
319        url: *const c_void,
320    ) -> Result<*mut c_void, mtl_foundation::Error> {
321        unsafe {
322            let mut error: *mut c_void = std::ptr::null_mut();
323            let ptr: *mut c_void = msg_send_2(
324                self.as_ptr(),
325                sel!(newDynamicLibraryWithURL:error:),
326                url,
327                &mut error as *mut _,
328            );
329            if !error.is_null() {
330                if let Some(err) = mtl_foundation::Error::from_ptr(error) {
331                    return Err(err);
332                }
333            }
334            if ptr.is_null() {
335                return Err(generic_error());
336            }
337            Ok(ptr)
338        }
339    }
340
341    // ========== Async Library Creation ==========
342
343    /// Create a new library asynchronously with a completion handler.
344    ///
345    /// C++ equivalent: `CompilerTask* newLibrary(const LibraryDescriptor*, NewLibraryCompletionHandler)`
346    pub fn new_library_async<F>(
347        &self,
348        descriptor: &LibraryDescriptor,
349        completion_handler: F,
350    ) -> Option<CompilerTask>
351    where
352        F: Fn(Option<Library>, Option<mtl_foundation::Error>) + Send + 'static,
353    {
354        let block =
355            mtl_sys::TwoArgBlock::from_fn(move |lib_ptr: *mut c_void, err_ptr: *mut c_void| {
356                let library = if lib_ptr.is_null() {
357                    None
358                } else {
359                    unsafe { Library::from_raw(lib_ptr) }
360                };
361
362                let error = if err_ptr.is_null() {
363                    None
364                } else {
365                    unsafe { mtl_foundation::Error::from_ptr(err_ptr) }
366                };
367
368                completion_handler(library, error);
369            });
370
371        unsafe {
372            let ptr: *mut c_void = msg_send_2(
373                self.as_ptr(),
374                sel!(newLibraryWithDescriptor:completionHandler:),
375                descriptor.as_ptr(),
376                block.as_ptr(),
377            );
378
379            std::mem::forget(block);
380            CompilerTask::from_raw(ptr)
381        }
382    }
383
384    // ========== Async Binary Function Creation ==========
385
386    /// Create a new binary function asynchronously with a completion handler.
387    ///
388    /// C++ equivalent: `CompilerTask* newBinaryFunction(const BinaryFunctionDescriptor*, CompilerTaskOptions*, NewBinaryFunctionCompletionHandler)`
389    pub fn new_binary_function_async<F>(
390        &self,
391        descriptor: &BinaryFunctionDescriptor,
392        options: Option<&CompilerTaskOptions>,
393        completion_handler: F,
394    ) -> Option<CompilerTask>
395    where
396        F: Fn(Option<BinaryFunction>, Option<mtl_foundation::Error>) + Send + 'static,
397    {
398        let block =
399            mtl_sys::TwoArgBlock::from_fn(move |fn_ptr: *mut c_void, err_ptr: *mut c_void| {
400                let function = if fn_ptr.is_null() {
401                    None
402                } else {
403                    unsafe { BinaryFunction::from_raw(fn_ptr) }
404                };
405
406                let error = if err_ptr.is_null() {
407                    None
408                } else {
409                    unsafe { mtl_foundation::Error::from_ptr(err_ptr) }
410                };
411
412                completion_handler(function, error);
413            });
414
415        let options_ptr = options.map_or(std::ptr::null(), |o| o.as_ptr());
416
417        unsafe {
418            let ptr: *mut c_void = msg_send_3(
419                self.as_ptr(),
420                sel!(newBinaryFunctionWithDescriptor:compilerTaskOptions:completionHandler:),
421                descriptor.as_ptr(),
422                options_ptr,
423                block.as_ptr(),
424            );
425
426            std::mem::forget(block);
427            CompilerTask::from_raw(ptr)
428        }
429    }
430
431    // ========== Async Compute Pipeline Creation ==========
432
433    /// Create a new compute pipeline state asynchronously with a completion handler.
434    ///
435    /// C++ equivalent: `CompilerTask* newComputePipelineState(const ComputePipelineDescriptor*, CompilerTaskOptions*, NewComputePipelineStateCompletionHandler)`
436    pub fn new_compute_pipeline_state_async<F>(
437        &self,
438        descriptor: &ComputePipelineDescriptor,
439        options: Option<&CompilerTaskOptions>,
440        completion_handler: F,
441    ) -> Option<CompilerTask>
442    where
443        F: Fn(Option<ComputePipelineState>, Option<mtl_foundation::Error>) + Send + 'static,
444    {
445        let block =
446            mtl_sys::TwoArgBlock::from_fn(move |state_ptr: *mut c_void, err_ptr: *mut c_void| {
447                let state = if state_ptr.is_null() {
448                    None
449                } else {
450                    unsafe { ComputePipelineState::from_raw(state_ptr) }
451                };
452
453                let error = if err_ptr.is_null() {
454                    None
455                } else {
456                    unsafe { mtl_foundation::Error::from_ptr(err_ptr) }
457                };
458
459                completion_handler(state, error);
460            });
461
462        let options_ptr = options.map_or(std::ptr::null(), |o| o.as_ptr());
463
464        unsafe {
465            let ptr: *mut c_void = msg_send_3(
466                self.as_ptr(),
467                sel!(newComputePipelineStateWithDescriptor:compilerTaskOptions:completionHandler:),
468                descriptor.as_ptr(),
469                options_ptr,
470                block.as_ptr(),
471            );
472
473            std::mem::forget(block);
474            CompilerTask::from_raw(ptr)
475        }
476    }
477
478    // ========== Async Render Pipeline Creation ==========
479
480    /// Create a new render pipeline state asynchronously with a completion handler.
481    ///
482    /// C++ equivalent: `CompilerTask* newRenderPipelineState(const PipelineDescriptor*, CompilerTaskOptions*, NewRenderPipelineStateCompletionHandler)`
483    pub fn new_render_pipeline_state_async<F>(
484        &self,
485        descriptor: &PipelineDescriptor,
486        options: Option<&CompilerTaskOptions>,
487        completion_handler: F,
488    ) -> Option<CompilerTask>
489    where
490        F: Fn(Option<RenderPipelineState>, Option<mtl_foundation::Error>) + Send + 'static,
491    {
492        let block =
493            mtl_sys::TwoArgBlock::from_fn(move |state_ptr: *mut c_void, err_ptr: *mut c_void| {
494                let state = if state_ptr.is_null() {
495                    None
496                } else {
497                    unsafe { RenderPipelineState::from_raw(state_ptr) }
498                };
499
500                let error = if err_ptr.is_null() {
501                    None
502                } else {
503                    unsafe { mtl_foundation::Error::from_ptr(err_ptr) }
504                };
505
506                completion_handler(state, error);
507            });
508
509        let options_ptr = options.map_or(std::ptr::null(), |o| o.as_ptr());
510
511        unsafe {
512            let ptr: *mut c_void = msg_send_3(
513                self.as_ptr(),
514                sel!(newRenderPipelineStateWithDescriptor:compilerTaskOptions:completionHandler:),
515                descriptor.as_ptr(),
516                options_ptr,
517                block.as_ptr(),
518            );
519
520            std::mem::forget(block);
521            CompilerTask::from_raw(ptr)
522        }
523    }
524
525    /// Create a new render pipeline state by specialization asynchronously.
526    ///
527    /// C++ equivalent: `CompilerTask* newRenderPipelineStateBySpecialization(..., NewRenderPipelineStateCompletionHandler)`
528    pub fn new_render_pipeline_state_by_specialization_async<F>(
529        &self,
530        descriptor: &PipelineDescriptor,
531        pipeline: &RenderPipelineState,
532        completion_handler: F,
533    ) -> Option<CompilerTask>
534    where
535        F: Fn(Option<RenderPipelineState>, Option<mtl_foundation::Error>) + Send + 'static,
536    {
537        let block =
538            mtl_sys::TwoArgBlock::from_fn(move |state_ptr: *mut c_void, err_ptr: *mut c_void| {
539                let state = if state_ptr.is_null() {
540                    None
541                } else {
542                    unsafe { RenderPipelineState::from_raw(state_ptr) }
543                };
544
545                let error = if err_ptr.is_null() {
546                    None
547                } else {
548                    unsafe { mtl_foundation::Error::from_ptr(err_ptr) }
549                };
550
551                completion_handler(state, error);
552            });
553
554        unsafe {
555            let ptr: *mut c_void = msg_send_3(
556                self.as_ptr(),
557                sel!(newRenderPipelineStateBySpecializationWithDescriptor:pipeline:completionHandler:),
558                descriptor.as_ptr(),
559                pipeline.as_ptr(),
560                block.as_ptr(),
561            );
562
563            std::mem::forget(block);
564            CompilerTask::from_raw(ptr)
565        }
566    }
567
568    // ========== Async Dynamic Library Creation ==========
569
570    /// Create a new dynamic library from a library asynchronously.
571    ///
572    /// C++ equivalent: `CompilerTask* newDynamicLibrary(const Library*, NewDynamicLibraryCompletionHandler)`
573    pub fn new_dynamic_library_from_library_async<F>(
574        &self,
575        library: &Library,
576        completion_handler: F,
577    ) -> Option<CompilerTask>
578    where
579        F: Fn(Option<DynamicLibrary>, Option<mtl_foundation::Error>) + Send + 'static,
580    {
581        let block =
582            mtl_sys::TwoArgBlock::from_fn(move |lib_ptr: *mut c_void, err_ptr: *mut c_void| {
583                let dynamic_lib = if lib_ptr.is_null() {
584                    None
585                } else {
586                    unsafe { DynamicLibrary::from_raw(lib_ptr) }
587                };
588
589                let error = if err_ptr.is_null() {
590                    None
591                } else {
592                    unsafe { mtl_foundation::Error::from_ptr(err_ptr) }
593                };
594
595                completion_handler(dynamic_lib, error);
596            });
597
598        unsafe {
599            let ptr: *mut c_void = msg_send_2(
600                self.as_ptr(),
601                sel!(newDynamicLibrary:completionHandler:),
602                library.as_ptr(),
603                block.as_ptr(),
604            );
605
606            std::mem::forget(block);
607            CompilerTask::from_raw(ptr)
608        }
609    }
610
611    /// Create a new dynamic library from a URL asynchronously.
612    ///
613    /// C++ equivalent: `CompilerTask* newDynamicLibrary(const NS::URL*, NewDynamicLibraryCompletionHandler)`
614    ///
615    /// # Safety
616    ///
617    /// The URL pointer must be valid.
618    pub unsafe fn new_dynamic_library_from_url_async<F>(
619        &self,
620        url: *const c_void,
621        completion_handler: F,
622    ) -> Option<CompilerTask>
623    where
624        F: Fn(Option<DynamicLibrary>, Option<mtl_foundation::Error>) + Send + 'static,
625    {
626        let block =
627            mtl_sys::TwoArgBlock::from_fn(move |lib_ptr: *mut c_void, err_ptr: *mut c_void| {
628                let dynamic_lib = if lib_ptr.is_null() {
629                    None
630                } else {
631                    unsafe { DynamicLibrary::from_raw(lib_ptr) }
632                };
633
634                let error = if err_ptr.is_null() {
635                    None
636                } else {
637                    unsafe { mtl_foundation::Error::from_ptr(err_ptr) }
638                };
639
640                completion_handler(dynamic_lib, error);
641            });
642
643        unsafe {
644            let ptr: *mut c_void = msg_send_2(
645                self.as_ptr(),
646                sel!(newDynamicLibraryWithURL:completionHandler:),
647                url,
648                block.as_ptr(),
649            );
650
651            std::mem::forget(block);
652            CompilerTask::from_raw(ptr)
653        }
654    }
655
656    // ========== Async Dynamic Linking Pipeline Creation ==========
657
658    /// Create a new compute pipeline state with dynamic linking asynchronously.
659    ///
660    /// C++ equivalent: `CompilerTask* newComputePipelineState(..., dynamicLinkingDescriptor, ..., completionHandler)`
661    pub fn new_compute_pipeline_state_with_dynamic_linking_async<F>(
662        &self,
663        descriptor: &ComputePipelineDescriptor,
664        dynamic_linking: &PipelineStageDynamicLinkingDescriptor,
665        options: Option<&CompilerTaskOptions>,
666        completion_handler: F,
667    ) -> Option<CompilerTask>
668    where
669        F: Fn(Option<ComputePipelineState>, Option<mtl_foundation::Error>) + Send + 'static,
670    {
671        let block =
672            mtl_sys::TwoArgBlock::from_fn(move |state_ptr: *mut c_void, err_ptr: *mut c_void| {
673                let state = if state_ptr.is_null() {
674                    None
675                } else {
676                    unsafe { ComputePipelineState::from_raw(state_ptr) }
677                };
678
679                let error = if err_ptr.is_null() {
680                    None
681                } else {
682                    unsafe { mtl_foundation::Error::from_ptr(err_ptr) }
683                };
684
685                completion_handler(state, error);
686            });
687
688        let options_ptr = options.map_or(std::ptr::null(), |o| o.as_ptr());
689
690        unsafe {
691            let ptr: *mut c_void = msg_send_4(
692                self.as_ptr(),
693                sel!(newComputePipelineStateWithDescriptor:dynamicLinkingDescriptor:compilerTaskOptions:completionHandler:),
694                descriptor.as_ptr(),
695                dynamic_linking.as_ptr(),
696                options_ptr,
697                block.as_ptr(),
698            );
699
700            std::mem::forget(block);
701            CompilerTask::from_raw(ptr)
702        }
703    }
704
705    /// Create a new render pipeline state with dynamic linking asynchronously.
706    ///
707    /// C++ equivalent: `CompilerTask* newRenderPipelineState(..., dynamicLinkingDescriptor, ..., completionHandler)`
708    pub fn new_render_pipeline_state_with_dynamic_linking_async<F>(
709        &self,
710        descriptor: &PipelineDescriptor,
711        dynamic_linking: &RenderPipelineDynamicLinkingDescriptor,
712        options: Option<&CompilerTaskOptions>,
713        completion_handler: F,
714    ) -> Option<CompilerTask>
715    where
716        F: Fn(Option<RenderPipelineState>, Option<mtl_foundation::Error>) + Send + 'static,
717    {
718        let block =
719            mtl_sys::TwoArgBlock::from_fn(move |state_ptr: *mut c_void, err_ptr: *mut c_void| {
720                let state = if state_ptr.is_null() {
721                    None
722                } else {
723                    unsafe { RenderPipelineState::from_raw(state_ptr) }
724                };
725
726                let error = if err_ptr.is_null() {
727                    None
728                } else {
729                    unsafe { mtl_foundation::Error::from_ptr(err_ptr) }
730                };
731
732                completion_handler(state, error);
733            });
734
735        let options_ptr = options.map_or(std::ptr::null(), |o| o.as_ptr());
736
737        unsafe {
738            let ptr: *mut c_void = msg_send_4(
739                self.as_ptr(),
740                sel!(newRenderPipelineStateWithDescriptor:dynamicLinkingDescriptor:compilerTaskOptions:completionHandler:),
741                descriptor.as_ptr(),
742                dynamic_linking.as_ptr(),
743                options_ptr,
744                block.as_ptr(),
745            );
746
747            std::mem::forget(block);
748            CompilerTask::from_raw(ptr)
749        }
750    }
751
752    // ========== Machine Learning Pipeline Creation ==========
753
754    /// Create a new machine learning pipeline state synchronously.
755    ///
756    /// C++ equivalent: `MTL4::MachineLearningPipelineState* newMachineLearningPipelineState(..., NS::Error**)`
757    pub fn new_machine_learning_pipeline_state(
758        &self,
759        descriptor: &MachineLearningPipelineDescriptor,
760        options: Option<&CompilerTaskOptions>,
761    ) -> Result<MachineLearningPipelineState, mtl_foundation::Error> {
762        unsafe {
763            let mut error: *mut c_void = std::ptr::null_mut();
764            let options_ptr = options.map_or(std::ptr::null(), |o| o.as_ptr());
765            let ptr: *mut c_void = msg_send_3(
766                self.as_ptr(),
767                sel!(newMachineLearningPipelineStateWithDescriptor:compilerTaskOptions:error:),
768                descriptor.as_ptr(),
769                options_ptr,
770                &mut error as *mut _,
771            );
772            if !error.is_null() {
773                if let Some(err) = mtl_foundation::Error::from_ptr(error) {
774                    return Err(err);
775                }
776            }
777            MachineLearningPipelineState::from_raw(ptr).ok_or_else(generic_error)
778        }
779    }
780
781    /// Create a new machine learning pipeline state asynchronously.
782    ///
783    /// C++ equivalent: `CompilerTask* newMachineLearningPipelineState(..., completionHandler)`
784    pub fn new_machine_learning_pipeline_state_async<F>(
785        &self,
786        descriptor: &MachineLearningPipelineDescriptor,
787        options: Option<&CompilerTaskOptions>,
788        completion_handler: F,
789    ) -> Option<CompilerTask>
790    where
791        F: Fn(Option<MachineLearningPipelineState>, Option<mtl_foundation::Error>)
792            + Send
793            + 'static,
794    {
795        let block =
796            mtl_sys::TwoArgBlock::from_fn(move |state_ptr: *mut c_void, err_ptr: *mut c_void| {
797                let state = if state_ptr.is_null() {
798                    None
799                } else {
800                    unsafe { MachineLearningPipelineState::from_raw(state_ptr) }
801                };
802
803                let error = if err_ptr.is_null() {
804                    None
805                } else {
806                    unsafe { mtl_foundation::Error::from_ptr(err_ptr) }
807                };
808
809                completion_handler(state, error);
810            });
811
812        let options_ptr = options.map_or(std::ptr::null(), |o| o.as_ptr());
813
814        unsafe {
815            let ptr: *mut c_void = msg_send_3(
816                self.as_ptr(),
817                sel!(newMachineLearningPipelineStateWithDescriptor:compilerTaskOptions:completionHandler:),
818                descriptor.as_ptr(),
819                options_ptr,
820                block.as_ptr(),
821            );
822
823            std::mem::forget(block);
824            CompilerTask::from_raw(ptr)
825        }
826    }
827}
828
829impl Clone for Compiler {
830    fn clone(&self) -> Self {
831        unsafe {
832            mtl_sys::msg_send_0::<*mut c_void>(self.as_ptr(), mtl_sys::sel!(retain));
833        }
834        Self(self.0)
835    }
836}
837
838impl Drop for Compiler {
839    fn drop(&mut self) {
840        unsafe {
841            mtl_sys::msg_send_0::<()>(self.as_ptr(), mtl_sys::sel!(release));
842        }
843    }
844}
845
846impl Referencing for Compiler {
847    #[inline]
848    fn as_ptr(&self) -> *const c_void {
849        self.0.as_ptr()
850    }
851}
852
853unsafe impl Send for Compiler {}
854unsafe impl Sync for Compiler {}
855
856impl std::fmt::Debug for Compiler {
857    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
858        f.debug_struct("Compiler")
859            .field("label", &self.label())
860            .finish()
861    }
862}