1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2018 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkCodec.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkJpegDecoder.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkPngDecoder.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkWebpDecoder.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFontMgr.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkGraphics.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPixmap.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "include/encode/SkPngEncoder.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTPin.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skottie/include/ExternalLayer.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skottie/include/Skottie.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skottie/utils/SkottieUtils.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skresources/include/SkResources.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skshaper/utils/FactoryHelpers.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkOSFile.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTaskGroup.h"
38*c8dee2aaSAndroid Build Coastguard Worker #include "src/utils/SkOSPath.h"
39*c8dee2aaSAndroid Build Coastguard Worker #include "tools/flags/CommandLineFlags.h"
40*c8dee2aaSAndroid Build Coastguard Worker
41*c8dee2aaSAndroid Build Coastguard Worker #if !defined(CPU_ONLY)
42*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/GpuTypes.h"
43*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrDirectContext.h"
44*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrTypes.h"
45*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/SkSurfaceGanesh.h"
46*c8dee2aaSAndroid Build Coastguard Worker #include "tools/gpu/ContextType.h"
47*c8dee2aaSAndroid Build Coastguard Worker #include "tools/gpu/GrContextFactory.h"
48*c8dee2aaSAndroid Build Coastguard Worker #endif
49*c8dee2aaSAndroid Build Coastguard Worker
50*c8dee2aaSAndroid Build Coastguard Worker #if !defined(CPU_ONLY) && !defined(GPU_ONLY)
51*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPicture.h"
52*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPictureRecorder.h"
53*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSerialProcs.h"
54*c8dee2aaSAndroid Build Coastguard Worker #include "src/image/SkImage_Base.h"
55*c8dee2aaSAndroid Build Coastguard Worker #endif
56*c8dee2aaSAndroid Build Coastguard Worker
57*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
58*c8dee2aaSAndroid Build Coastguard Worker #include <chrono>
59*c8dee2aaSAndroid Build Coastguard Worker #include <cstdio>
60*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
61*c8dee2aaSAndroid Build Coastguard Worker #include <functional>
62*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
63*c8dee2aaSAndroid Build Coastguard Worker #include <numeric>
64*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
65*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
66*c8dee2aaSAndroid Build Coastguard Worker
67*c8dee2aaSAndroid Build Coastguard Worker #if defined(HAVE_VIDEO_ENCODER)
68*c8dee2aaSAndroid Build Coastguard Worker #include <future>
69*c8dee2aaSAndroid Build Coastguard Worker #include "experimental/ffmpeg/SkVideoEncoder.h"
70*c8dee2aaSAndroid Build Coastguard Worker const char* formats_help = "Output format (png, skp, mp4, or null)";
71*c8dee2aaSAndroid Build Coastguard Worker #else
72*c8dee2aaSAndroid Build Coastguard Worker const char* formats_help = "Output format (png, skp, or null)";
73*c8dee2aaSAndroid Build Coastguard Worker #endif
74*c8dee2aaSAndroid Build Coastguard Worker
75*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_BUILD_FOR_MAC) && defined(SK_FONTMGR_CORETEXT_AVAILABLE)
76*c8dee2aaSAndroid Build Coastguard Worker #include "include/ports/SkFontMgr_mac_ct.h"
77*c8dee2aaSAndroid Build Coastguard Worker #elif defined(SK_BUILD_FOR_ANDROID) && defined(SK_FONTMGR_ANDROID_AVAILABLE)
78*c8dee2aaSAndroid Build Coastguard Worker #include "include/ports/SkFontMgr_android.h"
79*c8dee2aaSAndroid Build Coastguard Worker #include "include/ports/SkFontScanner_FreeType.h"
80*c8dee2aaSAndroid Build Coastguard Worker #elif defined(SK_BUILD_FOR_UNIX) && defined(SK_FONTMGR_FONTCONFIG_AVAILABLE) && defined(SK_TYPEFACE_FACTORY_FREETYPE)
81*c8dee2aaSAndroid Build Coastguard Worker #include "include/ports/SkFontMgr_fontconfig.h"
82*c8dee2aaSAndroid Build Coastguard Worker #include "include/ports/SkFontScanner_FreeType.h"
83*c8dee2aaSAndroid Build Coastguard Worker #else
84*c8dee2aaSAndroid Build Coastguard Worker #include "include/ports/SkFontMgr_empty.h"
85*c8dee2aaSAndroid Build Coastguard Worker #endif
86*c8dee2aaSAndroid Build Coastguard Worker
87*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string2(input , i, nullptr, "Input .json file.");
88*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string2(writePath, w, nullptr, "Output directory. Frames are names [0-9]{6}.png.");
89*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string2(format , f, "png" , formats_help);
90*c8dee2aaSAndroid Build Coastguard Worker
91*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_double(t0, 0, "Timeline start [0..1].");
92*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_double(t1, 1, "Timeline stop [0..1].");
93*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_double(fps, 0, "Decode frames per second (default is animation native fps).");
94*c8dee2aaSAndroid Build Coastguard Worker
95*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_int(width , 800, "Render width.");
96*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_int(height, 600, "Render height.");
97*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_int(threads, 0, "Number of worker threads (0 -> cores count).");
98*c8dee2aaSAndroid Build Coastguard Worker
99*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_bool2(gpu, g, false, "Enable GPU rasterization.");
100*c8dee2aaSAndroid Build Coastguard Worker
101*c8dee2aaSAndroid Build Coastguard Worker namespace {
102*c8dee2aaSAndroid Build Coastguard Worker
103*c8dee2aaSAndroid Build Coastguard Worker static constexpr SkColor kClearColor = SK_ColorWHITE;
104*c8dee2aaSAndroid Build Coastguard Worker
105*c8dee2aaSAndroid Build Coastguard Worker enum class OutputFormat {
106*c8dee2aaSAndroid Build Coastguard Worker kPNG,
107*c8dee2aaSAndroid Build Coastguard Worker kSKP,
108*c8dee2aaSAndroid Build Coastguard Worker kNull,
109*c8dee2aaSAndroid Build Coastguard Worker kMP4,
110*c8dee2aaSAndroid Build Coastguard Worker };
111*c8dee2aaSAndroid Build Coastguard Worker
112*c8dee2aaSAndroid Build Coastguard Worker
ms_since(std::chrono::steady_clock::time_point start)113*c8dee2aaSAndroid Build Coastguard Worker auto ms_since(std::chrono::steady_clock::time_point start) {
114*c8dee2aaSAndroid Build Coastguard Worker const auto elapsed = std::chrono::steady_clock::now() - start;
115*c8dee2aaSAndroid Build Coastguard Worker return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
116*c8dee2aaSAndroid Build Coastguard Worker }
117*c8dee2aaSAndroid Build Coastguard Worker
make_file_stream(size_t frame_index,const char * extension)118*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkFILEWStream> make_file_stream(size_t frame_index, const char* extension) {
119*c8dee2aaSAndroid Build Coastguard Worker const auto file = SkStringPrintf("0%06zu.%s", frame_index, extension);
120*c8dee2aaSAndroid Build Coastguard Worker const auto path = SkOSPath::Join(FLAGS_writePath[0], file.c_str());
121*c8dee2aaSAndroid Build Coastguard Worker
122*c8dee2aaSAndroid Build Coastguard Worker auto stream = std::make_unique<SkFILEWStream>(path.c_str());
123*c8dee2aaSAndroid Build Coastguard Worker
124*c8dee2aaSAndroid Build Coastguard Worker return stream->isValid() ? std::move(stream) : nullptr;
125*c8dee2aaSAndroid Build Coastguard Worker }
126*c8dee2aaSAndroid Build Coastguard Worker
127*c8dee2aaSAndroid Build Coastguard Worker class FrameSink {
128*c8dee2aaSAndroid Build Coastguard Worker public:
129*c8dee2aaSAndroid Build Coastguard Worker virtual ~FrameSink() = default;
130*c8dee2aaSAndroid Build Coastguard Worker
131*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<FrameSink> Make(OutputFormat fmt, size_t frame_count);
132*c8dee2aaSAndroid Build Coastguard Worker
133*c8dee2aaSAndroid Build Coastguard Worker virtual void writeFrame(sk_sp<SkImage> frame, size_t frame_index) = 0;
134*c8dee2aaSAndroid Build Coastguard Worker
finalize(double fps)135*c8dee2aaSAndroid Build Coastguard Worker virtual void finalize(double fps) {}
136*c8dee2aaSAndroid Build Coastguard Worker
137*c8dee2aaSAndroid Build Coastguard Worker protected:
138*c8dee2aaSAndroid Build Coastguard Worker FrameSink() = default;
139*c8dee2aaSAndroid Build Coastguard Worker
140*c8dee2aaSAndroid Build Coastguard Worker private:
141*c8dee2aaSAndroid Build Coastguard Worker FrameSink(const FrameSink&) = delete;
142*c8dee2aaSAndroid Build Coastguard Worker FrameSink& operator=(const FrameSink&) = delete;
143*c8dee2aaSAndroid Build Coastguard Worker };
144*c8dee2aaSAndroid Build Coastguard Worker
145*c8dee2aaSAndroid Build Coastguard Worker class PNGSink final : public FrameSink {
146*c8dee2aaSAndroid Build Coastguard Worker public:
writeFrame(sk_sp<SkImage> frame,size_t frame_index)147*c8dee2aaSAndroid Build Coastguard Worker void writeFrame(sk_sp<SkImage> frame, size_t frame_index) override {
148*c8dee2aaSAndroid Build Coastguard Worker auto stream = make_file_stream(frame_index, "png");
149*c8dee2aaSAndroid Build Coastguard Worker
150*c8dee2aaSAndroid Build Coastguard Worker if (!frame || !stream) {
151*c8dee2aaSAndroid Build Coastguard Worker return;
152*c8dee2aaSAndroid Build Coastguard Worker }
153*c8dee2aaSAndroid Build Coastguard Worker
154*c8dee2aaSAndroid Build Coastguard Worker // Set encoding options to favor speed over size.
155*c8dee2aaSAndroid Build Coastguard Worker SkPngEncoder::Options options;
156*c8dee2aaSAndroid Build Coastguard Worker options.fZLibLevel = 1;
157*c8dee2aaSAndroid Build Coastguard Worker options.fFilterFlags = SkPngEncoder::FilterFlag::kNone;
158*c8dee2aaSAndroid Build Coastguard Worker
159*c8dee2aaSAndroid Build Coastguard Worker SkPixmap pixmap;
160*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(frame->peekPixels(&pixmap));
161*c8dee2aaSAndroid Build Coastguard Worker
162*c8dee2aaSAndroid Build Coastguard Worker SkPngEncoder::Encode(stream.get(), pixmap, options);
163*c8dee2aaSAndroid Build Coastguard Worker }
164*c8dee2aaSAndroid Build Coastguard Worker };
165*c8dee2aaSAndroid Build Coastguard Worker
166*c8dee2aaSAndroid Build Coastguard Worker class NullSink final : public FrameSink {
167*c8dee2aaSAndroid Build Coastguard Worker public:
writeFrame(sk_sp<SkImage>,size_t)168*c8dee2aaSAndroid Build Coastguard Worker void writeFrame(sk_sp<SkImage>, size_t) override {}
169*c8dee2aaSAndroid Build Coastguard Worker };
170*c8dee2aaSAndroid Build Coastguard Worker
171*c8dee2aaSAndroid Build Coastguard Worker #if defined(HAVE_VIDEO_ENCODER)
172*c8dee2aaSAndroid Build Coastguard Worker class MP4Sink final : public FrameSink {
173*c8dee2aaSAndroid Build Coastguard Worker public:
MP4Sink(size_t frame_count)174*c8dee2aaSAndroid Build Coastguard Worker explicit MP4Sink(size_t frame_count) {
175*c8dee2aaSAndroid Build Coastguard Worker fFrames.resize(frame_count);
176*c8dee2aaSAndroid Build Coastguard Worker }
177*c8dee2aaSAndroid Build Coastguard Worker
writeFrame(sk_sp<SkImage> frame,size_t frame_index)178*c8dee2aaSAndroid Build Coastguard Worker void writeFrame(sk_sp<SkImage> frame, size_t frame_index) override {
179*c8dee2aaSAndroid Build Coastguard Worker fFrames[frame_index].set_value(std::move(frame));
180*c8dee2aaSAndroid Build Coastguard Worker }
181*c8dee2aaSAndroid Build Coastguard Worker
finalize(double fps)182*c8dee2aaSAndroid Build Coastguard Worker void finalize(double fps) override {
183*c8dee2aaSAndroid Build Coastguard Worker SkVideoEncoder encoder;
184*c8dee2aaSAndroid Build Coastguard Worker if (!encoder.beginRecording({FLAGS_width, FLAGS_height}, sk_double_round2int(fps))) {
185*c8dee2aaSAndroid Build Coastguard Worker fprintf(stderr, "Invalid video stream configuration.\n");
186*c8dee2aaSAndroid Build Coastguard Worker }
187*c8dee2aaSAndroid Build Coastguard Worker
188*c8dee2aaSAndroid Build Coastguard Worker std::vector<double> starved_ms;
189*c8dee2aaSAndroid Build Coastguard Worker starved_ms.reserve(fFrames.size());
190*c8dee2aaSAndroid Build Coastguard Worker
191*c8dee2aaSAndroid Build Coastguard Worker for (auto& frame_promise : fFrames) {
192*c8dee2aaSAndroid Build Coastguard Worker const auto start = std::chrono::steady_clock::now();
193*c8dee2aaSAndroid Build Coastguard Worker auto frame = frame_promise.get_future().get();
194*c8dee2aaSAndroid Build Coastguard Worker starved_ms.push_back(ms_since(start));
195*c8dee2aaSAndroid Build Coastguard Worker
196*c8dee2aaSAndroid Build Coastguard Worker if (!frame) continue;
197*c8dee2aaSAndroid Build Coastguard Worker
198*c8dee2aaSAndroid Build Coastguard Worker SkPixmap pixmap;
199*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(frame->peekPixels(&pixmap));
200*c8dee2aaSAndroid Build Coastguard Worker encoder.addFrame(pixmap);
201*c8dee2aaSAndroid Build Coastguard Worker }
202*c8dee2aaSAndroid Build Coastguard Worker
203*c8dee2aaSAndroid Build Coastguard Worker auto mp4 = encoder.endRecording();
204*c8dee2aaSAndroid Build Coastguard Worker
205*c8dee2aaSAndroid Build Coastguard Worker SkFILEWStream{FLAGS_writePath[0]}
206*c8dee2aaSAndroid Build Coastguard Worker .write(mp4->data(), mp4->size());
207*c8dee2aaSAndroid Build Coastguard Worker
208*c8dee2aaSAndroid Build Coastguard Worker // If everything's going well, the first frame should account for the most,
209*c8dee2aaSAndroid Build Coastguard Worker // and ideally nearly all, starvation.
210*c8dee2aaSAndroid Build Coastguard Worker double first = starved_ms[0];
211*c8dee2aaSAndroid Build Coastguard Worker std::sort(starved_ms.begin(), starved_ms.end());
212*c8dee2aaSAndroid Build Coastguard Worker double sum = std::accumulate(starved_ms.begin(), starved_ms.end(), 0);
213*c8dee2aaSAndroid Build Coastguard Worker printf("Encoder starved stats: "
214*c8dee2aaSAndroid Build Coastguard Worker "min %gms, med %gms, avg %gms, max %gms, sum %gms, first %gms (%s)\n",
215*c8dee2aaSAndroid Build Coastguard Worker starved_ms[0], starved_ms[fFrames.size()/2], sum/fFrames.size(), starved_ms.back(),
216*c8dee2aaSAndroid Build Coastguard Worker sum, first, first == starved_ms.back() ? "ok" : "BAD");
217*c8dee2aaSAndroid Build Coastguard Worker
218*c8dee2aaSAndroid Build Coastguard Worker }
219*c8dee2aaSAndroid Build Coastguard Worker
220*c8dee2aaSAndroid Build Coastguard Worker std::vector<std::promise<sk_sp<SkImage>>> fFrames;
221*c8dee2aaSAndroid Build Coastguard Worker };
222*c8dee2aaSAndroid Build Coastguard Worker #endif // HAVE_VIDEO_ENCODER
223*c8dee2aaSAndroid Build Coastguard Worker
Make(OutputFormat fmt,size_t frame_count)224*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<FrameSink> FrameSink::Make(OutputFormat fmt, size_t frame_count) {
225*c8dee2aaSAndroid Build Coastguard Worker switch (fmt) {
226*c8dee2aaSAndroid Build Coastguard Worker case OutputFormat::kPNG:
227*c8dee2aaSAndroid Build Coastguard Worker return std::make_unique<PNGSink>();
228*c8dee2aaSAndroid Build Coastguard Worker case OutputFormat::kSKP:
229*c8dee2aaSAndroid Build Coastguard Worker // The SKP generator does not use a sink.
230*c8dee2aaSAndroid Build Coastguard Worker [[fallthrough]];
231*c8dee2aaSAndroid Build Coastguard Worker case OutputFormat::kNull:
232*c8dee2aaSAndroid Build Coastguard Worker return std::make_unique<NullSink>();
233*c8dee2aaSAndroid Build Coastguard Worker case OutputFormat::kMP4:
234*c8dee2aaSAndroid Build Coastguard Worker #if defined(HAVE_VIDEO_ENCODER)
235*c8dee2aaSAndroid Build Coastguard Worker return std::make_unique<MP4Sink>(frame_count);
236*c8dee2aaSAndroid Build Coastguard Worker #else
237*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
238*c8dee2aaSAndroid Build Coastguard Worker #endif
239*c8dee2aaSAndroid Build Coastguard Worker }
240*c8dee2aaSAndroid Build Coastguard Worker
241*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
242*c8dee2aaSAndroid Build Coastguard Worker }
243*c8dee2aaSAndroid Build Coastguard Worker
244*c8dee2aaSAndroid Build Coastguard Worker class FrameGenerator {
245*c8dee2aaSAndroid Build Coastguard Worker public:
246*c8dee2aaSAndroid Build Coastguard Worker virtual ~FrameGenerator() = default;
247*c8dee2aaSAndroid Build Coastguard Worker
248*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<FrameGenerator> Make(FrameSink*, OutputFormat, const SkMatrix&);
249*c8dee2aaSAndroid Build Coastguard Worker
generateFrame(const skottie::Animation *,size_t frame_index)250*c8dee2aaSAndroid Build Coastguard Worker virtual void generateFrame(const skottie::Animation*, size_t frame_index) {}
251*c8dee2aaSAndroid Build Coastguard Worker
252*c8dee2aaSAndroid Build Coastguard Worker protected:
FrameGenerator(FrameSink * sink)253*c8dee2aaSAndroid Build Coastguard Worker explicit FrameGenerator(FrameSink* sink) : fSink(sink) {}
254*c8dee2aaSAndroid Build Coastguard Worker
255*c8dee2aaSAndroid Build Coastguard Worker FrameSink* fSink;
256*c8dee2aaSAndroid Build Coastguard Worker
257*c8dee2aaSAndroid Build Coastguard Worker private:
258*c8dee2aaSAndroid Build Coastguard Worker FrameGenerator(const FrameGenerator&) = delete;
259*c8dee2aaSAndroid Build Coastguard Worker FrameGenerator& operator=(const FrameGenerator&) = delete;
260*c8dee2aaSAndroid Build Coastguard Worker };
261*c8dee2aaSAndroid Build Coastguard Worker
262*c8dee2aaSAndroid Build Coastguard Worker class CPUGenerator final : public FrameGenerator {
263*c8dee2aaSAndroid Build Coastguard Worker public:
264*c8dee2aaSAndroid Build Coastguard Worker #if defined(GPU_ONLY)
Make(FrameSink * sink,const SkMatrix & matrix)265*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& matrix) {
266*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
267*c8dee2aaSAndroid Build Coastguard Worker }
268*c8dee2aaSAndroid Build Coastguard Worker #else
269*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& matrix) {
270*c8dee2aaSAndroid Build Coastguard Worker auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(FLAGS_width, FLAGS_height));
271*c8dee2aaSAndroid Build Coastguard Worker if (!surface) {
272*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("Could not allocate a %d x %d surface.\n", FLAGS_width, FLAGS_height);
273*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
274*c8dee2aaSAndroid Build Coastguard Worker }
275*c8dee2aaSAndroid Build Coastguard Worker
276*c8dee2aaSAndroid Build Coastguard Worker return std::unique_ptr<FrameGenerator>(new CPUGenerator(sink, std::move(surface), matrix));
277*c8dee2aaSAndroid Build Coastguard Worker }
278*c8dee2aaSAndroid Build Coastguard Worker
279*c8dee2aaSAndroid Build Coastguard Worker void generateFrame(const skottie::Animation* anim, size_t frame_index) override {
280*c8dee2aaSAndroid Build Coastguard Worker fSurface->getCanvas()->clear(kClearColor);
281*c8dee2aaSAndroid Build Coastguard Worker anim->render(fSurface->getCanvas());
282*c8dee2aaSAndroid Build Coastguard Worker
283*c8dee2aaSAndroid Build Coastguard Worker fSink->writeFrame(fSurface->makeImageSnapshot(), frame_index);
284*c8dee2aaSAndroid Build Coastguard Worker }
285*c8dee2aaSAndroid Build Coastguard Worker
286*c8dee2aaSAndroid Build Coastguard Worker private:
287*c8dee2aaSAndroid Build Coastguard Worker CPUGenerator(FrameSink* sink, sk_sp<SkSurface> surface, const SkMatrix& scale_matrix)
288*c8dee2aaSAndroid Build Coastguard Worker : FrameGenerator(sink)
289*c8dee2aaSAndroid Build Coastguard Worker , fSurface(std::move(surface))
290*c8dee2aaSAndroid Build Coastguard Worker {
291*c8dee2aaSAndroid Build Coastguard Worker fSurface->getCanvas()->concat(scale_matrix);
292*c8dee2aaSAndroid Build Coastguard Worker }
293*c8dee2aaSAndroid Build Coastguard Worker
294*c8dee2aaSAndroid Build Coastguard Worker const sk_sp<SkSurface> fSurface;
295*c8dee2aaSAndroid Build Coastguard Worker #endif // !GPU_ONLY
296*c8dee2aaSAndroid Build Coastguard Worker };
297*c8dee2aaSAndroid Build Coastguard Worker
298*c8dee2aaSAndroid Build Coastguard Worker class SKPGenerator final : public FrameGenerator {
299*c8dee2aaSAndroid Build Coastguard Worker public:
300*c8dee2aaSAndroid Build Coastguard Worker #if defined(CPU_ONLY) || defined(GPU_ONLY)
Make(FrameSink * sink,const SkMatrix & matrix)301*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& matrix) {
302*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
303*c8dee2aaSAndroid Build Coastguard Worker }
304*c8dee2aaSAndroid Build Coastguard Worker #else
305*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& scale_matrix) {
306*c8dee2aaSAndroid Build Coastguard Worker return std::unique_ptr<FrameGenerator>(new SKPGenerator(sink, scale_matrix));
307*c8dee2aaSAndroid Build Coastguard Worker }
308*c8dee2aaSAndroid Build Coastguard Worker
309*c8dee2aaSAndroid Build Coastguard Worker void generateFrame(const skottie::Animation* anim, size_t frame_index) override {
310*c8dee2aaSAndroid Build Coastguard Worker auto* canvas = fRecorder.beginRecording(FLAGS_width, FLAGS_height);
311*c8dee2aaSAndroid Build Coastguard Worker canvas->concat(fScaleMatrix);
312*c8dee2aaSAndroid Build Coastguard Worker anim->render(canvas);
313*c8dee2aaSAndroid Build Coastguard Worker
314*c8dee2aaSAndroid Build Coastguard Worker auto frame = fRecorder.finishRecordingAsPicture();
315*c8dee2aaSAndroid Build Coastguard Worker auto stream = make_file_stream(frame_index, "skp");
316*c8dee2aaSAndroid Build Coastguard Worker
317*c8dee2aaSAndroid Build Coastguard Worker if (frame && stream) {
318*c8dee2aaSAndroid Build Coastguard Worker SkSerialProcs sProcs;
319*c8dee2aaSAndroid Build Coastguard Worker sProcs.fImageProc = [](SkImage* img, void*) -> sk_sp<SkData> {
320*c8dee2aaSAndroid Build Coastguard Worker return SkPngEncoder::Encode(as_IB(img)->directContext(), img,
321*c8dee2aaSAndroid Build Coastguard Worker SkPngEncoder::Options{});
322*c8dee2aaSAndroid Build Coastguard Worker };
323*c8dee2aaSAndroid Build Coastguard Worker frame->serialize(stream.get(), &sProcs);
324*c8dee2aaSAndroid Build Coastguard Worker }
325*c8dee2aaSAndroid Build Coastguard Worker }
326*c8dee2aaSAndroid Build Coastguard Worker
327*c8dee2aaSAndroid Build Coastguard Worker private:
328*c8dee2aaSAndroid Build Coastguard Worker SKPGenerator(FrameSink* sink, const SkMatrix& scale_matrix)
329*c8dee2aaSAndroid Build Coastguard Worker : FrameGenerator(sink)
330*c8dee2aaSAndroid Build Coastguard Worker , fScaleMatrix(scale_matrix)
331*c8dee2aaSAndroid Build Coastguard Worker {}
332*c8dee2aaSAndroid Build Coastguard Worker
333*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix fScaleMatrix;
334*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder fRecorder;
335*c8dee2aaSAndroid Build Coastguard Worker #endif // !CPU_ONLY && !GPU_ONLY
336*c8dee2aaSAndroid Build Coastguard Worker };
337*c8dee2aaSAndroid Build Coastguard Worker
338*c8dee2aaSAndroid Build Coastguard Worker class GPUGenerator final : public FrameGenerator {
339*c8dee2aaSAndroid Build Coastguard Worker public:
340*c8dee2aaSAndroid Build Coastguard Worker #if defined(CPU_ONLY)
Make(FrameSink * sink,const SkMatrix & matrix)341*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& matrix) {
342*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
343*c8dee2aaSAndroid Build Coastguard Worker }
344*c8dee2aaSAndroid Build Coastguard Worker #else
345*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& matrix) {
346*c8dee2aaSAndroid Build Coastguard Worker auto gpu_generator = std::unique_ptr<GPUGenerator>(new GPUGenerator(sink, matrix));
347*c8dee2aaSAndroid Build Coastguard Worker
348*c8dee2aaSAndroid Build Coastguard Worker return gpu_generator->isValid()
349*c8dee2aaSAndroid Build Coastguard Worker ? std::unique_ptr<FrameGenerator>(gpu_generator.release())
350*c8dee2aaSAndroid Build Coastguard Worker : nullptr;
351*c8dee2aaSAndroid Build Coastguard Worker }
352*c8dee2aaSAndroid Build Coastguard Worker
353*c8dee2aaSAndroid Build Coastguard Worker ~GPUGenerator() override {
354*c8dee2aaSAndroid Build Coastguard Worker // ensure all pending reads are completed
355*c8dee2aaSAndroid Build Coastguard Worker fCtx->flushAndSubmit(GrSyncCpu::kYes);
356*c8dee2aaSAndroid Build Coastguard Worker }
357*c8dee2aaSAndroid Build Coastguard Worker
358*c8dee2aaSAndroid Build Coastguard Worker void generateFrame(const skottie::Animation* anim, size_t frame_index) override {
359*c8dee2aaSAndroid Build Coastguard Worker fSurface->getCanvas()->clear(kClearColor);
360*c8dee2aaSAndroid Build Coastguard Worker anim->render(fSurface->getCanvas());
361*c8dee2aaSAndroid Build Coastguard Worker
362*c8dee2aaSAndroid Build Coastguard Worker auto rec = std::make_unique<AsyncRec>(fSink, frame_index);
363*c8dee2aaSAndroid Build Coastguard Worker fSurface->asyncRescaleAndReadPixels(SkImageInfo::MakeN32Premul(FLAGS_width, FLAGS_height),
364*c8dee2aaSAndroid Build Coastguard Worker {0, 0, FLAGS_width, FLAGS_height},
365*c8dee2aaSAndroid Build Coastguard Worker SkSurface::RescaleGamma::kSrc,
366*c8dee2aaSAndroid Build Coastguard Worker SkImage::RescaleMode::kNearest,
367*c8dee2aaSAndroid Build Coastguard Worker AsyncCallback, rec.release());
368*c8dee2aaSAndroid Build Coastguard Worker
369*c8dee2aaSAndroid Build Coastguard Worker fCtx->submit();
370*c8dee2aaSAndroid Build Coastguard Worker }
371*c8dee2aaSAndroid Build Coastguard Worker
372*c8dee2aaSAndroid Build Coastguard Worker private:
373*c8dee2aaSAndroid Build Coastguard Worker GPUGenerator(FrameSink* sink, const SkMatrix& matrix)
374*c8dee2aaSAndroid Build Coastguard Worker : FrameGenerator(sink)
375*c8dee2aaSAndroid Build Coastguard Worker {
376*c8dee2aaSAndroid Build Coastguard Worker fCtx = fFactory.getContextInfo(skgpu::ContextType::kGL).directContext();
377*c8dee2aaSAndroid Build Coastguard Worker fSurface = SkSurfaces::RenderTarget(fCtx,
378*c8dee2aaSAndroid Build Coastguard Worker skgpu::Budgeted::kNo,
379*c8dee2aaSAndroid Build Coastguard Worker SkImageInfo::MakeN32Premul(FLAGS_width, FLAGS_height),
380*c8dee2aaSAndroid Build Coastguard Worker 0,
381*c8dee2aaSAndroid Build Coastguard Worker GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
382*c8dee2aaSAndroid Build Coastguard Worker nullptr);
383*c8dee2aaSAndroid Build Coastguard Worker if (fSurface) {
384*c8dee2aaSAndroid Build Coastguard Worker fSurface->getCanvas()->concat(matrix);
385*c8dee2aaSAndroid Build Coastguard Worker } else {
386*c8dee2aaSAndroid Build Coastguard Worker fprintf(stderr, "Could not initialize GL context.\n");
387*c8dee2aaSAndroid Build Coastguard Worker }
388*c8dee2aaSAndroid Build Coastguard Worker }
389*c8dee2aaSAndroid Build Coastguard Worker
390*c8dee2aaSAndroid Build Coastguard Worker bool isValid() const { return !!fSurface; }
391*c8dee2aaSAndroid Build Coastguard Worker
392*c8dee2aaSAndroid Build Coastguard Worker struct AsyncRec {
393*c8dee2aaSAndroid Build Coastguard Worker FrameSink* sink;
394*c8dee2aaSAndroid Build Coastguard Worker size_t index;
395*c8dee2aaSAndroid Build Coastguard Worker
396*c8dee2aaSAndroid Build Coastguard Worker AsyncRec(FrameSink* sink, size_t index) : sink(sink), index(index) {}
397*c8dee2aaSAndroid Build Coastguard Worker };
398*c8dee2aaSAndroid Build Coastguard Worker
399*c8dee2aaSAndroid Build Coastguard Worker static void AsyncCallback(SkSurface::ReadPixelsContext ctx,
400*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<const SkSurface::AsyncReadResult> result) {
401*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<const AsyncRec> rec(reinterpret_cast<const AsyncRec*>(ctx));
402*c8dee2aaSAndroid Build Coastguard Worker if (result && result->count() == 1) {
403*c8dee2aaSAndroid Build Coastguard Worker SkPixmap pm(SkImageInfo::MakeN32Premul(FLAGS_width, FLAGS_height),
404*c8dee2aaSAndroid Build Coastguard Worker result->data(0), result->rowBytes(0));
405*c8dee2aaSAndroid Build Coastguard Worker
406*c8dee2aaSAndroid Build Coastguard Worker auto release_proc = [](const void*, SkImages::ReleaseContext ctx) {
407*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<const SkSurface::AsyncReadResult>
408*c8dee2aaSAndroid Build Coastguard Worker adopted(reinterpret_cast<const SkSurface::AsyncReadResult*>(ctx));
409*c8dee2aaSAndroid Build Coastguard Worker };
410*c8dee2aaSAndroid Build Coastguard Worker
411*c8dee2aaSAndroid Build Coastguard Worker auto frame_image =
412*c8dee2aaSAndroid Build Coastguard Worker SkImages::RasterFromPixmap(pm, release_proc, (void*)result.release());
413*c8dee2aaSAndroid Build Coastguard Worker
414*c8dee2aaSAndroid Build Coastguard Worker rec->sink->writeFrame(std::move(frame_image), rec->index);
415*c8dee2aaSAndroid Build Coastguard Worker }
416*c8dee2aaSAndroid Build Coastguard Worker }
417*c8dee2aaSAndroid Build Coastguard Worker
418*c8dee2aaSAndroid Build Coastguard Worker sk_gpu_test::GrContextFactory fFactory;
419*c8dee2aaSAndroid Build Coastguard Worker GrDirectContext* fCtx;
420*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkSurface> fSurface;
421*c8dee2aaSAndroid Build Coastguard Worker #endif // !CPU_ONLY
422*c8dee2aaSAndroid Build Coastguard Worker };
423*c8dee2aaSAndroid Build Coastguard Worker
Make(FrameSink * sink,OutputFormat fmt,const SkMatrix & matrix)424*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<FrameGenerator> FrameGenerator::Make(FrameSink* sink,
425*c8dee2aaSAndroid Build Coastguard Worker OutputFormat fmt,
426*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& matrix) {
427*c8dee2aaSAndroid Build Coastguard Worker if (fmt == OutputFormat::kSKP) {
428*c8dee2aaSAndroid Build Coastguard Worker return SKPGenerator::Make(sink, matrix);
429*c8dee2aaSAndroid Build Coastguard Worker }
430*c8dee2aaSAndroid Build Coastguard Worker
431*c8dee2aaSAndroid Build Coastguard Worker return FLAGS_gpu
432*c8dee2aaSAndroid Build Coastguard Worker ? GPUGenerator::Make(sink, matrix)
433*c8dee2aaSAndroid Build Coastguard Worker : CPUGenerator::Make(sink, matrix);
434*c8dee2aaSAndroid Build Coastguard Worker }
435*c8dee2aaSAndroid Build Coastguard Worker
436*c8dee2aaSAndroid Build Coastguard Worker class Logger final : public skottie::Logger {
437*c8dee2aaSAndroid Build Coastguard Worker public:
438*c8dee2aaSAndroid Build Coastguard Worker struct LogEntry {
439*c8dee2aaSAndroid Build Coastguard Worker SkString fMessage,
440*c8dee2aaSAndroid Build Coastguard Worker fJSON;
441*c8dee2aaSAndroid Build Coastguard Worker };
442*c8dee2aaSAndroid Build Coastguard Worker
log(skottie::Logger::Level lvl,const char message[],const char json[])443*c8dee2aaSAndroid Build Coastguard Worker void log(skottie::Logger::Level lvl, const char message[], const char json[]) override {
444*c8dee2aaSAndroid Build Coastguard Worker auto& log = lvl == skottie::Logger::Level::kError ? fErrors : fWarnings;
445*c8dee2aaSAndroid Build Coastguard Worker log.push_back({ SkString(message), json ? SkString(json) : SkString() });
446*c8dee2aaSAndroid Build Coastguard Worker }
447*c8dee2aaSAndroid Build Coastguard Worker
report() const448*c8dee2aaSAndroid Build Coastguard Worker void report() const {
449*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("Animation loaded with %zu error%s, %zu warning%s.\n",
450*c8dee2aaSAndroid Build Coastguard Worker fErrors.size(), fErrors.size() == 1 ? "" : "s",
451*c8dee2aaSAndroid Build Coastguard Worker fWarnings.size(), fWarnings.size() == 1 ? "" : "s");
452*c8dee2aaSAndroid Build Coastguard Worker
453*c8dee2aaSAndroid Build Coastguard Worker const auto& show = [](const LogEntry& log, const char prefix[]) {
454*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("%s%s", prefix, log.fMessage.c_str());
455*c8dee2aaSAndroid Build Coastguard Worker if (!log.fJSON.isEmpty())
456*c8dee2aaSAndroid Build Coastguard Worker SkDebugf(" : %s", log.fJSON.c_str());
457*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("\n");
458*c8dee2aaSAndroid Build Coastguard Worker };
459*c8dee2aaSAndroid Build Coastguard Worker
460*c8dee2aaSAndroid Build Coastguard Worker for (const auto& err : fErrors) show(err, " !! ");
461*c8dee2aaSAndroid Build Coastguard Worker for (const auto& wrn : fWarnings) show(wrn, " ?? ");
462*c8dee2aaSAndroid Build Coastguard Worker }
463*c8dee2aaSAndroid Build Coastguard Worker
464*c8dee2aaSAndroid Build Coastguard Worker private:
465*c8dee2aaSAndroid Build Coastguard Worker std::vector<LogEntry> fErrors,
466*c8dee2aaSAndroid Build Coastguard Worker fWarnings;
467*c8dee2aaSAndroid Build Coastguard Worker };
468*c8dee2aaSAndroid Build Coastguard Worker
469*c8dee2aaSAndroid Build Coastguard Worker } // namespace
470*c8dee2aaSAndroid Build Coastguard Worker
471*c8dee2aaSAndroid Build Coastguard Worker extern bool gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental;
472*c8dee2aaSAndroid Build Coastguard Worker
main(int argc,char ** argv)473*c8dee2aaSAndroid Build Coastguard Worker int main(int argc, char** argv) {
474*c8dee2aaSAndroid Build Coastguard Worker gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental = true;
475*c8dee2aaSAndroid Build Coastguard Worker CommandLineFlags::Parse(argc, argv);
476*c8dee2aaSAndroid Build Coastguard Worker SkGraphics::Init();
477*c8dee2aaSAndroid Build Coastguard Worker
478*c8dee2aaSAndroid Build Coastguard Worker if (FLAGS_input.isEmpty() || FLAGS_writePath.isEmpty()) {
479*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("Missing required 'input' and 'writePath' args.\n");
480*c8dee2aaSAndroid Build Coastguard Worker return 1;
481*c8dee2aaSAndroid Build Coastguard Worker }
482*c8dee2aaSAndroid Build Coastguard Worker
483*c8dee2aaSAndroid Build Coastguard Worker OutputFormat fmt;
484*c8dee2aaSAndroid Build Coastguard Worker if (0 == std::strcmp(FLAGS_format[0], "png")) {
485*c8dee2aaSAndroid Build Coastguard Worker fmt = OutputFormat::kPNG;
486*c8dee2aaSAndroid Build Coastguard Worker } else if (0 == std::strcmp(FLAGS_format[0], "skp")) {
487*c8dee2aaSAndroid Build Coastguard Worker fmt = OutputFormat::kSKP;
488*c8dee2aaSAndroid Build Coastguard Worker } else if (0 == std::strcmp(FLAGS_format[0], "null")) {
489*c8dee2aaSAndroid Build Coastguard Worker fmt = OutputFormat::kNull;
490*c8dee2aaSAndroid Build Coastguard Worker #if defined(HAVE_VIDEO_ENCODER)
491*c8dee2aaSAndroid Build Coastguard Worker } else if (0 == std::strcmp(FLAGS_format[0], "mp4")) {
492*c8dee2aaSAndroid Build Coastguard Worker fmt = OutputFormat::kMP4;
493*c8dee2aaSAndroid Build Coastguard Worker #endif
494*c8dee2aaSAndroid Build Coastguard Worker } else {
495*c8dee2aaSAndroid Build Coastguard Worker fprintf(stderr, "Unknown format: %s\n", FLAGS_format[0]);
496*c8dee2aaSAndroid Build Coastguard Worker return 1;
497*c8dee2aaSAndroid Build Coastguard Worker }
498*c8dee2aaSAndroid Build Coastguard Worker
499*c8dee2aaSAndroid Build Coastguard Worker if (fmt != OutputFormat::kMP4 && !sk_mkdir(FLAGS_writePath[0])) {
500*c8dee2aaSAndroid Build Coastguard Worker return 1;
501*c8dee2aaSAndroid Build Coastguard Worker }
502*c8dee2aaSAndroid Build Coastguard Worker
503*c8dee2aaSAndroid Build Coastguard Worker SkCodecs::Register(SkPngDecoder::Decoder());
504*c8dee2aaSAndroid Build Coastguard Worker SkCodecs::Register(SkJpegDecoder::Decoder());
505*c8dee2aaSAndroid Build Coastguard Worker SkCodecs::Register(SkWebpDecoder::Decoder());
506*c8dee2aaSAndroid Build Coastguard Worker
507*c8dee2aaSAndroid Build Coastguard Worker // If necessary, clients should use a font manager that would load fonts from the system.
508*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_BUILD_FOR_MAC) && defined(SK_FONTMGR_CORETEXT_AVAILABLE)
509*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_CoreText(nullptr);
510*c8dee2aaSAndroid Build Coastguard Worker #elif defined(SK_BUILD_FOR_ANDROID) && defined(SK_FONTMGR_ANDROID_AVAILABLE) && defined(SK_TYPEFACE_FACTORY_FREETYPE)
511*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_Android(nullptr, SkFontScanner_Make_FreeType());
512*c8dee2aaSAndroid Build Coastguard Worker #elif defined(SK_BUILD_FOR_UNIX) && defined(SK_FONTMGR_FONTCONFIG_AVAILABLE) && defined(SK_TYPEFACE_FACTORY_FREETYPE)
513*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_FontConfig(nullptr, SkFontScanner_Make_FreeType());
514*c8dee2aaSAndroid Build Coastguard Worker #else
515*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_Custom_Empty();
516*c8dee2aaSAndroid Build Coastguard Worker #endif
517*c8dee2aaSAndroid Build Coastguard Worker
518*c8dee2aaSAndroid Build Coastguard Worker auto predecode = skresources::ImageDecodeStrategy::kPreDecode;
519*c8dee2aaSAndroid Build Coastguard Worker auto logger = sk_make_sp<Logger>();
520*c8dee2aaSAndroid Build Coastguard Worker auto rp = skresources::CachingResourceProvider::Make(
521*c8dee2aaSAndroid Build Coastguard Worker skresources::DataURIResourceProviderProxy::Make(
522*c8dee2aaSAndroid Build Coastguard Worker skresources::FileResourceProvider::Make(SkOSPath::Dirname(FLAGS_input[0]),
523*c8dee2aaSAndroid Build Coastguard Worker predecode),
524*c8dee2aaSAndroid Build Coastguard Worker predecode,
525*c8dee2aaSAndroid Build Coastguard Worker fontMgr));
526*c8dee2aaSAndroid Build Coastguard Worker auto data = SkData::MakeFromFileName(FLAGS_input[0]);
527*c8dee2aaSAndroid Build Coastguard Worker auto precomp_interceptor =
528*c8dee2aaSAndroid Build Coastguard Worker sk_make_sp<skottie_utils::ExternalAnimationPrecompInterceptor>(rp, "__");
529*c8dee2aaSAndroid Build Coastguard Worker
530*c8dee2aaSAndroid Build Coastguard Worker if (!data) {
531*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("Could not load %s.\n", FLAGS_input[0]);
532*c8dee2aaSAndroid Build Coastguard Worker return 1;
533*c8dee2aaSAndroid Build Coastguard Worker }
534*c8dee2aaSAndroid Build Coastguard Worker
535*c8dee2aaSAndroid Build Coastguard Worker const auto build_animation = [&fontMgr, &rp, &data](
536*c8dee2aaSAndroid Build Coastguard Worker const sk_sp<Logger>& logger,
537*c8dee2aaSAndroid Build Coastguard Worker const sk_sp<skottie::PrecompInterceptor>& pint) {
538*c8dee2aaSAndroid Build Coastguard Worker return skottie::Animation::Builder()
539*c8dee2aaSAndroid Build Coastguard Worker .setFontManager(fontMgr)
540*c8dee2aaSAndroid Build Coastguard Worker .setLogger(logger)
541*c8dee2aaSAndroid Build Coastguard Worker .setPrecompInterceptor(pint)
542*c8dee2aaSAndroid Build Coastguard Worker .setResourceProvider(rp)
543*c8dee2aaSAndroid Build Coastguard Worker .setTextShapingFactory(SkShapers::BestAvailable())
544*c8dee2aaSAndroid Build Coastguard Worker .make(static_cast<const char*>(data->data()), data->size());
545*c8dee2aaSAndroid Build Coastguard Worker };
546*c8dee2aaSAndroid Build Coastguard Worker
547*c8dee2aaSAndroid Build Coastguard Worker // Instantiate an animation on the main thread for two reasons:
548*c8dee2aaSAndroid Build Coastguard Worker // - we need to know its duration upfront
549*c8dee2aaSAndroid Build Coastguard Worker // - we want to only report parsing errors once
550*c8dee2aaSAndroid Build Coastguard Worker const auto anim = build_animation(logger, nullptr);
551*c8dee2aaSAndroid Build Coastguard Worker if (!anim) {
552*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("Could not parse animation: '%s'.\n", FLAGS_input[0]);
553*c8dee2aaSAndroid Build Coastguard Worker return 1;
554*c8dee2aaSAndroid Build Coastguard Worker }
555*c8dee2aaSAndroid Build Coastguard Worker
556*c8dee2aaSAndroid Build Coastguard Worker const auto scale_matrix = SkMatrix::RectToRect(SkRect::MakeSize(anim->size()),
557*c8dee2aaSAndroid Build Coastguard Worker SkRect::MakeIWH(FLAGS_width, FLAGS_height),
558*c8dee2aaSAndroid Build Coastguard Worker SkMatrix::kCenter_ScaleToFit);
559*c8dee2aaSAndroid Build Coastguard Worker logger->report();
560*c8dee2aaSAndroid Build Coastguard Worker
561*c8dee2aaSAndroid Build Coastguard Worker const auto t0 = SkTPin(FLAGS_t0, 0.0, 1.0),
562*c8dee2aaSAndroid Build Coastguard Worker t1 = SkTPin(FLAGS_t1, t0, 1.0),
563*c8dee2aaSAndroid Build Coastguard Worker native_fps = anim->fps(),
564*c8dee2aaSAndroid Build Coastguard Worker frame0 = anim->duration() * t0 * native_fps,
565*c8dee2aaSAndroid Build Coastguard Worker duration = anim->duration() * (t1 - t0);
566*c8dee2aaSAndroid Build Coastguard Worker
567*c8dee2aaSAndroid Build Coastguard Worker double fps = FLAGS_fps > 0 ? FLAGS_fps : native_fps;
568*c8dee2aaSAndroid Build Coastguard Worker if (fps <= 0) {
569*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("Invalid fps: %f.\n", fps);
570*c8dee2aaSAndroid Build Coastguard Worker return 1;
571*c8dee2aaSAndroid Build Coastguard Worker }
572*c8dee2aaSAndroid Build Coastguard Worker
573*c8dee2aaSAndroid Build Coastguard Worker auto frame_count = static_cast<int>(duration * fps);
574*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kMaxFrames = 10000;
575*c8dee2aaSAndroid Build Coastguard Worker if (frame_count > kMaxFrames) {
576*c8dee2aaSAndroid Build Coastguard Worker frame_count = kMaxFrames;
577*c8dee2aaSAndroid Build Coastguard Worker fps = frame_count / duration;
578*c8dee2aaSAndroid Build Coastguard Worker }
579*c8dee2aaSAndroid Build Coastguard Worker const auto fps_scale = native_fps / fps;
580*c8dee2aaSAndroid Build Coastguard Worker
581*c8dee2aaSAndroid Build Coastguard Worker printf("Rendering %f seconds (%d frames @%f fps).\n", duration, frame_count, fps);
582*c8dee2aaSAndroid Build Coastguard Worker
583*c8dee2aaSAndroid Build Coastguard Worker const auto sink = FrameSink::Make(fmt, frame_count);
584*c8dee2aaSAndroid Build Coastguard Worker
585*c8dee2aaSAndroid Build Coastguard Worker std::vector<double> frames_ms(frame_count);
586*c8dee2aaSAndroid Build Coastguard Worker
587*c8dee2aaSAndroid Build Coastguard Worker const auto thread_count = FLAGS_gpu ? 0 : FLAGS_threads - 1;
588*c8dee2aaSAndroid Build Coastguard Worker SkTaskGroup::Enabler enabler(thread_count);
589*c8dee2aaSAndroid Build Coastguard Worker
590*c8dee2aaSAndroid Build Coastguard Worker SkTaskGroup tg;
591*c8dee2aaSAndroid Build Coastguard Worker {
592*c8dee2aaSAndroid Build Coastguard Worker // Depending on type (gpu vs. everything else), we use either a single generator
593*c8dee2aaSAndroid Build Coastguard Worker // or one generator per worker thread, respectively.
594*c8dee2aaSAndroid Build Coastguard Worker // Scoping is important for the single generator case because we want its destructor to
595*c8dee2aaSAndroid Build Coastguard Worker // flush out any pending async operations.
596*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<FrameGenerator> singleton_generator;
597*c8dee2aaSAndroid Build Coastguard Worker if (FLAGS_gpu) {
598*c8dee2aaSAndroid Build Coastguard Worker singleton_generator = FrameGenerator::Make(sink.get(), fmt, scale_matrix);
599*c8dee2aaSAndroid Build Coastguard Worker }
600*c8dee2aaSAndroid Build Coastguard Worker
601*c8dee2aaSAndroid Build Coastguard Worker tg.batch(frame_count, [&](int i) {
602*c8dee2aaSAndroid Build Coastguard Worker // SkTaskGroup::Enabler creates a LIFO work pool,
603*c8dee2aaSAndroid Build Coastguard Worker // but we want our early frames to start first.
604*c8dee2aaSAndroid Build Coastguard Worker i = frame_count - 1 - i;
605*c8dee2aaSAndroid Build Coastguard Worker
606*c8dee2aaSAndroid Build Coastguard Worker const auto start = std::chrono::steady_clock::now();
607*c8dee2aaSAndroid Build Coastguard Worker thread_local static auto* anim =
608*c8dee2aaSAndroid Build Coastguard Worker build_animation(nullptr, precomp_interceptor).release();
609*c8dee2aaSAndroid Build Coastguard Worker thread_local static auto* gen = singleton_generator
610*c8dee2aaSAndroid Build Coastguard Worker ? singleton_generator.get()
611*c8dee2aaSAndroid Build Coastguard Worker : FrameGenerator::Make(sink.get(), fmt, scale_matrix).release();
612*c8dee2aaSAndroid Build Coastguard Worker
613*c8dee2aaSAndroid Build Coastguard Worker if (gen && anim) {
614*c8dee2aaSAndroid Build Coastguard Worker anim->seekFrame(frame0 + i * fps_scale);
615*c8dee2aaSAndroid Build Coastguard Worker gen->generateFrame(anim, SkToSizeT(i));
616*c8dee2aaSAndroid Build Coastguard Worker } else {
617*c8dee2aaSAndroid Build Coastguard Worker sink->writeFrame(nullptr, SkToSizeT(i));
618*c8dee2aaSAndroid Build Coastguard Worker }
619*c8dee2aaSAndroid Build Coastguard Worker
620*c8dee2aaSAndroid Build Coastguard Worker frames_ms[i] = ms_since(start);
621*c8dee2aaSAndroid Build Coastguard Worker });
622*c8dee2aaSAndroid Build Coastguard Worker }
623*c8dee2aaSAndroid Build Coastguard Worker
624*c8dee2aaSAndroid Build Coastguard Worker sink->finalize(fps);
625*c8dee2aaSAndroid Build Coastguard Worker tg.wait();
626*c8dee2aaSAndroid Build Coastguard Worker
627*c8dee2aaSAndroid Build Coastguard Worker
628*c8dee2aaSAndroid Build Coastguard Worker std::sort(frames_ms.begin(), frames_ms.end());
629*c8dee2aaSAndroid Build Coastguard Worker double sum = std::accumulate(frames_ms.begin(), frames_ms.end(), 0);
630*c8dee2aaSAndroid Build Coastguard Worker printf("Frame time stats: min %gms, med %gms, avg %gms, max %gms, sum %gms\n",
631*c8dee2aaSAndroid Build Coastguard Worker frames_ms[0], frames_ms[frame_count/2], sum/frame_count, frames_ms.back(), sum);
632*c8dee2aaSAndroid Build Coastguard Worker
633*c8dee2aaSAndroid Build Coastguard Worker return 0;
634*c8dee2aaSAndroid Build Coastguard Worker }
635