Yank GrPathTessellationShader out of PatchTessellators
The tessellators are now agnostic of the shader class, which makes them mostly usable by Graphite now. Also moves the code for writing the middle-out vertex and index buffers into the PatchTessellators, which makes sense since they also write the instance buffers. Bug: skia:12524 Change-Id: I6c415645e389e056c0db1d93663b1b295d6b4535 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/469631 Reviewed-by: Greg Daniel <egdaniel@google.com> Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
parent
6d823b35fa
commit
f0f447c2f0
@ -143,13 +143,12 @@ DEF_PATH_TESS_BENCH(GrPathCurveTessellator, make_cubic_path(8), SkMatrix::I()) {
|
||||
GrPipeline noVaryingsPipeline(GrScissorTest::kDisabled, SkBlendMode::kSrcOver,
|
||||
GrSwizzle::RGBA());
|
||||
auto tess = PathCurveTessellator::Make(&arena,
|
||||
fMatrix,
|
||||
SK_PMColor4fTRANSPARENT,
|
||||
PathCurveTessellator::DrawInnerFan::kNo,
|
||||
fTarget->caps().minPathVerbsForHwTessellation(),
|
||||
noVaryingsPipeline,
|
||||
fTarget->caps());
|
||||
tess->prepare(fTarget.get(), {gAlmostIdentity, fPath, SK_PMColor4fTRANSPARENT},
|
||||
fTarget->caps().shaderCaps()->infinitySupport());
|
||||
tess->prepare(fTarget.get(),
|
||||
1 << PathCurveTessellator::kMaxFixedResolveLevel,
|
||||
fMatrix,
|
||||
{gAlmostIdentity, fPath, SK_PMColor4fTRANSPARENT},
|
||||
fPath.countVerbs());
|
||||
}
|
||||
|
||||
@ -158,12 +157,11 @@ DEF_PATH_TESS_BENCH(GrPathWedgeTessellator, make_cubic_path(8), SkMatrix::I()) {
|
||||
GrPipeline noVaryingsPipeline(GrScissorTest::kDisabled, SkBlendMode::kSrcOver,
|
||||
GrSwizzle::RGBA());
|
||||
auto tess = PathWedgeTessellator::Make(&arena,
|
||||
fMatrix,
|
||||
SK_PMColor4fTRANSPARENT,
|
||||
fTarget->caps().minPathVerbsForHwTessellation(),
|
||||
noVaryingsPipeline,
|
||||
fTarget->caps());
|
||||
tess->prepare(fTarget.get(), {gAlmostIdentity, fPath, SK_PMColor4fTRANSPARENT},
|
||||
fTarget->caps().shaderCaps()->infinitySupport());
|
||||
tess->prepare(fTarget.get(),
|
||||
1 << PathCurveTessellator::kMaxFixedResolveLevel,
|
||||
fMatrix,
|
||||
{gAlmostIdentity, fPath, SK_PMColor4fTRANSPARENT},
|
||||
fPath.countVerbs());
|
||||
}
|
||||
|
||||
|
@ -80,60 +80,62 @@ private:
|
||||
const SkMatrix& shaderMatrix = SkMatrix::I();
|
||||
const SkMatrix& pathMatrix = fMatrix;
|
||||
const GrCaps& caps = flushState->caps();
|
||||
const GrShaderCaps& shaderCaps = *caps.shaderCaps();
|
||||
int numVerbsToGetMiddleOut = 0;
|
||||
int numVerbsToGetTessellation = caps.minPathVerbsForHwTessellation();
|
||||
auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState, std::move(fProcessors),
|
||||
fPipelineFlags);
|
||||
int numVerbs;
|
||||
switch (fMode) {
|
||||
using DrawInnerFan = PathCurveTessellator::DrawInnerFan;
|
||||
case Mode::kWedgeMiddleOut:
|
||||
fTessellator = PathWedgeTessellator::Make(alloc,
|
||||
shaderMatrix,
|
||||
kCyan,
|
||||
numVerbsToGetMiddleOut,
|
||||
*pipeline,
|
||||
|
||||
caps);
|
||||
fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
|
||||
numVerbs = numVerbsToGetMiddleOut;
|
||||
break;
|
||||
case Mode::kCurveMiddleOut:
|
||||
fTessellator = PathCurveTessellator::Make(alloc,
|
||||
shaderMatrix,
|
||||
kCyan,
|
||||
DrawInnerFan::kYes,
|
||||
numVerbsToGetMiddleOut,
|
||||
*pipeline,
|
||||
caps);
|
||||
shaderCaps.infinitySupport());
|
||||
numVerbs = numVerbsToGetMiddleOut;
|
||||
break;
|
||||
case Mode::kWedgeTessellate:
|
||||
fTessellator = PathWedgeTessellator::Make(alloc,
|
||||
shaderMatrix,
|
||||
kCyan,
|
||||
numVerbsToGetTessellation,
|
||||
*pipeline,
|
||||
caps);
|
||||
fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
|
||||
numVerbs = numVerbsToGetTessellation;
|
||||
break;
|
||||
case Mode::kCurveTessellate:
|
||||
fTessellator = PathCurveTessellator::Make(alloc,
|
||||
shaderMatrix,
|
||||
kCyan,
|
||||
DrawInnerFan::kYes,
|
||||
numVerbsToGetTessellation,
|
||||
*pipeline,
|
||||
caps);
|
||||
shaderCaps.infinitySupport());
|
||||
numVerbs = numVerbsToGetTessellation;
|
||||
break;
|
||||
}
|
||||
fTessellator->prepare(flushState, {pathMatrix, fPath, kCyan}, fPath.countVerbs());
|
||||
auto* tessShader = GrPathTessellationShader::Make(alloc,
|
||||
shaderMatrix,
|
||||
kCyan,
|
||||
numVerbs,
|
||||
*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(),
|
||||
flushState->renderPassBarriers(),
|
||||
GrLoadOp::kClear, &flushState->caps()},
|
||||
fTessellator->shader(), pipeline,
|
||||
tessShader,
|
||||
pipeline,
|
||||
&GrUserStencilSettings::kUnused);
|
||||
}
|
||||
void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
|
||||
flushState->bindPipeline(*fProgram, chainBounds);
|
||||
fTessellator->draw(flushState);
|
||||
fTessellator->draw(flushState, fProgram->geomProc().willUseTessellationShaders());
|
||||
}
|
||||
|
||||
const SkPath fPath;
|
||||
|
@ -44,6 +44,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
int maxTessellationSegments(const GrShaderCaps&) const override { SkUNREACHABLE; }
|
||||
|
||||
private:
|
||||
const char* name() const final { return "tessellate_HullShader"; }
|
||||
void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
|
||||
@ -249,15 +251,19 @@ void PathInnerTriangulateOp::prePreparePrograms(const GrTessellationShader::Prog
|
||||
// Pass 1: Tessellate the outer curves into the stencil buffer.
|
||||
if (!isLinear) {
|
||||
fTessellator = PathCurveTessellator::Make(args.fArena,
|
||||
fViewMatrix,
|
||||
SK_PMColor4fTRANSPARENT,
|
||||
PathCurveTessellator::DrawInnerFan::kNo,
|
||||
fPath.countVerbs(),
|
||||
*pipelineForStencils,
|
||||
*args.fCaps);
|
||||
args.fCaps->shaderCaps()->infinitySupport());
|
||||
auto* tessShader = GrPathTessellationShader::Make(args.fArena,
|
||||
fViewMatrix,
|
||||
SK_PMColor4fTRANSPARENT,
|
||||
fPath.countVerbs(),
|
||||
*pipelineForStencils,
|
||||
fTessellator->patchAttribs(),
|
||||
*args.fCaps);
|
||||
const GrUserStencilSettings* stencilPathSettings =
|
||||
GrPathTessellationShader::StencilPathSettings(GrFillRuleForSkPath(fPath));
|
||||
fStencilCurvesProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(),
|
||||
fStencilCurvesProgram = GrTessellationShader::MakeProgram(args,
|
||||
tessShader,
|
||||
pipelineForStencils,
|
||||
stencilPathSettings);
|
||||
}
|
||||
@ -412,10 +418,16 @@ void PathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
|
||||
|
||||
if (fTessellator) {
|
||||
// Must be called after polysToTriangles() in order for fFanBreadcrumbs to be complete.
|
||||
auto tessShader = &fStencilCurvesProgram->geomProc().cast<GrPathTessellationShader>();
|
||||
fTessellator->prepare(flushState,
|
||||
tessShader->maxTessellationSegments(*flushState->caps().shaderCaps()),
|
||||
tessShader->viewMatrix(),
|
||||
{SkMatrix::I(), fPath, SK_PMColor4fTRANSPARENT},
|
||||
fPath.countVerbs(),
|
||||
&fFanBreadcrumbs);
|
||||
if (!tessShader->willUseTessellationShaders()) {
|
||||
fTessellator->prepareFixedCountBuffers(flushState->resourceProvider());
|
||||
}
|
||||
}
|
||||
|
||||
if (!flushState->caps().shaderCaps()->vertexIDSupport()) {
|
||||
@ -433,7 +445,8 @@ void PathInnerTriangulateOp::onExecute(GrOpFlushState* flushState, const SkRect&
|
||||
if (fStencilCurvesProgram) {
|
||||
SkASSERT(fTessellator);
|
||||
flushState->bindPipelineAndScissorClip(*fStencilCurvesProgram, this->bounds());
|
||||
fTessellator->draw(flushState);
|
||||
fTessellator->draw(flushState,
|
||||
fStencilCurvesProgram->geomProc().willUseTessellationShaders());
|
||||
if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
|
||||
flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
|
||||
}
|
||||
|
@ -158,22 +158,21 @@ void PathStencilCoverOp::prePreparePrograms(const GrTessellationShader::ProgramA
|
||||
stencilPipeline,
|
||||
stencilSettings);
|
||||
fTessellator = PathCurveTessellator::Make(args.fArena,
|
||||
shaderMatrix,
|
||||
SK_PMColor4fTRANSPARENT,
|
||||
PathCurveTessellator::DrawInnerFan::kNo,
|
||||
fTotalCombinedPathVerbCnt,
|
||||
*stencilPipeline,
|
||||
*args.fCaps);
|
||||
args.fCaps->shaderCaps()->infinitySupport());
|
||||
} else {
|
||||
fTessellator = PathWedgeTessellator::Make(args.fArena,
|
||||
shaderMatrix,
|
||||
SK_PMColor4fTRANSPARENT,
|
||||
fTotalCombinedPathVerbCnt,
|
||||
*stencilPipeline,
|
||||
*args.fCaps);
|
||||
args.fCaps->shaderCaps()->infinitySupport());
|
||||
}
|
||||
auto* tessShader = GrPathTessellationShader::Make(args.fArena,
|
||||
shaderMatrix,
|
||||
SK_PMColor4fTRANSPARENT,
|
||||
fTotalCombinedPathVerbCnt,
|
||||
*stencilPipeline,
|
||||
fTessellator->patchAttribs(),
|
||||
*args.fCaps);
|
||||
fStencilPathProgram = GrTessellationShader::MakeProgram(args,
|
||||
fTessellator->shader(),
|
||||
tessShader,
|
||||
stencilPipeline,
|
||||
stencilSettings);
|
||||
|
||||
@ -260,7 +259,15 @@ void PathStencilCoverOp::onPrepare(GrOpFlushState* flushState) {
|
||||
vertexAlloc.unlock(fFanVertexCount);
|
||||
}
|
||||
|
||||
fTessellator->prepare(flushState, *fPathDrawList, fTotalCombinedPathVerbCnt);
|
||||
auto tessShader = &fStencilPathProgram->geomProc().cast<GrPathTessellationShader>();
|
||||
fTessellator->prepare(flushState,
|
||||
tessShader->maxTessellationSegments(*flushState->caps().shaderCaps()),
|
||||
tessShader->viewMatrix(),
|
||||
*fPathDrawList,
|
||||
fTotalCombinedPathVerbCnt);
|
||||
if (!tessShader->willUseTessellationShaders()) {
|
||||
fTessellator->prepareFixedCountBuffers(flushState->resourceProvider());
|
||||
}
|
||||
|
||||
if (fCoverBBoxProgram) {
|
||||
size_t instanceStride = fCoverBBoxProgram->geomProc().instanceStride();
|
||||
@ -325,7 +332,7 @@ void PathStencilCoverOp::onExecute(GrOpFlushState* flushState, const SkRect& cha
|
||||
// Stencil the rest of the path.
|
||||
SkASSERT(fStencilPathProgram);
|
||||
flushState->bindPipelineAndScissorClip(*fStencilPathProgram, this->bounds());
|
||||
fTessellator->draw(flushState);
|
||||
fTessellator->draw(flushState, fStencilPathProgram->geomProc().willUseTessellationShaders());
|
||||
if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
|
||||
flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
|
||||
}
|
||||
|
@ -74,16 +74,16 @@ void PathTessellateOp::prepareTessellator(const GrTessellationShader::ProgramArg
|
||||
auto* pipeline = GrTessellationShader::MakePipeline(args, fAAType, std::move(appliedClip),
|
||||
std::move(fProcessors));
|
||||
fTessellator = PathWedgeTessellator::Make(args.fArena,
|
||||
fShaderMatrix,
|
||||
this->headDraw().fColor,
|
||||
fTotalCombinedPathVerbCnt,
|
||||
*pipeline,
|
||||
*args.fCaps,
|
||||
args.fCaps->shaderCaps()->infinitySupport(),
|
||||
fPatchAttribs);
|
||||
fTessellationProgram = GrTessellationShader::MakeProgram(args,
|
||||
fTessellator->shader(),
|
||||
pipeline,
|
||||
fStencil);
|
||||
auto* tessShader = GrPathTessellationShader::Make(args.fArena,
|
||||
fShaderMatrix,
|
||||
this->headDraw().fColor,
|
||||
fTotalCombinedPathVerbCnt,
|
||||
*pipeline,
|
||||
fTessellator->patchAttribs(),
|
||||
*args.fCaps);
|
||||
fTessellationProgram = GrTessellationShader::MakeProgram(args, tessShader, pipeline, fStencil);
|
||||
}
|
||||
|
||||
void PathTessellateOp::onPrePrepare(GrRecordingContext* context,
|
||||
@ -109,7 +109,15 @@ void PathTessellateOp::onPrepare(GrOpFlushState* flushState) {
|
||||
&flushState->caps()}, flushState->detachAppliedClip());
|
||||
SkASSERT(fTessellator);
|
||||
}
|
||||
fTessellator->prepare(flushState, *fPathDrawList, fTotalCombinedPathVerbCnt);
|
||||
auto tessShader = &fTessellationProgram->geomProc().cast<GrPathTessellationShader>();
|
||||
fTessellator->prepare(flushState,
|
||||
tessShader->maxTessellationSegments(*flushState->caps().shaderCaps()),
|
||||
fShaderMatrix,
|
||||
*fPathDrawList,
|
||||
fTotalCombinedPathVerbCnt);
|
||||
if (!tessShader->willUseTessellationShaders()) {
|
||||
fTessellator->prepareFixedCountBuffers(flushState->resourceProvider());
|
||||
}
|
||||
}
|
||||
|
||||
void PathTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
|
||||
@ -118,7 +126,7 @@ void PathTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chain
|
||||
flushState->bindPipelineAndScissorClip(*fTessellationProgram, this->bounds());
|
||||
flushState->bindTextures(fTessellationProgram->geomProc(), nullptr,
|
||||
fTessellationProgram->pipeline());
|
||||
fTessellator->draw(flushState);
|
||||
fTessellator->draw(flushState, fTessellationProgram->geomProc().willUseTessellationShaders());
|
||||
}
|
||||
|
||||
} // namespace skgpu::v1
|
||||
|
@ -7,18 +7,16 @@
|
||||
|
||||
#include "src/gpu/tessellate/PathCurveTessellator.h"
|
||||
|
||||
#include "src/core/SkUtils.h"
|
||||
#include "src/gpu/GrMeshDrawTarget.h"
|
||||
#include "src/gpu/GrResourceProvider.h"
|
||||
#include "src/gpu/geometry/GrPathUtils.h"
|
||||
#include "src/gpu/tessellate/AffineMatrix.h"
|
||||
#include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
|
||||
#include "src/gpu/tessellate/PatchWriter.h"
|
||||
#include "src/gpu/tessellate/WangsFormula.h"
|
||||
#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
|
||||
|
||||
#if SK_GPU_V1
|
||||
#include "src/gpu/GrMeshDrawTarget.h"
|
||||
#include "src/gpu/GrOpFlushState.h"
|
||||
#include "src/gpu/GrResourceProvider.h"
|
||||
#endif
|
||||
|
||||
namespace skgpu {
|
||||
@ -27,45 +25,14 @@ using CubicPatch = PatchWriter::CubicPatch;
|
||||
using ConicPatch = PatchWriter::ConicPatch;
|
||||
using TrianglePatch = PatchWriter::TrianglePatch;
|
||||
|
||||
PathCurveTessellator* PathCurveTessellator::Make(SkArenaAlloc* arena,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f& color,
|
||||
DrawInnerFan drawInnerFan,
|
||||
int numPathVerbs,
|
||||
const GrPipeline& pipeline,
|
||||
const GrCaps& caps,
|
||||
PatchAttribs attribs) {
|
||||
if (!caps.shaderCaps()->infinitySupport()) {
|
||||
attribs |= PatchAttribs::kExplicitCurveType;
|
||||
}
|
||||
GrPathTessellationShader* shader;
|
||||
if (caps.shaderCaps()->tessellationSupport() &&
|
||||
caps.shaderCaps()->infinitySupport() && // The hw tessellation shaders use infinity.
|
||||
!pipeline.usesLocalCoords() && // Our tessellation back door doesn't handle varyings.
|
||||
!(attribs & PatchAttribs::kColor) && // Input color isn't implemented for tessellation.
|
||||
numPathVerbs >= caps.minPathVerbsForHwTessellation()) {
|
||||
shader = GrPathTessellationShader::MakeHardwareTessellationShader(arena, viewMatrix, color,
|
||||
attribs);
|
||||
} else {
|
||||
shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(*caps.shaderCaps(), arena,
|
||||
viewMatrix, color,
|
||||
attribs);
|
||||
}
|
||||
return arena->make([=](void* objStart) {
|
||||
return new(objStart) PathCurveTessellator(shader, attribs, drawInnerFan);
|
||||
});
|
||||
}
|
||||
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
|
||||
|
||||
void PathCurveTessellator::prepare(GrMeshDrawTarget* target,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
const PathDrawList& pathDrawList,
|
||||
int totalCombinedPathVerbCnt,
|
||||
const BreadcrumbTriangleList* breadcrumbTriangleList) {
|
||||
SkASSERT(fVertexChunkArray.empty());
|
||||
|
||||
const GrShaderCaps& shaderCaps = *target->caps().shaderCaps();
|
||||
SkASSERT(!fFixedResolveLevel);
|
||||
|
||||
// Determine how many triangles to allocate.
|
||||
int maxTriangles = 0;
|
||||
@ -85,10 +52,8 @@ void PathCurveTessellator::prepare(GrMeshDrawTarget* target,
|
||||
if (!patchAllocCount) {
|
||||
return;
|
||||
}
|
||||
size_t patchStride = fShader->willUseTessellationShaders() ? fShader->vertexStride() * 4
|
||||
: fShader->instanceStride();
|
||||
|
||||
PatchWriter patchWriter(target, &fVertexChunkArray, patchStride, patchAllocCount, fAttribs);
|
||||
PatchWriter patchWriter(target, &fVertexChunkArray, PatchStride(fAttribs), patchAllocCount,
|
||||
fAttribs);
|
||||
|
||||
// Write out inner fan triangles.
|
||||
if (fDrawInnerFan) {
|
||||
@ -139,17 +104,7 @@ void PathCurveTessellator::prepare(GrMeshDrawTarget* target,
|
||||
SkASSERT(count == breadcrumbTriangleList->count());
|
||||
}
|
||||
|
||||
int maxFixedCountResolveLevel = GrPathTessellationShader::kMaxFixedCountResolveLevel;
|
||||
int maxSegments;
|
||||
if (fShader->willUseTessellationShaders()) {
|
||||
// The curve shader tessellates T=0..(1/2) on the first side of the canonical triangle and
|
||||
// T=(1/2)..1 on the second side. This means we get double the max tessellation segments
|
||||
// for the range T=0..1.
|
||||
maxSegments = shaderCaps.maxTessellationSegments() * 2;
|
||||
} else {
|
||||
maxSegments = 1 << maxFixedCountResolveLevel;
|
||||
}
|
||||
float maxSegments_pow2 = pow2(maxSegments);
|
||||
float maxSegments_pow2 = pow2(maxTessellationSegments);
|
||||
float maxSegments_pow4 = pow2(maxSegments_pow2);
|
||||
|
||||
// If using fixed count, this is the number of segments we need to emit per instance. Always
|
||||
@ -158,7 +113,7 @@ void PathCurveTessellator::prepare(GrMeshDrawTarget* target,
|
||||
|
||||
for (auto [pathMatrix, path, color] : pathDrawList) {
|
||||
AffineMatrix m(pathMatrix);
|
||||
wangs_formula::VectorXform totalXform(SkMatrix::Concat(fShader->viewMatrix(), pathMatrix));
|
||||
wangs_formula::VectorXform totalXform(SkMatrix::Concat(shaderMatrix, pathMatrix));
|
||||
if (fAttribs & PatchAttribs::kColor) {
|
||||
patchWriter.updateColorAttrib(color);
|
||||
}
|
||||
@ -174,7 +129,7 @@ void PathCurveTessellator::prepare(GrMeshDrawTarget* target,
|
||||
break; // This quad only needs 1 segment, which is empty.
|
||||
}
|
||||
if (n4 <= maxSegments_pow4) {
|
||||
// This quad already fits into "maxSegments" tessellation segments.
|
||||
// This quad already fits in "maxTessellationSegments".
|
||||
CubicPatch(patchWriter) << QuadToCubic{p0, p1, p2};
|
||||
} else {
|
||||
// Chop until each quad tessellation requires "maxSegments" or fewer.
|
||||
@ -197,7 +152,7 @@ void PathCurveTessellator::prepare(GrMeshDrawTarget* target,
|
||||
break; // This conic only needs 1 segment, which is empty.
|
||||
}
|
||||
if (n2 <= maxSegments_pow2) {
|
||||
// This conic already fits into "maxSegments" tessellation segments.
|
||||
// This conic already fits in "maxTessellationSegments".
|
||||
ConicPatch(patchWriter) << p0 << p1 << p2 << *w;
|
||||
} else {
|
||||
// Chop until each conic tessellation requires "maxSegments" or fewer.
|
||||
@ -218,7 +173,7 @@ void PathCurveTessellator::prepare(GrMeshDrawTarget* target,
|
||||
break; // This cubic only needs 1 segment, which is empty.
|
||||
}
|
||||
if (n4 <= maxSegments_pow4) {
|
||||
// This cubic already fits into "maxSegments" tessellation segments.
|
||||
// This cubic already fits in "maxTessellationSegments".
|
||||
CubicPatch(patchWriter) << p0 << p1 << p2 << p3;
|
||||
} else {
|
||||
// Chop until each cubic tessellation requires "maxSegments" or fewer.
|
||||
@ -235,46 +190,127 @@ void PathCurveTessellator::prepare(GrMeshDrawTarget* target,
|
||||
}
|
||||
}
|
||||
|
||||
if (!fShader->willUseTessellationShaders()) {
|
||||
// log16(n^4) == log2(n).
|
||||
// We already chopped curves to make sure none needed a higher resolveLevel than
|
||||
// kMaxFixedCountResolveLevel.
|
||||
int fixedResolveLevel = std::min(wangs_formula::nextlog16(numFixedSegments_pow4),
|
||||
maxFixedCountResolveLevel);
|
||||
fFixedIndexCount =
|
||||
GrPathTessellationShader::NumCurveTrianglesAtResolveLevel(fixedResolveLevel) * 3;
|
||||
// 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));
|
||||
}
|
||||
|
||||
GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
|
||||
void PathCurveTessellator::WriteFixedVertexBuffer(VertexWriter vertexWriter, size_t bufferSize) {
|
||||
SkASSERT(bufferSize >= sizeof(SkPoint) * 2);
|
||||
SkASSERT(bufferSize % sizeof(SkPoint) == 0);
|
||||
int vertexCount = bufferSize / sizeof(SkPoint);
|
||||
SkASSERT(vertexCount > 3);
|
||||
SkDEBUGCODE(VertexWriter end = vertexWriter.makeOffset(vertexCount * sizeof(SkPoint));)
|
||||
|
||||
fFixedCountVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
|
||||
GrGpuBufferType::kVertex,
|
||||
GrPathTessellationShader::SizeOfVertexBufferForMiddleOutCurves(),
|
||||
gFixedCountVertexBufferKey,
|
||||
GrPathTessellationShader::InitializeVertexBufferForMiddleOutCurves);
|
||||
// Lay out the vertices in "middle-out" order:
|
||||
//
|
||||
// T= 0/1, 1/1, ; resolveLevel=0
|
||||
// 1/2, ; resolveLevel=1 (0/2 and 2/2 are already in resolveLevel 0)
|
||||
// 1/4, 3/4, ; resolveLevel=2 (2/4 is already in resolveLevel 1)
|
||||
// 1/8, 3/8, 5/8, 7/8, ; resolveLevel=3 (2/8 and 6/8 are already in resolveLevel 2)
|
||||
// ... ; resolveLevel=...
|
||||
//
|
||||
// Resolve level 0 is just the beginning and ending vertices.
|
||||
vertexWriter << (float)0/*resolveLevel*/ << (float)0/*idx*/;
|
||||
vertexWriter << (float)0/*resolveLevel*/ << (float)1/*idx*/;
|
||||
|
||||
GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
|
||||
|
||||
fFixedCountIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
|
||||
GrGpuBufferType::kIndex,
|
||||
GrPathTessellationShader::SizeOfIndexBufferForMiddleOutCurves(),
|
||||
gFixedCountIndexBufferKey,
|
||||
GrPathTessellationShader::InitializeIndexBufferForMiddleOutCurves);
|
||||
// Resolve levels 1..kMaxResolveLevel.
|
||||
int maxResolveLevel = SkPrevLog2(vertexCount - 1);
|
||||
SkASSERT((1 << maxResolveLevel) + 1 == vertexCount);
|
||||
for (int resolveLevel = 1; resolveLevel <= maxResolveLevel; ++resolveLevel) {
|
||||
int numSegmentsInResolveLevel = 1 << resolveLevel;
|
||||
// Write out the odd vertices in this resolveLevel. The even vertices were already written
|
||||
// out in previous resolveLevels and will be indexed from there.
|
||||
for (int i = 1; i < numSegmentsInResolveLevel; i += 2) {
|
||||
vertexWriter << (float)resolveLevel << (float)i;
|
||||
}
|
||||
}
|
||||
|
||||
SkASSERT(vertexWriter == end);
|
||||
}
|
||||
|
||||
void PathCurveTessellator::WriteFixedIndexBufferBaseIndex(VertexWriter vertexWriter,
|
||||
size_t bufferSize,
|
||||
uint16_t baseIndex) {
|
||||
SkASSERT(bufferSize % (sizeof(uint16_t) * 3) == 0);
|
||||
int triangleCount = bufferSize / (sizeof(uint16_t) * 3);
|
||||
SkASSERT(triangleCount >= 1);
|
||||
SkTArray<std::array<uint16_t, 3>> indexData(triangleCount);
|
||||
|
||||
// Connect the vertices with a middle-out triangulation. Refer to InitFixedCountVertexBuffer()
|
||||
// for the exact vertex ordering.
|
||||
//
|
||||
// Resolve level 1 is just a single triangle at T=[0, 1/2, 1].
|
||||
const auto* neighborInLastResolveLevel = &indexData.push_back({baseIndex,
|
||||
(uint16_t)(baseIndex + 2),
|
||||
(uint16_t)(baseIndex + 1)});
|
||||
|
||||
// Resolve levels 2..maxResolveLevel
|
||||
int maxResolveLevel = SkPrevLog2(triangleCount + 1);
|
||||
uint16_t nextIndex = baseIndex + 3;
|
||||
SkASSERT(NumCurveTrianglesAtResolveLevel(maxResolveLevel) == triangleCount);
|
||||
for (int resolveLevel = 2; resolveLevel <= maxResolveLevel; ++resolveLevel) {
|
||||
SkDEBUGCODE(auto* firstTriangleInCurrentResolveLevel = indexData.end());
|
||||
int numOuterTrianglelsInResolveLevel = 1 << (resolveLevel - 1);
|
||||
SkASSERT(numOuterTrianglelsInResolveLevel % 2 == 0);
|
||||
int numTrianglePairsInResolveLevel = numOuterTrianglelsInResolveLevel >> 1;
|
||||
for (int i = 0; i < numTrianglePairsInResolveLevel; ++i) {
|
||||
// First triangle shares the left edge of "neighborInLastResolveLevel".
|
||||
indexData.push_back({(*neighborInLastResolveLevel)[0],
|
||||
nextIndex++,
|
||||
(*neighborInLastResolveLevel)[1]});
|
||||
// Second triangle shares the right edge of "neighborInLastResolveLevel".
|
||||
indexData.push_back({(*neighborInLastResolveLevel)[1],
|
||||
nextIndex++,
|
||||
(*neighborInLastResolveLevel)[2]});
|
||||
++neighborInLastResolveLevel;
|
||||
}
|
||||
SkASSERT(neighborInLastResolveLevel == firstTriangleInCurrentResolveLevel);
|
||||
}
|
||||
SkASSERT(indexData.count() == triangleCount);
|
||||
SkASSERT(nextIndex == baseIndex + triangleCount + 2);
|
||||
|
||||
vertexWriter.writeArray(indexData.data(), indexData.count());
|
||||
}
|
||||
|
||||
#if SK_GPU_V1
|
||||
void PathCurveTessellator::draw(GrOpFlushState* flushState) const {
|
||||
if (fShader->willUseTessellationShaders()) {
|
||||
for (const GrVertexChunk& chunk : fVertexChunkArray) {
|
||||
flushState->bindBuffers(nullptr, nullptr, chunk.fBuffer);
|
||||
flushState->draw(chunk.fCount * 4, chunk.fBase * 4);
|
||||
}
|
||||
} else {
|
||||
SkASSERT(fShader->hasInstanceAttributes());
|
||||
for (const GrVertexChunk& chunk : fVertexChunkArray) {
|
||||
flushState->bindBuffers(fFixedCountIndexBuffer, chunk.fBuffer, fFixedCountVertexBuffer);
|
||||
flushState->drawIndexedInstanced(fFixedIndexCount, 0, chunk.fCount, chunk.fBase, 0);
|
||||
}
|
||||
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedVertexBufferKey);
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedIndexBufferKey);
|
||||
|
||||
void PathCurveTessellator::prepareFixedCountBuffers(GrResourceProvider* rp) {
|
||||
GR_DEFINE_STATIC_UNIQUE_KEY(gFixedVertexBufferKey);
|
||||
|
||||
fFixedVertexBuffer = rp->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
|
||||
FixedVertexBufferSize(kMaxFixedResolveLevel),
|
||||
gFixedVertexBufferKey,
|
||||
WriteFixedVertexBuffer);
|
||||
|
||||
GR_DEFINE_STATIC_UNIQUE_KEY(gFixedIndexBufferKey);
|
||||
|
||||
fFixedIndexBuffer = rp->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
|
||||
FixedIndexBufferSize(kMaxFixedResolveLevel),
|
||||
gFixedIndexBufferKey,
|
||||
WriteFixedIndexBuffer);
|
||||
}
|
||||
|
||||
void PathCurveTessellator::drawTessellated(GrOpFlushState* flushState) const {
|
||||
for (const GrVertexChunk& chunk : fVertexChunkArray) {
|
||||
flushState->bindBuffers(nullptr, nullptr, chunk.fBuffer);
|
||||
flushState->draw(chunk.fCount * 4, chunk.fBase * 4);
|
||||
}
|
||||
}
|
||||
|
||||
void PathCurveTessellator::drawFixedCount(GrOpFlushState* flushState) const {
|
||||
if (!fFixedVertexBuffer || !fFixedIndexBuffer) {
|
||||
return;
|
||||
}
|
||||
int fixedIndexCount = NumCurveTrianglesAtResolveLevel(fFixedResolveLevel) * 3;
|
||||
for (const GrVertexChunk& chunk : fVertexChunkArray) {
|
||||
flushState->bindBuffers(fFixedIndexBuffer, chunk.fBuffer, fFixedVertexBuffer);
|
||||
flushState->drawIndexedInstanced(fixedIndexCount, 0, chunk.fCount, chunk.fBase, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,6 +321,7 @@ void PathCurveTessellator::drawHullInstances(GrOpFlushState* flushState,
|
||||
flushState->drawInstanced(chunk.fCount, chunk.fBase, 4, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace skgpu
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "src/gpu/tessellate/PathTessellator.h"
|
||||
|
||||
class GrCaps;
|
||||
class GrGpuBuffer;
|
||||
class GrPipeline;
|
||||
|
||||
namespace skgpu {
|
||||
@ -20,7 +19,7 @@ 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.
|
||||
class PathCurveTessellator : public PathTessellator {
|
||||
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.
|
||||
@ -29,20 +28,26 @@ public:
|
||||
kYes
|
||||
};
|
||||
|
||||
// Creates a curve tessellator with the shader type best suited for the given path description.
|
||||
static PathCurveTessellator* Make(SkArenaAlloc*,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f&,
|
||||
DrawInnerFan,
|
||||
int numPathVerbs,
|
||||
const GrPipeline&,
|
||||
const GrCaps&,
|
||||
PatchAttribs = PatchAttribs::kNone);
|
||||
static PathCurveTessellator* Make(SkArenaAlloc* arena,
|
||||
DrawInnerFan drawInnerFan,
|
||||
bool infinitySupport,
|
||||
PatchAttribs attribs = PatchAttribs::kNone) {
|
||||
return arena->make<PathCurveTessellator>(drawInnerFan, infinitySupport, attribs);
|
||||
}
|
||||
|
||||
PathCurveTessellator(DrawInnerFan drawInnerFan,
|
||||
bool infinitySupport,
|
||||
PatchAttribs attribs = PatchAttribs::kNone)
|
||||
: PathTessellator(infinitySupport, attribs)
|
||||
, fDrawInnerFan(drawInnerFan != DrawInnerFan::kNo) {}
|
||||
|
||||
void prepare(GrMeshDrawTarget* target,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
const PathDrawList& pathDrawList,
|
||||
int totalCombinedPathVerbCnt) override {
|
||||
this->prepare(target, pathDrawList, totalCombinedPathVerbCnt, nullptr);
|
||||
int totalCombinedPathVerbCnt) final {
|
||||
this->prepare(target, maxTessellationSegments, shaderMatrix, pathDrawList,
|
||||
totalCombinedPathVerbCnt, nullptr);
|
||||
}
|
||||
|
||||
// Implements PathTessellator::prepare(), also sending an additional list of breadcrumb
|
||||
@ -51,33 +56,47 @@ public:
|
||||
// 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*);
|
||||
|
||||
#if SK_GPU_V1
|
||||
void draw(GrOpFlushState*) const override;
|
||||
// Size of the vertex buffer to use when rendering with a fixed count shader.
|
||||
constexpr static int FixedVertexBufferSize(int maxFixedResolveLevel) {
|
||||
return ((1 << maxFixedResolveLevel) + 1) * sizeof(SkPoint);
|
||||
}
|
||||
|
||||
// Draws a 4-point instance for each curve. This method is used for drawing convex hulls over
|
||||
// Writes the vertex buffer to use when rendering with a fixed count shader.
|
||||
static void WriteFixedVertexBuffer(VertexWriter, size_t bufferSize);
|
||||
|
||||
// Size of the index buffer to use when rendering with a fixed count shader.
|
||||
constexpr static int FixedIndexBufferSize(int maxFixedResolveLevel) {
|
||||
return NumCurveTrianglesAtResolveLevel(maxFixedResolveLevel) * 3 * sizeof(uint16_t);
|
||||
}
|
||||
|
||||
// Writes the index buffer to use when rendering with a fixed count shader.
|
||||
static void WriteFixedIndexBuffer(VertexWriter vertexWriter, size_t bufferSize) {
|
||||
WriteFixedIndexBufferBaseIndex(std::move(vertexWriter), bufferSize, 0);
|
||||
}
|
||||
|
||||
static void WriteFixedIndexBufferBaseIndex(VertexWriter, size_t bufferSize, uint16_t baseIndex);
|
||||
|
||||
#if SK_GPU_V1
|
||||
void prepareFixedCountBuffers(GrResourceProvider*) final;
|
||||
|
||||
void drawTessellated(GrOpFlushState*) const final;
|
||||
void drawFixedCount(GrOpFlushState*) const final;
|
||||
|
||||
// Draws a 4-point instance for each patch. This method is used for drawing convex hulls over
|
||||
// each cubic with GrFillCubicHullShader. The caller is responsible for binding its desired
|
||||
// pipeline ahead of time.
|
||||
void drawHullInstances(GrOpFlushState*, sk_sp<const GrGpuBuffer> vertexBufferIfNeeded) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
PathCurveTessellator(GrPathTessellationShader* shader,
|
||||
PatchAttribs attribs,
|
||||
DrawInnerFan drawInnerFan)
|
||||
: PathTessellator(shader, attribs)
|
||||
, fDrawInnerFan(drawInnerFan == DrawInnerFan::kYes) {}
|
||||
|
||||
const bool fDrawInnerFan;
|
||||
GrVertexChunkArray fVertexChunkArray;
|
||||
|
||||
// If using fixed count, this is the number of vertices we need to emit per instance.
|
||||
int fFixedIndexCount;
|
||||
sk_sp<const GrGpuBuffer> fFixedCountVertexBuffer;
|
||||
sk_sp<const GrGpuBuffer> fFixedCountIndexBuffer;
|
||||
};
|
||||
|
||||
} // namespace skgpu
|
||||
|
@ -16,9 +16,13 @@
|
||||
|
||||
class SkPath;
|
||||
class GrMeshDrawTarget;
|
||||
|
||||
#if SK_GPU_V1
|
||||
class GrGpuBuffer;
|
||||
class GrOpFlushState;
|
||||
class GrPathTessellationShader;
|
||||
class GrResourceProvider;
|
||||
#endif
|
||||
|
||||
|
||||
namespace skgpu {
|
||||
|
||||
@ -28,6 +32,11 @@ 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.
|
||||
constexpr static int kMaxFixedResolveLevel = 5;
|
||||
|
||||
struct PathDrawList {
|
||||
PathDrawList(const SkMatrix& pathMatrix,
|
||||
const SkPath& path,
|
||||
@ -54,18 +63,36 @@ public:
|
||||
|
||||
virtual ~PathTessellator() {}
|
||||
|
||||
const GrPathTessellationShader* shader() const { return fShader; }
|
||||
PatchAttribs patchAttribs() const { return fAttribs; }
|
||||
|
||||
// Called before draw(). Prepares GPU buffers containing the geometry to tessellate.
|
||||
//
|
||||
// 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*, const PathDrawList&, int totalCombinedPathVerbCnt) = 0;
|
||||
virtual void prepare(GrMeshDrawTarget*,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
const PathDrawList&,
|
||||
int totalCombinedPathVerbCnt) = 0;
|
||||
|
||||
#if SK_GPU_V1
|
||||
// Issues draw calls for the tessellated geometry. The caller is responsible for binding its
|
||||
// desired pipeline ahead of time.
|
||||
virtual void draw(GrOpFlushState*) const = 0;
|
||||
virtual void prepareFixedCountBuffers(GrResourceProvider*) = 0;
|
||||
|
||||
void draw(GrOpFlushState* flushState, bool willUseTessellationShaders) {
|
||||
if (willUseTessellationShaders) {
|
||||
this->drawTessellated(flushState);
|
||||
} else {
|
||||
this->drawFixedCount(flushState);
|
||||
}
|
||||
}
|
||||
|
||||
// Issues hardware tessellation draw calls over the patches. The caller is responsible for
|
||||
// binding its desired pipeline ahead of time.
|
||||
virtual void drawTessellated(GrOpFlushState*) const = 0;
|
||||
|
||||
// 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;
|
||||
#endif
|
||||
|
||||
// Returns an upper bound on the number of combined edges there might be from all inner fans in
|
||||
@ -79,11 +106,33 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
PathTessellator(GrPathTessellationShader* shader, PatchAttribs attribs)
|
||||
: fShader(shader), fAttribs(attribs) {}
|
||||
// How many triangles are in a curve with 2^resolveLevel line segments?
|
||||
constexpr static int NumCurveTrianglesAtResolveLevel(int resolveLevel) {
|
||||
// resolveLevel=0 -> 0 line segments -> 0 triangles
|
||||
// resolveLevel=1 -> 2 line segments -> 1 triangle
|
||||
// resolveLevel=2 -> 4 line segments -> 3 triangles
|
||||
// resolveLevel=3 -> 8 line segments -> 7 triangles
|
||||
// ...
|
||||
return (1 << resolveLevel) - 1;
|
||||
}
|
||||
|
||||
GrPathTessellationShader* const fShader;
|
||||
const PatchAttribs fAttribs;
|
||||
PathTessellator(bool infinitySupport, PatchAttribs attribs) : fAttribs(attribs) {
|
||||
if (!infinitySupport) {
|
||||
attribs |= PatchAttribs::kExplicitCurveType;
|
||||
}
|
||||
}
|
||||
|
||||
PatchAttribs fAttribs;
|
||||
|
||||
// Calculated during prepare(). If using fixed count, this is the resolveLevel to use on our
|
||||
// instanced draws. 2^resolveLevel == numSegments.
|
||||
int fFixedResolveLevel = 0;
|
||||
|
||||
#if SK_GPU_V1
|
||||
// If using fixed-count rendering, these are the vertex and index buffers.
|
||||
sk_sp<const GrGpuBuffer> fFixedVertexBuffer;
|
||||
sk_sp<const GrGpuBuffer> fFixedIndexBuffer;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace skgpu
|
||||
|
@ -7,17 +7,15 @@
|
||||
|
||||
#include "src/gpu/tessellate/PathWedgeTessellator.h"
|
||||
|
||||
#include "src/gpu/GrMeshDrawTarget.h"
|
||||
#include "src/gpu/GrResourceProvider.h"
|
||||
#include "src/gpu/geometry/GrPathUtils.h"
|
||||
#include "src/gpu/tessellate/AffineMatrix.h"
|
||||
#include "src/gpu/tessellate/PatchWriter.h"
|
||||
#include "src/gpu/tessellate/Tessellation.h"
|
||||
#include "src/gpu/tessellate/PathCurveTessellator.h"
|
||||
#include "src/gpu/tessellate/WangsFormula.h"
|
||||
#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
|
||||
|
||||
#if SK_GPU_V1
|
||||
#include "src/gpu/GrMeshDrawTarget.h"
|
||||
#include "src/gpu/GrOpFlushState.h"
|
||||
#include "src/gpu/GrResourceProvider.h"
|
||||
#endif
|
||||
|
||||
namespace skgpu {
|
||||
@ -128,44 +126,13 @@ private:
|
||||
|
||||
} // namespace
|
||||
|
||||
PathTessellator* PathWedgeTessellator::Make(SkArenaAlloc* arena,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f& color,
|
||||
int numPathVerbs,
|
||||
const GrPipeline& pipeline,
|
||||
const GrCaps& caps,
|
||||
PatchAttribs attribs) {
|
||||
GrPathTessellationShader* shader;
|
||||
attribs |= PatchAttribs::kFanPoint;
|
||||
if (!caps.shaderCaps()->infinitySupport()) {
|
||||
attribs |= PatchAttribs::kExplicitCurveType;
|
||||
}
|
||||
if (caps.shaderCaps()->tessellationSupport() &&
|
||||
caps.shaderCaps()->infinitySupport() && // The hw tessellation shaders use infinity.
|
||||
!pipeline.usesLocalCoords() && // Our tessellation back door doesn't handle varyings.
|
||||
!(attribs & PatchAttribs::kColor) && // Input color isn't implemented for tessellation.
|
||||
numPathVerbs >= caps.minPathVerbsForHwTessellation()) {
|
||||
shader = GrPathTessellationShader::MakeHardwareTessellationShader(arena, viewMatrix, color,
|
||||
attribs);
|
||||
} else {
|
||||
shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(*caps.shaderCaps(), arena,
|
||||
viewMatrix, color,
|
||||
attribs);
|
||||
}
|
||||
return arena->make([=](void* objStart) {
|
||||
return new(objStart) PathWedgeTessellator(shader, attribs);
|
||||
});
|
||||
}
|
||||
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
|
||||
|
||||
void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
const PathDrawList& pathDrawList,
|
||||
int totalCombinedPathVerbCnt) {
|
||||
SkASSERT(fVertexChunkArray.empty());
|
||||
|
||||
const GrShaderCaps& shaderCaps = *target->caps().shaderCaps();
|
||||
SkASSERT(!fFixedResolveLevel);
|
||||
|
||||
// Over-allocate enough wedges for 1 in 4 to chop.
|
||||
int maxWedges = MaxCombinedFanEdgesInPathDrawList(totalCombinedPathVerbCnt);
|
||||
@ -173,19 +140,10 @@ void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
|
||||
if (!wedgeAllocCount) {
|
||||
return;
|
||||
}
|
||||
size_t patchStride = fShader->willUseTessellationShaders() ? fShader->vertexStride() * 5
|
||||
: fShader->instanceStride();
|
||||
PatchWriter patchWriter(target, &fVertexChunkArray, PatchStride(fAttribs), wedgeAllocCount,
|
||||
fAttribs);
|
||||
|
||||
PatchWriter patchWriter(target, &fVertexChunkArray, patchStride, wedgeAllocCount, fAttribs);
|
||||
|
||||
int maxFixedCountResolveLevel = GrPathTessellationShader::kMaxFixedCountResolveLevel;
|
||||
int maxSegments;
|
||||
if (fShader->willUseTessellationShaders()) {
|
||||
maxSegments = shaderCaps.maxTessellationSegments();
|
||||
} else {
|
||||
maxSegments = 1 << maxFixedCountResolveLevel;
|
||||
}
|
||||
float maxSegments_pow2 = pow2(maxSegments);
|
||||
float maxSegments_pow2 = pow2(maxTessellationSegments);
|
||||
float maxSegments_pow4 = pow2(maxSegments_pow2);
|
||||
|
||||
// If using fixed count, this is the number of segments we need to emit per instance. Always
|
||||
@ -194,7 +152,7 @@ void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
|
||||
|
||||
for (auto [pathMatrix, path, color] : pathDrawList) {
|
||||
AffineMatrix m(pathMatrix);
|
||||
wangs_formula::VectorXform totalXform(SkMatrix::Concat(fShader->viewMatrix(), pathMatrix));
|
||||
wangs_formula::VectorXform totalXform(SkMatrix::Concat(shaderMatrix, pathMatrix));
|
||||
if (fAttribs & PatchAttribs::kColor) {
|
||||
patchWriter.updateColorAttrib(color);
|
||||
}
|
||||
@ -223,7 +181,7 @@ void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
|
||||
pts,
|
||||
totalXform);
|
||||
if (n4 <= maxSegments_pow4) {
|
||||
// This quad already fits into "maxSegments" tessellation segments.
|
||||
// This quad already fits in "maxTessellationSegments".
|
||||
CubicPatch(patchWriter) << QuadToCubic{p0, p1, p2};
|
||||
} else {
|
||||
// Chop until each quad tessellation requires "maxSegments" or fewer.
|
||||
@ -244,7 +202,7 @@ void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
|
||||
*w,
|
||||
totalXform);
|
||||
if (n2 <= maxSegments_pow2) {
|
||||
// This conic already fits into "maxSegments" tessellation segments.
|
||||
// This conic already fits in "maxTessellationSegments".
|
||||
ConicPatch(patchWriter) << p0 << p1 << p2 << *w;
|
||||
} else {
|
||||
// Chop until each conic tessellation requires "maxSegments" or fewer.
|
||||
@ -263,7 +221,7 @@ void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
|
||||
pts,
|
||||
totalXform);
|
||||
if (n4 <= maxSegments_pow4) {
|
||||
// This cubic already fits into "maxSegments" tessellation segments.
|
||||
// This cubic already fits in "maxTessellationSegments".
|
||||
CubicPatch(patchWriter) << p0 << p1 << p2 << p3;
|
||||
} else {
|
||||
// Chop until each cubic tessellation requires "maxSegments" or fewer.
|
||||
@ -288,50 +246,76 @@ void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
|
||||
}
|
||||
}
|
||||
|
||||
if (!fShader->willUseTessellationShaders()) {
|
||||
// log16(n^4) == log2(n).
|
||||
// We already chopped curves to make sure none needed a higher resolveLevel than
|
||||
// kMaxFixedCountResolveLevel.
|
||||
int fixedResolveLevel = std::min(wangs_formula::nextlog16(numFixedSegments_pow4),
|
||||
maxFixedCountResolveLevel);
|
||||
int numCurveTriangles =
|
||||
GrPathTessellationShader::NumCurveTrianglesAtResolveLevel(fixedResolveLevel);
|
||||
// Emit 3 vertices per curve triangle, plus 3 more for the fan triangle.
|
||||
fFixedIndexCount = numCurveTriangles * 3 + 3;
|
||||
// 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));
|
||||
}
|
||||
|
||||
GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
|
||||
void PathWedgeTessellator::WriteFixedVertexBuffer(VertexWriter vertexWriter, size_t bufferSize) {
|
||||
SkASSERT(bufferSize >= sizeof(SkPoint));
|
||||
|
||||
fFixedCountVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
|
||||
GrGpuBufferType::kVertex,
|
||||
GrPathTessellationShader::SizeOfVertexBufferForMiddleOutWedges(),
|
||||
gFixedCountVertexBufferKey,
|
||||
GrPathTessellationShader::InitializeVertexBufferForMiddleOutWedges);
|
||||
// Start out with the fan point. A negative resolve level indicates the fan point.
|
||||
vertexWriter << -1.f/*resolveLevel*/ << -1.f/*idx*/;
|
||||
|
||||
GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
|
||||
// The rest is the same as for curves.
|
||||
PathCurveTessellator::WriteFixedVertexBuffer(std::move(vertexWriter),
|
||||
bufferSize - sizeof(SkPoint));
|
||||
}
|
||||
|
||||
fFixedCountIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
|
||||
GrGpuBufferType::kIndex,
|
||||
GrPathTessellationShader::SizeOfIndexBufferForMiddleOutWedges(),
|
||||
gFixedCountIndexBufferKey,
|
||||
GrPathTessellationShader::InitializeIndexBufferForMiddleOutWedges);
|
||||
}
|
||||
void PathWedgeTessellator::WriteFixedIndexBuffer(VertexWriter vertexWriter, size_t bufferSize) {
|
||||
SkASSERT(bufferSize >= sizeof(uint16_t) * 3);
|
||||
|
||||
// Start out with the fan triangle.
|
||||
vertexWriter << (uint16_t)0 << (uint16_t)1 << (uint16_t)2;
|
||||
|
||||
// The rest is the same as for curves, with a baseIndex of 1.
|
||||
PathCurveTessellator::WriteFixedIndexBufferBaseIndex(std::move(vertexWriter),
|
||||
bufferSize - sizeof(uint16_t) * 3,
|
||||
1);
|
||||
}
|
||||
|
||||
#if SK_GPU_V1
|
||||
void PathWedgeTessellator::draw(GrOpFlushState* flushState) const {
|
||||
if (fShader->willUseTessellationShaders()) {
|
||||
for (const GrVertexChunk& chunk : fVertexChunkArray) {
|
||||
flushState->bindBuffers(nullptr, nullptr, chunk.fBuffer);
|
||||
flushState->draw(chunk.fCount * 5, chunk.fBase * 5);
|
||||
}
|
||||
} else {
|
||||
SkASSERT(fShader->hasInstanceAttributes());
|
||||
for (const GrVertexChunk& chunk : fVertexChunkArray) {
|
||||
flushState->bindBuffers(fFixedCountIndexBuffer, chunk.fBuffer, fFixedCountVertexBuffer);
|
||||
flushState->drawIndexedInstanced(fFixedIndexCount, 0, chunk.fCount, chunk.fBase, 0);
|
||||
}
|
||||
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedVertexBufferKey);
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedIndexBufferKey);
|
||||
|
||||
void PathWedgeTessellator::prepareFixedCountBuffers(GrResourceProvider* rp) {
|
||||
GR_DEFINE_STATIC_UNIQUE_KEY(gFixedVertexBufferKey);
|
||||
|
||||
fFixedVertexBuffer = rp->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
|
||||
FixedVertexBufferSize(kMaxFixedResolveLevel),
|
||||
gFixedVertexBufferKey,
|
||||
WriteFixedVertexBuffer);
|
||||
|
||||
GR_DEFINE_STATIC_UNIQUE_KEY(gFixedIndexBufferKey);
|
||||
|
||||
fFixedIndexBuffer = rp->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
|
||||
FixedIndexBufferSize(kMaxFixedResolveLevel),
|
||||
gFixedIndexBufferKey,
|
||||
WriteFixedIndexBuffer);
|
||||
}
|
||||
|
||||
void PathWedgeTessellator::drawTessellated(GrOpFlushState* flushState) const {
|
||||
for (const GrVertexChunk& chunk : fVertexChunkArray) {
|
||||
flushState->bindBuffers(nullptr, nullptr, chunk.fBuffer);
|
||||
flushState->draw(chunk.fCount * 5, chunk.fBase * 5);
|
||||
}
|
||||
}
|
||||
|
||||
void PathWedgeTessellator::drawFixedCount(GrOpFlushState* flushState) const {
|
||||
if (!fFixedVertexBuffer || !fFixedIndexBuffer) {
|
||||
return;
|
||||
}
|
||||
// Emit 3 vertices per curve triangle, plus 3 more for the fan triangle.
|
||||
int fixedIndexCount = (NumCurveTrianglesAtResolveLevel(fFixedResolveLevel) + 1) * 3;
|
||||
for (const GrVertexChunk& chunk : fVertexChunkArray) {
|
||||
flushState->bindBuffers(fFixedIndexBuffer, chunk.fBuffer, fFixedVertexBuffer);
|
||||
flushState->drawIndexedInstanced(fixedIndexCount, 0, chunk.fCount, chunk.fBase, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace skgpu
|
||||
|
@ -12,43 +12,64 @@
|
||||
#include "src/gpu/tessellate/PathTessellator.h"
|
||||
|
||||
class GrCaps;
|
||||
class GrGpuBuffer;
|
||||
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.
|
||||
class PathWedgeTessellator : public PathTessellator {
|
||||
class PathWedgeTessellator final : public PathTessellator {
|
||||
public:
|
||||
// Creates a wedge tessellator with the shader type best suited for the given path description.
|
||||
static PathTessellator* Make(SkArenaAlloc*,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f&,
|
||||
int numPathVerbs,
|
||||
const GrPipeline&,
|
||||
const GrCaps&,
|
||||
PatchAttribs = PatchAttribs::kNone);
|
||||
static PathWedgeTessellator* Make(SkArenaAlloc* arena,
|
||||
bool infinitySupport,
|
||||
PatchAttribs attribs = PatchAttribs::kNone) {
|
||||
return arena->make<PathWedgeTessellator>(infinitySupport, attribs);
|
||||
}
|
||||
|
||||
void prepare(GrMeshDrawTarget*, const PathDrawList&, int totalCombinedPathVerbCnt) override;
|
||||
PathWedgeTessellator(bool infinitySupport, PatchAttribs attribs = PatchAttribs::kNone)
|
||||
: PathTessellator(infinitySupport, attribs) {
|
||||
fAttribs |= PatchAttribs::kFanPoint;
|
||||
}
|
||||
|
||||
void prepare(GrMeshDrawTarget*,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
const PathDrawList&,
|
||||
int totalCombinedPathVerbCnt) final;
|
||||
|
||||
// Size of the vertex buffer to use when rendering with a fixed count shader.
|
||||
constexpr static int FixedVertexBufferSize(int maxFixedResolveLevel) {
|
||||
return (((1 << maxFixedResolveLevel) + 1) + 1/*fan vertex*/) * sizeof(SkPoint);
|
||||
}
|
||||
|
||||
// Writes the vertex buffer to use when rendering with a fixed count shader.
|
||||
static void WriteFixedVertexBuffer(VertexWriter, size_t bufferSize);
|
||||
|
||||
// Size of the index buffer to use when rendering with a fixed count shader.
|
||||
constexpr static int FixedIndexBufferSize(int maxFixedResolveLevel) {
|
||||
return (NumCurveTrianglesAtResolveLevel(maxFixedResolveLevel) + 1/*fan triangle*/) *
|
||||
3 * sizeof(uint16_t);
|
||||
}
|
||||
|
||||
// Writes the index buffer to use when rendering with a fixed count shader.
|
||||
static void WriteFixedIndexBuffer(VertexWriter vertexWriter, size_t bufferSize);
|
||||
|
||||
#if SK_GPU_V1
|
||||
void draw(GrOpFlushState*) const override;
|
||||
void prepareFixedCountBuffers(GrResourceProvider*) final;
|
||||
|
||||
void drawTessellated(GrOpFlushState*) const final;
|
||||
void drawFixedCount(GrOpFlushState*) const final;
|
||||
#endif
|
||||
|
||||
private:
|
||||
PathWedgeTessellator(GrPathTessellationShader* shader, PatchAttribs attribs)
|
||||
: PathTessellator(shader, attribs) {}
|
||||
|
||||
GrVertexChunkArray fVertexChunkArray;
|
||||
|
||||
// If using fixed count, this is the number of vertices we need to emit per instance.
|
||||
int fFixedIndexCount;
|
||||
sk_sp<const GrGpuBuffer> fFixedCountVertexBuffer;
|
||||
sk_sp<const GrGpuBuffer> fFixedCountIndexBuffer;
|
||||
};
|
||||
|
||||
} // namespace skgpu
|
||||
|
@ -38,6 +38,16 @@ enum class PatchAttribs {
|
||||
|
||||
GR_MAKE_BITFIELD_CLASS_OPS(PatchAttribs)
|
||||
|
||||
// Returns the packed size in bytes of a tessellation patch (or instance) in GPU buffers.
|
||||
constexpr size_t PatchStride(PatchAttribs attribs) {
|
||||
return sizeof(float) * 8 + // 4 control points
|
||||
(attribs & PatchAttribs::kFanPoint ? sizeof(float) * 2 : 0) +
|
||||
(attribs & PatchAttribs::kColor
|
||||
? (attribs & PatchAttribs::kWideColorIfEnabled ? sizeof(float)
|
||||
: sizeof(uint8_t)) * 4 : 0) +
|
||||
(attribs & PatchAttribs::kExplicitCurveType ? sizeof(float) : 0);
|
||||
}
|
||||
|
||||
// Use familiar type names from SkSL.
|
||||
template<int N> using vec = skvx::Vec<N, float>;
|
||||
using float2 = vec<2>;
|
||||
|
@ -28,6 +28,8 @@ public:
|
||||
this->setVertexAttributes(&kInputPointAttrib, 1);
|
||||
}
|
||||
|
||||
int maxTessellationSegments(const GrShaderCaps&) const override { SkUNREACHABLE; }
|
||||
|
||||
private:
|
||||
const char* name() const final { return "tessellate_SimpleTriangleShader"; }
|
||||
void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
|
||||
@ -54,6 +56,27 @@ std::unique_ptr<GrGeometryProcessor::ProgramImpl> SimpleTriangleShader::makeProg
|
||||
|
||||
} // namespace
|
||||
|
||||
GrPathTessellationShader* GrPathTessellationShader::Make(SkArenaAlloc* arena,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f& color,
|
||||
int totalCombinedPathVerbCnt,
|
||||
const GrPipeline& pipeline,
|
||||
skgpu::PatchAttribs attribs,
|
||||
const GrCaps& caps) {
|
||||
if (caps.shaderCaps()->tessellationSupport() &&
|
||||
totalCombinedPathVerbCnt >= caps.minPathVerbsForHwTessellation() &&
|
||||
!pipeline.usesLocalCoords() && // Our tessellation back door doesn't handle varyings.
|
||||
// Input color and explicit curve type workarounds aren't implemented yet for tessellation.
|
||||
!(attribs & (PatchAttribs::kColor | PatchAttribs::kExplicitCurveType))) {
|
||||
return GrPathTessellationShader::MakeHardwareTessellationShader(arena, viewMatrix, color,
|
||||
attribs);
|
||||
} else {
|
||||
return GrPathTessellationShader::MakeMiddleOutFixedCountShader(*caps.shaderCaps(), arena,
|
||||
viewMatrix, color,
|
||||
attribs);
|
||||
}
|
||||
}
|
||||
|
||||
GrPathTessellationShader* GrPathTessellationShader::MakeSimpleTriangleShader(
|
||||
SkArenaAlloc* arena, const SkMatrix& viewMatrix, const SkPMColor4f& color) {
|
||||
return arena->make<SimpleTriangleShader>(viewMatrix, color);
|
||||
@ -92,6 +115,12 @@ float2 eval_rational_cubic(float4x3 P, float T) {
|
||||
|
||||
void GrPathTessellationShader::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
|
||||
const auto& shader = args.fGeomProc.cast<GrPathTessellationShader>();
|
||||
|
||||
// We should use explicit curve type when, and only when, there isn't infinity support.
|
||||
// Otherwise the GPU can infer curve type based on infinity.
|
||||
SkASSERT(args.fShaderCaps->infinitySupport() !=
|
||||
(shader.fAttribs & PatchAttribs::kExplicitCurveType));
|
||||
|
||||
args.fVaryingHandler->emitAttributes(shader);
|
||||
|
||||
// Vertex shader.
|
||||
|
@ -19,15 +19,15 @@ public:
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f&);
|
||||
|
||||
// How many triangles are in a curve with 2^resolveLevel line segments?
|
||||
constexpr static int NumCurveTrianglesAtResolveLevel(int resolveLevel) {
|
||||
// resolveLevel=0 -> 0 line segments -> 0 triangles
|
||||
// resolveLevel=1 -> 2 line segments -> 1 triangle
|
||||
// resolveLevel=2 -> 4 line segments -> 3 triangles
|
||||
// resolveLevel=3 -> 8 line segments -> 7 triangles
|
||||
// ...
|
||||
return (1 << resolveLevel) - 1;
|
||||
}
|
||||
// Creates either a hardware tessellation or middle-out instanced shader, depending on support
|
||||
// and which is expected to perform better.
|
||||
static GrPathTessellationShader* Make(SkArenaAlloc*,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f&,
|
||||
int totalCombinedPathVerbCnt,
|
||||
const GrPipeline&,
|
||||
skgpu::PatchAttribs,
|
||||
const GrCaps&);
|
||||
|
||||
// Uses instanced draws to triangulate curves with a "middle-out" topology. Middle-out draws a
|
||||
// triangle with vertices at T=[0, 1/2, 1] and then recurses breadth first:
|
||||
@ -50,39 +50,6 @@ public:
|
||||
const SkPMColor4f&,
|
||||
skgpu::PatchAttribs);
|
||||
|
||||
// This is the largest number of segments the middle-out shader will accept in a single
|
||||
// instance. If a curve requires more segments, it needs to be chopped.
|
||||
constexpr static int kMaxFixedCountSegments = 32;
|
||||
constexpr static int kMaxFixedCountResolveLevel = 5; // log2(kMaxFixedCountSegments)
|
||||
static_assert(kMaxFixedCountSegments == 1 << kMaxFixedCountResolveLevel);
|
||||
|
||||
// These functions define the vertex and index buffers that should be bound when drawing with
|
||||
// the middle-out fixed count shader. The data sequence is identical for any length of
|
||||
// tessellation segments, so the caller can use them with any instance length (up to
|
||||
// kMaxFixedCountResolveLevel).
|
||||
constexpr static int SizeOfVertexBufferForMiddleOutCurves() {
|
||||
constexpr int kMaxVertexCount = (1 << kMaxFixedCountResolveLevel) + 1;
|
||||
return kMaxVertexCount * kMiddleOutVertexStride;
|
||||
}
|
||||
static void InitializeVertexBufferForMiddleOutCurves(skgpu::VertexWriter, size_t bufferSize);
|
||||
|
||||
constexpr static size_t SizeOfIndexBufferForMiddleOutCurves() {
|
||||
constexpr int kMaxTriangleCount =
|
||||
NumCurveTrianglesAtResolveLevel(kMaxFixedCountResolveLevel);
|
||||
return kMaxTriangleCount * 3 * sizeof(uint16_t);
|
||||
}
|
||||
static void InitializeIndexBufferForMiddleOutCurves(skgpu::VertexWriter, size_t bufferSize);
|
||||
|
||||
constexpr static int SizeOfVertexBufferForMiddleOutWedges() {
|
||||
return SizeOfVertexBufferForMiddleOutCurves() + kMiddleOutVertexStride;
|
||||
}
|
||||
static void InitializeVertexBufferForMiddleOutWedges(skgpu::VertexWriter, size_t bufferSize);
|
||||
|
||||
constexpr static size_t SizeOfIndexBufferForMiddleOutWedges() {
|
||||
return SizeOfIndexBufferForMiddleOutCurves() + 3 * sizeof(uint16_t);
|
||||
}
|
||||
static void InitializeIndexBufferForMiddleOutWedges(skgpu::VertexWriter, size_t bufferSize);
|
||||
|
||||
// Uses GPU tessellation shaders to linearize, triangulate, and render curves.
|
||||
//
|
||||
// If PatchAttribs::kFanPoint is set, an additional triangle is added, connecting the base of
|
||||
@ -153,6 +120,8 @@ public:
|
||||
const GrAppliedHardClip&,
|
||||
GrPipeline::InputFlags = GrPipeline::InputFlags::kNone);
|
||||
|
||||
virtual int maxTessellationSegments(const GrShaderCaps&) const = 0;
|
||||
|
||||
protected:
|
||||
constexpr static size_t kMiddleOutVertexStride = 2 * sizeof(float);
|
||||
|
||||
|
@ -40,6 +40,11 @@ public:
|
||||
constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
|
||||
kFloat2_GrSLType};
|
||||
this->setVertexAttributes(&kInputPointAttrib, 1);
|
||||
SkASSERT(this->vertexStride() * 5 == skgpu::PatchStride(fAttribs));
|
||||
}
|
||||
|
||||
int maxTessellationSegments(const GrShaderCaps& shaderCaps) const override {
|
||||
return shaderCaps.maxTessellationSegments();
|
||||
}
|
||||
|
||||
private:
|
||||
@ -175,6 +180,14 @@ public:
|
||||
constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
|
||||
kFloat2_GrSLType};
|
||||
this->setVertexAttributes(&kInputPointAttrib, 1);
|
||||
SkASSERT(this->vertexStride() * 4 == skgpu::PatchStride(fAttribs));
|
||||
}
|
||||
|
||||
int maxTessellationSegments(const GrShaderCaps& shaderCaps) const override {
|
||||
// This shader tessellates T=0..(1/2) on the first side of the canonical triangle and
|
||||
// T=(1/2)..1 on the second side. This means we get double the max tessellation segments for
|
||||
// the range T=0..1.
|
||||
return shaderCaps.maxTessellationSegments() * 2;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "src/core/SkMathPriv.h"
|
||||
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
|
||||
#include "src/gpu/tessellate/PathTessellator.h"
|
||||
#include "src/gpu/tessellate/Tessellation.h"
|
||||
#include "src/gpu/tessellate/WangsFormula.h"
|
||||
|
||||
@ -57,12 +58,17 @@ public:
|
||||
}
|
||||
this->setInstanceAttributes(fInstanceAttribs.data(), fInstanceAttribs.count());
|
||||
SkASSERT(fInstanceAttribs.count() <= kMaxInstanceAttribCount);
|
||||
SkASSERT(this->instanceStride() == skgpu::PatchStride(fAttribs));
|
||||
|
||||
constexpr static Attribute kVertexAttrib("resolveLevel_and_idx", kFloat2_GrVertexAttribType,
|
||||
kFloat2_GrSLType);
|
||||
this->setVertexAttributes(&kVertexAttrib, 1);
|
||||
}
|
||||
|
||||
int maxTessellationSegments(const GrShaderCaps&) const override {
|
||||
return 1 << skgpu::PathTessellator::kMaxFixedResolveLevel;
|
||||
}
|
||||
|
||||
private:
|
||||
const char* name() const final { return "tessellate_MiddleOutShader"; }
|
||||
void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
|
||||
@ -88,8 +94,10 @@ std::unique_ptr<GrGeometryProcessor::ProgramImpl> MiddleOutShader::makeProgramIm
|
||||
GrGPArgs* gpArgs) override {
|
||||
const MiddleOutShader& middleOutShader = shader.cast<MiddleOutShader>();
|
||||
v->defineConstant("PRECISION", skgpu::kTessellationPrecision);
|
||||
v->defineConstant("MAX_FIXED_RESOLVE_LEVEL", (float)kMaxFixedCountResolveLevel);
|
||||
v->defineConstant("MAX_FIXED_SEGMENTS", (float)kMaxFixedCountSegments);
|
||||
v->defineConstant("MAX_FIXED_RESOLVE_LEVEL",
|
||||
(float)skgpu::PathTessellator::kMaxFixedResolveLevel);
|
||||
v->defineConstant("MAX_FIXED_SEGMENTS",
|
||||
(float)(1 << skgpu::PathTessellator::kMaxFixedResolveLevel));
|
||||
v->insertFunction(skgpu::wangs_formula::as_sksl().c_str());
|
||||
if (middleOutShader.fAttribs & PatchAttribs::kExplicitCurveType) {
|
||||
v->insertFunction(SkStringPrintf(R"(
|
||||
@ -206,107 +214,3 @@ GrPathTessellationShader* GrPathTessellationShader::MakeMiddleOutFixedCountShade
|
||||
const SkPMColor4f& color, PatchAttribs attribs) {
|
||||
return arena->make<MiddleOutShader>(shaderCaps, viewMatrix, color, attribs);
|
||||
}
|
||||
|
||||
void GrPathTessellationShader::InitializeVertexBufferForMiddleOutCurves(VertexWriter vertexWriter,
|
||||
size_t bufferSize) {
|
||||
SkASSERT(bufferSize >= kMiddleOutVertexStride * 2);
|
||||
SkASSERT(bufferSize % kMiddleOutVertexStride == 0);
|
||||
int vertexCount = bufferSize / kMiddleOutVertexStride;
|
||||
SkASSERT(vertexCount > 3);
|
||||
SkDEBUGCODE(VertexWriter end = vertexWriter.makeOffset(vertexCount * kMiddleOutVertexStride);)
|
||||
|
||||
// Lay out the vertices in "middle-out" order:
|
||||
//
|
||||
// T= 0/1, 1/1, ; resolveLevel=0
|
||||
// 1/2, ; resolveLevel=1 (0/2 and 2/2 are already in resolveLevel 0)
|
||||
// 1/4, 3/4, ; resolveLevel=2 (2/4 is already in resolveLevel 1)
|
||||
// 1/8, 3/8, 5/8, 7/8, ; resolveLevel=3 (2/8 and 6/8 are already in resolveLevel 2)
|
||||
// ... ; resolveLevel=...
|
||||
//
|
||||
// Resolve level 0 is just the beginning and ending vertices.
|
||||
vertexWriter << (float)0/*resolveLevel*/ << (float)0/*idx*/;
|
||||
vertexWriter << (float)0/*resolveLevel*/ << (float)1/*idx*/;
|
||||
|
||||
// Resolve levels 1..kMaxResolveLevel.
|
||||
int maxResolveLevel = SkPrevLog2(vertexCount - 1);
|
||||
SkASSERT((1 << maxResolveLevel) + 1 == vertexCount);
|
||||
for (int resolveLevel = 1; resolveLevel <= maxResolveLevel; ++resolveLevel) {
|
||||
int numSegmentsInResolveLevel = 1 << resolveLevel;
|
||||
// Write out the odd vertices in this resolveLevel. The even vertices were already written
|
||||
// out in previous resolveLevels and will be indexed from there.
|
||||
for (int i = 1; i < numSegmentsInResolveLevel; i += 2) {
|
||||
vertexWriter << (float)resolveLevel << (float)i;
|
||||
}
|
||||
}
|
||||
|
||||
SkASSERT(vertexWriter == end);
|
||||
}
|
||||
|
||||
void GrPathTessellationShader::InitializeVertexBufferForMiddleOutWedges(VertexWriter vertexWriter,
|
||||
size_t bufferSize) {
|
||||
SkASSERT(bufferSize >= kMiddleOutVertexStride);
|
||||
// Start out with the fan point. A negative resolve level indicates the fan point.
|
||||
vertexWriter << (float)-1/*resolveLevel*/ << (float)-1/*idx*/;
|
||||
|
||||
InitializeVertexBufferForMiddleOutCurves(std::move(vertexWriter),
|
||||
bufferSize - kMiddleOutVertexStride);
|
||||
}
|
||||
|
||||
static void fill_index_buffer_for_curves(VertexWriter vertexWriter,
|
||||
size_t bufferSize,
|
||||
uint16_t baseIndex) {
|
||||
SkASSERT(bufferSize % (sizeof(uint16_t) * 3) == 0);
|
||||
int triangleCount = bufferSize / (sizeof(uint16_t) * 3);
|
||||
SkASSERT(triangleCount >= 1);
|
||||
SkTArray<std::array<uint16_t, 3>> indexData(triangleCount);
|
||||
|
||||
// Connect the vertices with a middle-out triangulation. Refer to
|
||||
// InitializeVertexBufferForMiddleOutCurves() for the exact vertex ordering.
|
||||
//
|
||||
// Resolve level 1 is just a single triangle at T=[0, 1/2, 1].
|
||||
const auto* neighborInLastResolveLevel = &indexData.push_back({baseIndex,
|
||||
(uint16_t)(baseIndex + 2),
|
||||
(uint16_t)(baseIndex + 1)});
|
||||
|
||||
// Resolve levels 2..maxResolveLevel
|
||||
int maxResolveLevel = SkPrevLog2(triangleCount + 1);
|
||||
uint16_t nextIndex = baseIndex + 3;
|
||||
SkASSERT(GrPathTessellationShader::NumCurveTrianglesAtResolveLevel(maxResolveLevel) ==
|
||||
triangleCount);
|
||||
for (int resolveLevel = 2; resolveLevel <= maxResolveLevel; ++resolveLevel) {
|
||||
SkDEBUGCODE(auto* firstTriangleInCurrentResolveLevel = indexData.end());
|
||||
int numOuterTrianglelsInResolveLevel = 1 << (resolveLevel - 1);
|
||||
SkASSERT(numOuterTrianglelsInResolveLevel % 2 == 0);
|
||||
int numTrianglePairsInResolveLevel = numOuterTrianglelsInResolveLevel >> 1;
|
||||
for (int i = 0; i < numTrianglePairsInResolveLevel; ++i) {
|
||||
// First triangle shares the left edge of "neighborInLastResolveLevel".
|
||||
indexData.push_back({(*neighborInLastResolveLevel)[0],
|
||||
nextIndex++,
|
||||
(*neighborInLastResolveLevel)[1]});
|
||||
// Second triangle shares the right edge of "neighborInLastResolveLevel".
|
||||
indexData.push_back({(*neighborInLastResolveLevel)[1],
|
||||
nextIndex++,
|
||||
(*neighborInLastResolveLevel)[2]});
|
||||
++neighborInLastResolveLevel;
|
||||
}
|
||||
SkASSERT(neighborInLastResolveLevel == firstTriangleInCurrentResolveLevel);
|
||||
}
|
||||
SkASSERT(indexData.count() == triangleCount);
|
||||
SkASSERT(nextIndex == baseIndex + triangleCount + 2);
|
||||
|
||||
vertexWriter.writeArray(indexData.data(), indexData.count());
|
||||
}
|
||||
|
||||
void GrPathTessellationShader::InitializeIndexBufferForMiddleOutCurves(VertexWriter vertexWriter,
|
||||
size_t bufferSize) {
|
||||
fill_index_buffer_for_curves(std::move(vertexWriter), bufferSize, 0);
|
||||
}
|
||||
|
||||
void GrPathTessellationShader::InitializeIndexBufferForMiddleOutWedges(VertexWriter vertexWriter,
|
||||
size_t bufferSize) {
|
||||
SkASSERT(bufferSize >= sizeof(uint16_t) * 3);
|
||||
// Start out with the fan triangle.
|
||||
vertexWriter << (uint16_t)0 << (uint16_t)1 << (uint16_t)2;
|
||||
|
||||
fill_index_buffer_for_curves(std::move(vertexWriter), bufferSize - sizeof(uint16_t) * 3, 1);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user