Restructure path tessellation shaders

Merges the GrStrokePathShader and GrFillPathShader classes together.
Creates a new base class called GrPathTessellationShader. Now they all
have a uniform color and can all be draw to stencil and color both.
This is necessary cleanup in general, but will also allow us to create
a convex tessellation op that bypasses the stencil buffer.

Bug: skia:10419
Change-Id: Ifc492c94d3de044a36bd9ea95b1d5aa22e007905
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/413696
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Chris Dalton 2021-06-01 12:11:57 -06:00 committed by Skia Commit-Bot
parent 8f73edeaf6
commit 2f733ec2dd
22 changed files with 493 additions and 574 deletions

View File

@ -113,21 +113,21 @@ protected:
DEF_PATH_TESS_BENCH(GrPathIndirectTessellator, make_cubic_path(18), SkMatrix::I()) {
SkArenaAlloc arena(1024);
auto tess = GrPathIndirectTessellator::Make(&arena, fMatrix, fPath,
auto tess = GrPathIndirectTessellator::Make(&arena, fPath, fMatrix, SK_PMColor4fTRANSPARENT,
GrPathIndirectTessellator::DrawInnerFan::kNo);
tess->prepare(fTarget.get(), SkRectPriv::MakeLargest(), fPath, nullptr);
}
DEF_PATH_TESS_BENCH(GrPathOuterCurveTessellator, make_cubic_path(8), SkMatrix::I()) {
SkArenaAlloc arena(1024);
auto tess = GrPathOuterCurveTessellator::Make(&arena, fMatrix,
auto tess = GrPathOuterCurveTessellator::Make(&arena, fMatrix, SK_PMColor4fTRANSPARENT,
GrPathTessellator::DrawInnerFan::kNo);
tess->prepare(fTarget.get(), SkRectPriv::MakeLargest(), fPath, nullptr);
}
DEF_PATH_TESS_BENCH(GrPathWedgeTessellator, make_cubic_path(8), SkMatrix::I()) {
SkArenaAlloc arena(1024);
auto tess = GrPathWedgeTessellator::Make(&arena, fMatrix);
auto tess = GrPathWedgeTessellator::Make(&arena, fMatrix, SK_PMColor4fTRANSPARENT);
tess->prepare(fTarget.get(), SkRectPriv::MakeLargest(), fPath, nullptr);
}

View File

@ -452,19 +452,16 @@ skia_gpu_sources = [
"$_src/gpu/tessellate/GrCullTest.h",
"$_src/gpu/tessellate/GrDrawAtlasPathOp.cpp",
"$_src/gpu/tessellate/GrDrawAtlasPathOp.h",
"$_src/gpu/tessellate/GrFillPathShader.cpp",
"$_src/gpu/tessellate/GrFillPathShader.h",
"$_src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h",
"$_src/gpu/tessellate/GrMidpointContourParser.h",
"$_src/gpu/tessellate/GrPathInnerTriangulateOp.cpp",
"$_src/gpu/tessellate/GrPathInnerTriangulateOp.h",
"$_src/gpu/tessellate/GrPathShader.h",
"$_src/gpu/tessellate/GrPathStencilFillOp.cpp",
"$_src/gpu/tessellate/GrPathStencilFillOp.h",
"$_src/gpu/tessellate/GrPathTessellationShader.cpp",
"$_src/gpu/tessellate/GrPathTessellationShader.h",
"$_src/gpu/tessellate/GrPathTessellator.cpp",
"$_src/gpu/tessellate/GrPathTessellator.h",
"$_src/gpu/tessellate/GrStencilPathShader.cpp",
"$_src/gpu/tessellate/GrStencilPathShader.h",
"$_src/gpu/tessellate/GrStrokeFixedCountTessellator.cpp",
"$_src/gpu/tessellate/GrStrokeFixedCountTessellator.h",
"$_src/gpu/tessellate/GrStrokeHardwareTessellator.cpp",
@ -483,6 +480,7 @@ skia_gpu_sources = [
"$_src/gpu/tessellate/GrStrokeTessellator.h",
"$_src/gpu/tessellate/GrTessellationPathRenderer.cpp",
"$_src/gpu/tessellate/GrTessellationPathRenderer.h",
"$_src/gpu/tessellate/GrTessellationShader.h",
"$_src/gpu/tessellate/GrVectorXform.h",
# text

View File

@ -14,8 +14,8 @@
#include "src/core/SkCanvasPriv.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/GrSurfaceDrawContext.h"
#include "src/gpu/tessellate/GrPathTessellationShader.h"
#include "src/gpu/tessellate/GrPathTessellator.h"
#include "src/gpu/tessellate/GrStencilPathShader.h"
namespace {
@ -66,28 +66,30 @@ private:
const GrXferProcessor::DstProxyView&, GrXferBarrierFlags,
GrLoadOp colorLoadOp) override {}
void onPrepare(GrOpFlushState* flushState) override {
constexpr static SkPMColor4f kCyan = {0,1,1,1};
auto alloc = flushState->allocator();
switch (fMode) {
case Mode::kCurveMiddleOut:
fTessellator = GrPathIndirectTessellator::Make(
alloc, fMatrix, fPath, GrPathTessellator::DrawInnerFan::kYes);
alloc, fPath, fMatrix, kCyan, GrPathTessellator::DrawInnerFan::kYes);
break;
case Mode::kWedgeTessellate:
fTessellator = GrPathWedgeTessellator::Make(alloc, fMatrix);
fTessellator = GrPathWedgeTessellator::Make(alloc, fMatrix, kCyan);
break;
case Mode::kCurveTessellate:
fTessellator = GrPathOuterCurveTessellator::Make(
alloc, fMatrix, GrPathTessellator::DrawInnerFan::kYes);
alloc, fMatrix, kCyan, GrPathTessellator::DrawInnerFan::kYes);
break;
}
fTessellator->prepare(flushState, this->bounds(), fPath);
auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState, std::move(fProcessors),
fPipelineFlags);
fProgram = GrPathShader::MakeProgram({alloc, flushState->writeView(),
&flushState->dstProxyView(),
flushState->renderPassBarriers(), GrLoadOp::kClear,
&flushState->caps()}, fTessellator->shader(), pipeline,
&GrUserStencilSettings::kUnused);
fProgram = GrTessellationShader::MakeProgram({alloc, flushState->writeView(),
&flushState->dstProxyView(),
flushState->renderPassBarriers(),
GrLoadOp::kClear, &flushState->caps()},
fTessellator->shader(), pipeline,
&GrUserStencilSettings::kUnused);
}
void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
flushState->bindPipeline(*fProgram, chainBounds);

View File

@ -128,14 +128,13 @@ public:
kStencilResolveProcessor_ClassID,
kFwidthSquircleTestProcessor_ClassID,
kSwizzleFragmentProcessor_ClassID,
kTessellate_BoundingBoxShader_ClassID,
kTessellate_GrCurveMiddleOutShader_ClassID,
kTessellate_GrCurveTessellateShader_ClassID,
kTessellate_GrFillBoundingBoxShader_ClassID,
kTessellate_GrFillCubicHullShader_ClassID,
kTessellate_GrFillTriangleShader_ClassID,
kTessellate_GrStencilTriangleShader_ClassID,
kTessellate_GrStrokeShader_ClassID,
kTessellate_GrTriangleShader_ClassID,
kTessellate_GrWedgeTessellateShader_ClassID,
kTessellate_HullShader_ClassID,
kTessellationTestTriShader_ClassID,
kTessellationTestRectShader_ClassID,
kTestFP_ClassID,

View File

@ -1,143 +0,0 @@
/*
* Copyright 2020 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/tessellate/GrFillPathShader.h"
#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
#include "src/gpu/glsl/GrGLSLVarying.h"
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
class GrFillPathShader::Impl : public GrGLSLGeometryProcessor {
public:
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
auto& shader = args.fGeomProc.cast<GrFillPathShader>();
args.fVaryingHandler->emitAttributes(shader);
const char* affineMatrix, *translate;
fAffineMatrixUniform = args.fUniformHandler->addUniform(
nullptr, kVertex_GrShaderFlag, kFloat4_GrSLType, "affineMatrix", &affineMatrix);
fTranslateUniform = args.fUniformHandler->addUniform(
nullptr, kVertex_GrShaderFlag, kFloat2_GrSLType, "translate", &translate);
args.fVertBuilder->codeAppendf("float2x2 AFFINE_MATRIX = float2x2(%s);", affineMatrix);
args.fVertBuilder->codeAppendf("float2 TRANSLATE = %s;", translate);
args.fVertBuilder->codeAppend("float2 localcoord, vertexpos;");
shader.emitVertexCode(this, args.fVertBuilder, args.fUniformHandler);
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
const char* color;
fColorUniform = args.fUniformHandler->addUniform(
nullptr, kFragment_GrShaderFlag, kHalf4_GrSLType, "color", &color);
args.fFragBuilder->codeAppendf("half4 %s = %s;", args.fOutputColor, color);
args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
}
void setData(const GrGLSLProgramDataManager& pdman,
const GrShaderCaps&,
const GrGeometryProcessor& geomProc) override {
const GrFillPathShader& shader = geomProc.cast<GrFillPathShader>();
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.fColor;
pdman.set4f(fColorUniform, color.fR, color.fG, color.fB, color.fA);
}
GrGLSLUniformHandler::UniformHandle fAffineMatrixUniform;
GrGLSLUniformHandler::UniformHandle fTranslateUniform;
GrGLSLUniformHandler::UniformHandle fColorUniform;
};
GrGLSLGeometryProcessor* GrFillPathShader::createGLSLInstance(const GrShaderCaps&) const {
return new Impl;
}
void GrFillTriangleShader::emitVertexCode(Impl*, GrGLSLVertexBuilder* v,
GrGLSLUniformHandler* uniformHandler) const {
v->codeAppend(R"(
localcoord = input_point;
vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
}
void GrFillCubicHullShader::emitVertexCode(Impl*, GrGLSLVertexBuilder* v,
GrGLSLUniformHandler* uniformHandler) const {
v->codeAppend(R"(
float4x2 P = float4x2(input_points_0_1, input_points_2_3);
if (isinf(P[3].y)) { // Is the curve a conic?
float w = P[3].x;
if (isinf(w)) {
// A conic with w=Inf is an exact triangle.
P = float4x2(P[0], P[1], P[2], P[2]);
} else {
// Convert the control points to a trapeziodal hull that circumcscribes the conic.
float2 p1w = P[1] * w;
float T = .51; // Bias outward a bit to ensure we cover the outermost samples.
float2 c1 = mix(P[0], p1w, T);
float2 c2 = mix(P[2], p1w, T);
float iw = 1 / mix(1, w, T);
P = float4x2(P[0], c1 * iw, c2 * iw, P[2]);
}
}
// Translate the points to v0..3 where v0=0.
float2 v1 = P[1] - P[0], v2 = P[2] - P[0], v3 = P[3] - P[0];
// Reorder the points so v2 bisects v1 and v3.
if (sign(determinant(float2x2(v2,v1))) == sign(determinant(float2x2(v2,v3)))) {
float2 tmp = P[2];
if (sign(determinant(float2x2(v1,v2))) != sign(determinant(float2x2(v1,v3)))) {
P[2] = P[1]; // swap(P2, P1)
P[1] = tmp;
} else {
P[2] = P[3]; // swap(P2, P3)
P[3] = tmp;
}
}
// sk_VertexID comes in fan order. Convert to strip order.
int vertexidx = sk_VertexID;
vertexidx ^= vertexidx >> 1;
// Find the "turn direction" of each corner and net turn direction.
float vertexdir = 0;
float netdir = 0;
for (int i = 0; i < 4; ++i) {
float2 prev = P[i] - P[(i + 3) & 3], next = P[(i + 1) & 3] - P[i];
float dir = sign(determinant(float2x2(prev, next)));
if (i == vertexidx) {
vertexdir = dir;
}
netdir += dir;
}
// Remove the non-convex vertex, if any.
if (vertexdir != sign(netdir)) {
vertexidx = (vertexidx + 1) & 3;
}
localcoord = P[vertexidx];
vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
}
void GrFillBoundingBoxShader::emitVertexCode(Impl* impl, GrGLSLVertexBuilder* v,
GrGLSLUniformHandler* uniformHandler) const {
v->codeAppendf(R"(
// Bloat the bounding box by 1/4px to avoid potential T-junctions at the edges.
float2x2 M_ = inverse(AFFINE_MATRIX);
float2 bloat = float2(abs(M_[0]) + abs(M_[1])) * .25;
// Find the vertex position.
float2 T = float2(sk_VertexID & 1, sk_VertexID >> 1);
localcoord = mix(pathBounds.xy - bloat, pathBounds.zw + bloat, T);
vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
}

View File

@ -1,117 +0,0 @@
/*
* Copyright 2020 Google LLC.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrFillPathShader_DEFINED
#define GrFillPathShader_DEFINED
#include "src/gpu/tessellate/GrPathShader.h"
class GrGLSLUniformHandler;
class GrGLSLVertexBuilder;
// This is the base class for shaders that fill a path's pixels in the final render target.
class GrFillPathShader : public GrPathShader {
public:
GrFillPathShader(ClassID classID, const SkMatrix& viewMatrix, SkPMColor4f color,
GrPrimitiveType primitiveType)
: GrPathShader(classID, viewMatrix, primitiveType, 0)
, fColor(color) {
}
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
static const GrPipeline* MakeFillPassPipeline(const GrPathShader::ProgramArgs& args,
GrAAType aaType, GrAppliedClip&& appliedClip,
GrProcessorSet&& processors) {
auto pipelineFlags = GrPipeline::InputFlags::kNone;
if (aaType != GrAAType::kNone) {
pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
}
return GrSimpleMeshDrawOpHelper::CreatePipeline(
args.fCaps, args.fArena, args.fWriteView.swizzle(), std::move(appliedClip),
*args.fDstProxyView, std::move(processors), pipelineFlags);
}
// Allows non-zero stencil values to pass and write a color, and resets the stencil value back
// to zero; discards immediately on stencil values of zero.
static const GrUserStencilSettings* TestAndResetStencilSettings() {
constexpr static GrUserStencilSettings kTestAndResetStencil(
GrUserStencilSettings::StaticInit<
0x0000,
// No need to check the clip because the previous stencil pass will have only
// written to samples already inside the clip.
GrUserStencilTest::kNotEqual,
0xffff,
GrUserStencilOp::kZero,
GrUserStencilOp::kKeep,
0xffff>());
return &kTestAndResetStencil;
}
protected:
class Impl;
virtual void emitVertexCode(Impl*, GrGLSLVertexBuilder*, GrGLSLUniformHandler*) const = 0;
private:
const SkPMColor4f fColor;
};
// Fills a simple array of triangles.
class GrFillTriangleShader : public GrFillPathShader {
public:
GrFillTriangleShader(const SkMatrix& viewMatrix, SkPMColor4f color)
: GrFillPathShader(kTessellate_GrFillTriangleShader_ClassID, viewMatrix, color,
GrPrimitiveType::kTriangles) {
static constexpr Attribute kPtAttrib = {
"input_point", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
this->setVertexAttributes(&kPtAttrib, 1);
}
private:
const char* name() const override { return "GrFillTriangleShader"; }
void emitVertexCode(Impl*, GrGLSLVertexBuilder*, GrGLSLUniformHandler*) const override;
};
// Fills an array of convex hulls surrounding 4-point cubic instances.
class GrFillCubicHullShader : public GrFillPathShader {
public:
GrFillCubicHullShader(const SkMatrix& viewMatrix, SkPMColor4f color)
: GrFillPathShader(kTessellate_GrFillCubicHullShader_ClassID, viewMatrix, color,
GrPrimitiveType::kTriangleStrip) {
static constexpr Attribute kPtsAttribs[] = {
{"input_points_0_1", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
{"input_points_2_3", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
this->setInstanceAttributes(kPtsAttribs, SK_ARRAY_COUNT(kPtsAttribs));
}
private:
const char* name() const override { return "GrFillCubicHullShader"; }
void emitVertexCode(Impl*, GrGLSLVertexBuilder*, GrGLSLUniformHandler*) const override;
};
// Fills a path's bounding box, with subpixel outset to avoid possible T-junctions with extreme
// edges of the path.
// NOTE: The emitted geometry may not be axis-aligned, depending on the view matrix.
class GrFillBoundingBoxShader : public GrFillPathShader {
public:
GrFillBoundingBoxShader(const SkMatrix& viewMatrix, SkPMColor4f color, const SkRect& pathBounds)
: GrFillPathShader(kTessellate_GrFillBoundingBoxShader_ClassID, viewMatrix, color,
GrPrimitiveType::kTriangleStrip) {
constexpr static Attribute kPathBoundsAttrib = {"pathBounds", kFloat4_GrVertexAttribType,
kFloat4_GrSLType};
this->setInstanceAttributes(&kPathBoundsAttrib, 1);
}
private:
const char* name() const override { return "GrFillBoundingBoxShader"; }
void emitVertexCode(Impl*, GrGLSLVertexBuilder*, GrGLSLUniformHandler*) const override;
};
#endif

View File

@ -11,13 +11,102 @@
#include "src/gpu/GrInnerFanTriangulator.h"
#include "src/gpu/GrOpFlushState.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/tessellate/GrFillPathShader.h"
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
#include "src/gpu/tessellate/GrPathTessellationShader.h"
#include "src/gpu/tessellate/GrPathTessellator.h"
#include "src/gpu/tessellate/GrStencilPathShader.h"
#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
using OpFlags = GrTessellationPathRenderer::OpFlags;
namespace {
// Fills an array of convex hulls surrounding 4-point cubic or conic instances. This shader is used
// for the "fill" pass after the curves have been fully stencilled.
class HullShader : public GrPathTessellationShader {
public:
HullShader(const SkMatrix& viewMatrix, SkPMColor4f color)
: GrPathTessellationShader(kTessellate_HullShader_ClassID,
GrPrimitiveType::kTriangleStrip, 0, viewMatrix, color) {
constexpr static Attribute kPtsAttribs[] = {
{"input_points_0_1", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
{"input_points_2_3", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
this->setInstanceAttributes(kPtsAttribs, SK_ARRAY_COUNT(kPtsAttribs));
}
private:
const char* name() const final { return "HullShader"; }
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
};
GrGLSLGeometryProcessor* HullShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
v->codeAppend(R"(
float4x2 P = float4x2(input_points_0_1, input_points_2_3);
if (isinf(P[3].y)) { // Is the curve a conic?
float w = P[3].x;
if (isinf(w)) {
// A conic with w=Inf is an exact triangle.
P = float4x2(P[0], P[1], P[2], P[2]);
} else {
// Convert the points to a trapeziodal hull that circumcscribes the conic.
float2 p1w = P[1] * w;
float T = .51; // Bias outward a bit to ensure we cover the outermost samples.
float2 c1 = mix(P[0], p1w, T);
float2 c2 = mix(P[2], p1w, T);
float iw = 1 / mix(1, w, T);
P = float4x2(P[0], c1 * iw, c2 * iw, P[2]);
}
}
// Translate the points to v0..3 where v0=0.
float2 v1 = P[1] - P[0], v2 = P[2] - P[0], v3 = P[3] - P[0];
// Reorder the points so v2 bisects v1 and v3.
if (sign(determinant(float2x2(v2,v1))) == sign(determinant(float2x2(v2,v3)))) {
float2 tmp = P[2];
if (sign(determinant(float2x2(v1,v2))) != sign(determinant(float2x2(v1,v3)))) {
P[2] = P[1]; // swap(P2, P1)
P[1] = tmp;
} else {
P[2] = P[3]; // swap(P2, P3)
P[3] = tmp;
}
}
// sk_VertexID comes in fan order. Convert to strip order.
int vertexidx = sk_VertexID;
vertexidx ^= vertexidx >> 1;
// Find the "turn direction" of each corner and net turn direction.
float vertexdir = 0;
float netdir = 0;
for (int i = 0; i < 4; ++i) {
float2 prev = P[i] - P[(i + 3) & 3], next = P[(i + 1) & 3] - P[i];
float dir = sign(determinant(float2x2(prev, next)));
if (i == vertexidx) {
vertexdir = dir;
}
netdir += dir;
}
// Remove the non-convex vertex, if any.
if (vertexdir != sign(netdir)) {
vertexidx = (vertexidx + 1) & 3;
}
float2 localcoord = P[vertexidx];
float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
}
};
return new Impl;
}
} // namespace
void GrPathInnerTriangulateOp::visitProxies(const VisitProxyFunc& fn) const {
if (fPipelineForFills) {
fPipelineForFills->visitProxies(fn);
@ -41,22 +130,23 @@ GrProcessorSet::Analysis GrPathInnerTriangulateOp::finalize(const GrCaps& caps,
clampType, &fColor);
}
void GrPathInnerTriangulateOp::pushFanStencilProgram(const GrPathShader::ProgramArgs& args,
void GrPathInnerTriangulateOp::pushFanStencilProgram(const GrTessellationShader::ProgramArgs& args,
const GrPipeline* pipelineForStencils,
const GrUserStencilSettings* stencil) {
SkASSERT(pipelineForStencils);
fFanPrograms.push_back(GrStencilPathShader::MakeStencilProgram<GrStencilTriangleShader>(
args, fViewMatrix, pipelineForStencils, stencil));
}
auto shader = args.fArena->make<GrTriangleShader>(fViewMatrix, SK_PMColor4fTRANSPARENT);
fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, pipelineForStencils,
stencil)); }
void GrPathInnerTriangulateOp::pushFanFillProgram(const GrPathShader::ProgramArgs& args,
void GrPathInnerTriangulateOp::pushFanFillProgram(const GrTessellationShader::ProgramArgs& args,
const GrUserStencilSettings* stencil) {
SkASSERT(fPipelineForFills);
auto* shader = args.fArena->make<GrFillTriangleShader>(fViewMatrix, fColor);
fFanPrograms.push_back(GrPathShader::MakeProgram(args, shader, fPipelineForFills, stencil));
auto* shader = args.fArena->make<GrTriangleShader>(fViewMatrix, fColor);
fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, fPipelineForFills,
stencil));
}
void GrPathInnerTriangulateOp::prePreparePrograms(const GrPathShader::ProgramArgs& args,
void GrPathInnerTriangulateOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
GrAppliedClip&& appliedClip) {
SkASSERT(!fFanTriangulator);
SkASSERT(!fFanPolys);
@ -82,15 +172,15 @@ void GrPathInnerTriangulateOp::prePreparePrograms(const GrPathShader::ProgramArg
// Create a pipeline for stencil passes if needed.
const GrPipeline* pipelineForStencils = nullptr;
if (forceRedbookStencilPass || !isLinear) { // Curves always get stencilled.
pipelineForStencils = GrStencilPathShader::MakeStencilPassPipeline(args, fAAType, fOpFlags,
appliedClip.hardClip());
pipelineForStencils = GrPathTessellationShader::MakeStencilOnlyPipeline(
args, fAAType, fOpFlags, appliedClip.hardClip());
}
// Create a pipeline for fill passes if needed.
if (doFill) {
fPipelineForFills = GrFillPathShader::MakeFillPassPipeline(args, fAAType,
std::move(appliedClip),
std::move(fProcessors));
fPipelineForFills = GrTessellationShader::MakePipeline(args, fAAType,
std::move(appliedClip),
std::move(fProcessors));
}
// Pass 1: Tessellate the outer curves into the stencil buffer.
@ -99,11 +189,14 @@ void GrPathInnerTriangulateOp::prePreparePrograms(const GrPathShader::ProgramArg
// and the middle-out topology used by indirect draws is easier on the rasterizer than what
// we can do with hw tessellation. So far we haven't found any platforms where trying to use
// hw tessellation here is worth it.
fTessellator = GrPathTessellator::Make(args.fArena, fViewMatrix, fPath,
fTessellator = GrPathTessellator::Make(args.fArena, fPath, fViewMatrix,
SK_PMColor4fTRANSPARENT,
GrPathTessellator::DrawInnerFan::kNo, *args.fCaps);
fStencilCurvesProgram = GrPathShader::MakeProgram(
args, fTessellator->shader(), pipelineForStencils,
GrStencilPathShader::StencilPassSettings(fPath.getFillType()));
const GrUserStencilSettings* stencilPathSettings =
GrPathTessellationShader::StencilPathSettings(fPath.getFillType());
fStencilCurvesProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(),
pipelineForStencils,
stencilPathSettings);
}
// Pass 2: Fill the path's inner fan with a stencil test against the curves.
@ -111,11 +204,12 @@ void GrPathInnerTriangulateOp::prePreparePrograms(const GrPathShader::ProgramArg
if (forceRedbookStencilPass) {
// Use a standard Redbook "stencil then fill" algorithm instead of bypassing the stencil
// buffer to fill the fan directly.
this->pushFanStencilProgram(
args, pipelineForStencils,
GrStencilPathShader::StencilPassSettings(fPath.getFillType()));
const GrUserStencilSettings* stencilPathSettings =
GrPathTessellationShader::StencilPathSettings(fPath.getFillType());
this->pushFanStencilProgram(args, pipelineForStencils, stencilPathSettings);
if (doFill) {
this->pushFanFillProgram(args, GrFillPathShader::TestAndResetStencilSettings());
this->pushFanFillProgram(args,
GrPathTessellationShader::TestAndResetStencilSettings());
}
} else if (isLinear) {
// There are no outer curves! Ignore stencil and fill the path directly.
@ -204,10 +298,10 @@ void GrPathInnerTriangulateOp::prePreparePrograms(const GrPathShader::ProgramArg
// by curves. We issue a final cover pass over the curves by drawing their convex hulls.
// This will fill in any remaining samples and reset the stencil values back to zero.
SkASSERT(fTessellator);
auto* hullShader = args.fArena->make<GrFillCubicHullShader>(fViewMatrix, fColor);
fFillHullsProgram = GrPathShader::MakeProgram(
auto* hullShader = args.fArena->make<HullShader>(fViewMatrix, fColor);
fFillHullsProgram = GrTessellationShader::MakeProgram(
args, hullShader, fPipelineForFills,
GrFillPathShader::TestAndResetStencilSettings());
GrPathTessellationShader::TestAndResetStencilSettings());
}
}

View File

@ -10,7 +10,8 @@
#include "src/gpu/GrInnerFanTriangulator.h"
#include "src/gpu/ops/GrDrawOp.h"
#include "src/gpu/tessellate/GrPathShader.h"
#include "src/gpu/tessellate/GrTessellationShader.h"
#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
class GrPathTessellator;
@ -46,10 +47,10 @@ private:
GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override;
// These calls set up the stencil & fill programs we will use prior to preparing and executing.
void pushFanStencilProgram(const GrPathShader::ProgramArgs&,
void pushFanStencilProgram(const GrTessellationShader::ProgramArgs&,
const GrPipeline* pipelineForStencils, const GrUserStencilSettings*);
void pushFanFillProgram(const GrPathShader::ProgramArgs&, const GrUserStencilSettings*);
void prePreparePrograms(const GrPathShader::ProgramArgs&, GrAppliedClip&&);
void pushFanFillProgram(const GrTessellationShader::ProgramArgs&, const GrUserStencilSettings*);
void prePreparePrograms(const GrTessellationShader::ProgramArgs&, GrAppliedClip&&);
void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*,
const GrXferProcessor::DstProxyView&, GrXferBarrierFlags,

View File

@ -10,14 +10,56 @@
#include "src/gpu/GrEagerVertexAllocator.h"
#include "src/gpu/GrOpFlushState.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/tessellate/GrFillPathShader.h"
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
#include "src/gpu/tessellate/GrPathTessellationShader.h"
#include "src/gpu/tessellate/GrPathTessellator.h"
#include "src/gpu/tessellate/GrStencilPathShader.h"
#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
using OpFlags = GrTessellationPathRenderer::OpFlags;
namespace {
// Fills a path's bounding box, with subpixel outset to avoid possible T-junctions with extreme
// edges of the path.
// NOTE: The emitted geometry may not be axis-aligned, depending on the view matrix.
class BoundingBoxShader : public GrPathTessellationShader {
public:
BoundingBoxShader(const SkMatrix& viewMatrix, SkPMColor4f color)
: GrPathTessellationShader(kTessellate_BoundingBoxShader_ClassID,
GrPrimitiveType::kTriangleStrip, 0, viewMatrix, color) {
constexpr static Attribute kPathBoundsAttrib = {"pathBounds", kFloat4_GrVertexAttribType,
kFloat4_GrSLType};
this->setInstanceAttributes(&kPathBoundsAttrib, 1);
}
private:
const char* name() const final { return "BoundingBoxShader"; }
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
};
GrGLSLGeometryProcessor* BoundingBoxShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
v->codeAppend(R"(
// Bloat the bounding box by 1/4px to avoid potential T-junctions at the edges.
float2x2 M_ = inverse(AFFINE_MATRIX);
float2 bloat = float2(abs(M_[0]) + abs(M_[1])) * .25;
// Find the vertex position.
float2 T = float2(sk_VertexID & 1, sk_VertexID >> 1);
float2 localcoord = mix(pathBounds.xy - bloat, pathBounds.zw + bloat, T);
float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
}
};
return new Impl;
}
} // namespace
void GrPathStencilFillOp::visitProxies(const VisitProxyFunc& fn) const {
if (fFillBBoxProgram) {
fFillBBoxProgram->pipeline().visitProxies(fn);
@ -41,7 +83,7 @@ GrProcessorSet::Analysis GrPathStencilFillOp::finalize(const GrCaps& caps,
clampType, &fColor);
}
void GrPathStencilFillOp::prePreparePrograms(const GrPathShader::ProgramArgs& args,
void GrPathStencilFillOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
GrAppliedClip&& appliedClip) {
SkASSERT(!fTessellator);
SkASSERT(!fStencilFanProgram);
@ -52,41 +94,45 @@ void GrPathStencilFillOp::prePreparePrograms(const GrPathShader::ProgramArgs& ar
return;
}
const GrPipeline* stencilPassPipeline = GrStencilPathShader::MakeStencilPassPipeline(
const GrPipeline* stencilPipeline = GrPathTessellationShader::MakeStencilOnlyPipeline(
args, fAAType, fOpFlags, appliedClip.hardClip());
const GrUserStencilSettings* stencilPathSettings =
GrPathTessellationShader::StencilPathSettings(fPath.getFillType());
if ((fOpFlags & OpFlags::kPreferWedges) && args.fCaps->shaderCaps()->tessellationSupport()) {
// The path is an atlas with relatively small contours, or something else that does best
// with wedges.
fTessellator = GrPathWedgeTessellator::Make(args.fArena, fViewMatrix);
fTessellator = GrPathWedgeTessellator::Make(args.fArena, fViewMatrix,
SK_PMColor4fTRANSPARENT);
} else {
auto drawFanWithTessellator = GrPathTessellator::DrawInnerFan::kYes;
if (fPath.countVerbs() > 50 && this->bounds().height() * this->bounds().width() > 256*256) {
// Large complex paths do better with a dedicated triangle shader for the inner fan.
// This takes less PCI bus bandwidth (6 floats per triangle instead of 8) and allows us
// to make sure it has an efficient middle-out topology.
fStencilFanProgram = GrStencilPathShader::MakeStencilProgram<GrStencilTriangleShader>(
args, fViewMatrix, stencilPassPipeline, fPath.getFillType());
auto shader = args.fArena->make<GrTriangleShader>(fViewMatrix, SK_PMColor4fTRANSPARENT);
fStencilFanProgram = GrTessellationShader::MakeProgram(args, shader, stencilPipeline,
stencilPathSettings);
drawFanWithTessellator = GrPathTessellator::DrawInnerFan::kNo;
}
fTessellator = GrPathTessellator::Make(args.fArena, fViewMatrix, fPath,
drawFanWithTessellator, *args.fCaps);
fTessellator = GrPathTessellator::Make(args.fArena, fPath, fViewMatrix,
SK_PMColor4fTRANSPARENT, drawFanWithTessellator,
*args.fCaps);
}
fStencilPathProgram = GrPathShader::MakeProgram(
args, fTessellator->shader(), stencilPassPipeline,
GrStencilPathShader::StencilPassSettings(fPath.getFillType()));
fStencilPathProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(),
stencilPipeline, stencilPathSettings);
if (!(fOpFlags & OpFlags::kStencilOnly)) {
// Create a program that draws a bounding box over the path and fills its stencil coverage
// into the color buffer.
auto* bboxShader = args.fArena->make<GrFillBoundingBoxShader>(fViewMatrix, fColor,
fPath.getBounds());
auto* bboxPipeline = GrFillPathShader::MakeFillPassPipeline(args, fAAType,
std::move(appliedClip),
std::move(fProcessors));
auto* bboxStencil = GrFillPathShader::TestAndResetStencilSettings();
fFillBBoxProgram = GrPathShader::MakeProgram(args, bboxShader, bboxPipeline, bboxStencil);
auto* bboxShader = args.fArena->make<BoundingBoxShader>(fViewMatrix, fColor);
auto* bboxPipeline = GrTessellationShader::MakePipeline(args, fAAType,
std::move(appliedClip),
std::move(fProcessors));
auto* bboxStencil = GrPathTessellationShader::TestAndResetStencilSettings();
fFillBBoxProgram = GrTessellationShader::MakeProgram(args, bboxShader, bboxPipeline,
bboxStencil);
}
}

View File

@ -9,8 +9,8 @@
#define GrPathStencilFillOp_DEFINED
#include "src/gpu/ops/GrDrawOp.h"
#include "src/gpu/tessellate/GrPathShader.h"
#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
#include "src/gpu/tessellate/GrTessellationShader.h"
class GrPathTessellator;
@ -41,7 +41,7 @@ private:
// Chooses the rendering method we will use and creates the corresponding tessellator and
// stencil/fill programs.
void prePreparePrograms(const GrPathShader::ProgramArgs&, GrAppliedClip&& clip);
void prePreparePrograms(const GrTessellationShader::ProgramArgs&, GrAppliedClip&& clip);
void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*,
const GrXferProcessor::DstProxyView&, GrXferBarrierFlags,

View File

@ -5,13 +5,47 @@
* found in the LICENSE file.
*/
#include "src/gpu/tessellate/GrStencilPathShader.h"
#include "src/gpu/tessellate/GrPathTessellationShader.h"
#include "src/gpu/geometry/GrWangsFormula.h"
#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
#include "src/gpu/glsl/GrGLSLVarying.h"
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
void GrPathTessellationShader::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
args.fVaryingHandler->emitAttributes(args.fGeomProc);
// Vertex shader.
const char* affineMatrix, *translate;
fAffineMatrixUniform = args.fUniformHandler->addUniform(nullptr, kVertex_GrShaderFlag,
kFloat4_GrSLType, "affineMatrix",
&affineMatrix);
fTranslateUniform = args.fUniformHandler->addUniform(nullptr, kVertex_GrShaderFlag,
kFloat2_GrSLType, "translate", &translate);
args.fVertBuilder->codeAppendf("float2x2 AFFINE_MATRIX = float2x2(%s);", affineMatrix);
args.fVertBuilder->codeAppendf("float2 TRANSLATE = %s;", translate);
this->emitVertexCode(args.fVertBuilder, 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);
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 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);
}
constexpr static char kSkSLTypeDefs[] = R"(
#define float4x3 mat4x3
#define float2 vec2
@ -50,55 +84,27 @@ float2 eval_rational_cubic(float4x3 P, float T) {
return abcd.xy / abcd.z;
})";
class GrStencilPathShader::Impl : public GrGLSLGeometryProcessor {
protected:
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
const auto& shader = args.fGeomProc.cast<GrStencilPathShader>();
args.fVaryingHandler->emitAttributes(shader);
auto v = args.fVertBuilder;
const char* affineMatrix, *translate;
fAffineMatrixUniform = args.fUniformHandler->addUniform(
nullptr, kVertex_GrShaderFlag, kFloat4_GrSLType, "affineMatrix", &affineMatrix);
fTranslateUniform = args.fUniformHandler->addUniform(
nullptr, kVertex_GrShaderFlag, kFloat2_GrSLType, "translate", &translate);
v->codeAppendf("float2 vertexpos = float2x2(%s) * inputPoint + %s;",
affineMatrix, translate);
if (shader.willUseTessellationShaders()) {
// If y is infinity then x is a conic weight. Don't transform.
v->codeAppendf("vertexpos = (isinf(inputPoint.y)) ? inputPoint : vertexpos;");
}
if (!shader.willUseTessellationShaders()) { // This is the case for the triangle shader.
GrGLSLGeometryProcessor* GrTriangleShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
v->codeAppend(R"(
float2 localcoord = inputPoint;
float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
} else {
v->declareGlobal(GrShaderVar("P", kFloat2_GrSLType, GrShaderVar::TypeModifier::Out));
v->codeAppendf("P = %s;", "vertexpos");
}
// The fragment shader is normally disabled, but output fully opaque white.
args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputColor);
args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
}
void setData(const GrGLSLProgramDataManager& pdman,
const GrShaderCaps&,
const GrGeometryProcessor& geomProc) override {
const SkMatrix& m = geomProc.cast<GrStencilPathShader>().viewMatrix();
pdman.set4f(fAffineMatrixUniform, m.getScaleX(), m.getSkewY(), m.getSkewX(), m.getScaleY());
pdman.set2f(fTranslateUniform, m.getTranslateX(), m.getTranslateY());
}
GrGLSLUniformHandler::UniformHandle fAffineMatrixUniform;
GrGLSLUniformHandler::UniformHandle fTranslateUniform;
};
GrGLSLGeometryProcessor* GrStencilPathShader::createGLSLInstance(const GrShaderCaps&) const {
};
return new Impl;
}
GrGLSLGeometryProcessor* GrCurveTessellateShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrStencilPathShader::Impl {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(GrGLSLVertexBuilder* v, 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.
P = (isinf(inputPoint.y)) ? inputPoint : AFFINE_MATRIX * inputPoint + TRANSLATE;)");
}
SkString getTessControlShaderGLSL(const GrGeometryProcessor&,
const char* versionAndExtensionDecls,
const GrGLSLUniformHandler&,
@ -178,7 +184,6 @@ GrGLSLGeometryProcessor* GrCurveTessellateShader::createGLSLInstance(const GrSha
return code;
}
SkString getTessEvaluationShaderGLSL(const GrGeometryProcessor&,
const char* versionAndExtensionDecls,
const GrGLSLUniformHandler&,
@ -226,12 +231,17 @@ GrGLSLGeometryProcessor* GrCurveTessellateShader::createGLSLInstance(const GrSha
return code;
}
};
return new Impl;
}
GrGLSLGeometryProcessor* GrWedgeTessellateShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrStencilPathShader::Impl {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(GrGLSLVertexBuilder* v, 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.
P = (isinf(inputPoint.y)) ? inputPoint : AFFINE_MATRIX * inputPoint + TRANSLATE;)");
}
SkString getTessControlShaderGLSL(const GrGeometryProcessor&,
const char* versionAndExtensionDecls,
const GrGLSLUniformHandler&,
@ -284,7 +294,6 @@ GrGLSLGeometryProcessor* GrWedgeTessellateShader::createGLSLInstance(const GrSha
return code;
}
SkString getTessEvaluationShaderGLSL(const GrGeometryProcessor&,
const char* versionAndExtensionDecls,
const GrGLSLUniformHandler&,
@ -328,19 +337,16 @@ GrGLSLGeometryProcessor* GrWedgeTessellateShader::createGLSLInstance(const GrSha
return code;
}
};
return new Impl;
}
class GrCurveMiddleOutShader::Impl : public GrStencilPathShader::Impl {
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
const auto& shader = args.fGeomProc.cast<GrCurveMiddleOutShader>();
args.fVaryingHandler->emitAttributes(shader);
args.fVertBuilder->insertFunction(kUnpackRationalCubicFn);
args.fVertBuilder->insertFunction(kEvalRationalCubicFn);
if (args.fShaderCaps->bitManipulationSupport()) {
class GrCurveMiddleOutShader::Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
v->insertFunction(kUnpackRationalCubicFn);
v->insertFunction(kEvalRationalCubicFn);
if (v->getProgramBuilder()->shaderCaps()->bitManipulationSupport()) {
// Determines the T value at which to place the given vertex in a "middle-out" topology.
args.fVertBuilder->insertFunction(R"(
v->insertFunction(R"(
float find_middle_out_T() {
int totalTriangleIdx = sk_VertexID/3 + 1;
int depth = findMSB(totalTriangleIdx);
@ -351,7 +357,7 @@ class GrCurveMiddleOutShader::Impl : public GrStencilPathShader::Impl {
})");
} else {
// Determines the T value at which to place the given vertex in a "middle-out" topology.
args.fVertBuilder->insertFunction(R"(
v->insertFunction(R"(
float find_middle_out_T() {
float totalTriangleIdx = float(sk_VertexID/3) + 1;
float depth = floor(log2(totalTriangleIdx));
@ -361,31 +367,22 @@ class GrCurveMiddleOutShader::Impl : public GrStencilPathShader::Impl {
return vertexIdxWithinDepth * exp2(-1 - depth);
})");
}
args.fVertBuilder->codeAppend(R"(
float2 pos;
v->codeAppend(R"(
float2 localcoord;
if (isinf(inputPoints_2_3.z)) {
// A conic with w=Inf is an exact triangle.
pos = (sk_VertexID < 1) ? inputPoints_0_1.xy
: (sk_VertexID == 1) ? inputPoints_0_1.zw
: inputPoints_2_3.xy;
localcoord = (sk_VertexID < 1) ? inputPoints_0_1.xy
: (sk_VertexID == 1) ? inputPoints_0_1.zw
: inputPoints_2_3.xy;
} else {
float4x3 P = unpack_rational_cubic(inputPoints_0_1.xy, inputPoints_0_1.zw,
inputPoints_2_3.xy, inputPoints_2_3.zw);
float T = find_middle_out_T();
pos = eval_rational_cubic(P, T);
})");
const char* affineMatrix, *translate;
fAffineMatrixUniform = args.fUniformHandler->addUniform(
nullptr, kVertex_GrShaderFlag, kFloat4_GrSLType, "affineMatrix", &affineMatrix);
fTranslateUniform = args.fUniformHandler->addUniform(
nullptr, kVertex_GrShaderFlag, kFloat2_GrSLType, "translate", &translate);
args.fVertBuilder->codeAppendf(R"(
pos = float2x2(%s) * pos + %s;)", affineMatrix, translate);
gpArgs->fPositionVar.set(kFloat2_GrSLType, "pos");
// The fragment shader is normally disabled, but output fully opaque white.
args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputColor);
args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
localcoord = eval_rational_cubic(P, T);
}
float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
}
};

View File

@ -5,43 +5,25 @@
* found in the LICENSE file.
*/
#ifndef GrStencilPathShader_DEFINED
#define GrStencilPathShader_DEFINED
#ifndef GrPathTessellationShader_DEFINED
#define GrPathTessellationShader_DEFINED
#include "src/gpu/GrDrawIndirectCommand.h"
#include "src/gpu/tessellate/GrPathShader.h"
#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
#include "src/gpu/tessellate/GrTessellationShader.h"
// This is the base class for shaders that stencil path elements, namely, triangles, standalone
// cubics, and wedges.
class GrStencilPathShader : public GrPathShader {
// This is the base class for shaders in the GPU tessellator that fill paths.
class GrPathTessellationShader : public GrTessellationShader {
public:
GrStencilPathShader(ClassID classID, const SkMatrix& viewMatrix, GrPrimitiveType primitiveType,
int tessellationPatchVertexCount = 0)
: GrPathShader(classID, viewMatrix, primitiveType, tessellationPatchVertexCount) {
GrPathTessellationShader(ClassID classID, GrPrimitiveType primitiveType,
int tessellationPatchVertexCount, const SkMatrix& viewMatrix,
const SkPMColor4f& color)
: GrTessellationShader(classID, primitiveType, tessellationPatchVertexCount, viewMatrix,
color) {
}
// Creates a pipeline that can be used for normal Redbook stencil draws.
static const GrPipeline* MakeStencilPassPipeline(const GrPathShader::ProgramArgs& args,
GrAAType aaType,
GrTessellationPathRenderer::OpFlags opFlags,
const GrAppliedHardClip& hardClip) {
using OpFlags = GrTessellationPathRenderer::OpFlags;
GrPipeline::InitArgs pipelineArgs;
if (aaType != GrAAType::kNone) {
pipelineArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
}
if (args.fCaps->wireframeSupport() && (opFlags & OpFlags::kWireframe)) {
pipelineArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
}
pipelineArgs.fCaps = args.fCaps;
return args.fArena->make<GrPipeline>(pipelineArgs,
GrDisableColorXPFactory::MakeXferProcessor(),
hardClip);
}
// Returns the stencil settings to use for a standard Redbook stencil draw.
static const GrUserStencilSettings* StencilPassSettings(SkPathFillType fillType) {
// Returns the stencil settings to use for a standard Redbook "stencil" pass.
static const GrUserStencilSettings* StencilPathSettings(SkPathFillType fillType) {
// Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
constexpr static GrUserStencilSettings kIncrDecrStencil(
GrUserStencilSettings::StaticInitSeparate<
@ -66,69 +48,111 @@ public:
return (fillType == SkPathFillType::kWinding) ? &kIncrDecrStencil : &kInvertStencil;
}
template<typename ShaderType>
static GrProgramInfo* MakeStencilProgram(const ProgramArgs& args, const SkMatrix& viewMatrix,
const GrPipeline* pipeline,
const GrUserStencilSettings* stencil) {
const auto* shader = args.fArena->make<ShaderType>(viewMatrix);
return GrPathShader::MakeProgram(args, shader, pipeline, stencil);
// Returns the stencil settings to use for a standard Redbook "fill" pass. Allows non-zero
// stencil values to pass and write a color, and resets the stencil value back to zero; discards
// immediately on stencil values of zero.
static const GrUserStencilSettings* TestAndResetStencilSettings() {
constexpr static GrUserStencilSettings kTestAndResetStencil(
GrUserStencilSettings::StaticInit<
0x0000,
// No need to check the clip because the previous stencil pass will have only
// written to samples already inside the clip.
GrUserStencilTest::kNotEqual,
0xffff,
GrUserStencilOp::kZero,
GrUserStencilOp::kKeep,
0xffff>());
return &kTestAndResetStencil;
}
template<typename ShaderType>
static GrProgramInfo* MakeStencilProgram(const ProgramArgs& args, const SkMatrix& viewMatrix,
const GrPipeline* pipeline,
const SkPathFillType fillType) {
return MakeStencilProgram<ShaderType>(args, viewMatrix, pipeline,
StencilPassSettings(fillType));
// Creates a pipeline that does not write to the color buffer.
static const GrPipeline* MakeStencilOnlyPipeline(const ProgramArgs& args, GrAAType aaType,
GrTessellationPathRenderer::OpFlags opFlags,
const GrAppliedHardClip& hardClip) {
using OpFlags = GrTessellationPathRenderer::OpFlags;
GrPipeline::InitArgs pipelineArgs;
if (aaType == GrAAType::kMSAA) {
pipelineArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
}
if (args.fCaps->wireframeSupport() && (opFlags & OpFlags::kWireframe)) {
pipelineArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
}
pipelineArgs.fCaps = args.fCaps;
return args.fArena->make<GrPipeline>(pipelineArgs,
GrDisableColorXPFactory::MakeXferProcessor(),
hardClip);
}
protected:
constexpr static Attribute kSinglePointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
kFloat2_GrSLType};
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {}
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override;
// Default path tessellation shader implementation that manages a uniform matrix and color.
class Impl : public GrGLSLGeometryProcessor {
public:
void onEmitCode(EmitArgs&, GrGPArgs*) final;
void setData(const GrGLSLProgramDataManager&, const GrShaderCaps&,
const GrGeometryProcessor&) override;
class Impl;
protected:
virtual void emitVertexCode(GrGLSLVertexBuilder*, GrGPArgs*) = 0;
GrGLSLUniformHandler::UniformHandle fAffineMatrixUniform;
GrGLSLUniformHandler::UniformHandle fTranslateUniform;
GrGLSLUniformHandler::UniformHandle fColorUniform;
};
};
// Draws simple triangles to the stencil buffer.
class GrStencilTriangleShader : public GrStencilPathShader {
// Draws a simple array of triangles.
class GrTriangleShader : public GrPathTessellationShader {
public:
GrStencilTriangleShader(const SkMatrix& viewMatrix) : GrStencilPathShader(
kTessellate_GrStencilTriangleShader_ClassID, viewMatrix, GrPrimitiveType::kTriangles) {
this->setVertexAttributes(&kSinglePointAttrib, 1);
GrTriangleShader(const SkMatrix& viewMatrix, SkPMColor4f color)
: GrPathTessellationShader(kTessellate_GrTriangleShader_ClassID,
GrPrimitiveType::kTriangles, 0, viewMatrix, color) {
constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
kFloat2_GrSLType};
this->setVertexAttributes(&kInputPointAttrib, 1);
}
const char* name() const override { return "tessellate_GrStencilTriangleShader"; }
private:
const char* name() const final { return "tessellate_GrTriangleShader"; }
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
};
// 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 GrCurveTessellateShader : public GrStencilPathShader {
class GrCurveTessellateShader : public GrPathTessellationShader {
public:
GrCurveTessellateShader(const SkMatrix& viewMatrix) : GrStencilPathShader(
kTessellate_GrCurveTessellateShader_ClassID, viewMatrix, GrPrimitiveType::kPatches, 4) {
this->setVertexAttributes(&kSinglePointAttrib, 1);
GrCurveTessellateShader(const SkMatrix& viewMatrix, const SkPMColor4f& color)
: GrPathTessellationShader(kTessellate_GrCurveTessellateShader_ClassID,
GrPrimitiveType::kPatches, 4, viewMatrix, color) {
constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
kFloat2_GrSLType};
this->setVertexAttributes(&kInputPointAttrib, 1);
}
const char* name() const override { return "tessellate_GrCurveTessellateShader"; }
private:
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override;
const char* name() const final { return "tessellate_GrCurveTessellateShader"; }
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
};
// 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 GrWedgeTessellateShader : public GrStencilPathShader {
class GrWedgeTessellateShader : public GrPathTessellationShader {
public:
GrWedgeTessellateShader(const SkMatrix& viewMatrix) : GrStencilPathShader(
kTessellate_GrWedgeTessellateShader_ClassID, viewMatrix, GrPrimitiveType::kPatches, 5) {
this->setVertexAttributes(&kSinglePointAttrib, 1);
GrWedgeTessellateShader(const SkMatrix& viewMatrix, const SkPMColor4f& color)
: GrPathTessellationShader(kTessellate_GrWedgeTessellateShader_ClassID,
GrPrimitiveType::kPatches, 5, viewMatrix, color) {
constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
kFloat2_GrSLType};
this->setVertexAttributes(&kInputPointAttrib, 1);
}
const char* name() const override { return "tessellate_GrWedgeTessellateShader"; }
private:
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override;
const char* name() const final { return "tessellate_GrWedgeTessellateShader"; }
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
};
// Uses instanced draws to triangulate standalone closed curves with a "middle-out" topology.
@ -142,7 +166,7 @@ private:
// The caller may compute each cubic's resolveLevel on the CPU (i.e., the log2 number of line
// segments it will be divided into; see GrWangsFormula::cubic_log2/quadratic_log2/conic_log2), and
// then sort the instance buffer by resolveLevel for efficient batching of indirect draws.
class GrCurveMiddleOutShader : public GrStencilPathShader {
class GrCurveMiddleOutShader : public GrPathTessellationShader {
public:
// How many vertices do we need to draw in order to triangulate a cubic with 2^resolveLevel
// line segments?
@ -167,19 +191,19 @@ public:
indirectWriter->write(instanceCount, baseInstance, vertexCount, 0);
}
GrCurveMiddleOutShader(const SkMatrix& viewMatrix)
: GrStencilPathShader(kTessellate_GrCurveMiddleOutShader_ClassID, viewMatrix,
GrPrimitiveType::kTriangles) {
GrCurveMiddleOutShader(const SkMatrix& viewMatrix, const SkPMColor4f& color)
: GrPathTessellationShader(kTessellate_GrCurveMiddleOutShader_ClassID,
GrPrimitiveType::kTriangles, 0, viewMatrix, color) {
constexpr static Attribute kInputPtsAttribs[] = {
{"inputPoints_0_1", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
{"inputPoints_2_3", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
this->setInstanceAttributes(kInputPtsAttribs, 2);
}
const char* name() const override { return "tessellate_GrCurveMiddleOutShader"; }
private:
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override;
const char* name() const final { return "tessellate_GrCurveMiddleOutShader"; }
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
class Impl;
};

View File

@ -14,7 +14,7 @@
#include "src/gpu/tessellate/GrCullTest.h"
#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
#include "src/gpu/tessellate/GrMidpointContourParser.h"
#include "src/gpu/tessellate/GrStencilPathShader.h"
#include "src/gpu/tessellate/GrPathTessellationShader.h"
constexpr static float kPrecision = GrTessellationPathRenderer::kLinearizationPrecision;
@ -27,27 +27,29 @@ static bool can_use_hardware_tessellation(const SkPath& path, const GrCaps& caps
return path.countVerbs() >= caps.minPathVerbsForHwTessellation();
}
GrPathTessellator* GrPathTessellator::Make(SkArenaAlloc* arena, const SkMatrix& viewMatrix,
const SkPath& path, DrawInnerFan drawInnerFan,
const GrCaps& caps) {
GrPathTessellator* GrPathTessellator::Make(SkArenaAlloc* arena, const SkPath& path,
const SkMatrix& viewMatrix, const SkPMColor4f& color,
DrawInnerFan drawInnerFan, const GrCaps& caps) {
if (can_use_hardware_tessellation(path, caps)) {
if (drawInnerFan == DrawInnerFan::kNo) {
return GrPathOuterCurveTessellator::Make(arena, viewMatrix, drawInnerFan);
return GrPathOuterCurveTessellator::Make(arena, viewMatrix, color, drawInnerFan);
} else {
return GrPathWedgeTessellator::Make(arena, viewMatrix);
return GrPathWedgeTessellator::Make(arena, viewMatrix, color);
}
} else {
return GrPathIndirectTessellator::Make(arena, viewMatrix, path, drawInnerFan);
return GrPathIndirectTessellator::Make(arena, path, viewMatrix, color, drawInnerFan);
}
}
GrPathTessellator* GrPathIndirectTessellator::Make(SkArenaAlloc* arena, const SkMatrix& viewMatrix,
const SkPath& path, DrawInnerFan drawInnerFan) {
auto shader = arena->make<GrCurveMiddleOutShader>(viewMatrix);
GrPathTessellator* GrPathIndirectTessellator::Make(SkArenaAlloc* arena, const SkPath& path,
const SkMatrix& viewMatrix,
const SkPMColor4f& color,
DrawInnerFan drawInnerFan) {
auto shader = arena->make<GrCurveMiddleOutShader>(viewMatrix, color);
return arena->make<GrPathIndirectTessellator>(shader, path, drawInnerFan);
}
GrPathIndirectTessellator::GrPathIndirectTessellator(GrStencilPathShader* shader,
GrPathIndirectTessellator::GrPathIndirectTessellator(GrPathTessellationShader* shader,
const SkPath& path, DrawInnerFan drawInnerFan)
: GrPathTessellator(shader)
, fDrawInnerFan(drawInnerFan != DrawInnerFan::kNo) {
@ -243,7 +245,7 @@ void GrPathIndirectTessellator::prepare(GrMeshDrawOp::Target* target, const SkRe
instanceLocations[level].writeArray(pts, 4);
break;
case SkPathVerb::kConic:
GrPathShader::WriteConicPatch(pts, *w, &instanceLocations[level]);
GrTessellationShader::WriteConicPatch(pts, *w, &instanceLocations[level]);
break;
default:
SkUNREACHABLE;
@ -279,8 +281,9 @@ void GrPathIndirectTessellator::drawHullInstances(GrOpFlushState* flushState) co
GrPathTessellator* GrPathOuterCurveTessellator::Make(SkArenaAlloc* arena,
const SkMatrix& viewMatrix,
const SkPMColor4f& color,
DrawInnerFan drawInnerFan) {
auto shader = arena->make<GrCurveTessellateShader>(viewMatrix);
auto shader = arena->make<GrCurveTessellateShader>(viewMatrix, color);
return arena->make<GrPathOuterCurveTessellator>(shader, drawInnerFan);
}
@ -359,7 +362,7 @@ void GrPathOuterCurveTessellator::prepare(GrMeshDrawOp::Target* target, const Sk
return;
}
if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
GrPathShader::WriteConicPatch(p, w, &vertexWriter);
GrTessellationShader::WriteConicPatch(p, w, &vertexWriter);
}
}
@ -446,8 +449,9 @@ void GrPathOuterCurveTessellator::prepare(GrMeshDrawOp::Target* target, const Sk
}
}
GrPathTessellator* GrPathWedgeTessellator::Make(SkArenaAlloc* arena, const SkMatrix& viewMatrix) {
auto shader = arena->make<GrWedgeTessellateShader>(viewMatrix);
GrPathTessellator* GrPathWedgeTessellator::Make(SkArenaAlloc* arena, const SkMatrix& viewMatrix,
const SkPMColor4f& color) {
auto shader = arena->make<GrWedgeTessellateShader>(viewMatrix, color);
return arena->make<GrPathWedgeTessellator>(shader);
}
@ -506,7 +510,7 @@ void GrPathWedgeTessellator::prepare(GrMeshDrawOp::Target* target, const SkRect&
return;
}
if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
GrPathShader::WriteConicPatch(p, w, &vertexWriter);
GrTessellationShader::WriteConicPatch(p, w, &vertexWriter);
vertexWriter.write(midpoint);
}
}

View File

@ -14,7 +14,7 @@
#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
class SkPath;
class GrStencilPathShader;
class GrPathTessellationShader;
// Prepares GPU data for, and then draws a path's tessellated geometry. Depending on the subclass,
// the caller may or may not be required to draw the path's inner fan separately.
@ -30,10 +30,10 @@ public:
};
// Creates the tessellator best suited to draw the given path.
static GrPathTessellator* Make(SkArenaAlloc*, const SkMatrix&, const SkPath&, DrawInnerFan,
const GrCaps&);
static GrPathTessellator* Make(SkArenaAlloc*, const SkPath&, const SkMatrix&,
const SkPMColor4f&, DrawInnerFan, const GrCaps&);
const GrStencilPathShader* shader() const { return fShader; }
const GrPathTessellationShader* shader() const { return fShader; }
// Called before draw(). Prepares GPU buffers containing the geometry to tessellate. If the
// given BreadcrumbTriangleList is non-null, then this class will also include the breadcrumb
@ -53,9 +53,9 @@ public:
virtual ~GrPathTessellator() {}
protected:
GrPathTessellator(GrStencilPathShader* shader) : fShader(shader) {}
GrPathTessellator(GrPathTessellationShader* shader) : fShader(shader) {}
GrStencilPathShader* fShader;
GrPathTessellationShader* fShader;
};
// Draws tessellations of the path's outer curves and, optionally, inner fan triangles using
@ -64,7 +64,8 @@ protected:
// cubic or a conic.
class GrPathIndirectTessellator final : public GrPathTessellator {
public:
static GrPathTessellator* Make(SkArenaAlloc*, const SkMatrix&, const SkPath&, DrawInnerFan);
static GrPathTessellator* Make(SkArenaAlloc*, const SkPath&, const SkMatrix&,
const SkPMColor4f&, DrawInnerFan);
void prepare(GrMeshDrawOp::Target*, const SkRect& cullBounds, const SkPath&,
const BreadcrumbTriangleList*) override;
@ -74,7 +75,7 @@ public:
private:
constexpr static int kMaxResolveLevel = GrTessellationPathRenderer::kMaxResolveLevel;
GrPathIndirectTessellator(GrStencilPathShader*, const SkPath&, DrawInnerFan);
GrPathIndirectTessellator(GrPathTessellationShader*, const SkPath&, DrawInnerFan);
const bool fDrawInnerFan;
int fResolveLevelCounts[kMaxResolveLevel + 1] = {0};
@ -97,7 +98,7 @@ public:
void draw(GrOpFlushState*) const final;
protected:
GrPathHardwareTessellator(GrStencilPathShader* shader, int numVerticesPerPatch)
GrPathHardwareTessellator(GrPathTessellationShader* shader, int numVerticesPerPatch)
: GrPathTessellator(shader), fNumVerticesPerPatch(numVerticesPerPatch) {}
GrVertexChunkArray fVertexChunkArray;
@ -109,14 +110,15 @@ protected:
// or a conic. Quadratics are converted to cubics and triangles are converted to conics with w=Inf.
class GrPathOuterCurveTessellator final : public GrPathHardwareTessellator {
public:
static GrPathTessellator* Make(SkArenaAlloc*, const SkMatrix&, DrawInnerFan drawInnerFan);
static GrPathTessellator* Make(SkArenaAlloc*, const SkMatrix&, const SkPMColor4f&,
DrawInnerFan);
void prepare(GrMeshDrawOp::Target*, const SkRect& cullBounds, const SkPath&,
const BreadcrumbTriangleList*) override;
void drawHullInstances(GrOpFlushState*) const override;
private:
GrPathOuterCurveTessellator(GrStencilPathShader* shader, DrawInnerFan drawInnerFan)
GrPathOuterCurveTessellator(GrPathTessellationShader* shader, DrawInnerFan drawInnerFan)
: GrPathHardwareTessellator(shader, 4)
, fDrawInnerFan(drawInnerFan == DrawInnerFan::kYes) {}
@ -131,13 +133,14 @@ private:
// converted to cubics. Once stencilled, these wedges alone define the complete path.
class GrPathWedgeTessellator final : public GrPathHardwareTessellator {
public:
static GrPathTessellator* Make(SkArenaAlloc*, const SkMatrix&);
static GrPathTessellator* Make(SkArenaAlloc*, const SkMatrix&, const SkPMColor4f&);
void prepare(GrMeshDrawOp::Target*, const SkRect& cullBounds, const SkPath&,
const BreadcrumbTriangleList*) override;
private:
GrPathWedgeTessellator(GrStencilPathShader* shader) : GrPathHardwareTessellator(shader, 5) {}
GrPathWedgeTessellator(GrPathTessellationShader* shader)
: GrPathHardwareTessellator(shader, 5) {}
friend class SkArenaAlloc; // For constructor.
};

View File

@ -84,7 +84,7 @@ public:
return;
}
SkPoint conic[4];
GrPathShader::WriteConicPatch(p, w, conic);
GrTessellationShader::WriteConicPatch(p, w, conic);
SkPoint endControlPoint = conic[1];
this->writeStroke(conic, endControlPoint);
fMaxParametricSegments_pow4 = std::max(numParametricSegments_pow4,

View File

@ -369,7 +369,7 @@ private:
if (w == 1) {
GrPathUtils::convertQuadToCubic(p, asPatch);
} else {
GrPathShader::WriteConicPatch(p, w, asPatch);
GrTessellationShader::WriteConicPatch(p, w, asPatch);
}
float numParametricSegments_pow4 = GrWangsFormula::quadratic_pow4(fParametricPrecision, p);
@ -835,7 +835,7 @@ void GrStrokeHardwareTessellator::prepare(GrMeshDrawOp::Target* target, int tota
// case. Write it out directly.
prevJoinFitsInPatch = patchWriter.stroke180FitsInPatch_withJoin(
numParametricSegments_pow4);
GrPathShader::WriteConicPatch(p, *w, scratchPts);
GrTessellationShader::WriteConicPatch(p, *w, scratchPts);
patchPts = scratchPts;
endControlPoint = p[1];
break;

View File

@ -890,7 +890,7 @@ void GrStrokeIndirectTessellator::writeBuffers(GrDrawIndirectWriter* indirectWri
binningWriter.writeCircle(cuspResolveLevel, cusp);
resolveLevel = (isRoundJoin) ? *nextResolveLevel++ : 0;
} else {
GrPathShader::WriteConicPatch(pts, iter.w(), scratch);
GrTessellationShader::WriteConicPatch(pts, iter.w(), scratch);
}
pts_ = scratch;
break;

View File

@ -8,7 +8,7 @@
#ifndef GrStrokeShader_DEFINED
#define GrStrokeShader_DEFINED
#include "src/gpu/tessellate/GrPathShader.h"
#include "src/gpu/tessellate/GrTessellationShader.h"
#include "include/core/SkStrokeRec.h"
#include "src/gpu/GrVx.h"
@ -23,7 +23,7 @@
// divide the curve's _rotation_ into even steps. The tessellation shader evaluates both sets of
// edges and sorts them into a single quad strip. With this combined set of edges we can stroke any
// curve, regardless of curvature.
class GrStrokeShader : public GrPathShader {
class GrStrokeShader : public GrTessellationShader {
public:
// Are we using hardware tessellation or indirect draws?
enum class Mode : int8_t {
@ -98,17 +98,17 @@ public:
};
// 'viewMatrix' is applied to the geometry post tessellation. It cannot have perspective.
GrStrokeShader(Mode mode, ShaderFlags shaderFlags, int8_t maxParametricSegments_log2,
const SkMatrix& viewMatrix, const SkStrokeRec& stroke, SkPMColor4f color)
: GrPathShader(kTessellate_GrStrokeShader_ClassID, viewMatrix,
(mode == Mode::kHardwareTessellation) ?
GrPrimitiveType::kPatches : GrPrimitiveType::kTriangleStrip,
(mode == Mode::kHardwareTessellation) ? 1 : 0)
GrStrokeShader(Mode mode, ShaderFlags shaderFlags, const SkMatrix& viewMatrix,
const SkStrokeRec& stroke, SkPMColor4f color, int8_t maxParametricSegments_log2)
: GrTessellationShader(kTessellate_GrStrokeShader_ClassID,
(mode == Mode::kHardwareTessellation)
? GrPrimitiveType::kPatches
: GrPrimitiveType::kTriangleStrip,
(mode == Mode::kHardwareTessellation) ? 1 : 0, viewMatrix, color)
, fMode(mode)
, fShaderFlags(shaderFlags)
, fMaxParametricSegments_log2(maxParametricSegments_log2)
, fStroke(stroke)
, fColor(color) {
, fMaxParametricSegments_log2(maxParametricSegments_log2) {
if (fMode == Mode::kHardwareTessellation) {
// A join calculates its starting angle using prevCtrlPtAttr.
fAttribs.emplace_back("prevCtrlPtAttr", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
@ -168,11 +168,10 @@ public:
Mode mode() const { return fMode; }
ShaderFlags flags() const { return fShaderFlags; }
int8_t maxParametricSegments_log2() const { return fMaxParametricSegments_log2; }
bool hasDynamicStroke() const { return fShaderFlags & ShaderFlags::kDynamicStroke; }
bool hasDynamicColor() const { return fShaderFlags & ShaderFlags::kDynamicColor; }
const SkStrokeRec& stroke() const { return fStroke;}
const SkPMColor4f& color() const { return fColor;}
int8_t maxParametricSegments_log2() const { return fMaxParametricSegments_log2; }
float fixedCountNumTotalEdges() const { return fFixedCountNumTotalEdges;}
// Used by GrFixedCountTessellator to configure the uniform value that tells the shader how many
@ -189,9 +188,8 @@ private:
const Mode fMode;
const ShaderFlags fShaderFlags;
const int8_t fMaxParametricSegments_log2;
const SkStrokeRec fStroke;
const SkPMColor4f fColor;
const int8_t fMaxParametricSegments_log2;
constexpr static int kMaxAttribCount = 5;
SkSTArray<kMaxAttribCount, Attribute> fAttribs;

View File

@ -9,11 +9,10 @@
#include "src/core/SkPathPriv.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/tessellate/GrFillPathShader.h"
#include "src/gpu/tessellate/GrStencilPathShader.h"
#include "src/gpu/tessellate/GrStrokeFixedCountTessellator.h"
#include "src/gpu/tessellate/GrStrokeHardwareTessellator.h"
#include "src/gpu/tessellate/GrStrokeIndirectTessellator.h"
#include "src/gpu/tessellate/GrTessellationShader.h"
using DynamicStroke = GrStrokeShader::DynamicStroke;
@ -164,7 +163,7 @@ bool GrStrokeTessellateOp::canUseHardwareTessellation(int numVerbs, const GrCaps
return numVerbs >= caps.minStrokeVerbsForHwTessellation();
}
void GrStrokeTessellateOp::prePrepareTessellator(GrPathShader::ProgramArgs&& args,
void GrStrokeTessellateOp::prePrepareTessellator(GrTessellationShader::ProgramArgs&& args,
GrAppliedClip&& clip) {
SkASSERT(!fTessellator);
SkASSERT(!fFillProgram);
@ -212,17 +211,18 @@ void GrStrokeTessellateOp::prePrepareTessellator(GrPathShader::ProgramArgs&& arg
strokeCullBounds);
}
auto* pipeline = GrFillPathShader::MakeFillPassPipeline(args, fAAType, std::move(clip),
std::move(fProcessors));
auto* pipeline = GrTessellationShader::MakePipeline(args, fAAType, std::move(clip),
std::move(fProcessors));
auto fillStencil = &GrUserStencilSettings::kUnused;
if (fNeedsStencil) {
fStencilProgram = GrPathShader::MakeProgram(args, fTessellator->shader(), pipeline,
&kMarkStencil);
fStencilProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(), pipeline,
&kMarkStencil);
fillStencil = &kTestAndResetStencil;
args.fXferBarrierFlags = GrXferBarrierFlags::kNone;
}
fFillProgram = GrPathShader::MakeProgram(args, fTessellator->shader(), pipeline, fillStencil);
fFillProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(), pipeline,
fillStencil);
}
void GrStrokeTessellateOp::onPrePrepare(GrRecordingContext* context,

View File

@ -10,8 +10,8 @@
#include "include/core/SkStrokeRec.h"
#include "src/gpu/ops/GrMeshDrawOp.h"
#include "src/gpu/tessellate/GrPathShader.h"
#include "src/gpu/tessellate/GrStrokeTessellator.h"
#include "src/gpu/tessellate/GrTessellationShader.h"
class GrRecordingContext;
@ -55,7 +55,7 @@ private:
CombineResult onCombineIfPossible(GrOp*, SkArenaAlloc*, const GrCaps&) override;
// Creates the tessellator and the stencil/fill program(s) we will use with it.
void prePrepareTessellator(GrPathShader::ProgramArgs&&, GrAppliedClip&&);
void prePrepareTessellator(GrTessellationShader::ProgramArgs&&, GrAppliedClip&&);
void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*,
const GrXferProcessor::DstProxyView&, GrXferBarrierFlags,

View File

@ -29,14 +29,14 @@ public:
int8_t maxParametricSegments_log2, const SkMatrix& viewMatrix,
PathStrokeList* pathStrokeList, std::array<float,2> matrixMinMaxScales,
const SkRect& strokeCullBounds)
: fShader(shaderMode, shaderFlags, maxParametricSegments_log2, viewMatrix,
pathStrokeList->fStroke, pathStrokeList->fColor)
: fShader(shaderMode, shaderFlags, viewMatrix, pathStrokeList->fStroke,
pathStrokeList->fColor, maxParametricSegments_log2)
, fPathStrokeList(pathStrokeList)
, fMatrixMinMaxScales(matrixMinMaxScales)
, fStrokeCullBounds(strokeCullBounds) {
}
const GrPathShader* shader() const { return &fShader; }
const GrTessellationShader* shader() const { return &fShader; }
// Called before draw(). Prepares GPU buffers containing the geometry to tessellate.
virtual void prepare(GrMeshDrawOp::Target*, int totalCombinedVerbCnt) = 0;

View File

@ -5,26 +5,27 @@
* found in the LICENSE file.
*/
#ifndef GrPathShader_DEFINED
#define GrPathShader_DEFINED
#ifndef GrTessellationShader_DEFINED
#define GrTessellationShader_DEFINED
#include "src/core/SkArenaAlloc.h"
#include "src/gpu/GrGeometryProcessor.h"
#include "src/gpu/GrOpFlushState.h"
#include "src/gpu/GrOpsRenderPass.h"
#include "src/gpu/GrProgramInfo.h"
#include "src/gpu/GrVertexWriter.h"
#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
class SkArenaAlloc;
// This is a common base class for shaders in the GPU tessellator.
class GrPathShader : public GrGeometryProcessor {
class GrTessellationShader : public GrGeometryProcessor {
public:
GrPathShader(ClassID classID, const SkMatrix& viewMatrix, GrPrimitiveType primitiveType,
int tessellationPatchVertexCount)
GrTessellationShader(ClassID classID, GrPrimitiveType primitiveType,
int tessellationPatchVertexCount, const SkMatrix& viewMatrix,
const SkPMColor4f& color)
: GrGeometryProcessor(classID)
, fViewMatrix(viewMatrix)
, fPrimitiveType(primitiveType)
, fTessellationPatchVertexCount(tessellationPatchVertexCount) {
, fTessellationPatchVertexCount(tessellationPatchVertexCount)
, fViewMatrix(viewMatrix)
, fColor(color) {
if (fTessellationPatchVertexCount) {
this->setWillUseTessellationShaders();
}
@ -33,24 +34,7 @@ public:
GrPrimitiveType primitiveType() const { return fPrimitiveType; }
int tessellationPatchVertexCount() const { return fTessellationPatchVertexCount; }
const SkMatrix& viewMatrix() const { return fViewMatrix; }
struct ProgramArgs {
SkArenaAlloc* fArena;
const GrSurfaceProxyView& fWriteView;
const GrXferProcessor::DstProxyView* fDstProxyView;
GrXferBarrierFlags fXferBarrierFlags;
GrLoadOp fColorLoadOp;
const GrCaps* fCaps;
};
static GrProgramInfo* MakeProgram(const ProgramArgs& args, const GrPathShader* shader,
const GrPipeline* pipeline,
const GrUserStencilSettings* stencil) {
return args.fArena->make<GrProgramInfo>(args.fWriteView, pipeline, stencil, shader,
shader->fPrimitiveType,
shader->fTessellationPatchVertexCount,
args.fXferBarrierFlags, args.fColorLoadOp);
}
const SkPMColor4f& color() const { return fColor;}
// Fills in a 4-point patch in such a way that the shader will recognize it as a conic.
static void WriteConicPatch(const SkPoint pts[3], float w, GrVertexWriter* writer) {
@ -64,12 +48,41 @@ public:
WriteConicPatch(pts, w, &writer);
}
struct ProgramArgs {
SkArenaAlloc* fArena;
const GrSurfaceProxyView& fWriteView;
const GrXferProcessor::DstProxyView* fDstProxyView;
GrXferBarrierFlags fXferBarrierFlags;
GrLoadOp fColorLoadOp;
const GrCaps* fCaps;
};
static const GrPipeline* MakePipeline(const ProgramArgs& args, GrAAType aaType,
GrAppliedClip&& appliedClip,
GrProcessorSet&& processors) {
auto pipelineFlags = GrPipeline::InputFlags::kNone;
if (aaType == GrAAType::kMSAA) {
pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
}
return GrSimpleMeshDrawOpHelper::CreatePipeline(
args.fCaps, args.fArena, args.fWriteView.swizzle(), std::move(appliedClip),
*args.fDstProxyView, std::move(processors), pipelineFlags);
}
static GrProgramInfo* MakeProgram(const ProgramArgs& args, const GrTessellationShader* shader,
const GrPipeline* pipeline,
const GrUserStencilSettings* stencil) {
return args.fArena->make<GrProgramInfo>(args.fWriteView, pipeline, stencil, shader,
shader->fPrimitiveType,
shader->fTessellationPatchVertexCount,
args.fXferBarrierFlags, args.fColorLoadOp);
}
private:
const SkMatrix fViewMatrix;
const GrPrimitiveType fPrimitiveType;
const int fTessellationPatchVertexCount;
class Impl;
const SkMatrix fViewMatrix;
const SkPMColor4f fColor;
};
#endif