Skip to content

Commit e8804de

Browse files
authored
fix: Disallow missing varyings in shell-less fragment input (#2165)
1 parent f5112a9 commit e8804de

5 files changed

Lines changed: 55 additions & 66 deletions

File tree

apps/typegpu-docs/src/examples/rendering/perlin-noise/index.ts

Lines changed: 32 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
import tgpu, { common, d } from 'typegpu';
21
import { perlin3d } from '@typegpu/noise';
2+
import tgpu, { common, d } from 'typegpu';
33
import { abs, mix, mul, pow, sign, tanh } from 'typegpu/std';
44
import { defineControls } from '../../common/defineControls.ts';
55

66
/** The depth of the perlin noise (in time), after which the pattern loops around */
77
const DEPTH = 10;
88

9-
const gridSizeAccess = tgpu['~unstable'].accessor(d.f32);
10-
const timeAccess = tgpu['~unstable'].accessor(d.f32);
11-
const sharpnessAccess = tgpu['~unstable'].accessor(d.f32);
12-
139
const exponentialSharpen = (n: number, sharpness: number): number => {
1410
'use gpu';
1511
return sign(n) * pow(abs(n), 1 - sharpness);
@@ -20,30 +16,6 @@ const tanhSharpen = (n: number, sharpness: number): number => {
2016
return tanh(n * (1 + sharpness * 10));
2117
};
2218

23-
const sharpenFnSlot = tgpu.slot<(n: number, sharpness: number) => number>(
24-
exponentialSharpen,
25-
);
26-
27-
const mainFragment = tgpu['~unstable'].fragmentFn({
28-
in: { uv: d.vec2f },
29-
out: d.vec4f,
30-
})((input) => {
31-
const uv = mul(gridSizeAccess.$, input.uv);
32-
33-
const n = perlin3d.sample(d.vec3f(uv, timeAccess.$));
34-
35-
// Apply sharpening function
36-
const sharp = sharpenFnSlot.$(n, sharpnessAccess.$);
37-
38-
// Map to 0-1 range
39-
const n01 = sharp * 0.5 + 0.5;
40-
41-
// Gradient map
42-
const dark = d.vec3f(0, 0.2, 1);
43-
const light = d.vec3f(1, 0.3, 0.5);
44-
return d.vec4f(mix(dark, light, n01), 1);
45-
});
46-
4719
// Configuring a dynamic (meaning it's size can change) cache
4820
// for perlin noise gradients.
4921
const perlinCacheConfig = perlin3d.dynamicCacheConfig();
@@ -53,38 +25,46 @@ const dynamicLayout = tgpu.bindGroupLayout({ ...perlinCacheConfig.layout });
5325

5426
const root = await tgpu.init();
5527

28+
const gridSize = root.createUniform(d.f32);
29+
const time = root.createUniform(d.f32, 0);
30+
const sharpness = root.createUniform(d.f32, 0.1);
31+
5632
// Instantiating the cache with an initial size.
5733
const perlinCache = perlinCacheConfig.instance(root, d.vec3u(4, 4, DEPTH));
5834

5935
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
6036
const context = root.configureContext({ canvas, alphaMode: 'premultiplied' });
6137
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
6238

63-
const gridSize = root.createUniform(d.f32);
64-
const time = root.createUniform(d.f32, 0);
65-
const sharpness = root.createUniform(d.f32, 0.1);
66-
67-
const renderPipelineBase = root['~unstable']
68-
.with(gridSizeAccess, gridSize)
69-
.with(timeAccess, time)
70-
.with(sharpnessAccess, sharpness)
71-
.pipe(perlinCacheConfig.inject(dynamicLayout.$));
72-
73-
const renderPipelines = {
74-
exponential: renderPipelineBase
75-
.with(sharpenFnSlot, exponentialSharpen)
76-
.createRenderPipeline({
77-
vertex: common.fullScreenTriangle,
78-
fragment: mainFragment,
79-
targets: { format: presentationFormat },
80-
}),
81-
tanh: renderPipelineBase
82-
.with(sharpenFnSlot, tanhSharpen)
39+
const createRenderPipeline = (
40+
sharpenFn: (n: number, sharpness: number) => number,
41+
) =>
42+
root['~unstable']
43+
.pipe(perlinCacheConfig.inject(dynamicLayout.$))
8344
.createRenderPipeline({
8445
vertex: common.fullScreenTriangle,
85-
fragment: mainFragment,
46+
fragment: ({ uv }) => {
47+
'use gpu';
48+
const suv = mul(gridSize.$, uv);
49+
const n = perlin3d.sample(d.vec3f(suv, time.$));
50+
51+
// Apply sharpening function
52+
const sharp = sharpenFn(n, sharpness.$);
53+
54+
// Map to 0-1 range
55+
const n01 = sharp * 0.5 + 0.5;
56+
57+
// Gradient map
58+
const dark = d.vec3f(0, 0.2, 1);
59+
const light = d.vec3f(1, 0.3, 0.5);
60+
return d.vec4f(mix(dark, light, n01), 1);
61+
},
8662
targets: { format: presentationFormat },
87-
}),
63+
});
64+
65+
const renderPipelines = {
66+
exponential: createRenderPipeline(exponentialSharpen),
67+
tanh: createRenderPipeline(tanhSharpen),
8868
};
8969

9070
let activeSharpenFn: 'exponential' | 'tanh' = 'exponential';
@@ -118,7 +98,7 @@ export const controls = defineControls({
11898
initial: '4',
11999
options: [1, 2, 4, 8, 16, 32, 64, 128, 256].map((x) => x.toString()),
120100
onSelectChange: (value: string) => {
121-
const iSize = Number.parseInt(value);
101+
const iSize = Number.parseInt(value, 10);
122102
perlinCache.size = d.vec3u(iSize, iSize, DEPTH);
123103
gridSize.write(iSize);
124104
bindGroup = root.createBindGroup(dynamicLayout, perlinCache.bindings);

packages/typegpu/src/core/function/tgpuFragmentFn.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ export type FragmentInConstrained = IORecord<
4848
| AnyFragmentInputBuiltin
4949
>;
5050

51-
export type FragmentInFromVertexOut<T> =
52-
& OmitBuiltins<{ [K in keyof T]: InstanceToSchema<T[K]> }>
53-
& Record<string, AnyFragmentInputBuiltin>;
51+
export type VertexOutToVarying<T> = OmitBuiltins<
52+
{ [K in keyof T]: InstanceToSchema<T[K]> }
53+
>;
5454

5555
type FragmentColorValue = Vec4f | Vec4i | Vec4u;
5656

packages/typegpu/src/core/root/rootTypes.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import type { AnyComputeBuiltin, OmitBuiltins } from '../../builtin.ts';
1+
import type {
2+
AnyComputeBuiltin,
3+
AnyFragmentInputBuiltin,
4+
OmitBuiltins,
5+
} from '../../builtin.ts';
26
import type { TgpuQuerySet } from '../../core/querySet/querySet.ts';
37
import type {
48
AnyData,
@@ -56,9 +60,9 @@ import type {
5660
import type { IORecord } from '../function/fnTypes.ts';
5761
import type {
5862
FragmentInConstrained,
59-
FragmentInFromVertexOut,
6063
FragmentOutConstrained,
6164
TgpuFragmentFn,
65+
VertexOutToVarying,
6266
} from '../function/tgpuFragmentFn.ts';
6367
import type { TgpuVertexFn } from '../function/tgpuVertexFn.ts';
6468
import type { TgpuComputePipeline } from '../pipeline/computePipeline.ts';
@@ -300,13 +304,14 @@ export interface WithBinding extends Withable<WithBinding> {
300304
) => AutoVertexOut<Assume<TVertexOut, AnyAutoCustoms>>);
301305
fragment:
302306
| TgpuFragmentFn<
303-
FragmentInFromVertexOut<TVertexOut>,
307+
& VertexOutToVarying<TVertexOut>
308+
& Record<string, AnyFragmentInputBuiltin>,
304309
Assume<TFragmentOut, TgpuFragmentFn.Out>
305310
>
306311
| ((
307312
input: AutoFragmentIn<
308313
Assume<
309-
InferGPURecord<FragmentInFromVertexOut<TVertexOut>>,
314+
InferGPURecord<VertexOutToVarying<TVertexOut>>,
310315
AnyAutoCustoms
311316
>
312317
>,
@@ -339,7 +344,8 @@ export interface WithBinding extends Withable<WithBinding> {
339344
fragment?:
340345
| undefined
341346
| TgpuFragmentFn<
342-
FragmentInFromVertexOut<OmitBuiltins<TVertexOut>>,
347+
& VertexOutToVarying<OmitBuiltins<TVertexOut>>
348+
& Record<string, AnyFragmentInputBuiltin>,
343349
Record<string, never> | Void
344350
>
345351
| ((
@@ -387,7 +393,8 @@ export interface WithBinding extends Withable<WithBinding> {
387393
>,
388394
) => AutoFragmentOut<Assume<TFragmentOut, AnyAutoCustoms | v4f>>)
389395
| TgpuFragmentFn<
390-
FragmentInFromVertexOut<OmitBuiltins<TVertexOut>>,
396+
& VertexOutToVarying<OmitBuiltins<TVertexOut>>
397+
& Record<string, AnyFragmentInputBuiltin>,
391398
Assume<TFragmentOut, TgpuFragmentFn.Out>
392399
>;
393400
targets: FragmentOutToTargets<NoInfer<TFragmentOut>>;
@@ -407,7 +414,8 @@ export interface WithBinding extends Withable<WithBinding> {
407414
fragment?:
408415
| undefined
409416
| TgpuFragmentFn<
410-
FragmentInFromVertexOut<OmitBuiltins<TVertexOut>>,
417+
& VertexOutToVarying<OmitBuiltins<TVertexOut>>
418+
& Record<string, AnyFragmentInputBuiltin>,
411419
Record<string, never>
412420
>
413421
| ((

packages/typegpu/tests/examples/individual/perlin-noise.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,13 @@ describe('perlin noise example', () => {
141141
142142
@group(0) @binding(2) var<uniform> sharpness: f32;
143143
144-
struct mainFragment_Input {
144+
struct FragmentIn {
145145
@location(0) uv: vec2f,
146146
}
147147
148-
@fragment fn mainFragment(input: mainFragment_Input) -> @location(0) vec4f {
149-
var uv = (gridSize * input.uv);
150-
let n = sample(vec3f(uv, time));
148+
@fragment fn fragment(_arg_0: FragmentIn) -> @location(0) vec4f {
149+
var suv = (gridSize * _arg_0.uv);
150+
let n = sample(vec3f(suv, time));
151151
let sharp = exponentialSharpen(n, sharpness);
152152
let n01 = ((sharp * 0.5f) + 0.5f);
153153
var dark = vec3f(0, 0.20000000298023224, 1);

packages/typegpu/tests/renderPipeline.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,7 @@ describe('root.createRenderPipeline', () => {
13141314
'use gpu';
13151315
return { $position: d.vec4f() };
13161316
},
1317+
// @ts-expect-error: The prop is not in the object
13171318
fragment: ({ prop }) => {
13181319
'use gpu';
13191320
const a = prop;

0 commit comments

Comments
 (0)