Dark Mode

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5575a5a

Browse files
authored
Merge pull request #8548 from processing/strands-boolean-handling
Handle booleans used as temp variables in p5.strands
2 parents 5357b21 + aa2b859 commit 5575a5a

File tree

10 files changed

+3431
-2991
lines changed
  • docs
    • parameterData.json
  • src
    • strands
      • ir_builders.js
      • ir_types.js
      • strands_api.js
      • strands_phi_utils.js
      • strands_transpiler.js
    • webgpu
      • strands_wgslBackend.js
  • test/unit
    • webgl
      • p5.Shader.js
    • webgpu
      • p5.Shader.js
  • utils
    • typescript.mjs

10 files changed

+3431
-2991
lines changed

docs/parameterData.json

Lines changed: 2158 additions & 2134 deletions
Large diffs are not rendered by default.

src/strands/ir_builders.js

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as DAG from './ir_dag'
22
import * as CFG from './ir_cfg'
33
import * as FES from './strands_FES'
4-
import { NodeType, OpCode, BaseType, DataType, BasePriority, OpCodeToSymbol, typeEquals, } from './ir_types';
4+
import { NodeType, OpCode, BaseType, DataType, BasePriority, OpCodeToSymbol, typeEquals, booleanOpCode } from './ir_types';
55
import { createStrandsNode, StrandsNode } from './strands_node';
66
import { strandsBuiltinFunctions } from './strands_builtins';
77

@@ -50,12 +50,22 @@ export function unaryOpNode(strandsContext, nodeOrValue, opCode) {
5050
node = createStrandsNode(id, dimension, strandsContext);
5151
}
5252
dependsOn = [node.id];
53+
54+
const typeInfo = {
55+
baseType: dag.baseTypes[node.id],
56+
dimension: node.dimension
57+
};
58+
if (booleanOpCode[opCode]) {
59+
typeInfo.baseType = BaseType.BOOL;
60+
typeInfo.dimension = 1;
61+
}
62+
5363
const nodeData = DAG.createNodeData({
5464
nodeType: NodeType.OPERATION,
5565
opCode,
5666
dependsOn,
57-
baseType: dag.baseTypes[node.id],
58-
dimension: node.dimension
67+
baseType: typeInfo.baseType,
68+
dimension: typeInfo.dimension
5969
})
6070
const id = DAG.getOrCreateNode(dag, nodeData);
6171
CFG.recordInBasicBlock(cfg, cfg.currentBlock, id);
@@ -137,6 +147,11 @@ export function binaryOpNode(strandsContext, leftStrandsNode, rightArg, opCode)
137147
}
138148
}
139149

150+
if (booleanOpCode[opCode]) {
151+
cast.toType.baseType = BaseType.BOOL;
152+
cast.toType.dimension = 1;
153+
}
154+
140155
const nodeData = DAG.createNodeData({
141156
nodeType: NodeType.OPERATION,
142157
opCode,
@@ -224,6 +239,17 @@ function mapPrimitiveDepsToIDs(strandsContext, typeInfo, dependsOn) {
224239
calculatedDimensions += dimension;
225240
continue;
226241
}
242+
else if (typeof dep === 'boolean') {
243+
// Handle boolean literals - convert to bool type
244+
const { id, dimension } = scalarLiteralNode(strandsContext, { dimension: 1, baseType: BaseType.BOOL }, dep);
245+
mappedDependencies.push(id);
246+
calculatedDimensions += dimension;
247+
// Update baseType to BOOL if it was inferred
248+
if (baseType !== BaseType.BOOL) {
249+
baseType = BaseType.BOOL;
250+
}
251+
continue;
252+
}
227253
else {
228254
FES.userError('type error', `You've tried to construct a scalar or vector type with a non-numeric value: ${dep}`);
229255
}
@@ -274,7 +300,12 @@ export function primitiveConstructorNode(strandsContext, typeInfo, dependsOn) {
274300
const { mappedDependencies, inferredTypeInfo } = mapPrimitiveDepsToIDs(strandsContext, typeInfo, dependsOn);
275301

276302
const finalType = {
277-
baseType: typeInfo.baseType,
303+
// We might have inferred a non numeric type. Currently this is
304+
// just used for booleans. Maybe this needs to be something more robust
305+
// if we ever want to support inference of e.g. int vectors?
306+
baseType: inferredTypeInfo.baseType === BaseType.BOOL
307+
? BaseType.BOOL
308+
: typeInfo.baseType,
278309
dimension: inferredTypeInfo.dimension
279310
};
280311

src/strands/ir_types.js

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export const BaseType = {
3737
BOOL: "bool",
3838
MAT: "mat",
3939
DEFER: "defer",
40+
ASSIGN_ON_USE: "assign_on_use",
4041
SAMPLER2D: "sampler2D",
4142
SAMPLER: "sampler",
4243
};
@@ -46,6 +47,7 @@ export const BasePriority = {
4647
[BaseType.BOOL]: 1,
4748
[BaseType.MAT]: 0,
4849
[BaseType.DEFER]: -1,
50+
[BaseType.ASSIGN_ON_USE]: -2,
4951
[BaseType.SAMPLER2D]: -10,
5052
[BaseType.SAMPLER]: -11,
5153
};
@@ -66,6 +68,7 @@ export const DataType = {
6668
mat3: { fnName: "mat3x3", baseType: BaseType.MAT, dimension:3, priority: 0, },
6769
mat4: { fnName: "mat4x4", baseType: BaseType.MAT, dimension:4, priority: 0, },
6870
defer: { fnName: null, baseType: BaseType.DEFER, dimension: null, priority: -1 },
71+
assign_on_use: { fnName: null, baseType: BaseType.ASSIGN_ON_USE, dimension: null, priority: -2 },
6972
sampler2D: { fnName: "sampler2D", baseType: BaseType.SAMPLER2D, dimension: 1, priority: -10 },
7073
sampler: { fnName: "sampler", baseType: BaseType.SAMPLER, dimension: 1, priority: -11 },
7174
}
@@ -137,22 +140,22 @@ export const OpCode = {
137140
}
138141
};
139142
export const OperatorTable = [
140-
{ arity: "unary", name: "not", symbol: "!", opCode: OpCode.Unary.LOGICAL_NOT },
143+
{ arity: "unary", boolean: true, name: "not", symbol: "!", opCode: OpCode.Unary.LOGICAL_NOT },
141144
{ arity: "unary", name: "neg", symbol: "-", opCode: OpCode.Unary.NEGATE },
142145
{ arity: "unary", name: "plus", symbol: "+", opCode: OpCode.Unary.PLUS },
143146
{ arity: "binary", name: "add", symbol: "+", opCode: OpCode.Binary.ADD },
144147
{ arity: "binary", name: "sub", symbol: "-", opCode: OpCode.Binary.SUBTRACT },
145148
{ arity: "binary", name: "mult", symbol: "*", opCode: OpCode.Binary.MULTIPLY },
146149
{ arity: "binary", name: "div", symbol: "/", opCode: OpCode.Binary.DIVIDE },
147150
{ arity: "binary", name: "mod", symbol: "%", opCode: OpCode.Binary.MODULO },
148-
{ arity: "binary", name: "equalTo", symbol: "==", opCode: OpCode.Binary.EQUAL },
149-
{ arity: "binary", name: "notEqual", symbol: "!=", opCode: OpCode.Binary.NOT_EQUAL },
150-
{ arity: "binary", name: "greaterThan", symbol: ">", opCode: OpCode.Binary.GREATER_THAN },
151-
{ arity: "binary", name: "greaterEqual", symbol: ">=", opCode: OpCode.Binary.GREATER_EQUAL },
152-
{ arity: "binary", name: "lessThan", symbol: "<", opCode: OpCode.Binary.LESS_THAN },
153-
{ arity: "binary", name: "lessEqual", symbol: "<=", opCode: OpCode.Binary.LESS_EQUAL },
154-
{ arity: "binary", name: "and", symbol: "&&", opCode: OpCode.Binary.LOGICAL_AND },
155-
{ arity: "binary", name: "or", symbol: "||", opCode: OpCode.Binary.LOGICAL_OR },
151+
{ arity: "binary", boolean: true, name: "equalTo", symbol: "==", opCode: OpCode.Binary.EQUAL },
152+
{ arity: "binary", boolean: true, name: "notEqual", symbol: "!=", opCode: OpCode.Binary.NOT_EQUAL },
153+
{ arity: "binary", boolean: true, name: "greaterThan", symbol: ">", opCode: OpCode.Binary.GREATER_THAN },
154+
{ arity: "binary", boolean: true, name: "greaterEqual", symbol: ">=", opCode: OpCode.Binary.GREATER_EQUAL },
155+
{ arity: "binary", boolean: true, name: "lessThan", symbol: "<", opCode: OpCode.Binary.LESS_THAN },
156+
{ arity: "binary", boolean: true, name: "lessEqual", symbol: "<=", opCode: OpCode.Binary.LESS_EQUAL },
157+
{ arity: "binary", boolean: true, name: "and", symbol: "&&", opCode: OpCode.Binary.LOGICAL_AND },
158+
{ arity: "binary", boolean: true, name: "or", symbol: "||", opCode: OpCode.Binary.LOGICAL_OR },
156159
];
157160
export const ConstantFolding = {
158161
[OpCode.Binary.ADD]: (a, b) => a + b,
@@ -173,7 +176,8 @@ export const ConstantFolding = {
173176
export const OpCodeToSymbol = {};
174177
export const UnarySymbolToName = {};
175178
export const BinarySymbolToName = {};
176-
for (const { symbol, opCode, name, arity } of OperatorTable) {
179+
export const booleanOpCode = {};
180+
for (const { symbol, opCode, name, arity, boolean } of OperatorTable) {
177181
// SymbolToOpCode[symbol] = opCode;
178182
OpCodeToSymbol[opCode] = symbol;
179183
if (arity === 'unary') {
@@ -182,6 +186,9 @@ for (const { symbol, opCode, name, arity } of OperatorTable) {
182186
if (arity === 'binary') {
183187
BinarySymbolToName[symbol] = name;
184188
}
189+
if (boolean) {
190+
booleanOpCode[opCode] = true;
191+
}
185192
}
186193
export const BlockType = {
187194
GLOBAL: 'global',

src/strands/strands_api.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,20 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
198198
if (args.length > 4) {
199199
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.")
200200
}
201-
const { id, dimension } = build.primitiveConstructorNode(strandsContext, { baseType: BaseType.FLOAT, dimension: null }, args.flat());
201+
// Filter out undefined/null values
202+
const flatArgs = args.flat();
203+
const definedArgs = flatArgs.filter(a => a !== undefined && a !== null);
204+
205+
// If all args are undefined, this is likely a `let myVar` at the
206+
// start of an if statement and it will be assigned within the branches.
207+
// For that, we use an assign-on-use node, meaning we'll take the type of the
208+
// values assigned to it.
209+
if (definedArgs.length === 0) {
210+
const { id, dimension } = build.primitiveConstructorNode(strandsContext, { baseType: BaseType.ASSIGN_ON_USE, dimension: null }, [0]);
211+
return createStrandsNode(id, dimension, strandsContext);
212+
}
213+
214+
const { id, dimension } = build.primitiveConstructorNode(strandsContext, { baseType: BaseType.FLOAT, dimension: null }, definedArgs);
202215
return createStrandsNode(id, dimension, strandsContext);//new StrandsNode(id, dimension, strandsContext);
203216
}
204217
//////////////////////////////////////////////
@@ -337,7 +350,7 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
337350
// variant or also one more directly translated from GLSL, or to be more compatible with
338351
// APIs we documented at the release of 2.x and have to continue supporting.
339352
for (const type in DataType) {
340-
if (type === BaseType.DEFER || type === 'sampler') {
353+
if (type === BaseType.DEFER || type === BaseType.ASSIGN_ON_USE || type === 'sampler') {
341354
continue;
342355
}
343356
const typeInfo = DataType[type];

src/strands/strands_phi_utils.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
import * as CFG from './ir_cfg';
22
import * as DAG from './ir_dag';
3-
import { NodeType } from './ir_types';
3+
import { NodeType, BaseType } from './ir_types';
44

55
export function createPhiNode(strandsContext, phiInputs, varName) {
66
// Determine the proper dimension and baseType from the inputs
77
const validInputs = phiInputs.filter(input => input.value.id !== null);
88
if (validInputs.length === 0) {
99
throw new Error(`No valid inputs for phi node for variable ${varName}`);
1010
}
11-
// Get dimension and baseType from first valid input
12-
let firstInput = validInputs
13-
.map((input) => DAG.getNodeDataFromID(strandsContext.dag, input.value.id))
14-
.find((input) => input.dimension) ??
15-
DAG.getNodeDataFromID(strandsContext.dag, validInputs[0].value.id);
11+
12+
// Get dimension and baseType from first valid input, skipping ASSIGN_ON_USE nodes
13+
const inputNodes = validInputs.map((input) => DAG.getNodeDataFromID(strandsContext.dag, input.value.id));
14+
let firstInput = inputNodes.find((input) => input.baseType !== BaseType.ASSIGN_ON_USE && input.dimension) ??
15+
inputNodes.find((input) => input.baseType !== BaseType.ASSIGN_ON_USE) ??
16+
inputNodes[0];
17+
1618
const dimension = firstInput.dimension;
1719
const baseType = firstInput.baseType;
20+
1821
const nodeData = {
1922
nodeType: NodeType.PHI,
2023
dimension,

0 commit comments

Comments
(0)