Skip to content

Commit d46a782

Browse files
committed
Handle swizzle assignments
1 parent 63ed355 commit d46a782

4 files changed

Lines changed: 118 additions & 16 deletions

File tree

src/strands/strands_glslBackend.js

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -200,22 +200,15 @@ export const glslBackend = {
200200
// dependsOn[0] = targetNodeID, dependsOn[1] = sourceNodeID
201201
const targetNodeID = node.dependsOn[0];
202202
const sourceNodeID = node.dependsOn[1];
203-
const targetNode = getNodeDataFromID(dag, targetNodeID);
204-
205-
// Check if this is a varying variable assignment (target has identifier)
206-
let targetName;
207-
if (targetNode && targetNode.identifier) {
208-
targetName = targetNode.identifier; // Use variable identifier directly
209-
} else {
210-
targetName = generationContext.tempNames[targetNodeID]; // Use temp name for phi nodes
211-
}
212203

204+
// Generate the target expression (could be variable or swizzle)
205+
const targetExpr = this.generateExpression(generationContext, dag, targetNodeID);
213206
const sourceExpr = this.generateExpression(generationContext, dag, sourceNodeID);
214207
const semicolon = generationContext.suppressSemicolon ? '' : ';';
215208

216209
// Generate assignment if we have both target and source
217-
if (targetName && sourceExpr && targetName !== sourceExpr) {
218-
generationContext.write(`${targetName} = ${sourceExpr}${semicolon}`);
210+
if (targetExpr && sourceExpr && targetExpr !== sourceExpr) {
211+
generationContext.write(`${targetExpr} = ${sourceExpr}${semicolon}`);
219212
}
220213
},
221214
generateDeclaration(generationContext, dag, nodeID) {

src/strands/strands_node.js

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { swizzleTrap, primitiveConstructorNode, variableNode } from './ir_builders';
2-
import { BaseType, NodeType } from './ir_types';
2+
import { BaseType, NodeType, OpCode } from './ir_types';
33
import { getNodeDataFromID, createNodeData, getOrCreateNode } from './ir_dag';
44
import { recordInBasicBlock } from './ir_cfg';
55
export class StrandsNode {
@@ -75,6 +75,72 @@ export class StrandsNode {
7575

7676
return this;
7777
}
78+
bridgeSwizzle(swizzlePattern, value) {
79+
const { dag, cfg } = this.strandsContext;
80+
const orig = getNodeDataFromID(dag, this.id);
81+
const baseType = orig?.baseType ?? BaseType.FLOAT;
82+
83+
let newValueID;
84+
if (value instanceof StrandsNode) {
85+
newValueID = value.id;
86+
} else {
87+
const newVal = primitiveConstructorNode(
88+
this.strandsContext,
89+
{ baseType, dimension: this.dimension },
90+
value
91+
);
92+
newValueID = newVal.id;
93+
}
94+
95+
// For varying variables, create swizzle assignment
96+
if (this._originalIdentifier) {
97+
// Create a variable node for the target with swizzle
98+
const { id: targetVarID } = variableNode(
99+
this.strandsContext,
100+
{ baseType: this._originalBaseType, dimension: this._originalDimension },
101+
this._originalIdentifier
102+
);
103+
104+
// Create a swizzle node for the target (myVarying.xyz)
105+
const swizzleNode = createNodeData({
106+
nodeType: NodeType.OPERATION,
107+
opCode: OpCode.Unary.SWIZZLE,
108+
baseType: this._originalBaseType,
109+
dimension: swizzlePattern.length, // xyz = 3, xy = 2, etc.
110+
swizzle: swizzlePattern,
111+
dependsOn: [targetVarID]
112+
});
113+
const swizzleID = getOrCreateNode(dag, swizzleNode);
114+
115+
// Create assignment node: myVarying.xyz = value
116+
const assignmentNode = createNodeData({
117+
nodeType: NodeType.ASSIGNMENT,
118+
dependsOn: [swizzleID, newValueID],
119+
phiBlocks: []
120+
});
121+
const assignmentID = getOrCreateNode(dag, assignmentNode);
122+
recordInBasicBlock(cfg, cfg.currentBlock, assignmentID);
123+
124+
// Track for global assignments processing in the current hook context
125+
this.strandsContext.globalAssignments.push(assignmentID);
126+
127+
// Simply update this node to be a variable node with the identifier
128+
// This ensures it always generates the variable name in expressions
129+
const variableNodeData = createNodeData({
130+
nodeType: NodeType.VARIABLE,
131+
baseType: this._originalBaseType,
132+
dimension: this._originalDimension,
133+
identifier: this._originalIdentifier
134+
});
135+
const variableID = getOrCreateNode(dag, variableNodeData);
136+
137+
this.id = variableID; // Point to the variable node, not the assignment node
138+
} else {
139+
this.id = newValueID; // For non-varying variables, just update to new value
140+
}
141+
142+
return this;
143+
}
78144
getValue() {
79145
if (this._originalIdentifier) {
80146
const { id, dimension } = variableNode(

src/strands/strands_transpiler.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ const ASTCallbacks = {
178178
node.operator = '=';
179179
node.right = rightReplacementNode;
180180
}
181+
// Handle direct varying variable assignment: myVarying = value
181182
if (_state.varyings[node.left.name]) {
182183
node.type = 'ExpressionStatement';
183184
node.expression = {
@@ -196,6 +197,51 @@ const ASTCallbacks = {
196197
arguments: [node.right],
197198
}
198199
}
200+
// Handle swizzle assignment to varying variable: myVarying.xyz = value
201+
// Note: node.left.object might be worldPos.getValue() due to prior Identifier transformation
202+
else if (node.left.type === 'MemberExpression') {
203+
let varyingName = null;
204+
205+
// Check if it's a direct identifier: myVarying.xyz
206+
if (node.left.object.type === 'Identifier' && _state.varyings[node.left.object.name]) {
207+
varyingName = node.left.object.name;
208+
}
209+
// Check if it's a getValue() call: myVarying.getValue().xyz
210+
else if (node.left.object.type === 'ExpressionStatement' &&
211+
node.left.object.expression?.type === 'CallExpression' &&
212+
node.left.object.expression.callee?.type === 'MemberExpression' &&
213+
node.left.object.expression.callee.property?.name === 'getValue' &&
214+
node.left.object.expression.callee.object?.type === 'Identifier' &&
215+
_state.varyings[node.left.object.expression.callee.object.name]) {
216+
varyingName = node.left.object.expression.callee.object.name;
217+
}
218+
219+
if (varyingName) {
220+
const swizzlePattern = node.left.property.name;
221+
node.type = 'ExpressionStatement';
222+
node.expression = {
223+
type: 'CallExpression',
224+
callee: {
225+
type: 'MemberExpression',
226+
object: {
227+
type: 'Identifier',
228+
name: varyingName
229+
},
230+
property: {
231+
type: 'Identifier',
232+
name: 'bridgeSwizzle',
233+
}
234+
},
235+
arguments: [
236+
{
237+
type: 'Literal',
238+
value: swizzlePattern
239+
},
240+
node.right
241+
],
242+
}
243+
}
244+
}
199245
},
200246
BinaryExpression(node, _state, ancestors) {
201247
// Don't convert uniform default values to node methods, as

test/unit/webgl/p5.Shader.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,7 +1100,7 @@ suite('p5.Shader', function() {
11001100
assert.approximately(cornerColor[2], 0, 5);
11011101
});
11021102

1103-
test.only('handle passing a value from a vertex hook to a fragment hook', () => {
1103+
test('handle passing a value from a vertex hook to a fragment hook', () => {
11041104
myp5.createCanvas(50, 50, myp5.WEBGL);
11051105

11061106
const testShader = myp5.baseMaterialShader().modify(() => {
@@ -1114,9 +1114,6 @@ suite('p5.Shader', function() {
11141114
});
11151115
}, { myp5 });
11161116

1117-
console.log('VERTEX SHADER OUTPUT:');
1118-
console.log(testShader.vertSrc());
1119-
11201117
myp5.background(0, 0, 255); // Make the background blue to tell it apart
11211118
myp5.noStroke();
11221119
myp5.shader(testShader);

0 commit comments

Comments
 (0)