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