Plumb YUV444 logic into additional renderers

This commit is contained in:
Cameron Gutman 2024-07-25 06:36:40 -05:00
parent da0244c538
commit c707dab70a
7 changed files with 135 additions and 51 deletions

View File

@ -8,6 +8,7 @@
extern "C" { extern "C" {
#include <libavutil/hwcontext_drm.h> #include <libavutil/hwcontext_drm.h>
#include <libavutil/pixdesc.h>
} }
#include <libdrm/drm_fourcc.h> #include <libdrm/drm_fourcc.h>
@ -23,6 +24,11 @@ extern "C" {
#define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5') #define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5')
#endif #endif
// Same as NV15 but non-subsampled
#ifndef DRM_FORMAT_NV30
#define DRM_FORMAT_NV30 fourcc_code('N', 'V', '3', '0')
#endif
// Special Raspberry Pi type (upstreamed) // Special Raspberry Pi type (upstreamed)
#ifndef DRM_FORMAT_P030 #ifndef DRM_FORMAT_P030
#define DRM_FORMAT_P030 fourcc_code('P', '0', '3', '0') #define DRM_FORMAT_P030 fourcc_code('P', '0', '3', '0')
@ -33,6 +39,11 @@ extern "C" {
#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') #define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0')
#endif #endif
// Upstreamed YUV444 10-bit
#ifndef DRM_FORMAT_Q410
#define DRM_FORMAT_Q410 fourcc_code('Q', '4', '1', '0')
#endif
// Values for "Colorspace" connector property // Values for "Colorspace" connector property
#ifndef DRM_MODE_COLORIMETRY_DEFAULT #ifndef DRM_MODE_COLORIMETRY_DEFAULT
#define DRM_MODE_COLORIMETRY_DEFAULT 0 #define DRM_MODE_COLORIMETRY_DEFAULT 0
@ -68,7 +79,7 @@ DrmRenderer::DrmRenderer(bool hwaccel, IFFmpegRenderer *backendRenderer)
m_DrmFd(-1), m_DrmFd(-1),
m_SdlOwnsDrmFd(false), m_SdlOwnsDrmFd(false),
m_SupportsDirectRendering(false), m_SupportsDirectRendering(false),
m_Main10Hdr(false), m_VideoFormat(0),
m_ConnectorId(0), m_ConnectorId(0),
m_EncoderId(0), m_EncoderId(0),
m_CrtcId(0), m_CrtcId(0),
@ -239,7 +250,7 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params)
int i; int i;
m_Window = params->window; m_Window = params->window;
m_Main10Hdr = (params->videoFormat & VIDEO_FORMAT_MASK_10BIT); m_VideoFormat = params->videoFormat;
m_SwFrameMapper.setVideoFormat(params->videoFormat); m_SwFrameMapper.setVideoFormat(params->videoFormat);
#if SDL_VERSION_ATLEAST(2, 0, 15) #if SDL_VERSION_ATLEAST(2, 0, 15)
@ -508,12 +519,31 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params)
// Validate that the candidate plane supports our pixel format // Validate that the candidate plane supports our pixel format
bool matchingFormat = false; bool matchingFormat = false;
for (uint32_t j = 0; j < plane->count_formats && !matchingFormat; j++) { for (uint32_t j = 0; j < plane->count_formats && !matchingFormat; j++) {
if (m_Main10Hdr) { if (m_VideoFormat & VIDEO_FORMAT_MASK_10BIT) {
if (m_VideoFormat & VIDEO_FORMAT_MASK_YUV444) {
switch (plane->formats[j]) {
case DRM_FORMAT_Q410:
case DRM_FORMAT_NV30:
matchingFormat = true;
break;
}
}
else {
switch (plane->formats[j]) {
case DRM_FORMAT_P010:
case DRM_FORMAT_P030:
case DRM_FORMAT_NA12:
case DRM_FORMAT_NV15:
matchingFormat = true;
break;
}
}
}
else if (m_VideoFormat & VIDEO_FORMAT_MASK_YUV444) {
switch (plane->formats[j]) { switch (plane->formats[j]) {
case DRM_FORMAT_P010: case DRM_FORMAT_NV24:
case DRM_FORMAT_P030: case DRM_FORMAT_NV42:
case DRM_FORMAT_NA12: case DRM_FORMAT_YUV444:
case DRM_FORMAT_NV15:
matchingFormat = true; matchingFormat = true;
break; break;
} }
@ -606,7 +636,7 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params)
else if (!strcmp(prop->name, "Colorspace")) { else if (!strcmp(prop->name, "Colorspace")) {
m_ColorspaceProp = prop; m_ColorspaceProp = prop;
} }
else if (!strcmp(prop->name, "max bpc") && m_Main10Hdr) { else if (!strcmp(prop->name, "max bpc") && (m_VideoFormat & VIDEO_FORMAT_MASK_10BIT)) {
if (drmModeObjectSetProperty(m_DrmFd, m_ConnectorId, DRM_MODE_OBJECT_CONNECTOR, prop->prop_id, 16) == 0) { if (drmModeObjectSetProperty(m_DrmFd, m_ConnectorId, DRM_MODE_OBJECT_CONNECTOR, prop->prop_id, 16) == 0) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Enabled 48-bit HDMI Deep Color"); "Enabled 48-bit HDMI Deep Color");
@ -676,8 +706,29 @@ bool DrmRenderer::isPixelFormatSupported(int videoFormat, AVPixelFormat pixelFor
return true; return true;
} }
else if (videoFormat & VIDEO_FORMAT_MASK_10BIT) { else if (videoFormat & VIDEO_FORMAT_MASK_10BIT) {
if (videoFormat & VIDEO_FORMAT_MASK_YUV444) {
switch (pixelFormat) {
case AV_PIX_FMT_YUV444P10:
return true;
default:
return false;
}
}
else {
switch (pixelFormat) {
case AV_PIX_FMT_P010:
return true;
default:
return false;
}
}
}
else if (videoFormat & VIDEO_FORMAT_MASK_YUV444) {
switch (pixelFormat) { switch (pixelFormat) {
case AV_PIX_FMT_P010: case AV_PIX_FMT_NV24:
case AV_PIX_FMT_NV42:
case AV_PIX_FMT_YUV444P:
case AV_PIX_FMT_YUVJ444P:
return true; return true;
default: default:
return false; return false;
@ -845,32 +896,38 @@ bool DrmRenderer::mapSoftwareFrame(AVFrame *frame, AVDRMFrameDescriptor *mappedF
freeFrame = false; freeFrame = false;
} }
const AVPixFmtDescriptor* formatDesc = av_pix_fmt_desc_get((AVPixelFormat) frame->format);
int planes = av_pix_fmt_count_planes((AVPixelFormat) frame->format);
uint32_t drmFormat; uint32_t drmFormat;
bool fullyPlanar;
int bpc;
// NB: Keep this list updated with isPixelFormatSupported() // NB: Keep this list updated with isPixelFormatSupported()
switch (frame->format) { switch (frame->format) {
case AV_PIX_FMT_NV12: case AV_PIX_FMT_NV12:
drmFormat = DRM_FORMAT_NV12; drmFormat = DRM_FORMAT_NV12;
fullyPlanar = false;
bpc = 8;
break; break;
case AV_PIX_FMT_NV21: case AV_PIX_FMT_NV21:
drmFormat = DRM_FORMAT_NV21; drmFormat = DRM_FORMAT_NV21;
fullyPlanar = false;
bpc = 8;
break; break;
case AV_PIX_FMT_P010: case AV_PIX_FMT_P010:
drmFormat = DRM_FORMAT_P010; drmFormat = DRM_FORMAT_P010;
fullyPlanar = false;
bpc = 16;
break; break;
case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUV420P:
case AV_PIX_FMT_YUVJ420P: case AV_PIX_FMT_YUVJ420P:
drmFormat = DRM_FORMAT_YUV420; drmFormat = DRM_FORMAT_YUV420;
fullyPlanar = true; break;
bpc = 8; case AV_PIX_FMT_NV24:
drmFormat = DRM_FORMAT_NV24;
break;
case AV_PIX_FMT_NV42:
drmFormat = DRM_FORMAT_NV42;
break;
case AV_PIX_FMT_YUV444P:
case AV_PIX_FMT_YUVJ444P:
drmFormat = DRM_FORMAT_YUV444;
break;
case AV_PIX_FMT_YUV444P10:
drmFormat = DRM_FORMAT_Q410;
break; break;
default: default:
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@ -884,8 +941,8 @@ bool DrmRenderer::mapSoftwareFrame(AVFrame *frame, AVDRMFrameDescriptor *mappedF
struct drm_mode_create_dumb createBuf = {}; struct drm_mode_create_dumb createBuf = {};
createBuf.width = frame->width; createBuf.width = frame->width;
createBuf.height = frame->height * 2; // Y + CbCr at 2x2 subsampling createBuf.height = frame->height + (2 * AV_CEIL_RSHIFT(frame->height, formatDesc->log2_chroma_h));
createBuf.bpp = bpc; createBuf.bpp = av_get_padded_bits_per_pixel(formatDesc);
int err = drmIoctl(m_DrmFd, DRM_IOCTL_MODE_CREATE_DUMB, &createBuf); int err = drmIoctl(m_DrmFd, DRM_IOCTL_MODE_CREATE_DUMB, &createBuf);
if (err < 0) { if (err < 0) {
@ -976,18 +1033,14 @@ bool DrmRenderer::mapSoftwareFrame(AVFrame *frame, AVDRMFrameDescriptor *mappedF
planeHeight = frame->height; planeHeight = frame->height;
plane.pitch = drmFrame->pitch; plane.pitch = drmFrame->pitch;
} }
else if (fullyPlanar) {
// U/V planes are 2x2 subsampled
planeHeight = frame->height / 2;
plane.pitch = drmFrame->pitch / 2;
}
else { else {
// UV/VU planes are 2x2 subsampled. planeHeight = AV_CEIL_RSHIFT(frame->height, formatDesc->log2_chroma_h);
// plane.pitch = AV_CEIL_RSHIFT(drmFrame->pitch, formatDesc->log2_chroma_w);
// NB: The pitch is the same between Y and UV/VU, because the 2x subsampling
// is cancelled out by the 2x plane size of UV/VU vs U/V alone. // If UV planes are interleaved, double the pitch to count both U+V together
planeHeight = frame->height / 2; if (planes == 2) {
plane.pitch = drmFrame->pitch; plane.pitch <<= 1;
}
} }
// Copy the plane data into the dumb buffer // Copy the plane data into the dumb buffer
@ -1353,7 +1406,7 @@ bool DrmRenderer::canExportEGL() {
"Using EGL rendering due to environment variable"); "Using EGL rendering due to environment variable");
return true; return true;
} }
else if (m_SupportsDirectRendering && m_Main10Hdr) { else if (m_SupportsDirectRendering && (m_VideoFormat & VIDEO_FORMAT_MASK_10BIT)) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Using direct rendering for HDR support"); "Using direct rendering for HDR support");
return false; return false;

View File

@ -85,7 +85,7 @@ private:
int m_DrmFd; int m_DrmFd;
bool m_SdlOwnsDrmFd; bool m_SdlOwnsDrmFd;
bool m_SupportsDirectRendering; bool m_SupportsDirectRendering;
bool m_Main10Hdr; int m_VideoFormat;
uint32_t m_ConnectorId; uint32_t m_ConnectorId;
uint32_t m_EncoderId; uint32_t m_EncoderId;
uint32_t m_CrtcId; uint32_t m_CrtcId;

View File

@ -765,11 +765,15 @@ bool DXVA2Renderer::initialize(PDECODER_PARAMETERS params)
m_Desc.SampleFormat.VideoTransferFunction = DXVA2_VideoTransFunc_Unknown; m_Desc.SampleFormat.VideoTransferFunction = DXVA2_VideoTransFunc_Unknown;
m_Desc.SampleFormat.SampleFormat = DXVA2_SampleProgressiveFrame; m_Desc.SampleFormat.SampleFormat = DXVA2_SampleProgressiveFrame;
if (m_VideoFormat & VIDEO_FORMAT_MASK_10BIT) { if (m_VideoFormat & VIDEO_FORMAT_MASK_YUV444) {
m_Desc.Format = (D3DFORMAT)MAKEFOURCC('P','0','1','0'); m_Desc.Format = (m_VideoFormat & VIDEO_FORMAT_MASK_10BIT) ?
(D3DFORMAT)MAKEFOURCC('Y','4','1','0') :
(D3DFORMAT)MAKEFOURCC('A','Y','U','V');
} }
else { else {
m_Desc.Format = (D3DFORMAT)MAKEFOURCC('N','V','1','2'); m_Desc.Format = (m_VideoFormat & VIDEO_FORMAT_MASK_10BIT) ?
(D3DFORMAT)MAKEFOURCC('P','0','1','0') :
(D3DFORMAT)MAKEFOURCC('N','V','1','2');
} }
if (!initializeDevice(params->window, params->enableVsync)) { if (!initializeDevice(params->window, params->enableVsync)) {

View File

@ -957,11 +957,24 @@ bool PlVkRenderer::isPixelFormatSupported(int videoFormat, AVPixelFormat pixelFo
} }
else if (videoFormat & VIDEO_FORMAT_MASK_YUV444) { else if (videoFormat & VIDEO_FORMAT_MASK_YUV444) {
if (videoFormat & VIDEO_FORMAT_MASK_10BIT) { if (videoFormat & VIDEO_FORMAT_MASK_10BIT) {
return pixelFormat == AV_PIX_FMT_YUV444P10; switch (pixelFormat) {
case AV_PIX_FMT_P410:
case AV_PIX_FMT_YUV444P10:
return true;
default:
return false;
}
} }
else { else {
return pixelFormat == AV_PIX_FMT_YUV444P || switch (pixelFormat) {
pixelFormat == AV_PIX_FMT_YUVJ444P; case AV_PIX_FMT_NV24:
case AV_PIX_FMT_NV42:
case AV_PIX_FMT_YUV444P:
case AV_PIX_FMT_YUVJ444P:
return true;
default:
return false;
}
} }
} }
else if (videoFormat & VIDEO_FORMAT_MASK_10BIT) { else if (videoFormat & VIDEO_FORMAT_MASK_10BIT) {

View File

@ -86,6 +86,10 @@ bool SdlRenderer::isPixelFormatSupported(int videoFormat, AVPixelFormat pixelFor
// SDL2 doesn't support 10-bit pixel formats // SDL2 doesn't support 10-bit pixel formats
return false; return false;
} }
else if (videoFormat & VIDEO_FORMAT_MASK_YUV444) {
// SDL2 doesn't support YUV444 pixel formats
return false;
}
else { else {
// Remember to keep this in sync with SdlRenderer::renderFrame()! // Remember to keep this in sync with SdlRenderer::renderFrame()!
switch (pixelFormat) { switch (pixelFormat) {
@ -108,8 +112,8 @@ bool SdlRenderer::initialize(PDECODER_PARAMETERS params)
m_VideoFormat = params->videoFormat; m_VideoFormat = params->videoFormat;
m_SwFrameMapper.setVideoFormat(m_VideoFormat); m_SwFrameMapper.setVideoFormat(m_VideoFormat);
if (params->videoFormat & VIDEO_FORMAT_MASK_10BIT) { if (params->videoFormat & (VIDEO_FORMAT_MASK_10BIT | VIDEO_FORMAT_MASK_YUV444)) {
// SDL doesn't support rendering YUV 10-bit textures yet // SDL doesn't support rendering YUV444 or 10-bit textures yet
return false; return false;
} }

View File

@ -20,6 +20,7 @@ VAAPIRenderer::VAAPIRenderer(int decoderSelectionPass)
: m_DecoderSelectionPass(decoderSelectionPass), : m_DecoderSelectionPass(decoderSelectionPass),
m_HwContext(nullptr), m_HwContext(nullptr),
m_BlacklistedForDirectRendering(false), m_BlacklistedForDirectRendering(false),
m_RequiresExplicitPixelFormat(false),
m_OverlayMutex(nullptr) m_OverlayMutex(nullptr)
#ifdef HAVE_EGL #ifdef HAVE_EGL
, m_EglExportType(EglExportType::Unknown), , m_EglExportType(EglExportType::Unknown),
@ -406,6 +407,8 @@ VAAPIRenderer::initialize(PDECODER_PARAMETERS params)
m_BlacklistedForDirectRendering = vendorStr.contains("iHD"); m_BlacklistedForDirectRendering = vendorStr.contains("iHD");
} }
m_RequiresExplicitPixelFormat = vendorStr.contains("i965");
// This will populate the driver_quirks // This will populate the driver_quirks
err = av_hwdevice_ctx_init(m_HwContext); err = av_hwdevice_ctx_init(m_HwContext);
if (err < 0) { if (err < 0) {
@ -922,18 +925,21 @@ VAAPIRenderer::canExportSurfaceHandle(int layerTypeFlag, VADRMPRIMESurfaceDescri
} }
// These attributes are required for i965 to create a surface that can // These attributes are required for i965 to create a surface that can
// be successfully exported via vaExportSurfaceHandle(). iHD doesn't // be successfully exported via vaExportSurfaceHandle(). YUV444 is not
// need these, but it doesn't seem to hurt either. // handled here but i965 supports no hardware with YUV444 decoding.
attrs[attributeCount].type = VASurfaceAttribPixelFormat; if (m_RequiresExplicitPixelFormat && !(m_VideoFormat & VIDEO_FORMAT_MASK_YUV444)) {
attrs[attributeCount].flags = VA_SURFACE_ATTRIB_SETTABLE; attrs[attributeCount].type = VASurfaceAttribPixelFormat;
attrs[attributeCount].value.type = VAGenericValueTypeInteger; attrs[attributeCount].flags = VA_SURFACE_ATTRIB_SETTABLE;
attrs[attributeCount].value.value.i = (m_VideoFormat & VIDEO_FORMAT_MASK_10BIT) ? attrs[attributeCount].value.type = VAGenericValueTypeInteger;
VA_FOURCC_P010 : VA_FOURCC_NV12; attrs[attributeCount].value.value.i =
attributeCount++; (m_VideoFormat & VIDEO_FORMAT_MASK_10BIT) ? VA_FOURCC_P010 : VA_FOURCC_NV12;
attributeCount++;
}
st = vaCreateSurfaces(vaDeviceContext->display, st = vaCreateSurfaces(vaDeviceContext->display,
(m_VideoFormat & VIDEO_FORMAT_MASK_10BIT) ? (m_VideoFormat & VIDEO_FORMAT_MASK_10BIT) ?
VA_RT_FORMAT_YUV420_10 : VA_RT_FORMAT_YUV420, ((m_VideoFormat & VIDEO_FORMAT_MASK_YUV444) ? VA_RT_FORMAT_YUV444_10 : VA_RT_FORMAT_YUV420_10) :
((m_VideoFormat & VIDEO_FORMAT_MASK_YUV444) ? VA_RT_FORMAT_YUV444 : VA_RT_FORMAT_YUV420),
1280, 1280,
720, 720,
&surfaceId, &surfaceId,
@ -986,6 +992,9 @@ VAAPIRenderer::canExportEGL() {
AVPixelFormat VAAPIRenderer::getEGLImagePixelFormat() { AVPixelFormat VAAPIRenderer::getEGLImagePixelFormat() {
switch (m_EglExportType) { switch (m_EglExportType) {
case EglExportType::Separate: case EglExportType::Separate:
// YUV444 surfaces can be in a variety of different formats, so we need to
// use the composed export that returns an opaque format-agnostic texture.
SDL_assert(!(m_VideoFormat & VIDEO_FORMAT_MASK_YUV444));
return (m_VideoFormat & VIDEO_FORMAT_MASK_10BIT) ? return (m_VideoFormat & VIDEO_FORMAT_MASK_10BIT) ?
AV_PIX_FMT_P010 : AV_PIX_FMT_NV12; AV_PIX_FMT_P010 : AV_PIX_FMT_NV12;

View File

@ -96,6 +96,7 @@ private:
AVBufferRef* m_HwContext; AVBufferRef* m_HwContext;
bool m_BlacklistedForDirectRendering; bool m_BlacklistedForDirectRendering;
bool m_HasRfiLatencyBug; bool m_HasRfiLatencyBug;
bool m_RequiresExplicitPixelFormat;
SDL_mutex* m_OverlayMutex; SDL_mutex* m_OverlayMutex;
VAImageFormat m_OverlayFormat; VAImageFormat m_OverlayFormat;