From 1d954f4e96bda707e58d2ebf5bc1e72854bdbc87 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Mon, 17 Mar 2025 16:40:59 -0700 Subject: [PATCH] Revert "[skwasm] Dynamic Threading (#164748)" (#165350) This reverts commit b2a4a05683b95e9c08efafb3a0f17193915897d5. This has been causing issues when rolling to flutter/packages repo. See https://github.com/flutter/flutter/issues/165347. --- engine/src/build/config/compiler/BUILD.gn | 14 +- engine/src/build/toolchain/wasm.gni | 2 +- .../ci/licenses_golden/licenses_flutter | 8 + .../web_ui/dev/steps/copy_artifacts_step.dart | 4 + .../web_ui/flutter_js/src/skwasm_loader.js | 64 ++------ engine/src/flutter/lib/web_ui/skwasm/BUILD.gn | 151 +++++++++++------- .../skwasm/library_skwasm_multi_threaded.js | 57 +++++++ .../skwasm/library_skwasm_single_threaded.js | 31 ++++ .../web_ui/skwasm/library_skwasm_support.js | 55 +------ .../lib/web_ui/skwasm/skwasm_support.h | 1 - .../src/flutter/lib/web_ui/skwasm/surface.cpp | 40 +---- .../src/flutter/lib/web_ui/skwasm/surface.h | 4 +- .../flutter/lib/web_ui/skwasm/surface_mt.cpp | 31 ++++ .../flutter/lib/web_ui/skwasm/surface_st.cpp | 15 ++ .../flutter/third_party/canvaskit/BUILD.gn | 43 ++++- engine/src/flutter/web_sdk/BUILD.gn | 4 + 16 files changed, 314 insertions(+), 210 deletions(-) create mode 100644 engine/src/flutter/lib/web_ui/skwasm/library_skwasm_multi_threaded.js create mode 100644 engine/src/flutter/lib/web_ui/skwasm/library_skwasm_single_threaded.js create mode 100644 engine/src/flutter/lib/web_ui/skwasm/surface_mt.cpp create mode 100644 engine/src/flutter/lib/web_ui/skwasm/surface_st.cpp diff --git a/engine/src/build/config/compiler/BUILD.gn b/engine/src/build/config/compiler/BUILD.gn index de15cb053e4..15d61c09c0f 100644 --- a/engine/src/build/config/compiler/BUILD.gn +++ b/engine/src/build/config/compiler/BUILD.gn @@ -277,9 +277,9 @@ config("compiler") { } if (is_wasm) { - if (wasm_use_workers) { - cflags += [ "-sWASM_WORKERS=1" ] - ldflags += [ "-sWASM_WORKERS=1" ] + if (wasm_use_pthreads) { + cflags += [ "-pthread" ] + ldflags += [ "-pthread" ] } if (wasm_shared_memory) { cflags += [ "-sSHARED_MEMORY=1" ] ldflags += [ "-sSHARED_MEMORY=1" ] @@ -396,12 +396,12 @@ config("compiler") { # to say that it does. Define them here instead. defines += [ "HAVE_SYS_UIO_H" ] - # When Android requires new flags consider also editing the flags in - # the following locations. + # When Android requires new flags consider also editing the flags in + # the following locations. # Framework plugin_ffi template: packages/flutter_tools/templates/plugin_ffi/src.tmpl/CMakeLists.txt.tmpl # Example PR: https://github.com/flutter/flutter/pull/155508 - # Dart Lang JNI package: pkgs/jni/src/CMakeLists.txt - # Example PR: https://github.com/dart-lang/native/pull/1615 + # Dart Lang JNI package: pkgs/jni/src/CMakeLists.txt + # Example PR: https://github.com/dart-lang/native/pull/1615 ldflags += [ "-Wl,--no-undefined", "-Wl,--exclude-libs,ALL", diff --git a/engine/src/build/toolchain/wasm.gni b/engine/src/build/toolchain/wasm.gni index e2515b7f3b1..0da69d2874e 100644 --- a/engine/src/build/toolchain/wasm.gni +++ b/engine/src/build/toolchain/wasm.gni @@ -11,7 +11,7 @@ declare_args() { # The location of an activated embedded emsdk. emsdk_dir = rebase_path("//flutter/prebuilts/emsdk") - wasm_use_workers = false + wasm_use_pthreads = false wasm_shared_memory = false wasm_use_dwarf = false } diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index b3bc225507a..11f6d56dd25 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -42734,6 +42734,8 @@ ORIGIN: ../../../flutter/lib/web_ui/skwasm/filters.cpp + ../../../flutter/LICENS ORIGIN: ../../../flutter/lib/web_ui/skwasm/fonts.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/helpers.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/image.cpp + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/skwasm/library_skwasm_multi_threaded.js + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/skwasm/library_skwasm_single_threaded.js + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/library_skwasm_support.js + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/paint.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/path.cpp + ../../../flutter/LICENSE @@ -42743,6 +42745,8 @@ ORIGIN: ../../../flutter/lib/web_ui/skwasm/skwasm_support.h + ../../../flutter/L ORIGIN: ../../../flutter/lib/web_ui/skwasm/string.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/surface.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/surface.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/skwasm/surface_mt.cpp + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/skwasm/surface_st.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/text/line_metrics.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/text/paragraph.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/text/paragraph_builder.cpp + ../../../flutter/LICENSE @@ -45705,6 +45709,8 @@ FILE: ../../../flutter/lib/web_ui/skwasm/filters.cpp FILE: ../../../flutter/lib/web_ui/skwasm/fonts.cpp FILE: ../../../flutter/lib/web_ui/skwasm/helpers.h FILE: ../../../flutter/lib/web_ui/skwasm/image.cpp +FILE: ../../../flutter/lib/web_ui/skwasm/library_skwasm_multi_threaded.js +FILE: ../../../flutter/lib/web_ui/skwasm/library_skwasm_single_threaded.js FILE: ../../../flutter/lib/web_ui/skwasm/library_skwasm_support.js FILE: ../../../flutter/lib/web_ui/skwasm/paint.cpp FILE: ../../../flutter/lib/web_ui/skwasm/path.cpp @@ -45714,6 +45720,8 @@ FILE: ../../../flutter/lib/web_ui/skwasm/skwasm_support.h FILE: ../../../flutter/lib/web_ui/skwasm/string.cpp FILE: ../../../flutter/lib/web_ui/skwasm/surface.cpp FILE: ../../../flutter/lib/web_ui/skwasm/surface.h +FILE: ../../../flutter/lib/web_ui/skwasm/surface_mt.cpp +FILE: ../../../flutter/lib/web_ui/skwasm/surface_st.cpp FILE: ../../../flutter/lib/web_ui/skwasm/text/line_metrics.cpp FILE: ../../../flutter/lib/web_ui/skwasm/text/paragraph.cpp FILE: ../../../flutter/lib/web_ui/skwasm/text/paragraph_builder.cpp diff --git a/engine/src/flutter/lib/web_ui/dev/steps/copy_artifacts_step.dart b/engine/src/flutter/lib/web_ui/dev/steps/copy_artifacts_step.dart index 9178a0236a1..f825680c488 100644 --- a/engine/src/flutter/lib/web_ui/dev/steps/copy_artifacts_step.dart +++ b/engine/src/flutter/lib/web_ui/dev/steps/copy_artifacts_step.dart @@ -76,6 +76,7 @@ class CopyArtifactsStep implements PipelineStep { final String canvaskitSourceDirectory; final String canvaskitChromiumSourceDirectory; final String skwasmSourceDirectory; + final String skwasmStSourceDirectory; switch (source) { case LocalArtifactSource(:final mode): final buildDirectory = getBuildDirectoryForRuntimeMode(mode).path; @@ -83,6 +84,7 @@ class CopyArtifactsStep implements PipelineStep { canvaskitSourceDirectory = pathlib.join(buildDirectory, 'canvaskit'); canvaskitChromiumSourceDirectory = pathlib.join(buildDirectory, 'canvaskit_chromium'); skwasmSourceDirectory = pathlib.join(buildDirectory, 'skwasm'); + skwasmStSourceDirectory = pathlib.join(buildDirectory, 'skwasm_st'); case GcsArtifactSource(:final realm): final artifactsDirectory = (await _downloadArtifacts(realm)).path; @@ -94,6 +96,7 @@ class CopyArtifactsStep implements PipelineStep { 'chromium', ); skwasmSourceDirectory = pathlib.join(artifactsDirectory, 'canvaskit'); + skwasmStSourceDirectory = pathlib.join(artifactsDirectory, 'canvaskit'); } await environment.webTestsArtifactsDir.create(recursive: true); @@ -113,6 +116,7 @@ class CopyArtifactsStep implements PipelineStep { if (artifactDeps.skwasm) { copied.add('Skwasm'); await copyWasmLibrary('skwasm', skwasmSourceDirectory, 'canvaskit'); + await copyWasmLibrary('skwasm_st', skwasmStSourceDirectory, 'canvaskit'); } print('Copied artifacts: ${copied.join(', ')}'); } diff --git a/engine/src/flutter/lib/web_ui/flutter_js/src/skwasm_loader.js b/engine/src/flutter/lib/web_ui/flutter_js/src/skwasm_loader.js index 311999776c4..8a72f1cd77c 100644 --- a/engine/src/flutter/lib/web_ui/flutter_js/src/skwasm_loader.js +++ b/engine/src/flutter/lib/web_ui/flutter_js/src/skwasm_loader.js @@ -6,64 +6,24 @@ import { createWasmInstantiator } from "./instantiate_wasm.js"; import { resolveUrlWithSegments } from "./utils.js"; export const loadSkwasm = async (deps, config, browserEnvironment, baseUrl) => { - const rawSkwasmUrl = resolveUrlWithSegments(baseUrl, 'skwasm.js') + const fileStem = (browserEnvironment.crossOriginIsolated && !config.forceSingleThreadedSkwasm) ? "skwasm" : "skwasm_st"; + const rawSkwasmUrl = resolveUrlWithSegments(baseUrl, `${fileStem}.js`) let skwasmUrl = rawSkwasmUrl; if (deps.flutterTT.policy) { skwasmUrl = deps.flutterTT.policy.createScriptURL(skwasmUrl); } - const wasmInstantiator = createWasmInstantiator(resolveUrlWithSegments(baseUrl, 'skwasm.wasm')); + const wasmInstantiator = createWasmInstantiator(resolveUrlWithSegments(baseUrl, `${fileStem}.wasm`)); const skwasm = await import(skwasmUrl); return await skwasm.default({ - skwasmSingleThreaded: !browserEnvironment.crossOriginIsolated || config.forceSingleThreadedSkwasm, instantiateWasm: wasmInstantiator, - locateFile: (filename, scriptDirectory) => { - // The wasm workers API has a separate .ww.js file that bootstraps the - // web worker. However, it turns out this worker bootstrapper doesn't - // actually work with ES6 modules, which we have enabled. So we instead - // pass our own bootstrapper that loads skwasm.js as an ES6 module, and - // queues/flushes pending messages that were received during the - // asynchronous load. - if (filename.endsWith('.ww.js')) { - const url = resolveUrlWithSegments(baseUrl, filename); - return URL.createObjectURL(new Blob( - [` -"use strict"; - -let eventListener; -eventListener = (message) => { - const pendingMessages = []; - const data = message.data; - data["instantiateWasm"] = (info,receiveInstance) => { - const instance = new WebAssembly.Instance(data["wasm"], info); - return receiveInstance(instance, data["wasm"]) - }; - import(data.js).then(async (skwasm) => { - await skwasm.default(data); - - removeEventListener("message", eventListener); - for (const message of pendingMessages) { - dispatchEvent(message); - } - }); - console.log("removing initial listener"); - removeEventListener("message", eventListener); - eventListener = (message) => { - - pendingMessages.push(message); - }; - - addEventListener("message", eventListener); -}; -addEventListener("message", eventListener); -` - ], - { 'type': 'application/javascript' })); - } - return url; - }, - // Because of the above workaround, the worker is just a blob and - // can't locate the main script using a relative path to itself, - // so we pass the main script location in. - mainScriptUrlOrBlob: rawSkwasmUrl, + // When hosted via a CDN or some other url that is not the same + // origin as the main script of the page, we will fail to create + // a web worker with the bootstrapping script. This workaround will + // make sure that the worker JS can be loaded regardless of where + // it is hosted. + mainScriptUrlOrBlob: new Blob( + [`import '${skwasmUrl}'`], + { 'type': 'application/javascript' }, + ), }); } diff --git a/engine/src/flutter/lib/web_ui/skwasm/BUILD.gn b/engine/src/flutter/lib/web_ui/skwasm/BUILD.gn index 1e4221cf71a..2d9063d5f59 100644 --- a/engine/src/flutter/lib/web_ui/skwasm/BUILD.gn +++ b/engine/src/flutter/lib/web_ui/skwasm/BUILD.gn @@ -4,69 +4,96 @@ import("//build/toolchain/wasm.gni") -wasm_lib("skwasm") { - public_configs = [ "//flutter:config" ] +template("skwasm_variant") { + wasm_lib(target_name) { + public_configs = [ "//flutter:config" ] - sources = [ - "canvas.cpp", - "contour_measure.cpp", - "data.cpp", - "export.h", - "filters.cpp", - "fonts.cpp", - "helpers.h", - "image.cpp", - "paint.cpp", - "path.cpp", - "picture.cpp", - "shaders.cpp", - "skwasm_support.h", - "string.cpp", - "surface.cpp", - "text/line_metrics.cpp", - "text/paragraph.cpp", - "text/paragraph_builder.cpp", - "text/paragraph_style.cpp", - "text/strut_style.cpp", - "text/text_style.cpp", - "vertices.cpp", - "wrappers.h", - ] - - cflags = [ "-mreference-types" ] - - ldflags = [ - "-std=c++20", - "-lGL", - "-sUSE_WEBGL2=1", - "-sMAX_WEBGL_VERSION=2", - "-sOFFSCREENCANVAS_SUPPORT", - "-sALLOW_MEMORY_GROWTH", - "-sALLOW_TABLE_GROWTH", - "-lexports.js", - "-sEXPORTED_FUNCTIONS=[stackAlloc]", - "-sEXPORTED_RUNTIME_METHODS=[addFunction,wasmExports,wasmMemory,stackAlloc]", - "-sINCOMING_MODULE_JS_API=[instantiateWasm,locateFile,noExitRuntime,mainScriptUrlOrBlob,wasmMemory,wasm,skwasmSingleThreaded]", - "-sUSE_ES6_IMPORT_META=0", - "--js-library", - rebase_path("library_skwasm_support.js"), - ] - - inputs = [ rebase_path("library_skwasm_support.js") ] - - if (is_debug) { - ldflags += [ - "-sASSERTIONS=1", - "-sGL_ASSERTIONS=1", - "-sSTACK_OVERFLOW_CHECK=2", + sources = [ + "canvas.cpp", + "contour_measure.cpp", + "data.cpp", + "export.h", + "filters.cpp", + "fonts.cpp", + "helpers.h", + "image.cpp", + "paint.cpp", + "path.cpp", + "picture.cpp", + "shaders.cpp", + "skwasm_support.h", + "string.cpp", + "surface.cpp", + "text/line_metrics.cpp", + "text/paragraph.cpp", + "text/paragraph_builder.cpp", + "text/paragraph_style.cpp", + "text/strut_style.cpp", + "text/text_style.cpp", + "vertices.cpp", + "wrappers.h", ] - } else { - ldflags += [ "--closure=1" ] - } - deps = [ - "//flutter/skia", - "//flutter/skia/modules/skparagraph", - "//flutter/skia/modules/skunicode", - ] + cflags = [ "-mreference-types" ] + + ldflags = [ + "-std=c++20", + "-lGL", + "-sUSE_WEBGL2=1", + "-sMAX_WEBGL_VERSION=2", + "-sOFFSCREENCANVAS_SUPPORT", + "-sALLOW_MEMORY_GROWTH", + "-sALLOW_TABLE_GROWTH", + "-lexports.js", + "-sEXPORTED_FUNCTIONS=[stackAlloc]", + "-sEXPORTED_RUNTIME_METHODS=[addFunction,wasmExports,wasmMemory,stackAlloc]", + "-sINCOMING_MODULE_JS_API=[instantiateWasm,noExitRuntime,mainScriptUrlOrBlob]", + "-sUSE_ES6_IMPORT_META=0", + "--js-library", + rebase_path("library_skwasm_support.js"), + ] + + inputs = [ rebase_path("library_skwasm_support.js") ] + + if (invoker.multi_threaded) { + sources += [ "surface_mt.cpp" ] + ldflags += [ + "-sPTHREAD_POOL_SIZE=1", + "-Wno-pthreads-mem-growth", + "--js-library", + rebase_path("library_skwasm_multi_threaded.js"), + ] + inputs += [ rebase_path("library_skwasm_multi_threaded.js") ] + } else { + sources += [ "surface_st.cpp" ] + ldflags += [ + "--js-library", + rebase_path("library_skwasm_single_threaded.js"), + ] + inputs += [ rebase_path("library_skwasm_single_threaded.js") ] + } + + if (is_debug) { + ldflags += [ + "-sASSERTIONS=1", + "-sGL_ASSERTIONS=1", + ] + } else { + ldflags += [ "--closure=1" ] + } + + deps = [ + "//flutter/skia", + "//flutter/skia/modules/skparagraph", + "//flutter/skia/modules/skunicode", + ] + } +} + +skwasm_variant("skwasm") { + multi_threaded = true +} + +skwasm_variant("skwasm_st") { + multi_threaded = false } diff --git a/engine/src/flutter/lib/web_ui/skwasm/library_skwasm_multi_threaded.js b/engine/src/flutter/lib/web_ui/skwasm/library_skwasm_multi_threaded.js new file mode 100644 index 00000000000..237ccf38bed --- /dev/null +++ b/engine/src/flutter/lib/web_ui/skwasm/library_skwasm_multi_threaded.js @@ -0,0 +1,57 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file adds JavaScript APIs that are accessible to the C++ layer. +// See: https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#implement-a-c-api-in-javascript + +mergeInto(LibraryManager.library, { + $skwasm_threading_setup__postset: 'skwasm_threading_setup();', + $skwasm_threading_setup: function() { + // This value represents the difference between the time origin of the main + // thread and whichever web worker this code is running on. This is so that + // when we report frame timings, that they are in the same time domain + // regardless of whether they are captured on the main thread or the web + // worker. + let timeOriginDelta = 0; + skwasm_registerMessageListener = function(threadId, listener) { + const eventListener = function({data}) { + const skwasmMessage = data.skwasmMessage; + if (!skwasmMessage) { + return; + } + if (skwasmMessage == 'syncTimeOrigin') { + timeOriginDelta = performance.timeOrigin - data.timeOrigin; + return; + } + listener(data); + }; + if (!threadId) { + addEventListener("message", eventListener); + } else { + PThread.pthreads[threadId].addEventListener("message", eventListener); + PThread.pthreads[threadId].postMessage({ + skwasmMessage: 'syncTimeOrigin', + timeOrigin: performance.timeOrigin, + }); + } + }; + skwasm_getCurrentTimestamp = function() { + return performance.now() + timeOriginDelta; + }; + skwasm_postMessage = function(message, transfers, threadId) { + if (threadId) { + PThread.pthreads[threadId].postMessage(message, transfers); + } else { + postMessage(message, transfers); + } + }; + }, + $skwasm_threading_setup__deps: ['$skwasm_registerMessageListener', '$skwasm_getCurrentTimestamp', '$skwasm_postMessage'], + $skwasm_registerMessageListener: function() {}, + $skwasm_registerMessageListener__deps: ['$skwasm_threading_setup'], + $skwasm_getCurrentTimestamp: function () {}, + $skwasm_getCurrentTimestamp__deps: ['$skwasm_threading_setup'], + $skwasm_postMessage: function () {}, + $skwasm_postMessage__deps: ['$skwasm_threading_setup'], +}); diff --git a/engine/src/flutter/lib/web_ui/skwasm/library_skwasm_single_threaded.js b/engine/src/flutter/lib/web_ui/skwasm/library_skwasm_single_threaded.js new file mode 100644 index 00000000000..6b6221ad885 --- /dev/null +++ b/engine/src/flutter/lib/web_ui/skwasm/library_skwasm_single_threaded.js @@ -0,0 +1,31 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file adds JavaScript APIs that are accessible to the C++ layer. +// See: https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#implement-a-c-api-in-javascript + +mergeInto(LibraryManager.library, { + $skwasm_threading_setup__postset: 'skwasm_threading_setup();', + $skwasm_threading_setup: function() { + let messageListener; + skwasm_registerMessageListener = function(threadId, listener) { + messageListener = listener; + }; + skwasm_getCurrentTimestamp = function() { + return performance.now(); + }; + skwasm_postMessage = function(message, transfers, threadId) { + queueMicrotask(() => { + messageListener(message); + }) + }; + }, + $skwasm_threading_setup__deps: ['$skwasm_registerMessageListener', '$skwasm_getCurrentTimestamp', '$skwasm_postMessage'], + $skwasm_registerMessageListener: function() {}, + $skwasm_registerMessageListener__deps: ['$skwasm_threading_setup'], + $skwasm_getCurrentTimestamp: function () {}, + $skwasm_getCurrentTimestamp__deps: ['$skwasm_threading_setup'], + $skwasm_postMessage: function () {}, + $skwasm_postMessage__deps: ['$skwasm_threading_setup'], +}); diff --git a/engine/src/flutter/lib/web_ui/skwasm/library_skwasm_support.js b/engine/src/flutter/lib/web_ui/skwasm/library_skwasm_support.js index 702724e71d8..db107d668da 100644 --- a/engine/src/flutter/lib/web_ui/skwasm/library_skwasm_support.js +++ b/engine/src/flutter/lib/web_ui/skwasm/library_skwasm_support.js @@ -8,51 +8,9 @@ mergeInto(LibraryManager.library, { $skwasm_support_setup__postset: 'skwasm_support_setup();', $skwasm_support_setup: function() { - // This value represents the difference between the time origin of the main - // thread and whichever web worker this code is running on. This is so that - // when we report frame timings, that they are in the same time domain - // regardless of whether they are captured on the main thread or the web - // worker. - let timeOriginDelta = 0; - skwasm_registerMessageListener = function(threadId, listener) { - const eventListener = function({data}) { - const skwasmMessage = data.skwasmMessage; - if (!skwasmMessage) { - return; - } - if (skwasmMessage == 'syncTimeOrigin') { - timeOriginDelta = performance.timeOrigin - data.timeOrigin; - return; - } - listener(data); - }; - if (!threadId) { - addEventListener("message", eventListener); - } else { - _wasmWorkers[threadId].addEventListener("message", eventListener); - _wasmWorkers[threadId].postMessage({ - skwasmMessage: 'syncTimeOrigin', - timeOrigin: performance.timeOrigin, - }); - } - }; - skwasm_getCurrentTimestamp = function() { - return performance.now() + timeOriginDelta; - }; - skwasm_postMessage = function(message, transfers, threadId) { - if (threadId) { - _wasmWorkers[threadId].postMessage(message, transfers); - } else { - postMessage(message, transfers); - } - }; - const handleToCanvasMap = new Map(); const associatedObjectsMap = new Map(); - _skwasm_isSingleThreaded = function() { - return Module["skwasmSingleThreaded"]; - }; _skwasm_setAssociatedObjectOnThread = function(threadId, pointer, object) { skwasm_postMessage({ skwasmMessage: 'setAssociatedObject', @@ -217,22 +175,15 @@ mergeInto(LibraryManager.library, { }); } }, - $skwasm_registerMessageListener: function() {}, - $skwasm_registerMessageListener__deps: ['$skwasm_support_setup'], - $skwasm_getCurrentTimestamp: function () {}, - $skwasm_getCurrentTimestamp__deps: ['$skwasm_support_setup'], - $skwasm_postMessage: function () {}, - $skwasm_postMessage__deps: ['$skwasm_support_setup'], - skwasm_isSingleThreaded: function() {}, - skwasm_isSingleThreaded__deps: ['$skwasm_support_setup'], + $skwasm_support_setup__deps: [ '$skwasm_threading_setup'], skwasm_setAssociatedObjectOnThread: function () {}, - skwasm_setAssociatedObjectOnThread__deps: ['$skwasm_support_setup', '$skwasm_postMessage'], + skwasm_setAssociatedObjectOnThread__deps: ['$skwasm_support_setup'], skwasm_getAssociatedObject: function () {}, skwasm_getAssociatedObject__deps: ['$skwasm_support_setup'], skwasm_disposeAssociatedObjectOnThread: function () {}, skwasm_disposeAssociatedObjectOnThread__deps: ['$skwasm_support_setup'], skwasm_connectThread: function() {}, - skwasm_connectThread__deps: ['$skwasm_support_setup', '$skwasm_registerMessageListener', '$skwasm_getCurrentTimestamp'], + skwasm_connectThread__deps: ['$skwasm_support_setup'], skwasm_dispatchRenderPictures: function() {}, skwasm_dispatchRenderPictures__deps: ['$skwasm_support_setup'], skwasm_createOffscreenCanvas: function () {}, diff --git a/engine/src/flutter/lib/web_ui/skwasm/skwasm_support.h b/engine/src/flutter/lib/web_ui/skwasm/skwasm_support.h index 54d6e5548db..57dff635b5d 100644 --- a/engine/src/flutter/lib/web_ui/skwasm/skwasm_support.h +++ b/engine/src/flutter/lib/web_ui/skwasm/skwasm_support.h @@ -13,7 +13,6 @@ using SkwasmObject = __externref_t; extern "C" { -extern bool skwasm_isSingleThreaded(); extern void skwasm_setAssociatedObjectOnThread(unsigned long threadId, void* pointer, SkwasmObject object); diff --git a/engine/src/flutter/lib/web_ui/skwasm/surface.cpp b/engine/src/flutter/lib/web_ui/skwasm/surface.cpp index cb91b10728e..da0f41c9ce7 100644 --- a/engine/src/flutter/lib/web_ui/skwasm/surface.cpp +++ b/engine/src/flutter/lib/web_ui/skwasm/surface.cpp @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "surface.h" -#include #include #include "skwasm_support.h" @@ -15,23 +14,6 @@ using namespace Skwasm; -Surface::Surface() { - if (skwasm_isSingleThreaded()) { - skwasm_connectThread(0); - } else { - assert(emscripten_is_main_browser_thread()); - - _thread = emscripten_malloc_wasm_worker(65536); - emscripten_wasm_worker_post_function_v(_thread, []() { - // Listen to the main thread from the worker - skwasm_connectThread(0); - }); - - // Listen to messages from the worker - skwasm_connectThread(_thread); - } -} - // Worker thread only void Surface::dispose() { delete this; @@ -76,8 +58,16 @@ void Surface::setCallbackHandler(CallbackHandler* callbackHandler) { _callbackHandler = callbackHandler; } +// Worker thread only +void Surface::_runWorker() { + _init(); + emscripten_exit_with_live_runtime(); +} + // Worker thread only void Surface::_init() { + // Listen to messages from the main thread + skwasm_connectThread(0); _glContext = skwasm_createOffscreenCanvas(256, 256); if (!_glContext) { printf("Failed to create context!\n"); @@ -105,8 +95,6 @@ void Surface::_init() { emscripten_glGetIntegerv(GL_SAMPLES, &_sampleCount); emscripten_glGetIntegerv(GL_STENCIL_BITS, &_stencil); - - _isInitialized = true; } // Worker thread only @@ -134,10 +122,6 @@ void Surface::renderPicturesOnWorker(sk_sp* pictures, int pictureCount, uint32_t callbackId, double rasterStart) { - if (!_isInitialized) { - _init(); - } - // This is populated by the `captureImageBitmap` call the first time it is // passed in. SkwasmObject imageBitmapArray = __builtin_wasm_ref_null_extern(); @@ -163,10 +147,6 @@ void Surface::renderPicturesOnWorker(sk_sp* pictures, void Surface::rasterizeImageOnWorker(SkImage* image, ImageByteFormat format, uint32_t callbackId) { - if (!_isInitialized) { - _init(); - } - // We handle PNG encoding with browser APIs so that we can omit libpng from // skia to save binary size. assert(format != ImageByteFormat::png); @@ -301,7 +281,3 @@ SKWASM_EXPORT void surface_onRasterizeComplete(Surface* surface, uint32_t callbackId) { surface->onRasterizeComplete(callbackId, data); } - -SKWASM_EXPORT bool skwasm_isMultiThreaded() { - return !skwasm_isSingleThreaded(); -} diff --git a/engine/src/flutter/lib/web_ui/skwasm/surface.h b/engine/src/flutter/lib/web_ui/skwasm/surface.h index f793fbb2fcc..3ef5dbfadcf 100644 --- a/engine/src/flutter/lib/web_ui/skwasm/surface.h +++ b/engine/src/flutter/lib/web_ui/skwasm/surface.h @@ -75,10 +75,12 @@ class Surface { uint32_t callbackId); private: + void _runWorker(); void _init(); void _resizeCanvasToFit(int width, int height); void _recreateSurface(); + std::string _canvasID; CallbackHandler* _callbackHandler = nullptr; uint32_t _currentCallbackId = 0; @@ -93,8 +95,6 @@ class Surface { GrGLint _stencil; pthread_t _thread; - - bool _isInitialized = false; }; } // namespace Skwasm diff --git a/engine/src/flutter/lib/web_ui/skwasm/surface_mt.cpp b/engine/src/flutter/lib/web_ui/skwasm/surface_mt.cpp new file mode 100644 index 00000000000..cd02dc6d9bb --- /dev/null +++ b/engine/src/flutter/lib/web_ui/skwasm/surface_mt.cpp @@ -0,0 +1,31 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "surface.h" + +#include "skwasm_support.h" + +using namespace Skwasm; + +Surface::Surface() { + assert(emscripten_is_main_browser_thread()); + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + pthread_create( + &_thread, &attr, + [](void* context) -> void* { + static_cast(context)->_runWorker(); + return nullptr; + }, + this); + // Listen to messages from the worker + skwasm_connectThread(_thread); +} + +SKWASM_EXPORT bool skwasm_isMultiThreaded() { + return true; +} diff --git a/engine/src/flutter/lib/web_ui/skwasm/surface_st.cpp b/engine/src/flutter/lib/web_ui/skwasm/surface_st.cpp new file mode 100644 index 00000000000..86c7b6b22ee --- /dev/null +++ b/engine/src/flutter/lib/web_ui/skwasm/surface_st.cpp @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "surface.h" + +using namespace Skwasm; + +Surface::Surface() : _thread(0) { + _init(); +} + +SKWASM_EXPORT bool skwasm_isMultiThreaded() { + return false; +} diff --git a/engine/src/flutter/third_party/canvaskit/BUILD.gn b/engine/src/flutter/third_party/canvaskit/BUILD.gn index 7fb119bc9e8..417cba35173 100644 --- a/engine/src/flutter/third_party/canvaskit/BUILD.gn +++ b/engine/src/flutter/third_party/canvaskit/BUILD.gn @@ -88,7 +88,31 @@ wasm_toolchain("skwasm") { skia_use_libpng_encode = false # skwasm is multithreaded - wasm_use_workers = true + wasm_use_pthreads = true + wasm_prioritize_size = true + } +} + +wasm_toolchain("skwasm_st") { + extra_toolchain_args = { + # In Chromium browsers, we can use the browser's APIs to get the necessary + # ICU data. + skia_use_icu = false + skia_use_client_icu = true + skia_icu_bidi_third_party_dir = "//flutter/third_party/canvaskit/icu_bidi" + + skia_use_libjpeg_turbo_decode = false + skia_use_libpng_decode = false + skia_use_libwebp_decode = false + + # We use OffscreenCanvas to produce PNG data instead of skia + skia_use_no_png_encode = true + skia_use_libpng_encode = false + + # skwasm_st doesn't use pthreads, but does pass the shared memory flag in order + # to be compatible with the way app modules import the memory object. + wasm_use_pthreads = false + wasm_shared_memory = true wasm_prioritize_size = true } } @@ -109,3 +133,20 @@ copy("skwasm_group") { } outputs = [ "$root_out_dir/flutter_web_sdk/canvaskit/{{source_file_part}}" ] } + +copy("skwasm_st_group") { + visibility = [ "//flutter/web_sdk:*" ] + public_deps = [ "//flutter/lib/web_ui/skwasm:skwasm_st(:skwasm_st)" ] + + sources = [ + "$root_out_dir/skwasm_st/skwasm_st.js", + "$root_out_dir/skwasm_st/skwasm_st.js.symbols", + "$root_out_dir/skwasm_st/skwasm_st.wasm", + ] + if (is_debug) { + if (!wasm_use_dwarf) { + sources += [ "$root_out_dir/skwasm_st/skwasm_st.wasm.map" ] + } + } + outputs = [ "$root_out_dir/flutter_web_sdk/canvaskit/{{source_file_part}}" ] +} diff --git a/engine/src/flutter/web_sdk/BUILD.gn b/engine/src/flutter/web_sdk/BUILD.gn index 67a77da0005..80f90672826 100644 --- a/engine/src/flutter/web_sdk/BUILD.gn +++ b/engine/src/flutter/web_sdk/BUILD.gn @@ -414,6 +414,7 @@ if (!is_fuchsia) { "//flutter/third_party/canvaskit:canvaskit_chromium_group", "//flutter/third_party/canvaskit:canvaskit_group", "//flutter/third_party/canvaskit:skwasm_group", + "//flutter/third_party/canvaskit:skwasm_st_group", ] } deps += [ "//flutter/lib/web_ui/flutter_js" ] @@ -438,6 +439,9 @@ if (!is_fuchsia) { "$root_out_dir/flutter_web_sdk/canvaskit/skwasm.js", "$root_out_dir/flutter_web_sdk/canvaskit/skwasm.js.symbols", "$root_out_dir/flutter_web_sdk/canvaskit/skwasm.wasm", + "$root_out_dir/flutter_web_sdk/canvaskit/skwasm_st.js", + "$root_out_dir/flutter_web_sdk/canvaskit/skwasm_st.js.symbols", + "$root_out_dir/flutter_web_sdk/canvaskit/skwasm_st.wasm", ] }