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, ®istry_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