Update vaapi_wrapper.cc

This commit is contained in:
Alexander David Frick 2022-06-17 07:20:36 -07:00 committed by GitHub
parent b65026de0a
commit 7a3a84e7ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -5,6 +5,7 @@
#include "media/gpu/vaapi/vaapi_wrapper.h"
#include <dlfcn.h>
#include <drm_fourcc.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
@ -54,6 +55,7 @@
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/linux/drm_util_linux.h"
#include "ui/gfx/linux/native_pixmap_dmabuf.h"
#include "ui/gfx/native_pixmap.h"
#include "ui/gfx/native_pixmap_handle.h"
@ -243,7 +245,7 @@ media::VAImplementation VendorStringToImplementationType(
} else if (base::StartsWith(va_vendor_string, "Intel iHD driver",
base::CompareCase::SENSITIVE)) {
return media::VAImplementation::kIntelIHD;
} else if (base::StartsWith(va_vendor_string, "Nvidia VDPAU",
} else if (base::StartsWith(va_vendor_string, "Splitted-Desktop Systems VDPAU",
base::CompareCase::SENSITIVE)) {
return media::VAImplementation::kNVIDIAVDPAU;
}
@ -262,6 +264,152 @@ bool UseGlobalVaapiLock(media::VAImplementation implementation_type) {
base::FeatureList::IsEnabled(media::kGlobalVaapiLock);
}
bool FillVADRMPRIMESurfaceDescriptor(const gfx::NativePixmap& pixmap,
VADRMPRIMESurfaceDescriptor& descriptor) {
memset(&descriptor, 0, sizeof(VADRMPRIMESurfaceDescriptor));
const gfx::BufferFormat buffer_format = pixmap.GetBufferFormat();
const uint32_t va_fourcc = BufferFormatToVAFourCC(buffer_format);
DCHECK(va_fourcc);
const gfx::Size size = pixmap.GetBufferSize();
const size_t num_planes = pixmap.GetNumberOfPlanes();
const int drm_fourcc = ui::GetFourCCFormatFromBufferFormat(buffer_format);
if (drm_fourcc == DRM_FORMAT_INVALID) {
LOG(ERROR) << "Failed to get the DRM format from the buffer format";
return false;
}
if (num_planes > std::size(descriptor.objects)) {
LOG(ERROR) << "Too many planes in the NativePixmap; got " << num_planes
<< " but the maximum number is "
<< std::size(descriptor.objects);
return false;
}
static_assert(std::size(VADRMPRIMESurfaceDescriptor{}.layers) ==
std::size(VADRMPRIMESurfaceDescriptor{}.objects));
static_assert(
std::size(VADRMPRIMESurfaceDescriptor{}.layers[0].object_index) ==
std::size(VADRMPRIMESurfaceDescriptor{}.objects));
static_assert(std::size(VADRMPRIMESurfaceDescriptor{}.layers[0].offset) ==
std::size(VADRMPRIMESurfaceDescriptor{}.objects));
static_assert(std::size(VADRMPRIMESurfaceDescriptor{}.layers[0].pitch) ==
std::size(VADRMPRIMESurfaceDescriptor{}.objects));
descriptor.fourcc = va_fourcc;
descriptor.width = base::checked_cast<uint32_t>(size.width());
descriptor.height = base::checked_cast<uint32_t>(size.height());
// We can pass the planes as separate layers or all in one layer. The choice
// of doing the latter was arbitrary.
descriptor.num_layers = 1u;
descriptor.layers[0].drm_format = base::checked_cast<uint32_t>(drm_fourcc);
descriptor.layers[0].num_planes = base::checked_cast<uint32_t>(num_planes);
descriptor.num_objects = base::checked_cast<uint32_t>(num_planes);
for (size_t i = 0u; i < num_planes; i++) {
const int dma_buf_fd = pixmap.GetDmaBufFd(i);
if (dma_buf_fd < 0) {
LOG(ERROR) << "Failed to get dmabuf from an Ozone NativePixmap";
return false;
}
const off_t data_size = lseek(dma_buf_fd, /*offset=*/0, SEEK_END);
if (data_size == static_cast<off_t>(-1)) {
PLOG(ERROR) << "Failed to get the size of the dma-buf";
return false;
}
if (lseek(dma_buf_fd, /*offset=*/0, SEEK_SET) == static_cast<off_t>(-1)) {
PLOG(ERROR) << "Failed to reset the file offset of the dma-buf";
return false;
}
descriptor.objects[i].fd = dma_buf_fd;
descriptor.objects[i].size = base::checked_cast<uint32_t>(data_size);
descriptor.objects[i].drm_format_modifier =
pixmap.GetBufferFormatModifier();
descriptor.layers[0].object_index[i] = base::checked_cast<uint32_t>(i);
if (!base::IsValueInRangeForNumericType<uint32_t>(
pixmap.GetDmaBufOffset(i))) {
LOG(ERROR) << "The offset for plane " << i << " is out-of-range";
return false;
}
descriptor.layers[0].offset[i] =
base::checked_cast<uint32_t>(pixmap.GetDmaBufOffset(i));
descriptor.layers[0].pitch[i] = pixmap.GetDmaBufPitch(i);
}
return true;
}
struct VASurfaceAttribExternalBuffersAndFD {
VASurfaceAttribExternalBuffers va_attrib_extbuf;
uintptr_t fd;
};
bool FillVASurfaceAttribExternalBuffers(
const gfx::NativePixmap& pixmap,
VASurfaceAttribExternalBuffersAndFD& va_attrib_extbuf_and_fd) {
VASurfaceAttribExternalBuffers& va_attrib_extbuf =
va_attrib_extbuf_and_fd.va_attrib_extbuf;
memset(&va_attrib_extbuf_and_fd, 0,
sizeof(VASurfaceAttribExternalBuffersAndFD));
const uint32_t va_fourcc = BufferFormatToVAFourCC(pixmap.GetBufferFormat());
DCHECK(va_fourcc);
const gfx::Size size = pixmap.GetBufferSize();
const size_t num_planes = pixmap.GetNumberOfPlanes();
va_attrib_extbuf.pixel_format = va_fourcc;
va_attrib_extbuf.width = base::checked_cast<uint32_t>(size.width());
va_attrib_extbuf.height = base::checked_cast<uint32_t>(size.height());
static_assert(std::size(VASurfaceAttribExternalBuffers{}.pitches) ==
std::size(VASurfaceAttribExternalBuffers{}.offsets));
if (num_planes > std::size(va_attrib_extbuf.pitches)) {
LOG(ERROR) << "Too many planes in the NativePixmap; got " << num_planes
<< " but the maximum number is "
<< std::size(va_attrib_extbuf.pitches);
return false;
}
for (size_t i = 0; i < num_planes; ++i) {
va_attrib_extbuf.pitches[i] = pixmap.GetDmaBufPitch(i);
va_attrib_extbuf.offsets[i] =
base::checked_cast<uint32_t>(pixmap.GetDmaBufOffset(i));
DVLOG(4) << "plane " << i << ": pitch: " << va_attrib_extbuf.pitches[i]
<< " offset: " << va_attrib_extbuf.offsets[i];
}
va_attrib_extbuf.num_planes = base::checked_cast<uint32_t>(num_planes);
const int dma_buf_fd = pixmap.GetDmaBufFd(0);
if (dma_buf_fd < 0) {
LOG(ERROR) << "Failed to get dmabuf from an Ozone NativePixmap";
return false;
}
const off_t data_size = lseek(dma_buf_fd, /*offset=*/0, SEEK_END);
if (data_size == static_cast<off_t>(-1)) {
PLOG(ERROR) << "Failed to get the size of the dma-buf";
return false;
}
if (lseek(dma_buf_fd, /*offset=*/0, SEEK_SET) == static_cast<off_t>(-1)) {
PLOG(ERROR) << "Failed to reset the file offset of the dma-buf";
return false;
}
// If the data size doesn't fit in a uint32_t, we probably have bigger
// problems.
va_attrib_extbuf.data_size = base::checked_cast<uint32_t>(data_size);
// We only have to pass the first file descriptor to a driver. A VA-API driver
// shall create a VASurface from the single fd correctly.
va_attrib_extbuf_and_fd.fd = base::checked_cast<uintptr_t>(dma_buf_fd);
va_attrib_extbuf.buffers = &va_attrib_extbuf_and_fd.fd;
va_attrib_extbuf.num_buffers = 1u;
DCHECK_EQ(va_attrib_extbuf.flags, 0u);
DCHECK_EQ(va_attrib_extbuf.private_data, nullptr);
return true;
}
} // namespace
namespace media {
@ -981,6 +1129,10 @@ class VASupportedProfiles {
// Determines if |mode| supports |va_profile| (and |va_entrypoint| if defined
// and valid). If so, returns a const pointer to its ProfileInfo, otherwise
// returns nullptr.
// TODO(hiroh): If VAEntrypoint is kVAEntrypointInvalid, the default entry
// point acquired by GetDefaultVaEntryPoint() is used. If the default entry
// point is not supported, the earlier supported entrypoint in
// |kAllowedEntryPopints| is used.
const ProfileInfo* IsProfileSupported(
VaapiWrapper::CodecMode mode,
VAProfile va_profile,
@ -1641,26 +1793,18 @@ bool VaapiWrapper::IsDecodingSupportedForInternalFormat(
}
// static
bool VaapiWrapper::GetDecodeMinResolution(VAProfile va_profile,
gfx::Size* min_size) {
bool VaapiWrapper::GetSupportedResolutions(VAProfile va_profile,
CodecMode codec_mode,
gfx::Size& min_size,
gfx::Size& max_size) {
const VASupportedProfiles::ProfileInfo* profile_info =
VASupportedProfiles::Get().IsProfileSupported(kDecode, va_profile);
if (!profile_info)
return false;
*min_size = gfx::Size(std::max(1, profile_info->min_resolution.width()),
std::max(1, profile_info->min_resolution.height()));
return true;
}
// static
bool VaapiWrapper::GetDecodeMaxResolution(VAProfile va_profile,
gfx::Size* max_size) {
const VASupportedProfiles::ProfileInfo* profile_info =
VASupportedProfiles::Get().IsProfileSupported(kDecode, va_profile);
if (!profile_info)
VASupportedProfiles::Get().IsProfileSupported(codec_mode, va_profile);
if (!profile_info || profile_info->max_resolution.IsEmpty())
return false;
*max_size = profile_info->max_resolution;
min_size = gfx::Size(std::max(1, profile_info->min_resolution.width()),
std::max(1, profile_info->min_resolution.height()));
max_size = profile_info->max_resolution;
return true;
}
@ -2178,65 +2322,31 @@ scoped_refptr<VASurface> VaapiWrapper::CreateVASurfaceForPixmap(
CHECK(!enforce_sequence_affinity_ ||
sequence_checker_.CalledOnValidSequence());
const gfx::BufferFormat buffer_format = pixmap->GetBufferFormat();
const uint32_t va_fourcc = BufferFormatToVAFourCC(buffer_format);
if (!va_fourcc) {
if (!BufferFormatToVAFourCC(buffer_format)) {
LOG(ERROR) << "Failed to get the VA fourcc from the buffer format";
return nullptr;
}
const size_t num_planes = pixmap->GetNumberOfPlanes();
// TODO(b/233894465): use the DRM_PRIME_2 API with the Mesa Gallium driver
// when AMD supports it.
// TODO(b/233924862): use the DRM_PRIME_2 API with protected content.
// TODO(b/233929647): use the DRM_PRIME_2 API with the i965 driver.
const bool use_drm_prime_2 =
GetImplementationType() == VAImplementation::kIntelIHD &&
!protected_content;
// Create a VASurface for a NativePixmap by importing the underlying dmabufs.
const gfx::Size size = pixmap->GetBufferSize();
VASurfaceAttribExternalBuffers va_attrib_extbuf{};
va_attrib_extbuf.pixel_format = va_fourcc;
va_attrib_extbuf.width = base::checked_cast<uint32_t>(size.width());
va_attrib_extbuf.height = base::checked_cast<uint32_t>(size.height());
union {
VADRMPRIMESurfaceDescriptor descriptor;
VASurfaceAttribExternalBuffersAndFD va_attrib_extbuf_and_fd;
};
static_assert(std::size(va_attrib_extbuf.pitches) ==
std::size(va_attrib_extbuf.offsets));
if (num_planes > std::size(va_attrib_extbuf.pitches)) {
LOG(ERROR) << "Too many planes in the NativePixmap; got " << num_planes
<< " but the maximum number is "
<< std::size(va_attrib_extbuf.pitches);
return nullptr;
if (use_drm_prime_2) {
if (!FillVADRMPRIMESurfaceDescriptor(*pixmap, descriptor))
return nullptr;
} else {
if (!FillVASurfaceAttribExternalBuffers(*pixmap, va_attrib_extbuf_and_fd))
return nullptr;
}
for (size_t i = 0; i < num_planes; ++i) {
va_attrib_extbuf.pitches[i] = pixmap->GetDmaBufPitch(i);
va_attrib_extbuf.offsets[i] =
base::checked_cast<uint32_t>(pixmap->GetDmaBufOffset(i));
DVLOG(4) << "plane " << i << ": pitch: " << va_attrib_extbuf.pitches[i]
<< " offset: " << va_attrib_extbuf.offsets[i];
}
va_attrib_extbuf.num_planes = base::checked_cast<uint32_t>(num_planes);
const int dma_buf_fd = pixmap->GetDmaBufFd(0);
if (dma_buf_fd < 0) {
LOG(ERROR) << "Failed to get dmabuf from an Ozone NativePixmap";
return nullptr;
}
const off_t data_size = lseek(dma_buf_fd, /*offset=*/0, SEEK_END);
if (data_size == static_cast<off_t>(-1)) {
PLOG(ERROR) << "Failed to get the size of the dma-buf";
return nullptr;
}
if (lseek(dma_buf_fd, /*offset=*/0, SEEK_SET) == static_cast<off_t>(-1)) {
PLOG(ERROR) << "Failed to reset the file offset of the dma-buf";
return nullptr;
}
// If the data size doesn't fit in a uint32_t, we probably have bigger
// problems.
va_attrib_extbuf.data_size = base::checked_cast<uint32_t>(data_size);
// We only have to pass the first file descriptor to a driver. A VA-API driver
// shall create a VASurface from the single fd correctly.
uintptr_t fd = base::checked_cast<uintptr_t>(dma_buf_fd);
va_attrib_extbuf.buffers = &fd;
va_attrib_extbuf.num_buffers = 1u;
DCHECK_EQ(va_attrib_extbuf.flags, 0u);
DCHECK_EQ(va_attrib_extbuf.private_data, nullptr);
unsigned int va_format =
base::strict_cast<unsigned int>(BufferFormatToVARTFormat(buffer_format));
@ -2246,10 +2356,12 @@ scoped_refptr<VASurface> VaapiWrapper::CreateVASurfaceForPixmap(
}
if (protected_content) {
if (GetImplementationType() == VAImplementation::kMesaGallium)
if (GetImplementationType() == VAImplementation::kMesaGallium) {
va_format |= VA_RT_FORMAT_PROTECTED;
else
va_attrib_extbuf.flags = VA_SURFACE_EXTBUF_DESC_PROTECTED;
} else {
va_attrib_extbuf_and_fd.va_attrib_extbuf.flags =
VA_SURFACE_EXTBUF_DESC_PROTECTED;
}
}
std::vector<VASurfaceAttrib> va_attribs(2);
@ -2257,13 +2369,18 @@ scoped_refptr<VASurface> VaapiWrapper::CreateVASurfaceForPixmap(
va_attribs[0].type = VASurfaceAttribMemoryType;
va_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE;
va_attribs[0].value.type = VAGenericValueTypeInteger;
va_attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
va_attribs[0].value.value.i = use_drm_prime_2
? VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2
: VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
va_attribs[1].type = VASurfaceAttribExternalBufferDescriptor;
va_attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE;
va_attribs[1].value.type = VAGenericValueTypePointer;
va_attribs[1].value.value.p = &va_attrib_extbuf;
va_attribs[1].value.value.p = use_drm_prime_2
? static_cast<void*>(&descriptor)
: &va_attrib_extbuf_and_fd.va_attrib_extbuf;
const gfx::Size size = pixmap->GetBufferSize();
VASurfaceID va_surface_id = VA_INVALID_ID;
{
base::AutoLockMaybe auto_lock(va_lock_);