testffmpeg: added support for Vulkan rendering

This commit is contained in:
Sam Lantinga
2024-03-02 10:05:38 -08:00
parent 48471f7dbd
commit dbec2150d0
4 changed files with 1088 additions and 20 deletions
+5 -2
View File
@@ -224,11 +224,13 @@ include("${SDL3_SOURCE_DIR}/cmake/FindFFmpeg.cmake")
if(FFmpeg_FOUND)
cmake_push_check_state()
list(APPEND CMAKE_REQUIRED_INCLUDES "${FFmpeg_AVUTIL_INCLUDE_DIRS}")
list(APPEND CMAKE_REQUIRED_INCLUDES "${SDL3_SOURCE_DIR}/src/video/khronos")
check_struct_has_member("AVFrame" "ch_layout" "libavutil/frame.h" LIBAVUTIL_AVFRAME_HAS_CH_LAYOUT)
check_struct_has_member("AVVulkanFramesContext" "format" "libavutil/hwcontext_vulkan.h" LIBAVUTIL_AVFULKANFRAMESCONTEXT_HAS_FORMAT)
cmake_pop_check_state()
endif()
if(FFmpeg_FOUND AND LIBAVUTIL_AVFRAME_HAS_CH_LAYOUT)
add_sdl_test_executable(testffmpeg NO_C90 SOURCES testffmpeg.c ${icon_bmp_header})
if(FFmpeg_FOUND AND LIBAVUTIL_AVFRAME_HAS_CH_LAYOUT AND LIBAVUTIL_AVFULKANFRAMESCONTEXT_HAS_FORMAT)
add_sdl_test_executable(testffmpeg NO_C90 SOURCES testffmpeg.c testffmpeg_vulkan.c ${icon_bmp_header})
if(APPLE)
target_link_options(testffmpeg PRIVATE "-Wl,-framework,CoreVideo")
endif()
@@ -237,6 +239,7 @@ if(FFmpeg_FOUND AND LIBAVUTIL_AVFRAME_HAS_CH_LAYOUT)
target_link_libraries(testffmpeg PRIVATE OpenGL::EGL)
target_compile_definitions(testffmpeg PRIVATE HAVE_EGL)
endif()
target_include_directories(testffmpeg BEFORE PRIVATE ${SDL3_SOURCE_DIR}/src/video/khronos)
target_link_libraries(testffmpeg PRIVATE ${FFMPEG_LIBRARIES})
else()
message(STATUS "Can't find ffmpeg 5.1.3 or newer, skipping testffmpeg")
+87 -18
View File
@@ -55,6 +55,8 @@
#include <libavutil/hwcontext_d3d11va.h>
#endif /* SDL_PLATFORM_WIN32 */
#include "testffmpeg_vulkan.h"
#include "icon.h"
@@ -82,6 +84,7 @@ static ID3D11Device *d3d11_device;
static ID3D11DeviceContext *d3d11_context;
static const GUID SDL_IID_ID3D11Resource = { 0xdc8e63f3, 0xd12b, 0x4952, { 0xb4, 0x7b, 0x5e, 0x45, 0x02, 0x6a, 0x86, 0x2d } };
#endif
static VulkanVideoContext *vulkan_context;
struct SwsContextContainer
{
struct SwsContext *context;
@@ -93,35 +96,59 @@ static SDL_bool CreateWindowAndRenderer(Uint32 window_flags, const char *driver)
{
SDL_PropertiesID props;
SDL_RendererInfo info;
SDL_bool useOpenGL = (driver && (SDL_strcmp(driver, "opengl") == 0 || SDL_strcmp(driver, "opengles2") == 0));
SDL_bool useEGL = (driver && SDL_strcmp(driver, "opengles2") == 0);
SDL_bool useVulkan = (driver && SDL_strcmp(driver, "vulkan") == 0);
Uint32 flags = SDL_WINDOW_HIDDEN;
SDL_SetHint(SDL_HINT_RENDER_DRIVER, driver);
if (useEGL) {
SDL_SetHint(SDL_HINT_VIDEO_FORCE_EGL, "1");
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
} else {
SDL_SetHint(SDL_HINT_VIDEO_FORCE_EGL, "0");
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
if (useOpenGL) {
if (useEGL) {
SDL_SetHint(SDL_HINT_VIDEO_FORCE_EGL, "1");
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
} else {
SDL_SetHint(SDL_HINT_VIDEO_FORCE_EGL, "0");
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
}
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
flags |= SDL_WINDOW_OPENGL;
}
if (useVulkan) {
flags |= SDL_WINDOW_VULKAN;
}
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
/* The window will be resized to the video size when it's loaded, in OpenVideoStream() */
window = SDL_CreateWindow("testffmpeg", 1920, 1080, 0);
window = SDL_CreateWindow("testffmpeg", 1920, 1080, flags);
if (!window) {
return SDL_FALSE;
}
if (useVulkan) {
vulkan_context = CreateVulkanVideoContext(window);
if (!vulkan_context) {
SDL_DestroyWindow(window);
window = NULL;
return SDL_FALSE;
}
}
props = SDL_CreateProperties();
SDL_SetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING, driver);
SDL_SetProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, window);
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB_LINEAR);
renderer = SDL_CreateRendererWithProperties(props);
if (useVulkan) {
SetupVulkanRenderProperties(vulkan_context, props);
}
if (SDL_GetBooleanProperty(SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window)), SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE)) {
/* Try to create an HDR capable renderer */
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB_LINEAR);
renderer = SDL_CreateRendererWithProperties(props);
}
if (!renderer) {
/* Try again with the sRGB colorspace */
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB);
@@ -129,6 +156,8 @@ static SDL_bool CreateWindowAndRenderer(Uint32 window_flags, const char *driver)
}
SDL_DestroyProperties(props);
if (!renderer) {
SDL_DestroyWindow(window);
window = NULL;
return SDL_FALSE;
}
@@ -285,6 +314,8 @@ static Uint32 GetTextureFormat(enum AVPixelFormat format)
return SDL_PIXELFORMAT_NV12;
case AV_PIX_FMT_NV21:
return SDL_PIXELFORMAT_NV21;
case AV_PIX_FMT_P010:
return SDL_PIXELFORMAT_P010;
default:
return SDL_PIXELFORMAT_UNKNOWN;
}
@@ -307,6 +338,9 @@ static SDL_bool SupportedPixelFormat(enum AVPixelFormat format)
return SDL_TRUE;
}
#endif
if (format == AV_PIX_FMT_VULKAN) {
return SDL_TRUE;
}
}
if (GetTextureFormat(format) != SDL_PIXELFORMAT_UNKNOWN) {
@@ -409,7 +443,21 @@ static AVCodecContext *OpenVideoStream(AVFormatContext *ic, int stream, const AV
}
} else
#endif
{
if (vulkan_context && type == AV_HWDEVICE_TYPE_VULKAN) {
AVVulkanDeviceContext *device_context;
context->hw_device_ctx = av_hwdevice_ctx_alloc(type);
device_context = (AVVulkanDeviceContext *)((AVHWDeviceContext *)context->hw_device_ctx->data)->hwctx;
SetupVulkanDeviceContextData(vulkan_context, device_context);
result = av_hwdevice_ctx_init(context->hw_device_ctx);
if (result < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create hardware device context: %s", av_err2str(result));
} else {
SDL_Log("Using %s hardware acceleration with pixel format %s\n", av_hwdevice_get_type_name(config->device_type), av_get_pix_fmt_name(config->pix_fmt));
}
} else {
result = av_hwdevice_ctx_create(&context->hw_device_ctx, type, NULL, NULL, 0);
if (result < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create hardware device context: %s", av_err2str(result));
@@ -799,6 +847,22 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex
#endif
}
static SDL_bool GetTextureForVulkanFrame(AVFrame *frame, SDL_Texture **texture)
{
SDL_PropertiesID props;
if (*texture) {
SDL_DestroyTexture(*texture);
}
props = CreateVideoTextureProperties(frame, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_STATIC, frame->width, frame->height);
*texture = CreateVulkanVideoTexture(vulkan_context, frame, renderer, props);
SDL_DestroyProperties(props);
if (!*texture) {
return SDL_FALSE;
}
return SDL_TRUE;
}
static SDL_bool GetTextureForFrame(AVFrame *frame, SDL_Texture **texture)
{
switch (frame->format) {
@@ -810,6 +874,8 @@ static SDL_bool GetTextureForFrame(AVFrame *frame, SDL_Texture **texture)
return GetTextureForD3D11Frame(frame, texture);
case AV_PIX_FMT_VIDEOTOOLBOX:
return GetTextureForVideoToolboxFrame(frame, texture);
case AV_PIX_FMT_VULKAN:
return GetTextureForVulkanFrame(frame, texture);
default:
return GetTextureForMemoryFrame(frame, texture);
}
@@ -1277,6 +1343,9 @@ quit:
avcodec_free_context(&video_context);
avformat_close_input(&ic);
SDL_DestroyRenderer(renderer);
if (vulkan_context) {
DestroyVulkanVideoContext(vulkan_context);
}
SDL_DestroyWindow(window);
SDL_Quit();
SDLTest_CommonDestroyState(state);
File diff suppressed because it is too large Load Diff
+22
View File
@@ -0,0 +1,22 @@
/*
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely.
*/
#include <libavutil/hwcontext_vulkan.h>
typedef struct VulkanVideoContext VulkanVideoContext;
VulkanVideoContext *CreateVulkanVideoContext(SDL_Window *window);
void SetupVulkanRenderProperties(VulkanVideoContext *context, SDL_PropertiesID props);
void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx);
SDL_Texture *CreateVulkanVideoTexture(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer, SDL_PropertiesID props);
void DestroyVulkanVideoContext(VulkanVideoContext *context);