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