Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 1 addition & 89 deletions apps/typegpu-docs/src/components/ExampleView.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import cs from 'classnames';
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import {
type RefObject,
Suspense,
useEffect,
useLayoutEffect,
useRef,
useState,
lazy,
} from 'react';
import { Suspense, useEffect, useLayoutEffect, useRef, useState, lazy } from 'react';
import { currentSnackbarAtom } from '../utils/examples/currentSnackbarAtom.ts';
import { tsoverUsedAtom, exampleFullscreenAtom } from '../utils/examples/exampleViewStateAtoms.ts';
import { ExecutionCancelledError } from '../utils/examples/errors.ts';
Expand Down Expand Up @@ -131,7 +123,6 @@ export function ExampleView({ example, common }: Props) {
}, [fullscreen, setFullscreen]);

const isLoading = useExample(tsImport, setSnackbarText);
useResizableCanvas(exampleHtmlRef);

return (
<>
Expand Down Expand Up @@ -348,85 +339,6 @@ function GPUUnsupportedPanel() {
);
}

function useResizableCanvas(exampleHtmlRef: RefObject<HTMLDivElement | null>) {
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.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { TgpuRoot } from 'typegpu';
import { useConfigureContext, useRoot } from '@typegpu/react';
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';

interface ExampleState {
onResize(): void;
onCleanup(): void;
}

Expand All @@ -12,8 +13,12 @@ interface HoverExampleLiveProps {

export default function HoverExampleLive({ setup }: HoverExampleLiveProps) {
const root = useRoot();
const exampleRef = useRef<ExampleState | null>(null);
const { ctxRef, ref: canvasRef } = useConfigureContext({
alphaMode: 'premultiplied',
onResize() {
exampleRef.current?.onResize();
},
});
const [isActive, setIsActive] = useState(false);
const [error, setError] = useState<unknown>(null);
Expand All @@ -40,13 +45,19 @@ export default function HoverExampleLive({ setup }: HoverExampleLiveProps) {

try {
const example = await setup(root, ctxRef.current);
onCleanup = () => example.onCleanup();

if (cancelled) {
onCleanup();
example.onCleanup();
return;
}

exampleRef.current = example;
onCleanup = () => {
exampleRef.current = null;
example.onCleanup();
};
// The initial resize notification can arrive before async setup finishes.
example.onResize();

setIsActive(true);
} catch (err) {
// Error boundaries can't catch throws from async effect callbacks, so
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ async function setup(root: TgpuRoot, context: GPUCanvasContext) {
const scene = await setupScene(root, context);
scene.qualityScale = await scene.computeOptimalQuality();

return { onCleanup: () => scene.onCleanup() };
return {
onResize: () => scene.onResize(),
onCleanup: () => scene.onCleanup(),
};
}

export default function JellySlider() {
Expand Down
46 changes: 0 additions & 46 deletions apps/typegpu-docs/src/components/stackblitz/stackBlitzIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,52 +10,6 @@ body.style.margin = '0';
body.style.boxSizing = 'border-box';
body.style.padding = '1rem';

// Resize canvases
for (const canvas of document.querySelectorAll('canvas')) {
if ('width' in canvas.attributes || 'height' in canvas.attributes) {
continue; // custom canvas, not replacing with resizable
}

const container = document.createElement('div');
const frame = document.createElement('div');

canvas.parentElement?.replaceChild(container, canvas);

frame.appendChild(canvas);
container.appendChild(frame);

container.style.display = 'flex';
container.style.flex = '1';
container.style.justifyContent = 'center';
container.style.alignItems = 'top';
container.style.width = '100%';

container.style.containerType = 'size';

frame.style.position = '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(calc(min(100cqw, 100cqh)/(${aspectRatio})), min(100cqw, 100cqh))`;
}

canvas.style.position = 'absolute';
canvas.style.width = '100%';
canvas.style.height = '100%';

const onResize = () => {
canvas.width = frame.clientWidth * window.devicePixelRatio;
canvas.height = frame.clientHeight * window.devicePixelRatio;
};

onResize();
new ResizeObserver(onResize).observe(container);
}

const controlsPanel = document.createElement('div');
controlsPanel.style.display = 'grid';
controlsPanel.style.gridTemplateColumns = '1fr 1fr';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<canvas data-fit-to-container></canvas>
<canvas class="w-full h-full"></canvas>
<div class="stats"></div>
<style>
.stats {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,11 @@ applyGridSize(...GRID_SIZES[gridSizeKey]);
applyTrack(generateGridTrack(trackSeed, ...GRID_SIZES[gridSizeKey]));
startSimulation();

const autoResizer = common.attachAutoResizer({
root,
canvas,
});

// #region Example controls & Cleanup

export const controls = defineControls({
Expand Down Expand Up @@ -623,6 +628,7 @@ export const controls = defineControls({

export function onCleanup() {
cancelAnimationFrame(rafHandle);
autoResizer.detach();
root.destroy();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<canvas data-fit-to-container></canvas>
<canvas class="w-full h-full"></canvas>
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ let resolutionScale = 0.5;
let [width, height] = [0, 0];

function updateDimensions() {
[width, height] = [canvas.width, canvas.height].map((v) => Math.floor(v * resolutionScale));
[width, height] = [canvas.width, canvas.height].map((v) =>
Math.max(1, Math.floor(v * resolutionScale)),
);
}
updateDimensions();

Expand Down Expand Up @@ -342,8 +344,6 @@ function recreateResources() {
sourceIdx = 0;
}

clearCanvas();

function getTouchPosition(rect: DOMRect, touches: TouchList) {
if (touches.length === 2) {
return {
Expand All @@ -357,17 +357,16 @@ function getTouchPosition(rect: DOMRect, touches: TouchList) {
};
}

// #region Example controls & Cleanup

let resizeTimeout: ReturnType<typeof setTimeout>;
const resizeObserver = new ResizeObserver(() => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
const autoResizer = 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) {
Expand Down Expand Up @@ -475,8 +474,7 @@ export const controls = defineControls({
});

export function onCleanup() {
clearTimeout(resizeTimeout);
resizeObserver.disconnect();
autoResizer.detach();
root.destroy();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<canvas data-fit-to-container></canvas>
<canvas class="w-full h-full"></canvas>
Original file line number Diff line number Diff line change
Expand Up @@ -241,17 +241,16 @@ function reset() {

reset();

// #region Example controls & Cleanup

let resizeTimeout: ReturnType<typeof setTimeout>;
const resizeObserver = new ResizeObserver(() => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
const autoResizer = common.attachAutoResizer({
root,
canvas,
onResize() {
recreateResources();
reset();
}, 100);
},
});
resizeObserver.observe(canvas);

// #region Example controls & Cleanup

export const controls = defineControls({
'Run Algorithm': {
Expand Down Expand Up @@ -295,8 +294,7 @@ export const controls = defineControls({
});

export function onCleanup() {
clearTimeout(resizeTimeout);
resizeObserver.disconnect();
autoResizer.detach();
root.destroy();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="loading">🤖 Downloading the model...</div>
<div class="example">
<canvas></canvas>
<div class="example gap-4 flex-col md:flex-row">
<canvas class="w-80 h-80"></canvas>
<div class="predictions-container">
<div class="bars-container">
<div class="bar">O</div>
Expand Down Expand Up @@ -37,7 +37,7 @@
}

.loading.loaded ~ .example {
display: contents;
display: flex;
}

.predictions-container {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { tgpu, d, std } from 'typegpu';
import { tgpu, common, d, std } from 'typegpu';
import type { LayerData, Network } from './data.ts';
import { downloadLayers } from './helpers.ts';
import { defineControls } from '../../common/defineControls.ts';
Expand Down Expand Up @@ -281,6 +281,8 @@ precisionEl.className = hasShaderF16 ? 'enabled' : 'disabled';

run();

const autoResizer = common.attachAutoResizer({ root, canvas });

canvas.addEventListener('mousedown', () => {
uiState.isDrawing = true;
});
Expand Down Expand Up @@ -428,6 +430,7 @@ export function onCleanup() {
cancelAnimationFrame(animationFrameId);
window.removeEventListener('mouseup', mouseUpEventListener);
window.removeEventListener('touchend', touchEndEventListener);
autoResizer.detach();
root.destroy();
}

Expand Down
2 changes: 1 addition & 1 deletion apps/typegpu-docs/src/examples/algorithms/sort/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<canvas></canvas>
<canvas class="w-full h-full object-contain"></canvas>
<div id="sort-overlay" hidden>
<div id="sort-spinner"></div>
<span id="sort-status"></span>
Expand Down
Loading
Loading