use crate::private::NodeSealed;
use {
crate::{
AnyResource, Graph, Node, Resource, ResourceMap,
cmd::{
AttachmentIndex, ClearColorValue, Command, CommandRef, ComputeCommandRef,
GraphicsCommandRef, LoadOp, PipelineCommand, RayTracingCommandRef, StoreOp,
Subresource, SubresourceRange,
},
driver::{
DriverError,
accel_struct::{
AccelerationStructure, AccelerationStructureInfo, AccelerationStructureInfoBuilder,
},
buffer::{Buffer, BufferInfo, BufferInfoBuilder},
compute::ComputePipeline,
graphics::{DepthStencilInfo, GraphicsPipeline},
image::{Image, ImageInfo, ImageInfoBuilder, ImageViewInfo},
ray_tracing::RayTracingPipeline,
},
node::{
AccelerationStructureLeaseNode, AccelerationStructureNode,
AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode,
ImageLeaseNode, ImageNode, SwapchainImageNode,
},
pool::SubmissionPool,
submission::Submission,
},
ash::vk,
std::{
collections::HashMap,
marker::PhantomData,
ops::Range,
sync::{Arc, Mutex},
},
};
#[cfg(feature = "checked")]
use crate::GraphId;
use std::sync::atomic::{AtomicU64, Ordering};
fn next_stream_scope_id() -> u64 {
static NEXT_ID: AtomicU64 = AtomicU64::new(1);
NEXT_ID.fetch_add(1, Ordering::Relaxed)
}
#[cfg(feature = "checked")]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub(crate) struct CommandStreamId(u64);
#[cfg(feature = "checked")]
impl CommandStreamId {
fn next() -> Self {
Self(next_stream_scope_id())
}
}
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct StreamArg<T> {
pub(crate) arg_index: usize,
pub(crate) index: usize,
#[cfg(feature = "checked")]
pub(crate) stream_id: CommandStreamId,
#[cfg(feature = "checked")]
pub(crate) graph_id: GraphId,
__: PhantomData<fn() -> T>,
}
impl<T> Clone for StreamArg<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for StreamArg<T> {}
impl<T> StreamArg<T> {
pub(crate) fn new(
arg_index: usize,
index: usize,
#[cfg(feature = "checked")] stream_id: CommandStreamId,
#[cfg(feature = "checked")] graph_id: GraphId,
) -> Self {
Self {
arg_index,
index,
#[cfg(feature = "checked")]
stream_id,
#[cfg(feature = "checked")]
graph_id,
__: PhantomData,
}
}
}
impl NodeSealed for StreamArg<AccelerationStructure> {
fn borrow(self, resources: &[AnyResource]) -> &<Self as Node>::Resource {
resources[self.index].expect_accel_struct()
}
fn borrow_at(self, resources: &[AnyResource], index: usize) -> &<Self as Node>::Resource {
resources[index].expect_accel_struct()
}
#[cfg(feature = "checked")]
fn assert_owner(&self, _graph_id: GraphId) {
#[cfg(feature = "checked")]
assert!(
self.graph_id == _graph_id,
"node belongs to a different graph"
);
}
}
impl Node for StreamArg<AccelerationStructure> {
type Resource = AccelerationStructure;
type SyncInfo = crate::driver::accel_struct::AccelerationStructureSyncInfo;
fn index(&self) -> usize {
self.index
}
}
impl NodeSealed for StreamArg<Buffer> {
fn borrow(self, resources: &[AnyResource]) -> &<Self as Node>::Resource {
resources[self.index].expect_buffer()
}
fn borrow_at(self, resources: &[AnyResource], index: usize) -> &<Self as Node>::Resource {
resources[index].expect_buffer()
}
#[cfg(feature = "checked")]
fn assert_owner(&self, _graph_id: GraphId) {
#[cfg(feature = "checked")]
assert!(
self.graph_id == _graph_id,
"node belongs to a different graph"
);
}
}
impl Node for StreamArg<Buffer> {
type Resource = Buffer;
type SyncInfo = crate::driver::buffer::BufferSyncInfo;
fn index(&self) -> usize {
self.index
}
}
impl NodeSealed for StreamArg<Image> {
fn borrow(self, resources: &[AnyResource]) -> &<Self as Node>::Resource {
resources[self.index].expect_image()
}
fn borrow_at(self, resources: &[AnyResource], index: usize) -> &<Self as Node>::Resource {
resources[index].expect_image()
}
#[cfg(feature = "checked")]
fn assert_owner(&self, _graph_id: GraphId) {
#[cfg(feature = "checked")]
assert!(
self.graph_id == _graph_id,
"node belongs to a different graph"
);
}
}
impl Node for StreamArg<Image> {
type Resource = Image;
type SyncInfo = crate::driver::image::ImageSyncInfo;
fn index(&self) -> usize {
self.index
}
}
pub type AccelerationStructureArg = StreamArg<AccelerationStructure>;
pub type BufferArg = StreamArg<Buffer>;
pub type ImageArg = StreamArg<Image>;
#[derive(Clone, Copy, Debug)]
pub(crate) enum StreamArgData {
AccelerationStructure(AccelerationStructureInfo),
Buffer(BufferInfo),
Image(ImageInfo),
}
#[derive(Debug)]
pub(crate) struct CommandStreamInner {
pub(crate) arg_nodes: Box<[usize]>,
pub(crate) args: Box<[StreamArgData]>,
pub(crate) prepared: bool,
pub(crate) submission: Mutex<Submission>,
#[cfg(feature = "checked")]
pub(crate) stream_id: CommandStreamId,
#[cfg(feature = "checked")]
pub(crate) graph_id: GraphId,
}
#[derive(Clone, Debug)]
pub struct CommandStream<A = ()> {
pub args: A,
pub(crate) inner: Arc<CommandStreamInner>,
}
#[derive(Debug)]
pub struct CommandStreamDraft<A = ()> {
pub args: A,
inner: CommandStreamInner,
}
pub struct CommandStreamMut {
pub(crate) arg_nodes: Vec<usize>,
pub(crate) args: Vec<StreamArgData>,
pub(crate) graph: Graph,
#[cfg(feature = "checked")]
pub(crate) stream_id: CommandStreamId,
}
pub struct StreamCommand<'a> {
inner: Command<'a>,
}
pub struct StreamPipelineCommand<'a, T> {
inner: PipelineCommand<'a, T>,
}
#[doc(hidden)]
pub trait StreamPipeline<'a>: stream_private::StreamPipelineSealed {
type Command;
fn bind_stream_cmd(self, cmd: StreamCommand<'a>) -> Self::Command;
}
macro_rules! stream_pipeline {
($pipeline:ty) => {
impl<'a> StreamPipeline<'a> for $pipeline {
type Command = StreamPipelineCommand<'a, $pipeline>;
fn bind_stream_cmd(self, cmd: StreamCommand<'a>) -> Self::Command {
StreamPipelineCommand {
inner: cmd.inner.bind_pipeline(self),
}
}
}
impl stream_private::StreamPipelineSealed for $pipeline {}
impl<'a> StreamPipeline<'a> for &'a $pipeline {
type Command = StreamPipelineCommand<'a, $pipeline>;
fn bind_stream_cmd(self, cmd: StreamCommand<'a>) -> Self::Command {
StreamPipelineCommand {
inner: cmd.inner.bind_pipeline(self),
}
}
}
impl<'a> stream_private::StreamPipelineSealed for &'a $pipeline {}
};
}
stream_pipeline!(ComputePipeline);
stream_pipeline!(GraphicsPipeline);
stream_pipeline!(RayTracingPipeline);
#[allow(private_bounds)]
impl<'a> StreamCommand<'a> {
pub fn bind_resource<R>(&mut self, resource: R) -> R::Node
where
R: Resource,
{
self.inner.bind_resource(resource)
}
pub fn bind_pipeline<P>(self, pipeline: P) -> P::Command
where
P: StreamPipeline<'a>,
{
pipeline.bind_stream_cmd(self)
}
pub fn debug_name(mut self, name: impl Into<String>) -> Self {
self.inner.set_debug_name(name);
self
}
pub fn record_cmd(
mut self,
func: impl for<'r> Fn(CommandRef<'r>) + Send + Sync + 'static,
) -> Self {
self.record_cmd_mut(func);
self
}
pub fn record_cmd_mut(
&mut self,
func: impl for<'r> Fn(CommandRef<'r>) + Send + Sync + 'static,
) {
self.inner.record_stream_mut(func);
}
pub fn resource_access<N>(mut self, resource_node: N, access: vk_sync::AccessType) -> Self
where
N: Node + Subresource,
SubresourceRange: From<N::Range>,
{
self.inner.set_resource_access(resource_node, access);
self
}
pub fn set_resource_access<N>(&mut self, resource_node: N, access: vk_sync::AccessType)
where
N: Node + Subresource,
SubresourceRange: From<N::Range>,
{
self.inner.set_resource_access(resource_node, access);
}
}
#[allow(private_bounds)]
impl<'a, T> StreamPipelineCommand<'a, T> {
pub fn bind_resource<R>(&mut self, resource: R) -> R::Node
where
R: Resource,
{
self.inner.bind_resource(resource)
}
pub fn resource_access<N>(mut self, resource_node: N, access: vk_sync::AccessType) -> Self
where
N: Node + Subresource,
SubresourceRange: From<N::Range>,
{
self.inner.set_resource_access(resource_node, access);
self
}
pub fn set_resource_access<N>(
&mut self,
resource_node: N,
access: vk_sync::AccessType,
) -> &mut Self
where
N: Node + Subresource,
SubresourceRange: From<N::Range>,
{
self.inner.set_resource_access(resource_node, access);
self
}
}
impl StreamPipelineCommand<'_, ComputePipeline> {
pub fn record_cmd(
mut self,
func: impl for<'r> Fn(ComputeCommandRef<'r>) + Send + Sync + 'static,
) -> Self {
self.record_cmd_mut(func);
self
}
pub fn record_cmd_mut(
&mut self,
func: impl for<'r> Fn(ComputeCommandRef<'r>) + Send + Sync + 'static,
) {
self.inner.record_stream_mut(func);
}
}
impl StreamPipelineCommand<'_, GraphicsPipeline> {
pub fn depth_stencil(mut self, depth_stencil: impl Into<DepthStencilInfo>) -> Self {
self.inner.set_depth_stencil(depth_stencil);
self
}
pub fn color_attachment_image(
mut self,
color_attachment: AttachmentIndex,
image: impl Into<AnyImageNode>,
load: LoadOp<ClearColorValue>,
store: StoreOp,
) -> Self {
self.inner
.set_color_attachment_image(color_attachment, image, load, store);
self
}
pub fn color_attachment_image_view(
mut self,
color_attachment: AttachmentIndex,
image: impl Into<AnyImageNode>,
image_view_info: impl Into<ImageViewInfo>,
load: LoadOp<ClearColorValue>,
store: StoreOp,
) -> Self {
self.inner.set_color_attachment_image_view(
color_attachment,
image,
image_view_info,
load,
store,
);
self
}
pub fn depth_stencil_attachment_image(
mut self,
image: impl Into<AnyImageNode>,
load: LoadOp<vk::ClearDepthStencilValue>,
store: StoreOp,
) -> Self {
self.inner
.set_depth_stencil_attachment_image(image, load, store);
self
}
pub fn record_cmd(
mut self,
func: impl for<'r> Fn(GraphicsCommandRef<'r>) + Send + Sync + 'static,
) -> Self {
self.record_cmd_mut(func);
self
}
pub fn record_cmd_mut(
&mut self,
func: impl for<'r> Fn(GraphicsCommandRef<'r>) + Send + Sync + 'static,
) {
self.inner.record_stream_mut(func);
}
}
impl StreamPipelineCommand<'_, RayTracingPipeline> {
pub fn record_cmd(
mut self,
func: impl for<'r> Fn(RayTracingCommandRef<'r>) + Send + Sync + 'static,
) -> Self {
self.record_cmd_mut(func);
self
}
pub fn record_cmd_mut(
&mut self,
func: impl for<'r> Fn(RayTracingCommandRef<'r>) + Send + Sync + 'static,
) {
self.inner.record_stream_mut(func);
}
}
impl CommandStreamMut {
pub fn arg<I>(&mut self, info: I) -> I::Arg
where
I: StreamArgInfo,
{
info.bind_stream_arg(self)
}
pub fn begin_cmd(&mut self) -> StreamCommand<'_> {
StreamCommand {
inner: self.graph.begin_cmd(),
}
}
pub fn bind_resource<R>(&mut self, resource: R) -> R::Node
where
R: Resource,
{
self.graph.bind_resource(resource)
}
pub fn resource<N>(&self, resource_node: N) -> &N::Resource
where
N: StreamResourceNode,
{
self.graph.resource(resource_node)
}
pub fn blit_image(
&mut self,
src: impl Into<AnyImageNode>,
dst: impl Into<AnyImageNode>,
filter: vk::Filter,
) -> &mut Self {
self.graph.blit_image(src, dst, filter);
self
}
#[doc(hidden)]
#[deprecated(note = "use Command::blit_image for explicit regions")]
pub fn blit_image_region(
&mut self,
src: impl Into<AnyImageNode>,
dst: impl Into<AnyImageNode>,
filter: vk::Filter,
regions: impl AsRef<[vk::ImageBlit]> + 'static + Send,
) -> &mut Self {
self.graph
.begin_cmd()
.debug_name("blit image")
.blit_image(src, dst, filter, regions)
.end_cmd();
self
}
pub fn clear_color_image(
&mut self,
image: impl Into<AnyImageNode>,
color: impl Into<ClearColorValue>,
) -> &mut Self {
self.graph.clear_color_image(image, color);
self
}
pub fn clear_depth_stencil_image(
&mut self,
image: impl Into<AnyImageNode>,
depth: f32,
stencil: u32,
) -> &mut Self {
self.graph.clear_depth_stencil_image(image, depth, stencil);
self
}
pub fn copy_buffer(
&mut self,
src: impl Into<AnyBufferNode>,
dst: impl Into<AnyBufferNode>,
) -> &mut Self {
self.graph.copy_buffer(src, dst);
self
}
#[doc(hidden)]
#[deprecated(note = "use Command::copy_buffer for explicit regions")]
pub fn copy_buffer_region(
&mut self,
src: impl Into<AnyBufferNode>,
dst: impl Into<AnyBufferNode>,
regions: impl AsRef<[vk::BufferCopy]> + 'static + Send,
) -> &mut Self {
self.graph
.begin_cmd()
.debug_name("copy buffer")
.copy_buffer(src, dst, regions)
.end_cmd();
self
}
pub fn copy_buffer_to_image(
&mut self,
src: impl Into<AnyBufferNode>,
dst: impl Into<AnyImageNode>,
) -> &mut Self {
self.graph.copy_buffer_to_image(src, dst);
self
}
#[doc(hidden)]
#[deprecated(note = "use Command::copy_buffer_to_image for explicit regions")]
pub fn copy_buffer_to_image_region(
&mut self,
src: impl Into<AnyBufferNode>,
dst: impl Into<AnyImageNode>,
regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send,
) -> &mut Self {
self.graph
.begin_cmd()
.debug_name("copy buffer to image")
.copy_buffer_to_image(src, dst, regions)
.end_cmd();
self
}
pub fn copy_image(
&mut self,
src: impl Into<AnyImageNode>,
dst: impl Into<AnyImageNode>,
) -> &mut Self {
self.graph.copy_image(src, dst);
self
}
#[doc(hidden)]
#[deprecated(note = "use Command::copy_image for explicit regions")]
pub fn copy_image_region(
&mut self,
src: impl Into<AnyImageNode>,
dst: impl Into<AnyImageNode>,
regions: impl AsRef<[vk::ImageCopy]> + 'static + Send,
) -> &mut Self {
self.graph
.begin_cmd()
.debug_name("copy image")
.copy_image(src, dst, regions)
.end_cmd();
self
}
pub fn copy_image_to_buffer(
&mut self,
src: impl Into<AnyImageNode>,
dst: impl Into<AnyBufferNode>,
) -> &mut Self {
self.graph.copy_image_to_buffer(src, dst);
self
}
#[doc(hidden)]
#[deprecated(note = "use Command::copy_image_to_buffer for explicit regions")]
pub fn copy_image_to_buffer_region(
&mut self,
src: impl Into<AnyImageNode>,
dst: impl Into<AnyBufferNode>,
regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send,
) -> &mut Self {
self.graph
.begin_cmd()
.debug_name("copy image to buffer")
.copy_image_to_buffer(src, dst, regions)
.end_cmd();
self
}
pub fn fill_buffer(
&mut self,
buffer: impl Into<AnyBufferNode>,
region: Range<vk::DeviceSize>,
data: u32,
) -> &mut Self {
self.graph.fill_buffer(buffer, region, data);
self
}
pub fn update_buffer(
&mut self,
buffer: impl Into<AnyBufferNode>,
offset: vk::DeviceSize,
data: impl AsRef<[u8]> + 'static + Send,
) -> &mut Self {
self.graph.update_buffer(buffer, offset, data);
self
}
fn push_arg(&mut self, data: StreamArgData) -> usize {
let index = self.args.len();
self.args.push(data);
index
}
fn bind_arg_resource(&mut self, data: StreamArgData) -> usize {
let resource = match data {
StreamArgData::AccelerationStructure(info) => {
AnyResource::AccelerationStructureArg(info)
}
StreamArgData::Buffer(info) => AnyResource::BufferArg(info),
StreamArgData::Image(info) => AnyResource::ImageArg(info),
};
self.graph.bind_stream_arg_resource(resource)
}
}
impl CommandStream<()> {
pub fn finalize<A>(build: impl FnOnce(&mut CommandStreamMut) -> A) -> CommandStreamDraft<A> {
let mut stream = CommandStreamMut {
arg_nodes: Vec::new(),
args: Vec::new(),
graph: Graph::new(),
#[cfg(feature = "checked")]
stream_id: CommandStreamId::next(),
};
let args = build(&mut stream);
#[cfg(feature = "checked")]
let graph_id = stream.graph.graph_id();
let submission = stream.graph.finalize();
submission.assert_reusable_commands();
CommandStreamDraft {
args,
inner: CommandStreamInner {
arg_nodes: stream.arg_nodes.into_boxed_slice(),
args: stream.args.into_boxed_slice(),
prepared: false,
submission: Mutex::new(submission),
#[cfg(feature = "checked")]
stream_id: stream.stream_id,
#[cfg(feature = "checked")]
graph_id,
},
}
}
pub fn prepare<P, A>(
pool: &mut P,
build: impl FnOnce(&mut CommandStreamMut) -> A,
) -> Result<CommandStream<A>, DriverError>
where
P: SubmissionPool,
{
Self::finalize(build).prepare(pool)
}
}
impl<A> CommandStreamDraft<A> {
pub fn into_stream(self) -> CommandStream<A> {
CommandStream {
args: self.args,
inner: Arc::new(self.inner),
}
}
pub fn prepare<P>(mut self, pool: &mut P) -> Result<CommandStream<A>, DriverError>
where
P: SubmissionPool,
{
self.inner
.submission
.get_mut()
.expect("poisoned command stream submission")
.prepare_command_stream(pool)?;
self.inner.prepared = true;
Ok(self.into_stream())
}
}
pub struct CommandStreamRun<'a, A> {
pub(crate) bindings: Vec<Option<usize>>,
pub(crate) graph: &'a mut Graph,
pub(crate) stream: &'a CommandStream<A>,
}
impl<'a, A> CommandStreamRun<'a, A> {
pub fn with_arg<T, N>(mut self, arg: StreamArg<T>, node: N) -> Self
where
N: StreamArgBindable<T>,
{
#[cfg(feature = "checked")]
assert!(
arg.stream_id == self.stream.inner.stream_id,
"argument belongs to a different command stream"
);
node.assert_parent_node();
self.graph.assert_node_owner(&node);
self.bindings[arg.arg_index] = Some(node.index());
self
}
pub fn finish(self) -> &'a mut Graph {
#[cfg(feature = "checked")]
assert!(
self.bindings.iter().all(Option::is_some),
"missing command stream argument"
);
self.graph
.append_command_stream(self.stream, &self.bindings);
self.graph
}
}
impl Graph {
pub fn insert_cmd_stream<'a, A>(
&'a mut self,
stream: &'a CommandStream<A>,
) -> CommandStreamRun<'a, A> {
CommandStreamRun {
bindings: vec![None; stream.inner.args.len()],
graph: self,
stream,
}
}
}
impl Graph {
pub(crate) fn append_command_stream<A>(
&mut self,
stream: &CommandStream<A>,
bindings: &[Option<usize>],
) {
if stream.inner.prepared {
self.append_prepared_command_stream(stream, bindings);
} else {
self.append_unprepared_command_stream(stream, bindings);
}
}
fn append_unprepared_command_stream<A>(
&mut self,
stream: &CommandStream<A>,
bindings: &[Option<usize>],
) {
let submission = stream
.inner
.submission
.lock()
.expect("poisoned command stream submission");
let stream_graph = submission.graph();
let mut arg_by_node = HashMap::new();
for (arg_idx, &node_idx) in stream.inner.arg_nodes.iter().enumerate() {
arg_by_node.insert(node_idx, arg_idx);
}
let mut node_map = Vec::with_capacity(stream_graph.resources.len());
for (node_idx, resource) in stream_graph.resources.iter().enumerate() {
if let Some(&arg_idx) = arg_by_node.get(&node_idx) {
node_map.push(bindings[arg_idx].expect("missing command stream argument"));
} else {
node_map.push(self.resources.bind(resource.clone()));
}
}
for cmd in &stream_graph.cmds {
let mut cmd = cmd.clone();
cmd.remap_nodes(&node_map);
#[cfg(feature = "checked")]
for exec in &mut cmd.execs {
exec.stream_graph_id = Some(stream.inner.graph_id);
}
self.cmds.push(cmd);
}
}
fn append_prepared_command_stream<A>(
&mut self,
stream: &CommandStream<A>,
bindings: &[Option<usize>],
) {
let stream_scope_id = next_stream_scope_id();
let submission = stream
.inner
.submission
.lock()
.expect("poisoned command stream submission");
let stream_graph = submission.graph();
let mut arg_by_node = HashMap::new();
for (arg_idx, &node_idx) in stream.inner.arg_nodes.iter().enumerate() {
arg_by_node.insert(node_idx, arg_idx);
}
let mut cmd = self.begin_cmd().debug_name("command stream");
cmd.set_stream_scope_id(stream_scope_id);
for stream_cmd in &stream_graph.cmds {
for (node_idx, accesses) in stream_cmd
.execs
.iter()
.flat_map(|exec| exec.accesses.iter())
{
let Some(&arg_idx) = arg_by_node.get(&node_idx) else {
continue;
};
let parent_node_idx = bindings[arg_idx].expect("missing command stream argument");
for access in accesses {
cmd.push_subresource_access_index(
parent_node_idx,
access.subresource,
access.access,
);
}
}
}
drop(submission);
let stream = Arc::clone(&stream.inner);
let bindings = bindings.to_vec();
cmd.record_stream(move |cmd| {
let submission = stream
.submission
.lock()
.expect("poisoned command stream submission");
let stream_graph = submission.graph();
let mut arg_by_node = HashMap::new();
for (arg_idx, &node_idx) in stream.arg_nodes.iter().enumerate() {
arg_by_node.insert(node_idx, arg_idx);
}
let resources = stream_graph
.resources
.iter()
.enumerate()
.map(|(node_idx, resource)| {
if let Some(&arg_idx) = arg_by_node.get(&node_idx) {
cmd.clone_resource_at(
bindings[arg_idx].expect("missing command stream argument"),
)
} else {
resource.clone()
}
})
.collect();
drop(submission);
stream
.submission
.lock()
.expect("poisoned command stream submission")
.record_prepared_command_stream(&cmd, ResourceMap::from_resources(resources))
.expect("unable to record command stream");
});
}
}
#[allow(private_bounds)]
#[doc(hidden)]
pub trait StreamArgInfo: stream_private::StreamArgInfoSealed {
type Arg;
#[doc(hidden)]
fn bind_stream_arg(self, stream: &mut CommandStreamMut) -> Self::Arg;
}
#[allow(private_bounds)]
#[doc(hidden)]
pub trait StreamArgBindable<T>: stream_private::StreamArgBindableSealed<T> + Node {
#[doc(hidden)]
fn assert_parent_node(&self);
}
#[allow(private_bounds)]
#[doc(hidden)]
pub trait StreamResourceNode: stream_private::StreamResourceNodeSealed + Node {}
mod stream_private {
pub trait StreamArgInfoSealed {}
pub trait StreamArgBindableSealed<T> {}
pub trait StreamResourceNodeSealed {}
pub trait StreamPipelineSealed {}
}
macro_rules! stream_arg_info {
($info:ty, $builder:ty, $variant:ident, $arg:ty) => {
impl stream_private::StreamArgInfoSealed for $info {}
impl StreamArgInfo for $info {
type Arg = $arg;
fn bind_stream_arg(self, stream: &mut CommandStreamMut) -> Self::Arg {
let data = StreamArgData::$variant(self);
let arg_index = stream.push_arg(data);
let node_index = stream.bind_arg_resource(data);
stream.arg_nodes.push(node_index);
StreamArg::new(
arg_index,
node_index,
#[cfg(feature = "checked")]
stream.stream_id,
#[cfg(feature = "checked")]
stream.graph.graph_id(),
)
}
}
impl stream_private::StreamArgInfoSealed for $builder {}
impl StreamArgInfo for $builder {
type Arg = $arg;
fn bind_stream_arg(self, stream: &mut CommandStreamMut) -> Self::Arg {
self.build().bind_stream_arg(stream)
}
}
};
}
stream_arg_info!(
AccelerationStructureInfo,
AccelerationStructureInfoBuilder,
AccelerationStructure,
AccelerationStructureArg
);
stream_arg_info!(BufferInfo, BufferInfoBuilder, Buffer, BufferArg);
stream_arg_info!(ImageInfo, ImageInfoBuilder, Image, ImageArg);
macro_rules! stream_arg_bindable {
($resource:ty => $($node:ty),+ $(,)?) => {
$(
impl stream_private::StreamArgBindableSealed<$resource> for $node {}
impl StreamArgBindable<$resource> for $node {
fn assert_parent_node(&self) {}
}
)+
};
}
stream_arg_bindable!(
AccelerationStructure => AccelerationStructureNode,
AccelerationStructureLeaseNode,
);
stream_arg_bindable!(Buffer => BufferNode, BufferLeaseNode);
stream_arg_bindable!(Image => ImageNode, ImageLeaseNode, SwapchainImageNode);
impl stream_private::StreamArgBindableSealed<AccelerationStructure>
for AnyAccelerationStructureNode
{
}
impl StreamArgBindable<AccelerationStructure> for AnyAccelerationStructureNode {
fn assert_parent_node(&self) {
assert!(
!matches!(self, Self::Arg(_)),
"stream argument cannot be supplied as a parent graph node"
);
}
}
impl stream_private::StreamArgBindableSealed<Buffer> for AnyBufferNode {}
impl StreamArgBindable<Buffer> for AnyBufferNode {
fn assert_parent_node(&self) {
assert!(
!matches!(self, Self::Arg(_)),
"stream argument cannot be supplied as a parent graph node"
);
}
}
impl stream_private::StreamArgBindableSealed<Image> for AnyImageNode {}
impl StreamArgBindable<Image> for AnyImageNode {
fn assert_parent_node(&self) {
assert!(
!matches!(self, Self::Arg(_)),
"stream argument cannot be supplied as a parent graph node"
);
}
}
macro_rules! stream_resource_node {
($($node:ty),+ $(,)?) => {
$(
impl stream_private::StreamResourceNodeSealed for $node {}
impl StreamResourceNode for $node {}
)+
};
}
stream_resource_node!(
AccelerationStructureNode,
AccelerationStructureLeaseNode,
BufferNode,
BufferLeaseNode,
ImageNode,
ImageLeaseNode,
SwapchainImageNode,
);
#[cfg(test)]
mod tests {
use super::*;
use crate::{
driver::{
buffer::BufferInfo,
descriptor_set::{DescriptorPool, DescriptorPoolInfo},
render_pass::{RenderPass, RenderPassInfo},
},
pool::Pool,
};
struct NoopPool;
impl Pool<DescriptorPoolInfo, DescriptorPool> for NoopPool {
fn resource(
&mut self,
_: DescriptorPoolInfo,
) -> Result<crate::pool::Lease<DescriptorPool>, DriverError> {
unreachable!()
}
}
impl Pool<RenderPassInfo, RenderPass> for NoopPool {
fn resource(
&mut self,
_: RenderPassInfo,
) -> Result<crate::pool::Lease<RenderPass>, DriverError> {
unreachable!()
}
}
#[test]
fn empty_stream_can_be_inserted() {
let stream = CommandStream::finalize(|_| {}).into_stream();
let mut graph = Graph::new();
graph.insert_cmd_stream(&stream).finish();
}
#[test]
fn reusable_callback_can_prepare_stream() {
let stream = CommandStream::finalize(|stream| {
stream.begin_cmd().record_cmd(|_| {});
})
.into_stream();
let mut graph = Graph::new();
graph.insert_cmd_stream(&stream).finish();
assert_eq!(graph.cmds.len(), 1);
}
#[test]
fn graph_copy_wrapper_can_prepare_stream() {
let _stream = CommandStream::finalize(|stream| {
let src = stream.arg(BufferInfo::device_mem(
4,
vk::BufferUsageFlags::TRANSFER_SRC,
));
let dst = stream.arg(BufferInfo::device_mem(
4,
vk::BufferUsageFlags::TRANSFER_DST,
));
stream.graph.copy_buffer(src, dst);
})
.into_stream();
}
#[test]
fn reusable_callback_can_prepare_optimized_stream() {
let mut pool = NoopPool;
let stream = CommandStream::prepare(&mut pool, |stream| {
stream.begin_cmd().record_cmd(|_| {});
})
.expect("prepare stream");
let mut graph = Graph::new();
graph.insert_cmd_stream(&stream).finish();
assert_eq!(graph.cmds.len(), 1);
}
#[test]
fn unprepared_stream_expands_commands() {
let stream = CommandStream::finalize(|stream| {
stream.begin_cmd().record_cmd(|_| {});
stream.begin_cmd().record_cmd(|_| {});
})
.into_stream();
let mut graph = Graph::new();
graph.insert_cmd_stream(&stream).finish();
assert_eq!(graph.cmds.len(), 2);
}
#[test]
fn prepared_stream_is_opaque_by_default() {
let mut pool = NoopPool;
let stream = CommandStream::prepare(&mut pool, |stream| {
stream.begin_cmd().record_cmd(|_| {});
stream.begin_cmd().record_cmd(|_| {});
})
.expect("prepare stream");
let mut graph = Graph::new();
graph.insert_cmd_stream(&stream).finish();
assert_eq!(graph.cmds.len(), 1);
}
#[test]
fn image_arg_can_use_info_based_helpers() {
let stream = CommandStream::finalize(|stream| {
let output = stream.arg(ImageInfo::image_2d(
1,
1,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::TRANSFER_DST,
));
stream.clear_color_image(output, [0.0, 0.0, 0.0, 0.0]);
output
})
.into_stream();
assert_eq!(stream.inner.args.len(), 1);
}
#[test]
#[should_panic(expected = "missing command stream argument")]
fn missing_arg_panics_at_finish() {
let stream = CommandStream::finalize(|stream| {
stream.arg(ImageInfo::image_2d(
1,
1,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::SAMPLED,
));
})
.into_stream();
let mut graph = Graph::new();
graph.insert_cmd_stream(&stream).finish();
}
#[test]
#[cfg(feature = "checked")]
#[should_panic(expected = "argument belongs to a different command stream")]
fn wrong_stream_arg_panics_at_with_arg() {
let stream_a = CommandStream::finalize(|stream| {
stream.arg(ImageInfo::image_2d(
1,
1,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::SAMPLED,
))
})
.into_stream();
let stream_b = CommandStream::finalize(|stream| {
stream.arg(ImageInfo::image_2d(
1,
1,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::SAMPLED,
))
})
.into_stream();
let mut graph = Graph::new();
graph
.insert_cmd_stream(&stream_a)
.with_arg(stream_b.args, AnyImageNode::from(stream_b.args))
.finish();
}
#[test]
#[should_panic(expected = "stream argument cannot be supplied as a parent graph node")]
fn stream_arg_cannot_bind_as_parent_graph_node() {
let stream = CommandStream::finalize(|stream| {
stream.arg(ImageInfo::image_2d(
1,
1,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::SAMPLED,
))
})
.into_stream();
let mut graph = Graph::new();
graph
.insert_cmd_stream(&stream)
.with_arg(stream.args, AnyImageNode::from(stream.args))
.finish();
}
#[test]
#[should_panic(expected = "command stream contains a one-shot callback")]
fn one_shot_callback_cannot_prepare_stream() {
let _ = CommandStream::finalize(|stream| {
stream.graph.begin_cmd().record_cmd(|_| {});
});
}
}