(strandsContext, bufferNode, indexNode, schema)
| 655 | } |
| 656 | |
| 657 | export function createStructArrayElementProxy(strandsContext, bufferNode, indexNode, schema) { |
| 658 | const { dag, cfg } = strandsContext; |
| 659 | |
| 660 | // Ensure index is a StrandsNode |
| 661 | let index; |
| 662 | if (indexNode instanceof StrandsNode) { |
| 663 | index = indexNode; |
| 664 | } else { |
| 665 | const { id, dimension } = primitiveConstructorNode( |
| 666 | strandsContext, |
| 667 | { baseType: BaseType.INT, dimension: 1 }, |
| 668 | indexNode |
| 669 | ); |
| 670 | index = createStrandsNode(id, dimension, strandsContext); |
| 671 | } |
| 672 | |
| 673 | // Create a plain object with getters/setters for each struct field. |
| 674 | // When read, a field creates an ARRAY_ACCESS IR node with the field name encoded |
| 675 | // in the identifier slot. When written, an ASSIGNMENT IR node is recorded in the CFG. |
| 676 | const proxy = {}; |
| 677 | |
| 678 | for (const field of schema.fields) { |
| 679 | Object.defineProperty(proxy, field.name, { |
| 680 | get() { |
| 681 | // Encode field name in identifier so WGSL backend can emit buf[idx].field |
| 682 | const nodeData = DAG.createNodeData({ |
| 683 | nodeType: NodeType.OPERATION, |
| 684 | opCode: OpCode.Binary.ARRAY_ACCESS, |
| 685 | dependsOn: [bufferNode.id, index.id], |
| 686 | dimension: field.dim, |
| 687 | baseType: BaseType.FLOAT, |
| 688 | identifier: field.name, |
| 689 | }); |
| 690 | const id = DAG.getOrCreateNode(dag, nodeData); |
| 691 | CFG.recordInBasicBlock(cfg, cfg.currentBlock, id); |
| 692 | // When a swizzle assignment fires (e.g. buf[i].vel.y *= -1), onRebind |
| 693 | // receives the new vector ID and writes it back to the buffer field, |
| 694 | // equivalent to buf[i].vel = newVec. |
| 695 | const onRebind = (newFieldID) => { |
| 696 | const accessData = DAG.createNodeData({ |
| 697 | nodeType: NodeType.OPERATION, |
| 698 | opCode: OpCode.Binary.ARRAY_ACCESS, |
| 699 | dependsOn: [bufferNode.id, index.id], |
| 700 | dimension: field.dim, |
| 701 | baseType: BaseType.FLOAT, |
| 702 | identifier: field.name, |
| 703 | }); |
| 704 | const accessID = DAG.getOrCreateNode(dag, accessData); |
| 705 | const assignData = DAG.createNodeData({ |
| 706 | nodeType: NodeType.ASSIGNMENT, |
| 707 | dependsOn: [accessID, newFieldID], |
| 708 | phiBlocks: [], |
| 709 | }); |
| 710 | const assignID = DAG.getOrCreateNode(dag, assignData); |
| 711 | CFG.recordInBasicBlock(cfg, cfg.currentBlock, assignID); |
| 712 | }; |
| 713 | return createStrandsNode(id, field.dim, strandsContext, onRebind); |
| 714 | }, |
no test coverage detected