diff options
Diffstat (limited to 'native')
-rw-r--r-- | native/android/Android.bp | 1 | ||||
-rw-r--r-- | native/android/libandroid.map.txt | 7 | ||||
-rw-r--r-- | native/android/performance_hint.cpp | 241 | ||||
-rw-r--r-- | native/android/tests/performance_hint/Android.bp | 65 | ||||
-rw-r--r-- | native/android/tests/performance_hint/PerformanceHintNativeTest.cpp | 124 | ||||
-rw-r--r-- | native/webview/plat_support/draw_functor.cpp | 46 |
6 files changed, 478 insertions, 6 deletions
diff --git a/native/android/Android.bp b/native/android/Android.bp index 3ee2c18a0eab..32b7a0780e63 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -57,6 +57,7 @@ cc_library_shared { "net.c", "obb.cpp", "permission_manager.cpp", + "performance_hint.cpp", "sensor.cpp", "sharedmem.cpp", "storage_manager.cpp", diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index de6db1ae7d23..f33e11817730 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -312,6 +312,13 @@ LIBANDROID { LIBANDROID_PLATFORM { global: + APerformanceHint_getManager; + APerformanceHint_createSession; + APerformanceHint_getPreferredUpdateRateNanos; + APerformanceHint_updateTargetWorkDuration; + APerformanceHint_reportActualWorkDuration; + APerformanceHint_closeSession; + APerformanceHint_setIHintManagerForTesting; extern "C++" { ASurfaceControl_registerSurfaceStatsListener*; ASurfaceControl_unregisterSurfaceStatsListener*; diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp new file mode 100644 index 000000000000..95a2da9226d9 --- /dev/null +++ b/native/android/performance_hint.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "perf_hint" + +#include <utility> +#include <vector> + +#include <android/os/IHintManager.h> +#include <android/os/IHintSession.h> +#include <binder/Binder.h> +#include <binder/IBinder.h> +#include <binder/IServiceManager.h> +#include <performance_hint_private.h> +#include <utils/SystemClock.h> + +using namespace android; +using namespace android::os; + +struct APerformanceHintSession; + +struct APerformanceHintManager { +public: + static APerformanceHintManager* getInstance(); + APerformanceHintManager(sp<IHintManager> service, int64_t preferredRateNanos); + APerformanceHintManager() = delete; + ~APerformanceHintManager() = default; + + APerformanceHintSession* createSession(const int32_t* threadIds, size_t size, + int64_t initialTargetWorkDurationNanos); + int64_t getPreferredRateNanos() const; + +private: + static APerformanceHintManager* create(sp<IHintManager> iHintManager); + + sp<IHintManager> mHintManager; + const int64_t mPreferredRateNanos; +}; + +struct APerformanceHintSession { +public: + APerformanceHintSession(sp<IHintSession> session, int64_t preferredRateNanos, + int64_t targetDurationNanos); + APerformanceHintSession() = delete; + ~APerformanceHintSession(); + + int updateTargetWorkDuration(int64_t targetDurationNanos); + int reportActualWorkDuration(int64_t actualDurationNanos); + +private: + friend struct APerformanceHintManager; + + sp<IHintSession> mHintSession; + // HAL preferred update rate + const int64_t mPreferredRateNanos; + // Target duration for choosing update rate + int64_t mTargetDurationNanos; + // Last update timestamp + int64_t mLastUpdateTimestamp; + // Cached samples + std::vector<int64_t> mActualDurationsNanos; + std::vector<int64_t> mTimestampsNanos; +}; + +static IHintManager* gIHintManagerForTesting = nullptr; +static APerformanceHintManager* gHintManagerForTesting = nullptr; + +// ===================================== APerformanceHintManager implementation +APerformanceHintManager::APerformanceHintManager(sp<IHintManager> manager, + int64_t preferredRateNanos) + : mHintManager(std::move(manager)), mPreferredRateNanos(preferredRateNanos) {} + +APerformanceHintManager* APerformanceHintManager::getInstance() { + if (gHintManagerForTesting) return gHintManagerForTesting; + if (gIHintManagerForTesting) { + APerformanceHintManager* manager = create(gIHintManagerForTesting); + gIHintManagerForTesting = nullptr; + return manager; + } + static APerformanceHintManager* instance = create(nullptr); + return instance; +} + +APerformanceHintManager* APerformanceHintManager::create(sp<IHintManager> manager) { + if (!manager) { + manager = interface_cast<IHintManager>( + defaultServiceManager()->checkService(String16("performance_hint"))); + } + if (manager == nullptr) { + ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__); + return nullptr; + } + int64_t preferredRateNanos = -1L; + binder::Status ret = manager->getHintSessionPreferredRate(&preferredRateNanos); + if (!ret.isOk()) { + ALOGE("%s: PerformanceHint cannot get preferred rate. %s", __FUNCTION__, + ret.exceptionMessage().c_str()); + return nullptr; + } + if (preferredRateNanos <= 0) { + ALOGE("%s: PerformanceHint invalid preferred rate.", __FUNCTION__); + return nullptr; + } + return new APerformanceHintManager(std::move(manager), preferredRateNanos); +} + +APerformanceHintSession* APerformanceHintManager::createSession( + const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos) { + sp<IBinder> token = sp<BBinder>::make(); + std::vector<int32_t> tids(threadIds, threadIds + size); + sp<IHintSession> session; + binder::Status ret = + mHintManager->createHintSession(token, tids, initialTargetWorkDurationNanos, &session); + if (!ret.isOk() || !session) { + return nullptr; + } + return new APerformanceHintSession(std::move(session), mPreferredRateNanos, + initialTargetWorkDurationNanos); +} + +int64_t APerformanceHintManager::getPreferredRateNanos() const { + return mPreferredRateNanos; +} + +// ===================================== APerformanceHintSession implementation + +APerformanceHintSession::APerformanceHintSession(sp<IHintSession> session, + int64_t preferredRateNanos, + int64_t targetDurationNanos) + : mHintSession(std::move(session)), + mPreferredRateNanos(preferredRateNanos), + mTargetDurationNanos(targetDurationNanos), + mLastUpdateTimestamp(elapsedRealtimeNano()) {} + +APerformanceHintSession::~APerformanceHintSession() { + binder::Status ret = mHintSession->close(); + if (!ret.isOk()) { + ALOGE("%s: HintSession close failed: %s", __FUNCTION__, ret.exceptionMessage().c_str()); + } +} + +int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) { + if (targetDurationNanos <= 0) { + ALOGE("%s: targetDurationNanos must be positive", __FUNCTION__); + return EINVAL; + } + binder::Status ret = mHintSession->updateTargetWorkDuration(targetDurationNanos); + if (!ret.isOk()) { + ALOGE("%s: HintSessionn updateTargetWorkDuration failed: %s", __FUNCTION__, + ret.exceptionMessage().c_str()); + return EPIPE; + } + mTargetDurationNanos = targetDurationNanos; + /** + * Most of the workload is target_duration dependent, so now clear the cached samples + * as they are most likely obsolete. + */ + mActualDurationsNanos.clear(); + mTimestampsNanos.clear(); + mLastUpdateTimestamp = elapsedRealtimeNano(); + return 0; +} + +int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNanos) { + if (actualDurationNanos <= 0) { + ALOGE("%s: actualDurationNanos must be positive", __FUNCTION__); + return EINVAL; + } + int64_t now = elapsedRealtimeNano(); + mActualDurationsNanos.push_back(actualDurationNanos); + mTimestampsNanos.push_back(now); + + /** + * Use current sample to determine the rate limit. We can pick a shorter rate limit + * if any sample underperformed, however, it could be the lower level system is slow + * to react. So here we explicitly choose the rate limit with the latest sample. + */ + int64_t rateLimit = actualDurationNanos > mTargetDurationNanos ? mPreferredRateNanos + : 10 * mPreferredRateNanos; + if (now - mLastUpdateTimestamp <= rateLimit) return 0; + + binder::Status ret = + mHintSession->reportActualWorkDuration(mActualDurationsNanos, mTimestampsNanos); + mActualDurationsNanos.clear(); + mTimestampsNanos.clear(); + if (!ret.isOk()) { + ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__, + ret.exceptionMessage().c_str()); + return EPIPE; + } + mLastUpdateTimestamp = now; + return 0; +} + +// ===================================== C API +APerformanceHintManager* APerformanceHint_getManager() { + return APerformanceHintManager::getInstance(); +} + +APerformanceHintSession* APerformanceHint_createSession(APerformanceHintManager* manager, + const int32_t* threadIds, size_t size, + int64_t initialTargetWorkDurationNanos) { + return manager->createSession(threadIds, size, initialTargetWorkDurationNanos); +} + +int64_t APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager* manager) { + return manager->getPreferredRateNanos(); +} + +int APerformanceHint_updateTargetWorkDuration(APerformanceHintSession* session, + int64_t targetDurationNanos) { + return session->updateTargetWorkDuration(targetDurationNanos); +} + +int APerformanceHint_reportActualWorkDuration(APerformanceHintSession* session, + int64_t actualDurationNanos) { + return session->reportActualWorkDuration(actualDurationNanos); +} + +void APerformanceHint_closeSession(APerformanceHintSession* session) { + delete session; +} + +void APerformanceHint_setIHintManagerForTesting(void* iManager) { + delete gHintManagerForTesting; + gHintManagerForTesting = nullptr; + gIHintManagerForTesting = static_cast<IHintManager*>(iManager); +} diff --git a/native/android/tests/performance_hint/Android.bp b/native/android/tests/performance_hint/Android.bp new file mode 100644 index 000000000000..fdc1bc6a20d8 --- /dev/null +++ b/native/android/tests/performance_hint/Android.bp @@ -0,0 +1,65 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +cc_test { + name: "PerformanceHintNativeTestCases", + + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + + srcs: ["PerformanceHintNativeTest.cpp"], + + shared_libs: [ + "libandroid", + "liblog", + "libbinder", + "libpowermanager", + "libutils", + ], + + static_libs: [ + "libbase", + "libgmock", + "libgtest", + ], + stl: "c++_shared", + + test_suites: [ + "device-tests", + ], + + cflags: [ + "-Werror", + "-Wall", + ], + + header_libs: [ + "libandroid_headers_private", + ], +} diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp new file mode 100644 index 000000000000..284e9ee909ee --- /dev/null +++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PerformanceHintNativeTest" + +#include <android/os/IHintManager.h> +#include <android/os/IHintSession.h> +#include <binder/IBinder.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <performance_hint_private.h> +#include <memory> +#include <vector> + +using android::binder::Status; +using android::os::IHintManager; +using android::os::IHintSession; + +using namespace android; +using namespace testing; + +class MockIHintManager : public IHintManager { +public: + MOCK_METHOD(Status, createHintSession, + (const ::android::sp<::android::IBinder>& token, const ::std::vector<int32_t>& tids, + int64_t durationNanos, ::android::sp<::android::os::IHintSession>* _aidl_return), + (override)); + MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * _aidl_return), (override)); + MOCK_METHOD(IBinder*, onAsBinder, (), (override)); +}; + +class MockIHintSession : public IHintSession { +public: + MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t targetDurationNanos), (override)); + MOCK_METHOD(Status, reportActualWorkDuration, + (const ::std::vector<int64_t>& actualDurationNanos, + const ::std::vector<int64_t>& timeStampNanos), + (override)); + MOCK_METHOD(Status, close, (), (override)); + MOCK_METHOD(IBinder*, onAsBinder, (), (override)); +}; + +class PerformanceHintTest : public Test { +public: + void SetUp() override { + mMockIHintManager = new StrictMock<MockIHintManager>(); + APerformanceHint_setIHintManagerForTesting(mMockIHintManager); + } + + void TearDown() override { + mMockIHintManager = nullptr; + // Destroys MockIHintManager. + APerformanceHint_setIHintManagerForTesting(nullptr); + } + + APerformanceHintManager* createManager() { + EXPECT_CALL(*mMockIHintManager, getHintSessionPreferredRate(_)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<0>(123L), Return(Status()))); + return APerformanceHint_getManager(); + } + + StrictMock<MockIHintManager>* mMockIHintManager = nullptr; +}; + +TEST_F(PerformanceHintTest, TestGetPreferredUpdateRateNanos) { + APerformanceHintManager* manager = createManager(); + int64_t preferredUpdateRateNanos = APerformanceHint_getPreferredUpdateRateNanos(manager); + EXPECT_EQ(123L, preferredUpdateRateNanos); +} + +TEST_F(PerformanceHintTest, TestSession) { + APerformanceHintManager* manager = createManager(); + + std::vector<int32_t> tids; + tids.push_back(1); + tids.push_back(2); + int64_t targetDuration = 56789L; + + StrictMock<MockIHintSession>* iSession = new StrictMock<MockIHintSession>(); + sp<IHintSession> session_sp(iSession); + + EXPECT_CALL(*mMockIHintManager, createHintSession(_, Eq(tids), Eq(targetDuration), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<3>(std::move(session_sp)), Return(Status()))); + + APerformanceHintSession* session = + APerformanceHint_createSession(manager, tids.data(), tids.size(), targetDuration); + ASSERT_TRUE(session); + + int64_t targetDurationNanos = 10; + EXPECT_CALL(*iSession, updateTargetWorkDuration(Eq(targetDurationNanos))).Times(Exactly(1)); + int result = APerformanceHint_updateTargetWorkDuration(session, targetDurationNanos); + EXPECT_EQ(0, result); + + usleep(2); // Sleep for longer than preferredUpdateRateNanos. + int64_t actualDurationNanos = 20; + std::vector<int64_t> actualDurations; + actualDurations.push_back(20); + EXPECT_CALL(*iSession, reportActualWorkDuration(Eq(actualDurations), _)).Times(Exactly(1)); + result = APerformanceHint_reportActualWorkDuration(session, actualDurationNanos); + EXPECT_EQ(0, result); + + result = APerformanceHint_updateTargetWorkDuration(session, -1L); + EXPECT_EQ(EINVAL, result); + result = APerformanceHint_reportActualWorkDuration(session, -1L); + EXPECT_EQ(EINVAL, result); + + EXPECT_CALL(*iSession, close()).Times(Exactly(1)); + APerformanceHint_closeSession(session); +} diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp index 472e0a4347fc..1584350bb6f1 100644 --- a/native/webview/plat_support/draw_functor.cpp +++ b/native/webview/plat_support/draw_functor.cpp @@ -77,7 +77,18 @@ void draw_gl(int functor, void* data, const uirenderer::DrawGlInfo& draw_gl_params, const uirenderer::WebViewOverlayData& overlay_params) { float gabcdef[7]; - draw_gl_params.color_space_ptr->transferFn(gabcdef); + if (draw_gl_params.color_space_ptr) { + draw_gl_params.color_space_ptr->transferFn(gabcdef); + } else { + // Assume sRGB. + gabcdef[0] = SkNamedTransferFn::kSRGB.g; + gabcdef[1] = SkNamedTransferFn::kSRGB.a; + gabcdef[2] = SkNamedTransferFn::kSRGB.b; + gabcdef[3] = SkNamedTransferFn::kSRGB.c; + gabcdef[4] = SkNamedTransferFn::kSRGB.d; + gabcdef[5] = SkNamedTransferFn::kSRGB.e; + gabcdef[6] = SkNamedTransferFn::kSRGB.f; + } AwDrawFn_DrawGLParams params = { .version = kAwDrawFnVersion, .clip_left = draw_gl_params.clipLeft, @@ -105,8 +116,14 @@ void draw_gl(int functor, void* data, } COMPILE_ASSERT(sizeof(params.color_space_toXYZD50) == sizeof(skcms_Matrix3x3), gamut_transform_size_mismatch); - draw_gl_params.color_space_ptr->toXYZD50( - reinterpret_cast<skcms_Matrix3x3*>(¶ms.color_space_toXYZD50)); + if (draw_gl_params.color_space_ptr) { + draw_gl_params.color_space_ptr->toXYZD50( + reinterpret_cast<skcms_Matrix3x3*>(¶ms.color_space_toXYZD50)); + } else { + // Assume sRGB. + memcpy(¶ms.color_space_toXYZD50, &SkNamedGamut::kSRGB, + sizeof(params.color_space_toXYZD50)); + } SupportData* support = static_cast<SupportData*>(data); support->callbacks.draw_gl(functor, support->data, ¶ms); @@ -147,7 +164,18 @@ void drawVk(int functor, void* data, const uirenderer::WebViewOverlayData& overlay_params) { SupportData* support = static_cast<SupportData*>(data); float gabcdef[7]; - draw_vk_params.color_space_ptr->transferFn(gabcdef); + if (draw_vk_params.color_space_ptr) { + draw_vk_params.color_space_ptr->transferFn(gabcdef); + } else { + // Assume sRGB. + gabcdef[0] = SkNamedTransferFn::kSRGB.g; + gabcdef[1] = SkNamedTransferFn::kSRGB.a; + gabcdef[2] = SkNamedTransferFn::kSRGB.b; + gabcdef[3] = SkNamedTransferFn::kSRGB.c; + gabcdef[4] = SkNamedTransferFn::kSRGB.d; + gabcdef[5] = SkNamedTransferFn::kSRGB.e; + gabcdef[6] = SkNamedTransferFn::kSRGB.f; + } AwDrawFn_DrawVkParams params{ .version = kAwDrawFnVersion, .width = draw_vk_params.width, @@ -174,8 +202,14 @@ void drawVk(int functor, void* data, }; COMPILE_ASSERT(sizeof(params.color_space_toXYZD50) == sizeof(skcms_Matrix3x3), gamut_transform_size_mismatch); - draw_vk_params.color_space_ptr->toXYZD50( - reinterpret_cast<skcms_Matrix3x3*>(¶ms.color_space_toXYZD50)); + if (draw_vk_params.color_space_ptr) { + draw_vk_params.color_space_ptr->toXYZD50( + reinterpret_cast<skcms_Matrix3x3*>(¶ms.color_space_toXYZD50)); + } else { + // Assume sRGB. + memcpy(¶ms.color_space_toXYZD50, &SkNamedGamut::kSRGB, + sizeof(params.color_space_toXYZD50)); + } COMPILE_ASSERT(NELEM(params.transform) == NELEM(draw_vk_params.transform), mismatched_transform_matrix_sizes); for (int i = 0; i < NELEM(params.transform); ++i) { |