19 #define VK_NO_PROTOTYPES
20 #define VK_ENABLE_BETA_EXTENSIONS
25 #if (SDL_VERSION_ATLEAST(2, 0, 6) && CONFIG_LIBPLACEBO)
27 #include <libplacebo/config.h>
28 #define HAVE_VULKAN_RENDERER (PL_API_VER >= 278)
30 #define HAVE_VULKAN_RENDERER 0
33 #if HAVE_VULKAN_RENDERER
35 #if defined(_WIN32) && !defined(VK_USE_PLATFORM_WIN32_KHR)
36 #define VK_USE_PLATFORM_WIN32_KHR
39 #include <libplacebo/vulkan.h>
40 #include <libplacebo/utils/frame_queue.h>
41 #include <libplacebo/utils/libav.h>
42 #include <SDL_vulkan.h>
63 #if HAVE_VULKAN_RENDERER
65 typedef struct RendererContext {
69 pl_vk_inst placebo_instance;
70 pl_vulkan placebo_vulkan;
71 pl_swapchain swapchain;
72 VkSurfaceKHR vk_surface;
83 PFN_vkGetInstanceProcAddr get_proc_addr;
90 static void vk_log_cb(
void *log_priv,
enum pl_log_level
level,
93 static const int level_map[] = {
110 VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
111 VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,
112 VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
113 VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME,
114 VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME,
115 VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME,
116 VK_KHR_COOPERATIVE_MATRIX_EXTENSION_NAME,
119 VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
120 VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME,
121 VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
122 VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
123 VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME,
125 VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,
126 VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME,
130 VK_KHR_VIDEO_QUEUE_EXTENSION_NAME,
131 VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME,
132 VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME,
133 VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME,
134 "VK_MESA_video_decode_av1",
144 static void hwctx_lock_queue(
void *priv, uint32_t qf, uint32_t qidx)
151 static void hwctx_unlock_queue(
void *priv, uint32_t qf, uint32_t qidx)
158 static int add_instance_extension(
const char **ext,
unsigned num_ext,
162 const char *inst_ext_key =
"instance_extensions";
165 char *ext_list =
NULL;
169 for (
int i = 0;
i < num_ext;
i++) {
189 static int add_device_extension(
const AVDictionary *opt,
192 const char *dev_ext_key =
"device_extensions";
195 char *ext_list =
NULL;
199 av_bprintf(&buf,
"%s", VK_KHR_SWAPCHAIN_EXTENSION_NAME);
200 for (
int i = 0;
i < pl_vulkan_num_recommended_extensions;
i++)
201 av_bprintf(&buf,
"+%s", pl_vulkan_recommended_extensions[
i]);
213 static const char *select_device(
const AVDictionary *opt)
224 const char **ext,
unsigned num_ext,
227 RendererContext *
ctx = (RendererContext *)
renderer;
233 ret = add_instance_extension(ext, num_ext, opt, &dict);
236 ret = add_device_extension(opt, &dict);
243 select_device(opt), dict, 0);
255 "hwdevice and SDL use different get_proc_addr. "
256 "Try -vulkan_params create_by_placebo=1\n");
263 struct pl_vulkan_import_params import_params = {
264 .instance = hwctx->
inst,
271 .lock_queue = hwctx_lock_queue,
272 .unlock_queue = hwctx_unlock_queue,
275 .index = VK_QUEUE_FAMILY_IGNORED,
279 .index = VK_QUEUE_FAMILY_IGNORED,
283 .index = VK_QUEUE_FAMILY_IGNORED,
287 for (
int i = 0;
i < hwctx->
nb_qf;
i++) {
290 if (qf->
flags & VK_QUEUE_GRAPHICS_BIT) {
291 import_params.queue_graphics.index = qf->
idx;
292 import_params.queue_graphics.count = qf->
num;
294 if (qf->
flags & VK_QUEUE_COMPUTE_BIT) {
295 import_params.queue_compute.index = qf->
idx;
296 import_params.queue_compute.count = qf->
num;
298 if (qf->
flags & VK_QUEUE_TRANSFER_BIT) {
299 import_params.queue_transfer.index = qf->
idx;
300 import_params.queue_transfer.count = qf->
num;
304 ctx->placebo_vulkan = pl_vulkan_import(
ctx->vk_log, &import_params);
305 if (!
ctx->placebo_vulkan)
312 uint32_t queue_family, uint32_t
index)
315 pl_vulkan vk =
ctx->placebo_vulkan;
316 vk->lock_queue(vk, queue_family,
index);
320 uint32_t queue_family,
324 pl_vulkan vk =
ctx->placebo_vulkan;
325 vk->unlock_queue(vk, queue_family,
index);
330 RendererContext *
ctx = (RendererContext *)
renderer;
331 VkQueueFamilyProperties *queue_family_prop =
NULL;
332 uint32_t num_queue_family_prop = 0;
333 PFN_vkGetPhysicalDeviceQueueFamilyProperties get_queue_family_prop;
334 PFN_vkGetInstanceProcAddr get_proc_addr =
ctx->get_proc_addr;
338 get_queue_family_prop = (PFN_vkGetPhysicalDeviceQueueFamilyProperties)
339 get_proc_addr(
ctx->placebo_instance->instance,
340 "vkGetPhysicalDeviceQueueFamilyProperties");
341 get_queue_family_prop(
ctx->placebo_vulkan->phys_device,
342 &num_queue_family_prop,
NULL);
343 if (!num_queue_family_prop)
346 queue_family_prop =
av_calloc(num_queue_family_prop,
347 sizeof(*queue_family_prop));
348 if (!queue_family_prop)
351 get_queue_family_prop(
ctx->placebo_vulkan->phys_device,
352 &num_queue_family_prop,
355 for (
int i = 0;
i < num_queue_family_prop;
i++) {
356 if (queue_family_prop[
i].queueFlags & VK_QUEUE_VIDEO_DECODE_BIT_KHR) {
358 *count = queue_family_prop[
i].queueCount;
368 const char **ext,
unsigned num_ext,
371 RendererContext *
ctx = (RendererContext *)
renderer;
378 ctx->get_proc_addr = SDL_Vulkan_GetVkGetInstanceProcAddr();
380 ctx->placebo_instance = pl_vk_inst_create(
ctx->vk_log, pl_vk_inst_params(
381 .get_proc_addr =
ctx->get_proc_addr,
382 .
debug = enable_debug(opt),
384 .num_extensions = num_ext
386 if (!
ctx->placebo_instance) {
389 ctx->inst =
ctx->placebo_instance->instance;
391 ctx->placebo_vulkan = pl_vulkan_create(
ctx->vk_log, pl_vulkan_params(
392 .instance =
ctx->placebo_instance->instance,
393 .get_proc_addr =
ctx->placebo_instance->get_proc_addr,
394 .surface =
ctx->vk_surface,
395 .allow_software =
false,
398 .extra_queues = VK_QUEUE_VIDEO_DECODE_BIT_KHR,
399 .device_name = select_device(opt),
401 if (!
ctx->placebo_vulkan)
404 if (!
ctx->hw_device_ref) {
411 vk_dev_ctx = device_ctx->
hwctx;
417 vk_dev_ctx->
inst =
ctx->placebo_instance->instance;
418 vk_dev_ctx->
phys_dev =
ctx->placebo_vulkan->phys_device;
419 vk_dev_ctx->
act_dev =
ctx->placebo_vulkan->device;
431 .
idx =
ctx->placebo_vulkan->queue_graphics.index,
432 .num =
ctx->placebo_vulkan->queue_graphics.count,
433 .
flags = VK_QUEUE_GRAPHICS_BIT,
437 .
idx =
ctx->placebo_vulkan->queue_transfer.index,
438 .num =
ctx->placebo_vulkan->queue_transfer.count,
439 .
flags = VK_QUEUE_TRANSFER_BIT,
443 .
idx =
ctx->placebo_vulkan->queue_compute.index,
444 .num =
ctx->placebo_vulkan->queue_compute.count,
445 .
flags = VK_QUEUE_COMPUTE_BIT,
456 .flags = VK_QUEUE_VIDEO_DECODE_BIT_KHR,
460 vk_dev_ctx->
nb_qf = nb_qf;
472 unsigned num_ext = 0;
473 const char **ext =
NULL;
475 struct pl_log_params vk_log_params = {
477 .log_level = PL_LOG_DEBUG,
480 RendererContext *
ctx = (RendererContext *)
renderer;
483 ctx->vk_log = pl_log_create(PL_API_VER, &vk_log_params);
485 if (!SDL_Vulkan_GetInstanceExtensions(
window, &num_ext,
NULL)) {
497 SDL_Vulkan_GetInstanceExtensions(
window, &num_ext, ext);
501 ret = create_vk_by_placebo(
renderer, ext, num_ext, opt);
503 ret = create_vk_by_hwcontext(
renderer, ext, num_ext, opt);
507 if (!SDL_Vulkan_CreateSurface(
window,
ctx->inst, &
ctx->vk_surface)) {
512 ctx->swapchain = pl_vulkan_create_swapchain(
514 pl_vulkan_swapchain_params(
515 .surface =
ctx->vk_surface,
516 .present_mode = VK_PRESENT_MODE_FIFO_KHR));
517 if (!
ctx->swapchain) {
522 SDL_Vulkan_GetDrawableSize(
window, &
w, &
h);
523 pl_swapchain_resize(
ctx->swapchain, &
w, &
h);
525 ctx->renderer = pl_renderer_create(
ctx->vk_log,
ctx->placebo_vulkan->gpu);
526 if (!
ctx->renderer) {
532 if (!
ctx->vk_frame) {
546 RendererContext *
ctx = (RendererContext *)
renderer;
548 *dev =
ctx->hw_device_ref;
554 RendererContext *
ctx = (RendererContext *)
renderer;
556 frame->hw_frames_ctx->data;
561 if (
ctx->hw_frame_ref) {
564 if (hw_frame->width ==
frame->width &&
565 hw_frame->height ==
frame->height &&
566 hw_frame->sw_format == src_hw_frame->
sw_format)
572 if (!
ctx->constraints) {
575 if (!
ctx->constraints)
581 if ((
ctx->constraints->max_width &&
582 ctx->constraints->max_width <
frame->width) ||
583 (
ctx->constraints->max_height &&
584 ctx->constraints->max_height <
frame->height) ||
585 (
ctx->constraints->min_width &&
586 ctx->constraints->min_width >
frame->width) ||
587 (
ctx->constraints->min_height &&
588 ctx->constraints->min_height >
frame->height))
591 if (
ctx->constraints->valid_sw_formats) {
594 if (*sw_formats == src_hw_frame->
sw_format)
603 if (!
ctx->hw_frame_ref)
608 hw_frame->sw_format = src_hw_frame->
sw_format;
609 hw_frame->width =
frame->width;
610 hw_frame->height =
frame->height;
613 vk_frame_ctx = hw_frame->hwctx;
626 &
ctx->transfer_formats, 0);
631 static inline int check_hw_transfer(RendererContext *
ctx,
AVFrame *
frame)
633 if (!
ctx->hw_frame_ref || !
ctx->transfer_formats)
637 if (
ctx->transfer_formats[
i] ==
frame->format)
643 static inline int move_to_output_frame(RendererContext *
ctx,
AVFrame *
frame)
655 RendererContext *
ctx = (RendererContext *)
renderer;
658 if (use_hw_frame && !
ctx->hw_frame_ref)
669 return move_to_output_frame(
ctx,
frame);
678 RendererContext *
ctx = (RendererContext *)
renderer;
681 if (use_hw_frame && !check_hw_transfer(
ctx,
frame))
689 return move_to_output_frame(
ctx,
frame);
701 if (!
frame->hw_frames_ctx)
711 for (
int use_hw = 1; use_hw >=0; use_hw--) {
730 struct pl_swapchain_frame swap_frame = {0};
731 struct pl_frame pl_frame = {0};
732 struct pl_frame target = {0};
733 RendererContext *
ctx = (RendererContext *)
renderer;
735 struct pl_color_space hint = {0};
741 if (!pl_map_avframe_ex(
ctx->placebo_vulkan->gpu, &pl_frame, pl_avframe_params(
748 pl_color_space_from_avframe(&hint,
frame);
749 pl_swapchain_colorspace_hint(
ctx->swapchain, &hint);
750 if (!pl_swapchain_start_frame(
ctx->swapchain, &swap_frame)) {
756 pl_frame_from_swapchain(&target, &swap_frame);
757 if (!pl_render_image(
ctx->renderer, &pl_frame, &target,
758 &pl_render_default_params)) {
764 if (!pl_swapchain_submit_frame(
ctx->swapchain)) {
769 pl_swapchain_swap_buffers(
ctx->swapchain);
772 pl_unmap_avframe(
ctx->placebo_vulkan->gpu, &pl_frame);
778 RendererContext *
ctx = (RendererContext *)
renderer;
787 RendererContext *
ctx = (RendererContext *)
renderer;
788 PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR;
795 if (
ctx->placebo_vulkan) {
797 pl_tex_destroy(
ctx->placebo_vulkan->gpu, &
ctx->tex[
i]);
798 pl_renderer_destroy(&
ctx->renderer);
799 pl_swapchain_destroy(&
ctx->swapchain);
800 pl_vulkan_destroy(&
ctx->placebo_vulkan);
803 if (
ctx->vk_surface) {
804 vkDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR)
805 ctx->get_proc_addr(
ctx->inst,
"vkDestroySurfaceKHR");
806 vkDestroySurfaceKHR(
ctx->inst,
ctx->vk_surface,
NULL);
807 ctx->vk_surface = VK_NULL_HANDLE;
811 pl_vk_inst_destroy(&
ctx->placebo_instance);
813 pl_log_destroy(&
ctx->vk_log);
816 static const AVClass vulkan_renderer_class = {
831 renderer->class = &vulkan_renderer_class;