blob: 2cfdd3fb0315048f64c2119d0f7deb7eadf5c803 [file] [log] [blame]
Stan Iliev500a0c32016-10-26 10:30:09 -04001/*
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
Jerome Gaillard09a38e42024-03-14 15:20:56 +000017#include "pipeline/skia/SkiaPipeline.h"
Stan Iliev500a0c32016-10-26 10:30:09 -040018
Kevin Lubick1175dc02022-02-28 12:41:27 -050019#include <SkCanvas.h>
20#include <SkColor.h>
21#include <SkColorSpace.h>
22#include <SkData.h>
23#include <SkImage.h>
Kevin Lubick93671082023-03-13 18:30:55 +000024#include <SkImageAndroid.h>
Peiyong Lin3bff1352018-12-11 07:56:07 -080025#include <SkImageInfo.h>
Kevin Lubick1175dc02022-02-28 12:41:27 -050026#include <SkMatrix.h>
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -040027#include <SkMultiPictureDocument.h>
Matt Sarettf58cc922016-11-14 18:33:38 -050028#include <SkOverdrawCanvas.h>
29#include <SkOverdrawColorFilter.h>
Stan Iliev500a0c32016-10-26 10:30:09 -040030#include <SkPicture.h>
31#include <SkPictureRecorder.h>
Kevin Lubick1175dc02022-02-28 12:41:27 -050032#include <SkRect.h>
33#include <SkRefCnt.h>
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -040034#include <SkSerialProcs.h>
Kevin Lubick1175dc02022-02-28 12:41:27 -050035#include <SkStream.h>
36#include <SkString.h>
Alec Mouri43fe6fc2019-12-23 07:46:19 -080037#include <SkTypeface.h>
38#include <android-base/properties.h>
John Reck29b1ee02023-04-04 17:44:23 -040039#include <gui/TraceUtils.h>
Jerome Gaillard09a38e42024-03-14 15:20:56 +000040#include <include/android/SkSurfaceAndroid.h>
41#include <include/encode/SkPngEncoder.h>
42#include <include/gpu/ganesh/SkSurfaceGanesh.h>
Alec Mouri43fe6fc2019-12-23 07:46:19 -080043#include <unistd.h>
44
45#include <sstream>
46
Fedor Kudasov90df0562019-06-19 11:41:34 +010047#include "LightingInfo.h"
Stan Iliev23c38a92017-03-23 00:12:50 -040048#include "VectorDrawable.h"
John Reck29b1ee02023-04-04 17:44:23 -040049#include "include/gpu/GpuTypes.h" // from Skia
John Reck322b8ab2019-03-14 13:15:28 -070050#include "thread/CommonPool.h"
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -040051#include "tools/SkSharingProc.h"
John Reckb36bfdd2020-07-23 13:47:49 -070052#include "utils/Color.h"
Nathaniel Nifong2945bff2019-11-25 09:34:21 -050053#include "utils/String8.h"
Jerome Gaillarda02a12d2019-05-28 18:07:56 +010054
Stan Iliev500a0c32016-10-26 10:30:09 -040055using namespace android::uirenderer::renderthread;
56
57namespace android {
58namespace uirenderer {
59namespace skiapipeline {
60
John Reck1bcacfd2017-11-03 10:12:19 -070061SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) {
Derek Sollenberger1863d942020-02-05 15:41:51 -050062 setSurfaceColorProperties(mColorMode);
Stan Iliev23c38a92017-03-23 00:12:50 -040063}
Stan Iliev500a0c32016-10-26 10:30:09 -040064
Jerome Gaillard09a38e42024-03-14 15:20:56 +000065SkiaPipeline::~SkiaPipeline() {}
Stan Iliev232f3622017-08-23 17:15:09 -040066
Stan Iliev500a0c32016-10-26 10:30:09 -040067void SkiaPipeline::onDestroyHardwareResources() {
Derek Sollenberger92a9eb92018-04-12 13:42:19 -040068 unpinImages();
Derek Sollenbergerf9e45d12017-06-01 13:07:39 -040069 mRenderThread.cacheManager().trimStaleResources();
Stan Iliev500a0c32016-10-26 10:30:09 -040070}
71
John Reckd9d7f122018-05-03 14:40:56 -070072void SkiaPipeline::renderLayers(const LightGeometry& lightGeometry,
John Reck1bcacfd2017-11-03 10:12:19 -070073 LayerUpdateQueue* layerUpdateQueue, bool opaque,
Peiyong Lin1f6aa122018-09-10 16:28:08 -070074 const LightInfo& lightInfo) {
Fedor Kudasov90df0562019-06-19 11:41:34 +010075 LightingInfo::updateLighting(lightGeometry, lightInfo);
Stan Iliev500a0c32016-10-26 10:30:09 -040076 ATRACE_NAME("draw layers");
Peiyong Lin1f6aa122018-09-10 16:28:08 -070077 renderLayersImpl(*layerUpdateQueue, opaque);
Stan Iliev500a0c32016-10-26 10:30:09 -040078 layerUpdateQueue->clear();
79}
80
Jerome Gaillard09a38e42024-03-14 15:20:56 +000081void SkiaPipeline::renderLayerImpl(RenderNode* layerNode, const Rect& layerDamage) {
82 // only schedule repaint if node still on layer - possible it may have been
83 // removed during a dropped frame, but layers may still remain scheduled so
84 // as not to lose info on what portion is damaged
85 if (CC_UNLIKELY(layerNode->getLayerSurface() == nullptr)) {
86 return;
87 }
88 SkASSERT(layerNode->getLayerSurface());
89 SkiaDisplayList* displayList = layerNode->getDisplayList().asSkiaDl();
90 if (!displayList || displayList->isEmpty()) {
91 ALOGE("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName());
92 return;
Stan Iliev500a0c32016-10-26 10:30:09 -040093 }
Derek Sollenbergerf7df1842017-09-05 11:15:58 -040094
Jerome Gaillard09a38e42024-03-14 15:20:56 +000095 SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas();
Stan Iliev500a0c32016-10-26 10:30:09 -040096
Jerome Gaillard09a38e42024-03-14 15:20:56 +000097 int saveCount = layerCanvas->save();
98 SkASSERT(saveCount == 1);
Derek Sollenberger03e6cff72017-12-04 15:07:08 -050099
Jerome Gaillard09a38e42024-03-14 15:20:56 +0000100 layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect());
Stan Iliev500a0c32016-10-26 10:30:09 -0400101
Jerome Gaillard09a38e42024-03-14 15:20:56 +0000102 // TODO: put localized light center calculation and storage to a drawable related code.
103 // It does not seem right to store something localized in a global state
104 // fix here and in recordLayers
105 const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw());
106 Vector3 transformedLightCenter(savedLightCenter);
107 // map current light center into RenderNode's coordinate space
108 layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter);
109 LightingInfo::setLightCenterRaw(transformedLightCenter);
110
111 const RenderProperties& properties = layerNode->properties();
112 const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
113 if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) {
114 return;
Stan Iliev500a0c32016-10-26 10:30:09 -0400115 }
Jerome Gaillard09a38e42024-03-14 15:20:56 +0000116
117 ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(),
118 bounds.height());
119
120 layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false;
121 layerCanvas->clear(SK_ColorTRANSPARENT);
122
123 RenderNodeDrawable root(layerNode, layerCanvas, false);
124 root.forceDraw(layerCanvas);
125 layerCanvas->restoreToCount(saveCount);
126
127 LightingInfo::setLightCenterRaw(savedLightCenter);
Stan Iliev500a0c32016-10-26 10:30:09 -0400128}
129
John Reck322b8ab2019-03-14 13:15:28 -0700130static void savePictureAsync(const sk_sp<SkData>& data, const std::string& filename) {
131 CommonPool::post([data, filename] {
132 if (0 == access(filename.c_str(), F_OK)) {
Stan Ilieve9d00122017-09-19 12:07:10 -0400133 return;
134 }
135
John Reck322b8ab2019-03-14 13:15:28 -0700136 SkFILEWStream stream(filename.c_str());
Stan Ilieve9d00122017-09-19 12:07:10 -0400137 if (stream.isValid()) {
John Reck322b8ab2019-03-14 13:15:28 -0700138 stream.write(data->data(), data->size());
Stan Ilieve9d00122017-09-19 12:07:10 -0400139 stream.flush();
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400140 ALOGD("SKP Captured Drawing Output (%zu bytes) for frame. %s", stream.bytesWritten(),
John Reck322b8ab2019-03-14 13:15:28 -0700141 filename.c_str());
Stan Ilieve9d00122017-09-19 12:07:10 -0400142 }
John Reck322b8ab2019-03-14 13:15:28 -0700143 });
144}
Stan Ilieve9d00122017-09-19 12:07:10 -0400145
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400146// Note multiple SkiaPipeline instances may be loaded if more than one app is visible.
147// Each instance may observe the filename changing and try to record to a file of the same name.
148// Only the first one will succeed. There is no scope available here where we could coordinate
149// to cause this function to return true for only one of the instances.
150bool SkiaPipeline::shouldStartNewFileCapture() {
151 // Don't start a new file based capture if one is currently ongoing.
152 if (mCaptureMode != CaptureMode::None) { return false; }
153
154 // A new capture is started when the filename property changes.
155 // Read the filename property.
156 std::string prop = base::GetProperty(PROPERTY_CAPTURE_SKP_FILENAME, "0");
157 // if the filename property changed to a valid value
158 if (prop[0] != '0' && mCapturedFile != prop) {
159 // remember this new filename
160 mCapturedFile = prop;
161 // and get a property indicating how many frames to capture.
162 mCaptureSequence = base::GetIntProperty(PROPERTY_CAPTURE_SKP_FRAMES, 1);
John Reck5cca8f22018-12-10 17:06:22 -0800163 if (mCaptureSequence <= 0) {
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400164 return false;
165 } else if (mCaptureSequence == 1) {
166 mCaptureMode = CaptureMode::SingleFrameSKP;
167 } else {
168 mCaptureMode = CaptureMode::MultiFrameSKP;
Stan Ilieve9d00122017-09-19 12:07:10 -0400169 }
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400170 return true;
171 }
172 return false;
173}
174
175// performs the first-frame work of a multi frame SKP capture. Returns true if successful.
176bool SkiaPipeline::setupMultiFrameCapture() {
177 ALOGD("Set up multi-frame capture, frames = %d", mCaptureSequence);
178 // We own this stream and need to hold it until close() finishes.
179 auto stream = std::make_unique<SkFILEWStream>(mCapturedFile.c_str());
180 if (stream->isValid()) {
181 mOpenMultiPicStream = std::move(stream);
182 mSerialContext.reset(new SkSharingSerialContext());
183 SkSerialProcs procs;
184 procs.fImageProc = SkSharingSerialContext::serializeImage;
185 procs.fImageCtx = mSerialContext.get();
Nathaniel Nifong429fff42020-01-30 14:38:21 -0500186 procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){
187 return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
188 };
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400189 // SkDocuments don't take owership of the streams they write.
190 // we need to keep it until after mMultiPic.close()
191 // procs is passed as a pointer, but just as a method of having an optional default.
192 // procs doesn't need to outlive this Make call.
Nathaniel Nifong7c216772021-01-22 13:23:25 -0500193 mMultiPic = SkMakeMultiPictureDocument(mOpenMultiPicStream.get(), &procs,
194 [sharingCtx = mSerialContext.get()](const SkPicture* pic) {
195 SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx);
196 });
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400197 return true;
198 } else {
199 ALOGE("Could not open \"%s\" for writing.", mCapturedFile.c_str());
200 mCaptureSequence = 0;
201 mCaptureMode = CaptureMode::None;
202 return false;
203 }
204}
205
Nathaniel Nifong2945bff2019-11-25 09:34:21 -0500206// recurse through the rendernode's children, add any nodes which are layers to the queue.
207static void collectLayers(RenderNode* node, LayerUpdateQueue* layers) {
John Reckbe671952021-01-13 22:39:32 -0500208 SkiaDisplayList* dl = node->getDisplayList().asSkiaDl();
Nathaniel Nifong2945bff2019-11-25 09:34:21 -0500209 if (dl) {
210 const auto& prop = node->properties();
211 if (node->hasLayer()) {
212 layers->enqueueLayerWithDamage(node, Rect(prop.getWidth(), prop.getHeight()));
213 }
214 // The way to recurse through rendernodes is to call this with a lambda.
215 dl->updateChildren([&](RenderNode* child) { collectLayers(child, layers); });
216 }
217}
218
219// record the provided layers to the provided canvas as self-contained skpictures.
220static void recordLayers(const LayerUpdateQueue& layers,
221 SkCanvas* mskpCanvas) {
222 const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw());
223 // Record the commands to re-draw each dirty layer into an SkPicture
224 for (size_t i = 0; i < layers.entries().size(); i++) {
225 RenderNode* layerNode = layers.entries()[i].renderNode.get();
226 const Rect& layerDamage = layers.entries()[i].damage;
227 const RenderProperties& properties = layerNode->properties();
228
229 // Temporarily map current light center into RenderNode's coordinate space
230 Vector3 transformedLightCenter(savedLightCenter);
231 layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter);
232 LightingInfo::setLightCenterRaw(transformedLightCenter);
233
234 SkPictureRecorder layerRec;
235 auto* recCanvas = layerRec.beginRecording(properties.getWidth(),
236 properties.getHeight());
237 // This is not recorded but still causes clipping.
238 recCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect());
239 RenderNodeDrawable root(layerNode, recCanvas, false);
240 root.forceDraw(recCanvas);
241 // Now write this picture into the SKP canvas with an annotation indicating what it is
242 mskpCanvas->drawAnnotation(layerDamage.toSkRect(), String8::format(
243 "OffscreenLayerDraw|%" PRId64, layerNode->uniqueId()).c_str(), nullptr);
244 mskpCanvas->drawPicture(layerRec.finishRecordingAsPicture());
245 }
246 LightingInfo::setLightCenterRaw(savedLightCenter);
247}
248
249SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface, RenderNode* root,
250 const LayerUpdateQueue& dirtyLayers) {
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400251 if (CC_LIKELY(!Properties::skpCaptureEnabled)) {
252 return surface->getCanvas(); // Bail out early when capture is not turned on.
253 }
254 // Note that shouldStartNewFileCapture tells us if this is the *first* frame of a capture.
Nathaniel Nifong2945bff2019-11-25 09:34:21 -0500255 bool firstFrameOfAnim = false;
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400256 if (shouldStartNewFileCapture() && mCaptureMode == CaptureMode::MultiFrameSKP) {
Nathaniel Nifong2945bff2019-11-25 09:34:21 -0500257 // set a reminder to record every layer near the end of this method, after we have set up
258 // the nway canvas.
259 firstFrameOfAnim = true;
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400260 if (!setupMultiFrameCapture()) {
261 return surface->getCanvas();
Stan Ilieve9d00122017-09-19 12:07:10 -0400262 }
263 }
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400264
265 // Create a canvas pointer, fill it depending on what kind of capture is requested (if any)
266 SkCanvas* pictureCanvas = nullptr;
267 switch (mCaptureMode) {
268 case CaptureMode::CallbackAPI:
269 case CaptureMode::SingleFrameSKP:
270 mRecorder.reset(new SkPictureRecorder());
Nathaniel Nifong31fee3a2019-07-11 16:27:14 -0400271 pictureCanvas = mRecorder->beginRecording(surface->width(), surface->height());
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400272 break;
273 case CaptureMode::MultiFrameSKP:
274 // If a multi frame recording is active, initialize recording for a single frame of a
275 // multi frame file.
276 pictureCanvas = mMultiPic->beginPage(surface->width(), surface->height());
277 break;
278 case CaptureMode::None:
279 // Returning here in the non-capture case means we can count on pictureCanvas being
280 // non-null below.
281 return surface->getCanvas();
282 }
283
284 // Setting up an nway canvas is common to any kind of capture.
285 mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height());
286 mNwayCanvas->addCanvas(surface->getCanvas());
287 mNwayCanvas->addCanvas(pictureCanvas);
Nathaniel Nifong2945bff2019-11-25 09:34:21 -0500288
289 if (firstFrameOfAnim) {
290 // On the first frame of any mskp capture we want to record any layers that are needed in
291 // frame but may have been rendered offscreen before recording began.
292 // We do not maintain a list of all layers, since it isn't needed outside this rare,
293 // recording use case. Traverse the tree to find them and put them in this LayerUpdateQueue.
294 LayerUpdateQueue luq;
295 collectLayers(root, &luq);
296 recordLayers(luq, mNwayCanvas.get());
297 } else {
298 // on non-first frames, we record any normal layer draws (dirty regions)
299 recordLayers(dirtyLayers, mNwayCanvas.get());
300 }
301
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400302 return mNwayCanvas.get();
Stan Ilieve9d00122017-09-19 12:07:10 -0400303}
304
305void SkiaPipeline::endCapture(SkSurface* surface) {
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400306 if (CC_LIKELY(mCaptureMode == CaptureMode::None)) { return; }
John Reck5cca8f22018-12-10 17:06:22 -0800307 mNwayCanvas.reset();
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400308 ATRACE_CALL();
309 if (mCaptureSequence > 0 && mCaptureMode == CaptureMode::MultiFrameSKP) {
310 mMultiPic->endPage();
311 mCaptureSequence--;
312 if (mCaptureSequence == 0) {
313 mCaptureMode = CaptureMode::None;
314 // Pass mMultiPic and mOpenMultiPicStream to a background thread, which will handle
315 // the heavyweight serialization work and destroy them. mOpenMultiPicStream is released
316 // to a bare pointer because keeping it in a smart pointer makes the lambda
317 // non-copyable. The lambda is only called once, so this is safe.
318 SkFILEWStream* stream = mOpenMultiPicStream.release();
319 CommonPool::post([doc = std::move(mMultiPic), stream]{
320 ALOGD("Finalizing multi frame SKP");
321 doc->close();
322 delete stream;
323 ALOGD("Multi frame SKP complete.");
324 });
325 }
326 } else {
Stan Ilieve9d00122017-09-19 12:07:10 -0400327 sk_sp<SkPicture> picture = mRecorder->finishRecordingAsPicture();
Stan Ilieve9d00122017-09-19 12:07:10 -0400328 if (picture->approximateOpCount() > 0) {
John Reck5cca8f22018-12-10 17:06:22 -0800329 if (mPictureCapturedCallback) {
330 std::invoke(mPictureCapturedCallback, std::move(picture));
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400331 } else {
332 // single frame skp to file
Nathaniel Nifong429fff42020-01-30 14:38:21 -0500333 SkSerialProcs procs;
334 procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){
335 return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
336 };
Kevin Lubick14d969d2023-09-07 14:28:59 +0000337 procs.fImageProc = [](SkImage* img, void* ctx) -> sk_sp<SkData> {
338 GrDirectContext* dCtx = static_cast<GrDirectContext*>(ctx);
339 return SkPngEncoder::Encode(dCtx,
340 img,
341 SkPngEncoder::Options{});
342 };
343 procs.fImageCtx = mRenderThread.getGrContext();
John Reck76005182021-06-09 22:43:05 -0400344 auto data = picture->serialize(&procs);
Nathaniel Nifongd2e49a22019-06-24 15:07:34 -0400345 savePictureAsync(data, mCapturedFile);
346 mCaptureSequence = 0;
Derek Sollenberger5f9753d2020-04-01 15:59:02 -0400347 mCaptureMode = CaptureMode::None;
Stan Ilieve9d00122017-09-19 12:07:10 -0400348 }
Stan Ilieve9d00122017-09-19 12:07:10 -0400349 }
350 mRecorder.reset();
351 }
352}
353
Stan Iliev500a0c32016-10-26 10:30:09 -0400354void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
John Reck1bcacfd2017-11-03 10:12:19 -0700355 const std::vector<sp<RenderNode>>& nodes, bool opaque,
Greg Danielc4076782019-01-08 16:01:18 -0500356 const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
357 const SkMatrix& preTransform) {
John Reck5cca8f22018-12-10 17:06:22 -0800358 bool previousSkpEnabled = Properties::skpCaptureEnabled;
359 if (mPictureCapturedCallback) {
360 Properties::skpCaptureEnabled = true;
361 }
362
Nathaniel Nifong2945bff2019-11-25 09:34:21 -0500363 // Initialize the canvas for the current frame, that might be a recording canvas if SKP
364 // capture is enabled.
365 SkCanvas* canvas = tryCapture(surface.get(), nodes[0].get(), layers);
366
Stan Iliev500a0c32016-10-26 10:30:09 -0400367 // draw all layers up front
Peiyong Lin1f6aa122018-09-10 16:28:08 -0700368 renderLayersImpl(layers, opaque);
Stan Iliev500a0c32016-10-26 10:30:09 -0400369
Nathaniel Nifongdc19a652019-11-11 11:47:50 -0500370 renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform);
Matt Sarettf58cc922016-11-14 18:33:38 -0500371
Stan Ilieve9d00122017-09-19 12:07:10 -0400372 endCapture(surface.get());
Matt Sarettf58cc922016-11-14 18:33:38 -0500373
374 if (CC_UNLIKELY(Properties::debugOverdraw)) {
Nathaniel Nifongdc19a652019-11-11 11:47:50 -0500375 renderOverdraw(clip, nodes, contentDrawBounds, surface, preTransform);
Matt Sarettf58cc922016-11-14 18:33:38 -0500376 }
377
John Reck5cca8f22018-12-10 17:06:22 -0800378 Properties::skpCaptureEnabled = previousSkpEnabled;
Matt Sarettf58cc922016-11-14 18:33:38 -0500379}
380
Stan Iliev52771272016-11-17 09:54:38 -0500381namespace {
382static Rect nodeBounds(RenderNode& node) {
383 auto& props = node.properties();
John Reck1bcacfd2017-11-03 10:12:19 -0700384 return Rect(props.getLeft(), props.getTop(), props.getRight(), props.getBottom());
Stan Iliev52771272016-11-17 09:54:38 -0500385}
John Reck0fa0cbc2019-04-05 16:57:46 -0700386} // namespace
Stan Iliev52771272016-11-17 09:54:38 -0500387
Nathaniel Nifongdc19a652019-11-11 11:47:50 -0500388void SkiaPipeline::renderFrameImpl(const SkRect& clip,
John Reck1bcacfd2017-11-03 10:12:19 -0700389 const std::vector<sp<RenderNode>>& nodes, bool opaque,
Greg Danielc4076782019-01-08 16:01:18 -0500390 const Rect& contentDrawBounds, SkCanvas* canvas,
391 const SkMatrix& preTransform) {
Stan Ilievb66b8bb2016-12-15 18:17:42 -0500392 SkAutoCanvasRestore saver(canvas, true);
Nathaniel Nifongcff969f2020-01-09 14:03:49 -0500393 auto clipRestriction = preTransform.mapRect(clip).roundOut();
John Reck76005182021-06-09 22:43:05 -0400394 if (CC_UNLIKELY(isCapturingSkp())) {
Nathaniel Nifong89a5dc52020-01-17 15:32:17 -0500395 canvas->drawAnnotation(SkRect::Make(clipRestriction), "AndroidDeviceClipRestriction",
396 nullptr);
397 } else {
398 // clip drawing to dirty region only when not recording SKP files (which should contain all
399 // draw ops on every frame)
400 canvas->androidFramework_setDeviceClipRestriction(clipRestriction);
401 }
Greg Danielc4076782019-01-08 16:01:18 -0500402 canvas->concat(preTransform);
Stan Iliev500a0c32016-10-26 10:30:09 -0400403
Nader Jawadf5ce4522023-03-27 13:02:41 -0700404 if (!opaque) {
Stan Iliev500a0c32016-10-26 10:30:09 -0400405 canvas->clear(SK_ColorTRANSPARENT);
406 }
407
Stan Iliev52771272016-11-17 09:54:38 -0500408 if (1 == nodes.size()) {
409 if (!nodes[0]->nothingToDraw()) {
Stan Iliev52771272016-11-17 09:54:38 -0500410 RenderNodeDrawable root(nodes[0].get(), canvas);
411 root.draw(canvas);
412 }
413 } else if (0 == nodes.size()) {
John Reck1bcacfd2017-11-03 10:12:19 -0700414 // nothing to draw
Stan Iliev52771272016-11-17 09:54:38 -0500415 } else {
416 // It there are multiple render nodes, they are laid out as follows:
417 // #0 - backdrop (content + caption)
418 // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop)
419 // #2 - additional overlay nodes
John Reck1bcacfd2017-11-03 10:12:19 -0700420 // Usually the backdrop cannot be seen since it will be entirely covered by the content.
421 // While
422 // resizing however it might become partially visible. The following render loop will crop
423 // the
424 // backdrop against the content and draw the remaining part of it. It will then draw the
425 // content
Stan Iliev52771272016-11-17 09:54:38 -0500426 // cropped to the backdrop (since that indicates a shrinking of the window).
427 //
428 // Additional nodes will be drawn on top with no particular clipping semantics.
Stan Iliev500a0c32016-10-26 10:30:09 -0400429
Stan Iliev52771272016-11-17 09:54:38 -0500430 // Usually the contents bounds should be mContentDrawBounds - however - we will
431 // move it towards the fixed edge to give it a more stable appearance (for the moment).
432 // If there is no content bounds we ignore the layering as stated above and start with 2.
Stan Iliev500a0c32016-10-26 10:30:09 -0400433
Stan Iliev52771272016-11-17 09:54:38 -0500434 // Backdrop bounds in render target space
435 const Rect backdrop = nodeBounds(*nodes[0]);
Stan Iliev500a0c32016-10-26 10:30:09 -0400436
John Reck1bcacfd2017-11-03 10:12:19 -0700437 // Bounds that content will fill in render target space (note content node bounds may be
438 // bigger)
Stan Iliev52771272016-11-17 09:54:38 -0500439 Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight());
440 content.translate(backdrop.left, backdrop.top);
441 if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) {
442 // Content doesn't entirely overlap backdrop, so fill around content (right/bottom)
Stan Iliev500a0c32016-10-26 10:30:09 -0400443
Stan Iliev52771272016-11-17 09:54:38 -0500444 // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to
John Reck1bcacfd2017-11-03 10:12:19 -0700445 // also fill left/top. Currently, both 2up and freeform position content at the top/left
446 // of
Stan Iliev52771272016-11-17 09:54:38 -0500447 // the backdrop, so this isn't necessary.
448 RenderNodeDrawable backdropNode(nodes[0].get(), canvas);
449 if (content.right < backdrop.right) {
450 // draw backdrop to right side of content
451 SkAutoCanvasRestore acr(canvas, true);
John Reck1bcacfd2017-11-03 10:12:19 -0700452 canvas->clipRect(SkRect::MakeLTRB(content.right, backdrop.top, backdrop.right,
453 backdrop.bottom));
Stan Iliev52771272016-11-17 09:54:38 -0500454 backdropNode.draw(canvas);
455 }
456 if (content.bottom < backdrop.bottom) {
457 // draw backdrop to bottom of content
458 // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill
459 SkAutoCanvasRestore acr(canvas, true);
John Reck1bcacfd2017-11-03 10:12:19 -0700460 canvas->clipRect(SkRect::MakeLTRB(content.left, content.bottom, content.right,
461 backdrop.bottom));
Stan Iliev52771272016-11-17 09:54:38 -0500462 backdropNode.draw(canvas);
463 }
Stan Iliev500a0c32016-10-26 10:30:09 -0400464 }
465
Stan Iliev52771272016-11-17 09:54:38 -0500466 RenderNodeDrawable contentNode(nodes[1].get(), canvas);
467 if (!backdrop.isEmpty()) {
468 // content node translation to catch up with backdrop
469 float dx = backdrop.left - contentDrawBounds.left;
470 float dy = backdrop.top - contentDrawBounds.top;
471
472 SkAutoCanvasRestore acr(canvas, true);
473 canvas->translate(dx, dy);
John Reck1bcacfd2017-11-03 10:12:19 -0700474 const SkRect contentLocalClip =
475 SkRect::MakeXYWH(contentDrawBounds.left, contentDrawBounds.top,
476 backdrop.getWidth(), backdrop.getHeight());
Stan Iliev52771272016-11-17 09:54:38 -0500477 canvas->clipRect(contentLocalClip);
478 contentNode.draw(canvas);
479 } else {
480 SkAutoCanvasRestore acr(canvas, true);
481 contentNode.draw(canvas);
482 }
483
484 // remaining overlay nodes, simply defer
485 for (size_t index = 2; index < nodes.size(); index++) {
486 if (!nodes[index]->nothingToDraw()) {
487 SkAutoCanvasRestore acr(canvas, true);
488 RenderNodeDrawable overlayNode(nodes[index].get(), canvas);
489 overlayNode.draw(canvas);
490 }
491 }
Stan Iliev500a0c32016-10-26 10:30:09 -0400492 }
Stan Iliev500a0c32016-10-26 10:30:09 -0400493}
494
Peiyong Lin3bff1352018-12-11 07:56:07 -0800495void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) {
Derek Sollenberger1863d942020-02-05 15:41:51 -0500496 mColorMode = colorMode;
John Reckb36bfdd2020-07-23 13:47:49 -0700497 switch (colorMode) {
498 case ColorMode::Default:
499 mSurfaceColorType = SkColorType::kN32_SkColorType;
500 mSurfaceColorSpace = SkColorSpace::MakeSRGB();
501 break;
502 case ColorMode::WideColorGamut:
503 mSurfaceColorType = DeviceInfo::get()->getWideColorType();
504 mSurfaceColorSpace = DeviceInfo::get()->getWideColorSpace();
505 break;
506 case ColorMode::Hdr:
Alec Mouri4a3035e2024-03-04 23:12:42 +0000507 if (DeviceInfo::get()->isSupportRgba10101010ForHdr()) {
508 mSurfaceColorType = SkColorType::kRGBA_10x6_SkColorType;
509 mSurfaceColorSpace = SkColorSpace::MakeRGB(
510 GetExtendedTransferFunction(mTargetSdrHdrRatio), SkNamedGamut::kDisplayP3);
511 } else if (DeviceInfo::get()->isSupportFp16ForHdr()) {
Alec Mouri22ab7f32023-09-06 02:11:56 +0000512 mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
513 mSurfaceColorSpace = SkColorSpace::MakeSRGB();
514 } else {
515 mSurfaceColorType = SkColorType::kN32_SkColorType;
516 mSurfaceColorSpace = SkColorSpace::MakeRGB(
517 GetExtendedTransferFunction(mTargetSdrHdrRatio), SkNamedGamut::kDisplayP3);
518 }
John Reckb36bfdd2020-07-23 13:47:49 -0700519 break;
John Reck0b3f3312023-01-31 16:21:28 -0500520 case ColorMode::Hdr10:
521 mSurfaceColorType = SkColorType::kRGBA_1010102_SkColorType;
522 mSurfaceColorSpace = SkColorSpace::MakeRGB(
523 GetExtendedTransferFunction(mTargetSdrHdrRatio), SkNamedGamut::kDisplayP3);
524 break;
Leon Scroggins IIIcbdbb662021-11-30 13:59:00 -0500525 case ColorMode::A8:
526 mSurfaceColorType = SkColorType::kAlpha_8_SkColorType;
527 mSurfaceColorSpace = nullptr;
528 break;
Peiyong Lin3bff1352018-12-11 07:56:07 -0800529 }
530}
531
John Reck55887762023-01-25 16:51:18 -0500532void SkiaPipeline::setTargetSdrHdrRatio(float ratio) {
John Reck0b3f3312023-01-31 16:21:28 -0500533 if (mColorMode == ColorMode::Hdr || mColorMode == ColorMode::Hdr10) {
John Reck55887762023-01-25 16:51:18 -0500534 mTargetSdrHdrRatio = ratio;
Alec Mouri22ab7f32023-09-06 02:11:56 +0000535
Alec Mouri4a3035e2024-03-04 23:12:42 +0000536 if (mColorMode == ColorMode::Hdr && DeviceInfo::get()->isSupportFp16ForHdr() &&
537 !DeviceInfo::get()->isSupportRgba10101010ForHdr()) {
Alec Mouri22ab7f32023-09-06 02:11:56 +0000538 mSurfaceColorSpace = SkColorSpace::MakeSRGB();
539 } else {
540 mSurfaceColorSpace = SkColorSpace::MakeRGB(
541 GetExtendedTransferFunction(mTargetSdrHdrRatio), SkNamedGamut::kDisplayP3);
542 }
John Reck55887762023-01-25 16:51:18 -0500543 } else {
544 mTargetSdrHdrRatio = 1.f;
545 }
546}
547
Matt Sarettf58cc922016-11-14 18:33:38 -0500548// Overdraw debugging
549
550// These colors should be kept in sync with Caches::getOverdrawColor() with a few differences.
Mike Reed331c4e12020-02-13 10:21:54 -0500551// This implementation requires transparent entries for "no overdraw" and "single draws".
552static const SkColor kOverdrawColors[2][6] = {
553 {
554 0x00000000,
555 0x00000000,
556 0x2f0000ff,
557 0x2f00ff00,
558 0x3fff0000,
559 0x7fff0000,
560 },
561 {
562 0x00000000,
563 0x00000000,
564 0x2f0000ff,
565 0x4fffff00,
566 0x5fff89d7,
567 0x7fff0000,
568 },
Matt Sarettf58cc922016-11-14 18:33:38 -0500569};
570
Nathaniel Nifongdc19a652019-11-11 11:47:50 -0500571void SkiaPipeline::renderOverdraw(const SkRect& clip,
John Reck1bcacfd2017-11-03 10:12:19 -0700572 const std::vector<sp<RenderNode>>& nodes,
Greg Danielc4076782019-01-08 16:01:18 -0500573 const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
574 const SkMatrix& preTransform) {
Matt Sarettf58cc922016-11-14 18:33:38 -0500575 // Set up the overdraw canvas.
576 SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height());
577 sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo);
Nathaniel Nifongfe05b7c2019-09-17 12:52:52 -0400578 LOG_ALWAYS_FATAL_IF(!offscreen, "Failed to create offscreen SkSurface for overdraw viz.");
Matt Sarettf58cc922016-11-14 18:33:38 -0500579 SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas());
580
581 // Fake a redraw to replay the draw commands. This will increment the alpha channel
582 // each time a pixel would have been drawn.
583 // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero
584 // initialized.
Nathaniel Nifongdc19a652019-11-11 11:47:50 -0500585 renderFrameImpl(clip, nodes, true, contentDrawBounds, &overdrawCanvas, preTransform);
Matt Sarettf58cc922016-11-14 18:33:38 -0500586 sk_sp<SkImage> counts = offscreen->makeImageSnapshot();
587
588 // Draw overdraw colors to the canvas. The color filter will convert counts to colors.
589 SkPaint paint;
Mike Reed331c4e12020-02-13 10:21:54 -0500590 const SkColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)];
591 paint.setColorFilter(SkOverdrawColorFilter::MakeWithSkColors(colors));
Mike Reed7994a312021-01-28 18:06:26 -0500592 surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, SkSamplingOptions(), &paint);
Matt Sarettf58cc922016-11-14 18:33:38 -0500593}
594
Stan Iliev500a0c32016-10-26 10:30:09 -0400595} /* namespace skiapipeline */
596} /* namespace uirenderer */
597} /* namespace android */