1 /*############################################################################
2   # Copyright (C) 2005 Intel Corporation
3   #
4   # SPDX-License-Identifier: MIT
5   ############################################################################*/
6 
7 #include <fcntl.h>
8 #include <poll.h>
9 #include <sys/mman.h>
10 #include <unistd.h>
11 #include <cstring>
12 #include <exception>
13 #include <iostream>
14 extern "C" {
15 #include <drm.h>
16 #include <intel_bufmgr.h>
17 #include <xf86drm.h>
18 }
19 #include "class_wayland.h"
20 #include "listener_wayland.h"
21 #include "wayland-drm-client-protocol.h"
22 
23 #define BATCH_SIZE 0x80000
24 
25 struct buffer {
26     struct wl_buffer* buffer;
27     mfxFrameSurface1* pInSurface;
28 };
29 
30 static const struct wl_callback_listener frame_listener = { handle_done };
31 
32 static const struct wl_buffer_listener buffer_listener = { buffer_release };
33 
Wayland()34 Wayland::Wayland()
35         : m_display(NULL),
36           m_registry(NULL),
37           m_compositor(NULL),
38           m_shell(NULL),
39           m_drm(NULL),
40           m_shm(NULL),
41           m_pool(NULL),
42           m_surface(NULL),
43           m_shell_surface(NULL),
44           m_callback(NULL),
45           m_event_queue(NULL),
46           m_pending_frame(0),
47           m_shm_pool(NULL),
48           m_display_fd(-1),
49           m_fd(-1),
50           m_bufmgr(NULL),
51           m_device_name(NULL),
52           m_x(0),
53           m_y(0),
54           m_perf_mode(false) {
55     std::memset(&m_poll, 0, sizeof(m_poll));
56 }
57 
InitDisplay()58 bool Wayland::InitDisplay() {
59     static const struct wl_registry_listener registry_listener = { .global = registry_handle_global,
60                                                                    .global_remove =
61                                                                        remove_registry_global };
62 
63     m_display = wl_display_connect(NULL);
64     if (NULL == m_display) {
65         std::cout << "Error: Cannot connect to wayland display\n";
66         return false;
67     }
68     m_registry = wl_display_get_registry(m_display);
69     wl_registry_add_listener(m_registry, &registry_listener, this);
70 
71     m_display_fd = wl_display_get_fd(m_display);
72     wl_display_roundtrip(m_display);
73     wl_display_roundtrip(m_display);
74     m_event_queue = wl_display_create_queue(m_display);
75     if (NULL == m_event_queue)
76         return false;
77 
78     m_poll.fd     = m_display_fd;
79     m_poll.events = POLLIN;
80     return true;
81 }
82 
DisplayRoundtrip()83 int Wayland::DisplayRoundtrip() {
84     return wl_display_roundtrip(m_display);
85 }
86 
CreateSurface()87 bool Wayland::CreateSurface() {
88     static const struct wl_shell_surface_listener shell_surface_listener = {
89         shell_surface_ping,
90         shell_surface_configure
91     };
92 
93     m_surface = wl_compositor_create_surface(m_compositor);
94     if (NULL == m_surface)
95         return false;
96 
97     m_shell_surface = wl_shell_get_shell_surface(m_shell, m_surface);
98     if (NULL == m_shell_surface) {
99         wl_surface_destroy(m_surface);
100         return false;
101     }
102 
103     wl_shell_surface_add_listener(m_shell_surface, &shell_surface_listener, 0);
104     wl_shell_surface_set_toplevel(m_shell_surface);
105     wl_shell_surface_set_user_data(m_shell_surface, m_surface);
106     wl_surface_set_user_data(m_surface, NULL);
107     return true;
108 }
109 
FreeSurface()110 void Wayland::FreeSurface() {
111     if (NULL != m_shell_surface)
112         wl_shell_surface_destroy(m_shell_surface);
113     if (NULL != m_surface)
114         wl_surface_destroy(m_surface);
115 }
116 
Sync()117 void Wayland::Sync() {
118     int ret;
119     while (NULL != m_callback) {
120         while (wl_display_prepare_read_queue(m_display, m_event_queue) < 0)
121             wl_display_dispatch_queue_pending(m_display, m_event_queue);
122 
123         wl_display_flush(m_display);
124 
125         ret = poll(&m_poll, 1, -1);
126         if (ret < 0)
127             wl_display_cancel_read(m_display);
128         else
129             wl_display_read_events(m_display);
130         wl_display_dispatch_queue_pending(m_display, m_event_queue);
131     }
132 }
133 
SetPerfMode(bool perf_mode)134 void Wayland::SetPerfMode(bool perf_mode) {
135     m_perf_mode = perf_mode;
136 }
137 
SetRenderWinPos(int x,int y)138 void Wayland::SetRenderWinPos(int x, int y) {
139     m_x = x;
140     m_y = y;
141 }
142 
RenderBuffer(struct wl_buffer * buffer,mfxFrameSurface1 * surface)143 void Wayland::RenderBuffer(struct wl_buffer* buffer, mfxFrameSurface1* surface) {
144     wld_buffer* m_buffer = new wld_buffer;
145     if (m_buffer == NULL)
146         return;
147 
148     m_buffer->buffer     = buffer;
149     m_buffer->pInSurface = surface;
150 
151     wl_surface_attach(m_surface, buffer, 0, 0);
152     wl_surface_damage(m_surface, m_x, m_y, surface->Info.CropW, surface->Info.CropH);
153 
154     wl_proxy_set_queue((struct wl_proxy*)buffer, m_event_queue);
155 
156     AddBufferToList(m_buffer);
157     wl_buffer_add_listener(buffer, &buffer_listener, this);
158     m_pending_frame = 1;
159     if (m_perf_mode)
160         m_callback = wl_display_sync(m_display);
161     else
162         m_callback = wl_surface_frame(m_surface);
163     wl_callback_add_listener(m_callback, &frame_listener, this);
164     wl_proxy_set_queue((struct wl_proxy*)m_callback, m_event_queue);
165     wl_surface_commit(m_surface);
166     wl_display_dispatch_queue(m_display, m_event_queue);
167     /* Force a Sync before and after render to ensure client handles
168       wayland events in a timely fashion. This also fixes the one time
169       flicker issue on wl_shell_surface pointer enter */
170     Sync();
171 }
172 
RenderBufferWinPosSize(struct wl_buffer * buffer,int x,int y,int32_t width,int32_t height)173 void Wayland::RenderBufferWinPosSize(struct wl_buffer* buffer,
174                                      int x,
175                                      int y,
176                                      int32_t width,
177                                      int32_t height) {
178     wl_surface_attach(m_surface, buffer, 0, 0);
179     wl_surface_damage(m_surface, x, y, width, height);
180 
181     wl_proxy_set_queue((struct wl_proxy*)buffer, m_event_queue);
182 
183     wl_buffer_add_listener(buffer, &buffer_listener, NULL);
184     m_pending_frame = 1;
185     if (m_perf_mode)
186         m_callback = wl_display_sync(m_display);
187     else
188         m_callback = wl_surface_frame(m_surface);
189     wl_callback_add_listener(m_callback, &frame_listener, this);
190     wl_proxy_set_queue((struct wl_proxy*)m_callback, m_event_queue);
191     wl_surface_commit(m_surface);
192     wl_display_dispatch_queue(m_display, m_event_queue);
193 }
194 
DestroyCallback()195 void Wayland::DestroyCallback() {
196     if (m_callback) {
197         wl_callback_destroy(m_callback);
198         m_callback      = NULL;
199         m_pending_frame = 0;
200     }
201 }
202 
203 //ShmPool
CreateShmPool(int fd,int32_t size,int prot)204 bool Wayland::CreateShmPool(int fd, int32_t size, int prot) {
205     m_shm_pool = new struct ShmPool;
206     if (NULL == m_shm_pool)
207         return false;
208 
209     m_shm_pool->capacity = size;
210     m_shm_pool->size     = 0;
211     m_shm_pool->fd       = fd;
212 
213     m_shm_pool->memory = static_cast<uint32_t*>(mmap(0, size, prot, MAP_SHARED, m_shm_pool->fd, 0));
214     if (MAP_FAILED == m_shm_pool->memory) {
215         delete m_shm_pool;
216         return false;
217     }
218 
219     m_pool = wl_shm_create_pool(m_shm, m_shm_pool->fd, size);
220     if (NULL == m_pool) {
221         munmap(m_shm_pool->memory, size);
222         delete m_shm_pool;
223         return false;
224     }
225     wl_shm_pool_set_user_data(m_pool, m_shm_pool);
226     return true;
227 }
228 
FreeShmPool()229 void Wayland::FreeShmPool() {
230     wl_shm_pool_destroy(m_pool);
231     munmap(m_shm_pool->memory, m_shm_pool->capacity);
232     delete m_shm_pool;
233 }
234 
CreateShmBuffer(unsigned width,unsigned height,unsigned stride,uint32_t PIXEL_FORMAT_ID)235 struct wl_buffer* Wayland::CreateShmBuffer(unsigned width,
236                                            unsigned height,
237                                            unsigned stride,
238                                            uint32_t PIXEL_FORMAT_ID) {
239     struct wl_buffer* buffer;
240     buffer = wl_shm_pool_create_buffer(m_pool,
241                                        m_shm_pool->size * sizeof(uint32_t),
242                                        width,
243                                        height,
244                                        stride,
245                                        PIXEL_FORMAT_ID);
246     if (NULL == buffer)
247         return NULL;
248 
249     m_shm_pool->size += stride * height;
250     return buffer;
251 }
252 
FreeShmBuffer(struct wl_buffer * buffer)253 void Wayland::FreeShmBuffer(struct wl_buffer* buffer) {
254     wl_buffer_destroy(buffer);
255 }
256 
Dispatch()257 int Wayland::Dispatch() {
258     return wl_display_dispatch(m_display);
259 }
260 
CreatePlanarBuffer(uint32_t name,int32_t width,int32_t height,uint32_t format,int32_t offsets[3],int32_t pitches[3])261 struct wl_buffer* Wayland::CreatePlanarBuffer(uint32_t name,
262                                               int32_t width,
263                                               int32_t height,
264                                               uint32_t format,
265                                               int32_t offsets[3],
266                                               int32_t pitches[3]) {
267     struct wl_buffer* buffer = NULL;
268     if (NULL == m_drm)
269         return NULL;
270 
271     buffer = wl_drm_create_planar_buffer(m_drm,
272                                          name,
273                                          width,
274                                          height,
275                                          format,
276                                          offsets[0],
277                                          pitches[0],
278                                          offsets[1],
279                                          pitches[1],
280                                          offsets[2],
281                                          pitches[2]);
282     return buffer;
283 }
284 
CreatePrimeBuffer(uint32_t name,int32_t width,int32_t height,uint32_t format,int32_t offsets[3],int32_t pitches[3])285 struct wl_buffer* Wayland::CreatePrimeBuffer(uint32_t name,
286                                              int32_t width,
287                                              int32_t height,
288                                              uint32_t format,
289                                              int32_t offsets[3],
290                                              int32_t pitches[3]) {
291     struct wl_buffer* buffer = NULL;
292     if (NULL == m_drm)
293         return NULL;
294 
295     buffer = wl_drm_create_prime_buffer(m_drm,
296                                         name,
297                                         width,
298                                         height,
299                                         format,
300                                         offsets[0],
301                                         pitches[0],
302                                         offsets[1],
303                                         pitches[1],
304                                         offsets[2],
305                                         pitches[2]);
306     return buffer;
307 }
308 
~Wayland()309 Wayland::~Wayland() {
310     if (NULL != m_shell)
311         wl_shell_destroy(m_shell);
312     if (NULL != m_shm)
313         wl_shm_destroy(m_shm);
314     if (NULL != m_bufmgr) {
315         drm_intel_bufmgr_destroy(m_bufmgr);
316     }
317     if (NULL != m_compositor)
318         wl_compositor_destroy(m_compositor);
319     if (NULL != m_event_queue)
320         wl_event_queue_destroy(m_event_queue);
321     if (0 != m_buffers_list.size())
322         DestroyBufferList();
323     if (NULL != m_registry)
324         wl_registry_destroy(m_registry);
325     if (NULL != m_display)
326         wl_display_disconnect(m_display);
327     if (NULL != m_device_name)
328         delete m_device_name;
329 }
330 
331 // Registry
RegistryGlobal(struct wl_registry * registry,uint32_t name,const char * interface,uint32_t version)332 void Wayland::RegistryGlobal(struct wl_registry* registry,
333                              uint32_t name,
334                              const char* interface,
335                              uint32_t version) {
336     if (0 == strcmp(interface, "wl_compositor"))
337         m_compositor = static_cast<wl_compositor*>(
338             wl_registry_bind(registry, name, &wl_compositor_interface, version));
339     else if (0 == strcmp(interface, "wl_shell"))
340         m_shell =
341             static_cast<wl_shell*>(wl_registry_bind(registry, name, &wl_shell_interface, version));
342     else if (0 == strcmp(interface, "wl_drm")) {
343         static const struct wl_drm_listener drm_listener = { drm_handle_device,
344                                                              drm_handle_format,
345                                                              drm_handle_authenticated,
346                                                              drm_handle_capabilities };
347         m_drm = static_cast<wl_drm*>(wl_registry_bind(registry, name, &wl_drm_interface, 2));
348         wl_drm_add_listener(m_drm, &drm_listener, this);
349     }
350 }
351 
DrmHandleDevice(const char * name)352 void Wayland::DrmHandleDevice(const char* name) {
353     m_device_name = strdup(name);
354     if (!m_device_name)
355         return;
356 
357     drm_magic_t magic;
358     m_fd = open(m_device_name, O_RDWR | O_CLOEXEC);
359     if (-1 == m_fd) {
360         std::cout << "Error: Could not open " << m_device_name << "\n";
361         return;
362     }
363     int type = drmGetNodeTypeFromFd(m_fd);
364     if (type != DRM_NODE_RENDER) {
365         drmGetMagic(m_fd, &magic);
366         wl_drm_authenticate(m_drm, magic);
367     }
368 }
369 
DrmHandleAuthenticated()370 void Wayland::DrmHandleAuthenticated() {
371     m_bufmgr = drm_intel_bufmgr_gem_init(m_fd, BATCH_SIZE);
372 }
373 
AddBufferToList(wld_buffer * buffer)374 void Wayland::AddBufferToList(wld_buffer* buffer) {
375     if (buffer == NULL)
376         return;
377 
378     if (buffer->pInSurface) {
379         msdkFrameSurface* surface = FindUsedSurface(buffer->pInSurface);
380         msdk_atomic_inc16(&(surface->render_lock));
381         m_buffers_list.push_back(buffer);
382     }
383 }
384 
RemoveBufferFromList(struct wl_buffer * buffer)385 void Wayland::RemoveBufferFromList(struct wl_buffer* buffer) {
386     wld_buffer* m_buffer = NULL;
387     m_buffer             = m_buffers_list.front();
388     if (NULL != m_buffer && (m_buffer->buffer == buffer)) {
389         if (m_buffer->pInSurface) {
390             msdkFrameSurface* surface = FindUsedSurface(m_buffer->pInSurface);
391             msdk_atomic_dec16(&(surface->render_lock));
392         }
393         m_buffer->buffer     = NULL;
394         m_buffer->pInSurface = NULL;
395         m_buffers_list.pop_front();
396         delete m_buffer;
397     }
398 }
399 
DestroyBufferList()400 void Wayland::DestroyBufferList() {
401     wld_buffer* m_buffer = NULL;
402     while (!m_buffers_list.empty()) {
403         m_buffer = m_buffers_list.front();
404         if (m_buffer->pInSurface) {
405             msdkFrameSurface* surface = FindUsedSurface(m_buffer->pInSurface);
406             msdk_atomic_dec16(&(surface->render_lock));
407         }
408         m_buffers_list.pop_front();
409         delete m_buffer;
410     }
411 }
412 
WaylandCreate()413 Wayland* WaylandCreate() {
414     return new Wayland;
415 }
416 
WaylandDestroy(Wayland * pWld)417 void WaylandDestroy(Wayland* pWld) {
418     delete pWld;
419 }
420