mod conv;
mod help;
mod keywords;
mod ray;
mod storage;
mod writer;
use alloc::{string::String, vec::Vec};
use core::fmt::Error as FmtError;
use thiserror::Error;
use crate::{back, ir, proc};
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub struct BindTarget {
pub space: u8,
pub register: u32,
pub binding_array_size: Option<u32>,
pub dynamic_storage_buffer_offsets_index: Option<u32>,
#[cfg_attr(any(feature = "serialize", feature = "deserialize"), serde(default))]
pub restrict_indexing: bool,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub struct OffsetsBindTarget {
pub space: u8,
pub register: u32,
pub size: u32,
}
#[cfg(feature = "deserialize")]
#[derive(serde::Deserialize)]
struct BindingMapSerialization {
resource_binding: crate::ResourceBinding,
bind_target: BindTarget,
}
#[cfg(feature = "deserialize")]
fn deserialize_binding_map<'de, D>(deserializer: D) -> Result<BindingMap, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::Deserialize;
let vec = Vec::<BindingMapSerialization>::deserialize(deserializer)?;
let mut map = BindingMap::default();
for item in vec {
map.insert(item.resource_binding, item.bind_target);
}
Ok(map)
}
pub type BindingMap = alloc::collections::BTreeMap<crate::ResourceBinding, BindTarget>;
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub enum ShaderModel {
V5_0,
V5_1,
V6_0,
V6_1,
V6_2,
V6_3,
V6_4,
V6_5,
V6_6,
V6_7,
V6_8,
V6_9,
}
impl ShaderModel {
pub const fn to_str(self) -> &'static str {
match self {
Self::V5_0 => "5_0",
Self::V5_1 => "5_1",
Self::V6_0 => "6_0",
Self::V6_1 => "6_1",
Self::V6_2 => "6_2",
Self::V6_3 => "6_3",
Self::V6_4 => "6_4",
Self::V6_5 => "6_5",
Self::V6_6 => "6_6",
Self::V6_7 => "6_7",
Self::V6_8 => "6_8",
Self::V6_9 => "6_9",
}
}
}
impl crate::ShaderStage {
pub const fn to_hlsl_str(self) -> &'static str {
match self {
Self::Vertex => "vs",
Self::Fragment => "ps",
Self::Compute => "cs",
Self::Task => "as",
Self::Mesh => "ms",
Self::RayGeneration | Self::AnyHit | Self::ClosestHit | Self::Miss => "lib",
}
}
}
impl crate::ImageDimension {
const fn to_hlsl_str(self) -> &'static str {
match self {
Self::D1 => "1D",
Self::D2 => "2D",
Self::D3 => "3D",
Self::Cube => "Cube",
}
}
}
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub struct SamplerIndexBufferKey {
pub group: u32,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
#[cfg_attr(feature = "deserialize", serde(default))]
pub struct SamplerHeapBindTargets {
pub standard_samplers: BindTarget,
pub comparison_samplers: BindTarget,
}
impl Default for SamplerHeapBindTargets {
fn default() -> Self {
Self {
standard_samplers: BindTarget {
space: 0,
register: 0,
binding_array_size: None,
dynamic_storage_buffer_offsets_index: None,
restrict_indexing: false,
},
comparison_samplers: BindTarget {
space: 1,
register: 0,
binding_array_size: None,
dynamic_storage_buffer_offsets_index: None,
restrict_indexing: false,
},
}
}
}
#[cfg(feature = "deserialize")]
#[derive(serde::Deserialize)]
struct SamplerIndexBufferBindingSerialization {
group: u32,
bind_target: BindTarget,
}
#[cfg(feature = "deserialize")]
fn deserialize_sampler_index_buffer_bindings<'de, D>(
deserializer: D,
) -> Result<SamplerIndexBufferBindingMap, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::Deserialize;
let vec = Vec::<SamplerIndexBufferBindingSerialization>::deserialize(deserializer)?;
let mut map = SamplerIndexBufferBindingMap::default();
for item in vec {
map.insert(
SamplerIndexBufferKey { group: item.group },
item.bind_target,
);
}
Ok(map)
}
pub type SamplerIndexBufferBindingMap =
alloc::collections::BTreeMap<SamplerIndexBufferKey, BindTarget>;
#[cfg(feature = "deserialize")]
#[derive(serde::Deserialize)]
struct DynamicStorageBufferOffsetTargetSerialization {
index: u32,
bind_target: OffsetsBindTarget,
}
#[cfg(feature = "deserialize")]
fn deserialize_storage_buffer_offsets<'de, D>(
deserializer: D,
) -> Result<DynamicStorageBufferOffsetsTargets, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::Deserialize;
let vec = Vec::<DynamicStorageBufferOffsetTargetSerialization>::deserialize(deserializer)?;
let mut map = DynamicStorageBufferOffsetsTargets::default();
for item in vec {
map.insert(item.index, item.bind_target);
}
Ok(map)
}
pub type DynamicStorageBufferOffsetsTargets = alloc::collections::BTreeMap<u32, OffsetsBindTarget>;
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub struct ExternalTextureBindTarget {
pub planes: [BindTarget; 3],
pub params: BindTarget,
}
#[cfg(feature = "deserialize")]
#[derive(serde::Deserialize)]
struct ExternalTextureBindingMapSerialization {
resource_binding: crate::ResourceBinding,
bind_target: ExternalTextureBindTarget,
}
#[cfg(feature = "deserialize")]
fn deserialize_external_texture_binding_map<'de, D>(
deserializer: D,
) -> Result<ExternalTextureBindingMap, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::Deserialize;
let vec = Vec::<ExternalTextureBindingMapSerialization>::deserialize(deserializer)?;
let mut map = ExternalTextureBindingMap::default();
for item in vec {
map.insert(item.resource_binding, item.bind_target);
}
Ok(map)
}
pub type ExternalTextureBindingMap =
alloc::collections::BTreeMap<crate::ResourceBinding, ExternalTextureBindTarget>;
type BackendResult = Result<(), Error>;
#[derive(Clone, Debug, PartialEq, thiserror::Error)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub enum EntryPointError {
#[error("mapping of {0:?} is missing")]
MissingBinding(crate::ResourceBinding),
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
#[cfg_attr(feature = "deserialize", serde(default))]
pub struct Options {
pub shader_model: ShaderModel,
#[cfg_attr(
feature = "deserialize",
serde(deserialize_with = "deserialize_binding_map")
)]
pub binding_map: BindingMap,
pub fake_missing_bindings: bool,
pub special_constants_binding: Option<BindTarget>,
pub immediates_target: Option<BindTarget>,
pub sampler_heap_target: SamplerHeapBindTargets,
#[cfg_attr(
feature = "deserialize",
serde(deserialize_with = "deserialize_sampler_index_buffer_bindings")
)]
pub sampler_buffer_binding_map: SamplerIndexBufferBindingMap,
#[cfg_attr(
feature = "deserialize",
serde(deserialize_with = "deserialize_storage_buffer_offsets")
)]
pub dynamic_storage_buffer_offsets_targets: DynamicStorageBufferOffsetsTargets,
#[cfg_attr(
feature = "deserialize",
serde(deserialize_with = "deserialize_external_texture_binding_map")
)]
pub external_texture_binding_map: ExternalTextureBindingMap,
pub zero_initialize_workgroup_memory: bool,
pub restrict_indexing: bool,
pub force_loop_bounding: bool,
pub ray_query_initialization_tracking: bool,
}
impl Default for Options {
fn default() -> Self {
Options {
shader_model: ShaderModel::V5_1,
binding_map: BindingMap::default(),
fake_missing_bindings: true,
special_constants_binding: None,
sampler_heap_target: SamplerHeapBindTargets::default(),
sampler_buffer_binding_map: alloc::collections::BTreeMap::default(),
immediates_target: None,
dynamic_storage_buffer_offsets_targets: alloc::collections::BTreeMap::new(),
external_texture_binding_map: ExternalTextureBindingMap::default(),
zero_initialize_workgroup_memory: true,
restrict_indexing: true,
force_loop_bounding: true,
ray_query_initialization_tracking: true,
}
}
}
impl Options {
fn resolve_resource_binding(
&self,
res_binding: &crate::ResourceBinding,
) -> Result<BindTarget, EntryPointError> {
match self.binding_map.get(res_binding) {
Some(target) => Ok(*target),
None if self.fake_missing_bindings => Ok(BindTarget {
space: res_binding.group as u8,
register: res_binding.binding,
binding_array_size: None,
dynamic_storage_buffer_offsets_index: None,
restrict_indexing: false,
}),
None => Err(EntryPointError::MissingBinding(*res_binding)),
}
}
fn resolve_external_texture_resource_binding(
&self,
res_binding: &crate::ResourceBinding,
) -> Result<ExternalTextureBindTarget, EntryPointError> {
match self.external_texture_binding_map.get(res_binding) {
Some(target) => Ok(*target),
None if self.fake_missing_bindings => {
let fake = BindTarget {
space: res_binding.group as u8,
register: res_binding.binding,
binding_array_size: None,
dynamic_storage_buffer_offsets_index: None,
restrict_indexing: false,
};
Ok(ExternalTextureBindTarget {
planes: [fake, fake, fake],
params: fake,
})
}
None => Err(EntryPointError::MissingBinding(*res_binding)),
}
}
}
#[derive(Default)]
pub struct ReflectionInfo {
pub entry_point_names: Vec<Result<String, EntryPointError>>,
}
#[derive(Debug, Default, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
#[cfg_attr(feature = "deserialize", serde(default))]
pub struct PipelineOptions {
pub entry_point: Option<(ir::ShaderStage, String)>,
}
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
IoError(#[from] FmtError),
#[error("A scalar with an unsupported width was requested: {0:?}")]
UnsupportedScalar(crate::Scalar),
#[error("{0}")]
Unimplemented(String), #[error("{0}")]
Custom(String),
#[error("overrides should not be present at this stage")]
Override,
#[error(transparent)]
ResolveArraySizeError(#[from] proc::ResolveArraySizeError),
#[error("entry point with stage {0:?} and name '{1}' not found")]
EntryPointNotFound(ir::ShaderStage, String),
#[error("requires shader model {1:?} for reason: {0}")]
ShaderModelTooLow(String, ShaderModel),
}
#[derive(PartialEq, Eq, Hash)]
enum WrappedType {
ZeroValue(help::WrappedZeroValue),
ArrayLength(help::WrappedArrayLength),
ImageSample(help::WrappedImageSample),
ImageQuery(help::WrappedImageQuery),
ImageLoad(help::WrappedImageLoad),
ImageLoadScalar(crate::Scalar),
Constructor(help::WrappedConstructor),
StructMatrixAccess(help::WrappedStructMatrixAccess),
MatCx2(help::WrappedMatCx2),
Math(help::WrappedMath),
UnaryOp(help::WrappedUnaryOp),
BinaryOp(help::WrappedBinaryOp),
Cast(help::WrappedCast),
}
#[derive(Default)]
struct Wrapped {
types: crate::FastHashSet<WrappedType>,
sampler_heaps: bool,
sampler_index_buffers: crate::FastHashMap<SamplerIndexBufferKey, String>,
}
impl Wrapped {
fn insert(&mut self, r#type: WrappedType) -> bool {
self.types.insert(r#type)
}
fn clear(&mut self) {
self.types.clear();
}
}
pub struct FragmentEntryPoint<'a> {
module: &'a crate::Module,
func: &'a crate::Function,
}
impl<'a> FragmentEntryPoint<'a> {
pub fn new(module: &'a crate::Module, ep_name: &'a str) -> Option<Self> {
module
.entry_points
.iter()
.find(|ep| ep.name == ep_name)
.filter(|ep| ep.stage == crate::ShaderStage::Fragment)
.map(|ep| Self {
module,
func: &ep.function,
})
}
}
pub struct Writer<'a, W> {
out: W,
names: crate::FastHashMap<proc::NameKey, String>,
namer: proc::Namer,
options: &'a Options,
pipeline_options: &'a PipelineOptions,
entry_point_io: crate::FastHashMap<usize, writer::EntryPointInterface>,
named_expressions: crate::NamedExpressions,
wrapped: Wrapped,
written_committed_intersection: bool,
written_candidate_intersection: bool,
continue_ctx: back::continue_forward::ContinueCtx,
temp_access_chain: Vec<storage::SubAccess>,
need_bake_expressions: back::NeedBakeExpressions,
}
pub fn supported_capabilities() -> crate::valid::Capabilities {
use crate::valid::Capabilities as Caps;
Caps::IMMEDIATES
| Caps::FLOAT64 | Caps::PRIMITIVE_INDEX
| Caps::TEXTURE_AND_SAMPLER_BINDING_ARRAY
| Caps::STORAGE_TEXTURE_BINDING_ARRAY
| Caps::ACCELERATION_STRUCTURE_BINDING_ARRAY
| Caps::STORAGE_TEXTURE_16BIT_NORM_FORMATS
| Caps::MULTIVIEW
| Caps::MULTISAMPLED_SHADING
| Caps::RAY_QUERY
| Caps::DUAL_SOURCE_BLENDING
| Caps::CUBE_ARRAY_TEXTURES
| Caps::SHADER_INT64
| Caps::SUBGROUP
| Caps::SHADER_INT64_ATOMIC_MIN_MAX
| Caps::SHADER_INT64_ATOMIC_ALL_OPS
| Caps::TEXTURE_ATOMIC
| Caps::TEXTURE_INT64_ATOMIC
| Caps::SHADER_FLOAT16
| Caps::TEXTURE_EXTERNAL
| Caps::SHADER_FLOAT16_IN_FLOAT32
| Caps::SHADER_BARYCENTRICS
| Caps::TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING
| Caps::STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING
| Caps::STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING
| Caps::MEMORY_DECORATION_COHERENT
}