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 <SkImagePriv.h>
21#include <SkOverdrawCanvas.h>
22#include <SkOverdrawColorFilter.h>
23#include <SkPicture.h>
24#include <SkPictureRecorder.h>
25#include "TreeInfo.h"
26#include "VectorDrawable.h"
27#include "utils/TraceUtils.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    mVectorDrawables.reserve(30);
45}
46
47SkiaPipeline::~SkiaPipeline() {
48    unpinImages();
49}
50
51TaskManager* SkiaPipeline::getTaskManager() {
52    return mRenderThread.cacheManager().getTaskManager();
53}
54
55void SkiaPipeline::onDestroyHardwareResources() {
56    unpinImages();
57    mRenderThread.cacheManager().trimStaleResources();
58}
59
60bool 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
71void SkiaPipeline::unpinImages() {
72    for (auto& image : mPinnedImages) {
73        SkImage_unpinAsTexture(image.get(), mRenderThread.getGrContext());
74    }
75    mPinnedImages.clear();
76}
77
78void SkiaPipeline::onPrepareTree() {
79    // The only time mVectorDrawables is not empty is if prepare tree was called 2 times without
80    // a renderFrame in the middle.
81    mVectorDrawables.clear();
82}
83
84void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
85                                LayerUpdateQueue* layerUpdateQueue, bool opaque,
86                                bool wideColorGamut, const BakedOpRenderer::LightInfo& lightInfo) {
87    updateLighting(lightGeometry, lightInfo);
88    ATRACE_NAME("draw layers");
89    renderVectorDrawableCache();
90    renderLayersImpl(*layerUpdateQueue, opaque, wideColorGamut);
91    layerUpdateQueue->clear();
92}
93
94void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque,
95                                    bool wideColorGamut) {
96    sk_sp<GrContext> cachedContext;
97
98    // Render all layers that need to be updated, in order.
99    for (size_t i = 0; i < layers.entries().size(); i++) {
100        RenderNode* layerNode = layers.entries()[i].renderNode.get();
101        // only schedule repaint if node still on layer - possible it may have been
102        // removed during a dropped frame, but layers may still remain scheduled so
103        // as not to lose info on what portion is damaged
104        if (CC_LIKELY(layerNode->getLayerSurface() != nullptr)) {
105            SkASSERT(layerNode->getLayerSurface());
106            SkASSERT(layerNode->getDisplayList()->isSkiaDL());
107            SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList();
108            if (!displayList || displayList->isEmpty()) {
109                SkDEBUGF(("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName()));
110                return;
111            }
112
113            const Rect& layerDamage = layers.entries()[i].damage;
114
115            SkCanvas* layerCanvas = tryCapture(layerNode->getLayerSurface());
116
117            int saveCount = layerCanvas->save();
118            SkASSERT(saveCount == 1);
119
120            layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect());
121
122            auto savedLightCenter = mLightCenter;
123            // map current light center into RenderNode's coordinate space
124            layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(mLightCenter);
125
126            const RenderProperties& properties = layerNode->properties();
127            const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
128            if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) {
129                return;
130            }
131
132            ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(),
133                          bounds.height());
134
135            layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false;
136            layerCanvas->clear(SK_ColorTRANSPARENT);
137
138            RenderNodeDrawable root(layerNode, layerCanvas, false);
139            root.forceDraw(layerCanvas);
140            layerCanvas->restoreToCount(saveCount);
141            mLightCenter = savedLightCenter;
142
143            endCapture(layerNode->getLayerSurface());
144
145            // cache the current context so that we can defer flushing it until
146            // either all the layers have been rendered or the context changes
147            GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext();
148            if (cachedContext.get() != currentContext) {
149                if (cachedContext.get()) {
150                    ATRACE_NAME("flush layers (context changed)");
151                    cachedContext->flush();
152                }
153                cachedContext.reset(SkSafeRef(currentContext));
154            }
155        }
156    }
157
158    if (cachedContext.get()) {
159        ATRACE_NAME("flush layers");
160        cachedContext->flush();
161    }
162}
163
164bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
165                                       bool wideColorGamut, ErrorHandler* errorHandler) {
166    // compute the size of the surface (i.e. texture) to be allocated for this layer
167    const int surfaceWidth = ceilf(node->getWidth() / float(LAYER_SIZE)) * LAYER_SIZE;
168    const int surfaceHeight = ceilf(node->getHeight() / float(LAYER_SIZE)) * LAYER_SIZE;
169
170    SkSurface* layer = node->getLayerSurface();
171    if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) {
172        SkImageInfo info;
173        if (wideColorGamut) {
174            info = SkImageInfo::Make(surfaceWidth, surfaceHeight, kRGBA_F16_SkColorType,
175                                     kPremul_SkAlphaType);
176        } else {
177            info = SkImageInfo::MakeN32Premul(surfaceWidth, surfaceHeight);
178        }
179        SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
180        SkASSERT(mRenderThread.getGrContext() != nullptr);
181        node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
182                                                          SkBudgeted::kYes, info, 0, &props));
183        if (node->getLayerSurface()) {
184            // update the transform in window of the layer to reset its origin wrt light source
185            // position
186            Matrix4 windowTransform;
187            damageAccumulator.computeCurrentTransform(&windowTransform);
188            node->getSkiaLayer()->inverseTransformInWindow = windowTransform;
189        } else {
190            String8 cachesOutput;
191            mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput,
192                    &mRenderThread.renderState());
193            ALOGE("%s", cachesOutput.string());
194            if (errorHandler) {
195                std::ostringstream err;
196                err << "Unable to create layer for " << node->getName();
197                const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
198                err << ", size " << info.width() << "x" << info.height() << " max size "
199                    << maxTextureSize << " color type " << (int)info.colorType()
200                    << " has context " << (int)(mRenderThread.getGrContext() != nullptr);
201                errorHandler->onError(err.str());
202            }
203        }
204        return true;
205    }
206    return false;
207}
208
209void SkiaPipeline::destroyLayer(RenderNode* node) {
210    node->setLayerSurface(nullptr);
211}
212
213void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
214    GrContext* context = thread.getGrContext();
215    if (context) {
216        ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
217        sk_sp<SkColorFilter> colorFilter;
218        auto image = bitmap->makeImage(&colorFilter);
219        if (image.get() && !bitmap->isHardware()) {
220            SkImage_pinAsTexture(image.get(), context);
221            SkImage_unpinAsTexture(image.get(), context);
222        }
223    }
224}
225
226void SkiaPipeline::renderVectorDrawableCache() {
227    if (!mVectorDrawables.empty()) {
228        sp<VectorDrawableAtlas> atlas = mRenderThread.cacheManager().acquireVectorDrawableAtlas();
229        auto grContext = mRenderThread.getGrContext();
230        atlas->prepareForDraw(grContext);
231        ATRACE_NAME("Update VectorDrawables");
232        for (auto vd : mVectorDrawables) {
233            vd->updateCache(atlas, grContext);
234        }
235        mVectorDrawables.clear();
236    }
237}
238
239class SkiaPipeline::SavePictureProcessor : public TaskProcessor<bool> {
240public:
241    explicit SavePictureProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {}
242
243    struct SavePictureTask : public Task<bool> {
244        sk_sp<SkData> data;
245        std::string filename;
246    };
247
248    void savePicture(const sk_sp<SkData>& data, const std::string& filename) {
249        sp<SavePictureTask> task(new SavePictureTask());
250        task->data = data;
251        task->filename = filename;
252        TaskProcessor<bool>::add(task);
253    }
254
255    virtual void onProcess(const sp<Task<bool>>& task) override {
256        SavePictureTask* t = static_cast<SavePictureTask*>(task.get());
257
258        if (0 == access(t->filename.c_str(), F_OK)) {
259            task->setResult(false);
260            return;
261        }
262
263        SkFILEWStream stream(t->filename.c_str());
264        if (stream.isValid()) {
265            stream.write(t->data->data(), t->data->size());
266            stream.flush();
267            SkDebugf("SKP Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(),
268                     t->filename.c_str());
269        }
270
271        task->setResult(true);
272    }
273};
274
275SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) {
276    if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
277        bool recordingPicture = mCaptureSequence > 0;
278        char prop[PROPERTY_VALUE_MAX] = {'\0'};
279        if (!recordingPicture) {
280            property_get(PROPERTY_CAPTURE_SKP_FILENAME, prop, "0");
281            recordingPicture = prop[0] != '0' &&
282                               mCapturedFile != prop;  // ensure we capture only once per filename
283            if (recordingPicture) {
284                mCapturedFile = prop;
285                mCaptureSequence = property_get_int32(PROPERTY_CAPTURE_SKP_FRAMES, 1);
286            }
287        }
288        if (recordingPicture) {
289            mRecorder.reset(new SkPictureRecorder());
290            return mRecorder->beginRecording(surface->width(), surface->height(), nullptr,
291                                             SkPictureRecorder::kPlaybackDrawPicture_RecordFlag);
292        }
293    }
294    return surface->getCanvas();
295}
296
297void SkiaPipeline::endCapture(SkSurface* surface) {
298    if (CC_UNLIKELY(mRecorder.get())) {
299        sk_sp<SkPicture> picture = mRecorder->finishRecordingAsPicture();
300        surface->getCanvas()->drawPicture(picture);
301        if (picture->approximateOpCount() > 0) {
302            auto data = picture->serialize();
303
304            // offload saving to file in a different thread
305            if (!mSavePictureProcessor.get()) {
306                TaskManager* taskManager = getTaskManager();
307                mSavePictureProcessor = new SavePictureProcessor(
308                        taskManager->canRunTasks() ? taskManager : nullptr);
309            }
310            if (1 == mCaptureSequence) {
311                mSavePictureProcessor->savePicture(data, mCapturedFile);
312            } else {
313                mSavePictureProcessor->savePicture(
314                        data,
315                        mCapturedFile + "_" + std::to_string(mCaptureSequence));
316            }
317            mCaptureSequence--;
318        }
319        mRecorder.reset();
320    }
321}
322
323void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
324                               const std::vector<sp<RenderNode>>& nodes, bool opaque,
325                               bool wideColorGamut, const Rect& contentDrawBounds,
326                               sk_sp<SkSurface> surface) {
327    renderVectorDrawableCache();
328
329    // draw all layers up front
330    renderLayersImpl(layers, opaque, wideColorGamut);
331
332    // initialize the canvas for the current frame, that might be a recording canvas if SKP
333    // capture is enabled.
334    std::unique_ptr<SkPictureRecorder> recorder;
335    SkCanvas* canvas = tryCapture(surface.get());
336
337    renderFrameImpl(layers, clip, nodes, opaque, wideColorGamut, contentDrawBounds, canvas);
338
339    endCapture(surface.get());
340
341    if (CC_UNLIKELY(Properties::debugOverdraw)) {
342        renderOverdraw(layers, clip, nodes, contentDrawBounds, surface);
343    }
344
345    ATRACE_NAME("flush commands");
346    surface->getCanvas()->flush();
347}
348
349namespace {
350static Rect nodeBounds(RenderNode& node) {
351    auto& props = node.properties();
352    return Rect(props.getLeft(), props.getTop(), props.getRight(), props.getBottom());
353}
354}
355
356void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
357                                   const std::vector<sp<RenderNode>>& nodes, bool opaque,
358                                   bool wideColorGamut, const Rect& contentDrawBounds,
359                                   SkCanvas* canvas) {
360    SkAutoCanvasRestore saver(canvas, true);
361    canvas->androidFramework_setDeviceClipRestriction(clip.roundOut());
362
363    // STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293
364    if (!opaque || wideColorGamut) {
365        canvas->clear(SK_ColorTRANSPARENT);
366    }
367
368    if (1 == nodes.size()) {
369        if (!nodes[0]->nothingToDraw()) {
370            RenderNodeDrawable root(nodes[0].get(), canvas);
371            root.draw(canvas);
372        }
373    } else if (0 == nodes.size()) {
374        // nothing to draw
375    } else {
376        // It there are multiple render nodes, they are laid out as follows:
377        // #0 - backdrop (content + caption)
378        // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop)
379        // #2 - additional overlay nodes
380        // Usually the backdrop cannot be seen since it will be entirely covered by the content.
381        // While
382        // resizing however it might become partially visible. The following render loop will crop
383        // the
384        // backdrop against the content and draw the remaining part of it. It will then draw the
385        // content
386        // cropped to the backdrop (since that indicates a shrinking of the window).
387        //
388        // Additional nodes will be drawn on top with no particular clipping semantics.
389
390        // Usually the contents bounds should be mContentDrawBounds - however - we will
391        // move it towards the fixed edge to give it a more stable appearance (for the moment).
392        // If there is no content bounds we ignore the layering as stated above and start with 2.
393
394        // Backdrop bounds in render target space
395        const Rect backdrop = nodeBounds(*nodes[0]);
396
397        // Bounds that content will fill in render target space (note content node bounds may be
398        // bigger)
399        Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight());
400        content.translate(backdrop.left, backdrop.top);
401        if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) {
402            // Content doesn't entirely overlap backdrop, so fill around content (right/bottom)
403
404            // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to
405            // also fill left/top. Currently, both 2up and freeform position content at the top/left
406            // of
407            // the backdrop, so this isn't necessary.
408            RenderNodeDrawable backdropNode(nodes[0].get(), canvas);
409            if (content.right < backdrop.right) {
410                // draw backdrop to right side of content
411                SkAutoCanvasRestore acr(canvas, true);
412                canvas->clipRect(SkRect::MakeLTRB(content.right, backdrop.top, backdrop.right,
413                                                  backdrop.bottom));
414                backdropNode.draw(canvas);
415            }
416            if (content.bottom < backdrop.bottom) {
417                // draw backdrop to bottom of content
418                // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill
419                SkAutoCanvasRestore acr(canvas, true);
420                canvas->clipRect(SkRect::MakeLTRB(content.left, content.bottom, content.right,
421                                                  backdrop.bottom));
422                backdropNode.draw(canvas);
423            }
424        }
425
426        RenderNodeDrawable contentNode(nodes[1].get(), canvas);
427        if (!backdrop.isEmpty()) {
428            // content node translation to catch up with backdrop
429            float dx = backdrop.left - contentDrawBounds.left;
430            float dy = backdrop.top - contentDrawBounds.top;
431
432            SkAutoCanvasRestore acr(canvas, true);
433            canvas->translate(dx, dy);
434            const SkRect contentLocalClip =
435                    SkRect::MakeXYWH(contentDrawBounds.left, contentDrawBounds.top,
436                                     backdrop.getWidth(), backdrop.getHeight());
437            canvas->clipRect(contentLocalClip);
438            contentNode.draw(canvas);
439        } else {
440            SkAutoCanvasRestore acr(canvas, true);
441            contentNode.draw(canvas);
442        }
443
444        // remaining overlay nodes, simply defer
445        for (size_t index = 2; index < nodes.size(); index++) {
446            if (!nodes[index]->nothingToDraw()) {
447                SkAutoCanvasRestore acr(canvas, true);
448                RenderNodeDrawable overlayNode(nodes[index].get(), canvas);
449                overlayNode.draw(canvas);
450            }
451        }
452    }
453}
454
455void SkiaPipeline::dumpResourceCacheUsage() const {
456    int resources, maxResources;
457    size_t bytes, maxBytes;
458    mRenderThread.getGrContext()->getResourceCacheUsage(&resources, &bytes);
459    mRenderThread.getGrContext()->getResourceCacheLimits(&maxResources, &maxBytes);
460
461    SkString log("Resource Cache Usage:\n");
462    log.appendf("%8d items out of %d maximum items\n", resources, maxResources);
463    log.appendf("%8zu bytes (%.2f MB) out of %.2f MB maximum\n", bytes,
464                bytes * (1.0f / (1024.0f * 1024.0f)), maxBytes * (1.0f / (1024.0f * 1024.0f)));
465
466    ALOGD("%s", log.c_str());
467}
468
469// Overdraw debugging
470
471// These colors should be kept in sync with Caches::getOverdrawColor() with a few differences.
472// This implementation:
473// (1) Requires transparent entries for "no overdraw" and "single draws".
474// (2) Requires premul colors (instead of unpremul).
475// (3) Requires RGBA colors (instead of BGRA).
476static const uint32_t kOverdrawColors[2][6] = {
477        {
478                0x00000000, 0x00000000, 0x2f2f0000, 0x2f002f00, 0x3f00003f, 0x7f00007f,
479        },
480        {
481                0x00000000, 0x00000000, 0x2f2f0000, 0x4f004f4f, 0x5f50335f, 0x7f00007f,
482        },
483};
484
485void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip,
486                                  const std::vector<sp<RenderNode>>& nodes,
487                                  const Rect& contentDrawBounds, sk_sp<SkSurface> surface) {
488    // Set up the overdraw canvas.
489    SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height());
490    sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo);
491    SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas());
492
493    // Fake a redraw to replay the draw commands.  This will increment the alpha channel
494    // each time a pixel would have been drawn.
495    // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero
496    // initialized.
497    renderFrameImpl(layers, clip, nodes, true, false, contentDrawBounds, &overdrawCanvas);
498    sk_sp<SkImage> counts = offscreen->makeImageSnapshot();
499
500    // Draw overdraw colors to the canvas.  The color filter will convert counts to colors.
501    SkPaint paint;
502    const SkPMColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)];
503    paint.setColorFilter(SkOverdrawColorFilter::Make(colors));
504    surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, &paint);
505}
506
507} /* namespace skiapipeline */
508} /* namespace uirenderer */
509} /* namespace android */
510