Skip to content

Commit f15202a

Browse files
egalliIshwar Raut
authored and
Ishwar Raut
committed
[WebNN EP] Enable IO Bindings with MLTensor (microsoft#21301)
### Description Enables using the MLTensor to pass data between models. ### Motivation and Context Using MLTensor instead of ArrayBuffers reduces the number of copies between the CPU and devices as well as the renderer and GPU process in Chromium.
1 parent 8763941 commit f15202a

33 files changed

+1287
-73
lines changed

include/onnxruntime/core/framework/allocator.h

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ constexpr const char* OpenVINO_GPU = "OpenVINO_GPU";
5353
constexpr const char* OpenVINO_RT = "OpenVINO_RT";
5454
constexpr const char* OpenVINO_RT_NPU = "OpenVINO_RT_NPU";
5555
constexpr const char* WEBGPU_BUFFER = "WebGPU_Buffer";
56+
constexpr const char* WEBNN_TENSOR = "WebNN_Tensor";
5657

5758
constexpr size_t kAllocAlignment = 256;
5859

js/common/lib/tensor-factory-impl.ts

+12
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
TensorFromImageBitmapOptions,
1212
TensorFromImageDataOptions,
1313
TensorFromImageElementOptions,
14+
TensorFromMLTensorOptions,
1415
TensorFromTextureOptions,
1516
TensorFromUrlOptions,
1617
} from './tensor-factory.js';
@@ -310,6 +311,17 @@ export const tensorFromGpuBuffer = <T extends TensorInterface.GpuBufferDataTypes
310311
return new Tensor({ location: 'gpu-buffer', type: dataType ?? 'float32', gpuBuffer, dims, download, dispose });
311312
};
312313

314+
/**
315+
* implementation of Tensor.fromMLTensor().
316+
*/
317+
export const tensorFromMLTensor = <T extends TensorInterface.MLTensorDataTypes>(
318+
mlTensor: TensorInterface.MLTensorType,
319+
options: TensorFromMLTensorOptions<T>,
320+
): Tensor => {
321+
const { dataType, dims, download, dispose } = options;
322+
return new Tensor({ location: 'ml-tensor', type: dataType ?? 'float32', mlTensor, dims, download, dispose });
323+
};
324+
313325
/**
314326
* implementation of Tensor.fromPinnedBuffer().
315327
*/

js/common/lib/tensor-factory.ts

+46
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,20 @@ export interface GpuBufferConstructorParameters<T extends Tensor.GpuBufferDataTy
8686
readonly gpuBuffer: Tensor.GpuBufferType;
8787
}
8888

89+
export interface MLTensorConstructorParameters<T extends Tensor.MLTensorDataTypes = Tensor.MLTensorDataTypes>
90+
extends CommonConstructorParameters<T>,
91+
GpuResourceConstructorParameters<T> {
92+
/**
93+
* Specify the location of the data to be 'ml-tensor'.
94+
*/
95+
readonly location: 'ml-tensor';
96+
97+
/**
98+
* Specify the WebNN MLTensor that holds the tensor data.
99+
*/
100+
readonly mlTensor: Tensor.MLTensorType;
101+
}
102+
89103
// #endregion
90104

91105
// the following region contains type definitions of each individual options.
@@ -219,6 +233,15 @@ export interface TensorFromGpuBufferOptions<T extends Tensor.GpuBufferDataTypes>
219233
dataType?: T;
220234
}
221235

236+
export interface TensorFromMLTensorOptions<T extends Tensor.MLTensorDataTypes>
237+
extends Pick<Tensor, 'dims'>,
238+
GpuResourceConstructorParameters<T> {
239+
/**
240+
* Describes the data type of the tensor.
241+
*/
242+
dataType?: T;
243+
}
244+
222245
// #endregion
223246

224247
/**
@@ -336,6 +359,29 @@ export interface TensorFactory {
336359
options: TensorFromGpuBufferOptions<T>,
337360
): TypedTensor<T>;
338361

362+
/**
363+
* create a tensor from a WebNN MLTensor
364+
*
365+
* @param tensor - the MLTensor object to create tensor from
366+
* @param options - An optional object representing options for creating tensor from a WebNN MLTensor.
367+
*
368+
* The options include following properties:
369+
* - `dataType`: the data type of the tensor. If omitted, assume 'float32'.
370+
* - `dims`: the dimension of the tensor. Required.
371+
* - `download`: an optional function to download the tensor data from the MLTensor to CPU. If omitted, the MLTensor
372+
* data will not be able to download. Usually, this is provided by the WebNN backend for the inference outputs.
373+
* Users don't need to provide this function.
374+
* - `dispose`: an optional function to dispose the tensor data on the WebNN MLTensor. If omitted, the MLTensor will
375+
* not be disposed. Usually, this is provided by the WebNN backend for the inference outputs. Users don't need to
376+
* provide this function.
377+
*
378+
* @returns a tensor object
379+
*/
380+
fromMLTensor<T extends Tensor.MLTensorDataTypes>(
381+
tensor: Tensor.MLTensorType,
382+
options: TensorFromMLTensorOptions<T>,
383+
): TypedTensor<T>;
384+
339385
/**
340386
* create a tensor from a pre-allocated buffer. The buffer will be used as a pinned buffer.
341387
*

js/common/lib/tensor-impl.ts

+57-2
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,19 @@ import { TensorToDataUrlOptions, TensorToImageDataOptions } from './tensor-conve
66
import {
77
tensorFromGpuBuffer,
88
tensorFromImage,
9+
tensorFromMLTensor,
910
tensorFromPinnedBuffer,
1011
tensorFromTexture,
1112
} from './tensor-factory-impl.js';
1213
import {
1314
CpuPinnedConstructorParameters,
1415
GpuBufferConstructorParameters,
16+
MLTensorConstructorParameters,
1517
TensorFromGpuBufferOptions,
1618
TensorFromImageBitmapOptions,
1719
TensorFromImageDataOptions,
1820
TensorFromImageElementOptions,
21+
TensorFromMLTensorOptions,
1922
TensorFromTextureOptions,
2023
TensorFromUrlOptions,
2124
TextureConstructorParameters,
@@ -37,6 +40,7 @@ type TensorDataType = TensorInterface.DataType;
3740
type TensorDataLocation = TensorInterface.DataLocation;
3841
type TensorTextureType = TensorInterface.TextureType;
3942
type TensorGpuBufferType = TensorInterface.GpuBufferType;
43+
type TensorMLTensorType = TensorInterface.MLTensorType;
4044

4145
/**
4246
* the implementation of Tensor interface.
@@ -86,6 +90,15 @@ export class Tensor implements TensorInterface {
8690
*/
8791
constructor(params: GpuBufferConstructorParameters);
8892

93+
/**
94+
* Construct a new tensor object from the WebNN MLTensor with the given type and dims.
95+
*
96+
* Tensor's location will be set to 'ml-tensor'.
97+
*
98+
* @param params - Specify the parameters to construct the tensor.
99+
*/
100+
constructor(params: MLTensorConstructorParameters);
101+
89102
/**
90103
* implementation.
91104
*/
@@ -98,7 +111,8 @@ export class Tensor implements TensorInterface {
98111
| readonly boolean[]
99112
| CpuPinnedConstructorParameters
100113
| TextureConstructorParameters
101-
| GpuBufferConstructorParameters,
114+
| GpuBufferConstructorParameters
115+
| MLTensorConstructorParameters,
102116
arg1?: TensorDataType | Uint8ClampedArray | readonly number[] | readonly string[] | readonly boolean[],
103117
arg2?: readonly number[],
104118
) {
@@ -155,6 +169,25 @@ export class Tensor implements TensorInterface {
155169
this.disposer = arg0.dispose;
156170
break;
157171
}
172+
case 'ml-tensor': {
173+
if (
174+
type !== 'float32' &&
175+
type !== 'float16' &&
176+
type !== 'int32' &&
177+
type !== 'int64' &&
178+
type !== 'uint32' &&
179+
type !== 'uint64' &&
180+
type !== 'int8' &&
181+
type !== 'uint8' &&
182+
type !== 'bool'
183+
) {
184+
throw new TypeError(`unsupported type "${type}" to create tensor from MLTensor`);
185+
}
186+
this.mlTensorData = arg0.mlTensor;
187+
this.downloader = arg0.download;
188+
this.disposer = arg0.dispose;
189+
break;
190+
}
158191
default:
159192
throw new Error(`Tensor constructor: unsupported location '${this.dataLocation}'`);
160193
}
@@ -325,6 +358,13 @@ export class Tensor implements TensorInterface {
325358
return tensorFromGpuBuffer(gpuBuffer, options);
326359
}
327360

361+
static fromMLTensor<T extends TensorInterface.MLTensorDataTypes>(
362+
mlTensor: TensorMLTensorType,
363+
options: TensorFromMLTensorOptions<T>,
364+
): TensorInterface {
365+
return tensorFromMLTensor(mlTensor, options);
366+
}
367+
328368
static fromPinnedBuffer<T extends TensorInterface.CpuPinnedDataTypes>(
329369
type: T,
330370
buffer: TensorInterface.DataTypeMap[T],
@@ -373,6 +413,11 @@ export class Tensor implements TensorInterface {
373413
*/
374414
private gpuBufferData?: TensorGpuBufferType;
375415

416+
/**
417+
* stores the underlying WebNN MLTensor when location is 'ml-tensor'. otherwise empty.
418+
*/
419+
private mlTensorData?: TensorMLTensorType;
420+
376421
/**
377422
* stores an optional downloader function to download data from GPU to CPU.
378423
*/
@@ -420,6 +465,14 @@ export class Tensor implements TensorInterface {
420465
}
421466
return this.gpuBufferData;
422467
}
468+
469+
get mlTensor(): TensorMLTensorType {
470+
this.ensureValid();
471+
if (!this.mlTensorData) {
472+
throw new Error('The data is not stored as a WebNN MLTensor.');
473+
}
474+
return this.mlTensorData;
475+
}
423476
// #endregion
424477

425478
// #region methods
@@ -431,7 +484,8 @@ export class Tensor implements TensorInterface {
431484
case 'cpu-pinned':
432485
return this.data;
433486
case 'texture':
434-
case 'gpu-buffer': {
487+
case 'gpu-buffer':
488+
case 'ml-tensor': {
435489
if (!this.downloader) {
436490
throw new Error('The current tensor is not created with a specified data downloader.');
437491
}
@@ -472,6 +526,7 @@ export class Tensor implements TensorInterface {
472526
this.cpuData = undefined;
473527
this.gpuTextureData = undefined;
474528
this.gpuBufferData = undefined;
529+
this.mlTensorData = undefined;
475530
this.downloader = undefined;
476531
this.isDownloading = undefined;
477532

js/common/lib/tensor-utils-impl.ts

+8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import {
55
CpuPinnedConstructorParameters,
66
GpuBufferConstructorParameters,
7+
MLTensorConstructorParameters,
78
TextureConstructorParameters,
89
} from './tensor-factory.js';
910
import { Tensor } from './tensor-impl.js';
@@ -56,6 +57,13 @@ export const tensorReshape = (tensor: Tensor, dims: readonly number[]): Tensor =
5657
type: tensor.type as GpuBufferConstructorParameters['type'],
5758
dims,
5859
});
60+
case 'ml-tensor':
61+
return new Tensor({
62+
location: 'ml-tensor',
63+
mlTensor: tensor.mlTensor,
64+
type: tensor.type as MLTensorConstructorParameters['type'],
65+
dims,
66+
});
5967
default:
6068
throw new Error(`tensorReshape: tensor location ${tensor.location} is not supported`);
6169
}

js/common/lib/tensor.ts

+29-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ interface TypedTensorBase<T extends Tensor.Type> {
4242
*/
4343
readonly gpuBuffer: Tensor.GpuBufferType;
4444

45+
/**
46+
* Get the WebNN MLTensor that holds the tensor data.
47+
*
48+
* If the data is not in a WebNN MLTensor, throw error.
49+
*/
50+
readonly mlTensor: Tensor.MLTensorType;
51+
4552
/**
4653
* Get the buffer data of the tensor.
4754
*
@@ -136,15 +143,36 @@ export declare namespace Tensor {
136143
*/
137144
export type GpuBufferType = { size: number; mapState: 'unmapped' | 'pending' | 'mapped' };
138145

146+
/**
147+
* type alias for WebNN MLTensor
148+
*
149+
* The specification for WebNN's MLTensor is currently in flux.
150+
*/
151+
export type MLTensorType = unknown;
152+
139153
/**
140154
* supported data types for constructing a tensor from a WebGPU buffer
141155
*/
142156
export type GpuBufferDataTypes = 'float32' | 'float16' | 'int32' | 'int64' | 'uint32' | 'uint8' | 'bool';
143157

158+
/**
159+
* supported data types for constructing a tensor from a WebNN MLTensor
160+
*/
161+
export type MLTensorDataTypes =
162+
| 'float32'
163+
| 'float16'
164+
| 'int8'
165+
| 'uint8'
166+
| 'int32'
167+
| 'uint32'
168+
| 'int64'
169+
| 'uint64'
170+
| 'bool';
171+
144172
/**
145173
* represent where the tensor data is stored
146174
*/
147-
export type DataLocation = 'none' | 'cpu' | 'cpu-pinned' | 'texture' | 'gpu-buffer';
175+
export type DataLocation = 'none' | 'cpu' | 'cpu-pinned' | 'texture' | 'gpu-buffer' | 'ml-tensor';
148176

149177
/**
150178
* represent the data type of a tensor

0 commit comments

Comments
 (0)