#[macro_export]
macro_rules! binding_layout {
($($loc:tt => $fmt:tt,)*) => {
binding_layout!([] ; None; $($loc => $fmt ,)*)
};
([$($t:expr,)*] ; $name:expr ; $vis:tt => {
$($loc:expr =>
$binding:tt$(<$generic:ident>)?$(:$($trait1:ident$(<$tgen1:ident>)?)? $(+$trait2:tt$(<$tgen2:ident>)?)?)?,)*
}, $($ll:tt => $ii:tt,)*) => {
$crate::binding_layout!(
[$($t,)* $(
wgpu::BindGroupLayoutEntry {
binding: $loc,
visibility: $crate::vis!($vis),
ty: $crate::generics!($binding ; $($generic)? ; $($($trait1)?)? ; $($($($tgen1)?)?)? ; $($($trait2)?)? ; $($($($tgen2)?)?)? ),
},)*] ;
$name;
$($ll => $ii,)*
);
};
([$($t:expr,)*] ; $old_name:expr ; Label => $name:expr, $($ll:tt => $ii:tt,)*) => {
binding_layout!([$($t,)*] ; Some($name) ; $($ll => $ii,)*)
};
([$($t:expr,)*] ; $name:expr ; ) => { wgpu::BindGroupLayoutDescriptor { label: $name, bindings: &[$($t,)*] } };
}
#[macro_export]
macro_rules! only_traits {
(Buffer ; ; ) => {
wgpu::BindingType::UniformBuffer { dynamic: false }
};
(Buffer ; Dyn ; ) => {
wgpu::BindingType::UniformBuffer { dynamic: true }
};
(Sampler ; ; ) => {
wgpu::BindingType::Sampler { comparison: false }
};
(Sampler ; Cmp ; ) => {
wgpu::BindingType::Sampler { comparison: true }
};
(StorageBuffer ; ; ) => {
wgpu::BindingType::StorageBuffer {
dynamic: false,
readonly: false,
}
};
(StorageBuffer ; Dyn ; ) => {
wgpu::BindingType::StorageBuffer {
dynamic: true,
readonly: false,
}
};
(StorageBuffer ; Readonly ; ) => {
wgpu::BindingType::StorageBuffer {
dynamic: false,
Readonly: true,
}
};
(StorageBuffer ; Readonly ; Dyn) => {
wgpu::BindingType::StorageBuffer {
dynamic: true,
readonly: true,
}
};
(StorageBuffer ; Dyn ; Readonly) => {
wgpu::BindingType::StorageBuffer {
dynamic: true,
readonly: true,
}
};
}
#[macro_export]
macro_rules! generics {
( $texType:ident ; ; $($trait1:ident)? ; ; $($trait2:ident)? ; ) => {
$crate::only_traits!($texType ; $($trait1)? ; $($trait2)? )
};
( $texType:ident ; $fmt:ident ; ; ; ; ) => {
wgpu::BindingType::SampledTexture {
dimension: $crate::d!($texType).0,
multisampled: $crate::d!($texType).1,
component_type: wgpu::TextureComponentType::$fmt,
}
};
( $texType:ident ; $fmt:ident ; Storage ; $strgfmt:ident ; ; ) => {
wgpu::BindingType::StorageTexture {
dimension: $crate::d!($texType).0,
component_type: wgpu::TextureComponentType::$fmt,
format: wgpu::TextureFormat::$strgfmt,
readonly: false,
}
};
( $texType:ident ; $fmt:ident ; Storage ; $strgfmt:ident ; Readonly ; ) => {
wgpu::BindingType::StorageTexture {
dimension: $crate::d!($texType).0,
component_type: wgpu::TextureComponentType::$fmt,
format: wgpu::TextureFormat::$strgfmt,
readonly: true,
}
};
( $texType:ident ; $fmt:ident ; Readonly ; ; Storage ; $strgfmt:ident) => {
wgpu::BindingType::StorageTexture {
dimension: $crate::d!($texType).0,
component_type: wgpu::TextureComponentType::$fmt,
format: wgpu::TextureFormat::$strgfmt,
readonly: true,
}
};
}
#[macro_export]
macro_rules! d {
(Tex1D) => {
(wgpu::TextureViewDimension::D1, false)
};
(Tex1DMS) => {
(wgpu::TextureViewDimension::D1, true)
};
(Tex2D) => {
(wgpu::TextureViewDimension::D2, false)
};
(Tex2DMS) => {
(wgpu::TextureViewDimension::D2, true)
};
(Tex3D) => {
(wgpu::TextureViewDimension::D3, false)
};
(Tex3DMS) => {
(wgpu::TextureViewDimension::D3, true)
};
(Tex2DArray) => {
(wgpu::TextureViewDimension::D2Array, false)
};
(Tex2DArrayMs) => {
(wgpu::TextureViewDimension::D2Array, true)
};
(TexCube) => {
(wgpu::TextureViewDimension::Cube, false)
};
(TexCubeMS) => {
(wgpu::TextureViewDimension::CubeArray, false)
};
(TexCubeArrayMs) => {
(wgpu::TextureViewDimension::CubeArray, true)
};
}
#[macro_export]
macro_rules! vis {
( Vertex ) => {
wgpu::ShaderStage::VERTEX
};
( Fragment ) => {
wgpu::ShaderStage::FRAGMENT
};
( Compute ) => {
wgpu::ShaderStage::COMPUTE
};
( None ) => {
wgpu::ShaderStage::NONE
};
( { $id:ident | $rest:ident }) => {
$crate::vis!($id) | $crate::vis!($rest)
};
( { $id:ident | $rest:ident | $more:ident }) => {
$crate::vis!($id) | $crate::vis!($rest) | $crate::vis!($more)
};
}
#[cfg(test)]
mod test {
use wgpu;
#[test]
fn buffers_and_names() {
let a = binding_layout! {
Label => "named",
{ Compute | Fragment } => { 1 => Buffer, },
};
assert_eq!(a.label, Some("named"));
assert_eq!(a.bindings[0].binding, 1);
assert_eq!(
a.bindings[0].ty,
wgpu::BindingType::UniformBuffer { dynamic: false }
);
assert_eq!(
a.bindings[0].visibility,
wgpu::ShaderStage::FRAGMENT | wgpu::ShaderStage::COMPUTE
);
}
#[test]
fn buffer_types() {
let a = binding_layout! {
Label => "named",
{ Compute | Fragment } => {
1 => Buffer: Dyn,
},
};
assert_eq!(a.label, Some("named"));
assert_eq!(a.bindings[0].binding, 1);
assert_eq!(
a.bindings[0].ty,
wgpu::BindingType::UniformBuffer { dynamic: true }
);
assert_eq!(
a.bindings[0].visibility,
wgpu::ShaderStage::FRAGMENT | wgpu::ShaderStage::COMPUTE
);
}
#[test]
fn templates() {
let a = binding_layout! {
Label => "named",
{ Compute | Fragment } => {
2 => Tex1D<Float>: Storage<R8Unorm> + Readonly,
4 => Buffer,
},
};
assert_eq!(a.label, Some("named"));
assert_eq!(a.bindings[0].binding, 2);
assert_eq!(
a.bindings[0].ty,
wgpu::BindingType::StorageTexture {
dimension: wgpu::TextureViewDimension::D1,
component_type: wgpu::TextureComponentType::Float,
readonly: true,
format: wgpu::TextureFormat::R8Unorm,
}
);
assert_eq!(
a.bindings[0].visibility,
wgpu::ShaderStage::FRAGMENT | wgpu::ShaderStage::COMPUTE
);
}
#[test]
fn long() {
let a = binding_layout! {
{ Vertex | Fragment } => {
0 => Tex1D<Float>: Storage<R8Unorm> + Readonly,
1 => Tex2D<Sint>: Readonly + Storage<Rgba32Uint>,
2 => StorageBuffer: Dyn,
3 => StorageBuffer,
4 => Buffer: Dyn,
5 => Buffer,
},
};
let b = &[
wgpu::BindGroupLayoutEntry {
ty: wgpu::BindingType::StorageTexture {
dimension: wgpu::TextureViewDimension::D1,
component_type: wgpu::TextureComponentType::Float,
readonly: true,
format: wgpu::TextureFormat::R8Unorm,
},
binding: 0,
visibility: wgpu::ShaderStage::FRAGMENT | wgpu::ShaderStage::VERTEX,
},
wgpu::BindGroupLayoutEntry {
ty: wgpu::BindingType::StorageTexture {
dimension: wgpu::TextureViewDimension::D2,
component_type: wgpu::TextureComponentType::Sint,
readonly: true,
format: wgpu::TextureFormat::Rgba32Uint,
},
binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT | wgpu::ShaderStage::VERTEX,
},
wgpu::BindGroupLayoutEntry {
ty: wgpu::BindingType::StorageBuffer {
dynamic: true,
readonly: false,
},
binding: 2,
visibility: wgpu::ShaderStage::FRAGMENT | wgpu::ShaderStage::VERTEX,
},
wgpu::BindGroupLayoutEntry {
ty: wgpu::BindingType::StorageBuffer {
dynamic: false,
readonly: false,
},
binding: 3,
visibility: wgpu::ShaderStage::FRAGMENT | wgpu::ShaderStage::VERTEX,
},
wgpu::BindGroupLayoutEntry {
ty: wgpu::BindingType::UniformBuffer { dynamic: true },
binding: 4,
visibility: wgpu::ShaderStage::FRAGMENT | wgpu::ShaderStage::VERTEX,
},
wgpu::BindGroupLayoutEntry {
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
binding: 5,
visibility: wgpu::ShaderStage::FRAGMENT | wgpu::ShaderStage::VERTEX,
},
];
assert_eq!(a.label, None);
a.bindings.iter().zip(b.iter()).for_each(|(a, b)| {
assert_eq!(a.ty, b.ty);
assert_eq!(a.binding, b.binding);
assert_eq!(a.visibility, b.visibility);
});
}
}