1use crate::{
2 ffi, util::take_optional_string, ComputePipelineState, MetalDevice, MetalFunction,
3 RenderPipelineState,
4};
5
6pub mod blend_factor {
8 pub const ZERO: usize = 0;
10 pub const ONE: usize = 1;
12 pub const SOURCE_COLOR: usize = 2;
14 pub const ONE_MINUS_SOURCE_COLOR: usize = 3;
16 pub const SOURCE_ALPHA: usize = 4;
18 pub const ONE_MINUS_SOURCE_ALPHA: usize = 5;
20 pub const DESTINATION_COLOR: usize = 6;
22 pub const ONE_MINUS_DESTINATION_COLOR: usize = 7;
24 pub const DESTINATION_ALPHA: usize = 8;
26 pub const ONE_MINUS_DESTINATION_ALPHA: usize = 9;
28 pub const SOURCE_ALPHA_SATURATED: usize = 10;
30 pub const BLEND_COLOR: usize = 11;
32 pub const ONE_MINUS_BLEND_COLOR: usize = 12;
34 pub const BLEND_ALPHA: usize = 13;
36 pub const ONE_MINUS_BLEND_ALPHA: usize = 14;
38 pub const SOURCE1_COLOR: usize = 15;
40 pub const ONE_MINUS_SOURCE1_COLOR: usize = 16;
42 pub const SOURCE1_ALPHA: usize = 17;
44 pub const ONE_MINUS_SOURCE1_ALPHA: usize = 18;
46 pub const UNSPECIALIZED: usize = 19;
48}
49
50pub mod blend_operation {
52 pub const ADD: usize = 0;
54 pub const SUBTRACT: usize = 1;
56 pub const REVERSE_SUBTRACT: usize = 2;
58 pub const MIN: usize = 3;
60 pub const MAX: usize = 4;
62 pub const UNSPECIALIZED: usize = 5;
64}
65
66pub mod color_write_mask {
68 pub const NONE: usize = 0;
70 pub const RED: usize = 0x1 << 3;
72 pub const GREEN: usize = 0x1 << 2;
74 pub const BLUE: usize = 0x1 << 1;
76 pub const ALPHA: usize = 0x1 << 0;
78 pub const ALL: usize = 0xf;
80 pub const UNSPECIALIZED: usize = 0x10;
82}
83
84#[derive(Clone, Copy)]
86pub struct ComputePipelineDescriptor<'a> {
87 pub label: Option<&'a str>,
89 pub compute_function: &'a MetalFunction,
91 pub thread_group_size_is_multiple_of_thread_execution_width: bool,
93 pub max_total_threads_per_threadgroup: usize,
95 pub support_indirect_command_buffers: bool,
97}
98
99impl<'a> ComputePipelineDescriptor<'a> {
100 #[must_use]
102 pub const fn new(compute_function: &'a MetalFunction) -> Self {
103 Self {
104 label: None,
105 compute_function,
106 thread_group_size_is_multiple_of_thread_execution_width: false,
107 max_total_threads_per_threadgroup: 0,
108 support_indirect_command_buffers: false,
109 }
110 }
111}
112
113#[derive(Debug, Clone, Copy)]
115pub struct RenderPipelineColorAttachmentDescriptor {
116 pub pixel_format: usize,
118 pub blending_enabled: bool,
120 pub source_rgb_blend_factor: usize,
122 pub destination_rgb_blend_factor: usize,
124 pub rgb_blend_operation: usize,
126 pub source_alpha_blend_factor: usize,
128 pub destination_alpha_blend_factor: usize,
130 pub alpha_blend_operation: usize,
132 pub write_mask: usize,
134}
135
136impl Default for RenderPipelineColorAttachmentDescriptor {
137 fn default() -> Self {
138 Self {
139 pixel_format: 0,
140 blending_enabled: false,
141 source_rgb_blend_factor: blend_factor::ONE,
142 destination_rgb_blend_factor: blend_factor::ZERO,
143 rgb_blend_operation: blend_operation::ADD,
144 source_alpha_blend_factor: blend_factor::ONE,
145 destination_alpha_blend_factor: blend_factor::ZERO,
146 alpha_blend_operation: blend_operation::ADD,
147 write_mask: color_write_mask::ALL,
148 }
149 }
150}
151
152impl RenderPipelineColorAttachmentDescriptor {
153 #[must_use]
155 pub const fn new(pixel_format: usize) -> Self {
156 Self {
157 pixel_format,
158 blending_enabled: false,
159 source_rgb_blend_factor: blend_factor::ONE,
160 destination_rgb_blend_factor: blend_factor::ZERO,
161 rgb_blend_operation: blend_operation::ADD,
162 source_alpha_blend_factor: blend_factor::ONE,
163 destination_alpha_blend_factor: blend_factor::ZERO,
164 alpha_blend_operation: blend_operation::ADD,
165 write_mask: color_write_mask::ALL,
166 }
167 }
168
169 const fn as_words(self) -> [usize; 9] {
170 [
171 self.pixel_format,
172 self.blending_enabled as usize,
173 self.source_rgb_blend_factor,
174 self.destination_rgb_blend_factor,
175 self.rgb_blend_operation,
176 self.source_alpha_blend_factor,
177 self.destination_alpha_blend_factor,
178 self.alpha_blend_operation,
179 self.write_mask,
180 ]
181 }
182}
183
184#[allow(clippy::struct_excessive_bools)]
186#[derive(Clone, Copy)]
187pub struct RenderPipelineDescriptor<'a> {
188 pub label: Option<&'a str>,
190 pub vertex_function: &'a MetalFunction,
192 pub fragment_function: Option<&'a MetalFunction>,
194 pub color_attachments: &'a [RenderPipelineColorAttachmentDescriptor],
196 pub raster_sample_count: usize,
198 pub alpha_to_coverage_enabled: bool,
200 pub alpha_to_one_enabled: bool,
202 pub rasterization_enabled: bool,
204 pub support_indirect_command_buffers: bool,
206 pub depth_attachment_pixel_format: usize,
208 pub stencil_attachment_pixel_format: usize,
210}
211
212impl<'a> RenderPipelineDescriptor<'a> {
213 #[must_use]
215 pub const fn new(
216 vertex_function: &'a MetalFunction,
217 fragment_function: Option<&'a MetalFunction>,
218 color_attachments: &'a [RenderPipelineColorAttachmentDescriptor],
219 ) -> Self {
220 Self {
221 label: None,
222 vertex_function,
223 fragment_function,
224 color_attachments,
225 raster_sample_count: 1,
226 alpha_to_coverage_enabled: false,
227 alpha_to_one_enabled: false,
228 rasterization_enabled: true,
229 support_indirect_command_buffers: false,
230 depth_attachment_pixel_format: 0,
231 stencil_attachment_pixel_format: 0,
232 }
233 }
234}
235
236#[derive(Debug, Clone, Copy)]
238pub struct TileRenderPipelineColorAttachmentDescriptor {
239 pub pixel_format: usize,
241}
242
243impl TileRenderPipelineColorAttachmentDescriptor {
244 #[must_use]
246 pub const fn new(pixel_format: usize) -> Self {
247 Self { pixel_format }
248 }
249}
250
251#[derive(Clone, Copy)]
253pub struct TileRenderPipelineDescriptor<'a> {
254 pub label: Option<&'a str>,
256 pub tile_function: &'a MetalFunction,
258 pub color_attachments: &'a [TileRenderPipelineColorAttachmentDescriptor],
260 pub raster_sample_count: usize,
262 pub threadgroup_size_matches_tile_size: bool,
264 pub max_total_threads_per_threadgroup: usize,
266}
267
268impl<'a> TileRenderPipelineDescriptor<'a> {
269 #[must_use]
271 pub const fn new(
272 tile_function: &'a MetalFunction,
273 color_attachments: &'a [TileRenderPipelineColorAttachmentDescriptor],
274 ) -> Self {
275 Self {
276 label: None,
277 tile_function,
278 color_attachments,
279 raster_sample_count: 1,
280 threadgroup_size_matches_tile_size: false,
281 max_total_threads_per_threadgroup: 0,
282 }
283 }
284}
285
286fn flatten_render_color_attachments(
287 color_attachments: &[RenderPipelineColorAttachmentDescriptor],
288) -> Result<Vec<usize>, String> {
289 if color_attachments.len() > 8 {
290 return Err("Metal render pipelines support at most 8 color attachments".to_string());
291 }
292
293 let mut flat = Vec::with_capacity(color_attachments.len().saturating_mul(9));
294 for attachment in color_attachments {
295 flat.extend_from_slice(&attachment.as_words());
296 }
297 Ok(flat)
298}
299
300fn flatten_tile_color_attachments(
301 color_attachments: &[TileRenderPipelineColorAttachmentDescriptor],
302) -> Result<Vec<usize>, String> {
303 if color_attachments.len() > 8 {
304 return Err("Metal tile pipelines support at most 8 color attachments".to_string());
305 }
306
307 let mut flat = Vec::with_capacity(color_attachments.len());
308 for attachment in color_attachments {
309 flat.push(attachment.pixel_format);
310 }
311 Ok(flat)
312}
313
314impl MetalDevice {
315 pub fn new_compute_pipeline_state_with_descriptor(
321 &self,
322 descriptor: &ComputePipelineDescriptor<'_>,
323 ) -> Result<ComputePipelineState, String> {
324 let label = descriptor
325 .label
326 .and_then(|value| std::ffi::CString::new(value).ok());
327 let label_ptr = label
328 .as_deref()
329 .map_or(core::ptr::null(), core::ffi::CStr::as_ptr);
330 let mut err: *mut core::ffi::c_char = core::ptr::null_mut();
331 let ptr = unsafe {
332 ffi::am_device_new_compute_pipeline_state_with_descriptor(
333 self.as_ptr(),
334 descriptor.compute_function.as_ptr(),
335 label_ptr,
336 descriptor.thread_group_size_is_multiple_of_thread_execution_width,
337 descriptor.max_total_threads_per_threadgroup,
338 descriptor.support_indirect_command_buffers,
339 &mut err,
340 )
341 };
342 if ptr.is_null() {
343 Err(unsafe {
344 take_optional_string(err).unwrap_or_else(|| {
345 "MTLDevice.makeComputePipelineState(descriptor:) returned nil".to_string()
346 })
347 })
348 } else {
349 Ok(unsafe { ComputePipelineState::from_retained_ptr(ptr) })
350 }
351 }
352
353 pub fn new_render_pipeline_state_with_descriptor(
359 &self,
360 descriptor: &RenderPipelineDescriptor<'_>,
361 ) -> Result<RenderPipelineState, String> {
362 let color_attachments = flatten_render_color_attachments(descriptor.color_attachments)?;
363 let label = descriptor
364 .label
365 .and_then(|value| std::ffi::CString::new(value).ok());
366 let label_ptr = label
367 .as_deref()
368 .map_or(core::ptr::null(), core::ffi::CStr::as_ptr);
369 let mut err: *mut core::ffi::c_char = core::ptr::null_mut();
370 let ptr = unsafe {
371 ffi::am_device_new_render_pipeline_state_with_descriptor(
372 self.as_ptr(),
373 descriptor.vertex_function.as_ptr(),
374 descriptor
375 .fragment_function
376 .map_or(core::ptr::null_mut(), MetalFunction::as_ptr),
377 label_ptr,
378 descriptor.raster_sample_count,
379 descriptor.alpha_to_coverage_enabled,
380 descriptor.alpha_to_one_enabled,
381 descriptor.rasterization_enabled,
382 descriptor.support_indirect_command_buffers,
383 descriptor.depth_attachment_pixel_format,
384 descriptor.stencil_attachment_pixel_format,
385 color_attachments.as_ptr(),
386 descriptor.color_attachments.len(),
387 &mut err,
388 )
389 };
390 if ptr.is_null() {
391 Err(unsafe {
392 take_optional_string(err).unwrap_or_else(|| {
393 "MTLDevice.makeRenderPipelineState(descriptor:) returned nil".to_string()
394 })
395 })
396 } else {
397 Ok(unsafe { RenderPipelineState::from_retained_ptr(ptr) })
398 }
399 }
400
401 pub fn new_tile_render_pipeline_state(
407 &self,
408 descriptor: &TileRenderPipelineDescriptor<'_>,
409 ) -> Result<RenderPipelineState, String> {
410 let color_attachments = flatten_tile_color_attachments(descriptor.color_attachments)?;
411 let label = descriptor
412 .label
413 .and_then(|value| std::ffi::CString::new(value).ok());
414 let label_ptr = label
415 .as_deref()
416 .map_or(core::ptr::null(), core::ffi::CStr::as_ptr);
417 let mut err: *mut core::ffi::c_char = core::ptr::null_mut();
418 let ptr = unsafe {
419 ffi::am_device_new_tile_render_pipeline_state(
420 self.as_ptr(),
421 descriptor.tile_function.as_ptr(),
422 label_ptr,
423 descriptor.raster_sample_count,
424 descriptor.threadgroup_size_matches_tile_size,
425 descriptor.max_total_threads_per_threadgroup,
426 color_attachments.as_ptr(),
427 descriptor.color_attachments.len(),
428 &mut err,
429 )
430 };
431 if ptr.is_null() {
432 Err(unsafe {
433 take_optional_string(err).unwrap_or_else(|| {
434 "MTLDevice.makeRenderPipelineState(tileDescriptor:) returned nil".to_string()
435 })
436 })
437 } else {
438 Ok(unsafe { RenderPipelineState::from_retained_ptr(ptr) })
439 }
440 }
441}