1 /*
2  * Copyright 2020 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 //#define LOG_NDEBUG 0
18 #undef LOG_TAG
19 #define LOG_TAG "RenderEngine"
20 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
21 
22 #include "SkiaGLRenderEngine.h"
23 
24 #include <EGL/egl.h>
25 #include <EGL/eglext.h>
26 #include <GrContextOptions.h>
27 #include <SkCanvas.h>
28 #include <SkColorFilter.h>
29 #include <SkColorMatrix.h>
30 #include <SkColorSpace.h>
31 #include <SkGraphics.h>
32 #include <SkImage.h>
33 #include <SkImageFilters.h>
34 #include <SkRegion.h>
35 #include <SkShadowUtils.h>
36 #include <SkSurface.h>
37 #include <android-base/stringprintf.h>
38 #include <gl/GrGLInterface.h>
39 #include <gui/TraceUtils.h>
40 #include <sync/sync.h>
41 #include <ui/BlurRegion.h>
42 #include <ui/DebugUtils.h>
43 #include <ui/GraphicBuffer.h>
44 #include <utils/Trace.h>
45 
46 #include <cmath>
47 #include <cstdint>
48 #include <memory>
49 
50 #include "../gl/GLExtensions.h"
51 #include "Cache.h"
52 #include "ColorSpaces.h"
53 #include "SkBlendMode.h"
54 #include "SkImageInfo.h"
55 #include "filters/BlurFilter.h"
56 #include "filters/LinearEffect.h"
57 #include "log/log_main.h"
58 #include "skia/debug/SkiaCapture.h"
59 #include "skia/debug/SkiaMemoryReporter.h"
60 #include "skia/filters/StretchShaderFactory.h"
61 #include "system/graphics-base-v1.0.h"
62 
63 namespace {
64 // Debugging settings
65 static const bool kPrintLayerSettings = false;
66 static const bool kFlushAfterEveryLayer = false;
67 } // namespace
68 
69 bool checkGlError(const char* op, int lineNumber);
70 
71 namespace android {
72 namespace renderengine {
73 namespace skia {
74 
75 using base::StringAppendF;
76 
selectConfigForAttribute(EGLDisplay dpy,EGLint const * attrs,EGLint attribute,EGLint wanted,EGLConfig * outConfig)77 static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute,
78                                          EGLint wanted, EGLConfig* outConfig) {
79     EGLint numConfigs = -1, n = 0;
80     eglGetConfigs(dpy, nullptr, 0, &numConfigs);
81     std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR);
82     eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n);
83     configs.resize(n);
84 
85     if (!configs.empty()) {
86         if (attribute != EGL_NONE) {
87             for (EGLConfig config : configs) {
88                 EGLint value = 0;
89                 eglGetConfigAttrib(dpy, config, attribute, &value);
90                 if (wanted == value) {
91                     *outConfig = config;
92                     return NO_ERROR;
93                 }
94             }
95         } else {
96             // just pick the first one
97             *outConfig = configs[0];
98             return NO_ERROR;
99         }
100     }
101 
102     return NAME_NOT_FOUND;
103 }
104 
selectEGLConfig(EGLDisplay display,EGLint format,EGLint renderableType,EGLConfig * config)105 static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType,
106                                 EGLConfig* config) {
107     // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
108     // it is to be used with WIFI displays
109     status_t err;
110     EGLint wantedAttribute;
111     EGLint wantedAttributeValue;
112 
113     std::vector<EGLint> attribs;
114     if (renderableType) {
115         const ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(format);
116         const bool is1010102 = pixelFormat == ui::PixelFormat::RGBA_1010102;
117 
118         // Default to 8 bits per channel.
119         const EGLint tmpAttribs[] = {
120                 EGL_RENDERABLE_TYPE,
121                 renderableType,
122                 EGL_RECORDABLE_ANDROID,
123                 EGL_TRUE,
124                 EGL_SURFACE_TYPE,
125                 EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
126                 EGL_FRAMEBUFFER_TARGET_ANDROID,
127                 EGL_TRUE,
128                 EGL_RED_SIZE,
129                 is1010102 ? 10 : 8,
130                 EGL_GREEN_SIZE,
131                 is1010102 ? 10 : 8,
132                 EGL_BLUE_SIZE,
133                 is1010102 ? 10 : 8,
134                 EGL_ALPHA_SIZE,
135                 is1010102 ? 2 : 8,
136                 EGL_NONE,
137         };
138         std::copy(tmpAttribs, tmpAttribs + (sizeof(tmpAttribs) / sizeof(EGLint)),
139                   std::back_inserter(attribs));
140         wantedAttribute = EGL_NONE;
141         wantedAttributeValue = EGL_NONE;
142     } else {
143         // if no renderable type specified, fallback to a simplified query
144         wantedAttribute = EGL_NATIVE_VISUAL_ID;
145         wantedAttributeValue = format;
146     }
147 
148     err = selectConfigForAttribute(display, attribs.data(), wantedAttribute, wantedAttributeValue,
149                                    config);
150     if (err == NO_ERROR) {
151         EGLint caveat;
152         if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
153             ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
154     }
155 
156     return err;
157 }
158 
create(const RenderEngineCreationArgs & args)159 std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create(
160         const RenderEngineCreationArgs& args) {
161     // initialize EGL for the default display
162     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
163     if (!eglInitialize(display, nullptr, nullptr)) {
164         LOG_ALWAYS_FATAL("failed to initialize EGL");
165     }
166 
167     const auto eglVersion = eglQueryString(display, EGL_VERSION);
168     if (!eglVersion) {
169         checkGlError(__FUNCTION__, __LINE__);
170         LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed");
171     }
172 
173     const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS);
174     if (!eglExtensions) {
175         checkGlError(__FUNCTION__, __LINE__);
176         LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed");
177     }
178 
179     auto& extensions = gl::GLExtensions::getInstance();
180     extensions.initWithEGLStrings(eglVersion, eglExtensions);
181 
182     // The code assumes that ES2 or later is available if this extension is
183     // supported.
184     EGLConfig config = EGL_NO_CONFIG_KHR;
185     if (!extensions.hasNoConfigContext()) {
186         config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
187     }
188 
189     EGLContext protectedContext = EGL_NO_CONTEXT;
190     const std::optional<RenderEngine::ContextPriority> priority = createContextPriority(args);
191     if (args.enableProtectedContext && extensions.hasProtectedContent()) {
192         protectedContext =
193                 createEglContext(display, config, nullptr, priority, Protection::PROTECTED);
194         ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
195     }
196 
197     EGLContext ctxt =
198             createEglContext(display, config, protectedContext, priority, Protection::UNPROTECTED);
199 
200     // if can't create a GL context, we can only abort.
201     LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
202 
203     EGLSurface placeholder = EGL_NO_SURFACE;
204     if (!extensions.hasSurfacelessContext()) {
205         placeholder = createPlaceholderEglPbufferSurface(display, config, args.pixelFormat,
206                                                          Protection::UNPROTECTED);
207         LOG_ALWAYS_FATAL_IF(placeholder == EGL_NO_SURFACE, "can't create placeholder pbuffer");
208     }
209     EGLBoolean success = eglMakeCurrent(display, placeholder, placeholder, ctxt);
210     LOG_ALWAYS_FATAL_IF(!success, "can't make placeholder pbuffer current");
211     extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
212                                  glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
213 
214     EGLSurface protectedPlaceholder = EGL_NO_SURFACE;
215     if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) {
216         protectedPlaceholder = createPlaceholderEglPbufferSurface(display, config, args.pixelFormat,
217                                                                   Protection::PROTECTED);
218         ALOGE_IF(protectedPlaceholder == EGL_NO_SURFACE,
219                  "can't create protected placeholder pbuffer");
220     }
221 
222     // initialize the renderer while GL is current
223     std::unique_ptr<SkiaGLRenderEngine> engine =
224             std::make_unique<SkiaGLRenderEngine>(args, display, ctxt, placeholder, protectedContext,
225                                                  protectedPlaceholder);
226 
227     ALOGI("OpenGL ES informations:");
228     ALOGI("vendor    : %s", extensions.getVendor());
229     ALOGI("renderer  : %s", extensions.getRenderer());
230     ALOGI("version   : %s", extensions.getVersion());
231     ALOGI("extensions: %s", extensions.getExtensions());
232     ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
233     ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
234 
235     return engine;
236 }
237 
primeCache()238 std::future<void> SkiaGLRenderEngine::primeCache() {
239     Cache::primeShaderCache(this);
240     return {};
241 }
242 
chooseEglConfig(EGLDisplay display,int format,bool logConfig)243 EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
244     status_t err;
245     EGLConfig config;
246 
247     // First try to get an ES3 config
248     err = selectEGLConfig(display, format, EGL_OPENGL_ES3_BIT, &config);
249     if (err != NO_ERROR) {
250         // If ES3 fails, try to get an ES2 config
251         err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config);
252         if (err != NO_ERROR) {
253             // If ES2 still doesn't work, probably because we're on the emulator.
254             // try a simplified query
255             ALOGW("no suitable EGLConfig found, trying a simpler query");
256             err = selectEGLConfig(display, format, 0, &config);
257             if (err != NO_ERROR) {
258                 // this EGL is too lame for android
259                 LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
260             }
261         }
262     }
263 
264     if (logConfig) {
265         // print some debugging info
266         EGLint r, g, b, a;
267         eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
268         eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
269         eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
270         eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
271         ALOGI("EGL information:");
272         ALOGI("vendor    : %s", eglQueryString(display, EGL_VENDOR));
273         ALOGI("version   : %s", eglQueryString(display, EGL_VERSION));
274         ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS));
275         ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported");
276         ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
277     }
278 
279     return config;
280 }
281 
load(const SkData & key)282 sk_sp<SkData> SkiaGLRenderEngine::SkSLCacheMonitor::load(const SkData& key) {
283     // This "cache" does not actually cache anything. It just allows us to
284     // monitor Skia's internal cache. So this method always returns null.
285     return nullptr;
286 }
287 
store(const SkData & key,const SkData & data,const SkString & description)288 void SkiaGLRenderEngine::SkSLCacheMonitor::store(const SkData& key, const SkData& data,
289                                                  const SkString& description) {
290     mShadersCachedSinceLastCall++;
291 }
292 
assertShadersCompiled(int numShaders)293 void SkiaGLRenderEngine::assertShadersCompiled(int numShaders) {
294     const int cached = mSkSLCacheMonitor.shadersCachedSinceLastCall();
295     LOG_ALWAYS_FATAL_IF(cached != numShaders, "Attempted to cache %i shaders; cached %i",
296                         numShaders, cached);
297 }
298 
reportShadersCompiled()299 int SkiaGLRenderEngine::reportShadersCompiled() {
300     return mSkSLCacheMonitor.shadersCachedSinceLastCall();
301 }
302 
SkiaGLRenderEngine(const RenderEngineCreationArgs & args,EGLDisplay display,EGLContext ctxt,EGLSurface placeholder,EGLContext protectedContext,EGLSurface protectedPlaceholder)303 SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
304                                        EGLContext ctxt, EGLSurface placeholder,
305                                        EGLContext protectedContext, EGLSurface protectedPlaceholder)
306       : SkiaRenderEngine(args.renderEngineType),
307         mEGLDisplay(display),
308         mEGLContext(ctxt),
309         mPlaceholderSurface(placeholder),
310         mProtectedEGLContext(protectedContext),
311         mProtectedPlaceholderSurface(protectedPlaceholder),
312         mDefaultPixelFormat(static_cast<PixelFormat>(args.pixelFormat)),
313         mUseColorManagement(args.useColorManagement) {
314     sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
315     LOG_ALWAYS_FATAL_IF(!glInterface.get());
316 
317     GrContextOptions options;
318     options.fDisableDriverCorrectnessWorkarounds = true;
319     options.fDisableDistanceFieldPaths = true;
320     options.fReducedShaderVariations = true;
321     options.fPersistentCache = &mSkSLCacheMonitor;
322     mGrContext = GrDirectContext::MakeGL(glInterface, options);
323     if (supportsProtectedContent()) {
324         useProtectedContext(true);
325         mProtectedGrContext = GrDirectContext::MakeGL(glInterface, options);
326         useProtectedContext(false);
327     }
328 
329     if (args.supportsBackgroundBlur) {
330         ALOGD("Background Blurs Enabled");
331         mBlurFilter = new BlurFilter();
332     }
333     mCapture = std::make_unique<SkiaCapture>();
334 }
335 
~SkiaGLRenderEngine()336 SkiaGLRenderEngine::~SkiaGLRenderEngine() {
337     std::lock_guard<std::mutex> lock(mRenderingMutex);
338     if (mBlurFilter) {
339         delete mBlurFilter;
340     }
341 
342     mCapture = nullptr;
343 
344     mGrContext->flushAndSubmit(true);
345     mGrContext->abandonContext();
346 
347     if (mProtectedGrContext) {
348         mProtectedGrContext->flushAndSubmit(true);
349         mProtectedGrContext->abandonContext();
350     }
351 
352     if (mPlaceholderSurface != EGL_NO_SURFACE) {
353         eglDestroySurface(mEGLDisplay, mPlaceholderSurface);
354     }
355     if (mProtectedPlaceholderSurface != EGL_NO_SURFACE) {
356         eglDestroySurface(mEGLDisplay, mProtectedPlaceholderSurface);
357     }
358     if (mEGLContext != EGL_NO_CONTEXT) {
359         eglDestroyContext(mEGLDisplay, mEGLContext);
360     }
361     if (mProtectedEGLContext != EGL_NO_CONTEXT) {
362         eglDestroyContext(mEGLDisplay, mProtectedEGLContext);
363     }
364     eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
365     eglTerminate(mEGLDisplay);
366     eglReleaseThread();
367 }
368 
supportsProtectedContent() const369 bool SkiaGLRenderEngine::supportsProtectedContent() const {
370     return mProtectedEGLContext != EGL_NO_CONTEXT;
371 }
372 
getActiveGrContext() const373 GrDirectContext* SkiaGLRenderEngine::getActiveGrContext() const {
374     return mInProtectedContext ? mProtectedGrContext.get() : mGrContext.get();
375 }
376 
useProtectedContext(bool useProtectedContext)377 void SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) {
378     if (useProtectedContext == mInProtectedContext ||
379         (useProtectedContext && !supportsProtectedContent())) {
380         return;
381     }
382 
383     // release any scratch resources before switching into a new mode
384     if (getActiveGrContext()) {
385         getActiveGrContext()->purgeUnlockedResources(true);
386     }
387 
388     const EGLSurface surface =
389             useProtectedContext ? mProtectedPlaceholderSurface : mPlaceholderSurface;
390     const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
391 
392     if (eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE) {
393         mInProtectedContext = useProtectedContext;
394         // given that we are sharing the same thread between two GrContexts we need to
395         // make sure that the thread state is reset when switching between the two.
396         if (getActiveGrContext()) {
397             getActiveGrContext()->resetContext();
398         }
399     }
400 }
401 
flush()402 base::unique_fd SkiaGLRenderEngine::flush() {
403     ATRACE_CALL();
404     if (!gl::GLExtensions::getInstance().hasNativeFenceSync()) {
405         return base::unique_fd();
406     }
407 
408     EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
409     if (sync == EGL_NO_SYNC_KHR) {
410         ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
411         return base::unique_fd();
412     }
413 
414     // native fence fd will not be populated until flush() is done.
415     glFlush();
416 
417     // get the fence fd
418     base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
419     eglDestroySyncKHR(mEGLDisplay, sync);
420     if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
421         ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
422     }
423 
424     return fenceFd;
425 }
426 
waitFence(base::borrowed_fd fenceFd)427 void SkiaGLRenderEngine::waitFence(base::borrowed_fd fenceFd) {
428     if (fenceFd.get() >= 0 && !waitGpuFence(fenceFd)) {
429         ATRACE_NAME("SkiaGLRenderEngine::waitFence");
430         sync_wait(fenceFd.get(), -1);
431     }
432 }
433 
waitGpuFence(base::borrowed_fd fenceFd)434 bool SkiaGLRenderEngine::waitGpuFence(base::borrowed_fd fenceFd) {
435     if (!gl::GLExtensions::getInstance().hasNativeFenceSync() ||
436         !gl::GLExtensions::getInstance().hasWaitSync()) {
437         return false;
438     }
439 
440     // Duplicate the fence for passing to eglCreateSyncKHR.
441     base::unique_fd fenceDup(dup(fenceFd.get()));
442     if (fenceDup.get() < 0) {
443         ALOGE("failed to create duplicate fence fd: %d", fenceDup.get());
444         return false;
445     }
446 
447     // release the fd and transfer the ownership to EGLSync
448     EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceDup.release(), EGL_NONE};
449     EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
450     if (sync == EGL_NO_SYNC_KHR) {
451         ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
452         return false;
453     }
454 
455     // XXX: The spec draft is inconsistent as to whether this should return an
456     // EGLint or void.  Ignore the return value for now, as it's not strictly
457     // needed.
458     eglWaitSyncKHR(mEGLDisplay, sync, 0);
459     EGLint error = eglGetError();
460     eglDestroySyncKHR(mEGLDisplay, sync);
461     if (error != EGL_SUCCESS) {
462         ALOGE("failed to wait for EGL native fence sync: %#x", error);
463         return false;
464     }
465 
466     return true;
467 }
468 
toDegrees(uint32_t transform)469 static float toDegrees(uint32_t transform) {
470     switch (transform) {
471         case ui::Transform::ROT_90:
472             return 90.0;
473         case ui::Transform::ROT_180:
474             return 180.0;
475         case ui::Transform::ROT_270:
476             return 270.0;
477         default:
478             return 0.0;
479     }
480 }
481 
toSkColorMatrix(const mat4 & matrix)482 static SkColorMatrix toSkColorMatrix(const mat4& matrix) {
483     return SkColorMatrix(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], 0, matrix[0][1],
484                          matrix[1][1], matrix[2][1], matrix[3][1], 0, matrix[0][2], matrix[1][2],
485                          matrix[2][2], matrix[3][2], 0, matrix[0][3], matrix[1][3], matrix[2][3],
486                          matrix[3][3], 0);
487 }
488 
needsToneMapping(ui::Dataspace sourceDataspace,ui::Dataspace destinationDataspace)489 static bool needsToneMapping(ui::Dataspace sourceDataspace, ui::Dataspace destinationDataspace) {
490     int64_t sourceTransfer = sourceDataspace & HAL_DATASPACE_TRANSFER_MASK;
491     int64_t destTransfer = destinationDataspace & HAL_DATASPACE_TRANSFER_MASK;
492 
493     // Treat unsupported dataspaces as srgb
494     if (destTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
495         destTransfer != HAL_DATASPACE_TRANSFER_HLG &&
496         destTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
497         destTransfer = HAL_DATASPACE_TRANSFER_SRGB;
498     }
499 
500     if (sourceTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
501         sourceTransfer != HAL_DATASPACE_TRANSFER_HLG &&
502         sourceTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
503         sourceTransfer = HAL_DATASPACE_TRANSFER_SRGB;
504     }
505 
506     const bool isSourceLinear = sourceTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
507     const bool isSourceSRGB = sourceTransfer == HAL_DATASPACE_TRANSFER_SRGB;
508     const bool isDestLinear = destTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
509     const bool isDestSRGB = destTransfer == HAL_DATASPACE_TRANSFER_SRGB;
510 
511     return !(isSourceLinear && isDestSRGB) && !(isSourceSRGB && isDestLinear) &&
512             sourceTransfer != destTransfer;
513 }
514 
mapExternalTextureBuffer(const sp<GraphicBuffer> & buffer,bool isRenderable)515 void SkiaGLRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
516                                                   bool isRenderable) {
517     // Only run this if RE is running on its own thread. This way the access to GL
518     // operations is guaranteed to be happening on the same thread.
519     if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) {
520         return;
521     }
522     // We currently don't attempt to map a buffer if the buffer contains protected content
523     // because GPU resources for protected buffers is much more limited.
524     const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
525     if (isProtectedBuffer) {
526         return;
527     }
528     ATRACE_CALL();
529 
530     // If we were to support caching protected buffers then we will need to switch the
531     // currently bound context if we are not already using the protected context (and subsequently
532     // switch back after the buffer is cached).  However, for non-protected content we can bind
533     // the texture in either GL context because they are initialized with the same share_context
534     // which allows the texture state to be shared between them.
535     auto grContext = getActiveGrContext();
536     auto& cache = mTextureCache;
537 
538     std::lock_guard<std::mutex> lock(mRenderingMutex);
539     mGraphicBufferExternalRefs[buffer->getId()]++;
540 
541     if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) {
542         std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
543                 std::make_shared<AutoBackendTexture::LocalRef>(grContext,
544                                                                buffer->toAHardwareBuffer(),
545                                                                isRenderable, mTextureCleanupMgr);
546         cache.insert({buffer->getId(), imageTextureRef});
547     }
548 }
549 
unmapExternalTextureBuffer(const sp<GraphicBuffer> & buffer)550 void SkiaGLRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
551     ATRACE_CALL();
552     std::lock_guard<std::mutex> lock(mRenderingMutex);
553     if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId());
554         iter != mGraphicBufferExternalRefs.end()) {
555         if (iter->second == 0) {
556             ALOGW("Attempted to unmap GraphicBuffer <id: %" PRId64
557                   "> from RenderEngine texture, but the "
558                   "ref count was already zero!",
559                   buffer->getId());
560             mGraphicBufferExternalRefs.erase(buffer->getId());
561             return;
562         }
563 
564         iter->second--;
565 
566         // Swap contexts if needed prior to deleting this buffer
567         // See Issue 1 of
568         // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_protected_content.txt: even
569         // when a protected context and an unprotected context are part of the same share group,
570         // protected surfaces may not be accessed by an unprotected context, implying that protected
571         // surfaces may only be freed when a protected context is active.
572         const bool inProtected = mInProtectedContext;
573         useProtectedContext(buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
574 
575         if (iter->second == 0) {
576             mTextureCache.erase(buffer->getId());
577             mGraphicBufferExternalRefs.erase(buffer->getId());
578         }
579 
580         // Swap back to the previous context so that cached values of isProtected in SurfaceFlinger
581         // are up-to-date.
582         if (inProtected != mInProtectedContext) {
583             useProtectedContext(inProtected);
584         }
585     }
586 }
587 
canSkipPostRenderCleanup() const588 bool SkiaGLRenderEngine::canSkipPostRenderCleanup() const {
589     std::lock_guard<std::mutex> lock(mRenderingMutex);
590     return mTextureCleanupMgr.isEmpty();
591 }
592 
cleanupPostRender()593 void SkiaGLRenderEngine::cleanupPostRender() {
594     ATRACE_CALL();
595     std::lock_guard<std::mutex> lock(mRenderingMutex);
596     mTextureCleanupMgr.cleanup();
597 }
598 
599 // Helper class intended to be used on the stack to ensure that texture cleanup
600 // is deferred until after this class goes out of scope.
601 class DeferTextureCleanup final {
602 public:
DeferTextureCleanup(AutoBackendTexture::CleanupManager & mgr)603     DeferTextureCleanup(AutoBackendTexture::CleanupManager& mgr) : mMgr(mgr) {
604         mMgr.setDeferredStatus(true);
605     }
~DeferTextureCleanup()606     ~DeferTextureCleanup() { mMgr.setDeferredStatus(false); }
607 
608 private:
609     DISALLOW_COPY_AND_ASSIGN(DeferTextureCleanup);
610     AutoBackendTexture::CleanupManager& mMgr;
611 };
612 
createRuntimeEffectShader(sk_sp<SkShader> shader,const LayerSettings * layer,const DisplaySettings & display,bool undoPremultipliedAlpha,bool requiresLinearEffect)613 sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
614         sk_sp<SkShader> shader,
615         const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha,
616         bool requiresLinearEffect) {
617     const auto stretchEffect = layer->stretchEffect;
618     // The given surface will be stretched by HWUI via matrix transformation
619     // which gets similar results for most surfaces
620     // Determine later on if we need to leverage the stertch shader within
621     // surface flinger
622     if (stretchEffect.hasEffect()) {
623         const auto targetBuffer = layer->source.buffer.buffer;
624         const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
625         if (graphicBuffer && shader) {
626             shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
627         }
628     }
629 
630     if (requiresLinearEffect) {
631         const ui::Dataspace inputDataspace =
632                 mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::V0_SRGB_LINEAR;
633         const ui::Dataspace outputDataspace =
634                 mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
635 
636         LinearEffect effect = LinearEffect{.inputDataspace = inputDataspace,
637                                            .outputDataspace = outputDataspace,
638                                            .undoPremultipliedAlpha = undoPremultipliedAlpha};
639 
640         auto effectIter = mRuntimeEffects.find(effect);
641         sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
642         if (effectIter == mRuntimeEffects.end()) {
643             runtimeEffect = buildRuntimeEffect(effect);
644             mRuntimeEffects.insert({effect, runtimeEffect});
645         } else {
646             runtimeEffect = effectIter->second;
647         }
648         float maxLuminance = layer->source.buffer.maxLuminanceNits;
649         // If the buffer doesn't have a max luminance, treat it as SDR & use the display's SDR
650         // white point
651         if (maxLuminance <= 0.f) {
652             maxLuminance = display.sdrWhitePointNits;
653         }
654         return createLinearEffectShader(shader, effect, runtimeEffect, layer->colorTransform,
655                                         display.maxLuminance, maxLuminance);
656     }
657     return shader;
658 }
659 
initCanvas(SkCanvas * canvas,const DisplaySettings & display)660 void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
661     if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
662         // Record display settings when capture is running.
663         std::stringstream displaySettings;
664         PrintTo(display, &displaySettings);
665         // Store the DisplaySettings in additional information.
666         canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings",
667                                SkData::MakeWithCString(displaySettings.str().c_str()));
668     }
669 
670     // Before doing any drawing, let's make sure that we'll start at the origin of the display.
671     // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual
672     // displays might have different scaling when compared to the physical screen.
673 
674     canvas->clipRect(getSkRect(display.physicalDisplay));
675     canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top);
676 
677     const auto clipWidth = display.clip.width();
678     const auto clipHeight = display.clip.height();
679     auto rotatedClipWidth = clipWidth;
680     auto rotatedClipHeight = clipHeight;
681     // Scale is contingent on the rotation result.
682     if (display.orientation & ui::Transform::ROT_90) {
683         std::swap(rotatedClipWidth, rotatedClipHeight);
684     }
685     const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) /
686             static_cast<SkScalar>(rotatedClipWidth);
687     const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) /
688             static_cast<SkScalar>(rotatedClipHeight);
689     canvas->scale(scaleX, scaleY);
690 
691     // Canvas rotation is done by centering the clip window at the origin, rotating, translating
692     // back so that the top left corner of the clip is at (0, 0).
693     canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2);
694     canvas->rotate(toDegrees(display.orientation));
695     canvas->translate(-clipWidth / 2, -clipHeight / 2);
696     canvas->translate(-display.clip.left, -display.clip.top);
697 }
698 
699 class AutoSaveRestore {
700 public:
AutoSaveRestore(SkCanvas * canvas)701     AutoSaveRestore(SkCanvas* canvas) : mCanvas(canvas) { mSaveCount = canvas->save(); }
~AutoSaveRestore()702     ~AutoSaveRestore() { restore(); }
replace(SkCanvas * canvas)703     void replace(SkCanvas* canvas) {
704         mCanvas = canvas;
705         mSaveCount = canvas->save();
706     }
restore()707     void restore() {
708         if (mCanvas) {
709             mCanvas->restoreToCount(mSaveCount);
710             mCanvas = nullptr;
711         }
712     }
713 
714 private:
715     SkCanvas* mCanvas;
716     int mSaveCount;
717 };
718 
getBlurRRect(const BlurRegion & region)719 static SkRRect getBlurRRect(const BlurRegion& region) {
720     const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom);
721     const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL),
722                                SkVector::Make(region.cornerRadiusTR, region.cornerRadiusTR),
723                                SkVector::Make(region.cornerRadiusBR, region.cornerRadiusBR),
724                                SkVector::Make(region.cornerRadiusBL, region.cornerRadiusBL)};
725     SkRRect roundedRect;
726     roundedRect.setRectRadii(rect, radii);
727     return roundedRect;
728 }
729 
drawLayers(const DisplaySettings & display,const std::vector<const LayerSettings * > & layers,const std::shared_ptr<ExternalTexture> & buffer,const bool,base::unique_fd && bufferFence,base::unique_fd * drawFence)730 status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
731                                         const std::vector<const LayerSettings*>& layers,
732                                         const std::shared_ptr<ExternalTexture>& buffer,
733                                         const bool /*useFramebufferCache*/,
734                                         base::unique_fd&& bufferFence, base::unique_fd* drawFence) {
735     ATRACE_NAME("SkiaGL::drawLayers");
736 
737     std::lock_guard<std::mutex> lock(mRenderingMutex);
738     if (layers.empty()) {
739         ALOGV("Drawing empty layer stack");
740         return NO_ERROR;
741     }
742 
743     if (buffer == nullptr) {
744         ALOGE("No output buffer provided. Aborting GPU composition.");
745         return BAD_VALUE;
746     }
747 
748     validateOutputBufferUsage(buffer->getBuffer());
749 
750     auto grContext = getActiveGrContext();
751     auto& cache = mTextureCache;
752 
753     // any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called
754     DeferTextureCleanup dtc(mTextureCleanupMgr);
755 
756     std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef;
757     if (const auto& it = cache.find(buffer->getBuffer()->getId()); it != cache.end()) {
758         surfaceTextureRef = it->second;
759     } else {
760         surfaceTextureRef =
761                 std::make_shared<AutoBackendTexture::LocalRef>(grContext,
762                                                                buffer->getBuffer()
763                                                                        ->toAHardwareBuffer(),
764                                                                true, mTextureCleanupMgr);
765     }
766 
767     // wait on the buffer to be ready to use prior to using it
768     waitFence(bufferFence);
769 
770     const ui::Dataspace dstDataspace =
771             mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
772     sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext);
773 
774     SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
775     if (dstCanvas == nullptr) {
776         ALOGE("Cannot acquire canvas from Skia.");
777         return BAD_VALUE;
778     }
779 
780     // setup color filter if necessary
781     sk_sp<SkColorFilter> displayColorTransform;
782     if (display.colorTransform != mat4()) {
783         displayColorTransform = SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform));
784     }
785     const bool ctModifiesAlpha =
786             displayColorTransform && !displayColorTransform->isAlphaUnchanged();
787 
788     // Find if any layers have requested blur, we'll use that info to decide when to render to an
789     // offscreen buffer and when to render to the native buffer.
790     sk_sp<SkSurface> activeSurface(dstSurface);
791     SkCanvas* canvas = dstCanvas;
792     SkiaCapture::OffscreenState offscreenCaptureState;
793     const LayerSettings* blurCompositionLayer = nullptr;
794     if (mBlurFilter) {
795         bool requiresCompositionLayer = false;
796         for (const auto& layer : layers) {
797             // if the layer doesn't have blur or it is not visible then continue
798             if (!layerHasBlur(layer, ctModifiesAlpha)) {
799                 continue;
800             }
801             if (layer->backgroundBlurRadius > 0 &&
802                 layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius) {
803                 requiresCompositionLayer = true;
804             }
805             for (auto region : layer->blurRegions) {
806                 if (region.blurRadius < BlurFilter::kMaxCrossFadeRadius) {
807                     requiresCompositionLayer = true;
808                 }
809             }
810             if (requiresCompositionLayer) {
811                 activeSurface = dstSurface->makeSurface(dstSurface->imageInfo());
812                 canvas = mCapture->tryOffscreenCapture(activeSurface.get(), &offscreenCaptureState);
813                 blurCompositionLayer = layer;
814                 break;
815             }
816         }
817     }
818 
819     AutoSaveRestore surfaceAutoSaveRestore(canvas);
820     // Clear the entire canvas with a transparent black to prevent ghost images.
821     canvas->clear(SK_ColorTRANSPARENT);
822     initCanvas(canvas, display);
823 
824     // TODO: clearRegion was required for SurfaceView when a buffer is not yet available but the
825     // view is still on-screen. The clear region could be re-specified as a black color layer,
826     // however.
827     if (!display.clearRegion.isEmpty()) {
828         ATRACE_NAME("ClearRegion");
829         size_t numRects = 0;
830         Rect const* rects = display.clearRegion.getArray(&numRects);
831         SkIRect skRects[numRects];
832         for (int i = 0; i < numRects; ++i) {
833             skRects[i] =
834                     SkIRect::MakeLTRB(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
835         }
836         SkRegion clearRegion;
837         SkPaint paint;
838         sk_sp<SkShader> shader =
839                 SkShaders::Color(SkColor4f{.fR = 0., .fG = 0., .fB = 0., .fA = 1.0},
840                                  toSkColorSpace(dstDataspace));
841         paint.setShader(shader);
842         clearRegion.setRects(skRects, numRects);
843         canvas->drawRegion(clearRegion, paint);
844     }
845 
846     for (const auto& layer : layers) {
847         ATRACE_FORMAT("DrawLayer: %s", layer->name.c_str());
848 
849         if (kPrintLayerSettings) {
850             std::stringstream ls;
851             PrintTo(*layer, &ls);
852             auto debugs = ls.str();
853             int pos = 0;
854             while (pos < debugs.size()) {
855                 ALOGD("cache_debug %s", debugs.substr(pos, 1000).c_str());
856                 pos += 1000;
857             }
858         }
859 
860         sk_sp<SkImage> blurInput;
861         if (blurCompositionLayer == layer) {
862             LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface);
863             LOG_ALWAYS_FATAL_IF(canvas == dstCanvas);
864 
865             // save a snapshot of the activeSurface to use as input to the blur shaders
866             blurInput = activeSurface->makeImageSnapshot();
867 
868             // TODO we could skip this step if we know the blur will cover the entire image
869             //  blit the offscreen framebuffer into the destination AHB
870             SkPaint paint;
871             paint.setBlendMode(SkBlendMode::kSrc);
872             if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
873                 uint64_t id = mCapture->endOffscreenCapture(&offscreenCaptureState);
874                 dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()),
875                                           String8::format("SurfaceID|%" PRId64, id).c_str(),
876                                           nullptr);
877                 dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint);
878             } else {
879                 activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint);
880             }
881 
882             // assign dstCanvas to canvas and ensure that the canvas state is up to date
883             canvas = dstCanvas;
884             surfaceAutoSaveRestore.replace(canvas);
885             initCanvas(canvas, display);
886 
887             LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getSaveCount() !=
888                                 dstSurface->getCanvas()->getSaveCount());
889             LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getTotalMatrix() !=
890                                 dstSurface->getCanvas()->getTotalMatrix());
891 
892             // assign dstSurface to activeSurface
893             activeSurface = dstSurface;
894         }
895 
896         SkAutoCanvasRestore layerAutoSaveRestore(canvas, true);
897         if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
898             // Record the name of the layer if the capture is running.
899             std::stringstream layerSettings;
900             PrintTo(*layer, &layerSettings);
901             // Store the LayerSettings in additional information.
902             canvas->drawAnnotation(SkRect::MakeEmpty(), layer->name.c_str(),
903                                    SkData::MakeWithCString(layerSettings.str().c_str()));
904         }
905         // Layers have a local transform that should be applied to them
906         canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());
907 
908         const auto [bounds, roundRectClip] =
909                 getBoundsAndClip(layer->geometry.boundaries, layer->geometry.roundedCornersCrop,
910                                  layer->geometry.roundedCornersRadius);
911         if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) {
912             std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
913 
914             // if multiple layers have blur, then we need to take a snapshot now because
915             // only the lowest layer will have blurImage populated earlier
916             if (!blurInput) {
917                 blurInput = activeSurface->makeImageSnapshot();
918             }
919             // rect to be blurred in the coordinate space of blurInput
920             const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect());
921 
922             // if the clip needs to be applied then apply it now and make sure
923             // it is restored before we attempt to draw any shadows.
924             SkAutoCanvasRestore acr(canvas, true);
925             if (!roundRectClip.isEmpty()) {
926                 canvas->clipRRect(roundRectClip, true);
927             }
928 
929             // TODO(b/182216890): Filter out empty layers earlier
930             if (blurRect.width() > 0 && blurRect.height() > 0) {
931                 if (layer->backgroundBlurRadius > 0) {
932                     ATRACE_NAME("BackgroundBlur");
933                     auto blurredImage =
934                             mBlurFilter->generate(grContext, layer->backgroundBlurRadius, blurInput,
935                                                   blurRect);
936 
937                     cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
938 
939                     mBlurFilter->drawBlurRegion(canvas, bounds, layer->backgroundBlurRadius, 1.0f,
940                                                 blurRect, blurredImage, blurInput);
941                 }
942 
943                 canvas->concat(getSkM44(layer->blurRegionTransform).asM33());
944                 for (auto region : layer->blurRegions) {
945                     if (cachedBlurs[region.blurRadius] == nullptr) {
946                         ATRACE_NAME("BlurRegion");
947                         cachedBlurs[region.blurRadius] =
948                                 mBlurFilter->generate(grContext, region.blurRadius, blurInput,
949                                                       blurRect);
950                     }
951 
952                     mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius,
953                                                 region.alpha, blurRect,
954                                                 cachedBlurs[region.blurRadius], blurInput);
955                 }
956             }
957         }
958 
959         if (layer->shadow.length > 0) {
960             // This would require a new parameter/flag to SkShadowUtils::DrawShadow
961             LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow");
962 
963             SkRRect shadowBounds, shadowClip;
964             if (layer->geometry.boundaries == layer->shadow.boundaries) {
965                 shadowBounds = bounds;
966                 shadowClip = roundRectClip;
967             } else {
968                 std::tie(shadowBounds, shadowClip) =
969                         getBoundsAndClip(layer->shadow.boundaries,
970                                          layer->geometry.roundedCornersCrop,
971                                          layer->geometry.roundedCornersRadius);
972             }
973 
974             // Technically, if bounds is a rect and roundRectClip is not empty,
975             // it means that the bounds and roundedCornersCrop were different
976             // enough that we should intersect them to find the proper shadow.
977             // In practice, this often happens when the two rectangles appear to
978             // not match due to rounding errors. Draw the rounded version, which
979             // looks more like the intent.
980             const auto& rrect =
981                     shadowBounds.isRect() && !shadowClip.isEmpty() ? shadowClip : shadowBounds;
982             drawShadow(canvas, rrect, layer->shadow);
983         }
984 
985         const bool requiresLinearEffect = layer->colorTransform != mat4() ||
986                 (mUseColorManagement &&
987                  needsToneMapping(layer->sourceDataspace, display.outputDataspace)) ||
988                 (display.sdrWhitePointNits > 0.f &&
989                  display.sdrWhitePointNits != display.maxLuminance);
990 
991         // quick abort from drawing the remaining portion of the layer
992         if (layer->skipContentDraw ||
993             (layer->alpha == 0 && !requiresLinearEffect && !layer->disableBlending &&
994              (!displayColorTransform || displayColorTransform->isAlphaUnchanged()))) {
995             continue;
996         }
997 
998         // If we need to map to linear space or color management is disabled, then mark the source
999         // image with the same colorspace as the destination surface so that Skia's color
1000         // management is a no-op.
1001         const ui::Dataspace layerDataspace = (!mUseColorManagement || requiresLinearEffect)
1002                 ? dstDataspace
1003                 : layer->sourceDataspace;
1004 
1005         SkPaint paint;
1006         if (layer->source.buffer.buffer) {
1007             ATRACE_NAME("DrawImage");
1008             validateInputBufferUsage(layer->source.buffer.buffer->getBuffer());
1009             const auto& item = layer->source.buffer;
1010             std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr;
1011 
1012             if (const auto& iter = cache.find(item.buffer->getBuffer()->getId());
1013                 iter != cache.end()) {
1014                 imageTextureRef = iter->second;
1015             } else {
1016                 // If we didn't find the image in the cache, then create a local ref but don't cache
1017                 // it. If we're using skia, we're guaranteed to run on a dedicated GPU thread so if
1018                 // we didn't find anything in the cache then we intentionally did not cache this
1019                 // buffer's resources.
1020                 imageTextureRef = std::make_shared<
1021                         AutoBackendTexture::LocalRef>(grContext,
1022                                                       item.buffer->getBuffer()->toAHardwareBuffer(),
1023                                                       false, mTextureCleanupMgr);
1024             }
1025 
1026             // if the layer's buffer has a fence, then we must must respect the fence prior to using
1027             // the buffer.
1028             if (layer->source.buffer.fence != nullptr) {
1029                 waitFence(layer->source.buffer.fence->get());
1030             }
1031 
1032             // isOpaque means we need to ignore the alpha in the image,
1033             // replacing it with the alpha specified by the LayerSettings. See
1034             // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean)
1035             // The proper way to do this is to use an SkColorType that ignores
1036             // alpha, like kRGB_888x_SkColorType, and that is used if the
1037             // incoming image is kRGBA_8888_SkColorType. However, the incoming
1038             // image may be kRGBA_F16_SkColorType, for which there is no RGBX
1039             // SkColorType, or kRGBA_1010102_SkColorType, for which we have
1040             // kRGB_101010x_SkColorType, but it is not yet supported as a source
1041             // on the GPU. (Adding both is tracked in skbug.com/12048.) In the
1042             // meantime, we'll use a workaround that works unless we need to do
1043             // any color conversion. The workaround requires that we pretend the
1044             // image is already premultiplied, so that we do not premultiply it
1045             // before applying SkBlendMode::kPlus.
1046             const bool useIsOpaqueWorkaround = item.isOpaque &&
1047                     (imageTextureRef->colorType() == kRGBA_1010102_SkColorType ||
1048                      imageTextureRef->colorType() == kRGBA_F16_SkColorType);
1049             const auto alphaType = useIsOpaqueWorkaround ? kPremul_SkAlphaType
1050                     : item.isOpaque                      ? kOpaque_SkAlphaType
1051                     : item.usePremultipliedAlpha         ? kPremul_SkAlphaType
1052                                                          : kUnpremul_SkAlphaType;
1053             sk_sp<SkImage> image = imageTextureRef->makeImage(layerDataspace, alphaType, grContext);
1054 
1055             auto texMatrix = getSkM44(item.textureTransform).asM33();
1056             // textureTansform was intended to be passed directly into a shader, so when
1057             // building the total matrix with the textureTransform we need to first
1058             // normalize it, then apply the textureTransform, then scale back up.
1059             texMatrix.preScale(1.0f / bounds.width(), 1.0f / bounds.height());
1060             texMatrix.postScale(image->width(), image->height());
1061 
1062             SkMatrix matrix;
1063             if (!texMatrix.invert(&matrix)) {
1064                 matrix = texMatrix;
1065             }
1066             // The shader does not respect the translation, so we add it to the texture
1067             // transform for the SkImage. This will make sure that the correct layer contents
1068             // are drawn in the correct part of the screen.
1069             matrix.postTranslate(bounds.rect().fLeft, bounds.rect().fTop);
1070 
1071             sk_sp<SkShader> shader;
1072 
1073             if (layer->source.buffer.useTextureFiltering) {
1074                 shader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
1075                                            SkSamplingOptions(
1076                                                    {SkFilterMode::kLinear, SkMipmapMode::kNone}),
1077                                            &matrix);
1078             } else {
1079                 shader = image->makeShader(SkSamplingOptions(), matrix);
1080             }
1081 
1082             if (useIsOpaqueWorkaround) {
1083                 shader = SkShaders::Blend(SkBlendMode::kPlus, shader,
1084                                           SkShaders::Color(SkColors::kBlack,
1085                                                            toSkColorSpace(layerDataspace)));
1086             }
1087 
1088             paint.setShader(createRuntimeEffectShader(shader, layer, display,
1089                                                       !item.isOpaque && item.usePremultipliedAlpha,
1090                                                       requiresLinearEffect));
1091             paint.setAlphaf(layer->alpha);
1092         } else {
1093             ATRACE_NAME("DrawColor");
1094             const auto color = layer->source.solidColor;
1095             sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r,
1096                                                                 .fG = color.g,
1097                                                                 .fB = color.b,
1098                                                                 .fA = layer->alpha},
1099                                                       toSkColorSpace(layerDataspace));
1100             paint.setShader(createRuntimeEffectShader(shader, layer, display,
1101                                                       /* undoPremultipliedAlpha */ false,
1102                                                       requiresLinearEffect));
1103         }
1104 
1105         if (layer->disableBlending) {
1106             paint.setBlendMode(SkBlendMode::kSrc);
1107         }
1108 
1109         paint.setColorFilter(displayColorTransform);
1110 
1111         if (!roundRectClip.isEmpty()) {
1112             canvas->clipRRect(roundRectClip, true);
1113         }
1114 
1115         if (!bounds.isRect()) {
1116             paint.setAntiAlias(true);
1117             canvas->drawRRect(bounds, paint);
1118         } else {
1119             canvas->drawRect(bounds.rect(), paint);
1120         }
1121         if (kFlushAfterEveryLayer) {
1122             ATRACE_NAME("flush surface");
1123             activeSurface->flush();
1124         }
1125     }
1126     surfaceAutoSaveRestore.restore();
1127     mCapture->endCapture();
1128     {
1129         ATRACE_NAME("flush surface");
1130         LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
1131         activeSurface->flush();
1132     }
1133 
1134     if (drawFence != nullptr) {
1135         *drawFence = flush();
1136     }
1137 
1138     // If flush failed or we don't support native fences, we need to force the
1139     // gl command stream to be executed.
1140     bool requireSync = drawFence == nullptr || drawFence->get() < 0;
1141     if (requireSync) {
1142         ATRACE_BEGIN("Submit(sync=true)");
1143     } else {
1144         ATRACE_BEGIN("Submit(sync=false)");
1145     }
1146     bool success = grContext->submit(requireSync);
1147     ATRACE_END();
1148     if (!success) {
1149         ALOGE("Failed to flush RenderEngine commands");
1150         // Chances are, something illegal happened (either the caller passed
1151         // us bad parameters, or we messed up our shader generation).
1152         return INVALID_OPERATION;
1153     }
1154 
1155     // checkErrors();
1156     return NO_ERROR;
1157 }
1158 
getSkRect(const FloatRect & rect)1159 inline SkRect SkiaGLRenderEngine::getSkRect(const FloatRect& rect) {
1160     return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
1161 }
1162 
getSkRect(const Rect & rect)1163 inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) {
1164     return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
1165 }
1166 
getBoundsAndClip(const FloatRect & boundsRect,const FloatRect & cropRect,const float cornerRadius)1167 inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
1168                                                                         const FloatRect& cropRect,
1169                                                                         const float cornerRadius) {
1170     const SkRect bounds = getSkRect(boundsRect);
1171     const SkRect crop = getSkRect(cropRect);
1172 
1173     SkRRect clip;
1174     if (cornerRadius > 0) {
1175         // it the crop and the bounds are equivalent or there is no crop then we don't need a clip
1176         if (bounds == crop || crop.isEmpty()) {
1177             return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip};
1178         }
1179 
1180         // This makes an effort to speed up common, simple bounds + clip combinations by
1181         // converting them to a single RRect draw. It is possible there are other cases
1182         // that can be converted.
1183         if (crop.contains(bounds)) {
1184             bool intersectionIsRoundRect = true;
1185             // check each cropped corner to ensure that it exactly matches the crop or is full
1186             SkVector radii[4];
1187 
1188             const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
1189 
1190             const bool leftEqual = bounds.fLeft == crop.fLeft;
1191             const bool topEqual = bounds.fTop == crop.fTop;
1192             const bool rightEqual = bounds.fRight == crop.fRight;
1193             const bool bottomEqual = bounds.fBottom == crop.fBottom;
1194 
1195             // compute the UpperLeft corner radius
1196             if (leftEqual && topEqual) {
1197                 radii[0].set(cornerRadius, cornerRadius);
1198             } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
1199                        (topEqual && bounds.fLeft >= insetCrop.fLeft) ||
1200                        insetCrop.contains(bounds.fLeft, bounds.fTop)) {
1201                 radii[0].set(0, 0);
1202             } else {
1203                 intersectionIsRoundRect = false;
1204             }
1205             // compute the UpperRight corner radius
1206             if (rightEqual && topEqual) {
1207                 radii[1].set(cornerRadius, cornerRadius);
1208             } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
1209                        (topEqual && bounds.fRight <= insetCrop.fRight) ||
1210                        insetCrop.contains(bounds.fRight, bounds.fTop)) {
1211                 radii[1].set(0, 0);
1212             } else {
1213                 intersectionIsRoundRect = false;
1214             }
1215             // compute the BottomRight corner radius
1216             if (rightEqual && bottomEqual) {
1217                 radii[2].set(cornerRadius, cornerRadius);
1218             } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
1219                        (bottomEqual && bounds.fRight <= insetCrop.fRight) ||
1220                        insetCrop.contains(bounds.fRight, bounds.fBottom)) {
1221                 radii[2].set(0, 0);
1222             } else {
1223                 intersectionIsRoundRect = false;
1224             }
1225             // compute the BottomLeft corner radius
1226             if (leftEqual && bottomEqual) {
1227                 radii[3].set(cornerRadius, cornerRadius);
1228             } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
1229                        (bottomEqual && bounds.fLeft >= insetCrop.fLeft) ||
1230                        insetCrop.contains(bounds.fLeft, bounds.fBottom)) {
1231                 radii[3].set(0, 0);
1232             } else {
1233                 intersectionIsRoundRect = false;
1234             }
1235 
1236             if (intersectionIsRoundRect) {
1237                 SkRRect intersectionBounds;
1238                 intersectionBounds.setRectRadii(bounds, radii);
1239                 return {intersectionBounds, clip};
1240             }
1241         }
1242 
1243         // we didn't it any of our fast paths so set the clip to the cropRect
1244         clip.setRectXY(crop, cornerRadius, cornerRadius);
1245     }
1246 
1247     // if we hit this point then we either don't have rounded corners or we are going to rely
1248     // on the clip to round the corners for us
1249     return {SkRRect::MakeRect(bounds), clip};
1250 }
1251 
layerHasBlur(const LayerSettings * layer,bool colorTransformModifiesAlpha)1252 inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer,
1253                                              bool colorTransformModifiesAlpha) {
1254     if (layer->backgroundBlurRadius > 0 || layer->blurRegions.size()) {
1255         // return false if the content is opaque and would therefore occlude the blur
1256         const bool opaqueContent = !layer->source.buffer.buffer || layer->source.buffer.isOpaque;
1257         const bool opaqueAlpha = layer->alpha == 1.0f && !colorTransformModifiesAlpha;
1258         return layer->skipContentDraw || !(opaqueContent && opaqueAlpha);
1259     }
1260     return false;
1261 }
1262 
getSkColor(const vec4 & color)1263 inline SkColor SkiaGLRenderEngine::getSkColor(const vec4& color) {
1264     return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255);
1265 }
1266 
getSkM44(const mat4 & matrix)1267 inline SkM44 SkiaGLRenderEngine::getSkM44(const mat4& matrix) {
1268     return SkM44(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0],
1269                  matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1],
1270                  matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2],
1271                  matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]);
1272 }
1273 
getSkPoint3(const vec3 & vector)1274 inline SkPoint3 SkiaGLRenderEngine::getSkPoint3(const vec3& vector) {
1275     return SkPoint3::Make(vector.x, vector.y, vector.z);
1276 }
1277 
getMaxTextureSize() const1278 size_t SkiaGLRenderEngine::getMaxTextureSize() const {
1279     return mGrContext->maxTextureSize();
1280 }
1281 
getMaxViewportDims() const1282 size_t SkiaGLRenderEngine::getMaxViewportDims() const {
1283     return mGrContext->maxRenderTargetSize();
1284 }
1285 
drawShadow(SkCanvas * canvas,const SkRRect & casterRRect,const ShadowSettings & settings)1286 void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
1287                                     const ShadowSettings& settings) {
1288     ATRACE_CALL();
1289     const float casterZ = settings.length / 2.0f;
1290     const auto flags =
1291             settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag;
1292 
1293     SkShadowUtils::DrawShadow(canvas, SkPath::RRect(casterRRect), SkPoint3::Make(0, 0, casterZ),
1294                               getSkPoint3(settings.lightPos), settings.lightRadius,
1295                               getSkColor(settings.ambientColor), getSkColor(settings.spotColor),
1296                               flags);
1297 }
1298 
createEglContext(EGLDisplay display,EGLConfig config,EGLContext shareContext,std::optional<ContextPriority> contextPriority,Protection protection)1299 EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
1300                                                 EGLContext shareContext,
1301                                                 std::optional<ContextPriority> contextPriority,
1302                                                 Protection protection) {
1303     EGLint renderableType = 0;
1304     if (config == EGL_NO_CONFIG_KHR) {
1305         renderableType = EGL_OPENGL_ES3_BIT;
1306     } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
1307         LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
1308     }
1309     EGLint contextClientVersion = 0;
1310     if (renderableType & EGL_OPENGL_ES3_BIT) {
1311         contextClientVersion = 3;
1312     } else if (renderableType & EGL_OPENGL_ES2_BIT) {
1313         contextClientVersion = 2;
1314     } else if (renderableType & EGL_OPENGL_ES_BIT) {
1315         contextClientVersion = 1;
1316     } else {
1317         LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
1318     }
1319 
1320     std::vector<EGLint> contextAttributes;
1321     contextAttributes.reserve(7);
1322     contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
1323     contextAttributes.push_back(contextClientVersion);
1324     if (contextPriority) {
1325         contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
1326         switch (*contextPriority) {
1327             case ContextPriority::REALTIME:
1328                 contextAttributes.push_back(EGL_CONTEXT_PRIORITY_REALTIME_NV);
1329                 break;
1330             case ContextPriority::MEDIUM:
1331                 contextAttributes.push_back(EGL_CONTEXT_PRIORITY_MEDIUM_IMG);
1332                 break;
1333             case ContextPriority::LOW:
1334                 contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LOW_IMG);
1335                 break;
1336             case ContextPriority::HIGH:
1337             default:
1338                 contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
1339                 break;
1340         }
1341     }
1342     if (protection == Protection::PROTECTED) {
1343         contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
1344         contextAttributes.push_back(EGL_TRUE);
1345     }
1346     contextAttributes.push_back(EGL_NONE);
1347 
1348     EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data());
1349 
1350     if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) {
1351         // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus
1352         // EGL_NO_CONTEXT so that we can abort.
1353         if (config != EGL_NO_CONFIG_KHR) {
1354             return context;
1355         }
1356         // If |config| is EGL_NO_CONFIG_KHR, we speculatively try to create GLES 3 context, so we
1357         // should try to fall back to GLES 2.
1358         contextAttributes[1] = 2;
1359         context = eglCreateContext(display, config, shareContext, contextAttributes.data());
1360     }
1361 
1362     return context;
1363 }
1364 
createContextPriority(const RenderEngineCreationArgs & args)1365 std::optional<RenderEngine::ContextPriority> SkiaGLRenderEngine::createContextPriority(
1366         const RenderEngineCreationArgs& args) {
1367     if (!gl::GLExtensions::getInstance().hasContextPriority()) {
1368         return std::nullopt;
1369     }
1370 
1371     switch (args.contextPriority) {
1372         case RenderEngine::ContextPriority::REALTIME:
1373             if (gl::GLExtensions::getInstance().hasRealtimePriority()) {
1374                 return RenderEngine::ContextPriority::REALTIME;
1375             } else {
1376                 ALOGI("Realtime priority unsupported, degrading gracefully to high priority");
1377                 return RenderEngine::ContextPriority::HIGH;
1378             }
1379         case RenderEngine::ContextPriority::HIGH:
1380         case RenderEngine::ContextPriority::MEDIUM:
1381         case RenderEngine::ContextPriority::LOW:
1382             return args.contextPriority;
1383         default:
1384             return std::nullopt;
1385     }
1386 }
1387 
createPlaceholderEglPbufferSurface(EGLDisplay display,EGLConfig config,int hwcFormat,Protection protection)1388 EGLSurface SkiaGLRenderEngine::createPlaceholderEglPbufferSurface(EGLDisplay display,
1389                                                                   EGLConfig config, int hwcFormat,
1390                                                                   Protection protection) {
1391     EGLConfig placeholderConfig = config;
1392     if (placeholderConfig == EGL_NO_CONFIG_KHR) {
1393         placeholderConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
1394     }
1395     std::vector<EGLint> attributes;
1396     attributes.reserve(7);
1397     attributes.push_back(EGL_WIDTH);
1398     attributes.push_back(1);
1399     attributes.push_back(EGL_HEIGHT);
1400     attributes.push_back(1);
1401     if (protection == Protection::PROTECTED) {
1402         attributes.push_back(EGL_PROTECTED_CONTENT_EXT);
1403         attributes.push_back(EGL_TRUE);
1404     }
1405     attributes.push_back(EGL_NONE);
1406 
1407     return eglCreatePbufferSurface(display, placeholderConfig, attributes.data());
1408 }
1409 
getContextPriority()1410 int SkiaGLRenderEngine::getContextPriority() {
1411     int value;
1412     eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value);
1413     return value;
1414 }
1415 
onActiveDisplaySizeChanged(ui::Size size)1416 void SkiaGLRenderEngine::onActiveDisplaySizeChanged(ui::Size size) {
1417     // This cache multiplier was selected based on review of cache sizes relative
1418     // to the screen resolution. Looking at the worst case memory needed by blur (~1.5x),
1419     // shadows (~1x), and general data structures (e.g. vertex buffers) we selected this as a
1420     // conservative default based on that analysis.
1421     const float SURFACE_SIZE_MULTIPLIER = 3.5f * bytesPerPixel(mDefaultPixelFormat);
1422     const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER;
1423 
1424     // start by resizing the current context
1425     getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
1426 
1427     // if it is possible to switch contexts then we will resize the other context
1428     const bool originalProtectedState = mInProtectedContext;
1429     useProtectedContext(!mInProtectedContext);
1430     if (mInProtectedContext != originalProtectedState) {
1431         getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
1432         // reset back to the initial context that was active when this method was called
1433         useProtectedContext(originalProtectedState);
1434     }
1435 }
1436 
dump(std::string & result)1437 void SkiaGLRenderEngine::dump(std::string& result) {
1438     const gl::GLExtensions& extensions = gl::GLExtensions::getInstance();
1439 
1440     StringAppendF(&result, "\n ------------RE-----------------\n");
1441     StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion());
1442     StringAppendF(&result, "%s\n", extensions.getEGLExtensions());
1443     StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
1444                   extensions.getVersion());
1445     StringAppendF(&result, "%s\n", extensions.getExtensions());
1446     StringAppendF(&result, "RenderEngine supports protected context: %d\n",
1447                   supportsProtectedContent());
1448     StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext);
1449     StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n",
1450                   mSkSLCacheMonitor.shadersCachedSinceLastCall());
1451 
1452     std::vector<ResourcePair> cpuResourceMap = {
1453             {"skia/sk_resource_cache/bitmap_", "Bitmaps"},
1454             {"skia/sk_resource_cache/rrect-blur_", "Masks"},
1455             {"skia/sk_resource_cache/rects-blur_", "Masks"},
1456             {"skia/sk_resource_cache/tessellated", "Shadows"},
1457             {"skia", "Other"},
1458     };
1459     SkiaMemoryReporter cpuReporter(cpuResourceMap, false);
1460     SkGraphics::DumpMemoryStatistics(&cpuReporter);
1461     StringAppendF(&result, "Skia CPU Caches: ");
1462     cpuReporter.logTotals(result);
1463     cpuReporter.logOutput(result);
1464 
1465     {
1466         std::lock_guard<std::mutex> lock(mRenderingMutex);
1467 
1468         std::vector<ResourcePair> gpuResourceMap = {
1469                 {"texture_renderbuffer", "Texture/RenderBuffer"},
1470                 {"texture", "Texture"},
1471                 {"gr_text_blob_cache", "Text"},
1472                 {"skia", "Other"},
1473         };
1474         SkiaMemoryReporter gpuReporter(gpuResourceMap, true);
1475         mGrContext->dumpMemoryStatistics(&gpuReporter);
1476         StringAppendF(&result, "Skia's GPU Caches: ");
1477         gpuReporter.logTotals(result);
1478         gpuReporter.logOutput(result);
1479         StringAppendF(&result, "Skia's Wrapped Objects:\n");
1480         gpuReporter.logOutput(result, true);
1481 
1482         StringAppendF(&result, "RenderEngine tracked buffers: %zu\n",
1483                       mGraphicBufferExternalRefs.size());
1484         StringAppendF(&result, "Dumping buffer ids...\n");
1485         for (const auto& [id, refCounts] : mGraphicBufferExternalRefs) {
1486             StringAppendF(&result, "- 0x%" PRIx64 " - %d refs \n", id, refCounts);
1487         }
1488         StringAppendF(&result, "RenderEngine AHB/BackendTexture cache size: %zu\n",
1489                       mTextureCache.size());
1490         StringAppendF(&result, "Dumping buffer ids...\n");
1491         // TODO(178539829): It would be nice to know which layer these are coming from and what
1492         // the texture sizes are.
1493         for (const auto& [id, unused] : mTextureCache) {
1494             StringAppendF(&result, "- 0x%" PRIx64 "\n", id);
1495         }
1496         StringAppendF(&result, "\n");
1497 
1498         SkiaMemoryReporter gpuProtectedReporter(gpuResourceMap, true);
1499         if (mProtectedGrContext) {
1500             mProtectedGrContext->dumpMemoryStatistics(&gpuProtectedReporter);
1501         }
1502         StringAppendF(&result, "Skia's GPU Protected Caches: ");
1503         gpuProtectedReporter.logTotals(result);
1504         gpuProtectedReporter.logOutput(result);
1505         StringAppendF(&result, "Skia's Protected Wrapped Objects:\n");
1506         gpuProtectedReporter.logOutput(result, true);
1507 
1508         StringAppendF(&result, "\n");
1509         StringAppendF(&result, "RenderEngine runtime effects: %zu\n", mRuntimeEffects.size());
1510         for (const auto& [linearEffect, unused] : mRuntimeEffects) {
1511             StringAppendF(&result, "- inputDataspace: %s\n",
1512                           dataspaceDetails(
1513                                   static_cast<android_dataspace>(linearEffect.inputDataspace))
1514                                   .c_str());
1515             StringAppendF(&result, "- outputDataspace: %s\n",
1516                           dataspaceDetails(
1517                                   static_cast<android_dataspace>(linearEffect.outputDataspace))
1518                                   .c_str());
1519             StringAppendF(&result, "undoPremultipliedAlpha: %s\n",
1520                           linearEffect.undoPremultipliedAlpha ? "true" : "false");
1521         }
1522     }
1523     StringAppendF(&result, "\n");
1524 }
1525 
1526 } // namespace skia
1527 } // namespace renderengine
1528 } // namespace android
1529