Home | History | Annotate | Download | only in ops
      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 "GrDrawVerticesOp.h"
      9 #include "GrDefaultGeoProcFactory.h"
     10 #include "GrOpFlushState.h"
     11 #include "SkGr.h"
     12 
     13 std::unique_ptr<GrDrawOp> GrDrawVerticesOp::Make(GrPaint&& paint,
     14                                                  sk_sp<SkVertices> vertices,
     15                                                  const SkMatrix& viewMatrix,
     16                                                  GrAAType aaType,
     17                                                  bool gammaCorrect,
     18                                                  sk_sp<GrColorSpaceXform> colorSpaceXform,
     19                                                  GrPrimitiveType* overridePrimType) {
     20     SkASSERT(vertices);
     21     GrPrimitiveType primType = overridePrimType ? *overridePrimType
     22                                                 : SkVertexModeToGrPrimitiveType(vertices->mode());
     23     return Helper::FactoryHelper<GrDrawVerticesOp>(std::move(paint), std::move(vertices), primType,
     24                                                    aaType, gammaCorrect, std::move(colorSpaceXform),
     25                                                    viewMatrix);
     26 }
     27 
     28 GrDrawVerticesOp::GrDrawVerticesOp(const Helper::MakeArgs& helperArgs, GrColor color,
     29                                    sk_sp<SkVertices> vertices, GrPrimitiveType primitiveType,
     30                                    GrAAType aaType, bool gammaCorrect,
     31                                    sk_sp<GrColorSpaceXform> colorSpaceXform,
     32                                    const SkMatrix& viewMatrix)
     33         : INHERITED(ClassID())
     34         , fHelper(helperArgs, aaType)
     35         , fPrimitiveType(primitiveType)
     36         , fColorSpaceXform(std::move(colorSpaceXform)) {
     37     SkASSERT(vertices);
     38 
     39     fVertexCount = vertices->vertexCount();
     40     fIndexCount = vertices->indexCount();
     41     fColorArrayType = vertices->hasColors() ? ColorArrayType::kSkColor
     42                                             : ColorArrayType::kPremulGrColor;
     43     // GrColor is linearized (and gamut converted) during paint conversion, but SkColors need to be
     44     // handled in the shader
     45     fLinearizeColors = gammaCorrect && vertices->hasColors();
     46 
     47     Mesh& mesh = fMeshes.push_back();
     48     mesh.fColor = color;
     49     mesh.fViewMatrix = viewMatrix;
     50     mesh.fVertices = std::move(vertices);
     51     mesh.fIgnoreTexCoords = false;
     52     mesh.fIgnoreColors = false;
     53 
     54     fFlags = 0;
     55     if (mesh.hasPerVertexColors()) {
     56         fFlags |= kRequiresPerVertexColors_Flag;
     57     }
     58     if (mesh.hasExplicitLocalCoords()) {
     59         fFlags |= kAnyMeshHasExplicitLocalCoords;
     60     }
     61 
     62     IsZeroArea zeroArea;
     63     if (GrIsPrimTypeLines(primitiveType) || GrPrimitiveType::kPoints == primitiveType) {
     64         zeroArea = IsZeroArea::kYes;
     65     } else {
     66         zeroArea = IsZeroArea::kNo;
     67     }
     68     this->setTransformedBounds(mesh.fVertices->bounds(), viewMatrix, HasAABloat::kNo, zeroArea);
     69 }
     70 
     71 SkString GrDrawVerticesOp::dumpInfo() const {
     72     SkString string;
     73     string.appendf("PrimType: %d, MeshCount %d, VCount: %d, ICount: %d\n", (int)fPrimitiveType,
     74                    fMeshes.count(), fVertexCount, fIndexCount);
     75     string += fHelper.dumpInfo();
     76     string += INHERITED::dumpInfo();
     77     return string;
     78 }
     79 
     80 GrDrawOp::FixedFunctionFlags GrDrawVerticesOp::fixedFunctionFlags() const {
     81     return fHelper.fixedFunctionFlags();
     82 }
     83 
     84 GrDrawOp::RequiresDstTexture GrDrawVerticesOp::finalize(const GrCaps& caps,
     85                                                         const GrAppliedClip* clip,
     86                                                         GrPixelConfigIsClamped dstIsClamped) {
     87     GrProcessorAnalysisColor gpColor;
     88     if (this->requiresPerVertexColors()) {
     89         gpColor.setToUnknown();
     90     } else {
     91         gpColor.setToConstant(fMeshes.front().fColor);
     92     }
     93     auto result = fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
     94                                                GrProcessorAnalysisCoverage::kNone, &gpColor);
     95     if (gpColor.isConstant(&fMeshes.front().fColor)) {
     96         fMeshes.front().fIgnoreColors = true;
     97         fFlags &= ~kRequiresPerVertexColors_Flag;
     98         fColorArrayType = ColorArrayType::kPremulGrColor;
     99         fLinearizeColors = false;
    100     }
    101     if (!fHelper.usesLocalCoords()) {
    102         fMeshes[0].fIgnoreTexCoords = true;
    103         fFlags &= ~kAnyMeshHasExplicitLocalCoords;
    104     }
    105     return result;
    106 }
    107 
    108 sk_sp<GrGeometryProcessor> GrDrawVerticesOp::makeGP(bool* hasColorAttribute,
    109                                                     bool* hasLocalCoordAttribute) const {
    110     using namespace GrDefaultGeoProcFactory;
    111     LocalCoords::Type localCoordsType;
    112     if (fHelper.usesLocalCoords()) {
    113         // If we have multiple view matrices we will transform the positions into device space. We
    114         // must then also provide untransformed positions as local coords.
    115         if (this->anyMeshHasExplicitLocalCoords() || this->hasMultipleViewMatrices()) {
    116             *hasLocalCoordAttribute = true;
    117             localCoordsType = LocalCoords::kHasExplicit_Type;
    118         } else {
    119             *hasLocalCoordAttribute = false;
    120             localCoordsType = LocalCoords::kUsePosition_Type;
    121         }
    122     } else {
    123         localCoordsType = LocalCoords::kUnused_Type;
    124         *hasLocalCoordAttribute = false;
    125     }
    126 
    127     Color color(fMeshes[0].fColor);
    128     if (this->requiresPerVertexColors()) {
    129         color.fType = (fColorArrayType == ColorArrayType::kPremulGrColor)
    130                               ? Color::kPremulGrColorAttribute_Type
    131                               : Color::kUnpremulSkColorAttribute_Type;
    132         color.fLinearize = fLinearizeColors;
    133         color.fColorSpaceXform = fColorSpaceXform;
    134         *hasColorAttribute = true;
    135     } else {
    136         *hasColorAttribute = false;
    137     };
    138     const SkMatrix& vm = this->hasMultipleViewMatrices() ? SkMatrix::I() : fMeshes[0].fViewMatrix;
    139     return GrDefaultGeoProcFactory::Make(color, Coverage::kSolid_Type, localCoordsType, vm);
    140 }
    141 
    142 void GrDrawVerticesOp::onPrepareDraws(Target* target) {
    143     bool hasColorAttribute;
    144     bool hasLocalCoordsAttribute;
    145     sk_sp<GrGeometryProcessor> gp = this->makeGP(&hasColorAttribute, &hasLocalCoordsAttribute);
    146     size_t vertexStride = gp->getVertexStride();
    147 
    148     SkASSERT(vertexStride == sizeof(SkPoint) + (hasColorAttribute ? sizeof(uint32_t) : 0) +
    149                                      (hasLocalCoordsAttribute ? sizeof(SkPoint) : 0));
    150 
    151     int instanceCount = fMeshes.count();
    152 
    153     const GrBuffer* vertexBuffer;
    154     int firstVertex;
    155 
    156     void* verts = target->makeVertexSpace(vertexStride, fVertexCount, &vertexBuffer, &firstVertex);
    157 
    158     if (!verts) {
    159         SkDebugf("Could not allocate vertices\n");
    160         return;
    161     }
    162 
    163     const GrBuffer* indexBuffer = nullptr;
    164     int firstIndex = 0;
    165 
    166     uint16_t* indices = nullptr;
    167     if (this->isIndexed()) {
    168         indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
    169 
    170         if (!indices) {
    171             SkDebugf("Could not allocate indices\n");
    172             return;
    173         }
    174     }
    175 
    176     int vertexOffset = 0;
    177     // We have a fast case below for uploading the vertex data when the matrix is translate
    178     // only and there are colors but not local coords.
    179     bool fastAttrs = hasColorAttribute && !hasLocalCoordsAttribute;
    180     for (int i = 0; i < instanceCount; i++) {
    181         const Mesh& mesh = fMeshes[i];
    182         if (indices) {
    183             int indexCount = mesh.fVertices->indexCount();
    184             for (int j = 0; j < indexCount; ++j) {
    185                 *indices++ = mesh.fVertices->indices()[j] + vertexOffset;
    186             }
    187         }
    188         int vertexCount = mesh.fVertices->vertexCount();
    189         const SkPoint* positions = mesh.fVertices->positions();
    190         const SkColor* colors = mesh.fVertices->colors();
    191         const SkPoint* localCoords = mesh.fVertices->texCoords();
    192         bool fastMesh = (!this->hasMultipleViewMatrices() ||
    193                          mesh.fViewMatrix.getType() <= SkMatrix::kTranslate_Mask) &&
    194                         mesh.hasPerVertexColors();
    195         if (fastAttrs && fastMesh) {
    196             struct V {
    197                 SkPoint fPos;
    198                 uint32_t fColor;
    199             };
    200             SkASSERT(sizeof(V) == vertexStride);
    201             V* v = (V*)verts;
    202             Sk2f t(0, 0);
    203             if (this->hasMultipleViewMatrices()) {
    204                 t = Sk2f(mesh.fViewMatrix.getTranslateX(), mesh.fViewMatrix.getTranslateY());
    205             }
    206             for (int j = 0; j < vertexCount; ++j) {
    207                 Sk2f p = Sk2f::Load(positions++) + t;
    208                 p.store(&v[j].fPos);
    209                 v[j].fColor = colors[j];
    210             }
    211             verts = v + vertexCount;
    212         } else {
    213             static constexpr size_t kColorOffset = sizeof(SkPoint);
    214             size_t localCoordOffset =
    215                     hasColorAttribute ? kColorOffset + sizeof(uint32_t) : kColorOffset;
    216 
    217             for (int j = 0; j < vertexCount; ++j) {
    218                 if (this->hasMultipleViewMatrices()) {
    219                     mesh.fViewMatrix.mapPoints(((SkPoint*)verts), &positions[j], 1);
    220                 } else {
    221                     *((SkPoint*)verts) = positions[j];
    222                 }
    223                 if (hasColorAttribute) {
    224                     if (mesh.hasPerVertexColors()) {
    225                         *(uint32_t*)((intptr_t)verts + kColorOffset) = colors[j];
    226                     } else {
    227                         *(uint32_t*)((intptr_t)verts + kColorOffset) = mesh.fColor;
    228                     }
    229                 }
    230                 if (hasLocalCoordsAttribute) {
    231                     if (mesh.hasExplicitLocalCoords()) {
    232                         *(SkPoint*)((intptr_t)verts + localCoordOffset) = localCoords[j];
    233                     } else {
    234                         *(SkPoint*)((intptr_t)verts + localCoordOffset) = positions[j];
    235                     }
    236                 }
    237                 verts = (void*)((intptr_t)verts + vertexStride);
    238             }
    239         }
    240         vertexOffset += vertexCount;
    241     }
    242 
    243     GrMesh mesh(this->primitiveType());
    244     if (!indices) {
    245         mesh.setNonIndexedNonInstanced(fVertexCount);
    246     } else {
    247         mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertexCount - 1);
    248     }
    249     mesh.setVertexData(vertexBuffer, firstVertex);
    250     target->draw(gp.get(), fHelper.makePipeline(target), mesh);
    251 }
    252 
    253 bool GrDrawVerticesOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
    254     GrDrawVerticesOp* that = t->cast<GrDrawVerticesOp>();
    255 
    256     if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
    257         return false;
    258     }
    259 
    260     if (!this->combinablePrimitive() || this->primitiveType() != that->primitiveType()) {
    261         return false;
    262     }
    263 
    264     if (fMeshes[0].fVertices->hasIndices() != that->fMeshes[0].fVertices->hasIndices()) {
    265         return false;
    266     }
    267 
    268     if (fColorArrayType != that->fColorArrayType) {
    269         return false;
    270     }
    271 
    272     if (fLinearizeColors != that->fLinearizeColors) {
    273         return false;
    274     }
    275 
    276     if (fVertexCount + that->fVertexCount > SK_MaxU16) {
    277         return false;
    278     }
    279 
    280     // NOTE: For SkColor vertex colors, the source color space is always sRGB, and the destination
    281     // gamut is determined by the render target context. A mis-match should be impossible.
    282     SkASSERT(GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get()));
    283 
    284     // If either op required explicit local coords or per-vertex colors the combined mesh does. Same
    285     // with multiple view matrices.
    286     fFlags |= that->fFlags;
    287 
    288     if (!this->requiresPerVertexColors() && this->fMeshes[0].fColor != that->fMeshes[0].fColor) {
    289         fFlags |= kRequiresPerVertexColors_Flag;
    290     }
    291     // Check whether we are about to acquire a mesh with a different view matrix.
    292     if (!this->hasMultipleViewMatrices() &&
    293         !this->fMeshes[0].fViewMatrix.cheapEqualTo(that->fMeshes[0].fViewMatrix)) {
    294         fFlags |= kHasMultipleViewMatrices_Flag;
    295     }
    296 
    297     fMeshes.push_back_n(that->fMeshes.count(), that->fMeshes.begin());
    298     fVertexCount += that->fVertexCount;
    299     fIndexCount += that->fIndexCount;
    300 
    301     this->joinBounds(*that);
    302     return true;
    303 }
    304 
    305 ///////////////////////////////////////////////////////////////////////////////////////////////////
    306 
    307 #if GR_TEST_UTILS
    308 
    309 #include "GrDrawOpTest.h"
    310 
    311 static uint32_t seed_vertices(GrPrimitiveType type) {
    312     switch (type) {
    313         case GrPrimitiveType::kTriangles:
    314         case GrPrimitiveType::kTriangleStrip:
    315         case GrPrimitiveType::kTriangleFan:
    316             return 3;
    317         case GrPrimitiveType::kPoints:
    318             return 1;
    319         case GrPrimitiveType::kLines:
    320         case GrPrimitiveType::kLineStrip:
    321             return 2;
    322         case GrPrimitiveType::kLinesAdjacency:
    323             return 4;
    324     }
    325     SK_ABORT("Incomplete switch\n");
    326     return 0;
    327 }
    328 
    329 static uint32_t primitive_vertices(GrPrimitiveType type) {
    330     switch (type) {
    331         case GrPrimitiveType::kTriangles:
    332             return 3;
    333         case GrPrimitiveType::kLines:
    334             return 2;
    335         case GrPrimitiveType::kTriangleStrip:
    336         case GrPrimitiveType::kTriangleFan:
    337         case GrPrimitiveType::kPoints:
    338         case GrPrimitiveType::kLineStrip:
    339             return 1;
    340         case GrPrimitiveType::kLinesAdjacency:
    341             return 4;
    342     }
    343     SK_ABORT("Incomplete switch\n");
    344     return 0;
    345 }
    346 
    347 static SkPoint random_point(SkRandom* random, SkScalar min, SkScalar max) {
    348     SkPoint p;
    349     p.fX = random->nextRangeScalar(min, max);
    350     p.fY = random->nextRangeScalar(min, max);
    351     return p;
    352 }
    353 
    354 static void randomize_params(size_t count, size_t maxVertex, SkScalar min, SkScalar max,
    355                              SkRandom* random, SkTArray<SkPoint>* positions,
    356                              SkTArray<SkPoint>* texCoords, bool hasTexCoords,
    357                              SkTArray<uint32_t>* colors, bool hasColors,
    358                              SkTArray<uint16_t>* indices, bool hasIndices) {
    359     for (uint32_t v = 0; v < count; v++) {
    360         positions->push_back(random_point(random, min, max));
    361         if (hasTexCoords) {
    362             texCoords->push_back(random_point(random, min, max));
    363         }
    364         if (hasColors) {
    365             colors->push_back(GrRandomColor(random));
    366         }
    367         if (hasIndices) {
    368             SkASSERT(maxVertex <= SK_MaxU16);
    369             indices->push_back(random->nextULessThan((uint16_t)maxVertex));
    370         }
    371     }
    372 }
    373 
    374 GR_DRAW_OP_TEST_DEFINE(GrDrawVerticesOp) {
    375     GrPrimitiveType type;
    376     do {
    377        type = GrPrimitiveType(random->nextULessThan(kNumGrPrimitiveTypes));
    378     } while (GrPrimTypeRequiresGeometryShaderSupport(type) &&
    379              !context->caps()->shaderCaps()->geometryShaderSupport());
    380 
    381     uint32_t primitiveCount = random->nextRangeU(1, 100);
    382 
    383     // TODO make 'sensible' indexbuffers
    384     SkTArray<SkPoint> positions;
    385     SkTArray<SkPoint> texCoords;
    386     SkTArray<uint32_t> colors;
    387     SkTArray<uint16_t> indices;
    388 
    389     bool hasTexCoords = random->nextBool();
    390     bool hasIndices = random->nextBool();
    391     bool hasColors = random->nextBool();
    392     bool linearizeColors = random->nextBool();
    393 
    394     uint32_t vertexCount = seed_vertices(type) + (primitiveCount - 1) * primitive_vertices(type);
    395 
    396     static const SkScalar kMinVertExtent = -100.f;
    397     static const SkScalar kMaxVertExtent = 100.f;
    398     randomize_params(seed_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent, random,
    399                      &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices,
    400                      hasIndices);
    401 
    402     for (uint32_t i = 1; i < primitiveCount; i++) {
    403         randomize_params(primitive_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent,
    404                          random, &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices,
    405                          hasIndices);
    406     }
    407 
    408     SkMatrix viewMatrix = GrTest::TestMatrix(random);
    409 
    410     sk_sp<GrColorSpaceXform> colorSpaceXform = GrTest::TestColorXform(random);
    411 
    412     static constexpr SkVertices::VertexMode kIgnoredMode = SkVertices::kTriangles_VertexMode;
    413     sk_sp<SkVertices> vertices = SkVertices::MakeCopy(kIgnoredMode, vertexCount, positions.begin(),
    414                                                       texCoords.begin(), colors.begin(),
    415                                                       hasIndices ? indices.count() : 0,
    416                                                       indices.begin());
    417     GrAAType aaType = GrAAType::kNone;
    418     if (GrFSAAType::kUnifiedMSAA == fsaaType && random->nextBool()) {
    419         aaType = GrAAType::kMSAA;
    420     }
    421     return GrDrawVerticesOp::Make(std::move(paint), std::move(vertices), viewMatrix, aaType,
    422                                   linearizeColors, std::move(colorSpaceXform), &type);
    423 }
    424 
    425 #endif
    426