move jpeg encoding to a worker thread pool

this also switches cvmts back to building with tsc, mostly because
I couldn't get parcel's worker interop to work at all.
This commit is contained in:
modeco80
2024-04-23 19:43:23 -04:00
parent 59d5331b68
commit db97a62046
5 changed files with 97 additions and 31 deletions

View File

@@ -5,22 +5,17 @@
"type": "module",
"main": "dist/index.js",
"scripts": {
"build": "parcel build src/index.ts --target node",
"build": "tsc -outDir dist -rootDir src/",
"serve": "node dist/index.js"
},
"author": "Elijah R, modeco80",
"license": "GPL-3.0",
"targets": {
"node": {
"context": "node",
"outputFormat": "esmodule"
}
},
"dependencies": {
"@computernewb/jpeg-turbo": "*",
"@cvmts/qemu": "*",
"execa": "^8.0.1",
"mnemonist": "^0.39.5",
"piscina": "^4.4.0",
"sharp": "^0.33.3",
"toml": "^3.0.0",
"ws": "^8.14.1"
@@ -28,7 +23,6 @@
"devDependencies": {
"@types/node": "^20.12.5",
"@types/ws": "^8.5.5",
"parcel": "^2.12.0",
"prettier": "^3.2.5",
"typescript": "^5.4.4"
}

View File

@@ -0,0 +1,16 @@
import jpegTurbo from "@computernewb/jpeg-turbo";
import Piscina from "piscina";
export default async (opts: any) => {
let res = await jpegTurbo.compress(opts.buffer, {
format: jpegTurbo.FORMAT_RGBA,
width: opts.width,
height: opts.height,
subsampling: jpegTurbo.SAMP_422,
stride: opts.stride,
quality: opts.quality
});
return Piscina.move(res);
}

View File

@@ -13,22 +13,20 @@ import { isIP } from 'node:net';
import { QemuVM, QemuVmDefinition } from '@cvmts/qemu';
import { IPData, IPDataManager } from './IPData.js';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'url';
import path from 'path';
import path from 'node:path';
import AuthManager from './AuthManager.js';
import { Size, Rect, Logger } from '@cvmts/shared';
import jpegTurbo from "@computernewb/jpeg-turbo";
import sharp from 'sharp';
import Piscina from 'piscina';
// @ts-expect-error (I know, but this is already ugly)
// really wish I didn't have to do it like this..
const __dirname = fileURLToPath(new __parcel__URL__('..'));
// Instead of strange hacks we can just use nodejs provided
// import.meta properties, which have existed since LTS if not before
const __filename = import.meta.filename;
const __dirname = import.meta.dirname;
const kCVMTSAssetsRoot = path.resolve(__dirname, '../../assets');
console.log(__dirname);
// ejla this exist. Useing it.
type ChatHistory = {
user: string,
msg: string
@@ -46,19 +44,27 @@ function GetRawSharpOptions(size: Size): sharp.CreateRaw {
}
}
const kJpegPool = new Piscina({
filename: path.join(import.meta.dirname + '/JPEGEncoderWorker.js'),
minThreads: 4,
maxThreads: 4
});
async function EncodeJpeg(canvas: Buffer, displaySize: Size, rect: Rect): Promise<Buffer> {
let offset = (rect.y * displaySize.width + rect.x) * 4;
//console.log('encoding rect', rect, 'with byteoffset', offset, '(size ', displaySize, ')');
let res = await kJpegPool.run({
buffer: canvas.subarray(offset),
width: rect.width,
height: rect.height,
stride: displaySize.width,
quality: kJpegQuality
});
return jpegTurbo.compress(canvas.subarray(offset), {
format: jpegTurbo.FORMAT_RGBA,
width: rect.width,
height: rect.height,
subsampling: jpegTurbo.SAMP_422,
stride: displaySize.width,
quality: kJpegQuality
});
// have to manually turn it back into a buffer because
// Piscina for some reason turns it into a Uint8Array
return Buffer.from(res);
}
export default class WSServer {
@@ -125,8 +131,8 @@ export default class WSServer {
this.voteCooldown = 0;
this.turnsAllowed = true;
this.screenHidden = false;
this.screenHiddenImg = readFileSync(__dirname + "/../assets/screenhidden.jpeg").toString("base64");
this.screenHiddenThumb = readFileSync(__dirname + "/../assets/screenhiddenthumb.jpeg").toString("base64");
this.screenHiddenImg = readFileSync(path.join(kCVMTSAssetsRoot, "screenhidden.jpeg")).toString("base64");
this.screenHiddenThumb = readFileSync(path.join(kCVMTSAssetsRoot, "screenhiddenthumb.jpeg")).toString("base64");
this.indefiniteTurn = null;
this.ModPerms = Utilities.MakeModPerms(this.Config.collabvm.moderatorPermissions);
@@ -937,7 +943,7 @@ export default class WSServer {
// TODO: pass custom options to Sharp.resize() probably
let out = await sharp(display.Buffer(), {raw: GetRawSharpOptions(display.Size())})
.resize(400, 300)
.resize(400, 300, { fit: 'fill' })
.toFormat('jpeg')
.toBuffer();
@@ -1000,4 +1006,4 @@ export default class WSServer {
});
return {yes:yes,no:no};
}
}
}

View File

@@ -1 +0,0 @@
../tsconfig.json

7
cvmts/tsconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
"extends": "../tsconfig.json",
"include": [ "src/**/*" ],
"compilerOptions": {
"resolveJsonModule": true,
}
}