• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &params_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 				 &registry_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