Reland "Implement batching for convex tessellated paths"

This is a reland of 9613060bdf

Original change's description:
> Implement batching for convex tessellated paths
>
> Moves the view matrix transformation onto the CPU (when local coords
> are not used), and plumbs a color attrib through the tessellation
> patches.
>
> Bug: skia:12524
> Change-Id: Ic4740048b4fc85cb18e7235a7754d57762db3daf
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/468997
> Reviewed-by: Michael Ludwig <michaelludwig@google.com>
> Commit-Queue: Chris Dalton <csmartdalton@google.com>

Bug: skia:12524
Change-Id: I77cd079d8921b224925bd2f7364c564822886d61
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/470436
Commit-Queue: Robert Phillips <robertphillips@google.com>
Auto-Submit: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Chris Dalton 2021-11-11 11:32:12 -07:00 committed by SkCQ
parent 05c41162fb
commit 08a971126e
22 changed files with 381 additions and 150 deletions

View File

@ -149,7 +149,8 @@ DEF_PATH_TESS_BENCH(GrPathCurveTessellator, make_cubic_path(8), SkMatrix::I()) {
fTarget->caps().minPathVerbsForHwTessellation(),
noVaryingsPipeline,
fTarget->caps());
tess->prepare(fTarget.get(), {gAlmostIdentity, fPath}, fPath.countVerbs());
tess->prepare(fTarget.get(), {gAlmostIdentity, fPath, SK_PMColor4fTRANSPARENT},
fPath.countVerbs());
}
DEF_PATH_TESS_BENCH(GrPathWedgeTessellator, make_cubic_path(8), SkMatrix::I()) {
@ -162,7 +163,8 @@ DEF_PATH_TESS_BENCH(GrPathWedgeTessellator, make_cubic_path(8), SkMatrix::I()) {
fTarget->caps().minPathVerbsForHwTessellation(),
noVaryingsPipeline,
fTarget->caps());
tess->prepare(fTarget.get(), {gAlmostIdentity, fPath}, fPath.countVerbs());
tess->prepare(fTarget.get(), {gAlmostIdentity, fPath, SK_PMColor4fTRANSPARENT},
fPath.countVerbs());
}
static void benchmark_wangs_formula_cubic_log2(const SkMatrix& matrix, const SkPath& path) {

59
gm/batchedconvexpaths.cpp Normal file
View File

@ -0,0 +1,59 @@
/*
* Copyright 2021 Google LLC.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/gpu/GrContextOptions.h"
namespace skiagm {
class BatchedConvexPathsGM : public GM {
private:
SkString onShortName() override { return SkString("batchedconvexpaths"); }
SkISize onISize() override { return SkISize::Make(512, 512); }
void modifyGrContextOptions(GrContextOptions* ctxOptions) override {
// Ensure our paths don't go through the atlas path renderer.
ctxOptions->fGpuPathRenderers &= ~GpuPathRenderers::kAtlas;
}
DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
canvas->clear(SK_ColorBLACK);
for (uint32_t i = 0; i < 10; ++i) {
SkAutoCanvasRestore acr(canvas, true);
int numPoints = (i + 3) * 3;
SkPath path;
path.moveTo(1, 0);
for (float j = 1; j < numPoints; j += 3) {
constexpr float k2PI = SK_ScalarPI * 2;
path.cubicTo(cosf(j/numPoints * k2PI), sinf(j/numPoints * k2PI),
cosf((j+1)/numPoints * k2PI), sinf((j+1)/numPoints * k2PI),
j+2 == numPoints ? 1 : cosf((j+2)/numPoints * k2PI),
j+2 == numPoints ? 0 : sinf((j+2)/numPoints * k2PI));
}
float scale = 256 - i*24;
canvas->translate(scale + (256 - scale) * .33f, scale + (256 - scale) * .33f);
canvas->scale(scale, scale);
SkPaint paint;
paint.setColor(((i + 123458383u) * 285018463u) | 0xff808080);
paint.setAlphaf(0.3f);
paint.setAntiAlias(true);
canvas->drawPath(path, paint);
}
return DrawResult::kOk;
}
};
DEF_GM( return new BatchedConvexPathsGM; )
} // namespace skiagm

View File

@ -31,6 +31,7 @@ gm_sources = [
"$_gm/backdrop.cpp",
"$_gm/backdrop_imagefilter_croprect.cpp",
"$_gm/badpaint.cpp",
"$_gm/batchedconvexpaths.cpp",
"$_gm/bc1_transparency.cpp",
"$_gm/beziers.cpp",
"$_gm/bicubic.cpp",

View File

@ -122,7 +122,7 @@ private:
caps);
break;
}
fTessellator->prepare(flushState, {pathMatrix, fPath}, fPath.countVerbs());
fTessellator->prepare(flushState, {pathMatrix, fPath, kCyan}, fPath.countVerbs());
fProgram = GrTessellationShader::MakeProgram({alloc, flushState->writeView(),
flushState->usesMSAASurface(),
&flushState->dstProxyView(),

View File

@ -68,7 +68,8 @@ private:
class AtlasPathList : SkNoncopyable {
public:
void add(PathDrawAllocator* alloc, const SkMatrix& pathMatrix, const SkPath& path) {
fPathDrawList = &alloc->emplace_back(pathMatrix, path, fPathDrawList);
fPathDrawList = &alloc->emplace_back(pathMatrix, path, SK_PMColor4fTRANSPARENT,
fPathDrawList);
if (path.isInverseFillType()) {
// The atlas never has inverse paths. The inversion happens later.
fPathDrawList->fPath.toggleInverseFillType();

View File

@ -24,7 +24,8 @@ class HullShader : public GrPathTessellationShader {
public:
HullShader(const SkMatrix& viewMatrix, SkPMColor4f color, const GrShaderCaps& shaderCaps)
: GrPathTessellationShader(kTessellate_HullShader_ClassID,
GrPrimitiveType::kTriangleStrip, 0, viewMatrix, color) {
GrPrimitiveType::kTriangleStrip, 0, viewMatrix, color,
skgpu::PatchAttribs::kNone) {
fInstanceAttribs.emplace_back("p01", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
fInstanceAttribs.emplace_back("p23", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
if (!shaderCaps.infinitySupport()) {
@ -55,8 +56,11 @@ private:
std::unique_ptr<GrGeometryProcessor::ProgramImpl> HullShader::makeProgramImpl(
const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(const GrShaderCaps& shaderCaps, const GrPathTessellationShader&,
GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
void emitVertexCode(const GrShaderCaps& shaderCaps,
const GrPathTessellationShader&,
GrGLSLVertexBuilder* v,
GrGLSLVaryingHandler*,
GrGPArgs* gpArgs) override {
if (shaderCaps.infinitySupport()) {
v->insertFunction(R"(
bool is_conic_curve() { return isinf(p23.w); }
@ -409,7 +413,7 @@ void PathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
if (fTessellator) {
// Must be called after polysToTriangles() in order for fFanBreadcrumbs to be complete.
fTessellator->prepare(flushState,
{SkMatrix::I(), fPath},
{SkMatrix::I(), fPath, SK_PMColor4fTRANSPARENT},
fPath.countVerbs(),
&fFanBreadcrumbs);
}

View File

@ -246,7 +246,7 @@ void PathStencilCoverOp::onPrepare(GrOpFlushState* flushState) {
int maxTrianglesInFans = std::max(maxCombinedFanEdges - 2, 0);
VertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxTrianglesInFans * 3);
int fanTriangleCount = 0;
for (auto [pathMatrix, path] : *fPathDrawList) {
for (auto [pathMatrix, path, color] : *fPathDrawList) {
AffineMatrix m(pathMatrix);
for (PathMiddleOutFanIter it(path); !it.done();) {
for (auto [p0, p1, p2] : it.nextStack()) {
@ -269,7 +269,7 @@ void PathStencilCoverOp::onPrepare(GrOpFlushState* flushState) {
&fBBoxBuffer,
&fBBoxBaseInstance);
SkDEBUGCODE(int pathCount = 0;)
for (auto [pathMatrix, path] : *fPathDrawList) {
for (auto [pathMatrix, path, color] : *fPathDrawList) {
SkDEBUGCODE(auto end = vertexWriter.makeOffset(instanceStride));
vertexWriter << pathMatrix.getScaleX()
<< pathMatrix.getSkewY()

View File

@ -34,7 +34,7 @@ private:
FillPathFlags pathFlags,
const SkRect& drawBounds)
: GrDrawOp(ClassID())
, fPathDrawList(arena->make<PathDrawList>(viewMatrix, path))
, fPathDrawList(arena->make<PathDrawList>(viewMatrix, path, SK_PMColor4fTRANSPARENT))
, fTotalCombinedPathVerbCnt(path.countVerbs())
, fPathCount(1)
, fPathFlags(pathFlags)

View File

@ -25,8 +25,46 @@ void PathTessellateOp::visitProxies(const GrVisitProxyFunc& func) const {
GrProcessorSet::Analysis PathTessellateOp::finalize(const GrCaps& caps,
const GrAppliedClip* clip,
GrClampType clampType) {
return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
clampType, &fColor);
auto analysis = fProcessors.finalize(this->headDraw().fColor,
GrProcessorAnalysisCoverage::kNone,
clip,
nullptr,
caps,
clampType,
&this->headDraw().fColor);
if (!analysis.usesLocalCoords()) {
// Since we don't need local coords, we can transform on CPU instead of in the shader. This
// gives us better batching potential.
this->headDraw().fPathMatrix = fShaderMatrix;
fShaderMatrix = SkMatrix::I();
}
return analysis;
}
GrDrawOp::CombineResult PathTessellateOp::onCombineIfPossible(GrOp* grOp,
SkArenaAlloc*,
const GrCaps&) {
auto* op = grOp->cast<PathTessellateOp>();
bool canMerge = fAAType == op->fAAType &&
fStencil == op->fStencil &&
fProcessors == op->fProcessors &&
fShaderMatrix == op->fShaderMatrix;
if (canMerge) {
fTotalCombinedPathVerbCnt += op->fTotalCombinedPathVerbCnt;
fPatchAttribs |= op->fPatchAttribs;
if (!(fPatchAttribs & PatchAttribs::kColor) &&
this->headDraw().fColor != op->headDraw().fColor) {
// Color is no longer uniform. Move it into patch attribs.
fPatchAttribs |= PatchAttribs::kColor;
}
*fPathDrawTail = op->fPathDrawList;
fPathDrawTail = op->fPathDrawTail;
return CombineResult::kMerged;
}
return CombineResult::kCannotCombine;
}
void PathTessellateOp::prepareTessellator(const GrTessellationShader::ProgramArgs& args,
@ -35,9 +73,16 @@ void PathTessellateOp::prepareTessellator(const GrTessellationShader::ProgramArg
SkASSERT(!fTessellationProgram);
auto* pipeline = GrTessellationShader::MakePipeline(args, fAAType, std::move(appliedClip),
std::move(fProcessors));
fTessellator = PathWedgeTessellator::Make(args.fArena, fViewMatrix, fColor, fPath.countVerbs(),
*pipeline, *args.fCaps);
fTessellationProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(), pipeline,
fTessellator = PathWedgeTessellator::Make(args.fArena,
fShaderMatrix,
this->headDraw().fColor,
fTotalCombinedPathVerbCnt,
*pipeline,
*args.fCaps,
fPatchAttribs);
fTessellationProgram = GrTessellationShader::MakeProgram(args,
fTessellator->shader(),
pipeline,
fStencil);
}
@ -64,7 +109,7 @@ void PathTessellateOp::onPrepare(GrOpFlushState* flushState) {
&flushState->caps()}, flushState->detachAppliedClip());
SkASSERT(fTessellator);
}
fTessellator->prepare(flushState, {SkMatrix::I(), fPath}, fPath.countVerbs());
fTessellator->prepare(flushState, *fPathDrawList, fTotalCombinedPathVerbCnt);
}
void PathTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
@ -73,7 +118,6 @@ void PathTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chain
flushState->bindPipelineAndScissorClip(*fTessellationProgram, this->bounds());
flushState->bindTextures(fTessellationProgram->geomProc(), nullptr,
fTessellationProgram->pipeline());
fTessellator->draw(flushState);
}

View File

@ -9,14 +9,10 @@
#define PathTessellateOp_DEFINED
#include "src/gpu/ops/GrDrawOp.h"
#include "src/gpu/tessellate/PathTessellator.h"
#include "src/gpu/tessellate/Tessellation.h"
#include "src/gpu/tessellate/shaders/GrTessellationShader.h"
namespace skgpu {
class PathTessellator;
};
namespace skgpu::v1 {
// Tessellates a path directly to the color buffer, using one single render pass. This currently
@ -25,39 +21,54 @@ class PathTessellateOp final : public GrDrawOp {
private:
DEFINE_OP_CLASS_ID
PathTessellateOp(const SkMatrix& viewMatrix, const SkPath& path, GrPaint&& paint,
GrAAType aaType, const GrUserStencilSettings* stencil,
using PathDrawList = PathTessellator::PathDrawList;
PathTessellateOp(SkArenaAlloc* arena,
GrAAType aaType,
const GrUserStencilSettings* stencil,
const SkMatrix& viewMatrix,
const SkPath& path,
GrPaint&& paint,
const SkRect& drawBounds)
: GrDrawOp(ClassID())
, fViewMatrix(viewMatrix)
, fPath(path)
, fAAType(aaType)
, fStencil(stencil)
, fColor(paint.getColor4f())
, fProcessors(std::move(paint)) {
SkASSERT(!fPath.isInverseFillType());
, fTotalCombinedPathVerbCnt(path.countVerbs())
, fPathDrawList(arena->make<PathDrawList>(SkMatrix::I(), path, paint.getColor4f()))
, fPathDrawTail(&fPathDrawList->fNext)
, fProcessors(std::move(paint))
, fShaderMatrix(viewMatrix) {
SkASSERT(!path.isInverseFillType());
if (!this->headDraw().fColor.fitsInBytes()) {
fPatchAttribs |= PatchAttribs::kWideColorIfEnabled;
}
this->setBounds(drawBounds, HasAABloat::kNo, IsHairline::kNo);
}
PathDrawList& headDraw() { return *fPathDrawList; }
void prepareTessellator(const GrTessellationShader::ProgramArgs&, GrAppliedClip&& clip);
// GrDrawOp overrides.
const char* name() const override { return "PathTessellateOp"; }
bool usesMSAA() const override { return fAAType == GrAAType::kMSAA; }
void visitProxies(const GrVisitProxyFunc&) const override;
GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override;
bool usesStencil() const override { return !fStencil->isUnused(); }
void prepareTessellator(const GrTessellationShader::ProgramArgs&, GrAppliedClip&& clip);
CombineResult onCombineIfPossible(GrOp*, SkArenaAlloc*, const GrCaps&) override;
void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*,
const GrDstProxyView&, GrXferBarrierFlags, GrLoadOp colorLoadOp) override;
void onPrepare(GrOpFlushState*) override;
void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
const SkMatrix fViewMatrix;
const SkPath fPath;
const GrAAType fAAType;
const GrUserStencilSettings* const fStencil;
SkPMColor4f fColor;
int fTotalCombinedPathVerbCnt;
PatchAttribs fPatchAttribs = PatchAttribs::kNone;
PathDrawList* const fPathDrawList;
PathDrawList** fPathDrawTail;
GrProcessorSet fProcessors;
SkMatrix fShaderMatrix;
// Decided during prepareTessellator.
PathTessellator* fTessellator = nullptr;

View File

@ -165,9 +165,14 @@ bool TessellationPathRenderer::onDrawPath(const DrawPathArgs& args) {
// Handle convex paths.
if (args.fShape->knownToBeConvex() && !path.isInverseFillType()) {
auto op = GrOp::Make<PathTessellateOp>(args.fContext, *args.fViewMatrix, path,
std::move(args.fPaint), args.fAAType,
args.fUserStencilSettings, pathDevBounds);
auto op = GrOp::Make<PathTessellateOp>(args.fContext,
args.fSurfaceDrawContext->arenaAlloc(),
args.fAAType,
args.fUserStencilSettings,
*args.fViewMatrix,
path,
std::move(args.fPaint),
pathDevBounds);
sdc->addDrawOp(args.fClip, std::move(op));
return true;
}
@ -221,8 +226,13 @@ void TessellationPathRenderer::onStencilPath(const StencilPathArgs& args) {
GrPaint stencilPaint;
stencilPaint.setXPFactory(GrDisableColorXPFactory::Get());
auto op = GrOp::Make<PathTessellateOp>(args.fContext, *args.fViewMatrix, path,
std::move(stencilPaint), aaType, &kMarkStencil,
auto op = GrOp::Make<PathTessellateOp>(args.fContext,
args.fSurfaceDrawContext->arenaAlloc(),
aaType,
&kMarkStencil,
*args.fViewMatrix,
path,
std::move(stencilPaint),
pathDevBounds);
sdc->addDrawOp(args.fClip, std::move(op));
return;

View File

@ -28,12 +28,20 @@ public:
}
// Updates the fan point that will be written out with each patch (i.e., the point that wedges
// fan around). PathPatchAttrib::kFanPoint must be enabled.
// fan around).
// PathPatchAttrib::kFanPoint must be enabled.
void updateFanPointAttrib(SkPoint fanPoint) {
SkASSERT(fPatchAttribs & PatchAttribs::kFanPoint);
fFanPointAttrib = fanPoint;
}
// Updates the color that will be written out with each patch.
// PathPatchAttrib::kColor must be enabled.
void updateColorAttrib(const SkPMColor4f& color) {
SkASSERT(fPatchAttribs & PatchAttribs::kColor);
fColorAttrib.set(color, fPatchAttribs & PatchAttribs::kWideColorIfEnabled);
}
// RAII. Appends a patch during construction and writes the remaining data for a cubic during
// destruction. The caller outputs p0,p1,p2,p3 (8 floats):
//
@ -122,11 +130,13 @@ private:
void outputPatchAttribs(VertexWriter vertexWriter, float explicitCurveType) {
vertexWriter << If((fPatchAttribs & PatchAttribs::kFanPoint), fFanPointAttrib)
<< If((fPatchAttribs & PatchAttribs::kColor), fColorAttrib)
<< If((fPatchAttribs & PatchAttribs::kExplicitCurveType), explicitCurveType);
}
const PatchAttribs fPatchAttribs;
SkPoint fFanPointAttrib;
GrVertexColor fColorAttrib;
GrVertexChunkBuilder fChunker;

View File

@ -33,22 +33,26 @@ PathCurveTessellator* PathCurveTessellator::Make(SkArenaAlloc* arena,
DrawInnerFan drawInnerFan,
int numPathVerbs,
const GrPipeline& pipeline,
const GrCaps& caps) {
using PatchType = GrPathTessellationShader::PatchType;
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,
PatchType::kCurves);
attribs);
} else {
shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(*caps.shaderCaps(), arena,
viewMatrix, color,
PatchType::kCurves);
attribs);
}
return arena->make([=](void* objStart) {
return new(objStart) PathCurveTessellator(shader, drawInnerFan);
return new(objStart) PathCurveTessellator(shader, attribs, drawInnerFan);
});
}
@ -84,16 +88,15 @@ void PathCurveTessellator::prepare(GrMeshDrawTarget* target,
size_t patchStride = fShader->willUseTessellationShaders() ? fShader->vertexStride() * 4
: fShader->instanceStride();
auto attribs = PatchAttribs::kNone;
if (!shaderCaps.infinitySupport()) {
attribs |= PatchAttribs::kExplicitCurveType;
}
PatchWriter patchWriter(target, &fVertexChunkArray, patchStride, patchAllocCount, attribs);
PatchWriter patchWriter(target, &fVertexChunkArray, patchStride, patchAllocCount, fAttribs);
// Write out inner fan triangles.
if (fDrawInnerFan) {
for (auto [pathMatrix, path] : pathDrawList) {
for (auto [pathMatrix, path, color] : pathDrawList) {
AffineMatrix m(pathMatrix);
if (fAttribs & PatchAttribs::kColor) {
patchWriter.updateColorAttrib(color);
}
for (PathMiddleOutFanIter it(path); !it.done();) {
for (auto [p0, p1, p2] : it.nextStack()) {
TrianglePatch(patchWriter) << m.map2Points(p0, p1) << m.mapPoint(p2);
@ -106,14 +109,18 @@ void PathCurveTessellator::prepare(GrMeshDrawTarget* target,
if (breadcrumbTriangleList) {
SkDEBUGCODE(int count = 0;)
#ifdef SK_DEBUG
for (auto [pathMatrix, path] : pathDrawList) {
// This assert isn't actually necessary, but we currently only use breadcrumb
// triangles with an identity pathMatrix. If that ever changes, this assert will
// serve as a gentle reminder to make sure the breadcrumb triangles are also
for (auto [pathMatrix, path, color] : pathDrawList) {
// This assert isn't actually necessary, but we currently only use breadcrumb triangles
// with an identity pathMatrix and transparent color. If that ever changes, this assert
// will serve as a gentle reminder to make sure the breadcrumb triangles are also
// transformed on the CPU.
SkASSERT(pathMatrix.isIdentity());
SkASSERT(color == SK_PMColor4fTRANSPARENT);
}
#endif
if (fAttribs & PatchAttribs::kColor) {
patchWriter.updateColorAttrib(SK_PMColor4fTRANSPARENT);
}
for (const auto* tri = breadcrumbTriangleList->head(); tri; tri = tri->fNext) {
SkDEBUGCODE(++count;)
auto p0 = float2::Load(tri->fPts);
@ -149,9 +156,12 @@ void PathCurveTessellator::prepare(GrMeshDrawTarget* target,
// emit at least 2 segments so we can support triangles.
float numFixedSegments_pow4 = 2*2*2*2;
for (auto [pathMatrix, path] : pathDrawList) {
for (auto [pathMatrix, path, color] : pathDrawList) {
AffineMatrix m(pathMatrix);
wangs_formula::VectorXform totalXform(SkMatrix::Concat(fShader->viewMatrix(), pathMatrix));
if (fAttribs & PatchAttribs::kColor) {
patchWriter.updateColorAttrib(color);
}
for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
switch (verb) {
case SkPathVerb::kQuad: {

View File

@ -36,7 +36,8 @@ public:
DrawInnerFan,
int numPathVerbs,
const GrPipeline&,
const GrCaps&);
const GrCaps&,
PatchAttribs = PatchAttribs::kNone);
void prepare(GrMeshDrawTarget* target,
const PathDrawList& pathDrawList,
@ -64,8 +65,10 @@ public:
#endif
private:
PathCurveTessellator(GrPathTessellationShader* shader, DrawInnerFan drawInnerFan)
: PathTessellator(shader)
PathCurveTessellator(GrPathTessellationShader* shader,
PatchAttribs attribs,
DrawInnerFan drawInnerFan)
: PathTessellator(shader, attribs)
, fDrawInnerFan(drawInnerFan == DrawInnerFan::kYes) {}
const bool fDrawInnerFan;

View File

@ -12,6 +12,7 @@
#include "src/gpu/BufferWriter.h"
#include "src/gpu/GrVx.h"
#include "src/gpu/geometry/GrInnerFanTriangulator.h"
#include "src/gpu/tessellate/Tessellation.h"
class SkPath;
class GrMeshDrawTarget;
@ -28,18 +29,22 @@ public:
using BreadcrumbTriangleList = GrInnerFanTriangulator::BreadcrumbTriangleList;
struct PathDrawList {
PathDrawList(const SkMatrix& pathMatrix, const SkPath& path, PathDrawList* next = nullptr)
: fPathMatrix(pathMatrix), fPath(path), fNext(next) {}
PathDrawList(const SkMatrix& pathMatrix,
const SkPath& path,
const SkPMColor4f& color,
PathDrawList* next = nullptr)
: fPathMatrix(pathMatrix), fPath(path), fColor(color), fNext(next) {}
SkMatrix fPathMatrix;
SkPath fPath;
SkPMColor4f fColor;
PathDrawList* fNext;
struct Iter {
void operator++() { fHead = fHead->fNext; }
bool operator!=(const Iter& b) const { return fHead != b.fHead; }
std::tuple<const SkMatrix&, const SkPath&> operator*() const {
return {fHead->fPathMatrix, fHead->fPath};
std::tuple<const SkMatrix&, const SkPath&, const SkPMColor4f&> operator*() const {
return {fHead->fPathMatrix, fHead->fPath, fHead->fColor};
}
const PathDrawList* fHead;
};
@ -74,9 +79,11 @@ public:
}
protected:
PathTessellator(GrPathTessellationShader* shader) : fShader(shader) {}
PathTessellator(GrPathTessellationShader* shader, PatchAttribs attribs)
: fShader(shader), fAttribs(attribs) {}
GrPathTessellationShader* fShader;
GrPathTessellationShader* const fShader;
const PatchAttribs fAttribs;
};
} // namespace skgpu

View File

@ -133,22 +133,27 @@ PathTessellator* PathWedgeTessellator::Make(SkArenaAlloc* arena,
const SkPMColor4f& color,
int numPathVerbs,
const GrPipeline& pipeline,
const GrCaps& caps) {
using PatchType = GrPathTessellationShader::PatchType;
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,
PatchType::kWedges);
attribs);
} else {
shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(*caps.shaderCaps(), arena,
viewMatrix, color,
PatchType::kWedges);
attribs);
}
return arena->make([=](void* objStart) {
return new(objStart) PathWedgeTessellator(shader);
return new(objStart) PathWedgeTessellator(shader, attribs);
});
}
@ -171,11 +176,7 @@ void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
size_t patchStride = fShader->willUseTessellationShaders() ? fShader->vertexStride() * 5
: fShader->instanceStride();
auto attribs = PatchAttribs::kFanPoint;
if (!shaderCaps.infinitySupport()) {
attribs |= PatchAttribs::kExplicitCurveType;
}
PatchWriter patchWriter(target, &fVertexChunkArray, patchStride, wedgeAllocCount, attribs);
PatchWriter patchWriter(target, &fVertexChunkArray, patchStride, wedgeAllocCount, fAttribs);
int maxFixedCountResolveLevel = GrPathTessellationShader::kMaxFixedCountResolveLevel;
int maxSegments;
@ -191,9 +192,12 @@ void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
// emit at least 1 segment.
float numFixedSegments_pow4 = 1;
for (auto [pathMatrix, path] : pathDrawList) {
for (auto [pathMatrix, path, color] : pathDrawList) {
AffineMatrix m(pathMatrix);
wangs_formula::VectorXform totalXform(SkMatrix::Concat(fShader->viewMatrix(), pathMatrix));
if (fAttribs & PatchAttribs::kColor) {
patchWriter.updateColorAttrib(color);
}
MidpointContourParser parser(path);
while (parser.parseNextContour()) {
patchWriter.updateFanPointAttrib(m.mapPoint(parser.currentMidpoint()));

View File

@ -29,7 +29,8 @@ public:
const SkPMColor4f&,
int numPathVerbs,
const GrPipeline&,
const GrCaps&);
const GrCaps&,
PatchAttribs = PatchAttribs::kNone);
void prepare(GrMeshDrawTarget*, const PathDrawList&, int totalCombinedPathVerbCnt) override;
@ -39,7 +40,8 @@ public:
#endif
private:
PathWedgeTessellator(GrPathTessellationShader* shader) : PathTessellator(shader) {}
PathWedgeTessellator(GrPathTessellationShader* shader, PatchAttribs attribs)
: PathTessellator(shader, attribs) {}
GrVertexChunkArray fVertexChunkArray;

View File

@ -23,11 +23,17 @@ struct VertexWriter;
// Don't allow linearized segments to be off by more than 1/4th of a pixel from the true curve.
SK_MAYBE_UNUSED constexpr static float kTessellationPrecision = 4;
// Optional attribs that are included in tessellation patches, following the control points.
// Optional attribs that are included in tessellation patches, following the control points and in
// the same order as they appear here.
enum class PatchAttribs {
// Attribs.
kNone = 0,
kFanPoint = 1, // Used by wedges. This is the center point the wedges fan around.
kExplicitCurveType = 1 << 1, // Used when the GPU can't infer curve type based on infinity.
kFanPoint = 1 << 0, // [float2] Used by wedges. This is the center point the wedges fan around.
kColor = 1 << 1, // [ubyte4 or float4] Used by direct-rendered convex paths.
kExplicitCurveType = 1 << 2, // [float] Used when GPU can't infer curve type based on infinity.
// Extra flags.
kWideColorIfEnabled = 1 << 3, // If kColor is set, specifies it to be float4 wide color.
};
GR_MAKE_BITFIELD_CLASS_OPS(PatchAttribs)

View File

@ -12,6 +12,8 @@
#include "src/gpu/glsl/GrGLSLVarying.h"
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
using skgpu::PatchAttribs;
namespace {
// Draws a simple array of triangles.
@ -19,7 +21,8 @@ class SimpleTriangleShader : public GrPathTessellationShader {
public:
SimpleTriangleShader(const SkMatrix& viewMatrix, SkPMColor4f color)
: GrPathTessellationShader(kTessellate_SimpleTriangleShader_ClassID,
GrPrimitiveType::kTriangles, 0, viewMatrix, color) {
GrPrimitiveType::kTriangles, 0, viewMatrix, color,
PatchAttribs::kNone) {
constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
kFloat2_GrSLType};
this->setVertexAttributes(&kInputPointAttrib, 1);
@ -34,8 +37,11 @@ private:
std::unique_ptr<GrGeometryProcessor::ProgramImpl> SimpleTriangleShader::makeProgramImpl(
const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(const GrShaderCaps&, const GrPathTessellationShader&,
GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
void emitVertexCode(const GrShaderCaps&,
const GrPathTessellationShader&,
GrGLSLVertexBuilder* v,
GrGLSLVaryingHandler*,
GrGPArgs* gpArgs) override {
v->codeAppend(R"(
float2 localcoord = inputPoint;
float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
@ -97,23 +103,34 @@ void GrPathTessellationShader::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs
kFloat2_GrSLType, "translate", &translate);
args.fVertBuilder->codeAppendf("float2x2 AFFINE_MATRIX = float2x2(%s);", affineMatrix);
args.fVertBuilder->codeAppendf("float2 TRANSLATE = %s;", translate);
this->emitVertexCode(*args.fShaderCaps, shader, args.fVertBuilder, gpArgs);
this->emitVertexCode(*args.fShaderCaps,
shader,
args.fVertBuilder,
args.fVaryingHandler,
gpArgs);
// Fragment shader.
const char* color;
fColorUniform = args.fUniformHandler->addUniform(nullptr, kFragment_GrShaderFlag,
kHalf4_GrSLType, "color", &color);
args.fFragBuilder->codeAppendf("half4 %s = %s;", args.fOutputColor, color);
if (!(shader.fAttribs & PatchAttribs::kColor)) {
const char* color;
fColorUniform = args.fUniformHandler->addUniform(nullptr, kFragment_GrShaderFlag,
kHalf4_GrSLType, "color", &color);
args.fFragBuilder->codeAppendf("half4 %s = %s;", args.fOutputColor, color);
} else {
args.fFragBuilder->codeAppendf("half4 %s = %s;",
args.fOutputColor, fVaryingColorName.c_str());
}
args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
}
void GrPathTessellationShader::Impl::setData(const GrGLSLProgramDataManager& pdman, const
GrShaderCaps&, const GrGeometryProcessor& geomProc) {
const auto& shader = geomProc.cast<GrTessellationShader>();
const auto& shader = geomProc.cast<GrPathTessellationShader>();
const SkMatrix& m = shader.viewMatrix();
pdman.set4f(fAffineMatrixUniform, m.getScaleX(), m.getSkewY(), m.getSkewX(), m.getScaleY());
pdman.set2f(fTranslateUniform, m.getTranslateX(), m.getTranslateY());
const SkPMColor4f& color = shader.color();
pdman.set4f(fColorUniform, color.fR, color.fG, color.fB, color.fA);
if (!(shader.fAttribs & PatchAttribs::kColor)) {
const SkPMColor4f& color = shader.color();
pdman.set4f(fColorUniform, color.fR, color.fG, color.fB, color.fA);
}
}

View File

@ -8,6 +8,7 @@
#ifndef GrPathTessellationShader_DEFINED
#define GrPathTessellationShader_DEFINED
#include "src/gpu/tessellate/Tessellation.h"
#include "src/gpu/tessellate/shaders/GrTessellationShader.h"
// This is the base class for shaders in the GPU tessellator that fill paths.
@ -28,14 +29,6 @@ public:
return (1 << resolveLevel) - 1;
}
enum class PatchType : bool {
// An ice cream cone shaped patch, with 4 curve control points on top of a triangle that
// fans from a 5th point at the center of the contour. (5 points per patch.)
kWedges,
// A standalone closed curve made up 4 control points. (4 points per patch.)
kCurves
};
// 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:
//
@ -48,10 +41,14 @@ public:
// 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).
//
// If PatchAttribs::kFanPoint is set, an additional triangle is added, connecting the base of
// the curve to the fan point.
static GrPathTessellationShader* MakeMiddleOutFixedCountShader(const GrShaderCaps&,
SkArenaAlloc*,
const SkMatrix& viewMatrix,
const SkPMColor4f&, PatchType);
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.
@ -63,9 +60,6 @@ public:
// 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).
//
// The "curve" and "wedge" buffers are nearly identical, but we keep them separate for now in
// case there is a perf hit in the curve case for not using index 0.
constexpr static int SizeOfVertexBufferForMiddleOutCurves() {
constexpr int kMaxVertexCount = (1 << kMaxFixedCountResolveLevel) + 1;
return kMaxVertexCount * kMiddleOutVertexStride;
@ -89,12 +83,14 @@ public:
}
static void InitializeIndexBufferForMiddleOutWedges(skgpu::VertexWriter, size_t bufferSize);
// 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.
// 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&, PatchType);
const SkPMColor4f&,
skgpu::PatchAttribs);
// Returns the stencil settings to use for a standard Redbook "stencil" pass.
static const GrUserStencilSettings* StencilPathSettings(GrFillRule fillRule) {
@ -162,9 +158,10 @@ protected:
GrPathTessellationShader(ClassID classID, GrPrimitiveType primitiveType,
int tessellationPatchVertexCount, const SkMatrix& viewMatrix,
const SkPMColor4f& color)
const SkPMColor4f& color, skgpu::PatchAttribs attribs)
: GrTessellationShader(classID, primitiveType, tessellationPatchVertexCount, viewMatrix,
color) {
color)
, fAttribs(attribs) {
}
// Default path tessellation shader implementation that manages a uniform matrix and color.
@ -183,13 +180,19 @@ protected:
// does not always.
static const char* kEvalRationalCubicFn;
virtual void emitVertexCode(const GrShaderCaps&, const GrPathTessellationShader&,
GrGLSLVertexBuilder*, GrGPArgs*) = 0;
virtual void emitVertexCode(const GrShaderCaps&,
const GrPathTessellationShader&,
GrGLSLVertexBuilder*,
GrGLSLVaryingHandler*,
GrGPArgs*) = 0;
GrGLSLUniformHandler::UniformHandle fAffineMatrixUniform;
GrGLSLUniformHandler::UniformHandle fTranslateUniform;
GrGLSLUniformHandler::UniformHandle fColorUniform;
SkString fVaryingColorName;
};
const skgpu::PatchAttribs fAttribs;
};
#endif

View File

@ -11,6 +11,8 @@
#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.
@ -30,9 +32,11 @@ constexpr static char kSkSLTypeDefs[] = R"(
// 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)
HardwareWedgeShader(const SkMatrix& viewMatrix,
const SkPMColor4f& color,
PatchAttribs attribs)
: GrPathTessellationShader(kTessellate_HardwareWedgeShader_ClassID,
GrPrimitiveType::kPatches, 5, viewMatrix, color) {
GrPrimitiveType::kPatches, 5, viewMatrix, color, attribs) {
constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
kFloat2_GrSLType};
this->setVertexAttributes(&kInputPointAttrib, 1);
@ -47,8 +51,11 @@ private:
std::unique_ptr<GrGeometryProcessor::ProgramImpl> HardwareWedgeShader::makeProgramImpl(
const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(const GrShaderCaps&, const GrPathTessellationShader&,
GrGLSLVertexBuilder* v, GrGPArgs*) override {
void emitVertexCode(const GrShaderCaps&,
const GrPathTessellationShader&,
GrGLSLVertexBuilder* v,
GrGLSLVaryingHandler*,
GrGPArgs*) override {
v->declareGlobal(GrShaderVar("vsPt", kFloat2_GrSLType, GrShaderVar::TypeModifier::Out));
v->codeAppend(R"(
// If y is infinity then x is a conic weight. Don't transform.
@ -159,9 +166,12 @@ std::unique_ptr<GrGeometryProcessor::ProgramImpl> HardwareWedgeShader::makeProgr
// 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)
HardwareCurveShader(const SkMatrix& viewMatrix,
const SkPMColor4f& color,
PatchAttribs attribs)
: GrPathTessellationShader(kTessellate_HardwareCurveShader_ClassID,
GrPrimitiveType::kPatches, 4, viewMatrix, color) {
GrPrimitiveType::kPatches, 4, viewMatrix, color,
attribs) {
constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
kFloat2_GrSLType};
this->setVertexAttributes(&kInputPointAttrib, 1);
@ -176,8 +186,11 @@ private:
std::unique_ptr<GrGeometryProcessor::ProgramImpl> HardwareCurveShader::makeProgramImpl(
const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(const GrShaderCaps&, const GrPathTessellationShader&,
GrGLSLVertexBuilder* v, GrGPArgs*) override {
void emitVertexCode(const GrShaderCaps&,
const GrPathTessellationShader&,
GrGLSLVertexBuilder* v,
GrGLSLVaryingHandler*,
GrGPArgs*) override {
v->declareGlobal(GrShaderVar("P", kFloat2_GrSLType, GrShaderVar::TypeModifier::Out));
v->codeAppend(R"(
// If y is infinity then x is a conic weight. Don't transform.
@ -315,12 +328,11 @@ std::unique_ptr<GrGeometryProcessor::ProgramImpl> HardwareCurveShader::makeProgr
GrPathTessellationShader* GrPathTessellationShader::MakeHardwareTessellationShader(
SkArenaAlloc* arena, const SkMatrix& viewMatrix, const SkPMColor4f& color,
PatchType patchType) {
switch (patchType) {
case PatchType::kWedges:
return arena->make<HardwareWedgeShader>(viewMatrix, color);
case PatchType::kCurves:
return arena->make<HardwareCurveShader>(viewMatrix, color);
PatchAttribs attribs) {
SkASSERT(!(attribs & PatchAttribs::kColor)); // Not yet implemented.
if (attribs & PatchAttribs::kFanPoint) {
return arena->make<HardwareWedgeShader>(viewMatrix, color, attribs);
} else {
return arena->make<HardwareCurveShader>(viewMatrix, color, attribs);
}
SkUNREACHABLE;
}

View File

@ -12,6 +12,7 @@
#include "src/gpu/tessellate/Tessellation.h"
#include "src/gpu/tessellate/WangsFormula.h"
using skgpu::PatchAttribs;
using skgpu::VertexWriter;
namespace {
@ -31,16 +32,24 @@ namespace {
class MiddleOutShader : public GrPathTessellationShader {
public:
MiddleOutShader(const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
const SkPMColor4f& color, PatchType patchType)
const SkPMColor4f& color, PatchAttribs attribs)
: GrPathTessellationShader(kTessellate_MiddleOutShader_ClassID,
GrPrimitiveType::kTriangles, 0, viewMatrix, color)
, fPatchType(patchType) {
GrPrimitiveType::kTriangles, 0, viewMatrix, color, attribs) {
fInstanceAttribs.emplace_back("p01", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
fInstanceAttribs.emplace_back("p23", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
if (fPatchType == PatchType::kWedges) {
fInstanceAttribs.emplace_back("fanPoint", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
if (fAttribs & PatchAttribs::kFanPoint) {
fInstanceAttribs.emplace_back("fanPointAttrib",
kFloat2_GrVertexAttribType,
kFloat2_GrSLType);
}
if (!shaderCaps.infinitySupport()) {
if (fAttribs & PatchAttribs::kColor) {
fInstanceAttribs.emplace_back("colorAttrib",
(fAttribs & PatchAttribs::kWideColorIfEnabled)
? kFloat4_GrVertexAttribType
: kUByte4_norm_GrVertexAttribType,
kHalf4_GrSLType);
}
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.
@ -57,35 +66,43 @@ public:
private:
const char* name() const final { return "tessellate_MiddleOutShader"; }
void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
b->add32((uint32_t)fPatchType);
// 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;
const PatchType fPatchType;
constexpr static int kMaxInstanceAttribCount = 4;
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, GrGPArgs* gpArgs) override {
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)kMaxFixedCountResolveLevel);
v->defineConstant("MAX_FIXED_SEGMENTS", (float)kMaxFixedCountSegments);
v->insertFunction(skgpu::wangs_formula::as_sksl().c_str());
if (shaderCaps.infinitySupport()) {
v->insertFunction(R"(
bool is_conic_curve() { return isinf(p23.w); }
bool is_triangular_conic_curve() { return isinf(p23.z); })");
} else {
if (middleOutShader.fAttribs & PatchAttribs::kExplicitCurveType) {
v->insertFunction(SkStringPrintf(R"(
bool is_conic_curve() { return curveType != %g; })", kCubicCurveType).c_str());
v->insertFunction(SkStringPrintf(R"(
bool is_triangular_conic_curve() {
return curveType == %g;
})", 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"(
@ -102,11 +119,11 @@ std::unique_ptr<GrGeometryProcessor::ProgramImpl> MiddleOutShader::makeProgramIm
float resolveLevel = resolveLevel_and_idx.x;
float idxInResolveLevel = resolveLevel_and_idx.y;
float2 localcoord;)");
if (shader.cast<MiddleOutShader>().fPatchType == PatchType::kWedges) {
if (middleOutShader.fAttribs & PatchAttribs::kFanPoint) {
v->codeAppend(R"(
// A negative resolve level means this is the fan point.
if (resolveLevel < 0) {
localcoord = fanPoint;
localcoord = fanPointAttrib;
} else)"); // Fall through to next if ().
}
v->codeAppend(R"(
@ -169,6 +186,14 @@ std::unique_ptr<GrGeometryProcessor::ProgramImpl> MiddleOutShader::makeProgramIm
float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
if (middleOutShader.fAttribs & PatchAttribs::kColor) {
GrGLSLVarying colorVarying(GrSLType::kHalf4_GrSLType);
varyingHandler->addVarying("color",
&colorVarying,
GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
v->codeAppendf("%s = colorAttrib;", colorVarying.vsOut());
fVaryingColorName = colorVarying.fsIn();
}
}
};
return std::make_unique<Impl>();
@ -178,8 +203,8 @@ std::unique_ptr<GrGeometryProcessor::ProgramImpl> MiddleOutShader::makeProgramIm
GrPathTessellationShader* GrPathTessellationShader::MakeMiddleOutFixedCountShader(
const GrShaderCaps& shaderCaps, SkArenaAlloc* arena, const SkMatrix& viewMatrix,
const SkPMColor4f& color, PatchType patchType) {
return arena->make<MiddleOutShader>(shaderCaps, viewMatrix, color, patchType);
const SkPMColor4f& color, PatchAttribs attribs) {
return arena->make<MiddleOutShader>(shaderCaps, viewMatrix, color, attribs);
}
void GrPathTessellationShader::InitializeVertexBufferForMiddleOutCurves(VertexWriter vertexWriter,