[go: nahoru, domu]

Add protobuf serialization to DisplayItemList

Serialize the following classes:
- DisplayItemListSettings
- DisplayItemList
- DisplayItem and all subclasses
  - Did not fully serialize FilterDisplayItem.  Serializing
    FilterOperations will require more work.  Will do in a follow up CL.

Added unit tests for all DisplayItems serialized.

BUG=541321
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel

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

Cr-Commit-Position: refs/heads/master@{#356563}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 778f637..2ff2593 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -292,6 +292,8 @@
     "playback/display_item_list.h",
     "playback/display_item_list_settings.cc",
     "playback/display_item_list_settings.h",
+    "playback/display_item_proto_factory.cc",
+    "playback/display_item_proto_factory.h",
     "playback/display_list_raster_source.cc",
     "playback/display_list_raster_source.h",
     "playback/display_list_recording_source.cc",
diff --git a/cc/cc.gyp b/cc/cc.gyp
index 9d0ec78..2a40437 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -354,6 +354,8 @@
         'playback/display_item_list.h',
         'playback/display_item_list_settings.cc',
         'playback/display_item_list_settings.h',
+        'playback/display_item_proto_factory.cc',
+        'playback/display_item_proto_factory.h',
         'playback/display_list_raster_source.cc',
         'playback/display_list_raster_source.h',
         'playback/display_list_recording_source.cc',
@@ -581,6 +583,7 @@
       'target_name': 'cc_proto',
       'type': '<(component)',
       'sources': [
+        'proto/display_item.proto',
         'proto/point.proto',
         'proto/pointf.proto',
         'proto/rect.proto',
diff --git a/cc/playback/clip_display_item.cc b/cc/playback/clip_display_item.cc
index 5941502..ac2bc93 100644
--- a/cc/playback/clip_display_item.cc
+++ b/cc/playback/clip_display_item.cc
@@ -6,8 +6,12 @@
 
 #include <string>
 
+#include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event_argument.h"
+#include "cc/proto/display_item.pb.h"
+#include "cc/proto/gfx_conversions.h"
+#include "cc/proto/skia_conversions.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "ui/gfx/skia_util.h"
 
@@ -31,6 +35,30 @@
                       external_memory_usage);
 }
 
+void ClipDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
+  proto->set_type(proto::DisplayItem::Type_Clip);
+
+  proto::ClipDisplayItem* details = proto->mutable_clip_item();
+  RectToProto(clip_rect_, details->mutable_clip_rect());
+  DCHECK_EQ(0, details->rounded_rects_size());
+  for (const auto& rrect : rounded_clip_rects_) {
+    SkRRectToProto(rrect, details->add_rounded_rects());
+  }
+}
+
+void ClipDisplayItem::FromProtobuf(const proto::DisplayItem& proto) {
+  DCHECK_EQ(proto::DisplayItem::Type_Clip, proto.type());
+
+  const proto::ClipDisplayItem& details = proto.clip_item();
+  gfx::Rect clip_rect = ProtoToRect(details.clip_rect());
+  std::vector<SkRRect> rounded_clip_rects;
+  rounded_clip_rects.reserve(details.rounded_rects_size());
+  for (int i = 0; i < details.rounded_rects_size(); i++) {
+    rounded_clip_rects.push_back(ProtoToSkRRect(details.rounded_rects(i)));
+  }
+  SetNew(clip_rect, rounded_clip_rects);
+}
+
 void ClipDisplayItem::Raster(SkCanvas* canvas,
                              const gfx::Rect& canvas_target_playback_rect,
                              SkPicture::AbortCallback* callback) const {
@@ -82,6 +110,14 @@
 EndClipDisplayItem::~EndClipDisplayItem() {
 }
 
+void EndClipDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
+  proto->set_type(proto::DisplayItem::Type_EndClip);
+}
+
+void EndClipDisplayItem::FromProtobuf(const proto::DisplayItem& proto) {
+  DCHECK_EQ(proto::DisplayItem::Type_EndClip, proto.type());
+}
+
 void EndClipDisplayItem::Raster(SkCanvas* canvas,
                                 const gfx::Rect& canvas_target_playback_rect,
                                 SkPicture::AbortCallback* callback) const {
diff --git a/cc/playback/clip_display_item.h b/cc/playback/clip_display_item.h
index 83e0b6bf..320ea31 100644
--- a/cc/playback/clip_display_item.h
+++ b/cc/playback/clip_display_item.h
@@ -25,6 +25,8 @@
   void SetNew(gfx::Rect clip_rect,
               const std::vector<SkRRect>& rounded_clip_rects);
 
+  void ToProtobuf(proto::DisplayItem* proto) const override;
+  void FromProtobuf(const proto::DisplayItem& proto) override;
   void Raster(SkCanvas* canvas,
               const gfx::Rect& canvas_target_playback_rect,
               SkPicture::AbortCallback* callback) const override;
@@ -40,6 +42,8 @@
   EndClipDisplayItem();
   ~EndClipDisplayItem() override;
 
+  void ToProtobuf(proto::DisplayItem* proto) const override;
+  void FromProtobuf(const proto::DisplayItem& proto) override;
   void Raster(SkCanvas* canvas,
               const gfx::Rect& canvas_target_playback_rect,
               SkPicture::AbortCallback* callback) const override;
diff --git a/cc/playback/clip_path_display_item.cc b/cc/playback/clip_path_display_item.cc
index f7fd606..3b46621 100644
--- a/cc/playback/clip_path_display_item.cc
+++ b/cc/playback/clip_path_display_item.cc
@@ -6,6 +6,8 @@
 
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event_argument.h"
+#include "cc/proto/display_item.pb.h"
+#include "cc/proto/skia_conversions.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 
 namespace cc {
@@ -29,6 +31,39 @@
                       0 /* external_memory_usage */);
 }
 
+void ClipPathDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
+  proto->set_type(proto::DisplayItem::Type_ClipPath);
+
+  proto::ClipPathDisplayItem* details = proto->mutable_clip_path_item();
+  details->set_clip_op(SkRegionOpToProto(clip_op_));
+  details->set_antialias(antialias_);
+
+  // Just use skia's serialization method for the SkPath for now.
+  size_t path_size = clip_path_.writeToMemory(nullptr);
+  if (path_size > 0) {
+    scoped_ptr<char[]> buffer(new char[path_size]);
+    clip_path_.writeToMemory(buffer.get());
+    details->set_clip_path(std::string(buffer.get(), path_size));
+  }
+}
+
+void ClipPathDisplayItem::FromProtobuf(const proto::DisplayItem& proto) {
+  DCHECK_EQ(proto::DisplayItem::Type_ClipPath, proto.type());
+
+  const proto::ClipPathDisplayItem& details = proto.clip_path_item();
+  SkRegion::Op clip_op = SkRegionOpFromProto(details.clip_op());
+  bool antialias = details.antialias();
+
+  SkPath clip_path;
+  if (details.has_clip_path()) {
+    size_t bytes_read = clip_path.readFromMemory(details.clip_path().c_str(),
+                                                 details.clip_path().size());
+    DCHECK_EQ(details.clip_path().size(), bytes_read);
+  }
+
+  SetNew(clip_path, clip_op, antialias);
+}
+
 void ClipPathDisplayItem::Raster(SkCanvas* canvas,
                                  const gfx::Rect& canvas_target_playback_rect,
                                  SkPicture::AbortCallback* callback) const {
@@ -50,6 +85,14 @@
 EndClipPathDisplayItem::~EndClipPathDisplayItem() {
 }
 
+void EndClipPathDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
+  proto->set_type(proto::DisplayItem::Type_EndClipPath);
+}
+
+void EndClipPathDisplayItem::FromProtobuf(const proto::DisplayItem& proto) {
+  DCHECK_EQ(proto::DisplayItem::Type_EndClipPath, proto.type());
+}
+
 void EndClipPathDisplayItem::Raster(
     SkCanvas* canvas,
     const gfx::Rect& canvas_target_playback_rect,
diff --git a/cc/playback/clip_path_display_item.h b/cc/playback/clip_path_display_item.h
index 732087b9..6e425390 100644
--- a/cc/playback/clip_path_display_item.h
+++ b/cc/playback/clip_path_display_item.h
@@ -22,6 +22,8 @@
 
   void SetNew(const SkPath& path, SkRegion::Op clip_op, bool antialias);
 
+  void ToProtobuf(proto::DisplayItem* proto) const override;
+  void FromProtobuf(const proto::DisplayItem& proto) override;
   void Raster(SkCanvas* canvas,
               const gfx::Rect& canvas_target_playback_rect,
               SkPicture::AbortCallback* callback) const override;
@@ -42,6 +44,8 @@
     return make_scoped_ptr(new EndClipPathDisplayItem());
   }
 
+  void ToProtobuf(proto::DisplayItem* proto) const override;
+  void FromProtobuf(const proto::DisplayItem& proto) override;
   void Raster(SkCanvas* canvas,
               const gfx::Rect& canvas_target_playback_rect,
               SkPicture::AbortCallback* callback) const override;
diff --git a/cc/playback/compositing_display_item.cc b/cc/playback/compositing_display_item.cc
index 31365b5..2489d3c3 100644
--- a/cc/playback/compositing_display_item.cc
+++ b/cc/playback/compositing_display_item.cc
@@ -6,7 +6,13 @@
 
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event_argument.h"
+#include "cc/proto/display_item.pb.h"
+#include "cc/proto/gfx_conversions.h"
+#include "cc/proto/skia_conversions.h"
 #include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkFlattenable.h"
+#include "third_party/skia/include/core/SkFlattenableSerialization.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/core/SkXfermode.h"
 #include "ui/gfx/skia_util.h"
@@ -36,6 +42,46 @@
                       external_memory_usage);
 }
 
+void CompositingDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
+  proto->set_type(proto::DisplayItem::Type_Compositing);
+
+  proto::CompositingDisplayItem* details = proto->mutable_compositing_item();
+  details->set_alpha(static_cast<uint32_t>(alpha_));
+  details->set_mode(SkXfermodeModeToProto(xfermode_));
+  if (has_bounds_)
+    RectFToProto(gfx::SkRectToRectF(bounds_), details->mutable_bounds());
+
+  if (color_filter_) {
+    skia::RefPtr<SkData> data =
+        skia::AdoptRef(SkValidatingSerializeFlattenable(color_filter_.get()));
+    if (data->size() > 0)
+      details->set_color_filter(data->data(), data->size());
+  }
+}
+
+void CompositingDisplayItem::FromProtobuf(const proto::DisplayItem& proto) {
+  DCHECK_EQ(proto::DisplayItem::Type_Compositing, proto.type());
+
+  const proto::CompositingDisplayItem& details = proto.compositing_item();
+  uint8_t alpha = static_cast<uint8_t>(details.alpha());
+  SkXfermode::Mode xfermode = SkXfermodeModeFromProto(details.mode());
+  scoped_ptr<SkRect> bounds;
+  if (details.has_bounds()) {
+    bounds.reset(
+        new SkRect(gfx::RectFToSkRect(ProtoToRectF(details.bounds()))));
+  }
+
+  skia::RefPtr<SkColorFilter> filter;
+  if (details.has_color_filter()) {
+    SkFlattenable* flattenable = SkValidatingDeserializeFlattenable(
+        details.color_filter().c_str(), details.color_filter().size(),
+        SkColorFilter::GetFlattenableType());
+    filter = skia::AdoptRef(static_cast<SkColorFilter*>(flattenable));
+  }
+
+  SetNew(alpha, xfermode, bounds.get(), filter.Pass());
+}
+
 void CompositingDisplayItem::Raster(
     SkCanvas* canvas,
     const gfx::Rect& canvas_target_playback_rect,
@@ -66,6 +112,14 @@
 EndCompositingDisplayItem::~EndCompositingDisplayItem() {
 }
 
+void EndCompositingDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
+  proto->set_type(proto::DisplayItem::Type_EndCompositing);
+}
+
+void EndCompositingDisplayItem::FromProtobuf(const proto::DisplayItem& proto) {
+  DCHECK_EQ(proto::DisplayItem::Type_EndCompositing, proto.type());
+}
+
 void EndCompositingDisplayItem::Raster(
     SkCanvas* canvas,
     const gfx::Rect& canvas_target_playback_rect,
diff --git a/cc/playback/compositing_display_item.h b/cc/playback/compositing_display_item.h
index 91cd3e7..8042de6 100644
--- a/cc/playback/compositing_display_item.h
+++ b/cc/playback/compositing_display_item.h
@@ -28,6 +28,8 @@
               SkRect* bounds,
               skia::RefPtr<SkColorFilter> color_filter);
 
+  void ToProtobuf(proto::DisplayItem* proto) const override;
+  void FromProtobuf(const proto::DisplayItem& proto) override;
   void Raster(SkCanvas* canvas,
               const gfx::Rect& canvas_target_playback_rect,
               SkPicture::AbortCallback* callback) const override;
@@ -50,6 +52,8 @@
     return make_scoped_ptr(new EndCompositingDisplayItem());
   }
 
+  void ToProtobuf(proto::DisplayItem* proto) const override;
+  void FromProtobuf(const proto::DisplayItem& proto) override;
   void Raster(SkCanvas* canvas,
               const gfx::Rect& canvas_target_playback_rect,
               SkPicture::AbortCallback* callback) const override;
diff --git a/cc/playback/display_item.h b/cc/playback/display_item.h
index d89807d..3991cb2 100644
--- a/cc/playback/display_item.h
+++ b/cc/playback/display_item.h
@@ -15,6 +15,10 @@
 
 namespace cc {
 
+namespace proto {
+class DisplayItem;
+}
+
 class CC_EXPORT DisplayItem {
  public:
   virtual ~DisplayItem() {}
@@ -27,6 +31,8 @@
     external_memory_usage_ = external_memory_usage;
   }
 
+  virtual void ToProtobuf(proto::DisplayItem* proto) const = 0;
+  virtual void FromProtobuf(const proto::DisplayItem& proto) = 0;
   virtual void Raster(SkCanvas* canvas,
                       const gfx::Rect& canvas_target_playback_rect,
                       SkPicture::AbortCallback* callback) const = 0;
diff --git a/cc/playback/display_item_list.cc b/cc/playback/display_item_list.cc
index 7614bfd..fbf184a 100644
--- a/cc/playback/display_item_list.cc
+++ b/cc/playback/display_item_list.cc
@@ -14,7 +14,10 @@
 #include "cc/debug/traced_display_item_list.h"
 #include "cc/debug/traced_value.h"
 #include "cc/playback/display_item_list_settings.h"
+#include "cc/playback/display_item_proto_factory.h"
 #include "cc/playback/largest_display_item.h"
+#include "cc/proto/display_item.pb.h"
+#include "cc/proto/gfx_conversions.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkPictureRecorder.h"
 #include "third_party/skia/include/utils/SkPictureUtils.h"
@@ -47,11 +50,29 @@
       !settings.use_cached_picture || DisplayItemsTracingEnabled()));
 }
 
+scoped_refptr<DisplayItemList> DisplayItemList::CreateFromProto(
+    const proto::DisplayItemList& proto) {
+  gfx::Rect layer_rect = ProtoToRect(proto.layer_rect());
+  scoped_refptr<DisplayItemList> list =
+      DisplayItemList::Create(ProtoToRect(proto.layer_rect()),
+                              DisplayItemListSettings(proto.settings()));
+
+  for (int i = 0; i < proto.items_size(); i++) {
+    const proto::DisplayItem& item_proto = proto.items(i);
+    DisplayItem* item =
+        DisplayItemProtoFactory::AllocateAndConstruct(list, item_proto);
+    if (item)
+      item->FromProtobuf(item_proto);
+  }
+
+  return list;
+}
+
 DisplayItemList::DisplayItemList(gfx::Rect layer_rect,
                                  const DisplayItemListSettings& settings,
                                  bool retain_individual_display_items)
     : items_(LargestDisplayItemSize(), kDefaultNumDisplayItemsToReserve),
-      use_cached_picture_(settings.use_cached_picture),
+      settings_(settings),
       retain_individual_display_items_(retain_individual_display_items),
       layer_rect_(layer_rect),
       is_suitable_for_gpu_rasterization_(true),
@@ -61,7 +82,7 @@
 #if DCHECK_IS_ON()
   needs_process_ = false;
 #endif
-  if (use_cached_picture_) {
+  if (settings_.use_cached_picture) {
     SkRTreeFactory factory;
     recorder_.reset(new SkPictureRecorder());
     canvas_ = skia::SharePtr(recorder_->beginRecording(
@@ -74,12 +95,21 @@
 DisplayItemList::~DisplayItemList() {
 }
 
+void DisplayItemList::ToProtobuf(proto::DisplayItemList* proto) {
+  RectToProto(layer_rect_, proto->mutable_layer_rect());
+  settings_.ToProtobuf(proto->mutable_settings());
+
+  DCHECK_EQ(0, proto->items_size());
+  for (auto* item : items_)
+    item->ToProtobuf(proto->add_items());
+}
+
 void DisplayItemList::Raster(SkCanvas* canvas,
                              SkPicture::AbortCallback* callback,
                              const gfx::Rect& canvas_target_playback_rect,
                              float contents_scale) const {
   DCHECK(ProcessAppendedItemsCalled());
-  if (!use_cached_picture_) {
+  if (!settings_.use_cached_picture) {
     canvas->save();
     canvas->scale(contents_scale, contents_scale);
     for (auto* item : items_)
@@ -122,7 +152,7 @@
   needs_process_ = false;
 #endif
   for (const DisplayItem* item : items_) {
-    if (use_cached_picture_) {
+    if (settings_.use_cached_picture) {
       // When using a cached picture we will calculate gpu suitability on the
       // entire cached picture instead of the items. This is more permissive
       // since none of the items might individually trigger a veto even though
@@ -165,16 +195,16 @@
   // The last item should not have been handled by ProcessAppendedItems, so we
   // don't need to remove it from approximate_op_count_, etc.
   DCHECK(retain_individual_display_items_);
-  DCHECK(!use_cached_picture_);
+  DCHECK(!settings_.use_cached_picture);
   items_.RemoveLast();
 }
 
 void DisplayItemList::Finalize() {
   ProcessAppendedItems();
 
-  if (use_cached_picture_) {
+  if (settings_.use_cached_picture) {
     // Convert to an SkPicture for faster rasterization.
-    DCHECK(use_cached_picture_);
+    DCHECK(settings_.use_cached_picture);
     DCHECK(!picture_);
     picture_ = skia::AdoptRef(recorder_->endRecordingAsPicture());
     DCHECK(picture_);
@@ -200,10 +230,10 @@
 size_t DisplayItemList::ApproximateMemoryUsage() const {
   DCHECK(ProcessAppendedItemsCalled());
   // We double-count in this case. Produce zero to avoid being misleading.
-  if (use_cached_picture_ && retain_individual_display_items_)
+  if (settings_.use_cached_picture && retain_individual_display_items_)
     return 0;
 
-  DCHECK_IMPLIES(use_cached_picture_, picture_);
+  DCHECK_IMPLIES(settings_.use_cached_picture, picture_);
 
   size_t memory_usage = sizeof(*this);
 
@@ -273,8 +303,8 @@
   DCHECK(ProcessAppendedItemsCalled());
   // This should be only called once, and only after CreateAndCacheSkPicture.
   DCHECK(image_map_.empty());
-  DCHECK_IMPLIES(use_cached_picture_, picture_);
-  if (use_cached_picture_ && !picture_->willPlayBackBitmaps())
+  DCHECK_IMPLIES(settings_.use_cached_picture, picture_);
+  if (settings_.use_cached_picture && !picture_->willPlayBackBitmaps())
     return;
 
   // The cached picture is translated by -layer_rect_.origin during record,
diff --git a/cc/playback/display_item_list.h b/cc/playback/display_item_list.h
index 95342ed..2142260 100644
--- a/cc/playback/display_item_list.h
+++ b/cc/playback/display_item_list.h
@@ -13,6 +13,7 @@
 #include "cc/base/list_container.h"
 #include "cc/playback/discardable_image_map.h"
 #include "cc/playback/display_item.h"
+#include "cc/playback/display_item_list_settings.h"
 #include "skia/ext/refptr.h"
 #include "third_party/skia/include/core/SkPicture.h"
 #include "ui/gfx/geometry/rect.h"
@@ -22,7 +23,9 @@
 
 namespace cc {
 
-class DisplayItemListSettings;
+namespace proto {
+class DisplayItemList;
+}
 
 class CC_EXPORT DisplayItemList
     : public base::RefCountedThreadSafe<DisplayItemList> {
@@ -36,6 +39,17 @@
       const gfx::Rect& layer_rect,
       const DisplayItemListSettings& settings);
 
+  // Creates a DisplayItemList from a Protobuf.
+  // TODO(dtrainor): Pass in a list of possible DisplayItems to reuse
+  // (crbug.com/548434).
+  static scoped_refptr<DisplayItemList> CreateFromProto(
+      const proto::DisplayItemList& proto);
+
+  // 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);
+
   void Raster(SkCanvas* canvas,
               SkPicture::AbortCallback* callback,
               const gfx::Rect& canvas_target_playback_rect,
@@ -103,7 +117,7 @@
 
   scoped_ptr<SkPictureRecorder> recorder_;
   skia::RefPtr<SkCanvas> canvas_;
-  const bool use_cached_picture_;
+  const DisplayItemListSettings settings_;
   bool retain_individual_display_items_;
 
   gfx::Rect layer_rect_;
diff --git a/cc/playback/display_item_list_settings.cc b/cc/playback/display_item_list_settings.cc
index c71d9ee..616fc61 100644
--- a/cc/playback/display_item_list_settings.cc
+++ b/cc/playback/display_item_list_settings.cc
@@ -3,13 +3,23 @@
 // found in the LICENSE file.
 
 #include "cc/playback/display_item_list_settings.h"
+#include "cc/proto/display_item.pb.h"
 
 namespace cc {
 
 DisplayItemListSettings::DisplayItemListSettings()
     : use_cached_picture(false) {}
 
+DisplayItemListSettings::DisplayItemListSettings(
+    const proto::DisplayItemListSettings& proto)
+    : use_cached_picture(proto.use_cached_picture()) {}
+
 DisplayItemListSettings::~DisplayItemListSettings() {
 }
 
+void DisplayItemListSettings::ToProtobuf(
+    proto::DisplayItemListSettings* proto) const {
+  proto->set_use_cached_picture(use_cached_picture);
+}
+
 }  // namespace cc
diff --git a/cc/playback/display_item_list_settings.h b/cc/playback/display_item_list_settings.h
index 85fe8db..9c08162 100644
--- a/cc/playback/display_item_list_settings.h
+++ b/cc/playback/display_item_list_settings.h
@@ -11,11 +11,18 @@
 
 namespace cc {
 
+namespace proto {
+class DisplayItemListSettings;
+}
+
 class CC_EXPORT DisplayItemListSettings {
  public:
   DisplayItemListSettings();
+  explicit DisplayItemListSettings(const proto::DisplayItemListSettings& proto);
   ~DisplayItemListSettings();
 
+  void ToProtobuf(proto::DisplayItemListSettings* proto) const;
+
   // If set, a picture will be cached inside the DisplayItemList.
   bool use_cached_picture;
 };
diff --git a/cc/playback/display_item_list_unittest.cc b/cc/playback/display_item_list_unittest.cc
index c490bbf..cb19e39 100644
--- a/cc/playback/display_item_list_unittest.cc
+++ b/cc/playback/display_item_list_unittest.cc
@@ -9,10 +9,14 @@
 #include "cc/output/filter_operation.h"
 #include "cc/output/filter_operations.h"
 #include "cc/playback/clip_display_item.h"
+#include "cc/playback/clip_path_display_item.h"
+#include "cc/playback/compositing_display_item.h"
 #include "cc/playback/display_item_list_settings.h"
 #include "cc/playback/drawing_display_item.h"
 #include "cc/playback/filter_display_item.h"
+#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/skia_common.h"
 #include "skia/ext/refptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -21,6 +25,7 @@
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkPictureRecorder.h"
 #include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/core/SkXfermode.h"
 #include "third_party/skia/include/effects/SkImageSource.h"
 #include "third_party/skia/include/utils/SkPictureUtils.h"
 #include "ui/gfx/geometry/rect_conversions.h"
@@ -28,6 +33,238 @@
 
 namespace cc {
 
+namespace {
+
+void AppendFirstSerializationTestPicture(scoped_refptr<DisplayItemList> list,
+                                         const gfx::Size& layer_size) {
+  gfx::PointF offset(2.f, 3.f);
+  SkPictureRecorder recorder;
+  skia::RefPtr<SkCanvas> canvas;
+  skia::RefPtr<SkPicture> picture;
+
+  SkPaint red_paint;
+  red_paint.setColor(SK_ColorRED);
+
+  canvas = skia::SharePtr(recorder.beginRecording(SkRect::MakeXYWH(
+      offset.x(), offset.y(), layer_size.width(), layer_size.height())));
+  canvas->translate(offset.x(), offset.y());
+  canvas->drawRectCoords(0.f, 0.f, 4.f, 4.f, red_paint);
+  picture = skia::AdoptRef(recorder.endRecordingAsPicture());
+  list->CreateAndAppendItem<DrawingDisplayItem>()->SetNew(picture);
+}
+
+void AppendSecondSerializationTestPicture(scoped_refptr<DisplayItemList> list,
+                                          const gfx::Size& layer_size) {
+  gfx::PointF offset(2.f, 2.f);
+  SkPictureRecorder recorder;
+  skia::RefPtr<SkCanvas> canvas;
+  skia::RefPtr<SkPicture> picture;
+
+  SkPaint blue_paint;
+  blue_paint.setColor(SK_ColorBLUE);
+
+  canvas = skia::SharePtr(recorder.beginRecording(SkRect::MakeXYWH(
+      offset.x(), offset.y(), layer_size.width(), layer_size.height())));
+  canvas->translate(offset.x(), offset.y());
+  canvas->drawRectCoords(3.f, 3.f, 7.f, 7.f, blue_paint);
+  picture = skia::AdoptRef(recorder.endRecordingAsPicture());
+  list->CreateAndAppendItem<DrawingDisplayItem>()->SetNew(picture);
+}
+
+void ValidateDisplayItemListSerialization(const gfx::Size& layer_size,
+                                          scoped_refptr<DisplayItemList> list) {
+  // Serialize and deserialize the DisplayItemList.
+  proto::DisplayItemList proto;
+  list->ToProtobuf(&proto);
+  scoped_refptr<DisplayItemList> new_list =
+      DisplayItemList::CreateFromProto(proto);
+
+  // Finalize the DisplayItemLists to perform raster.
+  list->Finalize();
+  new_list->Finalize();
+
+  const int pixel_size = 4 * layer_size.GetArea();
+
+  // Get the rendered contents of the old DisplayItemList.
+  scoped_ptr<unsigned char[]> pixels(new unsigned char[pixel_size]);
+  memset(pixels.get(), 0, pixel_size);
+  DrawDisplayList(pixels.get(), gfx::Rect(layer_size), list);
+
+  // Get the rendered contents of the new DisplayItemList.
+  scoped_ptr<unsigned char[]> new_pixels(new unsigned char[pixel_size]);
+  memset(new_pixels.get(), 0, pixel_size);
+  DrawDisplayList(new_pixels.get(), gfx::Rect(layer_size), new_list);
+
+  EXPECT_EQ(0, memcmp(pixels.get(), new_pixels.get(), pixel_size));
+}
+
+}  // namespace
+
+TEST(DisplayItemListTest, SerializeDisplayItemListSettings) {
+  DisplayItemListSettings settings;
+  settings.use_cached_picture = false;
+
+  {
+    proto::DisplayItemListSettings proto;
+    settings.ToProtobuf(&proto);
+    DisplayItemListSettings deserialized(proto);
+    EXPECT_EQ(settings.use_cached_picture, deserialized.use_cached_picture);
+  }
+
+  settings.use_cached_picture = true;
+  {
+    proto::DisplayItemListSettings proto;
+    settings.ToProtobuf(&proto);
+    DisplayItemListSettings deserialized(proto);
+    EXPECT_EQ(settings.use_cached_picture, deserialized.use_cached_picture);
+  }
+}
+
+TEST(DisplayItemListTest, SerializeSingleDrawingItem) {
+  gfx::Size layer_size(10, 10);
+
+  DisplayItemListSettings settings;
+  settings.use_cached_picture = true;
+  scoped_refptr<DisplayItemList> list =
+      DisplayItemList::Create(gfx::Rect(layer_size), settings);
+
+  // Build the DrawingDisplayItem.
+  AppendFirstSerializationTestPicture(list, layer_size);
+
+  ValidateDisplayItemListSerialization(layer_size, list);
+}
+
+TEST(DisplayItemListTest, SerializeClipItem) {
+  gfx::Size layer_size(10, 10);
+
+  DisplayItemListSettings settings;
+  settings.use_cached_picture = true;
+  scoped_refptr<DisplayItemList> list =
+      DisplayItemList::Create(gfx::Rect(layer_size), settings);
+
+  // Build the DrawingDisplayItem.
+  AppendFirstSerializationTestPicture(list, layer_size);
+
+  // Build the ClipDisplayItem.
+  gfx::Rect clip_rect(6, 6, 1, 1);
+  std::vector<SkRRect> rrects;
+  rrects.push_back(SkRRect::MakeOval(SkRect::MakeXYWH(5.f, 5.f, 4.f, 4.f)));
+  auto* item = list->CreateAndAppendItem<ClipDisplayItem>();
+  item->SetNew(clip_rect, rrects);
+
+  // Build the second DrawingDisplayItem.
+  AppendSecondSerializationTestPicture(list, layer_size);
+
+  // Build the EndClipDisplayItem.
+  list->CreateAndAppendItem<EndClipDisplayItem>();
+
+  ValidateDisplayItemListSerialization(layer_size, list);
+}
+
+TEST(DisplayItemListTest, SerializeClipPathItem) {
+  gfx::Size layer_size(10, 10);
+
+  DisplayItemListSettings settings;
+  settings.use_cached_picture = true;
+  scoped_refptr<DisplayItemList> list =
+      DisplayItemList::Create(gfx::Rect(layer_size), settings);
+
+  // Build the DrawingDisplayItem.
+  AppendFirstSerializationTestPicture(list, layer_size);
+
+  // Build the ClipPathDisplayItem.
+  SkPath path;
+  path.addCircle(5.f, 5.f, 2.f, SkPath::Direction::kCW_Direction);
+  auto* item = list->CreateAndAppendItem<ClipPathDisplayItem>();
+  item->SetNew(path, SkRegion::Op::kReplace_Op, false);
+
+  // Build the second DrawingDisplayItem.
+  AppendSecondSerializationTestPicture(list, layer_size);
+
+  // Build the EndClipPathDisplayItem.
+  list->CreateAndAppendItem<EndClipPathDisplayItem>();
+
+  ValidateDisplayItemListSerialization(layer_size, list);
+}
+
+TEST(DisplayItemListTest, SerializeCompositingItem) {
+  gfx::Size layer_size(10, 10);
+
+  DisplayItemListSettings settings;
+  settings.use_cached_picture = true;
+  scoped_refptr<DisplayItemList> list =
+      DisplayItemList::Create(gfx::Rect(layer_size), settings);
+
+  // Build the DrawingDisplayItem.
+  AppendFirstSerializationTestPicture(list, layer_size);
+
+  // Build the CompositingDisplayItem.
+  skia::RefPtr<SkColorFilter> filter = skia::AdoptRef(
+      SkColorFilter::CreateLightingFilter(SK_ColorRED, SK_ColorGREEN));
+  auto* item = list->CreateAndAppendItem<CompositingDisplayItem>();
+  item->SetNew(150, SkXfermode::Mode::kDst_Mode, nullptr, filter);
+
+  // Build the second DrawingDisplayItem.
+  AppendSecondSerializationTestPicture(list, layer_size);
+
+  // Build the EndCompositingDisplayItem.
+  list->CreateAndAppendItem<EndCompositingDisplayItem>();
+
+  ValidateDisplayItemListSerialization(layer_size, list);
+}
+
+TEST(DisplayItemListTest, SerializeFloatClipItem) {
+  gfx::Size layer_size(10, 10);
+
+  DisplayItemListSettings settings;
+  settings.use_cached_picture = true;
+  scoped_refptr<DisplayItemList> list =
+      DisplayItemList::Create(gfx::Rect(layer_size), settings);
+
+  // Build the DrawingDisplayItem.
+  AppendFirstSerializationTestPicture(list, layer_size);
+
+  // Build the FloatClipDisplayItem.
+  gfx::RectF clip_rect(6.f, 6.f, 1.f, 1.f);
+  auto* item2 = list->CreateAndAppendItem<FloatClipDisplayItem>();
+  item2->SetNew(clip_rect);
+
+  // Build the second DrawingDisplayItem.
+  AppendSecondSerializationTestPicture(list, layer_size);
+
+  // Build the EndFloatClipDisplayItem.
+  list->CreateAndAppendItem<EndFloatClipDisplayItem>();
+
+  ValidateDisplayItemListSerialization(layer_size, list);
+}
+
+TEST(DisplayItemListTest, SerializeTransformItem) {
+  gfx::Size layer_size(10, 10);
+
+  DisplayItemListSettings settings;
+  settings.use_cached_picture = true;
+  scoped_refptr<DisplayItemList> list =
+      DisplayItemList::Create(gfx::Rect(layer_size), settings);
+
+  // Build the DrawingDisplayItem.
+  AppendFirstSerializationTestPicture(list, layer_size);
+
+  // Build the TransformDisplayItem.
+  gfx::Transform transform;
+  transform.Scale(1.25f, 1.25f);
+  transform.Translate(-1.f, -1.f);
+  auto* item2 = list->CreateAndAppendItem<TransformDisplayItem>();
+  item2->SetNew(transform);
+
+  // Build the second DrawingDisplayItem.
+  AppendSecondSerializationTestPicture(list, layer_size);
+
+  // Build the EndTransformDisplayItem.
+  list->CreateAndAppendItem<EndTransformDisplayItem>();
+
+  ValidateDisplayItemListSerialization(layer_size, list);
+}
+
 TEST(DisplayItemListTest, SingleDrawingItem) {
   gfx::Rect layer_rect(100, 100);
   SkPictureRecorder recorder;
diff --git a/cc/playback/display_item_proto_factory.cc b/cc/playback/display_item_proto_factory.cc
new file mode 100644
index 0000000..b71fc9ae
--- /dev/null
+++ b/cc/playback/display_item_proto_factory.cc
@@ -0,0 +1,55 @@
+// Copyright 2015 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/playback/display_item_proto_factory.h"
+
+#include "cc/playback/clip_display_item.h"
+#include "cc/playback/clip_path_display_item.h"
+#include "cc/playback/compositing_display_item.h"
+#include "cc/playback/drawing_display_item.h"
+#include "cc/playback/filter_display_item.h"
+#include "cc/playback/float_clip_display_item.h"
+#include "cc/playback/transform_display_item.h"
+#include "cc/proto/display_item.pb.h"
+
+namespace cc {
+
+// static
+DisplayItem* DisplayItemProtoFactory::AllocateAndConstruct(
+    scoped_refptr<DisplayItemList> list,
+    const proto::DisplayItem& proto) {
+  switch (proto.type()) {
+    case proto::DisplayItem::Type_Clip:
+      return list->CreateAndAppendItem<ClipDisplayItem>();
+    case proto::DisplayItem::Type_EndClip:
+      return list->CreateAndAppendItem<EndClipDisplayItem>();
+    case proto::DisplayItem::Type_ClipPath:
+      return list->CreateAndAppendItem<ClipPathDisplayItem>();
+    case proto::DisplayItem::Type_EndClipPath:
+      return list->CreateAndAppendItem<EndClipPathDisplayItem>();
+    case proto::DisplayItem::Type_Compositing:
+      return list->CreateAndAppendItem<CompositingDisplayItem>();
+    case proto::DisplayItem::Type_EndCompositing:
+      return list->CreateAndAppendItem<EndCompositingDisplayItem>();
+    case proto::DisplayItem::Type_Drawing:
+      return list->CreateAndAppendItem<DrawingDisplayItem>();
+    case proto::DisplayItem::Type_Filter:
+      return list->CreateAndAppendItem<FilterDisplayItem>();
+    case proto::DisplayItem::Type_EndFilter:
+      return list->CreateAndAppendItem<EndFilterDisplayItem>();
+    case proto::DisplayItem::Type_FloatClip:
+      return list->CreateAndAppendItem<FloatClipDisplayItem>();
+    case proto::DisplayItem::Type_EndFloatClip:
+      return list->CreateAndAppendItem<EndFloatClipDisplayItem>();
+    case proto::DisplayItem::Type_Transform:
+      return list->CreateAndAppendItem<TransformDisplayItem>();
+    case proto::DisplayItem::Type_EndTransform:
+      return list->CreateAndAppendItem<EndTransformDisplayItem>();
+  }
+
+  NOTREACHED();
+  return nullptr;
+}
+
+}  // namespace cc
diff --git a/cc/playback/display_item_proto_factory.h b/cc/playback/display_item_proto_factory.h
new file mode 100644
index 0000000..43325df9
--- /dev/null
+++ b/cc/playback/display_item_proto_factory.h
@@ -0,0 +1,33 @@
+// Copyright 2015 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_PLAYBACK_DISPLAY_ITEM_PROTO_FACTORY_H_
+#define CC_PLAYBACK_DISPLAY_ITEM_PROTO_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "cc/playback/display_item.h"
+#include "cc/playback/display_item_list.h"
+
+namespace cc {
+
+namespace proto {
+class DisplayItem;
+}
+
+class DisplayItemProtoFactory {
+ public:
+  static DisplayItem* AllocateAndConstruct(scoped_refptr<DisplayItemList> list,
+                                           const proto::DisplayItem& proto);
+
+ private:
+  DisplayItemProtoFactory() {}
+  virtual ~DisplayItemProtoFactory() {}
+
+  DISALLOW_COPY_AND_ASSIGN(DisplayItemProtoFactory);
+};
+
+}  // namespace cc
+
+#endif  // CC_PLAYBACK_DISPLAY_ITEM_PROTO_FACTORY_H_
diff --git a/cc/playback/drawing_display_item.cc b/cc/playback/drawing_display_item.cc
index 6cebed2ae..a2740f9 100644
--- a/cc/playback/drawing_display_item.cc
+++ b/cc/playback/drawing_display_item.cc
@@ -9,9 +9,12 @@
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event_argument.h"
 #include "cc/debug/picture_debug_util.h"
+#include "cc/proto/display_item.pb.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"
 #include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkStream.h"
 #include "third_party/skia/include/utils/SkPictureUtils.h"
 #include "ui/gfx/skia_util.h"
 
@@ -30,6 +33,40 @@
                       SkPictureUtils::ApproximateBytesUsed(picture_.get()));
 }
 
+void DrawingDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
+  proto->set_type(proto::DisplayItem::Type_Drawing);
+
+  proto::DrawingDisplayItem* details = proto->mutable_drawing_item();
+
+  // Just use skia's serialize() method for now.
+  if (picture_) {
+    SkDynamicMemoryWStream stream;
+
+    // TODO(dtrainor, nyquist): Add an SkPixelSerializer to not serialize images
+    // more than once (crbug.com/548434).
+    picture_->serialize(&stream, nullptr);
+    if (stream.bytesWritten() > 0) {
+      SkAutoDataUnref data(stream.copyToData());
+      details->set_picture(data->data(), data->size());
+    }
+  }
+}
+
+void DrawingDisplayItem::FromProtobuf(const proto::DisplayItem& proto) {
+  DCHECK_EQ(proto::DisplayItem::Type_Drawing, proto.type());
+
+  skia::RefPtr<SkPicture> picture;
+  const proto::DrawingDisplayItem& details = proto.drawing_item();
+  if (details.has_picture()) {
+    SkMemoryStream stream(details.picture().data(), details.picture().size());
+
+    // TODO(dtrainor, nyquist): Add an image decoder.
+    picture = skia::AdoptRef(SkPicture::CreateFromStream(&stream, nullptr));
+  }
+
+  SetNew(picture.Pass());
+}
+
 void DrawingDisplayItem::Raster(SkCanvas* canvas,
                                 const gfx::Rect& canvas_target_playback_rect,
                                 SkPicture::AbortCallback* callback) const {
diff --git a/cc/playback/drawing_display_item.h b/cc/playback/drawing_display_item.h
index f94e7c9..9ca8530c 100644
--- a/cc/playback/drawing_display_item.h
+++ b/cc/playback/drawing_display_item.h
@@ -23,6 +23,8 @@
 
   void SetNew(skia::RefPtr<SkPicture> picture);
 
+  void ToProtobuf(proto::DisplayItem* proto) const override;
+  void FromProtobuf(const proto::DisplayItem& proto) override;
   void Raster(SkCanvas* canvas,
               const gfx::Rect& canvas_playback_rect,
               SkPicture::AbortCallback* callback) const override;
diff --git a/cc/playback/filter_display_item.cc b/cc/playback/filter_display_item.cc
index c155b68a..bcd676c7 100644
--- a/cc/playback/filter_display_item.cc
+++ b/cc/playback/filter_display_item.cc
@@ -7,6 +7,8 @@
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event_argument.h"
 #include "cc/output/render_surface_filters.h"
+#include "cc/proto/display_item.pb.h"
+#include "cc/proto/gfx_conversions.h"
 #include "skia/ext/refptr.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkImageFilter.h"
@@ -34,6 +36,27 @@
                       external_memory_usage);
 }
 
+void FilterDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
+  proto->set_type(proto::DisplayItem::Type_Filter);
+
+  proto::FilterDisplayItem* details = proto->mutable_filter_item();
+  RectFToProto(bounds_, details->mutable_bounds());
+
+  // TODO(dtrainor): Support serializing FilterOperations (crbug.com/541321).
+}
+
+void FilterDisplayItem::FromProtobuf(const proto::DisplayItem& proto) {
+  DCHECK_EQ(proto::DisplayItem::Type_Filter, proto.type());
+
+  const proto::FilterDisplayItem& details = proto.filter_item();
+  gfx::RectF bounds = ProtoToRectF(details.bounds());
+
+  // TODO(dtrainor): Support deserializing FilterOperations (crbug.com/541321).
+  FilterOperations filters;
+
+  SetNew(filters, bounds);
+}
+
 void FilterDisplayItem::Raster(SkCanvas* canvas,
                                const gfx::Rect& canvas_target_playback_rect,
                                SkPicture::AbortCallback* callback) const {
@@ -74,6 +97,14 @@
 EndFilterDisplayItem::~EndFilterDisplayItem() {
 }
 
+void EndFilterDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
+  proto->set_type(proto::DisplayItem::Type_EndFilter);
+}
+
+void EndFilterDisplayItem::FromProtobuf(const proto::DisplayItem& proto) {
+  DCHECK_EQ(proto::DisplayItem::Type_EndFilter, proto.type());
+}
+
 void EndFilterDisplayItem::Raster(SkCanvas* canvas,
                                   const gfx::Rect& canvas_target_playback_rect,
                                   SkPicture::AbortCallback* callback) const {
diff --git a/cc/playback/filter_display_item.h b/cc/playback/filter_display_item.h
index 9672878..edffb9e72 100644
--- a/cc/playback/filter_display_item.h
+++ b/cc/playback/filter_display_item.h
@@ -22,6 +22,8 @@
 
   void SetNew(const FilterOperations& filters, const gfx::RectF& bounds);
 
+  void ToProtobuf(proto::DisplayItem* proto) const override;
+  void FromProtobuf(const proto::DisplayItem& proto) override;
   void Raster(SkCanvas* canvas,
               const gfx::Rect& canvas_target_playback_rect,
               SkPicture::AbortCallback* callback) const override;
@@ -41,6 +43,8 @@
     return make_scoped_ptr(new EndFilterDisplayItem());
   }
 
+  void ToProtobuf(proto::DisplayItem* proto) const override;
+  void FromProtobuf(const proto::DisplayItem& proto) override;
   void Raster(SkCanvas* canvas,
               const gfx::Rect& canvas_target_playback_rect,
               SkPicture::AbortCallback* callback) const override;
diff --git a/cc/playback/float_clip_display_item.cc b/cc/playback/float_clip_display_item.cc
index 8b29fc2..f1ff6cb 100644
--- a/cc/playback/float_clip_display_item.cc
+++ b/cc/playback/float_clip_display_item.cc
@@ -6,6 +6,8 @@
 
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event_argument.h"
+#include "cc/proto/display_item.pb.h"
+#include "cc/proto/gfx_conversions.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "ui/gfx/skia_util.h"
 
@@ -24,6 +26,22 @@
                       0 /* external_memory_usage */);
 }
 
+void FloatClipDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
+  proto->set_type(proto::DisplayItem::Type_FloatClip);
+
+  proto::FloatClipDisplayItem* details = proto->mutable_float_clip_item();
+  RectFToProto(clip_rect_, details->mutable_clip_rect());
+}
+
+void FloatClipDisplayItem::FromProtobuf(const proto::DisplayItem& proto) {
+  DCHECK_EQ(proto::DisplayItem::Type_FloatClip, proto.type());
+
+  const proto::FloatClipDisplayItem& details = proto.float_clip_item();
+  gfx::RectF clip_rect = ProtoToRectF(details.clip_rect());
+
+  SetNew(clip_rect);
+}
+
 void FloatClipDisplayItem::Raster(SkCanvas* canvas,
                                   const gfx::Rect& canvas_target_playback_rect,
                                   SkPicture::AbortCallback* callback) const {
@@ -45,6 +63,14 @@
 EndFloatClipDisplayItem::~EndFloatClipDisplayItem() {
 }
 
+void EndFloatClipDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
+  proto->set_type(proto::DisplayItem::Type_EndFloatClip);
+}
+
+void EndFloatClipDisplayItem::FromProtobuf(const proto::DisplayItem& proto) {
+  DCHECK_EQ(proto::DisplayItem::Type_EndFloatClip, proto.type());
+}
+
 void EndFloatClipDisplayItem::Raster(
     SkCanvas* canvas,
     const gfx::Rect& canvas_target_playback_rect,
diff --git a/cc/playback/float_clip_display_item.h b/cc/playback/float_clip_display_item.h
index 13d637f..23999351 100644
--- a/cc/playback/float_clip_display_item.h
+++ b/cc/playback/float_clip_display_item.h
@@ -23,6 +23,8 @@
 
   void SetNew(const gfx::RectF& clip_rect);
 
+  void ToProtobuf(proto::DisplayItem* proto) const override;
+  void FromProtobuf(const proto::DisplayItem& proto) override;
   void Raster(SkCanvas* canvas,
               const gfx::Rect& canvas_target_playback_rect,
               SkPicture::AbortCallback* callback) const override;
@@ -41,6 +43,8 @@
     return make_scoped_ptr(new EndFloatClipDisplayItem());
   }
 
+  void ToProtobuf(proto::DisplayItem* proto) const override;
+  void FromProtobuf(const proto::DisplayItem& proto) override;
   void Raster(SkCanvas* canvas,
               const gfx::Rect& canvas_target_playback_rect,
               SkPicture::AbortCallback* callback) const override;
diff --git a/cc/playback/transform_display_item.cc b/cc/playback/transform_display_item.cc
index df7ca1b..d0e7de7 100644
--- a/cc/playback/transform_display_item.cc
+++ b/cc/playback/transform_display_item.cc
@@ -6,6 +6,8 @@
 
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event_argument.h"
+#include "cc/proto/display_item.pb.h"
+#include "cc/proto/gfx_conversions.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 
 namespace cc {
@@ -24,6 +26,22 @@
                       0 /* external_memory_usage */);
 }
 
+void TransformDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
+  proto->set_type(proto::DisplayItem::Type_Transform);
+
+  proto::TransformDisplayItem* details = proto->mutable_transform_item();
+  TransformToProto(transform_, details->mutable_transform());
+}
+
+void TransformDisplayItem::FromProtobuf(const proto::DisplayItem& proto) {
+  DCHECK_EQ(proto::DisplayItem::Type_Transform, proto.type());
+
+  const proto::TransformDisplayItem& details = proto.transform_item();
+  gfx::Transform transform = ProtoToTransform(details.transform());
+
+  SetNew(transform);
+}
+
 void TransformDisplayItem::Raster(SkCanvas* canvas,
                                   const gfx::Rect& canvas_target_playback_rect,
                                   SkPicture::AbortCallback* callback) const {
@@ -46,6 +64,14 @@
 EndTransformDisplayItem::~EndTransformDisplayItem() {
 }
 
+void EndTransformDisplayItem::ToProtobuf(proto::DisplayItem* proto) const {
+  proto->set_type(proto::DisplayItem::Type_EndTransform);
+}
+
+void EndTransformDisplayItem::FromProtobuf(const proto::DisplayItem& proto) {
+  DCHECK_EQ(proto::DisplayItem::Type_EndTransform, proto.type());
+}
+
 void EndTransformDisplayItem::Raster(
     SkCanvas* canvas,
     const gfx::Rect& canvas_target_playback_rect,
diff --git a/cc/playback/transform_display_item.h b/cc/playback/transform_display_item.h
index 9d3f9c6..304ee8cc 100644
--- a/cc/playback/transform_display_item.h
+++ b/cc/playback/transform_display_item.h
@@ -21,6 +21,8 @@
 
   void SetNew(const gfx::Transform& transform);
 
+  void ToProtobuf(proto::DisplayItem* proto) const override;
+  void FromProtobuf(const proto::DisplayItem& proto) override;
   void Raster(SkCanvas* canvas,
               const gfx::Rect& canvas_target_playback_rect,
               SkPicture::AbortCallback* callback) const override;
@@ -39,6 +41,8 @@
     return make_scoped_ptr(new EndTransformDisplayItem());
   }
 
+  void ToProtobuf(proto::DisplayItem* proto) const override;
+  void FromProtobuf(const proto::DisplayItem& proto) override;
   void Raster(SkCanvas* canvas,
               const gfx::Rect& canvas_target_playback_rect,
               SkPicture::AbortCallback* callback) const override;
diff --git a/cc/proto/BUILD.gn b/cc/proto/BUILD.gn
index acf5e9b..dcd4214 100644
--- a/cc/proto/BUILD.gn
+++ b/cc/proto/BUILD.gn
@@ -29,6 +29,7 @@
   sources = [
     # TODO(dtrainor): Move protos to their correct packages once it's possible
     # to include protos from other directories/targets (crbug.com/542423).
+    "display_item.proto",
     "point.proto",
     "pointf.proto",
     "rect.proto",
diff --git a/cc/proto/display_item.proto b/cc/proto/display_item.proto
new file mode 100644
index 0000000..6a01dff6
--- /dev/null
+++ b/cc/proto/display_item.proto
@@ -0,0 +1,93 @@
+// Copyright 2015 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.
+
+syntax = "proto2";
+
+import "skregion.proto";
+import "skrrect.proto";
+import "skxfermode.proto";
+import "rect.proto";
+import "rectf.proto";
+import "transform.proto";
+
+option optimize_for = LITE_RUNTIME;
+
+package cc.proto;
+
+message DisplayItemListSettings {
+  optional bool use_cached_picture = 1;
+}
+
+message DisplayItemList {
+  repeated DisplayItem items = 1;
+  optional cc.proto.Rect layer_rect = 2;
+  optional DisplayItemListSettings settings = 3;
+}
+
+message DisplayItem {
+  enum Type {
+    Type_Clip = 1;
+    Type_EndClip = 2;
+    Type_ClipPath = 3;
+    Type_EndClipPath = 4;
+    Type_Compositing = 5;
+    Type_EndCompositing = 6;
+    Type_Drawing = 7;
+    Type_Filter = 8;
+    Type_EndFilter = 9;
+    Type_FloatClip = 10;
+    Type_EndFloatClip = 11;
+    Type_Transform = 12;
+    Type_EndTransform = 13;
+  }
+
+  optional Type type = 1;
+
+  // Unique DisplayItem types.  |type| determines which one (if any) is valid.
+  optional ClipDisplayItem clip_item = 1000;
+  optional ClipPathDisplayItem clip_path_item = 1001;
+  optional CompositingDisplayItem compositing_item = 1002;
+  optional DrawingDisplayItem drawing_item = 1003;
+  optional FilterDisplayItem filter_item = 1004;
+  optional FloatClipDisplayItem float_clip_item = 1005;
+  optional TransformDisplayItem transform_item = 1006;
+}
+
+message ClipDisplayItem {
+  optional cc.proto.Rect clip_rect = 1;
+
+  repeated cc.proto.SkRRect rounded_rects = 2;
+}
+
+message ClipPathDisplayItem {
+  optional cc.proto.SkRegion.Op clip_op = 1;
+  optional bool antialias = 2;
+  optional bytes clip_path = 3; /* SkPath */
+}
+
+message CompositingDisplayItem {
+  optional uint32 alpha = 1;
+
+  optional cc.proto.SkXfermode.Mode mode = 2;
+  optional cc.proto.RectF bounds = 3;
+  optional bytes color_filter = 4; /* SkColorFilter */
+}
+
+message DrawingDisplayItem {
+  optional bytes picture = 1; /* SkPicture */
+}
+
+message FilterDisplayItem {
+  optional cc.proto.RectF bounds = 1;
+
+  // TODO(dtrainor): Support FilterOperations.
+}
+
+message FloatClipDisplayItem {
+  optional cc.proto.RectF clip_rect = 1;
+}
+
+message TransformDisplayItem {
+  optional cc.proto.Transform transform = 1;
+}
diff --git a/cc/proto/gfx_conversions.h b/cc/proto/gfx_conversions.h
index e982e58..5b5c315 100644
--- a/cc/proto/gfx_conversions.h
+++ b/cc/proto/gfx_conversions.h
@@ -29,6 +29,8 @@
 class Transform;
 }
 
+// TODO(dtrainor): Move these to a class and make them static
+// (crbug.com/548432).
 CC_EXPORT void PointToProto(const gfx::Point& point, proto::Point* proto);
 CC_EXPORT gfx::Point ProtoToPoint(const proto::Point& proto);
 
diff --git a/cc/proto/skia_conversions.h b/cc/proto/skia_conversions.h
index 302e313c..6735cf1 100644
--- a/cc/proto/skia_conversions.h
+++ b/cc/proto/skia_conversions.h
@@ -21,6 +21,8 @@
 class SkRRect;
 }
 
+// TODO(dtrainor): Move these to a class and make them static
+// (crbug.com/548432).
 CC_EXPORT SkRegion::Op SkRegionOpFromProto(proto::SkRegion::Op op);
 CC_EXPORT proto::SkRegion::Op SkRegionOpToProto(SkRegion::Op op);