pub use features::Features;
pub use writer::Writer;
use alloc::{
borrow::ToOwned,
format,
string::{String, ToString},
vec,
vec::Vec,
};
use core::{
cmp::Ordering,
fmt::{self, Error as FmtError, Write},
mem,
};
use hashbrown::hash_map;
use thiserror::Error;
use crate::{
back::{self, Baked},
common,
proc::{self, NameKey},
valid, Handle, ShaderStage, TypeInner,
};
use conv::*;
use features::FeaturesManager;
mod conv;
mod features;
mod keywords;
mod writer;
pub const SUPPORTED_CORE_VERSIONS: &[u16] = &[140, 150, 330, 400, 410, 420, 430, 440, 450, 460];
pub const SUPPORTED_ES_VERSIONS: &[u16] = &[300, 310, 320];
const CLAMPED_LOD_SUFFIX: &str = "_clamped_lod";
pub(crate) const MODF_FUNCTION: &str = "naga_modf";
pub(crate) const FREXP_FUNCTION: &str = "naga_frexp";
pub const FIRST_INSTANCE_BINDING: &str = "naga_vs_first_instance";
#[cfg(feature = "deserialize")]
#[derive(serde::Deserialize)]
struct BindingMapSerialization {
resource_binding: crate::ResourceBinding,
bind_target: u8,
}
#[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, u8>;
impl crate::AtomicFunction {
const fn to_glsl(self) -> &'static str {
match self {
Self::Add | Self::Subtract => "Add",
Self::And => "And",
Self::InclusiveOr => "Or",
Self::ExclusiveOr => "Xor",
Self::Min => "Min",
Self::Max => "Max",
Self::Exchange { compare: None } => "Exchange",
Self::Exchange { compare: Some(_) } => "", }
}
}
impl crate::AddressSpace {
const fn initializable(&self) -> bool {
match *self {
crate::AddressSpace::Function | crate::AddressSpace::Private => true,
crate::AddressSpace::WorkGroup
| crate::AddressSpace::Uniform
| crate::AddressSpace::Storage { .. }
| crate::AddressSpace::Handle
| crate::AddressSpace::Immediate
| crate::AddressSpace::TaskPayload => false,
crate::AddressSpace::RayPayload | crate::AddressSpace::IncomingRayPayload => {
unreachable!()
}
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub enum Version {
Desktop(u16),
Embedded { version: u16, is_webgl: bool },
}
impl Version {
pub const fn new_gles(version: u16) -> Self {
Self::Embedded {
version,
is_webgl: false,
}
}
const fn is_es(&self) -> bool {
match *self {
Version::Desktop(_) => false,
Version::Embedded { .. } => true,
}
}
const fn is_webgl(&self) -> bool {
match *self {
Version::Desktop(_) => false,
Version::Embedded { is_webgl, .. } => is_webgl,
}
}
fn is_supported(&self) -> bool {
match *self {
Version::Desktop(v) => SUPPORTED_CORE_VERSIONS.contains(&v),
Version::Embedded { version: v, .. } => SUPPORTED_ES_VERSIONS.contains(&v),
}
}
fn supports_io_locations(&self) -> bool {
*self >= Version::Desktop(330) || *self >= Version::new_gles(300)
}
fn supports_explicit_locations(&self) -> bool {
*self >= Version::Desktop(420) || *self >= Version::new_gles(310)
}
fn supports_early_depth_test(&self) -> bool {
*self >= Version::Desktop(130) || *self >= Version::new_gles(310)
}
fn supports_std140_layout(&self) -> bool {
*self >= Version::Desktop(140) || *self >= Version::new_gles(300)
}
fn supports_std430_layout(&self) -> bool {
*self >= Version::Desktop(400) || *self >= Version::new_gles(310)
}
fn supports_fma_function(&self) -> bool {
*self >= Version::Desktop(400) || *self >= Version::new_gles(320)
}
fn supports_integer_functions(&self) -> bool {
*self >= Version::Desktop(400) || *self >= Version::new_gles(310)
}
fn supports_frexp_function(&self) -> bool {
*self >= Version::Desktop(400) || *self >= Version::new_gles(310)
}
fn supports_derivative_control(&self) -> bool {
*self >= Version::Desktop(450)
}
fn supports_pack_unpack_4x8(&self) -> bool {
*self >= Version::Desktop(400) || *self >= Version::new_gles(310)
}
fn supports_pack_unpack_snorm_2x16(&self) -> bool {
*self >= Version::Desktop(420) || *self >= Version::new_gles(300)
}
fn supports_pack_unpack_unorm_2x16(&self) -> bool {
*self >= Version::Desktop(400) || *self >= Version::new_gles(300)
}
fn supports_pack_unpack_half_2x16(&self) -> bool {
*self >= Version::Desktop(420) || *self >= Version::new_gles(300)
}
}
impl PartialOrd for Version {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (*self, *other) {
(Version::Desktop(x), Version::Desktop(y)) => Some(x.cmp(&y)),
(Version::Embedded { version: x, .. }, Version::Embedded { version: y, .. }) => {
Some(x.cmp(&y))
}
_ => None,
}
}
}
impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Version::Desktop(v) => write!(f, "{v} core"),
Version::Embedded { version: v, .. } => write!(f, "{v} es"),
}
}
}
bitflags::bitflags! {
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct WriterFlags: u32 {
const ADJUST_COORDINATE_SPACE = 0x1;
const TEXTURE_SHADOW_LOD = 0x2;
const DRAW_PARAMETERS = 0x4;
const INCLUDE_UNUSED_ITEMS = 0x10;
const FORCE_POINT_SIZE = 0x20;
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
#[cfg_attr(feature = "deserialize", serde(default))]
pub struct Options {
pub version: Version,
pub writer_flags: WriterFlags,
#[cfg_attr(
feature = "deserialize",
serde(deserialize_with = "deserialize_binding_map")
)]
pub binding_map: BindingMap,
pub zero_initialize_workgroup_memory: bool,
}
impl Default for Options {
fn default() -> Self {
Options {
version: Version::new_gles(310),
writer_flags: WriterFlags::ADJUST_COORDINATE_SPACE,
binding_map: BindingMap::default(),
zero_initialize_workgroup_memory: true,
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub struct PipelineOptions {
pub shader_stage: ShaderStage,
pub entry_point: String,
pub multiview: Option<core::num::NonZeroU32>,
}
#[derive(Debug)]
pub struct VaryingLocation {
pub location: u32,
pub index: u32,
}
#[derive(Debug)]
pub struct ReflectionInfo {
pub texture_mapping: crate::FastHashMap<String, TextureMapping>,
pub uniforms: crate::FastHashMap<Handle<crate::GlobalVariable>, String>,
pub varying: crate::FastHashMap<String, VaryingLocation>,
pub immediates_items: Vec<ImmediateItem>,
pub clip_distance_count: u32,
}
#[derive(Debug, Clone)]
pub struct TextureMapping {
pub texture: Handle<crate::GlobalVariable>,
pub sampler: Option<Handle<crate::GlobalVariable>>,
}
#[derive(Debug, Clone)]
pub struct ImmediateItem {
pub access_path: String,
pub ty: Handle<crate::Type>,
pub offset: u32,
}
#[derive(Default)]
struct IdGenerator(u32);
impl IdGenerator {
const fn generate(&mut self) -> u32 {
let ret = self.0;
self.0 += 1;
ret
}
}
#[derive(Clone, Copy)]
struct VaryingOptions {
output: bool,
targeting_webgl: bool,
draw_parameters: bool,
}
impl VaryingOptions {
const fn from_writer_options(options: &Options, output: bool) -> Self {
Self {
output,
targeting_webgl: options.version.is_webgl(),
draw_parameters: options.writer_flags.contains(WriterFlags::DRAW_PARAMETERS),
}
}
}
struct VaryingName<'a> {
binding: &'a crate::Binding,
stage: ShaderStage,
options: VaryingOptions,
}
impl fmt::Display for VaryingName<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self.binding {
crate::Binding::Location {
blend_src: Some(1), ..
} => {
write!(f, "_fs2p_location1",)
}
crate::Binding::Location { location, .. } => {
let prefix = match (self.stage, self.options.output) {
(ShaderStage::Compute, _) => unreachable!(),
(ShaderStage::Vertex, false) => "p2vs",
(ShaderStage::Vertex, true) | (ShaderStage::Fragment, false) => "vs2fs",
(ShaderStage::Fragment, true) => "fs2p",
(
ShaderStage::Task
| ShaderStage::Mesh
| ShaderStage::RayGeneration
| ShaderStage::AnyHit
| ShaderStage::ClosestHit
| ShaderStage::Miss,
_,
) => unreachable!(),
};
write!(f, "_{prefix}_location{location}",)
}
crate::Binding::BuiltIn(built_in) => {
write!(f, "{}", glsl_built_in(built_in, self.options))
}
}
}
}
impl ShaderStage {
const fn to_str(self) -> &'static str {
match self {
ShaderStage::Compute => "cs",
ShaderStage::Fragment => "fs",
ShaderStage::Vertex => "vs",
ShaderStage::Task
| ShaderStage::Mesh
| ShaderStage::RayGeneration
| ShaderStage::AnyHit
| ShaderStage::ClosestHit
| ShaderStage::Miss => unreachable!(),
}
}
}
type BackendResult<T = ()> = Result<T, Error>;
#[derive(Debug, Error)]
pub enum Error {
#[error("Format error")]
FmtError(#[from] FmtError),
#[error("The selected version doesn't support {0:?}")]
MissingFeatures(Features),
#[error("Multiple immediates aren't supported")]
MultipleImmediateData,
#[error("The specified version isn't supported")]
VersionNotSupported,
#[error("The requested entry point couldn't be found")]
EntryPointNotFound,
#[error("A call was made to an unsupported external: {0}")]
UnsupportedExternal(String),
#[error("A scalar with an unsupported width was requested: {0:?}")]
UnsupportedScalar(crate::Scalar),
#[error("A image was used with multiple samplers")]
ImageMultipleSamplers,
#[error("{0}")]
Custom(String),
#[error("overrides should not be present at this stage")]
Override,
#[error("`{:?}` sampling is unsupported", crate::Sampling::First)]
FirstSamplingNotSupported,
#[error(transparent)]
ResolveArraySizeError(#[from] proc::ResolveArraySizeError),
}
enum BinaryOperation {
VectorCompare,
VectorComponentWise,
Modulo,
Other,
}
fn is_value_init_supported(module: &crate::Module, ty: Handle<crate::Type>) -> bool {
match module.types[ty].inner {
TypeInner::Scalar { .. } | TypeInner::Vector { .. } | TypeInner::Matrix { .. } => true,
TypeInner::Array { base, size, .. } => {
size != crate::ArraySize::Dynamic && is_value_init_supported(module, base)
}
TypeInner::Struct { ref members, .. } => members
.iter()
.all(|member| is_value_init_supported(module, member.ty)),
_ => false,
}
}
pub fn supported_capabilities() -> valid::Capabilities {
use valid::Capabilities as Caps;
Caps::IMMEDIATES
| Caps::FLOAT64
| Caps::PRIMITIVE_INDEX
| Caps::CLIP_DISTANCE
| Caps::MULTIVIEW
| Caps::EARLY_DEPTH_TEST
| Caps::MULTISAMPLED_SHADING
| Caps::DUAL_SOURCE_BLENDING
| Caps::CUBE_ARRAY_TEXTURES
| Caps::SHADER_INT64
| Caps::SHADER_INT64_ATOMIC_ALL_OPS
| Caps::TEXTURE_ATOMIC
| Caps::TEXTURE_INT64_ATOMIC
| Caps::SUBGROUP
| Caps::SUBGROUP_BARRIER
| Caps::SHADER_FLOAT16
| Caps::SHADER_FLOAT16_IN_FLOAT32
| Caps::SHADER_BARYCENTRICS
| Caps::DRAW_INDEX
| Caps::MEMORY_DECORATION_COHERENT
| Caps::MEMORY_DECORATION_VOLATILE
}