Add a stencil test for transparent tessellated strokes
Initially we restricted tessellated stroking to opaque solid colors.
This CL adds support for transparency by enabling a stencil test. The
stencil test also allows us to use mixed samples.
Bug: skia:10419
Change-Id: Ie40f3099d2b009f92ed49f7f43e5f269b1a479af
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/340798
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/include/private/GrTypesPriv.h b/include/private/GrTypesPriv.h
index e6f599e..a0972d7 100644
--- a/include/private/GrTypesPriv.h
+++ b/include/private/GrTypesPriv.h
@@ -61,10 +61,6 @@
return GrPrimitiveType::kLines == type || GrPrimitiveType::kLineStrip == type;
}
-static constexpr bool GrIsPrimTypeTris(GrPrimitiveType type) {
- return GrPrimitiveType::kTriangles == type || GrPrimitiveType::kTriangleStrip == type;
-}
-
enum class GrPrimitiveRestart : bool {
kNo = false,
kYes = true
diff --git a/src/gpu/GrOpsRenderPass.cpp b/src/gpu/GrOpsRenderPass.cpp
index c22ae3f..e48c14c 100644
--- a/src/gpu/GrOpsRenderPass.cpp
+++ b/src/gpu/GrOpsRenderPass.cpp
@@ -68,10 +68,6 @@
}
if (programInfo.pipeline().usesConservativeRaster()) {
SkASSERT(this->gpu()->caps()->conservativeRasterSupport());
- // Conservative raster, by default, only supports triangles. Implementations can
- // optionally indicate that they also support points and lines, but we don't currently
- // query or track that info.
- SkASSERT(GrIsPrimTypeTris(programInfo.primitiveType()));
}
if (programInfo.pipeline().isWireframe()) {
SkASSERT(this->gpu()->caps()->wireframeSupport());
diff --git a/src/gpu/GrPaint.h b/src/gpu/GrPaint.h
index 1ac34fb..15788c3 100644
--- a/src/gpu/GrPaint.h
+++ b/src/gpu/GrPaint.h
@@ -94,6 +94,10 @@
GrFragmentProcessor* getCoverageFragmentProcessor() const {
return fCoverageFragmentProcessor.get();
}
+ bool usesVaryingCoords() const {
+ return (fColorFragmentProcessor && fColorFragmentProcessor->usesVaryingCoords()) ||
+ (fCoverageFragmentProcessor && fCoverageFragmentProcessor->usesVaryingCoords());
+ }
/**
* Returns true if the paint's output color will be constant after blending. If the result is
diff --git a/src/gpu/tessellate/GrStrokeIndirectOp.cpp b/src/gpu/tessellate/GrStrokeIndirectOp.cpp
index 67b7b5d..a33c15e 100644
--- a/src/gpu/tessellate/GrStrokeIndirectOp.cpp
+++ b/src/gpu/tessellate/GrStrokeIndirectOp.cpp
@@ -37,10 +37,15 @@
auto* strokeTessellateShader = arena->make<GrStrokeTessellateShader>(
GrStrokeTessellateShader::Mode::kIndirect, fStroke, fParametricIntolerance,
fNumRadialSegmentsPerRadian, fViewMatrix, fColor);
- this->prePrepareColorProgram(context->priv().recordTimeAllocator(), strokeTessellateShader,
- writeView, std::move(*clip), dstProxyView, renderPassXferBarriers,
- colorLoadOp, *context->priv().caps());
- context->priv().recordProgramInfo(fColorProgram);
+ this->prePreparePrograms(context->priv().recordTimeAllocator(), strokeTessellateShader,
+ writeView, std::move(*clip), dstProxyView, renderPassXferBarriers,
+ colorLoadOp, *context->priv().caps());
+ if (fFillProgram) {
+ context->priv().recordProgramInfo(fFillProgram);
+ }
+ if (fStencilProgram) {
+ context->priv().recordProgramInfo(fStencilProgram);
+ }
}
// Helpers for GrStrokeIndirectOp::prePrepareResolveLevels.
@@ -580,10 +585,10 @@
auto* strokeTessellateShader = arena->make<GrStrokeTessellateShader>(
GrStrokeTessellateShader::Mode::kIndirect, fStroke, fParametricIntolerance,
fNumRadialSegmentsPerRadian, fViewMatrix, fColor);
- this->prePrepareColorProgram(arena, strokeTessellateShader, flushState->writeView(),
- flushState->detachAppliedClip(), flushState->dstProxyView(),
- flushState->renderPassBarriers(), flushState->colorLoadOp(),
- flushState->caps());
+ this->prePreparePrograms(arena, strokeTessellateShader, flushState->writeView(),
+ flushState->detachAppliedClip(), flushState->dstProxyView(),
+ flushState->renderPassBarriers(), flushState->colorLoadOp(),
+ flushState->caps());
}
SkASSERT(fResolveLevels);
@@ -809,11 +814,20 @@
SkASSERT(fDrawIndirectCount);
SkASSERT(fTotalInstanceCount > 0);
- SkASSERT(fColorProgram);
SkASSERT(chainBounds == this->bounds());
- flushState->bindPipelineAndScissorClip(*fColorProgram, this->bounds());
- flushState->bindTextures(fColorProgram->primProc(), nullptr, fColorProgram->pipeline());
- flushState->bindBuffers(nullptr, fInstanceBuffer, nullptr);
- flushState->drawIndirect(fDrawIndirectBuffer.get(), fDrawIndirectOffset, fDrawIndirectCount);
+ if (fStencilProgram) {
+ flushState->bindPipelineAndScissorClip(*fStencilProgram, this->bounds());
+ flushState->bindTextures(fStencilProgram->primProc(), nullptr, fStencilProgram->pipeline());
+ flushState->bindBuffers(nullptr, fInstanceBuffer, nullptr);
+ flushState->drawIndirect(fDrawIndirectBuffer.get(), fDrawIndirectOffset,
+ fDrawIndirectCount);
+ }
+ if (fFillProgram) {
+ flushState->bindPipelineAndScissorClip(*fFillProgram, this->bounds());
+ flushState->bindTextures(fFillProgram->primProc(), nullptr, fFillProgram->pipeline());
+ flushState->bindBuffers(nullptr, fInstanceBuffer, nullptr);
+ flushState->drawIndirect(fDrawIndirectBuffer.get(), fDrawIndirectOffset,
+ fDrawIndirectCount);
+ }
}
diff --git a/src/gpu/tessellate/GrStrokeOp.cpp b/src/gpu/tessellate/GrStrokeOp.cpp
index 217338d..4daf21a 100644
--- a/src/gpu/tessellate/GrStrokeOp.cpp
+++ b/src/gpu/tessellate/GrStrokeOp.cpp
@@ -12,14 +12,6 @@
#include "src/gpu/tessellate/GrStrokeTessellateOp.h"
#include "src/gpu/tessellate/GrStrokeTessellateShader.h"
-static SkPMColor4f get_paint_constant_blended_color(const GrPaint& paint) {
- SkPMColor4f constantColor;
- // Patches can overlap, so until a stencil technique is implemented, the provided paints must be
- // constant blended colors.
- SkAssertResult(paint.isConstantBlendedColor(&constantColor));
- return constantColor;
-}
-
GrStrokeOp::GrStrokeOp(uint32_t classID, GrAAType aaType, const SkMatrix& viewMatrix,
const SkStrokeRec& stroke, const SkPath& path, GrPaint&& paint)
: GrDrawOp(classID)
@@ -30,14 +22,13 @@
fViewMatrix.getMaxScale() * GrTessellationPathRenderer::kLinearizationIntolerance)
, fNumRadialSegmentsPerRadian(
.5f / acosf(std::max(1 - 2/(fParametricIntolerance * fStroke.getWidth()), -1.f)))
- , fColor(get_paint_constant_blended_color(paint))
+ , fColor(paint.getColor4f())
, fProcessors(std::move(paint))
, fPathList(path)
, fTotalCombinedVerbCnt(path.countVerbs()) {
// We don't support hairline strokes. For now, the client can transform the path into device
// space and then use a stroke width of 1.
SkASSERT(fStroke.getWidth() > 0);
- SkASSERT(fAAType != GrAAType::kCoverage); // No mixed samples support yet.
SkASSERT(fParametricIntolerance >= 0);
SkRect devBounds = path.getBounds();
float inflationRadius = fStroke.getInflationRadius();
@@ -47,7 +38,9 @@
}
GrDrawOp::FixedFunctionFlags GrStrokeOp::fixedFunctionFlags() const {
- auto flags = FixedFunctionFlags::kNone;
+ // We might not actually end up needing stencil, but won't know for sure until finalize().
+ // Request it just in case we do end up needing it.
+ auto flags = FixedFunctionFlags::kUsesStencil;
if (GrAAType::kNone != fAAType) {
flags |= FixedFunctionFlags::kUsesHWAA;
}
@@ -56,16 +49,22 @@
GrProcessorSet::Analysis GrStrokeOp::finalize(const GrCaps& caps, const GrAppliedClip* clip,
bool hasMixedSampledCoverage, GrClampType clampType) {
- return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip,
- &GrUserStencilSettings::kUnused, hasMixedSampledCoverage, caps,
- clampType, &fColor);
+ // Make sure the finalize happens before combining. We might change fNeedsStencil here.
+ SkASSERT(fPathList.begin().fCurr->fNext == nullptr);
+ const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
+ fColor, GrProcessorAnalysisCoverage::kNone, clip, &GrUserStencilSettings::kUnused,
+ hasMixedSampledCoverage, caps, clampType, &fColor);
+ fNeedsStencil = !analysis.unaffectedByDstValue();
+ return analysis;
}
GrOp::CombineResult GrStrokeOp::onCombineIfPossible(GrOp* grOp, SkArenaAlloc* alloc,
const GrCaps&) {
SkASSERT(grOp->classID() == this->classID());
auto* op = static_cast<GrStrokeOp*>(grOp);
- if (fColor != op->fColor ||
+ if (fNeedsStencil ||
+ op->fNeedsStencil ||
+ fColor != op->fColor ||
fViewMatrix != op->fViewMatrix ||
fAAType != op->fAAType ||
!fStroke.hasEqualEffect(op->fStroke) ||
@@ -79,23 +78,84 @@
return CombineResult::kMerged;
}
-void GrStrokeOp::prePrepareColorProgram(SkArenaAlloc* arena,
- GrStrokeTessellateShader* strokeTessellateShader,
- const GrSurfaceProxyView& writeView, GrAppliedClip&& clip,
- const GrXferProcessor::DstProxyView& dstProxyView,
- GrXferBarrierFlags renderPassXferBarriers,
- GrLoadOp colorLoadOp,
- const GrCaps& caps) {
- SkASSERT(!fColorProgram);
- auto pipelineFlags = GrPipeline::InputFlags::kNone;
- if (GrAAType::kNone != fAAType) {
- pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
- SkASSERT(writeView.asRenderTargetProxy()->numSamples() > 1); // No mixed samples yet.
- SkASSERT(fAAType != GrAAType::kCoverage); // No mixed samples yet.
+// Marks every stencil value as "1".
+constexpr static GrUserStencilSettings kMarkStencil(
+ GrUserStencilSettings::StaticInit<
+ 0x0001,
+ GrUserStencilTest::kLessIfInClip, // Match kTestAndResetStencil.
+ 0x0000, // Always fail.
+ GrUserStencilOp::kZero,
+ GrUserStencilOp::kReplace,
+ 0xffff>());
+
+// Passes if the stencil value is nonzero. Also resets the stencil value to zero on pass. This is
+// formulated to match kMarkStencil everywhere except the ref and compare mask. This will allow us
+// to use the same pipeline for both stencil and fill if dynamic stencil state is supported.
+constexpr static GrUserStencilSettings kTestAndResetStencil(
+ GrUserStencilSettings::StaticInit<
+ 0x0000,
+ GrUserStencilTest::kLessIfInClip, // i.e., "not equal to zero, if in clip".
+ 0x0001,
+ GrUserStencilOp::kZero,
+ GrUserStencilOp::kReplace,
+ 0xffff>());
+
+void GrStrokeOp::prePreparePrograms(SkArenaAlloc* arena,
+ GrStrokeTessellateShader* strokeTessellateShader,
+ const GrSurfaceProxyView& writeView, GrAppliedClip&& clip,
+ const GrXferProcessor::DstProxyView& dstProxyView,
+ GrXferBarrierFlags renderPassXferBarriers,
+ GrLoadOp colorLoadOp, const GrCaps& caps) {
+ using InputFlags = GrPipeline::InputFlags;
+ SkASSERT(!fFillProgram);
+ SkASSERT(!fStencilProgram);
+
+ // This will be created iff the stencil pass can't share a pipeline with the fill pass.
+ GrPipeline* standaloneStencilPipeline = nullptr;
+
+ GrPipeline::InitArgs fillArgs;
+ fillArgs.fCaps = ∩︀
+ fillArgs.fDstProxyView = dstProxyView;
+ fillArgs.fWriteSwizzle = writeView.swizzle();
+ if (fAAType != GrAAType::kNone) {
+ if (writeView.asRenderTargetProxy()->numSamples() == 1) {
+ // We are mixed sampled. We need to either enable conservative raster (preferred) or
+ // disable MSAA in order to avoid double blend artifacts. (Even if we disable MSAA for
+ // the cover geometry, the stencil test is still multisampled and will still produce
+ // smooth results.)
+ SkASSERT(GrAAType::kCoverage == fAAType);
+ if (caps.conservativeRasterSupport()) {
+ fillArgs.fInputFlags |= InputFlags::kHWAntialias | InputFlags::kConservativeRaster;
+ }
+ // Since we either need conservative raster enabled or MSAA disabled during fill, we
+ // need a separate pipeline for the stencil pass.
+ SkASSERT(fNeedsStencil); // Mixed samples always needs stencil.
+ GrPipeline::InitArgs stencilArgs;
+ stencilArgs.fCaps = ∩︀
+ stencilArgs.fInputFlags = InputFlags::kHWAntialias;
+ stencilArgs.fWriteSwizzle = writeView.swizzle();
+ standaloneStencilPipeline = arena->make<GrPipeline>(
+ stencilArgs, GrDisableColorXPFactory::MakeXferProcessor(), clip.hardClip());
+ } else {
+ // We are standard MSAA. Leave MSAA enabled for both the fill and stencil passes.
+ fillArgs.fInputFlags |= InputFlags::kHWAntialias;
+ }
}
- fColorProgram = GrPathShader::MakeProgramInfo(strokeTessellateShader, arena, writeView,
- pipelineFlags, std::move(fProcessors),
- std::move(clip), dstProxyView,
- renderPassXferBarriers, colorLoadOp,
- &GrUserStencilSettings::kUnused, caps);
+
+ auto fillPipeline = arena->make<GrPipeline>(fillArgs, std::move(fProcessors), std::move(clip));
+ auto fillStencil = &GrUserStencilSettings::kUnused;
+ auto fillXferFlags = renderPassXferBarriers;
+ if (fNeedsStencil) {
+ auto* stencilPipeline = (standaloneStencilPipeline) ? standaloneStencilPipeline
+ : fillPipeline;
+ fStencilProgram = GrPathShader::MakeProgramInfo(strokeTessellateShader, arena, writeView,
+ stencilPipeline, dstProxyView,
+ renderPassXferBarriers, colorLoadOp,
+ &kMarkStencil, caps);
+ fillStencil = &kTestAndResetStencil;
+ fillXferFlags = GrXferBarrierFlags::kNone;
+ }
+ fFillProgram = GrPathShader::MakeProgramInfo(strokeTessellateShader, arena, writeView,
+ fillPipeline, dstProxyView, fillXferFlags,
+ colorLoadOp, fillStencil, caps);
}
diff --git a/src/gpu/tessellate/GrStrokeOp.h b/src/gpu/tessellate/GrStrokeOp.h
index ce5d855..7cd4c82 100644
--- a/src/gpu/tessellate/GrStrokeOp.h
+++ b/src/gpu/tessellate/GrStrokeOp.h
@@ -34,10 +34,10 @@
bool hasMixedSampledCoverage, GrClampType) override;
CombineResult onCombineIfPossible(GrOp*, SkArenaAlloc*, const GrCaps&) override;
- void prePrepareColorProgram(SkArenaAlloc* arena, GrStrokeTessellateShader*,
- const GrSurfaceProxyView&, GrAppliedClip&&,
- const GrXferProcessor::DstProxyView&, GrXferBarrierFlags,
- GrLoadOp colorLoadOp, const GrCaps&);
+ void prePreparePrograms(SkArenaAlloc* arena, GrStrokeTessellateShader*,
+ const GrSurfaceProxyView&, GrAppliedClip&&,
+ const GrXferProcessor::DstProxyView&, GrXferBarrierFlags,
+ GrLoadOp colorLoadOp, const GrCaps&);
static float NumCombinedSegments(float numParametricSegments, float numRadialSegments) {
// The first and last edges are shared by both the parametric and radial sets of edges, so
@@ -76,12 +76,14 @@
// smoothness.
const float fNumRadialSegmentsPerRadian;
SkPMColor4f fColor;
+ bool fNeedsStencil = false;
GrProcessorSet fProcessors;
GrSTArenaList<SkPath> fPathList;
int fTotalCombinedVerbCnt;
- const GrProgramInfo* fColorProgram = nullptr;
+ const GrProgramInfo* fStencilProgram = nullptr;
+ const GrProgramInfo* fFillProgram = nullptr;
};
#endif
diff --git a/src/gpu/tessellate/GrStrokeTessellateOp.cpp b/src/gpu/tessellate/GrStrokeTessellateOp.cpp
index 72c3f5f..b1cd656 100644
--- a/src/gpu/tessellate/GrStrokeTessellateOp.cpp
+++ b/src/gpu/tessellate/GrStrokeTessellateOp.cpp
@@ -23,23 +23,29 @@
auto* strokeTessellateShader = arena->make<GrStrokeTessellateShader>(
GrStrokeTessellateShader::Mode::kTessellation, fStroke, fParametricIntolerance,
fNumRadialSegmentsPerRadian, fViewMatrix, fColor);
- this->prePrepareColorProgram(arena, strokeTessellateShader, writeView, std::move(*clip),
- dstProxyView, renderPassXferBarriers, colorLoadOp,
- *context->priv().caps());
- context->priv().recordProgramInfo(fColorProgram);
+ this->prePreparePrograms(arena, strokeTessellateShader, writeView, std::move(*clip),
+ dstProxyView, renderPassXferBarriers, colorLoadOp,
+ *context->priv().caps());
+ if (fStencilProgram) {
+ context->priv().recordProgramInfo(fStencilProgram);
+ }
+ if (fFillProgram) {
+ context->priv().recordProgramInfo(fFillProgram);
+ }
}
void GrStrokeTessellateOp::onPrepare(GrOpFlushState* flushState) {
- if (!fColorProgram) {
+ if (!fFillProgram && !fStencilProgram) {
SkArenaAlloc* arena = flushState->allocator();
auto* strokeTessellateShader = arena->make<GrStrokeTessellateShader>(
GrStrokeTessellateShader::Mode::kTessellation, fStroke, fParametricIntolerance,
fNumRadialSegmentsPerRadian, fViewMatrix, fColor);
- this->prePrepareColorProgram(flushState->allocator(), strokeTessellateShader,
- flushState->writeView(), flushState->detachAppliedClip(),
- flushState->dstProxyView(), flushState->renderPassBarriers(),
- flushState->colorLoadOp(), flushState->caps());
+ this->prePreparePrograms(flushState->allocator(), strokeTessellateShader,
+ flushState->writeView(), flushState->detachAppliedClip(),
+ flushState->dstProxyView(), flushState->renderPassBarriers(),
+ flushState->colorLoadOp(), flushState->caps());
}
+ SkASSERT(fFillProgram || fStencilProgram);
fTarget = flushState;
this->prepareBuffers();
@@ -585,15 +591,25 @@
}
void GrStrokeTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
- SkASSERT(fColorProgram);
SkASSERT(chainBounds == this->bounds());
-
- flushState->bindPipelineAndScissorClip(*fColorProgram, this->bounds());
- flushState->bindTextures(fColorProgram->primProc(), nullptr, fColorProgram->pipeline());
- for (const auto& chunk : fPatchChunks) {
- if (chunk.fPatchBuffer) {
- flushState->bindBuffers(nullptr, nullptr, std::move(chunk.fPatchBuffer));
- flushState->draw(chunk.fPatchCount, chunk.fBasePatch);
+ if (fStencilProgram) {
+ flushState->bindPipelineAndScissorClip(*fStencilProgram, this->bounds());
+ flushState->bindTextures(fStencilProgram->primProc(), nullptr, fStencilProgram->pipeline());
+ for (const auto& chunk : fPatchChunks) {
+ if (chunk.fPatchBuffer) {
+ flushState->bindBuffers(nullptr, nullptr, std::move(chunk.fPatchBuffer));
+ flushState->draw(chunk.fPatchCount, chunk.fBasePatch);
+ }
+ }
+ }
+ if (fFillProgram) {
+ flushState->bindPipelineAndScissorClip(*fFillProgram, this->bounds());
+ flushState->bindTextures(fFillProgram->primProc(), nullptr, fFillProgram->pipeline());
+ for (const auto& chunk : fPatchChunks) {
+ if (chunk.fPatchBuffer) {
+ flushState->bindBuffers(nullptr, nullptr, std::move(chunk.fPatchBuffer));
+ flushState->draw(chunk.fPatchCount, chunk.fBasePatch);
+ }
}
}
}
diff --git a/src/gpu/tessellate/GrStrokeTessellateOp.h b/src/gpu/tessellate/GrStrokeTessellateOp.h
index 6610580..fd7c7b9 100644
--- a/src/gpu/tessellate/GrStrokeTessellateOp.h
+++ b/src/gpu/tessellate/GrStrokeTessellateOp.h
@@ -20,9 +20,6 @@
DEFINE_OP_CLASS_ID
private:
- // The provided matrix must be a similarity matrix for the time being. This is so we can
- // bootstrap this Op on top of GrStrokeGeometry with minimal modifications.
- //
// Patches can overlap, so until a stencil technique is implemented, the provided paint must be
// a constant blended color.
GrStrokeTessellateOp(GrAAType aaType, const SkMatrix& viewMatrix, const SkStrokeRec& stroke,
diff --git a/src/gpu/tessellate/GrStrokeTessellateShader.cpp b/src/gpu/tessellate/GrStrokeTessellateShader.cpp
index 471c166..cfab725 100644
--- a/src/gpu/tessellate/GrStrokeTessellateShader.cpp
+++ b/src/gpu/tessellate/GrStrokeTessellateShader.cpp
@@ -930,26 +930,26 @@
}
args.fVertBuilder->codeAppend(R"(
- float2 tangent, position;
+ float2 tangent, localCoord;
eval_stroke_edge(P, numParametricSegments, combinedEdgeID, tan0, radsPerSegment, angle0,
- tangent, position);
+ tangent, localCoord);
if (combinedEdgeID == 0) {
// Edges at the beginning of their section use P[0] and tan0. This ensures crack-free
// seaming between instances.
- position = P[0];
+ localCoord = P[0];
tangent = tan0;
}
if (combinedEdgeID == numCombinedSegments) {
// Edges at the end of their section use P[1] and tan1. This ensures crack-free seaming
// between instances.
- position = P[3];
+ localCoord = P[3];
tangent = tan1;
}
float2 ortho = normalize(float2(tangent.y, -tangent.x));
- position += ortho * (uStrokeRadius * outset);)");
+ localCoord += ortho * (uStrokeRadius * outset);)");
// Do the transform after tessellation. Stroke widths and normals are defined in
// (pre-transform) local path space.
@@ -960,11 +960,13 @@
fAffineMatrixUniform = args.fUniformHandler->addUniform(
nullptr, kVertex_GrShaderFlag, kFloat4_GrSLType, "affineMatrix",
&affineMatrixName);
- args.fVertBuilder->codeAppendf("position = float2x2(%s) * position + %s;",
+ args.fVertBuilder->codeAppendf("float2 devCoord = float2x2(%s) * localCoord + %s;",
affineMatrixName, translateName);
+ gpArgs->fPositionVar.set(kFloat2_GrSLType, "devCoord");
+ } else {
+ gpArgs->fPositionVar.set(kFloat2_GrSLType, "localCoord");
}
- gpArgs->fPositionVar.set(kFloat2_GrSLType, "position");
- gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "position");
+ gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localCoord");
// The fragment shader just outputs a uniform color.
const char* colorUniformName;
diff --git a/src/gpu/tessellate/GrTessellationPathRenderer.cpp b/src/gpu/tessellate/GrTessellationPathRenderer.cpp
index ba135ee..292b430 100644
--- a/src/gpu/tessellate/GrTessellationPathRenderer.cpp
+++ b/src/gpu/tessellate/GrTessellationPathRenderer.cpp
@@ -141,18 +141,19 @@
shape.asPath(&path);
if (!shape.style().isSimpleFill()) {
- if (SkPathPriv::ConicWeightCnt(path)) {
- return CanDrawPath::kNo;
- }
- SkPMColor4f constantColor;
// These are only temporary restrictions while we bootstrap tessellated stroking. Every one
// of them will eventually go away.
if (shape.style().strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style ||
- GrAAType::kCoverage == args.fAAType ||
- !args.fPaint->isConstantBlendedColor(&constantColor) ||
- args.fPaint->hasCoverageFragmentProcessor()) {
+ SkPathPriv::ConicWeightCnt(path)) {
return CanDrawPath::kNo;
}
+ if (shape.style().isSimpleHairline()) {
+ // For the time being we translate hairline paths to device space. We can't do this if
+ // it's possible the paint might use local coordinates.
+ if (args.fPaint->usesVaryingCoords()) {
+ return CanDrawPath::kNo;
+ }
+ }
}
return CanDrawPath::kYes;
@@ -163,8 +164,10 @@
const SkPath& path, GrPaint&& paint,
const GrShaderCaps& shaderCaps) {
// Only use hardware tessellation if the path has a somewhat large number of verbs. Otherwise we
- // seem to be better off using indirect draws.
- if (shaderCaps.tessellationSupport() && path.countVerbs() > 50) {
+ // seem to be better off using indirect draws. Our back door for HW tessellation shaders isn't
+ // currently capable of passing varyings to the fragment shader either, so if the paint uses
+ // varyings we need to use indirect draws.
+ if (shaderCaps.tessellationSupport() && path.countVerbs() > 50 && !paint.usesVaryingCoords()) {
return GrOp::Make<GrStrokeTessellateOp>(context, aaType, viewMatrix, stroke, path,
std::move(paint));
} else {
@@ -261,13 +264,10 @@
}
if (args.fShape->style().isSimpleHairline()) {
- // Pre-transform the path into device space and use a stroke width of 1.
-#ifdef SK_DEBUG
// Since we will be transforming the path, just double check that we are still in a position
// where the paint will not use local coordinates.
- SkPMColor4f constantColor;
- SkASSERT(args.fPaint.isConstantBlendedColor(&constantColor));
-#endif
+ SkASSERT(!args.fPaint.usesVaryingCoords());
+ // Pre-transform the path into device space and use a stroke width of 1.
SkPath devPath;
path.transform(*args.fViewMatrix, &devPath);
SkStrokeRec devStroke = args.fShape->style().strokeRec();