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