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