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