blob: 5ba0f427d466aab4545f1cdb4be5d1f22e6e4ea4 [file] [log] [blame]
Stan Iliev500a0c32016-10-26 10:30:09 -04001/*
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"
Hal Canary10219fb2016-11-23 20:41:22 -050020#include <SkImageEncoder.h>
Leon Scroggins IIIee708fa2016-12-12 15:31:39 -050021#include <SkImagePriv.h>
Stan Iliev500a0c32016-10-26 10:30:09 -040022#include <SkOSFile.h>
Matt Sarettf58cc922016-11-14 18:33:38 -050023#include <SkOverdrawCanvas.h>
24#include <SkOverdrawColorFilter.h>
Stan Iliev500a0c32016-10-26 10:30:09 -040025#include <SkPicture.h>
26#include <SkPictureRecorder.h>
27#include <SkPixelSerializer.h>
28#include <SkStream.h>
29
30using namespace android::uirenderer::renderthread;
31
32namespace android {
33namespace uirenderer {
34namespace skiapipeline {
35
36float SkiaPipeline::mLightRadius = 0;
37uint8_t SkiaPipeline::mAmbientShadowAlpha = 0;
38uint8_t SkiaPipeline::mSpotShadowAlpha = 0;
39
40Vector3 SkiaPipeline::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN};
41
42SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) { }
43
44TaskManager* SkiaPipeline::getTaskManager() {
45 return &mTaskManager;
46}
47
48void SkiaPipeline::onDestroyHardwareResources() {
49 // No need to flush the caches here. There is a timer
50 // which will flush temporary resources over time.
51}
52
Derek Sollenbergerb7d34b62016-11-04 10:46:18 -040053bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) {
54 for (SkImage* image : mutableImages) {
Derek Sollenberger189e8742016-11-16 16:00:17 -050055 if (SkImage_pinAsTexture(image, mRenderThread.getGrContext())) {
56 mPinnedImages.emplace_back(sk_ref_sp(image));
57 } else {
58 return false;
59 }
Derek Sollenbergerb7d34b62016-11-04 10:46:18 -040060 }
61 return true;
62}
63
64void SkiaPipeline::unpinImages() {
65 for (auto& image : mPinnedImages) {
66 SkImage_unpinAsTexture(image.get(), mRenderThread.getGrContext());
67 }
68 mPinnedImages.clear();
69}
70
Stan Iliev500a0c32016-10-26 10:30:09 -040071void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
72 LayerUpdateQueue* layerUpdateQueue, bool opaque,
73 const BakedOpRenderer::LightInfo& lightInfo) {
74 updateLighting(lightGeometry, lightInfo);
75 ATRACE_NAME("draw layers");
76 renderLayersImpl(*layerUpdateQueue, opaque);
77 layerUpdateQueue->clear();
78}
79
80void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
81 // Render all layers that need to be updated, in order.
82 for (size_t i = 0; i < layers.entries().size(); i++) {
83 RenderNode* layerNode = layers.entries()[i].renderNode;
84 // only schedule repaint if node still on layer - possible it may have been
85 // removed during a dropped frame, but layers may still remain scheduled so
86 // as not to lose info on what portion is damaged
87 if (CC_LIKELY(layerNode->getLayerSurface() != nullptr)) {
88 SkASSERT(layerNode->getLayerSurface());
89 SkASSERT(layerNode->getDisplayList()->isSkiaDL());
90 SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList();
91 if (!displayList || displayList->isEmpty()) {
92 SkDEBUGF(("%p drawLayers(%s) : missing drawable", this, layerNode->getName()));
93 return;
94 }
95
96 const Rect& layerDamage = layers.entries()[i].damage;
97
98 SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas();
99
100 int saveCount = layerCanvas->save();
101 SkASSERT(saveCount == 1);
102
Mike Reed6e49c9f2016-12-02 15:36:59 -0500103 layerCanvas->clipRect(layerDamage.toSkRect(), kReplace_SkClipOp);
Stan Iliev500a0c32016-10-26 10:30:09 -0400104
105 auto savedLightCenter = mLightCenter;
106 // map current light center into RenderNode's coordinate space
107 layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(mLightCenter);
108
109 const RenderProperties& properties = layerNode->properties();
110 const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
111 if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) {
112 return;
113 }
114
Matt Sarett79756be2016-11-09 16:13:54 -0500115 layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false;
Stan Iliev500a0c32016-10-26 10:30:09 -0400116 layerCanvas->clear(SK_ColorTRANSPARENT);
117
118 RenderNodeDrawable root(layerNode, layerCanvas, false);
119 root.forceDraw(layerCanvas);
120 layerCanvas->restoreToCount(saveCount);
121 layerCanvas->flush();
122 mLightCenter = savedLightCenter;
123 }
124 }
125}
126
127bool SkiaPipeline::createOrUpdateLayer(RenderNode* node,
128 const DamageAccumulator& damageAccumulator) {
129 SkSurface* layer = node->getLayerSurface();
130 if (!layer || layer->width() != node->getWidth() || layer->height() != node->getHeight()) {
131 SkImageInfo info = SkImageInfo::MakeN32Premul(node->getWidth(), node->getHeight());
132 SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
133 SkASSERT(mRenderThread.getGrContext() != nullptr);
134 node->setLayerSurface(
135 SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
136 info, 0, &props));
137 if (node->getLayerSurface()) {
138 // update the transform in window of the layer to reset its origin wrt light source
139 // position
140 Matrix4 windowTransform;
141 damageAccumulator.computeCurrentTransform(&windowTransform);
142 node->getSkiaLayer()->inverseTransformInWindow = windowTransform;
143 }
144 return true;
145 }
146 return false;
147}
148
149void SkiaPipeline::destroyLayer(RenderNode* node) {
150 node->setLayerSurface(nullptr);
151}
152
153void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
154 GrContext* context = thread.getGrContext();
155 if (context) {
156 ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
157 SkBitmap skiaBitmap;
158 bitmap->getSkBitmap(&skiaBitmap);
159 sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode);
160 SkImage_pinAsTexture(image.get(), context);
161 SkImage_unpinAsTexture(image.get(), context);
162 }
163}
164
165// Encodes to PNG, unless there is already encoded data, in which case that gets
166// used.
167class PngPixelSerializer : public SkPixelSerializer {
168public:
169 bool onUseEncodedData(const void*, size_t) override { return true; }
170 SkData* onEncode(const SkPixmap& pixmap) override {
Hal Canary10219fb2016-11-23 20:41:22 -0500171 SkDynamicMemoryWStream buf;
172 return SkEncodeImage(&buf, pixmap, SkEncodedImageFormat::kPNG, 100)
173 ? buf.detachAsData().release()
174 : nullptr;
Stan Iliev500a0c32016-10-26 10:30:09 -0400175 }
176};
177
178void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
179 const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
180 sk_sp<SkSurface> surface) {
181
Stan Iliev500a0c32016-10-26 10:30:09 -0400182 // draw all layers up front
183 renderLayersImpl(layers, opaque);
184
185 // initialize the canvas for the current frame
186 SkCanvas* canvas = surface->getCanvas();
187
188 std::unique_ptr<SkPictureRecorder> recorder;
189 bool recordingPicture = false;
190 char prop[PROPERTY_VALUE_MAX];
191 if (skpCaptureEnabled()) {
192 property_get("debug.hwui.capture_frame_as_skp", prop, "0");
193 recordingPicture = prop[0] != '0' && !sk_exists(prop);
194 if (recordingPicture) {
195 recorder.reset(new SkPictureRecorder());
196 canvas = recorder->beginRecording(surface->width(), surface->height(),
197 nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag);
198 }
199 }
200
Matt Sarettf58cc922016-11-14 18:33:38 -0500201 renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas);
202
203 if (skpCaptureEnabled() && recordingPicture) {
204 sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture();
205 if (picture->approximateOpCount() > 0) {
206 SkFILEWStream stream(prop);
207 if (stream.isValid()) {
208 PngPixelSerializer serializer;
209 picture->serialize(&stream, &serializer);
210 stream.flush();
211 SkDebugf("Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), prop);
212 }
213 }
214 surface->getCanvas()->drawPicture(picture);
215 }
216
217 if (CC_UNLIKELY(Properties::debugOverdraw)) {
218 renderOverdraw(layers, clip, nodes, contentDrawBounds, surface);
219 }
220
221 ATRACE_NAME("flush commands");
222 canvas->flush();
223}
224
Stan Iliev52771272016-11-17 09:54:38 -0500225namespace {
226static Rect nodeBounds(RenderNode& node) {
227 auto& props = node.properties();
228 return Rect(props.getLeft(), props.getTop(),
229 props.getRight(), props.getBottom());
230}
231}
232
Matt Sarettf58cc922016-11-14 18:33:38 -0500233void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
234 const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
235 SkCanvas* canvas) {
236
Mike Reed6e49c9f2016-12-02 15:36:59 -0500237 canvas->clipRect(clip, kReplace_SkClipOp);
Stan Iliev500a0c32016-10-26 10:30:09 -0400238
239 if (!opaque) {
240 canvas->clear(SK_ColorTRANSPARENT);
241 }
242
Stan Iliev52771272016-11-17 09:54:38 -0500243 if (1 == nodes.size()) {
244 if (!nodes[0]->nothingToDraw()) {
245 SkAutoCanvasRestore acr(canvas, true);
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.
Stan Iliev500a0c32016-10-26 10:30:09 -0400262
Stan Iliev52771272016-11-17 09:54:38 -0500263 // 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.
Stan Iliev500a0c32016-10-26 10:30:09 -0400266
Stan Iliev52771272016-11-17 09:54:38 -0500267 // Backdrop bounds in render target space
268 const Rect backdrop = nodeBounds(*nodes[0]);
Stan Iliev500a0c32016-10-26 10:30:09 -0400269
Stan Iliev52771272016-11-17 09:54:38 -0500270 // 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)
Stan Iliev500a0c32016-10-26 10:30:09 -0400275
Stan Iliev52771272016-11-17 09:54:38 -0500276 // 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 }
Stan Iliev500a0c32016-10-26 10:30:09 -0400295 }
296
Stan Iliev52771272016-11-17 09:54:38 -0500297 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 }
Stan Iliev500a0c32016-10-26 10:30:09 -0400322 }
Stan Iliev500a0c32016-10-26 10:30:09 -0400323}
324
Matt Sarett4bda6bf2016-11-07 15:43:41 -0500325void 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
Matt Sarettf58cc922016-11-14 18:33:38 -0500339// 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
Stan Iliev500a0c32016-10-26 10:30:09 -0400373} /* namespace skiapipeline */
374} /* namespace uirenderer */
375} /* namespace android */