diff --git a/index.bs b/index.bs index 5ad2643c..f6fafdc0 100644 --- a/index.bs +++ b/index.bs @@ -67,19 +67,19 @@ urlPrefix: https://tc39.es/proposal-float16array/; spec: float16array
 {
-	"WEBGPU": {
-		"authors": [
-			"Dzmitry Malyshau",
-			"Kai Ninomiya"
-		],
-		"href": "https://gpuweb.github.io/gpuweb/",
-		"title": "WebGPU",
-		"status": "ED",
-		"publisher": "W3C",
-		"deliveredBy": [
-			"https://www.w3.org/2020/gpu/"
-		]
-	}
+    "WEBGPU": {
+        "authors": [
+            "Dzmitry Malyshau",
+            "Kai Ninomiya"
+        ],
+        "href": "https://gpuweb.github.io/gpuweb/",
+        "title": "WebGPU",
+        "status": "ED",
+        "publisher": "W3C",
+        "deliveredBy": [
+            "https://www.w3.org/2020/gpu/"
+        ]
+    }
 }
 
@@ -104,19 +104,19 @@ p, ul, ol, dl { margin: 1em 0; } -/* Box for Valid Usage requirements. */ -div.validusage { - padding: .5em; - border: thin solid #88e !important; - border-radius: .5em; -} - /* * Stylistic labels, for clarity of presentation of these blocks. * * NOTE: This text is non-accessible and non-selectable; surrounding * text must also explain the context. */ + +/* Box for Valid Usage requirements. */ +div.validusage { + padding: .5em; + border: thin solid #88e !important; + border-radius: .5em; +} .validusage { position: relative; } @@ -134,19 +134,51 @@ div.validusage { content: "Valid Usage"; } -/* Box for Informal steps. */ +details { + padding: .5em; + border: thin solid #88e !important; + border-radius: .5em; +} + +summary { + font-weight: bold; + margin: -0.5em -0.5em 0; + padding: 0.5em; +} + +/* Box for algorithm steps. */ + +div.algorithm-steps { + padding: .5em; + background-color: ghostwhite; +} + +.algorithm-steps { + position: relative; + overflow: hidden; +} +.algorithm-steps::after { + font-weight: bold; + font-style: italic; + font-size: 130%; + color: rgba(0, 0, 0, 0.15); + color: var(--watermark-text); + position: absolute; + right: .3em; + bottom: .1em; +} +.algorithm-steps::after { + content: "Algorithm"; +} + +/* Informal steps */ div.informalsteps { padding: .5em; border: thin solid #88e !important; border-radius: .5em; + background-color: ghostwhite; } -/* - * Stylistic labels, for clarity of presentation of these blocks. - * - * NOTE: This text is non-accessible and non-selectable; surrounding - * text must also explain the context. - */ .informalsteps { position: relative; } @@ -164,6 +196,28 @@ div.informalsteps { content: "Non-normative"; } +/* Internal slots */ +div.internal-slots { + padding: .5em; + border: thin solid #88e !important; + border-radius: .5em; + background-color: aliceblue; +} + +.internal-slots { + position: relative; +} +.internal-slots::after { + font-weight: bold; + font-style: italic; + font-size: 130%; + color: rgba(0, 0, 0, 0.15); + color: var(--watermark-text); + position: absolute; + right: .3em; + bottom: .1em; +} + /* * Ensure that argumentdef blocks don't overflow algorithm section borders. This is made far harder * than it needs to be because the top-level W3C stylesheet has several @media + min-width variants @@ -262,8 +316,116 @@ th, td { } } + +/* Floating button for collapse/expand all details elements */ + +.collapse-expand-button { + position: fixed; + bottom: 40px; + right: 40px; + width: 40px; + height: 40px; + border: none; + border-radius: 50%; + background-color: green; + color: ghostwhite; + font-size: 32px; + text-align: center; + align-items:center; + justify-content:center; + cursor: pointer; +} + +.collapse-expand-button:hover { + background-color: green; +} + +.collapse-expand-button.expand { + background-color: red; +} + +.collapse-expand-button.expand::before { + content: "+"; +} + +.collapse-expand-button.collapse { + background-color: green; +} + +.collapse-expand-button.collapse::before { + content: "-"; +} + +.collapse-expand-button .tooltiptext { + visibility: hidden; + bottom: 20px; + right: 20px; + width: 120px; + background-color: ghostwhite; + color: black; + font-size: 18px; + text-align: center; + align-items:center; + justify-content:center; + padding: 5px 0; + border-radius: 5px; + + /* position */ + position: absolute; + z-index: 1; + bottom: 100%; + left: 50%; + margin-left: -60px; + /* Use half of the width (120/2 = 60), to center the tooltip */ +} + +.collapse-expand-button:hover .tooltiptext { + visibility: visible; + opacity: 0.75; +} + +/* end of floating collapse/expand button */ + + + + + Introduction {#intro} ===================== @@ -647,31 +809,43 @@ string "webnn". Its default allowlist is 'self'. ### The {{ML/createContext()}} method ### {#api-ml-createcontext} -The {{ML/createContext()}} method steps are: -1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=allowed to use=] the [=webnn-feature|webnn=] feature, return [=a new promise=] [=rejected=] with a "{{SecurityError}}" {{DOMException}} and abort these steps. -1. Let |promise| be [=a new promise=]. -1. Return |promise| and run the following steps [=in parallel=]. -1. Let |options| be the first argument. -1. Run the create context steps given |options|: - 1. Let |context| be a new {{MLContext}} object. - 1. If |options| is a {{GPUDevice}} object, - 1. Set |context|.{{[[contextType]]}} to "[=webgpu-context|webgpu=]". - 1. Set |context|.{{[[deviceType]]}} to "[=device-type-gpu|gpu=]". - 1. Set |context|.{{[[powerPreference]]}} to "[=power-preference-default|default=]". - 1. Otherwise, - 1. Set |context|.{{[[contextType]]}} to "[=default-context|default=]". - 1. If |options|["{{deviceType}}"] [=map/exists=], then set |context|.{{[[deviceType]]}} to |options|["{{deviceType}}"]. Otherwise, set |context|.{{[[deviceType]]}} to "[=device-type-cpu|cpu=]". - 1. If |options|["{{powerPreference}}"] [=map/exists=], then set |context|.{{[[powerPreference]]}} to |options|["{{powerPreference}}"]. Otherwise, set |context|.{{[[powerPreference]]}} to "[=power-preference-default|default=]". -1. If the validate MLContext steps given |context| return `false`, [=reject=] |promise| with a "{{NotSupportedError}}" {{DOMException}} and abort these steps. -1. [=Resolve=] |promise| with |context|. +
+ + The {{ML/createContext()}} method steps are: + +
+ 1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=allowed to use=] the [=webnn-feature|webnn=] feature, return [=a new promise=] [=rejected=] with a "{{SecurityError}}" {{DOMException}} and abort these steps. + 1. Let |promise| be [=a new promise=]. + 1. Return |promise| and run the following steps [=in parallel=]. + 1. Let |options| be the first argument. + 1. Run the create context steps given |options|: + 1. Let |context| be a new {{MLContext}} object. + 1. If |options| is a {{GPUDevice}} object, + 1. Set |context|.{{[[contextType]]}} to "[=webgpu-context|webgpu=]". + 1. Set |context|.{{[[deviceType]]}} to "[=device-type-gpu|gpu=]". + 1. Set |context|.{{[[powerPreference]]}} to "[=power-preference-default|default=]". + 1. Otherwise, + 1. Set |context|.{{[[contextType]]}} to "[=default-context|default=]". + 1. If |options|["{{deviceType}}"] [=map/exists=], then set |context|.{{[[deviceType]]}} to |options|["{{deviceType}}"]. Otherwise, set |context|.{{[[deviceType]]}} to "[=device-type-cpu|cpu=]". + 1. If |options|["{{powerPreference}}"] [=map/exists=], then set |context|.{{[[powerPreference]]}} to |options|["{{powerPreference}}"]. Otherwise, set |context|.{{[[powerPreference]]}} to "[=power-preference-default|default=]". + 1. If the validate MLContext steps given |context| return `false`, [=reject=] |promise| with a "{{NotSupportedError}}" {{DOMException}} and abort these steps. + 1. [=Resolve=] |promise| with |context|. +
+
### The {{ML/createContextSync()}} method ### {#api-ml-createcontextsync} -The {{ML/createContextSync()}} method steps are: -1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=allowed to use=] the [=webnn-feature|webnn=] feature, throw a "{{SecurityError}}" {{DOMException}} and abort these steps. -1. Let |options| be the first argument. -1. Let |context| be the result of running the create context steps given |options|. -1. If the validate MLContext steps given |context| return `false`, throw a "{{NotSupportedError}}" {{DOMException}} and abort these steps. -1. Return |context|. +
+ + The {{ML/createContextSync()}} method steps are: + +
+ 1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=allowed to use=] the [=webnn-feature|webnn=] feature, throw a "{{SecurityError}}" {{DOMException}} and abort these steps. + 1. Let |options| be the first argument. + 1. Let |context| be the result of running the create context steps given |options|. + 1. If the validate MLContext steps given |context| return `false`, throw a "{{NotSupportedError}}" {{DOMException}} and abort these steps. + 1. Return |context|. +
+
## The MLGraph interface ## {#api-mlgraph} The {{MLGraph}} interface represents a compiled computational graph. A compiled graph once constructed is immutable and cannot be subsequently changed. @@ -681,9 +855,9 @@ The {{MLGraph}} interface represents a compiled computational graph. A compiled interface MLGraph {}; +
{{MLGraph}} has the following internal slots: - -
+
: \[[context]] of type {{MLContext}} :: The context of type {{MLContext}} associated with this {{MLGraph}}. @@ -699,7 +873,8 @@ interface MLGraph {}; : \[[implementation]] :: The underlying implementation provided by the User Agent. -
+
+
### The MLOperandDescriptor dictionary ### {#api-mloperanddescriptor} -
+
+ The byte length of an {{MLOperandDescriptor}} |desc| is the value returned by the following steps: - + +
1. Let |elementLength| be 1. 1. For each |dimension| of |desc|.{{MLOperandDescriptor/dimensions}}: 1. Set |elementLength| to |elementLength| × |dimension|. 1. Let |elementSize| be the [=element size=] of one of the {{ArrayBufferView}} types that matches |desc|.{{MLOperandDescriptor/type}} according to [this table](#appendices-mloperandtype-arraybufferview-compatibility). 1. Return |elementLength| × |elementSize|. -
+
+ ### The MLOperand interface ### {#api-mloperand} @@ -747,9 +925,9 @@ For instance, an {{MLOperand}} may represent a constant feeding to an operation interface MLOperand {}; -{{MLOperand}} has the following internal slots:
-
+{{MLOperand}} has the following internal slots: +
: \[[builder]] of type {{MLGraphBuilder}} :: The {{MLOperand}}'s associated builder object. @@ -769,7 +947,7 @@ interface MLOperand {}; : \[[operator]] of type [=object=] :: Reference to {{MLOperand}}'s corresponding [=implementation-defined=] platform operator object. -
+
To get the rank of an {{MLOperand}} |operand|, run the following steps: @@ -782,44 +960,60 @@ Since the {{MLOperand/[[builder]]}} object is bound by the {{MLGraphBuilder/cons #### Creating {{MLOperand}} #### {#api-mloperand-create} The {{MLOperand}} objects are created by the methods of {{MLGraphBuilder}}, internally using the following algorithms. -To create MLOperand given |builder| and |desc|, run the following steps: -
+
+ + To create MLOperand given |builder| and |desc|, run the following steps: + +
1. If |builder| is not an instance of {{MLGraphBuilder}}, then throw a "{{TypeError}}" {{DOMException}} and stop. 1. If |desc| is not an [=object=] that [=implements=] {{MLOperandDescriptor}}, then throw a "{{TypeError}}" {{DOMException}} and stop. 1. Let |operand| be a new [=object=]. 1. Set |operand|.{{MLOperand/[[builder]]}} to |builder|. 1. Set |operand|.{{MLOperand/[[descriptor]]}} to |desc|. 1. Return |operand|. -
+
+ -To copy MLOperand given |operand|, run the following steps: -
+
+ + To copy MLOperand given |operand|, run the following steps: + +
1. If |operand| is not an instance of {{MLOperand}}, then throw a "{{TypeError}}" and stop. 1. Let |result| be a new [=object=]. 1. Set |result|.{{MLOperand/[[builder]]}} to |operand|.{{MLOperand/[[builder]]}}. 1. Set |result|.{{MLOperand/[[descriptor]]}} to |operand|.{{MLOperand/[[descriptor]]}}. 1. If |operand|.{{MLOperand/[[name]]}} [=map/exists=], then set |result|.{{MLOperand/[[name]]}} to |operand|.{{MLOperand/[[name]]}}. 1. Return |result|. -
+
+ -To check dimensions given |dimensions| and |type|, run the following steps: -
+
+ + To check dimensions given |dimensions| and |type|, run the following steps: + +
1. If |dimensions| is not an array of positive numbers, return `false`; 1. If |dimensions|.length is 0, return `false`. 1. If |dimensions|.length is too large to be supported by the implementation, return `false`. 1. If any element of |dimensions| is not a positive number, or it is too large to be supported by the implementation given |type|, return `false`. 1. Return `true`. -
+
+ -To validate MLOperand given |operand| and |builder|, run the following steps: -
+
+ + To validate MLOperand given |operand| and |builder|, run the following steps: + +
1. If |operand|.{{MLOperand/[[builder]]}} is not an instance of {{MLGraphBuilder}}, return `false`. 1. If |builder| is not `undefined` and is not equal to |operand|.{{MLOperand/[[builder]]}}, return `false`. 1. Let |desc| be |operand|.{{MLOperand/[[descriptor]]}}. 1. If |desc| is not an [=object=] that [=implements=] {{MLOperandDescriptor}}, return `false`. 1. If |desc|.{{MLOperandDescriptor/dimensions}} [=map/exists=] and invoking check dimensions given |desc|.{{MLOperandDescriptor/dimensions}} and |desc|.{{MLOperandDescriptor/type}} returns `false`, then return `false`. 1. Return `true`. -
+
+ ### The MLActivation interface ### {#api-mlactivation} @@ -911,9 +1105,9 @@ typedef record MLNamedArrayBufferViews; interface MLContext {}; +
{{MLContext}} has the following internal slots: - -
+
: \[[contextType]] of type [=context type=] :: The {{MLContext}}'s [=context type=]. @@ -923,19 +1117,26 @@ interface MLContext {}; : \[[powerPreference]] of type [=power preference=] :: The {{MLContext}}'s [=power preference=]. -
+
+
When the {{[[contextType]]}} is set to [=default-context|default=] with the {{MLContextOptions}}.{{deviceType}} set to [=device-type-gpu|gpu=], the user agent is responsible for creating an internal GPU device that operates within the context and is capable of ML workload submission on behalf of the calling application. In this setting however, only {{ArrayBufferView}} inputs and outputs are allowed in and out of the graph execution since the application has no way to know what type of internal GPU device is being created on their behalf. In this case, the user agent is responsible for automatic uploads and downloads of the inputs and outputs to and from the GPU memory using this said internal device.
### The {{MLContext}} validation algorithm ### {#api-mlcontext-validate} -To validate {{MLContext}}, given |context|, run these steps: -1. If |context|.{{[[contextType]]}} is not "[=webgpu-context|webgpu=]" or "[=default-context|default=], return `false`. -1. If |context|.{{[[deviceType]]}} is not "[=device-type-cpu|cpu=]" or "[=device-type-gpu|gpu=]", return `false`. -1. If |context|.{{[[powerPreference]]}} is not "[=power-preference-default|default=]" or "[=power-preference-high-performance|high-performance=]" or "[=power-preference-low-power|low-power=]", return `false`. -1. If the user agent cannot support |context|.{{[[contextType]]}}, |context|.{{[[deviceType]]}} and |context|.{{[[powerPreference]]}}, return `false`. -1. Return `true`; +
+ + To validate {{MLContext}}, given |context|, run these steps: + +
+ 1. If |context|.{{[[contextType]]}} is not "[=webgpu-context|webgpu=]" or "[=default-context|default=], return `false`. + 1. If |context|.{{[[deviceType]]}} is not "[=device-type-cpu|cpu=]" or "[=device-type-gpu|gpu=]", return `false`. + 1. If |context|.{{[[powerPreference]]}} is not "[=power-preference-default|default=]" or "[=power-preference-high-performance|high-performance=]" or "[=power-preference-low-power|low-power=]", return `false`. + 1. If the user agent cannot support |context|.{{[[contextType]]}}, |context|.{{[[deviceType]]}} and |context|.{{[[powerPreference]]}}, return `false`. + 1. Return `true`; +
+
### Synchronous Execution ### {#api-mlcontext-sync-execution} Synchronously carries out the computational workload of a compiled graph {{MLGraph}} on the calling thread, which must be a worker thread, to produce results as defined by the operations in the graph. This method of execution requires an {{MLContext}} created with {{MLContextOptions}}. Otherwise, it throws an "{{OperationError}}" {{DOMException}}. @@ -1002,49 +1203,59 @@ To validate buffer with descriptor given |bufferView| and |descriptor #### Examples #### {#api-mlcontext-sync-execution-examples}
-The following code showcases the synchronous computation with optional outputs in a worker. -
-const context = navigator.ml.createContextSync();
-
-// Build a graph with two outputs.
-const builder = new MLGraphBuilder(context);
-const descA = {type: 'float32', dimensions: [3, 4]};
-const a = builder.input('a', descA);
-const descB = {type: 'float32', dimensions: [4, 3]};
-const bufferB = new Float32Array(sizeOfShape(descB.dimensions)).fill(0.5);
-const b = builder.constant(descB, bufferB);
-const descC = {type: 'float32', dimensions: [3, 3]};
-const bufferC = new Float32Array(sizeOfShape(descC.dimensions)).fill(1);
-const c = builder.constant(descC, bufferC);
-const d = builder.matmul(a, b);
-const e = builder.add(d, c);
-const graph = builder.buildSync({'d': d, 'e': e});
-
-const bufferA = new Float32Array(sizeOfShape(descA.dimensions)).fill(0.5);
-const inputs = {'a': bufferA};
-
-// Compute d.
-const bufferD = new Float32Array(sizeOfShape([3, 3]));
-context.computeSync(graph, inputs, {'d': bufferD});
-console.log(`values: ${bufferD}`);
-
-// Compute e.
-const bufferE = new Float32Array(sizeOfShape([3, 3]));
-context.computeSync(graph, inputs, {'e': bufferE});
-console.log(`values: ${bufferE}`);
-
+
+ + The following code showcases the synchronous computation with optional outputs in a worker. + +
+    const context = navigator.ml.createContextSync();
+
+    // Build a graph with two outputs.
+    const builder = new MLGraphBuilder(context);
+    const descA = {type: 'float32', dimensions: [3, 4]};
+    const a = builder.input('a', descA);
+    const descB = {type: 'float32', dimensions: [4, 3]};
+    const bufferB = new Float32Array(sizeOfShape(descB.dimensions)).fill(0.5);
+    const b = builder.constant(descB, bufferB);
+    const descC = {type: 'float32', dimensions: [3, 3]};
+    const bufferC = new Float32Array(sizeOfShape(descC.dimensions)).fill(1);
+    const c = builder.constant(descC, bufferC);
+    const d = builder.matmul(a, b);
+    const e = builder.add(d, c);
+    const graph = builder.buildSync({'d': d, 'e': e});
+
+    const bufferA = new Float32Array(sizeOfShape(descA.dimensions)).fill(0.5);
+    const inputs = {'a': bufferA};
+
+    // Compute d.
+    const bufferD = new Float32Array(sizeOfShape([3, 3]));
+    context.computeSync(graph, inputs, {'d': bufferD});
+    console.log(`values: ${bufferD}`);
+
+    // Compute e.
+    const bufferE = new Float32Array(sizeOfShape([3, 3]));
+    context.computeSync(graph, inputs, {'e': bufferE});
+    console.log(`values: ${bufferE}`);
+  
+
-### The {{MLNamedArrayBufferViews}} transfer algorithm ### {#mlnamedarraybufferviews-transfer} -To transfer an {{MLNamedArrayBufferViews}} |views|: -1. Let |transferredViews| be a new {{MLNamedArrayBufferViews}}. -1. For each |key| -> |value| of |views|: - 1. Let |transferredBuffer| be the result of [=ArrayBuffer/transfer|transferring=] the [=underlying buffer=] of |value|. - 1. Let |constructor| be the appropriate [=view constructor=] for the type of {{ArrayBufferView}} |value|. - 1. Let |elementsNumber| be the result of the [=buffer byte length|byte length=] of |value| ÷ [=element size=] of |value|. - 1. Let |transferredView| be [=Construct=](|constructor|, |transferredBuffer|, |value|.\[[ByteOffset]], |elementsNumber|). - 1. Set |transferredViews|[|key|] to |transferredView|. -1. Return |transferredViews|. +### The {{MLNamedArrayBufferViews}} transfer algorithm ### {#mlnamedarraybufferviews-transfer-alg} +
+ + To transfer an {{MLNamedArrayBufferViews}} |views|: + +
+ 1. Let |transferredViews| be a new {{MLNamedArrayBufferViews}}. + 1. For each |key| -> |value| of |views|: + 1. Let |transferredBuffer| be the result of [=ArrayBuffer/transfer|transferring=] the [=underlying buffer=] of |value|. + 1. Let |constructor| be the appropriate [=view constructor=] for the type of {{ArrayBufferView}} |value|. + 1. Let |elementsNumber| be the result of the [=buffer byte length|byte length=] of |value| ÷ [=element size=] of |value|. + 1. Let |transferredView| be [=Construct=](|constructor|, |transferredBuffer|, |value|.\[[ByteOffset]], |elementsNumber|). + 1. Set |transferredViews|[|key|] to |transferredView|. + 1. Return |transferredViews|. +
+
### Asynchronous Execution ### {#api-mlcontext-async-execution} Asynchronously carries out the computational workload of a compiled graph {{MLGraph}} on a separate timeline, either on a worker thread for the CPU execution, or on a GPU timeline for the submission of GPU workload on the command queue. The asynchronous nature of this call avoids blocking the calling thread while the computation for result is ongoing. This method of execution requires an {{MLContext}} created with {{MLContextOptions}}. Otherwise, it throws an "{{OperationError}}" {{DOMException}}. @@ -1122,31 +1333,35 @@ partial interface MLContext { #### Examples #### {#api-mlcontext-async-execution-examples}
-The following code showcases the asynchronous computation. -
-const operandType = {type: 'float32', dimensions: [2, 2]};
-const context = await navigator.ml.createContext();
-const builder = new MLGraphBuilder(context);
-// 1. Create a computational graph 'C = 0.2 * A + B'.
-const constant = builder.constant(0.2);
-const A = builder.input('A', operandType);
-const B = builder.input('B', operandType);
-const C = builder.add(builder.mul(A, constant), B);
-// 2. Compile it into an executable.
-const graph = await builder.build({'C': C});
-// 3. Bind inputs to the graph and execute for the result.
-const bufferA = new Float32Array(4).fill(1.0);
-const bufferB = new Float32Array(4).fill(0.8);
-const bufferC = new Float32Array(4);
-const inputs = {'A': bufferA, 'B': bufferB};
-const outputs = {'C': bufferC};
-const result = await context.compute(graph, inputs, outputs);
-// The computed result of [[1, 1], [1, 1]] is in the buffer associated with
-// the output operand.
-console.log('Output value: ' + result.outputs.C);
-// Note: the result.outputs.C buffer is different from the bufferC, but it
-// shares the same backing memory allocation.
-
+
+ + The following code showcases the asynchronous computation. + +
+    const operandType = {type: 'float32', dimensions: [2, 2]};
+    const context = await navigator.ml.createContext();
+    const builder = new MLGraphBuilder(context);
+    // 1. Create a computational graph 'C = 0.2 * A + B'.
+    const constant = builder.constant(0.2);
+    const A = builder.input('A', operandType);
+    const B = builder.input('B', operandType);
+    const C = builder.add(builder.mul(A, constant), B);
+    // 2. Compile it into an executable.
+    const graph = await builder.build({'C': C});
+    // 3. Bind inputs to the graph and execute for the result.
+    const bufferA = new Float32Array(4).fill(1.0);
+    const bufferB = new Float32Array(4).fill(0.8);
+    const bufferC = new Float32Array(4);
+    const inputs = {'A': bufferA, 'B': bufferB};
+    const outputs = {'C': bufferC};
+    const result = await context.compute(graph, inputs, outputs);
+    // The computed result of [[1, 1], [1, 1]] is in the buffer associated with
+    // the output operand.
+    console.log('Output value: ' + result.outputs.C);
+    // Note: the result.outputs.C buffer is different from the bufferC, but it
+    // shares the same backing memory allocation.
+  
+
### WebGPU Interoperability ### {#api-mlcontext-webgpu-interop} @@ -1174,9 +1389,9 @@ typedef record MLNamedGPUResources; interface MLCommandEncoder {}; +
{{MLCommandEncoder}} has the following internal slots: - -
+
: \[[context]] of type {{MLContext}} :: The context of type {{MLContext}} associated with this {{MLCommandEncoder}}. @@ -1184,7 +1399,8 @@ interface MLCommandEncoder {}; : \[[implementation]] :: The underlying implementation provided by the User Agent. -
+
+
### Graph Initialization ### {#api-mlcommandencoder-graph-initialization} Record the initialization of the {{MLGraph}}. This is a necessary step for optimal performance during graph execution as it gives the platform an opportunity to prepare and optimize constant input data for the subsequent execution of the graph. This method should only be called once per graph. @@ -1195,16 +1411,23 @@ partial interface MLCommandEncoder { }; -
+
**Arguments:** - *graph*: an {{MLGraph}}. The compiled graph to be initialized with graph constant inputs. **Returns:** {{undefined}}.
-
-Graph initialization stage typically involves a process known as "weight preprocessing" where all the constant inputs to the graph are preprocessed and cached at the operating system level for subsequent graph execution calls. The initializing inputs are typically the constant weight data specified through the {{MLGraphBuilder/constant(descriptor, bufferView)|MLGraphBuilder/constant()}} method as constant operands during graph construction time. -
+
+ + The {{MLCommandEncoder/initializeGraph(graph)}} steps are: + +
+
+ Graph initialization stage typically involves a process known as "weight preprocessing" where all the constant inputs to the graph are preprocessed and cached at the operating system level for subsequent graph execution calls. The initializing inputs are typically the constant weight data specified through the {{MLGraphBuilder/constant(descriptor, bufferView)|MLGraphBuilder/constant()}} method as constant operands during graph construction time. +
+
+
### Dispatch Execution Commands ### {#api-mlcommandencoder-dispatch-commands} Record the {{MLGraph}} execution with the inputs {{MLNamedGPUResources}} and outputs {{MLNamedGPUResources}}. @@ -1215,16 +1438,22 @@ partial interface MLCommandEncoder { }; -
+
**Arguments:** - *graph*: an {{MLGraph}}. The compiled graph to be executed. - *inputs*: an {{MLNamedGPUResources}}. The resources of inputs. - *outputs*: an {{MLNamedGPUResources}}. The pre-allocated resources of required outputs. **Returns:** {{undefined}}. +
- 1. If any of the following requirements are unmet, then throw a "{{DataError}}" {{DOMException}} and stop. -
+
+ + The {{MLCommandEncoder/dispatch(graph, inputs, outputs)}} steps are: + +
+ 1. If any of the following requirements are unmet, then throw a "{{DataError}}" {{DOMException}} and stop. +
1. For each |key| -> |value| of |inputs|: 1. |graph|.{{MLGraph/[[inputDescriptors]]}}[|key|] must [=map/exist=]. 1. Let |inputDesc| be |graph|.{{MLGraph/[[inputDescriptors]]}}[|key|]. @@ -1236,16 +1465,16 @@ partial interface MLCommandEncoder { 1. If |value| is a {{GPUBuffer}}, then: 1. |value|.{{GPUBuffer/size}} must equal to [=byte length=] of |outputDesc|.
- - 1. For each |key| -> |value| of |inputs|: - 1. Set the input of |graph|.{{MLGraph/[[implementation]]}} that is associated with |key| to |value|. - 1. For each |key| -> |value| of |outputs|: - 1. Set the output of |graph|.{{MLGraph/[[implementation]]}} that is associated with |key| to |value|. - 1. Issue a compute request of |graph|.{{MLGraph/[[implementation]]}}. - 1. If there is an error returned by |graph|.{{MLGraph/[[implementation]]}}, then: - 1. Throw an "{{OperationError}}" {{DOMException}} and stop. - 1. Return {{undefined}}. -
+ 1. For each |key| -> |value| of |inputs|: + 1. Set the input of |graph|.{{MLGraph/[[implementation]]}} that is associated with |key| to |value|. + 1. For each |key| -> |value| of |outputs|: + 1. Set the output of |graph|.{{MLGraph/[[implementation]]}} that is associated with |key| to |value|. + 1. Issue a compute request of |graph|.{{MLGraph/[[implementation]]}}. + 1. If there is an error returned by |graph|.{{MLGraph/[[implementation]]}}, then: + 1. Throw an "{{OperationError}}" {{DOMException}} and stop. + 1. Return {{undefined}}. +
+ ### Generate GPU Command Buffer ### {#api-mlcommandencoder-generate-gpu-command-buffer} Complete the recording of ML workload and return a WebGPU-compatible {{GPUCommandBuffer}} containing the recorded workload. @@ -1313,22 +1542,33 @@ Both {{MLGraphBuilder}}.{{MLGraphBuilder/build()}} and {{MLGraphBuilder}}.{{MLGr ### The {{MLGraphBuilder}} constructor ### {#api-mlgraphbuilder-constructor} -The [=new=] {{MLGraphBuilder}} constructor steps are: -1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=allowed to use=] the [=webnn-feature|webnn=] feature, throw a "{{SecurityError}}" {{DOMException}} and abort these steps. -1. Let |context| be the first argument. -1. If the validate MLContext steps given |context| return `false`, throw a "{{TypeError}}" and abort these steps. -1. Set {{MLGraphBuilder/[[context]]}} to |context|. +
+ + The [=new=] {{MLGraphBuilder}} constructor steps are: + +
+ 1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=allowed to use=] the [=webnn-feature|webnn=] feature, throw a "{{SecurityError}}" {{DOMException}} and abort these steps. + 1. Let |context| be the first argument. + 1. If the validate MLContext steps given |context| return `false`, throw a "{{TypeError}}" and abort these steps. + 1. Set {{MLGraphBuilder/[[context]]}} to |context|. +
+
### The {{MLGraphBuilder/input()}} method ### {#api-mlgraphbuilder-input} Create a named {{MLOperand}} based on a descriptor, that can be used as an input. -
+ +
**Arguments:** - *name*: a [=string=] name of the input. - *descriptor*: an {{MLOperandDescriptor}} object. **Returns:**: an {{MLOperand}} object.
-
+ +
+ The {{MLGraphBuilder/input(name, descriptor)}} steps are: + +
The permissions and context validity have been checked by [[#api-mlgraphbuilder-constructor]] steps.
@@ -1340,25 +1580,31 @@ Create a named {{MLOperand}} based on a descriptor, that can be used as an input 1. If |descriptor|.{{MLOperandDescriptor/dimensions}} [=map/exists=]: 1. If the [=check dimensions=] steps given |descriptor|.{{MLOperandDescriptor/type}} and |descriptor|.{{MLOperandDescriptor/dimensions}} return `false`, throw a "{{DataError}}" {{DOMException}} and stop. 1. If the [=byte length=] of |descriptor| is not supported by the underlying platform, then throw a "{{DataError}}" {{DOMException}} and stop. - 1. Let |operand| be the result of invoking the create MLOperand steps with [=this=], `"input"` and |descriptor|. + 1. Let |operand| be the result of invoking the create MLOperand steps with [=this=] and |descriptor|. 1. If that throws, re-throw the exception and stop. 1. Set |operand|.{{MLOperand/[[name]]}} to |name|. 1. Make a request to the underlying platform to register |operand| as an input and store a reference to the corresponding [=implementation-defined=] platform object in |operand|.{{MLOperand/[[operand]]}}. 1. If that fails, throw an "{{OperationError}}" {{DOMException}} and abort these steps. + 1. Return |operand|. +
+
### The constant() method ### {#api-mlgraphbuilder-constant-method} Create a constant {{MLOperand}} that can be used in {{MLGraphBuilder}} methods. #### The {{MLGraphBuilder/constant(descriptor, bufferView)}} method #### {#api-mlgraphbuilder-constant} -
+
**Arguments:** - *descriptor*: an {{MLOperandDescriptor}} object - *bufferView*: an {{MLBufferView}} **Returns:**: an {{MLOperand}} object.
-
-The {{MLGraphBuilder/constant(descriptor, bufferView)}} steps are: +
+ + The {{MLGraphBuilder/constant(descriptor, bufferView)}} steps are: + +
The permissions and context validity have been checked by [[#api-mlgraphbuilder-constructor]] steps.
@@ -1374,19 +1620,23 @@ The {{MLGraphBuilder/constant(descriptor, bufferView)}} steps are: 1. Make a request to the underlying platform to register |operand| as a tensor constant with |bytes| as value and store a reference to the corresponding [=implementation-defined=] object to |operand|.{{MLOperand/[[operand]]}}. 1. If that fails, throw an "{{OperationError}}" {{DOMException}} and stop. 1. Return |operand|. -
+
+ #### The {{MLGraphBuilder/constant(value, type)}} method #### {#api-mlgraphbuilder-constant-value-type} -
+
**Arguments:** - *value*: a number - *type*: an optional {{MLOperandType}}, by default *"float32"*. **Returns:**: an {{MLOperand}} object.
-
-The {{MLGraphBuilder/constant(value, type)}} steps are: +
+ + The {{MLGraphBuilder/constant(value, type)}} steps are: + +
The permissions and context validity have been checked by [[#api-mlgraphbuilder-constructor]] steps.
@@ -1404,7 +1654,8 @@ The {{MLGraphBuilder/constant(value, type)}} steps are: 1. Make a request to the underlying platform to register |operand| as a scalar constant with |value| as value and store a reference of the [=implementation-defined=] platform object for the corresponding (scalar or tensor constant) operand to |operand|.{{MLOperand/[[operand]]}}. 1. If that throws, re-throw the error and stop. 1. Return |operand|. -
+
+ ### The batchNormalization() method ### {#api-mlgraphbuilder-batchnorm} Normalize the tensor values of input features across the batch dimension using [[Batch-Normalization]]. For each input feature, the mean and variance values of that feature supplied in this calculation as parameters are previously computed across the batch dimension of the input during the model training phase of this operation. @@ -1447,23 +1698,21 @@ partial interface MLGraphBuilder { An {{MLActivation}} object. Specifies the optional activation function that immediately follows the normalization operation. -
+
**Arguments:** - *input*: an {{MLOperand}}. The input N-D tensor. - *mean*: an {{MLOperand}}. Specifies the 1-D tensor of the mean values of the input features across the batch whose length is equal to the size of the input dimension denoted by {{MLBatchNormalizationOptions/axis}}. - *variance*: an {{MLOperand}}. The 1-D tensor of the variance values of the input features across the batch whose length is equal to the size of the input dimension denoted by {{MLBatchNormalizationOptions/axis}}. - *options*: an optional {{MLBatchNormalizationOptions}}. Specifies the optional parameters of the operation. - - *scale*: an {{MLOperand}}. The 1-D tensor of the scaling values whose length is equal to the size of the input dimension denoted by *options.axis*. - - *bias*: an {{MLOperand}}. The 1-D tensor of the bias values whose length is equal to the size of the input dimension denoted by *options.axis*. - - *axis*: an {{unsigned long}} scalar. The index to the feature count dimension of the input shape for which the mean and variance values are. Its value must be in the range [0, N-1] where N is the rank of input tensor. When it's not specified, the default value is 1. - - *epsilon*: a {{float}} scalar. A small value to prevent computational error due to divide-by-zero. The default value is 0.00001 when not specified. - - *activation*: an {{MLActivation}}. The optional activation function that immediately follows the normalization operation. **Returns:** an {{MLOperand}}. The batch-normalized N-D tensor of the same shape as the input tensor.
-
+
+ The {{MLGraphBuilder/batchNormalization()}} method steps are: + +
1. Let |input| be the first argument. To validate |input|, run these substeps: 1. If |input| is not an [=object=] that [=implements=] {{MLOperand}}, then throw a "{{TypeError}}" {{DOMException}} and abort these steps. 1. Let |mean| be the second argument, representing a vector with the moving mean values for |input|. To validate |mean|, run the following substeps: @@ -1476,12 +1725,13 @@ partial interface MLGraphBuilder { 1. If |input| is a 4-D tensor of the *"nchw"* layout, set |options|.axis to 1. 1. If |input| is a 4-D tensor of the *"nhwc"* layout, set |options|.axis to 3. 1. Let |result| be an {{MLOperand}} representing the results. It may use the same underlying data as |input|. - 1. Issue a request to the underlying platform to initialize the batch normalization, given |input|, |mean|, |variance|, |options| and |result| to store the results and |options|. Wait for completion. + 1. Issue a request to the underlying platform to initialize the batch normalization, passing the arguments |input|, |mean|, |variance| and |options| and given |result| to store the results and |options|. Wait for completion.
1. If |options|.activation [=map/exists=], implementations MAY use it to optimize the operation flow.
1. Return |result|. -
+
+
The behavior of this operation when the input tensor is 4-D of the *"nchw"* layout and the activation is of operator type *relu* can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint. @@ -1517,11 +1767,14 @@ partial interface MLGraphBuilder {
+
+ The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint. -
+  
+
     if (options.minValue === undefined) {
       if (options.maxValue === undefined) {
         return x;
@@ -1537,17 +1790,23 @@ partial interface MLGraphBuilder {
             builder.constant(options.maxValue));
       }
     }
-    
+
-To check clamp options given |options|, run the following steps: - 1. If |options| is not an object that [=implements=] {{MLClampOptions}}, then return `false`. +
+ + To check clamp options given |options|, run the following steps: + +
+ 1. If |options| is not an [=object=] that [=implements=] {{MLClampOptions}}, then return `false`. 1. If |options|.{{MLClampOptions/minValue}} and |options|.{{MLClampOptions/maxValue}} are not a [=numeric type=], then then return `false`. 1. If |options|.{{MLClampOptions/minValue}} is greater than |options|.{{MLClampOptions/maxValue}}, then return `false`. 1. Return `true`. +
+
#### The {{MLGraphBuilder/clamp(operand, options)}} method #### {#api-mlgraphbuilder-clamp-operand-options} -
+
**Arguments:** - *operand*: an {{MLOperand}}. The input tensor. - *options*: an optional {{MLClampOptions}}. The optional parameters of the operation. @@ -1556,8 +1815,12 @@ To check clamp options given |options|, run the following steps: **Returns:** - an {{MLOperand}}. The output tensor of the same shape as *operand*.
-
+ +
+ The {{MLGraphBuilder/clamp(operand, options)}} method steps are: + +
1. Let |operand| be the first argument. 1. Let |options| be the second argument. 1. If running the check clamp options steps with |options| returns `false`, then throw a "{{TypeError}}" {{DOMException}} and abort these steps. @@ -1571,10 +1834,11 @@ To check clamp options given |options|, run the following steps: 1. Register the |result|.{{MLOperand/[[operand]]}} as output to |operatorImpl|. 1. Store a reference to |operatorImpl| in |result|.{{MLOperand/[[operator]]}}. 1. Return |result|. -
+
+ #### The {{MLGraphBuilder/clamp(options)}} method #### {#api-mlgraphbuilder-clamp-options} -
+
**Arguments:** - *options*: an optional {{MLClampOptions}}. The optional parameters of the operation. - *minValue*: a {{float}} scalar. Specifies the minimum value of the range. When it is not specified, the clamping is not performed on the lower limit of the range. @@ -1582,8 +1846,12 @@ To check clamp options given |options|, run the following steps: **Returns:** - an {{MLActivation}}. The operator representing the clamp operation.
-
+ +
+ The {{MLGraphBuilder/clamp(options)}} method steps are: + +
1. Let |options| be the first argument. 1. If running the check clamp options steps with |options| returns `false`, then throw a "{{TypeError}}" {{DOMException}} and abort these steps. 1. Let |op| be the result of invoking the create MLActivation steps with `"clamp"` and |options|. @@ -1592,7 +1860,8 @@ To check clamp options given |options|, run the following steps: 1. Make a request to the underlying platform to connect |op| with the [=implementation-defined=] platform operator for clamp |operatorImpl|. 1. Store a reference to |operatorImpl| in |op|.{{MLActivation/[[operator]]}}. 1. Return |op|. -
+
+ ### The concat() method ### {#api-mlgraphbuilder-concat} Concatenates the input tensors along a given axis. @@ -1612,8 +1881,12 @@ partial interface MLGraphBuilder { that all the inputs concatenated along. The size of that dimension is computed as the sum of all the input sizes of the same dimension.
-
+ +
+ The {{MLGraphBuilder/concat(inputs, axis)}} steps are: + +
The permissions and context validity have been checked by [[#api-mlgraphbuilder-constructor]] steps.
@@ -1642,7 +1915,8 @@ partial interface MLGraphBuilder { 1. Make a request to the underlying platform to create an operator for this method with |inputs| connected as input and |output| connected as output and store a reference to the [=implementation-defined=] platform object to |output|.{{MLOperand/[[operand]]}}. 1. If that fails, throw a "{{DataError}}" {{DOMException}} and stop. 1. Return |output|. -
+
+ ### The conv2d() method ### {#api-mlgraphbuilder-conv2d} Compute a 2-D convolution given 4-D input and filter tensors @@ -3177,12 +3451,15 @@ partial interface MLGraphBuilder { **Returns:** a sequence of {{MLOperand}}. The splitted output tensors. If *splits* is an {{unsigned long}}, the length of the output sequence equals to *splits*. The shape of each output tensor is the same as *input* except the dimension size of *axis* equals to the quotient of dividing the dimension size of *input* along *axis* by *splits*. If *splits* is a sequence of {{unsigned long}}, the length of the output sequence equals to the length of *splits*. The shape of the i-th output tensor is the same as as *input* except along *axis* where the dimension size is *splits[i]*. -
+
+
+ The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint. -
+  
+
     // This sample shows the case that the splits parameter is an array.
     const outputs = [];
     let starts = Array(input_rank).fill(0);
@@ -3195,8 +3472,8 @@ partial interface MLGraphBuilder {
       start += size;
     }
     return outputs;
-    
-
+ +
### The squeeze() method ### {#api-mlgraphbuilder-squeeze} @@ -3279,49 +3556,54 @@ const context = await navigator.ml.createContext({powerPreference: 'low-power'})
-The following code builds a graph as: +Given the following build graph:
-constant1 ---+
-             +--- Add ---> intermediateOutput1 ---+
-input1    ---+                                    |
-                                                  +--- Mul---> output
-constant2 ---+                                    |
-             +--- Add ---> intermediateOutput2 ---+
-input2    ---+
+    constant1 ---+
+                +--- Add ---> intermediateOutput1 ---+
+    input1    ---+                                    |
+                                                    +--- Mul---> output
+    constant2 ---+                                    |
+                +--- Add ---> intermediateOutput2 ---+
+    input2    ---+
 
-
-// Use tensors in 4 dimensions.
-const TENSOR_DIMS = [1, 2, 2, 2];
-const TENSOR_SIZE = 8;
+
+ + The following code implements the graph: + +
+    // Use tensors in 4 dimensions.
+    const TENSOR_DIMS = [1, 2, 2, 2];
+    const TENSOR_SIZE = 8;
 
-const builder = new MLGraphBuilder(context);
+    const builder = new MLGraphBuilder(context);
 
-// Create MLOperandDescriptor object.
-const desc = {type: 'float32', dimensions: TENSOR_DIMS};
+    // Create MLOperandDescriptor object.
+    const desc = {type: 'float32', dimensions: TENSOR_DIMS};
 
-// constant1 is a constant MLOperand with the value 0.5.
-const constantBuffer1 = new Float32Array(TENSOR_SIZE).fill(0.5);
-const constant1 = builder.constant(desc, constantBuffer1);
+    // constant1 is a constant MLOperand with the value 0.5.
+    const constantBuffer1 = new Float32Array(TENSOR_SIZE).fill(0.5);
+    const constant1 = builder.constant(desc, constantBuffer1);
 
-// input1 is one of the input MLOperands. Its value will be set before execution.
-const input1 = builder.input('input1', desc);
+    // input1 is one of the input MLOperands. Its value will be set before execution.
+    const input1 = builder.input('input1', desc);
 
-// constant2 is another constant MLOperand with the value 0.5.
-const constantBuffer2 = new Float32Array(TENSOR_SIZE).fill(0.5);
-const constant2 = builder.constant(desc, constantBuffer2);
+    // constant2 is another constant MLOperand with the value 0.5.
+    const constantBuffer2 = new Float32Array(TENSOR_SIZE).fill(0.5);
+    const constant2 = builder.constant(desc, constantBuffer2);
 
-// input2 is another input MLOperand. Its value will be set before execution.
-const input2 = builder.input('input2', desc);
+    // input2 is another input MLOperand. Its value will be set before execution.
+    const input2 = builder.input('input2', desc);
 
-// intermediateOutput1 is the output of the first Add operation.
-const intermediateOutput1 = builder.add(constant1, input1);
+    // intermediateOutput1 is the output of the first Add operation.
+    const intermediateOutput1 = builder.add(constant1, input1);
 
-// intermediateOutput2 is the output of the second Add operation.
-const intermediateOutput2 = builder.add(constant2, input2);
+    // intermediateOutput2 is the output of the second Add operation.
+    const intermediateOutput2 = builder.add(constant2, input2);
 
-// output is the output MLOperand of the Mul operation.
-const output = builder.mul(intermediateOutput1, intermediateOutput2);
-
+ // output is the output MLOperand of the Mul operation. + const output = builder.mul(intermediateOutput1, intermediateOutput2); +
+
@@ -3333,24 +3615,28 @@ const graph = await builder.build({'output': output});
-The following code executes the compiled graph. -
-// Setup the input buffers with value 1.
-const inputBuffer1 = new Float32Array(TENSOR_SIZE).fill(1);
-const inputBuffer2 = new Float32Array(TENSOR_SIZE).fill(1);
-const outputBuffer = new Float32Array(TENSOR_SIZE);
-
-// Execute the compiled graph with the specified inputs.
-const inputs = {
-  'input1': inputBuffer1,
-  'input2': inputBuffer2,
-};
-const outputs = {'output': outputBuffer};
-const result = await context.compute(graph, inputs, outputs);
-
-console.log('Output value: ' + result.outputs.output);
-// Output value: 2.25,2.25,2.25,2.25,2.25,2.25,2.25,2.25
-
+
+ + The following code executes the compiled graph. + +
+    // Setup the input buffers with value 1.
+    const inputBuffer1 = new Float32Array(TENSOR_SIZE).fill(1);
+    const inputBuffer2 = new Float32Array(TENSOR_SIZE).fill(1);
+    const outputBuffer = new Float32Array(TENSOR_SIZE);
+
+    // Execute the compiled graph with the specified inputs.
+    const inputs = {
+    'input1': inputBuffer1,
+    'input2': inputBuffer2,
+    };
+    const outputs = {'output': outputBuffer};
+    const result = await context.compute(graph, inputs, outputs);
+
+    console.log('Output value: ' + result.outputs.output);
+    // Output value: 2.25,2.25,2.25,2.25,2.25,2.25,2.25,2.25
+  
+
# Appendices # {#appendices}