Coverage Report

Created: 2025-06-24 08:20

/src/skia/tools/MSKPPlayer.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2021 Google LLC
3
 *
4
 * Use of this source code is governed by a BSD-style license that can be
5
 * found in the LICENSE file.
6
 */
7
8
#include "tools/MSKPPlayer.h"
9
10
#include "include/core/SkCanvas.h"
11
#include "include/core/SkCanvasVirtualEnforcer.h"
12
#include "include/core/SkPicture.h"
13
#include "include/core/SkPictureRecorder.h"
14
#include "include/core/SkSurface.h"
15
#include "include/docs/SkMultiPictureDocument.h"
16
#include "include/gpu/ganesh/GrDirectContext.h"
17
#include "include/private/base/SkTArray.h"
18
#include "include/utils/SkNoDrawCanvas.h"
19
#include "src/base/SkTLazy.h"
20
#include "src/core/SkCanvasPriv.h"
21
#include "src/core/SkStringUtils.h"
22
#include "tools/SkSharingProc.h"
23
24
using namespace skia_private;
25
26
///////////////////////////////////////////////////////////////////////////////
27
28
// Base Cmd struct.
29
struct MSKPPlayer::Cmd {
30
0
    virtual ~Cmd() = default;
31
    virtual bool isFullRedraw(SkCanvas*) const = 0;
32
    virtual void draw(SkCanvas* canvas, const LayerMap&, LayerStateMap*) const = 0;
33
    // If this command draws another layer, return it's ID. Otherwise, -1.
34
0
    virtual int layerID() const { return -1; }
35
};
36
37
// Draws a SkPicture.
38
struct MSKPPlayer::PicCmd : Cmd {
39
    sk_sp<SkPicture> fContent;
40
    SkIRect fClipRect = SkIRect::MakeEmpty(); // clip for picture (no clip if empty).
41
42
0
    bool isFullRedraw(SkCanvas* canvas) const override {
43
0
        if (fClipRect.isEmpty()) {
44
0
            return false;
45
0
        }
46
0
        return fClipRect.contains(SkIRect::MakeSize(canvas->getBaseLayerSize()));
47
0
    }
48
49
0
    void draw(SkCanvas* canvas, const LayerMap&, LayerStateMap*) const override {
50
0
        if (!fClipRect.isEmpty()) {
51
0
            canvas->save();
52
0
            canvas->clipIRect(fClipRect);
53
0
        }
54
0
        canvas->drawPicture(fContent.get());
55
0
        if (!fClipRect.isEmpty()) {
56
0
            canvas->restore();
57
0
        }
58
0
    }
59
};
60
61
// Draws another layer. Stores the ID of the layer to draw and what command index on that
62
// layer should be current when the layer is drawn. The layer contents are updated to the
63
// stored command index before the layer is drawn.
64
struct MSKPPlayer::DrawLayerCmd : Cmd {
65
    int                         fLayerId;
66
    size_t                      fLayerCmdCnt;
67
    SkRect                      fSrcRect;
68
    SkRect                      fDstRect;
69
    SkSamplingOptions           fSampling;
70
    SkCanvas::SrcRectConstraint fConstraint;
71
    SkTLazy<SkPaint>            fPaint;
72
73
0
    bool isFullRedraw(SkCanvas* canvas) const override { return false; }
74
    void draw(SkCanvas* canvas, const LayerMap&, LayerStateMap*) const override;
75
0
    int layerID() const override { return fLayerId; }
76
};
77
78
void MSKPPlayer::DrawLayerCmd::draw(SkCanvas* canvas,
79
                                    const LayerMap& layerMap,
80
0
                                    LayerStateMap* layerStateMap) const {
81
0
    const LayerCmds& layer = layerMap.at(fLayerId);
82
0
    LayerState* layerState = &(*layerStateMap)[fLayerId];
83
0
    if (!layerState->fSurface) {
84
0
        layerState->fCurrCmd = 0;
85
0
        layerState->fSurface = MSKPPlayer::MakeSurfaceForLayer(layer, canvas);
86
0
        if (!layerState->fSurface) {
87
0
            SkDebugf("Couldn't create surface for layer");
88
0
            return;
89
0
        }
90
0
    }
91
0
    size_t cmd = layerState->fCurrCmd;
92
0
    if (cmd > fLayerCmdCnt) {
93
        // If the layer contains contents from later commands then replay from the beginning.
94
0
        cmd = 0;
95
0
    }
96
0
    SkCanvas* layerCanvas = layerState->fSurface->getCanvas();
97
    // Check if there is a full redraw between cmd and fLayerCmdCnt and if so jump to it and ensure
98
    // we clear the canvas if starting from a full redraw.
99
0
    for (size_t checkCmd = fLayerCmdCnt - 1; checkCmd > cmd; --checkCmd) {
100
0
        if (layer.fCmds[checkCmd]->isFullRedraw(layerCanvas)) {
101
0
            cmd = checkCmd;
102
0
            break;
103
0
        }
104
0
    }
105
0
    for (; cmd < fLayerCmdCnt; ++cmd) {
106
0
        if (cmd == 0 || layer.fCmds[cmd]->isFullRedraw(layerCanvas)) {
107
0
            layerState->fSurface->getCanvas()->clear(SK_ColorTRANSPARENT);
108
0
        }
109
0
        layer.fCmds[cmd]->draw(layerCanvas, layerMap, layerStateMap);
110
0
    }
111
0
    layerState->fCurrCmd = fLayerCmdCnt;
112
0
    const SkPaint* paint = fPaint.isValid() ? fPaint.get() : nullptr;
113
0
    canvas->drawImageRect(layerState->fSurface->makeImageSnapshot(),
114
0
                          fSrcRect,
115
0
                          fDstRect,
116
0
                          fSampling,
117
0
                          paint,
118
0
                          fConstraint);
119
0
}
120
121
///////////////////////////////////////////////////////////////////////////////
122
123
class MSKPPlayer::CmdRecordCanvas : public SkCanvasVirtualEnforcer<SkCanvas> {
124
public:
125
    CmdRecordCanvas(LayerCmds* dst, LayerMap* offscreenLayers, const SkIRect* clipRect = nullptr)
126
0
            : fDst(dst), fOffscreenLayers(offscreenLayers) {
127
0
        if (clipRect) {
128
0
            fClipRect = *clipRect;
129
0
        }
130
0
        fRecorder.beginRecording(SkRect::Make(dst->fDimensions));
131
0
    }
132
0
    ~CmdRecordCanvas() override { this->recordPicCmd(); }
133
134
protected:
135
0
    void onDrawPaint(const SkPaint& paint) override {
136
0
        fRecorder.getRecordingCanvas()->drawPaint(paint);
137
0
    }
138
139
0
    void onDrawBehind(const SkPaint& paint) override {
140
0
        SkCanvasPriv::DrawBehind(fRecorder.getRecordingCanvas(), paint);
141
0
    }
142
143
0
    void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
144
0
        fRecorder.getRecordingCanvas()->drawRect(rect, paint);
145
0
    }
146
147
0
    void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {
148
0
        fRecorder.getRecordingCanvas()->drawRRect(rrect, paint);
149
0
    }
150
151
0
    void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) override {
152
0
        fRecorder.getRecordingCanvas()->drawDRRect(outer, inner, paint);
153
0
    }
154
155
0
    void onDrawOval(const SkRect& rect, const SkPaint& paint) override {
156
0
        fRecorder.getRecordingCanvas()->drawOval(rect, paint);
157
0
    }
158
159
    void onDrawArc(const SkRect& rect,
160
                   SkScalar startAngle,
161
                   SkScalar sweepAngle,
162
                   bool useCenter,
163
0
                   const SkPaint& paint) override {
164
0
        fRecorder.getRecordingCanvas()->drawArc(rect, startAngle, sweepAngle, useCenter, paint);
165
0
    }
166
167
0
    void onDrawPath(const SkPath& path, const SkPaint& paint) override {
168
0
        fRecorder.getRecordingCanvas()->drawPath(path, paint);
169
0
    }
170
171
0
    void onDrawRegion(const SkRegion& region, const SkPaint& paint) override {
172
0
        fRecorder.getRecordingCanvas()->drawRegion(region, paint);
173
0
    }
174
175
    void onDrawTextBlob(const SkTextBlob* blob,
176
                        SkScalar x,
177
                        SkScalar y,
178
0
                        const SkPaint& paint) override {
179
0
        fRecorder.getRecordingCanvas()->drawTextBlob(blob, x, y, paint);
180
0
    }
181
182
    void onDrawPatch(const SkPoint cubics[12],
183
                     const SkColor colors[4],
184
                     const SkPoint texCoords[4],
185
                     SkBlendMode mode,
186
0
                     const SkPaint& paint) override {
187
0
        fRecorder.getRecordingCanvas()->drawPatch(cubics, colors, texCoords, mode, paint);
188
0
    }
189
190
    void onDrawPoints(SkCanvas::PointMode mode,
191
                      size_t count,
192
                      const SkPoint pts[],
193
0
                      const SkPaint& paint) override {
194
0
        fRecorder.getRecordingCanvas()->drawPoints(mode, {pts, count}, paint);
195
0
    }
196
197
    void onDrawImage2(const SkImage* image,
198
                      SkScalar dx,
199
                      SkScalar dy,
200
                      const SkSamplingOptions& sampling,
201
0
                      const SkPaint* paint) override {
202
0
        fRecorder.getRecordingCanvas()->drawImage(image, dx, dy, sampling, paint);
203
0
    }
204
205
    void onDrawImageRect2(const SkImage* image,
206
                          const SkRect& src,
207
                          const SkRect& dst,
208
                          const SkSamplingOptions& sampling,
209
                          const SkPaint* paint,
210
0
                          SrcRectConstraint constraint) override {
211
0
        if (fNextDrawImageFromLayerID != -1) {
212
0
            this->recordPicCmd();
213
0
            auto drawLayer = std::make_unique<DrawLayerCmd>();
214
0
            drawLayer->fLayerId = fNextDrawImageFromLayerID;
215
0
            drawLayer->fLayerCmdCnt = fOffscreenLayers->at(fNextDrawImageFromLayerID).fCmds.size();
216
0
            drawLayer->fSrcRect = src;
217
0
            drawLayer->fDstRect = dst;
218
0
            drawLayer->fSampling = sampling;
219
0
            drawLayer->fConstraint = constraint;
220
0
            if (paint) {
221
0
                drawLayer->fPaint.init(*paint);
222
0
            }
223
0
            fDst->fCmds.push_back(std::move(drawLayer));
224
0
            fNextDrawImageFromLayerID = -1;
225
0
            return;
226
0
        }
227
0
        fRecorder.getRecordingCanvas()->drawImageRect(image, src, dst, sampling, paint, constraint);
228
0
    }
229
230
    void onDrawImageLattice2(const SkImage* image,
231
                             const Lattice& lattice,
232
                             const SkRect& dst,
233
                             SkFilterMode mode,
234
0
                             const SkPaint* paint) override {
235
0
        fRecorder.getRecordingCanvas()->drawImageLattice(image, lattice, dst, mode, paint);
236
0
    }
237
238
    void onDrawAtlas2(const SkImage* image,
239
                      const SkRSXform rsxForms[],
240
                      const SkRect src[],
241
                      const SkColor colors[],
242
                      int count,
243
                      SkBlendMode mode,
244
                      const SkSamplingOptions& sampling,
245
                      const SkRect* cull,
246
0
                      const SkPaint* paint) override {
247
0
        fRecorder.getRecordingCanvas()->drawAtlas(image,
248
0
                                                  {rsxForms, count},
249
0
                                                  {src, count},
250
0
                                                  {colors, colors ? count : 0},
251
0
                                                  mode,
252
0
                                                  sampling,
253
0
                                                  cull,
254
0
                                                  paint);
255
0
    }
256
257
    void onDrawEdgeAAImageSet2(const ImageSetEntry imageSet[],
258
                               int count,
259
                               const SkPoint dstClips[],
260
                               const SkMatrix preViewMatrices[],
261
                               const SkSamplingOptions& sampling,
262
                               const SkPaint* paint,
263
0
                               SrcRectConstraint constraint) override {
264
0
        fRecorder.getRecordingCanvas()->experimental_DrawEdgeAAImageSet(imageSet,
265
0
                                                                        count,
266
0
                                                                        dstClips,
267
0
                                                                        preViewMatrices,
268
0
                                                                        sampling,
269
0
                                                                        paint,
270
0
                                                                        constraint);
271
0
    }
272
273
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
274
    void onDrawEdgeAAQuad(const SkRect& rect,
275
                          const SkPoint clip[4],
276
                          SkCanvas::QuadAAFlags aaFlags,
277
                          const SkColor4f& color,
278
                          SkBlendMode mode) override {}
279
#else
280
    void onDrawEdgeAAQuad(const SkRect& rect,
281
                          const SkPoint clip[4],
282
                          SkCanvas::QuadAAFlags aaFlags,
283
                          const SkColor4f& color,
284
0
                          SkBlendMode mode) override {
285
0
        fRecorder.getRecordingCanvas()->experimental_DrawEdgeAAQuad(rect,
286
0
                                                                    clip,
287
0
                                                                    aaFlags,
288
0
                                                                    color,
289
0
                                                                    mode);
290
0
    }
291
#endif
292
293
0
    void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) override {
294
0
        static constexpr char kOffscreenLayerDraw[] = "OffscreenLayerDraw";
295
0
        static constexpr char kSurfaceID[] = "SurfaceID";
296
0
        TArray<SkString> tokens;
297
0
        SkStrSplit(key, "|", kStrict_SkStrSplitMode, &tokens);
298
0
        if (tokens.size() == 2) {
299
0
            if (tokens[0].equals(kOffscreenLayerDraw)) {
300
                // Indicates that the next drawPicture command contains the SkPicture to render
301
                // to the layer identified by the ID. 'rect' indicates the dirty area to update
302
                // (and indicates the layer size if this command is introducing a new layer id).
303
0
                fNextDrawPictureToLayerID = std::stoi(tokens[1].c_str());
304
0
                fNextDrawPictureToLayerClipRect = rect.roundOut();
305
0
                if (fOffscreenLayers->find(fNextDrawPictureToLayerID) == fOffscreenLayers->end()) {
306
0
                    SkASSERT(fNextDrawPictureToLayerClipRect.left() == 0 &&
307
0
                             fNextDrawPictureToLayerClipRect.top()  == 0);
308
0
                    (*fOffscreenLayers)[fNextDrawPictureToLayerID].fDimensions =
309
0
                            fNextDrawPictureToLayerClipRect.size();
310
0
                }
311
                // The next draw picture will notice that fNextDrawPictureToLayerID is set and
312
                // redirect the picture to the offscreen layer.
313
0
                return;
314
0
            } else if (tokens[0].equals(kSurfaceID)) {
315
                // Indicates that the following drawImageRect should draw an offscreen layer
316
                // to this layer.
317
0
                fNextDrawImageFromLayerID = std::stoi(tokens[1].c_str());
318
0
                return;
319
0
            }
320
0
        }
321
0
    }
Unexecuted instantiation: MSKPPlayer::CmdRecordCanvas::onDrawAnnotation(SkRect const&, char const*, SkData*)
Unexecuted instantiation: MSKPPlayer::CmdRecordCanvas::onDrawAnnotation(SkRect const&, char const*, SkData*)
322
323
0
    void onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) override {
324
0
        fRecorder.getRecordingCanvas()->private_draw_shadow_rec(path, rec);
325
0
    }
326
327
0
    void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
328
0
        fRecorder.getRecordingCanvas()->drawDrawable(drawable, matrix);
329
0
    }
330
331
    void onDrawPicture(const SkPicture* picture,
332
                       const SkMatrix* matrix,
333
0
                       const SkPaint* paint) override {
334
0
        if (fNextDrawPictureToLayerID != -1) {
335
0
            SkASSERT(!matrix);
336
0
            SkASSERT(!paint);
337
0
            LayerCmds* layer = &fOffscreenLayers->at(fNextDrawPictureToLayerID);
338
0
            CmdRecordCanvas sc(layer, fOffscreenLayers, &fNextDrawPictureToLayerClipRect);
339
0
            picture->playback(&sc);
340
0
            fNextDrawPictureToLayerID = -1;
341
0
            fNextDrawPictureToLayerClipRect = SkIRect::MakeEmpty();
342
0
            return;
343
0
        }
344
0
        if (paint) {
345
0
            this->saveLayer(nullptr, paint);
346
0
        }
347
0
        if (matrix) {
348
0
            this->save();
349
0
            this->concat(*matrix);
350
0
        }
351
352
0
        picture->playback(this);
353
354
0
        if (matrix) {
355
0
            this->restore();
356
0
        }
357
0
        if (paint) {
358
0
            this->restore();
359
0
        }
360
0
    }
Unexecuted instantiation: MSKPPlayer::CmdRecordCanvas::onDrawPicture(SkPicture const*, SkMatrix const*, SkPaint const*)
Unexecuted instantiation: MSKPPlayer::CmdRecordCanvas::onDrawPicture(SkPicture const*, SkMatrix const*, SkPaint const*)
361
362
private:
363
0
    void recordPicCmd() {
364
0
        auto cmd = std::make_unique<PicCmd>();
365
0
        cmd->fContent = fRecorder.finishRecordingAsPicture();
366
0
        cmd->fClipRect = fClipRect;
367
0
        if (cmd->fContent) {
368
0
            fDst->fCmds.push_back(std::move(cmd));
369
0
        }
370
        // Set up to accumulate again.
371
0
        fRecorder.beginRecording(SkRect::Make(fDst->fDimensions));
372
0
    }
373
374
    SkPictureRecorder fRecorder; // accumulates draws until we draw an offscreen into this layer.
375
    LayerCmds*        fDst                            = nullptr;
376
    SkIRect           fClipRect                       = SkIRect::MakeEmpty();
377
    int               fNextDrawPictureToLayerID       = -1;
378
    SkIRect           fNextDrawPictureToLayerClipRect = SkIRect::MakeEmpty();
379
    int               fNextDrawImageFromLayerID       = -1;
380
    LayerMap*         fOffscreenLayers                = nullptr;
381
};
382
383
///////////////////////////////////////////////////////////////////////////////
384
385
0
std::unique_ptr<MSKPPlayer> MSKPPlayer::Make(SkStreamSeekable* stream) {
386
0
    auto deserialContext = std::make_unique<SkSharingDeserialContext>();
387
0
    SkDeserialProcs procs;
388
0
    procs.fImageProc = SkSharingDeserialContext::deserializeImage;
389
0
    procs.fImageCtx = deserialContext.get();
390
391
0
    int pageCount = SkMultiPictureDocument::ReadPageCount(stream);
392
0
    if (!pageCount) {
393
0
        return nullptr;
394
0
    }
395
0
    std::vector<SkDocumentPage> pages(pageCount);
396
0
    if (!SkMultiPictureDocument::Read(stream, pages.data(), pageCount, &procs)) {
397
0
        return nullptr;
398
0
    }
399
0
    std::unique_ptr<MSKPPlayer> result(new MSKPPlayer);
400
0
    result->fRootLayers.reserve(pages.size());
401
0
    for (const auto& page : pages) {
402
0
        SkISize dims = {SkScalarCeilToInt(page.fSize.width()),
403
0
                        SkScalarCeilToInt(page.fSize.height())};
404
0
        result->fRootLayers.emplace_back();
405
0
        result->fRootLayers.back().fDimensions = dims;
406
0
        result->fMaxDimensions.fWidth  = std::max(dims.width() , result->fMaxDimensions.width() );
407
0
        result->fMaxDimensions.fHeight = std::max(dims.height(), result->fMaxDimensions.height());
408
0
        CmdRecordCanvas sc(&result->fRootLayers.back(), &result->fOffscreenLayers);
409
0
        page.fPicture->playback(&sc);
410
0
    }
411
0
    return result;
412
0
}
413
414
0
MSKPPlayer::~MSKPPlayer() = default;
415
416
0
SkISize MSKPPlayer::frameDimensions(int i) const {
417
0
    if (i < 0 || i >= this->numFrames()) {
418
0
        return {-1, -1};
419
0
    }
420
0
    return fRootLayers[i].fDimensions;
421
0
}
422
423
0
bool MSKPPlayer::playFrame(SkCanvas* canvas, int i) {
424
0
    if (i < 0 || i >= this->numFrames()) {
425
0
        return false;
426
0
    }
427
428
    // Find the first offscreen layer that has a valid surface. If it's recording context
429
    // differs from the passed canvas's then reset all the layers. Playback will
430
    // automatically allocate new surfaces for offscreen layers as they're encountered.
431
0
    for (const auto& ols : fOffscreenLayerStates) {
432
0
        const LayerState& state = ols.second;
433
0
        if (state.fSurface) {
434
0
            if (state.fSurface->recordingContext() != canvas->recordingContext()) {
435
0
                this->resetLayers();
436
0
            }
437
0
            break;
438
0
        }
439
0
    }
440
441
    // Replay all the commands for this frame to the caller's canvas.
442
0
    const LayerCmds& layer = fRootLayers[i];
443
0
    for (const auto& cmd : layer.fCmds) {
444
0
        cmd->draw(canvas, fOffscreenLayers, &fOffscreenLayerStates);
445
0
    }
446
0
    return true;
447
0
}
448
449
0
sk_sp<SkSurface> MSKPPlayer::MakeSurfaceForLayer(const LayerCmds& layer, SkCanvas* rootCanvas) {
450
    // Assume layer has same surface props and info as this (mskp doesn't currently record this
451
    // data).
452
0
    SkSurfaceProps props;
453
0
    rootCanvas->getProps(&props);
454
0
    return rootCanvas->makeSurface(rootCanvas->imageInfo().makeDimensions(layer.fDimensions),
455
0
                                   &props);
456
0
}
457
458
0
void MSKPPlayer::resetLayers() { fOffscreenLayerStates.clear(); }
459
460
0
void MSKPPlayer::rewindLayers() {
461
0
    for (auto& [id, state] : fOffscreenLayerStates) {
462
0
        state.fCurrCmd = -1;
463
0
    }
464
0
}
465
466
0
void MSKPPlayer::allocateLayers(SkCanvas* canvas) {
467
    // Iterate over layers not states as states are lazily created in playback but here we want to
468
    // create any that don't already exist.
469
0
    for (auto& [id, layer] : fOffscreenLayers) {
470
0
        LayerState& state = fOffscreenLayerStates[id];
471
0
        if (!state.fSurface || state.fSurface->recordingContext() != canvas->recordingContext()) {
472
0
            state.fCurrCmd = -1;
473
0
            state.fSurface = MakeSurfaceForLayer(fOffscreenLayers[id], canvas);
474
0
        }
475
0
    }
476
0
}
477
478
0
std::vector<int> MSKPPlayer::layerIDs(int frame) const {
479
0
    std::vector<int> result;
480
0
    if (frame < 0) {
481
0
        result.reserve(fOffscreenLayers.size());
482
0
        for (auto& [id, _] : fOffscreenLayers) {
483
0
            result.push_back(id);
484
0
        }
485
0
        return result;
486
0
    }
487
0
    if (frame < static_cast<int>(fRootLayers.size())) {
488
0
        this->collectReferencedLayers(fRootLayers[frame], &result);
489
0
    }
490
0
    return result;
491
0
}
492
493
0
sk_sp<SkImage> MSKPPlayer::layerSnapshot(int layerID) const {
494
0
    auto iter = fOffscreenLayerStates.find(layerID);
495
0
    if (iter == fOffscreenLayerStates.end() || !iter->second.fSurface) {
496
0
        return nullptr;
497
0
    }
498
0
    return iter->second.fSurface->makeImageSnapshot();
499
0
}
500
501
0
void MSKPPlayer::collectReferencedLayers(const LayerCmds& layer, std::vector<int>* out) const {
502
0
    for (const auto& cmd : layer.fCmds) {
503
0
        if (int id = cmd->layerID(); id >= 0) {
504
            // Linear, but we'd need to have a lot of layers to actually care.
505
0
            if (std::find(out->begin(), out->end(), id) == out->end()) {
506
0
                out->push_back(id);
507
0
                auto iter = fOffscreenLayers.find(id);
508
0
                SkASSERT(iter != fOffscreenLayers.end());
509
0
                this->collectReferencedLayers(iter->second, out);
510
0
            }
511
0
        }
512
0
    }
513
0
}
Unexecuted instantiation: MSKPPlayer::collectReferencedLayers(MSKPPlayer::LayerCmds const&, std::__1::vector<int, std::__1::allocator<int> >*) const
Unexecuted instantiation: MSKPPlayer::collectReferencedLayers(MSKPPlayer::LayerCmds const&, std::__1::vector<int, std::__1::allocator<int> >*) const