joshualitt | 9c80b5f | 2015-08-13 10:05:51 -0700 | [diff] [blame] | 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 "GrBWFillRectBatch.h" |
| 9 | |
| 10 | #include "GrBatch.h" |
| 11 | #include "GrBatchTarget.h" |
| 12 | #include "GrColor.h" |
| 13 | #include "GrDefaultGeoProcFactory.h" |
| 14 | #include "GrPrimitiveProcessor.h" |
| 15 | |
| 16 | class GrBatchTarget; |
| 17 | class SkMatrix; |
| 18 | struct SkRect; |
| 19 | |
bsalomon | abd30f5 | 2015-08-13 13:34:48 -0700 | [diff] [blame^] | 20 | class BWFillRectBatch : public GrVertexBatch { |
joshualitt | 9c80b5f | 2015-08-13 10:05:51 -0700 | [diff] [blame] | 21 | public: |
| 22 | struct Geometry { |
| 23 | SkMatrix fViewMatrix; |
| 24 | SkRect fRect; |
| 25 | SkRect fLocalRect; |
| 26 | SkMatrix fLocalMatrix; |
| 27 | GrColor fColor; |
| 28 | bool fHasLocalRect; |
| 29 | bool fHasLocalMatrix; |
| 30 | }; |
| 31 | |
bsalomon | abd30f5 | 2015-08-13 13:34:48 -0700 | [diff] [blame^] | 32 | static GrDrawBatch* Create(const Geometry& geometry) { |
joshualitt | 9c80b5f | 2015-08-13 10:05:51 -0700 | [diff] [blame] | 33 | return SkNEW_ARGS(BWFillRectBatch, (geometry)); |
| 34 | } |
| 35 | |
| 36 | const char* name() const override { return "RectBatch"; } |
| 37 | |
| 38 | void getInvariantOutputColor(GrInitInvariantOutput* out) const override { |
| 39 | // When this is called on a batch, there is only one geometry bundle |
| 40 | out->setKnownFourComponents(fGeoData[0].fColor); |
| 41 | } |
| 42 | |
| 43 | void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { |
| 44 | out->setKnownSingleComponent(0xff); |
| 45 | } |
| 46 | |
| 47 | void initBatchTracker(const GrPipelineOptimizations& init) override { |
| 48 | // Handle any color overrides |
| 49 | if (!init.readsColor()) { |
| 50 | fGeoData[0].fColor = GrColor_ILLEGAL; |
| 51 | } |
| 52 | init.getOverrideColorIfSet(&fGeoData[0].fColor); |
| 53 | |
| 54 | // setup batch properties |
| 55 | fBatch.fColorIgnored = !init.readsColor(); |
| 56 | fBatch.fColor = fGeoData[0].fColor; |
| 57 | fBatch.fUsesLocalCoords = init.readsLocalCoords(); |
| 58 | fBatch.fCoverageIgnored = !init.readsCoverage(); |
| 59 | } |
| 60 | |
| 61 | void generateGeometry(GrBatchTarget* batchTarget) override { |
| 62 | SkAutoTUnref<const GrGeometryProcessor> gp(this->createRectGP()); |
| 63 | if (!gp) { |
| 64 | SkDebugf("Could not create GrGeometryProcessor\n"); |
| 65 | return; |
| 66 | } |
| 67 | |
| 68 | batchTarget->initDraw(gp, this->pipeline()); |
| 69 | |
| 70 | int instanceCount = fGeoData.count(); |
| 71 | size_t vertexStride = gp->getVertexStride(); |
| 72 | SkASSERT(this->hasLocalRect() ? |
| 73 | vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr) : |
| 74 | vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)); |
| 75 | QuadHelper helper; |
| 76 | void* vertices = helper.init(batchTarget, vertexStride, instanceCount); |
| 77 | |
| 78 | if (!vertices) { |
| 79 | return; |
| 80 | } |
| 81 | |
| 82 | for (int i = 0; i < instanceCount; i++) { |
| 83 | const Geometry& geom = fGeoData[i]; |
| 84 | |
| 85 | intptr_t offset = reinterpret_cast<intptr_t>(vertices) + |
| 86 | kVerticesPerQuad * i * vertexStride; |
| 87 | SkPoint* positions = reinterpret_cast<SkPoint*>(offset); |
| 88 | |
| 89 | positions->setRectFan(geom.fRect.fLeft, geom.fRect.fTop, |
| 90 | geom.fRect.fRight, geom.fRect.fBottom, vertexStride); |
| 91 | geom.fViewMatrix.mapPointsWithStride(positions, vertexStride, kVerticesPerQuad); |
| 92 | |
| 93 | // TODO we should only do this if local coords are being read |
| 94 | if (geom.fHasLocalRect) { |
| 95 | static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor); |
| 96 | SkPoint* coords = reinterpret_cast<SkPoint*>(offset + kLocalOffset); |
| 97 | coords->setRectFan(geom.fLocalRect.fLeft, geom.fLocalRect.fTop, |
| 98 | geom.fLocalRect.fRight, geom.fLocalRect.fBottom, |
| 99 | vertexStride); |
| 100 | if (geom.fHasLocalMatrix) { |
| 101 | geom.fLocalMatrix.mapPointsWithStride(coords, vertexStride, kVerticesPerQuad); |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | static const int kColorOffset = sizeof(SkPoint); |
| 106 | GrColor* vertColor = reinterpret_cast<GrColor*>(offset + kColorOffset); |
| 107 | for (int j = 0; j < 4; ++j) { |
| 108 | *vertColor = geom.fColor; |
| 109 | vertColor = (GrColor*) ((intptr_t) vertColor + vertexStride); |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | helper.issueDraw(batchTarget); |
| 114 | } |
| 115 | |
| 116 | SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } |
| 117 | |
| 118 | private: |
| 119 | BWFillRectBatch(const Geometry& geometry) { |
| 120 | this->initClassID<BWFillRectBatch>(); |
| 121 | fGeoData.push_back(geometry); |
| 122 | |
| 123 | fBounds = geometry.fRect; |
| 124 | geometry.fViewMatrix.mapRect(&fBounds); |
| 125 | } |
| 126 | |
| 127 | GrColor color() const { return fBatch.fColor; } |
| 128 | bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } |
| 129 | bool colorIgnored() const { return fBatch.fColorIgnored; } |
| 130 | const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } |
| 131 | const SkMatrix& localMatrix() const { return fGeoData[0].fLocalMatrix; } |
| 132 | bool hasLocalRect() const { return fGeoData[0].fHasLocalRect; } |
| 133 | bool hasLocalMatrix() const { return fGeoData[0].fHasLocalMatrix; } |
| 134 | bool coverageIgnored() const { return fBatch.fCoverageIgnored; } |
| 135 | |
| 136 | bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override { |
bsalomon | abd30f5 | 2015-08-13 13:34:48 -0700 | [diff] [blame^] | 137 | BWFillRectBatch* that = t->cast<BWFillRectBatch>(); |
| 138 | if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), |
| 139 | that->bounds(), caps)) { |
joshualitt | 9c80b5f | 2015-08-13 10:05:51 -0700 | [diff] [blame] | 140 | return false; |
| 141 | } |
| 142 | |
joshualitt | 9c80b5f | 2015-08-13 10:05:51 -0700 | [diff] [blame] | 143 | if (this->hasLocalRect() != that->hasLocalRect()) { |
| 144 | return false; |
| 145 | } |
| 146 | |
| 147 | SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); |
| 148 | if (!this->hasLocalRect() && this->usesLocalCoords()) { |
| 149 | if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { |
| 150 | return false; |
| 151 | } |
| 152 | |
| 153 | if (this->hasLocalMatrix() != that->hasLocalMatrix()) { |
| 154 | return false; |
| 155 | } |
| 156 | |
| 157 | if (this->hasLocalMatrix() && !this->localMatrix().cheapEqualTo(that->localMatrix())) { |
| 158 | return false; |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | if (this->color() != that->color()) { |
| 163 | fBatch.fColor = GrColor_ILLEGAL; |
| 164 | } |
| 165 | fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); |
| 166 | this->joinBounds(that->bounds()); |
| 167 | return true; |
| 168 | } |
| 169 | |
| 170 | |
| 171 | /** We always use per-vertex colors so that rects can be batched across color changes. Sometimes |
| 172 | we have explicit local coords and sometimes not. We *could* always provide explicit local |
| 173 | coords and just duplicate the positions when the caller hasn't provided a local coord rect, |
| 174 | but we haven't seen a use case which frequently switches between local rect and no local |
| 175 | rect draws. |
| 176 | |
| 177 | The color param is used to determine whether the opaque hint can be set on the draw state. |
| 178 | The caller must populate the vertex colors itself. |
| 179 | |
| 180 | The vertex attrib order is always pos, color, [local coords]. |
| 181 | */ |
| 182 | const GrGeometryProcessor* createRectGP() const { |
| 183 | using namespace GrDefaultGeoProcFactory; |
| 184 | Color color(Color::kAttribute_Type); |
| 185 | Coverage coverage(this->coverageIgnored() ? Coverage::kNone_Type : Coverage::kSolid_Type); |
| 186 | |
| 187 | // if we have a local rect, then we apply the localMatrix directly to the localRect to |
| 188 | // generate vertex local coords |
| 189 | if (this->hasLocalRect()) { |
| 190 | LocalCoords localCoords(LocalCoords::kHasExplicit_Type); |
| 191 | return GrDefaultGeoProcFactory::Create(color, coverage, localCoords, SkMatrix::I()); |
| 192 | } else { |
| 193 | LocalCoords localCoords(LocalCoords::kUsePosition_Type, |
| 194 | this->hasLocalMatrix() ? &this->localMatrix() : NULL); |
| 195 | return GrDefaultGeoProcFactory::CreateForDeviceSpace(color, coverage, localCoords, |
| 196 | this->viewMatrix()); |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | struct BatchTracker { |
| 201 | GrColor fColor; |
| 202 | bool fUsesLocalCoords; |
| 203 | bool fColorIgnored; |
| 204 | bool fCoverageIgnored; |
| 205 | }; |
| 206 | |
| 207 | BatchTracker fBatch; |
| 208 | SkSTArray<1, Geometry, true> fGeoData; |
| 209 | }; |
| 210 | |
| 211 | namespace GrBWFillRectBatch { |
bsalomon | abd30f5 | 2015-08-13 13:34:48 -0700 | [diff] [blame^] | 212 | GrDrawBatch* Create(GrColor color, |
| 213 | const SkMatrix& viewMatrix, |
| 214 | const SkRect& rect, |
| 215 | const SkRect* localRect, |
| 216 | const SkMatrix* localMatrix) { |
joshualitt | 9c80b5f | 2015-08-13 10:05:51 -0700 | [diff] [blame] | 217 | BWFillRectBatch::Geometry geometry; |
| 218 | geometry.fColor = color; |
| 219 | geometry.fViewMatrix = viewMatrix; |
| 220 | geometry.fRect = rect; |
| 221 | |
| 222 | if (localRect) { |
| 223 | geometry.fHasLocalRect = true; |
| 224 | geometry.fLocalRect = *localRect; |
| 225 | } else { |
| 226 | geometry.fHasLocalRect = false; |
| 227 | } |
| 228 | |
| 229 | if (localMatrix) { |
| 230 | geometry.fHasLocalMatrix = true; |
| 231 | geometry.fLocalMatrix = *localMatrix; |
| 232 | } else { |
| 233 | geometry.fHasLocalMatrix = false; |
| 234 | } |
| 235 | |
| 236 | return BWFillRectBatch::Create(geometry); |
| 237 | } |
| 238 | }; |
| 239 | |
| 240 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
| 241 | |
| 242 | #ifdef GR_TEST_UTILS |
| 243 | |
| 244 | #include "GrBatchTest.h" |
| 245 | |
bsalomon | abd30f5 | 2015-08-13 13:34:48 -0700 | [diff] [blame^] | 246 | DRAW_BATCH_TEST_DEFINE(RectBatch) { |
joshualitt | 9c80b5f | 2015-08-13 10:05:51 -0700 | [diff] [blame] | 247 | BWFillRectBatch::Geometry geometry; |
| 248 | geometry.fColor = GrRandomColor(random); |
| 249 | |
| 250 | geometry.fRect = GrTest::TestRect(random); |
| 251 | geometry.fHasLocalRect = random->nextBool(); |
| 252 | |
| 253 | if (geometry.fHasLocalRect) { |
| 254 | geometry.fViewMatrix = GrTest::TestMatrixInvertible(random); |
| 255 | geometry.fLocalRect = GrTest::TestRect(random); |
| 256 | } else { |
| 257 | geometry.fViewMatrix = GrTest::TestMatrix(random); |
| 258 | } |
| 259 | |
| 260 | geometry.fHasLocalMatrix = random->nextBool(); |
| 261 | if (geometry.fHasLocalMatrix) { |
| 262 | geometry.fLocalMatrix = GrTest::TestMatrix(random); |
| 263 | } |
| 264 | |
| 265 | return BWFillRectBatch::Create(geometry); |
| 266 | } |
| 267 | |
| 268 | #endif |