xref: /aosp_15_r20/frameworks/native/libs/renderengine/benchmark/RenderEngineBench.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
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 <com_android_graphics_libgui_flags.h>
21 #include <gui/SurfaceComposerClient.h>
22 #include <log/log.h>
23 #include <renderengine/ExternalTexture.h>
24 #include <renderengine/LayerSettings.h>
25 #include <renderengine/RenderEngine.h>
26 #include <renderengine/impl/ExternalTexture.h>
27 
28 #include <mutex>
29 
30 using namespace android;
31 using namespace android::renderengine;
32 
33 // To run tests:
34 /**
35  * mmm frameworks/native/libs/renderengine/benchmark;\
36  * adb push $OUT/data/benchmarktest/librenderengine_bench/librenderengine_bench
37  *      /data/benchmarktest/librenderengine_bench/librenderengine_bench;\
38  * adb shell /data/benchmarktest/librenderengine_bench/librenderengine_bench
39  *
40  * (64-bit devices: out directory contains benchmarktest64 instead of benchmarktest)
41  */
42 
43 ///////////////////////////////////////////////////////////////////////////////
44 //  Helpers for calling drawLayers
45 ///////////////////////////////////////////////////////////////////////////////
46 
getDisplaySize()47 std::pair<uint32_t, uint32_t> getDisplaySize() {
48     // These will be retrieved from a ui::Size, which stores int32_t, but they will be passed
49     // to GraphicBuffer, which wants uint32_t.
50     static uint32_t width, height;
51     std::once_flag once;
52     std::call_once(once, []() {
53         auto surfaceComposerClient = SurfaceComposerClient::getDefault();
54         auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
55         LOG_ALWAYS_FATAL_IF(ids.empty(), "Failed to get any display!");
56         ui::Size resolution = ui::kEmptySize;
57         // find the largest display resolution
58         for (auto id : ids) {
59             auto displayToken = surfaceComposerClient->getPhysicalDisplayToken(id);
60             ui::DisplayMode displayMode;
61             if (surfaceComposerClient->getActiveDisplayMode(displayToken, &displayMode) < 0) {
62                 LOG_ALWAYS_FATAL("Failed to get active display mode!");
63             }
64             auto tw = displayMode.resolution.width;
65             auto th = displayMode.resolution.height;
66             LOG_ALWAYS_FATAL_IF(tw <= 0 || th <= 0, "Invalid display size!");
67             if (resolution.width * resolution.height <
68                 displayMode.resolution.width * displayMode.resolution.height) {
69                 resolution = displayMode.resolution;
70             }
71         }
72         width = static_cast<uint32_t>(resolution.width);
73         height = static_cast<uint32_t>(resolution.height);
74     });
75     return std::pair<uint32_t, uint32_t>(width, height);
76 }
77 
createRenderEngine(RenderEngine::Threaded threaded,RenderEngine::GraphicsApi graphicsApi,RenderEngine::BlurAlgorithm blurAlgorithm=RenderEngine::BlurAlgorithm::KAWASE)78 static std::unique_ptr<RenderEngine> createRenderEngine(
79         RenderEngine::Threaded threaded, RenderEngine::GraphicsApi graphicsApi,
80         RenderEngine::BlurAlgorithm blurAlgorithm = RenderEngine::BlurAlgorithm::KAWASE) {
81     auto args = RenderEngineCreationArgs::Builder()
82                         .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
83                         .setImageCacheSize(1)
84                         .setEnableProtectedContext(true)
85                         .setPrecacheToneMapperShaderOnly(false)
86                         .setBlurAlgorithm(blurAlgorithm)
87                         .setContextPriority(RenderEngine::ContextPriority::REALTIME)
88                         .setThreaded(threaded)
89                         .setGraphicsApi(graphicsApi)
90                         .build();
91     return RenderEngine::create(args);
92 }
93 
allocateBuffer(RenderEngine & re,uint32_t width,uint32_t height,uint64_t extraUsageFlags=0,std::string name="output")94 static std::shared_ptr<ExternalTexture> allocateBuffer(RenderEngine& re, uint32_t width,
95                                                        uint32_t height,
96                                                        uint64_t extraUsageFlags = 0,
97                                                        std::string name = "output") {
98     return std::make_shared<
99             impl::ExternalTexture>(sp<GraphicBuffer>::make(width, height,
100                                                            HAL_PIXEL_FORMAT_RGBA_8888, 1u,
101                                                            GRALLOC_USAGE_HW_RENDER |
102                                                                    GRALLOC_USAGE_HW_TEXTURE |
103                                                                    extraUsageFlags,
104                                                            std::move(name)),
105                                    re,
106                                    impl::ExternalTexture::Usage::READABLE |
107                                            impl::ExternalTexture::Usage::WRITEABLE);
108 }
109 
copyBuffer(RenderEngine & re,std::shared_ptr<ExternalTexture> original,uint64_t extraUsageFlags,std::string name)110 static std::shared_ptr<ExternalTexture> copyBuffer(RenderEngine& re,
111                                                    std::shared_ptr<ExternalTexture> original,
112                                                    uint64_t extraUsageFlags, std::string name) {
113     const uint32_t width = original->getBuffer()->getWidth();
114     const uint32_t height = original->getBuffer()->getHeight();
115     auto texture = allocateBuffer(re, width, height, extraUsageFlags, name);
116 
117     const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height));
118     DisplaySettings display{
119             .physicalDisplay = displayRect,
120             .clip = displayRect,
121             .maxLuminance = 500,
122     };
123 
124     const FloatRect layerRect(0, 0, width, height);
125     LayerSettings layer{
126             .geometry =
127                     Geometry{
128                             .boundaries = layerRect,
129                     },
130             .source =
131                     PixelSource{
132                             .buffer =
133                                     Buffer{
134                                             .buffer = original,
135                                     },
136                     },
137             .alpha = half(1.0f),
138     };
139     auto layers = std::vector<LayerSettings>{layer};
140 
141     sp<Fence> waitFence = re.drawLayers(display, layers, texture, base::unique_fd()).get().value();
142     waitFence->waitForever(LOG_TAG);
143     return texture;
144 }
145 
146 /**
147  * Helper for timing calls to drawLayers.
148  *
149  * Caller needs to create RenderEngine and the LayerSettings, and this takes
150  * care of setting up the display, starting and stopping the timer, calling
151  * drawLayers, and saving (if --save is used).
152  *
153  * This times both the CPU and GPU work initiated by drawLayers. All work done
154  * outside of the for loop is excluded from the timing measurements.
155  */
benchDrawLayers(RenderEngine & re,const std::vector<LayerSettings> & layers,benchmark::State & benchState,const char * saveFileName)156 static void benchDrawLayers(RenderEngine& re, const std::vector<LayerSettings>& layers,
157                             benchmark::State& benchState, const char* saveFileName) {
158     auto [width, height] = getDisplaySize();
159     auto outputBuffer = allocateBuffer(re, width, height);
160 
161     const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height));
162     DisplaySettings display{
163             .physicalDisplay = displayRect,
164             .clip = displayRect,
165             .maxLuminance = 500,
166     };
167 
168     // This loop starts and stops the timer.
169     for (auto _ : benchState) {
170         sp<Fence> waitFence =
171                 re.drawLayers(display, layers, outputBuffer, base::unique_fd()).get().value();
172         waitFence->waitForever(LOG_TAG);
173     }
174 
175     if (renderenginebench::save() && saveFileName) {
176         // Copy to a CPU-accessible buffer so we can encode it.
177         outputBuffer = copyBuffer(re, outputBuffer, GRALLOC_USAGE_SW_READ_OFTEN, "to_encode");
178 
179         std::string outFile = base::GetExecutableDirectory();
180         outFile.append("/");
181         outFile.append(saveFileName);
182         outFile.append(".jpg");
183         renderenginebench::encodeToJpeg(outFile.c_str(), outputBuffer->getBuffer());
184     }
185 }
186 
187 /**
188  * Return a buffer with the image in the provided path, relative to the executable directory
189  */
createTexture(RenderEngine & re,const char * relPathImg)190 static std::shared_ptr<ExternalTexture> createTexture(RenderEngine& re, const char* relPathImg) {
191     // Initially use cpu access so we can decode into it with AImageDecoder.
192     auto [width, height] = getDisplaySize();
193     auto srcBuffer =
194             allocateBuffer(re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN, "decoded_source");
195     std::string fileName = base::GetExecutableDirectory().append(relPathImg);
196     renderenginebench::decode(fileName.c_str(), srcBuffer->getBuffer());
197     // Now copy into GPU-only buffer for more realistic timing.
198     srcBuffer = copyBuffer(re, srcBuffer, 0, "source");
199     return srcBuffer;
200 }
201 
202 ///////////////////////////////////////////////////////////////////////////////
203 //  Benchmarks
204 ///////////////////////////////////////////////////////////////////////////////
205 
206 constexpr char kHomescreenPath[] = "/resources/homescreen.png";
207 
208 /**
209  * Draw a layer with texture and no additional shaders as a baseline to evaluate a shader's impact
210  * on performance
211  */
212 template <class... Args>
BM_homescreen(benchmark::State & benchState,Args &&...args)213 void BM_homescreen(benchmark::State& benchState, Args&&... args) {
214     auto args_tuple = std::make_tuple(std::move(args)...);
215     auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)),
216                                  static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple)));
217 
218     auto [width, height] = getDisplaySize();
219     auto srcBuffer = createTexture(*re, kHomescreenPath);
220 
221     const FloatRect layerRect(0, 0, width, height);
222     LayerSettings layer{
223             .geometry =
224                     Geometry{
225                             .boundaries = layerRect,
226                     },
227             .source =
228                     PixelSource{
229                             .buffer =
230                                     Buffer{
231                                             .buffer = srcBuffer,
232                                     },
233                     },
234             .alpha = half(1.0f),
235     };
236     auto layers = std::vector<LayerSettings>{layer};
237     benchDrawLayers(*re, layers, benchState, "homescreen");
238 }
239 
240 template <class... Args>
BM_homescreen_blur(benchmark::State & benchState,Args &&...args)241 void BM_homescreen_blur(benchmark::State& benchState, Args&&... args) {
242     auto args_tuple = std::make_tuple(std::move(args)...);
243     auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)),
244                                  static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple)));
245 
246     auto [width, height] = getDisplaySize();
247     auto srcBuffer = createTexture(*re, kHomescreenPath);
248 
249     const FloatRect layerRect(0, 0, width, height);
250     LayerSettings layer{
251             .geometry =
252                     Geometry{
253                             .boundaries = layerRect,
254                     },
255             .source =
256                     PixelSource{
257                             .buffer =
258                                     Buffer{
259                                             .buffer = srcBuffer,
260                                     },
261                     },
262             .alpha = half(1.0f),
263     };
264     LayerSettings blurLayer{
265             .geometry =
266                     Geometry{
267                             .boundaries = layerRect,
268                     },
269             .alpha = half(1.0f),
270             .skipContentDraw = true,
271             .backgroundBlurRadius = 60,
272     };
273 
274     auto layers = std::vector<LayerSettings>{layer, blurLayer};
275     benchDrawLayers(*re, layers, benchState, "homescreen_blurred");
276 }
277 
278 template <class... Args>
BM_homescreen_edgeExtension(benchmark::State & benchState,Args &&...args)279 void BM_homescreen_edgeExtension(benchmark::State& benchState, Args&&... args) {
280     auto args_tuple = std::make_tuple(std::move(args)...);
281     auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)),
282                                  static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple)));
283 
284     auto [width, height] = getDisplaySize();
285     auto srcBuffer = createTexture(*re, kHomescreenPath);
286 
287     LayerSettings layer{
288             .geometry =
289                     Geometry{
290                             .boundaries = FloatRect(0, 0, width, height),
291                     },
292             .source =
293                     PixelSource{
294                             .buffer =
295                                     Buffer{
296                                             .buffer = srcBuffer,
297                                             // Part of the screen is not covered by the texture but
298                                             // will be filled in by the shader
299                                             .textureTransform =
300                                                     mat4(mat3(),
301                                                          vec3(width * 0.3f, height * 0.3f, 0.0f)),
302                                     },
303                     },
304             .alpha = half(1.0f),
305             .edgeExtensionEffect =
306                     EdgeExtensionEffect(/* left */ true,
307                                         /* right  */ false, /* top */ true, /* bottom */ false),
308     };
309     auto layers = std::vector<LayerSettings>{layer};
310     benchDrawLayers(*re, layers, benchState, "homescreen_edge_extension");
311 }
312 
313 BENCHMARK_CAPTURE(BM_homescreen_blur, gaussian, RenderEngine::Threaded::YES,
314                   RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::GAUSSIAN);
315 
316 BENCHMARK_CAPTURE(BM_homescreen_blur, kawase, RenderEngine::Threaded::YES,
317                   RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::KAWASE);
318 
319 BENCHMARK_CAPTURE(BM_homescreen_blur, kawase_dual_filter, RenderEngine::Threaded::YES,
320                   RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER);
321 
322 BENCHMARK_CAPTURE(BM_homescreen, SkiaGLThreaded, RenderEngine::Threaded::YES,
323                   RenderEngine::GraphicsApi::GL);
324 
325 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_EDGE_EXTENSION_SHADER
326 BENCHMARK_CAPTURE(BM_homescreen_edgeExtension, SkiaGLThreaded, RenderEngine::Threaded::YES,
327                   RenderEngine::GraphicsApi::GL);
328 #endif
329