Add support for rendering software decoded frames with Vulkan

This commit is contained in:
Cameron Gutman 2024-07-06 16:49:35 -05:00
parent d085722911
commit b5b2731d5f
3 changed files with 85 additions and 21 deletions

View File

@ -94,8 +94,9 @@ void PlVkRenderer::overlayUploadComplete(void* opaque)
SDL_FreeSurface((SDL_Surface*)opaque);
}
PlVkRenderer::PlVkRenderer(IFFmpegRenderer* backendRenderer) :
m_Backend(backendRenderer)
PlVkRenderer::PlVkRenderer(bool hwaccel, IFFmpegRenderer *backendRenderer) :
m_Backend(backendRenderer),
m_HwAccelBackend(hwaccel)
{
bool ok;
@ -240,7 +241,7 @@ bool PlVkRenderer::tryInitializeDevice(VkPhysicalDevice device, VkPhysicalDevice
}
// If we're acting as the decoder backend, we need a physical device with Vulkan video support
if (m_Backend == nullptr) {
if (m_HwAccelBackend) {
const char* videoDecodeExtension;
if (decoderParams->videoFormat & VIDEO_FORMAT_MASK_H264) {
@ -467,7 +468,7 @@ bool PlVkRenderer::initialize(PDECODER_PARAMETERS params)
}
// We only need an hwaccel device context if we're going to act as the backend renderer too
if (m_Backend == nullptr) {
if (m_HwAccelBackend) {
m_HwDeviceCtx = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VULKAN);
if (m_HwDeviceCtx == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@ -521,13 +522,17 @@ bool PlVkRenderer::initialize(PDECODER_PARAMETERS params)
bool PlVkRenderer::prepareDecoderContext(AVCodecContext *context, AVDictionary **)
{
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Using Vulkan video decoding");
if (m_HwAccelBackend) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Using Vulkan video decoding");
// This should only be called when we're acting as the decoder backend
SDL_assert(m_Backend == nullptr);
context->hw_device_ctx = av_buffer_ref(m_HwDeviceCtx);
}
else {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Using Vulkan renderer");
}
context->hw_device_ctx = av_buffer_ref(m_HwDeviceCtx);
return true;
}
@ -932,11 +937,36 @@ bool PlVkRenderer::needsTestFrame()
bool PlVkRenderer::isPixelFormatSupported(int videoFormat, AVPixelFormat pixelFormat)
{
if (m_Backend) {
if (m_HwAccelBackend) {
return pixelFormat == AV_PIX_FMT_VULKAN;
}
else if (m_Backend) {
return m_Backend->isPixelFormatSupported(videoFormat, pixelFormat);
}
else {
return IFFmpegRenderer::isPixelFormatSupported(videoFormat, pixelFormat);
if (pixelFormat == AV_PIX_FMT_VULKAN) {
// Vulkan frames are always supported
return true;
}
else if (videoFormat & VIDEO_FORMAT_MASK_10BIT) {
switch (pixelFormat) {
case AV_PIX_FMT_P010:
return true;
default:
return false;
}
}
else {
switch (pixelFormat) {
case AV_PIX_FMT_NV12:
case AV_PIX_FMT_NV21:
case AV_PIX_FMT_YUV420P:
case AV_PIX_FMT_YUVJ420P:
return true;
default:
return false;
}
}
}
}

View File

@ -12,7 +12,7 @@
class PlVkRenderer : public IFFmpegRenderer {
public:
PlVkRenderer(IFFmpegRenderer* backendRenderer);
PlVkRenderer(bool hwaccel = false, IFFmpegRenderer *backendRenderer = nullptr);
virtual ~PlVkRenderer() override;
virtual bool initialize(PDECODER_PARAMETERS params) override;
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
@ -47,6 +47,7 @@ private:
// The backend renderer if we're frontend-only
IFFmpegRenderer* m_Backend;
bool m_HwAccelBackend;
// SDL state
SDL_Window* m_Window = nullptr;

View File

@ -137,8 +137,8 @@ int FFmpegVideoDecoder::getDecoderCapabilities()
}
else if (m_HwDecodeCfg == nullptr) {
// We have a non-hwaccel hardware decoder. This will always
// be using SDLRenderer/DrmRenderer so we will pick decoder
// capabilities based on the decoder name.
// be using SDLRenderer/DrmRenderer/PlVkRenderer so we will
// pick decoder capabilities based on the decoder name.
capabilities = k_NonHwaccelCodecInfo.value(m_VideoDecoderCtx->codec->name, 0);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Using capabilities table for decoder: %s -> %d",
@ -309,7 +309,7 @@ bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params, bool
// The Vulkan renderer can also handle HDR with a supported compositor. We prefer
// rendering HDR with Vulkan if possible since it's more fully featured than DRM.
if (m_BackendRenderer->getRendererType() != IFFmpegRenderer::RendererType::Vulkan) {
m_FrontendRenderer = new PlVkRenderer(m_BackendRenderer);
m_FrontendRenderer = new PlVkRenderer(false, m_BackendRenderer);
if (m_FrontendRenderer->initialize(params) && (m_FrontendRenderer->getRendererAttributes() & RENDERER_ATTRIBUTE_HDR_SUPPORT)) {
return true;
}
@ -335,7 +335,7 @@ bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params, bool
#if defined(HAVE_LIBPLACEBO_VULKAN) && defined(VULKAN_IS_SLOW)
if (m_BackendRenderer->getRendererType() != IFFmpegRenderer::RendererType::Vulkan) {
m_FrontendRenderer = new PlVkRenderer(m_BackendRenderer);
m_FrontendRenderer = new PlVkRenderer(false, m_BackendRenderer);
if (m_FrontendRenderer->initialize(params) && (m_FrontendRenderer->getRendererAttributes() & RENDERER_ATTRIBUTE_HDR_SUPPORT)) {
return true;
}
@ -349,7 +349,7 @@ bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params, bool
#ifdef HAVE_LIBPLACEBO_VULKAN
if (qgetenv("PREFER_VULKAN") == "1") {
if (m_BackendRenderer->getRendererType() != IFFmpegRenderer::RendererType::Vulkan) {
m_FrontendRenderer = new PlVkRenderer(m_BackendRenderer);
m_FrontendRenderer = new PlVkRenderer(false, m_BackendRenderer);
if (m_FrontendRenderer->initialize(params)) {
return true;
}
@ -383,8 +383,8 @@ bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params, bool
// The backend renderer cannot directly render to the display, so
// we will create an SDL or DRM renderer to draw the frames.
#ifdef GL_IS_SLOW
#ifdef HAVE_DRM
#if (defined(VULKAN_IS_SLOW) || defined(GL_IS_SLOW)) && defined(HAVE_DRM)
// Try DrmRenderer first if we have a slow GPU
m_FrontendRenderer = new DrmRenderer(false, m_BackendRenderer);
if (m_FrontendRenderer->initialize(params)) {
return true;
@ -393,7 +393,8 @@ bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params, bool
m_FrontendRenderer = nullptr;
#endif
#ifdef HAVE_EGL
#if defined(GL_IS_SLOW) && defined(HAVE_EGL)
// We explicitly skipped EGL in the GL_IS_SLOW case above.
// If DRM didn't work either, try EGL now.
if (m_BackendRenderer->canExportEGL()) {
@ -405,6 +406,14 @@ bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params, bool
m_FrontendRenderer = nullptr;
}
#endif
#if defined(HAVE_LIBPLACEBO_VULKAN) && defined(VULKAN_IS_SLOW)
m_FrontendRenderer = new PlVkRenderer(false, m_BackendRenderer);
if (m_FrontendRenderer->initialize(params)) {
return true;
}
delete m_FrontendRenderer;
m_FrontendRenderer = nullptr;
#endif
m_FrontendRenderer = new SdlRenderer();
@ -844,7 +853,7 @@ IFFmpegRenderer* FFmpegVideoDecoder::createHwAccelRenderer(const AVCodecHWConfig
#endif
#ifdef HAVE_LIBPLACEBO_VULKAN
case AV_HWDEVICE_TYPE_VULKAN:
return new PlVkRenderer(nullptr);
return new PlVkRenderer(true);
#endif
default:
return nullptr;
@ -1083,6 +1092,13 @@ bool FFmpegVideoDecoder::tryInitializeRendererForUnknownDecoder(const AVCodec* d
}
#endif
#if defined(HAVE_LIBPLACEBO_VULKAN) && !defined(VULKAN_IS_SLOW)
if (tryInitializeRenderer(decoder, AV_PIX_FMT_NONE, params, nullptr, nullptr,
[]() -> IFFmpegRenderer* { return new PlVkRenderer(); })) {
return true;
}
#endif
if (tryInitializeRenderer(decoder, AV_PIX_FMT_NONE, params, nullptr, nullptr,
[]() -> IFFmpegRenderer* { return new SdlRenderer(); })) {
return true;
@ -1113,6 +1129,9 @@ bool FFmpegVideoDecoder::tryInitializeRendererForUnknownDecoder(const AVCodec* d
#ifdef HAVE_DRM
TRY_PREFERRED_PIXEL_FORMAT(DrmRenderer);
#endif
#if defined(HAVE_LIBPLACEBO_VULKAN) && !defined(VULKAN_IS_SLOW)
TRY_PREFERRED_PIXEL_FORMAT(PlVkRenderer);
#endif
#ifndef GL_IS_SLOW
TRY_PREFERRED_PIXEL_FORMAT(SdlRenderer);
#endif
@ -1123,11 +1142,25 @@ bool FFmpegVideoDecoder::tryInitializeRendererForUnknownDecoder(const AVCodec* d
#ifdef HAVE_DRM
TRY_SUPPORTED_NON_PREFERRED_PIXEL_FORMAT(DrmRenderer);
#endif
#if defined(HAVE_LIBPLACEBO_VULKAN) && !defined(VULKAN_IS_SLOW)
TRY_SUPPORTED_NON_PREFERRED_PIXEL_FORMAT(PlVkRenderer);
#endif
#ifndef GL_IS_SLOW
TRY_SUPPORTED_NON_PREFERRED_PIXEL_FORMAT(SdlRenderer);
#endif
}
#if defined(HAVE_LIBPLACEBO_VULKAN) && defined(VULKAN_IS_SLOW)
// If we got here with VULKAN_IS_SLOW, DrmRenderer didn't work,
// so we have to resort to PlVkRenderer.
for (int i = 0; decoder->pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
TRY_PREFERRED_PIXEL_FORMAT(PlVkRenderer);
}
for (int i = 0; decoder->pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
TRY_SUPPORTED_NON_PREFERRED_PIXEL_FORMAT(PlVkRenderer);
}
#endif
#ifdef GL_IS_SLOW
// If we got here with GL_IS_SLOW, DrmRenderer didn't work, so we have
// to resort to SdlRenderer.