• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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