1use crate::{
2 ffi, util::take_optional_string, CommandBuffer, CommandQueue, ComputePipelineState,
3 CounterSampleBuffer, DepthStencilState, Event, Fence, MetalBuffer, MetalTexture,
4 RenderPipelineState, SamplerState,
5};
6use core::ffi::c_void;
7use core::ops::Range;
8
9macro_rules! opaque_encoder {
10 ($(#[$meta:meta])* pub struct $name:ident;) => {
11 $(#[$meta])*
12pub struct $name {
14 ptr: *mut c_void,
15 }
16
17 impl Drop for $name {
18 fn drop(&mut self) {
19 if !self.ptr.is_null() {
20 unsafe { ffi::am_object_release(self.ptr) };
21 self.ptr = core::ptr::null_mut();
22 }
23 }
24 }
25
26 impl $name {
27#[must_use]
29 pub const fn as_ptr(&self) -> *mut c_void {
30 self.ptr
31 }
32
33 fn wrap(ptr: *mut c_void) -> Option<Self> {
34 if ptr.is_null() {
35 None
36 } else {
37 Some(Self { ptr })
38 }
39 }
40 }
41 };
42}
43
44pub mod command_buffer_status {
46 pub const NOT_ENQUEUED: usize = 0;
48 pub const ENQUEUED: usize = 1;
50 pub const COMMITTED: usize = 2;
52 pub const SCHEDULED: usize = 3;
54 pub const COMPLETED: usize = 4;
56 pub const ERROR: usize = 5;
58}
59
60opaque_encoder!(
61 pub struct BlitCommandEncoder;
63);
64opaque_encoder!(
65 pub struct ComputeCommandEncoder;
67);
68opaque_encoder!(
69 pub struct RenderCommandEncoder;
71);
72
73impl CommandQueue {
74 #[must_use]
76 pub fn new_command_buffer_with_unretained_references(&self) -> Option<CommandBuffer> {
77 let ptr = unsafe {
78 ffi::am_command_queue_new_command_buffer_with_unretained_references(self.as_ptr())
79 };
80 if ptr.is_null() {
81 None
82 } else {
83 Some(unsafe { CommandBuffer::from_retained_ptr(ptr) })
84 }
85 }
86}
87
88impl CommandBuffer {
89 pub fn enqueue(&self) {
91 unsafe { ffi::am_command_buffer_enqueue(self.as_ptr()) };
92 }
93
94 pub fn wait_until_scheduled(&self) {
96 unsafe { ffi::am_command_buffer_wait_until_scheduled(self.as_ptr()) };
97 }
98
99 #[must_use]
101 pub fn status(&self) -> usize {
102 unsafe { ffi::am_command_buffer_status(self.as_ptr()) }
103 }
104
105 #[must_use]
107 pub fn error(&self) -> Option<String> {
108 unsafe { take_optional_string(ffi::am_command_buffer_error_message(self.as_ptr())) }
109 }
110
111 #[must_use]
113 pub fn new_blit_command_encoder(&self) -> Option<BlitCommandEncoder> {
114 BlitCommandEncoder::wrap(unsafe {
115 ffi::am_command_buffer_new_blit_command_encoder(self.as_ptr())
116 })
117 }
118
119 #[must_use]
121 pub fn new_compute_command_encoder(&self) -> Option<ComputeCommandEncoder> {
122 ComputeCommandEncoder::wrap(unsafe {
123 ffi::am_command_buffer_new_compute_command_encoder(self.as_ptr())
124 })
125 }
126
127 #[must_use]
129 pub fn new_render_command_encoder(
130 &self,
131 texture: &MetalTexture,
132 load_action: usize,
133 store_action: usize,
134 clear_color: [f64; 4],
135 ) -> Option<RenderCommandEncoder> {
136 RenderCommandEncoder::wrap(unsafe {
137 ffi::am_command_buffer_new_render_command_encoder(
138 self.as_ptr(),
139 texture.as_ptr(),
140 load_action,
141 store_action,
142 clear_color[0],
143 clear_color[1],
144 clear_color[2],
145 clear_color[3],
146 )
147 })
148 }
149
150 pub fn encode_wait_for_event(&self, event: &Event, value: u64) {
152 unsafe {
153 ffi::am_command_buffer_encode_wait_for_event(self.as_ptr(), event.as_ptr(), value);
154 };
155 }
156
157 pub fn encode_signal_event(&self, event: &Event, value: u64) {
159 unsafe { ffi::am_command_buffer_encode_signal_event(self.as_ptr(), event.as_ptr(), value) };
160 }
161}
162
163impl BlitCommandEncoder {
164 #[must_use]
166 pub fn copy_buffer(
167 &self,
168 src: &MetalBuffer,
169 src_offset: usize,
170 dst: &MetalBuffer,
171 dst_offset: usize,
172 size: usize,
173 ) -> bool {
174 unsafe {
175 ffi::am_blit_command_encoder_copy_buffer(
176 self.as_ptr(),
177 src.as_ptr(),
178 src_offset,
179 dst.as_ptr(),
180 dst_offset,
181 size,
182 )
183 }
184 }
185
186 #[must_use]
188 pub fn fill_buffer(&self, buffer: &MetalBuffer, range: Range<usize>, value: u8) -> bool {
189 let length = range.end.saturating_sub(range.start);
190 unsafe {
191 ffi::am_blit_command_encoder_fill_buffer(
192 self.as_ptr(),
193 buffer.as_ptr(),
194 range.start,
195 length,
196 value,
197 )
198 }
199 }
200
201 #[must_use]
203 pub fn sample_counters(
204 &self,
205 sample_buffer: &CounterSampleBuffer,
206 sample_index: usize,
207 barrier: bool,
208 ) -> bool {
209 unsafe {
210 ffi::am_blit_command_encoder_sample_counters(
211 self.as_ptr(),
212 sample_buffer.as_ptr(),
213 sample_index,
214 barrier,
215 )
216 }
217 }
218
219 pub fn update_fence(&self, fence: &Fence) {
221 unsafe { ffi::am_blit_command_encoder_update_fence(self.as_ptr(), fence.as_ptr()) };
222 }
223
224 pub fn wait_for_fence(&self, fence: &Fence) {
226 unsafe { ffi::am_blit_command_encoder_wait_for_fence(self.as_ptr(), fence.as_ptr()) };
227 }
228
229 pub fn end_encoding(&self) {
231 unsafe { ffi::am_command_encoder_end_encoding(self.as_ptr()) };
232 }
233}
234
235impl ComputeCommandEncoder {
236 pub fn set_compute_pipeline_state(&self, pipeline: &ComputePipelineState) {
238 unsafe {
239 ffi::am_compute_command_encoder_set_pipeline_state(self.as_ptr(), pipeline.as_ptr());
240 };
241 }
242
243 pub fn set_buffer(&self, buffer: &MetalBuffer, offset: usize, index: usize) {
245 unsafe {
246 ffi::am_compute_command_encoder_set_buffer(
247 self.as_ptr(),
248 buffer.as_ptr(),
249 offset,
250 index,
251 );
252 };
253 }
254
255 pub fn set_texture(&self, texture: &MetalTexture, index: usize) {
257 unsafe {
258 ffi::am_compute_command_encoder_set_texture(self.as_ptr(), texture.as_ptr(), index);
259 };
260 }
261
262 pub fn set_sampler_state(&self, sampler: &SamplerState, index: usize) {
264 unsafe {
265 ffi::am_compute_command_encoder_set_sampler_state(
266 self.as_ptr(),
267 sampler.as_ptr(),
268 index,
269 );
270 };
271 }
272
273 pub fn set_visible_function_table(&self, table: &crate::VisibleFunctionTable, index: usize) {
275 unsafe {
276 ffi::am_compute_command_encoder_set_visible_function_table(
277 self.as_ptr(),
278 table.as_ptr(),
279 index,
280 );
281 };
282 }
283
284 pub fn set_intersection_function_table(
286 &self,
287 table: &crate::IntersectionFunctionTable,
288 index: usize,
289 ) {
290 unsafe {
291 ffi::am_compute_command_encoder_set_intersection_function_table(
292 self.as_ptr(),
293 table.as_ptr(),
294 index,
295 );
296 };
297 }
298
299 pub fn set_acceleration_structure(
301 &self,
302 acceleration_structure: &crate::AccelerationStructure,
303 index: usize,
304 ) {
305 unsafe {
306 ffi::am_compute_command_encoder_set_acceleration_structure(
307 self.as_ptr(),
308 acceleration_structure.as_ptr(),
309 index,
310 );
311 };
312 }
313
314 pub fn dispatch_threadgroups(
316 &self,
317 threadgroups: (usize, usize, usize),
318 threads_per_threadgroup: (usize, usize, usize),
319 ) {
320 unsafe {
321 ffi::am_compute_command_encoder_dispatch_threadgroups(
322 self.as_ptr(),
323 threadgroups.0,
324 threadgroups.1,
325 threadgroups.2,
326 threads_per_threadgroup.0,
327 threads_per_threadgroup.1,
328 threads_per_threadgroup.2,
329 );
330 };
331 }
332
333 pub fn dispatch_threads(
335 &self,
336 threads: (usize, usize, usize),
337 threads_per_threadgroup: (usize, usize, usize),
338 ) {
339 unsafe {
340 ffi::am_compute_command_encoder_dispatch_threads(
341 self.as_ptr(),
342 threads.0,
343 threads.1,
344 threads.2,
345 threads_per_threadgroup.0,
346 threads_per_threadgroup.1,
347 threads_per_threadgroup.2,
348 );
349 };
350 }
351
352 pub fn update_fence(&self, fence: &Fence) {
354 unsafe { ffi::am_compute_command_encoder_update_fence(self.as_ptr(), fence.as_ptr()) };
355 }
356
357 pub fn wait_for_fence(&self, fence: &Fence) {
359 unsafe { ffi::am_compute_command_encoder_wait_for_fence(self.as_ptr(), fence.as_ptr()) };
360 }
361
362 pub fn end_encoding(&self) {
364 unsafe { ffi::am_command_encoder_end_encoding(self.as_ptr()) };
365 }
366}
367
368impl RenderCommandEncoder {
369 pub fn set_render_pipeline_state(&self, pipeline: &RenderPipelineState) {
371 unsafe {
372 ffi::am_render_command_encoder_set_render_pipeline_state(
373 self.as_ptr(),
374 pipeline.as_ptr(),
375 );
376 };
377 }
378
379 pub fn set_vertex_buffer(&self, buffer: &MetalBuffer, offset: usize, index: usize) {
381 unsafe {
382 ffi::am_render_command_encoder_set_vertex_buffer(
383 self.as_ptr(),
384 buffer.as_ptr(),
385 offset,
386 index,
387 );
388 };
389 }
390
391 pub fn set_fragment_sampler_state(&self, sampler: &SamplerState, index: usize) {
393 unsafe {
394 ffi::am_render_command_encoder_set_fragment_sampler_state(
395 self.as_ptr(),
396 sampler.as_ptr(),
397 index,
398 );
399 };
400 }
401
402 pub fn set_depth_stencil_state(&self, state: &DepthStencilState) {
404 unsafe {
405 ffi::am_render_command_encoder_set_depth_stencil_state(self.as_ptr(), state.as_ptr());
406 };
407 }
408
409 pub fn draw_primitives(&self, primitive_type: usize, vertex_start: usize, vertex_count: usize) {
411 unsafe {
412 ffi::am_render_command_encoder_draw_primitives(
413 self.as_ptr(),
414 primitive_type,
415 vertex_start,
416 vertex_count,
417 );
418 };
419 }
420
421 pub fn update_fence(&self, fence: &Fence) {
423 unsafe { ffi::am_render_command_encoder_update_fence(self.as_ptr(), fence.as_ptr()) };
424 }
425
426 pub fn wait_for_fence(&self, fence: &Fence) {
428 unsafe { ffi::am_render_command_encoder_wait_for_fence(self.as_ptr(), fence.as_ptr()) };
429 }
430
431 pub fn end_encoding(&self) {
433 unsafe { ffi::am_command_encoder_end_encoding(self.as_ptr()) };
434 }
435}