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

[Impeller] A toolkit for managed handles to Android NDK vended objects. #51334

Merged
merged 15 commits into from
Mar 15, 2024

Conversation

chinmaygarde
Copy link
Member

Only available on Android device API levels >= 29. Proc table is setup has versioning checks. All handles are type safe. Collection of handles takes into account cleanup tasks (like reparenting surface controls). The proc table contains code duplicated in ndk_helpers and I will remove that in favor of this in a subsequent patch.

Part of #51213 being chopped up.

Copy link
Member

@jonahwilliams jonahwilliams left a comment

Choose a reason for hiding this comment

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

Do we intented to (eventually) replace the existing AHardwareBuffer and NDK wrappers used in the android embedder with this entirely?

Comment on lines +81 to +82
// Zero sized hardware buffers cannot be allocated.
desc.size = size.Max(ISize{1u, 1u});
Copy link
Member

Choose a reason for hiding this comment

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

This seems like it should be an error state (std::option or statusOr) instead of returning a size that wasn't requested.

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 documented that restriction in the headerdoc with a large warning sign so it shows up more prominently. It took me some trial and error to understand that this was the cause of buffers not being allocatable and the Android API doesn't say why a size wasn't allocatable. So making the callers figure this out was a pretty poor experience. Also, we do the same clamping in other spots (like the pbuffer sizes for offscreen GLES surfaces). So it seemed like in keeping with the theme. There are IsAllocatable checks either way though.

/// @see Vulkan Format: VK_FORMAT_R8G8B8A8_UNORM
/// @see OpenGL ES Format: GL_RGBA8
///
/// Why have many format when one format do trick?
Copy link
Member

Choose a reason for hiding this comment

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

😆

/// the descriptor after this call is made to determine the size
/// of the allocated hardware buffer.
///
/// @param[in] size The size.
Copy link
Member

Choose a reason for hiding this comment

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

Note size requirements 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.

I just put the size restrictions in the @warning block right above. Hoping to draw attention to it. But that was obviously not enough. I'll add a note reiterating the warning.

bool IsValid() const;

//----------------------------------------------------------------------------
/// @return The current of the native window.
Copy link
Member

Choose a reason for hiding this comment

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

current size?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

///
/// This wrapper is only available on Android API 29 and above.
///
class SurfaceControl {
Copy link
Member

Choose a reason for hiding this comment

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

Does the surface control have any threading restrictions? In general, if we're aware of any threading restrictions for parts of these APIs it would be good to note them.

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 am not aware of any threading restrictions on any of the handle types. The only major quirk is the transaction completion callback being made on a system managed thread (presumably a binder thread). I've documented that and also the lifecycle implications to the transaction because of it in the headerdoc comment 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 audited all calls, and beyond the completion callback that I have already documented, there is little guidance from the Android docs about the threading restrictions of any of the calls. I'm just going to be safe and call these as needing to be externally synchronized.

// tests everywhere else.
if (__builtin_available(android 29, *)) {
} else {
GTEST_SKIP() << "Platform too old for this test.";
Copy link
Member

Choose a reason for hiding this comment

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

Given that we just went through a fire drill where tests weren't actually running (because they failed unsafely) - how can we guarantee that we get a failure somewhere if we accidentally stop running this on API 29+ ?

Copy link
Contributor

Choose a reason for hiding this comment

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

We have the same problem with the existing ndk_helpers_unittests.

We do, in fact, currently run emulators that will run this test, but we should probably patch ... something to fail if this test never runs at all anywhere.

Copy link
Member Author

Choose a reason for hiding this comment

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

Should I just do a GTEST_FAIL instead?

Copy link
Member

Choose a reason for hiding this comment

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

GTEST_FAIL SGTM

Copy link
Contributor

Choose a reason for hiding this comment

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

We have infra that runs an API 28 emulator.

We should probably have a test that verifies we have sane/non-crashing behavior if you use this API below 29 though...

Copy link
Member Author

Choose a reason for hiding this comment

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

runs an API 28 emulator...

So, I can't make this a fail right?

we have sane/non-crashing behavior if you use this API below 29 though

We are definitely missing a trick not using static analysis (like at-availability on iOS) for this. I spent a non-trivial amount of time the other day wiring it up. But I haven't been able to get it to work reliably with our toolchain. Something to consider in addition to just checking on all emulator versions.


namespace impeller::android::testing {

class ToolkitAndroidTest : public ::testing::Test {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't know if you want to do this as a follow up, but related to Jonah's question:

The tests here don't have as much coverage as the ndk_helpers_unittests (specifcially, there aren't assertions that the API works and that we can allocate/destroy these objects or that the API symbols are correctly resolved). Maybe this will be good for a follow up patch, but consider porting those tests to this toolkit, deleting the FML versions, and porting the Android embedding to use this toolkit?

If that's not possible, I'd rather not land this in Impeller - I'm somewhat concerned about having more places that we do these lookups and introduce typos/untested code, especially since we just got that cleaned up.

Copy link
Contributor

Choose a reason for hiding this comment

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

If that's not possible, I'd rather not land this in Impeller - I'm somewhat concerned about having more places that we do these lookups and introduce typos/untested code, especially since we just got that cleaned up.

+1

Copy link
Member Author

Choose a reason for hiding this comment

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

API works and that we can allocate/destroy these objects.

CanCreateHardwareBuffer for hardware buffers. CanApplySurfaceTransaction for surface transactions. Same as the NDK helpers.

API symbols are correctly resolved.

CanCreateProcTable. You'll only get a valid proc table if the API symbols are correctly resolved and the version checks pass. For the individual subcomponents, this is also checked with IsAvailableOnPlatform.

The tests here don't have as much coverage.

I'm surprised by this statement. There are only two tests in NDK helpers related to anything in this patch. HardwareBuffer and SurfaceTransaction. The entire corpus is only 5 tests in total (unless I am missing something). The two tests I mentioned above more than cover the the assertions in NDK helpers.

I want to use the toolkit for everything and remove the NDK helpers after porting all existing tests. But, at least till I get to the choreographer, there isn't going to be anything to port as it should already be covered.

Copy link
Contributor

Choose a reason for hiding this comment

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

We seem to be missing coverage here of AHardwareBuffer_describe, AHardwareBuffer_isSupported, and AHardwareBuffer_getId, which are needed by the current Android implementation.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, but the toolkit has no ability to describe or get the ID of those objects yet. Once I work on adding those features get rid of NDK helpers, I can patch those in.

Copy link
Contributor

@dnfield dnfield Mar 13, 2024

Choose a reason for hiding this comment

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

(Sorry, I did not mean to post this one! too many tabs open)

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, but the toolkit has no ability to describe or get the ID of those objects yet. Once I work on adding those features get rid of NDK helpers, I can patch those in.

I would rather see either Impeller use the FML utilities until it's ready to replace them with something better, or the rest of the code base using new improved utilities with similar test coverage. Otherwise we're likely to end up in a state where we have multiple locations to do NDK related lookups with different available/missing test coverage ...

Copy link
Member Author

Choose a reason for hiding this comment

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

All I want to do is to facilitate landing #51213. The ask on that patch was that it was too large and it should be split up. I have split it up into #51295, #51298, and this one. Two more patches are incoming after this to facilitate the request for smaller, more self-contained PRs.

I am on the hook for killing the FML utilities. I can kill the FML utilities in this patch if this gets us past the bikeshedding but it will make this patch bigger. Or, kill the FML utilities immediately after this lands if that seems more elegant.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ping @dnfield. Do you have a preference? Want to unblock this review ASAP.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would rather eithe rkill it in this patch or update the AHB swapchain patch to use the FML utilities.

Copy link
Member Author

@chinmaygarde chinmaygarde left a comment

Choose a reason for hiding this comment

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

Do we intented to (eventually) replace the existing AHardwareBuffer and NDK wrappers used in the android embedder with this entirely?

Yes, I was going to replace the few uses of the helpers immediately after landing the swapchain patch and get rid of the libAndroid stuff in FML.

/// the descriptor after this call is made to determine the size
/// of the allocated hardware buffer.
///
/// @param[in] size The size.
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 just put the size restrictions in the @warning block right above. Hoping to draw attention to it. But that was obviously not enough. I'll add a note reiterating the warning.

bool IsValid() const;

//----------------------------------------------------------------------------
/// @return The current of the native window.
Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

Comment on lines +81 to +82
// Zero sized hardware buffers cannot be allocated.
desc.size = size.Max(ISize{1u, 1u});
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 documented that restriction in the headerdoc with a large warning sign so it shows up more prominently. It took me some trial and error to understand that this was the cause of buffers not being allocatable and the Android API doesn't say why a size wasn't allocatable. So making the callers figure this out was a pretty poor experience. Also, we do the same clamping in other spots (like the pbuffer sizes for offscreen GLES surfaces). So it seemed like in keeping with the theme. There are IsAllocatable checks either way though.

///
/// This wrapper is only available on Android API 29 and above.
///
class SurfaceControl {
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 am not aware of any threading restrictions on any of the handle types. The only major quirk is the transaction completion callback being made on a system managed thread (presumably a binder thread). I've documented that and also the lifecycle implications to the transaction because of it in the headerdoc comment for it.

///
/// This wrapper is only available on Android API 29 and above.
///
class SurfaceControl {
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 audited all calls, and beyond the completion callback that I have already documented, there is little guidance from the Android docs about the threading restrictions of any of the calls. I'm just going to be safe and call these as needing to be externally synchronized.


namespace impeller::android::testing {

class ToolkitAndroidTest : public ::testing::Test {
Copy link
Member Author

Choose a reason for hiding this comment

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

API works and that we can allocate/destroy these objects.

CanCreateHardwareBuffer for hardware buffers. CanApplySurfaceTransaction for surface transactions. Same as the NDK helpers.

API symbols are correctly resolved.

CanCreateProcTable. You'll only get a valid proc table if the API symbols are correctly resolved and the version checks pass. For the individual subcomponents, this is also checked with IsAvailableOnPlatform.

The tests here don't have as much coverage.

I'm surprised by this statement. There are only two tests in NDK helpers related to anything in this patch. HardwareBuffer and SurfaceTransaction. The entire corpus is only 5 tests in total (unless I am missing something). The two tests I mentioned above more than cover the the assertions in NDK helpers.

I want to use the toolkit for everything and remove the NDK helpers after porting all existing tests. But, at least till I get to the choreographer, there isn't going to be anything to port as it should already be covered.

// tests everywhere else.
if (__builtin_available(android 29, *)) {
} else {
GTEST_SKIP() << "Platform too old for this test.";
Copy link
Member Author

Choose a reason for hiding this comment

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

Should I just do a GTEST_FAIL instead?

@chinmaygarde
Copy link
Member Author

chinmaygarde commented Mar 13, 2024

I believe I have addressed all open questions. PTAL. Past this, there are two patches to actually add the ABH swapchain implemnetation, and migrate existing users of NDK helpers. Then John should be able to wire up the Chroeographer VSync IDs.

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.

@chinmaygarde
Copy link
Member Author

okie dokie. Killing the FML utils in this patch.

Only available on Android device API levels >= 29. Proc table is setup has
versioning checks. All handles are type safe. Collection of handles takes into
account cleanup tasks (like reparenting surface controls). The proc table
contains code duplicated in ndk_helpers and I will remove that in favor of this
in a subsequent patch.

Part of flutter#51213 being chopped up.
#define FOR_EACH_ANDROID_PROC(INVOKE) \
INVOKE(ATrace_isEnabled, 23) \
INVOKE(AChoreographer_getInstance, 24) \
INVOKE(AChoreographer_postFrameCallback, 24) \
Copy link
Contributor

Choose a reason for hiding this comment

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

We must not use this if sizeof(uint64_t) != sizeof(long).

On 32 bit platforms we have to use the Java based API. But we still should try to resolve it on a 64 bit platform that is < API 29. I made the same mistake when migrating things to the NDKHelpers class :)

Copy link
Contributor

Choose a reason for hiding this comment

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

(That's why the #if FML_ARCH_64BIT guard was around this, or whatever it actually was)

@dnfield
Copy link
Contributor

dnfield commented Mar 15, 2024

Overall this LGTM, will approve once the guard is added back to prevent undesirable usage of AChoreographer_postFrameCallback(long is back and CI is passing (looks like you missed at least one more flutter::NDKHelpers reference).

@chinmaygarde
Copy link
Member Author

Oh, was still tinkering on this before signing off for the day. I doubt this compiles right now. Will ping again in the morning after the fixups.

@chinmaygarde chinmaygarde added the Work in progress (WIP) Not ready (yet) for review! label Mar 15, 2024
Copy link
Member Author

@chinmaygarde chinmaygarde left a comment

Choose a reason for hiding this comment

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

This is good to go. PTAL. Some updates since the last review:

  • The NDK helpers are gone and existing users migrated.
  • The toolkit now has a choreographer that handles the Callback32/64 variants under the hood.
  • The previous version of the toolkit had an extra layer of protection over just the dysym. It checked not just that the API was available, but also that the platform was modern enough per the documentation. At the time, I was worried that an API may have been available as a symbol in an unstable form on older Android versions. But the way to check the Android API level is itself 29. The NDK helpers also didn't have the extra checks I had in the proc table. I have now removed the extra checks in the toolkit too. I had no reason to doubt this way isn't fine. Just need to trust the platform more.
    • Subsequently, the accessors to get the API level have also been removed.

}

#if FML_ARCH_CPU_32_BITS
// On 32-bit platforms, the nanosecond resolution timestamp causes overflow on
Copy link
Member Author

Choose a reason for hiding this comment

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

@dnfield There was at least one contradictory comment in the thread you linked earlier about the non-Callback64 choreographer use on 32-bit platforms. This was my reading of the underlying issue. But please double check if I accurately described the issue.

To reiterate the intent; Use the Callback64 variant where available. And use the Callback32 variant only on 64-bit devices (and where available).

Copy link
Contributor

Choose a reason for hiding this comment

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

SGTM

@chinmaygarde chinmaygarde removed the Work in progress (WIP) Not ready (yet) for review! label Mar 15, 2024

TEST_F(ToolkitAndroidTest, CanPostAndWaitForFrameCallbacks) {
if ((true)) {
GTEST_SKIP()
Copy link
Member Author

Choose a reason for hiding this comment

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

@dnfield @jonahwilliams This test will currently hang because the unit-test run as a naked binary don't get choreographer callbacks. I'll remove this skip once I have the APK based unit-tests harness we discussed yesterday.

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 as long as CI is happy!

@chinmaygarde chinmaygarde added the autosubmit Merge PR when tree becomes green via auto submit App label Mar 15, 2024
@auto-submit auto-submit bot merged commit f086ec4 into flutter:main Mar 15, 2024
30 checks passed
@chinmaygarde chinmaygarde deleted the toolkit_android branch March 15, 2024 20:45
@matanlurey
Copy link
Contributor

@matanlurey matanlurey added the revert Label used to revert changes in a closed and merged pull request. label Mar 15, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Mar 15, 2024
auto-submit bot pushed a commit that referenced this pull request Mar 15, 2024
@auto-submit auto-submit bot removed the revert Label used to revert changes in a closed and merged pull request. label Mar 15, 2024
auto-submit bot added a commit that referenced this pull request Mar 15, 2024
…ed objects. (#51334)" (#51457)

Reverts: #51334
Initiated by: matanlurey
Reason for reverting: Broke engine post-submit, see https://logs.chromium.org/logs/flutter/buildbucket/cr-buildbucket/8753367119442265873/+/u/test:_Android_Unit_Tests__API_28_/stdout.
Original PR Author: chinmaygarde

Reviewed By: {dnfield}

This change reverts the following previous change:
Only available on Android device API levels >= 29. Proc table is setup has versioning checks. All handles are type safe. Collection of handles takes into account cleanup tasks (like reparenting surface controls). The proc table contains code duplicated in ndk_helpers and I will remove that in favor of this in a subsequent patch.

Part of #51213 being chopped up.
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Mar 15, 2024
auto-submit bot pushed a commit to flutter/flutter that referenced this pull request Mar 15, 2024
…145237)

flutter/engine@c2fd533...ba1115c

2024-03-15 [email protected] Add more explicit logging (just to `stderr`) if a try-job detects an untriaged image (flutter/engine#51454)
2024-03-15 98614782+auto-submit[bot]@users.noreply.github.com Reverts "[Impeller] A toolkit for managed handles to Android NDK vended objects. (#51334)" (flutter/engine#51457)
2024-03-15 [email protected] [Impeller] A toolkit for managed handles to Android NDK vended objects. (flutter/engine#51334)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
autosubmit Merge PR when tree becomes green via auto submit App e: impeller platform-android
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants