Skip to content

Commit

Permalink
Bug 1914235 [wpt PR 47718] - WebNN: add buffer usages for DML backend…
Browse files Browse the repository at this point in the history
…, a=testonly

Automatic update from web-platform-tests
WebNN: add buffer usages for DML backend

Exposes MLBufferUsageFlags to MLBufferDescriptor and adds new usages to
maximize device memory bandwidth. After this change, createBuffer()
assumes "no usage" by default. To readBuffer() or writeBuffer(), the
corresponding usage flag must be specified by the web developer.
Combining usages is allowed but could be inefficient. Usages are
always validated even if a backend doesn't use it.

webmachinelearning/webnn#542

Bug: 343638938
Change-Id: I4d78e3f8bacd7cbabce3038c234c062c7c07b095
Cq-Include-Trybots: luci.chromium.try​:win11-blink-rel
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5787041
Commit-Queue: Bryan Bernhart <bryan.bernhart@intel.com>
Reviewed-by: Alex Gough <ajgo@chromium.org>
Reviewed-by: ningxin hu <ningxin.hu@intel.com>
Reviewed-by: Austin Sullivan <asully@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1344910}

--

wpt-commits: f21d93823c4ca8b1cb01b3ff1730af9c049840e5
wpt-pr: 47718
  • Loading branch information
bbernhar authored and moz-wptsync-bot committed Aug 23, 2024
1 parent cf5542c commit f4c5ea8
Showing 4 changed files with 155 additions and 46 deletions.
105 changes: 80 additions & 25 deletions testing/web-platform/tests/webnn/conformance_tests/buffer.https.any.js
Original file line number Diff line number Diff line change
@@ -33,7 +33,11 @@ const sizeOfDescriptor = (descriptor) => {
};

const getDescriptorFromBuffer = (buffer) => {
return {dataType: buffer.dataType, dimensions: buffer.shape};
return {
dataType: buffer.dataType,
dimensions: buffer.shape,
usage: buffer.usage
};
};


@@ -160,7 +164,11 @@ const testWriteWebNNBuffer = (testName) => {
});

promise_test(async () => {
const bufferDescriptor = {dataType: 'int32', dimensions: [1]};
const bufferDescriptor = {
dataType: 'int32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO,
};
let mlBuffer = await mlContext.createBuffer(bufferDescriptor);

const bufferByteLength = sizeOfDescriptor(bufferDescriptor);
@@ -205,7 +213,11 @@ const testWriteWebNNBuffer = (testName) => {
}, `${testName} / error`);

promise_test(async () => {
const bufferDescriptor = {dataType: 'int32', dimensions: [2, 2]};
const bufferDescriptor = {
dataType: 'int32',
dimensions: [2, 2],
usage: MLBufferUsage.WRITE_TO,
};
let mlBuffer = await mlContext.createBuffer(bufferDescriptor);

// Writing data to a destroyed MLBuffer should throw.
@@ -218,7 +230,11 @@ const testWriteWebNNBuffer = (testName) => {
}, `${testName} / destroy`);

promise_test(async () => {
const bufferDescriptor = {dataType: 'int32', dimensions: [2, 3]};
const bufferDescriptor = {
dataType: 'int32',
dimensions: [2, 3],
usage: MLBufferUsage.WRITE_TO,
};
let mlBuffer = await mlContext.createBuffer(bufferDescriptor);

let anotherMLContext = await navigator.ml.createContext(contextOptions);
@@ -233,8 +249,11 @@ const testWriteWebNNBuffer = (testName) => {
}, `${testName} / context_mismatch`);

promise_test(async () => {
let mlBuffer =
await mlContext.createBuffer({dataType: 'int32', dimensions: [1]});
let mlBuffer = await mlContext.createBuffer({
dataType: 'int32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
});

// Initialize the buffer.
const inputData = Uint8Array.from([0xAA, 0xAA, 0xAA, 0xAA]);
@@ -253,7 +272,11 @@ const testWriteWebNNBuffer = (testName) => {
}, `${testName} / zero_write`);

promise_test(async () => {
const bufferDescriptor = {dataType: 'int32', dimensions: [2, 2]};
const bufferDescriptor = {
dataType: 'int32',
dimensions: [2, 2],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
};
let mlBuffer = await mlContext.createBuffer(bufferDescriptor);

const bufferByteLength = sizeOfDescriptor(bufferDescriptor);
@@ -300,8 +323,11 @@ const testReadWebNNBuffer = (testName) => {
});

promise_test(async t => {
let mlBuffer =
await mlContext.createBuffer({dataType: 'int32', dimensions: [2, 2]});
let mlBuffer = await mlContext.createBuffer({
dataType: 'int32',
dimensions: [2, 2],
usage: MLBufferUsage.READ_FROM,
});

// Reading a destroyed MLBuffer should reject.
mlBuffer.destroy();
@@ -311,8 +337,11 @@ const testReadWebNNBuffer = (testName) => {
}, `${testName} / read_after_destroy`);

promise_test(async t => {
let mlBuffer =
await mlContext.createBuffer({dataType: 'int32', dimensions: [2, 3]});
let mlBuffer = await mlContext.createBuffer({
dataType: 'int32',
dimensions: [2, 3],
usage: MLBufferUsage.READ_FROM,
});

let promise = mlContext.readBuffer(mlBuffer);
let anotherPromise = mlContext.readBuffer(mlBuffer);
@@ -324,16 +353,22 @@ const testReadWebNNBuffer = (testName) => {
}, `${testName} / read_before_destroy`);

promise_test(async () => {
let mlBuffer =
await mlContext.createBuffer({dataType: 'int32', dimensions: [1024]});
let mlBuffer = await mlContext.createBuffer({
dataType: 'int32',
dimensions: [1024],
usage: MLBufferUsage.READ_FROM,
});

await assert_buffer_data_equals(
mlContext, mlBuffer, new Uint32Array(1024));
}, `${testName} / uninitialized`);

promise_test(async () => {
let mlBuffer =
await mlContext.createBuffer({dataType: 'int32', dimensions: [1]});
let mlBuffer = await mlContext.createBuffer({
dataType: 'int32',
dimensions: [1],
usage: MLBufferUsage.READ_FROM | MLBufferUsage.WRITE_TO,
});

// Initialize the buffer.
mlContext.writeBuffer(mlBuffer, Uint8Array.from([0xAA, 0xAA, 0xAA, 0xAA]));
@@ -345,8 +380,11 @@ const testReadWebNNBuffer = (testName) => {
}, `${testName} / full_size`);

promise_test(async () => {
let mlBuffer =
await mlContext.createBuffer({dataType: 'int32', dimensions: [1]});
let mlBuffer = await mlContext.createBuffer({
dataType: 'int32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
});

// Initialize the buffer.
mlContext.writeBuffer(mlBuffer, Uint8Array.from([0xAA, 0xAA, 0xAA, 0xAA]));
@@ -360,8 +398,11 @@ const testReadWebNNBuffer = (testName) => {
}, `${testName} / src_offset_only`);

promise_test(async () => {
let mlBuffer =
await mlContext.createBuffer({dataType: 'int32', dimensions: [1]});
let mlBuffer = await mlContext.createBuffer({
dataType: 'int32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
});

// Initialize the buffer.
mlContext.writeBuffer(mlBuffer, Uint8Array.from([0xAA, 0xAA, 0xAA, 0xAA]));
@@ -375,8 +416,11 @@ const testReadWebNNBuffer = (testName) => {
}, `${testName} / src_offset_and_size`);

promise_test(async () => {
let mlBuffer =
await mlContext.createBuffer({dataType: 'int32', dimensions: [1]});
let mlBuffer = await mlContext.createBuffer({
dataType: 'int32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
});

// Initialize the buffer.
mlContext.writeBuffer(mlBuffer, Uint8Array.from([0xAA, 0xAA, 0xAA, 0xAA]));
@@ -390,8 +434,11 @@ const testReadWebNNBuffer = (testName) => {
}, `${testName} / larger_src_data`);

promise_test(async () => {
let mlBuffer =
await mlContext.createBuffer({dataType: 'int32', dimensions: [1]});
let mlBuffer = await mlContext.createBuffer({
dataType: 'int32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
});

const inputData = [0xAA, 0xAA, 0xAA, 0xAA];

@@ -404,7 +451,11 @@ const testReadWebNNBuffer = (testName) => {
}, `${testName} / no_src_offset`);

promise_test(async t => {
const bufferDescriptor = {dataType: 'int32', dimensions: [2, 3]};
const bufferDescriptor = {
dataType: 'int32',
dimensions: [2, 3],
usage: MLBufferUsage.READ_FROM,
};
let mlBuffer = await mlContext.createBuffer(bufferDescriptor);

let anotherMLContext = await navigator.ml.createContext(contextOptions);
@@ -436,7 +487,11 @@ const testDispatchWebNNBuffer = (testName) => {
}
// Construct a simple graph: A = B + C, with two outputs.
const builder = new MLGraphBuilder(mlContext);
const bufferDescriptor = {dataType: 'float32', dimensions: shape};
const bufferDescriptor = {
dataType: 'float32',
dimensions: shape,
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
};
const lhsOperand = builder.input('lhs', bufferDescriptor);
const rhsOperand = builder.input('rhs', bufferDescriptor);
const output1Operand = builder.add(lhsOperand, rhsOperand);
Original file line number Diff line number Diff line change
@@ -29,8 +29,11 @@ promise_setup(async () => {
}

try {
mlBuffer =
await mlContext.createBuffer({dataType: 'int32', dimensions: [2, 4]});
mlBuffer = await mlContext.createBuffer({
dataType: 'int32',
dimensions: [2, 4],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
});
} catch (e) {
throw new AssertionError(
`Unable to create buffer for ${variant} variant. ${e}`);
@@ -135,8 +138,11 @@ promise_test(async () => {
}, `readBuffer() with a larger TypedArray`);

promise_test(async (t) => {
const buffer =
await mlContext.createBuffer({dataType: 'int32', dimensions: [2, 2]});
const buffer = await mlContext.createBuffer({
dataType: 'int32',
dimensions: [2, 2],
usage: MLBufferUsage.READ_FROM,
});
const arrayBufferView = new Int32Array(2 * 2);
const arrayBuffer = arrayBufferView.buffer;

@@ -150,8 +156,11 @@ promise_test(async (t) => {
}, `readBuffer() rejects on a destroyed MLBuffer`);

promise_test(async (t) => {
const buffer =
await mlContext.createBuffer({dataType: 'int32', dimensions: [2, 2]});
const buffer = await mlContext.createBuffer({
dataType: 'int32',
dimensions: [2, 2],
usage: MLBufferUsage.READ_FROM,
});
const arrayBufferView = new Int32Array(2 * 2);
const arrayBuffer = arrayBufferView.buffer;

Original file line number Diff line number Diff line change
@@ -30,7 +30,11 @@ function buildMulGraph(context, operandDescriptor, multiplier) {
}

promise_test(async () => {
const operandDescriptor = {dataType: 'float32', dimensions: [1]};
const operandDescriptor = {
dataType: 'float32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
};

const [mlGraph, inputBuffer1, inputBuffer2, outputBuffer] =
await Promise.all([
@@ -66,7 +70,11 @@ promise_test(async () => {
}, 'dispatch queues behind readBuffer');

promise_test(async () => {
const operandDescriptor = {dataType: 'float32', dimensions: [1]};
const operandDescriptor = {
dataType: 'float32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
};
const mlGraph = await buildMulGraph(mlContext, operandDescriptor, 3);

// write/dispatch/read, write/dispatch/read, ...
@@ -90,7 +98,11 @@ promise_test(async () => {
}, 'same graph: write/dispatch/read, write/dispatch/read, ...');

promise_test(async () => {
const operandDescriptor = {dataType: 'float32', dimensions: [1]};
const operandDescriptor = {
dataType: 'float32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
};
const mlGraph = await buildMulGraph(mlContext, operandDescriptor, 10);

// write/write...
@@ -125,7 +137,11 @@ promise_test(async () => {
}, 'same graph: write/write..., dispatch/read, dispatch/read, ...');

promise_test(async () => {
const operandDescriptor = {dataType: 'float32', dimensions: [1]};
const operandDescriptor = {
dataType: 'float32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
};
const mlGraph = await buildMulGraph(mlContext, operandDescriptor, 9);

// write/write...
@@ -159,7 +175,11 @@ promise_test(async () => {
}, 'same graph: write/write..., dispatch/dispatch..., read/read...');

promise_test(async () => {
const operandDescriptor = {dataType: 'float32', dimensions: [1]};
const operandDescriptor = {
dataType: 'float32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
};
const mlGraph = await buildMulGraph(mlContext, operandDescriptor, 2);

const buffers = await Promise.all([
@@ -188,7 +208,11 @@ promise_test(async () => {
}, 'same graph serial inputs: dispatch/dispatch..., read/read...');

promise_test(async () => {
const operandDescriptor = {dataType: 'float32', dimensions: [1]};
const operandDescriptor = {
dataType: 'float32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
};

// write/write...
const testInputs = [1, 2, 3, 4];
@@ -223,7 +247,11 @@ promise_test(async () => {
}, 'different graphs: write/write..., dispatch/read, dispatch/read, ...');

promise_test(async () => {
const operandDescriptor = {dataType: 'float32', dimensions: [1]};
const operandDescriptor = {
dataType: 'float32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
};

// write/write...
const testInputs = [1, 2, 3, 4];
@@ -257,7 +285,11 @@ promise_test(async () => {
}, 'different graphs: write/write..., dispatch/dispatch..., read/read...');

promise_test(async () => {
const operandDescriptor = {dataType: 'float32', dimensions: [1]};
const operandDescriptor = {
dataType: 'float32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
};

const graphs = await Promise.all([3, 2].map(async (multiplier) => {
return buildMulGraph(mlContext, operandDescriptor, multiplier);
@@ -289,7 +321,11 @@ promise_test(async () => {
}, 'different graphs serial inputs: dispatch/dispatch..., read/read...');

promise_test(async () => {
const operandDescriptor = {dataType: 'float32', dimensions: [1]};
const operandDescriptor = {
dataType: 'float32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
};

const graphs = await Promise.all([2, 3].map(async (multiplier) => {
return buildMulGraph(mlContext, operandDescriptor, multiplier);
Original file line number Diff line number Diff line change
@@ -132,16 +132,22 @@ promise_test(async t => {

promise_test(async t => {
const context = await navigator.ml.createContext(contextOptions);
const buffer =
await context.createBuffer({dataType: 'float32', dimensions: [1]});
const buffer = await context.createBuffer({
dataType: 'float32',
dimensions: [1],
usage: MLBufferUsage.READ_FROM,
});
context.destroy();
promise_rejects_dom(t, 'InvalidStateError', context.readBuffer(buffer));
}, 'Destroyed context can not read buffer.');

promise_test(async t => {
const context = await navigator.ml.createContext(contextOptions);
const buffer =
await context.createBuffer({dataType: 'float32', dimensions: [1]});
const buffer = await context.createBuffer({
dataType: 'float32',
dimensions: [1],
usage: MLBufferUsage.READ_FROM,
});
let promise = context.readBuffer(buffer);
context.destroy();
promise_rejects_dom(t, 'InvalidStateError', promise);
@@ -152,8 +158,11 @@ promise_test(async t => {
// Destroying another context doesn't impact the first context.
const another_context = await navigator.ml.createContext(contextOptions);
another_context.destroy();
const buffer =
await context.createBuffer({dataType: 'float32', dimensions: [1]});
const buffer = await context.createBuffer({
dataType: 'float32',
dimensions: [1],
usage: MLBufferUsage.WRITE_TO,
});
let arrayBuffer = new ArrayBuffer(4);
context.destroy();
assert_throws_dom('InvalidStateError', () => {

0 comments on commit f4c5ea8

Please sign in to comment.