From dd5021234d8550723c9cf2a3d3d102daf2aec672 Mon Sep 17 00:00:00 2001 From: Capucho Date: Sat, 21 Nov 2020 18:45:05 +0000 Subject: [PATCH] [glsl-out] Reworked the backend The backend now works in a similar way to the msl backend Should require less loops, allocations and backtracking Overall just better to work with it Added a build script which reads a file with the glsl keywords and generates a slice to be used in the Namer Added a way to build a call graph --- Cargo.toml | 3 +- build.rs | 26 + examples/convert.rs | 7 +- glsl-keywords.txt | 20 + src/back/glsl.rs | 2641 ++++++++++++++++++---------------------- src/proc/call_graph.rs | 74 ++ src/proc/mod.rs | 4 + 7 files changed, 1300 insertions(+), 1475 deletions(-) create mode 100644 build.rs create mode 100644 glsl-keywords.txt create mode 100644 src/proc/call_graph.rs diff --git a/Cargo.toml b/Cargo.toml index 5cf912cc22..c79c225739 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ homepage = "https://github.com/gfx-rs/naga" repository = "https://github.com/gfx-rs/naga" keywords = ["shader", "SPIR-V"] license = "MIT OR Apache-2.0" +build = "build.rs" [dependencies] bitflags = "1" @@ -24,7 +25,7 @@ petgraph = { version ="0.5", optional = true } default = [] glsl-in = ["pomelo"] glsl-validate = [] -glsl-out = [] +glsl-out = ["petgraph"] msl-out = [] serialize = ["serde"] deserialize = ["serde"] diff --git a/build.rs b/build.rs new file mode 100644 index 0000000000..340a5d8287 --- /dev/null +++ b/build.rs @@ -0,0 +1,26 @@ +use std::{ + env, + fs::{read_to_string, OpenOptions}, + io::{Result, Write}, +}; + +fn main() -> Result<()> { + println!("cargo:rerun-if-changed=glsl-keywords.txt"); + + let keywords = read_to_string("glsl-keywords.txt")?.replace('\n', " "); + let mut out = OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(format!("{}/glsl_keywords.rs", env::var("OUT_DIR").unwrap()))?; + + writeln!(&mut out, "const RESERVED_KEYWORDS: &[&str] = &[")?; + + for keyword in keywords.split(' ') { + writeln!(&mut out, "\"{}\",", keyword)?; + } + + writeln!(&mut out, "];")?; + + Ok(()) +} diff --git a/examples/convert.rs b/examples/convert.rs index b75b48c82e..69b5c37505 100644 --- a/examples/convert.rs +++ b/examples/convert.rs @@ -216,14 +216,17 @@ fn main() { ), }; - let mut file = fs::OpenOptions::new() + let file = fs::OpenOptions::new() .write(true) .truncate(true) .create(true) .open(&args[2]) .unwrap(); - glsl::write(&module, &mut file, options) + let mut writer = glsl::Writer::new(file, &module, &options).unwrap(); + + writer + .write() .map_err(|e| { fs::remove_file(&args[2]).unwrap(); e diff --git a/glsl-keywords.txt b/glsl-keywords.txt new file mode 100644 index 0000000000..34b97552eb --- /dev/null +++ b/glsl-keywords.txt @@ -0,0 +1,20 @@ +attribute const uniform varying buffer shared coherent volatile restrict readonly writeonly +atomic_uint layout centroid flat smooth noperspective patch sample break continue do for while +switch case default if else subroutine in out inout float double int void bool true false invariant +precise discard return mat2 mat3 mat4 dmat2 dmat3 dmat4 mat2x2 mat2x3 mat2x4 dmat2x2 dmat2x3 dmat2x4 +mat3x2 mat3x3 mat3x4 dmat3x2 dmat3x3 dmat3x4 mat4x2 mat4x3 mat4x4 dmat4x2 dmat4x3 dmat4x4 vec2 vec3 +vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 dvec2 dvec3 dvec4 uint uvec2 uvec3 uvec4 lowp mediump highp +precision sampler1D sampler2D sampler3D samplerCube sampler1DShadow sampler2DShadow +samplerCubeShadow sampler1DArray sampler2DArray sampler1DArrayShadow sampler2DArrayShadow +isampler1D isampler2D isampler3D isamplerCube isampler1DArray isampler2DArray usampler1D usampler2D +usampler3D usamplerCube usampler1DArray usampler2DArray sampler2DRect sampler2DRectShadow isampler2D +Rect usampler2DRect samplerBuffer isamplerBuffer usamplerBuffer sampler2DMS isampler2DMS +usampler2DMS sampler2DMSArray isampler2DMSArray usampler2DMSArray samplerCubeArray +samplerCubeArrayShadow isamplerCubeArray usamplerCubeArray image1D iimage1D uimage1D image2D +iimage2D uimage2D image3D iimage3D uimage3D image2DRect iimage2DRect uimage2DRect imageCube +iimageCube uimageCube imageBuffer iimageBuffer uimageBuffer image1DArray iimage1DArray uimage1DArray +image2DArray iimage2DArray uimage2DArray imageCubeArray iimageCubeArray uimageCubeArray image2DMS +iimage2DMS uimage2DMS image2DMSArray iimage2DMSArray uimage2DMSArraystruct common partition active +asm class union enum typedef template this resource goto inline noinline public static extern +external interface long short half fixed unsigned superp input output hvec2 hvec3 hvec4 fvec2 fvec3 +fvec4 sampler3DRect filter sizeof cast namespace using main diff --git a/src/back/glsl.rs b/src/back/glsl.rs index 2b13283053..25a4f74f75 100644 --- a/src/back/glsl.rs +++ b/src/back/glsl.rs @@ -1,15 +1,37 @@ +//! 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::{Interface, ResolveContext, ResolveError, Typifier, Visitor}, + 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, MemberOrigin, Module, ScalarKind, - ShaderStage, Statement, StorageAccess, StorageClass, StorageFormat, StructMember, Type, - TypeInner, UnaryOperator, + ImageClass, Interpolation, IntrinsicFunction, LocalVariable, Module, ScalarKind, ShaderStage, + Statement, StorageAccess, StorageClass, StorageFormat, StructMember, Type, TypeInner, + UnaryOperator, }; use std::{ - borrow::Cow, cmp::Ordering, - fmt::{self, Error as FmtError, Write as FmtWrite}, + fmt::{self, Error as FmtError}, io::{Error as IoError, Write}, }; @@ -63,6 +85,14 @@ impl Version { Version::Embedded(_) => true, } } + + fn is_valid(&self) -> bool { + match self { + Version::Desktop(v) if SUPPORTED_CORE_VERSIONS.contains(v) => true, + Version::Embedded(v) if SUPPORTED_ES_VERSIONS.contains(v) => true, + _ => false, + } + } } impl PartialOrd for Version { @@ -96,6 +126,8 @@ pub struct TextureMapping { pub sampler: Option>, } +// imports a list of reserved keywords called RESERVED_KEYWORDS +include!(concat!(env!("OUT_DIR"), "/glsl_keywords.rs")); const SUPPORTED_CORE_VERSIONS: &[u16] = &[330, 400, 410, 420, 430, 440, 450]; const SUPPORTED_ES_VERSIONS: &[u16] = &[300, 310]; @@ -128,7 +160,7 @@ impl FeaturesManager { } #[allow(clippy::collapsible_if)] - pub fn write(&self, version: Version, out: &mut impl Write) -> Result<(), Error> { + 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!( @@ -282,1168 +314,1114 @@ impl FeaturesManager { } } -pub fn write<'a>( +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 get_local<'c>( + &self, + local: Handle, + names: &'c FastHashMap, + ) -> &'c str { + match self.func { + FunctionType::Function(handle) => { + names.get(&NameKey::FunctionLocal(handle, local)).unwrap() + } + FunctionType::EntryPoint(idx) => { + names.get(&NameKey::EntryPointLocal(idx, local)).unwrap() + } + } + } + + fn get_arg<'c>(&self, arg: u32, names: &'c FastHashMap) -> &'c str { + match self.func { + FunctionType::Function(handle) => { + names.get(&NameKey::FunctionArgument(handle, arg)).unwrap() + } + 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: &mut impl Write, - options: Options, -) -> Result, Error> { - let (version, es) = match options.version { - Version::Desktop(v) => (v, false), - Version::Embedded(v) => (v, true), - }; + out: W, + options: &'a Options, - if (!es && !SUPPORTED_CORE_VERSIONS.contains(&version)) - || (es && !SUPPORTED_ES_VERSIONS.contains(&version)) - { - return Err(Error::Custom(format!( - "Version not supported {}", - options.version - ))); - } + // Internal State + features: FeaturesManager, + names: FastHashMap, + entry_point: &'a crate::EntryPoint, + entry_point_idx: crate::proc::EntryPointIndex, + call_graph: CallGraph, - let mut manager = FeaturesManager::new(); - let mut buf = Vec::new(); + /// Used to generate a unique number for blocks + block_id: IdGenerator, +} - writeln!(out, "#version {}\n", options.version)?; - - if es { - writeln!(out, "precision highp float;\n")?; - } - - let entry_point = module - .entry_points - .get(&options.entry_point) - .ok_or_else(|| Error::Custom(String::from("Entry point not found")))?; - let func = &entry_point.function; - let stage = options.entry_point.0; - - if let Some(depth_test) = entry_point.early_depth_test { - manager.request(Features::IMAGE_LOAD_STORE); - writeln!(&mut buf, "layout(early_fragment_tests) in;\n")?; - - if let Some(conservative) = depth_test.conservative { - manager.request(Features::CONSERVATIVE_DEPTH); - - writeln!( - &mut buf, - "layout (depth_{}) out float gl_FragDepth;\n", - match conservative { - ConservativeDepth::GreaterEqual => "greater", - ConservativeDepth::LessEqual => "less", - ConservativeDepth::Unchanged => "unchanged", - } - )?; - } - } - - if let ShaderStage::Compute = stage { - manager.request(Features::COMPUTE_SHADER) - } - - let mut structs = FastHashMap::default(); - let mut built_structs = FastHashMap::default(); - - // Do a first pass to collect names - for (handle, ty) in module.types.iter() { - match ty.inner { - TypeInner::Struct { .. } => { - let name = ty - .name - .clone() - .filter(|ident| is_valid_ident(ident)) - .unwrap_or_else(|| format!("struct_{}", handle.index())); - - structs.insert(handle, name); - } - _ => continue, - } - } - - for ((_, global), usage) in module.global_variables.iter().zip(func.global_usage.iter()) { - if usage.is_empty() { - continue; +impl<'a, W: Write> Writer<'a, W> { + pub fn new(out: W, module: &'a Module, options: &'a Options) -> Result { + if !options.version.is_valid() { + return Err(Error::Custom(format!( + "Version not supported {}", + options.version + ))); } - let block = match global.class { - StorageClass::Storage | StorageClass::Uniform => true, - _ => false, - }; - - match module.types[global.ty].inner { - TypeInner::Struct { .. } if block => { - built_structs.insert(global.ty, ()); - } - _ => {} - } - } - - // Do a second pass to build the structs - for (handle, ty) in module.types.iter() { - match ty.inner { - TypeInner::Struct { ref members } => { - write_struct( - handle, - members, - module, - &structs, - &mut buf, - &mut built_structs, - &mut manager, - )?; - } - _ => continue, - } - } - - writeln!(&mut buf)?; - - let mut functions = FastHashMap::default(); - - for (handle, func) in module.functions.iter() { - let name = func - .name - .clone() - .filter(|ident| is_valid_ident(ident)) - .unwrap_or_else(|| format!("function_{}", handle.index())); - - writeln!( - &mut buf, - "{} {}({});", - func.return_type - .map(|ty| write_type( - ty, - &module.types, - &module.constants, - &structs, - None, - &mut manager - )) - .transpose()? - .as_deref() - .unwrap_or("void"), - name, - func.arguments - .iter() - .map(|arg| write_type( - arg.ty, - &module.types, - &module.constants, - &structs, - None, - &mut manager - )) - .collect::, _>>()? - .join(","), - )?; - - functions.insert(handle, name); - } - - writeln!(&mut buf)?; - - let texture_mappings = collect_texture_mapping( - functions - .keys() - .map(|handle| &module.functions[*handle]) - .chain(std::iter::once(func)), - )?; - let mut mappings_map = FastHashMap::default(); - let mut globals_lookup = FastHashMap::default(); - - for ((handle, global), _) in module - .global_variables - .iter() - .zip(func.global_usage.iter()) - .filter(|(_, usage)| !usage.is_empty()) - { - match module.types[global.ty].inner { - TypeInner::Image { - dim, - arrayed, - class, - } => { - let mapping = if let Some(map) = texture_mappings.get_key_value(&handle) { - map - } else { - log::warn!( - "Couldn't find a mapping for {:?}, handle {:?}", - global, - handle - ); - continue; - }; - - if let TypeInner::Image { - class: ImageClass::Storage(storage_format), - .. - } = module.types[global.ty].inner - { - write!( - &mut buf, - "layout({}) ", - write_format_glsl(storage_format, &mut manager) - )?; - } - - if global.storage_access == StorageAccess::LOAD { - write!(&mut buf, "readonly ")?; - } else if global.storage_access == StorageAccess::STORE { - write!(&mut buf, "writeonly ")?; - } - - let name = if let Some(ref binding) = global.binding { - match binding { - crate::Binding::Location(location) => format!("location_{}", location), - crate::Binding::Resource { group, binding } => { - format!("set_{}_binding_{}", group, binding) - } - crate::Binding::BuiltIn(_) => unreachable!(), - } - } else { - global - .name - .clone() - .filter(|ident| is_valid_ident(ident)) - .unwrap_or_else(|| format!("global_{}", handle.index())) - }; - - writeln!( - &mut buf, - "uniform {} {};", - write_image_type(dim, arrayed, class, &mut manager)?, - name - )?; - - mappings_map.insert( - name.clone(), - TextureMapping { - texture: *mapping.0, - sampler: *mapping.1, - }, - ); - globals_lookup.insert(handle, name); - } - TypeInner::Sampler { .. } => { - let name = global - .name - .clone() - .filter(|ident| is_valid_ident(ident)) - .unwrap_or_else(|| format!("global_{}", handle.index())); - - globals_lookup.insert(handle, name); - } - _ => continue, - } - } - - for ((handle, global), _) in module - .global_variables - .iter() - .zip(func.global_usage.iter()) - .filter(|(_, usage)| !usage.is_empty()) - { - match module.types[global.ty].inner { - TypeInner::Image { .. } | TypeInner::Sampler { .. } => continue, - _ => {} - } - - if let Some(crate::Binding::BuiltIn(built_in)) = global.binding { - let semantic = 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", - }; - - globals_lookup.insert(handle, String::from(semantic)); - continue; - } - - let name = if let Some(ref binding) = global.binding { - let prefix = match global.class { - StorageClass::Function => "fn", - StorageClass::Input => "in", - StorageClass::Output => "out", - StorageClass::Private => "priv", - StorageClass::Storage => "buffer", - StorageClass::Uniform => "uniform", - StorageClass::Handle => "handle", - StorageClass::WorkGroup => "wg", - StorageClass::PushConstant => "pc", - }; - - match binding { - crate::Binding::Location(location) => format!("{}_location_{}", prefix, location), - crate::Binding::Resource { group, binding } => { - format!("{}_set_{}_binding_{}", prefix, group, binding) - } - crate::Binding::BuiltIn(_) => unreachable!(), - } - } else { - global - .name - .clone() - .filter(|ident| is_valid_ident(ident)) - .unwrap_or_else(|| format!("global_{}", handle.index())) - }; - - if let TypeInner::Struct { .. } = module.types[global.ty].inner { - if built_structs.get(&global.ty).is_none() { - globals_lookup.insert(handle, name); - continue; - } - } - - if global.storage_access == StorageAccess::LOAD { - write!(&mut buf, "readonly ")?; - } else if global.storage_access == StorageAccess::STORE { - write!(&mut buf, "writeonly ")?; - } - - if let Some(interpolation) = global.interpolation { - match (stage, global.class) { - (ShaderStage::Fragment, StorageClass::Input) - | (ShaderStage::Vertex, StorageClass::Output) => { - write!(&mut buf, "{} ", write_interpolation(interpolation)?)?; - } - _ => {} - }; - } - - let block = match global.class { - StorageClass::Storage | StorageClass::Uniform => { - Some(format!("global_block_{}", handle.index())) - } - _ => None, - }; - - writeln!( - &mut buf, - "{}{} {};", - write_storage_class(global.class, &mut manager)?, - write_type( - global.ty, - &module.types, - &module.constants, - &structs, - block, - &mut manager - )?, - name - )?; - - globals_lookup.insert(handle, name); - } - - writeln!(&mut buf)?; - let mut typifier = Typifier::new(); - - let mut write_function = |func: &Function, name: &str| -> Result<(), Error> { - typifier.resolve_all( - &func.expressions, - &module.types, - &ResolveContext { - constants: &module.constants, - global_vars: &module.global_variables, - local_vars: &func.local_variables, - functions: &module.functions, - arguments: &func.arguments, - }, - )?; - - let args: FastHashMap<_, _> = func - .arguments + let (ep_idx, ep) = module + .entry_points .iter() .enumerate() - .map(|(pos, arg)| { - let name = arg - .name - .clone() - .filter(|ident| is_valid_ident(ident)) - .unwrap_or_else(|| format!("arg_{}", pos + 1)); - (pos as u32, name) + .find_map(|(i, (key, entry_point))| { + Some((i as u16, entry_point)).filter(|_| &options.entry_point == key) }) - .collect(); + .ok_or_else(|| Error::Custom(String::from("Entry point not found")))?; - writeln!( - &mut buf, - "{} {}({}) {{", - func.return_type - .map(|ty| write_type( - ty, - &module.types, - &module.constants, - &structs, - None, - &mut manager - )) - .transpose()? - .as_deref() - .unwrap_or("void"), - name, - func.arguments - .iter() - .enumerate() - .map::, _>(|(pos, arg)| { - let ty = write_type( - arg.ty, - &module.types, - &module.constants, - &structs, - None, - &mut manager, - )?; - Ok(format!("{} {}", ty, args[&(pos as u32)])) - }) - .collect::, _>>()? - .join(","), - )?; + let mut names = FastHashMap::default(); + Namer::process(module, RESERVED_KEYWORDS, &mut names); - let locals: FastHashMap<_, _> = func - .local_variables - .iter() - .map(|(handle, local)| { - ( - handle, - local - .name - .clone() - .filter(|ident| is_valid_ident(ident)) - .unwrap_or_else(|| format!("local_{}", handle.index())), - ) - }) - .collect(); + let call_graph = CallGraphBuilder { + functions: &module.functions, + } + .process(&ep.function); - let mut builder = StatementBuilder { - functions: &functions, - globals: &globals_lookup, - locals_lookup: &locals, - structs: &structs, - args: &args, - expressions: &func.expressions, - typifier: &typifier, + 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(), }; - for (handle, name) in locals.iter() { - let var = &func.local_variables[*handle]; - write!( - &mut buf, - "\t{} {}", - write_type( - var.ty, - &module.types, - &module.constants, - &structs, - None, - &mut manager - )?, - name - )?; - if let Some(init) = var.init { - write!( - &mut buf, - " = {}", - write_constant(&module.constants[init], module, &mut builder, &mut manager)? - )?; - } - writeln!(&mut buf, ";")?; - } + this.collect_required_features()?; - writeln!(&mut buf)?; - - for sta in func.body.iter() { - writeln!( - &mut buf, - "{}", - write_statement(sta, module, &mut builder, &mut manager, 1)? - )?; - } - - writeln!(&mut buf, "}}")?; - - Ok(()) - }; - - for (handle, name) in functions.iter() { - let func = &module.functions[*handle]; - write_function(func, name)?; + Ok(this) } - write_function(func, "main")?; + fn collect_required_features(&mut self) -> Result<(), Error> { + let stage = self.options.entry_point.0; - writeln!(out)?; + if let Some(depth_test) = self.entry_point.early_depth_test { + self.features.request(Features::IMAGE_LOAD_STORE); - manager.write(options.version, out)?; - out.write_all(&buf)?; - - Ok(mappings_map) -} - -struct StatementBuilder<'a> { - functions: &'a FastHashMap, String>, - globals: &'a FastHashMap, String>, - locals_lookup: &'a FastHashMap, String>, - structs: &'a FastHashMap, String>, - args: &'a FastHashMap, - expressions: &'a Arena, - typifier: &'a Typifier, -} - -fn write_statement<'a, 'b>( - sta: &Statement, - module: &'a Module, - builder: &'b mut StatementBuilder<'a>, - manager: &mut FeaturesManager, - indent: usize, -) -> Result { - Ok(match sta { - Statement::Block(block) => block - .iter() - .map(|sta| write_statement(sta, module, builder, manager, indent)) - .collect::, _>>()? - .join("\n"), - Statement::If { - condition, - accept, - reject, - } => { - let mut out = String::new(); - - writeln!( - &mut out, - "{}if({}) {{", - "\t".repeat(indent), - write_expression(&builder.expressions[*condition], module, builder, manager)? - )?; - - for sta in accept { - writeln!( - &mut out, - "{}", - write_statement(sta, module, builder, manager, indent + 1)? - )?; + if depth_test.conservative.is_some() { + self.features.request(Features::CONSERVATIVE_DEPTH); } - - if !reject.is_empty() { - writeln!(&mut out, "{}}} else {{", "\t".repeat(indent),)?; - for sta in reject { - writeln!( - &mut out, - "{}", - write_statement(sta, module, builder, manager, indent + 1)? - )?; - } - } - - write!(&mut out, "{}}}", "\t".repeat(indent),)?; - - out } - Statement::Switch { - selector, - cases, - default, - } => { - let mut out = String::new(); - writeln!( - &mut out, - "{}switch({}) {{", - "\t".repeat(indent), - write_expression(&builder.expressions[*selector], module, builder, manager)? - )?; - - for (label, (block, fallthrough)) in cases { - writeln!(&mut out, "{}case {}:", "\t".repeat(indent + 1), label)?; - - for sta in block { - writeln!( - &mut out, - "{}", - write_statement(sta, module, builder, manager, indent + 2)? - )?; - } - - if fallthrough.is_none() { - writeln!(&mut out, "{}break;", "\t".repeat(indent + 2),)?; - } - } - - if !default.is_empty() { - writeln!(&mut out, "{}default:", "\t".repeat(indent + 1),)?; - - for sta in default { - writeln!( - &mut out, - "{}", - write_statement(sta, module, builder, manager, indent + 2)? - )?; - } - } - - write!(&mut out, "{}}}", "\t".repeat(indent),)?; - - out + if let ShaderStage::Compute = stage { + self.features.request(Features::COMPUTE_SHADER) } - Statement::Loop { body, continuing } => { - let mut out = String::new(); - writeln!(&mut out, "{}while(true) {{", "\t".repeat(indent),)?; - - for sta in body.iter().chain(continuing.iter()) { - writeln!( - &mut out, - "{}", - write_statement(sta, module, builder, manager, indent + 1)? - )?; - } - - write!(&mut out, "{}}}", "\t".repeat(indent),)?; - - out - } - Statement::Break => format!("{}break;", "\t".repeat(indent),), - Statement::Continue => format!("{}continue;", "\t".repeat(indent),), - Statement::Return { value } => format!( - "{}{}", - "\t".repeat(indent), - if let Some(expr) = value { - format!( - "return {};", - write_expression(&builder.expressions[*expr], module, builder, manager)? - ) - } else { - String::from("return;") - } - ), - Statement::Kill => format!("{}discard;", "\t".repeat(indent)), - Statement::Store { pointer, value } => format!( - "{}{} = {};", - "\t".repeat(indent), - write_expression(&builder.expressions[*pointer], module, builder, manager)?, - write_expression(&builder.expressions[*value], module, builder, manager)? - ), - }) -} - -fn write_expression<'a, 'b>( - expr: &Expression, - module: &'a Module, - builder: &'b mut StatementBuilder<'a>, - manager: &mut FeaturesManager, -) -> Result, Error> { - Ok(match *expr { - Expression::Access { base, index } => { - let base_expr = write_expression(&builder.expressions[base], module, builder, manager)?; - Cow::Owned(format!( - "{}[{}]", - base_expr, - write_expression(&builder.expressions[index], module, builder, manager)? - )) - } - Expression::AccessIndex { base, index } => { - let base_expr = write_expression(&builder.expressions[base], module, builder, manager)?; - - match *builder.typifier.get(base, &module.types) { - TypeInner::Vector { .. } => Cow::Owned(format!("{}[{}]", base_expr, index)), - TypeInner::Matrix { .. } | TypeInner::Array { .. } => { - Cow::Owned(format!("{}[{}]", base_expr, index)) - } - TypeInner::Struct { ref members } => { - if let MemberOrigin::BuiltIn(builtin) = members[index as usize].origin { - Cow::Borrowed(builtin_to_glsl(builtin)) - } else { - Cow::Owned(format!( - "{}.{}", - base_expr, - members[index as usize] - .name - .as_ref() - .filter(|s| is_valid_ident(s)) - .unwrap_or(&format!("_{}", index)) - )) + 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) } } - ref other => return Err(Error::Custom(format!("Cannot index {:?}", other))), - } - } - Expression::Constant(constant) => Cow::Owned(write_constant( - &module.constants[constant], - module, - builder, - manager, - )?), - Expression::Compose { ty, ref components } => { - let constructor = match module.types[ty].inner { - TypeInner::Vector { size, kind, width } => format!( - "{}vec{}", - map_scalar(kind, width, manager)?.prefix, - size as u8, - ), - TypeInner::Matrix { - columns, - rows, - width, - } => format!( - "{}mat{}x{}", - map_scalar(crate::ScalarKind::Float, width, manager)?.prefix, - columns as u8, - rows as u8, - ), - TypeInner::Array { .. } => write_type( - ty, - &module.types, - &module.constants, - builder.structs, - None, - manager, - )? - .into_owned(), - TypeInner::Struct { .. } => builder.structs.get(&ty).unwrap().clone(), - _ => { - return Err(Error::Custom(format!( - "Cannot compose type {}", - write_type( - ty, - &module.types, - &module.constants, - builder.structs, - None, - manager - )? - ))) - } - }; - - Cow::Owned(format!( - "{}({})", - constructor, - components - .iter() - .map::, _>(|arg| Ok(write_expression( - &builder.expressions[*arg], - module, - builder, - manager, - )?)) - .collect::, _>>()? - .join(","), - )) - } - Expression::FunctionArgument(pos) => Cow::Borrowed(builder.args.get(&pos).unwrap()), - Expression::GlobalVariable(handle) => Cow::Borrowed(builder.globals.get(&handle).unwrap()), - Expression::LocalVariable(handle) => { - Cow::Borrowed(builder.locals_lookup.get(&handle).unwrap()) - } - Expression::Load { pointer } => { - write_expression(&builder.expressions[pointer], module, builder, manager)? - } - Expression::ImageSample { - image, - sampler, - coordinate, - level, - depth_ref, - } => { - let image_expr = - write_expression(&builder.expressions[image], module, builder, manager)?; - write_expression(&builder.expressions[sampler], module, builder, manager)?; - let coordinate_expr = - write_expression(&builder.expressions[coordinate], module, builder, manager)?; - - let size = match *builder.typifier.get(coordinate, &module.types) { - TypeInner::Vector { size, .. } => size, - ref other => { - return Err(Error::Custom(format!( - "Cannot sample with coordinates of type {:?}", - other - ))) - } - }; - - let coordinate_expr = if let Some(depth_ref) = depth_ref { - Cow::Owned(format!( - "vec{}({},{})", - size as u8 + 1, - coordinate_expr, - write_expression(&builder.expressions[depth_ref], module, builder, manager)? - )) - } else { - coordinate_expr - }; - - //TODO: handle MS - Cow::Owned(match level { - crate::SampleLevel::Auto => format!("texture({},{})", image_expr, coordinate_expr), - crate::SampleLevel::Zero => { - format!("textureLod({},{},0)", image_expr, coordinate_expr) - } - crate::SampleLevel::Exact(expr) => { - let level_expr = - write_expression(&builder.expressions[expr], module, builder, manager)?; - format!( - "textureLod({}, {}, {})", - image_expr, coordinate_expr, level_expr - ) - } - crate::SampleLevel::Bias(bias) => { - let bias_expr = - write_expression(&builder.expressions[bias], module, builder, manager)?; - format!("texture({},{},{})", image_expr, coordinate_expr, bias_expr) - } - }) - } - Expression::ImageLoad { - image, - coordinate, - index, - } => { - let image_expr = - write_expression(&builder.expressions[image], module, builder, manager)?; - let coordinate_expr = - write_expression(&builder.expressions[coordinate], module, builder, manager)?; - - let (dim, arrayed, class) = match *builder.typifier.get(image, &module.types) { TypeInner::Image { dim, arrayed, class, - } => (dim, arrayed, class), - ref other => return Err(Error::Custom(format!("Cannot load {:?}", other))), - }; + } => { + 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) + } - Cow::Owned(match class { - ImageClass::Sampled { kind, multi } => { - //TODO: fix this - let sampler_constructor = format!( - "{}sampler{}{}{}({})", - map_scalar(kind, 4, manager)?.prefix, - ImageDimension(dim), - if multi { "MS" } else { "" }, - if arrayed { "Array" } else { "" }, - image_expr, - ); - - let index_expr = write_expression( - &builder.expressions[index.unwrap()], - module, - builder, - manager, - )?; - format!( - "texelFetch({},{},{})", - sampler_constructor, coordinate_expr, index_expr - ) + 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) + } + _ => {} + }, + _ => {} + } } - ImageClass::Storage(_) => format!("imageLoad({},{})", image_expr, coordinate_expr), - ImageClass::Depth => todo!(), - }) + _ => {} + } } - Expression::Unary { op, expr } => { - let base_expr = write_expression(&builder.expressions[expr], module, builder, manager)?; - Cow::Owned(format!( - "({} {})", - match op { - UnaryOperator::Negate => "-", - UnaryOperator::Not => match *builder.typifier.get(expr, &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 - ))), - }, - }, - base_expr - )) + 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), + _ => {} + } } - Expression::Binary { op, left, right } => { - let left_expr = write_expression(&builder.expressions[left], module, builder, manager)?; - let right_expr = - write_expression(&builder.expressions[right], module, builder, manager)?; - let op_str = 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 => ">>", + 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", + } + )?; + } + } + + let mut built_structs = FastHashMap::default(); + + for (_, global) in self + .module + .global_variables + .iter() + .zip(&self.entry_point.function.global_usage) + .filter_map(|(global, usage)| Some(global).filter(|_| !usage.is_empty())) + { + match global.class { + StorageClass::Storage | StorageClass::Uniform => (), + _ => continue, + } + + if let TypeInner::Struct { .. } = self.module.types[global.ty].inner { + built_structs.insert(global.ty, ()); + } + } + + for (handle, ty) in self.module.types.iter() { + if let TypeInner::Struct { ref members } = ty.inner { + if built_structs.get(&handle).is_none() { + 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 TypeInner::Image { + dim, + arrayed, + class, + } = self.module.types[global.ty].inner + { + 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)?; + + let name = self.names.get(&NameKey::GlobalVariable(handle)).unwrap(); + writeln!(self.out, " {};", name)? + } + } + + 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())) + { + match self.module.types[global.ty].inner { + TypeInner::Image { .. } | TypeInner::Sampler { .. } => continue, + _ => {} + } + + if let Some(crate::Binding::BuiltIn(_)) = global.binding { + continue; + } + + 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.get(&NameKey::Type(global.ty)).cloned().unwrap(); + + Some(block_name) + } + _ => None, }; - Cow::Owned(format!("({} {} {})", left_expr, op_str, right_expr)) - } - Expression::Select { - condition, - accept, - reject, - } => { - let cond_expr = - write_expression(&builder.expressions[condition], module, builder, manager)?; - let accept_expr = - write_expression(&builder.expressions[accept], module, builder, manager)?; - let reject_expr = - write_expression(&builder.expressions[reject], module, builder, manager)?; - Cow::Owned(format!( - "({} ? {} : {})", - cond_expr, accept_expr, reject_expr - )) - } - Expression::Intrinsic { fun, argument } => { - let expr = write_expression(&builder.expressions[argument], module, builder, manager)?; + write!(self.out, "{} ", glsl_storage_class(global.class))?; - Cow::Owned(format!( - "{:?}({})", - match fun { - IntrinsicFunction::IsFinite => "!isinf", - IntrinsicFunction::IsInf => "isinf", - IntrinsicFunction::IsNan => "isnan", - IntrinsicFunction::IsNormal => "!isnan", - IntrinsicFunction::All => "all", - IntrinsicFunction::Any => "any", + self.write_type(global.ty, block)?; + + let name = self.names.get(&NameKey::GlobalVariable(handle)).unwrap(); + writeln!(self.out, " {};", name)? + } + + writeln!(self.out)?; + + let functions: Vec<_> = self + .call_graph + .raw_nodes() + .iter() + .map(|node| node.weight) + .collect(); + for handle in functions { + let name = self.names.get(&NameKey::Function(handle)).cloned().unwrap(); + 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_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 mut 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)?; + + let name = ctx.get_local(handle, &self.names); + + write!(self.out, " {}", name)?; + + 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, &mut 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 + .get(&NameKey::StructMember(ty, idx as u32)) + .unwrap() + )?; + } + + write!(self.out, "}}")? + } else { + write!(self.out, "{}", self.names.get(&NameKey::Type(ty)).unwrap())? + } + } + _ => 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, }, - expr - )) - } - Expression::Transpose(matrix) => { - let matrix_expr = - write_expression(&builder.expressions[matrix], module, builder, manager)?; - Cow::Owned(format!("transpose({})", matrix_expr)) - } - Expression::DotProduct(left, right) => { - let left_expr = write_expression(&builder.expressions[left], module, builder, manager)?; - let right_expr = - write_expression(&builder.expressions[right], module, builder, manager)?; - Cow::Owned(format!("dot({},{})", left_expr, right_expr)) - } - Expression::CrossProduct(left, right) => { - let left_expr = write_expression(&builder.expressions[left], module, builder, manager)?; - let right_expr = - write_expression(&builder.expressions[right], module, builder, manager)?; - Cow::Owned(format!("cross({},{})", left_expr, right_expr)) - } - Expression::As { - expr, - kind, - convert, - } => { - let value_expr = - write_expression(&builder.expressions[expr], module, builder, manager)?; - - let (source_kind, ty_expr) = match *builder.typifier.get(expr, &module.types) { - TypeInner::Scalar { - width, - kind: source_kind, - } => ( - source_kind, - Cow::Borrowed(map_scalar(kind, width, manager)?.full), - ), - TypeInner::Vector { - width, - kind: source_kind, - size, - } => ( - source_kind, - Cow::Owned(format!( - "{}vec{}", - map_scalar(kind, width, manager)?.prefix, - size as u32, - )), - ), - _ => return Err(Error::Custom(format!("Cannot convert {}", value_expr))), }; + interface.traverse(&func.body); - let op = if convert { - ty_expr - } else { - Cow::Borrowed(match (source_kind, kind) { - (ScalarKind::Float, ScalarKind::Sint) => "floatBitsToInt", - (ScalarKind::Float, ScalarKind::Uint) => "floatBitsToUInt", - (ScalarKind::Sint, ScalarKind::Float) => "intBitsToFloat", - (ScalarKind::Uint, ScalarKind::Float) => "uintBitsToFloat", - _ => { + if let Some(error) = interface.visitor.error { + return Err(error); + } + } + + Ok(mappings) + } + + fn write_struct( + &mut self, + handle: Handle, + members: &[StructMember], + ) -> Result<(), Error> { + let name = self.names.get(&NameKey::Type(handle)).unwrap(); + + writeln!(self.out, "struct {} {{", name)?; + for (idx, member) in members.iter().enumerate() { + write!(self.out, "\t")?; + self.write_type(member.ty, None)?; + writeln!( + self.out, + " {};", + self.names + .get(&NameKey::StructMember(handle, idx as u32)) + .unwrap() + )?; + } + writeln!(self.out, "}};")?; + + Ok(()) + } + + fn write_stmt( + &mut self, + sta: &Statement, + ctx: &mut 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: &mut 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.get(&NameKey::StructMember(ty, index)).unwrap() + )? + } + 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.get(&NameKey::GlobalVariable(handle)).unwrap() + )? + } + } + Expression::LocalVariable(handle) => { + write!(self.out, "{}", ctx.get_local(handle, &self.names))? + } + 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 bitcast {:?} to {:?}", - source_kind, kind + "Cannot sample with coordinates of type {:?}", + other ))) } - }) - }; - - Cow::Owned(format!("{}({})", op, value_expr)) - } - Expression::Derivative { axis, expr } => { - let expr = write_expression(&builder.expressions[expr], module, builder, manager)?; - - Cow::Owned(format!( - "{}({})", - match axis { - DerivativeAxis::X => "dFdx", - DerivativeAxis::Y => "dFdy", - DerivativeAxis::Width => "fwidth", - }, - expr - )) - } - Expression::Call { - origin: FunctionOrigin::Local(ref function), - ref arguments, - } => Cow::Owned(format!( - "{}({})", - builder.functions.get(function).unwrap(), - arguments - .iter() - .map::, _>(|arg| write_expression( - &builder.expressions[*arg], - module, - builder, - manager, - )) - .collect::, _>>()? - .join(","), - )), - Expression::Call { - origin: crate::FunctionOrigin::External(ref name), - ref arguments, - } => match name.as_str() { - "cos" | "normalize" | "sin" | "length" | "abs" | "floor" | "inverse" => { - let expr = - write_expression(&builder.expressions[arguments[0]], module, builder, manager)?; - - Cow::Owned(format!("{}({})", name, expr)) - } - "fclamp" | "clamp" | "mix" | "smoothstep" => { - let x = - write_expression(&builder.expressions[arguments[0]], module, builder, manager)?; - let y = - write_expression(&builder.expressions[arguments[1]], module, builder, manager)?; - let a = - write_expression(&builder.expressions[arguments[2]], module, builder, manager)?; - - let name = match name.as_str() { - "fclamp" => "clamp", - name => name, }; - Cow::Owned(format!("{}({}, {}, {})", name, x, y, a)) - } - "atan2" => { - let x = - write_expression(&builder.expressions[arguments[0]], module, builder, manager)?; - let y = - write_expression(&builder.expressions[arguments[1]], module, builder, manager)?; + 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)? + } - Cow::Owned(format!("atan({}, {})", y, x)) - } - "distance" | "dot" | "min" | "max" | "reflect" | "pow" | "step" | "cross" => { - let x = - write_expression(&builder.expressions[arguments[0]], module, builder, manager)?; - let y = - write_expression(&builder.expressions[arguments[1]], module, builder, manager)?; + 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)?; + } + } - Cow::Owned(format!("{}({}, {})", name, x, y)) + write!(self.out, ")")? } - other => { - return Err(Error::Custom(format!( - "Unsupported function call {}", - other - ))) - } - }, - Expression::ArrayLength(expr) => { - let base = write_expression(&builder.expressions[expr], module, builder, manager)?; - Cow::Owned(format!("uint({}.length())", base)) - } - }) -} + Expression::ImageLoad { + image, + coordinate, + index, + } => { + let class = match ctx.typifier.get(image, &self.module.types) { + TypeInner::Image { class, .. } => class, + _ => unreachable!(), + }; -fn write_constant( - constant: &Constant, - module: &Module, - builder: &mut StatementBuilder<'_>, - manager: &mut FeaturesManager, -) -> Result { - Ok(match constant.inner { - ConstantInner::Sint(int) => int.to_string(), - ConstantInner::Uint(int) => format!("{}u", int), - ConstantInner::Float(float) => format!("{:?}", float), - ConstantInner::Bool(boolean) => boolean.to_string(), - ConstantInner::Composite(ref components) => format!( - "{}({})", - match module.types[constant.ty].inner { - TypeInner::Vector { size, .. } => Cow::Owned(format!("vec{}", size as u8,)), - TypeInner::Matrix { columns, rows, .. } => - Cow::Owned(format!("mat{}x{}", columns as u8, rows as u8,)), - TypeInner::Struct { .. } => - Cow::::Borrowed(builder.structs.get(&constant.ty).unwrap()), - TypeInner::Array { .. } => write_type( - constant.ty, - &module.types, - &module.constants, - builder.structs, - None, - manager - )?, - _ => + 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.get(&NameKey::Function(*function)).unwrap() + )?; + 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!( - "Cannot build constant of type {}", - write_type( - constant.ty, - &module.types, - &module.constants, - builder.structs, - None, - manager - )? - ))), + "Unsupported function call {}", + other + ))) + } }, - components - .iter() - .map(|component| write_constant( - &module.constants[*component], - module, - builder, - manager - )) - .collect::, _>>()? - .join(","), - ), - }) + 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> { @@ -1451,11 +1429,7 @@ struct ScalarString<'a> { full: &'a str, } -fn map_scalar( - kind: ScalarKind, - width: crate::Bytes, - manager: &mut FeaturesManager, -) -> Result, Error> { +fn glsl_scalar(kind: ScalarKind, width: crate::Bytes) -> Result, Error> { Ok(match kind { ScalarKind::Sint => ScalarString { prefix: "i", @@ -1470,14 +1444,10 @@ fn map_scalar( prefix: "", full: "float", }, - 8 => { - manager.request(Features::DOUBLE_TYPE); - - ScalarString { - prefix: "d", - full: "double", - } - } + 8 => ScalarString { + prefix: "d", + full: "double", + }, _ => { return Err(Error::Custom(format!( "Cannot build float of width {}", @@ -1492,144 +1462,41 @@ fn map_scalar( }) } -fn write_type<'a>( - ty: Handle, - types: &Arena, - constants: &Arena, - structs: &'a FastHashMap, String>, - block: Option, - manager: &mut FeaturesManager, -) -> Result, Error> { - Ok(match types[ty].inner { - TypeInner::Scalar { kind, width } => Cow::Borrowed(map_scalar(kind, width, manager)?.full), - TypeInner::Vector { size, kind, width } => Cow::Owned(format!( - "{}vec{}", - map_scalar(kind, width, manager)?.prefix, - size as u8 - )), - TypeInner::Matrix { - columns, - rows, - width, - } => { - if width == 8 { - manager.request(Features::DOUBLE_TYPE); - } - - Cow::Owned(format!( - "{}mat{}x{}", - map_scalar(crate::ScalarKind::Float, width, manager)?.prefix, - columns as u8, - rows as u8 - )) - } - TypeInner::Pointer { base, .. } => { - write_type(base, types, constants, structs, None, manager)? - } - TypeInner::Array { base, size, .. } => { - if let TypeInner::Array { .. } = types[base].inner { - manager.request(Features::ARRAY_OF_ARRAYS) - } - - Cow::Owned(format!( - "{}[{}]", - write_type(base, types, constants, structs, None, manager)?, - write_array_size(size, constants)? - )) - } - TypeInner::Struct { ref members } => { - if let Some(name) = block { - let mut out = String::new(); - writeln!(&mut out, "{} {{", name)?; - - for (idx, member) in members.iter().enumerate() { - writeln!( - &mut out, - "\t{} {};", - write_type(member.ty, types, constants, structs, None, manager)?, - member - .name - .clone() - .filter(|s| is_valid_ident(s)) - .unwrap_or_else(|| format!("_{}", idx)) - )?; - } - - write!(&mut out, "}}")?; - - Cow::Owned(out) - } else { - Cow::Borrowed(structs.get(&ty).unwrap()) - } - } - _ => unreachable!(), - }) -} - -fn write_image_type( - dim: crate::ImageDimension, - arrayed: bool, - class: ImageClass, - manager: &mut FeaturesManager, -) -> Result { - if arrayed && dim == crate::ImageDimension::Cube { - manager.request(Features::CUBE_TEXTURES_ARRAY) - } else if dim == crate::ImageDimension::D1 { - manager.request(Features::TEXTURE_1D) +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", } - - let (base, kind, ms, comparison) = match class { - ImageClass::Sampled { kind, multi: true } => { - manager.request(Features::MULTISAMPLED_TEXTURES); - if arrayed { - manager.request(Features::MULTISAMPLED_TEXTURE_ARRAYS); - } - - ("sampler", kind, "MS", "") - } - ImageClass::Sampled { kind, multi: false } => ("sampler", kind, "", ""), - ImageClass::Depth => ("sampler", crate::ScalarKind::Float, "", "Shadow"), - ImageClass::Storage(format) => ("image", format.into(), "", ""), - }; - - Ok(format!( - "{}{}{}{}{}{}", - map_scalar(kind, 4, manager)?.prefix, - base, - ImageDimension(dim), - ms, - if arrayed { "Array" } else { "" }, - comparison - )) } -fn write_storage_class( - class: StorageClass, - manager: &mut FeaturesManager, -) -> Result<&'static str, Error> { - Ok(match class { +fn glsl_storage_class(class: StorageClass) -> &'static str { + match class { StorageClass::Function => "", - StorageClass::Input => "in ", - StorageClass::Output => "out ", + StorageClass::Input => "in", + StorageClass::Output => "out", StorageClass::Private => "", - StorageClass::Storage => { - manager.request(Features::BUFFER_STORAGE); - "buffer " - } - StorageClass::Uniform => "uniform ", - StorageClass::Handle => "uniform ", - StorageClass::WorkGroup => { - manager.request(Features::COMPUTE_SHADER); - "shared " - } - StorageClass::PushConstant => { - manager.request(Features::PUSH_CONSTANT); - "" - } - }) + StorageClass::Storage => "buffer", + StorageClass::Uniform => "uniform", + StorageClass::Handle => "uniform", + StorageClass::WorkGroup => "shared", + StorageClass::PushConstant => "", + } } -fn write_interpolation(interpolation: Interpolation) -> Result<&'static str, Error> { +fn glsl_interpolation(interpolation: Interpolation) -> Result<&'static str, Error> { Ok(match interpolation { Interpolation::Perspective => "smooth", Interpolation::Linear => "noperspective", @@ -1644,16 +1511,6 @@ fn write_interpolation(interpolation: Interpolation) -> Result<&'static str, Err }) } -fn write_array_size(size: ArraySize, constants: &Arena) -> Result { - Ok(match size { - ArraySize::Constant(const_handle) => match constants[const_handle].inner { - ConstantInner::Uint(size) => size.to_string(), - _ => unreachable!(), - }, - ArraySize::Dynamic => String::from(""), - }) -} - struct ImageDimension(crate::ImageDimension); impl fmt::Display for ImageDimension { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1670,186 +1527,34 @@ impl fmt::Display for ImageDimension { } } -fn write_struct( - handle: Handle, - members: &[StructMember], - module: &Module, - structs: &FastHashMap, String>, - out: &mut impl Write, - built_structs: &mut FastHashMap, ()>, - manager: &mut FeaturesManager, -) -> Result { - if built_structs.get(&handle).is_some() { - return Ok(true); - } - - let mut tmp = String::new(); - - let name = structs.get(&handle).unwrap(); - let mut fields = 0; - - writeln!(&mut tmp, "struct {} {{", name)?; - for (idx, member) in members.iter().enumerate() { - if let MemberOrigin::BuiltIn(_) = member.origin { - continue; - } - - if let TypeInner::Struct { ref members } = module.types[member.ty].inner { - if !write_struct( - member.ty, - members, - module, - structs, - out, - built_structs, - manager, - )? { - continue; - } - } - - writeln!( - &mut tmp, - "\t{} {};", - write_type( - member.ty, - &module.types, - &module.constants, - &structs, - None, - manager - )?, - member - .name - .clone() - .filter(|s| is_valid_ident(s)) - .unwrap_or_else(|| format!("_{}", idx)) - )?; - - fields += 1; - } - writeln!(&mut tmp, "}};")?; - - if fields != 0 { - built_structs.insert(handle, ()); - writeln!(out, "{}", tmp)?; - } - - Ok(fields != 0) -} - -fn is_valid_ident(ident: &str) -> bool { - ident.starts_with(|c: char| c.is_ascii_alphabetic() || c == '_') - && ident.contains(|c: char| c.is_ascii_alphanumeric() || c == '_') - && !ident.starts_with("gl_") - && ident != "main" -} - -fn builtin_to_glsl(builtin: BuiltIn) -> &'static str { - match builtin { - 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 write_format_glsl(format: StorageFormat, manager: &mut FeaturesManager) -> &'static str { +fn glsl_storage_format(format: StorageFormat) -> &'static str { match format { - StorageFormat::R8Unorm => { - manager.request(Features::FULL_IMAGE_FORMATS); - "r8" - } - StorageFormat::R8Snorm => { - manager.request(Features::FULL_IMAGE_FORMATS); - "r8_snorm" - } - StorageFormat::R8Uint => { - manager.request(Features::FULL_IMAGE_FORMATS); - "r8ui" - } - StorageFormat::R8Sint => { - manager.request(Features::FULL_IMAGE_FORMATS); - "r8i" - } - StorageFormat::R16Uint => { - manager.request(Features::FULL_IMAGE_FORMATS); - "r16ui" - } - StorageFormat::R16Sint => { - manager.request(Features::FULL_IMAGE_FORMATS); - "r16i" - } - StorageFormat::R16Float => { - manager.request(Features::FULL_IMAGE_FORMATS); - "r16f" - } - StorageFormat::Rg8Unorm => { - manager.request(Features::FULL_IMAGE_FORMATS); - "rg8" - } - StorageFormat::Rg8Snorm => { - manager.request(Features::FULL_IMAGE_FORMATS); - "rg8_snorm" - } - StorageFormat::Rg8Uint => { - manager.request(Features::FULL_IMAGE_FORMATS); - "rg8ui" - } - StorageFormat::Rg8Sint => { - manager.request(Features::FULL_IMAGE_FORMATS); - "rg8i" - } + 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 => { - manager.request(Features::FULL_IMAGE_FORMATS); - "rg16ui" - } - StorageFormat::Rg16Sint => { - manager.request(Features::FULL_IMAGE_FORMATS); - "rg16i" - } - StorageFormat::Rg16Float => { - manager.request(Features::FULL_IMAGE_FORMATS); - "rg16f" - } + StorageFormat::Rg16Uint => "rg16ui", + StorageFormat::Rg16Sint => "rg16i", + StorageFormat::Rg16Float => "rg16f", StorageFormat::Rgba8Unorm => "rgba8ui", StorageFormat::Rgba8Snorm => "rgba8_snorm", StorageFormat::Rgba8Uint => "rgba8ui", StorageFormat::Rgba8Sint => "rgba8i", - StorageFormat::Rgb10a2Unorm => { - manager.request(Features::FULL_IMAGE_FORMATS); - "rgb10_a2ui" - } - StorageFormat::Rg11b10Float => { - manager.request(Features::FULL_IMAGE_FORMATS); - "r11f_g11f_b10f" - } - StorageFormat::Rg32Uint => { - manager.request(Features::FULL_IMAGE_FORMATS); - "rg32ui" - } - StorageFormat::Rg32Sint => { - manager.request(Features::FULL_IMAGE_FORMATS); - "rg32i" - } - StorageFormat::Rg32Float => { - manager.request(Features::FULL_IMAGE_FORMATS); - "rg32f" - } + 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", @@ -1860,8 +1565,9 @@ fn write_format_glsl(format: StorageFormat, manager: &mut FeaturesManager) -> &' } struct TextureMappingVisitor<'a> { + names: &'a FastHashMap, expressions: &'a Arena, - map: &'a mut FastHashMap, Option>>, + map: &'a mut FastHashMap, error: Option, } @@ -1873,15 +1579,23 @@ impl<'a> Visitor for TextureMappingVisitor<'a> { Expression::GlobalVariable(global) => global, _ => unreachable!(), }; + let tex_name = self + .names + .get(&NameKey::GlobalVariable(tex_handle)) + .unwrap() + .clone(); let sampler_handle = match self.expressions[*sampler] { Expression::GlobalVariable(global) => global, _ => unreachable!(), }; - let sampler = self.map.entry(tex_handle).or_insert(Some(sampler_handle)); + let mapping = self.map.entry(tex_name).or_insert(TextureMapping { + texture: tex_handle, + sampler: Some(sampler_handle), + }); - if *sampler != Some(sampler_handle) { + if mapping.sampler != Some(sampler_handle) { self.error = Some(Error::Custom(String::from( "Cannot use texture with two different samplers", ))); @@ -1892,10 +1606,18 @@ impl<'a> Visitor for TextureMappingVisitor<'a> { Expression::GlobalVariable(global) => global, _ => unreachable!(), }; + let tex_name = self + .names + .get(&NameKey::GlobalVariable(tex_handle)) + .unwrap() + .clone(); - let sampler = self.map.entry(tex_handle).or_insert(None); + let mapping = self.map.entry(tex_name).or_insert(TextureMapping { + texture: tex_handle, + sampler: None, + }); - if *sampler != None { + if mapping.sampler != None { self.error = Some(Error::Custom(String::from( "Cannot use texture with two different samplers", ))); @@ -1905,28 +1627,3 @@ impl<'a> Visitor for TextureMappingVisitor<'a> { } } } - -fn collect_texture_mapping<'a>( - functions: impl Iterator, -) -> Result, Option>>, Error> { - let mut mappings = FastHashMap::default(); - - for func in functions { - let mut interface = Interface { - expressions: &func.expressions, - local_variables: &func.local_variables, - visitor: TextureMappingVisitor { - expressions: &func.expressions, - map: &mut mappings, - error: None, - }, - }; - interface.traverse(&func.body); - - if let Some(error) = interface.visitor.error { - return Err(error); - } - } - - Ok(mappings) -} diff --git a/src/proc/call_graph.rs b/src/proc/call_graph.rs new file mode 100644 index 0000000000..1c580d5c15 --- /dev/null +++ b/src/proc/call_graph.rs @@ -0,0 +1,74 @@ +use crate::{ + arena::{Arena, Handle}, + proc::{Interface, Visitor}, + Function, +}; +use petgraph::{ + graph::{DefaultIx, NodeIndex}, + Graph, +}; + +pub type CallGraph = Graph, ()>; + +pub struct CallGraphBuilder<'a> { + pub functions: &'a Arena, +} + +impl<'a> CallGraphBuilder<'a> { + pub fn process(&self, func: &Function) -> CallGraph { + let mut graph = Graph::new(); + let mut children = Vec::new(); + + let visitor = CallGraphVisitor { + children: &mut children, + }; + + let mut interface = Interface { + expressions: &func.expressions, + local_variables: &func.local_variables, + visitor, + }; + + interface.traverse(&func.body); + + for handle in children { + let id = graph.add_node(handle); + self.collect(handle, id, &mut graph); + } + + graph + } + + fn collect(&self, handle: Handle, id: NodeIndex, graph: &mut CallGraph) { + let mut children = Vec::new(); + let visitor = CallGraphVisitor { + children: &mut children, + }; + let func = &self.functions[handle]; + + let mut interface = Interface { + expressions: &func.expressions, + local_variables: &func.local_variables, + visitor, + }; + + interface.traverse(&func.body); + + for handle in children { + let child_id = graph.add_node(handle); + graph.add_edge(id, child_id, ()); + + self.collect(handle, child_id, graph); + } + } +} + +struct CallGraphVisitor<'a> { + children: &'a mut Vec>, +} + +impl<'a> Visitor for CallGraphVisitor<'a> { + fn visit_fun(&mut self, func: Handle) { + self.children.push(func) + } +} diff --git a/src/proc/mod.rs b/src/proc/mod.rs index 2d5753ea0d..961e55da7c 100644 --- a/src/proc/mod.rs +++ b/src/proc/mod.rs @@ -1,10 +1,14 @@ //! Module processing functionality. +#[cfg(feature = "petgraph")] +mod call_graph; mod interface; mod namer; mod typifier; mod validator; +#[cfg(feature = "petgraph")] +pub use call_graph::{CallGraph, CallGraphBuilder}; pub use interface::{Interface, Visitor}; pub use namer::{EntryPointIndex, NameKey, Namer}; pub use typifier::{check_constant_type, ResolveContext, ResolveError, Typifier};