Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(53)

Side by Side Diff: gpu/ipc/service/direct_composition_surface_win_unittest.cc

Issue 2749023011: Add DirectComposition overlay support. (Closed)
Patch Set: post-review changes Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "gpu/ipc/service/direct_composition_surface_win.h" 5 #include "gpu/ipc/service/direct_composition_surface_win.h"
6 #include "base/memory/weak_ptr.h" 6 #include "base/memory/weak_ptr.h"
7 #include "base/run_loop.h" 7 #include "base/run_loop.h"
8 #include "base/synchronization/waitable_event.h" 8 #include "base/synchronization/waitable_event.h"
9 #include "base/threading/thread_task_runner_handle.h"
10 #include "skia/ext/platform_canvas.h"
11 #include "skia/ext/skia_utils_win.h"
9 #include "testing/gtest/include/gtest/gtest.h" 12 #include "testing/gtest/include/gtest/gtest.h"
10 #include "ui/base/win/hidden_window.h" 13 #include "ui/base/win/hidden_window.h"
14 #include "ui/gfx/skia_util.h"
15 #include "ui/gfx/transform.h"
16 #include "ui/gl/dc_renderer_layer_params.h"
11 #include "ui/gl/gl_angle_util_win.h" 17 #include "ui/gl/gl_angle_util_win.h"
12 #include "ui/gl/gl_context.h" 18 #include "ui/gl/gl_context.h"
19 #include "ui/gl/gl_image_dxgi.h"
13 #include "ui/gl/init/gl_factory.h" 20 #include "ui/gl/init/gl_factory.h"
21 #include "ui/platform_window/platform_window_delegate.h"
22 #include "ui/platform_window/win/win_window.h"
14 23
15 namespace gpu { 24 namespace gpu {
16 namespace { 25 namespace {
17 26
27 bool CheckIfDCSupported() {
28 if (!gl::QueryDirectCompositionDevice(
29 gl::QueryD3D11DeviceObjectFromANGLE())) {
30 LOG(WARNING)
31 << "GL implementation not using DirectComposition, skipping test.";
32 return false;
33 }
34 return true;
35 }
36
18 class TestImageTransportSurfaceDelegate 37 class TestImageTransportSurfaceDelegate
19 : public ImageTransportSurfaceDelegate, 38 : public ImageTransportSurfaceDelegate,
20 public base::SupportsWeakPtr<TestImageTransportSurfaceDelegate> { 39 public base::SupportsWeakPtr<TestImageTransportSurfaceDelegate> {
21 public: 40 public:
22 ~TestImageTransportSurfaceDelegate() override {} 41 ~TestImageTransportSurfaceDelegate() override {}
23 42
24 // ImageTransportSurfaceDelegate implementation. 43 // ImageTransportSurfaceDelegate implementation.
25 void DidCreateAcceleratedSurfaceChildWindow( 44 void DidCreateAcceleratedSurfaceChildWindow(
26 SurfaceHandle parent_window, 45 SurfaceHandle parent_window,
27 SurfaceHandle child_window) override {} 46 SurfaceHandle child_window) override {
47 if (parent_window)
48 ::SetParent(child_window, parent_window);
49 }
28 void DidSwapBuffersComplete(SwapBuffersCompleteParams params) override {} 50 void DidSwapBuffersComplete(SwapBuffersCompleteParams params) override {}
29 const gles2::FeatureInfo* GetFeatureInfo() const override { return nullptr; } 51 const gles2::FeatureInfo* GetFeatureInfo() const override { return nullptr; }
30 void SetLatencyInfoCallback(const LatencyInfoCallback& callback) override {} 52 void SetLatencyInfoCallback(const LatencyInfoCallback& callback) override {}
31 void UpdateVSyncParameters(base::TimeTicks timebase, 53 void UpdateVSyncParameters(base::TimeTicks timebase,
32 base::TimeDelta interval) override {} 54 base::TimeDelta interval) override {}
33 void AddFilter(IPC::MessageFilter* message_filter) override {} 55 void AddFilter(IPC::MessageFilter* message_filter) override {}
34 int32_t GetRouteID() const override { return 0; } 56 int32_t GetRouteID() const override { return 0; }
35 }; 57 };
36 58
59 class TestPlatformDelegate : public ui::PlatformWindowDelegate {
60 public:
61 // ui::PlatformWindowDelegate implementation.
62 void OnBoundsChanged(const gfx::Rect& new_bounds) override {}
63 void OnDamageRect(const gfx::Rect& damaged_region) override {}
64 void DispatchEvent(ui::Event* event) override {}
65 void OnCloseRequest() override {}
66 void OnClosed() override {}
67 void OnWindowStateChanged(ui::PlatformWindowState new_state) override {}
68 void OnLostCapture() override {}
69 void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget,
70 float device_pixel_ratio) override {}
71 void OnAcceleratedWidgetDestroyed() override {}
72 void OnActivationChanged(bool active) override {}
73 };
74
37 void RunPendingTasks(scoped_refptr<base::TaskRunner> task_runner) { 75 void RunPendingTasks(scoped_refptr<base::TaskRunner> task_runner) {
38 base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC, 76 base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC,
39 base::WaitableEvent::InitialState::NOT_SIGNALED); 77 base::WaitableEvent::InitialState::NOT_SIGNALED);
40 task_runner->PostTask(FROM_HERE, 78 task_runner->PostTask(FROM_HERE,
41 Bind(&base::WaitableEvent::Signal, Unretained(&done))); 79 Bind(&base::WaitableEvent::Signal, Unretained(&done)));
42 done.Wait(); 80 done.Wait();
43 } 81 }
44 82
45 void DestroySurface(scoped_refptr<DirectCompositionSurfaceWin> surface) { 83 void DestroySurface(scoped_refptr<DirectCompositionSurfaceWin> surface) {
46 scoped_refptr<base::TaskRunner> task_runner = 84 scoped_refptr<base::TaskRunner> task_runner =
47 surface->GetWindowTaskRunnerForTesting(); 85 surface->GetWindowTaskRunnerForTesting();
48 DCHECK(surface->HasOneRef()); 86 DCHECK(surface->HasOneRef());
49 87
50 surface = nullptr; 88 surface = nullptr;
51 89
52 // Ensure that the ChildWindowWin posts the task to delete the thread to the 90 // Ensure that the ChildWindowWin posts the task to delete the thread to the
53 // main loop before doing RunUntilIdle. Otherwise the child threads could 91 // main loop before doing RunUntilIdle. Otherwise the child threads could
54 // outlive the main thread. 92 // outlive the main thread.
55 RunPendingTasks(task_runner); 93 RunPendingTasks(task_runner);
56 94
57 base::RunLoop().RunUntilIdle(); 95 base::RunLoop().RunUntilIdle();
58 } 96 }
59 97
60 TEST(DirectCompositionSurfaceTest, TestMakeCurrent) { 98 TEST(DirectCompositionSurfaceTest, TestMakeCurrent) {
61 if (!gl::QueryDirectCompositionDevice( 99 if (!CheckIfDCSupported())
62 gl::QueryD3D11DeviceObjectFromANGLE())) {
63 LOG(WARNING)
64 << "GL implementation not using DirectComposition, skipping test.";
65 return; 100 return;
66 }
67 101
68 TestImageTransportSurfaceDelegate delegate; 102 TestImageTransportSurfaceDelegate delegate;
69 103
70 scoped_refptr<DirectCompositionSurfaceWin> surface1( 104 scoped_refptr<DirectCompositionSurfaceWin> surface1(
71 new DirectCompositionSurfaceWin(delegate.AsWeakPtr(), 105 new DirectCompositionSurfaceWin(delegate.AsWeakPtr(),
72 ui::GetHiddenWindow())); 106 ui::GetHiddenWindow()));
73 EXPECT_TRUE(surface1->Initialize()); 107 EXPECT_TRUE(surface1->Initialize());
74 surface1->SetEnableDCLayers(true); 108 surface1->SetEnableDCLayers(true);
75 109
76 scoped_refptr<gl::GLContext> context1 = gl::init::CreateGLContext( 110 scoped_refptr<gl::GLContext> context1 = gl::init::CreateGLContext(
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 EXPECT_TRUE(context1->MakeCurrent(surface1.get())); 153 EXPECT_TRUE(context1->MakeCurrent(surface1.get()));
120 context2 = nullptr; 154 context2 = nullptr;
121 context1 = nullptr; 155 context1 = nullptr;
122 156
123 DestroySurface(std::move(surface1)); 157 DestroySurface(std::move(surface1));
124 DestroySurface(std::move(surface2)); 158 DestroySurface(std::move(surface2));
125 } 159 }
126 160
127 // Tests that switching using EnableDCLayers works. 161 // Tests that switching using EnableDCLayers works.
128 TEST(DirectCompositionSurfaceTest, DXGIDCLayerSwitch) { 162 TEST(DirectCompositionSurfaceTest, DXGIDCLayerSwitch) {
129 if (!gl::QueryDirectCompositionDevice( 163 if (!CheckIfDCSupported())
130 gl::QueryD3D11DeviceObjectFromANGLE())) {
131 LOG(WARNING)
132 << "GL implementation not using DirectComposition, skipping test.";
133 return; 164 return;
134 }
135 165
136 TestImageTransportSurfaceDelegate delegate; 166 TestImageTransportSurfaceDelegate delegate;
137 167
138 scoped_refptr<DirectCompositionSurfaceWin> surface( 168 scoped_refptr<DirectCompositionSurfaceWin> surface(
139 new DirectCompositionSurfaceWin(delegate.AsWeakPtr(), 169 new DirectCompositionSurfaceWin(delegate.AsWeakPtr(),
140 ui::GetHiddenWindow())); 170 ui::GetHiddenWindow()));
141 EXPECT_TRUE(surface->Initialize()); 171 EXPECT_TRUE(surface->Initialize());
142 172
143 scoped_refptr<gl::GLContext> context = 173 scoped_refptr<gl::GLContext> context =
144 gl::init::CreateGLContext(nullptr, surface.get(), gl::GLContextAttribs()); 174 gl::init::CreateGLContext(nullptr, surface.get(), gl::GLContextAttribs());
(...skipping 29 matching lines...) Expand all
174 204
175 // Surface switched to use IDXGISwapChain, so must draw to entire 205 // Surface switched to use IDXGISwapChain, so must draw to entire
176 // surface. 206 // surface.
177 EXPECT_FALSE(surface->SetDrawRectangle(gfx::Rect(0, 0, 50, 50))); 207 EXPECT_FALSE(surface->SetDrawRectangle(gfx::Rect(0, 0, 50, 50)));
178 EXPECT_TRUE(surface->SetDrawRectangle(gfx::Rect(0, 0, 100, 100))); 208 EXPECT_TRUE(surface->SetDrawRectangle(gfx::Rect(0, 0, 100, 100)));
179 EXPECT_TRUE(surface->swap_chain()); 209 EXPECT_TRUE(surface->swap_chain());
180 210
181 context = nullptr; 211 context = nullptr;
182 DestroySurface(std::move(surface)); 212 DestroySurface(std::move(surface));
183 } 213 }
214
215 SkBitmap ReadBackWindow(HWND window, const gfx::Rect& snapshot_bounds) {
216 std::unique_ptr<SkCanvas> canvas = skia::CreatePlatformCanvas(
217 snapshot_bounds.right(), snapshot_bounds.bottom(), false);
218 HDC mem_hdc = skia::GetNativeDrawingContext(canvas.get());
219
220 // Grab a copy of the window. Use PrintWindow because it works even when the
221 // window's partially occluded. The PW_RENDERFULLCONTENT flag is undocumented,
222 // but works starting in Windows 8.1. It allows for capturing the contents of
223 // the window that are drawn using DirectComposition.
224 UINT flags = PW_CLIENTONLY | PW_RENDERFULLCONTENT;
225
226 BOOL result = PrintWindow(window, mem_hdc, flags);
227 if (!result)
228 PLOG(ERROR) << "Failed to print window";
229
230 SkBitmap bitmap;
231 canvas->readPixels(gfx::RectToSkIRect(snapshot_bounds), &bitmap);
232
233 return bitmap;
234 }
235
236 class DirectCompositionPixelTest : public testing::Test {
237 public:
238 DirectCompositionPixelTest()
239 : window_(&platform_delegate_, gfx::Rect(0, 0, 100, 100)) {}
240
241 protected:
242 void InitializeSurface() {
243 static_cast<ui::PlatformWindow*>(&window_)->Show();
244
245 surface_ =
246 new DirectCompositionSurfaceWin(delegate_.AsWeakPtr(), window_.hwnd());
247 EXPECT_TRUE(surface_->Initialize());
248 }
249
250 void PixelTestSwapChain(bool layers_enabled) {
251 if (!CheckIfDCSupported())
252 return;
253
254 InitializeSurface();
255
256 surface_->SetEnableDCLayers(layers_enabled);
257 gfx::Size window_size(100, 100);
258
259 scoped_refptr<gl::GLContext> context = gl::init::CreateGLContext(
260 nullptr, surface_.get(), gl::GLContextAttribs());
261 EXPECT_TRUE(surface_->Resize(window_size, 1.0, true));
262 EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(window_size)));
263 EXPECT_TRUE(context->MakeCurrent(surface_.get()));
264
265 glClearColor(1.0, 0.0, 0.0, 1.0);
266 glClear(GL_COLOR_BUFFER_BIT);
267
268 EXPECT_EQ(gfx::SwapResult::SWAP_ACK, surface_->SwapBuffers());
269
270 // Ensure DWM swap completed.
271 Sleep(1000);
272
273 SkBitmap bitmap = ReadBackWindow(window_.hwnd(), gfx::Rect(window_size));
274 EXPECT_EQ(SK_ColorRED, bitmap.getColor(50, 50));
275
276 EXPECT_TRUE(context->IsCurrent(surface_.get()));
277
278 context = nullptr;
279 DestroySurface(std::move(surface_));
280 }
281
282 TestPlatformDelegate platform_delegate_;
283 TestImageTransportSurfaceDelegate delegate_;
284 ui::WinWindow window_;
285 scoped_refptr<DirectCompositionSurfaceWin> surface_;
286 };
287
288 TEST_F(DirectCompositionPixelTest, DCLayersEnabled) {
289 PixelTestSwapChain(true);
290 }
291
292 TEST_F(DirectCompositionPixelTest, DCLayersDisabled) {
293 PixelTestSwapChain(false);
294 }
295
296 base::win::ScopedComPtr<ID3D11Texture2D> CreateNV12Texture(
297 const base::win::ScopedComPtr<ID3D11Device>& d3d11_device,
298 const gfx::Size& size) {
299 D3D11_TEXTURE2D_DESC desc = {};
300 desc.Width = size.width();
301 desc.Height = size.height();
302 desc.MipLevels = 1;
303 desc.ArraySize = 1;
304 desc.Format = DXGI_FORMAT_NV12;
305 desc.Usage = D3D11_USAGE_DEFAULT;
306 desc.SampleDesc.Count = 1;
307 desc.BindFlags = 0;
308
309 std::vector<char> image_data(size.width() * size.height() * 3 / 2);
310 // Y, U, and V should all be Oxff. Output color should be pink.
311 memset(&image_data[0], 0xff, size.width() * size.height() * 3 / 2);
312
313 D3D11_SUBRESOURCE_DATA data = {};
314 data.pSysMem = (const void*)&image_data[0];
315 data.SysMemPitch = size.width();
316
317 base::win::ScopedComPtr<ID3D11Texture2D> texture;
318 HRESULT hr = d3d11_device->CreateTexture2D(&desc, &data, texture.Receive());
319 CHECK(SUCCEEDED(hr));
320 return texture;
321 }
322
323 bool AreColorsSimilar(int a, int b) {
324 // The precise colors may differ depending on the video processor, so allow
325 // a margin for error.
326 const int kMargin = 10;
327 return abs(SkColorGetA(a) - SkColorGetA(b)) < kMargin &&
328 abs(SkColorGetR(a) - SkColorGetR(b)) < kMargin &&
329 abs(SkColorGetG(a) - SkColorGetG(b)) < kMargin &&
330 abs(SkColorGetB(a) - SkColorGetB(b)) < kMargin;
331 }
332
333 TEST_F(DirectCompositionPixelTest, VideoSwapchain) {
334 if (!CheckIfDCSupported())
335 return;
336 InitializeSurface();
337 surface_->SetEnableDCLayers(true);
338 gfx::Size window_size(100, 100);
339
340 scoped_refptr<gl::GLContext> context = gl::init::CreateGLContext(
341 nullptr, surface_.get(), gl::GLContextAttribs());
342 EXPECT_TRUE(surface_->Resize(window_size, 1.0, true));
343
344 base::win::ScopedComPtr<ID3D11Device> d3d11_device =
345 gl::QueryD3D11DeviceObjectFromANGLE();
346
347 gfx::Size texture_size(50, 50);
348 base::win::ScopedComPtr<ID3D11Texture2D> texture =
349 CreateNV12Texture(d3d11_device, texture_size);
350
351 scoped_refptr<gl::GLImageDXGI> image_dxgi(
352 new gl::GLImageDXGI(texture_size, nullptr));
353 image_dxgi->SetTexture(texture, 0);
354
355 ui::DCRendererLayerParams params(false, gfx::Rect(), 1, gfx::Transform(),
356 image_dxgi.get(),
357 gfx::RectF(gfx::Rect(texture_size)),
358 gfx::Rect(window_size), 0, 0, 1.0, 0);
359 surface_->ScheduleDCLayer(params);
360
361 EXPECT_EQ(gfx::SwapResult::SWAP_ACK, surface_->SwapBuffers());
362 Sleep(1000);
363
364 SkBitmap bitmap = ReadBackWindow(window_.hwnd(), gfx::Rect(window_size));
365 SkColor expected_color = SkColorSetRGB(0xff, 0xb7, 0xff);
366 SkColor actual_color = bitmap.getColor(75, 75);
367 EXPECT_TRUE(AreColorsSimilar(expected_color, actual_color))
368 << std::hex << "Expected " << expected_color << " Actual "
369 << actual_color;
370
371 context = nullptr;
372 DestroySurface(std::move(surface_));
373 }
374
184 } // namespace 375 } // namespace
185 } // namespace gpu 376 } // namespace gpu
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698