audio HAL: Optimize for Spatial Audio am: afc2da8e8b

Original change: https://googleplex-android-review.googlesource.com/c/platform/hardware/interfaces/+/20612422

Change-Id: Ic4fdd6a007bb1f4abec1dedf0352b79af3f77625
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Andy Hung
2022-12-17 04:05:42 +00:00
committed by Automerger Merge Worker
2 changed files with 168 additions and 1 deletions

View File

@@ -3,7 +3,7 @@ service vendor.audio-hal /vendor/bin/hw/android.hardware.audio.service
user audioserver
# media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub
capabilities BLOCK_SUSPEND
capabilities BLOCK_SUSPEND SYS_NICE
# setting RLIMIT_RTPRIO allows binder RT priority inheritance
rlimit rtprio 10 10
ioprio rt 4

View File

@@ -25,8 +25,11 @@
#define ATRACE_TAG ATRACE_TAG_AUDIO
#include <HidlUtils.h>
#include <android/log.h>
#include <cutils/properties.h>
#include <media/EffectsFactoryApi.h>
#include <mediautils/ScopedStatistics.h>
#include <sys/syscall.h>
#include <system/audio_effects/effect_spatializer.h>
#include <util/EffectUtils.h>
#include <utils/Trace.h>
@@ -47,6 +50,160 @@ using ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::implementati
namespace {
/**
* Some basic scheduling tools.
*/
namespace scheduler {
int getCpu() {
return sched_getcpu();
}
uint64_t getAffinity(pid_t tid) {
cpu_set_t set;
CPU_ZERO_S(sizeof(set), &set);
if (sched_getaffinity(tid, sizeof(set), &set)) {
ALOGW("%s: for tid:%d returning 0, failed %s", __func__, tid, strerror(errno));
return 0;
}
const int count = CPU_COUNT_S(sizeof(set), &set);
uint64_t mask = 0;
for (int i = 0; i < CPU_SETSIZE; ++i) {
if (CPU_ISSET_S(i, sizeof(set), &set)) {
mask |= 1 << i;
}
}
ALOGV("%s: for tid:%d returning cpu count %d mask %llu", __func__, tid, count,
(unsigned long long)mask);
return mask;
}
status_t setAffinity(pid_t tid, uint64_t mask) {
cpu_set_t set;
CPU_ZERO_S(sizeof(set), &set);
for (uint64_t m = mask; m != 0;) {
uint64_t tz = __builtin_ctz(m);
CPU_SET_S(tz, sizeof(set), &set);
m &= ~(1 << tz);
}
if (sched_setaffinity(tid, sizeof(set), &set)) {
ALOGW("%s: for tid:%d setting cpu mask %llu failed %s", __func__, tid,
(unsigned long long)mask, strerror(errno));
return -errno;
}
ALOGV("%s: for tid:%d setting cpu mask %llu", __func__, tid, (unsigned long long)mask);
return OK;
}
__unused status_t setPriority(pid_t tid, int policy, int priority) {
struct sched_param param {
.sched_priority = priority,
};
if (sched_setscheduler(tid, policy, &param) != 0) {
ALOGW("%s: Cannot set FIFO priority for tid %d to policy %d priority %d %s", __func__, tid,
policy, priority, strerror(errno));
return -errno;
}
ALOGV("%s: Successfully set priority for tid %d to policy %d priority %d", __func__, tid,
policy, priority);
return NO_ERROR;
}
status_t setUtilMin(pid_t tid, uint32_t utilMin) {
// Currently, there is no wrapper in bionic: b/183240349.
struct {
uint32_t size;
uint32_t sched_policy;
uint64_t sched_flags;
int32_t sched_nice;
uint32_t sched_priority;
uint64_t sched_runtime;
uint64_t sched_deadline;
uint64_t sched_period;
uint32_t sched_util_min;
uint32_t sched_util_max;
} attr{
.size = sizeof(attr),
.sched_flags = SCHED_FLAG_KEEP_ALL | SCHED_FLAG_UTIL_CLAMP_MIN,
.sched_util_min = utilMin,
};
if (syscall(__NR_sched_setattr, tid, &attr, 0 /* flags */)) {
ALOGW("%s: Cannot set sched_util_min for pid %d to %u %s", __func__, tid, utilMin,
strerror(errno));
return -errno;
}
ALOGV("%s: Successfully set sched_util_min for pid %d to %u", __func__, tid, utilMin);
return NO_ERROR;
}
/*
Attempts to raise the priority and usage of tid for spatialization.
Returns OK if everything works.
*/
status_t updateSpatializerPriority(pid_t tid) {
status_t status = OK;
const int cpu = getCpu();
ALOGV("%s: current CPU:%d", __func__, cpu);
const auto currentAffinity = getAffinity(tid);
ALOGV("%s: current Affinity:%llx", __func__, (unsigned long long)currentAffinity);
// Set the desired CPU core affinity.
// Typically this would be done to move the Spatializer effect off of the little cores.
// The mid cores and large cores typically have more FP/NEON units
// and will advantageously reduce power and prevent glitches due CPU limitations.
//
// Since this is SOC dependent, we do not set the core affinity here but
// prefer to set the util_clamp_min below.
//
constexpr uint64_t kDefaultAffinity = 0;
const int32_t desiredAffinity =
property_get_int32("audio.spatializer.effect.affinity", kDefaultAffinity);
if (desiredAffinity != 0 && (desiredAffinity & ~currentAffinity) == 0) {
const status_t localStatus = setAffinity(tid, desiredAffinity);
status = status ? status : localStatus;
}
// Set the util_clamp_min.
// This is beneficial to reduce glitches when starting up, or due to scheduler
// thread statistics reset (e.g. core migration), which cause the CPU frequency to drop
// to minimum.
//
// Experimentation has found that moving to a mid core over a little core reduces
// power if the mid core (e.g. A76/78) has more (e.g. 2x) FP/NEON units
// than the little core (e.g. A55).
// A possible value is 300.
//
constexpr uint32_t kUtilMin = 0;
const int32_t utilMin = property_get_int32("audio.spatializer.effect.util_clamp_min", kUtilMin);
if (utilMin > 0 && utilMin <= 1024) {
const status_t localStatus = setUtilMin(tid, utilMin);
status = status ? status : localStatus;
}
#if 0
// Provided for local vendor testing but not enabled as audioserver does this for us.
//
// Set priority if specified.
constexpr int32_t kRTPriorityMin = 1;
constexpr int32_t kRTPriorityMax = 3;
const int32_t priorityBoost =
property_get_int32("audio.spatializer.priority", kRTPriorityMin);
if (priorityBoost >= kRTPriorityMin && priorityBoost <= kRTPriorityMax) {
const status_t localStatus = scheduler::setPriority(threadId, SCHED_FIFO, priorityBoost);
status = status ? status : localStatus;
}
#endif
return status;
}
} // namespace scheduler
#define SCOPED_STATS() \
::android::mediautils::ScopedStatistics scopedStatistics { \
std::string("EffectHal::").append(__func__), mEffectHal->mStatistics \
@@ -83,6 +240,16 @@ class ProcessThread : public Thread {
};
bool ProcessThread::threadLoop() {
// For a spatializer effect, we perform scheduler adjustments to reduce glitches and power.
{
effect_descriptor_t halDescriptor{};
if ((*mEffect)->get_descriptor(mEffect, &halDescriptor) == NO_ERROR &&
memcmp(&halDescriptor.type, FX_IID_SPATIALIZER, sizeof(effect_uuid_t)) == 0) {
const status_t status = scheduler::updateSpatializerPriority(gettid());
ALOGW_IF(status != OK, "Failed to update Spatializer priority");
}
}
// This implementation doesn't return control back to the Thread until it decides to stop,
// as the Thread uses mutexes, and this can lead to priority inversion.
while (!std::atomic_load_explicit(mStop, std::memory_order_acquire)) {