Add libvrflinger for use in SurfaceFlinger

A separate CL uses this code from SurfaceFlinger.

Bug: None
Test: Manually ran modified SurfaceFlinger
Change-Id: I34588df1365588c0a0265e1e2325e3dd5516206a
diff --git a/libs/vr/libvrflinger/compositor.cpp b/libs/vr/libvrflinger/compositor.cpp
new file mode 100644
index 0000000..5a111d4
--- /dev/null
+++ b/libs/vr/libvrflinger/compositor.cpp
@@ -0,0 +1,873 @@
+#include "compositor.h"
+
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+
+#include <memory>
+
+#include <cutils/properties.h>
+
+#include <dvr/graphics.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/debug.h>
+#include <private/dvr/display_types.h>
+#include <private/dvr/dummy_native_window.h>
+#include <private/dvr/gl_fenced_flush.h>
+#include <private/dvr/graphics/blur.h>
+#include <private/dvr/graphics/gpu_profiler.h>
+#include <private/dvr/lucid_metrics.h>
+#include <private/dvr/native_buffer.h>
+#include <private/dvr/platform_defines.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "debug_hud_data.h"
+#include "debug_hud_view.h"
+#include "display_surface.h"
+
+#define BINNING_CONTROL_HINT_QCOM 0x8FB0
+
+// Accepted by the <hint> parameter of glHint:
+#define BINNING_QCOM 0x8FB1
+#define VISIBILITY_OPTIMIZED_BINNING_QCOM 0x8FB2
+#define RENDER_DIRECT_TO_FRAMEBUFFER_QCOM 0x8FB3
+
+#ifndef EGL_CONTEXT_MAJOR_VERSION
+#define EGL_CONTEXT_MAJOR_VERSION 0x3098
+#define EGL_CONTEXT_MINOR_VERSION 0x30FB
+#endif
+
+using android::pdx::LocalHandle;
+
+static const int kDistortionMeshResolution = 40;
+
+static std::shared_ptr<int64_t> eds_gpu_duration_ns =
+    std::make_shared<int64_t>(0);
+
+static constexpr char kDisableLensDistortionProp[] =
+    "persist.dreamos.disable_distort";
+
+static constexpr char kEnableEdsPoseSaveProp[] =
+    "persist.dreamos.save_eds_pose";
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// An implementation of ANativeWindowBuffer backed by a temporary IonBuffer.
+// Do not hold on to this kind of object, because the IonBuffer may become
+// invalid in other scopes.
+class TemporaryNativeBuffer
+    : public ANativeObjectBase<ANativeWindowBuffer, TemporaryNativeBuffer,
+                               LightRefBase<TemporaryNativeBuffer>> {
+ public:
+  explicit TemporaryNativeBuffer(const IonBuffer* buffer) : BASE() {
+    ANativeWindowBuffer::width = buffer->width();
+    ANativeWindowBuffer::height = buffer->height();
+    ANativeWindowBuffer::stride = buffer->stride();
+    ANativeWindowBuffer::format = buffer->format();
+    ANativeWindowBuffer::usage = buffer->usage();
+    // TODO(eieio): Update NYC to support layer_count.
+    // ANativeWindowBuffer::layer_count = 1;
+    handle = buffer->handle();
+  }
+
+ private:
+  friend class android::LightRefBase<TemporaryNativeBuffer>;
+
+  TemporaryNativeBuffer(const TemporaryNativeBuffer&) = delete;
+  void operator=(TemporaryNativeBuffer&) = delete;
+};
+
+std::vector<uint8_t> ReadTextureRGBA(GLuint texture_id, int width, int height) {
+  std::vector<uint8_t> data(width * height * 4);
+  GLuint fbo;
+  glGenFramebuffers(1, &fbo);
+  glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                         texture_id, 0);
+  // Using default GL_PACK_ALIGNMENT of 4 for the 4 byte source data.
+  glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
+  glBindFramebuffer(GL_FRAMEBUFFER, 0);
+  glDeleteFramebuffers(1, &fbo);
+  CHECK_GL();
+  return data;
+}
+
+}  // namespace
+
+class Compositor::Texture {
+ public:
+  Texture(std::shared_ptr<BufferConsumer> consumer, EGLDisplay display,
+          int index);
+  ~Texture();
+
+  std::shared_ptr<BufferConsumer> consumer() const { return consumer_; }
+  GLuint texture_id() const { return texture_id_; }
+  vec2i size() const {
+    return vec2i(native_buffer_.get()->width, native_buffer_.get()->height);
+  }
+  int index() const { return index_; }
+
+  bool Initialize();
+
+ private:
+  Texture(const Texture&) = delete;
+  void operator=(const Texture&) = delete;
+
+  std::shared_ptr<BufferConsumer> consumer_;
+
+  android::sp<NativeBufferConsumer> native_buffer_;
+
+  EGLDisplay display_;
+  EGLImageKHR image_;
+  GLuint texture_id_;
+  int index_;
+};
+
+Compositor::Texture::Texture(std::shared_ptr<BufferConsumer> consumer,
+                             EGLDisplay display, int index)
+    : consumer_(consumer),
+      display_(display),
+      image_(nullptr),
+      texture_id_(0),
+      index_(index) {}
+
+Compositor::Texture::~Texture() {
+  glDeleteTextures(1, &texture_id_);
+  eglDestroyImageKHR(display_, image_);
+}
+
+bool Compositor::Texture::Initialize() {
+  native_buffer_ = new NativeBufferConsumer(consumer_, index_);
+
+  CHECK_GL();
+  image_ = eglCreateImageKHR(
+      display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+      static_cast<ANativeWindowBuffer*>(native_buffer_.get()), nullptr);
+  if (!image_) {
+    ALOGE("Failed to create EGLImage\n");
+    return false;
+  }
+
+  glGenTextures(1, &texture_id_);
+  glActiveTexture(GL_TEXTURE0);
+  glBindTexture(GL_TEXTURE_2D, texture_id_);
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image_);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+  CHECK_GL();
+  return true;
+}
+
+Compositor::RenderTarget::RenderTarget()
+    : buffer_texture_id_(0),
+      buffer_framebuffer_id_(0),
+      buffer_image_(nullptr) {}
+
+Compositor::RenderTarget::~RenderTarget() { Destroy(); }
+
+void Compositor::RenderTarget::Destroy() {
+  glDeleteFramebuffers(1, &buffer_framebuffer_id_);
+  glDeleteTextures(1, &buffer_texture_id_);
+  eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), buffer_image_);
+  buffer_texture_id_ = 0;
+  buffer_framebuffer_id_ = 0;
+  buffer_image_ = nullptr;
+}
+
+void Compositor::RenderTarget::Initialize(int width, int height) {
+  LOG_ALWAYS_FATAL_IF(buffer_texture_id_ || buffer_framebuffer_id_ ||
+                      buffer_image_);
+  constexpr int usage = GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_COMPOSER |
+                        GRALLOC_USAGE_HW_RENDER |
+                        GRALLOC_USAGE_QCOM_FRAMEBUFFER_COMPRESSION;
+  buffer_ = std::make_shared<IonBuffer>(width, height,
+                                        HAL_PIXEL_FORMAT_RGBA_8888, usage);
+
+  native_buffer_ = new NativeBuffer(buffer_);
+
+  buffer_image_ = eglCreateImageKHR(
+      eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_NO_CONTEXT,
+      EGL_NATIVE_BUFFER_ANDROID,
+      static_cast<ANativeWindowBuffer*>(native_buffer_.get()), nullptr);
+
+  glGenTextures(1, &buffer_texture_id_);
+  glBindTexture(GL_TEXTURE_2D, buffer_texture_id_);
+  CHECK_GL();
+
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, buffer_image_);
+  CHECK_GL();
+
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  glBindTexture(GL_TEXTURE_2D, 0);
+
+  // Generate a framebuffer.
+  glGenFramebuffers(1, &buffer_framebuffer_id_);
+  glBindFramebuffer(GL_FRAMEBUFFER, buffer_framebuffer_id_);
+  CHECK_GL();
+
+  // Attach the color buffer
+  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                         buffer_texture_id_, 0);
+  CHECK_GL();
+  GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+  CHECK_GL();
+  if (result != GL_FRAMEBUFFER_COMPLETE) {
+    ALOGE("Framebuffer incomplete: %d", result);
+  }
+
+  // Clear the render target to black once. In direct render mode we never draw
+  // the corner pixels.
+  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+  glClear(GL_COLOR_BUFFER_BIT);
+  glFlush();
+
+  glBindFramebuffer(GL_FRAMEBUFFER, 0);
+  CHECK_GL();
+}
+
+void Compositor::RenderTarget::BindFramebuffer() {
+  glBindFramebuffer(GL_FRAMEBUFFER, buffer_framebuffer_id_);
+}
+
+void Compositor::RenderTarget::DiscardColorAttachment() {
+  GLenum attachment = GL_COLOR_ATTACHMENT0;
+  glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, &attachment);
+  CHECK_GL();
+}
+
+class Compositor::RenderPoseBufferObject {
+ public:
+  RenderPoseBufferObject(LocalHandle&& render_pose_buffer_fd) {
+    // Create new pose tracking buffer for this surface.
+    glGenBuffers(1, &render_pose_buffer_object_);
+    glBindBuffer(GL_UNIFORM_BUFFER, render_pose_buffer_object_);
+    if (render_pose_buffer_fd) {
+      LOG_ALWAYS_FATAL_IF(!glBindSharedBufferQCOM);
+      if (glBindSharedBufferQCOM)
+        glBindSharedBufferQCOM(GL_UNIFORM_BUFFER,
+                               sizeof(DisplaySurfaceMetadata),
+                               render_pose_buffer_fd.Get());
+      else
+        ALOGE("Error: Missing gralloc buffer extension");
+      CHECK_GL();
+    }
+    glBindBuffer(GL_UNIFORM_BUFFER, 0);
+  }
+
+  ~RenderPoseBufferObject() { glDeleteBuffers(1, &render_pose_buffer_object_); }
+
+  GLuint object_id() const { return render_pose_buffer_object_; }
+
+ private:
+  // Render pose buffer object. This contains an array of poses that corresponds
+  // with the surface buffers.
+  GLuint render_pose_buffer_object_;
+
+  RenderPoseBufferObject(const RenderPoseBufferObject&) = delete;
+  void operator=(const RenderPoseBufferObject&) = delete;
+};
+
+HeadMountMetrics CreateDefaultHeadMountMetrics() {
+  const bool enable_distortion =
+      property_get_bool(kDisableLensDistortionProp, 0) == 0;
+  return enable_distortion ? CreateHeadMountMetrics()
+                           : CreateUndistortedHeadMountMetrics();
+}
+
+Compositor::Compositor()
+    : head_mount_metrics_(CreateDefaultHeadMountMetrics()),
+      display_(0),
+      config_(0),
+      surface_(0),
+      context_(0),
+      active_render_target_(0),
+      is_render_direct_(false),
+      compute_fbo_(0),
+      compute_fbo_texture_(0),
+      hmd_metrics_requires_update_(false),
+      eds_pose_capture_enabled_(false) {}
+
+Compositor::~Compositor() {}
+
+bool Compositor::Initialize(const DisplayMetrics& display_metrics) {
+  ATRACE_NAME("Compositor::Initialize");
+  if (!InitializeEGL())
+    return false;
+
+  display_metrics_ = display_metrics;
+  const int width = display_metrics_.GetSizePixels().x();
+  const int height = display_metrics_.GetSizePixels().y();
+
+  render_target_[0].Initialize(width, height);
+  render_target_[1].Initialize(width, height);
+
+  // EDS:
+  GpuProfiler::Get()->SetEnableGpuTracing(true);
+
+  eds_pose_capture_enabled_ = property_get_bool(kEnableEdsPoseSaveProp, 0) == 1;
+
+  CheckAndUpdateHeadMountMetrics(true);
+
+  debug_hud_.reset(new DebugHudView(*composite_hmd_.get()));
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+  return true;
+}
+
+void Compositor::UpdateHeadMountMetrics(
+    const HeadMountMetrics& head_mount_metrics) {
+  // Recalculating the mesh must be done in the draw loop, defer until then.
+  std::lock_guard<std::mutex> _lock(mutex_);
+  head_mount_metrics_ = head_mount_metrics;
+  hmd_metrics_requires_update_ = true;
+}
+
+void Compositor::CheckAndUpdateHeadMountMetrics(bool force_update) {
+  std::lock_guard<std::mutex> _lock(mutex_);
+  if (hmd_metrics_requires_update_ || force_update) {
+    hmd_metrics_requires_update_ = false;
+    composite_hmd_.reset(
+        new CompositeHmd(head_mount_metrics_, display_metrics_));
+    CHECK_GL();
+    eds_renderer_.reset(new DistortionRenderer(
+        *composite_hmd_.get(), display_metrics_.GetSizePixels(),
+        kDistortionMeshResolution, true, false, false, true, true));
+  }
+}
+
+bool Compositor::InitializeEGL() {
+  ATRACE_NAME("Compositor::InitializeEGL");
+  display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+  if (!display_) {
+    ALOGE("Failed to get egl display\n");
+    return false;
+  }
+
+  eglInitialize(display_, nullptr, nullptr);
+
+  EGLint attribs[] = {
+      EGL_BUFFER_SIZE,
+      32,
+      EGL_ALPHA_SIZE,
+      0,
+      EGL_BLUE_SIZE,
+      8,
+      EGL_RED_SIZE,
+      8,
+      EGL_GREEN_SIZE,
+      8,
+      EGL_DEPTH_SIZE,
+      0,
+      EGL_SURFACE_TYPE,
+      EGL_WINDOW_BIT,
+      EGL_RENDERABLE_TYPE,
+      EGL_OPENGL_ES2_BIT,
+      EGL_NONE,
+  };
+
+  EGLint num_configs;
+  if (!eglChooseConfig(display_, attribs, &config_, 1, &num_configs)) {
+    ALOGE("Couldn't find config");
+    return false;
+  }
+
+  std::unique_ptr<DummyNativeWindow> window(new DummyNativeWindow());
+
+  surface_ = eglCreateWindowSurface(display_, config_, window.get(), nullptr);
+  if (surface_ == EGL_NO_SURFACE) {
+    ALOGE("Failed to create egl surface");
+    return false;
+  }
+  window.release();
+
+  EGLint context_attribs[] = {EGL_CONTEXT_MAJOR_VERSION,
+                              3,
+                              EGL_CONTEXT_MINOR_VERSION,
+                              1,
+                              EGL_CONTEXT_PRIORITY_LEVEL_IMG,
+                              EGL_CONTEXT_PRIORITY_HIGH_IMG,
+                              EGL_NONE};
+  context_ = eglCreateContext(display_, config_, nullptr, context_attribs);
+  if (!eglMakeCurrent(display_, surface_, surface_, context_)) {
+    ALOGE("Unable to create GLESv2 context");
+    return false;
+  }
+
+  load_gl_extensions();
+
+  glEnable(BINNING_CONTROL_HINT_QCOM);
+  glHint(BINNING_CONTROL_HINT_QCOM, RENDER_DIRECT_TO_FRAMEBUFFER_QCOM);
+  is_render_direct_ = true;
+  CHECK_GL();
+
+  // Initialize the placeholder 1x1 framebuffer that we bind during compute
+  // shader instances to avoid accesses to other framebuffers.
+  glGenFramebuffers(1, &compute_fbo_);
+  glGenTextures(1, &compute_fbo_texture_);
+  glBindFramebuffer(GL_FRAMEBUFFER, compute_fbo_);
+  glBindTexture(GL_TEXTURE_2D, compute_fbo_texture_);
+  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+               nullptr);
+  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                         compute_fbo_texture_, 0);
+  CHECK_GL();
+  CHECK_GL_FBO();
+  glBindTexture(GL_TEXTURE_2D, 0);
+  glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+  return true;
+}
+
+void Compositor::Shutdown() {
+  render_target_[0].Destroy();
+  render_target_[1].Destroy();
+  layers_.clear();
+  glDeleteFramebuffers(1, &compute_fbo_);
+  glDeleteTextures(1, &compute_fbo_texture_);
+
+  debug_hud_.reset();
+  eds_renderer_.reset();
+
+  if (context_) {
+    eglDestroyContext(display_, context_);
+    context_ = 0;
+  }
+
+  if (surface_ != EGL_NO_SURFACE) {
+    eglDestroySurface(display_, surface_);
+    surface_ = EGL_NO_SURFACE;
+  }
+}
+
+void Compositor::RemoveAllBuffers() { layers_.clear(); }
+
+void Compositor::UpdateSurfaces(
+    const std::vector<std::shared_ptr<DisplaySurface>>& surfaces) {
+  // Delete the removed surfaces.
+  layers_.erase(
+      std::remove_if(layers_.begin(), layers_.end(),
+                     [&surfaces](const AppFrame& layer) {
+                       for (const auto& surface : surfaces)
+                         if (surface->surface_id() == layer.surface_id())
+                           return false;
+                       return true;
+                     }),
+      layers_.end());
+  // New surfaces are added on-demand as buffers are posted.
+}
+
+Compositor::AppFrame::AppFrame()
+    : surface_id_(-1),
+      blur_(0.0f),
+      z_order_(0),
+      vertical_flip_(false),
+      enable_cac_(true),
+      render_buffer_index_(0) {}
+
+Compositor::AppFrame::~AppFrame() {}
+
+const Compositor::Texture* Compositor::AppFrame::GetGlTextureId(
+    EGLDisplay display, int index) {
+  auto buffer_consumer = buffer_.buffer();
+  if (!buffer_consumer) {
+    return nullptr;
+  }
+  auto texture_it = std::find_if(
+      textures_.begin(), textures_.end(),
+      [buffer_consumer, index](const std::shared_ptr<Texture>& t) {
+        return t->consumer() == buffer_consumer && t->index() == index;
+      });
+
+  if (texture_it != textures_.end()) {
+    return (*texture_it).get();
+  }
+
+  textures_.push_back(
+      std::make_shared<Texture>(buffer_consumer, display, index));
+  if (!textures_.back()->Initialize()) {
+    textures_.pop_back();
+    return nullptr;
+  }
+  return textures_.back().get();
+}
+
+bool Compositor::AppFrame::UpdateSurface(
+    const std::shared_ptr<DisplaySurface>& surface) {
+  int surface_id = surface->surface_id();
+  float blur = surface->manager_blur();
+  bool need_sort = false;
+  if (z_order_ != surface->layer_order()) {
+    need_sort = true;
+    z_order_ = surface->layer_order();
+  }
+
+  surface_id_ = surface_id;
+  if (!render_pose_buffer_object_) {
+    render_pose_buffer_object_.reset(
+        new RenderPoseBufferObject(surface->GetMetadataBufferFd()));
+  }
+
+  blur_ = blur;
+  vertical_flip_ =
+      !!(surface->flags() & DVR_DISPLAY_SURFACE_FLAGS_VERTICAL_FLIP);
+  enable_cac_ =
+      !(surface->flags() & DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC);
+
+  AcquiredBuffer skipped_buffer;
+  AcquiredBuffer buffer =
+      surface->AcquireNewestAvailableBuffer(&skipped_buffer);
+  if (!skipped_buffer.IsEmpty()) {
+    DebugHudData::data.SkipLayerFrame(z_order_);
+    ATRACE_NAME("DropToCatchUp");
+    ATRACE_ASYNC_END("BufferPost", skipped_buffer.buffer()->id());
+  }
+  if (!buffer.IsEmpty()) {
+    DebugHudData::data.AddLayerFrame(z_order_);
+    // Buffer was already ready, so we don't need to wait on the fence.
+    buffer.ClaimAcquireFence().Close();
+    ATRACE_ASYNC_END("BufferPost", buffer.buffer()->id());
+
+    render_buffer_index_ = surface->GetRenderBufferIndex(buffer.buffer()->id());
+
+#ifdef TRACE
+    const volatile DisplaySurfaceMetadata* data =
+        surface->GetMetadataBufferPtr();
+#endif
+    ALOGE_IF(TRACE, "read pose index %d %f %f", render_buffer_index_,
+             data->orientation[render_buffer_index_][0],
+             data->orientation[render_buffer_index_][1]);
+
+    // Move the new buffer over the old. AcquiredBuffer releases the old one.
+    buffer_ = std::move(buffer);
+  }
+  return need_sort;
+}
+
+void Compositor::AppFrame::UpdateVideoMeshSurface(
+    const std::shared_ptr<DisplaySurface>& surface) {
+  // Update |video_compositors_| with |video_surface|. Note that
+  // |UpdateVideoMeshSurface| should only be called on the PostThread before
+  // |DrawFrame| is called. Thus, no synchronization is required for
+  // |video_compositors_|.
+  if (!surface->video_mesh_surfaces_updated())
+    return;
+
+  // TODO(jwcai) The following loop handles adding new surfaces; video mesh
+  // removal logic shall be handled by listening to |OnChannelClose| event from
+  // DisplayService.
+  for (const auto& video_surface : surface->GetVideoMeshSurfaces()) {
+    // Here we assume number of |video_surface|s is relatively small, thus, the
+    // merge should be efficient enough.
+    auto video_compositor_it = std::find_if(
+        video_compositors_.begin(), video_compositors_.end(),
+        [video_surface](const std::shared_ptr<VideoCompositor>& c) {
+          return c->surface_id() == video_surface->surface_id();
+        });
+
+    if (video_compositor_it == video_compositors_.end()) {
+      // This video surface is new, create a new VideoCompositor.
+      video_compositors_.push_back(std::make_shared<VideoCompositor>(
+          video_surface, surface->GetMetadataBufferPtr()));
+    } else {
+      // There is a compositor in |video_compositors_| is already set up for
+      // this |video_surface|.
+      ALOGW("Duplicated video_mesh_surface: surface_id=%d",
+            video_surface->surface_id());
+    }
+  }
+}
+
+void Compositor::AppFrame::ResetBlurrers() { blurrers_.clear(); }
+
+void Compositor::AppFrame::AddBlurrer(Blur* blurrer) {
+  blurrers_.emplace_back(blurrer);
+}
+
+void Compositor::PostBuffer(const std::shared_ptr<DisplaySurface>& surface) {
+  int surface_id = surface->surface_id();
+
+  ALOGD_IF(TRACE, "Post surface %d", surface_id);
+
+  auto layer_it = std::find_if(layers_.begin(), layers_.end(),
+                               [surface_id](const AppFrame& frame) {
+                                 return frame.surface_id() == surface_id;
+                               });
+
+  bool need_sort = false;
+  if (layer_it == layers_.end()) {
+    layers_.push_back(AppFrame());
+    layer_it = layers_.end() - 1;
+    need_sort = true;
+  }
+
+  need_sort |= layer_it->UpdateSurface(surface);
+  layer_it->UpdateVideoMeshSurface(surface);
+
+  if (need_sort) {
+    std::stable_sort(layers_.begin(), layers_.end());
+  }
+}
+
+std::vector<uint8_t> Compositor::ReadLayerPixels(size_t index, int* width,
+                                                 int* height) {
+  if (index >= layers_.size()) {
+    return {};
+  }
+
+  const Texture* texture = layers_[index].GetGlTextureId(display_, 0);
+  if (!texture) {
+    return {};
+  }
+
+  *width = texture->size()[0];
+  *height = texture->size()[1];
+  return ReadTextureRGBA(texture->texture_id(), *width, *height);
+}
+
+std::vector<uint8_t> Compositor::ReadBufferPixels(const IonBuffer* buffer) {
+  android::sp<TemporaryNativeBuffer> native_buffer =
+      new TemporaryNativeBuffer(buffer);
+
+  // Finish to make sure the GL driver has completed drawing of prior FBOs.
+  // Since we are creating an EGL image here, the driver will not know that
+  // there is a dependency on earlier GL draws.
+  glFinish();
+
+  EGLImageKHR image = eglCreateImageKHR(
+      display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+      static_cast<ANativeWindowBuffer*>(native_buffer.get()), nullptr);
+  if (!image) {
+    ALOGE("Failed to create EGLImage\n");
+    return {};
+  }
+
+  GLuint texture_id;
+  glGenTextures(1, &texture_id);
+  glActiveTexture(GL_TEXTURE0);
+  glBindTexture(GL_TEXTURE_2D, texture_id);
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
+
+  int width = buffer->width();
+  int height = buffer->height();
+  std::vector<uint8_t> data = ReadTextureRGBA(texture_id, width, height);
+
+  glBindTexture(GL_TEXTURE_2D, 0);
+  glDeleteTextures(1, &texture_id);
+  eglDestroyImageKHR(display_, image);
+  return data;
+}
+
+bool Compositor::DrawFrame(uint32_t target_vsync_count,
+                           LocalHandle* buffer_fence_fd) {
+  CheckAndUpdateHeadMountMetrics(false);
+
+  ATRACE_NAME("Compositor::DrawFrame");
+  GpuProfiler::Get()->PollGlTimerQueries();
+
+  if (buffer_fence_fd)
+    buffer_fence_fd->Close();
+
+  int num_layers = 0;
+  const int kMaxLayers = 4;
+  GLuint texture_id[2][kMaxLayers] = {{0}};
+  GLuint render_pose_buffer_id[kMaxLayers] = {0};
+  uint32_t render_buffer_index[kMaxLayers] = {0};
+  bool vertical_flip[kMaxLayers] = {false};
+  bool separate_eye_textures[kMaxLayers] = {false};
+  bool enable_cac[kMaxLayers] = {};
+  CHECK_GL();
+  for (auto& layer : layers_) {
+    if (!layer.buffer().buffer()) {
+      ATRACE_NAME("no_buffer");
+      continue;
+    }
+
+    // Extract surface parameters.
+    render_buffer_index[num_layers] = layer.render_buffer_index();
+    render_pose_buffer_id[num_layers] =
+        layer.render_pose_buffer_object()->object_id();
+    vertical_flip[num_layers] = layer.vertical_flip();
+    enable_cac[num_layers] =
+        head_mount_metrics_.supports_chromatic_aberration_correction() &&
+        layer.enable_cac();
+
+    // Extract per-eye textures. These may be separate or joined (atlased).
+    vec2i size(0, 0);
+    int view_count = layer.buffer().buffer()->slice_count();
+    ALOGE_IF(view_count > 2, "Error: more than 2 views not supported");
+    view_count = std::min(2, view_count);
+    separate_eye_textures[num_layers] = (view_count > 1);
+    bool is_missing_texture = false;
+    for (int eye = 0; eye < 2; ++eye) {
+      // If view_count is 1, each eye texture is the 0th.
+      int view_index = (view_count == 2) ? eye : 0;
+      const Texture* texture = layer.GetGlTextureId(display_, view_index);
+      // Texture will be null if the EGL image creation fails (hopefully never).
+      if (!texture) {
+        is_missing_texture = true;
+        break;
+      }
+      // All views are currently expected to have the same size.
+      size = texture->size();
+      texture_id[eye][num_layers] = texture->texture_id();
+    }
+    if (is_missing_texture) {
+      continue;
+    }
+
+    // Perform blur if requested.
+    if (fabs(layer.blur()) > 0.001f) {
+      // No need for CAC on blurred layers.
+      enable_cac[num_layers] = false;
+      if (layer.blurrer_count() < 1 || layer.blurrer(0)->width() != size[0] ||
+          layer.blurrer(0)->height() != size[1]) {
+        // Blur is created with the left eye texture, but the same instance
+        // can be used for the right eye as well.
+        layer.ResetBlurrers();
+        layer.AddBlurrer(new Blur(size[0], size[1], texture_id[0][num_layers],
+                                  GL_TEXTURE_2D, GL_TEXTURE_2D, true, display_,
+                                  view_count));
+      }
+      // Reset blur instances to prepare for drawing.
+      layer.blurrer(0)->StartFrame();
+      layer.blurrer(0)->set_scale(layer.blur());
+      // Perform blur and replace source texture with blurred output texture.
+      if (view_count == 1) {
+        // Single wide buffer for both eyes, blur both eyes in one operation.
+        texture_id[0][num_layers] = texture_id[1][num_layers] =
+            layer.blurrer(0)->DrawBlur(texture_id[0][num_layers]);
+      } else {
+        // Split eye buffers in a single frame, blur each framebuffer.
+        texture_id[0][num_layers] =
+            layer.blurrer(0)->DrawBlur(texture_id[0][num_layers]);
+        texture_id[1][num_layers] =
+            layer.blurrer(0)->DrawBlur(texture_id[1][num_layers]);
+      }
+    }
+
+    ++num_layers;
+    if (num_layers >= kMaxLayers)
+      break;
+  }
+
+  CHECK_GL();
+  // Set appropriate binning mode for the number of layers.
+  if (num_layers > 1 && is_render_direct_) {
+    is_render_direct_ = false;
+    glDisable(BINNING_CONTROL_HINT_QCOM);
+  } else if (num_layers <= 1 && !is_render_direct_) {
+    is_render_direct_ = true;
+    glEnable(BINNING_CONTROL_HINT_QCOM);
+    glHint(BINNING_CONTROL_HINT_QCOM, RENDER_DIRECT_TO_FRAMEBUFFER_QCOM);
+  }
+
+  // Workaround for GL driver bug that causes the currently bound FBO to be
+  // accessed during a compute shader pass (DoLateLatch below). Based on an
+  // analysis with systrace, the best pattern here was to run the compute shader
+  // with a *different* FBO than what will be drawn to afterward. So we bind
+  // a dummy 1x1 FBO here and discard it. If instead, the current render target
+  // is bound during the compute shader, the following draw calls will be forced
+  // into direct mode rendering.
+  glBindFramebuffer(GL_FRAMEBUFFER, compute_fbo_);
+  GLenum attachment = GL_COLOR_ATTACHMENT0;
+  glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, &attachment);
+
+  // Double buffer the render target.  Get the render target we're drawing into,
+  // and update the active buffer to the next buffer.
+  RenderTarget& render_target = GetRenderTarget();
+  SetNextRenderTarget();
+
+  if (num_layers > 0) {
+    // This trace prints the EDS+Warp GPU overhead and prints every 5 seconds:
+    TRACE_GPU_PRINT("GPU EDS+Warp", 5 * 60);
+    CHECK_GL();
+    eds_renderer_->DoLateLatch(target_vsync_count, render_buffer_index,
+                               render_pose_buffer_id, vertical_flip,
+                               separate_eye_textures, num_layers);
+
+    render_target.BindFramebuffer();
+
+    // Discard to avoid unresolving the framebuffer during tiled rendering.
+    render_target.DiscardColorAttachment();
+
+    // For tiled mode rendering, we clear every frame to avoid garbage showing
+    // up in the parts of tiles that are not rendered.
+    if (!is_render_direct_) {
+      glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+      glClear(GL_COLOR_BUFFER_BIT);
+    }
+
+    for (int eye = kLeftEye; eye <= kRightEye; ++eye) {
+      eds_renderer_->PrepGlState(static_cast<EyeType>(eye));
+      for (int layer_i = 0; layer_i < num_layers; ++layer_i) {
+        bool blend_with_previous = layer_i > 0;
+        uint32_t current_buffer_index = render_buffer_index[layer_i];
+
+        // Render video mesh in the background of each graphics layer.
+        layers_[layer_i].ForEachVideoCompositor([this, eye, layer_i,
+                                                 current_buffer_index,
+                                                 &blend_with_previous](
+            const std::shared_ptr<VideoCompositor>& video_compositor) mutable {
+          eds_renderer_->DrawVideoQuad(
+              static_cast<EyeType>(eye), layer_i,
+              video_compositor->GetActiveTextureId(display_),
+              video_compositor->GetTransform(eye, current_buffer_index));
+          blend_with_previous = true;
+        });
+
+        // Apply distortion to frame submitted from the app's GL context.
+        eds_renderer_->SetChromaticAberrationCorrectionEnabled(
+            enable_cac[layer_i]);
+        eds_renderer_->ApplyDistortionCorrectionToTexture(
+            static_cast<EyeType>(eye), &texture_id[eye][layer_i],
+            &vertical_flip[layer_i], &separate_eye_textures[layer_i], &layer_i,
+            1, blend_with_previous, false);
+      }
+    }
+    eds_renderer_->ResetGlState(1);
+    CHECK_GL();
+  } else {
+    ALOGI("No buffers for compositing, clearing to black.");
+    render_target.BindFramebuffer();
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    glClear(GL_COLOR_BUFFER_BIT);
+  }
+
+  debug_hud_->Update();
+  debug_hud_->Draw();
+
+  LocalHandle fence_fd = CreateGLSyncAndFlush(display_);
+
+  if (buffer_fence_fd)
+    *buffer_fence_fd = std::move(fence_fd);
+
+  if (eds_pose_capture_enabled_) {
+    std::lock_guard<std::mutex> _lock(mutex_);
+    eds_renderer_->GetLastEdsPose(&eds_pose_capture_);
+  }
+
+  return true;
+}
+
+bool Compositor::GetLastEdsPose(LateLatchOutput* out_data) {
+  if (eds_pose_capture_enabled_) {
+    std::lock_guard<std::mutex> _lock(mutex_);
+    *out_data = eds_pose_capture_;
+    return true;
+  } else {
+    ALOGE("Eds pose capture is not enabled.");
+    return false;
+  }
+}
+
+}  // namespace dvr
+}  // namespace android