Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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 <dlfcn.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
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<Fn##name>(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<PerformanceHintManagerApi> 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<PerformanceHintManagerApi> 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<hal::PerformanceHintSession> CreatePerformanceHintSession(
const nsTArray<PlatformThreadHandle>& 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<pid_t> 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<AndroidPerformanceHintSession>(session);
}
} // namespace hal_impl
} // namespace mozilla