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 "ccpr/GrCCGeometry.h"
|
||||
#include "ccpr/GrCCFillGeometry.h"
|
||||
#include "SkGeometry.h"
|
||||
|
||||
static int kNumBaseLoops = 50000;
|
||||
@ -61,7 +61,7 @@ public:
|
||||
private:
|
||||
SkPoint fPoints[5];
|
||||
SkString fName;
|
||||
GrCCGeometry fGeometry{4*100*kNumBaseLoops, 2*100*kNumBaseLoops};
|
||||
GrCCFillGeometry fGeometry{4*100*kNumBaseLoops, 2*100*kNumBaseLoops};
|
||||
|
||||
typedef Benchmark INHERITED;
|
||||
};
|
@ -51,7 +51,7 @@ bench_sources = [
|
||||
"$_bench/GeometryBench.cpp",
|
||||
"$_bench/GMBench.cpp",
|
||||
"$_bench/GradientBench.cpp",
|
||||
"$_bench/GrCCGeometryBench.cpp",
|
||||
"$_bench/GrCCFillGeometryBench.cpp",
|
||||
"$_bench/GrMemoryPoolBench.cpp",
|
||||
"$_bench/GrMipMapBench.cpp",
|
||||
"$_bench/GrResourceCacheBench.cpp",
|
||||
|
@ -313,12 +313,12 @@ skia_gpu_sources = [
|
||||
"$_src/gpu/ccpr/GrCCCubicShader.h",
|
||||
"$_src/gpu/ccpr/GrCCDrawPathsOp.cpp",
|
||||
"$_src/gpu/ccpr/GrCCDrawPathsOp.h",
|
||||
"$_src/gpu/ccpr/GrCCGeometry.cpp",
|
||||
"$_src/gpu/ccpr/GrCCGeometry.h",
|
||||
"$_src/gpu/ccpr/GrCCFiller.cpp",
|
||||
"$_src/gpu/ccpr/GrCCFiller.h",
|
||||
"$_src/gpu/ccpr/GrCCFillGeometry.cpp",
|
||||
"$_src/gpu/ccpr/GrCCFillGeometry.h",
|
||||
"$_src/gpu/ccpr/GrCCPathCache.cpp",
|
||||
"$_src/gpu/ccpr/GrCCPathCache.h",
|
||||
"$_src/gpu/ccpr/GrCCPathParser.cpp",
|
||||
"$_src/gpu/ccpr/GrCCPathParser.h",
|
||||
"$_src/gpu/ccpr/GrCCPathProcessor.cpp",
|
||||
"$_src/gpu/ccpr/GrCCPathProcessor.h",
|
||||
"$_src/gpu/ccpr/GrCCPerFlushResources.cpp",
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "SkPath.h"
|
||||
#include "SkRectPriv.h"
|
||||
#include "ccpr/GrCCCoverageProcessor.h"
|
||||
#include "ccpr/GrCCGeometry.h"
|
||||
#include "ccpr/GrCCFillGeometry.h"
|
||||
#include "gl/GrGLGpu.cpp"
|
||||
#include "glsl/GrGLSLFragmentProcessor.h"
|
||||
#include "ops/GrDrawOp.h"
|
||||
@ -76,7 +76,8 @@ class CCPRGeometryView::DrawCoverageCountOp : public GrDrawOp {
|
||||
|
||||
public:
|
||||
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 {
|
||||
@ -191,9 +192,10 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
||||
if (GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext()) {
|
||||
// Render coverage count.
|
||||
GrContext* ctx = canvas->getGrContext();
|
||||
SkASSERT(ctx);
|
||||
|
||||
GrOpMemoryPool* pool = ctx->contextPriv().opMemoryPool();
|
||||
|
||||
SkASSERT(ctx);
|
||||
sk_sp<GrRenderTargetContext> ccbuff =
|
||||
ctx->contextPriv().makeDeferredRenderTargetContext(SkBackingFit::kApprox,
|
||||
this->width(), this->height(),
|
||||
@ -249,26 +251,27 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
||||
}
|
||||
|
||||
void CCPRGeometryView::updateGpuData() {
|
||||
using Verb = GrCCFillGeometry::Verb;
|
||||
fTriPointInstances.reset();
|
||||
fQuadPointInstances.reset();
|
||||
|
||||
if (PrimitiveType::kCubics == fPrimitiveType) {
|
||||
double t[2], s[2];
|
||||
fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
|
||||
GrCCGeometry geometry;
|
||||
GrCCFillGeometry geometry;
|
||||
geometry.beginContour(fPoints[0]);
|
||||
geometry.cubicTo(fPoints, kDebugBloat / 2, kDebugBloat / 2);
|
||||
geometry.endContour();
|
||||
int ptsIdx = 0;
|
||||
for (GrCCGeometry::Verb verb : geometry.verbs()) {
|
||||
for (Verb verb : geometry.verbs()) {
|
||||
switch (verb) {
|
||||
case GrCCGeometry::Verb::kLineTo:
|
||||
case Verb::kLineTo:
|
||||
++ptsIdx;
|
||||
continue;
|
||||
case GrCCGeometry::Verb::kMonotonicQuadraticTo:
|
||||
case Verb::kMonotonicQuadraticTo:
|
||||
ptsIdx += 2;
|
||||
continue;
|
||||
case GrCCGeometry::Verb::kMonotonicCubicTo:
|
||||
case Verb::kMonotonicCubicTo:
|
||||
fQuadPointInstances.push_back().set(&geometry.points()[ptsIdx], 0, 0);
|
||||
ptsIdx += 3;
|
||||
continue;
|
||||
@ -278,7 +281,7 @@ void CCPRGeometryView::updateGpuData() {
|
||||
}
|
||||
} else if (PrimitiveType::kTriangles != fPrimitiveType) {
|
||||
SkPoint P3[3] = {fPoints[0], fPoints[1], fPoints[3]};
|
||||
GrCCGeometry geometry;
|
||||
GrCCFillGeometry geometry;
|
||||
geometry.beginContour(P3[0]);
|
||||
if (PrimitiveType::kQuadratics == fPrimitiveType) {
|
||||
geometry.quadraticTo(P3);
|
||||
@ -288,23 +291,22 @@ void CCPRGeometryView::updateGpuData() {
|
||||
}
|
||||
geometry.endContour();
|
||||
int ptsIdx = 0, conicWeightIdx = 0;
|
||||
for (GrCCGeometry::Verb verb : geometry.verbs()) {
|
||||
if (GrCCGeometry::Verb::kBeginContour == verb ||
|
||||
GrCCGeometry::Verb::kEndOpenContour == verb ||
|
||||
GrCCGeometry::Verb::kEndClosedContour == verb) {
|
||||
for (Verb verb : geometry.verbs()) {
|
||||
if (Verb::kBeginContour == verb ||
|
||||
Verb::kEndOpenContour == verb ||
|
||||
Verb::kEndClosedContour == verb) {
|
||||
continue;
|
||||
}
|
||||
if (GrCCGeometry::Verb::kLineTo == verb) {
|
||||
if (Verb::kLineTo == verb) {
|
||||
++ptsIdx;
|
||||
continue;
|
||||
}
|
||||
SkASSERT(GrCCGeometry::Verb::kMonotonicQuadraticTo == verb ||
|
||||
GrCCGeometry::Verb::kMonotonicConicTo == verb);
|
||||
SkASSERT(Verb::kMonotonicQuadraticTo == verb || Verb::kMonotonicConicTo == verb);
|
||||
if (PrimitiveType::kQuadratics == fPrimitiveType &&
|
||||
GrCCGeometry::Verb::kMonotonicQuadraticTo == verb) {
|
||||
Verb::kMonotonicQuadraticTo == verb) {
|
||||
fTriPointInstances.push_back().set(&geometry.points()[ptsIdx], Sk2f(0, 0));
|
||||
} else if (PrimitiveType::kConics == fPrimitiveType &&
|
||||
GrCCGeometry::Verb::kMonotonicConicTo == verb) {
|
||||
Verb::kMonotonicConicTo == verb) {
|
||||
fQuadPointInstances.push_back().setW(&geometry.points()[ptsIdx], Sk2f(0, 0),
|
||||
geometry.getConicWeight(conicWeightIdx++));
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "GrCCGeometry.h"
|
||||
#include "GrCCFillGeometry.h"
|
||||
|
||||
#include "GrTypes.h"
|
||||
#include "SkGeometry.h"
|
||||
@ -13,19 +13,14 @@
|
||||
#include <cmath>
|
||||
#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.
|
||||
|
||||
void GrCCGeometry::beginPath() {
|
||||
void GrCCFillGeometry::beginPath() {
|
||||
SkASSERT(!fBuildingContour);
|
||||
fVerbs.push_back(Verb::kBeginPath);
|
||||
}
|
||||
|
||||
void GrCCGeometry::beginContour(const SkPoint& pt) {
|
||||
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.
|
||||
@ -38,7 +33,7 @@ void GrCCGeometry::beginContour(const SkPoint& pt) {
|
||||
SkDEBUGCODE(fBuildingContour = true);
|
||||
}
|
||||
|
||||
void GrCCGeometry::lineTo(const SkPoint P[2]) {
|
||||
void GrCCFillGeometry::lineTo(const SkPoint P[2]) {
|
||||
SkASSERT(fBuildingContour);
|
||||
SkASSERT(P[0] == fPoints.back());
|
||||
Sk2f p0 = Sk2f::Load(P);
|
||||
@ -46,7 +41,7 @@ void GrCCGeometry::lineTo(const SkPoint P[2]) {
|
||||
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]));
|
||||
if ((p0 == p1).allTrue()) {
|
||||
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);
|
||||
}
|
||||
|
||||
void GrCCGeometry::quadraticTo(const SkPoint P[3]) {
|
||||
void GrCCFillGeometry::quadraticTo(const SkPoint P[3]) {
|
||||
SkASSERT(fBuildingContour);
|
||||
SkASSERT(P[0] == fPoints.back());
|
||||
Sk2f p0 = Sk2f::Load(P);
|
||||
@ -155,7 +150,7 @@ void GrCCGeometry::quadraticTo(const SkPoint P[3]) {
|
||||
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 tan1 = p2 - p1;
|
||||
|
||||
@ -193,7 +188,8 @@ inline void GrCCGeometry::appendQuadratics(const Sk2f& p0, const Sk2f& p1, const
|
||||
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).
|
||||
if (are_collinear(p0, p1, 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(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);
|
||||
}
|
||||
|
||||
void GrCCGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f& p1,
|
||||
const Sk2f& p2, const Sk2f& p3, const float chops[], int numChops,
|
||||
float localT0, float localT1) {
|
||||
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;
|
||||
@ -576,8 +572,8 @@ void GrCCGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f
|
||||
this->appendCubics(mode, p0, p1, p2, p3);
|
||||
}
|
||||
|
||||
void GrCCGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f& p1,
|
||||
const Sk2f& p2, const Sk2f& p3, int maxSubdivisions) {
|
||||
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));
|
||||
@ -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;
|
||||
}
|
||||
|
||||
inline void GrCCGeometry::chopAndAppendCubicAtMidTangent(AppendCubicMode mode, const Sk2f& p0,
|
||||
const Sk2f& p1, const Sk2f& p2,
|
||||
const Sk2f& p3, const Sk2f& tan0,
|
||||
const Sk2f& tan1,
|
||||
int maxFutureSubdivisions) {
|
||||
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);
|
||||
@ -694,7 +690,7 @@ inline void GrCCGeometry::chopAndAppendCubicAtMidTangent(AppendCubicMode mode, c
|
||||
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(P[0] == fPoints.back());
|
||||
Sk2f p0 = Sk2f::Load(P);
|
||||
@ -743,7 +739,8 @@ void GrCCGeometry::conicTo(const SkPoint P[3], float 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);
|
||||
|
||||
Sk2f base = p2 - p0;
|
||||
@ -784,7 +781,7 @@ void GrCCGeometry::appendMonotonicConic(const Sk2f& p0, const Sk2f& p1, const Sk
|
||||
++fCurrContourTallies.fConics;
|
||||
}
|
||||
|
||||
GrCCGeometry::PrimitiveTallies GrCCGeometry::endContour() {
|
||||
GrCCFillGeometry::PrimitiveTallies GrCCFillGeometry::endContour() {
|
||||
SkASSERT(fBuildingContour);
|
||||
SkASSERT(fVerbs.count() >= fCurrContourTallies.fTriangles);
|
||||
|
@ -5,8 +5,8 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef GrGrCCGeometry_DEFINED
|
||||
#define GrGrCCGeometry_DEFINED
|
||||
#ifndef GrGrCCFillGeometry_DEFINED
|
||||
#define GrGrCCFillGeometry_DEFINED
|
||||
|
||||
#include "SkGeometry.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
|
||||
* render. (See GrCCGeometry::Verb.)
|
||||
* fill. (See GrCCFillGeometry::Verb.)
|
||||
*
|
||||
* NOTE: This must be done in device space, since an affine transformation can change whether a
|
||||
* curve is monotonic.
|
||||
*/
|
||||
class GrCCGeometry {
|
||||
class GrCCFillGeometry {
|
||||
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
|
||||
// compact representation of what can later be expanded into GPU instance data.
|
||||
enum class Verb : uint8_t {
|
||||
@ -49,7 +49,7 @@ public:
|
||||
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.
|
||||
, fVerbs(numSkVerbs * 3)
|
||||
, fConicWeights(numConicWeights * 3/2) {}
|
||||
@ -64,17 +64,6 @@ public:
|
||||
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 beginContour(const SkPoint&);
|
||||
void lineTo(const SkPoint P[2]);
|
||||
@ -129,7 +118,7 @@ private:
|
||||
SkSTArray<32, float, true> fConicWeights;
|
||||
};
|
||||
|
||||
inline void GrCCGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b) {
|
||||
inline void GrCCFillGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b) {
|
||||
fTriangles += b.fTriangles;
|
||||
fWeightedTriangles += b.fWeightedTriangles;
|
||||
fQuadratics += b.fQuadratics;
|
||||
@ -137,8 +126,8 @@ inline void GrCCGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b
|
||||
fConics += b.fConics;
|
||||
}
|
||||
|
||||
GrCCGeometry::PrimitiveTallies
|
||||
inline GrCCGeometry::PrimitiveTallies::operator-(const PrimitiveTallies& b) const {
|
||||
GrCCFillGeometry::PrimitiveTallies
|
||||
inline GrCCFillGeometry::PrimitiveTallies::operator-(const PrimitiveTallies& b) const {
|
||||
return {fTriangles - b.fTriangles,
|
||||
fWeightedTriangles - b.fWeightedTriangles,
|
||||
fQuadratics - b.fQuadratics,
|
||||
@ -146,7 +135,7 @@ inline GrCCGeometry::PrimitiveTallies::operator-(const PrimitiveTallies& b) cons
|
||||
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 &&
|
||||
fQuadratics == b.fQuadratics && fCubics == b.fCubics && fConics == b.fConics;
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "GrCCPathParser.h"
|
||||
#include "GrCCFiller.h"
|
||||
|
||||
#include "GrCaps.h"
|
||||
#include "GrGpuCommandBuffer.h"
|
||||
@ -15,105 +15,36 @@
|
||||
#include "SkPath.h"
|
||||
#include "SkPathPriv.h"
|
||||
#include "SkPoint.h"
|
||||
#include "ccpr/GrCCGeometry.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
|
||||
using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
|
||||
|
||||
GrCCPathParser::GrCCPathParser(int numPaths, const PathStats& pathStats)
|
||||
// Overallocate by one point to accomodate for overflow with Sk4f. (See parsePath.)
|
||||
: fLocalDevPtsBuffer(pathStats.fMaxPointsPerPath + 1)
|
||||
, fGeometry(pathStats.fNumTotalSkPoints, pathStats.fNumTotalSkVerbs,
|
||||
GrCCFiller::GrCCFiller(int numPaths, const PathStats& pathStats)
|
||||
: fGeometry(pathStats.fNumTotalSkPoints, pathStats.fNumTotalSkVerbs,
|
||||
pathStats.fNumTotalConicWeights)
|
||||
, fPathsInfo(numPaths)
|
||||
, 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()};
|
||||
fCoverageCountBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count(),
|
||||
PrimitiveTallies()};
|
||||
fBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count(), PrimitiveTallies()};
|
||||
}
|
||||
|
||||
void GrCCPathParser::parsePath(const SkMatrix& m, const SkPath& path, SkRect* devBounds,
|
||||
SkRect* devBounds45) {
|
||||
const SkPoint* pts = SkPathPriv::PointData(path);
|
||||
int numPts = path.countPoints();
|
||||
SkASSERT(numPts + 1 <= fLocalDevPtsBuffer.count());
|
||||
void GrCCFiller::parseDeviceSpaceFill(const SkPath& path, const SkPoint* deviceSpacePts,
|
||||
GrScissorTest scissorTest, const SkIRect& clippedDevIBounds,
|
||||
const SkIVector& devToAtlasOffset) {
|
||||
SkASSERT(!fInstanceBuffer); // Can't call after prepareToDraw().
|
||||
SkASSERT(!path.isEmpty());
|
||||
|
||||
if (!numPts) {
|
||||
devBounds->setEmpty();
|
||||
devBounds45->setEmpty();
|
||||
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();
|
||||
int currPathPointsIdx = fGeometry.points().count();
|
||||
int currPathVerbsIdx = fGeometry.verbs().count();
|
||||
PrimitiveTallies currPathPrimitiveCounts = PrimitiveTallies();
|
||||
|
||||
fGeometry.beginPath();
|
||||
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const float* conicWeights = SkPathPriv::ConicWeightData(path);
|
||||
int ptsIdx = 0;
|
||||
int conicWeightsIdx = 0;
|
||||
@ -122,13 +53,17 @@ void GrCCPathParser::parsePath(const SkPath& path, const SkPoint* deviceSpacePts
|
||||
for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
|
||||
switch (verb) {
|
||||
case SkPath::kMove_Verb:
|
||||
this->endContourIfNeeded(insideContour);
|
||||
if (insideContour) {
|
||||
currPathPrimitiveCounts += fGeometry.endContour();
|
||||
}
|
||||
fGeometry.beginContour(deviceSpacePts[ptsIdx]);
|
||||
++ptsIdx;
|
||||
insideContour = true;
|
||||
continue;
|
||||
case SkPath::kClose_Verb:
|
||||
this->endContourIfNeeded(insideContour);
|
||||
if (insideContour) {
|
||||
currPathPrimitiveCounts += fGeometry.endContour();
|
||||
}
|
||||
insideContour = false;
|
||||
continue;
|
||||
case SkPath::kLine_Verb:
|
||||
@ -155,123 +90,124 @@ void GrCCPathParser::parsePath(const SkPath& path, const SkPoint* deviceSpacePts
|
||||
SkASSERT(ptsIdx == path.countPoints());
|
||||
SkASSERT(conicWeightsIdx == SkPathPriv::ConicWeightCnt(path));
|
||||
|
||||
this->endContourIfNeeded(insideContour);
|
||||
}
|
||||
|
||||
void GrCCPathParser::endContourIfNeeded(bool insideContour) {
|
||||
if (insideContour) {
|
||||
fCurrPathPrimitiveCounts += fGeometry.endContour();
|
||||
currPathPrimitiveCounts += fGeometry.endContour();
|
||||
}
|
||||
}
|
||||
|
||||
void GrCCPathParser::saveParsedPath(GrScissorTest scissorTest, const SkIRect& clippedDevIBounds,
|
||||
const SkIVector& devToAtlasOffset) {
|
||||
SkASSERT(fParsingPath);
|
||||
|
||||
fPathsInfo.emplace_back(scissorTest, devToAtlasOffset);
|
||||
fPathInfos.emplace_back(scissorTest, devToAtlasOffset);
|
||||
|
||||
// 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 fanningWork = (int64_t)clippedDevIBounds.height() * clippedDevIBounds.width();
|
||||
if (tessellationWork * (50*50) + (100*100) < fanningWork) { // Don't tessellate under 100x100.
|
||||
fCurrPathPrimitiveCounts.fTriangles =
|
||||
fCurrPathPrimitiveCounts.fWeightedTriangles = 0;
|
||||
|
||||
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);
|
||||
fPathInfos.back().tessellateFan(fGeometry, currPathVerbsIdx, currPathPointsIdx,
|
||||
clippedDevIBounds, &currPathPrimitiveCounts);
|
||||
}
|
||||
|
||||
fTotalPrimitiveCounts[(int)scissorTest] += fCurrPathPrimitiveCounts;
|
||||
fTotalPrimitiveCounts[(int)scissorTest] += currPathPrimitiveCounts;
|
||||
|
||||
if (GrScissorTest::kEnabled == scissorTest) {
|
||||
fScissorSubBatches.push_back() = {fTotalPrimitiveCounts[(int)GrScissorTest::kEnabled],
|
||||
clippedDevIBounds.makeOffset(devToAtlasOffset.fX,
|
||||
devToAtlasOffset.fY)};
|
||||
}
|
||||
|
||||
SkDEBUGCODE(fParsingPath = false);
|
||||
}
|
||||
|
||||
void GrCCPathParser::discardParsedPath() {
|
||||
SkASSERT(fParsingPath);
|
||||
fGeometry.resize_back(fCurrPathPointsIdx, fCurrPathVerbsIdx);
|
||||
SkDEBUGCODE(fParsingPath = false);
|
||||
void GrCCFiller::PathInfo::tessellateFan(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. 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(!fCoverageCountBatches.empty());
|
||||
SkASSERT(!fBatches.empty());
|
||||
|
||||
const auto& lastBatch = fCoverageCountBatches.back();
|
||||
const auto& lastBatch = fBatches.back();
|
||||
int maxMeshes = 1 + fScissorSubBatches.count() - lastBatch.fEndScissorSubBatchIdx;
|
||||
fMaxMeshesPerDraw = SkTMax(fMaxMeshesPerDraw, maxMeshes);
|
||||
|
||||
@ -282,12 +218,12 @@ GrCCPathParser::CoverageCountBatchID GrCCPathParser::closeCurrentBatch() {
|
||||
lastScissorSubBatch.fEndPrimitiveIndices;
|
||||
|
||||
// This will invalidate lastBatch.
|
||||
fCoverageCountBatches.push_back() = {
|
||||
fBatches.push_back() = {
|
||||
fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled],
|
||||
fScissorSubBatches.count(),
|
||||
batchTotalCounts
|
||||
};
|
||||
return fCoverageCountBatches.count() - 1;
|
||||
return fBatches.count() - 1;
|
||||
}
|
||||
|
||||
// Emits a contour's triangle fan.
|
||||
@ -334,7 +270,7 @@ static void emit_tessellated_fan(const GrTessellator::WindingVertex* vertices, i
|
||||
const Sk2f& devToAtlasOffset,
|
||||
TriPointInstance* triPointInstanceData,
|
||||
QuadPointInstance* quadPointInstanceData,
|
||||
GrCCGeometry::PrimitiveTallies* indices) {
|
||||
GrCCFillGeometry::PrimitiveTallies* indices) {
|
||||
for (int i = 0; i < numVertices; i += 3) {
|
||||
if (1 == abs(vertices[i].fWinding)) {
|
||||
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) {
|
||||
SkASSERT(!fParsingPath); // Call saveParsedPath() or discardParsedPath().
|
||||
SkASSERT(fCoverageCountBatches.back().fEndNonScissorIndices == // Call closeCurrentBatch().
|
||||
bool GrCCFiller::prepareToDraw(GrOnFlushResourceProvider* onFlushRP) {
|
||||
using Verb = GrCCFillGeometry::Verb;
|
||||
SkASSERT(!fInstanceBuffer);
|
||||
SkASSERT(fBatches.back().fEndNonScissorIndices == // Call closeCurrentBatch().
|
||||
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.
|
||||
//
|
||||
@ -393,6 +330,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
fInstanceBuffer = onFlushRP->makeBuffer(kVertex_GrBufferType,
|
||||
quadEndIdx * sizeof(QuadPointInstance));
|
||||
if (!fInstanceBuffer) {
|
||||
SkDebugf("WARNING: failed to allocate CCPR fill instance buffer.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -401,7 +339,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
reinterpret_cast<QuadPointInstance*>(triPointInstanceData);
|
||||
SkASSERT(quadPointInstanceData);
|
||||
|
||||
PathInfo* nextPathInfo = fPathsInfo.begin();
|
||||
PathInfo* nextPathInfo = fPathInfos.begin();
|
||||
Sk2f devToAtlasOffset;
|
||||
PrimitiveTallies instanceIndices[2] = {fBaseInstances[0], fBaseInstances[1]};
|
||||
PrimitiveTallies* currIndices = nullptr;
|
||||
@ -413,9 +351,9 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
int nextConicWeightIdx = 0;
|
||||
|
||||
// Expand the ccpr verbs into GPU instance buffers.
|
||||
for (GrCCGeometry::Verb verb : fGeometry.verbs()) {
|
||||
for (Verb verb : fGeometry.verbs()) {
|
||||
switch (verb) {
|
||||
case GrCCGeometry::Verb::kBeginPath:
|
||||
case Verb::kBeginPath:
|
||||
SkASSERT(currFan.empty());
|
||||
currIndices = &instanceIndices[(int)nextPathInfo->scissorTest()];
|
||||
devToAtlasOffset = Sk2f(static_cast<float>(nextPathInfo->devToAtlasOffset().fX),
|
||||
@ -429,7 +367,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
++nextPathInfo;
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kBeginContour:
|
||||
case Verb::kBeginContour:
|
||||
SkASSERT(currFan.empty());
|
||||
++ptsIdx;
|
||||
if (!currFanIsTessellated) {
|
||||
@ -437,7 +375,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
}
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kLineTo:
|
||||
case Verb::kLineTo:
|
||||
++ptsIdx;
|
||||
if (!currFanIsTessellated) {
|
||||
SkASSERT(!currFan.empty());
|
||||
@ -445,7 +383,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
}
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kMonotonicQuadraticTo:
|
||||
case Verb::kMonotonicQuadraticTo:
|
||||
triPointInstanceData[currIndices->fQuadratics++].set(&pts[ptsIdx],
|
||||
devToAtlasOffset);
|
||||
ptsIdx += 2;
|
||||
@ -455,7 +393,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
}
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kMonotonicCubicTo:
|
||||
case Verb::kMonotonicCubicTo:
|
||||
quadPointInstanceData[currIndices->fCubics++].set(&pts[ptsIdx], devToAtlasOffset[0],
|
||||
devToAtlasOffset[1]);
|
||||
ptsIdx += 3;
|
||||
@ -465,7 +403,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
}
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kMonotonicConicTo:
|
||||
case Verb::kMonotonicConicTo:
|
||||
quadPointInstanceData[currIndices->fConics++].setW(
|
||||
&pts[ptsIdx], devToAtlasOffset,
|
||||
fGeometry.getConicWeight(nextConicWeightIdx));
|
||||
@ -477,13 +415,13 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
}
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kEndClosedContour: // endPt == startPt.
|
||||
case Verb::kEndClosedContour: // endPt == startPt.
|
||||
if (!currFanIsTessellated) {
|
||||
SkASSERT(!currFan.empty());
|
||||
currFan.pop_back();
|
||||
}
|
||||
// fallthru.
|
||||
case GrCCGeometry::Verb::kEndOpenContour: // endPt != startPt.
|
||||
case Verb::kEndOpenContour: // endPt != startPt.
|
||||
SkASSERT(!currFanIsTessellated || currFan.empty());
|
||||
if (!currFanIsTessellated && currFan.count() >= 3) {
|
||||
int fanSize = currFan.count();
|
||||
@ -503,7 +441,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
|
||||
fInstanceBuffer->unmap();
|
||||
|
||||
SkASSERT(nextPathInfo == fPathsInfo.end());
|
||||
SkASSERT(nextPathInfo == fPathInfos.end());
|
||||
SkASSERT(ptsIdx == pts.count() - 1);
|
||||
SkASSERT(instanceIndices[0].fTriangles == fBaseInstances[1].fTriangles);
|
||||
SkASSERT(instanceIndices[1].fTriangles == fBaseInstances[0].fQuadratics);
|
||||
@ -522,13 +460,13 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCountBatchID batchID,
|
||||
const SkIRect& drawBounds) const {
|
||||
void GrCCFiller::drawFills(GrOpFlushState* flushState, BatchID batchID,
|
||||
const SkIRect& drawBounds) const {
|
||||
using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
|
||||
|
||||
SkASSERT(fInstanceBuffer);
|
||||
|
||||
const PrimitiveTallies& batchTotalCounts = fCoverageCountBatches[batchID].fTotalPrimitiveCounts;
|
||||
const PrimitiveTallies& batchTotalCounts = fBatches[batchID].fTotalPrimitiveCounts;
|
||||
|
||||
GrPipeline pipeline(flushState->drawOpArgs().fProxy, GrScissorTest::kEnabled,
|
||||
SkBlendMode::kPlus);
|
||||
@ -559,11 +497,10 @@ void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCount
|
||||
}
|
||||
}
|
||||
|
||||
void GrCCPathParser::drawPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
|
||||
CoverageCountBatchID batchID,
|
||||
GrCCCoverageProcessor::PrimitiveType primitiveType,
|
||||
int PrimitiveTallies::*instanceType,
|
||||
const SkIRect& drawBounds) const {
|
||||
void GrCCFiller::drawPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
|
||||
BatchID batchID, GrCCCoverageProcessor::PrimitiveType primitiveType,
|
||||
int PrimitiveTallies::*instanceType,
|
||||
const SkIRect& drawBounds) const {
|
||||
SkASSERT(pipeline.isScissorEnabled());
|
||||
|
||||
// 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);
|
||||
|
||||
SkASSERT(batchID > 0);
|
||||
SkASSERT(batchID < fCoverageCountBatches.count());
|
||||
const CoverageCountBatch& previousBatch = fCoverageCountBatches[batchID - 1];
|
||||
const CoverageCountBatch& batch = fCoverageCountBatches[batchID];
|
||||
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 -
|
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 "SkNx.h"
|
||||
#include "ccpr/GrCCPathParser.h"
|
||||
|
||||
// The maximum number of cache entries we allow in our own cache.
|
||||
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 "ccpr/GrCCPathCache.h"
|
||||
|
||||
using CoverageCountBatchID = GrCCPathParser::CoverageCountBatchID;
|
||||
using FillBatchID = GrCCFiller::BatchID;
|
||||
using PathInstance = GrCCPathProcessor::Instance;
|
||||
|
||||
namespace {
|
||||
@ -101,7 +101,7 @@ public:
|
||||
|
||||
static std::unique_ptr<GrDrawOp> Make(GrContext* context,
|
||||
sk_sp<const GrCCPerFlushResources> resources,
|
||||
CoverageCountBatchID batchID, const SkISize& drawBounds) {
|
||||
FillBatchID batchID, const SkISize& drawBounds) {
|
||||
GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
|
||||
|
||||
return pool->allocate<RenderAtlasOp>(std::move(resources), batchID, drawBounds);
|
||||
@ -111,20 +111,20 @@ public:
|
||||
const char* name() const override { return "RenderAtlasOp (CCPR)"; }
|
||||
|
||||
void onExecute(GrOpFlushState* flushState) override {
|
||||
fResources->pathParser().drawCoverageCount(flushState, fBatchID, fDrawBounds);
|
||||
fResources->filler().drawFills(flushState, fBatchID, fDrawBounds);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ::GrOpMemoryPool; // for ctor
|
||||
|
||||
RenderAtlasOp(sk_sp<const GrCCPerFlushResources> resources, CoverageCountBatchID batchID,
|
||||
RenderAtlasOp(sk_sp<const GrCCPerFlushResources> resources, FillBatchID batchID,
|
||||
const SkISize& drawBounds)
|
||||
: AtlasOp(ClassID(), std::move(resources), drawBounds)
|
||||
, fBatchID(batchID)
|
||||
, fDrawBounds(SkIRect::MakeWH(drawBounds.width(), drawBounds.height())) {
|
||||
}
|
||||
|
||||
const CoverageCountBatchID fBatchID;
|
||||
const FillBatchID fBatchID;
|
||||
const SkIRect fDrawBounds;
|
||||
};
|
||||
|
||||
@ -138,7 +138,11 @@ static int inst_buffer_count(const GrCCPerFlushResourceSpecs& specs) {
|
||||
|
||||
GrCCPerFlushResources::GrCCPerFlushResources(GrOnFlushResourceProvider* onFlushRP,
|
||||
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())
|
||||
, fRenderedAtlasStack(kAlpha_half_GrPixelConfig, specs.fRenderedAtlasSpecs,
|
||||
onFlushRP->caps())
|
||||
@ -183,6 +187,56 @@ GrCCAtlas* GrCCPerFlushResources::copyPathToCachedAtlas(const GrCCPathCacheEntry
|
||||
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 SkMatrix& m, const SkPath& path,
|
||||
SkRect* devBounds, SkRect* devBounds45,
|
||||
@ -191,13 +245,24 @@ const GrCCAtlas* GrCCPerFlushResources::renderPathInAtlas(const SkIRect& clipIBo
|
||||
SkASSERT(this->isMapped());
|
||||
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);
|
||||
|
||||
if (!this->placeParsedPathInAtlas(clipIBounds, *devIBounds, devToAtlasOffset)) {
|
||||
GrScissorTest scissorTest;
|
||||
SkIRect clippedPathIBounds;
|
||||
if (!this->placeRenderedPathInAtlas(clipIBounds, *devIBounds, &scissorTest, &clippedPathIBounds,
|
||||
devToAtlasOffset)) {
|
||||
SkDEBUGCODE(--fEndPathInstance);
|
||||
return nullptr; // Path was degenerate or clipped away.
|
||||
}
|
||||
|
||||
fFiller.parseDeviceSpaceFill(path, fLocalDevPtsBuffer.begin(), scissorTest, clippedPathIBounds,
|
||||
*devToAtlasOffset);
|
||||
return &fRenderedAtlasStack.current();
|
||||
}
|
||||
|
||||
@ -205,37 +270,45 @@ const GrCCAtlas* GrCCPerFlushResources::renderDeviceSpacePathInAtlas(
|
||||
const SkIRect& clipIBounds, const SkPath& devPath, const SkIRect& devPathIBounds,
|
||||
SkIVector* devToAtlasOffset) {
|
||||
SkASSERT(this->isMapped());
|
||||
fPathParser.parseDeviceSpacePath(devPath);
|
||||
if (!this->placeParsedPathInAtlas(clipIBounds, devPathIBounds, devToAtlasOffset)) {
|
||||
|
||||
if (devPath.isEmpty()) {
|
||||
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();
|
||||
}
|
||||
|
||||
bool GrCCPerFlushResources::placeParsedPathInAtlas(const SkIRect& clipIBounds,
|
||||
const SkIRect& pathIBounds,
|
||||
SkIVector* devToAtlasOffset) {
|
||||
GrScissorTest scissorTest;
|
||||
SkIRect clippedPathIBounds;
|
||||
bool GrCCPerFlushResources::placeRenderedPathInAtlas(const SkIRect& clipIBounds,
|
||||
const SkIRect& pathIBounds,
|
||||
GrScissorTest* scissorTest,
|
||||
SkIRect* clippedPathIBounds,
|
||||
SkIVector* devToAtlasOffset) {
|
||||
if (clipIBounds.contains(pathIBounds)) {
|
||||
clippedPathIBounds = pathIBounds;
|
||||
scissorTest = GrScissorTest::kDisabled;
|
||||
} else if (clippedPathIBounds.intersect(clipIBounds, pathIBounds)) {
|
||||
scissorTest = GrScissorTest::kEnabled;
|
||||
*clippedPathIBounds = pathIBounds;
|
||||
*scissorTest = GrScissorTest::kDisabled;
|
||||
} else if (clippedPathIBounds->intersect(clipIBounds, pathIBounds)) {
|
||||
*scissorTest = GrScissorTest::kEnabled;
|
||||
} else {
|
||||
fPathParser.discardParsedPath();
|
||||
return false;
|
||||
}
|
||||
|
||||
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
|
||||
// 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().
|
||||
CoverageCountBatchID batchID = fPathParser.closeCurrentBatch();
|
||||
FillBatchID batchID = fFiller.closeCurrentBatch();
|
||||
retiredAtlas->setUserBatchID(batchID);
|
||||
}
|
||||
fPathParser.saveParsedPath(scissorTest, clippedPathIBounds, *devToAtlasOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -253,14 +326,13 @@ bool GrCCPerFlushResources::finalize(GrOnFlushResourceProvider* onFlushRP,
|
||||
fCopyAtlasStack.current().setUserBatchID(fNextCopyInstanceIdx);
|
||||
}
|
||||
if (!fRenderedAtlasStack.empty()) {
|
||||
CoverageCountBatchID batchID = fPathParser.closeCurrentBatch();
|
||||
FillBatchID batchID = fFiller.closeCurrentBatch();
|
||||
fRenderedAtlasStack.current().setUserBatchID(batchID);
|
||||
}
|
||||
|
||||
// Build the GPU buffers to render path coverage counts. (This must not happen until after the
|
||||
// final call to fPathParser.closeCurrentBatch().)
|
||||
if (!fPathParser.finalize(onFlushRP)) {
|
||||
SkDebugf("WARNING: failed to allocate GPU buffers for CCPR. No paths will be drawn.\n");
|
||||
if (!fFiller.prepareToDraw(onFlushRP)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -320,5 +392,5 @@ void GrCCPerFlushResourceSpecs::convertCopiesToRenders() {
|
||||
fRenderedPathStats.fNumTotalSkPoints += fCopyPathStats.fNumTotalSkPoints;
|
||||
fRenderedPathStats.fNumTotalSkVerbs += fCopyPathStats.fNumTotalSkVerbs;
|
||||
fRenderedPathStats.fNumTotalConicWeights += fCopyPathStats.fNumTotalConicWeights;
|
||||
fCopyPathStats = GrCCPathParser::PathStats();
|
||||
fCopyPathStats = GrCCFiller::PathStats();
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
#include "GrNonAtomicRef.h"
|
||||
#include "ccpr/GrCCAtlas.h"
|
||||
#include "ccpr/GrCCPathParser.h"
|
||||
#include "ccpr/GrCCFiller.h"
|
||||
#include "ccpr/GrCCPathProcessor.h"
|
||||
|
||||
class GrCCPathCacheEntry;
|
||||
@ -24,12 +24,12 @@ struct GrCCPerFlushResourceSpecs {
|
||||
int fNumCachedPaths = 0;
|
||||
|
||||
int fNumCopiedPaths = 0;
|
||||
GrCCPathParser::PathStats fCopyPathStats;
|
||||
GrCCFiller::PathStats fCopyPathStats;
|
||||
GrCCAtlas::Specs fCopyAtlasSpecs;
|
||||
|
||||
int fNumRenderedPaths = 0;
|
||||
int fNumClipPaths = 0;
|
||||
GrCCPathParser::PathStats fRenderedPathStats;
|
||||
GrCCFiller::PathStats fRenderedPathStats;
|
||||
GrCCAtlas::Specs fRenderedAtlasSpecs;
|
||||
|
||||
bool isEmpty() const {
|
||||
@ -85,7 +85,7 @@ public:
|
||||
SkTArray<sk_sp<GrRenderTargetContext>>* out);
|
||||
|
||||
// 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* vertexBuffer() const { SkASSERT(!this->isMapped()); return fVertexBuffer.get();}
|
||||
GrBuffer* instanceBuffer() const { SkASSERT(!this->isMapped()); return fInstanceBuffer.get(); }
|
||||
@ -107,10 +107,12 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool placeParsedPathInAtlas(const SkIRect& clipIBounds, const SkIRect& pathIBounds,
|
||||
SkIVector* devToAtlasOffset);
|
||||
bool placeRenderedPathInAtlas(const SkIRect& clipIBounds, const SkIRect& pathIBounds,
|
||||
GrScissorTest*, SkIRect* clippedPathIBounds,
|
||||
SkIVector* devToAtlasOffset);
|
||||
|
||||
GrCCPathParser fPathParser;
|
||||
const SkAutoSTArray<32, SkPoint> fLocalDevPtsBuffer;
|
||||
GrCCFiller fFiller;
|
||||
GrCCAtlasStack fCopyAtlasStack;
|
||||
GrCCAtlasStack fRenderedAtlasStack;
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "ccpr/GrCCClipProcessor.h"
|
||||
#include "ccpr/GrCCDrawPathsOp.h"
|
||||
#include "ccpr/GrCCPathCache.h"
|
||||
#include "ccpr/GrCCPathParser.h"
|
||||
|
||||
using PathInstance = GrCCPathProcessor::Instance;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user