Reenable GrOp chaining.
GrRenderTargetOpList maintains an array of op chains. When it receives a
new op it tries to add it to an existing chain, working backwards from
the end of the current array. If the op can be added to a chain it
additionally tries to merge the new op with ops already in the chain
before adding it to the tail of the chain.
In forward combining it tries to concatenate chains. If chains can
concatenate it also attempts to merge ops between the two chains.
Now op chaining results reported by Op subclasses must be transitive.
Moreover, if op A is able to merge with B then it must be the case that
any op that can chain with A will either merge or chain with any op that
can chain to B.
Bug: skia:8491
Change-Id: Ib6a2a669acd4257134a37d271289b8b3f247cd3f
Reviewed-on: https://skia-review.googlesource.com/c/170351
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/src/gpu/ops/GrOp.cpp b/src/gpu/ops/GrOp.cpp
index 2d591bd..3a36d73 100644
--- a/src/gpu/ops/GrOp.cpp
+++ b/src/gpu/ops/GrOp.cpp
@@ -31,36 +31,50 @@
SkDEBUGCODE(fBoundsFlags = kUninitialized_BoundsFlag);
}
-GrOp::~GrOp() {}
-
GrOp::CombineResult GrOp::combineIfPossible(GrOp* that, const GrCaps& caps) {
+ SkASSERT(this != that);
if (this->classID() != that->classID()) {
return CombineResult::kCannotCombine;
}
- SkDEBUGCODE(bool thatWasChained = that->isChained());
auto result = this->onCombineIfPossible(that, caps);
- // Merging a chained 'that' would cause problems given the way op lists currently manage chains.
- SkASSERT(!(thatWasChained && result == CombineResult::kMerged));
if (result == CombineResult::kMerged) {
this->joinBounds(*that);
- if (fChainHead) {
- fChainHead->joinBounds(*that);
- }
}
return result;
}
-void GrOp::setNextInChain(GrOp* next) {
+void GrOp::chainConcat(std::unique_ptr<GrOp> next) {
SkASSERT(next);
SkASSERT(this->classID() == next->classID());
- // Each op begins life as a 1 element list. We assume lists are appended only with
SkASSERT(this->isChainTail());
- SkASSERT(!next->isChained());
- if (!fChainHead) {
- // We were using null to mark 'this' as unchained. Now 'this' is the head of the new chain.
- fChainHead = this;
- }
- fNextInChain = next;
- fChainHead->joinBounds(*next);
- next->fChainHead = this->fChainHead;
+ SkASSERT(next->isChainHead());
+ fNextInChain = std::move(next);
+ fNextInChain->fPrevInChain = this;
}
+
+std::unique_ptr<GrOp> GrOp::cutChain() {
+ if (fNextInChain) {
+ fNextInChain->fPrevInChain = nullptr;
+ return std::move(fNextInChain);
+ }
+ return nullptr;
+}
+
+#ifdef SK_DEBUG
+void GrOp::validateChain(GrOp* expectedTail) const {
+ SkASSERT(this->isChainHead());
+ uint32_t classID = this->classID();
+ const GrOp* op = this;
+ while (op) {
+ SkASSERT(op == this || (op->prevInChain() && op->prevInChain()->nextInChain() == op));
+ SkASSERT(classID == op->classID());
+ if (op->nextInChain()) {
+ SkASSERT(op->nextInChain()->prevInChain() == op);
+ SkASSERT(op != expectedTail);
+ } else {
+ SkASSERT(!expectedTail || op == expectedTail);
+ }
+ op = op->nextInChain();
+ }
+}
+#endif