Go to the documentation of this file.
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>
64 #if HAVE_VULKAN_RENDERER
66 typedef struct RendererContext {
70 pl_vk_inst placebo_instance;
71 pl_vulkan placebo_vulkan;
72 pl_swapchain swapchain;
73 VkSurfaceKHR vk_surface;
84 PFN_vkGetInstanceProcAddr get_proc_addr;
91 static void vk_log_cb(
void *log_priv,
enum pl_log_level
level,
94 static const int level_map[] = {
115 static void hwctx_lock_queue(
void *priv, uint32_t qf, uint32_t qidx)
119 #if FF_API_VULKAN_SYNC_QUEUES
126 static void hwctx_unlock_queue(
void *priv, uint32_t qf, uint32_t qidx)
130 #if FF_API_VULKAN_SYNC_QUEUES
137 static int add_instance_extension(
const char **ext,
unsigned num_ext,
141 const char *inst_ext_key =
"instance_extensions";
144 char *ext_list =
NULL;
148 for (
int i = 0;
i < num_ext;
i++) {
168 static int add_device_extension(
const AVDictionary *opt,
171 const char *dev_ext_key =
"device_extensions";
174 char *ext_list =
NULL;
178 av_bprintf(&buf,
"%s", VK_KHR_SWAPCHAIN_EXTENSION_NAME);
179 for (
int i = 0;
i < pl_vulkan_num_recommended_extensions;
i++)
180 av_bprintf(&buf,
"+%s", pl_vulkan_recommended_extensions[
i]);
192 static const char *select_device(
const AVDictionary *opt)
203 const char **ext,
unsigned num_ext,
206 RendererContext *
ctx = (RendererContext *)
renderer;
212 ret = add_instance_extension(ext, num_ext, opt, &dict);
215 ret = add_device_extension(opt, &dict);
222 select_device(opt), dict, 0);
234 "hwdevice and SDL use different get_proc_addr. "
235 "Try -vulkan_params create_by_placebo=1\n");
242 struct pl_vulkan_import_params import_params = {
243 .instance = hwctx->
inst,
250 .lock_queue = hwctx_lock_queue,
251 .unlock_queue = hwctx_unlock_queue,
254 .index = VK_QUEUE_FAMILY_IGNORED,
258 .index = VK_QUEUE_FAMILY_IGNORED,
262 .index = VK_QUEUE_FAMILY_IGNORED,
266 for (
int i = 0;
i < hwctx->
nb_qf;
i++) {
269 if (qf->
flags & VK_QUEUE_GRAPHICS_BIT) {
270 import_params.queue_graphics.index = qf->
idx;
271 import_params.queue_graphics.count = qf->
num;
273 if (qf->
flags & VK_QUEUE_COMPUTE_BIT) {
274 import_params.queue_compute.index = qf->
idx;
275 import_params.queue_compute.count = qf->
num;
277 if (qf->
flags & VK_QUEUE_TRANSFER_BIT) {
278 import_params.queue_transfer.index = qf->
idx;
279 import_params.queue_transfer.count = qf->
num;
283 ctx->placebo_vulkan = pl_vulkan_import(
ctx->vk_log, &import_params);
284 if (!
ctx->placebo_vulkan)
291 uint32_t queue_family, uint32_t
index)
294 pl_vulkan vk =
ctx->placebo_vulkan;
295 #if FF_API_VULKAN_SYNC_QUEUES
297 vk->lock_queue(vk, queue_family,
index);
303 uint32_t queue_family,
307 pl_vulkan vk =
ctx->placebo_vulkan;
308 #if FF_API_VULKAN_SYNC_QUEUES
310 vk->unlock_queue(vk, queue_family,
index);
317 RendererContext *
ctx = (RendererContext *)
renderer;
318 VkQueueFamilyProperties *queue_family_prop =
NULL;
319 uint32_t num_queue_family_prop = 0;
320 PFN_vkGetPhysicalDeviceQueueFamilyProperties get_queue_family_prop;
321 PFN_vkGetInstanceProcAddr get_proc_addr =
ctx->get_proc_addr;
325 get_queue_family_prop = (PFN_vkGetPhysicalDeviceQueueFamilyProperties)
326 get_proc_addr(
ctx->placebo_instance->instance,
327 "vkGetPhysicalDeviceQueueFamilyProperties");
328 get_queue_family_prop(
ctx->placebo_vulkan->phys_device,
329 &num_queue_family_prop,
NULL);
330 if (!num_queue_family_prop)
333 queue_family_prop =
av_calloc(num_queue_family_prop,
334 sizeof(*queue_family_prop));
335 if (!queue_family_prop)
338 get_queue_family_prop(
ctx->placebo_vulkan->phys_device,
339 &num_queue_family_prop,
342 for (
int i = 0;
i < num_queue_family_prop;
i++) {
343 if (queue_family_prop[
i].queueFlags & VK_QUEUE_VIDEO_DECODE_BIT_KHR) {
345 *count = queue_family_prop[
i].queueCount;
355 const char **ext,
unsigned num_ext,
358 RendererContext *
ctx = (RendererContext *)
renderer;
364 const char **dev_exts;
367 ctx->get_proc_addr = SDL_Vulkan_GetVkGetInstanceProcAddr();
369 ctx->placebo_instance = pl_vk_inst_create(
ctx->vk_log, pl_vk_inst_params(
370 .get_proc_addr =
ctx->get_proc_addr,
371 .
debug = enable_debug(opt),
373 .num_extensions = num_ext
375 if (!
ctx->placebo_instance) {
378 ctx->inst =
ctx->placebo_instance->instance;
384 ctx->placebo_vulkan = pl_vulkan_create(
ctx->vk_log, pl_vulkan_params(
385 .instance =
ctx->placebo_instance->instance,
386 .get_proc_addr =
ctx->placebo_instance->get_proc_addr,
387 .surface =
ctx->vk_surface,
388 .allow_software =
false,
389 .opt_extensions = dev_exts,
390 .num_opt_extensions = num_dev_exts,
391 .extra_queues = VK_QUEUE_VIDEO_DECODE_BIT_KHR,
392 .device_name = select_device(opt),
395 if (!
ctx->placebo_vulkan)
398 if (!
ctx->hw_device_ref) {
405 vk_dev_ctx = device_ctx->
hwctx;
415 vk_dev_ctx->
inst =
ctx->placebo_instance->instance;
416 vk_dev_ctx->
phys_dev =
ctx->placebo_vulkan->phys_device;
417 vk_dev_ctx->
act_dev =
ctx->placebo_vulkan->device;
429 .
idx =
ctx->placebo_vulkan->queue_graphics.index,
430 .num =
ctx->placebo_vulkan->queue_graphics.count,
431 .
flags = VK_QUEUE_GRAPHICS_BIT,
435 .
idx =
ctx->placebo_vulkan->queue_transfer.index,
436 .num =
ctx->placebo_vulkan->queue_transfer.count,
437 .
flags = VK_QUEUE_TRANSFER_BIT,
441 .
idx =
ctx->placebo_vulkan->queue_compute.index,
442 .num =
ctx->placebo_vulkan->queue_compute.count,
443 .
flags = VK_QUEUE_COMPUTE_BIT,
454 .flags = VK_QUEUE_VIDEO_DECODE_BIT_KHR,
458 vk_dev_ctx->
nb_qf = nb_qf;
470 unsigned num_ext = 0;
471 const char **ext =
NULL;
473 struct pl_log_params vk_log_params = {
475 .log_level = PL_LOG_DEBUG,
478 RendererContext *
ctx = (RendererContext *)
renderer;
481 ctx->vk_log = pl_log_create(PL_API_VER, &vk_log_params);
483 if (!SDL_Vulkan_GetInstanceExtensions(
window, &num_ext,
NULL)) {
495 SDL_Vulkan_GetInstanceExtensions(
window, &num_ext, ext);
499 ret = create_vk_by_placebo(
renderer, ext, num_ext, opt);
501 ret = create_vk_by_hwcontext(
renderer, ext, num_ext, opt);
505 if (!SDL_Vulkan_CreateSurface(
window,
ctx->inst, &
ctx->vk_surface)) {
510 ctx->swapchain = pl_vulkan_create_swapchain(
512 pl_vulkan_swapchain_params(
513 .surface =
ctx->vk_surface,
514 .present_mode = VK_PRESENT_MODE_FIFO_KHR));
515 if (!
ctx->swapchain) {
520 SDL_Vulkan_GetDrawableSize(
window, &
w, &
h);
521 pl_swapchain_resize(
ctx->swapchain, &
w, &
h);
523 ctx->renderer = pl_renderer_create(
ctx->vk_log,
ctx->placebo_vulkan->gpu);
524 if (!
ctx->renderer) {
530 if (!
ctx->vk_frame) {
544 RendererContext *
ctx = (RendererContext *)
renderer;
546 *dev =
ctx->hw_device_ref;
552 RendererContext *
ctx = (RendererContext *)
renderer;
554 frame->hw_frames_ctx->data;
559 if (
ctx->hw_frame_ref) {
562 if (hw_frame->width ==
frame->width &&
563 hw_frame->height ==
frame->height &&
564 hw_frame->sw_format == src_hw_frame->
sw_format)
570 if (!
ctx->constraints) {
573 if (!
ctx->constraints)
579 if ((
ctx->constraints->max_width &&
580 ctx->constraints->max_width <
frame->width) ||
581 (
ctx->constraints->max_height &&
582 ctx->constraints->max_height <
frame->height) ||
583 (
ctx->constraints->min_width &&
584 ctx->constraints->min_width >
frame->width) ||
585 (
ctx->constraints->min_height &&
586 ctx->constraints->min_height >
frame->height))
589 if (
ctx->constraints->valid_sw_formats) {
592 if (*sw_formats == src_hw_frame->
sw_format)
601 if (!
ctx->hw_frame_ref)
606 hw_frame->sw_format = src_hw_frame->
sw_format;
607 hw_frame->width =
frame->width;
608 hw_frame->height =
frame->height;
611 vk_frame_ctx = hw_frame->hwctx;
624 &
ctx->transfer_formats, 0);
629 static inline int check_hw_transfer(RendererContext *
ctx,
AVFrame *
frame)
631 if (!
ctx->hw_frame_ref || !
ctx->transfer_formats)
635 if (
ctx->transfer_formats[
i] ==
frame->format)
641 static inline int move_to_output_frame(RendererContext *
ctx,
AVFrame *
frame)
653 RendererContext *
ctx = (RendererContext *)
renderer;
656 if (use_hw_frame && !
ctx->hw_frame_ref)
667 return move_to_output_frame(
ctx,
frame);
676 RendererContext *
ctx = (RendererContext *)
renderer;
679 if (use_hw_frame && !check_hw_transfer(
ctx,
frame))
687 return move_to_output_frame(
ctx,
frame);
699 if (!
frame->hw_frames_ctx)
709 for (
int use_hw = 1; use_hw >=0; use_hw--) {
729 struct pl_swapchain_frame swap_frame = {0};
730 struct pl_frame pl_frame = {0};
731 struct pl_frame target = {0};
732 struct pl_render_params pl_params = pl_render_default_params;
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);
762 pl_params.background = PL_CLEAR_TILES;
766 pl_params.background = PL_CLEAR_COLOR;
767 for (
int i = 0;
i < 3;
i++)
772 pl_frame.repr.alpha = PL_ALPHA_NONE;
776 if (!pl_render_image(
ctx->renderer, &pl_frame, &target, &pl_params)) {
782 if (!pl_swapchain_submit_frame(
ctx->swapchain)) {
787 pl_swapchain_swap_buffers(
ctx->swapchain);
790 pl_unmap_avframe(
ctx->placebo_vulkan->gpu, &pl_frame);
796 RendererContext *
ctx = (RendererContext *)
renderer;
805 RendererContext *
ctx = (RendererContext *)
renderer;
806 PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR;
813 if (
ctx->placebo_vulkan) {
815 pl_tex_destroy(
ctx->placebo_vulkan->gpu, &
ctx->tex[
i]);
816 pl_renderer_destroy(&
ctx->renderer);
817 pl_swapchain_destroy(&
ctx->swapchain);
818 pl_vulkan_destroy(&
ctx->placebo_vulkan);
821 if (
ctx->vk_surface) {
822 vkDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR)
823 ctx->get_proc_addr(
ctx->inst,
"vkDestroySurfaceKHR");
824 vkDestroySurfaceKHR(
ctx->inst,
ctx->vk_surface,
NULL);
825 ctx->vk_surface = VK_NULL_HANDLE;
829 pl_vk_inst_destroy(&
ctx->placebo_instance);
831 pl_log_destroy(&
ctx->vk_log);
834 static const AVClass vulkan_renderer_class = {
849 renderer->class = &vulkan_renderer_class;
void * hwctx
The format-specific data, allocated and freed by libavutil along with this context.
#define VIDEO_BACKGROUND_TILE_SIZE
#define FF_ENABLE_DEPRECATION_WARNINGS
int(* create)(VkRenderer *renderer, SDL_Window *window, AVDictionary *dict)
VkPhysicalDevice phys_dev
Physical device.
#define AV_LOG_WARNING
Something somehow does not look correct.
@ AV_PIX_FMT_CUDA
HW acceleration through CUDA.
AVPixelFormat
Pixel format.
static int convert_frame(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Convert a frame from linear RGB to logspace LAB, and accumulate channel totals for each row Convert f...
@ AV_VK_FRAME_FLAG_DISABLE_MULTIPLANE
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
static void destroy(struct ResampleContext **c)
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
#define AV_LOG_QUIET
Print no output.
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
int av_hwframe_ctx_init(AVBufferRef *ref)
Finalize the context before use.
This structure describes decoded (raw) audio or video data.
PFN_vkGetInstanceProcAddr get_proc_addr
Pointer to a vkGetInstanceProcAddr loading function.
AVBufferRef * av_hwframe_ctx_alloc(AVBufferRef *device_ref_in)
Allocate an AVHWFramesContext tied to a given device context.
void * user_opaque
Arbitrary user data, to be used e.g.
int av_hwframe_map(AVFrame *dst, const AVFrame *src, int flags)
Map a hardware frame.
int vk_renderer_create(VkRenderer *renderer, SDL_Window *window, AVDictionary *opt)
VkInstance inst
Vulkan instance.
AVBufferRef * av_buffer_ref(const AVBufferRef *buf)
Create a new reference to an AVBuffer.
static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src, struct pl_frame *out)
void vk_renderer_destroy(VkRenderer *renderer)
AVHWFramesConstraints * av_hwdevice_get_hwframe_constraints(AVBufferRef *ref, const void *hwconfig)
Get the constraints on HW frames given a device and the HW-specific configuration to be used with tha...
int av_hwdevice_ctx_init(AVBufferRef *ref)
Finalize the device context before use.
@ AV_PIX_FMT_VULKAN
Vulkan hardware images.
const char ** av_vk_get_optional_device_extensions(int *count)
Returns an array of optional Vulkan device extensions that FFmpeg may use if enabled.
attribute_deprecated void(* unlock_queue)(struct AVHWDeviceContext *ctx, uint32_t queue_family, uint32_t index)
Similar to lock_queue(), unlocks a queue.
@ AV_HWDEVICE_TYPE_VULKAN
This struct describes the constraints on hardware frames attached to a given device with a hardware-s...
static SDL_Window * window
attribute_deprecated void(* lock_queue)(struct AVHWDeviceContext *ctx, uint32_t queue_family, uint32_t index)
Locks a queue, preventing other threads from submitting any command buffers to this queue.
Allocated as AVHWFramesContext.hwctx, used to set pool-specific options.
#define AV_BPRINT_SIZE_AUTOMATIC
#define AV_DICT_DONT_STRDUP_VAL
Take ownership of a value that's been allocated with av_malloc() or another memory allocation functio...
This struct aggregates all the (hardware/vendor-specific) "high-level" state, i.e.
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
VkRenderer * vk_get_renderer(void)
#define FF_ARRAY_ELEMS(a)
enum VideoBackgroundType video_background_type
AVBufferRef * av_hwdevice_ctx_alloc(enum AVHWDeviceType type)
Allocate an AVHWDeviceContext for a given hardware type.
AVDictionaryEntry * av_dict_get(const AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int flags)
Get a dictionary entry with matching key.
void av_hwframe_constraints_free(AVHWFramesConstraints **constraints)
Free an AVHWFrameConstraints structure.
int flags
Flags modifying the (de)muxer behaviour.
static int decode_index(SGAVideoContext *s, AVFrame *frame)
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
static AVFormatContext * ctx
int vk_renderer_get_hw_dev(VkRenderer *renderer, AVBufferRef **dev)
static SDL_Renderer * renderer
Main Vulkan context, allocated as AVHWDeviceContext.hwctx.
#define LIBAVUTIL_VERSION_INT
Describe the class of an AVClass context structure.
enum AVPixelFormat sw_format
The pixel format identifying the actual data layout of the hardware frames.
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
void av_buffer_unref(AVBufferRef **buf)
Free a given reference and automatically free the buffer if there are no more references to it.
int nb_enabled_dev_extensions
static struct ResampleContext * create(struct ResampleContext *c, int out_rate, int in_rate, int filter_size, int phase_shift, int linear, double cutoff, enum AVSampleFormat format, enum SwrFilterType filter_type, double kaiser_beta, double precision, int cheby, int exact_rational)
const char * av_default_item_name(void *ptr)
Return the context name.
const char *const * enabled_inst_extensions
Enabled instance extensions.
AVVulkanDeviceQueueFamily qf[64]
Queue families used.
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
#define i(width, name, range_min, range_max)
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
int(* get_hw_dev)(VkRenderer *renderer, AVBufferRef **dev)
#define AVERROR_EXTERNAL
Generic error in an external library.
void av_dict_free(AVDictionary **pm)
Free all the memory allocated for an AVDictionary struct and all keys and values.
#define AV_LOG_INFO
Standard information.
void av_frame_move_ref(AVFrame *dst, AVFrame *src)
Move everything contained in src to dst and reset src.
int vk_renderer_resize(VkRenderer *renderer, int width, int height)
void av_frame_unref(AVFrame *frame)
Unreference all the buffers referenced by frame and reset the frame fields.
void * av_calloc(size_t nmemb, size_t size)
This struct describes a set or pool of "hardware" frames (i.e.
#define AV_LOG_FATAL
Something went wrong and recovery is not possible.
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several the filter must be ready for frames arriving randomly on any input any filter with several inputs will most likely require some kind of queuing mechanism It is perfectly acceptable to have a limited queue and to drop frames when the inputs are too unbalanced request_frame For filters that do not use the this method is called when a frame is wanted on an output For a it should directly call filter_frame on the corresponding output For a if there are queued frames already one of these frames should be pushed If the filter should request a frame on one of its repeatedly until at least one frame has been pushed Return or at least make progress towards producing a frame
void(* destroy)(VkRenderer *renderer)
int av_hwdevice_ctx_create(AVBufferRef **pdevice_ref, enum AVHWDeviceType type, const char *device, AVDictionary *opts, int flags)
Open a device of the specified type and create an AVHWDeviceContext for it.
void av_bprintf(AVBPrint *buf, const char *fmt,...)
int av_hwframe_transfer_data(AVFrame *dst, const AVFrame *src, int flags)
Copy data to or from a hw surface.
int debug
Flags to enable debugging.
int vk_renderer_display(VkRenderer *renderer, AVFrame *frame, RenderParams *render_params)
#define FF_DISABLE_DEPRECATION_WARNINGS
int av_hwframe_transfer_get_formats(AVBufferRef *hwframe_ref, enum AVHWFrameTransferDirection dir, enum AVPixelFormat **formats, int flags)
Get a list of possible source or target formats usable in av_hwframe_transfer_data().
const char *const * enabled_dev_extensions
Enabled device extensions.
A reference to a data buffer.
VkDevice act_dev
Active device.
int nb_enabled_inst_extensions
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
Set the given entry in *pm, overwriting an existing entry.
int(* display)(VkRenderer *renderer, AVFrame *frame, RenderParams *params)
#define FF_API_VULKAN_SYNC_QUEUES
uint8_t video_background_color[4]
VkPhysicalDeviceFeatures2 device_features
This structure should be set to the set of features that present and enabled during device creation.
int av_hwframe_get_buffer(AVBufferRef *hwframe_ref, AVFrame *frame, int flags)
Allocate a new frame attached to the given AVHWFramesContext.
int(* resize)(VkRenderer *renderer, int width, int height)
@ AV_HWFRAME_TRANSFER_DIRECTION_TO
Transfer the data to the queried hw frame.