| /* |
| * Copyright 2015 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "GrDrawVerticesOp.h" |
| |
| #include "GrDefaultGeoProcFactory.h" |
| #include "GrInvariantOutput.h" |
| #include "GrOpFlushState.h" |
| |
| static sk_sp<GrGeometryProcessor> set_vertex_attributes(bool hasLocalCoords, |
| int* colorOffset, |
| int* texOffset, |
| const SkMatrix& viewMatrix, |
| bool coverageIgnored) { |
| using namespace GrDefaultGeoProcFactory; |
| *texOffset = -1; |
| *colorOffset = -1; |
| |
| Coverage coverage(coverageIgnored ? Coverage::kNone_Type : Coverage::kSolid_Type); |
| LocalCoords localCoords(hasLocalCoords ? LocalCoords::kHasExplicit_Type |
| : LocalCoords::kUsePosition_Type); |
| *colorOffset = sizeof(SkPoint); |
| if (hasLocalCoords) { |
| *texOffset = sizeof(SkPoint) + sizeof(GrColor); |
| } |
| return GrDefaultGeoProcFactory::Make(Color(Color::kAttribute_Type), coverage, localCoords, |
| viewMatrix); |
| } |
| |
| GrDrawVerticesOp::GrDrawVerticesOp(GrColor color, GrPrimitiveType primitiveType, |
| const SkMatrix& viewMatrix, const SkPoint* positions, |
| int vertexCount, const uint16_t* indices, int indexCount, |
| const GrColor* colors, const SkPoint* localCoords, |
| const SkRect& bounds) |
| : INHERITED(ClassID()) { |
| SkASSERT(positions); |
| |
| fViewMatrix = viewMatrix; |
| Mesh& mesh = fMeshes.push_back(); |
| mesh.fColor = color; |
| |
| mesh.fPositions.append(vertexCount, positions); |
| if (indices) { |
| mesh.fIndices.append(indexCount, indices); |
| } |
| |
| if (colors) { |
| fVariableColor = true; |
| mesh.fColors.append(vertexCount, colors); |
| } else { |
| fVariableColor = false; |
| } |
| |
| if (localCoords) { |
| mesh.fLocalCoords.append(vertexCount, localCoords); |
| } |
| fVertexCount = vertexCount; |
| fIndexCount = indexCount; |
| fPrimitiveType = primitiveType; |
| |
| IsZeroArea zeroArea; |
| if (GrIsPrimTypeLines(primitiveType) || kPoints_GrPrimitiveType == primitiveType) { |
| zeroArea = IsZeroArea::kYes; |
| } else { |
| zeroArea = IsZeroArea::kNo; |
| } |
| this->setBounds(bounds, HasAABloat::kNo, zeroArea); |
| } |
| |
| void GrDrawVerticesOp::computePipelineOptimizations(GrInitInvariantOutput* color, |
| GrInitInvariantOutput* coverage, |
| GrBatchToXPOverrides* overrides) const { |
| // When this is called there is only one mesh. |
| if (fVariableColor) { |
| color->setUnknownFourComponents(); |
| } else { |
| color->setKnownFourComponents(fMeshes[0].fColor); |
| } |
| coverage->setKnownSingleComponent(0xff); |
| } |
| |
| void GrDrawVerticesOp::initBatchTracker(const GrXPOverridesForBatch& overrides) { |
| SkASSERT(fMeshes.count() == 1); |
| GrColor overrideColor; |
| if (overrides.getOverrideColorIfSet(&overrideColor)) { |
| fMeshes[0].fColor = overrideColor; |
| fMeshes[0].fColors.reset(); |
| fVariableColor = false; |
| } |
| fCoverageIgnored = !overrides.readsCoverage(); |
| if (!overrides.readsLocalCoords()) { |
| fMeshes[0].fLocalCoords.reset(); |
| } |
| } |
| |
| void GrDrawVerticesOp::onPrepareDraws(Target* target) const { |
| bool hasLocalCoords = !fMeshes[0].fLocalCoords.isEmpty(); |
| int colorOffset = -1, texOffset = -1; |
| sk_sp<GrGeometryProcessor> gp(set_vertex_attributes(hasLocalCoords, &colorOffset, &texOffset, |
| fViewMatrix, fCoverageIgnored)); |
| size_t vertexStride = gp->getVertexStride(); |
| |
| SkASSERT(vertexStride == |
| sizeof(SkPoint) + (hasLocalCoords ? sizeof(SkPoint) : 0) + sizeof(GrColor)); |
| |
| int instanceCount = fMeshes.count(); |
| |
| const GrBuffer* vertexBuffer; |
| int firstVertex; |
| |
| void* verts = target->makeVertexSpace(vertexStride, fVertexCount, &vertexBuffer, &firstVertex); |
| |
| if (!verts) { |
| SkDebugf("Could not allocate vertices\n"); |
| return; |
| } |
| |
| const GrBuffer* indexBuffer = nullptr; |
| int firstIndex = 0; |
| |
| uint16_t* indices = nullptr; |
| if (!fMeshes[0].fIndices.isEmpty()) { |
| indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); |
| |
| if (!indices) { |
| SkDebugf("Could not allocate indices\n"); |
| return; |
| } |
| } |
| |
| int indexOffset = 0; |
| int vertexOffset = 0; |
| for (int i = 0; i < instanceCount; i++) { |
| const Mesh& mesh = fMeshes[i]; |
| |
| // TODO we can actually cache this interleaved and then just memcopy |
| if (indices) { |
| for (int j = 0; j < mesh.fIndices.count(); ++j, ++indexOffset) { |
| *(indices + indexOffset) = mesh.fIndices[j] + vertexOffset; |
| } |
| } |
| |
| for (int j = 0; j < mesh.fPositions.count(); ++j) { |
| *((SkPoint*)verts) = mesh.fPositions[j]; |
| if (mesh.fColors.isEmpty()) { |
| *(GrColor*)((intptr_t)verts + colorOffset) = mesh.fColor; |
| } else { |
| *(GrColor*)((intptr_t)verts + colorOffset) = mesh.fColors[j]; |
| } |
| if (hasLocalCoords) { |
| *(SkPoint*)((intptr_t)verts + texOffset) = mesh.fLocalCoords[j]; |
| } |
| verts = (void*)((intptr_t)verts + vertexStride); |
| vertexOffset++; |
| } |
| } |
| |
| GrMesh mesh; |
| if (indices) { |
| mesh.initIndexed(this->primitiveType(), vertexBuffer, indexBuffer, firstVertex, firstIndex, |
| fVertexCount, fIndexCount); |
| |
| } else { |
| mesh.init(this->primitiveType(), vertexBuffer, firstVertex, fVertexCount); |
| } |
| target->draw(gp.get(), mesh); |
| } |
| |
| bool GrDrawVerticesOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) { |
| GrDrawVerticesOp* that = t->cast<GrDrawVerticesOp>(); |
| |
| if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), |
| that->bounds(), caps)) { |
| return false; |
| } |
| |
| if (!this->batchablePrimitiveType() || this->primitiveType() != that->primitiveType()) { |
| return false; |
| } |
| |
| // We currently use a uniform viewmatrix for this op. |
| if (!fViewMatrix.cheapEqualTo(that->fViewMatrix)) { |
| return false; |
| } |
| |
| if (fMeshes[0].fIndices.isEmpty() != that->fMeshes[0].fIndices.isEmpty()) { |
| return false; |
| } |
| |
| if (fMeshes[0].fLocalCoords.isEmpty() != that->fMeshes[0].fLocalCoords.isEmpty()) { |
| return false; |
| } |
| |
| if (!fVariableColor) { |
| if (that->fVariableColor || that->fMeshes[0].fColor != fMeshes[0].fColor) { |
| fVariableColor = true; |
| } |
| } |
| |
| fMeshes.push_back_n(that->fMeshes.count(), that->fMeshes.begin()); |
| fVertexCount += that->fVertexCount; |
| fIndexCount += that->fIndexCount; |
| |
| this->joinBounds(*that); |
| return true; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| #ifdef GR_TEST_UTILS |
| |
| #include "GrDrawOpTest.h" |
| |
| static uint32_t seed_vertices(GrPrimitiveType type) { |
| switch (type) { |
| case kTriangles_GrPrimitiveType: |
| case kTriangleStrip_GrPrimitiveType: |
| case kTriangleFan_GrPrimitiveType: |
| return 3; |
| case kPoints_GrPrimitiveType: |
| return 1; |
| case kLines_GrPrimitiveType: |
| case kLineStrip_GrPrimitiveType: |
| return 2; |
| } |
| SkFAIL("Incomplete switch\n"); |
| return 0; |
| } |
| |
| static uint32_t primitive_vertices(GrPrimitiveType type) { |
| switch (type) { |
| case kTriangles_GrPrimitiveType: |
| return 3; |
| case kLines_GrPrimitiveType: |
| return 2; |
| case kTriangleStrip_GrPrimitiveType: |
| case kTriangleFan_GrPrimitiveType: |
| case kPoints_GrPrimitiveType: |
| case kLineStrip_GrPrimitiveType: |
| return 1; |
| } |
| SkFAIL("Incomplete switch\n"); |
| return 0; |
| } |
| |
| static SkPoint random_point(SkRandom* random, SkScalar min, SkScalar max) { |
| SkPoint p; |
| p.fX = random->nextRangeScalar(min, max); |
| p.fY = random->nextRangeScalar(min, max); |
| return p; |
| } |
| |
| static void randomize_params(size_t count, size_t maxVertex, SkScalar min, SkScalar max, |
| SkRandom* random, SkTArray<SkPoint>* positions, |
| SkTArray<SkPoint>* texCoords, bool hasTexCoords, |
| SkTArray<GrColor>* colors, bool hasColors, SkTArray<uint16_t>* indices, |
| bool hasIndices) { |
| for (uint32_t v = 0; v < count; v++) { |
| positions->push_back(random_point(random, min, max)); |
| if (hasTexCoords) { |
| texCoords->push_back(random_point(random, min, max)); |
| } |
| if (hasColors) { |
| colors->push_back(GrRandomColor(random)); |
| } |
| if (hasIndices) { |
| SkASSERT(maxVertex <= SK_MaxU16); |
| indices->push_back(random->nextULessThan((uint16_t)maxVertex)); |
| } |
| } |
| } |
| |
| DRAW_OP_TEST_DEFINE(VerticesOp) { |
| GrPrimitiveType type = GrPrimitiveType(random->nextULessThan(kLast_GrPrimitiveType + 1)); |
| uint32_t primitiveCount = random->nextRangeU(1, 100); |
| |
| // TODO make 'sensible' indexbuffers |
| SkTArray<SkPoint> positions; |
| SkTArray<SkPoint> texCoords; |
| SkTArray<GrColor> colors; |
| SkTArray<uint16_t> indices; |
| |
| bool hasTexCoords = random->nextBool(); |
| bool hasIndices = random->nextBool(); |
| bool hasColors = random->nextBool(); |
| |
| uint32_t vertexCount = seed_vertices(type) + (primitiveCount - 1) * primitive_vertices(type); |
| |
| static const SkScalar kMinVertExtent = -100.f; |
| static const SkScalar kMaxVertExtent = 100.f; |
| randomize_params(seed_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent, random, |
| &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices, |
| hasIndices); |
| |
| for (uint32_t i = 1; i < primitiveCount; i++) { |
| randomize_params(primitive_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent, |
| random, &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices, |
| hasIndices); |
| } |
| |
| SkMatrix viewMatrix = GrTest::TestMatrix(random); |
| SkRect bounds; |
| SkDEBUGCODE(bool result =) bounds.setBoundsCheck(positions.begin(), vertexCount); |
| SkASSERT(result); |
| |
| viewMatrix.mapRect(&bounds); |
| |
| GrColor color = GrRandomColor(random); |
| return GrDrawVerticesOp::Make(color, type, viewMatrix, positions.begin(), vertexCount, |
| indices.begin(), hasIndices ? vertexCount : 0, colors.begin(), |
| texCoords.begin(), bounds); |
| } |
| |
| #endif |