Handle memory issues in OutfitMovieLayer
I think what's happening in Sentry error IMPRESS-2020-1F is that mobile devices are running out of memory, so `canvas.getContext("2d")` returns null. Now, we have a UI affordance to let you know when this is probably what's happening! Also, when researching this, I learned about a Safari bug where you need to manually garbage-collect your own canvas data. It's possible that Safari users have been having particular trouble with memory leaks over long sessions? I'm not sure, but it seems like a good idea to add this small garbage-collection code!
This commit is contained in:
parent
5d827bc78d
commit
05282f4b7b
1 changed files with 35 additions and 7 deletions
|
@ -40,7 +40,7 @@ function OutfitMovieLayer({
|
||||||
console.error(`Error rendering movie clip ${libraryUrl}`);
|
console.error(`Error rendering movie clip ${libraryUrl}`);
|
||||||
logAndCapture(e);
|
logAndCapture(e);
|
||||||
toast({
|
toast({
|
||||||
status: "error",
|
status: "warning",
|
||||||
title:
|
title:
|
||||||
"Hmm, we're maybe having trouble playing one of these animations.",
|
"Hmm, we're maybe having trouble playing one of these animations.",
|
||||||
description:
|
description:
|
||||||
|
@ -59,19 +59,47 @@ function OutfitMovieLayer({
|
||||||
|
|
||||||
// This effect gives us a `stage` corresponding to the canvas element.
|
// This effect gives us a `stage` corresponding to the canvas element.
|
||||||
React.useLayoutEffect(() => {
|
React.useLayoutEffect(() => {
|
||||||
if (loadingDeps || !canvasRef.current) {
|
const canvas = canvasRef.current;
|
||||||
|
|
||||||
|
if (loadingDeps || !canvas) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canvas.getContext("2d") == null) {
|
||||||
|
console.warn(`Out of memory, can't use canvas for ${libraryUrl}.`);
|
||||||
|
toast({
|
||||||
|
status: "warning",
|
||||||
|
title: "Oops, too many animations!",
|
||||||
|
description:
|
||||||
|
`Your device is out of memory, so we can't show any more ` +
|
||||||
|
`animations. Try removing some items, or using another device.`,
|
||||||
|
duration: null,
|
||||||
|
isClosable: true,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setStage((stage) => {
|
setStage((stage) => {
|
||||||
if (stage && stage.canvas === canvasRef.current) {
|
if (stage && stage.canvas === canvas) {
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new window.createjs.Stage(canvasRef.current);
|
return new window.createjs.Stage(canvas);
|
||||||
});
|
});
|
||||||
return () => setStage(null);
|
|
||||||
}, [loadingDeps]);
|
return () => {
|
||||||
|
setStage(null);
|
||||||
|
|
||||||
|
if (canvas) {
|
||||||
|
// There's a Safari bug where it doesn't reliably garbage-collect
|
||||||
|
// canvas data. Clean it up ourselves, rather than leaking memory over
|
||||||
|
// time! https://stackoverflow.com/a/52586606/107415
|
||||||
|
// https://bugs.webkit.org/show_bug.cgi?id=195325
|
||||||
|
canvas.width = 0;
|
||||||
|
canvas.height = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [loadingDeps, libraryUrl, toast]);
|
||||||
|
|
||||||
// This effect gives us the `library` and `movieClip`, based on the incoming
|
// This effect gives us the `library` and `movieClip`, based on the incoming
|
||||||
// `libraryUrl`.
|
// `libraryUrl`.
|
||||||
|
@ -94,7 +122,7 @@ function OutfitMovieLayer({
|
||||||
setMovieClip(movieClip);
|
setMovieClip(movieClip);
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.error("Error loading outfit movie layer", e);
|
console.error(`Error loading outfit movie layer: ${libraryUrl}`, e);
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
|
Loading…
Reference in a new issue