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