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 <SkImageEncoder.h>
20 #include <SkImageInfo.h>
21 #include <SkImagePriv.h>
22 #include <SkMultiPictureDocument.h>
23 #include <SkOverdrawCanvas.h>
24 #include <SkOverdrawColorFilter.h>
25 #include <SkPicture.h>
26 #include <SkPictureRecorder.h>
27 #include <SkSerialProcs.h>
28 #include <SkTypeface.h>
29 #include <android-base/properties.h>
30 #include <unistd.h>
31
32 #include <sstream>
33
34 #include <gui/TraceUtils.h>
35 #include "LightingInfo.h"
36 #include "VectorDrawable.h"
37 #include "thread/CommonPool.h"
38 #include "tools/SkSharingProc.h"
39 #include "utils/Color.h"
40 #include "utils/String8.h"
41
42 using namespace android::uirenderer::renderthread;
43
44 namespace android {
45 namespace uirenderer {
46 namespace skiapipeline {
47
SkiaPipeline(RenderThread & thread)48 SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) {
49 setSurfaceColorProperties(mColorMode);
50 }
51
~SkiaPipeline()52 SkiaPipeline::~SkiaPipeline() {
53 unpinImages();
54 }
55
onDestroyHardwareResources()56 void SkiaPipeline::onDestroyHardwareResources() {
57 unpinImages();
58 mRenderThread.cacheManager().trimStaleResources();
59 }
60
pinImages(std::vector<SkImage * > & mutableImages)61 bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) {
62 if (!mRenderThread.getGrContext()) {
63 ALOGD("Trying to pin an image with an invalid GrContext");
64 return false;
65 }
66 for (SkImage* image : mutableImages) {
67 if (SkImage_pinAsTexture(image, mRenderThread.getGrContext())) {
68 mPinnedImages.emplace_back(sk_ref_sp(image));
69 } else {
70 return false;
71 }
72 }
73 return true;
74 }
75
unpinImages()76 void SkiaPipeline::unpinImages() {
77 for (auto& image : mPinnedImages) {
78 SkImage_unpinAsTexture(image.get(), mRenderThread.getGrContext());
79 }
80 mPinnedImages.clear();
81 }
82
renderLayers(const LightGeometry & lightGeometry,LayerUpdateQueue * layerUpdateQueue,bool opaque,const LightInfo & lightInfo)83 void SkiaPipeline::renderLayers(const LightGeometry& lightGeometry,
84 LayerUpdateQueue* layerUpdateQueue, bool opaque,
85 const LightInfo& lightInfo) {
86 LightingInfo::updateLighting(lightGeometry, lightInfo);
87 ATRACE_NAME("draw layers");
88 renderLayersImpl(*layerUpdateQueue, opaque);
89 layerUpdateQueue->clear();
90 }
91
renderLayersImpl(const LayerUpdateQueue & layers,bool opaque)92 void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
93 sk_sp<GrDirectContext> cachedContext;
94
95 // Render all layers that need to be updated, in order.
96 for (size_t i = 0; i < layers.entries().size(); i++) {
97 RenderNode* layerNode = layers.entries()[i].renderNode.get();
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
101 if (CC_UNLIKELY(layerNode->getLayerSurface() == nullptr)) {
102 continue;
103 }
104 SkASSERT(layerNode->getLayerSurface());
105 SkiaDisplayList* displayList = layerNode->getDisplayList().asSkiaDl();
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
149 GrDirectContext* currentContext =
150 GrAsDirectContext(layerNode->getLayerSurface()->getCanvas()->recordingContext());
151 if (cachedContext.get() != currentContext) {
152 if (cachedContext.get()) {
153 ATRACE_NAME("flush layers (context changed)");
154 cachedContext->flushAndSubmit();
155 }
156 cachedContext.reset(SkSafeRef(currentContext));
157 }
158 }
159
160 if (cachedContext.get()) {
161 ATRACE_NAME("flush layers");
162 cachedContext->flushAndSubmit();
163 }
164 }
165
createOrUpdateLayer(RenderNode * node,const DamageAccumulator & damageAccumulator,ErrorHandler * errorHandler)166 bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
167 ErrorHandler* errorHandler) {
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
172 SkSurface* layer = node->getLayerSurface();
173 if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) {
174 SkImageInfo info;
175 info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(),
176 kPremul_SkAlphaType, getSurfaceColorSpace());
177 SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
178 SkASSERT(mRenderThread.getGrContext() != nullptr);
179 node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
180 SkBudgeted::kYes, info, 0,
181 this->getSurfaceOrigin(), &props));
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);
187 node->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
188 } else {
189 String8 cachesOutput;
190 mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput,
191 &mRenderThread.renderState());
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 "
198 << maxTextureSize << " color type " << (int)info.colorType() << " has context "
199 << (int)(mRenderThread.getGrContext() != nullptr);
200 errorHandler->onError(err.str());
201 }
202 }
203 return true;
204 }
205 return false;
206 }
207
prepareToDraw(const RenderThread & thread,Bitmap * bitmap)208 void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
209 GrDirectContext* context = thread.getGrContext();
210 if (context && !bitmap->isHardware()) {
211 ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
212 auto image = bitmap->makeImage();
213 if (image.get()) {
214 SkImage_pinAsTexture(image.get(), context);
215 SkImage_unpinAsTexture(image.get(), context);
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();
220 }
221 }
222 }
223
savePictureAsync(const sk_sp<SkData> & data,const std::string & filename)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)) {
227 return;
228 }
229
230 SkFILEWStream stream(filename.c_str());
231 if (stream.isValid()) {
232 stream.write(data->data(), data->size());
233 stream.flush();
234 ALOGD("SKP Captured Drawing Output (%zu bytes) for frame. %s", stream.bytesWritten(),
235 filename.c_str());
236 }
237 });
238 }
239
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.
shouldStartNewFileCapture()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);
257 if (mCaptureSequence <= 0) {
258 return false;
259 } else if (mCaptureSequence == 1) {
260 mCaptureMode = CaptureMode::SingleFrameSKP;
261 } else {
262 mCaptureMode = CaptureMode::MultiFrameSKP;
263 }
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.
setupMultiFrameCapture()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();
280 procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){
281 return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
282 };
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.
287 mMultiPic = SkMakeMultiPictureDocument(mOpenMultiPicStream.get(), &procs,
288 [sharingCtx = mSerialContext.get()](const SkPicture* pic) {
289 SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx);
290 });
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
300 // recurse through the rendernode's children, add any nodes which are layers to the queue.
collectLayers(RenderNode * node,LayerUpdateQueue * layers)301 static void collectLayers(RenderNode* node, LayerUpdateQueue* layers) {
302 SkiaDisplayList* dl = node->getDisplayList().asSkiaDl();
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.
recordLayers(const LayerUpdateQueue & layers,SkCanvas * mskpCanvas)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
tryCapture(SkSurface * surface,RenderNode * root,const LayerUpdateQueue & dirtyLayers)343 SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface, RenderNode* root,
344 const LayerUpdateQueue& dirtyLayers) {
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.
349 bool firstFrameOfAnim = false;
350 if (shouldStartNewFileCapture() && mCaptureMode == CaptureMode::MultiFrameSKP) {
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;
354 if (!setupMultiFrameCapture()) {
355 return surface->getCanvas();
356 }
357 }
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());
365 pictureCanvas = mRecorder->beginRecording(surface->width(), surface->height());
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);
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
396 return mNwayCanvas.get();
397 }
398
endCapture(SkSurface * surface)399 void SkiaPipeline::endCapture(SkSurface* surface) {
400 if (CC_LIKELY(mCaptureMode == CaptureMode::None)) { return; }
401 mNwayCanvas.reset();
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 {
421 sk_sp<SkPicture> picture = mRecorder->finishRecordingAsPicture();
422 if (picture->approximateOpCount() > 0) {
423 if (mPictureCapturedCallback) {
424 std::invoke(mPictureCapturedCallback, std::move(picture));
425 } else {
426 // single frame skp to file
427 SkSerialProcs procs;
428 procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){
429 return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
430 };
431 auto data = picture->serialize(&procs);
432 savePictureAsync(data, mCapturedFile);
433 mCaptureSequence = 0;
434 mCaptureMode = CaptureMode::None;
435 }
436 }
437 mRecorder.reset();
438 }
439 }
440
renderFrame(const LayerUpdateQueue & layers,const SkRect & clip,const std::vector<sp<RenderNode>> & nodes,bool opaque,const Rect & contentDrawBounds,sk_sp<SkSurface> surface,const SkMatrix & preTransform)441 void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
442 const std::vector<sp<RenderNode>>& nodes, bool opaque,
443 const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
444 const SkMatrix& preTransform) {
445 bool previousSkpEnabled = Properties::skpCaptureEnabled;
446 if (mPictureCapturedCallback) {
447 Properties::skpCaptureEnabled = true;
448 }
449
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
454 // draw all layers up front
455 renderLayersImpl(layers, opaque);
456
457 renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform);
458
459 endCapture(surface.get());
460
461 if (CC_UNLIKELY(Properties::debugOverdraw)) {
462 renderOverdraw(clip, nodes, contentDrawBounds, surface, preTransform);
463 }
464
465 Properties::skpCaptureEnabled = previousSkpEnabled;
466 }
467
468 namespace {
nodeBounds(RenderNode & node)469 static Rect nodeBounds(RenderNode& node) {
470 auto& props = node.properties();
471 return Rect(props.getLeft(), props.getTop(), props.getRight(), props.getBottom());
472 }
473 } // namespace
474
renderFrameImpl(const SkRect & clip,const std::vector<sp<RenderNode>> & nodes,bool opaque,const Rect & contentDrawBounds,SkCanvas * canvas,const SkMatrix & preTransform)475 void SkiaPipeline::renderFrameImpl(const SkRect& clip,
476 const std::vector<sp<RenderNode>>& nodes, bool opaque,
477 const Rect& contentDrawBounds, SkCanvas* canvas,
478 const SkMatrix& preTransform) {
479 SkAutoCanvasRestore saver(canvas, true);
480 auto clipRestriction = preTransform.mapRect(clip).roundOut();
481 if (CC_UNLIKELY(isCapturingSkp())) {
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 }
489 canvas->concat(preTransform);
490
491 // STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293
492 if (!opaque || getSurfaceColorType() == kRGBA_F16_SkColorType) {
493 canvas->clear(SK_ColorTRANSPARENT);
494 }
495
496 if (1 == nodes.size()) {
497 if (!nodes[0]->nothingToDraw()) {
498 RenderNodeDrawable root(nodes[0].get(), canvas);
499 root.draw(canvas);
500 }
501 } else if (0 == nodes.size()) {
502 // nothing to draw
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
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
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.
517
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.
521
522 // Backdrop bounds in render target space
523 const Rect backdrop = nodeBounds(*nodes[0]);
524
525 // Bounds that content will fill in render target space (note content node bounds may be
526 // bigger)
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)
531
532 // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to
533 // also fill left/top. Currently, both 2up and freeform position content at the top/left
534 // of
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);
540 canvas->clipRect(SkRect::MakeLTRB(content.right, backdrop.top, backdrop.right,
541 backdrop.bottom));
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);
548 canvas->clipRect(SkRect::MakeLTRB(content.left, content.bottom, content.right,
549 backdrop.bottom));
550 backdropNode.draw(canvas);
551 }
552 }
553
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);
562 const SkRect contentLocalClip =
563 SkRect::MakeXYWH(contentDrawBounds.left, contentDrawBounds.top,
564 backdrop.getWidth(), backdrop.getHeight());
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 }
580 }
581 }
582
dumpResourceCacheUsage() const583 void SkiaPipeline::dumpResourceCacheUsage() const {
584 int resources;
585 size_t bytes;
586 mRenderThread.getGrContext()->getResourceCacheUsage(&resources, &bytes);
587 size_t maxBytes = mRenderThread.getGrContext()->getResourceCacheLimit();
588
589 SkString log("Resource Cache Usage:\n");
590 log.appendf("%8d items\n", resources);
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)));
593
594 ALOGD("%s", log.c_str());
595 }
596
setSurfaceColorProperties(ColorMode colorMode)597 void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) {
598 mColorMode = colorMode;
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;
616 case ColorMode::A8:
617 mSurfaceColorType = SkColorType::kAlpha_8_SkColorType;
618 mSurfaceColorSpace = nullptr;
619 break;
620 }
621 }
622
623 // Overdraw debugging
624
625 // These colors should be kept in sync with Caches::getOverdrawColor() with a few differences.
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 },
644 };
645
renderOverdraw(const SkRect & clip,const std::vector<sp<RenderNode>> & nodes,const Rect & contentDrawBounds,sk_sp<SkSurface> surface,const SkMatrix & preTransform)646 void SkiaPipeline::renderOverdraw(const SkRect& clip,
647 const std::vector<sp<RenderNode>>& nodes,
648 const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
649 const SkMatrix& preTransform) {
650 // Set up the overdraw canvas.
651 SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height());
652 sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo);
653 LOG_ALWAYS_FATAL_IF(!offscreen, "Failed to create offscreen SkSurface for overdraw viz.");
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.
660 renderFrameImpl(clip, nodes, true, contentDrawBounds, &overdrawCanvas, preTransform);
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;
665 const SkColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)];
666 paint.setColorFilter(SkOverdrawColorFilter::MakeWithSkColors(colors));
667 surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, SkSamplingOptions(), &paint);
668 }
669
670 } /* namespace skiapipeline */
671 } /* namespace uirenderer */
672 } /* namespace android */
673