diff --git a/sample/a-buffer/main.ts b/sample/a-buffer/main.ts index f4713ffa..ed9011ce 100644 --- a/sample/a-buffer/main.ts +++ b/sample/a-buffer/main.ts @@ -1,6 +1,7 @@ import { mat4, vec3 } from 'wgpu-matrix'; import { GUI } from 'dat.gui'; +import { initDeviceAndErrorDialog } from '../util'; import { mesh } from '../../meshes/teapot'; import opaqueWGSL from './opaque.wgsl'; @@ -12,8 +13,7 @@ function roundUp(n: number, k: number): number { } const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; const presentationFormat = navigator.gpu.getPreferredCanvasFormat(); diff --git a/sample/animometer/main.ts b/sample/animometer/main.ts index 72e814e5..128e84e9 100644 --- a/sample/animometer/main.ts +++ b/sample/animometer/main.ts @@ -1,9 +1,9 @@ import { GUI } from 'dat.gui'; import animometerWGSL from './animometer.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const perfDisplayContainer = document.createElement('div'); perfDisplayContainer.style.color = 'white'; diff --git a/sample/bitonicSort/utils.ts b/sample/bitonicSort/utils.ts index 371401d9..cdc43357 100644 --- a/sample/bitonicSort/utils.ts +++ b/sample/bitonicSort/utils.ts @@ -1,5 +1,6 @@ import type { GUI } from 'dat.gui'; import fullscreenTexturedQuad from '../../shaders/fullscreenTexturedQuad.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; type BindGroupBindingLayout = | GPUBufferBindingLayout @@ -111,16 +112,13 @@ export const SampleInitFactoryWebGPU = async ( callback: SampleInitCallback3D ): Promise => { const init = async ({ canvas, gui, stats }) => { - const adapter = await navigator.gpu.requestAdapter(); - const timestampQueryAvailable = adapter.features.has('timestamp-query'); - let device: GPUDevice; - if (timestampQueryAvailable) { - device = await adapter.requestDevice({ - requiredFeatures: ['timestamp-query'], - }); - } else { - device = await adapter.requestDevice(); - } + let timestampQueryAvailable; + const device = await initDeviceAndErrorDialog({}, (adapter) => { + timestampQueryAvailable = adapter.features.has('timestamp-query'); + return { + requiredFeatures: timestampQueryAvailable ? ['timestamp-query'] : [], + }; + }); const context = canvas.getContext('webgpu') as GPUCanvasContext; const devicePixelRatio = window.devicePixelRatio; canvas.width = canvas.clientWidth * devicePixelRatio; diff --git a/sample/cameras/main.ts b/sample/cameras/main.ts index 648cb61a..9ff195ba 100644 --- a/sample/cameras/main.ts +++ b/sample/cameras/main.ts @@ -10,6 +10,7 @@ import { import cubeWGSL from './cube.wgsl'; import { ArcballCamera, WASDCamera } from './camera'; import { createInputHandler } from './input'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; @@ -39,8 +40,7 @@ gui.add(params, 'type', ['arcball', 'WASD']).onChange(() => { oldCameraType = newCameraType; }); -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; const devicePixelRatio = window.devicePixelRatio; diff --git a/sample/computeBoids/main.ts b/sample/computeBoids/main.ts index 8a2ea996..02d806f6 100644 --- a/sample/computeBoids/main.ts +++ b/sample/computeBoids/main.ts @@ -1,13 +1,16 @@ +import { initDeviceAndErrorDialog } from '../util'; import spriteWGSL from './sprite.wgsl'; import updateSpritesWGSL from './updateSprites.wgsl'; import { GUI } from 'dat.gui'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const hasTimestampQuery = adapter.features.has('timestamp-query'); -const device = await adapter.requestDevice({ - requiredFeatures: hasTimestampQuery ? ['timestamp-query'] : [], +let hasTimestampQuery; +const device = await initDeviceAndErrorDialog({}, (adapter) => { + hasTimestampQuery = adapter.features.has('timestamp-query'); + return { + requiredFeatures: hasTimestampQuery ? ['timestamp-query'] : [], + }; }); const perfDisplayContainer = document.createElement('div'); diff --git a/sample/cornell/main.ts b/sample/cornell/main.ts index 38a1bcba..39c52b41 100644 --- a/sample/cornell/main.ts +++ b/sample/cornell/main.ts @@ -5,21 +5,24 @@ import Radiosity from './radiosity'; import Rasterizer from './rasterizer'; import Tonemapper from './tonemapper'; import Raytracer from './raytracer'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const presentationFormat = navigator.gpu.getPreferredCanvasFormat(); -const requiredFeatures: GPUFeatureName[] = - presentationFormat === 'bgra8unorm' ? ['bgra8unorm-storage'] : []; -const adapter = await navigator.gpu.requestAdapter(); -for (const feature of requiredFeatures) { - if (!adapter.features.has(feature)) { - throw new Error( - `sample requires ${feature}, but is not supported by the adapter` - ); +let presentationFormat; +const device = await initDeviceAndErrorDialog({}, (adapter) => { + presentationFormat = navigator.gpu.getPreferredCanvasFormat(); + const requiredFeatures: GPUFeatureName[] = + presentationFormat === 'bgra8unorm' ? ['bgra8unorm-storage'] : []; + for (const feature of requiredFeatures) { + if (!adapter.features.has(feature)) { + throw new Error( + `sample requires ${feature}, but is not supported by the adapter` + ); + } } -} -const device = await adapter.requestDevice({ requiredFeatures }); + return { requiredFeatures }; +}); const params: { renderer: 'rasterizer' | 'raytracer'; diff --git a/sample/cubemap/main.ts b/sample/cubemap/main.ts index 7a423ff2..4c207de2 100644 --- a/sample/cubemap/main.ts +++ b/sample/cubemap/main.ts @@ -10,10 +10,10 @@ import { import basicVertWGSL from '../../shaders/basic.vert.wgsl'; import sampleCubemapWGSL from './sampleCubemap.frag.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/deferredRendering/main.ts b/sample/deferredRendering/main.ts index e5d95621..8a535020 100644 --- a/sample/deferredRendering/main.ts +++ b/sample/deferredRendering/main.ts @@ -8,14 +8,14 @@ import fragmentWriteGBuffers from './fragmentWriteGBuffers.wgsl'; import vertexTextureQuad from './vertexTextureQuad.wgsl'; import fragmentGBuffersDebugView from './fragmentGBuffersDebugView.wgsl'; import fragmentDeferredRendering from './fragmentDeferredRendering.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const kMaxNumLights = 1024; const lightExtentMin = vec3.fromValues(-50, -30, -50); const lightExtentMax = vec3.fromValues(50, 50, 50); const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/fractalCube/main.ts b/sample/fractalCube/main.ts index 35c4fa66..afcc00fa 100644 --- a/sample/fractalCube/main.ts +++ b/sample/fractalCube/main.ts @@ -10,10 +10,10 @@ import { import basicVertWGSL from '../../shaders/basic.vert.wgsl'; import sampleSelfWGSL from './sampleSelf.frag.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/gameOfLife/main.ts b/sample/gameOfLife/main.ts index 3d5589ad..a367159c 100644 --- a/sample/gameOfLife/main.ts +++ b/sample/gameOfLife/main.ts @@ -2,10 +2,10 @@ import { GUI } from 'dat.gui'; import computeWGSL from './compute.wgsl'; import vertWGSL from './vert.wgsl'; import fragWGSL from './frag.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; const devicePixelRatio = window.devicePixelRatio; diff --git a/sample/helloTriangle/main.ts b/sample/helloTriangle/main.ts index ab7248f3..683be8f9 100644 --- a/sample/helloTriangle/main.ts +++ b/sample/helloTriangle/main.ts @@ -1,9 +1,9 @@ import triangleVertWGSL from '../../shaders/triangle.vert.wgsl'; import redFragWGSL from '../../shaders/red.frag.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/helloTriangleMSAA/main.ts b/sample/helloTriangleMSAA/main.ts index ad34cea5..ae2f24fb 100644 --- a/sample/helloTriangleMSAA/main.ts +++ b/sample/helloTriangleMSAA/main.ts @@ -1,9 +1,9 @@ import triangleVertWGSL from '../../shaders/triangle.vert.wgsl'; import redFragWGSL from '../../shaders/red.frag.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/imageBlur/main.ts b/sample/imageBlur/main.ts index b1bab78f..06e1b6f1 100644 --- a/sample/imageBlur/main.ts +++ b/sample/imageBlur/main.ts @@ -1,14 +1,14 @@ import { GUI } from 'dat.gui'; import blurWGSL from './blur.wgsl'; import fullscreenTexturedQuadWGSL from '../../shaders/fullscreenTexturedQuad.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; // Contants from the blur.wgsl shader. const tileDim = 128; const batch = [4, 4]; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/instancedCube/main.ts b/sample/instancedCube/main.ts index 87265398..f341aafb 100644 --- a/sample/instancedCube/main.ts +++ b/sample/instancedCube/main.ts @@ -10,10 +10,10 @@ import { import instancedVertWGSL from './instanced.vert.wgsl'; import vertexPositionColorWGSL from '../../shaders/vertexPositionColor.frag.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/multipleCanvases/main.ts b/sample/multipleCanvases/main.ts index 45a1f542..10c3b67c 100644 --- a/sample/multipleCanvases/main.ts +++ b/sample/multipleCanvases/main.ts @@ -1,6 +1,7 @@ /* eslint-disable prettier/prettier */ import { mat4, mat3 } from 'wgpu-matrix'; import { modelData } from './models'; +import { initDeviceAndErrorDialog } from '../util'; type TypedArrayView = Float32Array | Uint32Array; @@ -46,8 +47,7 @@ function createVertexAndIndexBuffer( }; } -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const models = Object.values(modelData).map(data => createVertexAndIndexBuffer(device, data)); diff --git a/sample/normalMap/main.ts b/sample/normalMap/main.ts index fe4f8189..ea929b0e 100644 --- a/sample/normalMap/main.ts +++ b/sample/normalMap/main.ts @@ -8,6 +8,7 @@ import { create3DRenderPipeline, createTextureFromImage, } from './utils'; +import { initDeviceAndErrorDialog } from '../util'; const MAT4X4_BYTES = 64; enum TextureAtlas { @@ -17,8 +18,7 @@ enum TextureAtlas { } const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; const devicePixelRatio = window.devicePixelRatio; canvas.width = canvas.clientWidth * devicePixelRatio; diff --git a/sample/particles/main.ts b/sample/particles/main.ts index e718fd91..a25d8819 100644 --- a/sample/particles/main.ts +++ b/sample/particles/main.ts @@ -3,6 +3,7 @@ import { GUI } from 'dat.gui'; import particleWGSL from './particle.wgsl'; import probabilityMapWGSL from './probabilityMap.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const numParticles = 50000; const particlePositionOffset = 0; @@ -16,8 +17,7 @@ const particleInstanceByteSize = 0; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/points/main.ts b/sample/points/main.ts index 6926af7d..323231d9 100644 --- a/sample/points/main.ts +++ b/sample/points/main.ts @@ -5,6 +5,7 @@ import distanceSizedPointsVertWGSL from './distance-sized-points.vert.wgsl'; import fixedSizePointsVertWGSL from './fixed-size-points.vert.wgsl'; import orangeFragWGSL from './orange.frag.wgsl'; import texturedFragWGSL from './textured.frag.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; // See: https://www.google.com/search?q=fibonacci+sphere function createFibonacciSphereVertices({ @@ -28,8 +29,7 @@ function createFibonacciSphereVertices({ return new Float32Array(vertices); } -const adapter = await navigator.gpu?.requestAdapter(); -const device = await adapter?.requestDevice(); +const device = await initDeviceAndErrorDialog(); // Get a WebGPU context from the canvas and configure it const canvas = document.querySelector('canvas') as HTMLCanvasElement; diff --git a/sample/renderBundles/main.ts b/sample/renderBundles/main.ts index 2d50bcfb..197157f0 100644 --- a/sample/renderBundles/main.ts +++ b/sample/renderBundles/main.ts @@ -4,6 +4,7 @@ import { createSphereMesh, SphereLayout } from '../../meshes/sphere'; import Stats from 'stats.js'; import meshWGSL from './mesh.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; interface Renderable { vertices: GPUBuffer; @@ -13,8 +14,7 @@ interface Renderable { } const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const settings = { useRenderBundles: true, diff --git a/sample/resizeCanvas/main.ts b/sample/resizeCanvas/main.ts index bf73f901..364bab61 100644 --- a/sample/resizeCanvas/main.ts +++ b/sample/resizeCanvas/main.ts @@ -1,9 +1,9 @@ import triangleVertWGSL from '../../shaders/triangle.vert.wgsl'; import redFragWGSL from '../../shaders/red.frag.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/resizeObserverHDDPI/main.ts b/sample/resizeObserverHDDPI/main.ts index fac614d0..5b089fa4 100644 --- a/sample/resizeObserverHDDPI/main.ts +++ b/sample/resizeObserverHDDPI/main.ts @@ -1,9 +1,9 @@ import { GUI } from 'dat.gui'; import checkerWGSL from './checker.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/reversedZ/main.ts b/sample/reversedZ/main.ts index bda1c2d7..bd1922d6 100644 --- a/sample/reversedZ/main.ts +++ b/sample/reversedZ/main.ts @@ -8,6 +8,7 @@ import vertexTextureQuadWGSL from './vertexTextureQuad.wgsl'; import fragmentTextureQuadWGSL from './fragmentTextureQuad.wgsl'; import vertexPrecisionErrorPassWGSL from './vertexPrecisionErrorPass.wgsl'; import fragmentPrecisionErrorPassWGSL from './fragmentPrecisionErrorPass.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; // Two planes close to each other for depth precision test const geometryVertexSize = 4 * 8; // Byte size of one geometry vertex. @@ -65,8 +66,7 @@ const depthClearValues = { }; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/rotatingCube/main.ts b/sample/rotatingCube/main.ts index 5dd83191..bf1554f4 100644 --- a/sample/rotatingCube/main.ts +++ b/sample/rotatingCube/main.ts @@ -10,10 +10,10 @@ import { import basicVertWGSL from '../../shaders/basic.vert.wgsl'; import vertexPositionColorWGSL from '../../shaders/vertexPositionColor.frag.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/samplerParameters/main.ts b/sample/samplerParameters/main.ts index 4917ecad..6bc8bc4d 100644 --- a/sample/samplerParameters/main.ts +++ b/sample/samplerParameters/main.ts @@ -3,6 +3,7 @@ import { GUI } from 'dat.gui'; import texturedSquareWGSL from './texturedSquare.wgsl'; import showTextureWGSL from './showTexture.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const kMatrices: Readonly = new Float32Array([ // Row 1: Scale by 2 @@ -27,8 +28,7 @@ const kMatrices: Readonly = new Float32Array([ ]); const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); // // GUI controls diff --git a/sample/shadowMapping/main.ts b/sample/shadowMapping/main.ts index fd94c6d2..c3873a69 100644 --- a/sample/shadowMapping/main.ts +++ b/sample/shadowMapping/main.ts @@ -4,12 +4,12 @@ import { mesh } from '../../meshes/stanfordDragon'; import vertexShadowWGSL from './vertexShadow.wgsl'; import vertexWGSL from './vertex.wgsl'; import fragmentWGSL from './fragment.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const shadowDepthTextureSize = 1024; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/skinnedMesh/main.ts b/sample/skinnedMesh/main.ts index 8203889e..d9349d6b 100644 --- a/sample/skinnedMesh/main.ts +++ b/sample/skinnedMesh/main.ts @@ -9,6 +9,7 @@ import { createSkinnedGridRenderPipeline, } from './gridUtils'; import { gridIndices } from './gridData'; +import { initDeviceAndErrorDialog } from '../util'; const MAT4X4_BYTES = 64; @@ -94,8 +95,7 @@ const getRotation = (mat: Mat4): Quat => { //Normal setup const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/textRenderingMsdf/main.ts b/sample/textRenderingMsdf/main.ts index 0e13daab..3f1aaad1 100644 --- a/sample/textRenderingMsdf/main.ts +++ b/sample/textRenderingMsdf/main.ts @@ -11,10 +11,10 @@ import { MsdfTextRenderer } from './msdfText'; import basicVertWGSL from '../../shaders/basic.vert.wgsl'; import vertexPositionColorWGSL from '../../shaders/vertexPositionColor.frag.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/texturedCube/main.ts b/sample/texturedCube/main.ts index 73442116..c5601d5d 100644 --- a/sample/texturedCube/main.ts +++ b/sample/texturedCube/main.ts @@ -10,10 +10,10 @@ import { import basicVertWGSL from '../../shaders/basic.vert.wgsl'; import sampleTextureMixColorWGSL from './sampleTextureMixColor.frag.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/twoCubes/main.ts b/sample/twoCubes/main.ts index 4a9f4374..63cc2adc 100644 --- a/sample/twoCubes/main.ts +++ b/sample/twoCubes/main.ts @@ -10,10 +10,10 @@ import { import basicVertWGSL from '../../shaders/basic.vert.wgsl'; import vertexPositionColorWGSL from '../../shaders/vertexPositionColor.frag.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/util.ts b/sample/util.ts new file mode 100644 index 00000000..ba1ca92c --- /dev/null +++ b/sample/util.ts @@ -0,0 +1,87 @@ +/** Asserts a condition is truthy; throws an Error if not. */ +export function assert(condition: unknown, msg?: string): asserts condition { + if (!condition) { + throw new Error(msg); + } +} + +/** + * Tries to get a GPUDevice. Shows an error dialog if initialization fails, or + * if later there is a device loss or uncaptured error. + */ +export async function initDeviceAndErrorDialog( + requestAdapterOptions: GPURequestAdapterOptions | undefined = undefined, + computeDeviceDescriptor: ( + adapter: GPUAdapter + ) => GPUDeviceDescriptor | undefined = () => undefined +): Promise { + const dialog = initDialog(); + try { + assert( + 'gpu' in navigator, + 'WebGPU is not available in this browser (navigator.gpu is undefined)' + ); + + const adapter = await navigator.gpu.requestAdapter(requestAdapterOptions); + assert( + adapter, + 'WebGPU is not available on this system (requestAdapter() returned null)' + ); + + const deviceDescriptor = computeDeviceDescriptor(adapter); + const device = await adapter.requestDevice(deviceDescriptor); + device.lost.then((reason) => { + dialog.show(`Device lost ("${reason.reason}"):\n${reason.message}`); + }); + device.onuncapturederror = (error) => { + dialog.show(`Uncaptured error:\n${error.error.message}`); + }; + + dialog.close(); + return device; + } catch (ex) { + dialog.show( + ex instanceof Error ? (ex.message ? ex.message : ex.stack) : 'error' + ); + throw ex; + } +} + +function initDialog() { + if (typeof document === 'undefined') { + // Not implemented in workers. + return { + show(msg: string) { + console.error(msg); + }, + close() {}, + }; + } + + const dialogBox = document.createElement('dialog'); + document.body.append(dialogBox); + + const dialogText = document.createElement('pre'); + dialogText.style.whiteSpace = 'pre-wrap'; + dialogBox.append(dialogText); + + const closeBtn = document.createElement('button'); + closeBtn.textContent = 'OK'; + closeBtn.onclick = () => dialogBox.close(); + dialogBox.append(closeBtn); + + return { + show(msg: string) { + // Don't overwrite the dialog message while it's still open + // (show the first error, not the most recent error). + if (!dialogBox.open) { + dialogText.textContent = msg; + dialogBox.showModal(); + } + }, + close() { + dialogText.textContent = ''; + dialogBox.close(); + }, + }; +} diff --git a/sample/videoUploading/main.ts b/sample/videoUploading/main.ts index 35283202..5f991e8a 100644 --- a/sample/videoUploading/main.ts +++ b/sample/videoUploading/main.ts @@ -1,6 +1,9 @@ import { GUI } from 'dat.gui'; import fullscreenTexturedQuadWGSL from '../../shaders/fullscreenTexturedQuad.wgsl'; import sampleExternalTextureWGSL from '../../shaders/sampleExternalTexture.frag.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; + +const device = await initDeviceAndErrorDialog(); // Set video element const video = document.createElement('video'); @@ -10,9 +13,6 @@ video.muted = true; video.src = '../../assets/video/pano.webm'; await video.play(); -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); - const canvas = document.querySelector('canvas') as HTMLCanvasElement; const context = canvas.getContext('webgpu') as GPUCanvasContext; const devicePixelRatio = window.devicePixelRatio; diff --git a/sample/videoUploading/video.ts b/sample/videoUploading/video.ts index 3c79d578..0a3d6d3b 100644 --- a/sample/videoUploading/video.ts +++ b/sample/videoUploading/video.ts @@ -1,6 +1,7 @@ import { GUI } from 'dat.gui'; import fullscreenTexturedQuadWGSL from '../../shaders/fullscreenTexturedQuad.wgsl'; import sampleExternalTextureWGSL from '../../shaders/sampleExternalTexture.frag.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; export default async function ({ useVideoFrame }: { useVideoFrame: boolean }) { // Set video element @@ -11,8 +12,7 @@ export default async function ({ useVideoFrame }: { useVideoFrame: boolean }) { video.src = '../../assets/video/pano.webm'; await video.play(); - const adapter = await navigator.gpu.requestAdapter(); - const device = await adapter.requestDevice(); + const device = await initDeviceAndErrorDialog(); const canvas = document.querySelector('canvas') as HTMLCanvasElement; const context = canvas.getContext('webgpu') as GPUCanvasContext; diff --git a/sample/volumeRenderingTexture3D/main.ts b/sample/volumeRenderingTexture3D/main.ts index 3b36ec74..eee4de0c 100644 --- a/sample/volumeRenderingTexture3D/main.ts +++ b/sample/volumeRenderingTexture3D/main.ts @@ -1,6 +1,7 @@ import { mat4 } from 'wgpu-matrix'; import { GUI } from 'dat.gui'; import volumeWGSL from './volume.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; const canvas = document.querySelector('canvas') as HTMLCanvasElement; @@ -17,8 +18,7 @@ gui.add(params, 'rotateCamera', true); gui.add(params, 'near', 2.0, 7.0); gui.add(params, 'far', 2.0, 7.0); -const adapter = await navigator.gpu.requestAdapter(); -const device = await adapter.requestDevice(); +const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu') as GPUCanvasContext; const sampleCount = 4; diff --git a/sample/worker/worker.ts b/sample/worker/worker.ts index 4a3e52f9..d07f6a97 100644 --- a/sample/worker/worker.ts +++ b/sample/worker/worker.ts @@ -10,6 +10,7 @@ import { import basicVertWGSL from '../../shaders/basic.vert.wgsl'; import vertexPositionColorWGSL from '../../shaders/vertexPositionColor.frag.wgsl'; +import { initDeviceAndErrorDialog } from '../util'; // The worker process can instantiate a WebGPU device immediately, but it still needs an // OffscreenCanvas to be able to display anything. Here we listen for an 'init' message from the @@ -34,8 +35,7 @@ self.addEventListener('message', (ev) => { // to the init() method for all the other samples. The remainder of this file is largely identical // to the rotatingCube sample. async function init(canvas) { - const adapter = await navigator.gpu.requestAdapter(); - const device = await adapter.requestDevice(); + const device = await initDeviceAndErrorDialog(); const context = canvas.getContext('webgpu'); const presentationFormat = navigator.gpu.getPreferredCanvasFormat();