From 2aa4a82499d4becd2284cdb482213d541b8804dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 16:29:10 +0200 Subject: Adding upstream version 86.0.1. Signed-off-by: Daniel Baumann --- third_party/rust/naga/src/back/glsl.rs | 1579 ++++++++++++++++++++++++++++++++ 1 file changed, 1579 insertions(+) create mode 100644 third_party/rust/naga/src/back/glsl.rs (limited to 'third_party/rust/naga/src/back/glsl.rs') diff --git a/third_party/rust/naga/src/back/glsl.rs b/third_party/rust/naga/src/back/glsl.rs new file mode 100644 index 0000000000..6a497a675b --- /dev/null +++ b/third_party/rust/naga/src/back/glsl.rs @@ -0,0 +1,1579 @@ +//! OpenGL shading language backend +//! +//! The main structure is [`Writer`](struct.Writer.html), it maintains internal state that is used +//! to output a `Module` into glsl +//! +//! # Supported versions +//! ### Core +//! - 330 +//! - 400 +//! - 410 +//! - 420 +//! - 430 +//! - 450 +//! - 460 +//! +//! ### ES +//! - 300 +//! - 310 +//! + +use crate::{ + proc::{ + CallGraph, CallGraphBuilder, Interface, NameKey, Namer, ResolveContext, ResolveError, + Typifier, Visitor, + }, + Arena, ArraySize, BinaryOperator, BuiltIn, ConservativeDepth, Constant, ConstantInner, + DerivativeAxis, Expression, FastHashMap, Function, FunctionOrigin, GlobalVariable, Handle, + ImageClass, Interpolation, IntrinsicFunction, LocalVariable, Module, ScalarKind, ShaderStage, + Statement, StorageAccess, StorageClass, StorageFormat, StructMember, Type, TypeInner, + UnaryOperator, +}; +use std::{ + cmp::Ordering, + fmt::{self, Error as FmtError}, + io::{Error as IoError, Write}, +}; + +/// Contains a constant with a slice of all the reserved keywords RESERVED_KEYWORDS +mod keywords; + +const SUPPORTED_CORE_VERSIONS: &[u16] = &[330, 400, 410, 420, 430, 440, 450]; +const SUPPORTED_ES_VERSIONS: &[u16] = &[300, 310]; + +#[derive(Debug)] +pub enum Error { + FormatError(FmtError), + IoError(IoError), + Type(ResolveError), + Custom(String), +} + +impl From for Error { + fn from(err: FmtError) -> Self { + Error::FormatError(err) + } +} + +impl From for Error { + fn from(err: IoError) -> Self { + Error::IoError(err) + } +} + +impl From for Error { + fn from(err: ResolveError) -> Self { + Error::Type(err) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::FormatError(err) => write!(f, "Formatting error {}", err), + Error::IoError(err) => write!(f, "Io error: {}", err), + Error::Type(err) => write!(f, "Type error: {:?}", err), + Error::Custom(err) => write!(f, "{}", err), + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum Version { + Desktop(u16), + Embedded(u16), +} + +impl Version { + fn is_es(&self) -> bool { + match self { + Version::Desktop(_) => false, + Version::Embedded(_) => true, + } + } + + fn is_supported(&self) -> bool { + match self { + Version::Desktop(v) => SUPPORTED_CORE_VERSIONS.contains(v), + Version::Embedded(v) => SUPPORTED_ES_VERSIONS.contains(v), + } + } +} + +impl PartialOrd for Version { + fn partial_cmp(&self, other: &Self) -> Option { + match (*self, *other) { + (Version::Desktop(x), Version::Desktop(y)) => Some(x.cmp(&y)), + (Version::Embedded(x), Version::Embedded(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, "{} core", v), + Version::Embedded(v) => write!(f, "{} es", v), + } + } +} + +#[derive(Debug, Clone)] +pub struct Options { + pub version: Version, + pub entry_point: (ShaderStage, String), +} + +#[derive(Debug, Clone)] +pub struct TextureMapping { + pub texture: Handle, + pub sampler: Option>, +} + +bitflags::bitflags! { + struct Features: u32 { + const BUFFER_STORAGE = 1; + const ARRAY_OF_ARRAYS = 1 << 1; + const DOUBLE_TYPE = 1 << 2; + const FULL_IMAGE_FORMATS = 1 << 3; + const MULTISAMPLED_TEXTURES = 1 << 4; + const MULTISAMPLED_TEXTURE_ARRAYS = 1 << 5; + const CUBE_TEXTURES_ARRAY = 1 << 6; + const COMPUTE_SHADER = 1 << 7; + const IMAGE_LOAD_STORE = 1 << 8; + const CONSERVATIVE_DEPTH = 1 << 9; + const TEXTURE_1D = 1 << 10; + const PUSH_CONSTANT = 1 << 11; + } +} + +struct FeaturesManager(Features); + +impl FeaturesManager { + pub fn new() -> Self { + Self(Features::empty()) + } + + pub fn request(&mut self, features: Features) { + self.0 |= features + } + + #[allow(clippy::collapsible_if)] + pub fn write(&self, version: Version, mut out: impl Write) -> Result<(), Error> { + if self.0.contains(Features::COMPUTE_SHADER) { + if version < Version::Embedded(310) || version < Version::Desktop(420) { + return Err(Error::Custom(format!( + "Version {} doesn't support compute shaders", + version + ))); + } + + if !version.is_es() { + // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_compute_shader.txt + writeln!(out, "#extension GL_ARB_compute_shader : require")?; + } + } + + if self.0.contains(Features::BUFFER_STORAGE) { + if version < Version::Embedded(310) || version < Version::Desktop(400) { + return Err(Error::Custom(format!( + "Version {} doesn't support buffer storage class", + version + ))); + } + + if let Version::Desktop(_) = version { + // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shader_storage_buffer_object.txt + writeln!( + out, + "#extension GL_ARB_shader_storage_buffer_object : require" + )?; + } + } + + if self.0.contains(Features::DOUBLE_TYPE) { + if version.is_es() || version < Version::Desktop(150) { + return Err(Error::Custom(format!( + "Version {} doesn't support doubles", + version + ))); + } + + if version < Version::Desktop(400) { + // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_gpu_shader_fp64.txt + writeln!(out, "#extension GL_ARB_gpu_shader_fp64 : require")?; + } + } + + if self.0.contains(Features::CUBE_TEXTURES_ARRAY) { + if version < Version::Embedded(310) || version < Version::Desktop(130) { + return Err(Error::Custom(format!( + "Version {} doesn't support cube map array textures", + version + ))); + } + + if version.is_es() { + // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_cube_map_array.txt + writeln!(out, "#extension GL_EXT_texture_cube_map_array : require")?; + } else if version < Version::Desktop(400) { + // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_cube_map_array.txt + writeln!(out, "#extension GL_ARB_texture_cube_map_array : require")?; + } + } + + if self.0.contains(Features::MULTISAMPLED_TEXTURES) { + if version < Version::Embedded(300) { + return Err(Error::Custom(format!( + "Version {} doesn't support multi sampled textures", + version + ))); + } + } + + if self.0.contains(Features::MULTISAMPLED_TEXTURE_ARRAYS) { + if version < Version::Embedded(310) { + return Err(Error::Custom(format!( + "Version {} doesn't support multi sampled texture arrays", + version + ))); + } + + if version.is_es() { + // https://www.khronos.org/registry/OpenGL/extensions/OES/OES_texture_storage_multisample_2d_array.txt + writeln!( + out, + "#extension GL_OES_texture_storage_multisample_2d_array : require" + )?; + } + } + + if self.0.contains(Features::ARRAY_OF_ARRAYS) { + if version < Version::Embedded(310) || version < Version::Desktop(120) { + return Err(Error::Custom(format!( + "Version {} doesn't arrays of arrays", + version + ))); + } + + if version < Version::Desktop(430) { + // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_arrays_of_arrays.txt + writeln!(out, "#extension ARB_arrays_of_arrays : require")?; + } + } + + if self.0.contains(Features::IMAGE_LOAD_STORE) { + if version < Version::Embedded(310) || version < Version::Desktop(130) { + return Err(Error::Custom(format!( + "Version {} doesn't support images load/stores", + version + ))); + } + + if self.0.contains(Features::FULL_IMAGE_FORMATS) && version.is_es() { + // https://www.khronos.org/registry/OpenGL/extensions/NV/NV_image_formats.txt + writeln!(out, "#extension GL_NV_image_formats : require")?; + } + + if version < Version::Desktop(420) { + // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shader_image_load_store.txt + writeln!(out, "#extension GL_ARB_shader_image_load_store : require")?; + } + } + + if self.0.contains(Features::CONSERVATIVE_DEPTH) { + if version < Version::Embedded(300) || version < Version::Desktop(130) { + return Err(Error::Custom(format!( + "Version {} doesn't support conservative depth", + version + ))); + } + + if version.is_es() { + // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_conservative_depth.txt + writeln!(out, "#extension GL_EXT_conservative_depth : require")?; + } + + if version < Version::Desktop(420) { + // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_conservative_depth.txt + writeln!(out, "#extension GL_ARB_conservative_depth : require")?; + } + } + + if self.0.contains(Features::TEXTURE_1D) { + if version.is_es() { + return Err(Error::Custom(format!( + "Version {} doesn't support 1d textures", + version + ))); + } + } + + Ok(()) + } +} + +enum FunctionType { + Function(Handle), + EntryPoint(crate::proc::EntryPointIndex), +} + +struct FunctionCtx<'a, 'b> { + func: FunctionType, + expressions: &'a Arena, + typifier: &'b Typifier, +} + +impl<'a, 'b> FunctionCtx<'a, 'b> { + fn name_key(&self, local: Handle) -> NameKey { + match self.func { + FunctionType::Function(handle) => NameKey::FunctionLocal(handle, local), + FunctionType::EntryPoint(idx) => NameKey::EntryPointLocal(idx, local), + } + } + + fn get_arg<'c>(&self, arg: u32, names: &'c FastHashMap) -> &'c str { + match self.func { + FunctionType::Function(handle) => &names[&NameKey::FunctionArgument(handle, arg)], + FunctionType::EntryPoint(_) => unreachable!(), + } + } +} + +/// Helper structure that generates a number +#[derive(Default)] +struct IdGenerator(u32); + +impl IdGenerator { + fn generate(&mut self) -> u32 { + let ret = self.0; + self.0 += 1; + ret + } +} + +/// Main structure of the glsl backend responsible for all code generation +pub struct Writer<'a, W> { + // Inputs + module: &'a Module, + out: W, + options: &'a Options, + + // Internal State + features: FeaturesManager, + names: FastHashMap, + entry_point: &'a crate::EntryPoint, + entry_point_idx: crate::proc::EntryPointIndex, + call_graph: CallGraph, + + /// Used to generate a unique number for blocks + block_id: IdGenerator, +} + +impl<'a, W: Write> Writer<'a, W> { + pub fn new(out: W, module: &'a Module, options: &'a Options) -> Result { + if !options.version.is_supported() { + return Err(Error::Custom(format!( + "Version not supported {}", + options.version + ))); + } + + let (ep_idx, ep) = module + .entry_points + .iter() + .enumerate() + .find_map(|(i, (key, entry_point))| { + Some((i as u16, entry_point)).filter(|_| &options.entry_point == key) + }) + .ok_or_else(|| Error::Custom(String::from("Entry point not found")))?; + + let mut names = FastHashMap::default(); + Namer::process(module, keywords::RESERVED_KEYWORDS, &mut names); + + let call_graph = CallGraphBuilder { + functions: &module.functions, + } + .process(&ep.function); + + let mut this = Self { + module, + out, + options, + + features: FeaturesManager::new(), + names, + entry_point: ep, + entry_point_idx: ep_idx, + call_graph, + + block_id: IdGenerator::default(), + }; + + this.collect_required_features()?; + + Ok(this) + } + + fn collect_required_features(&mut self) -> Result<(), Error> { + let stage = self.options.entry_point.0; + + if let Some(depth_test) = self.entry_point.early_depth_test { + self.features.request(Features::IMAGE_LOAD_STORE); + + if depth_test.conservative.is_some() { + self.features.request(Features::CONSERVATIVE_DEPTH); + } + } + + if let ShaderStage::Compute = stage { + self.features.request(Features::COMPUTE_SHADER) + } + + for (_, ty) in self.module.types.iter() { + match ty.inner { + TypeInner::Scalar { kind, width } => self.scalar_required_features(kind, width), + TypeInner::Vector { kind, width, .. } => self.scalar_required_features(kind, width), + TypeInner::Matrix { .. } => self.scalar_required_features(ScalarKind::Float, 8), + TypeInner::Array { base, .. } => { + if let TypeInner::Array { .. } = self.module.types[base].inner { + self.features.request(Features::ARRAY_OF_ARRAYS) + } + } + TypeInner::Image { + dim, + arrayed, + class, + } => { + if arrayed && dim == crate::ImageDimension::Cube { + self.features.request(Features::CUBE_TEXTURES_ARRAY) + } else if dim == crate::ImageDimension::D1 { + self.features.request(Features::TEXTURE_1D) + } + + match class { + ImageClass::Sampled { multi: true, .. } => { + self.features.request(Features::MULTISAMPLED_TEXTURES); + if arrayed { + self.features.request(Features::MULTISAMPLED_TEXTURE_ARRAYS); + } + } + ImageClass::Storage(format) => match format { + StorageFormat::R8Unorm + | StorageFormat::R8Snorm + | StorageFormat::R8Uint + | StorageFormat::R8Sint + | StorageFormat::R16Uint + | StorageFormat::R16Sint + | StorageFormat::R16Float + | StorageFormat::Rg8Unorm + | StorageFormat::Rg8Snorm + | StorageFormat::Rg8Uint + | StorageFormat::Rg8Sint + | StorageFormat::Rg16Uint + | StorageFormat::Rg16Sint + | StorageFormat::Rg16Float + | StorageFormat::Rgb10a2Unorm + | StorageFormat::Rg11b10Float + | StorageFormat::Rg32Uint + | StorageFormat::Rg32Sint + | StorageFormat::Rg32Float => { + self.features.request(Features::FULL_IMAGE_FORMATS) + } + _ => {} + }, + _ => {} + } + } + _ => {} + } + } + + for (_, global) in self.module.global_variables.iter() { + match global.class { + StorageClass::WorkGroup => self.features.request(Features::COMPUTE_SHADER), + StorageClass::Storage => self.features.request(Features::BUFFER_STORAGE), + StorageClass::PushConstant => self.features.request(Features::PUSH_CONSTANT), + _ => {} + } + } + + Ok(()) + } + + fn scalar_required_features(&mut self, kind: ScalarKind, width: crate::Bytes) { + if kind == ScalarKind::Float && width == 8 { + self.features.request(Features::DOUBLE_TYPE); + } + } + + pub fn write(&mut self) -> Result, Error> { + let es = self.options.version.is_es(); + + writeln!(self.out, "#version {}", self.options.version)?; + self.features.write(self.options.version, &mut self.out)?; + writeln!(self.out)?; + + if es { + writeln!(self.out, "precision highp float;\n")?; + } + + if let Some(depth_test) = self.entry_point.early_depth_test { + writeln!(self.out, "layout(early_fragment_tests) in;\n")?; + + if let Some(conservative) = depth_test.conservative { + writeln!( + self.out, + "layout (depth_{}) out float gl_FragDepth;\n", + match conservative { + ConservativeDepth::GreaterEqual => "greater", + ConservativeDepth::LessEqual => "less", + ConservativeDepth::Unchanged => "unchanged", + } + )?; + } + } + + for (handle, ty) in self.module.types.iter() { + if let TypeInner::Struct { ref members } = ty.inner { + self.write_struct(handle, members)? + } + } + + writeln!(self.out)?; + + let texture_mappings = self.collect_texture_mapping( + self.call_graph + .raw_nodes() + .iter() + .map(|node| &self.module.functions[node.weight]) + .chain(std::iter::once(&self.entry_point.function)), + )?; + + for (handle, global) in self + .module + .global_variables + .iter() + .zip(&self.entry_point.function.global_usage) + .filter_map(|(global, usage)| Some(global).filter(|_| !usage.is_empty())) + { + if let Some(crate::Binding::BuiltIn(_)) = global.binding { + continue; + } + + match self.module.types[global.ty].inner { + TypeInner::Image { + dim, + arrayed, + class, + } => { + if let TypeInner::Image { + class: ImageClass::Storage(format), + .. + } = self.module.types[global.ty].inner + { + write!(self.out, "layout({}) ", glsl_storage_format(format))?; + } + + if global.storage_access == StorageAccess::LOAD { + write!(self.out, "readonly ")?; + } else if global.storage_access == StorageAccess::STORE { + write!(self.out, "writeonly ")?; + } + + write!(self.out, "uniform ")?; + + self.write_image_type(dim, arrayed, class)?; + + writeln!( + self.out, + " {};", + self.names[&NameKey::GlobalVariable(handle)] + )? + } + TypeInner::Sampler { .. } => continue, + _ => self.write_global(handle, global)?, + } + } + + writeln!(self.out)?; + + // Sort the graph topologically so that functions calls are valid + // It's impossible for this to panic because the IR forbids cycles + let functions = petgraph::algo::toposort(&self.call_graph, None).unwrap(); + + for node in functions { + let handle = self.call_graph[node]; + let name = self.names[&NameKey::Function(handle)].clone(); + self.write_function( + FunctionType::Function(handle), + &self.module.functions[handle], + name, + )?; + } + + self.write_function( + FunctionType::EntryPoint(self.entry_point_idx), + &self.entry_point.function, + "main", + )?; + + Ok(texture_mappings) + } + + fn write_global( + &mut self, + handle: Handle, + global: &GlobalVariable, + ) -> Result<(), Error> { + if global.storage_access == StorageAccess::LOAD { + write!(self.out, "readonly ")?; + } else if global.storage_access == StorageAccess::STORE { + write!(self.out, "writeonly ")?; + } + + if let Some(interpolation) = global.interpolation { + match (self.options.entry_point.0, global.class) { + (ShaderStage::Fragment, StorageClass::Input) + | (ShaderStage::Vertex, StorageClass::Output) => { + write!(self.out, "{} ", glsl_interpolation(interpolation)?)?; + } + _ => (), + }; + } + + let block = match global.class { + StorageClass::Storage | StorageClass::Uniform => { + let block_name = self.names[&NameKey::Type(global.ty)].clone(); + + Some(block_name) + } + _ => None, + }; + + write!(self.out, "{} ", glsl_storage_class(global.class))?; + + self.write_type(global.ty, block)?; + + let name = &self.names[&NameKey::GlobalVariable(handle)]; + writeln!(self.out, " {};", name)?; + + Ok(()) + } + + fn write_function>( + &mut self, + ty: FunctionType, + func: &Function, + name: N, + ) -> Result<(), Error> { + let mut typifier = Typifier::new(); + + typifier.resolve_all( + &func.expressions, + &self.module.types, + &ResolveContext { + constants: &self.module.constants, + global_vars: &self.module.global_variables, + local_vars: &func.local_variables, + functions: &self.module.functions, + arguments: &func.arguments, + }, + )?; + + let ctx = FunctionCtx { + func: ty, + expressions: &func.expressions, + typifier: &typifier, + }; + + self.write_fn_header(name.as_ref(), func, &ctx)?; + writeln!(self.out, " {{",)?; + + for (handle, local) in func.local_variables.iter() { + write!(self.out, "\t")?; + self.write_type(local.ty, None)?; + + write!(self.out, " {}", self.names[&ctx.name_key(handle)])?; + + if let Some(init) = local.init { + write!(self.out, " = ",)?; + + self.write_constant(&self.module.constants[init])?; + } + + writeln!(self.out, ";")? + } + + writeln!(self.out)?; + + for sta in func.body.iter() { + self.write_stmt(sta, &ctx, 1)?; + } + + Ok(writeln!(self.out, "}}")?) + } + + fn write_slice Result<(), Error>>( + &mut self, + data: &[T], + mut f: F, + ) -> Result<(), Error> { + for (i, item) in data.iter().enumerate() { + f(self, i as u32, item)?; + + if i != data.len().saturating_sub(1) { + write!(self.out, ",")?; + } + } + + Ok(()) + } + + fn write_fn_header( + &mut self, + name: &str, + func: &Function, + ctx: &FunctionCtx<'_, '_>, + ) -> Result<(), Error> { + if let Some(ty) = func.return_type { + self.write_type(ty, None)?; + } else { + write!(self.out, "void")?; + } + + write!(self.out, " {}(", name)?; + + self.write_slice(&func.arguments, |this, i, arg| { + this.write_type(arg.ty, None)?; + + let name = ctx.get_arg(i, &this.names); + + Ok(write!(this.out, " {}", name)?) + })?; + + write!(self.out, ")")?; + + Ok(()) + } + + fn write_type(&mut self, ty: Handle, block: Option) -> Result<(), Error> { + match self.module.types[ty].inner { + TypeInner::Scalar { kind, width } => { + write!(self.out, "{}", glsl_scalar(kind, width)?.full)? + } + TypeInner::Vector { size, kind, width } => write!( + self.out, + "{}vec{}", + glsl_scalar(kind, width)?.prefix, + size as u8 + )?, + TypeInner::Matrix { + columns, + rows, + width, + } => write!( + self.out, + "{}mat{}x{}", + glsl_scalar(ScalarKind::Float, width)?.prefix, + columns as u8, + rows as u8 + )?, + TypeInner::Pointer { base, .. } => self.write_type(base, None)?, + TypeInner::Array { base, size, .. } => { + self.write_type(base, None)?; + + write!(self.out, "[")?; + self.write_array_size(size)?; + write!(self.out, "]")? + } + TypeInner::Struct { ref members } => { + if let Some(name) = block { + writeln!(self.out, "{}_block_{} {{", name, self.block_id.generate())?; + + for (idx, member) in members.iter().enumerate() { + self.write_type(member.ty, None)?; + + writeln!( + self.out, + " {};", + &self.names[&NameKey::StructMember(ty, idx as u32)] + )?; + } + + write!(self.out, "}}")? + } else { + write!(self.out, "{}", &self.names[&NameKey::Type(ty)])? + } + } + _ => unreachable!(), + } + + Ok(()) + } + + fn write_image_type( + &mut self, + dim: crate::ImageDimension, + arrayed: bool, + class: ImageClass, + ) -> Result<(), Error> { + let (base, kind, ms, comparison) = match class { + ImageClass::Sampled { kind, multi: true } => ("sampler", kind, "MS", ""), + ImageClass::Sampled { kind, multi: false } => ("sampler", kind, "", ""), + ImageClass::Depth => ("sampler", crate::ScalarKind::Float, "", "Shadow"), + ImageClass::Storage(format) => ("image", format.into(), "", ""), + }; + + Ok(write!( + self.out, + "{}{}{}{}{}{}", + glsl_scalar(kind, 4)?.prefix, + base, + ImageDimension(dim), + ms, + if arrayed { "Array" } else { "" }, + comparison + )?) + } + + fn write_array_size(&mut self, size: ArraySize) -> Result<(), Error> { + match size { + ArraySize::Constant(const_handle) => match self.module.constants[const_handle].inner { + ConstantInner::Uint(size) => write!(self.out, "{}", size)?, + _ => unreachable!(), + }, + ArraySize::Dynamic => (), + } + + Ok(()) + } + + fn collect_texture_mapping( + &self, + functions: impl Iterator, + ) -> Result, Error> { + let mut mappings = FastHashMap::default(); + + for func in functions { + let mut interface = Interface { + expressions: &func.expressions, + local_variables: &func.local_variables, + visitor: TextureMappingVisitor { + names: &self.names, + expressions: &func.expressions, + map: &mut mappings, + error: None, + }, + }; + interface.traverse(&func.body); + + if let Some(error) = interface.visitor.error { + return Err(error); + } + } + + Ok(mappings) + } + + fn write_struct( + &mut self, + handle: Handle, + members: &[StructMember], + ) -> Result<(), Error> { + writeln!(self.out, "struct {} {{", self.names[&NameKey::Type(handle)])?; + + for (idx, member) in members.iter().enumerate() { + write!(self.out, "\t")?; + self.write_type(member.ty, None)?; + writeln!( + self.out, + " {};", + self.names[&NameKey::StructMember(handle, idx as u32)] + )?; + } + + writeln!(self.out, "}};")?; + Ok(()) + } + + fn write_stmt( + &mut self, + sta: &Statement, + ctx: &FunctionCtx<'_, '_>, + indent: usize, + ) -> Result<(), Error> { + write!(self.out, "{}", "\t".repeat(indent))?; + + match sta { + Statement::Block(block) => { + writeln!(self.out, "{{")?; + for sta in block.iter() { + self.write_stmt(sta, ctx, indent + 1)? + } + writeln!(self.out, "{}}}", "\t".repeat(indent))? + } + Statement::If { + condition, + accept, + reject, + } => { + write!(self.out, "if(")?; + self.write_expr(*condition, ctx)?; + writeln!(self.out, ") {{")?; + + for sta in accept { + self.write_stmt(sta, ctx, indent + 1)?; + } + + if !reject.is_empty() { + writeln!(self.out, "{}}} else {{", "\t".repeat(indent))?; + + for sta in reject { + self.write_stmt(sta, ctx, indent + 1)?; + } + } + + writeln!(self.out, "{}}}", "\t".repeat(indent))? + } + Statement::Switch { + selector, + cases, + default, + } => { + write!(self.out, "switch(")?; + self.write_expr(*selector, ctx)?; + writeln!(self.out, ") {{")?; + + for (label, (block, fallthrough)) in cases { + writeln!(self.out, "{}case {}:", "\t".repeat(indent + 1), label)?; + + for sta in block { + self.write_stmt(sta, ctx, indent + 2)?; + } + + if fallthrough.is_none() { + writeln!(self.out, "{}break;", "\t".repeat(indent + 2))?; + } + } + + if !default.is_empty() { + writeln!(self.out, "{}default:", "\t".repeat(indent + 1))?; + + for sta in default { + self.write_stmt(sta, ctx, indent + 2)?; + } + } + + writeln!(self.out, "{}}}", "\t".repeat(indent))? + } + Statement::Loop { body, continuing } => { + writeln!(self.out, "while(true) {{")?; + + for sta in body.iter().chain(continuing.iter()) { + self.write_stmt(sta, ctx, indent + 1)?; + } + + writeln!(self.out, "{}}}", "\t".repeat(indent))? + } + Statement::Break => writeln!(self.out, "break;")?, + Statement::Continue => writeln!(self.out, "continue;")?, + Statement::Return { value } => { + write!(self.out, "return")?; + if let Some(expr) = value { + write!(self.out, " ")?; + self.write_expr(*expr, ctx)?; + } + writeln!(self.out, ";")?; + } + Statement::Kill => writeln!(self.out, "discard;")?, + Statement::Store { pointer, value } => { + self.write_expr(*pointer, ctx)?; + write!(self.out, " = ")?; + self.write_expr(*value, ctx)?; + writeln!(self.out, ";")? + } + } + + Ok(()) + } + + fn write_expr( + &mut self, + expr: Handle, + ctx: &FunctionCtx<'_, '_>, + ) -> Result<(), Error> { + match ctx.expressions[expr] { + Expression::Access { base, index } => { + self.write_expr(base, ctx)?; + write!(self.out, "[")?; + self.write_expr(index, ctx)?; + write!(self.out, "]")? + } + Expression::AccessIndex { base, index } => { + self.write_expr(base, ctx)?; + + match ctx.typifier.get(base, &self.module.types) { + TypeInner::Vector { .. } + | TypeInner::Matrix { .. } + | TypeInner::Array { .. } => write!(self.out, "[{}]", index)?, + TypeInner::Struct { .. } => { + let ty = ctx.typifier.get_handle(base).unwrap(); + + write!( + self.out, + ".{}", + &self.names[&NameKey::StructMember(ty, index)] + )? + } + ref other => return Err(Error::Custom(format!("Cannot index {:?}", other))), + } + } + Expression::Constant(constant) => { + self.write_constant(&self.module.constants[constant])? + } + Expression::Compose { ty, ref components } => { + match self.module.types[ty].inner { + TypeInner::Vector { .. } + | TypeInner::Matrix { .. } + | TypeInner::Array { .. } + | TypeInner::Struct { .. } => self.write_type(ty, None)?, + _ => unreachable!(), + } + + write!(self.out, "(")?; + self.write_slice(components, |this, _, arg| this.write_expr(*arg, ctx))?; + write!(self.out, ")")? + } + Expression::FunctionArgument(pos) => { + write!(self.out, "{}", ctx.get_arg(pos, &self.names))? + } + Expression::GlobalVariable(handle) => { + if let Some(crate::Binding::BuiltIn(built_in)) = + self.module.global_variables[handle].binding + { + write!(self.out, "{}", glsl_built_in(built_in))? + } else { + write!( + self.out, + "{}", + &self.names[&NameKey::GlobalVariable(handle)] + )? + } + } + Expression::LocalVariable(handle) => { + write!(self.out, "{}", self.names[&ctx.name_key(handle)])? + } + Expression::Load { pointer } => self.write_expr(pointer, ctx)?, + Expression::ImageSample { + image, + coordinate, + level, + depth_ref, + .. + } => { + //TODO: handle MS + write!( + self.out, + "{}(", + match level { + crate::SampleLevel::Auto | crate::SampleLevel::Bias(_) => "texture", + crate::SampleLevel::Zero | crate::SampleLevel::Exact(_) => "textureLod", + } + )?; + self.write_expr(image, ctx)?; + write!(self.out, ", ")?; + + let size = match *ctx.typifier.get(coordinate, &self.module.types) { + TypeInner::Vector { size, .. } => size, + ref other => { + return Err(Error::Custom(format!( + "Cannot sample with coordinates of type {:?}", + other + ))) + } + }; + + if let Some(depth_ref) = depth_ref { + write!(self.out, "vec{}(", size as u8 + 1)?; + self.write_expr(coordinate, ctx)?; + write!(self.out, ", ")?; + self.write_expr(depth_ref, ctx)?; + write!(self.out, ")")? + } else { + self.write_expr(coordinate, ctx)? + } + + match level { + crate::SampleLevel::Auto => (), + crate::SampleLevel::Zero => write!(self.out, ", 0")?, + crate::SampleLevel::Exact(expr) | crate::SampleLevel::Bias(expr) => { + write!(self.out, ", ")?; + self.write_expr(expr, ctx)?; + } + } + + write!(self.out, ")")? + } + Expression::ImageLoad { + image, + coordinate, + index, + } => { + let class = match ctx.typifier.get(image, &self.module.types) { + TypeInner::Image { class, .. } => class, + _ => unreachable!(), + }; + + match class { + ImageClass::Sampled { .. } => write!(self.out, "texelFetch(")?, + ImageClass::Storage(_) => write!(self.out, "imageLoad(")?, + ImageClass::Depth => todo!(), + } + + self.write_expr(image, ctx)?; + write!(self.out, ", ")?; + self.write_expr(coordinate, ctx)?; + + match class { + ImageClass::Sampled { .. } => { + write!(self.out, ", ")?; + self.write_expr(index.unwrap(), ctx)?; + write!(self.out, ")")? + } + ImageClass::Storage(_) => write!(self.out, ")")?, + ImageClass::Depth => todo!(), + } + } + Expression::Unary { op, expr } => { + write!( + self.out, + "({} ", + match op { + UnaryOperator::Negate => "-", + UnaryOperator::Not => match *ctx.typifier.get(expr, &self.module.types) { + TypeInner::Scalar { + kind: ScalarKind::Sint, + .. + } => "~", + TypeInner::Scalar { + kind: ScalarKind::Uint, + .. + } => "~", + TypeInner::Scalar { + kind: ScalarKind::Bool, + .. + } => "!", + ref other => + return Err(Error::Custom(format!( + "Cannot apply not to type {:?}", + other + ))), + }, + } + )?; + + self.write_expr(expr, ctx)?; + + write!(self.out, ")")? + } + Expression::Binary { op, left, right } => { + write!(self.out, "(")?; + self.write_expr(left, ctx)?; + + write!( + self.out, + " {} ", + match op { + BinaryOperator::Add => "+", + BinaryOperator::Subtract => "-", + BinaryOperator::Multiply => "*", + BinaryOperator::Divide => "/", + BinaryOperator::Modulo => "%", + BinaryOperator::Equal => "==", + BinaryOperator::NotEqual => "!=", + BinaryOperator::Less => "<", + BinaryOperator::LessEqual => "<=", + BinaryOperator::Greater => ">", + BinaryOperator::GreaterEqual => ">=", + BinaryOperator::And => "&", + BinaryOperator::ExclusiveOr => "^", + BinaryOperator::InclusiveOr => "|", + BinaryOperator::LogicalAnd => "&&", + BinaryOperator::LogicalOr => "||", + BinaryOperator::ShiftLeft => "<<", + BinaryOperator::ShiftRight => ">>", + } + )?; + + self.write_expr(right, ctx)?; + + write!(self.out, ")")? + } + Expression::Select { + condition, + accept, + reject, + } => { + write!(self.out, "(")?; + self.write_expr(condition, ctx)?; + write!(self.out, " ? ")?; + self.write_expr(accept, ctx)?; + write!(self.out, " : ")?; + self.write_expr(reject, ctx)?; + write!(self.out, ")")? + } + Expression::Intrinsic { fun, argument } => { + write!( + self.out, + "{}(", + match fun { + IntrinsicFunction::IsFinite => "!isinf", + IntrinsicFunction::IsInf => "isinf", + IntrinsicFunction::IsNan => "isnan", + IntrinsicFunction::IsNormal => "!isnan", + IntrinsicFunction::All => "all", + IntrinsicFunction::Any => "any", + } + )?; + + self.write_expr(argument, ctx)?; + + write!(self.out, ")")? + } + Expression::Transpose(matrix) => { + write!(self.out, "transpose(")?; + self.write_expr(matrix, ctx)?; + write!(self.out, ")")? + } + Expression::DotProduct(left, right) => { + write!(self.out, "dot(")?; + self.write_expr(left, ctx)?; + write!(self.out, ", ")?; + self.write_expr(right, ctx)?; + write!(self.out, ")")? + } + Expression::CrossProduct(left, right) => { + write!(self.out, "cross(")?; + self.write_expr(left, ctx)?; + write!(self.out, ", ")?; + self.write_expr(right, ctx)?; + write!(self.out, ")")? + } + Expression::As { + expr, + kind, + convert, + } => { + if convert { + self.write_type(ctx.typifier.get_handle(expr).unwrap(), None)?; + } else { + let source_kind = match *ctx.typifier.get(expr, &self.module.types) { + TypeInner::Scalar { + kind: source_kind, .. + } => source_kind, + TypeInner::Vector { + kind: source_kind, .. + } => source_kind, + _ => unreachable!(), + }; + + write!( + self.out, + "{}", + match (source_kind, kind) { + (ScalarKind::Float, ScalarKind::Sint) => "floatBitsToInt", + (ScalarKind::Float, ScalarKind::Uint) => "floatBitsToUInt", + (ScalarKind::Sint, ScalarKind::Float) => "intBitsToFloat", + (ScalarKind::Uint, ScalarKind::Float) => "uintBitsToFloat", + _ => { + return Err(Error::Custom(format!( + "Cannot bitcast {:?} to {:?}", + source_kind, kind + ))); + } + } + )?; + } + + write!(self.out, "(")?; + self.write_expr(expr, ctx)?; + write!(self.out, ")")? + } + Expression::Derivative { axis, expr } => { + write!( + self.out, + "{}(", + match axis { + DerivativeAxis::X => "dFdx", + DerivativeAxis::Y => "dFdy", + DerivativeAxis::Width => "fwidth", + } + )?; + self.write_expr(expr, ctx)?; + write!(self.out, ")")? + } + Expression::Call { + origin: FunctionOrigin::Local(ref function), + ref arguments, + } => { + write!(self.out, "{}(", &self.names[&NameKey::Function(*function)])?; + self.write_slice(arguments, |this, _, arg| this.write_expr(*arg, ctx))?; + write!(self.out, ")")? + } + Expression::Call { + origin: crate::FunctionOrigin::External(ref name), + ref arguments, + } => match name.as_str() { + "cos" | "normalize" | "sin" | "length" | "abs" | "floor" | "inverse" + | "distance" | "dot" | "min" | "max" | "reflect" | "pow" | "step" | "cross" + | "fclamp" | "clamp" | "mix" | "smoothstep" => { + let name = match name.as_str() { + "fclamp" => "clamp", + name => name, + }; + + write!(self.out, "{}(", name)?; + self.write_slice(arguments, |this, _, arg| this.write_expr(*arg, ctx))?; + write!(self.out, ")")? + } + "atan2" => { + write!(self.out, "atan(")?; + self.write_expr(arguments[1], ctx)?; + write!(self.out, ", ")?; + self.write_expr(arguments[0], ctx)?; + write!(self.out, ")")? + } + other => { + return Err(Error::Custom(format!( + "Unsupported function call {}", + other + ))) + } + }, + Expression::ArrayLength(expr) => { + write!(self.out, "uint(")?; + self.write_expr(expr, ctx)?; + write!(self.out, ".length())")? + } + } + + Ok(()) + } + + fn write_constant(&mut self, constant: &Constant) -> Result<(), Error> { + match constant.inner { + ConstantInner::Sint(int) => write!(self.out, "{}", int)?, + ConstantInner::Uint(int) => write!(self.out, "{}u", int)?, + ConstantInner::Float(float) => write!(self.out, "{:?}", float)?, + ConstantInner::Bool(boolean) => write!(self.out, "{}", boolean)?, + ConstantInner::Composite(ref components) => { + self.write_type(constant.ty, None)?; + write!(self.out, "(")?; + self.write_slice(components, |this, _, arg| { + this.write_constant(&this.module.constants[*arg]) + })?; + write!(self.out, ")")? + } + } + + Ok(()) + } +} + +struct ScalarString<'a> { + prefix: &'a str, + full: &'a str, +} + +fn glsl_scalar(kind: ScalarKind, width: crate::Bytes) -> Result, Error> { + Ok(match kind { + ScalarKind::Sint => ScalarString { + prefix: "i", + full: "int", + }, + ScalarKind::Uint => ScalarString { + prefix: "u", + full: "uint", + }, + ScalarKind::Float => match width { + 4 => ScalarString { + prefix: "", + full: "float", + }, + 8 => ScalarString { + prefix: "d", + full: "double", + }, + _ => { + return Err(Error::Custom(format!( + "Cannot build float of width {}", + width + ))) + } + }, + ScalarKind::Bool => ScalarString { + prefix: "b", + full: "bool", + }, + }) +} + +fn glsl_built_in(built_in: BuiltIn) -> &'static str { + match built_in { + BuiltIn::Position => "gl_Position", + BuiltIn::GlobalInvocationId => "gl_GlobalInvocationID", + BuiltIn::BaseInstance => "gl_BaseInstance", + BuiltIn::BaseVertex => "gl_BaseVertex", + BuiltIn::ClipDistance => "gl_ClipDistance", + BuiltIn::InstanceIndex => "gl_InstanceIndex", + BuiltIn::VertexIndex => "gl_VertexIndex", + BuiltIn::PointSize => "gl_PointSize", + BuiltIn::FragCoord => "gl_FragCoord", + BuiltIn::FrontFacing => "gl_FrontFacing", + BuiltIn::SampleIndex => "gl_SampleID", + BuiltIn::FragDepth => "gl_FragDepth", + BuiltIn::LocalInvocationId => "gl_LocalInvocationID", + BuiltIn::LocalInvocationIndex => "gl_LocalInvocationIndex", + BuiltIn::WorkGroupId => "gl_WorkGroupID", + } +} + +fn glsl_storage_class(class: StorageClass) -> &'static str { + match class { + StorageClass::Function => "", + StorageClass::Input => "in", + StorageClass::Output => "out", + StorageClass::Private => "", + StorageClass::Storage => "buffer", + StorageClass::Uniform => "uniform", + StorageClass::Handle => "uniform", + StorageClass::WorkGroup => "shared", + StorageClass::PushConstant => "", + } +} + +fn glsl_interpolation(interpolation: Interpolation) -> Result<&'static str, Error> { + Ok(match interpolation { + Interpolation::Perspective => "smooth", + Interpolation::Linear => "noperspective", + Interpolation::Flat => "flat", + Interpolation::Centroid => "centroid", + Interpolation::Sample => "sample", + Interpolation::Patch => { + return Err(Error::Custom( + "patch interpolation qualifier not supported".to_string(), + )) + } + }) +} + +struct ImageDimension(crate::ImageDimension); +impl fmt::Display for ImageDimension { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self.0 { + crate::ImageDimension::D1 => "1D", + crate::ImageDimension::D2 => "2D", + crate::ImageDimension::D3 => "3D", + crate::ImageDimension::Cube => "Cube", + } + ) + } +} + +fn glsl_storage_format(format: StorageFormat) -> &'static str { + match format { + StorageFormat::R8Unorm => "r8", + StorageFormat::R8Snorm => "r8_snorm", + StorageFormat::R8Uint => "r8ui", + StorageFormat::R8Sint => "r8i", + StorageFormat::R16Uint => "r16ui", + StorageFormat::R16Sint => "r16i", + StorageFormat::R16Float => "r16f", + StorageFormat::Rg8Unorm => "rg8", + StorageFormat::Rg8Snorm => "rg8_snorm", + StorageFormat::Rg8Uint => "rg8ui", + StorageFormat::Rg8Sint => "rg8i", + StorageFormat::R32Uint => "r32ui", + StorageFormat::R32Sint => "r32i", + StorageFormat::R32Float => "r32f", + StorageFormat::Rg16Uint => "rg16ui", + StorageFormat::Rg16Sint => "rg16i", + StorageFormat::Rg16Float => "rg16f", + StorageFormat::Rgba8Unorm => "rgba8ui", + StorageFormat::Rgba8Snorm => "rgba8_snorm", + StorageFormat::Rgba8Uint => "rgba8ui", + StorageFormat::Rgba8Sint => "rgba8i", + StorageFormat::Rgb10a2Unorm => "rgb10_a2ui", + StorageFormat::Rg11b10Float => "r11f_g11f_b10f", + StorageFormat::Rg32Uint => "rg32ui", + StorageFormat::Rg32Sint => "rg32i", + StorageFormat::Rg32Float => "rg32f", + StorageFormat::Rgba16Uint => "rgba16ui", + StorageFormat::Rgba16Sint => "rgba16i", + StorageFormat::Rgba16Float => "rgba16f", + StorageFormat::Rgba32Uint => "rgba32ui", + StorageFormat::Rgba32Sint => "rgba32i", + StorageFormat::Rgba32Float => "rgba32f", + } +} + +struct TextureMappingVisitor<'a> { + names: &'a FastHashMap, + expressions: &'a Arena, + map: &'a mut FastHashMap, + error: Option, +} + +impl<'a> Visitor for TextureMappingVisitor<'a> { + fn visit_expr(&mut self, expr: &crate::Expression) { + match expr { + Expression::ImageSample { image, sampler, .. } => { + let tex_handle = match self.expressions[*image] { + Expression::GlobalVariable(global) => global, + _ => unreachable!(), + }; + let tex_name = self.names[&NameKey::GlobalVariable(tex_handle)].clone(); + + let sampler_handle = match self.expressions[*sampler] { + Expression::GlobalVariable(global) => global, + _ => unreachable!(), + }; + + let mapping = self.map.entry(tex_name).or_insert(TextureMapping { + texture: tex_handle, + sampler: Some(sampler_handle), + }); + + if mapping.sampler != Some(sampler_handle) { + self.error = Some(Error::Custom(String::from( + "Cannot use texture with two different samplers", + ))); + } + } + Expression::ImageLoad { image, .. } => { + let tex_handle = match self.expressions[*image] { + Expression::GlobalVariable(global) => global, + _ => unreachable!(), + }; + let tex_name = self.names[&NameKey::GlobalVariable(tex_handle)].clone(); + + let mapping = self.map.entry(tex_name).or_insert(TextureMapping { + texture: tex_handle, + sampler: None, + }); + + if mapping.sampler != None { + self.error = Some(Error::Custom(String::from( + "Cannot use texture with two different samplers", + ))); + } + } + _ => {} + } + } +} -- cgit v1.2.3