aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Brian Salomon <bsalomon@google.com>2018-05-01 10:01:52 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-05-01 16:54:45 +0000
commitddff425b0a9cd4b178c74d7feee2c5c22e37c8d1 (patch)
treed20e3b410278c7d8b86926693eefbbd074b28f40
parentf8da8e59a4e26dbb3ebc7397b18b0599ce10a99b (diff)
Use a cached index buffer for triangle fans in GrDefaultPathRenderer.
Change-Id: I3c3c8db6506c71e4b273c8bc7adcc68d3d768626 Reviewed-on: https://skia-review.googlesource.com/124841 Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Brian Salomon <bsalomon@google.com>
-rw-r--r--src/gpu/GrResourceProvider.cpp42
-rw-r--r--src/gpu/GrResourceProvider.h30
-rw-r--r--src/gpu/ops/GrDefaultPathRenderer.cpp66
3 files changed, 105 insertions, 33 deletions
diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp
index 2d2b3a7bc9..62e2675b79 100644
--- a/src/gpu/GrResourceProvider.cpp
+++ b/src/gpu/GrResourceProvider.cpp
@@ -27,6 +27,7 @@
#include "SkMathPriv.h"
GR_DECLARE_STATIC_UNIQUE_KEY(gQuadIndexBufferKey);
+GR_DECLARE_STATIC_UNIQUE_KEY(gFanIndexBufferKey);
const uint32_t GrResourceProvider::kMinScratchTextureSize = 16;
@@ -59,6 +60,9 @@ GrResourceProvider::GrResourceProvider(GrGpu* gpu, GrResourceCache* cache, GrSin
GR_DEFINE_STATIC_UNIQUE_KEY(gQuadIndexBufferKey);
fQuadIndexBufferKey = gQuadIndexBufferKey;
+
+ GR_DEFINE_STATIC_UNIQUE_KEY(gFanIndexBufferKey);
+ fFanIndexBufferKey = gFanIndexBufferKey;
}
sk_sp<GrTexture> GrResourceProvider::createTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
@@ -341,13 +345,49 @@ sk_sp<const GrBuffer> GrResourceProvider::createPatternedIndexBuffer(const uint1
static constexpr int kMaxQuads = 1 << 12; // max possible: (1 << 14) - 1;
+int GrResourceProvider::QuadCountOfQuadBuffer() { return kMaxQuads; }
+
sk_sp<const GrBuffer> GrResourceProvider::createQuadIndexBuffer() {
GR_STATIC_ASSERT(4 * kMaxQuads <= 65535);
static const uint16_t kPattern[] = { 0, 1, 2, 2, 1, 3 };
return this->createPatternedIndexBuffer(kPattern, 6, kMaxQuads, 4, fQuadIndexBufferKey);
}
-int GrResourceProvider::QuadCountOfQuadBuffer() { return kMaxQuads; }
+// Don't make this maximally sized. It'd be way bigger than required for most cases.
+static constexpr int kMaxFanTris = 1 << 12;
+
+int GrResourceProvider::TriCountOfFanBuffer() { return kMaxFanTris; }
+
+sk_sp<const GrBuffer> GrResourceProvider::createFanIndexBuffer() {
+ GR_STATIC_ASSERT(kMaxFanTris + 2 <= SK_MaxU16 + 1);
+ size_t numIndices = kMaxFanTris * 3;
+ size_t bufferSize = numIndices * sizeof(uint16_t);
+ sk_sp<GrBuffer> buffer(this->createBuffer(bufferSize, kIndex_GrBufferType,
+ kStatic_GrAccessPattern, kNoPendingIO_Flag));
+ if (!buffer) {
+ return nullptr;
+ }
+ uint16_t* data = (uint16_t*)buffer->map();
+ SkAutoTArray<uint16_t> temp;
+ if (!data) {
+ temp.reset(numIndices);
+ data = temp.get();
+ }
+ for (int i = 0; i < kMaxFanTris; ++i) {
+ data[3 * i + 0] = 0;
+ data[3 * i + 1] = i + 1;
+ data[3 * i + 2] = i + 2;
+ }
+ if (temp.get()) {
+ if (!buffer->updateData(data, bufferSize)) {
+ return nullptr;
+ }
+ } else {
+ buffer->unmap();
+ }
+ this->assignUniqueKeyToResource(fFanIndexBufferKey, buffer.get());
+ return std::move(buffer);
+}
sk_sp<GrPath> GrResourceProvider::createPath(const SkPath& path, const GrStyle& style) {
if (this->isAbandoned()) {
diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h
index a853f08ca3..e8f93b561f 100644
--- a/src/gpu/GrResourceProvider.h
+++ b/src/gpu/GrResourceProvider.h
@@ -150,9 +150,9 @@ public:
/**
* Returns an index buffer that can be used to render quads.
* Six indices per quad: 0, 1, 2, 2, 1, 3, etc.
- * The max number of quads is the buffer's index capacity divided by 6.
+ * The max number of quads is specified by QuadCountOfQuadBuffer().
* Draw with GrPrimitiveType::kTriangles
- * @ return the quad index buffer
+ * @return the quad index buffer
*/
sk_sp<const GrBuffer> refQuadIndexBuffer() {
if (auto buffer = this->findByUniqueKey<const GrBuffer>(fQuadIndexBufferKey)) {
@@ -164,6 +164,22 @@ public:
static int QuadCountOfQuadBuffer();
/**
+ * Returns an index buffer that can be used to render triangle fans.
+ * The buffer has the pattern 0, 1, 2, 0, 2, 3, 0, 3, 4 ...
+ * The max number of quads is specified by TriCountOfFanBuffer().
+ * Draw with GrPrimitiveType::kTriangles
+ * @return the fan index buffer
+ */
+ sk_sp<const GrBuffer> refFanIndexBuffer() {
+ if (auto buffer = this->findByUniqueKey<const GrBuffer>(fFanIndexBufferKey)) {
+ return buffer;
+ }
+ return this->createFanIndexBuffer();
+ }
+
+ static int TriCountOfFanBuffer();
+
+ /**
* Factories for GrPath objects. It's an error to call these if path rendering
* is not supported.
*/
@@ -295,12 +311,14 @@ private:
const GrUniqueKey& key);
sk_sp<const GrBuffer> createQuadIndexBuffer();
+ sk_sp<const GrBuffer> createFanIndexBuffer();
- GrResourceCache* fCache;
- GrGpu* fGpu;
+ GrResourceCache* fCache;
+ GrGpu* fGpu;
sk_sp<const GrCaps> fCaps;
- GrUniqueKey fQuadIndexBufferKey;
- bool fExplicitlyAllocateGPUResources;
+ GrUniqueKey fQuadIndexBufferKey;
+ GrUniqueKey fFanIndexBufferKey;
+ bool fExplicitlyAllocateGPUResources;
// In debug builds we guard against improper thread handling
SkDEBUGCODE(mutable GrSingleOwner* fSingleOwner;)
diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp
index 1c5acb0a82..00e2b88087 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.cpp
+++ b/src/gpu/ops/GrDefaultPathRenderer.cpp
@@ -61,17 +61,20 @@ namespace {
class PathGeoBuilder {
public:
- PathGeoBuilder(GrPrimitiveType primitiveType, GrMeshDrawOp::Target* target,
- GrGeometryProcessor* geometryProcessor, const GrPipeline* pipeline)
+ PathGeoBuilder(GrPrimitiveType primitiveType, bool useTriFanIndexBuffer,
+ GrMeshDrawOp::Target* target, GrGeometryProcessor* geometryProcessor,
+ const GrPipeline* pipeline)
: fMesh(primitiveType)
, fTarget(target)
, fVertexStride(sizeof(SkPoint))
, fGeometryProcessor(geometryProcessor)
, fPipeline(pipeline)
, fIndexBuffer(nullptr)
+ , fUseTriFanIndexBuffer(useTriFanIndexBuffer)
, fFirstIndex(0)
, fIndicesInChunk(0)
, fIndices(nullptr) {
+ SkASSERT(!fUseTriFanIndexBuffer || GrPrimitiveType::kTriangles == primitiveType);
this->allocNewBuffers();
}
@@ -92,7 +95,7 @@ public:
void addLine(const SkPoint& p) {
needSpace(1, this->indexScale());
- if (this->isIndexed()) {
+ if (this->isDynamicallyIndexed()) {
uint16_t prevIdx = this->currentIndex() - 1;
appendCountourEdgeIndices(prevIdx);
}
@@ -108,7 +111,7 @@ public:
uint16_t numPts = (uint16_t)GrPathUtils::generateQuadraticPoints(
pts[0], pts[1], pts[2], srcSpaceTolSqd, &fCurVert,
GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
- if (this->isIndexed()) {
+ if (this->isDynamicallyIndexed()) {
for (uint16_t i = 0; i < numPts; ++i) {
appendCountourEdgeIndices(firstQPtIdx + i);
}
@@ -133,7 +136,7 @@ public:
uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
pts[0], pts[1], pts[2], pts[3], srcSpaceTolSqd, &fCurVert,
GrPathUtils::cubicPointCount(pts, srcSpaceTol));
- if (this->isIndexed()) {
+ if (this->isDynamicallyIndexed()) {
for (uint16_t i = 0; i < numPts; ++i) {
appendCountourEdgeIndices(firstCPtIdx + i);
}
@@ -194,9 +197,9 @@ private:
* Derived properties
* TODO: Cache some of these for better performance, rather than re-computing?
*/
- bool isIndexed() const {
- return GrPrimitiveType::kLines == fMesh.primitiveType() ||
- GrPrimitiveType::kTriangles == fMesh.primitiveType();
+ bool isDynamicallyIndexed() const {
+ return !fUseTriFanIndexBuffer && (GrPrimitiveType::kLines == fMesh.primitiveType() ||
+ GrPrimitiveType::kTriangles == fMesh.primitiveType());
}
bool isHairline() const {
return GrPrimitiveType::kLines == fMesh.primitiveType() ||
@@ -223,6 +226,7 @@ private:
// which have a worst-case of 1k points.
static const int kMinVerticesPerChunk = GrPathUtils::kMaxPointsPerCurve + 2;
static const int kFallbackVerticesPerChunk = 16384;
+ GR_STATIC_ASSERT(kFallbackVerticesPerChunk <= SK_MaxU16 + 1);
fVertices = static_cast<SkPoint*>(fTarget->makeVertexSpaceAtLeast(fVertexStride,
kMinVerticesPerChunk,
@@ -231,7 +235,7 @@ private:
&fFirstVertex,
&fVerticesInChunk));
- if (this->isIndexed()) {
+ if (this->isDynamicallyIndexed()) {
// Similar to above: Ensure we get enough indices for one worst-case quad/cubic.
// No extra indices are needed for stitching, though. If we can't get that many, ask
// for enough to match our large vertex request.
@@ -265,14 +269,25 @@ private:
SkASSERT(vertexCount <= fVerticesInChunk);
SkASSERT(indexCount <= fIndicesInChunk);
- if (this->isIndexed() ? SkToBool(indexCount) : SkToBool(vertexCount)) {
- if (!this->isIndexed()) {
- fMesh.setNonIndexedNonInstanced(vertexCount);
- } else {
- fMesh.setIndexed(fIndexBuffer, indexCount, fFirstIndex, 0, vertexCount - 1);
+ if (fUseTriFanIndexBuffer) {
+ if (vertexCount >= 3) {
+ if (auto indexBuffer = fTarget->resourceProvider()->refFanIndexBuffer()) {
+ fMesh.setIndexed(indexBuffer.get(), 3 * (vertexCount - 2), 0, 0,
+ vertexCount - 1);
+ fMesh.setVertexData(fVertexBuffer, fFirstVertex);
+ fTarget->draw(fGeometryProcessor, fPipeline, fMesh);
+ }
+ }
+ } else {
+ if (this->isDynamicallyIndexed() ? SkToBool(indexCount) : SkToBool(vertexCount)) {
+ if (!this->isDynamicallyIndexed()) {
+ fMesh.setNonIndexedNonInstanced(vertexCount);
+ } else {
+ fMesh.setIndexed(fIndexBuffer, indexCount, fFirstIndex, 0, vertexCount - 1);
+ }
+ fMesh.setVertexData(fVertexBuffer, fFirstVertex);
+ fTarget->draw(fGeometryProcessor, fPipeline, fMesh);
}
- fMesh.setVertexData(fVertexBuffer, fFirstVertex);
- fTarget->draw(fGeometryProcessor, fPipeline, fMesh);
}
fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
@@ -318,6 +333,7 @@ private:
SkPoint* fCurVert;
const GrBuffer* fIndexBuffer;
+ bool fUseTriFanIndexBuffer;
int fFirstIndex;
int fIndicesInChunk;
uint16_t* fIndices;
@@ -398,27 +414,25 @@ private:
SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
- int instanceCount = fPaths.count();
-
- // We avoid indices when we have a single hairline contour.
- bool isIndexed = !this->isHairline() || instanceCount > 1 ||
- PathGeoBuilder::PathHasMultipleSubpaths(fPaths[0].fPath);
+ bool useFanIndexBuffer = false;
+ bool singleContour =
+ 1 == fPaths.count() && !PathGeoBuilder::PathHasMultipleSubpaths(fPaths[0].fPath);
// determine primitiveType
GrPrimitiveType primitiveType;
if (this->isHairline()) {
- primitiveType = isIndexed ? GrPrimitiveType::kLines : GrPrimitiveType::kLineStrip;
+ primitiveType = singleContour ? GrPrimitiveType::kLineStrip : GrPrimitiveType::kLines;
} else {
+ useFanIndexBuffer = singleContour;
primitiveType = GrPrimitiveType::kTriangles;
}
- PathGeoBuilder pathGeoBuilder(primitiveType, target, gp.get(),
+ PathGeoBuilder pathGeoBuilder(primitiveType, useFanIndexBuffer, target, gp.get(),
fHelper.makePipeline(target));
// fill buffers
- for (int i = 0; i < instanceCount; i++) {
- const PathData& args = fPaths[i];
- pathGeoBuilder.addPath(args.fPath, args.fTolerance);
+ for (const auto& instance : fPaths) {
+ pathGeoBuilder.addPath(instance.fPath, instance.fTolerance);
}
}