19#define VK_NO_PROTOTYPES
20#define VK_ENABLE_BETA_EXTENSIONS
25#if (SDL_VERSION_ATLEAST(2, 0, 6) && CONFIG_LIBPLACEBO)
26#define HAVE_VULKAN_RENDERER 1
28#define HAVE_VULKAN_RENDERER 0
31#if HAVE_VULKAN_RENDERER
33#if defined(_WIN32) && !defined(VK_USE_PLATFORM_WIN32_KHR)
34#define VK_USE_PLATFORM_WIN32_KHR
37#include <libplacebo/vulkan.h>
38#include <libplacebo/utils/frame_queue.h>
39#include <libplacebo/utils/libav.h>
40#include <SDL_vulkan.h>
62#if HAVE_VULKAN_RENDERER
64typedef struct RendererContext {
68 pl_vk_inst placebo_instance;
69 pl_vulkan placebo_vulkan;
70 pl_swapchain swapchain;
71 VkSurfaceKHR vk_surface;
82 PFN_vkGetInstanceProcAddr get_proc_addr;
89static void vk_log_cb(
void *log_priv,
enum pl_log_level
level,
113static void hwctx_lock_queue(
void *priv, uint32_t qf, uint32_t qidx)
117#if FF_API_VULKAN_SYNC_QUEUES
119 hwctx->lock_queue(avhwctx, qf, qidx);
124static void hwctx_unlock_queue(
void *priv, uint32_t qf, uint32_t qidx)
128#if FF_API_VULKAN_SYNC_QUEUES
130 hwctx->unlock_queue(avhwctx, qf, qidx);
135static int add_instance_extension(
const char **ext,
unsigned num_ext,
139 const char *inst_ext_key =
"instance_extensions";
142 char *ext_list =
NULL;
146 for (
int i = 0;
i < num_ext;
i++) {
169 const char *dev_ext_key =
"device_extensions";
172 char *ext_list =
NULL;
176 av_bprintf(&buf,
"%s", VK_KHR_SWAPCHAIN_EXTENSION_NAME);
177 for (
int i = 0;
i < pl_vulkan_num_recommended_extensions;
i++)
178 av_bprintf(&buf,
"+%s", pl_vulkan_recommended_extensions[
i]);
190static const char *select_device(
const AVDictionary *opt)
201 const char **ext,
unsigned num_ext,
204 RendererContext *
ctx = (RendererContext *)
renderer;
210 ret = add_instance_extension(ext, num_ext, opt, &dict);
213 ret = add_device_extension(opt, &dict);
220 select_device(opt), dict, 0);
230 if (hwctx->
get_proc_addr != SDL_Vulkan_GetVkGetInstanceProcAddr()) {
232 "hwdevice and SDL use different get_proc_addr. "
233 "Try -vulkan_params create_by_placebo=1\n");
240 struct pl_vulkan_import_params import_params = {
241 .instance = hwctx->
inst,
248 .lock_queue = hwctx_lock_queue,
249 .unlock_queue = hwctx_unlock_queue,
252 .index = VK_QUEUE_FAMILY_IGNORED,
256 .index = VK_QUEUE_FAMILY_IGNORED,
260 .index = VK_QUEUE_FAMILY_IGNORED,
264 for (
int i = 0;
i < hwctx->
nb_qf;
i++) {
267 if (qf->
flags & VK_QUEUE_GRAPHICS_BIT) {
268 import_params.queue_graphics.index = qf->
idx;
269 import_params.queue_graphics.count = qf->
num;
271 if (qf->
flags & VK_QUEUE_COMPUTE_BIT) {
272 import_params.queue_compute.index = qf->
idx;
273 import_params.queue_compute.count = qf->
num;
275 if (qf->
flags & VK_QUEUE_TRANSFER_BIT) {
276 import_params.queue_transfer.index = qf->
idx;
277 import_params.queue_transfer.count = qf->
num;
281 ctx->placebo_vulkan = pl_vulkan_import(
ctx->vk_log, &import_params);
282 if (!
ctx->placebo_vulkan)
289 uint32_t queue_family, uint32_t
index)
292 pl_vulkan vk =
ctx->placebo_vulkan;
293#if FF_API_VULKAN_SYNC_QUEUES
295 vk->lock_queue(vk, queue_family,
index);
301 uint32_t queue_family,
305 pl_vulkan vk =
ctx->placebo_vulkan;
306#if FF_API_VULKAN_SYNC_QUEUES
308 vk->unlock_queue(vk, queue_family,
index);
315 RendererContext *
ctx = (RendererContext *)
renderer;
316 VkQueueFamilyProperties *queue_family_prop =
NULL;
317 uint32_t num_queue_family_prop = 0;
318 PFN_vkGetPhysicalDeviceQueueFamilyProperties get_queue_family_prop;
319 PFN_vkGetInstanceProcAddr get_proc_addr =
ctx->get_proc_addr;
323 get_queue_family_prop = (PFN_vkGetPhysicalDeviceQueueFamilyProperties)
324 get_proc_addr(
ctx->placebo_instance->instance,
325 "vkGetPhysicalDeviceQueueFamilyProperties");
326 get_queue_family_prop(
ctx->placebo_vulkan->phys_device,
327 &num_queue_family_prop,
NULL);
328 if (!num_queue_family_prop)
331 queue_family_prop =
av_calloc(num_queue_family_prop,
332 sizeof(*queue_family_prop));
333 if (!queue_family_prop)
336 get_queue_family_prop(
ctx->placebo_vulkan->phys_device,
337 &num_queue_family_prop,
340 for (
int i = 0;
i < num_queue_family_prop;
i++) {
341 if (queue_family_prop[
i].queueFlags & VK_QUEUE_VIDEO_DECODE_BIT_KHR) {
343 *count = queue_family_prop[
i].queueCount;
353 const char **ext,
unsigned num_ext,
356 RendererContext *
ctx = (RendererContext *)
renderer;
362 const char **dev_exts;
365 ctx->get_proc_addr = SDL_Vulkan_GetVkGetInstanceProcAddr();
367 ctx->placebo_instance = pl_vk_inst_create(
ctx->vk_log, pl_vk_inst_params(
368 .get_proc_addr =
ctx->get_proc_addr,
369 .debug = enable_debug(opt),
371 .num_extensions = num_ext
373 if (!
ctx->placebo_instance) {
376 ctx->inst =
ctx->placebo_instance->instance;
382 ctx->placebo_vulkan = pl_vulkan_create(
ctx->vk_log, pl_vulkan_params(
383 .instance =
ctx->placebo_instance->instance,
384 .get_proc_addr =
ctx->placebo_instance->get_proc_addr,
385 .surface =
ctx->vk_surface,
386 .allow_software =
false,
387 .opt_extensions = dev_exts,
388 .num_opt_extensions = num_dev_exts,
389 .extra_queues = VK_QUEUE_VIDEO_DECODE_BIT_KHR,
390 .device_name = select_device(opt),
393 if (!
ctx->placebo_vulkan)
396 if (!
ctx->hw_device_ref) {
403 vk_dev_ctx = device_ctx->
hwctx;
404#if FF_API_VULKAN_SYNC_QUEUES
406 vk_dev_ctx->lock_queue = placebo_lock_queue;
407 vk_dev_ctx->unlock_queue = placebo_unlock_queue;
413 vk_dev_ctx->
inst =
ctx->placebo_instance->instance;
414 vk_dev_ctx->
phys_dev =
ctx->placebo_vulkan->phys_device;
415 vk_dev_ctx->
act_dev =
ctx->placebo_vulkan->device;
427 .idx =
ctx->placebo_vulkan->queue_graphics.index,
428 .num =
ctx->placebo_vulkan->queue_graphics.count,
429 .flags = VK_QUEUE_GRAPHICS_BIT,
433 .idx =
ctx->placebo_vulkan->queue_transfer.index,
434 .num =
ctx->placebo_vulkan->queue_transfer.count,
435 .flags = VK_QUEUE_TRANSFER_BIT,
439 .idx =
ctx->placebo_vulkan->queue_compute.index,
440 .num =
ctx->placebo_vulkan->queue_compute.count,
441 .flags = VK_QUEUE_COMPUTE_BIT,
452 .flags = VK_QUEUE_VIDEO_DECODE_BIT_KHR,
456 vk_dev_ctx->
nb_qf = nb_qf;
468 unsigned num_ext = 0;
469 const char **ext =
NULL;
471 struct pl_log_params vk_log_params = {
473 .log_level = PL_LOG_DEBUG,
476 RendererContext *
ctx = (RendererContext *)
renderer;
479 ctx->vk_log = pl_log_create(PL_API_VER, &vk_log_params);
481 if (!SDL_Vulkan_GetInstanceExtensions(
window, &num_ext,
NULL)) {
493 SDL_Vulkan_GetInstanceExtensions(
window, &num_ext, ext);
497 ret = create_vk_by_placebo(
renderer, ext, num_ext, opt);
499 ret = create_vk_by_hwcontext(
renderer, ext, num_ext, opt);
503 if (!SDL_Vulkan_CreateSurface(
window,
ctx->inst, &
ctx->vk_surface)) {
508 ctx->swapchain = pl_vulkan_create_swapchain(
510 pl_vulkan_swapchain_params(
511 .surface =
ctx->vk_surface,
512 .present_mode = VK_PRESENT_MODE_FIFO_KHR));
513 if (!
ctx->swapchain) {
518 SDL_Vulkan_GetDrawableSize(
window, &
w, &
h);
519 pl_swapchain_resize(
ctx->swapchain, &
w, &
h);
521 ctx->renderer = pl_renderer_create(
ctx->vk_log,
ctx->placebo_vulkan->gpu);
522 if (!
ctx->renderer) {
528 if (!
ctx->vk_frame) {
542 RendererContext *
ctx = (RendererContext *)
renderer;
544 *dev =
ctx->hw_device_ref;
550 RendererContext *
ctx = (RendererContext *)
renderer;
552 frame->hw_frames_ctx->data;
557 if (
ctx->hw_frame_ref) {
562 hw_frame->
sw_format == src_hw_frame->sw_format)
568 if (!
ctx->constraints) {
571 if (!
ctx->constraints)
577 if ((
ctx->constraints->max_width &&
578 ctx->constraints->max_width <
frame->width) ||
579 (
ctx->constraints->max_height &&
580 ctx->constraints->max_height <
frame->height) ||
581 (
ctx->constraints->min_width &&
582 ctx->constraints->min_width >
frame->width) ||
583 (
ctx->constraints->min_height &&
584 ctx->constraints->min_height >
frame->height))
587 if (
ctx->constraints->valid_sw_formats) {
590 if (*sw_formats == src_hw_frame->sw_format)
599 if (!
ctx->hw_frame_ref)
604 hw_frame->
sw_format = src_hw_frame->sw_format;
609 vk_frame_ctx = hw_frame->
hwctx;
622 &
ctx->transfer_formats, 0);
627static inline int check_hw_transfer(RendererContext *
ctx,
AVFrame *
frame)
629 if (!
ctx->hw_frame_ref || !
ctx->transfer_formats)
633 if (
ctx->transfer_formats[
i] ==
frame->format)
639static inline int move_to_output_frame(RendererContext *
ctx,
AVFrame *
frame)
651 RendererContext *
ctx = (RendererContext *)
renderer;
654 if (use_hw_frame && !
ctx->hw_frame_ref)
665 return move_to_output_frame(
ctx,
frame);
674 RendererContext *
ctx = (RendererContext *)
renderer;
677 if (use_hw_frame && !check_hw_transfer(
ctx,
frame))
685 return move_to_output_frame(
ctx,
frame);
697 if (!
frame->hw_frames_ctx)
707 for (
int use_hw = 1; use_hw >=0; use_hw--) {
727 struct pl_swapchain_frame swap_frame = {0};
728 struct pl_frame pl_frame = {0};
729 struct pl_frame target = {0};
730 struct pl_render_params pl_params = pl_render_default_params;
731 RendererContext *
ctx = (RendererContext *)
renderer;
733 struct pl_color_space hint = {0};
739 if (!pl_map_avframe_ex(
ctx->placebo_vulkan->gpu, &pl_frame, pl_avframe_params(
746 pl_color_space_from_avframe(&hint,
frame);
747 pl_swapchain_colorspace_hint(
ctx->swapchain, &hint);
748 if (!pl_swapchain_start_frame(
ctx->swapchain, &swap_frame)) {
754 pl_frame_from_swapchain(&target, &swap_frame);
758 switch (
params->video_background_type) {
760 pl_params.background = PL_CLEAR_TILES;
764 pl_params.background = PL_CLEAR_COLOR;
765 for (
int i = 0;
i < 3;
i++)
766 pl_params.background_color[
i] =
params->video_background_color[
i] / 255.0;
767 pl_params.background_transparency = (255 -
params->video_background_color[3]) / 255.0;
770 pl_frame.repr.alpha = PL_ALPHA_NONE;
774 if (!pl_render_image(
ctx->renderer, &pl_frame, &target, &pl_params)) {
780 if (!pl_swapchain_submit_frame(
ctx->swapchain)) {
785 pl_swapchain_swap_buffers(
ctx->swapchain);
788 pl_unmap_avframe(
ctx->placebo_vulkan->gpu, &pl_frame);
794 RendererContext *
ctx = (RendererContext *)
renderer;
803 RendererContext *
ctx = (RendererContext *)
renderer;
804 PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR;
811 if (
ctx->placebo_vulkan) {
813 pl_tex_destroy(
ctx->placebo_vulkan->gpu, &
ctx->tex[
i]);
814 pl_renderer_destroy(&
ctx->renderer);
815 pl_swapchain_destroy(&
ctx->swapchain);
816 pl_vulkan_destroy(&
ctx->placebo_vulkan);
819 if (
ctx->vk_surface) {
820 vkDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR)
821 ctx->get_proc_addr(
ctx->inst,
"vkDestroySurfaceKHR");
822 vkDestroySurfaceKHR(
ctx->inst,
ctx->vk_surface,
NULL);
823 ctx->vk_surface = VK_NULL_HANDLE;
827 pl_vk_inst_destroy(&
ctx->placebo_instance);
829 pl_log_destroy(&
ctx->vk_log);
832static const AVClass vulkan_renderer_class = {
833 .class_name =
"Vulkan Renderer",
847 renderer->class = &vulkan_renderer_class;
SwsAArch64OpImplParams params
static AVFormatContext * ctx
void av_bprintf(AVBPrint *buf, const char *fmt,...)
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
#define AV_BPRINT_SIZE_AUTOMATIC
#define i(width, name, range_min, range_max)
static SDL_Renderer * renderer
static SDL_Window * window
int vk_renderer_create(VkRenderer *renderer, SDL_Window *window, AVDictionary *opt)
int vk_renderer_display(VkRenderer *renderer, AVFrame *frame, RenderParams *render_params)
VkRenderer * vk_get_renderer(void)
int vk_renderer_get_hw_dev(VkRenderer *renderer, AVBufferRef **dev)
int vk_renderer_resize(VkRenderer *renderer, int width, int height)
void vk_renderer_destroy(VkRenderer *renderer)
#define VIDEO_BACKGROUND_TILE_SIZE
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
void av_buffer_unref(AVBufferRef **buf)
Free a given reference and automatically free the buffer if there are no more references to it.
AVBufferRef * av_buffer_ref(const AVBufferRef *buf)
Create a new reference to an AVBuffer.
void av_dict_free(AVDictionary **pm)
Free all the memory allocated for an AVDictionary struct and all keys and values.
AVDictionaryEntry * av_dict_get(const AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int flags)
Get a dictionary entry with matching key.
#define AV_DICT_DONT_STRDUP_VAL
Take ownership of a value that's been allocated with av_malloc() or another memory allocation functio...
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
Set the given entry in *pm, overwriting an existing entry.
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
#define AVERROR_EXTERNAL
Generic error in an external library.
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
void av_frame_unref(AVFrame *frame)
Unreference all the buffers referenced by frame and reset the frame fields.
void av_frame_move_ref(AVFrame *dst, AVFrame *src)
Move everything contained in src to dst and reset src.
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
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_QUIET
Print no output.
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
#define AV_LOG_WARNING
Something somehow does not look correct.
#define AV_LOG_FATAL
Something went wrong and recovery is not possible.
#define AV_LOG_INFO
Standard information.
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
const char * av_default_item_name(void *ptr)
Return the context name.
#define LIBAVUTIL_VERSION_INT
int av_hwdevice_ctx_init(AVBufferRef *ref)
Finalize the device context before use.
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.
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().
void av_hwframe_constraints_free(AVHWFramesConstraints **constraints)
Free an AVHWFrameConstraints structure.
AVBufferRef * av_hwdevice_ctx_alloc(enum AVHWDeviceType type)
Allocate an AVHWDeviceContext for a given hardware type.
int av_hwframe_ctx_init(AVBufferRef *ref)
Finalize the context before use.
AVBufferRef * av_hwframe_ctx_alloc(AVBufferRef *device_ref_in)
Allocate an AVHWFramesContext tied to a given device context.
int av_hwframe_map(AVFrame *dst, const AVFrame *src, int flags)
Map a hardware frame.
int av_hwframe_transfer_data(AVFrame *dst, const AVFrame *src, int flags)
Copy data to or from a hw surface.
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_hwframe_get_buffer(AVBufferRef *hwframe_ref, AVFrame *frame, int flags)
Allocate a new frame attached to the given AVHWFramesContext.
@ AV_HWFRAME_TRANSFER_DIRECTION_TO
Transfer the data to the queried hw frame.
@ AV_HWDEVICE_TYPE_VULKAN
const char ** av_vk_get_optional_device_extensions(int *count)
Returns an array of optional Vulkan device extensions that FFmpeg may use if enabled.
@ AV_VK_FRAME_FLAG_DISABLE_MULTIPLANE
static const uint8_t level_map[LCEVC_LogLevelCount]
static int decode_index(SGAVideoContext *s, AVFrame *frame)
common internal API header
#define FF_DISABLE_DEPRECATION_WARNINGS
#define FF_ENABLE_DEPRECATION_WARNINGS
void * av_calloc(size_t nmemb, size_t size)
Memory handling functions.
AVPixelFormat
Pixel format.
@ AV_PIX_FMT_VULKAN
Vulkan hardware images.
@ AV_PIX_FMT_CUDA
HW acceleration through CUDA.
#define FF_ARRAY_ELEMS(a)
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)
static void destroy(struct ResampleContext **c)
A reference to a data buffer.
Describe the class of an AVClass context structure.
This structure describes decoded (raw) audio or video data.
This struct aggregates all the (hardware/vendor-specific) "high-level" state, i.e.
void * hwctx
The format-specific data, allocated and freed by libavutil along with this context.
void * user_opaque
Arbitrary user data, to be used e.g.
This struct describes the constraints on hardware frames attached to a given device with a hardware-s...
This struct describes a set or pool of "hardware" frames (i.e.
enum AVPixelFormat format
The pixel format identifying the underlying HW surface type.
void * hwctx
The format-specific data, allocated and freed automatically along with this context.
enum AVPixelFormat sw_format
The pixel format identifying the actual data layout of the hardware frames.
int width
The allocated dimensions of the frames in this pool.
Main Vulkan context, allocated as AVHWDeviceContext.hwctx.
VkPhysicalDevice phys_dev
Physical device.
AVVulkanDeviceQueueFamily qf[64]
Queue families used.
int nb_enabled_inst_extensions
const char *const * enabled_dev_extensions
Enabled device extensions.
VkDevice act_dev
Active device.
const char *const * enabled_inst_extensions
Enabled instance extensions.
VkInstance inst
Vulkan instance.
VkPhysicalDeviceFeatures2 device_features
This structure should be set to the set of features that present and enabled during device creation.
int nb_enabled_dev_extensions
PFN_vkGetInstanceProcAddr get_proc_addr
Pointer to a vkGetInstanceProcAddr loading function.
Allocated as AVHWFramesContext.hwctx, used to set pool-specific options.
AVVkFrameFlags flags
A combination of AVVkFrameFlags.
int(* display)(VkRenderer *renderer, AVFrame *frame, RenderParams *params)
void(* destroy)(VkRenderer *renderer)
int(* create)(VkRenderer *renderer, SDL_Window *window, AVDictionary *dict)
int(* resize)(VkRenderer *renderer, int width, int height)
int(* get_hw_dev)(VkRenderer *renderer, AVBufferRef **dev)
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...
static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src, struct pl_frame *out)