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

Grid caching feedback #5548

Open
wants to merge 13 commits into
base: release/v1.4.0
Choose a base branch
from
Open
9 changes: 6 additions & 3 deletions app/packages/core/src/components/Actions/Options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import {
useResetRecoilState,
useSetRecoilState,
} from "recoil";
import { GRID_SETTINGS, QP_MODE } from "../../utils/links";
import {
MANAGING_GRID_MEMORY,
OPTIMIZING_QUERY_PERFORMANCE,
} from "../../utils/links";
import Checkbox from "../Common/Checkbox";
import RadioGroup from "../Common/RadioGroup";
import { gridAutosizing, maxGridItemsSizeBytes } from "../Grid/recoil";
Expand Down Expand Up @@ -199,7 +202,7 @@ const QueryPerformance = () => {
<ActionOption
id="qp-mode"
text="Query Performance mode"
href={QP_MODE}
href={OPTIMIZING_QUERY_PERFORMANCE}
title={"More on Query Performance mode"}
style={{
background: "unset",
Expand Down Expand Up @@ -292,7 +295,7 @@ const Grid = () => {
<>
<ActionOption
id="grid-options"
href={GRID_SETTINGS}
href={MANAGING_GRID_MEMORY}
style={{
background: "unset",
color: theme.text.primary,
Expand Down
15 changes: 8 additions & 7 deletions app/packages/core/src/components/Grid/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
maxGridItemsSizeBytes,
pageParameters,
} from "./recoil";
import useAt from "./useAt";
import useEscape from "./useEscape";
import useEvents from "./useEvents";
import useLabelVisibility from "./useLabelVisibility";
Expand All @@ -23,9 +22,10 @@ import useRecords from "./useRecords";
import useRefreshers from "./useRefreshers";
import useRenderer from "./useRenderer";
import useResize from "./useResize";
import useScrollLocation from "./useScrollLocation";
import useSpotlightPager from "./useSpotlightPager";
import useThreshold from "./useThreshold";
import useUpdates from "./useUpdates";
import useZoomSetting from "./useZoomSetting";

const MAX_INSTANCES = 5151;
const MAX_ROWS = 5151;
Expand All @@ -36,13 +36,14 @@ function Grid() {
const spacing = useRecoilValue(gridSpacing);
const { pageReset, reset } = useRefreshers();
const [resizing, setResizing] = useState(false);
const threshold = useThreshold();
const zoom = useZoomSetting();

useSyncLabelsRenderingStatus();

const records = useRecords(pageReset);

const maxBytes = useRecoilValue(maxGridItemsSizeBytes);
// divide by two, half for the hidden cache and half for max shown
const maxBytes = useRecoilValue(maxGridItemsSizeBytes) / 2;
const cache = useLookerCache({
maxHiddenItems: MAX_INSTANCES,
maxHiddenItemsSizeBytes: maxBytes,
Expand All @@ -63,7 +64,7 @@ function Grid() {
records,
store,
});
const { get, set } = useAt(pageReset);
const { get, set } = useScrollLocation(pageReset);

const setSample = fos.useExpandSample(store);
const autosizing = useRecoilValue(gridAutosizing);
Expand All @@ -90,7 +91,7 @@ function Grid() {

get: (next) => page(next),
onItemClick: setSample,
rowAspectRatioThreshold: threshold,
rowAspectRatioThreshold: zoom,
});
}, [
cache,
Expand All @@ -103,7 +104,7 @@ function Grid() {
resizing,
setSample,
spacing,
threshold,
zoom,
]);

useEscape();
Expand Down
3 changes: 2 additions & 1 deletion app/packages/core/src/components/Grid/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default } from "./Grid";
export { gridZoom, gridZoomRange } from "./recoil";
export { gridZoom } from "./recoil";
export { ZOOM_RANGE } from "./useZoomSetting";
54 changes: 39 additions & 15 deletions app/packages/core/src/components/Grid/recoil.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { DefaultValue, atom, atomFamily, selector } from "recoil";

import { subscribe } from "@fiftyone/relay";
import * as fos from "@fiftyone/state";
import { DefaultValue, atom, atomFamily, selector } from "recoil";
import { MANAGING_GRID_MEMORY } from "../../utils/links";

/**
* Convert a [0, 10] zoom setting to [-15, -1]
*
* @param {number} defaultRange [0, 10] range
* @returns {number} [-15, -1]
*/
const convertDefault = (defaultRange) => {
return -(14 - (defaultRange / 10) * 14 + 1);
};

export const defaultGridZoom = selector<number>({
key: "defaultGridZoom",
Expand All @@ -10,7 +20,7 @@ export const defaultGridZoom = selector<number>({

const gridAutosizingStore = atomFamily<boolean, string>({
key: "gridAutosizingStore",
default: !window.IS_PLAYWRIGHT,
default: true,
effects: (datasetId) => [
fos.getBrowserStorageEffectForKey(`gridAutosizing-${datasetId}`, {
valueClass: "boolean",
Expand Down Expand Up @@ -49,11 +59,23 @@ export const gridSpacing = atom({
],
});

const gridZoomStore = atomFamily<number | null, string>({
key: "gridZoomStore",
default: null,
effects: (datasetId) => [
fos.getBrowserStorageEffectForKey(`gridZoomStore-${datasetId}`, {
valueClass: "number",
}),
],
});

export const gridZoom = selector<number>({
key: "gridZoom",
get: ({ get }) => {
const recommended = get(recommendedGridZoom);
const setting = get(storedGridZoom) ?? get(defaultGridZoom);
const setting =
get(gridZoomStore(get(fos.datasetId) ?? "")) ??
convertDefault(get(defaultGridZoom));
if (
get(gridAutosizing) &&
typeof recommended === "number" &&
Expand All @@ -65,25 +87,25 @@ export const gridZoom = selector<number>({
return setting;
},
set: ({ get, reset, set }, value) => {
const result = value instanceof DefaultValue ? get(defaultGridZoom) : value;
const result =
value instanceof DefaultValue
? convertDefault(get(defaultGridZoom))
: value;

const recommended = get(recommendedGridZoom);
if (typeof recommended === "number" && result < recommended) {
set(fos.snackbarErrors, ["Grid autosizing disabled"]);
set(fos.snackbarLink, {
link: MANAGING_GRID_MEMORY,
message: "Grid autosizing disabled",
});
set(gridAutosizing, false);
reset(recommendedGridZoom);
}

set(storedGridZoom, result);
set(gridZoomStore(get(fos.datasetId) ?? ""), result);
},
});

export const gridZoomRange = atom<[number, number]>({
key: "gridZoomRange",
default: [-5, 10],
effects: [() => subscribe((_, { reset }) => reset(gridZoomRange))],
});

export const gridCropCallback = selector({
key: "gridCropCallback",
get: ({ getCallback }) => {
Expand All @@ -110,7 +132,7 @@ const deviceMemory =

export const maxGridItemsSizeBytes = atom({
key: "maxGridItemsSizeBytes",
default: (deviceMemory / 16) * 1e9,
default: (deviceMemory / 8) * 1e9,
effects: [
fos.getBrowserStorageEffectForKey("maxGridItemsSizeBytes", {
valueClass: "number",
Expand Down Expand Up @@ -170,6 +192,8 @@ export const storedGridZoom = atom<number | null>({
key: "storedGridZoom",
default: null,
effects: [
fos.getBrowserStorageEffectForKey("gridZoom", { valueClass: "number" }),
fos.getBrowserStorageEffectForKey("storedGridZoom", {
valueClass: "number",
}),
],
});
22 changes: 12 additions & 10 deletions app/packages/core/src/components/Grid/useEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import type { Rejected } from "@fiftyone/spotlight";
import * as fos from "@fiftyone/state";
import { useLayoutEffect } from "react";
import { useSetRecoilState } from "recoil";
import { MANAGING_GRID_MEMORY } from "../../utils/links";
import { QP_WAIT, QueryPerformanceToastEvent } from "../QueryPerformanceToast";
import { recommendedGridZoom } from "./recoil";
import type { LookerCache } from "./types";
import type { AtInterface } from "./useAt";
import type { ScrollLocation } from "./useScrollLocation";

export default ({
id,
Expand All @@ -23,11 +24,11 @@ export default ({
cache: LookerCache;
pixels: string;
resizing: boolean;
set: (at: AtInterface) => void;
set: (location: ScrollLocation) => void;
spotlight?: Spotlight<number, fos.Sample>;
}) => {
const handleError = useSetRecoilState(fos.snackbarErrors);
const setRecommendedGridZoomRange = useSetRecoilState(recommendedGridZoom);
const handleAutosize = useSetRecoilState(fos.snackbarLink);
const setRecommendedZoom = useSetRecoilState(recommendedGridZoom);
useLayoutEffect(() => {
if (resizing || !spotlight) {
return undefined;
Expand All @@ -53,11 +54,12 @@ export default ({

const rejected = (event: Rejected) => {
clearTimeout(timeout);
setRecommendedGridZoomRange(
11 - event.recommendedRowAspectRatioThreshold
);
setRecommendedZoom(-event.recommendedRowAspectRatioThreshold);
spotlight.loaded &&
handleError(["That's a lot of data! We've zoomed in a bit"]);
handleAutosize({
link: MANAGING_GRID_MEMORY,
message: "That's a lot of data! We've zoomed in a bit",
});
};

element && spotlight.attach(element);
Expand All @@ -77,11 +79,11 @@ export default ({
}, [
cache,
id,
handleError,
handleAutosize,
pixels,
resizing,
set,
setRecommendedGridZoomRange,
setRecommendedZoom,
spotlight,
]);
};
11 changes: 4 additions & 7 deletions app/packages/core/src/components/Grid/useFontSize.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
import { useCallback } from "react";
import useThreshold from "./useThreshold";
import useZoomSetting from "./useZoomSetting";

const MAX = 14;
const MIN = 10;
const SCALE_FACTOR = 0.09;

export default (id: string) => {
const threshold = useThreshold();
const zoom = useZoomSetting();

return useCallback(() => {
const width = document.getElementById(id)?.getBoundingClientRect().width;
if (!width) {
throw new Error("unexpected");
}

return Math.max(
Math.min((width / threshold(width)) * SCALE_FACTOR, MAX),
MIN
);
}, [id, threshold]);
return Math.max(Math.min((width / zoom(width)) * SCALE_FACTOR, MAX), MIN);
}, [id, zoom]);
};
3 changes: 3 additions & 0 deletions app/packages/core/src/components/Grid/useLabelVisibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { useCallback } from "react";
import { useRecoilCallback } from "recoil";
import { gridActivePathsLUT } from "../Sidebar/useDetectNewActiveLabelFields";

/**
* Callbacks for the cache to sync {@link gridActivePathsLUT}
*/
export default () => ({
onDispose: useCallback((key: string) => gridActivePathsLUT.delete(key), []),
onSet: useRecoilCallback(
Expand Down
6 changes: 6 additions & 0 deletions app/packages/core/src/components/Grid/useLookerCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ export default function useLookerCache({
onSet?: (key: string) => void;
reset: string;
}) {
useEffect(() => {
const listener = () => cache.empty();
document.addEventListener("visibilitychange", listener);
return () => document.removeEventListener("visibilitychange", listener);
}, []);

const cache = useMemoOne(() => {
/** CLEAR CACHE WHEN reset CHANGES */
reset;
Expand Down
2 changes: 1 addition & 1 deletion app/packages/core/src/components/Grid/useRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default function ({

if (zooming) {
// we are scrolling fast, skip creation
return 0;
return Promise.resolve(0);
}

const result = store.get(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { useMemo } from "react";
import { useRecoilCallback, useRecoilTransaction_UNSTABLE } from "recoil";
import { gridAt, gridOffset, gridPage } from "./recoil";

export interface AtInterface {
export interface ScrollLocation {
at: ID;
page: number;
offset: number;
}

export default function useAt(pageReset: string) {
export default function useScrollLocation(pageReset: string) {
const getPage = useRecoilTransaction_UNSTABLE(
({ get }) =>
(ref: { current: number | null }) => {
Expand Down Expand Up @@ -58,10 +58,10 @@ export default function useAt(pageReset: string) {
// when scrolling ends, use set to save the grid location to recoil
const set = useRecoilTransaction_UNSTABLE(
({ set }) =>
(at: AtInterface) => {
set(gridPage, at.page);
set(gridAt, at.at.description);
set(gridOffset, at.offset);
(location: ScrollLocation) => {
set(gridPage, location.page);
set(gridAt, location.at.description);
set(gridOffset, location.offset);
},
[]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ const WIDEST = 1200;
const WIDE = 1000;
const NORMAL = 800;

/**
* Aspect ratio range is 1 to 15
*
* - Smaller aspect ratio -> more zoom
* - Larger aspect ratio -> less zoom
*
* Zoom range is then -15 to -1 for the slider all the way to the right to mean
* "max zoom"
*/
export const ZOOM_RANGE = [-15, -1];

/**
* Determines a maximium aspect ratio threshold for grid rows based on the
* container width. The smaller the container width, the smaller the maximum
Expand All @@ -15,17 +26,17 @@ export default () => {
const zoom = useRecoilValue(gridZoom);
return useCallback(
(width: number) => {
let min = 6;
let min = -8;

if (width >= WIDEST) {
min = -4;
min = -15;
} else if (width >= WIDE) {
min = -2;
min = -13;
} else if (width >= NORMAL) {
min = 2;
min = -10;
}

return 11 - Math.max(min, zoom);
return -Math.max(min, zoom);
},
[zoom]
);
Expand Down
Loading
Loading