-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
chore: Remove Skia βπ¨ #1740
Conversation
This reverts commit a7c137d.
This comment was marked as spam.
This comment was marked as spam.
Adding Skia and making it work with advance Camera is itself an OG accomplishment. |
Such a thoughtful decision. Totally agree with the direction to remove the overhead for maintenance and reducing complexity. A fork or for consulting seems the right direction. What are the use cases for this? I can primarily think of livestream / video calls where it is used to enhance / augment the stream. |
I could help with the matrix problem, i should figure that out since i am an applied mathematician. Could you provide me with the references that you used or details about what you are trying to do there? |
Thanks @Alexispap, I'll create a fork/new repo with that code where we can experiment then :) Should be possible to just play around with it to figure it out |
@thorbenprimke some use-cases that are all possible with VisionCamera + Skia (with this code that is on that branch actually!):
|
I mainly wanted to create overlays on my camera, i.e. bounding box around an object I detected. Is it not possible now that Skia is not in react-native-vision-camera? How can I do it then? |
@mrousavy So there is no way to draw text or SVG on a frame ? |
Nope. Yet it's in the docs, go figure. Figured it out along with similar issues too late and had major issues due to that. |
The docs say "proof of concept": ..and I even explained why I "removed it again", and how everybody who absolutely needs this feature can reach out to my agency so I can develop this feature for your company.
As I said, if you had major issues and blockers but absolutely need this feature, you could reach out to my agency to get a custom solution developed for your app. Or sponsor the project to collectively fund the development of the Skia integration. |
@tomerh2001 I have been trying to do the same task as you, did you figure out a way to draw the bounding boxes please? |
VisionCamera V4 now has Skia Frame Processors, so this now finally works π |
* Revert "feat: Skia for Android (mrousavy#1731)" This reverts commit a7c137d. * Remove some skia * Remove all the Skia stuff. * Update useFrameProcessor.ts * Update lockfiles * fix: Use native Preview again * Use `OpenGLTexture&` again * Remove `PreviewOutput` (we use `SurfaceView` in parallel) * fix: Log photo widths * fix: Fix cpplint
What
This has been a hard decision to make, but I decided that the Skia integration has to be removed from VisionCamera for various reasons.
I worked long nights, 10h-14h days, weekends and in total about 300 hours on the V3 Skia integration, but I decided that this will not land in production because it significantly increases the complexity of the VisionCamera codebase (see Why down below).
VisionCamera will stay as lean as possible, and my focus is to make it as stable and powerful as possible.
My work here has proven that it is possible to draw onto a Camera Frame in realtime using VisionCamera/React Native/JS/Skia (see for example my blog post on this here https://mrousavy.com/blog/VisionCamera-Pose-Detection-TFLite), and it is also possible for the drawn Frames to end up in the resulting photo or video recording. (See the Stori face filters example on our website, that's 60+FPS in React Native)
I can build this customized solution for you or your business for $$$ (contact us -> https://margelo.io), but I decided to not add this to the public VisionCamera repo because of the significantly increased complexity of also supporting the non-skia pipeline.
Right now, the semi-working version is on npm under
[email protected]
. Maybe I will create a separate repo/fork of that to provide a version of VisionCamera with uses only the Skia pipeline, this would simplify stuff a lot.Note: V3 is still a huge upgrade, there's a ton of changes on the iOS codebase, Frame Processors have been rewritten from scratch, and the entire Android codebase has been rewritten and now uses a GPU accelerated OpenGL pipeline. Stay tuned for the V3 release (next week?)
Journey
I already felt like I was a pretty good developer, but getting into realtime graphics, OpenGL, shaders, surfaces, etc was tough. There's almost no documentation on what I was trying to do in the internet, and even ChatGPT just hallucinated most of the time. OpenGL is tough to set up, passing through Camera frames to surfaces is not easy, and drawing onto them in-between while always staying fully on the GPU is even more tricky.
I learned so many incredible things about Graphics, Android Surfaces, OpenGL, C++, memory management, Cameras (Camera2), Skia etc, and I feel like I now finally have a full understanding of how this all works together.
I wrote my own OpenGL context from scratch that could render an EXTERNAL texture to an offscreen 2D texture, then pass that through using a pass-through shader to three output surfaces (preview, video recorder, frame processor) - that sh*t wasn't easy! But it was pretty damn cool once I got it running. Really fast as well because it's fully built on the GPU.
Why
The normal, non-skia Camera session is already pretty complex. There's multiple outputs that the Camera streams to (PREVIEW, PHOTO, VIDEO), and everything is happening in parallel.
To now draw onto the Camera Frame (and have the drawing show in realtime on the PREVIEW, as well as in the resulting VIDEO and PHOTO), we need to introduce a step in-between here. This step in-between is the Frame Processor, that now returns a texture which everything was rendered into, so we need a separate render pass.
On iOS this was somewhat complex but thanks to
CMSampleBuffer
s being GPU accessible, I got it working in just 2 weeks. This does not yet draw to the resulting video recording though, but that isn't too difficult to add, just some extra flows.On Android, I had to scrap everything on the video pipeline and rewrite it from scratch using OpenGL. I then had two flows, one for skia and one for the default native camera pipeline. The skia one had the extra step in there where the frame processor ran first and rendered everything into a texture, and then I rendered that texture into the outputs.
I finally got this working after 4 weeks, but now the orientation is messed up since Skia doesn't use the same OpenGL transform matrix (float[16]) as the native OpenGL pipeline. Also there's some issues where this goes out of sync and crashes if you flip the Camera, or fast-refresh in react native.
There's just so many added extra ifs/branches that this significantly increases the complexity of the VisionCamera codebase, making it much harder to maintain for me.
Also, the buildscript got much more complex. I now need to check if Skia is installed, and if it is I need to include all skia headers, link against Skia and it's dependencies, and then link against RN Skia. If anything changes on their end (eg a new header in a new folder that I don't know yet), VisionCamera's build will break (unless you disable Skia or stay on an older version that worked).
This is just significantly increasing my maintenance cost, all for something that is not directly Camera related but instead Skia related.
Honestly this is fun and cool and all but only a very small fraction of actual VisionCamera users are going to use this.
Some examples of where the code got really complex:
react-native-vision-camera/android/src/main/cpp/VideoPipeline.cpp
Lines 115 to 176 in a7c137d
react-native-vision-camera/android/src/main/cpp/skia/SkiaRenderer.cpp
Lines 181 to 182 in a7c137d
android.media.Image
. This is a really big change since most frameworks like MLKit or barcode detectors just take anandroid.media.Image
as an input. Now, I had to create a customFrame
object, which had a fully native implementation and used aByteBuffer
of the pixels as it's backing array. This is a GPU -> CPU copy of the Frame.react-native-vision-camera/android/src/main/java/com/mrousavy/camera/frameprocessor/Frame.java
Lines 8 to 61 in a7c137d
react-native-vision-camera/android/src/main/cpp/VideoPipeline.cpp
Lines 129 to 132 in a7c137d
ImageReader
does) to avoid themalloc
/free
of the large pixel buffer on every frame. Right now, it justmalloc
's a new pixel buffer on every single Camera frame, aka 30 to 60 times a second a 3-10MB CPU buffer gets allocated and freed again. Even if you don't need it.react-native-vision-camera/android/src/main/cpp/frameprocessor/java-bindings/JFrame.cpp
Line 57 in a7c137d
AHardwareBuffer*
, but this is again an additional renderpass - you have to set up an OpenGL framebuffer that renders to anEGLImageKHR
, which is just wrapping the HardwareBuffer. Then, after I finally rendered into a GPU HardwareBuffer, what is the user going to do with it? Most people that want to run processing (eg barcode scanning) want to read pixels on the CPU. Only a small fraction of users are actually writing GPU processing code, so all of this additional overhead is unnecessary. (Also, creating anandroid.media.Image
is really weird, I create anImageWriter
that streams into anImageReader
, then get the images there, fill them, and close them again)react-native-vision-camera/android/src/main/cpp/skia/DrawableFrameHostObject.cpp
Lines 38 to 49 in a7c137d
react-native-vision-camera/android/src/main/java/com/mrousavy/camera/CameraSession.kt
Line 205 in a7c137d
react-native-vision-camera/android/CMakeLists.txt
Lines 79 to 130 in a7c137d