aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/DDLTileHelper.cpp
blob: 2403b43fc30433b38942c229a789e0bdbdce4e69 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "DDLTileHelper.h"

#include "DDLPromiseImageHelper.h"
#include "SkCanvas.h"
#include "SkDeferredDisplayListRecorder.h"
#include "SkImage_Gpu.h"
#include "SkPicture.h"
#include "SkSurface.h"
#include "SkSurfaceCharacterization.h"
#include "SkTaskGroup.h"

DDLTileHelper::TileData::TileData(sk_sp<SkSurface> s, const SkIRect& clip)
        : fSurface(std::move(s))
        , fClip(clip) {
    SkAssertResult(fSurface->characterize(&fCharacterization));
}

void DDLTileHelper::TileData::createTileSpecificSKP(SkData* compressedPictureData,
                                                    const DDLPromiseImageHelper& helper) {
    SkASSERT(!fReconstitutedPicture);

    // This is bending the DDLRecorder contract! The promise images in the SKP should be
    // created by the same recorder used to create the matching DDL.
    SkDeferredDisplayListRecorder recorder(fCharacterization);

    fReconstitutedPicture = helper.reinflateSKP(&recorder, compressedPictureData, &fPromiseImages);
}

void DDLTileHelper::TileData::createDDL() {
    SkASSERT(fReconstitutedPicture);
    SkASSERT(!fDisplayList);

    SkDeferredDisplayListRecorder recorder(fCharacterization);

    // DDL TODO: the DDLRecorder's GrContext isn't initialized until getCanvas is called.
    // Maybe set it up in the ctor?
    SkCanvas* subCanvas = recorder.getCanvas();

    // Because we cheated in createTileSpecificSKP and used the wrong DDLRecorder, the GrContext's
    // stored in fReconstitutedPicture's promise images are incorrect. Patch them with the correct
    // one now.
    for (int i = 0; i < fPromiseImages.count(); ++i) {
        GrContext* newContext = subCanvas->getGrContext();

        if (fPromiseImages[i]->isTextureBacked()) {
            SkImage_Gpu* gpuImage = (SkImage_Gpu*) fPromiseImages[i].get();
            gpuImage->resetContext(sk_ref_sp(newContext));
        }
    }

    subCanvas->clipRect(SkRect::MakeWH(fClip.width(), fClip.height()));
    subCanvas->translate(-fClip.fLeft, -fClip.fTop);

    // Note: in this use case we only render a picture to the deferred canvas
    // but, more generally, clients will use arbitrary draw calls.
    subCanvas->drawPicture(fReconstitutedPicture);

    fDisplayList = recorder.detach();
}

void DDLTileHelper::TileData::draw() {
    SkASSERT(fDisplayList);

    fSurface->draw(fDisplayList.get());
}

void DDLTileHelper::TileData::compose(SkCanvas* dst) {
    sk_sp<SkImage> img = fSurface->makeImageSnapshot();
    dst->save();
    dst->clipRect(SkRect::Make(fClip));
    dst->drawImage(std::move(img), fClip.fLeft, fClip.fTop);
    dst->restore();
}

void DDLTileHelper::TileData::reset() {
    // TODO: when DDLs are re-renderable we don't need to do this
    fDisplayList = nullptr;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

DDLTileHelper::DDLTileHelper(SkCanvas* canvas, const SkIRect& viewport, int numDivisions)
        : fNumDivisions(numDivisions) {
    SkASSERT(fNumDivisions > 0);
    fTiles.reserve(fNumDivisions*fNumDivisions);

    int xTileSize = viewport.width()/fNumDivisions;
    int yTileSize = viewport.height()/fNumDivisions;

    // Create the destination tiles
    for (int y = 0, yOff = 0; y < fNumDivisions; ++y, yOff += yTileSize) {
        int ySize = (y < fNumDivisions-1) ? yTileSize : viewport.height()-yOff;

        for (int x = 0, xOff = 0; x < fNumDivisions; ++x, xOff += xTileSize) {
            int xSize = (x < fNumDivisions-1) ? xTileSize : viewport.width()-xOff;

            SkIRect clip = SkIRect::MakeXYWH(xOff, yOff, xSize, ySize);

            SkASSERT(viewport.contains(clip));

            SkImageInfo tileII = SkImageInfo::MakeN32Premul(xSize, ySize);

            sk_sp<SkSurface> tileSurface = canvas->makeSurface(tileII);

            // TODO: this is here to deal w/ a resource allocator bug (skbug.com/8007). If all
            // the DDLs are flushed at the same time (w/o the composition draws) the allocator
            // feels free to reuse the backing GrSurfaces!
            tileSurface->flush();

            fTiles.push_back(TileData(std::move(tileSurface), clip));
        }
    }
}

void DDLTileHelper::createSKPPerTile(SkData* compressedPictureData,
                                     const DDLPromiseImageHelper& helper) {
    for (int i = 0; i < fTiles.count(); ++i) {
        fTiles[i].createTileSpecificSKP(compressedPictureData, helper);
    }
}

void DDLTileHelper::createDDLsInParallel() {
#if 1
    SkTaskGroup().batch(fTiles.count(), [&](int i) { fTiles[i].createDDL(); });
    SkTaskGroup().wait();
#else
    // Use this code path to debug w/o threads
    for (int i = 0; i < fTiles.count(); ++i) {
        fTiles[i].createDDL();
    }
#endif

}

void DDLTileHelper::drawAllTilesAndFlush(GrContext* context, bool flush) {
    for (int i = 0; i < fTiles.count(); ++i) {
        fTiles[i].draw();
    }
    if (flush) {
        context->flush();
    }
}

void DDLTileHelper::composeAllTiles(SkCanvas* dstCanvas) {
    for (int i = 0; i < fTiles.count(); ++i) {
        fTiles[i].compose(dstCanvas);
    }
}

void DDLTileHelper::resetAllTiles() {
    for (int i = 0; i < fTiles.count(); ++i) {
        fTiles[i].reset();
    }
}