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 "GrNonAAFillRectBatch.h" 9 10#include "GrBatchFlushState.h" 11#include "GrColor.h" 12#include "GrDefaultGeoProcFactory.h" 13#include "GrPrimitiveProcessor.h" 14#include "GrResourceProvider.h" 15#include "GrTInstanceBatch.h" 16#include "GrQuad.h" 17#include "GrVertexBatch.h" 18 19// Common functions 20class NonAAFillRectBatchBase { 21public: 22 static const int kVertsPerInstance = 4; 23 static const int kIndicesPerInstance = 6; 24 25 static void InitInvariantOutputCoverage(GrInitInvariantOutput* out) { 26 out->setKnownSingleComponent(0xff); 27 } 28 29 static const GrIndexBuffer* GetIndexBuffer(GrResourceProvider* rp) { 30 return rp->refQuadIndexBuffer(); 31 } 32 33 template <typename Geometry> 34 static void SetBounds(const Geometry& geo, SkRect* outBounds) { 35 geo.fViewMatrix.mapRect(outBounds, geo.fRect); 36 } 37 38 template <typename Geometry> 39 static void UpdateBoundsAfterAppend(const Geometry& geo, SkRect* outBounds) { 40 SkRect bounds = geo.fRect; 41 geo.fViewMatrix.mapRect(&bounds); 42 outBounds->join(bounds); 43 } 44}; 45 46/** We always use per-vertex colors so that rects can be batched across color changes. Sometimes 47 we have explicit local coords and sometimes not. We *could* always provide explicit local 48 coords and just duplicate the positions when the caller hasn't provided a local coord rect, 49 but we haven't seen a use case which frequently switches between local rect and no local 50 rect draws. 51 52 The vertex attrib order is always pos, color, [local coords]. 53 */ 54static const GrGeometryProcessor* create_gp(const SkMatrix& viewMatrix, 55 bool readsCoverage, 56 bool hasExplicitLocalCoords, 57 const SkMatrix* localMatrix) { 58 using namespace GrDefaultGeoProcFactory; 59 Color color(Color::kAttribute_Type); 60 Coverage coverage(readsCoverage ? Coverage::kSolid_Type : Coverage::kNone_Type); 61 62 // If we have perspective on the viewMatrix then we won't map on the CPU, nor will we map 63 // the local rect on the cpu (in case the localMatrix also has perspective). 64 // Otherwise, if we have a local rect, then we apply the localMatrix directly to the localRect 65 // to generate vertex local coords 66 if (viewMatrix.hasPerspective()) { 67 LocalCoords localCoords(hasExplicitLocalCoords ? LocalCoords::kHasExplicit_Type : 68 LocalCoords::kUsePosition_Type, 69 localMatrix); 70 return GrDefaultGeoProcFactory::Create(color, coverage, localCoords, viewMatrix); 71 } else if (hasExplicitLocalCoords) { 72 LocalCoords localCoords(LocalCoords::kHasExplicit_Type); 73 return GrDefaultGeoProcFactory::Create(color, coverage, localCoords, SkMatrix::I()); 74 } else { 75 LocalCoords localCoords(LocalCoords::kUsePosition_Type, localMatrix); 76 return GrDefaultGeoProcFactory::CreateForDeviceSpace(color, coverage, localCoords, 77 viewMatrix); 78 } 79} 80 81static void tesselate(intptr_t vertices, 82 size_t vertexStride, 83 GrColor color, 84 const SkMatrix& viewMatrix, 85 const SkRect& rect, 86 const GrQuad* localQuad) { 87 SkPoint* positions = reinterpret_cast<SkPoint*>(vertices); 88 89 positions->setRectFan(rect.fLeft, rect.fTop, 90 rect.fRight, rect.fBottom, vertexStride); 91 92 if (!viewMatrix.hasPerspective()) { 93 viewMatrix.mapPointsWithStride(positions, vertexStride, 94 NonAAFillRectBatchBase::kVertsPerInstance); 95 } 96 97 // Setup local coords 98 // TODO we should only do this if local coords are being read 99 if (localQuad) { 100 static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor); 101 for (int i = 0; i < NonAAFillRectBatchBase::kVertsPerInstance; i++) { 102 SkPoint* coords = reinterpret_cast<SkPoint*>(vertices + kLocalOffset + 103 i * vertexStride); 104 *coords = localQuad->point(i); 105 } 106 } 107 108 static const int kColorOffset = sizeof(SkPoint); 109 GrColor* vertColor = reinterpret_cast<GrColor*>(vertices + kColorOffset); 110 for (int j = 0; j < 4; ++j) { 111 *vertColor = color; 112 vertColor = (GrColor*) ((intptr_t) vertColor + vertexStride); 113 } 114} 115 116class NonAAFillRectBatchImp : public NonAAFillRectBatchBase { 117public: 118 struct Geometry { 119 SkMatrix fViewMatrix; 120 SkRect fRect; 121 GrQuad fLocalQuad; 122 GrColor fColor; 123 }; 124 125 static const char* Name() { return "NonAAFillRectBatch"; } 126 127 static SkString DumpInfo(const Geometry& geo, int index) { 128 SkString str; 129 str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", 130 index, 131 geo.fColor, 132 geo.fRect.fLeft, geo.fRect.fTop, geo.fRect.fRight, geo.fRect.fBottom); 133 return str; 134 } 135 136 static bool CanCombine(const Geometry& mine, const Geometry& theirs, 137 const GrXPOverridesForBatch& overrides) { 138 return true; 139 } 140 141 static const GrGeometryProcessor* CreateGP(const Geometry& geo, 142 const GrXPOverridesForBatch& overrides) { 143 const GrGeometryProcessor* gp = create_gp(geo.fViewMatrix, overrides.readsCoverage(), true, 144 nullptr); 145 146 SkASSERT(gp->getVertexStride() == 147 sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)); 148 return gp; 149 } 150 151 static void Tesselate(intptr_t vertices, size_t vertexStride, const Geometry& geo, 152 const GrXPOverridesForBatch& overrides) { 153 tesselate(vertices, vertexStride, geo.fColor, geo.fViewMatrix, geo.fRect, &geo.fLocalQuad); 154 } 155}; 156 157// We handle perspective in the local matrix or viewmatrix with special batches 158class NonAAFillRectBatchPerspectiveImp : public NonAAFillRectBatchBase { 159public: 160 struct Geometry { 161 SkMatrix fViewMatrix; 162 SkMatrix fLocalMatrix; 163 SkRect fRect; 164 SkRect fLocalRect; 165 GrColor fColor; 166 bool fHasLocalMatrix; 167 bool fHasLocalRect; 168 }; 169 170 static const char* Name() { return "NonAAFillRectBatchPerspective"; } 171 172 static SkString DumpInfo(const Geometry& geo, int index) { 173 SkString str; 174 str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", 175 index, 176 geo.fColor, 177 geo.fRect.fLeft, geo.fRect.fTop, geo.fRect.fRight, geo.fRect.fBottom); 178 return str; 179 } 180 181 static bool CanCombine(const Geometry& mine, const Geometry& theirs, 182 const GrXPOverridesForBatch& overrides) { 183 // We could batch across perspective vm changes if we really wanted to 184 return mine.fViewMatrix.cheapEqualTo(theirs.fViewMatrix) && 185 mine.fHasLocalRect == theirs.fHasLocalRect && 186 (!mine.fHasLocalMatrix || mine.fLocalMatrix.cheapEqualTo(theirs.fLocalMatrix)); 187 } 188 189 static const GrGeometryProcessor* CreateGP(const Geometry& geo, 190 const GrXPOverridesForBatch& overrides) { 191 const GrGeometryProcessor* gp = create_gp(geo.fViewMatrix, overrides.readsCoverage(), 192 geo.fHasLocalRect, 193 geo.fHasLocalMatrix ? &geo.fLocalMatrix : 194 nullptr); 195 196 SkASSERT(geo.fHasLocalRect ? 197 gp->getVertexStride() == sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr) : 198 gp->getVertexStride() == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)); 199 return gp; 200 } 201 202 static void Tesselate(intptr_t vertices, size_t vertexStride, const Geometry& geo, 203 const GrXPOverridesForBatch& overrides) { 204 if (geo.fHasLocalRect) { 205 GrQuad quad(geo.fLocalRect); 206 tesselate(vertices, vertexStride, geo.fColor, geo.fViewMatrix, geo.fRect, &quad); 207 } else { 208 tesselate(vertices, vertexStride, geo.fColor, geo.fViewMatrix, geo.fRect, nullptr); 209 } 210 } 211}; 212 213typedef GrTInstanceBatch<NonAAFillRectBatchImp> NonAAFillRectBatchSimple; 214typedef GrTInstanceBatch<NonAAFillRectBatchPerspectiveImp> NonAAFillRectBatchPerspective; 215 216inline static void append_to_batch(NonAAFillRectBatchSimple* batch, GrColor color, 217 const SkMatrix& viewMatrix, const SkRect& rect, 218 const SkRect* localRect, const SkMatrix* localMatrix) { 219 SkASSERT(!viewMatrix.hasPerspective() && (!localMatrix || !localMatrix->hasPerspective())); 220 NonAAFillRectBatchSimple::Geometry& geo = batch->geoData()->push_back(); 221 222 geo.fColor = color; 223 geo.fViewMatrix = viewMatrix; 224 geo.fRect = rect; 225 226 if (localRect && localMatrix) { 227 geo.fLocalQuad.setFromMappedRect(*localRect, *localMatrix); 228 } else if (localRect) { 229 geo.fLocalQuad.set(*localRect); 230 } else if (localMatrix) { 231 geo.fLocalQuad.setFromMappedRect(rect, *localMatrix); 232 } else { 233 geo.fLocalQuad.set(rect); 234 } 235} 236 237inline static void append_to_batch(NonAAFillRectBatchPerspective* batch, GrColor color, 238 const SkMatrix& viewMatrix, const SkRect& rect, 239 const SkRect* localRect, const SkMatrix* localMatrix) { 240 SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective())); 241 NonAAFillRectBatchPerspective::Geometry& geo = batch->geoData()->push_back(); 242 243 geo.fColor = color; 244 geo.fViewMatrix = viewMatrix; 245 geo.fRect = rect; 246 geo.fHasLocalRect = SkToBool(localRect); 247 geo.fHasLocalMatrix = SkToBool(localMatrix); 248 if (localMatrix) { 249 geo.fLocalMatrix = *localMatrix; 250 } 251 if (localRect) { 252 geo.fLocalRect = *localRect; 253 } 254 255} 256 257namespace GrNonAAFillRectBatch { 258 259GrDrawBatch* Create(GrColor color, 260 const SkMatrix& viewMatrix, 261 const SkRect& rect, 262 const SkRect* localRect, 263 const SkMatrix* localMatrix) { 264 NonAAFillRectBatchSimple* batch = NonAAFillRectBatchSimple::Create(); 265 append_to_batch(batch, color, viewMatrix, rect, localRect, localMatrix); 266 batch->init(); 267 return batch; 268} 269 270GrDrawBatch* CreateWithPerspective(GrColor color, 271 const SkMatrix& viewMatrix, 272 const SkRect& rect, 273 const SkRect* localRect, 274 const SkMatrix* localMatrix) { 275 NonAAFillRectBatchPerspective* batch = NonAAFillRectBatchPerspective::Create(); 276 append_to_batch(batch, color, viewMatrix, rect, localRect, localMatrix); 277 batch->init(); 278 return batch; 279} 280 281bool Append(GrBatch* origBatch, 282 GrColor color, 283 const SkMatrix& viewMatrix, 284 const SkRect& rect, 285 const SkRect* localRect, 286 const SkMatrix* localMatrix) { 287 bool usePerspective = viewMatrix.hasPerspective() || 288 (localMatrix && localMatrix->hasPerspective()); 289 290 if (usePerspective && origBatch->classID() != NonAAFillRectBatchPerspective::ClassID()) { 291 return false; 292 } 293 294 if (!usePerspective) { 295 NonAAFillRectBatchSimple* batch = origBatch->cast<NonAAFillRectBatchSimple>(); 296 append_to_batch(batch, color, viewMatrix, rect, localRect, localMatrix); 297 batch->updateBoundsAfterAppend(); 298 } else { 299 NonAAFillRectBatchPerspective* batch = origBatch->cast<NonAAFillRectBatchPerspective>(); 300 const NonAAFillRectBatchPerspective::Geometry& geo = batch->geoData()->back(); 301 302 if (!geo.fViewMatrix.cheapEqualTo(viewMatrix) || 303 geo.fHasLocalRect != SkToBool(localRect) || 304 geo.fHasLocalMatrix != SkToBool(localMatrix) || 305 (geo.fHasLocalMatrix && !geo.fLocalMatrix.cheapEqualTo(*localMatrix))) { 306 return false; 307 } 308 309 append_to_batch(batch, color, viewMatrix, rect, localRect, localMatrix); 310 batch->updateBoundsAfterAppend(); 311 } 312 313 return true; 314} 315 316}; 317 318/////////////////////////////////////////////////////////////////////////////////////////////////// 319 320#ifdef GR_TEST_UTILS 321 322#include "GrBatchTest.h" 323 324DRAW_BATCH_TEST_DEFINE(RectBatch) { 325 GrColor color = GrRandomColor(random); 326 SkRect rect = GrTest::TestRect(random); 327 SkRect localRect = GrTest::TestRect(random); 328 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); 329 SkMatrix localMatrix = GrTest::TestMatrix(random); 330 331 bool hasLocalRect = random->nextBool(); 332 bool hasLocalMatrix = random->nextBool(); 333 return GrNonAAFillRectBatch::Create(color, viewMatrix, rect, 334 hasLocalRect ? &localRect : nullptr, 335 hasLocalMatrix ? &localMatrix : nullptr); 336} 337 338#endif 339