Skip to content

Commit

Permalink
Deploying to gh-pages from @ 36626bf 🚀
Browse files Browse the repository at this point in the history
  • Loading branch information
greggman committed Jan 14, 2025
1 parent ab50091 commit f35af7e
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 32 deletions.
17 changes: 11 additions & 6 deletions dist/0.x/webgpu-debug-helper.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/0.x/webgpu-debug-helper.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "webgpu-debug-helper",
"version": "0.1.4",
"version": "0.1.5",
"description": "webgpu debug helper",
"main": "dist/0.x/webgpu-debug-helper.js",
"type": "module",
Expand Down
17 changes: 11 additions & 6 deletions show-errors/show-errors.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* [email protected].4, license MIT */
/* [email protected].5, license MIT */
(function (factory) {
typeof define === 'function' && define.amd ? define(factory) :
factory();
Expand All @@ -19,11 +19,13 @@
const currentErrorScope = errorScopeStack.findLast(scope => scope.filter === 'validation');
const promise = origPopErrorScope.call(device)
.then(error => {
// If there was a currentErrorScope when we added pushed then remove our promise
// If there was a currentErrorScope and there was no error the remove our promise.
if (currentErrorScope) {
const ndx = currentErrorScope.errors.indexOf(promise);
if (ndx) {
currentErrorScope.errors.splice(ndx, 1);
if (!error) {
const ndx = currentErrorScope.errors.indexOf(promise);
if (ndx) {
currentErrorScope.errors.splice(ndx, 1);
}
}
}
else {
Expand Down Expand Up @@ -92,7 +94,10 @@
throw new DOMException('popErrorScope called on empty error scope stack', 'OperationError');
}
const errPromise = origFn.call(this);
return errorScope.errors.pop() ?? errPromise;
errorScope.errors.push(errPromise);
const errors = await Promise.all(errorScope.errors);
const error = errors.find(v => !!v);
return error ?? null;
};
})(GPUDevice.prototype.popErrorScope);
GPUAdapter.prototype.requestDevice = (function (origFn) {
Expand Down
17 changes: 11 additions & 6 deletions src/error-scope-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ if (typeof GPUDevice !== 'undefined') {

const promise = origPopErrorScope.call(device)
.then(error => {
// If there was a currentErrorScope when we added pushed then remove our promise
// If there was a currentErrorScope and there was no error the remove our promise.
if (currentErrorScope) {
const ndx = currentErrorScope.errors.indexOf(promise);
if (ndx) {
currentErrorScope.errors.splice(ndx, 1);
}
if (!error) {
const ndx = currentErrorScope.errors.indexOf(promise);
if (ndx) {
currentErrorScope.errors.splice(ndx, 1);
}
}
} else {
// there was no currentErrorScope so emit the error
if (error) {
Expand Down Expand Up @@ -102,7 +104,10 @@ if (typeof GPUDevice !== 'undefined') {
throw new DOMException('popErrorScope called on empty error scope stack', 'OperationError');
}
const errPromise = origFn.call(this);
return errorScope.errors.pop() ?? errPromise;
errorScope.errors.push(errPromise);
const errors = await Promise.all(errorScope.errors);
const error = errors.find(v => !!v);
return error ?? null;
};
})(GPUDevice.prototype.popErrorScope);

Expand Down
104 changes: 94 additions & 10 deletions test/tests/push-pop-error-tests.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,42 @@
import {describe} from '../mocha-support.js';
import { itWithDevice } from '../js/utils.js';
import { assertFalsy, assertInstanceOf, assertTruthy } from '../assert.js';
import { assertEqual, assertFalsy, assertInstanceOf } from '../assert.js';

function causeError(device, filter) {
switch (filter) {
case 'validation':
device.createBuffer({
size: 1024,
usage: 0xffff, // Invalid GPUBufferUsage
});
break;
case 'out-of-memory':
device.createTexture({
// One of the largest formats. With the base limits, the texture will be 256 GiB.
format: 'rgba32float',
usage: GPUTextureUsage.COPY_DST,
size: [
device.limits.maxTextureDimension2D,
device.limits.maxTextureDimension2D,
device.limits.maxTextureArrayLayers,
],
});
break;
default:
throw Error('unreachable');
}
}

describe('test push/pop error scope', () => {

itWithDevice('test we get error (because they are being captured)', async (device) => {
itWithDevice('test we get error (because they are being captured)', async (device) => {
device.pushErrorScope('validation');
device.createSampler({maxAnisotropy: 0});
const err = await device.popErrorScope();
assertInstanceOf(err, GPUValidationError);
});

itWithDevice('test we get errors nested', async (device) => {
itWithDevice('test we get errors nested', async (device) => {
device.pushErrorScope('validation');
device.pushErrorScope('validation');
device.createSampler({maxAnisotropy: 0});
Expand All @@ -28,7 +53,7 @@ describe('test push/pop error scope', () => {
assertFalsy(rootErr);
});

itWithDevice('test we get errors nested - more', async (device) => {
itWithDevice('test we get errors nested - more', async (device) => {
device.pushErrorScope('validation');
device.pushErrorScope('validation');
device.pushErrorScope('validation');
Expand All @@ -47,7 +72,15 @@ describe('test push/pop error scope', () => {
assertInstanceOf(rootErr, GPUValidationError);
});

itWithDevice('test we get uncaught errors', async (device) => {
itWithDevice('test we get errors with success after error', async (device) => {
device.pushErrorScope('validation');
device.createSampler({maxAnisotropy: 0});
device.queue.submit([]);
const err = await device.popErrorScope();
assertInstanceOf(err, GPUValidationError);
});

itWithDevice('test we get uncaught errors', async (device) => {
const promise = new Promise(resolve => {
device.addEventListener('uncapturederror', (e) => {
resolve(e.error);
Expand Down Expand Up @@ -80,18 +113,69 @@ describe('test push/pop error scope', () => {
assertInstanceOf(uncapturedError, GPUValidationError);
});

itWithDevice('test we get rejection for empty stack, not an exception', async (device) => {
let wasRejected = false;
describe('test scope filters work', () => {

itWithDevice('out-of-memory', async (device) => {
let uncapturedError;
device.addEventListener('uncapturederror', e => {
uncapturedError = e.error;
});
device.pushErrorScope('out-of-memory');
device.pushErrorScope('validation');
causeError(device, 'out-of-memory');
const validationError = await device.popErrorScope();
const outOfMemoryError = await device.popErrorScope();
assertFalsy(validationError, 'no validation error');
assertInstanceOf(outOfMemoryError, GPUOutOfMemoryError);
assertFalsy(uncapturedError, 'no uncaptured error');
});

itWithDevice('validation', async (device) => {
let uncapturedError;
device.addEventListener('uncapturederror', e => {
uncapturedError = e.error;
});
device.pushErrorScope('validation');
device.pushErrorScope('out-of-memory');
causeError(device, 'validation');
const outOfMemoryError = await device.popErrorScope();
const validationError = await device.popErrorScope();
assertFalsy(outOfMemoryError, 'no out of memory error');
assertInstanceOf(validationError, GPUValidationError);
assertFalsy(uncapturedError, 'no uncaptured error');
});

itWithDevice('both', async (device) => {
let uncapturedError;
device.addEventListener('uncapturederror', e => {
uncapturedError = e.error;
});
device.pushErrorScope('validation');
device.pushErrorScope('out-of-memory');
causeError(device, 'validation');
causeError(device, 'out-of-memory');
const outOfMemoryError = await device.popErrorScope();
const validationError = await device.popErrorScope();
assertInstanceOf(outOfMemoryError, GPUOutOfMemoryError);
assertInstanceOf(validationError, GPUValidationError);
assertFalsy(uncapturedError, 'no uncaptured error');
});

});

itWithDevice('test we get rejection for empty stack, not an exception', async (device) => {
let rejectedError;
try {
await device.popErrorScope()
.then(() => assertFalsy(true, 'should not succeed'))
.catch(() => {
wasRejected = true;
.catch((error) => {
rejectedError = error;
});
} catch {
assertFalsy(true, 'should not throw');
}
assertTruthy(wasRejected, 'was rejected');
assertInstanceOf(rejectedError, DOMException);
assertEqual(rejectedError.name, 'OperationError');
});

});

0 comments on commit f35af7e

Please sign in to comment.