From 6ca685c93a673035fd03d5116aec25f5fe9ac330 Mon Sep 17 00:00:00 2001
From: Iwo Plaza
Date: Thu, 21 May 2026 23:01:41 +0200
Subject: [PATCH 1/8] feat: common.attachAutoResizer
---
.../examples/algorithms/bitonic-sort/index.ts | 22 ++++------
.../rendering/cubemap-reflection/index.ts | 18 ++++----
.../src/examples/simple/triangle/index.ts | 25 +++++++----
.../typegpu/src/common/attachAutoResizer.ts | 42 +++++++++++++++++++
packages/typegpu/src/common/index.ts | 1 +
5 files changed, 76 insertions(+), 32 deletions(-)
create mode 100644 packages/typegpu/src/common/attachAutoResizer.ts
diff --git a/apps/typegpu-docs/src/examples/algorithms/bitonic-sort/index.ts b/apps/typegpu-docs/src/examples/algorithms/bitonic-sort/index.ts
index ad5bb150c0..5e54900d30 100644
--- a/apps/typegpu-docs/src/examples/algorithms/bitonic-sort/index.ts
+++ b/apps/typegpu-docs/src/examples/algorithms/bitonic-sort/index.ts
@@ -1,4 +1,4 @@
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
import {
type BitonicSorter,
type BitonicSorterOptions,
@@ -6,7 +6,6 @@ import {
decomposeWorkgroups,
} from '@typegpu/sort';
import { randf } from '@typegpu/noise';
-import { fullScreenTriangle } from 'typegpu/common';
import { defineControls } from '../../common/defineControls.ts';
const maxBufferSize = await navigator.gpu.requestAdapter().then((adapter) => {
@@ -32,7 +31,10 @@ const querySet = hasTimestampQuery ? root.createQuerySet('timestamp', 2) : null;
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
const context = root.configureContext({ canvas });
-const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+});
const maxSide = Math.floor(Math.sqrt(maxBufferSize / 4));
const minLog = 2; // log_2(4)
@@ -75,17 +77,11 @@ const state = {
const WORKGROUP_SIZE = 256;
const renderLayout = tgpu.bindGroupLayout({
- data: {
- storage: d.arrayOf(d.u32),
- access: 'readonly',
- },
+ data: { storage: d.arrayOf(d.u32), access: 'readonly' },
});
const initLayout = tgpu.bindGroupLayout({
- data: {
- storage: d.arrayOf(d.u32),
- access: 'mutable',
- },
+ data: { storage: d.arrayOf(d.u32), access: 'mutable' },
});
const initSeed = root.createUniform(d.f32, 0);
@@ -135,9 +131,8 @@ const initKernel = tgpu.computeFn({
});
const renderPipeline = root.createRenderPipeline({
- vertex: fullScreenTriangle,
+ vertex: common.fullScreenTriangle,
fragment: fragmentFn,
- targets: { format: presentationFormat },
});
const initPipeline = root.createComputePipeline({ compute: initKernel });
@@ -275,6 +270,7 @@ export function onCleanup() {
for (const s of Object.values(sorters)) {
s.destroy();
}
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/index.ts b/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/index.ts
index e82e818df7..34f9ba787e 100644
--- a/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/index.ts
@@ -1,4 +1,4 @@
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
import * as m from 'wgpu-matrix';
import { type CubemapNames, cubeVertices, loadCubemap } from './cubemap.ts';
import { Camera, CubeVertex, DirectionalLight, Material, Vertex } from './dataTypes.ts';
@@ -273,13 +273,10 @@ loop();
// #region Example controls and cleanup
-const resizeObserver = new ResizeObserver((entries) => {
- for (const entry of entries) {
- const dpr = window.devicePixelRatio;
- const width = entry.contentRect.width;
- const height = entry.contentRect.height;
- canvas.width = width * dpr;
- canvas.height = height * dpr;
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
const newProj = m.mat4.perspective(
Math.PI / 4,
canvas.width / canvas.height,
@@ -288,9 +285,8 @@ const resizeObserver = new ResizeObserver((entries) => {
d.mat4x4f(),
);
cameraBuffer.patch({ projection: newProj });
- }
+ },
});
-resizeObserver.observe(canvas);
// Variables for mouse interaction.
let isDragging = false;
@@ -499,7 +495,7 @@ export function onCleanup() {
window.removeEventListener('mousemove', mouseMoveEventListener);
window.removeEventListener('touchmove', touchMoveEventListener);
window.removeEventListener('touchend', touchEndEventListener);
- resizeObserver.unobserve(canvas);
+ detachAutoResizer();
icosphereGenerator.destroy();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simple/triangle/index.ts b/apps/typegpu-docs/src/examples/simple/triangle/index.ts
index 5794dfc374..9d98ea8c1d 100644
--- a/apps/typegpu-docs/src/examples/simple/triangle/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/triangle/index.ts
@@ -1,22 +1,22 @@
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
// Constants and helper functions
const purple = d.vec4f(0.769, 0.392, 1, 1);
const blue = d.vec4f(0.114, 0.447, 0.941, 1);
-const getGradientColor = (ratio: number) => {
+function getGradientColor(ratio: number) {
'use gpu';
return std.mix(purple, blue, ratio);
-};
+}
-const pos = tgpu.const(d.arrayOf(d.vec2f, 3), [
+const pos = tgpu.const(d.arrayOf(d.vec2f), [
d.vec2f(0.0, 0.5),
d.vec2f(-0.5, -0.5),
d.vec2f(0.5, -0.5),
]);
-const uv = tgpu.const(d.arrayOf(d.vec2f, 3), [
+const uv = tgpu.const(d.arrayOf(d.vec2f), [
d.vec2f(0.5, 1.0),
d.vec2f(0.0, 0.0),
d.vec2f(1.0, 0.0),
@@ -39,18 +39,27 @@ const pipeline = root.createRenderPipeline({
},
});
-// Setting up the canvas and drawing to it
+// Setting up the canvas
+const canvas = document.querySelector('canvas') as HTMLCanvasElement;
const context = root.configureContext({
- canvas: document.querySelector('canvas') as HTMLCanvasElement,
+ canvas,
alphaMode: 'premultiplied',
});
-pipeline.withColorAttachment({ view: context }).draw(3);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Drawing once, and then each time the canvas resizes
+ pipeline.withColorAttachment({ view: context }).draw(3);
+ },
+});
// #region Cleanup
export function onCleanup() {
+ detachAutoResizer();
root.destroy();
}
diff --git a/packages/typegpu/src/common/attachAutoResizer.ts b/packages/typegpu/src/common/attachAutoResizer.ts
new file mode 100644
index 0000000000..7f43ad00c8
--- /dev/null
+++ b/packages/typegpu/src/common/attachAutoResizer.ts
@@ -0,0 +1,42 @@
+import type { TgpuRoot } from '../core/root/rootTypes.ts';
+
+export interface AttachAutoResizerOptions {
+ root: TgpuRoot;
+ canvas: HTMLCanvasElement;
+ onResize?(): void;
+}
+
+export function attachAutoResizer({
+ root,
+ canvas,
+ onResize,
+}: AttachAutoResizerOptions): () => void {
+ const observer = new ResizeObserver((entries) => {
+ for (const entry of entries) {
+ if (!entry) {
+ return;
+ }
+ const width =
+ entry.devicePixelContentBoxSize?.[0]?.inlineSize ||
+ (entry.contentBoxSize[0]?.inlineSize ?? 0) * window.devicePixelRatio;
+ const height =
+ entry.devicePixelContentBoxSize?.[0]?.blockSize ||
+ (entry.contentBoxSize[0]?.blockSize ?? 0) * window.devicePixelRatio;
+ const canvas = entry.target as HTMLCanvasElement;
+ canvas.width = Math.max(1, Math.min(width, root.device.limits.maxTextureDimension2D));
+ canvas.height = Math.max(1, Math.min(height, root.device.limits.maxTextureDimension2D));
+
+ onResize?.();
+ }
+ });
+
+ try {
+ observer.observe(canvas, { box: 'device-pixel-content-box' });
+ } catch {
+ observer.observe(canvas, { box: 'content-box' });
+ }
+
+ return () => {
+ observer.disconnect();
+ };
+}
diff --git a/packages/typegpu/src/common/index.ts b/packages/typegpu/src/common/index.ts
index 9249008035..555b267f06 100644
--- a/packages/typegpu/src/common/index.ts
+++ b/packages/typegpu/src/common/index.ts
@@ -1,4 +1,5 @@
// NOTE: This is a barrel file, internal files should not import things from this file
export { fullScreenTriangle } from './fullScreenTriangle.ts';
+export { attachAutoResizer } from './attachAutoResizer.ts';
export { writeSoA } from './writeSoA.ts';
From 4072a1a79fa21ddd43340f0aaff4a2b8e54eb121 Mon Sep 17 00:00:00 2001
From: Iwo Plaza
Date: Fri, 22 May 2026 15:22:03 +0200
Subject: [PATCH 2/8] Update examples to use common.attachAutoResizer
---
.../examples/algorithms/bitonic-sort/index.ts | 13 +++---
.../algorithms/genetic-racing/index.ts | 6 +++
.../algorithms/jump-flood-distance/index.ts | 20 ++++-----
.../algorithms/jump-flood-voronoi/index.ts | 20 ++++-----
.../src/examples/geometry/circles/index.ts | 45 ++++++++++---------
.../geometry/lines-combinations/index.ts | 5 ++-
.../image-processing/ascii-filter/index.ts | 6 +++
.../background-segmentation/index.ts | 6 +++
.../examples/image-processing/blur/index.ts | 9 +++-
.../camera-thresholding/index.ts | 6 +++
.../image-processing/chroma-keying/index.ts | 6 +++
.../image-processing/image-tuning/index.ts | 11 ++++-
.../src/examples/rendering/3d-fish/index.ts | 39 ++++++++--------
.../rendering/box-raytracing/index.ts | 5 ++-
.../src/examples/rendering/caustics/index.ts | 5 ++-
.../src/examples/rendering/clouds/index.ts | 3 ++
.../src/examples/rendering/disco/index.ts | 8 +++-
.../rendering/function-visualizer/index.ts | 5 ++-
.../examples/rendering/jelly-slider/index.ts | 11 +++--
.../examples/rendering/jelly-switch/index.ts | 11 +++--
.../examples/rendering/perlin-noise/index.ts | 3 ++
.../rendering/phong-reflection/index.ts | 5 ++-
.../rendering/point-light-shadow/index.ts | 16 +++----
.../src/examples/rendering/pom/index.ts | 13 ++++--
.../radiance-cascades-drawing/index.ts | 3 ++
.../rendering/radiance-cascades/index.ts | 13 +++---
.../examples/rendering/ray-marching/index.ts | 8 +++-
.../rendering/render-bundles-with/index.ts | 5 ++-
.../rendering/render-bundles/index.ts | 8 +++-
.../examples/rendering/simple-shadow/index.ts | 35 ++++++++-------
.../rendering/smoky-triangle/index.ts | 5 ++-
.../src/examples/rendering/suika-sdf/index.ts | 29 ++++++------
.../src/examples/rendering/two-boxes/index.ts | 5 ++-
.../rendering/xor-dev-centrifuge-2/index.ts | 5 ++-
.../rendering/xor-dev-runner/index.ts | 8 +++-
.../examples/simple/gradient-tiles/index.ts | 9 +++-
.../src/examples/simple/liquid-glass/index.ts | 3 ++
.../examples/simple/mesh-skinning/index.ts | 3 ++
.../src/examples/simple/oklab/index.ts | 11 +++--
.../src/examples/simple/ripple-cube/index.ts | 5 ++-
.../src/examples/simple/square/index.ts | 12 ++++-
.../src/examples/simple/stencil/index.ts | 5 ++-
.../src/examples/simple/vaporrave/index.ts | 5 ++-
.../src/examples/simulation/boids/index.ts | 5 ++-
.../src/examples/simulation/confetti/index.ts | 5 ++-
.../fluid-double-buffering/index.ts | 5 ++-
.../simulation/fluid-with-atomics/index.ts | 3 ++
.../examples/simulation/game-of-life/index.ts | 3 ++
.../src/examples/simulation/gravity/index.ts | 5 ++-
.../simulation/slime-mold-3d/index.ts | 3 ++
.../examples/simulation/slime-mold/index.ts | 5 ++-
.../examples/simulation/stable-fluid/index.ts | 4 ++
.../src/examples/simulation/wind-map/index.ts | 5 ++-
.../src/examples/tests/log-test/index.ts | 5 ++-
.../examples/tests/rc-docs-examples/index.ts | 3 ++
.../examples/tests/sdf-docs-examples/index.ts | 3 ++
.../src/examples/tests/texture-test/index.ts | 3 ++
.../src/examples/tests/uniformity/index.ts | 13 +++---
58 files changed, 364 insertions(+), 160 deletions(-)
diff --git a/apps/typegpu-docs/src/examples/algorithms/bitonic-sort/index.ts b/apps/typegpu-docs/src/examples/algorithms/bitonic-sort/index.ts
index 5e54900d30..ab44e89723 100644
--- a/apps/typegpu-docs/src/examples/algorithms/bitonic-sort/index.ts
+++ b/apps/typegpu-docs/src/examples/algorithms/bitonic-sort/index.ts
@@ -31,11 +31,6 @@ const querySet = hasTimestampQuery ? root.createQuerySet('timestamp', 2) : null;
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
const context = root.configureContext({ canvas });
-const detachAutoResizer = common.attachAutoResizer({
- root,
- canvas,
-});
-
const maxSide = Math.floor(Math.sqrt(maxBufferSize / 4));
const minLog = 2; // log_2(4)
const maxLog = Math.floor(Math.log2(maxSide));
@@ -241,6 +236,14 @@ async function sort() {
hideOverlay();
}
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ render();
+ },
+});
+
// #region Example controls & Cleanup
const sortOrderKeys = Object.keys(sortOrders) as SortOrderKey[];
diff --git a/apps/typegpu-docs/src/examples/algorithms/genetic-racing/index.ts b/apps/typegpu-docs/src/examples/algorithms/genetic-racing/index.ts
index 86a13accbb..5eec334d28 100644
--- a/apps/typegpu-docs/src/examples/algorithms/genetic-racing/index.ts
+++ b/apps/typegpu-docs/src/examples/algorithms/genetic-racing/index.ts
@@ -532,6 +532,11 @@ applyGridSize(...GRID_SIZES[gridSizeKey]);
applyTrack(generateGridTrack(trackSeed, ...GRID_SIZES[gridSizeKey]));
startSimulation();
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+});
+
// #region Example controls & Cleanup
export const controls = defineControls({
@@ -605,6 +610,7 @@ export const controls = defineControls({
export function onCleanup() {
cancelAnimationFrame(rafHandle);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/algorithms/jump-flood-distance/index.ts b/apps/typegpu-docs/src/examples/algorithms/jump-flood-distance/index.ts
index ce3767e05b..b68242d354 100644
--- a/apps/typegpu-docs/src/examples/algorithms/jump-flood-distance/index.ts
+++ b/apps/typegpu-docs/src/examples/algorithms/jump-flood-distance/index.ts
@@ -342,8 +342,6 @@ function recreateResources() {
sourceIdx = 0;
}
-clearCanvas();
-
function getTouchPosition(rect: DOMRect, touches: TouchList) {
if (touches.length === 2) {
return {
@@ -357,17 +355,16 @@ function getTouchPosition(rect: DOMRect, touches: TouchList) {
};
}
-// #region Example controls & Cleanup
-
-let resizeTimeout: ReturnType;
-const resizeObserver = new ResizeObserver(() => {
- clearTimeout(resizeTimeout);
- resizeTimeout = setTimeout(() => {
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
recreateResources();
clearCanvas();
- }, 100);
+ },
});
-resizeObserver.observe(canvas);
+
+// #region Example controls & Cleanup
const onMouseDown = (e: MouseEvent) => {
if (e.button !== 0 && e.button !== 2) {
@@ -475,8 +472,7 @@ export const controls = defineControls({
});
export function onCleanup() {
- clearTimeout(resizeTimeout);
- resizeObserver.disconnect();
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/algorithms/jump-flood-voronoi/index.ts b/apps/typegpu-docs/src/examples/algorithms/jump-flood-voronoi/index.ts
index fa64ed38b4..1d8019bd8c 100644
--- a/apps/typegpu-docs/src/examples/algorithms/jump-flood-voronoi/index.ts
+++ b/apps/typegpu-docs/src/examples/algorithms/jump-flood-voronoi/index.ts
@@ -239,19 +239,16 @@ function reset() {
void runFloodAnimated(currentRunId);
}
-reset();
-
-// #region Example controls & Cleanup
-
-let resizeTimeout: ReturnType;
-const resizeObserver = new ResizeObserver(() => {
- clearTimeout(resizeTimeout);
- resizeTimeout = setTimeout(() => {
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
recreateResources();
reset();
- }, 100);
+ },
});
-resizeObserver.observe(canvas);
+
+// #region Example controls & Cleanup
export const controls = defineControls({
'Run Algorithm': {
@@ -295,8 +292,7 @@ export const controls = defineControls({
});
export function onCleanup() {
- clearTimeout(resizeTimeout);
- resizeObserver.disconnect();
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/geometry/circles/index.ts b/apps/typegpu-docs/src/examples/geometry/circles/index.ts
index 549f4b0d4b..2790b4c6a9 100644
--- a/apps/typegpu-docs/src/examples/geometry/circles/index.ts
+++ b/apps/typegpu-docs/src/examples/geometry/circles/index.ts
@@ -1,5 +1,5 @@
import { circle, circleVertexCount } from '@typegpu/geometry';
-import tgpu, { d, std as s } from 'typegpu';
+import tgpu, { common, d, std as s } from 'typegpu';
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
const canvas = document.querySelector('canvas');
@@ -128,29 +128,34 @@ const pipeline = root.createRenderPipeline({
multisample: { count: multisample ? 4 : 1 },
});
-setTimeout(() => {
- pipeline
- .with(uniformsBindGroup)
- .withColorAttachment({
- ...(multisample
- ? {
- view: msaaTextureView,
- resolveTarget: context,
- }
- : { view: context }),
- clearValue: [0, 0, 0, 0],
- loadOp: 'clear',
- storeOp: 'store',
- })
- .withPerformanceCallback((a, b) => {
- console.log((Number(b - a) * 1e-6).toFixed(3), 'ms');
- })
- .draw(circleVertexCount(4), circleCount);
-}, 100);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ pipeline
+ .with(uniformsBindGroup)
+ .withColorAttachment({
+ ...(multisample
+ ? {
+ view: msaaTextureView,
+ resolveTarget: context,
+ }
+ : { view: context }),
+ clearValue: [0, 0, 0, 0],
+ loadOp: 'clear',
+ storeOp: 'store',
+ })
+ .withPerformanceCallback((a, b) => {
+ console.log((Number(b - a) * 1e-6).toFixed(3), 'ms');
+ })
+ .draw(circleVertexCount(4), circleCount);
+ },
+});
// #region Example controls & Cleanup
export function onCleanup() {
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/geometry/lines-combinations/index.ts b/apps/typegpu-docs/src/examples/geometry/lines-combinations/index.ts
index 074dec69ee..a2252d8553 100644
--- a/apps/typegpu-docs/src/examples/geometry/lines-combinations/index.ts
+++ b/apps/typegpu-docs/src/examples/geometry/lines-combinations/index.ts
@@ -10,7 +10,7 @@ import {
startCapSlot,
arrowCapParamsSlot,
} from '@typegpu/geometry';
-import tgpu, { type ColorAttachment } from 'typegpu';
+import tgpu, { common, type ColorAttachment } from 'typegpu';
import {
arrayOf,
builtin,
@@ -409,6 +409,8 @@ const runAnimationFrame = (timeMs: number) => {
};
runAnimationFrame(0);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
const fillOptions = {
none: 0,
solid: 1,
@@ -505,6 +507,7 @@ export const controls = defineControls({
});
export function onCleanup() {
+ detachAutoResizer();
root.destroy();
cancelAnimationFrame(frameId);
}
diff --git a/apps/typegpu-docs/src/examples/image-processing/ascii-filter/index.ts b/apps/typegpu-docs/src/examples/image-processing/ascii-filter/index.ts
index 0adc3b6b4f..8a41daff68 100644
--- a/apps/typegpu-docs/src/examples/image-processing/ascii-filter/index.ts
+++ b/apps/typegpu-docs/src/examples/image-processing/ascii-filter/index.ts
@@ -241,6 +241,11 @@ if (isIOS) {
videoFrameCallbackId = video.requestVideoFrameCallback(processVideoFrame);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+});
+
export const controls = defineControls({
'use extended characters': {
initial: false,
@@ -279,5 +284,6 @@ export function onCleanup() {
}
}
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/image-processing/background-segmentation/index.ts b/apps/typegpu-docs/src/examples/image-processing/background-segmentation/index.ts
index 0c08feb8f6..9c44286576 100644
--- a/apps/typegpu-docs/src/examples/image-processing/background-segmentation/index.ts
+++ b/apps/typegpu-docs/src/examples/image-processing/background-segmentation/index.ts
@@ -295,6 +295,11 @@ async function processVideoFrame(_: number, metadata: VideoFrameCallbackMetadata
}
videoFrameCallbackId = video.requestVideoFrameCallback(processVideoFrame);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+});
+
// #region Example controls & Cleanup
export const controls = defineControls({
@@ -355,6 +360,7 @@ export function onCleanup() {
adapter.requestDevice = oldRequestDevice;
}
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/image-processing/blur/index.ts b/apps/typegpu-docs/src/examples/image-processing/blur/index.ts
index a6bd7bd0de..f31f1ea4b6 100644
--- a/apps/typegpu-docs/src/examples/image-processing/blur/index.ts
+++ b/apps/typegpu-docs/src/examples/image-processing/blur/index.ts
@@ -171,7 +171,13 @@ function render() {
renderPipeline.withColorAttachment({ view: context }).draw(3);
}
-render();
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ render();
+ },
+});
// #region Example controls & Cleanup
@@ -200,6 +206,7 @@ export const controls = defineControls({
});
export function onCleanup() {
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/image-processing/camera-thresholding/index.ts b/apps/typegpu-docs/src/examples/image-processing/camera-thresholding/index.ts
index b72f79e1fb..edf4576432 100644
--- a/apps/typegpu-docs/src/examples/image-processing/camera-thresholding/index.ts
+++ b/apps/typegpu-docs/src/examples/image-processing/camera-thresholding/index.ts
@@ -133,6 +133,11 @@ function processVideoFrame(_: number, metadata: VideoFrameCallbackMetadata) {
}
videoFrameCallbackId = video.requestVideoFrameCallback(processVideoFrame);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+});
+
// #region Example controls & Cleanup
export const controls = defineControls({
@@ -160,6 +165,7 @@ export function onCleanup() {
track.stop();
}
}
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/image-processing/chroma-keying/index.ts b/apps/typegpu-docs/src/examples/image-processing/chroma-keying/index.ts
index 498c3cf8b6..cae9ffc6cf 100644
--- a/apps/typegpu-docs/src/examples/image-processing/chroma-keying/index.ts
+++ b/apps/typegpu-docs/src/examples/image-processing/chroma-keying/index.ts
@@ -125,6 +125,11 @@ function processVideoFrame(_: number, metadata: VideoFrameCallbackMetadata) {
videoFrameCallbackId = video.requestVideoFrameCallback(processVideoFrame);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+});
+
// #region Example controls & Cleanup
export const controls = defineControls({
@@ -152,6 +157,7 @@ export function onCleanup() {
track.stop();
}
}
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/image-processing/image-tuning/index.ts b/apps/typegpu-docs/src/examples/image-processing/image-tuning/index.ts
index 4720c0aeb9..f4d60a043b 100644
--- a/apps/typegpu-docs/src/examples/image-processing/image-tuning/index.ts
+++ b/apps/typegpu-docs/src/examples/image-processing/image-tuning/index.ts
@@ -344,10 +344,17 @@ export const controls = defineControls({
},
});
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ render();
+ },
+});
+
export function onCleanup() {
+ detachAutoResizer();
root.destroy();
}
// #endregion
-
-render();
diff --git a/apps/typegpu-docs/src/examples/rendering/3d-fish/index.ts b/apps/typegpu-docs/src/examples/rendering/3d-fish/index.ts
index e94a1d7b5d..aa04d7e9bf 100644
--- a/apps/typegpu-docs/src/examples/rendering/3d-fish/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/3d-fish/index.ts
@@ -1,5 +1,5 @@
import { randf } from '@typegpu/noise';
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
import * as m from 'wgpu-matrix';
import { simulate } from './compute.ts';
import { loadModel } from './load-model.ts';
@@ -421,23 +421,26 @@ window.addEventListener('touchmove', touchMoveEventListener);
// observer and cleanup
-const resizeObserver = new ResizeObserver(() => {
- camera.projection = m.mat4.perspective(
- Math.PI / 4,
- canvas.clientWidth / canvas.clientHeight,
- 0.1,
- 1000,
- d.mat4x4f(),
- );
-
- depthTexture.destroy();
- depthTexture = root.device.createTexture({
- size: [canvas.width, canvas.height, 1],
- format: 'depth24plus',
- usage: GPUTextureUsage.RENDER_ATTACHMENT,
- });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ camera.projection = m.mat4.perspective(
+ Math.PI / 4,
+ canvas.clientWidth / canvas.clientHeight,
+ 0.1,
+ 1000,
+ d.mat4x4f(),
+ );
+
+ depthTexture.destroy();
+ depthTexture = root.device.createTexture({
+ size: [canvas.width, canvas.height, 1],
+ format: 'depth24plus',
+ usage: GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+ },
});
-resizeObserver.observe(canvas);
export function onCleanup() {
disposed = true;
@@ -445,7 +448,7 @@ export function onCleanup() {
window.removeEventListener('mouseup', mouseUpEventListener);
window.removeEventListener('mousemove', mouseMoveEventListener);
window.removeEventListener('touchmove', touchMoveEventListener);
- resizeObserver.disconnect();
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/box-raytracing/index.ts b/apps/typegpu-docs/src/examples/rendering/box-raytracing/index.ts
index b75bb209ef..3e6d6011e0 100644
--- a/apps/typegpu-docs/src/examples/rendering/box-raytracing/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/box-raytracing/index.ts
@@ -1,5 +1,5 @@
import { linearToSrgb, srgbToLinear } from '@typegpu/color';
-import tgpu, { d, type TgpuFragmentFn, type TgpuVertexFn } from 'typegpu';
+import tgpu, { common, d, type TgpuFragmentFn, type TgpuVertexFn } from 'typegpu';
import { discard, max, min, mul, normalize, pow, sub } from 'typegpu/std';
import { mat4 } from 'wgpu-matrix';
import { defineControls } from '../../common/defineControls.ts';
@@ -274,6 +274,8 @@ const runner = (timestamp: number) => {
};
animationFrameId = requestAnimationFrame(runner);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
// #region Example controls and cleanup
export const controls = defineControls({
@@ -320,6 +322,7 @@ export const controls = defineControls({
export function onCleanup() {
cancelAnimationFrame(animationFrameId);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/caustics/index.ts b/apps/typegpu-docs/src/examples/rendering/caustics/index.ts
index 5b970d312b..3f34e808e3 100644
--- a/apps/typegpu-docs/src/examples/rendering/caustics/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/caustics/index.ts
@@ -1,5 +1,5 @@
import { perlin3d } from '@typegpu/noise';
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
import { defineControls } from '../../common/defineControls.ts';
const mainVertex = tgpu.vertexFn({
@@ -146,6 +146,8 @@ function draw(timestamp: number) {
}
requestAnimationFrame(draw);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
// #region Example controls and cleanup
export const controls = defineControls({
@@ -162,6 +164,7 @@ export const controls = defineControls({
export function onCleanup() {
isRunning = false;
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/clouds/index.ts b/apps/typegpu-docs/src/examples/rendering/clouds/index.ts
index dd23f5f385..03de360513 100644
--- a/apps/typegpu-docs/src/examples/rendering/clouds/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/clouds/index.ts
@@ -109,6 +109,8 @@ function render(timestamp: number) {
frameId = requestAnimationFrame(render);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
const qualityOptions = {
'very high': {
maxSteps: 150,
@@ -145,5 +147,6 @@ export const controls = defineControls({
export function onCleanup() {
cancelAnimationFrame(frameId);
resizeObserver.disconnect();
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/disco/index.ts b/apps/typegpu-docs/src/examples/rendering/disco/index.ts
index 85ecec9513..303b42da3b 100644
--- a/apps/typegpu-docs/src/examples/rendering/disco/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/disco/index.ts
@@ -1,4 +1,4 @@
-import tgpu, { d } from 'typegpu';
+import tgpu, { common, d } from 'typegpu';
import { resolutionAccess, timeAccess } from './consts.ts';
import {
mainFragment1,
@@ -61,8 +61,14 @@ function render(timestamp: number) {
frameId = requestAnimationFrame(render);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+});
+
export function onCleanup() {
cancelAnimationFrame(frameId);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/function-visualizer/index.ts b/apps/typegpu-docs/src/examples/rendering/function-visualizer/index.ts
index b426e67e53..42aaf2691c 100644
--- a/apps/typegpu-docs/src/examples/rendering/function-visualizer/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/function-visualizer/index.ts
@@ -1,5 +1,5 @@
import type { TgpuGuardedComputePipeline, TgpuRawCodeSnippet } from 'typegpu';
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
import { mat4 } from 'wgpu-matrix';
import { defineControls } from '../../common/defineControls.ts';
@@ -398,6 +398,8 @@ const resizeObserver = new ResizeObserver(() => {
resizeObserver.observe(canvas);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
// #region Example controls and cleanup
export const controls = defineControls({
@@ -457,6 +459,7 @@ export function onCleanup() {
window.removeEventListener('touchmove', touchMoveEventListener);
window.removeEventListener('touchend', touchEndEventListener);
resizeObserver.disconnect();
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/jelly-slider/index.ts b/apps/typegpu-docs/src/examples/rendering/jelly-slider/index.ts
index 36f5ba3c56..05dc5f9070 100644
--- a/apps/typegpu-docs/src/examples/rendering/jelly-slider/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/jelly-slider/index.ts
@@ -792,10 +792,13 @@ function handleResize() {
bindGroups = createBindGroups();
}
-const resizeObserver = new ResizeObserver(() => {
- handleResize();
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ handleResize();
+ },
});
-resizeObserver.observe(canvas);
animationFrameHandle = requestAnimationFrame(render);
@@ -906,7 +909,7 @@ export const controls = defineControls({
export function onCleanup() {
cancelAnimationFrame(animationFrameHandle);
- resizeObserver.disconnect();
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/jelly-switch/index.ts b/apps/typegpu-docs/src/examples/rendering/jelly-switch/index.ts
index a84d6f9e2f..940a662671 100644
--- a/apps/typegpu-docs/src/examples/rendering/jelly-switch/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/jelly-switch/index.ts
@@ -545,10 +545,13 @@ function handleResize() {
bindGroups = createBindGroups();
}
-const resizeObserver = new ResizeObserver(() => {
- handleResize();
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ handleResize();
+ },
});
-resizeObserver.observe(canvas);
animationFrameHandle = requestAnimationFrame(render);
@@ -684,7 +687,7 @@ export const controls = defineControls({
export function onCleanup() {
cancelAnimationFrame(animationFrameHandle);
- resizeObserver.disconnect();
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/perlin-noise/index.ts b/apps/typegpu-docs/src/examples/rendering/perlin-noise/index.ts
index 9d3de4ec8e..21c8622e9b 100644
--- a/apps/typegpu-docs/src/examples/rendering/perlin-noise/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/perlin-noise/index.ts
@@ -80,6 +80,8 @@ function draw(timestamp: number) {
requestAnimationFrame(draw);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
export const controls = defineControls({
'grid size': {
initial: '4',
@@ -109,5 +111,6 @@ export const controls = defineControls({
export function onCleanup() {
isRunning = false;
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/phong-reflection/index.ts b/apps/typegpu-docs/src/examples/rendering/phong-reflection/index.ts
index 5005a7cdd6..c217e87c3f 100644
--- a/apps/typegpu-docs/src/examples/rendering/phong-reflection/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/phong-reflection/index.ts
@@ -1,4 +1,4 @@
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
import * as p from './params.ts';
import {
ExampleControls,
@@ -175,10 +175,13 @@ const resizeObserver = new ResizeObserver(() => {
});
resizeObserver.observe(canvas);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
export function onCleanup() {
cancelAnimationFrame(frameId);
cleanupCamera();
resizeObserver.unobserve(canvas);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/point-light-shadow/index.ts b/apps/typegpu-docs/src/examples/rendering/point-light-shadow/index.ts
index 6a047a4580..c317000cc9 100644
--- a/apps/typegpu-docs/src/examples/rendering/point-light-shadow/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/point-light-shadow/index.ts
@@ -420,13 +420,10 @@ function render(timestamp: number) {
}
animationFrameId = requestAnimationFrame(render);
-const resizeObserver = new ResizeObserver((entries) => {
- for (const entry of entries) {
- const width = entry.contentBoxSize[0].inlineSize;
- const height = entry.contentBoxSize[0].blockSize;
- canvas.width = Math.max(1, Math.min(width, device.limits.maxTextureDimension2D));
- canvas.height = Math.max(1, Math.min(height, device.limits.maxTextureDimension2D));
-
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
depthTexture = root
.createTexture({
size: [canvas.width, canvas.height],
@@ -441,9 +438,8 @@ const resizeObserver = new ResizeObserver((entries) => {
sampleCount: 4,
})
.$usage('render');
- }
+ },
});
-resizeObserver.observe(canvas);
const initialCamPos = { x: 5, y: 5, z: -5 };
let theta = Math.atan2(initialCamPos.z, initialCamPos.x);
@@ -651,7 +647,7 @@ export function onCleanup() {
window.removeEventListener('mousemove', mouseMoveEventListener);
window.removeEventListener('touchend', touchEndEventListener);
window.removeEventListener('touchmove', touchMoveEventListener);
- resizeObserver.disconnect();
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/pom/index.ts b/apps/typegpu-docs/src/examples/rendering/pom/index.ts
index 7778bd2e79..d2632fc2a5 100644
--- a/apps/typegpu-docs/src/examples/rendering/pom/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/pom/index.ts
@@ -1,5 +1,5 @@
import { randf } from '@typegpu/noise';
-import tgpu, { d, std, type RenderFlag, type TgpuTexture } from 'typegpu';
+import tgpu, { common, d, std, type RenderFlag, type TgpuTexture } from 'typegpu';
import { Camera, setupOrbitCamera } from '../../common/setup-orbit-camera.ts';
import { defineControls } from '../../common/defineControls.ts';
import {
@@ -483,13 +483,18 @@ export const controls = defineControls({
},
});
-const resizeObserver = new ResizeObserver(createDepthTexture);
-resizeObserver.observe(canvas);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ createDepthTexture();
+ },
+});
export function onCleanup() {
cancelAnimationFrame(frameId);
cleanupCamera();
- resizeObserver.unobserve(canvas);
+ detachAutoResizer();
depthTexture.destroy();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/index.ts b/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/index.ts
index 9d317a16a5..dedad17c0b 100644
--- a/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/index.ts
@@ -215,6 +215,8 @@ function frame(timestamp: number) {
frameId = requestAnimationFrame(frame);
}
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
// #region Example controls and cleanup
export const controls = defineControls({
@@ -247,6 +249,7 @@ export const controls = defineControls({
export function onCleanup() {
cancelAnimationFrame(frameId);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/radiance-cascades/index.ts b/apps/typegpu-docs/src/examples/rendering/radiance-cascades/index.ts
index 2895fbc41e..04e8dcf39f 100644
--- a/apps/typegpu-docs/src/examples/rendering/radiance-cascades/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/radiance-cascades/index.ts
@@ -475,15 +475,18 @@ const dragController = new DragController(canvas, onDrag, onDrag);
// #region Example controls and cleanup
let resizeTimeout: ReturnType;
-const resizeObserver = new ResizeObserver(() => {
- clearTimeout(resizeTimeout);
- resizeTimeout = setTimeout(recreateSizedResources, 100);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ clearTimeout(resizeTimeout);
+ resizeTimeout = setTimeout(recreateSizedResources, 100);
+ },
});
-resizeObserver.observe(canvas);
export function onCleanup() {
dragController.destroy();
- resizeObserver.disconnect();
+ detachAutoResizer();
clearTimeout(resizeTimeout);
if (frameId !== null) {
cancelAnimationFrame(frameId);
diff --git a/apps/typegpu-docs/src/examples/rendering/ray-marching/index.ts b/apps/typegpu-docs/src/examples/rendering/ray-marching/index.ts
index fd87b161d9..e7ccc894af 100644
--- a/apps/typegpu-docs/src/examples/rendering/ray-marching/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/ray-marching/index.ts
@@ -1,5 +1,5 @@
import { sdBoxFrame3d, sdPlane, sdSphere } from '@typegpu/sdf';
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
const root = await tgpu.init();
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
@@ -232,7 +232,13 @@ function run(timestamp: number) {
animationFrame = requestAnimationFrame(run);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+});
+
export function onCleanup() {
cancelAnimationFrame(animationFrame);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/render-bundles-with/index.ts b/apps/typegpu-docs/src/examples/rendering/render-bundles-with/index.ts
index ac7bf9f395..abe3e16944 100644
--- a/apps/typegpu-docs/src/examples/rendering/render-bundles-with/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/render-bundles-with/index.ts
@@ -1,5 +1,5 @@
import { perlin2d } from '@typegpu/noise';
-import tgpu, { d } from 'typegpu';
+import tgpu, { common, d } from 'typegpu';
import * as m from 'wgpu-matrix';
import { defineControls } from '../../common/defineControls.ts';
import { setupOrbitCamera } from '../../common/setup-orbit-camera.ts';
@@ -197,6 +197,8 @@ function frame() {
requestAnimationFrame(frame);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
// #region Example controls and cleanup
export const controls = defineControls({
@@ -220,5 +222,6 @@ export function onCleanup() {
cleanupCamera();
perlinCache.destroy();
depthTexture.destroy();
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/render-bundles/index.ts b/apps/typegpu-docs/src/examples/rendering/render-bundles/index.ts
index dd0b073176..1a31f6fea2 100644
--- a/apps/typegpu-docs/src/examples/rendering/render-bundles/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/render-bundles/index.ts
@@ -1,5 +1,5 @@
import { perlin2d } from '@typegpu/noise';
-import tgpu, { d } from 'typegpu';
+import tgpu, { common, d } from 'typegpu';
import * as m from 'wgpu-matrix';
import { defineControls } from '../../common/defineControls.ts';
import { setupOrbitCamera } from '../../common/setup-orbit-camera.ts';
@@ -197,6 +197,11 @@ function frame() {
requestAnimationFrame(frame);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+});
+
// #region Example controls and cleanup
export const controls = defineControls({
@@ -218,6 +223,7 @@ export const controls = defineControls({
export function onCleanup() {
disposed = true;
cleanupCamera();
+ detachAutoResizer();
perlinCache.destroy();
depthTexture.destroy();
root.destroy();
diff --git a/apps/typegpu-docs/src/examples/rendering/simple-shadow/index.ts b/apps/typegpu-docs/src/examples/rendering/simple-shadow/index.ts
index 33fb648256..82d5bebe47 100644
--- a/apps/typegpu-docs/src/examples/rendering/simple-shadow/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/simple-shadow/index.ts
@@ -1,4 +1,4 @@
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
import { mat4 } from 'wgpu-matrix';
import { createCuboid, createPlane } from './geometry.ts';
import {
@@ -340,21 +340,24 @@ function render() {
}
frameId = requestAnimationFrame(render);
-const resizeObserver = new ResizeObserver(() => {
- canvasTextures = createCanvasTextures();
-
- const newProjection = mat4.perspective(
- Math.PI / 4,
- canvas.width / canvas.height,
- 0.1,
- 100,
- d.mat4x4f(),
- );
- cameraUniform.patch({
- projection: newProjection,
- });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ canvasTextures = createCanvasTextures();
+
+ const newProjection = mat4.perspective(
+ Math.PI / 4,
+ canvas.width / canvas.height,
+ 0.1,
+ 100,
+ d.mat4x4f(),
+ );
+ cameraUniform.patch({
+ projection: newProjection,
+ });
+ },
});
-resizeObserver.observe(canvas);
export const controls = defineControls({
'camera X': {
@@ -451,6 +454,6 @@ export function onCleanup() {
if (frameId !== null) {
cancelAnimationFrame(frameId);
}
- resizeObserver.disconnect();
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/smoky-triangle/index.ts b/apps/typegpu-docs/src/examples/rendering/smoky-triangle/index.ts
index bb750e7a7e..5e085f56d3 100644
--- a/apps/typegpu-docs/src/examples/rendering/smoky-triangle/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/smoky-triangle/index.ts
@@ -1,5 +1,5 @@
import { perlin3d } from '@typegpu/noise';
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
const Params = d.struct({
fromColor: d.vec3f,
@@ -93,6 +93,8 @@ function frame(timestamp: number) {
}
frameId = requestAnimationFrame(frame);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
export const controls = {
Distortion: {
initial: 0.05,
@@ -164,5 +166,6 @@ export const controls = {
export function onCleanup() {
cancelAnimationFrame(frameId);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/suika-sdf/index.ts b/apps/typegpu-docs/src/examples/rendering/suika-sdf/index.ts
index c2c5b6450a..37fce94715 100644
--- a/apps/typegpu-docs/src/examples/rendering/suika-sdf/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/suika-sdf/index.ts
@@ -1,5 +1,5 @@
import * as sdf from '@typegpu/sdf';
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
import { fullScreenTriangle } from 'typegpu/common';
import {
DROP_Y,
@@ -414,18 +414,21 @@ const renderPipeline = root.createRenderPipeline({
},
});
-const resizeObserver = new ResizeObserver(() => {
- mergedField.distance.destroy();
- mergedField.info.destroy();
- mergedField = createMergedFieldResources();
- distanceView = mergedField.distance.createView(d.texture2d());
- infoView = mergedField.info.createView(d.texture2d());
- mergedFieldBindGroup = root.createBindGroup(mergedFieldLayout, {
- distance: distanceView,
- info: infoView,
- });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ mergedField.distance.destroy();
+ mergedField.info.destroy();
+ mergedField = createMergedFieldResources();
+ distanceView = mergedField.distance.createView(d.texture2d());
+ infoView = mergedField.info.createView(d.texture2d());
+ mergedFieldBindGroup = root.createBindGroup(mergedFieldLayout, {
+ distance: distanceView,
+ info: infoView,
+ });
+ },
});
-resizeObserver.observe(canvas);
canvas.addEventListener('touchstart', (e) => e.preventDefault(), { passive: false, signal });
canvas.addEventListener('touchmove', (e) => e.preventDefault(), { passive: false, signal });
@@ -676,6 +679,6 @@ export const controls = defineControls({
export function onCleanup() {
cleanupController.abort();
cancelAnimationFrame(animationFrameId);
- resizeObserver.disconnect();
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/two-boxes/index.ts b/apps/typegpu-docs/src/examples/rendering/two-boxes/index.ts
index feecac1b2a..11f52c24ee 100644
--- a/apps/typegpu-docs/src/examples/rendering/two-boxes/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/two-boxes/index.ts
@@ -1,5 +1,5 @@
import type { RenderFlag, TgpuBindGroup, TgpuBuffer, TgpuTexture, VertexFlag } from 'typegpu';
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
import * as m from 'wgpu-matrix';
// Initialization
@@ -465,6 +465,8 @@ const resizeObserver = new ResizeObserver(() => {
});
resizeObserver.observe(canvas);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
export function onCleanup() {
cancelAnimationFrame(animationFrameId);
window.removeEventListener('mouseup', mouseUpEventListener);
@@ -472,6 +474,7 @@ export function onCleanup() {
window.removeEventListener('touchend', touchEndEventListener);
window.removeEventListener('touchmove', touchMoveEventListener);
resizeObserver.disconnect();
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/xor-dev-centrifuge-2/index.ts b/apps/typegpu-docs/src/examples/rendering/xor-dev-centrifuge-2/index.ts
index 20dbe57d37..26809424c9 100644
--- a/apps/typegpu-docs/src/examples/rendering/xor-dev-centrifuge-2/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/xor-dev-centrifuge-2/index.ts
@@ -10,7 +10,7 @@
* ```
*/
-import tgpu, { d } from 'typegpu';
+import tgpu, { common, d } from 'typegpu';
import { abs, atan2, cos, gt, length, normalize, select, sign, tanh } from 'typegpu/std';
import { defineControls } from '../../common/defineControls.ts';
@@ -120,6 +120,8 @@ function draw(timestamp: number) {
requestAnimationFrame(draw);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
// #region Example controls and cleanup
export const controls = defineControls({
@@ -178,6 +180,7 @@ export const controls = defineControls({
export function onCleanup() {
isRunning = false;
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/xor-dev-runner/index.ts b/apps/typegpu-docs/src/examples/rendering/xor-dev-runner/index.ts
index 99225f1ed4..0d660e6c94 100644
--- a/apps/typegpu-docs/src/examples/rendering/xor-dev-runner/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/xor-dev-runner/index.ts
@@ -12,7 +12,7 @@
* ```
*/
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
import { abs, add, cos, max, min, mul, select, sign, sin, sub, tanh } from 'typegpu/std';
import { defineControls } from '../../common/defineControls.ts';
import { Camera, setupFirstPersonCamera } from '../../common/setup-first-person-camera.ts';
@@ -172,6 +172,11 @@ function draw() {
requestAnimationFrame(draw);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+});
+
// #region Example controls and cleanup
export const controls = defineControls({
@@ -210,6 +215,7 @@ export const controls = defineControls({
export function onCleanup() {
isRunning = false;
cleanupCamera();
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simple/gradient-tiles/index.ts b/apps/typegpu-docs/src/examples/simple/gradient-tiles/index.ts
index f53a95ac67..8063b03bea 100644
--- a/apps/typegpu-docs/src/examples/simple/gradient-tiles/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/gradient-tiles/index.ts
@@ -33,7 +33,13 @@ function draw(spanXValue: number, spanYValue: number) {
let spanX = 10;
let spanY = 10;
-draw(spanX, spanY);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ draw(spanX, spanY);
+ },
+});
// #region Example controls and cleanup
@@ -62,6 +68,7 @@ export const controls = defineControls({
});
export function onCleanup() {
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simple/liquid-glass/index.ts b/apps/typegpu-docs/src/examples/simple/liquid-glass/index.ts
index fd572f9cef..c5e0a8ee27 100644
--- a/apps/typegpu-docs/src/examples/simple/liquid-glass/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/liquid-glass/index.ts
@@ -187,6 +187,8 @@ function render() {
}
frameId = requestAnimationFrame(render);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
export const controls = defineControls({
'Rectangle dims': {
initial: defaultParams.rectDims,
@@ -305,5 +307,6 @@ export function onCleanup() {
cancelAnimationFrame(frameId);
}
window.removeEventListener('mousemove', handleMouseMove);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simple/mesh-skinning/index.ts b/apps/typegpu-docs/src/examples/simple/mesh-skinning/index.ts
index 397903641f..da9da9c0e2 100644
--- a/apps/typegpu-docs/src/examples/simple/mesh-skinning/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/mesh-skinning/index.ts
@@ -525,6 +525,8 @@ function render(frameTimeMs: number) {
let animationId: number | undefined;
animationId = requestAnimationFrame(render);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
export const controls = defineControls({
Animation: {
initial: toLabel(selectedVariant.id),
@@ -561,5 +563,6 @@ export function onCleanup() {
}
resizeObserver.disconnect();
cleanupCamera();
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simple/oklab/index.ts b/apps/typegpu-docs/src/examples/simple/oklab/index.ts
index 41dc549df1..4bb3a9798d 100644
--- a/apps/typegpu-docs/src/examples/simple/oklab/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/oklab/index.ts
@@ -143,9 +143,13 @@ function draw() {
pipeline.withColorAttachment({ view: context }).draw(3);
}
-setTimeout(() => {
- draw();
-}, 100);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ draw();
+ },
+});
// #region Example controls and cleanup
@@ -211,6 +215,7 @@ export const controls = defineControls({
});
export function onCleanup() {
+ detachAutoResizer();
root.destroy();
cleanupController.abort();
}
diff --git a/apps/typegpu-docs/src/examples/simple/ripple-cube/index.ts b/apps/typegpu-docs/src/examples/simple/ripple-cube/index.ts
index 1ddb0d0a29..198e228bba 100644
--- a/apps/typegpu-docs/src/examples/simple/ripple-cube/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/ripple-cube/index.ts
@@ -1,6 +1,6 @@
import { perlin3d, randf } from '@typegpu/noise';
import * as sdf from '@typegpu/sdf';
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
import { Camera, setupOrbitCamera } from '../../common/setup-orbit-camera.ts';
import { createBackgroundCubemap } from './background.ts';
import { GRID_SIZE, halton, LIGHT_COUNT, MAX_DIST, MAX_STEPS, SURF_DIST } from './constants.ts';
@@ -223,6 +223,8 @@ function run(timestamp: number) {
animationFrame = requestAnimationFrame(run);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
export const controls = defineControls({
metallic: {
min: 0,
@@ -299,5 +301,6 @@ export const controls = defineControls({
export function onCleanup() {
cancelAnimationFrame(animationFrame);
cameraResult.cleanupCamera();
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simple/square/index.ts b/apps/typegpu-docs/src/examples/simple/square/index.ts
index f4aa851d4d..fb4b115495 100644
--- a/apps/typegpu-docs/src/examples/simple/square/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/square/index.ts
@@ -1,4 +1,4 @@
-import tgpu, { d } from 'typegpu';
+import tgpu, { common, d } from 'typegpu';
import { defineControls } from '../../common/defineControls.ts';
const root = await tgpu.init();
@@ -61,7 +61,14 @@ const pipeline = root
function render() {
pipeline.with(vertexLayout, colorBuffer).withColorAttachment({ view: context }).drawIndexed(6);
}
-render();
+
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ render();
+ },
+});
// #region Example controls & Cleanup
@@ -92,6 +99,7 @@ export const controls = defineControls({
});
export function onCleanup() {
+ detachAutoResizer();
root.destroy();
}
// #endregion
diff --git a/apps/typegpu-docs/src/examples/simple/stencil/index.ts b/apps/typegpu-docs/src/examples/simple/stencil/index.ts
index 2cfdb28016..b37b88251d 100644
--- a/apps/typegpu-docs/src/examples/simple/stencil/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/stencil/index.ts
@@ -1,4 +1,4 @@
-import tgpu, { d } from 'typegpu';
+import tgpu, { common, d } from 'typegpu';
const root = await tgpu.init();
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
@@ -124,9 +124,12 @@ const resizeObserver = new ResizeObserver(() => {
});
resizeObserver.observe(canvas);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
export function onCleanup() {
if (frameId) {
cancelAnimationFrame(frameId);
}
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts b/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts
index 5dabe04333..b76ab18446 100644
--- a/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts
@@ -1,6 +1,6 @@
import { perlin3d } from '@typegpu/noise';
import { sdPlane } from '@typegpu/sdf';
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
import * as c from './constants.ts';
import { circles, grid } from './floor.ts';
@@ -157,10 +157,13 @@ function run(timestamp: number) {
animationFrame = requestAnimationFrame(run);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
// #region Example controls and cleanup
export function onCleanup() {
cancelAnimationFrame(animationFrame);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simulation/boids/index.ts b/apps/typegpu-docs/src/examples/simulation/boids/index.ts
index 0f484559cb..6be2f42814 100644
--- a/apps/typegpu-docs/src/examples/simulation/boids/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/boids/index.ts
@@ -1,4 +1,4 @@
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
import { defineControls } from '../../common/defineControls.ts';
const triangleAmount = 1000;
@@ -242,6 +242,8 @@ function frame() {
animationFrameId = requestAnimationFrame(frame);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
// #region Example controls and cleanup
export const controls = defineControls({
@@ -288,6 +290,7 @@ export const controls = defineControls({
export function onCleanup() {
cancelAnimationFrame(animationFrameId);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simulation/confetti/index.ts b/apps/typegpu-docs/src/examples/simulation/confetti/index.ts
index f92e441102..8aeeaa4ac7 100644
--- a/apps/typegpu-docs/src/examples/simulation/confetti/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/confetti/index.ts
@@ -1,4 +1,4 @@
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
import { defineControls } from '../../common/defineControls.ts';
// constants
@@ -151,6 +151,8 @@ const runner = (timestamp: number) => {
animationFrameId = requestAnimationFrame(runner);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
// example controls and cleanup
export const controls = defineControls({
@@ -161,5 +163,6 @@ export const controls = defineControls({
export function onCleanup() {
cancelAnimationFrame(animationFrameId);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.ts b/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.ts
index 06b40b268a..43d62216b7 100644
--- a/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.ts
@@ -1,5 +1,5 @@
import { randf } from '@typegpu/noise';
-import tgpu, { d, std, type TgpuBufferMutable, type TgpuBufferReadonly } from 'typegpu';
+import tgpu, { common, d, std, type TgpuBufferMutable, type TgpuBufferReadonly } from 'typegpu';
import { defineControls } from '../../common/defineControls.ts';
const MAX_GRID_SIZE = 1024;
@@ -537,6 +537,8 @@ const runner = (timestamp: number) => {
animationFrameId = requestAnimationFrame(runner);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
export const controls = defineControls({
'source intensity': {
initial: sourceIntensity,
@@ -598,5 +600,6 @@ export const controls = defineControls({
export function onCleanup() {
cancelAnimationFrame(animationFrameId);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simulation/fluid-with-atomics/index.ts b/apps/typegpu-docs/src/examples/simulation/fluid-with-atomics/index.ts
index 3e550a0eff..0e3b3f5733 100644
--- a/apps/typegpu-docs/src/examples/simulation/fluid-with-atomics/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/fluid-with-atomics/index.ts
@@ -625,6 +625,8 @@ function run(timestamp: number) {
}
animationFrame = requestAnimationFrame(run);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
export const controls = defineControls({
size: {
initial: 32,
@@ -694,6 +696,7 @@ export const controls = defineControls({
export function onCleanup() {
cancelAnimationFrame(animationFrame);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simulation/game-of-life/index.ts b/apps/typegpu-docs/src/examples/simulation/game-of-life/index.ts
index f6276a9478..90ad1dd5bb 100644
--- a/apps/typegpu-docs/src/examples/simulation/game-of-life/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/game-of-life/index.ts
@@ -402,6 +402,8 @@ function frame(timestamp: number) {
}
frameId = requestAnimationFrame(frame);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
// #region Example controls & Cleanup
export const controls = defineControls({
@@ -506,6 +508,7 @@ export const controls = defineControls({
export function onCleanup() {
cancelAnimationFrame(frameId);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simulation/gravity/index.ts b/apps/typegpu-docs/src/examples/simulation/gravity/index.ts
index 39601f5c9e..4e4b57a18f 100644
--- a/apps/typegpu-docs/src/examples/simulation/gravity/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/gravity/index.ts
@@ -1,4 +1,4 @@
-import tgpu, { d, type StorageFlag, type TgpuBindGroup, type TgpuBuffer } from 'typegpu';
+import tgpu, { common, d, type StorageFlag, type TgpuBindGroup, type TgpuBuffer } from 'typegpu';
import { computeCollisionsShader, computeGravityShader } from './compute.ts';
import {
collisionBehaviors,
@@ -260,6 +260,8 @@ const resizeObserver = new ResizeObserver(() => {
});
resizeObserver.observe(canvas);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
function hideHelp() {
const helpElem = document.getElementById('help');
if (helpElem) {
@@ -274,6 +276,7 @@ export function onCleanup() {
destroyed = true;
cleanupCamera();
resizeObserver.unobserve(canvas);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts
index dbe6d00016..746a3ef6c9 100644
--- a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts
@@ -471,6 +471,8 @@ function frame(timestamp: number) {
}
requestAnimationFrame(frame);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
// #region Example controls and cleanup
let isDragging = false;
@@ -620,6 +622,7 @@ export const controls = defineControls({
});
export function onCleanup() {
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts b/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts
index ecc3184d6f..1b9dac673d 100644
--- a/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts
@@ -1,5 +1,5 @@
import { randf } from '@typegpu/noise';
-import tgpu, { d, std } from 'typegpu';
+import tgpu, { common, d, std } from 'typegpu';
import { defineControls } from '../../common/defineControls.ts';
const root = await tgpu.init();
@@ -243,6 +243,8 @@ function frame(now: number) {
}
requestAnimationFrame(frame);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
// #region Example controls and cleanup
export const controls = defineControls({
@@ -294,6 +296,7 @@ export const controls = defineControls({
});
export function onCleanup() {
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simulation/stable-fluid/index.ts b/apps/typegpu-docs/src/examples/simulation/stable-fluid/index.ts
index 420c81d294..679013a5f9 100644
--- a/apps/typegpu-docs/src/examples/simulation/stable-fluid/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/stable-fluid/index.ts
@@ -1,4 +1,5 @@
import tgpu, {
+ common,
d,
type TgpuBindGroup,
type TgpuComputeFn,
@@ -362,6 +363,8 @@ function loop() {
loop();
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
// #region Example controls and cleanup
canvas.addEventListener('mousedown', (e) => {
@@ -484,6 +487,7 @@ export const controls = defineControls({
export function onCleanup() {
window.removeEventListener('mouseup', mouseUpEventListener);
window.removeEventListener('touchend', touchEndEventListener);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simulation/wind-map/index.ts b/apps/typegpu-docs/src/examples/simulation/wind-map/index.ts
index 2ac9277b7c..90a75698cb 100644
--- a/apps/typegpu-docs/src/examples/simulation/wind-map/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/wind-map/index.ts
@@ -6,7 +6,7 @@ import {
polylineVariableWidth,
startCapSlot,
} from '@typegpu/geometry';
-import tgpu from 'typegpu';
+import tgpu, { common } from 'typegpu';
import { arrayOf, builtin, f32, i32, struct, u16, u32, vec2f, vec4f } from 'typegpu/data';
import { clamp, mix, normalize, select } from 'typegpu/std';
import { defineControls } from '../../common/defineControls.ts';
@@ -267,6 +267,8 @@ const runAnimationFrame = () => {
};
runAnimationFrame();
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
// #region Example controls & Cleanup
export const controls = defineControls({
@@ -279,6 +281,7 @@ export const controls = defineControls({
});
export function onCleanup() {
+ detachAutoResizer();
root.destroy();
root.device.destroy();
cancelAnimationFrame(frameId);
diff --git a/apps/typegpu-docs/src/examples/tests/log-test/index.ts b/apps/typegpu-docs/src/examples/tests/log-test/index.ts
index 585a9bccbb..daf9edf0d2 100644
--- a/apps/typegpu-docs/src/examples/tests/log-test/index.ts
+++ b/apps/typegpu-docs/src/examples/tests/log-test/index.ts
@@ -1,4 +1,4 @@
-import tgpu, { d, std, type TgpuVertexFn } from 'typegpu';
+import tgpu, { common, d, std, type TgpuVertexFn } from 'typegpu';
import { defineControls } from '../../common/defineControls.ts';
const root = await tgpu.init({
@@ -288,7 +288,10 @@ export const controls = defineControls({
},
});
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
export function onCleanup() {
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/tests/rc-docs-examples/index.ts b/apps/typegpu-docs/src/examples/tests/rc-docs-examples/index.ts
index cd1b337b22..6f709488c8 100644
--- a/apps/typegpu-docs/src/examples/tests/rc-docs-examples/index.ts
+++ b/apps/typegpu-docs/src/examples/tests/rc-docs-examples/index.ts
@@ -270,6 +270,8 @@ function frame() {
frameId = requestAnimationFrame(frame);
}
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
function setSnippet(snippet: RcSnippet) {
snippetMode.write(RC_SNIPPETS.indexOf(snippet));
}
@@ -283,6 +285,7 @@ export const controls = defineControls({
});
export function onCleanup() {
+ detachAutoResizer();
cancelAnimationFrame(frameId);
basicRunner.destroy();
customRunner.destroy();
diff --git a/apps/typegpu-docs/src/examples/tests/sdf-docs-examples/index.ts b/apps/typegpu-docs/src/examples/tests/sdf-docs-examples/index.ts
index 8702000434..e360880d0f 100644
--- a/apps/typegpu-docs/src/examples/tests/sdf-docs-examples/index.ts
+++ b/apps/typegpu-docs/src/examples/tests/sdf-docs-examples/index.ts
@@ -291,6 +291,8 @@ function frame() {
frameId = requestAnimationFrame(frame);
}
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
function setSnippet(snippet: SdfSnippet) {
snippetMode.write(SDF_SNIPPETS.indexOf(snippet));
}
@@ -304,6 +306,7 @@ export const controls = defineControls({
});
export function onCleanup() {
+ detachAutoResizer();
cancelAnimationFrame(frameId);
floodRunner.destroy();
sourceTexture.destroy();
diff --git a/apps/typegpu-docs/src/examples/tests/texture-test/index.ts b/apps/typegpu-docs/src/examples/tests/texture-test/index.ts
index 9b0f7efccf..8bffe204fc 100644
--- a/apps/typegpu-docs/src/examples/tests/texture-test/index.ts
+++ b/apps/typegpu-docs/src/examples/tests/texture-test/index.ts
@@ -135,6 +135,8 @@ function render() {
}
requestAnimationFrame(render);
+const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+
export const controls = defineControls({
Format: {
initial: currentFormat,
@@ -179,5 +181,6 @@ export const controls = defineControls({
});
export function onCleanup() {
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/tests/uniformity/index.ts b/apps/typegpu-docs/src/examples/tests/uniformity/index.ts
index 62267e1c4a..191d81c369 100644
--- a/apps/typegpu-docs/src/examples/tests/uniformity/index.ts
+++ b/apps/typegpu-docs/src/examples/tests/uniformity/index.ts
@@ -83,14 +83,17 @@ export const controls = defineControls({
},
});
-const resizeObserver = new ResizeObserver(() => {
- canvasRatioUniform.write(canvas.width / canvas.height);
- redraw();
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ canvasRatioUniform.write(canvas.width / canvas.height);
+ redraw();
+ },
});
-resizeObserver.observe(canvas);
export function onCleanup() {
- resizeObserver.disconnect();
+ detachAutoResizer();
root.destroy();
}
From ab9aaa9915af69922473763eb61bc416797ef58e Mon Sep 17 00:00:00 2001
From: Iwo Plaza
Date: Tue, 16 Jun 2026 16:49:30 +0200
Subject: [PATCH 3/8] Update examples and not resizing canvases by default
---
.../src/components/ExampleView.tsx | 82 +--------
.../algorithms/bitonic-sort/index.html | 2 +-
.../algorithms/genetic-racing/index.html | 2 +-
.../algorithms/jump-flood-distance/index.html | 2 +-
.../algorithms/jump-flood-voronoi/index.html | 2 +-
.../algorithms/mnist-inference/index.html | 6 +-
.../algorithms/mnist-inference/index.ts | 5 +-
.../src/examples/geometry/circles/index.html | 2 +-
.../src/examples/geometry/circles/index.ts | 9 +-
.../geometry/lines-combinations/index.html | 2 +-
.../geometry/lines-combinations/index.ts | 16 +-
.../image-processing/ascii-filter/index.html | 2 +-
.../image-processing/ascii-filter/index.ts | 41 +++--
.../background-segmentation/index.html | 2 +-
.../background-segmentation/index.ts | 45 +++--
.../examples/image-processing/blur/index.html | 2 +-
.../examples/image-processing/blur/index.ts | 6 +
.../camera-thresholding/index.html | 2 +-
.../camera-thresholding/index.ts | 35 ++--
.../image-processing/chroma-keying/index.html | 2 +-
.../image-processing/chroma-keying/index.ts | 36 ++--
.../image-processing/image-tuning/index.html | 2 +-
.../image-processing/image-tuning/index.ts | 4 +
.../selfie-segmentation/index.html | 22 +--
.../selfie-segmentation/index.ts | 13 +-
.../react/shifting-gradient/index.tsx | 2 +-
.../react/spinning-triangle/index.tsx | 2 +-
.../src/examples/rendering/3d-fish/index.html | 2 +-
.../src/examples/rendering/3d-fish/index.ts | 42 +++--
.../rendering/box-raytracing/index.html | 2 +-
.../rendering/box-raytracing/index.ts | 18 +-
.../examples/rendering/caustics/index.html | 2 +-
.../src/examples/rendering/caustics/index.ts | 28 +++-
.../src/examples/rendering/clouds/index.html | 2 +-
.../src/examples/rendering/clouds/index.ts | 45 +++--
.../src/examples/rendering/clouds/types.ts | 1 +
.../rendering/cubemap-reflection/index.html | 2 +-
.../rendering/cubemap-reflection/index.ts | 2 +
.../src/examples/rendering/disco/index.html | 2 +-
.../src/examples/rendering/disco/index.ts | 22 ++-
.../rendering/function-visualizer/index.html | 2 +-
.../rendering/function-visualizer/index.ts | 31 ++--
.../rendering/jelly-slider/index.html | 2 +-
.../examples/rendering/jelly-slider/index.ts | 39 +++--
.../rendering/jelly-switch/index.html | 2 +-
.../examples/rendering/jelly-switch/index.ts | 35 ++--
.../examples/rendering/os-awards/index.html | 2 +-
.../src/examples/rendering/os-awards/index.ts | 42 +++--
.../rendering/perlin-noise/index.html | 2 +-
.../examples/rendering/perlin-noise/index.ts | 27 ++-
.../rendering/phong-reflection/index.html | 2 +-
.../rendering/phong-reflection/index.ts | 36 ++--
.../rendering/point-light-shadow/index.html | 2 +-
.../rendering/point-light-shadow/index.ts | 53 +++---
.../src/examples/rendering/pom/index.html | 2 +-
.../src/examples/rendering/pom/index.ts | 25 +--
.../drawInteraction.ts | 12 +-
.../radiance-cascades-drawing/index.html | 2 +-
.../radiance-cascades-drawing/index.ts | 23 ++-
.../rendering/radiance-cascades/index.html | 2 +-
.../rendering/radiance-cascades/index.ts | 5 +
.../rendering/ray-marching/index.html | 2 +-
.../rendering/render-bundles-with/index.html | 2 +-
.../rendering/render-bundles-with/index.ts | 22 ++-
.../rendering/render-bundles/index.html | 2 +-
.../rendering/render-bundles/index.ts | 13 +-
.../rendering/simple-shadow/index.html | 2 +-
.../examples/rendering/simple-shadow/index.ts | 16 +-
.../rendering/smoky-triangle/index.html | 2 +-
.../rendering/smoky-triangle/index.ts | 18 +-
.../examples/rendering/suika-sdf/index.html | 2 +-
.../src/examples/rendering/suika-sdf/index.ts | 33 ++--
.../examples/rendering/two-boxes/index.html | 2 +-
.../src/examples/rendering/two-boxes/index.ts | 19 ++-
.../rendering/xor-dev-centrifuge-2/index.html | 2 +-
.../rendering/xor-dev-runner/index.html | 2 +-
.../examples/simple/gradient-tiles/index.html | 2 +-
.../examples/simple/gradient-tiles/index.ts | 4 +
.../examples/simple/liquid-glass/index.html | 2 +-
.../src/examples/simple/liquid-glass/index.ts | 22 ++-
.../examples/simple/mesh-skinning/index.html | 2 +-
.../examples/simple/mesh-skinning/index.ts | 20 +--
.../src/examples/simple/oklab/index.html | 6 +-
.../src/examples/simple/oklab/index.ts | 27 ++-
.../examples/simple/ripple-cube/index.html | 2 +-
.../src/examples/simple/ripple-cube/index.ts | 52 ++++--
.../simple/ripple-cube/post-processing.ts | 156 ++++++++++++------
.../src/examples/simple/square/index.html | 2 +-
.../src/examples/simple/square/index.ts | 4 +
.../src/examples/simple/stencil/index.html | 2 +-
.../src/examples/simple/stencil/index.ts | 48 +++---
.../src/examples/simple/triangle/index.html | 2 +-
.../src/examples/simple/triangle/index.ts | 5 +
.../src/examples/simple/vaporrave/index.html | 2 +-
.../src/examples/simple/vaporrave/index.ts | 22 ++-
.../src/examples/simulation/boids/index.html | 2 +-
.../src/examples/simulation/boids/index.ts | 29 +++-
.../examples/simulation/confetti/index.html | 2 +-
.../fluid-double-buffering/index.html | 2 +-
.../fluid-double-buffering/index.ts | 12 +-
.../simulation/fluid-with-atomics/index.html | 2 +-
.../simulation/fluid-with-atomics/index.ts | 12 +-
.../simulation/game-of-life/index.html | 2 +-
.../examples/simulation/game-of-life/index.ts | 30 +++-
.../examples/simulation/game-of-life/input.ts | 15 +-
.../examples/simulation/gravity/index.html | 2 +-
.../src/examples/simulation/gravity/index.ts | 22 +--
.../simulation/slime-mold-3d/index.html | 2 +-
.../simulation/slime-mold-3d/index.ts | 28 +++-
.../examples/simulation/slime-mold/index.html | 2 +-
.../examples/simulation/slime-mold/index.ts | 28 +++-
.../simulation/stable-fluid/index.html | 2 +-
.../examples/simulation/stable-fluid/index.ts | 108 ++++++------
.../examples/simulation/wind-map/index.html | 2 +-
.../src/examples/simulation/wind-map/index.ts | 12 +-
.../tests/rc-docs-examples/index.html | 2 +-
.../tests/sdf-docs-examples/index.html | 2 +-
.../examples/tests/texture-test/index.html | 2 +-
.../src/examples/tests/uniformity/index.html | 2 +-
.../examples/threejs/attractors/index.html | 2 +-
.../examples/threejs/compute-cloth/index.html | 2 +-
.../threejs/compute-geometry/index.html | 2 +-
.../threejs/compute-particles-snow/index.html | 2 +-
.../threejs/compute-particles/index.html | 2 +-
.../src/examples/threejs/simple/index.html | 2 +-
.../threejs/test-mismatched-types/index.html | 2 +-
.../threejs/test-reused-functions/index.html | 2 +-
.../src/examples/threejs/varyings/index.html | 2 +-
.../typegpu/src/common/attachAutoResizer.ts | 2 +-
129 files changed, 1072 insertions(+), 659 deletions(-)
diff --git a/apps/typegpu-docs/src/components/ExampleView.tsx b/apps/typegpu-docs/src/components/ExampleView.tsx
index 61446fdc46..339216e0a9 100644
--- a/apps/typegpu-docs/src/components/ExampleView.tsx
+++ b/apps/typegpu-docs/src/components/ExampleView.tsx
@@ -1,6 +1,6 @@
import cs from 'classnames';
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
-import { type RefObject, useEffect, useLayoutEffect, useRef, useState } from 'react';
+import { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { currentSnackbarAtom } from '../utils/examples/currentSnackbarAtom.ts';
import { codeEditorShownAtom, tsoverUsedAtom } from '../utils/examples/exampleViewStateAtoms.ts';
import { ExecutionCancelledError } from '../utils/examples/errors.ts';
@@ -91,7 +91,6 @@ export function ExampleView({ example, common }: Props) {
}, [htmlFile]);
useExample(tsImport, setSnackbarText);
- useResizableCanvas(exampleHtmlRef);
return (
<>
@@ -207,85 +206,6 @@ function GPUUnsupportedPanel() {
);
}
-function useResizableCanvas(exampleHtmlRef: RefObject) {
- useEffect(() => {
- const canvases = exampleHtmlRef.current?.querySelectorAll('canvas') as
- | HTMLCanvasElement[]
- | undefined;
- const observers: ResizeObserver[] = [];
-
- for (const canvas of canvases ?? []) {
- if ('width' in canvas.attributes || 'height' in canvas.attributes) {
- continue;
- }
-
- const newCanvas = document.createElement('canvas');
- const container = document.createElement('div');
- const frame = document.createElement('div');
-
- frame.appendChild(newCanvas);
- container.appendChild(frame);
-
- container.className = 'flex flex-1 justify-center items-center w-full h-full md:w-auto';
- container.style.containerType = 'size';
-
- frame.className = 'relative';
-
- if (canvas.dataset.fitToContainer !== undefined) {
- frame.style.width = '100%';
- frame.style.height = '100%';
- } else {
- const aspectRatio = canvas.dataset.aspectRatio ?? '1';
- frame.style.aspectRatio = aspectRatio;
- frame.style.height = `min(100cqh, calc(100cqw/(${aspectRatio})))`;
- }
-
- for (const prop of canvas.style) {
- // @ts-expect-error
- newCanvas.style[prop] = canvas.style[prop];
- }
- for (const attribute of canvas.attributes) {
- // @ts-expect-error
- newCanvas[attribute.name] = attribute.value;
- }
- newCanvas.className = 'absolute w-full h-full';
-
- canvas.parentElement?.replaceChild(container, canvas);
-
- const onResize: ResizeObserverCallback = ([entry]) => {
- if (!entry) {
- return;
- }
-
- // Despite what the types say this property does not exist in Safari (hence the optional chaining).
- const dpcb = entry.devicePixelContentBoxSize?.[0] as ResizeObserverSize | undefined;
-
- const dpr = dpcb ? 1 : window.devicePixelRatio || 1;
- const box =
- dpcb ??
- (Array.isArray(entry.contentBoxSize) ? entry.contentBoxSize[0] : entry.contentBoxSize);
-
- if (!box) {
- return;
- }
-
- newCanvas.width = Math.round(box.inlineSize * dpr);
- newCanvas.height = Math.round(box.blockSize * dpr);
- };
-
- const observer = new ResizeObserver(onResize);
- observer.observe(newCanvas);
- observers.push(observer);
- }
-
- return () => {
- for (const observer of observers) {
- observer.disconnect();
- }
- };
- }, [exampleHtmlRef]);
-}
-
/**
* NOTE: this function only filters common files used in src files.
* Common files used in other common files will not be included.
diff --git a/apps/typegpu-docs/src/examples/algorithms/bitonic-sort/index.html b/apps/typegpu-docs/src/examples/algorithms/bitonic-sort/index.html
index 65c5005787..b3805f0cfc 100644
--- a/apps/typegpu-docs/src/examples/algorithms/bitonic-sort/index.html
+++ b/apps/typegpu-docs/src/examples/algorithms/bitonic-sort/index.html
@@ -1,4 +1,4 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/algorithms/genetic-racing/index.html b/apps/typegpu-docs/src/examples/algorithms/genetic-racing/index.html
index e049138b14..203db8e5bd 100644
--- a/apps/typegpu-docs/src/examples/algorithms/genetic-racing/index.html
+++ b/apps/typegpu-docs/src/examples/algorithms/genetic-racing/index.html
@@ -1,4 +1,4 @@
-
+
+
+
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/3d-fish/index.ts b/apps/typegpu-docs/src/examples/rendering/3d-fish/index.ts
index aa04d7e9bf..4061e7ebda 100644
--- a/apps/typegpu-docs/src/examples/rendering/3d-fish/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/3d-fish/index.ts
@@ -219,22 +219,7 @@ const computeBindGroups = [0, 1].map((idx) =>
}),
);
-// frame
-
-let odd = false;
-let lastTimestamp: DOMHighResTimeStamp = 0;
-let animationFrameId: number;
-
-function frame(timestamp: DOMHighResTimeStamp) {
- odd = !odd;
-
- currentTimeBuffer.write(timestamp);
- timePassedBuffer.write((timestamp - lastTimestamp) * speedMultiplier);
- lastTimestamp = timestamp;
- cameraBuffer.write(camera);
-
- simulatePipeline.with(computeBindGroups[odd ? 1 : 0]).dispatchThreads(p.fishAmount);
-
+function render() {
renderPipeline
.withColorAttachment({
view: context,
@@ -267,6 +252,25 @@ function frame(timestamp: DOMHighResTimeStamp) {
.with(renderInstanceLayout, fishDataBuffers[odd ? 1 : 0])
.with(renderFishBindGroups[odd ? 1 : 0])
.draw(fishModel.polygonCount, p.fishAmount);
+}
+
+// frame
+
+let odd = false;
+let lastTimestamp: DOMHighResTimeStamp = 0;
+let animationFrameId: number;
+
+function frame(timestamp: DOMHighResTimeStamp) {
+ odd = !odd;
+
+ currentTimeBuffer.write(timestamp);
+ timePassedBuffer.write((timestamp - lastTimestamp) * speedMultiplier);
+ lastTimestamp = timestamp;
+ cameraBuffer.write(camera);
+
+ simulatePipeline.with(computeBindGroups[odd ? 1 : 0]).dispatchThreads(p.fishAmount);
+
+ render();
animationFrameId = requestAnimationFrame(frame);
}
@@ -433,12 +437,18 @@ const detachAutoResizer = common.attachAutoResizer({
d.mat4x4f(),
);
+ cameraBuffer.patch({
+ projection: camera.projection,
+ });
+
depthTexture.destroy();
depthTexture = root.device.createTexture({
size: [canvas.width, canvas.height, 1],
format: 'depth24plus',
usage: GPUTextureUsage.RENDER_ATTACHMENT,
});
+
+ render();
},
});
diff --git a/apps/typegpu-docs/src/examples/rendering/box-raytracing/index.html b/apps/typegpu-docs/src/examples/rendering/box-raytracing/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/rendering/box-raytracing/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/box-raytracing/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/box-raytracing/index.ts b/apps/typegpu-docs/src/examples/rendering/box-raytracing/index.ts
index 3e6d6011e0..d2e7e582fe 100644
--- a/apps/typegpu-docs/src/examples/rendering/box-raytracing/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/box-raytracing/index.ts
@@ -246,6 +246,10 @@ const pipeline = root.createRenderPipeline({
// UI
+function render() {
+ pipeline.withColorAttachment({ view: context }).draw(3);
+}
+
let animationFrameId: number;
let lastTime: number | null = null;
@@ -269,12 +273,22 @@ const runner = (timestamp: number) => {
frame += (rotationSpeed * deltaTime) / 1000;
- pipeline.withColorAttachment({ view: context }).draw(3);
+ render();
animationFrameId = requestAnimationFrame(runner);
};
animationFrameId = requestAnimationFrame(runner);
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+ render();
+ },
+});
// #region Example controls and cleanup
diff --git a/apps/typegpu-docs/src/examples/rendering/caustics/index.html b/apps/typegpu-docs/src/examples/rendering/caustics/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/rendering/caustics/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/caustics/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/caustics/index.ts b/apps/typegpu-docs/src/examples/rendering/caustics/index.ts
index 3f34e808e3..4d2b84e84d 100644
--- a/apps/typegpu-docs/src/examples/rendering/caustics/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/caustics/index.ts
@@ -135,18 +135,30 @@ const pipeline = root.createRenderPipeline({
let isRunning = true;
-function draw(timestamp: number) {
+function render() {
+ pipeline.withColorAttachment({ view: context }).draw(3);
+}
+
+function frame(timestamp: number) {
if (!isRunning) return;
time.write((timestamp * 0.001) % 1000);
-
- pipeline.withColorAttachment({ view: context }).draw(3);
-
- requestAnimationFrame(draw);
+ render();
+ requestAnimationFrame(frame);
}
-requestAnimationFrame(draw);
-
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+requestAnimationFrame(frame);
+
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+ render();
+ },
+});
// #region Example controls and cleanup
diff --git a/apps/typegpu-docs/src/examples/rendering/clouds/index.html b/apps/typegpu-docs/src/examples/rendering/clouds/index.html
index 581d6789f8..367ab17706 100644
--- a/apps/typegpu-docs/src/examples/rendering/clouds/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/clouds/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/clouds/index.ts b/apps/typegpu-docs/src/examples/rendering/clouds/index.ts
index 03de360513..3b710ca6b6 100644
--- a/apps/typegpu-docs/src/examples/rendering/clouds/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/clouds/index.ts
@@ -1,4 +1,5 @@
import tgpu, { common, d, std } from 'typegpu';
+import { randf } from '@typegpu/noise';
import {
FOV_FACTOR,
NOISE_TEXTURE_SIZE,
@@ -11,7 +12,6 @@ import {
} from './consts.ts';
import { raymarch } from './utils.ts';
import { cloudsLayout, CloudsParams } from './types.ts';
-import { randf } from '@typegpu/noise';
import { defineControls } from '../../common/defineControls.ts';
const root = await tgpu.init();
@@ -21,10 +21,10 @@ const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
const paramsUniform = root.createUniform(CloudsParams, {
time: 0,
+ aspectRatio: 1,
maxSteps: 50,
maxDistance: 10.0,
});
-const resolutionUniform = root.createUniform(d.vec2f, d.vec2f(canvas.width, canvas.height));
const noiseData = new Uint8Array(NOISE_TEXTURE_SIZE * NOISE_TEXTURE_SIZE);
for (let i = 0; i < noiseData.length; i += 1) {
@@ -57,8 +57,7 @@ const pipeline = root.createRenderPipeline({
fragment: ({ uv }) => {
'use gpu';
randf.seed2(uv * cloudsLayout.$.params.time);
- const screenRes = resolutionUniform.$;
- const aspect = screenRes.x / screenRes.y;
+ const aspect = cloudsLayout.$.params.aspectRatio;
let screenPos = (uv - 0.5) * 2;
screenPos = d.vec2f(screenPos.x * std.max(aspect, 1), screenPos.y * std.max(1 / aspect, 1));
@@ -86,16 +85,7 @@ const pipeline = root.createRenderPipeline({
targets: { format: presentationFormat },
});
-const resizeObserver = new ResizeObserver(() => {
- resolutionUniform.write(d.vec2f(canvas.width, canvas.height));
-});
-resizeObserver.observe(canvas);
-
-let frameId: number;
-
-function render(timestamp: number) {
- paramsUniform.patch({ time: (timestamp / 1000) % 500 });
-
+function render() {
pipeline
.with(bindGroup)
.withColorAttachment({
@@ -103,13 +93,33 @@ function render(timestamp: number) {
clearValue: [0, 0, 0, 1],
})
.draw(6);
+}
+
+let frameId: number;
- frameId = requestAnimationFrame(render);
+function frame(timestamp: number) {
+ paramsUniform.patch({ time: (timestamp / 1000) % 500 });
+ render();
+ frameId = requestAnimationFrame(frame);
}
-frameId = requestAnimationFrame(render);
+frameId = requestAnimationFrame(frame);
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // The clouds are very blurry, so there is no benefit from full-resolution
+ canvas.width = Math.max(1, canvas.width / 4);
+ canvas.height = Math.max(1, canvas.height / 4);
+
+ paramsUniform.patch({
+ aspectRatio: canvas.width / canvas.height,
+ });
+
+ render();
+ },
+});
const qualityOptions = {
'very high': {
@@ -146,7 +156,6 @@ export const controls = defineControls({
export function onCleanup() {
cancelAnimationFrame(frameId);
- resizeObserver.disconnect();
detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/clouds/types.ts b/apps/typegpu-docs/src/examples/rendering/clouds/types.ts
index c4945fb27c..7a9504ac24 100644
--- a/apps/typegpu-docs/src/examples/rendering/clouds/types.ts
+++ b/apps/typegpu-docs/src/examples/rendering/clouds/types.ts
@@ -2,6 +2,7 @@ import tgpu, { d } from 'typegpu';
export const CloudsParams = d.struct({
time: d.f32,
+ aspectRatio: d.f32,
maxSteps: d.i32,
maxDistance: d.f32,
});
diff --git a/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/index.html b/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/index.html
index cb4615fc87..a2d13746bb 100644
--- a/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/index.html
@@ -38,4 +38,4 @@
under CC BY 4.0 / Modified from original
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/index.ts b/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/index.ts
index 34f9ba787e..f712e8952a 100644
--- a/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/index.ts
@@ -285,6 +285,8 @@ const detachAutoResizer = common.attachAutoResizer({
d.mat4x4f(),
);
cameraBuffer.patch({ projection: newProj });
+
+ render();
},
});
diff --git a/apps/typegpu-docs/src/examples/rendering/disco/index.html b/apps/typegpu-docs/src/examples/rendering/disco/index.html
index 581d6789f8..a1d12c864f 100644
--- a/apps/typegpu-docs/src/examples/rendering/disco/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/disco/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/disco/index.ts b/apps/typegpu-docs/src/examples/rendering/disco/index.ts
index 303b42da3b..825bd139ff 100644
--- a/apps/typegpu-docs/src/examples/rendering/disco/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/disco/index.ts
@@ -42,11 +42,7 @@ let currentPipeline = pipelines[0];
let startTime: null | number = null;
let frameId: number;
-function render(timestamp: number) {
- if (startTime === null) {
- startTime = timestamp;
- }
- time.write((timestamp - startTime) / 1000);
+function render() {
resolutionUniform.write(d.vec2f(canvas.width, canvas.height));
currentPipeline
@@ -55,15 +51,27 @@ function render(timestamp: number) {
clearValue: [0, 0, 0, 1],
})
.draw(6);
+}
+
+function frame(timestamp: number) {
+ if (startTime === null) {
+ startTime = timestamp;
+ }
+ time.write((timestamp - startTime) / 1000);
+
+ render();
- frameId = requestAnimationFrame(render);
+ frameId = requestAnimationFrame(frame);
}
-frameId = requestAnimationFrame(render);
+frameId = requestAnimationFrame(frame);
const detachAutoResizer = common.attachAutoResizer({
root,
canvas,
+ onResize() {
+ render();
+ },
});
export function onCleanup() {
diff --git a/apps/typegpu-docs/src/examples/rendering/function-visualizer/index.html b/apps/typegpu-docs/src/examples/rendering/function-visualizer/index.html
index 581d6789f8..367ab17706 100644
--- a/apps/typegpu-docs/src/examples/rendering/function-visualizer/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/function-visualizer/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/function-visualizer/index.ts b/apps/typegpu-docs/src/examples/rendering/function-visualizer/index.ts
index 42aaf2691c..0d1b20a46e 100644
--- a/apps/typegpu-docs/src/examples/rendering/function-visualizer/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/function-visualizer/index.ts
@@ -383,23 +383,23 @@ window.addEventListener('touchend', touchEndEventListener);
// Resize observer and cleanup
-const resizeObserver = new ResizeObserver(() => {
- queuePropertiesBufferUpdate();
-
- msTexture.destroy();
- msTexture = device.createTexture({
- size: [canvas.width, canvas.height],
- sampleCount: 4,
- format: presentationFormat,
- usage: GPUTextureUsage.RENDER_ATTACHMENT,
- });
- msView = msTexture.createView();
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ queuePropertiesBufferUpdate();
+
+ msTexture.destroy();
+ msTexture = device.createTexture({
+ size: [canvas.width, canvas.height],
+ sampleCount: 4,
+ format: presentationFormat,
+ usage: GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+ msView = msTexture.createView();
+ },
});
-resizeObserver.observe(canvas);
-
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
-
// #region Example controls and cleanup
export const controls = defineControls({
@@ -458,7 +458,6 @@ export function onCleanup() {
window.removeEventListener('mousemove', mouseMoveEventListener);
window.removeEventListener('touchmove', touchMoveEventListener);
window.removeEventListener('touchend', touchEndEventListener);
- resizeObserver.disconnect();
detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/jelly-slider/index.html b/apps/typegpu-docs/src/examples/rendering/jelly-slider/index.html
index a1a36aafb9..0942059962 100644
--- a/apps/typegpu-docs/src/examples/rendering/jelly-slider/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/jelly-slider/index.html
@@ -33,4 +33,4 @@
Inspired by work of
Voicu Apostol
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/jelly-slider/index.ts b/apps/typegpu-docs/src/examples/rendering/jelly-slider/index.ts
index 05dc5f9070..ae859fdfef 100644
--- a/apps/typegpu-docs/src/examples/rendering/jelly-slider/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/jelly-slider/index.ts
@@ -714,7 +714,6 @@ const renderPipeline = root.createRenderPipeline({
});
const eventHandler = new EventHandler(canvas);
-let lastTimestamp: number | null = null;
let frameCount = 0;
const taaResolver = new TAAResolver(root, width, height);
@@ -748,21 +747,13 @@ function createBindGroups() {
let bindGroups = createBindGroups();
-let animationFrameHandle: number;
-function render(timestamp: number) {
+function render() {
frameCount++;
- camera.jitter();
- const deltaTime = Math.min(lastTimestamp !== null ? (timestamp - lastTimestamp) * 0.001 : 0, 0.1);
- lastTimestamp = timestamp;
+ const currentFrame = frameCount % 2;
+ camera.jitter();
randomUniform.write(d.vec2f((Math.random() - 0.5) * 2, (Math.random() - 0.5) * 2));
- eventHandler.update();
- slider.setDragX(eventHandler.currentMouseX);
- slider.update(deltaTime);
-
- const currentFrame = frameCount % 2;
-
rayMarchPipeline
.withColorAttachment({
view: textures[currentFrame].sampled,
@@ -777,11 +768,14 @@ function render(timestamp: number) {
.withColorAttachment({ view: context })
.with(bindGroups.render[currentFrame])
.draw(3);
-
- animationFrameHandle = requestAnimationFrame(render);
}
function handleResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+
[width, height] = [canvas.width * qualityScale, canvas.height * qualityScale];
camera.updateProjection(Math.PI / 4, width, height);
textures = createTextures(root, width, height);
@@ -790,6 +784,7 @@ function handleResize() {
frameCount = 0;
bindGroups = createBindGroups();
+ render();
}
const detachAutoResizer = common.attachAutoResizer({
@@ -800,7 +795,21 @@ const detachAutoResizer = common.attachAutoResizer({
},
});
-animationFrameHandle = requestAnimationFrame(render);
+let lastTimestamp: number | null = null;
+let animationFrameHandle: number;
+function frame(timestamp: number) {
+ const deltaTime = Math.min(lastTimestamp !== null ? (timestamp - lastTimestamp) * 0.001 : 0, 0.1);
+ lastTimestamp = timestamp;
+
+ eventHandler.update();
+ slider.setDragX(eventHandler.currentMouseX);
+ slider.update(deltaTime);
+
+ render();
+
+ animationFrameHandle = requestAnimationFrame(frame);
+}
+animationFrameHandle = requestAnimationFrame(frame);
// #region Example controls and cleanup
diff --git a/apps/typegpu-docs/src/examples/rendering/jelly-switch/index.html b/apps/typegpu-docs/src/examples/rendering/jelly-switch/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/rendering/jelly-switch/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/jelly-switch/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/jelly-switch/index.ts b/apps/typegpu-docs/src/examples/rendering/jelly-switch/index.ts
index 940a662671..1248fee99d 100644
--- a/apps/typegpu-docs/src/examples/rendering/jelly-switch/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/jelly-switch/index.ts
@@ -484,7 +484,6 @@ const renderPipeline = root.createRenderPipeline({
targets: { format: presentationFormat },
});
-let lastTimestamp: number | null = null;
let frameCount = 0;
const taaResolver = new TAAResolver(root, width, height);
@@ -503,19 +502,13 @@ function createBindGroups() {
let bindGroups = createBindGroups();
-let animationFrameHandle: number;
-function render(timestamp: number) {
+function render() {
frameCount++;
- camera.jitter();
- const deltaTime = Math.min(lastTimestamp !== null ? (timestamp - lastTimestamp) * 0.001 : 0, 0.1);
- lastTimestamp = timestamp;
+ const currentFrame = frameCount % 2;
+ camera.jitter();
randomUniform.write(d.vec2f((Math.random() - 0.5) * 2, (Math.random() - 0.5) * 2));
- switchBehavior.update(deltaTime);
-
- const currentFrame = frameCount % 2;
-
rayMarchPipeline
.withColorAttachment({
view: textures[currentFrame].sampled,
@@ -530,11 +523,14 @@ function render(timestamp: number) {
.withColorAttachment({ view: context })
.with(bindGroups.render[currentFrame])
.draw(3);
-
- animationFrameHandle = requestAnimationFrame(render);
}
function handleResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+
[width, height] = [canvas.width * qualityScale, canvas.height * qualityScale];
camera.updateProjection(Math.PI / 4, width, height);
textures = createTextures(root, width, height);
@@ -543,6 +539,7 @@ function handleResize() {
frameCount = 0;
bindGroups = createBindGroups();
+ render();
}
const detachAutoResizer = common.attachAutoResizer({
@@ -553,7 +550,19 @@ const detachAutoResizer = common.attachAutoResizer({
},
});
-animationFrameHandle = requestAnimationFrame(render);
+let lastTimestamp: number | null = null;
+let animationFrameHandle: number;
+function frame(timestamp: number) {
+ const deltaTime = Math.min(lastTimestamp !== null ? (timestamp - lastTimestamp) * 0.001 : 0, 0.1);
+ lastTimestamp = timestamp;
+
+ switchBehavior.update(deltaTime);
+
+ render();
+
+ animationFrameHandle = requestAnimationFrame(frame);
+}
+animationFrameHandle = requestAnimationFrame(frame);
// #region Example controls and cleanup
diff --git a/apps/typegpu-docs/src/examples/rendering/os-awards/index.html b/apps/typegpu-docs/src/examples/rendering/os-awards/index.html
index 972583913a..86a6e42572 100644
--- a/apps/typegpu-docs/src/examples/rendering/os-awards/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/os-awards/index.html
@@ -12,4 +12,4 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/os-awards/index.ts b/apps/typegpu-docs/src/examples/rendering/os-awards/index.ts
index fbaaf7f3f2..f908aab54b 100644
--- a/apps/typegpu-docs/src/examples/rendering/os-awards/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/os-awards/index.ts
@@ -381,22 +381,7 @@ function createDepthTexture() {
return { texture, view: texture.createView() };
}
-let depth = createDepthTexture();
-const resizeObserver = new ResizeObserver(() => {
- depth.texture.destroy();
- depth = createDepthTexture();
-});
-resizeObserver.observe(canvas);
-
-let exampleDestroyed = false;
-let firstFrameDrawn = false;
-
-function frame(timeMs: number) {
- if (exampleDestroyed) {
- return;
- }
- updateAwardTransform(timeMs);
-
+function render() {
envPipeline.withColorAttachment({ view: context }).draw(3);
awardPipeline
@@ -410,6 +395,29 @@ function frame(timeMs: number) {
.with(awardVertexLayout, award.vertexBuffer)
.withIndexBuffer(award.indexBuffer)
.drawIndexed(award.indexCount);
+}
+
+let depth = createDepthTexture();
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ depth.texture.destroy();
+ depth = createDepthTexture();
+ render();
+ },
+});
+
+let exampleDestroyed = false;
+let firstFrameDrawn = false;
+
+function frame(timeMs: number) {
+ if (exampleDestroyed) {
+ return;
+ }
+ updateAwardTransform(timeMs);
+
+ render();
if (!firstFrameDrawn) {
loadingScreen.style.display = 'none';
@@ -432,6 +440,6 @@ export const controls = defineControls({
export function onCleanup() {
exampleDestroyed = true;
cleanupCamera();
- resizeObserver.unobserve(canvas);
+ detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/perlin-noise/index.html b/apps/typegpu-docs/src/examples/rendering/perlin-noise/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/rendering/perlin-noise/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/perlin-noise/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/perlin-noise/index.ts b/apps/typegpu-docs/src/examples/rendering/perlin-noise/index.ts
index 21c8622e9b..726aac4524 100644
--- a/apps/typegpu-docs/src/examples/rendering/perlin-noise/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/perlin-noise/index.ts
@@ -66,21 +66,34 @@ let activeSharpenFn: 'exponential' | 'tanh' = 'exponential';
let isRunning = true;
let bindGroup = root.createBindGroup(dynamicLayout, perlinCache.bindings);
-function draw(timestamp: number) {
+function render() {
+ renderPipelines[activeSharpenFn].with(bindGroup).withColorAttachment({ view: context }).draw(3);
+}
+
+function frame(timestamp: number) {
if (!isRunning) {
return;
}
time.write((timestamp * 0.0002) % DEPTH);
+ render();
- renderPipelines[activeSharpenFn].with(bindGroup).withColorAttachment({ view: context }).draw(3);
-
- requestAnimationFrame(draw);
+ requestAnimationFrame(frame);
}
-requestAnimationFrame(draw);
-
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+requestAnimationFrame(frame);
+
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+ render();
+ },
+});
export const controls = defineControls({
'grid size': {
diff --git a/apps/typegpu-docs/src/examples/rendering/phong-reflection/index.html b/apps/typegpu-docs/src/examples/rendering/phong-reflection/index.html
index 581d6789f8..a1d12c864f 100644
--- a/apps/typegpu-docs/src/examples/rendering/phong-reflection/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/phong-reflection/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/phong-reflection/index.ts b/apps/typegpu-docs/src/examples/rendering/phong-reflection/index.ts
index c217e87c3f..ea87431ac4 100644
--- a/apps/typegpu-docs/src/examples/rendering/phong-reflection/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/phong-reflection/index.ts
@@ -101,9 +101,7 @@ let depthTexture = root.device.createTexture({
usage: GPUTextureUsage.RENDER_ATTACHMENT,
});
-// frame
-let frameId: number;
-function frame() {
+function render() {
renderPipeline
.withColorAttachment({
view: context,
@@ -117,11 +115,30 @@ function frame() {
})
.with(modelVertexLayout, model.vertexBuffer)
.draw(model.polygonCount);
+}
+// frame
+let frameId: number;
+function frame() {
+ render();
frameId = requestAnimationFrame(frame);
}
frameId = requestAnimationFrame(frame);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ depthTexture.destroy();
+ depthTexture = root.device.createTexture({
+ size: [canvas.width, canvas.height, 1],
+ format: 'depth24plus',
+ usage: GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+ render();
+ },
+});
+
// #region Example controls and cleanup
export const controls = defineControls({
'light color': {
@@ -165,22 +182,9 @@ export const controls = defineControls({
},
});
-const resizeObserver = new ResizeObserver(() => {
- depthTexture.destroy();
- depthTexture = root.device.createTexture({
- size: [canvas.width, canvas.height, 1],
- format: 'depth24plus',
- usage: GPUTextureUsage.RENDER_ATTACHMENT,
- });
-});
-resizeObserver.observe(canvas);
-
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
-
export function onCleanup() {
cancelAnimationFrame(frameId);
cleanupCamera();
- resizeObserver.unobserve(canvas);
detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/point-light-shadow/index.html b/apps/typegpu-docs/src/examples/rendering/point-light-shadow/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/rendering/point-light-shadow/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/point-light-shadow/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/point-light-shadow/index.ts b/apps/typegpu-docs/src/examples/rendering/point-light-shadow/index.ts
index c317000cc9..76dd0c0c2b 100644
--- a/apps/typegpu-docs/src/examples/rendering/point-light-shadow/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/point-light-shadow/index.ts
@@ -7,7 +7,6 @@ import { CameraData, InstanceData, instanceLayout, VertexData, vertexLayout } fr
import { defineControls } from '../../common/defineControls.ts';
const root = await tgpu.init();
-const device = root.device;
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
const context = root.configureContext({ canvas });
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
@@ -356,28 +355,11 @@ let lastTime: number | null = null;
let time = 0;
let animationFrameId: number;
-function render(timestamp: number) {
- const dt = lastTime !== null ? (timestamp - lastTime) / 1000 : 0;
- lastTime = timestamp;
- time += dt;
-
- for (let i = 0; i < orbitingCubes.length; i++) {
- const offset = (i / orbitingCubes.length) * Math.PI * 2;
- const angle = time * 0.5 + offset;
- const radius = 4 + Math.sin(time * 2 + offset * 3) * 0.5;
- const x = Math.cos(angle) * radius;
- const z = Math.sin(angle) * radius;
- const y = 2 + Math.sin(time * 1.5 + offset * 2) * 1.5;
- orbitingCubes[i].position = d.vec3f(x, y, z);
- orbitingCubes[i].rotation = d.vec3f(time, time * 0.5, 0);
- }
-
- scene.update();
+function render() {
pointLight.renderShadowMaps(pipelineDepthOne, renderLayout, scene);
if (showDepthPreview) {
pipelinePreview.withColorAttachment({ view: context }).draw(3);
- animationFrameId = requestAnimationFrame(render);
return;
}
@@ -415,15 +397,42 @@ function render(timestamp: number) {
.withIndexBuffer(BoxGeometry.indexBuffer)
.with(vertexLayout, BoxGeometry.vertexBuffer)
.drawIndexed(BoxGeometry.indexCount);
+}
+
+function frame(timestamp: number) {
+ animationFrameId = requestAnimationFrame(frame);
+
+ const dt = lastTime !== null ? (timestamp - lastTime) / 1000 : 0;
+ lastTime = timestamp;
+ time += dt;
- animationFrameId = requestAnimationFrame(render);
+ for (let i = 0; i < orbitingCubes.length; i++) {
+ const offset = (i / orbitingCubes.length) * Math.PI * 2;
+ const angle = time * 0.5 + offset;
+ const radius = 4 + Math.sin(time * 2 + offset * 3) * 0.5;
+ const x = Math.cos(angle) * radius;
+ const z = Math.sin(angle) * radius;
+ const y = 2 + Math.sin(time * 1.5 + offset * 2) * 1.5;
+ orbitingCubes[i].position = d.vec3f(x, y, z);
+ orbitingCubes[i].rotation = d.vec3f(time, time * 0.5, 0);
+ }
+
+ scene.update();
+
+ render();
}
-animationFrameId = requestAnimationFrame(render);
+
+animationFrameId = requestAnimationFrame(frame);
const detachAutoResizer = common.attachAutoResizer({
root,
canvas,
onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+
depthTexture = root
.createTexture({
size: [canvas.width, canvas.height],
@@ -438,6 +447,8 @@ const detachAutoResizer = common.attachAutoResizer({
sampleCount: 4,
})
.$usage('render');
+
+ render();
},
});
diff --git a/apps/typegpu-docs/src/examples/rendering/pom/index.html b/apps/typegpu-docs/src/examples/rendering/pom/index.html
index 581d6789f8..367ab17706 100644
--- a/apps/typegpu-docs/src/examples/rendering/pom/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/pom/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/pom/index.ts b/apps/typegpu-docs/src/examples/rendering/pom/index.ts
index d2632fc2a5..3f14ea5b94 100644
--- a/apps/typegpu-docs/src/examples/rendering/pom/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/pom/index.ts
@@ -404,8 +404,7 @@ function createDepthTexture() {
}
createDepthTexture();
-let frameId: number;
-function frame() {
+function render() {
pipeline
.withColorAttachment({ view: context, clearValue: [0, 0, 0, 1] })
.withDepthStencilAttachment({
@@ -415,10 +414,24 @@ function frame() {
depthStoreOp: 'store',
})
.drawIndexed(planeMesh.indexCount);
+}
+
+let frameId: number;
+function frame() {
+ render();
frameId = requestAnimationFrame(frame);
}
frameId = requestAnimationFrame(frame);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ createDepthTexture();
+ render();
+ },
+});
+
// #region Example controls and cleanup
export const controls = defineControls({
@@ -483,14 +496,6 @@ export const controls = defineControls({
},
});
-const detachAutoResizer = common.attachAutoResizer({
- root,
- canvas,
- onResize() {
- createDepthTexture();
- },
-});
-
export function onCleanup() {
cancelAnimationFrame(frameId);
cleanupCamera();
diff --git a/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/drawInteraction.ts b/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/drawInteraction.ts
index 732ed5c213..3b42ac0a78 100644
--- a/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/drawInteraction.ts
+++ b/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/drawInteraction.ts
@@ -42,9 +42,17 @@ export function createDrawInteraction({ canvas, onDraw, onStop }: DrawInteractio
function mousePosition(e: MouseEvent): Point {
const rect = canvas.getBoundingClientRect();
+
+ // Taking into account the square aspect ratio
+ const centerX = rect.x + rect.width / 2;
+ const centerY = rect.y + rect.height / 2;
+ const size = Math.min(rect.width, rect.height);
+ const squareLeft = centerX - size / 2;
+ const squareTop = centerY - size / 2;
+
return {
- x: (e.clientX - rect.left) / rect.width,
- y: (e.clientY - rect.top) / rect.height,
+ x: (e.clientX - squareLeft) / size,
+ y: (e.clientY - squareTop) / size,
};
}
diff --git a/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/index.html b/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/index.ts b/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/index.ts
index dedad17c0b..ba94f90ec7 100644
--- a/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/radiance-cascades-drawing/index.ts
@@ -4,6 +4,8 @@ import tgpu, { common, d, std } from 'typegpu';
import { defineControls } from '../../common/defineControls.ts';
import { createDrawInteraction } from './drawInteraction.ts';
+const [sceneWidth, sceneHeight] = [1024, 1024];
+
const root = await tgpu.init();
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
@@ -15,12 +17,21 @@ context.configure({
format: presentationFormat,
});
-const [width, height] = [canvas.width, canvas.height];
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+ },
+});
// Scene texture + views.
const sceneTexture = root
.createTexture({
- size: [width, height],
+ size: [sceneWidth, sceneHeight],
format: 'rgba16float',
})
.$usage('storage', 'sampled');
@@ -80,7 +91,7 @@ const drawCompute = root.createGuardedComputePipeline((x, y) => {
std.textureStore(sceneWriteView.$, d.vec2u(x, y), out);
});
-const floodSize = { width: canvas.width, height: canvas.height };
+const floodSize = { width: sceneWidth, height: sceneHeight };
const floodRunner = sdf
.createJumpFlood({
root,
@@ -114,7 +125,7 @@ const floodColorView = floodRunner.colorOutput.createView();
const radianceRunner = rc.createRadianceCascades({
root,
- size: { width: Math.floor(width / 4), height: Math.floor(height / 4) },
+ size: { width: Math.floor(sceneWidth / 4), height: Math.floor(sceneHeight / 4) },
sdfResolution: floodSize,
sdf: (uv) => {
'use gpu';
@@ -176,7 +187,7 @@ const displayPipeline = root.createRenderPipeline({
let sceneDirty = false;
function drawScene() {
- drawCompute.dispatchThreads(width, height);
+ drawCompute.dispatchThreads(sceneWidth, sceneHeight);
sceneDirty = true;
}
@@ -215,8 +226,6 @@ function frame(timestamp: number) {
frameId = requestAnimationFrame(frame);
}
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
-
// #region Example controls and cleanup
export const controls = defineControls({
diff --git a/apps/typegpu-docs/src/examples/rendering/radiance-cascades/index.html b/apps/typegpu-docs/src/examples/rendering/radiance-cascades/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/rendering/radiance-cascades/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/radiance-cascades/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/radiance-cascades/index.ts b/apps/typegpu-docs/src/examples/rendering/radiance-cascades/index.ts
index 04e8dcf39f..2b50192000 100644
--- a/apps/typegpu-docs/src/examples/rendering/radiance-cascades/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/radiance-cascades/index.ts
@@ -481,6 +481,11 @@ const detachAutoResizer = common.attachAutoResizer({
onResize() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(recreateSizedResources, 100);
+
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
},
});
diff --git a/apps/typegpu-docs/src/examples/rendering/ray-marching/index.html b/apps/typegpu-docs/src/examples/rendering/ray-marching/index.html
index 581d6789f8..367ab17706 100644
--- a/apps/typegpu-docs/src/examples/rendering/ray-marching/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/ray-marching/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/render-bundles-with/index.html b/apps/typegpu-docs/src/examples/rendering/render-bundles-with/index.html
index 581d6789f8..a1d12c864f 100644
--- a/apps/typegpu-docs/src/examples/rendering/render-bundles-with/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/render-bundles-with/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/render-bundles-with/index.ts b/apps/typegpu-docs/src/examples/rendering/render-bundles-with/index.ts
index abe3e16944..19d9cc70dc 100644
--- a/apps/typegpu-docs/src/examples/rendering/render-bundles-with/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/render-bundles-with/index.ts
@@ -147,11 +147,7 @@ setCubeCount(INITIAL_CUBE_COUNT);
let useBundles = true;
-let disposed = false;
-
-function frame() {
- if (disposed) return;
-
+function render() {
if (depthTexture.width !== canvas.width || depthTexture.height !== canvas.height) {
depthTexture.destroy();
depthTexture = root.device.createTexture({
@@ -191,13 +187,27 @@ function frame() {
pass.end();
root.device.queue.submit([encoder.finish()]);
+}
+
+let disposed = false;
+
+function frame() {
+ if (disposed) return;
+
+ render();
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ render();
+ },
+});
// #region Example controls and cleanup
diff --git a/apps/typegpu-docs/src/examples/rendering/render-bundles/index.html b/apps/typegpu-docs/src/examples/rendering/render-bundles/index.html
index 581d6789f8..367ab17706 100644
--- a/apps/typegpu-docs/src/examples/rendering/render-bundles/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/render-bundles/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/render-bundles/index.ts b/apps/typegpu-docs/src/examples/rendering/render-bundles/index.ts
index 1a31f6fea2..f6e5f58cfd 100644
--- a/apps/typegpu-docs/src/examples/rendering/render-bundles/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/render-bundles/index.ts
@@ -147,9 +147,7 @@ let useBundles = true;
let disposed = false;
-function frame() {
- if (disposed) return;
-
+function render() {
if (depthTexture.width !== canvas.width || depthTexture.height !== canvas.height) {
depthTexture.destroy();
depthTexture = root.device.createTexture({
@@ -191,6 +189,12 @@ function frame() {
}
}
});
+}
+
+function frame() {
+ if (disposed) return;
+
+ render();
requestAnimationFrame(frame);
}
@@ -200,6 +204,9 @@ requestAnimationFrame(frame);
const detachAutoResizer = common.attachAutoResizer({
root,
canvas,
+ onResize() {
+ render();
+ },
});
// #region Example controls and cleanup
diff --git a/apps/typegpu-docs/src/examples/rendering/simple-shadow/index.html b/apps/typegpu-docs/src/examples/rendering/simple-shadow/index.html
index 581d6789f8..a1d12c864f 100644
--- a/apps/typegpu-docs/src/examples/rendering/simple-shadow/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/simple-shadow/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/simple-shadow/index.ts b/apps/typegpu-docs/src/examples/rendering/simple-shadow/index.ts
index 82d5bebe47..ab33bd1f87 100644
--- a/apps/typegpu-docs/src/examples/rendering/simple-shadow/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/simple-shadow/index.ts
@@ -280,11 +280,7 @@ function updateLightDirection(dir: d.v3f) {
});
}
-// Render loop
-let frameId: number | null = null;
function render() {
- frameId = requestAnimationFrame(render);
-
root['~unstable'].beginRenderPass(
{
colorAttachments: [],
@@ -338,7 +334,15 @@ function render() {
},
);
}
-frameId = requestAnimationFrame(render);
+
+// Render loop
+let frameId: number | null = null;
+function frame() {
+ frameId = requestAnimationFrame(frame);
+
+ render();
+}
+frameId = requestAnimationFrame(frame);
const detachAutoResizer = common.attachAutoResizer({
root,
@@ -356,6 +360,8 @@ const detachAutoResizer = common.attachAutoResizer({
cameraUniform.patch({
projection: newProjection,
});
+
+ render();
},
});
diff --git a/apps/typegpu-docs/src/examples/rendering/smoky-triangle/index.html b/apps/typegpu-docs/src/examples/rendering/smoky-triangle/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/rendering/smoky-triangle/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/smoky-triangle/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/smoky-triangle/index.ts b/apps/typegpu-docs/src/examples/rendering/smoky-triangle/index.ts
index 5e085f56d3..575b76306e 100644
--- a/apps/typegpu-docs/src/examples/rendering/smoky-triangle/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/smoky-triangle/index.ts
@@ -80,6 +80,10 @@ const pipeline = root.pipe(perlinCache.inject()).createRenderPipeline({
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
const context = root.configureContext({ canvas, alphaMode: 'premultiplied' });
+function render() {
+ pipeline.withColorAttachment({ view: context }).draw(3);
+}
+
let frameId: number;
function frame(timestamp: number) {
paramsUniform.patch({
@@ -87,13 +91,23 @@ function frame(timestamp: number) {
grainSeed: Math.floor(Math.random() * 100),
});
- pipeline.withColorAttachment({ view: context }).draw(3);
+ render();
frameId = requestAnimationFrame(frame);
}
frameId = requestAnimationFrame(frame);
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+ render();
+ },
+});
export const controls = {
Distortion: {
diff --git a/apps/typegpu-docs/src/examples/rendering/suika-sdf/index.html b/apps/typegpu-docs/src/examples/rendering/suika-sdf/index.html
index d50df83d4b..3eda9a7c1e 100644
--- a/apps/typegpu-docs/src/examples/rendering/suika-sdf/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/suika-sdf/index.html
@@ -1,4 +1,4 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/suika-sdf/index.ts b/apps/typegpu-docs/src/examples/rendering/suika-sdf/index.ts
index 37fce94715..2cfc90f4c3 100644
--- a/apps/typegpu-docs/src/examples/rendering/suika-sdf/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/suika-sdf/index.ts
@@ -427,6 +427,8 @@ const detachAutoResizer = common.attachAutoResizer({
distance: distanceView,
info: infoView,
});
+
+ render();
},
});
@@ -531,6 +533,24 @@ function restart() {
circleUniform.write(circleData);
}
+function render() {
+ frameUniform.patch({
+ canvasAspect: canvas.width / canvas.height,
+ });
+
+ mergedFieldPipeline
+ .withColorAttachment({
+ distance: { view: distanceView },
+ info: { view: infoView },
+ })
+ .draw(3);
+
+ renderPipeline
+ .with(mergedFieldBindGroup)
+ .withColorAttachment({ view: context, clearValue: { r: 0, g: 0, b: 0, a: 1 } })
+ .draw(3);
+}
+
let lastTime = 0;
let animationFrameId = 0;
@@ -645,18 +665,7 @@ function frame(now: number) {
},
});
- mergedFieldPipeline
- .withColorAttachment({
- distance: { view: distanceView },
- info: { view: infoView },
- })
- .draw(3);
-
- renderPipeline
- .with(mergedFieldBindGroup)
- .withColorAttachment({ view: context, clearValue: { r: 0, g: 0, b: 0, a: 1 } })
- .draw(3);
-
+ render();
animationFrameId = requestAnimationFrame(frame);
}
animationFrameId = requestAnimationFrame(frame);
diff --git a/apps/typegpu-docs/src/examples/rendering/two-boxes/index.html b/apps/typegpu-docs/src/examples/rendering/two-boxes/index.html
index 9209e7c2fd..14e7a57baf 100644
--- a/apps/typegpu-docs/src/examples/rendering/two-boxes/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/two-boxes/index.html
@@ -27,4 +27,4 @@
Right Mouse Button: Rotate cubes
Scroll / Two-Finger Drag: Zoom
-
+
diff --git a/apps/typegpu-docs/src/examples/rendering/two-boxes/index.ts b/apps/typegpu-docs/src/examples/rendering/two-boxes/index.ts
index 11f52c24ee..0a336307a4 100644
--- a/apps/typegpu-docs/src/examples/rendering/two-boxes/index.ts
+++ b/apps/typegpu-docs/src/examples/rendering/two-boxes/index.ts
@@ -30,7 +30,7 @@ const vertexLayout = tgpu.vertexLayout(d.arrayOf(Vertex));
// Scene Setup
-const aspect = canvas.clientWidth / canvas.clientHeight;
+const aspect = 1;
const target = d.vec3f(0, 0, 0);
const cameraInitialPos = d.vec4f(12, 5, 12, 1);
@@ -460,12 +460,18 @@ canvas.addEventListener(
{ passive: false },
);
-const resizeObserver = new ResizeObserver(() => {
- createDepthAndMsaaTextures();
-});
-resizeObserver.observe(canvas);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+ createDepthAndMsaaTextures();
+ },
+});
export function onCleanup() {
cancelAnimationFrame(animationFrameId);
@@ -473,7 +479,6 @@ export function onCleanup() {
window.removeEventListener('mousemove', mouseMoveEventListener);
window.removeEventListener('touchend', touchEndEventListener);
window.removeEventListener('touchmove', touchMoveEventListener);
- resizeObserver.disconnect();
detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/rendering/xor-dev-centrifuge-2/index.html b/apps/typegpu-docs/src/examples/rendering/xor-dev-centrifuge-2/index.html
index 1edc7df14f..57ee4dca06 100644
--- a/apps/typegpu-docs/src/examples/rendering/xor-dev-centrifuge-2/index.html
+++ b/apps/typegpu-docs/src/examples/rendering/xor-dev-centrifuge-2/index.html
@@ -1,4 +1,4 @@
-
+
Port of "Centrifuge 2" by XorDev (
+
Port of "Runner" by XorDev (
+
diff --git a/apps/typegpu-docs/src/examples/simple/gradient-tiles/index.ts b/apps/typegpu-docs/src/examples/simple/gradient-tiles/index.ts
index 8063b03bea..904f7b3714 100644
--- a/apps/typegpu-docs/src/examples/simple/gradient-tiles/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/gradient-tiles/index.ts
@@ -37,6 +37,10 @@ const detachAutoResizer = common.attachAutoResizer({
root,
canvas,
onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
draw(spanX, spanY);
},
});
diff --git a/apps/typegpu-docs/src/examples/simple/liquid-glass/index.html b/apps/typegpu-docs/src/examples/simple/liquid-glass/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/simple/liquid-glass/index.html
+++ b/apps/typegpu-docs/src/examples/simple/liquid-glass/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/simple/liquid-glass/index.ts b/apps/typegpu-docs/src/examples/simple/liquid-glass/index.ts
index c5e0a8ee27..debb0946ba 100644
--- a/apps/typegpu-docs/src/examples/simple/liquid-glass/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/liquid-glass/index.ts
@@ -180,14 +180,28 @@ canvas.addEventListener('touchmove', handleTouchMove, { passive: true });
canvas.addEventListener('touchstart', handleTouchStart, { passive: true });
canvas.addEventListener('click', handleClick);
-let frameId: number;
function render() {
- frameId = requestAnimationFrame(render);
pipeline.withColorAttachment({ view: context }).draw(3);
}
-frameId = requestAnimationFrame(render);
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+let frameId: number;
+function frame() {
+ frameId = requestAnimationFrame(frame);
+ render();
+}
+frameId = requestAnimationFrame(frame);
+
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+ render();
+ },
+});
export const controls = defineControls({
'Rectangle dims': {
diff --git a/apps/typegpu-docs/src/examples/simple/mesh-skinning/index.html b/apps/typegpu-docs/src/examples/simple/mesh-skinning/index.html
index e14a9c47e5..dc3b398f45 100644
--- a/apps/typegpu-docs/src/examples/simple/mesh-skinning/index.html
+++ b/apps/typegpu-docs/src/examples/simple/mesh-skinning/index.html
@@ -1,4 +1,4 @@
-
+
-
-
+
diff --git a/apps/typegpu-docs/src/examples/simple/oklab/index.ts b/apps/typegpu-docs/src/examples/simple/oklab/index.ts
index 4bb3a9798d..47db43b049 100644
--- a/apps/typegpu-docs/src/examples/simple/oklab/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/oklab/index.ts
@@ -27,8 +27,15 @@ document.addEventListener(
'mousemove',
(ev) => {
const rect = canvas.getBoundingClientRect();
- cssProbePosition.x = (ev.clientX - rect.x) / rect.width;
- cssProbePosition.y = 1 - (ev.clientY - rect.y) / rect.height;
+
+ const centerX = rect.x + rect.width / 2;
+ const centerY = rect.y + rect.height / 2;
+ const size = Math.min(rect.width, rect.height);
+ const squareLeft = centerX - size / 2;
+ const squareTop = centerY - size / 2;
+
+ cssProbePosition.x = std.saturate((ev.clientX - squareLeft) / size);
+ cssProbePosition.y = std.saturate(1 - (ev.clientY - squareTop) / size);
draw();
},
{
@@ -129,8 +136,16 @@ function draw() {
const chroma = pos.x;
const a = chroma * Math.cos(uniformsValue.hue);
const b = chroma * Math.sin(uniformsValue.hue);
- cssProbe.style.setProperty('--x', `${cssProbePosition.x * 100}%`);
- cssProbe.style.setProperty('--y', `${cssProbePosition.y * 100}%`);
+
+ const rect = canvas.getBoundingClientRect();
+ const centerX = rect.x + rect.width / 2;
+ const centerY = rect.y + rect.height / 2;
+ const size = Math.min(rect.width, rect.height);
+ const squareLeft = centerX - size / 2;
+ const squareTop = centerY - size / 2;
+
+ cssProbe.style.setProperty('--x', `${squareLeft - rect.left + cssProbePosition.x * size}px`);
+ cssProbe.style.setProperty('--y', `${squareTop - rect.top + cssProbePosition.y * size}px`);
canvas.parentElement?.style.setProperty('--l', `${lightness}`);
canvas.parentElement?.style.setProperty('--a', `${a}`);
canvas.parentElement?.style.setProperty('--b', `${b}`);
@@ -147,6 +162,10 @@ const detachAutoResizer = common.attachAutoResizer({
root,
canvas,
onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
draw();
},
});
diff --git a/apps/typegpu-docs/src/examples/simple/ripple-cube/index.html b/apps/typegpu-docs/src/examples/simple/ripple-cube/index.html
index aa8cc321b3..367ab17706 100644
--- a/apps/typegpu-docs/src/examples/simple/ripple-cube/index.html
+++ b/apps/typegpu-docs/src/examples/simple/ripple-cube/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/simple/ripple-cube/index.ts b/apps/typegpu-docs/src/examples/simple/ripple-cube/index.ts
index 198e228bba..33a67f2c09 100644
--- a/apps/typegpu-docs/src/examples/simple/ripple-cube/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/ripple-cube/index.ts
@@ -19,7 +19,7 @@ const perlinCache = perlin3d.staticCache({
size: d.vec3u(32, 32, 32),
});
-const [width, height] = [canvas.width / 1.4, canvas.height / 1.4];
+const resolutionScale = 1 / 1.4;
const cameraUniform = root.createUniform(Camera);
const timeUniform = root.createUniform(d.f32);
@@ -112,7 +112,12 @@ const envMapBindGroup = root.createBindGroup(envMapLayout, {
}),
});
-const postProcessing = createPostProcessingPipelines(root, width, height, initialBloom);
+const postProcessing = createPostProcessingPipelines(
+ root,
+ canvas.width * resolutionScale,
+ canvas.height * resolutionScale,
+ initialBloom,
+);
const cameraResult = setupOrbitCamera(
canvas,
@@ -138,7 +143,7 @@ const rayMarchPipeline = root
.createGuardedComputePipeline((x, y) => {
'use gpu';
randf.seed2(d.vec2f(d.f32(x), d.f32(y)).add(timeUniform.$));
- const textureSize = std.textureDimensions(postProcessing.result.writeView.$);
+ const textureSize = std.textureDimensions(postProcessing.$.result);
const uv = d.vec2f(x, y).add(0.5).div(d.vec2f(textureSize));
const ray = getRayForUV(uv);
@@ -186,7 +191,7 @@ const rayMarchPipeline = root
finalColor = std.mix(fogColor, sceneColor, fog);
}
- std.textureStore(postProcessing.result.writeView.$, d.vec2u(x, y), d.vec4f(finalColor, 1));
+ std.textureStore(postProcessing.$.result, d.vec2u(x, y), d.vec4f(finalColor, 1));
});
let animationFrame: number;
@@ -196,34 +201,47 @@ let accumulatedTime = 0;
let lastTimestamp = 0;
let isExtendedRipplesEnabled = false;
-function run(timestamp: number) {
- const deltaTime = lastTimestamp === 0 ? 0 : (timestamp - lastTimestamp) / 1000;
- lastTimestamp = timestamp;
- const maxTime = isExtendedRipplesEnabled ? 62 : 28;
- accumulatedTime = Math.min(maxTime, Math.max(0, accumulatedTime + deltaTime * timeScale));
- timeUniform.write(accumulatedTime);
-
- const jitterX = (halton(frameIndex % 16, 2) - 0.5) / width;
- const jitterY = (halton(frameIndex % 16, 3) - 0.5) / height;
+function render() {
+ const jitterX = (halton(frameIndex % 16, 2) - 0.5) / postProcessing.width;
+ const jitterY = (halton(frameIndex % 16, 3) - 0.5) / postProcessing.height;
jitterUniform.write(d.vec2f(jitterX, jitterY));
frameIndex++;
sdfPrecalcPipeline.dispatchThreads(GRID_SIZE / 2, GRID_SIZE / 2, GRID_SIZE / 2);
- rayMarchPipeline.with(envMapBindGroup).with(sdfBindGroup).dispatchThreads(width, height);
+ rayMarchPipeline
+ .with(envMapBindGroup)
+ .with(sdfBindGroup)
+ .with(postProcessing.userGroup)
+ .dispatchThreads(postProcessing.width, postProcessing.height);
postProcessing.runTaa();
-
postProcessing.runBloom();
-
postProcessing.render(context);
+}
+
+function run(timestamp: number) {
+ const deltaTime = lastTimestamp === 0 ? 0 : (timestamp - lastTimestamp) / 1000;
+ lastTimestamp = timestamp;
+ const maxTime = isExtendedRipplesEnabled ? 62 : 28;
+ accumulatedTime = Math.min(maxTime, Math.max(0, accumulatedTime + deltaTime * timeScale));
+ timeUniform.write(accumulatedTime);
+
+ render();
animationFrame = requestAnimationFrame(run);
}
animationFrame = requestAnimationFrame(run);
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ postProcessing.resize(canvas.width * resolutionScale, canvas.height * resolutionScale);
+ render();
+ },
+});
export const controls = defineControls({
metallic: {
diff --git a/apps/typegpu-docs/src/examples/simple/ripple-cube/post-processing.ts b/apps/typegpu-docs/src/examples/simple/ripple-cube/post-processing.ts
index 063c9af504..db231ee994 100644
--- a/apps/typegpu-docs/src/examples/simple/ripple-cube/post-processing.ts
+++ b/apps/typegpu-docs/src/examples/simple/ripple-cube/post-processing.ts
@@ -6,6 +6,10 @@ import { BloomParams } from './types.ts';
export const bloomParamsAccess = tgpu.accessor(BloomParams);
+const userLayout = tgpu.bindGroupLayout({
+ resultTexture: { storageTexture: d.textureStorage2d('rgba16float') },
+});
+
const taaResolveLayout = tgpu.bindGroupLayout({
currentTexture: { texture: d.texture2d() },
historyTexture: { texture: d.texture2d() },
@@ -41,19 +45,11 @@ function createProcessingTexture(root: TgpuRoot, width: number, height: number)
export function createPostProcessingPipelines(
root: TgpuRoot,
- width: number,
- height: number,
+ initialWidth: number,
+ initialHeight: number,
initialBloom: d.Infer,
) {
const bloomUniform = root.createUniform(BloomParams, initialBloom);
- const bloomWidth = Math.max(1, Math.floor(width / 2));
- const bloomHeight = Math.max(1, Math.floor(height / 2));
-
- const result = createProcessingTexture(root, width, height);
- const bloom = createProcessingTexture(root, bloomWidth, bloomHeight);
- const blurTemp = createProcessingTexture(root, bloomWidth, bloomHeight);
- const history = createProcessingTexture(root, width, height);
- const taaOutput = createProcessingTexture(root, width, height);
const sampler = root.createSampler({
magFilter: 'linear',
@@ -63,6 +59,7 @@ export function createPostProcessingPipelines(
const taaResolve = root.createGuardedComputePipeline((x, y) => {
'use gpu';
const coord = d.vec2i(d.i32(x), d.i32(y));
+ const dims = std.textureDimensions(taaResolveLayout.$.currentTexture);
const current = std.textureLoad(taaResolveLayout.$.currentTexture, coord, 0);
const historyColor = std.textureLoad(taaResolveLayout.$.historyTexture, coord, 0);
@@ -75,7 +72,7 @@ export function createPostProcessingPipelines(
const clampedCoord = std.clamp(
sampleCoord,
d.vec2i(0, 0),
- d.vec2i(d.i32(width) - 1, d.i32(height) - 1),
+ d.vec2i(d.i32(dims.x) - 1, d.i32(dims.y) - 1),
);
const neighbor = std.textureLoad(taaResolveLayout.$.currentTexture, clampedCoord, 0).rgb;
minColor = std.min(minColor, neighbor);
@@ -144,56 +141,109 @@ export function createPostProcessingPipelines(
fragment: fragmentMain,
});
- const taaBindGroup = root.createBindGroup(taaResolveLayout, {
- currentTexture: result.sampleView,
- historyTexture: history.sampleView,
- outputTexture: taaOutput.writeView,
- });
-
- const copyBindGroup = root.createBindGroup(processLayout, {
- inputTexture: taaOutput.sampleView,
- outputTexture: history.writeView,
- sampler,
- });
-
- const extractBindGroup = root.createBindGroup(processLayout, {
- inputTexture: result.sampleView,
- outputTexture: bloom.writeView,
- sampler,
- });
-
- const blurHorizontalBindGroup = root.createBindGroup(processLayout, {
- inputTexture: bloom.sampleView,
- outputTexture: blurTemp.writeView,
- sampler,
- });
-
- const blurVerticalBindGroup = root.createBindGroup(processLayout, {
- inputTexture: blurTemp.sampleView,
- outputTexture: bloom.writeView,
- sampler,
- });
-
- const compositeBindGroup = root.createBindGroup(compositeLayout, {
- colorTexture: taaOutput.sampleView,
- bloomTexture: bloom.sampleView,
- sampler,
- });
+ let width = initialWidth;
+ let height = initialHeight;
+ function createResolutionDependantResources() {
+ const bloomWidth = Math.max(1, Math.floor(width / 2));
+ const bloomHeight = Math.max(1, Math.floor(height / 2));
+
+ const result = createProcessingTexture(root, width, height);
+ const bloom = createProcessingTexture(root, bloomWidth, bloomHeight);
+ const blurTemp = createProcessingTexture(root, bloomWidth, bloomHeight);
+ const history = createProcessingTexture(root, width, height);
+ const taaOutput = createProcessingTexture(root, width, height);
+
+ return {
+ bloomWidth,
+ bloomHeight,
+
+ taaBindGroup: root.createBindGroup(taaResolveLayout, {
+ currentTexture: result.sampleView,
+ historyTexture: history.sampleView,
+ outputTexture: taaOutput.writeView,
+ }),
+
+ copyBindGroup: root.createBindGroup(processLayout, {
+ inputTexture: taaOutput.sampleView,
+ outputTexture: history.writeView,
+ sampler,
+ }),
+
+ extractBindGroup: root.createBindGroup(processLayout, {
+ inputTexture: result.sampleView,
+ outputTexture: bloom.writeView,
+ sampler,
+ }),
+
+ blurHorizontalBindGroup: root.createBindGroup(processLayout, {
+ inputTexture: bloom.sampleView,
+ outputTexture: blurTemp.writeView,
+ sampler,
+ }),
+
+ blurVerticalBindGroup: root.createBindGroup(processLayout, {
+ inputTexture: blurTemp.sampleView,
+ outputTexture: bloom.writeView,
+ sampler,
+ }),
+
+ compositeBindGroup: root.createBindGroup(compositeLayout, {
+ colorTexture: taaOutput.sampleView,
+ bloomTexture: bloom.sampleView,
+ sampler,
+ }),
+
+ userBindGroup: root.createBindGroup(userLayout, {
+ resultTexture: result.writeView,
+ }),
+ };
+ }
+
+ let resources = createResolutionDependantResources();
return {
- result,
bloomUniform,
+ get $() {
+ return {
+ result: userLayout.$.resultTexture,
+ };
+ },
+ get userGroup() {
+ return resources.userBindGroup;
+ },
+ get width() {
+ return width;
+ },
+ get height() {
+ return height;
+ },
+ resize: (newWidth: number, newHeight: number) => {
+ if (newWidth !== width || newHeight !== height) {
+ width = newWidth;
+ height = newHeight;
+ resources = createResolutionDependantResources();
+ }
+ },
runTaa: () => {
- taaResolve.with(taaBindGroup).dispatchThreads(width, height);
- copyToHistory.with(copyBindGroup).dispatchThreads(width, height);
+ taaResolve.with(resources.taaBindGroup).dispatchThreads(width, height);
+ copyToHistory.with(resources.copyBindGroup).dispatchThreads(width, height);
},
runBloom: () => {
- extractBright.with(extractBindGroup).dispatchThreads(bloomWidth, bloomHeight);
- blurHorizontal.with(blurHorizontalBindGroup).dispatchThreads(bloomWidth, bloomHeight);
- blurVertical.with(blurVerticalBindGroup).dispatchThreads(bloomWidth, bloomHeight);
+ extractBright
+ .with(resources.extractBindGroup)
+ .dispatchThreads(resources.bloomWidth, resources.bloomHeight);
+ blurHorizontal
+ .with(resources.blurHorizontalBindGroup)
+ .dispatchThreads(resources.bloomWidth, resources.bloomHeight);
+ blurVertical
+ .with(resources.blurVerticalBindGroup)
+ .dispatchThreads(resources.bloomWidth, resources.bloomHeight);
},
render: (targetView: ColorAttachment['view']) => {
- renderPipeline.with(compositeBindGroup).withColorAttachment({ view: targetView }).draw(3);
+ renderPipeline
+ .with(resources.compositeBindGroup)
+ .withColorAttachment({ view: targetView })
+ .draw(3);
},
};
}
diff --git a/apps/typegpu-docs/src/examples/simple/square/index.html b/apps/typegpu-docs/src/examples/simple/square/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/simple/square/index.html
+++ b/apps/typegpu-docs/src/examples/simple/square/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/simple/square/index.ts b/apps/typegpu-docs/src/examples/simple/square/index.ts
index fb4b115495..cb4d49a2f6 100644
--- a/apps/typegpu-docs/src/examples/simple/square/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/square/index.ts
@@ -66,6 +66,10 @@ const detachAutoResizer = common.attachAutoResizer({
root,
canvas,
onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
render();
},
});
diff --git a/apps/typegpu-docs/src/examples/simple/stencil/index.html b/apps/typegpu-docs/src/examples/simple/stencil/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/simple/stencil/index.html
+++ b/apps/typegpu-docs/src/examples/simple/stencil/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/simple/stencil/index.ts b/apps/typegpu-docs/src/examples/simple/stencil/index.ts
index b37b88251d..bd056b128a 100644
--- a/apps/typegpu-docs/src/examples/simple/stencil/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/stencil/index.ts
@@ -103,28 +103,34 @@ function frame(timestamp: number) {
}
frameId = requestAnimationFrame(frame);
-const resizeObserver = new ResizeObserver(() => {
- stencilTexture = root
- .createTexture({
- size: [canvas.width, canvas.height],
- format: 'stencil8',
- })
- .$usage('render');
-
- rotationUniform.write(d.mat2x2f.identity());
-
- writeStencilPipeline
- .withDepthStencilAttachment({
- view: stencilTexture,
- stencilClearValue: 0,
- stencilLoadOp: 'clear',
- stencilStoreOp: 'store',
- })
- .draw(3);
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+
+ stencilTexture = root
+ .createTexture({
+ size: [canvas.width, canvas.height],
+ format: 'stencil8',
+ })
+ .$usage('render');
+
+ rotationUniform.write(d.mat2x2f.identity());
+
+ writeStencilPipeline
+ .withDepthStencilAttachment({
+ view: stencilTexture,
+ stencilClearValue: 0,
+ stencilLoadOp: 'clear',
+ stencilStoreOp: 'store',
+ })
+ .draw(3);
+ },
});
-resizeObserver.observe(canvas);
-
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
export function onCleanup() {
if (frameId) {
diff --git a/apps/typegpu-docs/src/examples/simple/triangle/index.html b/apps/typegpu-docs/src/examples/simple/triangle/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/simple/triangle/index.html
+++ b/apps/typegpu-docs/src/examples/simple/triangle/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/simple/triangle/index.ts b/apps/typegpu-docs/src/examples/simple/triangle/index.ts
index 9d98ea8c1d..8ac4c836ae 100644
--- a/apps/typegpu-docs/src/examples/simple/triangle/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/triangle/index.ts
@@ -51,6 +51,11 @@ const detachAutoResizer = common.attachAutoResizer({
root,
canvas,
onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+
// Drawing once, and then each time the canvas resizes
pipeline.withColorAttachment({ view: context }).draw(3);
},
diff --git a/apps/typegpu-docs/src/examples/simple/vaporrave/index.html b/apps/typegpu-docs/src/examples/simple/vaporrave/index.html
index 581d6789f8..367ab17706 100644
--- a/apps/typegpu-docs/src/examples/simple/vaporrave/index.html
+++ b/apps/typegpu-docs/src/examples/simple/vaporrave/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts b/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts
index b76ab18446..05357d2192 100644
--- a/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts
+++ b/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts
@@ -132,6 +132,14 @@ let renderPipeline = root
fragment: fragmentMain,
});
+function render() {
+ floorAngleUniform.write(floorAngle);
+ sphereAngleUniform.write(sphereAngle);
+ resolutionUniform.write(d.vec2f(canvas.width, canvas.height));
+
+ renderPipeline.withColorAttachment({ view: context }).draw(3);
+}
+
let animationFrame: number;
let floorAngle = 0;
let sphereAngle = 0;
@@ -146,18 +154,20 @@ function run(timestamp: number) {
sphereAngle += delta * sphereSpeed;
sphereAngle %= c.NUM_CYCLES * Math.PI * 2;
- floorAngleUniform.write(floorAngle);
- sphereAngleUniform.write(sphereAngle);
- resolutionUniform.write(d.vec2f(canvas.width, canvas.height));
-
- renderPipeline.withColorAttachment({ view: context }).draw(3);
+ render();
animationFrame = requestAnimationFrame(run);
}
animationFrame = requestAnimationFrame(run);
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ render();
+ },
+});
// #region Example controls and cleanup
diff --git a/apps/typegpu-docs/src/examples/simulation/boids/index.html b/apps/typegpu-docs/src/examples/simulation/boids/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/simulation/boids/index.html
+++ b/apps/typegpu-docs/src/examples/simulation/boids/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/simulation/boids/index.ts b/apps/typegpu-docs/src/examples/simulation/boids/index.ts
index 6be2f42814..d062ebff5e 100644
--- a/apps/typegpu-docs/src/examples/simulation/boids/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/boids/index.ts
@@ -222,13 +222,8 @@ const computeBindGroups = [0, 1].map((idx) =>
);
let even = false;
-let animationFrameId: number;
-
-function frame() {
- even = !even;
-
- simulatePipeline.with(computeBindGroups[even ? 0 : 1]).dispatchThreads(triangleAmount);
+function render() {
renderPipeline
.withColorAttachment({
view: context,
@@ -236,13 +231,33 @@ function frame() {
})
.with(instanceLayout, trianglePosBuffers[even ? 1 : 0])
.draw(3, triangleAmount);
+}
+
+let animationFrameId: number;
+
+function frame() {
+ even = !even;
+
+ simulatePipeline.with(computeBindGroups[even ? 0 : 1]).dispatchThreads(triangleAmount);
+
+ render();
animationFrameId = requestAnimationFrame(frame);
}
animationFrameId = requestAnimationFrame(frame);
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+ render();
+ },
+});
// #region Example controls and cleanup
diff --git a/apps/typegpu-docs/src/examples/simulation/confetti/index.html b/apps/typegpu-docs/src/examples/simulation/confetti/index.html
index 581d6789f8..a1d12c864f 100644
--- a/apps/typegpu-docs/src/examples/simulation/confetti/index.html
+++ b/apps/typegpu-docs/src/examples/simulation/confetti/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.html b/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.html
+++ b/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.ts b/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.ts
index 43d62216b7..b3abdc375f 100644
--- a/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.ts
@@ -537,7 +537,17 @@ const runner = (timestamp: number) => {
animationFrameId = requestAnimationFrame(runner);
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+ primary.render();
+ },
+});
export const controls = defineControls({
'source intensity': {
diff --git a/apps/typegpu-docs/src/examples/simulation/fluid-with-atomics/index.html b/apps/typegpu-docs/src/examples/simulation/fluid-with-atomics/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/simulation/fluid-with-atomics/index.html
+++ b/apps/typegpu-docs/src/examples/simulation/fluid-with-atomics/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/simulation/fluid-with-atomics/index.ts b/apps/typegpu-docs/src/examples/simulation/fluid-with-atomics/index.ts
index 0e3b3f5733..5c8e836554 100644
--- a/apps/typegpu-docs/src/examples/simulation/fluid-with-atomics/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/fluid-with-atomics/index.ts
@@ -625,7 +625,17 @@ function run(timestamp: number) {
}
animationFrame = requestAnimationFrame(run);
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+ drawFrame();
+ },
+});
export const controls = defineControls({
size: {
diff --git a/apps/typegpu-docs/src/examples/simulation/game-of-life/index.html b/apps/typegpu-docs/src/examples/simulation/game-of-life/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/simulation/game-of-life/index.html
+++ b/apps/typegpu-docs/src/examples/simulation/game-of-life/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/simulation/game-of-life/index.ts b/apps/typegpu-docs/src/examples/simulation/game-of-life/index.ts
index 90ad1dd5bb..530859db52 100644
--- a/apps/typegpu-docs/src/examples/simulation/game-of-life/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/game-of-life/index.ts
@@ -345,6 +345,16 @@ const stepOnce = (timestamp: number) => {
);
};
+function render() {
+ const chosenDisplayPipeline =
+ chosenPipeline === 'bitpacked' ? bitpackedDisplayPipeline : displayPipeline;
+
+ chosenDisplayPipeline
+ .withColorAttachment({ view: context })
+ .with(displayBindGroups[1 - even])
+ .draw(3);
+}
+
let frameId: number;
function frame(timestamp: number) {
@@ -390,19 +400,23 @@ function frame(timestamp: number) {
}
}
- const chosenDisplayPipeline =
- chosenPipeline === 'bitpacked' ? bitpackedDisplayPipeline : displayPipeline;
-
- chosenDisplayPipeline
- .withColorAttachment({ view: context })
- .with(displayBindGroups[1 - even])
- .draw(3);
+ render();
frameId = requestAnimationFrame(frame);
}
frameId = requestAnimationFrame(frame);
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+ render();
+ },
+});
// #region Example controls & Cleanup
diff --git a/apps/typegpu-docs/src/examples/simulation/game-of-life/input.ts b/apps/typegpu-docs/src/examples/simulation/game-of-life/input.ts
index bc803b4395..a7d92b175f 100644
--- a/apps/typegpu-docs/src/examples/simulation/game-of-life/input.ts
+++ b/apps/typegpu-docs/src/examples/simulation/game-of-life/input.ts
@@ -1,3 +1,5 @@
+import { std } from 'typegpu';
+
export interface InputState {
drawPos: { x: number; y: number } | null;
lastDrawPos: { x: number; y: number } | null;
@@ -23,10 +25,17 @@ export function setupInput(canvas: HTMLCanvasElement): InputState {
y: (sy - 0.5) / state.zoomLevel + state.zoomCenter.y,
});
const toScreen = (clientX: number, clientY: number) => {
- const r = canvas.getBoundingClientRect();
+ const rect = canvas.getBoundingClientRect();
+
+ const centerX = rect.x + rect.width / 2;
+ const centerY = rect.y + rect.height / 2;
+ const size = Math.min(rect.width, rect.height);
+ const squareLeft = centerX - size / 2;
+ const squareTop = centerY - size / 2;
+
return {
- x: Math.min(1, Math.max(0, (clientX - r.left) / r.width)),
- y: Math.min(1, Math.max(0, (clientY - r.top) / r.height)),
+ x: std.saturate((clientX - squareLeft) / size),
+ y: std.saturate((clientY - squareTop) / size),
};
};
diff --git a/apps/typegpu-docs/src/examples/simulation/gravity/index.html b/apps/typegpu-docs/src/examples/simulation/gravity/index.html
index c83e0d8622..9f7e2b8386 100644
--- a/apps/typegpu-docs/src/examples/simulation/gravity/index.html
+++ b/apps/typegpu-docs/src/examples/simulation/gravity/index.html
@@ -38,4 +38,4 @@
under CC BY 4.0
-
+
diff --git a/apps/typegpu-docs/src/examples/simulation/gravity/index.ts b/apps/typegpu-docs/src/examples/simulation/gravity/index.ts
index 4e4b57a18f..99dadb6123 100644
--- a/apps/typegpu-docs/src/examples/simulation/gravity/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/gravity/index.ts
@@ -250,17 +250,18 @@ export const controls = defineControls({
},
});
-const resizeObserver = new ResizeObserver(() => {
- depthTexture.destroy();
- depthTexture = root.device.createTexture({
- size: [canvas.width, canvas.height, 1],
- format: 'depth24plus',
- usage: GPUTextureUsage.RENDER_ATTACHMENT,
- });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ depthTexture.destroy();
+ depthTexture = root.device.createTexture({
+ size: [canvas.width, canvas.height, 1],
+ format: 'depth24plus',
+ usage: GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+ },
});
-resizeObserver.observe(canvas);
-
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
function hideHelp() {
const helpElem = document.getElementById('help');
@@ -275,7 +276,6 @@ for (const eventName of ['click', 'keydown', 'wheel', 'touchstart']) {
export function onCleanup() {
destroyed = true;
cleanupCamera();
- resizeObserver.unobserve(canvas);
detachAutoResizer();
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.html b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.html
+++ b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts
index 746a3ef6c9..9208882fc7 100644
--- a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts
@@ -43,7 +43,7 @@ const Camera = d.struct({
const cameraTarget = resolution.div(2);
const cameraUp = d.vec3f(0, 1, 0);
const fov = (CAMERA_FOV_DEGREES * Math.PI) / 180;
-const aspect = canvas.width / canvas.height;
+const aspect = 1;
const near = 0.1;
const far = 1000.0;
@@ -439,9 +439,16 @@ const renderBindGroups = [0, 1].map((i) =>
}),
);
-let lastTime: number | null = null;
let currentTexture = 0;
+function render() {
+ renderPipeline
+ .withColorAttachment({ view: context })
+ .with(renderBindGroups[1 - currentTexture])
+ .draw(3);
+}
+
+let lastTime: number | null = null;
function frame(timestamp: number) {
const deltaTime = Math.min(lastTime !== null ? (timestamp - lastTime) / 1000 : 0, 0.1);
lastTime = timestamp;
@@ -460,10 +467,7 @@ function frame(timestamp: number) {
.with(bindGroups[currentTexture])
.dispatchWorkgroups(Math.ceil(NUM_AGENTS / AGENT_WORKGROUP_SIZE));
- renderPipeline
- .withColorAttachment({ view: context })
- .with(renderBindGroups[1 - currentTexture])
- .draw(3);
+ render();
currentTexture = 1 - currentTexture;
@@ -471,7 +475,17 @@ function frame(timestamp: number) {
}
requestAnimationFrame(frame);
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+ render();
+ },
+});
// #region Example controls and cleanup
diff --git a/apps/typegpu-docs/src/examples/simulation/slime-mold/index.html b/apps/typegpu-docs/src/examples/simulation/slime-mold/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/simulation/slime-mold/index.html
+++ b/apps/typegpu-docs/src/examples/simulation/slime-mold/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts b/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts
index 1b9dac673d..6b504c8482 100644
--- a/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts
@@ -7,7 +7,7 @@ const canvas = document.querySelector('canvas') as HTMLCanvasElement;
const context = root.configureContext({ canvas, alphaMode: 'premultiplied' });
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
-const resolution = d.vec2f(canvas.width, canvas.height);
+const resolution = d.vec2f(1024, 1024);
const Agent = d.struct({
position: d.vec2f,
@@ -217,9 +217,16 @@ const renderBindGroups = [0, 1].map((i) =>
}),
);
-let lastTime: number | null = null;
let currentTexture = 0;
+function render() {
+ renderPipeline
+ .withColorAttachment({ view: context })
+ .with(renderBindGroups[1 - currentTexture])
+ .draw(3);
+}
+
+let lastTime: number | null = null;
function frame(now: number) {
const deltaTimeValue = Math.min(lastTime !== null ? (now - lastTime) / 1000 : 0, 0.1);
lastTime = now;
@@ -232,10 +239,7 @@ function frame(now: number) {
computePipeline.with(bindGroups[currentTexture]).dispatchWorkgroups(Math.ceil(NUM_AGENTS / 64));
- renderPipeline
- .withColorAttachment({ view: context })
- .with(renderBindGroups[1 - currentTexture])
- .draw(3);
+ render();
currentTexture = 1 - currentTexture;
@@ -243,7 +247,17 @@ function frame(now: number) {
}
requestAnimationFrame(frame);
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+ render();
+ },
+});
// #region Example controls and cleanup
diff --git a/apps/typegpu-docs/src/examples/simulation/stable-fluid/index.html b/apps/typegpu-docs/src/examples/simulation/stable-fluid/index.html
index de6d9fbb40..9670960a81 100644
--- a/apps/typegpu-docs/src/examples/simulation/stable-fluid/index.html
+++ b/apps/typegpu-docs/src/examples/simulation/stable-fluid/index.html
@@ -27,4 +27,4 @@
Drag to stir the fluid.
-
+
diff --git a/apps/typegpu-docs/src/examples/simulation/stable-fluid/index.ts b/apps/typegpu-docs/src/examples/simulation/stable-fluid/index.ts
index 679013a5f9..57001f3409 100644
--- a/apps/typegpu-docs/src/examples/simulation/stable-fluid/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/stable-fluid/index.ts
@@ -32,9 +32,18 @@ function createComputePipeline(fn: TgpuComputeFn) {
return root.createComputePipeline({ compute: fn });
}
-function toGrid(x: number, y: number): [number, number] {
- const gx = Math.floor((x / canvas.width) * p.SIM_N);
- const gy = Math.floor(((canvas.height - y) / canvas.height) * p.SIM_N);
+function toGrid(clientX: number, clientY: number): [number, number] {
+ const rect = canvas.getBoundingClientRect();
+
+ // Taking into account the square aspect ratio
+ const centerX = rect.x + rect.width / 2;
+ const centerY = rect.y + rect.height / 2;
+ const size = Math.min(rect.width, rect.height);
+ const squareLeft = centerX - size / 2;
+ const squareTop = centerY - size / 2;
+
+ const gx = Math.floor(((clientX - squareLeft) / size) * p.SIM_N);
+ const gy = Math.floor((1 - (clientY - squareTop) / size) * p.SIM_N);
return [gx, gy];
}
@@ -273,10 +282,39 @@ const renderBindGroups = {
],
};
+function render() {
+ let renderBG: TgpuBindGroup<{
+ result: { texture: d.WgslTexture2d };
+ background: { texture: d.WgslTexture2d };
+ }>;
+ let pipeline: TgpuRenderPipeline;
+
+ switch (p.params.displayMode) {
+ case 'ink':
+ renderBG = renderBindGroups.ink[inkBuffer.currentIndex];
+ pipeline = renderPipelineInk;
+ break;
+ case 'image':
+ renderBG = renderBindGroups.ink[inkBuffer.currentIndex];
+ pipeline = renderPipelineImage;
+ break;
+ case 'velocity':
+ renderBG = renderBindGroups.velocity[velBuffer.currentIndex];
+ pipeline = renderPipelineVel;
+ break;
+ default:
+ throw new Error('Invalid display mode');
+ }
+
+ pipeline.withColorAttachment({ view: context }).with(renderBG).draw(3);
+}
+
// Main rendering loop
+let handle: number;
function loop() {
+ handle = requestAnimationFrame(loop);
+
if (p.params.paused) {
- requestAnimationFrame(loop);
return;
}
@@ -333,45 +371,28 @@ function loop() {
.dispatchWorkgroups(dispatchX, dispatchY);
inkBuffer.swap();
- let renderBG: TgpuBindGroup<{
- result: { texture: d.WgslTexture2d };
- background: { texture: d.WgslTexture2d };
- }>;
- let pipeline: TgpuRenderPipeline;
-
- switch (p.params.displayMode) {
- case 'ink':
- renderBG = renderBindGroups.ink[inkBuffer.currentIndex];
- pipeline = renderPipelineInk;
- break;
- case 'image':
- renderBG = renderBindGroups.ink[inkBuffer.currentIndex];
- pipeline = renderPipelineImage;
- break;
- case 'velocity':
- renderBG = renderBindGroups.velocity[velBuffer.currentIndex];
- pipeline = renderPipelineVel;
- break;
- default:
- throw new Error('Invalid display mode');
- }
-
- pipeline.withColorAttachment({ view: context }).with(renderBG).draw(3);
-
- requestAnimationFrame(loop);
+ render();
}
-loop();
-
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+handle = requestAnimationFrame(loop);
+
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+ render();
+ },
+});
// #region Example controls and cleanup
canvas.addEventListener('mousedown', (e) => {
- const x = e.offsetX * devicePixelRatio;
- const y = e.offsetY * devicePixelRatio;
brushState = {
- pos: toGrid(x, y),
+ pos: toGrid(e.clientX, e.clientY),
delta: [0, 0],
isDown: true,
};
@@ -381,11 +402,8 @@ canvas.addEventListener(
(e) => {
e.preventDefault();
const touch = e.touches[0];
- const rect = canvas.getBoundingClientRect();
- const x = (touch.clientX - rect.left) * devicePixelRatio;
- const y = (touch.clientY - rect.top) * devicePixelRatio;
brushState = {
- pos: toGrid(x, y),
+ pos: toGrid(touch.clientX, touch.clientY),
delta: [0, 0],
isDown: true,
};
@@ -404,9 +422,7 @@ const touchEndEventListener = () => {
window.addEventListener('touchend', touchEndEventListener);
canvas.addEventListener('mousemove', (e) => {
- const x = e.offsetX * devicePixelRatio;
- const y = e.offsetY * devicePixelRatio;
- const [newX, newY] = toGrid(x, y);
+ const [newX, newY] = toGrid(e.clientX, e.clientY);
brushState.delta = [newX - brushState.pos[0], newY - brushState.pos[1]];
brushState.pos = [newX, newY];
});
@@ -415,10 +431,7 @@ canvas.addEventListener(
(e) => {
e.preventDefault();
const touch = e.touches[0];
- const rect = canvas.getBoundingClientRect();
- const x = (touch.clientX - rect.left) * devicePixelRatio;
- const y = (touch.clientY - rect.top) * devicePixelRatio;
- const [newX, newY] = toGrid(x, y);
+ const [newX, newY] = toGrid(touch.clientX, touch.clientY);
brushState.delta = [newX - brushState.pos[0], newY - brushState.pos[1]];
brushState.pos = [newX, newY];
},
@@ -488,6 +501,7 @@ export function onCleanup() {
window.removeEventListener('mouseup', mouseUpEventListener);
window.removeEventListener('touchend', touchEndEventListener);
detachAutoResizer();
+ cancelAnimationFrame(handle);
root.destroy();
}
diff --git a/apps/typegpu-docs/src/examples/simulation/wind-map/index.html b/apps/typegpu-docs/src/examples/simulation/wind-map/index.html
index aa8cc321b3..8167d9f1c1 100644
--- a/apps/typegpu-docs/src/examples/simulation/wind-map/index.html
+++ b/apps/typegpu-docs/src/examples/simulation/wind-map/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/simulation/wind-map/index.ts b/apps/typegpu-docs/src/examples/simulation/wind-map/index.ts
index 90a75698cb..061e92efc8 100644
--- a/apps/typegpu-docs/src/examples/simulation/wind-map/index.ts
+++ b/apps/typegpu-docs/src/examples/simulation/wind-map/index.ts
@@ -267,7 +267,17 @@ const runAnimationFrame = () => {
};
runAnimationFrame();
-const detachAutoResizer = common.attachAutoResizer({ root, canvas });
+const detachAutoResizer = common.attachAutoResizer({
+ root,
+ canvas,
+ onResize() {
+ // Keeping the aspect ratio 1:1
+ const size = Math.min(canvas.width, canvas.height);
+ canvas.width = size;
+ canvas.height = size;
+ draw();
+ },
+});
// #region Example controls & Cleanup
diff --git a/apps/typegpu-docs/src/examples/tests/rc-docs-examples/index.html b/apps/typegpu-docs/src/examples/tests/rc-docs-examples/index.html
index aa8cc321b3..367ab17706 100644
--- a/apps/typegpu-docs/src/examples/tests/rc-docs-examples/index.html
+++ b/apps/typegpu-docs/src/examples/tests/rc-docs-examples/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/tests/sdf-docs-examples/index.html b/apps/typegpu-docs/src/examples/tests/sdf-docs-examples/index.html
index aa8cc321b3..367ab17706 100644
--- a/apps/typegpu-docs/src/examples/tests/sdf-docs-examples/index.html
+++ b/apps/typegpu-docs/src/examples/tests/sdf-docs-examples/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/tests/texture-test/index.html b/apps/typegpu-docs/src/examples/tests/texture-test/index.html
index aa8cc321b3..367ab17706 100644
--- a/apps/typegpu-docs/src/examples/tests/texture-test/index.html
+++ b/apps/typegpu-docs/src/examples/tests/texture-test/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/tests/uniformity/index.html b/apps/typegpu-docs/src/examples/tests/uniformity/index.html
index aa8cc321b3..367ab17706 100644
--- a/apps/typegpu-docs/src/examples/tests/uniformity/index.html
+++ b/apps/typegpu-docs/src/examples/tests/uniformity/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/threejs/attractors/index.html b/apps/typegpu-docs/src/examples/threejs/attractors/index.html
index 581d6789f8..a1d12c864f 100644
--- a/apps/typegpu-docs/src/examples/threejs/attractors/index.html
+++ b/apps/typegpu-docs/src/examples/threejs/attractors/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/threejs/compute-cloth/index.html b/apps/typegpu-docs/src/examples/threejs/compute-cloth/index.html
index 581d6789f8..a1d12c864f 100644
--- a/apps/typegpu-docs/src/examples/threejs/compute-cloth/index.html
+++ b/apps/typegpu-docs/src/examples/threejs/compute-cloth/index.html
@@ -1 +1 @@
-
+
diff --git a/apps/typegpu-docs/src/examples/threejs/compute-geometry/index.html b/apps/typegpu-docs/src/examples/threejs/compute-geometry/index.html
index 53fabe2eb9..037ff50a4a 100644
--- a/apps/typegpu-docs/src/examples/threejs/compute-geometry/index.html
+++ b/apps/typegpu-docs/src/examples/threejs/compute-geometry/index.html
@@ -1,5 +1,5 @@
Model is loading...
-
+