diff --git a/src/backend/metal/src/command.rs b/src/backend/metal/src/command.rs index 309fff6e30f..2bd44477bda 100644 --- a/src/backend/metal/src/command.rs +++ b/src/backend/metal/src/command.rs @@ -730,15 +730,10 @@ enum CommandSink { }, } -enum PassDoor<'a> { - Open, - Closed { label: &'a str }, -} - /// A helper temporary object that consumes state-setting commands only /// applicable to a render pass currently encoded. enum PreRender<'a> { - Immediate(&'a metal::RenderCommandEncoder), + Immediate(&'a metal::RenderCommandEncoderRef), Deferred(&'a mut Vec>), Void, } @@ -758,12 +753,29 @@ impl<'a> PreRender<'a> { PreRender::Void => (), } } + + fn issue_many<'b, I>(&mut self, commands: I) + where + I: Iterator> + { + match *self { + PreRender::Immediate(encoder) => { + for com in commands { + exec_render(encoder, com); + } + } + PreRender::Deferred(ref mut list) => { + list.extend(commands.map(soft::RenderCommand::own)) + } + PreRender::Void => {} + } + } } /// A helper temporary object that consumes state-setting commands only /// applicable to a compute pass currently encoded. enum PreCompute<'a> { - Immediate(&'a metal::ComputeCommandEncoder), + Immediate(&'a metal::ComputeCommandEncoderRef), Deferred(&'a mut Vec>), Void, } @@ -776,9 +788,44 @@ impl<'a> PreCompute<'a> { PreCompute::Void => (), } } + + fn issue_many<'b, I>(&mut self, commands: I) + where + I: Iterator> + { + match *self { + PreCompute::Immediate(encoder) => { + for com in commands { + exec_compute(encoder, com); + } + } + PreCompute::Deferred(ref mut list) => { + list.extend(commands.map(soft::ComputeCommand::own)) + } + PreCompute::Void => {} + } + } } impl CommandSink { + fn stop_encoding(&mut self) { + match *self { + CommandSink::Immediate { ref mut encoder_state, .. } => { + encoder_state.end(); + } + CommandSink::Deferred { ref mut is_encoding, ref mut journal } => { + *is_encoding = false; + journal.stop(); + } + CommandSink::Remote { ref queue, ref cmd_buffer, ref mut pass, ref mut capacity, .. } => { + if let Some(pass) = pass.take() { + pass.update(capacity); + pass.schedule(queue, cmd_buffer); + } + } + } + } + /// Start issuing pre-render commands. Those can be rejected, so the caller is responsible /// for updating the state cache accordingly, so that it's set upon the start of a next pass. fn pre_render(&mut self) -> PreRender { @@ -799,22 +846,55 @@ impl CommandSink { } } - /// Issue provided render commands, expecting that we are encoding a render pass. - fn render_commands<'a, I>(&mut self, commands: I) + /// Switch the active encoder to render by starting a render pass. + fn switch_render<'a>( + &'a mut self, + descriptor: &'a metal::RenderPassDescriptorRef, + ) -> PreRender<'a> { + //assert!(AutoReleasePool::is_active()); + self.stop_encoding(); + + match *self { + CommandSink::Immediate { ref cmd_buffer, ref mut encoder_state, ref mut num_passes, .. } => { + *num_passes += 1; + let encoder = cmd_buffer.new_render_command_encoder(descriptor); + *encoder_state = EncoderState::Render(encoder.to_owned()); + PreRender::Immediate(encoder) + } + CommandSink::Deferred { ref mut is_encoding, ref mut journal } => { + let pass = soft::Pass::Render(descriptor.to_owned()); + *is_encoding = true; + journal.passes.push((pass, journal.render_commands.len() .. 0)); + PreRender::Deferred(&mut journal.render_commands) + } + CommandSink::Remote { ref mut pass, ref capacity, .. } => { + let mut list = Vec::with_capacity(capacity.render); + *pass = Some(EncodePass::Render(list, descriptor.to_owned())); + match *pass { + Some(EncodePass::Render(ref mut list, _)) => PreRender::Deferred(list), + _ => unreachable!() + } + } + } + } + + fn quick_render<'a, I>( + &mut self, + label: &str, + descriptor: &'a metal::RenderPassDescriptorRef, + commands: I, + ) where I: Iterator>, { - match self.pre_render() { - PreRender::Immediate(encoder) => { - for command in commands { - exec_render(encoder, command); - } - } - PreRender::Deferred(ref mut list) => { - list.extend(commands.into_iter().map(soft::RenderCommand::own)) + { + let mut pre = self.switch_render(descriptor); + if let PreRender::Immediate(encoder) = pre { + encoder.set_label(label); } - PreRender::Void => panic!("Not in render encoding state!"), + pre.issue_many(commands); } + self.stop_encoding(); } /// Issue provided blit commands. This function doesn't expect an active blit pass, @@ -823,22 +903,21 @@ impl CommandSink { where I: Iterator, { - match *self { + enum PreBlit<'b> { + Immediate(&'b metal::BlitCommandEncoderRef), + Deferred(&'b mut Vec), + } + + let pre = match *self { CommandSink::Immediate { encoder_state: EncoderState::Blit(ref encoder), .. } => { - for command in commands { - exec_blit(encoder, command); - } + PreBlit::Immediate(encoder) } CommandSink::Immediate { ref cmd_buffer, ref mut encoder_state, ref mut num_passes, .. } => { *num_passes += 1; encoder_state.end(); - let encoder = cmd_buffer.new_blit_command_encoder().to_owned(); - - for command in commands { - exec_blit(&encoder, command); - } - - *encoder_state = EncoderState::Blit(encoder); + let encoder = cmd_buffer.new_blit_command_encoder(); + *encoder_state = EncoderState::Blit(encoder.to_owned()); + PreBlit::Immediate(encoder) } CommandSink::Deferred { ref mut is_encoding, ref mut journal } => { *is_encoding = true; @@ -847,10 +926,10 @@ impl CommandSink { journal.stop(); journal.passes.push((soft::Pass::Blit, journal.blit_commands.len() .. 0)); } - journal.blit_commands.extend(commands); + PreBlit::Deferred(&mut journal.blit_commands) } CommandSink::Remote { pass: Some(EncodePass::Blit(ref mut list)), .. } => { - list.extend(commands); + PreBlit::Deferred(list) } CommandSink::Remote { ref queue, ref cmd_buffer, ref mut pass, ref mut capacity, .. } => { if let Some(pass) = pass.take() { @@ -858,8 +937,22 @@ impl CommandSink { pass.schedule(queue, cmd_buffer); } let mut list = Vec::with_capacity(capacity.blit); - list.extend(commands); *pass = Some(EncodePass::Blit(list)); + match *pass { + Some(EncodePass::Blit(ref mut list)) => PreBlit::Deferred(list), + _ => unreachable!() + } + } + }; + + match pre { + PreBlit::Immediate(encoder) => { + for com in commands { + exec_blit(encoder, com); + } + } + PreBlit::Deferred(list) => { + list.extend(commands); } } } @@ -884,139 +977,63 @@ impl CommandSink { } } - /// Issue provided compute commands, expecting that we are encoding a compute pass. - fn compute_commands<'a, I>(&mut self, commands: I) - where - I: Iterator>, - { - match self.pre_compute() { - PreCompute::Immediate(ref encoder) => { - for command in commands { - exec_compute(encoder, command); - } - } - PreCompute::Deferred(ref mut list) => { - list.extend(commands.into_iter().map(soft::ComputeCommand::own)); - } - PreCompute::Void => panic!("Not in compute encoding state!"), - } - } - - fn stop_encoding(&mut self) { + /// Switch the active encoder to compute. + /// Second returned value is `true` if the switch has just happened. + fn switch_compute(&mut self) -> (PreCompute, bool) { match *self { - CommandSink::Immediate { ref mut encoder_state, .. } => { + CommandSink::Immediate { encoder_state: EncoderState::Compute(ref encoder), .. } => { + (PreCompute::Immediate(encoder), false) + } + CommandSink::Immediate { ref cmd_buffer, ref mut encoder_state, ref mut num_passes, .. } => { + *num_passes += 1; encoder_state.end(); + let encoder = cmd_buffer.new_compute_command_encoder(); + *encoder_state = EncoderState::Compute(encoder.to_owned()); + (PreCompute::Immediate(encoder), true) } CommandSink::Deferred { ref mut is_encoding, ref mut journal } => { - *is_encoding = false; - journal.stop(); + *is_encoding = true; + let switch = if let Some(&(soft::Pass::Compute, _)) = journal.passes.last() { + false + } else { + journal.stop(); + journal.passes.push((soft::Pass::Compute, journal.compute_commands.len() .. 0)); + true + }; + (PreCompute::Deferred(&mut journal.compute_commands), switch) + } + CommandSink::Remote { pass: Some(EncodePass::Compute(ref mut list)), .. } => { + (PreCompute::Deferred(list), false) } CommandSink::Remote { ref queue, ref cmd_buffer, ref mut pass, ref mut capacity, .. } => { if let Some(pass) = pass.take() { pass.update(capacity); pass.schedule(queue, cmd_buffer); } - } - } - } - - fn begin_render_pass<'a, I>( - &mut self, - door: PassDoor, - descriptor: &'a metal::RenderPassDescriptorRef, - init_commands: I, - ) where - I: Iterator>, - { - //assert!(AutoReleasePool::is_active()); - self.stop_encoding(); - - match *self { - CommandSink::Immediate { ref cmd_buffer, ref mut encoder_state, ref mut num_passes, .. } => { - *num_passes += 1; - let encoder = cmd_buffer.new_render_command_encoder(descriptor); - for command in init_commands { - exec_render(encoder, command); - } - match door { - PassDoor::Open => { - *encoder_state = EncoderState::Render(encoder.to_owned()) - } - PassDoor::Closed { label } => { - encoder.set_label(label); - encoder.end_encoding(); - } - } - } - CommandSink::Deferred { ref mut is_encoding, ref mut journal } => { - let pass = soft::Pass::Render(descriptor.to_owned()); - let mut range = journal.render_commands.len() .. 0; - journal.render_commands.extend(init_commands.map(soft::RenderCommand::own)); - match door { - PassDoor::Open => *is_encoding = true, - PassDoor::Closed {..} => range.end = journal.render_commands.len(), - } - journal.passes.push((pass, range)) - } - CommandSink::Remote { ref queue, ref cmd_buffer, ref mut pass, ref capacity, .. } => { - let mut list = Vec::with_capacity(capacity.render); - list.extend(init_commands.map(soft::RenderCommand::own)); - let new_pass = EncodePass::Render(list, descriptor.to_owned()); - match door { - PassDoor::Open => *pass = Some(new_pass), - PassDoor::Closed { .. } => new_pass.schedule(queue, cmd_buffer), + let mut list = Vec::with_capacity(capacity.compute); + *pass = Some(EncodePass::Compute(list)); + match *pass { + Some(EncodePass::Compute(ref mut list)) => (PreCompute::Deferred(list), true), + _ => unreachable!() } } } } - fn begin_compute_pass<'a, I>( - &mut self, - door: PassDoor, - init_commands: I, - ) where - I: Iterator>, + fn quick_compute<'a, I>(&mut self, label: &str, commands: I) + where + I: Iterator> { - self.stop_encoding(); - - match *self { - CommandSink::Immediate { ref cmd_buffer, ref mut encoder_state, ref mut num_passes, .. } => { - *num_passes += 1; - autoreleasepool(|| { - let encoder = cmd_buffer.new_compute_command_encoder(); - for command in init_commands { - exec_compute(encoder, command); - } - match door { - PassDoor::Open => { - *encoder_state = EncoderState::Compute(encoder.to_owned()); - } - PassDoor::Closed { label } => { - encoder.set_label(label); - encoder.end_encoding(); - } - } - }) - } - CommandSink::Deferred { ref mut is_encoding, ref mut journal } => { - let mut range = journal.compute_commands.len() .. 0; - journal.compute_commands.extend(init_commands.map(soft::ComputeCommand::own)); - match door { - PassDoor::Open => *is_encoding = true, - PassDoor::Closed {..} => range.end = journal.compute_commands.len(), - }; - journal.passes.push((soft::Pass::Compute, range)) - } - CommandSink::Remote { ref queue, ref cmd_buffer, ref mut pass, ref capacity, .. } => { - let mut list = Vec::with_capacity(capacity.compute); - list.extend(init_commands.map(soft::ComputeCommand::own)); - let new_pass = EncodePass::Compute(list); - match door { - PassDoor::Open => *pass = Some(new_pass), - PassDoor::Closed { .. } => new_pass.schedule(queue, cmd_buffer), + { + let (mut pre, switch) = self.switch_compute(); + pre.issue_many(commands); + if switch { + if let PreCompute::Immediate(encoder) = pre { + encoder.set_label(label); } } } + self.stop_encoding(); } } @@ -1878,10 +1895,7 @@ impl com::RawCommandBuffer for CommandBuffer { }, ]; - inner.sink().begin_compute_pass( - PassDoor::Closed { label: "fill_buffer" }, - commands.iter().cloned(), - ); + inner.sink().quick_compute("fill_buffer", commands.iter().cloned()); } fn update_buffer( @@ -2059,12 +2073,7 @@ impl com::RawCommandBuffer for CommandBuffer { sink.as_mut() .unwrap() - .begin_render_pass( - PassDoor::Closed { label: "clear_image" }, - descriptor, - iter::empty(), - ); - // no actual pass body - everything is in the attachment clear operations + .quick_render("clear_image", descriptor, iter::empty()); } } } @@ -2221,7 +2230,10 @@ impl com::RawCommandBuffer for CommandBuffer { .chain(com_vertex) .chain(com_draw); - inner.sink().render_commands(commands); + inner + .sink() + .pre_render() + .issue_many(commands); } // reset all the affected states @@ -2257,7 +2269,11 @@ impl com::RawCommandBuffer for CommandBuffer { .chain(com_ds) .chain(com_vs) .chain(com_fs); - inner.sink().render_commands(commands); + + inner + .sink() + .pre_render() + .issue_many(commands); vertices.clear(); } @@ -2487,11 +2503,7 @@ impl com::RawCommandBuffer for CommandBuffer { inner .sink() - .begin_render_pass( - PassDoor::Closed { label: "blit_image" }, - &descriptor, - commands, - ); + .quick_render("blit_image", &descriptor, commands); } }); } @@ -2749,7 +2761,8 @@ impl com::RawCommandBuffer for CommandBuffer { self.inner .borrow_mut() .sink() - .begin_render_pass(PassDoor::Open, &**desc_guard, init_commands); + .switch_render(&**desc_guard) + .issue_many(init_commands); } fn next_subpass(&mut self, _contents: com::SubpassContents) { @@ -3108,40 +3121,34 @@ impl com::RawCommandBuffer for CommandBuffer { } fn dispatch(&mut self, count: WorkGroupCount) { - let init_commands = self.state.make_compute_commands(); + let mut inner = self.inner.borrow_mut(); + let (mut pre, init) = inner.sink().switch_compute(); + if init { + pre.issue_many(self.state.make_compute_commands()); + } - let command = soft::ComputeCommand::Dispatch { + pre.issue(soft::ComputeCommand::Dispatch { wg_size: self.state.work_group_size, wg_count: MTLSize { width: count[0] as _, height: count[1] as _, depth: count[2] as _, }, - }; - - let mut inner = self.inner.borrow_mut(); - let sink = inner.sink(); - //TODO: re-use compute encoders - sink.begin_compute_pass(PassDoor::Open, init_commands); - sink.compute_commands(iter::once(command)); - sink.stop_encoding(); + }); } fn dispatch_indirect(&mut self, buffer: &native::Buffer, offset: buffer::Offset) { - let init_commands = self.state.make_compute_commands(); + let mut inner = self.inner.borrow_mut(); + let (mut pre, init) = inner.sink().switch_compute(); + if init { + pre.issue_many(self.state.make_compute_commands()); + } - let command = soft::ComputeCommand::DispatchIndirect { + pre.issue(soft::ComputeCommand::DispatchIndirect { wg_size: self.state.work_group_size, buffer: BufferPtr(buffer.raw.as_ptr()), offset, - }; - - let mut inner = self.inner.borrow_mut(); - let sink = inner.sink(); - //TODO: re-use compute encoders - sink.begin_compute_pass(PassDoor::Open, init_commands); - sink.compute_commands(iter::once(command)); - sink.stop_encoding(); + }); } fn copy_buffer( @@ -3210,12 +3217,8 @@ impl com::RawCommandBuffer for CommandBuffer { if !blit_commands.is_empty() { sink.blit_commands(blit_commands.into_iter()); } - if compute_commands.len() > 1 { // first is bind PSO - sink.begin_compute_pass( - PassDoor::Closed { label: "copy_buffer" }, - compute_commands.into_iter(), - ); + sink.quick_compute("copy_buffer", compute_commands.into_iter()); } } @@ -3324,7 +3327,8 @@ impl com::RawCommandBuffer for CommandBuffer { self.inner .borrow_mut() .sink() - .render_commands(iter::once(command)); + .pre_render() + .issue(command); } fn draw_indexed( @@ -3347,7 +3351,8 @@ impl com::RawCommandBuffer for CommandBuffer { self.inner .borrow_mut() .sink() - .render_commands(iter::once(command)); + .pre_render() + .issue(command); } fn draw_indirect( @@ -3370,7 +3375,8 @@ impl com::RawCommandBuffer for CommandBuffer { self.inner .borrow_mut() .sink() - .render_commands(commands); + .pre_render() + .issue_many(commands); } fn draw_indexed_indirect( @@ -3394,7 +3400,8 @@ impl com::RawCommandBuffer for CommandBuffer { self.inner .borrow_mut() .sink() - .render_commands(commands); + .pre_render() + .issue_many(commands); } fn begin_query(