1use std::collections::BTreeMap;
7
8use super::{
9 Backend, Element, Executable, ImageAtomic, ImageLoad, ImageQuery, ImageSample, ImageStore, Runner,
10 Scratch, error,
11};
12use crate::{AtomicAdd, Error, Execution, PixelShader, Sampler, Store};
13use iridia::{
14 BufferKind, D3dClass, Interface, Program, ResourceId, ResourceKind, Stage, StorageTarget, SystemValue,
15 TextureDim,
16};
17use std::sync::Mutex;
18
19#[derive(Debug)]
21struct Argument {
22 register: usize,
23 lanes: Vec<u8>,
25 boolean: bool,
27 used: bool,
28}
29
30#[derive(Debug)]
32struct Member {
33 register: usize,
34 lanes: Vec<u8>,
35 written: u8,
37}
38
39#[derive(Debug)]
41pub struct ProgramShader {
42 program: Program,
43 executable: Executable,
44 arguments: Vec<Argument>,
45 members: Vec<Member>,
46 input_count: usize,
47 output_count: usize,
48 position: Option<usize>,
51 constants: Vec<(u32, ResourceId, u32)>,
53 constant_buffers: Vec<(u32, u32)>,
54 textures: Vec<u32>,
55 samplers: Vec<u32>,
56 slots: BTreeMap<ResourceId, u32>,
58 buffers: BTreeMap<ResourceId, BufferKind>,
59 scalar_scratch: Mutex<Vec<Scratch<1>>>,
61 quad_scratch: Mutex<Vec<Scratch<4>>>,
62 lane_scratch: Mutex<Vec<Scratch<LANES>>>,
63}
64
65const LANES: usize = 16;
67
68#[derive(Default)]
70struct Room {
71 arguments: Vec<[u32; 4]>,
72 execution: Execution,
73}
74
75fn lanes_of(mask: u8) -> Vec<u8> {
76 (0..4).filter(|l| mask >> l & 1 != 0).collect()
77}
78
79fn width_of(v: &iridia::Varying) -> usize {
81 match v.system {
82 Some(SystemValue::FragCoord) => 4,
83 Some(
84 SystemValue::FrontFacing
85 | SystemValue::VertexIndex
86 | SystemValue::InstanceIndex
87 | SystemValue::PrimitiveIndex
88 | SystemValue::SampleIndex
89 | SystemValue::Coverage,
90 ) => 1,
91 _ => v.value.width as usize,
92 }
93}
94
95impl ProgramShader {
96 pub fn new(program: Program) -> Result<Self, Error> {
99 if !matches!(program.stage, Stage::Pixel | Stage::Vertex) {
100 return Err(error("not a pixel or vertex program"));
101 }
102 let executable = Executable::new(&program)?;
103 let interface = &program.interface;
104 let mut arguments = Vec::new();
105 let mut input_count = 0;
106 let io = iridia::entry_io(interface).map_err(error)?;
108 for item in &io.arguments {
109 let v = item
110 .varyings
111 .first()
112 .and_then(|&i| interface.inputs.get(i))
113 .ok_or_else(|| error("an entry argument names no input varying"))?;
114 let name = Program::varying_name(&v.label);
115 if v.provenance.d3d.is_none()
118 && matches!(
119 v.system,
120 Some(SystemValue::PrimitiveIndex | SystemValue::Coverage | SystemValue::SampleIndex)
121 )
122 {
123 arguments.push(Argument {
124 register: usize::MAX,
125 lanes: Vec::new(),
126 boolean: false,
127 used: false,
128 });
129 continue;
130 }
131 let sig = v
132 .provenance
133 .d3d
134 .ok_or_else(|| error(format!("input {name} has no D3D register")))?;
135 let lanes = lanes_of(sig.mask);
136 let width = width_of(v);
137 if lanes.len() < width {
138 return Err(error(format!(
139 "input {name} has {width} lanes but its register mask {:#x} marks {}",
140 sig.mask,
141 lanes.len()
142 )));
143 }
144 if v.used {
145 input_count = input_count.max(sig.register as usize + 1);
146 }
147 arguments.push(Argument {
148 register: sig.register as usize,
149 lanes: lanes.iter().take(width).copied().collect(),
150 boolean: v.system == Some(SystemValue::FrontFacing),
151 used: v.used,
152 });
153 }
154 let mut members = Vec::new();
155 let mut output_count = 0;
156 let position = interface
157 .outputs
158 .iter()
159 .position(|v| program.stage == Stage::Vertex && v.system == Some(SystemValue::ClipPosition));
160 for v in &interface.outputs {
161 let name = Program::varying_name(&v.label);
162 if !matches!(v.system, None | Some(SystemValue::Target(_))) {
164 members.push(Member {
165 register: usize::MAX,
166 lanes: Vec::new(),
167 written: 0,
168 });
169 continue;
170 }
171 let sig = v
172 .provenance
173 .d3d
174 .ok_or_else(|| error(format!("output {name} has no D3D register")))?;
175 output_count = output_count.max(sig.register as usize + 1);
176 members.push(Member {
177 register: sig.register as usize,
178 lanes: lanes_of(sig.mask),
179 written: if v.used {
181 sig.mask & !sig.read_write_mask
182 } else {
183 0
184 },
185 });
186 }
187 let mut constants = Vec::new();
188 let (mut textures, mut samplers) = (Vec::new(), Vec::new());
189 let (sampled_textures, sampled_samplers) = executable.sampled();
191 let mut slots = BTreeMap::new();
192 let mut buffers = BTreeMap::new();
193 for r in &interface.resources {
194 let binding = r
195 .provenance
196 .d3d
197 .ok_or_else(|| error(format!("resource {} has no D3D bind slot", r.provenance.name)))?;
198 slots.insert(r.id, binding.register);
199 match &r.kind {
200 ResourceKind::ConstantBuffer { .. } => {
201 let rows = rows_of(&program, r.id).ok_or_else(|| {
202 error(format!(
203 "constant buffer {} has no module array",
204 r.provenance.name
205 ))
206 })?;
207 constants.push((binding.register, r.id, rows));
208 }
209 ResourceKind::Texture {
212 dim: TextureDim::D2, ..
213 } if binding.class == D3dClass::ShaderResource && sampled_textures.contains(&r.id) => {
214 textures.push(binding.register);
215 }
216 ResourceKind::Sampler { .. } if sampled_samplers.contains(&r.id) => {
217 samplers.push(binding.register);
218 }
219 ResourceKind::Buffer(kind)
220 | ResourceKind::Storage {
221 target: StorageTarget::Buffer(kind),
222 ..
223 } => {
224 buffers.insert(r.id, *kind);
225 }
226 _ => {}
227 }
228 }
229 textures.sort_unstable();
230 textures.dedup();
231 samplers.sort_unstable();
232 samplers.dedup();
233 let constant_buffers = constants.iter().map(|&(slot, _, rows)| (slot, rows)).collect();
234 Ok(Self {
235 program,
236 executable,
237 arguments,
238 members,
239 input_count,
240 output_count,
241 position,
242 constants,
243 constant_buffers,
244 textures,
245 samplers,
246 slots,
247 buffers,
248 scalar_scratch: Mutex::new(Vec::new()),
249 quad_scratch: Mutex::new(Vec::new()),
250 lane_scratch: Mutex::new(Vec::new()),
251 })
252 }
253
254 pub fn program(&self) -> &Program {
256 &self.program
257 }
258
259 pub fn needs_quad(&self) -> bool {
261 self.executable.needs_quad()
262 }
263
264 fn arguments_of(&self, inputs: &[[u32; 4]]) -> Result<Vec<[u32; 4]>, Error> {
266 let mut out = Vec::with_capacity(self.arguments.len());
267 self.arguments_into(inputs, &mut out)?;
268 Ok(out)
269 }
270
271 fn arguments_into(&self, inputs: &[[u32; 4]], out: &mut Vec<[u32; 4]>) -> Result<(), Error> {
273 if inputs.len() < self.input_count {
274 return Err(error(format!(
275 "{} input registers given, {} needed",
276 inputs.len(),
277 self.input_count
278 )));
279 }
280 for a in &self.arguments {
281 let mut value = [0u32; 4];
282 if let Some(register) = inputs.get(a.register) {
283 for (slot, lane) in value.iter_mut().zip(&a.lanes) {
284 *slot = register.get(*lane as usize).copied().unwrap_or(0);
285 }
286 } else if a.used {
287 return Err(error(format!("input register {} not given", a.register)));
288 }
289 if a.boolean {
290 value[0] = u32::from(value[0] != 0);
291 }
292 out.push(value);
293 }
294 Ok(())
295 }
296
297 fn derivatives_of(&self, derivatives: &[[[u32; 4]; 2]]) -> Vec<[[u32; 4]; 2]> {
298 self.arguments
299 .iter()
300 .map(|a| {
301 let mut value = [[0u32; 4]; 2];
302 if let Some(d) = derivatives.get(a.register) {
303 for (out, from) in value.iter_mut().zip(d) {
304 for (slot, lane) in out.iter_mut().zip(&a.lanes) {
305 *slot = from.get(*lane as usize).copied().unwrap_or(0);
306 }
307 }
308 }
309 value
310 })
311 .collect()
312 }
313
314 fn execution(&self, o: super::Output) -> Execution {
315 let mut e = Execution::default();
316 self.execution_into(&o.values, o.discarded, o.steps, o.samples, &mut e);
317 e
318 }
319
320 fn execution_into(
322 &self,
323 values: &[[u32; 4]],
324 discarded: bool,
325 steps: u64,
326 samples: u64,
327 e: &mut Execution,
328 ) {
329 e.outputs.clear();
330 e.outputs.resize(self.output_count, [0; 4]);
331 e.output_masks.clear();
332 e.output_masks.resize(self.output_count, 0);
333 for (m, value) in self.members.iter().zip(values) {
334 if m.register == usize::MAX {
335 continue;
336 }
337 if let Some(register) = e.outputs.get_mut(m.register) {
338 for (lane, word) in m.lanes.iter().zip(value) {
339 if let Some(slot) = register.get_mut(*lane as usize) {
340 *slot = *word;
341 }
342 }
343 }
344 if let Some(mask) = e.output_masks.get_mut(m.register) {
345 *mask |= m.written;
346 }
347 }
348 e.instructions = usize::try_from(steps).unwrap_or(usize::MAX);
349 e.samples = usize::try_from(samples).unwrap_or(usize::MAX);
350 e.discarded = discarded;
351 }
352
353 fn bound<'a>(&self, buffers: &[(u32, &'a [[u32; 4]])]) -> Result<super::Bound<'a>, Error> {
354 for &(slot, _, rows) in &self.constants {
355 let given = buffers
356 .iter()
357 .find(|(s, _)| *s == slot)
358 .map(|(_, v)| *v)
359 .ok_or_else(|| error(format!("no constant buffer cb{slot}")))?;
360 if given.len() < rows as usize {
361 return Err(error(format!(
362 "cb{slot}: {} rows given, {rows} needed",
363 given.len()
364 )));
365 }
366 }
367 self.executable
369 .bind(
370 |id| {
371 let (slot, _, _) = self.constants.iter().find(|(_, c, _)| *c == id)?;
372 buffers.iter().find(|(s, _)| s == slot).map(|(_, v)| *v)
373 },
374 |_| None,
375 )
376 .map_err(error)
377 }
378
379 fn backend<'a>(&'a self, sampler: &'a mut dyn Sampler) -> SlotBackend<'a> {
380 SlotBackend {
381 sampler,
382 slots: &self.slots,
383 buffers: &self.buffers,
384 }
385 }
386}
387
388fn take<const N: usize>(pool: &Mutex<Vec<Scratch<N>>>) -> Option<Scratch<N>> {
390 pool.lock().ok()?.pop()
391}
392
393fn give_back<const N: usize>(pool: &Mutex<Vec<Scratch<N>>>, scratch: Scratch<N>) {
394 if let Ok(mut pool) = pool.lock() {
395 pool.push(scratch);
396 }
397}
398
399fn rows_of(program: &Program, id: ResourceId) -> Option<u32> {
401 let global = program
402 .module
403 .global_variables
404 .try_get(program.global(id)?)
405 .ok()?;
406 match &program.module.types[global.ty].inner {
407 naga::TypeInner::Array {
408 size: naga::ArraySize::Constant(n),
409 ..
410 } => Some(n.get()),
411 _ => None,
412 }
413}
414
415pub(super) struct SlotBackend<'a> {
418 pub(super) sampler: &'a mut dyn Sampler,
419 pub(super) slots: &'a BTreeMap<ResourceId, u32>,
420 pub(super) buffers: &'a BTreeMap<ResourceId, BufferKind>,
421}
422impl SlotBackend<'_> {
423 fn slot(&self, id: ResourceId) -> Result<u32, String> {
424 self.slots
425 .get(&id)
426 .copied()
427 .ok_or_else(|| format!("resource {} has no bind slot", id.0))
428 }
429 fn slot_of(&self, e: Element) -> Result<u32, String> {
431 if e.index != 0 {
432 return Err(format!(
433 "resource {}: element {} of an array, which a bind-slot sampler does not answer",
434 e.id.0, e.index
435 ));
436 }
437 self.slot(e.id)
438 }
439 fn address(&self, id: ResourceId, word: u32) -> [u32; 3] {
441 match self.buffers.get(&id) {
442 Some(BufferKind::Structured { stride }) if *stride >= 4 => {
443 let per = stride / 4;
444 [
445 word.checked_div(per).unwrap_or(0),
446 word.checked_rem(per).unwrap_or(0).saturating_mul(4),
447 0,
448 ]
449 }
450 _ => [word, 0, 0],
452 }
453 }
454}
455impl Backend for SlotBackend<'_> {
456 fn sample(&mut self, r: ImageSample<Element>) -> Result<[f32; 4], String> {
457 let (texture, sampler) = (self.slot_of(r.texture)?, self.slot_of(r.sampler)?);
458 self.sampler.sample_image(ImageSample {
459 texture,
460 sampler,
461 dim: r.dim,
462 coord: r.coord,
463 layer: r.layer,
464 lod: r.lod,
465 offset: r.offset,
466 gather: r.gather,
467 depth_ref: r.depth_ref,
468 depth: r.depth,
469 })
470 }
471 fn load(&mut self, r: ImageLoad<Element>) -> Result<[u32; 4], String> {
472 let texture = self.slot_of(r.texture)?;
473 self.sampler.load_image(ImageLoad {
474 texture,
475 dim: r.dim,
476 storage: r.storage,
477 texel: r.texel,
478 layer: r.layer,
479 sample: r.sample,
480 mip: r.mip,
481 })
482 }
483 fn query(&mut self, r: ImageQuery<Element>) -> Result<[u32; 4], String> {
484 let texture = self.slot_of(r.texture)?;
485 self.sampler.query_image(ImageQuery {
486 texture,
487 dim: r.dim,
488 kind: r.kind,
489 level: r.level,
490 })
491 }
492 fn store_image(&mut self, r: ImageStore<ResourceId>) -> Result<(), String> {
493 let view = self.slot_of(Element {
494 id: r.view,
495 index: r.index,
496 })?;
497 self.sampler.store_image(ImageStore {
498 view,
499 index: 0,
500 dim: r.dim,
501 texel: r.texel,
502 layer: r.layer,
503 value: r.value,
504 lanes: r.lanes,
505 })
506 }
507 fn atomic_image(&mut self, r: ImageAtomic<ResourceId>) -> Result<u32, String> {
508 let view = self.slot_of(Element {
509 id: r.view,
510 index: r.index,
511 })?;
512 self.sampler.atomic_image(ImageAtomic {
513 view,
514 index: 0,
515 dim: r.dim,
516 texel: r.texel,
517 layer: r.layer,
518 op: r.op,
519 signed: r.signed,
520 value: r.value,
521 })
522 }
523 fn store_buffer(&mut self, view: ResourceId, word: u32, value: [u32; 4], mask: u8) -> Result<(), String> {
524 let address = self.address(view, word);
525 let view = self.slot(view)?;
526 self.sampler.store(Store {
527 view,
528 address,
529 value,
530 mask,
531 })
532 }
533 fn atomic_add(&mut self, view: ResourceId, word: u32, value: u32) -> Result<u32, String> {
534 let address = self.address(view, word);
535 let view = self.slot(view)?;
536 self.sampler.atomic_add(AtomicAdd { view, address, value })
537 }
538 fn read_buffer(&mut self, view: ResourceId, word: u32, count: u8) -> Result<[u32; 4], String> {
539 let slot = self.slot(view)?;
540 let mut out = [0u32; 4];
541 match self.buffers.get(&view) {
542 Some(BufferKind::Typed(_)) => {
544 let (mut at, end) = (word, word.saturating_add(u32::from(count)));
545 while at < end {
546 let element = at / 4;
547 let lanes = self.sampler.load(crate::Load {
548 texture: slot,
549 texel: [i32::try_from(element).unwrap_or(i32::MAX), 0, 0],
550 mip: 0,
551 })?;
552 while at < end && at / 4 == element {
553 let from = lanes.get((at % 4) as usize).copied().unwrap_or(0);
554 if let Some(slot) = out.get_mut(at.saturating_sub(word) as usize) {
555 *slot = from;
556 }
557 at = at.saturating_add(1);
558 }
559 }
560 }
561 _ => {
562 for (i, w) in out.iter_mut().take(usize::from(count)).enumerate() {
563 let [element, offset, _] =
564 self.address(view, word.saturating_add(u32::try_from(i).unwrap_or(0)));
565 *w = self.sampler.structured(slot, element, offset)?;
566 }
567 }
568 }
569 Ok(out)
570 }
571 fn buffer_words(&mut self, view: ResourceId) -> Result<u32, String> {
572 let slot = self.slot(view)?;
573 let Some(elements) = self.sampler.buffer_elements(slot) else {
575 return Ok(u32::MAX);
576 };
577 let per = match self.buffers.get(&view) {
578 Some(BufferKind::Typed(_)) => 4,
579 Some(BufferKind::Structured { stride }) => (stride / 4).max(1),
580 _ => 1,
581 };
582 Ok(elements.saturating_mul(per))
583 }
584}
585
586impl PixelShader for ProgramShader {
587 fn interface(&self) -> &Interface {
588 &self.program.interface
589 }
590 fn input_count(&self) -> usize {
591 self.input_count
592 }
593 fn constant_buffers(&self) -> &[(u32, u32)] {
594 &self.constant_buffers
595 }
596 fn textures(&self) -> &[u32] {
597 &self.textures
598 }
599 fn samplers(&self) -> &[u32] {
600 &self.samplers
601 }
602
603 fn execute(
604 &self,
605 inputs: &[[u32; 4]],
606 buffers: &[(u32, &[[u32; 4]])],
607 sampler: &mut dyn Sampler,
608 ) -> Result<Execution, Error> {
609 self.run_scalar(inputs, None, buffers, sampler)
610 }
611
612 fn execute_with_derivatives(
613 &self,
614 inputs: &[[u32; 4]],
615 derivatives: &[[[u32; 4]; 2]],
616 buffers: &[(u32, &[[u32; 4]])],
617 sampler: &mut dyn Sampler,
618 ) -> Result<Execution, Error> {
619 self.run_scalar(inputs, Some(derivatives), buffers, sampler)
620 }
621
622 fn execute_lanes(
623 &self,
624 inputs: &[&[[u32; 4]]],
625 derivatives: &[[[u32; 4]; 2]],
626 buffers: &[(u32, &[[u32; 4]])],
627 sampler: &mut dyn Sampler,
628 each: &mut dyn FnMut(usize, &Execution),
629 ) -> Result<(), (usize, Error)> {
630 let lane_derivatives = self.derivatives_of(derivatives);
631 let mut room = Room::default();
632 for (k, chunk) in inputs.chunks(LANES).enumerate() {
633 let together = self.run_lanes(
634 chunk,
635 &lane_derivatives,
636 buffers,
637 sampler,
638 &mut room,
639 &mut |i, e| each(k.saturating_mul(LANES).saturating_add(i), e),
640 );
641 if together.is_err() {
644 for (i, one) in chunk.iter().enumerate() {
645 let done = self
646 .run_scalar(one, Some(derivatives), buffers, sampler)
647 .map_err(|e| (k.saturating_mul(LANES).saturating_add(i), e))?;
648 each(k.saturating_mul(LANES).saturating_add(i), &done);
649 }
650 }
651 }
652 Ok(())
653 }
654
655 fn execute_quad(
656 &self,
657 inputs: [&[[u32; 4]]; 4],
658 buffers: &[(u32, &[[u32; 4]])],
659 sampler: &mut dyn Sampler,
660 ) -> Result<[Execution; 4], Error> {
661 let bound = self.bound(buffers)?;
662 let arguments = [
663 self.arguments_of(inputs[0])?,
664 self.arguments_of(inputs[1])?,
665 self.arguments_of(inputs[2])?,
666 self.arguments_of(inputs[3])?,
667 ];
668 let mut backend = self.backend(sampler);
669 let scratch = take(&self.quad_scratch).unwrap_or_else(|| Scratch::new(&self.executable));
670 let mut runner = Runner::<4>::with_scratch(&self.executable, &bound, &mut backend, None, scratch);
671 let outputs = runner.run([&arguments[0], &arguments[1], &arguments[2], &arguments[3]]);
672 give_back(&self.quad_scratch, runner.into_scratch());
673 Ok(outputs.map_err(error)?.map(|o| self.execution(o)))
674 }
675}
676
677impl ProgramShader {
678 pub fn execute_vertex(
682 &self,
683 inputs: &[[u32; 4]],
684 buffers: &[(u32, &[[u32; 4]])],
685 sampler: &mut dyn Sampler,
686 ) -> Result<(Execution, [u32; 4]), Error> {
687 let at = self
688 .position
689 .ok_or_else(|| error("not a vertex program that writes a position"))?;
690 let bound = self.bound(buffers)?;
691 let arguments = self.arguments_of(inputs)?;
692 let mut backend = self.backend(sampler);
693 let scratch = take(&self.scalar_scratch).unwrap_or_else(|| Scratch::new(&self.executable));
694 let mut runner = Runner::<1>::with_scratch(&self.executable, &bound, &mut backend, None, scratch);
695 let output = runner.run([&arguments]);
696 give_back(&self.scalar_scratch, runner.into_scratch());
697 let [output] = output.map_err(error)?;
698 let position = output.values.get(at).copied().unwrap_or([0; 4]);
699 Ok((self.execution(output), position))
700 }
701
702 pub fn execute_lockstep(
706 &self,
707 inputs: &[&[[u32; 4]]],
708 derivatives: &[[[u32; 4]; 2]],
709 buffers: &[(u32, &[[u32; 4]])],
710 sampler: &mut dyn Sampler,
711 ) -> Result<Vec<Execution>, Error> {
712 let derivatives = self.derivatives_of(derivatives);
713 let mut room = Room::default();
714 let mut out = Vec::with_capacity(inputs.len());
715 for chunk in inputs.chunks(LANES) {
716 self.run_lanes(chunk, &derivatives, buffers, sampler, &mut room, &mut |_, e| {
717 out.push(e.clone())
718 })?;
719 }
720 Ok(out)
721 }
722
723 fn run_lanes(
726 &self,
727 inputs: &[&[[u32; 4]]],
728 derivatives: &[[[u32; 4]; 2]],
729 buffers: &[(u32, &[[u32; 4]])],
730 sampler: &mut dyn Sampler,
731 room: &mut Room,
732 each: &mut dyn FnMut(usize, &Execution),
733 ) -> Result<(), Error> {
734 let bound = self.bound(buffers)?;
735 let width = self.arguments.len();
736 room.arguments.clear();
737 for one in inputs {
738 self.arguments_into(one, &mut room.arguments)?;
739 }
740 let count = inputs.len();
741 if count == 0 {
742 return Ok(());
743 }
744 let arguments = &room.arguments;
746 let args: [&[[u32; 4]]; LANES] = std::array::from_fn(|l| {
747 let i = l.min(count.saturating_sub(1));
748 let from = i.saturating_mul(width);
749 arguments
750 .get(from..from.saturating_add(width))
751 .unwrap_or_default()
752 });
753 let mut backend = self.backend(sampler);
754 let scratch = take(&self.lane_scratch).unwrap_or_else(|| Scratch::new(&self.executable));
755 let mut runner = Runner::<LANES, u16>::with_scratch(
756 &self.executable,
757 &bound,
758 &mut backend,
759 Some(derivatives),
760 scratch,
761 );
762 let run = runner.run_in_place(args, count);
763 if run.is_ok() {
764 for l in 0..count {
765 let (values, discarded, steps, samples) = runner.lane(l);
766 self.execution_into(values, discarded, steps, samples, &mut room.execution);
767 each(l, &room.execution);
768 }
769 }
770 give_back(&self.lane_scratch, runner.into_scratch());
771 run.map_err(error)
772 }
773
774 fn run_scalar(
775 &self,
776 inputs: &[[u32; 4]],
777 derivatives: Option<&[[[u32; 4]; 2]]>,
778 buffers: &[(u32, &[[u32; 4]])],
779 sampler: &mut dyn Sampler,
780 ) -> Result<Execution, Error> {
781 let bound = self.bound(buffers)?;
782 let arguments = self.arguments_of(inputs)?;
783 let derivatives = derivatives.map(|d| self.derivatives_of(d));
784 let mut backend = self.backend(sampler);
785 let scratch = take(&self.scalar_scratch).unwrap_or_else(|| Scratch::new(&self.executable));
786 let mut runner = Runner::<1>::with_scratch(
787 &self.executable,
788 &bound,
789 &mut backend,
790 derivatives.as_deref(),
791 scratch,
792 );
793 let output = runner.run([&arguments]);
794 give_back(&self.scalar_scratch, runner.into_scratch());
795 let [output] = output.map_err(error)?;
796 Ok(self.execution(output))
797 }
798}