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 #include "Cache.h"
17 #include "AutoBackendTexture.h"
18 #include "SkiaRenderEngine.h"
19 #include "android-base/unique_fd.h"
20 #include "renderengine/DisplaySettings.h"
21 #include "renderengine/LayerSettings.h"
22 #include "ui/GraphicBuffer.h"
23 #include "ui/GraphicTypes.h"
24 #include "ui/PixelFormat.h"
25 #include "ui/Rect.h"
26 #include "utils/Timers.h"
27
28 namespace android::renderengine::skia {
29
30 namespace {
31 // Warming shader cache, not framebuffer cache.
32 constexpr bool kUseFrameBufferCache = false;
33
34 // clang-format off
35 // Any non-identity matrix will do.
36 const auto kScaleAndTranslate = mat4(0.7f, 0.f, 0.f, 0.f,
37 0.f, 0.7f, 0.f, 0.f,
38 0.f, 0.f, 1.f, 0.f,
39 67.3f, 52.2f, 0.f, 1.f);
40 const auto kScaleAsymmetric = mat4(0.8f, 0.f, 0.f, 0.f,
41 0.f, 1.1f, 0.f, 0.f,
42 0.f, 0.f, 1.f, 0.f,
43 0.f, 0.f, 0.f, 1.f);
44 const auto kFlip = mat4(1.1f, -0.1f, 0.f, 0.f,
45 0.1f, 1.1f, 0.f, 0.f,
46 0.f, 0.f, 1.f, 0.f,
47 2.f, 2.f, 0.f, 1.f);
48 // clang-format on
49 // When setting layer.sourceDataspace, whether it matches the destination or not determines whether
50 // a color correction effect is added to the shader.
51 constexpr auto kDestDataSpace = ui::Dataspace::SRGB;
52 constexpr auto kOtherDataSpace = ui::Dataspace::DISPLAY_P3;
53 } // namespace
54
drawShadowLayers(SkiaRenderEngine * renderengine,const DisplaySettings & display,const std::shared_ptr<ExternalTexture> & dstTexture)55 static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
56 const std::shared_ptr<ExternalTexture>& dstTexture) {
57 // Somewhat arbitrary dimensions, but on screen and slightly shorter, based
58 // on actual use.
59 const Rect& displayRect = display.physicalDisplay;
60 FloatRect rect(0, 0, displayRect.width(), displayRect.height());
61 FloatRect smallerRect(20, 20, displayRect.width()-20, displayRect.height()-20);
62
63 LayerSettings layer{
64 .geometry =
65 Geometry{
66 .boundaries = rect,
67 .roundedCornersCrop = rect,
68 .roundedCornersRadius = 50.f,
69 },
70 // drawShadow ignores alpha
71 .shadow =
72 ShadowSettings{
73 .boundaries = rect,
74 .ambientColor = vec4(0, 0, 0, 0.00935997f),
75 .spotColor = vec4(0, 0, 0, 0.0455841f),
76 .lightPos = vec3(500.f, -1500.f, 1500.f),
77 .lightRadius = 2500.0f,
78 .length = 15.f,
79 },
80 // setting this is mandatory for shadows and blurs
81 .skipContentDraw = true,
82 .alpha = 1,
83 };
84 LayerSettings caster{
85 .geometry =
86 Geometry{
87 .boundaries = smallerRect,
88 .roundedCornersCrop = rect,
89 .roundedCornersRadius = 50.f,
90 },
91 .source =
92 PixelSource{
93 .solidColor = half3(0.f, 0.f, 0.f),
94 },
95 .alpha = 1,
96 };
97
98 auto layers = std::vector<const LayerSettings*>{&layer, &caster};
99 // When sourceDataspace matches dest, the general shadow fragment shader doesn't
100 // have color correction added.
101 // independently, when it is not srgb, the *vertex* shader has color correction added.
102 // This may be a bug, but the shader still needs to be cached as it is triggered
103 // during youtube pip.
104 for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
105 layer.sourceDataspace = dataspace;
106 // The 2nd matrix, which has different scales for x and y, will
107 // generate the slower (more general case) shadow shader
108 for (auto transform : {mat4(), kScaleAndTranslate, kFlip}) {
109 layer.geometry.positionTransform = transform;
110 caster.geometry.positionTransform = transform;
111 for (bool translucent : {false, true}){
112 layer.shadow.casterIsTranslucent = translucent;
113 renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
114 base::unique_fd(), nullptr);
115 }
116 }
117 }
118 }
119
drawImageLayers(SkiaRenderEngine * renderengine,const DisplaySettings & display,const std::shared_ptr<ExternalTexture> & dstTexture,const std::shared_ptr<ExternalTexture> & srcTexture)120 static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
121 const std::shared_ptr<ExternalTexture>& dstTexture,
122 const std::shared_ptr<ExternalTexture>& srcTexture) {
123 const Rect& displayRect = display.physicalDisplay;
124 FloatRect rect(0, 0, displayRect.width(), displayRect.height());
125 LayerSettings layer{
126 .geometry =
127 Geometry{
128 // The position transform doesn't matter when the reduced shader mode
129 // in in effect. A matrix transform stage is always included.
130 .positionTransform = mat4(),
131 .boundaries = rect,
132 .roundedCornersCrop = rect,
133 },
134 .source = PixelSource{.buffer =
135 Buffer{
136 .buffer = srcTexture,
137 .maxLuminanceNits = 1000.f,
138 }},
139 };
140
141 auto layers = std::vector<const LayerSettings*>{&layer};
142 for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
143 layer.sourceDataspace = dataspace;
144 // Cache shaders for both rects and round rects.
145 // In reduced shader mode, all non-zero round rect radii get the same code path.
146 for (float roundedCornersRadius : {0.0f, 50.0f}) {
147 // roundedCornersCrop is always set, but the radius triggers the behavior
148 layer.geometry.roundedCornersRadius = roundedCornersRadius;
149 for (bool isOpaque : {true, false}) {
150 layer.source.buffer.isOpaque = isOpaque;
151 for (auto alpha : {half(.2f), half(1.0f)}) {
152 layer.alpha = alpha;
153 renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
154 base::unique_fd(), nullptr);
155 }
156 }
157 }
158 }
159 }
160
drawSolidLayers(SkiaRenderEngine * renderengine,const DisplaySettings & display,const std::shared_ptr<ExternalTexture> & dstTexture)161 static void drawSolidLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
162 const std::shared_ptr<ExternalTexture>& dstTexture) {
163 const Rect& displayRect = display.physicalDisplay;
164 FloatRect rect(0, 0, displayRect.width(), displayRect.height());
165 LayerSettings layer{
166 .geometry =
167 Geometry{
168 .boundaries = rect,
169 },
170 .source =
171 PixelSource{
172 .solidColor = half3(0.1f, 0.2f, 0.3f),
173 },
174 .alpha = 0.5,
175 };
176
177 auto layers = std::vector<const LayerSettings*>{&layer};
178 for (auto transform : {mat4(), kScaleAndTranslate}) {
179 layer.geometry.positionTransform = transform;
180 for (float roundedCornersRadius : {0.0f, 50.f}) {
181 layer.geometry.roundedCornersRadius = roundedCornersRadius;
182 renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
183 base::unique_fd(), nullptr);
184 }
185 }
186 }
187
drawBlurLayers(SkiaRenderEngine * renderengine,const DisplaySettings & display,const std::shared_ptr<ExternalTexture> & dstTexture)188 static void drawBlurLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
189 const std::shared_ptr<ExternalTexture>& dstTexture) {
190 const Rect& displayRect = display.physicalDisplay;
191 FloatRect rect(0, 0, displayRect.width(), displayRect.height());
192 LayerSettings layer{
193 .geometry =
194 Geometry{
195 .boundaries = rect,
196 },
197 .alpha = 1,
198 // setting this is mandatory for shadows and blurs
199 .skipContentDraw = true,
200 };
201
202 auto layers = std::vector<const LayerSettings*>{&layer};
203 // Different blur code is invoked for radii less and greater than 30 pixels
204 for (int radius : {9, 60}) {
205 layer.backgroundBlurRadius = radius;
206 renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
207 base::unique_fd(), nullptr);
208 }
209 }
210
211 // The unique feature of these layers is that the boundary is slightly smaller than the rounded
212 // rect crop, so the rounded edges intersect that boundary and require a different clipping method.
213 // For buffers, this is done with a stage that computes coverage and it will differ for round and
214 // elliptical corners.
drawClippedLayers(SkiaRenderEngine * renderengine,const DisplaySettings & display,const std::shared_ptr<ExternalTexture> & dstTexture,const std::shared_ptr<ExternalTexture> & srcTexture)215 static void drawClippedLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
216 const std::shared_ptr<ExternalTexture>& dstTexture,
217 const std::shared_ptr<ExternalTexture>& srcTexture) {
218 const Rect& displayRect = display.physicalDisplay;
219 FloatRect rect(0, 0, displayRect.width(), displayRect.height() - 20); // boundary is smaller
220
221 PixelSource bufferSource{.buffer = Buffer{
222 .buffer = srcTexture,
223 .isOpaque = 0,
224 .maxLuminanceNits = 1000.f,
225 }};
226 PixelSource bufferOpaque{.buffer = Buffer{
227 .buffer = srcTexture,
228 .isOpaque = 1,
229 .maxLuminanceNits = 1000.f,
230 }};
231 PixelSource colorSource{.solidColor = half3(0.1f, 0.2f, 0.3f)};
232
233 LayerSettings layer{
234 .geometry =
235 Geometry{
236 .boundaries = rect,
237 .roundedCornersRadius = 27, // larger than the 20 above.
238 .roundedCornersCrop =
239 FloatRect(0, 0, displayRect.width(), displayRect.height()),
240 },
241 };
242
243 auto layers = std::vector<const LayerSettings*>{&layer};
244 for (auto pixelSource : {bufferSource, bufferOpaque, colorSource}) {
245 layer.source = pixelSource;
246 for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
247 layer.sourceDataspace = dataspace;
248 // Produce a CircularRRect clip and an EllipticalRRect clip.
249 for (auto transform : {kScaleAndTranslate, kScaleAsymmetric}) {
250 layer.geometry.positionTransform = transform;
251 for (float alpha : {0.5f, 1.f}) {
252 layer.alpha = alpha,
253 renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
254 base::unique_fd(), nullptr);
255 }
256 }
257 }
258 }
259 }
260
drawPIPImageLayer(SkiaRenderEngine * renderengine,const DisplaySettings & display,const std::shared_ptr<ExternalTexture> & dstTexture,const std::shared_ptr<ExternalTexture> & srcTexture)261 static void drawPIPImageLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
262 const std::shared_ptr<ExternalTexture>& dstTexture,
263 const std::shared_ptr<ExternalTexture>& srcTexture) {
264 const Rect& displayRect = display.physicalDisplay;
265 FloatRect rect(0, 0, displayRect.width(), displayRect.height());
266 LayerSettings layer{
267 .geometry =
268 Geometry{
269 // Note that this flip matrix only makes a difference when clipping,
270 // which happens in this layer because the roundrect crop is just a bit
271 // larger than the layer bounds.
272 .positionTransform = kFlip,
273 .boundaries = rect,
274 .roundedCornersRadius = 94.2551,
275 .roundedCornersCrop = FloatRect(
276 -93.75, 0, displayRect.width() + 93.75, displayRect.height()),
277 },
278 .source = PixelSource{.buffer =
279 Buffer{
280 .buffer = srcTexture,
281 .maxLuminanceNits = 1000.f,
282 .isOpaque = 0,
283 .usePremultipliedAlpha = 1,
284 }},
285 .sourceDataspace = kOtherDataSpace,
286 .alpha = 1,
287
288 };
289
290 auto layers = std::vector<const LayerSettings*>{&layer};
291 renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
292 base::unique_fd(), nullptr);
293 }
294
drawHolePunchLayer(SkiaRenderEngine * renderengine,const DisplaySettings & display,const std::shared_ptr<ExternalTexture> & dstTexture)295 static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
296 const std::shared_ptr<ExternalTexture>& dstTexture) {
297 const Rect& displayRect = display.physicalDisplay;
298 FloatRect rect(0, 0, displayRect.width(), displayRect.height());
299 FloatRect small(0, 0, displayRect.width()-20, displayRect.height()+20);
300 LayerSettings layer{
301 .geometry =
302 Geometry{
303 .positionTransform = kScaleAndTranslate,
304 // the boundaries have to be smaller than the rounded crop so that
305 // clipRRect is used instead of drawRRect
306 .boundaries = small,
307 .roundedCornersRadius = 50.f,
308 .roundedCornersCrop = rect,
309 },
310 .source = PixelSource{
311 .solidColor = half3(0.f, 0.f, 0.f),
312 },
313 .sourceDataspace = kDestDataSpace,
314 .alpha = 0,
315 .disableBlending = true,
316
317 };
318
319 auto layers = std::vector<const LayerSettings*>{&layer};
320 renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
321 base::unique_fd(), nullptr);
322 }
323
324 //
325 // The collection of shaders cached here were found by using perfetto to record shader compiles
326 // during actions that involve RenderEngine, logging the layer settings, and the shader code
327 // and reproducing those settings here.
328 //
329 // It is helpful when debugging this to turn on
330 // in SkGLRenderEngine.cpp:
331 // kPrintLayerSettings = true
332 // kFlushAfterEveryLayer = true
333 // in external/skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
334 // gPrintSKSL = true
primeShaderCache(SkiaRenderEngine * renderengine)335 void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
336 const int previousCount = renderengine->reportShadersCompiled();
337 if (previousCount) {
338 ALOGD("%d Shaders already compiled before Cache::primeShaderCache ran\n", previousCount);
339 }
340
341 // The loop is beneficial for debugging and should otherwise be optimized out by the compiler.
342 // Adding additional bounds to the loop is useful for verifying that the size of the dst buffer
343 // does not impact the shader compilation counts by triggering different behaviors in RE/Skia.
344 for (SkSize bounds : {SkSize::Make(128, 128), /*SkSize::Make(1080, 2340)*/}) {
345 const nsecs_t timeBefore = systemTime();
346 // The dimensions should not matter, so long as we draw inside them.
347 const Rect displayRect(0, 0, bounds.fWidth, bounds.fHeight);
348 DisplaySettings display{
349 .physicalDisplay = displayRect,
350 .clip = displayRect,
351 .maxLuminance = 500,
352 .outputDataspace = kDestDataSpace,
353 };
354 DisplaySettings p3Display{
355 .physicalDisplay = displayRect,
356 .clip = displayRect,
357 .maxLuminance = 500,
358 .outputDataspace = kOtherDataSpace,
359 };
360
361 const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
362
363 sp<GraphicBuffer> dstBuffer =
364 new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
365 1, usage, "primeShaderCache_dst");
366
367 const auto dstTexture =
368 std::make_shared<ExternalTexture>(dstBuffer, *renderengine,
369 ExternalTexture::Usage::WRITEABLE);
370 // This buffer will be the source for the call to drawImageLayers. Draw
371 // something to it as a placeholder for what an app draws. We should draw
372 // something, but the details are not important. Make use of the shadow layer drawing step
373 // to populate it.
374 sp<GraphicBuffer> srcBuffer =
375 new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
376 1, usage, "drawImageLayer_src");
377
378 const auto srcTexture =
379 std::make_shared<ExternalTexture>(srcBuffer, *renderengine,
380 ExternalTexture::Usage::READABLE |
381 ExternalTexture::Usage::WRITEABLE);
382 drawHolePunchLayer(renderengine, display, dstTexture);
383 drawSolidLayers(renderengine, display, dstTexture);
384 drawShadowLayers(renderengine, display, srcTexture);
385 drawShadowLayers(renderengine, p3Display, srcTexture);
386
387 if (renderengine->supportsBackgroundBlur()) {
388 drawBlurLayers(renderengine, display, dstTexture);
389 }
390
391 // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
392 const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE;
393 sp<GraphicBuffer> externalBuffer =
394 new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
395 1, usageExternal, "primeShaderCache_external");
396 const auto externalTexture =
397 std::make_shared<ExternalTexture>(externalBuffer, *renderengine,
398 ExternalTexture::Usage::READABLE);
399
400 // Another external texture with a different pixel format triggers useIsOpaqueWorkaround
401 sp<GraphicBuffer> f16ExternalBuffer =
402 new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_FP16,
403 1, usageExternal, "primeShaderCache_external_f16");
404 const auto f16ExternalTexture =
405 std::make_shared<ExternalTexture>(f16ExternalBuffer, *renderengine,
406 ExternalTexture::Usage::READABLE);
407
408 // The majority of shaders are related to sampling images.
409 // These need to be generated with various source textures
410 // The F16 texture may not be usable on all devices, so check first that it was created with
411 // the requested usage bit.
412 auto textures = {srcTexture, externalTexture};
413 auto texturesWithF16 = {srcTexture, externalTexture, f16ExternalTexture};
414 bool canUsef16 = f16ExternalBuffer->getUsage() & GRALLOC_USAGE_HW_TEXTURE;
415
416 for (auto texture : canUsef16 ? texturesWithF16 : textures) {
417 drawImageLayers(renderengine, display, dstTexture, texture);
418 // Draw layers for b/185569240.
419 drawClippedLayers(renderengine, display, dstTexture, texture);
420 }
421
422 drawPIPImageLayer(renderengine, display, dstTexture, externalTexture);
423
424 const nsecs_t timeAfter = systemTime();
425 const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
426 const int shadersCompiled = renderengine->reportShadersCompiled();
427 ALOGD("Shader cache generated %d shaders in %f ms\n", shadersCompiled, compileTimeMs);
428 }
429 }
430
431 } // namespace android::renderengine::skia
432