-
Notifications
You must be signed in to change notification settings - Fork 6k
Add native Android image decoders supported by API 28+ #26746
Conversation
726902a
to
29d5d5c
Compare
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.
Needs tests.
/cc @blasten FYI, @jason-simmons
// Since this decoder is only installed for API 28 and 29, there is no | ||
// supported way to work around this unfortunate copy. It's possible to | ||
// produce an `SkImage` direcly from an `AHardwareBuffer`, but | ||
// `AndroidBitmap_getHardwareBuffer` and `Bitmap.getHardwareBuffer` are only | ||
// available in API 30+ and 31+ respectively. |
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.
There is! But we'd have to do some dynamic lookups to see whether symbols are available.
I'm ok having this in place without doing that yet, but we should file an issue if one doesn't exist and add a TODO here for it.
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.
I got most of the way through resolving/calling AndroidBitmap_getHardwareBuffer
at runtime, but even SkImage::MakeFromAHardwareBuffer and the underlying GrAHardwareBufferImageGenerator are preprocessed out with with __ANDROID_API__ >= 26
due to reliance on e.g. AHardwareBuffer_acquire
... :(
I could probably propose a reasonable patch to cheaply resolve the symbols in Skia instead of gating like this, but we gotta make some behavior decisions/maybe provide a way to query support, and so I made a note to discuss with Skia friends and follow up.
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.
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.
I think it would make sense to update Skia to check for the symbols rather than using the prepocessor to remove them entirely.
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.
I think for now we should just add a TODO to fix this when Skia API is available.
shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
Outdated
Show resolved
Hide resolved
@dnfield Ah sorry, this is still quite broken/untested, but thanks for the helpful comments. :) |
Ahh ok, I saw the review request and started looking. Feel free to let me know when it's ready for another pass. |
b27c8d7
to
96dc5b5
Compare
7b196ce
to
36358d3
Compare
aebdac6
to
4d859e6
Compare
93cdc4e
to
846f27c
Compare
@dnfield I finally got around to fixing this up and it's good to go for another pass. |
Hmm, there's definitely a crash bug when running on arm64 caused by the
|
846f27c
to
f8d3df8
Compare
All fixed! |
// The generator kicks off an IO task to decode everything, and calls to | ||
// "GetInfo()" block until the header has been decoded. The decoder explicitly | ||
// marks the height as -1 if the image is invalid or if the SDK image decoder | ||
// is unavailable. |
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 true? I'm not seeing any IO tasks getting kicked off, decoding is all happening on the platform thread right?
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.
AndroidImageGenerator::MakeFromData
kicks off the task and is the only reasonable way to produce this ImageGenerator
.
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.
Looking closer
f8d3df8
to
54d2a59
Compare
std::unique_ptr<ImageGenerator> AndroidImageGenerator::MakeFromData( | ||
sk_sp<SkData> data, | ||
fml::RefPtr<fml::TaskRunner> task_runner) { | ||
auto* generator = new AndroidImageGenerator(std::move(data)); |
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.
Please use std::make_unique
instead of the manual memory management here.
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.
Ownership semantics needed to be deferred here due to the task capturing the generator, but I just realized there's a possible use-after-free that could happen if the framework were to discard/collect an ImageDescriptor without attempting to decode first (doesn't currently happen AFAICT).
I changed the API to serve shared_ptr
s because of this.
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.
LGTM with nit about unique_ptr usage.
a5c427b
to
954170f
Compare
return; | ||
} | ||
|
||
SkData::ReleaseProc on_release = [](const void* ptr, void* context) -> void { |
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.
Do we need to do anything to make sure that this runs on the correct thread?
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.
Good question -- I think this is safe as-is. In practice, this currently always happens on a different thread. No explicit mentioning of thread safety in the docs for AndroidBitmap_unlockPixels
, though.
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.
SkData is thread safe.
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.
And if we end up on a different thread, AttachCurrentThread will make sure we're allowed to make JNI calls on it. Or reuse the JNIEnv for that thread if it already attached.
A rebase should fix the Linux Web Engine check. |
d237dad
to
5962097
Compare
Relevant docs: flutter.dev/go/image-decoding-registry and flutter.dev/go/fallback-image-decoding
Continuation of flutter/flutter#17356 and flutter/flutter#82603.
Resolves flutter/flutter#20522 for API 28+.
ImageDecoder
does not split the header parsing and image decoding stages into separate operations such that they can be executed by different threads -- instead, when image decoding starts, the SDK'sImageDecoder
provides the header data through a callback on the same thread before continuing on with decoding immediately.memcpy
inGetPixels
by retrieving anAHardwareBuffer
for the decoded SDKBitmap
in order to produce anSkImage
from it. But unfortunately,AndroidBitmap_getHardwareBuffer
(NDK) andBitmap.getHardwareBuffer
(SDK) are only available in API 30+ and 31 respectively, and I didn't manage to work out any other API-permitting ways to do this.Pre-launch Checklist
writing and running engine tests.
///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.