Skip to content

Commit

Permalink
WebNN: Enable in SharedWorker and ServiceWorker contexts
Browse files Browse the repository at this point in the history
webmachinelearning/webnn#804

Note that Chromium doesn't expose SharedArrayBuffer in
SharedWorker context, so tests are modified to probe
for the existence of the SAB interface, or are skipped
entirely if the test is SAB-specific.

Change-Id: Ib1662236560c26ebe79a7b0f2fe2b074ef0e0eeb
  • Loading branch information
inexorabletash authored and chromium-wpt-export-bot committed Feb 26, 2025
1 parent 78877d6 commit 2e34653
Show file tree
Hide file tree
Showing 20 changed files with 105 additions and 97 deletions.
14 changes: 6 additions & 8 deletions interfaces/webnn.idl
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ dictionary MLContextOptions {
MLPowerPreference powerPreference = "default";
};

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface ML {
[SecureContext, Exposed = (Window, Worker)] interface ML {
Promise<MLContext> createContext(optional MLContextOptions options = {});
Promise<MLContext> createContext(GPUDevice gpuDevice);
};
Expand All @@ -31,8 +30,7 @@ dictionary MLContextLostInfo {
DOMString message;
};

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLContext {
[SecureContext, Exposed = (Window, Worker)] interface MLContext {
undefined dispatch(MLGraph graph, MLNamedTensors inputs, MLNamedTensors outputs);

Promise<MLTensor> createTensor(MLTensorDescriptor descriptor);
Expand Down Expand Up @@ -71,7 +69,7 @@ dictionary MLSingleInputSupportLimits {
MLSupportLimits output;
};

[SecureContext, Exposed=(Window, DedicatedWorker)]
[SecureContext, Exposed=(Window, Worker)]
interface MLGraph {
undefined destroy();
};
Expand All @@ -97,7 +95,7 @@ dictionary MLOperandDescriptor {
required sequence<[EnforceRange] unsigned long> shape;
};

[SecureContext, Exposed=(Window, DedicatedWorker)]
[SecureContext, Exposed=(Window, Worker)]
interface MLOperand {
readonly attribute MLOperandDataType dataType;
readonly attribute FrozenArray<unsigned long> shape;
Expand All @@ -114,7 +112,7 @@ dictionary MLTensorDescriptor : MLOperandDescriptor {
boolean writable = false;
};

[SecureContext, Exposed=(Window, DedicatedWorker)]
[SecureContext, Exposed=(Window, Worker)]
interface MLTensor {
readonly attribute MLOperandDataType dataType;
readonly attribute FrozenArray<unsigned long> shape;
Expand All @@ -126,7 +124,7 @@ interface MLTensor {

typedef record<USVString, MLOperand> MLNamedOperands;

[SecureContext, Exposed=(Window, DedicatedWorker)]
[SecureContext, Exposed=(Window, Worker)]
interface MLGraphBuilder {
// Construct the graph builder from the context.
constructor(MLContext context);
Expand Down
26 changes: 14 additions & 12 deletions webnn/conformance_tests/byob_readtensor.https.any.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=test WebNN API tensor operations
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down Expand Up @@ -94,22 +94,24 @@ promise_test(async () => {
assert_array_equals(new Uint32Array(arrayBuffer), testContents);
}, `readTensor() with an ArrayBuffer`);

promise_test(async () => {
const sharedArrayBuffer = new SharedArrayBuffer(testContents.byteLength);
if ('SharedArrayBuffer' in globalThis) {
promise_test(async () => {
const sharedArrayBuffer = new SharedArrayBuffer(testContents.byteLength);

await mlContext.readTensor(mlTensor, sharedArrayBuffer);
await mlContext.readTensor(mlTensor, sharedArrayBuffer);

assert_array_equals(new Uint32Array(sharedArrayBuffer), testContents);
}, `readTensor() with a SharedArrayBuffer`);
assert_array_equals(new Uint32Array(sharedArrayBuffer), testContents);
}, `readTensor() with a SharedArrayBuffer`);

promise_test(async () => {
const sharedArrayBuffer = new SharedArrayBuffer(testContents.byteLength);
const typedArray = new Uint32Array(sharedArrayBuffer);
promise_test(async () => {
const sharedArrayBuffer = new SharedArrayBuffer(testContents.byteLength);
const typedArray = new Uint32Array(sharedArrayBuffer);

await mlContext.readTensor(mlTensor, typedArray);
await mlContext.readTensor(mlTensor, typedArray);

assert_array_equals(typedArray, testContents);
}, `readTensor() with a typeArray from a SharedArrayBuffer`);
assert_array_equals(typedArray, testContents);
}, `readTensor() with a typeArray from a SharedArrayBuffer`);
}

promise_test(async () => {
// Create a slightly larger ArrayBuffer and set up the TypedArray at an
Expand Down
2 changes: 1 addition & 1 deletion webnn/conformance_tests/identity.https.any.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=test WebNN API element-wise identity operation
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=test that input tensors are not modified during a call to dispatch()
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down
2 changes: 1 addition & 1 deletion webnn/conformance_tests/parallel-dispatch.https.any.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=test parallel WebNN API dispatch calls
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down
2 changes: 1 addition & 1 deletion webnn/conformance_tests/scalars.https.any.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=test that scalar values work as expected
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=test WebNN API constant with shared array buffer
// META: global=window,dedicatedworker
// META: global=window,dedicatedworker,serviceworker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down
2 changes: 1 addition & 1 deletion webnn/conformance_tests/subgraph.https.any.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=test WebNN API subgraph with multiple operations
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down
80 changes: 41 additions & 39 deletions webnn/conformance_tests/tensor.https.any.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=test WebNN API tensor operations
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down Expand Up @@ -175,44 +175,46 @@ const testWriteTensor = (testName) => {
}
});

promise_test(async () => {
const tensorDescriptor = {
dataType: 'int32',
shape: [4],
readable: true,
writable: true,
};
const tensorByteLength = sizeOfDescriptor(tensorDescriptor);

// Required to use SharedArrayBuffer.
assert_true(
self.crossOriginIsolated,
'The page is served with COOP and COEP, it should be cross-origin-isolated.');

let arrayBuffer = new ArrayBuffer(tensorByteLength);
let arrayBufferView = new Int32Array(arrayBuffer);
arrayBufferView.fill(7);

let sharedArrayBuffer = new SharedArrayBuffer(tensorByteLength);
let sharedArrayBufferView = new Int32Array(sharedArrayBuffer);
sharedArrayBufferView.fill(7);

const tensors = await Promise.all([
mlContext.createTensor(tensorDescriptor),
mlContext.createTensor(tensorDescriptor),
mlContext.createTensor(tensorDescriptor),
mlContext.createTensor(tensorDescriptor)
]);

mlContext.writeTensor(tensors[0], arrayBuffer);
mlContext.writeTensor(tensors[2], arrayBufferView);
mlContext.writeTensor(tensors[1], sharedArrayBuffer);
mlContext.writeTensor(tensors[3], sharedArrayBufferView);

await Promise.all(tensors.map(async (tensor) => {
assert_tensor_data_equals(mlContext, tensor, arrayBufferView);
}));
}, `${testName} / write with different kinds of buffers`);
if ('SharedArrayBuffer' in globalThis) {
promise_test(async () => {
const tensorDescriptor = {
dataType: 'int32',
shape: [4],
readable: true,
writable: true,
};
const tensorByteLength = sizeOfDescriptor(tensorDescriptor);

// Required to use SharedArrayBuffer.
assert_true(
self.crossOriginIsolated,
'The page is served with COOP and COEP, it should be cross-origin-isolated.');

let arrayBuffer = new ArrayBuffer(tensorByteLength);
let arrayBufferView = new Int32Array(arrayBuffer);
arrayBufferView.fill(7);

let sharedArrayBuffer = new SharedArrayBuffer(tensorByteLength);
let sharedArrayBufferView = new Int32Array(sharedArrayBuffer);
sharedArrayBufferView.fill(7);

const tensors = await Promise.all([
mlContext.createTensor(tensorDescriptor),
mlContext.createTensor(tensorDescriptor),
mlContext.createTensor(tensorDescriptor),
mlContext.createTensor(tensorDescriptor)
]);

mlContext.writeTensor(tensors[0], arrayBuffer);
mlContext.writeTensor(tensors[2], arrayBufferView);
mlContext.writeTensor(tensors[1], sharedArrayBuffer);
mlContext.writeTensor(tensors[3], sharedArrayBufferView);

await Promise.all(tensors.map(async (tensor) => {
assert_tensor_data_equals(mlContext, tensor, arrayBufferView);
}));
}, `${testName} / write with different kinds of buffers`);
}

promise_test(async () => {
const tensorDescriptor = {
Expand Down
2 changes: 1 addition & 1 deletion webnn/idlharness.https.any.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// META: global=window,dedicatedworker
// META: global=window,worker
// META: script=/resources/WebIDLParser.js
// META: script=/resources/idlharness.js
// META: script=./resources/utils.js
Expand Down
2 changes: 1 addition & 1 deletion webnn/validation_tests/build-more-than-once.https.any.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=ensure MLMLGraphBuilder may build at most one MLGraph
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=ensure MLGraphBuilder.constant() handles buffers which change
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down
50 changes: 28 additions & 22 deletions webnn/validation_tests/constant.https.any.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=validation tests for WebNN API constant interface
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down Expand Up @@ -128,8 +128,6 @@ tests.forEach(
const builder = new MLGraphBuilder(context);
const buffer = new ArrayBuffer(test.buffer.byteLength);
const bufferView = new test.buffer.type(buffer);
const sharedBuffer = new SharedArrayBuffer(test.buffer.byteLength);
const sharedBufferView = new test.buffer.type(sharedBuffer);

if (test.viewTestOnly === undefined || test.viewTestOnly === false) {
// Test building constant from ArrayBuffer.
Expand All @@ -141,15 +139,19 @@ tests.forEach(
assert_throws_js(
TypeError, () => builder.constant(test.descriptor, buffer));
}
// Test building constant from SharedArrayBuffer.
if (test.output) {
const constantOperand =
builder.constant(test.descriptor, sharedBuffer);
assert_equals(constantOperand.dataType, test.output.dataType);
assert_array_equals(constantOperand.shape, test.output.shape);
} else {
assert_throws_js(
TypeError, () => builder.constant(test.descriptor, sharedBuffer));
if ('SharedArrayBuffer' in globalThis) {
// Test building constant from SharedArrayBuffer.
const sharedBuffer = new SharedArrayBuffer(test.buffer.byteLength);
if (test.output) {
const constantOperand =
builder.constant(test.descriptor, sharedBuffer);
assert_equals(constantOperand.dataType, test.output.dataType);
assert_array_equals(constantOperand.shape, test.output.shape);
} else {
assert_throws_js(
TypeError,
() => builder.constant(test.descriptor, sharedBuffer));
}
}
}

Expand All @@ -162,15 +164,19 @@ tests.forEach(
assert_throws_js(
TypeError, () => builder.constant(test.descriptor, bufferView));
}
// Test building constant from shared ArrayBufferView.
if (test.output) {
const constantOperand =
builder.constant(test.descriptor, sharedBufferView);
assert_equals(constantOperand.dataType, test.output.dataType);
assert_array_equals(constantOperand.shape, test.output.shape);
} else {
assert_throws_js(
TypeError,
() => builder.constant(test.descriptor, sharedBufferView));
if ('SharedArrayBuffer' in globalThis) {
// Test building constant from shared ArrayBufferView.
const sharedBuffer = new SharedArrayBuffer(test.buffer.byteLength);
const sharedBufferView = new test.buffer.type(sharedBuffer);
if (test.output) {
const constantOperand =
builder.constant(test.descriptor, sharedBufferView);
assert_equals(constantOperand.dataType, test.output.dataType);
assert_array_equals(constantOperand.shape, test.output.shape);
} else {
assert_throws_js(
TypeError,
() => builder.constant(test.descriptor, sharedBufferView));
}
}
}, test.name));
2 changes: 1 addition & 1 deletion webnn/validation_tests/createContext.https.any.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=validation tests for WebNN API createContext()
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down
2 changes: 1 addition & 1 deletion webnn/validation_tests/destroyContext.https.any.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// META: timeout=long
// META: title=validation tests for WebNN API MLContext::destroy()
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down
2 changes: 1 addition & 1 deletion webnn/validation_tests/destroyGraph.https.any.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// META: timeout=long
// META: title=validation tests for WebNN API MLContext::destroy()
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down
2 changes: 1 addition & 1 deletion webnn/validation_tests/input.https.any.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=validation tests for WebNN API input interface
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down
2 changes: 1 addition & 1 deletion webnn/validation_tests/invalid-rank.https.any.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=ensure an MLOperand cannot be created with an invalid rank
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=validation tests for pooling and reduction operators keep dimensions
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down
2 changes: 1 addition & 1 deletion webnn/validation_tests/unprintableNames.https.any.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// META: title=test graph inputs/outputs with unprintable names
// META: global=window,dedicatedworker
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
Expand Down

0 comments on commit 2e34653

Please sign in to comment.