SkiaPipeline.cpp revision b66b8bb4a089593ee7c20ad7f59980c16dde9cc9
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 <SkOSFile.h> 23#include <SkOverdrawCanvas.h> 24#include <SkOverdrawColorFilter.h> 25#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 53bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) { 54 for (SkImage* image : mutableImages) { 55 if (SkImage_pinAsTexture(image, mRenderThread.getGrContext())) { 56 mPinnedImages.emplace_back(sk_ref_sp(image)); 57 } else { 58 return false; 59 } 60 } 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 71void 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 103 layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect()); 104 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 115 layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false; 116 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 { 171 SkDynamicMemoryWStream buf; 172 return SkEncodeImage(&buf, pixmap, SkEncodedImageFormat::kPNG, 100) 173 ? buf.detachAsData().release() 174 : nullptr; 175 } 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 182 // 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 201 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 225namespace { 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 233void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, 234 const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds, 235 SkCanvas* canvas) { 236 SkAutoCanvasRestore saver(canvas, true); 237 canvas->androidFramework_setDeviceClipRestriction(clip.roundOut()); 238 239 if (!opaque) { 240 canvas->clear(SK_ColorTRANSPARENT); 241 } 242 243 if (1 == nodes.size()) { 244 if (!nodes[0]->nothingToDraw()) { 245 RenderNodeDrawable root(nodes[0].get(), canvas); 246 root.draw(canvas); 247 } 248 } else if (0 == nodes.size()) { 249 //nothing to draw 250 } else { 251 // It there are multiple render nodes, they are laid out as follows: 252 // #0 - backdrop (content + caption) 253 // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop) 254 // #2 - additional overlay nodes 255 // Usually the backdrop cannot be seen since it will be entirely covered by the content. While 256 // resizing however it might become partially visible. The following render loop will crop the 257 // backdrop against the content and draw the remaining part of it. It will then draw the content 258 // cropped to the backdrop (since that indicates a shrinking of the window). 259 // 260 // Additional nodes will be drawn on top with no particular clipping semantics. 261 262 // Usually the contents bounds should be mContentDrawBounds - however - we will 263 // move it towards the fixed edge to give it a more stable appearance (for the moment). 264 // If there is no content bounds we ignore the layering as stated above and start with 2. 265 266 // Backdrop bounds in render target space 267 const Rect backdrop = nodeBounds(*nodes[0]); 268 269 // Bounds that content will fill in render target space (note content node bounds may be bigger) 270 Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight()); 271 content.translate(backdrop.left, backdrop.top); 272 if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) { 273 // Content doesn't entirely overlap backdrop, so fill around content (right/bottom) 274 275 // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to 276 // also fill left/top. Currently, both 2up and freeform position content at the top/left of 277 // the backdrop, so this isn't necessary. 278 RenderNodeDrawable backdropNode(nodes[0].get(), canvas); 279 if (content.right < backdrop.right) { 280 // draw backdrop to right side of content 281 SkAutoCanvasRestore acr(canvas, true); 282 canvas->clipRect(SkRect::MakeLTRB(content.right, backdrop.top, 283 backdrop.right, backdrop.bottom)); 284 backdropNode.draw(canvas); 285 } 286 if (content.bottom < backdrop.bottom) { 287 // draw backdrop to bottom of content 288 // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill 289 SkAutoCanvasRestore acr(canvas, true); 290 canvas->clipRect(SkRect::MakeLTRB(content.left, content.bottom, 291 content.right, backdrop.bottom)); 292 backdropNode.draw(canvas); 293 } 294 } 295 296 RenderNodeDrawable contentNode(nodes[1].get(), canvas); 297 if (!backdrop.isEmpty()) { 298 // content node translation to catch up with backdrop 299 float dx = backdrop.left - contentDrawBounds.left; 300 float dy = backdrop.top - contentDrawBounds.top; 301 302 SkAutoCanvasRestore acr(canvas, true); 303 canvas->translate(dx, dy); 304 const SkRect contentLocalClip = SkRect::MakeXYWH(contentDrawBounds.left, 305 contentDrawBounds.top, backdrop.getWidth(), backdrop.getHeight()); 306 canvas->clipRect(contentLocalClip); 307 contentNode.draw(canvas); 308 } else { 309 SkAutoCanvasRestore acr(canvas, true); 310 contentNode.draw(canvas); 311 } 312 313 // remaining overlay nodes, simply defer 314 for (size_t index = 2; index < nodes.size(); index++) { 315 if (!nodes[index]->nothingToDraw()) { 316 SkAutoCanvasRestore acr(canvas, true); 317 RenderNodeDrawable overlayNode(nodes[index].get(), canvas); 318 overlayNode.draw(canvas); 319 } 320 } 321 } 322} 323 324void SkiaPipeline::dumpResourceCacheUsage() const { 325 int resources, maxResources; 326 size_t bytes, maxBytes; 327 mRenderThread.getGrContext()->getResourceCacheUsage(&resources, &bytes); 328 mRenderThread.getGrContext()->getResourceCacheLimits(&maxResources, &maxBytes); 329 330 SkString log("Resource Cache Usage:\n"); 331 log.appendf("%8d items out of %d maximum items\n", resources, maxResources); 332 log.appendf("%8zu bytes (%.2f MB) out of %.2f MB maximum\n", 333 bytes, bytes * (1.0f / (1024.0f * 1024.0f)), maxBytes * (1.0f / (1024.0f * 1024.0f))); 334 335 ALOGD("%s", log.c_str()); 336} 337 338// Overdraw debugging 339 340// These colors should be kept in sync with Caches::getOverdrawColor() with a few differences. 341// This implementation: 342// (1) Requires transparent entries for "no overdraw" and "single draws". 343// (2) Requires premul colors (instead of unpremul). 344// (3) Requires RGBA colors (instead of BGRA). 345static const uint32_t kOverdrawColors[2][6] = { 346 { 0x00000000, 0x00000000, 0x2f2f0000, 0x2f002f00, 0x3f00003f, 0x7f00007f, }, 347 { 0x00000000, 0x00000000, 0x2f2f0000, 0x4f004f4f, 0x5f50335f, 0x7f00007f, }, 348}; 349 350void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip, 351 const std::vector<sp<RenderNode>>& nodes, const Rect &contentDrawBounds, 352 sk_sp<SkSurface> surface) { 353 // Set up the overdraw canvas. 354 SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height()); 355 sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo); 356 SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas()); 357 358 // Fake a redraw to replay the draw commands. This will increment the alpha channel 359 // each time a pixel would have been drawn. 360 // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero 361 // initialized. 362 renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas); 363 sk_sp<SkImage> counts = offscreen->makeImageSnapshot(); 364 365 // Draw overdraw colors to the canvas. The color filter will convert counts to colors. 366 SkPaint paint; 367 const SkPMColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)]; 368 paint.setColorFilter(SkOverdrawColorFilter::Make(colors)); 369 surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, &paint); 370} 371 372} /* namespace skiapipeline */ 373} /* namespace uirenderer */ 374} /* namespace android */ 375