no worker
This commit is contained in:
parent
732e7dc931
commit
a9c49e4fb4
|
@ -16,31 +16,18 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<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';
|
import { $ref } from 'vue/macros';
|
||||||
|
|
||||||
const workerPromise = new Promise<WorkerMultiDispatch | null>(resolve => {
|
const WEBGL2_IN_OFFSCREENCANVAS_SUPPORTED = (() => {
|
||||||
const testWorker = new TestWebGL2();
|
const canvas = new OffscreenCanvas(1, 1);
|
||||||
testWorker.addEventListener('message', event => {
|
const gl = canvas.getContext('webgl2');
|
||||||
if (event.data.result) {
|
if (_DEV_) console.info('WEBGL2_IN_OFFSCREENCANVAS_SUPPORTED', !!gl);
|
||||||
const workers = new WorkerMultiDispatch(
|
return !!gl;
|
||||||
() => new DrawBlurhash(),
|
})();
|
||||||
Math.min(navigator.hardwareConcurrency - 1, 4),
|
|
||||||
);
|
|
||||||
resolve(workers);
|
|
||||||
} else {
|
|
||||||
resolve(null);
|
|
||||||
}
|
|
||||||
testWorker.terminate();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, nextTick, onMounted, onUnmounted, shallowRef, useCssModule, watch } from 'vue';
|
import { computed, nextTick, onMounted, shallowRef, useCssModule, watch } from 'vue';
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
import { render } from 'buraha';
|
import { render } from 'buraha';
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store';
|
||||||
const $style = useCssModule();
|
const $style = useCssModule();
|
||||||
|
@ -74,7 +61,6 @@ const props = withDefaults(defineProps<{
|
||||||
forceBlurhash: false,
|
forceBlurhash: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const viewId = uuid();
|
|
||||||
const canvas = shallowRef<HTMLCanvasElement>();
|
const canvas = shallowRef<HTMLCanvasElement>();
|
||||||
const root = shallowRef<HTMLDivElement>();
|
const root = shallowRef<HTMLDivElement>();
|
||||||
const img = shallowRef<HTMLImageElement>();
|
const img = shallowRef<HTMLImageElement>();
|
||||||
|
@ -83,7 +69,6 @@ let canvasWidth = $ref(64);
|
||||||
let canvasHeight = $ref(64);
|
let canvasHeight = $ref(64);
|
||||||
let imgWidth = $ref(props.width);
|
let imgWidth = $ref(props.width);
|
||||||
let imgHeight = $ref(props.height);
|
let imgHeight = $ref(props.height);
|
||||||
let usingWorkerNumber = $ref<number>(-1);
|
|
||||||
const hide = computed(() => !loaded || props.forceBlurhash);
|
const hide = computed(() => !loaded || props.forceBlurhash);
|
||||||
|
|
||||||
function waitForDecode() {
|
function waitForDecode() {
|
||||||
|
@ -118,38 +103,41 @@ watch([() => props.width, () => props.height, root], () => {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
async function draw(transfer: boolean = false) {
|
// アクティブなwebgl2をたくさん持てないため、burahaは別のcanvasで描画させる
|
||||||
if (!canvas.value || props.hash == null) return;
|
// ImageBitmapで送るのは、なるべく早くwebgl2コンテキストを持ったcanvasを解放させるため
|
||||||
const workers = await workerPromise;
|
async function drawSub(): Promise<ImageBitmap | void> {
|
||||||
if (workers) {
|
if (props.hash == null) return;
|
||||||
let offscreen: OffscreenCanvas | undefined;
|
|
||||||
if (transfer) {
|
if (WEBGL2_IN_OFFSCREENCANVAS_SUPPORTED) {
|
||||||
offscreen = canvas.value.transferControlToOffscreen();
|
const work = new OffscreenCanvas(canvasWidth, canvasHeight);
|
||||||
|
render(props.hash, work);
|
||||||
|
return createImageBitmap(work);
|
||||||
}
|
}
|
||||||
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');
|
const work = document.createElement('canvas');
|
||||||
work.width = canvasWidth;
|
work.width = canvasWidth;
|
||||||
work.height = canvasHeight;
|
work.height = canvasHeight;
|
||||||
render(props.hash, work);
|
render(props.hash, work);
|
||||||
const bitmap = await createImageBitmap(work);
|
return createImageBitmap(work);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function draw() {
|
||||||
|
if (!canvas.value || props.hash == null) return;
|
||||||
const ctx = canvas.value.getContext('2d');
|
const ctx = canvas.value.getContext('2d');
|
||||||
ctx?.drawImage(bitmap, 0, 0, canvasWidth, canvasHeight);
|
|
||||||
|
if (!ctx) {
|
||||||
|
console.error('Unable to get canvas 2D context');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const bitmap = await drawSub();
|
||||||
|
if (!bitmap) return;
|
||||||
|
ctx.drawImage(bitmap, 0, 0, canvasWidth, canvasHeight);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error occured during drawing blurhash', error);
|
console.error('Error occured during drawing blurhash', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
watch(() => props.src, () => {
|
watch(() => props.src, () => {
|
||||||
waitForDecode();
|
waitForDecode();
|
||||||
|
@ -160,17 +148,9 @@ watch(() => props.hash, () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
draw(true);
|
draw();
|
||||||
waitForDecode();
|
waitForDecode();
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
if (usingWorkerNumber !== -1) {
|
|
||||||
workerPromise.then(worker => {
|
|
||||||
worker?.postMessage!({ id: viewId, delete: true }, undefined, () => usingWorkerNumber);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
import { render } from 'buraha';
|
|
||||||
|
|
||||||
const canvases = new Map<string, OffscreenCanvas>();
|
|
||||||
|
|
||||||
onmessage = async (event) => {
|
|
||||||
// console.log(event.data);
|
|
||||||
if (!('id' in event.data && typeof event.data.id === 'string')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.data.delete) {
|
|
||||||
canvases.delete(event.data.id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.data.canvas) {
|
|
||||||
canvases.set(event.data.id, event.data.canvas);
|
|
||||||
}
|
|
||||||
if (!('hash' in event.data && typeof event.data.hash === 'string')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const canvas = event.data.canvas ?? canvases.get(event.data.id);
|
|
||||||
if (!canvas) {
|
|
||||||
throw new Error('No canvas');
|
|
||||||
}
|
|
||||||
const work = new OffscreenCanvas(canvas.width, canvas.height);
|
|
||||||
render(event.data.hash, work);
|
|
||||||
const bitmap = await createImageBitmap(work);
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
ctx?.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
|
|
||||||
postMessage({ result: true });
|
|
||||||
};
|
|
|
@ -1,7 +0,0 @@
|
||||||
const canvas = new OffscreenCanvas(1, 1);
|
|
||||||
const gl = canvas.getContext('webgl2');
|
|
||||||
if (gl) {
|
|
||||||
postMessage({ result: true });
|
|
||||||
} else {
|
|
||||||
postMessage({ result: false });
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"lib": ["esnext", "webworker"],
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue