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