-
Notifications
You must be signed in to change notification settings - Fork 481
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
[iOS] Image re-rendering with potential memory leak #2909
Comments
Currently we are not using setExternalMemory pressure from Hermes and
therefore this skiaImage gets garbage collected eventually but at a
much later time than expected.
calling skiaImage.dispose() when setting a new image should help.
I would be curious to see the behavior you are experiencing. In my
case we can log that all objects are properly deleted from the garbage
collector but I still see a very high memory pressure, not sure why. I
asked the questions on the hermes github:
facebook/hermes#1518
…On Thu, Jan 23, 2025 at 3:22 PM Dimuthu Wannipurage ***@***.***> wrote:
Description
I am trying to build a pdf document viewer using react native skia and reanimated. This requires rendering bitmap tiles of a pdf page at different resolution levels when we peform pinch zoom on canvas. As a result of that, I have to regenerat the Skia Image multiple times and assign to the same image component. Please have a look at following simplified code sample. The issue I am struggling is, when I zoom in and generate images of 3000 x 3000 pixels, memory usage goes up as expected but when I zoom out and generate 300x300 bitmaps, memory does not go back down. I initially though that this is related to delayed garbage collection but it seems like not the case as the memory is occupied permanantly for a long period of time. I lookd at the memory profile and there is a lot of objects with stuck at SkData::MakeWithCopy and I think those are the images I created on demad when the pinch zoom ended. Can you help me to figure out why this memory leak is happening and what could be the potential resolution for this?
import React, { useEffect, useState } from 'react';
import { View, StyleSheet } from 'react-native';
import { Canvas, Skia, AlphaType, ColorType, Fill, Image, SkImage } from ***@***.***/react-native-skia';
import { GestureDetector, Gesture, GestureHandlerRootView } from 'react-native-gesture-handler';
import Animated, {
useSharedValue,
useDerivedValue,
runOnJS,
} from 'react-native-reanimated';
function App(): React.JSX.Element {
const tileSize = 512;
const scale = useSharedValue(1);
const translationX = useSharedValue(0);
const translationY = useSharedValue(0);
const [scaleEnd, setScaleEnd] = useState(1);
const [skiaImage, setSkiaImage] = useState<SkImage>();
const createTile = (tileSize: number) => {
const pixels = new Uint8Array(tileSize * tileSize * 4);
pixels.fill(255);
let i = 0;
for (let x = 0; x < tileSize; x++) {
for (let y = 0; y < tileSize; y++) {
pixels[i++] = (x * y) % 255;
}
}
const data = Skia.Data.fromBytes(pixels);
const img = Skia.Image.MakeImage(
{
width: tileSize,
height: tileSize,
alphaType: AlphaType.Opaque,
colorType: ColorType.RGBA_8888,
},
data,
tileSize * 4
);
return img;
}
if (skiaImage === null) {
return <View style={styles.container} />;
}
const panGesture = Gesture.Pan().onChange((e) => {
translationX.value += e.changeX;
translationY.value += e.changeY;
});
const scaleGesture = Gesture.Pinch().onChange((e) => {
scale.value += e.scaleChange - 1;
}).onEnd(() => {
runOnJS(() => {
setScaleEnd(scale.value);
})();
}).runOnJS(true);
useEffect(() => {
const skImg = createTile(tileSize * scale.value);
console.log("Skia image calculating for tile size " + tileSize * scale.value);
if (skImg !== null) {
setSkiaImage(skImg);
}
}, [scaleEnd]);
return (
<GestureHandlerRootView>
<View style={{ flex: 1 }}>
<Canvas style={{ flex: 1 }}>
<Fill color="pink" />
<Image image={skiaImage} x={translationX} y={translationY} width={useDerivedValue(() => tileSize * scale.value)} height={useDerivedValue(() => tileSize * scale.value)} />
</Canvas>
<GestureDetector gesture={Gesture.Race(panGesture, scaleGesture)}>
<Animated.View style={StyleSheet.absoluteFill} />
</GestureDetector>
</View>
</GestureHandlerRootView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "pink",
},
canvasContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
canvas: {
flex: 1,
},
animatedCanvas: {
flex: 1,
},
});
export default App;
React Native Skia Version
1.9.0
React Native Version
0.76.5
Using New Architecture
Enabled
Steps to Reproduce
Run the attached code and try multiple pinch zoom on iOS
Snack, Code Example, Screenshot, or Link to Repository
Screenshot.2025-01-23.at.9.04.32.AM.png (view on web) Screenshot.2025-01-23.at.9.18.32.AM.png (view on web)
—
Reply to this email directly, view it on GitHub or unsubscribe.
You are receiving this email because you are subscribed to this thread.
Triage notifications on the go with GitHub Mobile for iOS or Android.
|
@wcandillon I tried to dispose the old image but that raised some EXEC_BAD_ACCESS errors on C++. Please let me whether following way is correct?
![]() |
Had a similiar issue only on iOS when re-rendering the skia image from a base64 png it would cause a crash. Can also provide some logs if it helps. |
yes any log/reproduction would be extremely helpful Ronald, did get a
user report that hinted at a similar problem.
…On Wed, Feb 5, 2025 at 10:56 PM Ronald Goedeke ***@***.***> wrote:
Had a similiar issue only on iOS when re-rendering the skia image from a base64 png it would cause a crash. Can also provide some logs if it helps.
—
Reply to this email directly, view it on GitHub or unsubscribe.
You are receiving this email because you commented on the thread.
Triage notifications on the go with GitHub Mobile for iOS or Android.
|
Skia version: "@shopify/react-native-skia": "^1.11.4"
Issue occurs when creating an image like this:
Base64 string of the image image.txt |
@ronickg thanks a lot, this error seems consistent with some other reports we've seen. Just to make sure, it's not as simple as running the snippet you kindly sent to reproduce the error right? I have to assume that it used in another runtime context (reanimated worklet)? |
@wcandillon It should be as simple as just running the code without any worklets. To reproduce the error I just created a screen using expo router and just navigated to it. When wrapping it in a memo without any dependencies it works fine as it doesn't re-render, as the issue happens as soon as it needs to run the code a second time. And only had this issue on iOS, on android it works. |
I'll make a new project repo to mimic the error which should make debugging easier. |
Thanks a lot 🫶🏻 |
Okay, i wasn't able to reproduce the issue in a new repo, so I went back to testing my initial one. And initially I though it was crashing even with just the simple code snippet above, but I think i may have not correctly copied the base64 string. So from all that yes I do think its due to the worklets. I am using the latest react native worklets core and set the base64 string like so:
This function is called from inside the frameProcessor in vision camera. Which I am sure is somehow causing the crash. So I don't think there is anything wrong on the skia side. Maybe this helps someone else who has the same issue. Thx |
@DImuthuUpe any chance you could update your example at the top of the
issue to use .dipose (with the crash, and then I can investigate it?)
…On Thu, Feb 6, 2025 at 2:05 PM Ronald Goedeke ***@***.***> wrote:
Okay, i wasn't able to reproduce the issue in a new repo, so I went back to testing my initial one. And initially I though it was crashing even with just the simple code snippet above, but I think i may have not correctly copied the base64 string. So from all that yes I do think its due to the worklets. I am using the latest react native worklets core and set the base64 string like so:
const saveImagesAndStop = Worklets.createRunOnJS(
async (imagesString: string) => {
setSnapShotBase64(imagesString);
}
);
This function is called from inside the frameProcessor in vision camera. Which I am sure is somehow causing the crash.
So I don't think there is anything wrong on the skia side. Maybe this helps someone else who has the same issue. Thx
—
Reply to this email directly, view it on GitHub or unsubscribe.
You are receiving this email because you commented on the thread.
Triage notifications on the go with GitHub Mobile for iOS or Android.
|
@wcandillon here it is. For some reasosn, this does not crash for me now but still the memory does not come down when I zoom out. Try to zoom in until you see memory usage goes in beyond 1 gb and try to zoom out. You might notice that the memory does not scale back to 500MBs
|
The topic of the memory not coming down is one I need to be more educated on. I would like to learn more about Hermes behaviours there and understand better this setup and I asked the question at facebook/hermes#1518 but didn't get any feedback on it yet. |
I did some customizations to Skia C++ code to monitor the reference count of SKData shared pointer and in some cases, that does not go beyond 1 which causes the memory leak in my case (even if I call dispose of SkData and SkImage). I changed the logic to manually delete the pointer SkData when I call dispose from JS side and then the memory came down but this is causing crashes on long run. I am trying to come up with the minimal code to repreoduce this without gesture handlers. I think for this particular problem, I can clearly see a memory leak and SkData objects are not getting deleted when I did profiling on Xcode |
I seem to have memory leak issues with skia as well lately. Can't tell if it's related at all, not sure how I can debug this. When I activate skiaFrameProcessor, you can see the memory going up until app crashes. all the flag does is activate
where: const defaultPaint = Skia.Paint() ![]() Update: Able to fix the issue be downgrading version of: |
Description
I am trying to build a pdf document viewer using react native skia and reanimated. This requires rendering bitmap tiles of a pdf page at different resolution levels when we peform pinch zoom on canvas. As a result of that, I have to regenerat the Skia Image multiple times and assign to the same image component. Please have a look at following simplified code sample. The issue I am struggling is, when I zoom in and generate images of 3000 x 3000 pixels, memory usage goes up as expected but when I zoom out and generate 300x300 bitmaps, memory does not go back down. I initially though that this is related to delayed garbage collection but it seems like not the case as the memory is occupied permanantly for a long period of time. I lookd at the memory profile and there is a lot of objects with stuck at SkData::MakeWithCopy and I think those are the images I created on demad when the pinch zoom ended. Can you help me to figure out why this memory leak is happening and what could be the potential resolution for this?
React Native Skia Version
1.9.0
React Native Version
0.76.5
Using New Architecture
Steps to Reproduce
Run the attached code and try multiple pinch zoom on iOS
Snack, Code Example, Screenshot, or Link to Repository
The text was updated successfully, but these errors were encountered: