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