blob: 2470088d66db0f6cfc85bea1ec9b80bf639d4b14 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/windows/supported_profile_helpers.h"
#include <d3d11.h>
#include <d3d11_1.h>
#include <initguid.h>
#include <map>
#include <utility>
#include <vector>
#include "base/test/scoped_feature_list.h"
#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::_;
using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::WithArgs;
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:
void SetUp() override {
gpu_workarounds_.disable_dxgi_zero_copy_video = false;
mock_d3d11_device_ = MakeComPtr<NiceMock<D3D11DeviceMock>>();
mock_dxgi_device_ = MakeComPtr<NiceMock<DXGIDeviceMock>>();
ON_CALL(*mock_d3d11_device_.Get(), QueryInterface(IID_IDXGIDevice, _))
.WillByDefault(SetComPointeeAndReturnOk<1>(mock_dxgi_device_.Get()));
mock_d3d11_video_device_ = MakeComPtr<NiceMock<D3D11VideoDeviceMock>>();
ON_CALL(*mock_d3d11_device_.Get(), QueryInterface(IID_ID3D11VideoDevice, _))
.WillByDefault(
SetComPointeeAndReturnOk<1>(mock_d3d11_video_device_.Get()));
mock_dxgi_adapter_ = MakeComPtr<NiceMock<DXGIAdapterMock>>();
ON_CALL(*mock_dxgi_device_.Get(), GetAdapter(_))
.WillByDefault(SetComPointeeAndReturnOk<0>(mock_dxgi_adapter_.Get()));
SetGpuProfile(kRecentIntelGpu);
SetMaxResolution(D3D11_DECODER_PROFILE_H264_VLD_NOFGT, kSquare4k);
}
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(
WithArgs<0, 1>(Invoke([this](const D3D11_VIDEO_DECODER_DESC* desc,
UINT* count) -> HRESULT {
*count = 0;
const auto& itr = this->max_size_for_guids_.find(desc->Guid);
if (itr == this->max_size_for_guids_.end())
return E_FAIL;
const gfx::Size max = itr->second;
if (max.height() < 0 || max.width() < 0)
return E_FAIL;
if (static_cast<UINT>(max.height()) < desc->SampleHeight)
return E_FAIL;
if (static_cast<UINT>(max.width()) < desc->SampleWidth)
return S_OK;
*count = 1;
return S_OK;
})));
}
void EnableDecoders(const std::vector<GUID>& decoder_guids) {
ON_CALL(*mock_d3d11_video_device_.Get(), GetVideoDecoderProfileCount())
.WillByDefault(Return(decoder_guids.size()));
// Note that we don't check if the guid in the config actually matches
// |decoder_profile|. Perhaps we should.
ON_CALL(*mock_d3d11_video_device_.Get(), GetVideoDecoderProfile(_, _))
.WillByDefault(WithArgs<0, 1>(
Invoke([decoder_guids](UINT p_idx, GUID* guid) -> HRESULT {
if (p_idx >= decoder_guids.size())
return E_FAIL;
*guid = decoder_guids.at(p_idx);
return S_OK;
})));
}
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);
ON_CALL(*mock_dxgi_adapter_.Get(), GetDesc(_))
.WillByDefault(
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,
std::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_;
Microsoft::WRL::ComPtr<D3D11VideoDeviceMock> mock_d3d11_video_device_;
DXGI_ADAPTER_DESC mock_adapter_desc_;
gpu::GpuDriverBugWorkarounds gpu_workarounds_;
struct GUIDComparison {
bool operator()(const GUID& a, const GUID& b) const {
return memcmp(&a, &b, sizeof(GUID)) < 0;
}
};
base::flat_map<GUID, gfx::Size, GUIDComparison> max_size_for_guids_;
};
TEST_F(SupportedResolutionResolverTest, WorkaroundsDisableAv1) {
// Enable the av1 decoder.
EnableDecoders({DXVA_ModeAV1_VLD_Profile0});
SetMaxResolution(DXVA_ModeAV1_VLD_Profile0, kSquare8k);
gpu_workarounds_.disable_accelerated_av1_decode = true;
const auto supported_resolutions = GetSupportedD3D11VideoDecoderResolutions(
mock_d3d11_device_, gpu_workarounds_);
auto av1_supported_res = supported_resolutions.find(AV1PROFILE_PROFILE_MAIN);
// There should be no supported av1 resolutions.
ASSERT_EQ(av1_supported_res, supported_resolutions.end());
}
TEST_F(SupportedResolutionResolverTest, HasH264SupportByDefault) {
AssertDefaultSupport(
GetSupportedD3D11VideoDecoderResolutions(nullptr, gpu_workarounds_));
SetGpuProfile(kLegacyIntelGpu);
AssertDefaultSupport(GetSupportedD3D11VideoDecoderResolutions(
mock_d3d11_device_, gpu_workarounds_));
SetGpuProfile(kLegacyAmdGpu);
AssertDefaultSupport(GetSupportedD3D11VideoDecoderResolutions(
mock_d3d11_device_, gpu_workarounds_));
}
TEST_F(SupportedResolutionResolverTest, WorkaroundsDisableVpx) {
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});
AssertDefaultSupport(GetSupportedD3D11VideoDecoderResolutions(
mock_d3d11_device_, gpu_workarounds_));
}
TEST_F(SupportedResolutionResolverTest, WorkaroundsDisableVp92) {
gpu_workarounds_.disable_accelerated_vp9_profile2_decode = true;
EnableDecoders({D3D11_DECODER_PROFILE_VP8_VLD,
D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0,
D3D11_DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2});
const auto supported_resolutions = GetSupportedD3D11VideoDecoderResolutions(
mock_d3d11_device_, gpu_workarounds_);
// There should be no supported vp9.2 resolutions.
ASSERT_EQ(supported_resolutions.find(VP9PROFILE_PROFILE2),
supported_resolutions.end());
// vp9.0 should still be available.
ASSERT_NE(supported_resolutions.find(VP9PROFILE_PROFILE0),
supported_resolutions.end());
}
TEST_F(SupportedResolutionResolverTest, H264Supports4k) {
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, VP8Supports4k) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(kMediaFoundationVP8Decoding);
EnableDecoders({D3D11_DECODER_PROFILE_VP8_VLD});
SetMaxResolution(D3D11_DECODER_PROFILE_VP8_VLD, kSquare4k);
const auto supported_resolutions = GetSupportedD3D11VideoDecoderResolutions(
mock_d3d11_device_, gpu_workarounds_);
auto it = supported_resolutions.find(VP8PROFILE_ANY);
ASSERT_NE(it, supported_resolutions.end());
EXPECT_EQ(kSquare4k, it->second.max_landscape_resolution);
EXPECT_EQ(kSquare4k, it->second.max_portrait_resolution);
constexpr gfx::Size kMinVp8Resolution = gfx::Size(640, 480);
EXPECT_EQ(kMinVp8Resolution, it->second.min_resolution);
}
TEST_F(SupportedResolutionResolverTest, VP9Profile0Supports8k) {
TestDecoderSupport(D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0,
VP9PROFILE_PROFILE0, kSquare8k, kSquare8k, kSquare8k);
}
TEST_F(SupportedResolutionResolverTest, VP9Profile2Supports8k) {
TestDecoderSupport(D3D11_DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2,
VP9PROFILE_PROFILE2, kSquare8k, kSquare8k, kSquare8k);
}
TEST_F(SupportedResolutionResolverTest, MultipleCodecs) {
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});
SetMaxResolution(D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0, kSquare8k);
const auto supported_resolutions = GetSupportedD3D11VideoDecoderResolutions(
mock_d3d11_device_, gpu_workarounds_);
ASSERT_EQ(std::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);
}
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, AV1ProfileMainSupports8k) {
TestDecoderSupport(DXVA_ModeAV1_VLD_Profile0, AV1PROFILE_PROFILE_MAIN,
kSquare8k, kSquare8k, kSquare8k);
}
TEST_F(SupportedResolutionResolverTest, AV1ProfileHighSupports8k) {
TestDecoderSupport(DXVA_ModeAV1_VLD_Profile1, AV1PROFILE_PROFILE_HIGH,
kSquare8k, kSquare8k, kSquare8k);
}
TEST_F(SupportedResolutionResolverTest, AV1ProfileProSupports8k) {
TestDecoderSupport(DXVA_ModeAV1_VLD_Profile2, AV1PROFILE_PROFILE_PRO,
kSquare8k, kSquare8k, kSquare8k);
}
#if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
TEST_F(SupportedResolutionResolverTest, H265Supports8kIfEnabled) {
EnableDecoders({D3D11_DECODER_PROFILE_HEVC_VLD_MAIN});
SetMaxResolution(D3D11_DECODER_PROFILE_HEVC_VLD_MAIN, kSquare8k);
const auto resolutions_for_feature = GetSupportedD3D11VideoDecoderResolutions(
mock_d3d11_device_, gpu_workarounds_);
ASSERT_EQ(4u, resolutions_for_feature.size());
const auto it = resolutions_for_feature.find(HEVCPROFILE_MAIN);
ASSERT_NE(it, resolutions_for_feature.end());
ASSERT_EQ(it->second.max_landscape_resolution, kSquare8k);
ASSERT_EQ(it->second.max_portrait_resolution, kSquare8k);
}
#endif // BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
} // namespace media