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

First frame renders transparent canvas #2968

Open
1 task done
yard opened this issue Feb 18, 2025 · 9 comments · Fixed by #2991
Open
1 task done

First frame renders transparent canvas #2968

yard opened this issue Feb 18, 2025 · 9 comments · Fixed by #2991
Labels
bug Something isn't working released

Comments

@yard
Copy link

yard commented Feb 18, 2025

Description

Our app uses a Skia's canvas as a background for the home screen, and the first render always comes through transparent, although there are elements to be rendered.

We have debugged the issue and believe there are 2 places that cause this behaviour: Canvas component supplying children collection in useEffect and missing waitUntilCompleted call to commandBuffer in MetalWindowContext class.

We have attempted to fix it on our end by adding root.render(children) to the memoized creation of SkiaSGRoot inside Canvas component and added a bool flag to present() method to enforce command buffer completion when immediate draw is requested (e.g. from drawRect of the view).

We are happy to open a PR with these fixes, but figured it's a good idea to create a bug first and get a confirmation that it is a real issue and our solution is worth submitting.

React Native Skia Version

1.11.1

React Native Version

0.76.6

Using New Architecture

  • Enabled

Steps to Reproduce

  1. Add a canvas with some background color to the view
  2. Add an element (e.g. a circle) to the canvas
  3. Put the canvas onto any screen of the app
  4. Navigate to the screen and observe a flash

Snack, Code Example, Screenshot, or Link to Repository

Please have a look at two recordings from iOS Simulator (iPhone 16 Pro, iOS 18.1) where a screen with Canvas as a background opens (following a white flash). In both cases, the Canvas's style set to [StyleSheet.absoluteFill, {backgroundColor: "lime"}] and the first video shows a green flash, which is not present in the second video.

before fix after fix
Simulator.Screen.Recording.-.iPhone.16.Pro.-.2025-02-18.at.17.11.45.mp4
Simulator.Screen.Recording.-.iPhone.16.Pro.-.2025-02-18.at.17.11.59.mp4
@yard yard added the bug Something isn't working label Feb 18, 2025
@wcandillon
Copy link
Contributor

Thank you for this. I'm definitely interesting to have an example where I can reproduce the issue and also see the suggested fix. Thank you for looking into this.

@yard
Copy link
Author

yard commented Feb 20, 2025

Hey @wcandillon, here is a simple snack demonstrating the issue. A canvas with lime background is rendering a blue circle (which covers the whole canvas) after 1s delay. If you run it on device, you can notice a lime flash which should not be there (as there are just two states: canvas not shown completely and canvas is shown with a blue circle) – or just record the screen and inspect the video frame by frame.

I will prepare a PR today or tomorrow.

P.S. Love your videos on RN skia, great job!

@wcandillon
Copy link
Contributor

Thank you for the clear reproduction. I was able to fix the issue with this change: #2991 Can you confirm it on your side?

@yard
Copy link
Author

yard commented Mar 3, 2025

Thank you for the clear reproduction. I was able to fix the issue with this change: #2991 Can you confirm it on your side?

I am afraid I have tried that and it doesn't seem to be enough. I have pushed an example app to my fork into fabric app.

As far as I understand the issue, it is caused by 2 elements: populating the root with the delay and the native part not rasterizing the canvas on time. The first part can be addressed by either my patch (populating the root right after the construction) or by yours (using useLayoutEffect instead of useEffect). I sort of prefer mine since it means the root object never exists in an "empty" state, but happy to follow your advice here. The second part does require an explicit command buffer flush as the OS essentially asks us to render view's surface by calling drawRect method and we have to make sure the image will get rasterized and presented before returning from this method, as otherwise the OS reservres the right to present whatever was rendered there at the time (thus, the flash of the canvas' background color, which gets applied as a background color of the view since it's a style prop).

P.S. It's quite easy to miss, so I normally record the video and then scrub through around the switch to see if there's a lime frame.

@yard
Copy link
Author

yard commented Mar 3, 2025

Ok it looks like even my changes are not enough: I have changed the example to continiously show and hide the canvas, and I do see lime flashes crippling through. I will post an update here once I figure out what is missing.

@wcandillon
Copy link
Contributor

wcandillon commented Mar 3, 2025 via email

Copy link
Contributor

github-actions bot commented Mar 4, 2025

🎉 This issue has been resolved in version 1.11.10 🎉

The release is available on:

Your semantic-release bot 📦🚀

@yard
Copy link
Author

yard commented Mar 4, 2025

Shall we re-open this?

@wcandillon wcandillon reopened this Mar 4, 2025
@wcandillon
Copy link
Contributor

yes correct

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working released
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants