diff --git a/src/components/SampleLayout.tsx b/src/components/SampleLayout.tsx
index eca8a61a..6f93893e 100644
--- a/src/components/SampleLayout.tsx
+++ b/src/components/SampleLayout.tsx
@@ -136,6 +136,9 @@ const SampleLayout: React.FunctionComponent<
};
try {
const canvas = canvasRef.current;
+ if (!canvas) {
+ throw new Error('The canvas is not available');
+ }
const p = props.init({
canvas,
pageState,
@@ -190,7 +193,9 @@ const SampleLayout: React.FunctionComponent<
{props.description}
{error ? (
<>
- Is WebGPU Enabled?
+
+ Something went wrong. Do your browser and device support WebGPU?
+
{`${error}`}
>
) : null}
@@ -253,3 +258,9 @@ export const makeSample: (
) => JSX.Element = (props) => {
return ;
};
+
+export function assert(condition: unknown, msg?: string): asserts condition {
+ if (!condition) {
+ throw new Error(msg);
+ }
+}
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index 705fe378..86fbea7b 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -11,6 +11,10 @@ import { pages } from './samples/[slug]';
const title = 'WebGPU Samples';
+type PageType = {
+ [key: string]: React.ComponentType & { render: { preload: () => void } };
+};
+
const MainLayout: React.FunctionComponent = ({
Component,
pageProps,
@@ -71,7 +75,7 @@ const MainLayout: React.FunctionComponent = ({
key={slug}
className={className}
onMouseOver={() => {
- pages[slug].render.preload();
+ (pages as PageType)[slug].render.preload();
}}
>
import('../../sample/helloTriangle/main')),
helloTriangleMSAA: dynamic(
() => import('../../sample/helloTriangleMSAA/main')
@@ -59,9 +63,13 @@ export const getStaticPaths: GetStaticPaths = async () => {
export const getStaticProps: GetStaticProps = async ({
params,
}) => {
+ if (!params) {
+ return { notFound: true };
+ }
+
return {
props: {
- ...params,
+ slug: params.slug,
},
};
};
diff --git a/src/sample/animometer/main.ts b/src/sample/animometer/main.ts
index 5318c38e..8b6074af 100644
--- a/src/sample/animometer/main.ts
+++ b/src/sample/animometer/main.ts
@@ -1,9 +1,10 @@
-import { makeSample, SampleInit } from '../../components/SampleLayout';
+import { assert, makeSample, SampleInit } from '../../components/SampleLayout';
import animometerWGSL from './animometer.wgsl';
const init: SampleInit = async ({ canvas, pageState, gui }) => {
const adapter = await navigator.gpu.requestAdapter();
+ assert(adapter, 'requestAdapter returned null');
const device = await adapter.requestDevice();
if (!pageState.active) return;
@@ -17,7 +18,11 @@ const init: SampleInit = async ({ canvas, pageState, gui }) => {
const perfDisplay = document.createElement('pre');
perfDisplayContainer.appendChild(perfDisplay);
- canvas.parentNode.appendChild(perfDisplayContainer);
+ if (canvas.parentNode) {
+ canvas.parentNode.appendChild(perfDisplayContainer);
+ } else {
+ console.error('canvas.parentNode is null');
+ }
const params = new URLSearchParams(window.location.search);
const settings = {
@@ -272,16 +277,16 @@ const init: SampleInit = async ({ canvas, pageState, gui }) => {
}
}
- let startTime = undefined;
+ let startTime: number | undefined = undefined;
const uniformTime = new Float32Array([0]);
- const renderPassDescriptor: GPURenderPassDescriptor = {
+ const renderPassDescriptor = {
colorAttachments: [
{
- view: undefined, // Assigned later
+ view: undefined as any, // Assigned later
clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
- loadOp: 'clear',
- storeOp: 'store',
+ loadOp: 'clear' as const,
+ storeOp: 'store' as const,
},
],
};
@@ -292,7 +297,7 @@ const init: SampleInit = async ({ canvas, pageState, gui }) => {
recordRenderPass(renderBundleEncoder);
const renderBundle = renderBundleEncoder.finish();
- return function doDraw(timestamp) {
+ return function doDraw(timestamp: number) {
if (startTime === undefined) {
startTime = timestamp;
}
@@ -322,19 +327,23 @@ const init: SampleInit = async ({ canvas, pageState, gui }) => {
const updateSettings = () => {
doDraw = configure();
};
- gui
- .add(settings, 'numTriangles', 0, 200000)
- .step(1)
- .onFinishChange(updateSettings);
- gui.add(settings, 'renderBundles');
- gui.add(settings, 'dynamicOffsets');
-
- let previousFrameTimestamp = undefined;
- let jsTimeAvg = undefined;
- let frameTimeAvg = undefined;
+ if (gui === undefined) {
+ console.error('GUI not initialized');
+ } else {
+ gui
+ .add(settings, 'numTriangles', 0, 200000)
+ .step(1)
+ .onFinishChange(updateSettings);
+ gui.add(settings, 'renderBundles');
+ gui.add(settings, 'dynamicOffsets');
+ }
+
+ let previousFrameTimestamp: number | undefined = undefined;
+ let jsTimeAvg: number | undefined = undefined;
+ let frameTimeAvg: number | undefined = undefined;
let updateDisplay = true;
- function frame(timestamp) {
+ function frame(timestamp: number) {
// Sample is no longer the active page.
if (!pageState.active) return;
diff --git a/src/sample/computeBoids/main.ts b/src/sample/computeBoids/main.ts
index e6505bb1..ae8db740 100644
--- a/src/sample/computeBoids/main.ts
+++ b/src/sample/computeBoids/main.ts
@@ -1,10 +1,11 @@
-import { makeSample, SampleInit } from '../../components/SampleLayout';
+import { assert, makeSample, SampleInit } from '../../components/SampleLayout';
import spriteWGSL from './sprite.wgsl';
import updateSpritesWGSL from './updateSprites.wgsl';
const init: SampleInit = async ({ canvas, pageState, gui }) => {
const adapter = await navigator.gpu.requestAdapter();
+ assert(adapter, 'requestAdapter returned null');
const device = await adapter.requestDevice();
if (!pageState.active) return;
@@ -85,13 +86,13 @@ const init: SampleInit = async ({ canvas, pageState, gui }) => {
},
});
- const renderPassDescriptor: GPURenderPassDescriptor = {
+ const renderPassDescriptor = {
colorAttachments: [
{
- view: undefined, // Assigned later
+ view: undefined as any, // Assigned later
clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
- loadOp: 'clear',
- storeOp: 'store',
+ loadOp: 'clear' as const,
+ storeOp: 'store' as const,
},
],
};
@@ -144,7 +145,12 @@ const init: SampleInit = async ({ canvas, pageState, gui }) => {
updateSimParams();
Object.keys(simParams).forEach((k) => {
- gui.add(simParams, k).onFinishChange(updateSimParams);
+ const key = k as keyof typeof simParams;
+ if (gui === undefined) {
+ console.error('GUI not initialized');
+ } else {
+ gui.add(simParams, key).onFinishChange(updateSimParams);
+ }
});
const numParticles = 1500;
diff --git a/src/sample/resizeCanvas/main.ts b/src/sample/resizeCanvas/main.ts
index 58eea106..51be312f 100644
--- a/src/sample/resizeCanvas/main.ts
+++ b/src/sample/resizeCanvas/main.ts
@@ -1,4 +1,4 @@
-import { makeSample, SampleInit } from '../../components/SampleLayout';
+import { assert, makeSample, SampleInit } from '../../components/SampleLayout';
import triangleVertWGSL from '../../shaders/triangle.vert.wgsl';
import redFragWGSL from '../../shaders/red.frag.wgsl';
@@ -7,6 +7,7 @@ import styles from './animatedCanvasSize.module.css';
const init: SampleInit = async ({ canvas, pageState }) => {
const adapter = await navigator.gpu.requestAdapter();
+ assert(adapter, 'requestAdapter returned null');
const device = await adapter.requestDevice();
if (!pageState.active) return;
diff --git a/src/types.d.ts b/src/types.d.ts
index b12b0998..d32080f6 100644
--- a/src/types.d.ts
+++ b/src/types.d.ts
@@ -25,3 +25,6 @@ declare module '*.wgsl' {
const shader: string;
export default shader;
}
+
+declare module 'stats-js';
+declare module 'stanford-dragon/4';