• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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"
30 #include "SkCanvas.h"
31 #include "SkRect.h"
32 #include "SkTypeface.h"
33 #include "src/utils/SkMultiPictureDocument.h"
34 
35 namespace android {
36 namespace renderengine {
37 namespace skia {
38 
39 // The root of the filename to write a recorded SKP to. In order for this file to
40 // be written to /data/user/, user must run 'adb shell setenforce 0' on the device.
41 static const std::string CAPTURED_FILENAME_BASE = "/data/user/re_skiacapture";
42 
~SkiaCapture()43 SkiaCapture::~SkiaCapture() {
44     mTimer.stop();
45 }
46 
tryCapture(SkSurface * surface)47 SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) NO_THREAD_SAFETY_ANALYSIS {
48     ATRACE_CALL();
49 
50     // If we are not running yet, set up.
51     if (CC_LIKELY(!mCaptureRunning)) {
52         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] {
62                     const std::scoped_lock lock(mMutex);
63                     LOG_ALWAYS_FATAL_IF(mCurrentPageCanvas != nullptr);
64                     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 
72     mMutex.lock();
73 
74     // Create a canvas pointer, fill it.
75     mCurrentPageCanvas = mMultiPic->beginPage(surface->width(), surface->height());
76 
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());
80     mNwayCanvas->addCanvas(mCurrentPageCanvas);
81 
82     return mNwayCanvas.get();
83 }
84 
endCapture()85 void SkiaCapture::endCapture() NO_THREAD_SAFETY_ANALYSIS {
86     ATRACE_CALL();
87     // Don't end anything if we are not running.
88     if (CC_LIKELY(!mCaptureRunning)) {
89         return;
90     }
91     // Reset the canvas pointer.
92     mCurrentPageCanvas = nullptr;
93     mNwayCanvas.reset();
94     // End page.
95     if (mMultiPic) {
96         mMultiPic->endPage();
97     }
98     mMutex.unlock();
99 }
100 
tryOffscreenCapture(SkSurface * surface,OffscreenState * state)101 SkCanvas* 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 
endOffscreenCapture(OffscreenState * state)121 uint64_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;
148 }
149 
writeToFile()150 void 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();
158     CommonPool::post([doc = std::move(mMultiPic), stream, name = std::move(mCaptureFile)] {
159         ALOGD("Finalizing multi frame SKP");
160         doc->close();
161         delete stream;
162         ALOGD("Multi frame SKP saved to %s.", name.c_str());
163         base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_FILENAME, name);
164     });
165     mCaptureRunning = false;
166 }
167 
setupMultiFrameCapture()168 bool SkiaCapture::setupMultiFrameCapture() {
169     ATRACE_CALL();
170     ALOGD("Set up multi-frame capture, ms = %llu", mTimerInterval.count());
171     base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_FILENAME, "");
172     const std::scoped_lock lock(mMutex);
173 
174     // Attach a timestamp to the file.
175     mCaptureFile.clear();
176     base::StringAppendF(&mCaptureFile, "%s_%lld.mskp", CAPTURED_FILENAME_BASE.c_str(),
177                         std::chrono::steady_clock::now().time_since_epoch().count());
178     auto stream = std::make_unique<SkFILEWStream>(mCaptureFile.c_str());
179     // 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.
192         // 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                 });
200         mCaptureRunning = true;
201         return true;
202     } else {
203         ALOGE("Could not open \"%s\" for writing.", mCaptureFile.c_str());
204         return false;
205     }
206 }
207 
208 } // namespace skia
209 } // namespace renderengine
210 } // namespace android
211