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

clean overlays in detach function and not during reconciliation #5337

Merged
merged 6 commits into from
Jan 3, 2025
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
6 changes: 1 addition & 5 deletions app/packages/looker/src/lookers/abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,11 +293,6 @@ export abstract class AbstractLooker<
return;
}

if (this.state.destroyed && this.sampleOverlays) {
// close all current overlays
this.pluckedOverlays.forEach((overlay) => overlay.cleanup?.());
}

if (
!this.state.windowBBox ||
this.state.destroyed ||
Expand Down Expand Up @@ -609,6 +604,7 @@ export abstract class AbstractLooker<
this.detach();
this.abortController.abort();
this.updater({ destroyed: true });
this.sampleOverlays?.forEach((overlay) => overlay.cleanup?.());
}

disable() {
Expand Down
1 change: 1 addition & 0 deletions app/packages/looker/src/overlays/heatmap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ export default class HeatmapOverlay<State extends BaseState>

public cleanup(): void {
this.label.map?.bitmap?.close();
this.targets = null;
}
}

Expand Down
1 change: 1 addition & 0 deletions app/packages/looker/src/overlays/segmentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ export default class SegmentationOverlay<State extends BaseState>

public cleanup(): void {
this.label.mask?.bitmap?.close();
this.targets = null;
}
}

Expand Down
21 changes: 21 additions & 0 deletions app/packages/looker/src/processOverlays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

import { CONTAINS, Overlay } from "./overlays/base";
import { ClassificationsOverlay } from "./overlays/classifications";
import HeatmapOverlay from "./overlays/heatmap";
import SegmentationOverlay from "./overlays/segmentation";
import { BaseState } from "./state";
import { rotate } from "./util";

Expand All @@ -30,6 +32,25 @@ const processOverlays = <State extends BaseState>(

if (!(overlay.field && overlay.field in bins)) continue;

// todo: find a better approach / place for this.
// for instance, this won't work in detection overlay, where
// we might want the bounding boxes but masks might not have been loaded
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these instances can be filtered out during overlay creation. But this is a good place for now 👍

if (
overlay instanceof SegmentationOverlay &&
overlay.label.mask_path &&
!overlay.label.mask
) {
continue;
}

if (
overlay instanceof HeatmapOverlay &&
overlay.label.map_path &&
!overlay.label.map
) {
continue;
}

if (!overlay.isShown(state)) continue;

bins[overlay.field].push(overlay);
Expand Down
32 changes: 24 additions & 8 deletions app/packages/looker/src/worker/canvas-decoder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { OverlayMask } from "../numpy";

const offScreenCanvas = new OffscreenCanvas(1, 1);
const offScreenCanvasCtx = offScreenCanvas.getContext("2d", {
willReadFrequently: true,
})!;

const PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
/**
* Reads the PNG's image header chunk to determine the color type.
Expand Down Expand Up @@ -53,20 +58,31 @@ export const decodeWithCanvas = async (blob: Blob) => {
const imageBitmap = await createImageBitmap(blob);
const { width, height } = imageBitmap;

const canvas = new OffscreenCanvas(width, height);
const ctx = canvas.getContext("2d")!;
ctx.drawImage(imageBitmap, 0, 0);
offScreenCanvas.width = width;
offScreenCanvas.height = height;

offScreenCanvasCtx.drawImage(imageBitmap, 0, 0);

imageBitmap.close();

const imageData = ctx.getImageData(0, 0, width, height);
const imageData = offScreenCanvasCtx.getImageData(0, 0, width, height);

if (channels === 1) {
// get rid of the G, B, and A channels, new buffer will be 1/4 the size
const data = new Uint8ClampedArray(width * height);
for (let i = 0; i < data.length; i++) {
data[i] = imageData.data[i * 4];
const rawBuffer = imageData.data;
const totalPixels = width * height;

let read = 0;
let write = 0;

while (write < totalPixels) {
rawBuffer[write++] = rawBuffer[read];
// skip "G,B,A"
read += 4;
}
imageData.data.set(data);

const grayScaleData = rawBuffer.slice(0, totalPixels);
rawBuffer.set(grayScaleData);
}

return {
Expand Down
11 changes: 10 additions & 1 deletion app/packages/spotlight/src/createScrollReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default function createScrollReader(
render: (zooming: boolean, dispatchOffset?: boolean) => void,
getScrollSpeedThreshold: () => number
) {
let animationFrameId: ReturnType<typeof requestAnimationFrame>;
let destroyed = false;
let prior: number;
let scrolling = false;
Expand Down Expand Up @@ -63,7 +64,7 @@ export default function createScrollReader(
updateScrollStatus();
}

requestAnimationFrame(animate);
animationFrameId = requestAnimationFrame(animate);
};

animate();
Expand All @@ -73,6 +74,14 @@ export default function createScrollReader(
destroyed = true;
element.removeEventListener("scroll", scroll);
element.removeEventListener("scrollend", scrollEnd);

if (timeout) {
clearTimeout(timeout);
}

if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
Comment on lines +78 to +84
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like there is redundancy here. I would be surprised if the closure here is causing a memory leak. But it doesn't hurt 👍

},
zooming: () => zooming,
};
Expand Down
Loading