Skip to content

Commit 685479d

Browse files
committed
More tests and restrictions
1 parent 37914d5 commit 685479d

File tree

7 files changed

+62
-25
lines changed

7 files changed

+62
-25
lines changed

packages/typegpu/src/core/variable/tgpuVariable.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import type { InferGPU } from '../../shared/repr.ts';
1010
import {
1111
$gpuValueOf,
1212
$internal,
13-
$isRef,
1413
$ownSnippet,
1514
$resolve,
1615
} from '../../shared/symbols.ts';
@@ -78,7 +77,6 @@ export function isVariable<T extends TgpuVar>(
7877
class TgpuVarImpl<TScope extends VariableScope, TDataType extends AnyData>
7978
implements TgpuVar<TScope, TDataType>, SelfResolvable {
8079
readonly [$internal] = {};
81-
readonly [$isRef]: true;
8280
readonly #scope: TScope;
8381
readonly #dataType: TDataType;
8482
readonly #initialValue: InferGPU<TDataType> | undefined;
@@ -88,7 +86,6 @@ class TgpuVarImpl<TScope extends VariableScope, TDataType extends AnyData>
8886
dataType: TDataType,
8987
initialValue?: InferGPU<TDataType> | undefined,
9088
) {
91-
this[$isRef] = true;
9289
this.#scope = scope;
9390
this.#dataType = dataType;
9491
this.#initialValue = initialValue;

packages/typegpu/src/data/ref.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { stitch } from '../core/resolve/stitch.ts';
22
import { invariant } from '../errors.ts';
33
import { inCodegenMode } from '../execMode.ts';
44
import { setName } from '../shared/meta.ts';
5-
import { $internal, $isRef, $ownSnippet, $resolve } from '../shared/symbols.ts';
5+
import { $internal, $ownSnippet, $resolve } from '../shared/symbols.ts';
66
import type { ResolutionCtx, SelfResolvable } from '../types.ts';
77
import { UnknownData } from './dataTypes.ts';
88
import type { DualFn } from './dualFn.ts';
@@ -19,9 +19,15 @@ import {
1919
// Public API
2020
// ----------
2121

22+
/**
23+
* A reference to a value `T`. Can be passed to other functions to give them
24+
* mutable access to the underlying value.
25+
*
26+
* Conceptually, it represents a WGSL pointer.
27+
*/
2228
export interface ref<T> {
2329
readonly [$internal]: unknown;
24-
readonly [$isRef]: true;
30+
readonly type: 'ref';
2531

2632
/**
2733
* Derefences the reference, and gives access to the underlying value.
@@ -38,7 +44,6 @@ export interface ref<T> {
3844
$: T;
3945
}
4046

41-
// TODO: Restrict calls to this function only from within TypeGPU functions
4247
export const ref: DualFn<<T>(value: T) => ref<T>> = (() => {
4348
const gpuImpl = (value: Snippet) => {
4449
/**
@@ -84,19 +89,23 @@ export const ref: DualFn<<T>(value: T) => ref<T>> = (() => {
8489
return impl as unknown as DualFn<<T>(value: T) => ref<T>>;
8590
})();
8691

92+
export function isRef<T>(value: unknown | ref<T>): value is ref<T> {
93+
return value instanceof refImpl;
94+
}
95+
8796
// --------------
8897
// Implementation
8998
// --------------
9099

91100
class refImpl<T> implements ref<T> {
92-
#value: T;
93101
readonly [$internal]: true;
94-
readonly [$isRef]: true;
102+
readonly type: 'ref';
103+
#value: T;
95104

96105
constructor(value: T) {
97-
this.#value = value;
98106
this[$internal] = true;
99-
this[$isRef] = true;
107+
this.type = 'ref';
108+
this.#value = value;
100109
}
101110

102111
get $(): T {

packages/typegpu/src/shared/symbols.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,6 @@ export const $invalidSchemaReason = Symbol(
6969
`typegpu:${version}:$invalidSchemaReason`,
7070
);
7171

72-
/**
73-
* A symbol that identifies objects that act as references
74-
* (values returned from d.ref(), buffer usages, ...)
75-
*/
76-
export const $isRef = Symbol(`typegpu:${version}:$isRef`);
77-
78-
export function isRef(value: unknown): value is { [$isRef]: true } {
79-
return !!(value as { [$isRef]: true })?.[$isRef];
80-
}
81-
8272
export function isMarkedInternal(
8373
value: unknown,
8474
): value is { [$internal]: Record<string, unknown> | true } {

packages/typegpu/src/std/array.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ import { dualImpl } from '../core/function/dualImpl.ts';
22
import { stitch } from '../core/resolve/stitch.ts';
33
import { abstractInt, u32 } from '../data/numeric.ts';
44
import { ptrFn } from '../data/ptr.ts';
5-
import type { ref } from '../data/ref.ts';
5+
import { isRef, type ref } from '../data/ref.ts';
66
import { isPtr, isWgslArray, type StorableData } from '../data/wgslTypes.ts';
7-
import { isRef } from '../shared/symbols.ts';
87

98
const sizeOfPointedToArray = (dataType: unknown) =>
109
isPtr(dataType) && isWgslArray(dataType.inner)

packages/typegpu/src/tgsl/generationHelpers.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ import type { ShelllessRepository } from './shellless.ts';
6262
import { add, div, mul, sub } from '../std/operators.ts';
6363
import { $internal } from '../shared/symbols.ts';
6464
import { stitch } from '../core/resolve/stitch.ts';
65-
import { derefSnippet } from '../data/ref.ts';
65+
import { derefSnippet, isRef } from '../data/ref.ts';
6666

6767
type SwizzleableType = 'f' | 'h' | 'i' | 'u' | 'b';
6868
type SwizzleLength = 1 | 2 | 3 | 4;
@@ -428,6 +428,10 @@ export function coerceToSnippet(value: unknown): Snippet {
428428
return value;
429429
}
430430

431+
if (isRef(value)) {
432+
throw new Error('Cannot use refs (d.ref(...)) from the outer scope.');
433+
}
434+
431435
// Maybe the value can tell us what snippet it is
432436
const ownSnippet = getOwnSnippet(value);
433437
if (ownSnippet) {

packages/typegpu/tests/ref.test.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,20 @@ import { it } from './utils/extendedIt.ts';
44
import { asWgsl } from './utils/parseResolved.ts';
55

66
describe('ref', () => {
7-
it.skip('fails when created outside of a TypeGPU function', () => {
8-
expect(() => d.ref(0)).toThrowErrorMatchingInlineSnapshot();
7+
it('fails when using a ref as an external', () => {
8+
const sup = d.ref(0);
9+
10+
const foo = () => {
11+
'use gpu';
12+
sup.$ += 1;
13+
};
14+
15+
expect(() => asWgsl(foo)).toThrowErrorMatchingInlineSnapshot(`
16+
[Error: Resolution of the following tree failed:
17+
- <root>
18+
- fn*:foo
19+
- fn*:foo(): Cannot use refs (d.ref(...)) from the outer scope.]
20+
`);
921
});
1022

1123
it('creates a regular looking variable in WGSL', () => {

packages/typegpu/tests/tgsl/shellless.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,32 @@ describe('shellless', () => {
186186
`);
187187
});
188188

189+
it('generates private pointer params when passing a private variable ref to a function', ({ root }) => {
190+
const foo = tgpu.privateVar(d.vec3f);
191+
192+
const sumComponents = (vec: d.ref<d.v3f>) => {
193+
'use gpu';
194+
return vec.$.x + vec.$.y + vec.$.z;
195+
};
196+
197+
const main = () => {
198+
'use gpu';
199+
sumComponents(d.ref(foo.$));
200+
};
201+
202+
expect(asWgsl(main)).toMatchInlineSnapshot(`
203+
"fn sumComponents(vec: ptr<private, vec3f>) -> f32 {
204+
return (((*vec).x + (*vec).y) + (*vec).z);
205+
}
206+
207+
var<private> foo: vec3f;
208+
209+
fn main() {
210+
sumComponents((&foo));
211+
}"
212+
`);
213+
});
214+
189215
it('generates uniform pointer params when passing a fixed uniform ref to a function', ({ root }) => {
190216
const posUniform = root.createUniform(d.vec3f);
191217

0 commit comments

Comments
 (0)