ccpr: Rename GrCCPathParser to GrCCFiller
Various renames and other refactorings that will allow us to add new stroking classes alongside the existing code for fills. Bug: skia: Change-Id: Ib477f9e1d87f9d4c1604719f9af0695a53614081 Reviewed-on: https://skia-review.googlesource.com/147503 Commit-Queue: Chris Dalton <csmartdalton@google.com> Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
parent
9453731470
commit
e1639695bf
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#include "Benchmark.h"
|
#include "Benchmark.h"
|
||||||
|
|
||||||
#include "ccpr/GrCCGeometry.h"
|
#include "ccpr/GrCCFillGeometry.h"
|
||||||
#include "SkGeometry.h"
|
#include "SkGeometry.h"
|
||||||
|
|
||||||
static int kNumBaseLoops = 50000;
|
static int kNumBaseLoops = 50000;
|
||||||
@ -61,7 +61,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
SkPoint fPoints[5];
|
SkPoint fPoints[5];
|
||||||
SkString fName;
|
SkString fName;
|
||||||
GrCCGeometry fGeometry{4*100*kNumBaseLoops, 2*100*kNumBaseLoops};
|
GrCCFillGeometry fGeometry{4*100*kNumBaseLoops, 2*100*kNumBaseLoops};
|
||||||
|
|
||||||
typedef Benchmark INHERITED;
|
typedef Benchmark INHERITED;
|
||||||
};
|
};
|
@ -51,7 +51,7 @@ bench_sources = [
|
|||||||
"$_bench/GeometryBench.cpp",
|
"$_bench/GeometryBench.cpp",
|
||||||
"$_bench/GMBench.cpp",
|
"$_bench/GMBench.cpp",
|
||||||
"$_bench/GradientBench.cpp",
|
"$_bench/GradientBench.cpp",
|
||||||
"$_bench/GrCCGeometryBench.cpp",
|
"$_bench/GrCCFillGeometryBench.cpp",
|
||||||
"$_bench/GrMemoryPoolBench.cpp",
|
"$_bench/GrMemoryPoolBench.cpp",
|
||||||
"$_bench/GrMipMapBench.cpp",
|
"$_bench/GrMipMapBench.cpp",
|
||||||
"$_bench/GrResourceCacheBench.cpp",
|
"$_bench/GrResourceCacheBench.cpp",
|
||||||
|
@ -313,12 +313,12 @@ skia_gpu_sources = [
|
|||||||
"$_src/gpu/ccpr/GrCCCubicShader.h",
|
"$_src/gpu/ccpr/GrCCCubicShader.h",
|
||||||
"$_src/gpu/ccpr/GrCCDrawPathsOp.cpp",
|
"$_src/gpu/ccpr/GrCCDrawPathsOp.cpp",
|
||||||
"$_src/gpu/ccpr/GrCCDrawPathsOp.h",
|
"$_src/gpu/ccpr/GrCCDrawPathsOp.h",
|
||||||
"$_src/gpu/ccpr/GrCCGeometry.cpp",
|
"$_src/gpu/ccpr/GrCCFiller.cpp",
|
||||||
"$_src/gpu/ccpr/GrCCGeometry.h",
|
"$_src/gpu/ccpr/GrCCFiller.h",
|
||||||
|
"$_src/gpu/ccpr/GrCCFillGeometry.cpp",
|
||||||
|
"$_src/gpu/ccpr/GrCCFillGeometry.h",
|
||||||
"$_src/gpu/ccpr/GrCCPathCache.cpp",
|
"$_src/gpu/ccpr/GrCCPathCache.cpp",
|
||||||
"$_src/gpu/ccpr/GrCCPathCache.h",
|
"$_src/gpu/ccpr/GrCCPathCache.h",
|
||||||
"$_src/gpu/ccpr/GrCCPathParser.cpp",
|
|
||||||
"$_src/gpu/ccpr/GrCCPathParser.h",
|
|
||||||
"$_src/gpu/ccpr/GrCCPathProcessor.cpp",
|
"$_src/gpu/ccpr/GrCCPathProcessor.cpp",
|
||||||
"$_src/gpu/ccpr/GrCCPathProcessor.h",
|
"$_src/gpu/ccpr/GrCCPathProcessor.h",
|
||||||
"$_src/gpu/ccpr/GrCCPerFlushResources.cpp",
|
"$_src/gpu/ccpr/GrCCPerFlushResources.cpp",
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#include "SkPath.h"
|
#include "SkPath.h"
|
||||||
#include "SkRectPriv.h"
|
#include "SkRectPriv.h"
|
||||||
#include "ccpr/GrCCCoverageProcessor.h"
|
#include "ccpr/GrCCCoverageProcessor.h"
|
||||||
#include "ccpr/GrCCGeometry.h"
|
#include "ccpr/GrCCFillGeometry.h"
|
||||||
#include "gl/GrGLGpu.cpp"
|
#include "gl/GrGLGpu.cpp"
|
||||||
#include "glsl/GrGLSLFragmentProcessor.h"
|
#include "glsl/GrGLSLFragmentProcessor.h"
|
||||||
#include "ops/GrDrawOp.h"
|
#include "ops/GrDrawOp.h"
|
||||||
@ -76,7 +76,8 @@ class CCPRGeometryView::DrawCoverageCountOp : public GrDrawOp {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
DrawCoverageCountOp(CCPRGeometryView* view) : INHERITED(ClassID()), fView(view) {
|
DrawCoverageCountOp(CCPRGeometryView* view) : INHERITED(ClassID()), fView(view) {
|
||||||
this->setBounds(SkRectPriv::MakeLargest(), GrOp::HasAABloat::kNo, GrOp::IsZeroArea::kNo);
|
this->setBounds(SkRect::MakeIWH(fView->width(), fView->height()), GrOp::HasAABloat::kNo,
|
||||||
|
GrOp::IsZeroArea::kNo);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* name() const override {
|
const char* name() const override {
|
||||||
@ -191,9 +192,10 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
|||||||
if (GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext()) {
|
if (GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext()) {
|
||||||
// Render coverage count.
|
// Render coverage count.
|
||||||
GrContext* ctx = canvas->getGrContext();
|
GrContext* ctx = canvas->getGrContext();
|
||||||
|
SkASSERT(ctx);
|
||||||
|
|
||||||
GrOpMemoryPool* pool = ctx->contextPriv().opMemoryPool();
|
GrOpMemoryPool* pool = ctx->contextPriv().opMemoryPool();
|
||||||
|
|
||||||
SkASSERT(ctx);
|
|
||||||
sk_sp<GrRenderTargetContext> ccbuff =
|
sk_sp<GrRenderTargetContext> ccbuff =
|
||||||
ctx->contextPriv().makeDeferredRenderTargetContext(SkBackingFit::kApprox,
|
ctx->contextPriv().makeDeferredRenderTargetContext(SkBackingFit::kApprox,
|
||||||
this->width(), this->height(),
|
this->width(), this->height(),
|
||||||
@ -249,26 +251,27 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CCPRGeometryView::updateGpuData() {
|
void CCPRGeometryView::updateGpuData() {
|
||||||
|
using Verb = GrCCFillGeometry::Verb;
|
||||||
fTriPointInstances.reset();
|
fTriPointInstances.reset();
|
||||||
fQuadPointInstances.reset();
|
fQuadPointInstances.reset();
|
||||||
|
|
||||||
if (PrimitiveType::kCubics == fPrimitiveType) {
|
if (PrimitiveType::kCubics == fPrimitiveType) {
|
||||||
double t[2], s[2];
|
double t[2], s[2];
|
||||||
fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
|
fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
|
||||||
GrCCGeometry geometry;
|
GrCCFillGeometry geometry;
|
||||||
geometry.beginContour(fPoints[0]);
|
geometry.beginContour(fPoints[0]);
|
||||||
geometry.cubicTo(fPoints, kDebugBloat / 2, kDebugBloat / 2);
|
geometry.cubicTo(fPoints, kDebugBloat / 2, kDebugBloat / 2);
|
||||||
geometry.endContour();
|
geometry.endContour();
|
||||||
int ptsIdx = 0;
|
int ptsIdx = 0;
|
||||||
for (GrCCGeometry::Verb verb : geometry.verbs()) {
|
for (Verb verb : geometry.verbs()) {
|
||||||
switch (verb) {
|
switch (verb) {
|
||||||
case GrCCGeometry::Verb::kLineTo:
|
case Verb::kLineTo:
|
||||||
++ptsIdx;
|
++ptsIdx;
|
||||||
continue;
|
continue;
|
||||||
case GrCCGeometry::Verb::kMonotonicQuadraticTo:
|
case Verb::kMonotonicQuadraticTo:
|
||||||
ptsIdx += 2;
|
ptsIdx += 2;
|
||||||
continue;
|
continue;
|
||||||
case GrCCGeometry::Verb::kMonotonicCubicTo:
|
case Verb::kMonotonicCubicTo:
|
||||||
fQuadPointInstances.push_back().set(&geometry.points()[ptsIdx], 0, 0);
|
fQuadPointInstances.push_back().set(&geometry.points()[ptsIdx], 0, 0);
|
||||||
ptsIdx += 3;
|
ptsIdx += 3;
|
||||||
continue;
|
continue;
|
||||||
@ -278,7 +281,7 @@ void CCPRGeometryView::updateGpuData() {
|
|||||||
}
|
}
|
||||||
} else if (PrimitiveType::kTriangles != fPrimitiveType) {
|
} else if (PrimitiveType::kTriangles != fPrimitiveType) {
|
||||||
SkPoint P3[3] = {fPoints[0], fPoints[1], fPoints[3]};
|
SkPoint P3[3] = {fPoints[0], fPoints[1], fPoints[3]};
|
||||||
GrCCGeometry geometry;
|
GrCCFillGeometry geometry;
|
||||||
geometry.beginContour(P3[0]);
|
geometry.beginContour(P3[0]);
|
||||||
if (PrimitiveType::kQuadratics == fPrimitiveType) {
|
if (PrimitiveType::kQuadratics == fPrimitiveType) {
|
||||||
geometry.quadraticTo(P3);
|
geometry.quadraticTo(P3);
|
||||||
@ -288,23 +291,22 @@ void CCPRGeometryView::updateGpuData() {
|
|||||||
}
|
}
|
||||||
geometry.endContour();
|
geometry.endContour();
|
||||||
int ptsIdx = 0, conicWeightIdx = 0;
|
int ptsIdx = 0, conicWeightIdx = 0;
|
||||||
for (GrCCGeometry::Verb verb : geometry.verbs()) {
|
for (Verb verb : geometry.verbs()) {
|
||||||
if (GrCCGeometry::Verb::kBeginContour == verb ||
|
if (Verb::kBeginContour == verb ||
|
||||||
GrCCGeometry::Verb::kEndOpenContour == verb ||
|
Verb::kEndOpenContour == verb ||
|
||||||
GrCCGeometry::Verb::kEndClosedContour == verb) {
|
Verb::kEndClosedContour == verb) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (GrCCGeometry::Verb::kLineTo == verb) {
|
if (Verb::kLineTo == verb) {
|
||||||
++ptsIdx;
|
++ptsIdx;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
SkASSERT(GrCCGeometry::Verb::kMonotonicQuadraticTo == verb ||
|
SkASSERT(Verb::kMonotonicQuadraticTo == verb || Verb::kMonotonicConicTo == verb);
|
||||||
GrCCGeometry::Verb::kMonotonicConicTo == verb);
|
|
||||||
if (PrimitiveType::kQuadratics == fPrimitiveType &&
|
if (PrimitiveType::kQuadratics == fPrimitiveType &&
|
||||||
GrCCGeometry::Verb::kMonotonicQuadraticTo == verb) {
|
Verb::kMonotonicQuadraticTo == verb) {
|
||||||
fTriPointInstances.push_back().set(&geometry.points()[ptsIdx], Sk2f(0, 0));
|
fTriPointInstances.push_back().set(&geometry.points()[ptsIdx], Sk2f(0, 0));
|
||||||
} else if (PrimitiveType::kConics == fPrimitiveType &&
|
} else if (PrimitiveType::kConics == fPrimitiveType &&
|
||||||
GrCCGeometry::Verb::kMonotonicConicTo == verb) {
|
Verb::kMonotonicConicTo == verb) {
|
||||||
fQuadPointInstances.push_back().setW(&geometry.points()[ptsIdx], Sk2f(0, 0),
|
fQuadPointInstances.push_back().setW(&geometry.points()[ptsIdx], Sk2f(0, 0),
|
||||||
geometry.getConicWeight(conicWeightIdx++));
|
geometry.getConicWeight(conicWeightIdx++));
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
* found in the LICENSE file.
|
* found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "GrCCGeometry.h"
|
#include "GrCCFillGeometry.h"
|
||||||
|
|
||||||
#include "GrTypes.h"
|
#include "GrTypes.h"
|
||||||
#include "SkGeometry.h"
|
#include "SkGeometry.h"
|
||||||
@ -13,19 +13,14 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
// We convert between SkPoint and Sk2f freely throughout this file.
|
|
||||||
GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
|
|
||||||
GR_STATIC_ASSERT(2 * sizeof(float) == sizeof(SkPoint));
|
|
||||||
GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
|
|
||||||
|
|
||||||
static constexpr float kFlatnessThreshold = 1/16.f; // 1/16 of a pixel.
|
static constexpr float kFlatnessThreshold = 1/16.f; // 1/16 of a pixel.
|
||||||
|
|
||||||
void GrCCGeometry::beginPath() {
|
void GrCCFillGeometry::beginPath() {
|
||||||
SkASSERT(!fBuildingContour);
|
SkASSERT(!fBuildingContour);
|
||||||
fVerbs.push_back(Verb::kBeginPath);
|
fVerbs.push_back(Verb::kBeginPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCGeometry::beginContour(const SkPoint& pt) {
|
void GrCCFillGeometry::beginContour(const SkPoint& pt) {
|
||||||
SkASSERT(!fBuildingContour);
|
SkASSERT(!fBuildingContour);
|
||||||
// Store the current verb count in the fTriangles field for now. When we close the contour we
|
// 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.
|
// will use this value to calculate the actual number of triangles in its fan.
|
||||||
@ -38,7 +33,7 @@ void GrCCGeometry::beginContour(const SkPoint& pt) {
|
|||||||
SkDEBUGCODE(fBuildingContour = true);
|
SkDEBUGCODE(fBuildingContour = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCGeometry::lineTo(const SkPoint P[2]) {
|
void GrCCFillGeometry::lineTo(const SkPoint P[2]) {
|
||||||
SkASSERT(fBuildingContour);
|
SkASSERT(fBuildingContour);
|
||||||
SkASSERT(P[0] == fPoints.back());
|
SkASSERT(P[0] == fPoints.back());
|
||||||
Sk2f p0 = Sk2f::Load(P);
|
Sk2f p0 = Sk2f::Load(P);
|
||||||
@ -46,7 +41,7 @@ void GrCCGeometry::lineTo(const SkPoint P[2]) {
|
|||||||
this->appendLine(p0, p1);
|
this->appendLine(p0, p1);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void GrCCGeometry::appendLine(const Sk2f& p0, const Sk2f& p1) {
|
inline void GrCCFillGeometry::appendLine(const Sk2f& p0, const Sk2f& p1) {
|
||||||
SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
|
SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
|
||||||
if ((p0 == p1).allTrue()) {
|
if ((p0 == p1).allTrue()) {
|
||||||
return;
|
return;
|
||||||
@ -138,7 +133,7 @@ template<int N> static inline SkNx<N,float> lerp(const SkNx<N,float>& a, const S
|
|||||||
return SkNx_fma(t, b - a, a);
|
return SkNx_fma(t, b - a, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCGeometry::quadraticTo(const SkPoint P[3]) {
|
void GrCCFillGeometry::quadraticTo(const SkPoint P[3]) {
|
||||||
SkASSERT(fBuildingContour);
|
SkASSERT(fBuildingContour);
|
||||||
SkASSERT(P[0] == fPoints.back());
|
SkASSERT(P[0] == fPoints.back());
|
||||||
Sk2f p0 = Sk2f::Load(P);
|
Sk2f p0 = Sk2f::Load(P);
|
||||||
@ -155,7 +150,7 @@ void GrCCGeometry::quadraticTo(const SkPoint P[3]) {
|
|||||||
this->appendQuadratics(p0, p1, p2);
|
this->appendQuadratics(p0, p1, p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void GrCCGeometry::appendQuadratics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2) {
|
inline void GrCCFillGeometry::appendQuadratics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2) {
|
||||||
Sk2f tan0 = p1 - p0;
|
Sk2f tan0 = p1 - p0;
|
||||||
Sk2f tan1 = p2 - p1;
|
Sk2f tan1 = p2 - p1;
|
||||||
|
|
||||||
@ -193,7 +188,8 @@ inline void GrCCGeometry::appendQuadratics(const Sk2f& p0, const Sk2f& p1, const
|
|||||||
this->appendMonotonicQuadratic(p012, p12, p2);
|
this->appendMonotonicQuadratic(p012, p12, p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void GrCCGeometry::appendMonotonicQuadratic(const Sk2f& p0, const Sk2f& p1, const Sk2f& 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).
|
// Don't send curves to the GPU if we know they are nearly flat (or just very small).
|
||||||
if (are_collinear(p0, p1, p2)) {
|
if (are_collinear(p0, p1, p2)) {
|
||||||
this->appendLine(p0, p2);
|
this->appendLine(p0, p2);
|
||||||
@ -465,7 +461,7 @@ static inline void find_chops_around_loop_intersection(float padRadius, Sk2f t2,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCGeometry::cubicTo(const SkPoint P[4], float inflectPad, float loopIntersectPad) {
|
void GrCCFillGeometry::cubicTo(const SkPoint P[4], float inflectPad, float loopIntersectPad) {
|
||||||
SkASSERT(fBuildingContour);
|
SkASSERT(fBuildingContour);
|
||||||
SkASSERT(P[0] == fPoints.back());
|
SkASSERT(P[0] == fPoints.back());
|
||||||
|
|
||||||
@ -541,9 +537,9 @@ static inline void chop_cubic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2, co
|
|||||||
*abcd = lerp(*abc, *bcd, TT);
|
*abcd = lerp(*abc, *bcd, TT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f& p1,
|
void GrCCFillGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f& p1,
|
||||||
const Sk2f& p2, const Sk2f& p3, const float chops[], int numChops,
|
const Sk2f& p2, const Sk2f& p3, const float chops[],
|
||||||
float localT0, float localT1) {
|
int numChops, float localT0, float localT1) {
|
||||||
if (numChops) {
|
if (numChops) {
|
||||||
SkASSERT(numChops > 0);
|
SkASSERT(numChops > 0);
|
||||||
int midChopIdx = numChops/2;
|
int midChopIdx = numChops/2;
|
||||||
@ -576,8 +572,8 @@ void GrCCGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f
|
|||||||
this->appendCubics(mode, p0, p1, p2, p3);
|
this->appendCubics(mode, p0, p1, p2, p3);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f& p1,
|
void GrCCFillGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f& p1,
|
||||||
const Sk2f& p2, const Sk2f& p3, int maxSubdivisions) {
|
const Sk2f& p2, const Sk2f& p3, int maxSubdivisions) {
|
||||||
if (SkCubicType::kLoop != fCurrCubicType) {
|
if (SkCubicType::kLoop != fCurrCubicType) {
|
||||||
// Serpentines and cusps are always monotonic after chopping around inflection points.
|
// Serpentines and cusps are always monotonic after chopping around inflection points.
|
||||||
SkASSERT(!SkCubicIsDegenerate(fCurrCubicType));
|
SkASSERT(!SkCubicIsDegenerate(fCurrCubicType));
|
||||||
@ -672,11 +668,11 @@ static inline float find_midtangent(const Sk2f& tan0, const Sk2f& tan1,
|
|||||||
return std::abs(q*q - r) < std::abs(a*c - r) ? q/a : c/q;
|
return std::abs(q*q - r) < std::abs(a*c - r) ? q/a : c/q;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void GrCCGeometry::chopAndAppendCubicAtMidTangent(AppendCubicMode mode, const Sk2f& p0,
|
inline void GrCCFillGeometry::chopAndAppendCubicAtMidTangent(AppendCubicMode mode, const Sk2f& p0,
|
||||||
const Sk2f& p1, const Sk2f& p2,
|
const Sk2f& p1, const Sk2f& p2,
|
||||||
const Sk2f& p3, const Sk2f& tan0,
|
const Sk2f& p3, const Sk2f& tan0,
|
||||||
const Sk2f& tan1,
|
const Sk2f& tan1,
|
||||||
int maxFutureSubdivisions) {
|
int maxFutureSubdivisions) {
|
||||||
float midT = find_midtangent(tan0, tan1, p3 + (p1 - p2)*3 - p0,
|
float midT = find_midtangent(tan0, tan1, p3 + (p1 - p2)*3 - p0,
|
||||||
(p0 - p1*2 + p2)*2,
|
(p0 - p1*2 + p2)*2,
|
||||||
p1 - p0);
|
p1 - p0);
|
||||||
@ -694,7 +690,7 @@ inline void GrCCGeometry::chopAndAppendCubicAtMidTangent(AppendCubicMode mode, c
|
|||||||
this->appendCubics(mode, pT, p11, p12, p3, maxFutureSubdivisions);
|
this->appendCubics(mode, pT, p11, p12, p3, maxFutureSubdivisions);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCGeometry::conicTo(const SkPoint P[3], float w) {
|
void GrCCFillGeometry::conicTo(const SkPoint P[3], float w) {
|
||||||
SkASSERT(fBuildingContour);
|
SkASSERT(fBuildingContour);
|
||||||
SkASSERT(P[0] == fPoints.back());
|
SkASSERT(P[0] == fPoints.back());
|
||||||
Sk2f p0 = Sk2f::Load(P);
|
Sk2f p0 = Sk2f::Load(P);
|
||||||
@ -743,7 +739,8 @@ void GrCCGeometry::conicTo(const SkPoint P[3], float w) {
|
|||||||
this->appendMonotonicConic(p0, p1, p2, w);
|
this->appendMonotonicConic(p0, p1, p2, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCGeometry::appendMonotonicConic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2, float w) {
|
void GrCCFillGeometry::appendMonotonicConic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
|
||||||
|
float w) {
|
||||||
SkASSERT(w >= 0);
|
SkASSERT(w >= 0);
|
||||||
|
|
||||||
Sk2f base = p2 - p0;
|
Sk2f base = p2 - p0;
|
||||||
@ -784,7 +781,7 @@ void GrCCGeometry::appendMonotonicConic(const Sk2f& p0, const Sk2f& p1, const Sk
|
|||||||
++fCurrContourTallies.fConics;
|
++fCurrContourTallies.fConics;
|
||||||
}
|
}
|
||||||
|
|
||||||
GrCCGeometry::PrimitiveTallies GrCCGeometry::endContour() {
|
GrCCFillGeometry::PrimitiveTallies GrCCFillGeometry::endContour() {
|
||||||
SkASSERT(fBuildingContour);
|
SkASSERT(fBuildingContour);
|
||||||
SkASSERT(fVerbs.count() >= fCurrContourTallies.fTriangles);
|
SkASSERT(fVerbs.count() >= fCurrContourTallies.fTriangles);
|
||||||
|
|
@ -5,8 +5,8 @@
|
|||||||
* found in the LICENSE file.
|
* found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef GrGrCCGeometry_DEFINED
|
#ifndef GrGrCCFillGeometry_DEFINED
|
||||||
#define GrGrCCGeometry_DEFINED
|
#define GrGrCCFillGeometry_DEFINED
|
||||||
|
|
||||||
#include "SkGeometry.h"
|
#include "SkGeometry.h"
|
||||||
#include "SkNx.h"
|
#include "SkNx.h"
|
||||||
@ -15,14 +15,14 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class chops device-space contours up into a series of segments that CCPR knows how to
|
* This class chops device-space contours up into a series of segments that CCPR knows how to
|
||||||
* render. (See GrCCGeometry::Verb.)
|
* fill. (See GrCCFillGeometry::Verb.)
|
||||||
*
|
*
|
||||||
* NOTE: This must be done in device space, since an affine transformation can change whether a
|
* NOTE: This must be done in device space, since an affine transformation can change whether a
|
||||||
* curve is monotonic.
|
* curve is monotonic.
|
||||||
*/
|
*/
|
||||||
class GrCCGeometry {
|
class GrCCFillGeometry {
|
||||||
public:
|
public:
|
||||||
// These are the verbs that CCPR knows how to draw. If a path has any segments that don't map to
|
// 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
|
// 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.
|
// compact representation of what can later be expanded into GPU instance data.
|
||||||
enum class Verb : uint8_t {
|
enum class Verb : uint8_t {
|
||||||
@ -49,7 +49,7 @@ public:
|
|||||||
bool operator==(const PrimitiveTallies&);
|
bool operator==(const PrimitiveTallies&);
|
||||||
};
|
};
|
||||||
|
|
||||||
GrCCGeometry(int numSkPoints = 0, int numSkVerbs = 0, int numConicWeights = 0)
|
GrCCFillGeometry(int numSkPoints = 0, int numSkVerbs = 0, int numConicWeights = 0)
|
||||||
: fPoints(numSkPoints * 3) // Reserve for a 3x expansion in points and verbs.
|
: fPoints(numSkPoints * 3) // Reserve for a 3x expansion in points and verbs.
|
||||||
, fVerbs(numSkVerbs * 3)
|
, fVerbs(numSkVerbs * 3)
|
||||||
, fConicWeights(numConicWeights * 3/2) {}
|
, fConicWeights(numConicWeights * 3/2) {}
|
||||||
@ -64,17 +64,6 @@ public:
|
|||||||
fVerbs.reset();
|
fVerbs.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is included in case the caller needs to discard previously added contours. It is up to
|
|
||||||
// the caller to track counts and ensure we don't pop back into the middle of a different
|
|
||||||
// contour.
|
|
||||||
void resize_back(int numPoints, int numVerbs) {
|
|
||||||
SkASSERT(!fBuildingContour);
|
|
||||||
fPoints.resize_back(numPoints);
|
|
||||||
fVerbs.resize_back(numVerbs);
|
|
||||||
SkASSERT(fVerbs.empty() || fVerbs.back() == Verb::kEndOpenContour ||
|
|
||||||
fVerbs.back() == Verb::kEndClosedContour);
|
|
||||||
}
|
|
||||||
|
|
||||||
void beginPath();
|
void beginPath();
|
||||||
void beginContour(const SkPoint&);
|
void beginContour(const SkPoint&);
|
||||||
void lineTo(const SkPoint P[2]);
|
void lineTo(const SkPoint P[2]);
|
||||||
@ -129,7 +118,7 @@ private:
|
|||||||
SkSTArray<32, float, true> fConicWeights;
|
SkSTArray<32, float, true> fConicWeights;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void GrCCGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b) {
|
inline void GrCCFillGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b) {
|
||||||
fTriangles += b.fTriangles;
|
fTriangles += b.fTriangles;
|
||||||
fWeightedTriangles += b.fWeightedTriangles;
|
fWeightedTriangles += b.fWeightedTriangles;
|
||||||
fQuadratics += b.fQuadratics;
|
fQuadratics += b.fQuadratics;
|
||||||
@ -137,8 +126,8 @@ inline void GrCCGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b
|
|||||||
fConics += b.fConics;
|
fConics += b.fConics;
|
||||||
}
|
}
|
||||||
|
|
||||||
GrCCGeometry::PrimitiveTallies
|
GrCCFillGeometry::PrimitiveTallies
|
||||||
inline GrCCGeometry::PrimitiveTallies::operator-(const PrimitiveTallies& b) const {
|
inline GrCCFillGeometry::PrimitiveTallies::operator-(const PrimitiveTallies& b) const {
|
||||||
return {fTriangles - b.fTriangles,
|
return {fTriangles - b.fTriangles,
|
||||||
fWeightedTriangles - b.fWeightedTriangles,
|
fWeightedTriangles - b.fWeightedTriangles,
|
||||||
fQuadratics - b.fQuadratics,
|
fQuadratics - b.fQuadratics,
|
||||||
@ -146,7 +135,7 @@ inline GrCCGeometry::PrimitiveTallies::operator-(const PrimitiveTallies& b) cons
|
|||||||
fConics - b.fConics};
|
fConics - b.fConics};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool GrCCGeometry::PrimitiveTallies::operator==(const PrimitiveTallies& b) {
|
inline bool GrCCFillGeometry::PrimitiveTallies::operator==(const PrimitiveTallies& b) {
|
||||||
return fTriangles == b.fTriangles && fWeightedTriangles == b.fWeightedTriangles &&
|
return fTriangles == b.fTriangles && fWeightedTriangles == b.fWeightedTriangles &&
|
||||||
fQuadratics == b.fQuadratics && fCubics == b.fCubics && fConics == b.fConics;
|
fQuadratics == b.fQuadratics && fCubics == b.fCubics && fConics == b.fConics;
|
||||||
}
|
}
|
@ -5,7 +5,7 @@
|
|||||||
* found in the LICENSE file.
|
* found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "GrCCPathParser.h"
|
#include "GrCCFiller.h"
|
||||||
|
|
||||||
#include "GrCaps.h"
|
#include "GrCaps.h"
|
||||||
#include "GrGpuCommandBuffer.h"
|
#include "GrGpuCommandBuffer.h"
|
||||||
@ -15,105 +15,36 @@
|
|||||||
#include "SkPath.h"
|
#include "SkPath.h"
|
||||||
#include "SkPathPriv.h"
|
#include "SkPathPriv.h"
|
||||||
#include "SkPoint.h"
|
#include "SkPoint.h"
|
||||||
#include "ccpr/GrCCGeometry.h"
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
|
using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
|
||||||
using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
|
using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
|
||||||
|
|
||||||
GrCCPathParser::GrCCPathParser(int numPaths, const PathStats& pathStats)
|
GrCCFiller::GrCCFiller(int numPaths, const PathStats& pathStats)
|
||||||
// Overallocate by one point to accomodate for overflow with Sk4f. (See parsePath.)
|
: fGeometry(pathStats.fNumTotalSkPoints, pathStats.fNumTotalSkVerbs,
|
||||||
: fLocalDevPtsBuffer(pathStats.fMaxPointsPerPath + 1)
|
|
||||||
, fGeometry(pathStats.fNumTotalSkPoints, pathStats.fNumTotalSkVerbs,
|
|
||||||
pathStats.fNumTotalConicWeights)
|
pathStats.fNumTotalConicWeights)
|
||||||
, fPathsInfo(numPaths)
|
, fPathInfos(numPaths)
|
||||||
, fScissorSubBatches(numPaths)
|
, fScissorSubBatches(numPaths)
|
||||||
, fTotalPrimitiveCounts{PrimitiveTallies(), PrimitiveTallies()} {
|
, fTotalPrimitiveCounts{PrimitiveTallies(), PrimitiveTallies()} {
|
||||||
// Batches decide what to draw by looking where the previous one ended. Define initial batches
|
// 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
|
// that "end" at the beginning of the data. These will not be drawn, but will only be be read by
|
||||||
// the first actual batch.
|
// the first actual batch.
|
||||||
fScissorSubBatches.push_back() = {PrimitiveTallies(), SkIRect::MakeEmpty()};
|
fScissorSubBatches.push_back() = {PrimitiveTallies(), SkIRect::MakeEmpty()};
|
||||||
fCoverageCountBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count(),
|
fBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count(), PrimitiveTallies()};
|
||||||
PrimitiveTallies()};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCPathParser::parsePath(const SkMatrix& m, const SkPath& path, SkRect* devBounds,
|
void GrCCFiller::parseDeviceSpaceFill(const SkPath& path, const SkPoint* deviceSpacePts,
|
||||||
SkRect* devBounds45) {
|
GrScissorTest scissorTest, const SkIRect& clippedDevIBounds,
|
||||||
const SkPoint* pts = SkPathPriv::PointData(path);
|
const SkIVector& devToAtlasOffset) {
|
||||||
int numPts = path.countPoints();
|
SkASSERT(!fInstanceBuffer); // Can't call after prepareToDraw().
|
||||||
SkASSERT(numPts + 1 <= fLocalDevPtsBuffer.count());
|
SkASSERT(!path.isEmpty());
|
||||||
|
|
||||||
if (!numPts) {
|
int currPathPointsIdx = fGeometry.points().count();
|
||||||
devBounds->setEmpty();
|
int currPathVerbsIdx = fGeometry.verbs().count();
|
||||||
devBounds45->setEmpty();
|
PrimitiveTallies currPathPrimitiveCounts = PrimitiveTallies();
|
||||||
this->parsePath(path, nullptr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// m45 transforms path points into "45 degree" device space. A bounding box in this space gives
|
|
||||||
// the circumscribing octagon's diagonals. We could use SK_ScalarRoot2Over2, but an orthonormal
|
|
||||||
// transform is not necessary as long as the shader uses the correct inverse.
|
|
||||||
SkMatrix m45;
|
|
||||||
m45.setSinCos(1, 1);
|
|
||||||
m45.preConcat(m);
|
|
||||||
|
|
||||||
// X,Y,T are two parallel view matrices that accumulate two bounding boxes as they map points:
|
|
||||||
// device-space bounds and "45 degree" device-space bounds (| 1 -1 | * devCoords).
|
|
||||||
// | 1 1 |
|
|
||||||
Sk4f X = Sk4f(m.getScaleX(), m.getSkewY(), m45.getScaleX(), m45.getSkewY());
|
|
||||||
Sk4f Y = Sk4f(m.getSkewX(), m.getScaleY(), m45.getSkewX(), m45.getScaleY());
|
|
||||||
Sk4f T = Sk4f(m.getTranslateX(), m.getTranslateY(), m45.getTranslateX(), m45.getTranslateY());
|
|
||||||
|
|
||||||
// Map the path's points to device space and accumulate bounding boxes.
|
|
||||||
Sk4f devPt = SkNx_fma(Y, Sk4f(pts[0].y()), T);
|
|
||||||
devPt = SkNx_fma(X, Sk4f(pts[0].x()), devPt);
|
|
||||||
Sk4f topLeft = devPt;
|
|
||||||
Sk4f bottomRight = devPt;
|
|
||||||
|
|
||||||
// Store all 4 values [dev.x, dev.y, dev45.x, dev45.y]. We are only interested in the first two,
|
|
||||||
// and will overwrite [dev45.x, dev45.y] with the next point. This is why the dst buffer must
|
|
||||||
// be at least one larger than the number of points.
|
|
||||||
devPt.store(&fLocalDevPtsBuffer[0]);
|
|
||||||
|
|
||||||
for (int i = 1; i < numPts; ++i) {
|
|
||||||
devPt = SkNx_fma(Y, Sk4f(pts[i].y()), T);
|
|
||||||
devPt = SkNx_fma(X, Sk4f(pts[i].x()), devPt);
|
|
||||||
topLeft = Sk4f::Min(topLeft, devPt);
|
|
||||||
bottomRight = Sk4f::Max(bottomRight, devPt);
|
|
||||||
devPt.store(&fLocalDevPtsBuffer[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
SkPoint topLeftPts[2], bottomRightPts[2];
|
|
||||||
topLeft.store(topLeftPts);
|
|
||||||
bottomRight.store(bottomRightPts);
|
|
||||||
devBounds->setLTRB(topLeftPts[0].x(), topLeftPts[0].y(), bottomRightPts[0].x(),
|
|
||||||
bottomRightPts[0].y());
|
|
||||||
devBounds45->setLTRB(topLeftPts[1].x(), topLeftPts[1].y(), bottomRightPts[1].x(),
|
|
||||||
bottomRightPts[1].y());
|
|
||||||
|
|
||||||
this->parsePath(path, fLocalDevPtsBuffer.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrCCPathParser::parseDeviceSpacePath(const SkPath& deviceSpacePath) {
|
|
||||||
this->parsePath(deviceSpacePath, SkPathPriv::PointData(deviceSpacePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrCCPathParser::parsePath(const SkPath& path, const SkPoint* deviceSpacePts) {
|
|
||||||
SkASSERT(!fInstanceBuffer); // Can't call after finalize().
|
|
||||||
SkASSERT(!fParsingPath); // Call saveParsedPath() or discardParsedPath() for the last one first.
|
|
||||||
SkDEBUGCODE(fParsingPath = true);
|
|
||||||
SkASSERT(path.isEmpty() || deviceSpacePts);
|
|
||||||
|
|
||||||
fCurrPathPointsIdx = fGeometry.points().count();
|
|
||||||
fCurrPathVerbsIdx = fGeometry.verbs().count();
|
|
||||||
fCurrPathPrimitiveCounts = PrimitiveTallies();
|
|
||||||
|
|
||||||
fGeometry.beginPath();
|
fGeometry.beginPath();
|
||||||
|
|
||||||
if (path.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float* conicWeights = SkPathPriv::ConicWeightData(path);
|
const float* conicWeights = SkPathPriv::ConicWeightData(path);
|
||||||
int ptsIdx = 0;
|
int ptsIdx = 0;
|
||||||
int conicWeightsIdx = 0;
|
int conicWeightsIdx = 0;
|
||||||
@ -122,13 +53,17 @@ void GrCCPathParser::parsePath(const SkPath& path, const SkPoint* deviceSpacePts
|
|||||||
for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
|
for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
|
||||||
switch (verb) {
|
switch (verb) {
|
||||||
case SkPath::kMove_Verb:
|
case SkPath::kMove_Verb:
|
||||||
this->endContourIfNeeded(insideContour);
|
if (insideContour) {
|
||||||
|
currPathPrimitiveCounts += fGeometry.endContour();
|
||||||
|
}
|
||||||
fGeometry.beginContour(deviceSpacePts[ptsIdx]);
|
fGeometry.beginContour(deviceSpacePts[ptsIdx]);
|
||||||
++ptsIdx;
|
++ptsIdx;
|
||||||
insideContour = true;
|
insideContour = true;
|
||||||
continue;
|
continue;
|
||||||
case SkPath::kClose_Verb:
|
case SkPath::kClose_Verb:
|
||||||
this->endContourIfNeeded(insideContour);
|
if (insideContour) {
|
||||||
|
currPathPrimitiveCounts += fGeometry.endContour();
|
||||||
|
}
|
||||||
insideContour = false;
|
insideContour = false;
|
||||||
continue;
|
continue;
|
||||||
case SkPath::kLine_Verb:
|
case SkPath::kLine_Verb:
|
||||||
@ -155,123 +90,124 @@ void GrCCPathParser::parsePath(const SkPath& path, const SkPoint* deviceSpacePts
|
|||||||
SkASSERT(ptsIdx == path.countPoints());
|
SkASSERT(ptsIdx == path.countPoints());
|
||||||
SkASSERT(conicWeightsIdx == SkPathPriv::ConicWeightCnt(path));
|
SkASSERT(conicWeightsIdx == SkPathPriv::ConicWeightCnt(path));
|
||||||
|
|
||||||
this->endContourIfNeeded(insideContour);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrCCPathParser::endContourIfNeeded(bool insideContour) {
|
|
||||||
if (insideContour) {
|
if (insideContour) {
|
||||||
fCurrPathPrimitiveCounts += fGeometry.endContour();
|
currPathPrimitiveCounts += fGeometry.endContour();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void GrCCPathParser::saveParsedPath(GrScissorTest scissorTest, const SkIRect& clippedDevIBounds,
|
fPathInfos.emplace_back(scissorTest, devToAtlasOffset);
|
||||||
const SkIVector& devToAtlasOffset) {
|
|
||||||
SkASSERT(fParsingPath);
|
|
||||||
|
|
||||||
fPathsInfo.emplace_back(scissorTest, devToAtlasOffset);
|
|
||||||
|
|
||||||
// Tessellate fans from very large and/or simple paths, in order to reduce overdraw.
|
// Tessellate fans from very large and/or simple paths, in order to reduce overdraw.
|
||||||
int numVerbs = fGeometry.verbs().count() - fCurrPathVerbsIdx - 1;
|
int numVerbs = fGeometry.verbs().count() - currPathVerbsIdx - 1;
|
||||||
int64_t tessellationWork = (int64_t)numVerbs * (32 - SkCLZ(numVerbs)); // N log N.
|
int64_t tessellationWork = (int64_t)numVerbs * (32 - SkCLZ(numVerbs)); // N log N.
|
||||||
int64_t fanningWork = (int64_t)clippedDevIBounds.height() * clippedDevIBounds.width();
|
int64_t fanningWork = (int64_t)clippedDevIBounds.height() * clippedDevIBounds.width();
|
||||||
if (tessellationWork * (50*50) + (100*100) < fanningWork) { // Don't tessellate under 100x100.
|
if (tessellationWork * (50*50) + (100*100) < fanningWork) { // Don't tessellate under 100x100.
|
||||||
fCurrPathPrimitiveCounts.fTriangles =
|
fPathInfos.back().tessellateFan(fGeometry, currPathVerbsIdx, currPathPointsIdx,
|
||||||
fCurrPathPrimitiveCounts.fWeightedTriangles = 0;
|
clippedDevIBounds, &currPathPrimitiveCounts);
|
||||||
|
|
||||||
const SkTArray<GrCCGeometry::Verb, true>& verbs = fGeometry.verbs();
|
|
||||||
const SkTArray<SkPoint, true>& pts = fGeometry.points();
|
|
||||||
int ptsIdx = fCurrPathPointsIdx;
|
|
||||||
|
|
||||||
// Build an SkPath of the Redbook fan. We use "winding" fill type right now because we are
|
|
||||||
// producing a coverage count, and must fill in every region that has non-zero wind. The
|
|
||||||
// path processor will convert coverage count to the appropriate fill type later.
|
|
||||||
SkPath fan;
|
|
||||||
fan.setFillType(SkPath::kWinding_FillType);
|
|
||||||
SkASSERT(GrCCGeometry::Verb::kBeginPath == verbs[fCurrPathVerbsIdx]);
|
|
||||||
for (int i = fCurrPathVerbsIdx + 1; i < fGeometry.verbs().count(); ++i) {
|
|
||||||
switch (verbs[i]) {
|
|
||||||
case GrCCGeometry::Verb::kBeginPath:
|
|
||||||
SK_ABORT("Invalid GrCCGeometry");
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case GrCCGeometry::Verb::kBeginContour:
|
|
||||||
fan.moveTo(pts[ptsIdx++]);
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case GrCCGeometry::Verb::kLineTo:
|
|
||||||
fan.lineTo(pts[ptsIdx++]);
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case GrCCGeometry::Verb::kMonotonicQuadraticTo:
|
|
||||||
case GrCCGeometry::Verb::kMonotonicConicTo:
|
|
||||||
fan.lineTo(pts[ptsIdx + 1]);
|
|
||||||
ptsIdx += 2;
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case GrCCGeometry::Verb::kMonotonicCubicTo:
|
|
||||||
fan.lineTo(pts[ptsIdx + 2]);
|
|
||||||
ptsIdx += 3;
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case GrCCGeometry::Verb::kEndClosedContour:
|
|
||||||
case GrCCGeometry::Verb::kEndOpenContour:
|
|
||||||
fan.close();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GrTessellator::WindingVertex* vertices = nullptr;
|
|
||||||
int count = GrTessellator::PathToVertices(fan, std::numeric_limits<float>::infinity(),
|
|
||||||
SkRect::Make(clippedDevIBounds), &vertices);
|
|
||||||
SkASSERT(0 == count % 3);
|
|
||||||
for (int i = 0; i < count; i += 3) {
|
|
||||||
int tessWinding = vertices[i].fWinding;
|
|
||||||
SkASSERT(tessWinding == vertices[i + 1].fWinding);
|
|
||||||
SkASSERT(tessWinding == vertices[i + 2].fWinding);
|
|
||||||
|
|
||||||
// Ensure this triangle's points actually wind in the same direction as tessWinding.
|
|
||||||
// CCPR shaders use the sign of wind to determine which direction to bloat, so even for
|
|
||||||
// "wound" triangles the winding sign and point ordering need to agree.
|
|
||||||
float ax = vertices[i].fPos.fX - vertices[i + 1].fPos.fX;
|
|
||||||
float ay = vertices[i].fPos.fY - vertices[i + 1].fPos.fY;
|
|
||||||
float bx = vertices[i].fPos.fX - vertices[i + 2].fPos.fX;
|
|
||||||
float by = vertices[i].fPos.fY - vertices[i + 2].fPos.fY;
|
|
||||||
float wind = ax*by - ay*bx;
|
|
||||||
if ((wind > 0) != (-tessWinding > 0)) { // Tessellator has opposite winding sense.
|
|
||||||
std::swap(vertices[i + 1].fPos, vertices[i + 2].fPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (1 == abs(tessWinding)) {
|
|
||||||
++fCurrPathPrimitiveCounts.fTriangles;
|
|
||||||
} else {
|
|
||||||
++fCurrPathPrimitiveCounts.fWeightedTriangles;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fPathsInfo.back().adoptFanTessellation(vertices, count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fTotalPrimitiveCounts[(int)scissorTest] += fCurrPathPrimitiveCounts;
|
fTotalPrimitiveCounts[(int)scissorTest] += currPathPrimitiveCounts;
|
||||||
|
|
||||||
if (GrScissorTest::kEnabled == scissorTest) {
|
if (GrScissorTest::kEnabled == scissorTest) {
|
||||||
fScissorSubBatches.push_back() = {fTotalPrimitiveCounts[(int)GrScissorTest::kEnabled],
|
fScissorSubBatches.push_back() = {fTotalPrimitiveCounts[(int)GrScissorTest::kEnabled],
|
||||||
clippedDevIBounds.makeOffset(devToAtlasOffset.fX,
|
clippedDevIBounds.makeOffset(devToAtlasOffset.fX,
|
||||||
devToAtlasOffset.fY)};
|
devToAtlasOffset.fY)};
|
||||||
}
|
}
|
||||||
|
|
||||||
SkDEBUGCODE(fParsingPath = false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCPathParser::discardParsedPath() {
|
void GrCCFiller::PathInfo::tessellateFan(const GrCCFillGeometry& geometry, int verbsIdx,
|
||||||
SkASSERT(fParsingPath);
|
int ptsIdx, const SkIRect& clippedDevIBounds,
|
||||||
fGeometry.resize_back(fCurrPathPointsIdx, fCurrPathVerbsIdx);
|
PrimitiveTallies* newTriangleCounts) {
|
||||||
SkDEBUGCODE(fParsingPath = false);
|
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. We use "winding" fill type right now because we are
|
||||||
|
// producing a coverage count, and must fill in every region that has non-zero wind. The
|
||||||
|
// path processor will convert coverage count to the appropriate fill type later.
|
||||||
|
SkPath fan;
|
||||||
|
fan.setFillType(SkPath::kWinding_FillType);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GrTessellator::WindingVertex* vertices = nullptr;
|
||||||
|
fFanTessellationCount =
|
||||||
|
GrTessellator::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 tessWinding = vertices[i].fWinding;
|
||||||
|
SkASSERT(tessWinding == vertices[i + 1].fWinding);
|
||||||
|
SkASSERT(tessWinding == vertices[i + 2].fWinding);
|
||||||
|
|
||||||
|
// Ensure this triangle's points actually wind in the same direction as tessWinding.
|
||||||
|
// CCPR shaders use the sign of wind to determine which direction to bloat, so even for
|
||||||
|
// "wound" triangles the winding sign and point ordering need to agree.
|
||||||
|
float ax = vertices[i].fPos.fX - vertices[i + 1].fPos.fX;
|
||||||
|
float ay = vertices[i].fPos.fY - vertices[i + 1].fPos.fY;
|
||||||
|
float bx = vertices[i].fPos.fX - vertices[i + 2].fPos.fX;
|
||||||
|
float by = vertices[i].fPos.fY - vertices[i + 2].fPos.fY;
|
||||||
|
float wind = ax*by - ay*bx;
|
||||||
|
if ((wind > 0) != (-tessWinding > 0)) { // Tessellator has opposite winding sense.
|
||||||
|
std::swap(vertices[i + 1].fPos, vertices[i + 2].fPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 == abs(tessWinding)) {
|
||||||
|
++newTriangleCounts->fTriangles;
|
||||||
|
} else {
|
||||||
|
++newTriangleCounts->fWeightedTriangles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fFanTessellation.reset(vertices);
|
||||||
}
|
}
|
||||||
|
|
||||||
GrCCPathParser::CoverageCountBatchID GrCCPathParser::closeCurrentBatch() {
|
GrCCFiller::BatchID GrCCFiller::closeCurrentBatch() {
|
||||||
SkASSERT(!fInstanceBuffer);
|
SkASSERT(!fInstanceBuffer);
|
||||||
SkASSERT(!fCoverageCountBatches.empty());
|
SkASSERT(!fBatches.empty());
|
||||||
|
|
||||||
const auto& lastBatch = fCoverageCountBatches.back();
|
const auto& lastBatch = fBatches.back();
|
||||||
int maxMeshes = 1 + fScissorSubBatches.count() - lastBatch.fEndScissorSubBatchIdx;
|
int maxMeshes = 1 + fScissorSubBatches.count() - lastBatch.fEndScissorSubBatchIdx;
|
||||||
fMaxMeshesPerDraw = SkTMax(fMaxMeshesPerDraw, maxMeshes);
|
fMaxMeshesPerDraw = SkTMax(fMaxMeshesPerDraw, maxMeshes);
|
||||||
|
|
||||||
@ -282,12 +218,12 @@ GrCCPathParser::CoverageCountBatchID GrCCPathParser::closeCurrentBatch() {
|
|||||||
lastScissorSubBatch.fEndPrimitiveIndices;
|
lastScissorSubBatch.fEndPrimitiveIndices;
|
||||||
|
|
||||||
// This will invalidate lastBatch.
|
// This will invalidate lastBatch.
|
||||||
fCoverageCountBatches.push_back() = {
|
fBatches.push_back() = {
|
||||||
fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled],
|
fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled],
|
||||||
fScissorSubBatches.count(),
|
fScissorSubBatches.count(),
|
||||||
batchTotalCounts
|
batchTotalCounts
|
||||||
};
|
};
|
||||||
return fCoverageCountBatches.count() - 1;
|
return fBatches.count() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emits a contour's triangle fan.
|
// Emits a contour's triangle fan.
|
||||||
@ -334,7 +270,7 @@ static void emit_tessellated_fan(const GrTessellator::WindingVertex* vertices, i
|
|||||||
const Sk2f& devToAtlasOffset,
|
const Sk2f& devToAtlasOffset,
|
||||||
TriPointInstance* triPointInstanceData,
|
TriPointInstance* triPointInstanceData,
|
||||||
QuadPointInstance* quadPointInstanceData,
|
QuadPointInstance* quadPointInstanceData,
|
||||||
GrCCGeometry::PrimitiveTallies* indices) {
|
GrCCFillGeometry::PrimitiveTallies* indices) {
|
||||||
for (int i = 0; i < numVertices; i += 3) {
|
for (int i = 0; i < numVertices; i += 3) {
|
||||||
if (1 == abs(vertices[i].fWinding)) {
|
if (1 == abs(vertices[i].fWinding)) {
|
||||||
triPointInstanceData[indices->fTriangles++].set(vertices[i].fPos, vertices[i + 1].fPos,
|
triPointInstanceData[indices->fTriangles++].set(vertices[i].fPos, vertices[i + 1].fPos,
|
||||||
@ -347,11 +283,12 @@ static void emit_tessellated_fan(const GrTessellator::WindingVertex* vertices, i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
bool GrCCFiller::prepareToDraw(GrOnFlushResourceProvider* onFlushRP) {
|
||||||
SkASSERT(!fParsingPath); // Call saveParsedPath() or discardParsedPath().
|
using Verb = GrCCFillGeometry::Verb;
|
||||||
SkASSERT(fCoverageCountBatches.back().fEndNonScissorIndices == // Call closeCurrentBatch().
|
SkASSERT(!fInstanceBuffer);
|
||||||
|
SkASSERT(fBatches.back().fEndNonScissorIndices == // Call closeCurrentBatch().
|
||||||
fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled]);
|
fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled]);
|
||||||
SkASSERT(fCoverageCountBatches.back().fEndScissorSubBatchIdx == fScissorSubBatches.count());
|
SkASSERT(fBatches.back().fEndScissorSubBatchIdx == fScissorSubBatches.count());
|
||||||
|
|
||||||
// Here we build a single instance buffer to share with every internal batch.
|
// Here we build a single instance buffer to share with every internal batch.
|
||||||
//
|
//
|
||||||
@ -393,6 +330,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
|||||||
fInstanceBuffer = onFlushRP->makeBuffer(kVertex_GrBufferType,
|
fInstanceBuffer = onFlushRP->makeBuffer(kVertex_GrBufferType,
|
||||||
quadEndIdx * sizeof(QuadPointInstance));
|
quadEndIdx * sizeof(QuadPointInstance));
|
||||||
if (!fInstanceBuffer) {
|
if (!fInstanceBuffer) {
|
||||||
|
SkDebugf("WARNING: failed to allocate CCPR fill instance buffer.\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,7 +339,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
|||||||
reinterpret_cast<QuadPointInstance*>(triPointInstanceData);
|
reinterpret_cast<QuadPointInstance*>(triPointInstanceData);
|
||||||
SkASSERT(quadPointInstanceData);
|
SkASSERT(quadPointInstanceData);
|
||||||
|
|
||||||
PathInfo* nextPathInfo = fPathsInfo.begin();
|
PathInfo* nextPathInfo = fPathInfos.begin();
|
||||||
Sk2f devToAtlasOffset;
|
Sk2f devToAtlasOffset;
|
||||||
PrimitiveTallies instanceIndices[2] = {fBaseInstances[0], fBaseInstances[1]};
|
PrimitiveTallies instanceIndices[2] = {fBaseInstances[0], fBaseInstances[1]};
|
||||||
PrimitiveTallies* currIndices = nullptr;
|
PrimitiveTallies* currIndices = nullptr;
|
||||||
@ -413,9 +351,9 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
|||||||
int nextConicWeightIdx = 0;
|
int nextConicWeightIdx = 0;
|
||||||
|
|
||||||
// Expand the ccpr verbs into GPU instance buffers.
|
// Expand the ccpr verbs into GPU instance buffers.
|
||||||
for (GrCCGeometry::Verb verb : fGeometry.verbs()) {
|
for (Verb verb : fGeometry.verbs()) {
|
||||||
switch (verb) {
|
switch (verb) {
|
||||||
case GrCCGeometry::Verb::kBeginPath:
|
case Verb::kBeginPath:
|
||||||
SkASSERT(currFan.empty());
|
SkASSERT(currFan.empty());
|
||||||
currIndices = &instanceIndices[(int)nextPathInfo->scissorTest()];
|
currIndices = &instanceIndices[(int)nextPathInfo->scissorTest()];
|
||||||
devToAtlasOffset = Sk2f(static_cast<float>(nextPathInfo->devToAtlasOffset().fX),
|
devToAtlasOffset = Sk2f(static_cast<float>(nextPathInfo->devToAtlasOffset().fX),
|
||||||
@ -429,7 +367,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
|||||||
++nextPathInfo;
|
++nextPathInfo;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case GrCCGeometry::Verb::kBeginContour:
|
case Verb::kBeginContour:
|
||||||
SkASSERT(currFan.empty());
|
SkASSERT(currFan.empty());
|
||||||
++ptsIdx;
|
++ptsIdx;
|
||||||
if (!currFanIsTessellated) {
|
if (!currFanIsTessellated) {
|
||||||
@ -437,7 +375,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case GrCCGeometry::Verb::kLineTo:
|
case Verb::kLineTo:
|
||||||
++ptsIdx;
|
++ptsIdx;
|
||||||
if (!currFanIsTessellated) {
|
if (!currFanIsTessellated) {
|
||||||
SkASSERT(!currFan.empty());
|
SkASSERT(!currFan.empty());
|
||||||
@ -445,7 +383,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case GrCCGeometry::Verb::kMonotonicQuadraticTo:
|
case Verb::kMonotonicQuadraticTo:
|
||||||
triPointInstanceData[currIndices->fQuadratics++].set(&pts[ptsIdx],
|
triPointInstanceData[currIndices->fQuadratics++].set(&pts[ptsIdx],
|
||||||
devToAtlasOffset);
|
devToAtlasOffset);
|
||||||
ptsIdx += 2;
|
ptsIdx += 2;
|
||||||
@ -455,7 +393,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case GrCCGeometry::Verb::kMonotonicCubicTo:
|
case Verb::kMonotonicCubicTo:
|
||||||
quadPointInstanceData[currIndices->fCubics++].set(&pts[ptsIdx], devToAtlasOffset[0],
|
quadPointInstanceData[currIndices->fCubics++].set(&pts[ptsIdx], devToAtlasOffset[0],
|
||||||
devToAtlasOffset[1]);
|
devToAtlasOffset[1]);
|
||||||
ptsIdx += 3;
|
ptsIdx += 3;
|
||||||
@ -465,7 +403,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case GrCCGeometry::Verb::kMonotonicConicTo:
|
case Verb::kMonotonicConicTo:
|
||||||
quadPointInstanceData[currIndices->fConics++].setW(
|
quadPointInstanceData[currIndices->fConics++].setW(
|
||||||
&pts[ptsIdx], devToAtlasOffset,
|
&pts[ptsIdx], devToAtlasOffset,
|
||||||
fGeometry.getConicWeight(nextConicWeightIdx));
|
fGeometry.getConicWeight(nextConicWeightIdx));
|
||||||
@ -477,13 +415,13 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case GrCCGeometry::Verb::kEndClosedContour: // endPt == startPt.
|
case Verb::kEndClosedContour: // endPt == startPt.
|
||||||
if (!currFanIsTessellated) {
|
if (!currFanIsTessellated) {
|
||||||
SkASSERT(!currFan.empty());
|
SkASSERT(!currFan.empty());
|
||||||
currFan.pop_back();
|
currFan.pop_back();
|
||||||
}
|
}
|
||||||
// fallthru.
|
// fallthru.
|
||||||
case GrCCGeometry::Verb::kEndOpenContour: // endPt != startPt.
|
case Verb::kEndOpenContour: // endPt != startPt.
|
||||||
SkASSERT(!currFanIsTessellated || currFan.empty());
|
SkASSERT(!currFanIsTessellated || currFan.empty());
|
||||||
if (!currFanIsTessellated && currFan.count() >= 3) {
|
if (!currFanIsTessellated && currFan.count() >= 3) {
|
||||||
int fanSize = currFan.count();
|
int fanSize = currFan.count();
|
||||||
@ -503,7 +441,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
|||||||
|
|
||||||
fInstanceBuffer->unmap();
|
fInstanceBuffer->unmap();
|
||||||
|
|
||||||
SkASSERT(nextPathInfo == fPathsInfo.end());
|
SkASSERT(nextPathInfo == fPathInfos.end());
|
||||||
SkASSERT(ptsIdx == pts.count() - 1);
|
SkASSERT(ptsIdx == pts.count() - 1);
|
||||||
SkASSERT(instanceIndices[0].fTriangles == fBaseInstances[1].fTriangles);
|
SkASSERT(instanceIndices[0].fTriangles == fBaseInstances[1].fTriangles);
|
||||||
SkASSERT(instanceIndices[1].fTriangles == fBaseInstances[0].fQuadratics);
|
SkASSERT(instanceIndices[1].fTriangles == fBaseInstances[0].fQuadratics);
|
||||||
@ -522,13 +460,13 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCountBatchID batchID,
|
void GrCCFiller::drawFills(GrOpFlushState* flushState, BatchID batchID,
|
||||||
const SkIRect& drawBounds) const {
|
const SkIRect& drawBounds) const {
|
||||||
using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
|
using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
|
||||||
|
|
||||||
SkASSERT(fInstanceBuffer);
|
SkASSERT(fInstanceBuffer);
|
||||||
|
|
||||||
const PrimitiveTallies& batchTotalCounts = fCoverageCountBatches[batchID].fTotalPrimitiveCounts;
|
const PrimitiveTallies& batchTotalCounts = fBatches[batchID].fTotalPrimitiveCounts;
|
||||||
|
|
||||||
GrPipeline pipeline(flushState->drawOpArgs().fProxy, GrScissorTest::kEnabled,
|
GrPipeline pipeline(flushState->drawOpArgs().fProxy, GrScissorTest::kEnabled,
|
||||||
SkBlendMode::kPlus);
|
SkBlendMode::kPlus);
|
||||||
@ -559,11 +497,10 @@ void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCount
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCPathParser::drawPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
|
void GrCCFiller::drawPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
|
||||||
CoverageCountBatchID batchID,
|
BatchID batchID, GrCCCoverageProcessor::PrimitiveType primitiveType,
|
||||||
GrCCCoverageProcessor::PrimitiveType primitiveType,
|
int PrimitiveTallies::*instanceType,
|
||||||
int PrimitiveTallies::*instanceType,
|
const SkIRect& drawBounds) const {
|
||||||
const SkIRect& drawBounds) const {
|
|
||||||
SkASSERT(pipeline.isScissorEnabled());
|
SkASSERT(pipeline.isScissorEnabled());
|
||||||
|
|
||||||
// Don't call reset(), as that also resets the reserve count.
|
// Don't call reset(), as that also resets the reserve count.
|
||||||
@ -573,9 +510,9 @@ void GrCCPathParser::drawPrimitives(GrOpFlushState* flushState, const GrPipeline
|
|||||||
GrCCCoverageProcessor proc(flushState->resourceProvider(), primitiveType);
|
GrCCCoverageProcessor proc(flushState->resourceProvider(), primitiveType);
|
||||||
|
|
||||||
SkASSERT(batchID > 0);
|
SkASSERT(batchID > 0);
|
||||||
SkASSERT(batchID < fCoverageCountBatches.count());
|
SkASSERT(batchID < fBatches.count());
|
||||||
const CoverageCountBatch& previousBatch = fCoverageCountBatches[batchID - 1];
|
const Batch& previousBatch = fBatches[batchID - 1];
|
||||||
const CoverageCountBatch& batch = fCoverageCountBatches[batchID];
|
const Batch& batch = fBatches[batchID];
|
||||||
SkDEBUGCODE(int totalInstanceCount = 0);
|
SkDEBUGCODE(int totalInstanceCount = 0);
|
||||||
|
|
||||||
if (int instanceCount = batch.fEndNonScissorIndices.*instanceType -
|
if (int instanceCount = batch.fEndNonScissorIndices.*instanceType -
|
132
src/gpu/ccpr/GrCCFiller.h
Normal file
132
src/gpu/ccpr/GrCCFiller.h
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* 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 "GrMesh.h"
|
||||||
|
#include "SkPath.h"
|
||||||
|
#include "SkPathPriv.h"
|
||||||
|
#include "SkRect.h"
|
||||||
|
#include "SkRefCnt.h"
|
||||||
|
#include "GrTessellator.h"
|
||||||
|
#include "ccpr/GrCCCoverageProcessor.h"
|
||||||
|
#include "ccpr/GrCCFillGeometry.h"
|
||||||
|
#include "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:
|
||||||
|
struct PathStats {
|
||||||
|
int fMaxPointsPerPath = 0;
|
||||||
|
int fNumTotalSkPoints = 0;
|
||||||
|
int fNumTotalSkVerbs = 0;
|
||||||
|
int fNumTotalConicWeights = 0;
|
||||||
|
|
||||||
|
void statPath(const SkPath&);
|
||||||
|
};
|
||||||
|
|
||||||
|
GrCCFiller(int numPaths, const PathStats&);
|
||||||
|
|
||||||
|
// 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*, BatchID, const SkIRect& drawBounds) 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 GrTessellator::WindingVertex* fanTessellation() const {
|
||||||
|
SkASSERT(this->hasFanTessellation());
|
||||||
|
return fFanTessellation.get();
|
||||||
|
}
|
||||||
|
void tessellateFan(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 GrTessellator::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 drawPrimitives(GrOpFlushState*, const GrPipeline&, BatchID,
|
||||||
|
GrCCCoverageProcessor::PrimitiveType, int PrimitiveTallies::*instanceType,
|
||||||
|
const SkIRect& drawBounds) const;
|
||||||
|
|
||||||
|
GrCCFillGeometry fGeometry;
|
||||||
|
SkSTArray<32, PathInfo, true> fPathInfos;
|
||||||
|
SkSTArray<32, Batch, true> fBatches;
|
||||||
|
SkSTArray<32, ScissorSubBatch, true> fScissorSubBatches;
|
||||||
|
PrimitiveTallies fTotalPrimitiveCounts[kNumScissorModes];
|
||||||
|
int fMaxMeshesPerDraw = 0;
|
||||||
|
|
||||||
|
sk_sp<GrBuffer> fInstanceBuffer;
|
||||||
|
PrimitiveTallies fBaseInstances[kNumScissorModes];
|
||||||
|
mutable SkSTArray<32, GrMesh> fMeshesScratchBuffer;
|
||||||
|
mutable SkSTArray<32, SkIRect> fScissorRectScratchBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void GrCCFiller::PathStats::statPath(const SkPath& path) {
|
||||||
|
fMaxPointsPerPath = SkTMax(fMaxPointsPerPath, path.countPoints());
|
||||||
|
fNumTotalSkPoints += path.countPoints();
|
||||||
|
fNumTotalSkVerbs += path.countVerbs();
|
||||||
|
fNumTotalConicWeights += SkPathPriv::ConicWeightCnt(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
#include "GrShape.h"
|
#include "GrShape.h"
|
||||||
#include "SkNx.h"
|
#include "SkNx.h"
|
||||||
#include "ccpr/GrCCPathParser.h"
|
|
||||||
|
|
||||||
// The maximum number of cache entries we allow in our own cache.
|
// The maximum number of cache entries we allow in our own cache.
|
||||||
static constexpr int kMaxCacheCount = 1 << 16;
|
static constexpr int kMaxCacheCount = 1 << 16;
|
||||||
|
@ -1,166 +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 "GrMesh.h"
|
|
||||||
#include "SkPath.h"
|
|
||||||
#include "SkPathPriv.h"
|
|
||||||
#include "SkRect.h"
|
|
||||||
#include "SkRefCnt.h"
|
|
||||||
#include "GrTessellator.h"
|
|
||||||
#include "ccpr/GrCCCoverageProcessor.h"
|
|
||||||
#include "ccpr/GrCCGeometry.h"
|
|
||||||
#include "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 GrCCPathParser {
|
|
||||||
public:
|
|
||||||
struct PathStats {
|
|
||||||
int fMaxPointsPerPath = 0;
|
|
||||||
int fNumTotalSkPoints = 0;
|
|
||||||
int fNumTotalSkVerbs = 0;
|
|
||||||
int fNumTotalConicWeights = 0;
|
|
||||||
|
|
||||||
void statPath(const SkPath&);
|
|
||||||
};
|
|
||||||
|
|
||||||
GrCCPathParser(int numPaths, const PathStats&);
|
|
||||||
|
|
||||||
~GrCCPathParser() {
|
|
||||||
// Enforce the contract that the client always calls saveParsedPath or discardParsedPath.
|
|
||||||
SkASSERT(!fParsingPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
using CoverageCountBatchID = int;
|
|
||||||
|
|
||||||
// Parses an SkPath into a temporary staging area. The path will not be included in the current
|
|
||||||
// batch until there is a matching call to saveParsedPath. The user must complement this with a
|
|
||||||
// following call to either saveParsedPath or discardParsedPath.
|
|
||||||
//
|
|
||||||
// Returns two tight bounding boxes: device space and "45 degree" (| 1 -1 | * devCoords) space.
|
|
||||||
// | 1 1 |
|
|
||||||
void parsePath(const SkMatrix&, const SkPath&, SkRect* devBounds, SkRect* devBounds45);
|
|
||||||
|
|
||||||
// Parses a device-space SkPath into a temporary staging area. The path will not be included in
|
|
||||||
// the current batch until there is a matching call to saveParsedPath. The user must complement
|
|
||||||
// this with a following call to either saveParsedPath or discardParsedPath.
|
|
||||||
void parseDeviceSpacePath(const SkPath&);
|
|
||||||
|
|
||||||
// Commits the currently-parsed path from staging to the current batch, and specifies whether
|
|
||||||
// the mask should be rendered with a scissor in effect. Accepts an optional post-device-space
|
|
||||||
// translate for placement in an atlas.
|
|
||||||
void saveParsedPath(GrScissorTest, const SkIRect& clippedDevIBounds,
|
|
||||||
const SkIVector& devToAtlasOffset);
|
|
||||||
void discardParsedPath();
|
|
||||||
|
|
||||||
// Compiles the outstanding saved paths into a batch, and returns an ID that can be used to draw
|
|
||||||
// their coverage counts in the future.
|
|
||||||
CoverageCountBatchID closeCurrentBatch();
|
|
||||||
|
|
||||||
// Builds internal GPU buffers and prepares for calls to drawCoverageCount. Caller must close
|
|
||||||
// the current batch before calling this method, and cannot parse new paths afer.
|
|
||||||
bool finalize(GrOnFlushResourceProvider*);
|
|
||||||
|
|
||||||
// Called after finalize. Draws the given batch of parsed paths.
|
|
||||||
void drawCoverageCount(GrOpFlushState*, CoverageCountBatchID, const SkIRect& drawBounds) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr int kNumScissorModes = 2;
|
|
||||||
using PrimitiveTallies = GrCCGeometry::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 GrTessellator::WindingVertex* fanTessellation() const {
|
|
||||||
SkASSERT(this->hasFanTessellation());
|
|
||||||
return fFanTessellation.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void adoptFanTessellation(const GrTessellator::WindingVertex* vertices, int count) {
|
|
||||||
SkASSERT(count >= 0);
|
|
||||||
fFanTessellation.reset(vertices);
|
|
||||||
fFanTessellationCount = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
GrScissorTest fScissorTest;
|
|
||||||
SkIVector fDevToAtlasOffset; // Translation from device space to location in atlas.
|
|
||||||
int fFanTessellationCount = -1;
|
|
||||||
std::unique_ptr<const GrTessellator::WindingVertex[]> fFanTessellation;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Defines a batch of CCPR primitives. Start indices are deduced by looking at the previous
|
|
||||||
// CoverageCountBatch in the list.
|
|
||||||
struct CoverageCountBatch {
|
|
||||||
PrimitiveTallies fEndNonScissorIndices;
|
|
||||||
int fEndScissorSubBatchIdx;
|
|
||||||
PrimitiveTallies fTotalPrimitiveCounts;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Defines a sub-batch from CoverageCountBatch 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 parsePath(const SkPath&, const SkPoint* deviceSpacePts);
|
|
||||||
void endContourIfNeeded(bool insideContour);
|
|
||||||
|
|
||||||
void drawPrimitives(GrOpFlushState*, const GrPipeline&, CoverageCountBatchID,
|
|
||||||
GrCCCoverageProcessor::PrimitiveType, int PrimitiveTallies::*instanceType,
|
|
||||||
const SkIRect& drawBounds) const;
|
|
||||||
|
|
||||||
// Staging area for the path being parsed.
|
|
||||||
SkDEBUGCODE(int fParsingPath = false);
|
|
||||||
const SkAutoSTArray<32, SkPoint> fLocalDevPtsBuffer;
|
|
||||||
int fCurrPathPointsIdx;
|
|
||||||
int fCurrPathVerbsIdx;
|
|
||||||
PrimitiveTallies fCurrPathPrimitiveCounts;
|
|
||||||
|
|
||||||
GrCCGeometry fGeometry;
|
|
||||||
SkSTArray<32, PathInfo, true> fPathsInfo;
|
|
||||||
SkSTArray<32, CoverageCountBatch, true> fCoverageCountBatches;
|
|
||||||
SkSTArray<32, ScissorSubBatch, true> fScissorSubBatches;
|
|
||||||
PrimitiveTallies fTotalPrimitiveCounts[kNumScissorModes];
|
|
||||||
int fMaxMeshesPerDraw = 0;
|
|
||||||
|
|
||||||
sk_sp<GrBuffer> fInstanceBuffer;
|
|
||||||
PrimitiveTallies fBaseInstances[kNumScissorModes];
|
|
||||||
mutable SkSTArray<32, GrMesh> fMeshesScratchBuffer;
|
|
||||||
mutable SkSTArray<32, SkIRect> fScissorRectScratchBuffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline void GrCCPathParser::PathStats::statPath(const SkPath& path) {
|
|
||||||
fMaxPointsPerPath = SkTMax(fMaxPointsPerPath, path.countPoints());
|
|
||||||
fNumTotalSkPoints += path.countPoints();
|
|
||||||
fNumTotalSkVerbs += path.countVerbs();
|
|
||||||
fNumTotalConicWeights += SkPathPriv::ConicWeightCnt(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -15,7 +15,7 @@
|
|||||||
#include "SkMakeUnique.h"
|
#include "SkMakeUnique.h"
|
||||||
#include "ccpr/GrCCPathCache.h"
|
#include "ccpr/GrCCPathCache.h"
|
||||||
|
|
||||||
using CoverageCountBatchID = GrCCPathParser::CoverageCountBatchID;
|
using FillBatchID = GrCCFiller::BatchID;
|
||||||
using PathInstance = GrCCPathProcessor::Instance;
|
using PathInstance = GrCCPathProcessor::Instance;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -101,7 +101,7 @@ public:
|
|||||||
|
|
||||||
static std::unique_ptr<GrDrawOp> Make(GrContext* context,
|
static std::unique_ptr<GrDrawOp> Make(GrContext* context,
|
||||||
sk_sp<const GrCCPerFlushResources> resources,
|
sk_sp<const GrCCPerFlushResources> resources,
|
||||||
CoverageCountBatchID batchID, const SkISize& drawBounds) {
|
FillBatchID batchID, const SkISize& drawBounds) {
|
||||||
GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
|
GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
|
||||||
|
|
||||||
return pool->allocate<RenderAtlasOp>(std::move(resources), batchID, drawBounds);
|
return pool->allocate<RenderAtlasOp>(std::move(resources), batchID, drawBounds);
|
||||||
@ -111,20 +111,20 @@ public:
|
|||||||
const char* name() const override { return "RenderAtlasOp (CCPR)"; }
|
const char* name() const override { return "RenderAtlasOp (CCPR)"; }
|
||||||
|
|
||||||
void onExecute(GrOpFlushState* flushState) override {
|
void onExecute(GrOpFlushState* flushState) override {
|
||||||
fResources->pathParser().drawCoverageCount(flushState, fBatchID, fDrawBounds);
|
fResources->filler().drawFills(flushState, fBatchID, fDrawBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class ::GrOpMemoryPool; // for ctor
|
friend class ::GrOpMemoryPool; // for ctor
|
||||||
|
|
||||||
RenderAtlasOp(sk_sp<const GrCCPerFlushResources> resources, CoverageCountBatchID batchID,
|
RenderAtlasOp(sk_sp<const GrCCPerFlushResources> resources, FillBatchID batchID,
|
||||||
const SkISize& drawBounds)
|
const SkISize& drawBounds)
|
||||||
: AtlasOp(ClassID(), std::move(resources), drawBounds)
|
: AtlasOp(ClassID(), std::move(resources), drawBounds)
|
||||||
, fBatchID(batchID)
|
, fBatchID(batchID)
|
||||||
, fDrawBounds(SkIRect::MakeWH(drawBounds.width(), drawBounds.height())) {
|
, fDrawBounds(SkIRect::MakeWH(drawBounds.width(), drawBounds.height())) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const CoverageCountBatchID fBatchID;
|
const FillBatchID fBatchID;
|
||||||
const SkIRect fDrawBounds;
|
const SkIRect fDrawBounds;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -138,7 +138,11 @@ static int inst_buffer_count(const GrCCPerFlushResourceSpecs& specs) {
|
|||||||
|
|
||||||
GrCCPerFlushResources::GrCCPerFlushResources(GrOnFlushResourceProvider* onFlushRP,
|
GrCCPerFlushResources::GrCCPerFlushResources(GrOnFlushResourceProvider* onFlushRP,
|
||||||
const GrCCPerFlushResourceSpecs& specs)
|
const GrCCPerFlushResourceSpecs& specs)
|
||||||
: fPathParser(specs.fNumRenderedPaths + specs.fNumClipPaths, specs.fRenderedPathStats)
|
// Overallocate by one point so we can call Sk4f::Store at the final SkPoint in the array.
|
||||||
|
// (See transform_path_pts below.)
|
||||||
|
// FIXME: instead use built-in instructions to write only the first two lanes of an Sk4f.
|
||||||
|
: fLocalDevPtsBuffer(specs.fRenderedPathStats.fMaxPointsPerPath + 1)
|
||||||
|
, fFiller(specs.fNumRenderedPaths + specs.fNumClipPaths, specs.fRenderedPathStats)
|
||||||
, fCopyAtlasStack(kAlpha_8_GrPixelConfig, specs.fCopyAtlasSpecs, onFlushRP->caps())
|
, fCopyAtlasStack(kAlpha_8_GrPixelConfig, specs.fCopyAtlasSpecs, onFlushRP->caps())
|
||||||
, fRenderedAtlasStack(kAlpha_half_GrPixelConfig, specs.fRenderedAtlasSpecs,
|
, fRenderedAtlasStack(kAlpha_half_GrPixelConfig, specs.fRenderedAtlasSpecs,
|
||||||
onFlushRP->caps())
|
onFlushRP->caps())
|
||||||
@ -183,6 +187,56 @@ GrCCAtlas* GrCCPerFlushResources::copyPathToCachedAtlas(const GrCCPathCacheEntry
|
|||||||
return &fCopyAtlasStack.current();
|
return &fCopyAtlasStack.current();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void transform_path_pts(const SkMatrix& m, const SkPath& path,
|
||||||
|
const SkAutoSTArray<32, SkPoint>& outDevPts, SkRect* devBounds,
|
||||||
|
SkRect* devBounds45) {
|
||||||
|
const SkPoint* pts = SkPathPriv::PointData(path);
|
||||||
|
int numPts = path.countPoints();
|
||||||
|
SkASSERT(numPts + 1 <= outDevPts.count());
|
||||||
|
SkASSERT(numPts);
|
||||||
|
|
||||||
|
// m45 transforms path points into "45 degree" device space. A bounding box in this space gives
|
||||||
|
// the circumscribing octagon's diagonals. We could use SK_ScalarRoot2Over2, but an orthonormal
|
||||||
|
// transform is not necessary as long as the shader uses the correct inverse.
|
||||||
|
SkMatrix m45;
|
||||||
|
m45.setSinCos(1, 1);
|
||||||
|
m45.preConcat(m);
|
||||||
|
|
||||||
|
// X,Y,T are two parallel view matrices that accumulate two bounding boxes as they map points:
|
||||||
|
// device-space bounds and "45 degree" device-space bounds (| 1 -1 | * devCoords).
|
||||||
|
// | 1 1 |
|
||||||
|
Sk4f X = Sk4f(m.getScaleX(), m.getSkewY(), m45.getScaleX(), m45.getSkewY());
|
||||||
|
Sk4f Y = Sk4f(m.getSkewX(), m.getScaleY(), m45.getSkewX(), m45.getScaleY());
|
||||||
|
Sk4f T = Sk4f(m.getTranslateX(), m.getTranslateY(), m45.getTranslateX(), m45.getTranslateY());
|
||||||
|
|
||||||
|
// Map the path's points to device space and accumulate bounding boxes.
|
||||||
|
Sk4f devPt = SkNx_fma(Y, Sk4f(pts[0].y()), T);
|
||||||
|
devPt = SkNx_fma(X, Sk4f(pts[0].x()), devPt);
|
||||||
|
Sk4f topLeft = devPt;
|
||||||
|
Sk4f bottomRight = devPt;
|
||||||
|
|
||||||
|
// Store all 4 values [dev.x, dev.y, dev45.x, dev45.y]. We are only interested in the first two,
|
||||||
|
// and will overwrite [dev45.x, dev45.y] with the next point. This is why the dst buffer must
|
||||||
|
// be at least one larger than the number of points.
|
||||||
|
devPt.store(&outDevPts[0]);
|
||||||
|
|
||||||
|
for (int i = 1; i < numPts; ++i) {
|
||||||
|
devPt = SkNx_fma(Y, Sk4f(pts[i].y()), T);
|
||||||
|
devPt = SkNx_fma(X, Sk4f(pts[i].x()), devPt);
|
||||||
|
topLeft = Sk4f::Min(topLeft, devPt);
|
||||||
|
bottomRight = Sk4f::Max(bottomRight, devPt);
|
||||||
|
devPt.store(&outDevPts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
SkPoint topLeftPts[2], bottomRightPts[2];
|
||||||
|
topLeft.store(topLeftPts);
|
||||||
|
bottomRight.store(bottomRightPts);
|
||||||
|
devBounds->setLTRB(topLeftPts[0].x(), topLeftPts[0].y(), bottomRightPts[0].x(),
|
||||||
|
bottomRightPts[0].y());
|
||||||
|
devBounds45->setLTRB(topLeftPts[1].x(), topLeftPts[1].y(), bottomRightPts[1].x(),
|
||||||
|
bottomRightPts[1].y());
|
||||||
|
}
|
||||||
|
|
||||||
const GrCCAtlas* GrCCPerFlushResources::renderPathInAtlas(const SkIRect& clipIBounds,
|
const GrCCAtlas* GrCCPerFlushResources::renderPathInAtlas(const SkIRect& clipIBounds,
|
||||||
const SkMatrix& m, const SkPath& path,
|
const SkMatrix& m, const SkPath& path,
|
||||||
SkRect* devBounds, SkRect* devBounds45,
|
SkRect* devBounds, SkRect* devBounds45,
|
||||||
@ -191,13 +245,24 @@ const GrCCAtlas* GrCCPerFlushResources::renderPathInAtlas(const SkIRect& clipIBo
|
|||||||
SkASSERT(this->isMapped());
|
SkASSERT(this->isMapped());
|
||||||
SkASSERT(fNextPathInstanceIdx < fEndPathInstance);
|
SkASSERT(fNextPathInstanceIdx < fEndPathInstance);
|
||||||
|
|
||||||
fPathParser.parsePath(m, path, devBounds, devBounds45);
|
if (path.isEmpty()) {
|
||||||
|
SkDEBUGCODE(--fEndPathInstance);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
transform_path_pts(m, path, fLocalDevPtsBuffer, devBounds, devBounds45);
|
||||||
devBounds->roundOut(devIBounds);
|
devBounds->roundOut(devIBounds);
|
||||||
|
|
||||||
if (!this->placeParsedPathInAtlas(clipIBounds, *devIBounds, devToAtlasOffset)) {
|
GrScissorTest scissorTest;
|
||||||
|
SkIRect clippedPathIBounds;
|
||||||
|
if (!this->placeRenderedPathInAtlas(clipIBounds, *devIBounds, &scissorTest, &clippedPathIBounds,
|
||||||
|
devToAtlasOffset)) {
|
||||||
SkDEBUGCODE(--fEndPathInstance);
|
SkDEBUGCODE(--fEndPathInstance);
|
||||||
return nullptr; // Path was degenerate or clipped away.
|
return nullptr; // Path was degenerate or clipped away.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fFiller.parseDeviceSpaceFill(path, fLocalDevPtsBuffer.begin(), scissorTest, clippedPathIBounds,
|
||||||
|
*devToAtlasOffset);
|
||||||
return &fRenderedAtlasStack.current();
|
return &fRenderedAtlasStack.current();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,37 +270,45 @@ const GrCCAtlas* GrCCPerFlushResources::renderDeviceSpacePathInAtlas(
|
|||||||
const SkIRect& clipIBounds, const SkPath& devPath, const SkIRect& devPathIBounds,
|
const SkIRect& clipIBounds, const SkPath& devPath, const SkIRect& devPathIBounds,
|
||||||
SkIVector* devToAtlasOffset) {
|
SkIVector* devToAtlasOffset) {
|
||||||
SkASSERT(this->isMapped());
|
SkASSERT(this->isMapped());
|
||||||
fPathParser.parseDeviceSpacePath(devPath);
|
|
||||||
if (!this->placeParsedPathInAtlas(clipIBounds, devPathIBounds, devToAtlasOffset)) {
|
if (devPath.isEmpty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GrScissorTest scissorTest;
|
||||||
|
SkIRect clippedPathIBounds;
|
||||||
|
if (!this->placeRenderedPathInAtlas(clipIBounds, devPathIBounds, &scissorTest,
|
||||||
|
&clippedPathIBounds, devToAtlasOffset)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
fFiller.parseDeviceSpaceFill(devPath, SkPathPriv::PointData(devPath), scissorTest,
|
||||||
|
clippedPathIBounds, *devToAtlasOffset);
|
||||||
return &fRenderedAtlasStack.current();
|
return &fRenderedAtlasStack.current();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GrCCPerFlushResources::placeParsedPathInAtlas(const SkIRect& clipIBounds,
|
bool GrCCPerFlushResources::placeRenderedPathInAtlas(const SkIRect& clipIBounds,
|
||||||
const SkIRect& pathIBounds,
|
const SkIRect& pathIBounds,
|
||||||
SkIVector* devToAtlasOffset) {
|
GrScissorTest* scissorTest,
|
||||||
GrScissorTest scissorTest;
|
SkIRect* clippedPathIBounds,
|
||||||
SkIRect clippedPathIBounds;
|
SkIVector* devToAtlasOffset) {
|
||||||
if (clipIBounds.contains(pathIBounds)) {
|
if (clipIBounds.contains(pathIBounds)) {
|
||||||
clippedPathIBounds = pathIBounds;
|
*clippedPathIBounds = pathIBounds;
|
||||||
scissorTest = GrScissorTest::kDisabled;
|
*scissorTest = GrScissorTest::kDisabled;
|
||||||
} else if (clippedPathIBounds.intersect(clipIBounds, pathIBounds)) {
|
} else if (clippedPathIBounds->intersect(clipIBounds, pathIBounds)) {
|
||||||
scissorTest = GrScissorTest::kEnabled;
|
*scissorTest = GrScissorTest::kEnabled;
|
||||||
} else {
|
} else {
|
||||||
fPathParser.discardParsedPath();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GrCCAtlas* retiredAtlas =
|
if (GrCCAtlas* retiredAtlas =
|
||||||
fRenderedAtlasStack.addRect(clippedPathIBounds, devToAtlasOffset)) {
|
fRenderedAtlasStack.addRect(*clippedPathIBounds, devToAtlasOffset)) {
|
||||||
// We did not fit in the previous coverage count atlas and it was retired. Close the path
|
// 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
|
// 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().
|
// render this batch into the retired atlas during finalize().
|
||||||
CoverageCountBatchID batchID = fPathParser.closeCurrentBatch();
|
FillBatchID batchID = fFiller.closeCurrentBatch();
|
||||||
retiredAtlas->setUserBatchID(batchID);
|
retiredAtlas->setUserBatchID(batchID);
|
||||||
}
|
}
|
||||||
fPathParser.saveParsedPath(scissorTest, clippedPathIBounds, *devToAtlasOffset);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,14 +326,13 @@ bool GrCCPerFlushResources::finalize(GrOnFlushResourceProvider* onFlushRP,
|
|||||||
fCopyAtlasStack.current().setUserBatchID(fNextCopyInstanceIdx);
|
fCopyAtlasStack.current().setUserBatchID(fNextCopyInstanceIdx);
|
||||||
}
|
}
|
||||||
if (!fRenderedAtlasStack.empty()) {
|
if (!fRenderedAtlasStack.empty()) {
|
||||||
CoverageCountBatchID batchID = fPathParser.closeCurrentBatch();
|
FillBatchID batchID = fFiller.closeCurrentBatch();
|
||||||
fRenderedAtlasStack.current().setUserBatchID(batchID);
|
fRenderedAtlasStack.current().setUserBatchID(batchID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the GPU buffers to render path coverage counts. (This must not happen until after the
|
// Build the GPU buffers to render path coverage counts. (This must not happen until after the
|
||||||
// final call to fPathParser.closeCurrentBatch().)
|
// final call to fPathParser.closeCurrentBatch().)
|
||||||
if (!fPathParser.finalize(onFlushRP)) {
|
if (!fFiller.prepareToDraw(onFlushRP)) {
|
||||||
SkDebugf("WARNING: failed to allocate GPU buffers for CCPR. No paths will be drawn.\n");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,5 +392,5 @@ void GrCCPerFlushResourceSpecs::convertCopiesToRenders() {
|
|||||||
fRenderedPathStats.fNumTotalSkPoints += fCopyPathStats.fNumTotalSkPoints;
|
fRenderedPathStats.fNumTotalSkPoints += fCopyPathStats.fNumTotalSkPoints;
|
||||||
fRenderedPathStats.fNumTotalSkVerbs += fCopyPathStats.fNumTotalSkVerbs;
|
fRenderedPathStats.fNumTotalSkVerbs += fCopyPathStats.fNumTotalSkVerbs;
|
||||||
fRenderedPathStats.fNumTotalConicWeights += fCopyPathStats.fNumTotalConicWeights;
|
fRenderedPathStats.fNumTotalConicWeights += fCopyPathStats.fNumTotalConicWeights;
|
||||||
fCopyPathStats = GrCCPathParser::PathStats();
|
fCopyPathStats = GrCCFiller::PathStats();
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
#include "GrNonAtomicRef.h"
|
#include "GrNonAtomicRef.h"
|
||||||
#include "ccpr/GrCCAtlas.h"
|
#include "ccpr/GrCCAtlas.h"
|
||||||
#include "ccpr/GrCCPathParser.h"
|
#include "ccpr/GrCCFiller.h"
|
||||||
#include "ccpr/GrCCPathProcessor.h"
|
#include "ccpr/GrCCPathProcessor.h"
|
||||||
|
|
||||||
class GrCCPathCacheEntry;
|
class GrCCPathCacheEntry;
|
||||||
@ -24,12 +24,12 @@ struct GrCCPerFlushResourceSpecs {
|
|||||||
int fNumCachedPaths = 0;
|
int fNumCachedPaths = 0;
|
||||||
|
|
||||||
int fNumCopiedPaths = 0;
|
int fNumCopiedPaths = 0;
|
||||||
GrCCPathParser::PathStats fCopyPathStats;
|
GrCCFiller::PathStats fCopyPathStats;
|
||||||
GrCCAtlas::Specs fCopyAtlasSpecs;
|
GrCCAtlas::Specs fCopyAtlasSpecs;
|
||||||
|
|
||||||
int fNumRenderedPaths = 0;
|
int fNumRenderedPaths = 0;
|
||||||
int fNumClipPaths = 0;
|
int fNumClipPaths = 0;
|
||||||
GrCCPathParser::PathStats fRenderedPathStats;
|
GrCCFiller::PathStats fRenderedPathStats;
|
||||||
GrCCAtlas::Specs fRenderedAtlasSpecs;
|
GrCCAtlas::Specs fRenderedAtlasSpecs;
|
||||||
|
|
||||||
bool isEmpty() const {
|
bool isEmpty() const {
|
||||||
@ -85,7 +85,7 @@ public:
|
|||||||
SkTArray<sk_sp<GrRenderTargetContext>>* out);
|
SkTArray<sk_sp<GrRenderTargetContext>>* out);
|
||||||
|
|
||||||
// Accessors used by draw calls, once the resources have been finalized.
|
// Accessors used by draw calls, once the resources have been finalized.
|
||||||
const GrCCPathParser& pathParser() const { SkASSERT(!this->isMapped()); return fPathParser; }
|
const GrCCFiller& filler() const { SkASSERT(!this->isMapped()); return fFiller; }
|
||||||
const GrBuffer* indexBuffer() const { SkASSERT(!this->isMapped()); return fIndexBuffer.get(); }
|
const GrBuffer* indexBuffer() const { SkASSERT(!this->isMapped()); return fIndexBuffer.get(); }
|
||||||
const GrBuffer* vertexBuffer() const { SkASSERT(!this->isMapped()); return fVertexBuffer.get();}
|
const GrBuffer* vertexBuffer() const { SkASSERT(!this->isMapped()); return fVertexBuffer.get();}
|
||||||
GrBuffer* instanceBuffer() const { SkASSERT(!this->isMapped()); return fInstanceBuffer.get(); }
|
GrBuffer* instanceBuffer() const { SkASSERT(!this->isMapped()); return fInstanceBuffer.get(); }
|
||||||
@ -107,10 +107,12 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool placeParsedPathInAtlas(const SkIRect& clipIBounds, const SkIRect& pathIBounds,
|
bool placeRenderedPathInAtlas(const SkIRect& clipIBounds, const SkIRect& pathIBounds,
|
||||||
SkIVector* devToAtlasOffset);
|
GrScissorTest*, SkIRect* clippedPathIBounds,
|
||||||
|
SkIVector* devToAtlasOffset);
|
||||||
|
|
||||||
GrCCPathParser fPathParser;
|
const SkAutoSTArray<32, SkPoint> fLocalDevPtsBuffer;
|
||||||
|
GrCCFiller fFiller;
|
||||||
GrCCAtlasStack fCopyAtlasStack;
|
GrCCAtlasStack fCopyAtlasStack;
|
||||||
GrCCAtlasStack fRenderedAtlasStack;
|
GrCCAtlasStack fRenderedAtlasStack;
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
#include "ccpr/GrCCClipProcessor.h"
|
#include "ccpr/GrCCClipProcessor.h"
|
||||||
#include "ccpr/GrCCDrawPathsOp.h"
|
#include "ccpr/GrCCDrawPathsOp.h"
|
||||||
#include "ccpr/GrCCPathCache.h"
|
#include "ccpr/GrCCPathCache.h"
|
||||||
#include "ccpr/GrCCPathParser.h"
|
|
||||||
|
|
||||||
using PathInstance = GrCCPathProcessor::Instance;
|
using PathInstance = GrCCPathProcessor::Instance;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user