• Home
  • History
  • Annotate
  • 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 "utils/TraceUtils.h"
20 #include <SkImageEncoder.h>
21 #include <SkImagePriv.h>
22 #include <SkOverdrawCanvas.h>
23 #include <SkOverdrawColorFilter.h>
24 #include <SkPicture.h>
25 #include <SkPictureRecorder.h>
26 #include <SkPixelSerializer.h>
27 #include <SkStream.h>
28 
29 #include <unistd.h>
30 
31 using namespace android::uirenderer::renderthread;
32 
33 namespace android {
34 namespace uirenderer {
35 namespace skiapipeline {
36 
37 float   SkiaPipeline::mLightRadius = 0;
38 uint8_t SkiaPipeline::mAmbientShadowAlpha = 0;
39 uint8_t SkiaPipeline::mSpotShadowAlpha = 0;
40 
41 Vector3 SkiaPipeline::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN};
42 
SkiaPipeline(RenderThread & thread)43 SkiaPipeline::SkiaPipeline(RenderThread& thread) :  mRenderThread(thread) { }
44 
getTaskManager()45 TaskManager* SkiaPipeline::getTaskManager() {
46     return &mTaskManager;
47 }
48 
onDestroyHardwareResources()49 void SkiaPipeline::onDestroyHardwareResources() {
50     // No need to flush the caches here. There is a timer
51     // which will flush temporary resources over time.
52 }
53 
pinImages(std::vector<SkImage * > & mutableImages)54 bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) {
55     for (SkImage* image : mutableImages) {
56         if (SkImage_pinAsTexture(image, mRenderThread.getGrContext())) {
57             mPinnedImages.emplace_back(sk_ref_sp(image));
58         } else {
59             return false;
60         }
61     }
62     return true;
63 }
64 
unpinImages()65 void SkiaPipeline::unpinImages() {
66     for (auto& image : mPinnedImages) {
67         SkImage_unpinAsTexture(image.get(), mRenderThread.getGrContext());
68     }
69     mPinnedImages.clear();
70 }
71 
renderLayers(const FrameBuilder::LightGeometry & lightGeometry,LayerUpdateQueue * layerUpdateQueue,bool opaque,const BakedOpRenderer::LightInfo & lightInfo)72 void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
73         LayerUpdateQueue* layerUpdateQueue, bool opaque,
74         const BakedOpRenderer::LightInfo& lightInfo) {
75     updateLighting(lightGeometry, lightInfo);
76     ATRACE_NAME("draw layers");
77     renderLayersImpl(*layerUpdateQueue, opaque);
78     layerUpdateQueue->clear();
79 }
80 
renderLayersImpl(const LayerUpdateQueue & layers,bool opaque)81 void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
82     // Render all layers that need to be updated, in order.
83     for (size_t i = 0; i < layers.entries().size(); i++) {
84         RenderNode* layerNode = layers.entries()[i].renderNode.get();
85         // only schedule repaint if node still on layer - possible it may have been
86         // removed during a dropped frame, but layers may still remain scheduled so
87         // as not to lose info on what portion is damaged
88         if (CC_LIKELY(layerNode->getLayerSurface() != nullptr)) {
89             SkASSERT(layerNode->getLayerSurface());
90             SkASSERT(layerNode->getDisplayList()->isSkiaDL());
91             SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList();
92             if (!displayList || displayList->isEmpty()) {
93                 SkDEBUGF(("%p drawLayers(%s) : missing drawable", this, layerNode->getName()));
94                 return;
95             }
96 
97             const Rect& layerDamage = layers.entries()[i].damage;
98 
99             SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas();
100 
101             int saveCount = layerCanvas->save();
102             SkASSERT(saveCount == 1);
103 
104             layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect());
105 
106             auto savedLightCenter = mLightCenter;
107             // map current light center into RenderNode's coordinate space
108             layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(mLightCenter);
109 
110             const RenderProperties& properties = layerNode->properties();
111             const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
112             if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) {
113                 return;
114             }
115 
116             layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false;
117             layerCanvas->clear(SK_ColorTRANSPARENT);
118 
119             RenderNodeDrawable root(layerNode, layerCanvas, false);
120             root.forceDraw(layerCanvas);
121             layerCanvas->restoreToCount(saveCount);
122             layerCanvas->flush();
123             mLightCenter = savedLightCenter;
124         }
125     }
126 }
127 
createOrUpdateLayer(RenderNode * node,const DamageAccumulator & damageAccumulator)128 bool SkiaPipeline::createOrUpdateLayer(RenderNode* node,
129         const DamageAccumulator& damageAccumulator) {
130     SkSurface* layer = node->getLayerSurface();
131     if (!layer || layer->width() != node->getWidth() || layer->height() != node->getHeight()) {
132         SkImageInfo info = SkImageInfo::MakeN32Premul(node->getWidth(), node->getHeight());
133         SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
134         SkASSERT(mRenderThread.getGrContext() != nullptr);
135         node->setLayerSurface(
136                 SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
137                         info, 0, &props));
138         if (node->getLayerSurface()) {
139             // update the transform in window of the layer to reset its origin wrt light source
140             // position
141             Matrix4 windowTransform;
142             damageAccumulator.computeCurrentTransform(&windowTransform);
143             node->getSkiaLayer()->inverseTransformInWindow = windowTransform;
144         }
145         return true;
146     }
147     return false;
148 }
149 
destroyLayer(RenderNode * node)150 void SkiaPipeline::destroyLayer(RenderNode* node) {
151     node->setLayerSurface(nullptr);
152 }
153 
prepareToDraw(const RenderThread & thread,Bitmap * bitmap)154 void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
155     GrContext* context = thread.getGrContext();
156     if (context) {
157         ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
158         SkBitmap skiaBitmap;
159         bitmap->getSkBitmap(&skiaBitmap);
160         sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode);
161         SkImage_pinAsTexture(image.get(), context);
162         SkImage_unpinAsTexture(image.get(), context);
163     }
164 }
165 
166 // Encodes to PNG, unless there is already encoded data, in which case that gets
167 // used.
168 class PngPixelSerializer : public SkPixelSerializer {
169 public:
onUseEncodedData(const void *,size_t)170     bool onUseEncodedData(const void*, size_t) override { return true; }
onEncode(const SkPixmap & pixmap)171     SkData* onEncode(const SkPixmap& pixmap) override {
172         SkDynamicMemoryWStream buf;
173         return SkEncodeImage(&buf, pixmap, SkEncodedImageFormat::kPNG, 100)
174                ? buf.detachAsData().release()
175                : nullptr;
176     }
177 };
178 
renderFrame(const LayerUpdateQueue & layers,const SkRect & clip,const std::vector<sp<RenderNode>> & nodes,bool opaque,const Rect & contentDrawBounds,sk_sp<SkSurface> surface)179 void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
180         const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
181         sk_sp<SkSurface> surface) {
182 
183     // draw all layers up front
184     renderLayersImpl(layers, opaque);
185 
186     // initialize the canvas for the current frame
187     SkCanvas* canvas = surface->getCanvas();
188 
189     std::unique_ptr<SkPictureRecorder> recorder;
190     bool recordingPicture = false;
191     char prop[PROPERTY_VALUE_MAX];
192     if (skpCaptureEnabled()) {
193         property_get("debug.hwui.capture_frame_as_skp", prop, "0");
194         recordingPicture = prop[0] != '0' && access(prop, F_OK) != 0;
195         if (recordingPicture) {
196             recorder.reset(new SkPictureRecorder());
197             canvas = recorder->beginRecording(surface->width(), surface->height(),
198                     nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag);
199         }
200     }
201 
202     renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas);
203 
204     if (skpCaptureEnabled() && recordingPicture) {
205         sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture();
206         if (picture->approximateOpCount() > 0) {
207             SkFILEWStream stream(prop);
208             if (stream.isValid()) {
209                 PngPixelSerializer serializer;
210                 picture->serialize(&stream, &serializer);
211                 stream.flush();
212                 SkDebugf("Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), prop);
213             }
214         }
215         surface->getCanvas()->drawPicture(picture);
216     }
217 
218     if (CC_UNLIKELY(Properties::debugOverdraw)) {
219         renderOverdraw(layers, clip, nodes, contentDrawBounds, surface);
220     }
221 
222     ATRACE_NAME("flush commands");
223     canvas->flush();
224 }
225 
226 namespace {
nodeBounds(RenderNode & node)227 static Rect nodeBounds(RenderNode& node) {
228     auto& props = node.properties();
229     return Rect(props.getLeft(), props.getTop(),
230             props.getRight(), props.getBottom());
231 }
232 }
233 
renderFrameImpl(const LayerUpdateQueue & layers,const SkRect & clip,const std::vector<sp<RenderNode>> & nodes,bool opaque,const Rect & contentDrawBounds,SkCanvas * canvas)234 void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
235         const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
236         SkCanvas* canvas) {
237     SkAutoCanvasRestore saver(canvas, true);
238     canvas->androidFramework_setDeviceClipRestriction(clip.roundOut());
239 
240     if (!opaque) {
241         canvas->clear(SK_ColorTRANSPARENT);
242     }
243 
244     if (1 == nodes.size()) {
245         if (!nodes[0]->nothingToDraw()) {
246             RenderNodeDrawable root(nodes[0].get(), canvas);
247             root.draw(canvas);
248         }
249     } else if (0 == nodes.size()) {
250         //nothing to draw
251     } else {
252         // It there are multiple render nodes, they are laid out as follows:
253         // #0 - backdrop (content + caption)
254         // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop)
255         // #2 - additional overlay nodes
256         // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
257         // resizing however it might become partially visible. The following render loop will crop the
258         // backdrop against the content and draw the remaining part of it. It will then draw the content
259         // cropped to the backdrop (since that indicates a shrinking of the window).
260         //
261         // Additional nodes will be drawn on top with no particular clipping semantics.
262 
263         // Usually the contents bounds should be mContentDrawBounds - however - we will
264         // move it towards the fixed edge to give it a more stable appearance (for the moment).
265         // If there is no content bounds we ignore the layering as stated above and start with 2.
266 
267         // Backdrop bounds in render target space
268         const Rect backdrop = nodeBounds(*nodes[0]);
269 
270         // Bounds that content will fill in render target space (note content node bounds may be bigger)
271         Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight());
272         content.translate(backdrop.left, backdrop.top);
273         if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) {
274             // Content doesn't entirely overlap backdrop, so fill around content (right/bottom)
275 
276             // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to
277             // also fill left/top. Currently, both 2up and freeform position content at the top/left of
278             // the backdrop, so this isn't necessary.
279             RenderNodeDrawable backdropNode(nodes[0].get(), canvas);
280             if (content.right < backdrop.right) {
281                 // draw backdrop to right side of content
282                 SkAutoCanvasRestore acr(canvas, true);
283                 canvas->clipRect(SkRect::MakeLTRB(content.right, backdrop.top,
284                         backdrop.right, backdrop.bottom));
285                 backdropNode.draw(canvas);
286             }
287             if (content.bottom < backdrop.bottom) {
288                 // draw backdrop to bottom of content
289                 // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill
290                 SkAutoCanvasRestore acr(canvas, true);
291                 canvas->clipRect(SkRect::MakeLTRB(content.left, content.bottom,
292                         content.right, backdrop.bottom));
293                 backdropNode.draw(canvas);
294             }
295         }
296 
297         RenderNodeDrawable contentNode(nodes[1].get(), canvas);
298         if (!backdrop.isEmpty()) {
299             // content node translation to catch up with backdrop
300             float dx = backdrop.left - contentDrawBounds.left;
301             float dy = backdrop.top - contentDrawBounds.top;
302 
303             SkAutoCanvasRestore acr(canvas, true);
304             canvas->translate(dx, dy);
305             const SkRect contentLocalClip = SkRect::MakeXYWH(contentDrawBounds.left,
306                     contentDrawBounds.top, backdrop.getWidth(), backdrop.getHeight());
307             canvas->clipRect(contentLocalClip);
308             contentNode.draw(canvas);
309         } else {
310             SkAutoCanvasRestore acr(canvas, true);
311             contentNode.draw(canvas);
312         }
313 
314         // remaining overlay nodes, simply defer
315         for (size_t index = 2; index < nodes.size(); index++) {
316             if (!nodes[index]->nothingToDraw()) {
317                 SkAutoCanvasRestore acr(canvas, true);
318                 RenderNodeDrawable overlayNode(nodes[index].get(), canvas);
319                 overlayNode.draw(canvas);
320             }
321         }
322     }
323 }
324 
dumpResourceCacheUsage() const325 void SkiaPipeline::dumpResourceCacheUsage() const {
326     int resources, maxResources;
327     size_t bytes, maxBytes;
328     mRenderThread.getGrContext()->getResourceCacheUsage(&resources, &bytes);
329     mRenderThread.getGrContext()->getResourceCacheLimits(&maxResources, &maxBytes);
330 
331     SkString log("Resource Cache Usage:\n");
332     log.appendf("%8d items out of %d maximum items\n", resources, maxResources);
333     log.appendf("%8zu bytes (%.2f MB) out of %.2f MB maximum\n",
334             bytes, bytes * (1.0f / (1024.0f * 1024.0f)), maxBytes * (1.0f / (1024.0f * 1024.0f)));
335 
336     ALOGD("%s", log.c_str());
337 }
338 
339 // Overdraw debugging
340 
341 // These colors should be kept in sync with Caches::getOverdrawColor() with a few differences.
342 // This implementation:
343 // (1) Requires transparent entries for "no overdraw" and "single draws".
344 // (2) Requires premul colors (instead of unpremul).
345 // (3) Requires RGBA colors (instead of BGRA).
346 static const uint32_t kOverdrawColors[2][6] = {
347         { 0x00000000, 0x00000000, 0x2f2f0000, 0x2f002f00, 0x3f00003f, 0x7f00007f, },
348         { 0x00000000, 0x00000000, 0x2f2f0000, 0x4f004f4f, 0x5f50335f, 0x7f00007f, },
349 };
350 
renderOverdraw(const LayerUpdateQueue & layers,const SkRect & clip,const std::vector<sp<RenderNode>> & nodes,const Rect & contentDrawBounds,sk_sp<SkSurface> surface)351 void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip,
352         const std::vector<sp<RenderNode>>& nodes, const Rect &contentDrawBounds,
353         sk_sp<SkSurface> surface) {
354     // Set up the overdraw canvas.
355     SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height());
356     sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo);
357     SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas());
358 
359     // Fake a redraw to replay the draw commands.  This will increment the alpha channel
360     // each time a pixel would have been drawn.
361     // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero
362     // initialized.
363     renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas);
364     sk_sp<SkImage> counts = offscreen->makeImageSnapshot();
365 
366     // Draw overdraw colors to the canvas.  The color filter will convert counts to colors.
367     SkPaint paint;
368     const SkPMColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)];
369     paint.setColorFilter(SkOverdrawColorFilter::Make(colors));
370     surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, &paint);
371 }
372 
373 } /* namespace skiapipeline */
374 } /* namespace uirenderer */
375 } /* namespace android */
376