1/*
2 * Copyright 2016 Google Inc.
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#ifndef GrReducedClip_DEFINED
9#define GrReducedClip_DEFINED
10
11#include "src/core/SkClipStack.h"
12#include "src/core/SkTLList.h"
13#include "src/gpu/GrFragmentProcessor.h"
14#include "src/gpu/GrWindowRectangles.h"
15
16class GrCoverageCountingPathRenderer;
17class GrRecordingContext;
18class GrRenderTargetContext;
19
20/**
21 * This class takes a clip stack and produces a reduced set of elements that are equivalent to
22 * applying that full stack within a specified query rectangle.
23 */
24class GrReducedClip {
25public:
26 using Element = SkClipStack::Element;
27 using ElementList = SkTLList<SkClipStack::Element, 16>;
28
29 GrReducedClip(const SkClipStack&, const SkRect& queryBounds, const GrCaps* caps,
30 int maxWindowRectangles = 0, int maxAnalyticFPs = 0, int maxCCPRClipPaths = 0);
31
32 enum class InitialState : bool {
33 kAllIn,
34 kAllOut
35 };
36
37 InitialState initialState() const { return fInitialState; }
38
39 /**
40 * If hasScissor() is true, the clip mask is not valid outside this rect and the caller must
41 * enforce this scissor during draw.
42 */
43 const SkIRect& scissor() const { SkASSERT(fHasScissor); return fScissor; }
44 int left() const { return this->scissor().left(); }
45 int top() const { return this->scissor().top(); }
46 int width() const { return this->scissor().width(); }
47 int height() const { return this->scissor().height(); }
48
49 /**
50 * Indicates whether scissor() is defined. It will always be defined if the maskElements() are
51 * nonempty.
52 */
53 bool hasScissor() const { return fHasScissor; }
54
55 /**
56 * If nonempty, the clip mask is not valid inside these windows and the caller must clip them
57 * out using the window rectangles GPU extension.
58 */
59 const GrWindowRectangles& windowRectangles() const { return fWindowRects; }
60
61 /**
62 * An ordered list of clip elements that could not be skipped or implemented by other means. If
63 * nonempty, the caller must create an alpha and/or stencil mask for these elements and apply it
64 * during draw.
65 */
66 const ElementList& maskElements() const { return fMaskElements; }
67
68 /**
69 * If maskElements() are nonempty, uniquely identifies the region of the clip mask that falls
70 * inside of scissor().
71 *
72 * NOTE: since clip elements might fall outside the query bounds, different regions of the same
73 * clip stack might have more or less restrictive IDs.
74 *
75 * FIXME: this prevents us from reusing a sub-rect of a perfectly good mask when that rect has
76 * been assigned a less restrictive ID.
77 */
78 uint32_t maskGenID() const { SkASSERT(!fMaskElements.isEmpty()); return fMaskGenID; }
79
80 /**
81 * Indicates whether antialiasing is required to process any of the mask elements.
82 */
83 bool maskRequiresAA() const { SkASSERT(!fMaskElements.isEmpty()); return fMaskRequiresAA; }
84
85 bool drawAlphaClipMask(GrRenderTargetContext*) const;
86 bool drawStencilClipMask(GrRecordingContext*, GrRenderTargetContext*) const;
87
88 int numAnalyticFPs() const { return fAnalyticFPs.count() + fCCPRClipPaths.count(); }
89
90 /**
91 * Called once the client knows the ID of the opsTask that the clip FPs will operate in. This
92 * method finishes any outstanding work that was waiting for the opsTask ID, then detaches and
93 * returns this class's list of FPs that complete the clip.
94 *
95 * NOTE: this must be called AFTER producing the clip mask (if any) because draw calls on
96 * the render target context, surface allocations, and even switching render targets (pre MDB)
97 * may cause flushes or otherwise change which opsTask the actual draw is going into.
98 */
99 std::unique_ptr<GrFragmentProcessor> finishAndDetachAnalyticFPs(
100 GrCoverageCountingPathRenderer*, uint32_t opsTaskID);
101
102private:
103 void walkStack(const SkClipStack&, const SkRect& queryBounds);
104
105 enum class ClipResult {
106 kNotClipped,
107 kClipped,
108 kMadeEmpty
109 };
110
111 // Intersects the clip with the element's interior, regardless of inverse fill type.
112 // NOTE: do not call for elements followed by ops that can grow the clip.
113 ClipResult clipInsideElement(const Element*);
114
115 // Intersects the clip with the element's exterior, regardless of inverse fill type.
116 // NOTE: do not call for elements followed by ops that can grow the clip.
117 ClipResult clipOutsideElement(const Element*);
118
119 void addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA);
120
121 enum class Invert : bool {
122 kNo = false,
123 kYes = true
124 };
125
126 static GrClipEdgeType GetClipEdgeType(Invert, GrAA);
127 ClipResult addAnalyticFP(const SkRect& deviceSpaceRect, Invert, GrAA);
128 ClipResult addAnalyticFP(const SkRRect& deviceSpaceRRect, Invert, GrAA);
129 ClipResult addAnalyticFP(const SkPath& deviceSpacePath, Invert, GrAA);
130
131 void makeEmpty();
132
133 const GrCaps* fCaps;
134 const int fMaxWindowRectangles;
135 const int fMaxAnalyticFPs;
136 const int fMaxCCPRClipPaths;
137
138 InitialState fInitialState;
139 SkIRect fScissor;
140 bool fHasScissor;
141 SkRect fAAClipRect;
142 uint32_t fAAClipRectGenID; // GenID the mask will have if includes the AA clip rect.
143 GrWindowRectangles fWindowRects;
144 ElementList fMaskElements;
145 uint32_t fMaskGenID;
146 bool fMaskRequiresAA;
147 SkSTArray<4, std::unique_ptr<GrFragmentProcessor>> fAnalyticFPs;
148 SkSTArray<4, SkPath> fCCPRClipPaths; // Will convert to FPs once we have an opsTask ID for CCPR.
149};
150
151#endif
152