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 "GrDrawVerticesBatch.h" 9 10#include "GrBatchFlushState.h" 11#include "GrInvariantOutput.h" 12#include "GrDefaultGeoProcFactory.h" 13 14static const GrGeometryProcessor* set_vertex_attributes(bool hasLocalCoords, 15 int* colorOffset, 16 int* texOffset, 17 const SkMatrix& viewMatrix, 18 bool coverageIgnored) { 19 using namespace GrDefaultGeoProcFactory; 20 *texOffset = -1; 21 *colorOffset = -1; 22 23 Coverage coverage(coverageIgnored ? Coverage::kNone_Type : Coverage::kSolid_Type); 24 LocalCoords localCoords(hasLocalCoords ? LocalCoords::kHasExplicit_Type : 25 LocalCoords::kUsePosition_Type); 26 *colorOffset = sizeof(SkPoint); 27 if (hasLocalCoords) { 28 *texOffset = sizeof(SkPoint) + sizeof(GrColor); 29 } 30 return GrDefaultGeoProcFactory::Create(Color(Color::kAttribute_Type), 31 coverage, localCoords, viewMatrix); 32} 33 34GrDrawVerticesBatch::GrDrawVerticesBatch(const Geometry& geometry, GrPrimitiveType primitiveType, 35 const SkMatrix& viewMatrix, 36 const SkPoint* positions, int vertexCount, 37 const uint16_t* indices, int indexCount, 38 const GrColor* colors, const SkPoint* localCoords, 39 const SkRect& bounds) 40 : INHERITED(ClassID()) { 41 SkASSERT(positions); 42 43 fViewMatrix = viewMatrix; 44 Geometry& installedGeo = fGeoData.push_back(geometry); 45 46 installedGeo.fPositions.append(vertexCount, positions); 47 if (indices) { 48 installedGeo.fIndices.append(indexCount, indices); 49 } 50 51 if (colors) { 52 fVariableColor = true; 53 installedGeo.fColors.append(vertexCount, colors); 54 } else { 55 fVariableColor = false; 56 } 57 58 if (localCoords) { 59 installedGeo.fLocalCoords.append(vertexCount, localCoords); 60 } 61 fVertexCount = vertexCount; 62 fIndexCount = indexCount; 63 fPrimitiveType = primitiveType; 64 65 this->setBounds(bounds); 66} 67 68void GrDrawVerticesBatch::computePipelineOptimizations(GrInitInvariantOutput* color, 69 GrInitInvariantOutput* coverage, 70 GrBatchToXPOverrides* overrides) const { 71 // When this is called on a batch, there is only one geometry bundle 72 if (fVariableColor) { 73 color->setUnknownFourComponents(); 74 } else { 75 color->setKnownFourComponents(fGeoData[0].fColor); 76 } 77 coverage->setKnownSingleComponent(0xff); 78} 79 80void GrDrawVerticesBatch::initBatchTracker(const GrXPOverridesForBatch& overrides) { 81 SkASSERT(fGeoData.count() == 1); 82 GrColor overrideColor; 83 if (overrides.getOverrideColorIfSet(&overrideColor)) { 84 fGeoData[0].fColor = overrideColor; 85 fGeoData[0].fColors.reset(); 86 fVariableColor = false; 87 } 88 fCoverageIgnored = !overrides.readsCoverage(); 89 if (!overrides.readsLocalCoords()) { 90 fGeoData[0].fLocalCoords.reset(); 91 } 92} 93 94void GrDrawVerticesBatch::onPrepareDraws(Target* target) const { 95 bool hasLocalCoords = !fGeoData[0].fLocalCoords.isEmpty(); 96 int colorOffset = -1, texOffset = -1; 97 SkAutoTUnref<const GrGeometryProcessor> gp( 98 set_vertex_attributes(hasLocalCoords, &colorOffset, &texOffset, fViewMatrix, 99 fCoverageIgnored)); 100 target->initDraw(gp, this->pipeline()); 101 102 size_t vertexStride = gp->getVertexStride(); 103 104 SkASSERT(vertexStride == sizeof(SkPoint) + (hasLocalCoords ? sizeof(SkPoint) : 0) 105 + sizeof(GrColor)); 106 107 int instanceCount = fGeoData.count(); 108 109 const GrVertexBuffer* vertexBuffer; 110 int firstVertex; 111 112 void* verts = target->makeVertexSpace(vertexStride, fVertexCount, &vertexBuffer, &firstVertex); 113 114 if (!verts) { 115 SkDebugf("Could not allocate vertices\n"); 116 return; 117 } 118 119 const GrIndexBuffer* indexBuffer = nullptr; 120 int firstIndex = 0; 121 122 uint16_t* indices = nullptr; 123 if (!fGeoData[0].fIndices.isEmpty()) { 124 indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); 125 126 if (!indices) { 127 SkDebugf("Could not allocate indices\n"); 128 return; 129 } 130 } 131 132 int indexOffset = 0; 133 int vertexOffset = 0; 134 for (int i = 0; i < instanceCount; i++) { 135 const Geometry& args = fGeoData[i]; 136 137 // TODO we can actually cache this interleaved and then just memcopy 138 if (indices) { 139 for (int j = 0; j < args.fIndices.count(); ++j, ++indexOffset) { 140 *(indices + indexOffset) = args.fIndices[j] + vertexOffset; 141 } 142 } 143 144 for (int j = 0; j < args.fPositions.count(); ++j) { 145 *((SkPoint*)verts) = args.fPositions[j]; 146 if (args.fColors.isEmpty()) { 147 *(GrColor*)((intptr_t)verts + colorOffset) = args.fColor; 148 } else { 149 *(GrColor*)((intptr_t)verts + colorOffset) = args.fColors[j]; 150 } 151 if (hasLocalCoords) { 152 *(SkPoint*)((intptr_t)verts + texOffset) = args.fLocalCoords[j]; 153 } 154 verts = (void*)((intptr_t)verts + vertexStride); 155 vertexOffset++; 156 } 157 } 158 159 GrVertices vertices; 160 if (indices) { 161 vertices.initIndexed(this->primitiveType(), vertexBuffer, indexBuffer, firstVertex, 162 firstIndex, fVertexCount, fIndexCount); 163 164 } else { 165 vertices.init(this->primitiveType(), vertexBuffer, firstVertex, fVertexCount); 166 } 167 target->draw(vertices); 168} 169 170bool GrDrawVerticesBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) { 171 GrDrawVerticesBatch* that = t->cast<GrDrawVerticesBatch>(); 172 173 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), 174 that->bounds(), caps)) { 175 return false; 176 } 177 178 if (!this->batchablePrimitiveType() || this->primitiveType() != that->primitiveType()) { 179 return false; 180 } 181 182 // We currently use a uniform viewmatrix for this batch 183 if (!fViewMatrix.cheapEqualTo(that->fViewMatrix)) { 184 return false; 185 } 186 187 if (fGeoData[0].fIndices.isEmpty() != that->fGeoData[0].fIndices.isEmpty()) { 188 return false; 189 } 190 191 if (fGeoData[0].fLocalCoords.isEmpty() != that->fGeoData[0].fLocalCoords.isEmpty()) { 192 return false; 193 } 194 195 if (!fVariableColor) { 196 if (that->fVariableColor || that->fGeoData[0].fColor != fGeoData[0].fColor) { 197 fVariableColor = true; 198 } 199 } 200 201 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); 202 fVertexCount += that->fVertexCount; 203 fIndexCount += that->fIndexCount; 204 205 this->joinBounds(that->bounds()); 206 return true; 207} 208 209/////////////////////////////////////////////////////////////////////////////////////////////////// 210 211#ifdef GR_TEST_UTILS 212 213#include "GrBatchTest.h" 214 215static uint32_t seed_vertices(GrPrimitiveType type) { 216 switch (type) { 217 case kTriangles_GrPrimitiveType: 218 case kTriangleStrip_GrPrimitiveType: 219 case kTriangleFan_GrPrimitiveType: 220 return 3; 221 case kPoints_GrPrimitiveType: 222 return 1; 223 case kLines_GrPrimitiveType: 224 case kLineStrip_GrPrimitiveType: 225 return 2; 226 } 227 SkFAIL("Incomplete switch\n"); 228 return 0; 229} 230 231static uint32_t primitive_vertices(GrPrimitiveType type) { 232 switch (type) { 233 case kTriangles_GrPrimitiveType: 234 return 3; 235 case kLines_GrPrimitiveType: 236 return 2; 237 case kTriangleStrip_GrPrimitiveType: 238 case kTriangleFan_GrPrimitiveType: 239 case kPoints_GrPrimitiveType: 240 case kLineStrip_GrPrimitiveType: 241 return 1; 242 } 243 SkFAIL("Incomplete switch\n"); 244 return 0; 245} 246 247static SkPoint random_point(SkRandom* random, SkScalar min, SkScalar max) { 248 SkPoint p; 249 p.fX = random->nextRangeScalar(min, max); 250 p.fY = random->nextRangeScalar(min, max); 251 return p; 252} 253 254static void randomize_params(size_t count, size_t maxVertex, SkScalar min, SkScalar max, 255 SkRandom* random, 256 SkTArray<SkPoint>* positions, 257 SkTArray<SkPoint>* texCoords, bool hasTexCoords, 258 SkTArray<GrColor>* colors, bool hasColors, 259 SkTArray<uint16_t>* indices, bool hasIndices) { 260 for (uint32_t v = 0; v < count; v++) { 261 positions->push_back(random_point(random, min, max)); 262 if (hasTexCoords) { 263 texCoords->push_back(random_point(random, min, max)); 264 } 265 if (hasColors) { 266 colors->push_back(GrRandomColor(random)); 267 } 268 if (hasIndices) { 269 SkASSERT(maxVertex <= SK_MaxU16); 270 indices->push_back(random->nextULessThan((uint16_t)maxVertex)); 271 } 272 } 273} 274 275DRAW_BATCH_TEST_DEFINE(VerticesBatch) { 276 GrPrimitiveType type = GrPrimitiveType(random->nextULessThan(kLast_GrPrimitiveType + 1)); 277 uint32_t primitiveCount = random->nextRangeU(1, 100); 278 279 // TODO make 'sensible' indexbuffers 280 SkTArray<SkPoint> positions; 281 SkTArray<SkPoint> texCoords; 282 SkTArray<GrColor> colors; 283 SkTArray<uint16_t> indices; 284 285 bool hasTexCoords = random->nextBool(); 286 bool hasIndices = random->nextBool(); 287 bool hasColors = random->nextBool(); 288 289 uint32_t vertexCount = seed_vertices(type) + (primitiveCount - 1) * primitive_vertices(type); 290 291 static const SkScalar kMinVertExtent = -100.f; 292 static const SkScalar kMaxVertExtent = 100.f; 293 randomize_params(seed_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent, 294 random, 295 &positions, 296 &texCoords, hasTexCoords, 297 &colors, hasColors, 298 &indices, hasIndices); 299 300 for (uint32_t i = 1; i < primitiveCount; i++) { 301 randomize_params(primitive_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent, 302 random, 303 &positions, 304 &texCoords, hasTexCoords, 305 &colors, hasColors, 306 &indices, hasIndices); 307 } 308 309 SkMatrix viewMatrix = GrTest::TestMatrix(random); 310 SkRect bounds; 311 SkDEBUGCODE(bool result = ) bounds.setBoundsCheck(positions.begin(), vertexCount); 312 SkASSERT(result); 313 314 viewMatrix.mapRect(&bounds); 315 316 GrDrawVerticesBatch::Geometry geometry; 317 geometry.fColor = GrRandomColor(random); 318 return GrDrawVerticesBatch::Create(geometry, type, viewMatrix, 319 positions.begin(), vertexCount, 320 indices.begin(), hasIndices ? vertexCount : 0, 321 colors.begin(), 322 texCoords.begin(), 323 bounds); 324} 325 326#endif 327