From c44a6858e036b1a3b62787479106d323aa138a9f Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 30 Apr 2023 16:57:14 +0100 Subject: [PATCH] Add error handler --- src/index.html | 4 +-- src/index.scss | 2 +- src/index.ts | 53 ++++++++++++++++------------ src/utils/error-handler.ts | 45 +++++++++++++++++++++++ src/utils/graphics/initialize-gpu.ts | 16 +++++++-- src/utils/graphics/smart-compile.ts | 23 ++++++------ 6 files changed, 104 insertions(+), 39 deletions(-) create mode 100644 src/utils/error-handler.ts diff --git a/src/index.html b/src/index.html index ec305ae..23a5ad6 100644 --- a/src/index.html +++ b/src/index.html @@ -34,9 +34,7 @@
-
-            
-          
+
diff --git a/src/index.scss b/src/index.scss index b483f1d..415a09b 100644 --- a/src/index.scss +++ b/src/index.scss @@ -40,7 +40,7 @@ html { position: absolute; top: 0; left: 0; - display: none; + margin: var(--normal-margin); pre { font-size: 20px; diff --git a/src/index.ts b/src/index.ts index dd191f4..f9e80f4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,7 @@ import '../assets/icons/info.svg'; import GameLoop from './game-loop/game-loop'; import './index.scss'; import { applyArrayPlugins } from './utils/array'; +import { ErrorHandler, Severity } from './utils/error-handler'; import { FullScreenHandler } from './utils/full-screen-handler'; import { initializeGPU } from './utils/graphics/initialize-gpu'; @@ -34,43 +35,49 @@ const getElements = () => ({ restartButton: document.querySelector('button.restart') as HTMLButtonElement, canvas: document.querySelector('canvas') as HTMLCanvasElement, canvasContainer: document.querySelector('main.canvas-container') as HTMLCanvasElement, - errorContainer: document.querySelector('.errors') as HTMLDivElement, + errorContainer: document.querySelector('.errors-container') as HTMLDivElement, }); const main = async () => { - applyArrayPlugins(); const elements = getElements(); - const defaultTimeToLive = 3500; - const interval = 50; - let timeToLive = defaultTimeToLive; - setInterval(() => { - timeToLive = Math.max(0, timeToLive - interval); - elements.aside.style.opacity = - timeToLive == 0 && FullScreenHandler.isInFullScreenMode() ? '0' : '1'; - }, interval); + ErrorHandler.addOnErrorListener((error, metadata) => { + elements.errorContainer.innerHTML += ` +
${error.message}
+      

${JSON.stringify(metadata, null, 2)}

+ `; + }); - elements.aside.addEventListener('mouseover', () => (timeToLive = defaultTimeToLive)); + try { + applyArrayPlugins(); - new FullScreenHandler( - elements.minimizeFullScreenButton, - elements.maximizeFullScreenButton, - document.body - ); + const defaultTimeToLive = 3500; + const interval = 50; + let timeToLive = defaultTimeToLive; + setInterval(() => { + timeToLive = Math.max(0, timeToLive - interval); + elements.aside.style.opacity = + timeToLive == 0 && FullScreenHandler.isInFullScreenMode() ? '0' : '1'; + }, interval); + elements.aside.addEventListener('mouseover', () => (timeToLive = defaultTimeToLive)); - const gpu = await initializeGPU(); + new FullScreenHandler( + elements.minimizeFullScreenButton, + elements.maximizeFullScreenButton, + document.body + ); - let game: GameLoop | null = null; + const gpu = await initializeGPU(); + let game: GameLoop | null = null; - elements.restartButton.addEventListener('click', () => game?.destroy()); + elements.restartButton.addEventListener('click', () => game?.destroy()); - while (true) { - try { + while (true) { game = new GameLoop(elements.canvas, gpu); await game.start(); - } catch (e) { - elements.errorContainer.innerHTML = e.message; } + } catch (e) { + ErrorHandler.addError(Severity.ERROR, e.message); } }; diff --git a/src/utils/error-handler.ts b/src/utils/error-handler.ts new file mode 100644 index 0000000..e13a006 --- /dev/null +++ b/src/utils/error-handler.ts @@ -0,0 +1,45 @@ +export enum Severity { + INFO = 'info', + WARNING = 'warning', + ERROR = 'error', +} + +export interface ErrorHandlerError { + severity: Severity; + message: string; +} + +export type ErrorMetadata = { [key: string]: any }; + +export class ErrorHandler { + private static readonly errors: Array = []; + private static metadata: ErrorMetadata = {}; + private static onErrorListeners: Array< + (error: ErrorHandlerError, metadata: ErrorMetadata) => void + > = []; + + public static addException(exception: Error) { + ErrorHandler.addError(Severity.ERROR, exception.message); + } + + public static addError(severity: Severity, message: string) { + ErrorHandler.errors.push({ severity, message }); + ErrorHandler.onErrorListeners.forEach((listener) => + listener({ severity, message }, ErrorHandler.metadata) + ); + } + + public static addMetadata(key: string, value: any) { + const serialized = {}; + for (const k in value) { + serialized[k] = value[k]; + } + ErrorHandler.metadata[key] = serialized; + } + + public static addOnErrorListener( + listener: (error: ErrorHandlerError, metadata: ErrorMetadata) => void + ) { + ErrorHandler.onErrorListeners.push(listener); + } +} diff --git a/src/utils/graphics/initialize-gpu.ts b/src/utils/graphics/initialize-gpu.ts index 54a63ef..ba16e1d 100644 --- a/src/utils/graphics/initialize-gpu.ts +++ b/src/utils/graphics/initialize-gpu.ts @@ -1,7 +1,9 @@ +import { ErrorHandler, Severity } from '../error-handler'; + export const initializeGPU = async (): Promise => { const gpu = navigator.gpu; if (!gpu) { - throw new Error('WebGPU is not supported'); + throw new Error('WebGPU is not supported in your browser'); } const adapter = await gpu.requestAdapter({ @@ -12,5 +14,15 @@ export const initializeGPU = async (): Promise => { throw new Error('Could not request adatper'); } - return await adapter.requestDevice(); // could request more resources + ErrorHandler.addMetadata('adapter', await adapter.requestAdapterInfo()); + ErrorHandler.addMetadata('features', adapter.features); + ErrorHandler.addMetadata('limits', adapter.limits); + + const gpuDevice = await adapter.requestDevice(); // could request more resources + + gpuDevice.addEventListener('uncapturederror', (event: GPUUncapturedErrorEvent) => + ErrorHandler.addError(Severity.ERROR, event.error.message) + ); + + return gpuDevice; }; diff --git a/src/utils/graphics/smart-compile.ts b/src/utils/graphics/smart-compile.ts index 877afa0..275969b 100644 --- a/src/utils/graphics/smart-compile.ts +++ b/src/utils/graphics/smart-compile.ts @@ -1,3 +1,5 @@ +import { ErrorHandler, Severity } from '../error-handler'; + export const smartCompile = (device: GPUDevice, ...code: Array) => { const concatenated = code.join('\n\n'); @@ -5,17 +7,18 @@ export const smartCompile = (device: GPUDevice, ...code: Array) => { code: concatenated, }); - module - .getCompilationInfo() - .then((info) => - info.messages.forEach((message) => - console.warn( - message.type, - message.message, - concatenated.split('\n')[message.lineNum - 1] - ) + module.getCompilationInfo().then((info) => + info.messages.forEach((message) => + ErrorHandler.addError( + { + info: Severity.INFO, + warning: Severity.WARNING, + error: Severity.ERROR, + }[message.type], + `${message.message}\n${concatenated.split('\n')[message.lineNum - 1]}` ) - ); + ) + ); return module; };