aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/ccpr/GrCCPRCoverageOp.h
blob: e3eee488993ab48d7103d6b542682080c011c905 (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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef GrCCPRCoverageOp_DEFINED
#define GrCCPRCoverageOp_DEFINED

#include "GrMesh.h"
#include "SkRect.h"
#include "SkRefCnt.h"
#include "ccpr/GrCCPRCoverageProcessor.h"
#include "ccpr/GrCCPRGeometry.h"
#include "ops/GrDrawOp.h"

class GrCCPRCoverageOp;
class GrOnFlushResourceProvider;
class SkMatrix;
class SkPath;

/**
 * This class produces GrCCPRCoverageOps that render coverage count masks and atlases. A path is
 * added to the current op in two steps:
 *
 *   1) parsePath(ScissorMode, viewMatrix, path, &devBounds, &devBounds45);
 *
 *   <client decides where to put the mask within an atlas, if wanted>
 *
 *   2) saveParsedPath(offsetX, offsetY, clipBounds);
 *
 * The client can flush the currently saved paths to a GrCCPRCoverageOp by calling emitOp, and
 * retrieve all emitted ops after calling finalize().
 */
class GrCCPRCoverageOpsBuilder {
public:
    // Indicates whether a path should enforce a scissor clip when rendering its mask. (Specified
    // as an int because these values get used directly as indices into arrays.)
    enum class ScissorMode : int {
        kNonScissored = 0,
        kScissored = 1
    };
    static constexpr int kNumScissorModes = 2;

    GrCCPRCoverageOpsBuilder(int maxTotalPaths, int maxPathPoints, int numSkPoints, int numSkVerbs)
            : fPathsInfo(maxTotalPaths)
            , fLocalDevPtsBuffer(maxPathPoints + 1) // Overallocate by one point to accomodate for
                                                    // overflow with Sk4f. (See parsePath.)
            , fGeometry(numSkPoints, numSkVerbs)
            , fTallies{PrimitiveTallies(), PrimitiveTallies()}
            , fScissorBatches(maxTotalPaths) {}

    ~GrCCPRCoverageOpsBuilder() {
        // Enforce the contract that the client always calls saveParsedPath or discardParsedPath.
        SkASSERT(!fParsingPath);
    }

    // Parses an SkPath into a temporary staging area. The path will not yet be included in the next
    // Op unless there is a matching call to saveParsedPath. The user must complement this with a
    // following call to either saveParsedPath or discardParsedPath.
    //
    // Returns two tight bounding boxes: device space and "45 degree" (| 1 -1 | * devCoords) space.
    //                                                                 | 1  1 |
    void parsePath(const SkMatrix&, const SkPath&, SkRect* devBounds, SkRect* devBounds45);

    // Parses a device-space SkPath into a temporary staging area. The path will not yet be included
    // in the next Op unless there is a matching call to saveParsedPath. The user must complement
    // this with a following call to either saveParsedPath or discardParsedPath.
    void parseDeviceSpacePath(const SkPath&);

    // Commits the currently-parsed path from staging to the next Op, and specifies whether the mask
    // should be rendered with a scissor clip in effect. Accepts an optional post-device-space
    // translate for placement in an atlas.
    void saveParsedPath(ScissorMode, const SkIRect& clippedDevIBounds,
                        int16_t atlasOffsetX, int16_t atlasOffsetY);
    void discardParsedPath();

    // Flushes all currently-saved paths internally to a GrCCPRCoverageOp.
    //
    // NOTE: if there is a parsed path in the staging area, it will not be included. But the client
    // may still call saveParsedPath to include it in a future Op.
    void emitOp(SkISize drawBounds);

    // Builds GPU buffers and returns the list of GrCCPRCoverageOps as specified by calls to emitOp.
    bool finalize(GrOnFlushResourceProvider*, SkTArray<std::unique_ptr<GrCCPRCoverageOp>>*);

private:
    using PrimitiveTallies = GrCCPRGeometry::PrimitiveTallies;

    // Every kBeginPath verb has a corresponding PathInfo entry.
    struct PathInfo {
        ScissorMode fScissorMode;
        int16_t fAtlasOffsetX, fAtlasOffsetY;
        std::unique_ptr<GrCCPRCoverageOp> fTerminatingOp;
    };

    // Every PathInfo with a mode of kScissored has a corresponding ScissorBatch.
    struct ScissorBatch {
        PrimitiveTallies fInstanceCounts;
        SkIRect fScissor;
    };

    void parsePath(const SkPath&, const SkPoint* deviceSpacePts);
    void endContourIfNeeded(bool insideContour);

    // Staging area for the path being parsed.
    SkDEBUGCODE(int fParsingPath = false);
    int fCurrPathPointsIdx;
    int fCurrPathVerbsIdx;
    PrimitiveTallies fCurrPathTallies;

    SkSTArray<32, PathInfo, true> fPathsInfo;

    const SkAutoSTArray<32, SkPoint> fLocalDevPtsBuffer;
    GrCCPRGeometry fGeometry;

    PrimitiveTallies fTallies[kNumScissorModes];
    SkTArray<ScissorBatch, true> fScissorBatches;

    std::unique_ptr<GrCCPRCoverageOp>  fTerminatingOp;

    friend class GrCCPRCoverageOp; // For ScissorBatch.
};

/**
 * This Op renders coverage count masks and atlases. Create it using GrCCPRCoverageOpsBuilder.
 */
class GrCCPRCoverageOp : public GrDrawOp {
public:
    DEFINE_OP_CLASS_ID

    // GrDrawOp interface.
    const char* name() const override { return "GrCCPRCoverageOp"; }
    FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
    RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*,
                                GrPixelConfigIsClamped) override {
        return RequiresDstTexture::kNo;
    }
    bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; }
    void onPrepare(GrOpFlushState*) override {}
    void onExecute(GrOpFlushState*) override;

private:
    static constexpr int kNumScissorModes = GrCCPRCoverageOpsBuilder::kNumScissorModes;
    using PrimitiveTallies = GrCCPRGeometry::PrimitiveTallies;
    using ScissorBatch = GrCCPRCoverageOpsBuilder::ScissorBatch;

    GrCCPRCoverageOp(SkTArray<ScissorBatch, true>&& scissorBatches, const SkISize& drawBounds)
        : INHERITED(ClassID())
        , fScissorBatches(std::move(scissorBatches))
        , fDrawBounds(drawBounds) {
        this->setBounds(SkRect::MakeIWH(fDrawBounds.width(), fDrawBounds.height()),
                        GrOp::HasAABloat::kNo, GrOp::IsZeroArea::kNo);
    }

    void setInstanceBuffer(sk_sp<GrBuffer> instanceBuffer,
                           const PrimitiveTallies baseInstances[kNumScissorModes],
                           const PrimitiveTallies endInstances[kNumScissorModes]);

    void drawMaskPrimitives(GrOpFlushState*, const GrPipeline&, GrCCPRCoverageProcessor::RenderPass,
                            int PrimitiveTallies::* instanceType) const;

    sk_sp<GrBuffer> fInstanceBuffer;
    PrimitiveTallies fBaseInstances[kNumScissorModes];
    PrimitiveTallies fInstanceCounts[kNumScissorModes];
    const SkTArray<ScissorBatch, true> fScissorBatches;
    const SkISize fDrawBounds;

    mutable SkTArray<GrMesh> fMeshesScratchBuffer;
    mutable SkTArray<GrPipeline::DynamicState, true> fDynamicStatesScratchBuffer;

    friend class GrCCPRCoverageOpsBuilder;

    typedef GrDrawOp INHERITED;
};

#endif