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