use self::spirv::{Id, Instruction};
use crate::{
descriptor_set::layout::DescriptorType,
device::{Device, DeviceOwned},
format::{Format, NumericType},
image::view::ImageViewType,
instance::InstanceOwnedDebugWrapper,
macros::{impl_id_counter, vulkan_bitflags_enum},
pipeline::layout::PushConstantRange,
shader::spirv::{Capability, Spirv},
sync::PipelineStages,
Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError,
VulkanObject,
};
use ahash::{HashMap, HashSet};
use bytemuck::bytes_of;
use half::f16;
use smallvec::SmallVec;
use spirv::ExecutionModel;
use std::{
borrow::Cow,
collections::hash_map::Entry,
mem::{discriminant, size_of_val, MaybeUninit},
num::NonZeroU64,
ptr,
sync::Arc,
};
pub mod reflect;
pub mod spirv;
include!(concat!(env!("OUT_DIR"), "/spirv_reqs.rs"));
#[derive(Debug)]
pub struct ShaderModule {
handle: ash::vk::ShaderModule,
device: InstanceOwnedDebugWrapper<Arc<Device>>,
id: NonZeroU64,
spirv: Spirv,
specialization_constants: HashMap<u32, SpecializationConstant>,
}
impl ShaderModule {
#[inline]
pub unsafe fn new(
device: Arc<Device>,
create_info: ShaderModuleCreateInfo<'_>,
) -> Result<Arc<ShaderModule>, Validated<VulkanError>> {
let spirv = Spirv::new(create_info.code).map_err(|err| {
Box::new(ValidationError {
context: "create_info.code".into(),
problem: format!("error while parsing: {}", err).into(),
..Default::default()
})
})?;
Self::validate_new(&device, &create_info, &spirv)?;
Ok(Self::new_with_spirv_unchecked(device, create_info, spirv)?)
}
fn validate_new(
device: &Device,
create_info: &ShaderModuleCreateInfo<'_>,
spirv: &Spirv,
) -> Result<(), Box<ValidationError>> {
create_info
.validate(device, spirv)
.map_err(|err| err.add_context("create_info"))?;
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn new_unchecked(
device: Arc<Device>,
create_info: ShaderModuleCreateInfo<'_>,
) -> Result<Arc<ShaderModule>, VulkanError> {
let spirv = Spirv::new(create_info.code).unwrap();
Self::new_with_spirv_unchecked(device, create_info, spirv)
}
unsafe fn new_with_spirv_unchecked(
device: Arc<Device>,
create_info: ShaderModuleCreateInfo<'_>,
spirv: Spirv,
) -> Result<Arc<ShaderModule>, VulkanError> {
let &ShaderModuleCreateInfo { code, _ne: _ } = &create_info;
let handle = {
let infos = ash::vk::ShaderModuleCreateInfo {
flags: ash::vk::ShaderModuleCreateFlags::empty(),
code_size: size_of_val(code),
p_code: code.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()
};
Ok(Self::from_handle_with_spirv(
device,
handle,
create_info,
spirv,
))
}
pub unsafe fn from_handle(
device: Arc<Device>,
handle: ash::vk::ShaderModule,
create_info: ShaderModuleCreateInfo<'_>,
) -> Arc<ShaderModule> {
let spirv = Spirv::new(create_info.code).unwrap();
Self::from_handle_with_spirv(device, handle, create_info, spirv)
}
unsafe fn from_handle_with_spirv(
device: Arc<Device>,
handle: ash::vk::ShaderModule,
create_info: ShaderModuleCreateInfo<'_>,
spirv: Spirv,
) -> Arc<ShaderModule> {
let ShaderModuleCreateInfo { code: _, _ne: _ } = create_info;
let specialization_constants = reflect::specialization_constants(&spirv);
Arc::new(ShaderModule {
handle,
device: InstanceOwnedDebugWrapper(device),
id: Self::next_id(),
spirv,
specialization_constants,
})
}
#[deprecated(since = "0.34.0", note = "use `new` instead")]
#[inline]
pub unsafe fn from_words(
device: Arc<Device>,
words: &[u32],
) -> Result<Arc<ShaderModule>, Validated<VulkanError>> {
Self::new(device, ShaderModuleCreateInfo::new(words))
}
#[deprecated(
since = "0.34.0",
note = "use `shader::spirv::bytes_to_words`, and then use `new` instead"
)]
#[inline]
pub unsafe fn from_bytes(
device: Arc<Device>,
bytes: &[u8],
) -> Result<Arc<ShaderModule>, Validated<VulkanError>> {
let words = spirv::bytes_to_words(bytes).unwrap();
Self::new(device, ShaderModuleCreateInfo::new(&words))
}
#[inline]
pub fn specialization_constants(&self) -> &HashMap<u32, SpecializationConstant> {
&self.specialization_constants
}
#[inline]
pub fn specialize(
self: &Arc<Self>,
specialization_info: HashMap<u32, SpecializationConstant>,
) -> Result<Arc<SpecializedShaderModule>, Box<ValidationError>> {
SpecializedShaderModule::new(self.clone(), specialization_info)
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn specialize_unchecked(
self: &Arc<Self>,
specialization_info: HashMap<u32, SpecializationConstant>,
) -> Arc<SpecializedShaderModule> {
SpecializedShaderModule::new_unchecked(self.clone(), specialization_info)
}
#[inline]
pub fn entry_point(self: &Arc<Self>, name: &str) -> Option<EntryPoint> {
unsafe {
self.specialize_unchecked(HashMap::default())
.entry_point(name)
}
}
#[inline]
pub fn entry_point_with_execution(
self: &Arc<Self>,
name: &str,
execution: ExecutionModel,
) -> Option<EntryPoint> {
unsafe {
self.specialize_unchecked(HashMap::default())
.entry_point_with_execution(name, execution)
}
}
#[inline]
pub fn single_entry_point(self: &Arc<Self>) -> Option<EntryPoint> {
unsafe {
self.specialize_unchecked(HashMap::default())
.single_entry_point()
}
}
#[inline]
pub fn single_entry_point_with_execution(
self: &Arc<Self>,
execution: ExecutionModel,
) -> Option<EntryPoint> {
unsafe {
self.specialize_unchecked(HashMap::default())
.single_entry_point_with_execution(execution)
}
}
}
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
}
}
impl_id_counter!(ShaderModule);
pub struct ShaderModuleCreateInfo<'a> {
pub code: &'a [u32],
pub _ne: crate::NonExhaustive,
}
impl<'a> ShaderModuleCreateInfo<'a> {
#[inline]
pub fn new(code: &'a [u32]) -> Self {
Self {
code,
_ne: crate::NonExhaustive(()),
}
}
pub(crate) fn validate(
&self,
device: &Device,
spirv: &Spirv,
) -> Result<(), Box<ValidationError>> {
let &Self { code, _ne: _ } = self;
if code.is_empty() {
return Err(Box::new(ValidationError {
context: "code".into(),
problem: "is empty".into(),
vuids: &["VUID-VkShaderModuleCreateInfo-codeSize-01085"],
..Default::default()
}));
}
let spirv_version = Version {
patch: 0, ..spirv.version()
};
{
match spirv_version {
Version::V1_0 => None,
Version::V1_1 | Version::V1_2 | Version::V1_3 => {
(!(device.api_version() >= Version::V1_1)).then_some(RequiresOneOf(&[
RequiresAllOf(&[Requires::APIVersion(Version::V1_1)]),
]))
}
Version::V1_4 => (!(device.api_version() >= Version::V1_2
|| device.enabled_extensions().khr_spirv_1_4))
.then_some(RequiresOneOf(&[
RequiresAllOf(&[Requires::APIVersion(Version::V1_2)]),
RequiresAllOf(&[Requires::DeviceExtension("khr_spirv_1_4")]),
])),
Version::V1_5 => {
(!(device.api_version() >= Version::V1_2)).then_some(RequiresOneOf(&[
RequiresAllOf(&[Requires::APIVersion(Version::V1_2)]),
]))
}
Version::V1_6 => {
(!(device.api_version() >= Version::V1_3)).then_some(RequiresOneOf(&[
RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]),
]))
}
_ => {
return Err(Box::new(ValidationError {
context: "code".into(),
problem: format!(
"uses SPIR-V version {}.{}, which is not supported by Vulkan",
spirv_version.major, spirv_version.minor
)
.into(),
..Default::default()
}));
}
}
}
.map_or(Ok(()), |requires_one_of| {
Err(Box::new(ValidationError {
context: "code".into(),
problem: format!(
"uses SPIR-V version {}.{}",
spirv_version.major, spirv_version.minor
)
.into(),
requires_one_of,
..Default::default()
}))
})?;
for &capability in spirv
.iter_capability()
.filter_map(|instruction| match instruction {
Instruction::Capability { capability } => Some(capability),
_ => None,
})
{
validate_spirv_capability(device, capability).map_err(|err| err.add_context("code"))?;
}
for extension in spirv
.iter_extension()
.filter_map(|instruction| match instruction {
Instruction::Extension { name } => Some(name.as_str()),
_ => None,
})
{
validate_spirv_extension(device, extension).map_err(|err| err.add_context("code"))?;
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum SpecializationConstant {
Bool(bool),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
F16(f16),
F32(f32),
F64(f64),
}
impl SpecializationConstant {
#[inline]
pub fn as_bytes(&self) -> &[u8] {
match self {
Self::Bool(false) => bytes_of(&ash::vk::FALSE),
Self::Bool(true) => bytes_of(&ash::vk::TRUE),
Self::U8(value) => bytes_of(value),
Self::U16(value) => bytes_of(value),
Self::U32(value) => bytes_of(value),
Self::U64(value) => bytes_of(value),
Self::I8(value) => bytes_of(value),
Self::I16(value) => bytes_of(value),
Self::I32(value) => bytes_of(value),
Self::I64(value) => bytes_of(value),
Self::F16(value) => bytes_of(value),
Self::F32(value) => bytes_of(value),
Self::F64(value) => bytes_of(value),
}
}
#[inline]
pub fn eq_type(&self, other: &Self) -> bool {
discriminant(self) == discriminant(other)
}
}
impl From<bool> for SpecializationConstant {
#[inline]
fn from(value: bool) -> Self {
SpecializationConstant::Bool(value)
}
}
impl From<i8> for SpecializationConstant {
#[inline]
fn from(value: i8) -> Self {
SpecializationConstant::I8(value)
}
}
impl From<i16> for SpecializationConstant {
#[inline]
fn from(value: i16) -> Self {
SpecializationConstant::I16(value)
}
}
impl From<i32> for SpecializationConstant {
#[inline]
fn from(value: i32) -> Self {
SpecializationConstant::I32(value)
}
}
impl From<i64> for SpecializationConstant {
#[inline]
fn from(value: i64) -> Self {
SpecializationConstant::I64(value)
}
}
impl From<u8> for SpecializationConstant {
#[inline]
fn from(value: u8) -> Self {
SpecializationConstant::U8(value)
}
}
impl From<u16> for SpecializationConstant {
#[inline]
fn from(value: u16) -> Self {
SpecializationConstant::U16(value)
}
}
impl From<u32> for SpecializationConstant {
#[inline]
fn from(value: u32) -> Self {
SpecializationConstant::U32(value)
}
}
impl From<u64> for SpecializationConstant {
#[inline]
fn from(value: u64) -> Self {
SpecializationConstant::U64(value)
}
}
impl From<f16> for SpecializationConstant {
#[inline]
fn from(value: f16) -> Self {
SpecializationConstant::F16(value)
}
}
impl From<f32> for SpecializationConstant {
#[inline]
fn from(value: f32) -> Self {
SpecializationConstant::F32(value)
}
}
impl From<f64> for SpecializationConstant {
#[inline]
fn from(value: f64) -> Self {
SpecializationConstant::F64(value)
}
}
#[derive(Debug)]
pub struct SpecializedShaderModule {
base_module: Arc<ShaderModule>,
specialization_info: HashMap<u32, SpecializationConstant>,
spirv: Option<Spirv>,
entry_point_infos: SmallVec<[(Id, EntryPointInfo); 1]>,
}
impl SpecializedShaderModule {
#[inline]
pub fn new(
base_module: Arc<ShaderModule>,
specialization_info: HashMap<u32, SpecializationConstant>,
) -> Result<Arc<Self>, Box<ValidationError>> {
Self::validate_new(&base_module, &specialization_info)?;
unsafe { Ok(Self::new_unchecked(base_module, specialization_info)) }
}
fn validate_new(
base_module: &ShaderModule,
specialization_info: &HashMap<u32, SpecializationConstant>,
) -> Result<(), Box<ValidationError>> {
for (&constant_id, provided_value) in specialization_info {
if let Some(default_value) = base_module.specialization_constants.get(&constant_id) {
if !provided_value.eq_type(default_value) {
return Err(Box::new(ValidationError {
problem: format!(
"`specialization_info[{0}]` does not have the same type as \
`base_module.specialization_constants()[{0}]`",
constant_id
)
.into(),
vuids: &["VUID-VkSpecializationMapEntry-constantID-00776"],
..Default::default()
}));
}
}
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn new_unchecked(
base_module: Arc<ShaderModule>,
specialization_info: HashMap<u32, SpecializationConstant>,
) -> Arc<Self> {
let spirv = (!base_module.specialization_constants.is_empty()).then(|| {
let mut spirv = base_module.spirv.clone();
spirv.apply_specialization(&specialization_info);
spirv
});
let entry_point_infos =
reflect::entry_points(spirv.as_ref().unwrap_or(&base_module.spirv)).collect();
Arc::new(Self {
base_module,
specialization_info,
spirv,
entry_point_infos,
})
}
#[inline]
pub fn base_module(&self) -> &Arc<ShaderModule> {
&self.base_module
}
#[inline]
pub fn specialization_info(&self) -> &HashMap<u32, SpecializationConstant> {
&self.specialization_info
}
#[inline]
pub(crate) fn spirv(&self) -> &Spirv {
self.spirv.as_ref().unwrap_or(&self.base_module.spirv)
}
#[inline]
pub fn entry_point(self: &Arc<Self>, name: &str) -> Option<EntryPoint> {
self.single_entry_point_filter(|info| info.name == name)
}
#[inline]
pub fn entry_point_with_execution(
self: &Arc<Self>,
name: &str,
execution: ExecutionModel,
) -> Option<EntryPoint> {
self.single_entry_point_filter(|info| {
info.name == name && info.execution_model == execution
})
}
#[inline]
fn single_entry_point_filter<P>(self: &Arc<Self>, mut filter: P) -> Option<EntryPoint>
where
P: FnMut(&EntryPointInfo) -> bool,
{
let mut iter = self
.entry_point_infos
.iter()
.enumerate()
.filter(|(_, (_, infos))| filter(infos))
.map(|(x, _)| x);
let info_index = iter.next()?;
iter.next().is_none().then(|| EntryPoint {
module: self.clone(),
id: self.entry_point_infos[info_index].0,
info_index,
})
}
#[inline]
pub fn single_entry_point(self: &Arc<Self>) -> Option<EntryPoint> {
self.single_entry_point_filter(|_| true)
}
#[inline]
pub fn single_entry_point_with_execution(
self: &Arc<Self>,
execution: ExecutionModel,
) -> Option<EntryPoint> {
self.single_entry_point_filter(|info| info.execution_model == execution)
}
}
unsafe impl VulkanObject for SpecializedShaderModule {
type Handle = ash::vk::ShaderModule;
#[inline]
fn handle(&self) -> Self::Handle {
self.base_module.handle
}
}
unsafe impl DeviceOwned for SpecializedShaderModule {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.base_module.device
}
}
#[derive(Clone, Debug)]
pub struct EntryPointInfo {
pub name: String,
pub execution_model: ExecutionModel,
pub descriptor_binding_requirements: HashMap<(u32, u32), DescriptorBindingRequirements>,
pub push_constant_requirements: Option<PushConstantRange>,
pub input_interface: ShaderInterface,
pub output_interface: ShaderInterface,
}
#[derive(Clone, Debug)]
pub struct EntryPoint {
module: Arc<SpecializedShaderModule>,
id: Id,
info_index: usize,
}
impl EntryPoint {
#[inline]
pub fn module(&self) -> &Arc<SpecializedShaderModule> {
&self.module
}
pub(crate) fn id(&self) -> Id {
self.id
}
#[inline]
pub fn info(&self) -> &EntryPointInfo {
&self.module.entry_point_infos[self.info_index].1
}
}
#[derive(Clone, Debug, Default)]
pub struct DescriptorBindingRequirements {
pub descriptor_types: Vec<DescriptorType>,
pub descriptor_count: Option<u32>,
pub image_format: Option<Format>,
pub image_multisampled: bool,
pub image_scalar_type: Option<NumericType>,
pub image_view_type: Option<ImageViewType>,
pub stages: ShaderStages,
pub descriptors: HashMap<Option<u32>, DescriptorRequirements>,
}
#[derive(Clone, Debug, Default)]
pub struct DescriptorRequirements {
pub memory_read: ShaderStages,
pub memory_write: ShaderStages,
pub sampler_compare: bool,
pub sampler_no_unnormalized_coordinates: bool,
pub sampler_no_ycbcr_conversion: bool,
pub sampler_with_images: HashSet<DescriptorIdentifier>,
pub storage_image_atomic: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct DescriptorIdentifier {
pub set: u32,
pub binding: u32,
pub index: u32,
}
impl DescriptorBindingRequirements {
#[inline]
pub fn merge(&mut self, other: &Self) -> Result<(), Box<ValidationError>> {
let Self {
descriptor_types,
descriptor_count,
image_format,
image_multisampled,
image_scalar_type,
image_view_type,
stages,
descriptors,
} = self;
if !descriptor_types
.iter()
.any(|ty| other.descriptor_types.contains(ty))
{
return Err(Box::new(ValidationError {
problem: "the allowed descriptor types of the two descriptors do not overlap"
.into(),
..Default::default()
}));
}
if let (Some(first), Some(second)) = (*image_format, other.image_format) {
if first != second {
return Err(Box::new(ValidationError {
problem: "the descriptors require different formats".into(),
..Default::default()
}));
}
}
if let (Some(first), Some(second)) = (*image_scalar_type, other.image_scalar_type) {
if first != second {
return Err(Box::new(ValidationError {
problem: "the descriptors require different scalar types".into(),
..Default::default()
}));
}
}
if let (Some(first), Some(second)) = (*image_view_type, other.image_view_type) {
if first != second {
return Err(Box::new(ValidationError {
problem: "the descriptors require different image view types".into(),
..Default::default()
}));
}
}
if *image_multisampled != other.image_multisampled {
return Err(Box::new(ValidationError {
problem: "the multisampling requirements of the descriptors differ".into(),
..Default::default()
}));
}
descriptor_types.retain(|ty| other.descriptor_types.contains(ty));
*descriptor_count = (*descriptor_count).max(other.descriptor_count);
*image_format = image_format.or(other.image_format);
*image_scalar_type = image_scalar_type.or(other.image_scalar_type);
*image_view_type = image_view_type.or(other.image_view_type);
*stages |= other.stages;
for (&index, other) in &other.descriptors {
match descriptors.entry(index) {
Entry::Vacant(entry) => {
entry.insert(other.clone());
}
Entry::Occupied(entry) => {
entry.into_mut().merge(other);
}
}
}
Ok(())
}
}
impl DescriptorRequirements {
#[inline]
pub fn merge(&mut self, other: &Self) {
let Self {
memory_read,
memory_write,
sampler_compare,
sampler_no_unnormalized_coordinates,
sampler_no_ycbcr_conversion,
sampler_with_images,
storage_image_atomic,
} = self;
*memory_read |= other.memory_read;
*memory_write |= other.memory_write;
*sampler_compare |= other.sampler_compare;
*sampler_no_unnormalized_coordinates |= other.sampler_no_unnormalized_coordinates;
*sampler_no_ycbcr_conversion |= other.sampler_no_ycbcr_conversion;
sampler_with_images.extend(&other.sampler_with_images);
*storage_image_atomic |= other.storage_image_atomic;
}
}
#[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<(), Box<ValidationError>> {
if self.elements().len() != other.elements().len() {
return Err(Box::new(ValidationError {
problem: "the number of elements in the shader interfaces are not equal".into(),
..Default::default()
}));
}
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(Box::new(ValidationError {
problem: format!(
"the second shader is missing an interface element at location {}",
loc
)
.into(),
..Default::default()
}));
}
Some(b) => b,
};
if a.ty != b.ty {
return Err(Box::new(ValidationError {
problem: format!(
"the interface element at location {} does not have the same type \
in both shaders",
loc
)
.into(),
..Default::default()
}));
}
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct ShaderInterfaceEntry {
pub location: u32,
pub index: 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: NumericType,
pub num_components: u32,
pub num_elements: u32,
pub is_64bit: bool,
}
impl ShaderInterfaceEntryType {
pub(crate) fn num_locations(&self) -> u32 {
assert!(!self.is_64bit); self.num_elements
}
}
vulkan_bitflags_enum! {
#[non_exhaustive]
ShaderStages impl {
#[inline]
pub const fn all_graphics() -> ShaderStages {
ShaderStages::VERTEX
.union(ShaderStages::TESSELLATION_CONTROL)
.union(ShaderStages::TESSELLATION_EVALUATION)
.union(ShaderStages::GEOMETRY)
.union(ShaderStages::FRAGMENT)
}
},
ShaderStage,
= ShaderStageFlags(u32);
VERTEX, Vertex = VERTEX,
TESSELLATION_CONTROL, TessellationControl = TESSELLATION_CONTROL,
TESSELLATION_EVALUATION, TessellationEvaluation = TESSELLATION_EVALUATION,
GEOMETRY, Geometry = GEOMETRY,
FRAGMENT, Fragment = FRAGMENT,
COMPUTE, Compute = COMPUTE,
RAYGEN, Raygen = RAYGEN_KHR
RequiresOneOf([
RequiresAllOf([DeviceExtension(khr_ray_tracing_pipeline)]),
RequiresAllOf([DeviceExtension(nv_ray_tracing)]),
]),
ANY_HIT, AnyHit = ANY_HIT_KHR
RequiresOneOf([
RequiresAllOf([DeviceExtension(khr_ray_tracing_pipeline)]),
RequiresAllOf([DeviceExtension(nv_ray_tracing)]),
]),
CLOSEST_HIT, ClosestHit = CLOSEST_HIT_KHR
RequiresOneOf([
RequiresAllOf([DeviceExtension(khr_ray_tracing_pipeline)]),
RequiresAllOf([DeviceExtension(nv_ray_tracing)]),
]),
MISS, Miss = MISS_KHR
RequiresOneOf([
RequiresAllOf([DeviceExtension(khr_ray_tracing_pipeline)]),
RequiresAllOf([DeviceExtension(nv_ray_tracing)]),
]),
INTERSECTION, Intersection = INTERSECTION_KHR
RequiresOneOf([
RequiresAllOf([DeviceExtension(khr_ray_tracing_pipeline)]),
RequiresAllOf([DeviceExtension(nv_ray_tracing)]),
]),
CALLABLE, Callable = CALLABLE_KHR
RequiresOneOf([
RequiresAllOf([DeviceExtension(khr_ray_tracing_pipeline)]),
RequiresAllOf([DeviceExtension(nv_ray_tracing)]),
]),
TASK, Task = TASK_EXT
RequiresOneOf([
RequiresAllOf([DeviceExtension(ext_mesh_shader)]),
RequiresAllOf([DeviceExtension(nv_mesh_shader)]),
]),
MESH, Mesh = MESH_EXT
RequiresOneOf([
RequiresAllOf([DeviceExtension(ext_mesh_shader)]),
RequiresAllOf([DeviceExtension(nv_mesh_shader)]),
]),
SUBPASS_SHADING, SubpassShading = SUBPASS_SHADING_HUAWEI
RequiresOneOf([
RequiresAllOf([DeviceExtension(huawei_subpass_shading)]),
]),
}
impl From<ExecutionModel> for ShaderStage {
#[inline]
fn from(value: ExecutionModel) -> Self {
match value {
ExecutionModel::Vertex => ShaderStage::Vertex,
ExecutionModel::TessellationControl => ShaderStage::TessellationControl,
ExecutionModel::TessellationEvaluation => ShaderStage::TessellationEvaluation,
ExecutionModel::Geometry => ShaderStage::Geometry,
ExecutionModel::Fragment => ShaderStage::Fragment,
ExecutionModel::GLCompute => ShaderStage::Compute,
ExecutionModel::Kernel => {
unimplemented!("the `Kernel` execution model is not supported by Vulkan")
}
ExecutionModel::TaskNV | ExecutionModel::TaskEXT => ShaderStage::Task,
ExecutionModel::MeshNV | ExecutionModel::MeshEXT => ShaderStage::Mesh,
ExecutionModel::RayGenerationKHR => ShaderStage::Raygen,
ExecutionModel::IntersectionKHR => ShaderStage::Intersection,
ExecutionModel::AnyHitKHR => ShaderStage::AnyHit,
ExecutionModel::ClosestHitKHR => ShaderStage::ClosestHit,
ExecutionModel::MissKHR => ShaderStage::Miss,
ExecutionModel::CallableKHR => ShaderStage::Callable,
}
}
}
impl From<ShaderStages> for PipelineStages {
#[inline]
fn from(stages: ShaderStages) -> PipelineStages {
let mut result = PipelineStages::empty();
if stages.intersects(ShaderStages::VERTEX) {
result |= PipelineStages::VERTEX_SHADER
}
if stages.intersects(ShaderStages::TESSELLATION_CONTROL) {
result |= PipelineStages::TESSELLATION_CONTROL_SHADER
}
if stages.intersects(ShaderStages::TESSELLATION_EVALUATION) {
result |= PipelineStages::TESSELLATION_EVALUATION_SHADER
}
if stages.intersects(ShaderStages::GEOMETRY) {
result |= PipelineStages::GEOMETRY_SHADER
}
if stages.intersects(ShaderStages::FRAGMENT) {
result |= PipelineStages::FRAGMENT_SHADER
}
if stages.intersects(ShaderStages::COMPUTE) {
result |= PipelineStages::COMPUTE_SHADER
}
if stages.intersects(
ShaderStages::RAYGEN
| ShaderStages::ANY_HIT
| ShaderStages::CLOSEST_HIT
| ShaderStages::MISS
| ShaderStages::INTERSECTION
| ShaderStages::CALLABLE,
) {
result |= PipelineStages::RAY_TRACING_SHADER
}
if stages.intersects(ShaderStages::TASK) {
result |= PipelineStages::TASK_SHADER;
}
if stages.intersects(ShaderStages::MESH) {
result |= PipelineStages::MESH_SHADER;
}
if stages.intersects(ShaderStages::SUBPASS_SHADING) {
result |= PipelineStages::SUBPASS_SHADING;
}
result
}
}