1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "benchmark/benchmark.h" 18 19 #include <aidl/android/hardware/vibrator/BnVibratorCallback.h> 20 #include <aidl/android/hardware/vibrator/IVibrator.h> 21 22 #include <android/binder_manager.h> 23 #include <android/binder_process.h> 24 #include <future> 25 26 using ::benchmark::Counter; 27 using ::benchmark::Fixture; 28 using ::benchmark::kMicrosecond; 29 using ::benchmark::State; 30 using ::benchmark::internal::Benchmark; 31 using ::ndk::enum_range; 32 33 using namespace ::aidl::android::hardware::vibrator; 34 using namespace ::std::chrono_literals; 35 36 // Fixed number of iterations for benchmarks that trigger a vibration on the loop. 37 // They require slow cleanup to ensure a stable state on each run and less noisy metrics. 38 static constexpr auto VIBRATION_ITERATIONS = 500; 39 40 // Timeout to wait for vibration callback completion. 41 static constexpr auto VIBRATION_CALLBACK_TIMEOUT = 100ms; 42 43 // Max duration the vibrator can be turned on, in milliseconds. 44 static constexpr uint32_t MAX_ON_DURATION_MS = UINT16_MAX; 45 46 class VibratorBench : public Fixture { 47 public: SetUp(State &)48 void SetUp(State& /*state*/) override { 49 ABinderProcess_setThreadPoolMaxThreadCount(1); 50 ABinderProcess_startThreadPool(); 51 auto serviceName = std::string(IVibrator::descriptor) + "/default"; 52 this->mVibrator = IVibrator::fromBinder( 53 ndk::SpAIBinder(AServiceManager_waitForService(serviceName.c_str()))); 54 } 55 TearDown(State &)56 void TearDown(State& /*state*/) override { 57 if (mVibrator) { 58 mVibrator->off(); 59 mVibrator->setExternalControl(false); 60 } 61 } 62 DefaultConfig(Benchmark * b)63 static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); } 64 DefaultArgs(Benchmark *)65 static void DefaultArgs(Benchmark* /*b*/) { /* none */ } 66 67 protected: 68 std::shared_ptr<IVibrator> mVibrator; 69 getOtherArg(const State & state,std::size_t index) const70 auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); } 71 hasCapabilities(int32_t capabilities)72 int32_t hasCapabilities(int32_t capabilities) { 73 int32_t deviceCapabilities = 0; 74 this->mVibrator->getCapabilities(&deviceCapabilities); 75 return (deviceCapabilities & capabilities) == capabilities; 76 } 77 shouldSkipWithError(State & state,const ndk::ScopedAStatus && status)78 bool shouldSkipWithError(State& state, const ndk::ScopedAStatus&& status) { 79 if (!status.isOk()) { 80 state.SkipWithError(status.getMessage()); 81 return true; 82 } 83 return false; 84 } 85 waitForComplete(std::future<void> & callbackFuture)86 void waitForComplete(std::future<void>& callbackFuture) { 87 // Wait until the HAL has finished processing previous vibration before starting a new one, 88 // so the HAL state is consistent on each run and metrics are less noisy. Some of the newest 89 // HAL implementations are waiting on previous vibration cleanup and might be significantly 90 // slower, so make sure we measure vibrations on a clean slate. 91 if (callbackFuture.valid()) { 92 callbackFuture.wait_for(VIBRATION_CALLBACK_TIMEOUT); 93 } 94 } 95 SlowBenchConfig(Benchmark * b)96 static void SlowBenchConfig(Benchmark* b) { b->Iterations(VIBRATION_ITERATIONS); } 97 }; 98 99 class SlowVibratorBench : public VibratorBench { 100 public: DefaultConfig(Benchmark * b)101 static void DefaultConfig(Benchmark* b) { 102 VibratorBench::DefaultConfig(b); 103 SlowBenchConfig(b); 104 } 105 }; 106 107 class HalCallback : public BnVibratorCallback { 108 public: 109 HalCallback() = default; 110 ~HalCallback() = default; 111 onComplete()112 ndk::ScopedAStatus onComplete() override { 113 mPromise.set_value(); 114 return ndk::ScopedAStatus::ok(); 115 } 116 getFuture()117 std::future<void> getFuture() { return mPromise.get_future(); } 118 119 private: 120 std::promise<void> mPromise; 121 }; 122 123 #define BENCHMARK_WRAPPER(fixt, test, code) \ 124 BENCHMARK_DEFINE_F(fixt, test) \ 125 /* NOLINTNEXTLINE */ \ 126 (State & state) { \ 127 if (!mVibrator) { \ 128 state.SkipWithMessage("HAL unavailable"); \ 129 return; \ 130 } \ 131 \ 132 code \ 133 } \ 134 BENCHMARK_REGISTER_F(fixt, test)->Apply(fixt::DefaultConfig)->Apply(fixt::DefaultArgs) 135 136 BENCHMARK_WRAPPER(SlowVibratorBench, on, { 137 auto ms = MAX_ON_DURATION_MS; 138 139 for (auto _ : state) { 140 auto cb = hasCapabilities(IVibrator::CAP_ON_CALLBACK) 141 ? ndk::SharedRefBase::make<HalCallback>() 142 : nullptr; 143 // Grab the future before callback promise is destroyed by the HAL. 144 auto cbFuture = cb ? cb->getFuture() : std::future<void>(); 145 146 // Test 147 if (shouldSkipWithError(state, mVibrator->on(ms, cb))) { 148 return; 149 } 150 151 // Cleanup 152 state.PauseTiming(); 153 if (shouldSkipWithError(state, mVibrator->off())) { 154 return; 155 } 156 waitForComplete(cbFuture); 157 state.ResumeTiming(); 158 } 159 }); 160 161 BENCHMARK_WRAPPER(SlowVibratorBench, off, { 162 auto ms = MAX_ON_DURATION_MS; 163 164 for (auto _ : state) { 165 auto cb = hasCapabilities(IVibrator::CAP_ON_CALLBACK) 166 ? ndk::SharedRefBase::make<HalCallback>() 167 : nullptr; 168 // Grab the future before callback promise is destroyed by the HAL. 169 auto cbFuture = cb ? cb->getFuture() : std::future<void>(); 170 171 // Setup 172 state.PauseTiming(); 173 if (shouldSkipWithError(state, mVibrator->on(ms, cb))) { 174 return; 175 } 176 state.ResumeTiming(); 177 178 // Test 179 if (shouldSkipWithError(state, mVibrator->off())) { 180 return; 181 } 182 183 // Cleanup 184 state.PauseTiming(); 185 waitForComplete(cbFuture); 186 state.ResumeTiming(); 187 } 188 }); 189 190 BENCHMARK_WRAPPER(VibratorBench, getCapabilities, { 191 int32_t capabilities = 0; 192 193 for (auto _ : state) { 194 if (shouldSkipWithError(state, mVibrator->getCapabilities(&capabilities))) { 195 return; 196 } 197 } 198 }); 199 200 BENCHMARK_WRAPPER(VibratorBench, setAmplitude, { 201 auto ms = MAX_ON_DURATION_MS; 202 float amplitude = 1.0f; 203 204 if (!hasCapabilities(IVibrator::CAP_AMPLITUDE_CONTROL)) { 205 state.SkipWithMessage("amplitude control unavailable"); 206 return; 207 } 208 209 auto cb = hasCapabilities(IVibrator::CAP_ON_CALLBACK) 210 ? ndk::SharedRefBase::make<HalCallback>() 211 : nullptr; 212 if (shouldSkipWithError(state, mVibrator->on(ms, cb))) { 213 return; 214 } 215 216 for (auto _ : state) { 217 if (shouldSkipWithError(state, mVibrator->setAmplitude(amplitude))) { 218 return; 219 } 220 } 221 }); 222 223 BENCHMARK_WRAPPER(VibratorBench, setExternalControl, { 224 if (!hasCapabilities(IVibrator::CAP_EXTERNAL_CONTROL)) { 225 state.SkipWithMessage("external control unavailable"); 226 return; 227 } 228 229 for (auto _ : state) { 230 // Test 231 if (shouldSkipWithError(state, mVibrator->setExternalControl(true))) { 232 return; 233 } 234 235 // Cleanup 236 state.PauseTiming(); 237 if (shouldSkipWithError(state, mVibrator->setExternalControl(false))) { 238 return; 239 } 240 state.ResumeTiming(); 241 } 242 }); 243 244 BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitude, { 245 auto externalControl = static_cast<int32_t>(IVibrator::CAP_EXTERNAL_CONTROL); 246 auto externalAmplitudeControl = 247 static_cast<int32_t>(IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL); 248 if (!hasCapabilities(externalControl | externalAmplitudeControl)) { 249 state.SkipWithMessage("external amplitude control unavailable"); 250 return; 251 } 252 253 if (shouldSkipWithError(state, mVibrator->setExternalControl(true))) { 254 return; 255 } 256 257 float amplitude = 1.0f; 258 for (auto _ : state) { 259 if (shouldSkipWithError(state, mVibrator->setAmplitude(amplitude))) { 260 return; 261 } 262 } 263 }); 264 265 BENCHMARK_WRAPPER(VibratorBench, getSupportedEffects, { 266 std::vector<Effect> supportedEffects; 267 268 for (auto _ : state) { 269 if (shouldSkipWithError(state, mVibrator->getSupportedEffects(&supportedEffects))) { 270 return; 271 } 272 } 273 }); 274 275 BENCHMARK_WRAPPER(VibratorBench, getSupportedAlwaysOnEffects, { 276 if (!hasCapabilities(IVibrator::CAP_ALWAYS_ON_CONTROL)) { 277 state.SkipWithMessage("always on control unavailable"); 278 return; 279 } 280 281 std::vector<Effect> supportedEffects; 282 283 for (auto _ : state) { 284 if (shouldSkipWithError(state, mVibrator->getSupportedAlwaysOnEffects(&supportedEffects))) { 285 return; 286 } 287 } 288 }); 289 290 BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitives, { 291 std::vector<CompositePrimitive> supportedPrimitives; 292 293 for (auto _ : state) { 294 if (shouldSkipWithError(state, mVibrator->getSupportedPrimitives(&supportedPrimitives))) { 295 return; 296 } 297 } 298 }); 299 300 class EffectsVibratorBench : public VibratorBench { 301 public: DefaultArgs(Benchmark * b)302 static void DefaultArgs(Benchmark* b) { 303 b->ArgNames({"Effect", "Strength"}); 304 for (const auto& effect : enum_range<Effect>()) { 305 for (const auto& strength : enum_range<EffectStrength>()) { 306 b->Args({static_cast<long>(effect), static_cast<long>(strength)}); 307 } 308 } 309 } 310 311 protected: getEffect(const State & state) const312 auto getEffect(const State& state) const { 313 return static_cast<Effect>(this->getOtherArg(state, 0)); 314 } 315 getStrength(const State & state) const316 auto getStrength(const State& state) const { 317 return static_cast<EffectStrength>(this->getOtherArg(state, 1)); 318 } 319 isEffectSupported(const Effect & effect)320 bool isEffectSupported(const Effect& effect) { 321 std::vector<Effect> supported; 322 mVibrator->getSupportedEffects(&supported); 323 return std::find(supported.begin(), supported.end(), effect) != supported.end(); 324 } 325 isAlwaysOnEffectSupported(const Effect & effect)326 bool isAlwaysOnEffectSupported(const Effect& effect) { 327 std::vector<Effect> supported; 328 mVibrator->getSupportedAlwaysOnEffects(&supported); 329 return std::find(supported.begin(), supported.end(), effect) != supported.end(); 330 } 331 }; 332 333 class SlowEffectsVibratorBench : public EffectsVibratorBench { 334 public: DefaultConfig(Benchmark * b)335 static void DefaultConfig(Benchmark* b) { 336 EffectsVibratorBench::DefaultConfig(b); 337 SlowBenchConfig(b); 338 } 339 }; 340 341 BENCHMARK_WRAPPER(EffectsVibratorBench, alwaysOnEnable, { 342 if (!hasCapabilities(IVibrator::CAP_ALWAYS_ON_CONTROL)) { 343 state.SkipWithMessage("always on control unavailable"); 344 return; 345 } 346 347 int32_t id = 1; 348 auto effect = getEffect(state); 349 auto strength = getStrength(state); 350 351 if (!isAlwaysOnEffectSupported(effect)) { 352 state.SkipWithMessage("always on effect unsupported"); 353 return; 354 } 355 356 for (auto _ : state) { 357 // Test 358 if (shouldSkipWithError(state, mVibrator->alwaysOnEnable(id, effect, strength))) { 359 return; 360 } 361 362 // Cleanup 363 state.PauseTiming(); 364 if (shouldSkipWithError(state, mVibrator->alwaysOnDisable(id))) { 365 return; 366 } 367 state.ResumeTiming(); 368 } 369 }); 370 371 BENCHMARK_WRAPPER(EffectsVibratorBench, alwaysOnDisable, { 372 if (!hasCapabilities(IVibrator::CAP_ALWAYS_ON_CONTROL)) { 373 state.SkipWithMessage("always on control unavailable"); 374 return; 375 } 376 377 int32_t id = 1; 378 auto effect = getEffect(state); 379 auto strength = getStrength(state); 380 381 if (!isAlwaysOnEffectSupported(effect)) { 382 state.SkipWithMessage("always on effect unsupported"); 383 return; 384 } 385 386 for (auto _ : state) { 387 // Setup 388 state.PauseTiming(); 389 if (shouldSkipWithError(state, mVibrator->alwaysOnEnable(id, effect, strength))) { 390 return; 391 } 392 state.ResumeTiming(); 393 394 // Test 395 if (shouldSkipWithError(state, mVibrator->alwaysOnDisable(id))) { 396 return; 397 } 398 } 399 }); 400 401 BENCHMARK_WRAPPER(SlowEffectsVibratorBench, perform, { 402 auto effect = getEffect(state); 403 auto strength = getStrength(state); 404 405 if (!isEffectSupported(effect)) { 406 state.SkipWithMessage("effect unsupported"); 407 return; 408 } 409 410 int32_t lengthMs = 0; 411 412 for (auto _ : state) { 413 auto cb = hasCapabilities(IVibrator::CAP_PERFORM_CALLBACK) 414 ? ndk::SharedRefBase::make<HalCallback>() 415 : nullptr; 416 // Grab the future before callback promise is destroyed by the HAL. 417 auto cbFuture = cb ? cb->getFuture() : std::future<void>(); 418 419 // Test 420 if (shouldSkipWithError(state, mVibrator->perform(effect, strength, cb, &lengthMs))) { 421 return; 422 } 423 424 // Cleanup 425 state.PauseTiming(); 426 if (shouldSkipWithError(state, mVibrator->off())) { 427 return; 428 } 429 waitForComplete(cbFuture); 430 state.ResumeTiming(); 431 } 432 }); 433 434 class PrimitivesVibratorBench : public VibratorBench { 435 public: DefaultArgs(Benchmark * b)436 static void DefaultArgs(Benchmark* b) { 437 b->ArgNames({"Primitive"}); 438 for (const auto& primitive : enum_range<CompositePrimitive>()) { 439 b->Args({static_cast<long>(primitive)}); 440 } 441 } 442 443 protected: getPrimitive(const State & state) const444 auto getPrimitive(const State& state) const { 445 return static_cast<CompositePrimitive>(this->getOtherArg(state, 0)); 446 } 447 isPrimitiveSupported(const CompositePrimitive & primitive)448 bool isPrimitiveSupported(const CompositePrimitive& primitive) { 449 std::vector<CompositePrimitive> supported; 450 mVibrator->getSupportedPrimitives(&supported); 451 return std::find(supported.begin(), supported.end(), primitive) != supported.end(); 452 } 453 }; 454 455 class SlowPrimitivesVibratorBench : public PrimitivesVibratorBench { 456 public: DefaultConfig(Benchmark * b)457 static void DefaultConfig(Benchmark* b) { 458 PrimitivesVibratorBench::DefaultConfig(b); 459 SlowBenchConfig(b); 460 } 461 }; 462 463 BENCHMARK_WRAPPER(PrimitivesVibratorBench, getCompositionDelayMax, { 464 int32_t ms = 0; 465 466 for (auto _ : state) { 467 if (shouldSkipWithError(state, mVibrator->getCompositionDelayMax(&ms))) { 468 return; 469 } 470 } 471 }); 472 473 BENCHMARK_WRAPPER(PrimitivesVibratorBench, getCompositionSizeMax, { 474 int32_t size = 0; 475 476 for (auto _ : state) { 477 if (shouldSkipWithError(state, mVibrator->getCompositionSizeMax(&size))) { 478 return; 479 } 480 } 481 }); 482 483 BENCHMARK_WRAPPER(PrimitivesVibratorBench, getPrimitiveDuration, { 484 if (!hasCapabilities(IVibrator::CAP_COMPOSE_EFFECTS)) { 485 state.SkipWithMessage("compose effects unavailable"); 486 return; 487 } 488 489 auto primitive = getPrimitive(state); 490 int32_t ms = 0; 491 492 if (!isPrimitiveSupported(primitive)) { 493 state.SkipWithMessage("primitive unsupported"); 494 return; 495 } 496 497 for (auto _ : state) { 498 if (shouldSkipWithError(state, mVibrator->getPrimitiveDuration(primitive, &ms))) { 499 return; 500 } 501 } 502 }); 503 504 BENCHMARK_WRAPPER(SlowPrimitivesVibratorBench, compose, { 505 if (!hasCapabilities(IVibrator::CAP_COMPOSE_EFFECTS)) { 506 state.SkipWithMessage("compose effects unavailable"); 507 return; 508 } 509 510 CompositeEffect effect; 511 effect.primitive = getPrimitive(state); 512 effect.scale = 1.0f; 513 effect.delayMs = 0; 514 515 if (effect.primitive == CompositePrimitive::NOOP) { 516 state.SkipWithMessage("skipping primitive NOOP"); 517 return; 518 } 519 if (!isPrimitiveSupported(effect.primitive)) { 520 state.SkipWithMessage("primitive unsupported"); 521 return; 522 } 523 524 std::vector<CompositeEffect> effects; 525 effects.push_back(effect); 526 527 for (auto _ : state) { 528 auto cb = ndk::SharedRefBase::make<HalCallback>(); 529 // Grab the future before callback promise is moved and destroyed by the HAL. 530 auto cbFuture = cb->getFuture(); 531 532 // Test 533 if (shouldSkipWithError(state, mVibrator->compose(effects, cb))) { 534 return; 535 } 536 537 // Cleanup 538 state.PauseTiming(); 539 if (shouldSkipWithError(state, mVibrator->off())) { 540 return; 541 } 542 waitForComplete(cbFuture); 543 state.ResumeTiming(); 544 } 545 }); 546 547 BENCHMARK_MAIN(); 548