1 | /* |
2 | * Copyright 2018 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 GrQuadPerEdgeAA_DEFINED |
9 | #define GrQuadPerEdgeAA_DEFINED |
10 | |
11 | #include "include/core/SkPoint.h" |
12 | #include "include/core/SkPoint3.h" |
13 | #include "include/private/GrTypesPriv.h" |
14 | #include "src/gpu/GrColor.h" |
15 | #include "src/gpu/GrGeometryProcessor.h" |
16 | #include "src/gpu/GrSamplerState.h" |
17 | #include "src/gpu/GrVertexWriter.h" |
18 | #include "src/gpu/geometry/GrQuad.h" |
19 | #include "src/gpu/geometry/GrQuadUtils.h" |
20 | #include "src/gpu/ops/GrMeshDrawOp.h" |
21 | #include "src/gpu/ops/GrTextureOp.h" |
22 | |
23 | class GrCaps; |
24 | class GrColorSpaceXform; |
25 | class GrShaderCaps; |
26 | struct GrVertexWriter; |
27 | |
28 | namespace GrQuadPerEdgeAA { |
29 | using Saturate = GrTextureOp::Saturate; |
30 | |
31 | enum class CoverageMode { kNone, kWithPosition, kWithColor }; |
32 | enum class Domain : bool { kNo = false, kYes = true }; |
33 | enum class ColorType { kNone, kByte, kFloat, kLast = kFloat }; |
34 | static const int kColorTypeCount = static_cast<int>(ColorType::kLast) + 1; |
35 | |
36 | enum class IndexBufferOption { |
37 | kPictureFramed, // geometrically AA'd -> 8 verts/quad + an index buffer |
38 | kIndexedRects, // non-AA'd but indexed -> 4 verts/quad + an index buffer |
39 | kTriStrips, // non-AA'd -> 4 verts/quad but no index buffer |
40 | kLast = kTriStrips |
41 | }; |
42 | static const int kIndexBufferOptionCount = static_cast<int>(IndexBufferOption::kLast) + 1; |
43 | |
44 | IndexBufferOption CalcIndexBufferOption(GrAAType aa, int numQuads); |
45 | |
46 | // Gets the minimum ColorType that can represent a color. |
47 | ColorType MinColorType(SkPMColor4f); |
48 | |
49 | // Specifies the vertex configuration for an op that renders per-edge AA quads. The vertex |
50 | // order (when enabled) is device position, color, local position, domain, aa edge equations. |
51 | // This order matches the constructor argument order of VertexSpec and is the order that |
52 | // GPAttributes maintains. If hasLocalCoords is false, then the local quad type can be ignored. |
53 | struct VertexSpec { |
54 | public: |
55 | VertexSpec() |
56 | : fDeviceQuadType(0) // kAxisAligned |
57 | , fLocalQuadType(0) // kAxisAligned |
58 | , fIndexBufferOption(0) // kPictureFramed |
59 | , fHasLocalCoords(false) |
60 | , fColorType(0) // kNone |
61 | , fHasDomain(false) |
62 | , fUsesCoverageAA(false) |
63 | , fCompatibleWithCoverageAsAlpha(false) |
64 | , fRequiresGeometryDomain(false) {} |
65 | |
66 | VertexSpec(GrQuad::Type deviceQuadType, ColorType colorType, GrQuad::Type localQuadType, |
67 | bool hasLocalCoords, Domain domain, GrAAType aa, bool coverageAsAlpha, |
68 | IndexBufferOption indexBufferOption) |
69 | : fDeviceQuadType(static_cast<unsigned>(deviceQuadType)) |
70 | , fLocalQuadType(static_cast<unsigned>(localQuadType)) |
71 | , fIndexBufferOption(static_cast<unsigned>(indexBufferOption)) |
72 | , fHasLocalCoords(hasLocalCoords) |
73 | , fColorType(static_cast<unsigned>(colorType)) |
74 | , fHasDomain(static_cast<unsigned>(domain)) |
75 | , fUsesCoverageAA(aa == GrAAType::kCoverage) |
76 | , fCompatibleWithCoverageAsAlpha(coverageAsAlpha) |
77 | , fRequiresGeometryDomain(aa == GrAAType::kCoverage && |
78 | deviceQuadType > GrQuad::Type::kRectilinear) { } |
79 | |
80 | GrQuad::Type deviceQuadType() const { return static_cast<GrQuad::Type>(fDeviceQuadType); } |
81 | GrQuad::Type localQuadType() const { return static_cast<GrQuad::Type>(fLocalQuadType); } |
82 | IndexBufferOption indexBufferOption() const { |
83 | return static_cast<IndexBufferOption>(fIndexBufferOption); |
84 | } |
85 | bool hasLocalCoords() const { return fHasLocalCoords; } |
86 | ColorType colorType() const { return static_cast<ColorType>(fColorType); } |
87 | bool hasVertexColors() const { return ColorType::kNone != this->colorType(); } |
88 | bool hasDomain() const { return fHasDomain; } |
89 | bool usesCoverageAA() const { return fUsesCoverageAA; } |
90 | bool compatibleWithCoverageAsAlpha() const { return fCompatibleWithCoverageAsAlpha; } |
91 | bool requiresGeometryDomain() const { return fRequiresGeometryDomain; } |
92 | // Will always be 2 or 3 |
93 | int deviceDimensionality() const; |
94 | // Will always be 0 if hasLocalCoords is false, otherwise will be 2 or 3 |
95 | int localDimensionality() const; |
96 | |
97 | int verticesPerQuad() const { return fUsesCoverageAA ? 8 : 4; } |
98 | |
99 | CoverageMode coverageMode() const; |
100 | size_t vertexSize() const; |
101 | |
102 | bool needsIndexBuffer() const { return this->indexBufferOption() != |
103 | IndexBufferOption::kTriStrips; } |
104 | |
105 | GrPrimitiveType primitiveType() const { |
106 | switch (this->indexBufferOption()) { |
107 | case IndexBufferOption::kPictureFramed: return GrPrimitiveType::kTriangles; |
108 | case IndexBufferOption::kIndexedRects: return GrPrimitiveType::kTriangles; |
109 | case IndexBufferOption::kTriStrips: return GrPrimitiveType::kTriangleStrip; |
110 | } |
111 | |
112 | SkUNREACHABLE; |
113 | } |
114 | |
115 | private: |
116 | static_assert(GrQuad::kTypeCount <= 4, "GrQuad::Type doesn't fit in 2 bits" ); |
117 | static_assert(kColorTypeCount <= 4, "Color doesn't fit in 2 bits" ); |
118 | static_assert(kIndexBufferOptionCount <= 4, "IndexBufferOption doesn't fit in 2 bits" ); |
119 | |
120 | unsigned fDeviceQuadType: 2; |
121 | unsigned fLocalQuadType: 2; |
122 | unsigned fIndexBufferOption: 2; |
123 | unsigned fHasLocalCoords: 1; |
124 | unsigned fColorType : 2; |
125 | unsigned fHasDomain: 1; |
126 | unsigned fUsesCoverageAA: 1; |
127 | unsigned fCompatibleWithCoverageAsAlpha: 1; |
128 | // The geometry domain serves to clip off pixels touched by quads with sharp corners that |
129 | // would otherwise exceed the miter limit for the AA-outset geometry. |
130 | unsigned fRequiresGeometryDomain: 1; |
131 | }; |
132 | |
133 | // A Tessellator is responsible for processing a series of device+local GrQuads into a VBO, |
134 | // as specified by a VertexSpec. This vertex data can then be processed by a GP created with |
135 | // MakeProcessor and/or MakeTexturedProcessor. |
136 | class Tessellator { |
137 | public: |
138 | explicit Tessellator(const VertexSpec& spec, char* vertices); |
139 | |
140 | // Calculates (as needed) inset and outset geometry for anti-aliasing, and appends all |
141 | // necessary position and vertex attributes required by this Tessellator's VertexSpec into |
142 | // the 'vertices' the Tessellator was called with. The insetting and outsetting may |
143 | // damage the provided GrQuads (as this is intended to work with GrQuadBuffer::Iter). |
144 | // 'localQuad' can be null if the VertexSpec does not use local coords. |
145 | void append(GrQuad* deviceQuad, GrQuad* localQuad, |
146 | const SkPMColor4f& color, const SkRect& uvDomain, GrQuadAAFlags aaFlags); |
147 | |
148 | SkDEBUGCODE(char* vertices() const { return (char*) fVertexWriter.fPtr; }) |
149 | |
150 | private: |
151 | // VertexSpec defines many unique ways to write vertex attributes, which can be handled |
152 | // generically by branching per-quad based on the VertexSpec. However, there are several |
153 | // specs that appear in the wild far more frequently, so they use explicit WriteQuadProcs |
154 | // that have no branches. |
155 | typedef void (*WriteQuadProc)(GrVertexWriter* vertices, const VertexSpec& spec, |
156 | const GrQuad* deviceQuad, const GrQuad* localQuad, |
157 | const float coverage[4], const SkPMColor4f& color, |
158 | const SkRect& geomDomain, const SkRect& texDomain); |
159 | static WriteQuadProc GetWriteQuadProc(const VertexSpec& spec); |
160 | |
161 | GrQuadUtils::TessellationHelper fAAHelper; |
162 | VertexSpec fVertexSpec; |
163 | GrVertexWriter fVertexWriter; |
164 | WriteQuadProc fWriteProc; |
165 | }; |
166 | |
167 | GrGeometryProcessor* MakeProcessor(SkArenaAlloc*, const VertexSpec&); |
168 | |
169 | GrGeometryProcessor* MakeTexturedProcessor(SkArenaAlloc*, |
170 | const VertexSpec&, |
171 | const GrShaderCaps&, |
172 | const GrBackendFormat&, |
173 | GrSamplerState, |
174 | const GrSwizzle&, |
175 | sk_sp<GrColorSpaceXform> textureColorSpaceXform, |
176 | Saturate); |
177 | |
178 | // This method will return the correct index buffer for the specified indexBufferOption. |
179 | // It will, correctly, return nullptr if the indexBufferOption is kTriStrips. |
180 | sk_sp<const GrBuffer> GetIndexBuffer(GrMeshDrawOp::Target*, IndexBufferOption); |
181 | |
182 | // What is the maximum number of quads allowed for the specified indexBuffer option? |
183 | int QuadLimit(IndexBufferOption); |
184 | |
185 | // This method will issue the draw call on the provided GrOpsRenderPass, as specified by the |
186 | // indexing method in vertexSpec. It is up to the calling code to allocate, fill in, and bind a |
187 | // vertex buffer, and to acquire and bind the correct index buffer (if needed) with |
188 | // GrPrimitiveRestart::kNo. |
189 | // |
190 | // @param runningQuadCount the number of quads already stored in 'vertexBuffer' and |
191 | // 'indexBuffer' e.g., different GrMeshes have already been placed in |
192 | // the buffers to allow dynamic state changes. |
193 | // @param quadCount the number of quads that will be drawn by the provided 'mesh'. |
194 | // A subsequent ConfigureMesh call would the use |
195 | // 'runningQuadCount' + 'quadCount' for its new 'runningQuadCount'. |
196 | void IssueDraw(const GrCaps&, GrOpsRenderPass*, const VertexSpec&, int runningQuadCount, |
197 | int quadCount, int maxVerts, int absVertBufferOffset); |
198 | |
199 | } // namespace GrQuadPerEdgeAA |
200 | |
201 | #endif // GrQuadPerEdgeAA_DEFINED |
202 | |