1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
use crate::{ffi, util::take_optional_string, MetalDevice};
use core::ffi::c_void;
use std::ffi::CString;
macro_rules! opaque_state {
($(#[$meta:meta])* pub struct $name:ident;) => {
$(#[$meta])*
/// Mirrors the `Metal` framework counterpart for this type.
pub struct $name {
ptr: *mut c_void,
}
impl Drop for $name {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe { ffi::am_object_release(self.ptr) };
self.ptr = core::ptr::null_mut();
}
}
}
impl $name {
/// Mirrors the `Metal` framework constant `fn`.
#[must_use]
pub const fn as_ptr(&self) -> *mut c_void {
self.ptr
}
fn wrap(ptr: *mut c_void) -> Option<Self> {
if ptr.is_null() {
None
} else {
Some(Self { ptr })
}
}
}
};
}
/// `MTLCompareFunction` enum values.
pub mod compare_function {
/// Mirrors the `Metal` framework constant `NEVER`.
pub const NEVER: usize = 0;
/// Mirrors the `Metal` framework constant `LESS`.
pub const LESS: usize = 1;
/// Mirrors the `Metal` framework constant `EQUAL`.
pub const EQUAL: usize = 2;
/// Mirrors the `Metal` framework constant `LESS_EQUAL`.
pub const LESS_EQUAL: usize = 3;
/// Mirrors the `Metal` framework constant `GREATER`.
pub const GREATER: usize = 4;
/// Mirrors the `Metal` framework constant `NOT_EQUAL`.
pub const NOT_EQUAL: usize = 5;
/// Mirrors the `Metal` framework constant `GREATER_EQUAL`.
pub const GREATER_EQUAL: usize = 6;
/// Mirrors the `Metal` framework constant `ALWAYS`.
pub const ALWAYS: usize = 7;
}
/// `MTLStencilOperation` enum values.
pub mod stencil_operation {
/// Mirrors the `Metal` framework constant `KEEP`.
pub const KEEP: usize = 0;
/// Mirrors the `Metal` framework constant `ZERO`.
pub const ZERO: usize = 1;
/// Mirrors the `Metal` framework constant `REPLACE`.
pub const REPLACE: usize = 2;
/// Mirrors the `Metal` framework constant `INCREMENT_CLAMP`.
pub const INCREMENT_CLAMP: usize = 3;
/// Mirrors the `Metal` framework constant `DECREMENT_CLAMP`.
pub const DECREMENT_CLAMP: usize = 4;
/// Mirrors the `Metal` framework constant `INVERT`.
pub const INVERT: usize = 5;
/// Mirrors the `Metal` framework constant `INCREMENT_WRAP`.
pub const INCREMENT_WRAP: usize = 6;
/// Mirrors the `Metal` framework constant `DECREMENT_WRAP`.
pub const DECREMENT_WRAP: usize = 7;
}
/// `MTLSamplerMinMagFilter` enum values.
pub mod sampler_min_mag_filter {
/// Mirrors the `Metal` framework constant `NEAREST`.
pub const NEAREST: usize = 0;
/// Mirrors the `Metal` framework constant `LINEAR`.
pub const LINEAR: usize = 1;
}
/// `MTLSamplerMipFilter` enum values.
pub mod sampler_mip_filter {
/// Mirrors the `Metal` framework constant `NOT_MIPMAPPED`.
pub const NOT_MIPMAPPED: usize = 0;
/// Mirrors the `Metal` framework constant `NEAREST`.
pub const NEAREST: usize = 1;
/// Mirrors the `Metal` framework constant `LINEAR`.
pub const LINEAR: usize = 2;
}
/// `MTLSamplerAddressMode` enum values.
pub mod sampler_address_mode {
/// Mirrors the `Metal` framework constant `CLAMP_TO_EDGE`.
pub const CLAMP_TO_EDGE: usize = 0;
/// Mirrors the `Metal` framework constant `MIRROR_CLAMP_TO_EDGE`.
pub const MIRROR_CLAMP_TO_EDGE: usize = 1;
/// Mirrors the `Metal` framework constant `REPEAT`.
pub const REPEAT: usize = 2;
/// Mirrors the `Metal` framework constant `MIRROR_REPEAT`.
pub const MIRROR_REPEAT: usize = 3;
/// Mirrors the `Metal` framework constant `CLAMP_TO_ZERO`.
pub const CLAMP_TO_ZERO: usize = 4;
/// Mirrors the `Metal` framework constant `CLAMP_TO_BORDER_COLOR`.
pub const CLAMP_TO_BORDER_COLOR: usize = 5;
}
/// `MTLSamplerBorderColor` enum values.
pub mod sampler_border_color {
/// Mirrors the `Metal` framework constant `TRANSPARENT_BLACK`.
pub const TRANSPARENT_BLACK: usize = 0;
/// Mirrors the `Metal` framework constant `OPAQUE_BLACK`.
pub const OPAQUE_BLACK: usize = 1;
/// Mirrors the `Metal` framework constant `OPAQUE_WHITE`.
pub const OPAQUE_WHITE: usize = 2;
}
/// `MTLSamplerReductionMode` enum values.
pub mod sampler_reduction_mode {
/// Mirrors the `Metal` framework constant `WEIGHTED_AVERAGE`.
pub const WEIGHTED_AVERAGE: usize = 0;
/// Mirrors the `Metal` framework constant `MINIMUM`.
pub const MINIMUM: usize = 1;
/// Mirrors the `Metal` framework constant `MAXIMUM`.
pub const MAXIMUM: usize = 2;
}
/// Rust description of `MTLStencilDescriptor`.
#[derive(Debug, Clone, Copy)]
pub struct StencilDescriptor {
/// Mirrors the `Metal` framework property for `stencil_compare_function`.
pub stencil_compare_function: usize,
/// Mirrors the `Metal` framework property for `stencil_failure_operation`.
pub stencil_failure_operation: usize,
/// Mirrors the `Metal` framework property for `depth_failure_operation`.
pub depth_failure_operation: usize,
/// Mirrors the `Metal` framework property for `depth_stencil_pass_operation`.
pub depth_stencil_pass_operation: usize,
/// Mirrors the `Metal` framework property for `read_mask`.
pub read_mask: u32,
/// Mirrors the `Metal` framework property for `write_mask`.
pub write_mask: u32,
}
impl Default for StencilDescriptor {
fn default() -> Self {
Self {
stencil_compare_function: compare_function::ALWAYS,
stencil_failure_operation: stencil_operation::KEEP,
depth_failure_operation: stencil_operation::KEEP,
depth_stencil_pass_operation: stencil_operation::KEEP,
read_mask: u32::MAX,
write_mask: u32::MAX,
}
}
}
impl StencilDescriptor {
/// Create a descriptor with Metal's default compare and update behavior.
#[must_use]
pub const fn new() -> Self {
Self {
stencil_compare_function: compare_function::ALWAYS,
stencil_failure_operation: stencil_operation::KEEP,
depth_failure_operation: stencil_operation::KEEP,
depth_stencil_pass_operation: stencil_operation::KEEP,
read_mask: u32::MAX,
write_mask: u32::MAX,
}
}
}
/// Rust description of `MTLDepthStencilDescriptor`.
#[derive(Debug, Clone, Default)]
pub struct DepthStencilDescriptor {
/// Mirrors the `Metal` framework property for `depth_compare_function`.
pub depth_compare_function: usize,
/// Mirrors the `Metal` framework property for `depth_write_enabled`.
pub depth_write_enabled: bool,
/// Mirrors the `Metal` framework property for `front_face_stencil`.
pub front_face_stencil: Option<StencilDescriptor>,
/// Mirrors the `Metal` framework property for `back_face_stencil`.
pub back_face_stencil: Option<StencilDescriptor>,
/// Mirrors the `Metal` framework property for `label`.
pub label: Option<String>,
}
impl DepthStencilDescriptor {
/// Create a descriptor with Metal's default depth-test behavior.
#[must_use]
pub const fn new() -> Self {
Self {
depth_compare_function: compare_function::ALWAYS,
depth_write_enabled: false,
front_face_stencil: None,
back_face_stencil: None,
label: None,
}
}
}
/// Rust description of `MTLSamplerDescriptor`.
#[derive(Debug, Clone)]
pub struct SamplerDescriptor {
/// Mirrors the `Metal` framework property for `min_filter`.
pub min_filter: usize,
/// Mirrors the `Metal` framework property for `mag_filter`.
pub mag_filter: usize,
/// Mirrors the `Metal` framework property for `mip_filter`.
pub mip_filter: usize,
/// Mirrors the `Metal` framework property for `max_anisotropy`.
pub max_anisotropy: usize,
/// Mirrors the `Metal` framework property for `s_address_mode`.
pub s_address_mode: usize,
/// Mirrors the `Metal` framework property for `t_address_mode`.
pub t_address_mode: usize,
/// Mirrors the `Metal` framework property for `r_address_mode`.
pub r_address_mode: usize,
/// Mirrors the `Metal` framework property for `border_color`.
pub border_color: usize,
/// Mirrors the `Metal` framework property for `reduction_mode`.
pub reduction_mode: usize,
/// Mirrors the `Metal` framework property for `normalized_coordinates`.
pub normalized_coordinates: bool,
/// Mirrors the `Metal` framework property for `lod_min_clamp`.
pub lod_min_clamp: f32,
/// Mirrors the `Metal` framework property for `lod_max_clamp`.
pub lod_max_clamp: f32,
/// Mirrors the `Metal` framework property for `lod_average`.
pub lod_average: bool,
/// Mirrors the `Metal` framework property for `lod_bias`.
pub lod_bias: f32,
/// Mirrors the `Metal` framework property for `compare_function`.
pub compare_function: usize,
/// Mirrors the `Metal` framework property for `support_argument_buffers`.
pub support_argument_buffers: bool,
/// Mirrors the `Metal` framework property for `label`.
pub label: Option<String>,
}
impl Default for SamplerDescriptor {
fn default() -> Self {
Self {
min_filter: sampler_min_mag_filter::NEAREST,
mag_filter: sampler_min_mag_filter::NEAREST,
mip_filter: sampler_mip_filter::NOT_MIPMAPPED,
max_anisotropy: 1,
s_address_mode: sampler_address_mode::CLAMP_TO_EDGE,
t_address_mode: sampler_address_mode::CLAMP_TO_EDGE,
r_address_mode: sampler_address_mode::CLAMP_TO_EDGE,
border_color: sampler_border_color::TRANSPARENT_BLACK,
reduction_mode: sampler_reduction_mode::WEIGHTED_AVERAGE,
normalized_coordinates: true,
lod_min_clamp: 0.0,
lod_max_clamp: f32::MAX,
lod_average: false,
lod_bias: 0.0,
compare_function: compare_function::NEVER,
support_argument_buffers: false,
label: None,
}
}
}
impl SamplerDescriptor {
/// Create a descriptor with Metal's default sampling behavior.
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
opaque_state!(
/// Apple's `id<MTLDepthStencilState>` — compiled depth/stencil test state.
pub struct DepthStencilState;
);
opaque_state!(
/// Apple's `id<MTLSamplerState>` — immutable texture-sampling state.
pub struct SamplerState;
);
impl DepthStencilState {
/// Metal's label for this state object, if one was set.
#[must_use]
pub fn label(&self) -> Option<String> {
unsafe { take_optional_string(ffi::am_object_copy_label(self.as_ptr())) }
}
}
impl SamplerState {
/// Metal's label for this state object, if one was set.
#[must_use]
pub fn label(&self) -> Option<String> {
unsafe { take_optional_string(ffi::am_object_copy_label(self.as_ptr())) }
}
}
impl MetalDevice {
/// Query the device for the supported argument-buffer tier.
#[must_use]
pub fn argument_buffers_support(&self) -> usize {
unsafe { ffi::am_device_argument_buffers_support(self.as_ptr()) }
}
/// Compile a `MTLDepthStencilState` from the given descriptor.
#[must_use]
pub fn new_depth_stencil_state(
&self,
descriptor: &DepthStencilDescriptor,
) -> Option<DepthStencilState> {
let label = descriptor
.label
.as_deref()
.and_then(|value| CString::new(value).ok());
let label_ptr = label
.as_deref()
.map_or(core::ptr::null(), core::ffi::CStr::as_ptr);
let front = descriptor.front_face_stencil.unwrap_or_default();
let back = descriptor.back_face_stencil.unwrap_or_default();
DepthStencilState::wrap(unsafe {
ffi::am_device_new_depth_stencil_state(
self.as_ptr(),
descriptor.depth_compare_function,
descriptor.depth_write_enabled,
descriptor.front_face_stencil.is_some(),
front.stencil_compare_function,
front.stencil_failure_operation,
front.depth_failure_operation,
front.depth_stencil_pass_operation,
front.read_mask,
front.write_mask,
descriptor.back_face_stencil.is_some(),
back.stencil_compare_function,
back.stencil_failure_operation,
back.depth_failure_operation,
back.depth_stencil_pass_operation,
back.read_mask,
back.write_mask,
label_ptr,
)
})
}
/// Compile a `MTLSamplerState` from the given descriptor.
#[must_use]
pub fn new_sampler_state(&self, descriptor: &SamplerDescriptor) -> Option<SamplerState> {
let label = descriptor
.label
.as_deref()
.and_then(|value| CString::new(value).ok());
let label_ptr = label
.as_deref()
.map_or(core::ptr::null(), core::ffi::CStr::as_ptr);
SamplerState::wrap(unsafe {
ffi::am_device_new_sampler_state(
self.as_ptr(),
descriptor.min_filter,
descriptor.mag_filter,
descriptor.mip_filter,
descriptor.max_anisotropy,
descriptor.s_address_mode,
descriptor.t_address_mode,
descriptor.r_address_mode,
descriptor.border_color,
descriptor.reduction_mode,
descriptor.normalized_coordinates,
descriptor.lod_min_clamp,
descriptor.lod_max_clamp,
descriptor.lod_average,
descriptor.lod_bias,
descriptor.compare_function,
descriptor.support_argument_buffers,
label_ptr,
)
})
}
}