1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "SkiaPipeline.h"
18 
19 #include <SkImageEncoder.h>
20 #include <SkImageInfo.h>
21 #include <SkImagePriv.h>
22 #include <SkMultiPictureDocument.h>
23 #include <SkOverdrawCanvas.h>
24 #include <SkOverdrawColorFilter.h>
25 #include <SkPicture.h>
26 #include <SkPictureRecorder.h>
27 #include <SkSerialProcs.h>
28 #include <SkTypeface.h>
29 #include <android-base/properties.h>
30 #include <unistd.h>
31 
32 #include <sstream>
33 
34 #include "LightingInfo.h"
35 #include "VectorDrawable.h"
36 #include "thread/CommonPool.h"
37 #include "tools/SkSharingProc.h"
38 #include "utils/String8.h"
39 #include "utils/TraceUtils.h"
40 
41 using namespace android::uirenderer::renderthread;
42 
43 namespace android {
44 namespace uirenderer {
45 namespace skiapipeline {
46 
SkiaPipeline(RenderThread & thread)47 SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) {
48     setSurfaceColorProperties(mColorMode);
49 }
50 
~SkiaPipeline()51 SkiaPipeline::~SkiaPipeline() {
52     unpinImages();
53 }
54 
onDestroyHardwareResources()55 void SkiaPipeline::onDestroyHardwareResources() {
56     unpinImages();
57     mRenderThread.cacheManager().trimStaleResources();
58 }
59 
pinImages(std::vector<SkImage * > & mutableImages)60 bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) {
61     for (SkImage* image : mutableImages) {
62         if (SkImage_pinAsTexture(image, mRenderThread.getGrContext())) {
63             mPinnedImages.emplace_back(sk_ref_sp(image));
64         } else {
65             return false;
66         }
67     }
68     return true;
69 }
70 
unpinImages()71 void SkiaPipeline::unpinImages() {
72     for (auto& image : mPinnedImages) {
73         SkImage_unpinAsTexture(image.get(), mRenderThread.getGrContext());
74     }
75     mPinnedImages.clear();
76 }
77 
renderLayers(const LightGeometry & lightGeometry,LayerUpdateQueue * layerUpdateQueue,bool opaque,const LightInfo & lightInfo)78 void SkiaPipeline::renderLayers(const LightGeometry& lightGeometry,
79                                 LayerUpdateQueue* layerUpdateQueue, bool opaque,
80                                 const LightInfo& lightInfo) {
81     LightingInfo::updateLighting(lightGeometry, lightInfo);
82     ATRACE_NAME("draw layers");
83     renderLayersImpl(*layerUpdateQueue, opaque);
84     layerUpdateQueue->clear();
85 }
86 
renderLayersImpl(const LayerUpdateQueue & layers,bool opaque)87 void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
88     sk_sp<GrContext> cachedContext;
89 
90     // Render all layers that need to be updated, in order.
91     for (size_t i = 0; i < layers.entries().size(); i++) {
92         RenderNode* layerNode = layers.entries()[i].renderNode.get();
93         // only schedule repaint if node still on layer - possible it may have been
94         // removed during a dropped frame, but layers may still remain scheduled so
95         // as not to lose info on what portion is damaged
96         if (CC_UNLIKELY(layerNode->getLayerSurface() == nullptr)) {
97             continue;
98         }
99         SkASSERT(layerNode->getLayerSurface());
100         SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList();
101         if (!displayList || displayList->isEmpty()) {
102             ALOGE("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName());
103             return;
104         }
105 
106         const Rect& layerDamage = layers.entries()[i].damage;
107 
108         SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas();
109 
110         int saveCount = layerCanvas->save();
111         SkASSERT(saveCount == 1);
112 
113         layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect());
114 
115         // TODO: put localized light center calculation and storage to a drawable related code.
116         // It does not seem right to store something localized in a global state
117         // fix here and in recordLayers
118         const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw());
119         Vector3 transformedLightCenter(savedLightCenter);
120         // map current light center into RenderNode's coordinate space
121         layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter);
122         LightingInfo::setLightCenterRaw(transformedLightCenter);
123 
124         const RenderProperties& properties = layerNode->properties();
125         const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
126         if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) {
127             return;
128         }
129 
130         ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(),
131                       bounds.height());
132 
133         layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false;
134         layerCanvas->clear(SK_ColorTRANSPARENT);
135 
136         RenderNodeDrawable root(layerNode, layerCanvas, false);
137         root.forceDraw(layerCanvas);
138         layerCanvas->restoreToCount(saveCount);
139 
140         LightingInfo::setLightCenterRaw(savedLightCenter);
141 
142         // cache the current context so that we can defer flushing it until
143         // either all the layers have been rendered or the context changes
144         GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext();
145         if (cachedContext.get() != currentContext) {
146             if (cachedContext.get()) {
147                 ATRACE_NAME("flush layers (context changed)");
148                 cachedContext->flush();
149             }
150             cachedContext.reset(SkSafeRef(currentContext));
151         }
152     }
153 
154     if (cachedContext.get()) {
155         ATRACE_NAME("flush layers");
156         cachedContext->flush();
157     }
158 }
159 
createOrUpdateLayer(RenderNode * node,const DamageAccumulator & damageAccumulator,ErrorHandler * errorHandler)160 bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
161                                        ErrorHandler* errorHandler) {
162     // compute the size of the surface (i.e. texture) to be allocated for this layer
163     const int surfaceWidth = ceilf(node->getWidth() / float(LAYER_SIZE)) * LAYER_SIZE;
164     const int surfaceHeight = ceilf(node->getHeight() / float(LAYER_SIZE)) * LAYER_SIZE;
165 
166     SkSurface* layer = node->getLayerSurface();
167     if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) {
168         SkImageInfo info;
169         info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(),
170                                  kPremul_SkAlphaType, getSurfaceColorSpace());
171         SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
172         SkASSERT(mRenderThread.getGrContext() != nullptr);
173         node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
174                                                           SkBudgeted::kYes, info, 0,
175                                                           this->getSurfaceOrigin(), &props));
176         if (node->getLayerSurface()) {
177             // update the transform in window of the layer to reset its origin wrt light source
178             // position
179             Matrix4 windowTransform;
180             damageAccumulator.computeCurrentTransform(&windowTransform);
181             node->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
182         } else {
183             String8 cachesOutput;
184             mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput,
185                                                          &mRenderThread.renderState());
186             ALOGE("%s", cachesOutput.string());
187             if (errorHandler) {
188                 std::ostringstream err;
189                 err << "Unable to create layer for " << node->getName();
190                 const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
191                 err << ", size " << info.width() << "x" << info.height() << " max size "
192                     << maxTextureSize << " color type " << (int)info.colorType() << " has context "
193                     << (int)(mRenderThread.getGrContext() != nullptr);
194                 errorHandler->onError(err.str());
195             }
196         }
197         return true;
198     }
199     return false;
200 }
201 
prepareToDraw(const RenderThread & thread,Bitmap * bitmap)202 void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
203     GrContext* context = thread.getGrContext();
204     if (context) {
205         ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
206         auto image = bitmap->makeImage();
207         if (image.get() && !bitmap->isHardware()) {
208             SkImage_pinAsTexture(image.get(), context);
209             SkImage_unpinAsTexture(image.get(), context);
210         }
211     }
212 }
213 
savePictureAsync(const sk_sp<SkData> & data,const std::string & filename)214 static void savePictureAsync(const sk_sp<SkData>& data, const std::string& filename) {
215     CommonPool::post([data, filename] {
216         if (0 == access(filename.c_str(), F_OK)) {
217             return;
218         }
219 
220         SkFILEWStream stream(filename.c_str());
221         if (stream.isValid()) {
222             stream.write(data->data(), data->size());
223             stream.flush();
224             ALOGD("SKP Captured Drawing Output (%zu bytes) for frame. %s", stream.bytesWritten(),
225                      filename.c_str());
226         }
227     });
228 }
229 
230 // Note multiple SkiaPipeline instances may be loaded if more than one app is visible.
231 // Each instance may observe the filename changing and try to record to a file of the same name.
232 // Only the first one will succeed. There is no scope available here where we could coordinate
233 // to cause this function to return true for only one of the instances.
shouldStartNewFileCapture()234 bool SkiaPipeline::shouldStartNewFileCapture() {
235     // Don't start a new file based capture if one is currently ongoing.
236     if (mCaptureMode != CaptureMode::None) { return false; }
237 
238     // A new capture is started when the filename property changes.
239     // Read the filename property.
240     std::string prop = base::GetProperty(PROPERTY_CAPTURE_SKP_FILENAME, "0");
241     // if the filename property changed to a valid value
242     if (prop[0] != '0' && mCapturedFile != prop) {
243         // remember this new filename
244         mCapturedFile = prop;
245         // and get a property indicating how many frames to capture.
246         mCaptureSequence = base::GetIntProperty(PROPERTY_CAPTURE_SKP_FRAMES, 1);
247         if (mCaptureSequence <= 0) {
248             return false;
249         } else if (mCaptureSequence == 1) {
250             mCaptureMode = CaptureMode::SingleFrameSKP;
251         } else {
252             mCaptureMode = CaptureMode::MultiFrameSKP;
253         }
254         return true;
255     }
256     return false;
257 }
258 
259 // performs the first-frame work of a multi frame SKP capture. Returns true if successful.
setupMultiFrameCapture()260 bool SkiaPipeline::setupMultiFrameCapture() {
261     ALOGD("Set up multi-frame capture, frames = %d", mCaptureSequence);
262     // We own this stream and need to hold it until close() finishes.
263     auto stream = std::make_unique<SkFILEWStream>(mCapturedFile.c_str());
264     if (stream->isValid()) {
265         mOpenMultiPicStream = std::move(stream);
266         mSerialContext.reset(new SkSharingSerialContext());
267         SkSerialProcs procs;
268         procs.fImageProc = SkSharingSerialContext::serializeImage;
269         procs.fImageCtx = mSerialContext.get();
270         procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){
271             return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
272         };
273         // SkDocuments don't take owership of the streams they write.
274         // we need to keep it until after mMultiPic.close()
275         // procs is passed as a pointer, but just as a method of having an optional default.
276         // procs doesn't need to outlive this Make call.
277         mMultiPic = SkMakeMultiPictureDocument(mOpenMultiPicStream.get(), &procs);
278         return true;
279     } else {
280         ALOGE("Could not open \"%s\" for writing.", mCapturedFile.c_str());
281         mCaptureSequence = 0;
282         mCaptureMode = CaptureMode::None;
283         return false;
284     }
285 }
286 
287 // recurse through the rendernode's children, add any nodes which are layers to the queue.
collectLayers(RenderNode * node,LayerUpdateQueue * layers)288 static void collectLayers(RenderNode* node, LayerUpdateQueue* layers) {
289     SkiaDisplayList* dl = (SkiaDisplayList*)node->getDisplayList();
290     if (dl) {
291         const auto& prop = node->properties();
292         if (node->hasLayer()) {
293             layers->enqueueLayerWithDamage(node, Rect(prop.getWidth(), prop.getHeight()));
294         }
295         // The way to recurse through rendernodes is to call this with a lambda.
296         dl->updateChildren([&](RenderNode* child) { collectLayers(child, layers); });
297     }
298 }
299 
300 // record the provided layers to the provided canvas as self-contained skpictures.
recordLayers(const LayerUpdateQueue & layers,SkCanvas * mskpCanvas)301 static void recordLayers(const LayerUpdateQueue& layers,
302     SkCanvas* mskpCanvas) {
303     const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw());
304     // Record the commands to re-draw each dirty layer into an SkPicture
305     for (size_t i = 0; i < layers.entries().size(); i++) {
306         RenderNode* layerNode = layers.entries()[i].renderNode.get();
307         const Rect& layerDamage = layers.entries()[i].damage;
308         const RenderProperties& properties = layerNode->properties();
309 
310         // Temporarily map current light center into RenderNode's coordinate space
311         Vector3 transformedLightCenter(savedLightCenter);
312         layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter);
313         LightingInfo::setLightCenterRaw(transformedLightCenter);
314 
315         SkPictureRecorder layerRec;
316         auto* recCanvas = layerRec.beginRecording(properties.getWidth(),
317             properties.getHeight());
318         // This is not recorded but still causes clipping.
319         recCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect());
320         RenderNodeDrawable root(layerNode, recCanvas, false);
321         root.forceDraw(recCanvas);
322         // Now write this picture into the SKP canvas with an annotation indicating what it is
323         mskpCanvas->drawAnnotation(layerDamage.toSkRect(), String8::format(
324             "OffscreenLayerDraw|%" PRId64, layerNode->uniqueId()).c_str(), nullptr);
325         mskpCanvas->drawPicture(layerRec.finishRecordingAsPicture());
326     }
327     LightingInfo::setLightCenterRaw(savedLightCenter);
328 }
329 
tryCapture(SkSurface * surface,RenderNode * root,const LayerUpdateQueue & dirtyLayers)330 SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface, RenderNode* root,
331     const LayerUpdateQueue& dirtyLayers) {
332     if (CC_LIKELY(!Properties::skpCaptureEnabled)) {
333         return surface->getCanvas(); // Bail out early when capture is not turned on.
334     }
335     // Note that shouldStartNewFileCapture tells us if this is the *first* frame of a capture.
336     bool firstFrameOfAnim = false;
337     if (shouldStartNewFileCapture() && mCaptureMode == CaptureMode::MultiFrameSKP) {
338         // set a reminder to record every layer near the end of this method, after we have set up
339         // the nway canvas.
340         firstFrameOfAnim = true;
341         if (!setupMultiFrameCapture()) {
342             return surface->getCanvas();
343         }
344     }
345 
346     // Create a canvas pointer, fill it depending on what kind of capture is requested (if any)
347     SkCanvas* pictureCanvas = nullptr;
348     switch (mCaptureMode) {
349         case CaptureMode::CallbackAPI:
350         case CaptureMode::SingleFrameSKP:
351             mRecorder.reset(new SkPictureRecorder());
352             pictureCanvas = mRecorder->beginRecording(surface->width(), surface->height());
353             break;
354         case CaptureMode::MultiFrameSKP:
355             // If a multi frame recording is active, initialize recording for a single frame of a
356             // multi frame file.
357             pictureCanvas = mMultiPic->beginPage(surface->width(), surface->height());
358             break;
359         case CaptureMode::None:
360             // Returning here in the non-capture case means we can count on pictureCanvas being
361             // non-null below.
362             return surface->getCanvas();
363     }
364 
365     // Setting up an nway canvas is common to any kind of capture.
366     mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height());
367     mNwayCanvas->addCanvas(surface->getCanvas());
368     mNwayCanvas->addCanvas(pictureCanvas);
369 
370     if (firstFrameOfAnim) {
371         // On the first frame of any mskp capture we want to record any layers that are needed in
372         // frame but may have been rendered offscreen before recording began.
373         // We do not maintain a list of all layers, since it isn't needed outside this rare,
374         // recording use case. Traverse the tree to find them and put them in this LayerUpdateQueue.
375         LayerUpdateQueue luq;
376         collectLayers(root, &luq);
377         recordLayers(luq, mNwayCanvas.get());
378     } else {
379         // on non-first frames, we record any normal layer draws (dirty regions)
380         recordLayers(dirtyLayers, mNwayCanvas.get());
381     }
382 
383     return mNwayCanvas.get();
384 }
385 
endCapture(SkSurface * surface)386 void SkiaPipeline::endCapture(SkSurface* surface) {
387     if (CC_LIKELY(mCaptureMode == CaptureMode::None)) { return; }
388     mNwayCanvas.reset();
389     ATRACE_CALL();
390     if (mCaptureSequence > 0 && mCaptureMode == CaptureMode::MultiFrameSKP) {
391         mMultiPic->endPage();
392         mCaptureSequence--;
393         if (mCaptureSequence == 0) {
394             mCaptureMode = CaptureMode::None;
395             // Pass mMultiPic and mOpenMultiPicStream to a background thread, which will handle
396             // the heavyweight serialization work and destroy them. mOpenMultiPicStream is released
397             // to a bare pointer because keeping it in a smart pointer makes the lambda
398             // non-copyable. The lambda is only called once, so this is safe.
399             SkFILEWStream* stream = mOpenMultiPicStream.release();
400             CommonPool::post([doc = std::move(mMultiPic), stream]{
401                 ALOGD("Finalizing multi frame SKP");
402                 doc->close();
403                 delete stream;
404                 ALOGD("Multi frame SKP complete.");
405             });
406         }
407     } else {
408         sk_sp<SkPicture> picture = mRecorder->finishRecordingAsPicture();
409         if (picture->approximateOpCount() > 0) {
410             if (mPictureCapturedCallback) {
411                 std::invoke(mPictureCapturedCallback, std::move(picture));
412             } else {
413                 // single frame skp to file
414                 SkSerialProcs procs;
415                 procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){
416                     return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
417                 };
418                 auto data = picture->serialize();
419                 savePictureAsync(data, mCapturedFile);
420                 mCaptureSequence = 0;
421                 mCaptureMode = CaptureMode::None;
422             }
423         }
424         mRecorder.reset();
425     }
426 }
427 
renderFrame(const LayerUpdateQueue & layers,const SkRect & clip,const std::vector<sp<RenderNode>> & nodes,bool opaque,const Rect & contentDrawBounds,sk_sp<SkSurface> surface,const SkMatrix & preTransform)428 void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
429                                const std::vector<sp<RenderNode>>& nodes, bool opaque,
430                                const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
431                                const SkMatrix& preTransform) {
432     bool previousSkpEnabled = Properties::skpCaptureEnabled;
433     if (mPictureCapturedCallback) {
434         Properties::skpCaptureEnabled = true;
435     }
436 
437     // Initialize the canvas for the current frame, that might be a recording canvas if SKP
438     // capture is enabled.
439     SkCanvas* canvas = tryCapture(surface.get(), nodes[0].get(), layers);
440 
441     // draw all layers up front
442     renderLayersImpl(layers, opaque);
443 
444     renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform);
445 
446     endCapture(surface.get());
447 
448     if (CC_UNLIKELY(Properties::debugOverdraw)) {
449         renderOverdraw(clip, nodes, contentDrawBounds, surface, preTransform);
450     }
451 
452     ATRACE_NAME("flush commands");
453     surface->getCanvas()->flush();
454 
455     Properties::skpCaptureEnabled = previousSkpEnabled;
456 }
457 
458 namespace {
nodeBounds(RenderNode & node)459 static Rect nodeBounds(RenderNode& node) {
460     auto& props = node.properties();
461     return Rect(props.getLeft(), props.getTop(), props.getRight(), props.getBottom());
462 }
463 }  // namespace
464 
renderFrameImpl(const SkRect & clip,const std::vector<sp<RenderNode>> & nodes,bool opaque,const Rect & contentDrawBounds,SkCanvas * canvas,const SkMatrix & preTransform)465 void SkiaPipeline::renderFrameImpl(const SkRect& clip,
466                                    const std::vector<sp<RenderNode>>& nodes, bool opaque,
467                                    const Rect& contentDrawBounds, SkCanvas* canvas,
468                                    const SkMatrix& preTransform) {
469     SkAutoCanvasRestore saver(canvas, true);
470     auto clipRestriction = preTransform.mapRect(clip).roundOut();
471     if (CC_UNLIKELY(mCaptureMode == CaptureMode::SingleFrameSKP
472          || mCaptureMode == CaptureMode::MultiFrameSKP)) {
473         canvas->drawAnnotation(SkRect::Make(clipRestriction), "AndroidDeviceClipRestriction",
474             nullptr);
475     } else {
476         // clip drawing to dirty region only when not recording SKP files (which should contain all
477         // draw ops on every frame)
478         canvas->androidFramework_setDeviceClipRestriction(clipRestriction);
479     }
480     canvas->concat(preTransform);
481 
482     // STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293
483     if (!opaque || getSurfaceColorType() == kRGBA_F16_SkColorType) {
484         canvas->clear(SK_ColorTRANSPARENT);
485     }
486 
487     if (1 == nodes.size()) {
488         if (!nodes[0]->nothingToDraw()) {
489             RenderNodeDrawable root(nodes[0].get(), canvas);
490             root.draw(canvas);
491         }
492     } else if (0 == nodes.size()) {
493         // nothing to draw
494     } else {
495         // It there are multiple render nodes, they are laid out as follows:
496         // #0 - backdrop (content + caption)
497         // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop)
498         // #2 - additional overlay nodes
499         // Usually the backdrop cannot be seen since it will be entirely covered by the content.
500         // While
501         // resizing however it might become partially visible. The following render loop will crop
502         // the
503         // backdrop against the content and draw the remaining part of it. It will then draw the
504         // content
505         // cropped to the backdrop (since that indicates a shrinking of the window).
506         //
507         // Additional nodes will be drawn on top with no particular clipping semantics.
508 
509         // Usually the contents bounds should be mContentDrawBounds - however - we will
510         // move it towards the fixed edge to give it a more stable appearance (for the moment).
511         // If there is no content bounds we ignore the layering as stated above and start with 2.
512 
513         // Backdrop bounds in render target space
514         const Rect backdrop = nodeBounds(*nodes[0]);
515 
516         // Bounds that content will fill in render target space (note content node bounds may be
517         // bigger)
518         Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight());
519         content.translate(backdrop.left, backdrop.top);
520         if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) {
521             // Content doesn't entirely overlap backdrop, so fill around content (right/bottom)
522 
523             // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to
524             // also fill left/top. Currently, both 2up and freeform position content at the top/left
525             // of
526             // the backdrop, so this isn't necessary.
527             RenderNodeDrawable backdropNode(nodes[0].get(), canvas);
528             if (content.right < backdrop.right) {
529                 // draw backdrop to right side of content
530                 SkAutoCanvasRestore acr(canvas, true);
531                 canvas->clipRect(SkRect::MakeLTRB(content.right, backdrop.top, backdrop.right,
532                                                   backdrop.bottom));
533                 backdropNode.draw(canvas);
534             }
535             if (content.bottom < backdrop.bottom) {
536                 // draw backdrop to bottom of content
537                 // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill
538                 SkAutoCanvasRestore acr(canvas, true);
539                 canvas->clipRect(SkRect::MakeLTRB(content.left, content.bottom, content.right,
540                                                   backdrop.bottom));
541                 backdropNode.draw(canvas);
542             }
543         }
544 
545         RenderNodeDrawable contentNode(nodes[1].get(), canvas);
546         if (!backdrop.isEmpty()) {
547             // content node translation to catch up with backdrop
548             float dx = backdrop.left - contentDrawBounds.left;
549             float dy = backdrop.top - contentDrawBounds.top;
550 
551             SkAutoCanvasRestore acr(canvas, true);
552             canvas->translate(dx, dy);
553             const SkRect contentLocalClip =
554                     SkRect::MakeXYWH(contentDrawBounds.left, contentDrawBounds.top,
555                                      backdrop.getWidth(), backdrop.getHeight());
556             canvas->clipRect(contentLocalClip);
557             contentNode.draw(canvas);
558         } else {
559             SkAutoCanvasRestore acr(canvas, true);
560             contentNode.draw(canvas);
561         }
562 
563         // remaining overlay nodes, simply defer
564         for (size_t index = 2; index < nodes.size(); index++) {
565             if (!nodes[index]->nothingToDraw()) {
566                 SkAutoCanvasRestore acr(canvas, true);
567                 RenderNodeDrawable overlayNode(nodes[index].get(), canvas);
568                 overlayNode.draw(canvas);
569             }
570         }
571     }
572 }
573 
dumpResourceCacheUsage() const574 void SkiaPipeline::dumpResourceCacheUsage() const {
575     int resources;
576     size_t bytes;
577     mRenderThread.getGrContext()->getResourceCacheUsage(&resources, &bytes);
578     size_t maxBytes = mRenderThread.getGrContext()->getResourceCacheLimit();
579 
580     SkString log("Resource Cache Usage:\n");
581     log.appendf("%8d items\n", resources);
582     log.appendf("%8zu bytes (%.2f MB) out of %.2f MB maximum\n", bytes,
583                 bytes * (1.0f / (1024.0f * 1024.0f)), maxBytes * (1.0f / (1024.0f * 1024.0f)));
584 
585     ALOGD("%s", log.c_str());
586 }
587 
setSurfaceColorProperties(ColorMode colorMode)588 void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) {
589     mColorMode = colorMode;
590     if (colorMode == ColorMode::SRGB) {
591         mSurfaceColorType = SkColorType::kN32_SkColorType;
592         mSurfaceColorSpace = SkColorSpace::MakeSRGB();
593     } else if (colorMode == ColorMode::WideColorGamut) {
594         mSurfaceColorType = DeviceInfo::get()->getWideColorType();
595         mSurfaceColorSpace = DeviceInfo::get()->getWideColorSpace();
596     } else {
597         LOG_ALWAYS_FATAL("Unreachable: unsupported color mode.");
598     }
599 }
600 
601 // Overdraw debugging
602 
603 // These colors should be kept in sync with Caches::getOverdrawColor() with a few differences.
604 // This implementation requires transparent entries for "no overdraw" and "single draws".
605 static const SkColor kOverdrawColors[2][6] = {
606     {
607         0x00000000,
608         0x00000000,
609         0x2f0000ff,
610         0x2f00ff00,
611         0x3fff0000,
612         0x7fff0000,
613     },
614     {
615         0x00000000,
616         0x00000000,
617         0x2f0000ff,
618         0x4fffff00,
619         0x5fff89d7,
620         0x7fff0000,
621     },
622 };
623 
renderOverdraw(const SkRect & clip,const std::vector<sp<RenderNode>> & nodes,const Rect & contentDrawBounds,sk_sp<SkSurface> surface,const SkMatrix & preTransform)624 void SkiaPipeline::renderOverdraw(const SkRect& clip,
625                                   const std::vector<sp<RenderNode>>& nodes,
626                                   const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
627                                   const SkMatrix& preTransform) {
628     // Set up the overdraw canvas.
629     SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height());
630     sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo);
631     LOG_ALWAYS_FATAL_IF(!offscreen, "Failed to create offscreen SkSurface for overdraw viz.");
632     SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas());
633 
634     // Fake a redraw to replay the draw commands.  This will increment the alpha channel
635     // each time a pixel would have been drawn.
636     // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero
637     // initialized.
638     renderFrameImpl(clip, nodes, true, contentDrawBounds, &overdrawCanvas, preTransform);
639     sk_sp<SkImage> counts = offscreen->makeImageSnapshot();
640 
641     // Draw overdraw colors to the canvas.  The color filter will convert counts to colors.
642     SkPaint paint;
643     const SkColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)];
644     paint.setColorFilter(SkOverdrawColorFilter::MakeWithSkColors(colors));
645     surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, &paint);
646 }
647 
648 } /* namespace skiapipeline */
649 } /* namespace uirenderer */
650 } /* namespace android */
651