xref: /aosp_15_r20/frameworks/native/services/surfaceflinger/RefreshRateOverlay.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker  * Copyright 2019 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker  *
4*38e8c45fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker  *
8*38e8c45fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker  *
10*38e8c45fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker  * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker  */
16*38e8c45fSAndroid Build Coastguard Worker 
17*38e8c45fSAndroid Build Coastguard Worker #include <algorithm>
18*38e8c45fSAndroid Build Coastguard Worker 
19*38e8c45fSAndroid Build Coastguard Worker #include <common/FlagManager.h>
20*38e8c45fSAndroid Build Coastguard Worker #include "Client.h"
21*38e8c45fSAndroid Build Coastguard Worker #include "Layer.h"
22*38e8c45fSAndroid Build Coastguard Worker #include "RefreshRateOverlay.h"
23*38e8c45fSAndroid Build Coastguard Worker 
24*38e8c45fSAndroid Build Coastguard Worker #include <SkSurface.h>
25*38e8c45fSAndroid Build Coastguard Worker 
26*38e8c45fSAndroid Build Coastguard Worker #undef LOG_TAG
27*38e8c45fSAndroid Build Coastguard Worker #define LOG_TAG "RefreshRateOverlay"
28*38e8c45fSAndroid Build Coastguard Worker 
29*38e8c45fSAndroid Build Coastguard Worker namespace android {
30*38e8c45fSAndroid Build Coastguard Worker 
draw(int refreshRate,int renderFps,bool idle,SkColor color,ui::Transform::RotationFlags rotation,ftl::Flags<Features> features)31*38e8c45fSAndroid Build Coastguard Worker auto RefreshRateOverlay::draw(int refreshRate, int renderFps, bool idle, SkColor color,
32*38e8c45fSAndroid Build Coastguard Worker                               ui::Transform::RotationFlags rotation, ftl::Flags<Features> features)
33*38e8c45fSAndroid Build Coastguard Worker         -> Buffers {
34*38e8c45fSAndroid Build Coastguard Worker     const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
35*38e8c45fSAndroid Build Coastguard Worker     const bool isSetByHwc = features.test(Features::SetByHwc);
36*38e8c45fSAndroid Build Coastguard Worker 
37*38e8c45fSAndroid Build Coastguard Worker     Buffers buffers;
38*38e8c45fSAndroid Build Coastguard Worker     buffers.reserve(loopCount);
39*38e8c45fSAndroid Build Coastguard Worker 
40*38e8c45fSAndroid Build Coastguard Worker     for (size_t i = 0; i < loopCount; i++) {
41*38e8c45fSAndroid Build Coastguard Worker         // Pre-rotate the buffer before it reaches SurfaceFlinger.
42*38e8c45fSAndroid Build Coastguard Worker         SkMatrix canvasTransform = SkMatrix();
43*38e8c45fSAndroid Build Coastguard Worker         const auto [bufferWidth, bufferHeight] = [&]() -> std::pair<int, int> {
44*38e8c45fSAndroid Build Coastguard Worker             switch (rotation) {
45*38e8c45fSAndroid Build Coastguard Worker                 case ui::Transform::ROT_90:
46*38e8c45fSAndroid Build Coastguard Worker                     canvasTransform.setTranslate(kBufferHeight, 0);
47*38e8c45fSAndroid Build Coastguard Worker                     canvasTransform.preRotate(90.f);
48*38e8c45fSAndroid Build Coastguard Worker                     return {kBufferHeight, kBufferWidth};
49*38e8c45fSAndroid Build Coastguard Worker                 case ui::Transform::ROT_270:
50*38e8c45fSAndroid Build Coastguard Worker                     canvasTransform.setRotate(270.f, kBufferWidth / 2.f, kBufferWidth / 2.f);
51*38e8c45fSAndroid Build Coastguard Worker                     return {kBufferHeight, kBufferWidth};
52*38e8c45fSAndroid Build Coastguard Worker                 default:
53*38e8c45fSAndroid Build Coastguard Worker                     return {kBufferWidth, kBufferHeight};
54*38e8c45fSAndroid Build Coastguard Worker             }
55*38e8c45fSAndroid Build Coastguard Worker         }();
56*38e8c45fSAndroid Build Coastguard Worker 
57*38e8c45fSAndroid Build Coastguard Worker         const auto kUsageFlags =
58*38e8c45fSAndroid Build Coastguard Worker                 static_cast<uint64_t>(GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
59*38e8c45fSAndroid Build Coastguard Worker                                       GRALLOC_USAGE_HW_TEXTURE);
60*38e8c45fSAndroid Build Coastguard Worker         sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
61*38e8c45fSAndroid Build Coastguard Worker                                                            static_cast<uint32_t>(bufferHeight),
62*38e8c45fSAndroid Build Coastguard Worker                                                            HAL_PIXEL_FORMAT_RGBA_8888, 1u,
63*38e8c45fSAndroid Build Coastguard Worker                                                            kUsageFlags, "RefreshRateOverlayBuffer");
64*38e8c45fSAndroid Build Coastguard Worker 
65*38e8c45fSAndroid Build Coastguard Worker         const status_t bufferStatus = buffer->initCheck();
66*38e8c45fSAndroid Build Coastguard Worker         LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d",
67*38e8c45fSAndroid Build Coastguard Worker                             bufferStatus);
68*38e8c45fSAndroid Build Coastguard Worker 
69*38e8c45fSAndroid Build Coastguard Worker         sk_sp<SkSurface> surface = SkSurfaces::Raster(
70*38e8c45fSAndroid Build Coastguard Worker                 SkImageInfo::MakeN32Premul(bufferWidth, bufferHeight));
71*38e8c45fSAndroid Build Coastguard Worker         SkCanvas* canvas = surface->getCanvas();
72*38e8c45fSAndroid Build Coastguard Worker         canvas->setMatrix(canvasTransform);
73*38e8c45fSAndroid Build Coastguard Worker 
74*38e8c45fSAndroid Build Coastguard Worker         int left = 0;
75*38e8c45fSAndroid Build Coastguard Worker         if (idle && !isSetByHwc) {
76*38e8c45fSAndroid Build Coastguard Worker             drawDash(left, *canvas);
77*38e8c45fSAndroid Build Coastguard Worker         } else {
78*38e8c45fSAndroid Build Coastguard Worker             drawNumber(refreshRate, left, color, *canvas);
79*38e8c45fSAndroid Build Coastguard Worker         }
80*38e8c45fSAndroid Build Coastguard Worker         left += 3 * (kDigitWidth + kDigitSpace);
81*38e8c45fSAndroid Build Coastguard Worker         if (features.test(Features::Spinner)) {
82*38e8c45fSAndroid Build Coastguard Worker             switch (i) {
83*38e8c45fSAndroid Build Coastguard Worker                 case 0:
84*38e8c45fSAndroid Build Coastguard Worker                     SegmentDrawer::drawSegment(SegmentDrawer::Segment::Upper, left, color, *canvas);
85*38e8c45fSAndroid Build Coastguard Worker                     break;
86*38e8c45fSAndroid Build Coastguard Worker                 case 1:
87*38e8c45fSAndroid Build Coastguard Worker                     SegmentDrawer::drawSegment(SegmentDrawer::Segment::UpperRight, left, color,
88*38e8c45fSAndroid Build Coastguard Worker                                                *canvas);
89*38e8c45fSAndroid Build Coastguard Worker                     break;
90*38e8c45fSAndroid Build Coastguard Worker                 case 2:
91*38e8c45fSAndroid Build Coastguard Worker                     SegmentDrawer::drawSegment(SegmentDrawer::Segment::LowerRight, left, color,
92*38e8c45fSAndroid Build Coastguard Worker                                                *canvas);
93*38e8c45fSAndroid Build Coastguard Worker                     break;
94*38e8c45fSAndroid Build Coastguard Worker                 case 3:
95*38e8c45fSAndroid Build Coastguard Worker                     SegmentDrawer::drawSegment(SegmentDrawer::Segment::Bottom, left, color,
96*38e8c45fSAndroid Build Coastguard Worker                                                *canvas);
97*38e8c45fSAndroid Build Coastguard Worker                     break;
98*38e8c45fSAndroid Build Coastguard Worker                 case 4:
99*38e8c45fSAndroid Build Coastguard Worker                     SegmentDrawer::drawSegment(SegmentDrawer::Segment::LowerLeft, left, color,
100*38e8c45fSAndroid Build Coastguard Worker                                                *canvas);
101*38e8c45fSAndroid Build Coastguard Worker                     break;
102*38e8c45fSAndroid Build Coastguard Worker                 case 5:
103*38e8c45fSAndroid Build Coastguard Worker                     SegmentDrawer::drawSegment(SegmentDrawer::Segment::UpperLeft, left, color,
104*38e8c45fSAndroid Build Coastguard Worker                                                *canvas);
105*38e8c45fSAndroid Build Coastguard Worker                     break;
106*38e8c45fSAndroid Build Coastguard Worker             }
107*38e8c45fSAndroid Build Coastguard Worker         }
108*38e8c45fSAndroid Build Coastguard Worker 
109*38e8c45fSAndroid Build Coastguard Worker         left += kDigitWidth + kDigitSpace;
110*38e8c45fSAndroid Build Coastguard Worker 
111*38e8c45fSAndroid Build Coastguard Worker         if (features.test(Features::RenderRate)) {
112*38e8c45fSAndroid Build Coastguard Worker             if (idle) {
113*38e8c45fSAndroid Build Coastguard Worker                 drawDash(left, *canvas);
114*38e8c45fSAndroid Build Coastguard Worker             } else {
115*38e8c45fSAndroid Build Coastguard Worker                 drawNumber(renderFps, left, color, *canvas);
116*38e8c45fSAndroid Build Coastguard Worker             }
117*38e8c45fSAndroid Build Coastguard Worker         }
118*38e8c45fSAndroid Build Coastguard Worker         left += 3 * (kDigitWidth + kDigitSpace);
119*38e8c45fSAndroid Build Coastguard Worker 
120*38e8c45fSAndroid Build Coastguard Worker         void* pixels = nullptr;
121*38e8c45fSAndroid Build Coastguard Worker         buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
122*38e8c45fSAndroid Build Coastguard Worker 
123*38e8c45fSAndroid Build Coastguard Worker         const SkImageInfo& imageInfo = surface->imageInfo();
124*38e8c45fSAndroid Build Coastguard Worker         const size_t dstRowBytes =
125*38e8c45fSAndroid Build Coastguard Worker                 buffer->getStride() * static_cast<size_t>(imageInfo.bytesPerPixel());
126*38e8c45fSAndroid Build Coastguard Worker 
127*38e8c45fSAndroid Build Coastguard Worker         canvas->readPixels(imageInfo, pixels, dstRowBytes, 0, 0);
128*38e8c45fSAndroid Build Coastguard Worker         buffer->unlock();
129*38e8c45fSAndroid Build Coastguard Worker         buffers.push_back(std::move(buffer));
130*38e8c45fSAndroid Build Coastguard Worker     }
131*38e8c45fSAndroid Build Coastguard Worker     return buffers;
132*38e8c45fSAndroid Build Coastguard Worker }
133*38e8c45fSAndroid Build Coastguard Worker 
drawNumber(int number,int left,SkColor color,SkCanvas & canvas)134*38e8c45fSAndroid Build Coastguard Worker void RefreshRateOverlay::drawNumber(int number, int left, SkColor color, SkCanvas& canvas) {
135*38e8c45fSAndroid Build Coastguard Worker     if (number < 0 || number >= 1000) return;
136*38e8c45fSAndroid Build Coastguard Worker 
137*38e8c45fSAndroid Build Coastguard Worker     if (number >= 100) {
138*38e8c45fSAndroid Build Coastguard Worker         SegmentDrawer::drawDigit(number / 100, left, color, canvas);
139*38e8c45fSAndroid Build Coastguard Worker     }
140*38e8c45fSAndroid Build Coastguard Worker     left += kDigitWidth + kDigitSpace;
141*38e8c45fSAndroid Build Coastguard Worker 
142*38e8c45fSAndroid Build Coastguard Worker     if (number >= 10) {
143*38e8c45fSAndroid Build Coastguard Worker         SegmentDrawer::drawDigit((number / 10) % 10, left, color, canvas);
144*38e8c45fSAndroid Build Coastguard Worker     }
145*38e8c45fSAndroid Build Coastguard Worker     left += kDigitWidth + kDigitSpace;
146*38e8c45fSAndroid Build Coastguard Worker 
147*38e8c45fSAndroid Build Coastguard Worker     SegmentDrawer::drawDigit(number % 10, left, color, canvas);
148*38e8c45fSAndroid Build Coastguard Worker }
149*38e8c45fSAndroid Build Coastguard Worker 
drawDash(int left,SkCanvas & canvas)150*38e8c45fSAndroid Build Coastguard Worker void RefreshRateOverlay::drawDash(int left, SkCanvas& canvas) {
151*38e8c45fSAndroid Build Coastguard Worker     left += kDigitWidth + kDigitSpace;
152*38e8c45fSAndroid Build Coastguard Worker     SegmentDrawer::drawSegment(SegmentDrawer::Segment::Middle, left, SK_ColorRED, canvas);
153*38e8c45fSAndroid Build Coastguard Worker 
154*38e8c45fSAndroid Build Coastguard Worker     left += kDigitWidth + kDigitSpace;
155*38e8c45fSAndroid Build Coastguard Worker     SegmentDrawer::drawSegment(SegmentDrawer::Segment::Middle, left, SK_ColorRED, canvas);
156*38e8c45fSAndroid Build Coastguard Worker }
157*38e8c45fSAndroid Build Coastguard Worker 
create(FpsRange range,ftl::Flags<Features> features)158*38e8c45fSAndroid Build Coastguard Worker std::unique_ptr<RefreshRateOverlay> RefreshRateOverlay::create(FpsRange range,
159*38e8c45fSAndroid Build Coastguard Worker                                                                ftl::Flags<Features> features) {
160*38e8c45fSAndroid Build Coastguard Worker     std::unique_ptr<RefreshRateOverlay> overlay =
161*38e8c45fSAndroid Build Coastguard Worker             std::make_unique<RefreshRateOverlay>(ConstructorTag{}, range, features);
162*38e8c45fSAndroid Build Coastguard Worker     if (overlay->initCheck()) {
163*38e8c45fSAndroid Build Coastguard Worker         return overlay;
164*38e8c45fSAndroid Build Coastguard Worker     }
165*38e8c45fSAndroid Build Coastguard Worker 
166*38e8c45fSAndroid Build Coastguard Worker     ALOGE("%s: Failed to create RefreshRateOverlay", __func__);
167*38e8c45fSAndroid Build Coastguard Worker     return {};
168*38e8c45fSAndroid Build Coastguard Worker }
169*38e8c45fSAndroid Build Coastguard Worker 
RefreshRateOverlay(ConstructorTag,FpsRange fpsRange,ftl::Flags<Features> features)170*38e8c45fSAndroid Build Coastguard Worker RefreshRateOverlay::RefreshRateOverlay(ConstructorTag, FpsRange fpsRange,
171*38e8c45fSAndroid Build Coastguard Worker                                        ftl::Flags<Features> features)
172*38e8c45fSAndroid Build Coastguard Worker       : mFpsRange(fpsRange),
173*38e8c45fSAndroid Build Coastguard Worker         mFeatures(features),
174*38e8c45fSAndroid Build Coastguard Worker         mSurfaceControl(
175*38e8c45fSAndroid Build Coastguard Worker                 SurfaceControlHolder::createSurfaceControlHolder(String8("RefreshRateOverlay"))) {
176*38e8c45fSAndroid Build Coastguard Worker     if (!mSurfaceControl) {
177*38e8c45fSAndroid Build Coastguard Worker         ALOGE("%s: Failed to create buffer state layer", __func__);
178*38e8c45fSAndroid Build Coastguard Worker         return;
179*38e8c45fSAndroid Build Coastguard Worker     }
180*38e8c45fSAndroid Build Coastguard Worker 
181*38e8c45fSAndroid Build Coastguard Worker     createTransaction()
182*38e8c45fSAndroid Build Coastguard Worker             .setLayer(mSurfaceControl->get(), INT32_MAX - 2)
183*38e8c45fSAndroid Build Coastguard Worker             .setTrustedOverlay(mSurfaceControl->get(), true)
184*38e8c45fSAndroid Build Coastguard Worker             .apply();
185*38e8c45fSAndroid Build Coastguard Worker }
186*38e8c45fSAndroid Build Coastguard Worker 
initCheck() const187*38e8c45fSAndroid Build Coastguard Worker bool RefreshRateOverlay::initCheck() const {
188*38e8c45fSAndroid Build Coastguard Worker     return mSurfaceControl != nullptr;
189*38e8c45fSAndroid Build Coastguard Worker }
190*38e8c45fSAndroid Build Coastguard Worker 
getOrCreateBuffers(Fps refreshRate,Fps renderFps,bool idle)191*38e8c45fSAndroid Build Coastguard Worker auto RefreshRateOverlay::getOrCreateBuffers(Fps refreshRate, Fps renderFps, bool idle)
192*38e8c45fSAndroid Build Coastguard Worker         -> const Buffers& {
193*38e8c45fSAndroid Build Coastguard Worker     static const Buffers kNoBuffers;
194*38e8c45fSAndroid Build Coastguard Worker     if (!mSurfaceControl) return kNoBuffers;
195*38e8c45fSAndroid Build Coastguard Worker 
196*38e8c45fSAndroid Build Coastguard Worker     // avoid caching different render rates if RenderRate is anyway not visible
197*38e8c45fSAndroid Build Coastguard Worker     if (!mFeatures.test(Features::RenderRate)) {
198*38e8c45fSAndroid Build Coastguard Worker         renderFps = 0_Hz;
199*38e8c45fSAndroid Build Coastguard Worker     }
200*38e8c45fSAndroid Build Coastguard Worker 
201*38e8c45fSAndroid Build Coastguard Worker     const auto transformHint =
202*38e8c45fSAndroid Build Coastguard Worker             static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
203*38e8c45fSAndroid Build Coastguard Worker 
204*38e8c45fSAndroid Build Coastguard Worker     // Tell SurfaceFlinger about the pre-rotation on the buffer.
205*38e8c45fSAndroid Build Coastguard Worker     const auto transform = [&] {
206*38e8c45fSAndroid Build Coastguard Worker         switch (transformHint) {
207*38e8c45fSAndroid Build Coastguard Worker             case ui::Transform::ROT_90:
208*38e8c45fSAndroid Build Coastguard Worker                 return ui::Transform::ROT_270;
209*38e8c45fSAndroid Build Coastguard Worker             case ui::Transform::ROT_270:
210*38e8c45fSAndroid Build Coastguard Worker                 return ui::Transform::ROT_90;
211*38e8c45fSAndroid Build Coastguard Worker             default:
212*38e8c45fSAndroid Build Coastguard Worker                 return ui::Transform::ROT_0;
213*38e8c45fSAndroid Build Coastguard Worker         }
214*38e8c45fSAndroid Build Coastguard Worker     }();
215*38e8c45fSAndroid Build Coastguard Worker 
216*38e8c45fSAndroid Build Coastguard Worker     createTransaction().setTransform(mSurfaceControl->get(), transform).apply();
217*38e8c45fSAndroid Build Coastguard Worker 
218*38e8c45fSAndroid Build Coastguard Worker     BufferCache::const_iterator it = mBufferCache.find(
219*38e8c45fSAndroid Build Coastguard Worker             {refreshRate.getIntValue(), renderFps.getIntValue(), transformHint, idle});
220*38e8c45fSAndroid Build Coastguard Worker     if (it == mBufferCache.end()) {
221*38e8c45fSAndroid Build Coastguard Worker         const int maxFps = mFpsRange.max.getIntValue();
222*38e8c45fSAndroid Build Coastguard Worker 
223*38e8c45fSAndroid Build Coastguard Worker         // Clamp to supported refresh rate range: the current refresh rate may be outside of this
224*38e8c45fSAndroid Build Coastguard Worker         // range if the display has changed its set of supported refresh rates.
225*38e8c45fSAndroid Build Coastguard Worker         const int refreshIntFps = std::clamp(refreshRate.getIntValue(), 0, maxFps);
226*38e8c45fSAndroid Build Coastguard Worker         const int renderIntFps = renderFps.getIntValue();
227*38e8c45fSAndroid Build Coastguard Worker         const float fpsScale = static_cast<float>(refreshIntFps) / maxFps;
228*38e8c45fSAndroid Build Coastguard Worker 
229*38e8c45fSAndroid Build Coastguard Worker         constexpr SkColor kMinFpsColor = SK_ColorRED;
230*38e8c45fSAndroid Build Coastguard Worker         constexpr SkColor kMaxFpsColor = SK_ColorGREEN;
231*38e8c45fSAndroid Build Coastguard Worker         constexpr float kAlpha = 0.8f;
232*38e8c45fSAndroid Build Coastguard Worker 
233*38e8c45fSAndroid Build Coastguard Worker         SkColor4f colorBase = SkColor4f::FromColor(kMaxFpsColor) * fpsScale;
234*38e8c45fSAndroid Build Coastguard Worker         const SkColor4f minFpsColor = SkColor4f::FromColor(kMinFpsColor) * (1 - fpsScale);
235*38e8c45fSAndroid Build Coastguard Worker 
236*38e8c45fSAndroid Build Coastguard Worker         colorBase.fR = colorBase.fR + minFpsColor.fR;
237*38e8c45fSAndroid Build Coastguard Worker         colorBase.fG = colorBase.fG + minFpsColor.fG;
238*38e8c45fSAndroid Build Coastguard Worker         colorBase.fB = colorBase.fB + minFpsColor.fB;
239*38e8c45fSAndroid Build Coastguard Worker         colorBase.fA = kAlpha;
240*38e8c45fSAndroid Build Coastguard Worker 
241*38e8c45fSAndroid Build Coastguard Worker         const SkColor color = colorBase.toSkColor();
242*38e8c45fSAndroid Build Coastguard Worker 
243*38e8c45fSAndroid Build Coastguard Worker         auto buffers = draw(refreshIntFps, renderIntFps, idle, color, transformHint, mFeatures);
244*38e8c45fSAndroid Build Coastguard Worker         it = mBufferCache
245*38e8c45fSAndroid Build Coastguard Worker                      .try_emplace({refreshIntFps, renderIntFps, transformHint, idle},
246*38e8c45fSAndroid Build Coastguard Worker                      std::move(buffers)).first;
247*38e8c45fSAndroid Build Coastguard Worker     }
248*38e8c45fSAndroid Build Coastguard Worker 
249*38e8c45fSAndroid Build Coastguard Worker     return it->second;
250*38e8c45fSAndroid Build Coastguard Worker }
251*38e8c45fSAndroid Build Coastguard Worker 
setViewport(ui::Size viewport)252*38e8c45fSAndroid Build Coastguard Worker void RefreshRateOverlay::setViewport(ui::Size viewport) {
253*38e8c45fSAndroid Build Coastguard Worker     constexpr int32_t kMaxWidth = 1000;
254*38e8c45fSAndroid Build Coastguard Worker     const auto width = std::min({kMaxWidth, viewport.width, viewport.height});
255*38e8c45fSAndroid Build Coastguard Worker     const auto height = 2 * width;
256*38e8c45fSAndroid Build Coastguard Worker     Rect frame((5 * width) >> 4, height >> 5);
257*38e8c45fSAndroid Build Coastguard Worker 
258*38e8c45fSAndroid Build Coastguard Worker     if (!mFeatures.test(Features::ShowInMiddle)) {
259*38e8c45fSAndroid Build Coastguard Worker         frame.offsetBy(width >> 5, height >> 4);
260*38e8c45fSAndroid Build Coastguard Worker     } else {
261*38e8c45fSAndroid Build Coastguard Worker         frame.offsetBy(width >> 1, height >> 4);
262*38e8c45fSAndroid Build Coastguard Worker     }
263*38e8c45fSAndroid Build Coastguard Worker 
264*38e8c45fSAndroid Build Coastguard Worker     createTransaction()
265*38e8c45fSAndroid Build Coastguard Worker             .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
266*38e8c45fSAndroid Build Coastguard Worker                        0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
267*38e8c45fSAndroid Build Coastguard Worker             .setPosition(mSurfaceControl->get(), frame.left, frame.top)
268*38e8c45fSAndroid Build Coastguard Worker             .apply();
269*38e8c45fSAndroid Build Coastguard Worker }
270*38e8c45fSAndroid Build Coastguard Worker 
setLayerStack(ui::LayerStack stack)271*38e8c45fSAndroid Build Coastguard Worker void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
272*38e8c45fSAndroid Build Coastguard Worker     createTransaction().setLayerStack(mSurfaceControl->get(), stack).apply();
273*38e8c45fSAndroid Build Coastguard Worker }
274*38e8c45fSAndroid Build Coastguard Worker 
changeRefreshRate(Fps refreshRate,Fps renderFps)275*38e8c45fSAndroid Build Coastguard Worker void RefreshRateOverlay::changeRefreshRate(Fps refreshRate, Fps renderFps) {
276*38e8c45fSAndroid Build Coastguard Worker     mRefreshRate = refreshRate;
277*38e8c45fSAndroid Build Coastguard Worker     mRenderFps = renderFps;
278*38e8c45fSAndroid Build Coastguard Worker     const auto buffer = getOrCreateBuffers(refreshRate, renderFps, mIsVrrIdle)[mFrame];
279*38e8c45fSAndroid Build Coastguard Worker     createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
280*38e8c45fSAndroid Build Coastguard Worker }
281*38e8c45fSAndroid Build Coastguard Worker 
onVrrIdle(bool idle)282*38e8c45fSAndroid Build Coastguard Worker void RefreshRateOverlay::onVrrIdle(bool idle) {
283*38e8c45fSAndroid Build Coastguard Worker     mIsVrrIdle = idle;
284*38e8c45fSAndroid Build Coastguard Worker     if (!mRefreshRate || !mRenderFps) return;
285*38e8c45fSAndroid Build Coastguard Worker 
286*38e8c45fSAndroid Build Coastguard Worker     const auto buffer = getOrCreateBuffers(*mRefreshRate, *mRenderFps, mIsVrrIdle)[mFrame];
287*38e8c45fSAndroid Build Coastguard Worker     createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
288*38e8c45fSAndroid Build Coastguard Worker }
289*38e8c45fSAndroid Build Coastguard Worker 
changeRenderRate(Fps renderFps)290*38e8c45fSAndroid Build Coastguard Worker void RefreshRateOverlay::changeRenderRate(Fps renderFps) {
291*38e8c45fSAndroid Build Coastguard Worker     if (mFeatures.test(Features::RenderRate) && mRefreshRate &&
292*38e8c45fSAndroid Build Coastguard Worker         FlagManager::getInstance().misc1()) {
293*38e8c45fSAndroid Build Coastguard Worker         mRenderFps = renderFps;
294*38e8c45fSAndroid Build Coastguard Worker         const auto buffer = getOrCreateBuffers(*mRefreshRate, renderFps, mIsVrrIdle)[mFrame];
295*38e8c45fSAndroid Build Coastguard Worker         createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
296*38e8c45fSAndroid Build Coastguard Worker     }
297*38e8c45fSAndroid Build Coastguard Worker }
298*38e8c45fSAndroid Build Coastguard Worker 
animate()299*38e8c45fSAndroid Build Coastguard Worker void RefreshRateOverlay::animate() {
300*38e8c45fSAndroid Build Coastguard Worker     if (!mFeatures.test(Features::Spinner) || !mRefreshRate) return;
301*38e8c45fSAndroid Build Coastguard Worker 
302*38e8c45fSAndroid Build Coastguard Worker     const auto& buffers = getOrCreateBuffers(*mRefreshRate, *mRenderFps, mIsVrrIdle);
303*38e8c45fSAndroid Build Coastguard Worker     mFrame = (mFrame + 1) % buffers.size();
304*38e8c45fSAndroid Build Coastguard Worker     const auto buffer = buffers[mFrame];
305*38e8c45fSAndroid Build Coastguard Worker     createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
306*38e8c45fSAndroid Build Coastguard Worker }
307*38e8c45fSAndroid Build Coastguard Worker 
createTransaction() const308*38e8c45fSAndroid Build Coastguard Worker SurfaceComposerClient::Transaction RefreshRateOverlay::createTransaction() const {
309*38e8c45fSAndroid Build Coastguard Worker     constexpr float kFrameRate = 0.f;
310*38e8c45fSAndroid Build Coastguard Worker     constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE;
311*38e8c45fSAndroid Build Coastguard Worker     constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS;
312*38e8c45fSAndroid Build Coastguard Worker 
313*38e8c45fSAndroid Build Coastguard Worker     const sp<SurfaceControl>& surface = mSurfaceControl->get();
314*38e8c45fSAndroid Build Coastguard Worker 
315*38e8c45fSAndroid Build Coastguard Worker     SurfaceComposerClient::Transaction transaction;
316*38e8c45fSAndroid Build Coastguard Worker     if (isSetByHwc()) {
317*38e8c45fSAndroid Build Coastguard Worker         transaction.setFlags(surface, layer_state_t::eLayerIsRefreshRateIndicator,
318*38e8c45fSAndroid Build Coastguard Worker                              layer_state_t::eLayerIsRefreshRateIndicator);
319*38e8c45fSAndroid Build Coastguard Worker         // Disable overlay layer caching when refresh rate is updated by the HWC.
320*38e8c45fSAndroid Build Coastguard Worker         transaction.setCachingHint(surface, gui::CachingHint::Disabled);
321*38e8c45fSAndroid Build Coastguard Worker     }
322*38e8c45fSAndroid Build Coastguard Worker     transaction.setFrameRate(surface, kFrameRate, kCompatibility, kSeamlessness);
323*38e8c45fSAndroid Build Coastguard Worker     return transaction;
324*38e8c45fSAndroid Build Coastguard Worker }
325*38e8c45fSAndroid Build Coastguard Worker 
326*38e8c45fSAndroid Build Coastguard Worker } // namespace android
327