Skip to content

Commit

Permalink
feat(offscreen): disable redraw canvases immediate in offscreen mode
Browse files Browse the repository at this point in the history
  • Loading branch information
DeltaZN committed Dec 3, 2023
1 parent 48d44b4 commit 22cd89e
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 24 deletions.
2 changes: 1 addition & 1 deletion src/chart/canvas/offscreen/init-offscreen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ let canvasesIdxOffset = 0;
export const initOffscreenWorker = async (canvases: CanvasModel[]): Promise<Remote<OffscreenWorker>> => {
if (offscreenWorker === undefined) {
if (typeof OffscreenWorkerClass === 'function') {
offscreenWorker = await new OffscreenWorkerClass();
offscreenWorker = await new OffscreenWorkerClass(window.devicePixelRatio);
} else {
return Promise.reject('Offscreen worker is not available.');
}
Expand Down
7 changes: 7 additions & 0 deletions src/chart/canvas/offscreen/offscreen-worker.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export declare class OffscreenWorker {
constructor(devicePixelRatio: number);
/**
* Adds offscreen canvas to the worker
*/
Expand All @@ -21,5 +22,11 @@ export declare class OffscreenWorker {
*/
executeCanvasCommands(canvasIds: number[]): void;

/**
* Returns the color id for the given canvas idx and coordinates, @see HitTestCanvasModel
* @param idx canvas idx
* @param x - x coordinate on the canvas
* @param y - y coordinate on the canvas
*/
getColorId(idx: number, x: number, y: number): number;
}
35 changes: 18 additions & 17 deletions src/chart/canvas/offscreen/offscreen-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ const stringsPool = new Map();
const bigPrimeNumber = 317;

export class OffscreenWorker {
constructor() {
this.ctxs = {};
this.buffers = {};
constructor(dpr) {
this.dpr = dpr;
this.ctxs = new Map();
this.buffers = new Map();
// Pre-allocate args arrays to avoid GC
this.args = [
new Array(0),
Expand Down Expand Up @@ -59,20 +60,23 @@ export class OffscreenWorker {
},
});
const ctxs = this.ctxs;
const dpr = this.dpr;
Object.defineProperty(ctx, 'redrawBackgroundArea', {
value(backgroundCtxIdx, ctxIdx, x, y, width, height, opacity) {
const backgroundCtx = ctxs[backgroundCtxIdx];
const ctx = ctxs[ctxIdx];
ctx && backgroundCtx && redrawBackgroundArea(backgroundCtx, ctx, x, y, width, height, opacity);
const backgroundCtx = ctxs.get(backgroundCtxIdx);
const ctx = ctxs.get(ctxIdx);
ctx &&
backgroundCtx &&
redrawBackgroundArea(dpr, backgroundCtx, ctx, x, y, width, height, opacity);
},
});
}

addCanvas(canvasId, options, canvas, commandsBuffer) {
this.buffers[canvasId] = new Float64Array(commandsBuffer);
addCanvas(canvasIdx, options, canvas, commandsBuffer) {
this.buffers.set(canvasIdx, new Float64Array(commandsBuffer));
const ctx = canvas.getContext('2d', options);
this.defineCustomCanvasProperties(ctx);
this.ctxs[canvasId] = ctx;
this.ctxs.set(canvasIdx, ctx);
}

syncStrings(strs) {
Expand All @@ -82,14 +86,12 @@ export class OffscreenWorker {
}

executeCanvasCommands(canvasIds) {
// transform canvasIds to strings
canvasIds = canvasIds.map(canvasId => '' + canvasId);
for (const [canvasId, ctxCommands] of Object.entries(this.buffers)) {
for (const [canvasId, ctxCommands] of this.buffers.entries()) {
if (!canvasIds.includes(canvasId)) {
continue;
}
let counter = 0;
const ctx = this.ctxs[canvasId];
const ctx = this.ctxs.get(canvasId);
while (ctxCommands[counter] !== END_OF_FILE) {
const method = num2Ctx[ctxCommands[counter++]];
const argsLen = ctxCommands[counter++];
Expand All @@ -111,11 +113,11 @@ export class OffscreenWorker {
}

getColorId(idx, x, y) {
const ctx = this.ctxs[idx];
const ctx = this.ctxs.get(idx);
if (!ctx) {
return -1;
}
const data = ctx.getImageData(x * 2, y * 2, 1, 1).data;
const data = ctx.getImageData(x * this.dpr, y * this.dpr, 1, 1).data;
const id = (data[0] * 65536 + data[1] * 256 + data[2]) / bigPrimeNumber;
return id;
}
Expand All @@ -127,8 +129,7 @@ expose(OffscreenWorker);
const floor = value => ~~value;
// this function in used in case when
// some entity can overlap with another chart entity, so we need to hide the another entity
export const redrawBackgroundArea = (backgroundCtx, ctx, x, y, width, height, opacity) => {
const dpr = 2;
export const redrawBackgroundArea = (dpr, backgroundCtx, ctx, x, y, width, height, opacity) => {
const xCoord = x * dpr;
const yCoord = y * dpr;
const widthCoord = width * dpr;
Expand Down
16 changes: 10 additions & 6 deletions src/chart/drawers/drawing-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ import {
isOffscreenCanvasModel,
strsToSync,
} from '../canvas/offscreen/canvas-offscreen-wrapper';
import {
initOffscreenWorker,
isOffscreenWorkerAvailable,
} from '../canvas/offscreen/init-offscreen';
import { initOffscreenWorker, isOffscreenWorkerAvailable } from '../canvas/offscreen/init-offscreen';
import { OffscreenWorker } from '../canvas/offscreen/offscreen-worker';
import EventBus from '../events/event-bus';
import { EVENT_DRAW } from '../events/events';
Expand Down Expand Up @@ -70,7 +67,7 @@ export class DrawingManager {
private offscreenCanvases: CanvasModel<CanvasOffscreenContext2D>[] = [];

constructor(
config: FullChartConfig,
private config: FullChartConfig,
eventBus: EventBus,
private chartResizeHandler: ChartResizeHandler,
canvases: CanvasModel[],
Expand Down Expand Up @@ -107,6 +104,8 @@ export class DrawingManager {
}
animationFrameThrottled(this.animFrameId, async () => {
if (!this.isDrawable()) {
// previous rendering cycle is not finished yet, schedule another draw
eventBus.fireDraw();
return;
}
this.forceDraw();
Expand Down Expand Up @@ -140,9 +139,14 @@ export class DrawingManager {
* @doc-tags tricky,canvas,resize
*/
public async redrawCanvasesImmediate() {
// not safe and meaningless to use in offscreen mode
// I'm not sure if it's even possible because of async nature of offscreen
// of course we can implement some kind of spinlock, but it's insane
if (this.config.offscreen) {
return;
}
this.chartResizeHandler.fireUpdates();
this.forceDraw();
await this.drawOffscreen();
this.readyDraw = true;
}

Expand Down

0 comments on commit 22cd89e

Please sign in to comment.