Batch better in GrSmallPathRenderer and add perspective support.

Minor optimization to text regen also snuck in.

Bug: skia:
Change-Id: I0c5d97defdcf60b9ce663548c056db0e8bf7149b
Reviewed-on: https://skia-review.googlesource.com/57942
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Jim Van Verth 2017-10-12 10:18:44 -04:00 committed by Skia Commit-Bot
parent 7f9b215695
commit 5698c8a8a0
5 changed files with 144 additions and 99 deletions

View File

@ -320,7 +320,7 @@ sk_sp<GrGeometryProcessor> GrDistanceFieldA8TextGeoProc::TestCreate(GrProcessorT
class GrGLDistanceFieldPathGeoProc : public GrGLSLGeometryProcessor {
public:
GrGLDistanceFieldPathGeoProc()
: fViewMatrix(SkMatrix::InvalidMatrix())
: fMatrix(SkMatrix::InvalidMatrix())
, fAtlasSize({0,0}) {
}
@ -352,21 +352,35 @@ public:
// setup pass through color
varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor);
// Setup position
this->writeOutputPosition(vertBuilder,
uniformHandler,
gpArgs,
dfTexEffect.inPosition()->fName,
dfTexEffect.viewMatrix(),
&fViewMatrixUniform);
if (dfTexEffect.matrix().hasPerspective()) {
// Setup position
this->writeOutputPosition(vertBuilder,
uniformHandler,
gpArgs,
dfTexEffect.inPosition()->fName,
dfTexEffect.matrix(),
&fMatrixUniform);
// emit transforms
this->emitTransforms(vertBuilder,
varyingHandler,
uniformHandler,
gpArgs->fPositionVar,
dfTexEffect.inPosition()->fName,
args.fFPCoordTransformHandler);
// emit transforms
this->emitTransforms(vertBuilder,
varyingHandler,
uniformHandler,
gpArgs->fPositionVar,
dfTexEffect.inPosition()->fName,
args.fFPCoordTransformHandler);
} else {
// Setup position
this->writeOutputPosition(vertBuilder, gpArgs, dfTexEffect.inPosition()->fName);
// emit transforms
this->emitTransforms(vertBuilder,
varyingHandler,
uniformHandler,
gpArgs->fPositionVar,
dfTexEffect.inPosition()->fName,
dfTexEffect.matrix(),
args.fFPCoordTransformHandler);
}
// Use highp to work around aliasing issues
fragBuilder->codeAppendf("float2 uv = %s;", uv.fsIn());
@ -450,11 +464,11 @@ public:
const GrDistanceFieldPathGeoProc& dfpgp = proc.cast<GrDistanceFieldPathGeoProc>();
if (!dfpgp.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dfpgp.viewMatrix())) {
fViewMatrix = dfpgp.viewMatrix();
float viewMatrix[3 * 3];
GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
if (dfpgp.matrix().hasPerspective() && !fMatrix.cheapEqualTo(dfpgp.matrix())) {
fMatrix = dfpgp.matrix();
float matrix[3 * 3];
GrGLSLGetMatrix<3>(matrix, fMatrix);
pdman.setMatrix3f(fMatrixUniform, matrix);
}
SkASSERT(dfpgp.numTextureSamplers() >= 1);
@ -466,7 +480,11 @@ public:
pdman.set2f(fAtlasSizeInvUniform, 1.0f / atlas->width(), 1.0f / atlas->height());
}
this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
if (dfpgp.matrix().hasPerspective()) {
this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
} else {
this->setTransformDataHelper(dfpgp.matrix(), pdman, &transformIter);
}
}
static inline void GenKey(const GrGeometryProcessor& gp,
@ -475,14 +493,15 @@ public:
const GrDistanceFieldPathGeoProc& dfTexEffect = gp.cast<GrDistanceFieldPathGeoProc>();
uint32_t key = dfTexEffect.getFlags();
key |= ComputePosKey(dfTexEffect.viewMatrix()) << 16;
key |= ComputePosKey(dfTexEffect.matrix()) << 16;
b->add32(key);
b->add32(dfTexEffect.matrix().hasPerspective());
b->add32(dfTexEffect.numTextureSamplers());
}
private:
SkMatrix fViewMatrix;
UniformHandle fViewMatrixUniform;
SkMatrix fMatrix; // view matrix if perspective, local matrix otherwise
UniformHandle fMatrixUniform;
SkISize fAtlasSize;
UniformHandle fAtlasSizeInvUniform;
@ -493,17 +512,15 @@ private:
///////////////////////////////////////////////////////////////////////////////
GrDistanceFieldPathGeoProc::GrDistanceFieldPathGeoProc(
GrColor color,
const SkMatrix& viewMatrix,
const SkMatrix& matrix,
const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const GrSamplerState& params,
uint32_t flags,
bool usesLocalCoords)
uint32_t flags)
: INHERITED(kGrDistanceFieldPathGeoProc_ClassID)
, fColor(color)
, fViewMatrix(viewMatrix)
, fMatrix(matrix)
, fFlags(flags & kNonLCD_DistanceFieldEffectMask)
, fInColor(nullptr)
, fUsesLocalCoords(usesLocalCoords) {
, fInColor(nullptr) {
SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
@ -567,8 +584,7 @@ sk_sp<GrGeometryProcessor> GrDistanceFieldPathGeoProc::TestCreate(GrProcessorTes
GrTest::TestMatrix(d->fRandom),
proxies,
samplerState,
flags,
d->fRandom->nextBool());
flags);
}
#endif

View File

@ -129,13 +129,11 @@ class GrDistanceFieldPathGeoProc : public GrGeometryProcessor {
public:
static constexpr int kMaxTextures = 4;
static sk_sp<GrGeometryProcessor> Make(GrColor color, const SkMatrix& viewMatrix,
static sk_sp<GrGeometryProcessor> Make(GrColor color, const SkMatrix& matrix,
const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const GrSamplerState& params, uint32_t flags,
bool usesLocalCoords) {
const GrSamplerState& params, uint32_t flags) {
return sk_sp<GrGeometryProcessor>(
new GrDistanceFieldPathGeoProc(color, viewMatrix, proxies,
params, flags, usesLocalCoords));
new GrDistanceFieldPathGeoProc(color, matrix, proxies, params, flags));
}
~GrDistanceFieldPathGeoProc() override {}
@ -146,9 +144,8 @@ public:
const Attribute* inColor() const { return fInColor; }
const Attribute* inTextureCoords() const { return fInTextureCoords; }
GrColor color() const { return fColor; }
const SkMatrix& viewMatrix() const { return fViewMatrix; }
const SkMatrix& matrix() const { return fMatrix; }
uint32_t getFlags() const { return fFlags; }
bool usesLocalCoords() const { return fUsesLocalCoords; }
void addNewProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures], const GrSamplerState& p);
@ -157,18 +154,17 @@ public:
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
private:
GrDistanceFieldPathGeoProc(GrColor, const SkMatrix& viewMatrix,
GrDistanceFieldPathGeoProc(GrColor, const SkMatrix& matrix,
const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const GrSamplerState&, uint32_t flags, bool usesLocalCoords);
const GrSamplerState&, uint32_t flags);
GrColor fColor;
SkMatrix fViewMatrix;
SkMatrix fMatrix; // view matrix if perspective, local matrix otherwise
TextureSampler fTextureSamplers[kMaxTextures];
uint32_t fFlags;
const Attribute* fInPosition;
const Attribute* fInColor;
const Attribute* fInTextureCoords;
bool fUsesLocalCoords;
GR_DECLARE_GEOMETRY_PROCESSOR_TEST

View File

@ -102,16 +102,12 @@ GrPathRenderer::CanDrawPath GrSmallPathRenderer::onCanDrawPath(const CanDrawPath
if (args.fShape->inverseFilled()) {
return CanDrawPath::kNo;
}
// currently don't support perspective
if (args.fViewMatrix->hasPerspective()) {
return CanDrawPath::kNo;
}
// Only support paths with bounds within kMaxDim by kMaxDim,
// scaled to have bounds within kMaxSize by kMaxSize.
// The goal is to accelerate rendering of lots of small paths that may be scaling.
SkScalar scaleFactors[2];
if (!args.fViewMatrix->getMinMaxScales(scaleFactors)) {
SkScalar scaleFactors[2] = { 1, 1 };
if (!args.fViewMatrix->hasPerspective() && !args.fViewMatrix->getMinMaxScales(scaleFactors)) {
return CanDrawPath::kNo;
}
SkRect bounds = args.fShape->styledBounds();
@ -167,21 +163,10 @@ public:
// only use distance fields on desktop and Android framework to save space in the atlas
fUsesDistanceField = this->bounds().width() > kMaxMIP || this->bounds().height() > kMaxMIP;
#endif
fViewMatrix = viewMatrix;
SkVector translate = SkVector::Make(0, 0);
if (!fUsesDistanceField) {
// In this case we don't apply a view matrix, so we need to remove the non-subpixel
// translation and add it back when we generate the quad for the path
SkScalar translateX = viewMatrix.getTranslateX();
SkScalar translateY = viewMatrix.getTranslateY();
translate = SkVector::Make(SkScalarFloorToScalar(translateX),
SkScalarFloorToScalar(translateY));
// Only store the fractional part of the translation in the view matrix
fViewMatrix.setTranslateX(translateX - translate.fX);
fViewMatrix.setTranslateY(translateY - translate.fY);
}
// always use distance fields if in perspective
fUsesDistanceField = fUsesDistanceField || viewMatrix.hasPerspective();
fShapes.emplace_back(Entry{color, shape, translate});
fShapes.emplace_back(Entry{color, shape, viewMatrix});
fAtlas = atlas;
fShapeCache = shapeCache;
@ -234,7 +219,6 @@ private:
void onPrepareDraws(Target* target) override {
int instanceCount = fShapes.count();
const SkMatrix& ctm = this->viewMatrix();
FlushInfo flushInfo;
flushInfo.fPipeline = fHelper.makePipeline(target);
@ -244,25 +228,37 @@ private:
if (!atlasPageCount) {
return;
}
const SkMatrix& ctm = fShapes[0].fViewMatrix;
if (fUsesDistanceField) {
uint32_t flags = 0;
// Still need to key off of ctm to pick the right shader for the transformed quad
flags |= ctm.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
flags |= fGammaCorrect ? kGammaCorrect_DistanceFieldEffectFlag : 0;
flushInfo.fGeometryProcessor = GrDistanceFieldPathGeoProc::Make(
this->color(), this->viewMatrix(), atlas->getProxies(),
GrSamplerState::ClampBilerp(), flags, fHelper.usesLocalCoords());
} else {
const SkMatrix* matrix;
SkMatrix invert;
if (fHelper.usesLocalCoords()) {
if (!this->viewMatrix().invert(&invert)) {
if (ctm.hasPerspective()) {
matrix = &ctm;
} else if (fHelper.usesLocalCoords()) {
if (!ctm.invert(&invert)) {
SkDebugf("Could not invert viewmatrix\n");
return;
}
matrix = &invert;
} else {
matrix = &SkMatrix::I();
}
flushInfo.fGeometryProcessor = GrDistanceFieldPathGeoProc::Make(
this->color(), *matrix, atlas->getProxies(),
GrSamplerState::ClampBilerp(), flags);
} else {
SkMatrix invert;
if (fHelper.usesLocalCoords()) {
if (!ctm.invert(&invert)) {
SkDebugf("Could not invert viewmatrix\n");
return;
}
// for local coords, we need to add the translation back in that we removed
// from the stored view matrix
invert.preTranslate(-fShapes[0].fTranslate.fX, -fShapes[0].fTranslate.fY);
}
flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
@ -295,8 +291,17 @@ private:
ShapeData* shapeData;
if (fUsesDistanceField) {
// get mip level
SkScalar maxScale = SkScalarAbs(this->viewMatrix().getMaxScale());
SkScalar maxScale;
const SkRect& bounds = args.fShape.bounds();
if (args.fViewMatrix.hasPerspective()) {
// approximate the scale since we can't get it from the matrix
SkRect xformedBounds;
args.fViewMatrix.mapRect(&xformedBounds, bounds);
maxScale = SkTMax(xformedBounds.width() / bounds.width(),
xformedBounds.height() / bounds.height());
} else {
maxScale = SkScalarAbs(args.fViewMatrix.getMaxScale());
}
SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
// We try to create the DF at a 2^n scaled path resolution (1/2, 1, 2, 4, etc.)
// In the majority of cases this will yield a crisper rendering.
@ -355,7 +360,7 @@ private:
}
} else {
// check to see if bitmap path is cached
ShapeData::Key key(args.fShape, this->viewMatrix());
ShapeData::Key key(args.fShape, args.fViewMatrix);
shapeData = fShapeCache->find(key);
if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
// Remove the stale cache entry
@ -371,7 +376,7 @@ private:
atlas,
shapeData,
args.fShape,
this->viewMatrix())) {
args.fViewMatrix)) {
delete shapeData;
continue;
}
@ -385,7 +390,7 @@ private:
offset,
args.fColor,
vertexStride,
args.fTranslate,
args.fViewMatrix,
shapeData);
offset += kVerticesPerQuad * vertexStride;
flushInfo.fInstancesToFlush++;
@ -622,19 +627,41 @@ private:
intptr_t offset,
GrColor color,
size_t vertexStride,
const SkVector& preTranslate,
const SkMatrix& ctm,
const ShapeData* shapeData) const {
SkPoint* positions = reinterpret_cast<SkPoint*>(offset);
SkRect bounds = shapeData->fBounds;
SkRect translatedBounds(bounds);
if (!fUsesDistanceField) {
translatedBounds.offset(SkScalarTruncToScalar(ctm.get(SkMatrix::kMTransX)),
SkScalarTruncToScalar(ctm.get(SkMatrix::kMTransY)));
}
// vertex positions
// TODO make the vertex attributes a struct
positions->setRectFan(bounds.left() + preTranslate.fX,
bounds.top() + preTranslate.fY,
bounds.right() + preTranslate.fX,
bounds.bottom() + preTranslate.fY,
vertexStride);
if (fUsesDistanceField && !ctm.hasPerspective()) {
SkPoint quad[4];
ctm.mapRectToQuad(quad, translatedBounds);
intptr_t positionOffset = offset;
SkPoint* position = (SkPoint*)positionOffset;
*position = quad[0];
positionOffset += vertexStride;
position = (SkPoint*)positionOffset;
*position = quad[3];
positionOffset += vertexStride;
position = (SkPoint*)positionOffset;
*position = quad[2];
positionOffset += vertexStride;
position = (SkPoint*)positionOffset;
*position = quad[1];
} else {
positions->setRectFan(translatedBounds.left(),
translatedBounds.top(),
translatedBounds.right(),
translatedBounds.bottom(),
vertexStride);
}
// colors
for (int i = 0; i < kVerticesPerQuad; i++) {
@ -696,7 +723,6 @@ private:
}
GrColor color() const { return fShapes[0].fColor; }
const SkMatrix& viewMatrix() const { return fViewMatrix; }
bool usesDistanceField() const { return fUsesDistanceField; }
bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@ -709,28 +735,39 @@ private:
return false;
}
// TODO We can position on the cpu for distance field paths
if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
const SkMatrix& thisCtm = this->fShapes[0].fViewMatrix;
const SkMatrix& thatCtm = that->fShapes[0].fViewMatrix;
if (thisCtm.hasPerspective() != thatCtm.hasPerspective()) {
return false;
}
if (!this->usesDistanceField() && fHelper.usesLocalCoords() &&
!this->fShapes[0].fTranslate.equalsWithinTolerance(that->fShapes[0].fTranslate)) {
// We can position on the cpu unless we're in perspective,
// but also need to make sure local matrices are identical
if ((thisCtm.hasPerspective() || fHelper.usesLocalCoords()) &&
!thisCtm.cheapEqualTo(thatCtm)) {
return false;
}
// Depending on the ctm we may have a different shader for SDF paths
if (this->usesDistanceField()) {
if (thisCtm.isScaleTranslate() != thatCtm.isScaleTranslate() ||
thisCtm.isSimilarity() != thatCtm.isSimilarity()) {
return false;
}
}
fShapes.push_back_n(that->fShapes.count(), that->fShapes.begin());
this->joinBounds(*that);
return true;
}
SkMatrix fViewMatrix;
bool fUsesDistanceField;
struct Entry {
GrColor fColor;
GrShape fShape;
SkVector fTranslate;
SkMatrix fViewMatrix;
};
SkSTArray<1, Entry> fShapes;

View File

@ -65,6 +65,7 @@ private:
return *this;
}
// for SDF paths
void set(const GrShape& shape, uint32_t dim) {
// Shapes' keys are for their pre-style geometry, but by now we shouldn't have any
// relevant styling information.
@ -76,13 +77,8 @@ private:
shape.writeUnstyledKey(&fKey[1]);
}
// for bitmap paths
void set(const GrShape& shape, const SkMatrix& ctm) {
GrUniqueKey maskKey;
struct KeyData {
SkScalar fFractionalTranslateX;
SkScalar fFractionalTranslateY;
};
// Shapes' keys are for their pre-style geometry, but by now we shouldn't have any
// relevant styling information.
SkASSERT(shape.style().isSimpleFill());

View File

@ -166,6 +166,8 @@ void GrAtlasTextBlob::regenInOp(GrDrawOp::Target* target, GrAtlasGlyphCache* fon
}
bool brokenRun = false;
intptr_t vertex = reinterpret_cast<intptr_t>(fVertices);
vertex += info->vertexStartIndex();
for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
GrGlyph* glyph = nullptr;
if (regenTexCoords) {
@ -196,12 +198,10 @@ void GrAtlasTextBlob::regenInOp(GrDrawOp::Target* target, GrAtlasGlyphCache* fon
target->nextDrawToken());
}
intptr_t vertex = reinterpret_cast<intptr_t>(fVertices);
vertex += info->vertexStartIndex();
vertex += vertexStride * glyphIdx * GrAtlasTextOp::kVerticesPerGlyph;
regen_vertices<regenPos, regenCol, regenTexCoords>(vertex, glyph, vertexStride,
info->drawAsDistanceFields(), transX,
transY, color);
vertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
helper->incGlyphCount();
}