Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,292 changes: 2,158 additions & 2,134 deletions docs/parameterData.json

Large diffs are not rendered by default.

39 changes: 35 additions & 4 deletions src/strands/ir_builders.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as DAG from './ir_dag'
import * as CFG from './ir_cfg'
import * as FES from './strands_FES'
import { NodeType, OpCode, BaseType, DataType, BasePriority, OpCodeToSymbol, typeEquals, } from './ir_types';
import { NodeType, OpCode, BaseType, DataType, BasePriority, OpCodeToSymbol, typeEquals, booleanOpCode } from './ir_types';
import { createStrandsNode, StrandsNode } from './strands_node';
import { strandsBuiltinFunctions } from './strands_builtins';

Expand Down Expand Up @@ -50,12 +50,22 @@ export function unaryOpNode(strandsContext, nodeOrValue, opCode) {
node = createStrandsNode(id, dimension, strandsContext);
}
dependsOn = [node.id];

const typeInfo = {
baseType: dag.baseTypes[node.id],
dimension: node.dimension
};
if (booleanOpCode[opCode]) {
typeInfo.baseType = BaseType.BOOL;
typeInfo.dimension = 1;
}

const nodeData = DAG.createNodeData({
nodeType: NodeType.OPERATION,
opCode,
dependsOn,
baseType: dag.baseTypes[node.id],
dimension: node.dimension
baseType: typeInfo.baseType,
dimension: typeInfo.dimension
})
const id = DAG.getOrCreateNode(dag, nodeData);
CFG.recordInBasicBlock(cfg, cfg.currentBlock, id);
Expand Down Expand Up @@ -137,6 +147,11 @@ export function binaryOpNode(strandsContext, leftStrandsNode, rightArg, opCode)
}
}

if (booleanOpCode[opCode]) {
cast.toType.baseType = BaseType.BOOL;
cast.toType.dimension = 1;
}

const nodeData = DAG.createNodeData({
nodeType: NodeType.OPERATION,
opCode,
Expand Down Expand Up @@ -224,6 +239,17 @@ function mapPrimitiveDepsToIDs(strandsContext, typeInfo, dependsOn) {
calculatedDimensions += dimension;
continue;
}
else if (typeof dep === 'boolean') {
// Handle boolean literals - convert to bool type
const { id, dimension } = scalarLiteralNode(strandsContext, { dimension: 1, baseType: BaseType.BOOL }, dep);
mappedDependencies.push(id);
calculatedDimensions += dimension;
// Update baseType to BOOL if it was inferred
if (baseType !== BaseType.BOOL) {
baseType = BaseType.BOOL;
}
continue;
}
else {
FES.userError('type error', `You've tried to construct a scalar or vector type with a non-numeric value: ${dep}`);
}
Expand Down Expand Up @@ -274,7 +300,12 @@ export function primitiveConstructorNode(strandsContext, typeInfo, dependsOn) {
const { mappedDependencies, inferredTypeInfo } = mapPrimitiveDepsToIDs(strandsContext, typeInfo, dependsOn);

const finalType = {
baseType: typeInfo.baseType,
// We might have inferred a non numeric type. Currently this is
// just used for booleans. Maybe this needs to be something more robust
// if we ever want to support inference of e.g. int vectors?
baseType: inferredTypeInfo.baseType === BaseType.BOOL
? BaseType.BOOL
: typeInfo.baseType,
dimension: inferredTypeInfo.dimension
};

Expand Down
27 changes: 17 additions & 10 deletions src/strands/ir_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const BaseType = {
BOOL: "bool",
MAT: "mat",
DEFER: "defer",
ASSIGN_ON_USE: "assign_on_use",
SAMPLER2D: "sampler2D",
SAMPLER: "sampler",
};
Expand All @@ -46,6 +47,7 @@ export const BasePriority = {
[BaseType.BOOL]: 1,
[BaseType.MAT]: 0,
[BaseType.DEFER]: -1,
[BaseType.ASSIGN_ON_USE]: -2,
[BaseType.SAMPLER2D]: -10,
[BaseType.SAMPLER]: -11,
};
Expand All @@ -66,6 +68,7 @@ export const DataType = {
mat3: { fnName: "mat3x3", baseType: BaseType.MAT, dimension:3, priority: 0, },
mat4: { fnName: "mat4x4", baseType: BaseType.MAT, dimension:4, priority: 0, },
defer: { fnName: null, baseType: BaseType.DEFER, dimension: null, priority: -1 },
assign_on_use: { fnName: null, baseType: BaseType.ASSIGN_ON_USE, dimension: null, priority: -2 },
sampler2D: { fnName: "sampler2D", baseType: BaseType.SAMPLER2D, dimension: 1, priority: -10 },
sampler: { fnName: "sampler", baseType: BaseType.SAMPLER, dimension: 1, priority: -11 },
}
Expand Down Expand Up @@ -137,22 +140,22 @@ export const OpCode = {
}
};
export const OperatorTable = [
{ arity: "unary", name: "not", symbol: "!", opCode: OpCode.Unary.LOGICAL_NOT },
{ arity: "unary", boolean: true, name: "not", symbol: "!", opCode: OpCode.Unary.LOGICAL_NOT },
{ arity: "unary", name: "neg", symbol: "-", opCode: OpCode.Unary.NEGATE },
{ arity: "unary", name: "plus", symbol: "+", opCode: OpCode.Unary.PLUS },
{ arity: "binary", name: "add", symbol: "+", opCode: OpCode.Binary.ADD },
{ arity: "binary", name: "sub", symbol: "-", opCode: OpCode.Binary.SUBTRACT },
{ arity: "binary", name: "mult", symbol: "*", opCode: OpCode.Binary.MULTIPLY },
{ arity: "binary", name: "div", symbol: "/", opCode: OpCode.Binary.DIVIDE },
{ arity: "binary", name: "mod", symbol: "%", opCode: OpCode.Binary.MODULO },
{ arity: "binary", name: "equalTo", symbol: "==", opCode: OpCode.Binary.EQUAL },
{ arity: "binary", name: "notEqual", symbol: "!=", opCode: OpCode.Binary.NOT_EQUAL },
{ arity: "binary", name: "greaterThan", symbol: ">", opCode: OpCode.Binary.GREATER_THAN },
{ arity: "binary", name: "greaterEqual", symbol: ">=", opCode: OpCode.Binary.GREATER_EQUAL },
{ arity: "binary", name: "lessThan", symbol: "<", opCode: OpCode.Binary.LESS_THAN },
{ arity: "binary", name: "lessEqual", symbol: "<=", opCode: OpCode.Binary.LESS_EQUAL },
{ arity: "binary", name: "and", symbol: "&&", opCode: OpCode.Binary.LOGICAL_AND },
{ arity: "binary", name: "or", symbol: "||", opCode: OpCode.Binary.LOGICAL_OR },
{ arity: "binary", boolean: true, name: "equalTo", symbol: "==", opCode: OpCode.Binary.EQUAL },
{ arity: "binary", boolean: true, name: "notEqual", symbol: "!=", opCode: OpCode.Binary.NOT_EQUAL },
{ arity: "binary", boolean: true, name: "greaterThan", symbol: ">", opCode: OpCode.Binary.GREATER_THAN },
{ arity: "binary", boolean: true, name: "greaterEqual", symbol: ">=", opCode: OpCode.Binary.GREATER_EQUAL },
{ arity: "binary", boolean: true, name: "lessThan", symbol: "<", opCode: OpCode.Binary.LESS_THAN },
{ arity: "binary", boolean: true, name: "lessEqual", symbol: "<=", opCode: OpCode.Binary.LESS_EQUAL },
{ arity: "binary", boolean: true, name: "and", symbol: "&&", opCode: OpCode.Binary.LOGICAL_AND },
{ arity: "binary", boolean: true, name: "or", symbol: "||", opCode: OpCode.Binary.LOGICAL_OR },
];
export const ConstantFolding = {
[OpCode.Binary.ADD]: (a, b) => a + b,
Expand All @@ -173,7 +176,8 @@ export const ConstantFolding = {
export const OpCodeToSymbol = {};
export const UnarySymbolToName = {};
export const BinarySymbolToName = {};
for (const { symbol, opCode, name, arity } of OperatorTable) {
export const booleanOpCode = {};
for (const { symbol, opCode, name, arity, boolean } of OperatorTable) {
// SymbolToOpCode[symbol] = opCode;
OpCodeToSymbol[opCode] = symbol;
if (arity === 'unary') {
Expand All @@ -182,6 +186,9 @@ for (const { symbol, opCode, name, arity } of OperatorTable) {
if (arity === 'binary') {
BinarySymbolToName[symbol] = name;
}
if (boolean) {
booleanOpCode[opCode] = true;
}
}
export const BlockType = {
GLOBAL: 'global',
Expand Down
17 changes: 15 additions & 2 deletions src/strands/strands_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,20 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
if (args.length > 4) {
FES.userError("type error", "It looks like you've tried to construct a p5.strands node implicitly, with more than 4 components. This is currently not supported.")
}
const { id, dimension } = build.primitiveConstructorNode(strandsContext, { baseType: BaseType.FLOAT, dimension: null }, args.flat());
// Filter out undefined/null values
const flatArgs = args.flat();
const definedArgs = flatArgs.filter(a => a !== undefined && a !== null);

// If all args are undefined, this is likely a `let myVar` at the
// start of an if statement and it will be assigned within the branches.
// For that, we use an assign-on-use node, meaning we'll take the type of the
// values assigned to it.
if (definedArgs.length === 0) {
const { id, dimension } = build.primitiveConstructorNode(strandsContext, { baseType: BaseType.ASSIGN_ON_USE, dimension: null }, [0]);
return createStrandsNode(id, dimension, strandsContext);
}

const { id, dimension } = build.primitiveConstructorNode(strandsContext, { baseType: BaseType.FLOAT, dimension: null }, definedArgs);
return createStrandsNode(id, dimension, strandsContext);//new StrandsNode(id, dimension, strandsContext);
}
//////////////////////////////////////////////
Expand Down Expand Up @@ -337,7 +350,7 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
// variant or also one more directly translated from GLSL, or to be more compatible with
// APIs we documented at the release of 2.x and have to continue supporting.
for (const type in DataType) {
if (type === BaseType.DEFER || type === 'sampler') {
if (type === BaseType.DEFER || type === BaseType.ASSIGN_ON_USE || type === 'sampler') {
continue;
}
const typeInfo = DataType[type];
Expand Down
15 changes: 9 additions & 6 deletions src/strands/strands_phi_utils.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import * as CFG from './ir_cfg';
import * as DAG from './ir_dag';
import { NodeType } from './ir_types';
import { NodeType, BaseType } from './ir_types';

export function createPhiNode(strandsContext, phiInputs, varName) {
// Determine the proper dimension and baseType from the inputs
const validInputs = phiInputs.filter(input => input.value.id !== null);
if (validInputs.length === 0) {
throw new Error(`No valid inputs for phi node for variable ${varName}`);
}
// Get dimension and baseType from first valid input
let firstInput = validInputs
.map((input) => DAG.getNodeDataFromID(strandsContext.dag, input.value.id))
.find((input) => input.dimension) ??
DAG.getNodeDataFromID(strandsContext.dag, validInputs[0].value.id);

// Get dimension and baseType from first valid input, skipping ASSIGN_ON_USE nodes
const inputNodes = validInputs.map((input) => DAG.getNodeDataFromID(strandsContext.dag, input.value.id));
let firstInput = inputNodes.find((input) => input.baseType !== BaseType.ASSIGN_ON_USE && input.dimension) ??
inputNodes.find((input) => input.baseType !== BaseType.ASSIGN_ON_USE) ??
inputNodes[0];

const dimension = firstInput.dimension;
const baseType = firstInput.baseType;

const nodeData = {
nodeType: NodeType.PHI,
dimension,
Expand Down
Loading
Loading