195 lines
7.3 KiB
C++
195 lines
7.3 KiB
C++
/* -*- 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
|