1 /* 2 * Copyright 2017 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 "GrAppliedClip.h" 9 #include "GrColor.h" 10 #include "GrDefaultGeoProcFactory.h" 11 #include "GrDrawOpTest.h" 12 #include "GrMeshDrawOp.h" 13 #include "GrOpFlushState.h" 14 #include "GrPrimitiveProcessor.h" 15 #include "GrQuad.h" 16 #include "GrRectOpFactory.h" 17 #include "GrResourceProvider.h" 18 #include "GrSimpleMeshDrawOpHelper.h" 19 #include "SkMatrixPriv.h" 20 21 static const int kVertsPerRect = 4; 22 static const int kIndicesPerRect = 6; 23 24 /** We always use per-vertex colors so that rects can be combined across color changes. Sometimes 25 we have explicit local coords and sometimes not. We *could* always provide explicit local 26 coords and just duplicate the positions when the caller hasn't provided a local coord rect, 27 but we haven't seen a use case which frequently switches between local rect and no local 28 rect draws. 29 30 The vertex attrib order is always pos, color, [local coords]. 31 */ 32 static sk_sp<GrGeometryProcessor> make_gp() { 33 using namespace GrDefaultGeoProcFactory; 34 return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type, Coverage::kSolid_Type, 35 LocalCoords::kHasExplicit_Type, SkMatrix::I()); 36 } 37 38 static sk_sp<GrGeometryProcessor> make_perspective_gp(const SkMatrix& viewMatrix, 39 bool hasExplicitLocalCoords, 40 const SkMatrix* localMatrix) { 41 SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective())); 42 43 using namespace GrDefaultGeoProcFactory; 44 45 // If we have perspective on the viewMatrix then we won't map on the CPU, nor will we map 46 // the local rect on the cpu (in case the localMatrix also has perspective). 47 // Otherwise, if we have a local rect, then we apply the localMatrix directly to the localRect 48 // to generate vertex local coords 49 if (viewMatrix.hasPerspective()) { 50 LocalCoords localCoords(hasExplicitLocalCoords ? LocalCoords::kHasExplicit_Type 51 : LocalCoords::kUsePosition_Type, 52 localMatrix); 53 return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type, 54 Coverage::kSolid_Type, localCoords, viewMatrix); 55 } else if (hasExplicitLocalCoords) { 56 LocalCoords localCoords(LocalCoords::kHasExplicit_Type, localMatrix); 57 return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type, 58 Coverage::kSolid_Type, localCoords, SkMatrix::I()); 59 } else { 60 LocalCoords localCoords(LocalCoords::kUsePosition_Type, localMatrix); 61 return GrDefaultGeoProcFactory::MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, 62 Coverage::kSolid_Type, localCoords, 63 viewMatrix); 64 } 65 } 66 67 static void tesselate(intptr_t vertices, 68 size_t vertexStride, 69 GrColor color, 70 const SkMatrix* viewMatrix, 71 const SkRect& rect, 72 const GrQuad* localQuad) { 73 SkPoint* positions = reinterpret_cast<SkPoint*>(vertices); 74 75 positions->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride); 76 77 if (viewMatrix) { 78 SkMatrixPriv::MapPointsWithStride(*viewMatrix, positions, vertexStride, kVertsPerRect); 79 } 80 81 // Setup local coords 82 // TODO we should only do this if local coords are being read 83 if (localQuad) { 84 static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor); 85 for (int i = 0; i < kVertsPerRect; i++) { 86 SkPoint* coords = 87 reinterpret_cast<SkPoint*>(vertices + kLocalOffset + i * vertexStride); 88 *coords = localQuad->point(i); 89 } 90 } 91 92 static const int kColorOffset = sizeof(SkPoint); 93 GrColor* vertColor = reinterpret_cast<GrColor*>(vertices + kColorOffset); 94 for (int j = 0; j < 4; ++j) { 95 *vertColor = color; 96 vertColor = (GrColor*)((intptr_t)vertColor + vertexStride); 97 } 98 } 99 100 namespace { 101 102 class NonAAFillRectOp final : public GrMeshDrawOp { 103 private: 104 using Helper = GrSimpleMeshDrawOpHelperWithStencil; 105 106 public: 107 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 108 const SkRect& rect, const SkRect* localRect, 109 const SkMatrix* localMatrix, GrAAType aaType, 110 const GrUserStencilSettings* stencilSettings) { 111 SkASSERT(GrAAType::kCoverage != aaType); 112 return Helper::FactoryHelper<NonAAFillRectOp>(std::move(paint), viewMatrix, rect, localRect, 113 localMatrix, aaType, stencilSettings); 114 } 115 116 NonAAFillRectOp() = delete; 117 118 NonAAFillRectOp(const Helper::MakeArgs& args, GrColor color, const SkMatrix& viewMatrix, 119 const SkRect& rect, const SkRect* localRect, const SkMatrix* localMatrix, 120 GrAAType aaType, const GrUserStencilSettings* stencilSettings) 121 : INHERITED(ClassID()), fHelper(args, aaType, stencilSettings) { 122 123 SkASSERT(!viewMatrix.hasPerspective() && (!localMatrix || !localMatrix->hasPerspective())); 124 RectInfo& info = fRects.push_back(); 125 info.fColor = color; 126 info.fViewMatrix = viewMatrix; 127 info.fRect = rect; 128 if (localRect && localMatrix) { 129 info.fLocalQuad.setFromMappedRect(*localRect, *localMatrix); 130 } else if (localRect) { 131 info.fLocalQuad.set(*localRect); 132 } else if (localMatrix) { 133 info.fLocalQuad.setFromMappedRect(rect, *localMatrix); 134 } else { 135 info.fLocalQuad.set(rect); 136 } 137 this->setTransformedBounds(fRects[0].fRect, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo); 138 } 139 140 const char* name() const override { return "NonAAFillRectOp"; } 141 142 SkString dumpInfo() const override { 143 SkString str; 144 str.append(GrMeshDrawOp::dumpInfo()); 145 str.appendf("# combined: %d\n", fRects.count()); 146 for (int i = 0; i < fRects.count(); ++i) { 147 const RectInfo& info = fRects[i]; 148 str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i, 149 info.fColor, info.fRect.fLeft, info.fRect.fTop, info.fRect.fRight, 150 info.fRect.fBottom); 151 } 152 str += fHelper.dumpInfo(); 153 str += INHERITED::dumpInfo(); 154 return str; 155 } 156 157 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override { 158 GrColor* color = &fRects.front().fColor; 159 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kNone, color); 160 } 161 162 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 163 164 DEFINE_OP_CLASS_ID 165 166 private: 167 void onPrepareDraws(Target* target) const override { 168 sk_sp<GrGeometryProcessor> gp = make_gp(); 169 if (!gp) { 170 SkDebugf("Couldn't create GrGeometryProcessor\n"); 171 return; 172 } 173 SkASSERT(gp->getVertexStride() == 174 sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)); 175 176 size_t vertexStride = gp->getVertexStride(); 177 int rectCount = fRects.count(); 178 179 sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer()); 180 PatternHelper helper(GrPrimitiveType::kTriangles); 181 void* vertices = helper.init(target, vertexStride, indexBuffer.get(), kVertsPerRect, 182 kIndicesPerRect, rectCount); 183 if (!vertices || !indexBuffer) { 184 SkDebugf("Could not allocate vertices\n"); 185 return; 186 } 187 188 for (int i = 0; i < rectCount; i++) { 189 intptr_t verts = 190 reinterpret_cast<intptr_t>(vertices) + i * kVertsPerRect * vertexStride; 191 tesselate(verts, vertexStride, fRects[i].fColor, &fRects[i].fViewMatrix, 192 fRects[i].fRect, &fRects[i].fLocalQuad); 193 } 194 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target)); 195 } 196 197 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 198 NonAAFillRectOp* that = t->cast<NonAAFillRectOp>(); 199 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 200 return false; 201 } 202 fRects.push_back_n(that->fRects.count(), that->fRects.begin()); 203 this->joinBounds(*that); 204 return true; 205 } 206 207 struct RectInfo { 208 GrColor fColor; 209 SkMatrix fViewMatrix; 210 SkRect fRect; 211 GrQuad fLocalQuad; 212 }; 213 214 Helper fHelper; 215 SkSTArray<1, RectInfo, true> fRects; 216 typedef GrMeshDrawOp INHERITED; 217 }; 218 219 // We handle perspective in the local matrix or viewmatrix with special ops. 220 class NonAAFillRectPerspectiveOp final : public GrMeshDrawOp { 221 private: 222 using Helper = GrSimpleMeshDrawOpHelperWithStencil; 223 224 public: 225 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 226 const SkRect& rect, const SkRect* localRect, 227 const SkMatrix* localMatrix, GrAAType aaType, 228 const GrUserStencilSettings* stencilSettings) { 229 SkASSERT(GrAAType::kCoverage != aaType); 230 return Helper::FactoryHelper<NonAAFillRectPerspectiveOp>(std::move(paint), viewMatrix, rect, 231 localRect, localMatrix, aaType, 232 stencilSettings); 233 } 234 235 NonAAFillRectPerspectiveOp() = delete; 236 237 NonAAFillRectPerspectiveOp(const Helper::MakeArgs& args, GrColor color, 238 const SkMatrix& viewMatrix, const SkRect& rect, 239 const SkRect* localRect, const SkMatrix* localMatrix, 240 GrAAType aaType, const GrUserStencilSettings* stencilSettings) 241 : INHERITED(ClassID()) 242 , fHelper(args, aaType, stencilSettings) 243 , fViewMatrix(viewMatrix) { 244 SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective())); 245 RectInfo& info = fRects.push_back(); 246 info.fColor = color; 247 info.fRect = rect; 248 fHasLocalRect = SkToBool(localRect); 249 fHasLocalMatrix = SkToBool(localMatrix); 250 if (fHasLocalMatrix) { 251 fLocalMatrix = *localMatrix; 252 } 253 if (fHasLocalRect) { 254 info.fLocalRect = *localRect; 255 } 256 this->setTransformedBounds(rect, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo); 257 } 258 259 const char* name() const override { return "NonAAFillRectPerspectiveOp"; } 260 261 SkString dumpInfo() const override { 262 SkString str; 263 str.appendf("# combined: %d\n", fRects.count()); 264 for (int i = 0; i < fRects.count(); ++i) { 265 const RectInfo& geo = fRects[0]; 266 str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i, 267 geo.fColor, geo.fRect.fLeft, geo.fRect.fTop, geo.fRect.fRight, 268 geo.fRect.fBottom); 269 } 270 str += fHelper.dumpInfo(); 271 str += INHERITED::dumpInfo(); 272 return str; 273 } 274 275 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override { 276 GrColor* color = &fRects.front().fColor; 277 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kNone, color); 278 } 279 280 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 281 282 DEFINE_OP_CLASS_ID 283 284 private: 285 void onPrepareDraws(Target* target) const override { 286 sk_sp<GrGeometryProcessor> gp = make_perspective_gp( 287 fViewMatrix, fHasLocalRect, fHasLocalMatrix ? &fLocalMatrix : nullptr); 288 if (!gp) { 289 SkDebugf("Couldn't create GrGeometryProcessor\n"); 290 return; 291 } 292 SkASSERT(fHasLocalRect 293 ? gp->getVertexStride() == 294 sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr) 295 : gp->getVertexStride() == 296 sizeof(GrDefaultGeoProcFactory::PositionColorAttr)); 297 298 size_t vertexStride = gp->getVertexStride(); 299 int rectCount = fRects.count(); 300 301 sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer()); 302 PatternHelper helper(GrPrimitiveType::kTriangles); 303 void* vertices = helper.init(target, vertexStride, indexBuffer.get(), kVertsPerRect, 304 kIndicesPerRect, rectCount); 305 if (!vertices || !indexBuffer) { 306 SkDebugf("Could not allocate vertices\n"); 307 return; 308 } 309 310 for (int i = 0; i < rectCount; i++) { 311 const RectInfo& info = fRects[i]; 312 intptr_t verts = 313 reinterpret_cast<intptr_t>(vertices) + i * kVertsPerRect * vertexStride; 314 if (fHasLocalRect) { 315 GrQuad quad(info.fLocalRect); 316 tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, &quad); 317 } else { 318 tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, nullptr); 319 } 320 } 321 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target)); 322 } 323 324 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 325 NonAAFillRectPerspectiveOp* that = t->cast<NonAAFillRectPerspectiveOp>(); 326 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 327 return false; 328 } 329 330 // We could combine across perspective vm changes if we really wanted to. 331 if (!fViewMatrix.cheapEqualTo(that->fViewMatrix)) { 332 return false; 333 } 334 if (fHasLocalRect != that->fHasLocalRect) { 335 return false; 336 } 337 if (fHasLocalMatrix && !fLocalMatrix.cheapEqualTo(that->fLocalMatrix)) { 338 return false; 339 } 340 341 fRects.push_back_n(that->fRects.count(), that->fRects.begin()); 342 this->joinBounds(*that); 343 return true; 344 } 345 346 struct RectInfo { 347 SkRect fRect; 348 GrColor fColor; 349 SkRect fLocalRect; 350 }; 351 352 SkSTArray<1, RectInfo, true> fRects; 353 Helper fHelper; 354 bool fHasLocalMatrix; 355 bool fHasLocalRect; 356 SkMatrix fLocalMatrix; 357 SkMatrix fViewMatrix; 358 359 typedef GrMeshDrawOp INHERITED; 360 }; 361 362 } // anonymous namespace 363 364 namespace GrRectOpFactory { 365 366 std::unique_ptr<GrDrawOp> MakeNonAAFill(GrPaint&& paint, const SkMatrix& viewMatrix, 367 const SkRect& rect, GrAAType aaType, 368 const GrUserStencilSettings* stencilSettings) { 369 if (viewMatrix.hasPerspective()) { 370 return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, nullptr, 371 nullptr, aaType, stencilSettings); 372 } else { 373 return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, nullptr, nullptr, aaType, 374 stencilSettings); 375 } 376 } 377 378 std::unique_ptr<GrDrawOp> MakeNonAAFillWithLocalMatrix( 379 GrPaint&& paint, const SkMatrix& viewMatrix, const SkMatrix& localMatrix, 380 const SkRect& rect, GrAAType aaType, const GrUserStencilSettings* stencilSettings) { 381 if (viewMatrix.hasPerspective() || localMatrix.hasPerspective()) { 382 return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, nullptr, 383 &localMatrix, aaType, stencilSettings); 384 } else { 385 return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, nullptr, &localMatrix, 386 aaType, stencilSettings); 387 } 388 } 389 390 std::unique_ptr<GrDrawOp> MakeNonAAFillWithLocalRect(GrPaint&& paint, const SkMatrix& viewMatrix, 391 const SkRect& rect, const SkRect& localRect, 392 GrAAType aaType) { 393 if (viewMatrix.hasPerspective()) { 394 return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, &localRect, 395 nullptr, aaType, nullptr); 396 } else { 397 return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, &localRect, nullptr, 398 aaType, nullptr); 399 } 400 } 401 402 } // namespace GrRectOpFactory 403 404 /////////////////////////////////////////////////////////////////////////////////////////////////// 405 406 #if GR_TEST_UTILS 407 408 GR_DRAW_OP_TEST_DEFINE(NonAAFillRectOp) { 409 SkRect rect = GrTest::TestRect(random); 410 SkRect localRect = GrTest::TestRect(random); 411 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); 412 SkMatrix localMatrix = GrTest::TestMatrix(random); 413 const GrUserStencilSettings* stencil = GrGetRandomStencil(random, context); 414 GrAAType aaType = GrAAType::kNone; 415 if (fsaaType == GrFSAAType::kUnifiedMSAA) { 416 aaType = random->nextBool() ? GrAAType::kMSAA : GrAAType::kNone; 417 } 418 const SkRect* lr = random->nextBool() ? &localRect : nullptr; 419 const SkMatrix* lm = random->nextBool() ? &localMatrix : nullptr; 420 if (viewMatrix.hasPerspective() || (lm && lm->hasPerspective())) { 421 return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, lr, lm, aaType, 422 stencil); 423 } else { 424 return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, lr, lm, aaType, stencil); 425 } 426 } 427 428 #endif 429