1 /*
2 * Copyright (C) 2024 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 "GpuCalculationHelpers.h"
18
19 using std::literals::chrono_literals::operator""ns;
20 using std::chrono::duration;
21 using std::chrono::duration_cast;
22 using std::chrono::nanoseconds;
23
24 namespace aidl {
25 namespace google {
26 namespace hardware {
27 namespace power {
28 namespace impl {
29 namespace pixel {
30
31 // until std::lerp is available...
32 template <typename R, typename P>
lerp(duration<R,P> a,duration<R,P> b,float t)33 duration<R, P> lerp(duration<R, P> a, duration<R, P> b, float t) {
34 auto const fa = duration_cast<duration<float, std::nano>>(a);
35 auto const fb = duration_cast<duration<float, std::nano>>(b);
36 return duration_cast<duration<R, P>>(fa + (fb - fa) * t);
37 }
38
39 // In the event that the client reports that the GPU + CPU time is less than
40 // the total time, expand both CPU and GPU timings so that this constraint
41 // holds true.
sanitize_timings(nanoseconds total,nanoseconds cpu,nanoseconds gpu)42 nanoseconds sanitize_timings(nanoseconds total, nanoseconds cpu, nanoseconds gpu) {
43 auto const accounted_portion = cpu + gpu;
44 auto const unaccounted_portion = total - accounted_portion;
45 if (unaccounted_portion > 0ns) {
46 auto const cpu_portion = duration_cast<duration<float, std::nano>>(cpu) / accounted_portion;
47 gpu = lerp(gpu, gpu + unaccounted_portion, (1.0 - cpu_portion));
48 }
49 return gpu;
50 }
51
subtotal_timings_invalid(WorkDuration const & observation)52 inline bool subtotal_timings_invalid(WorkDuration const &observation) {
53 return observation.durationNanos < observation.gpuDurationNanos ||
54 observation.durationNanos < observation.cpuDurationNanos;
55 }
56
calculate_capacity(WorkDuration observation,nanoseconds target,Frequency gpu_frequency)57 Cycles calculate_capacity(WorkDuration observation, nanoseconds target, Frequency gpu_frequency) {
58 auto const total = nanoseconds(observation.durationNanos);
59
60 auto const overrun = duration_cast<duration<float, std::nano>>(total - target);
61 if (overrun < 0ns || subtotal_timings_invalid(observation)) {
62 return Cycles(0);
63 }
64
65 auto const gpu = sanitize_timings(total, nanoseconds(observation.cpuDurationNanos),
66 nanoseconds(observation.gpuDurationNanos));
67
68 auto const gpu_time_attribution_pct = gpu_time_attribution(total, gpu);
69 auto const gpu_delta = overrun * gpu_time_attribution_pct;
70 return gpu_frequency * gpu_delta;
71 }
72
73 } // namespace pixel
74 } // namespace impl
75 } // namespace power
76 } // namespace hardware
77 } // namespace google
78 } // namespace aidl
79