Chromium Code Reviews| Index: ui/ozone/platform/drm/gpu/gl_surface_gbm_surfaceless.cc |
| diff --git a/ui/ozone/platform/drm/gpu/gl_surface_gbm_surfaceless.cc b/ui/ozone/platform/drm/gpu/gl_surface_gbm_surfaceless.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..9f4368c2801b743fc9b90c9ae3c558b31bc16c5c |
| --- /dev/null |
| +++ b/ui/ozone/platform/drm/gpu/gl_surface_gbm_surfaceless.cc |
| @@ -0,0 +1,256 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "ui/ozone/platform/drm/gpu/gl_surface_gbm_surfaceless.h" |
| + |
| +#include <utility> |
| + |
| +#include "base/bind.h" |
| +#include "base/logging.h" |
| +#include "base/memory/ptr_util.h" |
| +#include "base/threading/worker_pool.h" |
| +#include "base/trace_event/trace_event.h" |
| +#include "ui/ozone/common/egl_util.h" |
| +#include "ui/ozone/platform/drm/gpu/drm_vsync_provider.h" |
| +#include "ui/ozone/platform/drm/gpu/drm_window_proxy.h" |
| +#include "ui/ozone/platform/drm/gpu/gbm_surface_factory.h" |
| +#include "ui/ozone/platform/drm/gpu/scanout_buffer.h" |
| + |
| +namespace ui { |
| + |
| +namespace { |
| + |
| +void WaitForFence(EGLDisplay display, EGLSyncKHR fence) { |
| + eglClientWaitSyncKHR(display, fence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, |
| + EGL_FOREVER_KHR); |
| +} |
| +} // namespace |
| + |
| +GLSurfaceGbmSurfaceless::PendingFrame::PendingFrame() : ready(false) {} |
| + |
| +GLSurfaceGbmSurfaceless::PendingFrame::~PendingFrame() {} |
| + |
| +bool GLSurfaceGbmSurfaceless::PendingFrame::ScheduleOverlayPlanes( |
| + gfx::AcceleratedWidget widget) { |
| + for (const auto& overlay : overlays) |
| + if (!overlay.ScheduleOverlayPlane(widget)) |
| + return false; |
| + return true; |
| +} |
| + |
| +void GLSurfaceGbmSurfaceless::PendingFrame::Flush() { |
| + for (const auto& overlay : overlays) |
| + overlay.Flush(); |
| +} |
| + |
| +GLSurfaceGbmSurfaceless::GLSurfaceGbmSurfaceless( |
| + std::unique_ptr<DrmWindowProxy> window, |
| + GbmSurfaceFactory* surface_manager, |
| + gfx::AcceleratedWidget widget) |
| + : SurfacelessEGL(gfx::Size()), |
| + window_(std::move(window)), |
| + surface_manager_(surface_manager), |
| + widget_(widget), |
| + has_implicit_external_sync_( |
| + HasEGLExtension("EGL_ARM_implicit_external_sync")), |
| + has_image_flush_external_( |
| + HasEGLExtension("EGL_EXT_image_flush_external")), |
| + last_swap_buffers_result_(true), |
| + swap_buffers_pending_(false), |
| + weak_factory_(this) { |
| + surface_manager_->RegisterSurface(window_->widget(), this); |
| + unsubmitted_frames_.push_back(new PendingFrame()); |
| +} |
| + |
| +void GLSurfaceGbmSurfaceless::QueueOverlayPlane(const OverlayPlane& plane) { |
| + planes_.push_back(plane); |
| +} |
| + |
| +bool GLSurfaceGbmSurfaceless::Initialize(gl::GLSurface::Format format) { |
| + if (!SurfacelessEGL::Initialize(format)) |
| + return false; |
| + vsync_provider_ = base::MakeUnique<DrmVSyncProvider>(window_.get()); |
| + if (!vsync_provider_) |
| + return false; |
| + return true; |
| +} |
| + |
| +gfx::SwapResult GLSurfaceGbmSurfaceless::SwapBuffers() { |
| + return gfx::SwapResult::SWAP_FAILED; |
|
dnicoara
2016/07/21 17:41:38
nit: Add a NOTREACHED() just in case.
kylechar
2016/07/21 18:34:07
Done.
|
| +} |
| + |
| +bool GLSurfaceGbmSurfaceless::ScheduleOverlayPlane( |
| + int z_order, |
| + gfx::OverlayTransform transform, |
| + gl::GLImage* image, |
| + const gfx::Rect& bounds_rect, |
| + const gfx::RectF& crop_rect) { |
| + unsubmitted_frames_.back()->overlays.push_back( |
| + gl::GLSurfaceOverlay(z_order, transform, image, bounds_rect, crop_rect)); |
| + return true; |
| +} |
| + |
| +bool GLSurfaceGbmSurfaceless::IsOffscreen() { |
| + return false; |
| +} |
| + |
| +gfx::VSyncProvider* GLSurfaceGbmSurfaceless::GetVSyncProvider() { |
| + return vsync_provider_.get(); |
| +} |
| + |
| +bool GLSurfaceGbmSurfaceless::SupportsAsyncSwap() { |
| + return true; |
| +} |
| + |
| +bool GLSurfaceGbmSurfaceless::SupportsPostSubBuffer() { |
| + return true; |
| +} |
| + |
| +gfx::SwapResult GLSurfaceGbmSurfaceless::PostSubBuffer(int x, |
| + int y, |
| + int width, |
| + int height) { |
| + // The actual sub buffer handling is handled at higher layers. |
| + NOTREACHED(); |
| + return gfx::SwapResult::SWAP_FAILED; |
| +} |
| + |
| +void GLSurfaceGbmSurfaceless::SwapBuffersAsync( |
| + const SwapCompletionCallback& callback) { |
| + TRACE_EVENT0("drm", "GLSurfaceGbmSurfaceless::SwapBuffersAsync"); |
| + // If last swap failed, don't try to schedule new ones. |
| + if (!last_swap_buffers_result_) { |
| + callback.Run(gfx::SwapResult::SWAP_FAILED); |
| + return; |
| + } |
| + |
| + glFlush(); |
| + unsubmitted_frames_.back()->Flush(); |
| + |
| + SwapCompletionCallback surface_swap_callback = |
| + base::Bind(&GLSurfaceGbmSurfaceless::SwapCompleted, |
| + weak_factory_.GetWeakPtr(), callback); |
| + |
| + PendingFrame* frame = unsubmitted_frames_.back(); |
| + frame->callback = surface_swap_callback; |
| + unsubmitted_frames_.push_back(new PendingFrame()); |
| + |
| + // TODO: the following should be replaced by a per surface flush as it gets |
| + // implemented in GL drivers. |
| + if (has_implicit_external_sync_ || has_image_flush_external_) { |
| + EGLSyncKHR fence = InsertFence(has_implicit_external_sync_); |
| + if (!fence) { |
| + callback.Run(gfx::SwapResult::SWAP_FAILED); |
| + return; |
| + } |
| + |
| + base::Closure fence_wait_task = |
| + base::Bind(&WaitForFence, GetDisplay(), fence); |
| + |
| + base::Closure fence_retired_callback = |
| + base::Bind(&GLSurfaceGbmSurfaceless::FenceRetired, |
| + weak_factory_.GetWeakPtr(), fence, frame); |
| + |
| + base::WorkerPool::PostTaskAndReply(FROM_HERE, fence_wait_task, |
| + fence_retired_callback, false); |
| + return; // Defer frame submission until fence signals. |
| + } |
| + |
| + frame->ready = true; |
| + SubmitFrame(); |
| +} |
| + |
| +void GLSurfaceGbmSurfaceless::PostSubBufferAsync( |
| + int x, |
| + int y, |
| + int width, |
| + int height, |
| + const SwapCompletionCallback& callback) { |
| + // The actual sub buffer handling is handled at higher layers. |
| + SwapBuffersAsync(callback); |
| +} |
| + |
| +EGLConfig GLSurfaceGbmSurfaceless::GetConfig() { |
| + if (!config_) { |
| + EGLint config_attribs[] = {EGL_BUFFER_SIZE, |
| + 32, |
| + EGL_ALPHA_SIZE, |
| + 8, |
| + EGL_BLUE_SIZE, |
| + 8, |
| + EGL_GREEN_SIZE, |
| + 8, |
| + EGL_RED_SIZE, |
| + 8, |
| + EGL_RENDERABLE_TYPE, |
| + EGL_OPENGL_ES2_BIT, |
| + EGL_SURFACE_TYPE, |
| + EGL_DONT_CARE, |
| + EGL_NONE}; |
| + config_ = ChooseEGLConfig(GetDisplay(), config_attribs); |
| + } |
| + return config_; |
| +} |
| + |
| +GLSurfaceGbmSurfaceless::~GLSurfaceGbmSurfaceless() { |
| + Destroy(); // The EGL surface must be destroyed before SurfaceOzone. |
| + surface_manager_->UnregisterSurface(window_->widget()); |
| +} |
| + |
| +void GLSurfaceGbmSurfaceless::SubmitFrame() { |
| + DCHECK(!unsubmitted_frames_.empty()); |
| + |
| + if (unsubmitted_frames_.front()->ready && !swap_buffers_pending_) { |
| + std::unique_ptr<PendingFrame> frame(unsubmitted_frames_.front()); |
| + unsubmitted_frames_.weak_erase(unsubmitted_frames_.begin()); |
| + swap_buffers_pending_ = true; |
| + |
| + if (!frame->ScheduleOverlayPlanes(widget_)) { |
| + // |callback| is a wrapper for SwapCompleted(). Call it to properly |
| + // propagate the failed state. |
| + frame->callback.Run(gfx::SwapResult::SWAP_FAILED); |
| + return; |
| + } |
| + |
| + if (IsUniversalDisplayLinkDevice()) |
| + glFinish(); |
| + |
| + window_->SchedulePageFlip(planes_, frame->callback); |
| + planes_.clear(); |
| + } |
| +} |
| + |
| +EGLSyncKHR GLSurfaceGbmSurfaceless::InsertFence(bool implicit) { |
| + const EGLint attrib_list[] = {EGL_SYNC_CONDITION_KHR, |
| + EGL_SYNC_PRIOR_COMMANDS_IMPLICIT_EXTERNAL_ARM, |
| + EGL_NONE}; |
| + return eglCreateSyncKHR(GetDisplay(), EGL_SYNC_FENCE_KHR, |
| + implicit ? attrib_list : NULL); |
| +} |
| + |
| +void GLSurfaceGbmSurfaceless::FenceRetired(EGLSyncKHR fence, |
| + PendingFrame* frame) { |
| + eglDestroySyncKHR(GetDisplay(), fence); |
| + frame->ready = true; |
| + SubmitFrame(); |
| +} |
| + |
| +void GLSurfaceGbmSurfaceless::SwapCompleted( |
| + const SwapCompletionCallback& callback, |
| + gfx::SwapResult result) { |
| + callback.Run(result); |
| + swap_buffers_pending_ = false; |
| + if (result == gfx::SwapResult::SWAP_FAILED) { |
| + last_swap_buffers_result_ = false; |
| + return; |
| + } |
| + |
| + SubmitFrame(); |
| +} |
| + |
| +bool GLSurfaceGbmSurfaceless::IsUniversalDisplayLinkDevice() { |
| + return planes_.empty() ? false : planes_[0].buffer->RequiresGlFinish(); |
| +} |
| + |
| +} // namespace ui |