• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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