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(®ion, dst_desc, &extent);
1839 let r = ®ion.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(®ion, src_desc, &extent);
1867 let r = ®ion.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