1/*
2 * Copyright 2012 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 "include/private/GrRecordingContext.h"
9#include "src/gpu/GrCaps.h"
10#include "src/gpu/GrFixedClip.h"
11#include "src/gpu/GrGpu.h"
12#include "src/gpu/GrPath.h"
13#include "src/gpu/GrRenderTargetContextPriv.h"
14#include "src/gpu/GrResourceProvider.h"
15#include "src/gpu/GrStencilClip.h"
16#include "src/gpu/GrStyle.h"
17#include "src/gpu/geometry/GrShape.h"
18#include "src/gpu/ops/GrDrawPathOp.h"
19#include "src/gpu/ops/GrStencilAndCoverPathRenderer.h"
20#include "src/gpu/ops/GrStencilPathOp.h"
21
22GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrResourceProvider* resourceProvider,
23 const GrCaps& caps) {
24 if (caps.shaderCaps()->pathRenderingSupport() && !caps.avoidStencilBuffers()) {
25 return new GrStencilAndCoverPathRenderer(resourceProvider);
26 } else {
27 return nullptr;
28 }
29}
30
31GrStencilAndCoverPathRenderer::GrStencilAndCoverPathRenderer(GrResourceProvider* resourceProvider)
32 : fResourceProvider(resourceProvider) {
33}
34
35GrPathRenderer::CanDrawPath
36GrStencilAndCoverPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
37 SkASSERT(!args.fTargetIsWrappedVkSecondaryCB);
38 // GrPath doesn't support hairline paths. An arbitrary path effect could produce a hairline
39 // path.
40 if (args.fShape->style().strokeRec().isHairlineStyle() ||
41 args.fShape->style().hasNonDashPathEffect() ||
42 args.fHasUserStencilSettings) {
43 return CanDrawPath::kNo;
44 }
45 if (GrAAType::kCoverage == args.fAAType && !args.fProxy->canUseMixedSamples(*args.fCaps)) {
46 // We rely on a mixed sampled stencil buffer to implement coverage AA.
47 return CanDrawPath::kNo;
48 }
49 return CanDrawPath::kYes;
50}
51
52static sk_sp<GrPath> get_gr_path(GrResourceProvider* resourceProvider, const GrShape& shape) {
53 GrUniqueKey key;
54 bool isVolatile;
55 GrPath::ComputeKey(shape, &key, &isVolatile);
56 sk_sp<GrPath> path;
57 if (!isVolatile) {
58 path = resourceProvider->findByUniqueKey<GrPath>(key);
59 }
60 if (!path) {
61 SkPath skPath;
62 shape.asPath(&skPath);
63 path = resourceProvider->createPath(skPath, shape.style());
64 if (!isVolatile) {
65 resourceProvider->assignUniqueKeyToResource(key, path.get());
66 }
67 } else {
68#ifdef SK_DEBUG
69 SkPath skPath;
70 shape.asPath(&skPath);
71 SkASSERT(path->isEqualTo(skPath, shape.style()));
72#endif
73 }
74 return path;
75}
76
77void GrStencilAndCoverPathRenderer::onStencilPath(const StencilPathArgs& args) {
78 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
79 "GrStencilAndCoverPathRenderer::onStencilPath");
80 sk_sp<GrPath> p(get_gr_path(fResourceProvider, *args.fShape));
81 args.fRenderTargetContext->priv().stencilPath(
82 *args.fClip, args.fDoStencilMSAA, *args.fViewMatrix, std::move(p));
83}
84
85bool GrStencilAndCoverPathRenderer::onDrawPath(const DrawPathArgs& args) {
86 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
87 "GrStencilAndCoverPathRenderer::onDrawPath");
88 SkASSERT(!args.fShape->style().strokeRec().isHairlineStyle());
89
90 const SkMatrix& viewMatrix = *args.fViewMatrix;
91
92 bool doStencilMSAA = GrAAType::kNone != args.fAAType;
93
94 sk_sp<GrPath> path(get_gr_path(fResourceProvider, *args.fShape));
95
96 if (args.fShape->inverseFilled()) {
97 SkMatrix vmi;
98 if (!viewMatrix.invert(&vmi)) {
99 return true;
100 }
101
102 SkRect devBounds = SkRect::MakeIWH(args.fRenderTargetContext->width(),
103 args.fRenderTargetContext->height()); // Inverse fill.
104
105 // fake inverse with a stencil and cover
106 GrAppliedClip appliedClip;
107 if (!args.fClip->apply(
108 args.fContext, args.fRenderTargetContext, doStencilMSAA, true, &appliedClip,
109 &devBounds)) {
110 return true;
111 }
112 GrStencilClip stencilClip(appliedClip.stencilStackID());
113 if (appliedClip.scissorState().enabled()) {
114 stencilClip.fixedClip().setScissor(appliedClip.scissorState().rect());
115 }
116 if (appliedClip.windowRectsState().enabled()) {
117 stencilClip.fixedClip().setWindowRectangles(appliedClip.windowRectsState().windows(),
118 appliedClip.windowRectsState().mode());
119 }
120 // Just ignore the analytic FPs (if any) during the stencil pass. They will still clip the
121 // final draw and it is meaningless to multiply by coverage when drawing to stencil.
122 args.fRenderTargetContext->priv().stencilPath(
123 stencilClip, GrAA(doStencilMSAA), viewMatrix, std::move(path));
124
125 {
126 static constexpr GrUserStencilSettings kInvertedCoverPass(
127 GrUserStencilSettings::StaticInit<
128 0x0000,
129 // We know our rect will hit pixels outside the clip and the user bits will
130 // be 0 outside the clip. So we can't just fill where the user bits are 0. We
131 // also need to check that the clip bit is set.
132 GrUserStencilTest::kEqualIfInClip,
133 0xffff,
134 GrUserStencilOp::kKeep,
135 GrUserStencilOp::kZero,
136 0xffff>()
137 );
138
139 SkRect coverBounds;
140 // mapRect through persp matrix may not be correct
141 if (!viewMatrix.hasPerspective()) {
142 vmi.mapRect(&coverBounds, devBounds);
143 // theoretically could set bloat = 0, instead leave it because of matrix inversion
144 // precision.
145 SkScalar bloat = viewMatrix.getMaxScale() * SK_ScalarHalf;
146 coverBounds.outset(bloat, bloat);
147 } else {
148 coverBounds = devBounds;
149 }
150 const SkMatrix& coverMatrix = !viewMatrix.hasPerspective() ? viewMatrix : SkMatrix::I();
151 const SkMatrix& localMatrix = !viewMatrix.hasPerspective() ? SkMatrix::I() : vmi;
152
153 // We have to suppress enabling MSAA for mixed samples or we will get seams due to
154 // coverage modulation along the edge where two triangles making up the rect meet.
155 GrAA doStencilMSAA = GrAA::kNo;
156 if (GrAAType::kMSAA == args.fAAType) {
157 doStencilMSAA = GrAA::kYes;
158 }
159 args.fRenderTargetContext->priv().stencilRect(
160 *args.fClip, &kInvertedCoverPass, std::move(args.fPaint), doStencilMSAA,
161 coverMatrix, coverBounds, &localMatrix);
162 }
163 } else {
164 std::unique_ptr<GrDrawOp> op = GrDrawPathOp::Make(
165 args.fContext, viewMatrix, std::move(args.fPaint), GrAA(doStencilMSAA),
166 std::move(path));
167 args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
168 }
169
170 return true;
171}
172