| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 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 |  | 
| Hal Canary | 10219fb | 2016-11-23 20:41:22 -0500 | [diff] [blame] | 19 | #include <SkImageEncoder.h> | 
| Peiyong Lin | 3bff135 | 2018-12-11 07:56:07 -0800 | [diff] [blame] | 20 | #include <SkImageInfo.h> | 
| Leon Scroggins III | ee708fa | 2016-12-12 15:31:39 -0500 | [diff] [blame] | 21 | #include <SkImagePriv.h> | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 22 | #include <SkMultiPictureDocument.h> | 
| Matt Sarett | f58cc92 | 2016-11-14 18:33:38 -0500 | [diff] [blame] | 23 | #include <SkOverdrawCanvas.h> | 
|  | 24 | #include <SkOverdrawColorFilter.h> | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 25 | #include <SkPicture.h> | 
|  | 26 | #include <SkPictureRecorder.h> | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 27 | #include <SkSerialProcs.h> | 
| Alec Mouri | 43fe6fc | 2019-12-23 07:46:19 -0800 | [diff] [blame] | 28 | #include <SkTypeface.h> | 
|  | 29 | #include <android-base/properties.h> | 
|  | 30 | #include <unistd.h> | 
|  | 31 |  | 
|  | 32 | #include <sstream> | 
|  | 33 |  | 
| rnlee | ce9762b | 2021-05-21 15:40:53 -0700 | [diff] [blame] | 34 | #include <gui/TraceUtils.h> | 
| Fedor Kudasov | 90df056 | 2019-06-19 11:41:34 +0100 | [diff] [blame] | 35 | #include "LightingInfo.h" | 
| Stan Iliev | 23c38a9 | 2017-03-23 00:12:50 -0400 | [diff] [blame] | 36 | #include "VectorDrawable.h" | 
| John Reck | 322b8ab | 2019-03-14 13:15:28 -0700 | [diff] [blame] | 37 | #include "thread/CommonPool.h" | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 38 | #include "tools/SkSharingProc.h" | 
| John Reck | b36bfdd | 2020-07-23 13:47:49 -0700 | [diff] [blame] | 39 | #include "utils/Color.h" | 
| Nathaniel Nifong | 2945bff | 2019-11-25 09:34:21 -0500 | [diff] [blame] | 40 | #include "utils/String8.h" | 
| Jerome Gaillard | a02a12d | 2019-05-28 18:07:56 +0100 | [diff] [blame] | 41 |  | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 42 | using namespace android::uirenderer::renderthread; | 
|  | 43 |  | 
|  | 44 | namespace android { | 
|  | 45 | namespace uirenderer { | 
|  | 46 | namespace skiapipeline { | 
|  | 47 |  | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 48 | SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) { | 
| Derek Sollenberger | 1863d94 | 2020-02-05 15:41:51 -0500 | [diff] [blame] | 49 | setSurfaceColorProperties(mColorMode); | 
| Stan Iliev | 23c38a9 | 2017-03-23 00:12:50 -0400 | [diff] [blame] | 50 | } | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 51 |  | 
| Stan Iliev | 232f362 | 2017-08-23 17:15:09 -0400 | [diff] [blame] | 52 | SkiaPipeline::~SkiaPipeline() { | 
|  | 53 | unpinImages(); | 
|  | 54 | } | 
|  | 55 |  | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 56 | void SkiaPipeline::onDestroyHardwareResources() { | 
| Derek Sollenberger | 92a9eb9 | 2018-04-12 13:42:19 -0400 | [diff] [blame] | 57 | unpinImages(); | 
| Derek Sollenberger | f9e45d1 | 2017-06-01 13:07:39 -0400 | [diff] [blame] | 58 | mRenderThread.cacheManager().trimStaleResources(); | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 59 | } | 
|  | 60 |  | 
| Derek Sollenberger | b7d34b6 | 2016-11-04 10:46:18 -0400 | [diff] [blame] | 61 | bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) { | 
| Greg Daniel | f8cb525 | 2021-06-30 10:40:34 -0400 | [diff] [blame] | 62 | if (!mRenderThread.getGrContext()) { | 
|  | 63 | ALOGD("Trying to pin an image with an invalid GrContext"); | 
|  | 64 | return false; | 
|  | 65 | } | 
| Derek Sollenberger | b7d34b6 | 2016-11-04 10:46:18 -0400 | [diff] [blame] | 66 | for (SkImage* image : mutableImages) { | 
| Derek Sollenberger | 189e874 | 2016-11-16 16:00:17 -0500 | [diff] [blame] | 67 | if (SkImage_pinAsTexture(image, mRenderThread.getGrContext())) { | 
|  | 68 | mPinnedImages.emplace_back(sk_ref_sp(image)); | 
|  | 69 | } else { | 
|  | 70 | return false; | 
|  | 71 | } | 
| Derek Sollenberger | b7d34b6 | 2016-11-04 10:46:18 -0400 | [diff] [blame] | 72 | } | 
|  | 73 | return true; | 
|  | 74 | } | 
|  | 75 |  | 
|  | 76 | void SkiaPipeline::unpinImages() { | 
|  | 77 | for (auto& image : mPinnedImages) { | 
|  | 78 | SkImage_unpinAsTexture(image.get(), mRenderThread.getGrContext()); | 
|  | 79 | } | 
|  | 80 | mPinnedImages.clear(); | 
|  | 81 | } | 
|  | 82 |  | 
| John Reck | d9d7f12 | 2018-05-03 14:40:56 -0700 | [diff] [blame] | 83 | void SkiaPipeline::renderLayers(const LightGeometry& lightGeometry, | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 84 | LayerUpdateQueue* layerUpdateQueue, bool opaque, | 
| Peiyong Lin | 1f6aa12 | 2018-09-10 16:28:08 -0700 | [diff] [blame] | 85 | const LightInfo& lightInfo) { | 
| Fedor Kudasov | 90df056 | 2019-06-19 11:41:34 +0100 | [diff] [blame] | 86 | LightingInfo::updateLighting(lightGeometry, lightInfo); | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 87 | ATRACE_NAME("draw layers"); | 
| Peiyong Lin | 1f6aa12 | 2018-09-10 16:28:08 -0700 | [diff] [blame] | 88 | renderLayersImpl(*layerUpdateQueue, opaque); | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 89 | layerUpdateQueue->clear(); | 
|  | 90 | } | 
|  | 91 |  | 
| Peiyong Lin | 1f6aa12 | 2018-09-10 16:28:08 -0700 | [diff] [blame] | 92 | void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) { | 
| Adlai Holler | ab76215 | 2020-09-16 10:37:19 -0400 | [diff] [blame] | 93 | sk_sp<GrDirectContext> cachedContext; | 
| Derek Sollenberger | f7df184 | 2017-09-05 11:15:58 -0400 | [diff] [blame] | 94 |  | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 95 | // Render all layers that need to be updated, in order. | 
|  | 96 | for (size_t i = 0; i < layers.entries().size(); i++) { | 
| John Reck | fc29f7acd | 2017-03-02 13:23:16 -0800 | [diff] [blame] | 97 | RenderNode* layerNode = layers.entries()[i].renderNode.get(); | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 98 | // only schedule repaint if node still on layer - possible it may have been | 
|  | 99 | // removed during a dropped frame, but layers may still remain scheduled so | 
|  | 100 | // as not to lose info on what portion is damaged | 
| Nathaniel Nifong | 2945bff | 2019-11-25 09:34:21 -0500 | [diff] [blame] | 101 | if (CC_UNLIKELY(layerNode->getLayerSurface() == nullptr)) { | 
|  | 102 | continue; | 
|  | 103 | } | 
|  | 104 | SkASSERT(layerNode->getLayerSurface()); | 
| John Reck | be67195 | 2021-01-13 22:39:32 -0500 | [diff] [blame] | 105 | SkiaDisplayList* displayList = layerNode->getDisplayList().asSkiaDl(); | 
| Nathaniel Nifong | 2945bff | 2019-11-25 09:34:21 -0500 | [diff] [blame] | 106 | if (!displayList || displayList->isEmpty()) { | 
|  | 107 | ALOGE("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName()); | 
|  | 108 | return; | 
|  | 109 | } | 
|  | 110 |  | 
|  | 111 | const Rect& layerDamage = layers.entries()[i].damage; | 
|  | 112 |  | 
|  | 113 | SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas(); | 
|  | 114 |  | 
|  | 115 | int saveCount = layerCanvas->save(); | 
|  | 116 | SkASSERT(saveCount == 1); | 
|  | 117 |  | 
|  | 118 | layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect()); | 
|  | 119 |  | 
|  | 120 | // TODO: put localized light center calculation and storage to a drawable related code. | 
|  | 121 | // It does not seem right to store something localized in a global state | 
|  | 122 | // fix here and in recordLayers | 
|  | 123 | const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw()); | 
|  | 124 | Vector3 transformedLightCenter(savedLightCenter); | 
|  | 125 | // map current light center into RenderNode's coordinate space | 
|  | 126 | layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter); | 
|  | 127 | LightingInfo::setLightCenterRaw(transformedLightCenter); | 
|  | 128 |  | 
|  | 129 | const RenderProperties& properties = layerNode->properties(); | 
|  | 130 | const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight()); | 
|  | 131 | if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) { | 
|  | 132 | return; | 
|  | 133 | } | 
|  | 134 |  | 
|  | 135 | ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(), | 
|  | 136 | bounds.height()); | 
|  | 137 |  | 
|  | 138 | layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false; | 
|  | 139 | layerCanvas->clear(SK_ColorTRANSPARENT); | 
|  | 140 |  | 
|  | 141 | RenderNodeDrawable root(layerNode, layerCanvas, false); | 
|  | 142 | root.forceDraw(layerCanvas); | 
|  | 143 | layerCanvas->restoreToCount(saveCount); | 
|  | 144 |  | 
|  | 145 | LightingInfo::setLightCenterRaw(savedLightCenter); | 
|  | 146 |  | 
|  | 147 | // cache the current context so that we can defer flushing it until | 
|  | 148 | // either all the layers have been rendered or the context changes | 
| Adlai Holler | ab76215 | 2020-09-16 10:37:19 -0400 | [diff] [blame] | 149 | GrDirectContext* currentContext = | 
|  | 150 | GrAsDirectContext(layerNode->getLayerSurface()->getCanvas()->recordingContext()); | 
| Nathaniel Nifong | 2945bff | 2019-11-25 09:34:21 -0500 | [diff] [blame] | 151 | if (cachedContext.get() != currentContext) { | 
|  | 152 | if (cachedContext.get()) { | 
|  | 153 | ATRACE_NAME("flush layers (context changed)"); | 
| Greg Daniel | c7ad408 | 2020-05-14 15:38:26 -0400 | [diff] [blame] | 154 | cachedContext->flushAndSubmit(); | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 155 | } | 
| Nathaniel Nifong | 2945bff | 2019-11-25 09:34:21 -0500 | [diff] [blame] | 156 | cachedContext.reset(SkSafeRef(currentContext)); | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 157 | } | 
|  | 158 | } | 
| Derek Sollenberger | f7df184 | 2017-09-05 11:15:58 -0400 | [diff] [blame] | 159 |  | 
|  | 160 | if (cachedContext.get()) { | 
| Derek Sollenberger | be3876c | 2018-04-20 16:13:31 -0400 | [diff] [blame] | 161 | ATRACE_NAME("flush layers"); | 
| Greg Daniel | c7ad408 | 2020-05-14 15:38:26 -0400 | [diff] [blame] | 162 | cachedContext->flushAndSubmit(); | 
| Derek Sollenberger | f7df184 | 2017-09-05 11:15:58 -0400 | [diff] [blame] | 163 | } | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 164 | } | 
|  | 165 |  | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 166 | bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, | 
| Peiyong Lin | 1f6aa12 | 2018-09-10 16:28:08 -0700 | [diff] [blame] | 167 | ErrorHandler* errorHandler) { | 
| Derek Sollenberger | 03e6cff7 | 2017-12-04 15:07:08 -0500 | [diff] [blame] | 168 | // compute the size of the surface (i.e. texture) to be allocated for this layer | 
|  | 169 | const int surfaceWidth = ceilf(node->getWidth() / float(LAYER_SIZE)) * LAYER_SIZE; | 
|  | 170 | const int surfaceHeight = ceilf(node->getHeight() / float(LAYER_SIZE)) * LAYER_SIZE; | 
|  | 171 |  | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 172 | SkSurface* layer = node->getLayerSurface(); | 
| Derek Sollenberger | 03e6cff7 | 2017-12-04 15:07:08 -0500 | [diff] [blame] | 173 | if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) { | 
| Stan Iliev | 08fc19a | 2017-07-24 10:20:33 -0400 | [diff] [blame] | 174 | SkImageInfo info; | 
| Peiyong Lin | 1f6aa12 | 2018-09-10 16:28:08 -0700 | [diff] [blame] | 175 | info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(), | 
| Derek Sollenberger | d01b591 | 2018-10-19 15:55:33 -0400 | [diff] [blame] | 176 | kPremul_SkAlphaType, getSurfaceColorSpace()); | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 177 | SkSurfaceProps props(0, kUnknown_SkPixelGeometry); | 
|  | 178 | SkASSERT(mRenderThread.getGrContext() != nullptr); | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 179 | node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), | 
| Derek Sollenberger | 25833d2 | 2019-01-14 13:55:55 -0500 | [diff] [blame] | 180 | SkBudgeted::kYes, info, 0, | 
|  | 181 | this->getSurfaceOrigin(), &props)); | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 182 | if (node->getLayerSurface()) { | 
|  | 183 | // update the transform in window of the layer to reset its origin wrt light source | 
|  | 184 | // position | 
|  | 185 | Matrix4 windowTransform; | 
|  | 186 | damageAccumulator.computeCurrentTransform(&windowTransform); | 
| Stan Iliev | da7c19c | 2019-05-22 14:43:44 -0400 | [diff] [blame] | 187 | node->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform); | 
| Stan Iliev | 216b157 | 2018-03-26 14:29:50 -0400 | [diff] [blame] | 188 | } else { | 
|  | 189 | String8 cachesOutput; | 
|  | 190 | mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput, | 
| John Reck | 283bb46 | 2018-12-13 16:40:14 -0800 | [diff] [blame] | 191 | &mRenderThread.renderState()); | 
| Stan Iliev | 216b157 | 2018-03-26 14:29:50 -0400 | [diff] [blame] | 192 | ALOGE("%s", cachesOutput.string()); | 
|  | 193 | if (errorHandler) { | 
|  | 194 | std::ostringstream err; | 
|  | 195 | err << "Unable to create layer for " << node->getName(); | 
|  | 196 | const int maxTextureSize = DeviceInfo::get()->maxTextureSize(); | 
|  | 197 | err << ", size " << info.width() << "x" << info.height() << " max size " | 
| John Reck | 283bb46 | 2018-12-13 16:40:14 -0800 | [diff] [blame] | 198 | << maxTextureSize << " color type " << (int)info.colorType() << " has context " | 
|  | 199 | << (int)(mRenderThread.getGrContext() != nullptr); | 
| Stan Iliev | 216b157 | 2018-03-26 14:29:50 -0400 | [diff] [blame] | 200 | errorHandler->onError(err.str()); | 
|  | 201 | } | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 202 | } | 
|  | 203 | return true; | 
|  | 204 | } | 
|  | 205 | return false; | 
|  | 206 | } | 
|  | 207 |  | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 208 | void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) { | 
| Adlai Holler | ab76215 | 2020-09-16 10:37:19 -0400 | [diff] [blame] | 209 | GrDirectContext* context = thread.getGrContext(); | 
| John Reck | cf1170f | 2021-07-14 15:52:19 -0400 | [diff] [blame] | 210 | if (context && !bitmap->isHardware()) { | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 211 | ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height()); | 
| Derek Sollenberger | d01b591 | 2018-10-19 15:55:33 -0400 | [diff] [blame] | 212 | auto image = bitmap->makeImage(); | 
| John Reck | cf1170f | 2021-07-14 15:52:19 -0400 | [diff] [blame] | 213 | if (image.get()) { | 
| Stan Iliev | 7bc3bc6 | 2017-05-24 13:28:36 -0400 | [diff] [blame] | 214 | SkImage_pinAsTexture(image.get(), context); | 
|  | 215 | SkImage_unpinAsTexture(image.get(), context); | 
| John Reck | cf1170f | 2021-07-14 15:52:19 -0400 | [diff] [blame] | 216 | // A submit is necessary as there may not be a frame coming soon, so without a call | 
|  | 217 | // to submit these texture uploads can just sit in the queue building up until | 
|  | 218 | // we run out of RAM | 
|  | 219 | context->flushAndSubmit(); | 
| Stan Iliev | 7bc3bc6 | 2017-05-24 13:28:36 -0400 | [diff] [blame] | 220 | } | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 221 | } | 
|  | 222 | } | 
|  | 223 |  | 
| John Reck | 322b8ab | 2019-03-14 13:15:28 -0700 | [diff] [blame] | 224 | static void savePictureAsync(const sk_sp<SkData>& data, const std::string& filename) { | 
|  | 225 | CommonPool::post([data, filename] { | 
|  | 226 | if (0 == access(filename.c_str(), F_OK)) { | 
| Stan Iliev | e9d0012 | 2017-09-19 12:07:10 -0400 | [diff] [blame] | 227 | return; | 
|  | 228 | } | 
|  | 229 |  | 
| John Reck | 322b8ab | 2019-03-14 13:15:28 -0700 | [diff] [blame] | 230 | SkFILEWStream stream(filename.c_str()); | 
| Stan Iliev | e9d0012 | 2017-09-19 12:07:10 -0400 | [diff] [blame] | 231 | if (stream.isValid()) { | 
| John Reck | 322b8ab | 2019-03-14 13:15:28 -0700 | [diff] [blame] | 232 | stream.write(data->data(), data->size()); | 
| Stan Iliev | e9d0012 | 2017-09-19 12:07:10 -0400 | [diff] [blame] | 233 | stream.flush(); | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 234 | ALOGD("SKP Captured Drawing Output (%zu bytes) for frame. %s", stream.bytesWritten(), | 
| John Reck | 322b8ab | 2019-03-14 13:15:28 -0700 | [diff] [blame] | 235 | filename.c_str()); | 
| Stan Iliev | e9d0012 | 2017-09-19 12:07:10 -0400 | [diff] [blame] | 236 | } | 
| John Reck | 322b8ab | 2019-03-14 13:15:28 -0700 | [diff] [blame] | 237 | }); | 
|  | 238 | } | 
| Stan Iliev | e9d0012 | 2017-09-19 12:07:10 -0400 | [diff] [blame] | 239 |  | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 240 | // Note multiple SkiaPipeline instances may be loaded if more than one app is visible. | 
|  | 241 | // Each instance may observe the filename changing and try to record to a file of the same name. | 
|  | 242 | // Only the first one will succeed. There is no scope available here where we could coordinate | 
|  | 243 | // to cause this function to return true for only one of the instances. | 
|  | 244 | bool SkiaPipeline::shouldStartNewFileCapture() { | 
|  | 245 | // Don't start a new file based capture if one is currently ongoing. | 
|  | 246 | if (mCaptureMode != CaptureMode::None) { return false; } | 
|  | 247 |  | 
|  | 248 | // A new capture is started when the filename property changes. | 
|  | 249 | // Read the filename property. | 
|  | 250 | std::string prop = base::GetProperty(PROPERTY_CAPTURE_SKP_FILENAME, "0"); | 
|  | 251 | // if the filename property changed to a valid value | 
|  | 252 | if (prop[0] != '0' && mCapturedFile != prop) { | 
|  | 253 | // remember this new filename | 
|  | 254 | mCapturedFile = prop; | 
|  | 255 | // and get a property indicating how many frames to capture. | 
|  | 256 | mCaptureSequence = base::GetIntProperty(PROPERTY_CAPTURE_SKP_FRAMES, 1); | 
| John Reck | 5cca8f2 | 2018-12-10 17:06:22 -0800 | [diff] [blame] | 257 | if (mCaptureSequence <= 0) { | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 258 | return false; | 
|  | 259 | } else if (mCaptureSequence == 1) { | 
|  | 260 | mCaptureMode = CaptureMode::SingleFrameSKP; | 
|  | 261 | } else { | 
|  | 262 | mCaptureMode = CaptureMode::MultiFrameSKP; | 
| Stan Iliev | e9d0012 | 2017-09-19 12:07:10 -0400 | [diff] [blame] | 263 | } | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 264 | return true; | 
|  | 265 | } | 
|  | 266 | return false; | 
|  | 267 | } | 
|  | 268 |  | 
|  | 269 | // performs the first-frame work of a multi frame SKP capture. Returns true if successful. | 
|  | 270 | bool SkiaPipeline::setupMultiFrameCapture() { | 
|  | 271 | ALOGD("Set up multi-frame capture, frames = %d", mCaptureSequence); | 
|  | 272 | // We own this stream and need to hold it until close() finishes. | 
|  | 273 | auto stream = std::make_unique<SkFILEWStream>(mCapturedFile.c_str()); | 
|  | 274 | if (stream->isValid()) { | 
|  | 275 | mOpenMultiPicStream = std::move(stream); | 
|  | 276 | mSerialContext.reset(new SkSharingSerialContext()); | 
|  | 277 | SkSerialProcs procs; | 
|  | 278 | procs.fImageProc = SkSharingSerialContext::serializeImage; | 
|  | 279 | procs.fImageCtx = mSerialContext.get(); | 
| Nathaniel Nifong | 429fff4 | 2020-01-30 14:38:21 -0500 | [diff] [blame] | 280 | procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){ | 
|  | 281 | return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData); | 
|  | 282 | }; | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 283 | // SkDocuments don't take owership of the streams they write. | 
|  | 284 | // we need to keep it until after mMultiPic.close() | 
|  | 285 | // procs is passed as a pointer, but just as a method of having an optional default. | 
|  | 286 | // procs doesn't need to outlive this Make call. | 
| Nathaniel Nifong | 7c21677 | 2021-01-22 13:23:25 -0500 | [diff] [blame] | 287 | mMultiPic = SkMakeMultiPictureDocument(mOpenMultiPicStream.get(), &procs, | 
|  | 288 | [sharingCtx = mSerialContext.get()](const SkPicture* pic) { | 
|  | 289 | SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx); | 
|  | 290 | }); | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 291 | return true; | 
|  | 292 | } else { | 
|  | 293 | ALOGE("Could not open \"%s\" for writing.", mCapturedFile.c_str()); | 
|  | 294 | mCaptureSequence = 0; | 
|  | 295 | mCaptureMode = CaptureMode::None; | 
|  | 296 | return false; | 
|  | 297 | } | 
|  | 298 | } | 
|  | 299 |  | 
| Nathaniel Nifong | 2945bff | 2019-11-25 09:34:21 -0500 | [diff] [blame] | 300 | // recurse through the rendernode's children, add any nodes which are layers to the queue. | 
|  | 301 | static void collectLayers(RenderNode* node, LayerUpdateQueue* layers) { | 
| John Reck | be67195 | 2021-01-13 22:39:32 -0500 | [diff] [blame] | 302 | SkiaDisplayList* dl = node->getDisplayList().asSkiaDl(); | 
| Nathaniel Nifong | 2945bff | 2019-11-25 09:34:21 -0500 | [diff] [blame] | 303 | if (dl) { | 
|  | 304 | const auto& prop = node->properties(); | 
|  | 305 | if (node->hasLayer()) { | 
|  | 306 | layers->enqueueLayerWithDamage(node, Rect(prop.getWidth(), prop.getHeight())); | 
|  | 307 | } | 
|  | 308 | // The way to recurse through rendernodes is to call this with a lambda. | 
|  | 309 | dl->updateChildren([&](RenderNode* child) { collectLayers(child, layers); }); | 
|  | 310 | } | 
|  | 311 | } | 
|  | 312 |  | 
|  | 313 | // record the provided layers to the provided canvas as self-contained skpictures. | 
|  | 314 | static void recordLayers(const LayerUpdateQueue& layers, | 
|  | 315 | SkCanvas* mskpCanvas) { | 
|  | 316 | const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw()); | 
|  | 317 | // Record the commands to re-draw each dirty layer into an SkPicture | 
|  | 318 | for (size_t i = 0; i < layers.entries().size(); i++) { | 
|  | 319 | RenderNode* layerNode = layers.entries()[i].renderNode.get(); | 
|  | 320 | const Rect& layerDamage = layers.entries()[i].damage; | 
|  | 321 | const RenderProperties& properties = layerNode->properties(); | 
|  | 322 |  | 
|  | 323 | // Temporarily map current light center into RenderNode's coordinate space | 
|  | 324 | Vector3 transformedLightCenter(savedLightCenter); | 
|  | 325 | layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter); | 
|  | 326 | LightingInfo::setLightCenterRaw(transformedLightCenter); | 
|  | 327 |  | 
|  | 328 | SkPictureRecorder layerRec; | 
|  | 329 | auto* recCanvas = layerRec.beginRecording(properties.getWidth(), | 
|  | 330 | properties.getHeight()); | 
|  | 331 | // This is not recorded but still causes clipping. | 
|  | 332 | recCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect()); | 
|  | 333 | RenderNodeDrawable root(layerNode, recCanvas, false); | 
|  | 334 | root.forceDraw(recCanvas); | 
|  | 335 | // Now write this picture into the SKP canvas with an annotation indicating what it is | 
|  | 336 | mskpCanvas->drawAnnotation(layerDamage.toSkRect(), String8::format( | 
|  | 337 | "OffscreenLayerDraw|%" PRId64, layerNode->uniqueId()).c_str(), nullptr); | 
|  | 338 | mskpCanvas->drawPicture(layerRec.finishRecordingAsPicture()); | 
|  | 339 | } | 
|  | 340 | LightingInfo::setLightCenterRaw(savedLightCenter); | 
|  | 341 | } | 
|  | 342 |  | 
|  | 343 | SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface, RenderNode* root, | 
|  | 344 | const LayerUpdateQueue& dirtyLayers) { | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 345 | if (CC_LIKELY(!Properties::skpCaptureEnabled)) { | 
|  | 346 | return surface->getCanvas(); // Bail out early when capture is not turned on. | 
|  | 347 | } | 
|  | 348 | // Note that shouldStartNewFileCapture tells us if this is the *first* frame of a capture. | 
| Nathaniel Nifong | 2945bff | 2019-11-25 09:34:21 -0500 | [diff] [blame] | 349 | bool firstFrameOfAnim = false; | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 350 | if (shouldStartNewFileCapture() && mCaptureMode == CaptureMode::MultiFrameSKP) { | 
| Nathaniel Nifong | 2945bff | 2019-11-25 09:34:21 -0500 | [diff] [blame] | 351 | // set a reminder to record every layer near the end of this method, after we have set up | 
|  | 352 | // the nway canvas. | 
|  | 353 | firstFrameOfAnim = true; | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 354 | if (!setupMultiFrameCapture()) { | 
|  | 355 | return surface->getCanvas(); | 
| Stan Iliev | e9d0012 | 2017-09-19 12:07:10 -0400 | [diff] [blame] | 356 | } | 
|  | 357 | } | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 358 |  | 
|  | 359 | // Create a canvas pointer, fill it depending on what kind of capture is requested (if any) | 
|  | 360 | SkCanvas* pictureCanvas = nullptr; | 
|  | 361 | switch (mCaptureMode) { | 
|  | 362 | case CaptureMode::CallbackAPI: | 
|  | 363 | case CaptureMode::SingleFrameSKP: | 
|  | 364 | mRecorder.reset(new SkPictureRecorder()); | 
| Nathaniel Nifong | 31fee3a | 2019-07-11 16:27:14 -0400 | [diff] [blame] | 365 | pictureCanvas = mRecorder->beginRecording(surface->width(), surface->height()); | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 366 | break; | 
|  | 367 | case CaptureMode::MultiFrameSKP: | 
|  | 368 | // If a multi frame recording is active, initialize recording for a single frame of a | 
|  | 369 | // multi frame file. | 
|  | 370 | pictureCanvas = mMultiPic->beginPage(surface->width(), surface->height()); | 
|  | 371 | break; | 
|  | 372 | case CaptureMode::None: | 
|  | 373 | // Returning here in the non-capture case means we can count on pictureCanvas being | 
|  | 374 | // non-null below. | 
|  | 375 | return surface->getCanvas(); | 
|  | 376 | } | 
|  | 377 |  | 
|  | 378 | // Setting up an nway canvas is common to any kind of capture. | 
|  | 379 | mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height()); | 
|  | 380 | mNwayCanvas->addCanvas(surface->getCanvas()); | 
|  | 381 | mNwayCanvas->addCanvas(pictureCanvas); | 
| Nathaniel Nifong | 2945bff | 2019-11-25 09:34:21 -0500 | [diff] [blame] | 382 |  | 
|  | 383 | if (firstFrameOfAnim) { | 
|  | 384 | // On the first frame of any mskp capture we want to record any layers that are needed in | 
|  | 385 | // frame but may have been rendered offscreen before recording began. | 
|  | 386 | // We do not maintain a list of all layers, since it isn't needed outside this rare, | 
|  | 387 | // recording use case. Traverse the tree to find them and put them in this LayerUpdateQueue. | 
|  | 388 | LayerUpdateQueue luq; | 
|  | 389 | collectLayers(root, &luq); | 
|  | 390 | recordLayers(luq, mNwayCanvas.get()); | 
|  | 391 | } else { | 
|  | 392 | // on non-first frames, we record any normal layer draws (dirty regions) | 
|  | 393 | recordLayers(dirtyLayers, mNwayCanvas.get()); | 
|  | 394 | } | 
|  | 395 |  | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 396 | return mNwayCanvas.get(); | 
| Stan Iliev | e9d0012 | 2017-09-19 12:07:10 -0400 | [diff] [blame] | 397 | } | 
|  | 398 |  | 
|  | 399 | void SkiaPipeline::endCapture(SkSurface* surface) { | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 400 | if (CC_LIKELY(mCaptureMode == CaptureMode::None)) { return; } | 
| John Reck | 5cca8f2 | 2018-12-10 17:06:22 -0800 | [diff] [blame] | 401 | mNwayCanvas.reset(); | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 402 | ATRACE_CALL(); | 
|  | 403 | if (mCaptureSequence > 0 && mCaptureMode == CaptureMode::MultiFrameSKP) { | 
|  | 404 | mMultiPic->endPage(); | 
|  | 405 | mCaptureSequence--; | 
|  | 406 | if (mCaptureSequence == 0) { | 
|  | 407 | mCaptureMode = CaptureMode::None; | 
|  | 408 | // Pass mMultiPic and mOpenMultiPicStream to a background thread, which will handle | 
|  | 409 | // the heavyweight serialization work and destroy them. mOpenMultiPicStream is released | 
|  | 410 | // to a bare pointer because keeping it in a smart pointer makes the lambda | 
|  | 411 | // non-copyable. The lambda is only called once, so this is safe. | 
|  | 412 | SkFILEWStream* stream = mOpenMultiPicStream.release(); | 
|  | 413 | CommonPool::post([doc = std::move(mMultiPic), stream]{ | 
|  | 414 | ALOGD("Finalizing multi frame SKP"); | 
|  | 415 | doc->close(); | 
|  | 416 | delete stream; | 
|  | 417 | ALOGD("Multi frame SKP complete."); | 
|  | 418 | }); | 
|  | 419 | } | 
|  | 420 | } else { | 
| Stan Iliev | e9d0012 | 2017-09-19 12:07:10 -0400 | [diff] [blame] | 421 | sk_sp<SkPicture> picture = mRecorder->finishRecordingAsPicture(); | 
| Stan Iliev | e9d0012 | 2017-09-19 12:07:10 -0400 | [diff] [blame] | 422 | if (picture->approximateOpCount() > 0) { | 
| John Reck | 5cca8f2 | 2018-12-10 17:06:22 -0800 | [diff] [blame] | 423 | if (mPictureCapturedCallback) { | 
|  | 424 | std::invoke(mPictureCapturedCallback, std::move(picture)); | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 425 | } else { | 
|  | 426 | // single frame skp to file | 
| Nathaniel Nifong | 429fff4 | 2020-01-30 14:38:21 -0500 | [diff] [blame] | 427 | SkSerialProcs procs; | 
|  | 428 | procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){ | 
|  | 429 | return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData); | 
|  | 430 | }; | 
| John Reck | 7600518 | 2021-06-09 22:43:05 -0400 | [diff] [blame] | 431 | auto data = picture->serialize(&procs); | 
| Nathaniel Nifong | d2e49a2 | 2019-06-24 15:07:34 -0400 | [diff] [blame] | 432 | savePictureAsync(data, mCapturedFile); | 
|  | 433 | mCaptureSequence = 0; | 
| Derek Sollenberger | 5f9753d | 2020-04-01 15:59:02 -0400 | [diff] [blame] | 434 | mCaptureMode = CaptureMode::None; | 
| Stan Iliev | e9d0012 | 2017-09-19 12:07:10 -0400 | [diff] [blame] | 435 | } | 
| Stan Iliev | e9d0012 | 2017-09-19 12:07:10 -0400 | [diff] [blame] | 436 | } | 
|  | 437 | mRecorder.reset(); | 
|  | 438 | } | 
|  | 439 | } | 
|  | 440 |  | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 441 | void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip, | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 442 | const std::vector<sp<RenderNode>>& nodes, bool opaque, | 
| Greg Daniel | c407678 | 2019-01-08 16:01:18 -0500 | [diff] [blame] | 443 | const Rect& contentDrawBounds, sk_sp<SkSurface> surface, | 
|  | 444 | const SkMatrix& preTransform) { | 
| John Reck | 5cca8f2 | 2018-12-10 17:06:22 -0800 | [diff] [blame] | 445 | bool previousSkpEnabled = Properties::skpCaptureEnabled; | 
|  | 446 | if (mPictureCapturedCallback) { | 
|  | 447 | Properties::skpCaptureEnabled = true; | 
|  | 448 | } | 
|  | 449 |  | 
| Nathaniel Nifong | 2945bff | 2019-11-25 09:34:21 -0500 | [diff] [blame] | 450 | // Initialize the canvas for the current frame, that might be a recording canvas if SKP | 
|  | 451 | // capture is enabled. | 
|  | 452 | SkCanvas* canvas = tryCapture(surface.get(), nodes[0].get(), layers); | 
|  | 453 |  | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 454 | // draw all layers up front | 
| Peiyong Lin | 1f6aa12 | 2018-09-10 16:28:08 -0700 | [diff] [blame] | 455 | renderLayersImpl(layers, opaque); | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 456 |  | 
| Nathaniel Nifong | dc19a65 | 2019-11-11 11:47:50 -0500 | [diff] [blame] | 457 | renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform); | 
| Matt Sarett | f58cc92 | 2016-11-14 18:33:38 -0500 | [diff] [blame] | 458 |  | 
| Stan Iliev | e9d0012 | 2017-09-19 12:07:10 -0400 | [diff] [blame] | 459 | endCapture(surface.get()); | 
| Matt Sarett | f58cc92 | 2016-11-14 18:33:38 -0500 | [diff] [blame] | 460 |  | 
|  | 461 | if (CC_UNLIKELY(Properties::debugOverdraw)) { | 
| Nathaniel Nifong | dc19a65 | 2019-11-11 11:47:50 -0500 | [diff] [blame] | 462 | renderOverdraw(clip, nodes, contentDrawBounds, surface, preTransform); | 
| Matt Sarett | f58cc92 | 2016-11-14 18:33:38 -0500 | [diff] [blame] | 463 | } | 
|  | 464 |  | 
| John Reck | 5cca8f2 | 2018-12-10 17:06:22 -0800 | [diff] [blame] | 465 | Properties::skpCaptureEnabled = previousSkpEnabled; | 
| Matt Sarett | f58cc92 | 2016-11-14 18:33:38 -0500 | [diff] [blame] | 466 | } | 
|  | 467 |  | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 468 | namespace { | 
|  | 469 | static Rect nodeBounds(RenderNode& node) { | 
|  | 470 | auto& props = node.properties(); | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 471 | return Rect(props.getLeft(), props.getTop(), props.getRight(), props.getBottom()); | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 472 | } | 
| John Reck | 0fa0cbc | 2019-04-05 16:57:46 -0700 | [diff] [blame] | 473 | }  // namespace | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 474 |  | 
| Nathaniel Nifong | dc19a65 | 2019-11-11 11:47:50 -0500 | [diff] [blame] | 475 | void SkiaPipeline::renderFrameImpl(const SkRect& clip, | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 476 | const std::vector<sp<RenderNode>>& nodes, bool opaque, | 
| Greg Daniel | c407678 | 2019-01-08 16:01:18 -0500 | [diff] [blame] | 477 | const Rect& contentDrawBounds, SkCanvas* canvas, | 
|  | 478 | const SkMatrix& preTransform) { | 
| Stan Iliev | b66b8bb | 2016-12-15 18:17:42 -0500 | [diff] [blame] | 479 | SkAutoCanvasRestore saver(canvas, true); | 
| Nathaniel Nifong | cff969f | 2020-01-09 14:03:49 -0500 | [diff] [blame] | 480 | auto clipRestriction = preTransform.mapRect(clip).roundOut(); | 
| John Reck | 7600518 | 2021-06-09 22:43:05 -0400 | [diff] [blame] | 481 | if (CC_UNLIKELY(isCapturingSkp())) { | 
| Nathaniel Nifong | 89a5dc5 | 2020-01-17 15:32:17 -0500 | [diff] [blame] | 482 | canvas->drawAnnotation(SkRect::Make(clipRestriction), "AndroidDeviceClipRestriction", | 
|  | 483 | nullptr); | 
|  | 484 | } else { | 
|  | 485 | // clip drawing to dirty region only when not recording SKP files (which should contain all | 
|  | 486 | // draw ops on every frame) | 
|  | 487 | canvas->androidFramework_setDeviceClipRestriction(clipRestriction); | 
|  | 488 | } | 
| Greg Daniel | c407678 | 2019-01-08 16:01:18 -0500 | [diff] [blame] | 489 | canvas->concat(preTransform); | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 490 |  | 
| Stan Iliev | aadc032 | 2018-03-23 10:50:11 -0400 | [diff] [blame] | 491 | // STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293 | 
| Peiyong Lin | 1f6aa12 | 2018-09-10 16:28:08 -0700 | [diff] [blame] | 492 | if (!opaque || getSurfaceColorType() == kRGBA_F16_SkColorType) { | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 493 | canvas->clear(SK_ColorTRANSPARENT); | 
|  | 494 | } | 
|  | 495 |  | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 496 | if (1 == nodes.size()) { | 
|  | 497 | if (!nodes[0]->nothingToDraw()) { | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 498 | RenderNodeDrawable root(nodes[0].get(), canvas); | 
|  | 499 | root.draw(canvas); | 
|  | 500 | } | 
|  | 501 | } else if (0 == nodes.size()) { | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 502 | // nothing to draw | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 503 | } else { | 
|  | 504 | // It there are multiple render nodes, they are laid out as follows: | 
|  | 505 | // #0 - backdrop (content + caption) | 
|  | 506 | // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop) | 
|  | 507 | // #2 - additional overlay nodes | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 508 | // Usually the backdrop cannot be seen since it will be entirely covered by the content. | 
|  | 509 | // While | 
|  | 510 | // resizing however it might become partially visible. The following render loop will crop | 
|  | 511 | // the | 
|  | 512 | // backdrop against the content and draw the remaining part of it. It will then draw the | 
|  | 513 | // content | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 514 | // cropped to the backdrop (since that indicates a shrinking of the window). | 
|  | 515 | // | 
|  | 516 | // Additional nodes will be drawn on top with no particular clipping semantics. | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 517 |  | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 518 | // Usually the contents bounds should be mContentDrawBounds - however - we will | 
|  | 519 | // move it towards the fixed edge to give it a more stable appearance (for the moment). | 
|  | 520 | // If there is no content bounds we ignore the layering as stated above and start with 2. | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 521 |  | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 522 | // Backdrop bounds in render target space | 
|  | 523 | const Rect backdrop = nodeBounds(*nodes[0]); | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 524 |  | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 525 | // Bounds that content will fill in render target space (note content node bounds may be | 
|  | 526 | // bigger) | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 527 | Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight()); | 
|  | 528 | content.translate(backdrop.left, backdrop.top); | 
|  | 529 | if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) { | 
|  | 530 | // Content doesn't entirely overlap backdrop, so fill around content (right/bottom) | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 531 |  | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 532 | // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 533 | // also fill left/top. Currently, both 2up and freeform position content at the top/left | 
|  | 534 | // of | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 535 | // the backdrop, so this isn't necessary. | 
|  | 536 | RenderNodeDrawable backdropNode(nodes[0].get(), canvas); | 
|  | 537 | if (content.right < backdrop.right) { | 
|  | 538 | // draw backdrop to right side of content | 
|  | 539 | SkAutoCanvasRestore acr(canvas, true); | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 540 | canvas->clipRect(SkRect::MakeLTRB(content.right, backdrop.top, backdrop.right, | 
|  | 541 | backdrop.bottom)); | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 542 | backdropNode.draw(canvas); | 
|  | 543 | } | 
|  | 544 | if (content.bottom < backdrop.bottom) { | 
|  | 545 | // draw backdrop to bottom of content | 
|  | 546 | // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill | 
|  | 547 | SkAutoCanvasRestore acr(canvas, true); | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 548 | canvas->clipRect(SkRect::MakeLTRB(content.left, content.bottom, content.right, | 
|  | 549 | backdrop.bottom)); | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 550 | backdropNode.draw(canvas); | 
|  | 551 | } | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 552 | } | 
|  | 553 |  | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 554 | RenderNodeDrawable contentNode(nodes[1].get(), canvas); | 
|  | 555 | if (!backdrop.isEmpty()) { | 
|  | 556 | // content node translation to catch up with backdrop | 
|  | 557 | float dx = backdrop.left - contentDrawBounds.left; | 
|  | 558 | float dy = backdrop.top - contentDrawBounds.top; | 
|  | 559 |  | 
|  | 560 | SkAutoCanvasRestore acr(canvas, true); | 
|  | 561 | canvas->translate(dx, dy); | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 562 | const SkRect contentLocalClip = | 
|  | 563 | SkRect::MakeXYWH(contentDrawBounds.left, contentDrawBounds.top, | 
|  | 564 | backdrop.getWidth(), backdrop.getHeight()); | 
| Stan Iliev | 5277127 | 2016-11-17 09:54:38 -0500 | [diff] [blame] | 565 | canvas->clipRect(contentLocalClip); | 
|  | 566 | contentNode.draw(canvas); | 
|  | 567 | } else { | 
|  | 568 | SkAutoCanvasRestore acr(canvas, true); | 
|  | 569 | contentNode.draw(canvas); | 
|  | 570 | } | 
|  | 571 |  | 
|  | 572 | // remaining overlay nodes, simply defer | 
|  | 573 | for (size_t index = 2; index < nodes.size(); index++) { | 
|  | 574 | if (!nodes[index]->nothingToDraw()) { | 
|  | 575 | SkAutoCanvasRestore acr(canvas, true); | 
|  | 576 | RenderNodeDrawable overlayNode(nodes[index].get(), canvas); | 
|  | 577 | overlayNode.draw(canvas); | 
|  | 578 | } | 
|  | 579 | } | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 580 | } | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 581 | } | 
|  | 582 |  | 
| Matt Sarett | 4bda6bf | 2016-11-07 15:43:41 -0500 | [diff] [blame] | 583 | void SkiaPipeline::dumpResourceCacheUsage() const { | 
| Robert Phillips | 57bb0bf | 2019-09-06 13:18:17 -0400 | [diff] [blame] | 584 | int resources; | 
|  | 585 | size_t bytes; | 
| Matt Sarett | 4bda6bf | 2016-11-07 15:43:41 -0500 | [diff] [blame] | 586 | mRenderThread.getGrContext()->getResourceCacheUsage(&resources, &bytes); | 
| Robert Phillips | 57bb0bf | 2019-09-06 13:18:17 -0400 | [diff] [blame] | 587 | size_t maxBytes = mRenderThread.getGrContext()->getResourceCacheLimit(); | 
| Matt Sarett | 4bda6bf | 2016-11-07 15:43:41 -0500 | [diff] [blame] | 588 |  | 
|  | 589 | SkString log("Resource Cache Usage:\n"); | 
| Robert Phillips | 57bb0bf | 2019-09-06 13:18:17 -0400 | [diff] [blame] | 590 | log.appendf("%8d items\n", resources); | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 591 | log.appendf("%8zu bytes (%.2f MB) out of %.2f MB maximum\n", bytes, | 
|  | 592 | bytes * (1.0f / (1024.0f * 1024.0f)), maxBytes * (1.0f / (1024.0f * 1024.0f))); | 
| Matt Sarett | 4bda6bf | 2016-11-07 15:43:41 -0500 | [diff] [blame] | 593 |  | 
|  | 594 | ALOGD("%s", log.c_str()); | 
|  | 595 | } | 
|  | 596 |  | 
| Peiyong Lin | 3bff135 | 2018-12-11 07:56:07 -0800 | [diff] [blame] | 597 | void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) { | 
| Derek Sollenberger | 1863d94 | 2020-02-05 15:41:51 -0500 | [diff] [blame] | 598 | mColorMode = colorMode; | 
| John Reck | b36bfdd | 2020-07-23 13:47:49 -0700 | [diff] [blame] | 599 | switch (colorMode) { | 
|  | 600 | case ColorMode::Default: | 
|  | 601 | mSurfaceColorType = SkColorType::kN32_SkColorType; | 
|  | 602 | mSurfaceColorSpace = SkColorSpace::MakeSRGB(); | 
|  | 603 | break; | 
|  | 604 | case ColorMode::WideColorGamut: | 
|  | 605 | mSurfaceColorType = DeviceInfo::get()->getWideColorType(); | 
|  | 606 | mSurfaceColorSpace = DeviceInfo::get()->getWideColorSpace(); | 
|  | 607 | break; | 
|  | 608 | case ColorMode::Hdr: | 
|  | 609 | mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType; | 
|  | 610 | mSurfaceColorSpace = SkColorSpace::MakeRGB(GetPQSkTransferFunction(), SkNamedGamut::kRec2020); | 
|  | 611 | break; | 
|  | 612 | case ColorMode::Hdr10: | 
|  | 613 | mSurfaceColorType = SkColorType::kRGBA_1010102_SkColorType; | 
|  | 614 | mSurfaceColorSpace = SkColorSpace::MakeRGB(GetPQSkTransferFunction(), SkNamedGamut::kRec2020); | 
|  | 615 | break; | 
| Leon Scroggins III | cbdbb66 | 2021-11-30 13:59:00 -0500 | [diff] [blame] | 616 | case ColorMode::A8: | 
|  | 617 | mSurfaceColorType = SkColorType::kAlpha_8_SkColorType; | 
|  | 618 | mSurfaceColorSpace = nullptr; | 
|  | 619 | break; | 
| Peiyong Lin | 3bff135 | 2018-12-11 07:56:07 -0800 | [diff] [blame] | 620 | } | 
|  | 621 | } | 
|  | 622 |  | 
| Matt Sarett | f58cc92 | 2016-11-14 18:33:38 -0500 | [diff] [blame] | 623 | // Overdraw debugging | 
|  | 624 |  | 
|  | 625 | // These colors should be kept in sync with Caches::getOverdrawColor() with a few differences. | 
| Mike Reed | 331c4e1 | 2020-02-13 10:21:54 -0500 | [diff] [blame] | 626 | // This implementation requires transparent entries for "no overdraw" and "single draws". | 
|  | 627 | static const SkColor kOverdrawColors[2][6] = { | 
|  | 628 | { | 
|  | 629 | 0x00000000, | 
|  | 630 | 0x00000000, | 
|  | 631 | 0x2f0000ff, | 
|  | 632 | 0x2f00ff00, | 
|  | 633 | 0x3fff0000, | 
|  | 634 | 0x7fff0000, | 
|  | 635 | }, | 
|  | 636 | { | 
|  | 637 | 0x00000000, | 
|  | 638 | 0x00000000, | 
|  | 639 | 0x2f0000ff, | 
|  | 640 | 0x4fffff00, | 
|  | 641 | 0x5fff89d7, | 
|  | 642 | 0x7fff0000, | 
|  | 643 | }, | 
| Matt Sarett | f58cc92 | 2016-11-14 18:33:38 -0500 | [diff] [blame] | 644 | }; | 
|  | 645 |  | 
| Nathaniel Nifong | dc19a65 | 2019-11-11 11:47:50 -0500 | [diff] [blame] | 646 | void SkiaPipeline::renderOverdraw(const SkRect& clip, | 
| John Reck | 1bcacfd | 2017-11-03 10:12:19 -0700 | [diff] [blame] | 647 | const std::vector<sp<RenderNode>>& nodes, | 
| Greg Daniel | c407678 | 2019-01-08 16:01:18 -0500 | [diff] [blame] | 648 | const Rect& contentDrawBounds, sk_sp<SkSurface> surface, | 
|  | 649 | const SkMatrix& preTransform) { | 
| Matt Sarett | f58cc92 | 2016-11-14 18:33:38 -0500 | [diff] [blame] | 650 | // Set up the overdraw canvas. | 
|  | 651 | SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height()); | 
|  | 652 | sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo); | 
| Nathaniel Nifong | fe05b7c | 2019-09-17 12:52:52 -0400 | [diff] [blame] | 653 | LOG_ALWAYS_FATAL_IF(!offscreen, "Failed to create offscreen SkSurface for overdraw viz."); | 
| Matt Sarett | f58cc92 | 2016-11-14 18:33:38 -0500 | [diff] [blame] | 654 | SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas()); | 
|  | 655 |  | 
|  | 656 | // Fake a redraw to replay the draw commands.  This will increment the alpha channel | 
|  | 657 | // each time a pixel would have been drawn. | 
|  | 658 | // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero | 
|  | 659 | // initialized. | 
| Nathaniel Nifong | dc19a65 | 2019-11-11 11:47:50 -0500 | [diff] [blame] | 660 | renderFrameImpl(clip, nodes, true, contentDrawBounds, &overdrawCanvas, preTransform); | 
| Matt Sarett | f58cc92 | 2016-11-14 18:33:38 -0500 | [diff] [blame] | 661 | sk_sp<SkImage> counts = offscreen->makeImageSnapshot(); | 
|  | 662 |  | 
|  | 663 | // Draw overdraw colors to the canvas.  The color filter will convert counts to colors. | 
|  | 664 | SkPaint paint; | 
| Mike Reed | 331c4e1 | 2020-02-13 10:21:54 -0500 | [diff] [blame] | 665 | const SkColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)]; | 
|  | 666 | paint.setColorFilter(SkOverdrawColorFilter::MakeWithSkColors(colors)); | 
| Mike Reed | 7994a31 | 2021-01-28 18:06:26 -0500 | [diff] [blame] | 667 | surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, SkSamplingOptions(), &paint); | 
| Matt Sarett | f58cc92 | 2016-11-14 18:33:38 -0500 | [diff] [blame] | 668 | } | 
|  | 669 |  | 
| Stan Iliev | 500a0c3 | 2016-10-26 10:30:09 -0400 | [diff] [blame] | 670 | } /* namespace skiapipeline */ | 
|  | 671 | } /* namespace uirenderer */ | 
|  | 672 | } /* namespace android */ |