+#ifdef HAVE_DRM_GRALLOC
+static const __DRIextension *droid_dri2_loader_extensions[] = {
+ &droid_dri2_loader_extension.base,
+ &image_lookup_extension.base,
+ &use_invalidate.base,
+ /* No __DRI_MUTABLE_RENDER_BUFFER_LOADER because it requires
+ * __DRI_IMAGE_LOADER.
+ */
+ NULL,
+};
+#endif /* HAVE_DRM_GRALLOC */
+
+static void
+droid_display_shared_buffer(__DRIdrawable *driDrawable, int fence_fd,
+ void *loaderPrivate)
+{
+ struct dri2_egl_surface *dri2_surf = loaderPrivate;
+ struct ANativeWindowBuffer *old_buffer UNUSED = dri2_surf->buffer;
+
+ if (!_eglSurfaceInSharedBufferMode(&dri2_surf->base)) {
+ _eglLog(_EGL_WARNING, "%s: internal error: buffer is not shared",
+ __func__);
+ return;
+ }
+
+ if (fence_fd >= 0) {
+ /* The driver's fence is more recent than the surface's out fence, if it
+ * exists at all. So use the driver's fence.
+ */
+ if (dri2_surf->out_fence_fd >= 0) {
+ close(dri2_surf->out_fence_fd);
+ dri2_surf->out_fence_fd = -1;
+ }
+ } else if (dri2_surf->out_fence_fd >= 0) {
+ fence_fd = dri2_surf->out_fence_fd;
+ dri2_surf->out_fence_fd = -1;
+ }
+
+ if (dri2_surf->window->queueBuffer(dri2_surf->window, dri2_surf->buffer,
+ fence_fd)) {
+ _eglLog(_EGL_WARNING, "%s: ANativeWindow::queueBuffer failed", __func__);
+ close(fence_fd);
+ return;
+ }
+
+ fence_fd = -1;
+
+ if (dri2_surf->window->dequeueBuffer(dri2_surf->window, &dri2_surf->buffer,
+ &fence_fd)) {
+ /* Tear down the surface because it no longer has a back buffer. */
+ struct dri2_egl_display *dri2_dpy =
+ dri2_egl_display(dri2_surf->base.Resource.Display);
+
+ _eglLog(_EGL_WARNING, "%s: ANativeWindow::dequeueBuffer failed", __func__);
+
+ dri2_surf->base.Lost = true;
+ dri2_surf->buffer = NULL;
+ dri2_surf->back = NULL;
+
+ if (dri2_surf->dri_image_back) {
+ dri2_dpy->image->destroyImage(dri2_surf->dri_image_back);
+ dri2_surf->dri_image_back = NULL;
+ }
+
+ dri2_dpy->flush->invalidate(dri2_surf->dri_drawable);
+ return;
+ }
+
+ if (fence_fd < 0)
+ return;
+
+ /* Access to the buffer is controlled by a sync fence. Block on it.
+ *
+ * Ideally, we would submit the fence to the driver, and the driver would
+ * postpone command execution until it signalled. But DRI lacks API for
+ * that (as of 2018-04-11).
+ *
+ * SYNC_IOC_WAIT waits forever if timeout < 0
+ */
+ sync_wait(fence_fd, -1);
+ close(fence_fd);
+}
+
+static const __DRImutableRenderBufferLoaderExtension droid_mutable_render_buffer_extension = {
+ .base = { __DRI_MUTABLE_RENDER_BUFFER_LOADER, 1 },
+ .displaySharedBuffer = droid_display_shared_buffer,
+};
+
+static const __DRIextension *droid_image_loader_extensions[] = {
+ &droid_image_loader_extension.base,
+ &image_lookup_extension.base,
+ &use_invalidate.base,
+ &droid_mutable_render_buffer_extension.base,
+ NULL,
+};
+
+static EGLBoolean
+droid_load_driver(_EGLDisplay *disp)
+{
+ struct dri2_egl_display *dri2_dpy = disp->DriverData;
+ const char *err;
+
+ dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd);
+ if (dri2_dpy->driver_name == NULL)
+ return false;
+
+ dri2_dpy->is_render_node = drmGetNodeTypeFromFd(dri2_dpy->fd) == DRM_NODE_RENDER;
+
+ if (!dri2_dpy->is_render_node) {
+#ifdef HAVE_DRM_GRALLOC
+ /* Handle control nodes using __DRI_DRI2_LOADER extension and GEM names
+ * for backwards compatibility with drm_gralloc. (Do not use on new
+ * systems.) */
+ dri2_dpy->loader_extensions = droid_dri2_loader_extensions;
+ if (!dri2_load_driver(disp)) {
+ err = "DRI2: failed to load driver";
+ goto error;
+ }
+#else
+ err = "DRI2: handle is not for a render node";
+ goto error;
+#endif
+ } else {
+ dri2_dpy->loader_extensions = droid_image_loader_extensions;
+ if (!dri2_load_driver_dri3(disp)) {
+ err = "DRI3: failed to load driver";
+ goto error;
+ }
+ }
+
+ return true;
+
+error:
+ free(dri2_dpy->driver_name);
+ dri2_dpy->driver_name = NULL;
+ return false;
+}
+
+static void
+droid_unload_driver(_EGLDisplay *disp)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+
+ dlclose(dri2_dpy->driver);
+ dri2_dpy->driver = NULL;
+ free(dri2_dpy->driver_name);
+ dri2_dpy->driver_name = NULL;
+}
+
+static int
+droid_filter_device(_EGLDisplay *disp, int fd, const char *vendor)
+{
+ drmVersionPtr ver = drmGetVersion(fd);
+ if (!ver)
+ return -1;
+
+ if (strcmp(vendor, ver->name) != 0) {
+ drmFreeVersion(ver);
+ return -1;
+ }
+
+ drmFreeVersion(ver);
+ return 0;
+}
+
+static EGLBoolean
+droid_probe_device(_EGLDisplay *disp)
+{
+ /* Check that the device is supported, by attempting to:
+ * - load the dri module
+ * - and, create a screen
+ */
+ if (!droid_load_driver(disp))
+ return EGL_FALSE;
+
+ if (!dri2_create_screen(disp)) {
+ _eglLog(_EGL_WARNING, "DRI2: failed to create screen");
+ droid_unload_driver(disp);
+ return EGL_FALSE;
+ }
+ return EGL_TRUE;
+}
+
+static EGLBoolean
+droid_open_device(_EGLDisplay *disp)
+{
+#define MAX_DRM_DEVICES 32
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+ drmDevicePtr device, devices[MAX_DRM_DEVICES] = { NULL };
+ int num_devices;
+
+ char *vendor_name = NULL;
+ char vendor_buf[PROPERTY_VALUE_MAX];
+
+ if (property_get("drm.gpu.vendor_name", vendor_buf, NULL) > 0)
+ vendor_name = vendor_buf;
+
+ num_devices = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
+ if (num_devices < 0)
+ return EGL_FALSE;
+
+ for (int i = 0; i < num_devices; i++) {
+ device = devices[i];
+
+ if (!(device->available_nodes & (1 << DRM_NODE_RENDER)))
+ continue;
+
+ dri2_dpy->fd = loader_open_device(device->nodes[DRM_NODE_RENDER]);
+ if (dri2_dpy->fd < 0) {
+ _eglLog(_EGL_WARNING, "%s() Failed to open DRM device %s",
+ __func__, device->nodes[DRM_NODE_RENDER]);
+ continue;
+ }
+
+ /* If a vendor is explicitly provided, we use only that.
+ * Otherwise we fall-back the first device that is supported.
+ */
+ if (vendor_name) {
+ if (droid_filter_device(disp, dri2_dpy->fd, vendor_name)) {
+ /* Device does not match - try next device */
+ close(dri2_dpy->fd);
+ dri2_dpy->fd = -1;
+ continue;
+ }
+ /* If the requested device matches - use it. Regardless if
+ * init fails, do not fall-back to any other device.
+ */
+ if (!droid_probe_device(disp)) {
+ close(dri2_dpy->fd);
+ dri2_dpy->fd = -1;
+ }
+
+ break;
+ }
+ if (droid_probe_device(disp))
+ break;
+
+ /* No explicit request - attempt the next device */
+ close(dri2_dpy->fd);
+ dri2_dpy->fd = -1;
+ }
+ drmFreeDevices(devices, num_devices);
+
+ if (dri2_dpy->fd < 0) {
+ _eglLog(_EGL_WARNING, "Failed to open %s DRM device",
+ vendor_name ? "desired": "any");
+ return EGL_FALSE;
+ }
+
+ return EGL_TRUE;
+#undef MAX_DRM_DEVICES
+}
+