-
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
feat: Use Metal for creating PlatformBuffers #2356
feat: Use Metal for creating PlatformBuffers #2356
Conversation
…ple PixelFormats (YUV) Currently it always returns RGB. In the future it might also support YUV
The PR looks good however it doesn't pass the tests on iOS. Metal is unfortunately not available on Github Action so we can only run such test locally (see actions/runner-images#6138) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
commented on the failing tests on the PR comment thread
@wcandillon I refactored |
very cool, I'm probably merge the makePlatformBuffer(). |
Yea, the call to Either way, I think the performance difference will come into effect once you actually submit work to the GPU (flush()), as it will need to upload the data to the GPU first with the |
indeed
first let’s check if the gpu buffer works with the current method (it will
not most likely) then we will have to use this this.
…On Thu 11 Apr 2024 at 18:33, Marc Rousavy ***@***.***> wrote:
Yea, the call to MakeImageFromPlatformBuffer(..) has the same performance.
I think the performance difference will come into effect once you actually
submit work to the GPU (flush()), as it will need to upload the data to the
GPU first with the SkData approach, and doesn't need to do that with the
Metal Texture approach.
At least that's my understanding of it, 99% sure.
—
Reply to this email directly, view it on GitHub
<#2356 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AACKXVXHL7XYDVQHVZUTTKTY423NBAVCNFSM6AAAAABGCAQLB6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDANJQGA4DANZSGY>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
As an extreme stress-test, we can try react-native-vision-camera I think we need this PR either way, but we can test this just for fun later if you want? |
Yea for the future we can add branches for both, right?
We don't need this for 99% of the cases, but maybe in the future we could add that if someone really needs CPU-based CMSampleBuffers (aka CVPixelBuffers that have been manually created from |
this new debug method for creating "test" platform buffer is the one we need to use. However is it also compatible with the MakeImageFromPlatformBuffer implementation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current method (in main) supports both types of SampleBuffers and seems to match exactly performance-wise (while abstracting the texture creation to Skia which is preferable).
Will run more benchmarks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to be benchmarked a bit more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to be benchmarked
This PR has two potentially compelling changes:
Right now I am observing neither but I will keep testing. |
This PR changes the implementation of both SkImage -> PlatformBuffer and PlatformBuffer -> SkImage to use GPU Metal Textures. The GPU Metal Texture is always preferred because that's the format it is in when you conventionally get a CMSampleBuffer - either from the Camera (AVCaptureVideoDataOutput) or when streaming frames from an existing video file. So GPU Metal Texture (aka IOSurface) is definitely the way to go from my side, I'm not sure if I understand what the counterargument here is? |
As for benchmark; my assumptions are that the SkData approach uses more memory (explicit CPU copy), as well as increased latency when rendering. We can test this by streaming 4k 120 FPS buffers in VisionCamera and compare the bigger picture - including GPU flush and draw times - not just the PlatformBuffer -> SkImage conversion itself. Let me know if you want to do that before merging that PR, but then I can only do it tomorrow (dont have a pc with me now) |
@wcandillon and I have chatted offline for a while about this. I am convinced that Metal Buffers are the way to go (that's what every guide on the internet including apple developer documentation suggests), but William made the discovery that the "CPU-approach" (aka getting the raw bytes of the To clarify; these are the two approaches we have:
I was actually quite surprised how fast the CPU-based approach is with SkData, but nevertheless the Metal approach is faster and more importantly; more energy efficient. Here are my findings:
And here's the screenshots from Xcode, where you can see that the SkData based approach throttles after time (yes I let the phone cool down in between): Xcode Profiling screenshotsGPU-basedCPU-basedSo my conclusion is, that the GPU-based approach is faster, and especially more consistent because after a while of leaving it running it continues to maintain that FPS rate, whereas the CPU usage likely got thermal throttled because of the huge FPS drops. Also it used more memory. |
Metal automatically flushes texture cache after 1 second.
|
||
// pragma MARK: CVPixelBuffer -> Skia Texture | ||
|
||
GrBackendTexture SkiaCVPixelBufferUtils::getSkiaTextureForCVPixelBufferPlane( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this part of this PR? I've not sure what it does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it converts one plane of a CMSampleBuffer to a GrBackendTexture
. In RGB, we currently only have one plane so this is just used directly for RGB buffers. See here:
react-native-skia/package/ios/RNSkia-iOS/SkiaCVPixelBufferUtils.mm
Lines 70 to 73 in fbf397f
GrBackendTexture SkiaCVPixelBufferUtils::RGB::getSkiaTextureForCVPixelBuffer( | |
CVPixelBufferRef pixelBuffer) { | |
return getSkiaTextureForCVPixelBufferPlane(pixelBuffer, /* planeIndex */ 0); | |
} |
But for YUV this is going to be relevant since we have multiple planes, so we need to call that method more often (once for Y plane and once for UV plane).
So we need this regardless of RGB or YUV.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RGB buffers are also split by planes. It's just always one single plane in RGB. The methods that don't require you to pass a plane index are just convenience methods for the plane methods with index 0.
So we need this method both for RGB and for YUV.
It looks good, there is just some code that referes to yuv planes that maybe shouldn't be here |
if (textureCache == nil) { | ||
// Create a new Texture Cache | ||
auto result = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, | ||
MTLCreateSystemDefaultDevice(), nil, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes I guess that's fine. So far in our code we made it so we invoke this function only once but I guess that's fair.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea I had code in #2352 where I moved that into a separate public function so that we can get that context elsewhere. This included the device
. But I think it's fair like now, maybe in the future if we need this somewhere else as well we can refactor a bit.
// pragma MARK: getTextureCache() | ||
|
||
CVMetalTextureCacheRef SkiaCVPixelBufferUtils::getTextureCache() { | ||
static thread_local CVMetalTextureCacheRef textureCache = nil; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this need to be in the metal context (which is thread safe and that you can pass as an argument).
That way the thread specific code is only at a single place
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm I don't really want to separate out the declaration of this variable (static thread_local) to somewhere else, especially not another file, when the initialization (the line below) is here..
If you are talking about also moving the initialization out to ThreadSkiaContext
then I wouldn't do that either, as we shouldn't always initialize a Metal Texture pool unless we really need it.
🎉 This PR is included in version 1.2.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Changes the implementation of
MakeImageFromPlatformBuffer
(which currently usesSkData
) to use Metal instead (MTLTexture
).In theory, this should be more efficient as it is operating on the GPU, whereas the previous approach (
SkData
) uses CPU-shared memory (IOSurface), but I haven't tested the performance yet.Either way, this PR also reorganizes the code a bit to be more flexible around the PixelBuffer formats and should allow YUV buffers as well (future PR).
I added
getCVPixelBufferBaseFormat(..)
which returns an enum, currently onlyrgb
. In the next follow-up PR that will be extended to also supportyuv
, and I will addCVPixelBufferUtils::YUV
utils.