Revert "Delete HW tessellation shaders for curve/wedge tessellation"
This reverts commit 038818ba8e
.
Reason for revert: breaking g3 roll, need to update public bzl file I think
Original change's description:
> Delete HW tessellation shaders for curve/wedge tessellation
>
> GrPathTessellationShader_MiddleOut.cpp is just moved into
> GrPathTessellationShader.cpp, and a few factories are cleaned up since
> we don't have to differentiate between middle-out or hardware.
>
> Bug: skia:13263
> Change-Id: I420faa614a89ef1a2c0f1075d1f8a067d15e9a81
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/534200
> Reviewed-by: Greg Daniel <egdaniel@google.com>
> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Bug: skia:13263
Change-Id: I42367a21c2bf1a4283e5d9b8d0e00961f9dea2e7
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/536039
Auto-Submit: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
This commit is contained in:
parent
644fc2e540
commit
7d9f07c108
@ -139,9 +139,11 @@ DEF_PATH_TESS_BENCH(GrPathCurveTessellator, make_cubic_path(8), SkMatrix::I()) {
|
||||
auto tess = PathCurveTessellator::Make(&arena,
|
||||
fTarget->caps().shaderCaps()->infinitySupport());
|
||||
tess->prepare(fTarget.get(),
|
||||
kMaxParametricSegments,
|
||||
fMatrix,
|
||||
{gAlmostIdentity, fPath, SK_PMColor4fTRANSPARENT},
|
||||
fPath.countVerbs());
|
||||
fPath.countVerbs(),
|
||||
true);
|
||||
}
|
||||
|
||||
DEF_PATH_TESS_BENCH(GrPathWedgeTessellator, make_cubic_path(8), SkMatrix::I()) {
|
||||
@ -151,9 +153,11 @@ DEF_PATH_TESS_BENCH(GrPathWedgeTessellator, make_cubic_path(8), SkMatrix::I()) {
|
||||
auto tess = PathWedgeTessellator::Make(&arena,
|
||||
fTarget->caps().shaderCaps()->infinitySupport());
|
||||
tess->prepare(fTarget.get(),
|
||||
kMaxParametricSegments,
|
||||
fMatrix,
|
||||
{gAlmostIdentity, fPath, SK_PMColor4fTRANSPARENT},
|
||||
fPath.countVerbs());
|
||||
fPath.countVerbs(),
|
||||
true);
|
||||
}
|
||||
|
||||
static void benchmark_wangs_formula_cubic_log2(const SkMatrix& matrix, const SkPath& path) {
|
||||
|
@ -316,6 +316,8 @@ skia_gpu_sources = [
|
||||
# tessellate/shaders
|
||||
"$_src/gpu/ganesh/tessellate/shaders/GrPathTessellationShader.cpp",
|
||||
"$_src/gpu/ganesh/tessellate/shaders/GrPathTessellationShader.h",
|
||||
"$_src/gpu/ganesh/tessellate/shaders/GrPathTessellationShader_Hardware.cpp",
|
||||
"$_src/gpu/ganesh/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp",
|
||||
"$_src/gpu/ganesh/tessellate/shaders/GrStrokeTessellationShader.cpp",
|
||||
"$_src/gpu/ganesh/tessellate/shaders/GrStrokeTessellationShader.h",
|
||||
"$_src/gpu/ganesh/tessellate/shaders/GrStrokeTessellationShader_HardwareImpl.cpp",
|
||||
|
@ -29,7 +29,9 @@ namespace {
|
||||
|
||||
enum class Mode {
|
||||
kWedgeMiddleOut,
|
||||
kCurveMiddleOut
|
||||
kCurveMiddleOut,
|
||||
kWedgeTessellate,
|
||||
kCurveTessellate
|
||||
};
|
||||
|
||||
static const char* ModeName(Mode mode) {
|
||||
@ -38,6 +40,10 @@ static const char* ModeName(Mode mode) {
|
||||
return "MiddleOutShader (kWedges)";
|
||||
case Mode::kCurveMiddleOut:
|
||||
return "MiddleOutShader (kCurves)";
|
||||
case Mode::kWedgeTessellate:
|
||||
return "HardwareWedgeShader";
|
||||
case Mode::kCurveTessellate:
|
||||
return "HardwareCurveShader";
|
||||
}
|
||||
SkUNREACHABLE;
|
||||
}
|
||||
@ -76,9 +82,33 @@ private:
|
||||
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 = (fMode == Mode::kWedgeMiddleOut || fMode == Mode::kCurveMiddleOut) ?
|
||||
numVerbsToGetMiddleOut : numVerbsToGetTessellation;
|
||||
auto* tessShader = GrPathTessellationShader::Make(alloc,
|
||||
shaderMatrix,
|
||||
kCyan,
|
||||
numVerbs,
|
||||
*pipeline,
|
||||
fTessellator->patchAttribs(),
|
||||
caps);
|
||||
fProgram = GrTessellationShader::MakeProgram({alloc, flushState->writeView(),
|
||||
flushState->usesMSAASurface(),
|
||||
&flushState->dstProxyView(),
|
||||
flushState->renderPassBarriers(),
|
||||
GrLoadOp::kClear, &flushState->caps()},
|
||||
tessShader,
|
||||
pipeline,
|
||||
&GrUserStencilSettings::kUnused);
|
||||
|
||||
|
||||
int maxSegments = tessShader->maxTessellationSegments(*caps.shaderCaps());
|
||||
PathTessellator::PathDrawList pathList{pathMatrix, fPath, kCyan};
|
||||
if (fMode == Mode::kCurveMiddleOut) {
|
||||
|
||||
if (fMode == Mode::kCurveTessellate || fMode == Mode::kCurveMiddleOut) {
|
||||
// This emulates what PathStencilCoverOp does when using curves, except we include the
|
||||
// middle-out triangles directly in the written patches for convenience (normally they
|
||||
// use a simple triangle pipeline). But PathCurveTessellator only knows how to read
|
||||
@ -96,35 +126,20 @@ private:
|
||||
}
|
||||
|
||||
auto* tess = PathCurveTessellator::Make(alloc, shaderCaps.infinitySupport());
|
||||
tess->prepareWithTriangles(flushState, shaderMatrix, &triangles, pathList,
|
||||
fPath.countVerbs());
|
||||
tess->prepareWithTriangles(flushState, maxSegments, shaderMatrix, &triangles, pathList,
|
||||
fPath.countVerbs(),tessShader->willUseTessellationShaders());
|
||||
fTessellator = tess;
|
||||
} else {
|
||||
// This emulates what PathStencilCoverOp does when using wedges.
|
||||
fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
|
||||
fTessellator->prepare(flushState, shaderMatrix, pathList, fPath.countVerbs());
|
||||
fTessellator->prepare(flushState, maxSegments, shaderMatrix, pathList,
|
||||
fPath.countVerbs(), tessShader->willUseTessellationShaders());
|
||||
}
|
||||
|
||||
auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState, std::move(fProcessors),
|
||||
fPipelineFlags);
|
||||
auto* tessShader = GrPathTessellationShader::Make(*caps.shaderCaps(),
|
||||
alloc,
|
||||
shaderMatrix,
|
||||
kCyan,
|
||||
fTessellator->patchAttribs());
|
||||
fProgram = GrTessellationShader::MakeProgram({alloc, flushState->writeView(),
|
||||
flushState->usesMSAASurface(),
|
||||
&flushState->dstProxyView(),
|
||||
flushState->renderPassBarriers(),
|
||||
GrLoadOp::kClear, &flushState->caps()},
|
||||
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;
|
||||
@ -191,6 +206,9 @@ void SamplePathTessellators::onDrawContent(SkCanvas* canvas) {
|
||||
error = "GPU Only.";
|
||||
} else if (!skgpu::v1::TessellationPathRenderer::IsSupported(*ctx->priv().caps())) {
|
||||
error = "TessellationPathRenderer not supported.";
|
||||
} else if (fMode >= Mode::kWedgeTessellate &&
|
||||
!ctx->priv().caps()->shaderCaps()->tessellationSupport()) {
|
||||
error.printf("%s requires hardware tessellation support.", ModeName(fMode));
|
||||
}
|
||||
if (!error.isEmpty()) {
|
||||
canvas->clear(SK_ColorRED);
|
||||
@ -311,6 +329,8 @@ bool SamplePathTessellators::onChar(SkUnichar unichar) {
|
||||
return true;
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
fMode = (Mode)(unichar - '1');
|
||||
return true;
|
||||
}
|
||||
|
@ -185,6 +185,8 @@ cc_library(
|
||||
"//src/gpu/ganesh/ops:TessellationPathRenderer_src",
|
||||
"//src/gpu/ganesh/ops:TextureOp_src",
|
||||
"//src/gpu/ganesh/ops:TriangulatingPathRenderer_src",
|
||||
"//src/gpu/ganesh/tessellate/shaders:GrPathTessellationShader_Hardware_src",
|
||||
"//src/gpu/ganesh/tessellate/shaders:GrPathTessellationShader_MiddleOut_src",
|
||||
"//src/gpu/ganesh/tessellate/shaders:GrPathTessellationShader_src",
|
||||
"//src/gpu/ganesh/tessellate/shaders:GrStrokeTessellationShader_HardwareImpl_src",
|
||||
"//src/gpu/ganesh/tessellate/shaders:GrStrokeTessellationShader_InstancedImpl_src",
|
||||
|
@ -47,6 +47,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
int maxTessellationSegments(const GrShaderCaps&) const override { SkUNREACHABLE; }
|
||||
|
||||
private:
|
||||
const char* name() const final { return "tessellate_HullShader"; }
|
||||
void addToKey(const GrShaderCaps&, KeyBuilder*) const final {}
|
||||
@ -251,11 +253,13 @@ void PathInnerTriangulateOp::prePreparePrograms(const GrTessellationShader::Prog
|
||||
if (!isLinear) {
|
||||
fTessellator = PathCurveTessellator::Make(args.fArena,
|
||||
args.fCaps->shaderCaps()->infinitySupport());
|
||||
auto* tessShader = GrPathTessellationShader::Make(*args.fCaps->shaderCaps(),
|
||||
args.fArena,
|
||||
auto* tessShader = GrPathTessellationShader::Make(args.fArena,
|
||||
fViewMatrix,
|
||||
SK_PMColor4fTRANSPARENT,
|
||||
fTessellator->patchAttribs());
|
||||
fPath.countVerbs(),
|
||||
*pipelineForStencils,
|
||||
fTessellator->patchAttribs(),
|
||||
*args.fCaps);
|
||||
const GrUserStencilSettings* stencilPathSettings =
|
||||
GrPathTessellationShader::StencilPathSettings(GrFillRuleForSkPath(fPath));
|
||||
fStencilCurvesProgram = GrTessellationShader::MakeProgram(args,
|
||||
@ -416,11 +420,15 @@ void PathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
|
||||
|
||||
if (fTessellator) {
|
||||
auto tessShader = &fStencilCurvesProgram->geomProc().cast<GrPathTessellationShader>();
|
||||
int maxSegments = tessShader->maxTessellationSegments(*caps.shaderCaps());
|
||||
|
||||
fTessellator->prepareWithTriangles(flushState,
|
||||
maxSegments,
|
||||
tessShader->viewMatrix(),
|
||||
&fFanBreadcrumbs,
|
||||
{SkMatrix::I(), fPath, SK_PMColor4fTRANSPARENT},
|
||||
fPath.countVerbs());
|
||||
fPath.countVerbs(),
|
||||
tessShader->willUseTessellationShaders());
|
||||
}
|
||||
|
||||
if (!caps.shaderCaps()->vertexIDSupport()) {
|
||||
@ -444,7 +452,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
|
||||
}
|
||||
|
@ -163,11 +163,13 @@ void PathStencilCoverOp::prePreparePrograms(const GrTessellationShader::ProgramA
|
||||
fTessellator = PathWedgeTessellator::Make(args.fArena,
|
||||
args.fCaps->shaderCaps()->infinitySupport());
|
||||
}
|
||||
auto* tessShader = GrPathTessellationShader::Make(*args.fCaps->shaderCaps(),
|
||||
args.fArena,
|
||||
auto* tessShader = GrPathTessellationShader::Make(args.fArena,
|
||||
shaderMatrix,
|
||||
SK_PMColor4fTRANSPARENT,
|
||||
fTessellator->patchAttribs());
|
||||
fTotalCombinedPathVerbCnt,
|
||||
*stencilPipeline,
|
||||
fTessellator->patchAttribs(),
|
||||
*args.fCaps);
|
||||
fStencilPathProgram = GrTessellationShader::MakeProgram(args,
|
||||
tessShader,
|
||||
stencilPipeline,
|
||||
@ -261,9 +263,11 @@ void PathStencilCoverOp::onPrepare(GrOpFlushState* flushState) {
|
||||
|
||||
auto tessShader = &fStencilPathProgram->geomProc().cast<GrPathTessellationShader>();
|
||||
fTessellator->prepare(flushState,
|
||||
tessShader->maxTessellationSegments(*flushState->caps().shaderCaps()),
|
||||
tessShader->viewMatrix(),
|
||||
*fPathDrawList,
|
||||
fTotalCombinedPathVerbCnt);
|
||||
fTotalCombinedPathVerbCnt,
|
||||
tessShader->willUseTessellationShaders());
|
||||
|
||||
if (fCoverBBoxProgram) {
|
||||
size_t instanceStride = fCoverBBoxProgram->geomProc().instanceStride();
|
||||
@ -334,7 +338,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
|
||||
}
|
||||
|
@ -75,11 +75,13 @@ void PathTessellateOp::prepareTessellator(const GrTessellationShader::ProgramArg
|
||||
fTessellator = PathWedgeTessellator::Make(args.fArena,
|
||||
args.fCaps->shaderCaps()->infinitySupport(),
|
||||
fPatchAttribs);
|
||||
auto* tessShader = GrPathTessellationShader::Make(*args.fCaps->shaderCaps(),
|
||||
args.fArena,
|
||||
auto* tessShader = GrPathTessellationShader::Make(args.fArena,
|
||||
fShaderMatrix,
|
||||
this->headDraw().fColor,
|
||||
fTessellator->patchAttribs());
|
||||
fTotalCombinedPathVerbCnt,
|
||||
*pipeline,
|
||||
fTessellator->patchAttribs(),
|
||||
*args.fCaps);
|
||||
fTessellationProgram = GrTessellationShader::MakeProgram(args, tessShader, pipeline, fStencil);
|
||||
}
|
||||
|
||||
@ -106,10 +108,13 @@ void PathTessellateOp::onPrepare(GrOpFlushState* flushState) {
|
||||
&flushState->caps()}, flushState->detachAppliedClip());
|
||||
SkASSERT(fTessellator);
|
||||
}
|
||||
auto tessShader = &fTessellationProgram->geomProc().cast<GrPathTessellationShader>();
|
||||
fTessellator->prepare(flushState,
|
||||
tessShader->maxTessellationSegments(*flushState->caps().shaderCaps()),
|
||||
fShaderMatrix,
|
||||
*fPathDrawList,
|
||||
fTotalCombinedPathVerbCnt);
|
||||
fTotalCombinedPathVerbCnt,
|
||||
tessShader->willUseTessellationShaders());
|
||||
}
|
||||
|
||||
void PathTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
|
||||
@ -118,7 +123,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
|
||||
|
@ -156,14 +156,16 @@ SKGPU_DECLARE_STATIC_UNIQUE_KEY(gFixedCountCurveIndexBufferKey);
|
||||
|
||||
void PathCurveTessellator::prepareWithTriangles(
|
||||
GrMeshDrawTarget* target,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
GrInnerFanTriangulator::BreadcrumbTriangleList* extraTriangles,
|
||||
const PathDrawList& pathDrawList,
|
||||
int totalCombinedPathVerbCnt) {
|
||||
int totalCombinedPathVerbCnt,
|
||||
bool willUseTessellationShaders) {
|
||||
int patchPreallocCount = FixedCountCurves::PreallocCount(totalCombinedPathVerbCnt) +
|
||||
(extraTriangles ? extraTriangles->count() : 0);
|
||||
if (patchPreallocCount) {
|
||||
CurveWriter writer{fAttribs, skgpu::kMaxParametricSegments,
|
||||
CurveWriter writer{fAttribs, maxTessellationSegments,
|
||||
target, &fVertexChunkArray, patchPreallocCount};
|
||||
|
||||
// Write out extra space-filling triangles to connect the curve patches with any external
|
||||
@ -191,7 +193,12 @@ void PathCurveTessellator::prepareWithTriangles(
|
||||
int resolveLevel = write_curve_patches(std::move(writer), shaderMatrix, pathDrawList);
|
||||
this->updateResolveLevel(resolveLevel);
|
||||
}
|
||||
if (!willUseTessellationShaders) {
|
||||
this->prepareFixedCountBuffers(target);
|
||||
}
|
||||
}
|
||||
|
||||
void PathCurveTessellator::prepareFixedCountBuffers(GrMeshDrawTarget* target) {
|
||||
GrResourceProvider* rp = target->resourceProvider();
|
||||
|
||||
SKGPU_DEFINE_STATIC_UNIQUE_KEY(gFixedCountCurveVertexBufferKey);
|
||||
@ -209,7 +216,14 @@ void PathCurveTessellator::prepareWithTriangles(
|
||||
FixedCountCurves::WriteIndexBuffer);
|
||||
}
|
||||
|
||||
void PathCurveTessellator::draw(GrOpFlushState* flushState) const {
|
||||
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;
|
||||
}
|
||||
@ -232,17 +246,7 @@ void PathCurveTessellator::drawHullInstances(GrOpFlushState* flushState,
|
||||
SKGPU_DECLARE_STATIC_UNIQUE_KEY(gFixedCountWedgesVertexBufferKey);
|
||||
SKGPU_DECLARE_STATIC_UNIQUE_KEY(gFixedCountWedgesIndexBufferKey);
|
||||
|
||||
void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
|
||||
const SkMatrix& shaderMatrix,
|
||||
const PathDrawList& pathDrawList,
|
||||
int totalCombinedPathVerbCnt) {
|
||||
if (int patchPreallocCount = FixedCountWedges::PreallocCount(totalCombinedPathVerbCnt)) {
|
||||
WedgeWriter writer{fAttribs, skgpu::kMaxParametricSegments,
|
||||
target, &fVertexChunkArray, patchPreallocCount};
|
||||
int resolveLevel = write_wedge_patches(std::move(writer), shaderMatrix, pathDrawList);
|
||||
this->updateResolveLevel(resolveLevel);
|
||||
}
|
||||
|
||||
void PathWedgeTessellator::prepareFixedCountBuffers(GrMeshDrawTarget* target) {
|
||||
GrResourceProvider* rp = target->resourceProvider();
|
||||
|
||||
SKGPU_DEFINE_STATIC_UNIQUE_KEY(gFixedCountWedgesVertexBufferKey);
|
||||
@ -260,7 +264,31 @@ void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
|
||||
FixedCountWedges::WriteIndexBuffer);
|
||||
}
|
||||
|
||||
void PathWedgeTessellator::draw(GrOpFlushState* flushState) const {
|
||||
void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
const PathDrawList& pathDrawList,
|
||||
int totalCombinedPathVerbCnt,
|
||||
bool willUseTessellationShaders) {
|
||||
if (int patchPreallocCount = FixedCountWedges::PreallocCount(totalCombinedPathVerbCnt)) {
|
||||
WedgeWriter writer{fAttribs, maxTessellationSegments,
|
||||
target, &fVertexChunkArray, patchPreallocCount};
|
||||
int resolveLevel = write_wedge_patches(std::move(writer), shaderMatrix, pathDrawList);
|
||||
this->updateResolveLevel(resolveLevel);
|
||||
}
|
||||
if (!willUseTessellationShaders) {
|
||||
this->prepareFixedCountBuffers(target);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -54,15 +54,32 @@ public:
|
||||
|
||||
PatchAttribs patchAttribs() const { return fAttribs; }
|
||||
|
||||
// Initializes the internal vertex and index buffers required for drawFixedCount().
|
||||
virtual void prepareFixedCountBuffers(GrMeshDrawTarget*) = 0;
|
||||
|
||||
// Called before draw(). Prepares GPU buffers containing the geometry to tessellate.
|
||||
virtual void prepare(GrMeshDrawTarget* target,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
const PathDrawList& pathDrawList,
|
||||
int totalCombinedPathVerbCnt) = 0;
|
||||
int totalCombinedPathVerbCnt,
|
||||
bool willUseTessellationShaders) = 0;
|
||||
|
||||
// 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 draw(GrOpFlushState* flushState) const = 0;
|
||||
virtual void drawFixedCount(GrOpFlushState*) const = 0;
|
||||
|
||||
void draw(GrOpFlushState* flushState, bool willUseTessellationShaders) {
|
||||
if (willUseTessellationShaders) {
|
||||
this->drawTessellated(flushState);
|
||||
} else {
|
||||
this->drawFixedCount(flushState);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
PathTessellator(bool infinitySupport, PatchAttribs attribs) : fAttribs(attribs) {
|
||||
@ -85,6 +102,7 @@ protected:
|
||||
|
||||
GrVertexChunkArray fVertexChunkArray;
|
||||
|
||||
// If using fixed-count rendering, these are the vertex and index buffers.
|
||||
sk_sp<const GrGpuBuffer> fFixedVertexBuffer;
|
||||
sk_sp<const GrGpuBuffer> fFixedIndexBuffer;
|
||||
};
|
||||
@ -105,23 +123,32 @@ public:
|
||||
: PathTessellator(infinitySupport, attribs) {}
|
||||
|
||||
void prepareWithTriangles(GrMeshDrawTarget* target,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
GrInnerFanTriangulator::BreadcrumbTriangleList* extraTriangles,
|
||||
const PathDrawList& pathDrawList,
|
||||
int totalCombinedPathVerbCnt);
|
||||
int totalCombinedPathVerbCnt,
|
||||
bool willUseTessellationShaders);
|
||||
|
||||
void prepare(GrMeshDrawTarget* target,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
const PathDrawList& pathDrawList,
|
||||
int totalCombinedPathVerbCnt) final {
|
||||
int totalCombinedPathVerbCnt,
|
||||
bool willUseTessellationShaders) final {
|
||||
this->prepareWithTriangles(target,
|
||||
maxTessellationSegments,
|
||||
shaderMatrix,
|
||||
nullptr, // no extra triangles by default
|
||||
pathDrawList,
|
||||
totalCombinedPathVerbCnt);
|
||||
totalCombinedPathVerbCnt,
|
||||
willUseTessellationShaders);
|
||||
}
|
||||
|
||||
void draw(GrOpFlushState*) const final;
|
||||
void prepareFixedCountBuffers(GrMeshDrawTarget*) 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
|
||||
@ -147,11 +174,16 @@ public:
|
||||
}
|
||||
|
||||
void prepare(GrMeshDrawTarget* target,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
const PathDrawList& pathDrawList,
|
||||
int totalCombinedPathVerbCnt) final;
|
||||
int totalCombinedPathVerbCnt,
|
||||
bool willUseTessellationShaders) final;
|
||||
|
||||
void draw(GrOpFlushState*) const final;
|
||||
void prepareFixedCountBuffers(GrMeshDrawTarget*) final;
|
||||
|
||||
void drawTessellated(GrOpFlushState*) const final;
|
||||
void drawFixedCount(GrOpFlushState*) const final;
|
||||
};
|
||||
|
||||
} // namespace skgpu::v1
|
||||
|
@ -4,6 +4,33 @@ licenses(["notice"])
|
||||
|
||||
exports_files_legacy()
|
||||
|
||||
generated_cc_atom(
|
||||
name = "GrPathTessellationShader_Hardware_src",
|
||||
srcs = ["GrPathTessellationShader_Hardware.cpp"],
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
":GrPathTessellationShader_hdr",
|
||||
"//src/gpu/ganesh/glsl:GrGLSLVertexGeoBuilder_hdr",
|
||||
"//src/gpu/tessellate:Tessellation_hdr",
|
||||
"//src/gpu/tessellate:WangsFormula_hdr",
|
||||
],
|
||||
)
|
||||
|
||||
generated_cc_atom(
|
||||
name = "GrPathTessellationShader_MiddleOut_src",
|
||||
srcs = ["GrPathTessellationShader_MiddleOut.cpp"],
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
":GrPathTessellationShader_hdr",
|
||||
"//src/core:SkMathPriv_hdr",
|
||||
"//src/gpu:KeyBuilder_hdr",
|
||||
"//src/gpu/ganesh/glsl:GrGLSLVertexGeoBuilder_hdr",
|
||||
"//src/gpu/tessellate:FixedCountBufferUtils_hdr",
|
||||
"//src/gpu/tessellate:Tessellation_hdr",
|
||||
"//src/gpu/tessellate:WangsFormula_hdr",
|
||||
],
|
||||
)
|
||||
|
||||
generated_cc_atom(
|
||||
name = "GrPathTessellationShader_hdr",
|
||||
hdrs = ["GrPathTessellationShader.h"],
|
||||
@ -20,15 +47,10 @@ generated_cc_atom(
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
":GrPathTessellationShader_hdr",
|
||||
"//src/core:SkMathPriv_hdr",
|
||||
"//src/gpu:KeyBuilder_hdr",
|
||||
"//src/gpu/ganesh/effects:GrDisableColorXP_hdr",
|
||||
"//src/gpu/ganesh/glsl:GrGLSLFragmentShaderBuilder_hdr",
|
||||
"//src/gpu/ganesh/glsl:GrGLSLVarying_hdr",
|
||||
"//src/gpu/ganesh/glsl:GrGLSLVertexGeoBuilder_hdr",
|
||||
"//src/gpu/tessellate:FixedCountBufferUtils_hdr",
|
||||
"//src/gpu/tessellate:Tessellation_hdr",
|
||||
"//src/gpu/tessellate:WangsFormula_hdr",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -7,15 +7,10 @@
|
||||
|
||||
#include "src/gpu/ganesh/tessellate/shaders/GrPathTessellationShader.h"
|
||||
|
||||
#include "src/core/SkMathPriv.h"
|
||||
#include "src/gpu/KeyBuilder.h"
|
||||
#include "src/gpu/ganesh/effects/GrDisableColorXP.h"
|
||||
#include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
#include "src/gpu/ganesh/glsl/GrGLSLVarying.h"
|
||||
#include "src/gpu/ganesh/glsl/GrGLSLVertexGeoBuilder.h"
|
||||
#include "src/gpu/tessellate/FixedCountBufferUtils.h"
|
||||
#include "src/gpu/tessellate/Tessellation.h"
|
||||
#include "src/gpu/tessellate/WangsFormula.h"
|
||||
|
||||
using skgpu::PatchAttribs;
|
||||
|
||||
@ -33,6 +28,8 @@ public:
|
||||
this->setVertexAttributesWithImplicitOffsets(&kInputPointAttrib, 1);
|
||||
}
|
||||
|
||||
int maxTessellationSegments(const GrShaderCaps&) const override { SkUNREACHABLE; }
|
||||
|
||||
private:
|
||||
const char* name() const final { return "tessellate_SimpleTriangleShader"; }
|
||||
void addToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const final {}
|
||||
@ -57,207 +54,27 @@ std::unique_ptr<GrGeometryProcessor::ProgramImpl> SimpleTriangleShader::makeProg
|
||||
return std::make_unique<Impl>();
|
||||
}
|
||||
|
||||
|
||||
// Uses instanced draws to triangulate standalone closed curves with a "middle-out" topology.
|
||||
// Middle-out draws a triangle with vertices at T=[0, 1/2, 1] and then recurses breadth first:
|
||||
//
|
||||
// depth=0: T=[0, 1/2, 1]
|
||||
// depth=1: T=[0, 1/4, 2/4], T=[2/4, 3/4, 1]
|
||||
// depth=2: T=[0, 1/8, 2/8], T=[2/8, 3/8, 4/8], T=[4/8, 5/8, 6/8], T=[6/8, 7/8, 1]
|
||||
// ...
|
||||
//
|
||||
// The shader determines how many segments are required to render each individual curve smoothly,
|
||||
// and emits empty triangles at any vertices whose sk_VertexIDs are higher than necessary. It is the
|
||||
// caller's responsibility to draw enough vertices per instance for the most complex curve in the
|
||||
// batch to render smoothly (i.e., NumTrianglesAtResolveLevel() * 3).
|
||||
class MiddleOutShader : public GrPathTessellationShader {
|
||||
public:
|
||||
MiddleOutShader(const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f& color, PatchAttribs attribs)
|
||||
: GrPathTessellationShader(kTessellate_MiddleOutShader_ClassID,
|
||||
GrPrimitiveType::kTriangles, 0, viewMatrix, color, attribs) {
|
||||
fInstanceAttribs.emplace_back("p01", kFloat4_GrVertexAttribType, SkSLType::kFloat4);
|
||||
fInstanceAttribs.emplace_back("p23", kFloat4_GrVertexAttribType, SkSLType::kFloat4);
|
||||
if (fAttribs & PatchAttribs::kFanPoint) {
|
||||
fInstanceAttribs.emplace_back("fanPointAttrib",
|
||||
kFloat2_GrVertexAttribType,
|
||||
SkSLType::kFloat2);
|
||||
}
|
||||
if (fAttribs & PatchAttribs::kColor) {
|
||||
fInstanceAttribs.emplace_back("colorAttrib",
|
||||
(fAttribs & PatchAttribs::kWideColorIfEnabled)
|
||||
? kFloat4_GrVertexAttribType
|
||||
: kUByte4_norm_GrVertexAttribType,
|
||||
SkSLType::kHalf4);
|
||||
}
|
||||
if (fAttribs & PatchAttribs::kExplicitCurveType) {
|
||||
// A conic curve is written out with p3=[w,Infinity], but GPUs that don't support
|
||||
// infinity can't detect this. On these platforms we also write out an extra float with
|
||||
// each patch that explicitly tells the shader what type of curve it is.
|
||||
fInstanceAttribs.emplace_back("curveType", kFloat_GrVertexAttribType, SkSLType::kFloat);
|
||||
}
|
||||
this->setInstanceAttributesWithImplicitOffsets(fInstanceAttribs.data(),
|
||||
fInstanceAttribs.count());
|
||||
SkASSERT(fInstanceAttribs.count() <= kMaxInstanceAttribCount);
|
||||
SkASSERT(this->instanceStride() ==
|
||||
sizeof(SkPoint) * 4 + skgpu::PatchAttribsStride(fAttribs));
|
||||
|
||||
constexpr static Attribute kVertexAttrib("resolveLevel_and_idx", kFloat2_GrVertexAttribType,
|
||||
SkSLType::kFloat2);
|
||||
this->setVertexAttributesWithImplicitOffsets(&kVertexAttrib, 1);
|
||||
}
|
||||
|
||||
private:
|
||||
const char* name() const final { return "tessellate_MiddleOutShader"; }
|
||||
void addToKey(const GrShaderCaps&, skgpu::KeyBuilder* b) const final {
|
||||
// When color is in a uniform, it's always wide so we need to ignore kWideColorIfEnabled.
|
||||
// When color is in an attrib, its wideness is accounted for as part of the attrib key in
|
||||
// GrGeometryProcessor::getAttributeKey().
|
||||
// Either way, we get the correct key by ignoring .
|
||||
b->add32((uint32_t)(fAttribs & ~PatchAttribs::kWideColorIfEnabled));
|
||||
}
|
||||
std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
|
||||
|
||||
constexpr static int kMaxInstanceAttribCount = 5;
|
||||
SkSTArray<kMaxInstanceAttribCount, Attribute> fInstanceAttribs;
|
||||
};
|
||||
|
||||
std::unique_ptr<GrGeometryProcessor::ProgramImpl> MiddleOutShader::makeProgramImpl(
|
||||
const GrShaderCaps&) const {
|
||||
class Impl : public GrPathTessellationShader::Impl {
|
||||
void emitVertexCode(const GrShaderCaps& shaderCaps,
|
||||
const GrPathTessellationShader& shader,
|
||||
GrGLSLVertexBuilder* v,
|
||||
GrGLSLVaryingHandler* varyingHandler,
|
||||
GrGPArgs* gpArgs) override {
|
||||
const MiddleOutShader& middleOutShader = shader.cast<MiddleOutShader>();
|
||||
v->defineConstant("PRECISION", skgpu::kTessellationPrecision);
|
||||
v->defineConstant("MAX_FIXED_RESOLVE_LEVEL",
|
||||
(float)skgpu::kMaxFixedResolveLevel);
|
||||
v->defineConstant("MAX_FIXED_SEGMENTS",
|
||||
(float)(skgpu::kMaxParametricSegments));
|
||||
v->insertFunction(GrTessellationShader::WangsFormulaSkSL());
|
||||
if (middleOutShader.fAttribs & PatchAttribs::kExplicitCurveType) {
|
||||
v->insertFunction(SkStringPrintf(R"(
|
||||
bool is_conic_curve() {
|
||||
return curveType != %g;
|
||||
})", skgpu::kCubicCurveType).c_str());
|
||||
v->insertFunction(SkStringPrintf(R"(
|
||||
bool is_triangular_conic_curve() {
|
||||
return curveType == %g;
|
||||
})", skgpu::kTriangularConicCurveType).c_str());
|
||||
} else {
|
||||
SkASSERT(shaderCaps.infinitySupport());
|
||||
v->insertFunction(R"(
|
||||
bool is_conic_curve() { return isinf(p23.w); }
|
||||
bool is_triangular_conic_curve() { return isinf(p23.z); })");
|
||||
}
|
||||
if (shaderCaps.bitManipulationSupport()) {
|
||||
v->insertFunction(R"(
|
||||
float ldexp_portable(float x, float p) {
|
||||
return ldexp(x, int(p));
|
||||
})");
|
||||
} else {
|
||||
v->insertFunction(R"(
|
||||
float ldexp_portable(float x, float p) {
|
||||
return x * exp2(p);
|
||||
})");
|
||||
}
|
||||
v->codeAppend(R"(
|
||||
float resolveLevel = resolveLevel_and_idx.x;
|
||||
float idxInResolveLevel = resolveLevel_and_idx.y;
|
||||
float2 localcoord;)");
|
||||
if (middleOutShader.fAttribs & PatchAttribs::kFanPoint) {
|
||||
v->codeAppend(R"(
|
||||
// A negative resolve level means this is the fan point.
|
||||
if (resolveLevel < 0) {
|
||||
localcoord = fanPointAttrib;
|
||||
} else)"); // Fall through to next if ().
|
||||
}
|
||||
v->codeAppend(R"(
|
||||
if (is_triangular_conic_curve()) {
|
||||
// This patch is an exact triangle.
|
||||
localcoord = (resolveLevel != 0) ? p01.zw
|
||||
: (idxInResolveLevel != 0) ? p23.xy
|
||||
: p01.xy;
|
||||
} else {
|
||||
float2 p0=p01.xy, p1=p01.zw, p2=p23.xy, p3=p23.zw;
|
||||
float w = -1; // w < 0 tells us to treat the instance as an integral cubic.
|
||||
float maxResolveLevel;
|
||||
if (is_conic_curve()) {
|
||||
// Conics are 3 points, with the weight in p3.
|
||||
w = p3.x;
|
||||
maxResolveLevel = wangs_formula_conic_log2(PRECISION, AFFINE_MATRIX * p0,
|
||||
AFFINE_MATRIX * p1,
|
||||
AFFINE_MATRIX * p2, w);
|
||||
p1 *= w; // Unproject p1.
|
||||
p3 = p2; // Duplicate the endpoint for shared code that also runs on cubics.
|
||||
} else {
|
||||
// The patch is an integral cubic.
|
||||
maxResolveLevel = wangs_formula_cubic_log2(PRECISION, p0, p1, p2, p3,
|
||||
AFFINE_MATRIX);
|
||||
}
|
||||
if (resolveLevel > maxResolveLevel) {
|
||||
// This vertex is at a higher resolve level than we need. Demote to a lower
|
||||
// resolveLevel, which will produce a degenerate triangle.
|
||||
idxInResolveLevel = floor(ldexp_portable(idxInResolveLevel,
|
||||
maxResolveLevel - resolveLevel));
|
||||
resolveLevel = maxResolveLevel;
|
||||
}
|
||||
// Promote our location to a discrete position in the maximum fixed resolve level.
|
||||
// This is extra paranoia to ensure we get the exact same fp32 coordinates for
|
||||
// colocated points from different resolve levels (e.g., the vertices T=3/4 and
|
||||
// T=6/8 should be exactly colocated).
|
||||
float fixedVertexID = floor(.5 + ldexp_portable(
|
||||
idxInResolveLevel, MAX_FIXED_RESOLVE_LEVEL - resolveLevel));
|
||||
if (0 < fixedVertexID && fixedVertexID < MAX_FIXED_SEGMENTS) {
|
||||
float T = fixedVertexID * (1 / MAX_FIXED_SEGMENTS);
|
||||
|
||||
// Evaluate at T. Use De Casteljau's for its accuracy and stability.
|
||||
float2 ab = mix(p0, p1, T);
|
||||
float2 bc = mix(p1, p2, T);
|
||||
float2 cd = mix(p2, p3, T);
|
||||
float2 abc = mix(ab, bc, T);
|
||||
float2 bcd = mix(bc, cd, T);
|
||||
float2 abcd = mix(abc, bcd, T);
|
||||
|
||||
// Evaluate the conic weight at T.
|
||||
float u = mix(1.0, w, T);
|
||||
float v = w + 1 - u; // == mix(w, 1, T)
|
||||
float uv = mix(u, v, T);
|
||||
|
||||
localcoord = (w < 0) ? /*cubic*/ abcd : /*conic*/ abc/uv;
|
||||
} else {
|
||||
localcoord = (fixedVertexID == 0) ? p0.xy : p3.xy;
|
||||
}
|
||||
}
|
||||
float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
|
||||
gpArgs->fLocalCoordVar.set(SkSLType::kFloat2, "localcoord");
|
||||
gpArgs->fPositionVar.set(SkSLType::kFloat2, "vertexpos");
|
||||
if (middleOutShader.fAttribs & PatchAttribs::kColor) {
|
||||
GrGLSLVarying colorVarying(SkSLType::kHalf4);
|
||||
varyingHandler->addVarying("color",
|
||||
&colorVarying,
|
||||
GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
|
||||
v->codeAppendf("%s = colorAttrib;", colorVarying.vsOut());
|
||||
fVaryingColorName = colorVarying.fsIn();
|
||||
}
|
||||
}
|
||||
};
|
||||
return std::make_unique<Impl>();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
GrPathTessellationShader* GrPathTessellationShader::Make(const GrShaderCaps& shaderCaps,
|
||||
SkArenaAlloc* arena,
|
||||
GrPathTessellationShader* GrPathTessellationShader::Make(SkArenaAlloc* arena,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f& color,
|
||||
skgpu::PatchAttribs attribs) {
|
||||
// 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(shaderCaps.infinitySupport() != (attribs & PatchAttribs::kExplicitCurveType));
|
||||
return arena->make<MiddleOutShader>(shaderCaps, viewMatrix, color, attribs);
|
||||
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(
|
||||
|
@ -19,6 +19,16 @@ public:
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f&);
|
||||
|
||||
// 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:
|
||||
//
|
||||
@ -34,11 +44,20 @@ public:
|
||||
//
|
||||
// If PatchAttribs::kFanPoint is set, an additional triangle is added, connecting the base of
|
||||
// the curve to the fan point.
|
||||
static GrPathTessellationShader* Make(const GrShaderCaps&,
|
||||
SkArenaAlloc*,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f&,
|
||||
skgpu::PatchAttribs);
|
||||
static GrPathTessellationShader* MakeMiddleOutFixedCountShader(const GrShaderCaps&,
|
||||
SkArenaAlloc*,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f&,
|
||||
skgpu::PatchAttribs);
|
||||
|
||||
// Uses GPU tessellation shaders to linearize, triangulate, and render curves.
|
||||
//
|
||||
// If PatchAttribs::kFanPoint is set, an additional triangle is added, connecting the base of
|
||||
// the curve to the fan point.
|
||||
static GrPathTessellationShader* MakeHardwareTessellationShader(SkArenaAlloc*,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f&,
|
||||
skgpu::PatchAttribs);
|
||||
|
||||
// Returns the stencil settings to use for a standard Redbook "stencil" pass.
|
||||
static const GrUserStencilSettings* StencilPathSettings(GrFillRule fillRule) {
|
||||
@ -101,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);
|
||||
|
||||
|
@ -0,0 +1,354 @@
|
||||
/*
|
||||
* Copyright 2019 Google LLC.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "src/gpu/ganesh/tessellate/shaders/GrPathTessellationShader.h"
|
||||
|
||||
#include "src/gpu/ganesh/glsl/GrGLSLVertexGeoBuilder.h"
|
||||
#include "src/gpu/tessellate/Tessellation.h"
|
||||
#include "src/gpu/tessellate/WangsFormula.h"
|
||||
|
||||
using skgpu::PatchAttribs;
|
||||
|
||||
namespace {
|
||||
|
||||
// Converts keywords from shared SkSL strings to native GLSL keywords.
|
||||
constexpr static char kSkSLTypeDefs[] = R"(
|
||||
#define float4x3 mat4x3
|
||||
#define float4x2 mat4x2
|
||||
#define float3x2 mat3x2
|
||||
#define float2x2 mat2
|
||||
#define float2 vec2
|
||||
#define float3 vec3
|
||||
#define float4 vec4
|
||||
)";
|
||||
|
||||
// Uses GPU tessellation shaders to linearize, triangulate, and render cubic "wedge" patches. A
|
||||
// wedge is a 5-point patch consisting of 4 cubic control points, plus an anchor point fanning from
|
||||
// the center of the curve's resident contour.
|
||||
// TODO: Eventually we want to use rational cubic wedges in order to support perspective and conics.
|
||||
class HardwareWedgeShader : public GrPathTessellationShader {
|
||||
public:
|
||||
HardwareWedgeShader(const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f& color,
|
||||
PatchAttribs attribs)
|
||||
: GrPathTessellationShader(kTessellate_HardwareWedgeShader_ClassID,
|
||||
GrPrimitiveType::kPatches, 5, viewMatrix, color, attribs) {
|
||||
constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
|
||||
SkSLType::kFloat2};
|
||||
this->setVertexAttributesWithImplicitOffsets(&kInputPointAttrib, 1);
|
||||
SkASSERT(this->vertexStride() * 5 ==
|
||||
sizeof(SkPoint) * 4 + skgpu::PatchAttribsStride(fAttribs));
|
||||
}
|
||||
|
||||
int maxTessellationSegments(const GrShaderCaps& shaderCaps) const override {
|
||||
return shaderCaps.maxTessellationSegments();
|
||||
}
|
||||
|
||||
private:
|
||||
const char* name() const final { return "tessellate_HardwareWedgeShader"; }
|
||||
void addToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const final {}
|
||||
std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
|
||||
};
|
||||
|
||||
std::unique_ptr<GrGeometryProcessor::ProgramImpl> HardwareWedgeShader::makeProgramImpl(
|
||||
const GrShaderCaps&) const {
|
||||
class Impl : public GrPathTessellationShader::Impl {
|
||||
void emitVertexCode(const GrShaderCaps&,
|
||||
const GrPathTessellationShader&,
|
||||
GrGLSLVertexBuilder* v,
|
||||
GrGLSLVaryingHandler*,
|
||||
GrGPArgs*) override {
|
||||
v->declareGlobal(GrShaderVar("vsPt", SkSLType::kFloat2, GrShaderVar::TypeModifier::Out));
|
||||
v->codeAppend(R"(
|
||||
// If y is infinity then x is a conic weight. Don't transform.
|
||||
vsPt = (isinf(inputPoint.y)) ? inputPoint : AFFINE_MATRIX * inputPoint + TRANSLATE;)");
|
||||
}
|
||||
SkString getTessControlShaderGLSL(const GrGeometryProcessor&,
|
||||
const char* versionAndExtensionDecls,
|
||||
const GrGLSLUniformHandler&,
|
||||
const GrShaderCaps& shaderCaps) const override {
|
||||
SkString code(versionAndExtensionDecls);
|
||||
code.appendf(R"(
|
||||
#define MAX_TESSELLATION_SEGMENTS %i)", shaderCaps.maxTessellationSegments());
|
||||
code.appendf(R"(
|
||||
#define PRECISION %f)", skgpu::kTessellationPrecision);
|
||||
code.append(kSkSLTypeDefs);
|
||||
code.append(GrTessellationShader::WangsFormulaSkSL());
|
||||
|
||||
code.append(R"(
|
||||
layout(vertices = 1) out;
|
||||
|
||||
in vec2 vsPt[];
|
||||
patch out mat4x2 rationalCubicXY;
|
||||
patch out float rationalCubicW;
|
||||
patch out vec2 fanpoint;
|
||||
|
||||
void main() {
|
||||
mat4x2 P = mat4x2(vsPt[0], vsPt[1], vsPt[2], vsPt[3]);
|
||||
float numSegments;
|
||||
if (isinf(P[3].y)) {
|
||||
// This is a conic.
|
||||
float w = P[3].x;
|
||||
numSegments = wangs_formula_conic(PRECISION, P[0], P[1], P[2], w);
|
||||
// Convert to a rational cubic in projected form.
|
||||
rationalCubicXY = mat4x2(P[0],
|
||||
mix(vec4(P[0], P[2]), (P[1] * w).xyxy, 2.0/3.0),
|
||||
P[2]);
|
||||
rationalCubicW = fma(w, 2.0/3.0, 1.0/3.0);
|
||||
} else {
|
||||
// This is a cubic.
|
||||
numSegments = wangs_formula_cubic(PRECISION, P[0], P[1], P[2], P[3], mat2(1));
|
||||
rationalCubicXY = P;
|
||||
rationalCubicW = 1;
|
||||
}
|
||||
fanpoint = vsPt[4];
|
||||
|
||||
// Tessellate the first side of the patch into numSegments triangles.
|
||||
gl_TessLevelOuter[0] = min(numSegments, MAX_TESSELLATION_SEGMENTS);
|
||||
|
||||
// Leave the other two sides of the patch as single segments.
|
||||
gl_TessLevelOuter[1] = 1.0;
|
||||
gl_TessLevelOuter[2] = 1.0;
|
||||
|
||||
// Changing the inner level to 1 when numSegments == 1 collapses the entire
|
||||
// patch to a single triangle. Otherwise, we need an inner level of 2 so our curve
|
||||
// triangles have an interior point to originate from.
|
||||
gl_TessLevelInner[0] = min(numSegments, 2.0);
|
||||
})");
|
||||
|
||||
return code;
|
||||
}
|
||||
SkString getTessEvaluationShaderGLSL(const GrGeometryProcessor&,
|
||||
const char* versionAndExtensionDecls,
|
||||
const GrGLSLUniformHandler&,
|
||||
const GrShaderCaps&) const override {
|
||||
SkString code(versionAndExtensionDecls);
|
||||
code.append(kSkSLTypeDefs);
|
||||
code.append(kEvalRationalCubicFn);
|
||||
code.append(R"(
|
||||
layout(triangles, equal_spacing, ccw) in;
|
||||
|
||||
uniform vec4 sk_RTAdjust;
|
||||
|
||||
patch in mat4x2 rationalCubicXY;
|
||||
patch in float rationalCubicW;
|
||||
patch in vec2 fanpoint;
|
||||
|
||||
void main() {
|
||||
// Locate our parametric point of interest. It is equal to the barycentric
|
||||
// y-coordinate if we are a vertex on the tessellated edge of the triangle patch,
|
||||
// 0.5 if we are the patch's interior vertex, or N/A if we are the fan point.
|
||||
// NOTE: We are on the tessellated edge when the barycentric x-coordinate == 0.
|
||||
float T = (gl_TessCoord.x == 0.0) ? gl_TessCoord.y : 0.5;
|
||||
|
||||
mat4x3 P = mat4x3(rationalCubicXY[0], 1,
|
||||
rationalCubicXY[1], rationalCubicW,
|
||||
rationalCubicXY[2], rationalCubicW,
|
||||
rationalCubicXY[3], 1);
|
||||
vec2 vertexpos = eval_rational_cubic(P, T);
|
||||
|
||||
if (gl_TessCoord.x == 1.0) {
|
||||
// We are the anchor point that fans from the center of the curve's contour.
|
||||
vertexpos = fanpoint;
|
||||
} else if (gl_TessCoord.x != 0.0) {
|
||||
// We are the interior point of the patch; center it inside [C(0), C(.5), C(1)].
|
||||
vertexpos = (P[0].xy + vertexpos + P[3].xy) / 3.0;
|
||||
}
|
||||
|
||||
gl_Position = vec4(vertexpos * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
|
||||
})");
|
||||
|
||||
return code;
|
||||
}
|
||||
};
|
||||
return std::make_unique<Impl>();
|
||||
}
|
||||
|
||||
// Uses GPU tessellation shaders to linearize, triangulate, and render standalone closed cubics.
|
||||
// TODO: Eventually we want to use rational cubic wedges in order to support perspective and conics.
|
||||
class HardwareCurveShader : public GrPathTessellationShader {
|
||||
public:
|
||||
HardwareCurveShader(const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f& color,
|
||||
PatchAttribs attribs)
|
||||
: GrPathTessellationShader(kTessellate_HardwareCurveShader_ClassID,
|
||||
GrPrimitiveType::kPatches, 4, viewMatrix, color,
|
||||
attribs) {
|
||||
constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
|
||||
SkSLType::kFloat2};
|
||||
this->setVertexAttributesWithImplicitOffsets(&kInputPointAttrib, 1);
|
||||
SkASSERT(this->vertexStride() * 4 ==
|
||||
sizeof(SkPoint) * 4 + skgpu::PatchAttribsStride(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:
|
||||
const char* name() const final { return "tessellate_HardwareCurveShader"; }
|
||||
void addToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const final {}
|
||||
std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
|
||||
};
|
||||
|
||||
std::unique_ptr<GrGeometryProcessor::ProgramImpl> HardwareCurveShader::makeProgramImpl(
|
||||
const GrShaderCaps&) const {
|
||||
class Impl : public GrPathTessellationShader::Impl {
|
||||
void emitVertexCode(const GrShaderCaps&,
|
||||
const GrPathTessellationShader&,
|
||||
GrGLSLVertexBuilder* v,
|
||||
GrGLSLVaryingHandler*,
|
||||
GrGPArgs*) override {
|
||||
v->declareGlobal(GrShaderVar("P", SkSLType::kFloat2, GrShaderVar::TypeModifier::Out));
|
||||
v->codeAppend(R"(
|
||||
// If y is infinity then x is a conic weight. Don't transform.
|
||||
P = (isinf(inputPoint.y)) ? inputPoint : AFFINE_MATRIX * inputPoint + TRANSLATE;)");
|
||||
}
|
||||
SkString getTessControlShaderGLSL(const GrGeometryProcessor&,
|
||||
const char* versionAndExtensionDecls,
|
||||
const GrGLSLUniformHandler&,
|
||||
const GrShaderCaps& shaderCaps) const override {
|
||||
SkString code(versionAndExtensionDecls);
|
||||
code.appendf(R"(
|
||||
#define MAX_TESSELLATION_SEGMENTS %i)", shaderCaps.maxTessellationSegments());
|
||||
code.appendf(R"(
|
||||
#define PRECISION %f)", skgpu::kTessellationPrecision);
|
||||
code.append(kSkSLTypeDefs);
|
||||
code.append(GrTessellationShader::WangsFormulaSkSL());
|
||||
code.append(R"(
|
||||
layout(vertices = 1) out;
|
||||
|
||||
in vec2 P[];
|
||||
patch out mat4x2 rationalCubicXY;
|
||||
patch out float rationalCubicW;
|
||||
|
||||
void main() {
|
||||
float w = -1; // w<0 means a cubic.
|
||||
vec2 p1w = P[1];
|
||||
if (isinf(P[3].y)) {
|
||||
// This patch is actually a conic. Project to homogeneous space.
|
||||
w = P[3].x;
|
||||
p1w *= w;
|
||||
}
|
||||
|
||||
// Chop the curve at T=1/2.
|
||||
vec2 ab = (P[0] + p1w) * .5;
|
||||
vec2 bc = (p1w + P[2]) * .5;
|
||||
vec2 cd = (P[2] + P[3]) * .5;
|
||||
vec2 abc = (ab + bc) * .5;
|
||||
vec2 bcd = (bc + cd) * .5;
|
||||
vec2 abcd = (abc + bcd) * .5;
|
||||
|
||||
float n0, n1;
|
||||
if (w < 0 || isinf(w)) {
|
||||
if (w < 0) {
|
||||
// The patch is a cubic. Calculate how many segments are required to
|
||||
// linearize each half of the curve.
|
||||
n0 = wangs_formula_cubic(PRECISION, P[0], ab, abc, abcd, mat2(1));
|
||||
n1 = wangs_formula_cubic(PRECISION, abcd, bcd, cd, P[3], mat2(1));
|
||||
rationalCubicW = 1;
|
||||
} else {
|
||||
// The patch is a triangle (a conic with infinite weight).
|
||||
n0 = n1 = 1;
|
||||
rationalCubicW = -1; // In the next stage, rationalCubicW<0 means triangle.
|
||||
}
|
||||
rationalCubicXY = mat4x2(P[0], P[1], P[2], P[3]);
|
||||
} else {
|
||||
// The patch is a conic. Unproject p0..5. w1 == w2 == w3 when chopping at .5.
|
||||
// (See SkConic::chopAt().)
|
||||
float r = 2.0 / (1.0 + w);
|
||||
ab *= r, bc *= r, abc *= r;
|
||||
// Put in "standard form" where w0 == w2 == w4 == 1.
|
||||
float w_ = inversesqrt(r); // Both halves have the same w' when chopping at .5.
|
||||
// Calculate how many segments are needed to linearize each half of the curve.
|
||||
n0 = wangs_formula_conic(PRECISION, P[0], ab, abc, w_);
|
||||
n1 = wangs_formula_conic(PRECISION, abc, bc, P[2], w_);
|
||||
// Covert the conic to a rational cubic in projected form.
|
||||
rationalCubicXY = mat4x2(P[0],
|
||||
mix(float4(P[0],P[2]), p1w.xyxy, 2.0/3.0),
|
||||
P[2]);
|
||||
rationalCubicW = fma(w, 2.0/3.0, 1.0/3.0);
|
||||
}
|
||||
|
||||
gl_TessLevelOuter[0] = min(n1, MAX_TESSELLATION_SEGMENTS);
|
||||
gl_TessLevelOuter[1] = 1.0;
|
||||
gl_TessLevelOuter[2] = min(n0, MAX_TESSELLATION_SEGMENTS);
|
||||
|
||||
// Changing the inner level to 1 when n0 == n1 == 1 collapses the entire patch to a
|
||||
// single triangle. Otherwise, we need an inner level of 2 so our curve triangles
|
||||
// have an interior point to originate from.
|
||||
gl_TessLevelInner[0] = min(max(n0, n1), 2.0);
|
||||
})");
|
||||
|
||||
return code;
|
||||
}
|
||||
SkString getTessEvaluationShaderGLSL(const GrGeometryProcessor&,
|
||||
const char* versionAndExtensionDecls,
|
||||
const GrGLSLUniformHandler&,
|
||||
const GrShaderCaps&) const override {
|
||||
SkString code(versionAndExtensionDecls);
|
||||
code.append(kSkSLTypeDefs);
|
||||
code.append(kEvalRationalCubicFn);
|
||||
code.append(R"(
|
||||
layout(triangles, equal_spacing, ccw) in;
|
||||
|
||||
uniform vec4 sk_RTAdjust;
|
||||
|
||||
patch in mat4x2 rationalCubicXY;
|
||||
patch in float rationalCubicW;
|
||||
|
||||
void main() {
|
||||
vec2 vertexpos;
|
||||
if (rationalCubicW < 0) { // rationalCubicW < 0 means a triangle now.
|
||||
vertexpos = (gl_TessCoord.x != 0) ? rationalCubicXY[0]
|
||||
: (gl_TessCoord.y != 0) ? rationalCubicXY[1]
|
||||
: rationalCubicXY[2];
|
||||
} else {
|
||||
// Locate our parametric point of interest. T ramps from [0..1/2] on the left
|
||||
// edge of the triangle, and [1/2..1] on the right. If we are the patch's
|
||||
// interior vertex, then we want T=1/2. Since the barycentric coords are
|
||||
// (1/3, 1/3, 1/3) at the interior vertex, the below fma() works in all 3
|
||||
// scenarios.
|
||||
float T = fma(.5, gl_TessCoord.y, gl_TessCoord.z);
|
||||
|
||||
mat4x3 P = mat4x3(rationalCubicXY[0], 1,
|
||||
rationalCubicXY[1], rationalCubicW,
|
||||
rationalCubicXY[2], rationalCubicW,
|
||||
rationalCubicXY[3], 1);
|
||||
vertexpos = eval_rational_cubic(P, T);
|
||||
if (all(notEqual(gl_TessCoord.xz, vec2(0)))) {
|
||||
// We are the interior point of the patch; center it inside
|
||||
// [C(0), C(.5), C(1)].
|
||||
vertexpos = (P[0].xy + vertexpos + P[3].xy) / 3.0;
|
||||
}
|
||||
}
|
||||
|
||||
gl_Position = vec4(vertexpos * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
|
||||
})");
|
||||
|
||||
return code;
|
||||
}
|
||||
};
|
||||
return std::make_unique<Impl>();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
GrPathTessellationShader* GrPathTessellationShader::MakeHardwareTessellationShader(
|
||||
SkArenaAlloc* arena, const SkMatrix& viewMatrix, const SkPMColor4f& color,
|
||||
PatchAttribs attribs) {
|
||||
SkASSERT(!(attribs & PatchAttribs::kColor)); // Not yet implemented.
|
||||
SkASSERT(!(attribs & PatchAttribs::kExplicitCurveType)); // Not yet implemented.
|
||||
if (attribs & PatchAttribs::kFanPoint) {
|
||||
return arena->make<HardwareWedgeShader>(viewMatrix, color, attribs);
|
||||
} else {
|
||||
return arena->make<HardwareCurveShader>(viewMatrix, color, attribs);
|
||||
}
|
||||
}
|
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2019 Google LLC.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "src/gpu/ganesh/tessellate/shaders/GrPathTessellationShader.h"
|
||||
|
||||
#include "src/core/SkMathPriv.h"
|
||||
#include "src/gpu/KeyBuilder.h"
|
||||
#include "src/gpu/ganesh/glsl/GrGLSLVertexGeoBuilder.h"
|
||||
#include "src/gpu/tessellate/FixedCountBufferUtils.h"
|
||||
#include "src/gpu/tessellate/Tessellation.h"
|
||||
#include "src/gpu/tessellate/WangsFormula.h"
|
||||
|
||||
using skgpu::PatchAttribs;
|
||||
using skgpu::VertexWriter;
|
||||
|
||||
namespace {
|
||||
|
||||
// Uses instanced draws to triangulate standalone closed curves with a "middle-out" topology.
|
||||
// Middle-out draws a triangle with vertices at T=[0, 1/2, 1] and then recurses breadth first:
|
||||
//
|
||||
// depth=0: T=[0, 1/2, 1]
|
||||
// depth=1: T=[0, 1/4, 2/4], T=[2/4, 3/4, 1]
|
||||
// depth=2: T=[0, 1/8, 2/8], T=[2/8, 3/8, 4/8], T=[4/8, 5/8, 6/8], T=[6/8, 7/8, 1]
|
||||
// ...
|
||||
//
|
||||
// The shader determines how many segments are required to render each individual curve smoothly,
|
||||
// and emits empty triangles at any vertices whose sk_VertexIDs are higher than necessary. It is the
|
||||
// caller's responsibility to draw enough vertices per instance for the most complex curve in the
|
||||
// batch to render smoothly (i.e., NumTrianglesAtResolveLevel() * 3).
|
||||
class MiddleOutShader : public GrPathTessellationShader {
|
||||
public:
|
||||
MiddleOutShader(const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f& color, PatchAttribs attribs)
|
||||
: GrPathTessellationShader(kTessellate_MiddleOutShader_ClassID,
|
||||
GrPrimitiveType::kTriangles, 0, viewMatrix, color, attribs) {
|
||||
fInstanceAttribs.emplace_back("p01", kFloat4_GrVertexAttribType, SkSLType::kFloat4);
|
||||
fInstanceAttribs.emplace_back("p23", kFloat4_GrVertexAttribType, SkSLType::kFloat4);
|
||||
if (fAttribs & PatchAttribs::kFanPoint) {
|
||||
fInstanceAttribs.emplace_back("fanPointAttrib",
|
||||
kFloat2_GrVertexAttribType,
|
||||
SkSLType::kFloat2);
|
||||
}
|
||||
if (fAttribs & PatchAttribs::kColor) {
|
||||
fInstanceAttribs.emplace_back("colorAttrib",
|
||||
(fAttribs & PatchAttribs::kWideColorIfEnabled)
|
||||
? kFloat4_GrVertexAttribType
|
||||
: kUByte4_norm_GrVertexAttribType,
|
||||
SkSLType::kHalf4);
|
||||
}
|
||||
if (fAttribs & PatchAttribs::kExplicitCurveType) {
|
||||
// A conic curve is written out with p3=[w,Infinity], but GPUs that don't support
|
||||
// infinity can't detect this. On these platforms we also write out an extra float with
|
||||
// each patch that explicitly tells the shader what type of curve it is.
|
||||
fInstanceAttribs.emplace_back("curveType", kFloat_GrVertexAttribType, SkSLType::kFloat);
|
||||
}
|
||||
this->setInstanceAttributesWithImplicitOffsets(fInstanceAttribs.data(),
|
||||
fInstanceAttribs.count());
|
||||
SkASSERT(fInstanceAttribs.count() <= kMaxInstanceAttribCount);
|
||||
SkASSERT(this->instanceStride() ==
|
||||
sizeof(SkPoint) * 4 + skgpu::PatchAttribsStride(fAttribs));
|
||||
|
||||
constexpr static Attribute kVertexAttrib("resolveLevel_and_idx", kFloat2_GrVertexAttribType,
|
||||
SkSLType::kFloat2);
|
||||
this->setVertexAttributesWithImplicitOffsets(&kVertexAttrib, 1);
|
||||
}
|
||||
|
||||
int maxTessellationSegments(const GrShaderCaps&) const override {
|
||||
return skgpu::kMaxParametricSegments;
|
||||
}
|
||||
|
||||
private:
|
||||
const char* name() const final { return "tessellate_MiddleOutShader"; }
|
||||
void addToKey(const GrShaderCaps&, skgpu::KeyBuilder* b) const final {
|
||||
// When color is in a uniform, it's always wide so we need to ignore kWideColorIfEnabled.
|
||||
// When color is in an attrib, its wideness is accounted for as part of the attrib key in
|
||||
// GrGeometryProcessor::getAttributeKey().
|
||||
// Either way, we get the correct key by ignoring .
|
||||
b->add32((uint32_t)(fAttribs & ~PatchAttribs::kWideColorIfEnabled));
|
||||
}
|
||||
std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
|
||||
|
||||
constexpr static int kMaxInstanceAttribCount = 5;
|
||||
SkSTArray<kMaxInstanceAttribCount, Attribute> fInstanceAttribs;
|
||||
};
|
||||
|
||||
std::unique_ptr<GrGeometryProcessor::ProgramImpl> MiddleOutShader::makeProgramImpl(
|
||||
const GrShaderCaps&) const {
|
||||
class Impl : public GrPathTessellationShader::Impl {
|
||||
void emitVertexCode(const GrShaderCaps& shaderCaps,
|
||||
const GrPathTessellationShader& shader,
|
||||
GrGLSLVertexBuilder* v,
|
||||
GrGLSLVaryingHandler* varyingHandler,
|
||||
GrGPArgs* gpArgs) override {
|
||||
const MiddleOutShader& middleOutShader = shader.cast<MiddleOutShader>();
|
||||
v->defineConstant("PRECISION", skgpu::kTessellationPrecision);
|
||||
v->defineConstant("MAX_FIXED_RESOLVE_LEVEL",
|
||||
(float)skgpu::kMaxFixedResolveLevel);
|
||||
v->defineConstant("MAX_FIXED_SEGMENTS",
|
||||
(float)(skgpu::kMaxParametricSegments));
|
||||
v->insertFunction(GrTessellationShader::WangsFormulaSkSL());
|
||||
if (middleOutShader.fAttribs & PatchAttribs::kExplicitCurveType) {
|
||||
v->insertFunction(SkStringPrintf(R"(
|
||||
bool is_conic_curve() {
|
||||
return curveType != %g;
|
||||
})", skgpu::kCubicCurveType).c_str());
|
||||
v->insertFunction(SkStringPrintf(R"(
|
||||
bool is_triangular_conic_curve() {
|
||||
return curveType == %g;
|
||||
})", skgpu::kTriangularConicCurveType).c_str());
|
||||
} else {
|
||||
SkASSERT(shaderCaps.infinitySupport());
|
||||
v->insertFunction(R"(
|
||||
bool is_conic_curve() { return isinf(p23.w); }
|
||||
bool is_triangular_conic_curve() { return isinf(p23.z); })");
|
||||
}
|
||||
if (shaderCaps.bitManipulationSupport()) {
|
||||
v->insertFunction(R"(
|
||||
float ldexp_portable(float x, float p) {
|
||||
return ldexp(x, int(p));
|
||||
})");
|
||||
} else {
|
||||
v->insertFunction(R"(
|
||||
float ldexp_portable(float x, float p) {
|
||||
return x * exp2(p);
|
||||
})");
|
||||
}
|
||||
v->codeAppend(R"(
|
||||
float resolveLevel = resolveLevel_and_idx.x;
|
||||
float idxInResolveLevel = resolveLevel_and_idx.y;
|
||||
float2 localcoord;)");
|
||||
if (middleOutShader.fAttribs & PatchAttribs::kFanPoint) {
|
||||
v->codeAppend(R"(
|
||||
// A negative resolve level means this is the fan point.
|
||||
if (resolveLevel < 0) {
|
||||
localcoord = fanPointAttrib;
|
||||
} else)"); // Fall through to next if ().
|
||||
}
|
||||
v->codeAppend(R"(
|
||||
if (is_triangular_conic_curve()) {
|
||||
// This patch is an exact triangle.
|
||||
localcoord = (resolveLevel != 0) ? p01.zw
|
||||
: (idxInResolveLevel != 0) ? p23.xy
|
||||
: p01.xy;
|
||||
} else {
|
||||
float2 p0=p01.xy, p1=p01.zw, p2=p23.xy, p3=p23.zw;
|
||||
float w = -1; // w < 0 tells us to treat the instance as an integral cubic.
|
||||
float maxResolveLevel;
|
||||
if (is_conic_curve()) {
|
||||
// Conics are 3 points, with the weight in p3.
|
||||
w = p3.x;
|
||||
maxResolveLevel = wangs_formula_conic_log2(PRECISION, AFFINE_MATRIX * p0,
|
||||
AFFINE_MATRIX * p1,
|
||||
AFFINE_MATRIX * p2, w);
|
||||
p1 *= w; // Unproject p1.
|
||||
p3 = p2; // Duplicate the endpoint for shared code that also runs on cubics.
|
||||
} else {
|
||||
// The patch is an integral cubic.
|
||||
maxResolveLevel = wangs_formula_cubic_log2(PRECISION, p0, p1, p2, p3,
|
||||
AFFINE_MATRIX);
|
||||
}
|
||||
if (resolveLevel > maxResolveLevel) {
|
||||
// This vertex is at a higher resolve level than we need. Demote to a lower
|
||||
// resolveLevel, which will produce a degenerate triangle.
|
||||
idxInResolveLevel = floor(ldexp_portable(idxInResolveLevel,
|
||||
maxResolveLevel - resolveLevel));
|
||||
resolveLevel = maxResolveLevel;
|
||||
}
|
||||
// Promote our location to a discrete position in the maximum fixed resolve level.
|
||||
// This is extra paranoia to ensure we get the exact same fp32 coordinates for
|
||||
// colocated points from different resolve levels (e.g., the vertices T=3/4 and
|
||||
// T=6/8 should be exactly colocated).
|
||||
float fixedVertexID = floor(.5 + ldexp_portable(
|
||||
idxInResolveLevel, MAX_FIXED_RESOLVE_LEVEL - resolveLevel));
|
||||
if (0 < fixedVertexID && fixedVertexID < MAX_FIXED_SEGMENTS) {
|
||||
float T = fixedVertexID * (1 / MAX_FIXED_SEGMENTS);
|
||||
|
||||
// Evaluate at T. Use De Casteljau's for its accuracy and stability.
|
||||
float2 ab = mix(p0, p1, T);
|
||||
float2 bc = mix(p1, p2, T);
|
||||
float2 cd = mix(p2, p3, T);
|
||||
float2 abc = mix(ab, bc, T);
|
||||
float2 bcd = mix(bc, cd, T);
|
||||
float2 abcd = mix(abc, bcd, T);
|
||||
|
||||
// Evaluate the conic weight at T.
|
||||
float u = mix(1.0, w, T);
|
||||
float v = w + 1 - u; // == mix(w, 1, T)
|
||||
float uv = mix(u, v, T);
|
||||
|
||||
localcoord = (w < 0) ? /*cubic*/ abcd : /*conic*/ abc/uv;
|
||||
} else {
|
||||
localcoord = (fixedVertexID == 0) ? p0.xy : p3.xy;
|
||||
}
|
||||
}
|
||||
float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
|
||||
gpArgs->fLocalCoordVar.set(SkSLType::kFloat2, "localcoord");
|
||||
gpArgs->fPositionVar.set(SkSLType::kFloat2, "vertexpos");
|
||||
if (middleOutShader.fAttribs & PatchAttribs::kColor) {
|
||||
GrGLSLVarying colorVarying(SkSLType::kHalf4);
|
||||
varyingHandler->addVarying("color",
|
||||
&colorVarying,
|
||||
GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
|
||||
v->codeAppendf("%s = colorAttrib;", colorVarying.vsOut());
|
||||
fVaryingColorName = colorVarying.fsIn();
|
||||
}
|
||||
}
|
||||
};
|
||||
return std::make_unique<Impl>();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
GrPathTessellationShader* GrPathTessellationShader::MakeMiddleOutFixedCountShader(
|
||||
const GrShaderCaps& shaderCaps,
|
||||
SkArenaAlloc* arena,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkPMColor4f& color,
|
||||
PatchAttribs attribs) {
|
||||
// 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(shaderCaps.infinitySupport() != (attribs & PatchAttribs::kExplicitCurveType));
|
||||
return arena->make<MiddleOutShader>(shaderCaps, viewMatrix, color, attribs);
|
||||
}
|
Loading…
Reference in New Issue
Block a user