Index: ui/ozone/platform/drm/gpu/gbm_surfaceless.cc |
diff --git a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc |
index 9631d28229886a1d6394be62c512613af9027bfa..2fe3306e323c97594ca1fd7af49bbc1cdee3eba3 100644 |
--- a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc |
+++ b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc |
@@ -1,4 +1,4 @@ |
-// Copyright 2014 The Chromium Authors. All rights reserved. |
+// 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. |
@@ -6,11 +6,12 @@ |
#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 "third_party/khronos/EGL/egl.h" |
#include "ui/ozone/common/egl_util.h" |
-#include "ui/ozone/platform/drm/gpu/drm_device.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" |
@@ -18,67 +19,232 @@ |
namespace ui { |
-GbmSurfaceless::GbmSurfaceless(std::unique_ptr<DrmWindowProxy> window, |
- GbmSurfaceFactory* surface_manager) |
- : window_(std::move(window)), surface_manager_(surface_manager) { |
- surface_manager_->RegisterSurface(window_->widget(), this); |
+namespace { |
+ |
+void WaitForFence(EGLDisplay display, EGLSyncKHR fence) { |
+ eglClientWaitSyncKHR(display, fence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, |
+ EGL_FOREVER_KHR); |
} |
-GbmSurfaceless::~GbmSurfaceless() { |
- surface_manager_->UnregisterSurface(window_->widget()); |
+} // namespace |
+ |
+GbmSurfaceless::GbmSurfaceless(GbmSurfaceFactory* surface_factory, |
+ std::unique_ptr<DrmWindowProxy> window, |
+ gfx::AcceleratedWidget widget) |
+ : SurfacelessEGL(gfx::Size()), |
+ surface_factory_(surface_factory), |
+ window_(std::move(window)), |
+ widget_(widget), |
+ has_implicit_external_sync_( |
+ HasEGLExtension("EGL_ARM_implicit_external_sync")), |
+ has_image_flush_external_( |
+ HasEGLExtension("EGL_EXT_image_flush_external")), |
+ weak_factory_(this) { |
+ surface_factory_->RegisterSurface(window_->widget(), this); |
+ unsubmitted_frames_.push_back(new PendingFrame()); |
} |
void GbmSurfaceless::QueueOverlayPlane(const OverlayPlane& plane) { |
planes_.push_back(plane); |
} |
-intptr_t GbmSurfaceless::GetNativeWindow() { |
+bool GbmSurfaceless::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 GbmSurfaceless::SwapBuffers() { |
NOTREACHED(); |
- return 0; |
+ return gfx::SwapResult::SWAP_FAILED; |
} |
-bool GbmSurfaceless::ResizeNativeWindow(const gfx::Size& viewport_size) { |
+bool GbmSurfaceless::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 GbmSurfaceless::OnSwapBuffers() { |
- NOTREACHED(); |
+bool GbmSurfaceless::IsOffscreen() { |
return false; |
} |
-void GbmSurfaceless::OnSwapBuffersAsync( |
+gfx::VSyncProvider* GbmSurfaceless::GetVSyncProvider() { |
+ return vsync_provider_.get(); |
+} |
+ |
+bool GbmSurfaceless::SupportsAsyncSwap() { |
+ return true; |
+} |
+ |
+bool GbmSurfaceless::SupportsPostSubBuffer() { |
+ return true; |
+} |
+ |
+gfx::SwapResult GbmSurfaceless::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 GbmSurfaceless::SwapBuffersAsync(const SwapCompletionCallback& callback) { |
+ TRACE_EVENT0("drm", "GbmSurfaceless::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( |
+ &GbmSurfaceless::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(&GbmSurfaceless::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 GbmSurfaceless::PostSubBufferAsync( |
+ int x, |
+ int y, |
+ int width, |
+ int height, |
const SwapCompletionCallback& callback) { |
- TRACE_EVENT0("drm", "GbmSurfaceless::OnSwapBuffersAsync"); |
- window_->SchedulePageFlip(planes_, callback); |
- planes_.clear(); |
+ // The actual sub buffer handling is handled at higher layers. |
+ SwapBuffersAsync(callback); |
} |
-std::unique_ptr<gfx::VSyncProvider> GbmSurfaceless::CreateVSyncProvider() { |
- return base::WrapUnique(new DrmVSyncProvider(window_.get())); |
+EGLConfig GbmSurfaceless::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_; |
} |
-bool GbmSurfaceless::IsUniversalDisplayLinkDevice() { |
- return planes_.empty() ? false : planes_[0].buffer->RequiresGlFinish(); |
+GbmSurfaceless::~GbmSurfaceless() { |
+ Destroy(); // The EGL surface must be destroyed before SurfaceOzone. |
+ surface_factory_->UnregisterSurface(window_->widget()); |
+} |
+ |
+GbmSurfaceless::PendingFrame::PendingFrame() {} |
+ |
+GbmSurfaceless::PendingFrame::~PendingFrame() {} |
+ |
+bool GbmSurfaceless::PendingFrame::ScheduleOverlayPlanes( |
+ gfx::AcceleratedWidget widget) { |
+ for (const auto& overlay : overlays) |
+ if (!overlay.ScheduleOverlayPlane(widget)) |
+ return false; |
+ return true; |
+} |
+ |
+void GbmSurfaceless::PendingFrame::Flush() { |
+ for (const auto& overlay : overlays) |
+ overlay.Flush(); |
+} |
+ |
+void GbmSurfaceless::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(); |
+ } |
} |
-void* /* EGLConfig */ GbmSurfaceless::GetEGLSurfaceConfig( |
- const EglConfigCallbacks& egl) { |
- 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}; |
- return ChooseEGLConfig(egl, config_attribs); |
+EGLSyncKHR GbmSurfaceless::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 GbmSurfaceless::FenceRetired(EGLSyncKHR fence, PendingFrame* frame) { |
+ eglDestroySyncKHR(GetDisplay(), fence); |
+ frame->ready = true; |
+ SubmitFrame(); |
+} |
+ |
+void GbmSurfaceless::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 GbmSurfaceless::IsUniversalDisplayLinkDevice() { |
+ return planes_.empty() ? false : planes_[0].buffer->RequiresGlFinish(); |
} |
} // namespace ui |