Movatterモバイル変換


[0]ホーム

URL:


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

[p5.strands] Significant refactor for p5.strands#8009

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
davepagurek merged 75 commits intoprocessing:dev-2.0fromlukeplowden:strands-refactor
Sep 18, 2025
Merged
Show file tree
Hide file tree
Changes from1 commit
Commits
Show all changes
75 commits
Select commitHold shift + click to select a range
23ff7e6
syntax/ remove unneccessary
lukeplowdenJun 24, 2025
1511ffb
blocking out new modular strands structure
lukeplowdenJun 27, 2025
604c2dd
chipping away at DOD approach.
lukeplowdenJul 1, 2025
8950817
nested ifs
lukeplowdenJul 5, 2025
f6369e7
if/else semi working
lukeplowdenJul 7, 2025
a355416
change if/elseif/else api to be chainable and functional (return assi…
lukeplowdenJul 9, 2025
3e1e149
binary ops and contructors prototyped
lukeplowdenJul 16, 2025
f718717
simplify type system
lukeplowdenJul 16, 2025
24f0c46
SSA
lukeplowdenJul 16, 2025
0851285
Return type checking for hooks with native types reimplemented (i.e. …
lukeplowdenJul 23, 2025
9b84f6f
declarations moved to backend, hook arguments fixed
lukeplowdenJul 23, 2025
8509231
rename file
lukeplowdenJul 24, 2025
47eda1a
update api imports for new filename
lukeplowdenJul 24, 2025
1088b4d
move extractTypeInfo and rename to extractNodeTypeInfo
lukeplowdenJul 24, 2025
87e8a99
rename files for clarity
lukeplowdenJul 24, 2025
e32fd47
builtin function overloads type checking
lukeplowdenJul 24, 2025
11a1610
function calls partially reimplemented. Still needs more error checking.
lukeplowdenJul 24, 2025
e8f03d6
update function calls to conform parameters when raw numbers are handed
lukeplowdenJul 25, 2025
1ddd9a2
adding struct types
lukeplowdenJul 25, 2025
f3155e6
adding struct types
lukeplowdenJul 25, 2025
babedfd
Merge branch 'strands-refactor' of github.com:lukeplowden/p5.js into …
lukeplowdenJul 25, 2025
afff707
struct types working
lukeplowdenJul 26, 2025
2e70e0e
comment old line. Should revisit structs if needs optimisation.
lukeplowdenJul 26, 2025
6d5913a
fix wrong ID in binary op node
lukeplowdenJul 26, 2025
2745bda
fix bug with binary op, and make strandsNode return node if arg is al…
lukeplowdenJul 26, 2025
4133fae
fix function call bugs
lukeplowdenJul 29, 2025
b3ce3ec
remove dag sort, use basic block instructions instead. Also start wor…
lukeplowdenJul 30, 2025
9ebf77e
syntax/ remove unneccessary
lukeplowdenJun 24, 2025
faae3aa
blocking out new modular strands structure
lukeplowdenJun 27, 2025
f6783d2
chipping away at DOD approach.
lukeplowdenJul 1, 2025
06faa2c
nested ifs
lukeplowdenJul 5, 2025
5d32089
if/else semi working
lukeplowdenJul 7, 2025
95fa410
change if/elseif/else api to be chainable and functional (return assi…
lukeplowdenJul 9, 2025
627b7a3
binary ops and contructors prototyped
lukeplowdenJul 16, 2025
7899f0d
simplify type system
lukeplowdenJul 16, 2025
b731c15
SSA
lukeplowdenJul 16, 2025
7166f35
Return type checking for hooks with native types reimplemented (i.e. …
lukeplowdenJul 23, 2025
e4e54ac
declarations moved to backend, hook arguments fixed
lukeplowdenJul 23, 2025
51e8ddd
rename file
lukeplowdenJul 24, 2025
79c2f8d
update api imports for new filename
lukeplowdenJul 24, 2025
18dc1d3
move extractTypeInfo and rename to extractNodeTypeInfo
lukeplowdenJul 24, 2025
eb5f1bf
rename files for clarity
lukeplowdenJul 24, 2025
446d3ec
builtin function overloads type checking
lukeplowdenJul 24, 2025
83b4cf4
function calls partially reimplemented. Still needs more error checking.
lukeplowdenJul 24, 2025
a743c68
update function calls to conform parameters when raw numbers are handed
lukeplowdenJul 25, 2025
295c140
adding struct types
lukeplowdenJul 25, 2025
7cd3d42
adding struct types
lukeplowdenJul 25, 2025
f7b1339
struct types working
lukeplowdenJul 26, 2025
ba4be8b
comment old line. Should revisit structs if needs optimisation.
lukeplowdenJul 26, 2025
4fe4aaf
fix wrong ID in binary op node
lukeplowdenJul 26, 2025
0908e43
fix bug with binary op, and make strandsNode return node if arg is al…
lukeplowdenJul 26, 2025
5ce9451
fix function call bugs
lukeplowdenJul 29, 2025
54851ba
remove dag sort, use basic block instructions instead. Also start wor…
lukeplowdenJul 30, 2025
2b681b8
Merge branch 'strands-refactor' of github.com:lukeplowden/p5.js into …
lukeplowdenJul 30, 2025
ebaaa08
change example
lukeplowdenJul 30, 2025
3d11637
Update src/strands/ir_builders.js
lukeplowdenAug 5, 2025
347900f
remove CFG sorting, make merge block use default behaviour, change ty…
lukeplowdenAug 5, 2025
1ddd5f8
remove old file and imports
lukeplowdenAug 5, 2025
085d1b8
Merge branch 'strands-refactor' of github.com:lukeplowden/p5.js into …
lukeplowdenAug 5, 2025
f806006
bug fixes, swizzle reads working, swizzle writes WIP
lukeplowdenSep 11, 2025
d2c17af
fix textures, struct bugs, and add swizzle assign.
lukeplowdenSep 15, 2025
d5c7fe8
remove old shadergenerator file
lukeplowdenSep 15, 2025
100304f
remove dev console.log
lukeplowdenSep 15, 2025
2b863e6
add instance mode changes, fix bug where struct properties returned i…
lukeplowdenSep 16, 2025
6462345
mark atan as p5 function, prevent bug where using atan outside strand…
lukeplowdenSep 16, 2025
37abf7f
add back documentation
lukeplowdenSep 16, 2025
f3afffc
add todo for internal parser options
lukeplowdenSep 16, 2025
d4d968a
Merge branch 'dev-2.0' into strands-refactor
davepagurekSep 17, 2025
bf92d1c
Fix issue with strands being immediately active
davepagurekSep 17, 2025
a269bd7
Add back alias for previous uniformVector2 syntax
davepagurekSep 17, 2025
18eb43c
add comments for clarity on swizzling and onrebind
lukeplowdenSep 18, 2025
bae0545
Merge branch 'strands-refactor' of github.com:lukeplowden/p5.js into …
lukeplowdenSep 18, 2025
e81920f
Parse hookTypes into a strands codegen type
davepagurekSep 18, 2025
f43770c
Merge branch 'dev-2.0' into strands-refactor
davepagurekSep 18, 2025
ecc8061
Merge atan test file into trigonometry tests and get it working
davepagurekSep 18, 2025
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
PrevPrevious commit
NextNext commit
Return type checking for hooks with native types reimplemented (i.e. …
…not p5 defined structs such as Vertex inputs)
  • Loading branch information
@lukeplowden
lukeplowden committedJul 23, 2025
commit085128519237b91c59c79ec0d586e01dd9e21207
5 changes: 3 additions & 2 deletionspreview/global/sketch.js
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,8 +3,9 @@ p5.disableFriendlyErrors = true;
function callback() {

getFinalColor((col) => {
let x = vec3(1);
return vec3(1).div(ivec3(1, 2, 4).mult(ivec3(2.0, 2, 3)));
let x = vec4(1);
// return 1;
return vec4(1).div(ivec4(1).mult(ivec4(2.0, 3.0, 2, 3)));
});
}

Expand Down
13 changes: 13 additions & 0 deletionssrc/strands/builder.js
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -196,6 +196,19 @@ export function createFunctionCallNode(strandsContext, identifier, overrides, de
return id;
}

export function createUnaryOpNode(strandsContext, strandsNode, opCode) {
const { dag, cfg } = strandsContext;
const nodeData = DAG.createNodeData({
nodeType: NodeType.OPERATION,
opCode,
dependsOn: strandsNode.id,
baseType: dag.baseTypes[strandsNode.id],
dimension: dag.dimensions[strandsNode.id],
})
CFG.recordInBasicBlock(cfg, cfg.currentBlock, id);
return id;
}

export function createStatementNode(strandsContext, type) {
return -99;
}
8 changes: 5 additions & 3 deletionssrc/strands/code_generation.js
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
import { WEBGL } from '../core/constants';
import { glslBackend } from './GLSL_backend';
import {dfsPostOrder, dfsReversePostOrder,NodeType } from './utils';
import { NodeType } from './utils';
import { extractTypeInfo } from './builder';
import { sortCFG } from './control_flow_graph';
import { sortDAG } from './directed_acyclic_graph';

let globalTempCounter = 0;
let backend;
Expand DownExpand Up@@ -41,8 +43,8 @@ export function generateShaderCode(strandsContext) {

for (const { hookType, entryBlockID, rootNodeID} of strandsContext.hooks) {
const { cfg, dag } = strandsContext;
const dagSorted =dfsPostOrder(dag.dependsOn, rootNodeID);
const cfgSorted =dfsReversePostOrder(cfg.outgoingEdges, entryBlockID);
const dagSorted =sortDAG(dag.dependsOn, rootNodeID);
const cfgSorted =sortCFG(cfg.outgoingEdges, entryBlockID);

const generationContext = {
...generateTopLevelDeclarations(strandsContext, dagSorted),
Expand Down
19 changes: 19 additions & 0 deletionssrc/strands/control_flow_graph.js
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,4 +59,23 @@ export function printBlockData(graph, id) {
const block = getBlockDataFromID(graph, id);
block.blockType = BlockTypeToName[block.blockType];
console.log(block);
}

export function sortCFG(adjacencyList, start) {
const visited = new Set();
const postOrder = [];

function dfs(v) {
if (visited.has(v)) {
return;
}
visited.add(v);
for (let w of adjacencyList[v].sort((a, b) => b-a) || []) {
dfs(w);
}
postOrder.push(v);
}

dfs(start);
return postOrder.reverse();
}
21 changes: 20 additions & 1 deletionsrc/strands/directed_acyclic_graph.js
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import { NodeTypeRequiredFields, NodeTypeToName, TypeInfo } from './utils';
import { NodeTypeRequiredFields, NodeTypeToName } from './utils';
import * as FES from './strands_FES';

/////////////////////////////////
Expand DownExpand Up@@ -113,4 +113,23 @@ function validateNode(node){
if (missingFields.length > 0) {
FES.internalError(`Missing fields ${missingFields.join(', ')} for a node type '${NodeTypeToName[nodeType]}'.`);
}
}

export function sortDAG(adjacencyList, start) {
const visited = new Set();
const postOrder = [];

function dfs(v) {
if (visited.has(v)) {
return;
}
visited.add(v);
for (let w of adjacencyList[v]) {
dfs(w);
}
postOrder.push(v);
}

dfs(start);
return postOrder;
}
4 changes: 2 additions & 2 deletionssrc/strands/p5.strands.js
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@ import { BlockType } from './utils';
import { createDirectedAcyclicGraph } from './directed_acyclic_graph'
import { createControlFlowGraph, createBasicBlock, pushBlock, popBlock } from './control_flow_graph';
import { generateShaderCode } from './code_generation';
import { initGlobalStrandsAPI,initShaderHooksFunctions } from './user_API';
import { initGlobalStrandsAPI,createShaderHooksFunctions } from './user_API';

function strands(p5, fn) {
//////////////////////////////////////////////
Expand DownExpand Up@@ -51,7 +51,7 @@ function strands(p5, fn) {
// Reset the context object every time modify is called;
const backend = WEBGL;
initStrandsContext(strandsContext, backend);
initShaderHooksFunctions(strandsContext, fn, this);
createShaderHooksFunctions(strandsContext, fn, this);

// 1. Transpile from strands DSL to JS
let strandsCallback;
Expand Down
79 changes: 61 additions & 18 deletionssrc/strands/user_API.js
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,8 +4,9 @@ import {
createVariableNode,
createStatementNode,
createTypeConstructorNode,
createUnaryOpNode,
} from './builder'
import { OperatorTable,SymbolToOpCode,BlockType, TypeInfo, BaseType, TypeInfoFromGLSLName } from './utils'
import { OperatorTable, BlockType, TypeInfo, BaseType, TypeInfoFromGLSLName } from './utils'
import { strandsShaderFunctions } from './shader_functions'
import { StrandsConditional } from './strands_conditionals'
import * as CFG from './control_flow_graph'
Expand All@@ -23,19 +24,19 @@ export class StrandsNode {
export function initGlobalStrandsAPI(p5, fn, strandsContext) {
// We augment the strands node with operations programatically
// this means methods like .add, .sub, etc can be chained
for (const { name,symbol, arity } of OperatorTable) {
for (const { name,arity, opCode, symbol } of OperatorTable) {
if (arity === 'binary') {
StrandsNode.prototype[name] = function (...right) {
const id = createBinaryOpNode(strandsContext, this, right,SymbolToOpCode[symbol]);
const id = createBinaryOpNode(strandsContext, this, right,opCode);
return new StrandsNode(id);
};
}
//if (arity === 'unary') {
// StrandsNode.prototype[name] = function () {
//const id =createUnaryExpressionNode(this, SymbolToOpCode[symbol]);
// return new StrandsNode(id);
// };
//}
if (arity === 'unary') {
fn[name] = function (strandsNode) {
const id =createUnaryOpNode(strandsContext, strandsNode, opCode);
return new StrandsNode(id);
}
}
}

//////////////////////////////////////////////
Expand DownExpand Up@@ -134,17 +135,20 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
//////////////////////////////////////////////
// Per-Hook functions
//////////////////////////////////////////////
const structTypes = ['Vertex', ]

function createHookArguments(strandsContext, parameters){
const structTypes = ['Vertex', ]
const args = [];

for (const param of parameters) {
const paramType = param.type;
if(structTypes.includes(paramType.typeName)) {
const propertiesNodes = paramType.properties.map(
(prop) => [prop.name, createVariableNode(strandsContext, TypeInfoFromGLSLName[prop.dataType], prop.name)]
);
const argObject = Object.fromEntries(propertiesNodes);
const propertyEntries = paramType.properties.map((prop) => {
const typeInfo = TypeInfoFromGLSLName[prop.dataType];
const variableNode = createVariableNode(strandsContext, typeInfo, prop.name);
return [prop.name, variableNode];
});
const argObject = Object.fromEntries(propertyEntries);
args.push(argObject);
} else {
const typeInfo = TypeInfoFromGLSLName[paramType.typeName];
Expand All@@ -155,24 +159,63 @@ function createHookArguments(strandsContext, parameters){
return args;
}

export functioninitShaderHooksFunctions(strandsContext, fn, shader) {
export functioncreateShaderHooksFunctions(strandsContext, fn, shader) {
const availableHooks = {
...shader.hooks.vertex,
...shader.hooks.fragment,
}
const hookTypes = Object.keys(availableHooks).map(name => shader.hookTypes(name));
const { cfg } = strandsContext;
const { cfg, dag } = strandsContext;

for (const hookType of hookTypes) {
window[hookType.name] = function(hookUserCallback) {
const entryBlockID = CFG.createBasicBlock(cfg, BlockType.FUNCTION);
CFG.addEdge(cfg, cfg.currentBlock, entryBlockID);
CFG.pushBlock(cfg, entryBlockID);

const args = createHookArguments(strandsContext, hookType.parameters);
const rootNodeID = hookUserCallback(args).id;
const returned = hookUserCallback(args);
let returnedNode;

const expectedReturnType = hookType.returnType;
if(structTypes.includes(expectedReturnType.typeName)) {

}
else {
// In this case we are expecting a native shader type, probably vec4 or vec3.
const expected = TypeInfoFromGLSLName[expectedReturnType.typeName];
// User may have returned a raw value like [1,1,1,1] or 25.
if (!(returned instanceof StrandsNode)) {
const id = createTypeConstructorNode(strandsContext, { baseType: BaseType.DEFER, dimension: null }, returned);
returnedNode = new StrandsNode(id);
}
else {
returnedNode = returned;
}

const received = {
baseType: dag.baseTypes[returnedNode.id],
dimension: dag.dimensions[returnedNode.id],
}
if (received.dimension !== expected.dimension) {
if (received.dimension !== 1) {
FES.userError('type error', `You have returned a vector with ${received.dimension} components in ${hookType.name} when a ${expected.baseType + expected.dimension} was expected!`);
}
else {
const newID = createTypeConstructorNode(strandsContext, expected, returnedNode);
returnedNode = new StrandsNode(newID);
}
}
else if (received.baseType !== expected.baseType) {
const newID = createTypeConstructorNode(strandsContext, expected, returnedNode);
returnedNode = new StrandsNode(newID);
}
}

strandsContext.hooks.push({
hookType,
entryBlockID,
rootNodeID,
rootNodeID: returnedNode.id,
});
CFG.popBlock(cfg);
}
Expand Down
100 changes: 21 additions & 79 deletionssrc/strands/utils.js
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -102,22 +102,22 @@ export const OpCode = {
};

export const OperatorTable = [
{ arity: "unary", 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: "unary", 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 },
];

export const ConstantFolding = {
Expand All@@ -138,13 +138,10 @@ export const ConstantFolding = {

export const SymbolToOpCode = {};
export const OpCodeToSymbol = {};
export const OpCodeArgs = {};
export const OpCodeToOperation = {};

for (const { arity, symbol, opcode } of OperatorTable) {
SymbolToOpCode[symbol] = opcode;
OpCodeToSymbol[opcode] = symbol;
OpCodeArgs[opcode] = args;
for (const { symbol, opCode } of OperatorTable) {
SymbolToOpCode[symbol] = opCode;
OpCodeToSymbol[opCode] = symbol;
}

export const BlockType = {
Expand All@@ -158,63 +155,8 @@ export const BlockType = {
FOR: 7,
MERGE: 8,
DEFAULT: 9,

}

export const BlockTypeToName = Object.fromEntries(
Object.entries(BlockType).map(([key, val]) => [val, key])
);

////////////////////////////
// Type Checking helpers
////////////////////////////
export function arrayToFloatType(array) {
let type = false;
if (array.length === 1) {
type = `FLOAT`;
} else if (array.length >= 2 && array.length <= 4) {
type = `VEC${array.length}`;
} else {
throw new Error('Tried to construct a float / vector with and empty array, or more than 4 components!')
}
}

////////////////////////////
// Graph utils
////////////////////////////
export function dfsPostOrder(adjacencyList, start) {
const visited = new Set();
const postOrder = [];

function dfs(v) {
if (visited.has(v)) {
return;
}
visited.add(v);
for (let w of adjacencyList[v]) {
dfs(w);
}
postOrder.push(v);
}

dfs(start);
return postOrder;
}

export function dfsReversePostOrder(adjacencyList, start) {
const visited = new Set();
const postOrder = [];

function dfs(v) {
if (visited.has(v)) {
return;
}
visited.add(v);
for (let w of adjacencyList[v].sort((a, b) => b-a) || []) {
dfs(w);
}
postOrder.push(v);
}

dfs(start);
return postOrder.reverse();
}
);

[8]ページ先頭

©2009-2025 Movatter.jp