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:
parent
5182ed33a9
commit
09ece934af
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user