void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override { int instanceCount = fGeoData.count(); SkMatrix invert; if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) { SkDebugf("Could not invert viewmatrix\n"); return; } // Setup GrGeometryProcessor SkAutoTUnref<GrGeometryProcessor> quadProcessor(QuadEdgeEffect::Create(this->color(), invert)); batchTarget->initDraw(quadProcessor, pipeline); // TODO remove this when batch is everywhere GrPipelineInfo init; init.fColorIgnored = fBatch.fColorIgnored; init.fOverrideColor = GrColor_ILLEGAL; init.fCoverageIgnored = fBatch.fCoverageIgnored; init.fUsesLocalCoords = this->usesLocalCoords(); quadProcessor->initBatchTracker(batchTarget->currentBatchTracker(), init); // TODO generate all segments for all paths and use one vertex buffer for (int i = 0; i < instanceCount; i++) { Geometry& args = fGeoData[i]; // We use the fact that SkPath::transform path does subdivision based on // perspective. Otherwise, we apply the view matrix when copying to the // segment representation. const SkMatrix* viewMatrix = &args.fViewMatrix; if (viewMatrix->hasPerspective()) { args.fPath.transform(*viewMatrix); viewMatrix = &SkMatrix::I(); } int vertexCount; int indexCount; enum { kPreallocSegmentCnt = 512 / sizeof(Segment), kPreallocDrawCnt = 4, }; SkSTArray<kPreallocSegmentCnt, Segment, true> segments; SkPoint fanPt; if (!get_segments(args.fPath, *viewMatrix, &segments, &fanPt, &vertexCount, &indexCount)) { continue; } const GrVertexBuffer* vertexBuffer; int firstVertex; size_t vertexStride = quadProcessor->getVertexStride(); void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex); if (!vertices) { SkDebugf("Could not allocate vertices\n"); return; } const GrIndexBuffer* indexBuffer; int firstIndex; void *indices = batchTarget->indexPool()->makeSpace(indexCount, &indexBuffer, &firstIndex); if (!indices) { SkDebugf("Could not allocate indices\n"); return; } QuadVertex* verts = reinterpret_cast<QuadVertex*>(vertices); uint16_t* idxs = reinterpret_cast<uint16_t*>(indices); SkSTArray<kPreallocDrawCnt, Draw, true> draws; create_vertices(segments, fanPt, &draws, verts, idxs); GrDrawTarget::DrawInfo info; info.setVertexBuffer(vertexBuffer); info.setIndexBuffer(indexBuffer); info.setPrimitiveType(kTriangles_GrPrimitiveType); info.setStartIndex(firstIndex); int vOffset = 0; for (int i = 0; i < draws.count(); ++i) { const Draw& draw = draws[i]; info.setStartVertex(vOffset + firstVertex); info.setVertexCount(draw.fVertexCnt); info.setIndexCount(draw.fIndexCnt); batchTarget->draw(info); vOffset += draw.fVertexCnt; } } }
void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override { int instanceCount = fGeoData.count(); SkMatrix invert; if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) { SkDebugf("Could not invert viewmatrix\n"); return; } uint32_t flags = 0; flags |= this->viewMatrix().isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0; GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode); // Setup GrGeometryProcessor GrBatchAtlas* atlas = fAtlas; SkAutoTUnref<GrGeometryProcessor> dfProcessor( GrDistanceFieldPathGeoProc::Create(this->color(), this->viewMatrix(), atlas->getTexture(), params, flags, false)); this->initDraw(batchTarget, dfProcessor, pipeline); static const int kVertsPerQuad = 4; static const int kIndicesPerQuad = 6; SkAutoTUnref<const GrIndexBuffer> indexBuffer( batchTarget->resourceProvider()->refQuadIndexBuffer()); // allocate vertices size_t vertexStride = dfProcessor->getVertexStride(); SkASSERT(vertexStride == 2 * sizeof(SkPoint)); const GrVertexBuffer* vertexBuffer; int vertexCount = kVertsPerQuad * instanceCount; int firstVertex; void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex); if (!vertices || !indexBuffer) { SkDebugf("Could not allocate vertices\n"); return; } // We may have to flush while uploading path data to the atlas, so we set up the draw here int maxInstancesPerDraw = indexBuffer->maxQuads(); GrDrawTarget::DrawInfo drawInfo; drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType); drawInfo.setStartVertex(0); drawInfo.setStartIndex(0); drawInfo.setVerticesPerInstance(kVertsPerQuad); drawInfo.setIndicesPerInstance(kIndicesPerQuad); drawInfo.adjustStartVertex(firstVertex); drawInfo.setVertexBuffer(vertexBuffer); drawInfo.setIndexBuffer(indexBuffer); int instancesToFlush = 0; for (int i = 0; i < instanceCount; i++) { Geometry& args = fGeoData[i]; // get mip level SkScalar maxScale = this->viewMatrix().getMaxScale(); const SkRect& bounds = args.fPath.getBounds(); SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); SkScalar size = maxScale * maxDim; uint32_t desiredDimension; if (size <= kSmallMIP) { desiredDimension = kSmallMIP; } else if (size <= kMediumMIP) { desiredDimension = kMediumMIP; } else { desiredDimension = kLargeMIP; } // check to see if path is cached // TODO: handle stroked vs. filled version of same path PathData::Key key = { args.fPath.getGenerationID(), desiredDimension }; args.fPathData = fPathCache->find(key); if (NULL == args.fPathData || !atlas->hasID(args.fPathData->fID)) { // Remove the stale cache entry if (args.fPathData) { fPathCache->remove(args.fPathData->fKey); fPathList->remove(args.fPathData); SkDELETE(args.fPathData); } SkScalar scale = desiredDimension/maxDim; args.fPathData = SkNEW(PathData); if (!this->addPathToAtlas(batchTarget, dfProcessor, pipeline, &drawInfo, &instancesToFlush, maxInstancesPerDraw, atlas, args.fPathData, args.fPath, args.fStroke, args.fAntiAlias, desiredDimension, scale)) { SkDebugf("Can't rasterize path\n"); return; } } atlas->setLastUseToken(args.fPathData->fID, batchTarget->currentToken()); // Now set vertices intptr_t offset = reinterpret_cast<intptr_t>(vertices); offset += i * kVertsPerQuad * vertexStride; SkPoint* positions = reinterpret_cast<SkPoint*>(offset); this->drawPath(batchTarget, atlas, pipeline, dfProcessor, positions, vertexStride, this->viewMatrix(), args.fPath, args.fPathData); instancesToFlush++; } this->flush(batchTarget, &drawInfo, instancesToFlush, maxInstancesPerDraw); }
void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override { // Go to device coords to allow batching across matrix changes SkMatrix invert = SkMatrix::I(); // if we have a local rect, then we apply the localMatrix directly to the localRect to // generate vertex local coords bool hasExplicitLocalCoords = this->hasLocalRect(); if (!hasExplicitLocalCoords) { if (!this->viewMatrix().isIdentity() && !this->viewMatrix().invert(&invert)) { SkDebugf("Could not invert\n"); return; } if (this->hasLocalMatrix()) { invert.preConcat(this->localMatrix()); } } SkAutoTUnref<const GrGeometryProcessor> gp(create_rect_gp(hasExplicitLocalCoords, this->color(), &invert)); batchTarget->initDraw(gp, pipeline); // TODO this is hacky, but the only way we have to initialize the GP is to use the // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch // everywhere we can remove this nastiness GrPipelineInfo init; init.fColorIgnored = fBatch.fColorIgnored; init.fOverrideColor = GrColor_ILLEGAL; init.fCoverageIgnored = fBatch.fCoverageIgnored; init.fUsesLocalCoords = this->usesLocalCoords(); gp->initBatchTracker(batchTarget->currentBatchTracker(), init); size_t vertexStride = gp->getVertexStride(); SkASSERT(hasExplicitLocalCoords ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr) : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)); int instanceCount = fGeoData.count(); int vertexCount = kVertsPerRect * instanceCount; const GrVertexBuffer* vertexBuffer; int firstVertex; void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex); if (!vertices || !batchTarget->quadIndexBuffer()) { SkDebugf("Could not allocate buffers\n"); return; } for (int i = 0; i < instanceCount; i++) { const Geometry& args = fGeoData[i]; intptr_t offset = GrTCast<intptr_t>(vertices) + kVertsPerRect * i * vertexStride; SkPoint* positions = GrTCast<SkPoint*>(offset); positions->setRectFan(args.fRect.fLeft, args.fRect.fTop, args.fRect.fRight, args.fRect.fBottom, vertexStride); args.fViewMatrix.mapPointsWithStride(positions, vertexStride, kVertsPerRect); if (args.fHasLocalRect) { static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor); SkPoint* coords = GrTCast<SkPoint*>(offset + kLocalOffset); coords->setRectFan(args.fLocalRect.fLeft, args.fLocalRect.fTop, args.fLocalRect.fRight, args.fLocalRect.fBottom, vertexStride); if (args.fHasLocalMatrix) { args.fLocalMatrix.mapPointsWithStride(coords, vertexStride, kVertsPerRect); } } static const int kColorOffset = sizeof(SkPoint); GrColor* vertColor = GrTCast<GrColor*>(offset + kColorOffset); for (int j = 0; j < 4; ++j) { *vertColor = args.fColor; vertColor = (GrColor*) ((intptr_t) vertColor + vertexStride); } } const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer(); GrDrawTarget::DrawInfo drawInfo; drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType); drawInfo.setStartVertex(0); drawInfo.setStartIndex(0); drawInfo.setVerticesPerInstance(kVertsPerRect); drawInfo.setIndicesPerInstance(kIndicesPerRect); drawInfo.adjustStartVertex(firstVertex); drawInfo.setVertexBuffer(vertexBuffer); drawInfo.setIndexBuffer(quadIndexBuffer); int maxInstancesPerDraw = quadIndexBuffer->maxQuads(); while (instanceCount) { drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw)); drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance()); drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance()); batchTarget->draw(drawInfo); drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount()); instanceCount -= drawInfo.instanceCount(); } }