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
31using namespace android::uirenderer::renderthread;
32
33namespace android {
34namespace uirenderer {
35namespace skiapipeline {
36
37float   SkiaPipeline::mLightRadius = 0;
38uint8_t SkiaPipeline::mAmbientShadowAlpha = 0;
39uint8_t SkiaPipeline::mSpotShadowAlpha = 0;
40
41Vector3 SkiaPipeline::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN};
42
43SkiaPipeline::SkiaPipeline(RenderThread& thread) :  mRenderThread(thread) { }
44
45TaskManager* SkiaPipeline::getTaskManager() {
46    return &mTaskManager;
47}
48
49void SkiaPipeline::onDestroyHardwareResources() {
50    // No need to flush the caches here. There is a timer
51    // which will flush temporary resources over time.
52}
53
54bool 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
65void SkiaPipeline::unpinImages() {
66    for (auto& image : mPinnedImages) {
67        SkImage_unpinAsTexture(image.get(), mRenderThread.getGrContext());
68    }
69    mPinnedImages.clear();
70}
71
72void 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
81void 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
128bool 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
150void SkiaPipeline::destroyLayer(RenderNode* node) {
151    node->setLayerSurface(nullptr);
152}
153
154void 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.
168class PngPixelSerializer : public SkPixelSerializer {
169public:
170    bool onUseEncodedData(const void*, size_t) override { return true; }
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
179void 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
226namespace {
227static Rect nodeBounds(RenderNode& node) {
228    auto& props = node.properties();
229    return Rect(props.getLeft(), props.getTop(),
230            props.getRight(), props.getBottom());
231}
232}
233
234void 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
325void 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).
346static const uint32_t kOverdrawColors[2][6] = {
347        { 0x00000000, 0x00000000, 0x2f2f0000, 0x2f002f00, 0x3f00003f, 0x7f00007f, },
348        { 0x00000000, 0x00000000, 0x2f2f0000, 0x4f004f4f, 0x5f50335f, 0x7f00007f, },
349};
350
351void 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