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 "GrAnalyticRectOp.h"
9
10 #include "GrDrawOpTest.h"
11 #include "GrGeometryProcessor.h"
12 #include "GrOpFlushState.h"
13 #include "GrProcessor.h"
14 #include "GrResourceProvider.h"
15 #include "SkRRect.h"
16 #include "SkStrokeRec.h"
17 #include "glsl/GrGLSLFragmentShaderBuilder.h"
18 #include "glsl/GrGLSLGeometryProcessor.h"
19 #include "glsl/GrGLSLProgramDataManager.h"
20 #include "glsl/GrGLSLUniformHandler.h"
21 #include "glsl/GrGLSLUtil.h"
22 #include "glsl/GrGLSLVarying.h"
23 #include "glsl/GrGLSLVertexShaderBuilder.h"
24 #include "ops/GrMeshDrawOp.h"
25
26 namespace {
27
28 struct RectVertex {
29 SkPoint fPos;
30 GrColor fColor;
31 SkPoint fCenter;
32 SkVector fDownDir;
33 SkScalar fHalfWidth;
34 SkScalar fHalfHeight;
35 };
36 }
37
38 ///////////////////////////////////////////////////////////////////////////////
39
40 /**
41 * The output of this effect is the input color and coverage for an arbitrarily oriented rect. The
42 * rect is specified as:
43 * Center of the rect
44 * Unit vector point down the height of the rect
45 * Half width + 0.5
46 * Half height + 0.5
47 * The center and vector are stored in a vec4 varying ("RectEdge") with the
48 * center in the xy components and the vector in the zw components.
49 * The munged width and height are stored in a vec2 varying ("WidthHeight")
50 * with the width in x and the height in y.
51 */
52 class RectGeometryProcessor : public GrGeometryProcessor {
53 public:
RectGeometryProcessor(const SkMatrix & localMatrix)54 RectGeometryProcessor(const SkMatrix& localMatrix) : fLocalMatrix(localMatrix) {
55 this->initClassID<RectGeometryProcessor>();
56 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
57 kHigh_GrSLPrecision);
58 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
59 fInRectEdge = &this->addVertexAttrib("inRectEdge", kVec4f_GrVertexAttribType);
60 fInWidthHeight = &this->addVertexAttrib("inWidthHeight", kVec2f_GrVertexAttribType);
61 }
62
implementsDistanceVector() const63 bool implementsDistanceVector() const override { return true; }
64
inPosition() const65 const Attribute* inPosition() const { return fInPosition; }
inColor() const66 const Attribute* inColor() const { return fInColor; }
inRectEdge() const67 const Attribute* inRectEdge() const { return fInRectEdge; }
inWidthHeight() const68 const Attribute* inWidthHeight() const { return fInWidthHeight; }
69
localMatrix() const70 const SkMatrix& localMatrix() const { return fLocalMatrix; }
71
~RectGeometryProcessor()72 ~RectGeometryProcessor() override {}
73
name() const74 const char* name() const override { return "RectEdge"; }
75
76 class GLSLProcessor : public GrGLSLGeometryProcessor {
77 public:
GLSLProcessor()78 GLSLProcessor() {}
79
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)80 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
81 const RectGeometryProcessor& rgp = args.fGP.cast<RectGeometryProcessor>();
82 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
83 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
84 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
85
86 // emit attributes
87 varyingHandler->emitAttributes(rgp);
88
89 // setup the varying for the position
90 GrGLSLVertToFrag positionVary(kVec2f_GrSLType);
91 varyingHandler->addVarying("Position", &positionVary);
92 vertBuilder->codeAppendf("%s = %s;", positionVary.vsOut(), rgp.inPosition()->fName);
93
94 // setup the varying for the center point and the unit vector that points down the
95 // height of the rect
96 GrGLSLVertToFrag rectEdgeVary(kVec4f_GrSLType);
97 varyingHandler->addVarying("RectEdge", &rectEdgeVary);
98 vertBuilder->codeAppendf("%s = %s;", rectEdgeVary.vsOut(), rgp.inRectEdge()->fName);
99
100 // setup the varying for the width/2+.5 and height/2+.5
101 GrGLSLVertToFrag widthHeightVary(kVec2f_GrSLType);
102 varyingHandler->addVarying("WidthHeight", &widthHeightVary);
103 vertBuilder->codeAppendf("%s = %s;", widthHeightVary.vsOut(),
104 rgp.inWidthHeight()->fName);
105
106 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
107
108 // setup pass through color
109 varyingHandler->addPassThroughAttribute(rgp.inColor(), args.fOutputColor);
110
111 // Setup position
112 this->setupPosition(vertBuilder, gpArgs, rgp.inPosition()->fName);
113
114 // emit transforms
115 this->emitTransforms(vertBuilder,
116 varyingHandler,
117 uniformHandler,
118 gpArgs->fPositionVar,
119 rgp.inPosition()->fName,
120 rgp.localMatrix(),
121 args.fFPCoordTransformHandler);
122
123 // TODO: compute all these offsets, spans, and scales in the VS
124 fragBuilder->codeAppendf("float insetW = min(1.0, %s.x) - 0.5;",
125 widthHeightVary.fsIn());
126 fragBuilder->codeAppendf("float insetH = min(1.0, %s.y) - 0.5;",
127 widthHeightVary.fsIn());
128 fragBuilder->codeAppend("float outset = 0.5;");
129 // For rects > 1 pixel wide and tall the span's are noops (i.e., 1.0). For rects
130 // < 1 pixel wide or tall they serve to normalize the < 1 ramp to a 0 .. 1 range.
131 fragBuilder->codeAppend("float spanW = insetW + outset;");
132 fragBuilder->codeAppend("float spanH = insetH + outset;");
133 // For rects < 1 pixel wide or tall, these scale factors are used to cap the maximum
134 // value of coverage that is used. In other words it is the coverage that is
135 // used in the interior of the rect after the ramp.
136 fragBuilder->codeAppend("float scaleW = min(1.0, 2.0*insetW/spanW);");
137 fragBuilder->codeAppend("float scaleH = min(1.0, 2.0*insetH/spanH);");
138 // Compute the coverage for the rect's width
139 fragBuilder->codeAppendf("vec2 offset = %s.xy - %s.xy;", positionVary.fsIn(),
140 rectEdgeVary.fsIn());
141 fragBuilder->codeAppendf("float perpDot = abs(offset.x * %s.w - offset.y * %s.z);",
142 rectEdgeVary.fsIn(), rectEdgeVary.fsIn());
143
144 if (args.fDistanceVectorName) {
145 fragBuilder->codeAppendf("float widthDistance = %s.x - perpDot;",
146 widthHeightVary.fsIn());
147 }
148
149 fragBuilder->codeAppendf(
150 "float coverage = scaleW*clamp((%s.x-perpDot)/spanW, 0.0, 1.0);",
151 widthHeightVary.fsIn());
152 // Compute the coverage for the rect's height and merge with the width
153 fragBuilder->codeAppendf("perpDot = abs(dot(offset, %s.zw));", rectEdgeVary.fsIn());
154
155 if (args.fDistanceVectorName) {
156 fragBuilder->codeAppendf("float heightDistance = %s.y - perpDot;",
157 widthHeightVary.fsIn());
158 }
159
160 fragBuilder->codeAppendf(
161 "coverage = coverage*scaleH*clamp((%s.y-perpDot)/spanH, 0.0, 1.0);",
162 widthHeightVary.fsIn());
163
164 fragBuilder->codeAppendf("%s = vec4(coverage);", args.fOutputCoverage);
165
166 if (args.fDistanceVectorName) {
167 fragBuilder->codeAppend("// Calculating distance vector\n");
168 fragBuilder->codeAppend("vec2 dvAxis;");
169 fragBuilder->codeAppend("float dvLength;");
170
171 fragBuilder->codeAppend("if (heightDistance < widthDistance) {");
172 fragBuilder->codeAppendf(" dvAxis = %s.zw;", rectEdgeVary.fsIn());
173 fragBuilder->codeAppend(" dvLength = heightDistance;");
174 fragBuilder->codeAppend("} else {");
175 fragBuilder->codeAppendf(" dvAxis = vec2(-%s.w, %s.z);", rectEdgeVary.fsIn(),
176 rectEdgeVary.fsIn());
177 fragBuilder->codeAppend(" dvLength = widthDistance;");
178 fragBuilder->codeAppend("}");
179
180 fragBuilder->codeAppend("float dvSign = sign(dot(offset, dvAxis));");
181 fragBuilder->codeAppendf("%s = vec4(dvSign * dvAxis, dvLength, 0.0);",
182 args.fDistanceVectorName);
183 }
184 }
185
GenKey(const GrGeometryProcessor & gp,const GrShaderCaps &,GrProcessorKeyBuilder * b)186 static void GenKey(const GrGeometryProcessor& gp,
187 const GrShaderCaps&,
188 GrProcessorKeyBuilder* b) {
189 b->add32(0x0);
190 }
191
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & primProc,FPCoordTransformIter && transformIter)192 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
193 FPCoordTransformIter&& transformIter) override {
194 const RectGeometryProcessor& rgp = primProc.cast<RectGeometryProcessor>();
195 this->setTransformDataHelper(rgp.fLocalMatrix, pdman, &transformIter);
196 }
197
198 private:
199 typedef GrGLSLGeometryProcessor INHERITED;
200 };
201
getGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const202 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
203 GLSLProcessor::GenKey(*this, caps, b);
204 }
205
createGLSLInstance(const GrShaderCaps &) const206 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
207 return new GLSLProcessor();
208 }
209
210 private:
211 SkMatrix fLocalMatrix;
212
213 const Attribute* fInPosition;
214 const Attribute* fInColor;
215 const Attribute* fInRectEdge;
216 const Attribute* fInWidthHeight;
217
218 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
219
220 typedef GrGeometryProcessor INHERITED;
221 };
222
223 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(RectGeometryProcessor);
224
225 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)226 sk_sp<GrGeometryProcessor> RectGeometryProcessor::TestCreate(GrProcessorTestData* d) {
227 return sk_sp<GrGeometryProcessor>(new RectGeometryProcessor(GrTest::TestMatrix(d->fRandom)));
228 }
229 #endif
230
231 ///////////////////////////////////////////////////////////////////////////////
232
233 class AnalyticRectOp final : public GrMeshDrawOp {
234 public:
235 DEFINE_OP_CLASS_ID
236
AnalyticRectOp(GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & croppedRect,const SkRect & bounds)237 AnalyticRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
238 const SkRect& croppedRect, const SkRect& bounds)
239 : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) {
240 SkPoint center = SkPoint::Make(rect.centerX(), rect.centerY());
241 viewMatrix.mapPoints(¢er, 1);
242 SkScalar halfWidth = viewMatrix.mapRadius(SkScalarHalf(rect.width()));
243 SkScalar halfHeight = viewMatrix.mapRadius(SkScalarHalf(rect.height()));
244 SkVector downDir = viewMatrix.mapVector(0.0f, 1.0f);
245 downDir.normalize();
246
247 SkRect deviceSpaceCroppedRect = croppedRect;
248 viewMatrix.mapRect(&deviceSpaceCroppedRect);
249
250 fGeoData.emplace_back(
251 Geometry{color, center, downDir, halfWidth, halfHeight, deviceSpaceCroppedRect});
252
253 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
254 }
255
name() const256 const char* name() const override { return "AnalyticRectOp"; }
257
dumpInfo() const258 SkString dumpInfo() const override {
259 SkString string;
260 for (int i = 0; i < fGeoData.count(); ++i) {
261 string.appendf("Color: 0x%08x Rect [C:(%.2f, %.2f) D:<%.2f,%.3f> W/2:%.2f H/2:%.2f]\n",
262 fGeoData[i].fColor, fGeoData[i].fCenter.x(), fGeoData[i].fCenter.y(),
263 fGeoData[i].fDownDir.x(), fGeoData[i].fDownDir.y(),
264 fGeoData[i].fHalfWidth, fGeoData[i].fHalfHeight);
265 }
266 string.append(DumpPipelineInfo(*this->pipeline()));
267 string.append(INHERITED::dumpInfo());
268 return string;
269 }
270
271 private:
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const272 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
273 GrPipelineAnalysisCoverage* coverage) const override {
274 color->setToConstant(fGeoData[0].fColor);
275 *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
276 }
277
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)278 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
279 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
280 if (!optimizations.readsLocalCoords()) {
281 fViewMatrixIfUsingLocalCoords.reset();
282 }
283 }
284
onPrepareDraws(Target * target) const285 void onPrepareDraws(Target* target) const override {
286 SkMatrix localMatrix;
287 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
288 return;
289 }
290
291 // Setup geometry processor
292 sk_sp<GrGeometryProcessor> gp(new RectGeometryProcessor(localMatrix));
293
294 int instanceCount = fGeoData.count();
295 size_t vertexStride = gp->getVertexStride();
296 SkASSERT(vertexStride == sizeof(RectVertex));
297 QuadHelper helper;
298 RectVertex* verts =
299 reinterpret_cast<RectVertex*>(helper.init(target, vertexStride, instanceCount));
300 if (!verts) {
301 return;
302 }
303
304 for (int i = 0; i < instanceCount; i++) {
305 const Geometry& geom = fGeoData[i];
306
307 GrColor color = geom.fColor;
308 SkPoint center = geom.fCenter;
309 SkVector downDir = geom.fDownDir;
310 SkScalar halfWidth = geom.fHalfWidth;
311 SkScalar halfHeight = geom.fHalfHeight;
312 SkRect croppedRect = geom.fCroppedRect;
313
314 SkVector rightDir;
315 downDir.rotateCCW(&rightDir);
316
317 verts[0].fPos = {croppedRect.fLeft, croppedRect.fTop};
318 verts[0].fColor = color;
319 verts[0].fCenter = center;
320 verts[0].fDownDir = downDir;
321 verts[0].fHalfWidth = halfWidth;
322 verts[0].fHalfHeight = halfHeight;
323
324 verts[1].fPos = {croppedRect.fRight, croppedRect.fTop};
325 verts[1].fColor = color;
326 verts[1].fCenter = center;
327 verts[1].fDownDir = downDir;
328 verts[1].fHalfWidth = halfWidth;
329 verts[1].fHalfHeight = halfHeight;
330
331 verts[2].fPos = {croppedRect.fRight, croppedRect.fBottom};
332 verts[2].fColor = color;
333 verts[2].fCenter = center;
334 verts[2].fDownDir = downDir;
335 verts[2].fHalfWidth = halfWidth;
336 verts[2].fHalfHeight = halfHeight;
337
338 verts[3].fPos = {croppedRect.fLeft, croppedRect.fBottom};
339 verts[3].fColor = color;
340 verts[3].fCenter = center;
341 verts[3].fDownDir = downDir;
342 verts[3].fHalfWidth = halfWidth;
343 verts[3].fHalfHeight = halfHeight;
344
345 verts += kVerticesPerQuad;
346 }
347 helper.recordDraw(target, gp.get());
348 }
349
onCombineIfPossible(GrOp * t,const GrCaps & caps)350 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
351 AnalyticRectOp* that = t->cast<AnalyticRectOp>();
352 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
353 that->bounds(), caps)) {
354 return false;
355 }
356
357 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
358 return false;
359 }
360
361 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
362 this->joinBounds(*that);
363 return true;
364 }
365
366 struct Geometry {
367 GrColor fColor;
368 SkPoint fCenter;
369 SkVector fDownDir;
370 SkScalar fHalfWidth;
371 SkScalar fHalfHeight;
372 SkRect fCroppedRect;
373 };
374
375 SkMatrix fViewMatrixIfUsingLocalCoords;
376 SkSTArray<1, Geometry, true> fGeoData;
377
378 typedef GrMeshDrawOp INHERITED;
379 };
380
Make(GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & croppedRect,const SkRect & bounds)381 std::unique_ptr<GrMeshDrawOp> GrAnalyticRectOp::Make(GrColor color,
382 const SkMatrix& viewMatrix,
383 const SkRect& rect,
384 const SkRect& croppedRect,
385 const SkRect& bounds) {
386 return std::unique_ptr<GrMeshDrawOp>(
387 new AnalyticRectOp(color, viewMatrix, rect, croppedRect, bounds));
388 }
389
390 #if GR_TEST_UTILS
391
DRAW_OP_TEST_DEFINE(AnalyticRectOp)392 DRAW_OP_TEST_DEFINE(AnalyticRectOp) {
393 SkMatrix viewMatrix = GrTest::TestMatrix(random);
394 GrColor color = GrRandomColor(random);
395 SkRect rect = GrTest::TestSquare(random);
396 SkRect croppedRect = GrTest::TestSquare(random);
397 SkRect bounds = GrTest::TestSquare(random);
398 return std::unique_ptr<GrMeshDrawOp>(
399 new AnalyticRectOp(color, viewMatrix, rect, croppedRect, bounds));
400 }
401
402 #endif
403