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 | #include "src/gpu/ops/GrFillRRectOp.h" |
9 | |
10 | #include "include/private/GrRecordingContext.h" |
11 | #include "src/core/SkRRectPriv.h" |
12 | #include "src/gpu/GrCaps.h" |
13 | #include "src/gpu/GrMemoryPool.h" |
14 | #include "src/gpu/GrOpFlushState.h" |
15 | #include "src/gpu/GrOpsRenderPass.h" |
16 | #include "src/gpu/GrProgramInfo.h" |
17 | #include "src/gpu/GrRecordingContextPriv.h" |
18 | #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" |
19 | #include "src/gpu/glsl/GrGLSLGeometryProcessor.h" |
20 | #include "src/gpu/glsl/GrGLSLVarying.h" |
21 | #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" |
22 | #include "src/gpu/ops/GrMeshDrawOp.h" |
23 | #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h" |
24 | |
25 | namespace { |
26 | |
27 | class FillRRectOp : public GrMeshDrawOp { |
28 | private: |
29 | using Helper = GrSimpleMeshDrawOpHelper; |
30 | |
31 | public: |
32 | DEFINE_OP_CLASS_ID |
33 | |
34 | static std::unique_ptr<GrDrawOp> Make(GrRecordingContext*, |
35 | GrPaint&&, |
36 | const SkMatrix& viewMatrix, |
37 | const SkRRect&, |
38 | GrAAType); |
39 | |
40 | const char* name() const final { return "GrFillRRectOp" ; } |
41 | |
42 | FixedFunctionFlags fixedFunctionFlags() const final { return fHelper.fixedFunctionFlags(); } |
43 | |
44 | GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, |
45 | bool hasMixedSampledCoverage, GrClampType) final; |
46 | CombineResult onCombineIfPossible(GrOp*, GrRecordingContext::Arenas*, const GrCaps&) final; |
47 | |
48 | void visitProxies(const VisitProxyFunc& fn) const override { |
49 | if (fProgramInfo) { |
50 | fProgramInfo->visitFPProxies(fn); |
51 | } else { |
52 | fHelper.visitProxies(fn); |
53 | } |
54 | } |
55 | |
56 | void onPrepareDraws(Target*) final; |
57 | |
58 | void onExecute(GrOpFlushState*, const SkRect& chainBounds) final; |
59 | |
60 | private: |
61 | friend class ::GrSimpleMeshDrawOpHelper; // for access to ctor |
62 | friend class ::GrOpMemoryPool; // for access to ctor |
63 | |
64 | enum class ProcessorFlags { |
65 | kNone = 0, |
66 | kUseHWDerivatives = 1 << 0, |
67 | kHasPerspective = 1 << 1, |
68 | kHasLocalCoords = 1 << 2, |
69 | kWideColor = 1 << 3 |
70 | }; |
71 | |
72 | GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(ProcessorFlags); |
73 | |
74 | class Processor; |
75 | |
76 | FillRRectOp(const Helper::MakeArgs&, |
77 | const SkPMColor4f& paintColor, |
78 | const SkMatrix& totalShapeMatrix, |
79 | const SkRRect&, |
80 | GrAAType, |
81 | ProcessorFlags, |
82 | const SkRect& devBounds); |
83 | |
84 | // These methods are used to append data of various POD types to our internal array of instance |
85 | // data. The actual layout of the instance buffer can vary from Op to Op. |
86 | template <typename T> inline T* appendInstanceData(int count) { |
87 | static_assert(std::is_pod<T>::value, "" ); |
88 | static_assert(4 == alignof(T), "" ); |
89 | return reinterpret_cast<T*>(fInstanceData.push_back_n(sizeof(T) * count)); |
90 | } |
91 | |
92 | template <typename T, typename... Args> |
93 | inline void writeInstanceData(const T& val, const Args&... remainder) { |
94 | memcpy(this->appendInstanceData<T>(1), &val, sizeof(T)); |
95 | this->writeInstanceData(remainder...); |
96 | } |
97 | |
98 | void writeInstanceData() {} // Halt condition. |
99 | |
100 | GrProgramInfo* programInfo() final { return fProgramInfo; } |
101 | |
102 | // Create a GrProgramInfo object in the provided arena |
103 | void onCreateProgramInfo(const GrCaps*, |
104 | SkArenaAlloc*, |
105 | const GrSurfaceProxyView* writeView, |
106 | GrAppliedClip&&, |
107 | const GrXferProcessor::DstProxyView&) final; |
108 | |
109 | Helper fHelper; |
110 | SkPMColor4f fColor; |
111 | const SkRect fLocalRect; |
112 | ProcessorFlags fProcessorFlags; |
113 | |
114 | SkSTArray<sizeof(float) * 16 * 4, char, /*MEM_MOVE=*/ true> fInstanceData; |
115 | int fInstanceCount = 1; |
116 | int fInstanceStride = 0; |
117 | |
118 | sk_sp<const GrBuffer> fInstanceBuffer; |
119 | sk_sp<const GrBuffer> fVertexBuffer; |
120 | sk_sp<const GrBuffer> fIndexBuffer; |
121 | int fBaseInstance = 0; |
122 | int fIndexCount = 0; |
123 | |
124 | // If this op is prePrepared the created programInfo will be stored here for use in |
125 | // onExecute. In the prePrepared case it will have been stored in the record-time arena. |
126 | GrProgramInfo* fProgramInfo = nullptr; |
127 | |
128 | typedef GrMeshDrawOp INHERITED; |
129 | }; |
130 | |
131 | GR_MAKE_BITFIELD_CLASS_OPS(FillRRectOp::ProcessorFlags) |
132 | |
133 | // Hardware derivatives are not always accurate enough for highly elliptical corners. This method |
134 | // checks to make sure the corners will still all look good if we use HW derivatives. |
135 | static bool can_use_hw_derivatives_with_coverage(const GrShaderCaps&, |
136 | const SkMatrix&, |
137 | const SkRRect&); |
138 | |
139 | std::unique_ptr<GrDrawOp> FillRRectOp::Make(GrRecordingContext* ctx, |
140 | GrPaint&& paint, |
141 | const SkMatrix& viewMatrix, |
142 | const SkRRect& rrect, |
143 | GrAAType aaType) { |
144 | using Helper = GrSimpleMeshDrawOpHelper; |
145 | |
146 | const GrCaps* caps = ctx->priv().caps(); |
147 | |
148 | if (!caps->drawInstancedSupport()) { |
149 | return nullptr; |
150 | } |
151 | |
152 | ProcessorFlags flags = ProcessorFlags::kNone; |
153 | if (GrAAType::kCoverage == aaType) { |
154 | // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we |
155 | // already use HW derivatives. The only trick will be adjusting the AA outset to account for |
156 | // perspective. (i.e., outset = 0.5 * z.) |
157 | if (viewMatrix.hasPerspective()) { |
158 | return nullptr; |
159 | } |
160 | if (can_use_hw_derivatives_with_coverage(*caps->shaderCaps(), viewMatrix, rrect)) { |
161 | // HW derivatives (more specifically, fwidth()) are consistently faster on all platforms |
162 | // in coverage mode. We use them as long as the approximation will be accurate enough. |
163 | flags |= ProcessorFlags::kUseHWDerivatives; |
164 | } |
165 | } else { |
166 | if (GrAAType::kMSAA == aaType) { |
167 | if (!caps->sampleLocationsSupport() || !caps->shaderCaps()->sampleMaskSupport() || |
168 | caps->shaderCaps()->canOnlyUseSampleMaskWithStencil()) { |
169 | return nullptr; |
170 | } |
171 | } |
172 | if (viewMatrix.hasPerspective()) { |
173 | // HW derivatives are consistently slower on all platforms in sample mask mode. We |
174 | // therefore only use them when there is perspective, since then we can't interpolate |
175 | // the symbolic screen-space gradient. |
176 | flags |= ProcessorFlags::kUseHWDerivatives | ProcessorFlags::kHasPerspective; |
177 | } |
178 | } |
179 | |
180 | // Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space. |
181 | float l = rrect.rect().left(), r = rrect.rect().right(), |
182 | t = rrect.rect().top(), b = rrect.rect().bottom(); |
183 | SkMatrix m; |
184 | // Unmap the normalized rect [-1, -1, +1, +1] back to [l, t, r, b]. |
185 | m.setScaleTranslate((r - l)/2, (b - t)/2, (l + r)/2, (t + b)/2); |
186 | // Map to device space. |
187 | m.postConcat(viewMatrix); |
188 | |
189 | SkRect devBounds; |
190 | if (!(flags & ProcessorFlags::kHasPerspective)) { |
191 | // Since m is an affine matrix that maps the rect [-1, -1, +1, +1] into the shape's |
192 | // device-space quad, it's quite simple to find the bounding rectangle: |
193 | devBounds = SkRect::MakeXYWH(m.getTranslateX(), m.getTranslateY(), 0, 0); |
194 | devBounds.outset(SkScalarAbs(m.getScaleX()) + SkScalarAbs(m.getSkewX()), |
195 | SkScalarAbs(m.getSkewY()) + SkScalarAbs(m.getScaleY())); |
196 | } else { |
197 | viewMatrix.mapRect(&devBounds, rrect.rect()); |
198 | } |
199 | |
200 | if (GrAAType::kMSAA == aaType && caps->preferTrianglesOverSampleMask()) { |
201 | // We are on a platform that prefers fine triangles instead of using the sample mask. See if |
202 | // the round rect is large enough that it will be faster for us to send it off to the |
203 | // default path renderer instead. The 200x200 threshold was arrived at using the |
204 | // "shapes_rrect" benchmark on an ARM Galaxy S9. |
205 | if (devBounds.height() * devBounds.width() > 200 * 200) { |
206 | return nullptr; |
207 | } |
208 | } |
209 | |
210 | return Helper::FactoryHelper<FillRRectOp>(ctx, std::move(paint), m, rrect, aaType, |
211 | flags, devBounds); |
212 | } |
213 | |
214 | FillRRectOp::FillRRectOp(const GrSimpleMeshDrawOpHelper::MakeArgs& helperArgs, |
215 | const SkPMColor4f& paintColor, |
216 | const SkMatrix& totalShapeMatrix, |
217 | const SkRRect& rrect, |
218 | GrAAType aaType, |
219 | ProcessorFlags processorFlags, |
220 | const SkRect& devBounds) |
221 | : INHERITED(ClassID()) |
222 | , fHelper(helperArgs, aaType) |
223 | , fColor(paintColor) |
224 | , fLocalRect(rrect.rect()) |
225 | , fProcessorFlags(processorFlags & ~(ProcessorFlags::kHasLocalCoords | |
226 | ProcessorFlags::kWideColor)) { |
227 | SkASSERT((fProcessorFlags & ProcessorFlags::kHasPerspective) == |
228 | totalShapeMatrix.hasPerspective()); |
229 | this->setBounds(devBounds, GrOp::HasAABloat::kYes, GrOp::IsHairline::kNo); |
230 | |
231 | // Write the matrix attribs. |
232 | const SkMatrix& m = totalShapeMatrix; |
233 | if (!(fProcessorFlags & ProcessorFlags::kHasPerspective)) { |
234 | // Affine 2D transformation (float2x2 plus float2 translate). |
235 | SkASSERT(!m.hasPerspective()); |
236 | this->writeInstanceData(m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY()); |
237 | this->writeInstanceData(m.getTranslateX(), m.getTranslateY()); |
238 | } else { |
239 | // Perspective float3x3 transformation matrix. |
240 | SkASSERT(m.hasPerspective()); |
241 | m.get9(this->appendInstanceData<float>(9)); |
242 | } |
243 | |
244 | // Convert the radii to [-1, -1, +1, +1] space and write their attribs. |
245 | Sk4f radiiX, radiiY; |
246 | Sk4f::Load2(SkRRectPriv::GetRadiiArray(rrect), &radiiX, &radiiY); |
247 | (radiiX * (2/rrect.width())).store(this->appendInstanceData<float>(4)); |
248 | (radiiY * (2/rrect.height())).store(this->appendInstanceData<float>(4)); |
249 | |
250 | // We will write the color and local rect attribs during finalize(). |
251 | } |
252 | |
253 | GrProcessorSet::Analysis FillRRectOp::finalize( |
254 | const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage, |
255 | GrClampType clampType) { |
256 | SkASSERT(1 == fInstanceCount); |
257 | |
258 | bool isWideColor; |
259 | auto analysis = fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType, |
260 | GrProcessorAnalysisCoverage::kSingleChannel, |
261 | &fColor, &isWideColor); |
262 | |
263 | // Finish writing the instance attribs. |
264 | if (isWideColor) { |
265 | fProcessorFlags |= ProcessorFlags::kWideColor; |
266 | this->writeInstanceData(fColor); |
267 | } else { |
268 | this->writeInstanceData(fColor.toBytes_RGBA()); |
269 | } |
270 | |
271 | if (analysis.usesLocalCoords()) { |
272 | fProcessorFlags |= ProcessorFlags::kHasLocalCoords; |
273 | this->writeInstanceData(fLocalRect); |
274 | } |
275 | fInstanceStride = fInstanceData.count(); |
276 | |
277 | return analysis; |
278 | } |
279 | |
280 | GrDrawOp::CombineResult FillRRectOp::onCombineIfPossible(GrOp* op, |
281 | GrRecordingContext::Arenas*, |
282 | const GrCaps& caps) { |
283 | const auto& that = *op->cast<FillRRectOp>(); |
284 | if (!fHelper.isCompatible(that.fHelper, caps, this->bounds(), that.bounds())) { |
285 | return CombineResult::kCannotCombine; |
286 | } |
287 | |
288 | if (fProcessorFlags != that.fProcessorFlags || |
289 | fInstanceData.count() > std::numeric_limits<int>::max() - that.fInstanceData.count()) { |
290 | return CombineResult::kCannotCombine; |
291 | } |
292 | |
293 | fInstanceData.push_back_n(that.fInstanceData.count(), that.fInstanceData.begin()); |
294 | fInstanceCount += that.fInstanceCount; |
295 | SkASSERT(fInstanceStride == that.fInstanceStride); |
296 | return CombineResult::kMerged; |
297 | } |
298 | |
299 | class FillRRectOp::Processor : public GrGeometryProcessor { |
300 | public: |
301 | static GrGeometryProcessor* Make(SkArenaAlloc* arena, GrAAType aaType, ProcessorFlags flags) { |
302 | return arena->make<Processor>(aaType, flags); |
303 | } |
304 | |
305 | const char* name() const final { return "GrFillRRectOp::Processor" ; } |
306 | |
307 | void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const final { |
308 | b->add32(((uint32_t)fFlags << 16) | (uint32_t)fAAType); |
309 | } |
310 | |
311 | GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final; |
312 | |
313 | private: |
314 | friend class ::SkArenaAlloc; // for access to ctor |
315 | |
316 | Processor(GrAAType aaType, ProcessorFlags flags) |
317 | : INHERITED(kGrFillRRectOp_Processor_ClassID) |
318 | , fAAType(aaType) |
319 | , fFlags(flags) { |
320 | int numVertexAttribs = (GrAAType::kCoverage == fAAType) ? 3 : 2; |
321 | this->setVertexAttributes(kVertexAttribs, numVertexAttribs); |
322 | |
323 | if (!(fFlags & ProcessorFlags::kHasPerspective)) { |
324 | // Affine 2D transformation (float2x2 plus float2 translate). |
325 | fInstanceAttribs.emplace_back("skew" , kFloat4_GrVertexAttribType, kFloat4_GrSLType); |
326 | fInstanceAttribs.emplace_back( |
327 | "translate" , kFloat2_GrVertexAttribType, kFloat2_GrSLType); |
328 | } else { |
329 | // Perspective float3x3 transformation matrix. |
330 | fInstanceAttribs.emplace_back("persp_x" , kFloat3_GrVertexAttribType, kFloat3_GrSLType); |
331 | fInstanceAttribs.emplace_back("persp_y" , kFloat3_GrVertexAttribType, kFloat3_GrSLType); |
332 | fInstanceAttribs.emplace_back("persp_z" , kFloat3_GrVertexAttribType, kFloat3_GrSLType); |
333 | } |
334 | fInstanceAttribs.emplace_back("radii_x" , kFloat4_GrVertexAttribType, kFloat4_GrSLType); |
335 | fInstanceAttribs.emplace_back("radii_y" , kFloat4_GrVertexAttribType, kFloat4_GrSLType); |
336 | fColorAttrib = &fInstanceAttribs.push_back( |
337 | MakeColorAttribute("color" , (fFlags & ProcessorFlags::kWideColor))); |
338 | if (fFlags & ProcessorFlags::kHasLocalCoords) { |
339 | fInstanceAttribs.emplace_back( |
340 | "local_rect" , kFloat4_GrVertexAttribType, kFloat4_GrSLType); |
341 | } |
342 | this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count()); |
343 | |
344 | if (GrAAType::kMSAA == fAAType) { |
345 | this->setWillUseCustomFeature(CustomFeatures::kSampleLocations); |
346 | } |
347 | } |
348 | |
349 | static constexpr Attribute kVertexAttribs[] = { |
350 | {"radii_selector" , kFloat4_GrVertexAttribType, kFloat4_GrSLType}, |
351 | {"corner_and_radius_outsets" , kFloat4_GrVertexAttribType, kFloat4_GrSLType}, |
352 | // Coverage only. |
353 | {"aa_bloat_and_coverage" , kFloat4_GrVertexAttribType, kFloat4_GrSLType}}; |
354 | |
355 | const GrAAType fAAType; |
356 | const ProcessorFlags fFlags; |
357 | |
358 | SkSTArray<6, Attribute> fInstanceAttribs; |
359 | const Attribute* fColorAttrib; |
360 | |
361 | class CoverageImpl; |
362 | class MSAAImpl; |
363 | |
364 | typedef GrGeometryProcessor INHERITED; |
365 | }; |
366 | |
367 | constexpr GrPrimitiveProcessor::Attribute FillRRectOp::Processor::kVertexAttribs[]; |
368 | |
369 | // Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear |
370 | // coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal |
371 | // edges. The Vertex struct tells the shader where to place its vertex within a normalized |
372 | // ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode. |
373 | struct CoverageVertex { |
374 | std::array<float, 4> fRadiiSelector; |
375 | std::array<float, 2> fCorner; |
376 | std::array<float, 2> fRadiusOutset; |
377 | std::array<float, 2> fAABloatDirection; |
378 | float fCoverage; |
379 | float fIsLinearCoverage; |
380 | }; |
381 | |
382 | // This is the offset (when multiplied by radii) from the corners of a bounding box to the vertices |
383 | // of its inscribed octagon. We draw the outside portion of arcs with quarter-octagons rather than |
384 | // rectangles. |
385 | static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2); |
386 | |
387 | static constexpr CoverageVertex kCoverageVertexData[] = { |
388 | // Left inset edge. |
389 | {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1}, |
390 | {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1}, |
391 | |
392 | // Top inset edge. |
393 | {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,+1}}, 1, 1}, |
394 | {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,+1}}, 1, 1}, |
395 | |
396 | // Right inset edge. |
397 | {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{-1,0}}, 1, 1}, |
398 | {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{-1,0}}, 1, 1}, |
399 | |
400 | // Bottom inset edge. |
401 | {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,-1}}, 1, 1}, |
402 | {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,-1}}, 1, 1}, |
403 | |
404 | |
405 | // Left outset edge. |
406 | {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{-1,0}}, 0, 1}, |
407 | {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{-1,0}}, 0, 1}, |
408 | |
409 | // Top outset edge. |
410 | {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}, {{0,-1}}, 0, 1}, |
411 | {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}, {{0,-1}}, 0, 1}, |
412 | |
413 | // Right outset edge. |
414 | {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}, {{+1,0}}, 0, 1}, |
415 | {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}, {{+1,0}}, 0, 1}, |
416 | |
417 | // Bottom outset edge. |
418 | {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}, {{0,+1}}, 0, 1}, |
419 | {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}, {{0,+1}}, 0, 1}, |
420 | |
421 | |
422 | // Top-left corner. |
423 | {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{-1, 0}}, 0, 0}, |
424 | {{{1,0,0,0}}, {{-1,-1}}, {{ 0,+1}}, {{+1, 0}}, 1, 0}, |
425 | {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,+1}}, 1, 0}, |
426 | {{{1,0,0,0}}, {{-1,-1}}, {{+1, 0}}, {{ 0,-1}}, 0, 0}, |
427 | {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}, {{-1,-1}}, 0, 0}, |
428 | {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}, {{-1,-1}}, 0, 0}, |
429 | |
430 | // Top-right corner. |
431 | {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,-1}}, 0, 0}, |
432 | {{{0,1,0,0}}, {{+1,-1}}, {{-1, 0}}, {{ 0,+1}}, 1, 0}, |
433 | {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{-1, 0}}, 1, 0}, |
434 | {{{0,1,0,0}}, {{+1,-1}}, {{ 0,+1}}, {{+1, 0}}, 0, 0}, |
435 | {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}, {{+1,-1}}, 0, 0}, |
436 | {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}, {{+1,-1}}, 0, 0}, |
437 | |
438 | // Bottom-right corner. |
439 | {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{+1, 0}}, 0, 0}, |
440 | {{{0,0,1,0}}, {{+1,+1}}, {{ 0,-1}}, {{-1, 0}}, 1, 0}, |
441 | {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,-1}}, 1, 0}, |
442 | {{{0,0,1,0}}, {{+1,+1}}, {{-1, 0}}, {{ 0,+1}}, 0, 0}, |
443 | {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}, {{+1,+1}}, 0, 0}, |
444 | {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}, {{+1,+1}}, 0, 0}, |
445 | |
446 | // Bottom-left corner. |
447 | {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,+1}}, 0, 0}, |
448 | {{{0,0,0,1}}, {{-1,+1}}, {{+1, 0}}, {{ 0,-1}}, 1, 0}, |
449 | {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{+1, 0}}, 1, 0}, |
450 | {{{0,0,0,1}}, {{-1,+1}}, {{ 0,-1}}, {{-1, 0}}, 0, 0}, |
451 | {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0}, |
452 | {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}}; |
453 | |
454 | GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey); |
455 | |
456 | static constexpr uint16_t kCoverageIndexData[] = { |
457 | // Inset octagon (solid coverage). |
458 | 0, 1, 7, |
459 | 1, 2, 7, |
460 | 7, 2, 6, |
461 | 2, 3, 6, |
462 | 6, 3, 5, |
463 | 3, 4, 5, |
464 | |
465 | // AA borders (linear coverage). |
466 | 0, 1, 8, 1, 9, 8, |
467 | 2, 3, 10, 3, 11, 10, |
468 | 4, 5, 12, 5, 13, 12, |
469 | 6, 7, 14, 7, 15, 14, |
470 | |
471 | // Top-left arc. |
472 | 16, 17, 21, |
473 | 17, 21, 18, |
474 | 21, 18, 20, |
475 | 18, 20, 19, |
476 | |
477 | // Top-right arc. |
478 | 22, 23, 27, |
479 | 23, 27, 24, |
480 | 27, 24, 26, |
481 | 24, 26, 25, |
482 | |
483 | // Bottom-right arc. |
484 | 28, 29, 33, |
485 | 29, 33, 30, |
486 | 33, 30, 32, |
487 | 30, 32, 31, |
488 | |
489 | // Bottom-left arc. |
490 | 34, 35, 39, |
491 | 35, 39, 36, |
492 | 39, 36, 38, |
493 | 36, 38, 37}; |
494 | |
495 | GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey); |
496 | |
497 | |
498 | // Our MSAA geometry consists of an inset octagon with full sample mask coverage, circumscribed |
499 | // by a larger octagon that modifies the sample mask for the arc at each corresponding corner. |
500 | struct MSAAVertex { |
501 | std::array<float, 4> fRadiiSelector; |
502 | std::array<float, 2> fCorner; |
503 | std::array<float, 2> fRadiusOutset; |
504 | }; |
505 | |
506 | static constexpr MSAAVertex kMSAAVertexData[] = { |
507 | // Left edge. (Negative radii selector indicates this is not an arc section.) |
508 | {{{0,0,0,-1}}, {{-1,+1}}, {{0,-1}}}, |
509 | {{{-1,0,0,0}}, {{-1,-1}}, {{0,+1}}}, |
510 | |
511 | // Top edge. |
512 | {{{-1,0,0,0}}, {{-1,-1}}, {{+1,0}}}, |
513 | {{{0,-1,0,0}}, {{+1,-1}}, {{-1,0}}}, |
514 | |
515 | // Right edge. |
516 | {{{0,-1,0,0}}, {{+1,-1}}, {{0,+1}}}, |
517 | {{{0,0,-1,0}}, {{+1,+1}}, {{0,-1}}}, |
518 | |
519 | // Bottom edge. |
520 | {{{0,0,-1,0}}, {{+1,+1}}, {{-1,0}}}, |
521 | {{{0,0,0,-1}}, {{-1,+1}}, {{+1,0}}}, |
522 | |
523 | // Top-left corner. |
524 | {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}}, |
525 | {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}}, |
526 | {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}}, |
527 | {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}}, |
528 | |
529 | // Top-right corner. |
530 | {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}}, |
531 | {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}}, |
532 | {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}}, |
533 | {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}}, |
534 | |
535 | // Bottom-right corner. |
536 | {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}}, |
537 | {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}}, |
538 | {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}}, |
539 | {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}}, |
540 | |
541 | // Bottom-left corner. |
542 | {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}}, |
543 | {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}}, |
544 | {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}}, |
545 | {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}}}; |
546 | |
547 | GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey); |
548 | |
549 | static constexpr uint16_t kMSAAIndexData[] = { |
550 | // Inset octagon. (Full sample mask.) |
551 | 0, 1, 2, |
552 | 0, 2, 3, |
553 | 0, 3, 6, |
554 | 3, 4, 5, |
555 | 3, 5, 6, |
556 | 6, 7, 0, |
557 | |
558 | // Top-left arc. (Sample mask is set to the arc.) |
559 | 8, 9, 10, |
560 | 9, 11, 10, |
561 | |
562 | // Top-right arc. |
563 | 12, 13, 14, |
564 | 13, 15, 14, |
565 | |
566 | // Bottom-right arc. |
567 | 16, 17, 18, |
568 | 17, 19, 18, |
569 | |
570 | // Bottom-left arc. |
571 | 20, 21, 22, |
572 | 21, 23, 22}; |
573 | |
574 | GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey); |
575 | |
576 | void FillRRectOp::onPrepareDraws(Target* target) { |
577 | if (void* instanceData = target->makeVertexSpace(fInstanceStride, fInstanceCount, |
578 | &fInstanceBuffer, &fBaseInstance)) { |
579 | SkASSERT(fInstanceStride * fInstanceCount == fInstanceData.count()); |
580 | memcpy(instanceData, fInstanceData.begin(), fInstanceData.count()); |
581 | } |
582 | |
583 | if (GrAAType::kCoverage == fHelper.aaType()) { |
584 | GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey); |
585 | |
586 | fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer( |
587 | GrGpuBufferType::kIndex, sizeof(kCoverageIndexData), kCoverageIndexData, |
588 | gCoverageIndexBufferKey); |
589 | |
590 | GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey); |
591 | |
592 | fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer( |
593 | GrGpuBufferType::kVertex, sizeof(kCoverageVertexData), kCoverageVertexData, |
594 | gCoverageVertexBufferKey); |
595 | |
596 | fIndexCount = SK_ARRAY_COUNT(kCoverageIndexData); |
597 | } else { |
598 | GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey); |
599 | |
600 | fIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer( |
601 | GrGpuBufferType::kIndex, sizeof(kMSAAIndexData), kMSAAIndexData, |
602 | gMSAAIndexBufferKey); |
603 | |
604 | GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey); |
605 | |
606 | fVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer( |
607 | GrGpuBufferType::kVertex, sizeof(kMSAAVertexData), kMSAAVertexData, |
608 | gMSAAVertexBufferKey); |
609 | |
610 | fIndexCount = SK_ARRAY_COUNT(kMSAAIndexData); |
611 | } |
612 | } |
613 | |
614 | class FillRRectOp::Processor::CoverageImpl : public GrGLSLGeometryProcessor { |
615 | void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { |
616 | const auto& proc = args.fGP.cast<Processor>(); |
617 | bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives); |
618 | |
619 | SkASSERT(proc.vertexStride() == sizeof(CoverageVertex)); |
620 | |
621 | GrGLSLVaryingHandler* varyings = args.fVaryingHandler; |
622 | varyings->emitAttributes(proc); |
623 | varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor, |
624 | GrGLSLVaryingHandler::Interpolation::kCanBeFlat); |
625 | |
626 | // Emit the vertex shader. |
627 | GrGLSLVertexBuilder* v = args.fVertBuilder; |
628 | |
629 | // Unpack vertex attribs. |
630 | v->codeAppend("float2 corner = corner_and_radius_outsets.xy;" ); |
631 | v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;" ); |
632 | v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;" ); |
633 | v->codeAppend("float coverage = aa_bloat_and_coverage.z;" ); |
634 | v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;" ); |
635 | |
636 | // Find the amount to bloat each edge for AA (in source space). |
637 | v->codeAppend("float2 pixellength = inversesqrt(" |
638 | "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));" ); |
639 | v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;" ); |
640 | v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + " |
641 | "abs(normalized_axis_dirs.zw));" ); |
642 | v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;" ); |
643 | |
644 | // Identify our radii. |
645 | v->codeAppend("float4 radii_and_neighbors = radii_selector" |
646 | "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);" ); |
647 | v->codeAppend("float2 radii = radii_and_neighbors.xy;" ); |
648 | v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;" ); |
649 | |
650 | v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {" ); |
651 | // The rrect is more narrow than an AA coverage ramp. We can't draw as-is |
652 | // or else opposite AA borders will overlap. Instead, fudge the size up to |
653 | // the width of a coverage ramp, and then reduce total coverage to make |
654 | // the rect appear more thin. |
655 | v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);" ); |
656 | v->codeAppend( "coverage /= max(aa_bloatradius.x, 1) * max(aa_bloatradius.y, 1);" ); |
657 | // Set radii to zero to ensure we take the "linear coverage" codepath. |
658 | // (The "coverage" variable only has effect in the linear codepath.) |
659 | v->codeAppend( "radii = float2(0);" ); |
660 | v->codeAppend("}" ); |
661 | |
662 | v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.25))) {" ); |
663 | // The radii are very small. Demote this arc to a sharp 90 degree corner. |
664 | v->codeAppend( "radii = aa_bloatradius;" ); |
665 | // Snap octagon vertices to the corner of the bounding box. |
666 | v->codeAppend( "radius_outset = floor(abs(radius_outset)) * radius_outset;" ); |
667 | v->codeAppend( "is_linear_coverage = 1;" ); |
668 | v->codeAppend("} else {" ); |
669 | // Don't let radii get smaller than a pixel. |
670 | v->codeAppend( "radii = clamp(radii, pixellength, 2 - pixellength);" ); |
671 | v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength, 2 - pixellength);" ); |
672 | // Don't let neighboring radii get closer together than 1/16 pixel. |
673 | v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;" ); |
674 | v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));" ); |
675 | v->codeAppend( "radii -= extra_pad * .5;" ); |
676 | v->codeAppend("}" ); |
677 | |
678 | // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in |
679 | // normalized [-1,-1,+1,+1] space. |
680 | v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;" ); |
681 | v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;" ); |
682 | |
683 | // Emit transforms. |
684 | GrShaderVar localCoord("" , kFloat2_GrSLType); |
685 | if (proc.fFlags & ProcessorFlags::kHasLocalCoords) { |
686 | v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + " |
687 | "local_rect.zw * (1 + vertexpos)) * .5;" ); |
688 | localCoord.set(kFloat2_GrSLType, "localcoord" ); |
689 | } |
690 | this->emitTransforms(v, varyings, args.fUniformHandler, localCoord, |
691 | args.fFPCoordTransformHandler); |
692 | |
693 | // Transform to device space. |
694 | SkASSERT(!(proc.fFlags & ProcessorFlags::kHasPerspective)); |
695 | v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);" ); |
696 | v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;" ); |
697 | gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord" ); |
698 | |
699 | // Setup interpolants for coverage. |
700 | GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType); |
701 | varyings->addVarying("arccoord" , &arcCoord); |
702 | v->codeAppend("if (0 != is_linear_coverage) {" ); |
703 | // We are a non-corner piece: Set x=0 to indicate built-in coverage, and |
704 | // interpolate linear coverage across y. |
705 | v->codeAppendf( "%s.xy = float2(0, coverage);" , arcCoord.vsOut()); |
706 | v->codeAppend("} else {" ); |
707 | // Find the normalized arc coordinates for our corner ellipse. |
708 | // (i.e., the coordinate system where x^2 + y^2 == 1). |
709 | v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;" ); |
710 | // We are a corner piece: Interpolate the arc coordinates for coverage. |
711 | // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0 |
712 | // instructs the fragment shader to use linear coverage). |
713 | v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);" , arcCoord.vsOut()); |
714 | if (!useHWDerivatives) { |
715 | // The gradient is order-1: Interpolate it across arccoord.zw. |
716 | v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);" ); |
717 | v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);" , arcCoord.vsOut()); |
718 | } |
719 | v->codeAppend("}" ); |
720 | |
721 | // Emit the fragment shader. |
722 | GrGLSLFPFragmentBuilder* f = args.fFragBuilder; |
723 | |
724 | f->codeAppendf("float x_plus_1=%s.x, y=%s.y;" , arcCoord.fsIn(), arcCoord.fsIn()); |
725 | f->codeAppendf("half coverage;" ); |
726 | f->codeAppendf("if (0 == x_plus_1) {" ); |
727 | f->codeAppendf( "coverage = half(y);" ); // We are a non-arc pixel (linear coverage). |
728 | f->codeAppendf("} else {" ); |
729 | f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);" ); // fn = (x+1)*(x-1) = x^2-1 |
730 | f->codeAppendf( "fn = fma(y,y, fn);" ); // fn = x^2 + y^2 - 1 |
731 | if (useHWDerivatives) { |
732 | f->codeAppendf("float fnwidth = fwidth(fn);" ); |
733 | } else { |
734 | // The gradient is interpolated across arccoord.zw. |
735 | f->codeAppendf("float gx=%s.z, gy=%s.w;" , arcCoord.fsIn(), arcCoord.fsIn()); |
736 | f->codeAppendf("float fnwidth = abs(gx) + abs(gy);" ); |
737 | } |
738 | f->codeAppendf( "half d = half(fn/fnwidth);" ); |
739 | f->codeAppendf( "coverage = clamp(.5 - d, 0, 1);" ); |
740 | f->codeAppendf("}" ); |
741 | f->codeAppendf("%s = half4(coverage);" , args.fOutputCoverage); |
742 | } |
743 | |
744 | void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&, |
745 | const CoordTransformRange& transformRange) override { |
746 | this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange); |
747 | } |
748 | }; |
749 | |
750 | |
751 | class FillRRectOp::Processor::MSAAImpl : public GrGLSLGeometryProcessor { |
752 | void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { |
753 | const auto& proc = args.fGP.cast<Processor>(); |
754 | bool useHWDerivatives = (proc.fFlags & ProcessorFlags::kUseHWDerivatives); |
755 | bool hasPerspective = (proc.fFlags & ProcessorFlags::kHasPerspective); |
756 | bool hasLocalCoords = (proc.fFlags & ProcessorFlags::kHasLocalCoords); |
757 | SkASSERT(useHWDerivatives == hasPerspective); |
758 | |
759 | SkASSERT(proc.vertexStride() == sizeof(MSAAVertex)); |
760 | |
761 | // Emit the vertex shader. |
762 | GrGLSLVertexBuilder* v = args.fVertBuilder; |
763 | |
764 | GrGLSLVaryingHandler* varyings = args.fVaryingHandler; |
765 | varyings->emitAttributes(proc); |
766 | varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor, |
767 | GrGLSLVaryingHandler::Interpolation::kCanBeFlat); |
768 | |
769 | // Unpack vertex attribs. |
770 | v->codeAppendf("float2 corner = corner_and_radius_outsets.xy;" ); |
771 | v->codeAppendf("float2 radius_outset = corner_and_radius_outsets.zw;" ); |
772 | |
773 | // Identify our radii. |
774 | v->codeAppend("float2 radii;" ); |
775 | v->codeAppend("radii.x = dot(radii_selector, radii_x);" ); |
776 | v->codeAppend("radii.y = dot(radii_selector, radii_y);" ); |
777 | v->codeAppendf("bool is_arc_section = (radii.x > 0);" ); |
778 | v->codeAppendf("radii = abs(radii);" ); |
779 | |
780 | // Find our vertex position, adjusted for radii. Our rect is drawn in normalized |
781 | // [-1,-1,+1,+1] space. |
782 | v->codeAppend("float2 vertexpos = corner + radius_outset * radii;" ); |
783 | |
784 | // Emit transforms. |
785 | GrShaderVar localCoord("" , kFloat2_GrSLType); |
786 | if (hasLocalCoords) { |
787 | v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + " |
788 | "local_rect.zw * (1 + vertexpos)) * .5;" ); |
789 | localCoord.set(kFloat2_GrSLType, "localcoord" ); |
790 | } |
791 | this->emitTransforms(v, varyings, args.fUniformHandler, localCoord, |
792 | args.fFPCoordTransformHandler); |
793 | |
794 | // Transform to device space. |
795 | if (!hasPerspective) { |
796 | v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);" ); |
797 | v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;" ); |
798 | gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord" ); |
799 | } else { |
800 | v->codeAppend("float3x3 persp_matrix = float3x3(persp_x, persp_y, persp_z);" ); |
801 | v->codeAppend("float3 devcoord = float3(vertexpos, 1) * persp_matrix;" ); |
802 | gpArgs->fPositionVar.set(kFloat3_GrSLType, "devcoord" ); |
803 | } |
804 | |
805 | // Determine normalized arc coordinates for the implicit function. |
806 | GrGLSLVarying arcCoord((useHWDerivatives) ? kFloat2_GrSLType : kFloat4_GrSLType); |
807 | varyings->addVarying("arccoord" , &arcCoord); |
808 | v->codeAppendf("if (is_arc_section) {" ); |
809 | v->codeAppendf( "%s.xy = 1 - abs(radius_outset);" , arcCoord.vsOut()); |
810 | if (!useHWDerivatives) { |
811 | // The gradient is order-1: Interpolate it across arccoord.zw. |
812 | // This doesn't work with perspective. |
813 | SkASSERT(!hasPerspective); |
814 | v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);" ); |
815 | v->codeAppendf("%s.zw = derivatives * (%s.xy/radii * corner * 2);" , |
816 | arcCoord.vsOut(), arcCoord.vsOut()); |
817 | } |
818 | v->codeAppendf("} else {" ); |
819 | if (useHWDerivatives) { |
820 | v->codeAppendf("%s = float2(0);" , arcCoord.vsOut()); |
821 | } else { |
822 | v->codeAppendf("%s = float4(0);" , arcCoord.vsOut()); |
823 | } |
824 | v->codeAppendf("}" ); |
825 | |
826 | // Emit the fragment shader. |
827 | GrGLSLFPFragmentBuilder* f = args.fFragBuilder; |
828 | |
829 | f->codeAppendf("%s = half4(1);" , args.fOutputCoverage); |
830 | |
831 | // If x,y == 0, then we are drawing a triangle that does not track an arc. |
832 | f->codeAppendf("if (float2(0) != %s.xy) {" , arcCoord.fsIn()); |
833 | f->codeAppendf( "float fn = dot(%s.xy, %s.xy) - 1;" , arcCoord.fsIn(), arcCoord.fsIn()); |
834 | if (GrAAType::kMSAA == proc.fAAType) { |
835 | using ScopeFlags = GrGLSLFPFragmentBuilder::ScopeFlags; |
836 | if (!useHWDerivatives) { |
837 | f->codeAppendf("float2 grad = %s.zw;" , arcCoord.fsIn()); |
838 | f->applyFnToMultisampleMask("fn" , "grad" , ScopeFlags::kInsidePerPrimitiveBranch); |
839 | } else { |
840 | f->applyFnToMultisampleMask("fn" , nullptr, ScopeFlags::kInsidePerPrimitiveBranch); |
841 | } |
842 | } else { |
843 | f->codeAppendf("if (fn > 0) {" ); |
844 | f->codeAppendf( "%s = half4(0);" , args.fOutputCoverage); |
845 | f->codeAppendf("}" ); |
846 | } |
847 | f->codeAppendf("}" ); |
848 | } |
849 | |
850 | void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&, |
851 | const CoordTransformRange& transformRange) override { |
852 | this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange); |
853 | } |
854 | }; |
855 | |
856 | GrGLSLPrimitiveProcessor* FillRRectOp::Processor::createGLSLInstance( |
857 | const GrShaderCaps&) const { |
858 | if (GrAAType::kCoverage != fAAType) { |
859 | return new MSAAImpl(); |
860 | } |
861 | return new CoverageImpl(); |
862 | } |
863 | |
864 | void FillRRectOp::onCreateProgramInfo(const GrCaps* caps, |
865 | SkArenaAlloc* arena, |
866 | const GrSurfaceProxyView* writeView, |
867 | GrAppliedClip&& appliedClip, |
868 | const GrXferProcessor::DstProxyView& dstProxyView) { |
869 | GrGeometryProcessor* gp = Processor::Make(arena, fHelper.aaType(), fProcessorFlags); |
870 | SkASSERT(gp->instanceStride() == (size_t)fInstanceStride); |
871 | |
872 | fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip), |
873 | dstProxyView, gp, GrPrimitiveType::kTriangles); |
874 | } |
875 | |
876 | void FillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) { |
877 | if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) { |
878 | return; // Setup failed. |
879 | } |
880 | |
881 | if (!fProgramInfo) { |
882 | this->createProgramInfo(flushState); |
883 | } |
884 | |
885 | flushState->bindPipelineAndScissorClip(*fProgramInfo, this->bounds()); |
886 | flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline()); |
887 | flushState->bindBuffers(fIndexBuffer.get(), fInstanceBuffer.get(), fVertexBuffer.get()); |
888 | flushState->drawIndexedInstanced(fIndexCount, 0, fInstanceCount, fBaseInstance, 0); |
889 | } |
890 | |
891 | // Will the given corner look good if we use HW derivatives? |
892 | static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) { |
893 | Sk2f devRadii = devScale * cornerRadii; |
894 | if (devRadii[1] < devRadii[0]) { |
895 | devRadii = SkNx_shuffle<1,0>(devRadii); |
896 | } |
897 | float minDevRadius = std::max(devRadii[0], 1.f); // Shader clamps radius at a minimum of 1. |
898 | // Is the gradient smooth enough for this corner look ok if we use hardware derivatives? |
899 | // This threshold was arrived at subjevtively on an NVIDIA chip. |
900 | return minDevRadius * minDevRadius * 5 > devRadii[1]; |
901 | } |
902 | |
903 | static bool can_use_hw_derivatives_with_coverage( |
904 | const Sk2f& devScale, const SkVector& cornerRadii) { |
905 | return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii)); |
906 | } |
907 | |
908 | // Will the given round rect look good if we use HW derivatives? |
909 | static bool can_use_hw_derivatives_with_coverage( |
910 | const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) { |
911 | if (!shaderCaps.shaderDerivativeSupport()) { |
912 | return false; |
913 | } |
914 | |
915 | Sk2f x = Sk2f(viewMatrix.getScaleX(), viewMatrix.getSkewX()); |
916 | Sk2f y = Sk2f(viewMatrix.getSkewY(), viewMatrix.getScaleY()); |
917 | Sk2f devScale = (x*x + y*y).sqrt(); |
918 | switch (rrect.getType()) { |
919 | case SkRRect::kEmpty_Type: |
920 | case SkRRect::kRect_Type: |
921 | return true; |
922 | |
923 | case SkRRect::kOval_Type: |
924 | case SkRRect::kSimple_Type: |
925 | return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii()); |
926 | |
927 | case SkRRect::kNinePatch_Type: { |
928 | Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect)); |
929 | Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2); |
930 | Sk2f minRadii = Sk2f::Min(r0, r1); |
931 | Sk2f maxRadii = Sk2f::Max(r0, r1); |
932 | return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) && |
933 | can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1])); |
934 | } |
935 | |
936 | case SkRRect::kComplex_Type: { |
937 | for (int i = 0; i < 4; ++i) { |
938 | auto corner = static_cast<SkRRect::Corner>(i); |
939 | if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) { |
940 | return false; |
941 | } |
942 | } |
943 | return true; |
944 | } |
945 | } |
946 | SK_ABORT("Invalid round rect type." ); |
947 | } |
948 | |
949 | } // anonymous namespace |
950 | |
951 | |
952 | std::unique_ptr<GrDrawOp> GrFillRRectOp::Make(GrRecordingContext* ctx, |
953 | GrPaint&& paint, |
954 | const SkMatrix& viewMatrix, |
955 | const SkRRect& rrect, |
956 | GrAAType aaType) { |
957 | return FillRRectOp::Make(ctx, std::move(paint), viewMatrix, rrect, aaType); |
958 | } |
959 | |
960 | #if GR_TEST_UTILS |
961 | |
962 | #include "src/gpu/GrDrawOpTest.h" |
963 | |
964 | GR_DRAW_OP_TEST_DEFINE(FillRRectOp) { |
965 | SkMatrix viewMatrix = GrTest::TestMatrix(random); |
966 | GrAAType aaType = GrAAType::kNone; |
967 | if (random->nextBool()) { |
968 | aaType = (numSamples > 1) ? GrAAType::kMSAA : GrAAType::kCoverage; |
969 | } |
970 | |
971 | SkRect rect = GrTest::TestRect(random); |
972 | float w = rect.width(); |
973 | float h = rect.height(); |
974 | |
975 | SkRRect rrect; |
976 | // TODO: test out other rrect configurations |
977 | rrect.setNinePatch(rect, w / 3.0f, h / 4.0f, w / 5.0f, h / 6.0); |
978 | |
979 | return GrFillRRectOp::Make(context, |
980 | std::move(paint), |
981 | viewMatrix, |
982 | rrect, |
983 | aaType); |
984 | } |
985 | |
986 | #endif |
987 | |