summaryrefslogtreecommitdiffstats
path: root/video/out/vulkan
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/vulkan')
-rw-r--r--video/out/vulkan/common.h13
-rw-r--r--video/out/vulkan/context.c215
-rw-r--r--video/out/vulkan/context.h14
-rw-r--r--video/out/vulkan/context_android.c1
-rw-r--r--video/out/vulkan/context_display.c496
-rw-r--r--video/out/vulkan/context_mac.m136
-rw-r--r--video/out/vulkan/context_wayland.c128
-rw-r--r--video/out/vulkan/context_win.c22
-rw-r--r--video/out/vulkan/context_xlib.c30
-rw-r--r--video/out/vulkan/utils.c19
10 files changed, 872 insertions, 202 deletions
diff --git a/video/out/vulkan/common.h b/video/out/vulkan/common.h
index b085fb462c..e75cb228f8 100644
--- a/video/out/vulkan/common.h
+++ b/video/out/vulkan/common.h
@@ -22,15 +22,18 @@
#if HAVE_WIN32_DESKTOP
#define VK_USE_PLATFORM_WIN32_KHR
#endif
+#if HAVE_COCOA
+#define VK_USE_PLATFORM_METAL_EXT
+#endif
#include <libplacebo/vulkan.h>
// Shared struct used to hold vulkan context information
struct mpvk_ctx {
- struct mp_log *pl_log;
- struct pl_context *ctx;
- const struct pl_vk_inst *vkinst;
- const struct pl_vulkan *vulkan;
- const struct pl_gpu *gpu; // points to vulkan->gpu for convenience
+ pl_log pllog;
+ pl_vk_inst vkinst;
+ pl_vulkan vulkan;
+ pl_gpu gpu; // points to vulkan->gpu for convenience
+ pl_swapchain swapchain;
VkSurfaceKHR surface;
};
diff --git a/video/out/vulkan/context.c b/video/out/vulkan/context.c
index 3518d3efd2..196f6f468d 100644
--- a/video/out/vulkan/context.c
+++ b/video/out/vulkan/context.c
@@ -15,73 +15,98 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "config.h"
+
+#if HAVE_LAVU_UUID
+#include <libavutil/uuid.h>
+#else
+#include "misc/uuid.h"
+#endif
+
#include "options/m_config.h"
#include "video/out/placebo/ra_pl.h"
+#include "video/out/placebo/utils.h"
#include "context.h"
-#include "utils.h"
struct vulkan_opts {
char *device; // force a specific GPU
int swap_mode;
int queue_count;
- int async_transfer;
- int async_compute;
- int disable_events;
+ bool async_transfer;
+ bool async_compute;
};
-static int vk_validate_dev(struct mp_log *log, const struct m_option *opt,
- struct bstr name, struct bstr param)
+static inline OPT_STRING_VALIDATE_FUNC(vk_validate_dev)
{
int ret = M_OPT_INVALID;
- VkResult res;
+ void *ta_ctx = talloc_new(NULL);
+ pl_log pllog = mppl_log_create(ta_ctx, log);
+ if (!pllog)
+ goto done;
// Create a dummy instance to validate/list the devices
- VkInstanceCreateInfo info = {
- .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
- };
-
- VkInstance inst;
- VkPhysicalDevice *devices = NULL;
- uint32_t num = 0;
-
- res = vkCreateInstance(&info, NULL, &inst);
- if (res != VK_SUCCESS)
+ mppl_log_set_probing(pllog, true);
+ pl_vk_inst inst = pl_vk_inst_create(pllog, pl_vk_inst_params());
+ mppl_log_set_probing(pllog, false);
+ if (!inst)
goto done;
- res = vkEnumeratePhysicalDevices(inst, &num, NULL);
+ uint32_t num = 0;
+ VkResult res = vkEnumeratePhysicalDevices(inst->instance, &num, NULL);
if (res != VK_SUCCESS)
goto done;
- devices = talloc_array(NULL, VkPhysicalDevice, num);
- vkEnumeratePhysicalDevices(inst, &num, devices);
+ VkPhysicalDevice *devices = talloc_array(ta_ctx, VkPhysicalDevice, num);
+ res = vkEnumeratePhysicalDevices(inst->instance, &num, devices);
if (res != VK_SUCCESS)
goto done;
+ struct bstr param = bstr0(*value);
bool help = bstr_equals0(param, "help");
if (help) {
mp_info(log, "Available vulkan devices:\n");
ret = M_OPT_EXIT;
}
+ AVUUID param_uuid;
+ bool is_uuid = av_uuid_parse(*value, param_uuid) == 0;
+
for (int i = 0; i < num; i++) {
- VkPhysicalDeviceProperties prop;
- vkGetPhysicalDeviceProperties(devices[i], &prop);
+ VkPhysicalDeviceIDPropertiesKHR id_prop = { 0 };
+ id_prop.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR;
+
+ VkPhysicalDeviceProperties2KHR prop2 = { 0 };
+ prop2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
+ prop2.pNext = &id_prop;
+
+ vkGetPhysicalDeviceProperties2(devices[i], &prop2);
+
+ const VkPhysicalDeviceProperties *prop = &prop2.properties;
if (help) {
- mp_info(log, " '%s' (GPU %d, ID %x:%x)\n", prop.deviceName, i,
- (unsigned)prop.vendorID, (unsigned)prop.deviceID);
- } else if (bstr_equals0(param, prop.deviceName)) {
+ char device_uuid[37];
+ av_uuid_unparse(id_prop.deviceUUID, device_uuid);
+ mp_info(log, " '%s' (GPU %d, PCI ID %x:%x, UUID %s)\n",
+ prop->deviceName, i, (unsigned)prop->vendorID,
+ (unsigned)prop->deviceID, device_uuid);
+ } else if (bstr_equals0(param, prop->deviceName)) {
+ ret = 0;
+ goto done;
+ } else if (is_uuid && av_uuid_equal(param_uuid, id_prop.deviceUUID)) {
ret = 0;
goto done;
}
}
if (!help)
- mp_err(log, "No device with name '%.*s'!\n", BSTR_P(param));
+ mp_err(log, "No device with %s '%.*s'!\n", is_uuid ? "UUID" : "name",
+ BSTR_P(param));
done:
- talloc_free(devices);
+ pl_vk_inst_destroy(&inst);
+ pl_log_destroy(&pllog);
+ talloc_free(ta_ctx);
return ret;
}
@@ -96,9 +121,9 @@ const struct m_sub_options vulkan_conf = {
{"mailbox", VK_PRESENT_MODE_MAILBOX_KHR},
{"immediate", VK_PRESENT_MODE_IMMEDIATE_KHR})},
{"vulkan-queue-count", OPT_INT(queue_count), M_RANGE(1, 8)},
- {"vulkan-async-transfer", OPT_FLAG(async_transfer)},
- {"vulkan-async-compute", OPT_FLAG(async_compute)},
- {"vulkan-disable-events", OPT_FLAG(disable_events)},
+ {"vulkan-async-transfer", OPT_BOOL(async_transfer)},
+ {"vulkan-async-compute", OPT_BOOL(async_compute)},
+ {"vulkan-disable-events", OPT_REMOVED("Unused")},
{0}
},
.size = sizeof(struct vulkan_opts),
@@ -114,7 +139,6 @@ struct priv {
struct mpvk_ctx *vk;
struct vulkan_opts *opts;
struct ra_vk_ctx_params params;
- const struct pl_swapchain *swapchain;
struct ra_tex proxy_tex;
};
@@ -122,7 +146,7 @@ static const struct ra_swapchain_fns vulkan_swapchain;
struct mpvk_ctx *ra_vk_ctx_get(struct ra_ctx *ctx)
{
- if (ctx->swapchain->fns != &vulkan_swapchain)
+ if (!ctx->swapchain || ctx->swapchain->fns != &vulkan_swapchain)
return NULL;
struct priv *p = ctx->swapchain->priv;
@@ -139,7 +163,7 @@ void ra_vk_ctx_uninit(struct ra_ctx *ctx)
if (ctx->ra) {
pl_gpu_finish(vk->gpu);
- pl_swapchain_destroy(&p->swapchain);
+ pl_swapchain_destroy(&vk->swapchain);
ctx->ra->fns->destroy(ctx->ra);
ctx->ra = NULL;
}
@@ -149,6 +173,75 @@ void ra_vk_ctx_uninit(struct ra_ctx *ctx)
TA_FREEP(&ctx->swapchain);
}
+pl_vulkan mppl_create_vulkan(struct vulkan_opts *opts,
+ pl_vk_inst vkinst,
+ pl_log pllog,
+ VkSurfaceKHR surface)
+{
+ VkPhysicalDeviceFeatures2 features = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
+ };
+
+#if HAVE_VULKAN_INTEROP
+ /*
+ * Request the additional extensions and features required to make full use
+ * of the ffmpeg Vulkan hwcontext and video decoding capability.
+ */
+ const char *opt_extensions[] = {
+ VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME,
+ VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME,
+ VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME,
+ VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME,
+ VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME,
+ VK_KHR_VIDEO_QUEUE_EXTENSION_NAME,
+ "VK_KHR_video_decode_av1", /* VK_KHR_VIDEO_DECODE_AV1_EXTENSION_NAME */
+ };
+
+ VkPhysicalDeviceDescriptorBufferFeaturesEXT descriptor_buffer_feature = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT,
+ .pNext = NULL,
+ .descriptorBuffer = true,
+ .descriptorBufferPushDescriptors = true,
+ };
+
+ VkPhysicalDeviceShaderAtomicFloatFeaturesEXT atomic_float_feature = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT,
+ .pNext = &descriptor_buffer_feature,
+ .shaderBufferFloat32Atomics = true,
+ .shaderBufferFloat32AtomicAdd = true,
+ };
+
+ features.pNext = &atomic_float_feature;
+#endif
+
+ AVUUID param_uuid = { 0 };
+ bool is_uuid = opts->device &&
+ av_uuid_parse(opts->device, param_uuid) == 0;
+
+ assert(pllog);
+ assert(vkinst);
+ struct pl_vulkan_params device_params = {
+ .instance = vkinst->instance,
+ .get_proc_addr = vkinst->get_proc_addr,
+ .surface = surface,
+ .async_transfer = opts->async_transfer,
+ .async_compute = opts->async_compute,
+ .queue_count = opts->queue_count,
+#if HAVE_VULKAN_INTEROP
+ .extra_queues = VK_QUEUE_VIDEO_DECODE_BIT_KHR,
+ .opt_extensions = opt_extensions,
+ .num_opt_extensions = MP_ARRAY_SIZE(opt_extensions),
+#endif
+ .features = &features,
+ .device_name = is_uuid ? NULL : opts->device,
+ };
+ if (is_uuid)
+ av_uuid_copy(device_params.device_uuid, param_uuid);
+
+ return pl_vulkan_create(pllog, &device_params);
+
+}
+
bool ra_vk_ctx_init(struct ra_ctx *ctx, struct mpvk_ctx *vk,
struct ra_vk_ctx_params params,
VkPresentModeKHR preferred_mode)
@@ -162,19 +255,7 @@ bool ra_vk_ctx_init(struct ra_ctx *ctx, struct mpvk_ctx *vk,
p->params = params;
p->opts = mp_get_config_group(p, ctx->global, &vulkan_conf);
- assert(vk->ctx);
- assert(vk->vkinst);
- vk->vulkan = pl_vulkan_create(vk->ctx, &(struct pl_vulkan_params) {
- .instance = vk->vkinst->instance,
- .surface = vk->surface,
- .async_transfer = p->opts->async_transfer,
- .async_compute = p->opts->async_compute,
- .queue_count = p->opts->queue_count,
- .device_name = p->opts->device,
-#if PL_API_VER >= 24
- .disable_events = p->opts->disable_events,
-#endif
- });
+ vk->vulkan = mppl_create_vulkan(p->opts, vk->vkinst, vk->pllog, vk->surface);
if (!vk->vulkan)
goto error;
@@ -188,18 +269,16 @@ bool ra_vk_ctx_init(struct ra_ctx *ctx, struct mpvk_ctx *vk,
.surface = vk->surface,
.present_mode = preferred_mode,
.swapchain_depth = ctx->vo->opts->swapchain_depth,
-#if PL_API_VER >= 29
// mpv already handles resize events, so gracefully allow suboptimal
// swapchains to exist in order to make resizing even smoother
.allow_suboptimal = true,
-#endif
};
if (p->opts->swap_mode >= 0) // user override
pl_params.present_mode = p->opts->swap_mode;
- p->swapchain = pl_vulkan_create_swapchain(vk->vulkan, &pl_params);
- if (!p->swapchain)
+ vk->swapchain = pl_vulkan_create_swapchain(vk->vulkan, &pl_params);
+ if (!vk->swapchain)
goto error;
return true;
@@ -213,28 +292,41 @@ bool ra_vk_ctx_resize(struct ra_ctx *ctx, int width, int height)
{
struct priv *p = ctx->swapchain->priv;
- bool ok = pl_swapchain_resize(p->swapchain, &width, &height);
+ bool ok = pl_swapchain_resize(p->vk->swapchain, &width, &height);
ctx->vo->dwidth = width;
ctx->vo->dheight = height;
return ok;
}
-static int color_depth(struct ra_swapchain *sw)
+char *ra_vk_ctx_get_device_name(struct ra_ctx *ctx)
{
- return 0; // TODO: implement this somehow?
+ /*
+ * This implementation is a bit odd because it has to work even if the
+ * ctx hasn't been initialised yet. A context implementation may need access
+ * to the device name before it can fully initialise the ctx.
+ */
+ struct vulkan_opts *opts = mp_get_config_group(NULL, ctx->global,
+ &vulkan_conf);
+ char *device_name = talloc_strdup(NULL, opts->device);
+ talloc_free(opts);
+ return device_name;
}
static bool start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo)
{
struct priv *p = sw->priv;
struct pl_swapchain_frame frame;
- bool start = true;
- if (p->params.start_frame)
- start = p->params.start_frame(sw->ctx);
- if (!start)
- return false;
- if (!pl_swapchain_start_frame(p->swapchain, &frame))
+
+ bool visible = true;
+ if (p->params.check_visible)
+ visible = p->params.check_visible(sw->ctx);
+
+ // If out_fbo is NULL, this was called from vo_gpu_next. Bail out.
+ if (out_fbo == NULL || !visible)
+ return visible;
+
+ if (!pl_swapchain_start_frame(p->vk->swapchain, &frame))
return false;
if (!mppl_wrap_tex(sw->ctx->ra, frame.fbo, &p->proxy_tex))
return false;
@@ -250,13 +342,13 @@ static bool start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo)
static bool submit_frame(struct ra_swapchain *sw, const struct vo_frame *frame)
{
struct priv *p = sw->priv;
- return pl_swapchain_submit_frame(p->swapchain);
+ return pl_swapchain_submit_frame(p->vk->swapchain);
}
static void swap_buffers(struct ra_swapchain *sw)
{
struct priv *p = sw->priv;
- pl_swapchain_swap_buffers(p->swapchain);
+ pl_swapchain_swap_buffers(p->vk->swapchain);
if (p->params.swap_buffers)
p->params.swap_buffers(sw->ctx);
}
@@ -270,7 +362,6 @@ static void get_vsync(struct ra_swapchain *sw,
}
static const struct ra_swapchain_fns vulkan_swapchain = {
- .color_depth = color_depth,
.start_frame = start_frame,
.submit_frame = submit_frame,
.swap_buffers = swap_buffers,
diff --git a/video/out/vulkan/context.h b/video/out/vulkan/context.h
index 6ae64bb0f5..32f08bf2d8 100644
--- a/video/out/vulkan/context.h
+++ b/video/out/vulkan/context.h
@@ -7,8 +7,9 @@ struct ra_vk_ctx_params {
// See ra_swapchain_fns.get_vsync.
void (*get_vsync)(struct ra_ctx *ctx, struct vo_vsync_info *info);
- // In case something special needs to be done when starting a frame.
- bool (*start_frame)(struct ra_ctx *ctx);
+ // For special contexts (i.e. wayland) that want to check visibility
+ // before drawing a frame.
+ bool (*check_visible)(struct ra_ctx *ctx);
// In case something special needs to be done on the buffer swap.
void (*swap_buffers)(struct ra_ctx *ctx);
@@ -20,8 +21,17 @@ bool ra_vk_ctx_init(struct ra_ctx *ctx, struct mpvk_ctx *vk,
struct ra_vk_ctx_params params,
VkPresentModeKHR preferred_mode);
+// Helper for initializing mpvk_ctx->vulkan
+pl_vulkan mppl_create_vulkan(struct vulkan_opts *opts,
+ pl_vk_inst vkinst,
+ pl_log pllog,
+ VkSurfaceKHR surface);
+
// Handles a resize request, and updates ctx->vo->dwidth/dheight
bool ra_vk_ctx_resize(struct ra_ctx *ctx, int width, int height);
// May be called on a ra_ctx of any type.
struct mpvk_ctx *ra_vk_ctx_get(struct ra_ctx *ctx);
+
+// Get the user requested Vulkan device name.
+char *ra_vk_ctx_get_device_name(struct ra_ctx *ctx);
diff --git a/video/out/vulkan/context_android.c b/video/out/vulkan/context_android.c
index ddab3917f1..d668bb4b94 100644
--- a/video/out/vulkan/context_android.c
+++ b/video/out/vulkan/context_android.c
@@ -89,6 +89,7 @@ static int android_control(struct ra_ctx *ctx, int *events, int request, void *a
const struct ra_ctx_fns ra_ctx_vulkan_android = {
.type = "vulkan",
.name = "androidvk",
+ .description = "Android/Vulkan",
.reconfig = android_reconfig,
.control = android_control,
.init = android_init,
diff --git a/video/out/vulkan/context_display.c b/video/out/vulkan/context_display.c
new file mode 100644
index 0000000000..7e3bc8ea2f
--- /dev/null
+++ b/video/out/vulkan/context_display.c
@@ -0,0 +1,496 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "common/common.h"
+#include "context.h"
+#include "options/m_config.h"
+#include "utils.h"
+#include "video/out/placebo/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;
+ int plane;
+};
+
+struct mode_selector {
+ // Indexes of selected display/mode/plane.
+ int display_idx;
+ int mode_idx;
+ int plane_idx;
+
+ // Must be freed with talloc_free
+ VkDisplayModePropertiesKHR *out_mode_props;
+};
+
+/**
+ * If a selector is passed, verify that it is valid and return the matching
+ * mode properties. If null is passed, walk all modes and print them out.
+ */
+static bool walk_display_properties(struct mp_log *log,
+ int msgl_err,
+ VkPhysicalDevice device,
+ struct mode_selector *selector) {
+ bool ret = false;
+ VkResult res;
+
+ int msgl_info = selector ? MSGL_TRACE : MSGL_INFO;
+
+ // Use a dummy as parent for all other allocations.
+ void *tmp = talloc_new(NULL);
+
+ VkPhysicalDeviceProperties prop;
+ vkGetPhysicalDeviceProperties(device, &prop);
+ mp_msg(log, msgl_info, " '%s' (GPU ID %x:%x)\n", prop.deviceName,
+ (unsigned)prop.vendorID, (unsigned)prop.deviceID);
+
+ // Count displays. This must be done before enumerating planes with the
+ // Intel driver, or it will not enumerate any planes. WTF.
+ int num_displays = 0;
+ vkGetPhysicalDeviceDisplayPropertiesKHR(device, &num_displays, NULL);
+ if (!num_displays) {
+ mp_msg(log, msgl_info, " No available displays for device.\n");
+ goto done;
+ }
+ if (selector && selector->display_idx + 1 > num_displays) {
+ mp_msg(log, msgl_err, "Selected display (%d) not present.\n",
+ selector->display_idx);
+ goto done;
+ }
+
+ // Enumerate Planes
+ int num_planes = 0;
+ vkGetPhysicalDeviceDisplayPlanePropertiesKHR(device, &num_planes, NULL);
+ if (!num_planes) {
+ mp_msg(log, msgl_info, " No available planes for device.\n");
+ goto done;
+ }
+ if (selector && selector->plane_idx + 1 > num_planes) {
+ mp_msg(log, msgl_err, "Selected plane (%d) not present.\n",
+ selector->plane_idx);
+ goto done;
+ }
+
+ VkDisplayPlanePropertiesKHR *planes =
+ talloc_array(tmp, VkDisplayPlanePropertiesKHR, num_planes);
+ res = vkGetPhysicalDeviceDisplayPlanePropertiesKHR(device, &num_planes,
+ planes);
+ if (res != VK_SUCCESS) {
+ mp_msg(log, msgl_err, " Failed enumerating planes\n");
+ goto done;
+ }
+
+ // Allocate zeroed arrays so that planes with no displays have a null entry.
+ VkDisplayKHR **planes_to_displays =
+ talloc_zero_array(tmp, VkDisplayKHR *, num_planes);
+ for (int j = 0; j < num_planes; j++) {
+ int num_displays_for_plane = 0;
+ vkGetDisplayPlaneSupportedDisplaysKHR(device, j,
+ &num_displays_for_plane, NULL);
+ if (!num_displays_for_plane)
+ continue;
+
+ // Null terminated array
+ VkDisplayKHR *displays =
+ talloc_zero_array(planes_to_displays, VkDisplayKHR,
+ num_displays_for_plane + 1);
+ res = vkGetDisplayPlaneSupportedDisplaysKHR(device, j,
+ &num_displays_for_plane,
+ displays);
+ if (res != VK_SUCCESS) {
+ mp_msg(log, msgl_err, " Failed enumerating plane displays\n");
+ continue;
+ }
+ planes_to_displays[j] = displays;
+ }
+
+ // Enumerate Displays and Modes
+ VkDisplayPropertiesKHR *props =
+ talloc_array(tmp, VkDisplayPropertiesKHR, num_displays);
+ res = vkGetPhysicalDeviceDisplayPropertiesKHR(device, &num_displays, props);
+ if (res != VK_SUCCESS) {
+ mp_msg(log, msgl_err, " Failed enumerating display properties\n");
+ goto done;
+ }
+
+ for (int j = 0; j < num_displays; j++) {
+ if (selector && selector->display_idx != j)
+ continue;
+
+ mp_msg(log, msgl_info, " Display %d: '%s' (%dx%d)\n",
+ j,
+ props[j].displayName,
+ props[j].physicalResolution.width,
+ props[j].physicalResolution.height);
+
+ VkDisplayKHR display = props[j].display;
+
+ mp_msg(log, msgl_info, " Modes:\n");
+
+ int num_modes = 0;
+ vkGetDisplayModePropertiesKHR(device, display, &num_modes, NULL);
+ if (!num_modes) {
+ mp_msg(log, msgl_info, " No available modes for display.\n");
+ continue;
+ }
+ if (selector && selector->mode_idx + 1 > num_modes) {
+ mp_msg(log, msgl_err, "Selected mode (%d) not present.\n",
+ selector->mode_idx);
+ goto done;
+ }
+
+ VkDisplayModePropertiesKHR *modes =
+ talloc_array(tmp, VkDisplayModePropertiesKHR, num_modes);
+ res = vkGetDisplayModePropertiesKHR(device, display, &num_modes, modes);
+ if (res != VK_SUCCESS) {
+ mp_msg(log, msgl_err, " Failed enumerating display modes\n");
+ continue;
+ }
+
+ for (int k = 0; k < num_modes; k++) {
+ if (selector && selector->mode_idx != k)
+ continue;
+
+ mp_msg(log, msgl_info, " Mode %02d: %dx%d (%02d.%03d Hz)\n", k,
+ modes[k].parameters.visibleRegion.width,
+ modes[k].parameters.visibleRegion.height,
+ modes[k].parameters.refreshRate / 1000,
+ modes[k].parameters.refreshRate % 1000);
+
+ if (selector)
+ selector->out_mode_props = talloc_dup(NULL, &modes[k]);
+ }
+
+ int found_plane = -1;
+ mp_msg(log, msgl_info, " Planes:\n");
+ for (int k = 0; k < num_planes; k++) {
+ VkDisplayKHR *displays = planes_to_displays[k];
+ if (!displays) {
+ // This plane is not connected to any displays.
+ continue;
+ }
+ for (int d = 0; displays[d]; d++) {
+ if (displays[d] == display) {
+ if (selector && selector->plane_idx != k)
+ continue;
+
+ mp_msg(log, msgl_info, " Plane: %d\n", k);
+ found_plane = k;
+ }
+ }
+ }
+ if (selector && selector->plane_idx != found_plane) {
+ mp_msg(log, msgl_err,
+ "Selected plane (%d) not available on selected display.\n",
+ selector->plane_idx);
+ goto done;
+ }
+ }
+ ret = true;
+done:
+ talloc_free(tmp);
+ return ret;
+}
+
+static int print_display_info(struct mp_log *log, const struct m_option *opt,
+ struct bstr name)
+{
+ void *ta_ctx = talloc_new(NULL);
+ pl_log pllog = mppl_log_create(ta_ctx, log);
+ if (!pllog)
+ goto done;
+
+ // Create a dummy instance to list the resources
+ mppl_log_set_probing(pllog, true);
+ pl_vk_inst inst = pl_vk_inst_create(pllog, pl_vk_inst_params(
+ .extensions = (const char *[]){
+ VK_KHR_DISPLAY_EXTENSION_NAME,
+ },
+ .num_extensions = 1,
+ ));
+ mppl_log_set_probing(pllog, false);
+ if (!inst) {
+ mp_warn(log, "Unable to create Vulkan instance.\n");
+ goto done;
+ }
+
+ uint32_t num_devices = 0;
+ VkResult res = vkEnumeratePhysicalDevices(inst->instance, &num_devices, NULL);
+ if (res != VK_SUCCESS || !num_devices) {
+ mp_info(log, "No Vulkan devices detected.\n");
+ goto done;
+ }
+
+ VkPhysicalDevice *devices = talloc_array(ta_ctx, VkPhysicalDevice, num_devices);
+ res = vkEnumeratePhysicalDevices(inst->instance, &num_devices, devices);
+ if (res != VK_SUCCESS) {
+ mp_warn(log, "Failed enumerating physical devices.\n");
+ goto done;
+ }
+
+ mp_info(log, "Vulkan Devices:\n");
+ for (int i = 0; i < num_devices; i++) {
+ walk_display_properties(log, MSGL_WARN, devices[i], NULL);
+ }
+
+done:
+ pl_vk_inst_destroy(&inst);
+ pl_log_destroy(&pllog);
+ talloc_free(ta_ctx);
+ return M_OPT_EXIT;
+}
+
+#define OPT_BASE_STRUCT struct vulkan_display_opts
+const struct m_sub_options vulkan_display_conf = {
+ .opts = (const struct m_option[]) {
+ {"vulkan-display-display", OPT_INT(display),
+ .help = print_display_info,
+ },
+ {"vulkan-display-mode", OPT_INT(mode),
+ .help = print_display_info,
+ },
+ {"vulkan-display-plane", OPT_INT(plane),
+ .help = print_display_info,
+ },
+ {0}
+ },
+ .size = sizeof(struct vulkan_display_opts),
+ .defaults = &(struct vulkan_display_opts) {0},
+};
+
+struct priv {
+ struct mpvk_ctx vk;
+ 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",
+ mp_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)
+{
+ struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
+ struct mpvk_ctx *vk = &p->vk;
+ int msgl = ctx->opts.probing ? MSGL_V : MSGL_ERR;
+ VkResult res;
+ bool ret = false;
+
+ VkDisplayModePropertiesKHR *mode = NULL;
+
+ p->opts = mp_get_config_group(p, ctx->global, &vulkan_display_conf);
+ int display_idx = p->opts->display;
+ int mode_idx = p->opts->mode;
+ int plane_idx = p->opts->plane;
+
+ if (!mpvk_init(vk, ctx, VK_KHR_DISPLAY_EXTENSION_NAME))
+ goto error;
+
+ char *device_name = ra_vk_ctx_get_device_name(ctx);
+ struct pl_vulkan_device_params vulkan_params = {
+ .instance = vk->vkinst->instance,
+ .device_name = device_name,
+ };
+ VkPhysicalDevice device = pl_vulkan_choose_device(vk->pllog, &vulkan_params);
+ talloc_free(device_name);
+ if (!device) {
+ MP_MSG(ctx, msgl, "Failed to open physical device.\n");
+ 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,
+ .plane_idx = plane_idx,
+
+ };
+ if (!walk_display_properties(ctx->log, msgl, device, &selector))
+ goto error;
+ mode = selector.out_mode_props;
+
+ VkDisplaySurfaceCreateInfoKHR xinfo = {
+ .sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR,
+ .displayMode = mode->displayMode,
+ .imageExtent = mode->parameters.visibleRegion,
+ .planeIndex = plane_idx,
+ .transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
+ .alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR,
+ };
+
+ res = vkCreateDisplayPlaneSurfaceKHR(vk->vkinst->instance, &xinfo, NULL,
+ &vk->surface);
+ if (res != VK_SUCCESS) {
+ MP_MSG(ctx, msgl, "Failed creating Display surface\n");
+ goto error;
+ }
+
+ p->width = mode->parameters.visibleRegion.width;
+ p->height = mode->parameters.visibleRegion.height;
+
+ struct ra_vk_ctx_params params = {0};
+ 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:
+ talloc_free(mode);
+ return ret;
+
+error:
+ display_uninit(ctx);
+ goto done;
+}
+
+static bool display_reconfig(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv;
+ return ra_vk_ctx_resize(ctx, p->width, p->height);
+}
+
+static int display_control(struct ra_ctx *ctx, int *events, int request, void *arg)
+{
+ return VO_NOTIMPL;
+}
+
+static void display_wakeup(struct ra_ctx *ctx)
+{
+ // TODO
+}
+
+static void display_wait_events(struct ra_ctx *ctx, int64_t until_time_ns)
+{
+ // TODO
+}
+
+const struct ra_ctx_fns ra_ctx_vulkan_display = {
+ .type = "vulkan",
+ .name = "displayvk",
+ .description = "VK_KHR_display",
+ .reconfig = display_reconfig,
+ .control = display_control,
+ .wakeup = display_wakeup,
+ .wait_events = display_wait_events,
+ .init = display_init,
+ .uninit = display_uninit,
+};
diff --git a/video/out/vulkan/context_mac.m b/video/out/vulkan/context_mac.m
new file mode 100644
index 0000000000..66093dd110
--- /dev/null
+++ b/video/out/vulkan/context_mac.m
@@ -0,0 +1,136 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.</