Add always-threaded SkRecord quilt tests.

Now that we're drawing tiles threaded like implside painting, remove the checks
that those lock counts are balanced.  They're just not right for anyone anymore.

SkBitmaps themselves are not threadsafe (even const ones), so shallow copy them
on playback of an SkRecord.  (The underlying SkPixelRefs are threadsafe.)

Simplify quilt drawing by using SkBitmap::extractSubset.  No need for locking.

Bump up to 256x256 tiles.  16x16 tiles just murders performance (way too much
contention).  This has the nice side effect of letting us enable a bunch more
GMs for quilt mode; they drew wrong with small tiles but exactly right with large.

BUG=171776
R=reed@google.com, mtklein@google.com

Author: mtklein@chromium.org

Review URL: https://codereview.chromium.org/371023005
diff --git a/dm/DMCpuGMTask.cpp b/dm/DMCpuGMTask.cpp
index d45da51..2252a1d 100644
--- a/dm/DMCpuGMTask.cpp
+++ b/dm/DMCpuGMTask.cpp
@@ -39,7 +39,8 @@
     SPAWN(PipeTask, fGMFactory(NULL), bitmap, PipeTask::kCrossProcess_Mode);
     SPAWN(PipeTask, fGMFactory(NULL), bitmap, PipeTask::kSharedAddress_Mode);
 
-    SPAWN(QuiltTask, fGMFactory(NULL), bitmap);
+    SPAWN(QuiltTask, fGMFactory(NULL), bitmap, QuiltTask::kNormal_Mode);
+    SPAWN(QuiltTask, fGMFactory(NULL), bitmap, QuiltTask::kSkRecord_Mode);
 
     SPAWN(ReplayTask, fGMFactory(NULL), bitmap, ReplayTask::kNormal_Mode);
     SPAWN(ReplayTask, fGMFactory(NULL), bitmap, ReplayTask::kRTree_Mode);
diff --git a/dm/DMQuiltTask.cpp b/dm/DMQuiltTask.cpp
index 44c4341..960ca86 100644
--- a/dm/DMQuiltTask.cpp
+++ b/dm/DMQuiltTask.cpp
@@ -7,14 +7,17 @@
 #include "SkThreadPool.h"
 
 DEFINE_bool(quilt, true, "If true, draw into a quilt of small tiles and compare.");
-DEFINE_int32(quiltTile, 16, "Dimension of (square) quilt tile.");
+DEFINE_int32(quiltTile, 256, "Dimension of (square) quilt tile.");
 DEFINE_bool(quiltThreaded, false, "If true, draw quilt tiles with multiple threads.");
 
+static const char* kSuffixes[] = { "quilt", "quilt_skr" };
+
 namespace DM {
 
-QuiltTask::QuiltTask(const Task& parent, skiagm::GM* gm, SkBitmap reference)
+QuiltTask::QuiltTask(const Task& parent, skiagm::GM* gm, SkBitmap reference, QuiltTask::Mode mode)
     : CpuTask(parent)
-    , fName(UnderJoin(parent.name().c_str(), "quilt"))
+    , fMode(mode)
+    , fName(UnderJoin(parent.name().c_str(), kSuffixes[mode]))
     , fGM(gm)
     , fReference(reference)
     {}
@@ -25,62 +28,51 @@
 
 class Tile : public SkRunnable {
 public:
-    Tile(int x, int y, SkColorType colorType,
-         const SkPicture& picture, SkCanvas* canvas, SkMutex* mutex)
-        : fX(x)
-        , fY(y)
-        , fColorType(colorType)
+    Tile(int x, int y, const SkPicture& picture, SkBitmap* quilt)
+        : fX(x * FLAGS_quiltTile)
+        , fY(y * FLAGS_quiltTile)
         , fPicture(picture)
-        , fCanvas(canvas)
-        , fMutex(mutex) {}
+        , fQuilt(quilt) {}
 
     virtual void run() SK_OVERRIDE {
         SkBitmap tile;
-        tile.allocPixels(SkImageInfo::Make(FLAGS_quiltTile, FLAGS_quiltTile,
-                                           fColorType, kPremul_SkAlphaType));
+        fQuilt->extractSubset(&tile, SkIRect::MakeXYWH(fX, fY, FLAGS_quiltTile, FLAGS_quiltTile));
         SkCanvas tileCanvas(tile);
 
-        const SkScalar xOffset = SkIntToScalar(fX * tile.width()),
-                       yOffset = SkIntToScalar(fY * tile.height());
-        tileCanvas.translate(-xOffset, -yOffset);
+        tileCanvas.translate(SkIntToScalar(-fX), SkIntToScalar(-fY));
         fPicture.draw(&tileCanvas);
         tileCanvas.flush();
 
-        {
-            SkAutoMutexAcquire lock(fMutex);
-            fCanvas->drawBitmap(tile, xOffset, yOffset, NULL);
-        }
-
         delete this;
     }
 
 private:
     const int fX, fY;
-    const SkColorType fColorType;
     const SkPicture& fPicture;
-    SkCanvas* fCanvas;
-    SkMutex* fMutex;  // Guards fCanvas.
+    SkBitmap* fQuilt;
 };
 
 void QuiltTask::draw() {
-    SkAutoTUnref<SkPicture> recorded(RecordPicture(fGM.get()));
+    SkAutoTUnref<SkPicture> recorded(
+            RecordPicture(fGM.get(), NULL/*bbh factory*/, kSkRecord_Mode == fMode));
 
     SkBitmap full;
     AllocatePixels(fReference, &full);
-    SkCanvas fullCanvas(full);
-    SkMutex mutex;  // Guards fullCanvas.
 
-    SkThreadPool pool(FLAGS_quiltThreaded ? SkThreadPool::kThreadPerCore : 0);
+    int threads = 0;
+    if (kSkRecord_Mode == fMode || FLAGS_quiltThreaded) {
+        threads = SkThreadPool::kThreadPerCore;
+    }
+    SkThreadPool pool(threads);
 
     for (int y = 0; y < tiles_needed(full.height(), FLAGS_quiltTile); y++) {
         for (int x = 0; x < tiles_needed(full.width(), FLAGS_quiltTile); x++) {
             // Deletes itself when done.
-            pool.add(new Tile(x, y, fReference.colorType(), *recorded, &fullCanvas, &mutex));
+            pool.add(new Tile(x, y, *recorded, &full));
         }
     }
 
     pool.wait();
-    fullCanvas.flush();
 
     if (!BitmapsEqual(full, fReference)) {
         this->fail();
diff --git a/dm/DMQuiltTask.h b/dm/DMQuiltTask.h
index 56f322f..5f65662 100644
--- a/dm/DMQuiltTask.h
+++ b/dm/DMQuiltTask.h
@@ -14,15 +14,22 @@
 class QuiltTask : public CpuTask {
 
 public:
-    QuiltTask(const Task& parent,   // QuiltTask must be a child task.  Pass its parent here.
-                skiagm::GM*,          // GM to run through a picture.  Takes ownership.
-                SkBitmap reference);  // Bitmap to compare picture replay results to.
+    enum Mode {
+        kNormal_Mode,
+        kSkRecord_Mode,
+    };
+
+    QuiltTask(const Task& parent,  // QuiltTask must be a child task.  Pass its parent here.
+              skiagm::GM*,         // GM to run through a picture.  Takes ownership.
+              SkBitmap reference,  // Bitmap to compare picture replay results to.
+              Mode mode);
 
     virtual void draw() SK_OVERRIDE;
     virtual bool shouldSkip() const SK_OVERRIDE;
     virtual SkString name() const SK_OVERRIDE { return fName; }
 
 private:
+    const Mode fMode;
     const SkString fName;
     SkAutoTDelete<skiagm::GM> fGM;
     const SkBitmap fReference;
diff --git a/gm/bigmatrix.cpp b/gm/bigmatrix.cpp
index 3ad128e..df542fd 100644
--- a/gm/bigmatrix.cpp
+++ b/gm/bigmatrix.cpp
@@ -19,10 +19,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() {
         return SkString("bigmatrix");
     }
diff --git a/gm/bitmaprecttest.cpp b/gm/bitmaprecttest.cpp
index 06495b9..6f0ab58 100644
--- a/gm/bitmaprecttest.cpp
+++ b/gm/bitmaprecttest.cpp
@@ -56,10 +56,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() {
         return SkString("bitmaprecttest");
     }
diff --git a/gm/bitmapscroll.cpp b/gm/bitmapscroll.cpp
index 0015cd8..426000f 100644
--- a/gm/bitmapscroll.cpp
+++ b/gm/bitmapscroll.cpp
@@ -59,10 +59,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() {
         return SkString("bitmapscroll");
     }
diff --git a/gm/bitmapshader.cpp b/gm/bitmapshader.cpp
index 5cf5e14..8085f5c 100644
--- a/gm/bitmapshader.cpp
+++ b/gm/bitmapshader.cpp
@@ -50,10 +50,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() {
         return SkString("bitmapshaders");
     }
diff --git a/gm/blurrect.cpp b/gm/blurrect.cpp
index 0ea6f87..768e798 100644
--- a/gm/blurrect.cpp
+++ b/gm/blurrect.cpp
@@ -115,7 +115,7 @@
         }
     }
 
-    virtual uint32_t onGetFlags() const { return kSkipPipe_Flag | kSkipTiled_Flag; }
+    virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; }
 
 private:
     void drawProcs(SkCanvas* canvas, const SkRect& r, const SkPaint& paint,
diff --git a/gm/blurroundrect.cpp b/gm/blurroundrect.cpp
index 3e6f1bd..d78335f 100644
--- a/gm/blurroundrect.cpp
+++ b/gm/blurroundrect.cpp
@@ -42,10 +42,6 @@
         fRRect.setRectRadii(r, radii);
     }
 
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() SK_OVERRIDE {
         return fName;
     }
@@ -104,10 +100,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() SK_OVERRIDE {
         return fName;
     }
diff --git a/gm/coloremoji.cpp b/gm/coloremoji.cpp
index 7fe54f5..2eab864 100644
--- a/gm/coloremoji.cpp
+++ b/gm/coloremoji.cpp
@@ -24,10 +24,6 @@
         SkSafeUnref(fTypeface);
     }
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual void onOnceBeforeDraw() SK_OVERRIDE {
         SkString filename = GetResourcePath("/Funkster.ttf");
         SkAutoTUnref<SkFILEStream> stream(new SkFILEStream(filename.c_str()));
diff --git a/gm/composeshader.cpp b/gm/composeshader.cpp
index e460cea..c9c8fe4 100644
--- a/gm/composeshader.cpp
+++ b/gm/composeshader.cpp
@@ -49,10 +49,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() SK_OVERRIDE {
         return SkString("composeshader");
     }
@@ -91,11 +87,6 @@
         return SkISize::Make(220, 750);
     }
 
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        // we're only off by 1 bit per-component
-        return kSkipTiled_Flag;
-    }
-
     virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
         SkAutoTUnref<SkShader> shader0(make_shader(SkXfermode::kDstIn_Mode));
         SkAutoTUnref<SkShader> shader1(make_shader(SkXfermode::kSrcOver_Mode));
diff --git a/gm/drawlooper.cpp b/gm/drawlooper.cpp
index a04955b..67e280e 100644
--- a/gm/drawlooper.cpp
+++ b/gm/drawlooper.cpp
@@ -27,10 +27,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkISize onISize() {
         return SkISize::Make(520, 160);
     }
diff --git a/gm/filterindiabox.cpp b/gm/filterindiabox.cpp
index c3316b4..405c659 100644
--- a/gm/filterindiabox.cpp
+++ b/gm/filterindiabox.cpp
@@ -71,10 +71,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() SK_OVERRIDE {
         return fName;
     }
diff --git a/gm/gradientDirtyLaundry.cpp b/gm/gradientDirtyLaundry.cpp
index ea41203..a452763 100644
--- a/gm/gradientDirtyLaundry.cpp
+++ b/gm/gradientDirtyLaundry.cpp
@@ -70,10 +70,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     SkString onShortName() SK_OVERRIDE { return SkString("gradient_dirty_laundry"); }
     virtual SkISize onISize() SK_OVERRIDE { return SkISize::Make(640, 615); }
 
diff --git a/gm/gradients.cpp b/gm/gradients.cpp
index 277033b..fd02cee 100644
--- a/gm/gradients.cpp
+++ b/gm/gradients.cpp
@@ -295,10 +295,6 @@
     ClampedGradientsGM() {}
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     SkString onShortName() { return SkString("clamped_gradients"); }
 
     virtual SkISize onISize() { return SkISize::Make(640, 510); }
diff --git a/gm/gradients_2pt_conical.cpp b/gm/gradients_2pt_conical.cpp
index 5a6cec2..f43356e 100644
--- a/gm/gradients_2pt_conical.cpp
+++ b/gm/gradients_2pt_conical.cpp
@@ -312,13 +312,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        if (fGradCaseType != kInside_GradCaseType) {
-            return kSkipTiled_Flag;
-        }
-        return 0;
-    }
-
     SkString onShortName() {
         return fName;
     }
diff --git a/gm/gradtext.cpp b/gm/gradtext.cpp
index 5fac185..54bf175 100644
--- a/gm/gradtext.cpp
+++ b/gm/gradtext.cpp
@@ -100,11 +100,6 @@
     GradTextGM () {}
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
-
     virtual SkString onShortName() {
         return SkString("gradtext");
     }
diff --git a/gm/lerpmode.cpp b/gm/lerpmode.cpp
index 89301ea..30e6abd 100644
--- a/gm/lerpmode.cpp
+++ b/gm/lerpmode.cpp
@@ -39,10 +39,6 @@
     LerpXfermodeGM() {}
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() SK_OVERRIDE {
         return SkString("lerpmode");
     }
diff --git a/gm/ninepatchstretch.cpp b/gm/ninepatchstretch.cpp
index b6f1a60..67f6bf4 100644
--- a/gm/ninepatchstretch.cpp
+++ b/gm/ninepatchstretch.cpp
@@ -44,10 +44,6 @@
     NinePatchStretchGM() {}
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() {
         return SkString("ninepatch-stretch");
     }
diff --git a/gm/pathinterior.cpp b/gm/pathinterior.cpp
index e587928..559fb89 100644
--- a/gm/pathinterior.cpp
+++ b/gm/pathinterior.cpp
@@ -25,10 +25,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkISize onISize() {
         return SkISize::Make(770, 770);
     }
diff --git a/gm/pathopsinverse.cpp b/gm/pathopsinverse.cpp
index bdd8217..743db22 100644
--- a/gm/pathopsinverse.cpp
+++ b/gm/pathopsinverse.cpp
@@ -50,10 +50,6 @@
         paint->setColor(color);
     }
 
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;  // Only for 565.  8888 is fine.
-    }
-
     virtual SkString onShortName() SK_OVERRIDE {
         return SkString("pathopsinverse");
     }
diff --git a/gm/pathopsskpclip.cpp b/gm/pathopsskpclip.cpp
index 31d532d..5d6e401 100644
--- a/gm/pathopsskpclip.cpp
+++ b/gm/pathopsskpclip.cpp
@@ -24,10 +24,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() SK_OVERRIDE {
         return SkString("pathopsskpclip");
     }
diff --git a/gm/resizeimagefilter.cpp b/gm/resizeimagefilter.cpp
index 6b5fa4a..e87dff0 100644
--- a/gm/resizeimagefilter.cpp
+++ b/gm/resizeimagefilter.cpp
@@ -21,10 +21,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() {
         return SkString("resizeimagefilter");
     }
diff --git a/gm/samplerstress.cpp b/gm/samplerstress.cpp
index ef84f35..12f93cc 100644
--- a/gm/samplerstress.cpp
+++ b/gm/samplerstress.cpp
@@ -28,10 +28,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() {
         return SkString("samplerstress");
     }
diff --git a/gm/shadows.cpp b/gm/shadows.cpp
index ca49a02..3ecbdd8 100644
--- a/gm/shadows.cpp
+++ b/gm/shadows.cpp
@@ -36,10 +36,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() {
         return SkString("shadows");
     }
diff --git a/gm/simpleaaclip.cpp b/gm/simpleaaclip.cpp
index 201f9bc..863ee88 100644
--- a/gm/simpleaaclip.cpp
+++ b/gm/simpleaaclip.cpp
@@ -124,10 +124,6 @@
         canvas->restore();
     }
 
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kPath_GeomType == fGeomType ? kSkipTiled_Flag : 0;
-    }
-
     virtual SkString onShortName() {
         SkString str;
         str.printf("simpleaaclip_%s",
diff --git a/gm/skbug1719.cpp b/gm/skbug1719.cpp
index 6a4bd92..a57596e 100644
--- a/gm/skbug1719.cpp
+++ b/gm/skbug1719.cpp
@@ -24,10 +24,6 @@
     SkBug1719GM() {}
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() SK_OVERRIDE {
         return SkString("skbug1719");
     }
diff --git a/gm/thinstrokedrects.cpp b/gm/thinstrokedrects.cpp
index 37ffb35..7e3e399 100644
--- a/gm/thinstrokedrects.cpp
+++ b/gm/thinstrokedrects.cpp
@@ -17,10 +17,6 @@
     }
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     virtual SkString onShortName() SK_OVERRIDE {
         return SkString("thinstrokedrects");
     }
diff --git a/gm/tilemodes_scaled.cpp b/gm/tilemodes_scaled.cpp
index 88a070a..9b1fb61 100644
--- a/gm/tilemodes_scaled.cpp
+++ b/gm/tilemodes_scaled.cpp
@@ -65,13 +65,6 @@
         kNPOTSize = 3,
     };
 
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        if (!fPowerOfTwoSize) {
-            return kSkipTiled_Flag;  // Only for 565.  8888 is fine.
-        }
-        return 0;
-    }
-
     SkString onShortName() {
         SkString name("scaled_tilemodes");
         if (!fPowerOfTwoSize) {
diff --git a/gm/twopointradial.cpp b/gm/twopointradial.cpp
index 4118a69..902c7ae 100644
--- a/gm/twopointradial.cpp
+++ b/gm/twopointradial.cpp
@@ -56,10 +56,6 @@
     TwoPointRadialGM() {}
 
 protected:
-    virtual uint32_t onGetFlags() const SK_OVERRIDE {
-        return kSkipTiled_Flag;
-    }
-
     SkString onShortName() {
         return SkString("twopointconical");
     }
diff --git a/gm/verylargebitmap.cpp b/gm/verylargebitmap.cpp
index d3b16e9..afb49b9 100644
--- a/gm/verylargebitmap.cpp
+++ b/gm/verylargebitmap.cpp
@@ -99,8 +99,8 @@
         show_bm(canvas, veryBig, small, colors);
     }
 
-    virtual uint32_t onGetFlags() const {
 #ifdef SK_BUILD_FOR_WIN32
+    virtual uint32_t onGetFlags() const {
         // The Windows bot runs out of memory in replay modes on this test in 32bit builds:
         // http://skbug.com/1756
         return kSkipPicture_Flag            |
@@ -108,10 +108,8 @@
                kSkipPipeCrossProcess_Flag   |
                kSkipTiled_Flag              |
                kSkipScaledReplay_Flag;
-#else
-        return kSkipTiled_Flag;
-#endif
     }
+#endif
 
 private:
     typedef skiagm::GM INHERITED;
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 90c8ec8..4b1749a 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -54,47 +54,6 @@
     #define dec_canvas()
 #endif
 
-#ifdef SK_DEBUG
-#include "SkPixelRef.h"
-
-/*
- *  Some pixelref subclasses can support being "locked" from another thread
- *  during the lock-scope of skia calling them. In these instances, this balance
- *  check will fail, but may not be indicative of a problem, so we allow a build
- *  flag to disable this check.
- *
- *  Potentially another fix would be to have a (debug-only) virtual or flag on
- *  pixelref, which could tell us at runtime if this check is valid. That would
- *  eliminate the need for this heavy-handed build check.
- */
-#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
-class AutoCheckLockCountBalance {
-public:
-    AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
-};
-#else
-class AutoCheckLockCountBalance {
-public:
-    AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
-        fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
-    }
-    ~AutoCheckLockCountBalance() {
-        const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
-        SkASSERT(count == fLockCount);
-    }
-
-private:
-    const SkPixelRef* fPixelRef;
-    int               fLockCount;
-};
-#endif
-
-#define CHECK_LOCKCOUNT_BALANCE(bitmap)  AutoCheckLockCountBalance clcb(bitmap)
-
-#else
-    #define CHECK_LOCKCOUNT_BALANCE(bitmap)
-#endif
-
 typedef SkTLazy<SkPaint> SkLazyPaint;
 
 void SkCanvas::predrawNotify() {
@@ -1135,7 +1094,6 @@
     }
 
     SkDEBUGCODE(bitmap.validate();)
-    CHECK_LOCKCOUNT_BALANCE(bitmap);
 
     SkRect storage;
     const SkRect* bounds = NULL;
@@ -1202,7 +1160,6 @@
         return;
     }
     SkDEBUGCODE(bitmap.validate();)
-    CHECK_LOCKCOUNT_BALANCE(bitmap);
 
     SkPaint tmp;
     if (NULL == paint) {
@@ -2011,8 +1968,6 @@
         return;
     }
 
-    CHECK_LOCKCOUNT_BALANCE(bitmap);
-
     SkRect storage;
     const SkRect* bounds = &dst;
     if (NULL == paint || paint->canComputeFastBounds()) {
diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp
index db14910..89efb8e 100644
--- a/src/core/SkRecordDraw.cpp
+++ b/src/core/SkRecordDraw.cpp
@@ -31,6 +31,11 @@
     return fCanvas->quickRejectY(r.minY, r.maxY);
 }
 
+// FIXME: SkBitmaps are stateful, so we need to copy them to play back in multiple threads.
+static SkBitmap shallow_copy(const SkBitmap& bitmap) {
+    return bitmap;
+}
+
 // NoOps draw nothing.
 template <> void Draw::draw(const NoOp&) {}
 
@@ -49,10 +54,11 @@
 DRAW(ClipRect, clipRect(r.rect, r.op, r.doAA));
 DRAW(ClipRegion, clipRegion(r.region, r.op));
 
-DRAW(DrawBitmap, drawBitmap(r.bitmap, r.left, r.top, r.paint));
-DRAW(DrawBitmapMatrix, drawBitmapMatrix(r.bitmap, r.matrix, r.paint));
-DRAW(DrawBitmapNine, drawBitmapNine(r.bitmap, r.center, r.dst, r.paint));
-DRAW(DrawBitmapRectToRect, drawBitmapRectToRect(r.bitmap, r.src, r.dst, r.paint, r.flags));
+DRAW(DrawBitmap, drawBitmap(shallow_copy(r.bitmap), r.left, r.top, r.paint));
+DRAW(DrawBitmapMatrix, drawBitmapMatrix(shallow_copy(r.bitmap), r.matrix, r.paint));
+DRAW(DrawBitmapNine, drawBitmapNine(shallow_copy(r.bitmap), r.center, r.dst, r.paint));
+DRAW(DrawBitmapRectToRect,
+        drawBitmapRectToRect(shallow_copy(r.bitmap), r.src, r.dst, r.paint, r.flags));
 DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint));
 DRAW(DrawOval, drawOval(r.oval, r.paint));
 DRAW(DrawPaint, drawPaint(r.paint));
@@ -62,7 +68,7 @@
 DRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint));
 DRAW(DrawRRect, drawRRect(r.rrect, r.paint));
 DRAW(DrawRect, drawRect(r.rect, r.paint));
-DRAW(DrawSprite, drawSprite(r.bitmap, r.left, r.top, r.paint));
+DRAW(DrawSprite, drawSprite(shallow_copy(r.bitmap), r.left, r.top, r.paint));
 DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint));
 DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, r.matrix, r.paint));
 DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.colors,