Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Distribution } from './types.ts';
import { Distribution, Generator } from './types.ts';

export const popupCooldown = 100000;
export const cameraPositionGeo = [0, 0, 0.5];
export const cameraPositionHist = [0, 0, 0.2];
export const initialCameraAngle = 15;
export const numSamplesOptions = [100, 1000, 2000, 5000, 10000, 50000];
export const initialNumSamples = numSamplesOptions[2];
export const initialGenerator: Generator = Generator.BPETER;
export const generators: Generator[] = Object.values(Generator);
export const initialDistribution: Distribution = Distribution.NORMAL;
export const distributions: Distribution[] = Object.values(Distribution);
59 changes: 43 additions & 16 deletions apps/typegpu-docs/src/examples/algorithms/probability/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import type {
TgpuSlot,
} from 'typegpu';
import * as d from 'typegpu/data';
import { randf } from '@typegpu/noise';
import {
randf,
randomGeneratorSlot,
type StatefulGenerator,
} from '@typegpu/noise';

export class Executor {
// don't exceed max workgroup grid X dimension size
Expand All @@ -36,7 +40,10 @@ export class Executor {
& StorageFlag,
]
>;
readonly #pipelineCache: Map<TgpuFn, TgpuComputePipeline>;
readonly #pipelineCache: WeakMap<
TgpuFn,
WeakMap<StatefulGenerator, TgpuComputePipeline>
>;

constructor(root: TgpuRoot) {
this.#root = root;
Expand Down Expand Up @@ -93,34 +100,54 @@ export class Executor {
});
}

cachedPipeline(distribution: TgpuFn<() => d.Vec3f>) {
if (!import.meta.env.DEV) {
throw new Error('Function only for testing purposes');
#pipelineCacheHas(
distribution: TgpuFn<() => d.Vec3f>,
generator: StatefulGenerator,
): boolean {
const generatorMap = this.#pipelineCache.get(distribution);
if (!generatorMap) {
return false;
}

return generatorMap.has(generator);
}

#pipelineCacheSet(
distribution: TgpuFn<() => d.Vec3f>,
generator: StatefulGenerator,
pipeline: TgpuComputePipeline,
) {
if (!this.#pipelineCache.has(distribution)) {
this.#pipelineCache.set(distribution, new Map([[generator, pipeline]]));
return;
}

// biome-ignore lint/style/noNonNullAssertion: just checked it above
this.#pipelineCache.get(distribution)!.set(generator, pipeline);
}

pipelineCacheGet(
distribution: TgpuFn<() => d.Vec3f>,
generator: StatefulGenerator,
): TgpuComputePipeline {
if (!this.#pipelineCacheHas(distribution, generator)) {
const pipeline = this.#root['~unstable']
.with(randomGeneratorSlot, generator)
.with(this.#distributionSlot, distribution)
.withCompute(this.#dataMoreWorkersFunc)
.withCompute(this.#dataMoreWorkersFunc as TgpuComputeFn)
.createPipeline();
this.#pipelineCache.set(distribution, pipeline);
this.#pipelineCacheSet(distribution, generator, pipeline);
}

// biome-ignore lint/style/noNonNullAssertion: just checked it above
return this.#pipelineCache.get(distribution)!;
return this.#pipelineCache.get(distribution)!.get(generator)!;
}

async executeMoreWorkers(
distribution: TgpuFn<() => d.Vec3f>,
generator: StatefulGenerator,
): Promise<d.v3f[]> {
let pipeline = this.#pipelineCache.get(distribution);
if (!pipeline) {
pipeline = this.#root['~unstable']
.with(this.#distributionSlot, distribution)
.withCompute(this.#dataMoreWorkersFunc as TgpuComputeFn)
.createPipeline();
this.#pipelineCache.set(distribution, pipeline);
}
const pipeline = this.pipelineCacheGet(distribution, generator);

pipeline
.with(this.#bindGroupLayout, this.#bindGroup)
Expand Down
12 changes: 10 additions & 2 deletions apps/typegpu-docs/src/examples/algorithms/probability/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import tgpu from 'typegpu';
import { randf } from '@typegpu/noise';
import { BPETER, LCG, randf, type StatefulGenerator } from '@typegpu/noise';
import * as d from 'typegpu/data';

import { Distribution, PlotType, type PRNG } from './types.ts';
import { Distribution, Generator, PlotType, type PRNG } from './types.ts';
import * as c from './constants.ts';

const normal = d.vec3f(1.41, 1.41, 0);
Expand Down Expand Up @@ -96,3 +96,11 @@ const distributionCameras = {
export function getCameraPosition(distribution: Distribution): number[] {
return distributionCameras[distribution];
}

const GENERATOR_MAP = {
[Generator.BPETER]: BPETER,
[Generator.LCG]: LCG,
};

export const getGenerator = (gen: Generator): StatefulGenerator =>
GENERATOR_MAP[gen];
51 changes: 40 additions & 11 deletions apps/typegpu-docs/src/examples/algorithms/probability/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,28 @@ import tgpu from 'typegpu';

import { Plotter } from './plotter.ts';
import { Executor } from './executor.ts';
import type { Distribution } from './types.ts';
import type { Distribution, Generator } from './types.ts';
import * as c from './constants.ts';
import { getCameraPosition, getPRNG } from './helpers.ts';
import { getCameraPosition, getGenerator, getPRNG } from './helpers.ts';

const root = await tgpu.init();

const executor = new Executor(root);
const plotter = new Plotter();

let currentDistribution = c.initialDistribution;
let currentGenerator = c.initialGenerator;

const replot = async (
currentDistribution: Distribution,
currentGenerator: Generator,
animate = false,
) => {
let samples = undefined;
const prng = getPRNG(currentDistribution);
const gen = getGenerator(currentGenerator);

samples = await executor.executeMoreWorkers(prng.prng);
samples = await executor.executeMoreWorkers(prng.prng, gen);
await plotter.plot(samples, prng, animate);
};

Expand Down Expand Up @@ -63,7 +66,27 @@ export const controls = {
'Reseed': {
async onButtonClick() {
executor.reseed();
await replot(currentDistribution, true);
await replot(
currentDistribution,
currentGenerator,
true,
);
plotter.resetView(getCameraPosition(currentDistribution));
},
},
'Generator': {
initial: c.initialGenerator,
options: c.generators,
onSelectChange: async (value: Generator) => {
if (currentGenerator === value) {
return;
}
currentGenerator = value;
await replot(
currentDistribution,
currentGenerator,
true,
);
plotter.resetView(getCameraPosition(currentDistribution));
},
},
Expand All @@ -78,6 +101,7 @@ export const controls = {
currentDistribution = value;
await replot(
currentDistribution,
currentGenerator,
true,
);
plotter.resetView(getCameraPosition(currentDistribution));
Expand All @@ -90,20 +114,25 @@ export const controls = {
executor.count = value;
await replot(
currentDistribution,
currentGenerator,
);
},
},
'Test Resolution': import.meta.env.DEV && {
onButtonClick() {
c.distributions
.map((dist) =>
tgpu.resolve({
for (const dist of c.distributions) {
for (const gen of c.generators) {
const code = tgpu.resolve({
externals: {
f: executor.cachedPipeline(getPRNG(dist).prng),
p: executor.pipelineCacheGet(
getPRNG(dist).prng,
getGenerator(gen),
),
},
})
)
.map((r) => root.device.createShaderModule({ code: r }));
});
root.device.createShaderModule({ code });
}
}
},
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,10 @@ export interface SimplePRNG {
plotType: PlotType;
prng: TgpuFn<() => d.Vec3f>;
}

export const Generator = {
BPETER: 'bpeter (default)',
LCG: 'lcg',
} as const;

export type Generator = typeof Generator[keyof typeof Generator];
31 changes: 0 additions & 31 deletions apps/typegpu-docs/src/examples/tests/uniformity/lcg.ts

This file was deleted.

4 changes: 1 addition & 3 deletions apps/typegpu-docs/src/examples/tests/uniformity/prngs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { BPETER, type StatefulGenerator } from '@typegpu/noise';

import { LCG } from './lcg.ts';
import { BPETER, LCG, type StatefulGenerator } from '@typegpu/noise';

export const PRNG = {
BPETER: 'bpeter (default)',
Expand Down
46 changes: 45 additions & 1 deletion packages/typegpu-noise/src/generator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import tgpu, { type TgpuFn, type TgpuFnShell, type TgpuSlot } from 'typegpu';
import * as d from 'typegpu/data';
import { add, cos, dot, fract } from 'typegpu/std';
import { add, cos, dot, fract, pow } from 'typegpu/std';

export interface StatefulGenerator {
seed: TgpuFn<(seed: d.F32) => d.Void> | ((seed: number) => void);
Expand Down Expand Up @@ -47,6 +47,50 @@ export const BPETER: StatefulGenerator = (() => {
};
})();

/**
* Naive Linear Congruential Generator (LCG)
*/
export const LCG: StatefulGenerator = (() => {
const seed = tgpu.privateVar(d.u32);

const u32To01Float = tgpu.fn([d.u32], d.f32)`(val){
let exponent: u32 = 0x3f800000;
let mantissa: u32 = 0x007fffff & val;
var ufloat: u32 = (exponent | mantissa);
return bitcast<f32>(ufloat) - 1f;
}`;

return {
seed: (value: number) => {
'kernel';
seed.$ = d.u32(value * pow(32, 3));
},
seed2: (value: d.v2f) => {
'kernel';
seed.$ = d.u32(value.x * pow(32, 3) + value.y * pow(32, 2));
},
seed3: (value: d.v3f) => {
'kernel';
seed.$ = d.u32(
value.x * pow(32, 3) + value.y * pow(32, 2) +
value.z * pow(32, 1),
);
},
seed4: (value: d.v4f) => {
'kernel';
seed.$ = d.u32(
value.x * pow(32, 3) + value.y * pow(32, 2) +
value.z * pow(32, 1) + value.w * pow(32, 0),
);
},
sample: () => {
'kernel';
seed.$ = seed.$ * 1664525 + 1013904223; // % 2 ^ 32
return u32To01Float(seed.$);
},
};
})();

// The default (Can change between releases to improve uniformity).
export const DefaultGenerator: StatefulGenerator = BPETER;

Expand Down
1 change: 1 addition & 0 deletions packages/typegpu-noise/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export {
BPETER,
// The default (Can change between releases to improve uniformity).
DefaultGenerator,
LCG,
// ---
randomGeneratorShell,
randomGeneratorSlot,
Expand Down
Loading