Move breadcrumb and inner-fan triangles out of PathCurveTessellator

The callsites output these triangles now. This removes the final
direct Ganesh dependency from the tessellator code.

Bug: skia:12524
Change-Id: Id0d16839ed35a2bd9a812b8747ca8669fac5bed8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/470196
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
This commit is contained in:
Chris Dalton 2021-11-10 20:36:03 -07:00 committed by SkCQ
parent 5182ed33a9
commit 09ece934af
12 changed files with 205 additions and 222 deletions

View File

@ -143,13 +143,13 @@ DEF_PATH_TESS_BENCH(GrPathCurveTessellator, make_cubic_path(8), SkMatrix::I()) {
GrPipeline noVaryingsPipeline(GrScissorTest::kDisabled, SkBlendMode::kSrcOver,
GrSwizzle::RGBA());
auto tess = PathCurveTessellator::Make(&arena,
PathCurveTessellator::DrawInnerFan::kNo,
fTarget->caps().shaderCaps()->infinitySupport());
tess->prepare(fTarget.get(),
1 << PathCurveTessellator::kMaxFixedResolveLevel,
fMatrix,
{gAlmostIdentity, fPath, SK_PMColor4fTRANSPARENT},
fPath.countVerbs());
fPath.countVerbs(),
true);
}
DEF_PATH_TESS_BENCH(GrPathWedgeTessellator, make_cubic_path(8), SkMatrix::I()) {
@ -162,7 +162,8 @@ DEF_PATH_TESS_BENCH(GrPathWedgeTessellator, make_cubic_path(8), SkMatrix::I()) {
1 << PathCurveTessellator::kMaxFixedResolveLevel,
fMatrix,
{gAlmostIdentity, fPath, SK_PMColor4fTRANSPARENT},
fPath.countVerbs());
fPath.countVerbs(),
true);
}
static void benchmark_wangs_formula_cubic_log2(const SkMatrix& matrix, const SkPath& path) {

View File

@ -17,6 +17,7 @@
#include "src/gpu/ops/GrDrawOp.h"
#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
#include "src/gpu/ops/TessellationPathRenderer.h"
#include "src/gpu/tessellate/AffineMatrix.h"
#include "src/gpu/tessellate/PathCurveTessellator.h"
#include "src/gpu/tessellate/PathWedgeTessellator.h"
#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
@ -24,6 +25,8 @@
namespace skgpu {
using TrianglePatch = PatchWriter::TrianglePatch;
namespace {
enum class Mode {
@ -86,27 +89,29 @@ private:
auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState, std::move(fProcessors),
fPipelineFlags);
int numVerbs;
bool needsInnerFan;
switch (fMode) {
using DrawInnerFan = PathCurveTessellator::DrawInnerFan;
case Mode::kWedgeMiddleOut:
fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
numVerbs = numVerbsToGetMiddleOut;
needsInnerFan = false;
break;
case Mode::kCurveMiddleOut:
fTessellator = PathCurveTessellator::Make(alloc,
DrawInnerFan::kYes,
shaderCaps.infinitySupport());
numVerbs = numVerbsToGetMiddleOut;
needsInnerFan = true;
break;
case Mode::kWedgeTessellate:
fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
numVerbs = numVerbsToGetTessellation;
needsInnerFan = false;
break;
case Mode::kCurveTessellate:
fTessellator = PathCurveTessellator::Make(alloc,
DrawInnerFan::kYes,
shaderCaps.infinitySupport());
numVerbs = numVerbsToGetTessellation;
needsInnerFan = true;
break;
}
auto* tessShader = GrPathTessellationShader::Make(alloc,
@ -116,14 +121,6 @@ private:
*pipeline,
fTessellator->patchAttribs(),
caps);
fTessellator->prepare(flushState,
tessShader->maxTessellationSegments(*caps.shaderCaps()),
shaderMatrix,
{pathMatrix, fPath, kCyan},
fPath.countVerbs());
if (!tessShader->willUseTessellationShaders()) {
fTessellator->prepareFixedCountBuffers(flushState->resourceProvider());
}
fProgram = GrTessellationShader::MakeProgram({alloc, flushState->writeView(),
flushState->usesMSAASurface(),
&flushState->dstProxyView(),
@ -132,6 +129,34 @@ private:
tessShader,
pipeline,
&GrUserStencilSettings::kUnused);
int patchPreallocCount = fTessellator->patchPreallocCount(fPath.countVerbs());
if (needsInnerFan) {
patchPreallocCount += fPath.countVerbs() - 1;
}
PatchWriter patchWriter(flushState, fTessellator, patchPreallocCount);
if (needsInnerFan) {
// Write out inner fan triangles.
AffineMatrix m(pathMatrix);
for (PathMiddleOutFanIter it(fPath); !it.done();) {
for (auto [p0, p1, p2] : it.nextStack()) {
TrianglePatch(patchWriter) << m.map2Points(p0, p1) << m.mapPoint(p2);
}
}
}
// Write out the curves.
fTessellator->writePatches(patchWriter,
tessShader->maxTessellationSegments(*caps.shaderCaps()),
shaderMatrix,
{pathMatrix, fPath, kCyan});
if (!tessShader->willUseTessellationShaders()) {
fTessellator->prepareFixedCountBuffers(flushState);
}
}
void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
flushState->bindPipeline(*fProgram, chainBounds);

View File

@ -251,7 +251,6 @@ void PathInnerTriangulateOp::prePreparePrograms(const GrTessellationShader::Prog
// Pass 1: Tessellate the outer curves into the stencil buffer.
if (!isLinear) {
fTessellator = PathCurveTessellator::Make(args.fArena,
PathCurveTessellator::DrawInnerFan::kNo,
args.fCaps->shaderCaps()->infinitySupport());
auto* tessShader = GrPathTessellationShader::Make(args.fArena,
fViewMatrix,
@ -401,11 +400,13 @@ void PathInnerTriangulateOp::onPrePrepare(GrRecordingContext* context,
GR_DECLARE_STATIC_UNIQUE_KEY(gHullVertexBufferKey);
void PathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
const GrCaps& caps = flushState->caps();
if (!fFanTriangulator) {
this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
flushState->usesMSAASurface(), &flushState->dstProxyView(),
flushState->renderPassBarriers(), flushState->colorLoadOp(),
&flushState->caps()}, flushState->detachAppliedClip());
&caps}, flushState->detachAppliedClip());
if (!fFanTriangulator) {
return;
}
@ -417,20 +418,45 @@ void PathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
}
if (fTessellator) {
// Must be called after polysToTriangles() in order for fFanBreadcrumbs to be complete.
int patchPreallocCount = fFanBreadcrumbs.count() +
fTessellator->patchPreallocCount(fPath.countVerbs());
SkASSERT(patchPreallocCount); // Otherwise fTessellator should be null.
PatchWriter patchWriter(flushState, fTessellator, patchPreallocCount);
// Write out breadcrumb triangles. This must be called after polysToTriangles() in order for
// fFanBreadcrumbs to be complete.
SkDEBUGCODE(int breadcrumbCount = 0;)
for (const auto* tri = fFanBreadcrumbs.head(); tri; tri = tri->fNext) {
SkDEBUGCODE(++breadcrumbCount;)
auto p0 = float2::Load(tri->fPts);
auto p1 = float2::Load(tri->fPts + 1);
auto p2 = float2::Load(tri->fPts + 2);
if (skvx::any((p0 == p1) & (p1 == p2))) {
// Cull completely horizontal or vertical triangles. GrTriangulator can't always
// get these breadcrumb edges right when they run parallel to the sweep
// direction because their winding is undefined by its current definition.
// FIXME(skia:12060): This seemed safe, but if there is a view matrix it will
// introduce T-junctions.
continue;
}
PatchWriter::TrianglePatch(patchWriter) << p0 << p1 << p2;
}
SkASSERT(breadcrumbCount == fFanBreadcrumbs.count());
// Write out the curves.
auto tessShader = &fStencilCurvesProgram->geomProc().cast<GrPathTessellationShader>();
fTessellator->prepare(flushState,
tessShader->maxTessellationSegments(*flushState->caps().shaderCaps()),
tessShader->viewMatrix(),
{SkMatrix::I(), fPath, SK_PMColor4fTRANSPARENT},
fPath.countVerbs(),
&fFanBreadcrumbs);
fTessellator->writePatches(patchWriter,
tessShader->maxTessellationSegments(*caps.shaderCaps()),
tessShader->viewMatrix(),
{SkMatrix::I(), fPath, SK_PMColor4fTRANSPARENT});
if (!tessShader->willUseTessellationShaders()) {
fTessellator->prepareFixedCountBuffers(flushState->resourceProvider());
fTessellator->prepareFixedCountBuffers(flushState);
}
}
if (!flushState->caps().shaderCaps()->vertexIDSupport()) {
if (!caps.shaderCaps()->vertexIDSupport()) {
constexpr static float kStripOrderIDs[4] = {0, 1, 3, 2};
GR_DEFINE_STATIC_UNIQUE_KEY(gHullVertexBufferKey);

View File

@ -158,7 +158,6 @@ void PathStencilCoverOp::prePreparePrograms(const GrTessellationShader::ProgramA
stencilPipeline,
stencilSettings);
fTessellator = PathCurveTessellator::Make(args.fArena,
PathCurveTessellator::DrawInnerFan::kNo,
args.fCaps->shaderCaps()->infinitySupport());
} else {
fTessellator = PathWedgeTessellator::Make(args.fArena,
@ -264,10 +263,8 @@ void PathStencilCoverOp::onPrepare(GrOpFlushState* flushState) {
tessShader->maxTessellationSegments(*flushState->caps().shaderCaps()),
tessShader->viewMatrix(),
*fPathDrawList,
fTotalCombinedPathVerbCnt);
if (!tessShader->willUseTessellationShaders()) {
fTessellator->prepareFixedCountBuffers(flushState->resourceProvider());
}
fTotalCombinedPathVerbCnt,
tessShader->willUseTessellationShaders());
if (fCoverBBoxProgram) {
size_t instanceStride = fCoverBBoxProgram->geomProc().instanceStride();

View File

@ -114,10 +114,8 @@ void PathTessellateOp::onPrepare(GrOpFlushState* flushState) {
tessShader->maxTessellationSegments(*flushState->caps().shaderCaps()),
fShaderMatrix,
*fPathDrawList,
fTotalCombinedPathVerbCnt);
if (!tessShader->willUseTessellationShaders()) {
fTessellator->prepareFixedCountBuffers(flushState->resourceProvider());
}
fTotalCombinedPathVerbCnt,
tessShader->willUseTessellationShaders());
}
void PathTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {

View File

@ -7,10 +7,23 @@
#include "src/gpu/tessellate/PatchWriter.h"
#include "src/gpu/tessellate/PathTessellator.h"
namespace skgpu {
SK_ALWAYS_INLINE SkPoint to_skpoint(float2 p) { return skvx::bit_pun<SkPoint>(p); }
#if SK_GPU_V1
PatchWriter::PatchWriter(GrMeshDrawTarget* target,
PathTessellator* tessellator,
int initialPatchAllocCount)
: PatchWriter(target,
&tessellator->fVertexChunkArray,
tessellator->fAttribs,
initialPatchAllocCount) {
}
#endif
void PatchWriter::chopAndWriteQuads(float2 p0, float2 p1, float2 p2, int numPatches) {
// If we aren't fanning, we need to fill the space between chops with triangles.
bool needsInnerTriangles = !(fPatchAttribs & PatchAttribs::kFanPoint);

View File

@ -15,18 +15,27 @@
namespace skgpu {
#if SK_GPU_V1
class PathTessellator;
#endif
// Writes out tessellation patches, formatted with their specific attribs, to a GPU buffer.
class PatchWriter {
public:
PatchWriter(GrMeshDrawTarget* target,
GrVertexChunkArray* vertexChunkArray,
size_t patchStride,
int initialPatchAllocCount,
PatchAttribs attribs)
PatchAttribs attribs,
int initialAllocCount)
: fPatchAttribs(attribs)
, fChunker(target, vertexChunkArray, patchStride, initialPatchAllocCount) {
, fChunker(target, vertexChunkArray, PatchStride(fPatchAttribs), initialAllocCount) {
}
#if SK_GPU_V1
// Creates a PatchWriter that writes directly to the GrVertexChunkArray stored on the provided
// PathTessellator.
PatchWriter(GrMeshDrawTarget*, PathTessellator* tessellator, int initialPatchAllocCount);
#endif
// Updates the fan point that will be written out with each patch (i.e., the point that wedges
// fan around).
// PathPatchAttrib::kFanPoint must be enabled.

View File

@ -25,85 +25,18 @@ using CubicPatch = PatchWriter::CubicPatch;
using ConicPatch = PatchWriter::ConicPatch;
using TrianglePatch = PatchWriter::TrianglePatch;
void PathCurveTessellator::prepare(GrMeshDrawTarget* target,
int maxTessellationSegments,
const SkMatrix& shaderMatrix,
const PathDrawList& pathDrawList,
int totalCombinedPathVerbCnt,
const BreadcrumbTriangleList* breadcrumbTriangleList) {
SkASSERT(fVertexChunkArray.empty());
SkASSERT(!fFixedResolveLevel);
// Determine how many triangles to allocate.
int maxTriangles = 0;
if (fDrawInnerFan) {
int maxCombinedFanEdges = MaxCombinedFanEdgesInPathDrawList(totalCombinedPathVerbCnt);
// A single n-sided polygon is fanned by n-2 triangles. Multiple polygons with a combined
// edge count of n are fanned by strictly fewer triangles.
int maxTrianglesInFans = std::max(maxCombinedFanEdges - 2, 0);
maxTriangles += maxTrianglesInFans;
}
if (breadcrumbTriangleList) {
maxTriangles += breadcrumbTriangleList->count();
}
int PathCurveTessellator::patchPreallocCount(int totalCombinedPathVerbCnt) const {
// Over-allocate enough curves for 1 in 4 to chop.
int curveAllocCount = (totalCombinedPathVerbCnt * 5 + 3) / 4; // i.e., ceil(numVerbs * 5/4)
int patchAllocCount = maxTriangles + curveAllocCount;
if (!patchAllocCount) {
return;
}
PatchWriter patchWriter(target, &fVertexChunkArray, PatchStride(fAttribs), patchAllocCount,
fAttribs);
// Write out inner fan triangles.
if (fDrawInnerFan) {
for (auto [pathMatrix, path, color] : pathDrawList) {
AffineMatrix m(pathMatrix);
if (fAttribs & PatchAttribs::kColor) {
patchWriter.updateColorAttrib(color);
}
for (PathMiddleOutFanIter it(path); !it.done();) {
for (auto [p0, p1, p2] : it.nextStack()) {
TrianglePatch(patchWriter) << m.map2Points(p0, p1) << m.mapPoint(p2);
}
}
}
}
// Write out breadcrumb triangles.
if (breadcrumbTriangleList) {
SkDEBUGCODE(int count = 0;)
#ifdef SK_DEBUG
for (auto [pathMatrix, path, color] : pathDrawList) {
// This assert isn't actually necessary, but we currently only use breadcrumb triangles
// with an identity pathMatrix and transparent color. If that ever changes, this assert
// will serve as a gentle reminder to make sure the breadcrumb triangles are also
// transformed on the CPU.
SkASSERT(pathMatrix.isIdentity());
SkASSERT(color == SK_PMColor4fTRANSPARENT);
}
#endif
if (fAttribs & PatchAttribs::kColor) {
patchWriter.updateColorAttrib(SK_PMColor4fTRANSPARENT);
}
for (const auto* tri = breadcrumbTriangleList->head(); tri; tri = tri->fNext) {
SkDEBUGCODE(++count;)
auto p0 = float2::Load(tri->fPts);
auto p1 = float2::Load(tri->fPts + 1);
auto p2 = float2::Load(tri->fPts + 2);
if (skvx::any((p0 == p1) & (p1 == p2))) {
// Cull completely horizontal or vertical triangles. GrTriangulator can't always
// get these breadcrumb edges right when they run parallel to the sweep
// direction because their winding is undefined by its current definition.
// FIXME(skia:12060): This seemed safe, but if there is a view matrix it will
// introduce T-junctions.
continue;
}
TrianglePatch(patchWriter) << p0 << p1 << p2;
}
SkASSERT(count == breadcrumbTriangleList->count());
}
int approxNumChops = (totalCombinedPathVerbCnt + 3) / 4;
// Every chop introduces 2 new patches: another curve patch and a triangle patch that glues the
// two chops together.
return totalCombinedPathVerbCnt + approxNumChops * 2;
}
void PathCurveTessellator::writePatches(PatchWriter& patchWriter,
int maxTessellationSegments,
const SkMatrix& shaderMatrix,
const PathDrawList& pathDrawList) {
float maxSegments_pow2 = pow2(maxTessellationSegments);
float maxSegments_pow4 = pow2(maxSegments_pow2);
@ -193,8 +126,9 @@ void PathCurveTessellator::prepare(GrMeshDrawTarget* target,
// log16(n^4) == log2(n).
// We already chopped curves to make sure none needed a higher resolveLevel than
// kMaxFixedResolveLevel.
fFixedResolveLevel = std::min(wangs_formula::nextlog16(numFixedSegments_pow4),
int(kMaxFixedResolveLevel));
fFixedResolveLevel = SkTPin(wangs_formula::nextlog16(numFixedSegments_pow4),
fFixedResolveLevel,
int(kMaxFixedResolveLevel));
}
void PathCurveTessellator::WriteFixedVertexBuffer(VertexWriter vertexWriter, size_t bufferSize) {
@ -280,7 +214,9 @@ void PathCurveTessellator::WriteFixedIndexBufferBaseIndex(VertexWriter vertexWri
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedVertexBufferKey);
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedIndexBufferKey);
void PathCurveTessellator::prepareFixedCountBuffers(GrResourceProvider* rp) {
void PathCurveTessellator::prepareFixedCountBuffers(GrMeshDrawTarget* target) {
GrResourceProvider* rp = target->resourceProvider();
GR_DEFINE_STATIC_UNIQUE_KEY(gFixedVertexBufferKey);
fFixedVertexBuffer = rp->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,

View File

@ -8,59 +8,31 @@
#ifndef tessellate_PathCurveTessellator_DEFINED
#define tessellate_PathCurveTessellator_DEFINED
#include "src/gpu/GrVertexChunkArray.h"
#include "src/gpu/tessellate/PathTessellator.h"
class GrCaps;
class GrPipeline;
namespace skgpu {
// Draws an array of "outer curve" patches and, optionally, inner fan triangles for
// GrCubicTessellateShader. Each patch is an independent 4-point curve, representing either a cubic
// or a conic. Quadratics are converted to cubics and triangles are converted to conics with w=Inf.
// Draws an array of "outer curve" patches. Each patch is an independent 4-point curve, representing
// either a cubic or a conic. Quadratics are converted to cubics and triangles are converted to
// conics with w=Inf.
class PathCurveTessellator final : public PathTessellator {
public:
// If DrawInnerFan is kNo, this class only emits the path's outer curves. In that case the
// caller is responsible to handle the path's inner fan.
enum class DrawInnerFan : bool {
kNo = false,
kYes
};
static PathCurveTessellator* Make(SkArenaAlloc* arena,
DrawInnerFan drawInnerFan,
bool infinitySupport,
PatchAttribs attribs = PatchAttribs::kNone) {
return arena->make<PathCurveTessellator>(drawInnerFan, infinitySupport, attribs);
return arena->make<PathCurveTessellator>(infinitySupport, attribs);
}
PathCurveTessellator(DrawInnerFan drawInnerFan,
bool infinitySupport,
PathCurveTessellator(bool infinitySupport,
PatchAttribs attribs = PatchAttribs::kNone)
: PathTessellator(infinitySupport, attribs)
, fDrawInnerFan(drawInnerFan != DrawInnerFan::kNo) {}
: PathTessellator(infinitySupport, attribs) {}
void prepare(GrMeshDrawTarget* target,
int maxTessellationSegments,
const SkMatrix& shaderMatrix,
const PathDrawList& pathDrawList,
int totalCombinedPathVerbCnt) final {
this->prepare(target, maxTessellationSegments, shaderMatrix, pathDrawList,
totalCombinedPathVerbCnt, nullptr);
}
int patchPreallocCount(int totalCombinedPathVerbCnt) const final;
// Implements PathTessellator::prepare(), also sending an additional list of breadcrumb
// triangles to the GPU. The breadcrumb triangles are implemented as conics with w=Infinity.
//
// ALSO NOTE: The breadcrumb triangles do not have a matrix. These need to be pre-transformed by
// the caller if a CPU-side transformation is desired.
void prepare(GrMeshDrawTarget*,
int maxTessellationSegments,
const SkMatrix& shaderMatrix,
const PathDrawList&,
int totalCombinedPathVerbCnt,
const BreadcrumbTriangleList*);
void writePatches(PatchWriter& patchWriter,
int maxTessellationSegments,
const SkMatrix& shaderMatrix,
const PathDrawList& pathDrawList) final;
// Size of the vertex buffer to use when rendering with a fixed count shader.
constexpr static int FixedVertexBufferSize(int maxFixedResolveLevel) {
@ -83,7 +55,7 @@ public:
static void WriteFixedIndexBufferBaseIndex(VertexWriter, size_t bufferSize, uint16_t baseIndex);
#if SK_GPU_V1
void prepareFixedCountBuffers(GrResourceProvider*) final;
void prepareFixedCountBuffers(GrMeshDrawTarget*) final;
void drawTessellated(GrOpFlushState*) const final;
void drawFixedCount(GrOpFlushState*) const final;
@ -93,10 +65,6 @@ public:
// pipeline ahead of time.
void drawHullInstances(GrOpFlushState*, sk_sp<const GrGpuBuffer> vertexBufferIfNeeded) const;
#endif
private:
const bool fDrawInnerFan;
GrVertexChunkArray fVertexChunkArray;
};
} // namespace skgpu

View File

@ -8,30 +8,29 @@
#ifndef tessellate_PathTessellator_DEFINED
#define tessellate_PathTessellator_DEFINED
#include "src/core/SkPathPriv.h"
#include "src/gpu/BufferWriter.h"
#include "src/gpu/GrVx.h"
#include "src/gpu/geometry/GrInnerFanTriangulator.h"
#include "src/gpu/tessellate/Tessellation.h"
class SkPath;
class GrMeshDrawTarget;
#if SK_GPU_V1
#include "src/gpu/GrVertexChunkArray.h"
#include "src/gpu/tessellate/PatchWriter.h"
class GrGpuBuffer;
class GrMeshDrawTarget;
class GrOpFlushState;
class GrResourceProvider;
#endif
namespace skgpu {
class PatchWriter;
// Prepares GPU data for, and then draws a path's tessellated geometry. Depending on the subclass,
// the caller may or may not be required to draw the path's inner fan separately.
class PathTessellator {
public:
using BreadcrumbTriangleList = GrInnerFanTriangulator::BreadcrumbTriangleList;
// This is the maximum number of segments contained in our vertex and index buffers for
// fixed-count rendering. If rendering in fixed-count mode and a curve requires more segments,
// it must be chopped.
@ -65,24 +64,38 @@ public:
PatchAttribs patchAttribs() const { return fAttribs; }
// Called before draw(). Prepares GPU buffers containing the geometry to tessellate.
// Gives an approximate initial buffer size for this class to write patches into. Ideally the
// whole path will fit into this initial buffer, but if it requires a lot of chopping, the
// PatchWriter will allocate more buffer(s).
virtual int patchPreallocCount(int totalCombinedPathVerbCnt) const = 0;
// Writes out patches to the given PatchWriter, chopping as necessary so the curves all fit in
// maxTessellationSegments or fewer.
//
// Each path's fPathMatrix in the list is applied on the CPU while the geometry is being written
// out. This is a tool for batching, and is applied in addition to the shader's on-GPU matrix.
virtual void prepare(GrMeshDrawTarget*,
int maxTessellationSegments,
const SkMatrix& shaderMatrix,
const PathDrawList&,
int totalCombinedPathVerbCnt) = 0;
virtual void writePatches(PatchWriter&,
int maxTessellationSegments,
const SkMatrix& shaderMatrix,
const PathDrawList&) = 0;
#if SK_GPU_V1
virtual void prepareFixedCountBuffers(GrResourceProvider*) = 0;
// Initializes the internal vertex and index buffers required for drawFixedCount().
virtual void prepareFixedCountBuffers(GrMeshDrawTarget*) = 0;
void draw(GrOpFlushState* flushState, bool willUseTessellationShaders) {
if (willUseTessellationShaders) {
this->drawTessellated(flushState);
} else {
this->drawFixedCount(flushState);
// Called before draw(). Prepares GPU buffers containing the geometry to tessellate.
void prepare(GrMeshDrawTarget* target,
int maxTessellationSegments,
const SkMatrix& shaderMatrix,
const PathDrawList& pathDrawList,
int totalCombinedPathVerbCnt,
bool willUseTessellationShaders) {
if (int patchPreallocCount = this->patchPreallocCount(totalCombinedPathVerbCnt)) {
PatchWriter patchWriter(target, this, patchPreallocCount);
this->writePatches(patchWriter, maxTessellationSegments, shaderMatrix, pathDrawList);
}
if (!willUseTessellationShaders) {
this->prepareFixedCountBuffers(target);
}
}
@ -93,6 +106,14 @@ public:
// Issues fixed-count instanced draw calls over the patches. The caller is responsible for
// binding its desired pipeline ahead of time.
virtual void drawFixedCount(GrOpFlushState*) const = 0;
void draw(GrOpFlushState* flushState, bool willUseTessellationShaders) {
if (willUseTessellationShaders) {
this->drawTessellated(flushState);
} else {
this->drawFixedCount(flushState);
}
}
#endif
// Returns an upper bound on the number of combined edges there might be from all inner fans in
@ -129,6 +150,10 @@ protected:
int fFixedResolveLevel = 0;
#if SK_GPU_V1
friend class PatchWriter; // To access fVertexChunkArray.
GrVertexChunkArray fVertexChunkArray;
// If using fixed-count rendering, these are the vertex and index buffers.
sk_sp<const GrGpuBuffer> fFixedVertexBuffer;
sk_sp<const GrGpuBuffer> fFixedIndexBuffer;

View File

@ -126,23 +126,16 @@ private:
} // namespace
void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
int maxTessellationSegments,
const SkMatrix& shaderMatrix,
const PathDrawList& pathDrawList,
int totalCombinedPathVerbCnt) {
SkASSERT(fVertexChunkArray.empty());
SkASSERT(!fFixedResolveLevel);
int PathWedgeTessellator::patchPreallocCount(int totalCombinedPathVerbCnt) const {
// Over-allocate enough wedges for 1 in 4 to chop.
int maxWedges = MaxCombinedFanEdgesInPathDrawList(totalCombinedPathVerbCnt);
int wedgeAllocCount = (maxWedges * 5 + 3) / 4; // i.e., ceil(maxWedges * 5/4)
if (!wedgeAllocCount) {
return;
}
PatchWriter patchWriter(target, &fVertexChunkArray, PatchStride(fAttribs), wedgeAllocCount,
fAttribs);
return (maxWedges * 5 + 3) / 4; // i.e., ceil(maxWedges * 5/4)
}
void PathWedgeTessellator::writePatches(PatchWriter& patchWriter,
int maxTessellationSegments,
const SkMatrix& shaderMatrix,
const PathDrawList& pathDrawList) {
float maxSegments_pow2 = pow2(maxTessellationSegments);
float maxSegments_pow4 = pow2(maxSegments_pow2);
@ -249,8 +242,9 @@ void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
// log16(n^4) == log2(n).
// We already chopped curves to make sure none needed a higher resolveLevel than
// kMaxFixedResolveLevel.
fFixedResolveLevel = std::min(wangs_formula::nextlog16(numFixedSegments_pow4),
int(kMaxFixedResolveLevel));
fFixedResolveLevel = SkTPin(wangs_formula::nextlog16(numFixedSegments_pow4),
fFixedResolveLevel,
int(kMaxFixedResolveLevel));
}
void PathWedgeTessellator::WriteFixedVertexBuffer(VertexWriter vertexWriter, size_t bufferSize) {
@ -281,7 +275,9 @@ void PathWedgeTessellator::WriteFixedIndexBuffer(VertexWriter vertexWriter, size
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedVertexBufferKey);
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedIndexBufferKey);
void PathWedgeTessellator::prepareFixedCountBuffers(GrResourceProvider* rp) {
void PathWedgeTessellator::prepareFixedCountBuffers(GrMeshDrawTarget* target) {
GrResourceProvider* rp = target->resourceProvider();
GR_DEFINE_STATIC_UNIQUE_KEY(gFixedVertexBufferKey);
fFixedVertexBuffer = rp->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,

View File

@ -8,23 +8,14 @@
#ifndef tessellate_PathWedgeTessellator_DEFINED
#define tessellate_PathWedgeTessellator_DEFINED
#include "src/gpu/GrVertexChunkArray.h"
#include "src/gpu/tessellate/PathTessellator.h"
class GrCaps;
class GrPipeline;
#if SK_GPU_V1
class GrGpuBuffer;
class GrResourceProvider;
#endif
namespace skgpu {
// Prepares an array of "wedge" patches for GrWedgeTessellateShader. A wedge is an independent,
// 5-point closed contour consisting of 4 control points plus an anchor point fanning from the
// center of the curve's resident contour. A wedge can be either a cubic or a conic. Quadratics and
// lines are converted to cubics. Once stencilled, these wedges alone define the complete path.
// Prepares an array of "wedge" patches. A wedge is an independent, 5-point closed contour
// consisting of 4 control points plus an anchor point fanning from the center of the curve's
// resident contour. A wedge can be either a cubic or a conic. Quadratics and lines are converted to
// cubics. Once stencilled, these wedges alone define the complete path.
class PathWedgeTessellator final : public PathTessellator {
public:
static PathWedgeTessellator* Make(SkArenaAlloc* arena,
@ -38,11 +29,12 @@ public:
fAttribs |= PatchAttribs::kFanPoint;
}
void prepare(GrMeshDrawTarget*,
int maxTessellationSegments,
const SkMatrix& shaderMatrix,
const PathDrawList&,
int totalCombinedPathVerbCnt) final;
int patchPreallocCount(int totalCombinedPathVerbCnt) const final;
void writePatches(PatchWriter&,
int maxTessellationSegments,
const SkMatrix& shaderMatrix,
const PathDrawList&) final;
// Size of the vertex buffer to use when rendering with a fixed count shader.
constexpr static int FixedVertexBufferSize(int maxFixedResolveLevel) {
@ -62,14 +54,11 @@ public:
static void WriteFixedIndexBuffer(VertexWriter vertexWriter, size_t bufferSize);
#if SK_GPU_V1
void prepareFixedCountBuffers(GrResourceProvider*) final;
void prepareFixedCountBuffers(GrMeshDrawTarget*) final;
void drawTessellated(GrOpFlushState*) const final;
void drawFixedCount(GrOpFlushState*) const final;
#endif
private:
GrVertexChunkArray fVertexChunkArray;
};
} // namespace skgpu