Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 27 additions & 6 deletions packages/typegpu/src/core/slot/accessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type AnyData, isData } from '../../data/dataTypes.ts';
import { schemaCallWrapper } from '../../data/schemaCallWrapper.ts';
import { isSnippet, type ResolvedSnippet, snip } from '../../data/snippet.ts';
import type { BaseData } from '../../data/wgslTypes.ts';
import { getResolutionCtx } from '../../execMode.ts';
import { getExecMode, getResolutionCtx } from '../../execMode.ts';
import { getName, hasTinyestMetadata, setName } from '../../shared/meta.ts';
import type { InferGPU } from '../../shared/repr.ts';
import {
Expand All @@ -14,6 +14,7 @@ import {
} from '../../shared/symbols.ts';
import type { UnwrapRuntimeConstructor } from '../../tgpuBindGroupLayout.ts';
import {
CodegenState,
getOwnSnippet,
NormalState,
type ResolutionCtx,
Expand Down Expand Up @@ -174,13 +175,33 @@ export class TgpuAccessorImpl<T extends BaseData>
}

get $(): InferGPU<T> {
if (getResolutionCtx()) {
return this[$gpuValueOf];
const ctx = getResolutionCtx();
if (!ctx) {
throw new Error(
'`tgpu.accessor` relies on GPU resources and cannot be accessed outside of a compute dispatch or draw call',
);
}

throw new Error(
'`tgpu.accessor` relies on GPU resources and cannot be accessed outside of a compute dispatch or draw call',
);
if (getExecMode().type !== 'codegen') {
const slotValue = ctx.unwrap(this.slot);

if (
typeof slotValue !== 'function' &&
!hasTinyestMetadata(slotValue) &&
!isTgpuFn(slotValue)
) {
return slotValue as unknown as InferGPU<T>;
}

ctx.pushMode(new CodegenState());
try {
return this[$gpuValueOf];
} finally {
ctx.popMode('codegen');
}
}

return this[$gpuValueOf];
}
}

Expand Down
82 changes: 79 additions & 3 deletions packages/typegpu/tests/tgsl/comptime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ describe('comptime', () => {
`);
});

it('can read and work with accessors in comptime', () => {
it('can read and work with accessors', () => {
const valueAccess = tgpu.accessor(d.f32, 1);
const doubleValue = tgpu.comptime(() => valueAccess.$ * 2);

Expand All @@ -113,17 +113,93 @@ describe('comptime', () => {

expect(tgpu.resolve([myFn])).toMatchInlineSnapshot(`
"fn myFn() -> f32 {
return NaNf;
return 2f;
}"
`);

expect(tgpu.resolve([myFn.with(valueAccess, 2)])).toMatchInlineSnapshot(`
"fn myFn() -> f32 {
return NaNf;
return 4f;
}"
`);
});

it('can read "use gpu" callback accessors', () => {
const colorAccess = tgpu.accessor(d.vec3f, () => {
'use gpu';
return d.vec3f(0, 1, 0);
});
const readColor = tgpu.comptime(() => colorAccess.$);

const myFn = tgpu.fn(
[],
d.vec3f,
)(() => {
return readColor();
});

expect(tgpu.resolve([myFn])).toMatchInlineSnapshot(`
"fn colorAccess() -> vec3f {
return vec3f(0, 1, 0);
}

fn myFn() -> vec3f {
return colorAccess();
}"
`);
});

it('can read GPU-resource accessors', ({ root }) => {
const Camera = d.struct({ pos: d.vec3f });
const camera = root.createUniform(Camera);

const posAccess = tgpu.accessor(d.vec3f, () => camera.$.pos);
const readPos = tgpu.comptime(() => posAccess.$);

const myFn = tgpu.fn(
[],
d.vec3f,
)(() => {
return readPos();
});

expect(tgpu.resolve([myFn])).toMatchInlineSnapshot(`
"struct Camera {
pos: vec3f,
}

@group(0) @binding(0) var<uniform> camera: Camera;

fn myFn() -> vec3f {
return camera.pos;
}"
`);
});

it('throws when reading "use gpu" callback accessor in js', () => {
const colorAccess = tgpu.accessor(d.vec3f, () => {
'use gpu';
return d.vec3f(0, 1, 0);
});
const readColor = tgpu.comptime(() => colorAccess.$);

expect(() => readColor()).toThrowErrorMatchingInlineSnapshot(
`[Error: \`tgpu.accessor\` relies on GPU resources and cannot be accessed outside of a compute dispatch or draw call]`,
);
});

it('throws when reading GPU-resource accessor in js', ({ root }) => {
const Camera = d.struct({ pos: d.vec3f });
const camera = root.createUniform(Camera);

const posAccess = tgpu.accessor(d.vec3f, () => camera.$.pos);
const readPos = tgpu.comptime(() => posAccess.$);

expect(() => readPos()).toThrowErrorMatchingInlineSnapshot(
`[Error: \`tgpu.accessor\` relies on GPU resources and cannot be accessed outside of a compute dispatch or draw call]`,
);
});

it('throws when a comptime-read accessor has no value', () => {
const value = tgpu.accessor(d.f32);
const readValue = tgpu.comptime(() => value.$);
Expand Down
Loading