[skottie] Fix motion blur asserts

MotionBlurEffect makes use of many abilities some consider to be
unnatural.  Notably, it mutates the state of its subtree at render time
(gasp) to sample various time points.

Mutation triggers scene graph invalidation, which bubbles up the
ancestor chain.  While we immediately revalidate the subtree, we
cannot do the same for ancestors (no full scene knowledge).  This means
post-rendering, we leave some SG nodes dirty - which triggers various
debug asserts).

The easiest fix is to temporarily suppress invalidation bubbling at the
MotionBlurEffect node level (this is safe, because we always revalidate
the subtree).

Also add a post-render assert for tighter state validation.

Change-Id: I376b7a8880f71d85e595c419334b42bc4720ac65
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/243420
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
This commit is contained in:
Florin Malita 2019-09-23 12:09:02 -04:00 committed by Skia Commit-Bot
parent ee11283a0c
commit 0f11e115d7
3 changed files with 24 additions and 0 deletions

View File

@ -17,6 +17,23 @@
namespace skottie { namespace skottie {
namespace internal { namespace internal {
class MotionBlurEffect::AutoInvalBlocker {
public:
AutoInvalBlocker(const MotionBlurEffect* mb, const sk_sp<RenderNode>& child)
: fMBNode(const_cast<MotionBlurEffect*>(mb))
, fChild(child) {
fMBNode->unobserveInval(fChild);
}
~AutoInvalBlocker() {
fMBNode->observeInval(fChild);
}
private:
MotionBlurEffect* fMBNode;
const sk_sp<RenderNode>& fChild;
};
sk_sp<MotionBlurEffect> MotionBlurEffect::Make(sk_sp<sksg::Animator> animator, sk_sp<MotionBlurEffect> MotionBlurEffect::Make(sk_sp<sksg::Animator> animator,
sk_sp<sksg::RenderNode> child, sk_sp<sksg::RenderNode> child,
size_t samples_per_frame, size_t samples_per_frame,
@ -188,6 +205,10 @@ void MotionBlurEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) cons
SkASSERT(this->children().size() == 1ul); SkASSERT(this->children().size() == 1ul);
const auto& child = this->children()[0]; const auto& child = this->children()[0];
// We're about to mutate/revalidate the subtree for sampling. Capture the invalidation
// at this scope, to prevent dirtying ancestor SG nodes (no way to revalidate the global scene).
AutoInvalBlocker aib(this, child);
SkPixmap pm; SkPixmap pm;
if (canvas->peekPixels(&pm) && (canvas->imageInfo().colorType() == kRGBA_8888_SkColorType || if (canvas->peekPixels(&pm) && (canvas->imageInfo().colorType() == kRGBA_8888_SkColorType ||
canvas->imageInfo().colorType() == kBGRA_8888_SkColorType ) canvas->imageInfo().colorType() == kBGRA_8888_SkColorType )

View File

@ -24,6 +24,8 @@ public:
SG_ATTRIBUTE(T, float, fT) SG_ATTRIBUTE(T, float, fT)
private: private:
class AutoInvalBlocker;
const RenderNode* onNodeAt(const SkPoint&) const override; const RenderNode* onNodeAt(const SkPoint&) const override;
SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override; SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override;

View File

@ -43,6 +43,7 @@ void RenderNode::render(SkCanvas* canvas, const RenderContext* ctx) const {
if (this->isVisible() && !this->bounds().isEmpty()) { if (this->isVisible() && !this->bounds().isEmpty()) {
this->onRender(canvas, ctx); this->onRender(canvas, ctx);
} }
SkASSERT(!this->hasInval());
} }
const RenderNode* RenderNode::nodeAt(const SkPoint& p) const { const RenderNode* RenderNode::nodeAt(const SkPoint& p) const {