[go: nahoru, domu]

blob: ecb5a5f2281677942c849bfd1217d16868a5dc50 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/viz/service/performance_hint/hint_session.h"
#include <utility>
#include <vector>
#include "base/time/time.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_ANDROID)
#include <dlfcn.h>
#include <sys/types.h>
#include "base/android/build_info.h"
#include "base/logging.h"
#include "base/native_library.h"
#include "base/threading/thread_checker.h"
#include "base/trace_event/trace_event.h"
static_assert(sizeof(base::PlatformThreadId) == sizeof(int32_t),
"thread id types incompatible");
extern "C" {
typedef struct APerformanceHintManager APerformanceHintManager;
typedef struct APerformanceHintSession APerformanceHintSession;
using pAPerformanceHint_getManager = APerformanceHintManager* (*)();
using pAPerformanceHint_createSession =
APerformanceHintSession* (*)(APerformanceHintManager* manager,
const int32_t* threadIds,
size_t size,
int64_t initialTargetWorkDurationNanos);
using pAPerformanceHint_reportActualWorkDuration =
int (*)(APerformanceHintSession* session, int64_t actualDurationNanos);
using pAPerformanceHint_closeSession =
void (*)(APerformanceHintSession* session);
}
namespace viz {
namespace {
class HintSessionFactoryImpl;
#define LOAD_FUNCTION(lib, func) \
do { \
func##Fn = reinterpret_cast<p##func>( \
base::GetFunctionPointerFromNativeLibrary(lib, #func)); \
if (!func##Fn) { \
supported = false; \
LOG(ERROR) << "Unable to load function " << #func; \
} \
} while (0)
struct AdpfMethods {
static const AdpfMethods& Get() {
static AdpfMethods instance;
return instance;
}
AdpfMethods() {
base::NativeLibraryLoadError error;
base::NativeLibrary main_dl_handle =
base::LoadNativeLibrary(base::FilePath("libandroid.so"), &error);
if (!main_dl_handle) {
LOG(ERROR) << "Couldnt load libandroid.so: " << error.ToString();
supported = false;
return;
}
LOAD_FUNCTION(main_dl_handle, APerformanceHint_getManager);
LOAD_FUNCTION(main_dl_handle, APerformanceHint_createSession);
LOAD_FUNCTION(main_dl_handle, APerformanceHint_reportActualWorkDuration);
LOAD_FUNCTION(main_dl_handle, APerformanceHint_closeSession);
}
~AdpfMethods() = default;
bool supported = true;
pAPerformanceHint_getManager APerformanceHint_getManagerFn;
pAPerformanceHint_createSession APerformanceHint_createSessionFn;
pAPerformanceHint_reportActualWorkDuration
APerformanceHint_reportActualWorkDurationFn;
pAPerformanceHint_closeSession APerformanceHint_closeSessionFn;
};
class AdpfHintSession : public HintSession {
public:
AdpfHintSession(APerformanceHintSession* session,
HintSessionFactoryImpl* factory,
base::TimeDelta target_duration);
~AdpfHintSession() override;
void ReportCpuCompletionTime(base::TimeDelta actual_duration) override;
void WakeUp();
private:
APerformanceHintSession* const hint_session_;
HintSessionFactoryImpl* const factory_;
const base::TimeDelta target_duration_;
};
class HintSessionFactoryImpl : public HintSessionFactory {
public:
HintSessionFactoryImpl(
APerformanceHintManager* manager,
base::flat_set<base::PlatformThreadId> permanent_thread_ids);
~HintSessionFactoryImpl() override;
std::unique_ptr<HintSession> CreateSession(
base::flat_set<base::PlatformThreadId> transient_thread_ids,
base::TimeDelta target_duration) override;
void WakeUp() override;
private:
friend class AdpfHintSession;
friend class HintSessionFactory;
APerformanceHintManager* const manager_;
const base::flat_set<base::PlatformThreadId> permanent_thread_ids_;
base::flat_set<AdpfHintSession*> hint_sessions_;
THREAD_CHECKER(thread_checker_);
};
AdpfHintSession::AdpfHintSession(APerformanceHintSession* session,
HintSessionFactoryImpl* factory,
base::TimeDelta target_duration)
: hint_session_(session),
factory_(factory),
target_duration_(target_duration) {
DCHECK_CALLED_ON_VALID_THREAD(factory_->thread_checker_);
factory_->hint_sessions_.insert(this);
}
AdpfHintSession::~AdpfHintSession() {
DCHECK_CALLED_ON_VALID_THREAD(factory_->thread_checker_);
factory_->hint_sessions_.erase(this);
AdpfMethods::Get().APerformanceHint_closeSessionFn(hint_session_);
}
void AdpfHintSession::ReportCpuCompletionTime(base::TimeDelta actual_duration) {
DCHECK_CALLED_ON_VALID_THREAD(factory_->thread_checker_);
AdpfMethods::Get().APerformanceHint_reportActualWorkDurationFn(
hint_session_, actual_duration.InNanoseconds());
}
void AdpfHintSession::WakeUp() {
DCHECK_CALLED_ON_VALID_THREAD(factory_->thread_checker_);
ReportCpuCompletionTime(target_duration_ * 1.5f);
}
HintSessionFactoryImpl::HintSessionFactoryImpl(
APerformanceHintManager* manager,
base::flat_set<base::PlatformThreadId> permanent_thread_ids)
: manager_(manager),
permanent_thread_ids_(std::move(permanent_thread_ids)) {
// Can be created on any thread.
DETACH_FROM_THREAD(thread_checker_);
}
HintSessionFactoryImpl::~HintSessionFactoryImpl() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(hint_sessions_.empty());
}
std::unique_ptr<HintSession> HintSessionFactoryImpl::CreateSession(
base::flat_set<base::PlatformThreadId> transient_thread_ids,
base::TimeDelta target_duration) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
transient_thread_ids.insert(permanent_thread_ids_.cbegin(),
permanent_thread_ids_.cend());
std::vector<int32_t> thread_ids(transient_thread_ids.begin(),
transient_thread_ids.end());
APerformanceHintSession* hint_session =
AdpfMethods::Get().APerformanceHint_createSessionFn(
manager_, thread_ids.data(), thread_ids.size(),
target_duration.InNanoseconds());
if (!hint_session)
return nullptr;
return std::make_unique<AdpfHintSession>(hint_session, this, target_duration);
}
void HintSessionFactoryImpl::WakeUp() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (hint_sessions_.empty())
return;
(*hint_sessions_.begin())->WakeUp();
}
} // namespace
// static
std::unique_ptr<HintSessionFactory> HintSessionFactory::Create(
base::flat_set<base::PlatformThreadId> permanent_thread_ids) {
if (base::android::BuildInfo::GetInstance()->sdk_int() <
base::android::SDK_VERSION_S)
return nullptr;
if (!AdpfMethods::Get().supported)
return nullptr;
APerformanceHintManager* manager =
AdpfMethods::Get().APerformanceHint_getManagerFn();
if (!manager)
return nullptr;
auto factory = std::make_unique<HintSessionFactoryImpl>(
manager, std::move(permanent_thread_ids));
// CreateSession is allowed to return null on unsupported device. Detect this
// at run time to avoid polluting any experiments with unsupported devices.
{
auto session = factory->CreateSession({}, base::Milliseconds(10));
if (!session)
return nullptr;
}
DETACH_FROM_THREAD(factory->thread_checker_);
return factory;
}
} // namespace viz
#else // BUILDFLAG(IS_ANDROID)
namespace viz {
std::unique_ptr<HintSessionFactory> HintSessionFactory::Create(
base::flat_set<base::PlatformThreadId> permanent_thread_ids) {
return nullptr;
}
} // namespace viz
#endif // BUILDFLAG(IS_ANDROID)