software-mansion / TypeGPU

TypeScript library that enhances the WebGPU API, allowing resource management in a type-safe, declarative way.
http://typegpu.com
MIT License
143 stars 2 forks source link

[Discussion] Rethink example-toolkit #143

Open iwoplaza opened 2 months ago

iwoplaza commented 2 months ago

(written by @mhawryluk)

As noticed by @piaskowyk currently the examples use our special example-toolkit functionalities which aren't available outside the page. Therefore users cannot copy paste our examples into their projects and expect them to work. We could let examples define their html template alongside the typescript, but while it could definitely work well for canvas and video elements, the gui controlls are more tricky to do this way.

iwoplaza commented 2 months ago

Good point, we have to think about this. We could try Svelte's approach of exporting values.

Before

// -- Hooks into the example environment
import {
  addElement,
  addSliderParam,
  onCleanup,
  onFrame,
} from '@typegpu/example-toolkit';
// --

import { createRuntime, wgsl } from 'typegpu';

const xSpanPlum = addSliderParam('x span', 16, { min: 1, max: 16, step: 1 });
const ySpanPlum = addSliderParam('y span', 16, { min: 1, max: 16, step: 1 });

const canvas = await addElement('canvas', { aspectRatio: 1 });

const runtime = await createRuntime();

onFrame((deltaTime) => {
  // ...
  runtime.flush();
});

onCleanup(() => {
  runtime.dispose();
});

After

import { createRuntime, wgsl } from 'typegpu';

/** @slider { min: 1, max: 16, step: 1 } */
export const xSpanPlum = wgsl.plum(16).$name('x span');
/** @slider { min: 1, max: 16, step: 1 } */
export const ySpanPlum = wgsl.plum(16).$name('y span');

const canvas = document.createElement('canvas');
canvas.style.setProperty('--aspect-ratio', 1);

const runtime = await createRuntime();

let lastTime = Date.now();
function loop() {
  const now = Date.now();
  const deltaTime = now - lastTime;
  lastTime = now;

  // ...
  runtime.flush();

  requestAnimationFrame(loop);
}
loop();
iwoplaza commented 2 months ago

We can try to add the Canvas and any other non-control elements in a separate HTML file, and keep the parameters as exported values.

import { createRuntime, wgsl } from 'typegpu';

/** @slider { min: 1, max: 16, step: 1 } */
export const xSpanPlum = wgsl.plum(16).$name('x span');
/** @slider { min: 1, max: 16, step: 1 } */
export const ySpanPlum = wgsl.plum(16).$name('y span');

const canvas = document.getElementById('canvas');

const runtime = await createRuntime();

let lastTime = Date.now();
function loop() {
  const now = Date.now();
  const deltaTime = now - lastTime;
  lastTime = now;

  // ...
  runtime.flush();

  requestAnimationFrame(loop);
}
loop();
<div class="row">
  <canvas id="canvas" class="aspect-1"></canvas>
</div>
iwoplaza commented 2 months ago

@mhawryluk @reczkok Thoughts? 👀

mhawryluk commented 2 months ago

I really like this approach, I have no notes. I assume dropdowns, toggles and buttons would be defined just like the slider? then dropdowns and toggles could have the plum support like sliders, but buttons (in the control panel) would be defined without plums I assume?

iwoplaza commented 2 months ago

Exported functions could become button on-click handlers.

/** @slider { min: 1, max: 16, step: 1 } */
export const fooPlum = wgsl.plum(16).$name('foo');

/** @select ['a', 'b', 'c'] */
export const barPlum = wgsl.plum('a').$name('bar');

/** @toggle */
export const zonkPlum = wgsl.plum(false).$name('zonk');

/** @button "Recompute" */
export function recompute() {
  // do something
}