Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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.DEFAULT_GENERATOR;
export const generators: Generator[] = Object.values(Generator);
export const initialDistribution: Distribution = Distribution.NORMAL;
export const distributions: Distribution[] = Object.values(Distribution);
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,37 +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 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: TgpuComputePipeline;
if (this.#pipelineCache.has(distribution)) {
// biome-ignore lint/style/noNonNullAssertion: just checked it above
pipeline = this.#pipelineCache.get(distribution)!;
} else {
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
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import tgpu from 'typegpu';
import { randf } from '@typegpu/noise';
import {
BPETER,
DefaultGenerator,
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 +102,12 @@ const distributionCameras = {
export function getCameraPosition(distribution: Distribution): number[] {
return distributionCameras[distribution];
}

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

export const getGenerator = (gen: Generator): StatefulGenerator =>
GENERATOR_MAP[gen];
45 changes: 36 additions & 9 deletions apps/typegpu-docs/src/content/examples/simple/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 @@ -58,6 +61,23 @@ export const controls = {
executor.reseed();
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 @@ -74,6 +94,7 @@ export const controls = {
currentDistribution = value;
await replot(
currentDistribution,
currentGenerator,
true,
);
plotter.resetView(getCameraPosition(currentDistribution));
Expand All @@ -86,18 +107,24 @@ export const controls = {
executor.count = value;
await replot(
currentDistribution,
currentGenerator,
);
},
},
'Test Resolution': import.meta.env.DEV && {
onButtonClick: () => {
c.distributions
.map((dist) =>
tgpu.resolve({
externals: {
f: executor.cachedPipeline(getPRNG(dist).prng),
},
})
.flatMap((dist) =>
c.generators.map((gen) =>
tgpu.resolve({
externals: {
p: executor.pipelineCacheGet(
getPRNG(dist).prng,
getGenerator(gen),
),
},
})
)
)
.map((r) => root.device.createShaderModule({ code: r }));
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,11 @@ export interface SimplePRNG {
plotType: PlotType;
prng: TgpuFn<() => d.Vec3f>;
}

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

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

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import {
BPETER,
DefaultGenerator,
LCG,
type StatefulGenerator,
} from '@typegpu/noise';

import { LCG } from './lcg.ts';

export const PRNG = {
BPETER: 'bpeter',
DEFAULT_GENERATOR: 'default',
Expand Down
42 changes: 41 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>;
Expand Down Expand Up @@ -47,6 +47,46 @@ export const BPETER: StatefulGenerator = (() => {
};
})();

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

const u32ToFloat = 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: tgpu.fn([d.f32])((value) => {
seed.$ = d.u32(value * pow(32, 3));
}),
seed2: tgpu.fn([d.vec2f])((value) => {
seed.$ = d.u32(value.x * pow(32, 3) + value.y * pow(32, 2));
}),
seed3: tgpu.fn([d.vec3f])((value) => {
seed.$ = d.u32(
value.x * pow(32, 3) + value.y * pow(32, 2) +
value.z * pow(32, 1),
);
}),
seed4: tgpu.fn([d.vec4f])((value) => {
seed.$ = d.u32(
value.x * pow(32, 3) + value.y * pow(32, 2) +
value.z * pow(32, 1) + value.w * pow(32, 0),
);
}),
sample: randomGeneratorShell(() => {
'kernel';
seed.$ = seed.$ * 1664525 + 1013904223; // % 2 ^ 32
return u32ToFloat(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 @@ -132,6 +132,7 @@ export {
BPETER,
// The default (Can change between releases to improve uniformity).
DefaultGenerator,
LCG,
// ---
randomGeneratorShell,
randomGeneratorSlot,
Expand Down
Loading