Commit 301fe942 authored by Gil Dekel's avatar Gil Dekel Committed by Commit Bot

media/gpu: ScopedVASurface in VaapiImageDecoder

This CL refactors VaapiImageDecoder to use
std::unique_ptr<ScopedVASurface>, together with a custom deleter to
destroy the associated context. This ensures the decoder owns and
properly clean the underlying VA surface and VA context. This is useful
because it removes clean-up responsibilities from clients of
ScopedVASurface.

Unfortunately, we have noticed that VaapiWrapper would reduce the manual
ref count of the VaDisplayState upon its demise, which may cause some
ScopedVASurfaces with stale copies of VADisplay to not be able to
self-clean. Therefore this CL introduces a (hopefully) temporary
modification to ScopedVASurface in which it holds a scoped_refptr to the
VaapiWrapper that created it and uses it to self clean.

In addition, the API of image decoders is changed slightly. A
scoped_refptr<VASurface> is no longer returned upon Decode() requests.
Should a client need access to the ScopedVASurface, the API exposes a
GetScopedVASurface(), which returns a const raw pointer to the decoder's
ScopedVASurface. This should feel familiar due to the get() call in
unique_ptrs.

Finally, this CL fixes some of the call-sites that use VaapiJpegDecoder
(i.e. VaapiImageDecoder's new API).

Bug: 877694, 980379
Test: jpeg_decode_accelerator_unittest
Change-Id: I834c3af64813e3004f8a62da4e2ef529585be465
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1696012
Commit-Queue: Gil Dekel <gildekel@chromium.org>
Reviewed-by: default avatarAndres Calderon Jaramillo <andrescj@chromium.org>
Reviewed-by: default avatarMiguel Casas <mcasas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#677035}
parent 52c285e3
...@@ -4,15 +4,24 @@ ...@@ -4,15 +4,24 @@
#include "media/gpu/vaapi/vaapi_image_decoder.h" #include "media/gpu/vaapi/vaapi_image_decoder.h"
#include <utility>
#include "base/logging.h" #include "base/logging.h"
#include "media/gpu/macros.h" #include "media/gpu/macros.h"
#include "media/gpu/vaapi/va_surface.h" #include "media/gpu/vaapi/va_surface.h"
#include "media/gpu/vaapi/vaapi_utils.h"
#include "media/gpu/vaapi/vaapi_wrapper.h" #include "media/gpu/vaapi/vaapi_wrapper.h"
#include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size.h"
#include "ui/gfx/linux/native_pixmap_dmabuf.h" #include "ui/gfx/linux/native_pixmap_dmabuf.h"
namespace media { namespace media {
void VAContextAndScopedVASurfaceDeleter::operator()(
ScopedVASurface* scoped_va_surface) const {
scoped_va_surface->vaapi_wrapper_->DestroyContext();
delete scoped_va_surface;
}
VaapiImageDecoder::VaapiImageDecoder(VAProfile va_profile) VaapiImageDecoder::VaapiImageDecoder(VAProfile va_profile)
: va_profile_(va_profile) {} : va_profile_(va_profile) {}
...@@ -24,6 +33,34 @@ bool VaapiImageDecoder::Initialize(const base::RepeatingClosure& error_uma_cb) { ...@@ -24,6 +33,34 @@ bool VaapiImageDecoder::Initialize(const base::RepeatingClosure& error_uma_cb) {
return !!vaapi_wrapper_; return !!vaapi_wrapper_;
} }
VaapiImageDecodeStatus VaapiImageDecoder::Decode(
base::span<const uint8_t> encoded_image) {
if (!vaapi_wrapper_) {
VLOGF(1) << "VaapiImageDecoder has not been initialized";
scoped_va_context_and_surface_.reset();
return VaapiImageDecodeStatus::kInvalidState;
}
const VaapiImageDecodeStatus status =
AllocateVASurfaceAndSubmitVABuffers(encoded_image);
if (status != VaapiImageDecodeStatus::kSuccess) {
scoped_va_context_and_surface_.reset();
return status;
}
if (!vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(
scoped_va_context_and_surface_->id())) {
VLOGF(1) << "ExecuteAndDestroyPendingBuffers() failed";
scoped_va_context_and_surface_.reset();
return VaapiImageDecodeStatus::kExecuteDecodeFailed;
}
return VaapiImageDecodeStatus::kSuccess;
}
const ScopedVASurface* VaapiImageDecoder::GetScopedVASurface() const {
return scoped_va_context_and_surface_.get();
}
gpu::ImageDecodeAcceleratorSupportedProfile gpu::ImageDecodeAcceleratorSupportedProfile
VaapiImageDecoder::GetSupportedProfile() const { VaapiImageDecoder::GetSupportedProfile() const {
if (!vaapi_wrapper_) { if (!vaapi_wrapper_) {
...@@ -56,21 +93,24 @@ scoped_refptr<gfx::NativePixmapDmaBuf> ...@@ -56,21 +93,24 @@ scoped_refptr<gfx::NativePixmapDmaBuf>
VaapiImageDecoder::ExportAsNativePixmapDmaBuf(VaapiImageDecodeStatus* status) { VaapiImageDecoder::ExportAsNativePixmapDmaBuf(VaapiImageDecodeStatus* status) {
DCHECK(status); DCHECK(status);
// We need to take ownership of the VASurface so that the next decode doesn't // We need to take ownership of the ScopedVASurface so that the next Decode()
// attempt to use the same VASurface. Otherwise, it could overwrite the result // doesn't attempt to use the same ScopedVASurface. Otherwise, it could
// of the current decode before it's used by the caller. This VASurface will // overwrite the result of the current decode before it's used by the caller.
// self-clean at the end of this scope, but the underlying buffer should stay // This ScopedVASurface will self-clean at the end of this scope, but the
// alive because of the exported FDs. // underlying buffer should stay alive because of the exported FDs.
scoped_refptr<VASurface> va_surface = ReleaseVASurface(); ScopedVAContextAndSurface temp_scoped_va_surface =
if (!va_surface) { std::move(scoped_va_context_and_surface_);
if (!temp_scoped_va_surface) {
DVLOGF(1) << "No decoded image available"; DVLOGF(1) << "No decoded image available";
*status = VaapiImageDecodeStatus::kInvalidState; *status = VaapiImageDecodeStatus::kInvalidState;
return nullptr; return nullptr;
} }
DCHECK(temp_scoped_va_surface->IsValid());
DCHECK_NE(VA_INVALID_ID, va_surface->id());
scoped_refptr<gfx::NativePixmapDmaBuf> pixmap = scoped_refptr<gfx::NativePixmapDmaBuf> pixmap =
vaapi_wrapper_->ExportVASurfaceAsNativePixmapDmaBuf(va_surface->id()); vaapi_wrapper_->ExportVASurfaceAsNativePixmapDmaBuf(
temp_scoped_va_surface->id());
if (!pixmap) { if (!pixmap) {
*status = VaapiImageDecodeStatus::kCannotExportSurface; *status = VaapiImageDecodeStatus::kCannotExportSurface;
return nullptr; return nullptr;
...@@ -79,8 +119,10 @@ VaapiImageDecoder::ExportAsNativePixmapDmaBuf(VaapiImageDecodeStatus* status) { ...@@ -79,8 +119,10 @@ VaapiImageDecoder::ExportAsNativePixmapDmaBuf(VaapiImageDecodeStatus* status) {
// In Intel's iHD driver the size requested for the surface may be different // In Intel's iHD driver the size requested for the surface may be different
// than the buffer size of the NativePixmap because of additional alignment. // than the buffer size of the NativePixmap because of additional alignment.
// See https://git.io/fj6nA. // See https://git.io/fj6nA.
DCHECK_LE(va_surface->size().width(), pixmap->GetBufferSize().width()); DCHECK_LE(temp_scoped_va_surface->size().width(),
DCHECK_LE(va_surface->size().height(), pixmap->GetBufferSize().height()); pixmap->GetBufferSize().width());
DCHECK_LE(temp_scoped_va_surface->size().height(),
pixmap->GetBufferSize().height());
*status = VaapiImageDecodeStatus::kSuccess; *status = VaapiImageDecodeStatus::kSuccess;
return pixmap; return pixmap;
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include <stdint.h> #include <stdint.h>
#include <memory>
#include <va/va.h> #include <va/va.h>
#include "base/callback_forward.h" #include "base/callback_forward.h"
...@@ -21,9 +23,16 @@ class NativePixmapDmaBuf; ...@@ -21,9 +23,16 @@ class NativePixmapDmaBuf;
namespace media { namespace media {
class VASurface; class ScopedVASurface;
class VaapiWrapper; class VaapiWrapper;
struct VAContextAndScopedVASurfaceDeleter {
void operator()(ScopedVASurface* scoped_va_surface) const;
};
using ScopedVAContextAndSurface =
std::unique_ptr<ScopedVASurface, VAContextAndScopedVASurfaceDeleter>;
enum class VaapiImageDecodeStatus : uint32_t { enum class VaapiImageDecodeStatus : uint32_t {
kSuccess, kSuccess,
kParseFailed, kParseFailed,
...@@ -54,13 +63,15 @@ class VaapiImageDecoder { ...@@ -54,13 +63,15 @@ class VaapiImageDecoder {
// Decodes a picture. It will fill VA-API parameters and call the // Decodes a picture. It will fill VA-API parameters and call the
// corresponding VA-API methods according to the image in |encoded_image|. // corresponding VA-API methods according to the image in |encoded_image|.
// The image will be decoded into an internally allocated VA surface. It // The image will be decoded into an internally allocated ScopedVASurface.
// will be returned as an unowned VASurface, which remains valid until the // This VA surface will remain valid until the next Decode() call or
// next Decode() call or destruction of this class. Returns nullptr on // destruction of this class. Returns a VaapiImageDecodeStatus that will
// failure and sets *|status| to the reason for failure. // indicate whether the decode succeeded or the reason it failed. Note that
virtual scoped_refptr<VASurface> Decode( // the internal ScopedVASurface is destroyed on failure.
base::span<const uint8_t> encoded_image, VaapiImageDecodeStatus Decode(base::span<const uint8_t> encoded_image);
VaapiImageDecodeStatus* status) = 0;
// Returns a pointer to the internally managed ScopedVASurface.
const ScopedVASurface* GetScopedVASurface() const;
// Returns the type of image supported by this decoder. // Returns the type of image supported by this decoder.
virtual gpu::ImageDecodeAcceleratorType GetType() const = 0; virtual gpu::ImageDecodeAcceleratorType GetType() const = 0;
...@@ -78,10 +89,14 @@ class VaapiImageDecoder { ...@@ -78,10 +89,14 @@ class VaapiImageDecoder {
protected: protected:
explicit VaapiImageDecoder(VAProfile va_profile); explicit VaapiImageDecoder(VAProfile va_profile);
// Transfers ownership of the VASurface that contains the result of the last // Submits an image to the VA-API by filling its parameters and calling on the
// decode to the caller. The returned VASurface is self-cleaning. // corresponding methods according to the image in |encoded_image|. Returns a
// Returns a nullptr on failure. // VaapiImageDecodeStatus that will indicate whether the operation succeeded
virtual scoped_refptr<VASurface> ReleaseVASurface() = 0; // or the reason it failed.
virtual VaapiImageDecodeStatus AllocateVASurfaceAndSubmitVABuffers(
base::span<const uint8_t> encoded_image) = 0;
ScopedVAContextAndSurface scoped_va_context_and_surface_;
scoped_refptr<VaapiWrapper> vaapi_wrapper_; scoped_refptr<VaapiWrapper> vaapi_wrapper_;
......
...@@ -55,10 +55,8 @@ void DecodeTask( ...@@ -55,10 +55,8 @@ void DecodeTask(
mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(decode_cb), mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(decode_cb),
nullptr); nullptr);
DCHECK(decoder); DCHECK(decoder);
VaapiImageDecodeStatus status; VaapiImageDecodeStatus status = decoder->Decode(
decoder->Decode( base::make_span<const uint8_t>(encoded_data.data(), encoded_data.size()));
base::make_span<const uint8_t>(encoded_data.data(), encoded_data.size()),
&status);
if (status != VaapiImageDecodeStatus::kSuccess) { if (status != VaapiImageDecodeStatus::kSuccess) {
DVLOGF(1) << "Failed to decode - status = " DVLOGF(1) << "Failed to decode - status = "
<< static_cast<uint32_t>(status); << static_cast<uint32_t>(status);
......
...@@ -8,21 +8,18 @@ ...@@ -8,21 +8,18 @@
#include <iostream> #include <iostream>
#include <type_traits> #include <type_traits>
#include <vector>
#include <va/va.h> #include <va/va.h>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "media/base/video_types.h" #include "media/base/video_types.h"
#include "media/gpu/macros.h" #include "media/gpu/macros.h"
#include "media/gpu/vaapi/va_surface.h"
#include "media/gpu/vaapi/vaapi_utils.h" #include "media/gpu/vaapi/vaapi_utils.h"
#include "media/gpu/vaapi/vaapi_wrapper.h" #include "media/gpu/vaapi/vaapi_wrapper.h"
#include "media/parsers/jpeg_parser.h" #include "media/parsers/jpeg_parser.h"
#include "ui/gfx/geometry/size.h"
namespace media { namespace media {
...@@ -214,33 +211,20 @@ unsigned int VaSurfaceFormatForJpeg(const JpegFrameHeader& frame_header) { ...@@ -214,33 +211,20 @@ unsigned int VaSurfaceFormatForJpeg(const JpegFrameHeader& frame_header) {
} }
VaapiJpegDecoder::VaapiJpegDecoder() VaapiJpegDecoder::VaapiJpegDecoder()
: VaapiImageDecoder(VAProfileJPEGBaseline), : VaapiImageDecoder(VAProfileJPEGBaseline) {}
va_surface_id_(VA_INVALID_SURFACE),
va_rt_format_(kInvalidVaRtFormat) {}
VaapiJpegDecoder::~VaapiJpegDecoder() {
if (vaapi_wrapper_) {
vaapi_wrapper_->DestroyContextAndSurfaces(
std::vector<VASurfaceID>({va_surface_id_}));
}
}
scoped_refptr<VASurface> VaapiJpegDecoder::Decode( VaapiJpegDecoder::~VaapiJpegDecoder() = default;
base::span<const uint8_t> encoded_image,
VaapiImageDecodeStatus* status) { VaapiImageDecodeStatus VaapiJpegDecoder::AllocateVASurfaceAndSubmitVABuffers(
if (!vaapi_wrapper_) { base::span<const uint8_t> encoded_image) {
VLOGF(1) << "VaapiJpegDecoder has not been initialized"; DCHECK(vaapi_wrapper_);
*status = VaapiImageDecodeStatus::kInvalidState;
return nullptr;
}
// Parse the JPEG encoded data. // Parse the JPEG encoded data.
JpegParseResult parse_result; JpegParseResult parse_result;
if (!ParseJpegPicture(encoded_image.data(), encoded_image.size(), if (!ParseJpegPicture(encoded_image.data(), encoded_image.size(),
&parse_result)) { &parse_result)) {
VLOGF(1) << "ParseJpegPicture failed"; VLOGF(1) << "ParseJpegPicture failed";
*status = VaapiImageDecodeStatus::kParseFailed; return VaapiImageDecodeStatus::kParseFailed;
return nullptr;
} }
// Figure out the right format for the VaSurface. // Figure out the right format for the VaSurface.
...@@ -248,37 +232,35 @@ scoped_refptr<VASurface> VaapiJpegDecoder::Decode( ...@@ -248,37 +232,35 @@ scoped_refptr<VASurface> VaapiJpegDecoder::Decode(
VaSurfaceFormatForJpeg(parse_result.frame_header); VaSurfaceFormatForJpeg(parse_result.frame_header);
if (picture_va_rt_format == kInvalidVaRtFormat) { if (picture_va_rt_format == kInvalidVaRtFormat) {
VLOGF(1) << "Unsupported subsampling"; VLOGF(1) << "Unsupported subsampling";
*status = VaapiImageDecodeStatus::kUnsupportedSubsampling; return VaapiImageDecodeStatus::kUnsupportedSubsampling;
return nullptr;
} }
// Make sure this JPEG can be decoded. // Make sure this JPEG can be decoded.
if (!IsVaapiSupportedJpeg(parse_result)) { if (!IsVaapiSupportedJpeg(parse_result)) {
VLOGF(1) << "The supplied JPEG is unsupported"; VLOGF(1) << "The supplied JPEG is unsupported";
*status = VaapiImageDecodeStatus::kUnsupportedImage; return VaapiImageDecodeStatus::kUnsupportedImage;
return nullptr;
} }
// Prepare the VaSurface for decoding. // Prepare the VaSurface for decoding.
const gfx::Size new_coded_size( const gfx::Size new_coded_size(
base::strict_cast<int>(parse_result.frame_header.coded_width), base::strict_cast<int>(parse_result.frame_header.coded_width),
base::strict_cast<int>(parse_result.frame_header.coded_height)); base::strict_cast<int>(parse_result.frame_header.coded_height));
if (new_coded_size != coded_size_ || va_surface_id_ == VA_INVALID_SURFACE || DCHECK(!scoped_va_context_and_surface_ ||
picture_va_rt_format != va_rt_format_) { scoped_va_context_and_surface_->IsValid());
vaapi_wrapper_->DestroyContextAndSurfaces( if (!scoped_va_context_and_surface_ ||
std::vector<VASurfaceID>({va_surface_id_})); new_coded_size != scoped_va_context_and_surface_->size() ||
va_surface_id_ = VA_INVALID_SURFACE; picture_va_rt_format != scoped_va_context_and_surface_->format()) {
va_rt_format_ = picture_va_rt_format; scoped_va_context_and_surface_.reset();
scoped_va_context_and_surface_ =
std::vector<VASurfaceID> va_surfaces; ScopedVAContextAndSurface(vaapi_wrapper_
if (!vaapi_wrapper_->CreateContextAndSurfaces(va_rt_format_, new_coded_size, ->CreateContextAndScopedVASurface(
1, &va_surfaces)) { picture_va_rt_format, new_coded_size)
VLOGF(1) << "Could not create the context or the surface"; .release());
*status = VaapiImageDecodeStatus::kSurfaceCreationFailed; if (!scoped_va_context_and_surface_) {
return nullptr; VLOGF(1) << "CreateContextAndScopedVASurface() failed";
return VaapiImageDecodeStatus::kSurfaceCreationFailed;
} }
va_surface_id_ = va_surfaces[0]; DCHECK(scoped_va_context_and_surface_->IsValid());
coded_size_ = new_coded_size;
} }
// Set picture parameters. // Set picture parameters.
...@@ -286,8 +268,7 @@ scoped_refptr<VASurface> VaapiJpegDecoder::Decode( ...@@ -286,8 +268,7 @@ scoped_refptr<VASurface> VaapiJpegDecoder::Decode(
FillPictureParameters(parse_result.frame_header, &pic_param); FillPictureParameters(parse_result.frame_header, &pic_param);
if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, &pic_param)) { if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, &pic_param)) {
VLOGF(1) << "Could not submit VAPictureParameterBufferType"; VLOGF(1) << "Could not submit VAPictureParameterBufferType";
*status = VaapiImageDecodeStatus::kSubmitVABuffersFailed; return VaapiImageDecodeStatus::kSubmitVABuffersFailed;
return nullptr;
} }
// Set quantization table. // Set quantization table.
...@@ -295,8 +276,7 @@ scoped_refptr<VASurface> VaapiJpegDecoder::Decode( ...@@ -295,8 +276,7 @@ scoped_refptr<VASurface> VaapiJpegDecoder::Decode(
FillIQMatrix(parse_result.q_table, &iq_matrix); FillIQMatrix(parse_result.q_table, &iq_matrix);
if (!vaapi_wrapper_->SubmitBuffer(VAIQMatrixBufferType, &iq_matrix)) { if (!vaapi_wrapper_->SubmitBuffer(VAIQMatrixBufferType, &iq_matrix)) {
VLOGF(1) << "Could not submit VAIQMatrixBufferType"; VLOGF(1) << "Could not submit VAIQMatrixBufferType";
*status = VaapiImageDecodeStatus::kSubmitVABuffersFailed; return VaapiImageDecodeStatus::kSubmitVABuffersFailed;
return nullptr;
} }
// Set huffman table. // Set huffman table.
...@@ -305,8 +285,7 @@ scoped_refptr<VASurface> VaapiJpegDecoder::Decode( ...@@ -305,8 +285,7 @@ scoped_refptr<VASurface> VaapiJpegDecoder::Decode(
&huffman_table); &huffman_table);
if (!vaapi_wrapper_->SubmitBuffer(VAHuffmanTableBufferType, &huffman_table)) { if (!vaapi_wrapper_->SubmitBuffer(VAHuffmanTableBufferType, &huffman_table)) {
VLOGF(1) << "Could not submit VAHuffmanTableBufferType"; VLOGF(1) << "Could not submit VAHuffmanTableBufferType";
*status = VaapiImageDecodeStatus::kSubmitVABuffersFailed; return VaapiImageDecodeStatus::kSubmitVABuffersFailed;
return nullptr;
} }
// Set slice parameters. // Set slice parameters.
...@@ -314,8 +293,7 @@ scoped_refptr<VASurface> VaapiJpegDecoder::Decode( ...@@ -314,8 +293,7 @@ scoped_refptr<VASurface> VaapiJpegDecoder::Decode(
FillSliceParameters(parse_result, &slice_param); FillSliceParameters(parse_result, &slice_param);
if (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, &slice_param)) { if (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, &slice_param)) {
VLOGF(1) << "Could not submit VASliceParameterBufferType"; VLOGF(1) << "Could not submit VASliceParameterBufferType";
*status = VaapiImageDecodeStatus::kSubmitVABuffersFailed; return VaapiImageDecodeStatus::kSubmitVABuffersFailed;
return nullptr;
} }
// Set scan data. // Set scan data.
...@@ -323,21 +301,10 @@ scoped_refptr<VASurface> VaapiJpegDecoder::Decode( ...@@ -323,21 +301,10 @@ scoped_refptr<VASurface> VaapiJpegDecoder::Decode(
parse_result.data_size, parse_result.data_size,
const_cast<char*>(parse_result.data))) { const_cast<char*>(parse_result.data))) {
VLOGF(1) << "Could not submit VASliceDataBufferType"; VLOGF(1) << "Could not submit VASliceDataBufferType";
*status = VaapiImageDecodeStatus::kSubmitVABuffersFailed; return VaapiImageDecodeStatus::kSubmitVABuffersFailed;
return nullptr;
}
// Execute the decode.
if (!vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(va_surface_id_)) {
VLOGF(1) << "Executing the decode failed";
*status = VaapiImageDecodeStatus::kExecuteDecodeFailed;
return nullptr;
} }
*status = VaapiImageDecodeStatus::kSuccess; return VaapiImageDecodeStatus::kSuccess;
return base::MakeRefCounted<VASurface>(va_surface_id_, coded_size_,
va_rt_format_,
base::DoNothing() /* release_cb */);
} }
gpu::ImageDecodeAcceleratorType VaapiJpegDecoder::GetType() const { gpu::ImageDecodeAcceleratorType VaapiJpegDecoder::GetType() const {
...@@ -347,23 +314,26 @@ gpu::ImageDecodeAcceleratorType VaapiJpegDecoder::GetType() const { ...@@ -347,23 +314,26 @@ gpu::ImageDecodeAcceleratorType VaapiJpegDecoder::GetType() const {
std::unique_ptr<ScopedVAImage> VaapiJpegDecoder::GetImage( std::unique_ptr<ScopedVAImage> VaapiJpegDecoder::GetImage(
uint32_t preferred_image_fourcc, uint32_t preferred_image_fourcc,
VaapiImageDecodeStatus* status) { VaapiImageDecodeStatus* status) {
if (va_surface_id_ == VA_INVALID_ID) { if (!scoped_va_context_and_surface_) {
VLOGF(1) << "No decoded JPEG available"; VLOGF(1) << "No decoded JPEG available";
*status = VaapiImageDecodeStatus::kInvalidState; *status = VaapiImageDecodeStatus::kInvalidState;
return nullptr; return nullptr;
} }
DCHECK(scoped_va_context_and_surface_->IsValid());
DCHECK(vaapi_wrapper_); DCHECK(vaapi_wrapper_);
uint32_t image_fourcc; uint32_t image_fourcc;
if (!VaapiWrapper::GetJpegDecodeSuitableImageFourCC( if (!VaapiWrapper::GetJpegDecodeSuitableImageFourCC(
va_rt_format_, preferred_image_fourcc, &image_fourcc)) { scoped_va_context_and_surface_->format(), preferred_image_fourcc,
&image_fourcc)) {
VLOGF(1) << "Cannot determine the output FOURCC"; VLOGF(1) << "Cannot determine the output FOURCC";
*status = VaapiImageDecodeStatus::kCannotGetImage; *status = VaapiImageDecodeStatus::kCannotGetImage;
return nullptr; return nullptr;
} }
VAImageFormat image_format{.fourcc = image_fourcc}; VAImageFormat image_format{.fourcc = image_fourcc};
auto scoped_image = auto scoped_image = vaapi_wrapper_->CreateVaImage(
vaapi_wrapper_->CreateVaImage(va_surface_id_, &image_format, coded_size_); scoped_va_context_and_surface_->id(), &image_format,
scoped_va_context_and_surface_->size());
if (!scoped_image) { if (!scoped_image) {
VLOGF(1) << "Cannot get VAImage, FOURCC = " VLOGF(1) << "Cannot get VAImage, FOURCC = "
<< FourccToString(image_format.fourcc); << FourccToString(image_format.fourcc);
...@@ -375,28 +345,4 @@ std::unique_ptr<ScopedVAImage> VaapiJpegDecoder::GetImage( ...@@ -375,28 +345,4 @@ std::unique_ptr<ScopedVAImage> VaapiJpegDecoder::GetImage(
return scoped_image; return scoped_image;
} }
scoped_refptr<VASurface> VaapiJpegDecoder::ReleaseVASurface() {
if (va_surface_id_ == VA_INVALID_ID)
return nullptr;
DCHECK(vaapi_wrapper_);
// Prepare a new VASurface object. Note this VASurface will self-destruct.
auto va_surface = base::MakeRefCounted<VASurface>(
va_surface_id_, coded_size_, va_rt_format_,
base::BindOnce(&VaapiWrapper::DestroySurface, vaapi_wrapper_));
// Destroy the context. It is no longer needed since the only surface is going
// to be given away. A new surface and context will be created the next time
// Decode() is called
vaapi_wrapper_->DestroyContext();
// Invalidate the decoder's internal state.
va_surface_id_ = VA_INVALID_ID;
coded_size_.SetSize(0, 0);
va_rt_format_ = kInvalidVaRtFormat;
return va_surface;
}
} // namespace media } // namespace media
...@@ -9,10 +9,8 @@ ...@@ -9,10 +9,8 @@
#include <memory> #include <memory>
#include "base/gtest_prod_util.h"
#include "base/macros.h" #include "base/macros.h"
#include "media/gpu/vaapi/vaapi_image_decoder.h" #include "media/gpu/vaapi/vaapi_image_decoder.h"
#include "ui/gfx/geometry/size.h"
namespace media { namespace media {
...@@ -32,8 +30,6 @@ class VaapiJpegDecoder : public VaapiImageDecoder { ...@@ -32,8 +30,6 @@ class VaapiJpegDecoder : public VaapiImageDecoder {
~VaapiJpegDecoder() override; ~VaapiJpegDecoder() override;
// VaapiImageDecoder implementation. // VaapiImageDecoder implementation.
scoped_refptr<VASurface> Decode(base::span<const uint8_t> encoded_image,
VaapiImageDecodeStatus* status) override;
gpu::ImageDecodeAcceleratorType GetType() const override; gpu::ImageDecodeAcceleratorType GetType() const override;
// Get the decoded data from the last Decode() call as a ScopedVAImage. The // Get the decoded data from the last Decode() call as a ScopedVAImage. The
...@@ -45,17 +41,9 @@ class VaapiJpegDecoder : public VaapiImageDecoder { ...@@ -45,17 +41,9 @@ class VaapiJpegDecoder : public VaapiImageDecoder {
VaapiImageDecodeStatus* status); VaapiImageDecodeStatus* status);
protected: protected:
scoped_refptr<VASurface> ReleaseVASurface() override; // VaapiImageDecoder implementation.
VaapiImageDecodeStatus AllocateVASurfaceAndSubmitVABuffers(
private: base::span<const uint8_t> encoded_image) override;
FRIEND_TEST_ALL_PREFIXES(VaapiJpegDecoderTest, ReleaseVASurface);
// The current VA surface for decoding.
VASurfaceID va_surface_id_;
// The coded size associated with |va_surface_id_|.
gfx::Size coded_size_;
// The VA RT format associated with |va_surface_id_|.
unsigned int va_rt_format_;
DISALLOW_COPY_AND_ASSIGN(VaapiJpegDecoder); DISALLOW_COPY_AND_ASSIGN(VaapiJpegDecoder);
}; };
......
...@@ -390,10 +390,9 @@ std::unique_ptr<ScopedVAImage> VaapiJpegDecoderTest::Decode( ...@@ -390,10 +390,9 @@ std::unique_ptr<ScopedVAImage> VaapiJpegDecoderTest::Decode(
base::span<const uint8_t> encoded_image, base::span<const uint8_t> encoded_image,
uint32_t preferred_fourcc, uint32_t preferred_fourcc,
VaapiImageDecodeStatus* status) { VaapiImageDecodeStatus* status) {
VaapiImageDecodeStatus decode_status; const VaapiImageDecodeStatus decode_status = decoder_.Decode(encoded_image);
scoped_refptr<VASurface> surface = EXPECT_EQ(!!decoder_.GetScopedVASurface(),
decoder_.Decode(encoded_image, &decode_status); decode_status == VaapiImageDecodeStatus::kSuccess);
EXPECT_EQ(!!surface, decode_status == VaapiImageDecodeStatus::kSuccess);
// Still try to get image when decode fails. // Still try to get image when decode fails.
VaapiImageDecodeStatus image_status; VaapiImageDecodeStatus image_status;
...@@ -419,10 +418,9 @@ scoped_refptr<gfx::NativePixmapDmaBuf> ...@@ -419,10 +418,9 @@ scoped_refptr<gfx::NativePixmapDmaBuf>
VaapiJpegDecoderTest::DecodeToNativePixmapDmaBuf( VaapiJpegDecoderTest::DecodeToNativePixmapDmaBuf(
base::span<const uint8_t> encoded_image, base::span<const uint8_t> encoded_image,
VaapiImageDecodeStatus* status) { VaapiImageDecodeStatus* status) {
VaapiImageDecodeStatus decode_status; const VaapiImageDecodeStatus decode_status = decoder_.Decode(encoded_image);
scoped_refptr<VASurface> surface = EXPECT_EQ(!!decoder_.GetScopedVASurface(),
decoder_.Decode(encoded_image, &decode_status); decode_status == VaapiImageDecodeStatus::kSuccess);
EXPECT_EQ(!!surface, decode_status == VaapiImageDecodeStatus::kSuccess);
// Still try to get the pixmap when decode fails. // Still try to get the pixmap when decode fails.
VaapiImageDecodeStatus pixmap_status; VaapiImageDecodeStatus pixmap_status;
...@@ -464,52 +462,6 @@ TEST_F(VaapiJpegDecoderTest, MinimalImageFormatSupport) { ...@@ -464,52 +462,6 @@ TEST_F(VaapiJpegDecoderTest, MinimalImageFormatSupport) {
} }
} }
TEST_F(VaapiJpegDecoderTest, ReleaseVASurface) {
base::FilePath input_file = FindTestDataFilePath(kYuv420Filename);
std::string jpeg_data;
ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data))
<< "failed to read input data from " << input_file.value();
const auto encoded_image = base::make_span<const uint8_t>(
reinterpret_cast<const uint8_t*>(jpeg_data.data()), jpeg_data.size());
VAImageFormat i420_format{};
i420_format.fourcc = VA_FOURCC_I420;
EXPECT_TRUE(VaapiWrapper::IsImageFormatSupported(i420_format));
// Get the first VASurface and validate success.
VaapiImageDecodeStatus decode_status;
scoped_refptr<VASurface> pre_release_surface =
decoder_.Decode(encoded_image, &decode_status);
ASSERT_TRUE(pre_release_surface);
ASSERT_EQ(VaapiImageDecodeStatus::kSuccess, decode_status);
// Take ownership of the |decoder_| surface.
scoped_refptr<VASurface> post_release_surface = decoder_.ReleaseVASurface();
ASSERT_TRUE(post_release_surface);
EXPECT_EQ(pre_release_surface->id(), post_release_surface->id());
EXPECT_EQ(pre_release_surface->size(), post_release_surface->size());
EXPECT_EQ(pre_release_surface->format(), post_release_surface->format());
// GetImage() should fail now because ReleaseVASurface() removed the internal
// |decoder_| surface.
VaapiImageDecodeStatus image_status;
ASSERT_FALSE(decoder_.GetImage(i420_format.fourcc, &image_status));
ASSERT_EQ(VaapiImageDecodeStatus::kInvalidState, image_status);
// Decode the same image again and get the new VASurface.
scoped_refptr<VASurface> new_surface =
decoder_.Decode(encoded_image, &decode_status);
ASSERT_TRUE(new_surface);
ASSERT_EQ(VaapiImageDecodeStatus::kSuccess, decode_status);
// The new VASurface should have a different surface ID than that of the old
// one, but same size and format since the same exact image was decoded.
EXPECT_NE(new_surface->id(), post_release_surface->id());
EXPECT_EQ(new_surface->size(), post_release_surface->size());
EXPECT_EQ(new_surface->format(), post_release_surface->format());
}
TEST_P(VaapiJpegDecoderTest, DecodeSucceeds) { TEST_P(VaapiJpegDecoderTest, DecodeSucceeds) {
base::FilePath input_file = FindTestDataFilePath(GetParam().filename); base::FilePath input_file = FindTestDataFilePath(GetParam().filename);
std::string jpeg_data; std::string jpeg_data;
...@@ -563,6 +515,13 @@ TEST_P(VaapiJpegDecoderTest, DecodeSucceeds) { ...@@ -563,6 +515,13 @@ TEST_P(VaapiJpegDecoderTest, DecodeSucceeds) {
std::unique_ptr<ScopedVAImage> scoped_image = std::unique_ptr<ScopedVAImage> scoped_image =
Decode(encoded_image, image_format.fourcc); Decode(encoded_image, image_format.fourcc);
ASSERT_TRUE(scoped_image); ASSERT_TRUE(scoped_image);
ASSERT_TRUE(decoder_.GetScopedVASurface());
EXPECT_TRUE(decoder_.GetScopedVASurface()->IsValid());
EXPECT_EQ(decoder_.GetScopedVASurface()->size().width(),
base::strict_cast<int>(parse_result.frame_header.coded_width));
EXPECT_EQ(decoder_.GetScopedVASurface()->size().height(),
base::strict_cast<int>(parse_result.frame_header.coded_height));
EXPECT_EQ(rt_format, decoder_.GetScopedVASurface()->format());
const uint32_t actual_fourcc = scoped_image->image()->format.fourcc; const uint32_t actual_fourcc = scoped_image->image()->format.fourcc;
// TODO(andrescj): CompareImages() only supports I420, NV12, YUY2, and YUYV. // TODO(andrescj): CompareImages() only supports I420, NV12, YUY2, and YUYV.
// Make it support all the image formats we expect and call it // Make it support all the image formats we expect and call it
...@@ -583,6 +542,8 @@ TEST_P(VaapiJpegDecoderTest, DecodeSucceeds) { ...@@ -583,6 +542,8 @@ TEST_P(VaapiJpegDecoderTest, DecodeSucceeds) {
// successfully. // successfully.
// //
// TODO(andrescj): for now, this assumes 4:2:0. Handle other formats. // TODO(andrescj): for now, this assumes 4:2:0. Handle other formats.
// TODO(andrescj): consider recreating the decoder for every size so that no
// state is retained.
TEST_F(VaapiJpegDecoderTest, DecodeSucceedsForSupportedSizes) { TEST_F(VaapiJpegDecoderTest, DecodeSucceedsForSupportedSizes) {
gfx::Size min_supported_size; gfx::Size min_supported_size;
ASSERT_TRUE(VaapiWrapper::GetDecodeMinResolution(VAProfileJPEGBaseline, ASSERT_TRUE(VaapiWrapper::GetDecodeMinResolution(VAProfileJPEGBaseline,
...@@ -619,6 +580,8 @@ TEST_F(VaapiJpegDecoderTest, DecodeSucceedsForSupportedSizes) { ...@@ -619,6 +580,8 @@ TEST_F(VaapiJpegDecoderTest, DecodeSucceedsForSupportedSizes) {
std::unique_ptr<ScopedVAImage> scoped_image = Decode(jpeg_data_span); std::unique_ptr<ScopedVAImage> scoped_image = Decode(jpeg_data_span);
ASSERT_TRUE(scoped_image) ASSERT_TRUE(scoped_image)
<< "Decode unexpectedly failed for size = " << test_size.ToString(); << "Decode unexpectedly failed for size = " << test_size.ToString();
ASSERT_TRUE(decoder_.GetScopedVASurface());
EXPECT_TRUE(decoder_.GetScopedVASurface()->IsValid());
EXPECT_TRUE(CompareImages( EXPECT_TRUE(CompareImages(
jpeg_data_span, ScopedVAImageToDecodedVAImage(scoped_image.get()))) jpeg_data_span, ScopedVAImageToDecodedVAImage(scoped_image.get())))
<< "The SSIM check unexpectedly failed for size = " << "The SSIM check unexpectedly failed for size = "
...@@ -651,6 +614,7 @@ TEST_F(VaapiJpegDecoderTest, DecodeAndExportAsNativePixmapDmaBuf) { ...@@ -651,6 +614,7 @@ TEST_F(VaapiJpegDecoderTest, DecodeAndExportAsNativePixmapDmaBuf) {
scoped_refptr<gfx::NativePixmapDmaBuf> pixmap = scoped_refptr<gfx::NativePixmapDmaBuf> pixmap =
DecodeToNativePixmapDmaBuf(encoded_image, &status); DecodeToNativePixmapDmaBuf(encoded_image, &status);
ASSERT_EQ(VaapiImageDecodeStatus::kSuccess, status); ASSERT_EQ(VaapiImageDecodeStatus::kSuccess, status);
EXPECT_FALSE(decoder_.GetScopedVASurface());
ASSERT_TRUE(pixmap); ASSERT_TRUE(pixmap);
// After exporting the surface, we should not be able to obtain a VAImage with // After exporting the surface, we should not be able to obtain a VAImage with
...@@ -752,6 +716,7 @@ TEST_F(VaapiJpegDecoderTest, DecodeFailsForBelowMinSize) { ...@@ -752,6 +716,7 @@ TEST_F(VaapiJpegDecoderTest, DecodeFailsForBelowMinSize) {
&status)) &status))
<< "Decode unexpectedly succeeded for size = " << test_size.ToString(); << "Decode unexpectedly succeeded for size = " << test_size.ToString();
EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedImage, status); EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedImage, status);
EXPECT_FALSE(decoder_.GetScopedVASurface());
} }
} }
...@@ -801,6 +766,7 @@ TEST_F(VaapiJpegDecoderTest, DecodeFailsForAboveMaxSize) { ...@@ -801,6 +766,7 @@ TEST_F(VaapiJpegDecoderTest, DecodeFailsForAboveMaxSize) {
&status)) &status))
<< "Decode unexpectedly succeeded for size = " << test_size.ToString(); << "Decode unexpectedly succeeded for size = " << test_size.ToString();
EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedImage, status); EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedImage, status);
EXPECT_FALSE(decoder_.GetScopedVASurface());
} }
} }
...@@ -816,6 +782,7 @@ TEST_F(VaapiJpegDecoderTest, DecodeFails) { ...@@ -816,6 +782,7 @@ TEST_F(VaapiJpegDecoderTest, DecodeFails) {
reinterpret_cast<const uint8_t*>(jpeg_data.data()), jpeg_data.size()), reinterpret_cast<const uint8_t*>(jpeg_data.data()), jpeg_data.size()),
&status)); &status));
EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedSubsampling, status); EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedSubsampling, status);
EXPECT_FALSE(decoder_.GetScopedVASurface());
} }
std::string TestParamToString( std::string TestParamToString(
......
...@@ -227,10 +227,8 @@ void VaapiMjpegDecodeAccelerator::DecodeTask( ...@@ -227,10 +227,8 @@ void VaapiMjpegDecodeAccelerator::DecodeTask(
DCHECK(decoder_task_runner_->BelongsToCurrentThread()); DCHECK(decoder_task_runner_->BelongsToCurrentThread());
TRACE_EVENT0("jpeg", "DecodeTask"); TRACE_EVENT0("jpeg", "DecodeTask");
VaapiImageDecodeStatus status; VaapiImageDecodeStatus status = decoder_.Decode(
decoder_.Decode( base::make_span(static_cast<const uint8_t*>(shm->memory()), shm->size()));
base::make_span(static_cast<const uint8_t*>(shm->memory()), shm->size()),
&status);
if (status != VaapiImageDecodeStatus::kSuccess) { if (status != VaapiImageDecodeStatus::kSuccess) {
NotifyError(bitstream_buffer_id, VaapiJpegDecodeStatusToError(status)); NotifyError(bitstream_buffer_id, VaapiJpegDecodeStatusToError(status));
return; return;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "media/gpu/vaapi/vaapi_utils.h" #include "media/gpu/vaapi/vaapi_utils.h"
#include <type_traits> #include <type_traits>
#include <utility>
#include <va/va.h> #include <va/va.h>
...@@ -107,27 +108,20 @@ ScopedVAImage::~ScopedVAImage() { ...@@ -107,27 +108,20 @@ ScopedVAImage::~ScopedVAImage() {
} }
} }
ScopedVASurface::ScopedVASurface(base::Lock* va_lock, ScopedVASurface::ScopedVASurface(scoped_refptr<VaapiWrapper> vaapi_wrapper,
VADisplay va_display,
VASurfaceID va_surface_id, VASurfaceID va_surface_id,
const gfx::Size& size, const gfx::Size& size,
unsigned int va_rt_format) unsigned int va_rt_format)
: va_lock_(va_lock), : vaapi_wrapper_(std::move(vaapi_wrapper)),
va_display_(va_display),
va_surface_id_(va_surface_id), va_surface_id_(va_surface_id),
size_(size), size_(size),
va_rt_format_(va_rt_format) {} va_rt_format_(va_rt_format) {
DCHECK(vaapi_wrapper_);
}
ScopedVASurface::~ScopedVASurface() { ScopedVASurface::~ScopedVASurface() {
if (VA_INVALID_ID == va_surface_id_) if (va_surface_id_ != VA_INVALID_ID)
return; vaapi_wrapper_->DestroySurface(va_surface_id_);
base::AutoLock auto_lock(*va_lock_);
VASurfaceID va_surface_to_destroy = va_surface_id_;
const VAStatus va_res =
vaDestroySurfaces(va_display_, &va_surface_to_destroy, 1);
if (VA_STATUS_SUCCESS != va_res)
LOG(ERROR) << "vaDestroySurfaces failed: " << vaErrorStr(va_res);
} }
bool ScopedVASurface::IsValid() const { bool ScopedVASurface::IsValid() const {
......
...@@ -26,6 +26,7 @@ class Lock; ...@@ -26,6 +26,7 @@ class Lock;
namespace media { namespace media {
class VaapiWrapper; class VaapiWrapper;
class Vp8ReferenceFrameVector; class Vp8ReferenceFrameVector;
struct VAContextAndScopedVASurfaceDeleter;
struct Vp8FrameHeader; struct Vp8FrameHeader;
// Class to map a given VABuffer, identified by |buffer_id|, for its lifetime. // Class to map a given VABuffer, identified by |buffer_id|, for its lifetime.
...@@ -90,13 +91,10 @@ class ScopedVAImage { ...@@ -90,13 +91,10 @@ class ScopedVAImage {
}; };
// A VA-API-specific surface used by video/image codec accelerators to work on. // A VA-API-specific surface used by video/image codec accelerators to work on.
// As the name suggests, this class is self-cleaning. It calls // As the name suggests, this class is self-cleaning.
// vaDestroySurfaces() on the underlying VA surface upon destruction. Thus
// |va_lock| is acquired for destruction purposes.
class ScopedVASurface { class ScopedVASurface {
public: public:
ScopedVASurface(base::Lock* va_lock, ScopedVASurface(scoped_refptr<VaapiWrapper> vaapi_wrapper,
VADisplay va_display,
VASurfaceID va_surface_id, VASurfaceID va_surface_id,
const gfx::Size& size, const gfx::Size& size,
unsigned int va_rt_format); unsigned int va_rt_format);
...@@ -108,8 +106,8 @@ class ScopedVASurface { ...@@ -108,8 +106,8 @@ class ScopedVASurface {
unsigned int format() const { return va_rt_format_; } unsigned int format() const { return va_rt_format_; }
private: private:
base::Lock* const va_lock_; friend struct VAContextAndScopedVASurfaceDeleter;
const VADisplay va_display_ GUARDED_BY(va_lock_); const scoped_refptr<VaapiWrapper> vaapi_wrapper_;
const VASurfaceID va_surface_id_; const VASurfaceID va_surface_id_;
const gfx::Size size_; const gfx::Size size_;
const unsigned int va_rt_format_; const unsigned int va_rt_format_;
......
...@@ -2028,7 +2028,7 @@ std::unique_ptr<ScopedVASurface> VaapiWrapper::CreateScopedVASurface( ...@@ -2028,7 +2028,7 @@ std::unique_ptr<ScopedVASurface> VaapiWrapper::CreateScopedVASurface(
<< "Invalid VA surface id after vaCreateSurfaces"; << "Invalid VA surface id after vaCreateSurfaces";
auto scoped_va_surface = std::make_unique<ScopedVASurface>( auto scoped_va_surface = std::make_unique<ScopedVASurface>(
va_lock_, va_display_, va_surface_id, size, va_rt_format); this, va_surface_id, size, va_rt_format);
DCHECK(scoped_va_surface); DCHECK(scoped_va_surface);
DCHECK(scoped_va_surface->IsValid()); DCHECK(scoped_va_surface->IsValid());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment