Skip to content

Commit ba7ff1e

Browse files
committed
benchmarking prngs
1 parent eb55b8e commit ba7ff1e

7 files changed

Lines changed: 273 additions & 115 deletions

File tree

apps/typegpu-docs/src/examples/tests/uniformity/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ import type { PRNGKey } from './prngs.ts';
33
export const gridSizes = [8, 16, 32, 64, 128, 256, 512, 1024];
44
export const initialGridSize = gridSizes[4];
55
export const initialPRNG: PRNGKey = 'bpeter';
6+
export const samplesPerThread = [1, 8, 16, 64, 256, 1024, 131072, 262144];
7+
export const initialSamplesPerThread = samplesPerThread[0];
8+
export const initialTakeAverage = false;

apps/typegpu-docs/src/examples/tests/uniformity/index.ts

Lines changed: 96 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { randf, randomGeneratorSlot } from '@typegpu/noise';
2-
import tgpu, { common, d, std, type TgpuRenderPipeline } from 'typegpu';
2+
import tgpu, { common, d, std, type TgpuGuardedComputePipeline } from 'typegpu';
33

44
import * as c from './constants.ts';
55
import { initialPRNG, prngKeys, prngs, type PRNGKey } from './prngs.ts';
66
import { defineControls } from '../../common/defineControls.ts';
77

8-
const root = await tgpu.init();
8+
const root = await tgpu.init({ device: { requiredFeatures: ['timestamp-query'] } });
99

1010
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
1111
const context = root.configureContext({ canvas, alphaMode: 'premultiplied' });
@@ -15,47 +15,102 @@ const Config = d.struct({
1515
gridSize: d.f32,
1616
canvasRatio: d.f32,
1717
useSeed2: d.u32,
18+
samplesPerThread: d.u32,
19+
takeAverage: d.u32,
1820
});
1921

2022
const configUniform = root.createUniform(Config, {
2123
gridSize: c.initialGridSize,
2224
canvasRatio: canvas.width / canvas.height,
2325
useSeed2: d.u32(prngs[initialPRNG].useSeed2),
26+
samplesPerThread: c.initialSamplesPerThread,
27+
takeAverage: d.u32(c.initialTakeAverage),
2428
});
2529

26-
const fragmentShader = tgpu.fragmentFn({
27-
in: { uv: d.vec2f },
28-
out: d.vec4f,
29-
})((input) => {
30+
const layouts = {
31+
compute: tgpu.bindGroupLayout({
32+
texture: { storageTexture: d.textureStorage2d('r32float', 'write-only') },
33+
}),
34+
display: tgpu.bindGroupLayout({
35+
texture: { storageTexture: d.textureStorage2d('r32float', 'read-only') },
36+
}),
37+
};
38+
39+
const bindGroups = Object.fromEntries(
40+
c.gridSizes.map((size) => {
41+
const texture = root['~unstable']
42+
.createTexture({ size: [size, size], format: 'r32float' })
43+
.$usage('storage', 'sampled');
44+
return [
45+
size,
46+
{
47+
compute: root.createBindGroup(layouts.compute, { texture }),
48+
display: root.createBindGroup(layouts.display, { texture }),
49+
},
50+
];
51+
}),
52+
);
53+
54+
const displayPipeline = root.createRenderPipeline({
55+
vertex: common.fullScreenTriangle,
56+
fragment: ({ uv }) => {
57+
'use gpu';
58+
const adjustedUv = uv * d.vec2f(configUniform.$.canvasRatio, 1);
59+
const gridSize = configUniform.$.gridSize;
60+
const coords = d.vec2u(std.floor(adjustedUv * gridSize));
61+
const value = std.textureLoad(layouts.display.$.texture, coords).r;
62+
return d.vec4f(d.vec3f(value), 1);
63+
},
64+
targets: { format: presentationFormat },
65+
});
66+
67+
const computeFn = (x: number, y: number) => {
3068
'use gpu';
3169
const gridSize = configUniform.$.gridSize;
32-
const uv = input.uv * d.vec2f(configUniform.$.canvasRatio, 1);
33-
const gridedUV = std.floor(uv * gridSize);
3470

3571
if (configUniform.$.useSeed2 === 1) {
36-
randf.seed2(gridedUV);
72+
randf.seed2(d.vec2f(x, y));
3773
} else {
38-
randf.seed(gridedUV.x * gridSize + gridedUV.y);
74+
randf.seed(d.f32(x) * gridSize + d.f32(y));
3975
}
4076

41-
return d.vec4f(d.vec3f(randf.sample()), 1);
42-
});
77+
let i = d.u32(0);
78+
const samplesPerThread = configUniform.$.samplesPerThread;
79+
let samples = d.f32(0);
80+
while (i < samplesPerThread - 1) {
81+
samples += randf.sample();
82+
i += 1;
83+
}
4384

44-
const pipelineCache = new Map<PRNGKey, TgpuRenderPipeline<d.Vec4f>>();
45-
let prng: PRNGKey = initialPRNG;
85+
let result = randf.sample();
86+
if (configUniform.$.takeAverage === 1) {
87+
result = (result + samples) / samplesPerThread;
88+
}
4689

47-
const redraw = () => {
48-
let pipeline = pipelineCache.get(prng);
90+
std.textureStore(layouts.compute.$.texture, d.vec2u(x, y), d.vec4f(result, 0, 0, 0));
91+
};
92+
93+
const computePipelineCache = new Map<PRNGKey, TgpuGuardedComputePipeline<[number, number]>>();
94+
const getComputePipeline = (key: PRNGKey) => {
95+
let pipeline = computePipelineCache.get(key);
4996
if (!pipeline) {
50-
pipeline = root.with(randomGeneratorSlot, prngs[prng].generator).createRenderPipeline({
51-
vertex: common.fullScreenTriangle,
52-
fragment: fragmentShader,
53-
targets: { format: presentationFormat },
54-
});
55-
pipelineCache.set(prng, pipeline);
97+
pipeline = root
98+
.with(randomGeneratorSlot, prngs[key].generator)
99+
.createGuardedComputePipeline(computeFn)
100+
.withPerformanceCallback((start, end) => {
101+
console.log(`[${key}] - ${Number(end - start) / 1000} ms.`);
102+
});
103+
computePipelineCache.set(key, pipeline);
56104
}
105+
return pipeline;
106+
};
107+
108+
let prng = initialPRNG;
109+
let gridSize = c.initialGridSize;
57110

58-
pipeline.withColorAttachment({ view: context }).draw(3);
111+
const redraw = () => {
112+
getComputePipeline(prng).with(bindGroups[gridSize].compute).dispatchThreads(gridSize, gridSize);
113+
displayPipeline.withColorAttachment({ view: context }).with(bindGroups[gridSize].display).draw(3);
59114
};
60115

61116
// #region Example controls & Cleanup
@@ -69,26 +124,35 @@ export const controls = defineControls({
69124
redraw();
70125
},
71126
},
127+
'Samples per thread': {
128+
initial: c.initialSamplesPerThread,
129+
options: c.samplesPerThread,
130+
onSelectChange: (value) => {
131+
configUniform.writePartial({ samplesPerThread: value });
132+
redraw();
133+
},
134+
},
135+
'Take Average': {
136+
initial: c.initialTakeAverage,
137+
onToggleChange: (value) => {
138+
configUniform.writePartial({ takeAverage: d.u32(value) });
139+
redraw();
140+
},
141+
},
72142
'Grid Size': {
73143
initial: c.initialGridSize,
74144
options: c.gridSizes,
75145
onSelectChange: (value) => {
76-
configUniform.writePartial({ gridSize: value });
146+
gridSize = value;
147+
configUniform.writePartial({ gridSize });
77148
redraw();
78149
},
79150
},
80151
// this is the only place where some niche prngs are tested
81152
'Test Resolution': import.meta.env.DEV && {
82153
onButtonClick: () => {
83154
prngKeys
84-
.map((key) =>
85-
tgpu.resolve([
86-
root.with(randomGeneratorSlot, prngs[key].generator).createRenderPipeline({
87-
vertex: common.fullScreenTriangle,
88-
fragment: fragmentShader,
89-
}),
90-
]),
91-
)
155+
.map((key) => tgpu.resolve([getComputePipeline(key).pipeline]))
92156
.forEach((r) => root.device.createShaderModule({ code: r }));
93157
},
94158
},

apps/typegpu-docs/tests/individual-example-tests/probability.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe('probability distribution plot example', () => {
1515
category: 'algorithms',
1616
name: 'probability',
1717
controlTriggers: ['Test Resolution'],
18-
expectedCalls: 3,
18+
expectedCalls: 13,
1919
},
2020
device,
2121
);

0 commit comments

Comments
 (0)