1 // Copyright 2018 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 "components/exo/wayland/clients/client_base.h"
6 
7 #include <linux-explicit-synchronization-unstable-v1-client-protocol.h>
8 
9 #include "base/at_exit.h"
10 #include "base/command_line.h"
11 #include "base/files/scoped_file.h"
12 #include "base/message_loop/message_pump_type.h"
13 #include "base/task/single_thread_task_executor.h"
14 #include "components/exo/wayland/clients/client_helper.h"
15 #include "third_party/skia/include/core/SkCanvas.h"
16 #include "third_party/skia/include/core/SkSurface.h"
17 #include "third_party/skia/include/gpu/GrContext.h"
18 #include "ui/gl/gl_bindings.h"
19 
20 namespace exo {
21 namespace wayland {
22 namespace clients {
23 namespace {
24 
FrameCallback(void * data,wl_callback * callback,uint32_t time)25 void FrameCallback(void* data, wl_callback* callback, uint32_t time) {
26   bool* frame_callback_pending = static_cast<bool*>(data);
27   *frame_callback_pending = false;
28 }
29 
30 }  // namespace
31 
32 // This client exercises the zwp_linux_explicit_synchronization_unstable_v1
33 // wayland protocol, which enables buffer acquire and release synchronization
34 // between client and server using dma-fences.
35 class ExplicitSynchronizationClient : public ClientBase {
36  public:
37   ExplicitSynchronizationClient() = default;
38 
39   // Initialize and run client main loop.
40   void Run();
41 
42  private:
43   DISALLOW_COPY_AND_ASSIGN(ExplicitSynchronizationClient);
44 };
45 
Run()46 void ExplicitSynchronizationClient::Run() {
47   wl_callback_listener frame_listener = {FrameCallback};
48 
49   /* With a 60Hz redraw rate this completes a half-oscillation in 3 seconds */
50   static const int kMaxDrawStep = 180;
51   int draw_step = 0;
52   int draw_step_dir = 1;
53 
54   // A valid GL context with support for fd fences is required for this client.
55   CHECK(gr_context_)
56       << "A valid GL context is required. Try running with --use-drm.";
57   CHECK_EQ(egl_sync_type_, static_cast<unsigned>(EGL_SYNC_NATIVE_FENCE_ANDROID))
58       << "EGL doesn't support the EGL_ANDROID_native_fence_sync extension.";
59 
60   // The server needs to support the linux_explicit_synchronization protocol.
61   CHECK(globals_.linux_explicit_synchronization)
62       << "Server doesn't support zwp_linux_explicit_synchronization_v1.";
63   std::unique_ptr<zwp_linux_surface_synchronization_v1> surface_synchronization(
64       zwp_linux_explicit_synchronization_v1_get_synchronization(
65           globals_.linux_explicit_synchronization.get(), surface_.get()));
66   DCHECK(surface_synchronization);
67 
68   std::unique_ptr<wl_callback> frame_callback;
69   bool frame_callback_pending = false;
70 
71   do {
72     if (frame_callback_pending)
73       continue;
74 
75     Buffer* buffer = DequeueBuffer();
76     if (!buffer)
77       continue;
78 
79     /* Oscillate between 0 and kMaxDrawStep */
80     draw_step += draw_step_dir;
81     if (draw_step == 0 || draw_step == kMaxDrawStep)
82       draw_step_dir = -draw_step_dir;
83 
84     SkCanvas* canvas = buffer->sk_surface->getCanvas();
85     float draw_step_percent = static_cast<float>(draw_step) / kMaxDrawStep;
86     canvas->clear(
87         SkColor4f{0.0, draw_step_percent, 1.0 - draw_step_percent, 1.0}
88             .toSkColor());
89 
90     // Create an EGLSyncKHR object to signal when rendering is done.
91     gr_context_->flush();
92     buffer->egl_sync.reset(new ScopedEglSync(
93         eglCreateSyncKHR(eglGetCurrentDisplay(), egl_sync_type_, nullptr)));
94     DCHECK(buffer->egl_sync->is_valid());
95     glFlush();
96 
97     wl_surface_set_buffer_scale(surface_.get(), scale_);
98     wl_surface_set_buffer_transform(surface_.get(), transform_);
99     wl_surface_damage(surface_.get(), 0, 0, surface_size_.width(),
100                       surface_size_.height());
101     wl_surface_attach(surface_.get(), buffer->buffer.get(), 0, 0);
102 
103     // Get a fence fd from from EGLSyncKHR and use it as the
104     // acquire fence for the commit.
105     base::ScopedFD fence_fd(eglDupNativeFenceFDANDROID(
106         eglGetCurrentDisplay(), buffer->egl_sync->get()));
107     DCHECK_GE(fence_fd.get(), 0);
108     zwp_linux_surface_synchronization_v1_set_acquire_fence(
109         surface_synchronization.get(), fence_fd.get());
110 
111     // Set up the frame callback.
112     frame_callback_pending = true;
113     frame_callback.reset(wl_surface_frame(surface_.get()));
114     wl_callback_add_listener(frame_callback.get(), &frame_listener,
115                              &frame_callback_pending);
116 
117     wl_surface_commit(surface_.get());
118     wl_display_flush(display_.get());
119   } while (wl_display_dispatch(display_.get()) != -1);
120 }
121 
122 }  // namespace clients
123 }  // namespace wayland
124 }  // namespace exo
125 
main(int argc,char * argv[])126 int main(int argc, char* argv[]) {
127   base::AtExitManager exit_manager;
128   base::CommandLine::Init(argc, argv);
129   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
130   exo::wayland::clients::ClientBase::InitParams params;
131   if (!params.FromCommandLine(*command_line))
132     return 1;
133 
134   base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
135   exo::wayland::clients::ExplicitSynchronizationClient client;
136   if (!client.Init(params))
137     return 1;
138 
139   client.Run();
140 
141   return 0;
142 }
143