diff options
Diffstat (limited to 'chromium/media/gpu/windows')
22 files changed, 723 insertions, 585 deletions
diff --git a/chromium/media/gpu/windows/av1_guids.h b/chromium/media/gpu/windows/av1_guids.h new file mode 100644 index 00000000000..c5e4e5d314c --- /dev/null +++ b/chromium/media/gpu/windows/av1_guids.h @@ -0,0 +1,52 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_GPU_WINDOWS_AV1_GUIDS_H_ +#define MEDIA_GPU_WINDOWS_AV1_GUIDS_H_ + +#include <dxva.h> +#include <initguid.h> + +#if WDK_NTDDI_VERSION <= NTDDI_WIN10_19H1 +DEFINE_GUID(DXVA_ModeAV1_VLD_Profile0, + 0xb8be4ccb, + 0xcf53, + 0x46ba, + 0x8d, + 0x59, + 0xd6, + 0xb8, + 0xa6, + 0xda, + 0x5d, + 0x2a); + +DEFINE_GUID(DXVA_ModeAV1_VLD_Profile1, + 0x6936ff0f, + 0x45b1, + 0x4163, + 0x9c, + 0xc1, + 0x64, + 0x6e, + 0xf6, + 0x94, + 0x61, + 0x08); + +DEFINE_GUID(DXVA_ModeAV1_VLD_Profile2, + 0x0c5f2aa1, + 0xe541, + 0x4089, + 0xbb, + 0x7b, + 0x98, + 0x11, + 0x0a, + 0x19, + 0xd7, + 0xc8); +#endif // WDK_NTDDI_VERSION <= NTDDI_WIN10_19H1 + +#endif // MEDIA_GPU_WINDOWS_AV1_GUIDS_H_ diff --git a/chromium/media/gpu/windows/d3d11_decoder_configurator.cc b/chromium/media/gpu/windows/d3d11_decoder_configurator.cc index a4a42eabf51..869106f87e9 100644 --- a/chromium/media/gpu/windows/d3d11_decoder_configurator.cc +++ b/chromium/media/gpu/windows/d3d11_decoder_configurator.cc @@ -73,9 +73,11 @@ bool D3D11DecoderConfigurator::SupportsDevice( ComD3D11Texture2D D3D11DecoderConfigurator::CreateOutputTexture( ComD3D11Device device, - gfx::Size size) { + gfx::Size size, + uint32_t array_size) { output_texture_desc_.Width = size.width(); output_texture_desc_.Height = size.height(); + output_texture_desc_.ArraySize = array_size; ComD3D11Texture2D result; if (!SUCCEEDED( @@ -100,7 +102,6 @@ void D3D11DecoderConfigurator::SetUpTextureDescriptor(bool supports_swap_chain, bool is_encrypted) { output_texture_desc_ = {}; output_texture_desc_.MipLevels = 1; - output_texture_desc_.ArraySize = D3D11DecoderConfigurator::BUFFER_COUNT; output_texture_desc_.Format = dxgi_format_; output_texture_desc_.SampleDesc.Count = 1; output_texture_desc_.Usage = D3D11_USAGE_DEFAULT; diff --git a/chromium/media/gpu/windows/d3d11_decoder_configurator.h b/chromium/media/gpu/windows/d3d11_decoder_configurator.h index a23535bc615..3d6cd49e90c 100644 --- a/chromium/media/gpu/windows/d3d11_decoder_configurator.h +++ b/chromium/media/gpu/windows/d3d11_decoder_configurator.h @@ -40,7 +40,9 @@ class MEDIA_GPU_EXPORT D3D11DecoderConfigurator { bool SupportsDevice(ComD3D11VideoDevice video_device); // Create the decoder's output texture. - ComD3D11Texture2D CreateOutputTexture(ComD3D11Device device, gfx::Size size); + ComD3D11Texture2D CreateOutputTexture(ComD3D11Device device, + gfx::Size size, + uint32_t array_size); const D3D11_VIDEO_DECODER_DESC* DecoderDescriptor() const { return &decoder_desc_; diff --git a/chromium/media/gpu/windows/d3d11_h264_accelerator.cc b/chromium/media/gpu/windows/d3d11_h264_accelerator.cc index df549d3a380..e87c1ece44f 100644 --- a/chromium/media/gpu/windows/d3d11_h264_accelerator.cc +++ b/chromium/media/gpu/windows/d3d11_h264_accelerator.cc @@ -45,12 +45,12 @@ void AppendSubsamples( class D3D11H264Picture : public H264Picture { public: D3D11H264Picture(D3D11PictureBuffer* picture) - : picture(picture), level_(picture->level()) { + : picture(picture), picture_index_(picture->picture_index()) { picture->set_in_picture_use(true); } D3D11PictureBuffer* picture; - size_t level_; + size_t picture_index_; protected: ~D3D11H264Picture() override; @@ -63,16 +63,16 @@ D3D11H264Picture::~D3D11H264Picture() { D3D11H264Accelerator::D3D11H264Accelerator( D3D11VideoDecoderClient* client, MediaLog* media_log, - ComD3D11VideoDecoder video_decoder, ComD3D11VideoDevice video_device, std::unique_ptr<VideoContextWrapper> video_context) : client_(client), media_log_(media_log), - video_decoder_(video_decoder), video_device_(video_device), video_context_(std::move(video_context)) { DCHECK(client); DCHECK(media_log_); + client->SetDecoderCB(base::BindRepeating( + &D3D11H264Accelerator::SetVideoDecoder, base::Unretained(this))); } D3D11H264Accelerator::~D3D11H264Accelerator() {} @@ -135,7 +135,7 @@ DecoderStatus D3D11H264Accelerator::SubmitFrameMetadata( D3D11H264Picture* our_ref_pic = static_cast<D3D11H264Picture*>(it->get()); if (!our_ref_pic->ref) continue; - ref_frame_list_[i].Index7Bits = our_ref_pic->level_; + ref_frame_list_[i].Index7Bits = our_ref_pic->picture_index_; ref_frame_list_[i].AssociatedFlag = our_ref_pic->long_term; field_order_cnt_list_[i][0] = our_ref_pic->top_field_order_cnt; field_order_cnt_list_[i][1] = our_ref_pic->bottom_field_order_cnt; @@ -281,7 +281,7 @@ void D3D11H264Accelerator::PicParamsFromSliceHeader( void D3D11H264Accelerator::PicParamsFromPic(DXVA_PicParams_H264* pic_param, scoped_refptr<H264Picture> pic) { pic_param->CurrPic.Index7Bits = - static_cast<D3D11H264Picture*>(pic.get())->level_; + static_cast<D3D11H264Picture*>(pic.get())->picture_index_; pic_param->RefPicFlag = pic->ref; pic_param->frame_num = pic->frame_num; @@ -588,4 +588,8 @@ void D3D11H264Accelerator::RecordFailure(const std::string& reason, MEDIA_LOG(ERROR, media_log_) << hr_string << ": " << reason; } +void D3D11H264Accelerator::SetVideoDecoder(ComD3D11VideoDecoder video_decoder) { + video_decoder_ = std::move(video_decoder); +} + } // namespace media diff --git a/chromium/media/gpu/windows/d3d11_h264_accelerator.h b/chromium/media/gpu/windows/d3d11_h264_accelerator.h index cd9dd468755..00e2bd5cecd 100644 --- a/chromium/media/gpu/windows/d3d11_h264_accelerator.h +++ b/chromium/media/gpu/windows/d3d11_h264_accelerator.h @@ -34,7 +34,6 @@ class D3D11H264Accelerator : public H264Decoder::H264Accelerator { public: D3D11H264Accelerator(D3D11VideoDecoderClient* client, MediaLog* media_log, - ComD3D11VideoDecoder video_decoder, ComD3D11VideoDevice video_device, std::unique_ptr<VideoContextWrapper> video_context); ~D3D11H264Accelerator() override; @@ -78,6 +77,8 @@ class D3D11H264Accelerator : public H264Decoder::H264Accelerator { void PicParamsFromPic(DXVA_PicParams_H264* pic_param, scoped_refptr<H264Picture> pic); + void SetVideoDecoder(ComD3D11VideoDecoder video_decoder); + private: bool SubmitSliceData(); bool RetrieveBitstreamBuffer(); diff --git a/chromium/media/gpu/windows/d3d11_picture_buffer.cc b/chromium/media/gpu/windows/d3d11_picture_buffer.cc index 7c0278b690e..60d1720e92f 100644 --- a/chromium/media/gpu/windows/d3d11_picture_buffer.cc +++ b/chromium/media/gpu/windows/d3d11_picture_buffer.cc @@ -24,15 +24,17 @@ namespace media { D3D11PictureBuffer::D3D11PictureBuffer( scoped_refptr<base::SequencedTaskRunner> delete_task_runner, ComD3D11Texture2D texture, + size_t array_slice, std::unique_ptr<Texture2DWrapper> texture_wrapper, gfx::Size size, - size_t level) + size_t picture_index) : RefCountedDeleteOnSequence<D3D11PictureBuffer>( std::move(delete_task_runner)), texture_(std::move(texture)), + array_slice_(array_slice), texture_wrapper_(std::move(texture_wrapper)), size_(size), - level_(level) {} + picture_index_(picture_index) {} D3D11PictureBuffer::~D3D11PictureBuffer() { } @@ -46,7 +48,7 @@ bool D3D11PictureBuffer::Init( D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC view_desc = {}; view_desc.DecodeProfile = decoder_guid; view_desc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D; - view_desc.Texture2D.ArraySlice = (UINT)level_; + view_desc.Texture2D.ArraySlice = array_slice_; if (!texture_wrapper_->Init(std::move(gpu_task_runner), std::move(get_helper_cb))) { @@ -69,8 +71,9 @@ bool D3D11PictureBuffer::ProcessTexture( const gfx::ColorSpace& input_color_space, MailboxHolderArray* mailbox_dest, gfx::ColorSpace* output_color_space) { - return texture_wrapper_->ProcessTexture(Texture(), level_, input_color_space, - mailbox_dest, output_color_space); + return texture_wrapper_->ProcessTexture(Texture(), array_slice_, + input_color_space, mailbox_dest, + output_color_space); } ComD3D11Texture2D D3D11PictureBuffer::Texture() const { diff --git a/chromium/media/gpu/windows/d3d11_picture_buffer.h b/chromium/media/gpu/windows/d3d11_picture_buffer.h index d605772d147..08e2c307725 100644 --- a/chromium/media/gpu/windows/d3d11_picture_buffer.h +++ b/chromium/media/gpu/windows/d3d11_picture_buffer.h @@ -47,13 +47,18 @@ class MEDIA_GPU_EXPORT D3D11PictureBuffer public: // |texture_wrapper| is responsible for controlling mailbox access to // the ID3D11Texture2D, - // |level| is the picturebuffer index inside the Array-type ID3D11Texture2D. + // |array_slice| is the picturebuffer index inside the Array-type + // ID3D11Texture2D. |picture_index| is a unique id used to identify this + // picture to the decoder. If a texture array is used, then it might as well + // be equal to the texture array index. Otherwise, any 0-based index is + // probably okay, though sequential makes sense. D3D11PictureBuffer( scoped_refptr<base::SequencedTaskRunner> delete_task_runner, ComD3D11Texture2D texture, + size_t array_slice, std::unique_ptr<Texture2DWrapper> texture_wrapper, gfx::Size size, - size_t level); + size_t picture_index); bool Init(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, GetCommandBufferHelperCB get_helper_cb, @@ -71,7 +76,7 @@ class MEDIA_GPU_EXPORT D3D11PictureBuffer ComD3D11Texture2D Texture() const; const gfx::Size& size() const { return size_; } - size_t level() const { return level_; } + size_t picture_index() const { return picture_index_; } // Is this PictureBuffer backing a VideoFrame right now? bool in_client_use() const { return in_client_use_; } @@ -97,11 +102,13 @@ class MEDIA_GPU_EXPORT D3D11PictureBuffer friend class base::DeleteHelper<D3D11PictureBuffer>; ComD3D11Texture2D texture_; + uint32_t array_slice_; + std::unique_ptr<Texture2DWrapper> texture_wrapper_; gfx::Size size_; bool in_picture_use_ = false; bool in_client_use_ = false; - size_t level_; + size_t picture_index_; ComD3D11VideoDecoderOutputView output_view_; diff --git a/chromium/media/gpu/windows/d3d11_texture_selector.cc b/chromium/media/gpu/windows/d3d11_texture_selector.cc index b3f0c78377d..dd3b88544ac 100644 --- a/chromium/media/gpu/windows/d3d11_texture_selector.cc +++ b/chromium/media/gpu/windows/d3d11_texture_selector.cc @@ -152,7 +152,7 @@ std::unique_ptr<TextureSelector> TextureSelector::Create( // If we're trying to produce an output texture that's different from what // the decoder is providing, then we need to copy it. - needs_texture_copy = (decoder_output_format != output_dxgi_format); + needs_texture_copy |= (decoder_output_format != output_dxgi_format); // Force texture copy on if requested for debugging. if (base::FeatureList::IsEnabled(kD3D11VideoDecoderAlwaysCopy)) diff --git a/chromium/media/gpu/windows/d3d11_texture_wrapper.cc b/chromium/media/gpu/windows/d3d11_texture_wrapper.cc index ab7ea22a87f..58f36986b9b 100644 --- a/chromium/media/gpu/windows/d3d11_texture_wrapper.cc +++ b/chromium/media/gpu/windows/d3d11_texture_wrapper.cc @@ -74,6 +74,9 @@ bool DefaultTexture2DWrapper::ProcessTexture( if (received_error_) return false; + // Temporary check to track down https://crbug.com/1077645 + CHECK(texture); + // It's okay to post and forget this call, since it'll be ordered correctly // with respect to any access on the gpu main thread. gpu_resources_.Post(FROM_HERE, &GpuResources::PushNewTexture, @@ -259,14 +262,24 @@ void DefaultTexture2DWrapper::GpuResources::Init( void DefaultTexture2DWrapper::GpuResources::PushNewTexture( ComD3D11Texture2D texture, size_t array_slice) { - if (!helper_ || !helper_->MakeContextCurrent()) { - NotifyError(StatusCode::kCantMakeContextCurrent); + // If init didn't complete, then signal (another) error that will probably be + // ignored in favor of whatever we signalled earlier. + if (!gl_image_ || !stream_) { + NotifyError(StatusCode::kDecoderInitializeNeverCompleted); return; } - // Notify |gl_image_| that it has a new texture. + // Notify |gl_image_| that it has a new texture. Do this unconditionally, so + // hat we can guarantee that the image isn't null. Nobody expects it to be, + // and failures will be noticed only asynchronously. + // https://crbug.com/1077645 gl_image_->SetTexture(texture, array_slice); + if (!helper_ || !helper_->MakeContextCurrent()) { + NotifyError(StatusCode::kCantMakeContextCurrent); + return; + } + // Notify angle that it has a new texture. EGLAttrib frame_attributes[] = { EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE, diff --git a/chromium/media/gpu/windows/d3d11_video_decoder.cc b/chromium/media/gpu/windows/d3d11_video_decoder.cc index 3ba6d9b3225..a98753cb255 100644 --- a/chromium/media/gpu/windows/d3d11_video_decoder.cc +++ b/chromium/media/gpu/windows/d3d11_video_decoder.cc @@ -9,6 +9,7 @@ #include <utility> #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/callback.h" #include "base/debug/crash_logging.h" #include "base/debug/dump_without_crashing.h" @@ -26,6 +27,7 @@ #include "media/base/video_decoder_config.h" #include "media/base/video_frame.h" #include "media/base/video_util.h" +#include "media/base/win/hresult_status_helper.h" #include "media/gpu/windows/d3d11_picture_buffer.h" #include "media/gpu/windows/d3d11_video_context_wrapper.h" #include "media/gpu/windows/d3d11_video_decoder_impl.h" @@ -166,23 +168,125 @@ HRESULT D3D11VideoDecoder::InitializeAcceleratedDecoder( profile_ = config.profile(); if (config.codec() == kCodecVP9) { accelerated_video_decoder_ = std::make_unique<VP9Decoder>( - std::make_unique<D3D11VP9Accelerator>(this, media_log_.get(), - video_decoder, video_device_, - std::move(video_context)), + std::make_unique<D3D11VP9Accelerator>( + this, media_log_.get(), video_device_, std::move(video_context)), profile_, config.color_space_info()); - return hr; - } - - if (config.codec() == kCodecH264) { + } else if (config.codec() == kCodecH264) { accelerated_video_decoder_ = std::make_unique<H264Decoder>( - std::make_unique<D3D11H264Accelerator>(this, media_log_.get(), - video_decoder, video_device_, - std::move(video_context)), + std::make_unique<D3D11H264Accelerator>( + this, media_log_.get(), video_device_, std::move(video_context)), profile_, config.color_space_info()); - return hr; + } else { + return E_FAIL; } - return E_FAIL; + // Provide the initial video decoder object. + DCHECK(set_accelerator_decoder_cb_); + set_accelerator_decoder_cb_.Run(std::move(video_decoder)); + + return hr; +} + +ErrorOr<std::tuple<ComD3D11VideoDecoder>> +D3D11VideoDecoder::CreateD3D11Decoder() { + HRESULT hr; + + // TODO: supported check? + + decoder_configurator_ = D3D11DecoderConfigurator::Create( + gpu_preferences_, gpu_workarounds_, config_, media_log_.get()); + if (!decoder_configurator_) + return StatusCode::kDecoderUnsupportedProfile; + + if (!decoder_configurator_->SupportsDevice(video_device_)) + return StatusCode::kDecoderUnsupportedCodec; + + FormatSupportChecker format_checker(device_); + if (!format_checker.Initialize()) { + // Don't fail; it'll just return no support a lot. + MEDIA_LOG(WARNING, media_log_) + << "Could not create format checker, continuing"; + } + + // Use IsHDRSupported to guess whether the compositor can output HDR textures. + // See TextureSelector for notes about why the decoder should not care. + texture_selector_ = TextureSelector::Create( + gpu_preferences_, gpu_workarounds_, + decoder_configurator_->TextureFormat(), + is_hdr_supported_ ? TextureSelector::HDRMode::kSDROrHDR + : TextureSelector::HDRMode::kSDROnly, + &format_checker, media_log_.get()); + if (!texture_selector_) + return StatusCode::kCannotCreateTextureSelector; + + UINT config_count = 0; + hr = video_device_->GetVideoDecoderConfigCount( + decoder_configurator_->DecoderDescriptor(), &config_count); + + if (FAILED(hr)) { + return Status(StatusCode::kCannotGetDecoderConfigCount) + .AddCause(HresultToStatus(hr)); + } + + if (config_count == 0) + return Status(StatusCode::kCannotGetDecoderConfigCount); + + D3D11_VIDEO_DECODER_CONFIG dec_config = {}; + bool found = false; + + for (UINT i = 0; i < config_count; i++) { + hr = video_device_->GetVideoDecoderConfig( + decoder_configurator_->DecoderDescriptor(), i, &dec_config); + if (FAILED(hr)) { + return Status(StatusCode::kCannotGetDecoderConfig) + .AddCause(HresultToStatus(hr)); + } + + if (config_.codec() == kCodecVP9 && dec_config.ConfigBitstreamRaw == 1) { + // DXVA VP9 specification mentions ConfigBitstreamRaw "shall be 1". + found = true; + break; + } + + if (config_.codec() == kCodecH264 && dec_config.ConfigBitstreamRaw == 2) { + // ConfigBitstreamRaw == 2 means the decoder uses DXVA_Slice_H264_Short. + found = true; + break; + } + } + if (!found) + return StatusCode::kDecoderUnsupportedConfig; + + // Prefer whatever the config tells us about whether to use one Texture2D with + // multiple array slices, or multiple Texture2Ds with one slice each. If bit + // 14 is clear, then it's the former, else it's the latter. + // + // Let the workaround override array texture mode, if enabled. + // + // For more information, please see: + // https://download.microsoft.com/download/9/2/A/92A4E198-67E0-4ABD-9DB7-635D711C2752/DXVA_VPx.pdf + // https://download.microsoft.com/download/5/f/c/5fc4ec5c-bd8c-4624-8034-319c1bab7671/DXVA_H264.pdf + use_single_video_decoder_texture_ = + !!(dec_config.ConfigDecoderSpecific & (1 << 14)) || + gpu_workarounds_.use_single_video_decoder_texture; + if (use_single_video_decoder_texture_) + MEDIA_LOG(INFO, media_log_) << "D3D11VideoDecoder is using single textures"; + else + MEDIA_LOG(INFO, media_log_) << "D3D11VideoDecoder is using array texture"; + + Microsoft::WRL::ComPtr<ID3D11VideoDecoder> video_decoder; + hr = video_device_->CreateVideoDecoder( + decoder_configurator_->DecoderDescriptor(), &dec_config, &video_decoder); + + if (!video_decoder.Get()) + return Status(StatusCode::kDecoderFailedCreation); + + if (FAILED(hr)) { + return Status(StatusCode::kDecoderFailedCreation) + .AddCause(HresultToStatus(hr)); + } + + return {std::move(video_decoder)}; } void D3D11VideoDecoder::Initialize(const VideoDecoderConfig& config, @@ -257,57 +361,20 @@ void D3D11VideoDecoder::Initialize(const VideoDecoderConfig& config, return; } - device_->GetImmediateContext(device_context_.ReleaseAndGetAddressOf()); + device_->GetImmediateContext(&device_context_); HRESULT hr; // TODO(liberato): Handle cleanup better. Also consider being less chatty in // the logs, since this will fall back. - hr = device_.CopyTo(video_device_.ReleaseAndGetAddressOf()); - if (!SUCCEEDED(hr)) { - NotifyError("Failed to get video device"); - return; - } - - decoder_configurator_ = D3D11DecoderConfigurator::Create( - gpu_preferences_, gpu_workarounds_, config, media_log_.get()); - if (!decoder_configurator_) { - NotifyError("D3DD11: Config provided unsupported profile"); - return; - } - - if (!decoder_configurator_->SupportsDevice(video_device_)) { - NotifyError("D3D11: Device does not support decoder GUID"); - return; - } - - FormatSupportChecker format_checker(device_); - if (!format_checker.Initialize()) { - // Don't fail; it'll just return no support a lot. - MEDIA_LOG(WARNING, media_log_) - << "Could not create format checker, continuing"; - } - - // Use IsHDRSupported to guess whether the compositor can output HDR textures. - // See TextureSelector for notes about why the decoder should not care. - texture_selector_ = TextureSelector::Create( - gpu_preferences_, gpu_workarounds_, - decoder_configurator_->TextureFormat(), - is_hdr_supported_ ? TextureSelector::HDRMode::kSDROrHDR - : TextureSelector::HDRMode::kSDROnly, - &format_checker, media_log_.get()); - if (!texture_selector_) { - NotifyError("D3DD11: Cannot get TextureSelector for format"); - return; - } - // TODO(liberato): dxva does this. don't know if we need to. if (!base::FeatureList::IsEnabled(kD3D11VideoDecoderSkipMultithreaded)) { ComD3D11Multithread multi_threaded; hr = device_->QueryInterface(IID_PPV_ARGS(&multi_threaded)); if (!SUCCEEDED(hr)) { - NotifyError("Failed to query ID3D11Multithread"); + NotifyError(Status(StatusCode::kCannotQueryID3D11Multithread) + .AddCause(HresultToStatus(hr))); return; } // TODO(liberato): This is a hack, since the unittest returns @@ -316,51 +383,20 @@ void D3D11VideoDecoder::Initialize(const VideoDecoderConfig& config, multi_threaded->SetMultithreadProtected(TRUE); } - UINT config_count = 0; - hr = video_device_->GetVideoDecoderConfigCount( - decoder_configurator_->DecoderDescriptor(), &config_count); - if (FAILED(hr) || config_count == 0) { - NotifyError("Failed to get video decoder config count"); - return; - } - - D3D11_VIDEO_DECODER_CONFIG dec_config = {}; - bool found = false; - - for (UINT i = 0; i < config_count; i++) { - hr = video_device_->GetVideoDecoderConfig( - decoder_configurator_->DecoderDescriptor(), i, &dec_config); - if (FAILED(hr)) { - NotifyError("Failed to get decoder config"); - return; - } - - if (config.codec() == kCodecVP9 && dec_config.ConfigBitstreamRaw == 1) { - // DXVA VP9 specification mentions ConfigBitstreamRaw "shall be 1". - found = true; - break; - } - - if (config.codec() == kCodecH264 && dec_config.ConfigBitstreamRaw == 2) { - // ConfigBitstreamRaw == 2 means the decoder uses DXVA_Slice_H264_Short. - found = true; - break; - } - } - if (!found) { - NotifyError("Failed to find decoder config"); + hr = device_.As(&video_device_); + if (!SUCCEEDED(hr)) { + NotifyError("Failed to get video device"); return; } - Microsoft::WRL::ComPtr<ID3D11VideoDecoder> video_decoder; - hr = video_device_->CreateVideoDecoder( - decoder_configurator_->DecoderDescriptor(), &dec_config, &video_decoder); - if (!video_decoder.Get()) { - NotifyError("Failed to create a video decoder"); + auto video_decoder_or_error = CreateD3D11Decoder(); + if (video_decoder_or_error.has_error()) { + NotifyError(video_decoder_or_error.error()); return; } - hr = InitializeAcceleratedDecoder(config, video_decoder); + hr = InitializeAcceleratedDecoder( + config, std::move(std::get<0>(video_decoder_or_error.value()))); if (!SUCCEEDED(hr)) { NotifyError("Failed to get device context"); @@ -556,13 +592,35 @@ void D3D11VideoDecoder::DoDecode() { return; CreatePictureBuffers(); } else if (result == media::AcceleratedVideoDecoder::kConfigChange) { + // TODO(liberato): I think we support this now, as long as it's the same + // decoder. Should update |config_| though. if (profile_ != accelerated_video_decoder_->GetProfile()) { // TODO(crbug.com/1022246): Handle profile change. LOG(ERROR) << "Profile change is not supported"; NotifyError("Profile change is not supported"); return; } - CreatePictureBuffers(); + // Before the first frame, we get a config change that we should ignore. + // We only want to take action if this is a mid-stream config change. We + // could wait until now to allocate the first D3D11VideoDecoder, but we + // don't, so that init can fail rather than decoding if there's a problem + // creating it. If there's a config change at the start of the stream, + // then this might not work. + if (!picture_buffers_.size()) + continue; + + // Update the config. + const auto new_coded_size = accelerated_video_decoder_->GetPicSize(); + config_.set_coded_size(new_coded_size); + auto video_decoder_or_error = CreateD3D11Decoder(); + if (video_decoder_or_error.has_error()) { + NotifyError(video_decoder_or_error.error()); + return; + } + DCHECK(set_accelerator_decoder_cb_); + set_accelerator_decoder_cb_.Run( + std::move(std::get<0>(video_decoder_or_error.value()))); + picture_buffers_.clear(); } else if (result == media::AcceleratedVideoDecoder::kTryAgain) { LOG(ERROR) << "Try again is not supported"; NotifyError("Try again is not supported"); @@ -627,14 +685,6 @@ void D3D11VideoDecoder::CreatePictureBuffers() { DCHECK(texture_selector_); gfx::Size size = accelerated_video_decoder_->GetPicSize(); - // Create an input texture array. - ComD3D11Texture2D in_texture = - decoder_configurator_->CreateOutputTexture(device_, size); - if (!in_texture) { - NotifyError("Failed to create a Texture2D for PictureBuffers"); - return; - } - HDRMetadata stream_metadata; if (config_.hdr_metadata()) stream_metadata = *config_.hdr_metadata(); @@ -653,8 +703,24 @@ void D3D11VideoDecoder::CreatePictureBuffers() { DCHECK(!buffer->in_picture_use()); picture_buffers_.clear(); + ComD3D11Texture2D in_texture; + // Create each picture buffer. for (size_t i = 0; i < D3D11DecoderConfigurator::BUFFER_COUNT; i++) { + // Create an input texture / texture array if we haven't already. + if (!in_texture) { + in_texture = decoder_configurator_->CreateOutputTexture( + device_, size, + use_single_video_decoder_texture_ + ? 1 + : D3D11DecoderConfigurator::BUFFER_COUNT); + } + + if (!in_texture) { + NotifyError("Failed to create a Texture2D for PictureBuffers"); + return; + } + auto tex_wrapper = texture_selector_->CreateTextureWrapper( device_, video_device_, device_context_, size); if (!tex_wrapper) { @@ -662,8 +728,10 @@ void D3D11VideoDecoder::CreatePictureBuffers() { return; } - picture_buffers_.push_back(new D3D11PictureBuffer( - decoder_task_runner_, in_texture, std::move(tex_wrapper), size, i)); + const size_t array_slice = use_single_video_decoder_texture_ ? 0 : i; + picture_buffers_.push_back( + new D3D11PictureBuffer(decoder_task_runner_, in_texture, array_slice, + std::move(tex_wrapper), size, i /* level */)); if (!picture_buffers_[i]->Init( gpu_task_runner_, get_helper_cb_, video_device_, decoder_configurator_->DecoderGuid(), media_log_->Clone())) { @@ -671,6 +739,11 @@ void D3D11VideoDecoder::CreatePictureBuffers() { return; } + // If we're using one texture per buffer, rather than an array, then clear + // the ref to it so that we allocate a new one above. + if (use_single_video_decoder_texture_) + in_texture = nullptr; + // If we have display metadata, then tell the processor. Note that the // order of these calls is important, and we must set the display metadata // if we set the stream metadata, else it can crash on some AMD cards. @@ -750,7 +823,7 @@ bool D3D11VideoDecoder::OutputResult(const CodecPicture* picture, frame->SetReleaseMailboxCB( base::BindOnce(release_mailbox_cb_, std::move(wait_complete_cb))); - frame->metadata()->SetBoolean(VideoFrameMetadata::POWER_EFFICIENT, true); + frame->metadata()->power_efficient = true; // For NV12, overlay is allowed by default. If the decoder is going to support // non-NV12 textures, then this may have to be conditionally set. Also note // that ALLOW_OVERLAY is required for encrypted video path. @@ -765,28 +838,33 @@ bool D3D11VideoDecoder::OutputResult(const CodecPicture* picture, // presenter decide if it wants to. const bool allow_overlay = base::FeatureList::IsEnabled(kD3D11VideoDecoderAllowOverlay); - frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, - allow_overlay); + frame->metadata()->allow_overlay = allow_overlay; frame->set_color_space(output_color_space); output_cb_.Run(frame); return true; } -// TODO(tmathmeyer) eventually have this take a Status and pass it through -// to each of the callbacks. +void D3D11VideoDecoder::SetDecoderCB(const SetAcceleratorDecoderCB& cb) { + set_accelerator_decoder_cb_ = cb; +} + +// TODO(tmathmeyer): Please don't add new uses of this overload. void D3D11VideoDecoder::NotifyError(const char* reason) { + NotifyError(Status(StatusCode::kDecoderInitializeNeverCompleted, reason)); +} + +void D3D11VideoDecoder::NotifyError(const Status& reason) { TRACE_EVENT0("gpu", "D3D11VideoDecoder::NotifyError"); state_ = State::kError; - DLOG(ERROR) << reason; // TODO(tmathmeyer) - Remove this after plumbing Status through the // decode_cb and input_buffer_queue cb's. - MEDIA_LOG(ERROR, media_log_) << reason; + MEDIA_LOG(ERROR, media_log_) + << "D3D11VideoDecoder error: " << std::hex << reason.code(); if (init_cb_) - std::move(init_cb_).Run( - Status(StatusCode::kDecoderInitializeNeverCompleted, reason)); + std::move(init_cb_).Run(reason); current_buffer_ = nullptr; if (current_decode_cb_) @@ -876,97 +954,35 @@ D3D11VideoDecoder::GetSupportedVideoDecoderConfigs( return {}; } + const auto supported_resolutions = + GetSupportedD3D11VideoDecoderResolutions(d3d11_device, gpu_workarounds); + std::vector<SupportedVideoDecoderConfig> configs; - // VP9 has no default resolutions since it may not even be supported. - ResolutionPair max_h264_resolutions(gfx::Size(1920, 1088), gfx::Size()); - ResolutionPair max_vp8_resolutions; - ResolutionPair max_vp9_profile0_resolutions; - ResolutionPair max_vp9_profile2_resolutions; - const gfx::Size min_resolution(64, 64); - - GetResolutionsForDecoders( - {D3D11_DECODER_PROFILE_H264_VLD_NOFGT}, d3d11_device, gpu_workarounds, - &max_h264_resolutions, &max_vp8_resolutions, - &max_vp9_profile0_resolutions, &max_vp9_profile2_resolutions); - - if (max_h264_resolutions.first.width() > 0) { - // Push H264 configs, except HIGH10. - // landscape - configs.push_back(SupportedVideoDecoderConfig( - H264PROFILE_MIN, // profile_min - static_cast<VideoCodecProfile>(H264PROFILE_HIGH10PROFILE - - 1), // profile_max - min_resolution, // coded_size_min - max_h264_resolutions.first, // coded_size_max - false, // allow_encrypted - false)); // require_encrypted - configs.push_back(SupportedVideoDecoderConfig( - static_cast<VideoCodecProfile>(H264PROFILE_HIGH10PROFILE + - 1), // profile_min - H264PROFILE_MAX, // profile_max - min_resolution, // coded_size_min - max_h264_resolutions.first, // coded_size_max - false, // allow_encrypted - false)); // require_encrypted - - // portrait - configs.push_back(SupportedVideoDecoderConfig( - H264PROFILE_MIN, // profile_min - static_cast<VideoCodecProfile>(H264PROFILE_HIGH10PROFILE - - 1), // profile_max - min_resolution, // coded_size_min - max_h264_resolutions.second, // coded_size_max - false, // allow_encrypted - false)); // require_encrypted - configs.push_back(SupportedVideoDecoderConfig( - static_cast<VideoCodecProfile>(H264PROFILE_HIGH10PROFILE + - 1), // profile_min - H264PROFILE_MAX, // profile_max - min_resolution, // coded_size_min - max_h264_resolutions.second, // coded_size_max - false, // allow_encrypted - false)); // require_encrypted - } - - // TODO(liberato): Fill this in for VP8. - - if (max_vp9_profile0_resolutions.first.width()) { - // landscape - configs.push_back(SupportedVideoDecoderConfig( - VP9PROFILE_PROFILE0, // profile_min - VP9PROFILE_PROFILE0, // profile_max - min_resolution, // coded_size_min - max_vp9_profile0_resolutions.first, // coded_size_max - false, // allow_encrypted - false)); // require_encrypted - // portrait - configs.push_back(SupportedVideoDecoderConfig( - VP9PROFILE_PROFILE0, // profile_min - VP9PROFILE_PROFILE0, // profile_max - min_resolution, // coded_size_min - max_vp9_profile0_resolutions.second, // coded_size_max - false, // allow_encrypted - false)); // require_encrypted - } - - if (base::FeatureList::IsEnabled(kD3D11VideoDecoderVP9Profile2)) { - if (max_vp9_profile2_resolutions.first.width()) { - // landscape - configs.push_back(SupportedVideoDecoderConfig( - VP9PROFILE_PROFILE2, // profile_min - VP9PROFILE_PROFILE2, // profile_max - min_resolution, // coded_size_min - max_vp9_profile2_resolutions.first, // coded_size_max - false, // allow_encrypted - false)); // require_encrypted - // portrait - configs.push_back(SupportedVideoDecoderConfig( - VP9PROFILE_PROFILE2, // profile_min - VP9PROFILE_PROFILE2, // profile_max - min_resolution, // coded_size_min - max_vp9_profile2_resolutions.second, // coded_size_max - false, // allow_encrypted - false)); // require_encrypted + for (const auto& kv : supported_resolutions) { + const auto profile = kv.first; + if (profile == VP9PROFILE_PROFILE2 && + !base::FeatureList::IsEnabled(kD3D11VideoDecoderVP9Profile2)) { + continue; + } + + // TODO(liberato): Add VP8 and AV1 support to D3D11VideoDecoder. + if (profile == VP8PROFILE_ANY || + (profile >= AV1PROFILE_MIN && profile <= AV1PROFILE_MAX)) { + continue; + } + + const auto& resolution_range = kv.second; + configs.emplace_back(profile, profile, resolution_range.min_resolution, + resolution_range.max_landscape_resolution, + /*allow_encrypted=*/false, + /*require_encrypted=*/false); + if (!resolution_range.max_portrait_resolution.IsEmpty() && + resolution_range.max_portrait_resolution != + resolution_range.max_landscape_resolution) { + configs.emplace_back(profile, profile, resolution_range.min_resolution, + resolution_range.max_portrait_resolution, + /*allow_encrypted=*/false, + /*require_encrypted=*/false); } } diff --git a/chromium/media/gpu/windows/d3d11_video_decoder.h b/chromium/media/gpu/windows/d3d11_video_decoder.h index d9ba26ab254..70e07b81300 100644 --- a/chromium/media/gpu/windows/d3d11_video_decoder.h +++ b/chromium/media/gpu/windows/d3d11_video_decoder.h @@ -85,6 +85,7 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder, D3D11PictureBuffer* GetPicture() override; bool OutputResult(const CodecPicture* picture, D3D11PictureBuffer* picture_buffer) override; + void SetDecoderCB(const SetAcceleratorDecoderCB&) override; static bool GetD3D11FeatureLevel( ComD3D11Device dev, @@ -142,6 +143,12 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder, // gpu main thread. void CreatePictureBuffers(); + // Create a D3D11VideoDecoder, if possible, based on the current config. + // TODO(liberato): we use a tuple only because ErrorOr<ComD3D111VideoDecoder> + // doesn't work. Something about base::Optional trying to convert to void*, + // but the conversion is ambiguous. + ErrorOr<std::tuple<ComD3D11VideoDecoder>> CreateD3D11Decoder(); + enum class NotSupportedReason { kVideoIsSupported = 0, @@ -205,8 +212,10 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder, }; // Enter the kError state. This will fail any pending |init_cb_| and / or - // pending decode as well. + // pending decode as well. Do not add new uses of the char* overload; send a + // Status instead. void NotifyError(const char* reason); + void NotifyError(const Status& reason); // The implementation, which lives on the GPU main thread. base::SequenceBound<D3D11VideoDecoderImpl> impl_; @@ -281,7 +290,15 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder, SupportedConfigs supported_configs_; // Should we assume that we're outputting to an HDR display? - bool is_hdr_supported_; + bool is_hdr_supported_ = false; + + // Should we use multiple single textures for the decoder output (true) or one + // texture with multiple array slices (false)? + bool use_single_video_decoder_texture_ = false; + + // Word-salad callback to set / update D3D11 Video callback to the + // accelerator. Needed for config changes. + SetAcceleratorDecoderCB set_accelerator_decoder_cb_; base::WeakPtrFactory<D3D11VideoDecoder> weak_factory_{this}; diff --git a/chromium/media/gpu/windows/d3d11_video_decoder_client.h b/chromium/media/gpu/windows/d3d11_video_decoder_client.h index a80e8430c7f..0286ad41ade 100644 --- a/chromium/media/gpu/windows/d3d11_video_decoder_client.h +++ b/chromium/media/gpu/windows/d3d11_video_decoder_client.h @@ -5,7 +5,9 @@ #ifndef MEDIA_GPU_WINDOWS_D3D11_VIDEO_DECODER_CLIENT_H_ #define MEDIA_GPU_WINDOWS_D3D11_VIDEO_DECODER_CLIENT_H_ +#include "base/callback.h" #include "media/base/video_color_space.h" +#include "media/gpu/windows/d3d11_com_defs.h" namespace media { @@ -16,10 +18,18 @@ class D3D11PictureBuffer; // required methods to D3D11VideoAccelerators. class D3D11VideoDecoderClient { public: + using SetAcceleratorDecoderCB = + base::RepeatingCallback<void(ComD3D11VideoDecoder)>; + virtual D3D11PictureBuffer* GetPicture() = 0; virtual bool OutputResult(const CodecPicture* picture, D3D11PictureBuffer* picture_buffer) = 0; + // Called by the accelerator to provide a callback that can be used to give + // the accelerator a D3D11VideoDecoder object. Must be called during + // construction of the accelerator. + virtual void SetDecoderCB(const SetAcceleratorDecoderCB&) = 0; + protected: virtual ~D3D11VideoDecoderClient() = default; }; diff --git a/chromium/media/gpu/windows/d3d11_video_decoder_unittest.cc b/chromium/media/gpu/windows/d3d11_video_decoder_unittest.cc index 91908445262..bb950b7717c 100644 --- a/chromium/media/gpu/windows/d3d11_video_decoder_unittest.cc +++ b/chromium/media/gpu/windows/d3d11_video_decoder_unittest.cc @@ -283,7 +283,7 @@ TEST_F(D3D11VideoDecoderTest, DoesNotSupportVP9WithLegacyGPU) { } TEST_F(D3D11VideoDecoderTest, DoesNotSupportVP9WithGPUWorkaroundDisableVPX) { - gpu_workarounds_.disable_accelerated_vpx_decode = true; + gpu_workarounds_.disable_accelerated_vp9_decode = true; VideoDecoderConfig configuration = TestVideoConfig::NormalCodecProfile(kCodecVP9, VP9PROFILE_PROFILE0); diff --git a/chromium/media/gpu/windows/d3d11_vp9_accelerator.cc b/chromium/media/gpu/windows/d3d11_vp9_accelerator.cc index eeec7896bde..7fe0f7f7eca 100644 --- a/chromium/media/gpu/windows/d3d11_vp9_accelerator.cc +++ b/chromium/media/gpu/windows/d3d11_vp9_accelerator.cc @@ -36,17 +36,17 @@ CreateSubsampleMappingBlock(const std::vector<SubsampleEntry>& from) { D3D11VP9Accelerator::D3D11VP9Accelerator( D3D11VideoDecoderClient* client, MediaLog* media_log, - ComD3D11VideoDecoder video_decoder, ComD3D11VideoDevice video_device, std::unique_ptr<VideoContextWrapper> video_context) : client_(client), media_log_(media_log), status_feedback_(0), - video_decoder_(std::move(video_decoder)), video_device_(std::move(video_device)), video_context_(std::move(video_context)) { DCHECK(client); DCHECK(media_log_); + client->SetDecoderCB(base::BindRepeating( + &D3D11VP9Accelerator::SetVideoDecoder, base::Unretained(this))); } D3D11VP9Accelerator::~D3D11VP9Accelerator() {} @@ -111,7 +111,7 @@ void D3D11VP9Accelerator::CopyFrameParams(const D3D11VP9Picture& pic, pic_params->BitDepthMinus8Luma = pic_params->BitDepthMinus8Chroma = pic.frame_hdr->bit_depth - 8; - pic_params->CurrPic.Index7Bits = pic.level(); + pic_params->CurrPic.Index7Bits = pic.picture_index(); pic_params->frame_type = !pic.frame_hdr->IsKeyframe(); COPY_PARAM(subsampling_x); @@ -150,7 +150,7 @@ void D3D11VP9Accelerator::CopyReferenceFrames( if (ref_pic) { scoped_refptr<D3D11VP9Picture> our_ref_pic( static_cast<D3D11VP9Picture*>(ref_pic.get())); - pic_params->ref_frame_map[i].Index7Bits = our_ref_pic->level(); + pic_params->ref_frame_map[i].Index7Bits = our_ref_pic->picture_index(); pic_params->ref_frame_coded_width[i] = texture_descriptor.Width; pic_params->ref_frame_coded_height[i] = texture_descriptor.Height; } else { @@ -185,19 +185,16 @@ void D3D11VP9Accelerator::CopyLoopFilterParams( // base::size(...) doesn't work well in an array initializer. DCHECK_EQ(4lu, base::size(pic_params->ref_deltas)); - int ref_deltas[4] = {0}; for (size_t i = 0; i < base::size(pic_params->ref_deltas); i++) { - if (loop_filter_params.update_ref_deltas[i]) - ref_deltas[i] = loop_filter_params.ref_deltas[i]; - pic_params->ref_deltas[i] = ref_deltas[i]; + // The update_ref_deltas[i] is _only_ for parsing! it allows omission of the + // 6 bytes that would otherwise be needed for a new value to overwrite the + // global one. It has nothing to do with setting the ref_deltas here. + pic_params->ref_deltas[i] = loop_filter_params.ref_deltas[i]; } - int mode_deltas[2] = {0}; DCHECK_EQ(2lu, base::size(pic_params->mode_deltas)); for (size_t i = 0; i < base::size(pic_params->mode_deltas); i++) { - if (loop_filter_params.update_mode_deltas[i]) - mode_deltas[i] = loop_filter_params.mode_deltas[i]; - pic_params->mode_deltas[i] = mode_deltas[i]; + pic_params->mode_deltas[i] = loop_filter_params.mode_deltas[i]; } } @@ -381,4 +378,8 @@ bool D3D11VP9Accelerator::GetFrameContext(scoped_refptr<VP9Picture> picture, return false; } +void D3D11VP9Accelerator::SetVideoDecoder(ComD3D11VideoDecoder video_decoder) { + video_decoder_ = std::move(video_decoder); +} + } // namespace media diff --git a/chromium/media/gpu/windows/d3d11_vp9_accelerator.h b/chromium/media/gpu/windows/d3d11_vp9_accelerator.h index dc262d68d26..43c2c26e595 100644 --- a/chromium/media/gpu/windows/d3d11_vp9_accelerator.h +++ b/chromium/media/gpu/windows/d3d11_vp9_accelerator.h @@ -24,7 +24,6 @@ class D3D11VP9Accelerator : public VP9Decoder::VP9Accelerator { public: D3D11VP9Accelerator(D3D11VideoDecoderClient* client, MediaLog* media_log, - ComD3D11VideoDecoder video_decoder, ComD3D11VideoDevice video_device, std::unique_ptr<VideoContextWrapper> video_context); ~D3D11VP9Accelerator() override; @@ -69,6 +68,8 @@ class D3D11VP9Accelerator : public VP9Decoder::VP9Accelerator { void RecordFailure(const std::string& fail_type, const std::string& reason); + void SetVideoDecoder(ComD3D11VideoDecoder video_decoder); + D3D11VideoDecoderClient* client_; MediaLog* const media_log_; UINT status_feedback_; diff --git a/chromium/media/gpu/windows/d3d11_vp9_picture.cc b/chromium/media/gpu/windows/d3d11_vp9_picture.cc index 24ae6033294..5efa82b5be0 100644 --- a/chromium/media/gpu/windows/d3d11_vp9_picture.cc +++ b/chromium/media/gpu/windows/d3d11_vp9_picture.cc @@ -7,7 +7,8 @@ namespace media { D3D11VP9Picture::D3D11VP9Picture(D3D11PictureBuffer* picture_buffer) - : picture_buffer_(picture_buffer), level_(picture_buffer_->level()) { + : picture_buffer_(picture_buffer), + picture_index_(picture_buffer_->picture_index()) { picture_buffer_->set_in_picture_use(true); } diff --git a/chromium/media/gpu/windows/d3d11_vp9_picture.h b/chromium/media/gpu/windows/d3d11_vp9_picture.h index 3d3bcbbb3f9..27b144402cc 100644 --- a/chromium/media/gpu/windows/d3d11_vp9_picture.h +++ b/chromium/media/gpu/windows/d3d11_vp9_picture.h @@ -19,14 +19,14 @@ class D3D11VP9Picture : public VP9Picture { D3D11PictureBuffer* picture_buffer() const { return picture_buffer_; } - size_t level() const { return level_; } + size_t picture_index() const { return picture_index_; } protected: ~D3D11VP9Picture() override; private: D3D11PictureBuffer* picture_buffer_; - size_t level_; + size_t picture_index_; }; } // namespace media diff --git a/chromium/media/gpu/windows/dxva_video_decode_accelerator_win.cc b/chromium/media/gpu/windows/dxva_video_decode_accelerator_win.cc index ff451f0bb17..350458a8598 100644 --- a/chromium/media/gpu/windows/dxva_video_decode_accelerator_win.cc +++ b/chromium/media/gpu/windows/dxva_video_decode_accelerator_win.cc @@ -7,10 +7,6 @@ #include <algorithm> #include <memory> -#if !defined(OS_WIN) -#error This file should only be built on Windows. -#endif // !defined(OS_WIN) - #include <codecapi.h> #include <dxgi1_2.h> #include <ks.h> @@ -121,13 +117,6 @@ DEFINE_GUID(MF_XVP_PLAYBACK_MODE, 0xcc, 0xe9); -// Defines the GUID for the Intel H264 DXVA device. -static const GUID DXVA2_Intel_ModeH264_E = { - 0x604F8E68, - 0x4951, - 0x4c54, - {0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6}}; - static const CLSID CLSID_CAV1DecoderMFT = { 0xC843981A, 0x3359, @@ -184,7 +173,7 @@ HRESULT g_last_device_removed_reason; namespace media { -static const VideoCodecProfile kSupportedProfiles[] = { +constexpr VideoCodecProfile kSupportedProfiles[] = { H264PROFILE_BASELINE, H264PROFILE_MAIN, H264PROFILE_HIGH, VP8PROFILE_ANY, VP9PROFILE_PROFILE0, VP9PROFILE_PROFILE2, AV1PROFILE_PROFILE_MAIN, AV1PROFILE_PROFILE_HIGH, AV1PROFILE_PROFILE_PRO}; @@ -606,7 +595,11 @@ DXVAVideoDecodeAccelerator::DXVAVideoDecodeAccelerator( enable_low_latency_(gpu_preferences.enable_low_latency_dxva), support_share_nv12_textures_( gpu_preferences.enable_zero_copy_dxgi_video && - !workarounds.disable_dxgi_zero_copy_video), + !workarounds.disable_dxgi_zero_copy_video && + /* Sharing will use an array texture, so avoid it if arrays are being + * worked around. https://crbug.com/971952 . + */ + !workarounds.use_single_video_decoder_texture), num_picture_buffers_requested_(support_share_nv12_textures_ ? kNumPictureBuffersForZeroCopy : kNumPictureBuffers), @@ -619,8 +612,12 @@ DXVAVideoDecodeAccelerator::DXVAVideoDecodeAccelerator( use_keyed_mutex_(false), using_angle_device_(false), using_debug_device_(false), - enable_accelerated_vpx_decode_( - !workarounds.disable_accelerated_vpx_decode), + enable_accelerated_av1_decode_( + !workarounds.disable_accelerated_av1_decode), + enable_accelerated_vp8_decode_( + !workarounds.disable_accelerated_vp8_decode), + enable_accelerated_vp9_decode_( + !workarounds.disable_accelerated_vp9_decode), processing_config_changed_(false), use_empty_video_hdr_metadata_(workarounds.use_empty_video_hdr_metadata) { weak_ptr_ = weak_this_factory_.GetWeakPtr(); @@ -1343,84 +1340,28 @@ DXVAVideoDecodeAccelerator::GetSupportedProfiles( } } - // On Windows 7 the maximum resolution supported by media foundation is - // 1920 x 1088. We use 1088 to account for 16x16 macroblocks. - ResolutionPair max_h264_resolutions(gfx::Size(1920, 1088), gfx::Size()); - - // VP8/VP9 has no default resolutions since it may not even be supported. - ResolutionPair max_vp8_resolutions; - ResolutionPair max_vp9_profile0_resolutions; - ResolutionPair max_vp9_profile2_resolutions; - - GetResolutionsForDecoders({DXVA2_ModeH264_E, DXVA2_Intel_ModeH264_E}, - gl::QueryD3D11DeviceObjectFromANGLE(), workarounds, - &max_h264_resolutions, &max_vp8_resolutions, - &max_vp9_profile0_resolutions, - &max_vp9_profile2_resolutions); - - for (const auto& supported_profile : kSupportedProfiles) { - const bool is_h264 = supported_profile >= H264PROFILE_MIN && - supported_profile <= H264PROFILE_MAX; - const bool is_vp9 = supported_profile >= VP9PROFILE_MIN && - supported_profile <= VP9PROFILE_MAX; - const bool is_vp8 = supported_profile == VP8PROFILE_ANY; - const bool is_av1 = supported_profile >= AV1PROFILE_MIN && - supported_profile <= AV1PROFILE_MAX; - DCHECK(is_h264 || is_vp9 || is_vp8 || is_av1); - - ResolutionPair max_resolutions; - if (is_h264) { - max_resolutions = max_h264_resolutions; - } else if (supported_profile == VP9PROFILE_PROFILE0) { - max_resolutions = max_vp9_profile0_resolutions; - } else if (supported_profile == VP9PROFILE_PROFILE2) { - max_resolutions = max_vp9_profile2_resolutions; - } else if (is_vp8) { - max_resolutions = max_vp8_resolutions; - } else if (is_av1) { - if (!base::FeatureList::IsEnabled(kMediaFoundationAV1Decoding)) - continue; - - // TODO(dalecurtis): Update GetResolutionsForDecoders() to support AV1. - SupportedProfile profile; - profile.profile = supported_profile; - profile.min_resolution = gfx::Size(); - profile.max_resolution = gfx::Size(8192, 8192); - profiles.push_back(profile); - continue; - } - - // Skip adding VPx profiles if it's not supported or disabled. - if ((is_vp9 || is_vp8) && max_resolutions.first.IsEmpty()) - continue; - - // Windows Media Foundation H.264 decoding does not support decoding videos - // with any dimension smaller than 48 pixels: - // http://msdn.microsoft.com/en-us/library/windows/desktop/dd797815 - // - // TODO(dalecurtis): These values are too low. We should only be using - // hardware decode for videos above ~360p, see http://crbug.com/684792. - const gfx::Size min_resolution = - is_h264 ? gfx::Size(48, 48) : gfx::Size(16, 16); - + const auto supported_resolutions = GetSupportedD3D11VideoDecoderResolutions( + gl::QueryD3D11DeviceObjectFromANGLE(), workarounds); + for (const auto& kv : supported_resolutions) { + const auto& resolution_range = kv.second; { SupportedProfile profile; - profile.profile = supported_profile; - profile.min_resolution = min_resolution; - profile.max_resolution = max_resolutions.first; + profile.profile = kv.first; + profile.min_resolution = resolution_range.min_resolution; + profile.max_resolution = resolution_range.max_landscape_resolution; profiles.push_back(profile); } - const gfx::Size portrait_max_resolution = max_resolutions.second; - if (!portrait_max_resolution.IsEmpty()) { + if (!resolution_range.max_portrait_resolution.IsEmpty() && + resolution_range.max_portrait_resolution != + resolution_range.max_landscape_resolution) { SupportedProfile profile; - profile.profile = supported_profile; - profile.min_resolution = min_resolution; - profile.max_resolution = portrait_max_resolution; + profile.profile = kv.first; + profile.min_resolution = resolution_range.min_resolution; + profile.max_resolution = resolution_range.max_portrait_resolution; profiles.push_back(profile); } } - return profiles; } @@ -1475,18 +1416,21 @@ bool DXVAVideoDecodeAccelerator::InitDecoder(VideoCodecProfile profile) { "blacklisted version of msmpeg2vdec.dll 6.1.7140", false); codec_ = kCodecH264; clsid = __uuidof(CMSH264DecoderMFT); - } else if (enable_accelerated_vpx_decode_ && - ((profile >= VP9PROFILE_PROFILE0 && - profile <= VP9PROFILE_PROFILE3) || - profile == VP8PROFILE_ANY)) { + } else if ((profile >= VP9PROFILE_PROFILE0 && + profile <= VP9PROFILE_PROFILE3) || + profile == VP8PROFILE_ANY) { codec_ = profile == VP8PROFILE_ANY ? kCodecVP8 : kCodecVP9; - clsid = CLSID_MSVPxDecoder; - decoder_dll = ::LoadLibrary(kMSVPxDecoderDLLName); - if (decoder_dll) - using_ms_vpx_mft_ = true; + if ((codec_ == kCodecVP8 && enable_accelerated_vp8_decode_) || + (codec_ == kCodecVP9 && enable_accelerated_vp9_decode_)) { + clsid = CLSID_MSVPxDecoder; + decoder_dll = ::LoadLibrary(kMSVPxDecoderDLLName); + if (decoder_dll) + using_ms_vpx_mft_ = true; + } } - if (base::FeatureList::IsEnabled(kMediaFoundationAV1Decoding) && + if (enable_accelerated_av1_decode_ && + base::FeatureList::IsEnabled(kMediaFoundationAV1Decoding) && (profile >= AV1PROFILE_MIN && profile <= AV1PROFILE_MAX)) { codec_ = kCodecAV1; clsid = CLSID_CAV1DecoderMFT; @@ -1512,17 +1456,15 @@ bool DXVAVideoDecodeAccelerator::InitDecoder(VideoCodecProfile profile) { CHECK(create_dxgi_device_manager_); if (media_log_) MEDIA_LOG(INFO, media_log_) << "Using D3D11 device for DXVA"; - RETURN_AND_NOTIFY_ON_FAILURE(CreateDX11DevManager(), - "Failed to initialize DX11 device and manager", - PLATFORM_FAILURE, false); + RETURN_ON_FAILURE(CreateDX11DevManager(), + "Failed to initialize DX11 device and manager", false); device_manager_to_use = reinterpret_cast<ULONG_PTR>(d3d11_device_manager_.Get()); } else { if (media_log_) MEDIA_LOG(INFO, media_log_) << "Using D3D9 device for DXVA"; - RETURN_AND_NOTIFY_ON_FAILURE(CreateD3DDevManager(), - "Failed to initialize D3D device and manager", - PLATFORM_FAILURE, false); + RETURN_ON_FAILURE(CreateD3DDevManager(), + "Failed to initialize D3D device and manager", false); device_manager_to_use = reinterpret_cast<ULONG_PTR>(device_manager_.Get()); } diff --git a/chromium/media/gpu/windows/dxva_video_decode_accelerator_win.h b/chromium/media/gpu/windows/dxva_video_decode_accelerator_win.h index 4377744a8fd..21d3ec0d3fb 100644 --- a/chromium/media/gpu/windows/dxva_video_decode_accelerator_win.h +++ b/chromium/media/gpu/windows/dxva_video_decode_accelerator_win.h @@ -7,18 +7,12 @@ #include <d3d11_1.h> #include <d3d9.h> +#include <dxva2api.h> #include <initguid.h> +#include <mfidl.h> #include <stdint.h> #include <wrl/client.h> -// Work around bug in this header by disabling the relevant warning for it. -// https://connect.microsoft.com/VisualStudio/feedback/details/911260/dxva2api-h-in-win8-sdk-triggers-c4201-with-w4 -#pragma warning(push) -#pragma warning(disable : 4201) -#include <dxva2api.h> -#pragma warning(pop) -#include <mfidl.h> - #include <list> #include <map> #include <memory> @@ -588,8 +582,12 @@ class MEDIA_GPU_EXPORT DXVAVideoDecodeAccelerator bool using_angle_device_; bool using_debug_device_; - // Enables hardware acceleration for VP9 video decoding. - const bool enable_accelerated_vpx_decode_; + // Enables hardware acceleration for AV1 video decoding. + const bool enable_accelerated_av1_decode_; + + // Enables hardware acceleration for VP8/VP9 video decoding. + const bool enable_accelerated_vp8_decode_; + const bool enable_accelerated_vp9_decode_; // The media foundation H.264 decoder has problems handling changes like // resolution change, bitrate change etc. If we reinitialize the decoder diff --git a/chromium/media/gpu/windows/supported_profile_helpers.cc b/chromium/media/gpu/windows/supported_profile_helpers.cc index 5004c5799b7..7d8622286f1 100644 --- a/chromium/media/gpu/windows/supported_profile_helpers.cc +++ b/chromium/media/gpu/windows/supported_profile_helpers.cc @@ -9,16 +9,14 @@ #include <memory> #include <utility> +#include <d3d9.h> +#include <dxva2api.h> + #include "base/feature_list.h" #include "base/trace_event/trace_event.h" #include "base/win/windows_version.h" -#include "build/build_config.h" -#include "gpu/config/gpu_driver_bug_workarounds.h" #include "media/base/media_switches.h" - -#if !defined(OS_WIN) -#error This file should only be built on Windows. -#endif // !defined(OS_WIN) +#include "media/gpu/windows/av1_guids.h" namespace { @@ -26,7 +24,7 @@ namespace { // or earlier, and don't handle resolutions higher than 1920 x 1088 well. // // NOTE: This list must be kept in sorted order. -static const uint16_t kLegacyAmdGpuList[] = { +constexpr uint16_t kLegacyAmdGpuList[] = { 0x130f, 0x6700, 0x6701, 0x6702, 0x6703, 0x6704, 0x6705, 0x6706, 0x6707, 0x6708, 0x6709, 0x6718, 0x6719, 0x671c, 0x671d, 0x671f, 0x6720, 0x6721, 0x6722, 0x6723, 0x6724, 0x6725, 0x6726, 0x6727, 0x6728, 0x6729, 0x6738, @@ -67,14 +65,18 @@ static const uint16_t kLegacyAmdGpuList[] = { // 1920 x 1088 are supported. Updated based on crash reports. // // NOTE: This list must be kept in sorted order. -static const uint16_t kLegacyIntelGpuList[] = { +constexpr uint16_t kLegacyIntelGpuList[] = { 0x102, 0x106, 0x116, 0x126, 0x152, 0x156, 0x166, 0x402, 0x406, 0x416, 0x41e, 0xa06, 0xa16, 0xf31, }; -} // namespace - -namespace media { +// Windows Media Foundation H.264 decoding does not support decoding videos +// with any dimension smaller than 48 pixels: +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd797815 +// +// TODO(dalecurtis): These values are too low. We should only be using +// hardware decode for videos above ~360p, see http://crbug.com/684792. +constexpr gfx::Size kMinResolution(64, 64); // Certain AMD GPU drivers like R600, R700, Evergreen and Cayman and some second // generation Intel GPU drivers crash if we create a video device with a @@ -157,32 +159,12 @@ bool IsResolutionSupportedForDevice(const gfx::Size& resolution_to_test, config_count > 0; } -// Returns a tuple of (LandscapeMax, PortraitMax). If landscape maximum can not -// be computed, the value of |default_max| is returned for the landscape maximum -// and a zero size value is returned for portrait max (erring conservatively). -ResolutionPair GetMaxResolutionsForGUIDs( - const gfx::Size& default_max, +media::SupportedResolutionRange GetResolutionsForGUID( ID3D11VideoDevice* video_device, - const std::vector<GUID>& valid_guids, + const GUID& decoder_guid, const std::vector<gfx::Size>& resolutions_to_test, - DXGI_FORMAT format) { - ResolutionPair result(default_max, gfx::Size()); - - // Enumerate supported video profiles and look for the profile. - GUID decoder_guid = GUID_NULL; - UINT profile_count = video_device->GetVideoDecoderProfileCount(); - for (UINT profile_idx = 0; profile_idx < profile_count; profile_idx++) { - GUID profile_id = {}; - if (SUCCEEDED( - video_device->GetVideoDecoderProfile(profile_idx, &profile_id)) && - std::find(valid_guids.begin(), valid_guids.end(), profile_id) != - valid_guids.end()) { - decoder_guid = profile_id; - break; - } - } - if (decoder_guid == GUID_NULL) - return result; + DXGI_FORMAT format = DXGI_FORMAT_NV12) { + media::SupportedResolutionRange result; // Verify input is in ascending order by height. DCHECK(std::is_sorted(resolutions_to_test.begin(), resolutions_to_test.end(), @@ -195,32 +177,54 @@ ResolutionPair GetMaxResolutionsForGUIDs( format)) { break; } - result.first = res; + result.max_landscape_resolution = res; } // The max supported portrait resolution should be just be a w/h flip of the // max supported landscape resolution. - gfx::Size flipped(result.first.height(), result.first.width()); - if (IsResolutionSupportedForDevice(flipped, decoder_guid, video_device, + const gfx::Size flipped(result.max_landscape_resolution.height(), + result.max_landscape_resolution.width()); + if (flipped == result.max_landscape_resolution || + IsResolutionSupportedForDevice(flipped, decoder_guid, video_device, format)) { - result.second = flipped; + result.max_portrait_resolution = flipped; } + if (!result.max_landscape_resolution.IsEmpty()) + result.min_resolution = kMinResolution; + return result; } -// TODO(tmathmeyer) refactor this so that we don'ty call -// GetMaxResolutionsForGUIDS so many times. -void GetResolutionsForDecoders(std::vector<GUID> h264_guids, - ComD3D11Device device, - const gpu::GpuDriverBugWorkarounds& workarounds, - ResolutionPair* h264_resolutions, - ResolutionPair* vp8_resolutions, - ResolutionPair* vp9_0_resolutions, - ResolutionPair* vp9_2_resolutions) { - TRACE_EVENT0("gpu,startup", "GetResolutionsForDecoders"); +} // namespace + +namespace media { + +SupportedResolutionRangeMap GetSupportedD3D11VideoDecoderResolutions( + ComD3D11Device device, + const gpu::GpuDriverBugWorkarounds& workarounds) { + TRACE_EVENT0("gpu,startup", "GetSupportedD3D11VideoDecoderResolutions"); + SupportedResolutionRangeMap supported_resolutions; + + // We always insert support for H.264 regardless of the tests below. It's old + // enough to be ubiquitous. + // + // On Windows 7 the maximum resolution supported by media foundation is + // 1920 x 1088. We use 1088 to account for 16x16 macro-blocks. + constexpr gfx::Size kDefaultMaxH264Resolution(1920, 1088); + SupportedResolutionRange h264_profile; + h264_profile.min_resolution = kMinResolution; + h264_profile.max_landscape_resolution = kDefaultMaxH264Resolution; + + // We don't have a way to map DXVA support to specific H.264 profiles, so just + // mark all the common ones with the same level of support. + constexpr VideoCodecProfile kSupportedH264Profiles[] = { + H264PROFILE_BASELINE, H264PROFILE_MAIN, H264PROFILE_HIGH}; + for (const auto profile : kSupportedH264Profiles) + supported_resolutions[profile] = h264_profile; + if (base::win::GetVersion() <= base::win::Version::WIN7) - return; + return supported_resolutions; // To detect if a driver supports the desired resolutions, we try and create // a DXVA decoder instance for that resolution and profile. If that succeeds @@ -228,43 +232,99 @@ void GetResolutionsForDecoders(std::vector<GUID> h264_guids, // Legacy AMD drivers with UVD3 or earlier and some Intel GPU's crash while // creating surfaces larger than 1920 x 1088. if (!device || IsLegacyGPU(device.Get())) - return; + return supported_resolutions; ComD3D11VideoDevice video_device; if (FAILED(device.As(&video_device))) - return; + return supported_resolutions; - *h264_resolutions = GetMaxResolutionsForGUIDs( - h264_resolutions->first, video_device.Get(), h264_guids, - {gfx::Size(2560, 1440), gfx::Size(3840, 2160), gfx::Size(4096, 2160), - gfx::Size(4096, 2304)}); + const std::vector<gfx::Size> kModernResolutions = { + gfx::Size(4096, 2160), gfx::Size(4096, 2304), gfx::Size(7680, 4320), + gfx::Size(8192, 4320), gfx::Size(8192, 8192)}; - if (workarounds.disable_accelerated_vpx_decode) - return; + const bool should_test_for_av1_support = + base::FeatureList::IsEnabled(kMediaFoundationAV1Decoding) && + !workarounds.disable_accelerated_av1_decode; - if (base::FeatureList::IsEnabled(kMediaFoundationVP8Decoding)) { - *vp8_resolutions = GetMaxResolutionsForGUIDs( - vp8_resolutions->first, video_device.Get(), - {D3D11_DECODER_PROFILE_VP8_VLD}, - {gfx::Size(4096, 2160), gfx::Size(4096, 2304)}); + // Enumerate supported video profiles and look for the known profile for each + // codec. We first look through the the decoder profiles so we don't run N + // resolution tests for a profile that's unsupported. + UINT profile_count = video_device->GetVideoDecoderProfileCount(); + for (UINT i = 0; i < profile_count; i++) { + GUID profile_id; + if (FAILED(video_device->GetVideoDecoderProfile(i, &profile_id))) + continue; + + if (profile_id == D3D11_DECODER_PROFILE_H264_VLD_NOFGT) { + const auto result = GetResolutionsForGUID( + video_device.Get(), profile_id, + {gfx::Size(2560, 1440), gfx::Size(3840, 2160), gfx::Size(4096, 2160), + gfx::Size(4096, 2304), gfx::Size(4096, 4096)}); + + // Unlike the other codecs, H.264 support is assumed up to 1080p, even if + // our initial queries fail. If they fail, we use the defaults set above. + if (!result.max_landscape_resolution.IsEmpty()) { + for (const auto profile : kSupportedH264Profiles) + supported_resolutions[profile] = result; + } + continue; + } + + // Note: Each bit depth of AV1 uses a different DXGI_FORMAT, here we only + // test for the 8-bit one (NV12). + if (should_test_for_av1_support) { + if (profile_id == DXVA_ModeAV1_VLD_Profile0) { + supported_resolutions[AV1PROFILE_PROFILE_MAIN] = GetResolutionsForGUID( + video_device.Get(), profile_id, kModernResolutions); + continue; + } + if (profile_id == DXVA_ModeAV1_VLD_Profile1) { + supported_resolutions[AV1PROFILE_PROFILE_HIGH] = GetResolutionsForGUID( + video_device.Get(), profile_id, kModernResolutions); + continue; + } + if (profile_id == DXVA_ModeAV1_VLD_Profile2) { + // TODO(dalecurtis): 12-bit profile 2 support is complicated. Ideally, + // we should test DXVA_ModeAV1_VLD_12bit_Profile2 and + // DXVA_ModeAV1_VLD_12bit_Profile2_420 when the bit depth of the content + // is 12-bit. However we don't know the bit depth or pixel format until + // too late. In these cases we'll end up initializing the decoder and + // failing on the first decode (which will trigger software fallback). + supported_resolutions[AV1PROFILE_PROFILE_PRO] = GetResolutionsForGUID( + video_device.Get(), profile_id, kModernResolutions); + continue; + } + } + + if (!workarounds.disable_accelerated_vp8_decode && + profile_id == D3D11_DECODER_PROFILE_VP8_VLD && + base::FeatureList::IsEnabled(kMediaFoundationVP8Decoding)) { + supported_resolutions[VP8PROFILE_ANY] = + GetResolutionsForGUID(video_device.Get(), profile_id, + {gfx::Size(4096, 2160), gfx::Size(4096, 2304), + gfx::Size(4096, 4096)}); + continue; + } + + if (workarounds.disable_accelerated_vp9_decode) + continue; + + if (profile_id == D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0) { + supported_resolutions[VP9PROFILE_PROFILE0] = GetResolutionsForGUID( + video_device.Get(), profile_id, kModernResolutions); + continue; + } + + // RS3 has issues with VP9.2 decoding. See https://crbug.com/937108. + if (profile_id == D3D11_DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2 && + base::win::GetVersion() != base::win::Version::WIN10_RS3) { + supported_resolutions[VP9PROFILE_PROFILE2] = GetResolutionsForGUID( + video_device.Get(), profile_id, kModernResolutions, DXGI_FORMAT_P010); + continue; + } } - *vp9_0_resolutions = GetMaxResolutionsForGUIDs( - vp9_0_resolutions->first, video_device.Get(), - {D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0}, - {gfx::Size(4096, 2160), gfx::Size(4096, 2304), gfx::Size(7680, 4320), - gfx::Size(8192, 4320), gfx::Size(8192, 8192)}); - - // RS3 has issues with VP9.2 decoding. See https://crbug.com/937108. - if (base::win::GetVersion() == base::win::Version::WIN10_RS3) - return; - - *vp9_2_resolutions = GetMaxResolutionsForGUIDs( - vp9_2_resolutions->first, video_device.Get(), - {D3D11_DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2}, - {gfx::Size(4096, 2160), gfx::Size(4096, 2304), gfx::Size(7680, 4320), - gfx::Size(8192, 4320), gfx::Size(8192, 8192)}, - DXGI_FORMAT_P010); + return supported_resolutions; } } // namespace media diff --git a/chromium/media/gpu/windows/supported_profile_helpers.h b/chromium/media/gpu/windows/supported_profile_helpers.h index 1834f0ba62a..6e521d08ff6 100644 --- a/chromium/media/gpu/windows/supported_profile_helpers.h +++ b/chromium/media/gpu/windows/supported_profile_helpers.h @@ -5,47 +5,35 @@ #ifndef MEDIA_GPU_WINDOWS_SUPPORTED_PROFILE_HELPERS_H_ #define MEDIA_GPU_WINDOWS_SUPPORTED_PROFILE_HELPERS_H_ -#include <d3d11_1.h> -#include <wrl/client.h> -#include <memory> -#include <utility> -#include <vector> - +#include "base/containers/flat_map.h" #include "gpu/config/gpu_driver_bug_workarounds.h" +#include "media/base/video_codecs.h" #include "media/gpu/media_gpu_export.h" #include "media/gpu/windows/d3d11_com_defs.h" -#include "ui/gfx/geometry/rect.h" - +#include "ui/gfx/geometry/size.h" namespace media { -using ResolutionPair = std::pair<gfx::Size, gfx::Size>; - -bool IsLegacyGPU(ID3D11Device* device); - -// Returns true if a ID3D11VideoDecoder can be created for |resolution_to_test| -// on the given |video_device|. -bool IsResolutionSupportedForDevice(const gfx::Size& resolution_to_test, - const GUID& decoder_guid, - ID3D11VideoDevice* video_device, - DXGI_FORMAT format); - -ResolutionPair GetMaxResolutionsForGUIDs( - const gfx::Size& default_max, - ID3D11VideoDevice* video_device, - const std::vector<GUID>& valid_guids, - const std::vector<gfx::Size>& resolutions_to_test, - DXGI_FORMAT format = DXGI_FORMAT_NV12); - -// TODO(dalecurtis): This function should be changed to use return values. +struct SupportedResolutionRange { + gfx::Size min_resolution; + gfx::Size max_landscape_resolution; + gfx::Size max_portrait_resolution; +}; + +using SupportedResolutionRangeMap = + base::flat_map<VideoCodecProfile, SupportedResolutionRange>; + +// Enumerates the extent of hardware decoding support for H.264, VP8, VP9, and +// AV1. If a codec is supported, its minimum and maximum supported resolutions +// are returned under the appropriate VideoCodecProfile entry. +// +// Notes: +// - VP8 and AV1 are only tested if their base::Feature entries are enabled. +// - Only baseline, main, and high H.264 profiles are supported. MEDIA_GPU_EXPORT -void GetResolutionsForDecoders(std::vector<GUID> h264_guids, - ComD3D11Device device, - const gpu::GpuDriverBugWorkarounds& workarounds, - ResolutionPair* h264_resolutions, - ResolutionPair* vp8_resolutions, - ResolutionPair* vp9_0_resolutions, - ResolutionPair* vp9_2_resolutions); +SupportedResolutionRangeMap GetSupportedD3D11VideoDecoderResolutions( + ComD3D11Device device, + const gpu::GpuDriverBugWorkarounds& workarounds); } // namespace media diff --git a/chromium/media/gpu/windows/supported_profile_helpers_unittest.cc b/chromium/media/gpu/windows/supported_profile_helpers_unittest.cc index 67aeb7d45b8..0d5da5b6e47 100644 --- a/chromium/media/gpu/windows/supported_profile_helpers_unittest.cc +++ b/chromium/media/gpu/windows/supported_profile_helpers_unittest.cc @@ -15,6 +15,7 @@ #include "media/base/media_switches.h" #include "media/base/test_helpers.h" #include "media/base/win/d3d11_mocks.h" +#include "media/gpu/windows/av1_guids.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; @@ -31,25 +32,28 @@ using ::testing::WithArgs; return; \ } while (0) -HRESULT SetIfSizeLessThan(D3D11_VIDEO_DECODER_DESC* desc, UINT* count) { - *count = 1; - return S_OK; -} +namespace { + +using PciId = std::pair<uint16_t, uint16_t>; +constexpr PciId kLegacyIntelGpu = {0x8086, 0x102}; +constexpr PciId kRecentIntelGpu = {0x8086, 0x100}; +constexpr PciId kLegacyAmdGpu = {0x1022, 0x130f}; +constexpr PciId kRecentAmdGpu = {0x1022, 0x130e}; + +constexpr gfx::Size kMinResolution(64, 64); +constexpr gfx::Size kFullHd(1920, 1088); +constexpr gfx::Size kSquare4k(4096, 4096); +constexpr gfx::Size kSquare8k(8192, 8192); + +} // namespace namespace media { +constexpr VideoCodecProfile kSupportedH264Profiles[] = { + H264PROFILE_BASELINE, H264PROFILE_MAIN, H264PROFILE_HIGH}; + class SupportedResolutionResolverTest : public ::testing::Test { public: - const std::pair<uint16_t, uint16_t> LegacyIntelGPU = {0x8086, 0x102}; - const std::pair<uint16_t, uint16_t> RecentIntelGPU = {0x8086, 0x100}; - const std::pair<uint16_t, uint16_t> LegacyAMDGPU = {0x1022, 0x130f}; - const std::pair<uint16_t, uint16_t> RecentAMDGPU = {0x1022, 0x130e}; - - const ResolutionPair ten_eighty = {{1920, 1080}, {1080, 1920}}; - const ResolutionPair zero = {{0, 0}, {0, 0}}; - const ResolutionPair tall4k = {{4096, 2304}, {2304, 4096}}; - const ResolutionPair eightKsquare = {{8192, 8192}, {8192, 8192}}; - void SetUp() override { gpu_workarounds_.disable_dxgi_zero_copy_video = false; mock_d3d11_device_ = CreateD3D11Mock<NiceMock<D3D11DeviceMock>>(); @@ -68,11 +72,11 @@ class SupportedResolutionResolverTest : public ::testing::Test { ON_CALL(*mock_dxgi_device_.Get(), GetAdapter(_)) .WillByDefault(SetComPointeeAndReturnOk<0>(mock_dxgi_adapter_.Get())); - SetGPUProfile(RecentIntelGPU); - SetMaxResolutionForGUID(D3D11_DECODER_PROFILE_H264_VLD_NOFGT, {4096, 4096}); + SetGpuProfile(kRecentIntelGpu); + SetMaxResolution(D3D11_DECODER_PROFILE_H264_VLD_NOFGT, kSquare4k); } - void SetMaxResolutionForGUID(const GUID& g, const gfx::Size& max_res) { + void SetMaxResolution(const GUID& g, const gfx::Size& max_res) { max_size_for_guids_[g] = max_res; ON_CALL(*mock_d3d11_video_device_.Get(), GetVideoDecoderConfigCount(_, _)) .WillByDefault( @@ -110,7 +114,7 @@ class SupportedResolutionResolverTest : public ::testing::Test { }))); } - void SetGPUProfile(std::pair<uint16_t, uint16_t> vendor_and_gpu) { + void SetGpuProfile(std::pair<uint16_t, uint16_t> vendor_and_gpu) { mock_adapter_desc_.DeviceId = static_cast<UINT>(vendor_and_gpu.second); mock_adapter_desc_.VendorId = static_cast<UINT>(vendor_and_gpu.first); @@ -119,6 +123,39 @@ class SupportedResolutionResolverTest : public ::testing::Test { DoAll(SetArgPointee<0>(mock_adapter_desc_), Return(S_OK))); } + void AssertDefaultSupport( + const SupportedResolutionRangeMap& supported_resolutions, + size_t expected_size = 3u) { + ASSERT_EQ(expected_size, supported_resolutions.size()); + for (const auto profile : kSupportedH264Profiles) { + auto it = supported_resolutions.find(profile); + ASSERT_NE(it, supported_resolutions.end()); + EXPECT_EQ(kMinResolution, it->second.min_resolution); + EXPECT_EQ(kFullHd, it->second.max_landscape_resolution); + EXPECT_EQ(gfx::Size(), it->second.max_portrait_resolution); + } + } + + void TestDecoderSupport(const GUID& decoder, + VideoCodecProfile profile, + const gfx::Size& max_res = kSquare4k, + const gfx::Size& max_landscape_res = kSquare4k, + const gfx::Size& max_portrait_res = kSquare4k) { + EnableDecoders({decoder}); + SetMaxResolution(decoder, max_res); + + const auto supported_resolutions = GetSupportedD3D11VideoDecoderResolutions( + mock_d3d11_device_, gpu_workarounds_); + AssertDefaultSupport(supported_resolutions, + base::size(kSupportedH264Profiles) + 1); + + auto it = supported_resolutions.find(profile); + ASSERT_NE(it, supported_resolutions.end()); + EXPECT_EQ(kMinResolution, it->second.min_resolution); + EXPECT_EQ(max_landscape_res, it->second.max_landscape_resolution); + EXPECT_EQ(max_portrait_res, it->second.max_portrait_resolution); + } + Microsoft::WRL::ComPtr<D3D11DeviceMock> mock_d3d11_device_; Microsoft::WRL::ComPtr<DXGIAdapterMock> mock_dxgi_adapter_; Microsoft::WRL::ComPtr<DXGIDeviceMock> mock_dxgi_device_; @@ -131,144 +168,128 @@ class SupportedResolutionResolverTest : public ::testing::Test { return memcmp(&a, &b, sizeof(GUID)) < 0; } }; - std::map<GUID, gfx::Size, GUIDComparison> max_size_for_guids_; + base::flat_map<GUID, gfx::Size, GUIDComparison> max_size_for_guids_; }; -TEST_F(SupportedResolutionResolverTest, NoDeviceAllDefault) { +TEST_F(SupportedResolutionResolverTest, HasH264SupportByDefault) { DONT_RUN_ON_WIN_7(); + AssertDefaultSupport( + GetSupportedD3D11VideoDecoderResolutions(nullptr, gpu_workarounds_)); - ResolutionPair h264_res_expected = {{1, 2}, {3, 4}}; - ResolutionPair h264_res = {{1, 2}, {3, 4}}; - ResolutionPair vp8_res; - ResolutionPair vp9_0_res; - ResolutionPair vp9_2_res; - GetResolutionsForDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT}, nullptr, - gpu_workarounds_, &h264_res, &vp8_res, &vp9_0_res, - &vp9_2_res); - - ASSERT_EQ(h264_res, h264_res_expected); - ASSERT_EQ(vp8_res, zero); - ASSERT_EQ(vp9_0_res, zero); - ASSERT_EQ(vp9_0_res, zero); -} - -TEST_F(SupportedResolutionResolverTest, LegacyGPUAllDefault) { - DONT_RUN_ON_WIN_7(); + SetGpuProfile(kLegacyIntelGpu); + AssertDefaultSupport(GetSupportedD3D11VideoDecoderResolutions( + mock_d3d11_device_, gpu_workarounds_)); - SetGPUProfile(LegacyIntelGPU); - - ResolutionPair h264_res_expected = {{1, 2}, {3, 4}}; - ResolutionPair h264_res = {{1, 2}, {3, 4}}; - ResolutionPair vp8_res; - ResolutionPair vp9_0_res; - ResolutionPair vp9_2_res; - GetResolutionsForDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT}, - mock_d3d11_device_, gpu_workarounds_, &h264_res, - &vp8_res, &vp9_0_res, &vp9_2_res); - - ASSERT_EQ(h264_res, h264_res_expected); - ASSERT_EQ(vp8_res, zero); - ASSERT_EQ(vp9_2_res, zero); - ASSERT_EQ(vp9_0_res, zero); + SetGpuProfile(kLegacyAmdGpu); + AssertDefaultSupport(GetSupportedD3D11VideoDecoderResolutions( + mock_d3d11_device_, gpu_workarounds_)); } TEST_F(SupportedResolutionResolverTest, WorkaroundsDisableVpx) { DONT_RUN_ON_WIN_7(); - gpu_workarounds_.disable_dxgi_zero_copy_video = true; - EnableDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT}); + gpu_workarounds_.disable_accelerated_vp8_decode = true; + gpu_workarounds_.disable_accelerated_vp9_decode = true; + EnableDecoders({D3D11_DECODER_PROFILE_VP8_VLD, + D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0, + D3D11_DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2}); - ResolutionPair h264_res; - ResolutionPair vp8_res; - ResolutionPair vp9_0_res; - ResolutionPair vp9_2_res; - GetResolutionsForDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT}, - mock_d3d11_device_, gpu_workarounds_, &h264_res, - &vp8_res, &vp9_0_res, &vp9_2_res); + AssertDefaultSupport(GetSupportedD3D11VideoDecoderResolutions( + mock_d3d11_device_, gpu_workarounds_)); +} - ASSERT_EQ(h264_res, tall4k); +TEST_F(SupportedResolutionResolverTest, H264Supports4k) { + DONT_RUN_ON_WIN_7(); - ASSERT_EQ(vp8_res, zero); - ASSERT_EQ(vp9_0_res, zero); - ASSERT_EQ(vp9_2_res, zero); + EnableDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT}); + const auto supported_resolutions = GetSupportedD3D11VideoDecoderResolutions( + mock_d3d11_device_, gpu_workarounds_); + + ASSERT_EQ(3u, supported_resolutions.size()); + for (const auto profile : kSupportedH264Profiles) { + auto it = supported_resolutions.find(profile); + ASSERT_NE(it, supported_resolutions.end()); + EXPECT_EQ(kMinResolution, it->second.min_resolution); + EXPECT_EQ(kSquare4k, it->second.max_landscape_resolution); + EXPECT_EQ(kSquare4k, it->second.max_portrait_resolution); + } } -TEST_F(SupportedResolutionResolverTest, VP8_Supports4k) { +TEST_F(SupportedResolutionResolverTest, VP8Supports4k) { DONT_RUN_ON_WIN_7(); base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature(kMediaFoundationVP8Decoding); + TestDecoderSupport(D3D11_DECODER_PROFILE_VP8_VLD, VP8PROFILE_ANY); +} - EnableDecoders( - {D3D11_DECODER_PROFILE_H264_VLD_NOFGT, D3D11_DECODER_PROFILE_VP8_VLD}); - SetMaxResolutionForGUID(D3D11_DECODER_PROFILE_VP8_VLD, {4096, 4096}); - - ResolutionPair h264_res; - ResolutionPair vp8_res; - ResolutionPair vp9_0_res; - ResolutionPair vp9_2_res; - GetResolutionsForDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT}, - mock_d3d11_device_, gpu_workarounds_, &h264_res, - &vp8_res, &vp9_0_res, &vp9_2_res); - - ASSERT_EQ(h264_res, tall4k); - - ASSERT_EQ(vp8_res, tall4k); - - ASSERT_EQ(vp9_0_res, zero); +TEST_F(SupportedResolutionResolverTest, VP9Profile0Supports8k) { + DONT_RUN_ON_WIN_7(); + TestDecoderSupport(D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0, + VP9PROFILE_PROFILE0, kSquare8k, kSquare8k, kSquare8k); +} - ASSERT_EQ(vp9_2_res, zero); +TEST_F(SupportedResolutionResolverTest, VP9Profile2Supports8k) { + DONT_RUN_ON_WIN_7(); + TestDecoderSupport(D3D11_DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2, + VP9PROFILE_PROFILE2, kSquare8k, kSquare8k, kSquare8k); } -TEST_F(SupportedResolutionResolverTest, VP9_0Supports8k) { +TEST_F(SupportedResolutionResolverTest, MultipleCodecs) { DONT_RUN_ON_WIN_7(); + SetGpuProfile(kRecentAmdGpu); + + // H.264 and VP9.0 are the most common supported codecs. EnableDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT, D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0}); - SetMaxResolutionForGUID(D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0, {8192, 8192}); - - ResolutionPair h264_res; - ResolutionPair vp8_res; - ResolutionPair vp9_0_res; - ResolutionPair vp9_2_res; - GetResolutionsForDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT}, - mock_d3d11_device_, gpu_workarounds_, &h264_res, - &vp8_res, &vp9_0_res, &vp9_2_res); - - ASSERT_EQ(h264_res, tall4k); - - ASSERT_EQ(vp8_res, zero); - - ASSERT_EQ(vp9_0_res, eightKsquare); + SetMaxResolution(D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0, kSquare8k); + + const auto supported_resolutions = GetSupportedD3D11VideoDecoderResolutions( + mock_d3d11_device_, gpu_workarounds_); + + ASSERT_EQ(base::size(kSupportedH264Profiles) + 1, + supported_resolutions.size()); + for (const auto profile : kSupportedH264Profiles) { + auto it = supported_resolutions.find(profile); + ASSERT_NE(it, supported_resolutions.end()); + EXPECT_EQ(kMinResolution, it->second.min_resolution); + EXPECT_EQ(kSquare4k, it->second.max_landscape_resolution); + EXPECT_EQ(kSquare4k, it->second.max_portrait_resolution); + } - ASSERT_EQ(vp9_2_res, zero); + auto it = supported_resolutions.find(VP9PROFILE_PROFILE0); + ASSERT_NE(it, supported_resolutions.end()); + EXPECT_EQ(kMinResolution, it->second.min_resolution); + EXPECT_EQ(kSquare8k, it->second.max_landscape_resolution); + EXPECT_EQ(kSquare8k, it->second.max_portrait_resolution); } -TEST_F(SupportedResolutionResolverTest, BothVP9ProfilesSupported) { +TEST_F(SupportedResolutionResolverTest, AV1ProfileMainSupports8k) { DONT_RUN_ON_WIN_7(); - EnableDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT, - D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0, - D3D11_DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2}); - SetMaxResolutionForGUID(D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0, {8192, 8192}); - SetMaxResolutionForGUID(D3D11_DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2, - {8192, 8192}); - - ResolutionPair h264_res; - ResolutionPair vp8_res; - ResolutionPair vp9_0_res; - ResolutionPair vp9_2_res; - GetResolutionsForDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT}, - mock_d3d11_device_, gpu_workarounds_, &h264_res, - &vp8_res, &vp9_0_res, &vp9_2_res); + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(kMediaFoundationAV1Decoding); + TestDecoderSupport(DXVA_ModeAV1_VLD_Profile0, AV1PROFILE_PROFILE_MAIN, + kSquare8k, kSquare8k, kSquare8k); +} - ASSERT_EQ(h264_res, tall4k); +TEST_F(SupportedResolutionResolverTest, AV1ProfileHighSupports8k) { + DONT_RUN_ON_WIN_7(); - ASSERT_EQ(vp8_res, zero); + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(kMediaFoundationAV1Decoding); + TestDecoderSupport(DXVA_ModeAV1_VLD_Profile1, AV1PROFILE_PROFILE_HIGH, + kSquare8k, kSquare8k, kSquare8k); +} - ASSERT_EQ(vp9_0_res, eightKsquare); +TEST_F(SupportedResolutionResolverTest, AV1ProfileProSupports8k) { + DONT_RUN_ON_WIN_7(); - ASSERT_EQ(vp9_2_res, eightKsquare); + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(kMediaFoundationAV1Decoding); + TestDecoderSupport(DXVA_ModeAV1_VLD_Profile2, AV1PROFILE_PROFILE_PRO, + kSquare8k, kSquare8k, kSquare8k); } } // namespace media |