• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&center, 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