From a16204456e6195e35ef577d95bafa0824315904c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Capucho?= Date: Tue, 11 Aug 2020 21:20:23 +0100 Subject: [PATCH] WIP: glsl 450 backend and common glsl module (#123) * Initial backend implementation * Refractored glsl450 backend to have a common module between further glsl backends Implemented more missing functionality * Error handling * Implemented most of the suggestions * Addresed all comments Fixed some bugs * Made code style consistent --- Cargo.toml | 1 + examples/convert.rs | 13 + src/back/glsl.rs | 923 ++++++++++++++++++++++++++++++++++++++++++++ src/back/mod.rs | 2 + 4 files changed, 939 insertions(+) create mode 100644 src/back/glsl.rs diff --git a/Cargo.toml b/Cargo.toml index 3884ba309a..144d7c5b25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ thiserror = "1.0" default = [] glsl_preprocessor = ["glsl"] glsl-new = ["pomelo"] +glsl-out = [] [dev-dependencies] env_logger = "0.6" diff --git a/examples/convert.rs b/examples/convert.rs index 5658f5a313..4de0177591 100644 --- a/examples/convert.rs +++ b/examples/convert.rs @@ -157,6 +157,19 @@ fn main() { fs::write(&args[2], bytes.as_slice()).unwrap(); } + #[cfg(feature = "glsl-out")] + "vert" | "frag" => { + use naga::back::glsl; + + let mut file = fs::OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(&args[2]) + .unwrap(); + + glsl::write(&module, &mut file).unwrap(); + } other => { panic!("Unknown output extension: {}", other); } diff --git a/src/back/glsl.rs b/src/back/glsl.rs new file mode 100644 index 0000000000..25f2cc86c6 --- /dev/null +++ b/src/back/glsl.rs @@ -0,0 +1,923 @@ +use crate::{ + Arena, ArraySize, BinaryOperator, BuiltIn, Constant, ConstantInner, DerivativeAxis, Expression, + FastHashMap, Function, FunctionOrigin, GlobalVariable, Handle, ImageFlags, IntrinsicFunction, + LocalVariable, Module, ScalarKind, Statement, StorageClass, Type, TypeInner, UnaryOperator, +}; +use std::{ + borrow::Cow, + fmt::{self, Error as FmtError, Write as FmtWrite}, + io::{Error as IoError, Write}, +}; + +#[derive(Debug)] +pub enum Error { + FormatError(FmtError), + IoError(IoError), + 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 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::Custom(err) => write!(f, "{}", err), + } + } +} + +pub fn write(module: &Module, out: &mut impl Write) -> Result<(), Error> { + writeln!(out, "#version 450 core")?; + + let mut counter = 0; + let mut names = FastHashMap::default(); + + let mut namer = |name: Option<&String>| { + if let Some(name) = name { + names.insert(name.clone(), ()); + name.clone() + } else { + counter += 1; + while names.get(&format!("_{}", counter)).is_some() { + counter += 1; + } + format!("_{}", counter) + } + }; + + let mut structs = FastHashMap::default(); + + // Do a first pass to collect names + for (handle, ty) in module.types.iter() { + match ty.inner { + TypeInner::Struct { .. } => { + let name = namer(ty.name.as_ref()); + + structs.insert(handle, name); + } + _ => continue, + } + } + + // Do a second pass to build the structs + // TODO: glsl is order dependent so we need to build structs in order + for (handle, ty) in module.types.iter() { + match ty.inner { + TypeInner::Struct { ref members } => { + let name = structs.get(&handle).unwrap(); + + writeln!(out, "struct {} {{", name)?; + for (idx, member) in members.iter().enumerate() { + writeln!( + out, + " {} {};", + write_type(member.ty, &module.types, &structs)?, + member.name.clone().unwrap_or_else(|| idx.to_string()) + )?; + } + writeln!(out, "}};")?; + } + _ => continue, + } + } + + let mut globals_lookup = FastHashMap::default(); + + for (handle, global) in module.global_variables.iter() { + 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; + } + + if let Some(ref binding) = global.binding { + write!(out, "layout({}) ", Binding(binding.clone()))?; + } + + let name = namer(global.name.as_ref()); + + writeln!( + out, + "{}{} {};", + write_storage_class(global.class)?, + write_type(global.ty, &module.types, &structs)?, + name + )?; + + globals_lookup.insert(handle, name); + } + + let mut functions = FastHashMap::default(); + + // Do a first pass to collect names + for (handle, func) in module.functions.iter() { + functions.insert(handle, namer(func.name.as_ref())); + } + + // TODO: glsl is order dependent so we need to build functions in order + for (handle, func) in module.functions.iter() { + let name = functions.get(&handle).unwrap(); + + writeln!( + out, + "{} {}({}) {{", + func.return_type + .map_or(Ok(String::from("void")), |ty| write_type( + ty, + &module.types, + &structs + ))?, + name, + func.parameter_types + .iter() + .map(|ty| write_type(*ty, &module.types, &structs)) + .collect::, _>>()? + .join(","), + )?; + + let locals: FastHashMap<_, _> = func + .local_variables + .iter() + .map(|(handle, local)| (handle, namer(local.name.as_ref()))) + .collect(); + + for (handle, name) in locals.iter() { + writeln!( + out, + "{} {};", + write_type(func.local_variables[*handle].ty, &module.types, &structs)?, + name + )?; + } + + let mut builder = StatementBuilder { + functions: &functions, + globals: &globals_lookup, + locals_lookup: &locals, + structs: &structs, + args: &func + .parameter_types + .iter() + .enumerate() + .map(|(pos, ty)| (pos as u32, (namer(None), *ty))) + .collect(), + expressions: &func.expressions, + locals: &func.local_variables, + }; + + for sta in func.body.iter() { + writeln!(out, "{}", write_statement(sta, module, &mut builder)?)?; + } + + writeln!(out, "}}")?; + } + + Ok(()) +} + +struct Binding(crate::Binding); +impl fmt::Display for Binding { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + crate::Binding::BuiltIn(_) => write!(f, ""), // Ignore because they are variables with a predefined name + crate::Binding::Location(location) => write!(f, "location={}", location), + crate::Binding::Descriptor { set, binding } => { + write!(f, "set={},binding={}", set, binding) + } + } + } +} + +struct StatementBuilder<'a> { + pub functions: &'a FastHashMap, String>, + pub globals: &'a FastHashMap, String>, + pub locals_lookup: &'a FastHashMap, String>, + pub structs: &'a FastHashMap, String>, + pub args: &'a FastHashMap)>, + pub expressions: &'a Arena, + pub locals: &'a Arena, +} + +fn write_statement( + sta: &Statement, + module: &Module, + builder: &mut StatementBuilder<'_>, +) -> Result { + Ok(match sta { + Statement::Empty => String::new(), + Statement::Block(block) => block + .iter() + .map(|sta| write_statement(sta, module, builder)) + .collect::, _>>()? + .join("\n"), + Statement::If { + condition, + accept, + reject, + } => { + let mut out = String::new(); + + writeln!( + &mut out, + "if({}) {{", + write_expression(&builder.expressions[*condition], module, builder)?.0 + )?; + for sta in accept { + writeln!(&mut out, "{}", write_statement(sta, module, builder)?)?; + } + writeln!(&mut out, "}} else {{")?; + for sta in reject { + writeln!(&mut out, "{}", write_statement(sta, module, builder)?)?; + } + write!(&mut out, "}}")?; + + out + } + Statement::Switch { + selector, + cases, + default, + } => { + let mut out = String::new(); + + writeln!( + &mut out, + "switch({}) {{", + write_expression(&builder.expressions[*selector], module, builder)?.0 + )?; + + for (label, (block, fallthrough)) in cases { + writeln!(&mut out, " case {}:", label)?; + + for sta in block { + writeln!(&mut out, " {}", write_statement(sta, module, builder)?)?; + } + + if fallthrough.is_some() { + writeln!(&mut out, " break;")?; + } + } + + writeln!(&mut out, " default:")?; + + for sta in default { + writeln!(&mut out, " {}", write_statement(sta, module, builder)?)?; + } + + write!(&mut out, "}}")?; + + out + } + Statement::Loop { body, continuing } => { + let mut out = String::new(); + + writeln!(&mut out, "while(true) {{",)?; + + for sta in body.iter().chain(continuing.iter()) { + writeln!(&mut out, " {}", write_statement(sta, module, builder)?)?; + } + + write!(&mut out, "}}")?; + + out + } + Statement::Break => String::from("break;"), + Statement::Continue => String::from("continue;"), + Statement::Return { value } => format!( + "return {};", + value.map_or::, _>(Ok(String::from("")), |expr| Ok( + write_expression(&builder.expressions[expr], module, builder)?.0 + ))? + ), + Statement::Kill => String::from("discard;"), + Statement::Store { pointer, value } => format!( + "{} = {};", + write_expression(&builder.expressions[*pointer], module, builder)?.0, + write_expression(&builder.expressions[*value], module, builder)?.0 + ), + }) +} + +fn write_expression<'a>( + expr: &Expression, + module: &'a Module, + builder: &mut StatementBuilder<'_>, +) -> Result<(String, Cow<'a, TypeInner>), Error> { + Ok(match expr { + Expression::Access { base, index } => { + let (base_expr, ty) = write_expression(&builder.expressions[*base], module, builder)?; + + let inner = match ty.as_ref() { + TypeInner::Vector { kind, width, .. } | TypeInner::Matrix { kind, width, .. } => { + Cow::Owned(TypeInner::Scalar { + kind: *kind, + width: *width, + }) + } + TypeInner::Array { base, .. } => Cow::Borrowed(&module.types[*base].inner), + _ => return Err(Error::Custom(format!("Cannot dynamically index {:?}", ty))), + }; + + ( + format!( + "{}[{}]", + base_expr, + write_expression(&builder.expressions[*index], module, builder)?.0 + ), + inner, + ) + } + Expression::AccessIndex { base, index } => { + let (base_expr, ty) = write_expression(&builder.expressions[*base], module, builder)?; + + match ty.as_ref() { + TypeInner::Vector { kind, width, .. } | TypeInner::Matrix { kind, width, .. } => ( + format!("{}[{}]", base_expr, index), + Cow::Owned(TypeInner::Scalar { + kind: *kind, + width: *width, + }), + ), + TypeInner::Array { base, .. } => ( + format!("{}[{}]", base_expr, index), + Cow::Borrowed(&module.types[*base].inner), + ), + TypeInner::Struct { members } => ( + format!( + "{}.{}", + base_expr, + members[*index as usize] + .name + .as_ref() + .unwrap_or(&index.to_string()) + ), + Cow::Borrowed(&module.types[members[*index as usize].ty].inner), + ), + _ => return Err(Error::Custom(format!("Cannot index {:?}", ty))), + } + } + Expression::Constant(constant) => ( + write_constant(&module.constants[*constant], module, builder)?, + Cow::Borrowed(&module.types[module.constants[*constant].ty].inner), + ), + Expression::Compose { ty, components } => { + let constructor = match module.types[*ty].inner { + TypeInner::Vector { size, kind, width } => format!( + "{}vec{}", + match kind { + ScalarKind::Sint => "i", + ScalarKind::Uint => "u", + ScalarKind::Float => match width { + 4 => "", + 8 => "d", + _ => + return Err(Error::Custom(format!( + "Cannot build float of width {}", + width + ))), + }, + ScalarKind::Bool => "b", + }, + size as u8, + ), + TypeInner::Matrix { + columns, + rows, + kind, + width, + } => format!( + "{}mat{}x{}", + match kind { + ScalarKind::Sint => "i", + ScalarKind::Uint => "u", + ScalarKind::Float => match width { + 4 => "", + 8 => "d", + _ => + return Err(Error::Custom(format!( + "Cannot build float of width {}", + width + ))), + }, + ScalarKind::Bool => "b", + }, + columns as u8, + rows as u8, + ), + TypeInner::Array { .. } => write_type(*ty, &module.types, builder.structs)?, + TypeInner::Struct { .. } => builder.structs.get(ty).unwrap().clone(), + _ => { + return Err(Error::Custom(format!( + "Cannot compose type {}", + write_type(*ty, &module.types, builder.structs)? + ))) + } + }; + + ( + format!( + "{}({})", + constructor, + components + .iter() + .map::, _>(|arg| Ok(write_expression( + &builder.expressions[*arg], + module, + builder + )? + .0)) + .collect::, _>>()? + .join(","), + ), + Cow::Borrowed(&module.types[*ty].inner), + ) + } + Expression::FunctionParameter(pos) => { + let (arg, ty) = builder.args.get(&pos).unwrap().clone(); + + (arg, Cow::Borrowed(&module.types[ty].inner)) + } + Expression::GlobalVariable(handle) => ( + builder.globals.get(&handle).unwrap().clone(), + Cow::Borrowed(&module.types[module.global_variables[*handle].ty].inner), + ), + Expression::LocalVariable(handle) => ( + builder.locals_lookup.get(&handle).unwrap().clone(), + Cow::Borrowed(&module.types[builder.locals[*handle].ty].inner), + ), + Expression::Load { pointer } => { + write_expression(&builder.expressions[*pointer], module, builder)? + } + Expression::ImageSample { + image, + sampler, + coordinate, + depth_ref, + } => { + let (image_expr, image_ty) = + write_expression(&builder.expressions[*image], module, builder)?; + let (sampler_expr, sampler_ty) = + write_expression(&builder.expressions[*sampler], module, builder)?; + let (coordinate_expr, coordinate_ty) = + write_expression(&builder.expressions[*coordinate], module, builder)?; + + let (kind, dim, arrayed, ms, width) = match image_ty.as_ref() { + TypeInner::Image { base, dim, flags } => match module.types[*base].inner { + TypeInner::Scalar { kind, width } => ( + kind, + *dim, + flags.contains(ImageFlags::ARRAYED), + flags.contains(ImageFlags::MULTISAMPLED), + width, + ), + _ => { + return Err(Error::Custom(format!( + "Cannot build image of {}", + write_type(*base, &module.types, builder.structs)? + ))) + } + }, + TypeInner::DepthImage { dim, arrayed } => { + (ScalarKind::Float, *dim, *arrayed, false, 4) + } + _ => return Err(Error::Custom(format!("Cannot sample {:?}", image_ty))), + }; + + let shadow = match sampler_ty.as_ref() { + TypeInner::Sampler { comparison } => *comparison, + _ => { + return Err(Error::Custom(format!( + "Cannot have a sampler of {:?}", + sampler_ty + ))) + } + }; + + let size = match coordinate_ty.as_ref() { + TypeInner::Vector { size, .. } => *size, + _ => { + return Err(Error::Custom(format!( + "Cannot sample with coordinates of type {:?}", + coordinate_ty + ))) + } + }; + + let sampler_constructor = format!( + "{}sampler{}{}{}{}({},{})", + match kind { + ScalarKind::Sint => "i", + ScalarKind::Uint => "u", + ScalarKind::Float => "", + _ => return Err(Error::Custom(String::from("Cannot build image of bools",))), + }, + ImageDimension(dim), + if ms { "MS" } else { "" }, + if arrayed { "Array" } else { "" }, + if shadow { "Shadow" } else { "" }, + image_expr, + sampler_expr + ); + + let coordinate = if let Some(depth_ref) = depth_ref { + format!( + "vec{}({},{})", + size as u8 + 1, + coordinate_expr, + write_expression(&builder.expressions[*depth_ref], module, builder)?.0 + ) + } else { + coordinate_expr + }; + + let expr = if !ms { + format!("texture({},{})", sampler_constructor, coordinate) + } else { + todo!() + }; + + let ty = if shadow { + Cow::Owned(TypeInner::Scalar { kind, width }) + } else { + Cow::Owned(TypeInner::Vector { kind, width, size }) + }; + + (expr, ty) + } + Expression::Unary { op, expr } => { + let (expr, ty) = write_expression(&builder.expressions[*expr], module, builder)?; + + ( + format!( + "({} {})", + match op { + UnaryOperator::Negate => "-", + UnaryOperator::Not => "~", + }, + expr + ), + ty, + ) + } + Expression::Binary { op, left, right } => { + let (left_expr, left_ty) = + write_expression(&builder.expressions[*left], module, builder)?; + let (right_expr, right_ty) = + write_expression(&builder.expressions[*right], module, builder)?; + + let op = 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::ShiftLeftLogical => "<<", + BinaryOperator::ShiftRightLogical => todo!(), + BinaryOperator::ShiftRightArithmetic => ">>", + }; + + let ty = match (left_ty.as_ref(), right_ty.as_ref()) { + (TypeInner::Scalar { .. }, TypeInner::Scalar { .. }) => left_ty, + (TypeInner::Scalar { .. }, TypeInner::Vector { .. }) => right_ty, + (TypeInner::Scalar { .. }, TypeInner::Matrix { .. }) => right_ty, + (TypeInner::Vector { .. }, TypeInner::Scalar { .. }) => left_ty, + (TypeInner::Vector { .. }, TypeInner::Vector { .. }) => left_ty, + (TypeInner::Vector { .. }, TypeInner::Matrix { .. }) => left_ty, + (TypeInner::Matrix { .. }, TypeInner::Scalar { .. }) => left_ty, + (TypeInner::Matrix { .. }, TypeInner::Vector { .. }) => right_ty, + (TypeInner::Matrix { .. }, TypeInner::Matrix { .. }) => left_ty, + _ => { + return Err(Error::Custom(format!( + "Cannot apply {} to {} and {}", + op, left_expr, right_expr + ))) + } + }; + + (format!("({} {} {})", left_expr, op, right_expr), ty) + } + Expression::Intrinsic { fun, argument } => { + let (expr, ty) = write_expression(&builder.expressions[*argument], module, builder)?; + + ( + format!( + "{:?}({})", + match fun { + IntrinsicFunction::IsFinite => "!isinf", + IntrinsicFunction::IsInf => "isinf", + IntrinsicFunction::IsNan => "isnan", + IntrinsicFunction::IsNormal => "!isnan", + IntrinsicFunction::All => "all", + IntrinsicFunction::Any => "any", + }, + expr + ), + ty, + ) + } + Expression::DotProduct(left, right) => { + let (left_expr, left_ty) = + write_expression(&builder.expressions[*left], module, builder)?; + let (right_expr, _) = write_expression(&builder.expressions[*right], module, builder)?; + + let ty = match left_ty.as_ref() { + TypeInner::Vector { kind, width, .. } => Cow::Owned(TypeInner::Scalar { + kind: *kind, + width: *width, + }), + _ => { + return Err(Error::Custom(format!( + "Cannot apply dot product to {}", + left_expr + ))) + } + }; + + (format!("dot({},{})", left_expr, right_expr), ty) + } + Expression::CrossProduct(left, right) => { + let (left_expr, left_ty) = + write_expression(&builder.expressions[*left], module, builder)?; + let (right_expr, _) = write_expression(&builder.expressions[*right], module, builder)?; + + (format!("cross({},{})", left_expr, right_expr), left_ty) + } + Expression::Derivative { axis, expr } => { + let (expr, ty) = write_expression(&builder.expressions[*expr], module, builder)?; + + ( + format!( + "{}({})", + match axis { + DerivativeAxis::X => "dFdx", + DerivativeAxis::Y => "dFdy", + DerivativeAxis::Width => "fwidth", + }, + expr + ), + ty, + ) + } + Expression::Call { origin, arguments } => { + let ty = match origin { + FunctionOrigin::Local(function) => module.functions[*function] + .return_type + .map(|ty| Cow::Borrowed(&module.types[ty].inner)) + .unwrap_or(Cow::Owned( + TypeInner::Sampler { comparison: false }, /*Dummy type*/ + )), + FunctionOrigin::External(_) => { + write_expression(&builder.expressions[arguments[0]], module, builder)?.1 + } + }; + + ( + format!( + "{}({})", + match origin { + FunctionOrigin::External(name) => name, + FunctionOrigin::Local(handle) => builder.functions.get(&handle).unwrap(), + }, + arguments + .iter() + .map::, _>(|arg| Ok(write_expression( + &builder.expressions[*arg], + module, + builder + )? + .0)) + .collect::, _>>()? + .join(","), + ), + ty, + ) + } + }) +} + +fn write_constant( + constant: &Constant, + module: &Module, + builder: &StatementBuilder<'_>, +) -> Result { + Ok(match constant.inner { + ConstantInner::Sint(int) => int.to_string(), + ConstantInner::Uint(int) => int.to_string(), + 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, .. } => format!("vec{}", size as u8,), + TypeInner::Matrix { columns, rows, .. } => + format!("mat{}x{}", columns as u8, rows as u8,), + TypeInner::Struct { .. } => builder.structs.get(&constant.ty).unwrap().clone(), + TypeInner::Array { .. } => write_type(constant.ty, &module.types, builder.structs)?, + _ => + return Err(Error::Custom(format!( + "Cannot build constant of type {}", + write_type(constant.ty, &module.types, builder.structs)? + ))), + }, + components + .iter() + .map(|component| write_constant(&module.constants[*component], module, builder)) + .collect::, _>>()? + .join(","), + ), + }) +} + +fn write_type<'a>( + ty: Handle, + types: &'a Arena, + structs: &'a FastHashMap, String>, +) -> Result { + Ok(match types[ty].inner { + TypeInner::Scalar { kind, width } => match kind { + ScalarKind::Sint => String::from("int"), + ScalarKind::Uint => String::from("uint"), + ScalarKind::Float => match width { + 4 => String::from("float"), + 8 => String::from("double"), + _ => { + return Err(Error::Custom(format!( + "Cannot build float of width {}", + width + ))) + } + }, + ScalarKind::Bool => String::from("bool"), + }, + TypeInner::Vector { size, kind, width } => format!( + "{}vec{}", + match kind { + ScalarKind::Sint => "i", + ScalarKind::Uint => "u", + ScalarKind::Float => match width { + 4 => "", + 8 => "d", + _ => + return Err(Error::Custom(format!( + "Cannot build float of width {}", + width + ))), + }, + ScalarKind::Bool => "b", + }, + size as u8 + ), + TypeInner::Matrix { + columns, + rows, + kind, + width, + } => format!( + "{}mat{}x{}", + match kind { + ScalarKind::Sint => "i", + ScalarKind::Uint => "u", + ScalarKind::Float => match width { + 4 => "", + 8 => "d", + _ => + return Err(Error::Custom(format!( + "Cannot build float of width {}", + width + ))), + }, + ScalarKind::Bool => "b", + }, + columns as u8, + rows as u8 + ), + TypeInner::Pointer { base, .. } => write_type(base, types, structs)?, + TypeInner::Array { base, size, .. } => format!( + "{}[{}]", + write_type(base, types, structs)?, + write_array_size(size)? + ), + TypeInner::Struct { .. } => structs.get(&ty).unwrap().clone(), + TypeInner::Image { base, dim, flags } => format!( + "{}texture{}{}", + match types[base].inner { + TypeInner::Scalar { kind, .. } => match kind { + ScalarKind::Sint => "i", + ScalarKind::Uint => "u", + ScalarKind::Float => "", + _ => + return Err(Error::Custom(String::from( + "Cannot build image of booleans", + ))), + }, + _ => + return Err(Error::Custom(format!( + "Cannot build image of type {}", + write_type(base, types, structs)? + ))), + }, + ImageDimension(dim), + write_image_flags(flags)? + ), + TypeInner::DepthImage { dim, arrayed } => format!( + "texture{}{}", + ImageDimension(dim), + if arrayed { "Array" } else { "" } + ), + TypeInner::Sampler { comparison } => String::from(if comparison { + "sampler" + } else { + "samplerShadow" + }), + }) +} + +fn write_storage_class(class: StorageClass) -> Result { + Ok(String::from(match class { + StorageClass::Constant => "const ", + StorageClass::Function => "", + StorageClass::Input => "in ", + StorageClass::Output => "out ", + StorageClass::Private => "", + StorageClass::StorageBuffer => "buffer ", + StorageClass::Uniform => "uniform ", + StorageClass::WorkGroup => "shared ", + })) +} + +fn write_array_size(size: ArraySize) -> Result { + Ok(match size { + ArraySize::Static(size) => size.to_string(), + ArraySize::Dynamic => String::from(""), + }) +} + +fn write_image_flags(flags: ImageFlags) -> Result { + let mut out = String::new(); + + if flags.contains(ImageFlags::MULTISAMPLED) { + write!(out, "MS")?; + } + + if flags.contains(ImageFlags::ARRAYED) { + write!(out, "Array")?; + } + + Ok(out) +} + +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", + } + ) + } +} diff --git a/src/back/mod.rs b/src/back/mod.rs index ce751b11ec..11439f9346 100644 --- a/src/back/mod.rs +++ b/src/back/mod.rs @@ -1,5 +1,7 @@ //! Functions which export shader modules into binary and text formats. +#[cfg(feature = "glsl-out")] +pub mod glsl; pub mod msl; #[cfg(feature = "spirv")] pub mod spv;