Pause animation when FPS gets too low

Woof, the "Swirl of Power Effect" item tanks my CPU waaay too much

(I bet it's those 7000x7000 PNGs lolol 😬)

Anyway, before thinking about optimizing specific issues, I'm just adding this emergency switch: if we detect FPS < 2 on any layer, we just pause the whole outfit, until the user decides to unpause.
This commit is contained in:
Emi Matchu 2021-06-16 16:26:24 -07:00
parent 307ab932c8
commit bb5ec56752
2 changed files with 44 additions and 5 deletions

View file

@ -10,6 +10,7 @@ function OutfitMovieLayer({
height,
isPaused = false,
onLoad = null,
onLowFps = null,
}) {
const [stage, setStage] = React.useState(null);
const [library, setLibrary] = React.useState(null);
@ -165,6 +166,8 @@ function OutfitMovieLayer({
return;
}
const targetFps = library.properties.fps;
let lastFpsLoggedAtInMs = performance.now();
let numFramesSinceLastLogged = 0;
const intervalId = setInterval(() => {
@ -181,16 +184,20 @@ function OutfitMovieLayer({
const roundedFps = Math.round(fps * 100) / 100;
console.debug(
`[OutfitMovieLayer] FPS: ${roundedFps} (Target: ${library.properties.fps}, ${numFramesSinceLastLogged}, ${timeSinceLastFpsLoggedAtInSec}) (${libraryUrl})`
`[OutfitMovieLayer] FPS: ${roundedFps} (Target: ${targetFps}) (${libraryUrl})`
);
if (onLowFps && fps < 2) {
onLowFps(fps);
}
lastFpsLoggedAtInMs = now;
numFramesSinceLastLogged = 0;
}
}, 1000 / library.properties.fps);
}, 1000 / targetFps);
return () => clearInterval(intervalId);
}, [libraryUrl, stage, updateStage, movieClip, library, isPaused]);
}, [libraryUrl, stage, updateStage, movieClip, library, isPaused, onLowFps]);
// This effect keeps the `movieClip` scaled correctly, based on the canvas
// size and the `library`'s natural size declaration. (If the canvas size

View file

@ -1,5 +1,12 @@
import React from "react";
import { Box, DarkMode, Flex, Text, useColorModeValue } from "@chakra-ui/react";
import {
Box,
DarkMode,
Flex,
Text,
useColorModeValue,
useToast,
} from "@chakra-ui/react";
import LRU from "lru-cache";
import { WarningIcon } from "@chakra-ui/icons";
import { ClassNames } from "@emotion/react";
@ -54,6 +61,9 @@ export function useOutfitPreview({
onChangeHasAnimations = null,
...props
}) {
const [isPaused, setIsPaused] = useLocalStorage("DTIOutfitIsPaused", true);
const toast = useToast();
const appearance = useOutfitAppearance({
speciesId,
colorId,
@ -70,7 +80,26 @@ export function useOutfitPreview({
layersHaveAnimations,
} = usePreloadLayers(visibleLayers);
const [isPaused] = useLocalStorage("DTIOutfitIsPaused", true);
const onLowFps = React.useCallback(
(fps) => {
setIsPaused(true);
console.warn(`[OutfitPreview] Pausing due to low FPS: ${fps}`);
if (!toast.isActive("low-fps-warning")) {
toast({
id: "low-fps-warning",
status: "warning",
title: "Sorry, the animation was lagging, so we paused it! 😖",
description:
"We do this to help make sure your machine doesn't lag too much! " +
"You can unpause the preview to try again.",
duration: null,
isClosable: true,
});
}
},
[setIsPaused, toast]
);
React.useEffect(() => {
if (onChangeHasAnimations) {
@ -100,6 +129,7 @@ export function useOutfitPreview({
loadingDelayMs={loadingDelayMs}
spinnerVariant={spinnerVariant}
onChangeHasAnimations={onChangeHasAnimations}
onLowFps={onLowFps}
doTransitions
isPaused={isPaused}
{...props}
@ -122,6 +152,7 @@ export function OutfitLayers({
spinnerVariant = "overlay",
doTransitions = false,
isPaused = true,
onLowFps = null,
...props
}) {
const [hiResMode] = useLocalStorage("DTIHiResMode", false);
@ -228,6 +259,7 @@ export function OutfitLayers({
width={canvasSize}
height={canvasSize}
isPaused={isPaused}
onLowFps={onLowFps}
/>
) : (
<Box