st/dri: Don't close the drm fd on failure
[mesa.git] / src / gallium / state_trackers / dri / dri2.c
index fcca48752299ef2e874e78d8be7cfbe68bc2efbb..5b7be64ba5a0bc1b5a4e24f4c69ba87845a9b308 100644 (file)
@@ -29,6 +29,7 @@
  */
 
 #include <xf86drm.h>
+#include <dlfcn.h>
 #include "util/u_memory.h"
 #include "util/u_inlines.h"
 #include "util/u_format.h"
@@ -42,6 +43,7 @@
 #include "dri_screen.h"
 #include "dri_context.h"
 #include "dri_drawable.h"
+#include "dri_query_renderer.h"
 #include "dri2_buffer.h"
 
 static int convert_fourcc(int format, int *dri_components_p)
@@ -75,6 +77,30 @@ static int convert_fourcc(int format, int *dri_components_p)
    return format;
 }
 
+static int convert_to_fourcc(int format)
+{
+   switch(format) {
+   case __DRI_IMAGE_FORMAT_RGB565:
+      format = __DRI_IMAGE_FOURCC_RGB565;
+      break;
+   case __DRI_IMAGE_FORMAT_ARGB8888:
+      format = __DRI_IMAGE_FOURCC_ARGB8888;
+      break;
+   case __DRI_IMAGE_FORMAT_XRGB8888:
+      format = __DRI_IMAGE_FOURCC_XRGB8888;
+      break;
+   case __DRI_IMAGE_FORMAT_ABGR8888:
+      format = __DRI_IMAGE_FOURCC_ABGR8888;
+      break;
+   case __DRI_IMAGE_FORMAT_XBGR8888:
+      format = __DRI_IMAGE_FOURCC_XBGR8888;
+      break;
+   default:
+      return -1;
+   }
+   return format;
+}
+
 /**
  * DRI2 flush extension.
  */
@@ -162,10 +188,10 @@ dri2_drawable_get_buffers(struct dri_drawable *drawable,
        * may occur as the stvis->color_format.
        */
       switch(format) {
-      case PIPE_FORMAT_B8G8R8A8_UNORM:
+      case PIPE_FORMAT_BGRA8888_UNORM:
         depth = 32;
         break;
-      case PIPE_FORMAT_B8G8R8X8_UNORM:
+      case PIPE_FORMAT_BGRX8888_UNORM:
         depth = 24;
         break;
       case PIPE_FORMAT_B5G6R5_UNORM:
@@ -235,13 +261,13 @@ dri_image_drawable_get_buffers(struct dri_drawable *drawable,
       case PIPE_FORMAT_B5G6R5_UNORM:
          image_format = __DRI_IMAGE_FORMAT_RGB565;
          break;
-      case PIPE_FORMAT_B8G8R8X8_UNORM:
+      case PIPE_FORMAT_BGRX8888_UNORM:
          image_format = __DRI_IMAGE_FORMAT_XRGB8888;
          break;
-      case PIPE_FORMAT_B8G8R8A8_UNORM:
+      case PIPE_FORMAT_BGRA8888_UNORM:
          image_format = __DRI_IMAGE_FORMAT_ARGB8888;
          break;
-      case PIPE_FORMAT_R8G8B8A8_UNORM:
+      case PIPE_FORMAT_RGBA8888_UNORM:
          image_format = __DRI_IMAGE_FORMAT_ABGR8888;
          break;
       default:
@@ -288,10 +314,10 @@ dri2_allocate_buffer(__DRIscreen *sPriv,
 
    switch (format) {
       case 32:
-         pf = PIPE_FORMAT_B8G8R8A8_UNORM;
+         pf = PIPE_FORMAT_BGRA8888_UNORM;
          break;
       case 24:
-         pf = PIPE_FORMAT_B8G8R8X8_UNORM;
+         pf = PIPE_FORMAT_BGRX8888_UNORM;
          break;
       case 16:
          pf = PIPE_FORMAT_Z16_UNORM;
@@ -322,7 +348,11 @@ dri2_allocate_buffer(__DRIscreen *sPriv,
    }
 
    memset(&whandle, 0, sizeof(whandle));
-   whandle.type = DRM_API_HANDLE_TYPE_SHARED;
+   if (screen->can_share_buffer)
+      whandle.type = DRM_API_HANDLE_TYPE_SHARED;
+   else
+      whandle.type = DRM_API_HANDLE_TYPE_KMS;
+
    screen->base.screen->resource_get_handle(screen->base.screen,
          buffer->resource, &whandle);
 
@@ -363,7 +393,7 @@ dri2_allocate_textures(struct dri_context *ctx,
    /* Image specific variables */
    struct __DRIimageList images;
    /* Dri2 specific variables */
-   __DRIbuffer *buffers;
+   __DRIbuffer *buffers = NULL;
    struct winsys_handle whandle;
    unsigned num_buffers = statts_count;
 
@@ -501,10 +531,12 @@ dri2_allocate_textures(struct dri_context *ctx,
          templ.height0 = dri_drawable->h;
          templ.format = format;
          templ.bind = bind;
-         whandle.type = DRM_API_HANDLE_TYPE_SHARED;
          whandle.handle = buf->name;
          whandle.stride = buf->pitch;
-
+         if (screen->can_share_buffer)
+            whandle.type = DRM_API_HANDLE_TYPE_SHARED;
+         else
+            whandle.type = DRM_API_HANDLE_TYPE_KMS;
          drawable->textures[statt] =
             screen->base.screen->resource_from_handle(screen->base.screen,
                   &templ, &whandle);
@@ -522,7 +554,7 @@ dri2_allocate_textures(struct dri_context *ctx,
 
          if (drawable->textures[statt]) {
             templ.format = drawable->textures[statt]->format;
-            templ.bind = drawable->textures[statt]->bind;
+            templ.bind = drawable->textures[statt]->bind & ~PIPE_BIND_SCANOUT;
             templ.nr_samples = drawable->stvis.samples;
 
             /* Try to reuse the resource.
@@ -692,13 +724,13 @@ dri2_create_image_from_winsys(__DRIscreen *_screen,
       pf = PIPE_FORMAT_B5G6R5_UNORM;
       break;
    case __DRI_IMAGE_FORMAT_XRGB8888:
-      pf = PIPE_FORMAT_B8G8R8X8_UNORM;
+      pf = PIPE_FORMAT_BGRX8888_UNORM;
       break;
    case __DRI_IMAGE_FORMAT_ARGB8888:
-      pf = PIPE_FORMAT_B8G8R8A8_UNORM;
+      pf = PIPE_FORMAT_BGRA8888_UNORM;
       break;
    case __DRI_IMAGE_FORMAT_ABGR8888:
-      pf = PIPE_FORMAT_R8G8B8A8_UNORM;
+      pf = PIPE_FORMAT_RGBA8888_UNORM;
       break;
    default:
       pf = PIPE_FORMAT_NONE;
@@ -813,13 +845,13 @@ dri2_create_image(__DRIscreen *_screen,
       pf = PIPE_FORMAT_B5G6R5_UNORM;
       break;
    case __DRI_IMAGE_FORMAT_XRGB8888:
-      pf = PIPE_FORMAT_B8G8R8X8_UNORM;
+      pf = PIPE_FORMAT_BGRX8888_UNORM;
       break;
    case __DRI_IMAGE_FORMAT_ARGB8888:
-      pf = PIPE_FORMAT_B8G8R8A8_UNORM;
+      pf = PIPE_FORMAT_BGRA8888_UNORM;
       break;
    case __DRI_IMAGE_FORMAT_ABGR8888:
-      pf = PIPE_FORMAT_R8G8B8A8_UNORM;
+      pf = PIPE_FORMAT_RGBA8888_UNORM;
       break;
    default:
       pf = PIPE_FORMAT_NONE;
@@ -902,6 +934,12 @@ dri2_query_image(__DRIimage *image, int attrib, int *value)
          return GL_FALSE;
       *value = image->dri_components;
       return GL_TRUE;
+   case __DRI_IMAGE_ATTRIB_FOURCC:
+      *value = convert_to_fourcc(image->dri_format);
+      return GL_TRUE;
+   case __DRI_IMAGE_ATTRIB_NUM_PLANES:
+      *value = 1;
+      return GL_TRUE;
    default:
       return GL_FALSE;
    }
@@ -1186,9 +1224,17 @@ dri2_destroy_image(__DRIimage *img)
    FREE(img);
 }
 
+static int
+dri2_get_capabilities(__DRIscreen *_screen)
+{
+   struct dri_screen *screen = dri_screen(_screen);
+
+   return (screen->can_share_buffer ? __DRI_IMAGE_CAP_GLOBAL_NAMES : 0);
+}
+
 /* The extension is modified during runtime if DRI_PRIME is detected */
 static __DRIimageExtension dri2ImageExtension = {
-    .base = { __DRI_IMAGE, 9 },
+    .base = { __DRI_IMAGE, 11 },
 
     .createImageFromName          = dri2_create_image_from_name,
     .createImageFromRenderbuffer  = dri2_create_image_from_renderbuffer,
@@ -1203,6 +1249,161 @@ static __DRIimageExtension dri2ImageExtension = {
     .createImageFromFds           = NULL,
     .createImageFromDmaBufs       = NULL,
     .blitImage                    = dri2_blit_image,
+    .getCapabilities              = dri2_get_capabilities,
+};
+
+
+static bool
+dri2_is_opencl_interop_loaded_locked(struct dri_screen *screen)
+{
+   return screen->opencl_dri_event_add_ref &&
+          screen->opencl_dri_event_release &&
+          screen->opencl_dri_event_wait &&
+          screen->opencl_dri_event_get_fence;
+}
+
+static bool
+dri2_load_opencl_interop(struct dri_screen *screen)
+{
+#if defined(RTLD_DEFAULT)
+   bool success;
+
+   pipe_mutex_lock(screen->opencl_func_mutex);
+
+   if (dri2_is_opencl_interop_loaded_locked(screen)) {
+      pipe_mutex_unlock(screen->opencl_func_mutex);
+      return true;
+   }
+
+   screen->opencl_dri_event_add_ref =
+      dlsym(RTLD_DEFAULT, "opencl_dri_event_add_ref");
+   screen->opencl_dri_event_release =
+      dlsym(RTLD_DEFAULT, "opencl_dri_event_release");
+   screen->opencl_dri_event_wait =
+      dlsym(RTLD_DEFAULT, "opencl_dri_event_wait");
+   screen->opencl_dri_event_get_fence =
+      dlsym(RTLD_DEFAULT, "opencl_dri_event_get_fence");
+
+   success = dri2_is_opencl_interop_loaded_locked(screen);
+   pipe_mutex_unlock(screen->opencl_func_mutex);
+   return success;
+#else
+   return false;
+#endif
+}
+
+struct dri2_fence {
+   struct dri_screen *driscreen;
+   struct pipe_fence_handle *pipe_fence;
+   void *cl_event;
+};
+
+static void *
+dri2_create_fence(__DRIcontext *_ctx)
+{
+   struct pipe_context *ctx = dri_context(_ctx)->st->pipe;
+   struct dri2_fence *fence = CALLOC_STRUCT(dri2_fence);
+
+   if (!fence)
+      return NULL;
+
+   ctx->flush(ctx, &fence->pipe_fence, 0);
+
+   if (!fence->pipe_fence) {
+      FREE(fence);
+      return NULL;
+   }
+
+   fence->driscreen = dri_screen(_ctx->driScreenPriv);
+   return fence;
+}
+
+static void *
+dri2_get_fence_from_cl_event(__DRIscreen *_screen, intptr_t cl_event)
+{
+   struct dri_screen *driscreen = dri_screen(_screen);
+   struct dri2_fence *fence;
+
+   if (!dri2_load_opencl_interop(driscreen))
+      return NULL;
+
+   fence = CALLOC_STRUCT(dri2_fence);
+   if (!fence)
+      return NULL;
+
+   fence->cl_event = (void*)cl_event;
+
+   if (!driscreen->opencl_dri_event_add_ref(fence->cl_event)) {
+      free(fence);
+      return NULL;
+   }
+
+   fence->driscreen = driscreen;
+   return fence;
+}
+
+static void
+dri2_destroy_fence(__DRIscreen *_screen, void *_fence)
+{
+   struct dri_screen *driscreen = dri_screen(_screen);
+   struct pipe_screen *screen = driscreen->base.screen;
+   struct dri2_fence *fence = (struct dri2_fence*)_fence;
+
+   if (fence->pipe_fence)
+      screen->fence_reference(screen, &fence->pipe_fence, NULL);
+   else if (fence->cl_event)
+      driscreen->opencl_dri_event_release(fence->cl_event);
+   else
+      assert(0);
+
+   FREE(fence);
+}
+
+static GLboolean
+dri2_client_wait_sync(__DRIcontext *_ctx, void *_fence, unsigned flags,
+                      uint64_t timeout)
+{
+   struct dri2_fence *fence = (struct dri2_fence*)_fence;
+   struct dri_screen *driscreen = fence->driscreen;
+   struct pipe_screen *screen = driscreen->base.screen;
+
+   /* No need to flush. The context was flushed when the fence was created. */
+
+   if (fence->pipe_fence)
+      return screen->fence_finish(screen, fence->pipe_fence, timeout);
+   else if (fence->cl_event) {
+      struct pipe_fence_handle *pipe_fence =
+         driscreen->opencl_dri_event_get_fence(fence->cl_event);
+
+      if (pipe_fence)
+         return screen->fence_finish(screen, pipe_fence, timeout);
+      else
+         return driscreen->opencl_dri_event_wait(fence->cl_event, timeout);
+   }
+   else {
+      assert(0);
+      return false;
+   }
+}
+
+static void
+dri2_server_wait_sync(__DRIcontext *_ctx, void *_fence, unsigned flags)
+{
+   /* AFAIK, no driver currently supports parallel context execution. */
+}
+
+static __DRI2fenceExtension dri2FenceExtension = {
+   .base = { __DRI2_FENCE, 1 },
+
+   .create_fence = dri2_create_fence,
+   .get_fence_from_cl_event = dri2_get_fence_from_cl_event,
+   .destroy_fence = dri2_destroy_fence,
+   .client_wait_sync = dri2_client_wait_sync,
+   .server_wait_sync = dri2_server_wait_sync
+};
+
+static const __DRIrobustnessExtension dri2Robustness = {
+   .base = { __DRI2_ROBUSTNESS, 1 }
 };
 
 /*
@@ -1213,8 +1414,22 @@ static const __DRIextension *dri_screen_extensions[] = {
    &driTexBufferExtension.base,
    &dri2FlushExtension.base,
    &dri2ImageExtension.base,
+   &dri2RendererQueryExtension.base,
    &dri2ConfigQueryExtension.base,
    &dri2ThrottleExtension.base,
+   &dri2FenceExtension.base,
+   NULL
+};
+
+static const __DRIextension *dri_robust_screen_extensions[] = {
+   &driTexBufferExtension.base,
+   &dri2FlushExtension.base,
+   &dri2ImageExtension.base,
+   &dri2RendererQueryExtension.base,
+   &dri2ConfigQueryExtension.base,
+   &dri2ThrottleExtension.base,
+   &dri2FenceExtension.base,
+   &dri2Robustness.base,
    NULL
 };
 
@@ -1238,22 +1453,16 @@ dri2_init_screen(__DRIscreen * sPriv)
 
    screen->sPriv = sPriv;
    screen->fd = sPriv->fd;
+   pipe_mutex_init(screen->opencl_func_mutex);
 
    sPriv->driverPrivate = (void *)screen;
 
-#if GALLIUM_STATIC_TARGETS
-   pscreen = dd_create_screen(screen->fd);
-
-   throttle_ret = dd_configuration(DRM_CONF_THROTTLE);
-   dmabuf_ret = dd_configuration(DRM_CONF_SHARE_FD);
-#else
-   if (pipe_loader_drm_probe_fd(&screen->dev, screen->fd, true)) {
-      pscreen = pipe_loader_create_screen(screen->dev, PIPE_SEARCH_DIR);
+   if (pipe_loader_drm_probe_fd(&screen->dev, dup(screen->fd))) {
+      pscreen = pipe_loader_create_screen(screen->dev);
 
       throttle_ret = pipe_loader_configuration(screen->dev, DRM_CONF_THROTTLE);
       dmabuf_ret = pipe_loader_configuration(screen->dev, DRM_CONF_SHARE_FD);
    }
-#endif // GALLIUM_STATIC_TARGETS
 
    if (throttle_ret && throttle_ret->val.val_int != -1) {
       screen->throttling_enabled = TRUE;
@@ -1270,18 +1479,73 @@ dri2_init_screen(__DRIscreen * sPriv)
       }
    }
 
-   sPriv->extensions = dri_screen_extensions;
+   if (pscreen && pscreen->get_param(pscreen, PIPE_CAP_DEVICE_RESET_STATUS_QUERY)) {
+      sPriv->extensions = dri_robust_screen_extensions;
+      screen->has_reset_status_query = true;
+   }
+   else
+      sPriv->extensions = dri_screen_extensions;
 
    /* dri_init_screen_helper checks pscreen for us */
 
-#if GALLIUM_STATIC_TARGETS
-   configs = dri_init_screen_helper(screen, pscreen, dd_driver_name());
-#else
    configs = dri_init_screen_helper(screen, pscreen, screen->dev->driver_name);
-#endif // GALLIUM_STATIC_TARGETS
    if (!configs)
       goto fail;
 
+   screen->can_share_buffer = true;
+   screen->auto_fake_front = dri_with_format(sPriv);
+   screen->broken_invalidate = !sPriv->dri2.useInvalidate;
+   screen->lookup_egl_image = dri2_lookup_egl_image;
+
+   return configs;
+fail:
+   dri_destroy_screen_helper(screen);
+   if (screen->dev)
+      pipe_loader_release(&screen->dev, 1);
+   FREE(screen);
+   return NULL;
+}
+
+/**
+ * This is the driver specific part of the createNewScreen entry point.
+ *
+ * Returns the struct gl_config supported by this driver.
+ */
+static const __DRIconfig **
+dri_kms_init_screen(__DRIscreen * sPriv)
+{
+#if defined(GALLIUM_SOFTPIPE)
+   const __DRIconfig **configs;
+   struct dri_screen *screen;
+   struct pipe_screen *pscreen = NULL;
+   uint64_t cap;
+
+   screen = CALLOC_STRUCT(dri_screen);
+   if (!screen)
+      return NULL;
+
+   screen->sPriv = sPriv;
+   screen->fd = sPriv->fd;
+
+   sPriv->driverPrivate = (void *)screen;
+
+   if (pipe_loader_sw_probe_kms(&screen->dev, dup(screen->fd)))
+      pscreen = pipe_loader_create_screen(screen->dev);
+
+   if (drmGetCap(sPriv->fd, DRM_CAP_PRIME, &cap) == 0 &&
+          (cap & DRM_PRIME_CAP_IMPORT)) {
+      dri2ImageExtension.createImageFromFds = dri2_from_fds;
+      dri2ImageExtension.createImageFromDmaBufs = dri2_from_dma_bufs;
+   }
+
+   sPriv->extensions = dri_screen_extensions;
+
+   /* dri_init_screen_helper checks pscreen for us */
+   configs = dri_init_screen_helper(screen, pscreen, "swrast");
+   if (!configs)
+      goto fail;
+
+   screen->can_share_buffer = false;
    screen->auto_fake_front = dri_with_format(sPriv);
    screen->broken_invalidate = !sPriv->dri2.useInvalidate;
    screen->lookup_egl_image = dri2_lookup_egl_image;
@@ -1289,11 +1553,10 @@ dri2_init_screen(__DRIscreen * sPriv)
    return configs;
 fail:
    dri_destroy_screen_helper(screen);
-#if !GALLIUM_STATIC_TARGETS
    if (screen->dev)
       pipe_loader_release(&screen->dev, 1);
-#endif // !GALLIUM_STATIC_TARGETS
    FREE(screen);
+#endif // GALLIUM_SOFTPIPE
    return NULL;
 }
 
@@ -1335,6 +1598,27 @@ const struct __DriverAPIRec galliumdrm_driver_api = {
    .ReleaseBuffer  = dri2_release_buffer,
 };
 
+/**
+ * DRI driver virtual function table.
+ *
+ * KMS/DRM version of the DriverAPI above sporting a different InitScreen
+ * hook. The latter is used to explicitly initialise the kms_swrast driver
+ * rather than selecting the approapriate driver as suggested by the loader.
+ */
+const struct __DriverAPIRec dri_kms_driver_api = {
+   .InitScreen = dri_kms_init_screen,
+   .DestroyScreen = dri_destroy_screen,
+   .CreateContext = dri_create_context,
+   .DestroyContext = dri_destroy_context,
+   .CreateBuffer = dri2_create_buffer,
+   .DestroyBuffer = dri_destroy_buffer,
+   .MakeCurrent = dri_make_current,
+   .UnbindContext = dri_unbind_context,
+
+   .AllocateBuffer = dri2_allocate_buffer,
+   .ReleaseBuffer  = dri2_release_buffer,
+};
+
 /* This is the table of extensions that the loader will dlsym() for. */
 const __DRIextension *galliumdrm_driver_extensions[] = {
     &driCoreExtension.base,