Delete all low-level rendering from ccpr
This converts ccpr to just a poorly named atlas manager. The atlas
gets rendered by normal calls on its MSAA draw context:
for (;;) {
surfaceDrawContext->stencilPath(); // Stencil.
}
surfaceDrawContext->stencilRect(atlasBounds); // Cover.
Bug: chromium:1158093
Change-Id: I758ffd372b2ed5bb8b370156b6f80f6204146700
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/381618
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/GrClipStack.cpp b/src/gpu/GrClipStack.cpp
index 43f0bbf..166693d 100644
--- a/src/gpu/GrClipStack.cpp
+++ b/src/gpu/GrClipStack.cpp
@@ -8,6 +8,7 @@
#include "src/gpu/GrClipStack.h"
#include "include/core/SkMatrix.h"
+#include "src/core/SkPathPriv.h"
#include "src/core/SkRRectPriv.h"
#include "src/core/SkRectPriv.h"
#include "src/core/SkTaskGroup.h"
diff --git a/src/gpu/GrSurfaceDrawContext.cpp b/src/gpu/GrSurfaceDrawContext.cpp
index 7556d15..628932c 100644
--- a/src/gpu/GrSurfaceDrawContext.cpp
+++ b/src/gpu/GrSurfaceDrawContext.cpp
@@ -746,6 +746,43 @@
}
}
+bool GrSurfaceDrawContext::stencilPath(const GrHardClip* clip,
+ GrAA doStencilMSAA,
+ const SkMatrix& viewMatrix,
+ const SkPath& path) {
+ SkIRect clipBounds = clip ? clip->getConservativeBounds()
+ : SkIRect::MakeSize(this->dimensions());
+ GrStyledShape shape(path, GrStyledShape::DoSimplify::kNo);
+
+ GrPathRenderer::CanDrawPathArgs canDrawArgs;
+ canDrawArgs.fCaps = fContext->priv().caps();
+ canDrawArgs.fProxy = this->asRenderTargetProxy();
+ canDrawArgs.fClipConservativeBounds = &clipBounds;
+ canDrawArgs.fViewMatrix = &viewMatrix;
+ canDrawArgs.fShape = &shape;
+ canDrawArgs.fPaint = nullptr;
+ canDrawArgs.fAAType = (doStencilMSAA == GrAA::kYes) ? GrAAType::kMSAA : GrAAType::kNone;
+ canDrawArgs.fHasUserStencilSettings = false;
+ canDrawArgs.fTargetIsWrappedVkSecondaryCB = this->wrapsVkSecondaryCB();
+ GrPathRenderer* pr = this->drawingManager()->getPathRenderer(
+ canDrawArgs, false, GrPathRendererChain::DrawType::kStencil);
+ if (!pr) {
+ SkDebugf("WARNING: No path renderer to stencil path.\n");
+ return false;
+ }
+
+ GrPathRenderer::StencilPathArgs args;
+ args.fContext = fContext;
+ args.fRenderTargetContext = this;
+ args.fClip = clip;
+ args.fClipConservativeBounds = &clipBounds;
+ args.fViewMatrix = &viewMatrix;
+ args.fShape = &shape;
+ args.fDoStencilMSAA = doStencilMSAA;
+ pr->stencilPath(args);
+ return true;
+}
+
void GrSurfaceDrawContext::stencilPath(const GrHardClip* clip,
GrAA doStencilMSAA,
const SkMatrix& viewMatrix,
diff --git a/src/gpu/GrSurfaceDrawContext.h b/src/gpu/GrSurfaceDrawContext.h
index ceda8be..f67d61a 100644
--- a/src/gpu/GrSurfaceDrawContext.h
+++ b/src/gpu/GrSurfaceDrawContext.h
@@ -583,6 +583,15 @@
this->drawFilledQuad(clip, std::move(paint), doStencilMSAA, &quad, ss);
}
+ // Fills the user stencil bits with a non-zero value at every sample inside the path. This will
+ // likely be implemented with a Redbook algorithm, but it is not guaranteed. The samples being
+ // rendered to must be zero initially.
+ bool stencilPath(const GrHardClip*,
+ GrAA doStencilMSAA,
+ const SkMatrix& viewMatrix,
+ const SkPath&);
+
+ // Same as for stencilPath, but for an NVPR path object.
void stencilPath(const GrHardClip*,
GrAA doStencilMSAA,
const SkMatrix& viewMatrix,
diff --git a/src/gpu/GrTriangulator.cpp b/src/gpu/GrTriangulator.cpp
index 71f2349..2931f9a 100644
--- a/src/gpu/GrTriangulator.cpp
+++ b/src/gpu/GrTriangulator.cpp
@@ -1477,39 +1477,3 @@
vertexAllocator->unlock(actualCount);
return actualCount;
}
-
-int GrTriangulator::PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
- WindingVertex** verts) {
- SkArenaAlloc alloc(kArenaDefaultChunkSize);
- GrTriangulator triangulator(path, &alloc);
- bool isLinear;
- Poly* polys = triangulator.pathToPolys(tolerance, clipBounds, &isLinear);
- int64_t count64 = CountPoints(polys, path.getFillType());
- if (0 == count64 || count64 > SK_MaxS32) {
- *verts = nullptr;
- return 0;
- }
- int count = count64;
-
- *verts = new WindingVertex[count];
- WindingVertex* vertsEnd = *verts;
- SkPoint* points = new SkPoint[count];
- SkPoint* pointsEnd = points;
- for (Poly* poly = polys; poly; poly = poly->fNext) {
- if (apply_fill_type(path.getFillType(), poly)) {
- SkPoint* start = pointsEnd;
- pointsEnd = static_cast<SkPoint*>(triangulator.emitPoly(poly, pointsEnd));
- while (start != pointsEnd) {
- vertsEnd->fPos = *start;
- vertsEnd->fWinding = poly->fWinding;
- ++start;
- ++vertsEnd;
- }
- }
- }
- int actualCount = static_cast<int>(vertsEnd - *verts);
- SkASSERT(actualCount <= count);
- SkASSERT(pointsEnd - points == actualCount);
- delete[] points;
- return actualCount;
-}
diff --git a/src/gpu/GrTriangulator.h b/src/gpu/GrTriangulator.h
index b90bcc0..2b45a18 100644
--- a/src/gpu/GrTriangulator.h
+++ b/src/gpu/GrTriangulator.h
@@ -36,21 +36,6 @@
return count;
}
- struct WindingVertex {
- SkPoint fPos;
- int fWinding;
- };
-
- // *DEPRECATED*: Once CCPR is removed this method will go away.
- //
- // Triangulates a path to an array of vertices. Each triangle is represented as a set of three
- // WindingVertex entries, each of which contains the position and winding count (which is the
- // same for all three vertices of a triangle). The 'verts' out parameter is set to point to the
- // resultant vertex array. CALLER IS RESPONSIBLE for deleting this buffer to avoid a memory
- // leak!
- static int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
- WindingVertex** verts);
-
// Enums used by GrTriangulator internals.
typedef enum { kLeft_Side, kRight_Side } Side;
enum class EdgeType { kInner, kOuter, kConnector };
diff --git a/src/gpu/ccpr/GrAutoMapVertexBuffer.h b/src/gpu/ccpr/GrAutoMapVertexBuffer.h
deleted file mode 100644
index ff2b831..0000000
--- a/src/gpu/ccpr/GrAutoMapVertexBuffer.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2020 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrAutoMapVertexBuffer_DEFINED
-#define GrAutoMapVertexBuffer_DEFINED
-
-#include "include/private/SkNoncopyable.h"
-#include "src/gpu/GrGpuBuffer.h"
-#include "src/gpu/GrOnFlushResourceProvider.h"
-
-// This class automatically allocates and maps a GPU vertex buffer, and polyfills the mapping
-// functionality with a mirror buffer on CPU if it is not supported.
-class GrAutoMapVertexBuffer : SkNoncopyable {
-public:
- ~GrAutoMapVertexBuffer() {
- if (this->isMapped()) {
- this->unmapBuffer();
- }
- }
-
- bool hasGpuBuffer() const { return SkToBool(fGpuBuffer.get()); }
- sk_sp<const GrGpuBuffer> gpuBuffer() const { return fGpuBuffer; }
- bool isMapped() const { return SkToBool(fData); }
- void* data() const { SkASSERT(this->isMapped()); return fData; }
-
- void resetAndMapBuffer(GrOnFlushResourceProvider* onFlushRP, size_t sizeInBytes) {
- if (this->isMapped()) {
- this->unmapBuffer();
- }
- fGpuBuffer = onFlushRP->makeBuffer(GrGpuBufferType::kVertex, sizeInBytes);
- if (!fGpuBuffer) {
- fSizeInBytes = 0;
- fData = nullptr;
- return;
- }
- fSizeInBytes = sizeInBytes;
- fData = fGpuBuffer->map();
- if (!fData) {
- // Mapping failed. Allocate a mirror buffer on CPU.
- fData = sk_malloc_throw(fSizeInBytes);
- }
- }
-
- void unmapBuffer() {
- SkASSERT(this->isMapped());
- if (fGpuBuffer->isMapped()) {
- fGpuBuffer->unmap();
- } else {
- // fData is a mirror buffer on CPU.
- fGpuBuffer->updateData(fData, fSizeInBytes);
- sk_free(fData);
- }
- fData = nullptr;
- }
-
-protected:
- sk_sp<GrGpuBuffer> fGpuBuffer;
- size_t fSizeInBytes = 0;
- void* fData = nullptr;
-};
-
-template<typename T> class GrTAutoMapVertexBuffer : public GrAutoMapVertexBuffer {
-public:
- T& operator[](int idx) {
- SkASSERT(this->isMapped());
- SkASSERT(idx >= 0 && (size_t)idx < fSizeInBytes / sizeof(T));
- return ((T*)fData)[idx];
- }
-};
-
-#endif
diff --git a/src/gpu/ccpr/GrCCClipPath.cpp b/src/gpu/ccpr/GrCCClipPath.cpp
index eeeae99..510d3da 100644
--- a/src/gpu/ccpr/GrCCClipPath.cpp
+++ b/src/gpu/ccpr/GrCCClipPath.cpp
@@ -47,15 +47,12 @@
fAccessRect = accessRect;
}
-void GrCCClipPath::accountForOwnPath(GrCCPerFlushResourceSpecs* specs) const {
+void GrCCClipPath::accountForOwnPath(GrCCAtlas::Specs* specs) const {
SkASSERT(this->isInitialized());
- ++specs->fNumClipPaths;
- specs->fRenderedPathStats.statPath(fDeviceSpacePath);
-
SkIRect ibounds;
if (ibounds.intersect(fAccessRect, fPathDevIBounds)) {
- specs->fRenderedAtlasSpecs.accountForSpace(ibounds.width(), ibounds.height());
+ specs->accountForSpace(ibounds.width(), ibounds.height());
}
}
@@ -64,7 +61,7 @@
SkASSERT(this->isInitialized());
SkASSERT(!fHasAtlas);
fAtlas = resources->renderDeviceSpacePathInAtlas(
- fAccessRect, fDeviceSpacePath, fPathDevIBounds, GrFillRuleForSkPath(fDeviceSpacePath),
- &fDevToAtlasOffset);
+ onFlushRP, fAccessRect, fDeviceSpacePath, fPathDevIBounds,
+ GrFillRuleForSkPath(fDeviceSpacePath), &fDevToAtlasOffset);
SkDEBUGCODE(fHasAtlas = true);
}
diff --git a/src/gpu/ccpr/GrCCClipPath.h b/src/gpu/ccpr/GrCCClipPath.h
index 7128fc7..49e7071 100644
--- a/src/gpu/ccpr/GrCCClipPath.h
+++ b/src/gpu/ccpr/GrCCClipPath.h
@@ -58,7 +58,7 @@
return fPathDevIBounds;
}
- void accountForOwnPath(GrCCPerFlushResourceSpecs*) const;
+ void accountForOwnPath(GrCCAtlas::Specs*) const;
void renderPathInAtlas(GrCCPerFlushResources*, GrOnFlushResourceProvider*);
const SkIVector& atlasTranslate() const {
diff --git a/src/gpu/ccpr/GrCCConicShader.cpp b/src/gpu/ccpr/GrCCConicShader.cpp
deleted file mode 100644
index b22e848..0000000
--- a/src/gpu/ccpr/GrCCConicShader.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrCCConicShader.h"
-
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
-
-void GrCCConicShader::emitSetupCode(
- GrGLSLVertexGeoBuilder* s, const char* pts, const char** outHull4) const {
- // K is distance from the line P2 -> P0. L is distance from the line P0 -> P1, scaled by 2w.
- // M is distance from the line P1 -> P2, scaled by 2w. We do this in a space where P1=0.
- s->declareGlobal(fKLMMatrix);
- s->codeAppendf("float x0 = %s[0].x - %s[1].x, x2 = %s[2].x - %s[1].x;", pts, pts, pts, pts);
- s->codeAppendf("float y0 = %s[0].y - %s[1].y, y2 = %s[2].y - %s[1].y;", pts, pts, pts, pts);
- s->codeAppendf("float w = %s[3].x;", pts);
- s->codeAppendf("%s = float3x3(y2 - y0, x0 - x2, x2*y0 - x0*y2, "
- "2*w * float2(+y0, -x0), 0, "
- "2*w * float2(-y2, +x2), 0);", fKLMMatrix.c_str());
-
- s->declareGlobal(fControlPoint);
- s->codeAppendf("%s = %s[1];", fControlPoint.c_str(), pts);
-
- // Scale KLM by the inverse Manhattan width of K, and make sure K is positive. This allows K to
- // double as the flat opposite edge AA. kwidth will not be 0 because we cull degenerate conics
- // on the CPU.
- s->codeAppendf("float kwidth = 2*bloat * (abs(%s[0].x) + abs(%s[0].y)) * sign(%s[0].z);",
- fKLMMatrix.c_str(), fKLMMatrix.c_str(), fKLMMatrix.c_str());
- s->codeAppendf("%s *= 1/kwidth;", fKLMMatrix.c_str());
-
- if (outHull4) {
- // Clip the conic triangle by the tangent line at maximum height. Conics have the nice
- // property that maximum height always occurs at T=.5. This is a simple application for
- // De Casteljau's algorithm.
- s->codeAppendf("float2 p1w = %s[1]*w;", pts);
- s->codeAppend ("float r = 1 / (1 + w);");
- s->codeAppend ("float2 conic_hull[4];");
- s->codeAppendf("conic_hull[0] = %s[0];", pts);
- s->codeAppendf("conic_hull[1] = (%s[0] + p1w) * r;", pts);
- s->codeAppendf("conic_hull[2] = (p1w + %s[2]) * r;", pts);
- s->codeAppendf("conic_hull[3] = %s[2];", pts);
- *outHull4 = "conic_hull";
- }
-}
-
-void GrCCConicShader::onEmitVaryings(
- GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code,
- const char* position, const char* coverage, const char* cornerCoverage, const char* wind) {
- code->appendf("float3 klm = float3(%s - %s, 1) * %s;",
- position, fControlPoint.c_str(), fKLMMatrix.c_str());
- fKLM_fWind.reset(kFloat3_GrSLType, scope);
- varyingHandler->addVarying("klm", &fKLM_fWind);
- code->appendf("%s.xyz = klm;", OutName(fKLM_fWind));
-
- fGrad_fCorner.reset(cornerCoverage ? kFloat4_GrSLType : kFloat2_GrSLType, scope);
- varyingHandler->addVarying((cornerCoverage) ? "grad_and_corner" : "grad", &fGrad_fCorner);
- code->appendf("%s.xy = 2*bloat * (float3x2(%s) * float3(2*klm[0], -klm[2], -klm[1]));",
- OutName(fGrad_fCorner), fKLMMatrix.c_str());
-
-}
-
-void GrCCConicShader::emitSampleMaskCode(GrGLSLFPFragmentBuilder* f) const {
- f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z;",
- fKLM_fWind.fsIn(), fKLM_fWind.fsIn(), fKLM_fWind.fsIn());
- f->codeAppendf("float f = k*k - l*m;");
- f->codeAppendf("float2 grad = %s;", fGrad_fCorner.fsIn());
- f->applyFnToMultisampleMask("f", "grad", GrGLSLFPFragmentBuilder::ScopeFlags::kTopLevel);
-}
diff --git a/src/gpu/ccpr/GrCCConicShader.h b/src/gpu/ccpr/GrCCConicShader.h
deleted file mode 100644
index 1c07127..0000000
--- a/src/gpu/ccpr/GrCCConicShader.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCCConicShader_DEFINED
-#define GrCCConicShader_DEFINED
-
-#include "src/gpu/ccpr/GrCCCoverageProcessor.h"
-
-/**
- * This class renders the coverage of closed conic curves using the techniques outlined in
- * "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
- * Jim Blinn:
- *
- * https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
- *
- * The provided curves must be monotonic with respect to the vector of their closing edge [P2 - P0].
- * (Use GrCCGeometry::conicTo().)
- */
-class GrCCConicShader : public GrCCCoverageProcessor::Shader {
-public:
- bool calculatesOwnEdgeCoverage() const override { return true; }
-
- void emitSetupCode(
- GrGLSLVertexGeoBuilder*, const char* pts, const char** outHull4) const override;
-
- void onEmitVaryings(
- GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, const char* position,
- const char* coverage, const char* cornerCoverage, const char* wind) override;
-
- void emitSampleMaskCode(GrGLSLFPFragmentBuilder*) const override;
-
-private:
- const GrShaderVar fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType};
- const GrShaderVar fControlPoint{"control_point", kFloat2_GrSLType};
- GrGLSLVarying fKLM_fWind;
- GrGLSLVarying fGrad_fCorner;
-};
-
-#endif
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp
deleted file mode 100644
index 3ecf5a7..0000000
--- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrCCCoverageProcessor.h"
-
-#include "src/gpu/GrOpFlushState.h"
-#include "src/gpu/GrOpsRenderPass.h"
-#include "src/gpu/GrProgramInfo.h"
-#include "src/gpu/ccpr/GrCCConicShader.h"
-#include "src/gpu/ccpr/GrCCCubicShader.h"
-#include "src/gpu/ccpr/GrCCQuadraticShader.h"
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
-#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
-
-class GrCCCoverageProcessor::TriangleShader : public GrCCCoverageProcessor::Shader {
- void onEmitVaryings(
- GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code,
- const char* position, const char* coverage, const char* cornerCoverage,
- const char* /*wind*/) override {
- fCoverages.reset(kHalf_GrSLType, scope);
- varyingHandler->addVarying("coverage", &fCoverages);
- code->appendf("%s = %s;", OutName(fCoverages), coverage);
- }
-
- void emitSampleMaskCode(GrGLSLFPFragmentBuilder*) const override { return; }
-
- GrGLSLVarying fCoverages;
-};
-
-void GrCCCoverageProcessor::Shader::CalcWind(const GrCCCoverageProcessor& proc,
- GrGLSLVertexGeoBuilder* s, const char* pts,
- const char* outputWind) {
- if (3 == proc.numInputPoints()) {
- s->codeAppendf("float2 a = %s[0] - %s[1], "
- "b = %s[0] - %s[2];", pts, pts, pts, pts);
- } else {
- // All inputs are convex, so it's sufficient to just average the middle two input points.
- SkASSERT(4 == proc.numInputPoints());
- s->codeAppendf("float2 p12 = (%s[1] + %s[2]) * .5;", pts, pts);
- s->codeAppendf("float2 a = %s[0] - p12, "
- "b = %s[0] - %s[3];", pts, pts, pts);
- }
-
- s->codeAppend ("float area_x2 = determinant(float2x2(a, b));");
- if (proc.isTriangles()) {
- // We cull extremely thin triangles by zeroing wind. When a triangle gets too thin it's
- // possible for FP round-off error to actually give us the wrong winding direction, causing
- // rendering artifacts. The criteria we choose is "height <~ 1/1024". So we drop a triangle
- // if the max effect it can have on any single pixel is <~ 1/1024, or 1/4 of a bit in 8888.
- s->codeAppend ("float2 bbox_size = max(abs(a), abs(b));");
- s->codeAppend ("float basewidth = max(bbox_size.x + bbox_size.y, 1);");
- s->codeAppendf("%s = (abs(area_x2 * 1024) > basewidth) ? sign(half(area_x2)) : 0;",
- outputWind);
- } else {
- // We already converted nearly-flat curves to lines on the CPU, so no need to worry about
- // thin curve hulls at this point.
- s->codeAppendf("%s = sign(half(area_x2));", outputWind);
- }
-}
-
-void GrCCCoverageProcessor::Shader::CalcEdgeCoverageAtBloatVertex(GrGLSLVertexGeoBuilder* s,
- const char* leftPt,
- const char* rightPt,
- const char* rasterVertexDir,
- const char* outputCoverage) {
- // Here we find an edge's coverage at one corner of a conservative raster bloat box whose center
- // falls on the edge in question. (A bloat box is axis-aligned and the size of one pixel.) We
- // always set up coverage so it is -1 at the outermost corner, 0 at the innermost, and -.5 at
- // the center. Interpolated, these coverage values convert jagged conservative raster edges into
- // smooth antialiased edges.
- //
- // d1 == (P + sign(n) * bloat) dot n (Distance at the bloat box vertex whose
- // == P dot n + (abs(n.x) + abs(n.y)) * bloatSize coverage=-1, where the bloat box is
- // centered on P.)
- //
- // d0 == (P - sign(n) * bloat) dot n (Distance at the bloat box vertex whose
- // == P dot n - (abs(n.x) + abs(n.y)) * bloatSize coverage=0, where the bloat box is
- // centered on P.)
- //
- // d == (P + rasterVertexDir * bloatSize) dot n (Distance at the bloat box vertex whose
- // == P dot n + (rasterVertexDir dot n) * bloatSize coverage we wish to calculate.)
- //
- // coverage == -(d - d0) / (d1 - d0) (coverage=-1 at d=d1; coverage=0 at d=d0)
- //
- // == (rasterVertexDir dot n) / (abs(n.x) + abs(n.y)) * -.5 - .5
- //
- s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
- rightPt, leftPt, leftPt, rightPt);
- s->codeAppend ("float nwidth = abs(n.x) + abs(n.y);");
- s->codeAppendf("float t = dot(%s, n);", rasterVertexDir);
- // The below conditional guarantees we get exactly 1 on the divide when nwidth=t (in case the
- // GPU divides by multiplying by the reciprocal?) It also guards against NaN when nwidth=0.
- s->codeAppendf("%s = half(abs(t) != nwidth ? t / nwidth : sign(t)) * -.5 - .5;",
- outputCoverage);
-}
-
-void GrCCCoverageProcessor::Shader::CalcEdgeCoveragesAtBloatVertices(GrGLSLVertexGeoBuilder* s,
- const char* leftPt,
- const char* rightPt,
- const char* bloatDir1,
- const char* bloatDir2,
- const char* outputCoverages) {
- // See comments in CalcEdgeCoverageAtBloatVertex.
- s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
- rightPt, leftPt, leftPt, rightPt);
- s->codeAppend ("float nwidth = abs(n.x) + abs(n.y);");
- s->codeAppendf("float2 t = n * float2x2(%s, %s);", bloatDir1, bloatDir2);
- s->codeAppendf("for (int i = 0; i < 2; ++i) {");
- s->codeAppendf( "%s[i] = half(abs(t[i]) != nwidth ? t[i] / nwidth : sign(t[i])) * -.5 - .5;",
- outputCoverages);
- s->codeAppendf("}");
-}
-
-void GrCCCoverageProcessor::Shader::CalcCornerAttenuation(GrGLSLVertexGeoBuilder* s,
- const char* leftDir, const char* rightDir,
- const char* outputAttenuation) {
- // obtuseness = cos(corner_angle) if corner_angle > 90 degrees
- // 0 if corner_angle <= 90 degrees
- //
- // NOTE: leftDir and rightDir are normalized and point in the same direction the path was
- // defined with, i.e., leftDir points into the corner and rightDir points away from the corner.
- s->codeAppendf("half obtuseness = max(half(dot(%s, %s)), 0);", leftDir, rightDir);
-
- // axis_alignedness = 1 - tan(angle_to_nearest_axis_from_corner_bisector)
- // (i.e., 1 when the corner bisector is aligned with the x- or y-axis
- // 0 when the corner bisector falls on a 45 degree angle
- // 0..1 when the corner bisector falls somewhere in between
- s->codeAppendf("half2 abs_bisect_maybe_transpose = abs((0 == obtuseness) ? half2(%s - %s) : "
- "half2(%s + %s));",
- leftDir, rightDir, leftDir, rightDir);
- s->codeAppend ("half axis_alignedness = "
- "1 - min(abs_bisect_maybe_transpose.y, abs_bisect_maybe_transpose.x) / "
- "max(abs_bisect_maybe_transpose.x, abs_bisect_maybe_transpose.y);");
-
- // ninety_degreesness = sin^2(corner_angle)
- // sin^2 just because... it's always positive and the results looked better than plain sine... ?
- s->codeAppendf("half ninety_degreesness = determinant(half2x2(%s, %s));", leftDir, rightDir);
- s->codeAppend ("ninety_degreesness = ninety_degreesness * ninety_degreesness;");
-
- // The below formula is not smart. It was just arrived at by considering the following
- // observations:
- //
- // 1. 90-degree, axis-aligned corners have full attenuation along the bisector.
- // (i.e. coverage = 1 - distance_to_corner^2)
- // (i.e. outputAttenuation = 0)
- //
- // 2. 180-degree corners always have zero attenuation.
- // (i.e. coverage = 1 - distance_to_corner)
- // (i.e. outputAttenuation = 1)
- //
- // 3. 90-degree corners whose bisector falls on a 45 degree angle also do not attenuate.
- // (i.e. outputAttenuation = 1)
- s->codeAppendf("%s = max(obtuseness, axis_alignedness * ninety_degreesness);",
- outputAttenuation);
-}
-
-GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
- std::unique_ptr<Shader> shader;
- switch (fPrimitiveType) {
- case PrimitiveType::kTriangles:
- case PrimitiveType::kWeightedTriangles:
- shader = std::make_unique<TriangleShader>();
- break;
- case PrimitiveType::kQuadratics:
- shader = std::make_unique<GrCCQuadraticShader>();
- break;
- case PrimitiveType::kCubics:
- shader = std::make_unique<GrCCCubicShader>();
- break;
- case PrimitiveType::kConics:
- shader = std::make_unique<GrCCConicShader>();
- break;
- }
- return this->onCreateGLSLInstance(std::move(shader));
-}
-
-void GrCCCoverageProcessor::bindPipeline(GrOpFlushState* flushState, const GrPipeline& pipeline,
- const SkRect& drawBounds,
- const GrUserStencilSettings* stencil) const {
- GrProgramInfo programInfo(flushState->writeView(), &pipeline, stencil, this,
- this->primType(), 0, flushState->renderPassBarriers(),
- flushState->colorLoadOp());
- flushState->bindPipeline(programInfo, drawBounds);
-}
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h
deleted file mode 100644
index ed32261..0000000
--- a/src/gpu/ccpr/GrCCCoverageProcessor.h
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCCCoverageProcessor_DEFINED
-#define GrCCCoverageProcessor_DEFINED
-
-#include "include/private/SkNx.h"
-#include "src/gpu/GrCaps.h"
-#include "src/gpu/GrGeometryProcessor.h"
-#include "src/gpu/GrPipeline.h"
-#include "src/gpu/GrShaderCaps.h"
-#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
-#include "src/gpu/glsl/GrGLSLShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLVarying.h"
-
-class GrGLSLFPFragmentBuilder;
-class GrGLSLVertexGeoBuilder;
-class GrOpFlushState;
-
-/**
- * This is the geometry processor for the simple convex primitive shapes (triangles and closed,
- * convex bezier curves) from which ccpr paths are composed. The output is a single-channel alpha
- * value, positive for clockwise shapes and negative for counter-clockwise, that indicates coverage.
- *
- * The caller is responsible to draw all primitives as produced by GrCCGeometry into a cleared,
- * floating point, alpha-only render target using SkBlendMode::kPlus. Once all of a path's
- * primitives have been drawn, the render target contains a composite coverage count that can then
- * be used to draw the path (see GrCCPathProcessor).
- *
- * To draw primitives, use appendMesh() and draw() (defined below).
- */
-class GrCCCoverageProcessor : public GrGeometryProcessor {
-public:
- enum class PrimitiveType {
- kTriangles,
- kWeightedTriangles, // Triangles (from the tessellator) whose winding magnitude > 1.
- kQuadratics,
- kCubics,
- kConics
- };
- static const char* PrimitiveTypeName(PrimitiveType);
-
- // Defines a single primitive shape with 3 input points (i.e. Triangles and Quadratics).
- // X,Y point values are transposed.
- struct TriPointInstance {
- float fValues[6];
-
- enum class Ordering : bool {
- kXYTransposed,
- kXYInterleaved,
- };
-
- void set(const SkPoint[3], const Sk2f& translate, Ordering);
- void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& translate, Ordering);
- void set(const Sk2f& P0, const Sk2f& P1, const Sk2f& P2, const Sk2f& translate, Ordering);
- };
-
- // Defines a single primitive shape with 4 input points, or 3 input points plus a "weight"
- // parameter duplicated in both lanes of the 4th input (i.e. Cubics, Conics, and Triangles with
- // a weighted winding number). X,Y point values are transposed.
- struct QuadPointInstance {
- float fX[4];
- float fY[4];
-
- void set(const SkPoint[4], float dx, float dy);
- void setW(const SkPoint[3], const Sk2f& trans, float w);
- void setW(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w);
- void setW(const Sk2f& P0, const Sk2f& P1, const Sk2f& P2, const Sk2f& trans, float w);
- };
-
- PrimitiveType primitiveType() const { return fPrimitiveType; }
-
- // Number of bezier points for curves, or 3 for triangles.
- int numInputPoints() const { return PrimitiveType::kCubics == fPrimitiveType ? 4 : 3; }
-
- bool isTriangles() const {
- return PrimitiveType::kTriangles == fPrimitiveType ||
- PrimitiveType::kWeightedTriangles == fPrimitiveType;
- }
-
- int hasInputWeight() const {
- return PrimitiveType::kWeightedTriangles == fPrimitiveType ||
- PrimitiveType::kConics == fPrimitiveType;
- }
-
- // GrPrimitiveProcessor overrides.
- const char* name() const override { return PrimitiveTypeName(fPrimitiveType); }
- void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
- SkDEBUGCODE(this->getDebugBloatKey(b));
- b->add32((int)fPrimitiveType);
- }
- GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
-
-#ifdef SK_DEBUG
- // Increases the 1/2 pixel AA bloat by a factor of debugBloat.
- void enableDebugBloat(float debugBloat) { fDebugBloat = debugBloat; }
- bool debugBloatEnabled() const { return fDebugBloat > 0; }
- float debugBloat() const { SkASSERT(this->debugBloatEnabled()); return fDebugBloat; }
- void getDebugBloatKey(GrProcessorKeyBuilder* b) const {
- uint32_t bloatBits;
- memcpy(&bloatBits, &fDebugBloat, 4);
- b->add32(bloatBits);
- }
-#endif
-
- // The caller uses these methods to actualy draw the coverage PrimitiveTypes. For each
- // subpassIdx of each PrimitiveType, it calls reset/bind*/drawInstances.
- virtual int numSubpasses() const = 0;
- virtual void reset(PrimitiveType, int subpassIdx, GrResourceProvider*) = 0;
- void bindPipeline(GrOpFlushState*, const GrPipeline&, const SkRect& drawBounds,
- const GrUserStencilSettings* = &GrUserStencilSettings::kUnused) const;
- virtual void bindBuffers(GrOpsRenderPass*, sk_sp<const GrBuffer> instanceBuffer) const = 0;
- virtual void drawInstances(GrOpsRenderPass*, int instanceCount, int baseInstance) const = 0;
-
- // The Shader provides code to calculate each pixel's coverage in a RenderPass. It also
- // provides details about shape-specific geometry.
- class Shader {
- public:
- // Returns true if the Impl should not calculate the coverage argument for emitVaryings().
- // If true, then "coverage" will have a signed magnitude of 1.
- virtual bool calculatesOwnEdgeCoverage() const { return false; }
-
- // Called before generating geometry. Subclasses may set up internal member variables during
- // this time that will be needed during onEmitVaryings (e.g. transformation matrices).
- //
- // If the 'outHull4' parameter is provided, and there are not 4 input points, the subclass
- // is required to fill it with the name of a 4-point hull around which the Impl can generate
- // its geometry. If it is left unchanged, the Impl will use the regular input points.
- virtual void emitSetupCode(
- GrGLSLVertexGeoBuilder*, const char* pts, const char** outHull4 = nullptr) const {
- SkASSERT(!outHull4);
- }
-
- void emitVaryings(
- GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code,
- const char* position, const char* coverage, const char* cornerCoverage,
- const char* wind) {
- SkASSERT(GrGLSLVarying::Scope::kVertToGeo != scope);
- this->onEmitVaryings(
- varyingHandler, scope, code, position, coverage, cornerCoverage, wind);
- }
-
- // Assigns the built-in sample mask at the current pixel.
- virtual void emitSampleMaskCode(GrGLSLFPFragmentBuilder*) const = 0;
-
- // Calculates the winding direction of the input points (+1, -1, or 0). Wind for extremely
- // thin triangles gets rounded to zero.
- static void CalcWind(const GrCCCoverageProcessor&, GrGLSLVertexGeoBuilder*, const char* pts,
- const char* outputWind);
-
- // Calculates an edge's coverage at a conservative raster vertex. The edge is defined by two
- // clockwise-ordered points, 'leftPt' and 'rightPt'. 'rasterVertexDir' is a pair of +/-1
- // values that point in the direction of conservative raster bloat, starting from an
- // endpoint.
- //
- // Coverage values ramp from -1 (completely outside the edge) to 0 (completely inside).
- static void CalcEdgeCoverageAtBloatVertex(GrGLSLVertexGeoBuilder*, const char* leftPt,
- const char* rightPt, const char* rasterVertexDir,
- const char* outputCoverage);
-
- // Calculates an edge's coverage at two conservative raster vertices.
- // (See CalcEdgeCoverageAtBloatVertex).
- static void CalcEdgeCoveragesAtBloatVertices(GrGLSLVertexGeoBuilder*, const char* leftPt,
- const char* rightPt, const char* bloatDir1,
- const char* bloatDir2,
- const char* outputCoverages);
-
- // Corner boxes require an additional "attenuation" varying that is multiplied by the
- // regular (linearly-interpolated) coverage. This function calculates the attenuation value
- // to use in the single, outermost vertex. The remaining three vertices of the corner box
- // all use an attenuation value of 1.
- static void CalcCornerAttenuation(GrGLSLVertexGeoBuilder*, const char* leftDir,
- const char* rightDir, const char* outputAttenuation);
-
- virtual ~Shader() {}
-
- protected:
- // Here the subclass adds its internal varyings to the handler and produces code to
- // initialize those varyings from a given position and coverage values.
- //
- // NOTE: the coverage values are signed appropriately for wind.
- // 'coverage' will only be +1 or -1 on curves.
- virtual void onEmitVaryings(
- GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, const char* position,
- const char* coverage, const char* cornerCoverage, const char* wind) = 0;
-
- // Returns the name of a Shader's internal varying at the point where where its value is
- // assigned. This is intended to work whether called for a vertex or a geometry shader.
- const char* OutName(const GrGLSLVarying& varying) const {
- using Scope = GrGLSLVarying::Scope;
- SkASSERT(Scope::kVertToGeo != varying.scope());
- return Scope::kGeoToFrag == varying.scope() ? varying.gsOut() : varying.vsOut();
- }
-
- // Our friendship with GrGLSLShaderBuilder does not propagate to subclasses.
- inline static SkString& AccessCodeString(GrGLSLShaderBuilder* s) { return s->code(); }
- };
-
-protected:
- // Slightly undershoot a bloat radius of 0.5 so vertices that fall on integer boundaries don't
- // accidentally bleed into neighbor pixels.
- static constexpr float kAABloatRadius = 0.491111f;
-
- GrCCCoverageProcessor(ClassID classID) : INHERITED(classID) {}
-
- virtual GrPrimitiveType primType() const = 0;
-
- virtual GrGLSLPrimitiveProcessor* onCreateGLSLInstance(std::unique_ptr<Shader>) const = 0;
-
- // Our friendship with GrGLSLShaderBuilder does not propagate to subclasses.
- inline static SkString& AccessCodeString(GrGLSLShaderBuilder* s) { return s->code(); }
-
- PrimitiveType fPrimitiveType;
- SkDEBUGCODE(float fDebugBloat = 0);
-
- class TriangleShader;
-
- using INHERITED = GrGeometryProcessor;
-};
-
-inline const char* GrCCCoverageProcessor::PrimitiveTypeName(PrimitiveType type) {
- switch (type) {
- case PrimitiveType::kTriangles: return "kTriangles";
- case PrimitiveType::kWeightedTriangles: return "kWeightedTriangles";
- case PrimitiveType::kQuadratics: return "kQuadratics";
- case PrimitiveType::kCubics: return "kCubics";
- case PrimitiveType::kConics: return "kConics";
- }
- SK_ABORT("Invalid PrimitiveType");
-}
-
-inline void GrCCCoverageProcessor::TriPointInstance::set(
- const SkPoint p[3], const Sk2f& translate, Ordering ordering) {
- this->set(p[0], p[1], p[2], translate, ordering);
-}
-
-inline void GrCCCoverageProcessor::TriPointInstance::set(
- const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, const Sk2f& translate,
- Ordering ordering) {
- Sk2f P0 = Sk2f::Load(&p0);
- Sk2f P1 = Sk2f::Load(&p1);
- Sk2f P2 = Sk2f::Load(&p2);
- this->set(P0, P1, P2, translate, ordering);
-}
-
-inline void GrCCCoverageProcessor::TriPointInstance::set(
- const Sk2f& P0, const Sk2f& P1, const Sk2f& P2, const Sk2f& translate, Ordering ordering) {
- if (Ordering::kXYTransposed == ordering) {
- Sk2f::Store3(fValues, P0 + translate, P1 + translate, P2 + translate);
- } else {
- (P0 + translate).store(fValues);
- (P1 + translate).store(fValues + 2);
- (P2 + translate).store(fValues + 4);
- }
-}
-
-inline void GrCCCoverageProcessor::QuadPointInstance::set(const SkPoint p[4], float dx, float dy) {
- Sk4f X,Y;
- Sk4f::Load2(p, &X, &Y);
- (X + dx).store(&fX);
- (Y + dy).store(&fY);
-}
-
-inline void GrCCCoverageProcessor::QuadPointInstance::setW(const SkPoint p[3], const Sk2f& trans,
- float w) {
- this->setW(p[0], p[1], p[2], trans, w);
-}
-
-inline void GrCCCoverageProcessor::QuadPointInstance::setW(const SkPoint& p0, const SkPoint& p1,
- const SkPoint& p2, const Sk2f& trans,
- float w) {
- Sk2f P0 = Sk2f::Load(&p0);
- Sk2f P1 = Sk2f::Load(&p1);
- Sk2f P2 = Sk2f::Load(&p2);
- this->setW(P0, P1, P2, trans, w);
-}
-
-inline void GrCCCoverageProcessor::QuadPointInstance::setW(const Sk2f& P0, const Sk2f& P1,
- const Sk2f& P2, const Sk2f& trans,
- float w) {
- Sk2f W = Sk2f(w);
- Sk2f::Store4(this, P0 + trans, P1 + trans, P2 + trans, W);
-}
-
-#endif
diff --git a/src/gpu/ccpr/GrCCCubicShader.cpp b/src/gpu/ccpr/GrCCCubicShader.cpp
deleted file mode 100644
index 67201ed..0000000
--- a/src/gpu/ccpr/GrCCCubicShader.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrCCCubicShader.h"
-
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
-#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
-
-using Shader = GrCCCoverageProcessor::Shader;
-
-void GrCCCubicShader::emitSetupCode(
- GrGLSLVertexGeoBuilder* s, const char* pts, const char** /*outHull4*/) const {
- // Find the cubic's power basis coefficients.
- s->codeAppendf("float2x4 C = float4x4(-1, 3, -3, 1, "
- " 3, -6, 3, 0, "
- "-3, 3, 0, 0, "
- " 1, 0, 0, 0) * transpose(%s);", pts);
-
- // Find the cubic's inflection function.
- s->codeAppend ("float D3 = +determinant(float2x2(C[0].yz, C[1].yz));");
- s->codeAppend ("float D2 = -determinant(float2x2(C[0].xz, C[1].xz));");
- s->codeAppend ("float D1 = +determinant(float2x2(C));");
-
- // Shift the exponents in D so the largest magnitude falls somewhere in 1..2. This protects us
- // from overflow while solving for roots and KLM functionals.
- s->codeAppend ("float Dmax = max(max(abs(D1), abs(D2)), abs(D3));");
- s->codeAppend ("float norm;");
- if (s->getProgramBuilder()->shaderCaps()->fpManipulationSupport()) {
- s->codeAppend ("int exp;");
- s->codeAppend ("frexp(Dmax, exp);");
- s->codeAppend ("norm = ldexp(1, 1 - exp);");
- } else {
- s->codeAppend ("norm = 1/Dmax;"); // Dmax will not be 0 because we cull line cubics on CPU.
- }
- s->codeAppend ("D3 *= norm;");
- s->codeAppend ("D2 *= norm;");
- s->codeAppend ("D1 *= norm;");
-
- // Calculate the KLM matrix.
- s->declareGlobal(fKLMMatrix);
- s->codeAppend ("float discr = 3*D2*D2 - 4*D1*D3;");
- s->codeAppend ("float x = discr >= 0 ? 3 : 1;");
- s->codeAppend ("float q = sqrt(x * abs(discr));");
- s->codeAppend ("q = x*D2 + (D2 >= 0 ? q : -q);");
-
- s->codeAppend ("float2 l, m;");
- s->codeAppend ("l.ts = float2(q, 2*x * D1);");
- s->codeAppend ("m.ts = float2(2, q) * (discr >= 0 ? float2(D3, 1) "
- ": float2(D2*D2 - D3*D1, D1));");
-
- s->codeAppend ("float4 K;");
- s->codeAppend ("float4 lm = l.sstt * m.stst;");
- s->codeAppend ("K = float4(0, lm.x, -lm.y - lm.z, lm.w);");
-
- s->codeAppend ("float4 L, M;");
- s->codeAppend ("lm.yz += 2*lm.zy;");
- s->codeAppend ("L = float4(-1,x,-x,1) * l.sstt * (discr >= 0 ? l.ssst * l.sttt : lm);");
- s->codeAppend ("M = float4(-1,x,-x,1) * m.sstt * (discr >= 0 ? m.ssst * m.sttt : lm.xzyw);");
-
- s->codeAppend ("int middlerow = abs(D2) > abs(D1) ? 2 : 1;");
- s->codeAppend ("float3x3 CI = inverse(float3x3(C[0][0], C[0][middlerow], C[0][3], "
- "C[1][0], C[1][middlerow], C[1][3], "
- " 0, 0, 1));");
- s->codeAppendf("%s = CI * float3x3(K[0], K[middlerow], K[3], "
- "L[0], L[middlerow], L[3], "
- "M[0], M[middlerow], M[3]);", fKLMMatrix.c_str());
-
- // Evaluate the cubic at T=.5 for a mid-ish point.
- s->codeAppendf("float2 midpoint = %s * float4(.125, .375, .375, .125);", pts);
-
- // Orient the KLM matrix so L & M are both positive on the side of the curve we wish to fill.
- s->codeAppendf("float2 orientation = sign(float3(midpoint, 1) * float2x3(%s[1], %s[2]));",
- fKLMMatrix.c_str(), fKLMMatrix.c_str());
- s->codeAppendf("%s *= float3x3(orientation[0] * orientation[1], 0, 0, "
- "0, orientation[0], 0, "
- "0, 0, orientation[1]);", fKLMMatrix.c_str());
-}
-
-void GrCCCubicShader::onEmitVaryings(
- GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code,
- const char* position, const char* coverage, const char* cornerCoverage, const char* wind) {
- code->appendf("float3 klm = float3(%s, 1) * %s;", position, fKLMMatrix.c_str());
- fKLM_fEdge.reset(kFloat3_GrSLType, scope);
- varyingHandler->addVarying("klm", &fKLM_fEdge);
- code->appendf("%s = klm;", OutName(fKLM_fEdge));
-
- fGradMatrix.reset(kFloat4_GrSLType, scope);
- varyingHandler->addVarying("grad_matrix", &fGradMatrix);
- code->appendf("%s.xy = 2*bloat * 3 * klm[0] * %s[0].xy;",
- OutName(fGradMatrix), fKLMMatrix.c_str());
- code->appendf("%s.zw = -2*bloat * (klm[1] * %s[2].xy + klm[2] * %s[1].xy);",
- OutName(fGradMatrix), fKLMMatrix.c_str(), fKLMMatrix.c_str());
-}
-
-void GrCCCubicShader::emitSampleMaskCode(GrGLSLFPFragmentBuilder* f) const {
- f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z;",
- fKLM_fEdge.fsIn(), fKLM_fEdge.fsIn(), fKLM_fEdge.fsIn());
- f->codeAppendf("float f = k*k*k - l*m;");
- f->codeAppendf("float2x2 grad_matrix = float2x2(%s);", fGradMatrix.fsIn());
- f->codeAppendf("float2 grad = grad_matrix * float2(k, 1);");
- f->applyFnToMultisampleMask("f", "grad", GrGLSLFPFragmentBuilder::ScopeFlags::kTopLevel);
-}
diff --git a/src/gpu/ccpr/GrCCCubicShader.h b/src/gpu/ccpr/GrCCCubicShader.h
deleted file mode 100644
index 76c14de..0000000
--- a/src/gpu/ccpr/GrCCCubicShader.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCCCubicShader_DEFINED
-#define GrCCCubicShader_DEFINED
-
-#include "src/gpu/ccpr/GrCCCoverageProcessor.h"
-
-/**
- * This class renders the coverage of convex closed cubic segments using the techniques outlined in
- * "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
- * Jim Blinn:
- *
- * https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
- *
- * The provided curve segments must be convex, monotonic with respect to the vector of their closing
- * edge [P3 - P0], and must not contain or be near any inflection points or loop intersections.
- * (Use GrCCGeometry::cubicTo().)
- */
-class GrCCCubicShader : public GrCCCoverageProcessor::Shader {
-public:
- void emitSetupCode(
- GrGLSLVertexGeoBuilder*, const char* pts, const char** outHull4) const override;
-
- void onEmitVaryings(
- GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, const char* position,
- const char* coverage, const char* cornerCoverage, const char* wind) override;
-
- void emitSampleMaskCode(GrGLSLFPFragmentBuilder*) const override;
-
-private:
- const GrShaderVar fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType};
- GrGLSLVarying fKLM_fEdge;
- GrGLSLVarying fGradMatrix;
- GrGLSLVarying fCornerCoverage;
-};
-
-#endif
diff --git a/src/gpu/ccpr/GrCCFillGeometry.cpp b/src/gpu/ccpr/GrCCFillGeometry.cpp
deleted file mode 100644
index 68ad448..0000000
--- a/src/gpu/ccpr/GrCCFillGeometry.cpp
+++ /dev/null
@@ -1,802 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrCCFillGeometry.h"
-
-#include "include/gpu/GrTypes.h"
-#include "src/core/SkGeometry.h"
-#include <algorithm>
-#include <cmath>
-#include <cstdlib>
-
-static constexpr float kFlatnessThreshold = 1/16.f; // 1/16 of a pixel.
-
-void GrCCFillGeometry::beginPath() {
- SkASSERT(!fBuildingContour);
- fVerbs.push_back(Verb::kBeginPath);
-}
-
-void GrCCFillGeometry::beginContour(const SkPoint& pt) {
- SkASSERT(!fBuildingContour);
- // Store the current verb count in the fTriangles field for now. When we close the contour we
- // will use this value to calculate the actual number of triangles in its fan.
- fCurrContourTallies = {fVerbs.count(), 0, 0, 0, 0};
-
- fPoints.push_back(pt);
- fVerbs.push_back(Verb::kBeginContour);
- fCurrAnchorPoint = pt;
-
- SkDEBUGCODE(fBuildingContour = true);
-}
-
-void GrCCFillGeometry::lineTo(const SkPoint P[2]) {
- SkASSERT(fBuildingContour);
- SkASSERT(P[0] == fPoints.back());
- Sk2f p0 = Sk2f::Load(P);
- Sk2f p1 = Sk2f::Load(P+1);
- this->appendLine(p0, p1);
-}
-
-inline void GrCCFillGeometry::appendLine(const Sk2f& p0, const Sk2f& p1) {
- SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
- if ((p0 == p1).allTrue()) {
- return;
- }
- p1.store(&fPoints.push_back());
- fVerbs.push_back(Verb::kLineTo);
-}
-
-static inline Sk2f normalize(const Sk2f& n) {
- Sk2f nn = n*n;
- return n * (nn + SkNx_shuffle<1,0>(nn)).rsqrt();
-}
-
-static inline float dot(const Sk2f& a, const Sk2f& b) {
- float product[2];
- (a * b).store(product);
- return product[0] + product[1];
-}
-
-static inline bool are_collinear(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
- float tolerance = kFlatnessThreshold) {
- Sk2f l = p2 - p0; // Line from p0 -> p2.
-
- // lwidth = Manhattan width of l.
- Sk2f labs = l.abs();
- float lwidth = labs[0] + labs[1];
-
- // d = |p1 - p0| dot | l.y|
- // |-l.x| = distance from p1 to l.
- Sk2f dd = (p1 - p0) * SkNx_shuffle<1,0>(l);
- float d = dd[0] - dd[1];
-
- // We are collinear if a box with radius "tolerance", centered on p1, touches the line l.
- // To decide this, we check if the distance from p1 to the line is less than the distance from
- // p1 to the far corner of this imaginary box, along that same normal vector.
- // The far corner of the box can be found at "p1 + sign(n) * tolerance", where n is normal to l:
- //
- // abs(dot(p1 - p0, n)) <= dot(sign(n) * tolerance, n)
- //
- // Which reduces to:
- //
- // abs(d) <= (n.x * sign(n.x) + n.y * sign(n.y)) * tolerance
- // abs(d) <= (abs(n.x) + abs(n.y)) * tolerance
- //
- // Use "<=" in case l == 0.
- return std::abs(d) <= lwidth * tolerance;
-}
-
-static inline bool are_collinear(const SkPoint P[4], float tolerance = kFlatnessThreshold) {
- Sk4f Px, Py; // |Px Py| |p0 - p3|
- Sk4f::Load2(P, &Px, &Py); // |. . | = |p1 - p3|
- Px -= Px[3]; // |. . | |p2 - p3|
- Py -= Py[3]; // |. . | | 0 |
-
- // Find [lx, ly] = the line from p3 to the furthest-away point from p3.
- Sk4f Pwidth = Px.abs() + Py.abs(); // Pwidth = Manhattan width of each point.
- int lidx = Pwidth[0] > Pwidth[1] ? 0 : 1;
- lidx = Pwidth[lidx] > Pwidth[2] ? lidx : 2;
- float lx = Px[lidx], ly = Py[lidx];
- float lwidth = Pwidth[lidx]; // lwidth = Manhattan width of [lx, ly].
-
- // |Px Py|
- // d = |. . | * | ly| = distances from each point to l (two of the distances will be zero).
- // |. . | |-lx|
- // |. . |
- Sk4f d = Px*ly - Py*lx;
-
- // We are collinear if boxes with radius "tolerance", centered on all 4 points all touch line l.
- // (See the rationale for this formula in the above, 3-point version of this function.)
- // Use "<=" in case l == 0.
- return (d.abs() <= lwidth * tolerance).allTrue();
-}
-
-// Returns whether the (convex) curve segment is monotonic with respect to [endPt - startPt].
-static inline bool is_convex_curve_monotonic(const Sk2f& startPt, const Sk2f& tan0,
- const Sk2f& endPt, const Sk2f& tan1) {
- Sk2f v = endPt - startPt;
- float dot0 = dot(tan0, v);
- float dot1 = dot(tan1, v);
-
- // A small, negative tolerance handles floating-point error in the case when one tangent
- // approaches 0 length, meaning the (convex) curve segment is effectively a flat line.
- float tolerance = -std::max(std::abs(dot0), std::abs(dot1)) * SK_ScalarNearlyZero;
- return dot0 >= tolerance && dot1 >= tolerance;
-}
-
-template<int N> static inline SkNx<N,float> lerp(const SkNx<N,float>& a, const SkNx<N,float>& b,
- const SkNx<N,float>& t) {
- return SkNx_fma(t, b - a, a);
-}
-
-void GrCCFillGeometry::quadraticTo(const SkPoint P[3]) {
- SkASSERT(fBuildingContour);
- SkASSERT(P[0] == fPoints.back());
- Sk2f p0 = Sk2f::Load(P);
- Sk2f p1 = Sk2f::Load(P+1);
- Sk2f p2 = Sk2f::Load(P+2);
-
- // Don't crunch on the curve if it is nearly flat (or just very small). Flat curves can break
- // The monotonic chopping math.
- if (are_collinear(p0, p1, p2)) {
- this->appendLine(p0, p2);
- return;
- }
-
- this->appendQuadratics(p0, p1, p2);
-}
-
-inline void GrCCFillGeometry::appendQuadratics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2) {
- Sk2f tan0 = p1 - p0;
- Sk2f tan1 = p2 - p1;
-
- // This should almost always be this case for well-behaved curves in the real world.
- if (is_convex_curve_monotonic(p0, tan0, p2, tan1)) {
- this->appendMonotonicQuadratic(p0, p1, p2);
- return;
- }
-
- // Chop the curve into two segments with equal curvature. To do this we find the T value whose
- // tangent angle is halfway between tan0 and tan1.
- Sk2f n = normalize(tan0) - normalize(tan1);
-
- // The midtangent can be found where (dQ(t) dot n) = 0:
- //
- // 0 = (dQ(t) dot n) = | 2*t 1 | * | p0 - 2*p1 + p2 | * | n |
- // | -2*p0 + 2*p1 | | . |
- //
- // = | 2*t 1 | * | tan1 - tan0 | * | n |
- // | 2*tan0 | | . |
- //
- // = 2*t * ((tan1 - tan0) dot n) + (2*tan0 dot n)
- //
- // t = (tan0 dot n) / ((tan0 - tan1) dot n)
- Sk2f dQ1n = (tan0 - tan1) * n;
- Sk2f dQ0n = tan0 * n;
- Sk2f t = (dQ0n + SkNx_shuffle<1,0>(dQ0n)) / (dQ1n + SkNx_shuffle<1,0>(dQ1n));
- t = Sk2f::Min(Sk2f::Max(t, 0), 1); // Clamp for FP error.
-
- Sk2f p01 = SkNx_fma(t, tan0, p0);
- Sk2f p12 = SkNx_fma(t, tan1, p1);
- Sk2f p012 = lerp(p01, p12, t);
-
- this->appendMonotonicQuadratic(p0, p01, p012);
- this->appendMonotonicQuadratic(p012, p12, p2);
-}
-
-inline void GrCCFillGeometry::appendMonotonicQuadratic(const Sk2f& p0, const Sk2f& p1,
- const Sk2f& p2) {
- // Don't send curves to the GPU if we know they are nearly flat (or just very small).
- if (are_collinear(p0, p1, p2)) {
- this->appendLine(p0, p2);
- return;
- }
-
- SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
- SkASSERT((p0 != p2).anyTrue());
- p1.store(&fPoints.push_back());
- p2.store(&fPoints.push_back());
- fVerbs.push_back(Verb::kMonotonicQuadraticTo);
- ++fCurrContourTallies.fQuadratics;
-}
-
-static inline Sk2f first_unless_nearly_zero(const Sk2f& a, const Sk2f& b) {
- Sk2f aa = a*a;
- aa += SkNx_shuffle<1,0>(aa);
- SkASSERT(aa[0] == aa[1]);
-
- Sk2f bb = b*b;
- bb += SkNx_shuffle<1,0>(bb);
- SkASSERT(bb[0] == bb[1]);
-
- return (aa > bb * SK_ScalarNearlyZero).thenElse(a, b);
-}
-
-static inline void get_cubic_tangents(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
- const Sk2f& p3, Sk2f* tan0, Sk2f* tan1) {
- *tan0 = first_unless_nearly_zero(p1 - p0, p2 - p0);
- *tan1 = first_unless_nearly_zero(p3 - p2, p3 - p1);
-}
-
-static inline bool is_cubic_nearly_quadratic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
- const Sk2f& p3, const Sk2f& tan0, const Sk2f& tan1,
- Sk2f* c) {
- Sk2f c1 = SkNx_fma(Sk2f(1.5f), tan0, p0);
- Sk2f c2 = SkNx_fma(Sk2f(-1.5f), tan1, p3);
- *c = (c1 + c2) * .5f; // Hopefully optimized out if not used?
- return ((c1 - c2).abs() <= 1).allTrue();
-}
-
-enum class ExcludedTerm : bool {
- kQuadraticTerm,
- kLinearTerm
-};
-
-// Finds where to chop a non-loop around its inflection points. The resulting cubic segments will be
-// chopped such that a box of radius 'padRadius', centered at any point along the curve segment, is
-// guaranteed to not cross the tangent lines at the inflection points (a.k.a lines L & M).
-//
-// 'chops' will be filled with 0, 2, or 4 T values. The segments between T0..T1 and T2..T3 must be
-// drawn with flat lines instead of cubics.
-//
-// A serpentine cubic has two inflection points, so this method takes Sk2f and computes the padding
-// for both in SIMD.
-static inline void find_chops_around_inflection_points(float padRadius, Sk2f tl, Sk2f sl,
- const Sk2f& C0, const Sk2f& C1,
- ExcludedTerm skipTerm, float Cdet,
- SkSTArray<4, float>* chops) {
- SkASSERT(chops->empty());
- SkASSERT(padRadius >= 0);
-
- padRadius /= std::abs(Cdet); // Scale this single value rather than all of C^-1 later on.
-
- // The homogeneous parametric functions for distance from lines L & M are:
- //
- // l(t,s) = (t*sl - s*tl)^3
- // m(t,s) = (t*sm - s*tm)^3
- //
- // See "Resolution Independent Curve Rendering using Programmable Graphics Hardware",
- // 4.3 Finding klmn:
- //
- // https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
- //
- // From here on we use Sk2f with "L" names, but the second lane will be for line M.
- tl = (sl > 0).thenElse(tl, -tl); // Tl=tl/sl is the triple root of l(t,s). Normalize so s >= 0.
- sl = sl.abs();
-
- // Convert l(t,s), m(t,s) to power-basis form:
- //
- // | l3 m3 |
- // |l(t,s) m(t,s)| = |t^3 t^2*s t*s^2 s^3| * | l2 m2 |
- // | l1 m1 |
- // | l0 m0 |
- //
- Sk2f l3 = sl*sl*sl;
- Sk2f l2or1 = (ExcludedTerm::kLinearTerm == skipTerm) ? sl*sl*tl*-3 : sl*tl*tl*3;
-
- // The equation for line L can be found as follows:
- //
- // L = C^-1 * (l excluding skipTerm)
- //
- // (See comments for GrPathUtils::calcCubicInverseTransposePowerBasisMatrix.)
- // We are only interested in the normal to L, so only need the upper 2x2 of C^-1. And rather
- // than divide by determinant(C) here, we have already performed this divide on padRadius.
- Sk2f Lx = C1[1]*l3 - C0[1]*l2or1;
- Sk2f Ly = -C1[0]*l3 + C0[0]*l2or1;
-
- // A box of radius "padRadius" is touching line L if "center dot L" is less than the Manhattan
- // with of L. (See rationale in are_collinear.)
- Sk2f Lwidth = Lx.abs() + Ly.abs();
- Sk2f pad = Lwidth * padRadius;
-
- // Will T=(t + cbrt(pad))/s be greater than 0? No need to solve roots outside T=0..1.
- Sk2f insideLeftPad = pad + tl*tl*tl;
-
- // Will T=(t - cbrt(pad))/s be less than 1? No need to solve roots outside T=0..1.
- Sk2f tms = tl - sl;
- Sk2f insideRightPad = pad - tms*tms*tms;
-
- // Solve for the T values where abs(l(T)) = pad.
- if (insideLeftPad[0] > 0 && insideRightPad[0] > 0) {
- float padT = cbrtf(pad[0]);
- Sk2f pts = (tl[0] + Sk2f(-padT, +padT)) / sl[0];
- pts.store(chops->push_back_n(2));
- }
-
- // Solve for the T values where abs(m(T)) = pad.
- if (insideLeftPad[1] > 0 && insideRightPad[1] > 0) {
- float padT = cbrtf(pad[1]);
- Sk2f pts = (tl[1] + Sk2f(-padT, +padT)) / sl[1];
- pts.store(chops->push_back_n(2));
- }
-}
-
-static inline void swap_if_greater(float& a, float& b) {
- if (a > b) {
- std::swap(a, b);
- }
-}
-
-// Finds where to chop a non-loop around its intersection point. The resulting cubic segments will
-// be chopped such that a box of radius 'padRadius', centered at any point along the curve segment,
-// is guaranteed to not cross the tangent lines at the intersection point (a.k.a lines L & M).
-//
-// 'chops' will be filled with 0, 2, or 4 T values. The segments between T0..T1 and T2..T3 must be
-// drawn with quadratic splines instead of cubics.
-//
-// A loop intersection falls at two different T values, so this method takes Sk2f and computes the
-// padding for both in SIMD.
-static inline void find_chops_around_loop_intersection(float padRadius, Sk2f t2, Sk2f s2,
- const Sk2f& C0, const Sk2f& C1,
- ExcludedTerm skipTerm, float Cdet,
- SkSTArray<4, float>* chops) {
- SkASSERT(chops->empty());
- SkASSERT(padRadius >= 0);
-
- padRadius /= std::abs(Cdet); // Scale this single value rather than all of C^-1 later on.
-
- // The parametric functions for distance from lines L & M are:
- //
- // l(T) = (T - Td)^2 * (T - Te)
- // m(T) = (T - Td) * (T - Te)^2
- //
- // See "Resolution Independent Curve Rendering using Programmable Graphics Hardware",
- // 4.3 Finding klmn:
- //
- // https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
- Sk2f T2 = t2/s2; // T2 is the double root of l(T).
- Sk2f T1 = SkNx_shuffle<1,0>(T2); // T1 is the other root of l(T).
-
- // Convert l(T), m(T) to power-basis form:
- //
- // | 1 1 |
- // |l(T) m(T)| = |T^3 T^2 T 1| * | l2 m2 |
- // | l1 m1 |
- // | l0 m0 |
- //
- // From here on we use Sk2f with "L" names, but the second lane will be for line M.
- Sk2f l2 = SkNx_fma(Sk2f(-2), T2, -T1);
- Sk2f l1 = T2 * SkNx_fma(Sk2f(2), T1, T2);
- Sk2f l0 = -T2*T2*T1;
-
- // The equation for line L can be found as follows:
- //
- // L = C^-1 * (l excluding skipTerm)
- //
- // (See comments for GrPathUtils::calcCubicInverseTransposePowerBasisMatrix.)
- // We are only interested in the normal to L, so only need the upper 2x2 of C^-1. And rather
- // than divide by determinant(C) here, we have already performed this divide on padRadius.
- Sk2f l2or1 = (ExcludedTerm::kLinearTerm == skipTerm) ? l2 : l1;
- Sk2f Lx = -C0[1]*l2or1 + C1[1]; // l3 is always 1.
- Sk2f Ly = C0[0]*l2or1 - C1[0];
-
- // A box of radius "padRadius" is touching line L if "center dot L" is less than the Manhattan
- // with of L. (See rationale in are_collinear.)
- Sk2f Lwidth = Lx.abs() + Ly.abs();
- Sk2f pad = Lwidth * padRadius;
-
- // Is l(T=0) outside the padding around line L?
- Sk2f lT0 = l0; // l(T=0) = |0 0 0 1| dot |1 l2 l1 l0| = l0
- Sk2f outsideT0 = lT0.abs() - pad;
-
- // Is l(T=1) outside the padding around line L?
- Sk2f lT1 = (Sk2f(1) + l2 + l1 + l0).abs(); // l(T=1) = |1 1 1 1| dot |1 l2 l1 l0|
- Sk2f outsideT1 = lT1.abs() - pad;
-
- // Values for solving the cubic.
- Sk2f p, q, qqq, discr, numRoots, D;
- bool hasDiscr = false;
-
- // Values for calculating one root (rarely needed).
- Sk2f R, QQ;
- bool hasOneRootVals = false;
-
- // Values for calculating three roots.
- Sk2f P, cosTheta3;
- bool hasThreeRootVals = false;
-
- // Solve for the T values where l(T) = +pad and m(T) = -pad.
- for (int i = 0; i < 2; ++i) {
- float T = T2[i]; // T is the point we are chopping around.
- if ((T < 0 && outsideT0[i] >= 0) || (T > 1 && outsideT1[i] >= 0)) {
- // The padding around T is completely out of range. No point solving for it.
- continue;
- }
-
- if (!hasDiscr) {
- p = Sk2f(+.5f, -.5f) * pad;
- q = (1.f/3) * (T2 - T1);
- qqq = q*q*q;
- discr = qqq*p*2 + p*p;
- numRoots = (discr < 0).thenElse(3, 1);
- D = T2 - q;
- hasDiscr = true;
- }
-
- if (1 == numRoots[i]) {
- if (!hasOneRootVals) {
- Sk2f r = qqq + p;
- Sk2f s = r.abs() + discr.sqrt();
- R = (r > 0).thenElse(-s, s);
- QQ = q*q;
- hasOneRootVals = true;
- }
-
- float A = cbrtf(R[i]);
- float B = A != 0 ? QQ[i]/A : 0;
- // When there is only one root, ine L chops from root..1, line M chops from 0..root.
- if (1 == i) {
- chops->push_back(0);
- }
- chops->push_back(A + B + D[i]);
- if (0 == i) {
- chops->push_back(1);
- }
- continue;
- }
-
- if (!hasThreeRootVals) {
- P = q.abs() * -2;
- cosTheta3 = (q >= 0).thenElse(1, -1) + p / qqq.abs();
- hasThreeRootVals = true;
- }
-
- static constexpr float k2PiOver3 = 2 * SK_ScalarPI / 3;
- float theta = std::acos(cosTheta3[i]) * (1.f/3);
- float roots[3] = {P[i] * std::cos(theta) + D[i],
- P[i] * std::cos(theta + k2PiOver3) + D[i],
- P[i] * std::cos(theta - k2PiOver3) + D[i]};
-
- // Sort the three roots.
- swap_if_greater(roots[0], roots[1]);
- swap_if_greater(roots[1], roots[2]);
- swap_if_greater(roots[0], roots[1]);
-
- // Line L chops around the first 2 roots, line M chops around the second 2.
- chops->push_back_n(2, &roots[i]);
- }
-}
-
-void GrCCFillGeometry::cubicTo(const SkPoint P[4], float inflectPad, float loopIntersectPad) {
- SkASSERT(fBuildingContour);
- SkASSERT(P[0] == fPoints.back());
-
- // Don't crunch on the curve or inflate geometry if it is nearly flat (or just very small).
- // Flat curves can break the math below.
- if (are_collinear(P)) {
- Sk2f p0 = Sk2f::Load(P);
- Sk2f p3 = Sk2f::Load(P+3);
- this->appendLine(p0, p3);
- return;
- }
-
- Sk2f p0 = Sk2f::Load(P);
- Sk2f p1 = Sk2f::Load(P+1);
- Sk2f p2 = Sk2f::Load(P+2);
- Sk2f p3 = Sk2f::Load(P+3);
-
- // Also detect near-quadratics ahead of time.
- Sk2f tan0, tan1, c;
- get_cubic_tangents(p0, p1, p2, p3, &tan0, &tan1);
- if (is_cubic_nearly_quadratic(p0, p1, p2, p3, tan0, tan1, &c)) {
- this->appendQuadratics(p0, c, p3);
- return;
- }
-
- double tt[2], ss[2], D[4];
- fCurrCubicType = SkClassifyCubic(P, tt, ss, D);
- SkASSERT(!SkCubicIsDegenerate(fCurrCubicType));
- Sk2f t = Sk2f(static_cast<float>(tt[0]), static_cast<float>(tt[1]));
- Sk2f s = Sk2f(static_cast<float>(ss[0]), static_cast<float>(ss[1]));
-
- ExcludedTerm skipTerm = (std::abs(D[2]) > std::abs(D[1]))
- ? ExcludedTerm::kQuadraticTerm
- : ExcludedTerm::kLinearTerm;
- Sk2f C0 = SkNx_fma(Sk2f(3), p1 - p2, p3 - p0);
- Sk2f C1 = (ExcludedTerm::kLinearTerm == skipTerm
- ? SkNx_fma(Sk2f(-2), p1, p0 + p2)
- : p1 - p0) * 3;
- Sk2f C0x1 = C0 * SkNx_shuffle<1,0>(C1);
- float Cdet = C0x1[0] - C0x1[1];
-
- SkSTArray<4, float> chops;
- if (SkCubicType::kLoop != fCurrCubicType) {
- find_chops_around_inflection_points(inflectPad, t, s, C0, C1, skipTerm, Cdet, &chops);
- } else {
- find_chops_around_loop_intersection(loopIntersectPad, t, s, C0, C1, skipTerm, Cdet, &chops);
- }
- if (4 == chops.count() && chops[1] >= chops[2]) {
- // This just the means the KLM roots are so close that their paddings overlap. We will
- // approximate the entire middle section, but still have it chopped midway. For loops this
- // chop guarantees the append code only sees convex segments. Otherwise, it means we are (at
- // least almost) a cusp and the chop makes sure we get a sharp point.
- Sk2f ts = t * SkNx_shuffle<1,0>(s);
- chops[1] = chops[2] = (ts[0] + ts[1]) / (2*s[0]*s[1]);
- }
-
-#ifdef SK_DEBUG
- for (int i = 1; i < chops.count(); ++i) {
- SkASSERT(chops[i] >= chops[i - 1]);
- }
-#endif
- this->appendCubics(AppendCubicMode::kLiteral, p0, p1, p2, p3, chops.begin(), chops.count());
-}
-
-static inline void chop_cubic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2, const Sk2f& p3,
- float T, Sk2f* ab, Sk2f* abc, Sk2f* abcd, Sk2f* bcd, Sk2f* cd) {
- Sk2f TT = T;
- *ab = lerp(p0, p1, TT);
- Sk2f bc = lerp(p1, p2, TT);
- *cd = lerp(p2, p3, TT);
- *abc = lerp(*ab, bc, TT);
- *bcd = lerp(bc, *cd, TT);
- *abcd = lerp(*abc, *bcd, TT);
-}
-
-void GrCCFillGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f& p1,
- const Sk2f& p2, const Sk2f& p3, const float chops[],
- int numChops, float localT0, float localT1) {
- if (numChops) {
- SkASSERT(numChops > 0);
- int midChopIdx = numChops/2;
- float T = chops[midChopIdx];
- // Chops alternate between literal and approximate mode.
- AppendCubicMode rightMode = (AppendCubicMode)((bool)mode ^ (midChopIdx & 1) ^ 1);
-
- if (T <= localT0) {
- // T is outside 0..1. Append the right side only.
- this->appendCubics(rightMode, p0, p1, p2, p3, &chops[midChopIdx + 1],
- numChops - midChopIdx - 1, localT0, localT1);
- return;
- }
-
- if (T >= localT1) {
- // T is outside 0..1. Append the left side only.
- this->appendCubics(mode, p0, p1, p2, p3, chops, midChopIdx, localT0, localT1);
- return;
- }
-
- float localT = (T - localT0) / (localT1 - localT0);
- Sk2f p01, p02, pT, p11, p12;
- chop_cubic(p0, p1, p2, p3, localT, &p01, &p02, &pT, &p11, &p12);
- this->appendCubics(mode, p0, p01, p02, pT, chops, midChopIdx, localT0, T);
- this->appendCubics(rightMode, pT, p11, p12, p3, &chops[midChopIdx + 1],
- numChops - midChopIdx - 1, T, localT1);
- return;
- }
-
- this->appendCubics(mode, p0, p1, p2, p3);
-}
-
-void GrCCFillGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f& p1,
- const Sk2f& p2, const Sk2f& p3, int maxSubdivisions) {
- if (SkCubicType::kLoop != fCurrCubicType) {
- // Serpentines and cusps are always monotonic after chopping around inflection points.
- SkASSERT(!SkCubicIsDegenerate(fCurrCubicType));
-
- if (AppendCubicMode::kApproximate == mode) {
- // This section passes through an inflection point, so we can get away with a flat line.
- // This can cause some curves to feel slightly more flat when inspected rigorously back
- // and forth against another renderer, but for now this seems acceptable given the
- // simplicity.
- this->appendLine(p0, p3);
- return;
- }
- } else {
- Sk2f tan0, tan1;
- get_cubic_tangents(p0, p1, p2, p3, &tan0, &tan1);
-
- if (maxSubdivisions && !is_convex_curve_monotonic(p0, tan0, p3, tan1)) {
- this->chopAndAppendCubicAtMidTangent(mode, p0, p1, p2, p3, tan0, tan1,
- maxSubdivisions - 1);
- return;
- }
-
- if (AppendCubicMode::kApproximate == mode) {
- Sk2f c;
- if (!is_cubic_nearly_quadratic(p0, p1, p2, p3, tan0, tan1, &c) && maxSubdivisions) {
- this->chopAndAppendCubicAtMidTangent(mode, p0, p1, p2, p3, tan0, tan1,
- maxSubdivisions - 1);
- return;
- }
-
- this->appendMonotonicQuadratic(p0, c, p3);
- return;
- }
- }
-
- // Don't send curves to the GPU if we know they are nearly flat (or just very small).
- // Since the cubic segment is known to be convex at this point, our flatness check is simple.
- if (are_collinear(p0, (p1 + p2) * .5f, p3)) {
- this->appendLine(p0, p3);
- return;
- }
-
- SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
- SkASSERT((p0 != p3).anyTrue());
- p1.store(&fPoints.push_back());
- p2.store(&fPoints.push_back());
- p3.store(&fPoints.push_back());
- fVerbs.push_back(Verb::kMonotonicCubicTo);
- ++fCurrContourTallies.fCubics;
-}
-
-// Given a convex curve segment with the following order-2 tangent function:
-//
-// |C2x C2y|
-// tan = some_scale * |dx/dt dy/dt| = |t^2 t 1| * |C1x C1y|
-// |C0x C0y|
-//
-// This function finds the T value whose tangent angle is halfway between the tangents at T=0 and
-// T=1 (tan0 and tan1).
-static inline float find_midtangent(const Sk2f& tan0, const Sk2f& tan1,
- const Sk2f& C2, const Sk2f& C1, const Sk2f& C0) {
- // Tangents point in the direction of increasing T, so tan0 and -tan1 both point toward the
- // midtangent. 'n' will therefore bisect tan0 and -tan1, giving us the normal to the midtangent.
- //
- // n dot midtangent = 0
- //
- Sk2f n = normalize(tan0) - normalize(tan1);
-
- // Find the T value at the midtangent. This is a simple quadratic equation:
- //
- // midtangent dot n = 0
- //
- // (|t^2 t 1| * C) dot n = 0
- //
- // |t^2 t 1| dot C*n = 0
- //
- // First find coeffs = C*n.
- Sk4f C[2];
- Sk2f::Store4(C, C2, C1, C0, 0);
- Sk4f coeffs = C[0]*n[0] + C[1]*n[1];
-
- // Now solve the quadratic.
- float a = coeffs[0], b = coeffs[1], c = coeffs[2];
- float discr = b*b - 4*a*c;
- if (discr < 0) {
- return 0; // This will only happen if the curve is a line.
- }
-
- // The roots are q/a and c/q. Pick the one closer to T=.5.
- float q = -.5f * (b + copysignf(std::sqrt(discr), b));
- float r = .5f*q*a;
- return std::abs(q*q - r) < std::abs(a*c - r) ? q/a : c/q;
-}
-
-inline void GrCCFillGeometry::chopAndAppendCubicAtMidTangent(AppendCubicMode mode, const Sk2f& p0,
- const Sk2f& p1, const Sk2f& p2,
- const Sk2f& p3, const Sk2f& tan0,
- const Sk2f& tan1,
- int maxFutureSubdivisions) {
- float midT = find_midtangent(tan0, tan1, p3 + (p1 - p2)*3 - p0,
- (p0 - p1*2 + p2)*2,
- p1 - p0);
- // Use positive logic since NaN fails comparisons. (However midT should not be NaN since we cull
- // near-flat cubics in cubicTo().)
- if (!(midT > 0 && midT < 1)) {
- // The cubic is flat. Otherwise there would be a real midtangent inside T=0..1.
- this->appendLine(p0, p3);
- return;
- }
-
- Sk2f p01, p02, pT, p11, p12;
- chop_cubic(p0, p1, p2, p3, midT, &p01, &p02, &pT, &p11, &p12);
- this->appendCubics(mode, p0, p01, p02, pT, maxFutureSubdivisions);
- this->appendCubics(mode, pT, p11, p12, p3, maxFutureSubdivisions);
-}
-
-void GrCCFillGeometry::conicTo(const SkPoint P[3], float w) {
- SkASSERT(fBuildingContour);
- SkASSERT(P[0] == fPoints.back());
- Sk2f p0 = Sk2f::Load(P);
- Sk2f p1 = Sk2f::Load(P+1);
- Sk2f p2 = Sk2f::Load(P+2);
-
- Sk2f tan0 = p1 - p0;
- Sk2f tan1 = p2 - p1;
-
- if (!is_convex_curve_monotonic(p0, tan0, p2, tan1)) {
- // The derivative of a conic has a cumbersome order-4 denominator. However, this isn't
- // necessary if we are only interested in a vector in the same *direction* as a given
- // tangent line. Since the denominator scales dx and dy uniformly, we can throw it out
- // completely after evaluating the derivative with the standard quotient rule. This leaves
- // us with a simpler quadratic function that we use to find the midtangent.
- float midT = find_midtangent(tan0, tan1, (w - 1) * (p2 - p0),
- (p2 - p0) - 2*w*(p1 - p0),
- w*(p1 - p0));
- // Use positive logic since NaN fails comparisons. (However midT should not be NaN since we
- // cull near-linear conics above. And while w=0 is flat, it's not a line and has valid
- // midtangents.)
- if (!(midT > 0 && midT < 1)) {
- // The conic is flat. Otherwise there would be a real midtangent inside T=0..1.
- this->appendLine(p0, p2);
- return;
- }
-
- // Chop the conic at midtangent to produce two monotonic segments.
- Sk4f p3d0 = Sk4f(p0[0], p0[1], 1, 0);
- Sk4f p3d1 = Sk4f(p1[0], p1[1], 1, 0) * w;
- Sk4f p3d2 = Sk4f(p2[0], p2[1], 1, 0);
- Sk4f midT4 = midT;
-
- Sk4f p3d01 = lerp(p3d0, p3d1, midT4);
- Sk4f p3d12 = lerp(p3d1, p3d2, midT4);
- Sk4f p3d012 = lerp(p3d01, p3d12, midT4);
-
- Sk2f midpoint = Sk2f(p3d012[0], p3d012[1]) / p3d012[2];
- Sk2f ww = Sk2f(p3d01[2], p3d12[2]) * Sk2f(p3d012[2]).rsqrt();
-
- this->appendMonotonicConic(p0, Sk2f(p3d01[0], p3d01[1]) / p3d01[2], midpoint, ww[0]);
- this->appendMonotonicConic(midpoint, Sk2f(p3d12[0], p3d12[1]) / p3d12[2], p2, ww[1]);
- return;
- }
-
- this->appendMonotonicConic(p0, p1, p2, w);
-}
-
-void GrCCFillGeometry::appendMonotonicConic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
- float w) {
- SkASSERT(w >= 0);
-
- Sk2f base = p2 - p0;
- Sk2f baseAbs = base.abs();
- float baseWidth = baseAbs[0] + baseAbs[1];
-
- // Find the height of the curve. Max height always occurs at T=.5 for conics.
- Sk2f d = (p1 - p0) * SkNx_shuffle<1,0>(base);
- float h1 = std::abs(d[1] - d[0]); // Height of p1 above the base.
- float ht = h1*w, hs = 1 + w; // Height of the conic = ht/hs.
-
- // i.e. (ht/hs <= baseWidth * kFlatnessThreshold). Use "<=" in case base == 0.
- if (ht <= (baseWidth*hs) * kFlatnessThreshold) {
- // We are flat. (See rationale in are_collinear.)
- this->appendLine(p0, p2);
- return;
- }
-
- // i.e. (w > 1 && h1 - ht/hs < baseWidth).
- if (w > 1 && h1*hs - ht < baseWidth*hs) {
- // If we get within 1px of p1 when w > 1, we will pick up artifacts from the implicit
- // function's reflection. Chop at max height (T=.5) and draw a triangle instead.
- Sk2f p1w = p1*w;
- Sk2f ab = p0 + p1w;
- Sk2f bc = p1w + p2;
- Sk2f highpoint = (ab + bc) / (2*(1 + w));
- this->appendLine(p0, highpoint);
- this->appendLine(highpoint, p2);
- return;
- }
-
- SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
- SkASSERT((p0 != p2).anyTrue());
- p1.store(&fPoints.push_back());
- p2.store(&fPoints.push_back());
- fConicWeights.push_back(w);
- fVerbs.push_back(Verb::kMonotonicConicTo);
- ++fCurrContourTallies.fConics;
-}
-
-GrCCFillGeometry::PrimitiveTallies GrCCFillGeometry::endContour() {
- SkASSERT(fBuildingContour);
- SkASSERT(fVerbs.count() >= fCurrContourTallies.fTriangles);
-
- // The fTriangles field currently contains this contour's starting verb index. We can now
- // use it to calculate the size of the contour's fan.
- int fanSize = fVerbs.count() - fCurrContourTallies.fTriangles;
- if (fPoints.back() == fCurrAnchorPoint) {
- --fanSize;
- fVerbs.push_back(Verb::kEndClosedContour);
- } else {
- fVerbs.push_back(Verb::kEndOpenContour);
- }
-
- fCurrContourTallies.fTriangles = std::max(fanSize - 2, 0);
-
- SkDEBUGCODE(fBuildingContour = false);
- return fCurrContourTallies;
-}
diff --git a/src/gpu/ccpr/GrCCFillGeometry.h b/src/gpu/ccpr/GrCCFillGeometry.h
deleted file mode 100644
index d0a3c1e..0000000
--- a/src/gpu/ccpr/GrCCFillGeometry.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrGrCCFillGeometry_DEFINED
-#define GrGrCCFillGeometry_DEFINED
-
-#include "include/core/SkPoint.h"
-#include "include/private/SkNx.h"
-#include "include/private/SkTArray.h"
-#include "src/core/SkGeometry.h"
-
-/**
- * This class chops device-space contours up into a series of segments that CCPR knows how to
- * fill. (See GrCCFillGeometry::Verb.)
- *
- * NOTE: This must be done in device space, since an affine transformation can change whether a
- * curve is monotonic.
- */
-class GrCCFillGeometry {
-public:
- // These are the verbs that CCPR knows how to fill. If a path has any segments that don't map to
- // this list, then they are chopped into smaller ones that do. A list of these comprise a
- // compact representation of what can later be expanded into GPU instance data.
- enum class Verb : uint8_t {
- kBeginPath, // Included only for caller convenience.
- kBeginContour,
- kLineTo,
- kMonotonicQuadraticTo, // Monotonic relative to the vector between its endpoints [P2 - P0].
- kMonotonicCubicTo,
- kMonotonicConicTo,
- kEndClosedContour, // endPt == startPt.
- kEndOpenContour // endPt != startPt.
- };
-
- // These tallies track numbers of CCPR primitives that are required to draw a contour.
- struct PrimitiveTallies {
- int fTriangles; // Number of triangles in the contour's fan.
- int fWeightedTriangles; // Triangles (from the tessellator) whose winding magnitude > 1.
- int fQuadratics;
- int fCubics;
- int fConics;
-
- void operator+=(const PrimitiveTallies&);
- PrimitiveTallies operator-(const PrimitiveTallies&) const;
- bool operator==(const PrimitiveTallies&);
- };
-
- GrCCFillGeometry(int numSkPoints = 0, int numSkVerbs = 0, int numConicWeights = 0)
- : fPoints(numSkPoints * 3) // Reserve for a 3x expansion in points and verbs.
- , fVerbs(numSkVerbs * 3)
- , fConicWeights(numConicWeights * 3/2) {}
-
- const SkTArray<SkPoint, true>& points() const { SkASSERT(!fBuildingContour); return fPoints; }
- const SkTArray<Verb, true>& verbs() const { SkASSERT(!fBuildingContour); return fVerbs; }
- float getConicWeight(int idx) const { SkASSERT(!fBuildingContour); return fConicWeights[idx]; }
-
- void reset() {
- SkASSERT(!fBuildingContour);
- fPoints.reset();
- fVerbs.reset();
- }
-
- void beginPath();
- void beginContour(const SkPoint&);
- void lineTo(const SkPoint P[2]);
- void quadraticTo(const SkPoint[3]);
-
- // We pass through inflection points and loop intersections using a line and quadratic(s)
- // respectively. 'inflectPad' and 'loopIntersectPad' specify how close (in pixels) cubic
- // segments are allowed to get to these points. For normal rendering you will want to use the
- // default values, but these can be overridden for testing purposes.
- //
- // NOTE: loops do appear to require two full pixels of padding around the intersection point.
- // With just one pixel-width of pad, we start to see bad pixels. Ultimately this has a
- // minimal effect on the total amount of segments produced. Most sections that pass
- // through the loop intersection can be approximated with a single quadratic anyway,
- // regardless of whether we are use one pixel of pad or two (1.622 avg. quads per loop
- // intersection vs. 1.489 on the tiger).
- void cubicTo(const SkPoint[4], float inflectPad = 0.55f, float loopIntersectPad = 2);
-
- void conicTo(const SkPoint[3], float w);
-
- PrimitiveTallies endContour(); // Returns the numbers of primitives needed to draw the contour.
-
-private:
- inline void appendLine(const Sk2f& p0, const Sk2f& p1);
-
- inline void appendQuadratics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2);
- inline void appendMonotonicQuadratic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2);
-
- enum class AppendCubicMode : bool {
- kLiteral,
- kApproximate
- };
- void appendCubics(AppendCubicMode, const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
- const Sk2f& p3, const float chops[], int numChops, float localT0 = 0,
- float localT1 = 1);
- void appendCubics(AppendCubicMode, const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
- const Sk2f& p3, int maxSubdivisions = 2);
- void chopAndAppendCubicAtMidTangent(AppendCubicMode, const Sk2f& p0, const Sk2f& p1,
- const Sk2f& p2, const Sk2f& p3, const Sk2f& tan0,
- const Sk2f& tan1, int maxFutureSubdivisions);
-
- void appendMonotonicConic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2, float w);
-
- // Transient state used while building a contour.
- SkPoint fCurrAnchorPoint;
- PrimitiveTallies fCurrContourTallies;
- SkCubicType fCurrCubicType;
- SkDEBUGCODE(bool fBuildingContour = false);
-
- SkSTArray<128, SkPoint, true> fPoints;
- SkSTArray<128, Verb, true> fVerbs;
- SkSTArray<32, float, true> fConicWeights;
-};
-
-inline void GrCCFillGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b) {
- fTriangles += b.fTriangles;
- fWeightedTriangles += b.fWeightedTriangles;
- fQuadratics += b.fQuadratics;
- fCubics += b.fCubics;
- fConics += b.fConics;
-}
-
-GrCCFillGeometry::PrimitiveTallies
-inline GrCCFillGeometry::PrimitiveTallies::operator-(const PrimitiveTallies& b) const {
- return {fTriangles - b.fTriangles,
- fWeightedTriangles - b.fWeightedTriangles,
- fQuadratics - b.fQuadratics,
- fCubics - b.fCubics,
- fConics - b.fConics};
-}
-
-inline bool GrCCFillGeometry::PrimitiveTallies::operator==(const PrimitiveTallies& b) {
- return fTriangles == b.fTriangles && fWeightedTriangles == b.fWeightedTriangles &&
- fQuadratics == b.fQuadratics && fCubics == b.fCubics && fConics == b.fConics;
-}
-
-#endif
diff --git a/src/gpu/ccpr/GrCCFiller.cpp b/src/gpu/ccpr/GrCCFiller.cpp
deleted file mode 100644
index d7035ae..0000000
--- a/src/gpu/ccpr/GrCCFiller.cpp
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrCCFiller.h"
-
-#include "include/core/SkPath.h"
-#include "include/core/SkPoint.h"
-#include "src/core/SkMathPriv.h"
-#include "src/core/SkPathPriv.h"
-#include "src/gpu/GrCaps.h"
-#include "src/gpu/GrOnFlushResourceProvider.h"
-#include "src/gpu/GrOpFlushState.h"
-#include "src/gpu/GrProgramInfo.h"
-#include <cstdlib>
-
-using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
-using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
-
-GrCCFiller::GrCCFiller(Algorithm algorithm, int numPaths, int numSkPoints, int numSkVerbs,
- int numConicWeights)
- : fAlgorithm(algorithm)
- , fGeometry(numSkPoints, numSkVerbs, numConicWeights)
- , fPathInfos(numPaths)
- , fScissorSubBatches(numPaths)
- , fTotalPrimitiveCounts{PrimitiveTallies(), PrimitiveTallies()} {
- // Batches decide what to draw by looking where the previous one ended. Define initial batches
- // that "end" at the beginning of the data. These will not be drawn, but will only be be read by
- // the first actual batch.
- fScissorSubBatches.push_back() = {PrimitiveTallies(), SkIRect::MakeEmpty()};
- fBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count(), PrimitiveTallies()};
-}
-
-void GrCCFiller::parseDeviceSpaceFill(const SkPath& path, const SkPoint* deviceSpacePts,
- GrScissorTest scissorTest, const SkIRect& clippedDevIBounds,
- const SkIVector& devToAtlasOffset) {
- SkASSERT(!fInstanceBuffer.hasGpuBuffer()); // Can't call after prepareToDraw().
- SkASSERT(!path.isEmpty());
-
- int currPathPointsIdx = fGeometry.points().count();
- int currPathVerbsIdx = fGeometry.verbs().count();
- PrimitiveTallies currPathPrimitiveCounts = PrimitiveTallies();
-
- fGeometry.beginPath();
-
- const float* conicWeights = SkPathPriv::ConicWeightData(path);
- int ptsIdx = 0;
- int conicWeightsIdx = 0;
- bool insideContour = false;
-
- for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
- switch (verb) {
- case SkPath::kMove_Verb:
- if (insideContour) {
- currPathPrimitiveCounts += fGeometry.endContour();
- }
- fGeometry.beginContour(deviceSpacePts[ptsIdx]);
- ++ptsIdx;
- insideContour = true;
- continue;
- case SkPath::kClose_Verb:
- if (insideContour) {
- currPathPrimitiveCounts += fGeometry.endContour();
- }
- insideContour = false;
- continue;
- case SkPath::kLine_Verb:
- fGeometry.lineTo(&deviceSpacePts[ptsIdx - 1]);
- ++ptsIdx;
- continue;
- case SkPath::kQuad_Verb:
- fGeometry.quadraticTo(&deviceSpacePts[ptsIdx - 1]);
- ptsIdx += 2;
- continue;
- case SkPath::kCubic_Verb:
- fGeometry.cubicTo(&deviceSpacePts[ptsIdx - 1]);
- ptsIdx += 3;
- continue;
- case SkPath::kConic_Verb:
- fGeometry.conicTo(&deviceSpacePts[ptsIdx - 1], conicWeights[conicWeightsIdx]);
- ptsIdx += 2;
- ++conicWeightsIdx;
- continue;
- default:
- SK_ABORT("Unexpected path verb.");
- }
- }
- SkASSERT(ptsIdx == path.countPoints());
- SkASSERT(conicWeightsIdx == SkPathPriv::ConicWeightCnt(path));
-
- if (insideContour) {
- currPathPrimitiveCounts += fGeometry.endContour();
- }
-
- fPathInfos.emplace_back(scissorTest, devToAtlasOffset);
-
- // Tessellate fans from very large and/or simple paths, in order to reduce overdraw.
- int numVerbs = fGeometry.verbs().count() - currPathVerbsIdx - 1;
- int64_t tessellationWork = (int64_t)numVerbs * (32 - SkCLZ(numVerbs)); // N log N.
- int64_t fanningWork = (int64_t)clippedDevIBounds.height() * clippedDevIBounds.width();
- if (tessellationWork * (50*50) + (100*100) < fanningWork) { // Don't tessellate under 100x100.
- fPathInfos.back().tessellateFan(
- fAlgorithm, path, fGeometry, currPathVerbsIdx, currPathPointsIdx, clippedDevIBounds,
- &currPathPrimitiveCounts);
- }
-
- fTotalPrimitiveCounts[(int)scissorTest] += currPathPrimitiveCounts;
-
- if (GrScissorTest::kEnabled == scissorTest) {
- fScissorSubBatches.push_back() = {fTotalPrimitiveCounts[(int)GrScissorTest::kEnabled],
- clippedDevIBounds.makeOffset(devToAtlasOffset)};
- }
-}
-
-void GrCCFiller::PathInfo::tessellateFan(
- Algorithm algorithm, const SkPath& originalPath, const GrCCFillGeometry& geometry,
- int verbsIdx, int ptsIdx, const SkIRect& clippedDevIBounds,
- PrimitiveTallies* newTriangleCounts) {
- using Verb = GrCCFillGeometry::Verb;
- SkASSERT(-1 == fFanTessellationCount);
- SkASSERT(!fFanTessellation);
-
- const SkTArray<Verb, true>& verbs = geometry.verbs();
- const SkTArray<SkPoint, true>& pts = geometry.points();
-
- newTriangleCounts->fTriangles =
- newTriangleCounts->fWeightedTriangles = 0;
-
- // Build an SkPath of the Redbook fan.
- SkPath fan;
- // When counting winding numbers in the stencil buffer, it works to use even/odd for the fan
- // tessellation (where applicable). But we need to strip out inverse fill info because
- // inverse-ness gets accounted for later on.
- fan.setFillType(SkPathFillType_ConvertToNonInverse(originalPath.getFillType()));
- SkASSERT(Verb::kBeginPath == verbs[verbsIdx]);
- for (int i = verbsIdx + 1; i < verbs.count(); ++i) {
- switch (verbs[i]) {
- case Verb::kBeginPath:
- SK_ABORT("Invalid GrCCFillGeometry");
- continue;
-
- case Verb::kBeginContour:
- fan.moveTo(pts[ptsIdx++]);
- continue;
-
- case Verb::kLineTo:
- fan.lineTo(pts[ptsIdx++]);
- continue;
-
- case Verb::kMonotonicQuadraticTo:
- case Verb::kMonotonicConicTo:
- fan.lineTo(pts[ptsIdx + 1]);
- ptsIdx += 2;
- continue;
-
- case Verb::kMonotonicCubicTo:
- fan.lineTo(pts[ptsIdx + 2]);
- ptsIdx += 3;
- continue;
-
- case Verb::kEndClosedContour:
- case Verb::kEndOpenContour:
- fan.close();
- continue;
- }
- }
-
- GrTriangulator::WindingVertex* vertices = nullptr;
- SkASSERT(!fan.isInverseFillType());
- fFanTessellationCount = GrTriangulator::PathToVertices(
- fan, std::numeric_limits<float>::infinity(), SkRect::Make(clippedDevIBounds),
- &vertices);
- if (fFanTessellationCount <= 0) {
- SkASSERT(0 == fFanTessellationCount);
- SkASSERT(nullptr == vertices);
- return;
- }
-
- SkASSERT(0 == fFanTessellationCount % 3);
- for (int i = 0; i < fFanTessellationCount; i += 3) {
- int weight = abs(vertices[i].fWinding);
- if (SkPathFillType::kEvenOdd == fan.getFillType()) {
- // The tessellator doesn't wrap weights modulo 2 when we request even/odd fill type.
- SkASSERT(weight & 1);
- weight = 1;
- }
- newTriangleCounts->fTriangles += weight;
- vertices[i].fWinding = weight;
- }
-
- fFanTessellation.reset(vertices);
-}
-
-GrCCFiller::BatchID GrCCFiller::closeCurrentBatch() {
- SkASSERT(!fInstanceBuffer.hasGpuBuffer());
- SkASSERT(!fBatches.empty());
-
- const auto& lastBatch = fBatches.back();
- int maxMeshes = 1 + fScissorSubBatches.count() - lastBatch.fEndScissorSubBatchIdx;
- fMaxMeshesPerDraw = std::max(fMaxMeshesPerDraw, maxMeshes);
-
- const auto& lastScissorSubBatch = fScissorSubBatches[lastBatch.fEndScissorSubBatchIdx - 1];
- PrimitiveTallies batchTotalCounts = fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled] -
- lastBatch.fEndNonScissorIndices;
- batchTotalCounts += fTotalPrimitiveCounts[(int)GrScissorTest::kEnabled] -
- lastScissorSubBatch.fEndPrimitiveIndices;
-
- // This will invalidate lastBatch.
- fBatches.push_back() = {
- fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled],
- fScissorSubBatches.count(),
- batchTotalCounts
- };
- return fBatches.count() - 1;
-}
-
-// Emits a contour's triangle fan.
-//
-// Classic Redbook fanning would be the triangles: [0 1 2], [0 2 3], ..., [0 n-2 n-1].
-//
-// This function emits the triangle: [0 n/3 n*2/3], and then recurses on all three sides. The
-// advantage to this approach is that for a convex-ish contour, it generates larger triangles.
-// Classic fanning tends to generate long, skinny triangles, which are expensive to draw since they
-// have a longer perimeter to rasterize and antialias.
-//
-// The indices array indexes the fan's points (think: glDrawElements), and must have at least log3
-// elements past the end for this method to use as scratch space.
-//
-// Returns the next triangle instance after the final one emitted.
-static TriPointInstance* emit_recursive_fan(
- const SkTArray<SkPoint, true>& pts, SkTArray<int32_t, true>& indices, int firstIndex,
- int indexCount, const Sk2f& devToAtlasOffset, TriPointInstance::Ordering ordering,
- TriPointInstance out[]) {
- if (indexCount < 3) {
- return out;
- }
-
- int32_t oneThirdCount = indexCount / 3;
- int32_t twoThirdsCount = (2 * indexCount) / 3;
- out++->set(pts[indices[firstIndex]], pts[indices[firstIndex + oneThirdCount]],
- pts[indices[firstIndex + twoThirdsCount]], devToAtlasOffset, ordering);
-
- out = emit_recursive_fan(
- pts, indices, firstIndex, oneThirdCount + 1, devToAtlasOffset, ordering, out);
- out = emit_recursive_fan(
- pts, indices, firstIndex + oneThirdCount, twoThirdsCount - oneThirdCount + 1,
- devToAtlasOffset, ordering, out);
-
- int endIndex = firstIndex + indexCount;
- int32_t oldValue = indices[endIndex];
- indices[endIndex] = indices[firstIndex];
- out = emit_recursive_fan(
- pts, indices, firstIndex + twoThirdsCount, indexCount - twoThirdsCount + 1,
- devToAtlasOffset, ordering, out);
- indices[endIndex] = oldValue;
-
- return out;
-}
-
-void GrCCFiller::emitTessellatedFan(
- const GrTriangulator::WindingVertex* vertices, int numVertices,
- const Sk2f& devToAtlasOffset, TriPointInstance::Ordering ordering,
- TriPointInstance* triPointInstanceData, QuadPointInstance* quadPointInstanceData,
- GrCCFillGeometry::PrimitiveTallies* indices) {
- for (int i = 0; i < numVertices; i += 3) {
- int weight = vertices[i].fWinding;
- SkASSERT(weight >= 1);
- for (int j = 0; j < weight; ++j) {
- // Unfortunately, there is not a way to increment stencil values by an amount larger
- // than 1. Instead we draw the triangle 'weight' times.
- triPointInstanceData[indices->fTriangles++].set(
- vertices[i].fPos, vertices[i + 1].fPos, vertices[i + 2].fPos, devToAtlasOffset,
- ordering);
- }
- }
-}
-
-bool GrCCFiller::prepareToDraw(GrOnFlushResourceProvider* onFlushRP) {
- using Verb = GrCCFillGeometry::Verb;
- SkASSERT(!fInstanceBuffer.hasGpuBuffer());
- SkASSERT(fBatches.back().fEndNonScissorIndices == // Call closeCurrentBatch().
- fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled]);
- SkASSERT(fBatches.back().fEndScissorSubBatchIdx == fScissorSubBatches.count());
-
- auto triangleOrdering = TriPointInstance::Ordering::kXYInterleaved;
-
- // Here we build a single instance buffer to share with every internal batch.
- //
- // CCPR processs 3 different types of primitives: triangles, quadratics, cubics. Each primitive
- // type is further divided into instances that require a scissor and those that don't. This
- // leaves us with 3*2 = 6 independent instance arrays to build for the GPU.
- //
- // Rather than place each instance array in its own GPU buffer, we allocate a single
- // megabuffer and lay them all out side-by-side. We can offset the "baseInstance" parameter in
- // our draw calls to direct the GPU to the applicable elements within a given array.
- //
- // We already know how big to make each of the 6 arrays from fTotalPrimitiveCounts, so layout is
- // straightforward. Start with triangles and quadratics. They both view the instance buffer as
- // an array of TriPointInstance[], so we can begin at zero and lay them out one after the other.
- fBaseInstances[0].fTriangles = 0;
- fBaseInstances[1].fTriangles = fBaseInstances[0].fTriangles +
- fTotalPrimitiveCounts[0].fTriangles;
- fBaseInstances[0].fQuadratics = fBaseInstances[1].fTriangles +
- fTotalPrimitiveCounts[1].fTriangles;
- fBaseInstances[1].fQuadratics = fBaseInstances[0].fQuadratics +
- fTotalPrimitiveCounts[0].fQuadratics;
- int triEndIdx = fBaseInstances[1].fQuadratics + fTotalPrimitiveCounts[1].fQuadratics;
-
- // Wound triangles and cubics both view the same instance buffer as an array of
- // QuadPointInstance[]. So, reinterpreting the instance data as QuadPointInstance[], we start
- // them on the first index that will not overwrite previous TriPointInstance data.
- int quadBaseIdx =
- GrSizeDivRoundUp(triEndIdx * sizeof(TriPointInstance), sizeof(QuadPointInstance));
- fBaseInstances[0].fWeightedTriangles = quadBaseIdx;
- fBaseInstances[1].fWeightedTriangles = fBaseInstances[0].fWeightedTriangles +
- fTotalPrimitiveCounts[0].fWeightedTriangles;
- fBaseInstances[0].fCubics = fBaseInstances[1].fWeightedTriangles +
- fTotalPrimitiveCounts[1].fWeightedTriangles;
- fBaseInstances[1].fCubics = fBaseInstances[0].fCubics + fTotalPrimitiveCounts[0].fCubics;
- fBaseInstances[0].fConics = fBaseInstances[1].fCubics + fTotalPrimitiveCounts[1].fCubics;
- fBaseInstances[1].fConics = fBaseInstances[0].fConics + fTotalPrimitiveCounts[0].fConics;
- int quadEndIdx = fBaseInstances[1].fConics + fTotalPrimitiveCounts[1].fConics;
-
- fInstanceBuffer.resetAndMapBuffer(onFlushRP, quadEndIdx * sizeof(QuadPointInstance));
- if (!fInstanceBuffer.hasGpuBuffer()) {
- SkDebugf("WARNING: failed to allocate CCPR fill instance buffer.\n");
- return false;
- }
-
- auto triPointInstanceData = reinterpret_cast<TriPointInstance*>(fInstanceBuffer.data());
- auto quadPointInstanceData = reinterpret_cast<QuadPointInstance*>(fInstanceBuffer.data());
- SkASSERT(quadPointInstanceData);
-
- PathInfo* nextPathInfo = fPathInfos.begin();
- Sk2f devToAtlasOffset;
- PrimitiveTallies instanceIndices[2] = {fBaseInstances[0], fBaseInstances[1]};
- PrimitiveTallies* currIndices = nullptr;
- SkSTArray<256, int32_t, true> currFan;
- bool currFanIsTessellated = false;
-
- const SkTArray<SkPoint, true>& pts = fGeometry.points();
- int ptsIdx = -1;
- int nextConicWeightIdx = 0;
-
- // Expand the ccpr verbs into GPU instance buffers.
- for (Verb verb : fGeometry.verbs()) {
- switch (verb) {
- case Verb::kBeginPath:
- SkASSERT(currFan.empty());
- currIndices = &instanceIndices[(int)nextPathInfo->scissorTest()];
- devToAtlasOffset = Sk2f(static_cast<float>(nextPathInfo->devToAtlasOffset().fX),
- static_cast<float>(nextPathInfo->devToAtlasOffset().fY));
- currFanIsTessellated = nextPathInfo->hasFanTessellation();
- if (currFanIsTessellated) {
- this->emitTessellatedFan(
- nextPathInfo->fanTessellation(), nextPathInfo->fanTessellationCount(),
- devToAtlasOffset, triangleOrdering, triPointInstanceData,
- quadPointInstanceData, currIndices);
- }
- ++nextPathInfo;
- continue;
-
- case Verb::kBeginContour:
- SkASSERT(currFan.empty());
- ++ptsIdx;
- if (!currFanIsTessellated) {
- currFan.push_back(ptsIdx);
- }
- continue;
-
- case Verb::kLineTo:
- ++ptsIdx;
- if (!currFanIsTessellated) {
- SkASSERT(!currFan.empty());
- currFan.push_back(ptsIdx);
- }
- continue;
-
- case Verb::kMonotonicQuadraticTo:
- triPointInstanceData[currIndices->fQuadratics++].set(
- &pts[ptsIdx], devToAtlasOffset, TriPointInstance::Ordering::kXYTransposed);
- ptsIdx += 2;
- if (!currFanIsTessellated) {
- SkASSERT(!currFan.empty());
- currFan.push_back(ptsIdx);
- }
- continue;
-
- case Verb::kMonotonicCubicTo:
- quadPointInstanceData[currIndices->fCubics++].set(
- &pts[ptsIdx], devToAtlasOffset[0], devToAtlasOffset[1]);
- ptsIdx += 3;
- if (!currFanIsTessellated) {
- SkASSERT(!currFan.empty());
- currFan.push_back(ptsIdx);
- }
- continue;
-
- case Verb::kMonotonicConicTo:
- quadPointInstanceData[currIndices->fConics++].setW(
- &pts[ptsIdx], devToAtlasOffset,
- fGeometry.getConicWeight(nextConicWeightIdx));
- ptsIdx += 2;
- ++nextConicWeightIdx;
- if (!currFanIsTessellated) {
- SkASSERT(!currFan.empty());
- currFan.push_back(ptsIdx);
- }
- continue;
-
- case Verb::kEndClosedContour: // endPt == startPt.
- if (!currFanIsTessellated) {
- SkASSERT(!currFan.empty());
- currFan.pop_back();
- }
- [[fallthrough]];
- case Verb::kEndOpenContour: // endPt != startPt.
- SkASSERT(!currFanIsTessellated || currFan.empty());
- if (!currFanIsTessellated && currFan.count() >= 3) {
- int fanSize = currFan.count();
- // Reserve space for emit_recursive_fan. Technically this can grow to
- // fanSize + log3(fanSize), but we approximate with log2.
- currFan.push_back_n(SkNextLog2(fanSize));
- SkDEBUGCODE(TriPointInstance* end =) emit_recursive_fan(
- pts, currFan, 0, fanSize, devToAtlasOffset, triangleOrdering,
- triPointInstanceData + currIndices->fTriangles);
- currIndices->fTriangles += fanSize - 2;
- SkASSERT(triPointInstanceData + currIndices->fTriangles == end);
- }
- currFan.reset();
- continue;
- }
- }
-
- fInstanceBuffer.unmapBuffer();
-
- SkASSERT(nextPathInfo == fPathInfos.end());
- SkASSERT(ptsIdx == pts.count() - 1);
- SkASSERT(instanceIndices[0].fTriangles == fBaseInstances[1].fTriangles);
- SkASSERT(instanceIndices[1].fTriangles == fBaseInstances[0].fQuadratics);
- SkASSERT(instanceIndices[0].fQuadratics == fBaseInstances[1].fQuadratics);
- SkASSERT(instanceIndices[1].fQuadratics == triEndIdx);
- SkASSERT(instanceIndices[0].fWeightedTriangles == fBaseInstances[1].fWeightedTriangles);
- SkASSERT(instanceIndices[1].fWeightedTriangles == fBaseInstances[0].fCubics);
- SkASSERT(instanceIndices[0].fCubics == fBaseInstances[1].fCubics);
- SkASSERT(instanceIndices[1].fCubics == fBaseInstances[0].fConics);
- SkASSERT(instanceIndices[0].fConics == fBaseInstances[1].fConics);
- SkASSERT(instanceIndices[1].fConics == quadEndIdx);
-
- return true;
-}
-
-void GrCCFiller::drawFills(
- GrOpFlushState* flushState, GrCCCoverageProcessor* proc, const GrPipeline& pipeline,
- BatchID batchID, const SkIRect& drawBounds, const GrUserStencilSettings* stencil) const {
- using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
-
- SkASSERT(fInstanceBuffer.hasGpuBuffer());
-
- GrResourceProvider* rp = flushState->resourceProvider();
- const PrimitiveTallies& batchTotalCounts = fBatches[batchID].fTotalPrimitiveCounts;
-
- int numSubpasses = proc->numSubpasses();
-
- if (batchTotalCounts.fTriangles) {
- for (int i = 0; i < numSubpasses; ++i) {
- proc->reset(PrimitiveType::kTriangles, i, rp);
- this->drawPrimitives(flushState, *proc, pipeline, stencil, batchID,
- &PrimitiveTallies::fTriangles, drawBounds);
- }
- }
-
- if (batchTotalCounts.fWeightedTriangles) {
- SkASSERT(Algorithm::kStencilWindingCount != fAlgorithm);
- for (int i = 0; i < numSubpasses; ++i) {
- proc->reset(PrimitiveType::kWeightedTriangles, i, rp);
- this->drawPrimitives(flushState, *proc, pipeline, stencil, batchID,
- &PrimitiveTallies::fWeightedTriangles, drawBounds);
- }
- }
-
- if (batchTotalCounts.fQuadratics) {
- for (int i = 0; i < numSubpasses; ++i) {
- proc->reset(PrimitiveType::kQuadratics, i, rp);
- this->drawPrimitives(flushState, *proc, pipeline, stencil, batchID,
- &PrimitiveTallies::fQuadratics, drawBounds);
- }
- }
-
- if (batchTotalCounts.fCubics) {
- for (int i = 0; i < numSubpasses; ++i) {
- proc->reset(PrimitiveType::kCubics, i, rp);
- this->drawPrimitives(flushState, *proc, pipeline, stencil, batchID,
- &PrimitiveTallies::fCubics, drawBounds);
- }
- }
-
- if (batchTotalCounts.fConics) {
- for (int i = 0; i < numSubpasses; ++i) {
- proc->reset(PrimitiveType::kConics, i, rp);
- this->drawPrimitives(flushState, *proc, pipeline, stencil, batchID,
- &PrimitiveTallies::fConics, drawBounds);
- }
- }
-}
-
-void GrCCFiller::drawPrimitives(
- GrOpFlushState* flushState, const GrCCCoverageProcessor& proc, const GrPipeline& pipeline,
- const GrUserStencilSettings* stencil, BatchID batchID, int PrimitiveTallies::*instanceType,
- const SkIRect& drawBounds) const {
- SkASSERT(pipeline.isScissorTestEnabled());
-
- GrOpsRenderPass* renderPass = flushState->opsRenderPass();
- proc.bindPipeline(flushState, pipeline, SkRect::Make(drawBounds), stencil);
- proc.bindBuffers(renderPass, fInstanceBuffer.gpuBuffer());
-
- SkASSERT(batchID > 0);
- SkASSERT(batchID < fBatches.count());
- const Batch& previousBatch = fBatches[batchID - 1];
- const Batch& batch = fBatches[batchID];
- SkDEBUGCODE(int totalInstanceCount = 0);
-
- if (int instanceCount = batch.fEndNonScissorIndices.*instanceType -
- previousBatch.fEndNonScissorIndices.*instanceType) {
- SkASSERT(instanceCount > 0);
- int baseInstance = fBaseInstances[(int)GrScissorTest::kDisabled].*instanceType +
- previousBatch.fEndNonScissorIndices.*instanceType;
- renderPass->setScissorRect(SkIRect::MakeXYWH(0, 0, drawBounds.width(),
- drawBounds.height()));
- proc.drawInstances(renderPass, instanceCount, baseInstance);
- SkDEBUGCODE(totalInstanceCount += instanceCount);
- }
-
- SkASSERT(previousBatch.fEndScissorSubBatchIdx > 0);
- SkASSERT(batch.fEndScissorSubBatchIdx <= fScissorSubBatches.count());
- int baseScissorInstance = fBaseInstances[(int)GrScissorTest::kEnabled].*instanceType;
- for (int i = previousBatch.fEndScissorSubBatchIdx; i < batch.fEndScissorSubBatchIdx; ++i) {
- const ScissorSubBatch& previousSubBatch = fScissorSubBatches[i - 1];
- const ScissorSubBatch& scissorSubBatch = fScissorSubBatches[i];
- int startIndex = previousSubBatch.fEndPrimitiveIndices.*instanceType;
- int instanceCount = scissorSubBatch.fEndPrimitiveIndices.*instanceType - startIndex;
- if (!instanceCount) {
- continue;
- }
- SkASSERT(instanceCount > 0);
- renderPass->setScissorRect(scissorSubBatch.fScissor);
- proc.drawInstances(renderPass, instanceCount, baseScissorInstance + startIndex);
- SkDEBUGCODE(totalInstanceCount += instanceCount);
- }
-
- SkASSERT(totalInstanceCount == batch.fTotalPrimitiveCounts.*instanceType);
-}
diff --git a/src/gpu/ccpr/GrCCFiller.h b/src/gpu/ccpr/GrCCFiller.h
deleted file mode 100644
index 50ace24..0000000
--- a/src/gpu/ccpr/GrCCFiller.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCCPathParser_DEFINED
-#define GrCCPathParser_DEFINED
-
-#include "include/core/SkRect.h"
-#include "include/core/SkRefCnt.h"
-#include "src/core/SkPathPriv.h"
-#include "src/gpu/GrTriangulator.h"
-#include "src/gpu/ccpr/GrAutoMapVertexBuffer.h"
-#include "src/gpu/ccpr/GrCCCoverageProcessor.h"
-#include "src/gpu/ccpr/GrCCFillGeometry.h"
-#include "src/gpu/ops/GrDrawOp.h"
-
-class GrOnFlushResourceProvider;
-class SkMatrix;
-class SkPath;
-
-/**
- * This class parses SkPaths into CCPR primitives in GPU buffers, then issues calls to draw their
- * coverage counts.
- */
-class GrCCFiller {
-public:
- enum class Algorithm : bool {
- kStencilWindingCount
- };
-
- GrCCFiller(Algorithm, int numPaths, int numSkPoints, int numSkVerbs, int numConicWeights);
-
- // Parses a device-space SkPath into the current batch, using the SkPath's original verbs and
- // 'deviceSpacePts'. Accepts an optional post-device-space translate for placement in an atlas.
- void parseDeviceSpaceFill(const SkPath&, const SkPoint* deviceSpacePts, GrScissorTest,
- const SkIRect& clippedDevIBounds, const SkIVector& devToAtlasOffset);
-
- using BatchID = int;
-
- // Compiles the outstanding parsed paths into a batch, and returns an ID that can be used to
- // draw their fills in the future.
- BatchID closeCurrentBatch();
-
- // Builds internal GPU buffers and prepares for calls to drawFills(). Caller must close the
- // current batch before calling this method, and cannot parse new paths afer.
- bool prepareToDraw(GrOnFlushResourceProvider*);
-
- // Called after prepareToDraw(). Draws the given batch of path fills.
- void drawFills(GrOpFlushState*, GrCCCoverageProcessor*, const GrPipeline&, BatchID,
- const SkIRect& drawBounds,
- const GrUserStencilSettings* = &GrUserStencilSettings::kUnused) const;
-
-private:
- static constexpr int kNumScissorModes = 2;
- using PrimitiveTallies = GrCCFillGeometry::PrimitiveTallies;
-
- // Every kBeginPath verb has a corresponding PathInfo entry.
- class PathInfo {
- public:
- PathInfo(GrScissorTest scissorTest, const SkIVector& devToAtlasOffset)
- : fScissorTest(scissorTest), fDevToAtlasOffset(devToAtlasOffset) {}
-
- GrScissorTest scissorTest() const { return fScissorTest; }
- const SkIVector& devToAtlasOffset() const { return fDevToAtlasOffset; }
-
- // An empty tessellation fan is also valid; we use negative count to denote not tessellated.
- bool hasFanTessellation() const { return fFanTessellationCount >= 0; }
- int fanTessellationCount() const {
- SkASSERT(this->hasFanTessellation());
- return fFanTessellationCount;
- }
- const GrTriangulator::WindingVertex* fanTessellation() const {
- SkASSERT(this->hasFanTessellation());
- return fFanTessellation.get();
- }
- void tessellateFan(
- Algorithm, const SkPath& originalPath, const GrCCFillGeometry&, int verbsIdx,
- int ptsIdx, const SkIRect& clippedDevIBounds, PrimitiveTallies* newTriangleCounts);
-
- private:
- GrScissorTest fScissorTest;
- SkIVector fDevToAtlasOffset; // Translation from device space to location in atlas.
- int fFanTessellationCount = -1;
- std::unique_ptr<const GrTriangulator::WindingVertex[]> fFanTessellation;
- };
-
- // Defines a batch of CCPR primitives. Start indices are deduced by looking at the previous
- // Batch in the list.
- struct Batch {
- PrimitiveTallies fEndNonScissorIndices;
- int fEndScissorSubBatchIdx;
- PrimitiveTallies fTotalPrimitiveCounts;
- };
-
- // Defines a sub-batch that will be drawn with the given scissor rect. Start indices are deduced
- // by looking at the previous ScissorSubBatch in the list.
- struct ScissorSubBatch {
- PrimitiveTallies fEndPrimitiveIndices;
- SkIRect fScissor;
- };
-
- void emitTessellatedFan(
- const GrTriangulator::WindingVertex*, int numVertices, const Sk2f& devToAtlasOffset,
- GrCCCoverageProcessor::TriPointInstance::Ordering,
- GrCCCoverageProcessor::TriPointInstance*, GrCCCoverageProcessor::QuadPointInstance*,
- GrCCFillGeometry::PrimitiveTallies*);
- void drawPrimitives(GrOpFlushState*, const GrCCCoverageProcessor&, const GrPipeline&,
- const GrUserStencilSettings*, BatchID,
- int PrimitiveTallies::*instanceType, const SkIRect& drawBounds) const;
-
- const Algorithm fAlgorithm;
- GrCCFillGeometry fGeometry;
- SkSTArray<32, PathInfo, true> fPathInfos;
- SkSTArray<32, Batch, true> fBatches;
- SkSTArray<32, ScissorSubBatch, true> fScissorSubBatches;
- PrimitiveTallies fTotalPrimitiveCounts[kNumScissorModes];
- int fMaxMeshesPerDraw = 0;
-
- GrAutoMapVertexBuffer fInstanceBuffer;
- PrimitiveTallies fBaseInstances[kNumScissorModes];
-};
-
-#endif
diff --git a/src/gpu/ccpr/GrCCPerFlushResources.cpp b/src/gpu/ccpr/GrCCPerFlushResources.cpp
index a48c3fc..674f620 100644
--- a/src/gpu/ccpr/GrCCPerFlushResources.cpp
+++ b/src/gpu/ccpr/GrCCPerFlushResources.cpp
@@ -8,118 +8,23 @@
#include "src/gpu/ccpr/GrCCPerFlushResources.h"
#include "include/gpu/GrRecordingContext.h"
+#include "src/gpu/GrFixedClip.h"
#include "src/gpu/GrMemoryPool.h"
#include "src/gpu/GrOnFlushResourceProvider.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/GrSurfaceDrawContext.h"
-#include "src/gpu/ccpr/GrSampleMaskProcessor.h"
#include "src/gpu/geometry/GrStyledShape.h"
-
-#include <algorithm>
-
-using FillBatchID = GrCCFiller::BatchID;
-
-namespace {
-
-// Base class for an Op that renders a CCPR atlas.
-class AtlasOp : public GrDrawOp {
-public:
- FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
- GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
- bool hasMixedSampledCoverage, GrClampType) override {
- return GrProcessorSet::EmptySetAnalysis();
- }
- CombineResult onCombineIfPossible(GrOp* other, SkArenaAlloc*, const GrCaps&) override {
- // We will only make multiple copy ops if they have different source proxies.
- // TODO: make use of texture chaining.
- return CombineResult::kCannotCombine;
- }
-
-protected:
- AtlasOp(uint32_t classID, sk_sp<const GrCCPerFlushResources> resources,
- const SkISize& drawBounds)
- : GrDrawOp(classID)
- , fResources(std::move(resources)) {
- this->setBounds(SkRect::MakeIWH(drawBounds.width(), drawBounds.height()),
- GrOp::HasAABloat::kNo, GrOp::IsHairline::kNo);
- }
-
- const sk_sp<const GrCCPerFlushResources> fResources;
-
-private:
- void onPrePrepare(GrRecordingContext*,
- const GrSurfaceProxyView& writeView,
- GrAppliedClip*,
- const GrXferProcessor::DstProxyView&,
- GrXferBarrierFlags renderPassXferBarriers,
- GrLoadOp colorLoadOp) final {}
- void onPrepare(GrOpFlushState*) final {}
-};
-
-// Renders coverage counts to a CCPR atlas using the resources' pre-filled GrCCPathParser.
-template<typename ProcessorType> class RenderAtlasOp : public AtlasOp {
-public:
- DEFINE_OP_CLASS_ID
-
- static GrOp::Owner Make(
- GrRecordingContext* context, sk_sp<const GrCCPerFlushResources> resources,
- FillBatchID fillBatchID, const SkISize& drawBounds) {
- return GrOp::Make<RenderAtlasOp>(context,
- std::move(resources), fillBatchID, drawBounds);
- }
-
- // GrDrawOp interface.
- const char* name() const override { return "RenderAtlasOp (CCPR)"; }
-
- void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
- ProcessorType proc;
- GrPipeline pipeline(GrScissorTest::kEnabled, SkBlendMode::kPlus,
- flushState->drawOpArgs().writeView().swizzle());
- fResources->filler().drawFills(flushState, &proc, pipeline, fFillBatchID, fDrawBounds);
- }
-
-private:
- friend class ::GrOp; // for ctor
-
- RenderAtlasOp(sk_sp<const GrCCPerFlushResources> resources, FillBatchID fillBatchID,
- const SkISize& drawBounds)
- : AtlasOp(ClassID(), std::move(resources), drawBounds)
- , fFillBatchID(fillBatchID)
- , fDrawBounds(SkIRect::MakeWH(drawBounds.width(), drawBounds.height())) {
- }
-
- const FillBatchID fFillBatchID;
- const SkIRect fDrawBounds;
-};
-
-} // namespace
+#include "src/gpu/ops/GrFillRectOp.h"
GrCCPerFlushResources::GrCCPerFlushResources(GrOnFlushResourceProvider* onFlushRP,
- const GrCCPerFlushResourceSpecs& specs)
- : fFiller(GrCCFiller::Algorithm::kStencilWindingCount,
- specs.fNumClipPaths,
- specs.fRenderedPathStats.fNumTotalSkPoints,
- specs.fRenderedPathStats.fNumTotalSkVerbs,
- specs.fRenderedPathStats.fNumTotalConicWeights)
- , fRenderedAtlasStack(specs.fRenderedAtlasSpecs, onFlushRP->caps()) {
- int numRenderedPaths = specs.fNumClipPaths;
- fStencilResolveBuffer.resetAndMapBuffer(
- onFlushRP, numRenderedPaths * sizeof(GrStencilAtlasOp::ResolveRectInstance));
- if (!fStencilResolveBuffer.hasGpuBuffer()) {
- SkDebugf("WARNING: failed to allocate CCPR stencil resolve buffer. "
- "No paths will be drawn.\n");
- return;
- }
- SkDEBUGCODE(fEndStencilResolveInstance = numRenderedPaths);
+ const GrCCAtlas::Specs& specs)
+ : fRenderedAtlasStack(specs, onFlushRP->caps()) {
}
const GrCCAtlas* GrCCPerFlushResources::renderDeviceSpacePathInAtlas(
- const SkIRect& clipIBounds, const SkPath& devPath, const SkIRect& devPathIBounds,
- GrFillRule fillRule, SkIVector* devToAtlasOffset) {
- SkASSERT(this->isMapped());
-
+ GrOnFlushResourceProvider* onFlushRP, const SkIRect& clipIBounds, const SkPath& devPath,
+ const SkIRect& devPathIBounds, GrFillRule fillRule, SkIVector* devToAtlasOffset) {
if (devPath.isEmpty()) {
- SkDEBUGCODE(--fEndStencilResolveInstance);
return nullptr;
}
@@ -132,87 +37,155 @@
enableScissorInAtlas = GrScissorTest::kEnabled;
} else {
// The clip and path bounds do not intersect. Draw nothing.
- SkDEBUGCODE(--fEndStencilResolveInstance);
return nullptr;
}
- this->placeRenderedPathInAtlas(clippedPathIBounds, enableScissorInAtlas, devToAtlasOffset);
- fFiller.parseDeviceSpaceFill(devPath, SkPathPriv::PointData(devPath), enableScissorInAtlas,
- clippedPathIBounds, *devToAtlasOffset);
+ this->placeRenderedPathInAtlas(onFlushRP, clippedPathIBounds, enableScissorInAtlas,
+ devToAtlasOffset);
- // In MSAA mode we also record an internal draw instance that will be used to resolve stencil
- // winding values to coverage when the atlas is generated.
- this->recordStencilResolveInstance(clippedPathIBounds, *devToAtlasOffset, fillRule);
+ SkMatrix atlasMatrix = SkMatrix::Translate(devToAtlasOffset->fX, devToAtlasOffset->fY);
+ this->enqueueRenderedPath(devPath, fillRule, clippedPathIBounds, atlasMatrix,
+ enableScissorInAtlas, *devToAtlasOffset);
return &fRenderedAtlasStack.current();
}
void GrCCPerFlushResources::placeRenderedPathInAtlas(
- const SkIRect& clippedPathIBounds, GrScissorTest scissorTest, SkIVector* devToAtlasOffset) {
+ GrOnFlushResourceProvider* onFlushRP, const SkIRect& clippedPathIBounds,
+ GrScissorTest scissorTest, SkIVector* devToAtlasOffset) {
if (GrCCAtlas* retiredAtlas =
fRenderedAtlasStack.addRect(clippedPathIBounds, devToAtlasOffset)) {
- // We did not fit in the previous coverage count atlas and it was retired. Close the path
- // parser's current batch (which does not yet include the path we just parsed). We will
- // render this batch into the retired atlas during finalize().
- retiredAtlas->setFillBatchID(fFiller.closeCurrentBatch());
- retiredAtlas->setEndStencilResolveInstance(fNextStencilResolveInstanceIdx);
+ // We did not fit in the previous coverage count atlas and it was retired. Render the
+ // retired atlas.
+ this->flushRenderedPaths(onFlushRP, retiredAtlas);
}
}
-void GrCCPerFlushResources::recordStencilResolveInstance(
- const SkIRect& clippedPathIBounds, const SkIVector& devToAtlasOffset, GrFillRule fillRule) {
- SkASSERT(fNextStencilResolveInstanceIdx < fEndStencilResolveInstance);
-
- SkIRect atlasIBounds = clippedPathIBounds.makeOffset(devToAtlasOffset);
- if (GrFillRule::kEvenOdd == fillRule) {
- // Make even/odd fills counterclockwise. The resolve draw uses two-sided stencil, with
- // "nonzero" settings in front and "even/odd" settings in back.
- std::swap(atlasIBounds.fLeft, atlasIBounds.fRight);
+void GrCCPerFlushResources::enqueueRenderedPath(const SkPath& path, GrFillRule fillRule,
+ const SkIRect& clippedDevIBounds,
+ const SkMatrix& pathToAtlasMatrix,
+ GrScissorTest enableScissorInAtlas,
+ SkIVector devToAtlasOffset) {
+ SkPath* atlasPath;
+ if (enableScissorInAtlas == GrScissorTest::kDisabled) {
+ atlasPath = &fAtlasPaths[(int)fillRule].fUberPath;
+ } else {
+ auto& [scissoredPath, scissor] = fAtlasPaths[(int)fillRule].fScissoredPaths.push_back();
+ scissor = clippedDevIBounds.makeOffset(devToAtlasOffset);
+ atlasPath = &scissoredPath;
}
- fStencilResolveBuffer[fNextStencilResolveInstanceIdx++] = {
- (int16_t)atlasIBounds.left(), (int16_t)atlasIBounds.top(),
- (int16_t)atlasIBounds.right(), (int16_t)atlasIBounds.bottom()};
+ auto origin = clippedDevIBounds.topLeft() + devToAtlasOffset;
+ atlasPath->moveTo(origin.fX, origin.fY); // Implicit moveTo(0,0).
+ atlasPath->addPath(path, pathToAtlasMatrix);
+}
+
+static void draw_stencil_to_coverage(GrOnFlushResourceProvider* onFlushRP,
+ GrSurfaceDrawContext* surfaceDrawContext, SkRect&& rect) {
+ auto aaType = GrAAType::kMSAA;
+ auto fillRectFlags = GrSimpleMeshDrawOpHelper::InputFlags::kNone;
+
+ // This will be the final op in the surfaceDrawContext. So if Ganesh is planning to discard the
+ // stencil values anyway, then we might not actually need to reset the stencil values back to 0.
+ bool mustResetStencil = !onFlushRP->caps()->discardStencilValuesAfterRenderPass();
+
+ if (surfaceDrawContext->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.)
+ if (onFlushRP->caps()->conservativeRasterSupport()) {
+ fillRectFlags |= GrSimpleMeshDrawOpHelper::InputFlags::kConservativeRaster;
+ } else {
+ aaType = GrAAType::kNone;
+ }
+ mustResetStencil = true;
+ }
+
+ const GrUserStencilSettings* stencil;
+ if (mustResetStencil) {
+ constexpr static GrUserStencilSettings kTestAndResetStencil(
+ GrUserStencilSettings::StaticInit<
+ 0x0000,
+ GrUserStencilTest::kNotEqual,
+ 0xffff,
+ GrUserStencilOp::kZero,
+ GrUserStencilOp::kKeep,
+ 0xffff>());
+
+ // Outset the cover rect in case there are T-junctions in the path bounds.
+ rect.outset(1, 1);
+ stencil = &kTestAndResetStencil;
+ } else {
+ constexpr static GrUserStencilSettings kTestStencil(
+ GrUserStencilSettings::StaticInit<
+ 0x0000,
+ GrUserStencilTest::kNotEqual,
+ 0xffff,
+ GrUserStencilOp::kKeep,
+ GrUserStencilOp::kKeep,
+ 0xffff>());
+
+ stencil = &kTestStencil;
+ }
+
+ GrPaint paint;
+ paint.setColor4f(SK_PMColor4fWHITE);
+ GrQuad coverQuad(rect);
+ DrawQuad drawQuad{coverQuad, coverQuad, GrQuadAAFlags::kAll};
+ auto coverOp = GrFillRectOp::Make(surfaceDrawContext->recordingContext(), std::move(paint),
+ aaType, &drawQuad, stencil, fillRectFlags);
+ surfaceDrawContext->addDrawOp(nullptr, std::move(coverOp));
+}
+
+void GrCCPerFlushResources::flushRenderedPaths(GrOnFlushResourceProvider* onFlushRP,
+ GrCCAtlas* atlas) {
+ auto surfaceDrawContext = atlas->instantiate(onFlushRP);
+ if (!surfaceDrawContext) {
+ for (int i = 0; i < (int)SK_ARRAY_COUNT(fAtlasPaths); ++i) {
+ fAtlasPaths[i].fUberPath.reset();
+ fAtlasPaths[i].fScissoredPaths.reset();
+ }
+ return;
+ }
+
+ for (int i = 0; i < (int)SK_ARRAY_COUNT(fAtlasPaths); ++i) {
+ SkPathFillType fillType = (i == (int)GrFillRule::kNonzero) ? SkPathFillType::kWinding
+ : SkPathFillType::kEvenOdd;
+ SkPath& uberPath = fAtlasPaths[i].fUberPath;
+ if (!uberPath.isEmpty()) {
+ uberPath.setIsVolatile(true);
+ uberPath.setFillType(fillType);
+ surfaceDrawContext->stencilPath(nullptr, GrAA::kYes, SkMatrix::I(), uberPath);
+ uberPath.reset();
+ }
+ for (auto& [scissoredPath, scissor] : fAtlasPaths[i].fScissoredPaths) {
+ GrFixedClip fixedClip(
+ surfaceDrawContext->asRenderTargetProxy()->backingStoreDimensions(), scissor);
+ scissoredPath.setIsVolatile(true);
+ scissoredPath.setFillType(fillType);
+ surfaceDrawContext->stencilPath(&fixedClip, GrAA::kYes, SkMatrix::I(), scissoredPath);
+ }
+ fAtlasPaths[i].fScissoredPaths.reset();
+ }
+
+ draw_stencil_to_coverage(onFlushRP, surfaceDrawContext.get(),
+ SkRect::MakeSize(SkSize::Make(atlas->drawBounds())));
+
+ if (surfaceDrawContext->asSurfaceProxy()->requiresManualMSAAResolve()) {
+ onFlushRP->addTextureResolveTask(sk_ref_sp(surfaceDrawContext->asTextureProxy()),
+ GrSurfaceProxy::ResolveFlags::kMSAA);
+ }
}
bool GrCCPerFlushResources::finalize(GrOnFlushResourceProvider* onFlushRP) {
- SkASSERT(this->isMapped());
- SkASSERT(fNextStencilResolveInstanceIdx == fEndStencilResolveInstance);
-
- if (fStencilResolveBuffer.hasGpuBuffer()) {
- fStencilResolveBuffer.unmapBuffer();
- }
-
if (!fRenderedAtlasStack.empty()) {
- fRenderedAtlasStack.current().setFillBatchID(fFiller.closeCurrentBatch());
- fRenderedAtlasStack.current().setEndStencilResolveInstance(fNextStencilResolveInstanceIdx);
+ this->flushRenderedPaths(onFlushRP, &fRenderedAtlasStack.current());
}
-
- // Build the GPU buffers to render path coverage counts. (This must not happen until after the
- // final calls to fFiller/fStroker.closeCurrentBatch().)
- if (!fFiller.prepareToDraw(onFlushRP)) {
- return false;
+#ifdef SK_DEBUG
+ // These paths should have been rendered and reset to empty by this point.
+ for (const auto& [uberPath, scissoredPaths] : fAtlasPaths) {
+ SkASSERT(uberPath.isEmpty());
+ SkASSERT(scissoredPaths.empty());
}
-
- // Render the coverage count atlas(es).
- int baseStencilResolveInstance = 0;
- for (GrCCAtlas& atlas : fRenderedAtlasStack.atlases()) {
- if (auto rtc = atlas.instantiate(onFlushRP)) {
- GrOp::Owner op;
- op = GrStencilAtlasOp::Make(
- rtc->recordingContext(), sk_ref_sp(this), atlas.getFillBatchID(),
- baseStencilResolveInstance,
- atlas.getEndStencilResolveInstance(), atlas.drawBounds());
- rtc->addDrawOp(nullptr, std::move(op));
- if (rtc->asSurfaceProxy()->requiresManualMSAAResolve()) {
- onFlushRP->addTextureResolveTask(sk_ref_sp(rtc->asTextureProxy()),
- GrSurfaceProxy::ResolveFlags::kMSAA);
- }
- }
-
- SkASSERT(atlas.getEndStencilResolveInstance() >= baseStencilResolveInstance);
- baseStencilResolveInstance = atlas.getEndStencilResolveInstance();
- }
- SkASSERT(baseStencilResolveInstance == fEndStencilResolveInstance);
-
+#endif
return true;
}
diff --git a/src/gpu/ccpr/GrCCPerFlushResources.h b/src/gpu/ccpr/GrCCPerFlushResources.h
index 81c602f..6c67401 100644
--- a/src/gpu/ccpr/GrCCPerFlushResources.h
+++ b/src/gpu/ccpr/GrCCPerFlushResources.h
@@ -9,42 +9,9 @@
#define GrCCPerFlushResources_DEFINED
#include "src/gpu/GrNonAtomicRef.h"
-#include "src/gpu/ccpr/GrAutoMapVertexBuffer.h"
#include "src/gpu/ccpr/GrCCAtlas.h"
-#include "src/gpu/ccpr/GrCCFiller.h"
-#include "src/gpu/ccpr/GrStencilAtlasOp.h"
-class GrCCPathCache;
-class GrCCPathCacheEntry;
-class GrOctoBounds;
class GrOnFlushResourceProvider;
-class GrStyledShape;
-
-/**
- * This struct counts values that help us preallocate buffers for rendered path geometry.
- */
-struct GrCCRenderedPathStats {
- int fMaxPointsPerPath = 0;
- int fNumTotalSkPoints = 0;
- int fNumTotalSkVerbs = 0;
- int fNumTotalConicWeights = 0;
-
- void statPath(const SkPath&);
-};
-
-/**
- * This struct encapsulates the minimum and desired requirements for the GPU resources required by
- * CCPR in a given flush.
- */
-struct GrCCPerFlushResourceSpecs {
- int fNumClipPaths = 0;
- GrCCRenderedPathStats fRenderedPathStats;
- GrCCAtlas::Specs fRenderedAtlasSpecs;
-
- bool isEmpty() const {
- return 0 == fNumClipPaths;
- }
-};
/**
* This class wraps all the GPU resources that CCPR builds at flush time. It is allocated in CCPR's
@@ -53,57 +20,39 @@
*/
class GrCCPerFlushResources : public GrNonAtomicRef<GrCCPerFlushResources> {
public:
- GrCCPerFlushResources(
- GrOnFlushResourceProvider*,const GrCCPerFlushResourceSpecs&);
-
- bool isMapped() const { return fStencilResolveBuffer.isMapped(); }
+ GrCCPerFlushResources(GrOnFlushResourceProvider*, const GrCCAtlas::Specs&);
// Renders a path into an atlas.
const GrCCAtlas* renderDeviceSpacePathInAtlas(
- const SkIRect& clipIBounds, const SkPath& devPath, const SkIRect& devPathIBounds,
- GrFillRule fillRule, SkIVector* devToAtlasOffset);
+ GrOnFlushResourceProvider*, const SkIRect& clipIBounds, const SkPath& devPath,
+ const SkIRect& devPathIBounds, GrFillRule fillRule, SkIVector* devToAtlasOffset);
// Finishes off the GPU buffers and renders the atlas(es).
bool finalize(GrOnFlushResourceProvider*);
// Accessors used by draw calls, once the resources have been finalized.
- const GrCCFiller& filler() const { SkASSERT(!this->isMapped()); return fFiller; }
- sk_sp<const GrGpuBuffer> stencilResolveBuffer() const {
- SkASSERT(!this->isMapped());
- return fStencilResolveBuffer.gpuBuffer();
- }
-
-private:
void placeRenderedPathInAtlas(
- const SkIRect& clippedPathIBounds, GrScissorTest, SkIVector* devToAtlasOffset);
+ GrOnFlushResourceProvider*, const SkIRect& clippedPathIBounds, GrScissorTest,
+ SkIVector* devToAtlasOffset);
- // In MSAA mode we record an additional instance per path that draws a rectangle on top of its
- // corresponding path in the atlas and resolves stencil winding values to coverage.
- void recordStencilResolveInstance(
- const SkIRect& clippedPathIBounds, const SkIVector& devToAtlasOffset, GrFillRule);
+ // Enqueues the given path to be rendered during the next call to flushRenderedPaths().
+ void enqueueRenderedPath(const SkPath&, GrFillRule, const SkIRect& clippedDevIBounds,
+ const SkMatrix& pathToAtlasMatrix, GrScissorTest enableScissorInAtlas,
+ SkIVector devToAtlasOffset);
- GrCCFiller fFiller;
+ // Renders all enqueued paths into the given atlas and clears our path queue.
+ void flushRenderedPaths(GrOnFlushResourceProvider*, GrCCAtlas*);
+
GrCCAtlasStack fRenderedAtlasStack;
- // Used in MSAA mode make an intermediate draw that resolves stencil winding values to coverage.
- GrTAutoMapVertexBuffer<GrStencilAtlasOp::ResolveRectInstance> fStencilResolveBuffer;
- int fNextStencilResolveInstanceIdx = 0;
- SkDEBUGCODE(int fEndStencilResolveInstance);
-
-public:
-#ifdef SK_DEBUG
- void debugOnly_didReuseRenderedPath() {
- --fEndStencilResolveInstance;
- }
-#endif
- const GrTexture* testingOnly_frontRenderedAtlasTexture() const;
+ // Paths to be rendered in the atlas we are currently building.
+ struct AtlasPaths {
+ SkPath fUberPath; // Contains all contours from all non-scissored paths.
+ SkSTArray<32, std::tuple<SkPath, SkIRect>> fScissoredPaths;
+ };
+ static_assert((int)GrFillRule::kNonzero == 0);
+ static_assert((int)GrFillRule::kEvenOdd == 1);
+ AtlasPaths fAtlasPaths[2]; // One for "nonzero" fill rule and one for "even-odd".
};
-inline void GrCCRenderedPathStats::statPath(const SkPath& path) {
- fMaxPointsPerPath = std::max(fMaxPointsPerPath, path.countPoints());
- fNumTotalSkPoints += path.countPoints();
- fNumTotalSkVerbs += path.countVerbs();
- fNumTotalConicWeights += SkPathPriv::ConicWeightCnt(path);
-}
-
#endif
diff --git a/src/gpu/ccpr/GrCCPerOpsTaskPaths.h b/src/gpu/ccpr/GrCCPerOpsTaskPaths.h
index 4692dc9..d2d2965 100644
--- a/src/gpu/ccpr/GrCCPerOpsTaskPaths.h
+++ b/src/gpu/ccpr/GrCCPerOpsTaskPaths.h
@@ -23,8 +23,6 @@
// DDL TODO: given the usage pattern in DDL mode, this could probably be non-atomic refcounting.
struct GrCCPerOpsTaskPaths : public SkRefCnt {
std::map<uint32_t, GrCCClipPath> fClipPaths;
- SkSTArenaAlloc<10 * 1024> fAllocator{10 * 1024 * 2};
- sk_sp<const GrCCPerFlushResources> fFlushResources;
};
#endif
diff --git a/src/gpu/ccpr/GrCCQuadraticShader.cpp b/src/gpu/ccpr/GrCCQuadraticShader.cpp
deleted file mode 100644
index 135cb77..0000000
--- a/src/gpu/ccpr/GrCCQuadraticShader.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrCCQuadraticShader.h"
-
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
-
-void GrCCQuadraticShader::emitSetupCode(
- GrGLSLVertexGeoBuilder* s, const char* pts, const char** outHull4) const {
- s->declareGlobal(fQCoordMatrix);
- s->codeAppendf("%s = float2x2(1, 1, .5, 0) * inverse(float2x2(%s[2] - %s[0], %s[1] - %s[0]));",
- fQCoordMatrix.c_str(), pts, pts, pts, pts);
-
- s->declareGlobal(fQCoord0);
- s->codeAppendf("%s = %s[0];", fQCoord0.c_str(), pts);
-
- if (outHull4) {
- // Clip the bezier triangle by the tangent line at maximum height. Quadratics have the nice
- // property that maximum height always occurs at T=.5. This is a simple application for
- // De Casteljau's algorithm.
- s->codeAppend ("float2 quadratic_hull[4];");
- s->codeAppendf("quadratic_hull[0] = %s[0];", pts);
- s->codeAppendf("quadratic_hull[1] = (%s[0] + %s[1]) * .5;", pts, pts);
- s->codeAppendf("quadratic_hull[2] = (%s[1] + %s[2]) * .5;", pts, pts);
- s->codeAppendf("quadratic_hull[3] = %s[2];", pts);
- *outHull4 = "quadratic_hull";
- }
-}
-
-void GrCCQuadraticShader::onEmitVaryings(
- GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code,
- const char* position, const char* coverage, const char* cornerCoverage, const char* wind) {
- fCoord_fGrad.reset(kFloat4_GrSLType, scope);
- varyingHandler->addVarying("coord_and_grad", &fCoord_fGrad);
- code->appendf("%s.xy = %s * (%s - %s);", // Quadratic coords.
- OutName(fCoord_fGrad), fQCoordMatrix.c_str(), position, fQCoord0.c_str());
- code->appendf("%s.zw = 2*bloat * float2(2 * %s.x, -1) * %s;", // Gradient.
- OutName(fCoord_fGrad), OutName(fCoord_fGrad), fQCoordMatrix.c_str());
-}
-
-void GrCCQuadraticShader::emitSampleMaskCode(GrGLSLFPFragmentBuilder* f) const {
- f->codeAppendf("float x = %s.x, y = %s.y;", fCoord_fGrad.fsIn(), fCoord_fGrad.fsIn());
- f->codeAppendf("float f = x*x - y;");
- f->codeAppendf("float2 grad = %s.zw;", fCoord_fGrad.fsIn());
- f->applyFnToMultisampleMask("f", "grad", GrGLSLFPFragmentBuilder::ScopeFlags::kTopLevel);
-}
diff --git a/src/gpu/ccpr/GrCCQuadraticShader.h b/src/gpu/ccpr/GrCCQuadraticShader.h
deleted file mode 100644
index 055b771..0000000
--- a/src/gpu/ccpr/GrCCQuadraticShader.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCCQuadraticShader_DEFINED
-#define GrCCQuadraticShader_DEFINED
-
-#include "src/gpu/ccpr/GrCCCoverageProcessor.h"
-
-/**
- * This class renders the coverage of closed quadratic curves using the techniques outlined in
- * "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
- * Jim Blinn:
- *
- * https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
- *
- * The provided curves must be monotonic with respect to the vector of their closing edge [P2 - P0].
- * (Use GrCCGeometry::quadraticTo().)
- */
-class GrCCQuadraticShader : public GrCCCoverageProcessor::Shader {
-public:
- void emitSetupCode(
- GrGLSLVertexGeoBuilder*, const char* pts, const char** outHull4) const override;
-
- void onEmitVaryings(
- GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, const char* position,
- const char* coverage, const char* cornerCoverage, const char* wind) override;
-
- void emitSampleMaskCode(GrGLSLFPFragmentBuilder*) const override;
-
-private:
- const GrShaderVar fQCoordMatrix{"qcoord_matrix", kFloat2x2_GrSLType};
- const GrShaderVar fQCoord0{"qcoord0", kFloat2_GrSLType};
- GrGLSLVarying fCoord_fGrad;
- GrGLSLVarying fEdge_fWind_fCorner;
-};
-
-#endif
diff --git a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
index d587f50..a93ca9f 100644
--- a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
+++ b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
@@ -101,10 +101,10 @@
return; // Nothing to draw.
}
- GrCCPerFlushResourceSpecs specs;
+ GrCCAtlas::Specs specs;
int maxPreferredRTSize = onFlushRP->caps()->maxPreferredRenderTargetSize();
- specs.fRenderedAtlasSpecs.fMaxPreferredTextureSize = maxPreferredRTSize;
- specs.fRenderedAtlasSpecs.fMinTextureSize = std::min(512, maxPreferredRTSize);
+ specs.fMaxPreferredTextureSize = maxPreferredRTSize;
+ specs.fMinTextureSize = std::min(512, maxPreferredRTSize);
// Move the per-opsTask paths that are about to be flushed from fPendingPaths to fFlushingPaths,
// and count them up so we can preallocate buffers.
@@ -123,45 +123,26 @@
}
}
- if (specs.isEmpty()) {
- return; // Nothing to draw.
- }
+ fPerFlushResources = std::make_unique<GrCCPerFlushResources>(onFlushRP, specs);
- auto resources = sk_make_sp<GrCCPerFlushResources>(onFlushRP, specs);
- if (!resources->isMapped()) {
- return; // Some allocation failed.
- }
-
- // Layout the atlas(es) and parse paths.
+ // Layout the atlas(es) and render paths.
for (const auto& flushingPaths : fFlushingPaths) {
for (auto& clipsIter : flushingPaths->fClipPaths) {
- clipsIter.second.renderPathInAtlas(resources.get(), onFlushRP);
+ clipsIter.second.renderPathInAtlas(fPerFlushResources.get(), onFlushRP);
}
}
// Allocate resources and then render the atlas(es).
- if (!resources->finalize(onFlushRP)) {
- return;
- }
-
- // Commit flushing paths to the resources once they are successfully completed.
- for (auto& flushingPaths : fFlushingPaths) {
- SkASSERT(!flushingPaths->fFlushResources);
- flushingPaths->fFlushResources = resources;
- }
+ fPerFlushResources->finalize(onFlushRP);
}
void GrCoverageCountingPathRenderer::postFlush(GrDeferredUploadToken,
SkSpan<const uint32_t> /* taskIDs */) {
SkASSERT(fFlushing);
- if (!fFlushingPaths.empty()) {
- // In DDL mode these aren't guaranteed to be deleted so we must clear out the perFlush
- // resources manually.
- for (auto& flushingPaths : fFlushingPaths) {
- flushingPaths->fFlushResources = nullptr;
- }
+ fPerFlushResources.reset();
+ if (!fFlushingPaths.empty()) {
// We wait to erase these until after flush, once Ops and FPs are done accessing their data.
fFlushingPaths.reset();
}
diff --git a/src/gpu/ccpr/GrCoverageCountingPathRenderer.h b/src/gpu/ccpr/GrCoverageCountingPathRenderer.h
index 09d06e0..12795dd 100644
--- a/src/gpu/ccpr/GrCoverageCountingPathRenderer.h
+++ b/src/gpu/ccpr/GrCoverageCountingPathRenderer.h
@@ -83,10 +83,9 @@
// (It will only contain elements when fFlushing is true.)
SkSTArray<4, sk_sp<GrCCPerOpsTaskPaths>> fFlushingPaths;
- SkDEBUGCODE(bool fFlushing = false);
+ std::unique_ptr<GrCCPerFlushResources> fPerFlushResources;
-public:
- const GrCCPerFlushResources* testingOnly_getCurrentFlushResources();
+ SkDEBUGCODE(bool fFlushing = false);
};
#endif
diff --git a/src/gpu/ccpr/GrSampleMaskProcessor.cpp b/src/gpu/ccpr/GrSampleMaskProcessor.cpp
deleted file mode 100644
index 01613ae..0000000
--- a/src/gpu/ccpr/GrSampleMaskProcessor.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright 2019 Google LLC.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrSampleMaskProcessor.h"
-
-#include "src/gpu/GrOpsRenderPass.h"
-#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
-
-class GrSampleMaskProcessor::Impl : public GrGLSLGeometryProcessor {
-public:
- Impl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {}
-
-private:
- void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override {}
-
- void onEmitCode(EmitArgs&, GrGPArgs*) override;
-
- const std::unique_ptr<Shader> fShader;
-};
-
-void GrSampleMaskProcessor::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
- const GrSampleMaskProcessor& proc = args.fGP.cast<GrSampleMaskProcessor>();
- GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
- GrGLSLVertexBuilder* v = args.fVertBuilder;
- int numInputPoints = proc.numInputPoints();
- int inputWidth = (4 == numInputPoints || proc.hasInputWeight()) ? 4 : 3;
-
- varyingHandler->emitAttributes(proc);
- SkASSERT(!*args.fFPCoordTransformHandler);
-
- if (PrimitiveType::kTriangles == proc.fPrimitiveType) {
- SkASSERT(!proc.hasInstanceAttributes()); // Triangles are drawn with vertex arrays.
- gpArgs->fPositionVar = proc.fInputAttribs.front().asShaderVar();
- } else {
- SkASSERT(!proc.hasVertexAttributes()); // Curves are drawn with instanced rendering.
-
- // Shaders expect a global "bloat" variable when calculating gradients.
- v->defineConstant("half", "bloat", ".5");
-
- const char* swizzle = (4 == numInputPoints || proc.hasInputWeight()) ? "xyzw" : "xyz";
- v->codeAppendf("float%ix2 pts = transpose(float2x%i(X.%s, Y.%s));",
- inputWidth, inputWidth, swizzle, swizzle);
-
- const char* hullPts = "pts";
- fShader->emitSetupCode(v, "pts", &hullPts);
- v->codeAppendf("float2 vertexpos = %s[sk_VertexID ^ (sk_VertexID >> 1)];", hullPts);
- gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
-
- fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag,
- &AccessCodeString(v), "vertexpos", nullptr, nullptr, nullptr);
- }
-
- // Fragment shader.
- fShader->emitSampleMaskCode(args.fFragBuilder);
-}
-
-void GrSampleMaskProcessor::reset(PrimitiveType primitiveType, int subpassIdx,
- GrResourceProvider* rp) {
- SkASSERT(subpassIdx == 0);
- fPrimitiveType = primitiveType; // This will affect the return values for numInputPoints, etc.
- SkASSERT(PrimitiveType::kWeightedTriangles != fPrimitiveType);
-
- this->resetCustomFeatures();
- fInputAttribs.reset();
-
- switch (fPrimitiveType) {
- case PrimitiveType::kTriangles:
- case PrimitiveType::kWeightedTriangles:
- fInputAttribs.emplace_back("point", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
- this->setVertexAttributes(fInputAttribs.begin(), 1);
- this->setInstanceAttributes(nullptr, 0);
- break;
- case PrimitiveType::kQuadratics:
- case PrimitiveType::kCubics:
- case PrimitiveType::kConics: {
- auto instanceAttribType = (PrimitiveType::kQuadratics == fPrimitiveType)
- ? kFloat3_GrVertexAttribType : kFloat4_GrVertexAttribType;
- auto shaderVarType = (PrimitiveType::kQuadratics == fPrimitiveType)
- ? kFloat3_GrSLType : kFloat4_GrSLType;
- fInputAttribs.emplace_back("X", instanceAttribType, shaderVarType);
- fInputAttribs.emplace_back("Y", instanceAttribType, shaderVarType);
- this->setVertexAttributes(nullptr, 0);
- this->setInstanceAttributes(fInputAttribs.begin(), fInputAttribs.count());
- this->setWillUseCustomFeature(CustomFeatures::kSampleLocations);
- break;
- }
- }
-}
-
-GrPrimitiveType GrSampleMaskProcessor::primType() const {
- SkASSERT(PrimitiveType::kWeightedTriangles != fPrimitiveType);
-
- switch (fPrimitiveType) {
- case PrimitiveType::kTriangles:
- case PrimitiveType::kWeightedTriangles:
- return GrPrimitiveType::kTriangles;
- case PrimitiveType::kQuadratics:
- case PrimitiveType::kCubics:
- case PrimitiveType::kConics:
- return GrPrimitiveType::kTriangleStrip;
- default:
- return GrPrimitiveType::kTriangleStrip;
- }
-}
-
-void GrSampleMaskProcessor::bindBuffers(GrOpsRenderPass* renderPass,
- sk_sp<const GrBuffer> instanceBuffer) const {
- SkASSERT(PrimitiveType::kWeightedTriangles != fPrimitiveType);
-
- switch (fPrimitiveType) {
- case PrimitiveType::kTriangles:
- case PrimitiveType::kWeightedTriangles: {
- renderPass->bindBuffers(nullptr, nullptr, std::move(instanceBuffer));
- break;
- }
- case PrimitiveType::kQuadratics:
- case PrimitiveType::kCubics:
- case PrimitiveType::kConics: {
- renderPass->bindBuffers(nullptr, std::move(instanceBuffer), nullptr);
- break;
- }
- }
-}
-
-void GrSampleMaskProcessor::drawInstances(GrOpsRenderPass* renderPass, int instanceCount,
- int baseInstance) const {
- SkASSERT(PrimitiveType::kWeightedTriangles != fPrimitiveType);
-
- switch (fPrimitiveType) {
- case PrimitiveType::kTriangles:
- case PrimitiveType::kWeightedTriangles: {
- renderPass->draw(instanceCount * 3, baseInstance * 3);
- break;
- }
- case PrimitiveType::kQuadratics:
- case PrimitiveType::kCubics:
- case PrimitiveType::kConics: {
- renderPass->drawInstanced(instanceCount, baseInstance, 4, 0);
- break;
- }
- }
-}
-
-GrGLSLPrimitiveProcessor* GrSampleMaskProcessor::onCreateGLSLInstance(
- std::unique_ptr<Shader> shader) const {
- return new Impl(std::move(shader));
-}
diff --git a/src/gpu/ccpr/GrSampleMaskProcessor.h b/src/gpu/ccpr/GrSampleMaskProcessor.h
deleted file mode 100644
index f3a6796..0000000
--- a/src/gpu/ccpr/GrSampleMaskProcessor.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2019 Google LLC.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrSampleMaskProcessor_DEFINED
-#define GrSampleMaskProcessor_DEFINED
-
-#include "src/gpu/ccpr/GrCCCoverageProcessor.h"
-
-/**
- * This class implements GrCCCoverageProcessor with MSAA using the sample mask.
- */
-class GrSampleMaskProcessor : public GrCCCoverageProcessor {
-public:
- GrSampleMaskProcessor() : GrCCCoverageProcessor(kGrSampleMaskProcessor_ClassID) {}
-
-private:
- GrPrimitiveType primType() const final;
- int numSubpasses() const override { return 1; }
- void reset(PrimitiveType, int subpassIdx, GrResourceProvider*) override;
- void bindBuffers(GrOpsRenderPass*, sk_sp<const GrBuffer> instanceBuffer) const override;
- void drawInstances(GrOpsRenderPass*, int instanceCount, int baseInstance) const override;
-
- GrGLSLPrimitiveProcessor* onCreateGLSLInstance(std::unique_ptr<Shader>) const override;
-
- SkSTArray<2, Attribute> fInputAttribs;
-
- class Impl;
-};
-
-#endif
diff --git a/src/gpu/ccpr/GrStencilAtlasOp.cpp b/src/gpu/ccpr/GrStencilAtlasOp.cpp
deleted file mode 100644
index 0787805..0000000
--- a/src/gpu/ccpr/GrStencilAtlasOp.cpp
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright 2019 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrStencilAtlasOp.h"
-
-#include "include/gpu/GrRecordingContext.h"
-#include "src/gpu/GrOpFlushState.h"
-#include "src/gpu/GrOpsRenderPass.h"
-#include "src/gpu/GrProgramInfo.h"
-#include "src/gpu/GrRecordingContextPriv.h"
-#include "src/gpu/ccpr/GrCCPerFlushResources.h"
-#include "src/gpu/ccpr/GrSampleMaskProcessor.h"
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
-
-namespace {
-
-class StencilResolveProcessor : public GrGeometryProcessor {
-public:
- StencilResolveProcessor() : INHERITED(kStencilResolveProcessor_ClassID) {
- static constexpr Attribute kIBounds = {
- "ibounds", kShort4_GrVertexAttribType, kShort4_GrSLType};
- this->setInstanceAttributes(&kIBounds, 1);
- SkASSERT(this->instanceStride() == sizeof(GrStencilAtlasOp::ResolveRectInstance));
- }
-
-private:
- const char* name() const final { return "StencilResolveProcessor"; }
- void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
- GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
- class Impl;
-
- using INHERITED = GrGeometryProcessor;
-};
-
-// This processor draws pixel-aligned rectangles directly on top of every path in the atlas.
-// The caller should have set up the instance data such that "Nonzero" paths get clockwise
-// rectangles (l < r) and "even/odd" paths get counter-clockwise (r < l). Its purpose
-// is to convert winding counts in the stencil buffer to A8 coverage in the color buffer.
-class StencilResolveProcessor::Impl : public GrGLSLGeometryProcessor {
- void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
- args.fVaryingHandler->emitAttributes(args.fGP.cast<StencilResolveProcessor>());
-
- GrGLSLVertexBuilder* v = args.fVertBuilder;
- v->codeAppendf("short2 devcoord;");
- v->codeAppendf("devcoord.x = (0 == (sk_VertexID & 1)) ? ibounds.x : ibounds.z;");
- v->codeAppendf("devcoord.y = (sk_VertexID < 2) ? ibounds.y : ibounds.w;");
-
- v->codeAppendf("float2 atlascoord = float2(devcoord);");
- gpArgs->fPositionVar.set(kFloat2_GrSLType, "atlascoord");
-
- // Just output "1" for coverage. This will be modulated by the MSAA stencil test.
- GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
- f->codeAppendf("const half4 %s = half4(1), %s = half4(1);",
- args.fOutputColor, args.fOutputCoverage);
- }
-
- void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override {}
-};
-
-GrGLSLPrimitiveProcessor* StencilResolveProcessor::createGLSLInstance(const GrShaderCaps&) const {
- return new Impl();
-}
-
-} // namespace
-
-GrOp::Owner GrStencilAtlasOp::Make(
- GrRecordingContext* context, sk_sp<const GrCCPerFlushResources> resources,
- FillBatchID fillBatchID, int baseStencilResolveInstance,
- int endStencilResolveInstance, const SkISize& drawBounds) {
-
- return GrOp::Make<GrStencilAtlasOp>(
- context, std::move(resources), fillBatchID, baseStencilResolveInstance,
- endStencilResolveInstance, drawBounds);
-}
-
-// Increments clockwise triangles and decrements counterclockwise. We use the same incr/decr
-// settings regardless of fill rule; fill rule is accounted for during the resolve step.
-static constexpr GrUserStencilSettings kIncrDecrStencil(
- GrUserStencilSettings::StaticInitSeparate<
- 0x0000, 0x0000,
- GrUserStencilTest::kNever, GrUserStencilTest::kNever,
- 0xffff, 0xffff,
- GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
- GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
- 0xffff, 0xffff>()
-);
-
-// Resolves stencil winding counts to A8 coverage. Leaves stencil values untouched.
-// NOTE: For the CCW face we intentionally use "1 == (stencil & 1)" because the contrapositive logic
-// (i.e. 0 != ...) causes bugs on Adreno Vulkan. http://skbug.com/9643
-static constexpr GrUserStencilSettings kResolveStencilCoverage(
- GrUserStencilSettings::StaticInitSeparate<
- 0x0000, 0x0001,
- GrUserStencilTest::kNotEqual, GrUserStencilTest::kEqual,
- 0xffff, 0x0001,
- GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
- GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
- 0xffff, 0xffff>()
-);
-
-// Same as above, but also resets stencil values to zero. This is better for non-tilers
-// where we prefer to not clear the stencil buffer at the beginning of every render pass.
-static constexpr GrUserStencilSettings kResolveStencilCoverageAndReset(
- GrUserStencilSettings::StaticInitSeparate<
- 0x0000, 0x0000,
- GrUserStencilTest::kNotEqual, GrUserStencilTest::kNotEqual,
- 0xffff, 0x0001,
- GrUserStencilOp::kZero, GrUserStencilOp::kZero,
- GrUserStencilOp::kKeep, GrUserStencilOp::kZero,
- 0xffff, 0xffff>()
-);
-
-// Same as above, but done in two passes for D3D, which doesn't support mismatched refs or masks on
-// dual sided stencil settings.
-static constexpr GrUserStencilSettings kResolveWindingCoverageAndReset(
- GrUserStencilSettings::StaticInitSeparate<
- 0x0000, 0x0000,
- GrUserStencilTest::kNotEqual, GrUserStencilTest::kNever,
- 0xffff, 0xffff,
- GrUserStencilOp::kZero, GrUserStencilOp::kKeep,
- GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
- 0xffff, 0xffff>()
-);
-static constexpr GrUserStencilSettings kResolveEvenOddCoverageAndReset(
- GrUserStencilSettings::StaticInitSeparate<
- 0x0000, 0x0000,
- GrUserStencilTest::kNever, GrUserStencilTest::kNotEqual,
- 0x0001, 0x0001,
- GrUserStencilOp::kKeep, GrUserStencilOp::kZero,
- GrUserStencilOp::kKeep, GrUserStencilOp::kZero,
- 0xffff, 0xffff>()
-);
-
-
-void GrStencilAtlasOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
- SkIRect drawBoundsRect = SkIRect::MakeWH(fDrawBounds.width(), fDrawBounds.height());
-
- GrPipeline pipeline(GrScissorTest::kEnabled, GrDisableColorXPFactory::MakeXferProcessor(),
- flushState->drawOpArgs().writeView().swizzle(),
- GrPipeline::InputFlags::kHWAntialias);
-
- GrSampleMaskProcessor sampleMaskProc;
-
- fResources->filler().drawFills(
- flushState, &sampleMaskProc, pipeline, fFillBatchID, drawBoundsRect, &kIncrDecrStencil);
-
- // We resolve the stencil coverage to alpha by drawing pixel-aligned boxes. Fine raster is
- // not necessary, and will even cause artifacts if using mixed samples.
- constexpr auto noHWAA = GrPipeline::InputFlags::kNone;
-
- GrPipeline resolvePipeline(GrScissorTest::kEnabled, SkBlendMode::kSrc,
- flushState->drawOpArgs().writeView().swizzle(), noHWAA);
- StencilResolveProcessor primProc;
-
- if (!flushState->caps().twoSidedStencilRefsAndMasksMustMatch()) {
- const GrUserStencilSettings* stencil =
- (flushState->caps().discardStencilValuesAfterRenderPass()) ?
- &kResolveStencilCoverage : &kResolveStencilCoverageAndReset;
- this->drawResolve(flushState, resolvePipeline, stencil, primProc, drawBoundsRect);
- return;
- }
-
- // If this ever becomes true then we should add new per-fill-type stencil settings that also
- // don't reset back to zero.
- SkASSERT(!flushState->caps().discardStencilValuesAfterRenderPass());
-
- this->drawResolve(flushState, resolvePipeline, &kResolveWindingCoverageAndReset, primProc,
- drawBoundsRect);
- this->drawResolve(flushState, resolvePipeline, &kResolveEvenOddCoverageAndReset, primProc,
- drawBoundsRect);
-}
-
-void GrStencilAtlasOp::drawResolve(GrOpFlushState* flushState, const GrPipeline& resolvePipeline,
- const GrUserStencilSettings* stencil,
- const GrPrimitiveProcessor& primProc,
- const SkIRect& drawBounds) const {
- GrProgramInfo programInfo(flushState->writeView(), &resolvePipeline, stencil,
- &primProc, GrPrimitiveType::kTriangleStrip, 0,
- flushState->renderPassBarriers(),
- flushState->colorLoadOp());
- flushState->bindPipeline(programInfo, SkRect::Make(drawBounds));
- flushState->setScissorRect(drawBounds);
- flushState->bindBuffers(nullptr, fResources->stencilResolveBuffer(), nullptr);
- flushState->drawInstanced(fEndStencilResolveInstance - fBaseStencilResolveInstance,
- fBaseStencilResolveInstance, 4, 0);
-}
diff --git a/src/gpu/ccpr/GrStencilAtlasOp.h b/src/gpu/ccpr/GrStencilAtlasOp.h
deleted file mode 100644
index da21dc5..0000000
--- a/src/gpu/ccpr/GrStencilAtlasOp.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2019 Google LLC.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrStencilAtlasOp_DEFINED
-#define GrStencilAtlasOp_DEFINED
-
-#include "src/gpu/GrMemoryPool.h"
-#include "src/gpu/ccpr/GrCCFiller.h"
-#include "src/gpu/ops/GrDrawOp.h"
-
-class GrCCPerFlushResources;
-
-// Renders literal A8 coverage to a CCPR atlas using an intermediate MSAA stencil buffer.
-class GrStencilAtlasOp : public GrDrawOp {
-public:
- DEFINE_OP_CLASS_ID
-
- using FillBatchID = GrCCFiller::BatchID;
-
- // Once all the paths in an atlas have been drawn to the stencil buffer, we make a final pass
- // where we draw "resolve" rects over each path whose purpose is to convert winding counts to A8
- // coverage.
- struct ResolveRectInstance {
- int16_t l, t, r, b;
- };
-
- // GrDrawOp interface.
- const char* name() const override { return "StencilAtlasOp (CCPR)"; }
- FixedFunctionFlags fixedFunctionFlags() const override {
- return FixedFunctionFlags::kUsesHWAA | FixedFunctionFlags::kUsesStencil;
- }
-
- GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
- bool hasMixedSampledCoverage, GrClampType) override {
- return GrProcessorSet::EmptySetAnalysis();
- }
- CombineResult onCombineIfPossible(GrOp* other, SkArenaAlloc*, const GrCaps&) override {
- // We will only make multiple copy ops if they have different source proxies.
- // TODO: make use of texture chaining.
- return CombineResult::kCannotCombine;
- }
-
- static GrOp::Owner Make(
- GrRecordingContext*, sk_sp<const GrCCPerFlushResources>, FillBatchID,
- int baseStencilResolveInstance, int endStencilResolveInstance,
- const SkISize& drawBounds);
-
-private:
- void onPrePrepare(GrRecordingContext*,
- const GrSurfaceProxyView& writeView,
- GrAppliedClip*,
- const GrXferProcessor::DstProxyView&,
- GrXferBarrierFlags renderPassXferBarriers,
- GrLoadOp colorLoadOp) override {}
- void onPrepare(GrOpFlushState*) override {}
- void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
- void drawResolve(GrOpFlushState*, const GrPipeline&, const GrUserStencilSettings*,
- const GrPrimitiveProcessor&, const SkIRect& drawBounds) const;
-
- friend class ::GrOp; // for ctor
-
- GrStencilAtlasOp(sk_sp<const GrCCPerFlushResources> resources, FillBatchID fillBatchID,
- int baseStencilResolveInstance,
- int endStencilResolveInstance, const SkISize& drawBounds)
- : GrDrawOp(ClassID())
- , fResources(std::move(resources))
- , fFillBatchID(fillBatchID)
- , fBaseStencilResolveInstance(baseStencilResolveInstance)
- , fEndStencilResolveInstance(endStencilResolveInstance)
- , fDrawBounds(drawBounds) {
- this->setBounds(SkRect::MakeIWH(fDrawBounds.width(), fDrawBounds.height()),
- GrOp::HasAABloat::kNo, GrOp::IsHairline::kNo);
- }
-
- const sk_sp<const GrCCPerFlushResources> fResources;
- const FillBatchID fFillBatchID;
- const int fBaseStencilResolveInstance;
- const int fEndStencilResolveInstance;
- const SkISize fDrawBounds;
- int fResolveBaseVertex;
-};
-
-
-#endif