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
3 // found in the LICENSE file.
4
5 #include "ui/gl/direct_composition_surface_win.h"
6
7 #include <dxgi1_6.h>
8
9 #include <memory>
10 #include <utility>
11
12 #include "base/bind.h"
13 #include "base/metrics/histogram_functions.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/no_destructor.h"
16 #include "base/synchronization/lock.h"
17 #include "base/trace_event/trace_event.h"
18 #include "base/trace_event/traced_value.h"
19 #include "base/win/windows_version.h"
20 #include "ui/gl/dc_layer_tree.h"
21 #include "ui/gl/direct_composition_child_surface_win.h"
22 #include "ui/gl/gl_angle_util_win.h"
23 #include "ui/gl/gl_implementation.h"
24 #include "ui/gl/gl_switches.h"
25 #include "ui/gl/gpu_switching_manager.h"
26
27 #ifndef EGL_ANGLE_flexible_surface_compatibility
28 #define EGL_ANGLE_flexible_surface_compatibility 1
29 #define EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE 0x33A6
30 #endif /* EGL_ANGLE_flexible_surface_compatibility */
31
32 namespace gl {
33 namespace {
34 // Whether the overlay caps are valid or not. GUARDED_BY GetOverlayLock().
35 bool g_overlay_caps_valid = false;
36 // Indicates support for either NV12 or YUY2 overlays. GUARDED_BY
37 // GetOverlayLock().
38 bool g_supports_overlays = false;
39 // Whether the DecodeSwapChain is disabled or not.
40 bool g_decode_swap_chain_disabled = false;
41 // Whether to force the nv12 overlay support.
42 bool g_force_nv12_overlay_support = false;
43
44 // The lock to guard g_overlay_caps_valid and g_supports_overlays.
GetOverlayLock()45 base::Lock& GetOverlayLock() {
46 static base::NoDestructor<base::Lock> overlay_lock;
47 return *overlay_lock;
48 }
49
SupportsOverlays()50 bool SupportsOverlays() {
51 base::AutoLock auto_lock(GetOverlayLock());
52 return g_supports_overlays;
53 }
54
SetSupportsOverlays(bool support)55 void SetSupportsOverlays(bool support) {
56 base::AutoLock auto_lock(GetOverlayLock());
57 g_supports_overlays = support;
58 }
59
OverlayCapsValid()60 bool OverlayCapsValid() {
61 base::AutoLock auto_lock(GetOverlayLock());
62 return g_overlay_caps_valid;
63 }
SetOverlayCapsValid(bool valid)64 void SetOverlayCapsValid(bool valid) {
65 base::AutoLock auto_lock(GetOverlayLock());
66 g_overlay_caps_valid = valid;
67 }
68 // A warpper of IDXGIOutput4::CheckOverlayColorSpaceSupport()
CheckOverlayColorSpaceSupport(DXGI_FORMAT dxgi_format,DXGI_COLOR_SPACE_TYPE dxgi_color_space,Microsoft::WRL::ComPtr<IDXGIOutput> output,Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device)69 bool CheckOverlayColorSpaceSupport(
70 DXGI_FORMAT dxgi_format,
71 DXGI_COLOR_SPACE_TYPE dxgi_color_space,
72 Microsoft::WRL::ComPtr<IDXGIOutput> output,
73 Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device) {
74 UINT color_space_support_flags = 0;
75 Microsoft::WRL::ComPtr<IDXGIOutput4> output4;
76 if (FAILED(output.As(&output4)) ||
77 FAILED(output4->CheckOverlayColorSpaceSupport(
78 dxgi_format, dxgi_color_space, d3d11_device.Get(),
79 &color_space_support_flags)))
80 return false;
81 return (color_space_support_flags &
82 DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT);
83 }
84
85 // Used for adjusting overlay size to monitor size.
86 gfx::Size g_primary_monitor_size;
87
88 // The number of all visible display monitors on a desktop.
89 int g_num_of_monitors = 0;
90
91 DirectCompositionSurfaceWin::OverlayHDRInfoUpdateCallback
92 g_overlay_hdr_gpu_info_callback;
93
94 // Preferred overlay format set when detecting overlay support during
95 // initialization. Set to NV12 by default so that it's used when enabling
96 // overlays using command line flags.
97 DXGI_FORMAT g_overlay_format_used = DXGI_FORMAT_NV12;
98 DXGI_FORMAT g_overlay_format_used_hdr = DXGI_FORMAT_UNKNOWN;
99
100 // These are the raw support info, which shouldn't depend on field trial state,
101 // or command line flags. GUARDED_BY GetOverlayLock().
102 UINT g_nv12_overlay_support_flags = 0;
103 UINT g_yuy2_overlay_support_flags = 0;
104 UINT g_bgra8_overlay_support_flags = 0;
105 UINT g_rgb10a2_overlay_support_flags = 0;
106
107 // When this is set, if NV12 or YUY2 overlays are supported, set BGRA8 overlays
108 // as supported as well.
109 bool g_enable_bgra8_overlays_with_yuv_overlay_support = false;
110
SetOverlaySupportFlagsForFormats(UINT nv12_flags,UINT yuy2_flags,UINT bgra8_flags,UINT rgb10a2_flags)111 void SetOverlaySupportFlagsForFormats(UINT nv12_flags,
112 UINT yuy2_flags,
113 UINT bgra8_flags,
114 UINT rgb10a2_flags) {
115 base::AutoLock auto_lock(GetOverlayLock());
116 g_nv12_overlay_support_flags = nv12_flags;
117 g_yuy2_overlay_support_flags = yuy2_flags;
118 g_bgra8_overlay_support_flags = bgra8_flags;
119 g_rgb10a2_overlay_support_flags = rgb10a2_flags;
120 }
121
FlagsSupportsOverlays(UINT flags)122 bool FlagsSupportsOverlays(UINT flags) {
123 return (flags & (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT |
124 DXGI_OVERLAY_SUPPORT_FLAG_SCALING));
125 }
126
GetGpuDriverOverlayInfo(bool * supports_overlays,DXGI_FORMAT * overlay_format_used,DXGI_FORMAT * overlay_format_used_hdr,UINT * nv12_overlay_support_flags,UINT * yuy2_overlay_support_flags,UINT * bgra8_overlay_support_flags,UINT * rgb10a2_overlay_support_flags)127 void GetGpuDriverOverlayInfo(bool* supports_overlays,
128 DXGI_FORMAT* overlay_format_used,
129 DXGI_FORMAT* overlay_format_used_hdr,
130 UINT* nv12_overlay_support_flags,
131 UINT* yuy2_overlay_support_flags,
132 UINT* bgra8_overlay_support_flags,
133 UINT* rgb10a2_overlay_support_flags) {
134 // Initialization
135 *supports_overlays = false;
136 *overlay_format_used = DXGI_FORMAT_NV12;
137 *overlay_format_used_hdr = DXGI_FORMAT_R10G10B10A2_UNORM;
138 *nv12_overlay_support_flags = 0;
139 *yuy2_overlay_support_flags = 0;
140 *bgra8_overlay_support_flags = 0;
141 *rgb10a2_overlay_support_flags = 0;
142
143 // Check for DirectComposition support first to prevent likely crashes.
144 if (!DirectCompositionSurfaceWin::IsDirectCompositionSupported())
145 return;
146
147 // Before Windows 10 Anniversary Update (Redstone 1), overlay planes wouldn't
148 // be assigned to non-UWP apps.
149 if (base::win::GetVersion() < base::win::Version::WIN10_RS1)
150 return;
151
152 Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
153 QueryD3D11DeviceObjectFromANGLE();
154 if (!d3d11_device) {
155 DLOG(ERROR) << "Failed to retrieve D3D11 device";
156 return;
157 }
158
159 Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
160 if (FAILED(d3d11_device.As(&dxgi_device))) {
161 DLOG(ERROR) << "Failed to retrieve DXGI device";
162 return;
163 }
164
165 Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
166 if (FAILED(dxgi_device->GetAdapter(&dxgi_adapter))) {
167 DLOG(ERROR) << "Failed to retrieve DXGI adapter";
168 return;
169 }
170
171 // This will fail if the D3D device is "Microsoft Basic Display Adapter".
172 Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device;
173 if (FAILED(d3d11_device.As(&video_device))) {
174 DLOG(ERROR) << "Failed to retrieve video device";
175 return;
176 }
177
178 unsigned int i = 0;
179 while (true) {
180 Microsoft::WRL::ComPtr<IDXGIOutput> output;
181 if (FAILED(dxgi_adapter->EnumOutputs(i++, &output)))
182 break;
183 DCHECK(output);
184 Microsoft::WRL::ComPtr<IDXGIOutput3> output3;
185 if (FAILED(output.As(&output3)))
186 continue;
187 DCHECK(output3);
188 output3->CheckOverlaySupport(DXGI_FORMAT_NV12, d3d11_device.Get(),
189 nv12_overlay_support_flags);
190 output3->CheckOverlaySupport(DXGI_FORMAT_YUY2, d3d11_device.Get(),
191 yuy2_overlay_support_flags);
192 output3->CheckOverlaySupport(DXGI_FORMAT_B8G8R8A8_UNORM, d3d11_device.Get(),
193 bgra8_overlay_support_flags);
194 // Today it still returns false, which blocks Chrome from using HDR
195 // overlays.
196 output3->CheckOverlaySupport(DXGI_FORMAT_R10G10B10A2_UNORM,
197 d3d11_device.Get(),
198 rgb10a2_overlay_support_flags);
199 if (FlagsSupportsOverlays(*nv12_overlay_support_flags)) {
200 // NV12 format is preferred if it's supported.
201
202 // Per Intel's request, use NV12 only when
203 // COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 is also supported. Rec 709 is
204 // commonly used for H.264 and HEVC. At least one Intel Gen9 SKU will not
205 // support NV12 overlays.
206 if (CheckOverlayColorSpaceSupport(
207 DXGI_FORMAT_NV12, DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709,
208 output, d3d11_device)) {
209 // Some new Intel drivers only claim to support unscaled overlays, but
210 // scaled overlays still work. It's possible DWM works around it by
211 // performing an extra scaling Blt before calling the driver. Even when
212 // scaled overlays aren't actually supported, presentation using the
213 // overlay path should be relatively efficient.
214 *overlay_format_used = DXGI_FORMAT_NV12;
215 *supports_overlays = true;
216 }
217 }
218 if (!*supports_overlays &&
219 FlagsSupportsOverlays(*yuy2_overlay_support_flags)) {
220 // If NV12 isn't supported, fallback to YUY2 if it's supported.
221 *overlay_format_used = DXGI_FORMAT_YUY2;
222 *supports_overlays = true;
223 }
224 if (g_enable_bgra8_overlays_with_yuv_overlay_support) {
225 if (FlagsSupportsOverlays(*nv12_overlay_support_flags))
226 *bgra8_overlay_support_flags = *nv12_overlay_support_flags;
227 else if (FlagsSupportsOverlays(*yuy2_overlay_support_flags))
228 *bgra8_overlay_support_flags = *yuy2_overlay_support_flags;
229 }
230
231 // RGB10A2 overlay is used for displaying HDR content. In Intel's
232 // platform, RGB10A2 overlay is enabled only when
233 // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 is supported.
234 if (FlagsSupportsOverlays(*rgb10a2_overlay_support_flags)) {
235 if (!CheckOverlayColorSpaceSupport(
236 DXGI_FORMAT_R10G10B10A2_UNORM,
237 DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, output, d3d11_device))
238 *rgb10a2_overlay_support_flags = 0;
239 }
240
241 // Early out after the first output that reports overlay support. All
242 // outputs are expected to report the same overlay support according to
243 // Microsoft's WDDM documentation:
244 // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/multiplane-overlay-hardware-requirements
245 // TODO(sunnyps): If the above is true, then we can only look at first
246 // output instead of iterating over all outputs.
247 if (*supports_overlays)
248 break;
249 }
250
251 base::UmaHistogramBoolean("GPU.DirectComposition.HardwareOverlaysSupported",
252 *supports_overlays);
253
254 if (*supports_overlays || !base::FeatureList::IsEnabled(
255 features::kDirectCompositionSoftwareOverlays)) {
256 return;
257 }
258
259 // If no devices with hardware overlay support were found use software ones.
260 *supports_overlays = true;
261 *nv12_overlay_support_flags = 0;
262 *yuy2_overlay_support_flags = 0;
263 *bgra8_overlay_support_flags = 0;
264 *rgb10a2_overlay_support_flags = 0;
265
266 // Software overlays always use NV12 because it's slightly more efficient and
267 // YUY2 was only used because Skylake doesn't support NV12 hardware overlays.
268 *overlay_format_used = DXGI_FORMAT_NV12;
269 }
270
UpdateOverlaySupport()271 void UpdateOverlaySupport() {
272 if (OverlayCapsValid())
273 return;
274 SetOverlayCapsValid(true);
275
276 bool supports_overlays = false;
277 DXGI_FORMAT overlay_format_used = DXGI_FORMAT_NV12;
278 DXGI_FORMAT overlay_format_used_hdr = DXGI_FORMAT_R10G10B10A2_UNORM;
279 UINT nv12_overlay_support_flags = 0;
280 UINT yuy2_overlay_support_flags = 0;
281 UINT bgra8_overlay_support_flags = 0;
282 UINT rgb10a2_overlay_support_flags = 0;
283
284 GetGpuDriverOverlayInfo(
285 &supports_overlays, &overlay_format_used, &overlay_format_used_hdr,
286 &nv12_overlay_support_flags, &yuy2_overlay_support_flags,
287 &bgra8_overlay_support_flags, &rgb10a2_overlay_support_flags);
288
289 if (g_force_nv12_overlay_support) {
290 supports_overlays = true;
291 nv12_overlay_support_flags = DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
292 overlay_format_used = DXGI_FORMAT_NV12;
293 }
294
295 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
296 switches::kDirectCompositionVideoSwapChainFormat)) {
297 std::string override_format =
298 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
299 switches::kDirectCompositionVideoSwapChainFormat);
300 if (override_format == kSwapChainFormatNV12) {
301 overlay_format_used = DXGI_FORMAT_NV12;
302 } else if (override_format == kSwapChainFormatYUY2) {
303 overlay_format_used = DXGI_FORMAT_YUY2;
304 } else if (override_format == kSwapChainFormatBGRA) {
305 overlay_format_used = DXGI_FORMAT_B8G8R8A8_UNORM;
306 } else {
307 DLOG(ERROR) << "Invalid value for switch "
308 << switches::kDirectCompositionVideoSwapChainFormat;
309 }
310 }
311
312 if (supports_overlays != SupportsOverlays() ||
313 overlay_format_used != g_overlay_format_used) {
314 // Record the new histograms
315 if (supports_overlays) {
316 base::UmaHistogramSparse("GPU.DirectComposition.OverlayFormatUsed3",
317 overlay_format_used);
318 }
319 UMA_HISTOGRAM_BOOLEAN("GPU.DirectComposition.OverlaysSupported",
320 supports_overlays);
321 }
322
323 // Update global caps
324 SetSupportsOverlays(supports_overlays);
325 SetOverlaySupportFlagsForFormats(
326 nv12_overlay_support_flags, yuy2_overlay_support_flags,
327 bgra8_overlay_support_flags, rgb10a2_overlay_support_flags);
328 g_overlay_format_used = overlay_format_used;
329 g_overlay_format_used_hdr = overlay_format_used_hdr;
330 }
331
RunOverlayHdrGpuInfoUpdateCallback()332 void RunOverlayHdrGpuInfoUpdateCallback() {
333 if (g_overlay_hdr_gpu_info_callback)
334 g_overlay_hdr_gpu_info_callback.Run();
335 }
336
UpdateMonitorInfo()337 void UpdateMonitorInfo() {
338 g_num_of_monitors = GetSystemMetrics(SM_CMONITORS);
339
340 MONITORINFO monitor_info;
341 monitor_info.cbSize = sizeof(monitor_info);
342 if (GetMonitorInfo(MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY),
343 &monitor_info)) {
344 g_primary_monitor_size = gfx::Rect(monitor_info.rcMonitor).size();
345 } else {
346 g_primary_monitor_size = gfx::Size();
347 }
348 }
349 } // namespace
350
DirectCompositionSurfaceWin(HWND parent_window,VSyncCallback vsync_callback,const Settings & settings)351 DirectCompositionSurfaceWin::DirectCompositionSurfaceWin(
352 HWND parent_window,
353 VSyncCallback vsync_callback,
354 const Settings& settings)
355 : GLSurfaceEGL(),
356 child_window_(parent_window),
357 root_surface_(new DirectCompositionChildSurfaceWin(
358 std::move(vsync_callback),
359 settings.use_angle_texture_offset,
360 settings.max_pending_frames,
361 settings.force_root_surface_full_damage)),
362 layer_tree_(std::make_unique<DCLayerTree>(
363 settings.disable_nv12_dynamic_textures,
364 settings.disable_vp_scaling,
365 settings.reset_vp_when_colorspace_changes)) {
366 ui::GpuSwitchingManager::GetInstance()->AddObserver(this);
367 }
368
~DirectCompositionSurfaceWin()369 DirectCompositionSurfaceWin::~DirectCompositionSurfaceWin() {
370 ui::GpuSwitchingManager::GetInstance()->RemoveObserver(this);
371 Destroy();
372 }
373
374 // static
IsDirectCompositionSupported()375 bool DirectCompositionSurfaceWin::IsDirectCompositionSupported() {
376 static const bool supported = [] {
377 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
378 if (command_line->HasSwitch(switches::kDisableDirectComposition))
379 return false;
380
381 // Direct composition can only be used with ANGLE.
382 if (gl::GetGLImplementation() != gl::kGLImplementationEGLANGLE)
383 return false;
384
385 // Blocklist direct composition if MCTU.dll or MCTUX.dll are injected. These
386 // are user mode drivers for display adapters from Magic Control Technology
387 // Corporation.
388 if (GetModuleHandle(TEXT("MCTU.dll")) ||
389 GetModuleHandle(TEXT("MCTUX.dll"))) {
390 DLOG(ERROR) << "Blocklisted due to third party modules";
391 return false;
392 }
393
394 // Flexible surface compatibility is required to be able to MakeCurrent with
395 // the default pbuffer surface.
396 if (!GLSurfaceEGL::IsEGLFlexibleSurfaceCompatibilitySupported()) {
397 DLOG(ERROR) << "EGL_ANGLE_flexible_surface_compatibility not supported";
398 return false;
399 }
400
401 Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
402 QueryD3D11DeviceObjectFromANGLE();
403 if (!d3d11_device) {
404 DLOG(ERROR) << "Failed to retrieve D3D11 device";
405 return false;
406 }
407
408 // This will fail if the D3D device is "Microsoft Basic Display Adapter".
409 Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device;
410 if (FAILED(d3d11_device.As(&video_device))) {
411 DLOG(ERROR) << "Failed to retrieve video device";
412 return false;
413 }
414
415 // This will fail if DirectComposition DLL can't be loaded.
416 Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device =
417 QueryDirectCompositionDevice(d3d11_device);
418 if (!dcomp_device) {
419 DLOG(ERROR) << "Failed to retrieve direct composition device";
420 return false;
421 }
422
423 return true;
424 }();
425 return supported && !DirectCompositionChildSurfaceWin::
426 IsDirectCompositionSwapChainFailed();
427 }
428
429 // static
AreOverlaysSupported()430 bool DirectCompositionSurfaceWin::AreOverlaysSupported() {
431 // Always initialize and record overlay support information irrespective of
432 // command line flags.
433 UpdateOverlaySupport();
434
435 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
436 // Enable flag should be checked before the disable flag, so we could
437 // overwrite GPU driver bug workarounds in testing.
438 if (command_line->HasSwitch(switches::kEnableDirectCompositionVideoOverlays))
439 return true;
440 if (command_line->HasSwitch(switches::kDisableDirectCompositionVideoOverlays))
441 return false;
442
443 return SupportsOverlays();
444 }
445
446 // static
IsDecodeSwapChainSupported()447 bool DirectCompositionSurfaceWin::IsDecodeSwapChainSupported() {
448 if (!g_decode_swap_chain_disabled) {
449 UpdateOverlaySupport();
450 return GetOverlayFormatUsedForSDR() == DXGI_FORMAT_NV12;
451 }
452 return false;
453 }
454
455 // static
DisableDecodeSwapChain()456 void DirectCompositionSurfaceWin::DisableDecodeSwapChain() {
457 g_decode_swap_chain_disabled = true;
458 }
459
460 // static
DisableOverlays()461 void DirectCompositionSurfaceWin::DisableOverlays() {
462 SetSupportsOverlays(false);
463 RunOverlayHdrGpuInfoUpdateCallback();
464 }
465
466 // static
InvalidateOverlayCaps()467 void DirectCompositionSurfaceWin::InvalidateOverlayCaps() {
468 SetOverlayCapsValid(false);
469 }
470
471 // static
AreScaledOverlaysSupported()472 bool DirectCompositionSurfaceWin::AreScaledOverlaysSupported() {
473 UpdateOverlaySupport();
474 if (g_overlay_format_used == DXGI_FORMAT_NV12) {
475 return (g_nv12_overlay_support_flags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING) ||
476 (SupportsOverlays() &&
477 base::FeatureList::IsEnabled(
478 features::kDirectCompositionSoftwareOverlays));
479 } else if (g_overlay_format_used == DXGI_FORMAT_YUY2) {
480 return !!(g_yuy2_overlay_support_flags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING);
481 } else {
482 DCHECK_EQ(g_overlay_format_used, DXGI_FORMAT_B8G8R8A8_UNORM);
483 // Assume scaling is supported for BGRA overlays.
484 return true;
485 }
486 }
487
488 // static
GetOverlaySupportFlags(DXGI_FORMAT format)489 UINT DirectCompositionSurfaceWin::GetOverlaySupportFlags(DXGI_FORMAT format) {
490 UpdateOverlaySupport();
491 base::AutoLock auto_lock(GetOverlayLock());
492 UINT support_flag = 0;
493 switch (format) {
494 case DXGI_FORMAT_NV12:
495 support_flag = g_nv12_overlay_support_flags;
496 break;
497 case DXGI_FORMAT_YUY2:
498 support_flag = g_yuy2_overlay_support_flags;
499 break;
500 case DXGI_FORMAT_B8G8R8A8_UNORM:
501 support_flag = g_bgra8_overlay_support_flags;
502 break;
503 case DXGI_FORMAT_R10G10B10A2_UNORM:
504 support_flag = g_rgb10a2_overlay_support_flags;
505 break;
506 default:
507 NOTREACHED();
508 break;
509 }
510 return support_flag;
511 }
512
513 // static
GetPrimaryMonitorSize()514 gfx::Size DirectCompositionSurfaceWin::GetPrimaryMonitorSize() {
515 return g_primary_monitor_size;
516 }
517
518 // static
GetNumOfMonitors()519 int DirectCompositionSurfaceWin::GetNumOfMonitors() {
520 return g_num_of_monitors;
521 }
522
523 // static
GetOverlayFormatUsedForSDR()524 DXGI_FORMAT DirectCompositionSurfaceWin::GetOverlayFormatUsedForSDR() {
525 return g_overlay_format_used;
526 }
527
528 // static
SetScaledOverlaysSupportedForTesting(bool supported)529 void DirectCompositionSurfaceWin::SetScaledOverlaysSupportedForTesting(
530 bool supported) {
531 UpdateOverlaySupport();
532 if (supported) {
533 g_nv12_overlay_support_flags |= DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
534 g_yuy2_overlay_support_flags |= DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
535 g_rgb10a2_overlay_support_flags |= DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
536 } else {
537 g_nv12_overlay_support_flags &= ~DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
538 g_yuy2_overlay_support_flags &= ~DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
539 g_rgb10a2_overlay_support_flags &= ~DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
540 }
541 DCHECK_EQ(supported, AreScaledOverlaysSupported());
542 }
543
544 // static
SetOverlayFormatUsedForTesting(DXGI_FORMAT format)545 void DirectCompositionSurfaceWin::SetOverlayFormatUsedForTesting(
546 DXGI_FORMAT format) {
547 DCHECK(format == DXGI_FORMAT_NV12 || format == DXGI_FORMAT_YUY2 ||
548 format == DXGI_FORMAT_B8G8R8A8_UNORM);
549 UpdateOverlaySupport();
550 g_overlay_format_used = format;
551 DCHECK_EQ(format, GetOverlayFormatUsedForSDR());
552 }
553
554 // static
IsHDRSupported()555 bool DirectCompositionSurfaceWin::IsHDRSupported() {
556 // HDR support was introduced in Windows 10 Creators Update.
557 if (base::win::GetVersion() < base::win::Version::WIN10_RS2)
558 return false;
559
560 // Only direct composition surface can allocate HDR swap chains.
561 if (!IsDirectCompositionSupported())
562 return false;
563
564 HRESULT hr = S_OK;
565 Microsoft::WRL::ComPtr<IDXGIFactory1> factory;
566 hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));
567 if (FAILED(hr)) {
568 DLOG(ERROR) << "Failed to create DXGI factory.";
569 return false;
570 }
571
572 bool hdr_monitor_found = false;
573 for (UINT adapter_index = 0;; ++adapter_index) {
574 Microsoft::WRL::ComPtr<IDXGIAdapter> adapter;
575 hr = factory->EnumAdapters(adapter_index, &adapter);
576 if (hr == DXGI_ERROR_NOT_FOUND)
577 break;
578 if (FAILED(hr)) {
579 DLOG(ERROR) << "Unexpected error creating DXGI adapter.";
580 break;
581 }
582
583 for (UINT output_index = 0;; ++output_index) {
584 Microsoft::WRL::ComPtr<IDXGIOutput> output;
585 hr = adapter->EnumOutputs(output_index, &output);
586 if (hr == DXGI_ERROR_NOT_FOUND)
587 break;
588 if (FAILED(hr)) {
589 DLOG(ERROR) << "Unexpected error creating DXGI adapter.";
590 break;
591 }
592
593 Microsoft::WRL::ComPtr<IDXGIOutput6> output6;
594 hr = output->QueryInterface(IID_PPV_ARGS(&output6));
595 if (FAILED(hr)) {
596 DLOG(WARNING) << "IDXGIOutput6 is required for HDR detection.";
597 continue;
598 }
599
600 DXGI_OUTPUT_DESC1 desc;
601 if (FAILED(output6->GetDesc1(&desc))) {
602 DLOG(ERROR) << "Unexpected error getting output descriptor.";
603 continue;
604 }
605
606 if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
607 hdr_monitor_found = true;
608 }
609 }
610 }
611
612 UMA_HISTOGRAM_BOOLEAN("GPU.Output.HDR", hdr_monitor_found);
613 return hdr_monitor_found;
614 }
615
616 // static
IsSwapChainTearingSupported()617 bool DirectCompositionSurfaceWin::IsSwapChainTearingSupported() {
618 static const bool supported = [] {
619 Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
620 QueryD3D11DeviceObjectFromANGLE();
621 if (!d3d11_device) {
622 DLOG(ERROR) << "Not using swap chain tearing because failed to retrieve "
623 "D3D11 device from ANGLE";
624 return false;
625 }
626 Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
627 d3d11_device.As(&dxgi_device);
628 DCHECK(dxgi_device);
629 Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
630 dxgi_device->GetAdapter(&dxgi_adapter);
631 DCHECK(dxgi_adapter);
632 Microsoft::WRL::ComPtr<IDXGIFactory5> dxgi_factory;
633 if (FAILED(dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory)))) {
634 DLOG(ERROR) << "Not using swap chain tearing because failed to retrieve "
635 "IDXGIFactory5 interface";
636 return false;
637 }
638
639 BOOL present_allow_tearing = FALSE;
640 DCHECK(dxgi_factory);
641 if (FAILED(dxgi_factory->CheckFeatureSupport(
642 DXGI_FEATURE_PRESENT_ALLOW_TEARING, &present_allow_tearing,
643 sizeof(present_allow_tearing)))) {
644 DLOG(ERROR)
645 << "Not using swap chain tearing because CheckFeatureSupport failed";
646 return false;
647 }
648 return !!present_allow_tearing;
649 }();
650 return supported;
651 }
652
653 // static
AllowTearing()654 bool DirectCompositionSurfaceWin::AllowTearing() {
655 // Swap chain tearing is used only if vsync is disabled explicitly.
656 return base::CommandLine::ForCurrentProcess()->HasSwitch(
657 switches::kDisableGpuVsync) &&
658 DirectCompositionSurfaceWin::IsSwapChainTearingSupported();
659 }
660
661 // static
SetOverlayHDRGpuInfoUpdateCallback(OverlayHDRInfoUpdateCallback callback)662 void DirectCompositionSurfaceWin::SetOverlayHDRGpuInfoUpdateCallback(
663 OverlayHDRInfoUpdateCallback callback) {
664 g_overlay_hdr_gpu_info_callback = std::move(callback);
665 }
666
667 // static
EnableBGRA8OverlaysWithYUVOverlaySupport()668 void DirectCompositionSurfaceWin::EnableBGRA8OverlaysWithYUVOverlaySupport() {
669 // This has to be set before initializing overlay caps.
670 DCHECK(!OverlayCapsValid());
671 g_enable_bgra8_overlays_with_yuv_overlay_support = true;
672 }
673
674 // static
ForceNV12OverlaySupport()675 void DirectCompositionSurfaceWin::ForceNV12OverlaySupport() {
676 // This has to be set before initializing overlay caps.
677 DCHECK(!OverlayCapsValid());
678 g_force_nv12_overlay_support = true;
679 }
680
Initialize(GLSurfaceFormat format)681 bool DirectCompositionSurfaceWin::Initialize(GLSurfaceFormat format) {
682 d3d11_device_ = QueryD3D11DeviceObjectFromANGLE();
683 if (!d3d11_device_) {
684 DLOG(ERROR) << "Failed to retrieve D3D11 device from ANGLE";
685 return false;
686 }
687
688 dcomp_device_ = QueryDirectCompositionDevice(d3d11_device_);
689 if (!dcomp_device_) {
690 DLOG(ERROR)
691 << "Failed to retrieve direct compostion device from D3D11 device";
692 return false;
693 }
694
695 child_window_.Initialize();
696
697 window_ = child_window_.window();
698
699 if (!layer_tree_->Initialize(window_, d3d11_device_, dcomp_device_))
700 return false;
701
702 if (!root_surface_->Initialize(GLSurfaceFormat()))
703 return false;
704
705 UpdateMonitorInfo();
706 return true;
707 }
708
Destroy()709 void DirectCompositionSurfaceWin::Destroy() {
710 root_surface_->Destroy();
711 // Freeing DComp resources such as visuals and surfaces causes the
712 // device to become 'dirty'. We must commit the changes to the device
713 // in order for the objects to actually be destroyed.
714 // Leaving the device in the dirty state for long periods of time means
715 // that if DWM.exe crashes, the Chromium window will become black until
716 // the next Commit.
717 layer_tree_.reset();
718 if (dcomp_device_)
719 dcomp_device_->Commit();
720 }
721
GetSize()722 gfx::Size DirectCompositionSurfaceWin::GetSize() {
723 return root_surface_->GetSize();
724 }
725
IsOffscreen()726 bool DirectCompositionSurfaceWin::IsOffscreen() {
727 return false;
728 }
729
GetHandle()730 void* DirectCompositionSurfaceWin::GetHandle() {
731 return root_surface_->GetHandle();
732 }
733
Resize(const gfx::Size & size,float scale_factor,const gfx::ColorSpace & color_space,bool has_alpha)734 bool DirectCompositionSurfaceWin::Resize(const gfx::Size& size,
735 float scale_factor,
736 const gfx::ColorSpace& color_space,
737 bool has_alpha) {
738 // Force a resize and redraw (but not a move, activate, etc.).
739 if (!SetWindowPos(window_, nullptr, 0, 0, size.width(), size.height(),
740 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS |
741 SWP_NOOWNERZORDER | SWP_NOZORDER)) {
742 return false;
743 }
744 return root_surface_->Resize(size, scale_factor, color_space, has_alpha);
745 }
746
SwapBuffers(PresentationCallback callback)747 gfx::SwapResult DirectCompositionSurfaceWin::SwapBuffers(
748 PresentationCallback callback) {
749 TRACE_EVENT0("gpu", "DirectCompositionSurfaceWin::SwapBuffers");
750
751 if (root_surface_->SwapBuffers(std::move(callback)) !=
752 gfx::SwapResult::SWAP_ACK)
753 return gfx::SwapResult::SWAP_FAILED;
754
755 if (!layer_tree_->CommitAndClearPendingOverlays(root_surface_.get()))
756 return gfx::SwapResult::SWAP_FAILED;
757
758 return gfx::SwapResult::SWAP_ACK;
759 }
760
PostSubBuffer(int x,int y,int width,int height,PresentationCallback callback)761 gfx::SwapResult DirectCompositionSurfaceWin::PostSubBuffer(
762 int x,
763 int y,
764 int width,
765 int height,
766 PresentationCallback callback) {
767 // The arguments are ignored because SetDrawRectangle specified the area to
768 // be swapped.
769 return SwapBuffers(std::move(callback));
770 }
771
GetVSyncProvider()772 gfx::VSyncProvider* DirectCompositionSurfaceWin::GetVSyncProvider() {
773 return root_surface_->GetVSyncProvider();
774 }
775
SetVSyncEnabled(bool enabled)776 void DirectCompositionSurfaceWin::SetVSyncEnabled(bool enabled) {
777 root_surface_->SetVSyncEnabled(enabled);
778 }
779
ScheduleDCLayer(const ui::DCRendererLayerParams & params)780 bool DirectCompositionSurfaceWin::ScheduleDCLayer(
781 const ui::DCRendererLayerParams& params) {
782 return layer_tree_->ScheduleDCLayer(params);
783 }
784
SetFrameRate(float frame_rate)785 void DirectCompositionSurfaceWin::SetFrameRate(float frame_rate) {
786 // Only try to reduce vsync frequency through the video swap chain.
787 // This allows us to experiment UseSetPresentDuration optimization to
788 // fullscreen video overlays only and avoid compromising
789 // UsePreferredIntervalForVideo optimization where we skip compositing
790 // every other frame when fps <= half the vsync frame rate.
791 layer_tree_->SetFrameRate(frame_rate);
792 }
793
SetEnableDCLayers(bool enable)794 bool DirectCompositionSurfaceWin::SetEnableDCLayers(bool enable) {
795 return root_surface_->SetEnableDCLayers(enable);
796 }
797
GetOrigin() const798 gfx::SurfaceOrigin DirectCompositionSurfaceWin::GetOrigin() const {
799 return gfx::SurfaceOrigin::kTopLeft;
800 }
801
SupportsPostSubBuffer()802 bool DirectCompositionSurfaceWin::SupportsPostSubBuffer() {
803 return true;
804 }
805
OnMakeCurrent(GLContext * context)806 bool DirectCompositionSurfaceWin::OnMakeCurrent(GLContext* context) {
807 return root_surface_->OnMakeCurrent(context);
808 }
809
SupportsDCLayers() const810 bool DirectCompositionSurfaceWin::SupportsDCLayers() const {
811 return true;
812 }
813
SupportsProtectedVideo() const814 bool DirectCompositionSurfaceWin::SupportsProtectedVideo() const {
815 // TODO(magchen): Check the gpu driver date (or a function) which we know this
816 // new support is enabled.
817 return AreOverlaysSupported();
818 }
819
SetDrawRectangle(const gfx::Rect & rectangle)820 bool DirectCompositionSurfaceWin::SetDrawRectangle(const gfx::Rect& rectangle) {
821 bool result = root_surface_->SetDrawRectangle(rectangle);
822 if (!result &&
823 DirectCompositionChildSurfaceWin::IsDirectCompositionSwapChainFailed()) {
824 RunOverlayHdrGpuInfoUpdateCallback();
825 }
826
827 return result;
828 }
829
GetDrawOffset() const830 gfx::Vector2d DirectCompositionSurfaceWin::GetDrawOffset() const {
831 return root_surface_->GetDrawOffset();
832 }
833
SupportsGpuVSync() const834 bool DirectCompositionSurfaceWin::SupportsGpuVSync() const {
835 return true;
836 }
837
SetGpuVSyncEnabled(bool enabled)838 void DirectCompositionSurfaceWin::SetGpuVSyncEnabled(bool enabled) {
839 root_surface_->SetGpuVSyncEnabled(enabled);
840 }
841
OnGpuSwitched(gl::GpuPreference active_gpu_heuristic)842 void DirectCompositionSurfaceWin::OnGpuSwitched(
843 gl::GpuPreference active_gpu_heuristic) {}
844
OnDisplayAdded()845 void DirectCompositionSurfaceWin::OnDisplayAdded() {
846 InvalidateOverlayCaps();
847 UpdateOverlaySupport();
848 UpdateMonitorInfo();
849 RunOverlayHdrGpuInfoUpdateCallback();
850 }
851
OnDisplayRemoved()852 void DirectCompositionSurfaceWin::OnDisplayRemoved() {
853 InvalidateOverlayCaps();
854 UpdateOverlaySupport();
855 UpdateMonitorInfo();
856 RunOverlayHdrGpuInfoUpdateCallback();
857 }
858
OnDisplayMetricsChanged()859 void DirectCompositionSurfaceWin::OnDisplayMetricsChanged() {
860 UpdateMonitorInfo();
861 }
862
863 scoped_refptr<base::TaskRunner>
GetWindowTaskRunnerForTesting()864 DirectCompositionSurfaceWin::GetWindowTaskRunnerForTesting() {
865 return child_window_.GetTaskRunnerForTesting();
866 }
867
868 Microsoft::WRL::ComPtr<IDXGISwapChain1>
GetLayerSwapChainForTesting(size_t index) const869 DirectCompositionSurfaceWin::GetLayerSwapChainForTesting(size_t index) const {
870 return layer_tree_->GetLayerSwapChainForTesting(index);
871 }
872
873 Microsoft::WRL::ComPtr<IDXGISwapChain1>
GetBackbufferSwapChainForTesting() const874 DirectCompositionSurfaceWin::GetBackbufferSwapChainForTesting() const {
875 return root_surface_->swap_chain();
876 }
877
878 scoped_refptr<DirectCompositionChildSurfaceWin>
GetRootSurfaceForTesting() const879 DirectCompositionSurfaceWin::GetRootSurfaceForTesting() const {
880 return root_surface_;
881 }
882
GetSwapChainVisualInfoForTesting(size_t index,gfx::Transform * transform,gfx::Point * offset,gfx::Rect * clip_rect) const883 void DirectCompositionSurfaceWin::GetSwapChainVisualInfoForTesting(
884 size_t index,
885 gfx::Transform* transform,
886 gfx::Point* offset,
887 gfx::Rect* clip_rect) const {
888 layer_tree_->GetSwapChainVisualInfoForTesting( // IN-TEST
889 index, transform, offset, clip_rect);
890 }
891
SetMonitorInfoForTesting(int num_of_monitors,gfx::Size monitor_size)892 void DirectCompositionSurfaceWin::SetMonitorInfoForTesting(
893 int num_of_monitors,
894 gfx::Size monitor_size) {
895 g_num_of_monitors = num_of_monitors;
896 g_primary_monitor_size = monitor_size;
897 }
898
899 } // namespace gl
900