1 /* 2 * Copyright 2015 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 "GrNonAAFillRectOp.h" 9 10 #include "GrColor.h" 11 #include "GrDefaultGeoProcFactory.h" 12 #include "GrMeshDrawOp.h" 13 #include "GrOpFlushState.h" 14 #include "GrPrimitiveProcessor.h" 15 #include "GrQuad.h" 16 #include "GrResourceProvider.h" 17 18 static const int kVertsPerInstance = 4; 19 static const int kIndicesPerInstance = 6; 20 21 /** We always use per-vertex colors so that rects can be combined across color changes. Sometimes 22 we have explicit local coords and sometimes not. We *could* always provide explicit local 23 coords and just duplicate the positions when the caller hasn't provided a local coord rect, 24 but we haven't seen a use case which frequently switches between local rect and no local 25 rect draws. 26 27 The vertex attrib order is always pos, color, [local coords]. 28 */ 29 static sk_sp<GrGeometryProcessor> make_persp_gp(const SkMatrix& viewMatrix, 30 bool hasExplicitLocalCoords, 31 const SkMatrix* localMatrix) { 32 SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective())); 33 34 using namespace GrDefaultGeoProcFactory; 35 36 // If we have perspective on the viewMatrix then we won't map on the CPU, nor will we map 37 // the local rect on the cpu (in case the localMatrix also has perspective). 38 // Otherwise, if we have a local rect, then we apply the localMatrix directly to the localRect 39 // to generate vertex local coords 40 if (viewMatrix.hasPerspective()) { 41 LocalCoords localCoords(hasExplicitLocalCoords ? LocalCoords::kHasExplicit_Type 42 : LocalCoords::kUsePosition_Type, 43 localMatrix); 44 return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type, 45 Coverage::kSolid_Type, localCoords, viewMatrix); 46 } else if (hasExplicitLocalCoords) { 47 LocalCoords localCoords(LocalCoords::kHasExplicit_Type, localMatrix); 48 return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type, 49 Coverage::kSolid_Type, localCoords, SkMatrix::I()); 50 } else { 51 LocalCoords localCoords(LocalCoords::kUsePosition_Type, localMatrix); 52 return GrDefaultGeoProcFactory::MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, 53 Coverage::kSolid_Type, localCoords, 54 viewMatrix); 55 } 56 } 57 58 static void tesselate(intptr_t vertices, 59 size_t vertexStride, 60 GrColor color, 61 const SkMatrix* viewMatrix, 62 const SkRect& rect, 63 const GrQuad* localQuad) { 64 SkPoint* positions = reinterpret_cast<SkPoint*>(vertices); 65 66 positions->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride); 67 68 if (viewMatrix) { 69 viewMatrix->mapPointsWithStride(positions, vertexStride, kVertsPerInstance); 70 } 71 72 // Setup local coords 73 // TODO we should only do this if local coords are being read 74 if (localQuad) { 75 static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor); 76 for (int i = 0; i < kVertsPerInstance; i++) { 77 SkPoint* coords = 78 reinterpret_cast<SkPoint*>(vertices + kLocalOffset + i * vertexStride); 79 *coords = localQuad->point(i); 80 } 81 } 82 83 static const int kColorOffset = sizeof(SkPoint); 84 GrColor* vertColor = reinterpret_cast<GrColor*>(vertices + kColorOffset); 85 for (int j = 0; j < 4; ++j) { 86 *vertColor = color; 87 vertColor = (GrColor*)((intptr_t)vertColor + vertexStride); 88 } 89 } 90 91 // We handle perspective in the local matrix or viewmatrix with special ops. 92 class NonAAFillRectPerspectiveOp final : public GrMeshDrawOp { 93 public: 94 DEFINE_OP_CLASS_ID 95 96 NonAAFillRectPerspectiveOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect, 97 const SkRect* localRect, const SkMatrix* localMatrix) 98 : INHERITED(ClassID()), fViewMatrix(viewMatrix) { 99 SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective())); 100 RectInfo& info = fRects.push_back(); 101 info.fColor = color; 102 info.fRect = rect; 103 fHasLocalRect = SkToBool(localRect); 104 fHasLocalMatrix = SkToBool(localMatrix); 105 if (fHasLocalMatrix) { 106 fLocalMatrix = *localMatrix; 107 } 108 if (fHasLocalRect) { 109 info.fLocalRect = *localRect; 110 } 111 this->setTransformedBounds(rect, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo); 112 } 113 114 const char* name() const override { return "NonAAFillRectPerspectiveOp"; } 115 116 SkString dumpInfo() const override { 117 SkString str; 118 str.appendf("# combined: %d\n", fRects.count()); 119 for (int i = 0; i < fRects.count(); ++i) { 120 const RectInfo& geo = fRects[0]; 121 str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i, 122 geo.fColor, geo.fRect.fLeft, geo.fRect.fTop, geo.fRect.fRight, 123 geo.fRect.fBottom); 124 } 125 str.append(DumpPipelineInfo(*this->pipeline())); 126 str.append(INHERITED::dumpInfo()); 127 return str; 128 } 129 130 private: 131 NonAAFillRectPerspectiveOp() : INHERITED(ClassID()) {} 132 133 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color, 134 GrPipelineAnalysisCoverage* coverage) const override { 135 color->setToConstant(fRects[0].fColor); 136 *coverage = GrPipelineAnalysisCoverage::kNone; 137 } 138 139 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override { 140 optimizations.getOverrideColorIfSet(&fRects[0].fColor); 141 } 142 143 void onPrepareDraws(Target* target) const override { 144 sk_sp<GrGeometryProcessor> gp = make_persp_gp(fViewMatrix, 145 fHasLocalRect, 146 fHasLocalMatrix ? &fLocalMatrix : nullptr); 147 if (!gp) { 148 SkDebugf("Couldn't create GrGeometryProcessor\n"); 149 return; 150 } 151 SkASSERT(fHasLocalRect 152 ? gp->getVertexStride() == 153 sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr) 154 : gp->getVertexStride() == 155 sizeof(GrDefaultGeoProcFactory::PositionColorAttr)); 156 157 size_t vertexStride = gp->getVertexStride(); 158 int instanceCount = fRects.count(); 159 160 sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer()); 161 InstancedHelper helper; 162 void* vertices = 163 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(), 164 kVertsPerInstance, kIndicesPerInstance, instanceCount); 165 if (!vertices || !indexBuffer) { 166 SkDebugf("Could not allocate vertices\n"); 167 return; 168 } 169 170 for (int i = 0; i < instanceCount; i++) { 171 const RectInfo& info = fRects[i]; 172 intptr_t verts = 173 reinterpret_cast<intptr_t>(vertices) + i * kVertsPerInstance * vertexStride; 174 if (fHasLocalRect) { 175 GrQuad quad(info.fLocalRect); 176 tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, &quad); 177 } else { 178 tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, nullptr); 179 } 180 } 181 helper.recordDraw(target, gp.get()); 182 } 183 184 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 185 NonAAFillRectPerspectiveOp* that = t->cast<NonAAFillRectPerspectiveOp>(); 186 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), 187 that->bounds(), caps)) { 188 return false; 189 } 190 191 // We could combine across perspective vm changes if we really wanted to. 192 if (!fViewMatrix.cheapEqualTo(that->fViewMatrix)) { 193 return false; 194 } 195 if (fHasLocalRect != that->fHasLocalRect) { 196 return false; 197 } 198 if (fHasLocalMatrix && !fLocalMatrix.cheapEqualTo(that->fLocalMatrix)) { 199 return false; 200 } 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 SkRect fRect; 209 GrColor fColor; 210 SkRect fLocalRect; 211 }; 212 213 SkSTArray<1, RectInfo, true> fRects; 214 bool fHasLocalMatrix; 215 bool fHasLocalRect; 216 SkMatrix fLocalMatrix; 217 SkMatrix fViewMatrix; 218 219 typedef GrMeshDrawOp INHERITED; 220 }; 221 222 namespace GrNonAAFillRectOp { 223 224 std::unique_ptr<GrMeshDrawOp> MakeWithPerspective(GrColor color, 225 const SkMatrix& viewMatrix, 226 const SkRect& rect, 227 const SkRect* localRect, 228 const SkMatrix* localMatrix) { 229 return std::unique_ptr<GrMeshDrawOp>( 230 new NonAAFillRectPerspectiveOp(color, viewMatrix, rect, localRect, localMatrix)); 231 } 232 }; 233 234 /////////////////////////////////////////////////////////////////////////////////////////////////// 235 236 #if GR_TEST_UTILS 237 238 #include "GrDrawOpTest.h" 239 240 DRAW_OP_TEST_DEFINE(NonAAFillRectPerspectiveOp) { 241 GrColor color = GrRandomColor(random); 242 SkRect rect = GrTest::TestRect(random); 243 SkRect localRect = GrTest::TestRect(random); 244 SkMatrix viewMatrix = GrTest::TestMatrix(random); 245 bool hasLocalMatrix = random->nextBool(); 246 SkMatrix localMatrix; 247 if (!viewMatrix.hasPerspective()) { 248 localMatrix = GrTest::TestMatrixPerspective(random); 249 hasLocalMatrix = true; 250 } 251 252 bool hasLocalRect = random->nextBool(); 253 return GrNonAAFillRectOp::MakeWithPerspective(color, viewMatrix, rect, 254 hasLocalRect ? &localRect : nullptr, 255 hasLocalMatrix ? &localMatrix : nullptr); 256 } 257 258 #endif 259