From 583117697e9f401fa71a89f3f048c9a8566b1a2a Mon Sep 17 00:00:00 2001 From: Jake Archibald Date: Mon, 9 Jan 2023 10:42:14 +0000 Subject: [PATCH] Ensure browser supports workers-in-workers (#1325) Fixes #1324 --- client-tsconfig.json | 3 ++- generic-tsconfig.json | 3 ++- package-lock.json | 1 + package.json | 6 ++---- rollup.config.js | 1 + src/features/encoders/avif/worker/avifEncode.ts | 4 ++-- src/features/encoders/jxl/worker/jxlEncode.ts | 5 +++-- .../encoders/oxiPNG/worker/oxipngEncode.ts | 4 ++-- src/features/encoders/wp2/worker/wp2Encode.ts | 5 +++-- src/sw/to-cache.ts | 5 +++-- src/worker-shared/supports-wasm-threads.ts | 17 +++++++++++++++++ worker-tsconfig.json | 1 + 12 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 src/worker-shared/supports-wasm-threads.ts diff --git a/client-tsconfig.json b/client-tsconfig.json index 676e06ee..4e30dd80 100644 --- a/client-tsconfig.json +++ b/client-tsconfig.json @@ -14,6 +14,7 @@ // for comlink "src/features/**/worker/**/*", "src/features-worker/**/*", - "src/features/worker-utils/**/*" + "src/features/worker-utils/**/*", + "src/worker-shared/**/*" ] } diff --git a/generic-tsconfig.json b/generic-tsconfig.json index 63be8ac0..af9a6c5e 100644 --- a/generic-tsconfig.json +++ b/generic-tsconfig.json @@ -17,7 +17,8 @@ "static-build/*": ["src/static-build/*"], "client/*": ["src/client/*"], "shared/*": ["src/shared/*"], - "features/*": ["src/features/*"] + "features/*": ["src/features/*"], + "worker-shared/*": ["src/worker-shared/*"] } } } diff --git a/package-lock.json b/package-lock.json index 929a9b81..f643147e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "squoosh", "version": "2.0.0", "license": "apache-2.0", "dependencies": { diff --git a/package.json b/package.json index 81edc7d9..c8eb3078 100644 --- a/package.json +++ b/package.json @@ -46,14 +46,12 @@ "rollup-plugin-terser": "^7.0.2", "serve": "^11.3.2", "typescript": "^4.4.4", - "which": "^2.0.2" + "which": "^2.0.2", + "wasm-feature-detect": "^1.2.11" }, "lint-staged": { "*.{js,css,json,md,ts,tsx}": "prettier --write", "*.{c,h,cpp,hpp}": "clang-format -i", "*.rs": "rustfmt" - }, - "dependencies": { - "wasm-feature-detect": "^1.2.11" } } diff --git a/rollup.config.js b/rollup.config.js index 394b4277..f4a57384 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -86,6 +86,7 @@ export default async function ({ watch }) { 'src/features-worker', 'src/features-worker-worker-bridge', 'src/sw', + 'src/worker-shared', 'codecs', ]), urlPlugin(), diff --git a/src/features/encoders/avif/worker/avifEncode.ts b/src/features/encoders/avif/worker/avifEncode.ts index 94f828b7..a8ec4d76 100644 --- a/src/features/encoders/avif/worker/avifEncode.ts +++ b/src/features/encoders/avif/worker/avifEncode.ts @@ -13,12 +13,12 @@ import type { AVIFModule } from 'codecs/avif/enc/avif_enc'; import type { EncodeOptions } from '../shared/meta'; import { initEmscriptenModule } from 'features/worker-utils'; -import { threads } from 'wasm-feature-detect'; +import checkThreadsSupport from 'worker-shared/supports-wasm-threads'; let emscriptenModule: Promise; async function init() { - if (await threads()) { + if (await checkThreadsSupport()) { const avifEncoder = await import('codecs/avif/enc/avif_enc_mt'); return initEmscriptenModule(avifEncoder.default); } diff --git a/src/features/encoders/jxl/worker/jxlEncode.ts b/src/features/encoders/jxl/worker/jxlEncode.ts index 02a0f19c..c8c08521 100644 --- a/src/features/encoders/jxl/worker/jxlEncode.ts +++ b/src/features/encoders/jxl/worker/jxlEncode.ts @@ -14,12 +14,13 @@ import type { JXLModule } from 'codecs/jxl/enc/jxl_enc'; import type { EncodeOptions } from '../shared/meta'; import { initEmscriptenModule } from 'features/worker-utils'; -import { threads, simd } from 'wasm-feature-detect'; +import { simd } from 'wasm-feature-detect'; +import checkThreadsSupport from 'worker-shared/supports-wasm-threads'; let emscriptenModule: Promise; async function init() { - if (await threads()) { + if (await checkThreadsSupport()) { if (await simd()) { const jxlEncoder = await import('codecs/jxl/enc/jxl_enc_mt_simd'); return initEmscriptenModule(jxlEncoder.default); diff --git a/src/features/encoders/oxiPNG/worker/oxipngEncode.ts b/src/features/encoders/oxiPNG/worker/oxipngEncode.ts index e047ed77..110ba7d8 100644 --- a/src/features/encoders/oxiPNG/worker/oxipngEncode.ts +++ b/src/features/encoders/oxiPNG/worker/oxipngEncode.ts @@ -11,7 +11,7 @@ * limitations under the License. */ import { EncodeOptions } from '../shared/meta'; -import { threads } from 'wasm-feature-detect'; +import checkThreadsSupport from 'worker-shared/supports-wasm-threads'; async function initMT() { const { @@ -39,7 +39,7 @@ export default async function encode( options: EncodeOptions, ): Promise { if (!wasmReady) { - wasmReady = threads().then((hasThreads: boolean) => + wasmReady = checkThreadsSupport().then((hasThreads: boolean) => hasThreads ? initMT() : initST(), ); } diff --git a/src/features/encoders/wp2/worker/wp2Encode.ts b/src/features/encoders/wp2/worker/wp2Encode.ts index f6d9c0fd..ecfc5568 100644 --- a/src/features/encoders/wp2/worker/wp2Encode.ts +++ b/src/features/encoders/wp2/worker/wp2Encode.ts @@ -14,12 +14,13 @@ import type { WP2Module } from 'codecs/wp2/enc/wp2_enc'; import type { EncodeOptions } from '../shared/meta'; import { initEmscriptenModule } from 'features/worker-utils'; -import { threads, simd } from 'wasm-feature-detect'; +import { simd } from 'wasm-feature-detect'; +import checkThreadsSupport from 'worker-shared/supports-wasm-threads'; let emscriptenModule: Promise; async function init() { - if (await threads()) { + if (await checkThreadsSupport()) { if (await simd()) { const wp2Encoder = await import('codecs/wp2/enc/wp2_enc_mt_simd'); return initEmscriptenModule(wp2Encoder.default); diff --git a/src/sw/to-cache.ts b/src/sw/to-cache.ts index 4f90bb4f..49afaf07 100644 --- a/src/sw/to-cache.ts +++ b/src/sw/to-cache.ts @@ -1,6 +1,7 @@ -import { threads, simd } from 'wasm-feature-detect'; +import { simd } from 'wasm-feature-detect'; import webpDataUrl from 'data-url:./tiny.webp'; import avifDataUrl from 'data-url:./tiny.avif'; +import checkThreadsSupport from 'worker-shared/supports-wasm-threads'; // Give TypeScript the correct global. declare var self: ServiceWorkerGlobalScope; @@ -84,7 +85,7 @@ export const initial = ['/', ...initialJs]; export const theRest = (async () => { const [supportsThreads, supportsSimd, supportsWebP, supportsAvif] = await Promise.all([ - threads(), + checkThreadsSupport(), simd(), ...[webpDataUrl, avifDataUrl].map(async (dataUrl) => { if (!self.createImageBitmap) return false; diff --git a/src/worker-shared/supports-wasm-threads.ts b/src/worker-shared/supports-wasm-threads.ts new file mode 100644 index 00000000..d3855d0d --- /dev/null +++ b/src/worker-shared/supports-wasm-threads.ts @@ -0,0 +1,17 @@ +import { threads } from 'wasm-feature-detect'; + +export default async function checkThreadsSupport() { + const supportsWasmThreads = await threads(); + if (!supportsWasmThreads) return false; + + // Safari 16 shipped with WASM threads support, but it didn't ship with nested workers support. + // This meant Squoosh failed in Safari 16, since we call our wasm from inside a worker to begin with. + + // Right now, this check is only run from a worker. + // More implementation is needed to run it from a page. + if (!('importScripts' in self)) { + throw Error('Not implemented'); + } + + return 'Worker' in self; +} diff --git a/worker-tsconfig.json b/worker-tsconfig.json index b605e757..6dfcee29 100644 --- a/worker-tsconfig.json +++ b/worker-tsconfig.json @@ -8,6 +8,7 @@ "src/features/**/shared/**/*", "src/features/worker-utils/**/*", "src/features-worker/**/*", + "src/worker-shared/**/*", "src/sw/**/*" ] }