From 65e915f9985e19183ee36d5acf299d35e6d91deb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Capucho?= Date: Thu, 29 Jul 2021 22:37:24 +0100 Subject: [PATCH] [glsl-in] Implicit convert texture functions --- src/front/glsl/ast.rs | 3 +- src/front/glsl/functions.rs | 1444 +++++++++++++++++------------------ 2 files changed, 716 insertions(+), 731 deletions(-) diff --git a/src/front/glsl/ast.rs b/src/front/glsl/ast.rs index fca6d44986..3deecf4836 100644 --- a/src/front/glsl/ast.rs +++ b/src/front/glsl/ast.rs @@ -625,7 +625,8 @@ impl<'function> Context<'function> { } } HirExprKind::Call(call) if !lhs => { - let maybe_expr = program.function_call(self, body, call.kind, &call.args, meta)?; + let maybe_expr = program + .function_or_constructor_call(self, body, call.kind, &call.args, meta)?; return Ok((maybe_expr, meta)); } HirExprKind::Conditional { diff --git a/src/front/glsl/functions.rs b/src/front/glsl/functions.rs index 126b4fadb8..f6f6f5ea7c 100644 --- a/src/front/glsl/functions.rs +++ b/src/front/glsl/functions.rs @@ -23,7 +23,7 @@ impl Program<'_> { }) } - pub fn function_call( + pub fn function_or_constructor_call( &mut self, ctx: &mut Context, body: &mut Block, @@ -255,745 +255,729 @@ impl Program<'_> { Ok(Some(h)) } FunctionCallKind::Function(name) => { - match name.as_str() { - "sampler1D" - | "sampler1DArray" - | "sampler2D" - | "sampler2DArray" - | "sampler2DMS" - | "sampler2DMSArray" - | "sampler3D" - | "samplerCube" - | "samplerCubeArray" - | "sampler1DShadow" - | "sampler1DArrayShadow" - | "sampler2DShadow" - | "sampler2DArrayShadow" - | "samplerCubeShadow" - | "samplerCubeArrayShadow" => { - if args.len() != 2 { - return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); - } - ctx.samplers.insert(args[0].0, args[1].0); - Ok(Some(args[0].0)) - } - "texture" => { - if !(2..=3).contains(&args.len()) { - return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); - } - if let Some(sampler) = ctx.samplers.get(&args[0].0).copied() { - Ok(Some(ctx.add_expression( - Expression::ImageSample { - image: args[0].0, - sampler, - coordinate: args[1].0, - array_index: None, //TODO - offset: None, //TODO - level: args.get(2).map_or(SampleLevel::Auto, |&(expr, _)| { - SampleLevel::Bias(expr) - }), - depth_ref: None, - }, - body, - ))) - } else { - Err(ErrorKind::SemanticError(meta, "Bad call to texture".into())) - } - } - "textureLod" => { - if args.len() != 3 { - return Err(ErrorKind::wrong_function_args(name, 3, args.len(), meta)); - } - let exact = ctx.add_expression( - Expression::As { - kind: crate::ScalarKind::Float, - expr: args[2].0, - convert: Some(4), - }, - body, - ); - if let Some(sampler) = ctx.samplers.get(&args[0].0).copied() { - Ok(Some(ctx.add_expression( - Expression::ImageSample { - image: args[0].0, - sampler, - coordinate: args[1].0, - array_index: None, //TODO - offset: None, //TODO - level: SampleLevel::Exact(exact), - depth_ref: None, - }, - body, - ))) - } else { - Err(ErrorKind::SemanticError( - meta, - "Bad call to textureLod".into(), - )) - } - } - "textureProj" => { - if !(2..=3).contains(&args.len()) { - return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); - } - let level = args.get(2).map_or(SampleLevel::Auto, |&(expr, _)| { - let exact = ctx.add_expression( - Expression::As { - kind: ScalarKind::Float, - expr, - convert: Some(4), - }, - body, - ); - SampleLevel::Bias(exact) - }); - let dim = match *self.resolve_type(ctx, args[0].0, args[0].1)? { - TypeInner::Image { dim, .. } => match dim { - crate::ImageDimension::D1 => 1, - crate::ImageDimension::D2 => 2, - crate::ImageDimension::D3 => 3, - crate::ImageDimension::Cube => { - return Err(ErrorKind::SemanticError( - meta, - "textureProj doesn't accept cube texture".into(), - )) - } - }, - _ => { - return Err(ErrorKind::SemanticError( - meta, - "Bad call to textureProj".into(), - )) - } - }; - match *self.resolve_type(ctx, args[1].0, args[1].1)? { - TypeInner::Vector { size, .. } => { - if !(size as usize + 1 == dim || size == VectorSize::Quad) { - return Err(ErrorKind::SemanticError( - meta, - "Bad call to textureProj".into(), - )); - } - } - _ => { - return Err(ErrorKind::SemanticError( - meta, - "Bad call to textureProj".into(), - )) - } - } - if let Some(sampler) = ctx.samplers.get(&args[0].0).copied() { - Ok(Some(ctx.add_expression( - Expression::ImageSample { - image: args[0].0, - sampler, - coordinate: args[1].0, - array_index: None, //TODO - offset: None, //TODO - level, - depth_ref: None, - }, - body, - ))) - } else { - Err(ErrorKind::SemanticError( - meta, - "Bad call to textureProj".into(), - )) - } - } - "textureSize" => { - if !(1..=2).contains(&args.len()) { - return Err(ErrorKind::wrong_function_args(name, 1, args.len(), meta)); - } + self.function_call(ctx, body, name, args, raw_args, meta) + } + } + } - Ok(Some(ctx.add_expression( - Expression::ImageQuery { + fn function_call( + &mut self, + ctx: &mut Context, + body: &mut Block, + name: String, + mut args: Vec<(Handle, SourceMetadata)>, + raw_args: &[Handle], + meta: SourceMetadata, + ) -> Result>, ErrorKind> { + match name.as_str() { + "sampler1D" + | "sampler1DArray" + | "sampler2D" + | "sampler2DArray" + | "sampler2DMS" + | "sampler2DMSArray" + | "sampler3D" + | "samplerCube" + | "samplerCubeArray" + | "sampler1DShadow" + | "sampler1DArrayShadow" + | "sampler2DShadow" + | "sampler2DArrayShadow" + | "samplerCubeShadow" + | "samplerCubeArrayShadow" => { + if args.len() != 2 { + return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); + } + ctx.samplers.insert(args[0].0, args[1].0); + Ok(Some(args[0].0)) + } + "texture" => { + if !(2..=3).contains(&args.len()) { + return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); + } + let arg_1 = &mut args[1]; + ctx.implicit_conversion(self, &mut arg_1.0, arg_1.1, ScalarKind::Float, 4)?; + if let Some(&mut (ref mut expr, meta)) = args.get_mut(2) { + ctx.implicit_conversion(self, expr, meta, ScalarKind::Float, 4)?; + } + if let Some(sampler) = ctx.samplers.get(&args[0].0).copied() { + Ok(Some( + ctx.add_expression( + Expression::ImageSample { image: args[0].0, - query: ImageQuery::Size { - level: args.get(1).map(|e| e.0), - }, + sampler, + coordinate: args[1].0, + array_index: None, //TODO + offset: None, + level: args.get(2).map_or(SampleLevel::Auto, |&(expr, _)| { + SampleLevel::Bias(expr) + }), + depth_ref: None, }, body, - ))) - } - "texelFetch" => { - if args.len() != 3 { - return Err(ErrorKind::wrong_function_args(name, 3, args.len(), meta)); - } - if ctx.samplers.get(&args[0].0).is_some() { - let (arrayed, dims) = - match *self.resolve_type(ctx, args[0].0, args[0].1)? { - TypeInner::Image { arrayed, dim, .. } => (arrayed, dim), - _ => (false, crate::ImageDimension::D1), - }; - - let (coordinate, array_index) = if arrayed { - ( - match dims { - crate::ImageDimension::D1 => ctx.add_expression( - Expression::AccessIndex { - base: args[1].0, - index: 0, - }, - body, - ), - crate::ImageDimension::D2 => ctx.add_expression( - Expression::Swizzle { - size: VectorSize::Bi, - vector: args[1].0, - pattern: SwizzleComponent::XYZW, - }, - body, - ), - _ => ctx.add_expression( - Expression::Swizzle { - size: VectorSize::Tri, - vector: args[1].0, - pattern: SwizzleComponent::XYZW, - }, - body, - ), - }, - Some(ctx.add_expression( - Expression::AccessIndex { - base: args[1].0, - index: match dims { - crate::ImageDimension::D1 => 1, - crate::ImageDimension::D2 => 2, - crate::ImageDimension::D3 => 3, - crate::ImageDimension::Cube => 2, - }, - }, - body, - )), - ) - } else { - (args[1].0, None) - }; - - Ok(Some(ctx.add_expression( - Expression::ImageLoad { - image: args[0].0, - coordinate, - array_index, - index: Some(args[2].0), - }, - body, - ))) - } else { - Err(ErrorKind::SemanticError( - meta, - "Bad call to texelFetch".into(), - )) - } - } - "ceil" | "round" | "floor" | "fract" | "trunc" | "sin" | "abs" | "sqrt" - | "inversesqrt" | "exp" | "exp2" | "sign" | "transpose" | "inverse" - | "normalize" | "sinh" | "cos" | "cosh" | "tan" | "tanh" | "acos" | "asin" - | "log" | "log2" | "length" | "determinant" | "bitCount" - | "bitfieldReverse" => { - if args.len() != 1 { - return Err(ErrorKind::wrong_function_args(name, 1, args.len(), meta)); - } - Ok(Some(ctx.add_expression( - Expression::Math { - fun: match name.as_str() { - "ceil" => MathFunction::Ceil, - "round" => MathFunction::Round, - "floor" => MathFunction::Floor, - "fract" => MathFunction::Fract, - "trunc" => MathFunction::Trunc, - "sin" => MathFunction::Sin, - "abs" => MathFunction::Abs, - "sqrt" => MathFunction::Sqrt, - "inversesqrt" => MathFunction::InverseSqrt, - "exp" => MathFunction::Exp, - "exp2" => MathFunction::Exp2, - "sign" => MathFunction::Sign, - "transpose" => MathFunction::Transpose, - "inverse" => MathFunction::Inverse, - "normalize" => MathFunction::Normalize, - "sinh" => MathFunction::Sinh, - "cos" => MathFunction::Cos, - "cosh" => MathFunction::Cosh, - "tan" => MathFunction::Tan, - "tanh" => MathFunction::Tanh, - "acos" => MathFunction::Acos, - "asin" => MathFunction::Asin, - "log" => MathFunction::Log, - "log2" => MathFunction::Log2, - "length" => MathFunction::Length, - "determinant" => MathFunction::Determinant, - "bitCount" => MathFunction::CountOneBits, - "bitfieldReverse" => MathFunction::ReverseBits, - _ => unreachable!(), - }, - arg: args[0].0, - arg1: None, - arg2: None, - }, - body, - ))) - } - "atan" => { - let expr = match args.len() { - 1 => Expression::Math { - fun: MathFunction::Atan, - arg: args[0].0, - arg1: None, - arg2: None, - }, - 2 => Expression::Math { - fun: MathFunction::Atan2, - arg: args[0].0, - arg1: Some(args[1].0), - arg2: None, - }, - _ => { - return Err(ErrorKind::wrong_function_args( - name, - 2, - args.len(), - meta, - )) - } - }; - Ok(Some(ctx.add_expression(expr, body))) - } - "mod" => { - if args.len() != 2 { - return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); - } - - let (mut left, left_meta) = args[0]; - let (mut right, right_meta) = args[1]; - - ctx.binary_implicit_conversion( - self, &mut left, left_meta, &mut right, right_meta, - )?; - - Ok(Some(ctx.add_expression( - Expression::Binary { - op: BinaryOperator::Modulo, - left, - right, - }, - body, - ))) - } - "max" => { - if args.len() != 2 { - return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); - } - - let (mut arg0, arg0_meta) = args[0]; - let (mut arg1, arg1_meta) = args[1]; - - let arg0_size = match *self.resolve_type(ctx, arg0, arg0_meta)? { - TypeInner::Vector { size, .. } => Some(size), - _ => None, - }; - let arg1_size = match *self.resolve_type(ctx, arg1, arg1_meta)? { - TypeInner::Vector { size, .. } => Some(size), - _ => None, - }; - - ctx.binary_implicit_conversion( - self, &mut arg0, arg0_meta, &mut arg1, arg1_meta, - )?; - - ctx.implicit_splat(self, &mut arg0, arg0_meta, arg1_size)?; - ctx.implicit_splat(self, &mut arg1, arg1_meta, arg0_size)?; - - Ok(Some(ctx.add_expression( - Expression::Math { - fun: MathFunction::Max, - arg: arg0, - arg1: Some(arg1), - arg2: None, - }, - body, - ))) - } - "pow" | "dot" | "min" | "reflect" | "cross" | "outerProduct" | "distance" - | "step" | "modf" | "frexp" | "ldexp" => { - if args.len() != 2 { - return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); - } - - let (mut arg0, arg0_meta) = args[0]; - let (mut arg1, arg1_meta) = args[1]; - - ctx.binary_implicit_conversion( - self, &mut arg0, arg0_meta, &mut arg1, arg1_meta, - )?; - - Ok(Some(ctx.add_expression( - Expression::Math { - fun: match name.as_str() { - "pow" => MathFunction::Pow, - "dot" => MathFunction::Dot, - "min" => MathFunction::Min, - "reflect" => MathFunction::Reflect, - "cross" => MathFunction::Cross, - "outerProduct" => MathFunction::Outer, - "distance" => MathFunction::Distance, - "step" => MathFunction::Step, - "modf" => MathFunction::Modf, - "frexp" => MathFunction::Frexp, - "ldexp" => MathFunction::Ldexp, - _ => unreachable!(), - }, - arg: arg0, - arg1: Some(arg1), - arg2: None, - }, - body, - ))) - } - "mix" => { - if args.len() != 3 { - return Err(ErrorKind::wrong_function_args(name, 3, args.len(), meta)); - } - - let (mut arg, arg_meta) = args[0]; - let (mut arg1, arg1_meta) = args[1]; - let (mut selector, selector_meta) = args[2]; - - ctx.binary_implicit_conversion( - self, &mut arg, arg_meta, &mut arg1, arg1_meta, - )?; - ctx.binary_implicit_conversion( - self, - &mut arg, - arg_meta, - &mut selector, - selector_meta, - )?; - - let is_vector = match *self.resolve_type(ctx, selector, selector_meta)? { - TypeInner::Vector { .. } => true, - _ => false, - }; - match *self.resolve_type(ctx, args[0].0, args[0].1)? { - TypeInner::Vector { size, .. } if !is_vector => { - selector = ctx.add_expression( - Expression::Splat { - size, - value: selector, - }, - body, - ) - } - _ => {} - }; - - let expr = match self - .resolve_type(ctx, selector, selector_meta)? - .scalar_kind() - { - Some(ScalarKind::Bool) => Expression::Select { - condition: selector, - accept: arg, - reject: arg1, - }, - _ => Expression::Math { - fun: MathFunction::Mix, - arg, - arg1: Some(arg1), - arg2: Some(selector), - }, - }; - - Ok(Some(ctx.add_expression(expr, body))) - } - "clamp" => { - if args.len() != 3 { - return Err(ErrorKind::wrong_function_args(name, 3, args.len(), meta)); - } - - let (mut arg0, arg0_meta) = args[0]; - let (mut arg1, arg1_meta) = args[1]; - let (mut arg2, arg2_meta) = args[2]; - - let vector_size = match *(self.resolve_type(ctx, arg0, arg0_meta)?) { - TypeInner::Vector { size, .. } => Some(size), - _ => None, - }; - - ctx.binary_implicit_conversion( - self, &mut arg0, arg0_meta, &mut arg1, arg1_meta, - )?; - ctx.binary_implicit_conversion( - self, &mut arg1, arg1_meta, &mut arg2, arg2_meta, - )?; - ctx.binary_implicit_conversion( - self, &mut arg2, arg2_meta, &mut arg0, arg0_meta, - )?; - - ctx.implicit_splat(self, &mut arg1, arg1_meta, vector_size)?; - ctx.implicit_splat(self, &mut arg2, arg2_meta, vector_size)?; - - Ok(Some(ctx.add_expression( - Expression::Math { - fun: MathFunction::Clamp, - arg: arg0, - arg1: Some(arg1), - arg2: Some(arg2), - }, - body, - ))) - } - "faceforward" | "refract" | "fma" | "smoothstep" => { - if args.len() != 3 { - return Err(ErrorKind::wrong_function_args(name, 3, args.len(), meta)); - } - Ok(Some(ctx.add_expression( - Expression::Math { - fun: match name.as_str() { - "faceforward" => MathFunction::FaceForward, - "refract" => MathFunction::Refract, - "fma" => MathFunction::Fma, - "smoothstep" => MathFunction::SmoothStep, - _ => unreachable!(), - }, - arg: args[0].0, - arg1: Some(args[1].0), - arg2: Some(args[2].0), - }, - body, - ))) - } - "lessThan" | "greaterThan" | "lessThanEqual" | "greaterThanEqual" | "equal" - | "notEqual" => { - if args.len() != 2 { - return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); - } - Ok(Some(ctx.add_expression( - Expression::Binary { - op: match name.as_str() { - "lessThan" => BinaryOperator::Less, - "greaterThan" => BinaryOperator::Greater, - "lessThanEqual" => BinaryOperator::LessEqual, - "greaterThanEqual" => BinaryOperator::GreaterEqual, - "equal" => BinaryOperator::Equal, - "notEqual" => BinaryOperator::NotEqual, - _ => unreachable!(), - }, - left: args[0].0, - right: args[1].0, - }, - body, - ))) - } - "isinf" | "isnan" | "all" | "any" => { - let fun = match name.as_str() { - "isinf" => RelationalFunction::IsInf, - "isnan" => RelationalFunction::IsNan, - "all" => RelationalFunction::All, - "any" => RelationalFunction::Any, - _ => unreachable!(), - }; - - Ok(Some( - self.parse_relational_fun(ctx, body, name, &args, fun, meta)?, - )) - } - _ => { - let declarations = self.lookup_function.get(&name).ok_or_else(|| { - ErrorKind::SemanticError( - meta, - format!("Unknown function '{}'", name).into(), - ) - })?; - - let mut maybe_decl = None; - let mut ambiguous = false; - - 'outer: for decl in declarations { - if args.len() != decl.parameters.len() { - continue; - } - - let mut exact = true; - - for (decl_arg, call_arg) in decl.parameters.iter().zip(args.iter()) { - let decl_inner = &self.module.types[*decl_arg].inner; - let call_inner = self.resolve_type(ctx, call_arg.0, call_arg.1)?; - - if decl_inner == call_inner { - continue; - } - - exact = false; - - let (decl_kind, call_kind) = match (decl_inner, call_inner) { - ( - &TypeInner::Scalar { - kind: decl_kind, .. - }, - &TypeInner::Scalar { - kind: call_kind, .. - }, - ) => (decl_kind, call_kind), - ( - &TypeInner::Vector { - kind: decl_kind, - size: decl_size, - .. - }, - &TypeInner::Vector { - kind: call_kind, - size: call_size, - .. - }, - ) if decl_size == call_size => (decl_kind, call_kind), - ( - &TypeInner::Matrix { - rows: decl_rows, - columns: decl_columns, - .. - }, - &TypeInner::Matrix { - rows: call_rows, - columns: call_columns, - .. - }, - ) if decl_columns == call_columns && decl_rows == call_rows => { - (ScalarKind::Float, ScalarKind::Float) - } - _ => continue 'outer, - }; - - match (type_power(decl_kind), type_power(call_kind)) { - (Some(decl_power), Some(call_power)) - if decl_power > call_power => {} - _ => continue 'outer, - } - } - - if exact { - maybe_decl = Some(decl); - ambiguous = false; - break; - } else if maybe_decl.is_some() { - ambiguous = true; - } else { - maybe_decl = Some(decl) - } - } - - if ambiguous { + ), + )) + } else { + Err(ErrorKind::SemanticError(meta, "Bad call to texture".into())) + } + } + "textureLod" => { + if args.len() != 3 { + return Err(ErrorKind::wrong_function_args(name, 3, args.len(), meta)); + } + let arg_1 = &mut args[1]; + ctx.implicit_conversion(self, &mut arg_1.0, arg_1.1, ScalarKind::Float, 4)?; + let arg_2 = &mut args[2]; + ctx.implicit_conversion(self, &mut arg_2.0, arg_2.1, ScalarKind::Float, 4)?; + if let Some(sampler) = ctx.samplers.get(&args[0].0).copied() { + Ok(Some(ctx.add_expression( + Expression::ImageSample { + image: args[0].0, + sampler, + coordinate: args[1].0, + array_index: None, //TODO + offset: None, + level: SampleLevel::Exact(args[2].0), + depth_ref: None, + }, + body, + ))) + } else { + Err(ErrorKind::SemanticError( + meta, + "Bad call to textureLod".into(), + )) + } + } + "textureProj" => { + if !(2..=3).contains(&args.len()) { + return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); + } + let arg_1 = &mut args[1]; + ctx.implicit_conversion(self, &mut arg_1.0, arg_1.1, ScalarKind::Float, 4)?; + if let Some(&mut (ref mut expr, meta)) = args.get_mut(2) { + ctx.implicit_conversion(self, expr, meta, ScalarKind::Float, 4)?; + } + let level = args + .get(2) + .map_or(SampleLevel::Auto, |&(expr, _)| SampleLevel::Bias(expr)); + let dim = match *self.resolve_type(ctx, args[0].0, args[0].1)? { + TypeInner::Image { dim, .. } => match dim { + crate::ImageDimension::D1 => 1, + crate::ImageDimension::D2 => 2, + crate::ImageDimension::D3 => 3, + crate::ImageDimension::Cube => { return Err(ErrorKind::SemanticError( meta, - format!("Ambiguous best function for '{}'", name).into(), + "textureProj doesn't accept cube texture".into(), + )) + } + }, + _ => { + return Err(ErrorKind::SemanticError( + meta, + "Bad call to textureProj".into(), + )) + } + }; + match *self.resolve_type(ctx, args[1].0, args[1].1)? { + TypeInner::Vector { size, .. } => { + if !(size as usize + 1 == dim || size == VectorSize::Quad) { + return Err(ErrorKind::SemanticError( + meta, + "Bad call to textureProj".into(), )); } - - let decl = maybe_decl.ok_or_else(|| { - ErrorKind::SemanticError( - meta, - format!("Unknown function '{}'", name).into(), - ) - })?; - - let qualifiers = decl.qualifiers.clone(); - let parameters = decl.parameters.clone(); - let function = decl.handle; - let is_void = decl.void; - - let mut arguments = Vec::with_capacity(args.len()); - let mut proxy_writes = Vec::new(); - for (qualifier, (expr, parameter)) in qualifiers - .iter() - .zip(raw_args.iter().zip(parameters.iter())) - { - let (mut handle, meta) = - ctx.lower_expect(self, *expr, qualifier.is_lhs(), body)?; - - if let TypeInner::Vector { size, kind, width } = - *self.resolve_type(ctx, handle, meta)? - { - if qualifier.is_lhs() - && matches!( - *ctx.get_expression(handle), - Expression::Swizzle { .. } - ) - { - let ty = self.module.types.fetch_or_append(Type { - name: None, - inner: TypeInner::Vector { size, kind, width }, - }); - let temp_var = ctx.locals.append(LocalVariable { - name: None, - ty, - init: None, - }); - let temp_expr = ctx - .add_expression(Expression::LocalVariable(temp_var), body); - - body.push(Statement::Store { - pointer: temp_expr, - value: handle, - }); - - arguments.push(temp_expr); - proxy_writes.push((*expr, temp_expr)); - continue; - } - } - - let scalar_components = - scalar_components(&self.module.types[*parameter].inner); - if let Some((kind, width)) = scalar_components { - ctx.implicit_conversion(self, &mut handle, meta, kind, width)?; - } - - arguments.push(handle) - } - - ctx.emit_flush(body); - - let result = if !is_void { - Some(ctx.add_expression(Expression::Call(function), body)) - } else { - None - }; - - body.push(crate::Statement::Call { - function, - arguments, - result, - }); - - ctx.emit_start(); - for (tgt, pointer) in proxy_writes { - let temp_ref = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Variable(VariableReference { - expr: pointer, - load: true, - mutable: true, - entry_arg: None, - }), - meta, - }); - let assign = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Assign { - tgt, - value: temp_ref, - }, - meta, - }); - - let _ = ctx.lower_expect(self, assign, false, body)?; - } - ctx.emit_flush(body); - ctx.emit_start(); - - Ok(result) + } + _ => { + return Err(ErrorKind::SemanticError( + meta, + "Bad call to textureProj".into(), + )) } } + if let Some(sampler) = ctx.samplers.get(&args[0].0).copied() { + Ok(Some(ctx.add_expression( + Expression::ImageSample { + image: args[0].0, + sampler, + coordinate: args[1].0, + array_index: None, //TODO + offset: None, + level, + depth_ref: None, + }, + body, + ))) + } else { + Err(ErrorKind::SemanticError( + meta, + "Bad call to textureProj".into(), + )) + } + } + "textureSize" => { + if !(1..=2).contains(&args.len()) { + return Err(ErrorKind::wrong_function_args(name, 1, args.len(), meta)); + } + if let Some(&mut (ref mut expr, meta)) = args.get_mut(1) { + ctx.implicit_conversion(self, expr, meta, ScalarKind::Sint, 4)?; + } + Ok(Some(ctx.add_expression( + Expression::ImageQuery { + image: args[0].0, + query: ImageQuery::Size { + level: args.get(1).map(|e| e.0), + }, + }, + body, + ))) + } + "texelFetch" => { + if args.len() != 3 { + return Err(ErrorKind::wrong_function_args(name, 3, args.len(), meta)); + } + let arg_1 = &mut args[1]; + ctx.implicit_conversion(self, &mut arg_1.0, arg_1.1, ScalarKind::Sint, 4)?; + let arg_2 = &mut args[2]; + ctx.implicit_conversion(self, &mut arg_2.0, arg_2.1, ScalarKind::Sint, 4)?; + if ctx.samplers.get(&args[0].0).is_some() { + let (arrayed, dims) = match *self.resolve_type(ctx, args[0].0, args[0].1)? { + TypeInner::Image { arrayed, dim, .. } => (arrayed, dim), + _ => (false, crate::ImageDimension::D1), + }; + + let (coordinate, array_index) = if arrayed { + ( + match dims { + crate::ImageDimension::D1 => ctx.add_expression( + Expression::AccessIndex { + base: args[1].0, + index: 0, + }, + body, + ), + crate::ImageDimension::D2 => ctx.add_expression( + Expression::Swizzle { + size: VectorSize::Bi, + vector: args[1].0, + pattern: SwizzleComponent::XYZW, + }, + body, + ), + _ => ctx.add_expression( + Expression::Swizzle { + size: VectorSize::Tri, + vector: args[1].0, + pattern: SwizzleComponent::XYZW, + }, + body, + ), + }, + Some(ctx.add_expression( + Expression::AccessIndex { + base: args[1].0, + index: match dims { + crate::ImageDimension::D1 => 1, + crate::ImageDimension::D2 => 2, + crate::ImageDimension::D3 => 3, + crate::ImageDimension::Cube => 2, + }, + }, + body, + )), + ) + } else { + (args[1].0, None) + }; + + Ok(Some(ctx.add_expression( + Expression::ImageLoad { + image: args[0].0, + coordinate, + array_index, + index: Some(args[2].0), + }, + body, + ))) + } else { + Err(ErrorKind::SemanticError( + meta, + "Bad call to texelFetch".into(), + )) + } + } + "ceil" | "round" | "floor" | "fract" | "trunc" | "sin" | "abs" | "sqrt" + | "inversesqrt" | "exp" | "exp2" | "sign" | "transpose" | "inverse" | "normalize" + | "sinh" | "cos" | "cosh" | "tan" | "tanh" | "acos" | "asin" | "log" | "log2" + | "length" | "determinant" | "bitCount" | "bitfieldReverse" => { + if args.len() != 1 { + return Err(ErrorKind::wrong_function_args(name, 1, args.len(), meta)); + } + Ok(Some(ctx.add_expression( + Expression::Math { + fun: match name.as_str() { + "ceil" => MathFunction::Ceil, + "round" => MathFunction::Round, + "floor" => MathFunction::Floor, + "fract" => MathFunction::Fract, + "trunc" => MathFunction::Trunc, + "sin" => MathFunction::Sin, + "abs" => MathFunction::Abs, + "sqrt" => MathFunction::Sqrt, + "inversesqrt" => MathFunction::InverseSqrt, + "exp" => MathFunction::Exp, + "exp2" => MathFunction::Exp2, + "sign" => MathFunction::Sign, + "transpose" => MathFunction::Transpose, + "inverse" => MathFunction::Inverse, + "normalize" => MathFunction::Normalize, + "sinh" => MathFunction::Sinh, + "cos" => MathFunction::Cos, + "cosh" => MathFunction::Cosh, + "tan" => MathFunction::Tan, + "tanh" => MathFunction::Tanh, + "acos" => MathFunction::Acos, + "asin" => MathFunction::Asin, + "log" => MathFunction::Log, + "log2" => MathFunction::Log2, + "length" => MathFunction::Length, + "determinant" => MathFunction::Determinant, + "bitCount" => MathFunction::CountOneBits, + "bitfieldReverse" => MathFunction::ReverseBits, + _ => unreachable!(), + }, + arg: args[0].0, + arg1: None, + arg2: None, + }, + body, + ))) + } + "atan" => { + let expr = match args.len() { + 1 => Expression::Math { + fun: MathFunction::Atan, + arg: args[0].0, + arg1: None, + arg2: None, + }, + 2 => Expression::Math { + fun: MathFunction::Atan2, + arg: args[0].0, + arg1: Some(args[1].0), + arg2: None, + }, + _ => return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)), + }; + Ok(Some(ctx.add_expression(expr, body))) + } + "mod" => { + if args.len() != 2 { + return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); + } + + let (mut left, left_meta) = args[0]; + let (mut right, right_meta) = args[1]; + + ctx.binary_implicit_conversion(self, &mut left, left_meta, &mut right, right_meta)?; + + Ok(Some(ctx.add_expression( + Expression::Binary { + op: BinaryOperator::Modulo, + left, + right, + }, + body, + ))) + } + "max" => { + if args.len() != 2 { + return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); + } + + let (mut arg0, arg0_meta) = args[0]; + let (mut arg1, arg1_meta) = args[1]; + + let arg0_size = match *self.resolve_type(ctx, arg0, arg0_meta)? { + TypeInner::Vector { size, .. } => Some(size), + _ => None, + }; + let arg1_size = match *self.resolve_type(ctx, arg1, arg1_meta)? { + TypeInner::Vector { size, .. } => Some(size), + _ => None, + }; + + ctx.binary_implicit_conversion(self, &mut arg0, arg0_meta, &mut arg1, arg1_meta)?; + + ctx.implicit_splat(self, &mut arg0, arg0_meta, arg1_size)?; + ctx.implicit_splat(self, &mut arg1, arg1_meta, arg0_size)?; + + Ok(Some(ctx.add_expression( + Expression::Math { + fun: MathFunction::Max, + arg: arg0, + arg1: Some(arg1), + arg2: None, + }, + body, + ))) + } + "pow" | "dot" | "min" | "reflect" | "cross" | "outerProduct" | "distance" | "step" + | "modf" | "frexp" | "ldexp" => { + if args.len() != 2 { + return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); + } + + let (mut arg0, arg0_meta) = args[0]; + let (mut arg1, arg1_meta) = args[1]; + + ctx.binary_implicit_conversion(self, &mut arg0, arg0_meta, &mut arg1, arg1_meta)?; + + Ok(Some(ctx.add_expression( + Expression::Math { + fun: match name.as_str() { + "pow" => MathFunction::Pow, + "dot" => MathFunction::Dot, + "min" => MathFunction::Min, + "reflect" => MathFunction::Reflect, + "cross" => MathFunction::Cross, + "outerProduct" => MathFunction::Outer, + "distance" => MathFunction::Distance, + "step" => MathFunction::Step, + "modf" => MathFunction::Modf, + "frexp" => MathFunction::Frexp, + "ldexp" => MathFunction::Ldexp, + _ => unreachable!(), + }, + arg: arg0, + arg1: Some(arg1), + arg2: None, + }, + body, + ))) + } + "mix" => { + if args.len() != 3 { + return Err(ErrorKind::wrong_function_args(name, 3, args.len(), meta)); + } + + let (mut arg, arg_meta) = args[0]; + let (mut arg1, arg1_meta) = args[1]; + let (mut selector, selector_meta) = args[2]; + + ctx.binary_implicit_conversion(self, &mut arg, arg_meta, &mut arg1, arg1_meta)?; + ctx.binary_implicit_conversion( + self, + &mut arg, + arg_meta, + &mut selector, + selector_meta, + )?; + + let is_vector = match *self.resolve_type(ctx, selector, selector_meta)? { + TypeInner::Vector { .. } => true, + _ => false, + }; + match *self.resolve_type(ctx, args[0].0, args[0].1)? { + TypeInner::Vector { size, .. } if !is_vector => { + selector = ctx.add_expression( + Expression::Splat { + size, + value: selector, + }, + body, + ) + } + _ => {} + }; + + let expr = match self + .resolve_type(ctx, selector, selector_meta)? + .scalar_kind() + { + Some(ScalarKind::Bool) => Expression::Select { + condition: selector, + accept: arg, + reject: arg1, + }, + _ => Expression::Math { + fun: MathFunction::Mix, + arg, + arg1: Some(arg1), + arg2: Some(selector), + }, + }; + + Ok(Some(ctx.add_expression(expr, body))) + } + "clamp" => { + if args.len() != 3 { + return Err(ErrorKind::wrong_function_args(name, 3, args.len(), meta)); + } + + let (mut arg0, arg0_meta) = args[0]; + let (mut arg1, arg1_meta) = args[1]; + let (mut arg2, arg2_meta) = args[2]; + + let vector_size = match *(self.resolve_type(ctx, arg0, arg0_meta)?) { + TypeInner::Vector { size, .. } => Some(size), + _ => None, + }; + + ctx.binary_implicit_conversion(self, &mut arg0, arg0_meta, &mut arg1, arg1_meta)?; + ctx.binary_implicit_conversion(self, &mut arg1, arg1_meta, &mut arg2, arg2_meta)?; + ctx.binary_implicit_conversion(self, &mut arg2, arg2_meta, &mut arg0, arg0_meta)?; + + ctx.implicit_splat(self, &mut arg1, arg1_meta, vector_size)?; + ctx.implicit_splat(self, &mut arg2, arg2_meta, vector_size)?; + + Ok(Some(ctx.add_expression( + Expression::Math { + fun: MathFunction::Clamp, + arg: arg0, + arg1: Some(arg1), + arg2: Some(arg2), + }, + body, + ))) + } + "faceforward" | "refract" | "fma" | "smoothstep" => { + if args.len() != 3 { + return Err(ErrorKind::wrong_function_args(name, 3, args.len(), meta)); + } + Ok(Some(ctx.add_expression( + Expression::Math { + fun: match name.as_str() { + "faceforward" => MathFunction::FaceForward, + "refract" => MathFunction::Refract, + "fma" => MathFunction::Fma, + "smoothstep" => MathFunction::SmoothStep, + _ => unreachable!(), + }, + arg: args[0].0, + arg1: Some(args[1].0), + arg2: Some(args[2].0), + }, + body, + ))) + } + "lessThan" | "greaterThan" | "lessThanEqual" | "greaterThanEqual" | "equal" + | "notEqual" => { + if args.len() != 2 { + return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); + } + Ok(Some(ctx.add_expression( + Expression::Binary { + op: match name.as_str() { + "lessThan" => BinaryOperator::Less, + "greaterThan" => BinaryOperator::Greater, + "lessThanEqual" => BinaryOperator::LessEqual, + "greaterThanEqual" => BinaryOperator::GreaterEqual, + "equal" => BinaryOperator::Equal, + "notEqual" => BinaryOperator::NotEqual, + _ => unreachable!(), + }, + left: args[0].0, + right: args[1].0, + }, + body, + ))) + } + "isinf" | "isnan" | "all" | "any" => { + let fun = match name.as_str() { + "isinf" => RelationalFunction::IsInf, + "isnan" => RelationalFunction::IsNan, + "all" => RelationalFunction::All, + "any" => RelationalFunction::Any, + _ => unreachable!(), + }; + + Ok(Some( + self.parse_relational_fun(ctx, body, name, &args, fun, meta)?, + )) + } + _ => { + let declarations = self.lookup_function.get(&name).ok_or_else(|| { + ErrorKind::SemanticError(meta, format!("Unknown function '{}'", name).into()) + })?; + + let mut maybe_decl = None; + let mut ambiguous = false; + + 'outer: for decl in declarations { + if args.len() != decl.parameters.len() { + continue; + } + + let mut exact = true; + + for (decl_arg, call_arg) in decl.parameters.iter().zip(args.iter()) { + let decl_inner = &self.module.types[*decl_arg].inner; + let call_inner = self.resolve_type(ctx, call_arg.0, call_arg.1)?; + + if decl_inner == call_inner { + continue; + } + + exact = false; + + let (decl_kind, call_kind) = match (decl_inner, call_inner) { + ( + &TypeInner::Scalar { + kind: decl_kind, .. + }, + &TypeInner::Scalar { + kind: call_kind, .. + }, + ) => (decl_kind, call_kind), + ( + &TypeInner::Vector { + kind: decl_kind, + size: decl_size, + .. + }, + &TypeInner::Vector { + kind: call_kind, + size: call_size, + .. + }, + ) if decl_size == call_size => (decl_kind, call_kind), + ( + &TypeInner::Matrix { + rows: decl_rows, + columns: decl_columns, + .. + }, + &TypeInner::Matrix { + rows: call_rows, + columns: call_columns, + .. + }, + ) if decl_columns == call_columns && decl_rows == call_rows => { + (ScalarKind::Float, ScalarKind::Float) + } + _ => continue 'outer, + }; + + match (type_power(decl_kind), type_power(call_kind)) { + (Some(decl_power), Some(call_power)) if decl_power > call_power => {} + _ => continue 'outer, + } + } + + if exact { + maybe_decl = Some(decl); + ambiguous = false; + break; + } else if maybe_decl.is_some() { + ambiguous = true; + } else { + maybe_decl = Some(decl) + } + } + + if ambiguous { + return Err(ErrorKind::SemanticError( + meta, + format!("Ambiguous best function for '{}'", name).into(), + )); + } + + let decl = maybe_decl.ok_or_else(|| { + ErrorKind::SemanticError(meta, format!("Unknown function '{}'", name).into()) + })?; + + let qualifiers = decl.qualifiers.clone(); + let parameters = decl.parameters.clone(); + let function = decl.handle; + let is_void = decl.void; + + let mut arguments = Vec::with_capacity(args.len()); + let mut proxy_writes = Vec::new(); + for (qualifier, (expr, parameter)) in qualifiers + .iter() + .zip(raw_args.iter().zip(parameters.iter())) + { + let (mut handle, meta) = + ctx.lower_expect(self, *expr, qualifier.is_lhs(), body)?; + + if let TypeInner::Vector { size, kind, width } = + *self.resolve_type(ctx, handle, meta)? + { + if qualifier.is_lhs() + && matches!(*ctx.get_expression(handle), Expression::Swizzle { .. }) + { + let ty = self.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Vector { size, kind, width }, + }); + let temp_var = ctx.locals.append(LocalVariable { + name: None, + ty, + init: None, + }); + let temp_expr = + ctx.add_expression(Expression::LocalVariable(temp_var), body); + + body.push(Statement::Store { + pointer: temp_expr, + value: handle, + }); + + arguments.push(temp_expr); + proxy_writes.push((*expr, temp_expr)); + continue; + } + } + + let scalar_components = scalar_components(&self.module.types[*parameter].inner); + if let Some((kind, width)) = scalar_components { + ctx.implicit_conversion(self, &mut handle, meta, kind, width)?; + } + + arguments.push(handle) + } + + ctx.emit_flush(body); + + let result = if !is_void { + Some(ctx.add_expression(Expression::Call(function), body)) + } else { + None + }; + + body.push(crate::Statement::Call { + function, + arguments, + result, + }); + + ctx.emit_start(); + for (tgt, pointer) in proxy_writes { + let temp_ref = ctx.hir_exprs.append(HirExpr { + kind: HirExprKind::Variable(VariableReference { + expr: pointer, + load: true, + mutable: true, + entry_arg: None, + }), + meta, + }); + let assign = ctx.hir_exprs.append(HirExpr { + kind: HirExprKind::Assign { + tgt, + value: temp_ref, + }, + meta, + }); + + let _ = ctx.lower_expect(self, assign, false, body)?; + } + ctx.emit_flush(body); + ctx.emit_start(); + + Ok(result) } } }