Move GrTessellator::VertexAllocator to GrEagerVertexAllocator
Moves the interface up to Ganesh level and starts using it from other locations. Change-Id: I939d2b357d3ae8551976d0d71b877b72da403712 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/266063 Reviewed-by: Stephen White <senorblanco@chromium.org> Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
parent
1a496c507e
commit
d081dce968
@ -89,6 +89,7 @@ skia_gpu_sources = [
|
||||
"$_src/gpu/GrDrawOpTest.cpp",
|
||||
"$_src/gpu/GrDrawOpTest.h",
|
||||
"$_src/gpu/GrDriverBugWorkarounds.cpp",
|
||||
"$_src/gpu/GrEagerVertexAllocator.h",
|
||||
"$_src/gpu/GrFixedClip.cpp",
|
||||
"$_src/gpu/GrFixedClip.h",
|
||||
"$_src/gpu/GrFragmentProcessor.cpp",
|
||||
|
84
src/gpu/GrEagerVertexAllocator.h
Normal file
84
src/gpu/GrEagerVertexAllocator.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2020 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef GrEagerVertexAllocator_DEFINED
|
||||
#define GrEagerVertexAllocator_DEFINED
|
||||
|
||||
#include "src/gpu/ops/GrMeshDrawOp.h"
|
||||
|
||||
// This interface is used to allocate and map GPU vertex data before the exact number of required
|
||||
// vertices is known. Usage pattern:
|
||||
//
|
||||
// 1. Call lock(eagerCount) with an upper bound on the number of required vertices.
|
||||
// 2. Compute and write vertex data to the returned pointer (if not null).
|
||||
// 3. Call unlock(actualCount) and provide the actual number of vertices written during step #2.
|
||||
//
|
||||
// On step #3, the implementation will attempt to shrink the underlying GPU memory slot to fit the
|
||||
// actual vertex count.
|
||||
class GrEagerVertexAllocator {
|
||||
public:
|
||||
template<typename T> T* lock(int eagerCount) {
|
||||
return static_cast<T*>(this->lock(sizeof(T), eagerCount));
|
||||
}
|
||||
virtual void* lock(size_t stride, int eagerCount) = 0;
|
||||
|
||||
virtual void unlock(int actualCount) = 0;
|
||||
|
||||
virtual ~GrEagerVertexAllocator() {}
|
||||
};
|
||||
|
||||
// GrEagerVertexAllocator implementation that uses GrMeshDrawOp::Target::makeVertexSpace and
|
||||
// GrMeshDrawOp::Target::putBackVertices.
|
||||
class GrEagerDynamicVertexAllocator : public GrEagerVertexAllocator {
|
||||
public:
|
||||
GrEagerDynamicVertexAllocator(GrMeshDrawOp::Target* target,
|
||||
sk_sp<const GrBuffer>* vertexBuffer, int* baseVertex)
|
||||
: fTarget(target)
|
||||
, fVertexBuffer(vertexBuffer)
|
||||
, fBaseVertex(baseVertex) {
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
~GrEagerDynamicVertexAllocator() override {
|
||||
SkASSERT(!fLockCount);
|
||||
}
|
||||
#endif
|
||||
|
||||
void* lock(size_t stride, int eagerCount) override {
|
||||
SkASSERT(!fLockCount);
|
||||
SkASSERT(eagerCount);
|
||||
if (void* data = fTarget->makeVertexSpace(stride, eagerCount, fVertexBuffer, fBaseVertex)) {
|
||||
fLockStride = stride;
|
||||
fLockCount = eagerCount;
|
||||
return data;
|
||||
}
|
||||
fVertexBuffer->reset();
|
||||
*fBaseVertex = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void unlock(int actualCount) override {
|
||||
SkASSERT(fLockCount);
|
||||
SkASSERT(actualCount <= fLockCount);
|
||||
fTarget->putBackVertices(fLockCount - actualCount, fLockStride);
|
||||
if (!actualCount) {
|
||||
fVertexBuffer->reset();
|
||||
*fBaseVertex = 0;
|
||||
}
|
||||
fLockCount = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
GrMeshDrawOp::Target* const fTarget;
|
||||
sk_sp<const GrBuffer>* const fVertexBuffer;
|
||||
int* const fBaseVertex;
|
||||
|
||||
size_t fLockStride;
|
||||
int fLockCount = 0;
|
||||
};
|
||||
|
||||
#endif
|
@ -8,6 +8,7 @@
|
||||
#include "src/gpu/GrTessellator.h"
|
||||
|
||||
#include "src/gpu/GrDefaultGeoProcFactory.h"
|
||||
#include "src/gpu/GrEagerVertexAllocator.h"
|
||||
#include "src/gpu/GrVertexWriter.h"
|
||||
#include "src/gpu/geometry/GrPathUtils.h"
|
||||
|
||||
@ -2358,7 +2359,7 @@ namespace GrTessellator {
|
||||
// Stage 6: Triangulate the monotone polygons into a vertex buffer.
|
||||
|
||||
int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
|
||||
VertexAllocator* vertexAllocator, bool antialias, bool* isLinear) {
|
||||
GrEagerVertexAllocator* vertexAllocator, bool antialias, bool* isLinear) {
|
||||
int contourCnt = get_contour_count(path, tolerance);
|
||||
if (contourCnt <= 0) {
|
||||
*isLinear = true;
|
||||
@ -2378,7 +2379,8 @@ int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBo
|
||||
}
|
||||
int count = count64;
|
||||
|
||||
void* verts = vertexAllocator->lock(count);
|
||||
size_t vertexStride = GetVertexStride(antialias);
|
||||
void* verts = vertexAllocator->lock(vertexStride, count);
|
||||
if (!verts) {
|
||||
SkDebugf("Could not allocate vertices\n");
|
||||
return 0;
|
||||
@ -2389,7 +2391,7 @@ int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBo
|
||||
end = outer_mesh_to_triangles(outerMesh, true, end);
|
||||
|
||||
int actualCount = static_cast<int>((static_cast<uint8_t*>(end) - static_cast<uint8_t*>(verts))
|
||||
/ vertexAllocator->stride());
|
||||
/ vertexStride);
|
||||
SkASSERT(actualCount <= count);
|
||||
vertexAllocator->unlock(actualCount);
|
||||
return actualCount;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "include/private/SkColorData.h"
|
||||
#include "src/gpu/GrColor.h"
|
||||
|
||||
class GrEagerVertexAllocator;
|
||||
class SkPath;
|
||||
struct SkRect;
|
||||
|
||||
@ -23,17 +24,6 @@ struct SkRect;
|
||||
|
||||
namespace GrTessellator {
|
||||
|
||||
class VertexAllocator {
|
||||
public:
|
||||
VertexAllocator(size_t stride) : fStride(stride) {}
|
||||
virtual ~VertexAllocator() {}
|
||||
virtual void* lock(int vertexCount) = 0;
|
||||
virtual void unlock(int actualCount) = 0;
|
||||
size_t stride() const { return fStride; }
|
||||
private:
|
||||
size_t fStride;
|
||||
};
|
||||
|
||||
struct WindingVertex {
|
||||
SkPoint fPos;
|
||||
int fWinding;
|
||||
@ -46,8 +36,12 @@ struct WindingVertex {
|
||||
int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
|
||||
WindingVertex** verts);
|
||||
|
||||
constexpr size_t GetVertexStride(bool antialias) {
|
||||
return sizeof(SkPoint) + ((antialias) ? sizeof(float) : 0);
|
||||
}
|
||||
|
||||
int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
|
||||
VertexAllocator*, bool antialias, bool *isLinear);
|
||||
GrEagerVertexAllocator*, bool antialias, bool *isLinear);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "src/gpu/GrClip.h"
|
||||
#include "src/gpu/GrDefaultGeoProcFactory.h"
|
||||
#include "src/gpu/GrDrawOpTest.h"
|
||||
#include "src/gpu/GrEagerVertexAllocator.h"
|
||||
#include "src/gpu/GrMesh.h"
|
||||
#include "src/gpu/GrOpFlushState.h"
|
||||
#include "src/gpu/GrResourceCache.h"
|
||||
@ -68,16 +69,22 @@ bool cache_match(GrGpuBuffer* vertexBuffer, SkScalar tol, int* actualCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
class StaticVertexAllocator : public GrTessellator::VertexAllocator {
|
||||
class StaticVertexAllocator : public GrEagerVertexAllocator {
|
||||
public:
|
||||
StaticVertexAllocator(size_t stride, GrResourceProvider* resourceProvider, bool canMapVB)
|
||||
: VertexAllocator(stride)
|
||||
, fResourceProvider(resourceProvider)
|
||||
StaticVertexAllocator(GrResourceProvider* resourceProvider, bool canMapVB)
|
||||
: fResourceProvider(resourceProvider)
|
||||
, fCanMapVB(canMapVB)
|
||||
, fVertices(nullptr) {
|
||||
}
|
||||
void* lock(int vertexCount) override {
|
||||
size_t size = vertexCount * stride();
|
||||
#ifdef SK_DEBUG
|
||||
~StaticVertexAllocator() override {
|
||||
SkASSERT(!fLockStride);
|
||||
}
|
||||
#endif
|
||||
void* lock(size_t stride, int eagerCount) override {
|
||||
SkASSERT(!fLockStride);
|
||||
SkASSERT(stride);
|
||||
size_t size = eagerCount * stride;
|
||||
fVertexBuffer = fResourceProvider->createBuffer(size, GrGpuBufferType::kVertex,
|
||||
kStatic_GrAccessPattern);
|
||||
if (!fVertexBuffer.get()) {
|
||||
@ -86,18 +93,21 @@ public:
|
||||
if (fCanMapVB) {
|
||||
fVertices = fVertexBuffer->map();
|
||||
} else {
|
||||
fVertices = sk_malloc_throw(vertexCount * stride());
|
||||
fVertices = sk_malloc_throw(eagerCount * stride);
|
||||
}
|
||||
fLockStride = stride;
|
||||
return fVertices;
|
||||
}
|
||||
void unlock(int actualCount) override {
|
||||
SkASSERT(fLockStride);
|
||||
if (fCanMapVB) {
|
||||
fVertexBuffer->unmap();
|
||||
} else {
|
||||
fVertexBuffer->updateData(fVertices, actualCount * stride());
|
||||
fVertexBuffer->updateData(fVertices, actualCount * fLockStride);
|
||||
sk_free(fVertices);
|
||||
}
|
||||
fVertices = nullptr;
|
||||
fLockStride = 0;
|
||||
}
|
||||
sk_sp<GrGpuBuffer> detachVertexBuffer() { return std::move(fVertexBuffer); }
|
||||
|
||||
@ -106,33 +116,7 @@ private:
|
||||
GrResourceProvider* fResourceProvider;
|
||||
bool fCanMapVB;
|
||||
void* fVertices;
|
||||
};
|
||||
|
||||
class DynamicVertexAllocator : public GrTessellator::VertexAllocator {
|
||||
public:
|
||||
DynamicVertexAllocator(size_t stride, GrMeshDrawOp::Target* target)
|
||||
: VertexAllocator(stride)
|
||||
, fTarget(target)
|
||||
, fVertexBuffer(nullptr)
|
||||
, fVertices(nullptr) {}
|
||||
void* lock(int vertexCount) override {
|
||||
fVertexCount = vertexCount;
|
||||
fVertices = fTarget->makeVertexSpace(stride(), vertexCount, &fVertexBuffer, &fFirstVertex);
|
||||
return fVertices;
|
||||
}
|
||||
void unlock(int actualCount) override {
|
||||
fTarget->putBackVertices(fVertexCount - actualCount, stride());
|
||||
fVertices = nullptr;
|
||||
}
|
||||
sk_sp<const GrBuffer> detachVertexBuffer() const { return std::move(fVertexBuffer); }
|
||||
int firstVertex() const { return fFirstVertex; }
|
||||
|
||||
private:
|
||||
GrMeshDrawOp::Target* fTarget;
|
||||
sk_sp<const GrBuffer> fVertexBuffer;
|
||||
int fVertexCount;
|
||||
int fFirstVertex;
|
||||
void* fVertices;
|
||||
size_t fLockStride = 0;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@ -255,7 +239,7 @@ private:
|
||||
return path;
|
||||
}
|
||||
|
||||
void draw(Target* target, const GrGeometryProcessor* gp, size_t vertexStride) {
|
||||
void draw(Target* target, const GrGeometryProcessor* gp) {
|
||||
SkASSERT(!fAntiAlias);
|
||||
GrResourceProvider* rp = target->resourceProvider();
|
||||
bool inverseFill = fShape.inverseFilled();
|
||||
@ -292,7 +276,7 @@ private:
|
||||
vmi.mapRect(&clipBounds);
|
||||
bool isLinear;
|
||||
bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
|
||||
StaticVertexAllocator allocator(vertexStride, rp, canMapVB);
|
||||
StaticVertexAllocator allocator(rp, canMapVB);
|
||||
int count = GrTessellator::PathToTriangles(getPath(), tol, clipBounds, &allocator, false,
|
||||
&isLinear);
|
||||
if (count == 0) {
|
||||
@ -309,7 +293,7 @@ private:
|
||||
this->drawVertices(target, gp, std::move(vb), 0, count);
|
||||
}
|
||||
|
||||
void drawAA(Target* target, const GrGeometryProcessor* gp, size_t vertexStride) {
|
||||
void drawAA(Target* target, const GrGeometryProcessor* gp) {
|
||||
SkASSERT(fAntiAlias);
|
||||
SkPath path = getPath();
|
||||
if (path.isEmpty()) {
|
||||
@ -318,15 +302,16 @@ private:
|
||||
SkRect clipBounds = SkRect::Make(fDevClipBounds);
|
||||
path.transform(fViewMatrix);
|
||||
SkScalar tol = GrPathUtils::kDefaultTolerance;
|
||||
sk_sp<const GrBuffer> vertexBuffer;
|
||||
int firstVertex;
|
||||
bool isLinear;
|
||||
DynamicVertexAllocator allocator(vertexStride, target);
|
||||
GrEagerDynamicVertexAllocator allocator(target, &vertexBuffer, &firstVertex);
|
||||
int count = GrTessellator::PathToTriangles(path, tol, clipBounds, &allocator, true,
|
||||
&isLinear);
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
this->drawVertices(target, gp, allocator.detachVertexBuffer(),
|
||||
allocator.firstVertex(), count);
|
||||
this->drawVertices(target, gp, std::move(vertexBuffer), firstVertex, count);
|
||||
}
|
||||
|
||||
void onPrepareDraws(Target* target) override {
|
||||
@ -362,11 +347,11 @@ private:
|
||||
if (!gp) {
|
||||
return;
|
||||
}
|
||||
size_t vertexStride = gp->vertexStride();
|
||||
SkASSERT(GrTessellator::GetVertexStride(fAntiAlias) == gp->vertexStride());
|
||||
if (fAntiAlias) {
|
||||
this->drawAA(target, gp, vertexStride);
|
||||
this->drawAA(target, gp);
|
||||
} else {
|
||||
this->draw(target, gp, vertexStride);
|
||||
this->draw(target, gp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "include/private/SkTArray.h"
|
||||
#include "src/core/SkPathPriv.h"
|
||||
#include "src/gpu/GrEagerVertexAllocator.h"
|
||||
|
||||
static SkPoint lerp(const SkPoint& a, const SkPoint& b, float T) {
|
||||
SkASSERT(1 != T); // The below does not guarantee lerp(a, b, 1) === b.
|
||||
@ -76,7 +77,19 @@ private:
|
||||
int fMidpointWeight;
|
||||
};
|
||||
|
||||
int GrPathParser::EmitCenterWedgePatches(const SkPath& path, SkPoint* patchData) {
|
||||
constexpr int max_wedge_vertex_count(int numPathVerbs) {
|
||||
// No initial moveTo, one wedge per verb, plus an implicit close at the end.
|
||||
// Each wedge has 5 vertices.
|
||||
return (numPathVerbs + 1) * 5;
|
||||
}
|
||||
|
||||
int GrPathParser::EmitCenterWedgePatches(const SkPath& path, GrEagerVertexAllocator* vertexAlloc) {
|
||||
int maxVertices = max_wedge_vertex_count(path.countVerbs());
|
||||
auto* vertexData = vertexAlloc->lock<SkPoint>(maxVertices);
|
||||
if (!vertexData) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vertexCount = 0;
|
||||
MidpointContourParser parser(path);
|
||||
while (parser.parseNextContour()) {
|
||||
@ -88,7 +101,7 @@ int GrPathParser::EmitCenterWedgePatches(const SkPath& path, SkPoint* patchData)
|
||||
case SkPathVerb::kDone:
|
||||
if (parser.startPoint() != lastPoint) {
|
||||
lastPoint = write_line_as_cubic(
|
||||
patchData + vertexCount, lastPoint, parser.startPoint());
|
||||
vertexData + vertexCount, lastPoint, parser.startPoint());
|
||||
break;
|
||||
} // fallthru
|
||||
default:
|
||||
@ -99,29 +112,29 @@ int GrPathParser::EmitCenterWedgePatches(const SkPath& path, SkPoint* patchData)
|
||||
continue;
|
||||
|
||||
case SkPathVerb::kLine:
|
||||
lastPoint = write_line_as_cubic(patchData + vertexCount, lastPoint,
|
||||
lastPoint = write_line_as_cubic(vertexData + vertexCount, lastPoint,
|
||||
parser.atPoint(ptsIdx));
|
||||
++ptsIdx;
|
||||
break;
|
||||
case SkPathVerb::kQuad:
|
||||
lastPoint = write_quadratic_as_cubic(patchData + vertexCount, lastPoint,
|
||||
lastPoint = write_quadratic_as_cubic(vertexData + vertexCount, lastPoint,
|
||||
parser.atPoint(ptsIdx),
|
||||
parser.atPoint(ptsIdx + 1));
|
||||
ptsIdx += 2;
|
||||
break;
|
||||
case SkPathVerb::kCubic:
|
||||
lastPoint = write_cubic(patchData + vertexCount, lastPoint,
|
||||
lastPoint = write_cubic(vertexData + vertexCount, lastPoint,
|
||||
parser.atPoint(ptsIdx), parser.atPoint(ptsIdx + 1),
|
||||
parser.atPoint(ptsIdx + 2));
|
||||
ptsIdx += 3;
|
||||
break;
|
||||
}
|
||||
patchData[vertexCount + 4] = parser.midpoint();
|
||||
vertexData[vertexCount + 4] = parser.midpoint();
|
||||
vertexCount += 5;
|
||||
}
|
||||
}
|
||||
|
||||
SkASSERT(vertexCount <= MaxWedgeVertices(path));
|
||||
vertexAlloc->unlock(vertexCount);
|
||||
return vertexCount;
|
||||
}
|
||||
|
||||
@ -147,8 +160,9 @@ static int emit_subpolygon(const SkPoint* points, int first, int last, SkPoint*
|
||||
|
||||
class InnerPolygonContourParser : public SkTPathContourParser<InnerPolygonContourParser> {
|
||||
public:
|
||||
InnerPolygonContourParser(const SkPath& path) : SkTPathContourParser(path) {
|
||||
fPolyPoints.reserve(GrPathParser::MaxInnerPolygonVertices(path));
|
||||
InnerPolygonContourParser(const SkPath& path, int vertexReserveCount)
|
||||
: SkTPathContourParser(path)
|
||||
, fPolyPoints(vertexReserveCount) {
|
||||
}
|
||||
|
||||
int emitInnerPolygon(SkPoint* vertexData) {
|
||||
@ -197,36 +211,51 @@ private:
|
||||
int fNumCurves;
|
||||
};
|
||||
|
||||
int GrPathParser::EmitInnerPolygonTriangles(const SkPath& path, SkPoint* vertexData,
|
||||
int* numCurves) {
|
||||
*numCurves = 0;
|
||||
int vertexCount = 0;
|
||||
InnerPolygonContourParser parser(path);
|
||||
while (parser.parseNextContour()) {
|
||||
vertexCount += parser.emitInnerPolygon(vertexData + vertexCount);
|
||||
*numCurves += parser.numCurves();
|
||||
constexpr int max_inner_poly_vertex_count(int numPathVerbs) {
|
||||
// No initial moveTo, plus an implicit close at the end; n-2 trianles fill an n-gon.
|
||||
// Each triangle has 3 vertices.
|
||||
return (numPathVerbs - 1) * 3;
|
||||
}
|
||||
|
||||
int GrPathParser::EmitInnerPolygonTriangles(const SkPath& path,
|
||||
GrEagerVertexAllocator* vertexAlloc) {
|
||||
int maxVertices = max_inner_poly_vertex_count(path.countVerbs());
|
||||
InnerPolygonContourParser parser(path, maxVertices);
|
||||
auto* vertexData = vertexAlloc->lock<SkPoint>(maxVertices);
|
||||
if (!vertexData) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SkASSERT(vertexCount <= MaxInnerPolygonVertices(path));
|
||||
int vertexCount = 0;
|
||||
while (parser.parseNextContour()) {
|
||||
vertexCount += parser.emitInnerPolygon(vertexData + vertexCount);
|
||||
}
|
||||
|
||||
vertexAlloc->unlock(vertexCount);
|
||||
return vertexCount;
|
||||
}
|
||||
|
||||
int GrPathParser::EmitCubicInstances(const SkPath& path, SkPoint* vertexData) {
|
||||
int GrPathParser::EmitCubicInstances(const SkPath& path, GrEagerVertexAllocator* vertexAlloc) {
|
||||
auto* instanceData = vertexAlloc->lock<std::array<SkPoint, 4>>(path.countVerbs());
|
||||
if (!instanceData) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int instanceCount = 0;
|
||||
SkPath::Iter iter(path, false);
|
||||
SkPath::Verb verb;
|
||||
SkPoint pts[4];
|
||||
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
|
||||
if (SkPath::kQuad_Verb == verb) {
|
||||
write_quadratic_as_cubic(vertexData + (instanceCount * 4), pts[0], pts[1], pts[2]);
|
||||
++instanceCount;
|
||||
write_quadratic_as_cubic(instanceData[instanceCount++].data(), pts[0], pts[1], pts[2]);
|
||||
continue;
|
||||
}
|
||||
if (SkPath::kCubic_Verb == verb) {
|
||||
write_cubic(vertexData + (instanceCount * 4), pts[0], pts[1], pts[2], pts[3]);
|
||||
++instanceCount;
|
||||
instanceData[instanceCount++] = {pts[0], pts[1], pts[2], pts[3]};
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
vertexAlloc->unlock(instanceCount);
|
||||
return instanceCount;
|
||||
}
|
||||
|
@ -10,48 +10,35 @@
|
||||
|
||||
#include "include/core/SkPath.h"
|
||||
|
||||
class GrEagerVertexAllocator;
|
||||
|
||||
namespace GrPathParser {
|
||||
|
||||
// Returns the maximum number of vertices that can be written by EmitCenterWedges() for the given
|
||||
// path.
|
||||
inline int MaxWedgeVertices(const SkPath& path) {
|
||||
// No initial moveTo, one wedge per verb, plus an implicit close at the end.
|
||||
// Each wedge has 5 vertices.
|
||||
return (path.countVerbs() + 1) * 5;
|
||||
}
|
||||
|
||||
// Writes an array of cubic "wedges" from an SkPath, converting any lines or quadratics to cubics.
|
||||
// These wedges can then be fed into GrTessellateWedgeShader to stencil the path. A wedge is a
|
||||
// 5-point tessellation patch consisting of 4 cubic control points, plus an anchor point fanning
|
||||
// from the center of the curve's resident contour.
|
||||
// These wedges can then be fed into GrStencilWedgeShader to stencil the path. A wedge is a 5-point
|
||||
// tessellation patch consisting of 4 cubic control points, plus an anchor point fanning from the
|
||||
// center of the curve's resident contour.
|
||||
//
|
||||
// TODO: Eventually we want to use rational cubic wedges in order to support conics.
|
||||
//
|
||||
// Returns the number of vertices written to the array.
|
||||
//
|
||||
// The incoming patchData array must have at least MaxWedgeVertices() elements.
|
||||
int EmitCenterWedgePatches(const SkPath&, SkPoint* patchData);
|
||||
int EmitCenterWedgePatches(const SkPath&, GrEagerVertexAllocator*);
|
||||
|
||||
// Returns the maximum number of vertices required to triangulate the given path's inner polygon(s).
|
||||
inline int MaxInnerPolygonVertices(const SkPath& path) {
|
||||
// No initial moveTo, plus an implicit close at the end; n-2 trianles fill an n-gon.
|
||||
// Each triangle has 3 vertices.
|
||||
return (path.countVerbs() - 1) * 3;
|
||||
}
|
||||
|
||||
// Triangulates the path's inner polygon(s) and writes the result to "vertexData". The inner
|
||||
// polygons connect the endpoints of each verb. (i.e., they are the path that would result from
|
||||
// collapsing all curves to single lines.)
|
||||
// Triangulates and writes an SkPath's inner polygon(s). The inner polygons connect the endpoints of
|
||||
// each verb. (i.e., they are the path that would result from collapsing all curves to single
|
||||
// lines.)
|
||||
//
|
||||
// This method works by recursively subdividing the path rather than emitting a linear triangle fan
|
||||
// or strip. This can reduce the load on the rasterizer by a great deal on complex paths.
|
||||
//
|
||||
// Returns the number of vertices written to the array.
|
||||
//
|
||||
// The incoming vertexData array must have at least MaxInnerPolygonVertices() elements.
|
||||
int EmitInnerPolygonTriangles(const SkPath&, SkPoint* vertexData, int* numCurves);
|
||||
int EmitInnerPolygonTriangles(const SkPath&, GrEagerVertexAllocator*);
|
||||
|
||||
int EmitCubicInstances(const SkPath&, SkPoint* vertexData);
|
||||
// Writes out an array of cubics from an SkPath as 4-point instances, converting any quadratics to
|
||||
// cubics.
|
||||
//
|
||||
// Returns the number of *instances* written to the array.
|
||||
int EmitCubicInstances(const SkPath&, GrEagerVertexAllocator*);
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "src/gpu/tessellate/GrTessellatePathOp.h"
|
||||
|
||||
#include "src/gpu/GrEagerVertexAllocator.h"
|
||||
#include "src/gpu/GrGpu.h"
|
||||
#include "src/gpu/GrOpFlushState.h"
|
||||
#include "src/gpu/GrOpsRenderPass.h"
|
||||
@ -24,6 +25,10 @@ GrTessellatePathOp::FixedFunctionFlags GrTessellatePathOp::fixedFunctionFlags()
|
||||
}
|
||||
|
||||
void GrTessellatePathOp::onPrepare(GrOpFlushState* state) {
|
||||
GrEagerDynamicVertexAllocator pathVertexAllocator(state, &fPathVertexBuffer, &fBasePathVertex);
|
||||
GrEagerDynamicVertexAllocator cubicInstanceAllocator(state, &fCubicInstanceBuffer,
|
||||
&fBaseCubicInstance);
|
||||
|
||||
// First see if we should split up inner polygon triangles and curves, and triangulate the inner
|
||||
// polygon(s) more efficiently. This causes greater CPU overhead due to the extra shaders and
|
||||
// draw calls, but the better triangulation can reduce the rasterizer load by a great deal on
|
||||
@ -33,39 +38,15 @@ void GrTessellatePathOp::onPrepare(GrOpFlushState* state) {
|
||||
// Raster-edge work is 1-dimensional, so we sum height and width rather than multiplying them.
|
||||
float rasterEdgeWork = (bounds.height() + bounds.width()) * scale * fPath.countVerbs();
|
||||
if (rasterEdgeWork > 1000 * 1000) {
|
||||
int numCurves = 0;
|
||||
int maxVertexCount = GrPathParser::MaxInnerPolygonVertices(fPath);
|
||||
if (auto* triangleData = (SkPoint*)state->makeVertexSpace(
|
||||
sizeof(SkPoint), maxVertexCount, &fPathVertexBuffer, &fBasePathVertex)) {
|
||||
if ((fPathVertexCount =
|
||||
GrPathParser::EmitInnerPolygonTriangles(fPath, triangleData, &numCurves))) {
|
||||
fPathShader = state->allocator()->make<GrStencilTriangleShader>(fViewMatrix);
|
||||
} else {
|
||||
fPathVertexBuffer.reset();
|
||||
}
|
||||
state->putBackVertices(maxVertexCount - fPathVertexCount, sizeof(SkPoint));
|
||||
}
|
||||
if (numCurves) {
|
||||
if (auto* cubicData = (SkPoint*)state->makeVertexSpace(
|
||||
sizeof(SkPoint) * 4, numCurves, &fCubicInstanceBuffer, &fBaseCubicInstance)) {
|
||||
fCubicInstanceCount = GrPathParser::EmitCubicInstances(fPath, cubicData);
|
||||
SkASSERT(fCubicInstanceCount == numCurves);
|
||||
}
|
||||
}
|
||||
fPathShader = state->allocator()->make<GrStencilTriangleShader>(fViewMatrix);
|
||||
fPathVertexCount = GrPathParser::EmitInnerPolygonTriangles(fPath, &pathVertexAllocator);
|
||||
fCubicInstanceCount = GrPathParser::EmitCubicInstances(fPath, &cubicInstanceAllocator);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
|
||||
int maxVertexCount = GrPathParser::MaxWedgeVertices(fPath);
|
||||
if (auto* patchData = (SkPoint*)state->makeVertexSpace(
|
||||
sizeof(SkPoint), maxVertexCount, &fPathVertexBuffer, &fBasePathVertex)) {
|
||||
if ((fPathVertexCount = GrPathParser::EmitCenterWedgePatches(fPath, patchData))) {
|
||||
fPathShader = state->allocator()->make<GrStencilWedgeShader>(fViewMatrix);
|
||||
} else {
|
||||
fPathVertexBuffer.reset();
|
||||
}
|
||||
state->putBackVertices(maxVertexCount - fPathVertexCount, sizeof(SkPoint));
|
||||
}
|
||||
fPathShader = state->allocator()->make<GrStencilWedgeShader>(fViewMatrix);
|
||||
fPathVertexCount = GrPathParser::EmitCenterWedgePatches(fPath, &pathVertexAllocator);
|
||||
}
|
||||
|
||||
void GrTessellatePathOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
|
||||
|
@ -67,6 +67,9 @@ private:
|
||||
SkPMColor4f fColor;
|
||||
GrProcessorSet fProcessors;
|
||||
|
||||
// The "path shader" draws the below path geometry.
|
||||
GrStencilPathShader* fPathShader;
|
||||
|
||||
// The "path vertex buffer" is made up of either inner polygon triangles (see
|
||||
// GrPathParser::EmitInnerPolygonTriangles) or cubic wedge patches (see
|
||||
// GrPathParser::EmitCenterWedgePatches).
|
||||
@ -74,9 +77,6 @@ private:
|
||||
int fBasePathVertex;
|
||||
int fPathVertexCount;
|
||||
|
||||
// The "path shader" draws the above path geometry.
|
||||
GrStencilPathShader* fPathShader;
|
||||
|
||||
// The cubic instance buffer defines standalone cubics to tessellate into the stencil buffer, in
|
||||
// addition to the above path geometry.
|
||||
sk_sp<const GrBuffer> fCubicInstanceBuffer;
|
||||
|
Loading…
Reference in New Issue
Block a user