• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::unique_fd fenceFd)427 bool SkiaGLRenderEngine::waitFence(base::unique_fd fenceFd) {
428     if (!gl::GLExtensions::getInstance().hasNativeFenceSync() ||
429         !gl::GLExtensions::getInstance().hasWaitSync()) {
430         return false;
431     }
432 
433     // release the fd and transfer the ownership to EGLSync
434     EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), EGL_NONE};
435     EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
436     if (sync == EGL_NO_SYNC_KHR) {
437         ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
438         return false;
439     }
440 
441     // XXX: The spec draft is inconsistent as to whether this should return an
442     // EGLint or void.  Ignore the return value for now, as it's not strictly
443     // needed.
444     eglWaitSyncKHR(mEGLDisplay, sync, 0);
445     EGLint error = eglGetError();
446     eglDestroySyncKHR(mEGLDisplay, sync);
447     if (error != EGL_SUCCESS) {
448         ALOGE("failed to wait for EGL native fence sync: %#x", error);
449         return false;
450     }
451 
452     return true;
453 }
454 
toDegrees(uint32_t transform)455 static float toDegrees(uint32_t transform) {
456     switch (transform) {
457         case ui::Transform::ROT_90:
458             return 90.0;
459         case ui::Transform::ROT_180:
460             return 180.0;
461         case ui::Transform::ROT_270:
462             return 270.0;
463         default:
464             return 0.0;
465     }
466 }
467 
toSkColorMatrix(const mat4 & matrix)468 static SkColorMatrix toSkColorMatrix(const mat4& matrix) {
469     return SkColorMatrix(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], 0, matrix[0][1],
470                          matrix[1][1], matrix[2][1], matrix[3][1], 0, matrix[0][2], matrix[1][2],
471                          matrix[2][2], matrix[3][2], 0, matrix[0][3], matrix[1][3], matrix[2][3],
472                          matrix[3][3], 0);
473 }
474 
needsToneMapping(ui::Dataspace sourceDataspace,ui::Dataspace destinationDataspace)475 static bool needsToneMapping(ui::Dataspace sourceDataspace, ui::Dataspace destinationDataspace) {
476     int64_t sourceTransfer = sourceDataspace & HAL_DATASPACE_TRANSFER_MASK;
477     int64_t destTransfer = destinationDataspace & HAL_DATASPACE_TRANSFER_MASK;
478 
479     // Treat unsupported dataspaces as srgb
480     if (destTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
481         destTransfer != HAL_DATASPACE_TRANSFER_HLG &&
482         destTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
483         destTransfer = HAL_DATASPACE_TRANSFER_SRGB;
484     }
485 
486     if (sourceTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
487         sourceTransfer != HAL_DATASPACE_TRANSFER_HLG &&
488         sourceTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
489         sourceTransfer = HAL_DATASPACE_TRANSFER_SRGB;
490     }
491 
492     const bool isSourceLinear = sourceTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
493     const bool isSourceSRGB = sourceTransfer == HAL_DATASPACE_TRANSFER_SRGB;
494     const bool isDestLinear = destTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
495     const bool isDestSRGB = destTransfer == HAL_DATASPACE_TRANSFER_SRGB;
496 
497     return !(isSourceLinear && isDestSRGB) && !(isSourceSRGB && isDestLinear) &&
498             sourceTransfer != destTransfer;
499 }
500 
mapExternalTextureBuffer(const sp<GraphicBuffer> & buffer,bool isRenderable)501 void SkiaGLRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
502                                                   bool isRenderable) {
503     // Only run this if RE is running on its own thread. This way the access to GL
504     // operations is guaranteed to be happening on the same thread.
505     if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) {
506         return;
507     }
508     // We currently don't attempt to map a buffer if the buffer contains protected content
509     // because GPU resources for protected buffers is much more limited.
510     const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
511     if (isProtectedBuffer) {
512         return;
513     }
514     ATRACE_CALL();
515 
516     // If we were to support caching protected buffers then we will need to switch the
517     // currently bound context if we are not already using the protected context (and subsequently
518     // switch back after the buffer is cached).  However, for non-protected content we can bind
519     // the texture in either GL context because they are initialized with the same share_context
520     // which allows the texture state to be shared between them.
521     auto grContext = getActiveGrContext();
522     auto& cache = mTextureCache;
523 
524     std::lock_guard<std::mutex> lock(mRenderingMutex);
525     mGraphicBufferExternalRefs[buffer->getId()]++;
526 
527     if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) {
528         std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
529                 std::make_shared<AutoBackendTexture::LocalRef>(grContext,
530                                                                buffer->toAHardwareBuffer(),
531                                                                isRenderable, mTextureCleanupMgr);
532         cache.insert({buffer->getId(), imageTextureRef});
533     }
534 }
535 
unmapExternalTextureBuffer(const sp<GraphicBuffer> & buffer)536 void SkiaGLRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
537     ATRACE_CALL();
538     std::lock_guard<std::mutex> lock(mRenderingMutex);
539     if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId());
540         iter != mGraphicBufferExternalRefs.end()) {
541         if (iter->second == 0) {
542             ALOGW("Attempted to unmap GraphicBuffer <id: %" PRId64
543                   "> from RenderEngine texture, but the "
544                   "ref count was already zero!",
545                   buffer->getId());
546             mGraphicBufferExternalRefs.erase(buffer->getId());
547             return;
548         }
549 
550         iter->second--;
551 
552         // Swap contexts if needed prior to deleting this buffer
553         // See Issue 1 of
554         // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_protected_content.txt: even
555         // when a protected context and an unprotected context are part of the same share group,
556         // protected surfaces may not be accessed by an unprotected context, implying that protected
557         // surfaces may only be freed when a protected context is active.
558         const bool inProtected = mInProtectedContext;
559         useProtectedContext(buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
560 
561         if (iter->second == 0) {
562             mTextureCache.erase(buffer->getId());
563             mGraphicBufferExternalRefs.erase(buffer->getId());
564         }
565 
566         // Swap back to the previous context so that cached values of isProtected in SurfaceFlinger
567         // are up-to-date.
568         if (inProtected != mInProtectedContext) {
569             useProtectedContext(inProtected);
570         }
571     }
572 }
573 
canSkipPostRenderCleanup() const574 bool SkiaGLRenderEngine::canSkipPostRenderCleanup() const {
575     std::lock_guard<std::mutex> lock(mRenderingMutex);
576     return mTextureCleanupMgr.isEmpty();
577 }
578 
cleanupPostRender()579 void SkiaGLRenderEngine::cleanupPostRender() {
580     ATRACE_CALL();
581     std::lock_guard<std::mutex> lock(mRenderingMutex);
582     mTextureCleanupMgr.cleanup();
583 }
584 
585 // Helper class intended to be used on the stack to ensure that texture cleanup
586 // is deferred until after this class goes out of scope.
587 class DeferTextureCleanup final {
588 public:
DeferTextureCleanup(AutoBackendTexture::CleanupManager & mgr)589     DeferTextureCleanup(AutoBackendTexture::CleanupManager& mgr) : mMgr(mgr) {
590         mMgr.setDeferredStatus(true);
591     }
~DeferTextureCleanup()592     ~DeferTextureCleanup() { mMgr.setDeferredStatus(false); }
593 
594 private:
595     DISALLOW_COPY_AND_ASSIGN(DeferTextureCleanup);
596     AutoBackendTexture::CleanupManager& mMgr;
597 };
598 
createRuntimeEffectShader(sk_sp<SkShader> shader,const LayerSettings * layer,const DisplaySettings & display,bool undoPremultipliedAlpha,bool requiresLinearEffect)599 sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
600         sk_sp<SkShader> shader,
601         const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha,
602         bool requiresLinearEffect) {
603     const auto stretchEffect = layer->stretchEffect;
604     // The given surface will be stretched by HWUI via matrix transformation
605     // which gets similar results for most surfaces
606     // Determine later on if we need to leverage the stertch shader within
607     // surface flinger
608     if (stretchEffect.hasEffect()) {
609         const auto targetBuffer = layer->source.buffer.buffer;
610         const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
611         if (graphicBuffer && shader) {
612             shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
613         }
614     }
615 
616     if (requiresLinearEffect) {
617         const ui::Dataspace inputDataspace =
618                 mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::V0_SRGB_LINEAR;
619         const ui::Dataspace outputDataspace =
620                 mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
621 
622         LinearEffect effect = LinearEffect{.inputDataspace = inputDataspace,
623                                            .outputDataspace = outputDataspace,
624                                            .undoPremultipliedAlpha = undoPremultipliedAlpha};
625 
626         auto effectIter = mRuntimeEffects.find(effect);
627         sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
628         if (effectIter == mRuntimeEffects.end()) {
629             runtimeEffect = buildRuntimeEffect(effect);
630             mRuntimeEffects.insert({effect, runtimeEffect});
631         } else {
632             runtimeEffect = effectIter->second;
633         }
634         float maxLuminance = layer->source.buffer.maxLuminanceNits;
635         // If the buffer doesn't have a max luminance, treat it as SDR & use the display's SDR
636         // white point
637         if (maxLuminance <= 0.f) {
638             maxLuminance = display.sdrWhitePointNits;
639         }
640         return createLinearEffectShader(shader, effect, runtimeEffect, layer->colorTransform,
641                                         display.maxLuminance, maxLuminance);
642     }
643     return shader;
644 }
645 
initCanvas(SkCanvas * canvas,const DisplaySettings & display)646 void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
647     if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
648         // Record display settings when capture is running.
649         std::stringstream displaySettings;
650         PrintTo(display, &displaySettings);
651         // Store the DisplaySettings in additional information.
652         canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings",
653                                SkData::MakeWithCString(displaySettings.str().c_str()));
654     }
655 
656     // Before doing any drawing, let's make sure that we'll start at the origin of the display.
657     // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual
658     // displays might have different scaling when compared to the physical screen.
659 
660     canvas->clipRect(getSkRect(display.physicalDisplay));
661     canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top);
662 
663     const auto clipWidth = display.clip.width();
664     const auto clipHeight = display.clip.height();
665     auto rotatedClipWidth = clipWidth;
666     auto rotatedClipHeight = clipHeight;
667     // Scale is contingent on the rotation result.
668     if (display.orientation & ui::Transform::ROT_90) {
669         std::swap(rotatedClipWidth, rotatedClipHeight);
670     }
671     const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) /
672             static_cast<SkScalar>(rotatedClipWidth);
673     const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) /
674             static_cast<SkScalar>(rotatedClipHeight);
675     canvas->scale(scaleX, scaleY);
676 
677     // Canvas rotation is done by centering the clip window at the origin, rotating, translating
678     // back so that the top left corner of the clip is at (0, 0).
679     canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2);
680     canvas->rotate(toDegrees(display.orientation));
681     canvas->translate(-clipWidth / 2, -clipHeight / 2);
682     canvas->translate(-display.clip.left, -display.clip.top);
683 }
684 
685 class AutoSaveRestore {
686 public:
AutoSaveRestore(SkCanvas * canvas)687     AutoSaveRestore(SkCanvas* canvas) : mCanvas(canvas) { mSaveCount = canvas->save(); }
~AutoSaveRestore()688     ~AutoSaveRestore() { restore(); }
replace(SkCanvas * canvas)689     void replace(SkCanvas* canvas) {
690         mCanvas = canvas;
691         mSaveCount = canvas->save();
692     }
restore()693     void restore() {
694         if (mCanvas) {
695             mCanvas->restoreToCount(mSaveCount);
696             mCanvas = nullptr;
697         }
698     }
699 
700 private:
701     SkCanvas* mCanvas;
702     int mSaveCount;
703 };
704 
getBlurRRect(const BlurRegion & region)705 static SkRRect getBlurRRect(const BlurRegion& region) {
706     const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom);
707     const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL),
708                                SkVector::Make(region.cornerRadiusTR, region.cornerRadiusTR),
709                                SkVector::Make(region.cornerRadiusBR, region.cornerRadiusBR),
710                                SkVector::Make(region.cornerRadiusBL, region.cornerRadiusBL)};
711     SkRRect roundedRect;
712     roundedRect.setRectRadii(rect, radii);
713     return roundedRect;
714 }
715 
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)716 status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
717                                         const std::vector<const LayerSettings*>& layers,
718                                         const std::shared_ptr<ExternalTexture>& buffer,
719                                         const bool /*useFramebufferCache*/,
720                                         base::unique_fd&& bufferFence, base::unique_fd* drawFence) {
721     ATRACE_NAME("SkiaGL::drawLayers");
722 
723     std::lock_guard<std::mutex> lock(mRenderingMutex);
724     if (layers.empty()) {
725         ALOGV("Drawing empty layer stack");
726         return NO_ERROR;
727     }
728 
729     if (bufferFence.get() >= 0) {
730         // Duplicate the fence for passing to waitFence.
731         base::unique_fd bufferFenceDup(dup(bufferFence.get()));
732         if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) {
733             ATRACE_NAME("Waiting before draw");
734             sync_wait(bufferFence.get(), -1);
735         }
736     }
737     if (buffer == nullptr) {
738         ALOGE("No output buffer provided. Aborting GPU composition.");
739         return BAD_VALUE;
740     }
741 
742     validateOutputBufferUsage(buffer->getBuffer());
743 
744     auto grContext = getActiveGrContext();
745     auto& cache = mTextureCache;
746 
747     // any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called
748     DeferTextureCleanup dtc(mTextureCleanupMgr);
749 
750     std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef;
751     if (const auto& it = cache.find(buffer->getBuffer()->getId()); it != cache.end()) {
752         surfaceTextureRef = it->second;
753     } else {
754         surfaceTextureRef =
755                 std::make_shared<AutoBackendTexture::LocalRef>(grContext,
756                                                                buffer->getBuffer()
757                                                                        ->toAHardwareBuffer(),
758                                                                true, mTextureCleanupMgr);
759     }
760 
761     const ui::Dataspace dstDataspace =
762             mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
763     sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext);
764 
765     SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
766     if (dstCanvas == nullptr) {
767         ALOGE("Cannot acquire canvas from Skia.");
768         return BAD_VALUE;
769     }
770 
771     // setup color filter if necessary
772     sk_sp<SkColorFilter> displayColorTransform;
773     if (display.colorTransform != mat4()) {
774         displayColorTransform = SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform));
775     }
776     const bool ctModifiesAlpha =
777             displayColorTransform && !displayColorTransform->isAlphaUnchanged();
778 
779     // Find if any layers have requested blur, we'll use that info to decide when to render to an
780     // offscreen buffer and when to render to the native buffer.
781     sk_sp<SkSurface> activeSurface(dstSurface);
782     SkCanvas* canvas = dstCanvas;
783     SkiaCapture::OffscreenState offscreenCaptureState;
784     const LayerSettings* blurCompositionLayer = nullptr;
785     if (mBlurFilter) {
786         bool requiresCompositionLayer = false;
787         for (const auto& layer : layers) {
788             // if the layer doesn't have blur or it is not visible then continue
789             if (!layerHasBlur(layer, ctModifiesAlpha)) {
790                 continue;
791             }
792             if (layer->backgroundBlurRadius > 0 &&
793                 layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius) {
794                 requiresCompositionLayer = true;
795             }
796             for (auto region : layer->blurRegions) {
797                 if (region.blurRadius < BlurFilter::kMaxCrossFadeRadius) {
798                     requiresCompositionLayer = true;
799                 }
800             }
801             if (requiresCompositionLayer) {
802                 activeSurface = dstSurface->makeSurface(dstSurface->imageInfo());
803                 canvas = mCapture->tryOffscreenCapture(activeSurface.get(), &offscreenCaptureState);
804                 blurCompositionLayer = layer;
805                 break;
806             }
807         }
808     }
809 
810     AutoSaveRestore surfaceAutoSaveRestore(canvas);
811     // Clear the entire canvas with a transparent black to prevent ghost images.
812     canvas->clear(SK_ColorTRANSPARENT);
813     initCanvas(canvas, display);
814 
815     // TODO: clearRegion was required for SurfaceView when a buffer is not yet available but the
816     // view is still on-screen. The clear region could be re-specified as a black color layer,
817     // however.
818     if (!display.clearRegion.isEmpty()) {
819         ATRACE_NAME("ClearRegion");
820         size_t numRects = 0;
821         Rect const* rects = display.clearRegion.getArray(&numRects);
822         SkIRect skRects[numRects];
823         for (int i = 0; i < numRects; ++i) {
824             skRects[i] =
825                     SkIRect::MakeLTRB(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
826         }
827         SkRegion clearRegion;
828         SkPaint paint;
829         sk_sp<SkShader> shader =
830                 SkShaders::Color(SkColor4f{.fR = 0., .fG = 0., .fB = 0., .fA = 1.0},
831                                  toSkColorSpace(dstDataspace));
832         paint.setShader(shader);
833         clearRegion.setRects(skRects, numRects);
834         canvas->drawRegion(clearRegion, paint);
835     }
836 
837     for (const auto& layer : layers) {
838         ATRACE_FORMAT("DrawLayer: %s", layer->name.c_str());
839 
840         if (kPrintLayerSettings) {
841             std::stringstream ls;
842             PrintTo(*layer, &ls);
843             auto debugs = ls.str();
844             int pos = 0;
845             while (pos < debugs.size()) {
846                 ALOGD("cache_debug %s", debugs.substr(pos, 1000).c_str());
847                 pos += 1000;
848             }
849         }
850 
851         sk_sp<SkImage> blurInput;
852         if (blurCompositionLayer == layer) {
853             LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface);
854             LOG_ALWAYS_FATAL_IF(canvas == dstCanvas);
855 
856             // save a snapshot of the activeSurface to use as input to the blur shaders
857             blurInput = activeSurface->makeImageSnapshot();
858 
859             // TODO we could skip this step if we know the blur will cover the entire image
860             //  blit the offscreen framebuffer into the destination AHB
861             SkPaint paint;
862             paint.setBlendMode(SkBlendMode::kSrc);
863             if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
864                 uint64_t id = mCapture->endOffscreenCapture(&offscreenCaptureState);
865                 dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()),
866                                           String8::format("SurfaceID|%" PRId64, id).c_str(),
867                                           nullptr);
868                 dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint);
869             } else {
870                 activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint);
871             }
872 
873             // assign dstCanvas to canvas and ensure that the canvas state is up to date
874             canvas = dstCanvas;
875             surfaceAutoSaveRestore.replace(canvas);
876             initCanvas(canvas, display);
877 
878             LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getSaveCount() !=
879                                 dstSurface->getCanvas()->getSaveCount());
880             LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getTotalMatrix() !=
881                                 dstSurface->getCanvas()->getTotalMatrix());
882 
883             // assign dstSurface to activeSurface
884             activeSurface = dstSurface;
885         }
886 
887         SkAutoCanvasRestore layerAutoSaveRestore(canvas, true);
888         if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
889             // Record the name of the layer if the capture is running.
890             std::stringstream layerSettings;
891             PrintTo(*layer, &layerSettings);
892             // Store the LayerSettings in additional information.
893             canvas->drawAnnotation(SkRect::MakeEmpty(), layer->name.c_str(),
894                                    SkData::MakeWithCString(layerSettings.str().c_str()));
895         }
896         // Layers have a local transform that should be applied to them
897         canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());
898 
899         const auto [bounds, roundRectClip] =
900                 getBoundsAndClip(layer->geometry.boundaries, layer->geometry.roundedCornersCrop,
901                                  layer->geometry.roundedCornersRadius);
902         if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) {
903             std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
904 
905             // if multiple layers have blur, then we need to take a snapshot now because
906             // only the lowest layer will have blurImage populated earlier
907             if (!blurInput) {
908                 blurInput = activeSurface->makeImageSnapshot();
909             }
910             // rect to be blurred in the coordinate space of blurInput
911             const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect());
912 
913             // if the clip needs to be applied then apply it now and make sure
914             // it is restored before we attempt to draw any shadows.
915             SkAutoCanvasRestore acr(canvas, true);
916             if (!roundRectClip.isEmpty()) {
917                 canvas->clipRRect(roundRectClip, true);
918             }
919 
920             // TODO(b/182216890): Filter out empty layers earlier
921             if (blurRect.width() > 0 && blurRect.height() > 0) {
922                 if (layer->backgroundBlurRadius > 0) {
923                     ATRACE_NAME("BackgroundBlur");
924                     auto blurredImage =
925                             mBlurFilter->generate(grContext, layer->backgroundBlurRadius, blurInput,
926                                                   blurRect);
927 
928                     cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
929 
930                     mBlurFilter->drawBlurRegion(canvas, bounds, layer->backgroundBlurRadius, 1.0f,
931                                                 blurRect, blurredImage, blurInput);
932                 }
933 
934                 canvas->concat(getSkM44(layer->blurRegionTransform).asM33());
935                 for (auto region : layer->blurRegions) {
936                     if (cachedBlurs[region.blurRadius] == nullptr) {
937                         ATRACE_NAME("BlurRegion");
938                         cachedBlurs[region.blurRadius] =
939                                 mBlurFilter->generate(grContext, region.blurRadius, blurInput,
940                                                       blurRect);
941                     }
942 
943                     mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius,
944                                                 region.alpha, blurRect,
945                                                 cachedBlurs[region.blurRadius], blurInput);
946                 }
947             }
948         }
949 
950         if (layer->shadow.length > 0) {
951             // This would require a new parameter/flag to SkShadowUtils::DrawShadow
952             LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow");
953 
954             SkRRect shadowBounds, shadowClip;
955             if (layer->geometry.boundaries == layer->shadow.boundaries) {
956                 shadowBounds = bounds;
957                 shadowClip = roundRectClip;
958             } else {
959                 std::tie(shadowBounds, shadowClip) =
960                         getBoundsAndClip(layer->shadow.boundaries,
961                                          layer->geometry.roundedCornersCrop,
962                                          layer->geometry.roundedCornersRadius);
963             }
964 
965             // Technically, if bounds is a rect and roundRectClip is not empty,
966             // it means that the bounds and roundedCornersCrop were different
967             // enough that we should intersect them to find the proper shadow.
968             // In practice, this often happens when the two rectangles appear to
969             // not match due to rounding errors. Draw the rounded version, which
970             // looks more like the intent.
971             const auto& rrect =
972                     shadowBounds.isRect() && !shadowClip.isEmpty() ? shadowClip : shadowBounds;
973             drawShadow(canvas, rrect, layer->shadow);
974         }
975 
976         const bool requiresLinearEffect = layer->colorTransform != mat4() ||
977                 (mUseColorManagement &&
978                  needsToneMapping(layer->sourceDataspace, display.outputDataspace)) ||
979                 (display.sdrWhitePointNits > 0.f &&
980                  display.sdrWhitePointNits != display.maxLuminance);
981 
982         // quick abort from drawing the remaining portion of the layer
983         if (layer->skipContentDraw ||
984             (layer->alpha == 0 && !requiresLinearEffect && !layer->disableBlending &&
985              (!displayColorTransform || displayColorTransform->isAlphaUnchanged()))) {
986             continue;
987         }
988 
989         // If we need to map to linear space or color management is disabled, then mark the source
990         // image with the same colorspace as the destination surface so that Skia's color
991         // management is a no-op.
992         const ui::Dataspace layerDataspace = (!mUseColorManagement || requiresLinearEffect)
993                 ? dstDataspace
994                 : layer->sourceDataspace;
995 
996         SkPaint paint;
997         if (layer->source.buffer.buffer) {
998             ATRACE_NAME("DrawImage");
999             validateInputBufferUsage(layer->source.buffer.buffer->getBuffer());
1000             const auto& item = layer->source.buffer;
1001             std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr;
1002 
1003             if (const auto& iter = cache.find(item.buffer->getBuffer()->getId());
1004                 iter != cache.end()) {
1005                 imageTextureRef = iter->second;
1006             } else {
1007                 // If we didn't find the image in the cache, then create a local ref but don't cache
1008                 // it. If we're using skia, we're guaranteed to run on a dedicated GPU thread so if
1009                 // we didn't find anything in the cache then we intentionally did not cache this
1010                 // buffer's resources.
1011                 imageTextureRef = std::make_shared<
1012                         AutoBackendTexture::LocalRef>(grContext,
1013                                                       item.buffer->getBuffer()->toAHardwareBuffer(),
1014                                                       false, mTextureCleanupMgr);
1015             }
1016 
1017             // isOpaque means we need to ignore the alpha in the image,
1018             // replacing it with the alpha specified by the LayerSettings. See
1019             // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean)
1020             // The proper way to do this is to use an SkColorType that ignores
1021             // alpha, like kRGB_888x_SkColorType, and that is used if the
1022             // incoming image is kRGBA_8888_SkColorType. However, the incoming
1023             // image may be kRGBA_F16_SkColorType, for which there is no RGBX
1024             // SkColorType, or kRGBA_1010102_SkColorType, for which we have
1025             // kRGB_101010x_SkColorType, but it is not yet supported as a source
1026             // on the GPU. (Adding both is tracked in skbug.com/12048.) In the
1027             // meantime, we'll use a workaround that works unless we need to do
1028             // any color conversion. The workaround requires that we pretend the
1029             // image is already premultiplied, so that we do not premultiply it
1030             // before applying SkBlendMode::kPlus.
1031             const bool useIsOpaqueWorkaround = item.isOpaque &&
1032                     (imageTextureRef->colorType() == kRGBA_1010102_SkColorType ||
1033                      imageTextureRef->colorType() == kRGBA_F16_SkColorType);
1034             const auto alphaType = useIsOpaqueWorkaround ? kPremul_SkAlphaType
1035                     : item.isOpaque                      ? kOpaque_SkAlphaType
1036                     : item.usePremultipliedAlpha         ? kPremul_SkAlphaType
1037                                                          : kUnpremul_SkAlphaType;
1038             sk_sp<SkImage> image = imageTextureRef->makeImage(layerDataspace, alphaType, grContext);
1039 
1040             auto texMatrix = getSkM44(item.textureTransform).asM33();
1041             // textureTansform was intended to be passed directly into a shader, so when
1042             // building the total matrix with the textureTransform we need to first
1043             // normalize it, then apply the textureTransform, then scale back up.
1044             texMatrix.preScale(1.0f / bounds.width(), 1.0f / bounds.height());
1045             texMatrix.postScale(image->width(), image->height());
1046 
1047             SkMatrix matrix;
1048             if (!texMatrix.invert(&matrix)) {
1049                 matrix = texMatrix;
1050             }
1051             // The shader does not respect the translation, so we add it to the texture
1052             // transform for the SkImage. This will make sure that the correct layer contents
1053             // are drawn in the correct part of the screen.
1054             matrix.postTranslate(bounds.rect().fLeft, bounds.rect().fTop);
1055 
1056             sk_sp<SkShader> shader;
1057 
1058             if (layer->source.buffer.useTextureFiltering) {
1059                 shader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
1060                                            SkSamplingOptions(
1061                                                    {SkFilterMode::kLinear, SkMipmapMode::kNone}),
1062                                            &matrix);
1063             } else {
1064                 shader = image->makeShader(SkSamplingOptions(), matrix);
1065             }
1066 
1067             if (useIsOpaqueWorkaround) {
1068                 shader = SkShaders::Blend(SkBlendMode::kPlus, shader,
1069                                           SkShaders::Color(SkColors::kBlack,
1070                                                            toSkColorSpace(layerDataspace)));
1071             }
1072 
1073             paint.setShader(createRuntimeEffectShader(shader, layer, display,
1074                                                       !item.isOpaque && item.usePremultipliedAlpha,
1075                                                       requiresLinearEffect));
1076             paint.setAlphaf(layer->alpha);
1077         } else {
1078             ATRACE_NAME("DrawColor");
1079             const auto color = layer->source.solidColor;
1080             sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r,
1081                                                                 .fG = color.g,
1082                                                                 .fB = color.b,
1083                                                                 .fA = layer->alpha},
1084                                                       toSkColorSpace(layerDataspace));
1085             paint.setShader(createRuntimeEffectShader(shader, layer, display,
1086                                                       /* undoPremultipliedAlpha */ false,
1087                                                       requiresLinearEffect));
1088         }
1089 
1090         if (layer->disableBlending) {
1091             paint.setBlendMode(SkBlendMode::kSrc);
1092         }
1093 
1094         paint.setColorFilter(displayColorTransform);
1095 
1096         if (!roundRectClip.isEmpty()) {
1097             canvas->clipRRect(roundRectClip, true);
1098         }
1099 
1100         if (!bounds.isRect()) {
1101             paint.setAntiAlias(true);
1102             canvas->drawRRect(bounds, paint);
1103         } else {
1104             canvas->drawRect(bounds.rect(), paint);
1105         }
1106         if (kFlushAfterEveryLayer) {
1107             ATRACE_NAME("flush surface");
1108             activeSurface->flush();
1109         }
1110     }
1111     surfaceAutoSaveRestore.restore();
1112     mCapture->endCapture();
1113     {
1114         ATRACE_NAME("flush surface");
1115         LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
1116         activeSurface->flush();
1117     }
1118 
1119     if (drawFence != nullptr) {
1120         *drawFence = flush();
1121     }
1122 
1123     // If flush failed or we don't support native fences, we need to force the
1124     // gl command stream to be executed.
1125     bool requireSync = drawFence == nullptr || drawFence->get() < 0;
1126     if (requireSync) {
1127         ATRACE_BEGIN("Submit(sync=true)");
1128     } else {
1129         ATRACE_BEGIN("Submit(sync=false)");
1130     }
1131     bool success = grContext->submit(requireSync);
1132     ATRACE_END();
1133     if (!success) {
1134         ALOGE("Failed to flush RenderEngine commands");
1135         // Chances are, something illegal happened (either the caller passed
1136         // us bad parameters, or we messed up our shader generation).
1137         return INVALID_OPERATION;
1138     }
1139 
1140     // checkErrors();
1141     return NO_ERROR;
1142 }
1143 
getSkRect(const FloatRect & rect)1144 inline SkRect SkiaGLRenderEngine::getSkRect(const FloatRect& rect) {
1145     return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
1146 }
1147 
getSkRect(const Rect & rect)1148 inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) {
1149     return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
1150 }
1151 
getBoundsAndClip(const FloatRect & boundsRect,const FloatRect & cropRect,const float cornerRadius)1152 inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
1153                                                                         const FloatRect& cropRect,
1154                                                                         const float cornerRadius) {
1155     const SkRect bounds = getSkRect(boundsRect);
1156     const SkRect crop = getSkRect(cropRect);
1157 
1158     SkRRect clip;
1159     if (cornerRadius > 0) {
1160         // it the crop and the bounds are equivalent or there is no crop then we don't need a clip
1161         if (bounds == crop || crop.isEmpty()) {
1162             return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip};
1163         }
1164 
1165         // This makes an effort to speed up common, simple bounds + clip combinations by
1166         // converting them to a single RRect draw. It is possible there are other cases
1167         // that can be converted.
1168         if (crop.contains(bounds)) {
1169             bool intersectionIsRoundRect = true;
1170             // check each cropped corner to ensure that it exactly matches the crop or is full
1171             SkVector radii[4];
1172 
1173             const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
1174 
1175             const bool leftEqual = bounds.fLeft == crop.fLeft;
1176             const bool topEqual = bounds.fTop == crop.fTop;
1177             const bool rightEqual = bounds.fRight == crop.fRight;
1178             const bool bottomEqual = bounds.fBottom == crop.fBottom;
1179 
1180             // compute the UpperLeft corner radius
1181             if (leftEqual && topEqual) {
1182                 radii[0].set(cornerRadius, cornerRadius);
1183             } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
1184                        (topEqual && bounds.fLeft >= insetCrop.fLeft) ||
1185                        insetCrop.contains(bounds.fLeft, bounds.fTop)) {
1186                 radii[0].set(0, 0);
1187             } else {
1188                 intersectionIsRoundRect = false;
1189             }
1190             // compute the UpperRight corner radius
1191             if (rightEqual && topEqual) {
1192                 radii[1].set(cornerRadius, cornerRadius);
1193             } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
1194                        (topEqual && bounds.fRight <= insetCrop.fRight) ||
1195                        insetCrop.contains(bounds.fRight, bounds.fTop)) {
1196                 radii[1].set(0, 0);
1197             } else {
1198                 intersectionIsRoundRect = false;
1199             }
1200             // compute the BottomRight corner radius
1201             if (rightEqual && bottomEqual) {
1202                 radii[2].set(cornerRadius, cornerRadius);
1203             } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
1204                        (bottomEqual && bounds.fRight <= insetCrop.fRight) ||
1205                        insetCrop.contains(bounds.fRight, bounds.fBottom)) {
1206                 radii[2].set(0, 0);
1207             } else {
1208                 intersectionIsRoundRect = false;
1209             }
1210             // compute the BottomLeft corner radius
1211             if (leftEqual && bottomEqual) {
1212                 radii[3].set(cornerRadius, cornerRadius);
1213             } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
1214                        (bottomEqual && bounds.fLeft >= insetCrop.fLeft) ||
1215                        insetCrop.contains(bounds.fLeft, bounds.fBottom)) {
1216                 radii[3].set(0, 0);
1217             } else {
1218                 intersectionIsRoundRect = false;
1219             }
1220 
1221             if (intersectionIsRoundRect) {
1222                 SkRRect intersectionBounds;
1223                 intersectionBounds.setRectRadii(bounds, radii);
1224                 return {intersectionBounds, clip};
1225             }
1226         }
1227 
1228         // we didn't it any of our fast paths so set the clip to the cropRect
1229         clip.setRectXY(crop, cornerRadius, cornerRadius);
1230     }
1231 
1232     // if we hit this point then we either don't have rounded corners or we are going to rely
1233     // on the clip to round the corners for us
1234     return {SkRRect::MakeRect(bounds), clip};
1235 }
1236 
layerHasBlur(const LayerSettings * layer,bool colorTransformModifiesAlpha)1237 inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer,
1238                                              bool colorTransformModifiesAlpha) {
1239     if (layer->backgroundBlurRadius > 0 || layer->blurRegions.size()) {
1240         // return false if the content is opaque and would therefore occlude the blur
1241         const bool opaqueContent = !layer->source.buffer.buffer || layer->source.buffer.isOpaque;
1242         const bool opaqueAlpha = layer->alpha == 1.0f && !colorTransformModifiesAlpha;
1243         return layer->skipContentDraw || !(opaqueContent && opaqueAlpha);
1244     }
1245     return false;
1246 }
1247 
getSkColor(const vec4 & color)1248 inline SkColor SkiaGLRenderEngine::getSkColor(const vec4& color) {
1249     return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255);
1250 }
1251 
getSkM44(const mat4 & matrix)1252 inline SkM44 SkiaGLRenderEngine::getSkM44(const mat4& matrix) {
1253     return SkM44(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0],
1254                  matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1],
1255                  matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2],
1256                  matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]);
1257 }
1258 
getSkPoint3(const vec3 & vector)1259 inline SkPoint3 SkiaGLRenderEngine::getSkPoint3(const vec3& vector) {
1260     return SkPoint3::Make(vector.x, vector.y, vector.z);
1261 }
1262 
getMaxTextureSize() const1263 size_t SkiaGLRenderEngine::getMaxTextureSize() const {
1264     return mGrContext->maxTextureSize();
1265 }
1266 
getMaxViewportDims() const1267 size_t SkiaGLRenderEngine::getMaxViewportDims() const {
1268     return mGrContext->maxRenderTargetSize();
1269 }
1270 
drawShadow(SkCanvas * canvas,const SkRRect & casterRRect,const ShadowSettings & settings)1271 void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
1272                                     const ShadowSettings& settings) {
1273     ATRACE_CALL();
1274     const float casterZ = settings.length / 2.0f;
1275     const auto flags =
1276             settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag;
1277 
1278     SkShadowUtils::DrawShadow(canvas, SkPath::RRect(casterRRect), SkPoint3::Make(0, 0, casterZ),
1279                               getSkPoint3(settings.lightPos), settings.lightRadius,
1280                               getSkColor(settings.ambientColor), getSkColor(settings.spotColor),
1281                               flags);
1282 }
1283 
createEglContext(EGLDisplay display,EGLConfig config,EGLContext shareContext,std::optional<ContextPriority> contextPriority,Protection protection)1284 EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
1285                                                 EGLContext shareContext,
1286                                                 std::optional<ContextPriority> contextPriority,
1287                                                 Protection protection) {
1288     EGLint renderableType = 0;
1289     if (config == EGL_NO_CONFIG_KHR) {
1290         renderableType = EGL_OPENGL_ES3_BIT;
1291     } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
1292         LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
1293     }
1294     EGLint contextClientVersion = 0;
1295     if (renderableType & EGL_OPENGL_ES3_BIT) {
1296         contextClientVersion = 3;
1297     } else if (renderableType & EGL_OPENGL_ES2_BIT) {
1298         contextClientVersion = 2;
1299     } else if (renderableType & EGL_OPENGL_ES_BIT) {
1300         contextClientVersion = 1;
1301     } else {
1302         LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
1303     }
1304 
1305     std::vector<EGLint> contextAttributes;
1306     contextAttributes.reserve(7);
1307     contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
1308     contextAttributes.push_back(contextClientVersion);
1309     if (contextPriority) {
1310         contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
1311         switch (*contextPriority) {
1312             case ContextPriority::REALTIME:
1313                 contextAttributes.push_back(EGL_CONTEXT_PRIORITY_REALTIME_NV);
1314                 break;
1315             case ContextPriority::MEDIUM:
1316                 contextAttributes.push_back(EGL_CONTEXT_PRIORITY_MEDIUM_IMG);
1317                 break;
1318             case ContextPriority::LOW:
1319                 contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LOW_IMG);
1320                 break;
1321             case ContextPriority::HIGH:
1322             default:
1323                 contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
1324                 break;
1325         }
1326     }
1327     if (protection == Protection::PROTECTED) {
1328         contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
1329         contextAttributes.push_back(EGL_TRUE);
1330     }
1331     contextAttributes.push_back(EGL_NONE);
1332 
1333     EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data());
1334 
1335     if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) {
1336         // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus
1337         // EGL_NO_CONTEXT so that we can abort.
1338         if (config != EGL_NO_CONFIG_KHR) {
1339             return context;
1340         }
1341         // If |config| is EGL_NO_CONFIG_KHR, we speculatively try to create GLES 3 context, so we
1342         // should try to fall back to GLES 2.
1343         contextAttributes[1] = 2;
1344         context = eglCreateContext(display, config, shareContext, contextAttributes.data());
1345     }
1346 
1347     return context;
1348 }
1349 
createContextPriority(const RenderEngineCreationArgs & args)1350 std::optional<RenderEngine::ContextPriority> SkiaGLRenderEngine::createContextPriority(
1351         const RenderEngineCreationArgs& args) {
1352     if (!gl::GLExtensions::getInstance().hasContextPriority()) {
1353         return std::nullopt;
1354     }
1355 
1356     switch (args.contextPriority) {
1357         case RenderEngine::ContextPriority::REALTIME:
1358             if (gl::GLExtensions::getInstance().hasRealtimePriority()) {
1359                 return RenderEngine::ContextPriority::REALTIME;
1360             } else {
1361                 ALOGI("Realtime priority unsupported, degrading gracefully to high priority");
1362                 return RenderEngine::ContextPriority::HIGH;
1363             }
1364         case RenderEngine::ContextPriority::HIGH:
1365         case RenderEngine::ContextPriority::MEDIUM:
1366         case RenderEngine::ContextPriority::LOW:
1367             return args.contextPriority;
1368         default:
1369             return std::nullopt;
1370     }
1371 }
1372 
createPlaceholderEglPbufferSurface(EGLDisplay display,EGLConfig config,int hwcFormat,Protection protection)1373 EGLSurface SkiaGLRenderEngine::createPlaceholderEglPbufferSurface(EGLDisplay display,
1374                                                                   EGLConfig config, int hwcFormat,
1375                                                                   Protection protection) {
1376     EGLConfig placeholderConfig = config;
1377     if (placeholderConfig == EGL_NO_CONFIG_KHR) {
1378         placeholderConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
1379     }
1380     std::vector<EGLint> attributes;
1381     attributes.reserve(7);
1382     attributes.push_back(EGL_WIDTH);
1383     attributes.push_back(1);
1384     attributes.push_back(EGL_HEIGHT);
1385     attributes.push_back(1);
1386     if (protection == Protection::PROTECTED) {
1387         attributes.push_back(EGL_PROTECTED_CONTENT_EXT);
1388         attributes.push_back(EGL_TRUE);
1389     }
1390     attributes.push_back(EGL_NONE);
1391 
1392     return eglCreatePbufferSurface(display, placeholderConfig, attributes.data());
1393 }
1394 
getContextPriority()1395 int SkiaGLRenderEngine::getContextPriority() {
1396     int value;
1397     eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value);
1398     return value;
1399 }
1400 
onPrimaryDisplaySizeChanged(ui::Size size)1401 void SkiaGLRenderEngine::onPrimaryDisplaySizeChanged(ui::Size size) {
1402     // This cache multiplier was selected based on review of cache sizes relative
1403     // to the screen resolution. Looking at the worst case memory needed by blur (~1.5x),
1404     // shadows (~1x), and general data structures (e.g. vertex buffers) we selected this as a
1405     // conservative default based on that analysis.
1406     const float SURFACE_SIZE_MULTIPLIER = 3.5f * bytesPerPixel(mDefaultPixelFormat);
1407     const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER;
1408 
1409     // start by resizing the current context
1410     getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
1411 
1412     // if it is possible to switch contexts then we will resize the other context
1413     const bool originalProtectedState = mInProtectedContext;
1414     useProtectedContext(!mInProtectedContext);
1415     if (mInProtectedContext != originalProtectedState) {
1416         getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
1417         // reset back to the initial context that was active when this method was called
1418         useProtectedContext(originalProtectedState);
1419     }
1420 }
1421 
dump(std::string & result)1422 void SkiaGLRenderEngine::dump(std::string& result) {
1423     const gl::GLExtensions& extensions = gl::GLExtensions::getInstance();
1424 
1425     StringAppendF(&result, "\n ------------RE-----------------\n");
1426     StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion());
1427     StringAppendF(&result, "%s\n", extensions.getEGLExtensions());
1428     StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
1429                   extensions.getVersion());
1430     StringAppendF(&result, "%s\n", extensions.getExtensions());
1431     StringAppendF(&result, "RenderEngine supports protected context: %d\n",
1432                   supportsProtectedContent());
1433     StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext);
1434     StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n",
1435                   mSkSLCacheMonitor.shadersCachedSinceLastCall());
1436 
1437     std::vector<ResourcePair> cpuResourceMap = {
1438             {"skia/sk_resource_cache/bitmap_", "Bitmaps"},
1439             {"skia/sk_resource_cache/rrect-blur_", "Masks"},
1440             {"skia/sk_resource_cache/rects-blur_", "Masks"},
1441             {"skia/sk_resource_cache/tessellated", "Shadows"},
1442             {"skia", "Other"},
1443     };
1444     SkiaMemoryReporter cpuReporter(cpuResourceMap, false);
1445     SkGraphics::DumpMemoryStatistics(&cpuReporter);
1446     StringAppendF(&result, "Skia CPU Caches: ");
1447     cpuReporter.logTotals(result);
1448     cpuReporter.logOutput(result);
1449 
1450     {
1451         std::lock_guard<std::mutex> lock(mRenderingMutex);
1452 
1453         std::vector<ResourcePair> gpuResourceMap = {
1454                 {"texture_renderbuffer", "Texture/RenderBuffer"},
1455                 {"texture", "Texture"},
1456                 {"gr_text_blob_cache", "Text"},
1457                 {"skia", "Other"},
1458         };
1459         SkiaMemoryReporter gpuReporter(gpuResourceMap, true);
1460         mGrContext->dumpMemoryStatistics(&gpuReporter);
1461         StringAppendF(&result, "Skia's GPU Caches: ");
1462         gpuReporter.logTotals(result);
1463         gpuReporter.logOutput(result);
1464         StringAppendF(&result, "Skia's Wrapped Objects:\n");
1465         gpuReporter.logOutput(result, true);
1466 
1467         StringAppendF(&result, "RenderEngine tracked buffers: %zu\n",
1468                       mGraphicBufferExternalRefs.size());
1469         StringAppendF(&result, "Dumping buffer ids...\n");
1470         for (const auto& [id, refCounts] : mGraphicBufferExternalRefs) {
1471             StringAppendF(&result, "- 0x%" PRIx64 " - %d refs \n", id, refCounts);
1472         }
1473         StringAppendF(&result, "RenderEngine AHB/BackendTexture cache size: %zu\n",
1474                       mTextureCache.size());
1475         StringAppendF(&result, "Dumping buffer ids...\n");
1476         // TODO(178539829): It would be nice to know which layer these are coming from and what
1477         // the texture sizes are.
1478         for (const auto& [id, unused] : mTextureCache) {
1479             StringAppendF(&result, "- 0x%" PRIx64 "\n", id);
1480         }
1481         StringAppendF(&result, "\n");
1482 
1483         SkiaMemoryReporter gpuProtectedReporter(gpuResourceMap, true);
1484         if (mProtectedGrContext) {
1485             mProtectedGrContext->dumpMemoryStatistics(&gpuProtectedReporter);
1486         }
1487         StringAppendF(&result, "Skia's GPU Protected Caches: ");
1488         gpuProtectedReporter.logTotals(result);
1489         gpuProtectedReporter.logOutput(result);
1490         StringAppendF(&result, "Skia's Protected Wrapped Objects:\n");
1491         gpuProtectedReporter.logOutput(result, true);
1492 
1493         StringAppendF(&result, "\n");
1494         StringAppendF(&result, "RenderEngine runtime effects: %zu\n", mRuntimeEffects.size());
1495         for (const auto& [linearEffect, unused] : mRuntimeEffects) {
1496             StringAppendF(&result, "- inputDataspace: %s\n",
1497                           dataspaceDetails(
1498                                   static_cast<android_dataspace>(linearEffect.inputDataspace))
1499                                   .c_str());
1500             StringAppendF(&result, "- outputDataspace: %s\n",
1501                           dataspaceDetails(
1502                                   static_cast<android_dataspace>(linearEffect.outputDataspace))
1503                                   .c_str());
1504             StringAppendF(&result, "undoPremultipliedAlpha: %s\n",
1505                           linearEffect.undoPremultipliedAlpha ? "true" : "false");
1506         }
1507     }
1508     StringAppendF(&result, "\n");
1509 }
1510 
1511 } // namespace skia
1512 } // namespace renderengine
1513 } // namespace android
1514