/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "PowerHalControllerBenchmarks" #include #include using ::android::enum_range; using ::android::hardware::vibrator::CompositeEffect; using ::android::hardware::vibrator::CompositePrimitive; using ::android::hardware::vibrator::Effect; using ::android::hardware::vibrator::EffectStrength; using ::benchmark::Counter; using ::benchmark::Fixture; using ::benchmark::kMicrosecond; using ::benchmark::State; using ::benchmark::internal::Benchmark; using namespace android; using namespace std::chrono_literals; class VibratorBench : public Fixture { public: void SetUp(State& /*state*/) override { mController.init(); } void TearDown(State& state) override { turnVibratorOff(state); } static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); } static void DefaultArgs(Benchmark* /*b*/) { // none } protected: vibrator::HalController mController; auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); } bool hasCapabilities(vibrator::Capabilities&& query, State& state) { auto result = mController.getInfo().capabilities; if (result.isFailed()) { state.SkipWithError(result.errorMessage()); return false; } if (!result.isOk()) { return false; } return (result.value() & query) == query; } void turnVibratorOff(State& state) { checkHalResult(halCall(mController, [](auto hal) { return hal->off(); }), state); } template bool checkHalResult(const vibrator::HalResult& result, State& state) { if (result.isFailed()) { state.SkipWithError(result.errorMessage()); return false; } return true; } template vibrator::HalResult halCall(vibrator::HalController& controller, const vibrator::HalFunction>& halFn) { return controller.doWithRetry(halFn, "benchmark"); } }; #define BENCHMARK_WRAPPER(fixt, test, code) \ BENCHMARK_DEFINE_F(fixt, test) \ /* NOLINTNEXTLINE */ \ (State& state){code} BENCHMARK_REGISTER_F(fixt, test) \ ->Apply(fixt::DefaultConfig) \ ->Apply(fixt::DefaultArgs) BENCHMARK_WRAPPER(VibratorBench, init, { for (auto _ : state) { state.PauseTiming(); vibrator::HalController controller; state.ResumeTiming(); controller.init(); } }); BENCHMARK_WRAPPER(VibratorBench, initCached, { for (auto _ : state) { mController.init(); } }); BENCHMARK_WRAPPER(VibratorBench, ping, { for (auto _ : state) { state.ResumeTiming(); auto ret = halCall(mController, [](auto hal) { return hal->ping(); }); state.PauseTiming(); checkHalResult(ret, state); } }); BENCHMARK_WRAPPER(VibratorBench, tryReconnect, { for (auto _ : state) { mController.tryReconnect(); } }); BENCHMARK_WRAPPER(VibratorBench, on, { auto duration = 60s; auto callback = []() {}; for (auto _ : state) { state.ResumeTiming(); auto ret = halCall(mController, [&](auto hal) { return hal->on(duration, callback); }); state.PauseTiming(); if (checkHalResult(ret, state)) { turnVibratorOff(state); } } }); BENCHMARK_WRAPPER(VibratorBench, off, { auto duration = 60s; auto callback = []() {}; for (auto _ : state) { state.PauseTiming(); auto ret = halCall(mController, [&](auto hal) { return hal->on(duration, callback); }); if (!checkHalResult(ret, state)) { continue; } state.ResumeTiming(); turnVibratorOff(state); } }); BENCHMARK_WRAPPER(VibratorBench, setAmplitude, { if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) { return; } auto duration = 60s; auto callback = []() {}; auto amplitude = 1.0f; for (auto _ : state) { state.PauseTiming(); vibrator::HalController controller; controller.init(); auto result = halCall(controller, [&](auto hal) { return hal->on(duration, callback); }); if (!checkHalResult(result, state)) { continue; } state.ResumeTiming(); auto ret = halCall(controller, [&](auto hal) { return hal->setAmplitude(amplitude); }); state.PauseTiming(); if (checkHalResult(ret, state)) { turnVibratorOff(state); } } }); BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, { if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) { return; } auto duration = 6000s; auto callback = []() {}; auto amplitude = 1.0f; auto onResult = halCall(mController, [&](auto hal) { return hal->on(duration, callback); }); checkHalResult(onResult, state); for (auto _ : state) { auto ret = halCall(mController, [&](auto hal) { return hal->setAmplitude(amplitude); }); checkHalResult(ret, state); } }); BENCHMARK_WRAPPER(VibratorBench, setExternalControl, { if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) { return; } for (auto _ : state) { state.PauseTiming(); vibrator::HalController controller; controller.init(); state.ResumeTiming(); auto ret = halCall(controller, [](auto hal) { return hal->setExternalControl(true); }); state.PauseTiming(); if (checkHalResult(ret, state)) { auto result = halCall(controller, [](auto hal) { return hal->setExternalControl(false); }); checkHalResult(result, state); } } }); BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, { if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) { return; } for (auto _ : state) { state.ResumeTiming(); auto result = halCall(mController, [](auto hal) { return hal->setExternalControl(true); }); state.PauseTiming(); if (checkHalResult(result, state)) { auto ret = halCall(mController, [](auto hal) { return hal->setExternalControl(false); }); checkHalResult(ret, state); } } }); BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, { if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) { return; } auto amplitude = 1.0f; auto onResult = halCall(mController, [](auto hal) { return hal->setExternalControl(true); }); checkHalResult(onResult, state); for (auto _ : state) { auto ret = halCall(mController, [&](auto hal) { return hal->setAmplitude(amplitude); }); checkHalResult(ret, state); } auto offResult = halCall(mController, [](auto hal) { return hal->setExternalControl(false); }); checkHalResult(offResult, state); }); BENCHMARK_WRAPPER(VibratorBench, getInfo, { for (auto _ : state) { state.PauseTiming(); vibrator::HalController controller; controller.init(); state.ResumeTiming(); auto result = controller.getInfo(); checkHalResult(result.capabilities, state); checkHalResult(result.supportedEffects, state); checkHalResult(result.supportedPrimitives, state); checkHalResult(result.primitiveDurations, state); checkHalResult(result.resonantFrequency, state); checkHalResult(result.qFactor, state); } }); BENCHMARK_WRAPPER(VibratorBench, getInfoCached, { // First call to cache values. mController.getInfo(); for (auto _ : state) { auto result = mController.getInfo(); checkHalResult(result.capabilities, state); checkHalResult(result.supportedEffects, state); checkHalResult(result.supportedPrimitives, state); checkHalResult(result.primitiveDurations, state); checkHalResult(result.resonantFrequency, state); checkHalResult(result.qFactor, state); } }); class VibratorEffectsBench : public VibratorBench { public: static void DefaultArgs(Benchmark* b) { vibrator::HalController controller; auto effectsResult = controller.getInfo().supportedEffects; if (!effectsResult.isOk()) { return; } std::vector supported = effectsResult.value(); b->ArgNames({"Effect", "Strength"}); if (supported.empty()) { b->Args({static_cast(-1), static_cast(-1)}); return; } for (const auto& effect : enum_range()) { if (std::find(supported.begin(), supported.end(), effect) == supported.end()) { continue; } for (const auto& strength : enum_range()) { b->Args({static_cast(effect), static_cast(strength)}); } } } protected: bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; } auto getEffect(const State& state) const { return static_cast(this->getOtherArg(state, 0)); } auto getStrength(const State& state) const { return static_cast(this->getOtherArg(state, 1)); } }; BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, { if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) { return; } if (!hasArgs(state)) { return; } int32_t id = 1; auto effect = getEffect(state); auto strength = getStrength(state); for (auto _ : state) { state.ResumeTiming(); auto ret = halCall(mController, [&](auto hal) { return hal->alwaysOnEnable(id, effect, strength); }); state.PauseTiming(); if (checkHalResult(ret, state)) { auto disableResult = halCall(mController, [&](auto hal) { return hal->alwaysOnDisable(id); }); checkHalResult(disableResult, state); } } }); BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, { if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) { return; } if (!hasArgs(state)) { return; } int32_t id = 1; auto effect = getEffect(state); auto strength = getStrength(state); for (auto _ : state) { state.PauseTiming(); auto enableResult = halCall(mController, [&](auto hal) { return hal->alwaysOnEnable(id, effect, strength); }); if (!checkHalResult(enableResult, state)) { continue; } state.ResumeTiming(); auto disableResult = halCall(mController, [&](auto hal) { return hal->alwaysOnDisable(id); }); checkHalResult(disableResult, state); } }); BENCHMARK_WRAPPER(VibratorEffectsBench, performEffect, { if (!hasArgs(state)) { return; } auto effect = getEffect(state); auto strength = getStrength(state); auto callback = []() {}; for (auto _ : state) { state.ResumeTiming(); auto ret = halCall(mController, [&](auto hal) { return hal->performEffect(effect, strength, callback); }); state.PauseTiming(); if (checkHalResult(ret, state)) { turnVibratorOff(state); } } }); class VibratorPrimitivesBench : public VibratorBench { public: static void DefaultArgs(Benchmark* b) { vibrator::HalController controller; auto primitivesResult = controller.getInfo().supportedPrimitives; if (!primitivesResult.isOk()) { return; } std::vector supported = primitivesResult.value(); b->ArgNames({"Primitive"}); if (supported.empty()) { b->Args({static_cast(-1)}); return; } for (const auto& primitive : enum_range()) { if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) { continue; } if (primitive == CompositePrimitive::NOOP) { continue; } b->Args({static_cast(primitive)}); } } protected: bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; } auto getPrimitive(const State& state) const { return static_cast(this->getOtherArg(state, 0)); } }; BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, { if (!hasCapabilities(vibrator::Capabilities::COMPOSE_EFFECTS, state)) { return; } if (!hasArgs(state)) { return; } CompositeEffect effect; effect.primitive = getPrimitive(state); effect.scale = 1.0f; effect.delayMs = static_cast(0); std::vector effects; effects.push_back(effect); auto callback = []() {}; for (auto _ : state) { state.ResumeTiming(); auto ret = halCall(mController, [&](auto hal) { return hal->performComposedEffect(effects, callback); }); state.PauseTiming(); if (checkHalResult(ret, state)) { turnVibratorOff(state); } } }); BENCHMARK_MAIN();