Sever fOriginalPath connection whenever a GrShape becomes a simple type

When drawing a round-rect, for example, we may end up in drawPath with a
temporary path that was created with the rrect added. We ended up putting
a genID listener on that (stack allocated) path, so we would evict cache
entries immediately. This restores the old behavior, where cache entries
for paths derived from simple types are never invalidated.

Bug: skia:7087
Change-Id: I3eed9c3a289241bfe1e42036be3362f592256693
Reviewed-on: https://skia-review.googlesource.com/54460
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
Brian Osman 2017-10-04 15:44:05 -04:00 committed by Skia Commit-Bot
parent 5fcd3913da
commit b379dcd64e
3 changed files with 60 additions and 6 deletions

View File

@ -386,7 +386,6 @@ GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
scale)) {
tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr));
*this = tmpParent.get()->applyStyle(apply, scale);
fOriginalPath = parent.fOriginalPath;
return;
}
// A path effect has access to change the res scale but we aren't expecting it to and it
@ -498,6 +497,10 @@ void GrShape::attemptToSimplifyPath() {
}
if (Type::kPath != fType) {
fInheritedKey.reset(0);
// Whenever we simplify to a non-path, break the chain so we no longer refer to the
// original path. This prevents attaching genID listeners to temporary paths created when
// drawing simple shapes.
fOriginalPath.reset();
if (Type::kRRect == fType) {
this->attemptToSimplifyRRect();
} else if (Type::kLine == fType) {

View File

@ -373,9 +373,11 @@ public:
void addGenIDChangeListener(SkPathRef::GenIDChangeListener* listener) const;
/**
* Gets the generation ID of the *original* path. This is only exposed for unit tests.
* Helpers that are only exposed for unit tests, to determine if the shape is a path, and get
* the generation ID of the *original* path.
*/
uint32_t testingOnly_getOriginalGenerationID() const;
bool testingOnly_isPath() const;
private:

View File

@ -21,6 +21,10 @@ uint32_t GrShape::testingOnly_getOriginalGenerationID() const {
return fOriginalPath.getGenerationID();
}
bool GrShape::testingOnly_isPath() const {
return Type::kPath == fType;
}
using Key = SkTArray<uint32_t>;
static bool make_key(Key* key, const GrShape& shape) {
@ -213,6 +217,54 @@ static void check_equivalence(skiatest::Reporter* r, const GrShape& a, const GrS
REPORTER_ASSERT(r, ignoreInversenessDifference || a.inverseFilled() == b.inverseFilled());
}
static void check_original_path_ids(skiatest::Reporter* r, const GrShape& base, const GrShape& pe,
const GrShape& peStroke, const GrShape& full) {
bool baseIsPath = base.testingOnly_isPath();
bool peIsPath = pe.testingOnly_isPath();
bool peStrokeIsPath = peStroke.testingOnly_isPath();
bool fullIsPath = full.testingOnly_isPath();
REPORTER_ASSERT(r, peStrokeIsPath == fullIsPath);
uint32_t baseID = base.testingOnly_getOriginalGenerationID();
uint32_t peID = pe.testingOnly_getOriginalGenerationID();
uint32_t peStrokeID = peStroke.testingOnly_getOriginalGenerationID();
uint32_t fullID = full.testingOnly_getOriginalGenerationID();
// All empty paths have the same gen ID
uint32_t emptyID = SkPath().getGenerationID();
// If we started with a real path, then our genID should match that path's gen ID (and not be
// empty). If we started with a simple shape, our original path should have been reset.
REPORTER_ASSERT(r, baseIsPath == (baseID != emptyID));
// For the derived shapes, if they're simple types, their original paths should have been reset
REPORTER_ASSERT(r, peIsPath || (peID == emptyID));
REPORTER_ASSERT(r, peStrokeIsPath || (peStrokeID == emptyID));
REPORTER_ASSERT(r, fullIsPath || (fullID == emptyID));
if (!peIsPath) {
// If the path effect produces a simple shape, then there are no unbroken chains to test
return;
}
// From here on, we know that the path effect produced a shape that was a "real" path
if (baseIsPath) {
REPORTER_ASSERT(r, baseID == peID);
}
if (peStrokeIsPath) {
REPORTER_ASSERT(r, peID == peStrokeID);
REPORTER_ASSERT(r, peStrokeID == fullID);
}
if (baseIsPath && peStrokeIsPath) {
REPORTER_ASSERT(r, baseID == peStrokeID);
REPORTER_ASSERT(r, baseID == fullID);
}
}
void test_inversions(skiatest::Reporter* r, const GrShape& shape, const Key& shapeKey) {
GrShape preserve = GrShape::MakeFilled(shape, GrShape::FillInversion::kPreserve);
Key preserveKey;
@ -499,10 +551,7 @@ private:
// All shapes should report the same "original" path, so that path renderers can get to it
// if necessary.
uint32_t baseGenID = fBase.testingOnly_getOriginalGenerationID();
REPORTER_ASSERT(r, baseGenID == fAppliedPE.testingOnly_getOriginalGenerationID());
REPORTER_ASSERT(r, baseGenID == fAppliedPEThenStroke.testingOnly_getOriginalGenerationID());
REPORTER_ASSERT(r, baseGenID == fAppliedFull.testingOnly_getOriginalGenerationID());
check_original_path_ids(r, fBase, fAppliedPE, fAppliedPEThenStroke, fAppliedFull);
// Applying the path effect and then the stroke should always be the same as applying
// both in one go.