1 /*
2  * Copyright 2021 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 <RenderEngineBench.h>
18 #include <android-base/file.h>
19 #include <benchmark/benchmark.h>
20 #include <gui/SurfaceComposerClient.h>
21 #include <log/log.h>
22 #include <renderengine/ExternalTexture.h>
23 #include <renderengine/LayerSettings.h>
24 #include <renderengine/RenderEngine.h>
25 #include <renderengine/impl/ExternalTexture.h>
26 
27 #include <mutex>
28 
29 using namespace android;
30 using namespace android::renderengine;
31 
32 ///////////////////////////////////////////////////////////////////////////////
33 //  Helpers for calling drawLayers
34 ///////////////////////////////////////////////////////////////////////////////
35 
getDisplaySize()36 std::pair<uint32_t, uint32_t> getDisplaySize() {
37     // These will be retrieved from a ui::Size, which stores int32_t, but they will be passed
38     // to GraphicBuffer, which wants uint32_t.
39     static uint32_t width, height;
40     std::once_flag once;
41     std::call_once(once, []() {
42         auto surfaceComposerClient = SurfaceComposerClient::getDefault();
43         auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
44         LOG_ALWAYS_FATAL_IF(ids.empty(), "Failed to get any display!");
45         ui::Size resolution = ui::kEmptySize;
46         // find the largest display resolution
47         for (auto id : ids) {
48             auto displayToken = surfaceComposerClient->getPhysicalDisplayToken(id);
49             ui::DisplayMode displayMode;
50             if (surfaceComposerClient->getActiveDisplayMode(displayToken, &displayMode) < 0) {
51                 LOG_ALWAYS_FATAL("Failed to get active display mode!");
52             }
53             auto tw = displayMode.resolution.width;
54             auto th = displayMode.resolution.height;
55             LOG_ALWAYS_FATAL_IF(tw <= 0 || th <= 0, "Invalid display size!");
56             if (resolution.width * resolution.height <
57                 displayMode.resolution.width * displayMode.resolution.height) {
58                 resolution = displayMode.resolution;
59             }
60         }
61         width = static_cast<uint32_t>(resolution.width);
62         height = static_cast<uint32_t>(resolution.height);
63     });
64     return std::pair<uint32_t, uint32_t>(width, height);
65 }
66 
createRenderEngine(RenderEngine::Threaded threaded,RenderEngine::GraphicsApi graphicsApi)67 static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::Threaded threaded,
68                                                         RenderEngine::GraphicsApi graphicsApi) {
69     auto args = RenderEngineCreationArgs::Builder()
70                         .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
71                         .setImageCacheSize(1)
72                         .setEnableProtectedContext(true)
73                         .setPrecacheToneMapperShaderOnly(false)
74                         .setBlurAlgorithm(renderengine::RenderEngine::BlurAlgorithm::KAWASE)
75                         .setContextPriority(RenderEngine::ContextPriority::REALTIME)
76                         .setThreaded(threaded)
77                         .setGraphicsApi(graphicsApi)
78                         .build();
79     return RenderEngine::create(args);
80 }
81 
allocateBuffer(RenderEngine & re,uint32_t width,uint32_t height,uint64_t extraUsageFlags=0,std::string name="output")82 static std::shared_ptr<ExternalTexture> allocateBuffer(RenderEngine& re, uint32_t width,
83                                                        uint32_t height,
84                                                        uint64_t extraUsageFlags = 0,
85                                                        std::string name = "output") {
86     return std::make_shared<
87             impl::ExternalTexture>(sp<GraphicBuffer>::make(width, height,
88                                                            HAL_PIXEL_FORMAT_RGBA_8888, 1u,
89                                                            GRALLOC_USAGE_HW_RENDER |
90                                                                    GRALLOC_USAGE_HW_TEXTURE |
91                                                                    extraUsageFlags,
92                                                            std::move(name)),
93                                    re,
94                                    impl::ExternalTexture::Usage::READABLE |
95                                            impl::ExternalTexture::Usage::WRITEABLE);
96 }
97 
copyBuffer(RenderEngine & re,std::shared_ptr<ExternalTexture> original,uint64_t extraUsageFlags,std::string name)98 static std::shared_ptr<ExternalTexture> copyBuffer(RenderEngine& re,
99                                                    std::shared_ptr<ExternalTexture> original,
100                                                    uint64_t extraUsageFlags, std::string name) {
101     const uint32_t width = original->getBuffer()->getWidth();
102     const uint32_t height = original->getBuffer()->getHeight();
103     auto texture = allocateBuffer(re, width, height, extraUsageFlags, name);
104 
105     const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height));
106     DisplaySettings display{
107             .physicalDisplay = displayRect,
108             .clip = displayRect,
109             .maxLuminance = 500,
110     };
111 
112     const FloatRect layerRect(0, 0, width, height);
113     LayerSettings layer{
114             .geometry =
115                     Geometry{
116                             .boundaries = layerRect,
117                     },
118             .source =
119                     PixelSource{
120                             .buffer =
121                                     Buffer{
122                                             .buffer = original,
123                                     },
124                     },
125             .alpha = half(1.0f),
126     };
127     auto layers = std::vector<LayerSettings>{layer};
128 
129     sp<Fence> waitFence = re.drawLayers(display, layers, texture, base::unique_fd()).get().value();
130     waitFence->waitForever(LOG_TAG);
131     return texture;
132 }
133 
134 /**
135  * Helper for timing calls to drawLayers.
136  *
137  * Caller needs to create RenderEngine and the LayerSettings, and this takes
138  * care of setting up the display, starting and stopping the timer, calling
139  * drawLayers, and saving (if --save is used).
140  *
141  * This times both the CPU and GPU work initiated by drawLayers. All work done
142  * outside of the for loop is excluded from the timing measurements.
143  */
benchDrawLayers(RenderEngine & re,const std::vector<LayerSettings> & layers,benchmark::State & benchState,const char * saveFileName)144 static void benchDrawLayers(RenderEngine& re, const std::vector<LayerSettings>& layers,
145                             benchmark::State& benchState, const char* saveFileName) {
146     auto [width, height] = getDisplaySize();
147     auto outputBuffer = allocateBuffer(re, width, height);
148 
149     const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height));
150     DisplaySettings display{
151             .physicalDisplay = displayRect,
152             .clip = displayRect,
153             .maxLuminance = 500,
154     };
155 
156     // This loop starts and stops the timer.
157     for (auto _ : benchState) {
158         sp<Fence> waitFence =
159                 re.drawLayers(display, layers, outputBuffer, base::unique_fd()).get().value();
160         waitFence->waitForever(LOG_TAG);
161     }
162 
163     if (renderenginebench::save() && saveFileName) {
164         // Copy to a CPU-accessible buffer so we can encode it.
165         outputBuffer = copyBuffer(re, outputBuffer, GRALLOC_USAGE_SW_READ_OFTEN, "to_encode");
166 
167         std::string outFile = base::GetExecutableDirectory();
168         outFile.append("/");
169         outFile.append(saveFileName);
170         outFile.append(".jpg");
171         renderenginebench::encodeToJpeg(outFile.c_str(), outputBuffer->getBuffer());
172     }
173 }
174 
175 ///////////////////////////////////////////////////////////////////////////////
176 //  Benchmarks
177 ///////////////////////////////////////////////////////////////////////////////
178 
179 template <class... Args>
BM_blur(benchmark::State & benchState,Args &&...args)180 void BM_blur(benchmark::State& benchState, Args&&... args) {
181     auto args_tuple = std::make_tuple(std::move(args)...);
182     auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)),
183                                  static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple)));
184 
185     // Initially use cpu access so we can decode into it with AImageDecoder.
186     auto [width, height] = getDisplaySize();
187     auto srcBuffer =
188             allocateBuffer(*re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN, "decoded_source");
189     {
190         std::string srcImage = base::GetExecutableDirectory();
191         srcImage.append("/resources/homescreen.png");
192         renderenginebench::decode(srcImage.c_str(), srcBuffer->getBuffer());
193 
194         // Now copy into GPU-only buffer for more realistic timing.
195         srcBuffer = copyBuffer(*re, srcBuffer, 0, "source");
196     }
197 
198     const FloatRect layerRect(0, 0, width, height);
199     LayerSettings layer{
200             .geometry =
201                     Geometry{
202                             .boundaries = layerRect,
203                     },
204             .source =
205                     PixelSource{
206                             .buffer =
207                                     Buffer{
208                                             .buffer = srcBuffer,
209                                     },
210                     },
211             .alpha = half(1.0f),
212     };
213     LayerSettings blurLayer{
214             .geometry =
215                     Geometry{
216                             .boundaries = layerRect,
217                     },
218             .alpha = half(1.0f),
219             .skipContentDraw = true,
220             .backgroundBlurRadius = 60,
221     };
222 
223     auto layers = std::vector<LayerSettings>{layer, blurLayer};
224     benchDrawLayers(*re, layers, benchState, "blurred");
225 }
226 
227 BENCHMARK_CAPTURE(BM_blur, SkiaGLThreaded, RenderEngine::Threaded::YES,
228                   RenderEngine::GraphicsApi::GL);
229