blob: 8006a111bd050240e640bbe26b1176f331a0decb [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"
30#include "src/utils/SkMultiPictureDocument.h"
31
32namespace android {
33namespace renderengine {
34namespace skia {
35
36// The root of the filename to write a recorded SKP to. In order for this file to
37// be written to /data/user/, user must run 'adb shell setenforce 0' in the device.
38static const std::string CAPTURED_FILENAME_BASE = "/data/user/re_skiacapture";
39
40SkiaCapture::~SkiaCapture() {
41 mTimer.stop();
42}
43
44SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) {
45 ATRACE_CALL();
46
47 // If we are not running yet, set up.
48 if (!mCaptureRunning) {
49 mTimerInterval = std::chrono::milliseconds(
50 base::GetIntProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_SKIA_MS, 0));
51 // Set up the multi-frame capture. If we fail to set it up, then just return canvas.
52 // If interval is 0, return surface.
53 if (CC_LIKELY(mTimerInterval == 0ms || !setupMultiFrameCapture())) {
54 return surface->getCanvas();
55 }
56 // Start the new timer. When timer expires, write to file.
57 mTimer.setTimeout(
58 [this] {
59 endCapture();
60 writeToFile();
61 // To avoid going in circles, set the flag to 0. This way the capture can be
62 // restarted just by setting the flag and without restarting the process.
63 base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_SKIA_MS, "0");
64 },
65 mTimerInterval);
66 }
67
68 // Create a canvas pointer, fill it.
69 SkCanvas* pictureCanvas = mMultiPic->beginPage(surface->width(), surface->height());
70
71 // Setting up an nway canvas is common to any kind of capture.
72 mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height());
73 mNwayCanvas->addCanvas(surface->getCanvas());
74 mNwayCanvas->addCanvas(pictureCanvas);
75
76 return mNwayCanvas.get();
77}
78
79void SkiaCapture::endCapture() {
80 ATRACE_CALL();
81 // Don't end anything if we are not running.
82 if (!mCaptureRunning) {
83 return;
84 }
85 // Reset the canvas pointer.
86 mNwayCanvas.reset();
87 // End page.
88 if (mMultiPic) {
89 mMultiPic->endPage();
90 }
91}
92
93void SkiaCapture::writeToFile() {
94 ATRACE_CALL();
95 // Pass mMultiPic and mOpenMultiPicStream to a background thread, which will
96 // handle the heavyweight serialization work and destroy them.
97 // mOpenMultiPicStream is released to a bare pointer because keeping it in
98 // a smart pointer makes the lambda non-copyable. The lambda is only called
99 // once, so this is safe.
100 SkFILEWStream* stream = mOpenMultiPicStream.release();
101 CommonPool::post([doc = std::move(mMultiPic), stream] {
102 ALOGD("Finalizing multi frame SKP");
103 doc->close();
104 delete stream;
105 ALOGD("Multi frame SKP complete.");
106 });
107 mCaptureRunning = false;
108}
109
110bool SkiaCapture::setupMultiFrameCapture() {
111 ATRACE_CALL();
112 ALOGD("Set up multi-frame capture, ms = %llu", mTimerInterval.count());
113
114 std::string captureFile;
115 // Attach a timestamp to the file.
116 base::StringAppendF(&captureFile, "%s_%lld.mskp", CAPTURED_FILENAME_BASE.c_str(),
117 std::chrono::steady_clock::now().time_since_epoch().count());
118 auto stream = std::make_unique<SkFILEWStream>(captureFile.c_str());
119 // We own this stream and need to hold it until close() finishes.
120 if (stream->isValid()) {
121 mOpenMultiPicStream = std::move(stream);
122 mSerialContext.reset(new SkSharingSerialContext());
123 SkSerialProcs procs;
124 procs.fImageProc = SkSharingSerialContext::serializeImage;
125 procs.fImageCtx = mSerialContext.get();
126 procs.fTypefaceProc = [](SkTypeface* tf, void* ctx) {
127 return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
128 };
129 // SkDocuments don't take ownership of the streams they write.
130 // we need to keep it until after mMultiPic.close()
131 // procs is passed as a pointer, but just as a method of having an optional default.
132 // procs doesn't need to outlive this Make call.
133 mMultiPic = SkMakeMultiPictureDocument(mOpenMultiPicStream.get(), &procs);
134 mCaptureRunning = true;
135 return true;
136 } else {
137 ALOGE("Could not open \"%s\" for writing.", captureFile.c_str());
138 return false;
139 }
140}
141
142} // namespace skia
143} // namespace renderengine
144} // namespace android