11import { stitch } from '../core/resolve/stitch.ts' ;
2- import { invariant , WgslTypeError } from '../errors.ts' ;
2+ import { invariant } from '../errors.ts' ;
33import { inCodegenMode } from '../execMode.ts' ;
44import { setName } from '../shared/meta.ts' ;
55import { $internal , $isRef , $ownSnippet , $resolve } from '../shared/symbols.ts' ;
66import type { ResolutionCtx , SelfResolvable } from '../types.ts' ;
77import { UnknownData } from './dataTypes.ts' ;
88import type { DualFn } from './dualFn.ts' ;
9- import { INTERNAL_createPtr } from './ptr.ts' ;
10- import {
11- isEphemeralSnippet ,
12- type OriginToPtrParams ,
13- originToPtrParams ,
14- type ResolvedSnippet ,
15- snip ,
16- type Snippet ,
17- } from './snippet.ts' ;
9+ import { createPtrFromOrigin } from './ptr.ts' ;
10+ import { type ResolvedSnippet , snip , type Snippet } from './snippet.ts' ;
1811import {
1912 isNaturallyEphemeral ,
2013 isPtr ,
@@ -29,17 +22,25 @@ import {
2922export interface ref < T > {
3023 readonly [ $internal ] : unknown ;
3124 readonly [ $isRef ] : true ;
25+
26+ /**
27+ * Derefences the reference, and gives access to the underlying value.
28+ *
29+ * @example ```ts
30+ * const boid = Boid({ pos: d.vec3f(3, 2, 1) });
31+ * const posRef = d.ref(boid.pos);
32+ *
33+ * // Actually updates `boid.pos`
34+ * posRef.$ = d.vec3f(1, 2, 3);
35+ * console.log(boid.pos); // Output: vec3f(1, 2, 3)
36+ * ```
37+ */
3238 $ : T ;
3339}
3440
3541// TODO: Restrict calls to this function only from within TypeGPU functions
3642export const ref : DualFn < < T > ( value : T ) => ref < T > > = ( ( ) => {
3743 const gpuImpl = ( value : Snippet ) => {
38- if ( ! isEphemeralSnippet ( value ) ) {
39- throw new WgslTypeError (
40- `Can't create refs from references. Copy the value first by wrapping it in its schema.` ,
41- ) ;
42- }
4344 return snip ( new RefOnGPU ( value ) , UnknownData , /* origin */ 'runtime' ) ;
4445 } ;
4546
@@ -71,11 +72,11 @@ export const ref: DualFn<<T>(value: T) => ref<T>> = (() => {
7172// --------------
7273
7374class refImpl < T > implements ref < T > {
74- readonly #value: T | string ;
75+ #value: T ;
7576 readonly [ $internal ] : true ;
7677 readonly [ $isRef ] : true ;
7778
78- constructor ( value : T | string ) {
79+ constructor ( value : T ) {
7980 this . #value = value ;
8081 this [ $internal ] = true ;
8182 this [ $isRef ] = true ;
@@ -84,20 +85,49 @@ class refImpl<T> implements ref<T> {
8485 get $ ( ) : T {
8586 return this . #value as T ;
8687 }
88+
89+ set $ ( value : T ) {
90+ if ( value && typeof value === 'object' ) {
91+ // Setting an object means updating the properties of the original object.
92+ // e.g.: foo.$ = Boid();
93+ for ( const key of Object . keys ( value ) as ( keyof T ) [ ] ) {
94+ this . #value[ key ] = value [ key ] ;
95+ }
96+ } else {
97+ this . #value = value ;
98+ }
99+ }
87100}
88101
89102export class RefOnGPU {
90- readonly snippet : Snippet ;
91103 readonly [ $internal ] : true ;
92104
105+ readonly snippet : Snippet ;
106+ /**
107+ * Pointer params only exist if the ref was created from a reference (buttery-butter).
108+ */
109+ readonly ptrType : Ptr | undefined ;
110+
93111 constructor ( snippet : Snippet ) {
94- this . snippet = snippet ;
95112 this [ $internal ] = true ;
113+ this . snippet = snippet ;
114+ this . ptrType = createPtrFromOrigin (
115+ snippet . origin ,
116+ snippet . dataType as StorableData ,
117+ ) ;
96118 }
97119
98120 toString ( ) : string {
99121 return `ref:${ this . snippet . value } ` ;
100122 }
123+
124+ [ $resolve ] ( ctx : ResolutionCtx ) : ResolvedSnippet {
125+ invariant (
126+ ! ! this . ptrType ,
127+ 'RefOnGPU must have a pointer type when resolved' ,
128+ ) ;
129+ return snip ( stitch `(&${ this . snippet } )` , this . ptrType , this . snippet . origin ) ;
130+ }
101131}
102132
103133export class RefOperator implements SelfResolvable {
@@ -109,20 +139,18 @@ export class RefOperator implements SelfResolvable {
109139 this [ $internal ] = true ;
110140 this . snippet = snippet ;
111141
112- const ptrParams =
113- originToPtrParams [ this . snippet . origin as keyof OriginToPtrParams ] ;
142+ const ptrType = createPtrFromOrigin (
143+ snippet . origin ,
144+ snippet . dataType as StorableData ,
145+ ) ;
114146
115- if ( ! ptrParams ) {
147+ if ( ! ptrType ) {
116148 throw new Error (
117149 `Cannot take a reference of a value with origin ${ this . snippet . origin } ` ,
118150 ) ;
119151 }
120152
121- this . #ptrType = INTERNAL_createPtr (
122- ptrParams . space ,
123- this . snippet . dataType as StorableData ,
124- ptrParams . access ,
125- ) ;
153+ this . #ptrType = ptrType ;
126154 }
127155
128156 get [ $ownSnippet ] ( ) : Snippet {
0 commit comments