From b030cfe05f585234c10337576047bc314b36faef Mon Sep 17 00:00:00 2001
From: Philip Langdale <philipl@overt.org>
Date: Mon, 16 Dec 2019 19:23:14 -0800
Subject: [PATCH] vo_gpu: vulkan: open DRM render fd when using VK_KHR_display

While the basic Vulkan Display context can theoretically drive the
display without the involvement of any non-Vulkan code, that prevents
us from using VAAPI acceleration. When initialising VAAPI without a
window system, we need to provide it with an opened DRM render fd
corresponding to the device to use.

In the context of using VK_KHR_display, that means we need to identify
which DRM device matches the selected Vulkan device, and then open its
render fd and set the necessary state that VAAPI expects to find.

With that done, the normal VAAPI<->Vulkan interop can kick in and we
get working acceleration
---
 video/out/vulkan/context_display.c | 104 ++++++++++++++++++++++++++++-
 1 file changed, 102 insertions(+), 2 deletions(-)

diff --git a/video/out/vulkan/context_display.c b/video/out/vulkan/context_display.c
index 5545b3378d5..956a3f2a2f9 100644
--- a/video/out/vulkan/context_display.c
+++ b/video/out/vulkan/context_display.c
@@ -15,11 +15,19 @@
  * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "options/m_config.h"
-
 #include "context.h"
+#include "options/m_config.h"
 #include "utils.h"
 
+#if HAVE_DRM
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "libmpv/render_gl.h"
+#include "video/out/drm_common.h"
+#endif
+
 struct vulkan_display_opts {
     int display;
     int mode;
@@ -278,14 +286,83 @@ struct priv {
     struct vulkan_display_opts *opts;
     uint32_t width;
     uint32_t height;
+
+#if HAVE_DRM
+    struct mpv_opengl_drm_params_v2 drm_params;
+#endif
 };
 
+#if HAVE_DRM
+static void open_render_fd(struct ra_ctx *ctx, const char *render_path)
+{
+    struct priv *p = ctx->priv;
+    p->drm_params.fd = -1;
+    p->drm_params.render_fd = open(render_path, O_RDWR | O_CLOEXEC);
+    if (p->drm_params.render_fd == -1) {
+        MP_WARN(ctx, "Failed to open render node: %s\n",
+                strerror(errno));
+    }
+}
+
+static bool drm_setup(struct ra_ctx *ctx, int display_idx,
+                      VkPhysicalDevicePCIBusInfoPropertiesEXT *pci_props)
+{
+    drmDevice *devs[32] = {};
+    int count = drmGetDevices2(0, devs, MP_ARRAY_SIZE(devs));
+    for (int i = 0; i < count; i++) {
+        drmDevice *dev = devs[i];
+
+        if (dev->bustype != DRM_BUS_PCI ||
+            dev->businfo.pci->domain != pci_props->pciDomain ||
+            dev->businfo.pci->bus != pci_props->pciBus ||
+            dev->businfo.pci->dev != pci_props->pciDevice ||
+            dev->businfo.pci->func != pci_props->pciFunction)
+        {
+            continue;
+        }
+
+        // Found our matching device.
+        MP_DBG(ctx, "DRM device found for Vulkan device at %04X:%02X:%02X:%02X\n",
+                pci_props->pciDomain, pci_props->pciBus,
+                pci_props->pciDevice, pci_props->pciFunction);
+
+        if (!(dev->available_nodes & 1 << DRM_NODE_RENDER)) {
+            MP_DBG(ctx, "Card does not have a render node.\n");
+            continue;
+        }
+
+        open_render_fd(ctx, dev->nodes[DRM_NODE_RENDER]);
+
+        break;
+    }
+    drmFreeDevices(devs, MP_ARRAY_SIZE(devs));
+
+    struct priv *p = ctx->priv;
+    if (p->drm_params.render_fd == -1) {
+        MP_WARN(ctx, "Couldn't open DRM render node for Vulkan device "
+                     "at: %04X:%02X:%02X:%02X\n",
+                     pci_props->pciDomain, pci_props->pciBus,
+                     pci_props->pciDevice, pci_props->pciFunction);
+        return false;
+    }
+
+    return true;
+}
+#endif
+
 static void display_uninit(struct ra_ctx *ctx)
 {
     struct priv *p = ctx->priv;
 
     ra_vk_ctx_uninit(ctx);
     mpvk_uninit(&p->vk);
+
+#if HAVE_DRM
+    if (p->drm_params.render_fd != -1) {
+        close(p->drm_params.render_fd);
+        p->drm_params.render_fd = -1;
+    }
+#endif
 }
 
 static bool display_init(struct ra_ctx *ctx)
@@ -318,6 +395,20 @@ static bool display_init(struct ra_ctx *ctx)
         goto error;
     }
 
+#if HAVE_DRM
+        VkPhysicalDevicePCIBusInfoPropertiesEXT pci_props = {
+            .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT,
+        };
+        VkPhysicalDeviceProperties2KHR props = {
+            .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
+            .pNext = &pci_props,
+        };
+        vkGetPhysicalDeviceProperties2(device, &props);
+
+        if (!drm_setup(ctx, display_idx, &pci_props))
+            MP_WARN(ctx, "Failed to set up DRM.\n");
+#endif
+
     struct mode_selector selector = {
         .display_idx = display_idx,
         .mode_idx = mode_idx,
@@ -351,6 +442,15 @@ static bool display_init(struct ra_ctx *ctx)
     if (!ra_vk_ctx_init(ctx, vk, params, VK_PRESENT_MODE_FIFO_KHR))
         goto error;
 
+#if HAVE_DRM
+    if (p->drm_params.render_fd > -1) {
+        ra_add_native_resource(ctx->ra, "drm_params_v2", &p->drm_params);
+    } else {
+        MP_WARN(ctx,
+               "No DRM render fd available. VAAPI hwaccel will not be usable.\n");
+    }
+#endif
+
     ret = true;
 
 done:
