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 #include "SkMatrixPriv.h" 19 20 static const int kVertsPerInstance = 4; 21 static const int kIndicesPerInstance = 6; 22 23 /** We always use per-vertex colors so that rects can be combined across color changes. Sometimes 24 we have explicit local coords and sometimes not. We *could* always provide explicit local 25 coords and just duplicate the positions when the caller hasn't provided a local coord rect, 26 but we haven't seen a use case which frequently switches between local rect and no local 27 rect draws. 28 29 The vertex attrib order is always pos, color, [local coords]. 30 */ 31 static sk_sp<GrGeometryProcessor> make_gp() { 32 using namespace GrDefaultGeoProcFactory; 33 return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type, Coverage::kSolid_Type, 34 LocalCoords::kHasExplicit_Type, SkMatrix::I()); 35 } 36 37 static void tesselate(intptr_t vertices, 38 size_t vertexStride, 39 GrColor color, 40 const SkMatrix* viewMatrix, 41 const SkRect& rect, 42 const GrQuad* localQuad) { 43 SkPoint* positions = reinterpret_cast<SkPoint*>(vertices); 44 45 positions->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride); 46 47 if (viewMatrix) { 48 SkMatrixPriv::MapPointsWithStride(*viewMatrix, positions, vertexStride, kVertsPerInstance); 49 } 50 51 // Setup local coords 52 // TODO we should only do this if local coords are being read 53 if (localQuad) { 54 static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor); 55 for (int i = 0; i < kVertsPerInstance; i++) { 56 SkPoint* coords = 57 reinterpret_cast<SkPoint*>(vertices + kLocalOffset + i * vertexStride); 58 *coords = localQuad->point(i); 59 } 60 } 61 62 static const int kColorOffset = sizeof(SkPoint); 63 GrColor* vertColor = reinterpret_cast<GrColor*>(vertices + kColorOffset); 64 for (int j = 0; j < 4; ++j) { 65 *vertColor = color; 66 vertColor = (GrColor*)((intptr_t)vertColor + vertexStride); 67 } 68 } 69 70 class NonAAFillRectOp final : public GrMeshDrawOp { 71 public: 72 DEFINE_OP_CLASS_ID 73 74 NonAAFillRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect, 75 const SkRect* localRect, const SkMatrix* localMatrix) 76 : INHERITED(ClassID()) { 77 SkASSERT(!viewMatrix.hasPerspective() && (!localMatrix || !localMatrix->hasPerspective())); 78 RectInfo& info = fRects.push_back(); 79 info.fColor = color; 80 info.fViewMatrix = viewMatrix; 81 info.fRect = rect; 82 if (localRect && localMatrix) { 83 info.fLocalQuad.setFromMappedRect(*localRect, *localMatrix); 84 } else if (localRect) { 85 info.fLocalQuad.set(*localRect); 86 } else if (localMatrix) { 87 info.fLocalQuad.setFromMappedRect(rect, *localMatrix); 88 } else { 89 info.fLocalQuad.set(rect); 90 } 91 this->setTransformedBounds(fRects[0].fRect, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo); 92 } 93 94 const char* name() const override { return "NonAAFillRectOp"; } 95 96 SkString dumpInfo() const override { 97 SkString str; 98 str.appendf("# combined: %d\n", fRects.count()); 99 for (int i = 0; i < fRects.count(); ++i) { 100 const RectInfo& info = fRects[i]; 101 str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i, 102 info.fColor, info.fRect.fLeft, info.fRect.fTop, info.fRect.fRight, 103 info.fRect.fBottom); 104 } 105 str.append(DumpPipelineInfo(*this->pipeline())); 106 str.append(INHERITED::dumpInfo()); 107 return str; 108 } 109 110 private: 111 NonAAFillRectOp() : INHERITED(ClassID()) {} 112 113 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color, 114 GrPipelineAnalysisCoverage* coverage) const override { 115 color->setToConstant(fRects[0].fColor); 116 *coverage = GrPipelineAnalysisCoverage::kNone; 117 } 118 119 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override { 120 optimizations.getOverrideColorIfSet(&fRects[0].fColor); 121 } 122 123 void onPrepareDraws(Target* target) const override { 124 sk_sp<GrGeometryProcessor> gp = make_gp(); 125 if (!gp) { 126 SkDebugf("Couldn't create GrGeometryProcessor\n"); 127 return; 128 } 129 SkASSERT(gp->getVertexStride() == 130 sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)); 131 132 size_t vertexStride = gp->getVertexStride(); 133 int instanceCount = fRects.count(); 134 135 sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer()); 136 InstancedHelper helper; 137 void* vertices = 138 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(), 139 kVertsPerInstance, kIndicesPerInstance, instanceCount); 140 if (!vertices || !indexBuffer) { 141 SkDebugf("Could not allocate vertices\n"); 142 return; 143 } 144 145 for (int i = 0; i < instanceCount; i++) { 146 intptr_t verts = 147 reinterpret_cast<intptr_t>(vertices) + i * kVertsPerInstance * vertexStride; 148 tesselate(verts, vertexStride, fRects[i].fColor, &fRects[i].fViewMatrix, 149 fRects[i].fRect, &fRects[i].fLocalQuad); 150 } 151 helper.recordDraw(target, gp.get()); 152 } 153 154 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 155 NonAAFillRectOp* that = t->cast<NonAAFillRectOp>(); 156 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), 157 that->bounds(), caps)) { 158 return false; 159 } 160 161 fRects.push_back_n(that->fRects.count(), that->fRects.begin()); 162 this->joinBounds(*that); 163 return true; 164 } 165 166 struct RectInfo { 167 GrColor fColor; 168 SkMatrix fViewMatrix; 169 SkRect fRect; 170 GrQuad fLocalQuad; 171 }; 172 173 SkSTArray<1, RectInfo, true> fRects; 174 175 typedef GrMeshDrawOp INHERITED; 176 }; 177 178 namespace GrNonAAFillRectOp { 179 180 std::unique_ptr<GrMeshDrawOp> Make(GrColor color, 181 const SkMatrix& viewMatrix, 182 const SkRect& rect, 183 const SkRect* localRect, 184 const SkMatrix* localMatrix) { 185 return std::unique_ptr<GrMeshDrawOp>( 186 new NonAAFillRectOp(color, viewMatrix, rect, localRect, localMatrix)); 187 } 188 }; 189 190 /////////////////////////////////////////////////////////////////////////////////////////////////// 191 192 #if GR_TEST_UTILS 193 194 #include "GrDrawOpTest.h" 195 196 DRAW_OP_TEST_DEFINE(NonAAFillRectOp) { 197 GrColor color = GrRandomColor(random); 198 SkRect rect = GrTest::TestRect(random); 199 SkRect localRect = GrTest::TestRect(random); 200 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); 201 SkMatrix localMatrix = GrTest::TestMatrix(random); 202 203 bool hasLocalRect = random->nextBool(); 204 bool hasLocalMatrix = random->nextBool(); 205 return GrNonAAFillRectOp::Make(color, 206 viewMatrix, 207 rect, 208 hasLocalRect ? &localRect : nullptr, 209 hasLocalMatrix ? &localMatrix : nullptr); 210 } 211 212 #endif 213