diff --git a/mediapipe/tasks/web/vision/core/BUILD b/mediapipe/tasks/web/vision/core/BUILD index cd2954eb..8077f30d 100644 --- a/mediapipe/tasks/web/vision/core/BUILD +++ b/mediapipe/tasks/web/vision/core/BUILD @@ -40,6 +40,7 @@ mediapipe_ts_library( "//mediapipe/tasks/web/core:task_runner", "//mediapipe/web/graph_runner:graph_runner_image_lib_ts", "//mediapipe/web/graph_runner:graph_runner_ts", + "//mediapipe/web/graph_runner:platform_utils", "//mediapipe/web/graph_runner:register_model_resources_graph_service_ts", ], ) diff --git a/mediapipe/tasks/web/vision/core/vision_task_runner.ts b/mediapipe/tasks/web/vision/core/vision_task_runner.ts index 4f0a3e08..2a5c3f0e 100644 --- a/mediapipe/tasks/web/vision/core/vision_task_runner.ts +++ b/mediapipe/tasks/web/vision/core/vision_task_runner.ts @@ -20,6 +20,7 @@ import {WasmFileset} from '../../../../tasks/web/core/wasm_fileset'; import {ImageProcessingOptions} from '../../../../tasks/web/vision/core/image_processing_options'; import {GraphRunner, ImageSource, WasmMediaPipeConstructor} from '../../../../web/graph_runner/graph_runner'; import {SupportImage, WasmImage} from '../../../../web/graph_runner/graph_runner_image_lib'; +import {isWebKit} from '../../../../web/graph_runner/platform_utils'; import {SupportModelResourcesGraphService} from '../../../../web/graph_runner/register_model_resources_graph_service'; import {VisionTaskOptions} from './vision_task_options'; @@ -39,11 +40,13 @@ export class VisionGraphRunner extends GraphRunnerVisionType {} * GraphRunner should create its own canvas. */ function createCanvas(): HTMLCanvasElement|OffscreenCanvas|undefined { - // Returns an HTML canvas or `undefined` if OffscreenCanvas is available + const supportsWebGL2ForOffscreenCanvas = + typeof OffscreenCanvas !== 'undefined' && !isWebKit(); + + // Returns an HTML canvas or `undefined` if OffscreenCanvas is fully supported // (since the graph runner can initialize its own OffscreenCanvas). - return typeof OffscreenCanvas === 'undefined' ? - document.createElement('canvas') : - undefined; + return supportsWebGL2ForOffscreenCanvas ? undefined : + document.createElement('canvas'); } /** Base class for all MediaPipe Vision Tasks. */ diff --git a/mediapipe/web/graph_runner/BUILD b/mediapipe/web/graph_runner/BUILD index 5c12947a..6fc6379f 100644 --- a/mediapipe/web/graph_runner/BUILD +++ b/mediapipe/web/graph_runner/BUILD @@ -1,6 +1,7 @@ # The TypeScript graph runner used by all MediaPipe Web tasks. load("//mediapipe/framework/port:build_config.bzl", "mediapipe_ts_library") +load("@npm//@bazel/jasmine:index.bzl", "jasmine_node_test") package(default_visibility = [ "//mediapipe/tasks:internal", @@ -12,6 +13,7 @@ mediapipe_ts_library( ":graph_runner.ts", ], allow_unoptimized_namespaces = True, + deps = [":platform_utils"], ) mediapipe_ts_library( @@ -31,3 +33,26 @@ mediapipe_ts_library( allow_unoptimized_namespaces = True, deps = [":graph_runner_ts"], ) + +mediapipe_ts_library( + name = "platform_utils", + srcs = [ + "platform_utils.ts", + ], +) + +mediapipe_ts_library( + name = "platform_utils_test_lib", + testonly = True, + srcs = [ + "platform_utils.test.ts", + ], + deps = [":platform_utils"], +) + +jasmine_node_test( + name = "platform_utils_test", + deps = [ + ":platform_utils_test_lib", + ], +) diff --git a/mediapipe/web/graph_runner/graph_runner.ts b/mediapipe/web/graph_runner/graph_runner.ts index afa9c7eb..88c9d939 100644 --- a/mediapipe/web/graph_runner/graph_runner.ts +++ b/mediapipe/web/graph_runner/graph_runner.ts @@ -1,5 +1,6 @@ // Placeholder for internal dependency on assertTruthy // Placeholder for internal dependency on jsloader +import {isWebKit} from '../../web/graph_runner/platform_utils'; // Placeholder for internal dependency on trusted resource url // This file can serve as a common interface for most simple TypeScript @@ -216,13 +217,15 @@ export class GraphRunner { if (glCanvas !== undefined) { this.wasmModule.canvas = glCanvas; - } else if (typeof OffscreenCanvas !== 'undefined') { + } else if (typeof OffscreenCanvas !== 'undefined' && !isWebKit()) { // If no canvas is provided, assume Chrome/Firefox and just make an - // OffscreenCanvas for GPU processing. + // OffscreenCanvas for GPU processing. Note that we exclude Safari + // since it does not (yet) support WebGL for OffscreenCanvas. this.wasmModule.canvas = new OffscreenCanvas(1, 1); } else { - console.warn('OffscreenCanvas not detected and GraphRunner constructor ' - + 'glCanvas parameter is undefined. Creating backup canvas.'); + console.warn( + 'OffscreenCanvas not supported and GraphRunner constructor ' + + 'glCanvas parameter is undefined. Creating backup canvas.'); this.wasmModule.canvas = document.createElement('canvas'); } } diff --git a/mediapipe/web/graph_runner/platform_utils.test.ts b/mediapipe/web/graph_runner/platform_utils.test.ts new file mode 100644 index 00000000..080260cf --- /dev/null +++ b/mediapipe/web/graph_runner/platform_utils.test.ts @@ -0,0 +1,67 @@ +/** + * Copyright 2022 The MediaPipe Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import 'jasmine'; + +import {isWebKit} from '../../web/graph_runner/platform_utils'; + + +const DESKTOP_FIREFOX = + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0'; +const DESKTOP_SAFARI = + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Safari/604.1.38,gzip(gfe)'; +const IOS_SAFARI = + 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3'; +const IPAD_SAFARI = + 'Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10'; +const DESKTOP_CHROME = + 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/535.8 (KHTML, like Gecko) Chrome/40.0.1000.10 Safari/535.8'; +const IOS_CHROME = + 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_1_1 like Mac OS X; en-us) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/22.0.1194.0 Mobile/11E53 Safari/7534.48.3'; + + +describe('isWebKit()', () => { + const navigator = {userAgent: ''}; + + it('returns false for Firefox on desktop', () => { + navigator.userAgent = DESKTOP_FIREFOX; + expect(isWebKit(navigator as Navigator)).toBeFalse(); + }); + + it('returns true for Safari on desktop', () => { + navigator.userAgent = DESKTOP_SAFARI; + expect(isWebKit(navigator as Navigator)).toBeTrue(); + }); + + it('returns true for Safari on iOS', () => { + navigator.userAgent = IOS_SAFARI; + expect(isWebKit(navigator as Navigator)).toBeTrue(); + }); + + it('returns true for Safari on iPad', () => { + navigator.userAgent = IPAD_SAFARI; + expect(isWebKit(navigator as Navigator)).toBeTrue(); + }); + + it('returns false for Chrome on desktop', () => { + navigator.userAgent = DESKTOP_CHROME; + expect(isWebKit(navigator as Navigator)).toBeFalse(); + }); + + it('returns true for Chrome on iOS', () => { + navigator.userAgent = IOS_CHROME; + expect(isWebKit(navigator as Navigator)).toBeTrue(); + }); +}); diff --git a/mediapipe/web/graph_runner/platform_utils.ts b/mediapipe/web/graph_runner/platform_utils.ts new file mode 100644 index 00000000..59359a14 --- /dev/null +++ b/mediapipe/web/graph_runner/platform_utils.ts @@ -0,0 +1,23 @@ +/** + * Copyright 2023 The MediaPipe Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** Returns whether the underlying rendering engine is WebKit. */ +export function isWebKit(browser = navigator) { + const userAgent = browser.userAgent; + // Note that this returns true for Chrome on iOS (which is running WebKit) as + // it uses "CriOS". + return userAgent.includes('Safari') && !userAgent.includes('Chrome'); +}