Skip to content

Commit b655bdf

Browse files
committed
uniformity example refactor
1 parent 88d51fd commit b655bdf

8 files changed

Lines changed: 80 additions & 132 deletions

File tree

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
import { Distribution, Generator } from './types.ts';
1+
import { Distribution } from './types.ts';
22

33
export const popupCooldown = 100000;
44
export const cameraPositionGeo = [0, 0, 0.5];
55
export const cameraPositionHist = [0, 0, 0.2];
66
export const initialCameraAngle = 15;
77
export const numSamplesOptions = [100, 1000, 2000, 5000, 10000, 50000];
88
export const initialNumSamples = numSamplesOptions[2];
9-
export const initialGenerator: Generator = Generator.BPETER;
10-
export const generators: Generator[] = Object.values(Generator);
119
export const initialDistribution: Distribution = Distribution.NORMAL;
1210
export const distributions: Distribution[] = Object.values(Distribution);

apps/typegpu-docs/src/examples/algorithms/probability/executor.ts

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { randf, randomGeneratorSlot, type StatefulGenerator } from '@typegpu/noise';
1+
import { randf } from '@typegpu/noise';
22
import type {
33
StorageFlag,
44
TgpuBindGroup,
@@ -26,8 +26,7 @@ export class Executor {
2626
number,
2727
[TgpuBuffer<d.WgslArray<d.Vec3f>> & StorageFlag, TgpuBuffer<d.WgslArray<d.F32>> & StorageFlag]
2828
>;
29-
// they can be WeakMaps, because we always have reference to distribution and PRNG
30-
readonly #pipelineCache: WeakMap<TgpuFn, WeakMap<StatefulGenerator, TgpuComputePipeline>>;
29+
readonly #pipelineCache: Map<TgpuFn, TgpuComputePipeline>;
3130

3231
constructor(root: TgpuRoot) {
3332
this.#root = root;
@@ -80,43 +79,21 @@ export class Executor {
8079
});
8180
}
8281

83-
#pipelineCacheSet(
84-
distribution: TgpuFn<() => d.Vec3f>,
85-
generator: StatefulGenerator,
86-
pipeline: TgpuComputePipeline,
87-
) {
88-
let distributionMap = this.#pipelineCache.get(distribution);
89-
if (!distributionMap) {
90-
distributionMap = new WeakMap();
91-
this.#pipelineCache.set(distribution, distributionMap);
92-
}
93-
94-
distributionMap.set(generator, pipeline);
95-
}
96-
97-
pipelineCacheGet(
98-
distribution: TgpuFn<() => d.Vec3f>,
99-
generator: StatefulGenerator,
100-
): TgpuComputePipeline {
101-
let pipeline = this.#pipelineCache.get(distribution)?.get(generator);
82+
getPipeline(distribution: TgpuFn<() => d.Vec3f>) {
83+
let pipeline = this.#pipelineCache.get(distribution);
10284
if (!pipeline) {
10385
pipeline = this.#root
104-
.with(randomGeneratorSlot, generator)
10586
.with(this.#distributionSlot, distribution)
10687
.createComputePipeline({ compute: this.#dataMoreWorkersFunc });
107-
this.#pipelineCacheSet(distribution, generator, pipeline);
88+
this.#pipelineCache.set(distribution, pipeline);
10889
}
109-
11090
return pipeline;
11191
}
11292

113-
async executeMoreWorkers(
114-
distribution: TgpuFn<() => d.Vec3f>,
115-
generator: StatefulGenerator,
116-
): Promise<d.v3f[]> {
117-
const pipeline = this.pipelineCacheGet(distribution, generator);
118-
119-
pipeline.with(this.#bindGroup).dispatchWorkgroups(Math.ceil(this.#count / 64));
93+
async executeMoreWorkers(distribution: TgpuFn<() => d.Vec3f>): Promise<d.v3f[]> {
94+
this.getPipeline(distribution)
95+
.with(this.#bindGroup)
96+
.dispatchWorkgroups(Math.ceil(this.#count / 64));
12097

12198
return await this.#samplesBuffer.read();
12299
}

apps/typegpu-docs/src/examples/algorithms/probability/helpers.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import { randf } from '@typegpu/noise';
12
import tgpu, { d } from 'typegpu';
2-
import { BPETER, LCG, randf, type StatefulGenerator } from '@typegpu/noise';
3-
import { Distribution, Generator, PlotType, type PRNG } from './types.ts';
3+
44
import * as c from './constants.ts';
5+
import { Distribution, PlotType, type PRNG } from './types.ts';
56

67
const normal = d.vec3f(1.41, 1.41, 0);
78
const z2D = 0.5;
@@ -94,10 +95,3 @@ const distributionCameras = {
9495
export function getCameraPosition(distribution: Distribution): number[] {
9596
return distributionCameras[distribution];
9697
}
97-
98-
const GENERATOR_MAP = {
99-
[Generator.BPETER]: BPETER,
100-
[Generator.LCG]: LCG,
101-
};
102-
103-
export const getGenerator = (gen: Generator): StatefulGenerator => GENERATOR_MAP[gen];

apps/typegpu-docs/src/examples/algorithms/probability/index.ts

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import tgpu from 'typegpu';
22

33
import { Plotter } from './plotter.ts';
44
import { Executor } from './executor.ts';
5-
import type { Distribution, Generator } from './types.ts';
5+
import type { Distribution } from './types.ts';
66
import * as c from './constants.ts';
7-
import { getCameraPosition, getGenerator, getPRNG } from './helpers.ts';
7+
import { getCameraPosition, getPRNG } from './helpers.ts';
88
import { defineControls } from '../../common/defineControls.ts';
99

1010
const root = await tgpu.init();
@@ -13,17 +13,11 @@ const executor = new Executor(root);
1313
const plotter = new Plotter();
1414

1515
let currentDistribution = c.initialDistribution;
16-
let currentGenerator = c.initialGenerator;
1716

18-
const replot = async (
19-
currentDistribution: Distribution,
20-
currentGenerator: Generator,
21-
animate = false,
22-
) => {
17+
const replot = async (currentDistribution: Distribution, animate = false) => {
2318
const prng = getPRNG(currentDistribution);
24-
const gen = getGenerator(currentGenerator);
2519

26-
const samples = await executor.executeMoreWorkers(prng.prng, gen);
20+
const samples = await executor.executeMoreWorkers(prng.prng);
2721
await plotter.plot(samples, prng, animate);
2822
};
2923

@@ -70,19 +64,7 @@ export const controls = defineControls({
7064
Reseed: {
7165
async onButtonClick() {
7266
executor.reseed();
73-
await replot(currentDistribution, currentGenerator, true);
74-
plotter.resetView(getCameraPosition(currentDistribution));
75-
},
76-
},
77-
Generator: {
78-
initial: c.initialGenerator,
79-
options: c.generators,
80-
onSelectChange: async (value: Generator) => {
81-
if (currentGenerator === value) {
82-
return;
83-
}
84-
currentGenerator = value;
85-
await replot(currentDistribution, currentGenerator, true);
67+
await replot(currentDistribution, true);
8668
plotter.resetView(getCameraPosition(currentDistribution));
8769
},
8870
},
@@ -95,7 +77,7 @@ export const controls = defineControls({
9577
}
9678

9779
currentDistribution = value;
98-
await replot(currentDistribution, currentGenerator, true);
80+
await replot(currentDistribution, true);
9981
plotter.resetView(getCameraPosition(currentDistribution));
10082
},
10183
},
@@ -104,26 +86,15 @@ export const controls = defineControls({
10486
options: c.numSamplesOptions,
10587
async onSelectChange(value) {
10688
executor.count = value;
107-
await replot(currentDistribution, currentGenerator);
89+
await replot(currentDistribution);
10890
},
10991
},
92+
// this is the only place where some niche distributions are tested
11093
'Test Resolution': import.meta.env.DEV && {
11194
onButtonClick() {
112-
for (const [i, j] of [
113-
[0, 0],
114-
[8, 1],
115-
[10, 0],
116-
]) {
117-
const code = tgpu.resolve({
118-
externals: {
119-
p: executor.pipelineCacheGet(
120-
getPRNG(c.distributions[i]).prng,
121-
getGenerator(c.generators[j]),
122-
),
123-
},
124-
});
125-
root.device.createShaderModule({ code });
126-
}
95+
c.distributions
96+
.map((dist) => tgpu.resolve([executor.getPipeline(getPRNG(dist).prng)]))
97+
.forEach((r) => root.device.createShaderModule({ code: r }));
12798
},
12899
},
129100
});
@@ -132,4 +103,5 @@ export function onCleanup() {
132103
root.destroy();
133104
plotter.destroy();
134105
}
106+
135107
// #endregion

apps/typegpu-docs/src/examples/algorithms/probability/types.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,3 @@ export interface SimplePRNG {
6969
plotType: PlotType;
7070
prng: TgpuFn<() => d.Vec3f>;
7171
}
72-
73-
export const Generator = {
74-
BPETER: 'bpeter (default)',
75-
LCG: 'lcg',
76-
} as const;
77-
78-
export type Generator = (typeof Generator)[keyof typeof Generator];
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { PRNG } from './prngs.ts';
1+
import type { PRNGKey } from './prngs.ts';
22

33
export const gridSizes = [8, 16, 32, 64, 128, 256, 512, 1024];
44
export const initialGridSize = gridSizes[4];
5-
export const initialPRNG = PRNG.BPETER;
6-
export const prngs: PRNG[] = Object.values(PRNG);
5+
export const initialPRNG: PRNGKey = 'bpeter';

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

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { randf, randomGeneratorSlot } from '@typegpu/noise';
22
import tgpu, { common, d, std, type TgpuRenderPipeline } from 'typegpu';
33

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

88
const root = await tgpu.init();
@@ -11,29 +11,43 @@ const canvas = document.querySelector('canvas') as HTMLCanvasElement;
1111
const context = root.configureContext({ canvas, alphaMode: 'premultiplied' });
1212
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
1313

14-
const gridSizeUniform = root.createUniform(d.f32, c.initialGridSize);
15-
const canvasRatioUniform = root.createUniform(d.f32, canvas.width / canvas.height);
14+
const Config = d.struct({
15+
gridSize: d.f32,
16+
canvasRatio: d.f32,
17+
useSeed2: d.u32,
18+
});
19+
20+
const configUniform = root.createUniform(Config, {
21+
gridSize: c.initialGridSize,
22+
canvasRatio: canvas.width / canvas.height,
23+
useSeed2: d.u32(prngs[initialPRNG].useSeed2),
24+
});
1625

1726
const fragmentShader = tgpu.fragmentFn({
1827
in: { uv: d.vec2f },
1928
out: d.vec4f,
2029
})((input) => {
2130
'use gpu';
22-
const uv = ((input.uv + 1) / 2) * d.vec2f(canvasRatioUniform.$, 1);
23-
const gridedUV = std.floor(uv * gridSizeUniform.$);
31+
const gridSize = configUniform.$.gridSize;
32+
const uv = input.uv * d.vec2f(configUniform.$.canvasRatio, 1);
33+
const gridedUV = std.floor(uv * gridSize);
2434

25-
randf.seed2(gridedUV);
35+
if (configUniform.$.useSeed2 === 1) {
36+
randf.seed2(gridedUV);
37+
} else {
38+
randf.seed(gridedUV.x * gridSize + gridedUV.y);
39+
}
2640

2741
return d.vec4f(d.vec3f(randf.sample()), 1);
2842
});
2943

30-
const pipelineCache = new Map<PRNG, TgpuRenderPipeline<d.Vec4f>>();
31-
let prng: PRNG = c.initialPRNG;
44+
const pipelineCache = new Map<PRNGKey, TgpuRenderPipeline<d.Vec4f>>();
45+
let prng: PRNGKey = initialPRNG;
3246

3347
const redraw = () => {
3448
let pipeline = pipelineCache.get(prng);
3549
if (!pipeline) {
36-
pipeline = root.with(randomGeneratorSlot, getPRNG(prng)).createRenderPipeline({
50+
pipeline = root.with(randomGeneratorSlot, prngs[prng].generator).createRenderPipeline({
3751
vertex: common.fullScreenTriangle,
3852
fragment: fragmentShader,
3953
targets: { format: presentationFormat },
@@ -47,44 +61,41 @@ const redraw = () => {
4761
// #region Example controls & Cleanup
4862
export const controls = defineControls({
4963
PRNG: {
50-
initial: c.initialPRNG,
51-
options: c.prngs,
64+
initial: initialPRNG,
65+
options: prngKeys,
5266
onSelectChange: (value) => {
5367
prng = value;
68+
configUniform.writePartial({ useSeed2: d.u32(prngs[value].useSeed2) });
5469
redraw();
5570
},
5671
},
5772
'Grid Size': {
5873
initial: c.initialGridSize,
5974
options: c.gridSizes,
6075
onSelectChange: (value) => {
61-
gridSizeUniform.write(value);
76+
configUniform.writePartial({ gridSize: value });
6277
redraw();
6378
},
6479
},
80+
// this is the only place where some niche prngs are tested
6581
'Test Resolution': import.meta.env.DEV && {
6682
onButtonClick: () => {
67-
const namespace = tgpu['~unstable'].namespace();
68-
c.prngs
69-
.map((prng) =>
70-
tgpu.resolve(
71-
[
72-
root.with(randomGeneratorSlot, getPRNG(prng)).createRenderPipeline({
73-
vertex: common.fullScreenTriangle,
74-
fragment: fragmentShader,
75-
targets: { format: presentationFormat },
76-
}),
77-
],
78-
{ names: namespace },
79-
),
83+
prngKeys
84+
.map((key) =>
85+
tgpu.resolve([
86+
root.with(randomGeneratorSlot, prngs[key].generator).createRenderPipeline({
87+
vertex: common.fullScreenTriangle,
88+
fragment: fragmentShader,
89+
}),
90+
]),
8091
)
81-
.map((r) => root.device.createShaderModule({ code: r }));
92+
.forEach((r) => root.device.createShaderModule({ code: r }));
8293
},
8394
},
8495
});
8596

8697
const resizeObserver = new ResizeObserver(() => {
87-
canvasRatioUniform.write(canvas.width / canvas.height);
98+
configUniform.writePartial({ canvasRatio: canvas.width / canvas.height });
8899
redraw();
89100
});
90101
resizeObserver.observe(canvas);
Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
import { BPETER, LCG, type StatefulGenerator } from '@typegpu/noise';
1+
import { BPETER, LCG32, XOROSHIRO64STARSTAR, type StatefulGenerator } from '@typegpu/noise';
22

3-
export const PRNG = {
4-
BPETER: 'bpeter (default)',
5-
LCG: 'lcg',
6-
} as const;
3+
interface PRNGOptions {
4+
name: string;
5+
useSeed2: boolean;
6+
generator: StatefulGenerator;
7+
}
78

8-
export type PRNG = (typeof PRNG)[keyof typeof PRNG];
9+
export const prngs = {
10+
bpeter: { name: 'bpeter (default)', useSeed2: true, generator: BPETER },
11+
lcg32: { name: 'lcg32', useSeed2: false, generator: LCG32 },
12+
xoroshiro64: { name: 'xoroshiro64', useSeed2: true, generator: XOROSHIRO64STARSTAR },
13+
} as const satisfies Record<string, PRNGOptions>;
914

10-
const PRNG_MAP = {
11-
[PRNG.BPETER]: BPETER,
12-
[PRNG.LCG]: LCG,
13-
};
15+
export type PRNGKey = keyof typeof prngs;
1416

15-
export const getPRNG = (prng: PRNG): StatefulGenerator => PRNG_MAP[prng];
17+
export const prngKeys = Object.keys(prngs) as PRNGKey[];
18+
19+
export const initialPRNG: PRNGKey = 'bpeter';

0 commit comments

Comments
 (0)