Revert changes which were breaking the build.
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 18 Feb 2014 18:13:34 +0000 (18:13 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 18 Feb 2014 18:13:34 +0000 (18:13 +0000)
Revert "Improve saveLayer handling in SkMatrixClipStateMgr"

This reverts commit f7d08ed626a4825317405c3708cf2896509209d6.

Revert "Compile fix for r13488 (Improve saveLayer handling in SkMatrixClipStateMgr)"

This reverts commit a48822f3ebe86056afc76c96da73c950c80c686a.

R=robertphillips@google.com
TBR=robertphillips@google.com
NOTREECHECKS=true
NOTRY=True

Author: scroggo@google.com

Review URL: https://codereview.chromium.org/170973002

git-svn-id: http://skia.googlecode.com/svn/trunk@13490 2bbb7eff-a529-9590-31e7-b0007b416f81

src/core/SkMatrixClipStateMgr.cpp
src/core/SkMatrixClipStateMgr.h
src/core/SkPictureRecord.cpp
tests/MatrixClipCollapseTest.cpp

index cffad71..4cf99bc 100644 (file)
@@ -21,6 +21,7 @@ bool SkMatrixClipStateMgr::MatrixClipState::ClipInfo::clipPath(SkPictureRecord*
     newClip->fOp = op;
     newClip->fDoAA = doAA;
     newClip->fMatrixID = matrixID;
+    newClip->fOffset = kInvalidJumpOffset;
     return false;
 }
 
@@ -35,6 +36,7 @@ bool SkMatrixClipStateMgr::MatrixClipState::ClipInfo::clipRegion(SkPictureRecord
     newClip->fOp = op;
     newClip->fDoAA = true;      // not necessary but sanity preserving
     newClip->fMatrixID = matrixID;
+    newClip->fOffset = kInvalidJumpOffset;
     return false;
 }
 
@@ -53,10 +55,17 @@ void SkMatrixClipStateMgr::writeDeltaMat(int currentMatID, int desiredMatID) {
 // Note: this only writes out the clips for the current save state. To get the
 // entire clip stack requires iterating of the entire matrix/clip stack.
 void SkMatrixClipStateMgr::MatrixClipState::ClipInfo::writeClip(int* curMatID,
-                                                                SkMatrixClipStateMgr* mgr) {
+                                                                SkMatrixClipStateMgr* mgr,
+                                                                bool* overrideFirstOp) {
     for (int i = 0; i < fClips.count(); ++i) {
         ClipOp& curClip = fClips[i];
 
+        SkRegion::Op op = curClip.fOp;
+        if (*overrideFirstOp) {
+            op = SkRegion::kReplace_Op;
+            *overrideFirstOp = false;
+        }
+
         // TODO: use the matrix ID to skip writing the identity matrix
         // over and over, i.e.:
         //  if (*curMatID != curClip.fMatrixID) {
@@ -70,31 +79,43 @@ void SkMatrixClipStateMgr::MatrixClipState::ClipInfo::writeClip(int* curMatID,
         mgr->writeDeltaMat(*curMatID, curClip.fMatrixID);
         *curMatID = curClip.fMatrixID;
 
-        int offset;
-
         switch (curClip.fClipType) {
         case kRect_ClipType:
-            offset = mgr->getPicRecord()->recordClipRect(curClip.fGeom.fRRect.rect(),
-                                                         curClip.fOp, curClip.fDoAA);
+            curClip.fOffset = mgr->getPicRecord()->recordClipRect(curClip.fGeom.fRRect.rect(),
+                                                                  op, curClip.fDoAA);
             break;
         case kRRect_ClipType:
-            offset = mgr->getPicRecord()->recordClipRRect(curClip.fGeom.fRRect, curClip.fOp,
-                                                         curClip.fDoAA);
+            curClip.fOffset = mgr->getPicRecord()->recordClipRRect(curClip.fGeom.fRRect, op,
+                                                                   curClip.fDoAA);
             break;
         case kPath_ClipType:
-            offset = mgr->getPicRecord()->recordClipPath(curClip.fGeom.fPathID, curClip.fOp,
-                                                         curClip.fDoAA);
+            curClip.fOffset = mgr->getPicRecord()->recordClipPath(curClip.fGeom.fPathID, op,
+                                                                  curClip.fDoAA);
             break;
         case kRegion_ClipType: {
             const SkRegion* region = mgr->lookupRegion(curClip.fGeom.fRegionID);
-            offset = mgr->getPicRecord()->recordClipRegion(*region, curClip.fOp);
+            curClip.fOffset = mgr->getPicRecord()->recordClipRegion(*region, op);
             break;
         }
         default:
             SkASSERT(0);
         }
+    }
+}
 
-        mgr->addClipOffset(offset);
+// Fill in the skip offsets for all the clips written in the current block
+void SkMatrixClipStateMgr::MatrixClipState::ClipInfo::fillInSkips(SkWriter32* writer,
+                                                                  int32_t restoreOffset) {
+    for (int i = 0; i < fClips.count(); ++i) {
+        ClipOp& curClip = fClips[i];
+
+        if (-1 == curClip.fOffset) {
+            continue;
+        }
+//        SkDEBUGCODE(uint32_t peek = writer->read32At(curClip.fOffset);)
+//        SkASSERT(-1 == peek);
+        writer->overwriteTAt(curClip.fOffset, restoreOffset);
+        SkDEBUGCODE(curClip.fOffset = -1;)
     }
 }
 
@@ -105,8 +126,6 @@ SkMatrixClipStateMgr::SkMatrixClipStateMgr()
                        sizeof(fMatrixClipStackStorage))
     , fCurOpenStateID(kIdentityWideOpenStateID) {
 
-    fSkipOffsets = SkNEW(SkTDArray<int>);
-
     // The first slot in the matrix dictionary is reserved for the identity matrix
     fMatrixDict.append()->reset();
 
@@ -118,12 +137,12 @@ SkMatrixClipStateMgr::~SkMatrixClipStateMgr() {
     for (int i = 0; i < fRegionDict.count(); ++i) {
         SkDELETE(fRegionDict[i]);
     }
-
-    SkDELETE(fSkipOffsets);
 }
 
 
-int SkMatrixClipStateMgr::MCStackPush(SkCanvas::SaveFlags flags) {
+int SkMatrixClipStateMgr::save(SkCanvas::SaveFlags flags) {
+    SkDEBUGCODE(this->validate();)
+
     MatrixClipState* newTop = (MatrixClipState*)fMatrixClipStack.push_back();
     new (newTop) MatrixClipState(fCurMCState, flags); // balanced in restore()
     fCurMCState = newTop;
@@ -133,29 +152,14 @@ int SkMatrixClipStateMgr::MCStackPush(SkCanvas::SaveFlags flags) {
     return fMatrixClipStack.count();
 }
 
-int SkMatrixClipStateMgr::save(SkCanvas::SaveFlags flags) {
-    SkDEBUGCODE(this->validate();)
-
-    return this->MCStackPush(flags);
-}
-
 int SkMatrixClipStateMgr::saveLayer(const SkRect* bounds, const SkPaint* paint,
                                     SkCanvas::SaveFlags flags) {
-    // Since the saveLayer call draws something we need to potentially dump
-    // out the MC state
-    this->call(kOther_CallType);
-
-    int result = this->MCStackPush(flags);
+    int result = this->save(flags);
     ++fCurMCState->fLayerID;
     fCurMCState->fIsSaveLayer = true;
 
+    fCurMCState->fSaveLayerBracketed = this->call(kOther_CallType);
     fCurMCState->fSaveLayerBaseStateID = fCurOpenStateID;
-    fCurMCState->fSavedSkipOffsets = fSkipOffsets;
-
-    // TODO: recycle these rather then new & deleting them on every saveLayer/
-    // restore
-    fSkipOffsets = SkNEW(SkTDArray<int>);
-
     fPicRecord->recordSaveLayer(bounds, paint,
                                 (SkCanvas::SaveFlags)(flags| SkCanvas::kMatrixClip_SaveFlag));
     return result;
@@ -166,20 +170,21 @@ void SkMatrixClipStateMgr::restore() {
 
     if (fCurMCState->fIsSaveLayer) {
         if (fCurMCState->fSaveLayerBaseStateID != fCurOpenStateID) {
-            fPicRecord->recordRestore(); // Close the open block inside the saveLayer
+            fPicRecord->recordRestore(); // Close the open block
         }
         // The saveLayer's don't carry any matrix or clip state in the
         // new scheme so make sure the saveLayer's recordRestore doesn't
         // try to finalize them (i.e., fill in their skip offsets).
         fPicRecord->recordRestore(false); // close of saveLayer
 
-        fCurOpenStateID = fCurMCState->fSaveLayerBaseStateID;
-
-        SkASSERT(0 == fSkipOffsets->count());
-        SkASSERT(NULL != fCurMCState->fSavedSkipOffsets);
+        // Close the Save that brackets the saveLayer. TODO: this doesn't handle
+        // the skip offsets correctly
+        if (fCurMCState->fSaveLayerBracketed) {
+            fPicRecord->recordRestore(false);
+        }
 
-        SkDELETE(fSkipOffsets);
-        fSkipOffsets = fCurMCState->fSavedSkipOffsets;
+        // MC states can be allowed to fuse across saveLayer/restore boundaries
+        fCurOpenStateID = kIdentityWideOpenStateID;
     }
 
     fCurMCState->~MatrixClipState();       // balanced in save()
@@ -215,11 +220,7 @@ bool SkMatrixClipStateMgr::call(CallType callType) {
         return false;
     }
 
-    if (kIdentityWideOpenStateID != fCurOpenStateID && 
-                  (!fCurMCState->fIsSaveLayer || 
-                   fCurMCState->fSaveLayerBaseStateID != fCurOpenStateID)) {
-        // Don't write a restore if the open state is one in which a saveLayer
-        // is nested. The save after the saveLayer's restore will close it.
+    if (kIdentityWideOpenStateID != fCurOpenStateID) {
         fPicRecord->recordRestore();    // Close the open block
     }
 
@@ -229,40 +230,17 @@ bool SkMatrixClipStateMgr::call(CallType callType) {
     fPicRecord->recordSave(SkCanvas::kMatrixClip_SaveFlag);
 
     // write out clips
-    SkDeque::Iter iter(fMatrixClipStack, SkDeque::Iter::kBack_IterStart);
-    const MatrixClipState* state;
-    // Loop back across the MC states until the last saveLayer. The MC
-    // state in front of the saveLayer has already been written out.
-    for (state = (const MatrixClipState*) iter.prev();
-         state != NULL;
-         state = (const MatrixClipState*) iter.prev()) {
-        if (state->fIsSaveLayer) {
-            break;
-        }
-    }
+    SkDeque::F2BIter iter(fMatrixClipStack);
+    bool firstClip = true;
 
-    if (NULL == state) {
-        // There was no saveLayer in the MC stack so we need to output them all
-        iter.reset(fMatrixClipStack, SkDeque::Iter::kFront_IterStart);
-        state = (const MatrixClipState*) iter.next();
-    } else {
-        // SkDeque's iterators actually return the previous location so we
-        // need to reverse and go forward one to get back on track.
-        iter.next(); 
-        SkDEBUGCODE(const MatrixClipState* test =) (const MatrixClipState*) iter.next(); 
-        SkASSERT(test == state);
-    }
-
-    int curMatID = NULL != state ? state->fMatrixInfo->getID(this) : kIdentityMatID;
-    for ( ; state != NULL; state = (const MatrixClipState*) iter.next()) {
-         state->fClipInfo->writeClip(&curMatID, this);
+    int curMatID = kIdentityMatID;
+    for (const MatrixClipState* state = (const MatrixClipState*) iter.next();
+         state != NULL;
+         state = (const MatrixClipState*) iter.next()) {
+         state->fClipInfo->writeClip(&curMatID, this, &firstClip);
     }
 
     // write out matrix
-    // TODO: this test isn't quite right. It should be:
-    //   if (curMatID != fCurMCState->fMatrixInfo->getID(this)) {
-    // but right now the testing harness always expects a matrix if
-    // the matrices are non-I
     if (kIdentityMatID != fCurMCState->fMatrixInfo->getID(this)) {
         // TODO: writing out the delta matrix here is an artifact of the writing
         // out of the entire clip stack (with its matrices). Ultimately we will
@@ -275,17 +253,6 @@ bool SkMatrixClipStateMgr::call(CallType callType) {
     return true;
 }
 
-// Fill in the skip offsets for all the clips written in the current block
-void SkMatrixClipStateMgr::fillInSkips(SkWriter32* writer, int32_t restoreOffset) {
-    for (int i = 0; i < fSkipOffsets->count(); ++i) {
-        SkDEBUGCODE(uint32_t peek = writer->readTAt<int32_t>((*fSkipOffsets)[i]);)
-//        SkASSERT(-1 == peek);
-        writer->overwriteTAt<int32_t>((*fSkipOffsets)[i], restoreOffset);
-    }
-
-    fSkipOffsets->rewind();
-}
-
 void SkMatrixClipStateMgr::finish() {
     if (kIdentityWideOpenStateID != fCurOpenStateID) {
         fPicRecord->recordRestore();    // Close the open block
@@ -295,23 +262,16 @@ void SkMatrixClipStateMgr::finish() {
 
 #ifdef SK_DEBUG
 void SkMatrixClipStateMgr::validate() {
-    if (fCurOpenStateID == fCurMCState->fMCStateID && 
-            (!fCurMCState->fIsSaveLayer || 
-             fCurOpenStateID != fCurMCState->fSaveLayerBaseStateID)) {
-        // The current state is the active one so it should have a skip
-        // offset for each clip
-        SkDeque::Iter iter(fMatrixClipStack, SkDeque::Iter::kBack_IterStart);
-        int clipCount = 0;
-        for (const MatrixClipState* state = (const MatrixClipState*) iter.prev();
+    if (fCurOpenStateID == fCurMCState->fMCStateID) {
+        // The current state is the active one so all its skip offsets should
+        // still be -1
+        SkDeque::F2BIter iter(fMatrixClipStack);
+
+        for (const MatrixClipState* state = (const MatrixClipState*) iter.next();
              state != NULL;
-             state = (const MatrixClipState*) iter.prev()) {
-             clipCount += state->fClipInfo->numClips();
-             if (state->fIsSaveLayer) {
-                 break;
-             }
+             state = (const MatrixClipState*) iter.next()) {
+            state->fClipInfo->checkOffsetNotEqual(-1);
         }
-
-        SkASSERT(fSkipOffsets->count() == clipCount);
     }
 }
 #endif
index ef0a99d..60f9fa0 100644 (file)
@@ -120,6 +120,7 @@ public:
                 newClip->fOp = op;
                 newClip->fDoAA = doAA;
                 newClip->fMatrixID = matrixID;
+                newClip->fOffset = kInvalidJumpOffset;
                 return false;
             }
 
@@ -133,6 +134,7 @@ public:
                 newClip->fOp = op;
                 newClip->fDoAA = doAA;
                 newClip->fMatrixID = matrixID;
+                newClip->fOffset = kInvalidJumpOffset;
                 return false;
             }
 
@@ -145,10 +147,19 @@ public:
                             int regionID,
                             SkRegion::Op op,
                             int matrixID);
-            void writeClip(int* curMatID, SkMatrixClipStateMgr* mgr);
-
-            SkDEBUGCODE(int numClips() const { return fClips.count(); })
+            void writeClip(int* curMatID,
+                           SkMatrixClipStateMgr* mgr,
+                           bool* overrideFirstOp);
+            void fillInSkips(SkWriter32* writer, int32_t restoreOffset);
 
+#ifdef SK_DEBUG
+            void checkOffsetNotEqual(int32_t offset) {
+                for (int i = 0; i < fClips.count(); ++i) {
+                    ClipOp& curClip = fClips[i];
+                    SkASSERT(offset != curClip.fOffset);
+                }
+            }
+#endif
         private:
             enum ClipType {
                 kRect_ClipType,
@@ -157,6 +168,8 @@ public:
                 kRegion_ClipType
             };
 
+            static const int kInvalidJumpOffset = -1;
+
             class ClipOp {
             public:
                 ClipType     fClipType;
@@ -172,6 +185,10 @@ public:
 
                 // The CTM in effect when this clip call was issued
                 int          fMatrixID;
+
+                // The offset of this clipOp's "jump-to-offset" location in the skp.
+                // -1 means the offset hasn't been written.
+                int32_t      fOffset;
             };
 
             SkTDArray<ClipOp> fClips;
@@ -232,7 +249,7 @@ public:
 
         // The next two fields are only valid when fIsSaveLayer is set.
         int32_t      fSaveLayerBaseStateID;
-        SkTDArray<int>* fSavedSkipOffsets;
+        bool         fSaveLayerBracketed;
 
 #ifdef SK_DEBUG
         MatrixClipState* fPrev; // debugging aid
@@ -330,7 +347,17 @@ public:
 
     bool call(CallType callType);
 
-    void fillInSkips(SkWriter32* writer, int32_t restoreOffset);
+    void fillInSkips(SkWriter32* writer, int32_t restoreOffset) {
+        // Since we write out the entire clip stack at each block start we
+        // need to update the skips for the entire stack each time too.
+        SkDeque::F2BIter iter(fMatrixClipStack);
+
+        for (const MatrixClipState* state = (const MatrixClipState*) iter.next();
+             state != NULL;
+             state = (const MatrixClipState*) iter.next()) {
+            state->fClipInfo->fillInSkips(writer, restoreOffset);
+        }
+    }
 
     void finish();
 
@@ -352,23 +379,9 @@ protected:
 
     // The MCStateID of the state currently in effect in the byte stream. 0 if none.
     int32_t          fCurOpenStateID;
-    // The skip offsets for the current open state. These are the locations in the 
-    // skp that must be filled in when the current open state is closed. These are
-    // here rather then distributed across the MatrixClipState's because saveLayers
-    // can cause MC states to be nested.
-    SkTDArray<int32_t>  *fSkipOffsets;
 
     SkDEBUGCODE(void validate();)
 
-    int MCStackPush(SkCanvas::SaveFlags flags);
-
-    void addClipOffset(int offset) {
-        SkASSERT(NULL != fSkipOffsets);
-        SkASSERT(kIdentityWideOpenStateID != fCurOpenStateID);
-
-        *fSkipOffsets->append() = offset;
-    }
-
     void writeDeltaMat(int currentMatID, int desiredMatID);
     static int32_t   NewMCStateID();
 
index c14328b..5890ac4 100644 (file)
@@ -598,6 +598,7 @@ void SkPictureRecord::restore() {
         return;
     }
 
+    // TODO: don't write the restore to the op stream for normal saves
     fMCMgr.restore();
 #else
     // check for underflow
index 6981fe2..d379e33 100644 (file)
@@ -129,14 +129,10 @@ enum DrawOpType {
     kDrawVertices_DrawOpType,
 #endif
 
-    kLastNonSaveLayer_DrawOpType = kRect_DrawOpType,
-
-    // saveLayer's have to handled apart from the other draw operations
-    // since they also alter the save/restore structure.
-    kSaveLayer_DrawOpType,
+    kLast_DrawOpType = kRect_DrawOpType
 };
 
-static const int kNonSaveLayerDrawOpTypeCount = kLastNonSaveLayer_DrawOpType + 1;
+static const int kDrawOpTypeCount = kLast_DrawOpType + 1;
 
 typedef void (*PFEmitMC)(SkCanvas* canvas, MatType mat, ClipType clip,
                          DrawOpType draw, SkTDArray<DrawType>* expected,
@@ -325,13 +321,13 @@ static void emit_draw(SkCanvas* canvas, DrawOpType draw, SkTDArray<DrawType>* ex
 static void emit_clip_and_mat(SkCanvas* canvas, MatType mat, ClipType clip,
                               DrawOpType draw, SkTDArray<DrawType>* expected,
                               int accumulatedClips) {
-    emit_clip(canvas, clip);
-    emit_mat(canvas, mat);
-
     if (kNone_DrawOpType == draw) {
         return;
     }
 
+    emit_clip(canvas, clip);
+    emit_mat(canvas, mat);
+
     for (int i = 0; i < accumulatedClips; ++i) {
         add_clip(clip, mat, expected);
     }
@@ -346,13 +342,13 @@ static void emit_clip_and_mat(SkCanvas* canvas, MatType mat, ClipType clip,
 static void emit_mat_and_clip(SkCanvas* canvas, MatType mat, ClipType clip,
                               DrawOpType draw, SkTDArray<DrawType>* expected,
                               int accumulatedClips) {
-    emit_mat(canvas, mat);
-    emit_clip(canvas, clip);
-
     if (kNone_DrawOpType == draw) {
         return;
     }
 
+    emit_mat(canvas, mat);
+    emit_clip(canvas, clip);
+
     // the matrix & clip order will be reversed once collapsed!
     for (int i = 0; i < accumulatedClips; ++i) {
         add_clip(clip, mat, expected);
@@ -369,15 +365,15 @@ static void emit_mat_and_clip(SkCanvas* canvas, MatType mat, ClipType clip,
 static void emit_double_mat_and_clip(SkCanvas* canvas, MatType mat, ClipType clip,
                                      DrawOpType draw, SkTDArray<DrawType>* expected,
                                      int accumulatedClips) {
+    if (kNone_DrawOpType == draw) {
+        return;
+    }
+
     emit_mat(canvas, mat);
     emit_clip(canvas, clip);
     emit_mat(canvas, mat);
     emit_clip(canvas, clip);
 
-    if (kNone_DrawOpType == draw) {
-        return;
-    }
-
     for (int i = 0; i < accumulatedClips; ++i) {
         add_clip(clip, mat, expected);
         add_clip(clip, mat, expected);
@@ -394,14 +390,14 @@ static void emit_double_mat_and_clip(SkCanvas* canvas, MatType mat, ClipType cli
 static void emit_mat_clip_clip(SkCanvas* canvas, MatType mat, ClipType clip,
                                DrawOpType draw, SkTDArray<DrawType>* expected,
                                int accumulatedClips) {
-    emit_mat(canvas, mat);
-    emit_clip(canvas, clip);
-    emit_clip(canvas, clip);
-
     if (kNone_DrawOpType == draw) {
         return;
     }
 
+    emit_mat(canvas, mat);
+    emit_clip(canvas, clip);
+    emit_clip(canvas, clip);
+
     for (int i = 0; i < accumulatedClips; ++i) {
         add_clip(clip, mat, expected);
         add_clip(clip, mat, expected);
@@ -469,24 +465,22 @@ static void emit_body2(SkCanvas* canvas, PFEmitMC emitMC, MatType mat,
     bool needsSaveRestore = kNone_DrawOpType != draw &&
                             (kNone_MatType != mat || kNone_ClipType != clip);
 
-    if (kNone_MatType != mat || kNone_ClipType != clip) {
-        *expected->append() = SAVE;
+    if (needsSaveRestore) {
+        *expected->append() = SAVE_LAYER;
     }
-    (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, accumulatedClips+1);
-    *expected->append() = SAVE_LAYER;
+    (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops
     // TODO: widen testing to exercise saveLayer's parameters
     canvas->saveLayer(NULL, NULL);
         if (needsSaveRestore) {
             *expected->append() = SAVE;
         }
-        (*emitMC)(canvas, mat, clip, draw, expected, 1);
+        (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+2);
         emit_draw(canvas, draw, expected);
         if (needsSaveRestore) {
             *expected->append() = RESTORE;
         }
     canvas->restore();
-    *expected->append() = RESTORE;
-    if (kNone_MatType != mat || kNone_ClipType != clip) {
+    if (needsSaveRestore) {
         *expected->append() = RESTORE;
     }
 }
@@ -507,39 +501,35 @@ static void emit_body3(SkCanvas* canvas, PFEmitMC emitMC, MatType mat,
     bool needsSaveRestore = kNone_DrawOpType != draw &&
                             (kNone_MatType != mat || kNone_ClipType != clip);
 
-    if (kNone_MatType != mat || kNone_ClipType != clip) {
-        *expected->append() = SAVE;
-    }
-    (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, accumulatedClips+1); 
+    // This saveLayer will always be forced b.c. we currently can't tell
+    // ahead of time if it will be empty (see comment in SkMatrixClipStateMgr::save)
     *expected->append() = SAVE_LAYER;
+
+    (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops
     // TODO: widen testing to exercise saveLayer's parameters
     canvas->saveLayer(NULL, NULL);
-        (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, 1);
-        if (kNone_MatType != mat || kNone_ClipType != clip) {
-            *expected->append() = SAVE;
+        (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops
+        if (needsSaveRestore) {
+            *expected->append() = SAVE_LAYER;
         }
-        *expected->append() = SAVE_LAYER;
         // TODO: widen testing to exercise saveLayer's parameters
         canvas->saveLayer(NULL, NULL);
             if (needsSaveRestore) {
                 *expected->append() = SAVE;
             }
-            (*emitMC)(canvas, mat, clip, draw, expected, 1);
+            (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+3);
             emit_draw(canvas, draw, expected);
             if (needsSaveRestore) {
                 *expected->append() = RESTORE;
             }
-        canvas->restore();             // for saveLayer
-        *expected->append() = RESTORE; // for saveLayer
-        if (kNone_MatType != mat || kNone_ClipType != clip) {
+        canvas->restore();
+        if (needsSaveRestore) {
             *expected->append() = RESTORE;
         }
     canvas->restore();
+
     // required to match forced SAVE_LAYER
     *expected->append() = RESTORE;
-    if (kNone_MatType != mat || kNone_ClipType != clip) {
-        *expected->append() = RESTORE;
-    }
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -670,14 +660,13 @@ static void test_collapse(skiatest::Reporter* reporter) {
             for (size_t k = 0; k < SK_ARRAY_COUNT(gMCs); ++k) {
                 for (int l = 0; l < kMatTypeCount; ++l) {
                     for (int m = 0; m < kClipTypeCount; ++m) {
-                        for (int n = 0; n < kNonSaveLayerDrawOpTypeCount; ++n) {
+                        for (int n = 0; n < kDrawOpTypeCount; ++n) {
 #ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE
                             static int testID = -1;
                             ++testID;
                             if (testID < -1) {
                                 continue;
                             }
-                            SkDebugf("test: %d\n", testID);
 #endif
 
                             SkTDArray<DrawType> expected, actual;