gltrace: Make framebuffer contents an optional message.

Currently, the contents of the FrameBuffer are sent by encoding
them as the last argument to the function call. As a result, it is
not possible to know if a message has the framebuffer encoded in it
without looking at the function type.

This patch modifies the protobuf definition to include a separate
optional framebuffer message.

Change-Id: Ief3a6950052d927ca0743e729457435b48c25a92
diff --git a/opengl/libs/GLES_trace/gltrace.proto b/opengl/libs/GLES_trace/gltrace.proto
index 59f80e3..12d8e7c 100644
--- a/opengl/libs/GLES_trace/gltrace.proto
+++ b/opengl/libs/GLES_trace/gltrace.proto
@@ -459,7 +459,7 @@
             BYTE = 3;       // GLbyte, GLubyte
             INT = 4;        // GLbitfield, GLshort, GLint, GLsizei, GLushort, GLuint, GLfixed
             FLOAT = 5;      // GLfloat, GLclampf
-            BOOL = 6;    // GLboolean
+            BOOL = 6;       // GLboolean
             ENUM = 7;       // GLenum
         };
 
@@ -473,9 +473,16 @@
         repeated bool   boolValue = 7;
     }
 
+    message FrameBuffer {
+        required int32  width = 1;
+        required int32  height = 2;
+        repeated bytes  contents = 3;
+    }
+
     required int32      context_id = 1;                     // GL context ID
     required Function   function = 2 [default = invalid];   // GL function called
     repeated DataType   args = 3;                           // GL function's arguments
     optional DataType   returnValue = 4;                    // GL function's return value
     optional float      duration = 5;                       // duration of GL call
+    optional FrameBuffer fb = 6;                            // contents of the framebuffer
 };
diff --git a/opengl/libs/GLES_trace/src/gltrace.pb.cpp b/opengl/libs/GLES_trace/src/gltrace.pb.cpp
index 160afbf..6c1bb91 100644
--- a/opengl/libs/GLES_trace/src/gltrace.pb.cpp
+++ b/opengl/libs/GLES_trace/src/gltrace.pb.cpp
@@ -13,6 +13,7 @@
 void protobuf_ShutdownFile_gltrace_2eproto() {
   delete GLMessage::default_instance_;
   delete GLMessage_DataType::default_instance_;
+  delete GLMessage_FrameBuffer::default_instance_;
 }
 
 void protobuf_AddDesc_gltrace_2eproto() {
@@ -23,8 +24,10 @@
 
   GLMessage::default_instance_ = new GLMessage();
   GLMessage_DataType::default_instance_ = new GLMessage_DataType();
+  GLMessage_FrameBuffer::default_instance_ = new GLMessage_FrameBuffer();
   GLMessage::default_instance_->InitAsDefaultInstance();
   GLMessage_DataType::default_instance_->InitAsDefaultInstance();
+  GLMessage_FrameBuffer::default_instance_->InitAsDefaultInstance();
   ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_gltrace_2eproto);
 }
 
@@ -1306,11 +1309,238 @@
 // -------------------------------------------------------------------
 
 #ifndef _MSC_VER
+const int GLMessage_FrameBuffer::kWidthFieldNumber;
+const int GLMessage_FrameBuffer::kHeightFieldNumber;
+const int GLMessage_FrameBuffer::kContentsFieldNumber;
+#endif  // !_MSC_VER
+
+GLMessage_FrameBuffer::GLMessage_FrameBuffer()
+  : ::google::protobuf::MessageLite() {
+  SharedCtor();
+}
+
+void GLMessage_FrameBuffer::InitAsDefaultInstance() {
+}
+
+GLMessage_FrameBuffer::GLMessage_FrameBuffer(const GLMessage_FrameBuffer& from)
+  : ::google::protobuf::MessageLite() {
+  SharedCtor();
+  MergeFrom(from);
+}
+
+void GLMessage_FrameBuffer::SharedCtor() {
+  _cached_size_ = 0;
+  width_ = 0;
+  height_ = 0;
+  ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+GLMessage_FrameBuffer::~GLMessage_FrameBuffer() {
+  SharedDtor();
+}
+
+void GLMessage_FrameBuffer::SharedDtor() {
+  if (this != default_instance_) {
+  }
+}
+
+void GLMessage_FrameBuffer::SetCachedSize(int size) const {
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const GLMessage_FrameBuffer& GLMessage_FrameBuffer::default_instance() {
+  if (default_instance_ == NULL) protobuf_AddDesc_gltrace_2eproto();  return *default_instance_;
+}
+
+GLMessage_FrameBuffer* GLMessage_FrameBuffer::default_instance_ = NULL;
+
+GLMessage_FrameBuffer* GLMessage_FrameBuffer::New() const {
+  return new GLMessage_FrameBuffer;
+}
+
+void GLMessage_FrameBuffer::Clear() {
+  if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    width_ = 0;
+    height_ = 0;
+  }
+  contents_.Clear();
+  ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+bool GLMessage_FrameBuffer::MergePartialFromCodedStream(
+    ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) return false
+  ::google::protobuf::uint32 tag;
+  while ((tag = input->ReadTag()) != 0) {
+    switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+      // required int32 width = 1;
+      case 1: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &width_)));
+          _set_bit(0);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(16)) goto parse_height;
+        break;
+      }
+      
+      // required int32 height = 2;
+      case 2: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_height:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &height_)));
+          _set_bit(1);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(26)) goto parse_contents;
+        break;
+      }
+      
+      // repeated bytes contents = 3;
+      case 3: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+         parse_contents:
+          DO_(::google::protobuf::internal::WireFormatLite::ReadBytes(
+                input, this->add_contents()));
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(26)) goto parse_contents;
+        if (input->ExpectAtEnd()) return true;
+        break;
+      }
+      
+      default: {
+      handle_uninterpreted:
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+          return true;
+        }
+        DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag));
+        break;
+      }
+    }
+  }
+  return true;
+#undef DO_
+}
+
+void GLMessage_FrameBuffer::SerializeWithCachedSizes(
+    ::google::protobuf::io::CodedOutputStream* output) const {
+  // required int32 width = 1;
+  if (_has_bit(0)) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->width(), output);
+  }
+  
+  // required int32 height = 2;
+  if (_has_bit(1)) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->height(), output);
+  }
+  
+  // repeated bytes contents = 3;
+  for (int i = 0; i < this->contents_size(); i++) {
+    ::google::protobuf::internal::WireFormatLite::WriteBytes(
+      3, this->contents(i), output);
+  }
+  
+}
+
+int GLMessage_FrameBuffer::ByteSize() const {
+  int total_size = 0;
+  
+  if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    // required int32 width = 1;
+    if (has_width()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::Int32Size(
+          this->width());
+    }
+    
+    // required int32 height = 2;
+    if (has_height()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::Int32Size(
+          this->height());
+    }
+    
+  }
+  // repeated bytes contents = 3;
+  total_size += 1 * this->contents_size();
+  for (int i = 0; i < this->contents_size(); i++) {
+    total_size += ::google::protobuf::internal::WireFormatLite::BytesSize(
+      this->contents(i));
+  }
+  
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = total_size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+  return total_size;
+}
+
+void GLMessage_FrameBuffer::CheckTypeAndMergeFrom(
+    const ::google::protobuf::MessageLite& from) {
+  MergeFrom(*::google::protobuf::down_cast<const GLMessage_FrameBuffer*>(&from));
+}
+
+void GLMessage_FrameBuffer::MergeFrom(const GLMessage_FrameBuffer& from) {
+  GOOGLE_CHECK_NE(&from, this);
+  contents_.MergeFrom(from.contents_);
+  if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    if (from._has_bit(0)) {
+      set_width(from.width());
+    }
+    if (from._has_bit(1)) {
+      set_height(from.height());
+    }
+  }
+}
+
+void GLMessage_FrameBuffer::CopyFrom(const GLMessage_FrameBuffer& from) {
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool GLMessage_FrameBuffer::IsInitialized() const {
+  if ((_has_bits_[0] & 0x00000003) != 0x00000003) return false;
+  
+  return true;
+}
+
+void GLMessage_FrameBuffer::Swap(GLMessage_FrameBuffer* other) {
+  if (other != this) {
+    std::swap(width_, other->width_);
+    std::swap(height_, other->height_);
+    contents_.Swap(&other->contents_);
+    std::swap(_has_bits_[0], other->_has_bits_[0]);
+    std::swap(_cached_size_, other->_cached_size_);
+  }
+}
+
+::std::string GLMessage_FrameBuffer::GetTypeName() const {
+  return "android.gltrace.GLMessage.FrameBuffer";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
 const int GLMessage::kContextIdFieldNumber;
 const int GLMessage::kFunctionFieldNumber;
 const int GLMessage::kArgsFieldNumber;
 const int GLMessage::kReturnValueFieldNumber;
 const int GLMessage::kDurationFieldNumber;
+const int GLMessage::kFbFieldNumber;
 #endif  // !_MSC_VER
 
 GLMessage::GLMessage()
@@ -1320,6 +1550,7 @@
 
 void GLMessage::InitAsDefaultInstance() {
   returnvalue_ = const_cast< ::android::gltrace::GLMessage_DataType*>(&::android::gltrace::GLMessage_DataType::default_instance());
+  fb_ = const_cast< ::android::gltrace::GLMessage_FrameBuffer*>(&::android::gltrace::GLMessage_FrameBuffer::default_instance());
 }
 
 GLMessage::GLMessage(const GLMessage& from)
@@ -1334,6 +1565,7 @@
   function_ = 3000;
   returnvalue_ = NULL;
   duration_ = 0;
+  fb_ = NULL;
   ::memset(_has_bits_, 0, sizeof(_has_bits_));
 }
 
@@ -1344,6 +1576,7 @@
 void GLMessage::SharedDtor() {
   if (this != default_instance_) {
     delete returnvalue_;
+    delete fb_;
   }
 }
 
@@ -1370,6 +1603,9 @@
       if (returnvalue_ != NULL) returnvalue_->::android::gltrace::GLMessage_DataType::Clear();
     }
     duration_ = 0;
+    if (_has_bit(5)) {
+      if (fb_ != NULL) fb_->::android::gltrace::GLMessage_FrameBuffer::Clear();
+    }
   }
   args_.Clear();
   ::memset(_has_bits_, 0, sizeof(_has_bits_));
@@ -1456,6 +1692,20 @@
         } else {
           goto handle_uninterpreted;
         }
+        if (input->ExpectTag(50)) goto parse_fb;
+        break;
+      }
+      
+      // optional .android.gltrace.GLMessage.FrameBuffer fb = 6;
+      case 6: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+         parse_fb:
+          DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+               input, mutable_fb()));
+        } else {
+          goto handle_uninterpreted;
+        }
         if (input->ExpectAtEnd()) return true;
         break;
       }
@@ -1505,6 +1755,12 @@
     ::google::protobuf::internal::WireFormatLite::WriteFloat(5, this->duration(), output);
   }
   
+  // optional .android.gltrace.GLMessage.FrameBuffer fb = 6;
+  if (_has_bit(5)) {
+    ::google::protobuf::internal::WireFormatLite::WriteMessage(
+      6, this->fb(), output);
+  }
+  
 }
 
 int GLMessage::ByteSize() const {
@@ -1536,6 +1792,13 @@
       total_size += 1 + 4;
     }
     
+    // optional .android.gltrace.GLMessage.FrameBuffer fb = 6;
+    if (has_fb()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+          this->fb());
+    }
+    
   }
   // repeated .android.gltrace.GLMessage.DataType args = 3;
   total_size += 1 * this->args_size();
@@ -1572,6 +1835,9 @@
     if (from._has_bit(4)) {
       set_duration(from.duration());
     }
+    if (from._has_bit(5)) {
+      mutable_fb()->::android::gltrace::GLMessage_FrameBuffer::MergeFrom(from.fb());
+    }
   }
 }
 
@@ -1590,6 +1856,9 @@
   if (has_returnvalue()) {
     if (!this->returnvalue().IsInitialized()) return false;
   }
+  if (has_fb()) {
+    if (!this->fb().IsInitialized()) return false;
+  }
   return true;
 }
 
@@ -1600,6 +1869,7 @@
     args_.Swap(&other->args_);
     std::swap(returnvalue_, other->returnvalue_);
     std::swap(duration_, other->duration_);
+    std::swap(fb_, other->fb_);
     std::swap(_has_bits_[0], other->_has_bits_[0]);
     std::swap(_cached_size_, other->_cached_size_);
   }
diff --git a/opengl/libs/GLES_trace/src/gltrace.pb.h b/opengl/libs/GLES_trace/src/gltrace.pb.h
index be5ca2a..c39e1b2 100644
--- a/opengl/libs/GLES_trace/src/gltrace.pb.h
+++ b/opengl/libs/GLES_trace/src/gltrace.pb.h
@@ -34,6 +34,7 @@
 
 class GLMessage;
 class GLMessage_DataType;
+class GLMessage_FrameBuffer;
 
 enum GLMessage_DataType_Type {
   GLMessage_DataType_Type_VOID = 1,
@@ -658,6 +659,108 @@
 };
 // -------------------------------------------------------------------
 
+class GLMessage_FrameBuffer : public ::google::protobuf::MessageLite {
+ public:
+  GLMessage_FrameBuffer();
+  virtual ~GLMessage_FrameBuffer();
+  
+  GLMessage_FrameBuffer(const GLMessage_FrameBuffer& from);
+  
+  inline GLMessage_FrameBuffer& operator=(const GLMessage_FrameBuffer& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  
+  static const GLMessage_FrameBuffer& default_instance();
+  
+  void Swap(GLMessage_FrameBuffer* other);
+  
+  // implements Message ----------------------------------------------
+  
+  GLMessage_FrameBuffer* New() const;
+  void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+  void CopyFrom(const GLMessage_FrameBuffer& from);
+  void MergeFrom(const GLMessage_FrameBuffer& from);
+  void Clear();
+  bool IsInitialized() const;
+  
+  int ByteSize() const;
+  bool MergePartialFromCodedStream(
+      ::google::protobuf::io::CodedInputStream* input);
+  void SerializeWithCachedSizes(
+      ::google::protobuf::io::CodedOutputStream* output) const;
+  int GetCachedSize() const { return _cached_size_; }
+  private:
+  void SharedCtor();
+  void SharedDtor();
+  void SetCachedSize(int size) const;
+  public:
+  
+  ::std::string GetTypeName() const;
+  
+  // nested types ----------------------------------------------------
+  
+  // accessors -------------------------------------------------------
+  
+  // required int32 width = 1;
+  inline bool has_width() const;
+  inline void clear_width();
+  static const int kWidthFieldNumber = 1;
+  inline ::google::protobuf::int32 width() const;
+  inline void set_width(::google::protobuf::int32 value);
+  
+  // required int32 height = 2;
+  inline bool has_height() const;
+  inline void clear_height();
+  static const int kHeightFieldNumber = 2;
+  inline ::google::protobuf::int32 height() const;
+  inline void set_height(::google::protobuf::int32 value);
+  
+  // repeated bytes contents = 3;
+  inline int contents_size() const;
+  inline void clear_contents();
+  static const int kContentsFieldNumber = 3;
+  inline const ::std::string& contents(int index) const;
+  inline ::std::string* mutable_contents(int index);
+  inline void set_contents(int index, const ::std::string& value);
+  inline void set_contents(int index, const char* value);
+  inline void set_contents(int index, const void* value, size_t size);
+  inline ::std::string* add_contents();
+  inline void add_contents(const ::std::string& value);
+  inline void add_contents(const char* value);
+  inline void add_contents(const void* value, size_t size);
+  inline const ::google::protobuf::RepeatedPtrField< ::std::string>& contents() const;
+  inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_contents();
+  
+  // @@protoc_insertion_point(class_scope:android.gltrace.GLMessage.FrameBuffer)
+ private:
+  mutable int _cached_size_;
+  
+  ::google::protobuf::int32 width_;
+  ::google::protobuf::int32 height_;
+  ::google::protobuf::RepeatedPtrField< ::std::string> contents_;
+  friend void  protobuf_AddDesc_gltrace_2eproto();
+  friend void protobuf_AssignDesc_gltrace_2eproto();
+  friend void protobuf_ShutdownFile_gltrace_2eproto();
+  
+  ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32];
+  
+  // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
+  inline bool _has_bit(int index) const {
+    return (_has_bits_[index / 32] & (1u << (index % 32))) != 0;
+  }
+  inline void _set_bit(int index) {
+    _has_bits_[index / 32] |= (1u << (index % 32));
+  }
+  inline void _clear_bit(int index) {
+    _has_bits_[index / 32] &= ~(1u << (index % 32));
+  }
+  
+  void InitAsDefaultInstance();
+  static GLMessage_FrameBuffer* default_instance_;
+};
+// -------------------------------------------------------------------
+
 class GLMessage : public ::google::protobuf::MessageLite {
  public:
   GLMessage();
@@ -700,6 +803,7 @@
   // nested types ----------------------------------------------------
   
   typedef GLMessage_DataType DataType;
+  typedef GLMessage_FrameBuffer FrameBuffer;
   
   typedef GLMessage_Function Function;
   static const Function glActiveTexture = GLMessage_Function_glActiveTexture;
@@ -1178,6 +1282,13 @@
   inline float duration() const;
   inline void set_duration(float value);
   
+  // optional .android.gltrace.GLMessage.FrameBuffer fb = 6;
+  inline bool has_fb() const;
+  inline void clear_fb();
+  static const int kFbFieldNumber = 6;
+  inline const ::android::gltrace::GLMessage_FrameBuffer& fb() const;
+  inline ::android::gltrace::GLMessage_FrameBuffer* mutable_fb();
+  
   // @@protoc_insertion_point(class_scope:android.gltrace.GLMessage)
  private:
   mutable int _cached_size_;
@@ -1187,11 +1298,12 @@
   ::google::protobuf::RepeatedPtrField< ::android::gltrace::GLMessage_DataType > args_;
   ::android::gltrace::GLMessage_DataType* returnvalue_;
   float duration_;
+  ::android::gltrace::GLMessage_FrameBuffer* fb_;
   friend void  protobuf_AddDesc_gltrace_2eproto();
   friend void protobuf_AssignDesc_gltrace_2eproto();
   friend void protobuf_ShutdownFile_gltrace_2eproto();
   
-  ::google::protobuf::uint32 _has_bits_[(5 + 31) / 32];
+  ::google::protobuf::uint32 _has_bits_[(6 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
   inline bool _has_bit(int index) const {
@@ -1412,6 +1524,86 @@
 
 // -------------------------------------------------------------------
 
+// GLMessage_FrameBuffer
+
+// required int32 width = 1;
+inline bool GLMessage_FrameBuffer::has_width() const {
+  return _has_bit(0);
+}
+inline void GLMessage_FrameBuffer::clear_width() {
+  width_ = 0;
+  _clear_bit(0);
+}
+inline ::google::protobuf::int32 GLMessage_FrameBuffer::width() const {
+  return width_;
+}
+inline void GLMessage_FrameBuffer::set_width(::google::protobuf::int32 value) {
+  _set_bit(0);
+  width_ = value;
+}
+
+// required int32 height = 2;
+inline bool GLMessage_FrameBuffer::has_height() const {
+  return _has_bit(1);
+}
+inline void GLMessage_FrameBuffer::clear_height() {
+  height_ = 0;
+  _clear_bit(1);
+}
+inline ::google::protobuf::int32 GLMessage_FrameBuffer::height() const {
+  return height_;
+}
+inline void GLMessage_FrameBuffer::set_height(::google::protobuf::int32 value) {
+  _set_bit(1);
+  height_ = value;
+}
+
+// repeated bytes contents = 3;
+inline int GLMessage_FrameBuffer::contents_size() const {
+  return contents_.size();
+}
+inline void GLMessage_FrameBuffer::clear_contents() {
+  contents_.Clear();
+}
+inline const ::std::string& GLMessage_FrameBuffer::contents(int index) const {
+  return contents_.Get(index);
+}
+inline ::std::string* GLMessage_FrameBuffer::mutable_contents(int index) {
+  return contents_.Mutable(index);
+}
+inline void GLMessage_FrameBuffer::set_contents(int index, const ::std::string& value) {
+  contents_.Mutable(index)->assign(value);
+}
+inline void GLMessage_FrameBuffer::set_contents(int index, const char* value) {
+  contents_.Mutable(index)->assign(value);
+}
+inline void GLMessage_FrameBuffer::set_contents(int index, const void* value, size_t size) {
+  contents_.Mutable(index)->assign(
+    reinterpret_cast<const char*>(value), size);
+}
+inline ::std::string* GLMessage_FrameBuffer::add_contents() {
+  return contents_.Add();
+}
+inline void GLMessage_FrameBuffer::add_contents(const ::std::string& value) {
+  contents_.Add()->assign(value);
+}
+inline void GLMessage_FrameBuffer::add_contents(const char* value) {
+  contents_.Add()->assign(value);
+}
+inline void GLMessage_FrameBuffer::add_contents(const void* value, size_t size) {
+  contents_.Add()->assign(reinterpret_cast<const char*>(value), size);
+}
+inline const ::google::protobuf::RepeatedPtrField< ::std::string>&
+GLMessage_FrameBuffer::contents() const {
+  return contents_;
+}
+inline ::google::protobuf::RepeatedPtrField< ::std::string>*
+GLMessage_FrameBuffer::mutable_contents() {
+  return &contents_;
+}
+
+// -------------------------------------------------------------------
+
 // GLMessage
 
 // required int32 context_id = 1;
@@ -1505,6 +1697,23 @@
   duration_ = value;
 }
 
+// optional .android.gltrace.GLMessage.FrameBuffer fb = 6;
+inline bool GLMessage::has_fb() const {
+  return _has_bit(5);
+}
+inline void GLMessage::clear_fb() {
+  if (fb_ != NULL) fb_->::android::gltrace::GLMessage_FrameBuffer::Clear();
+  _clear_bit(5);
+}
+inline const ::android::gltrace::GLMessage_FrameBuffer& GLMessage::fb() const {
+  return fb_ != NULL ? *fb_ : *default_instance_->fb_;
+}
+inline ::android::gltrace::GLMessage_FrameBuffer* GLMessage::mutable_fb() {
+  _set_bit(5);
+  if (fb_ == NULL) fb_ = new ::android::gltrace::GLMessage_FrameBuffer;
+  return fb_;
+}
+
 
 // @@protoc_insertion_point(namespace_scope)
 
diff --git a/opengl/libs/GLES_trace/src/gltrace_fixup.cpp b/opengl/libs/GLES_trace/src/gltrace_fixup.cpp
index 90021f4..c5b0451 100644
--- a/opengl/libs/GLES_trace/src/gltrace_fixup.cpp
+++ b/opengl/libs/GLES_trace/src/gltrace_fixup.cpp
@@ -95,20 +95,16 @@
     }
 }
 
-/* Add the contents of the framebuffer as an argument */
+/* Add the contents of the framebuffer to the protobuf message */
 void fixup_addFBContents(GLMessage *glmsg) {
-    GLMessage_DataType *arg_fb = glmsg->add_args();    /* Add the FB as the last argument */
-    GLTraceContext *glContext = getGLTraceContext();
-
-    void *fb;
+    void *fbcontents;
     unsigned fbsize, fbwidth, fbheight;
-    glContext->getCompressedFB(&fb, &fbsize, &fbwidth, &fbheight);
+    getGLTraceContext()->getCompressedFB(&fbcontents, &fbsize, &fbwidth, &fbheight);
 
-    arg_fb->set_isarray(true);
-    arg_fb->set_type(GLMessage::DataType::BYTE);
-    arg_fb->add_rawbytes(fb, fbsize);
-    arg_fb->add_intvalue(fbwidth);
-    arg_fb->add_intvalue(fbheight);
+    GLMessage_FrameBuffer *fb = glmsg->mutable_fb();
+    fb->set_width(fbwidth);
+    fb->set_height(fbheight);
+    fb->add_contents(fbcontents, fbsize);
 }
 
 void fixup_glTexImage2D(GLMessage *glmsg) {