1 use crate::{
2     conversions as conv,
3     internal::{BlitVertex, ClearKey, ClearVertex},
4     native,
5     soft,
6     window,
7     AsNative,
8     Backend,
9     BufferPtr,
10     OnlineRecording,
11     PrivateDisabilities,
12     ResourceIndex,
13     ResourcePtr,
14     SamplerPtr,
15     Shared,
16     TexturePtr,
17     MAX_BOUND_DESCRIPTOR_SETS,
18 };
19 
20 use hal::{
21     buffer,
22     command as com,
23     device::OutOfMemory,
24     format::{Aspects, FormatDesc},
25     image::{Extent, Filter, Layout, Level, SubresourceRange},
26     memory,
27     pass::AttachmentLoadOp,
28     pso,
29     query,
30     range::RangeArg,
31     window::{PresentError, Suboptimal, SwapImageIndex},
32     DrawCount,
33     IndexCount,
34     IndexType,
35     InstanceCount,
36     VertexCount,
37     VertexOffset,
38     WorkGroupCount,
39 };
40 
41 use arrayvec::ArrayVec;
42 use auxil::FastHashMap;
43 use block::ConcreteBlock;
44 use cocoa::foundation::{NSRange, NSUInteger};
45 use copyless::VecHelper;
46 #[cfg(feature = "dispatch")]
47 use dispatch;
48 use foreign_types::ForeignType;
49 use metal::{self, MTLIndexType, MTLPrimitiveType, MTLScissorRect, MTLSize, MTLViewport};
50 use objc::rc::autoreleasepool;
51 use parking_lot::Mutex;
52 
53 #[cfg(feature = "dispatch")]
54 use std::fmt;
55 use std::{
56     borrow::Borrow,
57     cell::RefCell,
58     iter,
59     mem,
60     ops::{Deref, Range},
61     ptr,
62     slice,
63     sync::{
64         atomic::{AtomicBool, Ordering},
65         Arc,
66     },
67     thread,
68     time,
69 };
70 
71 
72 const WORD_SIZE: usize = 4;
73 const WORD_ALIGNMENT: u64 = WORD_SIZE as _;
74 /// Number of frames to average when reporting the performance counters.
75 const COUNTERS_REPORT_WINDOW: usize = 0;
76 
77 #[cfg(feature = "dispatch")]
78 struct NoDebug<T>(T);
79 #[cfg(feature = "dispatch")]
80 impl<T> fmt::Debug for NoDebug<T> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result81     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
82         write!(f, "<hidden>")
83     }
84 }
85 
86 #[derive(Debug)]
87 pub struct QueueInner {
88     raw: metal::CommandQueue,
89     reserve: Range<usize>,
90     debug_retain_references: bool,
91 }
92 
93 #[must_use]
94 #[derive(Debug)]
95 pub struct Token {
96     active: bool,
97 }
98 
99 impl Drop for Token {
drop(&mut self)100     fn drop(&mut self) {
101         // poor man's linear type...
102         if !thread::panicking() {
103             debug_assert!(!self.active);
104         }
105     }
106 }
107 
108 impl QueueInner {
new(device: &metal::DeviceRef, pool_size: Option<usize>) -> Self109     pub(crate) fn new(device: &metal::DeviceRef, pool_size: Option<usize>) -> Self {
110         match pool_size {
111             Some(count) => QueueInner {
112                 raw: device.new_command_queue_with_max_command_buffer_count(count as u64),
113                 reserve: 0 .. count,
114                 debug_retain_references: false,
115             },
116             None => QueueInner {
117                 raw: device.new_command_queue(),
118                 reserve: 0 .. 64,
119                 debug_retain_references: true,
120             },
121         }
122     }
123 
124     /// Spawns a command buffer from a virtual pool.
spawn(&mut self) -> (metal::CommandBuffer, Token)125     pub(crate) fn spawn(&mut self) -> (metal::CommandBuffer, Token) {
126         self.reserve.start += 1;
127         let cmd_buf = autoreleasepool(|| self.spawn_temp().to_owned());
128         (cmd_buf, Token { active: true })
129     }
130 
spawn_temp(&self) -> &metal::CommandBufferRef131     pub(crate) fn spawn_temp(&self) -> &metal::CommandBufferRef {
132         if self.debug_retain_references {
133             self.raw.new_command_buffer()
134         } else {
135             self.raw.new_command_buffer_with_unretained_references()
136         }
137     }
138 
139     /// Returns a command buffer to a virtual pool.
release(&mut self, mut token: Token)140     pub(crate) fn release(&mut self, mut token: Token) {
141         token.active = false;
142         self.reserve.start -= 1;
143     }
144 
145     /// Block until GPU is idle.
wait_idle(queue: &Mutex<Self>)146     pub(crate) fn wait_idle(queue: &Mutex<Self>) {
147         debug!("waiting for idle");
148         // note: we deliberately don't hold the Mutex lock while waiting,
149         // since the completion handlers need to access it.
150         let (cmd_buf, token) = queue.lock().spawn();
151         cmd_buf.set_label("empty");
152         cmd_buf.commit();
153         cmd_buf.wait_until_completed();
154         queue.lock().release(token);
155     }
156 }
157 
158 #[derive(Debug)]
159 pub struct BlockedSubmission {
160     wait_events: Vec<Arc<AtomicBool>>,
161     command_buffers: Vec<metal::CommandBuffer>,
162 }
163 
164 /// Class responsible for keeping the state of submissions between the
165 /// requested user submission that is blocked by a host event, and
166 /// setting the event itself on the host.
167 #[derive(Debug, Default)]
168 pub struct QueueBlocker {
169     submissions: Vec<BlockedSubmission>,
170 }
171 
172 impl QueueBlocker {
submit_impl(&mut self, cmd_buffer: &metal::CommandBufferRef)173     fn submit_impl(&mut self, cmd_buffer: &metal::CommandBufferRef) {
174         match self.submissions.last_mut() {
175             Some(blocked) => blocked.command_buffers.push(cmd_buffer.to_owned()),
176             None => cmd_buffer.commit(),
177         }
178     }
179 
triage(&mut self)180     pub(crate) fn triage(&mut self) {
181         // clean up the relevant blocks
182         let done = {
183             let blocked = match self.submissions.first_mut() {
184                 Some(blocked) => blocked,
185                 None => return,
186             };
187             blocked.wait_events.retain(|ev| !ev.load(Ordering::Acquire));
188             blocked.wait_events.is_empty()
189         };
190 
191         // execute unblocked command buffers
192         if done {
193             let blocked = self.submissions.remove(0);
194             for cmd_buf in blocked.command_buffers {
195                 cmd_buf.commit();
196             }
197         }
198     }
199 }
200 
201 
202 #[derive(Debug)]
203 struct PoolShared {
204     online_recording: OnlineRecording,
205     #[cfg(feature = "dispatch")]
206     dispatch_queue: Option<NoDebug<dispatch::Queue>>,
207 }
208 
209 type CommandBufferInnerPtr = Arc<RefCell<CommandBufferInner>>;
210 type PoolSharedPtr = Arc<RefCell<PoolShared>>;
211 
212 #[derive(Debug)]
213 pub struct CommandPool {
214     shared: Arc<Shared>,
215     allocated: Vec<CommandBufferInnerPtr>,
216     pool_shared: PoolSharedPtr,
217 }
218 
219 unsafe impl Send for CommandPool {}
220 unsafe impl Sync for CommandPool {}
221 
222 impl CommandPool {
new(shared: &Arc<Shared>, online_recording: OnlineRecording) -> Self223     pub(crate) fn new(shared: &Arc<Shared>, online_recording: OnlineRecording) -> Self {
224         let pool_shared = PoolShared {
225             #[cfg(feature = "dispatch")]
226             dispatch_queue: match online_recording {
227                 OnlineRecording::Immediate | OnlineRecording::Deferred => None,
228                 OnlineRecording::Remote(ref priority) => {
229                     Some(NoDebug(dispatch::Queue::global(priority.clone())))
230                 }
231             },
232             online_recording,
233         };
234         CommandPool {
235             shared: Arc::clone(shared),
236             allocated: Vec::new(),
237             pool_shared: Arc::new(RefCell::new(pool_shared)),
238         }
239     }
240 }
241 
242 #[derive(Debug)]
243 pub struct CommandBuffer {
244     shared: Arc<Shared>,
245     pool_shared: PoolSharedPtr,
246     inner: CommandBufferInnerPtr,
247     state: State,
248     temp: Temp,
249     pub name: String,
250 }
251 
252 unsafe impl Send for CommandBuffer {}
253 unsafe impl Sync for CommandBuffer {}
254 
255 #[derive(Debug)]
256 struct Temp {
257     clear_vertices: Vec<ClearVertex>,
258     blit_vertices: FastHashMap<(Aspects, Level), Vec<BlitVertex>>,
259     clear_values: Vec<Option<com::ClearValue>>,
260 }
261 
262 type VertexBufferMaybeVec = Vec<Option<(pso::VertexBufferDesc, pso::ElemOffset)>>;
263 
264 #[derive(Debug)]
265 struct RenderPipelineState {
266     raw: metal::RenderPipelineState,
267     ds_desc: pso::DepthStencilDesc,
268     vertex_buffers: VertexBufferMaybeVec,
269     formats: native::SubpassFormats,
270 }
271 
272 #[derive(Debug)]
273 struct SubpassInfo {
274     descriptor: metal::RenderPassDescriptor,
275     combined_aspects: Aspects,
276     formats: native::SubpassFormats,
277 }
278 
279 #[derive(Debug, Default)]
280 struct DescriptorSetInfo {
281     graphics_resources: Vec<(ResourcePtr, metal::MTLResourceUsage)>,
282     compute_resources: Vec<(ResourcePtr, metal::MTLResourceUsage)>,
283 }
284 
285 /// The current state of a command buffer, used for two distinct purposes:
286 ///   1. inherit resource bindings between passes
287 ///   2. avoid redundant state settings
288 ///
289 /// ## Spaces
290 /// Note that these two usages are distinct and operate in technically different
291 /// spaces (1 - Vulkan, 2 - Metal), so be careful not to confuse them.
292 /// For example, Vulkan spaces are `pending_subpasses`, `rasterizer_state`, `target_*`.
293 /// While Metal spaces are `resources_*`.
294 ///
295 /// ## Vertex buffers
296 /// You may notice that vertex buffers are stored in two separate places: per pipeline, and
297 /// here in the state. These can't be merged together easily because at binding time we
298 /// want one input vertex buffer to potentially be bound to multiple entry points....
299 #[derive(Debug)]
300 struct State {
301     // Note: this could be `MTLViewport` but we have to patch the depth separately.
302     viewport: Option<(pso::Rect, Range<f32>)>,
303     scissors: Option<MTLScissorRect>,
304     blend_color: Option<pso::ColorValue>,
305     //TODO: move some of that state out, to avoid redundant allocations
306     render_pso: Option<RenderPipelineState>,
307     /// A flag to handle edge cases of Vulkan binding inheritance:
308     /// we don't want to consider the current PSO bound for a new pass if it's not compatible.
309     render_pso_is_compatible: bool,
310     compute_pso: Option<metal::ComputePipelineState>,
311     work_group_size: MTLSize,
312     primitive_type: MTLPrimitiveType,
313     //TODO: move Metal-side state into a separate struct
314     resources_vs: StageResources,
315     resources_ps: StageResources,
316     resources_cs: StageResources,
317     index_buffer: Option<IndexBuffer<BufferPtr>>,
318     rasterizer_state: Option<native::RasterizerState>,
319     depth_bias: pso::DepthBias,
320     stencil: native::StencilState<pso::StencilValue>,
321     push_constants: Vec<u32>,
322     vertex_buffers: Vec<Option<(BufferPtr, u64)>>,
323     ///TODO: add a structure to store render target state
324     target_aspects: Aspects,
325     target_extent: Extent,
326     target_formats: native::SubpassFormats,
327     visibility_query: (metal::MTLVisibilityResultMode, buffer::Offset),
328     pending_subpasses: Vec<SubpassInfo>,
329     descriptor_sets: ArrayVec<[DescriptorSetInfo; MAX_BOUND_DESCRIPTOR_SETS]>,
330 }
331 
332 impl State {
333     /// Resets the current Metal side of the state tracking.
reset_resources(&mut self)334     fn reset_resources(&mut self) {
335         self.resources_vs.clear();
336         self.resources_ps.clear();
337         self.resources_cs.clear();
338         self.push_constants.clear();
339         self.vertex_buffers.clear();
340         self.pending_subpasses.clear();
341         for ds in self.descriptor_sets.iter_mut() {
342             ds.graphics_resources.clear();
343             ds.compute_resources.clear();
344         }
345     }
346 
clamp_scissor(sr: MTLScissorRect, extent: Extent) -> MTLScissorRect347     fn clamp_scissor(sr: MTLScissorRect, extent: Extent) -> MTLScissorRect {
348         // sometimes there is not even an active render pass at this point
349         let x = sr.x.min(extent.width.max(1) as u64 - 1);
350         let y = sr.y.min(extent.height.max(1) as u64 - 1);
351         //TODO: handle the zero scissor size sensibly
352         MTLScissorRect {
353             x,
354             y,
355             width: ((sr.x + sr.width).min(extent.width as u64) - x).max(1),
356             height: ((sr.y + sr.height).min(extent.height as u64) - y).max(1),
357         }
358     }
359 
make_pso_commands( &self, ) -> ( Option<soft::RenderCommand<&soft::Ref>>, Option<soft::RenderCommand<&soft::Ref>>, )360     fn make_pso_commands(
361         &self,
362     ) -> (
363         Option<soft::RenderCommand<&soft::Ref>>,
364         Option<soft::RenderCommand<&soft::Ref>>,
365     ) {
366         if self.render_pso_is_compatible {
367             (
368                 self.render_pso
369                     .as_ref()
370                     .map(|ps| soft::RenderCommand::BindPipeline(&*ps.raw)),
371                 self.rasterizer_state
372                     .clone()
373                     .map(soft::RenderCommand::SetRasterizerState),
374             )
375         } else {
376             // Note: this is technically valid, we should not warn.
377             (None, None)
378         }
379     }
380 
make_render_commands( &self, aspects: Aspects, ) -> impl Iterator<Item = soft::RenderCommand<&soft::Ref>>381     fn make_render_commands(
382         &self,
383         aspects: Aspects,
384     ) -> impl Iterator<Item = soft::RenderCommand<&soft::Ref>> {
385         // Apply previously bound values for this command buffer
386         let com_vp = self
387             .viewport
388             .as_ref()
389             .map(|&(rect, ref depth)| soft::RenderCommand::SetViewport(rect, depth.clone()));
390         let com_scissor = self
391             .scissors
392             .map(|sr| soft::RenderCommand::SetScissor(Self::clamp_scissor(sr, self.target_extent)));
393         let com_blend = if aspects.contains(Aspects::COLOR) {
394             self.blend_color.map(soft::RenderCommand::SetBlendColor)
395         } else {
396             None
397         };
398         let com_depth_bias = if aspects.contains(Aspects::DEPTH) {
399             Some(soft::RenderCommand::SetDepthBias(self.depth_bias))
400         } else {
401             None
402         };
403         let com_visibility = if self.visibility_query.0 != metal::MTLVisibilityResultMode::Disabled
404         {
405             Some(soft::RenderCommand::SetVisibilityResult(
406                 self.visibility_query.0,
407                 self.visibility_query.1,
408             ))
409         } else {
410             None
411         };
412         let (com_pso, com_rast) = self.make_pso_commands();
413 
414         let render_resources = iter::once(&self.resources_vs).chain(iter::once(&self.resources_ps));
415         let push_constants = self.push_constants.as_slice();
416         let com_resources = [pso::Stage::Vertex, pso::Stage::Fragment]
417             .iter()
418             .zip(render_resources)
419             .flat_map(move |(&stage, resources)| {
420                 let com_buffers = soft::RenderCommand::BindBuffers {
421                     stage,
422                     index: 0,
423                     buffers: (&resources.buffers[..], &resources.buffer_offsets[..]),
424                 };
425                 let com_textures = soft::RenderCommand::BindTextures {
426                     stage,
427                     index: 0,
428                     textures: &resources.textures[..],
429                 };
430                 let com_samplers = soft::RenderCommand::BindSamplers {
431                     stage,
432                     index: 0,
433                     samplers: &resources.samplers[..],
434                 };
435                 let com_push_constants =
436                     resources
437                         .push_constants
438                         .map(|pc| soft::RenderCommand::BindBufferData {
439                             stage,
440                             index: pc.buffer_index as _,
441                             words: &push_constants[.. pc.count as usize],
442                         });
443                 iter::once(com_buffers)
444                     .chain(iter::once(com_textures))
445                     .chain(iter::once(com_samplers))
446                     .chain(com_push_constants)
447             });
448         let com_used_resources = self.descriptor_sets.iter().flat_map(|ds| {
449             ds.graphics_resources
450                 .iter()
451                 .map(|&(resource, usage)| soft::RenderCommand::UseResource { resource, usage })
452         });
453 
454         com_vp
455             .into_iter()
456             .chain(com_scissor)
457             .chain(com_blend)
458             .chain(com_depth_bias)
459             .chain(com_visibility)
460             .chain(com_pso)
461             .chain(com_rast)
462             //.chain(com_ds) // done outside
463             .chain(com_resources)
464             .chain(com_used_resources)
465     }
466 
make_compute_commands(&self) -> impl Iterator<Item = soft::ComputeCommand<&soft::Ref>>467     fn make_compute_commands(&self) -> impl Iterator<Item = soft::ComputeCommand<&soft::Ref>> {
468         let resources = &self.resources_cs;
469         let com_pso = self
470             .compute_pso
471             .as_ref()
472             .map(|pso| soft::ComputeCommand::BindPipeline(&**pso));
473         let com_buffers = soft::ComputeCommand::BindBuffers {
474             index: 0,
475             buffers: (&resources.buffers[..], &resources.buffer_offsets[..]),
476         };
477         let com_textures = soft::ComputeCommand::BindTextures {
478             index: 0,
479             textures: &resources.textures[..],
480         };
481         let com_samplers = soft::ComputeCommand::BindSamplers {
482             index: 0,
483             samplers: &resources.samplers[..],
484         };
485         let com_push_constants =
486             resources
487                 .push_constants
488                 .map(|pc| soft::ComputeCommand::BindBufferData {
489                     index: pc.buffer_index as _,
490                     words: &self.push_constants[.. pc.count as usize],
491                 });
492         let com_used_resources = self.descriptor_sets.iter().flat_map(|ds| {
493             ds.compute_resources
494                 .iter()
495                 .map(|&(resource, usage)| soft::ComputeCommand::UseResource { resource, usage })
496         });
497 
498 
499         com_pso
500             .into_iter()
501             .chain(iter::once(com_buffers))
502             .chain(iter::once(com_textures))
503             .chain(iter::once(com_samplers))
504             .chain(com_push_constants)
505             .chain(com_used_resources)
506     }
507 
set_vertex_buffers(&mut self, end: usize) -> Option<soft::RenderCommand<&soft::Ref>>508     fn set_vertex_buffers(&mut self, end: usize) -> Option<soft::RenderCommand<&soft::Ref>> {
509         let rps = self.render_pso.as_ref()?;
510         let start = end - rps.vertex_buffers.len();
511         self.resources_vs.pre_allocate_buffers(end);
512 
513         for ((out_buffer, out_offset), vb_maybe) in self.resources_vs.buffers[.. end]
514             .iter_mut()
515             .rev()
516             .zip(self.resources_vs.buffer_offsets[.. end].iter_mut().rev())
517             .zip(&rps.vertex_buffers)
518         {
519             match vb_maybe {
520                 Some((ref vb, extra_offset)) => {
521                     match self.vertex_buffers.get(vb.binding as usize) {
522                         Some(&Some((buffer, base_offset))) => {
523                             *out_buffer = Some(buffer);
524                             *out_offset = *extra_offset as u64 + base_offset;
525                         }
526                         _ => {
527                             // being unable to bind a buffer here is technically fine, since before this moment
528                             // and actual rendering there might be more bind calls
529                             *out_buffer = None;
530                         }
531                     }
532                 }
533                 None => {
534                     *out_buffer = None;
535                 }
536             }
537         }
538 
539         Some(soft::RenderCommand::BindBuffers {
540             stage: pso::Stage::Vertex,
541             index: start as ResourceIndex,
542             buffers: (
543                 &self.resources_vs.buffers[start .. end],
544                 &self.resources_vs.buffer_offsets[start .. end],
545             ),
546         })
547     }
548 
build_depth_stencil(&self) -> Option<pso::DepthStencilDesc>549     fn build_depth_stencil(&self) -> Option<pso::DepthStencilDesc> {
550         let mut desc = match self.render_pso {
551             Some(ref ps) => ps.ds_desc,
552             None => return None,
553         };
554 
555         if !self.target_aspects.contains(Aspects::DEPTH) {
556             desc.depth = None;
557         }
558         if !self.target_aspects.contains(Aspects::STENCIL) {
559             desc.stencil = None;
560         }
561 
562         if let Some(ref mut stencil) = desc.stencil {
563             stencil.reference_values = pso::State::Dynamic;
564             if stencil.read_masks.is_dynamic() {
565                 stencil.read_masks = pso::State::Static(self.stencil.read_masks);
566             }
567             if stencil.write_masks.is_dynamic() {
568                 stencil.write_masks = pso::State::Static(self.stencil.write_masks);
569             }
570         }
571 
572         Some(desc)
573     }
574 
set_depth_bias<'a>( &mut self, depth_bias: &pso::DepthBias, ) -> soft::RenderCommand<&'a soft::Ref>575     fn set_depth_bias<'a>(
576         &mut self,
577         depth_bias: &pso::DepthBias,
578     ) -> soft::RenderCommand<&'a soft::Ref> {
579         self.depth_bias = *depth_bias;
580         soft::RenderCommand::SetDepthBias(*depth_bias)
581     }
582 
push_vs_constants( &mut self, pc: native::PushConstantInfo, ) -> soft::RenderCommand<&soft::Ref>583     fn push_vs_constants(
584         &mut self,
585         pc: native::PushConstantInfo,
586     ) -> soft::RenderCommand<&soft::Ref> {
587         self.resources_vs.push_constants = Some(pc);
588         soft::RenderCommand::BindBufferData {
589             stage: pso::Stage::Vertex,
590             index: pc.buffer_index,
591             words: &self.push_constants[.. pc.count as usize],
592         }
593     }
594 
push_ps_constants( &mut self, pc: native::PushConstantInfo, ) -> soft::RenderCommand<&soft::Ref>595     fn push_ps_constants(
596         &mut self,
597         pc: native::PushConstantInfo,
598     ) -> soft::RenderCommand<&soft::Ref> {
599         self.resources_ps.push_constants = Some(pc);
600         soft::RenderCommand::BindBufferData {
601             stage: pso::Stage::Fragment,
602             index: pc.buffer_index,
603             words: &self.push_constants[.. pc.count as usize],
604         }
605     }
606 
push_cs_constants( &mut self, pc: native::PushConstantInfo, ) -> soft::ComputeCommand<&soft::Ref>607     fn push_cs_constants(
608         &mut self,
609         pc: native::PushConstantInfo,
610     ) -> soft::ComputeCommand<&soft::Ref> {
611         self.resources_cs.push_constants = Some(pc);
612         soft::ComputeCommand::BindBufferData {
613             index: pc.buffer_index,
614             words: &self.push_constants[.. pc.count as usize],
615         }
616     }
617 
set_viewport<'a>( &mut self, vp: &'a pso::Viewport, disabilities: PrivateDisabilities, ) -> soft::RenderCommand<&'a soft::Ref>618     fn set_viewport<'a>(
619         &mut self,
620         vp: &'a pso::Viewport,
621         disabilities: PrivateDisabilities,
622     ) -> soft::RenderCommand<&'a soft::Ref> {
623         let depth = vp.depth.start .. if disabilities.broken_viewport_near_depth {
624             (vp.depth.end - vp.depth.start)
625         } else {
626             vp.depth.end
627         };
628         self.viewport = Some((vp.rect, depth.clone()));
629         soft::RenderCommand::SetViewport(vp.rect, depth)
630     }
631 
set_scissor<'a>(&mut self, rect: pso::Rect) -> soft::RenderCommand<&'a soft::Ref>632     fn set_scissor<'a>(&mut self, rect: pso::Rect) -> soft::RenderCommand<&'a soft::Ref> {
633         let scissor = MTLScissorRect {
634             x: rect.x as _,
635             y: rect.y as _,
636             width: rect.w as _,
637             height: rect.h as _,
638         };
639         self.scissors = Some(scissor);
640         let clamped = State::clamp_scissor(scissor, self.target_extent);
641         soft::RenderCommand::SetScissor(clamped)
642     }
643 
set_blend_color<'a>( &mut self, color: &'a pso::ColorValue, ) -> soft::RenderCommand<&'a soft::Ref>644     fn set_blend_color<'a>(
645         &mut self,
646         color: &'a pso::ColorValue,
647     ) -> soft::RenderCommand<&'a soft::Ref> {
648         self.blend_color = Some(*color);
649         soft::RenderCommand::SetBlendColor(*color)
650     }
651 
update_push_constants(&mut self, offset: u32, constants: &[u32], total: u32)652     fn update_push_constants(&mut self, offset: u32, constants: &[u32], total: u32) {
653         assert_eq!(offset % WORD_ALIGNMENT as u32, 0);
654         let offset = (offset / WORD_ALIGNMENT as u32) as usize;
655         let data = &mut self.push_constants;
656         if data.len() < total as usize {
657             data.resize(total as usize, 0);
658         }
659         data[offset .. offset + constants.len()].copy_from_slice(constants);
660     }
661 
set_visibility_query( &mut self, mode: metal::MTLVisibilityResultMode, offset: buffer::Offset, ) -> soft::RenderCommand<&soft::Ref>662     fn set_visibility_query(
663         &mut self,
664         mode: metal::MTLVisibilityResultMode,
665         offset: buffer::Offset,
666     ) -> soft::RenderCommand<&soft::Ref> {
667         self.visibility_query = (mode, offset);
668         soft::RenderCommand::SetVisibilityResult(mode, offset)
669     }
670 }
671 
672 #[derive(Debug)]
673 struct StageResources {
674     buffers: Vec<Option<BufferPtr>>,
675     buffer_offsets: Vec<buffer::Offset>,
676     textures: Vec<Option<TexturePtr>>,
677     samplers: Vec<Option<SamplerPtr>>,
678     push_constants: Option<native::PushConstantInfo>,
679 }
680 
681 impl StageResources {
new() -> Self682     fn new() -> Self {
683         StageResources {
684             buffers: Vec::new(),
685             buffer_offsets: Vec::new(),
686             textures: Vec::new(),
687             samplers: Vec::new(),
688             push_constants: None,
689         }
690     }
691 
clear(&mut self)692     fn clear(&mut self) {
693         self.buffers.clear();
694         self.buffer_offsets.clear();
695         self.textures.clear();
696         self.samplers.clear();
697         self.push_constants = None;
698     }
699 
pre_allocate_buffers(&mut self, count: usize)700     fn pre_allocate_buffers(&mut self, count: usize) {
701         debug_assert_eq!(self.buffers.len(), self.buffer_offsets.len());
702         if self.buffers.len() < count {
703             self.buffers.resize(count, None);
704             self.buffer_offsets.resize(count, 0);
705         }
706     }
pre_allocate(&mut self, counters: &native::ResourceData<ResourceIndex>)707     fn pre_allocate(&mut self, counters: &native::ResourceData<ResourceIndex>) {
708         if self.textures.len() < counters.textures as usize {
709             self.textures.resize(counters.textures as usize, None);
710         }
711         if self.samplers.len() < counters.samplers as usize {
712             self.samplers.resize(counters.samplers as usize, None);
713         }
714         self.pre_allocate_buffers(counters.buffers as usize);
715     }
716 
bind_set( &mut self, stage: pso::ShaderStageFlags, data: &native::DescriptorEmulatedPoolInner, mut res_offset: native::ResourceData<ResourceIndex>, layouts: &[native::DescriptorLayout], pool_range: &native::ResourceData<Range<native::PoolResourceIndex>>, ) -> native::ResourceData<ResourceIndex>717     fn bind_set(
718         &mut self,
719         stage: pso::ShaderStageFlags,
720         data: &native::DescriptorEmulatedPoolInner,
721         mut res_offset: native::ResourceData<ResourceIndex>,
722         layouts: &[native::DescriptorLayout],
723         pool_range: &native::ResourceData<Range<native::PoolResourceIndex>>,
724     ) -> native::ResourceData<ResourceIndex> {
725         let mut pool_offsets = pool_range.map(|r| r.start);
726         for layout in layouts {
727             if layout.stages.contains(stage) {
728                 if layout.content.contains(native::DescriptorContent::SAMPLER) {
729                     self.samplers[res_offset.samplers as usize] =
730                         data.samplers[pool_offsets.samplers as usize];
731                     res_offset.samplers += 1;
732                     pool_offsets.samplers += 1;
733                 }
734                 if layout.content.contains(native::DescriptorContent::TEXTURE) {
735                     self.textures[res_offset.textures as usize] =
736                         data.textures[pool_offsets.textures as usize].map(|(t, _)| t);
737                     res_offset.textures += 1;
738                     pool_offsets.textures += 1;
739                 }
740                 if layout.content.contains(native::DescriptorContent::BUFFER) {
741                     let (buffer, offset) = match data.buffers[pool_offsets.buffers as usize] {
742                         Some((buffer, offset)) => (Some(buffer), offset),
743                         None => (None, 0),
744                     };
745                     self.buffers[res_offset.buffers as usize] = buffer;
746                     self.buffer_offsets[res_offset.buffers as usize] = offset;
747                     res_offset.buffers += 1;
748                     pool_offsets.buffers += 1;
749                 }
750             } else {
751                 pool_offsets.add(layout.content);
752             }
753         }
754         res_offset
755     }
756 }
757 
758 #[cfg(feature = "dispatch")]
759 #[derive(Debug, Default)]
760 struct Capacity {
761     render: usize,
762     compute: usize,
763     blit: usize,
764 }
765 
766 //TODO: make sure to recycle the heap allocation of these commands.
767 #[cfg(feature = "dispatch")]
768 #[derive(Debug)]
769 enum EncodePass {
770     Render(
771         Vec<soft::RenderCommand<soft::Own>>,
772         soft::Own,
773         metal::RenderPassDescriptor,
774         String,
775     ),
776     Compute(Vec<soft::ComputeCommand<soft::Own>>, soft::Own, String),
777     Blit(Vec<soft::BlitCommand>, String),
778 }
779 #[cfg(feature = "dispatch")]
780 unsafe impl Send for EncodePass {}
781 
782 #[cfg(feature = "dispatch")]
783 struct SharedCommandBuffer(Arc<Mutex<metal::CommandBuffer>>);
784 #[cfg(feature = "dispatch")]
785 unsafe impl Send for SharedCommandBuffer {}
786 
787 #[cfg(feature = "dispatch")]
788 impl EncodePass {
schedule(self, queue: &dispatch::Queue, cmd_buffer_arc: &Arc<Mutex<metal::CommandBuffer>>)789     fn schedule(self, queue: &dispatch::Queue, cmd_buffer_arc: &Arc<Mutex<metal::CommandBuffer>>) {
790         let cmd_buffer = SharedCommandBuffer(Arc::clone(cmd_buffer_arc));
791         queue.r#async(move || match self {
792             EncodePass::Render(list, resources, desc, label) => {
793                 let encoder = cmd_buffer
794                     .0
795                     .lock()
796                     .new_render_command_encoder(&desc)
797                     .to_owned();
798                 encoder.set_label(&label);
799                 for command in list {
800                     exec_render(&encoder, command, &resources);
801                 }
802                 encoder.end_encoding();
803             }
804             EncodePass::Compute(list, resources, label) => {
805                 let encoder = cmd_buffer.0.lock().new_compute_command_encoder().to_owned();
806                 encoder.set_label(&label);
807                 for command in list {
808                     exec_compute(&encoder, command, &resources);
809                 }
810                 encoder.end_encoding();
811             }
812             EncodePass::Blit(list, label) => {
813                 let encoder = cmd_buffer.0.lock().new_blit_command_encoder().to_owned();
814                 encoder.set_label(&label);
815                 for command in list {
816                     exec_blit(&encoder, command);
817                 }
818                 encoder.end_encoding();
819             }
820         });
821     }
822 
update(&self, capacity: &mut Capacity)823     fn update(&self, capacity: &mut Capacity) {
824         match &self {
825             EncodePass::Render(ref list, _, _, _) => capacity.render = capacity.render.max(list.len()),
826             EncodePass::Compute(ref list, _, _) => capacity.compute = capacity.compute.max(list.len()),
827             EncodePass::Blit(ref list, _) => capacity.blit = capacity.blit.max(list.len()),
828         }
829     }
830 }
831 
832 #[derive(Debug, Default)]
833 struct Journal {
834     resources: soft::Own,
835     passes: Vec<(soft::Pass, Range<usize>, String)>,
836     render_commands: Vec<soft::RenderCommand<soft::Own>>,
837     compute_commands: Vec<soft::ComputeCommand<soft::Own>>,
838     blit_commands: Vec<soft::BlitCommand>,
839 }
840 
841 impl Journal {
clear(&mut self)842     fn clear(&mut self) {
843         self.resources.clear();
844         self.passes.clear();
845         self.render_commands.clear();
846         self.compute_commands.clear();
847         self.blit_commands.clear();
848     }
849 
stop(&mut self)850     fn stop(&mut self) {
851         match self.passes.last_mut() {
852             None => {}
853             Some(&mut (soft::Pass::Render(_), ref mut range, _)) => {
854                 range.end = self.render_commands.len();
855             }
856             Some(&mut (soft::Pass::Compute, ref mut range, _)) => {
857                 range.end = self.compute_commands.len();
858             }
859             Some(&mut (soft::Pass::Blit, ref mut range, _)) => {
860                 range.end = self.blit_commands.len();
861             }
862         };
863     }
864 
record(&self, command_buf: &metal::CommandBufferRef)865     fn record(&self, command_buf: &metal::CommandBufferRef) {
866         for (ref pass, ref range, ref label) in &self.passes {
867             match *pass {
868                 soft::Pass::Render(ref desc) => {
869                     let encoder = command_buf.new_render_command_encoder(desc);
870                     encoder.set_label(label);
871                     for command in &self.render_commands[range.clone()] {
872                         exec_render(&encoder, command, &self.resources);
873                     }
874                     encoder.end_encoding();
875                 }
876                 soft::Pass::Blit => {
877                     let encoder = command_buf.new_blit_command_encoder();
878                     encoder.set_label(label);
879                     for command in &self.blit_commands[range.clone()] {
880                         exec_blit(&encoder, command);
881                     }
882                     encoder.end_encoding();
883                 }
884                 soft::Pass::Compute => {
885                     let encoder = command_buf.new_compute_command_encoder();
886                     encoder.set_label(label);
887                     for command in &self.compute_commands[range.clone()] {
888                         exec_compute(&encoder, command, &self.resources);
889                     }
890                     encoder.end_encoding();
891                 }
892             }
893         }
894     }
895 
extend(&mut self, other: &Self, inherit_pass: bool)896     fn extend(&mut self, other: &Self, inherit_pass: bool) {
897         if inherit_pass {
898             assert_eq!(other.passes.len(), 1);
899             match *self.passes.last_mut().unwrap() {
900                 (soft::Pass::Render(_), ref mut range, _) => {
901                     range.end += other.render_commands.len();
902                 }
903                 (soft::Pass::Compute, _, _) | (soft::Pass::Blit, _, _) => {
904                     panic!("Only render passes can inherit")
905                 }
906             }
907         } else {
908             for (pass, range, label) in &other.passes {
909                 let offset = match *pass {
910                     soft::Pass::Render(_) => self.render_commands.len(),
911                     soft::Pass::Compute => self.compute_commands.len(),
912                     soft::Pass::Blit => self.blit_commands.len(),
913                 };
914                 self.passes
915                     .alloc()
916                     .init((pass.clone(), range.start + offset .. range.end + offset, label.clone()));
917             }
918         }
919 
920         // Note: journals contain 3 levels of stuff:
921         // resources, commands, and passes
922         // Each upper level points to the lower one with index
923         // sub-ranges. In order to merge two journals, we need
924         // to fix those indices of the one that goes on top.
925         // This is referred here as "rebasing".
926         for mut com in other.render_commands.iter().cloned() {
927             self.resources.rebase_render(&mut com);
928             self.render_commands.push(com);
929         }
930         for mut com in other.compute_commands.iter().cloned() {
931             self.resources.rebase_compute(&mut com);
932             self.compute_commands.push(com);
933         }
934         self.blit_commands.extend_from_slice(&other.blit_commands);
935 
936         self.resources.extend(&other.resources);
937     }
938 }
939 
940 #[derive(Debug)]
941 enum CommandSink {
942     Immediate {
943         cmd_buffer: metal::CommandBuffer,
944         token: Token,
945         encoder_state: EncoderState,
946         num_passes: usize,
947         label: String,
948     },
949     Deferred {
950         is_encoding: bool,
951         is_inheriting: bool,
952         journal: Journal,
953         label: String,
954     },
955     #[cfg(feature = "dispatch")]
956     Remote {
957         queue: NoDebug<dispatch::Queue>,
958         cmd_buffer: Arc<Mutex<metal::CommandBuffer>>,
959         token: Token,
960         pass: Option<EncodePass>,
961         capacity: Capacity,
962         label: String,
963     },
964 }
965 
966 /// A helper temporary object that consumes state-setting commands only
967 /// applicable to a render pass currently encoded.
968 enum PreRender<'a> {
969     Immediate(&'a metal::RenderCommandEncoderRef),
970     Deferred(
971         &'a mut soft::Own,
972         &'a mut Vec<soft::RenderCommand<soft::Own>>,
973     ),
974     Void,
975 }
976 
977 impl<'a> PreRender<'a> {
is_void(&self) -> bool978     fn is_void(&self) -> bool {
979         match *self {
980             PreRender::Void => true,
981             _ => false,
982         }
983     }
984 
issue(&mut self, command: soft::RenderCommand<&soft::Ref>)985     fn issue(&mut self, command: soft::RenderCommand<&soft::Ref>) {
986         match *self {
987             PreRender::Immediate(encoder) => exec_render(encoder, command, &&soft::Ref),
988             PreRender::Deferred(ref mut resources, ref mut list) => {
989                 list.alloc().init(resources.own_render(command));
990             }
991             PreRender::Void => (),
992         }
993     }
994 
issue_many<'b, I>(&mut self, commands: I) where I: Iterator<Item = soft::RenderCommand<&'b soft::Ref>>,995     fn issue_many<'b, I>(&mut self, commands: I)
996     where
997         I: Iterator<Item = soft::RenderCommand<&'b soft::Ref>>,
998     {
999         match *self {
1000             PreRender::Immediate(encoder) => {
1001                 for com in commands {
1002                     exec_render(encoder, com, &&soft::Ref);
1003                 }
1004             }
1005             PreRender::Deferred(ref mut resources, ref mut list) => {
1006                 list.extend(commands.map(|com| resources.own_render(com)))
1007             }
1008             PreRender::Void => {}
1009         }
1010     }
1011 }
1012 
1013 /// A helper temporary object that consumes state-setting commands only
1014 /// applicable to a compute pass currently encoded.
1015 enum PreCompute<'a> {
1016     Immediate(&'a metal::ComputeCommandEncoderRef),
1017     Deferred(
1018         &'a mut soft::Own,
1019         &'a mut Vec<soft::ComputeCommand<soft::Own>>,
1020     ),
1021     Void,
1022 }
1023 
1024 impl<'a> PreCompute<'a> {
issue<'b>(&mut self, command: soft::ComputeCommand<&'b soft::Ref>)1025     fn issue<'b>(&mut self, command: soft::ComputeCommand<&'b soft::Ref>) {
1026         match *self {
1027             PreCompute::Immediate(encoder) => exec_compute(encoder, command, &&soft::Ref),
1028             PreCompute::Deferred(ref mut resources, ref mut list) => {
1029                 list.alloc().init(resources.own_compute(command));
1030             }
1031             PreCompute::Void => (),
1032         }
1033     }
1034 
issue_many<'b, I>(&mut self, commands: I) where I: Iterator<Item = soft::ComputeCommand<&'b soft::Ref>>,1035     fn issue_many<'b, I>(&mut self, commands: I)
1036     where
1037         I: Iterator<Item = soft::ComputeCommand<&'b soft::Ref>>,
1038     {
1039         match *self {
1040             PreCompute::Immediate(encoder) => {
1041                 for com in commands {
1042                     exec_compute(encoder, com, &&soft::Ref);
1043                 }
1044             }
1045             PreCompute::Deferred(ref mut resources, ref mut list) => {
1046                 list.extend(commands.map(|com| resources.own_compute(com)))
1047             }
1048             PreCompute::Void => {}
1049         }
1050     }
1051 }
1052 
1053 impl CommandSink {
label(&mut self, label: &str) -> &Self1054     fn label(&mut self, label: &str) -> &Self {
1055         match self {
1056             CommandSink::Immediate { label: l, .. } | CommandSink::Deferred { label: l, .. } => *l = label.to_string(),
1057             #[cfg(feature = "dispatch")]
1058             CommandSink::Remote { label: l, .. } => *l = label.to_string(),
1059         }
1060         self
1061     }
1062 
stop_encoding(&mut self)1063     fn stop_encoding(&mut self) {
1064         match *self {
1065             CommandSink::Immediate {
1066                 ref mut encoder_state,
1067                 ..
1068             } => {
1069                 encoder_state.end();
1070             }
1071             CommandSink::Deferred {
1072                 ref mut is_encoding,
1073                 ref mut journal,
1074                 ..
1075             } => {
1076                 *is_encoding = false;
1077                 journal.stop();
1078             }
1079             #[cfg(feature = "dispatch")]
1080             CommandSink::Remote {
1081                 queue: NoDebug(ref queue),
1082                 ref cmd_buffer,
1083                 ref mut pass,
1084                 ref mut capacity,
1085                 ..
1086             } => {
1087                 if let Some(pass) = pass.take() {
1088                     pass.update(capacity);
1089                     pass.schedule(queue, cmd_buffer);
1090                 }
1091             }
1092         }
1093     }
1094 
1095     /// Start issuing pre-render commands. Those can be rejected, so the caller is responsible
1096     /// for updating the state cache accordingly, so that it's set upon the start of a next pass.
pre_render(&mut self) -> PreRender1097     fn pre_render(&mut self) -> PreRender {
1098         match *self {
1099             CommandSink::Immediate {
1100                 encoder_state: EncoderState::Render(ref encoder),
1101                 ..
1102             } => PreRender::Immediate(encoder),
1103             CommandSink::Deferred {
1104                 is_encoding: true,
1105                 ref mut journal,
1106                 ..
1107             } => match journal.passes.last() {
1108                 Some(&(soft::Pass::Render(_), _, _)) => {
1109                     PreRender::Deferred(&mut journal.resources, &mut journal.render_commands)
1110                 }
1111                 _ => PreRender::Void,
1112             },
1113             #[cfg(feature = "dispatch")]
1114             CommandSink::Remote {
1115                 pass: Some(EncodePass::Render(ref mut list, ref mut resources, _, _)),
1116                 ..
1117             } => PreRender::Deferred(resources, list),
1118             _ => PreRender::Void,
1119         }
1120     }
1121 
1122     /// Switch the active encoder to render by starting a render pass.
switch_render(&mut self, descriptor: metal::RenderPassDescriptor) -> PreRender1123     fn switch_render(&mut self, descriptor: metal::RenderPassDescriptor) -> PreRender {
1124         //assert!(AutoReleasePool::is_active());
1125         self.stop_encoding();
1126 
1127         match *self {
1128             CommandSink::Immediate {
1129                 ref cmd_buffer,
1130                 ref mut encoder_state,
1131                 ref mut num_passes,
1132                 ref label,
1133                 ..
1134             } => {
1135                 *num_passes += 1;
1136                 let encoder = cmd_buffer.new_render_command_encoder(&descriptor);
1137                 encoder.set_label(label);
1138                 *encoder_state = EncoderState::Render(encoder.to_owned());
1139                 PreRender::Immediate(encoder)
1140             }
1141             CommandSink::Deferred {
1142                 ref mut is_encoding,
1143                 ref mut journal,
1144                 is_inheriting,
1145                 ref label,
1146                 ..
1147             } => {
1148                 assert!(!is_inheriting);
1149                 *is_encoding = true;
1150                 journal.passes.alloc().init((
1151                     soft::Pass::Render(descriptor),
1152                     journal.render_commands.len() .. 0,
1153                     label.clone(),
1154                 ));
1155                 PreRender::Deferred(&mut journal.resources, &mut journal.render_commands)
1156             }
1157             #[cfg(feature = "dispatch")]
1158             CommandSink::Remote {
1159                 ref mut pass,
1160                 ref capacity,
1161                 ref label,
1162                 ..
1163             } => {
1164                 let list = Vec::with_capacity(capacity.render);
1165                 *pass = Some(EncodePass::Render(list, soft::Own::default(), descriptor, label.clone()));
1166                 match *pass {
1167                     Some(EncodePass::Render(ref mut list, ref mut resources, _, _)) => {
1168                         PreRender::Deferred(resources, list)
1169                     }
1170                     _ => unreachable!(),
1171                 }
1172             }
1173         }
1174     }
1175 
quick_render<'a, I>( &mut self, label: &str, descriptor: metal::RenderPassDescriptor, commands: I, ) where I: Iterator<Item = soft::RenderCommand<&'a soft::Ref>>,1176     fn quick_render<'a, I>(
1177         &mut self,
1178         label: &str,
1179         descriptor: metal::RenderPassDescriptor,
1180         commands: I,
1181     ) where
1182         I: Iterator<Item = soft::RenderCommand<&'a soft::Ref>>,
1183     {
1184         {
1185             let mut pre = self.switch_render(descriptor);
1186             if let PreRender::Immediate(encoder) = pre {
1187                 encoder.set_label(label);
1188             }
1189             pre.issue_many(commands);
1190         }
1191         self.stop_encoding();
1192     }
1193 
1194     /// Issue provided blit commands. This function doesn't expect an active blit pass,
1195     /// it will automatically start one when needed.
blit_commands<I>(&mut self, commands: I) where I: Iterator<Item = soft::BlitCommand>,1196     fn blit_commands<I>(&mut self, commands: I)
1197     where
1198         I: Iterator<Item = soft::BlitCommand>,
1199     {
1200         enum PreBlit<'b> {
1201             Immediate(&'b metal::BlitCommandEncoderRef),
1202             Deferred(&'b mut Vec<soft::BlitCommand>),
1203         }
1204 
1205         let pre = match *self {
1206             CommandSink::Immediate {
1207                 encoder_state: EncoderState::Blit(ref encoder),
1208                 ..
1209             } => PreBlit::Immediate(encoder),
1210             CommandSink::Immediate {
1211                 ref cmd_buffer,
1212                 ref mut encoder_state,
1213                 ref mut num_passes,
1214                 ..
1215             } => {
1216                 *num_passes += 1;
1217                 encoder_state.end();
1218                 let encoder = cmd_buffer.new_blit_command_encoder();
1219                 *encoder_state = EncoderState::Blit(encoder.to_owned());
1220                 PreBlit::Immediate(encoder)
1221             }
1222             CommandSink::Deferred {
1223                 ref mut is_encoding,
1224                 is_inheriting,
1225                 ref mut journal,
1226                 ref label,
1227                 ..
1228             } => {
1229                 assert!(!is_inheriting);
1230                 *is_encoding = true;
1231                 if let Some(&(soft::Pass::Blit, _, _)) = journal.passes.last() {
1232                 } else {
1233                     journal.stop();
1234                     journal
1235                         .passes
1236                         .alloc()
1237                         .init((soft::Pass::Blit, journal.blit_commands.len() .. 0, label.clone()));
1238                 }
1239                 PreBlit::Deferred(&mut journal.blit_commands)
1240             }
1241             #[cfg(feature = "dispatch")]
1242             CommandSink::Remote {
1243                 pass: Some(EncodePass::Blit(ref mut list, _)),
1244                 ..
1245             } => PreBlit::Deferred(list),
1246             #[cfg(feature = "dispatch")]
1247             CommandSink::Remote {
1248                 queue: NoDebug(ref queue),
1249                 ref cmd_buffer,
1250                 ref mut pass,
1251                 ref mut capacity,
1252                 ref label,
1253                 ..
1254             } => {
1255                 if let Some(pass) = pass.take() {
1256                     pass.update(capacity);
1257                     pass.schedule(queue, cmd_buffer);
1258                 }
1259                 let list = Vec::with_capacity(capacity.blit);
1260                 *pass = Some(EncodePass::Blit(list, label.clone()));
1261                 match *pass {
1262                     Some(EncodePass::Blit(ref mut list, _)) => PreBlit::Deferred(list),
1263                     _ => unreachable!(),
1264                 }
1265             }
1266         };
1267 
1268         match pre {
1269             PreBlit::Immediate(encoder) => {
1270                 for com in commands {
1271                     exec_blit(encoder, com);
1272                 }
1273             }
1274             PreBlit::Deferred(list) => {
1275                 list.extend(commands);
1276             }
1277         }
1278     }
1279 
1280     /// Start issuing pre-compute commands. Those can be rejected, so the caller is responsible
1281     /// for updating the state cache accordingly, so that it's set upon the start of a next pass.
pre_compute(&mut self) -> PreCompute1282     fn pre_compute(&mut self) -> PreCompute {
1283         match *self {
1284             CommandSink::Immediate {
1285                 encoder_state: EncoderState::Compute(ref encoder),
1286                 ..
1287             } => PreCompute::Immediate(encoder),
1288             CommandSink::Deferred {
1289                 is_encoding: true,
1290                 is_inheriting: false,
1291                 ref mut journal,
1292                 ..
1293             } => match journal.passes.last() {
1294                 Some(&(soft::Pass::Compute, _, _)) => {
1295                     PreCompute::Deferred(&mut journal.resources, &mut journal.compute_commands)
1296                 }
1297                 _ => PreCompute::Void,
1298             },
1299             #[cfg(feature = "dispatch")]
1300             CommandSink::Remote {
1301                 pass: Some(EncodePass::Compute(ref mut list, ref mut resources, _)),
1302                 ..
1303             } => PreCompute::Deferred(resources, list),
1304             _ => PreCompute::Void,
1305         }
1306     }
1307 
1308     /// Switch the active encoder to compute.
1309     /// Second returned value is `true` if the switch has just happened.
switch_compute(&mut self) -> (PreCompute, bool)1310     fn switch_compute(&mut self) -> (PreCompute, bool) {
1311         match *self {
1312             CommandSink::Immediate {
1313                 encoder_state: EncoderState::Compute(ref encoder),
1314                 ..
1315             } => (PreCompute::Immediate(encoder), false),
1316             CommandSink::Immediate {
1317                 ref cmd_buffer,
1318                 ref mut encoder_state,
1319                 ref mut num_passes,
1320                 ..
1321             } => {
1322                 *num_passes += 1;
1323                 encoder_state.end();
1324                 let encoder = cmd_buffer.new_compute_command_encoder();
1325                 *encoder_state = EncoderState::Compute(encoder.to_owned());
1326                 (PreCompute::Immediate(encoder), true)
1327             }
1328             CommandSink::Deferred {
1329                 ref mut is_encoding,
1330                 is_inheriting,
1331                 ref mut journal,
1332                 ref label,
1333                 ..
1334             } => {
1335                 assert!(!is_inheriting);
1336                 *is_encoding = true;
1337                 let switch = if let Some(&(soft::Pass::Compute, _, _)) = journal.passes.last() {
1338                     false
1339                 } else {
1340                     journal.stop();
1341                     journal
1342                         .passes
1343                         .alloc()
1344                         .init((soft::Pass::Compute, journal.compute_commands.len() .. 0, label.clone()));
1345                     true
1346                 };
1347                 (
1348                     PreCompute::Deferred(&mut journal.resources, &mut journal.compute_commands),
1349                     switch,
1350                 )
1351             }
1352             #[cfg(feature = "dispatch")]
1353             CommandSink::Remote {
1354                 pass: Some(EncodePass::Compute(ref mut list, ref mut resources, _)),
1355                 ..
1356             } => (PreCompute::Deferred(resources, list), false),
1357             #[cfg(feature = "dispatch")]
1358             CommandSink::Remote {
1359                 queue: NoDebug(ref queue),
1360                 ref cmd_buffer,
1361                 ref mut pass,
1362                 ref mut capacity,
1363                 ref label,
1364                 ..
1365             } => {
1366                 if let Some(pass) = pass.take() {
1367                     pass.update(capacity);
1368                     pass.schedule(queue, cmd_buffer);
1369                 }
1370                 let list = Vec::with_capacity(capacity.compute);
1371                 *pass = Some(EncodePass::Compute(list, soft::Own::default(), label.clone()));
1372                 match *pass {
1373                     Some(EncodePass::Compute(ref mut list, ref mut resources, _)) => {
1374                         (PreCompute::Deferred(resources, list), true)
1375                     }
1376                     _ => unreachable!(),
1377                 }
1378             }
1379         }
1380     }
1381 
quick_compute<'a, I>(&mut self, label: &str, commands: I) where I: Iterator<Item = soft::ComputeCommand<&'a soft::Ref>>,1382     fn quick_compute<'a, I>(&mut self, label: &str, commands: I)
1383     where
1384         I: Iterator<Item = soft::ComputeCommand<&'a soft::Ref>>,
1385     {
1386         {
1387             let (mut pre, switch) = self.switch_compute();
1388             pre.issue_many(commands);
1389             if switch {
1390                 if let PreCompute::Immediate(encoder) = pre {
1391                     encoder.set_label(label);
1392                 }
1393             }
1394         }
1395         self.stop_encoding();
1396     }
1397 }
1398 
1399 #[derive(Clone, Debug)]
1400 pub struct IndexBuffer<B> {
1401     buffer: B,
1402     offset: u32,
1403     stride: u32,
1404 }
1405 
1406 /// This is an inner mutable part of the command buffer that is
1407 /// accessible by the owning command pool for one single reason:
1408 /// to reset it.
1409 #[derive(Debug)]
1410 pub struct CommandBufferInner {
1411     sink: Option<CommandSink>,
1412     level: com::Level,
1413     backup_journal: Option<Journal>,
1414     #[cfg(feature = "dispatch")]
1415     backup_capacity: Option<Capacity>,
1416     retained_buffers: Vec<metal::Buffer>,
1417     retained_textures: Vec<metal::Texture>,
1418     active_visibility_queries: Vec<query::Id>,
1419     events: Vec<(Arc<AtomicBool>, bool)>,
1420     host_events: Vec<Arc<AtomicBool>>,
1421 }
1422 
1423 impl Drop for CommandBufferInner {
drop(&mut self)1424     fn drop(&mut self) {
1425         if self.sink.is_some() {
1426             error!("Command buffer not released properly!");
1427         }
1428     }
1429 }
1430 
1431 impl CommandBufferInner {
reset(&mut self, shared: &Shared, release: bool)1432     pub(crate) fn reset(&mut self, shared: &Shared, release: bool) {
1433         match self.sink.take() {
1434             Some(CommandSink::Immediate {
1435                 token,
1436                 mut encoder_state,
1437                 ..
1438             }) => {
1439                 encoder_state.end();
1440                 shared.queue.lock().release(token);
1441             }
1442             Some(CommandSink::Deferred { mut journal, .. }) => {
1443                 if !release {
1444                     journal.clear();
1445                     self.backup_journal = Some(journal);
1446                 }
1447             }
1448             #[cfg(feature = "dispatch")]
1449             Some(CommandSink::Remote {
1450                 token, capacity, ..
1451             }) => {
1452                 shared.queue.lock().release(token);
1453                 if !release {
1454                     self.backup_capacity = Some(capacity);
1455                 }
1456             }
1457             None => {}
1458         };
1459         self.retained_buffers.clear();
1460         self.retained_textures.clear();
1461         self.active_visibility_queries.clear();
1462         self.events.clear();
1463     }
1464 
sink(&mut self) -> &mut CommandSink1465     fn sink(&mut self) -> &mut CommandSink {
1466         self.sink.as_mut().unwrap()
1467     }
1468 }
1469 
1470 #[derive(Debug)]
1471 enum EncoderState {
1472     None,
1473     Blit(metal::BlitCommandEncoder),
1474     Render(metal::RenderCommandEncoder),
1475     Compute(metal::ComputeCommandEncoder),
1476 }
1477 
1478 impl EncoderState {
end(&mut self)1479     fn end(&mut self) {
1480         match mem::replace(self, EncoderState::None) {
1481             EncoderState::None => {}
1482             EncoderState::Render(ref encoder) => {
1483                 encoder.end_encoding();
1484             }
1485             EncoderState::Blit(ref encoder) => {
1486                 encoder.end_encoding();
1487             }
1488             EncoderState::Compute(ref encoder) => {
1489                 encoder.end_encoding();
1490             }
1491         }
1492     }
1493 }
1494 
div(a: u32, b: u32) -> u321495 fn div(a: u32, b: u32) -> u32 {
1496     (a + b - 1) / b
1497 }
1498 
compute_pitches(region: &com::BufferImageCopy, fd: FormatDesc, extent: &MTLSize) -> (u32, u32)1499 fn compute_pitches(region: &com::BufferImageCopy, fd: FormatDesc, extent: &MTLSize) -> (u32, u32) {
1500     let buffer_width = if region.buffer_width == 0 {
1501         extent.width as u32
1502     } else {
1503         region.buffer_width
1504     };
1505     let buffer_height = if region.buffer_height == 0 {
1506         extent.height as u32
1507     } else {
1508         region.buffer_height
1509     };
1510     let row_pitch = div(buffer_width, fd.dim.0 as _) * (fd.bits / 8) as u32;
1511     let slice_pitch = div(buffer_height, fd.dim.1 as _) * row_pitch;
1512     (row_pitch, slice_pitch)
1513 }
1514 
exec_render<R, C>(encoder: &metal::RenderCommandEncoderRef, command: C, resources: &R) where R: soft::Resources, R::Data: Borrow<[u32]>, R::BufferArray: soft::AsSlice<Option<BufferPtr>, R> + soft::AsSlice<buffer::Offset, R>, R::TextureArray: soft::AsSlice<Option<TexturePtr>, R>, R::SamplerArray: soft::AsSlice<Option<SamplerPtr>, R>, R::DepthStencil: Borrow<metal::DepthStencilStateRef>, R::RenderPipeline: Borrow<metal::RenderPipelineStateRef>, C: Borrow<soft::RenderCommand<R>>,1515 fn exec_render<R, C>(encoder: &metal::RenderCommandEncoderRef, command: C, resources: &R)
1516 where
1517     R: soft::Resources,
1518     R::Data: Borrow<[u32]>,
1519     R::BufferArray: soft::AsSlice<Option<BufferPtr>, R> + soft::AsSlice<buffer::Offset, R>,
1520     R::TextureArray: soft::AsSlice<Option<TexturePtr>, R>,
1521     R::SamplerArray: soft::AsSlice<Option<SamplerPtr>, R>,
1522     R::DepthStencil: Borrow<metal::DepthStencilStateRef>,
1523     R::RenderPipeline: Borrow<metal::RenderPipelineStateRef>,
1524     C: Borrow<soft::RenderCommand<R>>,
1525 {
1526     use crate::soft::RenderCommand as Cmd;
1527     match *command.borrow() {
1528         Cmd::SetViewport(ref rect, ref depth) => {
1529             encoder.set_viewport(MTLViewport {
1530                 originX: rect.x as _,
1531                 originY: rect.y as _,
1532                 width: rect.w as _,
1533                 height: rect.h as _,
1534                 znear: depth.start as _,
1535                 zfar: depth.end as _,
1536             });
1537         }
1538         Cmd::SetScissor(scissor) => {
1539             encoder.set_scissor_rect(scissor);
1540         }
1541         Cmd::SetBlendColor(color) => {
1542             encoder.set_blend_color(color[0], color[1], color[2], color[3]);
1543         }
1544         Cmd::SetDepthBias(depth_bias) => {
1545             encoder.set_depth_bias(
1546                 depth_bias.const_factor,
1547                 depth_bias.slope_factor,
1548                 depth_bias.clamp,
1549             );
1550         }
1551         Cmd::SetDepthStencilState(ref depth_stencil) => {
1552             encoder.set_depth_stencil_state(depth_stencil.borrow());
1553         }
1554         Cmd::SetStencilReferenceValues(sided) => {
1555             encoder.set_stencil_front_back_reference_value(sided.front, sided.back);
1556         }
1557         Cmd::SetRasterizerState(ref rs) => {
1558             encoder.set_front_facing_winding(rs.front_winding);
1559             encoder.set_cull_mode(rs.cull_mode);
1560             encoder.set_triangle_fill_mode(rs.fill_mode);
1561             if let Some(depth_clip) = rs.depth_clip {
1562                 encoder.set_depth_clip_mode(depth_clip);
1563             }
1564         }
1565         Cmd::SetVisibilityResult(mode, offset) => {
1566             encoder.set_visibility_result_mode(mode, offset);
1567         }
1568         Cmd::BindBuffer {
1569             stage,
1570             index,
1571             buffer,
1572             offset,
1573         } => {
1574             let native = Some(buffer.as_native());
1575             match stage {
1576                 pso::Stage::Vertex => encoder.set_vertex_buffer(index as _, native, offset as _),
1577                 pso::Stage::Fragment => {
1578                     encoder.set_fragment_buffer(index as _, native, offset as _)
1579                 }
1580                 _ => unreachable!(),
1581             }
1582         }
1583         Cmd::BindBuffers {
1584             stage,
1585             index,
1586             ref buffers,
1587         } => {
1588             use crate::soft::AsSlice;
1589             let values: &[Option<BufferPtr>] = buffers.as_slice(resources);
1590             if !values.is_empty() {
1591                 let data = unsafe {
1592                     // convert `BufferPtr` -> `&metal::BufferRef`
1593                     mem::transmute(values)
1594                 };
1595                 let offsets = buffers.as_slice(resources);
1596                 match stage {
1597                     pso::Stage::Vertex => encoder.set_vertex_buffers(index as _, data, offsets),
1598                     pso::Stage::Fragment => encoder.set_fragment_buffers(index as _, data, offsets),
1599                     _ => unreachable!(),
1600                 }
1601             }
1602         }
1603         Cmd::BindBufferData {
1604             stage,
1605             index,
1606             ref words,
1607         } => {
1608             let slice = words.borrow();
1609             match stage {
1610                 pso::Stage::Vertex => encoder.set_vertex_bytes(
1611                     index as _,
1612                     (slice.len() * WORD_SIZE) as u64,
1613                     slice.as_ptr() as _,
1614                 ),
1615                 pso::Stage::Fragment => encoder.set_fragment_bytes(
1616                     index as _,
1617                     (slice.len() * WORD_SIZE) as u64,
1618                     slice.as_ptr() as _,
1619                 ),
1620                 _ => unreachable!(),
1621             }
1622         }
1623         Cmd::BindTextures {
1624             stage,
1625             index,
1626             ref textures,
1627         } => {
1628             use crate::soft::AsSlice;
1629             let values = textures.as_slice(resources);
1630             if !values.is_empty() {
1631                 let data = unsafe {
1632                     // convert `TexturePtr` -> `&metal::TextureRef`
1633                     mem::transmute(values)
1634                 };
1635                 match stage {
1636                     pso::Stage::Vertex => encoder.set_vertex_textures(index as _, data),
1637                     pso::Stage::Fragment => encoder.set_fragment_textures(index as _, data),
1638                     _ => unreachable!(),
1639                 }
1640             }
1641         }
1642         Cmd::BindSamplers {
1643             stage,
1644             index,
1645             ref samplers,
1646         } => {
1647             use crate::soft::AsSlice;
1648             let values = samplers.as_slice(resources);
1649             if !values.is_empty() {
1650                 let data = unsafe {
1651                     // convert `SamplerPtr` -> `&metal::SamplerStateRef`
1652                     mem::transmute(values)
1653                 };
1654                 match stage {
1655                     pso::Stage::Vertex => encoder.set_vertex_sampler_states(index as _, data),
1656                     pso::Stage::Fragment => encoder.set_fragment_sampler_states(index as _, data),
1657                     _ => unreachable!(),
1658                 }
1659             }
1660         }
1661         Cmd::BindPipeline(ref pipeline_state) => {
1662             encoder.set_render_pipeline_state(pipeline_state.borrow());
1663         }
1664         Cmd::UseResource { resource, usage } => {
1665             encoder.use_resource(resource.as_native(), usage);
1666         }
1667         Cmd::Draw {
1668             primitive_type,
1669             ref vertices,
1670             ref instances,
1671         } => {
1672             if instances.end == 1 {
1673                 encoder.draw_primitives(
1674                     primitive_type,
1675                     vertices.start as _,
1676                     (vertices.end - vertices.start) as _,
1677                 );
1678             } else if instances.start == 0 {
1679                 encoder.draw_primitives_instanced(
1680                     primitive_type,
1681                     vertices.start as _,
1682                     (vertices.end - vertices.start) as _,
1683                     instances.end as _,
1684                 );
1685             } else {
1686                 encoder.draw_primitives_instanced_base_instance(
1687                     primitive_type,
1688                     vertices.start as _,
1689                     (vertices.end - vertices.start) as _,
1690                     (instances.end - instances.start) as _,
1691                     instances.start as _,
1692                 );
1693             }
1694         }
1695         Cmd::DrawIndexed {
1696             primitive_type,
1697             ref index,
1698             ref indices,
1699             base_vertex,
1700             ref instances,
1701         } => {
1702             let index_count = (indices.end - indices.start) as _;
1703             let index_type = match index.stride {
1704                 2 => MTLIndexType::UInt16,
1705                 4 => MTLIndexType::UInt32,
1706                 _ => unreachable!(),
1707             };
1708             let offset = (index.offset + indices.start * index.stride) as u64;
1709             let index_buffer = index.buffer.as_native();
1710             if base_vertex == 0 && instances.end == 1 {
1711                 encoder.draw_indexed_primitives(
1712                     primitive_type,
1713                     index_count,
1714                     index_type,
1715                     index_buffer,
1716                     offset,
1717                 );
1718             } else if base_vertex == 0 && instances.start == 0 {
1719                 encoder.draw_indexed_primitives_instanced(
1720                     primitive_type,
1721                     index_count,
1722                     index_type,
1723                     index_buffer,
1724                     offset,
1725                     instances.end as _,
1726                 );
1727             } else {
1728                 encoder.draw_indexed_primitives_instanced_base_instance(
1729                     primitive_type,
1730                     index_count,
1731                     index_type,
1732                     index_buffer,
1733                     offset,
1734                     (instances.end - instances.start) as _,
1735                     base_vertex as _,
1736                     instances.start as _,
1737                 );
1738             }
1739         }
1740         Cmd::DrawIndirect {
1741             primitive_type,
1742             buffer,
1743             offset,
1744         } => {
1745             encoder.draw_primitives_indirect(primitive_type, buffer.as_native(), offset);
1746         }
1747         Cmd::DrawIndexedIndirect {
1748             primitive_type,
1749             ref index,
1750             buffer,
1751             offset,
1752         } => {
1753             let index_type = match index.stride {
1754                 2 => MTLIndexType::UInt16,
1755                 4 => MTLIndexType::UInt32,
1756                 _ => unreachable!(),
1757             };
1758             encoder.draw_indexed_primitives_indirect(
1759                 primitive_type,
1760                 index_type,
1761                 index.buffer.as_native(),
1762                 index.offset as u64,
1763                 buffer.as_native(),
1764                 offset,
1765             );
1766         }
1767     }
1768 }
1769 
exec_blit<C>(encoder: &metal::BlitCommandEncoderRef, command: C) where C: Borrow<soft::BlitCommand>,1770 fn exec_blit<C>(encoder: &metal::BlitCommandEncoderRef, command: C)
1771 where
1772     C: Borrow<soft::BlitCommand>,
1773 {
1774     use crate::soft::BlitCommand as Cmd;
1775     match *command.borrow() {
1776         Cmd::FillBuffer {
1777             dst,
1778             ref range,
1779             value,
1780         } => {
1781             encoder.fill_buffer(
1782                 dst.as_native(),
1783                 NSRange {
1784                     location: range.start,
1785                     length: range.end - range.start,
1786                 },
1787                 value,
1788             );
1789         }
1790         Cmd::CopyBuffer {
1791             src,
1792             dst,
1793             ref region,
1794         } => {
1795             encoder.copy_from_buffer(
1796                 src.as_native(),
1797                 region.src as NSUInteger,
1798                 dst.as_native(),
1799                 region.dst as NSUInteger,
1800                 region.size as NSUInteger,
1801             );
1802         }
1803         Cmd::CopyImage {
1804             src,
1805             dst,
1806             ref region,
1807         } => {
1808             let size = conv::map_extent(region.extent);
1809             let src_offset = conv::map_offset(region.src_offset);
1810             let dst_offset = conv::map_offset(region.dst_offset);
1811             let layers = region
1812                 .src_subresource
1813                 .layers
1814                 .clone()
1815                 .zip(region.dst_subresource.layers.clone());
1816             for (src_layer, dst_layer) in layers {
1817                 encoder.copy_from_texture(
1818                     src.as_native(),
1819                     src_layer as _,
1820                     region.src_subresource.level as _,
1821                     src_offset,
1822                     size,
1823                     dst.as_native(),
1824                     dst_layer as _,
1825                     region.dst_subresource.level as _,
1826                     dst_offset,
1827                 );
1828             }
1829         }
1830         Cmd::CopyBufferToImage {
1831             src,
1832             dst,
1833             dst_desc,
1834             ref region,
1835         } => {
1836             let extent = conv::map_extent(region.image_extent);
1837             let origin = conv::map_offset(region.image_offset);
1838             let (row_pitch, slice_pitch) = compute_pitches(&region, dst_desc, &extent);
1839             let r = &region.image_layers;
1840 
1841             for layer in r.layers.clone() {
1842                 let offset = region.buffer_offset
1843                     + slice_pitch as NSUInteger * (layer - r.layers.start) as NSUInteger;
1844                 encoder.copy_from_buffer_to_texture(
1845                     src.as_native(),
1846                     offset as NSUInteger,
1847                     row_pitch as NSUInteger,
1848                     slice_pitch as NSUInteger,
1849                     extent,
1850                     dst.as_native(),
1851                     layer as NSUInteger,
1852                     r.level as NSUInteger,
1853                     origin,
1854                     metal::MTLBlitOption::empty(),
1855                 );
1856             }
1857         }
1858         Cmd::CopyImageToBuffer {
1859             src,
1860             src_desc,
1861             dst,
1862             ref region,
1863         } => {
1864             let extent = conv::map_extent(region.image_extent);
1865             let origin = conv::map_offset(region.image_offset);
1866             let (row_pitch, slice_pitch) = compute_pitches(&region, src_desc, &extent);
1867             let r = &region.image_layers;
1868 
1869             for layer in r.layers.clone() {
1870                 let offset = region.buffer_offset
1871                     + slice_pitch as NSUInteger * (layer - r.layers.start) as NSUInteger;
1872                 encoder.copy_from_texture_to_buffer(
1873                     src.as_native(),
1874                     layer as NSUInteger,
1875                     r.level as NSUInteger,
1876                     origin,
1877                     extent,
1878                     dst.as_native(),
1879                     offset as NSUInteger,
1880                     row_pitch as NSUInteger,
1881                     slice_pitch as NSUInteger,
1882                     metal::MTLBlitOption::empty(),
1883                 );
1884             }
1885         }
1886     }
1887 }
1888 
exec_compute<R, C>(encoder: &metal::ComputeCommandEncoderRef, command: C, resources: &R) where R: soft::Resources, R::Data: Borrow<[u32]>, R::BufferArray: soft::AsSlice<Option<BufferPtr>, R> + soft::AsSlice<buffer::Offset, R>, R::TextureArray: soft::AsSlice<Option<TexturePtr>, R>, R::SamplerArray: soft::AsSlice<Option<SamplerPtr>, R>, R::ComputePipeline: Borrow<metal::ComputePipelineStateRef>, C: Borrow<soft::ComputeCommand<R>>,1889 fn exec_compute<R, C>(encoder: &metal::ComputeCommandEncoderRef, command: C, resources: &R)
1890 where
1891     R: soft::Resources,
1892     R::Data: Borrow<[u32]>,
1893     R::BufferArray: soft::AsSlice<Option<BufferPtr>, R> + soft::AsSlice<buffer::Offset, R>,
1894     R::TextureArray: soft::AsSlice<Option<TexturePtr>, R>,
1895     R::SamplerArray: soft::AsSlice<Option<SamplerPtr>, R>,
1896     R::ComputePipeline: Borrow<metal::ComputePipelineStateRef>,
1897     C: Borrow<soft::ComputeCommand<R>>,
1898 {
1899     use crate::soft::ComputeCommand as Cmd;
1900     match *command.borrow() {
1901         Cmd::BindBuffer {
1902             index,
1903             buffer,
1904             offset,
1905         } => {
1906             let native = Some(buffer.as_native());
1907             encoder.set_buffer(index as _, native, offset);
1908         }
1909         Cmd::BindBuffers { index, ref buffers } => {
1910             use crate::soft::AsSlice;
1911             let values: &[Option<BufferPtr>] = buffers.as_slice(resources);
1912             if !values.is_empty() {
1913                 let data = unsafe {
1914                     // convert `BufferPtr` -> `&metal::BufferRef`
1915                     mem::transmute(values)
1916                 };
1917                 let offsets = buffers.as_slice(resources);
1918                 encoder.set_buffers(index as _, data, offsets);
1919             }
1920         }
1921         Cmd::BindBufferData { ref words, index } => {
1922             let slice = words.borrow();
1923             encoder.set_bytes(
1924                 index as _,
1925                 (slice.len() * WORD_SIZE) as u64,
1926                 slice.as_ptr() as _,
1927             );
1928         }
1929         Cmd::BindTextures {
1930             index,
1931             ref textures,
1932         } => {
1933             use crate::soft::AsSlice;
1934             let values = textures.as_slice(resources);
1935             if !values.is_empty() {
1936                 let data = unsafe {
1937                     // convert `TexturePtr` -> `&metal::TextureRef`
1938                     mem::transmute(values)
1939                 };
1940                 encoder.set_textures(index as _, data);
1941             }
1942         }
1943         Cmd::BindSamplers {
1944             index,
1945             ref samplers,
1946         } => {
1947             use crate::soft::AsSlice;
1948             let values = samplers.as_slice(resources);
1949             if !values.is_empty() {
1950                 let data = unsafe {
1951                     // convert `SamplerPtr` -> `&metal::SamplerStateRef`
1952                     mem::transmute(values)
1953                 };
1954                 encoder.set_sampler_states(index as _, data);
1955             }
1956         }
1957         Cmd::BindPipeline(ref pipeline) => {
1958             encoder.set_compute_pipeline_state(pipeline.borrow());
1959         }
1960         Cmd::UseResource { resource, usage } => {
1961             encoder.use_resource(resource.as_native(), usage);
1962         }
1963         Cmd::Dispatch { wg_size, wg_count } => {
1964             encoder.dispatch_thread_groups(wg_count, wg_size);
1965         }
1966         Cmd::DispatchIndirect {
1967             wg_size,
1968             buffer,
1969             offset,
1970         } => {
1971             encoder.dispatch_thread_groups_indirect(buffer.as_native(), offset, wg_size);
1972         }
1973     }
1974 }
1975 
1976 
1977 #[derive(Default, Debug)]
1978 struct PerformanceCounters {
1979     immediate_command_buffers: usize,
1980     deferred_command_buffers: usize,
1981     remote_command_buffers: usize,
1982     signal_command_buffers: usize,
1983     frame_wait_duration: time::Duration,
1984     frame_wait_count: usize,
1985     frame: usize,
1986 }
1987 
1988 #[derive(Debug)]
1989 pub struct CommandQueue {
1990     shared: Arc<Shared>,
1991     retained_buffers: Vec<metal::Buffer>,
1992     retained_textures: Vec<metal::Texture>,
1993     active_visibility_queries: Vec<query::Id>,
1994     perf_counters: Option<PerformanceCounters>,
1995     /// If true, we combine deferred command buffers together into one giant
1996     /// command buffer per submission, including the signalling logic.
1997     pub stitch_deferred: bool,
1998     /// Hack around the Metal System Trace logic that ignores empty command buffers entirely.
1999     pub insert_dummy_encoders: bool,
2000 }
2001 
2002 unsafe impl Send for CommandQueue {}
2003 unsafe impl Sync for CommandQueue {}
2004 
2005 impl CommandQueue {
new(shared: Arc<Shared>) -> Self2006     pub(crate) fn new(shared: Arc<Shared>) -> Self {
2007         CommandQueue {
2008             shared,
2009             retained_buffers: Vec::new(),
2010             retained_textures: Vec::new(),
2011             active_visibility_queries: Vec::new(),
2012             perf_counters: if COUNTERS_REPORT_WINDOW != 0 {
2013                 Some(PerformanceCounters::default())
2014             } else {
2015                 None
2016             },
2017             stitch_deferred: true,
2018             insert_dummy_encoders: false,
2019         }
2020     }
2021 
2022     /// This is a hack around Metal System Trace logic that ignores empty command buffers entirely.
record_empty(&self, command_buf: &metal::CommandBufferRef)2023     fn record_empty(&self, command_buf: &metal::CommandBufferRef) {
2024         if self.insert_dummy_encoders {
2025             command_buf.new_blit_command_encoder().end_encoding();
2026         }
2027     }
2028 
wait<'a, T, I>(&mut self, wait_semaphores: I) where T: 'a + Borrow<native::Semaphore>, I: IntoIterator<Item = &'a T>,2029     fn wait<'a, T, I>(&mut self, wait_semaphores: I)
2030     where
2031         T: 'a + Borrow<native::Semaphore>,
2032         I: IntoIterator<Item = &'a T>,
2033     {
2034         for semaphore in wait_semaphores {
2035             let sem = semaphore.borrow();
2036             if let Some(ref system) = sem.system {
2037                 system.wait(!0);
2038             }
2039             if let Some(swap_image) = sem.image_ready.lock().take() {
2040                 let start = time::Instant::now();
2041                 let count = swap_image.wait_until_ready();
2042                 if let Some(ref mut counters) = self.perf_counters {
2043                     counters.frame_wait_count += count;
2044                     counters.frame_wait_duration += start.elapsed();
2045                 }
2046             }
2047         }
2048     }
2049 }
2050 
2051 impl hal::queue::CommandQueue<Backend> for CommandQueue {
2052     unsafe fn submit<'a, T, Ic, S, Iw, Is>(
2053         &mut self,
2054         hal::queue::Submission {
2055             command_buffers,
2056             wait_semaphores,
2057             signal_semaphores,
2058         }: hal::queue::Submission<Ic, Iw, Is>,
2059         fence: Option<&native::Fence>,
2060     ) where
2061         T: 'a + Borrow<CommandBuffer>,
2062         Ic: IntoIterator<Item = &'a T>,
2063         S: 'a + Borrow<native::Semaphore>,
2064         Iw: IntoIterator<Item = (&'a S, pso::PipelineStage)>,
2065         Is: IntoIterator<Item = &'a S>,
2066     {
2067         use smallvec::SmallVec;
2068 
2069         debug!("submitting with fence {:?}", fence);
2070         self.wait(wait_semaphores.into_iter().map(|(s, _)| s));
2071 
2072         const BLOCK_BUCKET: usize = 4;
2073         let system_semaphores = signal_semaphores
2074             .into_iter()
2075             .filter_map(|sem| sem.borrow().system.clone())
2076             .collect::<SmallVec<[_; BLOCK_BUCKET]>>();
2077 
2078         #[allow(unused_mut)]
2079         let (mut num_immediate, mut num_deferred, mut num_remote) = (0, 0, 0);
2080         let mut event_commands = Vec::new();
2081         let do_signal = fence.is_some() || !system_semaphores.is_empty();
2082 
2083         autoreleasepool(|| {
2084             // for command buffers
2085             let cmd_queue = self.shared.queue.lock();
2086             let mut blocker = self.shared.queue_blocker.lock();
2087             let mut deferred_cmd_buffer = None::<&metal::CommandBufferRef>;
2088 
2089             for buffer in command_buffers {
2090                 let mut inner = buffer.borrow().inner.borrow_mut();
2091                 let CommandBufferInner {
2092                     ref sink,
2093                     ref mut retained_buffers,
2094                     ref mut retained_textures,
2095                     ref mut active_visibility_queries,
2096                     ref events,
2097                     ref host_events,
2098                     ..
2099                 } = *inner;
2100 
2101                 //TODO: split event commands into immediate/blocked submissions?
2102                 event_commands.extend_from_slice(events);
2103                 // wait for anything not previously fired
2104                 let wait_events = host_events
2105                     .iter()
2106                     .filter(|event| {
2107                         event_commands
2108                             .iter()
2109                             .rfind(|ev| Arc::ptr_eq(event, &ev.0))
2110                             .map_or(true, |ev| !ev.1)
2111                     })
2112                     .cloned()
2113                     .collect::<Vec<_>>();
2114                 if !wait_events.is_empty() {
2115                     blocker.submissions.push(BlockedSubmission {
2116                         wait_events,
2117                         command_buffers: Vec::new(),
2118                     });
2119                 }
2120 
2121                 match *sink {
2122                     Some(CommandSink::Immediate {
2123                         ref cmd_buffer,
2124                         ref token,
2125                         num_passes,
2126                         ..
2127                     }) => {
2128                         num_immediate += 1;
2129                         trace!("\timmediate {:?} with {} passes", token, num_passes);
2130                         self.retained_buffers.extend(retained_buffers.drain(..));
2131                         self.retained_textures.extend(retained_textures.drain(..));
2132                         self.active_visibility_queries
2133                             .extend(active_visibility_queries.drain(..));
2134                         if num_passes != 0 {
2135                             // flush the deferred recording, if any
2136                             if let Some(cb) = deferred_cmd_buffer.take() {
2137                                 blocker.submit_impl(cb);
2138                             }
2139                             blocker.submit_impl(cmd_buffer);
2140                         }
2141                     }
2142                     Some(CommandSink::Deferred { ref journal, .. }) => {
2143                         num_deferred += 1;
2144                         trace!("\tdeferred with {} passes", journal.passes.len());
2145                         self.active_visibility_queries
2146                             .extend_from_slice(active_visibility_queries);
2147                         if !journal.passes.is_empty() {
2148                             let cmd_buffer = deferred_cmd_buffer.take().unwrap_or_else(|| {
2149                                 let cmd_buffer = cmd_queue.spawn_temp();
2150                                 cmd_buffer.enqueue();
2151                                 cmd_buffer.set_label("deferred");
2152                                 cmd_buffer
2153                             });
2154                             journal.record(&*cmd_buffer);
2155                             if self.stitch_deferred {
2156                                 deferred_cmd_buffer = Some(cmd_buffer);
2157                             } else {
2158                                 blocker.submit_impl(cmd_buffer);
2159                             }
2160                         }
2161                     }
2162                     #[cfg(feature = "dispatch")]
2163                     Some(CommandSink::Remote {
2164                         queue: NoDebug(ref queue),
2165                         ref cmd_buffer,
2166                         ref token,
2167                         ..
2168                     }) => {
2169                         num_remote += 1;
2170                         trace!("\tremote {:?}", token);
2171                         cmd_buffer.lock().enqueue();
2172                         let shared_cb = SharedCommandBuffer(Arc::clone(cmd_buffer));
2173                         //TODO: make this compatible with events
2174                         queue.sync(move || {
2175                             shared_cb.0.lock().commit();
2176                         });
2177                     }
2178                     None => panic!("Command buffer not recorded for submission"),
2179                 }
2180             }
2181 
2182             if do_signal || !event_commands.is_empty() || !self.active_visibility_queries.is_empty()
2183             {
2184                 //Note: there is quite a bit copying here
2185                 let free_buffers = self
2186                     .retained_buffers
2187                     .drain(..)
2188                     .collect::<SmallVec<[_; BLOCK_BUCKET]>>();
2189                 let free_textures = self
2190                     .retained_textures
2191                     .drain(..)
2192                     .collect::<SmallVec<[_; BLOCK_BUCKET]>>();
2193                 let visibility = if self.active_visibility_queries.is_empty() {
2194                     None
2195                 } else {
2196                     let queries = self
2197                         .active_visibility_queries
2198                         .drain(..)
2199                         .collect::<SmallVec<[_; BLOCK_BUCKET]>>();
2200                     Some((Arc::clone(&self.shared), queries))
2201                 };
2202 
2203                 let block = ConcreteBlock::new(move |_cb: *mut ()| {
2204                     // signal the semaphores
2205                     for semaphore in &system_semaphores {
2206                         semaphore.signal();
2207                     }
2208                     // process events
2209                     for &(ref atomic, value) in &event_commands {
2210                         atomic.store(value, Ordering::Release);
2211                     }
2212                     // free all the manually retained resources
2213                     let _ = free_buffers;
2214                     let _ = free_textures;
2215                     // update visibility queries
2216                     if let Some((ref shared, ref queries)) = visibility {
2217                         let vis = &shared.visibility;
2218                         let availability_ptr = (vis.buffer.contents() as *mut u8)
2219                             .offset(vis.availability_offset as isize)
2220                             as *mut u32;
2221                         for &q in queries {
2222                             *availability_ptr.offset(q as isize) = 1;
2223                         }
2224                         //HACK: the lock is needed to wake up, but it doesn't hold the checked data
2225                         let _ = vis.allocator.lock();
2226                         vis.condvar.notify_all();
2227                     }
2228                 })
2229                 .copy();
2230 
2231                 let cmd_buffer = deferred_cmd_buffer.take().unwrap_or_else(|| {
2232                     let cmd_buffer = cmd_queue.spawn_temp();
2233                     cmd_buffer.set_label("signal");
2234                     self.record_empty(cmd_buffer);
2235                     cmd_buffer
2236                 });
2237                 let () = msg_send![cmd_buffer, addCompletedHandler: block.deref() as *const _];
2238                 blocker.submit_impl(cmd_buffer);
2239 
2240                 if let Some(fence) = fence {
2241                     debug!("\tmarking fence ptr {:?} as pending", fence.0.as_ptr());
2242                     fence
2243                         .0
2244                         .replace(native::FenceInner::PendingSubmission(cmd_buffer.to_owned()));
2245                 }
2246             } else if let Some(cmd_buffer) = deferred_cmd_buffer {
2247                 blocker.submit_impl(cmd_buffer);
2248             }
2249         });
2250 
2251         debug!(
2252             "\t{} immediate, {} deferred, and {} remote command buffers",
2253             num_immediate, num_deferred, num_remote
2254         );
2255         if let Some(ref mut counters) = self.perf_counters {
2256             counters.immediate_command_buffers += num_immediate;
2257             counters.deferred_command_buffers += num_deferred;
2258             counters.remote_command_buffers += num_remote;
2259             if do_signal {
2260                 counters.signal_command_buffers += 1;
2261             }
2262         }
2263     }
2264 
present<'a, W, Is, S, Iw>( &mut self, swapchains: Is, wait_semaphores: Iw, ) -> Result<Option<Suboptimal>, PresentError> where W: 'a + Borrow<window::Swapchain>, Is: IntoIterator<Item = (&'a W, SwapImageIndex)>, S: 'a + Borrow<native::Semaphore>, Iw: IntoIterator<Item = &'a S>,2265     unsafe fn present<'a, W, Is, S, Iw>(
2266         &mut self,
2267         swapchains: Is,
2268         wait_semaphores: Iw,
2269     ) -> Result<Option<Suboptimal>, PresentError>
2270     where
2271         W: 'a + Borrow<window::Swapchain>,
2272         Is: IntoIterator<Item = (&'a W, SwapImageIndex)>,
2273         S: 'a + Borrow<native::Semaphore>,
2274         Iw: IntoIterator<Item = &'a S>,
2275     {
2276         self.wait(wait_semaphores);
2277 
2278         let queue = self.shared.queue.lock();
2279         autoreleasepool(|| {
2280             let command_buffer = queue.raw.new_command_buffer();
2281             command_buffer.set_label("present");
2282             self.record_empty(command_buffer);
2283 
2284             for (swapchain, index) in swapchains {
2285                 debug!("presenting frame {}", index);
2286                 let drawable = swapchain
2287                     .borrow()
2288                     .take_drawable(index)
2289                     .map_err(|()| PresentError::OutOfDate)?; // What `Err(())` represents?
2290                 command_buffer.present_drawable(&drawable);
2291             }
2292             command_buffer.commit();
2293             Ok(())
2294         })?;
2295 
2296         if let Some(ref mut counters) = self.perf_counters {
2297             counters.frame += 1;
2298             if counters.frame >= COUNTERS_REPORT_WINDOW {
2299                 let time = counters.frame_wait_duration / counters.frame as u32;
2300                 let total_submitted = counters.immediate_command_buffers
2301                     + counters.deferred_command_buffers
2302                     + counters.remote_command_buffers
2303                     + counters.signal_command_buffers;
2304                 println!("Performance counters:");
2305                 println!(
2306                     "\tCommand buffers: {} immediate, {} deferred, {} remote, {} signals",
2307                     counters.immediate_command_buffers / counters.frame,
2308                     counters.deferred_command_buffers / counters.frame,
2309                     counters.remote_command_buffers / counters.frame,
2310                     counters.signal_command_buffers / counters.frame,
2311                 );
2312                 println!("\tEstimated pipeline length is {} frames, given the total active {} command buffers",
2313                     counters.frame * queue.reserve.start / total_submitted.max(1),
2314                     queue.reserve.start,
2315                 );
2316                 println!(
2317                     "\tFrame wait time is {}ms over {} requests",
2318                     time.as_secs() as u32 * 1000 + time.subsec_millis(),
2319                     counters.frame_wait_count as f32 / counters.frame as f32,
2320                 );
2321                 *counters = PerformanceCounters::default();
2322             }
2323         }
2324 
2325         Ok(None)
2326     }
2327 
present_surface( &mut self, _surface: &mut window::Surface, image: window::SurfaceImage, wait_semaphore: Option<&native::Semaphore>, ) -> Result<Option<Suboptimal>, PresentError>2328     unsafe fn present_surface(
2329         &mut self,
2330         _surface: &mut window::Surface,
2331         image: window::SurfaceImage,
2332         wait_semaphore: Option<&native::Semaphore>,
2333     ) -> Result<Option<Suboptimal>, PresentError> {
2334         self.wait(wait_semaphore);
2335 
2336         let queue = self.shared.queue.lock();
2337         let drawable = image.into_drawable();
2338         autoreleasepool(|| {
2339             let command_buffer = queue.raw.new_command_buffer();
2340             command_buffer.set_label("present");
2341             self.record_empty(command_buffer);
2342 
2343             command_buffer.present_drawable(&drawable);
2344             command_buffer.commit();
2345         });
2346         Ok(None)
2347     }
2348 
wait_idle(&self) -> Result<(), OutOfMemory>2349     fn wait_idle(&self) -> Result<(), OutOfMemory> {
2350         QueueInner::wait_idle(&self.shared.queue);
2351         Ok(())
2352     }
2353 }
2354 
assign_sides( this: &mut pso::Sided<pso::StencilValue>, faces: pso::Face, value: pso::StencilValue, )2355 fn assign_sides(
2356     this: &mut pso::Sided<pso::StencilValue>,
2357     faces: pso::Face,
2358     value: pso::StencilValue,
2359 ) {
2360     if faces.contains(pso::Face::FRONT) {
2361         this.front = value;
2362     }
2363     if faces.contains(pso::Face::BACK) {
2364         this.back = value;
2365     }
2366 }
2367 
2368 impl hal::pool::CommandPool<Backend> for CommandPool {
reset(&mut self, release_resources: bool)2369     unsafe fn reset(&mut self, release_resources: bool) {
2370         for cmd_buffer in &self.allocated {
2371             cmd_buffer
2372                 .borrow_mut()
2373                 .reset(&self.shared, release_resources);
2374         }
2375     }
2376 
allocate_one(&mut self, level: com::Level) -> CommandBuffer2377     unsafe fn allocate_one(&mut self, level: com::Level) -> CommandBuffer {
2378         //TODO: fail with OOM if we allocate more actual command buffers
2379         // than our mega-queue supports.
2380         let inner = Arc::new(RefCell::new(CommandBufferInner {
2381             sink: None,
2382             level,
2383             backup_journal: None,
2384             #[cfg(feature = "dispatch")]
2385             backup_capacity: None,
2386             retained_buffers: Vec::new(),
2387             retained_textures: Vec::new(),
2388             active_visibility_queries: Vec::new(),
2389             events: Vec::new(),
2390             host_events: Vec::new(),
2391         }));
2392         self.allocated.push(Arc::clone(&inner));
2393 
2394         CommandBuffer {
2395             shared: Arc::clone(&self.shared),
2396             pool_shared: Arc::clone(&self.pool_shared),
2397             inner,
2398             state: State {
2399                 viewport: None,
2400                 scissors: None,
2401                 blend_color: None,
2402                 render_pso: None,
2403                 render_pso_is_compatible: false,
2404                 compute_pso: None,
2405                 work_group_size: MTLSize {
2406                     width: 0,
2407                     height: 0,
2408                     depth: 0,
2409                 },
2410                 primitive_type: MTLPrimitiveType::Point,
2411                 resources_vs: StageResources::new(),
2412                 resources_ps: StageResources::new(),
2413                 resources_cs: StageResources::new(),
2414                 index_buffer: None,
2415                 rasterizer_state: None,
2416                 depth_bias: pso::DepthBias::default(),
2417                 stencil: native::StencilState {
2418                     reference_values: pso::Sided::new(0),
2419                     read_masks: pso::Sided::new(!0),
2420                     write_masks: pso::Sided::new(!0),
2421                 },
2422                 push_constants: Vec::new(),
2423                 vertex_buffers: Vec::new(),
2424                 target_aspects: Aspects::empty(),
2425                 target_extent: Extent::default(),
2426                 target_formats: native::SubpassFormats::default(),
2427                 visibility_query: (metal::MTLVisibilityResultMode::Disabled, 0),
2428                 pending_subpasses: Vec::new(),
2429                 descriptor_sets: (0 .. MAX_BOUND_DESCRIPTOR_SETS)
2430                     .map(|_| DescriptorSetInfo::default())
2431                     .collect(),
2432             },
2433             temp: Temp {
2434                 clear_vertices: Vec::new(),
2435                 blit_vertices: FastHashMap::default(),
2436                 clear_values: Vec::new(),
2437             },
2438             name: String::new(),
2439         }
2440     }
2441 
2442     /// Free command buffers which are allocated from this pool.
free<I>(&mut self, cmd_buffers: I) where I: IntoIterator<Item = CommandBuffer>,2443     unsafe fn free<I>(&mut self, cmd_buffers: I)
2444     where
2445         I: IntoIterator<Item = CommandBuffer>,
2446     {
2447         use hal::command::CommandBuffer as _;
2448         for mut cmd_buf in cmd_buffers {
2449             cmd_buf.reset(true);
2450             match self
2451                 .allocated
2452                 .iter_mut()
2453                 .position(|b| Arc::ptr_eq(b, &cmd_buf.inner))
2454             {
2455                 Some(index) => {
2456                     self.allocated.swap_remove(index);
2457                 }
2458                 None => error!("Unable to free a command buffer!"),
2459             }
2460         }
2461     }
2462 }
2463 
2464 impl CommandBuffer {
update_depth_stencil(&self)2465     fn update_depth_stencil(&self) {
2466         let mut inner = self.inner.borrow_mut();
2467         let mut pre = inner.sink().pre_render();
2468         if !pre.is_void() {
2469             let ds_store = &self.shared.service_pipes.depth_stencil_states;
2470             if let Some(desc) = self.state.build_depth_stencil() {
2471                 let state = &**ds_store.get(desc, &self.shared.device);
2472                 pre.issue(soft::RenderCommand::SetDepthStencilState(state));
2473             }
2474         }
2475     }
2476 }
2477 
2478 impl com::CommandBuffer<Backend> for CommandBuffer {
begin( &mut self, flags: com::CommandBufferFlags, info: com::CommandBufferInheritanceInfo<Backend>, )2479     unsafe fn begin(
2480         &mut self,
2481         flags: com::CommandBufferFlags,
2482         info: com::CommandBufferInheritanceInfo<Backend>,
2483     ) {
2484         self.reset(false);
2485 
2486         let mut inner = self.inner.borrow_mut();
2487         let can_immediate = inner.level == com::Level::Primary
2488             && flags.contains(com::CommandBufferFlags::ONE_TIME_SUBMIT);
2489         let sink = match self.pool_shared.borrow_mut().online_recording {
2490             OnlineRecording::Immediate if can_immediate => {
2491                 let (cmd_buffer, token) = self.shared.queue.lock().spawn();
2492                 cmd_buffer.set_label(&self.name);
2493                 CommandSink::Immediate {
2494                     cmd_buffer,
2495                     token,
2496                     encoder_state: EncoderState::None,
2497                     num_passes: 0,
2498                     label: String::new(),
2499                 }
2500             }
2501             #[cfg(feature = "dispatch")]
2502             OnlineRecording::Remote(_) if can_immediate => {
2503                 let (cmd_buffer, token) = self.shared.queue.lock().spawn();
2504                 cmd_buffer.set_label(&self.name);
2505                 CommandSink::Remote {
2506                     queue: NoDebug(dispatch::Queue::with_target_queue(
2507                         "gfx-metal",
2508                         dispatch::QueueAttribute::Serial,
2509                         &self
2510                             .pool_shared
2511                             .borrow_mut()
2512                             .dispatch_queue
2513                             .as_ref()
2514                             .unwrap()
2515                             .0,
2516                     )),
2517                     cmd_buffer: Arc::new(Mutex::new(cmd_buffer)),
2518                     token,
2519                     pass: None,
2520                     capacity: inner.backup_capacity.take().unwrap_or_default(),
2521                     label: String::new(),
2522                 }
2523             }
2524             _ => CommandSink::Deferred {
2525                 is_encoding: false,
2526                 is_inheriting: info.subpass.is_some(),
2527                 journal: inner.backup_journal.take().unwrap_or_default(),
2528                 label: String::new(),
2529             },
2530         };
2531         inner.sink = Some(sink);
2532 
2533         if let Some(framebuffer) = info.framebuffer {
2534             self.state.target_extent = framebuffer.extent;
2535         }
2536         if let Some(sp) = info.subpass {
2537             let subpass = &sp.main_pass.subpasses[sp.index];
2538             self.state.target_formats.copy_from(&subpass.target_formats);
2539 
2540             self.state.target_aspects = Aspects::empty();
2541             if !subpass.colors.is_empty() {
2542                 self.state.target_aspects |= Aspects::COLOR;
2543             }
2544             if let Some((at_id, _)) = subpass.depth_stencil {
2545                 let rat = &sp.main_pass.attachments[at_id];
2546                 let aspects = rat.format.unwrap().surface_desc().aspects;
2547                 self.state.target_aspects |= aspects;
2548             }
2549 
2550             match inner.sink {
2551                 Some(CommandSink::Deferred {
2552                     ref mut is_encoding,
2553                     ref mut journal,
2554                     ref label,
2555                     ..
2556                 }) => {
2557                     *is_encoding = true;
2558                     let pass_desc = metal::RenderPassDescriptor::new().to_owned();
2559                     journal
2560                         .passes
2561                         .alloc()
2562                         .init((soft::Pass::Render(pass_desc), 0 .. 0, label.clone()));
2563                 }
2564                 _ => {
2565                     warn!("Unexpected inheritance info on a primary command buffer");
2566                 }
2567             }
2568         }
2569     }
2570 
finish(&mut self)2571     unsafe fn finish(&mut self) {
2572         self.inner.borrow_mut().sink().stop_encoding();
2573     }
2574 
reset(&mut self, release_resources: bool)2575     unsafe fn reset(&mut self, release_resources: bool) {
2576         self.state.reset_resources();
2577         self.inner
2578             .borrow_mut()
2579             .reset(&self.shared, release_resources);
2580     }
2581 
pipeline_barrier<'a, T>( &mut self, _stages: Range<pso::PipelineStage>, _dependencies: memory::Dependencies, _barriers: T, ) where T: IntoIterator, T::Item: Borrow<memory::Barrier<'a, Backend>>,2582     unsafe fn pipeline_barrier<'a, T>(
2583         &mut self,
2584         _stages: Range<pso::PipelineStage>,
2585         _dependencies: memory::Dependencies,
2586         _barriers: T,
2587     ) where
2588         T: IntoIterator,
2589         T::Item: Borrow<memory::Barrier<'a, Backend>>,
2590     {
2591     }
2592 
fill_buffer<R>(&mut self, buffer: &native::Buffer, range: R, data: u32) where R: RangeArg<buffer::Offset>,2593     unsafe fn fill_buffer<R>(&mut self, buffer: &native::Buffer, range: R, data: u32)
2594     where
2595         R: RangeArg<buffer::Offset>,
2596     {
2597         let (raw, base_range) = buffer.as_bound();
2598         let mut inner = self.inner.borrow_mut();
2599 
2600         let start = base_range.start + *range.start().unwrap_or(&0);
2601         assert_eq!(start % WORD_ALIGNMENT, 0);
2602 
2603         let end = match range.end() {
2604             Some(&e) => {
2605                 assert_eq!(e % WORD_ALIGNMENT, 0);
2606                 base_range.start + e
2607             }
2608             None => base_range.end,
2609         };
2610 
2611         if (data & 0xFF) * 0x0101_0101 == data {
2612             let command = soft::BlitCommand::FillBuffer {
2613                 dst: AsNative::from(raw),
2614                 range: start .. end,
2615                 value: data as u8,
2616             };
2617             inner.sink().blit_commands(iter::once(command));
2618         } else {
2619             let pso = &*self.shared.service_pipes.fill_buffer;
2620             let length = (end - start) / WORD_ALIGNMENT;
2621             let value_and_length = [data, length as _];
2622 
2623             // TODO: Consider writing multiple values per thread in shader
2624             let threads_per_threadgroup = pso.thread_execution_width();
2625             let threadgroups = (length + threads_per_threadgroup - 1) / threads_per_threadgroup;
2626 
2627             let wg_count = MTLSize {
2628                 width: threadgroups,
2629                 height: 1,
2630                 depth: 1,
2631             };
2632             let wg_size = MTLSize {
2633                 width: threads_per_threadgroup,
2634                 height: 1,
2635                 depth: 1,
2636             };
2637 
2638             let commands = [
2639                 soft::ComputeCommand::BindPipeline(pso),
2640                 soft::ComputeCommand::BindBuffer {
2641                     index: 0,
2642                     buffer: AsNative::from(raw),
2643                     offset: start,
2644                 },
2645                 soft::ComputeCommand::BindBufferData {
2646                     index: 1,
2647                     words: &value_and_length[..],
2648                 },
2649                 soft::ComputeCommand::Dispatch { wg_size, wg_count },
2650             ];
2651 
2652             inner
2653                 .sink()
2654                 .quick_compute("fill_buffer", commands.iter().cloned());
2655         }
2656     }
2657 
update_buffer(&mut self, dst: &native::Buffer, offset: buffer::Offset, data: &[u8])2658     unsafe fn update_buffer(&mut self, dst: &native::Buffer, offset: buffer::Offset, data: &[u8]) {
2659         let (dst_raw, dst_range) = dst.as_bound();
2660         assert!(dst_range.start + offset + data.len() as buffer::Offset <= dst_range.end);
2661 
2662         let src = self.shared.device.lock().new_buffer_with_data(
2663             data.as_ptr() as _,
2664             data.len() as _,
2665             metal::MTLResourceOptions::CPUCacheModeWriteCombined,
2666         );
2667         src.set_label("update_buffer");
2668 
2669         let mut inner = self.inner.borrow_mut();
2670         {
2671             let command = soft::BlitCommand::CopyBuffer {
2672                 src: AsNative::from(src.as_ref()),
2673                 dst: AsNative::from(dst_raw),
2674                 region: com::BufferCopy {
2675                     src: 0,
2676                     dst: dst_range.start + offset,
2677                     size: data.len() as _,
2678                 },
2679             };
2680 
2681             inner.sink().blit_commands(iter::once(command));
2682         }
2683 
2684         inner.retained_buffers.push(src);
2685     }
2686 
clear_image<T>( &mut self, image: &native::Image, _layout: Layout, value: com::ClearValue, subresource_ranges: T, ) where T: IntoIterator, T::Item: Borrow<SubresourceRange>,2687     unsafe fn clear_image<T>(
2688         &mut self,
2689         image: &native::Image,
2690         _layout: Layout,
2691         value: com::ClearValue,
2692         subresource_ranges: T,
2693     ) where
2694         T: IntoIterator,
2695         T::Item: Borrow<SubresourceRange>,
2696     {
2697         let CommandBufferInner {
2698             ref mut retained_textures,
2699             ref mut sink,
2700             ..
2701         } = *self.inner.borrow_mut();
2702 
2703         let clear_color = image.shader_channel.interpret(value.color);
2704         let base_extent = image.kind.extent();
2705         let is_layered = !self.shared.disabilities.broken_layered_clear_image;
2706 
2707         autoreleasepool(|| {
2708             let raw = image.like.as_texture();
2709             for subresource_range in subresource_ranges {
2710                 let sub = subresource_range.borrow();
2711                 let num_layers = (sub.layers.end - sub.layers.start) as u64;
2712                 let layers = if is_layered {
2713                     0 .. 1
2714                 } else {
2715                     sub.layers.clone()
2716                 };
2717                 let texture = if is_layered && sub.layers.start > 0 {
2718                     // aliasing is necessary for bulk-clearing all layers starting with 0
2719                     let tex = raw.new_texture_view_from_slice(
2720                         image.mtl_format,
2721                         image.mtl_type,
2722                         NSRange {
2723                             location: 0,
2724                             length: raw.mipmap_level_count(),
2725                         },
2726                         NSRange {
2727                             location: sub.layers.start as _,
2728                             length: num_layers,
2729                         },
2730                     );
2731                     retained_textures.push(tex);
2732                     retained_textures.last().unwrap()
2733                 } else {
2734                     raw
2735                 };
2736 
2737                 for layer in layers {
2738                     for level in sub.levels.clone() {
2739                         let descriptor = metal::RenderPassDescriptor::new().to_owned();
2740                         if base_extent.depth > 1 {
2741                             assert_eq!(sub.layers.end, 1);
2742                             let depth = base_extent.at_level(level).depth as u64;
2743                             descriptor.set_render_target_array_length(depth);
2744                         } else if is_layered {
2745                             descriptor.set_render_target_array_length(num_layers);
2746                         };
2747 
2748                         if image.format_desc.aspects.contains(Aspects::COLOR) {
2749                             let attachment = descriptor.color_attachments().object_at(0).unwrap();
2750                             attachment.set_texture(Some(texture));
2751                             attachment.set_level(level as _);
2752                             if !is_layered {
2753                                 attachment.set_slice(layer as _);
2754                             }
2755                             attachment.set_store_action(metal::MTLStoreAction::Store);
2756                             if sub.aspects.contains(Aspects::COLOR) {
2757                                 attachment.set_load_action(metal::MTLLoadAction::Clear);
2758                                 attachment.set_clear_color(clear_color.clone());
2759                             } else {
2760                                 attachment.set_load_action(metal::MTLLoadAction::Load);
2761                             }
2762                         } else {
2763                             assert!(!sub.aspects.contains(Aspects::COLOR));
2764                         };
2765 
2766                         if image.format_desc.aspects.contains(Aspects::DEPTH) {
2767                             let attachment = descriptor.depth_attachment().unwrap();
2768                             attachment.set_texture(Some(texture));
2769                             attachment.set_level(level as _);
2770                             if !is_layered {
2771                                 attachment.set_slice(layer as _);
2772                             }
2773                             attachment.set_store_action(metal::MTLStoreAction::Store);
2774                             if sub.aspects.contains(Aspects::DEPTH) {
2775                                 attachment.set_load_action(metal::MTLLoadAction::Clear);
2776                                 attachment.set_clear_depth(value.depth_stencil.depth as _);
2777                             } else {
2778                                 attachment.set_load_action(metal::MTLLoadAction::Load);
2779                             }
2780                         } else {
2781                             assert!(!sub.aspects.contains(Aspects::DEPTH));
2782                         };
2783 
2784                         if image.format_desc.aspects.contains(Aspects::STENCIL) {
2785                             let attachment = descriptor.stencil_attachment().unwrap();
2786                             attachment.set_texture(Some(texture));
2787                             attachment.set_level(level as _);
2788                             if !is_layered {
2789                                 attachment.set_slice(layer as _);
2790                             }
2791                             attachment.set_store_action(metal::MTLStoreAction::Store);
2792                             if sub.aspects.contains(Aspects::STENCIL) {
2793                                 attachment.set_load_action(metal::MTLLoadAction::Clear);
2794                                 attachment.set_clear_stencil(value.depth_stencil.stencil);
2795                             } else {
2796                                 attachment.set_load_action(metal::MTLLoadAction::Load);
2797                             }
2798                         } else {
2799                             assert!(!sub.aspects.contains(Aspects::STENCIL));
2800                         };
2801 
2802                         sink.as_mut().unwrap().quick_render(
2803                             "clear_image",
2804                             descriptor,
2805                             iter::empty(),
2806                         );
2807                     }
2808                 }
2809             }
2810         });
2811     }
2812 
clear_attachments<T, U>(&mut self, clears: T, rects: U) where T: IntoIterator, T::Item: Borrow<com::AttachmentClear>, U: IntoIterator, U::Item: Borrow<pso::ClearRect>,2813     unsafe fn clear_attachments<T, U>(&mut self, clears: T, rects: U)
2814     where
2815         T: IntoIterator,
2816         T::Item: Borrow<com::AttachmentClear>,
2817         U: IntoIterator,
2818         U::Item: Borrow<pso::ClearRect>,
2819     {
2820         // gather vertices/polygons
2821         let de = self.state.target_extent;
2822         let vertices = &mut self.temp.clear_vertices;
2823         vertices.clear();
2824 
2825         for rect in rects {
2826             let r = rect.borrow();
2827             for layer in r.layers.clone() {
2828                 let data = [
2829                     [r.rect.x, r.rect.y],
2830                     [r.rect.x, r.rect.y + r.rect.h],
2831                     [r.rect.x + r.rect.w, r.rect.y + r.rect.h],
2832                     [r.rect.x + r.rect.w, r.rect.y],
2833                 ];
2834                 // now use the hard-coded index array to add 6 vertices to the list
2835                 //TODO: could use instancing here
2836                 // - with triangle strips
2837                 // - with half of the data supplied per instance
2838 
2839                 for &index in &[0usize, 1, 2, 2, 3, 0] {
2840                     let d = data[index];
2841                     vertices.alloc().init(ClearVertex {
2842                         pos: [
2843                             d[0] as f32 / de.width as f32,
2844                             d[1] as f32 / de.height as f32,
2845                             0.0, //TODO: depth Z
2846                             layer as f32,
2847                         ],
2848                     });
2849                 }
2850             }
2851         }
2852 
2853         let mut vertex_is_dirty = true;
2854         let mut inner = self.inner.borrow_mut();
2855         let clear_pipes = &self.shared.service_pipes.clears;
2856         let ds_store = &self.shared.service_pipes.depth_stencil_states;
2857         let ds_state;
2858 
2859         //  issue a PSO+color switch and a draw for each requested clear
2860         let mut key = ClearKey {
2861             framebuffer_aspects: self.state.target_aspects,
2862             color_formats: [metal::MTLPixelFormat::Invalid; 1],
2863             depth_stencil_format: self
2864                 .state
2865                 .target_formats
2866                 .depth_stencil
2867                 .unwrap_or(metal::MTLPixelFormat::Invalid),
2868             target_index: None,
2869         };
2870         for (out, &(mtl_format, _)) in key
2871             .color_formats
2872             .iter_mut()
2873             .zip(&self.state.target_formats.colors)
2874         {
2875             *out = mtl_format;
2876         }
2877 
2878         for clear in clears {
2879             let pso; // has to live at least as long as all the commands
2880             let depth_stencil;
2881             let raw_value;
2882 
2883             let (com_clear, target_index) = match *clear.borrow() {
2884                 com::AttachmentClear::Color { index, value } => {
2885                     let channel = self.state.target_formats.colors[index].1;
2886                     //Note: technically we should be able to derive the Channel from the
2887                     // `value` variant, but this is blocked by the portability that is
2888                     // always passing the attachment clears as `ClearColor::Sfloat` atm.
2889                     raw_value = com::ClearColor::from(value);
2890                     let com = soft::RenderCommand::BindBufferData {
2891                         stage: pso::Stage::Fragment,
2892                         index: 0,
2893                         words: slice::from_raw_parts(
2894                             raw_value.float32.as_ptr() as *const u32,
2895                             mem::size_of::<com::ClearColor>() / WORD_SIZE,
2896                         ),
2897                     };
2898                     (com, Some((index as u8, channel)))
2899                 }
2900                 com::AttachmentClear::DepthStencil { depth, stencil } => {
2901                     let mut aspects = Aspects::empty();
2902                     if let Some(value) = depth {
2903                         for v in vertices.iter_mut() {
2904                             v.pos[2] = value;
2905                         }
2906                         vertex_is_dirty = true;
2907                         aspects |= Aspects::DEPTH;
2908                     }
2909                     if stencil.is_some() {
2910                         //TODO: soft::RenderCommand::SetStencilReference
2911                         aspects |= Aspects::STENCIL;
2912                     }
2913                     depth_stencil = ds_store.get_write(aspects);
2914                     let com = soft::RenderCommand::SetDepthStencilState(&**depth_stencil);
2915                     (com, None)
2916                 }
2917             };
2918 
2919             key.target_index = target_index;
2920             pso = clear_pipes.get(
2921                 key,
2922                 &self.shared.service_pipes.library,
2923                 &self.shared.device,
2924                 &self.shared.private_caps,
2925             );
2926 
2927             let com_pso = iter::once(soft::RenderCommand::BindPipeline(&**pso));
2928             let com_rast = iter::once(soft::RenderCommand::SetRasterizerState(
2929                 native::RasterizerState::default(),
2930             ));
2931 
2932             let com_vertex = if vertex_is_dirty {
2933                 vertex_is_dirty = false;
2934                 Some(soft::RenderCommand::BindBufferData {
2935                     stage: pso::Stage::Vertex,
2936                     index: 0,
2937                     words: slice::from_raw_parts(
2938                         vertices.as_ptr() as *const u32,
2939                         vertices.len() * mem::size_of::<ClearVertex>() / WORD_SIZE,
2940                     ),
2941                 })
2942             } else {
2943                 None
2944             };
2945 
2946             let ext = self.state.target_extent;
2947             let rect = pso::Rect {
2948                 x: 0,
2949                 y: ext.height as _,
2950                 w: ext.width as _,
2951                 h: -(ext.height as i16),
2952             };
2953             let com_viewport = iter::once(soft::RenderCommand::SetViewport(rect, 0.0 .. 1.0));
2954             let com_scissor = iter::once(soft::RenderCommand::SetScissor(MTLScissorRect {
2955                 x: 0,
2956                 y: 0,
2957                 width: ext.width as _,
2958                 height: ext.height as _,
2959             }));
2960 
2961             let com_draw = iter::once(soft::RenderCommand::Draw {
2962                 primitive_type: MTLPrimitiveType::Triangle,
2963                 vertices: 0 .. vertices.len() as _,
2964                 instances: 0 .. 1,
2965             });
2966 
2967             let commands = iter::once(com_clear)
2968                 .chain(com_pso)
2969                 .chain(com_rast)
2970                 .chain(com_viewport)
2971                 .chain(com_scissor)
2972                 .chain(com_vertex)
2973                 .chain(com_draw);
2974 
2975             inner.sink().pre_render().issue_many(commands);
2976         }
2977 
2978         // reset all the affected states
2979         let (com_pso, com_rast) = self.state.make_pso_commands();
2980 
2981         let device_lock = &self.shared.device;
2982         let com_ds = match self.state.build_depth_stencil() {
2983             Some(desc) => {
2984                 ds_state = ds_store.get(desc, device_lock);
2985                 Some(soft::RenderCommand::SetDepthStencilState(&**ds_state))
2986             }
2987             None => None,
2988         };
2989 
2990         let com_vs = match (
2991             self.state.resources_vs.buffers.first(),
2992             self.state.resources_vs.buffer_offsets.first(),
2993         ) {
2994             (Some(&Some(buffer)), Some(&offset)) => Some(soft::RenderCommand::BindBuffer {
2995                 stage: pso::Stage::Vertex,
2996                 index: 0,
2997                 buffer,
2998                 offset,
2999             }),
3000             _ => None,
3001         };
3002         let com_ps = match (
3003             self.state.resources_ps.buffers.first(),
3004             self.state.resources_ps.buffer_offsets.first(),
3005         ) {
3006             (Some(&Some(buffer)), Some(&offset)) => Some(soft::RenderCommand::BindBuffer {
3007                 stage: pso::Stage::Fragment,
3008                 index: 0,
3009                 buffer,
3010                 offset,
3011             }),
3012             _ => None,
3013         };
3014 
3015         let commands = com_pso
3016             .into_iter()
3017             .chain(com_rast)
3018             .chain(com_ds)
3019             .chain(com_vs)
3020             .chain(com_ps);
3021 
3022         inner.sink().pre_render().issue_many(commands);
3023 
3024         vertices.clear();
3025     }
3026 
resolve_image<T>( &mut self, _src: &native::Image, _src_layout: Layout, _dst: &native::Image, _dst_layout: Layout, _regions: T, ) where T: IntoIterator, T::Item: Borrow<com::ImageResolve>,3027     unsafe fn resolve_image<T>(
3028         &mut self,
3029         _src: &native::Image,
3030         _src_layout: Layout,
3031         _dst: &native::Image,
3032         _dst_layout: Layout,
3033         _regions: T,
3034     ) where
3035         T: IntoIterator,
3036         T::Item: Borrow<com::ImageResolve>,
3037     {
3038         unimplemented!()
3039     }
3040 
blit_image<T>( &mut self, src: &native::Image, _src_layout: Layout, dst: &native::Image, _dst_layout: Layout, filter: Filter, regions: T, ) where T: IntoIterator, T::Item: Borrow<com::ImageBlit>,3041     unsafe fn blit_image<T>(
3042         &mut self,
3043         src: &native::Image,
3044         _src_layout: Layout,
3045         dst: &native::Image,
3046         _dst_layout: Layout,
3047         filter: Filter,
3048         regions: T,
3049     ) where
3050         T: IntoIterator,
3051         T::Item: Borrow<com::ImageBlit>,
3052     {
3053         let CommandBufferInner {
3054             ref mut retained_textures,
3055             ref mut sink,
3056             ..
3057         } = *self.inner.borrow_mut();
3058 
3059         let src_cubish = src.view_cube_as_2d();
3060         let dst_cubish = dst.view_cube_as_2d();
3061         let dst_layers = dst.kind.num_layers();
3062 
3063         let vertices = &mut self.temp.blit_vertices;
3064         vertices.clear();
3065 
3066         let sampler = self.shared.service_pipes.sampler_states.get(filter);
3067         let ds_state;
3068         let key_mtl_type = match dst_cubish {
3069             Some(_) => metal::MTLTextureType::D2Array,
3070             None => dst.mtl_type,
3071         };
3072         let key = (
3073             key_mtl_type,
3074             dst.mtl_format,
3075             src.format_desc.aspects,
3076             dst.shader_channel,
3077         );
3078         let pso = self.shared.service_pipes.blits.get(
3079             key,
3080             &self.shared.service_pipes.library,
3081             &self.shared.device,
3082             &self.shared.private_caps,
3083         );
3084 
3085         for region in regions {
3086             let r = region.borrow();
3087 
3088             // layer count must be equal in both subresources
3089             debug_assert_eq!(
3090                 r.src_subresource.layers.len(),
3091                 r.dst_subresource.layers.len()
3092             );
3093             debug_assert_eq!(r.src_subresource.aspects, r.dst_subresource.aspects);
3094             debug_assert!(src.format_desc.aspects.contains(r.src_subresource.aspects));
3095             debug_assert!(dst.format_desc.aspects.contains(r.dst_subresource.aspects));
3096 
3097             let se = src.kind.extent().at_level(r.src_subresource.level);
3098             let de = dst.kind.extent().at_level(r.dst_subresource.level);
3099             //TODO: support 3D textures
3100             if se.depth != 1 || de.depth != 1 {
3101                 warn!(
3102                     "3D image blits are not supported properly yet: {:?} -> {:?}",
3103                     se, de
3104                 );
3105             }
3106 
3107             let layers = r
3108                 .src_subresource
3109                 .layers
3110                 .clone()
3111                 .zip(r.dst_subresource.layers.clone());
3112             let list = vertices
3113                 .entry((r.dst_subresource.aspects, r.dst_subresource.level))
3114                 .or_insert_with(Vec::new);
3115 
3116             for (src_layer, dst_layer) in layers {
3117                 // this helper array defines unique data for quad vertices
3118                 let data = [
3119                     [
3120                         r.src_bounds.start.x,
3121                         r.src_bounds.start.y,
3122                         r.dst_bounds.start.x,
3123                         r.dst_bounds.start.y,
3124                     ],
3125                     [
3126                         r.src_bounds.start.x,
3127                         r.src_bounds.end.y,
3128                         r.dst_bounds.start.x,
3129                         r.dst_bounds.end.y,
3130                     ],
3131                     [
3132                         r.src_bounds.end.x,
3133                         r.src_bounds.end.y,
3134                         r.dst_bounds.end.x,
3135                         r.dst_bounds.end.y,
3136                     ],
3137                     [
3138                         r.src_bounds.end.x,
3139                         r.src_bounds.start.y,
3140                         r.dst_bounds.end.x,
3141                         r.dst_bounds.start.y,
3142                     ],
3143                 ];
3144                 // now use the hard-coded index array to add 6 vertices to the list
3145                 //TODO: could use instancing here
3146                 // - with triangle strips
3147                 // - with half of the data supplied per instance
3148 
3149                 for &index in &[0usize, 1, 2, 2, 3, 0] {
3150                     let d = data[index];
3151                     list.alloc().init(BlitVertex {
3152                         uv: [
3153                             d[0] as f32 / se.width as f32,
3154                             d[1] as f32 / se.height as f32,
3155                             src_layer as f32,
3156                             r.src_subresource.level as f32,
3157                         ],
3158                         pos: [
3159                             d[2] as f32 / de.width as f32,
3160                             d[3] as f32 / de.height as f32,
3161                             0.0,
3162                             dst_layer as f32,
3163                         ],
3164                     });
3165                 }
3166             }
3167         }
3168 
3169         // Note: we don't bother to restore any render states here, since we are currently
3170         // outside of a render pass, and the state will be reset automatically once
3171         // we enter the next pass.
3172 
3173         let src_native = AsNative::from(match src_cubish {
3174             Some(ref tex) => tex.as_ref(),
3175             None => src.like.as_texture(),
3176         });
3177         let prelude = [
3178             soft::RenderCommand::BindPipeline(&**pso),
3179             soft::RenderCommand::BindSamplers {
3180                 stage: pso::Stage::Fragment,
3181                 index: 0,
3182                 samplers: &[Some(AsNative::from(sampler))][..],
3183             },
3184             soft::RenderCommand::BindTextures {
3185                 stage: pso::Stage::Fragment,
3186                 index: 0,
3187                 textures: &[Some(src_native)][..],
3188             },
3189         ];
3190 
3191         let com_ds = if src
3192             .format_desc
3193             .aspects
3194             .intersects(Aspects::DEPTH | Aspects::STENCIL)
3195         {
3196             ds_state = self
3197                 .shared
3198                 .service_pipes
3199                 .depth_stencil_states
3200                 .get_write(src.format_desc.aspects);
3201             Some(soft::RenderCommand::SetDepthStencilState(&**ds_state))
3202         } else {
3203             None
3204         };
3205 
3206         let layered_rendering = self.shared.private_caps.layered_rendering;
3207         autoreleasepool(|| {
3208             let dst_new = match dst_cubish {
3209                 Some(ref tex) => tex.as_ref(),
3210                 None => dst.like.as_texture(),
3211             };
3212 
3213             for ((aspects, level), list) in vertices.drain() {
3214                 let descriptor = metal::RenderPassDescriptor::new().to_owned();
3215                 if layered_rendering {
3216                     descriptor.set_render_target_array_length(dst_layers as _);
3217                 }
3218 
3219                 if aspects.contains(Aspects::COLOR) {
3220                     let att = descriptor.color_attachments().object_at(0).unwrap();
3221                     att.set_texture(Some(dst_new));
3222                     att.set_level(level as _);
3223                 }
3224                 if aspects.contains(Aspects::DEPTH) {
3225                     let att = descriptor.depth_attachment().unwrap();
3226                     att.set_texture(Some(dst_new));
3227                     att.set_level(level as _);
3228                 }
3229                 if aspects.contains(Aspects::STENCIL) {
3230                     let att = descriptor.stencil_attachment().unwrap();
3231                     att.set_texture(Some(dst_new));
3232                     att.set_level(level as _);
3233                 }
3234 
3235                 let ext = dst.kind.extent().at_level(level);
3236                 //Note: flipping Y coordinate of the destination here
3237                 let rect = pso::Rect {
3238                     x: 0,
3239                     y: ext.height as _,
3240                     w: ext.width as _,
3241                     h: -(ext.height as i16),
3242                 };
3243 
3244                 let extra = [
3245                     soft::RenderCommand::SetViewport(rect, 0.0 .. 1.0),
3246                     soft::RenderCommand::SetScissor(MTLScissorRect {
3247                         x: 0,
3248                         y: 0,
3249                         width: ext.width as _,
3250                         height: ext.height as _,
3251                     }),
3252                     soft::RenderCommand::BindBufferData {
3253                         stage: pso::Stage::Vertex,
3254                         index: 0,
3255                         words: slice::from_raw_parts(
3256                             list.as_ptr() as *const u32,
3257                             list.len() * mem::size_of::<BlitVertex>() / WORD_SIZE,
3258                         ),
3259                     },
3260                     soft::RenderCommand::Draw {
3261                         primitive_type: MTLPrimitiveType::Triangle,
3262                         vertices: 0 .. list.len() as _,
3263                         instances: 0 .. 1,
3264                     },
3265                 ];
3266 
3267                 let commands = prelude.iter().chain(&com_ds).chain(&extra).cloned();
3268 
3269                 sink.as_mut()
3270                     .unwrap()
3271                     .quick_render("blit_image", descriptor, commands);
3272             }
3273         });
3274 
3275         retained_textures.extend(src_cubish);
3276         retained_textures.extend(dst_cubish);
3277     }
3278 
bind_index_buffer(&mut self, view: buffer::IndexBufferView<Backend>)3279     unsafe fn bind_index_buffer(&mut self, view: buffer::IndexBufferView<Backend>) {
3280         let (raw, range) = view.buffer.as_bound();
3281         assert!(range.start + view.offset < range.end); // conservative
3282         self.state.index_buffer = Some(IndexBuffer {
3283             buffer: AsNative::from(raw),
3284             offset: (range.start + view.offset) as _,
3285             stride: match view.index_type {
3286                 IndexType::U16 => 2,
3287                 IndexType::U32 => 4,
3288             },
3289         });
3290     }
3291 
bind_vertex_buffers<I, T>(&mut self, first_binding: pso::BufferIndex, buffers: I) where I: IntoIterator<Item = (T, buffer::Offset)>, T: Borrow<native::Buffer>,3292     unsafe fn bind_vertex_buffers<I, T>(&mut self, first_binding: pso::BufferIndex, buffers: I)
3293     where
3294         I: IntoIterator<Item = (T, buffer::Offset)>,
3295         T: Borrow<native::Buffer>,
3296     {
3297         if self.state.vertex_buffers.len() <= first_binding as usize {
3298             self.state
3299                 .vertex_buffers
3300                 .resize(first_binding as usize + 1, None);
3301         }
3302         for (i, (buffer, offset)) in buffers.into_iter().enumerate() {
3303             let b = buffer.borrow();
3304             let (raw, range) = b.as_bound();
3305             let buffer_ptr = AsNative::from(raw);
3306             let index = first_binding as usize + i;
3307             self.state
3308                 .vertex_buffers
3309                 .entry(index)
3310                 .set(Some((buffer_ptr, range.start + offset)));
3311         }
3312 
3313         if let Some(command) = self
3314             .state
3315             .set_vertex_buffers(self.shared.private_caps.max_buffers_per_stage as usize)
3316         {
3317             self.inner.borrow_mut().sink().pre_render().issue(command);
3318         }
3319     }
3320 
set_viewports<T>(&mut self, first_viewport: u32, vps: T) where T: IntoIterator, T::Item: Borrow<pso::Viewport>,3321     unsafe fn set_viewports<T>(&mut self, first_viewport: u32, vps: T)
3322     where
3323         T: IntoIterator,
3324         T::Item: Borrow<pso::Viewport>,
3325     {
3326         // macOS_GPUFamily1_v3 supports >1 viewport, todo
3327         if first_viewport != 0 {
3328             panic!("First viewport != 0; Metal supports only one viewport");
3329         }
3330         let mut vps = vps.into_iter();
3331         let vp_borrowable = vps
3332             .next()
3333             .expect("No viewport provided, Metal supports exactly one");
3334         let vp = vp_borrowable.borrow();
3335         if vps.next().is_some() {
3336             // TODO should we panic here or set buffer in an erroneous state?
3337             panic!("More than one viewport set; Metal supports only one viewport");
3338         }
3339 
3340         let com = self.state.set_viewport(vp, self.shared.disabilities);
3341         self.inner.borrow_mut().sink().pre_render().issue(com);
3342     }
3343 
set_scissors<T>(&mut self, first_scissor: u32, rects: T) where T: IntoIterator, T::Item: Borrow<pso::Rect>,3344     unsafe fn set_scissors<T>(&mut self, first_scissor: u32, rects: T)
3345     where
3346         T: IntoIterator,
3347         T::Item: Borrow<pso::Rect>,
3348     {
3349         // macOS_GPUFamily1_v3 supports >1 scissor/viewport, todo
3350         if first_scissor != 0 {
3351             panic!("First scissor != 0; Metal supports only one viewport");
3352         }
3353         let mut rects = rects.into_iter();
3354         let rect_borrowable = rects
3355             .next()
3356             .expect("No scissor provided, Metal supports exactly one");
3357         let rect = rect_borrowable.borrow();
3358         if rects.next().is_some() {
3359             panic!("More than one scissor set; Metal supports only one viewport");
3360         }
3361 
3362         let com = self.state.set_scissor(*rect);
3363         self.inner.borrow_mut().sink().pre_render().issue(com);
3364     }
3365 
set_blend_constants(&mut self, color: pso::ColorValue)3366     unsafe fn set_blend_constants(&mut self, color: pso::ColorValue) {
3367         let com = self.state.set_blend_color(&color);
3368         self.inner.borrow_mut().sink().pre_render().issue(com);
3369     }
3370 
set_depth_bounds(&mut self, _: Range<f32>)3371     unsafe fn set_depth_bounds(&mut self, _: Range<f32>) {
3372         warn!("Depth bounds test is not supported");
3373     }
3374 
set_line_width(&mut self, width: f32)3375     unsafe fn set_line_width(&mut self, width: f32) {
3376         // Note from the Vulkan spec:
3377         // > If the wide lines feature is not enabled, lineWidth must be 1.0
3378         // Simply assert and no-op because Metal never exposes `Features::LINE_WIDTH`
3379         assert_eq!(width, 1.0);
3380     }
3381 
set_depth_bias(&mut self, depth_bias: pso::DepthBias)3382     unsafe fn set_depth_bias(&mut self, depth_bias: pso::DepthBias) {
3383         let com = self.state.set_depth_bias(&depth_bias);
3384         self.inner.borrow_mut().sink().pre_render().issue(com);
3385     }
3386 
set_stencil_reference(&mut self, faces: pso::Face, value: pso::StencilValue)3387     unsafe fn set_stencil_reference(&mut self, faces: pso::Face, value: pso::StencilValue) {
3388         assign_sides(&mut self.state.stencil.reference_values, faces, value);
3389         let com =
3390             soft::RenderCommand::SetStencilReferenceValues(self.state.stencil.reference_values);
3391         self.inner.borrow_mut().sink().pre_render().issue(com);
3392     }
3393 
set_stencil_read_mask(&mut self, faces: pso::Face, value: pso::StencilValue)3394     unsafe fn set_stencil_read_mask(&mut self, faces: pso::Face, value: pso::StencilValue) {
3395         assign_sides(&mut self.state.stencil.read_masks, faces, value);
3396         self.update_depth_stencil();
3397     }
3398 
set_stencil_write_mask(&mut self, faces: pso::Face, value: pso::StencilValue)3399     unsafe fn set_stencil_write_mask(&mut self, faces: pso::Face, value: pso::StencilValue) {
3400         assign_sides(&mut self.state.stencil.write_masks, faces, value);
3401         self.update_depth_stencil();
3402     }
3403 
begin_render_pass<T>( &mut self, render_pass: &native::RenderPass, framebuffer: &native::Framebuffer, _render_area: pso::Rect, clear_values: T, first_subpass_contents: com::SubpassContents, ) where T: IntoIterator, T::Item: Borrow<com::ClearValue>,3404     unsafe fn begin_render_pass<T>(
3405         &mut self,
3406         render_pass: &native::RenderPass,
3407         framebuffer: &native::Framebuffer,
3408         _render_area: pso::Rect,
3409         clear_values: T,
3410         first_subpass_contents: com::SubpassContents,
3411     ) where
3412         T: IntoIterator,
3413         T::Item: Borrow<com::ClearValue>,
3414     {
3415         // fill out temporary clear values per attachment
3416         self.temp
3417             .clear_values
3418             .resize(render_pass.attachments.len(), None);
3419         for ((out_val, _), in_val) in self
3420             .temp
3421             .clear_values
3422             .iter_mut()
3423             .zip(&render_pass.attachments)
3424             .filter(|(_, rat)| rat.has_clears())
3425             .zip(clear_values)
3426         {
3427             *out_val = Some(*in_val.borrow());
3428         }
3429 
3430         self.state.pending_subpasses.clear();
3431         self.state.target_extent = framebuffer.extent;
3432 
3433         //TODO: cache produced `RenderPassDescriptor` objects
3434         // we stack the subpasses in the opposite order
3435         for subpass in render_pass.subpasses.iter().rev() {
3436             let mut combined_aspects = Aspects::empty();
3437             let descriptor = autoreleasepool(|| {
3438                 let descriptor = metal::RenderPassDescriptor::new().to_owned();
3439                 descriptor.set_visibility_result_buffer(Some(&self.shared.visibility.buffer));
3440                 if self.shared.private_caps.layered_rendering {
3441                     descriptor.set_render_target_array_length(framebuffer.extent.depth as _);
3442                 }
3443 
3444                 for (i, &(at_id, op_flags, resolve_id)) in subpass.colors.iter().enumerate() {
3445                     let rat = &render_pass.attachments[at_id];
3446                     let texture = framebuffer.attachments[at_id].as_ref();
3447                     let desc = descriptor.color_attachments().object_at(i as _).unwrap();
3448 
3449                     combined_aspects |= Aspects::COLOR;
3450                     desc.set_texture(Some(texture));
3451 
3452                     if op_flags.contains(native::SubpassOps::LOAD) {
3453                         desc.set_load_action(conv::map_load_operation(rat.ops.load));
3454                         if rat.ops.load == AttachmentLoadOp::Clear {
3455                             let channel = subpass.target_formats.colors[i].1;
3456                             let raw = self.temp.clear_values[at_id].unwrap().color;
3457                             desc.set_clear_color(channel.interpret(raw));
3458                         }
3459                     }
3460                     if let Some(id) = resolve_id {
3461                         let resolve = &framebuffer.attachments[id];
3462                         //Note: the selection of levels and slices is already handled by `ImageView`
3463                         desc.set_resolve_texture(Some(resolve));
3464                         desc.set_store_action(conv::map_resolved_store_operation(rat.ops.store));
3465                     } else if op_flags.contains(native::SubpassOps::STORE) {
3466                         desc.set_store_action(conv::map_store_operation(rat.ops.store));
3467                     }
3468                 }
3469 
3470                 if let Some((at_id, op_flags)) = subpass.depth_stencil {
3471                     let rat = &render_pass.attachments[at_id];
3472                     let texture = framebuffer.attachments[at_id].as_ref();
3473                     let aspects = rat.format.unwrap().surface_desc().aspects;
3474                     combined_aspects |= aspects;
3475 
3476                     if aspects.contains(Aspects::DEPTH) {
3477                         let desc = descriptor.depth_attachment().unwrap();
3478                         desc.set_texture(Some(texture));
3479 
3480                         if op_flags.contains(native::SubpassOps::LOAD) {
3481                             desc.set_load_action(conv::map_load_operation(rat.ops.load));
3482                             if rat.ops.load == AttachmentLoadOp::Clear {
3483                                 let raw = self.temp.clear_values[at_id].unwrap().depth_stencil;
3484                                 desc.set_clear_depth(raw.depth as f64);
3485                             }
3486                         }
3487                         if op_flags.contains(native::SubpassOps::STORE) {
3488                             desc.set_store_action(conv::map_store_operation(rat.ops.store));
3489                         }
3490                     }
3491                     if aspects.contains(Aspects::STENCIL) {
3492                         let desc = descriptor.stencil_attachment().unwrap();
3493                         desc.set_texture(Some(texture));
3494 
3495                         if op_flags.contains(native::SubpassOps::LOAD) {
3496                             desc.set_load_action(conv::map_load_operation(rat.stencil_ops.load));
3497                             if rat.stencil_ops.load == AttachmentLoadOp::Clear {
3498                                 let raw = self.temp.clear_values[at_id].unwrap().depth_stencil;
3499                                 desc.set_clear_stencil(raw.stencil);
3500                             }
3501                         }
3502                         if op_flags.contains(native::SubpassOps::STORE) {
3503                             desc.set_store_action(conv::map_store_operation(rat.stencil_ops.store));
3504                         }
3505                     }
3506                 }
3507 
3508                 descriptor
3509             });
3510 
3511             self.state.pending_subpasses.alloc().init(SubpassInfo {
3512                 descriptor,
3513                 combined_aspects,
3514                 formats: subpass.target_formats.clone(),
3515             });
3516         }
3517 
3518         self.inner.borrow_mut().sink().label(&render_pass.name);
3519         self.next_subpass(first_subpass_contents);
3520     }
3521 
next_subpass(&mut self, _contents: com::SubpassContents)3522     unsafe fn next_subpass(&mut self, _contents: com::SubpassContents) {
3523         let sin = self.state.pending_subpasses.pop().unwrap();
3524 
3525         self.state.render_pso_is_compatible = match self.state.render_pso {
3526             Some(ref ps) => ps.formats == sin.formats,
3527             None => false,
3528         };
3529         self.state.target_aspects = sin.combined_aspects;
3530         self.state.target_formats.copy_from(&sin.formats);
3531 
3532         let ds_store = &self.shared.service_pipes.depth_stencil_states;
3533         let ds_state;
3534         let com_ds = if sin
3535             .combined_aspects
3536             .intersects(Aspects::DEPTH | Aspects::STENCIL)
3537         {
3538             match self.state.build_depth_stencil() {
3539                 Some(desc) => {
3540                     ds_state = ds_store.get(desc, &self.shared.device);
3541                     Some(soft::RenderCommand::SetDepthStencilState(&**ds_state))
3542                 }
3543                 None => None,
3544             }
3545         } else {
3546             None
3547         };
3548 
3549         let init_commands = self
3550             .state
3551             .make_render_commands(sin.combined_aspects)
3552             .chain(com_ds);
3553 
3554         autoreleasepool(|| {
3555             self.inner
3556                 .borrow_mut()
3557                 .sink()
3558                 .switch_render(sin.descriptor)
3559                 .issue_many(init_commands);
3560         });
3561     }
3562 
end_render_pass(&mut self)3563     unsafe fn end_render_pass(&mut self) {
3564         self.inner.borrow_mut().sink().stop_encoding();
3565     }
3566 
bind_graphics_pipeline(&mut self, pipeline: &native::GraphicsPipeline)3567     unsafe fn bind_graphics_pipeline(&mut self, pipeline: &native::GraphicsPipeline) {
3568         let mut inner = self.inner.borrow_mut();
3569         let mut pre = inner.sink().pre_render();
3570 
3571         if let Some(ref stencil) = pipeline.depth_stencil_desc.stencil {
3572             if let pso::State::Static(value) = stencil.read_masks {
3573                 self.state.stencil.read_masks = value;
3574             }
3575             if let pso::State::Static(value) = stencil.write_masks {
3576                 self.state.stencil.write_masks = value;
3577             }
3578             if let pso::State::Static(value) = stencil.reference_values {
3579                 self.state.stencil.reference_values = value;
3580                 pre.issue(soft::RenderCommand::SetStencilReferenceValues(value));
3581             }
3582         }
3583 
3584         self.state.render_pso_is_compatible =
3585             pipeline.attachment_formats == self.state.target_formats;
3586         let set_pipeline = match self.state.render_pso {
3587             Some(ref ps) if ps.raw.as_ptr() == pipeline.raw.as_ptr() => false,
3588             Some(ref mut ps) => {
3589                 ps.raw = pipeline.raw.to_owned();
3590                 ps.vertex_buffers.clear();
3591                 ps.vertex_buffers
3592                     .extend(pipeline.vertex_buffers.iter().cloned().map(Some));
3593                 ps.ds_desc = pipeline.depth_stencil_desc;
3594                 ps.formats.copy_from(&pipeline.attachment_formats);
3595                 true
3596             }
3597             None => {
3598                 self.state.render_pso = Some(RenderPipelineState {
3599                     raw: pipeline.raw.to_owned(),
3600                     ds_desc: pipeline.depth_stencil_desc,
3601                     vertex_buffers: pipeline.vertex_buffers.iter().cloned().map(Some).collect(),
3602                     formats: pipeline.attachment_formats.clone(),
3603                 });
3604                 true
3605             }
3606         };
3607 
3608         if self.state.render_pso_is_compatible {
3609             if set_pipeline {
3610                 self.state.rasterizer_state = pipeline.rasterizer_state.clone();
3611                 self.state.primitive_type = pipeline.primitive_type;
3612 
3613                 pre.issue(soft::RenderCommand::BindPipeline(&*pipeline.raw));
3614                 if let Some(ref rs) = pipeline.rasterizer_state {
3615                     pre.issue(soft::RenderCommand::SetRasterizerState(rs.clone()))
3616                 }
3617                 // re-bind vertex buffers
3618                 if let Some(command) = self
3619                     .state
3620                     .set_vertex_buffers(self.shared.private_caps.max_buffers_per_stage as usize)
3621                 {
3622                     pre.issue(command);
3623                 }
3624                 // re-bind push constants
3625                 if let Some(pc) = pipeline.vs_pc_info {
3626                     if Some(pc) != self.state.resources_vs.push_constants {
3627                         // if we don't have enough constants, then binding will follow
3628                         if pc.count as usize <= self.state.push_constants.len() {
3629                             pre.issue(self.state.push_vs_constants(pc));
3630                         }
3631                     }
3632                 }
3633                 if let Some(pc) = pipeline.ps_pc_info {
3634                     if Some(pc) != self.state.resources_ps.push_constants
3635                         && pc.count as usize <= self.state.push_constants.len()
3636                     {
3637                         pre.issue(self.state.push_ps_constants(pc));
3638                     }
3639                 }
3640             } else {
3641                 debug_assert_eq!(self.state.rasterizer_state, pipeline.rasterizer_state);
3642                 debug_assert_eq!(self.state.primitive_type, pipeline.primitive_type);
3643             }
3644 
3645             if let Some(desc) = self.state.build_depth_stencil() {
3646                 let ds_store = &self.shared.service_pipes.depth_stencil_states;
3647                 let state = &**ds_store.get(desc, &self.shared.device);
3648                 pre.issue(soft::RenderCommand::SetDepthStencilState(state));
3649             }
3650         } else {
3651             // This may be tricky: we expect either another pipeline to be bound
3652             // (this overwriting these), or a new render pass started (thus using these).
3653             self.state.rasterizer_state = pipeline.rasterizer_state.clone();
3654             self.state.primitive_type = pipeline.primitive_type;
3655         }
3656 
3657         if let pso::State::Static(value) = pipeline.depth_bias {
3658             self.state.depth_bias = value;
3659             pre.issue(soft::RenderCommand::SetDepthBias(value));
3660         }
3661 
3662         if let Some(ref vp) = pipeline.baked_states.viewport {
3663             pre.issue(self.state.set_viewport(vp, self.shared.disabilities));
3664         }
3665         if let Some(rect) = pipeline.baked_states.scissor {
3666             pre.issue(self.state.set_scissor(rect));
3667         }
3668         if let Some(ref color) = pipeline.baked_states.blend_color {
3669             pre.issue(self.state.set_blend_color(color));
3670         }
3671     }
3672 
bind_graphics_descriptor_sets<I, J>( &mut self, pipe_layout: &native::PipelineLayout, first_set: usize, sets: I, dynamic_offsets: J, ) where I: IntoIterator, I::Item: Borrow<native::DescriptorSet>, J: IntoIterator, J::Item: Borrow<com::DescriptorSetOffset>,3673     unsafe fn bind_graphics_descriptor_sets<I, J>(
3674         &mut self,
3675         pipe_layout: &native::PipelineLayout,
3676         first_set: usize,
3677         sets: I,
3678         dynamic_offsets: J,
3679     ) where
3680         I: IntoIterator,
3681         I::Item: Borrow<native::DescriptorSet>,
3682         J: IntoIterator,
3683         J::Item: Borrow<com::DescriptorSetOffset>,
3684     {
3685         let vbuf_count = self
3686             .state
3687             .render_pso
3688             .as_ref()
3689             .map_or(0, |pso| pso.vertex_buffers.len()) as ResourceIndex;
3690         assert!(
3691             pipe_layout.total.vs.buffers + vbuf_count
3692                 <= self.shared.private_caps.max_buffers_per_stage
3693         );
3694 
3695         self.state.resources_vs.pre_allocate(&pipe_layout.total.vs);
3696         self.state.resources_ps.pre_allocate(&pipe_layout.total.ps);
3697 
3698         let mut dynamic_offset_iter = dynamic_offsets.into_iter();
3699         let mut inner = self.inner.borrow_mut();
3700         let mut pre = inner.sink().pre_render();
3701         let mut bind_range = {
3702             let first = &pipe_layout.infos[first_set].offsets;
3703             native::MultiStageData {
3704                 vs: first.vs.map(|&i| i .. i),
3705                 ps: first.ps.map(|&i| i .. i),
3706                 cs: first.cs.map(|&i| i .. i),
3707             }
3708         };
3709         for ((info, desc_set), cached_ds) in pipe_layout.infos[first_set ..]
3710             .iter()
3711             .zip(sets)
3712             .zip(self.state.descriptor_sets[first_set ..].iter_mut())
3713         {
3714             match *desc_set.borrow() {
3715                 native::DescriptorSet::Emulated {
3716                     ref pool,
3717                     ref layouts,
3718                     ref resources,
3719                 } => {
3720                     let data = pool.read();
3721 
3722                     let end_vs_offsets = self.state.resources_vs.bind_set(
3723                         pso::ShaderStageFlags::VERTEX,
3724                         &*data,
3725                         info.offsets.vs.clone(),
3726                         layouts,
3727                         resources,
3728                     );
3729                     bind_range.vs.expand(end_vs_offsets);
3730                     let end_ps_offsets = self.state.resources_ps.bind_set(
3731                         pso::ShaderStageFlags::FRAGMENT,
3732                         &*data,
3733                         info.offsets.ps.clone(),
3734                         layouts,
3735                         resources,
3736                     );
3737                     bind_range.ps.expand(end_ps_offsets);
3738 
3739                     for (dyn_data, offset) in info
3740                         .dynamic_buffers
3741                         .iter()
3742                         .zip(dynamic_offset_iter.by_ref())
3743                     {
3744                         if dyn_data.vs != !0 {
3745                             self.state.resources_vs.buffer_offsets[dyn_data.vs as usize] +=
3746                                 *offset.borrow() as buffer::Offset;
3747                         }
3748                         if dyn_data.ps != !0 {
3749                             self.state.resources_ps.buffer_offsets[dyn_data.ps as usize] +=
3750                                 *offset.borrow() as buffer::Offset;
3751                         }
3752                     }
3753                 }
3754                 native::DescriptorSet::ArgumentBuffer {
3755                     ref raw,
3756                     raw_offset,
3757                     ref pool,
3758                     ref range,
3759                     stage_flags,
3760                     ..
3761                 } => {
3762                     //Note: this is incompatible with the binding scheme below
3763                     if stage_flags.contains(pso::ShaderStageFlags::VERTEX) {
3764                         let index = info.offsets.vs.buffers;
3765                         self.state.resources_vs.buffers[index as usize] =
3766                             Some(AsNative::from(raw.as_ref()));
3767                         self.state.resources_vs.buffer_offsets[index as usize] = raw_offset;
3768                         pre.issue(soft::RenderCommand::BindBuffer {
3769                             stage: pso::Stage::Vertex,
3770                             index,
3771                             buffer: AsNative::from(raw.as_ref()),
3772                             offset: raw_offset,
3773                         });
3774                     }
3775                     if stage_flags.contains(pso::ShaderStageFlags::FRAGMENT) {
3776                         let index = info.offsets.ps.buffers;
3777                         self.state.resources_ps.buffers[index as usize] =
3778                             Some(AsNative::from(raw.as_ref()));
3779                         self.state.resources_ps.buffer_offsets[index as usize] = raw_offset;
3780                         pre.issue(soft::RenderCommand::BindBuffer {
3781                             stage: pso::Stage::Fragment,
3782                             index,
3783                             buffer: AsNative::from(raw.as_ref()),
3784                             offset: raw_offset,
3785                         });
3786                     }
3787                     if stage_flags
3788                         .intersects(pso::ShaderStageFlags::VERTEX | pso::ShaderStageFlags::FRAGMENT)
3789                     {
3790                         cached_ds.graphics_resources.clear();
3791                         cached_ds.graphics_resources.extend(
3792                             pool.read().resources[range.start as usize .. range.end as usize]
3793                                 .iter()
3794                                 .filter_map(|ur| {
3795                                     ptr::NonNull::new(ur.ptr).map(|res| (res, ur.usage))
3796                                 }),
3797                         );
3798                         pre.issue_many(cached_ds.graphics_resources.iter().map(
3799                             |&(resource, usage)| soft::RenderCommand::UseResource {
3800                                 resource,
3801                                 usage,
3802                             },
3803                         ));
3804                     }
3805                 }
3806             }
3807         }
3808 
3809         // now bind all the affected resources
3810         for (stage, cache, range) in
3811             iter::once((pso::Stage::Vertex, &self.state.resources_vs, bind_range.vs)).chain(
3812                 iter::once((
3813                     pso::Stage::Fragment,
3814                     &self.state.resources_ps,
3815                     bind_range.ps,
3816                 )),
3817             )
3818         {
3819             if range.textures.start != range.textures.end {
3820                 pre.issue(soft::RenderCommand::BindTextures {
3821                     stage,
3822                     index: range.textures.start,
3823                     textures: &cache.textures
3824                         [range.textures.start as usize .. range.textures.end as usize],
3825                 });
3826             }
3827             if range.samplers.start != range.samplers.end {
3828                 pre.issue(soft::RenderCommand::BindSamplers {
3829                     stage,
3830                     index: range.samplers.start,
3831                     samplers: &cache.samplers
3832                         [range.samplers.start as usize .. range.samplers.end as usize],
3833                 });
3834             }
3835             if range.buffers.start != range.buffers.end {
3836                 pre.issue(soft::RenderCommand::BindBuffers {
3837                     stage,
3838                     index: range.buffers.start,
3839                     buffers: {
3840                         let range = range.buffers.start as usize .. range.buffers.end as usize;
3841                         (&cache.buffers[range.clone()], &cache.buffer_offsets[range])
3842                     },
3843                 });
3844             }
3845         }
3846     }
3847 
bind_compute_pipeline(&mut self, pipeline: &native::ComputePipeline)3848     unsafe fn bind_compute_pipeline(&mut self, pipeline: &native::ComputePipeline) {
3849         self.state.compute_pso = Some(pipeline.raw.clone());
3850         self.state.work_group_size = pipeline.work_group_size;
3851 
3852         let mut inner = self.inner.borrow_mut();
3853         let mut pre = inner.sink().pre_compute();
3854 
3855         pre.issue(soft::ComputeCommand::BindPipeline(&*pipeline.raw));
3856 
3857         if let Some(pc) = pipeline.pc_info {
3858             if Some(pc) != self.state.resources_cs.push_constants
3859                 && pc.count as usize <= self.state.push_constants.len()
3860             {
3861                 pre.issue(self.state.push_cs_constants(pc));
3862             }
3863         }
3864     }
3865 
bind_compute_descriptor_sets<I, J>( &mut self, pipe_layout: &native::PipelineLayout, first_set: usize, sets: I, dynamic_offsets: J, ) where I: IntoIterator, I::Item: Borrow<native::DescriptorSet>, J: IntoIterator, J::Item: Borrow<com::DescriptorSetOffset>,3866     unsafe fn bind_compute_descriptor_sets<I, J>(
3867         &mut self,
3868         pipe_layout: &native::PipelineLayout,
3869         first_set: usize,
3870         sets: I,
3871         dynamic_offsets: J,
3872     ) where
3873         I: IntoIterator,
3874         I::Item: Borrow<native::DescriptorSet>,
3875         J: IntoIterator,
3876         J::Item: Borrow<com::DescriptorSetOffset>,
3877     {
3878         self.state.resources_cs.pre_allocate(&pipe_layout.total.cs);
3879 
3880         let mut dynamic_offset_iter = dynamic_offsets.into_iter();
3881         let mut inner = self.inner.borrow_mut();
3882         let mut pre = inner.sink().pre_compute();
3883         let cache = &mut self.state.resources_cs;
3884         let mut bind_range = pipe_layout.infos[first_set].offsets.cs.map(|&i| i .. i);
3885 
3886         for ((info, desc_set), cached_ds) in pipe_layout.infos[first_set ..]
3887             .iter()
3888             .zip(sets)
3889             .zip(self.state.descriptor_sets[first_set ..].iter_mut())
3890         {
3891             let res_offset = &info.offsets.cs;
3892             match *desc_set.borrow() {
3893                 native::DescriptorSet::Emulated {
3894                     ref pool,
3895                     ref layouts,
3896                     ref resources,
3897                 } => {
3898                     let data = pool.read();
3899 
3900                     let end_offsets = cache.bind_set(
3901                         pso::ShaderStageFlags::COMPUTE,
3902                         &*data,
3903                         res_offset.clone(),
3904                         layouts,
3905                         resources,
3906                     );
3907                     bind_range.expand(end_offsets);
3908 
3909                     for (dyn_data, offset) in info
3910                         .dynamic_buffers
3911                         .iter()
3912                         .zip(dynamic_offset_iter.by_ref())
3913                     {
3914                         if dyn_data.cs != !0 {
3915                             cache.buffer_offsets[dyn_data.cs as usize] +=
3916                                 *offset.borrow() as buffer::Offset;
3917                         }
3918                     }
3919                 }
3920                 native::DescriptorSet::ArgumentBuffer {
3921                     ref raw,
3922                     raw_offset,
3923                     ref pool,
3924                     ref range,
3925                     stage_flags,
3926                     ..
3927                 } => {
3928                     if stage_flags.contains(pso::ShaderStageFlags::COMPUTE) {
3929                         let index = res_offset.buffers;
3930                         cache.buffers[index as usize] = Some(AsNative::from(raw.as_ref()));
3931                         cache.buffer_offsets[index as usize] = raw_offset;
3932                         pre.issue(soft::ComputeCommand::BindBuffer {
3933                             index,
3934                             buffer: AsNative::from(raw.as_ref()),
3935                             offset: raw_offset,
3936                         });
3937 
3938                         cached_ds.compute_resources.clear();
3939                         cached_ds.compute_resources.extend(
3940                             pool.read().resources[range.start as usize .. range.end as usize]
3941                                 .iter()
3942                                 .filter_map(|ur| {
3943                                     ptr::NonNull::new(ur.ptr).map(|res| (res, ur.usage))
3944                                 }),
3945                         );
3946                         pre.issue_many(cached_ds.compute_resources.iter().map(
3947                             |&(resource, usage)| soft::ComputeCommand::UseResource {
3948                                 resource,
3949                                 usage,
3950                             },
3951                         ));
3952                     }
3953                 }
3954             }
3955         }
3956 
3957         // now bind all the affected resources
3958         if bind_range.textures.start != bind_range.textures.end {
3959             pre.issue(soft::ComputeCommand::BindTextures {
3960                 index: bind_range.textures.start,
3961                 textures: &cache.textures
3962                     [bind_range.textures.start as usize .. bind_range.textures.end as usize],
3963             });
3964         }
3965         if bind_range.samplers.start != bind_range.samplers.end {
3966             pre.issue(soft::ComputeCommand::BindSamplers {
3967                 index: bind_range.samplers.start,
3968                 samplers: &cache.samplers
3969                     [bind_range.samplers.start as usize .. bind_range.samplers.end as usize],
3970             });
3971         }
3972         if bind_range.buffers.start != bind_range.buffers.end {
3973             pre.issue(soft::ComputeCommand::BindBuffers {
3974                 index: bind_range.buffers.start,
3975                 buffers: {
3976                     let range =
3977                         bind_range.buffers.start as usize .. bind_range.buffers.end as usize;
3978                     (&cache.buffers[range.clone()], &cache.buffer_offsets[range])
3979                 },
3980             });
3981         }
3982     }
3983 
dispatch(&mut self, count: WorkGroupCount)3984     unsafe fn dispatch(&mut self, count: WorkGroupCount) {
3985         let mut inner = self.inner.borrow_mut();
3986         let (mut pre, init) = inner.sink().switch_compute();
3987         if init {
3988             pre.issue_many(self.state.make_compute_commands());
3989         }
3990 
3991         pre.issue(soft::ComputeCommand::Dispatch {
3992             wg_size: self.state.work_group_size,
3993             wg_count: MTLSize {
3994                 width: count[0] as _,
3995                 height: count[1] as _,
3996                 depth: count[2] as _,
3997             },
3998         });
3999     }
4000 
dispatch_indirect(&mut self, buffer: &native::Buffer, offset: buffer::Offset)4001     unsafe fn dispatch_indirect(&mut self, buffer: &native::Buffer, offset: buffer::Offset) {
4002         let mut inner = self.inner.borrow_mut();
4003         let (mut pre, init) = inner.sink().switch_compute();
4004         if init {
4005             pre.issue_many(self.state.make_compute_commands());
4006         }
4007 
4008         let (raw, range) = buffer.as_bound();
4009         assert!(range.start + offset < range.end);
4010 
4011         pre.issue(soft::ComputeCommand::DispatchIndirect {
4012             wg_size: self.state.work_group_size,
4013             buffer: AsNative::from(raw),
4014             offset: range.start + offset,
4015         });
4016     }
4017 
copy_buffer<T>(&mut self, src: &native::Buffer, dst: &native::Buffer, regions: T) where T: IntoIterator, T::Item: Borrow<com::BufferCopy>,4018     unsafe fn copy_buffer<T>(&mut self, src: &native::Buffer, dst: &native::Buffer, regions: T)
4019     where
4020         T: IntoIterator,
4021         T::Item: Borrow<com::BufferCopy>,
4022     {
4023         let pso = &*self.shared.service_pipes.copy_buffer;
4024         let wg_size = MTLSize {
4025             width: pso.thread_execution_width(),
4026             height: 1,
4027             depth: 1,
4028         };
4029 
4030         let (src_raw, src_range) = src.as_bound();
4031         let (dst_raw, dst_range) = dst.as_bound();
4032 
4033         let mut compute_datas = Vec::new();
4034         let mut inner = self.inner.borrow_mut();
4035         let mut blit_commands = Vec::new();
4036         let mut compute_commands = vec![
4037             //TODO: get rid of heap
4038             soft::ComputeCommand::BindPipeline(pso),
4039         ];
4040 
4041         for region in regions {
4042             let r = region.borrow();
4043             if r.size % WORD_SIZE as u64 == 0
4044                 && r.src % WORD_SIZE as u64 == 0
4045                 && r.dst % WORD_SIZE as u64 == 0
4046             {
4047                 blit_commands.alloc().init(soft::BlitCommand::CopyBuffer {
4048                     src: AsNative::from(src_raw),
4049                     dst: AsNative::from(dst_raw),
4050                     region: com::BufferCopy {
4051                         src: r.src + src_range.start,
4052                         dst: r.dst + dst_range.start,
4053                         size: r.size,
4054                     },
4055                 });
4056             } else {
4057                 // not natively supported, going through a compute shader
4058                 assert_eq!(0, r.size >> 32);
4059                 let src_aligned = r.src & !(WORD_SIZE as u64 - 1);
4060                 let dst_aligned = r.dst & !(WORD_SIZE as u64 - 1);
4061                 let offsets = (r.src - src_aligned) | ((r.dst - dst_aligned) << 16);
4062                 let size_and_offsets = [r.size as u32, offsets as u32];
4063                 compute_datas.push(Box::new(size_and_offsets));
4064 
4065                 let wg_count = MTLSize {
4066                     width: (r.size + wg_size.width - 1) / wg_size.width,
4067                     height: 1,
4068                     depth: 1,
4069                 };
4070 
4071                 compute_commands
4072                     .alloc()
4073                     .init(soft::ComputeCommand::BindBuffer {
4074                         index: 0,
4075                         buffer: AsNative::from(dst_raw),
4076                         offset: dst_aligned + dst_range.start,
4077                     });
4078                 compute_commands
4079                     .alloc()
4080                     .init(soft::ComputeCommand::BindBuffer {
4081                         index: 1,
4082                         buffer: AsNative::from(src_raw),
4083                         offset: src_aligned + src_range.start,
4084                     });
4085                 compute_commands
4086                     .alloc()
4087                     .init(soft::ComputeCommand::BindBufferData {
4088                         index: 2,
4089                         // Rust doesn't see that compute_datas will not lose this
4090                         // item and the boxed contents can't be moved otherwise.
4091                         words: mem::transmute(&compute_datas.last().unwrap()[..]),
4092                     });
4093                 compute_commands
4094                     .alloc()
4095                     .init(soft::ComputeCommand::Dispatch { wg_size, wg_count });
4096             }
4097         }
4098 
4099         let sink = inner.sink();
4100         if !blit_commands.is_empty() {
4101             sink.blit_commands(blit_commands.into_iter());
4102         }
4103         if compute_commands.len() > 1 {
4104             // first is bind PSO
4105             sink.quick_compute("copy_buffer", compute_commands.into_iter());
4106         }
4107     }
4108 
copy_image<T>( &mut self, src: &native::Image, src_layout: Layout, dst: &native::Image, dst_layout: Layout, regions: T, ) where T: IntoIterator, T::Item: Borrow<com::ImageCopy>,4109     unsafe fn copy_image<T>(
4110         &mut self,
4111         src: &native::Image,
4112         src_layout: Layout,
4113         dst: &native::Image,
4114         dst_layout: Layout,
4115         regions: T,
4116     ) where
4117         T: IntoIterator,
4118         T::Item: Borrow<com::ImageCopy>,
4119     {
4120         match (&src.like, &dst.like) {
4121             (&native::ImageLike::Unbound { .. }, _) | (_, &native::ImageLike::Unbound { .. }) => {
4122                 panic!("Unexpected Image::Unbound");
4123             }
4124             (
4125                 &native::ImageLike::Texture(ref src_raw),
4126                 &native::ImageLike::Texture(ref dst_raw),
4127             ) => {
4128                 let CommandBufferInner {
4129                     ref mut retained_textures,
4130                     ref mut sink,
4131                     ..
4132                 } = *self.inner.borrow_mut();
4133 
4134                 let new_src = if src.mtl_format == dst.mtl_format {
4135                     src_raw
4136                 } else {
4137                     assert_eq!(src.format_desc.bits, dst.format_desc.bits);
4138                     let tex = src_raw.new_texture_view(dst.mtl_format);
4139                     retained_textures.push(tex);
4140                     retained_textures.last().unwrap()
4141                 };
4142 
4143                 let commands = regions.into_iter().filter_map(|region| {
4144                     let r = region.borrow();
4145                     if r.extent.is_empty() {
4146                         None
4147                     } else {
4148                         Some(soft::BlitCommand::CopyImage {
4149                             src: AsNative::from(new_src.as_ref()),
4150                             dst: AsNative::from(dst_raw.as_ref()),
4151                             region: r.clone(),
4152                         })
4153                     }
4154                 });
4155 
4156                 sink.as_mut().unwrap().blit_commands(commands);
4157             }
4158             (&native::ImageLike::Buffer(ref src_buffer), &native::ImageLike::Texture(_)) => {
4159                 let src_extent = src.kind.extent();
4160                 self.copy_buffer_to_image(
4161                     src_buffer,
4162                     dst,
4163                     dst_layout,
4164                     regions.into_iter().map(|region| {
4165                         let r = region.borrow();
4166                         com::BufferImageCopy {
4167                             buffer_offset: src.byte_offset(r.src_offset),
4168                             buffer_width: src_extent.width,
4169                             buffer_height: src_extent.height,
4170                             image_layers: r.dst_subresource.clone(),
4171                             image_offset: r.dst_offset,
4172                             image_extent: r.extent,
4173                         }
4174                     }),
4175                 )
4176             }
4177             (&native::ImageLike::Texture(_), &native::ImageLike::Buffer(ref dst_buffer)) => {
4178                 let dst_extent = dst.kind.extent();
4179                 self.copy_image_to_buffer(
4180                     src,
4181                     src_layout,
4182                     dst_buffer,
4183                     regions.into_iter().map(|region| {
4184                         let r = region.borrow();
4185                         com::BufferImageCopy {
4186                             buffer_offset: dst.byte_offset(r.dst_offset),
4187                             buffer_width: dst_extent.width,
4188                             buffer_height: dst_extent.height,
4189                             image_layers: r.src_subresource.clone(),
4190                             image_offset: r.src_offset,
4191                             image_extent: r.extent,
4192                         }
4193                     }),
4194                 )
4195             }
4196             (
4197                 &native::ImageLike::Buffer(ref src_buffer),
4198                 &native::ImageLike::Buffer(ref dst_buffer),
4199             ) => self.copy_buffer(
4200                 src_buffer,
4201                 dst_buffer,
4202                 regions.into_iter().map(|region| {
4203                     let r = region.borrow();
4204                     com::BufferCopy {
4205                         src: src.byte_offset(r.src_offset),
4206                         dst: dst.byte_offset(r.dst_offset),
4207                         size: src.byte_extent(r.extent),
4208                     }
4209                 }),
4210             ),
4211         }
4212     }
4213 
copy_buffer_to_image<T>( &mut self, src: &native::Buffer, dst: &native::Image, _dst_layout: Layout, regions: T, ) where T: IntoIterator, T::Item: Borrow<com::BufferImageCopy>,4214     unsafe fn copy_buffer_to_image<T>(
4215         &mut self,
4216         src: &native::Buffer,
4217         dst: &native::Image,
4218         _dst_layout: Layout,
4219         regions: T,
4220     ) where
4221         T: IntoIterator,
4222         T::Item: Borrow<com::BufferImageCopy>,
4223     {
4224         match dst.like {
4225             native::ImageLike::Unbound { .. } => {
4226                 panic!("Unexpected Image::Unbound");
4227             }
4228             native::ImageLike::Texture(ref dst_raw) => {
4229                 let (src_raw, src_range) = src.as_bound();
4230                 let commands = regions.into_iter().filter_map(|region| {
4231                     let r = region.borrow();
4232                     if r.image_extent.is_empty() {
4233                         None
4234                     } else {
4235                         Some(soft::BlitCommand::CopyBufferToImage {
4236                             src: AsNative::from(src_raw),
4237                             dst: AsNative::from(dst_raw.as_ref()),
4238                             dst_desc: dst.format_desc,
4239                             region: com::BufferImageCopy {
4240                                 buffer_offset: r.buffer_offset + src_range.start,
4241                                 ..r.clone()
4242                             },
4243                         })
4244                     }
4245                 });
4246                 self.inner.borrow_mut().sink().blit_commands(commands);
4247             }
4248             native::ImageLike::Buffer(ref dst_buffer) => self.copy_buffer(
4249                 src,
4250                 dst_buffer,
4251                 regions.into_iter().map(|region| {
4252                     let r = region.borrow();
4253                     com::BufferCopy {
4254                         src: r.buffer_offset,
4255                         dst: dst.byte_offset(r.image_offset),
4256                         size: dst.byte_extent(r.image_extent),
4257                     }
4258                 }),
4259             ),
4260         }
4261     }
4262 
copy_image_to_buffer<T>( &mut self, src: &native::Image, _src_layout: Layout, dst: &native::Buffer, regions: T, ) where T: IntoIterator, T::Item: Borrow<com::BufferImageCopy>,4263     unsafe fn copy_image_to_buffer<T>(
4264         &mut self,
4265         src: &native::Image,
4266         _src_layout: Layout,
4267         dst: &native::Buffer,
4268         regions: T,
4269     ) where
4270         T: IntoIterator,
4271         T::Item: Borrow<com::BufferImageCopy>,
4272     {
4273         match src.like {
4274             native::ImageLike::Unbound { .. } => {
4275                 panic!("Unexpected Image::Unbound");
4276             }
4277             native::ImageLike::Texture(ref src_raw) => {
4278                 let (dst_raw, dst_range) = dst.as_bound();
4279                 let commands = regions.into_iter().filter_map(|region| {
4280                     let r = region.borrow();
4281                     if r.image_extent.is_empty() {
4282                         None
4283                     } else {
4284                         Some(soft::BlitCommand::CopyImageToBuffer {
4285                             src: AsNative::from(src_raw.as_ref()),
4286                             src_desc: src.format_desc,
4287                             dst: AsNative::from(dst_raw),
4288                             region: com::BufferImageCopy {
4289                                 buffer_offset: r.buffer_offset + dst_range.start,
4290                                 ..r.clone()
4291                             },
4292                         })
4293                     }
4294                 });
4295                 self.inner.borrow_mut().sink().blit_commands(commands);
4296             }
4297             native::ImageLike::Buffer(ref src_buffer) => self.copy_buffer(
4298                 src_buffer,
4299                 dst,
4300                 regions.into_iter().map(|region| {
4301                     let r = region.borrow();
4302                     com::BufferCopy {
4303                         src: src.byte_offset(r.image_offset),
4304                         dst: r.buffer_offset,
4305                         size: src.byte_extent(r.image_extent),
4306                     }
4307                 }),
4308             ),
4309         }
4310     }
4311 
draw(&mut self, vertices: Range<VertexCount>, instances: Range<InstanceCount>)4312     unsafe fn draw(&mut self, vertices: Range<VertexCount>, instances: Range<InstanceCount>) {
4313         debug_assert!(self.state.render_pso_is_compatible);
4314         if instances.start == instances.end {
4315             return;
4316         }
4317 
4318         let command = soft::RenderCommand::Draw {
4319             primitive_type: self.state.primitive_type,
4320             vertices,
4321             instances,
4322         };
4323         self.inner.borrow_mut().sink().pre_render().issue(command);
4324     }
4325 
draw_indexed( &mut self, indices: Range<IndexCount>, base_vertex: VertexOffset, instances: Range<InstanceCount>, )4326     unsafe fn draw_indexed(
4327         &mut self,
4328         indices: Range<IndexCount>,
4329         base_vertex: VertexOffset,
4330         instances: Range<InstanceCount>,
4331     ) {
4332         debug_assert!(self.state.render_pso_is_compatible);
4333         if instances.start == instances.end {
4334             return;
4335         }
4336 
4337         let command = soft::RenderCommand::DrawIndexed {
4338             primitive_type: self.state.primitive_type,
4339             index: self
4340                 .state
4341                 .index_buffer
4342                 .clone()
4343                 .expect("must bind index buffer"),
4344             indices,
4345             base_vertex,
4346             instances,
4347         };
4348         self.inner.borrow_mut().sink().pre_render().issue(command);
4349     }
4350 
draw_indirect( &mut self, buffer: &native::Buffer, offset: buffer::Offset, count: DrawCount, stride: u32, )4351     unsafe fn draw_indirect(
4352         &mut self,
4353         buffer: &native::Buffer,
4354         offset: buffer::Offset,
4355         count: DrawCount,
4356         stride: u32,
4357     ) {
4358         assert_eq!(offset % WORD_ALIGNMENT, 0);
4359         assert_eq!(stride % WORD_ALIGNMENT as u32, 0);
4360         debug_assert!(self.state.render_pso_is_compatible);
4361         let (raw, range) = buffer.as_bound();
4362 
4363         let commands = (0 .. count).map(|i| soft::RenderCommand::DrawIndirect {
4364             primitive_type: self.state.primitive_type,
4365             buffer: AsNative::from(raw),
4366             offset: range.start + offset + (i * stride) as buffer::Offset,
4367         });
4368 
4369         self.inner
4370             .borrow_mut()
4371             .sink()
4372             .pre_render()
4373             .issue_many(commands);
4374     }
4375 
draw_indexed_indirect( &mut self, buffer: &native::Buffer, offset: buffer::Offset, count: DrawCount, stride: u32, )4376     unsafe fn draw_indexed_indirect(
4377         &mut self,
4378         buffer: &native::Buffer,
4379         offset: buffer::Offset,
4380         count: DrawCount,
4381         stride: u32,
4382     ) {
4383         assert_eq!(offset % WORD_ALIGNMENT, 0);
4384         assert_eq!(stride % WORD_ALIGNMENT as u32, 0);
4385         debug_assert!(self.state.render_pso_is_compatible);
4386         let (raw, range) = buffer.as_bound();
4387 
4388         let commands = (0 .. count).map(|i| soft::RenderCommand::DrawIndexedIndirect {
4389             primitive_type: self.state.primitive_type,
4390             index: self
4391                 .state
4392                 .index_buffer
4393                 .clone()
4394                 .expect("must bind index buffer"),
4395             buffer: AsNative::from(raw),
4396             offset: range.start + offset + (i * stride) as buffer::Offset,
4397         });
4398 
4399         self.inner
4400             .borrow_mut()
4401             .sink()
4402             .pre_render()
4403             .issue_many(commands);
4404     }
4405 
set_event(&mut self, event: &native::Event, _: pso::PipelineStage)4406     unsafe fn set_event(&mut self, event: &native::Event, _: pso::PipelineStage) {
4407         self.inner
4408             .borrow_mut()
4409             .events
4410             .push((Arc::clone(&event.0), true));
4411     }
4412 
reset_event(&mut self, event: &native::Event, _: pso::PipelineStage)4413     unsafe fn reset_event(&mut self, event: &native::Event, _: pso::PipelineStage) {
4414         self.inner
4415             .borrow_mut()
4416             .events
4417             .push((Arc::clone(&event.0), false));
4418     }
4419 
wait_events<'a, I, J>( &mut self, events: I, stages: Range<pso::PipelineStage>, barriers: J, ) where I: IntoIterator, I::Item: Borrow<native::Event>, J: IntoIterator, J::Item: Borrow<memory::Barrier<'a, Backend>>,4420     unsafe fn wait_events<'a, I, J>(
4421         &mut self,
4422         events: I,
4423         stages: Range<pso::PipelineStage>,
4424         barriers: J,
4425     ) where
4426         I: IntoIterator,
4427         I::Item: Borrow<native::Event>,
4428         J: IntoIterator,
4429         J::Item: Borrow<memory::Barrier<'a, Backend>>,
4430     {
4431         let mut need_barrier = false;
4432 
4433         for event in events {
4434             let mut inner = self.inner.borrow_mut();
4435             let event = &event.borrow().0;
4436             let is_local = inner
4437                 .events
4438                 .iter()
4439                 .rfind(|ev| Arc::ptr_eq(&ev.0, event))
4440                 .map_or(false, |ev| ev.1);
4441             if is_local {
4442                 need_barrier = true;
4443             } else {
4444                 inner.host_events.push(Arc::clone(event));
4445             }
4446         }
4447 
4448         if need_barrier {
4449             self.pipeline_barrier(stages, memory::Dependencies::empty(), barriers);
4450         }
4451     }
4452 
begin_query(&mut self, query: query::Query<Backend>, flags: query::ControlFlags)4453     unsafe fn begin_query(&mut self, query: query::Query<Backend>, flags: query::ControlFlags) {
4454         match query.pool {
4455             native::QueryPool::Occlusion(ref pool_range) => {
4456                 debug_assert!(pool_range.start + query.id < pool_range.end);
4457                 let offset = (query.id + pool_range.start) as buffer::Offset
4458                     * mem::size_of::<u64>() as buffer::Offset;
4459                 let mode = if flags.contains(query::ControlFlags::PRECISE) {
4460                     metal::MTLVisibilityResultMode::Counting
4461                 } else {
4462                     metal::MTLVisibilityResultMode::Boolean
4463                 };
4464 
4465                 let com = self.state.set_visibility_query(mode, offset);
4466                 self.inner.borrow_mut().sink().pre_render().issue(com);
4467             }
4468         }
4469     }
4470 
end_query(&mut self, query: query::Query<Backend>)4471     unsafe fn end_query(&mut self, query: query::Query<Backend>) {
4472         match query.pool {
4473             native::QueryPool::Occlusion(ref pool_range) => {
4474                 let mut inner = self.inner.borrow_mut();
4475                 debug_assert!(pool_range.start + query.id < pool_range.end);
4476                 inner
4477                     .active_visibility_queries
4478                     .push(pool_range.start + query.id);
4479 
4480                 let com = self
4481                     .state
4482                     .set_visibility_query(metal::MTLVisibilityResultMode::Disabled, 0);
4483                 inner.sink().pre_render().issue(com);
4484             }
4485         }
4486     }
4487 
reset_query_pool(&mut self, pool: &native::QueryPool, queries: Range<query::Id>)4488     unsafe fn reset_query_pool(&mut self, pool: &native::QueryPool, queries: Range<query::Id>) {
4489         let visibility = &self.shared.visibility;
4490         match *pool {
4491             native::QueryPool::Occlusion(ref pool_range) => {
4492                 let mut inner = self.inner.borrow_mut();
4493                 debug_assert!(pool_range.start + queries.end <= pool_range.end);
4494                 inner.active_visibility_queries.retain(|&id| {
4495                     id < pool_range.start + queries.start || id >= pool_range.start + queries.end
4496                 });
4497 
4498                 let size_data = mem::size_of::<u64>() as buffer::Offset;
4499                 let offset_data = pool_range.start as buffer::Offset * size_data;
4500                 let command_data = soft::BlitCommand::FillBuffer {
4501                     dst: AsNative::from(visibility.buffer.as_ref()),
4502                     range: offset_data + queries.start as buffer::Offset * size_data
4503                         .. offset_data + queries.end as buffer::Offset * size_data,
4504                     value: 0,
4505                 };
4506 
4507                 let size_meta = mem::size_of::<u32>() as buffer::Offset;
4508                 let offset_meta =
4509                     visibility.availability_offset + pool_range.start as buffer::Offset * size_meta;
4510                 let command_meta = soft::BlitCommand::FillBuffer {
4511                     dst: AsNative::from(visibility.buffer.as_ref()),
4512                     range: offset_meta + queries.start as buffer::Offset * size_meta
4513                         .. offset_meta + queries.end as buffer::Offset * size_meta,
4514                     value: 0,
4515                 };
4516 
4517                 let commands = iter::once(command_data).chain(iter::once(command_meta));
4518                 inner.sink().blit_commands(commands);
4519             }
4520         }
4521     }
4522 
copy_query_pool_results( &mut self, pool: &native::QueryPool, queries: Range<query::Id>, buffer: &native::Buffer, offset: buffer::Offset, stride: buffer::Offset, flags: query::ResultFlags, )4523     unsafe fn copy_query_pool_results(
4524         &mut self,
4525         pool: &native::QueryPool,
4526         queries: Range<query::Id>,
4527         buffer: &native::Buffer,
4528         offset: buffer::Offset,
4529         stride: buffer::Offset,
4530         flags: query::ResultFlags,
4531     ) {
4532         let (raw, range) = buffer.as_bound();
4533         match *pool {
4534             native::QueryPool::Occlusion(ref pool_range) => {
4535                 let visibility = &self.shared.visibility;
4536                 let size_data = mem::size_of::<u64>() as buffer::Offset;
4537                 let size_meta = mem::size_of::<u32>() as buffer::Offset;
4538 
4539                 if stride == size_data
4540                     && flags.contains(query::ResultFlags::BITS_64)
4541                     && !flags.contains(query::ResultFlags::WITH_AVAILABILITY)
4542                 {
4543                     // if stride is matching, copy everything in one go
4544                     let com = soft::BlitCommand::CopyBuffer {
4545                         src: AsNative::from(visibility.buffer.as_ref()),
4546                         dst: AsNative::from(raw),
4547                         region: com::BufferCopy {
4548                             src: (pool_range.start + queries.start) as buffer::Offset * size_data,
4549                             dst: range.start + offset,
4550                             size: (queries.end - queries.start) as buffer::Offset * size_data,
4551                         },
4552                     };
4553                     self.inner
4554                         .borrow_mut()
4555                         .sink()
4556                         .blit_commands(iter::once(com));
4557                 } else {
4558                     // copy parts of individual entries
4559                     let size_payload = if flags.contains(query::ResultFlags::BITS_64) {
4560                         mem::size_of::<u64>() as buffer::Offset
4561                     } else {
4562                         mem::size_of::<u32>() as buffer::Offset
4563                     };
4564                     let commands = (0 .. queries.end - queries.start).flat_map(|i| {
4565                         let absolute_index =
4566                             (pool_range.start + queries.start + i) as buffer::Offset;
4567                         let dst_offset = range.start + offset + i as buffer::Offset * stride;
4568                         let com_data = soft::BlitCommand::CopyBuffer {
4569                             src: AsNative::from(visibility.buffer.as_ref()),
4570                             dst: AsNative::from(raw),
4571                             region: com::BufferCopy {
4572                                 src: absolute_index * size_data,
4573                                 dst: dst_offset,
4574                                 size: size_payload,
4575                             },
4576                         };
4577 
4578                         let (com_avail, com_pad) = if flags.contains(
4579                             query::ResultFlags::WITH_AVAILABILITY | query::ResultFlags::WAIT,
4580                         ) {
4581                             // Technically waiting is a no-op on a single queue. However,
4582                             // the client expects the availability to be set regardless.
4583                             let com = soft::BlitCommand::FillBuffer {
4584                                 dst: AsNative::from(raw),
4585                                 range: dst_offset + size_payload .. dst_offset + 2 * size_payload,
4586                                 value: !0,
4587                             };
4588                             (Some(com), None)
4589                         } else if flags.contains(query::ResultFlags::WITH_AVAILABILITY) {
4590                             let com_avail = soft::BlitCommand::CopyBuffer {
4591                                 src: AsNative::from(visibility.buffer.as_ref()),
4592                                 dst: AsNative::from(raw),
4593                                 region: com::BufferCopy {
4594                                     src: visibility.availability_offset
4595                                         + absolute_index * size_meta,
4596                                     dst: dst_offset + size_payload,
4597                                     size: size_meta,
4598                                 },
4599                             };
4600                             // An extra padding is required if the client expects 64 bits availability without a wait
4601                             let com_pad = if flags.contains(query::ResultFlags::BITS_64) {
4602                                 Some(soft::BlitCommand::FillBuffer {
4603                                     dst: AsNative::from(raw),
4604                                     range: dst_offset + size_payload + size_meta
4605                                         .. dst_offset + 2 * size_payload,
4606                                     value: 0,
4607                                 })
4608                             } else {
4609                                 None
4610                             };
4611                             (Some(com_avail), com_pad)
4612                         } else {
4613                             (None, None)
4614                         };
4615 
4616                         iter::once(com_data).chain(com_avail).chain(com_pad)
4617                     });
4618                     self.inner.borrow_mut().sink().blit_commands(commands);
4619                 }
4620             }
4621         }
4622     }
4623 
write_timestamp(&mut self, _: pso::PipelineStage, _: query::Query<Backend>)4624     unsafe fn write_timestamp(&mut self, _: pso::PipelineStage, _: query::Query<Backend>) {
4625         // nothing to do, timestamps are unsupported on Metal
4626     }
4627 
push_graphics_constants( &mut self, layout: &native::PipelineLayout, stages: pso::ShaderStageFlags, offset: u32, constants: &[u32], )4628     unsafe fn push_graphics_constants(
4629         &mut self,
4630         layout: &native::PipelineLayout,
4631         stages: pso::ShaderStageFlags,
4632         offset: u32,
4633         constants: &[u32],
4634     ) {
4635         self.state
4636             .update_push_constants(offset, constants, layout.total_push_constants);
4637         if stages.intersects(pso::ShaderStageFlags::GRAPHICS) {
4638             let mut inner = self.inner.borrow_mut();
4639             let mut pre = inner.sink().pre_render();
4640             // Note: the whole range is re-uploaded, which may be inefficient
4641             if stages.contains(pso::ShaderStageFlags::VERTEX) {
4642                 let pc = layout.push_constants.vs.unwrap();
4643                 pre.issue(self.state.push_vs_constants(pc));
4644             }
4645             if stages.contains(pso::ShaderStageFlags::FRAGMENT) {
4646                 let pc = layout.push_constants.ps.unwrap();
4647                 pre.issue(self.state.push_ps_constants(pc));
4648             }
4649         }
4650     }
4651 
push_compute_constants( &mut self, layout: &native::PipelineLayout, offset: u32, constants: &[u32], )4652     unsafe fn push_compute_constants(
4653         &mut self,
4654         layout: &native::PipelineLayout,
4655         offset: u32,
4656         constants: &[u32],
4657     ) {
4658         self.state
4659             .update_push_constants(offset, constants, layout.total_push_constants);
4660         let pc = layout.push_constants.cs.unwrap();
4661 
4662         // Note: the whole range is re-uploaded, which may be inefficient
4663         self.inner
4664             .borrow_mut()
4665             .sink()
4666             .pre_compute()
4667             .issue(self.state.push_cs_constants(pc));
4668     }
4669 
execute_commands<'a, T, I>(&mut self, cmd_buffers: I) where T: 'a + Borrow<CommandBuffer>, I: IntoIterator<Item = &'a T>,4670     unsafe fn execute_commands<'a, T, I>(&mut self, cmd_buffers: I)
4671     where
4672         T: 'a + Borrow<CommandBuffer>,
4673         I: IntoIterator<Item = &'a T>,
4674     {
4675         for cmd_buffer in cmd_buffers {
4676             let outer_borrowed = cmd_buffer.borrow();
4677             let inner_borrowed = outer_borrowed.inner.borrow_mut();
4678 
4679             let (exec_journal, is_inheriting) = match inner_borrowed.sink {
4680                 Some(CommandSink::Deferred {
4681                     ref journal,
4682                     is_inheriting,
4683                     ..
4684                 }) => (journal, is_inheriting),
4685                 _ => panic!("Unexpected secondary sink!"),
4686             };
4687 
4688             for (a, b) in self
4689                 .state
4690                 .descriptor_sets
4691                 .iter_mut()
4692                 .zip(&outer_borrowed.state.descriptor_sets)
4693             {
4694                 if !b.graphics_resources.is_empty() {
4695                     a.graphics_resources.clear();
4696                     a.graphics_resources
4697                         .extend_from_slice(&b.graphics_resources);
4698                 }
4699                 if !b.compute_resources.is_empty() {
4700                     a.compute_resources.clear();
4701                     a.compute_resources.extend_from_slice(&b.compute_resources);
4702                 }
4703             }
4704 
4705             let mut inner_self = self.inner.borrow_mut();
4706             inner_self.events.extend_from_slice(&inner_borrowed.events);
4707 
4708             match *inner_self.sink() {
4709                 CommandSink::Immediate {
4710                     ref mut cmd_buffer,
4711                     ref mut encoder_state,
4712                     ref mut num_passes,
4713                     ..
4714                 } => {
4715                     if is_inheriting {
4716                         let encoder = match encoder_state {
4717                             EncoderState::Render(ref encoder) => encoder,
4718                             _ => panic!("Expected Render encoder!"),
4719                         };
4720                         for command in &exec_journal.render_commands {
4721                             exec_render(encoder, command, &exec_journal.resources);
4722                         }
4723                     } else {
4724                         encoder_state.end();
4725                         *num_passes += exec_journal.passes.len();
4726                         exec_journal.record(cmd_buffer);
4727                     }
4728                 }
4729                 CommandSink::Deferred {
4730                     ref mut journal, ..
4731                 } => {
4732                     journal.extend(exec_journal, is_inheriting);
4733                 }
4734                 #[cfg(feature = "dispatch")]
4735                 CommandSink::Remote { .. } => unimplemented!(),
4736             }
4737         }
4738     }
4739 }
4740