/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #include "Hal.h" #include "HalLog.h" #include "HalTypes.h" #include "AndroidBuild.h" #include #include #include #include typedef struct APerformanceHintManager APerformanceHintManager; typedef struct APerformanceHintSession APerformanceHintSession; namespace mozilla { namespace hal_impl { #define LOAD_FN(api, lib, name) \ do { \ api->m##name = reinterpret_cast(dlsym(handle, #name)); \ if (!api->m##name) { \ HAL_ERR("Failed to load %s", #name); \ return nullptr; \ } \ } while (false) class PerformanceHintManagerApi final { public: static PerformanceHintManagerApi* Get() { // C++ guarantees local static variable initialization is thread safe static UniquePtr api = Create(); return api.get(); } APerformanceHintManager* APerformanceHint_getManager() const { return mAPerformanceHint_getManager(); } APerformanceHintSession* APerformanceHint_createSession( APerformanceHintManager* manager, const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos) const { return mAPerformanceHint_createSession(manager, threadIds, size, initialTargetWorkDurationNanos); } int APerformanceHint_updateTargetWorkDuration( APerformanceHintSession* session, int64_t targetDurationNanos) const { return mAPerformanceHint_updateTargetWorkDuration(session, targetDurationNanos); } int APerformanceHint_reportActualWorkDuration( APerformanceHintSession* session, int64_t actualDurationNanos) const { return mAPerformanceHint_reportActualWorkDuration(session, actualDurationNanos); } void APerformanceHint_closeSession(APerformanceHintSession* session) const { mAPerformanceHint_closeSession(session); } private: PerformanceHintManagerApi() = default; static UniquePtr Create() { if (mozilla::jni::GetAPIVersion() < __ANDROID_API_T__) { return nullptr; } void* const handle = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL); if (!handle) { HAL_ERR("Failed to open libandroid.so"); return nullptr; } auto api = WrapUnique(new PerformanceHintManagerApi()); LOAD_FN(api, handle, APerformanceHint_getManager); LOAD_FN(api, handle, APerformanceHint_createSession); LOAD_FN(api, handle, APerformanceHint_updateTargetWorkDuration); LOAD_FN(api, handle, APerformanceHint_reportActualWorkDuration); LOAD_FN(api, handle, APerformanceHint_closeSession); return api; } using FnAPerformanceHint_getManager = APerformanceHintManager* (*)(); using FnAPerformanceHint_createSession = APerformanceHintSession* (*)(APerformanceHintManager* manager, const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos); using FnAPerformanceHint_updateTargetWorkDuration = int (*)(APerformanceHintSession* session, int64_t targetDurationNanos); using FnAPerformanceHint_reportActualWorkDuration = int (*)(APerformanceHintSession* session, int64_t actualDurationNanos); using FnAPerformanceHint_closeSession = void (*)(APerformanceHintSession* session); FnAPerformanceHint_getManager mAPerformanceHint_getManager = nullptr; FnAPerformanceHint_createSession mAPerformanceHint_createSession = nullptr; FnAPerformanceHint_updateTargetWorkDuration mAPerformanceHint_updateTargetWorkDuration = nullptr; FnAPerformanceHint_reportActualWorkDuration mAPerformanceHint_reportActualWorkDuration = nullptr; FnAPerformanceHint_closeSession mAPerformanceHint_closeSession = nullptr; }; class AndroidPerformanceHintSession final : public hal::PerformanceHintSession { public: // Creates a PerformanceHintSession wrapping the provided NDK // APerformanceHintSession instance. This assumes ownership of aSession, // therefore the caller must not close the session itself. explicit AndroidPerformanceHintSession(APerformanceHintSession* aSession) : mSession(aSession) {} AndroidPerformanceHintSession(AndroidPerformanceHintSession& aOther) = delete; AndroidPerformanceHintSession(AndroidPerformanceHintSession&& aOther) { mSession = aOther.mSession; aOther.mSession = nullptr; } ~AndroidPerformanceHintSession() { if (mSession) { PerformanceHintManagerApi::Get()->APerformanceHint_closeSession(mSession); } } void UpdateTargetWorkDuration(TimeDuration aDuration) override { PerformanceHintManagerApi::Get()->APerformanceHint_updateTargetWorkDuration( mSession, aDuration.ToMicroseconds() * 1000); } void ReportActualWorkDuration(TimeDuration aDuration) override { PerformanceHintManagerApi::Get()->APerformanceHint_reportActualWorkDuration( mSession, aDuration.ToMicroseconds() * 1000); } private: APerformanceHintSession* mSession; }; static APerformanceHintManager* InitManager() { const auto* api = PerformanceHintManagerApi::Get(); if (!api) { return nullptr; } // At the time of writing we are only aware of PerformanceHintManager being // implemented on Tensor devices (Pixel 6 and 7 families). On most devices // createSession() will simply return null. However, on some devices // createSession() does return a session but scheduling does not appear to be // affected in any way. Rather than pretending to the caller that // PerformanceHintManager is available on such devices, return null allowing // them to use another means of achieving the performance they require. const auto socManufacturer = java::sdk::Build::SOC_MANUFACTURER()->ToString(); if (!socManufacturer.EqualsASCII("Google")) { return nullptr; } return api->APerformanceHint_getManager(); } UniquePtr CreatePerformanceHintSession( const nsTArray& aThreads, mozilla::TimeDuration aTargetWorkDuration) { // C++ guarantees local static variable initialization is thread safe static APerformanceHintManager* manager = InitManager(); if (!manager) { return nullptr; } const auto* api = PerformanceHintManagerApi::Get(); nsTArray tids(aThreads.Length()); std::transform(aThreads.cbegin(), aThreads.cend(), MakeBackInserter(tids), [](pthread_t handle) { return pthread_gettid_np(handle); }); APerformanceHintSession* session = api->APerformanceHint_createSession( manager, tids.Elements(), tids.Length(), aTargetWorkDuration.ToMicroseconds() * 1000); if (!session) { return nullptr; } return MakeUnique(session); } } // namespace hal_impl } // namespace mozilla