Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🥗: Utility modules starter #807

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
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
1 change: 1 addition & 0 deletions apps/typegpu-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-slider": "^1.2.0",
"@stackblitz/sdk": "^1.11.0",
"@typegpu/noise": "workspace:*",
"@types/dom-mediacapture-transform": "^0.1.9",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
Expand Down
82 changes: 30 additions & 52 deletions apps/typegpu-docs/src/components/CodeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,47 @@ import Editor, {
type Monaco,
type OnMount,
} from '@monaco-editor/react';
import webgpuTypes from '@webgpu/types/dist/index.d.ts?raw';
// biome-ignore lint/correctness/noUnusedImports: <its a namespace, Biome>
import type { editor } from 'monaco-editor';
import { entries, map, pipe } from 'remeda';
import typedBinary from 'typed-binary/dist/index.d.ts?raw';
import {
entries,
filter,
fromEntries,
isTruthy,
map,
pipe,
values,
} from 'remeda';
import { SANDBOX_MODULES } from '../utils/examples/sandboxModules';
import { tsCompilerOptions } from '../utils/liveEditor/embeddedTypeScript';

const typegpuDtsFiles: Record<string, string> = import.meta.glob(
'../../../../packages/typegpu/dist/**/*.d.ts',
{
query: 'raw',
eager: true,
import: 'default',
},
);

const typegpuExtraLibs = pipe(
entries(typegpuDtsFiles),
map(([path, content]) => ({
filename: path.replace('../../../../packages/typegpu/dist', 'typegpu/dist'),
content,
})),
);

const mediacaptureDtsFiles: Record<string, string> = import.meta.glob(
'../../node_modules/@types/dom-mediacapture-transform/**/*.d.ts',
{
query: 'raw',
eager: true,
import: 'default',
},
);

const mediacaptureExtraLibs = pipe(
entries(mediacaptureDtsFiles),
map(([path, content]) => ({
filename: path.replace(
'../../node_modules/@types/dom-mediacapture-transform',
'@types/dom-mediacapture-transform',
),
content,
})),
);

function handleEditorWillMount(monaco: Monaco) {
const tsDefaults = monaco?.languages.typescript.typescriptDefaults;

tsDefaults.addExtraLib(webgpuTypes);
for (const lib of typegpuExtraLibs) {
tsDefaults.addExtraLib(lib.content, lib.filename);
for (const moduleDef of values(SANDBOX_MODULES)) {
if ('content' in moduleDef.typeDef) {
tsDefaults.addExtraLib(
moduleDef.typeDef.content,
moduleDef.typeDef.filename,
);
}
}
for (const lib of mediacaptureExtraLibs) {
tsDefaults.addExtraLib(lib.content, lib.filename);
}
tsDefaults.addExtraLib(typedBinary, 'typed-binary.d.ts');

const reroutes = pipe(
entries(SANDBOX_MODULES),
map(([key, moduleDef]) => {
if ('reroute' in moduleDef.typeDef) {
return [key, moduleDef.typeDef.reroute] as const;
}
return null;
}),
filter(isTruthy),
fromEntries(),
);

tsDefaults.setCompilerOptions({
...tsCompilerOptions,
paths: {
typegpu: ['typegpu/dist/index.d.ts'],
'typegpu/data': ['typegpu/dist/data/index.d.ts'],
'typegpu/std': ['typegpu/dist/std/index.d.ts'],
},
paths: reroutes,
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { BPETER, rand } from '@typegpu/noise';
import tgpu, {
unstable_asMutable,
unstable_asReadonly,
Expand All @@ -6,7 +7,6 @@ import tgpu, {
type TgpuBufferMutable,
} from 'typegpu';
import * as d from 'typegpu/data';
import { cos, dot, fract } from 'typegpu/std';

const canvas = document.querySelector('canvas') as HTMLCanvasElement;
const context = canvas.getContext('webgpu') as GPUCanvasContext;
Expand All @@ -22,24 +22,6 @@ context.configure({

const MAX_GRID_SIZE = 1024;

const randSeed = tgpu['~unstable'].privateVar(d.vec2f);

const setupRandomSeed = tgpu['~unstable'].fn([d.vec2f]).does((coord) => {
randSeed.value = coord;
});

/**
* Yoinked from https://www.cg.tuwien.ac.at/research/publications/2023/PETER-2023-PSW/PETER-2023-PSW-.pdf
* "Particle System in WebGPU" by Benedikt Peter
*/
const rand01 = tgpu['~unstable'].fn([], d.f32).does(() => {
const a = dot(randSeed.value, d.vec2f(23.14077926, 232.61690225));
const b = dot(randSeed.value, d.vec2f(54.47856553, 345.84153136));
randSeed.value.x = fract(cos(a) * 136.8168);
randSeed.value.y = fract(cos(b) * 534.7645);
return randSeed.value.y;
});

type GridData = typeof GridData;
/**
* x - velocity.x
Expand Down Expand Up @@ -244,7 +226,7 @@ const computeVelocity = tgpu['~unstable']
let least_cost_dir = dir_choices[u32(rand01() * f32(dir_choice_count))];
return least_cost_dir;
}`)
.$uses({ getCell, isValidFlowOut, isValidCoord, rand01 });
.$uses({ getCell, isValidFlowOut, isValidCoord, rand01: rand.float01 });

const mainInitWorld = tgpu['~unstable']
.computeFn([d.builtin.globalInvocationId], { workgroupSize: [1] })
Expand Down Expand Up @@ -443,7 +425,7 @@ const mainCompute = tgpu['~unstable']
`)
.$uses({
coordsToIndex,
setupRandomSeed,
setupRandomSeed: BPETER.seed,
timeUniform,
getCell,
computeVelocity,
Expand Down
17 changes: 8 additions & 9 deletions apps/typegpu-docs/src/utils/examples/exampleRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { transpileModule } from 'typescript';
import { tsCompilerOptions } from '../liveEditor/embeddedTypeScript';
import type { ExampleControlParam } from './exampleControlAtom';
import type { ExampleState } from './exampleState';
import { SANDBOX_MODULES } from './sandboxModules';

// NOTE: @babel/standalone does expose internal packages, as specified in the docs, but the
// typing for @babel/standalone does not expose them.
Expand Down Expand Up @@ -164,15 +165,6 @@ export async function executeExample(
* modules available.
*/
const _import = async (moduleKey: string) => {
if (moduleKey === 'typegpu') {
return await import('typegpu');
}
if (moduleKey === 'typegpu/data') {
return await import('typegpu/data');
}
if (moduleKey === 'typegpu/std') {
return await import('typegpu/std');
}
if (moduleKey === '@typegpu/example-toolkit') {
return {
onCleanup(callback: () => unknown) {
Expand All @@ -181,6 +173,13 @@ export async function executeExample(
addParameters,
};
}

if (moduleKey in SANDBOX_MODULES) {
return await SANDBOX_MODULES[
moduleKey as keyof typeof SANDBOX_MODULES
].importer?.();
}

throw new Error(`Module ${moduleKey} is not available in the sandbox.`);
};

Expand Down
88 changes: 88 additions & 0 deletions apps/typegpu-docs/src/utils/examples/sandboxModules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { entries, fromEntries, map, pipe } from 'remeda';

// import dtsTypeGPUNoise from '@typegpu/noise/dist/index.d.ts?raw';
import dtsWebGPU from '@webgpu/types/dist/index.d.ts?raw';
import dtsTypedBinary from 'typed-binary/dist/index.d.ts?raw';

interface SandboxModuleDefinition<TModuleType> {
importer?: () => Promise<TModuleType>;
typeDef: { filename?: string; content: string } | { reroute: [string] };
}

function dtsFileToModule(
[filepath, content]: [string, string],
baseUrl: string,
): [moduleKey: string, moduleDef: SandboxModuleDefinition<unknown>] {
const filename = filepath.replace(baseUrl, '');

return [
filename,
{
typeDef: {
filename,
content,
},
},
] as const;
}

const typegpuModules = pipe(
entries(
import.meta.glob('../../../../../packages/typegpu/dist/**/*.d.ts', {
query: 'raw',
eager: true,
import: 'default',
}) as Record<string, string>,
),
map((dtsFile) => dtsFileToModule(dtsFile, '../../../../../packages/')),
fromEntries(),
);

const mediacaptureModules = pipe(
entries(
import.meta.glob(
'../../../node_modules/@types/dom-mediacapture-transform/**/*.d.ts',
{
query: 'raw',
eager: true,
import: 'default',
},
) as Record<string, string>,
),
map((dtsFile) => dtsFileToModule(dtsFile, '../../../node_modules/')),
fromEntries(),
);

export const SANDBOX_MODULES: Record<
string,
SandboxModuleDefinition<unknown>
> = {
...typegpuModules,
...mediacaptureModules,

'@webgpu/types': {
typeDef: { content: dtsWebGPU },
},
'typed-binary': {
typeDef: { filename: 'typed-binary.d.ts', content: dtsTypedBinary },
},
typegpu: {
importer: () => import('typegpu'),
typeDef: { reroute: ['typegpu/dist/index.d.ts'] },
},
'typegpu/data': {
importer: () => import('typegpu/data'),
typeDef: { reroute: ['typegpu/dist/data/index.d.ts'] },
},
'typegpu/std': {
importer: () => import('typegpu/std'),
typeDef: { reroute: ['typegpu/dist/std/index.d.ts'] },
},

// Utility modules
'@typegpu/noise': {
importer: () => import('@typegpu/noise'),
// TODO: Add type definitions
typeDef: { filename: '@typegpu-noise.d.ts', content: '' },
},
};
28 changes: 28 additions & 0 deletions packages/typegpu-noise/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<div align="center">

# @typegpu/noise

🚧 **Under Construction** 🚧

</div>

A set of noise/pseudo-random functions for use in WebGPU/TypeGPU apps.

```ts
import { setSeed, rand } from '@typegpu/noise';

const timeBuffer = root.createBuffer(f32).$usage('uniform');
const timeUniform = timeBuffer.as('uniform');

const main = tgpu
.fragmentFn({ pos: builtin.position }, vec4f)
.does(({ pos }) => {
const time = timeUniform.value;
setSeed(add(pos.xy, vec2f(time)));

const val = rand.float01(); // => number
const normal = rand.onUnitSphere(); // => v3f
const dir = rand.inUnitCircle(); // => v2f
});

```
12 changes: 12 additions & 0 deletions packages/typegpu-noise/build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { type BuildConfig, defineBuildConfig } from "unbuild";
import typegpu from "rollup-plugin-typegpu";

const Config: BuildConfig[] = defineBuildConfig({
hooks: {
"rollup:options": (_options, config) => {
config.plugins.push(typegpu({ include: [/\.ts$/] }));
},
},
});

export default Config;
34 changes: 34 additions & 0 deletions packages/typegpu-noise/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "@typegpu/noise",
"type": "module",
"version": "0.0.3",
"description": "A set of noise/pseudo-random functions for use in WebGPU/TypeGPU apps.",
"main": "./dist/index.cjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"module": "./dist/index.mjs",
"import": "./dist/index.mjs",
"default": "./dist/index.cjs"
}
},
"files": ["README.md", "package.json", "dist/**/*"],
"sideEffects": false,
"scripts": {
"build": "unbuild",
"dev:build": "unbuild --stub"
},
"keywords": [],
"license": "MIT",
"peerDependencies": {
"typegpu": "^0.3.2"
},
"devDependencies": {
"@webgpu/types": "^0.1.51",
"rollup-plugin-typegpu": "workspace:*",
"typegpu": "workspace:*",
"typescript": "^5.7.2",
"unbuild": "^3.3.1"
}
}
Loading
Loading