use crate::{
descriptor_set::layout::DescriptorType,
device::{Device, DeviceOwned},
format::{Format, NumericType},
image::view::ImageViewType,
macros::{vulkan_bitflags, vulkan_enum},
pipeline::{graphics::input_assembly::PrimitiveTopology, layout::PushConstantRange},
shader::spirv::{Capability, Spirv, SpirvError},
sync::PipelineStages,
DeviceSize, OomError, Version, VulkanError, VulkanObject,
};
use ahash::{HashMap, HashSet};
use std::{
borrow::Cow,
error::Error,
ffi::{CStr, CString},
fmt::{Display, Error as FmtError, Formatter},
mem,
mem::MaybeUninit,
num::NonZeroU64,
ptr,
sync::Arc,
};
pub mod reflect;
pub mod spirv;
use spirv::ExecutionModel;
include!(concat!(env!("OUT_DIR"), "/spirv_reqs.rs"));
#[derive(Debug)]
pub struct ShaderModule {
handle: ash::vk::ShaderModule,
device: Arc<Device>,
id: NonZeroU64,
entry_points: HashMap<String, HashMap<ExecutionModel, EntryPointInfo>>,
}
impl ShaderModule {
#[inline]
pub unsafe fn from_words(
device: Arc<Device>,
words: &[u32],
) -> Result<Arc<ShaderModule>, ShaderCreationError> {
let spirv = Spirv::new(words)?;
Self::from_words_with_data(
device,
words,
spirv.version(),
reflect::spirv_capabilities(&spirv),
reflect::spirv_extensions(&spirv),
reflect::entry_points(&spirv),
)
}
#[inline]
pub unsafe fn from_bytes(
device: Arc<Device>,
bytes: &[u8],
) -> Result<Arc<ShaderModule>, ShaderCreationError> {
assert!((bytes.len() % 4) == 0);
Self::from_words(
device,
std::slice::from_raw_parts(
bytes.as_ptr() as *const _,
bytes.len() / mem::size_of::<u32>(),
),
)
}
pub unsafe fn from_words_with_data<'a>(
device: Arc<Device>,
words: &[u32],
spirv_version: Version,
spirv_capabilities: impl IntoIterator<Item = &'a Capability>,
spirv_extensions: impl IntoIterator<Item = &'a str>,
entry_points: impl IntoIterator<Item = (String, ExecutionModel, EntryPointInfo)>,
) -> Result<Arc<ShaderModule>, ShaderCreationError> {
if let Err(reason) = check_spirv_version(&device, spirv_version) {
return Err(ShaderCreationError::SpirvVersionNotSupported {
version: spirv_version,
reason,
});
}
for &capability in spirv_capabilities {
if let Err(reason) = check_spirv_capability(&device, capability) {
return Err(ShaderCreationError::SpirvCapabilityNotSupported {
capability,
reason,
});
}
}
for extension in spirv_extensions {
if let Err(reason) = check_spirv_extension(&device, extension) {
return Err(ShaderCreationError::SpirvExtensionNotSupported {
extension: extension.to_owned(),
reason,
});
}
}
let handle = {
let infos = ash::vk::ShaderModuleCreateInfo {
flags: ash::vk::ShaderModuleCreateFlags::empty(),
code_size: words.len() * mem::size_of::<u32>(),
p_code: words.as_ptr(),
..Default::default()
};
let fns = device.fns();
let mut output = MaybeUninit::uninit();
(fns.v1_0.create_shader_module)(
device.handle(),
&infos,
ptr::null(),
output.as_mut_ptr(),
)
.result()
.map_err(VulkanError::from)?;
output.assume_init()
};
let entries = entry_points.into_iter().collect::<Vec<_>>();
let entry_points = entries
.iter()
.map(|(name, _, _)| name)
.collect::<HashSet<_>>()
.iter()
.map(|name| {
(
(*name).clone(),
entries
.iter()
.filter_map(|(entry_name, entry_model, info)| {
if &entry_name == name {
Some((*entry_model, info.clone()))
} else {
None
}
})
.collect::<HashMap<_, _>>(),
)
})
.collect();
Ok(Arc::new(ShaderModule {
handle,
device,
id: Self::next_id(),
entry_points,
}))
}
pub unsafe fn from_bytes_with_data<'a>(
device: Arc<Device>,
bytes: &[u8],
spirv_version: Version,
spirv_capabilities: impl IntoIterator<Item = &'a Capability>,
spirv_extensions: impl IntoIterator<Item = &'a str>,
entry_points: impl IntoIterator<Item = (String, ExecutionModel, EntryPointInfo)>,
) -> Result<Arc<ShaderModule>, ShaderCreationError> {
assert!((bytes.len() % 4) == 0);
Self::from_words_with_data(
device,
std::slice::from_raw_parts(
bytes.as_ptr() as *const _,
bytes.len() / mem::size_of::<u32>(),
),
spirv_version,
spirv_capabilities,
spirv_extensions,
entry_points,
)
}
#[inline]
pub fn entry_point<'a>(&'a self, name: &str) -> Option<EntryPoint<'a>> {
self.entry_points.get(name).and_then(|infos| {
if infos.len() == 1 {
infos.iter().next().map(|(_, info)| EntryPoint {
module: self,
name: CString::new(name).unwrap(),
info,
})
} else {
None
}
})
}
#[inline]
pub fn entry_point_with_execution<'a>(
&'a self,
name: &str,
execution: ExecutionModel,
) -> Option<EntryPoint<'a>> {
self.entry_points.get(name).and_then(|infos| {
infos.get(&execution).map(|info| EntryPoint {
module: self,
name: CString::new(name).unwrap(),
info,
})
})
}
}
impl Drop for ShaderModule {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.device.fns();
(fns.v1_0.destroy_shader_module)(self.device.handle(), self.handle, ptr::null());
}
}
}
unsafe impl VulkanObject for ShaderModule {
type Handle = ash::vk::ShaderModule;
#[inline]
fn handle(&self) -> Self::Handle {
self.handle
}
}
unsafe impl DeviceOwned for ShaderModule {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
crate::impl_id_counter!(ShaderModule);
#[derive(Clone, Debug)]
pub enum ShaderCreationError {
OomError(OomError),
SpirvCapabilityNotSupported {
capability: Capability,
reason: ShaderSupportError,
},
SpirvError(SpirvError),
SpirvExtensionNotSupported {
extension: String,
reason: ShaderSupportError,
},
SpirvVersionNotSupported {
version: Version,
reason: ShaderSupportError,
},
}
impl Error for ShaderCreationError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::OomError(err) => Some(err),
Self::SpirvCapabilityNotSupported { reason, .. } => Some(reason),
Self::SpirvError(err) => Some(err),
Self::SpirvExtensionNotSupported { reason, .. } => Some(reason),
Self::SpirvVersionNotSupported { reason, .. } => Some(reason),
}
}
}
impl Display for ShaderCreationError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::OomError(_) => write!(f, "not enough memory available"),
Self::SpirvCapabilityNotSupported { capability, .. } => write!(
f,
"the SPIR-V capability {:?} enabled by the shader is not supported by the device",
capability,
),
Self::SpirvError(_) => write!(f, "the SPIR-V module could not be read"),
Self::SpirvExtensionNotSupported { extension, .. } => write!(
f,
"the SPIR-V extension {} enabled by the shader is not supported by the device",
extension,
),
Self::SpirvVersionNotSupported { version, .. } => write!(
f,
"the shader uses SPIR-V version {}.{}, which is not supported by the device",
version.major, version.minor,
),
}
}
}
impl From<VulkanError> for ShaderCreationError {
fn from(err: VulkanError) -> Self {
Self::OomError(err.into())
}
}
impl From<SpirvError> for ShaderCreationError {
fn from(err: SpirvError) -> Self {
Self::SpirvError(err)
}
}
#[derive(Clone, Copy, Debug)]
pub enum ShaderSupportError {
NotSupportedByVulkan,
RequirementsNotMet(&'static [&'static str]),
}
impl Error for ShaderSupportError {}
impl Display for ShaderSupportError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::NotSupportedByVulkan => write!(f, "not supported by Vulkan"),
Self::RequirementsNotMet(requirements) => write!(
f,
"at least one of the following must be available/enabled on the device: {}",
requirements.join(", "),
),
}
}
}
#[derive(Clone, Debug)]
pub struct EntryPointInfo {
pub execution: ShaderExecution,
pub descriptor_requirements: HashMap<(u32, u32), DescriptorRequirements>,
pub push_constant_requirements: Option<PushConstantRange>,
pub specialization_constant_requirements: HashMap<u32, SpecializationConstantRequirements>,
pub input_interface: ShaderInterface,
pub output_interface: ShaderInterface,
}
#[derive(Clone, Debug)]
pub struct EntryPoint<'a> {
module: &'a ShaderModule,
name: CString,
info: &'a EntryPointInfo,
}
impl<'a> EntryPoint<'a> {
#[inline]
pub fn module(&self) -> &'a ShaderModule {
self.module
}
#[inline]
pub fn name(&self) -> &CStr {
&self.name
}
#[inline]
pub fn execution(&self) -> &ShaderExecution {
&self.info.execution
}
#[inline]
pub fn descriptor_requirements(
&self,
) -> impl ExactSizeIterator<Item = ((u32, u32), &DescriptorRequirements)> {
self.info
.descriptor_requirements
.iter()
.map(|(k, v)| (*k, v))
}
#[inline]
pub fn push_constant_requirements(&self) -> Option<&PushConstantRange> {
self.info.push_constant_requirements.as_ref()
}
#[inline]
pub fn specialization_constant_requirements(
&self,
) -> impl ExactSizeIterator<Item = (u32, &SpecializationConstantRequirements)> {
self.info
.specialization_constant_requirements
.iter()
.map(|(k, v)| (*k, v))
}
#[inline]
pub fn input_interface(&self) -> &ShaderInterface {
&self.info.input_interface
}
#[inline]
pub fn output_interface(&self) -> &ShaderInterface {
&self.info.output_interface
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ShaderExecution {
Vertex,
TessellationControl,
TessellationEvaluation,
Geometry(GeometryShaderExecution),
Fragment,
Compute,
RayGeneration,
AnyHit,
ClosestHit,
Miss,
Intersection,
Callable,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct GeometryShaderExecution {
pub input: GeometryShaderInput,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum GeometryShaderInput {
Points,
Lines,
LinesWithAdjacency,
Triangles,
TrianglesWithAdjacency,
}
impl GeometryShaderInput {
#[inline]
pub fn is_compatible_with(&self, topology: PrimitiveTopology) -> bool {
match self {
Self::Points => matches!(topology, PrimitiveTopology::PointList),
Self::Lines => matches!(
topology,
PrimitiveTopology::LineList | PrimitiveTopology::LineStrip
),
Self::LinesWithAdjacency => matches!(
topology,
PrimitiveTopology::LineListWithAdjacency
| PrimitiveTopology::LineStripWithAdjacency
),
Self::Triangles => matches!(
topology,
PrimitiveTopology::TriangleList
| PrimitiveTopology::TriangleStrip
| PrimitiveTopology::TriangleFan,
),
Self::TrianglesWithAdjacency => matches!(
topology,
PrimitiveTopology::TriangleListWithAdjacency
| PrimitiveTopology::TriangleStripWithAdjacency,
),
}
}
}
#[derive(Clone, Debug, Default)]
pub struct DescriptorRequirements {
pub descriptor_types: Vec<DescriptorType>,
pub descriptor_count: Option<u32>,
pub image_format: Option<Format>,
pub image_multisampled: bool,
pub image_scalar_type: Option<ShaderScalarType>,
pub image_view_type: Option<ImageViewType>,
pub sampler_compare: HashSet<u32>,
pub sampler_no_unnormalized_coordinates: HashSet<u32>,
pub sampler_no_ycbcr_conversion: HashSet<u32>,
pub sampler_with_images: HashMap<u32, HashSet<DescriptorIdentifier>>,
pub stages: ShaderStages,
pub storage_image_atomic: HashSet<u32>,
pub storage_read: HashSet<u32>,
pub storage_write: HashSet<u32>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct DescriptorIdentifier {
pub set: u32,
pub binding: u32,
pub index: u32,
}
impl DescriptorRequirements {
#[inline]
pub fn intersection(&self, other: &Self) -> Result<Self, DescriptorRequirementsIncompatible> {
let descriptor_types: Vec<_> = self
.descriptor_types
.iter()
.copied()
.filter(|ty| other.descriptor_types.contains(ty))
.collect();
if descriptor_types.is_empty() {
return Err(DescriptorRequirementsIncompatible::DescriptorType);
}
if let (Some(first), Some(second)) = (self.image_format, other.image_format) {
if first != second {
return Err(DescriptorRequirementsIncompatible::ImageFormat);
}
}
if let (Some(first), Some(second)) = (self.image_scalar_type, other.image_scalar_type) {
if first != second {
return Err(DescriptorRequirementsIncompatible::ImageScalarType);
}
}
if let (Some(first), Some(second)) = (self.image_view_type, other.image_view_type) {
if first != second {
return Err(DescriptorRequirementsIncompatible::ImageViewType);
}
}
if self.image_multisampled != other.image_multisampled {
return Err(DescriptorRequirementsIncompatible::ImageMultisampled);
}
let sampler_with_images = {
let mut result = self.sampler_with_images.clone();
for (&index, other_identifiers) in &other.sampler_with_images {
result.entry(index).or_default().extend(other_identifiers);
}
result
};
Ok(Self {
descriptor_types,
descriptor_count: self.descriptor_count.max(other.descriptor_count),
image_format: self.image_format.or(other.image_format),
image_multisampled: self.image_multisampled,
image_scalar_type: self.image_scalar_type.or(other.image_scalar_type),
image_view_type: self.image_view_type.or(other.image_view_type),
sampler_compare: &self.sampler_compare | &other.sampler_compare,
sampler_no_unnormalized_coordinates: &self.sampler_no_unnormalized_coordinates
| &other.sampler_no_unnormalized_coordinates,
sampler_no_ycbcr_conversion: &self.sampler_no_ycbcr_conversion
| &other.sampler_no_ycbcr_conversion,
sampler_with_images,
stages: self.stages | other.stages,
storage_image_atomic: &self.storage_image_atomic | &other.storage_image_atomic,
storage_read: &self.storage_read | &other.storage_read,
storage_write: &self.storage_write | &other.storage_write,
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DescriptorRequirementsIncompatible {
DescriptorType,
ImageFormat,
ImageScalarType,
ImageMultisampled,
ImageViewType,
}
impl Error for DescriptorRequirementsIncompatible {}
impl Display for DescriptorRequirementsIncompatible {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
DescriptorRequirementsIncompatible::DescriptorType => write!(
f,
"the allowed descriptor types of the two descriptors do not overlap",
),
DescriptorRequirementsIncompatible::ImageFormat => {
write!(f, "the descriptors require different formats",)
}
DescriptorRequirementsIncompatible::ImageMultisampled => write!(
f,
"the multisampling requirements of the descriptors differ",
),
DescriptorRequirementsIncompatible::ImageScalarType => {
write!(f, "the descriptors require different scalar types",)
}
DescriptorRequirementsIncompatible::ImageViewType => {
write!(f, "the descriptors require different image view types",)
}
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct SpecializationConstantRequirements {
pub size: DeviceSize,
}
pub unsafe trait SpecializationConstants {
fn descriptors() -> &'static [SpecializationMapEntry];
}
unsafe impl SpecializationConstants for () {
#[inline]
fn descriptors() -> &'static [SpecializationMapEntry] {
&[]
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct SpecializationMapEntry {
pub constant_id: u32,
pub offset: u32,
pub size: usize,
}
impl From<SpecializationMapEntry> for ash::vk::SpecializationMapEntry {
#[inline]
fn from(val: SpecializationMapEntry) -> Self {
Self {
constant_id: val.constant_id,
offset: val.offset,
size: val.size,
}
}
}
#[derive(Clone, Debug)]
pub struct ShaderInterface {
elements: Vec<ShaderInterfaceEntry>,
}
impl ShaderInterface {
#[inline]
pub unsafe fn new_unchecked(elements: Vec<ShaderInterfaceEntry>) -> ShaderInterface {
ShaderInterface { elements }
}
#[inline]
pub const fn empty() -> ShaderInterface {
ShaderInterface {
elements: Vec::new(),
}
}
#[inline]
pub fn elements(&self) -> &[ShaderInterfaceEntry] {
self.elements.as_ref()
}
#[inline]
pub fn matches(&self, other: &ShaderInterface) -> Result<(), ShaderInterfaceMismatchError> {
if self.elements().len() != other.elements().len() {
return Err(ShaderInterfaceMismatchError::ElementsCountMismatch {
self_elements: self.elements().len() as u32,
other_elements: other.elements().len() as u32,
});
}
for a in self.elements() {
let location_range = a.location..a.location + a.ty.num_locations();
for loc in location_range {
let b = match other
.elements()
.iter()
.find(|e| loc >= e.location && loc < e.location + e.ty.num_locations())
{
None => {
return Err(ShaderInterfaceMismatchError::MissingElement { location: loc })
}
Some(b) => b,
};
if a.ty != b.ty {
return Err(ShaderInterfaceMismatchError::TypeMismatch {
location: loc,
self_ty: a.ty,
other_ty: b.ty,
});
}
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct ShaderInterfaceEntry {
pub location: u32,
pub component: u32,
pub name: Option<Cow<'static, str>>,
pub ty: ShaderInterfaceEntryType,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ShaderInterfaceEntryType {
pub base_type: ShaderScalarType,
pub num_components: u32,
pub num_elements: u32,
pub is_64bit: bool,
}
impl ShaderInterfaceEntryType {
pub(crate) fn to_format(&self) -> Format {
assert!(!self.is_64bit); match self.base_type {
ShaderScalarType::Float => match self.num_components {
1 => Format::R32_SFLOAT,
2 => Format::R32G32_SFLOAT,
3 => Format::R32G32B32_SFLOAT,
4 => Format::R32G32B32A32_SFLOAT,
_ => unreachable!(),
},
ShaderScalarType::Sint => match self.num_components {
1 => Format::R32_SINT,
2 => Format::R32G32_SINT,
3 => Format::R32G32B32_SINT,
4 => Format::R32G32B32A32_SINT,
_ => unreachable!(),
},
ShaderScalarType::Uint => match self.num_components {
1 => Format::R32_UINT,
2 => Format::R32G32_UINT,
3 => Format::R32G32B32_UINT,
4 => Format::R32G32B32A32_UINT,
_ => unreachable!(),
},
}
}
pub(crate) fn num_locations(&self) -> u32 {
assert!(!self.is_64bit); self.num_elements
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ShaderScalarType {
Float,
Sint,
Uint,
}
impl From<NumericType> for ShaderScalarType {
#[inline]
fn from(val: NumericType) -> Self {
match val {
NumericType::SFLOAT => Self::Float,
NumericType::UFLOAT => Self::Float,
NumericType::SINT => Self::Sint,
NumericType::UINT => Self::Uint,
NumericType::SNORM => Self::Float,
NumericType::UNORM => Self::Float,
NumericType::SSCALED => Self::Float,
NumericType::USCALED => Self::Float,
NumericType::SRGB => Self::Float,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ShaderInterfaceMismatchError {
ElementsCountMismatch {
self_elements: u32,
other_elements: u32,
},
MissingElement {
location: u32,
},
TypeMismatch {
location: u32,
self_ty: ShaderInterfaceEntryType,
other_ty: ShaderInterfaceEntryType,
},
}
impl Error for ShaderInterfaceMismatchError {}
impl Display for ShaderInterfaceMismatchError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
write!(
f,
"{}",
match self {
ShaderInterfaceMismatchError::ElementsCountMismatch { .. } => {
"the number of elements mismatches"
}
ShaderInterfaceMismatchError::MissingElement { .. } => "an element is missing",
ShaderInterfaceMismatchError::TypeMismatch { .. } => {
"the type of an element does not match"
}
}
)
}
}
vulkan_enum! {
#[non_exhaustive]
ShaderStage = ShaderStageFlags(u32);
Vertex = VERTEX,
TessellationControl = TESSELLATION_CONTROL,
TessellationEvaluation = TESSELLATION_EVALUATION,
Geometry = GEOMETRY,
Fragment = FRAGMENT,
Compute = COMPUTE,
Raygen = RAYGEN_KHR {
device_extensions: [khr_ray_tracing_pipeline, nv_ray_tracing],
},
AnyHit = ANY_HIT_KHR {
device_extensions: [khr_ray_tracing_pipeline, nv_ray_tracing],
},
ClosestHit = CLOSEST_HIT_KHR {
device_extensions: [khr_ray_tracing_pipeline, nv_ray_tracing],
},
Miss = MISS_KHR {
device_extensions: [khr_ray_tracing_pipeline, nv_ray_tracing],
},
Intersection = INTERSECTION_KHR {
device_extensions: [khr_ray_tracing_pipeline, nv_ray_tracing],
},
Callable = CALLABLE_KHR {
device_extensions: [khr_ray_tracing_pipeline, nv_ray_tracing],
},
}
impl From<ShaderExecution> for ShaderStage {
#[inline]
fn from(val: ShaderExecution) -> Self {
match val {
ShaderExecution::Vertex => Self::Vertex,
ShaderExecution::TessellationControl => Self::TessellationControl,
ShaderExecution::TessellationEvaluation => Self::TessellationEvaluation,
ShaderExecution::Geometry(_) => Self::Geometry,
ShaderExecution::Fragment => Self::Fragment,
ShaderExecution::Compute => Self::Compute,
ShaderExecution::RayGeneration => Self::Raygen,
ShaderExecution::AnyHit => Self::AnyHit,
ShaderExecution::ClosestHit => Self::ClosestHit,
ShaderExecution::Miss => Self::Miss,
ShaderExecution::Intersection => Self::Intersection,
ShaderExecution::Callable => Self::Callable,
}
}
}
impl From<ShaderStage> for ShaderStages {
#[inline]
fn from(val: ShaderStage) -> Self {
match val {
ShaderStage::Vertex => Self {
vertex: true,
..Self::empty()
},
ShaderStage::TessellationControl => Self {
tessellation_control: true,
..Self::empty()
},
ShaderStage::TessellationEvaluation => Self {
tessellation_evaluation: true,
..Self::empty()
},
ShaderStage::Geometry => Self {
geometry: true,
..Self::empty()
},
ShaderStage::Fragment => Self {
fragment: true,
..Self::empty()
},
ShaderStage::Compute => Self {
compute: true,
..Self::empty()
},
ShaderStage::Raygen => Self {
raygen: true,
..Self::empty()
},
ShaderStage::AnyHit => Self {
any_hit: true,
..Self::empty()
},
ShaderStage::ClosestHit => Self {
closest_hit: true,
..Self::empty()
},
ShaderStage::Miss => Self {
miss: true,
..Self::empty()
},
ShaderStage::Intersection => Self {
intersection: true,
..Self::empty()
},
ShaderStage::Callable => Self {
callable: true,
..Self::empty()
},
}
}
}
vulkan_bitflags! {
#[non_exhaustive]
ShaderStages = ShaderStageFlags(u32);
vertex = VERTEX,
tessellation_control = TESSELLATION_CONTROL,
tessellation_evaluation = TESSELLATION_EVALUATION,
geometry = GEOMETRY,
fragment = FRAGMENT,
compute = COMPUTE,
raygen = RAYGEN_KHR {
device_extensions: [khr_ray_tracing_pipeline, nv_ray_tracing],
},
any_hit = ANY_HIT_KHR {
device_extensions: [khr_ray_tracing_pipeline, nv_ray_tracing],
},
closest_hit = CLOSEST_HIT_KHR {
device_extensions: [khr_ray_tracing_pipeline, nv_ray_tracing],
},
miss = MISS_KHR {
device_extensions: [khr_ray_tracing_pipeline, nv_ray_tracing],
},
intersection = INTERSECTION_KHR {
device_extensions: [khr_ray_tracing_pipeline, nv_ray_tracing],
},
callable = CALLABLE_KHR {
device_extensions: [khr_ray_tracing_pipeline, nv_ray_tracing],
},
}
impl ShaderStages {
#[inline]
pub const fn all_graphics() -> ShaderStages {
ShaderStages {
vertex: true,
tessellation_control: true,
tessellation_evaluation: true,
geometry: true,
fragment: true,
..ShaderStages::empty()
}
}
#[inline]
pub const fn compute() -> ShaderStages {
ShaderStages {
compute: true,
..ShaderStages::empty()
}
}
}
impl From<ShaderStages> for PipelineStages {
#[inline]
fn from(stages: ShaderStages) -> PipelineStages {
let ShaderStages {
vertex,
tessellation_control,
tessellation_evaluation,
geometry,
fragment,
compute,
raygen,
any_hit,
closest_hit,
miss,
intersection,
callable,
_ne: _,
} = stages;
PipelineStages {
vertex_shader: vertex,
tessellation_control_shader: tessellation_control,
tessellation_evaluation_shader: tessellation_evaluation,
geometry_shader: geometry,
fragment_shader: fragment,
compute_shader: compute,
ray_tracing_shader: raygen | any_hit | closest_hit | miss | intersection | callable,
..PipelineStages::empty()
}
}
}
fn check_spirv_version(device: &Device, mut version: Version) -> Result<(), ShaderSupportError> {
version.patch = 0;
match version {
Version::V1_0 => {}
Version::V1_1 | Version::V1_2 | Version::V1_3 => {
if !(device.api_version() >= Version::V1_1) {
return Err(ShaderSupportError::RequirementsNotMet(&[
"Vulkan API version 1.1",
]));
}
}
Version::V1_4 => {
if !(device.api_version() >= Version::V1_2 || device.enabled_extensions().khr_spirv_1_4)
{
return Err(ShaderSupportError::RequirementsNotMet(&[
"Vulkan API version 1.2",
"extension `khr_spirv_1_4`",
]));
}
}
Version::V1_5 => {
if !(device.api_version() >= Version::V1_2) {
return Err(ShaderSupportError::RequirementsNotMet(&[
"Vulkan API version 1.2",
]));
}
}
Version::V1_6 => {
if !(device.api_version() >= Version::V1_3) {
return Err(ShaderSupportError::RequirementsNotMet(&[
"Vulkan API version 1.3",
]));
}
}
_ => return Err(ShaderSupportError::NotSupportedByVulkan),
}
Ok(())
}