blob: b21b01cc1b62bff16d035d836f20b2a130832b51 [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>
25#include <log/log.h>
26#include <renderengine/RenderEngine.h>
27#include <utils/Trace.h>
28
29#include "CommonPool.h"
Kevin Lubick8e0ef682022-03-04 10:34:35 -050030#include "SkCanvas.h"
31#include "SkRect.h"
32#include "SkTypeface.h"
Ana Krulec70d15b1b2020-12-01 10:05:15 -080033#include "src/utils/SkMultiPictureDocument.h"
34
35namespace android {
36namespace renderengine {
37namespace skia {
38
39// The root of the filename to write a recorded SKP to. In order for this file to
Leon Scroggins III2eca03f2021-05-25 15:30:43 -040040// be written to /data/user/, user must run 'adb shell setenforce 0' on the device.
Ana Krulec70d15b1b2020-12-01 10:05:15 -080041static const std::string CAPTURED_FILENAME_BASE = "/data/user/re_skiacapture";
42
43SkiaCapture::~SkiaCapture() {
44 mTimer.stop();
45}
46
Derek Sollenberger76664d62021-02-04 11:13:09 -050047SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) NO_THREAD_SAFETY_ANALYSIS {
Ana Krulec70d15b1b2020-12-01 10:05:15 -080048 ATRACE_CALL();
49
50 // If we are not running yet, set up.
Derek Sollenberger76664d62021-02-04 11:13:09 -050051 if (CC_LIKELY(!mCaptureRunning)) {
Ana Krulec70d15b1b2020-12-01 10:05:15 -080052 mTimerInterval = std::chrono::milliseconds(
53 base::GetIntProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_SKIA_MS, 0));
54 // Set up the multi-frame capture. If we fail to set it up, then just return canvas.
55 // If interval is 0, return surface.
56 if (CC_LIKELY(mTimerInterval == 0ms || !setupMultiFrameCapture())) {
57 return surface->getCanvas();
58 }
59 // Start the new timer. When timer expires, write to file.
60 mTimer.setTimeout(
61 [this] {
Derek Sollenberger76664d62021-02-04 11:13:09 -050062 const std::scoped_lock lock(mMutex);
63 LOG_ALWAYS_FATAL_IF(mCurrentPageCanvas != nullptr);
Ana Krulec70d15b1b2020-12-01 10:05:15 -080064 writeToFile();
65 // To avoid going in circles, set the flag to 0. This way the capture can be
66 // restarted just by setting the flag and without restarting the process.
67 base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_SKIA_MS, "0");
68 },
69 mTimerInterval);
70 }
71
Derek Sollenberger76664d62021-02-04 11:13:09 -050072 mMutex.lock();
73
Ana Krulec70d15b1b2020-12-01 10:05:15 -080074 // Create a canvas pointer, fill it.
Derek Sollenberger76664d62021-02-04 11:13:09 -050075 mCurrentPageCanvas = mMultiPic->beginPage(surface->width(), surface->height());
Ana Krulec70d15b1b2020-12-01 10:05:15 -080076
77 // Setting up an nway canvas is common to any kind of capture.
78 mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height());
79 mNwayCanvas->addCanvas(surface->getCanvas());
Derek Sollenberger76664d62021-02-04 11:13:09 -050080 mNwayCanvas->addCanvas(mCurrentPageCanvas);
Ana Krulec70d15b1b2020-12-01 10:05:15 -080081
82 return mNwayCanvas.get();
83}
84
Derek Sollenberger76664d62021-02-04 11:13:09 -050085void SkiaCapture::endCapture() NO_THREAD_SAFETY_ANALYSIS {
Ana Krulec70d15b1b2020-12-01 10:05:15 -080086 ATRACE_CALL();
87 // Don't end anything if we are not running.
Derek Sollenberger76664d62021-02-04 11:13:09 -050088 if (CC_LIKELY(!mCaptureRunning)) {
Ana Krulec70d15b1b2020-12-01 10:05:15 -080089 return;
90 }
91 // Reset the canvas pointer.
Derek Sollenberger76664d62021-02-04 11:13:09 -050092 mCurrentPageCanvas = nullptr;
Ana Krulec70d15b1b2020-12-01 10:05:15 -080093 mNwayCanvas.reset();
94 // End page.
95 if (mMultiPic) {
96 mMultiPic->endPage();
97 }
Derek Sollenberger76664d62021-02-04 11:13:09 -050098 mMutex.unlock();
99}
100
101SkCanvas* SkiaCapture::tryOffscreenCapture(SkSurface* surface, OffscreenState* state) {
102 ATRACE_CALL();
103 // Don't start anything if we are not running.
104 if (CC_LIKELY(!mCaptureRunning)) {
105 return surface->getCanvas();
106 }
107
108 // Create a canvas pointer, fill it.
109 state->offscreenRecorder = std::make_unique<SkPictureRecorder>();
110 SkCanvas* pictureCanvas =
111 state->offscreenRecorder->beginRecording(surface->width(), surface->height());
112
113 // Setting up an nway canvas is common to any kind of capture.
114 state->offscreenCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height());
115 state->offscreenCanvas->addCanvas(surface->getCanvas());
116 state->offscreenCanvas->addCanvas(pictureCanvas);
117
118 return state->offscreenCanvas.get();
119}
120
121uint64_t SkiaCapture::endOffscreenCapture(OffscreenState* state) {
122 ATRACE_CALL();
123 // Don't end anything if we are not running.
124 if (CC_LIKELY(!mCaptureRunning)) {
125 return 0;
126 }
127
128 // compute the uniqueID for this capture
129 static std::atomic<uint64_t> nextID{1};
130 const uint64_t uniqueID = nextID.fetch_add(1, std::memory_order_relaxed);
131
132 // Reset the canvas pointer as we are no longer drawing into it
133 state->offscreenCanvas.reset();
134
135 // Record the offscreen as a picture in the currently active page.
136 SkRect bounds =
137 SkRect::Make(state->offscreenRecorder->getRecordingCanvas()->imageInfo().dimensions());
138 mCurrentPageCanvas
139 ->drawAnnotation(bounds,
140 String8::format("OffscreenLayerDraw|%" PRId64, uniqueID).c_str(),
141 nullptr);
142 mCurrentPageCanvas->drawPicture(state->offscreenRecorder->finishRecordingAsPicture());
143
144 // Reset the offscreen picture recorder
145 state->offscreenRecorder.reset();
146
147 return uniqueID;
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800148}
149
150void SkiaCapture::writeToFile() {
151 ATRACE_CALL();
152 // Pass mMultiPic and mOpenMultiPicStream to a background thread, which will
153 // handle the heavyweight serialization work and destroy them.
154 // mOpenMultiPicStream is released to a bare pointer because keeping it in
155 // a smart pointer makes the lambda non-copyable. The lambda is only called
156 // once, so this is safe.
157 SkFILEWStream* stream = mOpenMultiPicStream.release();
Leon Scroggins III2eca03f2021-05-25 15:30:43 -0400158 CommonPool::post([doc = std::move(mMultiPic), stream, name = std::move(mCaptureFile)] {
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800159 ALOGD("Finalizing multi frame SKP");
160 doc->close();
161 delete stream;
Leon Scroggins III2eca03f2021-05-25 15:30:43 -0400162 ALOGD("Multi frame SKP saved to %s.", name.c_str());
163 base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_FILENAME, name);
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800164 });
165 mCaptureRunning = false;
166}
167
168bool SkiaCapture::setupMultiFrameCapture() {
169 ATRACE_CALL();
170 ALOGD("Set up multi-frame capture, ms = %llu", mTimerInterval.count());
Leon Scroggins III2eca03f2021-05-25 15:30:43 -0400171 base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_FILENAME, "");
172 const std::scoped_lock lock(mMutex);
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800173
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800174 // Attach a timestamp to the file.
Leon Scroggins III2eca03f2021-05-25 15:30:43 -0400175 mCaptureFile.clear();
176 base::StringAppendF(&mCaptureFile, "%s_%lld.mskp", CAPTURED_FILENAME_BASE.c_str(),
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800177 std::chrono::steady_clock::now().time_since_epoch().count());
Leon Scroggins III2eca03f2021-05-25 15:30:43 -0400178 auto stream = std::make_unique<SkFILEWStream>(mCaptureFile.c_str());
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800179 // We own this stream and need to hold it until close() finishes.
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();
186 procs.fTypefaceProc = [](SkTypeface* tf, void* ctx) {
187 return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
188 };
189 // SkDocuments don't take ownership 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.
Nathaniel Nifong98fabaf2020-12-17 12:10:46 -0500192 // procs doesn't need to outlive this Make call
193 // The last argument is a callback for the endPage behavior.
194 // See SkSharingProc.h for more explanation of this callback.
195 mMultiPic = SkMakeMultiPictureDocument(
196 mOpenMultiPicStream.get(), &procs,
197 [sharingCtx = mSerialContext.get()](const SkPicture* pic) {
198 SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx);
199 });
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800200 mCaptureRunning = true;
201 return true;
202 } else {
Leon Scroggins III2eca03f2021-05-25 15:30:43 -0400203 ALOGE("Could not open \"%s\" for writing.", mCaptureFile.c_str());
Ana Krulec70d15b1b2020-12-01 10:05:15 -0800204 return false;
205 }
206}
207
208} // namespace skia
209} // namespace renderengine
210} // namespace android