blob: e6a0e22dcf41d68f7f2416c8c956f3c50acabcec [file] [log] [blame]
Ana Krulec70d15b1b2020-12-01 10:05:15 -08001/*
2 * Copyright 2020 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 "SkiaCapture.h"
18
19#undef LOG_TAG
20#define LOG_TAG "RenderEngine"
21#define ATRACE_TAG ATRACE_TAG_GRAPHICS
22
23#include <android-base/properties.h>
24#include <android-base/stringprintf.h>
Vishnu Nair40d80012024-07-13 23:25:06 +000025#include <common/trace.h>
Ana Krulec70d15b1b2020-12-01 10:05:15 -080026#include <log/log.h>
27#include <renderengine/RenderEngine.h>
Ana Krulec70d15b1b2020-12-01 10:05:15 -080028
29#include "CommonPool.h"
Kevin Lubick8e0ef682022-03-04 10:34:35 -050030#include "SkCanvas.h"
31#include "SkRect.h"
32#include "SkTypeface.h"
Kevin Lubick93c8a612024-04-12 17:37:05 +000033#include "include/docs/SkMultiPictureDocument.h"
Leon Scroggins III9ce85682023-08-25 15:28:46 -040034#include <sys/stat.h>
Ana Krulec70d15b1b2020-12-01 10:05:15 -080035
36namespace android {
37namespace renderengine {
38namespace skia {
39
40// The root of the filename to write a recorded SKP to. In order for this file to
Leon Scroggins III9ce85682023-08-25 15:28:46 -040041// be written, user must run 'adb shell setenforce 0' on the device. Note: This
42// is handled by record.sh. FIXME(b/296282988): With updated selinux policies,
43// 'adb shell setenforce 0' should be unnecessary.
44static const std::string CAPTURED_FILE_DIR = "/data/misc/mskps";
Ana Krulec70d15b1b2020-12-01 10:05:15 -080045
46SkiaCapture::~SkiaCapture() {
47 mTimer.stop();
48}
49
Derek Sollenberger76664d62021-02-04 11:13:09 -050050SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) NO_THREAD_SAFETY_ANALYSIS {
Vishnu Nair40d80012024-07-13 23:25:06 +000051 SFTRACE_CALL();
Ana Krulec70d15b1b2020-12-01 10:05:15 -080052
53 // If we are not running yet, set up.
Derek Sollenberger76664d62021-02-04 11:13:09 -050054 if (CC_LIKELY(!mCaptureRunning)) {
Ana Krulec70d15b1b2020-12-01 10:05:15 -080055 mTimerInterval = std::chrono::milliseconds(
56 base::GetIntProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_SKIA_MS, 0));
57 // Set up the multi-frame capture. If we fail to set it up, then just return canvas.
58 // If interval is 0, return surface.
59 if (CC_LIKELY(mTimerInterval == 0ms || !setupMultiFrameCapture())) {
60 return surface->getCanvas();
61 }
62 // Start the new timer. When timer expires, write to file.
63 mTimer.setTimeout(
64 [this] {
Derek Sollenberger76664d62021-02-04 11:13:09 -050065 const std::scoped_lock lock(mMutex);
66 LOG_ALWAYS_FATAL_IF(mCurrentPageCanvas != nullptr);
Ana Krulec70d15b1b2020-12-01 10:05:15 -080067 writeToFile();
68 // To avoid going in circles, set the flag to 0. This way the capture can be
69 // restarted just by setting the flag and without restarting the process.
70 base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_SKIA_MS, "0");
71 },
72 mTimerInterval);
73 }
74
Derek Sollenberger76664d62021-02-04 11:13:09 -050075 mMutex.lock();
76
Ana Krulec70d15b1b2020-12-01 10:05:15 -080077 // Create a canvas pointer, fill it.
Derek Sollenberger76664d62021-02-04 11:13:09 -050078 mCurrentPageCanvas = mMultiPic->beginPage(surface->width(), surface->height());
Ana Krulec70d15b1b2020-12-01 10:05:15 -080079
80 // Setting up an nway canvas is common to any kind of capture.
81 mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height());
82 mNwayCanvas->addCanvas(surface->getCanvas());
Derek Sollenberger76664d62021-02-04 11:13:09 -050083 mNwayCanvas->addCanvas(mCurrentPageCanvas);
Ana Krulec70d15b1b2020-12-01 10:05:15 -080084
85 return mNwayCanvas.get();
86}
87
Derek Sollenberger76664d62021-02-04 11:13:09 -050088void SkiaCapture::endCapture() NO_THREAD_SAFETY_ANALYSIS {
Vishnu Nair40d80012024-07-13 23:25:06 +000089 SFTRACE_CALL();
Ana Krulec70d15b1b2020-12-01 10:05:15 -080090 // Don't end anything if we are not running.
Derek Sollenberger76664d62021-02-04 11:13:09 -050091 if (CC_LIKELY(!mCaptureRunning)) {
Ana Krulec70d15b1b2020-12-01 10:05:15 -080092 return;
93 }
94 // Reset the canvas pointer.
Derek Sollenberger76664d62021-02-04 11:13:09 -050095 mCurrentPageCanvas = nullptr;
Ana Krulec70d15b1b2020-12-01 10:05:15 -080096 mNwayCanvas.reset();
97 // End page.
98 if (mMultiPic) {
99 mMultiPic->endPage();
100 }
Derek Sollenberger76664d62021-02-04 11:13:09 -0500101 mMutex.unlock();
102}
103
104SkCanvas* SkiaCapture::tryOffscreenCapture(SkSurface* surface, OffscreenState* state) {
Vishnu Nair40d80012024-07-13 23:25:06 +0000105 SFTRACE_CALL();
Derek Sollenberger76664d62021-02-04 11:13:09 -0500106 // Don't start anything if we are not running.
107 if (CC_LIKELY(!mCaptureRunning)) {
108 return surface->getCanvas();
109 }
110
111 // Create a canvas pointer, fill it.
112 state->offscreenRecorder = std::make_unique<SkPictureRecorder>();
113 SkCanvas* pictureCanvas =
114 state->offscreenRecorder->beginRecording(surface->width(), surface->height());
115
116 // Setting up an nway canvas is common to any kind of capture.
117 state->offscreenCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height());
118 state->offscreenCanvas->addCanvas(surface->getCanvas());
119 state->offscreenCanvas->addCanvas(pictureCanvas);
120
121 return state->offscreenCanvas.get();
122}
123
124uint64_t SkiaCapture::endOffscreenCapture(OffscreenState* state) {
Vishnu Nair40d80012024-07-13 23:25:06 +0000125 SFTRACE_CALL();
Derek Sollenberger76664d62021-02-04 11:13:09 -0500126 // Don't end anything if we are not running.
127 if (CC_LIKELY(!mCaptureRunning)) {
128 return 0;
129 }
130
131 // compute the uniqueID for this capture
132 static std::atomic<uint64_t> nextID{1};
133 const uint64_t uniqueID = nextID.fetch_add(1, std::memory_order_relaxed);
134
135 // Reset the canvas pointer as we are no longer drawing into it
136 state->offscreenCanvas.reset();
137
138 // Record the offscreen as a picture in the currently active page.
139 SkRect bounds =
140 SkRect::Make(state->offscreenRecorder->getRecordingCanvas()->imageInfo().dimensions());
141 mCurrentPageCanvas
142 ->drawAnnotation(bounds,
143 String8::format("OffscreenLayerDraw|%" PRId64, uniqueID).c_str(),
144 nullptr);
145 mCurrentPageCanvas->drawPicture(state->offscreenRecorder->finishRecordingAsPicture());
146
147 // Reset the offscreen picture recorder
148 state->offscreenRecorder.reset();
149
150 return uniqueID;
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800151}
152
153void SkiaCapture::writeToFile() {
Vishnu Nair40d80012024-07-13 23:25:06 +0000154 SFTRACE_CALL();
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800155 // Pass mMultiPic and mOpenMultiPicStream to a background thread, which will
156 // handle the heavyweight serialization work and destroy them.
157 // mOpenMultiPicStream is released to a bare pointer because keeping it in
158 // a smart pointer makes the lambda non-copyable. The lambda is only called
159 // once, so this is safe.
160 SkFILEWStream* stream = mOpenMultiPicStream.release();
Leon Scroggins III2eca03f2021-05-25 15:30:43 -0400161 CommonPool::post([doc = std::move(mMultiPic), stream, name = std::move(mCaptureFile)] {
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800162 ALOGD("Finalizing multi frame SKP");
163 doc->close();
164 delete stream;
Leon Scroggins III2eca03f2021-05-25 15:30:43 -0400165 ALOGD("Multi frame SKP saved to %s.", name.c_str());
166 base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_FILENAME, name);
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800167 });
168 mCaptureRunning = false;
169}
170
171bool SkiaCapture::setupMultiFrameCapture() {
Vishnu Nair40d80012024-07-13 23:25:06 +0000172 SFTRACE_CALL();
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800173 ALOGD("Set up multi-frame capture, ms = %llu", mTimerInterval.count());
Leon Scroggins III2eca03f2021-05-25 15:30:43 -0400174 base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_FILENAME, "");
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800175
Leon Scroggins III9ce85682023-08-25 15:28:46 -0400176 mkdir(CAPTURED_FILE_DIR.c_str(), 0700);
177
178 const std::scoped_lock lock(mMutex);
Leon Scroggins III2eca03f2021-05-25 15:30:43 -0400179 mCaptureFile.clear();
Leon Scroggins III9ce85682023-08-25 15:28:46 -0400180 base::StringAppendF(&mCaptureFile, "%s/re_skiacapture_%lld.mskp", CAPTURED_FILE_DIR.c_str(),
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800181 std::chrono::steady_clock::now().time_since_epoch().count());
Leon Scroggins III2eca03f2021-05-25 15:30:43 -0400182 auto stream = std::make_unique<SkFILEWStream>(mCaptureFile.c_str());
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800183 // We own this stream and need to hold it until close() finishes.
184 if (stream->isValid()) {
185 mOpenMultiPicStream = std::move(stream);
186 mSerialContext.reset(new SkSharingSerialContext());
187 SkSerialProcs procs;
188 procs.fImageProc = SkSharingSerialContext::serializeImage;
189 procs.fImageCtx = mSerialContext.get();
190 procs.fTypefaceProc = [](SkTypeface* tf, void* ctx) {
191 return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
192 };
193 // SkDocuments don't take ownership of the streams they write.
194 // we need to keep it until after mMultiPic.close()
195 // procs is passed as a pointer, but just as a method of having an optional default.
Nathaniel Nifong98fabaf2020-12-17 12:10:46 -0500196 // procs doesn't need to outlive this Make call
197 // The last argument is a callback for the endPage behavior.
198 // See SkSharingProc.h for more explanation of this callback.
Kevin Lubick93c8a612024-04-12 17:37:05 +0000199 mMultiPic = SkMultiPictureDocument::Make(
Nathaniel Nifong98fabaf2020-12-17 12:10:46 -0500200 mOpenMultiPicStream.get(), &procs,
201 [sharingCtx = mSerialContext.get()](const SkPicture* pic) {
202 SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx);
203 });
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800204 mCaptureRunning = true;
205 return true;
206 } else {
Leon Scroggins III2eca03f2021-05-25 15:30:43 -0400207 ALOGE("Could not open \"%s\" for writing.", mCaptureFile.c_str());
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800208 return false;
209 }
210}
211
212} // namespace skia
213} // namespace renderengine
214} // namespace android