wip
This commit is contained in:
parent
5367e896af
commit
732e7dc931
|
@ -18,12 +18,21 @@
|
|||
<script lang="ts">
|
||||
import DrawBlurhash from '@/workers/draw-blurhash?worker';
|
||||
import TestWebGL2 from '@/workers/test-webgl2?worker';
|
||||
import { WorkerMultiDispatch } from '@/scripts/worker-multi-dispatch';
|
||||
import { $ref } from 'vue/macros';
|
||||
|
||||
const workerPromise = new Promise<Worker | null>(resolve => {
|
||||
const workerPromise = new Promise<WorkerMultiDispatch | null>(resolve => {
|
||||
const testWorker = new TestWebGL2();
|
||||
testWorker.addEventListener('message', event => {
|
||||
if (event.data.result) resolve(new DrawBlurhash());
|
||||
else resolve(null);
|
||||
if (event.data.result) {
|
||||
const workers = new WorkerMultiDispatch(
|
||||
() => new DrawBlurhash(),
|
||||
Math.min(navigator.hardwareConcurrency - 1, 4),
|
||||
);
|
||||
resolve(workers);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
testWorker.terminate();
|
||||
});
|
||||
});
|
||||
|
@ -74,6 +83,7 @@ let canvasWidth = $ref(64);
|
|||
let canvasHeight = $ref(64);
|
||||
let imgWidth = $ref(props.width);
|
||||
let imgHeight = $ref(props.height);
|
||||
let usingWorkerNumber = $ref<number>(-1);
|
||||
const hide = computed(() => !loaded || props.forceBlurhash);
|
||||
|
||||
function waitForDecode() {
|
||||
|
@ -110,17 +120,22 @@ watch([() => props.width, () => props.height, root], () => {
|
|||
|
||||
async function draw(transfer: boolean = false) {
|
||||
if (!canvas.value || props.hash == null) return;
|
||||
const worker = await workerPromise;
|
||||
if (worker) {
|
||||
const workers = await workerPromise;
|
||||
if (workers) {
|
||||
let offscreen: OffscreenCanvas | undefined;
|
||||
if (transfer) {
|
||||
offscreen = canvas.value.transferControlToOffscreen();
|
||||
}
|
||||
worker.postMessage({
|
||||
id: viewId,
|
||||
canvas: offscreen ?? undefined,
|
||||
hash: props.hash,
|
||||
}, offscreen ? [offscreen] : []);
|
||||
const workerNumber = workers.postMessage(
|
||||
{
|
||||
id: viewId,
|
||||
canvas: offscreen ?? undefined,
|
||||
hash: props.hash,
|
||||
},
|
||||
offscreen ? [offscreen] : [],
|
||||
usingWorkerNumber === -1 ? undefined : () => usingWorkerNumber,
|
||||
);
|
||||
usingWorkerNumber = workerNumber;
|
||||
} else {
|
||||
try {
|
||||
const work = document.createElement('canvas');
|
||||
|
@ -150,7 +165,11 @@ onMounted(() => {
|
|||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
workerPromise.then(worker => worker?.postMessage!({ id: viewId, delete: true }));
|
||||
if (usingWorkerNumber !== -1) {
|
||||
workerPromise.then(worker => {
|
||||
worker?.postMessage!({ id: viewId, delete: true }, undefined, () => usingWorkerNumber);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
62
packages/frontend/src/scripts/worker-multi-dispatch.ts
Normal file
62
packages/frontend/src/scripts/worker-multi-dispatch.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
function defaultUseWorkerNumber(prev: number, totalWorkers: number) {
|
||||
return prev + 1;
|
||||
}
|
||||
|
||||
export class WorkerMultiDispatch<POST = any, RETURN = any> {
|
||||
private sym = Symbol('WorkerMultiDispatch');
|
||||
public workers: Worker[] = [];
|
||||
private prevWorkerNumber = 0;
|
||||
private getUseWorkerNumber = defaultUseWorkerNumber;
|
||||
private finalizationRegistry: FinalizationRegistry<symbol>;
|
||||
|
||||
constructor(workerConstructor: () => Worker, concurrency: number, getUseWorkerNumber = defaultUseWorkerNumber) {
|
||||
this.getUseWorkerNumber = getUseWorkerNumber;
|
||||
for (let i = 0; i < concurrency; i++) {
|
||||
this.workers.push(workerConstructor());
|
||||
}
|
||||
|
||||
this.finalizationRegistry = new FinalizationRegistry(() => {
|
||||
this.terminate();
|
||||
});
|
||||
this.finalizationRegistry.register(this, this.sym);
|
||||
|
||||
if (_DEV_) console.log('WorkerMultiDispatch: Created', this);
|
||||
}
|
||||
|
||||
public postMessage(message: POST, options?: Transferable[] | StructuredSerializeOptions, useWorkerNumber: typeof defaultUseWorkerNumber = this.getUseWorkerNumber) {
|
||||
let workerNumber = useWorkerNumber(this.prevWorkerNumber, this.workers.length);
|
||||
workerNumber = Math.abs(Math.round(workerNumber)) % this.workers.length;
|
||||
if (_DEV_) console.log('WorkerMultiDispatch: Posting message to worker', workerNumber, useWorkerNumber);
|
||||
this.prevWorkerNumber = workerNumber;
|
||||
|
||||
// 不毛だがunionをoverloadに突っ込めない
|
||||
// https://stackoverflow.com/questions/66507585/overload-signatures-union-types-and-no-overload-matches-this-call-error
|
||||
// https://github.com/microsoft/TypeScript/issues/14107
|
||||
if (Array.isArray(options)) {
|
||||
this.workers[workerNumber].postMessage(message, options);
|
||||
} else {
|
||||
this.workers[workerNumber].postMessage(message, options);
|
||||
}
|
||||
return workerNumber;
|
||||
}
|
||||
|
||||
public addListener(callback: (this: Worker, ev: MessageEvent<RETURN>) => any, options?: boolean | AddEventListenerOptions) {
|
||||
this.workers.forEach(worker => {
|
||||
worker.addEventListener('message', callback, options);
|
||||
});
|
||||
}
|
||||
|
||||
public removeListener(callback: (this: Worker, ev: MessageEvent<RETURN>) => any, options?: boolean | AddEventListenerOptions) {
|
||||
this.workers.forEach(worker => {
|
||||
worker.removeEventListener('message', callback, options);
|
||||
});
|
||||
}
|
||||
|
||||
public terminate() {
|
||||
this.workers.forEach(worker => {
|
||||
worker.terminate();
|
||||
});
|
||||
this.workers = [];
|
||||
this.finalizationRegistry.unregister(this);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue