Skip to content

Commit

Permalink
feat(offscreen): fix render in multichart mode
Browse files Browse the repository at this point in the history
  • Loading branch information
DeltaZN committed Nov 25, 2023
1 parent 47d8a3e commit 5e0c288
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 79 deletions.
2 changes: 1 addition & 1 deletion src/chart/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export default class ChartBootstrap {
{
// can be read frequently, see {redrawBackgroundArea} function
willReadFrequently: true,
offscreen: true,
offscreen: false,
},
);
this.backgroundCanvasModel = backgroundCanvasModel;
Expand Down
63 changes: 31 additions & 32 deletions src/chart/canvas/offscreen/canvas-offscreen-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
const END_OF_FILE = 0xdead;
const canvas = document.createElement('canvas');
// @ts-ignore
const ctxForMeasure: CanvasRenderingContext2D = canvas.getContext('2d');
export const ctxForMeasure: CanvasRenderingContext2D = canvas.getContext('2d');

let curPtr = Number.MIN_SAFE_INTEGER;
// it's intentional that this map is global, bcs we want to have common strings pool between all charts
Expand All @@ -43,7 +43,6 @@ export const strSync: unknown[] = [];
export class CanvasOffscreenContext2D implements Partial<CanvasRenderingContext2D> {
public commands: Float64Array;
public buffer: SharedArrayBuffer;
public locked = false;

private getStrPtr(str: string): number {
let id = stringPtrs.get(str);
Expand All @@ -61,47 +60,47 @@ export class CanvasOffscreenContext2D implements Partial<CanvasRenderingContext2
constructor(public idx: number) {
this.buffer = new SharedArrayBuffer(8 * 100000);
this.commands = new Float64Array(this.buffer);
this.commit();
}

commit() {
this.commands[this.counter] = END_OF_FILE;
this.locked = true;
this.counter = 0;
}

nop() {
this.commands[this.counter++] = NOP;
}

private __check() {
if (this.locked) {
// debugger;
}
}
// private __check() {
// if (this.locked) {
// // debugger;
// }
// }

set font(val: string) {
this.__font = val;
this.commands[this.counter++] = FONT;
this.commands[this.counter++] = -1;
this.commands[this.counter++] = this.getStrPtr(val);

this.__check();
// this.__check() ;
}

set width(val: number) {
this.commands[this.counter++] = WIDTH;
this.commands[this.counter++] = -1;
this.commands[this.counter++] = val;

this.__check();
// this.__check() ;
}

set height(val: number) {
this.commands[this.counter++] = HEIGHT;
this.commands[this.counter++] = -1;
this.commands[this.counter++] = val;

this.__check();
// this.__check() ;
}

set fillStyle(val: string | CanvasGradient | CanvasPattern | undefined) {
Expand All @@ -110,7 +109,7 @@ export class CanvasOffscreenContext2D implements Partial<CanvasRenderingContext2
// @ts-ignore
this.commands[this.counter++] = this.getStrPtr(val);

this.__check();
// this.__check() ;
}

set strokeStyle(val: string | CanvasGradient | CanvasPattern | undefined) {
Expand All @@ -119,37 +118,37 @@ export class CanvasOffscreenContext2D implements Partial<CanvasRenderingContext2
// @ts-ignore
this.commands[this.counter++] = this.getStrPtr(val);

this.__check();
// this.__check() ;
}

set lineWidth(val: number) {
this.commands[this.counter++] = LINE_WIDTH;
this.commands[this.counter++] = -1;
this.commands[this.counter++] = val;

this.__check();
// this.__check() ;
}

set lineCap(val: CanvasLineCap) {
this.commands[this.counter++] = LINE_CAP;
this.commands[this.counter++] = -1;
this.commands[this.counter++] = this.getStrPtr(val);

this.__check();
// this.__check() ;
}

public save(): void {
this.commands[this.counter++] = SAVE;
this.commands[this.counter++] = 0;

this.__check();
// this.__check() ;
}

public restore(): void {
this.commands[this.counter++] = RESTORE;
this.commands[this.counter++] = 0;

this.__check();
// this.__check() ;
}

public measureText(text: string): TextMetrics {
Expand Down Expand Up @@ -189,7 +188,7 @@ export class CanvasOffscreenContext2D implements Partial<CanvasRenderingContext2
this.commands[this.counter++] = w;
this.commands[this.counter++] = h;

this.__check();
// this.__check() ;
}

public fillRect(x: number, y: number, w: number, h: number): void {
Expand All @@ -200,7 +199,7 @@ export class CanvasOffscreenContext2D implements Partial<CanvasRenderingContext2
this.commands[this.counter++] = w;
this.commands[this.counter++] = h;

this.__check();
// this.__check() ;
}

public arc(
Expand Down Expand Up @@ -233,7 +232,7 @@ export class CanvasOffscreenContext2D implements Partial<CanvasRenderingContext2
this.commands[this.counter++] = maxWidth;
}

this.__check();
// this.__check() ;
}

public setLineDash(): void {
Expand All @@ -252,7 +251,7 @@ export class CanvasOffscreenContext2D implements Partial<CanvasRenderingContext2
this.commands[this.counter++] = maxWidth;
}

this.__check();
// this.__check() ;
}

public rect(x: number, y: number, w: number, h: number): void {
Expand All @@ -263,14 +262,14 @@ export class CanvasOffscreenContext2D implements Partial<CanvasRenderingContext2
this.commands[this.counter++] = w;
this.commands[this.counter++] = h;

this.__check();
// this.__check() ;
}

public clip(): void {
this.commands[this.counter++] = CLIP;
this.commands[this.counter++] = 0;

this.__check();
// this.__check() ;
}

public quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void {
Expand All @@ -294,7 +293,7 @@ export class CanvasOffscreenContext2D implements Partial<CanvasRenderingContext2
this.commands[this.counter++] = x;
this.commands[this.counter++] = y;

this.__check();
// this.__check() ;
}

public getImageData(sx: number, sy: number, sw: number, sh: number): ImageData {
Expand All @@ -305,7 +304,7 @@ export class CanvasOffscreenContext2D implements Partial<CanvasRenderingContext2
this.commands[this.counter++] = BEGIN_PATH;
this.commands[this.counter++] = 0;

this.__check();
// this.__check() ;
}

public moveTo(x: number, y: number): void {
Expand All @@ -314,7 +313,7 @@ export class CanvasOffscreenContext2D implements Partial<CanvasRenderingContext2
this.commands[this.counter++] = x;
this.commands[this.counter++] = y;

this.__check();
// this.__check() ;
}

public lineTo(x: number, y: number): void {
Expand All @@ -323,28 +322,28 @@ export class CanvasOffscreenContext2D implements Partial<CanvasRenderingContext2
this.commands[this.counter++] = x;
this.commands[this.counter++] = y;

this.__check();
// this.__check() ;
}

public stroke(): void {
this.commands[this.counter++] = STROKE;
this.commands[this.counter++] = 0;

this.__check();
// this.__check() ;
}

public fill(): void {
this.commands[this.counter++] = FILL;
this.commands[this.counter++] = 0;

this.__check();
// this.__check() ;
}

public closePath(): void {
this.commands[this.counter++] = CLOSE_PATH;
this.commands[this.counter++] = 0;

this.__check();
// this.__check() ;
}

public strokeRect(x: number, y: number, w: number, h: number): void {
Expand All @@ -355,7 +354,7 @@ export class CanvasOffscreenContext2D implements Partial<CanvasRenderingContext2
this.commands[this.counter++] = w;
this.commands[this.counter++] = h;

this.__check();
// this.__check() ;
}

public scale(x: number, y: number): void {
Expand All @@ -364,6 +363,6 @@ export class CanvasOffscreenContext2D implements Partial<CanvasRenderingContext2
this.commands[this.counter++] = x;
this.commands[this.counter++] = y;

this.__check();
// this.__check() ;
}
}
5 changes: 0 additions & 5 deletions src/chart/canvas/offscreen/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,12 @@ const OffscreenWorker = wrap<any>(new Worker(new URL('./offscreen-worker.js', im
// const OffscreenWorker = wrap<any>(new Worker(new URL('http://localhost:3000/worker.js')));
let worker: any = null;
let _startOffset = 0;
let first = false;

export const initOffscreenWorker = async (canvases: CanvasModel[]) => {
if (worker === null) {
// @ts-ignore
worker = await new OffscreenWorker();
}
if (!first) {
first = true;
return worker;
}
const startOffset = _startOffset;
_startOffset += 100;
for (let i = 0; i < canvases.length; i++) {
Expand Down
31 changes: 19 additions & 12 deletions src/chart/canvas/offscreen/offscreen-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,24 @@ export class OffscreenWorker {
new Array(9),
new Array(10),
];
this.debug = {};
}

addCanvas(canvasId, options, canvas, commandsBuffer) {
this.buffers[canvasId] = new Float64Array(commandsBuffer);
this.ctxs[canvasId] = canvas.getContext('2d', options);
const ctx = canvas.getContext('2d', options);
Object.defineProperty(ctx, 'width', {
set(width) {
return (ctx.canvas.width = width);
},
});
Object.defineProperty(ctx, 'height', {
set(height) {
return (ctx.canvas.height = height);
},
});
this.ctxs[canvasId] = ctx;
this.debug[canvasId] = [];
}

syncStrings(strs) {
Expand All @@ -35,26 +48,21 @@ export class OffscreenWorker {
}
}

executeCanvasCommands() {
executeCanvasCommands(canvasIds) {
canvasIds = canvasIds.map((canvasId) => '' + canvasId);
for (const [canvasId, ctxCommands] of Object.entries(this.buffers)) {
// if (!canvasIds.includes(canvasId)) {
// continue;
// }
if (!canvasIds.includes(canvasId)) {
continue;
}
let counter = 0;
const ctx = this.ctxs[canvasId];
while (ctxCommands[counter] !== END_OF_FILE) {
const method = num2Ctx[ctxCommands[counter++]];
if (method === undefined) {
debugger;
}
const argsLen = ctxCommands[counter++];
if (argsLen !== -1) {
const args = this.args[argsLen];
for (let i = 0; i < argsLen; i++) {
const arg = ctxCommands[counter++];
if (args === undefined) {
debugger;
}
args[i] = stringsPool.get(arg) ?? arg;
}
ctx[method].apply(ctx, args);
Expand All @@ -63,7 +71,6 @@ export class OffscreenWorker {
ctx[method] = stringsPool.get(arg) ?? arg;
}
}
// ctxCommands[0] = END_OF_FILE;
}
}
}
Expand Down
16 changes: 4 additions & 12 deletions src/chart/drawers/drawing-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,28 +87,19 @@ export class DrawingManager {
if (!this.isDrawable()) {
return;
}
// if (this.chartResizeHandler.canvasNeedsResize) {
// this.chartResizeHandler.canvasNeedsResize = false;
// this.chartResizeHandler.canvasModels.forEach(
// model => this.chartResizeHandler.previousBCR && model.updateDPR(this.chartResizeHandler.previousBCR),
// );
// }
this.forceDraw();
this.drawHitTestCanvas();
this.readyDraw = false;
// @ts-ignore
canvases.forEach(canvas => canvas.ctx.commit?.());
console.log('locked');
if (strSync.length) {
await this.worker.syncStrings(strSync);
strSync.length = 0;
}
// @ts-ignore
await this.worker.executeCanvasCommands();
await this.worker.executeCanvasCommands(canvases.map(canvas => canvas.idx));
// @ts-ignore
canvases.forEach(canvas => (canvas.ctx.locked = false));
canvases.forEach(canvas => canvas.ctx.finish?.());
this.readyDraw = true;
console.log('unlocked');
this.canvasIdsList = [];
});
}
Expand All @@ -123,7 +114,7 @@ export class DrawingManager {
*/
public redrawCanvasesImmediate() {
this.chartResizeHandler.fireUpdates();
this.forceDraw();
// this.forceDraw();
}

drawLastBar() {
Expand All @@ -139,6 +130,7 @@ export class DrawingManager {
if (!this.isDrawable()) {
return;
}
this.readyDraw = false;
this.drawingOrder.forEach(drawerName => {
if (drawerName.indexOf(HIT_TEST_PREFIX) === -1) {
const drawer = this.drawersMap[drawerName];
Expand Down
Loading

0 comments on commit 5e0c288

Please sign in to comment.