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 <memory>
6 #include <utility>
7 
8 #include "base/memory/scoped_refptr.h"
9 #include "base/run_loop.h"
10 #include "base/test/mock_callback.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12 #include "third_party/skia/include/core/SkSurface.h"
13 #include "ui/gfx/linux/gbm_buffer.h"
14 #include "ui/gfx/linux/gbm_device.h"
15 #include "ui/gfx/linux/test/mock_gbm_device.h"
16 #include "ui/gfx/native_pixmap.h"
17 #include "ui/gl/gl_image_egl.h"
18 #include "ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h"
19 #include "ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h"
20 #include "ui/ozone/platform/wayland/gpu/wayland_surface_factory.h"
21 #include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
22 #include "ui/ozone/platform/wayland/host/wayland_window.h"
23 #include "ui/ozone/platform/wayland/test/mock_surface.h"
24 #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
25 #include "ui/ozone/platform/wayland/test/test_zwp_linux_buffer_params.h"
26 #include "ui/ozone/platform/wayland/test/wayland_test.h"
27 #include "ui/ozone/public/surface_ozone_canvas.h"
28 #include "ui/ozone/test/mock_platform_window_delegate.h"
29 
30 using ::testing::_;
31 using ::testing::Expectation;
32 using ::testing::SaveArg;
33 
34 namespace ui {
35 
36 namespace {
37 
38 // Fake GLImage that just schedules overlay plane. It must become busy when
39 // scheduled and be associated with the swap id to track correct order of swaps
40 // and releases of the image.
41 class FakeGLImageNativePixmap : public gl::GLImageEGL {
42  public:
FakeGLImageNativePixmap(scoped_refptr<gfx::NativePixmap> pixmap,const gfx::Size & size)43   FakeGLImageNativePixmap(scoped_refptr<gfx::NativePixmap> pixmap,
44                           const gfx::Size& size)
45       : gl::GLImageEGL(size), pixmap_(pixmap) {}
46 
47   // Associates swap id with this image.
AssociateWithSwapId(uint32_t swap_id)48   void AssociateWithSwapId(uint32_t swap_id) {
49     DCHECK_NE(swap_id_, swap_id);
50     swap_id_ = swap_id;
51   }
52 
53   // Returns associated swap id with this image.
GetAssociateWithSwapId()54   uint32_t GetAssociateWithSwapId() { return swap_id_; }
55 
56   // The image is set busy when scheduled as overlay plane for
57   // GbmSurfacelessWayland
SetBusy(bool busy)58   void SetBusy(bool busy) { busy_ = busy; }
busy() const59   bool busy() const { return busy_; }
60 
61   // Sets the image as displayed.
SetDisplayed(bool displayed)62   void SetDisplayed(bool displayed) { displayed_ = displayed; }
displayed() const63   bool displayed() const { return displayed_; }
64 
65   // Overridden from GLImage:
Flush()66   void Flush() override {}
ScheduleOverlayPlane(gfx::AcceleratedWidget widget,int z_order,gfx::OverlayTransform transform,const gfx::Rect & bounds_rect,const gfx::RectF & crop_rect,bool enable_blend,std::unique_ptr<gfx::GpuFence> gpu_fence)67   bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
68                             int z_order,
69                             gfx::OverlayTransform transform,
70                             const gfx::Rect& bounds_rect,
71                             const gfx::RectF& crop_rect,
72                             bool enable_blend,
73                             std::unique_ptr<gfx::GpuFence> gpu_fence) override {
74     // The GLImage must be set busy as it has been scheduled before when
75     // GbmSurfacelessWayland::ScheduleOverlayPlane was called.
76     DCHECK(busy_);
77     return pixmap_->ScheduleOverlayPlane(widget, z_order, transform,
78                                          bounds_rect, crop_rect, enable_blend,
79                                          std::move(gpu_fence));
80   }
GetNativePixmap()81   scoped_refptr<gfx::NativePixmap> GetNativePixmap() override {
82     return pixmap_;
83   }
84 
85  protected:
~FakeGLImageNativePixmap()86   ~FakeGLImageNativePixmap() override {}
87 
88  private:
89   scoped_refptr<gfx::NativePixmap> pixmap_;
90 
91   // Indicated if the gl image is busy. If yes, it was scheduled as overlay
92   // plane for further submission and can't be reused until it's freed.
93   bool busy_ = false;
94 
95   bool displayed_ = false;
96 
97   uint32_t swap_id_ = std::numeric_limits<uint32_t>::max();
98 };
99 
100 // Helper that helps to identify the last swap id. Also sets gl image associated
101 // with that swap as free.
102 class CallbacksHelper {
103  public:
104   CallbacksHelper() = default;
105   ~CallbacksHelper() = default;
106 
107   // Returns last executed swap id that received SwapCompletionCallback.
GetLastFinishedSwapId() const108   uint32_t GetLastFinishedSwapId() const { return last_finish_swap_id_; }
109 
110   // Returns next available swap id that must be used for the next submission of
111   // the buffer.
GetNextLocalSwapId()112   uint32_t GetNextLocalSwapId() {
113     auto next_swap_id = local_swap_id_++;
114     pending_local_swap_ids_.push(next_swap_id);
115     return next_swap_id;
116   }
117 
ResetLastFinishedSwapId()118   void ResetLastFinishedSwapId() {
119     last_finish_swap_id_ = std::numeric_limits<uint32_t>::max();
120   }
121 
122   // Finishes the submission by setting the swap id of completed buffer swap and
123   // sets the associated gl_image as displayed and non-busy, which indicates
124   // that 1) the image has been sent to be shown after being scheduled 2) the
125   // image is displayed. This sort of mimics a buffer queue, but in a simpliear
126   // way.
FinishSwapBuffersAsync(uint32_t local_swap_id,scoped_refptr<FakeGLImageNativePixmap> gl_image,gfx::SwapResult result,std::unique_ptr<gfx::GpuFence> gpu_fence)127   void FinishSwapBuffersAsync(uint32_t local_swap_id,
128                               scoped_refptr<FakeGLImageNativePixmap> gl_image,
129                               gfx::SwapResult result,
130                               std::unique_ptr<gfx::GpuFence> gpu_fence) {
131     last_finish_swap_id_ = pending_local_swap_ids_.front();
132     pending_local_swap_ids_.pop();
133 
134     EXPECT_EQ(gl_image->GetAssociateWithSwapId(), last_finish_swap_id_);
135     EXPECT_TRUE(gl_image->busy() && !gl_image->displayed());
136     if (displayed_image_)
137       displayed_image_->SetDisplayed(false);
138     displayed_image_ = gl_image;
139     displayed_image_->SetBusy(false);
140     displayed_image_->SetDisplayed(true);
141   }
142 
BufferPresented(uint64_t local_swap_id,const gfx::PresentationFeedback & feedback)143   void BufferPresented(uint64_t local_swap_id,
144                        const gfx::PresentationFeedback& feedback) {
145     // Make sure the presentation doesn't come earlier than than swap
146     // completion. We don't explicitly check if the buffer is presented as this
147     // DCHECK is more that enough.
148     DCHECK(pending_local_swap_ids_.empty() ||
149            pending_local_swap_ids_.front() > local_swap_id);
150   }
151 
152  private:
153   uint32_t local_swap_id_ = 0;
154   // Make sure that local_swap_id_ != last_finish_swap_id_.
155   uint32_t last_finish_swap_id_ = std::numeric_limits<uint32_t>::max();
156   base::queue<uint64_t> pending_local_swap_ids_;
157 
158   // Keeps track of a displayed image.
159   scoped_refptr<FakeGLImageNativePixmap> displayed_image_;
160 };
161 
162 }  // namespace
163 
164 class WaylandSurfaceFactoryTest : public WaylandTest {
165  public:
166   WaylandSurfaceFactoryTest() = default;
167   ~WaylandSurfaceFactoryTest() override = default;
168 
SetUp()169   void SetUp() override {
170     WaylandTest::SetUp();
171 
172     auto manager_ptr = connection_->buffer_manager_host()->BindInterface();
173     buffer_manager_gpu_->Initialize(std::move(manager_ptr), {}, false);
174 
175     // Wait until initialization and mojo calls go through.
176     base::RunLoop().RunUntilIdle();
177   }
178 
TearDown()179   void TearDown() override {
180     // The mojo call to destroy shared buffer goes after surfaces are destroyed.
181     // Wait until it's done.
182     base::RunLoop().RunUntilIdle();
183   }
184 
185  protected:
CreateCanvas(gfx::AcceleratedWidget widget)186   std::unique_ptr<SurfaceOzoneCanvas> CreateCanvas(
187       gfx::AcceleratedWidget widget) {
188     auto canvas = surface_factory_->CreateCanvasForWidget(
189         widget_, base::ThreadTaskRunnerHandle::Get().get());
190     base::RunLoop().RunUntilIdle();
191 
192     return canvas;
193   }
194 
195  private:
196   DISALLOW_COPY_AND_ASSIGN(WaylandSurfaceFactoryTest);
197 };
198 
TEST_P(WaylandSurfaceFactoryTest,GbmSurfacelessWaylandCheckOrderOfCallbacksTest)199 TEST_P(WaylandSurfaceFactoryTest,
200        GbmSurfacelessWaylandCheckOrderOfCallbacksTest) {
201   gl::SetGLImplementation(gl::kGLImplementationEGLGLES2);
202 
203   buffer_manager_gpu_->set_gbm_device(std::make_unique<MockGbmDevice>());
204 
205   auto* gl_ozone = surface_factory_->GetGLOzone(gl::kGLImplementationEGLGLES2);
206   auto gl_surface = gl_ozone->CreateSurfacelessViewGLSurface(widget_);
207   EXPECT_TRUE(gl_surface);
208   gl_surface->SetRelyOnImplicitSync();
209   static_cast<ui::GbmSurfacelessWayland*>(gl_surface.get())
210       ->SetNoGLFlushForTests();
211 
212   // Expect to create 3 buffers.
213   EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(3);
214 
215   // Create buffers and FakeGlImageNativePixmap.
216   std::vector<scoped_refptr<FakeGLImageNativePixmap>> fake_gl_image;
217   for (int i = 0; i < 3; ++i) {
218     auto native_pixmap = surface_factory_->CreateNativePixmap(
219         widget_, nullptr, window_->GetBounds().size(),
220         gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::SCANOUT);
221     fake_gl_image.push_back(base::MakeRefCounted<FakeGLImageNativePixmap>(
222         native_pixmap, window_->GetBounds().size()));
223 
224     Sync();
225 
226     // Create one buffer at a time.
227     auto params_vector = server_.zwp_linux_dmabuf_v1()->buffer_params();
228     DCHECK_EQ(params_vector.size(), 1u);
229     zwp_linux_buffer_params_v1_send_created(
230         params_vector.front()->resource(),
231         params_vector.front()->buffer_resource());
232 
233     Sync();
234   }
235 
236   // Now, schedule 3 buffers for swap.
237   auto* mock_surface = server_.GetObject<wl::MockSurface>(widget_);
238 
239   CallbacksHelper cbs_helper;
240   // Submit all the available buffers.
241   for (const auto& gl_image : fake_gl_image) {
242     // Associate each image with swap id so that we could track released
243     // buffers.
244     auto swap_id = cbs_helper.GetNextLocalSwapId();
245     // Associate the image with the next swap id so that we can easily track if
246     // it became free to reuse.
247     gl_image->AssociateWithSwapId(swap_id);
248     // And set it to be busy...
249     gl_image->SetBusy(true);
250 
251     // Prepare overlay plane.
252     gl_surface->ScheduleOverlayPlane(
253         0, gfx::OverlayTransform::OVERLAY_TRANSFORM_FLIP_VERTICAL,
254         gl_image.get(), window_->GetBounds(), {}, false, nullptr);
255 
256     // And submit each image. They will be executed in FIFO manner.
257     gl_surface->SwapBuffersAsync(
258         base::BindOnce(&CallbacksHelper::FinishSwapBuffersAsync,
259                        base::Unretained(&cbs_helper), swap_id, gl_image),
260         base::BindOnce(&CallbacksHelper::BufferPresented,
261                        base::Unretained(&cbs_helper), swap_id));
262   }
263 
264   // Let's sync so that 1) GbmSurfacelessWayland submits the buffer according to
265   // internal queue and fake server processes the request.
266 
267   // Also, we expect only one buffer to be committed.
268   EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
269   EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
270   EXPECT_CALL(*mock_surface, DamageBuffer(_, _, _, _)).Times(1);
271   EXPECT_CALL(*mock_surface, Commit()).Times(1);
272 
273   Sync();
274 
275   testing::Mock::VerifyAndClearExpectations(&mock_surface);
276 
277   // Give mojo the chance to pass the callbacks.
278   base::RunLoop().RunUntilIdle();
279 
280   // We have just received Attach/DamageBuffer/Commit for buffer with swap
281   // id=0u. The SwapCompletionCallback must be executed automatically as long as
282   // we didn't have any buffers attached to the surface before.
283   EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), 0u);
284 
285   cbs_helper.ResetLastFinishedSwapId();
286 
287   for (const auto& gl_image : fake_gl_image) {
288     // All the images except the first one, which was associated with swap
289     // id=0u, must be busy and not displayed. The first one must be displayed.
290     if (gl_image->GetAssociateWithSwapId() == 0u) {
291       EXPECT_FALSE(gl_image->busy());
292       EXPECT_TRUE(gl_image->displayed());
293     } else {
294       EXPECT_TRUE(gl_image->busy());
295       EXPECT_FALSE(gl_image->displayed());
296     }
297   }
298 
299   // Expect buffer for swap with id=1u to be committed.
300   EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
301   EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
302   EXPECT_CALL(*mock_surface, DamageBuffer(_, _, _, _)).Times(1);
303   EXPECT_CALL(*mock_surface, Commit()).Times(1);
304 
305   // Send the frame callback so that pending buffer for swap id=1u is processed
306   // and swapped.
307   mock_surface->SendFrameCallback();
308 
309   Sync();
310 
311   // Give mojo the chance to pass the callbacks.
312   base::RunLoop().RunUntilIdle();
313 
314   // Even though the second buffer was submitted, we mustn't receive
315   // SwapCompletionCallback until the previous buffer is released.
316   EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(),
317             std::numeric_limits<uint32_t>::max());
318 
319   // This will result in Wayland server releasing previously attached buffer for
320   // swap id=0u and calling OnSubmission for buffer with swap id=1u.
321   mock_surface->ReleasePrevAttachedBuffer();
322 
323   Sync();
324 
325   // Give mojo the chance to pass the callbacks.
326   base::RunLoop().RunUntilIdle();
327 
328   // We expect only one buffer to be released. Thus, the last swap id must be
329   // 0 as we waited until next buffer was attached to the surface.
330   EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), 1u);
331 
332   // Reset to test further swap ids.
333   cbs_helper.ResetLastFinishedSwapId();
334 
335   for (const auto& gl_image : fake_gl_image) {
336     // The first image is not displayed and not busy, the second is displayed
337     // and not busy. And others are not display and busy.
338     if (gl_image->GetAssociateWithSwapId() == 0u) {
339       EXPECT_FALSE(gl_image->busy());
340       EXPECT_FALSE(gl_image->displayed());
341     } else if (gl_image->GetAssociateWithSwapId() == 1u) {
342       EXPECT_FALSE(gl_image->busy());
343       EXPECT_TRUE(gl_image->displayed());
344     } else {
345       EXPECT_TRUE(gl_image->busy());
346       EXPECT_FALSE(gl_image->displayed());
347     }
348   }
349 
350   EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
351   EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
352   EXPECT_CALL(*mock_surface, DamageBuffer(_, _, _, _)).Times(1);
353   EXPECT_CALL(*mock_surface, Commit()).Times(1);
354 
355   // Send the frame callback, so that the pending buffer with swap id=2u can
356   // be processed.
357   mock_surface->SendFrameCallback();
358 
359   Sync();
360 
361   // Give mojo the chance to pass the callbacks.
362   base::RunLoop().RunUntilIdle();
363 
364   // Even though the second buffer was submitted, we mustn't receive
365   // SwapCompletionCallback until the previous buffer is released.
366   EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(),
367             std::numeric_limits<uint32_t>::max());
368 
369   // This will result in Wayland server releasing previously attached buffer for
370   // swap id=1u and calling OnSubmission for buffer with swap id=2u.
371   mock_surface->ReleasePrevAttachedBuffer();
372 
373   Sync();
374 
375   // Give mojo the chance to pass the callbacks.
376   base::RunLoop().RunUntilIdle();
377 
378   // We should receive next callbacks for the next swap id.
379   EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), 2u);
380 
381   cbs_helper.ResetLastFinishedSwapId();
382 
383   // All images must be free now and the last one is displayed.
384   for (const auto& gl_image : fake_gl_image) {
385     if (gl_image->GetAssociateWithSwapId() == 2u) {
386       EXPECT_TRUE(gl_image->displayed());
387       EXPECT_FALSE(gl_image->busy());
388     } else {
389       EXPECT_FALSE(gl_image->displayed());
390       EXPECT_FALSE(gl_image->busy());
391     }
392   }
393 
394   // There are no buffers left. Send last frame callback and verify that.
395   EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0);
396   EXPECT_CALL(*mock_surface, Frame(_)).Times(0);
397   EXPECT_CALL(*mock_surface, DamageBuffer(_, _, _, _)).Times(0);
398   EXPECT_CALL(*mock_surface, Commit()).Times(0);
399 
400   // Send a frame callback so that the WaylandBufferManagerHost processes the
401   // pending buffers if any exist.
402   mock_surface->SendFrameCallback();
403 }
404 
TEST_P(WaylandSurfaceFactoryTest,Canvas)405 TEST_P(WaylandSurfaceFactoryTest, Canvas) {
406   auto canvas = CreateCanvas(widget_);
407   ASSERT_TRUE(canvas);
408 
409   canvas->ResizeCanvas(window_->GetBounds().size());
410   auto* sk_canvas = canvas->GetCanvas();
411   DCHECK(sk_canvas);
412   canvas->PresentCanvas(gfx::Rect(5, 10, 20, 15));
413 
414   // Wait until the mojo calls are done.
415   base::RunLoop().RunUntilIdle();
416 
417   Expectation damage = EXPECT_CALL(*surface_, DamageBuffer(5, 10, 20, 15));
418   wl_resource* buffer_resource = nullptr;
419   Expectation attach = EXPECT_CALL(*surface_, Attach(_, 0, 0))
420                            .WillOnce(SaveArg<0>(&buffer_resource));
421   EXPECT_CALL(*surface_, Commit()).After(damage, attach);
422 
423   Sync();
424 
425   ASSERT_TRUE(buffer_resource);
426   wl_shm_buffer* buffer = wl_shm_buffer_get(buffer_resource);
427   ASSERT_TRUE(buffer);
428   EXPECT_EQ(wl_shm_buffer_get_width(buffer), 800);
429   EXPECT_EQ(wl_shm_buffer_get_height(buffer), 600);
430 
431   // TODO(forney): We could check that the contents match something drawn to the
432   // SkSurface above.
433 }
434 
TEST_P(WaylandSurfaceFactoryTest,CanvasResize)435 TEST_P(WaylandSurfaceFactoryTest, CanvasResize) {
436   auto canvas = CreateCanvas(widget_);
437   ASSERT_TRUE(canvas);
438 
439   canvas->ResizeCanvas(window_->GetBounds().size());
440   auto* sk_canvas = canvas->GetCanvas();
441   DCHECK(sk_canvas);
442   canvas->ResizeCanvas(gfx::Size(100, 50));
443   sk_canvas = canvas->GetCanvas();
444   DCHECK(sk_canvas);
445   canvas->PresentCanvas(gfx::Rect(0, 0, 100, 50));
446 
447   base::RunLoop().RunUntilIdle();
448 
449   Expectation damage = EXPECT_CALL(*surface_, DamageBuffer(0, 0, 100, 50));
450   wl_resource* buffer_resource = nullptr;
451   Expectation attach = EXPECT_CALL(*surface_, Attach(_, 0, 0))
452                            .WillOnce(SaveArg<0>(&buffer_resource));
453   EXPECT_CALL(*surface_, Commit()).After(damage, attach);
454 
455   Sync();
456 
457   ASSERT_TRUE(buffer_resource);
458   wl_shm_buffer* buffer = wl_shm_buffer_get(buffer_resource);
459   ASSERT_TRUE(buffer);
460   EXPECT_EQ(wl_shm_buffer_get_width(buffer), 100);
461   EXPECT_EQ(wl_shm_buffer_get_height(buffer), 50);
462 }
463 
TEST_P(WaylandSurfaceFactoryTest,CreateSurfaceCheckGbm)464 TEST_P(WaylandSurfaceFactoryTest, CreateSurfaceCheckGbm) {
465   gl::SetGLImplementation(gl::kGLImplementationEGLGLES2);
466 
467   // When gbm is not available, only canvas can be created with viz process
468   // used.
469   EXPECT_FALSE(buffer_manager_gpu_->gbm_device());
470 
471   auto* gl_ozone = surface_factory_->GetGLOzone(gl::kGLImplementationEGLGLES2);
472   EXPECT_TRUE(gl_ozone);
473   auto gl_surface = gl_ozone->CreateSurfacelessViewGLSurface(widget_);
474   EXPECT_FALSE(gl_surface);
475 
476   // Now, set gbm.
477   buffer_manager_gpu_->set_gbm_device(std::make_unique<MockGbmDevice>());
478 
479   gl_surface = gl_ozone->CreateSurfacelessViewGLSurface(widget_);
480   EXPECT_TRUE(gl_surface);
481 
482   // Reset gbm now. WaylandConnectionProxy can reset it when zwp is not
483   // available. And factory must behave the same way as previously.
484   buffer_manager_gpu_->set_gbm_device(nullptr);
485   gl_surface = gl_ozone->CreateSurfacelessViewGLSurface(widget_);
486   EXPECT_FALSE(gl_surface);
487 }
488 
489 INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
490                          WaylandSurfaceFactoryTest,
491                          ::testing::Values(kXdgShellStable));
492 INSTANTIATE_TEST_SUITE_P(XdgVersionV6Test,
493                          WaylandSurfaceFactoryTest,
494                          ::testing::Values(kXdgShellV6));
495 
496 }  // namespace ui
497