1use objc2::{
2 rc::{autoreleasepool, Retained},
3 runtime::ProtocolObject,
4};
5use objc2_foundation::{NSRange, NSString, NSUInteger};
6use objc2_metal::{
7 MTLAccelerationStructure, MTLAccelerationStructureCommandEncoder, MTLBlitCommandEncoder,
8 MTLBlitPassDescriptor, MTLBuffer, MTLCommandBuffer, MTLCommandBufferStatus, MTLCommandEncoder,
9 MTLCommandQueue, MTLComputeCommandEncoder, MTLComputePassDescriptor, MTLCounterDontSample,
10 MTLDevice, MTLLoadAction, MTLPrimitiveType, MTLRenderCommandEncoder, MTLRenderPassDescriptor,
11 MTLResidencySet, MTLResidencySetDescriptor, MTLSamplerState, MTLScissorRect, MTLSize,
12 MTLStoreAction, MTLTexture, MTLVertexAmplificationViewMapping, MTLViewport,
13 MTLVisibilityResultMode,
14};
15
16use super::{adapter, conv, TimestampQuerySupport};
17use crate::CommandEncoder as _;
18use alloc::{
19 borrow::{Cow, ToOwned as _},
20 sync::Arc,
21 vec::Vec,
22};
23use core::{ops::Range, ptr::NonNull, sync::atomic};
24use smallvec::SmallVec;
25
26const WORD_SIZE: usize = 4;
28
29impl Default for super::CommandState {
30 fn default() -> Self {
31 Self {
32 blit: None,
33 acceleration_structure_builder: None,
34 render: None,
35 compute: None,
36 raw_primitive_type: MTLPrimitiveType::Point,
37 index: None,
38 stage_infos: Default::default(),
39 storage_buffer_length_map: Default::default(),
40 vertex_buffer_size_map: Default::default(),
41 immediates: Vec::new(),
42 pending_timer_queries: Vec::new(),
43 }
44 }
45}
46
47enum Encoder<'e> {
52 Vertex(&'e ProtocolObject<dyn MTLRenderCommandEncoder>),
53 Fragment(&'e ProtocolObject<dyn MTLRenderCommandEncoder>),
54 Task(&'e ProtocolObject<dyn MTLRenderCommandEncoder>),
55 Mesh(&'e ProtocolObject<dyn MTLRenderCommandEncoder>),
56 Compute(&'e ProtocolObject<dyn MTLComputeCommandEncoder>),
57}
58
59impl Encoder<'_> {
60 fn stage(&self) -> naga::ShaderStage {
61 match self {
62 Self::Vertex(_) => naga::ShaderStage::Vertex,
63 Self::Fragment(_) => naga::ShaderStage::Fragment,
64 Self::Task(_) => naga::ShaderStage::Task,
65 Self::Mesh(_) => naga::ShaderStage::Mesh,
66 Self::Compute(_) => naga::ShaderStage::Compute,
67 }
68 }
69
70 fn set_buffer(
71 &self,
72 buffer: Option<&ProtocolObject<dyn MTLBuffer>>,
73 offset: NSUInteger,
74 index: NSUInteger,
75 ) {
76 unsafe {
77 match *self {
78 Self::Vertex(enc) => enc.setVertexBuffer_offset_atIndex(buffer, offset, index),
79 Self::Fragment(enc) => enc.setFragmentBuffer_offset_atIndex(buffer, offset, index),
80 Self::Task(enc) => enc.setObjectBuffer_offset_atIndex(buffer, offset, index),
81 Self::Mesh(enc) => enc.setMeshBuffer_offset_atIndex(buffer, offset, index),
82 Self::Compute(enc) => enc.setBuffer_offset_atIndex(buffer, offset, index),
83 }
84 }
85 }
86
87 fn set_acceleration_structure(
88 &self,
89 buffer: Option<&ProtocolObject<dyn MTLAccelerationStructure>>,
90 index: NSUInteger,
91 ) {
92 unsafe {
93 match *self {
94 Self::Vertex(enc) => {
95 enc.setVertexAccelerationStructure_atBufferIndex(buffer, index)
96 }
97 Self::Fragment(enc) => {
98 enc.setFragmentAccelerationStructure_atBufferIndex(buffer, index)
99 }
100 Self::Task(_) => {
101 unreachable!("Acceleration structures are not allowed in task shaders")
102 }
103 Self::Mesh(_) => {
104 unreachable!("Acceleration structures are not allowed in mesh shaders")
105 }
106 Self::Compute(enc) => enc.setAccelerationStructure_atBufferIndex(buffer, index),
107 }
108 }
109 }
110
111 fn set_bytes(&self, bytes: NonNull<core::ffi::c_void>, length: NSUInteger, index: NSUInteger) {
112 unsafe {
113 match *self {
114 Self::Vertex(enc) => enc.setVertexBytes_length_atIndex(bytes, length, index),
115 Self::Fragment(enc) => enc.setFragmentBytes_length_atIndex(bytes, length, index),
116 Self::Task(enc) => enc.setObjectBytes_length_atIndex(bytes, length, index),
117 Self::Mesh(enc) => enc.setMeshBytes_length_atIndex(bytes, length, index),
118 Self::Compute(enc) => enc.setBytes_length_atIndex(bytes, length, index),
119 }
120 }
121 }
122
123 fn set_sampler_state(
124 &self,
125 state: Option<&ProtocolObject<dyn MTLSamplerState>>,
126 index: NSUInteger,
127 ) {
128 unsafe {
129 match *self {
130 Self::Vertex(enc) => enc.setVertexSamplerState_atIndex(state, index),
131 Self::Fragment(enc) => enc.setFragmentSamplerState_atIndex(state, index),
132 Self::Task(enc) => enc.setObjectSamplerState_atIndex(state, index),
133 Self::Mesh(enc) => enc.setMeshSamplerState_atIndex(state, index),
134 Self::Compute(enc) => enc.setSamplerState_atIndex(state, index),
135 }
136 }
137 }
138
139 fn set_texture(&self, texture: Option<&ProtocolObject<dyn MTLTexture>>, index: NSUInteger) {
140 unsafe {
141 match *self {
142 Self::Vertex(enc) => enc.setVertexTexture_atIndex(texture, index),
143 Self::Fragment(enc) => enc.setFragmentTexture_atIndex(texture, index),
144 Self::Task(enc) => enc.setObjectTexture_atIndex(texture, index),
145 Self::Mesh(enc) => enc.setMeshTexture_atIndex(texture, index),
146 Self::Compute(enc) => enc.setTexture_atIndex(texture, index),
147 }
148 }
149 }
150}
151
152impl super::CommandEncoder {
153 pub fn raw_command_buffer(&self) -> Option<&ProtocolObject<dyn MTLCommandBuffer>> {
154 self.raw_cmd_buf.as_deref()
155 }
156
157 fn enter_blit(&mut self) -> Retained<ProtocolObject<dyn MTLBlitCommandEncoder>> {
158 if self.state.blit.is_none() {
159 self.leave_acceleration_structure_builder();
160 debug_assert!(self.state.render.is_none() && self.state.compute.is_none());
161 let cmd_buf = self.raw_cmd_buf.as_ref().unwrap();
162
163 let supports_sample_counters_in_buffer = self
179 .shared
180 .private_caps
181 .timestamp_query_support
182 .contains(TimestampQuerySupport::ON_BLIT_ENCODER);
183
184 if !self.state.pending_timer_queries.is_empty() && !supports_sample_counters_in_buffer {
185 autoreleasepool(|_| {
186 let descriptor = MTLBlitPassDescriptor::new();
187 let mut last_query = None;
188 for (i, (set, index)) in self.state.pending_timer_queries.drain(..).enumerate()
189 {
190 let sba_descriptor = unsafe {
191 descriptor
192 .sampleBufferAttachments()
193 .objectAtIndexedSubscript(i)
194 };
195 sba_descriptor
196 .setSampleBuffer(Some(set.counter_sample_buffer.as_ref().unwrap()));
197
198 unsafe {
201 sba_descriptor.setStartOfEncoderSampleIndex(MTLCounterDontSample)
202 };
203 unsafe { sba_descriptor.setEndOfEncoderSampleIndex(index as _) };
204
205 last_query = Some((set, index));
206 }
207 let encoder = cmd_buf
208 .blitCommandEncoderWithDescriptor(&descriptor)
209 .unwrap();
210
211 let raw_range = NSRange {
215 location: last_query.as_ref().unwrap().1 as usize
216 * crate::QUERY_SIZE as usize,
217 length: 1,
218 };
219 encoder.fillBuffer_range_value(
220 &last_query.as_ref().unwrap().0.raw_buffer,
221 raw_range,
222 255, );
224
225 encoder.endEncoding();
226 });
227 }
228
229 autoreleasepool(|_| {
230 self.state.blit = Some(cmd_buf.blitCommandEncoder().unwrap());
231 });
232
233 #[allow(clippy::panicking_unwrap, reason = "false positive")]
235 let encoder = self.state.blit.as_ref().unwrap();
236
237 for (set, index) in self.state.pending_timer_queries.drain(..) {
240 debug_assert!(supports_sample_counters_in_buffer);
241 unsafe {
242 encoder.sampleCountersInBuffer_atSampleIndex_withBarrier(
243 set.counter_sample_buffer.as_ref().unwrap(),
244 index as _,
245 true,
246 )
247 };
248 }
249 }
250 self.state.blit.as_ref().unwrap().clone()
251 }
252
253 pub(super) fn leave_blit(&mut self) {
254 if let Some(encoder) = self.state.blit.take() {
255 encoder.endEncoding();
256 }
257 }
258
259 fn enter_acceleration_structure_builder(
260 &mut self,
261 ) -> Retained<ProtocolObject<dyn MTLAccelerationStructureCommandEncoder>> {
262 if self.state.acceleration_structure_builder.is_none() {
263 self.leave_blit();
264 debug_assert!(
265 self.state.render.is_none()
266 && self.state.compute.is_none()
267 && self.state.blit.is_none()
268 );
269 let cmd_buf = self.raw_cmd_buf.as_ref().unwrap();
270 autoreleasepool(|_| {
271 self.state.acceleration_structure_builder =
272 cmd_buf.accelerationStructureCommandEncoder().to_owned();
273 });
274 }
275 self.state.acceleration_structure_builder.clone().unwrap()
276 }
277
278 pub(super) fn leave_acceleration_structure_builder(&mut self) {
279 if let Some(encoder) = self.state.acceleration_structure_builder.take() {
280 encoder.endEncoding();
281 }
282 }
283
284 fn active_encoder(&mut self) -> Option<&ProtocolObject<dyn MTLCommandEncoder>> {
285 if let Some(ref encoder) = self.state.render {
286 Some(ProtocolObject::from_ref(&**encoder))
287 } else if let Some(ref encoder) = self.state.acceleration_structure_builder {
288 Some(ProtocolObject::from_ref(&**encoder))
289 } else if let Some(ref encoder) = self.state.compute {
290 Some(ProtocolObject::from_ref(&**encoder))
291 } else if let Some(ref encoder) = self.state.blit {
292 Some(ProtocolObject::from_ref(&**encoder))
293 } else {
294 None
295 }
296 }
297
298 fn begin_pass(&mut self) {
299 self.state.reset();
300 self.leave_blit();
301 self.leave_acceleration_structure_builder();
302 }
303
304 fn update_bind_group_state(
306 &mut self,
307 encoder: Encoder<'_>,
308 index_base: super::ResourceData<u32>,
309 bg_info: &super::BindGroupLayoutInfo,
310 dynamic_offsets: &[wgt::DynamicOffset],
311 group_index: u32,
312 group: &super::BindGroup,
313 ) {
314 use naga::ShaderStage as S;
315 let resource_indices = match encoder.stage() {
316 S::Vertex => &bg_info.base_resource_indices.vs,
317 S::Fragment => &bg_info.base_resource_indices.fs,
318 S::Task => &bg_info.base_resource_indices.ts,
319 S::Mesh => &bg_info.base_resource_indices.ms,
320 S::Compute => &bg_info.base_resource_indices.cs,
321 S::RayGeneration | S::AnyHit | S::ClosestHit | S::Miss => unimplemented!(),
322 };
323 let buffers = match encoder.stage() {
324 S::Vertex => group.counters.vs.buffers,
325 S::Fragment => group.counters.fs.buffers,
326 S::Task => group.counters.ts.buffers,
327 S::Mesh => group.counters.ms.buffers,
328 S::Compute => group.counters.cs.buffers,
329 S::RayGeneration | S::AnyHit | S::ClosestHit | S::Miss => unimplemented!(),
330 };
331 let mut changes_sizes_buffer = false;
332 for index in 0..buffers {
333 let res = &group.buffers[(index_base.buffers + index) as usize];
334 match res {
335 super::BufferLikeResource::Buffer {
336 ptr,
337 mut offset,
338 dynamic_index,
339 binding_size,
340 binding_location,
341 } => {
342 let buffer = Some(unsafe { ptr.as_ref() });
343 if let Some(dyn_index) = dynamic_index {
344 offset += dynamic_offsets[*dyn_index as usize] as wgt::BufferAddress;
345 }
346 let index = (resource_indices.buffers + index) as usize;
347 encoder.set_buffer(buffer, offset as usize, index);
348 if let Some(size) = binding_size {
349 let br = naga::ResourceBinding {
350 group: group_index,
351 binding: *binding_location,
352 };
353 self.state.storage_buffer_length_map.insert(br, *size);
354 changes_sizes_buffer = true;
355 }
356 }
357 super::BufferLikeResource::AccelerationStructure(ptr) => {
358 let buffer = Some(unsafe { ptr.as_ref() });
359 let index = (resource_indices.buffers + index) as usize;
360 encoder.set_acceleration_structure(buffer, index);
361 }
362 }
363 }
364 if changes_sizes_buffer {
365 if let Some((index, sizes)) = self
366 .state
367 .make_sizes_buffer_update(encoder.stage(), &mut self.temp.binding_sizes)
368 {
369 let bytes_ptr = NonNull::new(sizes.as_ptr().cast_mut().cast()).unwrap();
370 let length = sizes.len() * WORD_SIZE;
371 let index = index as _;
372 encoder.set_bytes(bytes_ptr, length, index);
373 }
374 }
375 let samplers = match encoder.stage() {
376 S::Vertex => group.counters.vs.samplers,
377 S::Fragment => group.counters.fs.samplers,
378 S::Task => group.counters.ts.samplers,
379 S::Mesh => group.counters.ms.samplers,
380 S::Compute => group.counters.cs.samplers,
381 S::RayGeneration | S::AnyHit | S::ClosestHit | S::Miss => unimplemented!(),
382 };
383 for index in 0..samplers {
384 let res = group.samplers[(index_base.samplers + index) as usize];
385 let index = (resource_indices.samplers + index) as usize;
386 let state = Some(unsafe { res.as_ref() });
387 encoder.set_sampler_state(state, index);
388 }
389
390 let textures = match encoder.stage() {
391 S::Vertex => group.counters.vs.textures,
392 S::Fragment => group.counters.fs.textures,
393 S::Task => group.counters.ts.textures,
394 S::Mesh => group.counters.ms.textures,
395 S::Compute => group.counters.cs.textures,
396 S::RayGeneration | S::AnyHit | S::ClosestHit | S::Miss => unimplemented!(),
397 };
398 for index in 0..textures {
399 let res = group.textures[(index_base.textures + index) as usize];
400 let index = (resource_indices.textures + index) as usize;
401 let texture = Some(unsafe { res.as_ref() });
402 encoder.set_texture(texture, index);
403 }
404 }
405}
406
407impl super::CommandState {
408 fn reset(&mut self) {
409 self.storage_buffer_length_map.clear();
410 self.vertex_buffer_size_map.clear();
411 self.stage_infos.vs.clear();
412 self.stage_infos.fs.clear();
413 self.stage_infos.cs.clear();
414 self.stage_infos.ts.clear();
415 self.stage_infos.ms.clear();
416 self.immediates.clear();
417 }
418
419 fn make_sizes_buffer_update<'a>(
420 &self,
421 stage: naga::ShaderStage,
422 result_sizes: &'a mut Vec<u32>,
423 ) -> Option<(u32, &'a [u32])> {
424 let stage_info = &self.stage_infos[stage];
425 let slot = stage_info.sizes_slot?;
426
427 result_sizes.clear();
428 result_sizes.extend(stage_info.sized_bindings.iter().map(|br| {
429 self.storage_buffer_length_map
430 .get(br)
431 .map(|size| u32::try_from(size.get()).unwrap_or(u32::MAX))
432 .unwrap_or_default()
433 }));
434
435 result_sizes.extend(stage_info.vertex_buffer_mappings.iter().map(|vbm| {
438 self.vertex_buffer_size_map
439 .get(&(vbm.id as u64))
440 .map(|size| u32::try_from(size.get()).unwrap_or(u32::MAX))
441 .unwrap_or_default()
442 }));
443
444 if !result_sizes.is_empty() {
445 Some((slot as _, result_sizes))
446 } else {
447 None
448 }
449 }
450}
451
452impl crate::CommandEncoder for super::CommandEncoder {
453 type A = super::Api;
454
455 unsafe fn begin_encoding(&mut self, label: crate::Label) -> Result<(), crate::DeviceError> {
456 let queue = &self.queue_shared.raw;
457 let retain_references = self.shared.settings.retain_command_buffer_references;
458
459 let previous = self
463 .queue_shared
464 .command_buffer_created_not_submitted
465 .fetch_add(1, atomic::Ordering::AcqRel);
466 if previous >= adapter::MAX_COMMAND_BUFFERS {
467 let current = previous + 1;
468 log::warn!(
469 "metal: refusing to create new command buffer; {current} outstanding command \
470 buffers exceeds the limit of {}. Treating this as device lost. \
471 Ensure command encoders are submitted or dropped rather than kept alive \
472 to avoid exhausting Metal's command buffer budget.",
473 adapter::MAX_COMMAND_BUFFERS
474 );
475 return Err(crate::DeviceError::Lost);
476 }
477
478 let raw = autoreleasepool(move |_| {
479 let cmd_buf_ref = if retain_references {
480 queue.commandBuffer()
481 } else {
482 queue.commandBufferWithUnretainedReferences()
483 }
484 .unwrap();
485 if let Some(label) = label {
486 cmd_buf_ref.setLabel(Some(&NSString::from_str(label)));
487 }
488 cmd_buf_ref.to_owned()
489 });
490
491 self.raw_cmd_buf = Some(raw);
492
493 Ok(())
494 }
495
496 unsafe fn discard_encoding(&mut self) {
497 self.leave_blit();
498 self.leave_acceleration_structure_builder();
499 if let Some(encoder) = self.state.render.take() {
502 encoder.endEncoding();
503 }
504 if let Some(encoder) = self.state.compute.take() {
505 encoder.endEncoding();
506 }
507 let had_command_buffer = self.raw_cmd_buf.is_some();
508 self.raw_cmd_buf = None;
511 if had_command_buffer {
512 self.queue_shared
513 .command_buffer_created_not_submitted
514 .fetch_sub(1, atomic::Ordering::AcqRel);
515 }
516 }
517
518 unsafe fn end_encoding(&mut self) -> Result<super::CommandBuffer, crate::DeviceError> {
519 if !self.state.pending_timer_queries.is_empty() {
521 self.leave_blit();
522 self.enter_blit();
523 }
524
525 self.leave_blit();
526 self.leave_acceleration_structure_builder();
527 debug_assert!(self.state.render.is_none());
528 debug_assert!(self.state.compute.is_none());
529 debug_assert!(self.state.pending_timer_queries.is_empty());
530
531 Ok(super::CommandBuffer {
532 raw: self.raw_cmd_buf.take().unwrap(),
533 queue_shared: Arc::clone(&self.queue_shared),
534 })
535 }
536
537 unsafe fn reset_all<I>(&mut self, _cmd_bufs: I)
538 where
539 I: Iterator<Item = super::CommandBuffer>,
540 {
541 }
543
544 unsafe fn transition_buffers<'a, T>(&mut self, _barriers: T)
545 where
546 T: Iterator<Item = crate::BufferBarrier<'a, super::Buffer>>,
547 {
548 }
549
550 unsafe fn transition_textures<'a, T>(&mut self, _barriers: T)
551 where
552 T: Iterator<Item = crate::TextureBarrier<'a, super::Texture>>,
553 {
554 }
555
556 unsafe fn clear_buffer(&mut self, buffer: &super::Buffer, range: crate::MemoryRange) {
557 let encoder = self.enter_blit();
558 encoder.fillBuffer_range_value(&buffer.raw, conv::map_range(&range), 0);
559 }
560
561 unsafe fn copy_buffer_to_buffer<T>(
562 &mut self,
563 src: &super::Buffer,
564 dst: &super::Buffer,
565 regions: T,
566 ) where
567 T: Iterator<Item = crate::BufferCopy>,
568 {
569 let encoder = self.enter_blit();
570 for copy in regions {
571 unsafe {
572 encoder.copyFromBuffer_sourceOffset_toBuffer_destinationOffset_size(
573 &src.raw,
574 copy.src_offset as usize,
575 &dst.raw,
576 copy.dst_offset as usize,
577 copy.size.get() as usize,
578 )
579 };
580 }
581 }
582
583 unsafe fn copy_texture_to_texture<T>(
584 &mut self,
585 src: &super::Texture,
586 _src_usage: wgt::TextureUses,
587 dst: &super::Texture,
588 regions: T,
589 ) where
590 T: Iterator<Item = crate::TextureCopy>,
591 {
592 let dst_texture = if src.format != dst.format {
593 let raw_format = self
594 .shared
595 .private_texture_format_caps
596 .map_format(src.format);
597 Cow::Owned(autoreleasepool(|_| {
598 dst.raw.newTextureViewWithPixelFormat(raw_format).unwrap()
599 }))
600 } else {
601 Cow::Borrowed(&dst.raw)
602 };
603 let encoder = self.enter_blit();
604 for copy in regions {
605 let src_origin = conv::map_origin(©.src_base.origin);
606 let dst_origin = conv::map_origin(©.dst_base.origin);
607 let extent = conv::map_copy_extent(©.size);
609 unsafe {
610 encoder.copyFromTexture_sourceSlice_sourceLevel_sourceOrigin_sourceSize_toTexture_destinationSlice_destinationLevel_destinationOrigin(
611 &src.raw,
612 copy.src_base.array_layer as usize,
613 copy.src_base.mip_level as usize,
614 src_origin,
615 extent,
616 &dst_texture,
617 copy.dst_base.array_layer as usize,
618 copy.dst_base.mip_level as usize,
619 dst_origin,
620 )
621 };
622 }
623 }
624
625 unsafe fn copy_buffer_to_texture<T>(
626 &mut self,
627 src: &super::Buffer,
628 dst: &super::Texture,
629 regions: T,
630 ) where
631 T: Iterator<Item = crate::BufferTextureCopy>,
632 {
633 let encoder = self.enter_blit();
634 for copy in regions {
635 let dst_origin = conv::map_origin(©.texture_base.origin);
636 let extent = copy
638 .texture_base
639 .max_copy_size(&dst.copy_size)
640 .min(©.size);
641 let bytes_per_row = copy.buffer_layout.bytes_per_row.unwrap_or(0) as u64;
642 let image_byte_stride = if extent.depth > 1 {
643 copy.buffer_layout
644 .rows_per_image
645 .map_or(0, |v| v as u64 * bytes_per_row)
646 } else {
647 0
651 };
652 unsafe {
653 encoder.copyFromBuffer_sourceOffset_sourceBytesPerRow_sourceBytesPerImage_sourceSize_toTexture_destinationSlice_destinationLevel_destinationOrigin_options(
654 &src.raw,
655 copy.buffer_layout.offset as usize,
656 bytes_per_row as usize,
657 image_byte_stride as usize,
658 conv::map_copy_extent(&extent),
659 &dst.raw,
660 copy.texture_base.array_layer as usize,
661 copy.texture_base.mip_level as usize,
662 dst_origin,
663 conv::get_blit_option(dst.format, copy.texture_base.aspect),
664 )
665 };
666 }
667 }
668
669 unsafe fn copy_texture_to_buffer<T>(
670 &mut self,
671 src: &super::Texture,
672 _src_usage: wgt::TextureUses,
673 dst: &super::Buffer,
674 regions: T,
675 ) where
676 T: Iterator<Item = crate::BufferTextureCopy>,
677 {
678 let encoder = self.enter_blit();
679 for copy in regions {
680 let src_origin = conv::map_origin(©.texture_base.origin);
681 let extent = copy
683 .texture_base
684 .max_copy_size(&src.copy_size)
685 .min(©.size);
686 let bytes_per_row = copy.buffer_layout.bytes_per_row.unwrap_or(0) as u64;
687 let bytes_per_image = copy
688 .buffer_layout
689 .rows_per_image
690 .map_or(0, |v| v as u64 * bytes_per_row);
691 unsafe {
692 encoder.copyFromTexture_sourceSlice_sourceLevel_sourceOrigin_sourceSize_toBuffer_destinationOffset_destinationBytesPerRow_destinationBytesPerImage_options(
693 &src.raw,
694 copy.texture_base.array_layer as usize,
695 copy.texture_base.mip_level as usize,
696 src_origin,
697 conv::map_copy_extent(&extent),
698 &dst.raw,
699 copy.buffer_layout.offset as usize,
700 bytes_per_row as usize,
701 bytes_per_image as usize,
702 conv::get_blit_option(src.format, copy.texture_base.aspect),
703 )
704 };
705 }
706 }
707
708 unsafe fn copy_acceleration_structure_to_acceleration_structure(
709 &mut self,
710 src: &super::AccelerationStructure,
711 dst: &super::AccelerationStructure,
712 copy: wgt::AccelerationStructureCopy,
713 ) {
714 let command_encoder = self.enter_acceleration_structure_builder();
715 match copy {
716 wgt::AccelerationStructureCopy::Clone => unsafe {
717 command_encoder
718 .copyAccelerationStructure_toAccelerationStructure(&src.raw, &dst.raw);
719 },
720 wgt::AccelerationStructureCopy::Compact => {
721 command_encoder.copyAndCompactAccelerationStructure_toAccelerationStructure(
722 &src.raw, &dst.raw,
723 );
724 }
725 };
726 }
727
728 unsafe fn begin_query(&mut self, set: &super::QuerySet, index: u32) {
729 match set.ty {
730 wgt::QueryType::Occlusion => {
731 self.state
732 .render
733 .as_ref()
734 .unwrap()
735 .setVisibilityResultMode_offset(
736 MTLVisibilityResultMode::Boolean,
737 index as usize * crate::QUERY_SIZE as usize,
738 );
739 }
740 _ => {}
741 }
742 }
743 unsafe fn end_query(&mut self, set: &super::QuerySet, _index: u32) {
744 match set.ty {
745 wgt::QueryType::Occlusion => {
746 self.state
747 .render
748 .as_ref()
749 .unwrap()
750 .setVisibilityResultMode_offset(MTLVisibilityResultMode::Disabled, 0);
751 }
752 _ => {}
753 }
754 }
755 unsafe fn write_timestamp(&mut self, set: &super::QuerySet, index: u32) {
756 let support = self.shared.private_caps.timestamp_query_support;
757 debug_assert!(
758 support.contains(TimestampQuerySupport::STAGE_BOUNDARIES),
759 "Timestamp queries are not supported"
760 );
761 let sample_buffer = set.counter_sample_buffer.as_ref().unwrap();
762 let with_barrier = true;
763
764 if let (true, Some(encoder)) = (
767 support.contains(TimestampQuerySupport::ON_BLIT_ENCODER),
768 self.state.blit.as_ref(),
769 ) {
770 unsafe {
771 encoder.sampleCountersInBuffer_atSampleIndex_withBarrier(
772 sample_buffer,
773 index as _,
774 with_barrier,
775 )
776 };
777 } else if let (true, Some(encoder)) = (
778 support.contains(TimestampQuerySupport::ON_RENDER_ENCODER),
779 self.state.render.as_ref(),
780 ) {
781 unsafe {
782 encoder.sampleCountersInBuffer_atSampleIndex_withBarrier(
783 sample_buffer,
784 index as _,
785 with_barrier,
786 )
787 };
788 } else if let (true, Some(encoder)) = (
789 support.contains(TimestampQuerySupport::ON_COMPUTE_ENCODER),
790 self.state.compute.as_ref(),
791 ) {
792 unsafe {
793 encoder.sampleCountersInBuffer_atSampleIndex_withBarrier(
794 sample_buffer,
795 index as _,
796 with_barrier,
797 )
798 };
799 } else {
800 debug_assert!(self.state.render.is_none() && self.state.compute.is_none());
803
804 self.state.pending_timer_queries.push((set.clone(), index));
806
807 self.leave_blit();
809 };
810 }
811
812 unsafe fn reset_queries(&mut self, set: &super::QuerySet, range: Range<u32>) {
813 let encoder = self.enter_blit();
814 let raw_range = NSRange {
815 location: range.start as usize * crate::QUERY_SIZE as usize,
816 length: (range.end - range.start) as usize * crate::QUERY_SIZE as usize,
817 };
818 encoder.fillBuffer_range_value(&set.raw_buffer, raw_range, 0);
819 }
820
821 unsafe fn copy_query_results(
822 &mut self,
823 set: &super::QuerySet,
824 range: Range<u32>,
825 buffer: &super::Buffer,
826 offset: wgt::BufferAddress,
827 _: wgt::BufferSize, ) {
829 let encoder = self.enter_blit();
830 match set.ty {
831 wgt::QueryType::Occlusion => {
832 let size = (range.end - range.start) as u64 * crate::QUERY_SIZE;
833 unsafe {
834 encoder.copyFromBuffer_sourceOffset_toBuffer_destinationOffset_size(
835 &set.raw_buffer,
836 range.start as usize * crate::QUERY_SIZE as usize,
837 &buffer.raw,
838 offset as usize,
839 size as usize,
840 )
841 };
842 }
843 wgt::QueryType::Timestamp => {
844 unsafe {
845 encoder.resolveCounters_inRange_destinationBuffer_destinationOffset(
846 set.counter_sample_buffer.as_ref().unwrap(),
847 NSRange::new(range.start as usize, (range.end - range.start) as usize),
848 &buffer.raw,
849 offset as usize,
850 )
851 };
852 }
853 wgt::QueryType::PipelineStatistics(_) => todo!(),
854 }
855 }
856
857 unsafe fn begin_render_pass(
860 &mut self,
861 desc: &crate::RenderPassDescriptor<super::QuerySet, super::TextureView>,
862 ) -> Result<(), crate::DeviceError> {
863 self.begin_pass();
864 self.state.index = None;
865
866 assert!(self.state.blit.is_none());
867 assert!(self.state.compute.is_none());
868 assert!(self.state.render.is_none());
869
870 autoreleasepool(|_| {
871 let descriptor = MTLRenderPassDescriptor::new();
872
873 for (i, at) in desc.color_attachments.iter().enumerate() {
874 if let Some(at) = at.as_ref() {
875 let at_descriptor =
876 unsafe { descriptor.colorAttachments().objectAtIndexedSubscript(i) };
877 at_descriptor.setTexture(Some(&at.target.view.raw));
878 if let Some(depth_slice) = at.depth_slice {
879 at_descriptor.setDepthPlane(depth_slice as usize);
880 }
881 if let Some(ref resolve) = at.resolve_target {
882 at_descriptor.setResolveTexture(Some(&resolve.view.raw));
884 }
885 let load_action = if at.ops.contains(crate::AttachmentOps::LOAD) {
886 MTLLoadAction::Load
887 } else if at.ops.contains(crate::AttachmentOps::LOAD_DONT_CARE) {
888 MTLLoadAction::DontCare
889 } else if at.ops.contains(crate::AttachmentOps::LOAD_CLEAR) {
890 at_descriptor.setClearColor(conv::map_clear_color(&at.clear_value));
891 MTLLoadAction::Clear
892 } else {
893 unreachable!()
894 };
895 let store_action = conv::map_store_action(
896 at.ops.contains(crate::AttachmentOps::STORE),
897 at.resolve_target.is_some(),
898 );
899 at_descriptor.setLoadAction(load_action);
900 at_descriptor.setStoreAction(store_action);
901 }
902 }
903
904 if let Some(ref at) = desc.depth_stencil_attachment {
905 if at.target.view.aspects.contains(crate::FormatAspects::DEPTH) {
906 let at_descriptor = descriptor.depthAttachment();
907 at_descriptor.setTexture(Some(&at.target.view.raw));
908
909 let load_action = if at.depth_ops.contains(crate::AttachmentOps::LOAD) {
910 MTLLoadAction::Load
911 } else if at.depth_ops.contains(crate::AttachmentOps::LOAD_DONT_CARE) {
912 MTLLoadAction::DontCare
913 } else if at.depth_ops.contains(crate::AttachmentOps::LOAD_CLEAR) {
914 at_descriptor.setClearDepth(at.clear_value.0 as f64);
915 MTLLoadAction::Clear
916 } else {
917 unreachable!();
918 };
919 let store_action = if at.depth_ops.contains(crate::AttachmentOps::STORE) {
920 MTLStoreAction::Store
921 } else {
922 MTLStoreAction::DontCare
923 };
924 at_descriptor.setLoadAction(load_action);
925 at_descriptor.setStoreAction(store_action);
926 }
927 if at
928 .target
929 .view
930 .aspects
931 .contains(crate::FormatAspects::STENCIL)
932 {
933 let at_descriptor = descriptor.stencilAttachment();
934 at_descriptor.setTexture(Some(&at.target.view.raw));
935
936 let load_action = if at.stencil_ops.contains(crate::AttachmentOps::LOAD) {
937 MTLLoadAction::Load
938 } else if at
939 .stencil_ops
940 .contains(crate::AttachmentOps::LOAD_DONT_CARE)
941 {
942 MTLLoadAction::DontCare
943 } else if at.stencil_ops.contains(crate::AttachmentOps::LOAD_CLEAR) {
944 at_descriptor.setClearStencil(at.clear_value.1);
945 MTLLoadAction::Clear
946 } else {
947 unreachable!()
948 };
949 let store_action = if at.stencil_ops.contains(crate::AttachmentOps::STORE) {
950 MTLStoreAction::Store
951 } else {
952 MTLStoreAction::DontCare
953 };
954 at_descriptor.setLoadAction(load_action);
955 at_descriptor.setStoreAction(store_action);
956 }
957 }
958
959 let mut sba_index = 0;
960 let mut next_sba_descriptor = || {
961 let sba_descriptor = unsafe {
962 descriptor
963 .sampleBufferAttachments()
964 .objectAtIndexedSubscript(sba_index)
965 };
966
967 unsafe { sba_descriptor.setEndOfVertexSampleIndex(MTLCounterDontSample) };
968 unsafe { sba_descriptor.setStartOfFragmentSampleIndex(MTLCounterDontSample) };
969
970 sba_index += 1;
971 sba_descriptor
972 };
973
974 for (set, index) in self.state.pending_timer_queries.drain(..) {
975 let sba_descriptor = next_sba_descriptor();
976 sba_descriptor.setSampleBuffer(Some(set.counter_sample_buffer.as_ref().unwrap()));
977 unsafe { sba_descriptor.setStartOfVertexSampleIndex(index as _) };
978 unsafe { sba_descriptor.setEndOfFragmentSampleIndex(MTLCounterDontSample) };
979 }
980
981 if let Some(ref timestamp_writes) = desc.timestamp_writes {
982 let sba_descriptor = next_sba_descriptor();
983 sba_descriptor.setSampleBuffer(Some(
984 timestamp_writes
985 .query_set
986 .counter_sample_buffer
987 .as_ref()
988 .unwrap(),
989 ));
990
991 unsafe {
992 sba_descriptor.setStartOfVertexSampleIndex(
993 timestamp_writes
994 .beginning_of_pass_write_index
995 .map_or(MTLCounterDontSample, |i| i as _),
996 )
997 };
998 unsafe {
999 sba_descriptor.setEndOfFragmentSampleIndex(
1000 timestamp_writes
1001 .end_of_pass_write_index
1002 .map_or(MTLCounterDontSample, |i| i as _),
1003 )
1004 };
1005 }
1006
1007 if let Some(occlusion_query_set) = desc.occlusion_query_set {
1008 descriptor.setVisibilityResultBuffer(Some(occlusion_query_set.raw_buffer.as_ref()))
1009 }
1010 if let Some(mv) = desc.multiview_mask {
1016 descriptor.setRenderTargetArrayLength(32 - mv.leading_zeros() as usize);
1017 }
1018 let raw = self.raw_cmd_buf.as_ref().unwrap();
1019 let encoder = raw.renderCommandEncoderWithDescriptor(&descriptor).unwrap();
1020 if let Some(mv) = desc.multiview_mask {
1021 let mv = mv.get();
1024 let msb = 32 - mv.leading_zeros();
1025 let mut maps: SmallVec<[MTLVertexAmplificationViewMapping; 32]> = SmallVec::new();
1026 for i in 0..msb {
1027 if (mv & (1 << i)) != 0 {
1028 maps.push(MTLVertexAmplificationViewMapping {
1029 renderTargetArrayIndexOffset: i,
1030 viewportArrayIndexOffset: i,
1031 });
1032 }
1033 }
1034 unsafe {
1035 encoder.setVertexAmplificationCount_viewMappings(
1036 mv.count_ones() as usize,
1037 maps.as_ptr(),
1038 )
1039 };
1040 }
1041 if let Some(label) = desc.label {
1042 encoder.setLabel(Some(&NSString::from_str(label)));
1043 }
1044 self.state.render = Some(encoder);
1045 });
1046
1047 Ok(())
1048 }
1049
1050 unsafe fn end_render_pass(&mut self) {
1051 self.state.render.take().unwrap().endEncoding();
1052 }
1053
1054 unsafe fn set_bind_group(
1055 &mut self,
1056 layout: &super::PipelineLayout,
1057 group_index: u32,
1058 group: &super::BindGroup,
1059 dynamic_offsets: &[wgt::DynamicOffset],
1060 ) {
1061 let bg_info = layout.bind_group_infos[group_index as usize]
1062 .as_ref()
1063 .unwrap();
1064 let render_encoder = self.state.render.clone();
1065 let compute_encoder = self.state.compute.clone();
1066 if let Some(encoder) = render_encoder {
1067 self.update_bind_group_state(
1068 Encoder::Vertex(&encoder),
1069 super::ResourceData::default(),
1071 bg_info,
1072 dynamic_offsets,
1073 group_index,
1074 group,
1075 );
1076 self.update_bind_group_state(
1077 Encoder::Task(&encoder),
1078 super::ResourceData::default(),
1080 bg_info,
1081 dynamic_offsets,
1082 group_index,
1083 group,
1084 );
1085 self.update_bind_group_state(
1086 Encoder::Mesh(&encoder),
1087 group.counters.ts.clone(),
1088 bg_info,
1089 dynamic_offsets,
1090 group_index,
1091 group,
1092 );
1093 self.update_bind_group_state(
1094 Encoder::Fragment(&encoder),
1095 super::ResourceData {
1096 buffers: group.counters.vs.buffers
1097 + group.counters.ts.buffers
1098 + group.counters.ms.buffers,
1099 textures: group.counters.vs.textures
1100 + group.counters.ts.textures
1101 + group.counters.ms.textures,
1102 samplers: group.counters.vs.samplers
1103 + group.counters.ts.samplers
1104 + group.counters.ms.samplers,
1105 },
1106 bg_info,
1107 dynamic_offsets,
1108 group_index,
1109 group,
1110 );
1111 for (resource, use_info) in group.resources_to_use.iter() {
1113 encoder.useResource_usage_stages(
1114 unsafe { resource.as_ref() },
1115 use_info.uses,
1116 use_info.stages,
1117 );
1118 }
1119 }
1120 if let Some(encoder) = compute_encoder {
1121 self.update_bind_group_state(
1122 Encoder::Compute(&encoder),
1123 super::ResourceData {
1124 buffers: group.counters.vs.buffers
1125 + group.counters.ts.buffers
1126 + group.counters.ms.buffers
1127 + group.counters.fs.buffers,
1128 textures: group.counters.vs.textures
1129 + group.counters.ts.textures
1130 + group.counters.ms.textures
1131 + group.counters.fs.textures,
1132 samplers: group.counters.vs.samplers
1133 + group.counters.ts.samplers
1134 + group.counters.ms.samplers
1135 + group.counters.fs.samplers,
1136 },
1137 bg_info,
1138 dynamic_offsets,
1139 group_index,
1140 group,
1141 );
1142 for (resource, use_info) in group.resources_to_use.iter() {
1144 if !use_info.visible_in_compute {
1145 continue;
1146 }
1147 encoder.useResource_usage(unsafe { resource.as_ref() }, use_info.uses);
1148 }
1149 }
1150 }
1151
1152 unsafe fn set_immediates(
1153 &mut self,
1154 layout: &super::PipelineLayout,
1155 offset_bytes: u32,
1156 data: &[u32],
1157 ) {
1158 let state_pc = &mut self.state.immediates;
1159 if state_pc.len() < layout.total_immediates as usize {
1160 state_pc.resize(layout.total_immediates as usize, 0);
1161 }
1162 debug_assert_eq!(offset_bytes as usize % WORD_SIZE, 0);
1163
1164 let offset_words = offset_bytes as usize / WORD_SIZE;
1165 state_pc[offset_words..offset_words + data.len()].copy_from_slice(data);
1166
1167 let bytes = NonNull::new(state_pc.as_ptr().cast_mut().cast()).unwrap();
1168 if let Some(ref compute) = self.state.compute {
1169 unsafe {
1170 compute.setBytes_length_atIndex(
1171 bytes,
1172 layout.total_immediates as usize * WORD_SIZE,
1173 layout.immediates_infos.cs.unwrap().buffer_index as usize,
1174 )
1175 };
1176 }
1177 if let Some(ref render) = self.state.render {
1178 if let Some(vs) = layout.immediates_infos.vs {
1179 unsafe {
1180 render.setVertexBytes_length_atIndex(
1181 bytes,
1182 layout.total_immediates as usize * WORD_SIZE,
1183 vs.buffer_index as _,
1184 )
1185 }
1186 }
1187 if let Some(fs) = layout.immediates_infos.fs {
1188 unsafe {
1189 render.setFragmentBytes_length_atIndex(
1190 bytes,
1191 layout.total_immediates as usize * WORD_SIZE,
1192 fs.buffer_index as _,
1193 )
1194 }
1195 }
1196 if let Some(ts) = layout.immediates_infos.ts {
1197 if self.shared.private_caps.mesh_shaders {
1198 unsafe {
1199 render.setObjectBytes_length_atIndex(
1200 bytes,
1201 layout.total_immediates as usize * WORD_SIZE,
1202 ts.buffer_index as _,
1203 )
1204 }
1205 }
1206 }
1207 if let Some(ms) = layout.immediates_infos.ms {
1208 if self.shared.private_caps.mesh_shaders {
1209 unsafe {
1210 render.setMeshBytes_length_atIndex(
1211 bytes,
1212 layout.total_immediates as usize * WORD_SIZE,
1213 ms.buffer_index as _,
1214 )
1215 }
1216 }
1217 }
1218 }
1219 }
1220
1221 unsafe fn insert_debug_marker(&mut self, label: &str) {
1222 if let Some(encoder) = self.active_encoder() {
1223 encoder.insertDebugSignpost(&NSString::from_str(label));
1224 }
1225 }
1226 unsafe fn begin_debug_marker(&mut self, group_label: &str) {
1227 if let Some(encoder) = self.active_encoder() {
1228 encoder.pushDebugGroup(&NSString::from_str(group_label));
1229 } else if let Some(ref buf) = self.raw_cmd_buf {
1230 buf.pushDebugGroup(&NSString::from_str(group_label));
1231 }
1232 }
1233 unsafe fn end_debug_marker(&mut self) {
1234 if let Some(encoder) = self.active_encoder() {
1235 encoder.popDebugGroup();
1236 } else if let Some(ref buf) = self.raw_cmd_buf {
1237 buf.popDebugGroup();
1238 }
1239 }
1240
1241 unsafe fn set_render_pipeline(&mut self, pipeline: &super::RenderPipeline) {
1242 self.state.raw_primitive_type = pipeline.raw_primitive_type;
1243 match pipeline.vs_info {
1244 Some(ref info) => self.state.stage_infos.vs.assign_from(info),
1245 None => self.state.stage_infos.vs.clear(),
1246 }
1247 match pipeline.fs_info {
1248 Some(ref info) => self.state.stage_infos.fs.assign_from(info),
1249 None => self.state.stage_infos.fs.clear(),
1250 }
1251 match pipeline.ts_info {
1252 Some(ref info) => self.state.stage_infos.ts.assign_from(info),
1253 None => self.state.stage_infos.ts.clear(),
1254 }
1255 match pipeline.ms_info {
1256 Some(ref info) => self.state.stage_infos.ms.assign_from(info),
1257 None => self.state.stage_infos.ms.clear(),
1258 }
1259
1260 let encoder = self.state.render.as_ref().unwrap();
1261 encoder.setRenderPipelineState(&pipeline.raw);
1262 encoder.setFrontFacingWinding(pipeline.raw_front_winding);
1263 encoder.setCullMode(pipeline.raw_cull_mode);
1264 encoder.setTriangleFillMode(pipeline.raw_triangle_fill_mode);
1265 if let Some(depth_clip) = pipeline.raw_depth_clip_mode {
1266 encoder.setDepthClipMode(depth_clip);
1267 }
1268 if let Some((ref state, bias)) = pipeline.depth_stencil {
1269 encoder.setDepthStencilState(Some(state));
1270 encoder.setDepthBias_slopeScale_clamp(
1271 bias.constant as f32,
1272 bias.slope_scale,
1273 bias.clamp,
1274 );
1275 }
1276
1277 if pipeline.vs_info.is_some() {
1278 if let Some((index, sizes)) = self
1279 .state
1280 .make_sizes_buffer_update(naga::ShaderStage::Vertex, &mut self.temp.binding_sizes)
1281 {
1282 unsafe {
1283 encoder.setVertexBytes_length_atIndex(
1284 NonNull::new(sizes.as_ptr().cast_mut().cast()).unwrap(),
1285 sizes.len() * WORD_SIZE,
1286 index as _,
1287 )
1288 };
1289 }
1290 }
1291 if pipeline.fs_info.is_some() {
1292 if let Some((index, sizes)) = self
1293 .state
1294 .make_sizes_buffer_update(naga::ShaderStage::Fragment, &mut self.temp.binding_sizes)
1295 {
1296 unsafe {
1297 encoder.setFragmentBytes_length_atIndex(
1298 NonNull::new(sizes.as_ptr().cast_mut().cast()).unwrap(),
1299 sizes.len() * WORD_SIZE,
1300 index as _,
1301 )
1302 };
1303 }
1304 }
1305 if let Some(ts_info) = &pipeline.ts_info {
1306 while self.state.stage_infos.ms.work_group_memory_sizes.len()
1308 < ts_info.work_group_memory_sizes.len()
1309 {
1310 self.state.stage_infos.ms.work_group_memory_sizes.push(0);
1311 }
1312 for (index, (cur_size, pipeline_size)) in self
1313 .state
1314 .stage_infos
1315 .ms
1316 .work_group_memory_sizes
1317 .iter_mut()
1318 .zip(ts_info.work_group_memory_sizes.iter())
1319 .enumerate()
1320 {
1321 let size = pipeline_size.next_multiple_of(16);
1322 if *cur_size != size {
1323 *cur_size = size;
1324 unsafe { encoder.setObjectThreadgroupMemoryLength_atIndex(size as _, index) };
1325 }
1326 }
1327 if let Some((index, sizes)) = self
1328 .state
1329 .make_sizes_buffer_update(naga::ShaderStage::Task, &mut self.temp.binding_sizes)
1330 {
1331 unsafe {
1332 encoder.setObjectBytes_length_atIndex(
1333 NonNull::new(sizes.as_ptr().cast_mut().cast()).unwrap(),
1334 sizes.len() * WORD_SIZE,
1335 index as _,
1336 )
1337 };
1338 }
1339 }
1340 if let Some(_ms_info) = &pipeline.ms_info {
1341 if let Some((index, sizes)) = self
1347 .state
1348 .make_sizes_buffer_update(naga::ShaderStage::Mesh, &mut self.temp.binding_sizes)
1349 {
1350 unsafe {
1351 encoder.setMeshBytes_length_atIndex(
1352 NonNull::new(sizes.as_ptr().cast_mut().cast()).unwrap(),
1353 sizes.len() * WORD_SIZE,
1354 index as _,
1355 )
1356 };
1357 }
1358 }
1359 }
1360
1361 unsafe fn set_index_buffer<'a>(
1362 &mut self,
1363 binding: crate::BufferBinding<'a, super::Buffer>,
1364 format: wgt::IndexFormat,
1365 ) {
1366 let (stride, raw_type) = conv::map_index_format(format);
1367 self.state.index = Some(super::IndexState {
1368 buffer_ptr: NonNull::from(&*binding.buffer.raw),
1369 offset: binding.offset,
1370 stride,
1371 raw_type,
1372 });
1373 }
1374
1375 unsafe fn set_vertex_buffer<'a>(
1376 &mut self,
1377 index: u32,
1378 binding: crate::BufferBinding<'a, super::Buffer>,
1379 ) {
1380 let buffer_index = self.shared.private_caps.max_vertex_buffers as u64 - 1 - index as u64;
1381 let encoder = self.state.render.as_ref().unwrap();
1382 unsafe {
1383 encoder.setVertexBuffer_offset_atIndex(
1384 Some(&binding.buffer.raw),
1385 binding.offset as usize,
1386 buffer_index as usize,
1387 )
1388 };
1389
1390 let buffer_size = binding.resolve_size();
1391 if buffer_size > 0 {
1392 self.state.vertex_buffer_size_map.insert(
1393 buffer_index,
1394 core::num::NonZeroU64::new(buffer_size).unwrap(),
1395 );
1396 } else {
1397 self.state.vertex_buffer_size_map.remove(&buffer_index);
1398 }
1399
1400 if let Some((index, sizes)) = self
1401 .state
1402 .make_sizes_buffer_update(naga::ShaderStage::Vertex, &mut self.temp.binding_sizes)
1403 {
1404 unsafe {
1405 encoder.setVertexBytes_length_atIndex(
1406 NonNull::new(sizes.as_ptr().cast_mut().cast()).unwrap(),
1407 sizes.len() * WORD_SIZE,
1408 index as _,
1409 )
1410 };
1411 }
1412 }
1413
1414 unsafe fn set_viewport(&mut self, rect: &crate::Rect<f32>, depth_range: Range<f32>) {
1415 let zfar = if self.shared.disabilities.broken_viewport_near_depth {
1416 depth_range.end - depth_range.start
1417 } else {
1418 depth_range.end
1419 };
1420 let encoder = self.state.render.as_ref().unwrap();
1421 encoder.setViewport(MTLViewport {
1422 originX: rect.x as _,
1423 originY: rect.y as _,
1424 width: rect.w as _,
1425 height: rect.h as _,
1426 znear: depth_range.start as _,
1427 zfar: zfar as _,
1428 });
1429 }
1430 unsafe fn set_scissor_rect(&mut self, rect: &crate::Rect<u32>) {
1431 let scissor = MTLScissorRect {
1433 x: rect.x as _,
1434 y: rect.y as _,
1435 width: rect.w as _,
1436 height: rect.h as _,
1437 };
1438 let encoder = self.state.render.as_ref().unwrap();
1439 encoder.setScissorRect(scissor);
1440 }
1441 unsafe fn set_stencil_reference(&mut self, value: u32) {
1442 let encoder = self.state.render.as_ref().unwrap();
1443 encoder.setStencilFrontReferenceValue_backReferenceValue(value, value);
1444 }
1445 unsafe fn set_blend_constants(&mut self, color: &[f32; 4]) {
1446 let encoder = self.state.render.as_ref().unwrap();
1447 encoder.setBlendColorRed_green_blue_alpha(color[0], color[1], color[2], color[3]);
1448 }
1449
1450 unsafe fn draw(
1451 &mut self,
1452 first_vertex: u32,
1453 vertex_count: u32,
1454 first_instance: u32,
1455 instance_count: u32,
1456 ) {
1457 let encoder = self.state.render.as_ref().unwrap();
1458 if first_instance != 0 {
1459 unsafe {
1460 encoder.drawPrimitives_vertexStart_vertexCount_instanceCount_baseInstance(
1461 self.state.raw_primitive_type,
1462 first_vertex as _,
1463 vertex_count as _,
1464 instance_count as _,
1465 first_instance as _,
1466 )
1467 };
1468 } else if instance_count != 1 {
1469 unsafe {
1470 encoder.drawPrimitives_vertexStart_vertexCount_instanceCount(
1471 self.state.raw_primitive_type,
1472 first_vertex as _,
1473 vertex_count as _,
1474 instance_count as _,
1475 )
1476 };
1477 } else {
1478 unsafe {
1479 encoder.drawPrimitives_vertexStart_vertexCount(
1480 self.state.raw_primitive_type,
1481 first_vertex as _,
1482 vertex_count as _,
1483 )
1484 };
1485 }
1486 }
1487
1488 unsafe fn draw_indexed(
1489 &mut self,
1490 first_index: u32,
1491 index_count: u32,
1492 base_vertex: i32,
1493 first_instance: u32,
1494 instance_count: u32,
1495 ) {
1496 let encoder = self.state.render.as_ref().unwrap();
1497 let index = self.state.index.as_ref().unwrap();
1498 let offset = (index.offset + index.stride * first_index as wgt::BufferAddress) as usize;
1499 if base_vertex != 0 || first_instance != 0 {
1500 unsafe {
1501 encoder.drawIndexedPrimitives_indexCount_indexType_indexBuffer_indexBufferOffset_instanceCount_baseVertex_baseInstance(
1502 self.state.raw_primitive_type,
1503 index_count as _,
1504 index.raw_type,
1505 index.buffer_ptr.as_ref(),
1506 offset,
1507 instance_count as _,
1508 base_vertex as _,
1509 first_instance as _,
1510 )
1511 };
1512 } else if instance_count != 1 {
1513 unsafe {
1514 encoder.drawIndexedPrimitives_indexCount_indexType_indexBuffer_indexBufferOffset_instanceCount(
1515 self.state.raw_primitive_type,
1516 index_count as _,
1517 index.raw_type,
1518 index.buffer_ptr.as_ref(),
1519 offset,
1520 instance_count as _,
1521 )
1522 };
1523 } else {
1524 unsafe {
1525 encoder.drawIndexedPrimitives_indexCount_indexType_indexBuffer_indexBufferOffset(
1526 self.state.raw_primitive_type,
1527 index_count as _,
1528 index.raw_type,
1529 index.buffer_ptr.as_ref(),
1530 offset,
1531 )
1532 };
1533 }
1534 }
1535
1536 unsafe fn draw_mesh_tasks(
1537 &mut self,
1538 group_count_x: u32,
1539 group_count_y: u32,
1540 group_count_z: u32,
1541 ) {
1542 let encoder = self.state.render.as_ref().unwrap();
1543 let size = MTLSize {
1544 width: group_count_x as usize,
1545 height: group_count_y as usize,
1546 depth: group_count_z as usize,
1547 };
1548 encoder.drawMeshThreadgroups_threadsPerObjectThreadgroup_threadsPerMeshThreadgroup(
1549 size,
1550 self.state.stage_infos.ts.raw_wg_size,
1551 self.state.stage_infos.ms.raw_wg_size,
1552 );
1553 }
1554
1555 unsafe fn draw_indirect(
1556 &mut self,
1557 buffer: &super::Buffer,
1558 mut offset: wgt::BufferAddress,
1559 draw_count: u32,
1560 ) {
1561 let encoder = self.state.render.as_ref().unwrap();
1562 for _ in 0..draw_count {
1563 unsafe {
1564 encoder.drawPrimitives_indirectBuffer_indirectBufferOffset(
1565 self.state.raw_primitive_type,
1566 &buffer.raw,
1567 offset as usize,
1568 )
1569 };
1570 offset += size_of::<wgt::DrawIndirectArgs>() as wgt::BufferAddress;
1571 }
1572 }
1573
1574 unsafe fn draw_indexed_indirect(
1575 &mut self,
1576 buffer: &super::Buffer,
1577 mut offset: wgt::BufferAddress,
1578 draw_count: u32,
1579 ) {
1580 let encoder = self.state.render.as_ref().unwrap();
1581 let index = self.state.index.as_ref().unwrap();
1582 for _ in 0..draw_count {
1583 unsafe {
1584 encoder.drawIndexedPrimitives_indexType_indexBuffer_indexBufferOffset_indirectBuffer_indirectBufferOffset(
1585 self.state.raw_primitive_type,
1586 index.raw_type,
1587 index.buffer_ptr.as_ref(),
1588 index.offset as usize,
1589 &buffer.raw,
1590 offset as usize,
1591 )
1592 };
1593 offset += size_of::<wgt::DrawIndexedIndirectArgs>() as wgt::BufferAddress;
1594 }
1595 }
1596
1597 unsafe fn draw_mesh_tasks_indirect(
1598 &mut self,
1599 buffer: &<Self::A as crate::Api>::Buffer,
1600 mut offset: wgt::BufferAddress,
1601 draw_count: u32,
1602 ) {
1603 let encoder = self.state.render.as_ref().unwrap();
1604 for _ in 0..draw_count {
1605 unsafe {
1606 encoder.drawMeshThreadgroupsWithIndirectBuffer_indirectBufferOffset_threadsPerObjectThreadgroup_threadsPerMeshThreadgroup(
1607 &buffer.raw,
1608 offset as usize,
1609 self.state.stage_infos.ts.raw_wg_size,
1610 self.state.stage_infos.ms.raw_wg_size,
1611 )
1612 };
1613 offset += size_of::<wgt::DispatchIndirectArgs>() as wgt::BufferAddress;
1614 }
1615 }
1616
1617 unsafe fn draw_indirect_count(
1618 &mut self,
1619 _buffer: &super::Buffer,
1620 _offset: wgt::BufferAddress,
1621 _count_buffer: &super::Buffer,
1622 _count_offset: wgt::BufferAddress,
1623 _max_count: u32,
1624 ) {
1625 }
1627 unsafe fn draw_indexed_indirect_count(
1628 &mut self,
1629 _buffer: &super::Buffer,
1630 _offset: wgt::BufferAddress,
1631 _count_buffer: &super::Buffer,
1632 _count_offset: wgt::BufferAddress,
1633 _max_count: u32,
1634 ) {
1635 }
1637
1638 unsafe fn draw_mesh_tasks_indirect_count(
1639 &mut self,
1640 _buffer: &<Self::A as crate::Api>::Buffer,
1641 _offset: wgt::BufferAddress,
1642 _count_buffer: &<Self::A as crate::Api>::Buffer,
1643 _count_offset: wgt::BufferAddress,
1644 _max_count: u32,
1645 ) {
1646 unreachable!()
1647 }
1648
1649 unsafe fn begin_compute_pass(&mut self, desc: &crate::ComputePassDescriptor<super::QuerySet>) {
1652 self.begin_pass();
1653
1654 debug_assert!(self.state.blit.is_none());
1655 debug_assert!(self.state.compute.is_none());
1656 debug_assert!(self.state.render.is_none());
1657
1658 let raw = self.raw_cmd_buf.as_ref().unwrap();
1659
1660 autoreleasepool(|_| {
1661 let encoder = if self.shared.private_caps.timestamp_query_support.is_empty() {
1664 raw.computeCommandEncoder().unwrap()
1665 } else {
1666 let descriptor = MTLComputePassDescriptor::new();
1667
1668 let mut sba_index = 0;
1669 let mut next_sba_descriptor = || {
1670 let sba_descriptor = unsafe {
1671 descriptor
1672 .sampleBufferAttachments()
1673 .objectAtIndexedSubscript(sba_index)
1674 };
1675 sba_index += 1;
1676 sba_descriptor
1677 };
1678
1679 for (set, index) in self.state.pending_timer_queries.drain(..) {
1680 let sba_descriptor = next_sba_descriptor();
1681 sba_descriptor
1682 .setSampleBuffer(Some(set.counter_sample_buffer.as_ref().unwrap()));
1683 unsafe { sba_descriptor.setStartOfEncoderSampleIndex(index as _) };
1684 unsafe { sba_descriptor.setEndOfEncoderSampleIndex(MTLCounterDontSample) };
1685 }
1686
1687 if let Some(timestamp_writes) = desc.timestamp_writes.as_ref() {
1688 let sba_descriptor = next_sba_descriptor();
1689 sba_descriptor.setSampleBuffer(Some(
1690 timestamp_writes
1691 .query_set
1692 .counter_sample_buffer
1693 .as_ref()
1694 .unwrap(),
1695 ));
1696
1697 unsafe {
1698 sba_descriptor.setStartOfEncoderSampleIndex(
1699 timestamp_writes
1700 .beginning_of_pass_write_index
1701 .map_or(MTLCounterDontSample, |i| i as _),
1702 )
1703 };
1704 unsafe {
1705 sba_descriptor.setEndOfEncoderSampleIndex(
1706 timestamp_writes
1707 .end_of_pass_write_index
1708 .map_or(MTLCounterDontSample, |i| i as _),
1709 )
1710 };
1711 }
1712
1713 raw.computeCommandEncoderWithDescriptor(&descriptor)
1714 .unwrap()
1715 };
1716
1717 if let Some(label) = desc.label {
1718 encoder.setLabel(Some(&NSString::from_str(label)));
1719 }
1720
1721 self.state.compute = Some(encoder.to_owned());
1722 });
1723 }
1724 unsafe fn end_compute_pass(&mut self) {
1725 self.state.compute.take().unwrap().endEncoding();
1726 }
1727
1728 unsafe fn set_compute_pipeline(&mut self, pipeline: &super::ComputePipeline) {
1729 let previous_sizes =
1730 core::mem::take(&mut self.state.stage_infos.cs.work_group_memory_sizes);
1731 self.state.stage_infos.cs.assign_from(&pipeline.cs_info);
1732
1733 let encoder = self.state.compute.as_ref().unwrap();
1734 encoder.setComputePipelineState(&pipeline.raw);
1735
1736 if let Some((index, sizes)) = self
1737 .state
1738 .make_sizes_buffer_update(naga::ShaderStage::Compute, &mut self.temp.binding_sizes)
1739 {
1740 unsafe {
1741 encoder.setBytes_length_atIndex(
1742 NonNull::new(sizes.as_ptr().cast_mut().cast()).unwrap(),
1743 sizes.len() * WORD_SIZE,
1744 index as _,
1745 )
1746 };
1747 }
1748
1749 for (i, current_size) in self
1751 .state
1752 .stage_infos
1753 .cs
1754 .work_group_memory_sizes
1755 .iter_mut()
1756 .enumerate()
1757 {
1758 let prev_size = if i < previous_sizes.len() {
1759 previous_sizes[i]
1760 } else {
1761 u32::MAX
1762 };
1763 let size: u32 = current_size.next_multiple_of(16);
1764 *current_size = size;
1765 if size != prev_size {
1766 unsafe { encoder.setThreadgroupMemoryLength_atIndex(size as _, i) };
1767 }
1768 }
1769 }
1770
1771 unsafe fn dispatch(&mut self, count: [u32; 3]) {
1772 if count[0] > 0 && count[1] > 0 && count[2] > 0 {
1773 let encoder = self.state.compute.as_ref().unwrap();
1774 let raw_count = MTLSize {
1775 width: count[0] as usize,
1776 height: count[1] as usize,
1777 depth: count[2] as usize,
1778 };
1779 encoder.dispatchThreadgroups_threadsPerThreadgroup(
1780 raw_count,
1781 self.state.stage_infos.cs.raw_wg_size,
1782 );
1783 }
1784 }
1785
1786 unsafe fn dispatch_indirect(&mut self, buffer: &super::Buffer, offset: wgt::BufferAddress) {
1787 let encoder = self.state.compute.as_ref().unwrap();
1788 unsafe {
1789 encoder
1790 .dispatchThreadgroupsWithIndirectBuffer_indirectBufferOffset_threadsPerThreadgroup(
1791 &buffer.raw,
1792 offset as usize,
1793 self.state.stage_infos.cs.raw_wg_size,
1794 )
1795 };
1796 }
1797
1798 unsafe fn build_acceleration_structures<'a, T>(
1799 &mut self,
1800 _descriptor_count: u32,
1801 descriptors: T,
1802 ) where
1803 super::Api: 'a,
1804 T: IntoIterator<
1805 Item = crate::BuildAccelerationStructureDescriptor<
1806 'a,
1807 super::Buffer,
1808 super::AccelerationStructure,
1809 >,
1810 >,
1811 {
1812 let command_encoder = self.enter_acceleration_structure_builder();
1813 for descriptor in descriptors {
1814 let acceleration_structure_descriptor =
1815 conv::map_acceleration_structure_descriptor(descriptor.entries, descriptor.flags);
1816 match descriptor.mode {
1817 crate::AccelerationStructureBuildMode::Build => {
1818 command_encoder
1819 .buildAccelerationStructure_descriptor_scratchBuffer_scratchBufferOffset(
1820 &descriptor.destination_acceleration_structure.raw,
1821 &acceleration_structure_descriptor,
1822 &descriptor.scratch_buffer.raw,
1823 descriptor.scratch_buffer_offset as usize,
1824 );
1825 }
1826 crate::AccelerationStructureBuildMode::Update => unsafe {
1827 command_encoder.refitAccelerationStructure_descriptor_destination_scratchBuffer_scratchBufferOffset(
1828 &descriptor.source_acceleration_structure.unwrap().raw,
1829 &acceleration_structure_descriptor,
1830 Some(&descriptor.destination_acceleration_structure.raw),
1831 Some(&descriptor.scratch_buffer.raw),
1832 descriptor.scratch_buffer_offset as usize,
1833 );
1834 },
1835 }
1836 }
1837 }
1838
1839 unsafe fn place_acceleration_structure_barrier(
1840 &mut self,
1841 _barriers: crate::AccelerationStructureBarrier,
1842 ) {
1843 }
1844
1845 unsafe fn read_acceleration_structure_compact_size(
1846 &mut self,
1847 acceleration_structure: &super::AccelerationStructure,
1848 buffer: &super::Buffer,
1849 ) {
1850 let command_encoder = self.enter_acceleration_structure_builder();
1851 command_encoder.writeCompactedAccelerationStructureSize_toBuffer_offset(
1852 &acceleration_structure.raw,
1853 &buffer.raw,
1854 0,
1855 );
1856 }
1857
1858 unsafe fn set_acceleration_structure_dependencies(
1859 command_buffers: &[&super::CommandBuffer],
1860 dependencies: &[&super::AccelerationStructure],
1861 ) {
1862 let Some(first_command_buffer) = command_buffers.first() else {
1863 return;
1864 };
1865 let desc = MTLResidencySetDescriptor::new();
1866 desc.setLabel(first_command_buffer.raw.label().as_deref());
1867 let residency_set = first_command_buffer
1868 .raw
1869 .device()
1870 .newResidencySetWithDescriptor_error(&desc)
1871 .unwrap();
1872 for command_buffer in command_buffers {
1873 command_buffer.raw.useResidencySet(&residency_set);
1874 }
1875 for dependency in dependencies {
1876 residency_set.addAllocation(ProtocolObject::from_ref(&*dependency.raw));
1877 }
1878 residency_set.commit();
1879 }
1880}
1881
1882impl Drop for super::CommandEncoder {
1883 fn drop(&mut self) {
1884 unsafe {
1894 self.discard_encoding();
1895 }
1896 self.counters.command_encoders.sub(1);
1897 }
1898}
1899
1900impl Drop for super::CommandBuffer {
1901 fn drop(&mut self) {
1902 let status = self.raw.status();
1906 if status == MTLCommandBufferStatus::NotEnqueued
1907 || status == MTLCommandBufferStatus::Enqueued
1908 {
1909 let previous = self
1910 .queue_shared
1911 .command_buffer_created_not_submitted
1912 .fetch_sub(1, atomic::Ordering::AcqRel);
1913 debug_assert!(previous > 0);
1914 }
1915 }
1916}