Frame lifecycle MVP updates

We have decided to show the lifecycle of a frame as a view of ownership,
which makes it easy to understand for the users who are not
SurfaceFlinger-savvy. To do this, we try to create the ownership
slices(Phases) from the existing buffer events. This calls for a bit
more bookkeeping than usual.
Along with the updates, this CL also includes the following:
1) Move frame events parsing out of the graphics_event_parser
2) Rename the existing graphics_event_parser as gpu_event_parser as it
has only gpu events after the move
3) Code cleanup of the existing frame events parsing

Test: diff_test_trace_processor --trace-filter='graphics_frame*'
Bug: 155242423

Change-Id: I89201b1416f8ff37c03658dbd1ad9509e12a8453
diff --git a/Android.bp b/Android.bp
index ccae72b..6221868 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6645,8 +6645,9 @@
     "src/trace_processor/importers/proto/android_probes_module.cc",
     "src/trace_processor/importers/proto/android_probes_parser.cc",
     "src/trace_processor/importers/proto/android_probes_tracker.cc",
+    "src/trace_processor/importers/proto/gpu_event_parser.cc",
     "src/trace_processor/importers/proto/graphics_event_module.cc",
-    "src/trace_processor/importers/proto/graphics_event_parser.cc",
+    "src/trace_processor/importers/proto/graphics_frame_event_parser.cc",
     "src/trace_processor/importers/proto/heap_graph_module.cc",
     "src/trace_processor/importers/proto/heap_graph_tracker.cc",
     "src/trace_processor/importers/proto/system_probes_module.cc",
diff --git a/BUILD b/BUILD
index 578133a..4cc4a94 100644
--- a/BUILD
+++ b/BUILD
@@ -985,10 +985,12 @@
         "src/trace_processor/importers/proto/android_probes_parser.h",
         "src/trace_processor/importers/proto/android_probes_tracker.cc",
         "src/trace_processor/importers/proto/android_probes_tracker.h",
+        "src/trace_processor/importers/proto/gpu_event_parser.cc",
+        "src/trace_processor/importers/proto/gpu_event_parser.h",
         "src/trace_processor/importers/proto/graphics_event_module.cc",
         "src/trace_processor/importers/proto/graphics_event_module.h",
-        "src/trace_processor/importers/proto/graphics_event_parser.cc",
-        "src/trace_processor/importers/proto/graphics_event_parser.h",
+        "src/trace_processor/importers/proto/graphics_frame_event_parser.cc",
+        "src/trace_processor/importers/proto/graphics_frame_event_parser.h",
         "src/trace_processor/importers/proto/heap_graph_module.cc",
         "src/trace_processor/importers/proto/heap_graph_module.h",
         "src/trace_processor/importers/proto/heap_graph_tracker.cc",
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 1f26298..2b26b7e 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -208,8 +208,10 @@
     "importers/proto/android_probes_tracker.h",
     "importers/proto/graphics_event_module.cc",
     "importers/proto/graphics_event_module.h",
-    "importers/proto/graphics_event_parser.cc",
-    "importers/proto/graphics_event_parser.h",
+    "importers/proto/gpu_event_parser.cc",
+    "importers/proto/gpu_event_parser.h",
+    "importers/proto/graphics_frame_event_parser.cc",
+    "importers/proto/graphics_frame_event_parser.h",
     "importers/proto/heap_graph_module.cc",
     "importers/proto/heap_graph_module.h",
     "importers/proto/heap_graph_tracker.cc",
diff --git a/src/trace_processor/importers/common/slice_tracker.cc b/src/trace_processor/importers/common/slice_tracker.cc
index 40dfa43..8c82982 100644
--- a/src/trace_processor/importers/common/slice_tracker.cc
+++ b/src/trace_processor/importers/common/slice_tracker.cc
@@ -96,16 +96,18 @@
   });
 }
 
-void SliceTracker::ScopedFrameEvent(
+SliceId SliceTracker::ScopedFrameEvent(
     const tables::GraphicsFrameSliceTable::Row& row,
     SetArgsCallback args_callback) {
   PERFETTO_DCHECK(row.dur >= 0);
 
-  StartSlice(row.ts, TrackId(row.track_id), args_callback, [this, &row]() {
-    return context_->storage->mutable_graphics_frame_slice_table()
-        ->Insert(row)
-        .id;
+  SliceId id;
+  StartSlice(row.ts, TrackId(row.track_id), args_callback, [this, &row, &id]() {
+    id =
+        context_->storage->mutable_graphics_frame_slice_table()->Insert(row).id;
+    return id;
   });
+  return id;
 }
 
 base::Optional<uint32_t> SliceTracker::End(int64_t timestamp,
diff --git a/src/trace_processor/importers/common/slice_tracker.h b/src/trace_processor/importers/common/slice_tracker.h
index 56be608..804806c 100644
--- a/src/trace_processor/importers/common/slice_tracker.h
+++ b/src/trace_processor/importers/common/slice_tracker.h
@@ -61,8 +61,8 @@
   void ScopedGpu(const tables::GpuSliceTable::Row& row,
                  SetArgsCallback args_callback = SetArgsCallback());
 
-  void ScopedFrameEvent(const tables::GraphicsFrameSliceTable::Row& row,
-                        SetArgsCallback args_callback = SetArgsCallback());
+  SliceId ScopedFrameEvent(const tables::GraphicsFrameSliceTable::Row& row,
+                           SetArgsCallback args_callback = SetArgsCallback());
 
   // virtual for testing
   virtual base::Optional<uint32_t> End(
diff --git a/src/trace_processor/importers/proto/graphics_event_parser.cc b/src/trace_processor/importers/proto/gpu_event_parser.cc
similarity index 68%
rename from src/trace_processor/importers/proto/graphics_event_parser.cc
rename to src/trace_processor/importers/proto/gpu_event_parser.cc
index 6981a63..4ba14d8 100644
--- a/src/trace_processor/importers/proto/graphics_event_parser.cc
+++ b/src/trace_processor/importers/proto/gpu_event_parser.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/importers/proto/graphics_event_parser.h"
+#include "src/trace_processor/importers/proto/gpu_event_parser.h"
 
 #include <inttypes.h>
 
@@ -92,44 +92,12 @@
 
 }  // anonymous namespace
 
-GraphicsEventParser::GraphicsEventParser(TraceProcessorContext* context)
+GpuEventParser::GpuEventParser(TraceProcessorContext* context)
     : context_(context),
       vulkan_memory_tracker_(context),
       description_id_(context->storage->InternString("description")),
       gpu_render_stage_scope_id_(
           context->storage->InternString("gpu_render_stage")),
-      graphics_event_scope_id_(
-          context->storage->InternString("graphics_frame_event")),
-      unknown_event_name_id_(context->storage->InternString("unknown_event")),
-      no_layer_name_name_id_(context->storage->InternString("no_layer_name")),
-      layer_name_key_id_(context->storage->InternString("layer_name")),
-      event_type_name_ids_{
-          {context->storage->InternString(
-               "unspecified_event") /* UNSPECIFIED */,
-           context->storage->InternString("Dequeue") /* DEQUEUE */,
-           context->storage->InternString("Queue") /* QUEUE */,
-           context->storage->InternString("Post") /* POST */,
-           context->storage->InternString(
-               "AcquireFenceSignaled") /* ACQUIRE_FENCE */,
-           context->storage->InternString("Latch") /* LATCH */,
-           context->storage->InternString(
-               "HWCCompositionQueued") /* HWC_COMPOSITION_QUEUED */,
-           context->storage->InternString(
-               "FallbackComposition") /* FALLBACK_COMPOSITION */,
-           context->storage->InternString(
-               "PresentFenceSignaled") /* PRESENT_FENCE */,
-           context->storage->InternString(
-               "ReleaseFenceSignaled") /* RELEASE_FENCE */,
-           context->storage->InternString("Modify") /* MODIFY */,
-           context->storage->InternString("Detach") /* DETACH */,
-           context->storage->InternString("Attach") /* ATTACH */,
-           context->storage->InternString("Cancel") /* CANCEL */}},
-      present_frame_name_(present_frame_buffer_,
-                          base::ArraySize(present_frame_buffer_)),
-      present_frame_layer_name_(present_frame_layer_buffer_,
-                                base::ArraySize(present_frame_layer_buffer_)),
-      present_frame_numbers_(present_frame_numbers_buffer_,
-                             base::ArraySize(present_frame_numbers_buffer_)),
       gpu_log_track_name_id_(context_->storage->InternString("GPU Log")),
       gpu_log_scope_id_(context_->storage->InternString("gpu_log")),
       tag_id_(context_->storage->InternString("tag")),
@@ -146,7 +114,7 @@
       vk_event_scope_id_(context->storage->InternString("vulkan_events")),
       vk_queue_submit_id_(context->storage->InternString("vkQueueSubmit")) {}
 
-void GraphicsEventParser::ParseGpuCounterEvent(int64_t ts, ConstBytes blob) {
+void GpuEventParser::ParseGpuCounterEvent(int64_t ts, ConstBytes blob) {
   protos::pbzero::GpuCounterEvent::Decoder event(blob.data, blob.size);
 
   protos::pbzero::GpuCounterDescriptor::Decoder descriptor(
@@ -246,7 +214,7 @@
   }
 }
 
-const StringId GraphicsEventParser::GetFullStageName(
+const StringId GpuEventParser::GetFullStageName(
     const protos::pbzero::GpuRenderStageEvent_Decoder& event) const {
   size_t stage_id = static_cast<size_t>(event.stage_id());
   StringId stage_name;
@@ -265,7 +233,7 @@
  * Create a GPU render stage track based
  * GpuRenderStageEvent.Specifications.Description.
  */
-void GraphicsEventParser::InsertGpuTrack(
+void GpuEventParser::InsertGpuTrack(
     const protos::pbzero::
         GpuRenderStageEvent_Specifications_Description_Decoder& hw_queue) {
   StringId track_name = context_->storage->InternString(hw_queue.name());
@@ -306,7 +274,7 @@
   }
   ++gpu_hw_queue_counter_;
 }
-base::Optional<std::string> GraphicsEventParser::FindDebugName(
+base::Optional<std::string> GpuEventParser::FindDebugName(
     int32_t vk_object_type,
     uint64_t vk_handle) const {
   auto map = debug_marker_names_.find(vk_object_type);
@@ -322,8 +290,7 @@
   }
 }
 
-void GraphicsEventParser::ParseGpuRenderStageEvent(int64_t ts,
-                                                   ConstBytes blob) {
+void GpuEventParser::ParseGpuRenderStageEvent(int64_t ts, ConstBytes blob) {
   protos::pbzero::GpuRenderStageEvent::Decoder event(blob.data, blob.size);
 
   if (event.has_specifications()) {
@@ -395,21 +362,24 @@
       gpu_hw_queue_ids_[hw_queue_id] = track_id;
     }
 
-    auto render_target_name = FindDebugName(VK_OBJECT_TYPE_FRAMEBUFFER, event.render_target_handle());
+    auto render_target_name =
+        FindDebugName(VK_OBJECT_TYPE_FRAMEBUFFER, event.render_target_handle());
     auto render_target_name_id = render_target_name.has_value()
-                                  ? context_->storage->InternString(
-                                      render_target_name.value().c_str())
-                                  : kNullStringId;
-    auto render_pass_name = FindDebugName(VK_OBJECT_TYPE_RENDER_PASS, event.render_pass_handle());
-    auto render_pass_name_id = render_pass_name.has_value()
-                                  ? context_->storage->InternString(
-                                      render_pass_name.value().c_str())
-                                  : kNullStringId;
-    auto command_buffer_name = FindDebugName(VK_OBJECT_TYPE_COMMAND_BUFFER, event.command_buffer_handle());
+                                     ? context_->storage->InternString(
+                                           render_target_name.value().c_str())
+                                     : kNullStringId;
+    auto render_pass_name =
+        FindDebugName(VK_OBJECT_TYPE_RENDER_PASS, event.render_pass_handle());
+    auto render_pass_name_id =
+        render_pass_name.has_value()
+            ? context_->storage->InternString(render_pass_name.value().c_str())
+            : kNullStringId;
+    auto command_buffer_name = FindDebugName(VK_OBJECT_TYPE_COMMAND_BUFFER,
+                                             event.command_buffer_handle());
     auto command_buffer_name_id = command_buffer_name.has_value()
-                                  ? context_->storage->InternString(
-                                      command_buffer_name.value().c_str())
-                                  : kNullStringId;
+                                      ? context_->storage->InternString(
+                                            command_buffer_name.value().c_str())
+                                      : kNullStringId;
 
     tables::GpuSliceTable::Row row;
     row.ts = ts;
@@ -430,203 +400,7 @@
   }
 }
 
-void GraphicsEventParser::ParseGraphicsFrameEvent(int64_t timestamp,
-                                                  ConstBytes blob) {
-  using GraphicsFrameEvent = protos::pbzero::GraphicsFrameEvent;
-  protos::pbzero::GraphicsFrameEvent_Decoder frame_event(blob.data, blob.size);
-  if (!frame_event.has_buffer_event()) {
-    return;
-  }
-
-  ConstBytes bufferBlob = frame_event.buffer_event();
-  protos::pbzero::GraphicsFrameEvent_BufferEvent_Decoder event(bufferBlob.data,
-                                                               bufferBlob.size);
-
-  if (!event.has_buffer_id()) {
-    context_->storage->IncrementStats(
-        stats::graphics_frame_event_parser_errors);
-    PERFETTO_ELOG("GraphicsFrameEvent with missing buffer id field.");
-    return;
-  }
-
-  StringId event_name_id = unknown_event_name_id_;
-  if (event.has_type()) {
-    const auto type = static_cast<size_t>(event.type());
-    if (type < event_type_name_ids_.size()) {
-      event_name_id = event_type_name_ids_[type];
-      graphics_frame_stats_map_[event.buffer_id()][type] = timestamp;
-    } else {
-      context_->storage->IncrementStats(
-          stats::graphics_frame_event_parser_errors);
-      PERFETTO_ELOG("GraphicsFrameEvent with unknown type %zu.", type);
-    }
-  } else {
-    context_->storage->IncrementStats(
-        stats::graphics_frame_event_parser_errors);
-    PERFETTO_ELOG("GraphicsFrameEvent with missing type field.");
-  }
-
-  const uint32_t buffer_id = event.buffer_id();
-  StringId layer_name_id;
-
-  char buffer[4096];
-  const size_t layerNameMaxLength = 4000;
-  base::StringWriter track_name(buffer, sizeof(buffer));
-  if (event.has_layer_name()) {
-    const base::StringView layer_name(event.layer_name());
-    layer_name_id = context_->storage->InternString(layer_name);
-    track_name.AppendString(layer_name.substr(0, layerNameMaxLength));
-  } else {
-    layer_name_id = no_layer_name_name_id_;
-    track_name.AppendLiteral("unknown_layer");
-  }
-  track_name.AppendLiteral("[buffer:");
-  track_name.AppendUnsignedInt(buffer_id);
-  track_name.AppendChar(']');
-
-  const StringId track_name_id =
-      context_->storage->InternString(track_name.GetStringView());
-  const int64_t duration =
-      event.has_duration_ns() ? static_cast<int64_t>(event.duration_ns()) : 0;
-  const uint32_t frame_number =
-      event.has_frame_number() ? event.frame_number() : 0;
-
-  tables::GpuTrackTable::Row track(track_name_id);
-  track.scope = graphics_event_scope_id_;
-  TrackId track_id = context_->track_tracker->InternGpuTrack(track);
-
-  {
-    char frame_number_buffer[256];
-    base::StringWriter frame_numbers(frame_number_buffer,
-                                     base::ArraySize(frame_number_buffer));
-    frame_numbers.AppendUnsignedInt(frame_number);
-
-    tables::GraphicsFrameSliceTable::Row row;
-    row.ts = timestamp;
-    row.track_id = track_id;
-    row.name = event_name_id;
-    row.dur = duration;
-    row.frame_numbers =
-        context_->storage->InternString(frame_numbers.GetStringView());
-    row.layer_names = layer_name_id;
-    context_->slice_tracker->ScopedFrameEvent(row);
-  }
-
-  /* Displayed Frame track */
-  if (event.type() == GraphicsFrameEvent::PRESENT_FENCE) {
-    // Insert the frame stats for the buffer that was presented
-    auto acquire_ts =
-        graphics_frame_stats_map_[event.buffer_id()]
-                                 [GraphicsFrameEvent::ACQUIRE_FENCE];
-    auto queue_ts =
-        graphics_frame_stats_map_[event.buffer_id()][GraphicsFrameEvent::QUEUE];
-    auto latch_ts =
-        graphics_frame_stats_map_[event.buffer_id()][GraphicsFrameEvent::LATCH];
-    tables::GraphicsFrameStatsTable::Row stats_row;
-    // AcquireFence can signal before Queue sometimes, so have 0 as a bound.
-    stats_row.queue_to_acquire_time =
-        std::max(acquire_ts - queue_ts, static_cast<int64_t>(0));
-    stats_row.acquire_to_latch_time = latch_ts - acquire_ts;
-    stats_row.latch_to_present_time = timestamp - latch_ts;
-    auto stats_row_id =
-        context_->storage->mutable_graphics_frame_stats_table()->Insert(
-            stats_row);
-
-    if (previous_timestamp_ == 0) {
-      const StringId present_track_name_id =
-          context_->storage->InternString("Displayed Frame");
-      tables::GpuTrackTable::Row present_track(present_track_name_id);
-      present_track.scope = graphics_event_scope_id_;
-      present_track_id_ =
-          context_->track_tracker->InternGpuTrack(present_track);
-    }
-
-    // The displayed frame is a slice from one present fence to another present
-    // fence. If multiple buffers have present fence at the same time, they all
-    // are shown on screen at the same time. So that particular displayed frame
-    // slice should include info from all those buffers in it.
-    // Since the events are parsed one by one, the following bookkeeping is
-    // needed to create the slice properly.
-    if (previous_timestamp_ == timestamp && previous_timestamp_ != 0) {
-      // Same timestamp as previous present fence. This buffer should also
-      // contribute to this slice.
-      present_frame_name_.AppendLiteral(", ");
-      present_frame_name_.AppendUnsignedInt(buffer_id);
-
-      // Append Layer names
-      present_frame_layer_name_.AppendLiteral(", ");
-      present_frame_layer_name_.AppendString(event.layer_name());
-
-      // Append Frame numbers
-      present_frame_numbers_.AppendLiteral(", ");
-      present_frame_numbers_.AppendUnsignedInt(frame_number);
-
-      // Add the current stats row to the list of stats that go with this frame
-      graphics_frame_stats_idx_.push_back(stats_row_id.row);
-    } else {
-
-      if (previous_timestamp_ != 0) {
-        StringId present_frame_layer_name_id = context_->storage->InternString(
-            present_frame_layer_name_.GetStringView());
-        // End the current slice that's being tracked.
-        const auto opt_slice_id = context_->slice_tracker->EndFrameEvent(
-            timestamp, present_track_id_);
-
-        if (opt_slice_id) {
-          // The slice could have had additional buffers in it, so we need to
-          // update the table.
-          auto* graphics_frame_slice_table =
-              context_->storage->mutable_graphics_frame_slice_table();
-
-          uint32_t row_idx =
-              *graphics_frame_slice_table->id().IndexOf(*opt_slice_id);
-          StringId frame_name_id = context_->storage->InternString(
-              present_frame_name_.GetStringView());
-          graphics_frame_slice_table->mutable_name()->Set(row_idx,
-                                                          frame_name_id);
-
-          StringId present_frame_numbers_id = context_->storage->InternString(
-              present_frame_numbers_.GetStringView());
-          graphics_frame_slice_table->mutable_frame_numbers()->Set(
-              row_idx, present_frame_numbers_id);
-          graphics_frame_slice_table->mutable_layer_names()->Set(
-              row_idx, present_frame_layer_name_id);
-
-          // Set the slice_id for the frame_stats rows under this displayed
-          // frame
-          auto* slice_table = context_->storage->mutable_slice_table();
-          uint32_t slice_idx = *slice_table->id().IndexOf(*opt_slice_id);
-          for (uint32_t i = 0; i < graphics_frame_stats_idx_.size(); i++) {
-            context_->storage->mutable_graphics_frame_stats_table()
-                ->mutable_slice_id()
-                ->Set(graphics_frame_stats_idx_[i], slice_idx);
-          }
-        }
-        present_frame_layer_name_.reset();
-        present_frame_name_.reset();
-        present_frame_numbers_.reset();
-        graphics_frame_stats_idx_.clear();
-      }
-
-      // Start a new slice
-      present_frame_name_.AppendUnsignedInt(buffer_id);
-      previous_timestamp_ = timestamp;
-      present_frame_layer_name_.AppendString(event.layer_name());
-      present_frame_numbers_.AppendUnsignedInt(frame_number);
-      present_event_name_id_ =
-          context_->storage->InternString(present_frame_name_.GetStringView());
-      graphics_frame_stats_idx_.push_back(stats_row_id.row);
-
-      tables::GraphicsFrameSliceTable::Row row;
-      row.ts = timestamp;
-      row.track_id = present_track_id_;
-      row.name = present_event_name_id_;
-      context_->slice_tracker->BeginFrameEvent(row);
-    }
-  }
-}
-
-void GraphicsEventParser::UpdateVulkanMemoryAllocationCounters(
+void GpuEventParser::UpdateVulkanMemoryAllocationCounters(
     UniquePid upid,
     const VulkanMemoryEvent::Decoder& event) {
   StringId track_str_id = kNullStringId;
@@ -722,7 +496,7 @@
   }
 }
 
-void GraphicsEventParser::ParseVulkanMemoryEvent(
+void GpuEventParser::ParseVulkanMemoryEvent(
     PacketSequenceStateGeneration* sequence_state,
     ConstBytes blob) {
   using protos::pbzero::InternedData;
@@ -803,7 +577,7 @@
   }
 }
 
-void GraphicsEventParser::ParseGpuLog(int64_t ts, ConstBytes blob) {
+void GpuEventParser::ParseGpuLog(int64_t ts, ConstBytes blob) {
   protos::pbzero::GpuLog::Decoder event(blob.data, blob.size);
 
   tables::GpuTrackTable::Row track(gpu_log_track_name_id_);
@@ -837,7 +611,7 @@
   context_->slice_tracker->ScopedGpu(row, args_callback);
 }
 
-void GraphicsEventParser::ParseVulkanApiEvent(int64_t ts, ConstBytes blob) {
+void GpuEventParser::ParseVulkanApiEvent(int64_t ts, ConstBytes blob) {
   protos::pbzero::VulkanApiEvent::Decoder vk_event(blob.data, blob.size);
   if (vk_event.has_vk_debug_utils_object_name()) {
     protos::pbzero::VulkanApiEvent_VkDebugUtilsObjectName::Decoder event(
diff --git a/src/trace_processor/importers/proto/graphics_event_parser.h b/src/trace_processor/importers/proto/gpu_event_parser.h
similarity index 76%
rename from src/trace_processor/importers/proto/graphics_event_parser.h
rename to src/trace_processor/importers/proto/gpu_event_parser.h
index 93224d4..175f62c 100644
--- a/src/trace_processor/importers/proto/graphics_event_parser.h
+++ b/src/trace_processor/importers/proto/gpu_event_parser.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GRAPHICS_EVENT_PARSER_H_
-#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GRAPHICS_EVENT_PARSER_H_
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GPU_EVENT_PARSER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GPU_EVENT_PARSER_H_
 
 #include <vector>
 
@@ -52,16 +52,15 @@
 };
 
 // Class for parsing graphics related events.
-class GraphicsEventParser {
+class GpuEventParser {
  public:
   using ConstBytes = protozero::ConstBytes;
   using VulkanMemoryEventSource = VulkanMemoryEvent::Source;
   using VulkanMemoryEventOperation = VulkanMemoryEvent::Operation;
-  explicit GraphicsEventParser(TraceProcessorContext*);
+  explicit GpuEventParser(TraceProcessorContext*);
 
   void ParseGpuCounterEvent(int64_t ts, ConstBytes);
   void ParseGpuRenderStageEvent(int64_t ts, ConstBytes);
-  void ParseGraphicsFrameEvent(int64_t timestamp, ConstBytes);
   void ParseGpuLog(int64_t ts, ConstBytes);
 
   void ParseVulkanMemoryEvent(PacketSequenceStateGeneration*, ConstBytes);
@@ -90,27 +89,6 @@
   size_t gpu_hw_queue_counter_ = 0;
   // Map of stage ID -> pair(stage name, stage description)
   std::vector<std::pair<StringId, StringId>> gpu_render_stage_ids_;
-  // For GraphicsFrameEvent
-  const StringId graphics_event_scope_id_;
-  const StringId unknown_event_name_id_;
-  const StringId no_layer_name_name_id_;
-  const StringId layer_name_key_id_;
-  std::array<StringId, 14> event_type_name_ids_;
-  int64_t previous_timestamp_ = 0;
-  char present_frame_buffer_[4096];
-  char present_frame_layer_buffer_[4096];
-  char present_frame_numbers_buffer_[4096];
-  StringId present_event_name_id_;
-  base::StringWriter present_frame_name_;
-  base::StringWriter present_frame_layer_name_;
-  base::StringWriter present_frame_numbers_;
-  TrackId present_track_id_;
-  // Row indices of frame stats table. Used to populate the slice_id after
-  // inserting the rows.
-  std::vector<uint32_t> graphics_frame_stats_idx_;
-  // Map of buffer ID -> (Map of GraphicsFrameEvent -> ts of that event)
-  std::unordered_map<uint32_t, std::unordered_map<uint64_t, int64_t>>
-      graphics_frame_stats_map_;
   // For VulkanMemoryEvent
   std::unordered_map<VulkanMemoryEvent::AllocationScope,
                      int64_t /*counter_value*/,
@@ -140,4 +118,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GRAPHICS_EVENT_PARSER_H_
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GPU_EVENT_PARSER_H_
diff --git a/src/trace_processor/importers/proto/graphics_event_module.cc b/src/trace_processor/importers/proto/graphics_event_module.cc
index f003464..432b3bd 100644
--- a/src/trace_processor/importers/proto/graphics_event_module.cc
+++ b/src/trace_processor/importers/proto/graphics_event_module.cc
@@ -22,7 +22,7 @@
 using perfetto::protos::pbzero::TracePacket;
 
 GraphicsEventModule::GraphicsEventModule(TraceProcessorContext* context)
-    : parser_(context) {
+    : parser_(context), frame_parser_(context) {
   RegisterForField(TracePacket::kGpuCounterEventFieldNumber, context);
   RegisterForField(TracePacket::kGpuRenderStageEventFieldNumber, context);
   RegisterForField(TracePacket::kGpuLogFieldNumber, context);
@@ -48,8 +48,8 @@
       parser_.ParseGpuLog(ttp.timestamp, decoder.gpu_log());
       return;
     case TracePacket::kGraphicsFrameEventFieldNumber:
-      parser_.ParseGraphicsFrameEvent(ttp.timestamp,
-                                      decoder.graphics_frame_event());
+      frame_parser_.ParseGraphicsFrameEvent(ttp.timestamp,
+                                            decoder.graphics_frame_event());
       return;
     case TracePacket::kVulkanMemoryEventFieldNumber:
       PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
diff --git a/src/trace_processor/importers/proto/graphics_event_module.h b/src/trace_processor/importers/proto/graphics_event_module.h
index f7a36dd..f84b2c5 100644
--- a/src/trace_processor/importers/proto/graphics_event_module.h
+++ b/src/trace_processor/importers/proto/graphics_event_module.h
@@ -18,7 +18,8 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GRAPHICS_EVENT_MODULE_H_
 
 #include "perfetto/base/build_config.h"
-#include "src/trace_processor/importers/proto/graphics_event_parser.h"
+#include "src/trace_processor/importers/proto/gpu_event_parser.h"
+#include "src/trace_processor/importers/proto/graphics_frame_event_parser.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
 #include "src/trace_processor/timestamped_trace_piece.h"
 
@@ -38,7 +39,8 @@
                    uint32_t field_id) override;
 
  private:
-  GraphicsEventParser parser_;
+  GpuEventParser parser_;
+  GraphicsFrameEventParser frame_parser_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/graphics_frame_event_parser.cc b/src/trace_processor/importers/proto/graphics_frame_event_parser.cc
new file mode 100644
index 0000000..e33ed9c
--- /dev/null
+++ b/src/trace_processor/importers/proto/graphics_frame_event_parser.cc
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/importers/proto/graphics_frame_event_parser.h"
+
+#include <inttypes.h>
+
+#include "perfetto/ext/base/utils.h"
+#include "perfetto/protozero/field.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+constexpr char kQueueLostMessage[] =
+    "Missing queue event. The slice is now a bit extended than it might "
+    "actually have been";
+GraphicsFrameEventParser::GraphicsFrameEventParser(
+    TraceProcessorContext* context)
+    : context_(context),
+      graphics_event_scope_id_(
+          context->storage->InternString("graphics_frame_event")),
+      unknown_event_name_id_(context->storage->InternString("unknown_event")),
+      no_layer_name_name_id_(context->storage->InternString("no_layer_name")),
+      layer_name_key_id_(context->storage->InternString("layer_name")),
+      event_type_name_ids_{
+          {context->storage->InternString(
+               "unspecified_event") /* UNSPECIFIED */,
+           context->storage->InternString("Dequeue") /* DEQUEUE */,
+           context->storage->InternString("Queue") /* QUEUE */,
+           context->storage->InternString("Post") /* POST */,
+           context->storage->InternString(
+               "AcquireFenceSignaled") /* ACQUIRE_FENCE */,
+           context->storage->InternString("Latch") /* LATCH */,
+           context->storage->InternString(
+               "HWCCompositionQueued") /* HWC_COMPOSITION_QUEUED */,
+           context->storage->InternString(
+               "FallbackComposition") /* FALLBACK_COMPOSITION */,
+           context->storage->InternString(
+               "PresentFenceSignaled") /* PRESENT_FENCE */,
+           context->storage->InternString(
+               "ReleaseFenceSignaled") /* RELEASE_FENCE */,
+           context->storage->InternString("Modify") /* MODIFY */,
+           context->storage->InternString("Detach") /* DETACH */,
+           context->storage->InternString("Attach") /* ATTACH */,
+           context->storage->InternString("Cancel") /* CANCEL */}},
+      queue_lost_message_id_(
+          context->storage->InternString(kQueueLostMessage)) {}
+
+bool GraphicsFrameEventParser::CreateBufferEvent(
+    int64_t timestamp,
+    GraphicsFrameEventDecoder& event) {
+  if (!event.has_buffer_id()) {
+    context_->storage->IncrementStats(
+        stats::graphics_frame_event_parser_errors);
+    PERFETTO_ELOG("GraphicsFrameEvent with missing buffer id field.");
+    return false;
+  }
+
+  StringId event_name_id = unknown_event_name_id_;
+  if (event.has_type()) {
+    const auto type = static_cast<size_t>(event.type());
+    if (type < event_type_name_ids_.size()) {
+      event_name_id = event_type_name_ids_[type];
+      graphics_frame_stats_map_[event.buffer_id()][type] = timestamp;
+    } else {
+      context_->storage->IncrementStats(
+          stats::graphics_frame_event_parser_errors);
+      PERFETTO_ELOG("GraphicsFrameEvent with unknown type %zu.", type);
+    }
+  } else {
+    context_->storage->IncrementStats(
+        stats::graphics_frame_event_parser_errors);
+    PERFETTO_ELOG("GraphicsFrameEvent with missing type field.");
+  }
+
+  const uint32_t buffer_id = event.buffer_id();
+  StringId layer_name_id;
+
+  if (event.has_layer_name()) {
+    auto layer_name_str = event.layer_name();
+    const base::StringView layer_name(layer_name_str);
+    layer_name_id = context_->storage->InternString(layer_name);
+  } else {
+    layer_name_id = no_layer_name_name_id_;
+  }
+  char buffer[4096];
+  base::StringWriter track_name(buffer, sizeof(buffer));
+  track_name.AppendLiteral("Buffer: ");
+  track_name.AppendUnsignedInt(buffer_id);
+
+  const StringId track_name_id =
+      context_->storage->InternString(track_name.GetStringView());
+  const int64_t duration =
+      event.has_duration_ns() ? static_cast<int64_t>(event.duration_ns()) : 0;
+  uint32_t frame_number = event.has_frame_number() ? event.frame_number() : 0;
+
+  tables::GpuTrackTable::Row track(track_name_id);
+  track.scope = graphics_event_scope_id_;
+  TrackId track_id = context_->track_tracker->InternGpuTrack(track);
+
+  {
+    tables::GraphicsFrameSliceTable::Row row;
+    row.ts = timestamp;
+    row.track_id = track_id;
+    row.name = event_name_id;
+    row.dur = duration;
+    row.frame_number = frame_number;
+    row.layer_name = layer_name_id;
+    if (event.type() == GraphicsFrameEvent::PRESENT_FENCE) {
+      auto acquire_ts =
+          graphics_frame_stats_map_[event.buffer_id()]
+                                   [GraphicsFrameEvent::ACQUIRE_FENCE];
+      auto queue_ts = graphics_frame_stats_map_[event.buffer_id()]
+                                               [GraphicsFrameEvent::QUEUE];
+      auto latch_ts = graphics_frame_stats_map_[event.buffer_id()]
+                                               [GraphicsFrameEvent::LATCH];
+
+      row.queue_to_acquire_time =
+          std::max(acquire_ts - queue_ts, static_cast<int64_t>(0));
+      row.acquire_to_latch_time = latch_ts - acquire_ts;
+      row.latch_to_present_time = timestamp - latch_ts;
+    }
+    auto slice_id = context_->slice_tracker->ScopedFrameEvent(row);
+    if (event.type() == GraphicsFrameEvent::DEQUEUE) {
+      dequeue_slice_ids_[buffer_id] = slice_id;
+    } else if (event.type() == GraphicsFrameEvent::QUEUE) {
+      auto it = dequeue_slice_ids_.find(buffer_id);
+      if (it != dequeue_slice_ids_.end()) {
+        auto dequeue_slice_id = it->second;
+        auto* graphics_frame_slice_table =
+            context_->storage->mutable_graphics_frame_slice_table();
+        uint32_t row_idx =
+            *graphics_frame_slice_table->id().IndexOf(dequeue_slice_id);
+        graphics_frame_slice_table->mutable_frame_number()->Set(row_idx,
+                                                                frame_number);
+      }
+    }
+  }
+  return true;
+}
+
+// Here we convert the buffer events into Phases(slices)
+// APP: Dequeue to Queue
+// Wait for GPU: Queue to Acquire
+// SurfaceFlinger (SF): Latch to Present
+// Display: Present to next Present (of the same layer)
+void GraphicsFrameEventParser::CreatePhaseEvent(
+    int64_t timestamp,
+    GraphicsFrameEventDecoder& event) {
+  const uint32_t buffer_id = event.buffer_id();
+  uint32_t frame_number = event.has_frame_number() ? event.frame_number() : 0;
+  StringId layer_name_id;
+  if (event.has_layer_name()) {
+    auto layer_name_str = event.layer_name();
+    const base::StringView layer_name(layer_name_str);
+    layer_name_id = context_->storage->InternString(layer_name);
+  } else {
+    layer_name_id = no_layer_name_name_id_;
+  }
+  char track_buffer[4096];
+  char slice_buffer[4096];
+  // We'll be using the name StringWriter and name_id for writing track names
+  // and slice names.
+  base::StringWriter track_name(track_buffer, sizeof(track_buffer));
+  base::StringWriter slice_name(slice_buffer, sizeof(slice_buffer));
+  StringId track_name_id;
+  TrackId track_id;
+  bool start_slice = true;
+
+  // Close the previous phase before starting the new phase
+  switch (event.type()) {
+    case GraphicsFrameEvent::DEQUEUE: {
+      track_name.reset();
+      track_name.AppendLiteral("APP_");
+      track_name.AppendUnsignedInt(buffer_id);
+      track_name_id =
+          context_->storage->InternString(track_name.GetStringView());
+      tables::GpuTrackTable::Row app_track(track_name_id);
+      app_track.scope = graphics_event_scope_id_;
+      track_id = context_->track_tracker->InternGpuTrack(app_track);
+      dequeue_map_[buffer_id] = track_id;
+      last_dequeued_[buffer_id] = timestamp;
+      break;
+    }
+
+    case GraphicsFrameEvent::QUEUE: {
+      auto dequeueTime = dequeue_map_.find(buffer_id);
+      if (dequeueTime != dequeue_map_.end()) {
+        const auto opt_slice_id = context_->slice_tracker->EndFrameEvent(
+            timestamp, dequeueTime->second);
+        slice_name.reset();
+        slice_name.AppendUnsignedInt(frame_number);
+        if (opt_slice_id) {
+          auto* graphics_frame_slice_table =
+              context_->storage->mutable_graphics_frame_slice_table();
+          // Set the name of the slice to be the frame number since dequeue did
+          // not have a frame number at that time.
+          uint32_t row_idx =
+              *graphics_frame_slice_table->id().IndexOf(*opt_slice_id);
+          StringId frame_name_id =
+              context_->storage->InternString(slice_name.GetStringView());
+          graphics_frame_slice_table->mutable_name()->Set(row_idx,
+                                                          frame_name_id);
+          graphics_frame_slice_table->mutable_frame_number()->Set(row_idx,
+                                                                  frame_number);
+          dequeue_map_.erase(dequeueTime);
+        }
+      }
+      // The AcquireFence might be signaled before receiving a QUEUE event
+      // sometimes. In that case, we shouldn't start a slice.
+      if (last_acquired_[buffer_id] > last_dequeued_[buffer_id] &&
+          last_acquired_[buffer_id] < timestamp) {
+        start_slice = false;
+        break;
+      }
+      track_name.reset();
+      track_name.AppendLiteral("GPU_");
+      track_name.AppendUnsignedInt(buffer_id);
+      track_name_id =
+          context_->storage->InternString(track_name.GetStringView());
+      tables::GpuTrackTable::Row gpu_track(track_name_id);
+      gpu_track.scope = graphics_event_scope_id_;
+      track_id = context_->track_tracker->InternGpuTrack(gpu_track);
+      queue_map_[buffer_id] = track_id;
+      break;
+    }
+    case GraphicsFrameEvent::ACQUIRE_FENCE: {
+      auto queueTime = queue_map_.find(buffer_id);
+      if (queueTime != queue_map_.end()) {
+        context_->slice_tracker->EndFrameEvent(timestamp, queueTime->second);
+        queue_map_.erase(queueTime);
+      }
+      last_acquired_[buffer_id] = timestamp;
+      start_slice = false;
+      break;
+    }
+    case GraphicsFrameEvent::LATCH: {
+      // b/157578286 - Sometimes Queue event goes missing. To prevent having a
+      // wrong slice info, we try to close any existing APP slice.
+      auto dequeueTime = dequeue_map_.find(buffer_id);
+      if (dequeueTime != dequeue_map_.end()) {
+        auto args_callback = [this](ArgsTracker::BoundInserter* inserter) {
+          inserter->AddArg(context_->storage->InternString("Details"),
+                           Variadic::String(queue_lost_message_id_));
+        };
+        const auto opt_slice_id = context_->slice_tracker->EndFrameEvent(
+            timestamp, dequeueTime->second, args_callback);
+        slice_name.reset();
+        slice_name.AppendUnsignedInt(frame_number);
+        if (opt_slice_id) {
+          auto* graphics_frame_slice_table =
+              context_->storage->mutable_graphics_frame_slice_table();
+          // Set the name of the slice to be the frame number since dequeue did
+          // not have a frame number at that time.
+          uint32_t row_idx =
+              *graphics_frame_slice_table->id().IndexOf(*opt_slice_id);
+          StringId frame_name_id =
+              context_->storage->InternString(slice_name.GetStringView());
+          graphics_frame_slice_table->mutable_name()->Set(row_idx,
+                                                          frame_name_id);
+          graphics_frame_slice_table->mutable_frame_number()->Set(row_idx,
+                                                                  frame_number);
+          dequeue_map_.erase(dequeueTime);
+        }
+      }
+      track_name.reset();
+      track_name.AppendLiteral("SF_");
+      track_name.AppendUnsignedInt(buffer_id);
+      track_name_id =
+          context_->storage->InternString(track_name.GetStringView());
+      tables::GpuTrackTable::Row sf_track(track_name_id);
+      sf_track.scope = graphics_event_scope_id_;
+      track_id = context_->track_tracker->InternGpuTrack(sf_track);
+      latch_map_[buffer_id] = track_id;
+      break;
+    }
+
+    case GraphicsFrameEvent::PRESENT_FENCE: {
+      auto latchTime = latch_map_.find(buffer_id);
+      if (latchTime != latch_map_.end()) {
+        context_->slice_tracker->EndFrameEvent(timestamp, latchTime->second);
+        latch_map_.erase(latchTime);
+      }
+      auto displayTime = display_map_.find(layer_name_id);
+      if (displayTime != display_map_.end()) {
+        context_->slice_tracker->EndFrameEvent(timestamp, displayTime->second);
+        display_map_.erase(displayTime);
+      }
+      base::StringView layerName(event.layer_name());
+      track_name.reset();
+      track_name.AppendLiteral("Display_");
+      track_name.AppendString(layerName.substr(0, 10));
+      track_name_id =
+          context_->storage->InternString(track_name.GetStringView());
+      tables::GpuTrackTable::Row display_track(track_name_id);
+      display_track.scope = graphics_event_scope_id_;
+      track_id = context_->track_tracker->InternGpuTrack(display_track);
+      display_map_[layer_name_id] = track_id;
+      break;
+    }
+
+    default:
+      start_slice = false;
+  }
+
+  // Start the new phase if needed.
+  if (start_slice) {
+    tables::GraphicsFrameSliceTable::Row slice;
+    slice.ts = timestamp;
+    slice.track_id = track_id;
+    slice.layer_name = layer_name_id;
+    slice_name.reset();
+    // If the frame_number is known, set it as the name of the slice.
+    // If not known (DEQUEUE), set the name as the timestamp.
+    // Timestamp is chosen here because the stack_id is hashed based on the name
+    // of the slice. To not have any conflicting stack_id with any of the
+    // existing slices, we use timestamp as the temporary name.
+    if (frame_number != 0) {
+      slice_name.AppendUnsignedInt(frame_number);
+    } else {
+      slice_name.AppendInt(timestamp);
+    }
+    slice.name = context_->storage->InternString(slice_name.GetStringView());
+    slice.frame_number = frame_number;
+    context_->slice_tracker->BeginFrameEvent(slice);
+  }
+}
+
+void GraphicsFrameEventParser::ParseGraphicsFrameEvent(int64_t timestamp,
+                                                       ConstBytes blob) {
+  protos::pbzero::GraphicsFrameEvent_Decoder frame_event(blob.data, blob.size);
+  if (!frame_event.has_buffer_event()) {
+    return;
+  }
+
+  ConstBytes bufferBlob = frame_event.buffer_event();
+  protos::pbzero::GraphicsFrameEvent_BufferEvent_Decoder event(bufferBlob.data,
+                                                               bufferBlob.size);
+  if (CreateBufferEvent(timestamp, event)) {
+    // Create a phase event only if the buffer event finishes successfully
+    CreatePhaseEvent(timestamp, event);
+  }
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/graphics_frame_event_parser.h b/src/trace_processor/importers/proto/graphics_frame_event_parser.h
new file mode 100644
index 0000000..e23fa73
--- /dev/null
+++ b/src/trace_processor/importers/proto/graphics_frame_event_parser.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GRAPHICS_FRAME_EVENT_PARSER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GRAPHICS_FRAME_EVENT_PARSER_H_
+
+#include <vector>
+
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_writer.h"
+#include "perfetto/protozero/field.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/proto/proto_incremental_state.h"
+#include "src/trace_processor/importers/proto/vulkan_memory_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+#include "protos/perfetto/trace/android/graphics_frame_event.pbzero.h"
+
+namespace perfetto {
+
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+// Class for parsing graphics frame related events.
+class GraphicsFrameEventParser {
+ public:
+  using ConstBytes = protozero::ConstBytes;
+  explicit GraphicsFrameEventParser(TraceProcessorContext*);
+
+  void ParseGraphicsFrameEvent(int64_t timestamp, ConstBytes);
+
+ private:
+  using GraphicsFrameEventDecoder =
+      protos::pbzero::GraphicsFrameEvent_BufferEvent_Decoder;
+  using GraphicsFrameEvent = protos::pbzero::GraphicsFrameEvent;
+  bool CreateBufferEvent(int64_t timestamp, GraphicsFrameEventDecoder& event);
+  void CreatePhaseEvent(int64_t timestamp, GraphicsFrameEventDecoder& event);
+
+  TraceProcessorContext* const context_;
+  const StringId graphics_event_scope_id_;
+  const StringId unknown_event_name_id_;
+  const StringId no_layer_name_name_id_;
+  const StringId layer_name_key_id_;
+  std::array<StringId, 14> event_type_name_ids_;
+  const StringId queue_lost_message_id_;
+  // Map of buffer ID -> slice id of the dequeue event
+  std::unordered_map<uint32_t, SliceId> dequeue_slice_ids_;
+
+  // Row indices of frame stats table. Used to populate the slice_id after
+  // inserting the rows.
+  std::vector<uint32_t> graphics_frame_stats_idx_;
+  // Map of buffer ID -> (Map of GraphicsFrameEvent -> ts of that event)
+  std::unordered_map<uint32_t, std::unordered_map<uint64_t, int64_t>>
+      graphics_frame_stats_map_;
+
+  // Maps of buffer id -> track id
+  std::unordered_map<uint32_t, TrackId> dequeue_map_;
+  std::unordered_map<uint32_t, TrackId> queue_map_;
+  std::unordered_map<uint32_t, TrackId> latch_map_;
+  // Map of layer name -> track id
+  std::unordered_map<StringId, TrackId> display_map_;
+
+  // Maps of buffer id -> timestamp
+  std::unordered_map<uint32_t, int64_t> last_dequeued_;
+  std::unordered_map<uint32_t, int64_t> last_acquired_;
+};
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GRAPHICS_FRAME_EVENT_PARSER_H_
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index 6846bd5..290de0d 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -593,14 +593,6 @@
     return &graphics_frame_slice_table_;
   }
 
-  const tables::GraphicsFrameStatsTable& graphics_frame_stats_table() const {
-    return graphics_frame_stats_table_;
-  }
-
-  tables::GraphicsFrameStatsTable* mutable_graphics_frame_stats_table() {
-    return &graphics_frame_stats_table_;
-  }
-
   const StringPool& string_pool() const { return string_pool_; }
   StringPool* mutable_string_pool() { return &string_pool_; }
 
@@ -815,8 +807,6 @@
 
   tables::GraphicsFrameSliceTable graphics_frame_slice_table_{&string_pool_,
                                                               &slice_table_};
-  tables::GraphicsFrameStatsTable graphics_frame_stats_table_{&string_pool_,
-                                                              nullptr};
 
   // The below array allow us to map between enums and their string
   // representations.
diff --git a/src/trace_processor/tables/slice_tables.h b/src/trace_processor/tables/slice_tables.h
index 49eaaf1..c347dcb 100644
--- a/src/trace_processor/tables/slice_tables.h
+++ b/src/trace_processor/tables/slice_tables.h
@@ -91,23 +91,13 @@
 #define PERFETTO_TP_GRAPHICS_FRAME_SLICES_DEF(NAME, PARENT, C) \
   NAME(GraphicsFrameSliceTable, "frame_slice")                 \
   PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C)                       \
-  C(StringPool::Id, frame_numbers)                             \
-  C(StringPool::Id, layer_names)
-
-PERFETTO_TP_TABLE(PERFETTO_TP_GRAPHICS_FRAME_SLICES_DEF);
-
-// frame_slice -> frame_stats : 1 -> Many,
-// with frame_slice.id = frame_stats.slice_id
-// @tablegroup Events
-#define PERFETTO_TP_GRAPHICS_FRAME_STATS_DEF(NAME, PARENT, C) \
-  NAME(GraphicsFrameStatsTable, "frame_stats")                \
-  PERFETTO_TP_ROOT_TABLE(PARENT, C)                           \
-  C(uint32_t, slice_id)                                       \
-  C(int64_t, queue_to_acquire_time)                           \
-  C(int64_t, acquire_to_latch_time)                           \
+  C(uint32_t, frame_number)                                    \
+  C(StringPool::Id, layer_name)                                \
+  C(int64_t, queue_to_acquire_time)                            \
+  C(int64_t, acquire_to_latch_time)                            \
   C(int64_t, latch_to_present_time)
 
-PERFETTO_TP_TABLE(PERFETTO_TP_GRAPHICS_FRAME_STATS_DEF);
+PERFETTO_TP_TABLE(PERFETTO_TP_GRAPHICS_FRAME_SLICES_DEF);
 
 #define PERFETTO_TP_DESCRIBE_SLICE_TABLE(NAME, PARENT, C) \
   NAME(DescribeSliceTable, "describe_slice")              \
diff --git a/src/trace_processor/tables/table_destructors.cc b/src/trace_processor/tables/table_destructors.cc
index 1f15028..859be5d 100644
--- a/src/trace_processor/tables/table_destructors.cc
+++ b/src/trace_processor/tables/table_destructors.cc
@@ -66,7 +66,6 @@
 SchedSliceTable::~SchedSliceTable() = default;
 GpuSliceTable::~GpuSliceTable() = default;
 GraphicsFrameSliceTable::~GraphicsFrameSliceTable() = default;
-GraphicsFrameStatsTable::~GraphicsFrameStatsTable() = default;
 DescribeSliceTable::~DescribeSliceTable() = default;
 
 // track_tables.h
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index ea0764c..db4f201 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -617,7 +617,6 @@
   RegisterDbTable(storage->vulkan_memory_allocations_table());
 
   RegisterDbTable(storage->graphics_frame_slice_table());
-  RegisterDbTable(storage->graphics_frame_stats_table());
 
   RegisterDbTable(storage->metadata_table());
   RegisterDbTable(storage->cpu_table());
diff --git a/test/trace_processor/graphics_frame_events.out b/test/trace_processor/graphics_frame_events.out
index 7c1d441..8aca540 100644
--- a/test/trace_processor/graphics_frame_events.out
+++ b/test/trace_processor/graphics_frame_events.out
@@ -1,20 +1,21 @@
-"ts","track_name","dur","slice_name","frame_numbers","layer_names"
-1,"layer1[buffer:1]",6,"Dequeue","11","layer1"
-2,"layer1[buffer:1]",7,"Queue","11","layer1"
-3,"layer1[buffer:1]",8,"Post","11","layer1"
-4,"layer1[buffer:1]",9,"AcquireFenceSignaled","11","layer1"
-5,"layer1[buffer:1]",10,"Latch","11","layer1"
-6,"layer1[buffer:1]",0,"PresentFenceSignaled","11","layer1"
-6,"layer2[buffer:2]",5,"Dequeue","12","layer2"
-6,"Displayed Frame",10,"1, 5","11, 10","layer1, layer5"
-6,"layer5[buffer:5]",0,"PresentFenceSignaled","10","layer5"
-6,"layer1[buffer:2]",11,"Dequeue","12","layer1"
-7,"layer2[buffer:2]",0,"Queue","12","layer2"
-7,"layer2[buffer:2]",12,"Queue","12","layer2"
-7,"layer7[buffer:7]",12,"unknown_event","13","layer7"
-8,"layer2[buffer:2]",2,"Detach","14","layer2"
-8,"layer3[buffer:2]",13,"Post","12","layer3"
-9,"layer3[buffer:3]",5,"Attach","15","layer3"
-10,"layer3[buffer:3]",10,"Cancel","16","layer3"
-16,"Displayed Frame",-1,"6","[NULL]","[NULL]"
-16,"layer5[buffer:6]",0,"PresentFenceSignaled","17","layer5"
+"ts","track_name","dur","slice_name","frame_number","layer_name"
+1,"Buffer: 1",0,"Dequeue",11,"layer1"
+1,"APP_1",3,"11",11,"layer1"
+4,"Buffer: 1",0,"Queue",11,"layer1"
+4,"GPU_1",2,"11",11,"layer1"
+6,"Buffer: 1",0,"AcquireFenceSignaled",11,"layer1"
+6,"Buffer: 2",0,"Dequeue",12,"layer2"
+6,"APP_2",3,"12",12,"layer2"
+7,"Buffer: 7",0,"unknown_event",15,"layer7"
+8,"Buffer: 1",0,"Latch",11,"layer1"
+8,"Buffer: 2",0,"AcquireFenceSignaled",12,"layer2"
+8,"SF_1",6,"11",11,"layer1"
+9,"Buffer: 2",0,"Queue",12,"layer2"
+11,"Buffer: 2",0,"Latch",12,"layer2"
+11,"SF_2",5,"12",12,"layer2"
+14,"Buffer: 1",0,"PresentFenceSignaled",11,"layer1"
+14,"Display_layer1",10,"11",11,"layer1"
+16,"Buffer: 2",0,"PresentFenceSignaled",12,"layer2"
+16,"Display_layer2",-1,"12",12,"layer2"
+24,"Buffer: 1",0,"PresentFenceSignaled",13,"layer1"
+24,"Display_layer1",-1,"13",13,"layer1"
diff --git a/test/trace_processor/graphics_frame_events.py b/test/trace_processor/graphics_frame_events.py
index 64c0606..beaf23a 100755
--- a/test/trace_processor/graphics_frame_events.py
+++ b/test/trace_processor/graphics_frame_events.py
@@ -34,26 +34,22 @@
   CANCEL = 13
 
 trace = synth_common.create_trace()
-trace.add_buffer_event_packet(ts=1, buffer_id=1, layer_name="layer1", frame_number=11, event_type=BufferEvent.DEQUEUE, duration=6)
-trace.add_buffer_event_packet(ts=2, buffer_id=1, layer_name="layer1", frame_number=11, event_type=BufferEvent.QUEUE, duration=7)
-trace.add_buffer_event_packet(ts=3, buffer_id=1, layer_name="layer1", frame_number=11, event_type=BufferEvent.POST, duration=8)
-trace.add_buffer_event_packet(ts=4, buffer_id=1, layer_name="layer1", frame_number=11, event_type=BufferEvent.ACQUIRE_FENCE, duration=9)
-trace.add_buffer_event_packet(ts=5, buffer_id=1, layer_name="layer1", frame_number=11, event_type=BufferEvent.LATCH, duration=10)
-trace.add_buffer_event_packet(ts=6, buffer_id=2, layer_name="layer2", frame_number=12, event_type=BufferEvent.DEQUEUE, duration=5)
-trace.add_buffer_event_packet(ts=6, buffer_id=1, layer_name="layer1", frame_number=11, event_type=BufferEvent.PRESENT_FENCE, duration=0)
-trace.add_buffer_event_packet(ts=7, buffer_id=2, layer_name="layer2", frame_number=12, event_type=BufferEvent.QUEUE, duration=0)
-trace.add_buffer_event_packet(ts=6, buffer_id=5, layer_name="layer5", frame_number=10, event_type=BufferEvent.PRESENT_FENCE, duration=0)
-trace.add_buffer_event_packet(ts=16, buffer_id=6, layer_name="layer5", frame_number=17, event_type=BufferEvent.PRESENT_FENCE, duration=0)
-# Repeat some layers
-trace.add_buffer_event_packet(ts=6, buffer_id=2, layer_name="layer1", frame_number=12, event_type=BufferEvent.DEQUEUE, duration=11)
-trace.add_buffer_event_packet(ts=7, buffer_id=2, layer_name="layer2", frame_number=12, event_type=BufferEvent.QUEUE, duration=12)
-trace.add_buffer_event_packet(ts=8, buffer_id=2, layer_name="layer3", frame_number=12, event_type=BufferEvent.POST, duration=13)
+# Layer 1
+trace.add_buffer_event_packet(ts=1, buffer_id=1, layer_name="layer1", frame_number=11, event_type=BufferEvent.DEQUEUE, duration=0)
+trace.add_buffer_event_packet(ts=4, buffer_id=1, layer_name="layer1", frame_number=11, event_type=BufferEvent.QUEUE, duration=0)
+trace.add_buffer_event_packet(ts=6, buffer_id=1, layer_name="layer1", frame_number=11, event_type=BufferEvent.ACQUIRE_FENCE, duration=0)
+trace.add_buffer_event_packet(ts=8, buffer_id=1, layer_name="layer1", frame_number=11, event_type=BufferEvent.LATCH, duration=0)
+trace.add_buffer_event_packet(ts=14, buffer_id=1, layer_name="layer1", frame_number=11, event_type=BufferEvent.PRESENT_FENCE, duration=0)
+# Layer 2
+trace.add_buffer_event_packet(ts=6, buffer_id=2, layer_name="layer2", frame_number=12, event_type=BufferEvent.DEQUEUE, duration=0)
+trace.add_buffer_event_packet(ts=8, buffer_id=2, layer_name="layer2", frame_number=12, event_type=BufferEvent.ACQUIRE_FENCE, duration=0)
+trace.add_buffer_event_packet(ts=9, buffer_id=2, layer_name="layer2", frame_number=12, event_type=BufferEvent.QUEUE, duration=0)
+trace.add_buffer_event_packet(ts=11, buffer_id=2, layer_name="layer2", frame_number=12, event_type=BufferEvent.LATCH, duration=0)
+trace.add_buffer_event_packet(ts=16, buffer_id=2, layer_name="layer2", frame_number=12, event_type=BufferEvent.PRESENT_FENCE, duration=0)
+# Next Present of layer 1
+trace.add_buffer_event_packet(ts=24, buffer_id=1, layer_name="layer1", frame_number=13, event_type=BufferEvent.PRESENT_FENCE, duration=0)
 # Missing id.
-trace.add_buffer_event_packet(ts=6, buffer_id=-1, layer_name="layer6", frame_number=13, event_type=BufferEvent.HWC_COMPOSITION_QUEUED, duration=11)
+trace.add_buffer_event_packet(ts=6, buffer_id=-1, layer_name="layer6", frame_number=14, event_type=BufferEvent.HWC_COMPOSITION_QUEUED, duration=0)
 # Missing type.
-trace.add_buffer_event_packet(ts=7, buffer_id=7, layer_name="layer7", frame_number=13, event_type=-1, duration=12)
-# Add some more events
-trace.add_buffer_event_packet(ts=8, buffer_id=2, layer_name="layer2", frame_number=14, event_type=BufferEvent.DETACH, duration=2)
-trace.add_buffer_event_packet(ts=9, buffer_id=3, layer_name="layer3", frame_number=15, event_type=BufferEvent.ATTACH, duration=5)
-trace.add_buffer_event_packet(ts=10, buffer_id=3, layer_name="layer3", frame_number=16, event_type=BufferEvent.CANCEL, duration=10)
+trace.add_buffer_event_packet(ts=7, buffer_id=7, layer_name="layer7", frame_number=15, event_type=-1, duration=0)
 print(trace.trace.SerializeToString())
diff --git a/test/trace_processor/graphics_frame_events.sql b/test/trace_processor/graphics_frame_events.sql
index dea74e8..3e42f53 100644
--- a/test/trace_processor/graphics_frame_events.sql
+++ b/test/trace_processor/graphics_frame_events.sql
@@ -14,7 +14,7 @@
 -- limitations under the License.
 --
 select ts, gpu_track.name as track_name, dur, frame_slice.name as slice_name,
-    frame_numbers, layer_names
+    frame_number, layer_name
 from gpu_track
 left join frame_slice on gpu_track.id=frame_slice.track_id
 where scope='graphics_frame_event'