1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/ozone/platform/drm/gpu/gbm_surfaceless.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "base/bind.h"
11 #include "base/check_op.h"
12 #include "base/notreached.h"
13 #include "base/task/post_task.h"
14 #include "base/task/thread_pool.h"
15 #include "base/trace_event/trace_event.h"
16 #include "ui/gfx/gpu_fence.h"
17 #include "ui/gfx/presentation_feedback.h"
18 #include "ui/ozone/common/egl_util.h"
19 #include "ui/ozone/platform/drm/gpu/drm_device.h"
20 #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
21 #include "ui/ozone/platform/drm/gpu/drm_window_proxy.h"
22 #include "ui/ozone/platform/drm/gpu/gbm_surface_factory.h"
23 
24 namespace ui {
25 
26 namespace {
27 
WaitForFence(EGLDisplay display,EGLSyncKHR fence)28 void WaitForFence(EGLDisplay display, EGLSyncKHR fence) {
29   eglClientWaitSyncKHR(display, fence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
30                        EGL_FOREVER_KHR);
31   eglDestroySyncKHR(display, fence);
32 }
33 
34 }  // namespace
35 
GbmSurfaceless(GbmSurfaceFactory * surface_factory,std::unique_ptr<DrmWindowProxy> window,gfx::AcceleratedWidget widget)36 GbmSurfaceless::GbmSurfaceless(GbmSurfaceFactory* surface_factory,
37                                std::unique_ptr<DrmWindowProxy> window,
38                                gfx::AcceleratedWidget widget)
39     : SurfacelessEGL(gfx::Size()),
40       surface_factory_(surface_factory),
41       window_(std::move(window)),
42       widget_(widget),
43       has_implicit_external_sync_(
44           HasEGLExtension("EGL_ARM_implicit_external_sync")),
45       has_image_flush_external_(
46           HasEGLExtension("EGL_EXT_image_flush_external")) {
47   surface_factory_->RegisterSurface(window_->widget(), this);
48   supports_plane_gpu_fences_ = window_->SupportsGpuFences();
49   unsubmitted_frames_.push_back(std::make_unique<PendingFrame>());
50 }
51 
QueueOverlayPlane(DrmOverlayPlane plane)52 void GbmSurfaceless::QueueOverlayPlane(DrmOverlayPlane plane) {
53   is_on_external_drm_device_ = !plane.buffer->drm_device()->is_primary_device();
54   planes_.push_back(std::move(plane));
55 }
56 
Initialize(gl::GLSurfaceFormat format)57 bool GbmSurfaceless::Initialize(gl::GLSurfaceFormat format) {
58   if (!SurfacelessEGL::Initialize(format))
59     return false;
60   return true;
61 }
62 
SwapBuffers(PresentationCallback callback)63 gfx::SwapResult GbmSurfaceless::SwapBuffers(PresentationCallback callback) {
64   NOTREACHED();
65   return gfx::SwapResult::SWAP_FAILED;
66 }
67 
ScheduleOverlayPlane(int z_order,gfx::OverlayTransform transform,gl::GLImage * image,const gfx::Rect & bounds_rect,const gfx::RectF & crop_rect,bool enable_blend,std::unique_ptr<gfx::GpuFence> gpu_fence)68 bool GbmSurfaceless::ScheduleOverlayPlane(
69     int z_order,
70     gfx::OverlayTransform transform,
71     gl::GLImage* image,
72     const gfx::Rect& bounds_rect,
73     const gfx::RectF& crop_rect,
74     bool enable_blend,
75     std::unique_ptr<gfx::GpuFence> gpu_fence) {
76   unsubmitted_frames_.back()->overlays.push_back(
77       gl::GLSurfaceOverlay(z_order, transform, image, bounds_rect, crop_rect,
78                            enable_blend, std::move(gpu_fence)));
79   return true;
80 }
81 
Resize(const gfx::Size & size,float scale_factor,const gfx::ColorSpace & color_space,bool has_alpha)82 bool GbmSurfaceless::Resize(const gfx::Size& size,
83                             float scale_factor,
84                             const gfx::ColorSpace& color_space,
85                             bool has_alpha) {
86   if (window_)
87     window_->SetColorSpace(color_space);
88 
89   return SurfacelessEGL::Resize(size, scale_factor, color_space, has_alpha);
90 }
91 
IsOffscreen()92 bool GbmSurfaceless::IsOffscreen() {
93   return false;
94 }
95 
SupportsAsyncSwap()96 bool GbmSurfaceless::SupportsAsyncSwap() {
97   return true;
98 }
99 
SupportsPostSubBuffer()100 bool GbmSurfaceless::SupportsPostSubBuffer() {
101   return true;
102 }
103 
SupportsPlaneGpuFences() const104 bool GbmSurfaceless::SupportsPlaneGpuFences() const {
105   return supports_plane_gpu_fences_;
106 }
107 
PostSubBuffer(int x,int y,int width,int height,PresentationCallback callback)108 gfx::SwapResult GbmSurfaceless::PostSubBuffer(int x,
109                                               int y,
110                                               int width,
111                                               int height,
112                                               PresentationCallback callback) {
113   // The actual sub buffer handling is handled at higher layers.
114   NOTREACHED();
115   return gfx::SwapResult::SWAP_FAILED;
116 }
117 
SwapBuffersAsync(SwapCompletionCallback completion_callback,PresentationCallback presentation_callback)118 void GbmSurfaceless::SwapBuffersAsync(
119     SwapCompletionCallback completion_callback,
120     PresentationCallback presentation_callback) {
121   TRACE_EVENT0("drm", "GbmSurfaceless::SwapBuffersAsync");
122   // If last swap failed, don't try to schedule new ones.
123   if (!last_swap_buffers_result_) {
124     std::move(completion_callback)
125         .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_FAILED));
126     // Notify the caller, the buffer is never presented on a screen.
127     std::move(presentation_callback).Run(gfx::PresentationFeedback::Failure());
128     return;
129   }
130 
131   if ((!has_image_flush_external_ && !supports_plane_gpu_fences_) ||
132       requires_gl_flush_on_swap_buffers_) {
133     glFlush();
134   }
135 
136   unsubmitted_frames_.back()->Flush();
137 
138   PendingFrame* frame = unsubmitted_frames_.back().get();
139   frame->completion_callback = std::move(completion_callback);
140   frame->presentation_callback = std::move(presentation_callback);
141   unsubmitted_frames_.push_back(std::make_unique<PendingFrame>());
142 
143   // TODO(dcastagna): Remove the following workaround once we get explicit sync
144   // on all Intel boards, currently we don't have it on legacy KMS.
145   // We can not rely on implicit sync on external devices (crbug.com/692508).
146   // NOTE: When on internal devices, |is_on_external_drm_device_| is set to true
147   // by default conservatively, and it is correctly computed after the first
148   // plane is enqueued in QueueOverlayPlane, that is called from
149   // GbmSurfaceless::SubmitFrame.
150   // This means |is_on_external_drm_device_| could be incorrectly set to true
151   // the first time we're testing it.
152   if (supports_plane_gpu_fences_ ||
153       (!use_egl_fence_sync_ && !is_on_external_drm_device_)) {
154     frame->ready = true;
155     SubmitFrame();
156     return;
157   }
158 
159   // TODO: the following should be replaced by a per surface flush as it gets
160   // implemented in GL drivers.
161   EGLSyncKHR fence = InsertFence(has_implicit_external_sync_);
162   CHECK_NE(fence, EGL_NO_SYNC_KHR) << "eglCreateSyncKHR failed";
163 
164   base::OnceClosure fence_wait_task =
165       base::BindOnce(&WaitForFence, GetDisplay(), fence);
166 
167   base::OnceClosure fence_retired_callback = base::BindOnce(
168       &GbmSurfaceless::FenceRetired, weak_factory_.GetWeakPtr(), frame);
169 
170   base::ThreadPool::PostTaskAndReply(
171       FROM_HERE,
172       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
173       std::move(fence_wait_task), std::move(fence_retired_callback));
174 }
175 
PostSubBufferAsync(int x,int y,int width,int height,SwapCompletionCallback completion_callback,PresentationCallback presentation_callback)176 void GbmSurfaceless::PostSubBufferAsync(
177     int x,
178     int y,
179     int width,
180     int height,
181     SwapCompletionCallback completion_callback,
182     PresentationCallback presentation_callback) {
183   // The actual sub buffer handling is handled at higher layers.
184   SwapBuffersAsync(std::move(completion_callback),
185                    std::move(presentation_callback));
186 }
187 
GetConfig()188 EGLConfig GbmSurfaceless::GetConfig() {
189   if (!config_) {
190     EGLint config_attribs[] = {EGL_BUFFER_SIZE,
191                                32,
192                                EGL_ALPHA_SIZE,
193                                8,
194                                EGL_BLUE_SIZE,
195                                8,
196                                EGL_GREEN_SIZE,
197                                8,
198                                EGL_RED_SIZE,
199                                8,
200                                EGL_RENDERABLE_TYPE,
201                                EGL_OPENGL_ES2_BIT,
202                                EGL_SURFACE_TYPE,
203                                EGL_DONT_CARE,
204                                EGL_NONE};
205     config_ = ChooseEGLConfig(GetDisplay(), config_attribs);
206   }
207   return config_;
208 }
209 
SetRelyOnImplicitSync()210 void GbmSurfaceless::SetRelyOnImplicitSync() {
211   use_egl_fence_sync_ = false;
212 }
213 
SetForceGlFlushOnSwapBuffers()214 void GbmSurfaceless::SetForceGlFlushOnSwapBuffers() {
215   requires_gl_flush_on_swap_buffers_ = true;
216 }
217 
GetOrigin() const218 gfx::SurfaceOrigin GbmSurfaceless::GetOrigin() const {
219   return gfx::SurfaceOrigin::kTopLeft;
220 }
221 
~GbmSurfaceless()222 GbmSurfaceless::~GbmSurfaceless() {
223   Destroy();  // The EGL surface must be destroyed before SurfaceOzone.
224   surface_factory_->UnregisterSurface(window_->widget());
225 }
226 
PendingFrame()227 GbmSurfaceless::PendingFrame::PendingFrame() {}
228 
~PendingFrame()229 GbmSurfaceless::PendingFrame::~PendingFrame() {}
230 
ScheduleOverlayPlanes(gfx::AcceleratedWidget widget)231 bool GbmSurfaceless::PendingFrame::ScheduleOverlayPlanes(
232     gfx::AcceleratedWidget widget) {
233   for (auto& overlay : overlays)
234     if (!overlay.ScheduleOverlayPlane(widget))
235       return false;
236   return true;
237 }
238 
Flush()239 void GbmSurfaceless::PendingFrame::Flush() {
240   for (const auto& overlay : overlays)
241     overlay.Flush();
242 }
243 
SubmitFrame()244 void GbmSurfaceless::SubmitFrame() {
245   DCHECK(!unsubmitted_frames_.empty());
246 
247   if (unsubmitted_frames_.front()->ready && !submitted_frame_) {
248     for (auto& overlay : unsubmitted_frames_.front()->overlays) {
249       if (overlay.z_order() == 0 && overlay.gpu_fence()) {
250         submitted_frame_gpu_fence_ = std::make_unique<gfx::GpuFence>(
251             overlay.gpu_fence()->GetGpuFenceHandle().Clone());
252         break;
253       }
254     }
255     submitted_frame_ = std::move(unsubmitted_frames_.front());
256     unsubmitted_frames_.erase(unsubmitted_frames_.begin());
257 
258     bool schedule_planes_succeeded =
259         submitted_frame_->ScheduleOverlayPlanes(widget_);
260 
261     if (!schedule_planes_succeeded) {
262       OnSubmission(gfx::SwapResult::SWAP_FAILED, nullptr);
263       OnPresentation(gfx::PresentationFeedback::Failure());
264       return;
265     }
266 
267     window_->SchedulePageFlip(std::move(planes_),
268                               base::BindOnce(&GbmSurfaceless::OnSubmission,
269                                              weak_factory_.GetWeakPtr()),
270                               base::BindOnce(&GbmSurfaceless::OnPresentation,
271                                              weak_factory_.GetWeakPtr()));
272     planes_.clear();
273   }
274 }
275 
InsertFence(bool implicit)276 EGLSyncKHR GbmSurfaceless::InsertFence(bool implicit) {
277   const EGLint attrib_list[] = {EGL_SYNC_CONDITION_KHR,
278                                 EGL_SYNC_PRIOR_COMMANDS_IMPLICIT_EXTERNAL_ARM,
279                                 EGL_NONE};
280   return eglCreateSyncKHR(GetDisplay(), EGL_SYNC_FENCE_KHR,
281                           implicit ? attrib_list : NULL);
282 }
283 
FenceRetired(PendingFrame * frame)284 void GbmSurfaceless::FenceRetired(PendingFrame* frame) {
285   frame->ready = true;
286   SubmitFrame();
287 }
288 
OnSubmission(gfx::SwapResult result,std::unique_ptr<gfx::GpuFence> out_fence)289 void GbmSurfaceless::OnSubmission(gfx::SwapResult result,
290                                   std::unique_ptr<gfx::GpuFence> out_fence) {
291   submitted_frame_->swap_result = result;
292 }
293 
OnPresentation(const gfx::PresentationFeedback & feedback)294 void GbmSurfaceless::OnPresentation(const gfx::PresentationFeedback& feedback) {
295   gfx::PresentationFeedback feedback_copy = feedback;
296 
297   if (submitted_frame_gpu_fence_ && !feedback.failed()) {
298     feedback_copy.ready_timestamp =
299         submitted_frame_gpu_fence_->GetMaxTimestamp();
300   }
301   submitted_frame_gpu_fence_.reset();
302   submitted_frame_->overlays.clear();
303 
304   gfx::SwapResult result = submitted_frame_->swap_result;
305   std::move(submitted_frame_->completion_callback)
306       .Run(gfx::SwapCompletionResult(result));
307   std::move(submitted_frame_->presentation_callback).Run(feedback_copy);
308   submitted_frame_.reset();
309 
310   if (result == gfx::SwapResult::SWAP_FAILED) {
311     last_swap_buffers_result_ = false;
312     return;
313   }
314 
315   SubmitFrame();
316 }
317 
318 }  // namespace ui
319