Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

bdero
Copy link
Member

@bdero bdero commented Jun 14, 2021

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+.

  • The NDK decoder (API 30+) is provided by Skia.
  • The SDK decoder (API 28+) is adapted from this branch by @dnfield.
    • The primary difference is that it now performs the image decode on the IO thread and doesn't block the UI thread during the full decode.
    • However, the UI thread still needs to block until the decoding task has finished parsing the header. This is because the Android SDK 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's ImageDecoder provides the header data through a callback on the same thread before continuing on with decoding immediately.
    • I also attempted to avoid the memcpy in GetPixels by retrieving an AHardwareBuffer for the decoded SDK Bitmap in order to produce an SkImage from it. But unfortunately, AndroidBitmap_getHardwareBuffer (NDK) and Bitmap.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

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide and the C++, Objective-C, Java style guides.
  • I listed at least one issue that this PR fixes in the description above.
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt. See testing the engine for instructions on
    writing and running engine tests.
  • I updated/added relevant documentation (doc comments with ///).
  • I signed the CLA.
  • All existing and new tests are passing.
  • The reviewer has submitted any presubmit flakes in this PR using the engine presubmit flakes form before re-triggering the failure.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@google-cla google-cla bot added the cla: yes label Jun 14, 2021
@bdero bdero force-pushed the bdero/embedder-image-decoders branch 3 times, most recently from 726902a to 29d5d5c Compare June 14, 2021 22:06
Copy link
Contributor

@dnfield dnfield left a 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

Comment on lines 65 to 73
// 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.
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

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.

Copy link
Contributor

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.

@bdero
Copy link
Member Author

bdero commented Jun 15, 2021

@dnfield Ah sorry, this is still quite broken/untested, but thanks for the helpful comments. :)

@dnfield
Copy link
Contributor

dnfield commented Jun 15, 2021

Ahh ok, I saw the review request and started looking. Feel free to let me know when it's ready for another pass.

@bdero bdero force-pushed the bdero/embedder-image-decoders branch 3 times, most recently from b27c8d7 to 96dc5b5 Compare June 21, 2021 20:18
@bdero bdero force-pushed the bdero/embedder-image-decoders branch from 7b196ce to 36358d3 Compare June 29, 2021 07:35
@bdero bdero force-pushed the bdero/embedder-image-decoders branch 5 times, most recently from aebdac6 to 4d859e6 Compare July 9, 2021 23:31
@bdero bdero changed the title WIP: Add native Android image decoders supported by API 28+ Add native Android image decoders supported by API 28+ Jul 9, 2021
@bdero bdero force-pushed the bdero/embedder-image-decoders branch from 93cdc4e to 846f27c Compare July 12, 2021 16:47
@bdero
Copy link
Member Author

bdero commented Jul 12, 2021

@dnfield I finally got around to fixing this up and it's good to go for another pass.
I flipped on the grilled chicken heic test for Android and added coverage for the shell method. Let me know if this seems sufficient or if you think there are other kinds of tests/scenarios that should be tossed in.

@bdero bdero requested a review from dnfield July 13, 2021 18:56
@bdero
Copy link
Member Author

bdero commented Jul 13, 2021

Hmm, there's definitely a crash bug when running on arm64 caused by the AndroidImageGenerator address not making it back to NativeImageHeaderCallback correctly. Investigating it:

[ +396 ms] E/flutter ( 9134): [ERROR:flutter/shell/platform/android/android_image_generator.cc(95)] Old address: 526384806704
[  +11 ms] E/flutter ( 9134): [ERROR:flutter/shell/platform/android/android_image_generator.cc(177)] New address: 526653397424

@bdero bdero force-pushed the bdero/embedder-image-decoders branch from 846f27c to f8d3df8 Compare July 15, 2021 05:04
@bdero
Copy link
Member Author

bdero commented Jul 15, 2021

All fixed!

Comment on lines 185 to 194
// 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.
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

@dnfield dnfield left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking closer

@bdero bdero force-pushed the bdero/embedder-image-decoders branch from f8d3df8 to 54d2a59 Compare July 15, 2021 20:24
std::unique_ptr<ImageGenerator> AndroidImageGenerator::MakeFromData(
sk_sp<SkData> data,
fml::RefPtr<fml::TaskRunner> task_runner) {
auto* generator = new AndroidImageGenerator(std::move(data));
Copy link
Contributor

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.

Copy link
Member Author

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_ptrs because of this.

Copy link
Contributor

@dnfield dnfield left a 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.

@bdero bdero force-pushed the bdero/embedder-image-decoders branch 3 times, most recently from a5c427b to 954170f Compare July 20, 2021 20:51
return;
}

SkData::ReleaseProc on_release = [](const void* ptr, void* context) -> void {
Copy link
Member

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?

Copy link
Member Author

@bdero bdero Jul 21, 2021

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SkData is thread safe.

Copy link
Contributor

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.

@zanderso
Copy link
Member

A rebase should fix the Linux Web Engine check.

@bdero bdero force-pushed the bdero/embedder-image-decoders branch from d237dad to 5962097 Compare July 21, 2021 18:31
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Image widget does not support HEIC images
4 participants