Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Wasm and WebGL backends (WebNN-Polyfill) #281

Merged
merged 5 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ We welcome contributions from the community to make webnn-samples even better! I
### WebNN Resources
To learn more about Web Neural Network API (WebNN) and its capabilities, check out the following resources:
* [Web Neural Network API Specification](https://webmachinelearning.github.io/webnn/)
* [WebNN Polyfill](https://github.com/webmachinelearning/webnn-polyfill)
* [WebNN Community Group](https://webmachinelearning.github.io/)
* [W3C WebML Working Group and Community Group](https://webmachinelearning.github.io/)

### WebNN API Samples
* [WebNN code editor](https://webmachinelearning.github.io/webnn-samples/code/)
Expand Down
2 changes: 1 addition & 1 deletion code/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
<script src="./libs/codemirror/codemirror.js"></script>
<script src="./libs/codemirror/javascript.js"></script>
<script src="./libs/codemirror/sublime.js"></script>
<script src="https://webmachinelearning.github.io/webnn-polyfill/dist/webnn-polyfill.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js"></script>
<script type="module">
import {main} from './main.js';
window.onload = async () => {
Expand Down
8 changes: 4 additions & 4 deletions code/main.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {samplesRepo} from './samples_repo.js';
import * as utils from '../common/utils.js';
import {addAlert} from '../common/ui.js';

window.sizeOfShape = utils.sizeOfShape;

export async function main() {
// Set backend
if (await utils.isWebNN()) {
await utils.setBackend('webnn', 'cpu');
} else {
await utils.setBackend('polyfill', 'cpu');
if (!await utils.isWebNN()) {
console.log(utils.webNNNotSupportMessage());
addAlert(utils.webNNNotSupportMessageHTML());
}
const selectElement = document.getElementById('example-select');
for (const name of samplesRepo.names()) {
Expand Down
7 changes: 0 additions & 7 deletions common/component/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -599,13 +599,6 @@ $(document).ready(async () => {
$("#footer").html(footer());
if (await isWebNN()) {
if ($("#backendBtns")) {
$('label[name="polyfill"]').addClass("disabled");
$('label[name="polyfill"]').addClass("btn-outline-secondary");
$('label[name="polyfill"]').removeClass("btn-outline-info");
$('label[name="polyfill"]').attr(
"title",
"WebNN is supported, disable WebNN Polyfill."
);
// Disable WebNN NPU backend if failed to find a capable NPU adapter.
try {
await navigator.ml.createContext({deviceType: 'npu'});
Expand Down
103 changes: 8 additions & 95 deletions common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,48 +269,6 @@ export function getMedianValue(array) {
(array[array.length / 2 - 1] + array[array.length / 2]) / 2;
}

// Set tf.js backend based WebNN's 'MLDeviceType' option
export async function setPolyfillBackend(device) {
// Simulate WebNN's device selection using various tf.js backends.
// MLDeviceType: ['default', 'gpu', 'cpu']
// 'default' or 'gpu': tfjs-backend-webgl, 'cpu': tfjs-backend-wasm
if (!device) device = 'gpu';
// Use 'webgl' by default for better performance.
// Note: 'wasm' backend may run failed on some samples since
// some ops aren't supported on 'wasm' backend at present
const backend = device === 'cpu' ? 'wasm' : 'webgl';
const context = await navigator.ml.createContext();
const tf = context.tf;
if (tf) {
if (backend == 'wasm') {
const wasm = context.wasm;
// Force to use Wasm SIMD only
wasm.setWasmPath(`https://unpkg.com/@tensorflow/tfjs-backend-wasm@${tf.version_core}/dist/tfjs-backend-wasm-simd.wasm`);
}
if (!(await tf.setBackend(backend))) {
throw new Error(`Failed to set tf.js backend ${backend}.`);
}
await tf.ready();
let backendInfo = backend == 'wasm' ? 'WASM' : 'WebGL';
if (backendInfo == 'WASM') {
const hasSimd = tf.env().features['WASM_HAS_SIMD_SUPPORT'];
const hasThreads = tf.env().features['WASM_HAS_MULTITHREAD_SUPPORT'];
if (hasThreads && hasSimd) {
backendInfo += ' (SIMD + threads)';
} else if (hasThreads && !hasSimd) {
backendInfo += ' (threads)';
} else if (!hasThreads && hasSimd) {
backendInfo += ' (SIMD)';
}
}
addAlert(
`This sample is running on ` +
`<a href='https://github.com/webmachinelearning/webnn-polyfill'>` +
`WebNN-polyfill</a> with tf.js ${tf.version_core} ` +
`<b>${backendInfo}</b> backend.`, 'info');
}
}

// Get url params
export function getUrlParams() {
const params = new URLSearchParams(location.search);
Expand Down Expand Up @@ -346,59 +304,6 @@ export function getUrlParams() {
return [numRuns, powerPreference, numThreads];
}

// Set backend for using WebNN-polyfill or WebNN
export async function setBackend(backend, device) {
const webnnPolyfillId = 'webnn_polyfill';
const webnnNodeId = 'webnn_node';
const webnnPolyfillElem = document.getElementById(webnnPolyfillId);
const webnnNodeElem = document.getElementById(webnnNodeId);

if (backend === 'polyfill') {
if (webnnNodeElem) {
document.body.removeChild(webnnNodeElem);
// Unset global objects defined in node_setup.js
global.navigator.ml = undefined;
global.MLContext = undefined;
global.MLGraphBuilder = undefined;
global.MLGraph = undefined;
global.MLOperand = undefined;
}
if (!webnnPolyfillElem) {
const webnnPolyfillUrl =
'https://webmachinelearning.github.io/webnn-polyfill/dist/webnn-polyfill.js';
if (typeof(tf) != 'undefined') {
// Reset tf.ENV to avoid environments from tf.min.js
// affect webnn-polyfill.js
tf.engine().reset();
}
// Create WebNN-polyfill script
await loadScript(webnnPolyfillUrl, webnnPolyfillId);
}
await setPolyfillBackend(device);
} else if (backend === 'webnn') {
if (!await isWebNN()) {
addAlert(`WebNN is not supported!`, 'warning');
}
} else {
addAlert(`Unknow backend: ${backend}`, 'warning');
}
}

// Promise to load script with url and id
async function loadScript(url, id) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.onload = resolve;
script.onerror = reject;
script.src = url;
script.id = id;
if (url.startsWith('http')) {
script.crossOrigin = 'anonymous';
}
document.body.appendChild(script);
});
}

export async function isWebNN() {
if (typeof MLGraphBuilder !== 'undefined') {
const context = await navigator.ml.createContext();
Expand All @@ -408,6 +313,14 @@ export async function isWebNN() {
}
}

export function webNNNotSupportMessage() {
return 'Your browser does not support WebNN.';
}

export function webNNNotSupportMessageHTML() {
return 'Your browser does not support WebNN. Please refer to <a href="https://github.com/webmachinelearning/webnn-samples/#webnn-installation-guides">WebNN Installation Guides</a> for more details.';
}

// Derive from
// https://github.com/webmachinelearning/webnn-baseline/blob/main/src/lib/compute-padding.js
/**
Expand Down
8 changes: 0 additions & 8 deletions face_recognition/facenet_nchw.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,14 +276,6 @@ export class FaceNetNchw {
this.graph_ = await this.builder_.build({'output': outputOperand});
}

// Release the constant tensors of a model
dispose() {
// dispose() is only available in webnn-polyfill
if (this.graph_ !== null && 'dispose' in this.graph_) {
this.graph_.dispose();
}
}

async compute(inputBuffer) {
this.context_.writeTensor(this.inputTensor_, inputBuffer);
const inputs = {'input': this.inputTensor_};
Expand Down
8 changes: 0 additions & 8 deletions face_recognition/facenet_nhwc.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,14 +245,6 @@ export class FaceNetNhwc {
this.graph_ = await this.builder_.build({'output': outputOperand});
}

// Release the constant tensors of a model
dispose() {
// dispose() is only available in webnn-polyfill
if (this.graph_ !== null && 'dispose' in this.graph_) {
this.graph_.dispose();
}
}

async compute(inputBuffer) {
this.context_.writeTensor(this.inputTensor_, inputBuffer);
const inputs = {'input': this.inputTensor_};
Expand Down
6 changes: 0 additions & 6 deletions face_recognition/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,6 @@
</div>
<div class="col-md-auto">
<div class="btn-group-toggle" data-toggle="buttons" id="backendBtns">
<label class="btn btn-outline-info custom" name="polyfill">
<input type="radio" name="backend" id="polyfill_cpu" autocomplete="off">Wasm (CPU)
</label>
<label class="btn btn-outline-info custom" name="polyfill">
<input type="radio" name="backend" id="polyfill_gpu" autocomplete="off">WebGL (GPU)
</label>
<label class="btn btn-outline-info custom" name="webnn">
<input type="radio" name="backend" id="webnn_cpu" autocomplete="off">WebNN (CPU)
</label>
Expand Down
12 changes: 2 additions & 10 deletions face_recognition/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ $(document).ready(async () => {
if (await utils.isWebNN()) {
$('#webnn_cpu').click();
} else {
$('#polyfill_cpu').click();
console.log(utils.webNNNotSupportMessage());
ui.addAlert(utils.webNNNotSupportMessageHTML());
}
});

Expand Down Expand Up @@ -307,19 +308,10 @@ async function main() {
lastdeviceType != deviceType || lastBackend != backend) {
if (lastdeviceType != deviceType || lastBackend != backend) {
// Set backend and device
await utils.setBackend(backend, deviceType);
lastdeviceType = lastdeviceType != deviceType ?
deviceType : lastdeviceType;
lastBackend = lastBackend != backend ? backend : lastBackend;
}
if (frInstance !== null) {
// Call dispose() to and avoid memory leak
frInstance.dispose();
}
if (fdInstance !== null) {
// Call dispose() to and avoid memory leak
fdInstance.dispose();
}
fdInstanceType = fdModelName + layout;
frInstanceType = frModelName + layout;
fdInstance = constructNetObject(fdInstanceType);
Expand Down
8 changes: 0 additions & 8 deletions facial_landmark_detection/face_landmark_nchw.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,6 @@ export class FaceLandmarkNchw {
this.graph_ = await this.builder_.build({'output': outputOperand});
}

// Release the constant tensors of a model
dispose() {
// dispose() is only available in webnn-polyfill
if (this.graph_ !== null && 'dispose' in this.graph_) {
this.graph_.dispose();
}
}

async compute(inputBuffer) {
this.context_.writeTensor(this.inputTensor_, inputBuffer);
const inputs = {'input': this.inputTensor_};
Expand Down
8 changes: 0 additions & 8 deletions facial_landmark_detection/face_landmark_nhwc.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,6 @@ export class FaceLandmarkNhwc {
this.graph_ = await this.builder_.build({'output': outputOperand});
}

// Release the constant tensors of a model
dispose() {
// dispose() is only available in webnn-polyfill
if (this.graph_ !== null && 'dispose' in this.graph_) {
this.graph_.dispose();
}
}

async compute(inputBuffer) {
this.context_.writeTensor(this.inputTensor_, inputBuffer);
const inputs = {'input': this.inputTensor_};
Expand Down
6 changes: 0 additions & 6 deletions facial_landmark_detection/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,6 @@
</div>
<div class="col-md-auto">
<div class="btn-group-toggle" data-toggle="buttons" id="backendBtns">
<label class="btn btn-outline-info custom" name="polyfill">
<input type="radio" name="backend" id="polyfill_cpu" autocomplete="off">Wasm (CPU)
</label>
<label class="btn btn-outline-info custom" name="polyfill">
<input type="radio" name="backend" id="polyfill_gpu" autocomplete="off">WebGL (GPU)
</label>
<label class="btn btn-outline-info custom" name="webnn">
<input type="radio" name="backend" id="webnn_cpu" autocomplete="off">WebNN (CPU)
</label>
Expand Down
12 changes: 2 additions & 10 deletions facial_landmark_detection/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ $(document).ready(async () => {
if (await utils.isWebNN()) {
$('#webnn_cpu').click();
} else {
$('#polyfill_cpu').click();
console.log(utils.webNNNotSupportMessage());
ui.addAlert(utils.webNNNotSupportMessageHTML());
}
});

Expand Down Expand Up @@ -243,19 +244,10 @@ async function main() {
lastdeviceType != deviceType || lastBackend != backend) {
if (lastdeviceType != deviceType || lastBackend != backend) {
// Set backend and device
await utils.setBackend(backend, deviceType);
lastdeviceType = lastdeviceType != deviceType ?
deviceType : lastdeviceType;
lastBackend = lastBackend != backend ? backend : lastBackend;
}
if (fldInstance !== null) {
// Call dispose() to and avoid memory leak
fldInstance.dispose();
}
if (fdInstance !== null) {
// Call dispose() to and avoid memory leak
fdInstance.dispose();
}
fdInstanceType = fdModelName + layout;
fldInstanceType = fldModelName + layout;
fdInstance = constructNetObject(fdInstanceType);
Expand Down
8 changes: 0 additions & 8 deletions facial_landmark_detection/ssd_mobilenetv2_face_nchw.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,14 +243,6 @@ ${nameArray[1]}`;
this.graph_ = await this.builder_.build(outputOperand);
}

// Release the constant tensors of a model
dispose() {
// dispose() is only available in webnn-polyfill
if (this.graph_ !== null && 'dispose' in this.graph_) {
this.graph_.dispose();
}
}

async compute(inputBuffer) {
this.context_.writeTensor(this.inputTensor_, inputBuffer);
const inputs = {'input': this.inputTensor_};
Expand Down
8 changes: 0 additions & 8 deletions facial_landmark_detection/ssd_mobilenetv2_face_nhwc.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,14 +255,6 @@ ${nameArray[1]}`;
this.graph_ = await this.builder_.build(outputOperand);
}

// Release the constant tensors of a model
dispose() {
// dispose() is only available in webnn-polyfill
if (this.graph_ !== null && 'dispose' in this.graph_) {
this.graph_.dispose();
}
}

async compute(inputBuffer) {
this.context_.writeTensor(this.inputTensor_, inputBuffer);
const inputs = {'input': this.inputTensor_};
Expand Down
8 changes: 0 additions & 8 deletions image_classification/efficientnet_fp16_nchw.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,6 @@ export class EfficientNetFP16Nchw {
this.graph_ = await this.builder_.build({'output': outputOperand});
}

// Release the constant tensors of a model
dispose() {
// dispose() is only available in webnn-polyfill
if (this.graph_ !== null && 'dispose' in this.graph_) {
this.graph_.dispose();
}
}

async compute(inputBuffer) {
this.context_.writeTensor(this.inputTensor_, inputBuffer);
const inputs = {'input': this.inputTensor_};
Expand Down
6 changes: 0 additions & 6 deletions image_classification/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,6 @@
</div>
<div class="col-md-auto">
<div class="btn-group-toggle" data-toggle="buttons" id="backendBtns">
<label class="btn btn-outline-info custom" name="polyfill">
<input type="radio" name="backend" id="polyfill_cpu" autocomplete="off">Wasm (CPU)
</label>
<label class="btn btn-outline-info custom" name="polyfill">
<input type="radio" name="backend" id="polyfill_gpu" autocomplete="off">WebGL (GPU)
</label>
<label class="btn btn-outline-info custom" name="webnn">
<input type="radio" name="backend" id="webnn_cpu" autocomplete="off">WebNN (CPU)
</label>
Expand Down
Loading