diff options
author | joshualitt <joshualitt@chromium.org> | 2015-02-27 10:06:15 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-27 10:06:15 -0800 |
commit | 0823bfae151dd27f364db3a7f07017a7cef6bfdd (patch) | |
tree | 00f31b5717071e65b04ed8a5125f596515d5bc9a /src | |
parent | 9888b22fc9cd5d3260d2c3639aec20dc79915d37 (diff) |
Context batches
BUG=skia:
Review URL: https://codereview.chromium.org/913253003
Diffstat (limited to 'src')
-rwxr-xr-x | src/gpu/GrContext.cpp | 549 |
1 files changed, 446 insertions, 103 deletions
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 85ea9cab28..679ee84cad 100755 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -9,6 +9,8 @@ #include "GrContext.h" #include "GrAARectRenderer.h" +#include "GrBatch.h" +#include "GrBatchTarget.h" #include "GrBufferAllocPool.h" #include "GrDefaultGeoProcFactory.h" #include "GrFontCache.h" @@ -440,28 +442,6 @@ void GrContext::dumpFontCache() const { //////////////////////////////////////////////////////////////////////////////// -/* create a triangle strip that strokes the specified triangle. There are 8 - unique vertices, but we repreat the last 2 to close up. Alternatively we - could use an indices array, and then only send 8 verts, but not sure that - would be faster. - */ -static void setStrokeRectStrip(SkPoint verts[10], SkRect rect, - SkScalar width) { - const SkScalar rad = SkScalarHalf(width); - rect.sort(); - - verts[0].set(rect.fLeft + rad, rect.fTop + rad); - verts[1].set(rect.fLeft - rad, rect.fTop - rad); - verts[2].set(rect.fRight - rad, rect.fTop + rad); - verts[3].set(rect.fRight + rad, rect.fTop - rad); - verts[4].set(rect.fRight - rad, rect.fBottom - rad); - verts[5].set(rect.fRight + rad, rect.fBottom + rad); - verts[6].set(rect.fLeft + rad, rect.fBottom - rad); - verts[7].set(rect.fLeft - rad, rect.fBottom + rad); - verts[8] = verts[0]; - verts[9] = verts[1]; -} - static inline bool is_irect(const SkRect& r) { return SkScalarIsInt(r.fLeft) && SkScalarIsInt(r.fTop) && SkScalarIsInt(r.fRight) && SkScalarIsInt(r.fBottom); @@ -510,6 +490,178 @@ static inline bool rect_contains_inclusive(const SkRect& rect, const SkPoint& po point.fY >= rect.fTop && point.fY <= rect.fBottom; } +class StrokeRectBatch : public GrBatch { +public: + struct Geometry { + GrColor fColor; + SkMatrix fViewMatrix; + SkRect fRect; + SkScalar fStrokeWidth; + }; + + static GrBatch* Create(const Geometry& geometry) { + return SkNEW_ARGS(StrokeRectBatch, (geometry)); + } + + const char* name() const SK_OVERRIDE { return "StrokeRectBatch"; } + + void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE { + // When this is called on a batch, there is only one geometry bundle + out->setKnownFourComponents(fGeoData[0].fColor); + } + + void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE { + out->setKnownSingleComponent(0xff); + } + + void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE { + // Handle any color overrides + if (init.fColorIgnored) { + fGeoData[0].fColor = GrColor_ILLEGAL; + } else if (GrColor_ILLEGAL != init.fOverrideColor) { + fGeoData[0].fColor = init.fOverrideColor; + } + + // setup batch properties + fBatch.fColorIgnored = init.fColorIgnored; + fBatch.fColor = fGeoData[0].fColor; + fBatch.fUsesLocalCoords = init.fUsesLocalCoords; + fBatch.fCoverageIgnored = init.fCoverageIgnored; + } + + void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE { + SkAutoTUnref<const GrGeometryProcessor> gp( + GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType, + this->color(), + this->viewMatrix(), + SkMatrix::I())); + + 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(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr)); + + Geometry& args = fGeoData[0]; + + int vertexCount = kVertsPerHairlineRect; + if (args.fStrokeWidth > 0) { + vertexCount = kVertsPerStrokeRect; + } + + const GrVertexBuffer* vertexBuffer; + int firstVertex; + + void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride, + vertexCount, + &vertexBuffer, + &firstVertex); + + SkPoint* vertex = reinterpret_cast<SkPoint*>(vertices); + + GrPrimitiveType primType; + + if (args.fStrokeWidth > 0) {; + primType = kTriangleStrip_GrPrimitiveType; + args.fRect.sort(); + this->setStrokeRectStrip(vertex, args.fRect, args.fStrokeWidth); + } else { + // hairline + primType = kLineStrip_GrPrimitiveType; + vertex[0].set(args.fRect.fLeft, args.fRect.fTop); + vertex[1].set(args.fRect.fRight, args.fRect.fTop); + vertex[2].set(args.fRect.fRight, args.fRect.fBottom); + vertex[3].set(args.fRect.fLeft, args.fRect.fBottom); + vertex[4].set(args.fRect.fLeft, args.fRect.fTop); + } + + GrDrawTarget::DrawInfo drawInfo; + drawInfo.setPrimitiveType(primType); + drawInfo.setVertexBuffer(vertexBuffer); + drawInfo.setStartVertex(firstVertex); + drawInfo.setVertexCount(vertexCount); + drawInfo.setStartIndex(0); + drawInfo.setIndexCount(0); + drawInfo.setInstanceCount(0); + drawInfo.setVerticesPerInstance(0); + drawInfo.setIndicesPerInstance(0); + batchTarget->draw(drawInfo); + } + + SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } + +private: + StrokeRectBatch(const Geometry& geometry) { + this->initClassID<StrokeRectBatch>(); + + fBatch.fHairline = geometry.fStrokeWidth == 0; + + fGeoData.push_back(geometry); + } + + /* create a triangle strip that strokes the specified rect. There are 8 + unique vertices, but we repeat the last 2 to close up. Alternatively we + could use an indices array, and then only send 8 verts, but not sure that + would be faster. + */ + void setStrokeRectStrip(SkPoint verts[10], const SkRect& rect, SkScalar width) { + const SkScalar rad = SkScalarHalf(width); + // TODO we should be able to enable this assert, but we'd have to filter these draws + // this is a bug + //SkASSERT(rad < rect.width() / 2 && rad < rect.height() / 2); + + verts[0].set(rect.fLeft + rad, rect.fTop + rad); + verts[1].set(rect.fLeft - rad, rect.fTop - rad); + verts[2].set(rect.fRight - rad, rect.fTop + rad); + verts[3].set(rect.fRight + rad, rect.fTop - rad); + verts[4].set(rect.fRight - rad, rect.fBottom - rad); + verts[5].set(rect.fRight + rad, rect.fBottom + rad); + verts[6].set(rect.fLeft + rad, rect.fBottom - rad); + verts[7].set(rect.fLeft - rad, rect.fBottom + rad); + verts[8] = verts[0]; + verts[9] = verts[1]; + } + + + GrColor color() const { return fBatch.fColor; } + bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } + bool colorIgnored() const { return fBatch.fColorIgnored; } + const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } + bool hairline() const { return fBatch.fHairline; } + + bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE { + // StrokeRectBatch* that = t->cast<StrokeRectBatch>(); + + // NonAA stroke rects cannot batch right now + // TODO make these batchable + return false; + } + + struct BatchTracker { + GrColor fColor; + bool fUsesLocalCoords; + bool fColorIgnored; + bool fCoverageIgnored; + bool fHairline; + }; + + const static int kVertsPerHairlineRect = 5; + const static int kVertsPerStrokeRect = 10; + + BatchTracker fBatch; + SkSTArray<1, Geometry, true> fGeoData; +}; + void GrContext::drawRect(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint, @@ -597,47 +749,19 @@ void GrContext::drawRect(GrRenderTarget* rt, } if (width >= 0) { - // TODO: consider making static vertex buffers for these cases. - // Hairline could be done by just adding closing vertex to - // unitSquareVertexBuffer() - - static const int worstCaseVertCount = 10; - SkAutoTUnref<const GrGeometryProcessor> gp( - GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType, - color, - viewMatrix, - SkMatrix::I())); - GrDrawTarget::AutoReleaseGeometry geo(target, - worstCaseVertCount, - gp->getVertexStride(), - 0); - SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); - - if (!geo.succeeded()) { - SkDebugf("Failed to get space for vertices!\n"); - return; - } - - GrPrimitiveType primType; - int vertCount; - SkPoint* vertex = geo.positions(); - - if (width > 0) { - vertCount = 10; - primType = kTriangleStrip_GrPrimitiveType; - setStrokeRectStrip(vertex, rect, width); - } else { - // hairline - vertCount = 5; - primType = kLineStrip_GrPrimitiveType; - vertex[0].set(rect.fLeft, rect.fTop); - vertex[1].set(rect.fRight, rect.fTop); - vertex[2].set(rect.fRight, rect.fBottom); - vertex[3].set(rect.fLeft, rect.fBottom); - vertex[4].set(rect.fLeft, rect.fTop); - } - - target->drawNonIndexed(&pipelineBuilder, gp, primType, 0, vertCount); + StrokeRectBatch::Geometry geometry; + geometry.fViewMatrix = viewMatrix; + geometry.fColor = color; + geometry.fRect = rect; + geometry.fStrokeWidth = width; + + SkAutoTUnref<GrBatch> batch(StrokeRectBatch::Create(geometry)); + + SkRect bounds = rect; + SkScalar rad = SkScalarHalf(width); + bounds.outset(rad, rad); + viewMatrix.mapRect(&bounds); + target->drawBatch(&pipelineBuilder, batch, &bounds); } else { // filled BW rect target->drawSimpleRect(&pipelineBuilder, color, viewMatrix, rect); @@ -669,8 +793,8 @@ void GrContext::drawNonAARectToRect(GrRenderTarget* rt, localMatrix); } -static const GrGeometryProcessor* set_vertex_attributes(const SkPoint* texCoords, - const GrColor* colors, +static const GrGeometryProcessor* set_vertex_attributes(bool hasLocalCoords, + bool hasColors, int* colorOffset, int* texOffset, GrColor color, @@ -678,21 +802,268 @@ static const GrGeometryProcessor* set_vertex_attributes(const SkPoint* texCoords *texOffset = -1; *colorOffset = -1; uint32_t flags = GrDefaultGeoProcFactory::kPosition_GPType; - if (texCoords && colors) { + if (hasLocalCoords && hasColors) { *colorOffset = sizeof(SkPoint); *texOffset = sizeof(SkPoint) + sizeof(GrColor); flags |= GrDefaultGeoProcFactory::kColor_GPType | GrDefaultGeoProcFactory::kLocalCoord_GPType; - } else if (texCoords) { + } else if (hasLocalCoords) { *texOffset = sizeof(SkPoint); flags |= GrDefaultGeoProcFactory::kLocalCoord_GPType; - } else if (colors) { + } else if (hasColors) { *colorOffset = sizeof(SkPoint); flags |= GrDefaultGeoProcFactory::kColor_GPType; } return GrDefaultGeoProcFactory::Create(flags, color, viewMatrix, SkMatrix::I()); } +class DrawVerticesBatch : public GrBatch { +public: + struct Geometry { + GrColor fColor; + SkTDArray<SkPoint> fPositions; + SkTDArray<uint16_t> fIndices; + SkTDArray<GrColor> fColors; + SkTDArray<SkPoint> fLocalCoords; + }; + + static GrBatch* Create(const Geometry& geometry, GrPrimitiveType primitiveType, + const SkMatrix& viewMatrix, + const SkPoint* positions, int vertexCount, + const uint16_t* indices, int indexCount, + const GrColor* colors, const SkPoint* localCoords) { + return SkNEW_ARGS(DrawVerticesBatch, (geometry, primitiveType, viewMatrix, positions, + vertexCount, indices, indexCount, colors, + localCoords)); + } + + const char* name() const SK_OVERRIDE { return "DrawVerticesBatch"; } + + void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE { + // When this is called on a batch, there is only one geometry bundle + if (this->hasColors()) { + out->setUnknownFourComponents(); + } else { + out->setKnownFourComponents(fGeoData[0].fColor); + } + } + + void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE { + out->setUnknownSingleComponent(); + } + + void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE { + // Handle any color overrides + if (init.fColorIgnored) { + fGeoData[0].fColor = GrColor_ILLEGAL; + } else if (GrColor_ILLEGAL != init.fOverrideColor) { + fGeoData[0].fColor = init.fOverrideColor; + } + + // setup batch properties + fBatch.fColorIgnored = init.fColorIgnored; + fBatch.fColor = fGeoData[0].fColor; + fBatch.fUsesLocalCoords = init.fUsesLocalCoords; + fBatch.fCoverageIgnored = init.fCoverageIgnored; + } + + void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE { + int colorOffset = -1, texOffset = -1; + SkAutoTUnref<const GrGeometryProcessor> gp( + set_vertex_attributes(this->hasLocalCoords(), this->hasColors(), &colorOffset, + &texOffset, this->color(), this->viewMatrix())); + + 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(vertexStride == sizeof(SkPoint) + (this->hasLocalCoords() ? sizeof(SkPoint) : 0) + + (this->hasColors() ? sizeof(GrColor) : 0)); + + int instanceCount = fGeoData.count(); + + const GrVertexBuffer* vertexBuffer; + int firstVertex; + + void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride, + this->vertexCount(), + &vertexBuffer, + &firstVertex); + + const GrIndexBuffer* indexBuffer; + int firstIndex; + + void* indices = NULL; + if (this->hasIndices()) { + indices = batchTarget->indexPool()->makeSpace(this->indexCount(), + &indexBuffer, + &firstIndex); + } + + int indexOffset = 0; + int vertexOffset = 0; + for (int i = 0; i < instanceCount; i++) { + const Geometry& args = fGeoData[i]; + + // TODO we can actually cache this interleaved and then just memcopy + if (this->hasIndices()) { + for (int j = 0; j < args.fIndices.count(); ++j, ++indexOffset) { + *((uint16_t*)indices + indexOffset) = args.fIndices[j] + vertexOffset; + } + } + + for (int j = 0; j < args.fPositions.count(); ++j) { + *((SkPoint*)vertices) = args.fPositions[j]; + if (this->hasColors()) { + *(GrColor*)((intptr_t)vertices + colorOffset) = args.fColors[j]; + } + if (this->hasLocalCoords()) { + *(SkPoint*)((intptr_t)vertices + texOffset) = args.fLocalCoords[j]; + } + vertices = (void*)((intptr_t)vertices + vertexStride); + vertexOffset++; + } + } + + GrDrawTarget::DrawInfo drawInfo; + drawInfo.setPrimitiveType(this->primitiveType()); + drawInfo.setVertexBuffer(vertexBuffer); + drawInfo.setStartVertex(firstVertex); + drawInfo.setVertexCount(this->vertexCount()); + if (this->hasIndices()) { + drawInfo.setIndexBuffer(indexBuffer); + drawInfo.setStartIndex(firstIndex); + drawInfo.setIndexCount(this->indexCount()); + } else { + drawInfo.setStartIndex(0); + drawInfo.setIndexCount(0); + } + batchTarget->draw(drawInfo); + } + + SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } + +private: + DrawVerticesBatch(const Geometry& geometry, GrPrimitiveType primitiveType, + const SkMatrix& viewMatrix, + const SkPoint* positions, int vertexCount, + const uint16_t* indices, int indexCount, + const GrColor* colors, const SkPoint* localCoords) { + this->initClassID<DrawVerticesBatch>(); + SkASSERT(positions); + + fBatch.fViewMatrix = viewMatrix; + Geometry& installedGeo = fGeoData.push_back(geometry); + + installedGeo.fPositions.append(vertexCount, positions); + if (indices) { + installedGeo.fIndices.append(indexCount, indices); + fBatch.fHasIndices = true; + } else { + fBatch.fHasIndices = false; + } + + if (colors) { + installedGeo.fColors.append(vertexCount, colors); + fBatch.fHasColors = true; + } else { + fBatch.fHasColors = false; + } + + if (localCoords) { + installedGeo.fLocalCoords.append(vertexCount, localCoords); + fBatch.fHasLocalCoords = true; + } else { + fBatch.fHasLocalCoords = false; + } + fBatch.fVertexCount = vertexCount; + fBatch.fIndexCount = indexCount; + fBatch.fPrimitiveType = primitiveType; + } + + GrPrimitiveType primitiveType() const { return fBatch.fPrimitiveType; } + bool batchablePrimitiveType() const { + return kTriangles_GrPrimitiveType == fBatch.fPrimitiveType || + kLines_GrPrimitiveType == fBatch.fPrimitiveType || + kPoints_GrPrimitiveType == fBatch.fPrimitiveType; + } + GrColor color() const { return fBatch.fColor; } + bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } + bool colorIgnored() const { return fBatch.fColorIgnored; } + const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; } + bool hasColors() const { return fBatch.fHasColors; } + bool hasIndices() const { return fBatch.fHasIndices; } + bool hasLocalCoords() const { return fBatch.fHasLocalCoords; } + int vertexCount() const { return fBatch.fVertexCount; } + int indexCount() const { return fBatch.fIndexCount; } + + bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE { + DrawVerticesBatch* that = t->cast<DrawVerticesBatch>(); + + if (!this->batchablePrimitiveType() || this->primitiveType() != that->primitiveType()) { + return false; + } + + SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); + + // We currently use a uniform viewmatrix for this batch + if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { + return false; + } + + if (this->hasColors() != that->hasColors()) { + return false; + } + + if (this->hasIndices() != that->hasIndices()) { + return false; + } + + if (this->hasLocalCoords() != that->hasLocalCoords()) { + return false; + } + + if (!this->hasColors() && this->color() != that->color()) { + return false; + } + + if (this->color() != that->color()) { + fBatch.fColor = GrColor_ILLEGAL; + } + fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); + fBatch.fVertexCount += that->vertexCount(); + fBatch.fIndexCount += that->indexCount(); + return true; + } + + struct BatchTracker { + GrPrimitiveType fPrimitiveType; + SkMatrix fViewMatrix; + GrColor fColor; + bool fUsesLocalCoords; + bool fColorIgnored; + bool fCoverageIgnored; + bool fHasColors; + bool fHasIndices; + bool fHasLocalCoords; + int fVertexCount; + int fIndexCount; + }; + + BatchTracker fBatch; + SkSTArray<1, Geometry, true> fGeoData; +}; + void GrContext::drawVertices(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint, @@ -716,43 +1087,15 @@ void GrContext::drawVertices(GrRenderTarget* rt, GR_CREATE_TRACE_MARKER("GrContext::drawVertices", target); - int colorOffset = -1, texOffset = -1; - SkAutoTUnref<const GrGeometryProcessor> gp( - set_vertex_attributes(texCoords, colors, &colorOffset, &texOffset, - paint.getColor(), viewMatrix)); + DrawVerticesBatch::Geometry geometry; + geometry.fColor = paint.getColor(); - size_t vertexStride = gp->getVertexStride(); - SkASSERT(vertexStride == sizeof(SkPoint) + (SkToBool(texCoords) ? sizeof(SkPoint) : 0) - + (SkToBool(colors) ? sizeof(GrColor) : 0)); - if (!geo.set(target, vertexCount, vertexStride, indexCount)) { - SkDebugf("Failed to get space for vertices!\n"); - return; - } - void* curVertex = geo.vertices(); - - for (int i = 0; i < vertexCount; ++i) { - *((SkPoint*)curVertex) = positions[i]; + SkAutoTUnref<GrBatch> batch(DrawVerticesBatch::Create(geometry, primitiveType, viewMatrix, + positions, vertexCount, indices, + indexCount,colors, texCoords)); - if (texOffset >= 0) { - *(SkPoint*)((intptr_t)curVertex + texOffset) = texCoords[i]; - } - if (colorOffset >= 0) { - *(GrColor*)((intptr_t)curVertex + colorOffset) = colors[i]; - } - curVertex = (void*)((intptr_t)curVertex + vertexStride); - } - - // we don't currently apply offscreen AA to this path. Need improved - // management of GrDrawTarget's geometry to avoid copying points per-tile. - if (indices) { - uint16_t* curIndex = (uint16_t*)geo.indices(); - for (int i = 0; i < indexCount; ++i) { - curIndex[i] = indices[i]; - } - target->drawIndexed(&pipelineBuilder, gp, primitiveType, 0, 0, vertexCount, indexCount); - } else { - target->drawNonIndexed(&pipelineBuilder, gp, primitiveType, 0, vertexCount); - } + // TODO figure out bounds + target->drawBatch(&pipelineBuilder, batch, NULL); } /////////////////////////////////////////////////////////////////////////////// |