1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <string.h>
6
7 #include <utility>
8
9 #include "base/callback_helpers.h"
10 #include "base/test/task_environment.h"
11 #include "media/gpu/windows/d3d11_copying_texture_wrapper.h"
12 #include "media/gpu/windows/d3d11_texture_wrapper.h"
13 #include "media/gpu/windows/d3d11_video_processor_proxy.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "ui/gl/hdr_metadata_helper_win.h"
17
18 using ::testing::_;
19 using ::testing::Bool;
20 using ::testing::Combine;
21 using ::testing::Return;
22 using ::testing::Values;
23
24 namespace media {
25
26 class MockVideoProcessorProxy : public VideoProcessorProxy {
27 public:
MockVideoProcessorProxy()28 MockVideoProcessorProxy() : VideoProcessorProxy(nullptr, nullptr) {}
29
Init(uint32_t width,uint32_t height)30 Status Init(uint32_t width, uint32_t height) override {
31 return MockInit(width, height);
32 }
33
CreateVideoProcessorOutputView(ID3D11Texture2D * output_texture,D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC * output_view_descriptor,ID3D11VideoProcessorOutputView ** output_view)34 HRESULT CreateVideoProcessorOutputView(
35 ID3D11Texture2D* output_texture,
36 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC* output_view_descriptor,
37 ID3D11VideoProcessorOutputView** output_view) override {
38 return MockCreateVideoProcessorOutputView();
39 }
40
CreateVideoProcessorInputView(ID3D11Texture2D * input_texture,D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC * input_view_descriptor,ID3D11VideoProcessorInputView ** input_view)41 HRESULT CreateVideoProcessorInputView(
42 ID3D11Texture2D* input_texture,
43 D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC* input_view_descriptor,
44 ID3D11VideoProcessorInputView** input_view) override {
45 return MockCreateVideoProcessorInputView();
46 }
47
SetStreamColorSpace(const gfx::ColorSpace & color_space)48 void SetStreamColorSpace(const gfx::ColorSpace& color_space) override {
49 last_stream_color_space_ = color_space;
50 }
51
SetOutputColorSpace(const gfx::ColorSpace & color_space)52 void SetOutputColorSpace(const gfx::ColorSpace& color_space) override {
53 last_output_color_space_ = color_space;
54 }
55
SetStreamHDRMetadata(const DXGI_HDR_METADATA_HDR10 & stream_metadata)56 void SetStreamHDRMetadata(
57 const DXGI_HDR_METADATA_HDR10& stream_metadata) override {
58 last_stream_metadata_ = stream_metadata;
59 }
60
SetDisplayHDRMetadata(const DXGI_HDR_METADATA_HDR10 & display_metadata)61 void SetDisplayHDRMetadata(
62 const DXGI_HDR_METADATA_HDR10& display_metadata) override {
63 last_display_metadata_ = display_metadata;
64 }
65
VideoProcessorBlt(ID3D11VideoProcessorOutputView * output_view,UINT output_frameno,UINT stream_count,D3D11_VIDEO_PROCESSOR_STREAM * streams)66 HRESULT VideoProcessorBlt(ID3D11VideoProcessorOutputView* output_view,
67 UINT output_frameno,
68 UINT stream_count,
69 D3D11_VIDEO_PROCESSOR_STREAM* streams) override {
70 return MockVideoProcessorBlt();
71 }
72
73 MOCK_METHOD2(MockInit, Status(uint32_t, uint32_t));
74 MOCK_METHOD0(MockCreateVideoProcessorOutputView, HRESULT());
75 MOCK_METHOD0(MockCreateVideoProcessorInputView, HRESULT());
76 MOCK_METHOD0(MockVideoProcessorBlt, HRESULT());
77
78 // Most recent arguments to SetStream/OutputColorSpace()/etc.
79 base::Optional<gfx::ColorSpace> last_stream_color_space_;
80 base::Optional<gfx::ColorSpace> last_output_color_space_;
81 base::Optional<DXGI_HDR_METADATA_HDR10> last_stream_metadata_;
82 base::Optional<DXGI_HDR_METADATA_HDR10> last_display_metadata_;
83
84 private:
85 ~MockVideoProcessorProxy() override = default;
86 };
87
88 class MockTexture2DWrapper : public Texture2DWrapper {
89 public:
MockTexture2DWrapper()90 MockTexture2DWrapper() {}
91
ProcessTexture(const gfx::ColorSpace & input_color_space,MailboxHolderArray * mailbox_dest,gfx::ColorSpace * output_color_space)92 Status ProcessTexture(const gfx::ColorSpace& input_color_space,
93 MailboxHolderArray* mailbox_dest,
94 gfx::ColorSpace* output_color_space) override {
95 // Pretend we created an arbitrary color space, so that we're sure that it
96 // is returned from the copying wrapper.
97 *output_color_space = gfx::ColorSpace::CreateHDR10();
98 return MockProcessTexture();
99 }
100
Init(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,GetCommandBufferHelperCB get_helper_cb,ComD3D11Texture2D in_texture,size_t array_slice)101 Status Init(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
102 GetCommandBufferHelperCB get_helper_cb,
103 ComD3D11Texture2D in_texture,
104 size_t array_slice) override {
105 gpu_task_runner_ = std::move(gpu_task_runner);
106 return MockInit();
107 }
108
109 MOCK_METHOD0(MockInit, Status());
110 MOCK_METHOD0(MockProcessTexture, Status());
111 MOCK_METHOD1(SetStreamHDRMetadata,
112 void(const gfx::HDRMetadata& stream_metadata));
113 MOCK_METHOD1(SetDisplayHDRMetadata,
114 void(const DXGI_HDR_METADATA_HDR10& dxgi_display_metadata));
115
116 scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
117 };
118
UselessHelper()119 CommandBufferHelperPtr UselessHelper() {
120 return nullptr;
121 }
122
123 class D3D11CopyingTexture2DWrapperTest
124 : public ::testing::TestWithParam<
125 std::tuple<HRESULT, HRESULT, HRESULT, bool, bool, bool, bool>> {
126 public:
127 #define FIELD(TYPE, NAME, INDEX) \
128 TYPE Get##NAME() { return std::get<INDEX>(GetParam()); }
129 FIELD(HRESULT, CreateVideoProcessorOutputView, 0)
130 FIELD(HRESULT, CreateVideoProcessorInputView, 1)
131 FIELD(HRESULT, VideoProcessorBlt, 2)
132 FIELD(bool, ProcessorProxyInit, 3)
133 FIELD(bool, TextureWrapperInit, 4)
134 FIELD(bool, ProcessTexture, 5)
135 FIELD(bool, PassthroughColorSpace, 6)
136 #undef FIELD
137
SetUp()138 void SetUp() override {
139 gpu_task_runner_ = task_environment_.GetMainThreadTaskRunner();
140 }
141
ExpectProcessorProxy()142 scoped_refptr<MockVideoProcessorProxy> ExpectProcessorProxy() {
143 auto result = base::MakeRefCounted<MockVideoProcessorProxy>();
144 ON_CALL(*result.get(), MockInit(_, _))
145 .WillByDefault(Return(GetProcessorProxyInit()
146 ? StatusCode::kOk
147 : StatusCode::kCodeOnlyForTesting));
148
149 ON_CALL(*result.get(), MockCreateVideoProcessorOutputView())
150 .WillByDefault(Return(GetCreateVideoProcessorOutputView()));
151
152 ON_CALL(*result.get(), MockCreateVideoProcessorInputView())
153 .WillByDefault(Return(GetCreateVideoProcessorInputView()));
154
155 ON_CALL(*result.get(), MockVideoProcessorBlt())
156 .WillByDefault(Return(GetVideoProcessorBlt()));
157
158 return result;
159 }
160
ExpectTextureWrapper()161 std::unique_ptr<MockTexture2DWrapper> ExpectTextureWrapper() {
162 auto result = std::make_unique<MockTexture2DWrapper>();
163
164 ON_CALL(*result.get(), MockInit())
165 .WillByDefault(Return(GetTextureWrapperInit()
166 ? StatusCode::kOk
167 : StatusCode::kCodeOnlyForTesting));
168
169 ON_CALL(*result.get(), MockProcessTexture())
170 .WillByDefault(Return(GetProcessTexture()
171 ? StatusCode::kOk
172 : StatusCode::kCodeOnlyForTesting));
173
174 return result;
175 }
176
CreateMockHelperCB()177 GetCommandBufferHelperCB CreateMockHelperCB() {
178 return base::BindRepeating(&UselessHelper);
179 }
180
InitSucceeds()181 bool InitSucceeds() {
182 return GetProcessorProxyInit() && GetTextureWrapperInit();
183 }
184
ProcessTextureSucceeds()185 bool ProcessTextureSucceeds() {
186 return GetProcessTexture() &&
187 SUCCEEDED(GetCreateVideoProcessorOutputView()) &&
188 SUCCEEDED(GetCreateVideoProcessorInputView()) &&
189 SUCCEEDED(GetVideoProcessorBlt());
190 }
191
192 base::test::TaskEnvironment task_environment_;
193 scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
194 };
195
196 INSTANTIATE_TEST_CASE_P(CopyingTexture2DWrapperTest,
197 D3D11CopyingTexture2DWrapperTest,
198 Combine(Values(S_OK, E_FAIL),
199 Values(S_OK, E_FAIL),
200 Values(S_OK, E_FAIL),
201 Bool(),
202 Bool(),
203 Bool(),
204 Bool()));
205
206 // For ever potential return value combination for the D3D11VideoProcessor,
207 // make sure that any failures result in a total failure.
TEST_P(D3D11CopyingTexture2DWrapperTest,CopyingTextureWrapperProcessesCorrectly)208 TEST_P(D3D11CopyingTexture2DWrapperTest,
209 CopyingTextureWrapperProcessesCorrectly) {
210 gfx::Size size;
211 auto processor = ExpectProcessorProxy();
212 MockVideoProcessorProxy* processor_raw = processor.get();
213 // Provide an unlikely color space, to see if it gets to the video processor,
214 // if we're not just doing a pass-through of the input.
215 base::Optional<gfx::ColorSpace> copy_color_space;
216 if (!GetPassthroughColorSpace())
217 copy_color_space = gfx::ColorSpace::CreateDisplayP3D65();
218 auto texture_wrapper = ExpectTextureWrapper();
219 MockTexture2DWrapper* texture_wrapper_raw = texture_wrapper.get();
220 auto wrapper = std::make_unique<CopyingTexture2DWrapper>(
221 size, std::move(texture_wrapper), processor, nullptr, copy_color_space);
222
223 // TODO: check |gpu_task_runner_|.
224
225 MailboxHolderArray mailboxes;
226 gfx::ColorSpace input_color_space = gfx::ColorSpace::CreateSCRGBLinear();
227 gfx::ColorSpace output_color_space;
228 EXPECT_EQ(wrapper
229 ->Init(gpu_task_runner_, CreateMockHelperCB(),
230 /*texture_d3d=*/nullptr, /*array_slice=*/0)
231 .is_ok(),
232 InitSucceeds());
233 task_environment_.RunUntilIdle();
234 if (GetProcessorProxyInit())
235 EXPECT_EQ(texture_wrapper_raw->gpu_task_runner_, gpu_task_runner_);
236 EXPECT_EQ(
237 wrapper
238 ->ProcessTexture(input_color_space, &mailboxes, &output_color_space)
239 .is_ok(),
240 ProcessTextureSucceeds());
241
242 if (ProcessTextureSucceeds()) {
243 // Regardless of what the input space is, the output should be provided by
244 // the mock wrapper.
245 EXPECT_EQ(gfx::ColorSpace::CreateHDR10(), output_color_space);
246
247 // Also expect that the input and copy spaces were provided to the video
248 // processor as the stream and output color spaces, respectively. If no
249 // copy space was provided, then expect that the output is the input.
250 EXPECT_TRUE(processor_raw->last_stream_color_space_);
251 EXPECT_EQ(*processor_raw->last_stream_color_space_, input_color_space);
252 EXPECT_TRUE(processor_raw->last_output_color_space_);
253 EXPECT_EQ(*processor_raw->last_output_color_space_,
254 copy_color_space ? *copy_color_space : input_color_space);
255 }
256
257 // TODO: verify that these aren't sent multiple times, unless they change.
258 }
259
TEST_P(D3D11CopyingTexture2DWrapperTest,HDRMetadataIsSentToVideoProcessor)260 TEST_P(D3D11CopyingTexture2DWrapperTest, HDRMetadataIsSentToVideoProcessor) {
261 gfx::HDRMetadata metadata;
262 metadata.mastering_metadata.primary_r =
263 gfx::MasteringMetadata::Chromaticity(0.1, 0.2);
264 metadata.mastering_metadata.primary_g =
265 gfx::MasteringMetadata::Chromaticity(0.3, 0.4);
266 metadata.mastering_metadata.primary_b =
267 gfx::MasteringMetadata::Chromaticity(0.5, 0.6);
268 metadata.mastering_metadata.white_point =
269 gfx::MasteringMetadata::Chromaticity(0.7, 0.8);
270 metadata.mastering_metadata.luminance_max = 0.9;
271 metadata.mastering_metadata.luminance_min = 0.05;
272 metadata.max_content_light_level = 1000;
273 metadata.max_frame_average_light_level = 10000;
274
275 auto processor = ExpectProcessorProxy();
276 MockVideoProcessorProxy* processor_raw = processor.get();
277 auto wrapper = std::make_unique<CopyingTexture2DWrapper>(
278 gfx::Size(100, 200), ExpectTextureWrapper(), std::move(processor),
279 nullptr, gfx::ColorSpace::CreateSCRGBLinear());
280
281 const DXGI_HDR_METADATA_HDR10 dxgi_metadata =
282 gl::HDRMetadataHelperWin::HDRMetadataToDXGI(metadata);
283
284 wrapper->SetStreamHDRMetadata(metadata);
285 EXPECT_TRUE(processor_raw->last_stream_metadata_);
286 EXPECT_FALSE(processor_raw->last_display_metadata_);
287 EXPECT_EQ(memcmp(&dxgi_metadata, &(*processor_raw->last_stream_metadata_),
288 sizeof(dxgi_metadata)),
289 0);
290 processor_raw->last_stream_metadata_.reset();
291
292 wrapper->SetDisplayHDRMetadata(dxgi_metadata);
293 EXPECT_FALSE(processor_raw->last_stream_metadata_);
294 EXPECT_TRUE(processor_raw->last_display_metadata_);
295 EXPECT_EQ(memcmp(&dxgi_metadata, &(*processor_raw->last_display_metadata_),
296 sizeof(dxgi_metadata)),
297 0);
298 }
299
300 } // namespace media
301