Add all standard library functions to the IR

This commit is contained in:
Dzmitry Malyshau
2020-12-09 00:12:53 -05:00
committed by Dzmitry Malyshau
parent 66d994566c
commit b35e901249
12 changed files with 744 additions and 462 deletions

View File

@@ -49,10 +49,9 @@ use crate::{
Typifier, Visitor,
},
Arena, ArraySize, BinaryOperator, BuiltIn, Bytes, ConservativeDepth, Constant, ConstantInner,
DerivativeAxis, Expression, FastHashMap, Function, FunctionOrigin, GlobalVariable, Handle,
ImageClass, Interpolation, IntrinsicFunction, LocalVariable, Module, ScalarKind, ShaderStage,
Statement, StorageAccess, StorageClass, StorageFormat, StructMember, Type, TypeInner,
UnaryOperator,
DerivativeAxis, Expression, FastHashMap, Function, GlobalVariable, Handle, ImageClass,
Interpolation, LocalVariable, Module, RelationalFunction, ScalarKind, ShaderStage, Statement,
StorageAccess, StorageClass, StorageFormat, StructMember, Type, TypeInner, UnaryOperator,
};
use features::FeaturesManager;
use std::{
@@ -1371,49 +1370,119 @@ impl<'a, W: Write> Writer<'a, W> {
self.write_expr(reject, ctx)?;
write!(self.out, ")")?
}
// `Intrinsic` is a normal function call to some glsl provided functions
Expression::Intrinsic { fun, argument } => {
// `Derivative` is a function call to a glsl provided function
Expression::Derivative { axis, expr } => {
write!(
self.out,
"{}(",
match fun {
// There's no specific function for this but we can invert the result of `isinf`
IntrinsicFunction::IsFinite => "!isinf",
IntrinsicFunction::IsInf => "isinf",
IntrinsicFunction::IsNan => "isnan",
// There's also no function for this but we can invert `isnan`
IntrinsicFunction::IsNormal => "!isnan",
IntrinsicFunction::All => "all",
IntrinsicFunction::Any => "any",
match axis {
DerivativeAxis::X => "dFdx",
DerivativeAxis::Y => "dFdy",
DerivativeAxis::Width => "fwidth",
}
)?;
self.write_expr(expr, ctx)?;
write!(self.out, ")")?
}
// `Relational` is a normal function call to some glsl provided functions
Expression::Relational { fun, argument } => {
let fun_name = match fun {
// There's no specific function for this but we can invert the result of `isinf`
RelationalFunction::IsFinite => "!isinf",
RelationalFunction::IsInf => "isinf",
RelationalFunction::IsNan => "isnan",
// There's also no function for this but we can invert `isnan`
RelationalFunction::IsNormal => "!isnan",
RelationalFunction::All => "all",
RelationalFunction::Any => "any",
};
write!(self.out, "{}(", fun_name)?;
self.write_expr(argument, ctx)?;
write!(self.out, ")")?
}
Expression::Math {
fun,
arg,
arg1,
arg2,
} => {
use crate::MathFunction as Mf;
let fun_name = match fun {
// comparison
Mf::Abs => "abs",
Mf::Min => "min",
Mf::Max => "max",
Mf::Clamp => "clamp",
// trigonometry
Mf::Cos => "cos",
Mf::Cosh => "cosh",
Mf::Sin => "sin",
Mf::Sinh => "sinh",
Mf::Tan => "tan",
Mf::Tanh => "tanh",
Mf::Acos => "acos",
Mf::Asin => "asin",
Mf::Atan => "atan",
Mf::Atan2 => "atan2",
// decomposition
Mf::Ceil => "ceil",
Mf::Floor => "floor",
Mf::Round => "round",
Mf::Fract => "fract",
Mf::Trunc => "trunc",
Mf::Modf => "modf",
Mf::Frexp => "frexp",
Mf::Ldexp => "ldexp",
// exponent
Mf::Exp => "exp",
Mf::Exp2 => "exp2",
Mf::Log => "log",
Mf::Log2 => "log2",
Mf::Pow => "pow",
// geometry
Mf::Dot => "dot",
Mf::Outer => "outerProduct",
Mf::Cross => "cross",
Mf::Distance => "distance",
Mf::Length => "length",
Mf::Normalize => "normalize",
Mf::FaceForward => "faceforward",
Mf::Reflect => "reflect",
// computational
Mf::Sign => "sign",
Mf::Fma => "fma",
Mf::Mix => "mix",
Mf::Step => "step",
Mf::SmoothStep => "smoothstep",
Mf::Sqrt => "sqrt",
Mf::InverseSqrt => "inversesqrt",
Mf::Determinant => "determinant",
// bits
Mf::CountOneBits => "bitCount",
Mf::ReverseBits => "bitfieldReverse",
};
write!(self.out, "{}(", fun_name)?;
self.write_expr(arg, ctx)?;
if let Some(arg) = arg1 {
write!(self.out, ", ")?;
self.write_expr(arg, ctx)?;
}
if let Some(arg) = arg2 {
write!(self.out, ", ")?;
self.write_expr(arg, ctx)?;
}
write!(self.out, ")")?
}
// `Transpose` is a call to the glsl function `transpose`
Expression::Transpose(matrix) => {
write!(self.out, "transpose(")?;
self.write_expr(matrix, ctx)?;
write!(self.out, ")")?
}
// Both `Dot` and `Cross` products are a call to a glsl provide functions with `left`
// and `right` as arguments
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, ")")?
}
// `As` is always a call.
// If `convert` is true the function name is the type
// Else the function name is one of the glsl provided bitcast functions
@@ -1457,55 +1526,15 @@ impl<'a, W: Write> Writer<'a, W> {
self.write_expr(expr, ctx)?;
write!(self.out, ")")?
}
// `Derivative` is a function call to a glsl provided function
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, ")")?
}
// A `Call` is written `name(arguments)` where `arguments` is a comma separated expressions list
Expression::Call {
origin: FunctionOrigin::Local(ref function),
function,
ref arguments,
} => {
write!(self.out, "{}(", &self.names[&NameKey::Function(*function)])?;
write!(self.out, "{}(", &self.names[&NameKey::Function(function)])?;
self.write_slice(arguments, |this, _, arg| this.write_expr(*arg, ctx))?;
write!(self.out, ")")?
}
Expression::Call {
origin: crate::FunctionOrigin::External(ref name),
ref arguments,
} => match name.as_str() {
"cos" | "normalize" | "sin" | "length" | "abs" | "floor" | "inverse"
| "distance" | "dot" | "min" | "max" | "reflect" | "pow" | "step" | "cross"
| "fclamp" | "clamp" | "mix" | "smoothstep" => {
let name = match name.as_str() {
"fclamp" => "clamp",
name => name,
};
write!(self.out, "{}(", name)?;
self.write_slice(arguments, |this, _, arg| this.write_expr(*arg, ctx))?;
write!(self.out, ")")?
}
// `atan2` is implemented as `atan(y,x)` so we must handle it separately
"atan2" => {
write!(self.out, "atan(")?;
self.write_expr(arguments[1], ctx)?;
write!(self.out, ", ")?;
self.write_expr(arguments[0], ctx)?;
write!(self.out, ")")?
}
_ => return Err(Error::UnsupportedExternal(name.clone())),
},
// `ArrayLength` is written as `expr.length()` and we convert it to a uint
Expression::ArrayLength(expr) => {
write!(self.out, "uint(")?;

View File

@@ -346,26 +346,103 @@ impl<W: Write> Writer<W> {
self.put_expression(reject, context)?;
write!(self.out, ")")?;
}
crate::Expression::Intrinsic { fun, argument } => {
crate::Expression::Derivative { axis, expr } => {
let op = match axis {
crate::DerivativeAxis::X => "dfdx",
crate::DerivativeAxis::Y => "dfdy",
crate::DerivativeAxis::Width => "fwidth",
};
self.put_call(op, &[expr], context)?;
}
crate::Expression::Relational { fun, argument } => {
let op = match fun {
crate::IntrinsicFunction::Any => "any",
crate::IntrinsicFunction::All => "all",
crate::IntrinsicFunction::IsNan => "",
crate::IntrinsicFunction::IsInf => "",
crate::IntrinsicFunction::IsFinite => "",
crate::IntrinsicFunction::IsNormal => "",
crate::RelationalFunction::Any => "any",
crate::RelationalFunction::All => "all",
crate::RelationalFunction::IsNan => "isnan",
crate::RelationalFunction::IsInf => "isinf",
crate::RelationalFunction::IsFinite => "isfinite",
crate::RelationalFunction::IsNormal => "isnormal",
};
self.put_call(op, &[argument], context)?;
}
crate::Expression::Math {
fun,
arg,
arg1,
arg2,
} => {
use crate::MathFunction as Mf;
let fun_name = match fun {
// comparison
Mf::Abs => "abs",
Mf::Min => "min",
Mf::Max => "max",
Mf::Clamp => "clamp",
// trigonometry
Mf::Cos => "cos",
Mf::Cosh => "cosh",
Mf::Sin => "sin",
Mf::Sinh => "sinh",
Mf::Tan => "tan",
Mf::Tanh => "tanh",
Mf::Acos => "acos",
Mf::Asin => "asin",
Mf::Atan => "atan",
Mf::Atan2 => "atan2",
// decomposition
Mf::Ceil => "ceil",
Mf::Floor => "floor",
Mf::Round => "round",
Mf::Fract => "fract",
Mf::Trunc => "trunc",
Mf::Modf => "modf",
Mf::Frexp => "frexp",
Mf::Ldexp => "ldexp",
// exponent
Mf::Exp => "exp",
Mf::Exp2 => "exp2",
Mf::Log => "log",
Mf::Log2 => "log2",
Mf::Pow => "pow",
// geometry
Mf::Dot => "dot",
Mf::Outer => return Err(Error::UnsupportedCall(format!("{:?}", fun))),
Mf::Cross => "cross",
Mf::Distance => "distance",
Mf::Length => "length",
Mf::Normalize => "normalize",
Mf::FaceForward => "faceforward",
Mf::Reflect => "reflect",
// computational
Mf::Sign => "sign",
Mf::Fma => "fma",
Mf::Mix => "mix",
Mf::Step => "step",
Mf::SmoothStep => "smoothstep",
Mf::Sqrt => "sqrt",
Mf::InverseSqrt => "rsqrt",
Mf::Determinant => "determinant",
// bits
Mf::CountOneBits => "popcount",
Mf::ReverseBits => "reverse_bits",
};
write!(self.out, "metal::{}(", fun_name)?;
self.put_expression(arg, context)?;
if let Some(arg) = arg1 {
write!(self.out, ", ")?;
self.put_expression(arg, context)?;
}
if let Some(arg) = arg2 {
write!(self.out, ", ")?;
self.put_expression(arg, context)?;
}
write!(self.out, ")")?;
}
crate::Expression::Transpose(expr) => {
self.put_call("transpose", &[expr], context)?;
}
crate::Expression::DotProduct(a, b) => {
self.put_call("dot", &[a, b], context)?;
}
crate::Expression::CrossProduct(a, b) => {
self.put_call("cross", &[a, b], context)?;
}
crate::Expression::As {
expr,
kind,
@@ -382,34 +459,14 @@ impl<W: Write> Writer<W> {
self.put_expression(expr, context)?;
write!(self.out, ")")?;
}
crate::Expression::Derivative { axis, expr } => {
let op = match axis {
crate::DerivativeAxis::X => "dfdx",
crate::DerivativeAxis::Y => "dfdy",
crate::DerivativeAxis::Width => "fwidth",
};
self.put_call(op, &[expr], context)?;
}
crate::Expression::Call {
origin: crate::FunctionOrigin::Local(handle),
function,
ref arguments,
} => {
let name = &self.names[&NameKey::Function(handle)];
let name = &self.names[&NameKey::Function(function)];
write!(self.out, "{}", name)?;
self.put_call("", arguments, context)?;
}
crate::Expression::Call {
origin: crate::FunctionOrigin::External(ref name),
ref arguments,
} => match name.as_str() {
"atan2" | "cos" | "distance" | "length" | "mix" | "normalize" | "sin" => {
self.put_call(name, arguments, context)?;
}
"fclamp" => {
self.put_call("clamp", arguments, context)?;
}
other => return Err(Error::UnsupportedCall(other.to_owned())),
},
crate::Expression::ArrayLength(expr) => match *self
.typifier
.get(expr, &context.module.types)

View File

@@ -1232,6 +1232,10 @@ impl Writer {
));
Ok((id, result_lookup_ty))
}
crate::Expression::Math { fun, .. } => {
log::error!("unimplemented math function {:?}", fun);
Err(Error::FeatureNotImplemented("math function"))
}
crate::Expression::LocalVariable(variable) => {
let var = &ir_function.local_variables[variable];
let local_var = &function.variables[&variable];
@@ -1251,49 +1255,38 @@ impl Writer {
Ok((load_id, LookupType::Handle(handle)))
}
crate::Expression::Call {
ref origin,
function: local_function,
ref arguments,
} => match *origin {
crate::FunctionOrigin::Local(local_function) => {
let origin_function = &ir_module.functions[local_function];
let id = self.generate_id();
let mut argument_ids = vec![];
} => {
let target_function = &ir_module.functions[local_function];
let id = self.generate_id();
let mut argument_ids = vec![];
for argument in arguments {
let expression = &ir_function.expressions[*argument];
let (arg_id, _) = self.write_expression(
ir_module,
ir_function,
expression,
block,
function,
)?;
argument_ids.push(arg_id);
}
let return_type_id = self
.get_function_return_type(origin_function.return_type, &ir_module.types)?;
block
.body
.push(super::instructions::instruction_function_call(
return_type_id,
id,
*self.lookup_function.get(&local_function).unwrap(),
argument_ids.as_slice(),
));
let result_type = match origin_function.return_type {
Some(ty_handle) => LookupType::Handle(ty_handle),
None => LookupType::Local(LocalType::Void),
};
Ok((id, result_type))
for argument in arguments {
let expression = &ir_function.expressions[*argument];
let (arg_id, _) =
self.write_expression(ir_module, ir_function, expression, block, function)?;
argument_ids.push(arg_id);
}
crate::FunctionOrigin::External(ref string) => {
log::error!("unimplemented stdlib function {}", string);
Err(Error::FeatureNotImplemented("stdlib function"))
}
},
let return_type_id =
self.get_function_return_type(target_function.return_type, &ir_module.types)?;
block
.body
.push(super::instructions::instruction_function_call(
return_type_id,
id,
*self.lookup_function.get(&local_function).unwrap(),
argument_ids.as_slice(),
));
let result_type = match target_function.return_type {
Some(ty_handle) => LookupType::Handle(ty_handle),
None => LookupType::Local(LocalType::Void),
};
Ok((id, result_type))
}
crate::Expression::As {
expr,
kind,

View File

@@ -115,8 +115,8 @@ impl<I: Iterator<Item = u32>> super::Parser<I> {
}
// Read body
let mut local_function_calls = FastHashMap::default();
let mut flow_graph = FlowGraph::new();
let base_deferred_call_index = self.deferred_function_calls.len();
// Scan the blocks and add them as nodes
loop {
@@ -135,7 +135,6 @@ impl<I: Iterator<Item = u32>> super::Parser<I> {
&module.types,
&module.constants,
&module.global_variables,
&mut local_function_calls,
)?;
flow_graph.add_node(node);
@@ -176,9 +175,14 @@ impl<I: Iterator<Item = u32>> super::Parser<I> {
}
};
for dfc in self.deferred_function_calls[base_deferred_call_index..].iter_mut() {
dfc.source = source.clone();
}
if let Some(ref prefix) = self.options.flow_graph_dump_prefix {
let dump = flow_graph.to_graphviz().unwrap_or_default();
let suffix = match source {
DeferredSource::Undefined => unreachable!(),
DeferredSource::EntryPoint(stage, ref name) => {
format!("flow.{:?}-{}.dot", stage, name)
}
@@ -187,14 +191,6 @@ impl<I: Iterator<Item = u32>> super::Parser<I> {
let _ = std::fs::write(prefix.join(suffix), dump);
}
for (expr_handle, dst_id) in local_function_calls {
self.deferred_function_calls.push(DeferredFunctionCall {
source: source.clone(),
expr_handle,
dst_id,
});
}
self.lookup_expression.clear();
self.lookup_sampled_image.clear();
Ok(())

View File

@@ -289,6 +289,7 @@ struct LookupSampledImage {
}
#[derive(Clone, Debug)]
enum DeferredSource {
Undefined,
EntryPoint(crate::ShaderStage, String),
Function(Handle<crate::Function>),
}
@@ -296,6 +297,7 @@ struct DeferredFunctionCall {
source: DeferredSource,
expr_handle: Handle<crate::Expression>,
dst_id: spirv::Word,
arguments: Vec<Handle<crate::Expression>>,
}
#[derive(Clone, Debug)]
@@ -530,7 +532,6 @@ impl<I: Iterator<Item = u32>> Parser<I> {
type_arena: &Arena<crate::Type>,
const_arena: &Arena<crate::Constant>,
global_arena: &Arena<crate::GlobalVariable>,
local_function_calls: &mut FastHashMap<Handle<crate::Expression>, spirv::Word>,
) -> Result<ControlFlowNode, Error> {
let mut assignments = Vec::new();
let mut phis = Vec::new();
@@ -850,7 +851,12 @@ impl<I: Iterator<Item = u32>> Parser<I> {
let right_id = self.next()?;
let left_lexp = self.lookup_expression.lookup(left_id)?;
let right_lexp = self.lookup_expression.lookup(right_id)?;
let expr = crate::Expression::DotProduct(left_lexp.handle, right_lexp.handle);
let expr = crate::Expression::Math {
fun: crate::MathFunction::Dot,
arg: left_lexp.handle,
arg1: Some(right_lexp.handle),
arg2: None,
};
self.lookup_expression.insert(
result_id,
LookupExpression {
@@ -1185,13 +1191,15 @@ impl<I: Iterator<Item = u32>> Parser<I> {
let arg_id = self.next()?;
arguments.push(self.lookup_expression.lookup(arg_id)?.handle);
}
let expr = crate::Expression::Call {
// will be replaced by `Local()` after all the functions are parsed
origin: crate::FunctionOrigin::External(String::new()),
arguments,
};
// will be replaced by the actual expression
let expr = crate::Expression::FunctionArgument(!0);
let expr_handle = expressions.append(expr);
local_function_calls.insert(expr_handle, func_id);
self.deferred_function_calls.push(DeferredFunctionCall {
source: DeferredSource::Undefined,
expr_handle,
dst_id: func_id,
arguments,
});
self.lookup_expression.insert(
result_id,
LookupExpression {
@@ -1201,6 +1209,9 @@ impl<I: Iterator<Item = u32>> Parser<I> {
);
}
Op::ExtInst => {
use crate::MathFunction as Mf;
use spirv::GLOp as Glo;
let base_wc = 5;
inst.expect_at_least(base_wc)?;
let result_type_id = self.next()?;
@@ -1210,106 +1221,76 @@ impl<I: Iterator<Item = u32>> Parser<I> {
return Err(Error::UnsupportedExtInstSet(set_id));
}
let inst_id = self.next()?;
let name = match spirv::GLOp::from_u32(inst_id) {
Some(spirv::GLOp::FAbs) | Some(spirv::GLOp::SAbs) => {
inst.expect(base_wc + 1)?;
"abs"
}
Some(spirv::GLOp::FSign) | Some(spirv::GLOp::SSign) => {
inst.expect(base_wc + 1)?;
"sign"
}
Some(spirv::GLOp::Floor) => {
inst.expect(base_wc + 1)?;
"floor"
}
Some(spirv::GLOp::Ceil) => {
inst.expect(base_wc + 1)?;
"ceil"
}
Some(spirv::GLOp::Fract) => {
inst.expect(base_wc + 1)?;
"fract"
}
Some(spirv::GLOp::Sin) => {
inst.expect(base_wc + 1)?;
"sin"
}
Some(spirv::GLOp::Cos) => {
inst.expect(base_wc + 1)?;
"cos"
}
Some(spirv::GLOp::Tan) => {
inst.expect(base_wc + 1)?;
"tan"
}
Some(spirv::GLOp::Atan2) => {
inst.expect(base_wc + 2)?;
"atan2"
}
Some(spirv::GLOp::Pow) => {
inst.expect(base_wc + 2)?;
"pow"
}
Some(spirv::GLOp::MatrixInverse) => {
inst.expect(base_wc + 1)?;
"inverse"
}
Some(spirv::GLOp::FMix) => {
inst.expect(base_wc + 3)?;
"mix"
}
Some(spirv::GLOp::Step) => {
inst.expect(base_wc + 2)?;
"step"
}
Some(spirv::GLOp::SmoothStep) => {
inst.expect(base_wc + 3)?;
"smoothstep"
}
Some(spirv::GLOp::FMin) => {
inst.expect(base_wc + 2)?;
"min"
}
Some(spirv::GLOp::FMax) => {
inst.expect(base_wc + 2)?;
"max"
}
Some(spirv::GLOp::FClamp) => {
inst.expect(base_wc + 3)?;
"clamp"
}
Some(spirv::GLOp::Length) => {
inst.expect(base_wc + 1)?;
"length"
}
Some(spirv::GLOp::Distance) => {
inst.expect(base_wc + 2)?;
"distance"
}
Some(spirv::GLOp::Cross) => {
inst.expect(base_wc + 2)?;
"cross"
}
Some(spirv::GLOp::Normalize) => {
inst.expect(base_wc + 1)?;
"normalize"
}
Some(spirv::GLOp::Reflect) => {
inst.expect(base_wc + 2)?;
"reflect"
}
let gl_op = Glo::from_u32(inst_id).ok_or(Error::UnsupportedExtInst(inst_id))?;
let fun = match gl_op {
Glo::Round => Mf::Round,
Glo::Trunc => Mf::Trunc,
Glo::FAbs | Glo::SAbs => Mf::Abs,
Glo::FSign | Glo::SSign => Mf::Sign,
Glo::Floor => Mf::Floor,
Glo::Ceil => Mf::Ceil,
Glo::Fract => Mf::Fract,
Glo::Sin => Mf::Sin,
Glo::Cos => Mf::Cos,
Glo::Tan => Mf::Tan,
Glo::Asin => Mf::Asin,
Glo::Acos => Mf::Acos,
Glo::Atan => Mf::Atan,
Glo::Sinh => Mf::Sinh,
Glo::Cosh => Mf::Cosh,
Glo::Tanh => Mf::Tanh,
Glo::Atan2 => Mf::Atan2,
Glo::Pow => Mf::Pow,
Glo::Exp => Mf::Exp,
Glo::Log => Mf::Log,
Glo::Exp2 => Mf::Exp2,
Glo::Log2 => Mf::Log2,
Glo::Sqrt => Mf::Sqrt,
Glo::InverseSqrt => Mf::InverseSqrt,
Glo::Determinant => Mf::Determinant,
Glo::Modf => Mf::Modf,
Glo::FMin | Glo::UMin | Glo::SMin | Glo::NMin => Mf::Min,
Glo::FMax | Glo::UMax | Glo::SMax | Glo::NMax => Mf::Max,
Glo::FClamp | Glo::UClamp | Glo::SClamp | Glo::NClamp => Mf::Clamp,
Glo::FMix => Mf::Mix,
Glo::Step => Mf::Step,
Glo::SmoothStep => Mf::SmoothStep,
Glo::Fma => Mf::Fma,
Glo::Frexp => Mf::Frexp, //TODO: FrexpStruct?
Glo::Ldexp => Mf::Ldexp,
Glo::Length => Mf::Length,
Glo::Distance => Mf::Distance,
Glo::Cross => Mf::Cross,
Glo::Normalize => Mf::Normalize,
Glo::FaceForward => Mf::FaceForward,
Glo::Reflect => Mf::Reflect,
_ => return Err(Error::UnsupportedExtInst(inst_id)),
};
let mut arguments = Vec::with_capacity((inst.wc - base_wc) as usize);
for _ in 0..arguments.capacity() {
let arg_count = fun.argument_count();
inst.expect(base_wc + arg_count as u16)?;
let arg = {
let arg_id = self.next()?;
arguments.push(self.lookup_expression.lookup(arg_id)?.handle);
}
let expr = crate::Expression::Call {
origin: crate::FunctionOrigin::External(name.to_string()),
arguments,
self.lookup_expression.lookup(arg_id)?.handle
};
let arg1 = if arg_count > 1 {
let arg_id = self.next()?;
Some(self.lookup_expression.lookup(arg_id)?.handle)
} else {
None
};
let arg2 = if arg_count > 2 {
let arg_id = self.next()?;
Some(self.lookup_expression.lookup(arg_id)?.handle)
} else {
None
};
let expr = crate::Expression::Math {
fun,
arg,
arg1,
arg2,
};
self.lookup_expression.insert(
result_id,
@@ -1553,6 +1534,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
for dfc in self.deferred_function_calls.drain(..) {
let dst_handle = *self.lookup_function.lookup(dfc.dst_id)?;
let fun = match dfc.source {
DeferredSource::Undefined => unreachable!(),
DeferredSource::Function(fun_handle) => module.functions.get_mut(fun_handle),
DeferredSource::EntryPoint(stage, name) => {
&mut module
@@ -1562,13 +1544,10 @@ impl<I: Iterator<Item = u32>> Parser<I> {
.function
}
};
match *fun.expressions.get_mut(dfc.expr_handle) {
crate::Expression::Call {
ref mut origin,
arguments: _,
} => *origin = crate::FunctionOrigin::Local(dst_handle),
_ => unreachable!(),
}
*fun.expressions.get_mut(dfc.expr_handle) = crate::Expression::Call {
function: dst_handle,
arguments: dfc.arguments,
};
}
if !self.future_decor.is_empty() {

View File

@@ -97,18 +97,7 @@ pub fn get_scalar_type(word: &str) -> Option<(crate::ScalarKind, crate::Bytes)>
}
}
pub fn get_intrinsic(word: &str) -> Option<crate::IntrinsicFunction> {
match word {
"any" => Some(crate::IntrinsicFunction::Any),
"all" => Some(crate::IntrinsicFunction::All),
"is_nan" => Some(crate::IntrinsicFunction::IsNan),
"is_inf" => Some(crate::IntrinsicFunction::IsInf),
"is_normal" => Some(crate::IntrinsicFunction::IsNormal),
_ => None,
}
}
pub fn get_derivative(word: &str) -> Option<crate::DerivativeAxis> {
pub fn map_derivative_axis(word: &str) -> Option<crate::DerivativeAxis> {
match word {
"dpdx" => Some(crate::DerivativeAxis::X),
"dpdy" => Some(crate::DerivativeAxis::Y),
@@ -117,16 +106,73 @@ pub fn get_derivative(word: &str) -> Option<crate::DerivativeAxis> {
}
}
// Returns argument count on success
pub fn get_standard_fun(word: &str) -> Option<usize> {
pub fn map_relational_fun(word: &str) -> Option<crate::RelationalFunction> {
match word {
"abs" | "acos" | "asin" | "atan" | "ceil" | "cos" | "cosh" | "exp" | "exp2" | "floor"
| "fract" | "inverseSqrt" | "length" | "log" | "log2" | "normalize" | "round" | "sign"
| "sin" | "sinh" | "sqrt" | "tan" | "tanh" | "trunc" => Some(1),
"countOneBits" | "reverseBits" | "determinant" => Some(1),
"atan2" | "distance" | "frexp" | "ldexp" | "max" | "min" | "outerProduct" | "pow"
| "reflect" | "step" => Some(2),
"clamp" | "faceForward" | "fma" | "smoothStep" => Some(3),
"any" => Some(crate::RelationalFunction::Any),
"all" => Some(crate::RelationalFunction::All),
"isFinite" => Some(crate::RelationalFunction::IsFinite),
"isInf" => Some(crate::RelationalFunction::IsInf),
"isNan" => Some(crate::RelationalFunction::IsNan),
"isNormal" => Some(crate::RelationalFunction::IsNormal),
_ => None,
}
}
pub fn map_standard_fun(word: &str) -> Option<crate::MathFunction> {
use crate::MathFunction as Mf;
Some(match word {
// comparison
"abs" => Mf::Abs,
"min" => Mf::Min,
"max" => Mf::Max,
"clamp" => Mf::Clamp,
// trigonometry
"cos" => Mf::Cos,
"cosh" => Mf::Cosh,
"sin" => Mf::Sin,
"sinh" => Mf::Sinh,
"tan" => Mf::Tan,
"tanh" => Mf::Tanh,
"acos" => Mf::Acos,
"asin" => Mf::Asin,
"atan" => Mf::Atan,
"atan2" => Mf::Atan2,
// decomposition
"ceil" => Mf::Ceil,
"floor" => Mf::Floor,
"round" => Mf::Round,
"fract" => Mf::Fract,
"trunc" => Mf::Trunc,
"modf" => Mf::Modf,
"frexp" => Mf::Frexp,
"ldexp" => Mf::Ldexp,
// exponent
"exp" => Mf::Exp,
"exp2" => Mf::Exp2,
"log" => Mf::Log,
"log2" => Mf::Log2,
"pow" => Mf::Pow,
// geometry
"dot" => Mf::Dot,
"outerProduct" => Mf::Outer,
"cross" => Mf::Cross,
"distance" => Mf::Distance,
"length" => Mf::Length,
"normalize" => Mf::Normalize,
"faceForward" => Mf::FaceForward,
"reflect" => Mf::Reflect,
// computational
"sign" => Mf::Sign,
"fma" => Mf::Fma,
"mix" => Mf::Mix,
"step" => Mf::Step,
"smoothStep" => Mf::SmoothStep,
"sqrt" => Mf::Sqrt,
"inverseSqrt" => Mf::InverseSqrt,
"determinant" => Mf::Determinant,
// bits
"countOneBits" => Mf::CountOneBits,
"reverseBits" => Mf::ReverseBits,
_ => return None,
})
}

View File

@@ -336,40 +336,54 @@ impl Parser {
) -> Result<Option<(crate::Expression, Lexer<'a>)>, Error<'a>> {
let mut lexer = lexer.clone();
let external_function = if let Some(std_namespaces) = self.std_namespace.as_deref() {
std_namespaces.iter().all(|namespace| {
lexer.skip(Token::Word(namespace)) && lexer.skip(Token::DoubleColon)
})
} else {
false
};
let origin = if external_function {
let function = lexer.next_ident()?;
crate::FunctionOrigin::External(function.to_string())
} else if let Ok(function) = lexer.next_ident() {
if let Some(&function) = self.function_lookup.get(function) {
crate::FunctionOrigin::Local(function)
let name = lexer.next_ident()?;
//TODO: avoid code duplication with `parse_singular_expression`
Ok(if let Some(fun) = conv::map_standard_fun(name) {
lexer.expect(Token::Paren('('))?;
let arg_count = fun.argument_count();
let arg = self.parse_general_expression(&mut lexer, ctx.reborrow())?;
let arg1 = if arg_count > 1 {
lexer.expect(Token::Separator(','))?;
Some(self.parse_general_expression(&mut lexer, ctx.reborrow())?)
} else {
None
};
let arg2 = if arg_count > 1 {
lexer.expect(Token::Separator(','))?;
Some(self.parse_general_expression(&mut lexer, ctx.reborrow())?)
} else {
None
};
lexer.expect(Token::Paren(')'))?;
let expr = crate::Expression::Math {
fun,
arg,
arg1,
arg2,
};
Some((expr, lexer))
} else if let Some(&function) = self.function_lookup.get(name) {
if !lexer.skip(Token::Paren('(')) {
return Ok(None);
}
} else {
return Ok(None);
};
if !lexer.skip(Token::Paren('(')) {
return Ok(None);
}
let mut arguments = Vec::new();
while !lexer.skip(Token::Paren(')')) {
if !arguments.is_empty() {
lexer.expect(Token::Separator(','))?;
let mut arguments = Vec::new();
while !lexer.skip(Token::Paren(')')) {
if !arguments.is_empty() {
lexer.expect(Token::Separator(','))?;
}
let arg = self.parse_general_expression(&mut lexer, ctx.reborrow())?;
arguments.push(arg);
}
let arg = self.parse_general_expression(&mut lexer, ctx.reborrow())?;
arguments.push(arg);
}
Ok(Some((crate::Expression::Call { origin, arguments }, lexer)))
let expr = crate::Expression::Call {
function,
arguments,
};
Some((expr, lexer))
} else {
None
})
}
fn parse_const_expression<'a>(
@@ -605,12 +619,12 @@ impl Parser {
expr: self.parse_singular_expression(lexer, ctx.reborrow())?,
}),
Token::Word(word) => {
if let Some(fun) = conv::get_intrinsic(word) {
if let Some(fun) = conv::map_relational_fun(word) {
lexer.expect(Token::Paren('('))?;
let argument = self.parse_general_expression(lexer, ctx.reborrow())?;
lexer.expect(Token::Paren(')'))?;
Some(crate::Expression::Intrinsic { fun, argument })
} else if let Some(axis) = conv::get_derivative(word) {
Some(crate::Expression::Relational { fun, argument })
} else if let Some(axis) = conv::map_derivative_axis(word) {
lexer.expect(Token::Paren('('))?;
let expr = self.parse_general_expression(lexer, ctx.reborrow())?;
lexer.expect(Token::Paren(')'))?;
@@ -624,40 +638,32 @@ impl Parser {
kind,
convert: true,
})
} else if let Some(arg_count) = conv::get_standard_fun(word) {
} else if let Some(fun) = conv::map_standard_fun(word) {
lexer.expect(Token::Paren('('))?;
let mut arguments = Vec::with_capacity(arg_count);
for i in 0..arg_count {
let arg = self.parse_general_expression(lexer, ctx.reborrow())?;
arguments.push(arg);
lexer.expect(if i + 1 == arg_count {
Token::Paren(')')
} else {
Token::Separator(',')
})?;
}
Some(crate::Expression::Call {
origin: crate::FunctionOrigin::External(word.to_string()),
arguments,
let arg_count = fun.argument_count();
let arg = self.parse_general_expression(lexer, ctx.reborrow())?;
let arg1 = if arg_count > 1 {
lexer.expect(Token::Separator(','))?;
Some(self.parse_general_expression(lexer, ctx.reborrow())?)
} else {
None
};
let arg2 = if arg_count > 2 {
lexer.expect(Token::Separator(','))?;
Some(self.parse_general_expression(lexer, ctx.reborrow())?)
} else {
None
};
lexer.expect(Token::Paren(')'))?;
Some(crate::Expression::Math {
fun,
arg,
arg1,
arg2,
})
} else {
// texture sampling
match word {
"dot" => {
lexer.expect(Token::Paren('('))?;
let a = self.parse_general_expression(lexer, ctx.reborrow())?;
lexer.expect(Token::Separator(','))?;
let b = self.parse_general_expression(lexer, ctx.reborrow())?;
lexer.expect(Token::Paren(')'))?;
Some(crate::Expression::DotProduct(a, b))
}
"cross" => {
lexer.expect(Token::Paren('('))?;
let a = self.parse_general_expression(lexer, ctx.reborrow())?;
lexer.expect(Token::Separator(','))?;
let b = self.parse_general_expression(lexer, ctx.reborrow())?;
lexer.expect(Token::Paren(')'))?;
Some(crate::Expression::CrossProduct(a, b))
}
"textureSample" => {
lexer.expect(Token::Paren('('))?;
let image_name = lexer.next_ident()?;

View File

@@ -516,19 +516,6 @@ pub enum BinaryOperator {
ShiftRight,
}
/// Built-in shader function.
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
pub enum IntrinsicFunction {
Any,
All,
IsNan,
IsInf,
IsFinite,
IsNormal,
}
/// Axis on which to compute a derivative.
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
@@ -539,17 +526,76 @@ pub enum DerivativeAxis {
Width,
}
/// Origin of a function to call.
#[derive(Clone, Debug, PartialEq)]
/// Built-in shader function for testing relation between values.
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
pub enum FunctionOrigin {
Local(Handle<Function>),
// External {
// namespace: String, // Maybe this should be a handle to a namespace Arena?
// function: String,
// },
External(String),
pub enum RelationalFunction {
All,
Any,
IsNan,
IsInf,
IsFinite,
IsNormal,
}
/// Built-in shader function for math.
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
pub enum MathFunction {
// comparison
Abs,
Min,
Max,
Clamp,
// trigonometry
Cos,
Cosh,
Sin,
Sinh,
Tan,
Tanh,
Acos,
Asin,
Atan,
Atan2,
// decomposition
Ceil,
Floor,
Round,
Fract,
Trunc,
Modf,
Frexp,
Ldexp,
// exponent
Exp,
Exp2,
Log,
Log2,
Pow,
// geometry
Dot,
Outer,
Cross,
Distance,
Length,
Normalize,
FaceForward,
Reflect,
// computational
Sign,
Fma,
Mix,
Step,
SmoothStep,
Sqrt,
InverseSqrt,
Determinant,
// bits
CountOneBits,
ReverseBits,
}
/// Sampling modifier to control the level of detail.
@@ -628,17 +674,26 @@ pub enum Expression {
accept: Handle<Expression>,
reject: Handle<Expression>,
},
/// Call an intrinsic function.
Intrinsic {
fun: IntrinsicFunction,
/// Compute the derivative on an axis.
Derivative {
axis: DerivativeAxis,
//modifier,
expr: Handle<Expression>,
},
/// Call a relational function.
Relational {
fun: RelationalFunction,
argument: Handle<Expression>,
},
/// Call a math function
Math {
fun: MathFunction,
arg: Handle<Expression>,
arg1: Option<Handle<Expression>>,
arg2: Option<Handle<Expression>>,
},
/// Transpose of a matrix.
Transpose(Handle<Expression>),
/// Dot product between two vectors.
DotProduct(Handle<Expression>, Handle<Expression>),
/// Cross product between two vectors.
CrossProduct(Handle<Expression>, Handle<Expression>),
/// Cast a simply type to another kind.
As {
/// Source expression, which can only be a scalar or a vector.
@@ -648,15 +703,9 @@ pub enum Expression {
/// True = conversion needs to take place; False = bitcast.
convert: bool,
},
/// Compute the derivative on an axis.
Derivative {
axis: DerivativeAxis,
//modifier,
expr: Handle<Expression>,
},
/// Call another function.
Call {
origin: FunctionOrigin,
function: Handle<Function>,
arguments: Vec<Handle<Expression>>,
},
/// Get the length of an array.

View File

@@ -88,36 +88,37 @@ where
self.traverse_expr(accept);
self.traverse_expr(reject);
}
E::Intrinsic { argument, .. } => {
E::Derivative { expr, .. } => {
self.traverse_expr(expr);
}
E::Relational { argument, .. } => {
self.traverse_expr(argument);
}
E::Math {
arg, arg1, arg2, ..
} => {
self.traverse_expr(arg);
if let Some(arg) = arg1 {
self.traverse_expr(arg);
}
if let Some(arg) = arg2 {
self.traverse_expr(arg);
}
}
E::Transpose(matrix) => {
self.traverse_expr(matrix);
}
E::DotProduct(left, right) => {
self.traverse_expr(left);
self.traverse_expr(right);
}
E::CrossProduct(left, right) => {
self.traverse_expr(left);
self.traverse_expr(right);
}
E::As { expr, .. } => {
self.traverse_expr(expr);
}
E::Derivative { expr, .. } => {
self.traverse_expr(expr);
}
E::Call {
ref origin,
function,
ref arguments,
} => {
for &argument in arguments {
self.traverse_expr(argument);
}
if let crate::FunctionOrigin::Local(fun) = *origin {
self.visitor.visit_fun(fun);
}
self.visitor.visit_fun(function);
}
E::ArrayLength(expr) => {
self.traverse_expr(expr);

View File

@@ -67,3 +67,62 @@ impl crate::TypeInner {
}
}
}
impl crate::MathFunction {
pub fn argument_count(&self) -> usize {
match *self {
// comparison
Self::Abs => 1,
Self::Min => 2,
Self::Max => 2,
Self::Clamp => 3,
// trigonometry
Self::Cos => 1,
Self::Cosh => 1,
Self::Sin => 1,
Self::Sinh => 1,
Self::Tan => 1,
Self::Tanh => 1,
Self::Acos => 1,
Self::Asin => 1,
Self::Atan => 1,
Self::Atan2 => 2,
// decomposition
Self::Ceil => 1,
Self::Floor => 1,
Self::Round => 1,
Self::Fract => 1,
Self::Trunc => 1,
Self::Modf => 2,
Self::Frexp => 2,
Self::Ldexp => 2,
// exponent
Self::Exp => 1,
Self::Exp2 => 1,
Self::Log => 1,
Self::Log2 => 1,
Self::Pow => 2,
// geometry
Self::Dot => 2,
Self::Outer => 2,
Self::Cross => 2,
Self::Distance => 2,
Self::Length => 1,
Self::Normalize => 1,
Self::FaceForward => 3,
Self::Reflect => 2,
// computational
Self::Sign => 1,
Self::Fma => 3,
Self::Mix => 3,
Self::Step => 2,
Self::SmoothStep => 3,
Self::Sqrt => 1,
Self::InverseSqrt => 1,
Self::Determinant => 1,
// bits
Self::CountOneBits => 1,
Self::ReverseBits => 1,
}
}
}

View File

@@ -246,7 +246,122 @@ impl Typifier {
| crate::BinaryOperator::ShiftRight => self.resolutions[left.index()].clone(),
},
crate::Expression::Select { accept, .. } => self.resolutions[accept.index()].clone(),
crate::Expression::Intrinsic { .. } => unimplemented!(),
crate::Expression::Derivative { axis: _, expr } => {
self.resolutions[expr.index()].clone()
}
crate::Expression::Relational { .. } => Resolution::Value(crate::TypeInner::Scalar {
kind: crate::ScalarKind::Bool,
width: 4,
}),
crate::Expression::Math {
fun,
arg,
arg1,
arg2: _,
} => {
use crate::MathFunction as Mf;
match fun {
// comparison
Mf::Abs |
Mf::Min |
Mf::Max |
Mf::Clamp |
// trigonometry
Mf::Cos |
Mf::Cosh |
Mf::Sin |
Mf::Sinh |
Mf::Tan |
Mf::Tanh |
Mf::Acos |
Mf::Asin |
Mf::Atan |
Mf::Atan2 |
// decomposition
Mf::Ceil |
Mf::Floor |
Mf::Round |
Mf::Fract |
Mf::Trunc |
Mf::Modf |
Mf::Frexp |
Mf::Ldexp |
// exponent
Mf::Exp |
Mf::Exp2 |
Mf::Log |
Mf::Log2 |
Mf::Pow => self.resolutions[arg.index()].clone(),
// geometry
Mf::Dot => match *self.get(arg, types) {
crate::TypeInner::Vector {
kind,
size: _,
width,
} => Resolution::Value(crate::TypeInner::Scalar { kind, width }),
ref other => {
return Err(ResolveError::IncompatibleOperand {
op: "dot product".to_string(),
operand: format!("{:?}", other),
})
}
},
Mf::Outer => {
let arg1 = arg1.ok_or_else(|| ResolveError::IncompatibleOperand {
op: "outer product".to_string(),
operand: "".to_string(),
})?;
match (self.get(arg, types), self.get(arg1,types)) {
(&crate::TypeInner::Vector {kind: _, size: columns,width}, &crate::TypeInner::Vector{ size: rows, .. }) => Resolution::Value(crate::TypeInner::Matrix { columns, rows, width }),
(left, right) => {
return Err(ResolveError::IncompatibleOperands {
op: "outer product".to_string(),
left: format!("{:?}", left),
right: format!("{:?}", right),
})
}
}
},
Mf::Cross => self.resolutions[arg.index()].clone(),
Mf::Distance |
Mf::Length => match *self.get(arg, types) {
crate::TypeInner::Scalar {width,kind} |
crate::TypeInner::Vector {width,kind,size:_} => Resolution::Value(crate::TypeInner::Scalar { kind, width }),
ref other => {
return Err(ResolveError::IncompatibleOperand {
op: format!("{:?}", fun),
operand: format!("{:?}", other),
})
}
},
Mf::Normalize |
Mf::FaceForward |
Mf::Reflect => self.resolutions[arg.index()].clone(),
// computational
Mf::Sign |
Mf::Fma |
Mf::Mix |
Mf::Step |
Mf::SmoothStep |
Mf::Sqrt |
Mf::InverseSqrt => self.resolutions[arg.index()].clone(),
Mf::Determinant => match *self.get(arg, types) {
crate::TypeInner::Matrix {
width,
..
} => Resolution::Value(crate::TypeInner::Scalar { kind: crate::ScalarKind::Float, width }),
ref other => {
return Err(ResolveError::IncompatibleOperand {
op: "determinant".to_string(),
operand: format!("{:?}", other),
})
}
},
// bits
Mf::CountOneBits |
Mf::ReverseBits => self.resolutions[arg.index()].clone(),
}
}
crate::Expression::Transpose(expr) => match *self.get(expr, types) {
crate::TypeInner::Matrix {
columns,
@@ -264,20 +379,6 @@ impl Typifier {
})
}
},
crate::Expression::DotProduct(left_expr, _) => match *self.get(left_expr, types) {
crate::TypeInner::Vector {
kind,
size: _,
width,
} => Resolution::Value(crate::TypeInner::Scalar { kind, width }),
ref other => {
return Err(ResolveError::IncompatibleOperand {
op: "dot product".to_string(),
operand: format!("{:?}", other),
})
}
},
crate::Expression::CrossProduct(_, _) => unimplemented!(),
crate::Expression::As {
expr,
kind,
@@ -298,45 +399,11 @@ impl Typifier {
})
}
},
crate::Expression::Derivative { .. } => unimplemented!(),
crate::Expression::Call {
origin: crate::FunctionOrigin::External(ref name),
ref arguments,
} => match name.as_str() {
"distance" | "length" => match *self.get(arguments[0], types) {
crate::TypeInner::Vector { kind, width, .. }
| crate::TypeInner::Scalar { kind, width } => {
Resolution::Value(crate::TypeInner::Scalar { kind, width })
}
ref other => {
return Err(ResolveError::IncompatibleOperand {
op: name.clone(),
operand: format!("{:?}", other),
})
}
},
"dot" => match *self.get(arguments[0], types) {
crate::TypeInner::Vector { kind, width, .. } => {
Resolution::Value(crate::TypeInner::Scalar { kind, width })
}
ref other => {
return Err(ResolveError::IncompatibleOperand {
op: name.clone(),
operand: format!("{:?}", other),
})
}
},
//Note: `cross` is here too, we still need to figure out what to do with it
"abs" | "atan2" | "cos" | "sin" | "floor" | "inverse" | "normalize" | "min"
| "max" | "reflect" | "pow" | "clamp" | "fclamp" | "mix" | "step"
| "smoothstep" | "cross" => self.resolutions[arguments[0].index()].clone(),
_ => return Err(ResolveError::FunctionNotDefined { name: name.clone() }),
},
crate::Expression::Call {
origin: crate::FunctionOrigin::Local(handle),
function,
arguments: _,
} => {
let ty = ctx.functions[handle]
let ty = ctx.functions[function]
.return_type
.ok_or(ResolveError::FunctionReturnsVoid)?;
Resolution::Handle(ty)

View File

@@ -23,10 +23,10 @@ import "GLSL.std.450" as std;
[[stage(vertex)]]
fn main() -> void {
var angle : f32 = -std::atan2(a_particleVel.x, a_particleVel.y);
var angle : f32 = -atan2(a_particleVel.x, a_particleVel.y);
var pos : vec2<f32> = vec2<f32>(
(a_pos.x * std::cos(angle)) - (a_pos.y * std::sin(angle)),
(a_pos.x * std::sin(angle)) + (a_pos.y * std::cos(angle)));
(a_pos.x * cos(angle)) - (a_pos.y * sin(angle)),
(a_pos.x * sin(angle)) + (a_pos.y * cos(angle)));
gl_Position = vec4<f32>(pos + a_particlePos, 0.0, 1.0);
return;
}
@@ -97,14 +97,14 @@ fn main() -> void {
pos = particlesA.particles[i].pos.xy;
vel = particlesA.particles[i].vel.xy;
if (std::distance(pos, vPos) < params.rule1Distance) {
if (distance(pos, vPos) < params.rule1Distance) {
cMass = cMass + pos;
cMassCount = cMassCount + 1;
}
if (std::distance(pos, vPos) < params.rule2Distance) {
if (distance(pos, vPos) < params.rule2Distance) {
colVel = colVel - (pos - vPos);
}
if (std::distance(pos, vPos) < params.rule3Distance) {
if (distance(pos, vPos) < params.rule3Distance) {
cVel = cVel + vel;
cVelCount = cVelCount + 1;
}
@@ -124,7 +124,7 @@ fn main() -> void {
(cVel * params.rule3Scale);
# clamp velocity for a more pleasing simulation
vVel = std::normalize(vVel) * std::fclamp(std::length(vVel), 0.0, 0.1);
vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1);
# kinematic update
vPos = vPos + (vVel * params.deltaT);