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