1use crate::private::NodeSealed;
58use {
59 crate::{
60 AnyResource, Graph, Node, Resource, ResourceMap,
61 cmd::{
62 AttachmentIndex, ClearColorValue, Command, CommandRef, ComputeCommandRef,
63 GraphicsCommandRef, LoadOp, PipelineCommand, RayTracingCommandRef, StoreOp,
64 Subresource, SubresourceRange,
65 },
66 driver::{
67 DriverError,
68 accel_struct::{
69 AccelerationStructure, AccelerationStructureInfo, AccelerationStructureInfoBuilder,
70 },
71 buffer::{Buffer, BufferInfo, BufferInfoBuilder},
72 compute::ComputePipeline,
73 graphics::{DepthStencilInfo, GraphicsPipeline},
74 image::{Image, ImageInfo, ImageInfoBuilder, ImageViewInfo},
75 ray_tracing::RayTracingPipeline,
76 },
77 node::{
78 AccelerationStructureLeaseNode, AccelerationStructureNode,
79 AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode,
80 ImageLeaseNode, ImageNode, SwapchainImageNode,
81 },
82 pool::SubmissionPool,
83 submission::Submission,
84 },
85 ash::vk,
86 std::{
87 collections::HashMap,
88 marker::PhantomData,
89 ops::Range,
90 sync::{Arc, Mutex},
91 },
92};
93
94#[cfg(feature = "checked")]
95use crate::GraphId;
96
97use std::sync::atomic::{AtomicU64, Ordering};
98
99fn next_stream_scope_id() -> u64 {
100 static NEXT_ID: AtomicU64 = AtomicU64::new(1);
101
102 NEXT_ID.fetch_add(1, Ordering::Relaxed)
103}
104
105#[cfg(feature = "checked")]
106#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
107pub(crate) struct CommandStreamId(u64);
108
109#[cfg(feature = "checked")]
110impl CommandStreamId {
111 fn next() -> Self {
112 Self(next_stream_scope_id())
113 }
114}
115
116#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
142pub struct StreamArg<T> {
143 pub(crate) arg_index: usize,
144 pub(crate) index: usize,
145
146 #[cfg(feature = "checked")]
147 pub(crate) stream_id: CommandStreamId,
148
149 #[cfg(feature = "checked")]
150 pub(crate) graph_id: GraphId,
151
152 __: PhantomData<fn() -> T>,
153}
154
155impl<T> Clone for StreamArg<T> {
156 fn clone(&self) -> Self {
157 *self
158 }
159}
160
161impl<T> Copy for StreamArg<T> {}
162
163impl<T> StreamArg<T> {
164 pub(crate) fn new(
165 arg_index: usize,
166 index: usize,
167 #[cfg(feature = "checked")] stream_id: CommandStreamId,
168 #[cfg(feature = "checked")] graph_id: GraphId,
169 ) -> Self {
170 Self {
171 arg_index,
172 index,
173 #[cfg(feature = "checked")]
174 stream_id,
175 #[cfg(feature = "checked")]
176 graph_id,
177 __: PhantomData,
178 }
179 }
180}
181
182impl NodeSealed for StreamArg<AccelerationStructure> {
183 fn borrow(self, resources: &[AnyResource]) -> &<Self as Node>::Resource {
184 resources[self.index].expect_accel_struct()
185 }
186
187 fn borrow_at(self, resources: &[AnyResource], index: usize) -> &<Self as Node>::Resource {
188 resources[index].expect_accel_struct()
189 }
190
191 #[cfg(feature = "checked")]
192 fn assert_owner(&self, _graph_id: GraphId) {
193 #[cfg(feature = "checked")]
194 assert!(
195 self.graph_id == _graph_id,
196 "node belongs to a different graph"
197 );
198 }
199}
200
201impl Node for StreamArg<AccelerationStructure> {
202 type Resource = AccelerationStructure;
203 type SyncInfo = crate::driver::accel_struct::AccelerationStructureSyncInfo;
204
205 fn index(&self) -> usize {
206 self.index
207 }
208}
209
210impl NodeSealed for StreamArg<Buffer> {
211 fn borrow(self, resources: &[AnyResource]) -> &<Self as Node>::Resource {
212 resources[self.index].expect_buffer()
213 }
214
215 fn borrow_at(self, resources: &[AnyResource], index: usize) -> &<Self as Node>::Resource {
216 resources[index].expect_buffer()
217 }
218
219 #[cfg(feature = "checked")]
220 fn assert_owner(&self, _graph_id: GraphId) {
221 #[cfg(feature = "checked")]
222 assert!(
223 self.graph_id == _graph_id,
224 "node belongs to a different graph"
225 );
226 }
227}
228
229impl Node for StreamArg<Buffer> {
230 type Resource = Buffer;
231 type SyncInfo = crate::driver::buffer::BufferSyncInfo;
232
233 fn index(&self) -> usize {
234 self.index
235 }
236}
237
238impl NodeSealed for StreamArg<Image> {
239 fn borrow(self, resources: &[AnyResource]) -> &<Self as Node>::Resource {
240 resources[self.index].expect_image()
241 }
242
243 fn borrow_at(self, resources: &[AnyResource], index: usize) -> &<Self as Node>::Resource {
244 resources[index].expect_image()
245 }
246
247 #[cfg(feature = "checked")]
248 fn assert_owner(&self, _graph_id: GraphId) {
249 #[cfg(feature = "checked")]
250 assert!(
251 self.graph_id == _graph_id,
252 "node belongs to a different graph"
253 );
254 }
255}
256
257impl Node for StreamArg<Image> {
258 type Resource = Image;
259 type SyncInfo = crate::driver::image::ImageSyncInfo;
260
261 fn index(&self) -> usize {
262 self.index
263 }
264}
265
266pub type AccelerationStructureArg = StreamArg<AccelerationStructure>;
278
279pub type BufferArg = StreamArg<Buffer>;
294
295pub type ImageArg = StreamArg<Image>;
312
313#[derive(Clone, Copy, Debug)]
314pub(crate) enum StreamArgData {
315 AccelerationStructure(AccelerationStructureInfo),
316 Buffer(BufferInfo),
317 Image(ImageInfo),
318}
319
320#[derive(Debug)]
321pub(crate) struct CommandStreamInner {
322 pub(crate) arg_nodes: Box<[usize]>,
323 pub(crate) args: Box<[StreamArgData]>,
324 pub(crate) prepared: bool,
325 pub(crate) submission: Mutex<Submission>,
326
327 #[cfg(feature = "checked")]
328 pub(crate) stream_id: CommandStreamId,
329
330 #[cfg(feature = "checked")]
331 pub(crate) graph_id: GraphId,
332}
333
334#[derive(Clone, Debug)]
356pub struct CommandStream<A = ()> {
357 pub args: A,
359 pub(crate) inner: Arc<CommandStreamInner>,
360}
361
362#[derive(Debug)]
381pub struct CommandStreamDraft<A = ()> {
382 pub args: A,
384 inner: CommandStreamInner,
385}
386
387pub struct CommandStreamMut {
407 pub(crate) arg_nodes: Vec<usize>,
408 pub(crate) args: Vec<StreamArgData>,
409 pub(crate) graph: Graph,
410 #[cfg(feature = "checked")]
411 pub(crate) stream_id: CommandStreamId,
412}
413
414pub struct StreamCommand<'a> {
429 inner: Command<'a>,
430}
431
432pub struct StreamPipelineCommand<'a, T> {
449 inner: PipelineCommand<'a, T>,
450}
451
452#[doc(hidden)]
454pub trait StreamPipeline<'a>: stream_private::StreamPipelineSealed {
455 type Command;
457
458 fn bind_stream_cmd(self, cmd: StreamCommand<'a>) -> Self::Command;
460}
461
462macro_rules! stream_pipeline {
463 ($pipeline:ty) => {
464 impl<'a> StreamPipeline<'a> for $pipeline {
465 type Command = StreamPipelineCommand<'a, $pipeline>;
466
467 fn bind_stream_cmd(self, cmd: StreamCommand<'a>) -> Self::Command {
468 StreamPipelineCommand {
469 inner: cmd.inner.bind_pipeline(self),
470 }
471 }
472 }
473
474 impl stream_private::StreamPipelineSealed for $pipeline {}
475
476 impl<'a> StreamPipeline<'a> for &'a $pipeline {
477 type Command = StreamPipelineCommand<'a, $pipeline>;
478
479 fn bind_stream_cmd(self, cmd: StreamCommand<'a>) -> Self::Command {
480 StreamPipelineCommand {
481 inner: cmd.inner.bind_pipeline(self),
482 }
483 }
484 }
485
486 impl<'a> stream_private::StreamPipelineSealed for &'a $pipeline {}
487 };
488}
489
490stream_pipeline!(ComputePipeline);
491stream_pipeline!(GraphicsPipeline);
492stream_pipeline!(RayTracingPipeline);
493
494#[allow(private_bounds)]
495impl<'a> StreamCommand<'a> {
496 pub fn bind_resource<R>(&mut self, resource: R) -> R::Node
498 where
499 R: Resource,
500 {
501 self.inner.bind_resource(resource)
502 }
503
504 pub fn bind_pipeline<P>(self, pipeline: P) -> P::Command
506 where
507 P: StreamPipeline<'a>,
508 {
509 pipeline.bind_stream_cmd(self)
510 }
511
512 pub fn debug_name(mut self, name: impl Into<String>) -> Self {
514 self.inner.set_debug_name(name);
515 self
516 }
517
518 pub fn record_cmd(
523 mut self,
524 func: impl for<'r> Fn(CommandRef<'r>) + Send + Sync + 'static,
525 ) -> Self {
526 self.record_cmd_mut(func);
527 self
528 }
529
530 pub fn record_cmd_mut(
535 &mut self,
536 func: impl for<'r> Fn(CommandRef<'r>) + Send + Sync + 'static,
537 ) {
538 self.inner.record_stream_mut(func);
539 }
540
541 pub fn resource_access<N>(mut self, resource_node: N, access: vk_sync::AccessType) -> Self
543 where
544 N: Node + Subresource,
545 SubresourceRange: From<N::Range>,
546 {
547 self.inner.set_resource_access(resource_node, access);
548 self
549 }
550
551 pub fn set_resource_access<N>(&mut self, resource_node: N, access: vk_sync::AccessType)
553 where
554 N: Node + Subresource,
555 SubresourceRange: From<N::Range>,
556 {
557 self.inner.set_resource_access(resource_node, access);
558 }
559}
560
561#[allow(private_bounds)]
562impl<'a, T> StreamPipelineCommand<'a, T> {
563 pub fn bind_resource<R>(&mut self, resource: R) -> R::Node
565 where
566 R: Resource,
567 {
568 self.inner.bind_resource(resource)
569 }
570
571 pub fn resource_access<N>(mut self, resource_node: N, access: vk_sync::AccessType) -> Self
573 where
574 N: Node + Subresource,
575 SubresourceRange: From<N::Range>,
576 {
577 self.inner.set_resource_access(resource_node, access);
578 self
579 }
580
581 pub fn set_resource_access<N>(
583 &mut self,
584 resource_node: N,
585 access: vk_sync::AccessType,
586 ) -> &mut Self
587 where
588 N: Node + Subresource,
589 SubresourceRange: From<N::Range>,
590 {
591 self.inner.set_resource_access(resource_node, access);
592 self
593 }
594}
595
596impl StreamPipelineCommand<'_, ComputePipeline> {
597 pub fn record_cmd(
602 mut self,
603 func: impl for<'r> Fn(ComputeCommandRef<'r>) + Send + Sync + 'static,
604 ) -> Self {
605 self.record_cmd_mut(func);
606 self
607 }
608
609 pub fn record_cmd_mut(
614 &mut self,
615 func: impl for<'r> Fn(ComputeCommandRef<'r>) + Send + Sync + 'static,
616 ) {
617 self.inner.record_stream_mut(func);
618 }
619}
620
621impl StreamPipelineCommand<'_, GraphicsPipeline> {
622 pub fn depth_stencil(mut self, depth_stencil: impl Into<DepthStencilInfo>) -> Self {
624 self.inner.set_depth_stencil(depth_stencil);
625 self
626 }
627
628 pub fn color_attachment_image(
630 mut self,
631 color_attachment: AttachmentIndex,
632 image: impl Into<AnyImageNode>,
633 load: LoadOp<ClearColorValue>,
634 store: StoreOp,
635 ) -> Self {
636 self.inner
637 .set_color_attachment_image(color_attachment, image, load, store);
638 self
639 }
640
641 pub fn color_attachment_image_view(
643 mut self,
644 color_attachment: AttachmentIndex,
645 image: impl Into<AnyImageNode>,
646 image_view_info: impl Into<ImageViewInfo>,
647 load: LoadOp<ClearColorValue>,
648 store: StoreOp,
649 ) -> Self {
650 self.inner.set_color_attachment_image_view(
651 color_attachment,
652 image,
653 image_view_info,
654 load,
655 store,
656 );
657 self
658 }
659
660 pub fn depth_stencil_attachment_image(
662 mut self,
663 image: impl Into<AnyImageNode>,
664 load: LoadOp<vk::ClearDepthStencilValue>,
665 store: StoreOp,
666 ) -> Self {
667 self.inner
668 .set_depth_stencil_attachment_image(image, load, store);
669 self
670 }
671
672 pub fn record_cmd(
677 mut self,
678 func: impl for<'r> Fn(GraphicsCommandRef<'r>) + Send + Sync + 'static,
679 ) -> Self {
680 self.record_cmd_mut(func);
681 self
682 }
683
684 pub fn record_cmd_mut(
689 &mut self,
690 func: impl for<'r> Fn(GraphicsCommandRef<'r>) + Send + Sync + 'static,
691 ) {
692 self.inner.record_stream_mut(func);
693 }
694}
695
696impl StreamPipelineCommand<'_, RayTracingPipeline> {
697 pub fn record_cmd(
702 mut self,
703 func: impl for<'r> Fn(RayTracingCommandRef<'r>) + Send + Sync + 'static,
704 ) -> Self {
705 self.record_cmd_mut(func);
706 self
707 }
708
709 pub fn record_cmd_mut(
714 &mut self,
715 func: impl for<'r> Fn(RayTracingCommandRef<'r>) + Send + Sync + 'static,
716 ) {
717 self.inner.record_stream_mut(func);
718 }
719}
720
721impl CommandStreamMut {
722 pub fn arg<I>(&mut self, info: I) -> I::Arg
724 where
725 I: StreamArgInfo,
726 {
727 info.bind_stream_arg(self)
728 }
729
730 pub fn begin_cmd(&mut self) -> StreamCommand<'_> {
732 StreamCommand {
733 inner: self.graph.begin_cmd(),
734 }
735 }
736
737 pub fn bind_resource<R>(&mut self, resource: R) -> R::Node
739 where
740 R: Resource,
741 {
742 self.graph.bind_resource(resource)
743 }
744
745 pub fn resource<N>(&self, resource_node: N) -> &N::Resource
747 where
748 N: StreamResourceNode,
749 {
750 self.graph.resource(resource_node)
751 }
752
753 pub fn blit_image(
755 &mut self,
756 src: impl Into<AnyImageNode>,
757 dst: impl Into<AnyImageNode>,
758 filter: vk::Filter,
759 ) -> &mut Self {
760 self.graph.blit_image(src, dst, filter);
761 self
762 }
763
764 pub fn blit_image_region(
766 &mut self,
767 src: impl Into<AnyImageNode>,
768 dst: impl Into<AnyImageNode>,
769 filter: vk::Filter,
770 regions: impl AsRef<[vk::ImageBlit]> + 'static + Send,
771 ) -> &mut Self {
772 self.graph.blit_image_region(src, dst, filter, regions);
773 self
774 }
775
776 pub fn clear_color_image(
778 &mut self,
779 image: impl Into<AnyImageNode>,
780 color: impl Into<ClearColorValue>,
781 ) -> &mut Self {
782 self.graph.clear_color_image(image, color);
783 self
784 }
785
786 pub fn clear_depth_stencil_image(
788 &mut self,
789 image: impl Into<AnyImageNode>,
790 depth: f32,
791 stencil: u32,
792 ) -> &mut Self {
793 self.graph.clear_depth_stencil_image(image, depth, stencil);
794 self
795 }
796
797 pub fn copy_buffer(
799 &mut self,
800 src: impl Into<AnyBufferNode>,
801 dst: impl Into<AnyBufferNode>,
802 ) -> &mut Self {
803 self.graph.copy_buffer(src, dst);
804 self
805 }
806
807 pub fn copy_buffer_region(
809 &mut self,
810 src: impl Into<AnyBufferNode>,
811 dst: impl Into<AnyBufferNode>,
812 regions: impl AsRef<[vk::BufferCopy]> + 'static + Send,
813 ) -> &mut Self {
814 self.graph.copy_buffer_region(src, dst, regions);
815 self
816 }
817
818 pub fn copy_buffer_to_image(
820 &mut self,
821 src: impl Into<AnyBufferNode>,
822 dst: impl Into<AnyImageNode>,
823 ) -> &mut Self {
824 self.graph.copy_buffer_to_image(src, dst);
825 self
826 }
827
828 pub fn copy_buffer_to_image_region(
830 &mut self,
831 src: impl Into<AnyBufferNode>,
832 dst: impl Into<AnyImageNode>,
833 regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send,
834 ) -> &mut Self {
835 self.graph.copy_buffer_to_image_region(src, dst, regions);
836 self
837 }
838
839 pub fn copy_image(
841 &mut self,
842 src: impl Into<AnyImageNode>,
843 dst: impl Into<AnyImageNode>,
844 ) -> &mut Self {
845 self.graph.copy_image(src, dst);
846 self
847 }
848
849 pub fn copy_image_region(
851 &mut self,
852 src: impl Into<AnyImageNode>,
853 dst: impl Into<AnyImageNode>,
854 regions: impl AsRef<[vk::ImageCopy]> + 'static + Send,
855 ) -> &mut Self {
856 self.graph.copy_image_region(src, dst, regions);
857 self
858 }
859
860 pub fn copy_image_to_buffer(
862 &mut self,
863 src: impl Into<AnyImageNode>,
864 dst: impl Into<AnyBufferNode>,
865 ) -> &mut Self {
866 self.graph.copy_image_to_buffer(src, dst);
867 self
868 }
869
870 pub fn copy_image_to_buffer_region(
872 &mut self,
873 src: impl Into<AnyImageNode>,
874 dst: impl Into<AnyBufferNode>,
875 regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send,
876 ) -> &mut Self {
877 self.graph.copy_image_to_buffer_region(src, dst, regions);
878 self
879 }
880
881 pub fn fill_buffer(
883 &mut self,
884 buffer: impl Into<AnyBufferNode>,
885 region: Range<vk::DeviceSize>,
886 data: u32,
887 ) -> &mut Self {
888 self.graph.fill_buffer(buffer, region, data);
889 self
890 }
891
892 pub fn update_buffer(
894 &mut self,
895 buffer: impl Into<AnyBufferNode>,
896 offset: vk::DeviceSize,
897 data: impl AsRef<[u8]> + 'static + Send,
898 ) -> &mut Self {
899 self.graph.update_buffer(buffer, offset, data);
900 self
901 }
902
903 fn push_arg(&mut self, data: StreamArgData) -> usize {
904 let index = self.args.len();
905 self.args.push(data);
906 index
907 }
908
909 fn bind_arg_resource(&mut self, data: StreamArgData) -> usize {
910 let resource = match data {
911 StreamArgData::AccelerationStructure(info) => {
912 AnyResource::AccelerationStructureArg(info)
913 }
914 StreamArgData::Buffer(info) => AnyResource::BufferArg(info),
915 StreamArgData::Image(info) => AnyResource::ImageArg(info),
916 };
917
918 self.graph.bind_stream_arg_resource(resource)
919 }
920}
921
922impl CommandStream<()> {
923 pub fn finalize<A>(build: impl FnOnce(&mut CommandStreamMut) -> A) -> CommandStreamDraft<A> {
928 let mut stream = CommandStreamMut {
929 arg_nodes: Vec::new(),
930 args: Vec::new(),
931 graph: Graph::new(),
932 #[cfg(feature = "checked")]
933 stream_id: CommandStreamId::next(),
934 };
935 let args = build(&mut stream);
936
937 #[cfg(feature = "checked")]
938 let graph_id = stream.graph.graph_id();
939
940 let submission = stream.graph.finalize();
941 submission.assert_reusable_commands();
942
943 CommandStreamDraft {
944 args,
945 inner: CommandStreamInner {
946 arg_nodes: stream.arg_nodes.into_boxed_slice(),
947 args: stream.args.into_boxed_slice(),
948 prepared: false,
949 submission: Mutex::new(submission),
950
951 #[cfg(feature = "checked")]
952 stream_id: stream.stream_id,
953
954 #[cfg(feature = "checked")]
955 graph_id,
956 },
957 }
958 }
959
960 pub fn prepare<P, A>(
965 pool: &mut P,
966 build: impl FnOnce(&mut CommandStreamMut) -> A,
967 ) -> Result<CommandStream<A>, DriverError>
968 where
969 P: SubmissionPool,
970 {
971 Self::finalize(build).prepare(pool)
972 }
973}
974
975impl<A> CommandStreamDraft<A> {
976 pub fn into_stream(self) -> CommandStream<A> {
981 CommandStream {
982 args: self.args,
983 inner: Arc::new(self.inner),
984 }
985 }
986
987 pub fn prepare<P>(mut self, pool: &mut P) -> Result<CommandStream<A>, DriverError>
992 where
993 P: SubmissionPool,
994 {
995 self.inner
996 .submission
997 .get_mut()
998 .expect("poisoned command stream submission")
999 .prepare_command_stream(pool)?;
1000 self.inner.prepared = true;
1001
1002 Ok(self.into_stream())
1003 }
1004}
1005
1006pub struct CommandStreamRun<'a, A> {
1031 pub(crate) bindings: Vec<Option<usize>>,
1032 pub(crate) graph: &'a mut Graph,
1033 pub(crate) stream: &'a CommandStream<A>,
1034}
1035
1036impl<'a, A> CommandStreamRun<'a, A> {
1037 pub fn with_arg<T, N>(mut self, arg: StreamArg<T>, node: N) -> Self
1039 where
1040 N: StreamArgBindable<T>,
1041 {
1042 #[cfg(feature = "checked")]
1043 assert!(
1044 arg.stream_id == self.stream.inner.stream_id,
1045 "argument belongs to a different command stream"
1046 );
1047 node.assert_parent_node();
1048 self.graph.assert_node_owner(&node);
1049 self.bindings[arg.arg_index] = Some(node.index());
1050 self
1051 }
1052
1053 pub fn finish(self) -> &'a mut Graph {
1055 #[cfg(feature = "checked")]
1056 assert!(
1057 self.bindings.iter().all(Option::is_some),
1058 "missing command stream argument"
1059 );
1060
1061 self.graph
1062 .append_command_stream(self.stream, &self.bindings);
1063 self.graph
1064 }
1065}
1066
1067impl Graph {
1068 pub fn insert_cmd_stream<'a, A>(
1073 &'a mut self,
1074 stream: &'a CommandStream<A>,
1075 ) -> CommandStreamRun<'a, A> {
1076 CommandStreamRun {
1077 bindings: vec![None; stream.inner.args.len()],
1078 graph: self,
1079 stream,
1080 }
1081 }
1082}
1083
1084impl Graph {
1085 pub(crate) fn append_command_stream<A>(
1086 &mut self,
1087 stream: &CommandStream<A>,
1088 bindings: &[Option<usize>],
1089 ) {
1090 if stream.inner.prepared {
1091 self.append_prepared_command_stream(stream, bindings);
1092 } else {
1093 self.append_unprepared_command_stream(stream, bindings);
1094 }
1095 }
1096
1097 fn append_unprepared_command_stream<A>(
1098 &mut self,
1099 stream: &CommandStream<A>,
1100 bindings: &[Option<usize>],
1101 ) {
1102 let submission = stream
1103 .inner
1104 .submission
1105 .lock()
1106 .expect("poisoned command stream submission");
1107 let stream_graph = submission.graph();
1108 let mut arg_by_node = HashMap::new();
1109
1110 for (arg_idx, &node_idx) in stream.inner.arg_nodes.iter().enumerate() {
1111 arg_by_node.insert(node_idx, arg_idx);
1112 }
1113
1114 let mut node_map = Vec::with_capacity(stream_graph.resources.len());
1115 for (node_idx, resource) in stream_graph.resources.iter().enumerate() {
1116 if let Some(&arg_idx) = arg_by_node.get(&node_idx) {
1117 node_map.push(bindings[arg_idx].expect("missing command stream argument"));
1118 } else {
1119 node_map.push(self.resources.bind(resource.clone()));
1120 }
1121 }
1122
1123 for cmd in &stream_graph.cmds {
1124 let mut cmd = cmd.clone();
1125 cmd.remap_nodes(&node_map);
1126
1127 #[cfg(feature = "checked")]
1128 for exec in &mut cmd.execs {
1129 exec.stream_graph_id = Some(stream.inner.graph_id);
1130 }
1131
1132 self.cmds.push(cmd);
1133 }
1134 }
1135
1136 fn append_prepared_command_stream<A>(
1137 &mut self,
1138 stream: &CommandStream<A>,
1139 bindings: &[Option<usize>],
1140 ) {
1141 let stream_scope_id = next_stream_scope_id();
1142 let submission = stream
1143 .inner
1144 .submission
1145 .lock()
1146 .expect("poisoned command stream submission");
1147 let stream_graph = submission.graph();
1148 let mut arg_by_node = HashMap::new();
1149
1150 for (arg_idx, &node_idx) in stream.inner.arg_nodes.iter().enumerate() {
1151 arg_by_node.insert(node_idx, arg_idx);
1152 }
1153
1154 let mut cmd = self.begin_cmd().debug_name("command stream");
1155 cmd.set_stream_scope_id(stream_scope_id);
1156
1157 for stream_cmd in &stream_graph.cmds {
1158 for (node_idx, accesses) in stream_cmd
1159 .execs
1160 .iter()
1161 .flat_map(|exec| exec.accesses.iter())
1162 {
1163 let Some(&arg_idx) = arg_by_node.get(&node_idx) else {
1164 continue;
1165 };
1166 let parent_node_idx = bindings[arg_idx].expect("missing command stream argument");
1167
1168 for access in accesses {
1169 cmd.push_subresource_access_index(
1170 parent_node_idx,
1171 access.subresource,
1172 access.access,
1173 );
1174 }
1175 }
1176 }
1177
1178 drop(submission);
1179
1180 let stream = Arc::clone(&stream.inner);
1181 let bindings = bindings.to_vec();
1182 cmd.record_stream(move |cmd| {
1183 let submission = stream
1184 .submission
1185 .lock()
1186 .expect("poisoned command stream submission");
1187 let stream_graph = submission.graph();
1188 let mut arg_by_node = HashMap::new();
1189
1190 for (arg_idx, &node_idx) in stream.arg_nodes.iter().enumerate() {
1191 arg_by_node.insert(node_idx, arg_idx);
1192 }
1193
1194 let resources = stream_graph
1195 .resources
1196 .iter()
1197 .enumerate()
1198 .map(|(node_idx, resource)| {
1199 if let Some(&arg_idx) = arg_by_node.get(&node_idx) {
1200 cmd.clone_resource_at(
1201 bindings[arg_idx].expect("missing command stream argument"),
1202 )
1203 } else {
1204 resource.clone()
1205 }
1206 })
1207 .collect();
1208 drop(submission);
1209
1210 stream
1211 .submission
1212 .lock()
1213 .expect("poisoned command stream submission")
1214 .record_prepared_command_stream(&cmd, ResourceMap::from_resources(resources))
1215 .expect("unable to record command stream");
1216 });
1217 }
1218}
1219
1220#[allow(private_bounds)]
1222#[doc(hidden)]
1223pub trait StreamArgInfo: stream_private::StreamArgInfoSealed {
1224 type Arg;
1226
1227 #[doc(hidden)]
1228 fn bind_stream_arg(self, stream: &mut CommandStreamMut) -> Self::Arg;
1229}
1230
1231#[allow(private_bounds)]
1233#[doc(hidden)]
1234pub trait StreamArgBindable<T>: stream_private::StreamArgBindableSealed<T> + Node {
1235 #[doc(hidden)]
1236 fn assert_parent_node(&self);
1237}
1238
1239#[allow(private_bounds)]
1241#[doc(hidden)]
1242pub trait StreamResourceNode: stream_private::StreamResourceNodeSealed + Node {}
1243
1244mod stream_private {
1245 pub trait StreamArgInfoSealed {}
1246
1247 pub trait StreamArgBindableSealed<T> {}
1248
1249 pub trait StreamResourceNodeSealed {}
1250
1251 pub trait StreamPipelineSealed {}
1252}
1253
1254macro_rules! stream_arg_info {
1255 ($info:ty, $builder:ty, $variant:ident, $arg:ty) => {
1256 impl stream_private::StreamArgInfoSealed for $info {}
1257
1258 impl StreamArgInfo for $info {
1259 type Arg = $arg;
1260
1261 fn bind_stream_arg(self, stream: &mut CommandStreamMut) -> Self::Arg {
1262 let data = StreamArgData::$variant(self);
1263 let arg_index = stream.push_arg(data);
1264 let node_index = stream.bind_arg_resource(data);
1265 stream.arg_nodes.push(node_index);
1266 StreamArg::new(
1267 arg_index,
1268 node_index,
1269 #[cfg(feature = "checked")]
1270 stream.stream_id,
1271 #[cfg(feature = "checked")]
1272 stream.graph.graph_id(),
1273 )
1274 }
1275 }
1276
1277 impl stream_private::StreamArgInfoSealed for $builder {}
1278
1279 impl StreamArgInfo for $builder {
1280 type Arg = $arg;
1281
1282 fn bind_stream_arg(self, stream: &mut CommandStreamMut) -> Self::Arg {
1283 self.build().bind_stream_arg(stream)
1284 }
1285 }
1286 };
1287}
1288
1289stream_arg_info!(
1290 AccelerationStructureInfo,
1291 AccelerationStructureInfoBuilder,
1292 AccelerationStructure,
1293 AccelerationStructureArg
1294);
1295stream_arg_info!(BufferInfo, BufferInfoBuilder, Buffer, BufferArg);
1296stream_arg_info!(ImageInfo, ImageInfoBuilder, Image, ImageArg);
1297
1298macro_rules! stream_arg_bindable {
1299 ($resource:ty => $($node:ty),+ $(,)?) => {
1300 $(
1301 impl stream_private::StreamArgBindableSealed<$resource> for $node {}
1302
1303 impl StreamArgBindable<$resource> for $node {
1304 fn assert_parent_node(&self) {}
1305 }
1306 )+
1307 };
1308}
1309
1310stream_arg_bindable!(
1311 AccelerationStructure => AccelerationStructureNode,
1312 AccelerationStructureLeaseNode,
1313);
1314stream_arg_bindable!(Buffer => BufferNode, BufferLeaseNode);
1315stream_arg_bindable!(Image => ImageNode, ImageLeaseNode, SwapchainImageNode);
1316
1317impl stream_private::StreamArgBindableSealed<AccelerationStructure>
1318 for AnyAccelerationStructureNode
1319{
1320}
1321
1322impl StreamArgBindable<AccelerationStructure> for AnyAccelerationStructureNode {
1323 fn assert_parent_node(&self) {
1324 assert!(
1325 !matches!(self, Self::Arg(_)),
1326 "stream argument cannot be supplied as a parent graph node"
1327 );
1328 }
1329}
1330
1331impl stream_private::StreamArgBindableSealed<Buffer> for AnyBufferNode {}
1332
1333impl StreamArgBindable<Buffer> for AnyBufferNode {
1334 fn assert_parent_node(&self) {
1335 assert!(
1336 !matches!(self, Self::Arg(_)),
1337 "stream argument cannot be supplied as a parent graph node"
1338 );
1339 }
1340}
1341
1342impl stream_private::StreamArgBindableSealed<Image> for AnyImageNode {}
1343
1344impl StreamArgBindable<Image> for AnyImageNode {
1345 fn assert_parent_node(&self) {
1346 assert!(
1347 !matches!(self, Self::Arg(_)),
1348 "stream argument cannot be supplied as a parent graph node"
1349 );
1350 }
1351}
1352
1353macro_rules! stream_resource_node {
1354 ($($node:ty),+ $(,)?) => {
1355 $(
1356 impl stream_private::StreamResourceNodeSealed for $node {}
1357 impl StreamResourceNode for $node {}
1358 )+
1359 };
1360}
1361
1362stream_resource_node!(
1363 AccelerationStructureNode,
1364 AccelerationStructureLeaseNode,
1365 BufferNode,
1366 BufferLeaseNode,
1367 ImageNode,
1368 ImageLeaseNode,
1369 SwapchainImageNode,
1370);
1371
1372#[cfg(test)]
1373mod tests {
1374 use super::*;
1375 use crate::{
1376 driver::{
1377 descriptor_set::{DescriptorPool, DescriptorPoolInfo},
1378 render_pass::{RenderPass, RenderPassInfo},
1379 },
1380 pool::Pool,
1381 };
1382
1383 struct NoopPool;
1384
1385 impl Pool<DescriptorPoolInfo, DescriptorPool> for NoopPool {
1386 fn resource(
1387 &mut self,
1388 _: DescriptorPoolInfo,
1389 ) -> Result<crate::pool::Lease<DescriptorPool>, DriverError> {
1390 unreachable!()
1391 }
1392 }
1393
1394 impl Pool<RenderPassInfo, RenderPass> for NoopPool {
1395 fn resource(
1396 &mut self,
1397 _: RenderPassInfo,
1398 ) -> Result<crate::pool::Lease<RenderPass>, DriverError> {
1399 unreachable!()
1400 }
1401 }
1402
1403 #[test]
1404 fn empty_stream_can_be_inserted() {
1405 let stream = CommandStream::finalize(|_| {}).into_stream();
1406 let mut graph = Graph::new();
1407
1408 graph.insert_cmd_stream(&stream).finish();
1409 }
1410
1411 #[test]
1412 fn reusable_callback_can_prepare_stream() {
1413 let stream = CommandStream::finalize(|stream| {
1414 stream.begin_cmd().record_cmd(|_| {});
1415 })
1416 .into_stream();
1417 let mut graph = Graph::new();
1418
1419 graph.insert_cmd_stream(&stream).finish();
1420 assert_eq!(graph.cmds.len(), 1);
1421 }
1422
1423 #[test]
1424 fn reusable_callback_can_prepare_optimized_stream() {
1425 let mut pool = NoopPool;
1426 let stream = CommandStream::prepare(&mut pool, |stream| {
1427 stream.begin_cmd().record_cmd(|_| {});
1428 })
1429 .expect("prepare stream");
1430 let mut graph = Graph::new();
1431
1432 graph.insert_cmd_stream(&stream).finish();
1433 assert_eq!(graph.cmds.len(), 1);
1434 }
1435
1436 #[test]
1437 fn unprepared_stream_expands_commands() {
1438 let stream = CommandStream::finalize(|stream| {
1439 stream.begin_cmd().record_cmd(|_| {});
1440 stream.begin_cmd().record_cmd(|_| {});
1441 })
1442 .into_stream();
1443 let mut graph = Graph::new();
1444
1445 graph.insert_cmd_stream(&stream).finish();
1446 assert_eq!(graph.cmds.len(), 2);
1447 }
1448
1449 #[test]
1450 fn prepared_stream_is_opaque_by_default() {
1451 let mut pool = NoopPool;
1452 let stream = CommandStream::prepare(&mut pool, |stream| {
1453 stream.begin_cmd().record_cmd(|_| {});
1454 stream.begin_cmd().record_cmd(|_| {});
1455 })
1456 .expect("prepare stream");
1457 let mut graph = Graph::new();
1458
1459 graph.insert_cmd_stream(&stream).finish();
1460 assert_eq!(graph.cmds.len(), 1);
1461 }
1462
1463 #[test]
1464 fn image_arg_can_use_info_based_helpers() {
1465 let stream = CommandStream::finalize(|stream| {
1466 let output = stream.arg(ImageInfo::image_2d(
1467 1,
1468 1,
1469 vk::Format::R8G8B8A8_UNORM,
1470 vk::ImageUsageFlags::TRANSFER_DST,
1471 ));
1472
1473 stream.clear_color_image(output, [0.0, 0.0, 0.0, 0.0]);
1474
1475 output
1476 })
1477 .into_stream();
1478
1479 assert_eq!(stream.inner.args.len(), 1);
1480 }
1481
1482 #[test]
1483 #[should_panic(expected = "missing command stream argument")]
1484 fn missing_arg_panics_at_finish() {
1485 let stream = CommandStream::finalize(|stream| {
1486 stream.arg(ImageInfo::image_2d(
1487 1,
1488 1,
1489 vk::Format::R8G8B8A8_UNORM,
1490 vk::ImageUsageFlags::SAMPLED,
1491 ));
1492 })
1493 .into_stream();
1494 let mut graph = Graph::new();
1495
1496 graph.insert_cmd_stream(&stream).finish();
1497 }
1498
1499 #[test]
1500 #[cfg(feature = "checked")]
1501 #[should_panic(expected = "argument belongs to a different command stream")]
1502 fn wrong_stream_arg_panics_at_with_arg() {
1503 let stream_a = CommandStream::finalize(|stream| {
1504 stream.arg(ImageInfo::image_2d(
1505 1,
1506 1,
1507 vk::Format::R8G8B8A8_UNORM,
1508 vk::ImageUsageFlags::SAMPLED,
1509 ))
1510 })
1511 .into_stream();
1512 let stream_b = CommandStream::finalize(|stream| {
1513 stream.arg(ImageInfo::image_2d(
1514 1,
1515 1,
1516 vk::Format::R8G8B8A8_UNORM,
1517 vk::ImageUsageFlags::SAMPLED,
1518 ))
1519 })
1520 .into_stream();
1521 let mut graph = Graph::new();
1522
1523 graph
1524 .insert_cmd_stream(&stream_a)
1525 .with_arg(stream_b.args, AnyImageNode::from(stream_b.args))
1526 .finish();
1527 }
1528
1529 #[test]
1530 #[should_panic(expected = "stream argument cannot be supplied as a parent graph node")]
1531 fn stream_arg_cannot_bind_as_parent_graph_node() {
1532 let stream = CommandStream::finalize(|stream| {
1533 stream.arg(ImageInfo::image_2d(
1534 1,
1535 1,
1536 vk::Format::R8G8B8A8_UNORM,
1537 vk::ImageUsageFlags::SAMPLED,
1538 ))
1539 })
1540 .into_stream();
1541 let mut graph = Graph::new();
1542
1543 graph
1544 .insert_cmd_stream(&stream)
1545 .with_arg(stream.args, AnyImageNode::from(stream.args))
1546 .finish();
1547 }
1548
1549 #[test]
1550 #[should_panic(expected = "command stream contains a one-shot callback")]
1551 fn one_shot_callback_cannot_prepare_stream() {
1552 let _ = CommandStream::finalize(|stream| {
1553 stream.graph.begin_cmd().record_cmd(|_| {});
1554 });
1555 }
1556}