Add AVIF thread support

This commit is contained in:
Surma
2021-06-25 00:11:25 +01:00
parent d12b040bd3
commit d4056026fb
7 changed files with 99 additions and 4 deletions

View File

@@ -0,0 +1,49 @@
/**
* Copyright 2020 Google Inc. 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 { promises as fs } from 'fs';
import { basename } from 'path';
const defaultOpts = {
prefix: 'chunk-url',
};
export default function chunkPlugin(opts) {
opts = { ...defaultOpts, ...opts };
const prefix = opts.prefix + ':';
return {
name: 'chunk-plugin',
async resolveId(id, importer) {
if (!id.startsWith(prefix)) return;
const realId = id.slice(prefix.length);
const resolveResult = await this.resolve(realId, importer);
if (!resolveResult) {
throw Error(`Cannot find ${realId}`);
}
return prefix + resolveResult.id;
},
async load(id) {
if (!id.startsWith(prefix)) return;
const realId = id.slice(prefix.length);
const source = await fs.readFile(realId);
this.addWatchFile(realId);
return `export default import.meta.ROLLUP_FILE_URL_${this.emitFile({
type: 'chunk',
source,
id: realId,
})}`;
},
};
}

View File

@@ -9,6 +9,7 @@
"version": "0.3.1", "version": "0.3.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"wasm-feature-detect": "^1.2.11",
"web-streams-polyfill": "^3.0.3" "web-streams-polyfill": "^3.0.3"
}, },
"devDependencies": { "devDependencies": {
@@ -22,6 +23,9 @@
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"typescript": "^4.1.3", "typescript": "^4.1.3",
"which": "^2.0.2" "which": "^2.0.2"
},
"engines": {
"node": " ^12.5.0 || ^14.0.0 || ^16.0.0 "
} }
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
@@ -2055,6 +2059,11 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/wasm-feature-detect": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.11.tgz",
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w=="
},
"node_modules/web-streams-polyfill": { "node_modules/web-streams-polyfill": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.0.3.tgz", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.0.3.tgz",
@@ -3948,6 +3957,11 @@
"integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==",
"dev": true "dev": true
}, },
"wasm-feature-detect": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.11.tgz",
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w=="
},
"web-streams-polyfill": { "web-streams-polyfill": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.0.3.tgz", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.0.3.tgz",

View File

@@ -22,6 +22,7 @@
"node": " ^12.5.0 || ^14.0.0 || ^16.0.0 " "node": " ^12.5.0 || ^14.0.0 || ^16.0.0 "
}, },
"dependencies": { "dependencies": {
"wasm-feature-detect": "^1.2.11",
"web-streams-polyfill": "^3.0.3" "web-streams-polyfill": "^3.0.3"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -2,6 +2,7 @@ import resolve from '@rollup/plugin-node-resolve';
import cjs from '@rollup/plugin-commonjs'; import cjs from '@rollup/plugin-commonjs';
import simpleTS from './lib/simple-ts'; import simpleTS from './lib/simple-ts';
import asset from './lib/asset-plugin.js'; import asset from './lib/asset-plugin.js';
import chunk from './lib/chunk-plugin.js';
import json from './lib/json-plugin.js'; import json from './lib/json-plugin.js';
import autojson from './lib/autojson-plugin.js'; import autojson from './lib/autojson-plugin.js';
import { getBabelOutputPlugin } from '@rollup/plugin-babel'; import { getBabelOutputPlugin } from '@rollup/plugin-babel';
@@ -18,6 +19,7 @@ export default {
plugins: [ plugins: [
resolve(), resolve(),
cjs(), cjs(),
chunk(),
asset(), asset(),
autojson(), autojson(),
json(), json(),

View File

@@ -1,5 +1,13 @@
import { promises as fsp } from 'fs'; import { promises as fsp } from 'fs';
import { instantiateEmscriptenWasm, pathify } from './emscripten-utils.js'; import { instantiateEmscriptenWasm, pathify } from './emscripten-utils.js';
import { threads } from 'wasm-feature-detect';
import { cpus } from 'os';
// We use `navigator.hardwareConcurrency` for Emscriptens pthread pool size.
// This is the only workaround I can get working without crying.
(globalThis as any).navigator = {
hardwareConcurrency: cpus().length,
};
interface RotateModuleInstance { interface RotateModuleInstance {
exports: { exports: {
@@ -47,6 +55,9 @@ import webpDecWasm from 'asset-url:../../codecs/webp/dec/webp_node_dec.wasm';
// AVIF // AVIF
import avifEnc from '../../codecs/avif/enc/avif_node_enc.js'; import avifEnc from '../../codecs/avif/enc/avif_node_enc.js';
import avifEncWasm from 'asset-url:../../codecs/avif/enc/avif_node_enc.wasm'; import avifEncWasm from 'asset-url:../../codecs/avif/enc/avif_node_enc.wasm';
import avifEncMt from '../../codecs/avif/enc/avif_node_enc_mt.js';
import avifEncMtWorker from 'chunk-url:../../codecs/avif/enc/avif_node_enc_mt.worker.js';
import avifEncMtWasm from 'asset-url:../../codecs/avif/enc/avif_node_enc_mt.wasm';
import avifDec from '../../codecs/avif/dec/avif_node_dec.js'; import avifDec from '../../codecs/avif/dec/avif_node_dec.js';
import avifDecWasm from 'asset-url:../../codecs/avif/dec/avif_node_dec.wasm'; import avifDecWasm from 'asset-url:../../codecs/avif/dec/avif_node_dec.wasm';
@@ -325,7 +336,16 @@ export const codecs = {
extension: 'avif', extension: 'avif',
detectors: [/^\x00\x00\x00 ftypavif\x00\x00\x00\x00/], detectors: [/^\x00\x00\x00 ftypavif\x00\x00\x00\x00/],
dec: () => instantiateEmscriptenWasm(avifDec, avifDecWasm), dec: () => instantiateEmscriptenWasm(avifDec, avifDecWasm),
enc: () => instantiateEmscriptenWasm(avifEnc, avifEncWasm), enc: async () => {
if (await threads()) {
return instantiateEmscriptenWasm(
avifEncMt,
avifEncMtWasm,
avifEncMtWorker,
);
}
return instantiateEmscriptenWasm(avifEnc, avifEncWasm);
},
defaultEncoderOptions: { defaultEncoderOptions: {
cqLevel: 33, cqLevel: 33,
cqAlphaLevel: -1, cqAlphaLevel: -1,

View File

@@ -1,4 +1,4 @@
import { fileURLToPath } from 'url'; import { fileURLToPath, URL } from 'url';
export function pathify(path: string): string { export function pathify(path: string): string {
if (path.startsWith('file://')) { if (path.startsWith('file://')) {
@@ -10,10 +10,14 @@ export function pathify(path: string): string {
export function instantiateEmscriptenWasm<T extends EmscriptenWasm.Module>( export function instantiateEmscriptenWasm<T extends EmscriptenWasm.Module>(
factory: EmscriptenWasm.ModuleFactory<T>, factory: EmscriptenWasm.ModuleFactory<T>,
path: string, path: string,
workerWasm: string = '',
): Promise<T> { ): Promise<T> {
return factory({ return factory({
locateFile() { locateFile(requestPath) {
return pathify(path); if (requestPath.endsWith('.wasm')) return pathify(path);
if (requestPath.endsWith('.worker.js'))
return new URL(workerWasm).pathname;
return requestPath;
}, },
}); });
} }

View File

@@ -23,6 +23,11 @@ declare module 'asset-url:../../codecs/resize/pkg/squoosh_resize_bg.wasm' {
export default value; export default value;
} }
declare module 'chunk-url:../../codecs/avif/enc/avif_node_enc_mt.worker.js' {
const value: string;
export default value;
}
// These don't exist in NodeJS types so we're not able to use them but they are referenced in some emscripten and codec types // These don't exist in NodeJS types so we're not able to use them but they are referenced in some emscripten and codec types
// Thus, we need to explicitly assign them to be `never` // Thus, we need to explicitly assign them to be `never`
// We're also not able to use the APIs that use these types // We're also not able to use the APIs that use these types