[go: nahoru, domu]

[blimp] Add SkPicture caching support.

This CL adds support for de-duplication of SkPicture blobs across
consecutive frames.

Previously all DrawingDisplayItems and their respective SkPictures were
transferred from the engine to the client whenever the frames that
contained them was sent to the client. This lead to the same SkPictures
being sent over and over.

This CL changes this behavior so the engine can assume that the client
always keeps around the contents of the interest rect since last commit
and only sends down the new SkPictures that occur in the interest rect.

Both the engine and the client agrees on what is currently part of the
interest rect, and as such they can separately control their own caches
accordingly.

This has the benefit of sending less data from the engine to the client
whenever two consecutive frames have a lot of the same content.

This CL also creates //cc/blimp which is to be used for blimp-related
code living in //cc.

BUG=597811
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel

Review-Url: https://codereview.chromium.org/1982893002
Cr-Commit-Position: refs/heads/master@{#402006}
diff --git a/blimp/client/BUILD.gn b/blimp/client/BUILD.gn
index ebed114..dd1fbd1 100644
--- a/blimp/client/BUILD.gn
+++ b/blimp/client/BUILD.gn
@@ -188,6 +188,8 @@
 
 source_set("compositor") {
   sources = [
+    "feature/compositor/blimp_client_picture_cache.cc",
+    "feature/compositor/blimp_client_picture_cache.h",
     "feature/compositor/blimp_compositor.cc",
     "feature/compositor/blimp_compositor.h",
     "feature/compositor/blimp_compositor_manager.cc",
@@ -245,6 +247,7 @@
   testonly = true
 
   sources = [
+    "feature/compositor/blimp_client_picture_cache_unittest.cc",
     "feature/compositor/blimp_compositor_manager_unittest.cc",
     "feature/navigation_feature_unittest.cc",
     "feature/render_widget_feature_unittest.cc",
@@ -261,6 +264,7 @@
     "//blimp/common/proto",
     "//blimp/net",
     "//blimp/net:test_support",
+    "//blimp/test:support",
     "//cc/proto",
     "//net",
     "//net:test_support",
diff --git a/blimp/client/feature/compositor/blimp_client_picture_cache.cc b/blimp/client/feature/compositor/blimp_client_picture_cache.cc
new file mode 100644
index 0000000..b751e50b
--- /dev/null
+++ b/blimp/client/feature/compositor/blimp_client_picture_cache.cc
@@ -0,0 +1,98 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "blimp/client/feature/compositor/blimp_client_picture_cache.h"
+
+#include <utility>
+#include <vector>
+
+#include "third_party/skia/include/core/SkStream.h"
+
+namespace blimp {
+namespace client {
+namespace {
+
+// Helper function to deserialize the content of |picture_data| into an
+// SkPicture.
+sk_sp<const SkPicture> DeserializePicture(
+    SkPicture::InstallPixelRefProc pixel_deserializer,
+    const cc::PictureData& picture_data) {
+  SkMemoryStream stream(picture_data.data);
+  return SkPicture::MakeFromStream(&stream, pixel_deserializer);
+}
+
+}  // namespace
+
+BlimpClientPictureCache::BlimpClientPictureCache(
+    SkPicture::InstallPixelRefProc pixel_deserializer)
+    : pixel_deserializer_(pixel_deserializer) {}
+
+BlimpClientPictureCache::~BlimpClientPictureCache() = default;
+
+sk_sp<const SkPicture> BlimpClientPictureCache::GetPicture(
+    uint32_t engine_picture_id) {
+  return GetPictureFromCache(engine_picture_id);
+}
+
+void BlimpClientPictureCache::ApplyCacheUpdate(
+    const std::vector<cc::PictureData>& cache_update) {
+  // Add new pictures from the |cache_update| to |pictures_|.
+  for (const cc::PictureData& picture_data : cache_update) {
+    DCHECK(pictures_.find(picture_data.unique_id) == pictures_.end());
+    sk_sp<const SkPicture> deserialized_picture =
+        DeserializePicture(pixel_deserializer_, picture_data);
+
+    pictures_[picture_data.unique_id] = std::move(deserialized_picture);
+
+#if DCHECK_IS_ON()
+    last_added_.insert(picture_data.unique_id);
+#endif  // DCHECK_IS_ON()
+  }
+}
+
+void BlimpClientPictureCache::Flush() {
+  // Calculate which pictures can now be removed. |added| is only used for
+  // verifying that what we calculated matches the new items that have been
+  // inserted into the cache.
+  std::vector<uint32_t> added;
+  std::vector<uint32_t> removed;
+  reference_tracker_.CommitRefCounts(&added, &removed);
+
+  VerifyCacheUpdateMatchesReferenceTrackerData(added);
+
+  RemoveUnusedPicturesFromCache(removed);
+  reference_tracker_.ClearRefCounts();
+}
+
+void BlimpClientPictureCache::MarkUsed(uint32_t engine_picture_id) {
+  reference_tracker_.IncrementRefCount(engine_picture_id);
+}
+
+void BlimpClientPictureCache::RemoveUnusedPicturesFromCache(
+    const std::vector<uint32_t>& removed) {
+  for (const auto& engine_picture_id : removed) {
+    auto entry = pictures_.find(engine_picture_id);
+    DCHECK(entry != pictures_.end());
+    pictures_.erase(entry);
+  }
+}
+
+sk_sp<const SkPicture> BlimpClientPictureCache::GetPictureFromCache(
+    uint32_t engine_picture_id) {
+  DCHECK(pictures_.find(engine_picture_id) != pictures_.end());
+  return pictures_[engine_picture_id];
+}
+
+void BlimpClientPictureCache::VerifyCacheUpdateMatchesReferenceTrackerData(
+    const std::vector<uint32_t>& new_tracked_items) {
+#if DCHECK_IS_ON()
+  DCHECK_EQ(new_tracked_items.size(), last_added_.size());
+  DCHECK(std::unordered_set<uint32_t>(new_tracked_items.begin(),
+                                      new_tracked_items.end()) == last_added_);
+  last_added_.clear();
+#endif  // DCHECK_IS_ON()
+}
+
+}  // namespace client
+}  // namespace blimp
diff --git a/blimp/client/feature/compositor/blimp_client_picture_cache.h b/blimp/client/feature/compositor/blimp_client_picture_cache.h
new file mode 100644
index 0000000..cf73d65
--- /dev/null
+++ b/blimp/client/feature/compositor/blimp_client_picture_cache.h
@@ -0,0 +1,84 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BLIMP_CLIENT_FEATURE_COMPOSITOR_BLIMP_CLIENT_PICTURE_CACHE_H_
+#define BLIMP_CLIENT_FEATURE_COMPOSITOR_BLIMP_CLIENT_PICTURE_CACHE_H_
+
+#include <stdint.h>
+#include <memory>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "blimp/common/compositor/reference_tracker.h"
+#include "cc/blimp/client_picture_cache.h"
+#include "cc/blimp/engine_picture_cache.h"
+#include "cc/blimp/picture_data.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blimp {
+namespace client {
+
+// BlimpClientPictureCache provides functionality for caching SkPictures once
+// they are received from the engine, and cleaning up once the pictures are no
+// longer in use. It is required to update this cache when an SkPicture starts
+// being used and when it is not longer in use by calling
+// MarkPictureForRegistration and MarkPictureForUnregistration respectively.
+class BlimpClientPictureCache : public cc::ClientPictureCache {
+ public:
+  explicit BlimpClientPictureCache(
+      SkPicture::InstallPixelRefProc pixel_deserializer);
+  ~BlimpClientPictureCache() override;
+
+  // cc::ClientPictureCache implementation.
+  sk_sp<const SkPicture> GetPicture(uint32_t engine_picture_id) override;
+  void ApplyCacheUpdate(
+      const std::vector<cc::PictureData>& cache_update) override;
+  void Flush() override;
+  void MarkUsed(uint32_t engine_picture_id) override;
+
+ private:
+  // Removes all pictures specified in |picture_ids| from |pictures_|. The
+  // picture IDs passed to this function must refer to the pictures that are no
+  // longer in use.
+  void RemoveUnusedPicturesFromCache(const std::vector<uint32_t>& picture_ids);
+
+  // Retrieves the SkPicture with the given |picture_id| from the cache. The
+  // given |picture_id| is the unique ID that the engine used to identify the
+  // picture in the cc::PictureCacheUpdate that contained the image.
+  sk_sp<const SkPicture> GetPictureFromCache(uint32_t picture_id);
+
+  // Verify that the incoming cache update matches the new items that were added
+  // to the |reference_tracker_|.
+  void VerifyCacheUpdateMatchesReferenceTrackerData(
+      const std::vector<uint32_t>& new_tracked_items);
+
+#if DCHECK_IS_ON()
+  // A set of the items that were added when the last cache update was applied.
+  // Used for verifying that the calculation from the registry matches the
+  // expectations.
+  std::unordered_set<uint32_t> last_added_;
+#endif  // DCHECK_IS_ON()
+
+  // A function pointer valid to use for deserializing images when
+  // using SkPicture::CreateFromStream to create an SkPicture from a stream.
+  SkPicture::InstallPixelRefProc pixel_deserializer_;
+
+  // The current cache of SkPictures. The key is the unique ID used by the
+  // engine, and the value is the SkPicture itself.
+  std::unordered_map<uint32_t, sk_sp<const SkPicture>> pictures_;
+
+  // The reference tracker maintains the reference count of used SkPictures.
+  ReferenceTracker reference_tracker_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlimpClientPictureCache);
+};
+
+}  // namespace client
+}  // namespace blimp
+
+#endif  // BLIMP_CLIENT_FEATURE_COMPOSITOR_BLIMP_CLIENT_PICTURE_CACHE_H_
diff --git a/blimp/client/feature/compositor/blimp_client_picture_cache_unittest.cc b/blimp/client/feature/compositor/blimp_client_picture_cache_unittest.cc
new file mode 100644
index 0000000..6a19ca1
--- /dev/null
+++ b/blimp/client/feature/compositor/blimp_client_picture_cache_unittest.cc
@@ -0,0 +1,66 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "blimp/client/feature/compositor/blimp_client_picture_cache.h"
+
+#include <stdint.h>
+#include <memory>
+#include <vector>
+
+#include "blimp/test/support/compositor/picture_cache_test_support.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blimp {
+namespace client {
+namespace {
+
+bool FakeImageDecoder(const void* input, size_t input_size, SkBitmap* bitmap) {
+  return true;
+}
+
+class BlimpClientPictureCacheTest : public testing::Test {
+ public:
+  BlimpClientPictureCacheTest() : cache_(&FakeImageDecoder) {}
+  ~BlimpClientPictureCacheTest() override = default;
+
+ protected:
+  BlimpClientPictureCache cache_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BlimpClientPictureCacheTest);
+};
+
+TEST_F(BlimpClientPictureCacheTest, TwoSkPicturesInCache) {
+  sk_sp<const SkPicture> picture1 = CreateSkPicture(SK_ColorBLUE);
+  sk_sp<const SkPicture> picture2 = CreateSkPicture(SK_ColorRED);
+  cc::PictureData picture1_data = CreatePictureData(picture1);
+  cc::PictureData picture2_data = CreatePictureData(picture2);
+
+  std::vector<cc::PictureData> cache_update;
+  cache_update.push_back(picture1_data);
+  cache_update.push_back(picture2_data);
+
+  cache_.ApplyCacheUpdate(cache_update);
+
+  cache_.MarkUsed(picture1->uniqueID());
+  cache_.MarkUsed(picture2->uniqueID());
+
+  sk_sp<const SkPicture> cached_picture1 =
+      cache_.GetPicture(picture1->uniqueID());
+  EXPECT_NE(nullptr, cached_picture1);
+  EXPECT_EQ(GetBlobId(picture1), GetBlobId(cached_picture1));
+  sk_sp<const SkPicture> cached_picture2 =
+      cache_.GetPicture(picture2->uniqueID());
+  EXPECT_NE(nullptr, cached_picture2);
+  EXPECT_EQ(GetBlobId(picture2), GetBlobId(cached_picture2));
+
+  cache_.Flush();
+}
+
+}  // namespace
+}  // namespace client
+}  // namespace blimp
diff --git a/blimp/client/feature/compositor/blimp_image_decoder.cc b/blimp/client/feature/compositor/blimp_image_decoder.cc
index 2a00793..46e8a38 100644
--- a/blimp/client/feature/compositor/blimp_image_decoder.cc
+++ b/blimp/client/feature/compositor/blimp_image_decoder.cc
@@ -19,7 +19,7 @@
 namespace blimp {
 namespace client {
 
-bool BlimpImageDecoder(const void* input, size_t input_size, SkBitmap* bitmap) {
+bool DecodeBlimpImage(const void* input, size_t input_size, SkBitmap* bitmap) {
   DCHECK(bitmap);
 
   // Initialize an empty WebPDecoderConfig.
diff --git a/blimp/client/feature/compositor/blimp_image_decoder.h b/blimp/client/feature/compositor/blimp_image_decoder.h
index e7f6c81f..838c841 100644
--- a/blimp/client/feature/compositor/blimp_image_decoder.h
+++ b/blimp/client/feature/compositor/blimp_image_decoder.h
@@ -12,10 +12,10 @@
 namespace blimp {
 namespace client {
 
-// BlimpImageDecoder is an implementation of SkPicture::InstallPixelRefProc
+// DecodeBlimpImage is an implementation of SkPicture::InstallPixelRefProc
 // which is used by the client to decode WebP images that are part of an
 // SkPicture.
-bool BlimpImageDecoder(const void* input, size_t input_size, SkBitmap* bitmap);
+bool DecodeBlimpImage(const void* input, size_t input_size, SkBitmap* bitmap);
 
 }  // namespace client
 }  // namespace blimp
diff --git a/blimp/client/feature/compositor/blob_image_serialization_processor.cc b/blimp/client/feature/compositor/blob_image_serialization_processor.cc
index 65e1d0d..f0728fc 100644
--- a/blimp/client/feature/compositor/blob_image_serialization_processor.cc
+++ b/blimp/client/feature/compositor/blob_image_serialization_processor.cc
@@ -9,9 +9,11 @@
 #include <vector>
 
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/sha1.h"
 #include "base/strings/string_number_conversions.h"
+#include "blimp/client/feature/compositor/blimp_client_picture_cache.h"
 #include "blimp/client/feature/compositor/blimp_image_decoder.h"
 #include "blimp/common/blob_cache/blob_cache.h"
 #include "blimp/common/blob_cache/id_util.h"
@@ -77,18 +79,20 @@
   DVLOG(1) << "GetAndDecodeBlob(" << BlobIdToString(parsed_metadata.id())
            << ")";
 
-  return BlimpImageDecoder(reinterpret_cast<const void*>(&blob->data[0]),
-                           blob->data.size(), bitmap);
+  return DecodeBlimpImage(reinterpret_cast<const void*>(&blob->data[0]),
+                          blob->data.size(), bitmap);
 }
 
-SkPixelSerializer* BlobImageSerializationProcessor::GetPixelSerializer() {
+std::unique_ptr<cc::EnginePictureCache>
+BlobImageSerializationProcessor::CreateEnginePictureCache() {
   NOTREACHED();
   return nullptr;
 }
 
-SkPicture::InstallPixelRefProc
-BlobImageSerializationProcessor::GetPixelDeserializer() {
-  return &BlobImageSerializationProcessor::InstallPixelRefProc;
+std::unique_ptr<cc::ClientPictureCache>
+BlobImageSerializationProcessor::CreateClientPictureCache() {
+  return base::WrapUnique(new BlimpClientPictureCache(
+      &BlobImageSerializationProcessor::InstallPixelRefProc));
 }
 
 }  // namespace client
diff --git a/blimp/client/feature/compositor/blob_image_serialization_processor.h b/blimp/client/feature/compositor/blob_image_serialization_processor.h
index 432a43a..8e96e2a 100644
--- a/blimp/client/feature/compositor/blob_image_serialization_processor.h
+++ b/blimp/client/feature/compositor/blob_image_serialization_processor.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "base/memory/singleton.h"
-#include "cc/proto/image_serialization_processor.h"
+#include "cc/blimp/image_serialization_processor.h"
 #include "third_party/skia/include/core/SkPicture.h"
 
 class SkPixelSerializer;
@@ -20,7 +20,8 @@
 
 namespace client {
 
-// Adds BlobChannel image retrieval support to the Skia image decoding process.
+// Adds BlobChannel image retrieval support to the Skia image decoding process,
+// in addition to providing a cache for Skia images.
 class BlobImageSerializationProcessor : public cc::ImageSerializationProcessor {
  public:
   class ErrorDelegate {
@@ -33,7 +34,7 @@
   static BlobImageSerializationProcessor* current();
 
   BlobImageSerializationProcessor();
-  virtual ~BlobImageSerializationProcessor();
+  ~BlobImageSerializationProcessor() override;
 
   // Sets the |blob_receiver| to use for reading images.
   // |blob_receiver| must outlive |this|.
@@ -62,8 +63,8 @@
                                   SkBitmap* bitmap);
 
   // cc:ImageSerializationProcessor implementation.
-  SkPixelSerializer* GetPixelSerializer() override;
-  SkPicture::InstallPixelRefProc GetPixelDeserializer() override;
+  std::unique_ptr<cc::EnginePictureCache> CreateEnginePictureCache() override;
+  std::unique_ptr<cc::ClientPictureCache> CreateClientPictureCache() override;
 
   // Interface for accessing stored images received over the Blob Channel.
   BlobChannelReceiver* blob_receiver_ = nullptr;
diff --git a/blimp/common/BUILD.gn b/blimp/common/BUILD.gn
index 4a11b7d..b8972706 100644
--- a/blimp/common/BUILD.gn
+++ b/blimp/common/BUILD.gn
@@ -16,6 +16,8 @@
     "blob_cache/in_memory_blob_cache.h",
     "compositor/blimp_task_graph_runner.cc",
     "compositor/blimp_task_graph_runner.h",
+    "compositor/reference_tracker.cc",
+    "compositor/reference_tracker.h",
     "create_blimp_message.cc",
     "create_blimp_message.h",
     "get_client_token.cc",
@@ -64,6 +66,7 @@
   sources = [
     "blob_cache/id_util_unittest.cc",
     "blob_cache/in_memory_blob_cache_unittest.cc",
+    "compositor/reference_tracker_unittest.cc",
     "create_blimp_message_unittest.cc",
     "logging_unittest.cc",
   ]
diff --git a/blimp/common/compositor/reference_tracker.cc b/blimp/common/compositor/reference_tracker.cc
new file mode 100644
index 0000000..d5a2edc3
--- /dev/null
+++ b/blimp/common/compositor/reference_tracker.cc
@@ -0,0 +1,63 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "blimp/common/compositor/reference_tracker.h"
+
+#include <stdint.h>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "base/logging.h"
+
+namespace blimp {
+
+ReferenceTracker::ReferenceTracker() {}
+
+ReferenceTracker::~ReferenceTracker() {}
+
+void ReferenceTracker::IncrementRefCount(uint32_t item) {
+  ++active_ref_counts_[item];
+}
+
+void ReferenceTracker::DecrementRefCount(uint32_t item) {
+  DCHECK_GT(active_ref_counts_[item], 0);
+  --active_ref_counts_[item];
+}
+
+void ReferenceTracker::ClearRefCounts() {
+  for (auto it = active_ref_counts_.begin(); it != active_ref_counts_.end();
+       ++it) {
+    it->second = 0;
+  }
+}
+
+void ReferenceTracker::CommitRefCounts(std::vector<uint32_t>* added_entries,
+                                       std::vector<uint32_t>* removed_entries) {
+  DCHECK(added_entries);
+  DCHECK(removed_entries);
+  for (auto it = active_ref_counts_.begin(); it != active_ref_counts_.end();) {
+    uint32_t key = it->first;
+    uint32_t ref_count = it->second;
+    bool is_committed = committed_.count(key) > 0u;
+    if (ref_count > 0u) {
+      if (!is_committed) {
+        // The entry is new and has a positive reference count, so needs commit.
+        committed_.insert(key);
+        added_entries->push_back(key);
+      }
+      ++it;
+    } else {
+      if (is_committed) {
+        // The entry has already been committed, but is not reference anymore.
+        committed_.erase(key);
+        removed_entries->push_back(key);
+      }
+      // The entry has no references, so should not be staged anymore.
+      it = active_ref_counts_.erase(it);
+    }
+  }
+}
+
+}  // namespace blimp
diff --git a/blimp/common/compositor/reference_tracker.h b/blimp/common/compositor/reference_tracker.h
new file mode 100644
index 0000000..82c8241
--- /dev/null
+++ b/blimp/common/compositor/reference_tracker.h
@@ -0,0 +1,63 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BLIMP_COMMON_COMPOSITOR_REFERENCE_TRACKER_H_
+#define BLIMP_COMMON_COMPOSITOR_REFERENCE_TRACKER_H_
+
+#include <stdint.h>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "base/macros.h"
+#include "blimp/common/blimp_common_export.h"
+
+namespace blimp {
+
+// ReferenceTracker provides functionality to count the number of references to
+// a given uint32_t, and a way of retrieving the delta in the number of items
+// with a positive reference count since last call to CommitRefCounts().
+// CommitRefCounts() functions provides the deltas since last call to Commit(),
+// as sets of added and removed entries.
+// The important thing for any given item is whether it is in a state of having
+// positive reference count at the time of the call to CommitRefCounts(), not
+// how the reference count changed overtime in-between two calls to
+// CommitRefCounts().
+class BLIMP_COMMON_EXPORT ReferenceTracker {
+ public:
+  ReferenceTracker();
+  ~ReferenceTracker();
+
+  // Increment the reference count for an |item|.
+  void IncrementRefCount(uint32_t item);
+
+  // Decrement the reference count for an |item|. Negative reference counts are
+  // not allowed.
+  void DecrementRefCount(uint32_t item);
+
+  // Clears the reference count for all items.
+  void ClearRefCounts();
+
+  // Calculates the delta of items that still have a positive reference count
+  // since last call to CommitRefCounts() and provides calculated delta in the
+  // output params |added_items| and |removed_items|. This method changes
+  // the internal state of the ReferenceTracker to be able to track what has
+  // in fact been committed to make it possible to find a delta.
+  void CommitRefCounts(std::vector<uint32_t>* added_items,
+                       std::vector<uint32_t>* removed_items);
+
+ private:
+  // A reference count for all the given items. The key is the item, and the
+  // value is the count for that item.
+  std::unordered_map<uint32_t, int> active_ref_counts_;
+
+  // The full set of all committed items.
+  std::unordered_set<uint32_t> committed_;
+
+  DISALLOW_COPY_AND_ASSIGN(ReferenceTracker);
+};
+
+}  // namespace blimp
+
+#endif  // BLIMP_COMMON_COMPOSITOR_REFERENCE_TRACKER_H_
diff --git a/blimp/common/compositor/reference_tracker_unittest.cc b/blimp/common/compositor/reference_tracker_unittest.cc
new file mode 100644
index 0000000..21a9e90
--- /dev/null
+++ b/blimp/common/compositor/reference_tracker_unittest.cc
@@ -0,0 +1,199 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "blimp/common/compositor/reference_tracker.h"
+
+#include <stdint.h>
+#include <algorithm>
+#include <unordered_set>
+#include <vector>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blimp {
+namespace {
+
+class ReferenceTrackerTest : public testing::Test {
+ public:
+  ReferenceTrackerTest() = default;
+  ~ReferenceTrackerTest() override = default;
+
+ protected:
+  ReferenceTracker tracker_;
+  std::vector<uint32_t> added_;
+  std::vector<uint32_t> removed_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ReferenceTrackerTest);
+};
+
+TEST_F(ReferenceTrackerTest, SingleItemCommitFlow) {
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_TRUE(added_.empty());
+  EXPECT_TRUE(removed_.empty());
+
+  uint32_t item = 1;
+  tracker_.IncrementRefCount(item);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_THAT(added_, testing::UnorderedElementsAre(item));
+  EXPECT_TRUE(removed_.empty());
+  added_.clear();
+
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_TRUE(added_.empty());
+  EXPECT_TRUE(removed_.empty());
+
+  tracker_.DecrementRefCount(item);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_TRUE(added_.empty());
+  EXPECT_THAT(removed_, testing::UnorderedElementsAre(item));
+}
+
+TEST_F(ReferenceTrackerTest, SingleItemMultipleTimesInSingleCommit) {
+  uint32_t item = 1;
+  tracker_.IncrementRefCount(item);
+  tracker_.IncrementRefCount(item);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_THAT(added_, testing::UnorderedElementsAre(item));
+  EXPECT_TRUE(removed_.empty());
+  added_.clear();
+
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_TRUE(added_.empty());
+  EXPECT_TRUE(removed_.empty());
+
+  tracker_.DecrementRefCount(item);
+  tracker_.DecrementRefCount(item);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_TRUE(added_.empty());
+  EXPECT_THAT(removed_, testing::UnorderedElementsAre(item));
+}
+
+TEST_F(ReferenceTrackerTest, SingleItemMultipleTimesAcrossCommits) {
+  uint32_t item = 1;
+  tracker_.IncrementRefCount(item);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_THAT(added_, testing::UnorderedElementsAre(item));
+  EXPECT_TRUE(removed_.empty());
+  added_.clear();
+
+  tracker_.IncrementRefCount(item);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_TRUE(added_.empty());
+  EXPECT_TRUE(removed_.empty());
+
+  tracker_.DecrementRefCount(item);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_TRUE(added_.empty());
+  EXPECT_TRUE(removed_.empty());
+
+  tracker_.DecrementRefCount(item);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_TRUE(added_.empty());
+  EXPECT_THAT(removed_, testing::UnorderedElementsAre(item));
+}
+
+TEST_F(ReferenceTrackerTest, SingleItemComplexInteractions) {
+  uint32_t item = 1;
+  tracker_.IncrementRefCount(item);
+  tracker_.DecrementRefCount(item);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_TRUE(added_.empty());
+  EXPECT_TRUE(removed_.empty());
+
+  tracker_.IncrementRefCount(item);
+  tracker_.DecrementRefCount(item);
+  tracker_.IncrementRefCount(item);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_THAT(added_, testing::UnorderedElementsAre(item));
+  EXPECT_TRUE(removed_.empty());
+  added_.clear();
+
+  tracker_.DecrementRefCount(item);
+  tracker_.IncrementRefCount(item);
+  tracker_.DecrementRefCount(item);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_TRUE(added_.empty());
+  EXPECT_THAT(removed_, testing::UnorderedElementsAre(item));
+}
+
+TEST_F(ReferenceTrackerTest, MultipleItems) {
+  uint32_t item1 = 1;
+  uint32_t item2 = 2;
+  uint32_t item3 = 3;
+  tracker_.IncrementRefCount(item1);
+  tracker_.IncrementRefCount(item2);
+  tracker_.IncrementRefCount(item3);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_THAT(added_, testing::UnorderedElementsAre(item1, item2, item3));
+  EXPECT_TRUE(removed_.empty());
+  added_.clear();
+
+  tracker_.DecrementRefCount(item3);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_TRUE(added_.empty());
+  EXPECT_THAT(removed_, testing::UnorderedElementsAre(item3));
+  removed_.clear();
+
+  tracker_.DecrementRefCount(item2);
+  tracker_.IncrementRefCount(item3);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_THAT(added_, testing::UnorderedElementsAre(item3));
+  EXPECT_THAT(removed_, testing::UnorderedElementsAre(item2));
+  added_.clear();
+  removed_.clear();
+
+  tracker_.IncrementRefCount(item2);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_THAT(added_, testing::UnorderedElementsAre(item2));
+  EXPECT_TRUE(removed_.empty());
+  added_.clear();
+
+  tracker_.DecrementRefCount(item1);
+  tracker_.DecrementRefCount(item2);
+  tracker_.DecrementRefCount(item3);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_TRUE(added_.empty());
+  EXPECT_THAT(removed_, testing::UnorderedElementsAre(item1, item2, item3));
+}
+
+TEST_F(ReferenceTrackerTest, MultipleItemsWithClear) {
+  uint32_t item1 = 1;
+  uint32_t item2 = 2;
+  tracker_.IncrementRefCount(item1);
+  tracker_.IncrementRefCount(item2);
+  tracker_.ClearRefCounts();
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_TRUE(added_.empty());
+  EXPECT_TRUE(removed_.empty());
+
+  tracker_.IncrementRefCount(item1);
+  tracker_.IncrementRefCount(item2);
+  tracker_.ClearRefCounts();
+  tracker_.IncrementRefCount(item1);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_THAT(added_, testing::UnorderedElementsAre(item1));
+  EXPECT_TRUE(removed_.empty());
+  added_.clear();
+
+  tracker_.ClearRefCounts();
+  tracker_.IncrementRefCount(item2);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_THAT(added_, testing::UnorderedElementsAre(item2));
+  EXPECT_THAT(removed_, testing::UnorderedElementsAre(item1));
+  added_.clear();
+  removed_.clear();
+
+  tracker_.ClearRefCounts();
+  tracker_.IncrementRefCount(item1);
+  tracker_.IncrementRefCount(item2);
+  tracker_.CommitRefCounts(&added_, &removed_);
+  EXPECT_THAT(added_, testing::UnorderedElementsAre(item1));
+  EXPECT_TRUE(removed_.empty());
+  added_.clear();
+}
+
+}  // namespace
+}  // namespace blimp
diff --git a/blimp/engine/BUILD.gn b/blimp/engine/BUILD.gn
index cb1527a..020cc44 100644
--- a/blimp/engine/BUILD.gn
+++ b/blimp/engine/BUILD.gn
@@ -289,6 +289,8 @@
   sources = [
     "renderer/blimp_content_renderer_client.cc",
     "renderer/blimp_content_renderer_client.h",
+    "renderer/blimp_engine_picture_cache.cc",
+    "renderer/blimp_engine_picture_cache.h",
     "renderer/blob_channel_sender_proxy.cc",
     "renderer/blob_channel_sender_proxy.h",
     "renderer/engine_image_serialization_processor.cc",
@@ -465,6 +467,22 @@
   ]
 }
 
+source_set("renderer_unit_tests") {
+  testonly = true
+
+  sources = [
+    "renderer/blimp_engine_picture_cache_unittest.cc",
+  ]
+
+  deps = [
+    ":renderer",
+    "//blimp/test:support",
+    "//skia",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
+
 source_set("unit_tests") {
   testonly = true
 
@@ -472,6 +490,7 @@
     ":app_unit_tests",
     ":common_unit_tests",
     ":feature_unit_tests",
+    ":renderer_unit_tests",
   ]
 }
 
diff --git a/blimp/engine/renderer/blimp_engine_picture_cache.cc b/blimp/engine/renderer/blimp_engine_picture_cache.cc
new file mode 100644
index 0000000..bb67345
--- /dev/null
+++ b/blimp/engine/renderer/blimp_engine_picture_cache.cc
@@ -0,0 +1,67 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "blimp/engine/renderer/blimp_engine_picture_cache.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "third_party/skia/include/core/SkStream.h"
+
+namespace blimp {
+namespace engine {
+
+BlimpEnginePictureCache::BlimpEnginePictureCache(
+    SkPixelSerializer* pixel_serializer)
+    : pixel_serializer_(pixel_serializer) {}
+
+BlimpEnginePictureCache::~BlimpEnginePictureCache() = default;
+
+void BlimpEnginePictureCache::MarkUsed(const SkPicture* picture) {
+  DCHECK(picture);
+  reference_tracker_.IncrementRefCount(picture->uniqueID());
+
+  // Do not serialize multiple times, even though the item is referred to from
+  // multiple places.
+  if (pictures_.find(picture->uniqueID()) != pictures_.end()) {
+    return;
+  }
+
+  Put(picture);
+}
+
+std::vector<cc::PictureData>
+BlimpEnginePictureCache::CalculateCacheUpdateAndFlush() {
+  std::vector<uint32_t> added;
+  std::vector<uint32_t> removed;
+  reference_tracker_.CommitRefCounts(&added, &removed);
+
+  // Create cache update consisting of new pictures.
+  std::vector<cc::PictureData> update;
+  for (const uint32_t item : added) {
+    auto entry = pictures_.find(item);
+    DCHECK(entry != pictures_.end());
+    update.push_back(entry->second);
+  }
+
+  // All new items will be sent to the client, so clear everything.
+  pictures_.clear();
+  reference_tracker_.ClearRefCounts();
+
+  return update;
+}
+
+void BlimpEnginePictureCache::Put(const SkPicture* picture) {
+  SkDynamicMemoryWStream stream;
+  picture->serialize(&stream, pixel_serializer_);
+  DCHECK_GE(stream.bytesWritten(), 0u);
+
+  // Store the picture data until it is sent to the client.
+  pictures_.insert(
+      std::make_pair(picture->uniqueID(),
+                     cc::PictureData(picture->uniqueID(),
+                                     sk_sp<SkData>(stream.copyToData()))));
+}
+
+}  // namespace engine
+}  // namespace blimp
diff --git a/blimp/engine/renderer/blimp_engine_picture_cache.h b/blimp/engine/renderer/blimp_engine_picture_cache.h
new file mode 100644
index 0000000..84161a7a6
--- /dev/null
+++ b/blimp/engine/renderer/blimp_engine_picture_cache.h
@@ -0,0 +1,63 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BLIMP_ENGINE_RENDERER_BLIMP_ENGINE_PICTURE_CACHE_H_
+#define BLIMP_ENGINE_RENDERER_BLIMP_ENGINE_PICTURE_CACHE_H_
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+#include "base/macros.h"
+#include "blimp/common/compositor/reference_tracker.h"
+#include "cc/blimp/engine_picture_cache.h"
+#include "cc/blimp/picture_data.h"
+#include "third_party/skia/include/core/SkPicture.h"
+
+class SkPixelSerializer;
+
+namespace blimp {
+namespace engine {
+
+// BlimpEnginePictureCache provides functionality for caching SkPictures before
+// they are sent from the engine to the client. The cache is cleared after
+// every time it is flushed which happens when CalculateCacheUpdateAndFlush()
+// is called. The expected state of what the client already has cached is
+// tracked. It is required to update this cache when an SkPicture
+// starts being used and when it is not longer in use by calling
+// MarkPictureForRegistration and MarkPictureForUnregistration respectively.
+// The lifetime of a cache matches the lifetime of a specific compositor.
+// All interaction with this class should happen on the main thread.
+class BlimpEnginePictureCache : public cc::EnginePictureCache {
+ public:
+  explicit BlimpEnginePictureCache(SkPixelSerializer* pixel_serializer);
+  ~BlimpEnginePictureCache() override;
+
+  // cc::EnginePictureCache implementation.
+  void MarkUsed(const SkPicture* picture) override;
+  std::vector<cc::PictureData> CalculateCacheUpdateAndFlush() override;
+
+ private:
+  // Serializes the SkPicture and adds it to |pictures_|.
+  void Put(const SkPicture* picture);
+
+  // A serializer that be used to pass in to SkPicture::serialize(...) for
+  // serializing the SkPicture to a stream.
+  SkPixelSerializer* pixel_serializer_;
+
+  // The current cache of pictures. Used for temporarily storing pictures until
+  // the next call to CalculateCacheUpdateAndFlush(), at which point this map
+  // is cleared.
+  std::unordered_map<uint32_t, cc::PictureData> pictures_;
+
+  // The reference tracker maintains the reference count of used SkPictures.
+  ReferenceTracker reference_tracker_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlimpEnginePictureCache);
+};
+
+}  // namespace engine
+}  // namespace blimp
+
+#endif  // BLIMP_ENGINE_RENDERER_BLIMP_ENGINE_PICTURE_CACHE_H_
diff --git a/blimp/engine/renderer/blimp_engine_picture_cache_unittest.cc b/blimp/engine/renderer/blimp_engine_picture_cache_unittest.cc
new file mode 100644
index 0000000..1c76ca44
--- /dev/null
+++ b/blimp/engine/renderer/blimp_engine_picture_cache_unittest.cc
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "blimp/engine/renderer/blimp_engine_picture_cache.h"
+
+#include "blimp/test/support/compositor/picture_cache_test_support.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blimp {
+namespace engine {
+namespace {
+
+class BlimpEnginePictureCacheTest : public testing::Test {
+ public:
+  BlimpEnginePictureCacheTest() : cache_(nullptr) {}
+  ~BlimpEnginePictureCacheTest() override = default;
+
+ protected:
+  BlimpEnginePictureCache cache_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BlimpEnginePictureCacheTest);
+};
+
+TEST_F(BlimpEnginePictureCacheTest, TwoCachedPicturesCanBeRetrieved) {
+  EXPECT_TRUE(cache_.CalculateCacheUpdateAndFlush().empty());
+
+  sk_sp<const SkPicture> picture1 = CreateSkPicture(SK_ColorBLUE);
+  cache_.MarkUsed(picture1.get());
+  sk_sp<const SkPicture> picture2 = CreateSkPicture(SK_ColorRED);
+  cache_.MarkUsed(picture2.get());
+
+  std::vector<cc::PictureData> update = cache_.CalculateCacheUpdateAndFlush();
+  ASSERT_EQ(2U, update.size());
+  EXPECT_THAT(update, testing::UnorderedElementsAre(PictureMatches(picture1),
+                                                    PictureMatches(picture2)));
+
+  EXPECT_TRUE(cache_.CalculateCacheUpdateAndFlush().empty());
+}
+
+TEST_F(BlimpEnginePictureCacheTest, SamePictureOnlyReturnedOnce) {
+  EXPECT_TRUE(cache_.CalculateCacheUpdateAndFlush().empty());
+
+  sk_sp<const SkPicture> picture = CreateSkPicture(SK_ColorBLUE);
+  cache_.MarkUsed(picture.get());
+  cache_.MarkUsed(picture.get());
+
+  std::vector<cc::PictureData> update = cache_.CalculateCacheUpdateAndFlush();
+  ASSERT_EQ(1U, update.size());
+  EXPECT_THAT(update, testing::UnorderedElementsAre(PictureMatches(picture)));
+}
+
+}  // namespace
+}  // namespace engine
+}  // namespace blimp
diff --git a/blimp/engine/renderer/engine_image_serialization_processor.cc b/blimp/engine/renderer/engine_image_serialization_processor.cc
index 7d74501..c79f2af0 100644
--- a/blimp/engine/renderer/engine_image_serialization_processor.cc
+++ b/blimp/engine/renderer/engine_image_serialization_processor.cc
@@ -11,10 +11,12 @@
 
 #include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "blimp/common/blob_cache/id_util.h"
 #include "blimp/common/proto/blob_cache.pb.h"
+#include "blimp/engine/renderer/blimp_engine_picture_cache.h"
 #include "blimp/engine/renderer/blob_channel_sender_proxy.h"
 #include "content/public/renderer/render_frame.h"
 #include "third_party/libwebp/webp/encode.h"
@@ -96,12 +98,14 @@
   WebPMemoryWriterClear(&writer_state_);
 }
 
-SkPixelSerializer* EngineImageSerializationProcessor::GetPixelSerializer() {
-  return this;
+std::unique_ptr<cc::EnginePictureCache>
+EngineImageSerializationProcessor::CreateEnginePictureCache() {
+  return base::WrapUnique(new BlimpEnginePictureCache(this));
 }
 
-SkPicture::InstallPixelRefProc
-EngineImageSerializationProcessor::GetPixelDeserializer() {
+std::unique_ptr<cc::ClientPictureCache>
+EngineImageSerializationProcessor::CreateClientPictureCache() {
+  NOTREACHED();
   return nullptr;
 }
 
diff --git a/blimp/engine/renderer/engine_image_serialization_processor.h b/blimp/engine/renderer/engine_image_serialization_processor.h
index b551967..52ee511 100644
--- a/blimp/engine/renderer/engine_image_serialization_processor.h
+++ b/blimp/engine/renderer/engine_image_serialization_processor.h
@@ -12,9 +12,10 @@
 #include "blimp/common/blimp_common_export.h"
 #include "blimp/common/blob_cache/id_util.h"
 #include "blimp/engine/mojo/blob_channel.mojom.h"
-#include "cc/proto/image_serialization_processor.h"
+#include "cc/blimp/client_picture_cache.h"
+#include "cc/blimp/engine_picture_cache.h"
+#include "cc/blimp/image_serialization_processor.h"
 #include "third_party/libwebp/webp/encode.h"
-#include "third_party/skia/include/core/SkPicture.h"
 #include "third_party/skia/include/core/SkPixelSerializer.h"
 
 namespace content {
@@ -26,8 +27,8 @@
 
 class BlobChannelSenderProxy;
 
-// EngineImageSerializationProcessor provides functionality to serialize and
-// deserialize Skia images.
+// EngineImageSerializationProcessor provides functionality to serialize
+// and temporarily cache Skia images.
 class BLIMP_COMMON_EXPORT EngineImageSerializationProcessor
     : public cc::ImageSerializationProcessor,
       public SkPixelSerializer {
@@ -37,8 +38,8 @@
   ~EngineImageSerializationProcessor() override;
 
   // cc::ImageSerializationProcessor implementation.
-  SkPixelSerializer* GetPixelSerializer() override;
-  SkPicture::InstallPixelRefProc GetPixelDeserializer() override;
+  std::unique_ptr<cc::EnginePictureCache> CreateEnginePictureCache() override;
+  std::unique_ptr<cc::ClientPictureCache> CreateClientPictureCache() override;
 
   // SkPixelSerializer implementation.
   bool onUseEncodedData(const void* data, size_t len) override;
@@ -47,6 +48,8 @@
  private:
   scoped_refptr<BlobData> EncodeImageAsBlob(const SkPixmap& pixmap);
 
+  // A serializer that be used to pass in to SkPicture::serialize(...) for
+  // serializing the SkPicture to a stream.
   std::unique_ptr<SkPixelSerializer> pixel_serializer_;
 
   // Sends image data as blobs to the browser process.
diff --git a/blimp/test/BUILD.gn b/blimp/test/BUILD.gn
new file mode 100644
index 0000000..0802b66
--- /dev/null
+++ b/blimp/test/BUILD.gn
@@ -0,0 +1,21 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("support") {
+  testonly = true
+
+  sources = [
+    "support/compositor/picture_cache_test_support.cc",
+    "support/compositor/picture_cache_test_support.h",
+  ]
+
+  deps = [
+    "//blimp/common",
+    "//cc",
+    "//skia",
+    "//testing/gmock",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+  ]
+}
diff --git a/blimp/test/support/compositor/picture_cache_test_support.cc b/blimp/test/support/compositor/picture_cache_test_support.cc
new file mode 100644
index 0000000..c58b941a
--- /dev/null
+++ b/blimp/test/support/compositor/picture_cache_test_support.cc
@@ -0,0 +1,63 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "blimp/test/support/compositor/picture_cache_test_support.h"
+
+#include "base/memory/ptr_util.h"
+#include "blimp/common/blob_cache/blob_cache.h"
+#include "blimp/common/blob_cache/id_util.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkStream.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/skia_util.h"
+
+namespace blimp {
+
+sk_sp<const SkPicture> CreateSkPicture(SkColor color) {
+  SkPictureRecorder recorder;
+  sk_sp<SkCanvas> canvas =
+      sk_ref_sp(recorder.beginRecording(SkRect::MakeWH(1, 1)));
+  canvas->drawColor(color);
+  return recorder.finishRecordingAsPicture();
+}
+
+sk_sp<SkData> SerializePicture(sk_sp<const SkPicture> picture) {
+  SkDynamicMemoryWStream stream;
+  picture->serialize(&stream, nullptr);
+  DCHECK(stream.bytesWritten());
+  return sk_sp<SkData>(stream.copyToData());
+}
+
+BlobId GetBlobId(sk_sp<const SkPicture> picture) {
+  sk_sp<SkData> data = SerializePicture(picture);
+  return CalculateBlobId(data->data(), data->size());
+}
+
+sk_sp<const SkPicture> DeserializePicture(sk_sp<SkData> data) {
+  SkMemoryStream stream(data);
+  return SkPicture::MakeFromStream(&stream, nullptr);
+}
+
+bool PicturesEqual(sk_sp<const SkPicture> picture,
+                   const cc::PictureData& picture_data) {
+  if (picture->uniqueID() != picture_data.unique_id) {
+    return false;
+  }
+  sk_sp<const SkPicture> deserialized_picture =
+      DeserializePicture(picture_data.data);
+  return GetBlobId(picture) == GetBlobId(deserialized_picture);
+}
+
+cc::PictureData CreatePictureData(sk_sp<const SkPicture> picture) {
+  return cc::PictureData(picture->uniqueID(), SerializePicture(picture));
+}
+
+}  // namespace blimp
diff --git a/blimp/test/support/compositor/picture_cache_test_support.h b/blimp/test/support/compositor/picture_cache_test_support.h
new file mode 100644
index 0000000..443313c8
--- /dev/null
+++ b/blimp/test/support/compositor/picture_cache_test_support.h
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BLIMP_TEST_SUPPORT_COMPOSITOR_PICTURE_CACHE_TEST_SUPPORT_H_
+#define BLIMP_TEST_SUPPORT_COMPOSITOR_PICTURE_CACHE_TEST_SUPPORT_H_
+
+#include "blimp/common/blob_cache/blob_cache.h"
+#include "cc/blimp/picture_data.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blimp {
+
+// Creates an SkPicture filled with with the given |color|.
+sk_sp<const SkPicture> CreateSkPicture(SkColor color);
+
+// Serializes the given picture into an SkData object.
+sk_sp<SkData> SerializePicture(sk_sp<const SkPicture> picture);
+
+// Serializes an SkPicture and returns the BlobId for the serialized form of
+// the picture.
+BlobId GetBlobId(sk_sp<const SkPicture> picture);
+
+// Deserializes the SkPicture represented by |data.
+sk_sp<const SkPicture> DeserializePicture(sk_sp<SkData> data);
+
+// Compares an SkPicture and SkPicture represented by the SkData in the
+// cc::PictureData.
+bool PicturesEqual(sk_sp<const SkPicture> picture,
+                   const cc::PictureData& picture_data);
+
+// Utility function to create a valid cc::PictureData from an SkPicture.
+cc::PictureData CreatePictureData(sk_sp<const SkPicture> picture);
+
+MATCHER_P(PictureMatches, picture, "") {
+  return PicturesEqual(picture, arg);
+}
+
+}  // namespace blimp
+
+#endif  // BLIMP_TEST_SUPPORT_COMPOSITOR_PICTURE_CACHE_TEST_SUPPORT_H_
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index bd3c386..2064895 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -40,6 +40,13 @@
     "animation/transform_operation.h",
     "animation/transform_operations.cc",
     "animation/transform_operations.h",
+    "blimp/client_picture_cache.h",
+    "blimp/engine_picture_cache.h",
+    "blimp/image_serialization_processor.h",
+    "blimp/picture_data.cc",
+    "blimp/picture_data.h",
+    "blimp/picture_data_conversions.cc",
+    "blimp/picture_data_conversions.h",
     "debug/benchmark_instrumentation.cc",
     "debug/benchmark_instrumentation.h",
     "debug/debug_colors.cc",
@@ -311,7 +318,6 @@
     "proto/gfx_conversions.h",
     "proto/gpu_conversions.cc",
     "proto/gpu_conversions.h",
-    "proto/image_serialization_processor.h",
     "proto/skia_conversions.cc",
     "proto/skia_conversions.h",
     "proto/synced_property_conversions.cc",
@@ -588,8 +594,12 @@
     "test/failure_output_surface.h",
     "test/fake_channel_impl.cc",
     "test/fake_channel_impl.h",
+    "test/fake_client_picture_cache.cc",
+    "test/fake_client_picture_cache.h",
     "test/fake_content_layer_client.cc",
     "test/fake_content_layer_client.h",
+    "test/fake_engine_picture_cache.cc",
+    "test/fake_engine_picture_cache.h",
     "test/fake_external_begin_frame_source.cc",
     "test/fake_external_begin_frame_source.h",
     "test/fake_image_serialization_processor.cc",
@@ -667,6 +677,8 @@
     "test/ordered_texture_map.h",
     "test/paths.cc",
     "test/paths.h",
+    "test/picture_cache_model.cc",
+    "test/picture_cache_model.h",
     "test/pixel_comparator.cc",
     "test/pixel_comparator.h",
     "test/pixel_test.cc",
@@ -792,6 +804,7 @@
     "base/simple_enclosed_region_unittest.cc",
     "base/tiling_data_unittest.cc",
     "base/unique_notifier_unittest.cc",
+    "blimp/picture_data_conversions_unittest.cc",
     "debug/layer_tree_debug_state_unittest.cc",
     "debug/micro_benchmark_controller_unittest.cc",
     "debug/rendering_stats_unittest.cc",
diff --git a/cc/blimp/client_picture_cache.h b/cc/blimp/client_picture_cache.h
new file mode 100644
index 0000000..ddee6a3
--- /dev/null
+++ b/cc/blimp/client_picture_cache.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BLIMP_CLIENT_PICTURE_CACHE_H_
+#define CC_BLIMP_CLIENT_PICTURE_CACHE_H_
+
+#include <vector>
+
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkPicture;
+
+namespace cc {
+struct PictureData;
+
+// ClientPictureCache provides functionaltiy for marking when SkPictures are in
+// use an when they stop being in use. PictureCacheUpdates are provided
+// with all new SkPictures that a client needs and the respective unique IDs
+// used on the engine. The SkPictures are kept in memory until they are no
+// longer in use.
+class ClientPictureCache {
+ public:
+  virtual ~ClientPictureCache() {}
+
+  // MarkUsed must be called when the client has started using an SkPicture that
+  // was sent to from the engine.
+  virtual void MarkUsed(uint32_t engine_picture_id) = 0;
+
+  // Retrieves an SkPicture from the in-memory cache. It is required that the
+  // provided ID is the same as the unique ID that is given through the cache
+  // update.
+  virtual sk_sp<const SkPicture> GetPicture(uint32_t engine_picture_id) = 0;
+
+  // Called when a PictureCacheUpdate has been retrieved from the engine. This
+  // must contain all the new SkPictures that are in use.
+  virtual void ApplyCacheUpdate(const std::vector<PictureData>& pictures) = 0;
+
+  // Flushes the current state of the cache, removing unused SkPictures. Must be
+  // called after deserialization is finished.
+  virtual void Flush() = 0;
+};
+
+}  // namespace cc
+
+#endif  // CC_BLIMP_CLIENT_PICTURE_CACHE_H_
diff --git a/cc/blimp/engine_picture_cache.h b/cc/blimp/engine_picture_cache.h
new file mode 100644
index 0000000..d198b32
--- /dev/null
+++ b/cc/blimp/engine_picture_cache.h
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BLIMP_ENGINE_PICTURE_CACHE_H_
+#define CC_BLIMP_ENGINE_PICTURE_CACHE_H_
+
+#include <vector>
+
+class SkPicture;
+
+namespace cc {
+struct PictureData;
+
+// EnginePictureCache provides functionaltiy for marking when SkPictures are in
+// use an when they stop being in use. Based on this, a PictureCacheUpdate can
+// be retrieved that includes all the new items since last time it was called.
+// This PictureCacheUpdate is then supposed to be sent to the client.
+class EnginePictureCache {
+ public:
+  virtual ~EnginePictureCache() {}
+
+  // MarkUsed must be called when an SkPicture needs to available on the client.
+  virtual void MarkUsed(const SkPicture* picture) = 0;
+
+  // Called when a PictureCacheUpdate is going to be sent to the client. This
+  // must contain all the new SkPictures that are in use since last call.
+  virtual std::vector<PictureData> CalculateCacheUpdateAndFlush() = 0;
+};
+
+}  // namespace cc
+
+#endif  // CC_BLIMP_ENGINE_PICTURE_CACHE_H_
diff --git a/cc/blimp/image_serialization_processor.h b/cc/blimp/image_serialization_processor.h
new file mode 100644
index 0000000..697317b
--- /dev/null
+++ b/cc/blimp/image_serialization_processor.h
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BLIMP_IMAGE_SERIALIZATION_PROCESSOR_H_
+#define CC_BLIMP_IMAGE_SERIALIZATION_PROCESSOR_H_
+
+#include "base/memory/ptr_util.h"
+#include "cc/blimp/client_picture_cache.h"
+#include "cc/blimp/engine_picture_cache.h"
+#include "third_party/skia/include/core/SkPicture.h"
+
+class SkPixelSerializer;
+
+namespace cc {
+
+// ImageSerializationProcessor provides functionality to serialize,
+// deserialize and cache Skia images.
+class ImageSerializationProcessor {
+ public:
+  virtual ~ImageSerializationProcessor() {}
+
+  virtual std::unique_ptr<EnginePictureCache> CreateEnginePictureCache() = 0;
+  virtual std::unique_ptr<ClientPictureCache> CreateClientPictureCache() = 0;
+};
+
+}  // namespace cc
+
+#endif  // CC_BLIMP_IMAGE_SERIALIZATION_PROCESSOR_H_
diff --git a/cc/blimp/picture_data.cc b/cc/blimp/picture_data.cc
new file mode 100644
index 0000000..a7827f28
--- /dev/null
+++ b/cc/blimp/picture_data.cc
@@ -0,0 +1,16 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/blimp/picture_data.h"
+
+namespace cc {
+
+PictureData::PictureData(uint32_t unique_id, sk_sp<SkData> data)
+    : unique_id(unique_id), data(data) {}
+
+PictureData::PictureData(const PictureData& other) = default;
+
+PictureData::~PictureData() = default;
+
+}  // namespace cc
diff --git a/cc/blimp/picture_data.h b/cc/blimp/picture_data.h
new file mode 100644
index 0000000..62db568
--- /dev/null
+++ b/cc/blimp/picture_data.h
@@ -0,0 +1,28 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BLIMP_PICTURE_DATA_H_
+#define CC_BLIMP_PICTURE_DATA_H_
+
+#include <stdint.h>
+
+#include "cc/base/cc_export.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace cc {
+
+// PictureData is a holder object for a serialized SkPicture and its unique ID.
+struct CC_EXPORT PictureData {
+  PictureData(uint32_t unique_id, sk_sp<SkData> data);
+  PictureData(const PictureData& other);
+  ~PictureData();
+
+  uint32_t unique_id;
+  sk_sp<SkData> data;
+};
+
+}  // namespace cc
+
+#endif  // CC_BLIMP_PICTURE_DATA_H_
diff --git a/cc/blimp/picture_data_conversions.cc b/cc/blimp/picture_data_conversions.cc
new file mode 100644
index 0000000..d54835f6
--- /dev/null
+++ b/cc/blimp/picture_data_conversions.cc
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/blimp/picture_data_conversions.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "cc/proto/display_item.pb.h"
+#include "cc/proto/layer_tree_host.pb.h"
+
+namespace cc {
+namespace proto {
+
+void PictureDataVectorToSkPicturesProto(
+    const std::vector<PictureData>& cache_update,
+    SkPictures* proto_pictures) {
+  for (const PictureData& picture : cache_update) {
+    proto::SkPictureData* picture_data = proto_pictures->add_pictures();
+    proto::SkPictureID* picture_id = picture_data->mutable_id();
+    picture_id->set_unique_id(picture.unique_id);
+    picture_data->set_payload(picture.data->data(), picture.data->size());
+  }
+}
+
+std::vector<PictureData> SkPicturesProtoToPictureDataVector(
+    const SkPictures& proto_pictures) {
+  std::vector<PictureData> result;
+  for (int i = 0; i < proto_pictures.pictures_size(); ++i) {
+    SkPictureData proto_picture = proto_pictures.pictures(i);
+    DCHECK(proto_picture.has_id());
+    DCHECK(proto_picture.id().has_unique_id());
+    DCHECK(proto_picture.has_payload());
+    PictureData picture_data(
+        proto_picture.id().unique_id(),
+        SkData::MakeWithCopy(proto_picture.payload().data(),
+                             proto_picture.payload().size()));
+    result.push_back(picture_data);
+  }
+  return result;
+}
+
+}  // namespace proto
+}  // namespace cc
diff --git a/cc/blimp/picture_data_conversions.h b/cc/blimp/picture_data_conversions.h
new file mode 100644
index 0000000..6fa6d0f
--- /dev/null
+++ b/cc/blimp/picture_data_conversions.h
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BLIMP_PICTURE_DATA_CONVERSIONS_H_
+#define CC_BLIMP_PICTURE_DATA_CONVERSIONS_H_
+
+#include <memory>
+#include <vector>
+
+#include "cc/base/cc_export.h"
+#include "cc/blimp/picture_data.h"
+
+namespace cc {
+
+namespace proto {
+class SkPictures;
+
+CC_EXPORT void PictureDataVectorToSkPicturesProto(
+    const std::vector<PictureData>& cache_update,
+    SkPictures* proto_pictures);
+
+CC_EXPORT std::vector<PictureData> SkPicturesProtoToPictureDataVector(
+    const SkPictures& proto_pictures);
+
+}  // namespace proto
+}  // namespace cc
+
+#endif  // CC_BLIMP_PICTURE_DATA_CONVERSIONS_H_
diff --git a/cc/blimp/picture_data_conversions_unittest.cc b/cc/blimp/picture_data_conversions_unittest.cc
new file mode 100644
index 0000000..8c298cf
--- /dev/null
+++ b/cc/blimp/picture_data_conversions_unittest.cc
@@ -0,0 +1,75 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/blimp/picture_data_conversions.h"
+
+#include <memory>
+#include <set>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "cc/blimp/picture_data.h"
+#include "cc/proto/layer_tree_host.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkStream.h"
+
+namespace cc {
+namespace {
+sk_sp<const SkPicture> CreateSkPicture(SkColor color) {
+  SkPictureRecorder recorder;
+  sk_sp<SkCanvas> canvas =
+      sk_ref_sp(recorder.beginRecording(SkRect::MakeWH(1, 1)));
+  canvas->drawColor(color);
+  return recorder.finishRecordingAsPicture();
+}
+
+sk_sp<SkData> SerializePicture(sk_sp<const SkPicture> picture) {
+  SkDynamicMemoryWStream stream;
+  picture->serialize(&stream, nullptr);
+  DCHECK(stream.bytesWritten());
+  return sk_sp<SkData>(stream.copyToData());
+}
+
+bool SamePicture(sk_sp<const SkPicture> picture,
+                 const PictureData& picture_data) {
+  if (picture->uniqueID() != picture_data.unique_id)
+    return false;
+
+  sk_sp<const SkData> serialized_picture = SerializePicture(picture);
+  return picture_data.data->equals(serialized_picture);
+}
+
+PictureData CreatePictureData(sk_sp<const SkPicture> picture) {
+  return PictureData(picture->uniqueID(), SerializePicture(picture));
+}
+
+TEST(PictureCacheConversionsTest, SerializePictureCaheUpdate) {
+  sk_sp<const SkPicture> picture1 = CreateSkPicture(SK_ColorRED);
+  sk_sp<const SkPicture> picture2 = CreateSkPicture(SK_ColorBLUE);
+  std::vector<PictureData> update;
+  update.push_back(CreatePictureData(picture1));
+  update.push_back(CreatePictureData(picture2));
+
+  proto::SkPictures proto_pictures;
+  PictureDataVectorToSkPicturesProto(update, &proto_pictures);
+  ASSERT_EQ(2, proto_pictures.pictures_size());
+
+  std::vector<PictureData> deserialized =
+      SkPicturesProtoToPictureDataVector(proto_pictures);
+
+  ASSERT_EQ(2U, deserialized.size());
+  PictureData picture_data_1 = deserialized.at(0);
+  PictureData picture_data_2 = deserialized.at(1);
+  EXPECT_TRUE(SamePicture(picture1, picture_data_1));
+  EXPECT_TRUE(SamePicture(picture2, picture_data_2));
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/cc.gyp b/cc/cc.gyp
index bdddd8a..fe365781 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -101,6 +101,13 @@
         'base/time_util.h',
         'base/unique_notifier.cc',
         'base/unique_notifier.h',
+        'blimp/client_picture_cache.h',
+        'blimp/engine_picture_cache.h',
+        'blimp/image_serialization_processor.h',
+        'blimp/picture_data.cc',
+        'blimp/picture_data.h',
+        'blimp/picture_data_conversions.cc',
+        'blimp/picture_data_conversions.h',
         'debug/benchmark_instrumentation.cc',
         'debug/benchmark_instrumentation.h',
         'debug/debug_colors.cc',
@@ -372,7 +379,6 @@
         'proto/gfx_conversions.h',
         'proto/gpu_conversions.cc',
         'proto/gpu_conversions.h',
-        'proto/image_serialization_processor.h',
         'proto/skia_conversions.cc',
         'proto/skia_conversions.h',
         'proto/synced_property_conversions.cc',
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
index 6c6f33ae..271c2c4f 100644
--- a/cc/cc_tests.gyp
+++ b/cc/cc_tests.gyp
@@ -27,6 +27,7 @@
       'base/simple_enclosed_region_unittest.cc',
       'base/tiling_data_unittest.cc',
       'base/unique_notifier_unittest.cc',
+      'blimp/picture_data_conversions_unittest.cc',
       'debug/layer_tree_debug_state_unittest.cc',
       'debug/micro_benchmark_controller_unittest.cc',
       'debug/rendering_stats_unittest.cc',
@@ -181,8 +182,12 @@
       'test/failure_output_surface.h',
       'test/fake_channel_impl.cc',
       'test/fake_channel_impl.h',
+      'test/fake_client_picture_cache.cc',
+      'test/fake_client_picture_cache.h',
       'test/fake_content_layer_client.cc',
       'test/fake_content_layer_client.h',
+      'test/fake_engine_picture_cache.cc',
+      'test/fake_engine_picture_cache.h',
       'test/fake_external_begin_frame_source.cc',
       'test/fake_external_begin_frame_source.h',
       'test/fake_image_serialization_processor.cc',
@@ -260,6 +265,8 @@
       'test/ordered_texture_map.h',
       'test/paths.cc',
       'test/paths.h',
+      'test/picture_cache_model.cc',
+      'test/picture_cache_model.h',
       'test/pixel_comparator.cc',
       'test/pixel_comparator.h',
       'test/pixel_test.cc',
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc
index 526c7a4..7eddc38 100644
--- a/cc/layers/picture_layer.cc
+++ b/cc/layers/picture_layer.cc
@@ -179,9 +179,21 @@
   DropRecordingSourceContentIfInvalid();
 
   proto::PictureLayerProperties* picture = proto->mutable_picture();
-  recording_source_->ToProtobuf(
-      picture->mutable_recording_source(),
-      layer_tree_host()->image_serialization_processor());
+  recording_source_->ToProtobuf(picture->mutable_recording_source());
+
+  // Add all SkPicture items to the picture cache.
+  const DisplayItemList* display_list = recording_source_->GetDisplayItemList();
+  if (display_list) {
+    for (auto it = display_list->begin(); it != display_list->end(); ++it) {
+      sk_sp<const SkPicture> picture = it->GetPicture();
+      // Only DrawingDisplayItems have SkPictures.
+      if (!picture)
+        continue;
+
+      layer_tree_host()->engine_picture_cache()->MarkUsed(picture.get());
+    }
+  }
+
   RegionToProto(last_updated_invalidation_, picture->mutable_invalidation());
   picture->set_is_mask(is_mask_);
   picture->set_nearest_neighbor(nearest_neighbor_);
@@ -201,9 +213,14 @@
   if (!recording_source_)
     recording_source_.reset(new RecordingSource);
 
-  recording_source_->FromProtobuf(
-      picture.recording_source(),
-      layer_tree_host()->image_serialization_processor());
+  std::vector<uint32_t> used_engine_picture_ids;
+  recording_source_->FromProtobuf(picture.recording_source(),
+                                  layer_tree_host()->client_picture_cache(),
+                                  &used_engine_picture_ids);
+
+  // Inform picture cache about which SkPictures are now in use.
+  for (uint32_t engine_picture_id : used_engine_picture_ids)
+    layer_tree_host()->client_picture_cache()->MarkUsed(engine_picture_id);
 
   Region new_invalidation = RegionFromProto(picture.invalidation());
   last_updated_invalidation_.Swap(&new_invalidation);
diff --git a/cc/layers/picture_layer_unittest.cc b/cc/layers/picture_layer_unittest.cc
index ec0d88b..a5c7ede 100644
--- a/cc/layers/picture_layer_unittest.cc
+++ b/cc/layers/picture_layer_unittest.cc
@@ -15,6 +15,8 @@
 #include "cc/layers/picture_layer_impl.h"
 #include "cc/playback/display_item_list_settings.h"
 #include "cc/proto/layer.pb.h"
+#include "cc/test/fake_client_picture_cache.h"
+#include "cc/test/fake_engine_picture_cache.h"
 #include "cc/test/fake_image_serialization_processor.h"
 #include "cc/test/fake_layer_tree_host.h"
 #include "cc/test/fake_output_surface.h"
@@ -27,6 +29,7 @@
 #include "cc/test/test_shared_bitmap_manager.h"
 #include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/single_thread_proxy.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 
@@ -61,25 +64,31 @@
     nearest_neighbor_ = nearest_neighbor;
   }
 
-  void ValidateSerialization() {
+  void ValidateSerialization(
+      ImageSerializationProcessor* image_serialization_processor,
+      LayerTreeHost* host) {
+    std::vector<uint32_t> engine_picture_ids = GetPictureIds();
     proto::LayerProperties proto;
     LayerSpecificPropertiesToProto(&proto);
 
-    FakeLayerTreeHostClient host_client(FakeLayerTreeHostClient::DIRECT_3D);
-    TestTaskGraphRunner task_graph_runner;
-    LayerTreeSettings settings;
-    std::unique_ptr<FakeImageSerializationProcessor>
-        fake_image_serialization_processor =
-            base::WrapUnique(new FakeImageSerializationProcessor);
-    std::unique_ptr<FakeLayerTreeHost> host =
-        FakeLayerTreeHost::Create(&host_client, &task_graph_runner, settings,
-                                  CompositorMode::SINGLE_THREADED,
-                                  fake_image_serialization_processor.get());
+    FakeEnginePictureCache* engine_picture_cache =
+        static_cast<FakeEnginePictureCache*>(host->engine_picture_cache());
+    EXPECT_THAT(engine_picture_ids,
+                testing::UnorderedElementsAreArray(
+                    engine_picture_cache->GetAllUsedPictureIds()));
+
     scoped_refptr<TestSerializationPictureLayer> layer =
         TestSerializationPictureLayer::Create(recording_source_viewport_);
     host->SetRootLayer(layer);
+
     layer->FromLayerSpecificPropertiesProto(proto);
 
+    FakeClientPictureCache* client_picture_cache =
+        static_cast<FakeClientPictureCache*>(host->client_picture_cache());
+    EXPECT_THAT(engine_picture_ids,
+                testing::UnorderedElementsAreArray(
+                    client_picture_cache->GetAllUsedPictureIds()));
+
     // Validate that the PictureLayer specific fields are properly set.
     EXPECT_TRUE(recording_source()->EqualsTo(*layer->recording_source()));
     EXPECT_EQ(update_source_frame_number_, layer->update_source_frame_number_);
@@ -87,6 +96,23 @@
     EXPECT_EQ(nearest_neighbor_, layer->nearest_neighbor_);
   }
 
+  std::vector<uint32_t> GetPictureIds() {
+    std::vector<uint32_t> ids;
+    const DisplayItemList* display_list =
+        recording_source()->GetDisplayItemList();
+    if (!display_list)
+      return ids;
+
+    for (auto it = display_list->begin(); it != display_list->end(); ++it) {
+      sk_sp<const SkPicture> picture = it->GetPicture();
+      if (!picture)
+        continue;
+
+      ids.push_back(picture->uniqueID());
+    }
+    return ids;
+  }
+
  private:
   TestSerializationPictureLayer(ContentLayerClient* client,
                                 std::unique_ptr<RecordingSource> source,
@@ -113,6 +139,7 @@
       FakeLayerTreeHost::Create(&host_client, &task_graph_runner, settings,
                                 CompositorMode::SINGLE_THREADED,
                                 fake_image_serialization_processor.get());
+  host->InitializePictureCacheForTesting();
 
   gfx::Size recording_source_viewport(256, 256);
   scoped_refptr<TestSerializationPictureLayer> layer =
@@ -131,7 +158,8 @@
       gfx::Rect(recording_source_viewport));
   layer->recording_source()->SetGenerateDiscardableImagesMetadata(true);
   layer->recording_source()->Rerecord();
-  layer->ValidateSerialization();
+  layer->ValidateSerialization(fake_image_serialization_processor.get(),
+                               host.get());
 }
 
 TEST(PictureLayerTest, TestSerializationDeserialization) {
@@ -144,6 +172,8 @@
       &host_client, &task_graph_runner, LayerTreeSettings(),
       CompositorMode::SINGLE_THREADED,
       fake_image_serialization_processor.get());
+  host->InitializePictureCacheForTesting();
+
   gfx::Size recording_source_viewport(256, 256);
   scoped_refptr<TestSerializationPictureLayer> layer =
       TestSerializationPictureLayer::Create(recording_source_viewport);
@@ -156,20 +186,28 @@
       gfx::Rect(recording_source_viewport));
   layer->recording_source()->SetGenerateDiscardableImagesMetadata(true);
   layer->recording_source()->Rerecord();
-  layer->ValidateSerialization();
+  layer->ValidateSerialization(fake_image_serialization_processor.get(),
+                               host.get());
 }
 
 TEST(PictureLayerTest, TestEmptySerializationDeserialization) {
+  std::unique_ptr<FakeImageSerializationProcessor>
+      fake_image_serialization_processor =
+          base::WrapUnique(new FakeImageSerializationProcessor);
   FakeLayerTreeHostClient host_client(FakeLayerTreeHostClient::DIRECT_3D);
   TestTaskGraphRunner task_graph_runner;
-  std::unique_ptr<FakeLayerTreeHost> host =
-      FakeLayerTreeHost::Create(&host_client, &task_graph_runner);
+  std::unique_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(
+      &host_client, &task_graph_runner, LayerTreeSettings(),
+      CompositorMode::SINGLE_THREADED,
+      fake_image_serialization_processor.get());
+  host->InitializePictureCacheForTesting();
 
   gfx::Size recording_source_viewport(256, 256);
   scoped_refptr<TestSerializationPictureLayer> layer =
       TestSerializationPictureLayer::Create(recording_source_viewport);
   host->SetRootLayer(layer);
-  layer->ValidateSerialization();
+  layer->ValidateSerialization(fake_image_serialization_processor.get(),
+                               host.get());
 }
 
 TEST(PictureLayerTest, NoTilesIfEmptyBounds) {
diff --git a/cc/playback/clip_display_item.cc b/cc/playback/clip_display_item.cc
index d8fd80c..4f2840f 100644
--- a/cc/playback/clip_display_item.cc
+++ b/cc/playback/clip_display_item.cc
@@ -50,9 +50,7 @@
 
 ClipDisplayItem::~ClipDisplayItem() {}
 
-void ClipDisplayItem::ToProtobuf(
-    proto::DisplayItem* proto,
-    ImageSerializationProcessor* image_serialization_processor) const {
+void ClipDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
   proto->set_type(proto::DisplayItem::Type_Clip);
 
   proto::ClipDisplayItem* details = proto->mutable_clip_item();
@@ -122,9 +120,7 @@
 EndClipDisplayItem::~EndClipDisplayItem() {
 }
 
-void EndClipDisplayItem::ToProtobuf(
-    proto::DisplayItem* proto,
-    ImageSerializationProcessor* image_serialization_processor) const {
+void EndClipDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
   proto->set_type(proto::DisplayItem::Type_EndClip);
 }
 
diff --git a/cc/playback/clip_display_item.h b/cc/playback/clip_display_item.h
index a8cc723..72fa6bc 100644
--- a/cc/playback/clip_display_item.h
+++ b/cc/playback/clip_display_item.h
@@ -18,7 +18,6 @@
 class SkCanvas;
 
 namespace cc {
-class ImageSerializationProcessor;
 
 class CC_EXPORT ClipDisplayItem : public DisplayItem {
  public:
@@ -28,9 +27,7 @@
   explicit ClipDisplayItem(const proto::DisplayItem& proto);
   ~ClipDisplayItem() override;
 
-  void ToProtobuf(proto::DisplayItem* proto,
-                  ImageSerializationProcessor* image_serialization_processor)
-      const override;
+  void ToProtobuf(proto::DisplayItem* proto) const override;
   void Raster(SkCanvas* canvas,
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
@@ -55,9 +52,7 @@
   explicit EndClipDisplayItem(const proto::DisplayItem& proto);
   ~EndClipDisplayItem() override;
 
-  void ToProtobuf(proto::DisplayItem* proto,
-                  ImageSerializationProcessor* image_serialization_processor)
-      const override;
+  void ToProtobuf(proto::DisplayItem* proto) const override;
   void Raster(SkCanvas* canvas,
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
diff --git a/cc/playback/clip_path_display_item.cc b/cc/playback/clip_path_display_item.cc
index 2875cbc..64f3f957 100644
--- a/cc/playback/clip_path_display_item.cc
+++ b/cc/playback/clip_path_display_item.cc
@@ -14,7 +14,6 @@
 #include "third_party/skia/include/core/SkCanvas.h"
 
 namespace cc {
-class ImageSerializationProcessor;
 
 ClipPathDisplayItem::ClipPathDisplayItem(const SkPath& clip_path,
                                          SkRegion::Op clip_op,
@@ -50,9 +49,7 @@
   antialias_ = antialias;
 }
 
-void ClipPathDisplayItem::ToProtobuf(
-    proto::DisplayItem* proto,
-    ImageSerializationProcessor* image_serialization_processor) const {
+void ClipPathDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
   proto->set_type(proto::DisplayItem::Type_ClipPath);
 
   proto::ClipPathDisplayItem* details = proto->mutable_clip_path_item();
@@ -98,9 +95,7 @@
 EndClipPathDisplayItem::~EndClipPathDisplayItem() {
 }
 
-void EndClipPathDisplayItem::ToProtobuf(
-    proto::DisplayItem* proto,
-    ImageSerializationProcessor* image_serialization_processor) const {
+void EndClipPathDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
   proto->set_type(proto::DisplayItem::Type_EndClipPath);
 }
 
diff --git a/cc/playback/clip_path_display_item.h b/cc/playback/clip_path_display_item.h
index f242a65..992183a0 100644
--- a/cc/playback/clip_path_display_item.h
+++ b/cc/playback/clip_path_display_item.h
@@ -18,7 +18,6 @@
 class SkCanvas;
 
 namespace cc {
-class ImageSerializationProcessor;
 
 class CC_EXPORT ClipPathDisplayItem : public DisplayItem {
  public:
@@ -26,9 +25,7 @@
   explicit ClipPathDisplayItem(const proto::DisplayItem& proto);
   ~ClipPathDisplayItem() override;
 
-  void ToProtobuf(proto::DisplayItem* proto,
-                  ImageSerializationProcessor* image_serialization_processor)
-      const override;
+  void ToProtobuf(proto::DisplayItem* proto) const override;
   void Raster(SkCanvas* canvas,
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
@@ -55,9 +52,7 @@
     return base::WrapUnique(new EndClipPathDisplayItem());
   }
 
-  void ToProtobuf(proto::DisplayItem* proto,
-                  ImageSerializationProcessor* image_serialization_processor)
-      const override;
+  void ToProtobuf(proto::DisplayItem* proto) const override;
   void Raster(SkCanvas* canvas,
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
diff --git a/cc/playback/compositing_display_item.cc b/cc/playback/compositing_display_item.cc
index 1626933..521bcd1 100644
--- a/cc/playback/compositing_display_item.cc
+++ b/cc/playback/compositing_display_item.cc
@@ -21,7 +21,6 @@
 #include "ui/gfx/skia_util.h"
 
 namespace cc {
-class ImageSerializationProcessor;
 
 CompositingDisplayItem::CompositingDisplayItem(
     uint8_t alpha,
@@ -78,9 +77,7 @@
   lcd_text_requires_opaque_layer_ = lcd_text_requires_opaque_layer;
 }
 
-void CompositingDisplayItem::ToProtobuf(
-    proto::DisplayItem* proto,
-    ImageSerializationProcessor* image_serialization_processor) const {
+void CompositingDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
   proto->set_type(proto::DisplayItem::Type_Compositing);
 
   proto::CompositingDisplayItem* details = proto->mutable_compositing_item();
@@ -142,9 +139,7 @@
 EndCompositingDisplayItem::~EndCompositingDisplayItem() {
 }
 
-void EndCompositingDisplayItem::ToProtobuf(
-    proto::DisplayItem* proto,
-    ImageSerializationProcessor* image_serialization_processor) const {
+void EndCompositingDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
   proto->set_type(proto::DisplayItem::Type_EndCompositing);
 }
 
diff --git a/cc/playback/compositing_display_item.h b/cc/playback/compositing_display_item.h
index f9fafcf8..c56dc407 100644
--- a/cc/playback/compositing_display_item.h
+++ b/cc/playback/compositing_display_item.h
@@ -22,7 +22,6 @@
 class SkCanvas;
 
 namespace cc {
-class ImageSerializationProcessor;
 
 class CC_EXPORT CompositingDisplayItem : public DisplayItem {
  public:
@@ -34,9 +33,7 @@
   explicit CompositingDisplayItem(const proto::DisplayItem& proto);
   ~CompositingDisplayItem() override;
 
-  void ToProtobuf(proto::DisplayItem* proto,
-                  ImageSerializationProcessor* image_serialization_processor)
-      const override;
+  void ToProtobuf(proto::DisplayItem* proto) const override;
   void Raster(SkCanvas* canvas,
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
@@ -70,9 +67,7 @@
     return base::WrapUnique(new EndCompositingDisplayItem());
   }
 
-  void ToProtobuf(proto::DisplayItem* proto,
-                  ImageSerializationProcessor* image_serialization_processor)
-      const override;
+  void ToProtobuf(proto::DisplayItem* proto) const override;
   void Raster(SkCanvas* canvas,
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
diff --git a/cc/playback/display_item.cc b/cc/playback/display_item.cc
index cbfee03..015a0b61 100644
--- a/cc/playback/display_item.cc
+++ b/cc/playback/display_item.cc
@@ -9,4 +9,8 @@
 DisplayItem::DisplayItem() {
 }
 
+sk_sp<const SkPicture> DisplayItem::GetPicture() const {
+  return nullptr;
+}
+
 }  // namespace cc
diff --git a/cc/playback/display_item.h b/cc/playback/display_item.h
index 0caf11e..08d3d01 100644
--- a/cc/playback/display_item.h
+++ b/cc/playback/display_item.h
@@ -17,7 +17,6 @@
 class SkCanvas;
 
 namespace cc {
-class ImageSerializationProcessor;
 
 namespace proto {
 class DisplayItem;
@@ -27,9 +26,8 @@
  public:
   virtual ~DisplayItem() {}
 
-  virtual void ToProtobuf(
-      proto::DisplayItem* proto,
-      ImageSerializationProcessor* image_serialization_processor) const = 0;
+  virtual void ToProtobuf(proto::DisplayItem* proto) const = 0;
+  virtual sk_sp<const SkPicture> GetPicture() const;
   virtual void Raster(SkCanvas* canvas,
                       SkPicture::AbortCallback* callback) const = 0;
   virtual void AsValueInto(const gfx::Rect& visual_rect,
diff --git a/cc/playback/display_item_list.cc b/cc/playback/display_item_list.cc
index 511e5bbf..1753abd8 100644
--- a/cc/playback/display_item_list.cc
+++ b/cc/playback/display_item_list.cc
@@ -29,7 +29,6 @@
 #include "ui/gfx/skia_util.h"
 
 namespace cc {
-class ImageSerializationProcessor;
 
 namespace {
 
@@ -58,7 +57,8 @@
 
 scoped_refptr<DisplayItemList> DisplayItemList::CreateFromProto(
     const proto::DisplayItemList& proto,
-    ImageSerializationProcessor* image_serialization_processor) {
+    ClientPictureCache* client_picture_cache,
+    std::vector<uint32_t>* used_engine_picture_ids) {
   gfx::Rect layer_rect = ProtoToRect(proto.layer_rect());
   scoped_refptr<DisplayItemList> list =
       DisplayItemList::Create(ProtoToRect(proto.layer_rect()),
@@ -67,7 +67,8 @@
   for (int i = 0; i < proto.items_size(); i++) {
     const proto::DisplayItem& item_proto = proto.items(i);
     DisplayItemProtoFactory::AllocateAndConstruct(
-        layer_rect, list.get(), item_proto, image_serialization_processor);
+        layer_rect, list.get(), item_proto, client_picture_cache,
+        used_engine_picture_ids);
   }
 
   list->Finalize();
@@ -100,9 +101,7 @@
 DisplayItemList::~DisplayItemList() {
 }
 
-void DisplayItemList::ToProtobuf(
-    proto::DisplayItemList* proto,
-    ImageSerializationProcessor* image_serialization_processor) {
+void DisplayItemList::ToProtobuf(proto::DisplayItemList* proto) {
   // The flattened SkPicture approach is going away, and the proto
   // doesn't currently support serializing that flattened picture.
   DCHECK(retain_individual_display_items_);
@@ -112,7 +111,7 @@
 
   DCHECK_EQ(0, proto->items_size());
   for (const auto& item : items_)
-    item.ToProtobuf(proto->add_items(), image_serialization_processor);
+    item.ToProtobuf(proto->add_items());
 }
 
 void DisplayItemList::Raster(SkCanvas* canvas,
diff --git a/cc/playback/display_item_list.h b/cc/playback/display_item_list.h
index 02f2c7ec..710284e4 100644
--- a/cc/playback/display_item_list.h
+++ b/cc/playback/display_item_list.h
@@ -26,9 +26,9 @@
 class SkPictureRecorder;
 
 namespace cc {
+class ClientPictureCache;
 class DisplayItem;
 class DrawingDisplayItem;
-class ImageSerializationProcessor;
 
 namespace proto {
 class DisplayItemList;
@@ -51,13 +51,11 @@
   // (crbug.com/548434).
   static scoped_refptr<DisplayItemList> CreateFromProto(
       const proto::DisplayItemList& proto,
-      ImageSerializationProcessor* image_serialization_processor);
+      ClientPictureCache* client_picture_cache,
+      std::vector<uint32_t>* used_engine_picture_ids);
 
   // Creates a Protobuf representing the state of this DisplayItemList.
-  // TODO(dtrainor): Don't resend DisplayItems that were already serialized
-  // (crbug.com/548434).
-  void ToProtobuf(proto::DisplayItemList* proto,
-                  ImageSerializationProcessor* image_serialization_processor);
+  void ToProtobuf(proto::DisplayItemList* proto);
 
   // TODO(trchen): Deprecated. Apply clip and scale on the canvas instead.
   void Raster(SkCanvas* canvas,
@@ -112,6 +110,14 @@
 
   gfx::Rect VisualRectForTesting(int index) { return visual_rects_[index]; }
 
+  ContiguousContainer<DisplayItem>::const_iterator begin() const {
+    return items_.begin();
+  }
+
+  ContiguousContainer<DisplayItem>::const_iterator end() const {
+    return items_.end();
+  }
+
  private:
   DisplayItemList(gfx::Rect layer_rect,
                   const DisplayItemListSettings& display_list_settings,
diff --git a/cc/playback/display_item_list_unittest.cc b/cc/playback/display_item_list_unittest.cc
index 88ba7e8e..1bb6d3d 100644
--- a/cc/playback/display_item_list_unittest.cc
+++ b/cc/playback/display_item_list_unittest.cc
@@ -20,8 +20,11 @@
 #include "cc/playback/float_clip_display_item.h"
 #include "cc/playback/transform_display_item.h"
 #include "cc/proto/display_item.pb.h"
+#include "cc/test/fake_client_picture_cache.h"
+#include "cc/test/fake_engine_picture_cache.h"
 #include "cc/test/fake_image_serialization_processor.h"
 #include "cc/test/skia_common.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -83,15 +86,29 @@
   std::unique_ptr<FakeImageSerializationProcessor>
       fake_image_serialization_processor =
           base::WrapUnique(new FakeImageSerializationProcessor);
+  std::unique_ptr<EnginePictureCache> fake_engine_picture_cache =
+      fake_image_serialization_processor->CreateEnginePictureCache();
+  FakeEnginePictureCache* fake_engine_picture_cache_ptr =
+      static_cast<FakeEnginePictureCache*>(fake_engine_picture_cache.get());
+  std::unique_ptr<ClientPictureCache> fake_client_picture_cache =
+      fake_image_serialization_processor->CreateClientPictureCache();
+
+  fake_engine_picture_cache_ptr->MarkAllSkPicturesAsUsed(list.get());
 
   // Serialize and deserialize the DisplayItemList.
   proto::DisplayItemList proto;
-  list->ToProtobuf(&proto, fake_image_serialization_processor.get());
-  scoped_refptr<DisplayItemList> new_list = DisplayItemList::CreateFromProto(
-      proto, fake_image_serialization_processor.get());
+  list->ToProtobuf(&proto);
 
-  EXPECT_TRUE(
-      AreDisplayListDrawingResultsSame(gfx::Rect(layer_size), list, new_list));
+  std::vector<uint32_t> actual_picture_ids;
+  scoped_refptr<DisplayItemList> new_list = DisplayItemList::CreateFromProto(
+      proto, fake_client_picture_cache.get(), &actual_picture_ids);
+
+  EXPECT_THAT(actual_picture_ids,
+              testing::UnorderedElementsAreArray(
+                  fake_engine_picture_cache_ptr->GetAllUsedPictureIds()));
+
+  EXPECT_TRUE(AreDisplayListDrawingResultsSame(gfx::Rect(layer_size),
+                                               list.get(), new_list.get()));
 }
 
 }  // namespace
diff --git a/cc/playback/display_item_proto_factory.cc b/cc/playback/display_item_proto_factory.cc
index 0dedda38..75de323 100644
--- a/cc/playback/display_item_proto_factory.cc
+++ b/cc/playback/display_item_proto_factory.cc
@@ -15,14 +15,15 @@
 #include "ui/gfx/geometry/rect.h"
 
 namespace cc {
-class ImageSerializationProcessor;
+class ClientPictureCache;
 
 // static
 void DisplayItemProtoFactory::AllocateAndConstruct(
     const gfx::Rect& visual_rect,
     DisplayItemList* list,
     const proto::DisplayItem& proto,
-    ImageSerializationProcessor* image_serialization_processor) {
+    ClientPictureCache* client_picture_cache,
+    std::vector<uint32_t>* used_engine_picture_ids) {
   switch (proto.type()) {
     case proto::DisplayItem::Type_Clip:
       list->CreateAndAppendItem<ClipDisplayItem>(visual_rect, proto);
@@ -44,7 +45,7 @@
       return;
     case proto::DisplayItem::Type_Drawing:
       list->CreateAndAppendItem<DrawingDisplayItem>(
-          visual_rect, proto, image_serialization_processor);
+          visual_rect, proto, client_picture_cache, used_engine_picture_ids);
       return;
     case proto::DisplayItem::Type_Filter:
       list->CreateAndAppendItem<FilterDisplayItem>(visual_rect, proto);
diff --git a/cc/playback/display_item_proto_factory.h b/cc/playback/display_item_proto_factory.h
index f881e793..755be54 100644
--- a/cc/playback/display_item_proto_factory.h
+++ b/cc/playback/display_item_proto_factory.h
@@ -11,7 +11,7 @@
 #include "cc/playback/display_item_list.h"
 
 namespace cc {
-class ImageSerializationProcessor;
+class ClientPictureCache;
 
 namespace proto {
 class DisplayItem;
@@ -23,7 +23,8 @@
       const gfx::Rect& visual_rect,
       DisplayItemList* list,
       const proto::DisplayItem& proto,
-      ImageSerializationProcessor* image_serialization_processor);
+      ClientPictureCache* client_picture_cache,
+      std::vector<uint32_t>* used_engine_picture_ids);
 
  private:
   DisplayItemProtoFactory() {}
diff --git a/cc/playback/drawing_display_item.cc b/cc/playback/drawing_display_item.cc
index 29ceab0..2fa382e 100644
--- a/cc/playback/drawing_display_item.cc
+++ b/cc/playback/drawing_display_item.cc
@@ -12,9 +12,10 @@
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/trace_event_argument.h"
 #include "base/values.h"
+#include "cc/blimp/client_picture_cache.h"
+#include "cc/blimp/image_serialization_processor.h"
 #include "cc/debug/picture_debug_util.h"
 #include "cc/proto/display_item.pb.h"
-#include "cc/proto/image_serialization_processor.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkData.h"
 #include "third_party/skia/include/core/SkMatrix.h"
@@ -33,18 +34,21 @@
 
 DrawingDisplayItem::DrawingDisplayItem(
     const proto::DisplayItem& proto,
-    ImageSerializationProcessor* image_serialization_processor) {
+    ClientPictureCache* client_picture_cache,
+    std::vector<uint32_t>* used_engine_picture_ids) {
   DCHECK_EQ(proto::DisplayItem::Type_Drawing, proto.type());
+  DCHECK(client_picture_cache);
 
-  sk_sp<SkPicture> picture;
   const proto::DrawingDisplayItem& details = proto.drawing_item();
-  if (details.has_picture()) {
-    SkMemoryStream stream(details.picture().data(), details.picture().size());
+  DCHECK(details.has_id());
+  const proto::SkPictureID& sk_picture_id = details.id();
+  DCHECK(sk_picture_id.has_unique_id());
 
-    picture = SkPicture::MakeFromStream(
-        &stream, image_serialization_processor->GetPixelDeserializer());
-  }
+  uint32_t unique_id = sk_picture_id.unique_id();
+  sk_sp<const SkPicture> picture = client_picture_cache->GetPicture(unique_id);
+  DCHECK(picture);
 
+  used_engine_picture_ids->push_back(unique_id);
   SetNew(std::move(picture));
 }
 
@@ -59,26 +63,19 @@
   picture_ = std::move(picture);
 }
 
-void DrawingDisplayItem::ToProtobuf(
-    proto::DisplayItem* proto,
-    ImageSerializationProcessor* image_serialization_processor) const {
+void DrawingDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
   TRACE_EVENT0("cc.remote", "DrawingDisplayItem::ToProtobuf");
   proto->set_type(proto::DisplayItem::Type_Drawing);
 
-  proto::DrawingDisplayItem* details = proto->mutable_drawing_item();
+  if (!picture_)
+    return;
 
-  // Just use skia's serialize() method for now.
-  if (picture_) {
-    TRACE_EVENT0("cc.remote",
-                 "DrawingDisplayItem::ToProtobuf SkPicture::Serialize");
-    SkDynamicMemoryWStream stream;
-    picture_->serialize(&stream,
-                        image_serialization_processor->GetPixelSerializer());
-    if (stream.bytesWritten() > 0) {
-      SkAutoDataUnref data(stream.copyToData());
-      details->set_picture(data->data(), data->size());
-    }
-  }
+  proto->mutable_drawing_item()->mutable_id()->set_unique_id(
+      picture_->uniqueID());
+}
+
+sk_sp<const SkPicture> DrawingDisplayItem::GetPicture() const {
+  return picture_;
 }
 
 void DrawingDisplayItem::Raster(SkCanvas* canvas,
diff --git a/cc/playback/drawing_display_item.h b/cc/playback/drawing_display_item.h
index 8d06928..0bad775 100644
--- a/cc/playback/drawing_display_item.h
+++ b/cc/playback/drawing_display_item.h
@@ -8,6 +8,7 @@
 #include <stddef.h>
 
 #include <memory>
+#include <vector>
 
 #include "cc/base/cc_export.h"
 #include "cc/playback/display_item.h"
@@ -18,21 +19,20 @@
 class SkPicture;
 
 namespace cc {
-class ImageSerializationProcessor;
+class ClientPictureCache;
 
 class CC_EXPORT DrawingDisplayItem : public DisplayItem {
  public:
   DrawingDisplayItem();
   explicit DrawingDisplayItem(sk_sp<const SkPicture> picture);
-  explicit DrawingDisplayItem(
-      const proto::DisplayItem& proto,
-      ImageSerializationProcessor* image_serialization_processor);
+  explicit DrawingDisplayItem(const proto::DisplayItem& proto,
+                              ClientPictureCache* client_picture_cache,
+                              std::vector<uint32_t>* used_engine_picture_ids);
   explicit DrawingDisplayItem(const DrawingDisplayItem& item);
   ~DrawingDisplayItem() override;
 
-  void ToProtobuf(proto::DisplayItem* proto,
-                  ImageSerializationProcessor* image_serialization_processor)
-      const override;
+  void ToProtobuf(proto::DisplayItem* proto) const override;
+  sk_sp<const SkPicture> GetPicture() const override;
   void Raster(SkCanvas* canvas,
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
diff --git a/cc/playback/filter_display_item.cc b/cc/playback/filter_display_item.cc
index 116685ba..224d1c8 100644
--- a/cc/playback/filter_display_item.cc
+++ b/cc/playback/filter_display_item.cc
@@ -19,7 +19,6 @@
 #include "ui/gfx/skia_util.h"
 
 namespace cc {
-class ImageSerializationProcessor;
 
 FilterDisplayItem::FilterDisplayItem(const FilterOperations& filters,
                                      const gfx::RectF& bounds) {
@@ -46,9 +45,7 @@
   bounds_ = bounds;
 }
 
-void FilterDisplayItem::ToProtobuf(
-    proto::DisplayItem* proto,
-    ImageSerializationProcessor* image_serialization_processor) const {
+void FilterDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
   proto->set_type(proto::DisplayItem::Type_Filter);
 
   proto::FilterDisplayItem* details = proto->mutable_filter_item();
@@ -96,9 +93,7 @@
 
 EndFilterDisplayItem::~EndFilterDisplayItem() {}
 
-void EndFilterDisplayItem::ToProtobuf(
-    proto::DisplayItem* proto,
-    ImageSerializationProcessor* image_serialization_processor) const {
+void EndFilterDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
   proto->set_type(proto::DisplayItem::Type_EndFilter);
 }
 
diff --git a/cc/playback/filter_display_item.h b/cc/playback/filter_display_item.h
index 6cc13cd..887f13d 100644
--- a/cc/playback/filter_display_item.h
+++ b/cc/playback/filter_display_item.h
@@ -18,7 +18,6 @@
 class SkCanvas;
 
 namespace cc {
-class ImageSerializationProcessor;
 
 class CC_EXPORT FilterDisplayItem : public DisplayItem {
  public:
@@ -26,9 +25,7 @@
   explicit FilterDisplayItem(const proto::DisplayItem& proto);
   ~FilterDisplayItem() override;
 
-  void ToProtobuf(proto::DisplayItem* proto,
-                  ImageSerializationProcessor* image_serialization_processor)
-      const override;
+  void ToProtobuf(proto::DisplayItem* proto) const override;
   void Raster(SkCanvas* canvas,
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
@@ -54,9 +51,7 @@
     return base::WrapUnique(new EndFilterDisplayItem());
   }
 
-  void ToProtobuf(proto::DisplayItem* proto,
-                  ImageSerializationProcessor* image_serialization_processor)
-      const override;
+  void ToProtobuf(proto::DisplayItem* proto) const override;
   void Raster(SkCanvas* canvas,
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
diff --git a/cc/playback/float_clip_display_item.cc b/cc/playback/float_clip_display_item.cc
index 100bbda..cdf16a04 100644
--- a/cc/playback/float_clip_display_item.cc
+++ b/cc/playback/float_clip_display_item.cc
@@ -14,7 +14,6 @@
 #include "ui/gfx/skia_util.h"
 
 namespace cc {
-class ImageSerializationProcessor;
 
 FloatClipDisplayItem::FloatClipDisplayItem(const gfx::RectF& clip_rect) {
   SetNew(clip_rect);
@@ -36,9 +35,7 @@
   clip_rect_ = clip_rect;
 }
 
-void FloatClipDisplayItem::ToProtobuf(
-    proto::DisplayItem* proto,
-    ImageSerializationProcessor* image_serialization_processor) const {
+void FloatClipDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
   proto->set_type(proto::DisplayItem::Type_FloatClip);
 
   proto::FloatClipDisplayItem* details = proto->mutable_float_clip_item();
@@ -73,9 +70,7 @@
 EndFloatClipDisplayItem::~EndFloatClipDisplayItem() {
 }
 
-void EndFloatClipDisplayItem::ToProtobuf(
-    proto::DisplayItem* proto,
-    ImageSerializationProcessor* image_serialization_processor) const {
+void EndFloatClipDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
   proto->set_type(proto::DisplayItem::Type_EndFloatClip);
 }
 
diff --git a/cc/playback/float_clip_display_item.h b/cc/playback/float_clip_display_item.h
index c36bd76..2015e7f 100644
--- a/cc/playback/float_clip_display_item.h
+++ b/cc/playback/float_clip_display_item.h
@@ -18,7 +18,6 @@
 class SkCanvas;
 
 namespace cc {
-class ImageSerializationProcessor;
 
 class CC_EXPORT FloatClipDisplayItem : public DisplayItem {
  public:
@@ -26,9 +25,7 @@
   explicit FloatClipDisplayItem(const proto::DisplayItem& proto);
   ~FloatClipDisplayItem() override;
 
-  void ToProtobuf(proto::DisplayItem* proto,
-                  ImageSerializationProcessor* image_serialization_processor)
-      const override;
+  void ToProtobuf(proto::DisplayItem* proto) const override;
   void Raster(SkCanvas* canvas,
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
@@ -53,9 +50,7 @@
     return base::WrapUnique(new EndFloatClipDisplayItem());
   }
 
-  void ToProtobuf(proto::DisplayItem* proto,
-                  ImageSerializationProcessor* image_serialization_processor)
-      const override;
+  void ToProtobuf(proto::DisplayItem* proto) const override;
   void Raster(SkCanvas* canvas,
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
diff --git a/cc/playback/recording_source.cc b/cc/playback/recording_source.cc
index feaae88..9806e9e 100644
--- a/cc/playback/recording_source.cc
+++ b/cc/playback/recording_source.cc
@@ -28,7 +28,6 @@
 }  // namespace
 
 namespace cc {
-class ImageSerializationProcessor;
 
 RecordingSource::RecordingSource()
     : slow_down_raster_scale_factor_for_debug_(0),
@@ -42,9 +41,7 @@
 
 RecordingSource::~RecordingSource() {}
 
-void RecordingSource::ToProtobuf(
-    proto::RecordingSource* proto,
-    ImageSerializationProcessor* image_serialization_processor) const {
+void RecordingSource::ToProtobuf(proto::RecordingSource* proto) const {
   RectToProto(recorded_viewport_, proto->mutable_recorded_viewport());
   SizeToProto(size_, proto->mutable_size());
   proto->set_slow_down_raster_scale_factor_for_debug(
@@ -56,15 +53,15 @@
   proto->set_clear_canvas_with_debug_color(clear_canvas_with_debug_color_);
   proto->set_solid_color(static_cast<uint64_t>(solid_color_));
   proto->set_background_color(static_cast<uint64_t>(background_color_));
-  if (display_list_) {
-    display_list_->ToProtobuf(proto->mutable_display_list(),
-                              image_serialization_processor);
-  }
+  if (display_list_)
+    display_list_->ToProtobuf(proto->mutable_display_list());
 }
 
 void RecordingSource::FromProtobuf(
     const proto::RecordingSource& proto,
-    ImageSerializationProcessor* image_serialization_processor) {
+    ClientPictureCache* client_picture_cache,
+    std::vector<uint32_t>* used_engine_picture_ids) {
+  DCHECK(client_picture_cache);
   recorded_viewport_ = ProtoToRect(proto.recorded_viewport());
   size_ = ProtoToSize(proto.size());
   slow_down_raster_scale_factor_for_debug_ =
@@ -82,7 +79,7 @@
   // called.
   if (proto.has_display_list()) {
     display_list_ = DisplayItemList::CreateFromProto(
-        proto.display_list(), image_serialization_processor);
+        proto.display_list(), client_picture_cache, used_engine_picture_ids);
     FinishDisplayItemListUpdate();
   } else {
     display_list_ = nullptr;
@@ -214,6 +211,10 @@
   return !display_list_ || display_list_->IsSuitableForGpuRasterization();
 }
 
+const DisplayItemList* RecordingSource::GetDisplayItemList() {
+  return display_list_.get();
+}
+
 scoped_refptr<RasterSource> RecordingSource::CreateRasterSource(
     bool can_use_lcd_text) const {
   return scoped_refptr<RasterSource>(
diff --git a/cc/playback/recording_source.h b/cc/playback/recording_source.h
index 9e5aa7e..36d0511 100644
--- a/cc/playback/recording_source.h
+++ b/cc/playback/recording_source.h
@@ -23,10 +23,10 @@
 class RecordingSource;
 }  // namespace proto
 
+class ClientPictureCache;
 class ContentLayerClient;
 class DisplayItemList;
 class RasterSource;
-class ImageSerializationProcessor;
 class Region;
 
 class CC_EXPORT RecordingSource {
@@ -46,11 +46,10 @@
   RecordingSource();
   virtual ~RecordingSource();
 
-  void ToProtobuf(
-      proto::RecordingSource* proto,
-      ImageSerializationProcessor* image_serialization_processor) const;
+  void ToProtobuf(proto::RecordingSource* proto) const;
   void FromProtobuf(const proto::RecordingSource& proto,
-                    ImageSerializationProcessor* image_serialization_processor);
+                    ClientPictureCache* client_picture_cache,
+                    std::vector<uint32_t>* used_engine_picture_ids);
 
   bool UpdateAndExpandInvalidation(ContentLayerClient* painter,
                                    Region* invalidation,
@@ -73,6 +72,8 @@
 
   gfx::Rect recorded_viewport() const { return recorded_viewport_; }
 
+  const DisplayItemList* GetDisplayItemList();
+
  protected:
   void Clear();
 
diff --git a/cc/playback/recording_source_unittest.cc b/cc/playback/recording_source_unittest.cc
index 878242d..7b83a6d 100644
--- a/cc/playback/recording_source_unittest.cc
+++ b/cc/playback/recording_source_unittest.cc
@@ -8,10 +8,13 @@
 #include "cc/base/region.h"
 #include "cc/playback/raster_source.h"
 #include "cc/proto/recording_source.pb.h"
+#include "cc/test/fake_client_picture_cache.h"
 #include "cc/test/fake_content_layer_client.h"
+#include "cc/test/fake_engine_picture_cache.h"
 #include "cc/test/fake_image_serialization_processor.h"
 #include "cc/test/fake_recording_source.h"
 #include "cc/test/skia_common.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 
@@ -37,12 +40,27 @@
   std::unique_ptr<FakeImageSerializationProcessor>
       fake_image_serialization_processor =
           base::WrapUnique(new FakeImageSerializationProcessor);
+  std::unique_ptr<EnginePictureCache> fake_engine_picture_cache =
+      fake_image_serialization_processor->CreateEnginePictureCache();
+  FakeEnginePictureCache* fake_engine_picture_cache_ptr =
+      static_cast<FakeEnginePictureCache*>(fake_engine_picture_cache.get());
+  std::unique_ptr<ClientPictureCache> fake_client_picture_cache =
+      fake_image_serialization_processor->CreateClientPictureCache();
+
+  fake_engine_picture_cache_ptr->MarkAllSkPicturesAsUsed(
+      source->GetDisplayItemList());
 
   proto::RecordingSource proto;
-  source->ToProtobuf(&proto, fake_image_serialization_processor.get());
+  source->ToProtobuf(&proto);
 
+  std::vector<uint32_t> actual_picture_ids;
   FakeRecordingSource new_source;
-  new_source.FromProtobuf(proto, fake_image_serialization_processor.get());
+  new_source.FromProtobuf(proto, fake_client_picture_cache.get(),
+                          &actual_picture_ids);
+
+  EXPECT_THAT(actual_picture_ids,
+              testing::UnorderedElementsAreArray(
+                  fake_engine_picture_cache_ptr->GetAllUsedPictureIds()));
 
   EXPECT_TRUE(source->EqualsTo(new_source));
 }
diff --git a/cc/playback/transform_display_item.cc b/cc/playback/transform_display_item.cc
index 3faf575..ef28136 100644
--- a/cc/playback/transform_display_item.cc
+++ b/cc/playback/transform_display_item.cc
@@ -13,7 +13,6 @@
 #include "third_party/skia/include/core/SkCanvas.h"
 
 namespace cc {
-class ImageSerializationProcessor;
 
 TransformDisplayItem::TransformDisplayItem(const gfx::Transform& transform)
     : transform_(gfx::Transform::kSkipInitialization) {
@@ -36,9 +35,7 @@
   transform_ = transform;
 }
 
-void TransformDisplayItem::ToProtobuf(
-    proto::DisplayItem* proto,
-    ImageSerializationProcessor* image_serialization_processor) const {
+void TransformDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
   proto->set_type(proto::DisplayItem::Type_Transform);
 
   proto::TransformDisplayItem* details = proto->mutable_transform_item();
@@ -74,9 +71,7 @@
 EndTransformDisplayItem::~EndTransformDisplayItem() {
 }
 
-void EndTransformDisplayItem::ToProtobuf(
-    proto::DisplayItem* proto,
-    ImageSerializationProcessor* image_serialization_processor) const {
+void EndTransformDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
   proto->set_type(proto::DisplayItem::Type_EndTransform);
 }
 
diff --git a/cc/playback/transform_display_item.h b/cc/playback/transform_display_item.h
index c547332..661c5fd 100644
--- a/cc/playback/transform_display_item.h
+++ b/cc/playback/transform_display_item.h
@@ -17,7 +17,6 @@
 class SkCanvas;
 
 namespace cc {
-class ImageSerializationProcessor;
 
 class CC_EXPORT TransformDisplayItem : public DisplayItem {
  public:
@@ -25,9 +24,7 @@
   explicit TransformDisplayItem(const proto::DisplayItem& proto);
   ~TransformDisplayItem() override;
 
-  void ToProtobuf(proto::DisplayItem* proto,
-                  ImageSerializationProcessor* image_serialization_processor)
-      const override;
+  void ToProtobuf(proto::DisplayItem* proto) const override;
   void Raster(SkCanvas* canvas,
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
@@ -52,9 +49,7 @@
     return base::WrapUnique(new EndTransformDisplayItem());
   }
 
-  void ToProtobuf(proto::DisplayItem* proto,
-                  ImageSerializationProcessor* image_serialization_processor)
-      const override;
+  void ToProtobuf(proto::DisplayItem* proto) const override;
   void Raster(SkCanvas* canvas,
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
diff --git a/cc/proto/display_item.proto b/cc/proto/display_item.proto
index a8c8031..05cc060 100644
--- a/cc/proto/display_item.proto
+++ b/cc/proto/display_item.proto
@@ -74,8 +74,12 @@
   optional bool lcd_text_requires_opaque_layer = 5;
 }
 
+message SkPictureID {
+  optional uint32 unique_id = 1;
+}
+
 message DrawingDisplayItem {
-  optional bytes picture = 1; /* SkPicture */
+  optional SkPictureID id = 1;
 }
 
 message FilterDisplayItem {
diff --git a/cc/proto/image_serialization_processor.h b/cc/proto/image_serialization_processor.h
deleted file mode 100644
index 2827eb4..0000000
--- a/cc/proto/image_serialization_processor.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CC_PROTO_IMAGE_SERIALIZATION_PROCESSOR_H_
-#define CC_PROTO_IMAGE_SERIALIZATION_PROCESSOR_H_
-
-#include "third_party/skia/include/core/SkPicture.h"
-
-class SkPixelSerializer;
-
-namespace cc {
-
-// ImageSerializationProcessor provides functionality to serialize and
-// deserialize Skia images.
-class ImageSerializationProcessor {
- public:
-  // The serializer returned from this function can be used to pass in to
-  // SkPicture::serialize(...) for serializing the SkPicture to a stream.
-  virtual SkPixelSerializer* GetPixelSerializer() = 0;
-
-  // Returns a function pointer valid to use for deserializing images when using
-  // SkPicture::CreateFromStream to create an SkPicture from a stream.
-  virtual SkPicture::InstallPixelRefProc GetPixelDeserializer() = 0;
-};
-
-}  // namespace cc
-
-#endif  // CC_PROTO_IMAGE_SERIALIZATION_PROCESSOR_H_
diff --git a/cc/proto/layer_tree_host.proto b/cc/proto/layer_tree_host.proto
index 5061633..4d89190 100644
--- a/cc/proto/layer_tree_host.proto
+++ b/cc/proto/layer_tree_host.proto
@@ -4,6 +4,7 @@
 
 syntax = "proto2";
 
+import "display_item.proto";
 import "layer.proto";
 import "layer_selection_bound.proto";
 import "layer_tree_debug_state.proto";
@@ -15,6 +16,15 @@
 
 option optimize_for = LITE_RUNTIME;
 
+message SkPictureData {
+  optional SkPictureID id = 1;
+  optional bytes payload = 2; /* SkData */
+}
+
+message SkPictures {
+  repeated SkPictureData pictures = 1;
+}
+
 message LayerTreeHost {
   // Not all members of LayerTreeHost are serialized, as they are not helpful
   // for remote usage. See implementation of
@@ -57,4 +67,5 @@
   optional uint32 touch_start_or_move_event_listener_properties = 36;
   repeated int32 layers_that_should_push_properties = 37;
   optional uint32 touch_end_or_cancel_event_listener_properties = 38;
+  optional SkPictures pictures = 39;
 }
diff --git a/cc/test/fake_client_picture_cache.cc b/cc/test/fake_client_picture_cache.cc
new file mode 100644
index 0000000..cf70574
--- /dev/null
+++ b/cc/test/fake_client_picture_cache.cc
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/test/fake_client_picture_cache.h"
+
+#include "cc/test/picture_cache_model.h"
+#include "third_party/skia/include/core/SkPicture.h"
+
+namespace cc {
+
+FakeClientPictureCache::FakeClientPictureCache(PictureCacheModel* model)
+    : model_(model) {}
+
+FakeClientPictureCache::~FakeClientPictureCache() = default;
+
+const std::vector<uint32_t>& FakeClientPictureCache::GetAllUsedPictureIds() {
+  return used_picture_ids_;
+}
+
+void FakeClientPictureCache::MarkUsed(uint32_t engine_picture_id) {
+  used_picture_ids_.push_back(engine_picture_id);
+}
+
+sk_sp<const SkPicture> FakeClientPictureCache::GetPicture(uint32_t unique_id) {
+  if (!model_)
+    return nullptr;
+
+  return model_->GetPicture(unique_id);
+}
+
+void FakeClientPictureCache::ApplyCacheUpdate(
+    const std::vector<PictureData>& cache_update) {}
+
+void FakeClientPictureCache::Flush() {}
+
+}  // namespace cc
diff --git a/cc/test/fake_client_picture_cache.h b/cc/test/fake_client_picture_cache.h
new file mode 100644
index 0000000..4c7d571
--- /dev/null
+++ b/cc/test/fake_client_picture_cache.h
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_TEST_FAKE_CLIENT_PICTURE_CACHE_H_
+#define CC_TEST_FAKE_CLIENT_PICTURE_CACHE_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+
+#include "base/macros.h"
+#include "cc/blimp/client_picture_cache.h"
+
+namespace cc {
+class PictureCacheModel;
+
+class FakeClientPictureCache : public ClientPictureCache {
+ public:
+  explicit FakeClientPictureCache(PictureCacheModel* model);
+  ~FakeClientPictureCache() override;
+
+  const std::vector<uint32_t>& GetAllUsedPictureIds();
+
+  // ClientPictureCache implementation.
+  void MarkUsed(uint32_t engine_picture_id) override;
+  sk_sp<const SkPicture> GetPicture(uint32_t unique_id) override;
+  void ApplyCacheUpdate(const std::vector<PictureData>& cache_update) override;
+  void Flush() override;
+
+ private:
+  PictureCacheModel* model_;
+  std::vector<uint32_t> used_picture_ids_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeClientPictureCache);
+};
+
+}  // namespace cc
+
+#endif  // CC_TEST_FAKE_CLIENT_PICTURE_CACHE_H_
diff --git a/cc/test/fake_engine_picture_cache.cc b/cc/test/fake_engine_picture_cache.cc
new file mode 100644
index 0000000..2c7b839
--- /dev/null
+++ b/cc/test/fake_engine_picture_cache.cc
@@ -0,0 +1,51 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/test/fake_engine_picture_cache.h"
+
+#include <map>
+#include <memory>
+
+#include "cc/blimp/picture_data.h"
+#include "cc/playback/display_item_list.h"
+#include "cc/test/picture_cache_model.h"
+#include "third_party/skia/include/core/SkPicture.h"
+
+namespace cc {
+
+FakeEnginePictureCache::FakeEnginePictureCache(PictureCacheModel* model)
+    : model_(model) {}
+
+FakeEnginePictureCache::~FakeEnginePictureCache() {}
+
+void FakeEnginePictureCache::MarkAllSkPicturesAsUsed(
+    const DisplayItemList* display_list) {
+  if (!display_list)
+    return;
+
+  for (auto it = display_list->begin(); it != display_list->end(); ++it) {
+    sk_sp<const SkPicture> picture = it->GetPicture();
+    if (!picture)
+      continue;
+
+    MarkUsed(picture.get());
+  }
+}
+
+const std::vector<uint32_t>& FakeEnginePictureCache::GetAllUsedPictureIds() {
+  return used_picture_ids_;
+}
+
+void FakeEnginePictureCache::MarkUsed(const SkPicture* picture) {
+  if (model_)
+    model_->AddPicture(picture);
+  used_picture_ids_.push_back(picture->uniqueID());
+}
+
+std::vector<PictureData>
+FakeEnginePictureCache::CalculateCacheUpdateAndFlush() {
+  return std::vector<PictureData>();
+}
+
+}  // namespace cc
diff --git a/cc/test/fake_engine_picture_cache.h b/cc/test/fake_engine_picture_cache.h
new file mode 100644
index 0000000..4510419
--- /dev/null
+++ b/cc/test/fake_engine_picture_cache.h
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_TEST_FAKE_ENGINE_PICTURE_CACHE_H_
+#define CC_TEST_FAKE_ENGINE_PICTURE_CACHE_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "cc/blimp/engine_picture_cache.h"
+
+class SkPicture;
+
+namespace cc {
+class DisplayItemList;
+class PictureCacheModel;
+struct PictureData;
+
+class FakeEnginePictureCache : public EnginePictureCache {
+ public:
+  explicit FakeEnginePictureCache(PictureCacheModel* model);
+  ~FakeEnginePictureCache() override;
+
+  void MarkAllSkPicturesAsUsed(const DisplayItemList* display_list);
+  const std::vector<uint32_t>& GetAllUsedPictureIds();
+
+  // EnginePictureCache implementation.
+  std::vector<PictureData> CalculateCacheUpdateAndFlush() override;
+  void MarkUsed(const SkPicture* picture) override;
+
+ private:
+  PictureCacheModel* model_;
+  std::vector<uint32_t> used_picture_ids_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeEnginePictureCache);
+};
+
+}  // namespace cc
+
+#endif  // CC_TEST_FAKE_ENGINE_PICTURE_CACHE_H_
diff --git a/cc/test/fake_image_serialization_processor.cc b/cc/test/fake_image_serialization_processor.cc
index c43b3e8..696c0dc 100644
--- a/cc/test/fake_image_serialization_processor.cc
+++ b/cc/test/fake_image_serialization_processor.cc
@@ -4,29 +4,29 @@
 
 #include "cc/test/fake_image_serialization_processor.h"
 
-#include "third_party/skia/include/core/SkPicture.h"
-
-namespace {
-bool NoopDecoder(const void* input, size_t input_size, SkBitmap* bitmap) {
-  return false;
-}
-}
-
-class SkPixelSerializer;
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "cc/test/fake_client_picture_cache.h"
+#include "cc/test/fake_engine_picture_cache.h"
+#include "cc/test/picture_cache_model.h"
 
 namespace cc {
 
-FakeImageSerializationProcessor::FakeImageSerializationProcessor() {}
+FakeImageSerializationProcessor::FakeImageSerializationProcessor()
+    : picture_cache_model_(base::WrapUnique(new PictureCacheModel)) {}
 
 FakeImageSerializationProcessor::~FakeImageSerializationProcessor() {}
 
-SkPixelSerializer* FakeImageSerializationProcessor::GetPixelSerializer() {
-  return nullptr;
+std::unique_ptr<EnginePictureCache>
+FakeImageSerializationProcessor::CreateEnginePictureCache() {
+  return base::WrapUnique(
+      new FakeEnginePictureCache(picture_cache_model_.get()));
 }
 
-SkPicture::InstallPixelRefProc
-FakeImageSerializationProcessor::GetPixelDeserializer() {
-  return &NoopDecoder;
+std::unique_ptr<ClientPictureCache>
+FakeImageSerializationProcessor::CreateClientPictureCache() {
+  return base::WrapUnique(
+      new FakeClientPictureCache(picture_cache_model_.get()));
 }
 
 }  // namespace cc
diff --git a/cc/test/fake_image_serialization_processor.h b/cc/test/fake_image_serialization_processor.h
index 613e93b1..308fd49 100644
--- a/cc/test/fake_image_serialization_processor.h
+++ b/cc/test/fake_image_serialization_processor.h
@@ -5,23 +5,29 @@
 #ifndef CC_TEST_FAKE_IMAGE_SERIALIZATION_PROCESSOR_H_
 #define CC_TEST_FAKE_IMAGE_SERIALIZATION_PROCESSOR_H_
 
-#include "base/macros.h"
-#include "cc/proto/image_serialization_processor.h"
+#include <memory>
 
-class SkPixelSerializer;
+#include "base/macros.h"
+#include "cc/blimp/client_picture_cache.h"
+#include "cc/blimp/engine_picture_cache.h"
+#include "cc/blimp/image_serialization_processor.h"
+#include "cc/test/picture_cache_model.h"
 
 namespace cc {
 
 class FakeImageSerializationProcessor : public ImageSerializationProcessor {
  public:
   FakeImageSerializationProcessor();
-  ~FakeImageSerializationProcessor();
+  ~FakeImageSerializationProcessor() override;
 
   // ImageSerializationProcessor implementation.
-  SkPixelSerializer* GetPixelSerializer() override;
-  SkPicture::InstallPixelRefProc GetPixelDeserializer() override;
+  std::unique_ptr<EnginePictureCache> CreateEnginePictureCache() override;
+  std::unique_ptr<ClientPictureCache> CreateClientPictureCache() override;
 
  private:
+  // A PictureCacheModel shared between both the engine and client cache.
+  std::unique_ptr<PictureCacheModel> picture_cache_model_;
+
   DISALLOW_COPY_AND_ASSIGN(FakeImageSerializationProcessor);
 };
 
diff --git a/cc/test/fake_layer_tree_host.h b/cc/test/fake_layer_tree_host.h
index 0255078..86ffd28 100644
--- a/cc/test/fake_layer_tree_host.h
+++ b/cc/test/fake_layer_tree_host.h
@@ -66,6 +66,7 @@
   using LayerTreeHost::SetOutputSurfaceLostForTesting;
   using LayerTreeHost::InitializeSingleThreaded;
   using LayerTreeHost::InitializeForTesting;
+  using LayerTreeHost::InitializePictureCacheForTesting;
   using LayerTreeHost::RecordGpuRasterizationHistogram;
 
   void UpdateLayers() { LayerTreeHost::UpdateLayers(); }
diff --git a/cc/test/fake_recording_source.cc b/cc/test/fake_recording_source.cc
index 45057f3..01e4fd0 100644
--- a/cc/test/fake_recording_source.cc
+++ b/cc/test/fake_recording_source.cc
@@ -31,7 +31,7 @@
   bool display_lists_equal = !display_list_ && !other.display_list_;
   if (display_list_ && other.display_list_) {
     display_lists_equal = AreDisplayListDrawingResultsSame(
-        recorded_viewport_, display_list_, other.display_list_);
+        recorded_viewport_, display_list_.get(), other.display_list_.get());
   }
 
   return recorded_viewport_ == other.recorded_viewport_ &&
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index 789a9c1..eaacb72 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -15,6 +15,7 @@
 #include "cc/animation/element_animations.h"
 #include "cc/animation/timing_function.h"
 #include "cc/base/switches.h"
+#include "cc/blimp/image_serialization_processor.h"
 #include "cc/input/input_handler.h"
 #include "cc/layers/layer.h"
 #include "cc/layers/layer_impl.h"
@@ -22,6 +23,7 @@
 #include "cc/test/animation_test_common.h"
 #include "cc/test/begin_frame_args_test.h"
 #include "cc/test/fake_external_begin_frame_source.h"
+#include "cc/test/fake_image_serialization_processor.h"
 #include "cc/test/fake_layer_tree_host_client.h"
 #include "cc/test/fake_output_surface.h"
 #include "cc/test/remote_channel_impl_for_test.h"
@@ -417,13 +419,16 @@
       const LayerTreeSettings& settings,
       scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner,
-      std::unique_ptr<BeginFrameSource> external_begin_frame_source) {
+      std::unique_ptr<BeginFrameSource> external_begin_frame_source,
+      ImageSerializationProcessor* image_serialization_processor) {
     LayerTreeHost::InitParams params;
     params.client = client;
     params.shared_bitmap_manager = shared_bitmap_manager;
     params.gpu_memory_buffer_manager = gpu_memory_buffer_manager;
     params.task_graph_runner = task_graph_runner;
     params.settings = &settings;
+    params.image_serialization_processor = image_serialization_processor;
+
     params.animation_host =
         AnimationHost::CreateForTesting(ThreadInstance::MAIN);
     std::unique_ptr<LayerTreeHostForTesting> layer_tree_host(
@@ -504,6 +509,8 @@
     : output_surface_(nullptr),
       external_begin_frame_source_(nullptr),
       remote_proto_channel_bridge_(this),
+      image_serialization_processor_(
+          base::WrapUnique(new FakeImageSerializationProcessor)),
       beginning_(false),
       end_when_begin_returns_(false),
       timed_out_(false),
@@ -709,7 +716,8 @@
         this, mode_, client_.get(), &remote_proto_channel_bridge_.channel_main,
         nullptr, nullptr, task_graph_runner_.get(), settings_,
         base::ThreadTaskRunnerHandle::Get(), nullptr,
-        std::move(external_begin_frame_source));
+        std::move(external_begin_frame_source),
+        image_serialization_processor_.get());
     DCHECK(remote_proto_channel_bridge_.channel_main.HasReceiver());
   } else {
     layer_tree_host_ = LayerTreeHostForTesting::Create(
@@ -717,7 +725,8 @@
         gpu_memory_buffer_manager_.get(), task_graph_runner_.get(), settings_,
         base::ThreadTaskRunnerHandle::Get(),
         impl_thread_ ? impl_thread_->task_runner() : NULL,
-        std::move(external_begin_frame_source));
+        std::move(external_begin_frame_source),
+        image_serialization_processor_.get());
   }
 
   ASSERT_TRUE(layer_tree_host_);
@@ -964,8 +973,8 @@
   remote_client_layer_tree_host_ = LayerTreeHostForTesting::Create(
       this, mode_, client_.get(), &remote_proto_channel_bridge_.channel_impl,
       nullptr, nullptr, task_graph_runner_.get(), settings,
-      base::ThreadTaskRunnerHandle::Get(), impl_thread_->task_runner(),
-      nullptr);
+      base::ThreadTaskRunnerHandle::Get(), impl_thread_->task_runner(), nullptr,
+      image_serialization_processor_.get());
 
   DCHECK(remote_proto_channel_bridge_.channel_impl.HasReceiver());
   DCHECK(task_runner_provider()->HasImplThread());
diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h
index 5d05539..6ed2e35 100644
--- a/cc/test/layer_tree_test.h
+++ b/cc/test/layer_tree_test.h
@@ -200,6 +200,8 @@
   FakeExternalBeginFrameSource* external_begin_frame_source_;
   RemoteProtoChannelBridge remote_proto_channel_bridge_;
 
+  std::unique_ptr<ImageSerializationProcessor> image_serialization_processor_;
+
   bool beginning_;
   bool end_when_begin_returns_;
   bool timed_out_;
diff --git a/cc/test/picture_cache_model.cc b/cc/test/picture_cache_model.cc
new file mode 100644
index 0000000..54ed06b
--- /dev/null
+++ b/cc/test/picture_cache_model.cc
@@ -0,0 +1,47 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/test/picture_cache_model.h"
+
+#include "base/logging.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkStream.h"
+
+class SkPixelSerializer;
+
+namespace cc {
+namespace {
+
+sk_sp<SkPicture> CopySkPicture(const SkPicture* picture) {
+  SkDynamicMemoryWStream write_stream;
+  picture->serialize(&write_stream, nullptr);
+  DCHECK_GT(write_stream.bytesWritten(), 0u);
+
+  sk_sp<SkData> data(write_stream.copyToData());
+
+  SkMemoryStream read_stream(data);
+  return SkPicture::MakeFromStream(&read_stream, nullptr);
+}
+
+}  // namespace
+
+PictureCacheModel::PictureCacheModel() = default;
+
+PictureCacheModel::~PictureCacheModel() = default;
+
+void PictureCacheModel::AddPicture(const SkPicture* picture) {
+  sk_sp<SkPicture> picture_copy = CopySkPicture(picture);
+  pictures_.insert(std::make_pair(picture->uniqueID(), picture_copy));
+}
+
+sk_sp<const SkPicture> PictureCacheModel::GetPicture(uint32_t unique_id) {
+  if (pictures_.find(unique_id) == pictures_.end())
+    return nullptr;
+
+  return pictures_.find(unique_id)->second;
+}
+
+}  // namespace cc
diff --git a/cc/test/picture_cache_model.h b/cc/test/picture_cache_model.h
new file mode 100644
index 0000000..d60a4fc
--- /dev/null
+++ b/cc/test/picture_cache_model.h
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_TEST_PICTURE_CACHE_MODEL_H_
+#define CC_TEST_PICTURE_CACHE_MODEL_H_
+
+#include <stdint.h>
+
+#include <unordered_map>
+
+#include "base/macros.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkPicture;
+
+namespace cc {
+
+// PictureCacheModel provides a way for sharing a cache between an engine
+// and the client. When the FakeEnginePictureCache and the ClientPictureCache
+// point to the same PictureCacheModel, the calls to AddPicture on the
+// engine make an SkPicture available through GetPicture on the client.
+class PictureCacheModel {
+ public:
+  PictureCacheModel();
+  ~PictureCacheModel();
+
+  void AddPicture(const SkPicture* picture);
+
+  sk_sp<const SkPicture> GetPicture(uint32_t unique_id);
+
+ private:
+  std::unordered_map<uint32_t, sk_sp<const SkPicture>> pictures_;
+
+  DISALLOW_COPY_AND_ASSIGN(PictureCacheModel);
+};
+
+}  // namespace cc
+
+#endif  // CC_TEST_PICTURE_CACHE_MODEL_H_
diff --git a/cc/test/skia_common.cc b/cc/test/skia_common.cc
index 13fdd6f0..980fc4b2 100644
--- a/cc/test/skia_common.cc
+++ b/cc/test/skia_common.cc
@@ -26,7 +26,7 @@
 
 void DrawDisplayList(unsigned char* buffer,
                      const gfx::Rect& layer_rect,
-                     scoped_refptr<DisplayItemList> list) {
+                     scoped_refptr<const DisplayItemList> list) {
   SkImageInfo info =
       SkImageInfo::MakeN32Premul(layer_rect.width(), layer_rect.height());
   SkBitmap bitmap;
@@ -37,8 +37,8 @@
 }
 
 bool AreDisplayListDrawingResultsSame(const gfx::Rect& layer_rect,
-                                      scoped_refptr<DisplayItemList> list_a,
-                                      scoped_refptr<DisplayItemList> list_b) {
+                                      const DisplayItemList* list_a,
+                                      const DisplayItemList* list_b) {
   const size_t pixel_size = 4 * layer_rect.size().GetArea();
 
   std::unique_ptr<unsigned char[]> pixels_a(new unsigned char[pixel_size]);
diff --git a/cc/test/skia_common.h b/cc/test/skia_common.h
index 2de9e8e..30db5b1 100644
--- a/cc/test/skia_common.h
+++ b/cc/test/skia_common.h
@@ -21,11 +21,11 @@
 
 void DrawDisplayList(unsigned char* buffer,
                      const gfx::Rect& layer_rect,
-                     scoped_refptr<DisplayItemList> list);
+                     scoped_refptr<const DisplayItemList> list);
 
 bool AreDisplayListDrawingResultsSame(const gfx::Rect& layer_rect,
-                                      scoped_refptr<DisplayItemList> list_a,
-                                      scoped_refptr<DisplayItemList> list_b);
+                                      const DisplayItemList* list_a,
+                                      const DisplayItemList* list_b);
 
 sk_sp<SkImage> CreateDiscardableImage(const gfx::Size& size);
 
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 3ed5b0e..76d3ebf 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <algorithm>
+#include <memory>
 #include <stack>
 #include <string>
 #include <unordered_map>
@@ -28,6 +29,9 @@
 #include "cc/animation/animation_events.h"
 #include "cc/animation/animation_host.h"
 #include "cc/base/math_util.h"
+#include "cc/blimp/image_serialization_processor.h"
+#include "cc/blimp/picture_data.h"
+#include "cc/blimp/picture_data_conversions.h"
 #include "cc/debug/devtools_instrumentation.h"
 #include "cc/debug/frame_viewer_instrumentation.h"
 #include "cc/debug/rendering_stats_instrumentation.h"
@@ -284,6 +288,11 @@
     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) {
   task_runner_provider_ = TaskRunnerProvider::Create(main_task_runner, nullptr);
 
+  if (image_serialization_processor_) {
+    engine_picture_cache_ =
+        image_serialization_processor_->CreateEnginePictureCache();
+  }
+
   // The LayerTreeHost on the server never requests the output surface since
   // it is only needed on the client. Since ProxyMain aborts commits if
   // output_surface_lost() is true, always assume we have the output surface
@@ -302,6 +311,11 @@
   task_runner_provider_ =
       TaskRunnerProvider::Create(main_task_runner, impl_task_runner);
 
+  if (image_serialization_processor_) {
+    client_picture_cache_ =
+        image_serialization_processor_->CreateClientPictureCache();
+  }
+
   // For the remote mode, the RemoteChannelImpl implements the Proxy, which is
   // owned by the LayerTreeHost. The RemoteChannelImpl pipes requests which need
   // to handled locally, for instance the Output Surface creation to the
@@ -318,10 +332,25 @@
     std::unique_ptr<Proxy> proxy_for_testing,
     std::unique_ptr<BeginFrameSource> external_begin_frame_source) {
   task_runner_provider_ = std::move(task_runner_provider);
+
+  InitializePictureCacheForTesting();
+
   InitializeProxy(std::move(proxy_for_testing),
                   std::move(external_begin_frame_source));
 }
 
+void LayerTreeHost::InitializePictureCacheForTesting() {
+  if (!image_serialization_processor_)
+    return;
+
+  // Initialize both engine and client cache to ensure serialization tests
+  // with a single LayerTreeHost can work correctly.
+  engine_picture_cache_ =
+      image_serialization_processor_->CreateEnginePictureCache();
+  client_picture_cache_ =
+      image_serialization_processor_->CreateClientPictureCache();
+}
+
 void LayerTreeHost::SetTaskRunnerProviderForTesting(
     std::unique_ptr<TaskRunnerProvider> task_runner_provider) {
   DCHECK(!task_runner_provider_);
@@ -1499,6 +1528,7 @@
 void LayerTreeHost::ToProtobufForCommit(
     proto::LayerTreeHost* proto,
     std::vector<std::unique_ptr<SwapPromise>>* swap_promises) {
+  DCHECK(engine_picture_cache_);
   // Not all fields are serialized, as they are either not needed for a commit,
   // or implementation isn't ready yet.
   // Unsupported items:
@@ -1538,6 +1568,11 @@
   LayerProtoConverter::SerializeLayerProperties(this,
                                                 proto->mutable_layer_updates());
 
+  std::vector<PictureData> pictures =
+      engine_picture_cache_->CalculateCacheUpdateAndFlush();
+  proto::PictureDataVectorToSkPicturesProto(pictures,
+                                            proto->mutable_pictures());
+
   proto->set_hud_layer_id(hud_layer_ ? hud_layer_->id() : Layer::INVALID_ID);
   debug_state_.ToProtobuf(proto->mutable_debug_state());
   SizeToProto(device_viewport_size_, proto->mutable_device_viewport_size());
@@ -1594,6 +1629,8 @@
 }
 
 void LayerTreeHost::FromProtobufForCommit(const proto::LayerTreeHost& proto) {
+  DCHECK(client_picture_cache_);
+
   needs_full_tree_sync_ = proto.needs_full_tree_sync();
   needs_meta_info_recomputation_ = proto.needs_meta_info_recomputation();
   source_frame_number_ = proto.source_frame_number();
@@ -1609,9 +1646,19 @@
   for (auto layer_id : proto.layers_that_should_push_properties())
     layers_that_should_push_properties_.insert(layer_id_map_[layer_id]);
 
+  // Ensure ClientPictureCache contains all the necessary SkPictures before
+  // deserializing the properties.
+  proto::SkPictures proto_pictures = proto.pictures();
+  std::vector<PictureData> pictures =
+      SkPicturesProtoToPictureDataVector(proto_pictures);
+  client_picture_cache_->ApplyCacheUpdate(pictures);
+
   LayerProtoConverter::DeserializeLayerProperties(root_layer_.get(),
                                                   proto.layer_updates());
 
+  // The deserialization is finished, so now clear the cache.
+  client_picture_cache_->Flush();
+
   debug_state_.FromProtobuf(proto.debug_state());
   device_viewport_size_ = ProtoToSize(proto.device_viewport_size());
   top_controls_shrink_blink_size_ = proto.top_controls_shrink_blink_size();
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 545fbb05f..7581ff2 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -22,6 +22,8 @@
 #include "base/time/time.h"
 #include "cc/animation/target_property.h"
 #include "cc/base/cc_export.h"
+#include "cc/blimp/client_picture_cache.h"
+#include "cc/blimp/engine_picture_cache.h"
 #include "cc/debug/micro_benchmark.h"
 #include "cc/debug/micro_benchmark_controller.h"
 #include "cc/input/event_listener_properties.h"
@@ -426,6 +428,14 @@
     return image_serialization_processor_;
   }
 
+  EnginePictureCache* engine_picture_cache() const {
+    return engine_picture_cache_ ? engine_picture_cache_.get() : nullptr;
+  }
+
+  ClientPictureCache* client_picture_cache() const {
+    return client_picture_cache_ ? client_picture_cache_.get() : nullptr;
+  }
+
  protected:
   LayerTreeHost(InitParams* params, CompositorMode mode);
   void InitializeThreaded(
@@ -447,6 +457,7 @@
       std::unique_ptr<TaskRunnerProvider> task_runner_provider,
       std::unique_ptr<Proxy> proxy_for_testing,
       std::unique_ptr<BeginFrameSource> external_begin_frame_source);
+  void InitializePictureCacheForTesting();
   void SetOutputSurfaceLostForTesting(bool is_lost) {
     output_surface_lost_ = is_lost;
   }
@@ -580,6 +591,8 @@
   TaskGraphRunner* task_graph_runner_;
 
   ImageSerializationProcessor* image_serialization_processor_;
+  std::unique_ptr<EnginePictureCache> engine_picture_cache_;
+  std::unique_ptr<ClientPictureCache> client_picture_cache_;
 
   std::vector<std::unique_ptr<SwapPromise>> swap_promise_list_;
   std::set<SwapPromiseMonitor*> swap_promise_monitor_;
diff --git a/cc/trees/layer_tree_host_unittest_serialization.cc b/cc/trees/layer_tree_host_unittest_serialization.cc
index b1beece7..59bc7016 100644
--- a/cc/trees/layer_tree_host_unittest_serialization.cc
+++ b/cc/trees/layer_tree_host_unittest_serialization.cc
@@ -4,13 +4,21 @@
 
 #include "cc/trees/layer_tree_host.h"
 
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "cc/layers/empty_content_layer_client.h"
 #include "cc/layers/heads_up_display_layer.h"
 #include "cc/layers/layer.h"
 #include "cc/proto/layer.pb.h"
 #include "cc/proto/layer_tree_host.pb.h"
+#include "cc/test/fake_image_serialization_processor.h"
 #include "cc/test/fake_layer_tree_host.h"
 #include "cc/test/fake_layer_tree_host_client.h"
+#include "cc/test/fake_picture_layer.h"
+#include "cc/test/fake_recording_source.h"
 #include "cc/test/layer_tree_test.h"
+#include "cc/test/skia_common.h"
 #include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/layer_tree_settings.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -21,18 +29,54 @@
 
 namespace cc {
 
+namespace {
+std::unique_ptr<FakeRecordingSource> CreateRecordingSource(
+    const gfx::Rect& viewport) {
+  gfx::Rect layer_rect(viewport.right(), viewport.bottom());
+  std::unique_ptr<FakeRecordingSource> recording_source =
+      FakeRecordingSource::CreateRecordingSource(viewport, layer_rect.size());
+  return recording_source;
+}
+
+scoped_refptr<FakePictureLayer> CreatePictureLayer() {
+  gfx::Rect recorded_viewport(0, 0, 256, 256);
+
+  std::unique_ptr<FakeRecordingSource> recording_source =
+      CreateRecordingSource(recorded_viewport);
+  recording_source->SetDisplayListUsesCachedPicture(false);
+
+  SkPaint simple_paint;
+  simple_paint.setColor(SkColorSetARGB(255, 12, 23, 34));
+  recording_source->add_draw_rect_with_paint(gfx::Rect(0, 0, 256, 256),
+                                             simple_paint);
+  recording_source->SetGenerateDiscardableImagesMetadata(true);
+  recording_source->Rerecord();
+
+  ContentLayerClient* client = EmptyContentLayerClient::GetInstance();
+  return FakePictureLayer::CreateWithRecordingSource(
+      client, std::move(recording_source));
+}
+}  // namespace
+
 class LayerTreeHostSerializationTest : public testing::Test {
  public:
   LayerTreeHostSerializationTest()
-      : client_src_(FakeLayerTreeHostClient::DIRECT_3D),
+      : image_serialization_processor_(
+            base::WrapUnique(new FakeImageSerializationProcessor)),
+        client_src_(FakeLayerTreeHostClient::DIRECT_3D),
         client_dst_(FakeLayerTreeHostClient::DIRECT_3D) {}
 
  protected:
   void SetUp() override {
-    layer_tree_host_src_ =
-        FakeLayerTreeHost::Create(&client_src_, &task_graph_runner_src_);
-    layer_tree_host_dst_ =
-        FakeLayerTreeHost::Create(&client_dst_, &task_graph_runner_dst_);
+    LayerTreeSettings settings;
+    layer_tree_host_src_ = FakeLayerTreeHost::Create(
+        &client_src_, &task_graph_runner_src_, settings,
+        CompositorMode::SINGLE_THREADED, image_serialization_processor_.get());
+    layer_tree_host_dst_ = FakeLayerTreeHost::Create(
+        &client_dst_, &task_graph_runner_dst_, settings,
+        CompositorMode::SINGLE_THREADED, image_serialization_processor_.get());
+    layer_tree_host_src_->InitializePictureCacheForTesting();
+    layer_tree_host_dst_->InitializePictureCacheForTesting();
   }
 
   void TearDown() override {
@@ -311,6 +355,39 @@
     VerifySerializationAndDeserialization();
   }
 
+  void RunPictureLayerMultipleSerializationsTest() {
+    // Just fake setup a layer for both source and dest.
+    scoped_refptr<Layer> root_layer_src = Layer::Create();
+    layer_tree_host_src_->SetRootLayer(root_layer_src);
+    layer_tree_host_dst_->SetRootLayer(Layer::Create());
+
+    // Ensure that a PictureLayer work correctly for multiple rounds of
+    // serialization and deserialization.
+    scoped_refptr<FakePictureLayer> picture_layer_src = CreatePictureLayer();
+    root_layer_src->AddChild(picture_layer_src);
+    picture_layer_src->SetBounds(gfx::Size(10, 10));
+    picture_layer_src->SetIsDrawable(true);
+    picture_layer_src->SavePaintProperties();
+    picture_layer_src->Update();
+    picture_layer_src->SavePaintProperties();
+    VerifySerializationAndDeserialization();
+    ASSERT_EQ(1U, layer_tree_host_dst_->root_layer()->children().size());
+    PictureLayer* picture_layer_dst = reinterpret_cast<PictureLayer*>(
+        layer_tree_host_dst_->root_layer()->child_at(0));
+
+    RecordingSource* recording_source_src =
+        picture_layer_src->GetRecordingSourceForTesting();
+    RecordingSource* recording_source_dst =
+        picture_layer_dst->GetRecordingSourceForTesting();
+    EXPECT_EQ(recording_source_src->GetSize(), recording_source_dst->GetSize());
+    EXPECT_TRUE(AreDisplayListDrawingResultsSame(
+        gfx::Rect(recording_source_src->GetSize()),
+        recording_source_src->GetDisplayItemList(),
+        recording_source_dst->GetDisplayItemList()));
+
+    VerifySerializationAndDeserialization();
+  }
+
   void RunAddAndRemoveNodeFromLayerTree() {
     /* Testing serialization when the tree hierarchy changes like this:
          root                  root
@@ -345,13 +422,15 @@
   }
 
  private:
+  std::unique_ptr<ImageSerializationProcessor> image_serialization_processor_;
+
   TestTaskGraphRunner task_graph_runner_src_;
   FakeLayerTreeHostClient client_src_;
-  std::unique_ptr<LayerTreeHost> layer_tree_host_src_;
+  std::unique_ptr<FakeLayerTreeHost> layer_tree_host_src_;
 
   TestTaskGraphRunner task_graph_runner_dst_;
   FakeLayerTreeHostClient client_dst_;
-  std::unique_ptr<LayerTreeHost> layer_tree_host_dst_;
+  std::unique_ptr<FakeLayerTreeHost> layer_tree_host_dst_;
 };
 
 TEST_F(LayerTreeHostSerializationTest, AllMembersChanged) {
@@ -370,4 +449,8 @@
   RunAddAndRemoveNodeFromLayerTree();
 }
 
+TEST_F(LayerTreeHostSerializationTest, PictureLayerMultipleSerializations) {
+  RunPictureLayerMultipleSerializationsTest();
+}
+
 }  // namespace cc