git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

scenefx/render/egl.c (31.6K)

   1 #include <assert.h>
   2 #include <drm_fourcc.h>
   3 #include <fcntl.h>
   4 #include <stdio.h>
   5 #include <stdlib.h>
   6 #include <unistd.h>
   7 #include <gbm.h>
   8 #include <wlr/render/egl.h>
   9 #include <wlr/util/log.h>
  10 #include <wlr/util/region.h>
  11 #include <xf86drm.h>
  12 #include "render/egl.h"
  13 #include "util/env.h"
  14 
  15 static enum wlr_log_importance egl_log_importance_to_wlr(EGLint type) {
  16 	switch (type) {
  17 	case EGL_DEBUG_MSG_CRITICAL_KHR: return WLR_ERROR;
  18 	case EGL_DEBUG_MSG_ERROR_KHR:    return WLR_ERROR;
  19 	case EGL_DEBUG_MSG_WARN_KHR:     return WLR_ERROR;
  20 	case EGL_DEBUG_MSG_INFO_KHR:     return WLR_INFO;
  21 	default:                         return WLR_INFO;
  22 	}
  23 }
  24 
  25 static const char *egl_error_str(EGLint error) {
  26 	switch (error) {
  27 	case EGL_SUCCESS:
  28 		return "EGL_SUCCESS";
  29 	case EGL_NOT_INITIALIZED:
  30 		return "EGL_NOT_INITIALIZED";
  31 	case EGL_BAD_ACCESS:
  32 		return "EGL_BAD_ACCESS";
  33 	case EGL_BAD_ALLOC:
  34 		return "EGL_BAD_ALLOC";
  35 	case EGL_BAD_ATTRIBUTE:
  36 		return "EGL_BAD_ATTRIBUTE";
  37 	case EGL_BAD_CONTEXT:
  38 		return "EGL_BAD_CONTEXT";
  39 	case EGL_BAD_CONFIG:
  40 		return "EGL_BAD_CONFIG";
  41 	case EGL_BAD_CURRENT_SURFACE:
  42 		return "EGL_BAD_CURRENT_SURFACE";
  43 	case EGL_BAD_DISPLAY:
  44 		return "EGL_BAD_DISPLAY";
  45 	case EGL_BAD_DEVICE_EXT:
  46 		return "EGL_BAD_DEVICE_EXT";
  47 	case EGL_BAD_SURFACE:
  48 		return "EGL_BAD_SURFACE";
  49 	case EGL_BAD_MATCH:
  50 		return "EGL_BAD_MATCH";
  51 	case EGL_BAD_PARAMETER:
  52 		return "EGL_BAD_PARAMETER";
  53 	case EGL_BAD_NATIVE_PIXMAP:
  54 		return "EGL_BAD_NATIVE_PIXMAP";
  55 	case EGL_BAD_NATIVE_WINDOW:
  56 		return "EGL_BAD_NATIVE_WINDOW";
  57 	case EGL_CONTEXT_LOST:
  58 		return "EGL_CONTEXT_LOST";
  59 	}
  60 	return "unknown error";
  61 }
  62 
  63 static void egl_log(EGLenum error, const char *command, EGLint msg_type,
  64 		EGLLabelKHR thread, EGLLabelKHR obj, const char *msg) {
  65 	_wlr_log(egl_log_importance_to_wlr(msg_type),
  66 		"[EGL] command: %s, error: %s (0x%x), message: \"%s\"",
  67 		command, egl_error_str(error), error, msg);
  68 }
  69 
  70 static bool check_egl_ext(const char *exts, const char *ext) {
  71 	size_t extlen = strlen(ext);
  72 	const char *end = exts + strlen(exts);
  73 
  74 	while (exts < end) {
  75 		if (*exts == ' ') {
  76 			exts++;
  77 			continue;
  78 		}
  79 		size_t n = strcspn(exts, " ");
  80 		if (n == extlen && strncmp(ext, exts, n) == 0) {
  81 			return true;
  82 		}
  83 		exts += n;
  84 	}
  85 	return false;
  86 }
  87 
  88 static void load_egl_proc(void *proc_ptr, const char *name) {
  89 	void *proc = (void *)eglGetProcAddress(name);
  90 	if (proc == NULL) {
  91 		wlr_log(WLR_ERROR, "eglGetProcAddress(%s) failed", name);
  92 		abort();
  93 	}
  94 	*(void **)proc_ptr = proc;
  95 }
  96 
  97 static int get_egl_dmabuf_formats(struct wlr_egl *egl, EGLint **formats);
  98 static int get_egl_dmabuf_modifiers(struct wlr_egl *egl, EGLint format,
  99 	uint64_t **modifiers, EGLBoolean **external_only);
 100 
 101 static void log_modifier(uint64_t modifier, bool external_only) {
 102 	char *mod_name = drmGetFormatModifierName(modifier);
 103 	wlr_log(WLR_DEBUG, "    %s (0x%016"PRIX64"): ✓ texture  %s render",
 104 		mod_name ? mod_name : "<unknown>", modifier, external_only ? "✗" : "✓");
 105 	free(mod_name);
 106 }
 107 
 108 static void init_dmabuf_formats(struct wlr_egl *egl) {
 109 	bool no_modifiers = env_parse_bool("WLR_EGL_NO_MODIFIERS");
 110 	if (no_modifiers) {
 111 		wlr_log(WLR_INFO, "WLR_EGL_NO_MODIFIERS set, disabling modifiers for EGL");
 112 	}
 113 
 114 	EGLint *formats;
 115 	int formats_len = get_egl_dmabuf_formats(egl, &formats);
 116 	if (formats_len < 0) {
 117 		return;
 118 	}
 119 
 120 	wlr_log(WLR_DEBUG, "Supported DMA-BUF formats:");
 121 
 122 	bool has_modifiers = false;
 123 	for (int i = 0; i < formats_len; i++) {
 124 		EGLint fmt = formats[i];
 125 
 126 		uint64_t *modifiers = NULL;
 127 		EGLBoolean *external_only = NULL;
 128 		int modifiers_len = 0;
 129 		if (!no_modifiers) {
 130 			modifiers_len = get_egl_dmabuf_modifiers(egl, fmt, &modifiers, &external_only);
 131 		}
 132 		if (modifiers_len < 0) {
 133 			continue;
 134 		}
 135 
 136 		has_modifiers = has_modifiers || modifiers_len > 0;
 137 
 138 		bool all_external_only = true;
 139 		for (int j = 0; j < modifiers_len; j++) {
 140 			wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt,
 141 				modifiers[j]);
 142 			if (!external_only[j]) {
 143 				wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt,
 144 					modifiers[j]);
 145 				all_external_only = false;
 146 			}
 147 		}
 148 
 149 		// EGL always supports implicit modifiers. If at least one modifier supports rendering,
 150 		// assume the implicit modifier supports rendering too.
 151 		wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt,
 152 			DRM_FORMAT_MOD_INVALID);
 153 		if (modifiers_len == 0 || !all_external_only) {
 154 			wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt,
 155 				DRM_FORMAT_MOD_INVALID);
 156 		}
 157 
 158 		if (modifiers_len == 0) {
 159 			// Assume the linear layout is supported if the driver doesn't
 160 			// explicitly say otherwise
 161 			wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt,
 162 				DRM_FORMAT_MOD_LINEAR);
 163 			wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt,
 164 				DRM_FORMAT_MOD_LINEAR);
 165 		}
 166 
 167 		if (wlr_log_get_verbosity() >= WLR_DEBUG) {
 168 			char *fmt_name = drmGetFormatName(fmt);
 169 			wlr_log(WLR_DEBUG, "  %s (0x%08"PRIX32")",
 170 				fmt_name ? fmt_name : "<unknown>", fmt);
 171 			free(fmt_name);
 172 
 173 			log_modifier(DRM_FORMAT_MOD_INVALID, false);
 174 			if (modifiers_len == 0) {
 175 				log_modifier(DRM_FORMAT_MOD_LINEAR, false);
 176 			}
 177 			for (int j = 0; j < modifiers_len; j++) {
 178 				log_modifier(modifiers[j], external_only[j]);
 179 			}
 180 		}
 181 
 182 		free(modifiers);
 183 		free(external_only);
 184 	}
 185 	free(formats);
 186 
 187 	egl->has_modifiers = has_modifiers;
 188 	if (!no_modifiers) {
 189 		wlr_log(WLR_DEBUG, "EGL DMA-BUF format modifiers %s",
 190 			has_modifiers ? "supported" : "unsupported");
 191 	}
 192 }
 193 
 194 static struct wlr_egl *egl_create(void) {
 195 	const char *client_exts_str = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
 196 	if (client_exts_str == NULL) {
 197 		if (eglGetError() == EGL_BAD_DISPLAY) {
 198 			wlr_log(WLR_ERROR, "EGL_EXT_client_extensions not supported");
 199 		} else {
 200 			wlr_log(WLR_ERROR, "Failed to query EGL client extensions");
 201 		}
 202 		return NULL;
 203 	}
 204 
 205 	wlr_log(WLR_INFO, "Supported EGL client extensions: %s", client_exts_str);
 206 
 207 	if (!check_egl_ext(client_exts_str, "EGL_EXT_platform_base")) {
 208 		wlr_log(WLR_ERROR, "EGL_EXT_platform_base not supported");
 209 		return NULL;
 210 	}
 211 
 212 	struct wlr_egl *egl = calloc(1, sizeof(*egl));
 213 	if (egl == NULL) {
 214 		wlr_log_errno(WLR_ERROR, "Allocation failed");
 215 		return NULL;
 216 	}
 217 
 218 	load_egl_proc(&egl->procs.eglGetPlatformDisplayEXT,
 219 		"eglGetPlatformDisplayEXT");
 220 
 221 	egl->exts.KHR_platform_gbm = check_egl_ext(client_exts_str,
 222 			"EGL_KHR_platform_gbm");
 223 	egl->exts.EXT_platform_device = check_egl_ext(client_exts_str,
 224 			"EGL_EXT_platform_device");
 225 	egl->exts.KHR_display_reference = check_egl_ext(client_exts_str,
 226 			"EGL_KHR_display_reference");
 227 
 228 	if (check_egl_ext(client_exts_str, "EGL_EXT_device_base") || check_egl_ext(client_exts_str, "EGL_EXT_device_enumeration")) {
 229 		load_egl_proc(&egl->procs.eglQueryDevicesEXT, "eglQueryDevicesEXT");
 230 	}
 231 
 232 	if (check_egl_ext(client_exts_str, "EGL_EXT_device_base") || check_egl_ext(client_exts_str, "EGL_EXT_device_query")) {
 233 		egl->exts.EXT_device_query = true;
 234 		load_egl_proc(&egl->procs.eglQueryDeviceStringEXT,
 235 			"eglQueryDeviceStringEXT");
 236 		load_egl_proc(&egl->procs.eglQueryDisplayAttribEXT,
 237 			"eglQueryDisplayAttribEXT");
 238 	}
 239 
 240 	if (check_egl_ext(client_exts_str, "EGL_KHR_debug")) {
 241 		load_egl_proc(&egl->procs.eglDebugMessageControlKHR,
 242 			"eglDebugMessageControlKHR");
 243 
 244 		static const EGLAttrib debug_attribs[] = {
 245 			EGL_DEBUG_MSG_CRITICAL_KHR, EGL_TRUE,
 246 			EGL_DEBUG_MSG_ERROR_KHR, EGL_TRUE,
 247 			EGL_DEBUG_MSG_WARN_KHR, EGL_TRUE,
 248 			EGL_DEBUG_MSG_INFO_KHR, EGL_TRUE,
 249 			EGL_NONE,
 250 		};
 251 		egl->procs.eglDebugMessageControlKHR(egl_log, debug_attribs);
 252 	}
 253 
 254 	if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) {
 255 		wlr_log(WLR_ERROR, "Failed to bind to the OpenGL ES API");
 256 		free(egl);
 257 		return NULL;
 258 	}
 259 
 260 	return egl;
 261 }
 262 
 263 static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display,
 264 		bool allow_software) {
 265 	egl->display = display;
 266 
 267 	EGLint major, minor;
 268 	if (eglInitialize(egl->display, &major, &minor) == EGL_FALSE) {
 269 		wlr_log(WLR_ERROR, "Failed to initialize EGL");
 270 		return false;
 271 	}
 272 
 273 	const char *display_exts_str = eglQueryString(egl->display, EGL_EXTENSIONS);
 274 	if (display_exts_str == NULL) {
 275 		wlr_log(WLR_ERROR, "Failed to query EGL display extensions");
 276 		return false;
 277 	}
 278 
 279 	if (check_egl_ext(display_exts_str, "EGL_KHR_image_base")) {
 280 		egl->exts.KHR_image_base = true;
 281 		load_egl_proc(&egl->procs.eglCreateImageKHR, "eglCreateImageKHR");
 282 		load_egl_proc(&egl->procs.eglDestroyImageKHR, "eglDestroyImageKHR");
 283 	}
 284 
 285 	egl->exts.EXT_image_dma_buf_import =
 286 		check_egl_ext(display_exts_str, "EGL_EXT_image_dma_buf_import");
 287 	if (check_egl_ext(display_exts_str,
 288 			"EGL_EXT_image_dma_buf_import_modifiers")) {
 289 		egl->exts.EXT_image_dma_buf_import_modifiers = true;
 290 		load_egl_proc(&egl->procs.eglQueryDmaBufFormatsEXT,
 291 			"eglQueryDmaBufFormatsEXT");
 292 		load_egl_proc(&egl->procs.eglQueryDmaBufModifiersEXT,
 293 			"eglQueryDmaBufModifiersEXT");
 294 	}
 295 
 296 	egl->exts.EXT_create_context_robustness =
 297 		check_egl_ext(display_exts_str, "EGL_EXT_create_context_robustness");
 298 
 299 	const char *device_exts_str = NULL, *driver_name = NULL;
 300 	if (egl->exts.EXT_device_query) {
 301 		EGLAttrib device_attrib;
 302 		if (!egl->procs.eglQueryDisplayAttribEXT(egl->display,
 303 				EGL_DEVICE_EXT, &device_attrib)) {
 304 			wlr_log(WLR_ERROR, "eglQueryDisplayAttribEXT(EGL_DEVICE_EXT) failed");
 305 			return false;
 306 		}
 307 		egl->device = (EGLDeviceEXT)device_attrib;
 308 
 309 		device_exts_str =
 310 			egl->procs.eglQueryDeviceStringEXT(egl->device, EGL_EXTENSIONS);
 311 		if (device_exts_str == NULL) {
 312 			wlr_log(WLR_ERROR, "eglQueryDeviceStringEXT(EGL_EXTENSIONS) failed");
 313 			return false;
 314 		}
 315 
 316 #ifdef EGL_DRIVER_NAME_EXT
 317 		if (check_egl_ext(device_exts_str, "EGL_EXT_device_persistent_id")) {
 318 			driver_name = egl->procs.eglQueryDeviceStringEXT(egl->device,
 319 				EGL_DRIVER_NAME_EXT);
 320 		}
 321 #endif
 322 
 323 		egl->exts.EXT_device_drm =
 324 			check_egl_ext(device_exts_str, "EGL_EXT_device_drm");
 325 		egl->exts.EXT_device_drm_render_node =
 326 			check_egl_ext(device_exts_str, "EGL_EXT_device_drm_render_node");
 327 
 328 		// The only way a non-DRM device is selected is when the user
 329 		// explicitly picks software rendering
 330 		if (check_egl_ext(device_exts_str, "EGL_MESA_device_software")) {
 331 			if (allow_software || env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) {
 332 				wlr_log(WLR_INFO, "Using software rendering");
 333 			} else {
 334 				wlr_log(WLR_ERROR, "Software rendering detected, please use "
 335 					"the WLR_RENDERER_ALLOW_SOFTWARE environment variable "
 336 					"to proceed");
 337 				return false;
 338 			}
 339 		}
 340 	}
 341 
 342 	if (!check_egl_ext(display_exts_str, "EGL_KHR_no_config_context") &&
 343 			!check_egl_ext(display_exts_str, "EGL_MESA_configless_context")) {
 344 		wlr_log(WLR_ERROR, "EGL_KHR_no_config_context or "
 345 			"EGL_MESA_configless_context not supported");
 346 		return false;
 347 	}
 348 
 349 	if (!check_egl_ext(display_exts_str, "EGL_KHR_surfaceless_context")) {
 350 		wlr_log(WLR_ERROR, "EGL_KHR_surfaceless_context not supported");
 351 		return false;
 352 	}
 353 
 354 	if (check_egl_ext(display_exts_str, "EGL_KHR_fence_sync") &&
 355 			check_egl_ext(display_exts_str, "EGL_ANDROID_native_fence_sync")) {
 356 		load_egl_proc(&egl->procs.eglCreateSyncKHR, "eglCreateSyncKHR");
 357 		load_egl_proc(&egl->procs.eglDestroySyncKHR, "eglDestroySyncKHR");
 358 		load_egl_proc(&egl->procs.eglDupNativeFenceFDANDROID,
 359 			"eglDupNativeFenceFDANDROID");
 360 	}
 361 
 362 	if (check_egl_ext(display_exts_str, "EGL_KHR_wait_sync")) {
 363 		load_egl_proc(&egl->procs.eglWaitSyncKHR, "eglWaitSyncKHR");
 364 	}
 365 
 366 	egl->exts.IMG_context_priority =
 367 		check_egl_ext(display_exts_str, "EGL_IMG_context_priority");
 368 
 369 	wlr_log(WLR_INFO, "Using EGL %d.%d", (int)major, (int)minor);
 370 	wlr_log(WLR_INFO, "Supported EGL display extensions: %s", display_exts_str);
 371 	if (device_exts_str != NULL) {
 372 		wlr_log(WLR_INFO, "Supported EGL device extensions: %s", device_exts_str);
 373 	}
 374 	wlr_log(WLR_INFO, "EGL vendor: %s", eglQueryString(egl->display, EGL_VENDOR));
 375 	if (driver_name != NULL) {
 376 		wlr_log(WLR_INFO, "EGL driver name: %s", driver_name);
 377 	}
 378 
 379 	init_dmabuf_formats(egl);
 380 
 381 	return true;
 382 }
 383 
 384 static bool egl_init(struct wlr_egl *egl, EGLenum platform,
 385 		void *remote_display, bool allow_software) {
 386 	EGLint display_attribs[3] = {0};
 387 	size_t display_attribs_len = 0;
 388 
 389 	if (egl->exts.KHR_display_reference) {
 390 		display_attribs[display_attribs_len++] = EGL_TRACK_REFERENCES_KHR;
 391 		display_attribs[display_attribs_len++] = EGL_TRUE;
 392 	}
 393 
 394 	display_attribs[display_attribs_len++] = EGL_NONE;
 395 	assert(display_attribs_len <= sizeof(display_attribs) / sizeof(display_attribs[0]));
 396 
 397 	EGLDisplay display = egl->procs.eglGetPlatformDisplayEXT(platform,
 398 		remote_display, display_attribs);
 399 	if (display == EGL_NO_DISPLAY) {
 400 		wlr_log(WLR_ERROR, "Failed to create EGL display");
 401 		return false;
 402 	}
 403 
 404 	if (!egl_init_display(egl, display, allow_software)) {
 405 		if (egl->exts.KHR_display_reference) {
 406 			eglTerminate(display);
 407 		}
 408 		return false;
 409 	}
 410 
 411 	size_t atti = 0;
 412 	EGLint attribs[7];
 413 
 414 	attribs[atti++] = EGL_CONTEXT_CLIENT_VERSION;
 415 	attribs[atti++] = 2;
 416 
 417 	// Request a high priority context if possible
 418 	// TODO: only do this if we're running as the DRM master
 419 	bool request_high_priority = egl->exts.IMG_context_priority;
 420 
 421 	// Try to reschedule all of our rendering to be completed first. If it
 422 	// fails, it will fallback to the default priority (MEDIUM).
 423 	if (request_high_priority) {
 424 		attribs[atti++] = EGL_CONTEXT_PRIORITY_LEVEL_IMG;
 425 		attribs[atti++] = EGL_CONTEXT_PRIORITY_HIGH_IMG;
 426 	}
 427 
 428 	if (egl->exts.EXT_create_context_robustness) {
 429 		attribs[atti++] = EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT;
 430 		attribs[atti++] = EGL_LOSE_CONTEXT_ON_RESET_EXT;
 431 	}
 432 
 433 	attribs[atti++] = EGL_NONE;
 434 	assert(atti <= sizeof(attribs)/sizeof(attribs[0]));
 435 
 436 	egl->context = eglCreateContext(egl->display, EGL_NO_CONFIG_KHR,
 437 		EGL_NO_CONTEXT, attribs);
 438 	if (egl->context != EGL_NO_CONTEXT) {
 439 		wlr_log(WLR_DEBUG, "Created EGL context using OpenGL ES 2.0");
 440 	} else {
 441 		wlr_log(WLR_ERROR, "Failed to create EGL context using OpenGL ES 2.0");
 442 		return false;
 443 	}
 444 
 445 	if (request_high_priority) {
 446 		EGLint priority = EGL_CONTEXT_PRIORITY_MEDIUM_IMG;
 447 		eglQueryContext(egl->display, egl->context,
 448 			EGL_CONTEXT_PRIORITY_LEVEL_IMG, &priority);
 449 		if (priority != EGL_CONTEXT_PRIORITY_HIGH_IMG) {
 450 			wlr_log(WLR_INFO, "Failed to obtain a high priority context");
 451 		} else {
 452 			wlr_log(WLR_DEBUG, "Obtained high priority context");
 453 		}
 454 	}
 455 
 456 	return true;
 457 }
 458 
 459 static bool device_has_name(const drmDevice *device, const char *name);
 460 
 461 static EGLDeviceEXT get_egl_device_from_drm_fd(struct wlr_egl *egl,
 462 		int drm_fd) {
 463 	if (egl->procs.eglQueryDevicesEXT == NULL) {
 464 		wlr_log(WLR_DEBUG, "EGL_EXT_device_enumeration not supported");
 465 		return EGL_NO_DEVICE_EXT;
 466 	} else if (!egl->exts.EXT_device_query) {
 467 		wlr_log(WLR_DEBUG, "EGL_EXT_device_query not supported");
 468 		return EGL_NO_DEVICE_EXT;
 469 	}
 470 
 471 	EGLint nb_devices = 0;
 472 	if (!egl->procs.eglQueryDevicesEXT(0, NULL, &nb_devices)) {
 473 		wlr_log(WLR_ERROR, "Failed to query EGL devices");
 474 		return EGL_NO_DEVICE_EXT;
 475 	}
 476 
 477 	EGLDeviceEXT *devices = calloc(nb_devices, sizeof(*devices));
 478 	if (devices == NULL) {
 479 		wlr_log_errno(WLR_ERROR, "Failed to allocate EGL device list");
 480 		return EGL_NO_DEVICE_EXT;
 481 	}
 482 
 483 	if (!egl->procs.eglQueryDevicesEXT(nb_devices, devices, &nb_devices)) {
 484 		wlr_log(WLR_ERROR, "Failed to query EGL devices");
 485 		free(devices);
 486 		return EGL_NO_DEVICE_EXT;
 487 	}
 488 
 489 	drmDevice *selected_drm_device = NULL;
 490 	if (drm_fd >= 0) {
 491 		int ret = drmGetDevice(drm_fd, &selected_drm_device);
 492 		if (ret < 0) {
 493 			wlr_log(WLR_ERROR, "Failed to get DRM device: %s", strerror(-ret));
 494 			free(devices);
 495 			return EGL_NO_DEVICE_EXT;
 496 		}
 497 	}
 498 
 499 	EGLDeviceEXT egl_device = NULL;
 500 	for (int i = 0; i < nb_devices; i++) {
 501 		const char *device_exts_str = egl->procs.eglQueryDeviceStringEXT(devices[i], EGL_EXTENSIONS);
 502 		if (device_exts_str == NULL) {
 503 			wlr_log(WLR_ERROR, "eglQueryDeviceStringEXT(EGL_EXTENSIONS) failed");
 504 			continue;
 505 		}
 506 
 507 		const char *egl_device_name = NULL;
 508 		if (check_egl_ext(device_exts_str, "EGL_EXT_device_drm")) {
 509 			egl_device_name = egl->procs.eglQueryDeviceStringEXT(devices[i], EGL_DRM_DEVICE_FILE_EXT);
 510 			if (egl_device_name == NULL) {
 511 				wlr_log(WLR_ERROR, "eglQueryDeviceStringEXT(EGL_DRM_DEVICE_FILE_EXT) failed");
 512 				continue;
 513 			}
 514 		}
 515 
 516 		bool is_software = check_egl_ext(device_exts_str, "EGL_MESA_device_software");
 517 
 518 		bool found;
 519 		if (selected_drm_device != NULL) {
 520 			found = egl_device_name != NULL && device_has_name(selected_drm_device, egl_device_name);
 521 		} else {
 522 			found = is_software;
 523 		}
 524 		if (found) {
 525 			if (egl_device_name != NULL) {
 526 				wlr_log(WLR_DEBUG, "Using EGL device %s", egl_device_name);
 527 			}
 528 			egl_device = devices[i];
 529 			break;
 530 		}
 531 	}
 532 
 533 	drmFreeDevice(&selected_drm_device);
 534 	free(devices);
 535 
 536 	return egl_device;
 537 }
 538 
 539 static int open_render_node(int drm_fd) {
 540 	char *render_name = drmGetRenderDeviceNameFromFd(drm_fd);
 541 	if (render_name == NULL) {
 542 		// This can happen on split render/display platforms, fallback to
 543 		// primary node
 544 		render_name = drmGetPrimaryDeviceNameFromFd(drm_fd);
 545 		if (render_name == NULL) {
 546 			wlr_log_errno(WLR_ERROR, "drmGetPrimaryDeviceNameFromFd failed");
 547 			return -1;
 548 		}
 549 		wlr_log(WLR_DEBUG, "DRM device '%s' has no render node, "
 550 			"falling back to primary node", render_name);
 551 	}
 552 
 553 	int render_fd = open(render_name, O_RDWR | O_CLOEXEC);
 554 	if (render_fd < 0) {
 555 		wlr_log_errno(WLR_ERROR, "Failed to open DRM node '%s'", render_name);
 556 	}
 557 	free(render_name);
 558 	return render_fd;
 559 }
 560 
 561 struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) {
 562 	bool allow_software = drm_fd < 0;
 563 
 564 	struct wlr_egl *egl = egl_create();
 565 	if (egl == NULL) {
 566 		wlr_log(WLR_ERROR, "Failed to create EGL context");
 567 		return NULL;
 568 	}
 569 
 570 	if (egl->exts.EXT_platform_device) {
 571 		/*
 572 		 * Search for the EGL device matching the DRM fd using the
 573 		 * EXT_device_enumeration extension.
 574 		 */
 575 		EGLDeviceEXT egl_device = get_egl_device_from_drm_fd(egl, drm_fd);
 576 		if (egl_device != EGL_NO_DEVICE_EXT) {
 577 			if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device, allow_software)) {
 578 				wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_DEVICE_EXT");
 579 				return egl;
 580 			}
 581 			goto error;
 582 		}
 583 		/* Falls back on GBM in case the device was not found */
 584 	} else {
 585 		wlr_log(WLR_DEBUG, "EXT_platform_device not supported");
 586 	}
 587 
 588 	if (egl->exts.KHR_platform_gbm && drm_fd >= 0) {
 589 		int gbm_fd = open_render_node(drm_fd);
 590 		if (gbm_fd < 0) {
 591 			wlr_log(WLR_ERROR, "Failed to open DRM render node");
 592 			goto error;
 593 		}
 594 
 595 		egl->gbm_device = gbm_create_device(gbm_fd);
 596 		if (!egl->gbm_device) {
 597 			close(gbm_fd);
 598 			wlr_log(WLR_ERROR, "Failed to create GBM device");
 599 			goto error;
 600 		}
 601 
 602 		if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device, allow_software)) {
 603 			wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_GBM_KHR");
 604 			return egl;
 605 		}
 606 
 607 		gbm_device_destroy(egl->gbm_device);
 608 		close(gbm_fd);
 609 	} else {
 610 		wlr_log(WLR_DEBUG, "KHR_platform_gbm not supported");
 611 	}
 612 
 613 error:
 614 	wlr_log(WLR_ERROR, "Failed to initialize EGL context");
 615 	free(egl);
 616 	eglReleaseThread();
 617 	return NULL;
 618 }
 619 
 620 struct wlr_egl *wlr_egl_create_with_context(EGLDisplay display,
 621 		EGLContext context) {
 622 	EGLint client_type;
 623 	if (!eglQueryContext(display, context, EGL_CONTEXT_CLIENT_TYPE, &client_type) ||
 624 			client_type != EGL_OPENGL_ES_API) {
 625 		wlr_log(WLR_ERROR, "Unsupported EGL context client type (need OpenGL ES)");
 626 		return NULL;
 627 	}
 628 
 629 	EGLint client_version;
 630 	if (!eglQueryContext(display, context, EGL_CONTEXT_CLIENT_VERSION, &client_version) ||
 631 			client_version < 2) {
 632 		wlr_log(WLR_ERROR, "Unsupported EGL context client version (need OpenGL ES >= 2)");
 633 		return NULL;
 634 	}
 635 
 636 	struct wlr_egl *egl = egl_create();
 637 	if (egl == NULL) {
 638 		return NULL;
 639 	}
 640 
 641 	if (!egl_init_display(egl, display, true)) {
 642 		free(egl);
 643 		return NULL;
 644 	}
 645 
 646 	egl->context = context;
 647 
 648 	return egl;
 649 }
 650 
 651 void wlr_egl_destroy(struct wlr_egl *egl) {
 652 	if (egl == NULL) {
 653 		return;
 654 	}
 655 
 656 	wlr_drm_format_set_finish(&egl->dmabuf_render_formats);
 657 	wlr_drm_format_set_finish(&egl->dmabuf_texture_formats);
 658 
 659 	eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
 660 	eglDestroyContext(egl->display, egl->context);
 661 
 662 	if (egl->exts.KHR_display_reference) {
 663 		eglTerminate(egl->display);
 664 	}
 665 
 666 	eglReleaseThread();
 667 
 668 	if (egl->gbm_device) {
 669 		int gbm_fd = gbm_device_get_fd(egl->gbm_device);
 670 		gbm_device_destroy(egl->gbm_device);
 671 		close(gbm_fd);
 672 	}
 673 
 674 	free(egl);
 675 }
 676 
 677 EGLDisplay wlr_egl_get_display(struct wlr_egl *egl) {
 678 	return egl->display;
 679 }
 680 
 681 EGLContext wlr_egl_get_context(struct wlr_egl *egl) {
 682 	return egl->context;
 683 }
 684 
 685 bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImage image) {
 686 	if (!egl->exts.KHR_image_base) {
 687 		return false;
 688 	}
 689 	if (!image) {
 690 		return true;
 691 	}
 692 	return egl->procs.eglDestroyImageKHR(egl->display, image);
 693 }
 694 
 695 bool wlr_egl_make_current(struct wlr_egl *egl,
 696 		struct wlr_egl_context *save_context) {
 697 	if (save_context != NULL) {
 698 		save_context->display = eglGetCurrentDisplay();
 699 		save_context->context = eglGetCurrentContext();
 700 		save_context->draw_surface = eglGetCurrentSurface(EGL_DRAW);
 701 		save_context->read_surface = eglGetCurrentSurface(EGL_READ);
 702 	}
 703 	if (!eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
 704 			egl->context)) {
 705 		wlr_log(WLR_ERROR, "eglMakeCurrent failed");
 706 		return false;
 707 	}
 708 	return true;
 709 }
 710 
 711 bool wlr_egl_unset_current(struct wlr_egl *egl) {
 712 	if (!eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
 713 			EGL_NO_CONTEXT)) {
 714 		wlr_log(WLR_ERROR, "eglMakeCurrent failed");
 715 		return false;
 716 	}
 717 	return true;
 718 }
 719 
 720 bool wlr_egl_restore_context(struct wlr_egl_context *context) {
 721 	// If the saved context is a null-context, we must use the current
 722 	// display instead of the saved display because eglMakeCurrent() can't
 723 	// handle EGL_NO_DISPLAY.
 724 	EGLDisplay display = context->display == EGL_NO_DISPLAY ?
 725 		eglGetCurrentDisplay() : context->display;
 726 
 727 	// If the current display is also EGL_NO_DISPLAY, we assume that there
 728 	// is currently no context set and no action needs to be taken to unset
 729 	// the context.
 730 	if (display == EGL_NO_DISPLAY) {
 731 		return true;
 732 	}
 733 
 734 	return eglMakeCurrent(display, context->draw_surface,
 735 			context->read_surface, context->context);
 736 }
 737 
 738 EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl,
 739 		struct wlr_dmabuf_attributes *attributes, bool *external_only) {
 740 	if (!egl->exts.KHR_image_base || !egl->exts.EXT_image_dma_buf_import) {
 741 		wlr_log(WLR_ERROR, "dmabuf import extension not present");
 742 		return NULL;
 743 	}
 744 
 745 	if (attributes->modifier != DRM_FORMAT_MOD_INVALID &&
 746 			attributes->modifier != DRM_FORMAT_MOD_LINEAR &&
 747 			!egl->has_modifiers) {
 748 		wlr_log(WLR_ERROR, "EGL implementation doesn't support modifiers");
 749 		return NULL;
 750 	}
 751 
 752 	unsigned int atti = 0;
 753 	EGLint attribs[50];
 754 	attribs[atti++] = EGL_WIDTH;
 755 	attribs[atti++] = attributes->width;
 756 	attribs[atti++] = EGL_HEIGHT;
 757 	attribs[atti++] = attributes->height;
 758 	attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
 759 	attribs[atti++] = attributes->format;
 760 
 761 	struct {
 762 		EGLint fd;
 763 		EGLint offset;
 764 		EGLint pitch;
 765 		EGLint mod_lo;
 766 		EGLint mod_hi;
 767 	} attr_names[WLR_DMABUF_MAX_PLANES] = {
 768 		{
 769 			EGL_DMA_BUF_PLANE0_FD_EXT,
 770 			EGL_DMA_BUF_PLANE0_OFFSET_EXT,
 771 			EGL_DMA_BUF_PLANE0_PITCH_EXT,
 772 			EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT,
 773 			EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT
 774 		}, {
 775 			EGL_DMA_BUF_PLANE1_FD_EXT,
 776 			EGL_DMA_BUF_PLANE1_OFFSET_EXT,
 777 			EGL_DMA_BUF_PLANE1_PITCH_EXT,
 778 			EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT,
 779 			EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT
 780 		}, {
 781 			EGL_DMA_BUF_PLANE2_FD_EXT,
 782 			EGL_DMA_BUF_PLANE2_OFFSET_EXT,
 783 			EGL_DMA_BUF_PLANE2_PITCH_EXT,
 784 			EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT,
 785 			EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT
 786 		}, {
 787 			EGL_DMA_BUF_PLANE3_FD_EXT,
 788 			EGL_DMA_BUF_PLANE3_OFFSET_EXT,
 789 			EGL_DMA_BUF_PLANE3_PITCH_EXT,
 790 			EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT,
 791 			EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT
 792 		}
 793 	};
 794 
 795 	for (int i = 0; i < attributes->n_planes; i++) {
 796 		attribs[atti++] = attr_names[i].fd;
 797 		attribs[atti++] = attributes->fd[i];
 798 		attribs[atti++] = attr_names[i].offset;
 799 		attribs[atti++] = attributes->offset[i];
 800 		attribs[atti++] = attr_names[i].pitch;
 801 		attribs[atti++] = attributes->stride[i];
 802 		if (egl->has_modifiers &&
 803 				attributes->modifier != DRM_FORMAT_MOD_INVALID) {
 804 			attribs[atti++] = attr_names[i].mod_lo;
 805 			attribs[atti++] = attributes->modifier & 0xFFFFFFFF;
 806 			attribs[atti++] = attr_names[i].mod_hi;
 807 			attribs[atti++] = attributes->modifier >> 32;
 808 		}
 809 	}
 810 
 811 	// Our clients don't expect our usage to trash the buffer contents
 812 	attribs[atti++] = EGL_IMAGE_PRESERVED_KHR;
 813 	attribs[atti++] = EGL_TRUE;
 814 
 815 	attribs[atti++] = EGL_NONE;
 816 	assert(atti <= sizeof(attribs)/sizeof(attribs[0]));
 817 
 818 	EGLImageKHR image = egl->procs.eglCreateImageKHR(egl->display, EGL_NO_CONTEXT,
 819 		EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
 820 	if (image == EGL_NO_IMAGE_KHR) {
 821 		wlr_log(WLR_ERROR, "eglCreateImageKHR failed");
 822 		return EGL_NO_IMAGE_KHR;
 823 	}
 824 
 825 	*external_only = !wlr_drm_format_set_has(&egl->dmabuf_render_formats,
 826 		attributes->format, attributes->modifier);
 827 	return image;
 828 }
 829 
 830 static int get_egl_dmabuf_formats(struct wlr_egl *egl, EGLint **formats) {
 831 	if (!egl->exts.EXT_image_dma_buf_import) {
 832 		wlr_log(WLR_DEBUG, "DMA-BUF import extension not present");
 833 		return -1;
 834 	}
 835 
 836 	// when we only have the image_dmabuf_import extension we can't query
 837 	// which formats are supported. These two are on almost always
 838 	// supported; it's the intended way to just try to create buffers.
 839 	// Just a guess but better than not supporting dmabufs at all,
 840 	// given that the modifiers extension isn't supported everywhere.
 841 	if (!egl->exts.EXT_image_dma_buf_import_modifiers) {
 842 		static const EGLint fallback_formats[] = {
 843 			DRM_FORMAT_ARGB8888,
 844 			DRM_FORMAT_XRGB8888,
 845 		};
 846 		int num = sizeof(fallback_formats) / sizeof(fallback_formats[0]);
 847 
 848 		*formats = calloc(num, sizeof(**formats));
 849 		if (!*formats) {
 850 			wlr_log_errno(WLR_ERROR, "Allocation failed");
 851 			return -1;
 852 		}
 853 
 854 		memcpy(*formats, fallback_formats, num * sizeof(**formats));
 855 		return num;
 856 	}
 857 
 858 	EGLint num;
 859 	if (!egl->procs.eglQueryDmaBufFormatsEXT(egl->display, 0, NULL, &num)) {
 860 		wlr_log(WLR_ERROR, "Failed to query number of dmabuf formats");
 861 		return -1;
 862 	}
 863 
 864 	*formats = calloc(num, sizeof(**formats));
 865 	if (*formats == NULL) {
 866 		wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno));
 867 		return -1;
 868 	}
 869 
 870 	if (!egl->procs.eglQueryDmaBufFormatsEXT(egl->display, num, *formats, &num)) {
 871 		wlr_log(WLR_ERROR, "Failed to query dmabuf format");
 872 		free(*formats);
 873 		return -1;
 874 	}
 875 	return num;
 876 }
 877 
 878 static int get_egl_dmabuf_modifiers(struct wlr_egl *egl, EGLint format,
 879 		uint64_t **modifiers, EGLBoolean **external_only) {
 880 	*modifiers = NULL;
 881 	*external_only = NULL;
 882 
 883 	if (!egl->exts.EXT_image_dma_buf_import) {
 884 		wlr_log(WLR_DEBUG, "DMA-BUF extension not present");
 885 		return -1;
 886 	}
 887 	if (!egl->exts.EXT_image_dma_buf_import_modifiers) {
 888 		return 0;
 889 	}
 890 
 891 	EGLint num;
 892 	if (!egl->procs.eglQueryDmaBufModifiersEXT(egl->display, format, 0,
 893 			NULL, NULL, &num)) {
 894 		wlr_log(WLR_ERROR, "Failed to query dmabuf number of modifiers");
 895 		return -1;
 896 	}
 897 	if (num == 0) {
 898 		return 0;
 899 	}
 900 
 901 	*modifiers = calloc(num, sizeof(**modifiers));
 902 	if (*modifiers == NULL) {
 903 		wlr_log_errno(WLR_ERROR, "Allocation failed");
 904 		return -1;
 905 	}
 906 	*external_only = calloc(num, sizeof(**external_only));
 907 	if (*external_only == NULL) {
 908 		wlr_log_errno(WLR_ERROR, "Allocation failed");
 909 		free(*modifiers);
 910 		*modifiers = NULL;
 911 		return -1;
 912 	}
 913 
 914 	if (!egl->procs.eglQueryDmaBufModifiersEXT(egl->display, format, num,
 915 			*modifiers, *external_only, &num)) {
 916 		wlr_log(WLR_ERROR, "Failed to query dmabuf modifiers");
 917 		free(*modifiers);
 918 		free(*external_only);
 919 		return -1;
 920 	}
 921 	return num;
 922 }
 923 
 924 const struct wlr_drm_format_set *wlr_egl_get_dmabuf_texture_formats(
 925 		struct wlr_egl *egl) {
 926 	return &egl->dmabuf_texture_formats;
 927 }
 928 
 929 const struct wlr_drm_format_set *wlr_egl_get_dmabuf_render_formats(
 930 		struct wlr_egl *egl) {
 931 	return &egl->dmabuf_render_formats;
 932 }
 933 
 934 static bool device_has_name(const drmDevice *device, const char *name) {
 935 	for (size_t i = 0; i < DRM_NODE_MAX; i++) {
 936 		if (!(device->available_nodes & (1 << i))) {
 937 			continue;
 938 		}
 939 		if (strcmp(device->nodes[i], name) == 0) {
 940 			return true;
 941 		}
 942 	}
 943 	return false;
 944 }
 945 
 946 static char *get_render_name(const char *name) {
 947 	uint32_t flags = 0;
 948 	int devices_len = drmGetDevices2(flags, NULL, 0);
 949 	if (devices_len < 0) {
 950 		wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len));
 951 		return NULL;
 952 	}
 953 	drmDevice **devices = calloc(devices_len, sizeof(*devices));
 954 	if (devices == NULL) {
 955 		wlr_log_errno(WLR_ERROR, "Allocation failed");
 956 		return NULL;
 957 	}
 958 	devices_len = drmGetDevices2(flags, devices, devices_len);
 959 	if (devices_len < 0) {
 960 		free(devices);
 961 		wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len));
 962 		return NULL;
 963 	}
 964 
 965 	const drmDevice *match = NULL;
 966 	for (int i = 0; i < devices_len; i++) {
 967 		if (device_has_name(devices[i], name)) {
 968 			match = devices[i];
 969 			break;
 970 		}
 971 	}
 972 
 973 	char *render_name = NULL;
 974 	if (match == NULL) {
 975 		wlr_log(WLR_ERROR, "Cannot find DRM device %s", name);
 976 	} else if (!(match->available_nodes & (1 << DRM_NODE_RENDER))) {
 977 		// Likely a split display/render setup. Pick the primary node and hope
 978 		// Mesa will open the right render node under-the-hood.
 979 		wlr_log(WLR_DEBUG, "DRM device %s has no render node, "
 980 			"falling back to primary node", name);
 981 		assert(match->available_nodes & (1 << DRM_NODE_PRIMARY));
 982 		render_name = strdup(match->nodes[DRM_NODE_PRIMARY]);
 983 	} else {
 984 		render_name = strdup(match->nodes[DRM_NODE_RENDER]);
 985 	}
 986 
 987 	for (int i = 0; i < devices_len; i++) {
 988 		drmFreeDevice(&devices[i]);
 989 	}
 990 	free(devices);
 991 
 992 	return render_name;
 993 }
 994 
 995 static int dup_egl_device_drm_fd(struct wlr_egl *egl) {
 996 	if (egl->device == EGL_NO_DEVICE_EXT || (!egl->exts.EXT_device_drm &&
 997 			!egl->exts.EXT_device_drm_render_node)) {
 998 		return -1;
 999 	}
1000 
1001 	char *render_name = NULL;
1002 #ifdef EGL_EXT_device_drm_render_node
1003 	if (egl->exts.EXT_device_drm_render_node) {
1004 		const char *name = egl->procs.eglQueryDeviceStringEXT(egl->device,
1005 			EGL_DRM_RENDER_NODE_FILE_EXT);
1006 		if (name == NULL) {
1007 			wlr_log(WLR_DEBUG, "EGL device has no render node");
1008 			return -1;
1009 		}
1010 		render_name = strdup(name);
1011 	}
1012 #endif
1013 
1014 	if (render_name == NULL) {
1015 		const char *primary_name = egl->procs.eglQueryDeviceStringEXT(egl->device,
1016 			EGL_DRM_DEVICE_FILE_EXT);
1017 		if (primary_name == NULL) {
1018 			wlr_log(WLR_ERROR,
1019 				"eglQueryDeviceStringEXT(EGL_DRM_DEVICE_FILE_EXT) failed");
1020 			return -1;
1021 		}
1022 
1023 		render_name = get_render_name(primary_name);
1024 		if (render_name == NULL) {
1025 			wlr_log(WLR_ERROR, "Can't find render node name for device %s",
1026 				primary_name);
1027 			return -1;
1028 		}
1029 	}
1030 
1031 	int render_fd = open(render_name, O_RDWR | O_NONBLOCK | O_CLOEXEC);
1032 	if (render_fd < 0) {
1033 		wlr_log_errno(WLR_ERROR, "Failed to open DRM render node %s",
1034 			render_name);
1035 		free(render_name);
1036 		return -1;
1037 	}
1038 	free(render_name);
1039 
1040 	return render_fd;
1041 }
1042 
1043 int wlr_egl_dup_drm_fd(struct wlr_egl *egl) {
1044 	int fd = dup_egl_device_drm_fd(egl);
1045 	if (fd >= 0) {
1046 		return fd;
1047 	}
1048 
1049 	// Fallback to GBM's FD if we can't use EGLDevice
1050 	if (egl->gbm_device == NULL) {
1051 		return -1;
1052 	}
1053 
1054 	fd = fcntl(gbm_device_get_fd(egl->gbm_device), F_DUPFD_CLOEXEC, 0);
1055 	if (fd < 0) {
1056 		wlr_log_errno(WLR_ERROR, "Failed to dup GBM FD");
1057 	}
1058 	return fd;
1059 }
1060 
1061 EGLSyncKHR wlr_egl_create_sync(struct wlr_egl *egl, int fence_fd) {
1062 	if (!egl->procs.eglCreateSyncKHR) {
1063 		return EGL_NO_SYNC_KHR;
1064 	}
1065 
1066 	EGLint attribs[3] = { EGL_NONE };
1067 	int dup_fd = -1;
1068 	if (fence_fd >= 0) {
1069 		dup_fd = fcntl(fence_fd, F_DUPFD_CLOEXEC, 0);
1070 		if (dup_fd < 0) {
1071 			wlr_log_errno(WLR_ERROR, "dup failed");
1072 			return EGL_NO_SYNC_KHR;
1073 		}
1074 
1075 		attribs[0] = EGL_SYNC_NATIVE_FENCE_FD_ANDROID;
1076 		attribs[1] = dup_fd;
1077 		attribs[2] = EGL_NONE;
1078 	}
1079 
1080 	EGLSyncKHR sync = egl->procs.eglCreateSyncKHR(egl->display,
1081 		EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
1082 	if (sync == EGL_NO_SYNC_KHR) {
1083 		wlr_log(WLR_ERROR, "eglCreateSyncKHR failed");
1084 		if (dup_fd >= 0) {
1085 			close(dup_fd);
1086 		}
1087 	}
1088 	return sync;
1089 }
1090 
1091 void wlr_egl_destroy_sync(struct wlr_egl *egl, EGLSyncKHR sync) {
1092 	if (sync == EGL_NO_SYNC_KHR) {
1093 		return;
1094 	}
1095 	assert(egl->procs.eglDestroySyncKHR);
1096 	if (egl->procs.eglDestroySyncKHR(egl->display, sync) != EGL_TRUE) {
1097 		wlr_log(WLR_ERROR, "eglDestroySyncKHR failed");
1098 	}
1099 }
1100 
1101 int wlr_egl_dup_fence_fd(struct wlr_egl *egl, EGLSyncKHR sync) {
1102 	if (!egl->procs.eglDupNativeFenceFDANDROID) {
1103 		return -1;
1104 	}
1105 
1106 	int fd = egl->procs.eglDupNativeFenceFDANDROID(egl->display, sync);
1107 	if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
1108 		wlr_log(WLR_ERROR, "eglDupNativeFenceFDANDROID failed");
1109 		return -1;
1110 	}
1111 
1112 	return fd;
1113 }
1114 
1115 bool wlr_egl_wait_sync(struct wlr_egl *egl, EGLSyncKHR sync) {
1116 	if (egl->procs.eglWaitSyncKHR(egl->display, sync, 0) != EGL_TRUE) {
1117 		wlr_log(WLR_ERROR, "eglWaitSyncKHR failed");
1118 		return false;
1119 	}
1120 	return true;
1121 }