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