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 #include "src/gpu/ops/RegionOp.h"
9
10 #include "include/core/SkRegion.h"
11 #include "src/core/SkMatrixPriv.h"
12 #include "src/gpu/BufferWriter.h"
13 #include "src/gpu/GrCaps.h"
14 #include "src/core/SkSafeMath.h"
15 #include "src/gpu/GrDefaultGeoProcFactory.h"
16 #include "src/gpu/GrOpFlushState.h"
17 #include "src/gpu/GrProgramInfo.h"
18 #include "src/gpu/GrResourceProvider.h"
19 #include "src/gpu/ops/GrMeshDrawOp.h"
20 #include "src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h"
21
22 namespace skgpu::v1::RegionOp {
23
24 namespace {
25
make_gp(SkArenaAlloc * arena,const SkMatrix & viewMatrix,bool wideColor)26 GrGeometryProcessor* make_gp(SkArenaAlloc* arena,
27 const SkMatrix& viewMatrix,
28 bool wideColor) {
29 using namespace GrDefaultGeoProcFactory;
30 Color::Type colorType = wideColor ? Color::kPremulWideColorAttribute_Type
31 : Color::kPremulGrColorAttribute_Type;
32 return GrDefaultGeoProcFactory::Make(arena, colorType, Coverage::kSolid_Type,
33 LocalCoords::kUsePosition_Type, viewMatrix);
34 }
35
36 class RegionOpImpl final : public GrMeshDrawOp {
37 private:
38 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
39
40 public:
41 DEFINE_OP_CLASS_ID
42
Make(GrRecordingContext * context,GrPaint && paint,const SkMatrix & viewMatrix,const SkRegion & region,GrAAType aaType,const GrUserStencilSettings * stencilSettings=nullptr)43 static GrOp::Owner Make(GrRecordingContext* context,
44 GrPaint&& paint,
45 const SkMatrix& viewMatrix,
46 const SkRegion& region,
47 GrAAType aaType,
48 const GrUserStencilSettings* stencilSettings = nullptr) {
49 return Helper::FactoryHelper<RegionOpImpl>(context, std::move(paint), viewMatrix, region,
50 aaType, stencilSettings);
51 }
52
RegionOpImpl(GrProcessorSet * processorSet,const SkPMColor4f & color,const SkMatrix & viewMatrix,const SkRegion & region,GrAAType aaType,const GrUserStencilSettings * stencilSettings)53 RegionOpImpl(GrProcessorSet* processorSet, const SkPMColor4f& color,
54 const SkMatrix& viewMatrix, const SkRegion& region, GrAAType aaType,
55 const GrUserStencilSettings* stencilSettings)
56 : INHERITED(ClassID())
57 , fHelper(processorSet, aaType, stencilSettings)
58 , fViewMatrix(viewMatrix) {
59 RegionInfo& info = fRegions.push_back();
60 info.fColor = color;
61 info.fRegion = region;
62
63 SkRect bounds = SkRect::Make(region.getBounds());
64 this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kNo, IsHairline::kNo);
65 }
66
name() const67 const char* name() const override { return "GrRegionOp"; }
68
visitProxies(const GrVisitProxyFunc & func) const69 void visitProxies(const GrVisitProxyFunc& func) const override {
70 if (fProgramInfo) {
71 fProgramInfo->visitFPProxies(func);
72 } else {
73 fHelper.visitProxies(func);
74 }
75 }
76
fixedFunctionFlags() const77 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
78
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)79 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
80 GrClampType clampType) override {
81 return fHelper.finalizeProcessors(caps, clip, clampType, GrProcessorAnalysisCoverage::kNone,
82 &fRegions[0].fColor, &fWideColor);
83 }
84
85 private:
programInfo()86 GrProgramInfo* programInfo() override { return fProgramInfo; }
87
onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)88 void onCreateProgramInfo(const GrCaps* caps,
89 SkArenaAlloc* arena,
90 const GrSurfaceProxyView& writeView,
91 bool usesMSAASurface,
92 GrAppliedClip&& appliedClip,
93 const GrDstProxyView& dstProxyView,
94 GrXferBarrierFlags renderPassXferBarriers,
95 GrLoadOp colorLoadOp) override {
96 GrGeometryProcessor* gp = make_gp(arena, fViewMatrix, fWideColor);
97 if (!gp) {
98 SkDebugf("Couldn't create GrGeometryProcessor\n");
99 return;
100 }
101
102 fProgramInfo = fHelper.createProgramInfoWithStencil(caps, arena, writeView, usesMSAASurface,
103 std::move(appliedClip), dstProxyView,
104 gp, GrPrimitiveType::kTriangles,
105 renderPassXferBarriers, colorLoadOp);
106 }
107
onPrepareDraws(GrMeshDrawTarget * target)108 void onPrepareDraws(GrMeshDrawTarget* target) override {
109 if (!fProgramInfo) {
110 this->createProgramInfo(target);
111 if (!fProgramInfo) {
112 return;
113 }
114 }
115
116 int numRegions = fRegions.count();
117 int numRects = 0;
118
119 SkSafeMath safeMath;
120 for (int i = 0; i < numRegions; i++) {
121 numRects = safeMath.addInt(numRects, fRegions[i].fRegion.computeRegionComplexity());
122 }
123
124 if (!numRects || !safeMath) {
125 return;
126 }
127
128 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), numRects);
129
130 VertexWriter vertices{helper.vertices()};
131 if (!vertices) {
132 SkDebugf("Could not allocate vertices\n");
133 return;
134 }
135
136 for (int i = 0; i < numRegions; i++) {
137 GrVertexColor color(fRegions[i].fColor, fWideColor);
138 SkRegion::Iterator iter(fRegions[i].fRegion);
139 while (!iter.done()) {
140 SkRect rect = SkRect::Make(iter.rect());
141 vertices.writeQuad(VertexWriter::TriStripFromRect(rect), color);
142 iter.next();
143 }
144 }
145
146 fMesh = helper.mesh();
147 }
148
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)149 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
150 if (!fProgramInfo || !fMesh) {
151 return;
152 }
153
154 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
155 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
156 flushState->drawMesh(*fMesh);
157 }
158
onCombineIfPossible(GrOp * t,SkArenaAlloc *,const GrCaps & caps)159 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
160 auto that = t->cast<RegionOpImpl>();
161 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
162 return CombineResult::kCannotCombine;
163 }
164
165 if (fViewMatrix != that->fViewMatrix) {
166 return CombineResult::kCannotCombine;
167 }
168
169 fRegions.push_back_n(that->fRegions.count(), that->fRegions.begin());
170 fWideColor |= that->fWideColor;
171 return CombineResult::kMerged;
172 }
173
174 #if GR_TEST_UTILS
onDumpInfo() const175 SkString onDumpInfo() const override {
176 SkString str = SkStringPrintf("# combined: %d\n", fRegions.count());
177 for (int i = 0; i < fRegions.count(); ++i) {
178 const RegionInfo& info = fRegions[i];
179 str.appendf("%d: Color: 0x%08x, Region with %d rects\n", i, info.fColor.toBytes_RGBA(),
180 info.fRegion.computeRegionComplexity());
181 }
182 str += fHelper.dumpInfo();
183 return str;
184 }
185 #endif
186
187 struct RegionInfo {
188 SkPMColor4f fColor;
189 SkRegion fRegion;
190 };
191
192 Helper fHelper;
193 SkMatrix fViewMatrix;
194 SkSTArray<1, RegionInfo, true> fRegions;
195 bool fWideColor;
196
197 GrSimpleMesh* fMesh = nullptr;
198 GrProgramInfo* fProgramInfo = nullptr;
199
200 using INHERITED = GrMeshDrawOp;
201 };
202
203 } // anonymous namespace
204
Make(GrRecordingContext * context,GrPaint && paint,const SkMatrix & viewMatrix,const SkRegion & region,GrAAType aaType,const GrUserStencilSettings * stencilSettings)205 GrOp::Owner Make(GrRecordingContext* context,
206 GrPaint&& paint,
207 const SkMatrix& viewMatrix,
208 const SkRegion& region,
209 GrAAType aaType,
210 const GrUserStencilSettings* stencilSettings) {
211 if (aaType != GrAAType::kNone && aaType != GrAAType::kMSAA) {
212 return nullptr;
213 }
214 return RegionOpImpl::Make(context, std::move(paint), viewMatrix, region, aaType,
215 stencilSettings);
216 }
217
218 } // namespace skgpu::v1::RegionOp
219
220 #if GR_TEST_UTILS
221
222 #include "src/gpu/GrDrawOpTest.h"
223
GR_DRAW_OP_TEST_DEFINE(RegionOp)224 GR_DRAW_OP_TEST_DEFINE(RegionOp) {
225 SkRegion region;
226 int n = random->nextULessThan(200);
227 for (int i = 0; i < n; ++i) {
228 SkIPoint center;
229 center.fX = random->nextULessThan(1000);
230 center.fY = random->nextULessThan(1000);
231 int w = random->nextRangeU(10, 1000);
232 int h = random->nextRangeU(10, 1000);
233 SkIRect rect = {center.fX - w / 2, center.fY - h / 2, center.fX + w / 2, center.fY + h / 2};
234 SkRegion::Op op;
235 if (i == 0) {
236 op = SkRegion::kReplace_Op;
237 } else {
238 // Pick an other than replace.
239 static_assert(SkRegion::kLastOp == SkRegion::kReplace_Op);
240 op = (SkRegion::Op)random->nextULessThan(SkRegion::kLastOp);
241 }
242 region.op(rect, op);
243 }
244 SkMatrix viewMatrix = GrTest::TestMatrix(random);
245 GrAAType aaType = GrAAType::kNone;
246 if (numSamples > 1 && random->nextBool()) {
247 aaType = GrAAType::kMSAA;
248 }
249 return skgpu::v1::RegionOp::RegionOpImpl::Make(context, std::move(paint), viewMatrix, region,
250 aaType, GrGetRandomStencil(random, context));
251 }
252
253 #endif // GR_TEST_UTILS
254