1 /*
2 * Copyright © 2011 Benjamin Franzke
3 * Copyright © 2010 Intel Corporation
4 * Copyright © 2014,2018 Collabora Ltd.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial
16 * portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28 #include "config.h"
29
30 #include <assert.h>
31 #include <fcntl.h>
32 #include <getopt.h>
33 #include <signal.h>
34 #include <stdbool.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <sys/time.h>
41
42 #include <drm_fourcc.h>
43 #include <xf86drm.h>
44
45 #include <wayland-client.h>
46 #include "shared/helpers.h"
47 #include "shared/platform.h"
48 #include <libweston/zalloc.h>
49 #include "xdg-shell-client-protocol.h"
50 #include "fullscreen-shell-unstable-v1-client-protocol.h"
51 #include "linux-dmabuf-unstable-v1-client-protocol.h"
52 #include "weston-direct-display-client-protocol.h"
53 #include "linux-explicit-synchronization-unstable-v1-client-protocol.h"
54
55 #include <EGL/egl.h>
56 #include <EGL/eglext.h>
57 #include <GLES2/gl2.h>
58 #include <GLES2/gl2ext.h>
59
60 #include "shared/weston-egl-ext.h"
61
62 #ifndef DRM_FORMAT_MOD_INVALID
63 #define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
64 #endif
65
66 /* Possible options that affect the displayed image */
67 #define OPT_IMMEDIATE (1 << 0) /* create wl_buffer immediately */
68 #define OPT_IMPLICIT_SYNC (1 << 1) /* force implicit sync */
69 #define OPT_MANDELBROT (1 << 2) /* render mandelbrot */
70 #define OPT_DIRECT_DISPLAY (1 << 3) /* direct-display */
71
72 #define BUFFER_FORMAT DRM_FORMAT_XRGB8888
73 #define MAX_BUFFER_PLANES 4
74
75 struct display {
76 struct wl_display *display;
77 struct wl_registry *registry;
78 struct wl_compositor *compositor;
79 struct xdg_wm_base *wm_base;
80 struct zwp_fullscreen_shell_v1 *fshell;
81 struct zwp_linux_dmabuf_v1 *dmabuf;
82 struct weston_direct_display_v1 *direct_display;
83 struct zwp_linux_explicit_synchronization_v1 *explicit_sync;
84 uint64_t *modifiers;
85 int modifiers_count;
86 int req_dmabuf_immediate;
87 bool use_explicit_sync;
88 struct {
89 EGLDisplay display;
90 EGLContext context;
91 EGLConfig conf;
92 bool has_dma_buf_import_modifiers;
93 bool has_no_config_context;
94 PFNEGLQUERYDMABUFMODIFIERSEXTPROC query_dma_buf_modifiers;
95 PFNEGLCREATEIMAGEKHRPROC create_image;
96 PFNEGLDESTROYIMAGEKHRPROC destroy_image;
97 PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
98 PFNEGLCREATESYNCKHRPROC create_sync;
99 PFNEGLDESTROYSYNCKHRPROC destroy_sync;
100 PFNEGLCLIENTWAITSYNCKHRPROC client_wait_sync;
101 PFNEGLDUPNATIVEFENCEFDANDROIDPROC dup_native_fence_fd;
102 PFNEGLWAITSYNCKHRPROC wait_sync;
103 } egl;
104 struct {
105 int drm_fd;
106 struct gbm_device *device;
107 } gbm;
108 };
109
110 struct buffer {
111 struct display *display;
112 struct wl_buffer *buffer;
113 int busy;
114
115 struct gbm_bo *bo;
116
117 int width;
118 int height;
119 int format;
120 uint64_t modifier;
121 int plane_count;
122 int dmabuf_fds[MAX_BUFFER_PLANES];
123 uint32_t strides[MAX_BUFFER_PLANES];
124 uint32_t offsets[MAX_BUFFER_PLANES];
125
126 EGLImageKHR egl_image;
127 GLuint gl_texture;
128 GLuint gl_fbo;
129
130 struct zwp_linux_buffer_release_v1 *buffer_release;
131 /* The buffer owns the release_fence_fd, until it passes ownership
132 * to it to EGL (see wait_for_buffer_release_fence). */
133 int release_fence_fd;
134 };
135
136 #define NUM_BUFFERS 3
137
138 struct window {
139 struct display *display;
140 int width, height;
141 struct wl_surface *surface;
142 struct xdg_surface *xdg_surface;
143 struct xdg_toplevel *xdg_toplevel;
144 struct zwp_linux_surface_synchronization_v1 *surface_sync;
145 struct buffer buffers[NUM_BUFFERS];
146 struct wl_callback *callback;
147 bool initialized;
148 bool wait_for_configure;
149 struct {
150 GLuint program;
151 GLuint pos;
152 GLuint color;
153 GLuint offset_uniform;
154 } gl;
155 bool render_mandelbrot;
156 };
157
158 static sig_atomic_t running = 1;
159
160 static void
161 redraw(void *data, struct wl_callback *callback, uint32_t time);
162
163 static void
buffer_release(void * data,struct wl_buffer * buffer)164 buffer_release(void *data, struct wl_buffer *buffer)
165 {
166 struct buffer *mybuf = data;
167
168 mybuf->busy = 0;
169 }
170
171 static const struct wl_buffer_listener buffer_listener = {
172 buffer_release
173 };
174
175 static void
buffer_free(struct buffer * buf)176 buffer_free(struct buffer *buf)
177 {
178 int i;
179
180 if (buf->release_fence_fd >= 0)
181 close(buf->release_fence_fd);
182
183 if (buf->buffer_release)
184 zwp_linux_buffer_release_v1_destroy(buf->buffer_release);
185
186 if (buf->gl_fbo)
187 glDeleteFramebuffers(1, &buf->gl_fbo);
188
189 if (buf->gl_texture)
190 glDeleteTextures(1, &buf->gl_texture);
191
192 if (buf->egl_image) {
193 buf->display->egl.destroy_image(buf->display->egl.display,
194 buf->egl_image);
195 }
196
197 if (buf->buffer)
198 wl_buffer_destroy(buf->buffer);
199
200 if (buf->bo)
201 gbm_bo_destroy(buf->bo);
202
203 for (i = 0; i < buf->plane_count; ++i) {
204 if (buf->dmabuf_fds[i] >= 0)
205 close(buf->dmabuf_fds[i]);
206 }
207 }
208
209 static void
create_succeeded(void * data,struct zwp_linux_buffer_params_v1 * params,struct wl_buffer * new_buffer)210 create_succeeded(void *data,
211 struct zwp_linux_buffer_params_v1 *params,
212 struct wl_buffer *new_buffer)
213 {
214 struct buffer *buffer = data;
215
216 buffer->buffer = new_buffer;
217 /* When not using explicit synchronization listen to wl_buffer.release
218 * for release notifications, otherwise we are going to use
219 * zwp_linux_buffer_release_v1. */
220 if (!buffer->display->use_explicit_sync) {
221 wl_buffer_add_listener(buffer->buffer, &buffer_listener,
222 buffer);
223 }
224
225 zwp_linux_buffer_params_v1_destroy(params);
226 }
227
228 static void
create_failed(void * data,struct zwp_linux_buffer_params_v1 * params)229 create_failed(void *data, struct zwp_linux_buffer_params_v1 *params)
230 {
231 struct buffer *buffer = data;
232
233 buffer->buffer = NULL;
234 running = 0;
235
236 zwp_linux_buffer_params_v1_destroy(params);
237
238 fprintf(stderr, "Error: zwp_linux_buffer_params.create failed.\n");
239 }
240
241 static const struct zwp_linux_buffer_params_v1_listener params_listener = {
242 create_succeeded,
243 create_failed
244 };
245
246 static bool
create_fbo_for_buffer(struct display * display,struct buffer * buffer)247 create_fbo_for_buffer(struct display *display, struct buffer *buffer)
248 {
249 static const int general_attribs = 3;
250 static const int plane_attribs = 5;
251 static const int entries_per_attrib = 2;
252 EGLint attribs[(general_attribs + plane_attribs * MAX_BUFFER_PLANES) *
253 entries_per_attrib + 1];
254 unsigned int atti = 0;
255
256 attribs[atti++] = EGL_WIDTH;
257 attribs[atti++] = buffer->width;
258 attribs[atti++] = EGL_HEIGHT;
259 attribs[atti++] = buffer->height;
260 attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
261 attribs[atti++] = buffer->format;
262
263 #define ADD_PLANE_ATTRIBS(plane_idx) { \
264 attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _FD_EXT; \
265 attribs[atti++] = buffer->dmabuf_fds[plane_idx]; \
266 attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _OFFSET_EXT; \
267 attribs[atti++] = (int) buffer->offsets[plane_idx]; \
268 attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _PITCH_EXT; \
269 attribs[atti++] = (int) buffer->strides[plane_idx]; \
270 if (display->egl.has_dma_buf_import_modifiers) { \
271 attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_LO_EXT; \
272 attribs[atti++] = buffer->modifier & 0xFFFFFFFF; \
273 attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_HI_EXT; \
274 attribs[atti++] = buffer->modifier >> 32; \
275 } \
276 }
277
278 if (buffer->plane_count > 0)
279 ADD_PLANE_ATTRIBS(0);
280
281 if (buffer->plane_count > 1)
282 ADD_PLANE_ATTRIBS(1);
283
284 if (buffer->plane_count > 2)
285 ADD_PLANE_ATTRIBS(2);
286
287 if (buffer->plane_count > 3)
288 ADD_PLANE_ATTRIBS(3);
289
290 #undef ADD_PLANE_ATTRIBS
291
292 attribs[atti] = EGL_NONE;
293
294 assert(atti < ARRAY_LENGTH(attribs));
295
296 buffer->egl_image = display->egl.create_image(display->egl.display,
297 EGL_NO_CONTEXT,
298 EGL_LINUX_DMA_BUF_EXT,
299 NULL, attribs);
300 if (buffer->egl_image == EGL_NO_IMAGE_KHR) {
301 fprintf(stderr, "EGLImageKHR creation failed\n");
302 return false;
303 }
304
305 eglMakeCurrent(display->egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
306 display->egl.context);
307
308 glGenTextures(1, &buffer->gl_texture);
309 glBindTexture(GL_TEXTURE_2D, buffer->gl_texture);
310 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
311 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
312 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
313 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
314
315 display->egl.image_target_texture_2d(GL_TEXTURE_2D, buffer->egl_image);
316
317 glGenFramebuffers(1, &buffer->gl_fbo);
318 glBindFramebuffer(GL_FRAMEBUFFER, buffer->gl_fbo);
319 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
320 GL_TEXTURE_2D, buffer->gl_texture, 0);
321 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
322 fprintf(stderr, "FBO creation failed\n");
323 return false;
324 }
325
326 return true;
327 }
328
329
330 static int
create_dmabuf_buffer(struct display * display,struct buffer * buffer,int width,int height,uint32_t opts)331 create_dmabuf_buffer(struct display *display, struct buffer *buffer,
332 int width, int height, uint32_t opts)
333 {
334 /* Y-Invert the buffer image, since we are going to renderer to the
335 * buffer through a FBO. */
336 static uint32_t flags = ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT;
337 struct zwp_linux_buffer_params_v1 *params;
338 int i;
339
340 buffer->display = display;
341 buffer->width = width;
342 buffer->height = height;
343 buffer->format = BUFFER_FORMAT;
344 buffer->release_fence_fd = -1;
345
346 #ifdef HAVE_GBM_MODIFIERS
347 if (display->modifiers_count > 0) {
348 buffer->bo = gbm_bo_create_with_modifiers(display->gbm.device,
349 buffer->width,
350 buffer->height,
351 buffer->format,
352 display->modifiers,
353 display->modifiers_count);
354 if (buffer->bo)
355 buffer->modifier = gbm_bo_get_modifier(buffer->bo);
356 }
357 #endif
358
359 if (!buffer->bo) {
360 buffer->bo = gbm_bo_create(display->gbm.device,
361 buffer->width,
362 buffer->height,
363 buffer->format,
364 GBM_BO_USE_RENDERING);
365 buffer->modifier = DRM_FORMAT_MOD_INVALID;
366 }
367
368 if (!buffer->bo) {
369 fprintf(stderr, "create_bo failed\n");
370 goto error;
371 }
372
373 #ifdef HAVE_GBM_MODIFIERS
374 buffer->plane_count = gbm_bo_get_plane_count(buffer->bo);
375 for (i = 0; i < buffer->plane_count; ++i) {
376 int ret;
377 union gbm_bo_handle handle;
378
379 handle = gbm_bo_get_handle_for_plane(buffer->bo, i);
380 if (handle.s32 == -1) {
381 fprintf(stderr, "error: failed to get gbm_bo_handle\n");
382 goto error;
383 }
384
385 ret = drmPrimeHandleToFD(display->gbm.drm_fd, handle.u32, 0,
386 &buffer->dmabuf_fds[i]);
387 if (ret < 0 || buffer->dmabuf_fds[i] < 0) {
388 fprintf(stderr, "error: failed to get dmabuf_fd\n");
389 goto error;
390 }
391 buffer->strides[i] = gbm_bo_get_stride_for_plane(buffer->bo, i);
392 buffer->offsets[i] = gbm_bo_get_offset(buffer->bo, i);
393 }
394 #else
395 buffer->plane_count = 1;
396 buffer->strides[0] = gbm_bo_get_stride(buffer->bo);
397 buffer->dmabuf_fds[0] = gbm_bo_get_fd(buffer->bo);
398 if (buffer->dmabuf_fds[0] < 0) {
399 fprintf(stderr, "error: failed to get dmabuf_fd\n");
400 goto error;
401 }
402 #endif
403
404 params = zwp_linux_dmabuf_v1_create_params(display->dmabuf);
405
406 if ((opts & OPT_DIRECT_DISPLAY) && display->direct_display) {
407 weston_direct_display_v1_enable(display->direct_display, params);
408 /* turn off Y_INVERT otherwise linux-dmabuf will reject it and
409 * we need all dmabuf flags turned off */
410 flags &= ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT;
411
412 fprintf(stdout, "image is y-inverted as direct-display flag was set, "
413 "dmabuf y-inverted attribute flag was removed\n");
414 }
415
416 for (i = 0; i < buffer->plane_count; ++i) {
417 zwp_linux_buffer_params_v1_add(params,
418 buffer->dmabuf_fds[i],
419 i,
420 buffer->offsets[i],
421 buffer->strides[i],
422 buffer->modifier >> 32,
423 buffer->modifier & 0xffffffff);
424 }
425
426 zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, buffer);
427 if (display->req_dmabuf_immediate) {
428 buffer->buffer =
429 zwp_linux_buffer_params_v1_create_immed(params,
430 buffer->width,
431 buffer->height,
432 buffer->format,
433 flags);
434 /* When not using explicit synchronization listen to
435 * wl_buffer.release for release notifications, otherwise we
436 * are going to use zwp_linux_buffer_release_v1. */
437 if (!buffer->display->use_explicit_sync) {
438 wl_buffer_add_listener(buffer->buffer,
439 &buffer_listener,
440 buffer);
441 }
442 }
443 else {
444 zwp_linux_buffer_params_v1_create(params,
445 buffer->width,
446 buffer->height,
447 buffer->format,
448 flags);
449 }
450
451 if (!create_fbo_for_buffer(display, buffer))
452 goto error;
453
454 return 0;
455
456 error:
457 buffer_free(buffer);
458 return -1;
459 }
460
461 static void
xdg_surface_handle_configure(void * data,struct xdg_surface * surface,uint32_t serial)462 xdg_surface_handle_configure(void *data, struct xdg_surface *surface,
463 uint32_t serial)
464 {
465 struct window *window = data;
466
467 xdg_surface_ack_configure(surface, serial);
468
469 if (window->initialized && window->wait_for_configure)
470 redraw(window, NULL, 0);
471 window->wait_for_configure = false;
472 }
473
474 static const struct xdg_surface_listener xdg_surface_listener = {
475 xdg_surface_handle_configure,
476 };
477
478 static void
xdg_toplevel_handle_configure(void * data,struct xdg_toplevel * toplevel,int32_t width,int32_t height,struct wl_array * states)479 xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *toplevel,
480 int32_t width, int32_t height,
481 struct wl_array *states)
482 {
483 }
484
485 static void
xdg_toplevel_handle_close(void * data,struct xdg_toplevel * xdg_toplevel)486 xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel)
487 {
488 running = 0;
489 }
490
491 static const struct xdg_toplevel_listener xdg_toplevel_listener = {
492 xdg_toplevel_handle_configure,
493 xdg_toplevel_handle_close,
494 };
495
496 static const char *vert_shader_text =
497 "uniform float offset;\n"
498 "attribute vec4 pos;\n"
499 "attribute vec4 color;\n"
500 "varying vec4 v_color;\n"
501 "void main() {\n"
502 " gl_Position = pos + vec4(offset, offset, 0.0, 0.0);\n"
503 " v_color = color;\n"
504 "}\n";
505
506 static const char *frag_shader_text =
507 "precision mediump float;\n"
508 "varying vec4 v_color;\n"
509 "void main() {\n"
510 " gl_FragColor = v_color;\n"
511 "}\n";
512
513 static const char *vert_shader_mandelbrot_text =
514 "uniform float offset;\n"
515 "attribute vec4 pos;\n"
516 "varying vec2 v_pos;\n"
517 "void main() {\n"
518 " v_pos = pos.xy;\n"
519 " gl_Position = pos + vec4(offset, offset, 0.0, 0.0);\n"
520 "}\n";
521
522
523 /* Mandelbrot set shader using the escape time algorithm. */
524 static const char *frag_shader_mandelbrot_text =
525 "precision mediump float;\n"
526 "varying vec2 v_pos;\n"
527 "void main() {\n"
528 " const int max_iteration = 500;\n"
529 " // Scale and translate position to get a nice mandelbrot drawing for\n"
530 " // the used v_pos x and y range (-0.5 to 0.5).\n"
531 " float x0 = 3.0 * v_pos.x - 0.5;\n"
532 " float y0 = 3.0 * v_pos.y;\n"
533 " float x = 0.0;\n"
534 " float y = 0.0;\n"
535 " int iteration = 0;\n"
536 " while (x * x + y * y <= 4.0 && iteration < max_iteration) {\n"
537 " float xtemp = x * x - y * y + x0;\n"
538 " y = 2.0 * x * y + y0;\n"
539 " x = xtemp;\n"
540 " ++iteration;\n"
541 " }\n"
542 " float red = iteration == max_iteration ?\n"
543 " 0.0 : 1.0 - fract(float(iteration) / 20.0);\n"
544 " gl_FragColor = vec4(red, 0.0, 0.0, 1.0);\n"
545 "}\n";
546
547 static GLuint
create_shader(const char * source,GLenum shader_type)548 create_shader(const char *source, GLenum shader_type)
549 {
550 GLuint shader;
551 GLint status;
552
553 shader = glCreateShader(shader_type);
554 assert(shader != 0);
555
556 glShaderSource(shader, 1, (const char **) &source, NULL);
557 glCompileShader(shader);
558
559 glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
560 if (!status) {
561 char log[1000];
562 GLsizei len;
563 glGetShaderInfoLog(shader, 1000, &len, log);
564 fprintf(stderr, "Error: compiling %s: %.*s\n",
565 shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
566 len, log);
567 return 0;
568 }
569
570 return shader;
571 }
572
573 static GLuint
create_and_link_program(GLuint vert,GLuint frag)574 create_and_link_program(GLuint vert, GLuint frag)
575 {
576 GLint status;
577 GLuint program = glCreateProgram();
578
579 glAttachShader(program, vert);
580 glAttachShader(program, frag);
581 glLinkProgram(program);
582
583 glGetProgramiv(program, GL_LINK_STATUS, &status);
584 if (!status) {
585 char log[1000];
586 GLsizei len;
587 glGetProgramInfoLog(program, 1000, &len, log);
588 fprintf(stderr, "Error: linking:\n%.*s\n", len, log);
589 return 0;
590 }
591
592 return program;
593 }
594
595 static bool
window_set_up_gl(struct window * window)596 window_set_up_gl(struct window *window)
597 {
598 GLuint vert = create_shader(
599 window->render_mandelbrot ? vert_shader_mandelbrot_text :
600 vert_shader_text,
601 GL_VERTEX_SHADER);
602 GLuint frag = create_shader(
603 window->render_mandelbrot ? frag_shader_mandelbrot_text :
604 frag_shader_text,
605 GL_FRAGMENT_SHADER);
606
607 window->gl.program = create_and_link_program(vert, frag);
608
609 glDeleteShader(vert);
610 glDeleteShader(frag);
611
612 window->gl.pos = glGetAttribLocation(window->gl.program, "pos");
613 window->gl.color = glGetAttribLocation(window->gl.program, "color");
614
615 glUseProgram(window->gl.program);
616
617 window->gl.offset_uniform =
618 glGetUniformLocation(window->gl.program, "offset");
619
620 return window->gl.program != 0;
621 }
622
623 static void
destroy_window(struct window * window)624 destroy_window(struct window *window)
625 {
626 int i;
627
628 if (window->gl.program)
629 glDeleteProgram(window->gl.program);
630
631 if (window->callback)
632 wl_callback_destroy(window->callback);
633
634 for (i = 0; i < NUM_BUFFERS; i++) {
635 if (window->buffers[i].buffer)
636 buffer_free(&window->buffers[i]);
637 }
638
639 if (window->xdg_toplevel)
640 xdg_toplevel_destroy(window->xdg_toplevel);
641 if (window->xdg_surface)
642 xdg_surface_destroy(window->xdg_surface);
643 if (window->surface_sync)
644 zwp_linux_surface_synchronization_v1_destroy(window->surface_sync);
645 wl_surface_destroy(window->surface);
646 free(window);
647 }
648
649 static struct window *
create_window(struct display * display,int width,int height,int opts)650 create_window(struct display *display, int width, int height, int opts)
651 {
652 struct window *window;
653 int i;
654 int ret;
655
656 window = zalloc(sizeof *window);
657 if (!window)
658 return NULL;
659
660 window->callback = NULL;
661 window->display = display;
662 window->width = width;
663 window->height = height;
664 window->surface = wl_compositor_create_surface(display->compositor);
665
666 if (display->wm_base) {
667 window->xdg_surface =
668 xdg_wm_base_get_xdg_surface(display->wm_base,
669 window->surface);
670
671 assert(window->xdg_surface);
672
673 xdg_surface_add_listener(window->xdg_surface,
674 &xdg_surface_listener, window);
675
676 window->xdg_toplevel =
677 xdg_surface_get_toplevel(window->xdg_surface);
678
679 assert(window->xdg_toplevel);
680
681 xdg_toplevel_add_listener(window->xdg_toplevel,
682 &xdg_toplevel_listener, window);
683
684 xdg_toplevel_set_title(window->xdg_toplevel, "simple-dmabuf-egl");
685
686 window->wait_for_configure = true;
687 wl_surface_commit(window->surface);
688 } else if (display->fshell) {
689 zwp_fullscreen_shell_v1_present_surface(display->fshell,
690 window->surface,
691 ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT,
692 NULL);
693 } else {
694 assert(0);
695 }
696
697 if (display->explicit_sync) {
698 window->surface_sync =
699 zwp_linux_explicit_synchronization_v1_get_synchronization(
700 display->explicit_sync, window->surface);
701 assert(window->surface_sync);
702 }
703
704 for (i = 0; i < NUM_BUFFERS; ++i) {
705 int j;
706 for (j = 0; j < MAX_BUFFER_PLANES; ++j)
707 window->buffers[i].dmabuf_fds[j] = -1;
708
709 }
710
711 for (i = 0; i < NUM_BUFFERS; ++i) {
712 ret = create_dmabuf_buffer(display, &window->buffers[i],
713 width, height, opts);
714
715 if (ret < 0)
716 goto error;
717 }
718
719 window->render_mandelbrot = opts & OPT_MANDELBROT;
720
721 if (!window_set_up_gl(window))
722 goto error;
723
724 return window;
725
726 error:
727 if (window)
728 destroy_window(window);
729
730 return NULL;
731 }
732
733 static int
create_egl_fence_fd(struct window * window)734 create_egl_fence_fd(struct window *window)
735 {
736 struct display *d = window->display;
737 EGLSyncKHR sync = d->egl.create_sync(d->egl.display,
738 EGL_SYNC_NATIVE_FENCE_ANDROID,
739 NULL);
740 int fd;
741
742 assert(sync != EGL_NO_SYNC_KHR);
743 /* We need to flush before we can get the fence fd. */
744 glFlush();
745 fd = d->egl.dup_native_fence_fd(d->egl.display, sync);
746 assert(fd >= 0);
747
748 d->egl.destroy_sync(d->egl.display, sync);
749
750 return fd;
751 }
752
753 static struct buffer *
window_next_buffer(struct window * window)754 window_next_buffer(struct window *window)
755 {
756 int i;
757
758 for (i = 0; i < NUM_BUFFERS; i++)
759 if (!window->buffers[i].busy)
760 return &window->buffers[i];
761
762 return NULL;
763 }
764
765 static const struct wl_callback_listener frame_listener;
766
767 /* Renders a square moving from the lower left corner to the
768 * upper right corner of the window. The square's vertices have
769 * the following colors:
770 *
771 * green +-----+ yellow
772 * | |
773 * | |
774 * red +-----+ blue
775 */
776 static void
render(struct window * window,struct buffer * buffer)777 render(struct window *window, struct buffer *buffer)
778 {
779 /* Complete a movement iteration in 5000 ms. */
780 static const uint64_t iteration_ms = 5000;
781 static const GLfloat verts[4][2] = {
782 { -0.5, -0.5 },
783 { -0.5, 0.5 },
784 { 0.5, -0.5 },
785 { 0.5, 0.5 }
786 };
787 static const GLfloat colors[4][3] = {
788 { 1, 0, 0 },
789 { 0, 1, 0 },
790 { 0, 0, 1 },
791 { 1, 1, 0 }
792 };
793 GLfloat offset;
794 struct timeval tv;
795 uint64_t time_ms;
796
797 gettimeofday(&tv, NULL);
798 time_ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
799
800 /* Split time_ms in repeating windows of [0, iteration_ms) and map them
801 * to offsets in the [-0.5, 0.5) range. */
802 offset = (time_ms % iteration_ms) / (float) iteration_ms - 0.5;
803
804 /* Direct all GL draws to the buffer through the FBO */
805 glBindFramebuffer(GL_FRAMEBUFFER, buffer->gl_fbo);
806
807 glViewport(0, 0, window->width, window->height);
808
809 glUniform1f(window->gl.offset_uniform, offset);
810
811 glClearColor(0.0,0.0, 0.0, 1.0);
812 glClear(GL_COLOR_BUFFER_BIT);
813
814 glVertexAttribPointer(window->gl.pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
815 glVertexAttribPointer(window->gl.color, 3, GL_FLOAT, GL_FALSE, 0, colors);
816 glEnableVertexAttribArray(window->gl.pos);
817 glEnableVertexAttribArray(window->gl.color);
818
819 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
820
821 glDisableVertexAttribArray(window->gl.pos);
822 glDisableVertexAttribArray(window->gl.color);
823 }
824
825 static void
render_mandelbrot(struct window * window,struct buffer * buffer)826 render_mandelbrot(struct window *window, struct buffer *buffer)
827 {
828 /* Complete a movement iteration in 5000 ms. */
829 static const uint64_t iteration_ms = 5000;
830 /* Split drawing in a square grid consisting of grid_side * grid_side
831 * cells. */
832 static const int grid_side = 4;
833 GLfloat norm_cell_side = 1.0 / grid_side;
834 int num_cells = grid_side * grid_side;
835 GLfloat offset;
836 struct timeval tv;
837 uint64_t time_ms;
838 int i;
839
840 gettimeofday(&tv, NULL);
841 time_ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
842
843 /* Split time_ms in repeating windows of [0, iteration_ms) and map them
844 * to offsets in the [-0.5, 0.5) range. */
845 offset = (time_ms % iteration_ms) / (float) iteration_ms - 0.5;
846
847 /* Direct all GL draws to the buffer through the FBO */
848 glBindFramebuffer(GL_FRAMEBUFFER, buffer->gl_fbo);
849
850 glViewport(0, 0, window->width, window->height);
851
852 glUniform1f(window->gl.offset_uniform, offset);
853
854 glClearColor(0.6, 0.6, 0.6, 1.0);
855 glClear(GL_COLOR_BUFFER_BIT);
856
857 for (i = 0; i < num_cells; ++i) {
858 /* Calculate the vertex coordinates of the current grid cell. */
859 int row = i / grid_side;
860 int col = i % grid_side;
861 GLfloat left = -0.5 + norm_cell_side * col;
862 GLfloat right = left + norm_cell_side;
863 GLfloat top = 0.5 - norm_cell_side * row;
864 GLfloat bottom = top - norm_cell_side;
865 GLfloat verts[4][2] = {
866 { left, bottom },
867 { left, top },
868 { right, bottom },
869 { right, top }
870 };
871
872 /* ... and draw it. */
873 glVertexAttribPointer(window->gl.pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
874 glEnableVertexAttribArray(window->gl.pos);
875
876 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
877
878 glDisableVertexAttribArray(window->gl.pos);
879 }
880 }
881
882 static void
buffer_fenced_release(void * data,struct zwp_linux_buffer_release_v1 * release,int32_t fence)883 buffer_fenced_release(void *data,
884 struct zwp_linux_buffer_release_v1 *release,
885 int32_t fence)
886 {
887 struct buffer *buffer = data;
888
889 assert(release == buffer->buffer_release);
890 assert(buffer->release_fence_fd == -1);
891
892 buffer->busy = 0;
893 buffer->release_fence_fd = fence;
894 zwp_linux_buffer_release_v1_destroy(buffer->buffer_release);
895 buffer->buffer_release = NULL;
896 }
897
898 static void
buffer_immediate_release(void * data,struct zwp_linux_buffer_release_v1 * release)899 buffer_immediate_release(void *data,
900 struct zwp_linux_buffer_release_v1 *release)
901 {
902 struct buffer *buffer = data;
903
904 assert(release == buffer->buffer_release);
905 assert(buffer->release_fence_fd == -1);
906
907 buffer->busy = 0;
908 zwp_linux_buffer_release_v1_destroy(buffer->buffer_release);
909 buffer->buffer_release = NULL;
910 }
911
912 static const struct zwp_linux_buffer_release_v1_listener buffer_release_listener = {
913 buffer_fenced_release,
914 buffer_immediate_release,
915 };
916
917 static void
wait_for_buffer_release_fence(struct buffer * buffer)918 wait_for_buffer_release_fence(struct buffer *buffer)
919 {
920 struct display *d = buffer->display;
921 EGLint attrib_list[] = {
922 EGL_SYNC_NATIVE_FENCE_FD_ANDROID, buffer->release_fence_fd,
923 EGL_NONE,
924 };
925 EGLSyncKHR sync = d->egl.create_sync(d->egl.display,
926 EGL_SYNC_NATIVE_FENCE_ANDROID,
927 attrib_list);
928 int ret;
929
930 assert(sync);
931
932 /* EGLSyncKHR takes ownership of the fence fd. */
933 buffer->release_fence_fd = -1;
934
935 if (d->egl.wait_sync)
936 ret = d->egl.wait_sync(d->egl.display, sync, 0);
937 else
938 ret = d->egl.client_wait_sync(d->egl.display, sync, 0,
939 EGL_FOREVER_KHR);
940 assert(ret == EGL_TRUE);
941
942 ret = d->egl.destroy_sync(d->egl.display, sync);
943 assert(ret == EGL_TRUE);
944 }
945
946 static void
redraw(void * data,struct wl_callback * callback,uint32_t time)947 redraw(void *data, struct wl_callback *callback, uint32_t time)
948 {
949 struct window *window = data;
950 struct buffer *buffer;
951
952 buffer = window_next_buffer(window);
953 if (!buffer) {
954 fprintf(stderr,
955 !callback ? "Failed to create the first buffer.\n" :
956 "All buffers busy at redraw(). Server bug?\n");
957 abort();
958 }
959
960 if (buffer->release_fence_fd >= 0)
961 wait_for_buffer_release_fence(buffer);
962
963 if (window->render_mandelbrot)
964 render_mandelbrot(window, buffer);
965 else
966 render(window, buffer);
967
968 if (window->display->use_explicit_sync) {
969 int fence_fd = create_egl_fence_fd(window);
970 zwp_linux_surface_synchronization_v1_set_acquire_fence(
971 window->surface_sync, fence_fd);
972 close(fence_fd);
973
974 buffer->buffer_release =
975 zwp_linux_surface_synchronization_v1_get_release(window->surface_sync);
976 zwp_linux_buffer_release_v1_add_listener(
977 buffer->buffer_release, &buffer_release_listener, buffer);
978 } else {
979 glFinish();
980 }
981
982 wl_surface_attach(window->surface, buffer->buffer, 0, 0);
983 wl_surface_damage(window->surface, 0, 0, window->width, window->height);
984
985 if (callback)
986 wl_callback_destroy(callback);
987
988 window->callback = wl_surface_frame(window->surface);
989 wl_callback_add_listener(window->callback, &frame_listener, window);
990 wl_surface_commit(window->surface);
991 buffer->busy = 1;
992 }
993
994 static const struct wl_callback_listener frame_listener = {
995 redraw
996 };
997
998 static void
dmabuf_modifiers(void * data,struct zwp_linux_dmabuf_v1 * zwp_linux_dmabuf,uint32_t format,uint32_t modifier_hi,uint32_t modifier_lo)999 dmabuf_modifiers(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf,
1000 uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo)
1001 {
1002 struct display *d = data;
1003
1004 switch (format) {
1005 case BUFFER_FORMAT:
1006 ++d->modifiers_count;
1007 d->modifiers = realloc(d->modifiers,
1008 d->modifiers_count * sizeof(*d->modifiers));
1009 d->modifiers[d->modifiers_count - 1] =
1010 ((uint64_t)modifier_hi << 32) | modifier_lo;
1011 break;
1012 default:
1013 break;
1014 }
1015 }
1016
1017 static void
dmabuf_format(void * data,struct zwp_linux_dmabuf_v1 * zwp_linux_dmabuf,uint32_t format)1018 dmabuf_format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format)
1019 {
1020 /* XXX: deprecated */
1021 }
1022
1023 static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
1024 dmabuf_format,
1025 dmabuf_modifiers
1026 };
1027
1028 static void
xdg_wm_base_ping(void * data,struct xdg_wm_base * wm_base,uint32_t serial)1029 xdg_wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial)
1030 {
1031 xdg_wm_base_pong(wm_base, serial);
1032 }
1033
1034 static const struct xdg_wm_base_listener xdg_wm_base_listener = {
1035 xdg_wm_base_ping,
1036 };
1037
1038 static void
registry_handle_global(void * data,struct wl_registry * registry,uint32_t id,const char * interface,uint32_t version)1039 registry_handle_global(void *data, struct wl_registry *registry,
1040 uint32_t id, const char *interface, uint32_t version)
1041 {
1042 struct display *d = data;
1043
1044 if (strcmp(interface, "wl_compositor") == 0) {
1045 d->compositor =
1046 wl_registry_bind(registry,
1047 id, &wl_compositor_interface, 1);
1048 } else if (strcmp(interface, "xdg_wm_base") == 0) {
1049 d->wm_base = wl_registry_bind(registry,
1050 id, &xdg_wm_base_interface, 1);
1051 xdg_wm_base_add_listener(d->wm_base, &xdg_wm_base_listener, d);
1052 } else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
1053 d->fshell = wl_registry_bind(registry,
1054 id, &zwp_fullscreen_shell_v1_interface, 1);
1055 } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) {
1056 if (version < 3)
1057 return;
1058 d->dmabuf = wl_registry_bind(registry,
1059 id, &zwp_linux_dmabuf_v1_interface, 3);
1060 zwp_linux_dmabuf_v1_add_listener(d->dmabuf, &dmabuf_listener, d);
1061 } else if (strcmp(interface, "zwp_linux_explicit_synchronization_v1") == 0) {
1062 d->explicit_sync = wl_registry_bind(
1063 registry, id,
1064 &zwp_linux_explicit_synchronization_v1_interface, 1);
1065 } else if (strcmp(interface, "weston_direct_display_v1") == 0) {
1066 d->direct_display = wl_registry_bind(registry,
1067 id, &weston_direct_display_v1_interface, 1);
1068 }
1069 }
1070
1071 static void
registry_handle_global_remove(void * data,struct wl_registry * registry,uint32_t name)1072 registry_handle_global_remove(void *data, struct wl_registry *registry,
1073 uint32_t name)
1074 {
1075 }
1076
1077 static const struct wl_registry_listener registry_listener = {
1078 registry_handle_global,
1079 registry_handle_global_remove
1080 };
1081
1082 static void
destroy_display(struct display * display)1083 destroy_display(struct display *display)
1084 {
1085 if (display->gbm.device)
1086 gbm_device_destroy(display->gbm.device);
1087
1088 if (display->gbm.drm_fd >= 0)
1089 close(display->gbm.drm_fd);
1090
1091 if (display->egl.context != EGL_NO_CONTEXT)
1092 eglDestroyContext(display->egl.display, display->egl.context);
1093
1094 if (display->egl.display != EGL_NO_DISPLAY)
1095 eglTerminate(display->egl.display);
1096
1097 free(display->modifiers);
1098
1099 if (display->dmabuf)
1100 zwp_linux_dmabuf_v1_destroy(display->dmabuf);
1101
1102 if (display->wm_base)
1103 xdg_wm_base_destroy(display->wm_base);
1104
1105 if (display->fshell)
1106 zwp_fullscreen_shell_v1_release(display->fshell);
1107
1108 if (display->compositor)
1109 wl_compositor_destroy(display->compositor);
1110
1111 if (display->registry)
1112 wl_registry_destroy(display->registry);
1113
1114 if (display->display) {
1115 wl_display_flush(display->display);
1116 wl_display_disconnect(display->display);
1117 }
1118
1119 free(display);
1120 }
1121
1122 static bool
display_set_up_egl(struct display * display)1123 display_set_up_egl(struct display *display)
1124 {
1125 static const EGLint context_attribs[] = {
1126 EGL_CONTEXT_CLIENT_VERSION, 2,
1127 EGL_NONE
1128 };
1129 EGLint major, minor, ret, count;
1130 const char *egl_extensions = NULL;
1131 const char *gl_extensions = NULL;
1132
1133 EGLint config_attribs[] = {
1134 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
1135 EGL_RED_SIZE, 1,
1136 EGL_GREEN_SIZE, 1,
1137 EGL_BLUE_SIZE, 1,
1138 EGL_ALPHA_SIZE, 1,
1139 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
1140 EGL_NONE
1141 };
1142
1143 display->egl.display =
1144 weston_platform_get_egl_display(EGL_PLATFORM_GBM_KHR,
1145 display->gbm.device, NULL);
1146 if (display->egl.display == EGL_NO_DISPLAY) {
1147 fprintf(stderr, "Failed to create EGLDisplay\n");
1148 goto error;
1149 }
1150
1151 if (eglInitialize(display->egl.display, &major, &minor) == EGL_FALSE) {
1152 fprintf(stderr, "Failed to initialize EGLDisplay\n");
1153 goto error;
1154 }
1155
1156 if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) {
1157 fprintf(stderr, "Failed to bind OpenGL ES API\n");
1158 goto error;
1159 }
1160
1161 egl_extensions = eglQueryString(display->egl.display, EGL_EXTENSIONS);
1162 assert(egl_extensions != NULL);
1163
1164 if (!weston_check_egl_extension(egl_extensions,
1165 "EGL_EXT_image_dma_buf_import")) {
1166 fprintf(stderr, "EGL_EXT_image_dma_buf_import not supported\n");
1167 goto error;
1168 }
1169
1170 if (!weston_check_egl_extension(egl_extensions,
1171 "EGL_KHR_surfaceless_context")) {
1172 fprintf(stderr, "EGL_KHR_surfaceless_context not supported\n");
1173 goto error;
1174 }
1175
1176 if (weston_check_egl_extension(egl_extensions,
1177 "EGL_KHR_no_config_context")) {
1178 display->egl.has_no_config_context = true;
1179 }
1180
1181 if (display->egl.has_no_config_context) {
1182 display->egl.conf = EGL_NO_CONFIG_KHR;
1183 } else {
1184 fprintf(stderr,
1185 "Warning: EGL_KHR_no_config_context not supported\n");
1186 ret = eglChooseConfig(display->egl.display, config_attribs,
1187 &display->egl.conf, 1, &count);
1188 assert(ret && count >= 1);
1189 }
1190
1191 display->egl.context = eglCreateContext(display->egl.display,
1192 display->egl.conf,
1193 EGL_NO_CONTEXT,
1194 context_attribs);
1195 if (display->egl.context == EGL_NO_CONTEXT) {
1196 fprintf(stderr, "Failed to create EGLContext\n");
1197 goto error;
1198 }
1199
1200 eglMakeCurrent(display->egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
1201 display->egl.context);
1202
1203 gl_extensions = (const char *) glGetString(GL_EXTENSIONS);
1204 assert(gl_extensions != NULL);
1205
1206 if (!weston_check_egl_extension(gl_extensions,
1207 "GL_OES_EGL_image")) {
1208 fprintf(stderr, "GL_OES_EGL_image not supported\n");
1209 goto error;
1210 }
1211
1212 if (weston_check_egl_extension(egl_extensions,
1213 "EGL_EXT_image_dma_buf_import_modifiers")) {
1214 display->egl.has_dma_buf_import_modifiers = true;
1215 display->egl.query_dma_buf_modifiers =
1216 (void *) eglGetProcAddress("eglQueryDmaBufModifiersEXT");
1217 assert(display->egl.query_dma_buf_modifiers);
1218 }
1219
1220 display->egl.create_image =
1221 (void *) eglGetProcAddress("eglCreateImageKHR");
1222 assert(display->egl.create_image);
1223
1224 display->egl.destroy_image =
1225 (void *) eglGetProcAddress("eglDestroyImageKHR");
1226 assert(display->egl.destroy_image);
1227
1228 display->egl.image_target_texture_2d =
1229 (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
1230 assert(display->egl.image_target_texture_2d);
1231
1232 if (weston_check_egl_extension(egl_extensions, "EGL_KHR_fence_sync") &&
1233 weston_check_egl_extension(egl_extensions,
1234 "EGL_ANDROID_native_fence_sync")) {
1235 display->egl.create_sync =
1236 (void *) eglGetProcAddress("eglCreateSyncKHR");
1237 assert(display->egl.create_sync);
1238
1239 display->egl.destroy_sync =
1240 (void *) eglGetProcAddress("eglDestroySyncKHR");
1241 assert(display->egl.destroy_sync);
1242
1243 display->egl.client_wait_sync =
1244 (void *) eglGetProcAddress("eglClientWaitSyncKHR");
1245 assert(display->egl.client_wait_sync);
1246
1247 display->egl.dup_native_fence_fd =
1248 (void *) eglGetProcAddress("eglDupNativeFenceFDANDROID");
1249 assert(display->egl.dup_native_fence_fd);
1250 }
1251
1252 if (weston_check_egl_extension(egl_extensions,
1253 "EGL_KHR_wait_sync")) {
1254 display->egl.wait_sync =
1255 (void *) eglGetProcAddress("eglWaitSyncKHR");
1256 assert(display->egl.wait_sync);
1257 }
1258
1259 return true;
1260
1261 error:
1262 return false;
1263 }
1264
1265 static bool
display_update_supported_modifiers_for_egl(struct display * d)1266 display_update_supported_modifiers_for_egl(struct display *d)
1267 {
1268 uint64_t *egl_modifiers = NULL;
1269 int num_egl_modifiers = 0;
1270 EGLBoolean ret;
1271 int i;
1272 bool try_modifiers = d->egl.has_dma_buf_import_modifiers;
1273
1274 if (try_modifiers) {
1275 ret = d->egl.query_dma_buf_modifiers(d->egl.display,
1276 BUFFER_FORMAT,
1277 0, /* max_modifiers */
1278 NULL, /* modifiers */
1279 NULL, /* external_only */
1280 &num_egl_modifiers);
1281 if (ret == EGL_FALSE) {
1282 fprintf(stderr, "Failed to query num EGL modifiers for format\n");
1283 goto error;
1284 }
1285 }
1286
1287 if (!num_egl_modifiers)
1288 try_modifiers = false;
1289
1290 /* If EGL doesn't support modifiers, don't use them at all. */
1291 if (!try_modifiers) {
1292 d->modifiers_count = 0;
1293 free(d->modifiers);
1294 d->modifiers = NULL;
1295 return true;
1296 }
1297
1298 egl_modifiers = zalloc(num_egl_modifiers * sizeof(*egl_modifiers));
1299
1300 ret = d->egl.query_dma_buf_modifiers(d->egl.display,
1301 BUFFER_FORMAT,
1302 num_egl_modifiers,
1303 egl_modifiers,
1304 NULL, /* external_only */
1305 &num_egl_modifiers);
1306 if (ret == EGL_FALSE) {
1307 fprintf(stderr, "Failed to query EGL modifiers for format\n");
1308 goto error;
1309 }
1310
1311 /* Poor person's set intersection: d->modifiers INTERSECT
1312 * egl_modifiers. If a modifier is not supported, replace it with
1313 * DRM_FORMAT_MOD_INVALID in the d->modifiers array.
1314 */
1315 for (i = 0; i < d->modifiers_count; ++i) {
1316 uint64_t mod = d->modifiers[i];
1317 bool egl_supported = false;
1318 int j;
1319
1320 for (j = 0; j < num_egl_modifiers; ++j) {
1321 if (egl_modifiers[j] == mod) {
1322 egl_supported = true;
1323 break;
1324 }
1325 }
1326
1327 if (!egl_supported)
1328 d->modifiers[i] = DRM_FORMAT_MOD_INVALID;
1329 }
1330
1331 free(egl_modifiers);
1332
1333 return true;
1334
1335 error:
1336 free(egl_modifiers);
1337
1338 return false;
1339 }
1340
1341 static bool
display_set_up_gbm(struct display * display,char const * drm_render_node)1342 display_set_up_gbm(struct display *display, char const* drm_render_node)
1343 {
1344 display->gbm.drm_fd = open(drm_render_node, O_RDWR);
1345 if (display->gbm.drm_fd < 0) {
1346 fprintf(stderr, "Failed to open drm render node %s\n",
1347 drm_render_node);
1348 return false;
1349 }
1350
1351 display->gbm.device = gbm_create_device(display->gbm.drm_fd);
1352 if (display->gbm.device == NULL) {
1353 fprintf(stderr, "Failed to create gbm device\n");
1354 return false;
1355 }
1356
1357 return true;
1358 }
1359
1360 static struct display *
create_display(char const * drm_render_node,int opts)1361 create_display(char const *drm_render_node, int opts)
1362 {
1363 struct display *display = NULL;
1364
1365 display = zalloc(sizeof *display);
1366 if (display == NULL) {
1367 fprintf(stderr, "out of memory\n");
1368 goto error;
1369 }
1370
1371 display->gbm.drm_fd = -1;
1372
1373 display->display = wl_display_connect(NULL);
1374 assert(display->display);
1375
1376 display->req_dmabuf_immediate = opts & OPT_IMMEDIATE;
1377
1378 display->registry = wl_display_get_registry(display->display);
1379 wl_registry_add_listener(display->registry,
1380 ®istry_listener, display);
1381 wl_display_roundtrip(display->display);
1382 if (display->dmabuf == NULL) {
1383 fprintf(stderr, "No zwp_linux_dmabuf global\n");
1384 goto error;
1385 }
1386
1387 wl_display_roundtrip(display->display);
1388
1389 if (!display->modifiers_count) {
1390 fprintf(stderr, "format XRGB8888 is not available\n");
1391 goto error;
1392 }
1393
1394 /* GBM needs to be initialized before EGL, so that we have a valid
1395 * render node gbm_device to create the EGL display from. */
1396 if (!display_set_up_gbm(display, drm_render_node))
1397 goto error;
1398
1399 if (!display_set_up_egl(display))
1400 goto error;
1401
1402 if (!display_update_supported_modifiers_for_egl(display))
1403 goto error;
1404
1405 /* We use explicit synchronization only if the user hasn't disabled it,
1406 * the compositor supports it, we can handle fence fds. */
1407 display->use_explicit_sync =
1408 !(opts & OPT_IMPLICIT_SYNC) &&
1409 display->explicit_sync &&
1410 display->egl.dup_native_fence_fd;
1411
1412 if (opts & OPT_IMPLICIT_SYNC) {
1413 fprintf(stderr, "Warning: Not using explicit sync, disabled by user\n");
1414 } else if (!display->explicit_sync) {
1415 fprintf(stderr,
1416 "Warning: zwp_linux_explicit_synchronization_v1 not supported,\n"
1417 " will not use explicit synchronization\n");
1418 } else if (!display->egl.dup_native_fence_fd) {
1419 fprintf(stderr,
1420 "Warning: EGL_ANDROID_native_fence_sync not supported,\n"
1421 " will not use explicit synchronization\n");
1422 } else if (!display->egl.wait_sync) {
1423 fprintf(stderr,
1424 "Warning: EGL_KHR_wait_sync not supported,\n"
1425 " will not use server-side wait\n");
1426 }
1427
1428 return display;
1429
1430 error:
1431 if (display != NULL)
1432 destroy_display(display);
1433 return NULL;
1434 }
1435
1436 static void
signal_int(int signum)1437 signal_int(int signum)
1438 {
1439 running = 0;
1440 }
1441
1442 static void
print_usage_and_exit(void)1443 print_usage_and_exit(void)
1444 {
1445 printf("usage flags:\n"
1446 "\t'-i,--import-immediate=<>'"
1447 "\n\t\t0 to import dmabuf via roundtrip, "
1448 "\n\t\t1 to enable import without roundtrip\n"
1449 "\t'-d,--drm-render-node=<>'"
1450 "\n\t\tthe full path to the drm render node to use\n"
1451 "\t'-s,--size=<>'"
1452 "\n\t\tthe window size in pixels (default: 256)\n"
1453 "\t'-e,--explicit-sync=<>'"
1454 "\n\t\t0 to disable explicit sync, "
1455 "\n\t\t1 to enable explicit sync (default: 1)\n"
1456 "\t'-m,--mandelbrot'"
1457 "\n\t\trender a mandelbrot set with multiple draw calls\n"
1458 "\t'-g,--direct-display'"
1459 "\n\t\tenables weston-direct-display extension to attempt "
1460 "direct scan-out;\n\t\tnote this will cause the image to be "
1461 "displayed inverted as GL uses a\n\t\tdifferent texture "
1462 "coordinate system\n");
1463 exit(0);
1464 }
1465
1466 static int
is_true(const char * c)1467 is_true(const char* c)
1468 {
1469 if (!strcmp(c, "1"))
1470 return 1;
1471 else if (!strcmp(c, "0"))
1472 return 0;
1473 else
1474 print_usage_and_exit();
1475
1476 return 0;
1477 }
1478
1479 int
main(int argc,char ** argv)1480 main(int argc, char **argv)
1481 {
1482 struct sigaction sigint;
1483 struct display *display;
1484 struct window *window;
1485 int opts = 0;
1486 char const *drm_render_node = "/dev/dri/renderD128";
1487 int c, option_index, ret = 0;
1488 int window_size = 256;
1489
1490 static struct option long_options[] = {
1491 {"import-immediate", required_argument, 0, 'i' },
1492 {"drm-render-node", required_argument, 0, 'd' },
1493 {"size", required_argument, 0, 's' },
1494 {"explicit-sync", required_argument, 0, 'e' },
1495 {"mandelbrot", no_argument, 0, 'm' },
1496 {"direct-display", no_argument, 0, 'g' },
1497 {"help", no_argument , 0, 'h' },
1498 {0, 0, 0, 0}
1499 };
1500
1501 while ((c = getopt_long(argc, argv, "hi:d:s:e:mg",
1502 long_options, &option_index)) != -1) {
1503 switch (c) {
1504 case 'i':
1505 if (is_true(optarg))
1506 opts |= OPT_IMMEDIATE;
1507 break;
1508 case 'd':
1509 drm_render_node = optarg;
1510 break;
1511 case 's':
1512 window_size = strtol(optarg, NULL, 10);
1513 break;
1514 case 'e':
1515 if (!is_true(optarg))
1516 opts |= OPT_IMPLICIT_SYNC;
1517 break;
1518 case 'm':
1519 opts |= OPT_MANDELBROT;
1520 break;
1521 case 'g':
1522 opts |= OPT_DIRECT_DISPLAY;
1523 break;
1524 default:
1525 print_usage_and_exit();
1526 }
1527 }
1528
1529 display = create_display(drm_render_node, opts);
1530 if (!display)
1531 return 1;
1532 window = create_window(display, window_size, window_size, opts);
1533 if (!window)
1534 return 1;
1535
1536 sigint.sa_handler = signal_int;
1537 sigemptyset(&sigint.sa_mask);
1538 sigint.sa_flags = SA_RESETHAND;
1539 sigaction(SIGINT, &sigint, NULL);
1540
1541 /* Here we retrieve the linux-dmabuf objects if executed without immed,
1542 * or error */
1543 wl_display_roundtrip(display->display);
1544
1545 if (!running)
1546 return 1;
1547
1548 window->initialized = true;
1549
1550 if (!window->wait_for_configure)
1551 redraw(window, NULL, 0);
1552
1553 while (running && ret != -1)
1554 ret = wl_display_dispatch(display->display);
1555
1556 fprintf(stderr, "simple-dmabuf-egl exiting\n");
1557 destroy_window(window);
1558 destroy_display(display);
1559
1560 return 0;
1561 }
1562