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: 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 63 bool implementsDistanceVector() const override { return true; } 64 65 const Attribute* inPosition() const { return fInPosition; } 66 const Attribute* inColor() const { return fInColor; } 67 const Attribute* inRectEdge() const { return fInRectEdge; } 68 const Attribute* inWidthHeight() const { return fInWidthHeight; } 69 70 const SkMatrix& localMatrix() const { return fLocalMatrix; } 71 72 ~RectGeometryProcessor() override {} 73 74 const char* name() const override { return "RectEdge"; } 75 76 class GLSLProcessor : public GrGLSLGeometryProcessor { 77 public: 78 GLSLProcessor() {} 79 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 186 static void GenKey(const GrGeometryProcessor& gp, 187 const GrShaderCaps&, 188 GrProcessorKeyBuilder* b) { 189 b->add32(0x0); 190 } 191 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 202 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { 203 GLSLProcessor::GenKey(*this, caps, b); 204 } 205 206 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 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 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 256 const char* name() const override { return "AnalyticRectOp"; } 257 258 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: 272 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color, 273 GrPipelineAnalysisCoverage* coverage) const override { 274 color->setToConstant(fGeoData[0].fColor); 275 *coverage = GrPipelineAnalysisCoverage::kSingleChannel; 276 } 277 278 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override { 279 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor); 280 if (!optimizations.readsLocalCoords()) { 281 fViewMatrixIfUsingLocalCoords.reset(); 282 } 283 } 284 285 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 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 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 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