blob: 83a704111168f95e801b2c55b3187f68d22abcb0 [file] [log] [blame]
// Copyright 2019 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.
#include <string.h>
#include <utility>
#include "base/callback_helpers.h"
#include "base/test/task_environment.h"
#include "media/gpu/windows/d3d11_copying_texture_wrapper.h"
#include "media/gpu/windows/d3d11_texture_wrapper.h"
#include "media/gpu/windows/d3d11_video_processor_proxy.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/hdr_metadata_helper_win.h"
using ::testing::_;
using ::testing::Bool;
using ::testing::Combine;
using ::testing::Return;
using ::testing::Values;
namespace media {
class MockVideoProcessorProxy : public VideoProcessorProxy {
public:
MockVideoProcessorProxy() : VideoProcessorProxy(nullptr, nullptr) {}
Status Init(uint32_t width, uint32_t height) override {
return MockInit(width, height);
}
HRESULT CreateVideoProcessorOutputView(
ID3D11Texture2D* output_texture,
D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC* output_view_descriptor,
ID3D11VideoProcessorOutputView** output_view) override {
return MockCreateVideoProcessorOutputView();
}
HRESULT CreateVideoProcessorInputView(
ID3D11Texture2D* input_texture,
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC* input_view_descriptor,
ID3D11VideoProcessorInputView** input_view) override {
return MockCreateVideoProcessorInputView();
}
void SetStreamColorSpace(const gfx::ColorSpace& color_space) override {
last_stream_color_space_ = color_space;
}
void SetOutputColorSpace(const gfx::ColorSpace& color_space) override {
last_output_color_space_ = color_space;
}
void SetStreamHDRMetadata(
const DXGI_HDR_METADATA_HDR10& stream_metadata) override {
last_stream_metadata_ = stream_metadata;
}
void SetDisplayHDRMetadata(
const DXGI_HDR_METADATA_HDR10& display_metadata) override {
last_display_metadata_ = display_metadata;
}
HRESULT VideoProcessorBlt(ID3D11VideoProcessorOutputView* output_view,
UINT output_frameno,
UINT stream_count,
D3D11_VIDEO_PROCESSOR_STREAM* streams) override {
return MockVideoProcessorBlt();
}
MOCK_METHOD2(MockInit, Status(uint32_t, uint32_t));
MOCK_METHOD0(MockCreateVideoProcessorOutputView, HRESULT());
MOCK_METHOD0(MockCreateVideoProcessorInputView, HRESULT());
MOCK_METHOD0(MockVideoProcessorBlt, HRESULT());
// Most recent arguments to SetStream/OutputColorSpace()/etc.
absl::optional<gfx::ColorSpace> last_stream_color_space_;
absl::optional<gfx::ColorSpace> last_output_color_space_;
absl::optional<DXGI_HDR_METADATA_HDR10> last_stream_metadata_;
absl::optional<DXGI_HDR_METADATA_HDR10> last_display_metadata_;
private:
~MockVideoProcessorProxy() override = default;
};
class MockTexture2DWrapper : public Texture2DWrapper {
public:
MockTexture2DWrapper() {}
Status ProcessTexture(const gfx::ColorSpace& input_color_space,
MailboxHolderArray* mailbox_dest,
gfx::ColorSpace* output_color_space) override {
// Pretend we created an arbitrary color space, so that we're sure that it
// is returned from the copying wrapper.
*output_color_space = gfx::ColorSpace::CreateHDR10();
return MockProcessTexture();
}
Status Init(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
GetCommandBufferHelperCB get_helper_cb,
ComD3D11Texture2D in_texture,
size_t array_slice) override {
gpu_task_runner_ = std::move(gpu_task_runner);
return MockInit();
}
Status AcquireKeyedMutexIfNeeded() override {
return MockAcquireKeyedMutexIfNeeded();
}
MOCK_METHOD0(MockInit, Status());
MOCK_METHOD0(MockAcquireKeyedMutexIfNeeded, Status());
MOCK_METHOD0(MockProcessTexture, Status());
MOCK_METHOD1(SetStreamHDRMetadata,
void(const gfx::HDRMetadata& stream_metadata));
MOCK_METHOD1(SetDisplayHDRMetadata,
void(const DXGI_HDR_METADATA_HDR10& dxgi_display_metadata));
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
};
CommandBufferHelperPtr UselessHelper() {
return nullptr;
}
class D3D11CopyingTexture2DWrapperTest
: public ::testing::TestWithParam<
std::tuple<HRESULT, HRESULT, HRESULT, bool, bool, bool, bool, bool>> {
public:
#define FIELD(TYPE, NAME, INDEX) \
TYPE Get##NAME() { return std::get<INDEX>(GetParam()); }
FIELD(HRESULT, CreateVideoProcessorOutputView, 0)
FIELD(HRESULT, CreateVideoProcessorInputView, 1)
FIELD(HRESULT, VideoProcessorBlt, 2)
FIELD(bool, ProcessorProxyInit, 3)
FIELD(bool, TextureWrapperInit, 4)
FIELD(bool, ProcessTexture, 5)
FIELD(bool, PassthroughColorSpace, 6)
FIELD(bool, AcquireKeyedMutexIfNeeded, 7)
#undef FIELD
void SetUp() override {
gpu_task_runner_ = task_environment_.GetMainThreadTaskRunner();
}
scoped_refptr<MockVideoProcessorProxy> ExpectProcessorProxy() {
auto result = base::MakeRefCounted<MockVideoProcessorProxy>();
ON_CALL(*result.get(), MockInit(_, _))
.WillByDefault(Return(GetProcessorProxyInit()
? StatusCode::kOk
: StatusCode::kCodeOnlyForTesting));
ON_CALL(*result.get(), MockCreateVideoProcessorOutputView())
.WillByDefault(Return(GetCreateVideoProcessorOutputView()));
ON_CALL(*result.get(), MockCreateVideoProcessorInputView())
.WillByDefault(Return(GetCreateVideoProcessorInputView()));
ON_CALL(*result.get(), MockVideoProcessorBlt())
.WillByDefault(Return(GetVideoProcessorBlt()));
return result;
}
std::unique_ptr<MockTexture2DWrapper> ExpectTextureWrapper() {
auto result = std::make_unique<MockTexture2DWrapper>();
ON_CALL(*result.get(), MockInit())
.WillByDefault(Return(GetTextureWrapperInit()
? StatusCode::kOk
: StatusCode::kCodeOnlyForTesting));
ON_CALL(*result.get(), MockAcquireKeyedMutexIfNeeded())
.WillByDefault(Return(GetAcquireKeyedMutexIfNeeded()
? StatusCode::kOk
: StatusCode::kCodeOnlyForTesting));
ON_CALL(*result.get(), MockProcessTexture())
.WillByDefault(Return(GetProcessTexture()
? StatusCode::kOk
: StatusCode::kCodeOnlyForTesting));
return result;
}
GetCommandBufferHelperCB CreateMockHelperCB() {
return base::BindRepeating(&UselessHelper);
}
bool InitSucceeds() {
return GetProcessorProxyInit() && GetTextureWrapperInit();
}
bool ProcessTextureSucceeds() {
return GetAcquireKeyedMutexIfNeeded() && GetProcessTexture() &&
SUCCEEDED(GetCreateVideoProcessorOutputView()) &&
SUCCEEDED(GetCreateVideoProcessorInputView()) &&
SUCCEEDED(GetVideoProcessorBlt());
}
base::test::TaskEnvironment task_environment_;
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
};
INSTANTIATE_TEST_CASE_P(CopyingTexture2DWrapperTest,
D3D11CopyingTexture2DWrapperTest,
Combine(Values(S_OK, E_FAIL),
Values(S_OK, E_FAIL),
Values(S_OK, E_FAIL),
Bool(),
Bool(),
Bool(),
Bool(),
Bool()));
// For ever potential return value combination for the D3D11VideoProcessor,
// make sure that any failures result in a total failure.
TEST_P(D3D11CopyingTexture2DWrapperTest,
CopyingTextureWrapperProcessesCorrectly) {
gfx::Size size;
auto processor = ExpectProcessorProxy();
MockVideoProcessorProxy* processor_raw = processor.get();
// Provide an unlikely color space, to see if it gets to the video processor,
// if we're not just doing a pass-through of the input.
absl::optional<gfx::ColorSpace> copy_color_space;
if (!GetPassthroughColorSpace())
copy_color_space = gfx::ColorSpace::CreateDisplayP3D65();
auto texture_wrapper = ExpectTextureWrapper();
MockTexture2DWrapper* texture_wrapper_raw = texture_wrapper.get();
auto wrapper = std::make_unique<CopyingTexture2DWrapper>(
size, std::move(texture_wrapper), processor, nullptr, copy_color_space);
// TODO: check |gpu_task_runner_|.
MailboxHolderArray mailboxes;
gfx::ColorSpace input_color_space = gfx::ColorSpace::CreateSCRGBLinear();
gfx::ColorSpace output_color_space;
EXPECT_EQ(wrapper
->Init(gpu_task_runner_, CreateMockHelperCB(),
/*texture_d3d=*/nullptr, /*array_slice=*/0)
.is_ok(),
InitSucceeds());
task_environment_.RunUntilIdle();
if (GetProcessorProxyInit())
EXPECT_EQ(texture_wrapper_raw->gpu_task_runner_, gpu_task_runner_);
EXPECT_EQ(
wrapper
->ProcessTexture(input_color_space, &mailboxes, &output_color_space)
.is_ok(),
ProcessTextureSucceeds());
if (ProcessTextureSucceeds()) {
// Regardless of what the input space is, the output should be provided by
// the mock wrapper.
EXPECT_EQ(gfx::ColorSpace::CreateHDR10(), output_color_space);
// Also expect that the input and copy spaces were provided to the video
// processor as the stream and output color spaces, respectively. If no
// copy space was provided, then expect that the output is the input.
EXPECT_TRUE(processor_raw->last_stream_color_space_);
EXPECT_EQ(*processor_raw->last_stream_color_space_, input_color_space);
EXPECT_TRUE(processor_raw->last_output_color_space_);
EXPECT_EQ(*processor_raw->last_output_color_space_,
copy_color_space ? *copy_color_space : input_color_space);
}
// TODO: verify that these aren't sent multiple times, unless they change.
}
TEST_P(D3D11CopyingTexture2DWrapperTest, HDRMetadataIsSentToVideoProcessor) {
gfx::HDRMetadata metadata;
metadata.color_volume_metadata.primary_r =
gfx::ColorVolumeMetadata::Chromaticity(0.1, 0.2);
metadata.color_volume_metadata.primary_g =
gfx::ColorVolumeMetadata::Chromaticity(0.3, 0.4);
metadata.color_volume_metadata.primary_b =
gfx::ColorVolumeMetadata::Chromaticity(0.5, 0.6);
metadata.color_volume_metadata.white_point =
gfx::ColorVolumeMetadata::Chromaticity(0.7, 0.8);
metadata.color_volume_metadata.luminance_max = 0.9;
metadata.color_volume_metadata.luminance_min = 0.05;
metadata.max_content_light_level = 1000;
metadata.max_frame_average_light_level = 10000;
auto processor = ExpectProcessorProxy();
MockVideoProcessorProxy* processor_raw = processor.get();
auto wrapper = std::make_unique<CopyingTexture2DWrapper>(
gfx::Size(100, 200), ExpectTextureWrapper(), std::move(processor),
nullptr, gfx::ColorSpace::CreateSCRGBLinear());
const DXGI_HDR_METADATA_HDR10 dxgi_metadata =
gl::HDRMetadataHelperWin::HDRMetadataToDXGI(metadata);
wrapper->SetStreamHDRMetadata(metadata);
EXPECT_TRUE(processor_raw->last_stream_metadata_);
EXPECT_FALSE(processor_raw->last_display_metadata_);
EXPECT_EQ(memcmp(&dxgi_metadata, &(*processor_raw->last_stream_metadata_),
sizeof(dxgi_metadata)),
0);
processor_raw->last_stream_metadata_.reset();
wrapper->SetDisplayHDRMetadata(dxgi_metadata);
EXPECT_FALSE(processor_raw->last_stream_metadata_);
EXPECT_TRUE(processor_raw->last_display_metadata_);
EXPECT_EQ(memcmp(&dxgi_metadata, &(*processor_raw->last_display_metadata_),
sizeof(dxgi_metadata)),
0);
}
} // namespace media