1 /*
2  * Copyright 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 // TODO(b/129481165): remove the #pragma below and fix conversion issues
18 #pragma clang diagnostic push
19 #pragma clang diagnostic ignored "-Wconversion"
20 #pragma clang diagnostic ignored "-Wextra"
21 
22 #include <algorithm>
23 
24 #include "RefreshRateOverlay.h"
25 #include "Client.h"
26 #include "Layer.h"
27 
28 #include <gui/IProducerListener.h>
29 
30 #undef LOG_TAG
31 #define LOG_TAG "RefreshRateOverlay"
32 
33 namespace android {
34 
drawRect(const Rect & r,const half4 & color,const sp<GraphicBuffer> & buffer,uint8_t * pixels)35 void RefreshRateOverlay::SevenSegmentDrawer::drawRect(const Rect& r, const half4& color,
36                                                       const sp<GraphicBuffer>& buffer,
37                                                       uint8_t* pixels) {
38     for (int32_t j = r.top; j < r.bottom; j++) {
39         if (j >= buffer->getHeight()) {
40             break;
41         }
42 
43         for (int32_t i = r.left; i < r.right; i++) {
44             if (i >= buffer->getWidth()) {
45                 break;
46             }
47 
48             uint8_t* iter = pixels + 4 * (i + (buffer->getStride() * j));
49             iter[0] = uint8_t(color.r * 255);
50             iter[1] = uint8_t(color.g * 255);
51             iter[2] = uint8_t(color.b * 255);
52             iter[3] = uint8_t(color.a * 255);
53         }
54     }
55 }
56 
drawSegment(Segment segment,int left,const half4 & color,const sp<GraphicBuffer> & buffer,uint8_t * pixels)57 void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left,
58                                                          const half4& color,
59                                                          const sp<GraphicBuffer>& buffer,
60                                                          uint8_t* pixels) {
61     const Rect rect = [&]() {
62         switch (segment) {
63             case Segment::Upper:
64                 return Rect(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE);
65             case Segment::UpperLeft:
66                 return Rect(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2);
67             case Segment::UpperRight:
68                 return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH,
69                             DIGIT_HEIGHT / 2);
70             case Segment::Middle:
71                 return Rect(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2, left + DIGIT_WIDTH,
72                             DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2);
73             case Segment::LowerLeft:
74                 return Rect(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT);
75             case Segment::LowerRight:
76                 return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2, left + DIGIT_WIDTH,
77                             DIGIT_HEIGHT);
78             case Segment::Buttom:
79                 return Rect(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH, DIGIT_HEIGHT);
80         }
81     }();
82 
83     drawRect(rect, color, buffer, pixels);
84 }
85 
drawDigit(int digit,int left,const half4 & color,const sp<GraphicBuffer> & buffer,uint8_t * pixels)86 void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, const half4& color,
87                                                        const sp<GraphicBuffer>& buffer,
88                                                        uint8_t* pixels) {
89     if (digit < 0 || digit > 9) return;
90 
91     if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 ||
92         digit == 8 || digit == 9)
93         drawSegment(Segment::Upper, left, color, buffer, pixels);
94     if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9)
95         drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
96     if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 ||
97         digit == 8 || digit == 9)
98         drawSegment(Segment::UpperRight, left, color, buffer, pixels);
99     if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 ||
100         digit == 9)
101         drawSegment(Segment::Middle, left, color, buffer, pixels);
102     if (digit == 0 || digit == 2 || digit == 6 || digit == 8)
103         drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
104     if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 ||
105         digit == 7 || digit == 8 || digit == 9)
106         drawSegment(Segment::LowerRight, left, color, buffer, pixels);
107     if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 ||
108         digit == 9)
109         drawSegment(Segment::Buttom, left, color, buffer, pixels);
110 }
111 
drawNumber(int number,const half4 & color,bool showSpinner)112 std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(
113         int number, const half4& color, bool showSpinner) {
114     if (number < 0 || number > 1000) return {};
115 
116     const auto hundreds = number / 100;
117     const auto tens = (number / 10) % 10;
118     const auto ones = number % 10;
119 
120     std::vector<sp<GraphicBuffer>> buffers;
121     const auto loopCount = showSpinner ? 6 : 1;
122     for (int i = 0; i < loopCount; i++) {
123         sp<GraphicBuffer> buffer =
124                 new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
125                                   GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
126                                           GRALLOC_USAGE_HW_TEXTURE,
127                                   "RefreshRateOverlayBuffer");
128         const status_t bufferStatus = buffer->initCheck();
129         LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d",
130                             bufferStatus);
131         uint8_t* pixels;
132         buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
133         // Clear buffer content
134         drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels);
135         int left = 0;
136         if (hundreds != 0) {
137             drawDigit(hundreds, left, color, buffer, pixels);
138         }
139         left += DIGIT_WIDTH + DIGIT_SPACE;
140 
141         if (tens != 0) {
142             drawDigit(tens, left, color, buffer, pixels);
143         }
144         left += DIGIT_WIDTH + DIGIT_SPACE;
145 
146         drawDigit(ones, left, color, buffer, pixels);
147         left += DIGIT_WIDTH + DIGIT_SPACE;
148 
149         if (showSpinner) {
150             switch (i) {
151                 case 0:
152                     drawSegment(Segment::Upper, left, color, buffer, pixels);
153                     break;
154                 case 1:
155                     drawSegment(Segment::UpperRight, left, color, buffer, pixels);
156                     break;
157                 case 2:
158                     drawSegment(Segment::LowerRight, left, color, buffer, pixels);
159                     break;
160                 case 3:
161                     drawSegment(Segment::Buttom, left, color, buffer, pixels);
162                     break;
163                 case 4:
164                     drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
165                     break;
166                 case 5:
167                     drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
168                     break;
169             }
170         }
171 
172         buffer->unlock();
173         buffers.emplace_back(buffer);
174     }
175     return buffers;
176 }
177 
RefreshRateOverlay(SurfaceFlinger & flinger,uint32_t lowFps,uint32_t highFps,bool showSpinner)178 RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger, uint32_t lowFps, uint32_t highFps,
179                                        bool showSpinner)
180       : mFlinger(flinger),
181         mClient(new Client(&mFlinger)),
182         mShowSpinner(showSpinner),
183         mLowFps(lowFps),
184         mHighFps(highFps) {
185     createLayer();
186 }
187 
createLayer()188 bool RefreshRateOverlay::createLayer() {
189     int32_t layerId;
190     const status_t ret =
191             mFlinger.createLayer(String8("RefreshRateOverlay"), mClient,
192                                  SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(),
193                                  PIXEL_FORMAT_RGBA_8888,
194                                  ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(),
195                                  &mIBinder, &mGbp, nullptr, &layerId);
196     if (ret) {
197         ALOGE("failed to create buffer state layer");
198         return false;
199     }
200 
201     mLayer = mClient->getLayerUser(mIBinder);
202     mLayer->setFrameRate(Layer::FrameRate(Fps(0.0f), Layer::FrameRateCompatibility::NoVote));
203     mLayer->setIsAtRoot(true);
204 
205     // setting Layer's Z requires resorting layersSortedByZ
206     ssize_t idx = mFlinger.mDrawingState.layersSortedByZ.indexOf(mLayer);
207     if (mLayer->setLayer(INT32_MAX - 2) && idx >= 0) {
208         mFlinger.mDrawingState.layersSortedByZ.removeAt(idx);
209         mFlinger.mDrawingState.layersSortedByZ.add(mLayer);
210     }
211 
212     return true;
213 }
214 
215 const std::vector<std::shared_ptr<renderengine::ExternalTexture>>&
getOrCreateBuffers(uint32_t fps)216 RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) {
217     if (mBufferCache.find(fps) == mBufferCache.end()) {
218         // Ensure the range is > 0, so we don't divide by 0.
219         const auto rangeLength = std::max(1u, mHighFps - mLowFps);
220         // Clip values outside the range [mLowFps, mHighFps]. The current fps may be outside
221         // of this range if the display has changed its set of supported refresh rates.
222         fps = std::max(fps, mLowFps);
223         fps = std::min(fps, mHighFps);
224         const auto fpsScale = static_cast<float>(fps - mLowFps) / rangeLength;
225         half4 color;
226         color.r = HIGH_FPS_COLOR.r * fpsScale + LOW_FPS_COLOR.r * (1 - fpsScale);
227         color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale);
228         color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale);
229         color.a = ALPHA;
230         auto buffers = SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner);
231         std::vector<std::shared_ptr<renderengine::ExternalTexture>> textures;
232         std::transform(buffers.begin(), buffers.end(), std::back_inserter(textures),
233                        [&](const auto& buffer) -> std::shared_ptr<renderengine::ExternalTexture> {
234                            return std::make_shared<
235                                    renderengine::ExternalTexture>(buffer,
236                                                                   mFlinger.getRenderEngine(),
237                                                                   renderengine::ExternalTexture::
238                                                                           Usage::READABLE);
239                        });
240         mBufferCache.emplace(fps, textures);
241     }
242 
243     return mBufferCache[fps];
244 }
245 
setViewport(ui::Size viewport)246 void RefreshRateOverlay::setViewport(ui::Size viewport) {
247     constexpr int32_t kMaxWidth = 1000;
248     const auto width = std::min(kMaxWidth, std::min(viewport.width, viewport.height));
249     const auto height = 2 * width;
250     Rect frame((3 * width) >> 4, height >> 5);
251     frame.offsetBy(width >> 5, height >> 4);
252 
253     layer_state_t::matrix22_t matrix;
254     matrix.dsdx = frame.getWidth() / static_cast<float>(SevenSegmentDrawer::getWidth());
255     matrix.dtdx = 0;
256     matrix.dtdy = 0;
257     matrix.dsdy = frame.getHeight() / static_cast<float>(SevenSegmentDrawer::getHeight());
258     mLayer->setMatrix(matrix, true);
259     mLayer->setPosition(frame.left, frame.top);
260     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
261 }
262 
setLayerStack(uint32_t stack)263 void RefreshRateOverlay::setLayerStack(uint32_t stack) {
264     mLayer->setLayerStack(stack);
265     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
266 }
267 
changeRefreshRate(const Fps & fps)268 void RefreshRateOverlay::changeRefreshRate(const Fps& fps) {
269     mCurrentFps = fps.getIntValue();
270     auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
271     mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
272                       mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
273                       std::nullopt /* dequeueTime */, FrameTimelineInfo{},
274                       nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */);
275 
276     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
277 }
278 
onInvalidate()279 void RefreshRateOverlay::onInvalidate() {
280     if (!mCurrentFps.has_value()) return;
281 
282     const auto& buffers = getOrCreateBuffers(*mCurrentFps);
283     mFrame = (mFrame + 1) % buffers.size();
284     auto buffer = buffers[mFrame];
285     mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
286                       mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
287                       std::nullopt /* dequeueTime */, FrameTimelineInfo{},
288                       nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */);
289 
290     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
291 }
292 
293 } // namespace android
294 
295 // TODO(b/129481165): remove the #pragma below and fix conversion issues
296 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
297