Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

line chart: introduce onContextLost renderer callback #5237

Merged
merged 1 commit into from
Aug 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion tensorboard/webapp/widgets/line_chart_v2/lib/chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ export class ChartImpl implements Chart {
this.renderer = new ThreeRenderer(
option.container,
coordinator,
option.devicePixelRatio
option.devicePixelRatio,
option.callbacks.onContextLost
);
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export interface Chart {

export interface ChartCallbacks {
onDrawEnd(): void;
onContextLost(): void;
}

export interface BaseChartOptions {
Expand Down
41 changes: 41 additions & 0 deletions tensorboard/webapp/widgets/line_chart_v2/lib/integration_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ describe('line_chart_v2/lib/integration test', () => {
dom = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
callbacks = {
onDrawEnd: jasmine.createSpy(),
onContextLost: jasmine.createSpy(),
};
chart = new ChartImpl({
type: RendererType.SVG,
Expand Down Expand Up @@ -460,4 +461,44 @@ describe('line_chart_v2/lib/integration test', () => {
expect(circle.getAttribute('cy')).toBe('5');
});
});

describe('webgl', () => {
it('invokes onContextLost after losing webgl context', async () => {
const canvas = document.createElement('canvas');
chart = new ChartImpl({
type: RendererType.WEBGL,
container: canvas,
callbacks,
domDimension: {width: 200, height: 100},
useDarkMode: false,
devicePixelRatio: 1,
});
chart.setXScaleType(ScaleType.LINEAR);
chart.setYScaleType(ScaleType.LINEAR);

expect(callbacks.onContextLost).not.toHaveBeenCalled();

// For more info about forcing context loss, see
// https://developer.mozilla.org/en-US/docs/Web/API/WEBGL_lose_context/loseContext
const glExtension = canvas
.getContext('webgl2')
?.getExtension('WEBGL_lose_context');
if (!glExtension) {
console.log(
'The browser used for testing does not ' +
'support WebGL or extensions needed for testing.'
);
return;
}

// The `loseContext` triggers the event asynchronously, which.
const contextLostPromise = new Promise((resolve) => {
canvas.addEventListener('webglcontextlost', resolve);
});
glExtension.loseContext();

await contextLostPromise;
expect(callbacks.onContextLost).toHaveBeenCalledTimes(1);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -268,12 +268,19 @@ export class ThreeRenderer implements ObjectRenderer<CacheValue> {
constructor(
canvas: HTMLCanvasElement | OffscreenCanvas,
private readonly coordinator: ThreeCoordinator,
devicePixelRatio: number
devicePixelRatio: number,
onContextLost?: EventListener
) {
if (isOffscreenCanvasSupported() && canvas instanceof OffscreenCanvas) {
// THREE.js require the style object which Offscreen canvas lacks.
(canvas as any).style = (canvas as any).style || {};
}
// WebGL contexts may be abandoned by the browser if too many contexts are
// created on the same page.
if (onContextLost) {
canvas.addEventListener('webglcontextlost', onContextLost);
}

this.renderer = new THREE.WebGLRenderer({
canvas: canvas as HTMLCanvasElement,
context: canvas.getContext('webgl2', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,15 @@ export type HostToGuestMessage =

export enum GuestToMainType {
ON_REDRAW_END,
ON_CONTEXT_LOST,
}

export interface RedrawEndMessage {
type: GuestToMainType.ON_REDRAW_END;
}

export type GuestToMainMessage = RedrawEndMessage;
export interface ContextLostMessage {
type: GuestToMainType.ON_CONTEXT_LOST;
}

export type GuestToMainMessage = RedrawEndMessage | ContextLostMessage;
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ export class WorkerChart implements Chart {
this.callbacks.onDrawEnd();
break;
}
case GuestToMainType.ON_CONTEXT_LOST: {
this.callbacks.onContextLost();
break;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ function createPortHandler(port: MessagePort, initMessage: InitMessage) {
type: GuestToMainType.ON_REDRAW_END,
});
},
onContextLost: () => {
port.postMessage({
type: GuestToMainType.ON_CONTEXT_LOST,
});
},
};

let chartOptions: ChartOptions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('line_chart_v2/lib/worker_chart test', () => {
let workerPostMessageSpy: jasmine.Spy;
let channelTxSpy: jasmine.Spy;
let onDrawEndSpy: jasmine.Spy;
let onContextLostSpy: jasmine.Spy;
let chart: WorkerChart;

beforeEach(() => {
Expand All @@ -41,10 +42,11 @@ describe('line_chart_v2/lib/worker_chart test', () => {
});

onDrawEndSpy = jasmine.createSpy();
onContextLostSpy = jasmine.createSpy();
chart = new WorkerChart({
type: RendererType.WEBGL,
devicePixelRatio: 1,
callbacks: {onDrawEnd: onDrawEndSpy},
callbacks: {onDrawEnd: onDrawEndSpy, onContextLost: onContextLostSpy},
container: document.createElement('canvas'),
domDimension: {width: 100, height: 200},
useDarkMode: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,10 @@ export class LineChartComponent

const rendererType = this.getRendererType();
// Do not yet need to subscribe to the `onDrawEnd`.
const callbacks: ChartCallbacks = {onDrawEnd: () => {}};
const callbacks: ChartCallbacks = {
onDrawEnd: () => {},
onContextLost: () => {},
};
let params: ChartOptions | null = null;

this.readAndUpdateDomDimensions();
Expand Down