vsync_thread_win.cc 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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. #include "ui/gl/vsync_thread_win.h"
  5. #include "base/bind.h"
  6. #include "base/logging.h"
  7. #include "base/memory/singleton.h"
  8. #include "base/power_monitor/power_monitor.h"
  9. #include "ui/gl/gl_angle_util_win.h"
  10. #include "ui/gl/vsync_observer.h"
  11. namespace gl {
  12. namespace {
  13. Microsoft::WRL::ComPtr<IDXGIOutput> DXGIOutputFromMonitor(
  14. HMONITOR monitor,
  15. const Microsoft::WRL::ComPtr<ID3D11Device>& d3d11_device) {
  16. Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
  17. if (FAILED(d3d11_device.As(&dxgi_device))) {
  18. DLOG(ERROR) << "Failed to retrieve DXGI device";
  19. return nullptr;
  20. }
  21. Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
  22. if (FAILED(dxgi_device->GetAdapter(&dxgi_adapter))) {
  23. DLOG(ERROR) << "Failed to retrieve DXGI adapter";
  24. return nullptr;
  25. }
  26. size_t i = 0;
  27. while (true) {
  28. Microsoft::WRL::ComPtr<IDXGIOutput> output;
  29. if (FAILED(dxgi_adapter->EnumOutputs(i++, &output)))
  30. break;
  31. DXGI_OUTPUT_DESC desc = {};
  32. if (FAILED(output->GetDesc(&desc))) {
  33. DLOG(ERROR) << "DXGI output GetDesc failed";
  34. return nullptr;
  35. }
  36. if (desc.Monitor == monitor)
  37. return output;
  38. }
  39. return nullptr;
  40. }
  41. } // namespace
  42. // static
  43. VSyncThreadWin* VSyncThreadWin::GetInstance() {
  44. return base::Singleton<VSyncThreadWin>::get();
  45. }
  46. VSyncThreadWin::VSyncThreadWin()
  47. : vsync_thread_("GpuVSyncThread"),
  48. vsync_provider_(gfx::kNullAcceleratedWidget),
  49. d3d11_device_(QueryD3D11DeviceObjectFromANGLE()) {
  50. DCHECK(d3d11_device_);
  51. is_suspended_ =
  52. base::PowerMonitor::AddPowerSuspendObserverAndReturnSuspendedState(this);
  53. vsync_thread_.StartWithOptions(
  54. base::Thread::Options(base::ThreadType::kDisplayCritical));
  55. }
  56. VSyncThreadWin::~VSyncThreadWin() {
  57. {
  58. base::AutoLock auto_lock(lock_);
  59. observers_.clear();
  60. }
  61. vsync_thread_.Stop();
  62. base::PowerMonitor::RemovePowerSuspendObserver(this);
  63. }
  64. void VSyncThreadWin::PostTaskIfNeeded() {
  65. lock_.AssertAcquired();
  66. // PostTaskIfNeeded is called from AddObserver and OnResume.
  67. // Before queuing up a task, make sure that there are observers waiting for
  68. // VSync and that we're not already firing events to consumers. Avoid firing
  69. // events if we're suspended to conserve battery life.
  70. if (!is_vsync_task_posted_ && !observers_.empty() && !is_suspended_) {
  71. vsync_thread_.task_runner()->PostTask(
  72. FROM_HERE,
  73. base::BindOnce(&VSyncThreadWin::WaitForVSync, base::Unretained(this)));
  74. is_vsync_task_posted_ = true;
  75. }
  76. }
  77. void VSyncThreadWin::AddObserver(VSyncObserver* obs) {
  78. base::AutoLock auto_lock(lock_);
  79. observers_.insert(obs);
  80. PostTaskIfNeeded();
  81. }
  82. void VSyncThreadWin::RemoveObserver(VSyncObserver* obs) {
  83. base::AutoLock auto_lock(lock_);
  84. observers_.erase(obs);
  85. }
  86. void VSyncThreadWin::OnSuspend() {
  87. base::AutoLock auto_lock(lock_);
  88. is_suspended_ = true;
  89. }
  90. void VSyncThreadWin::OnResume() {
  91. base::AutoLock auto_lock(lock_);
  92. is_suspended_ = false;
  93. PostTaskIfNeeded();
  94. }
  95. void VSyncThreadWin::WaitForVSync() {
  96. base::TimeTicks vsync_phase;
  97. base::TimeDelta vsync_interval;
  98. const bool get_vsync_params_succeeded =
  99. vsync_provider_.GetVSyncParametersIfAvailable(&vsync_phase,
  100. &vsync_interval);
  101. DCHECK(get_vsync_params_succeeded);
  102. // From Raymond Chen's blog "How do I get a handle to the primary monitor?"
  103. // https://devblogs.microsoft.com/oldnewthing/20141106-00/?p=43683
  104. const HMONITOR monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
  105. if (primary_monitor_ != monitor) {
  106. primary_monitor_ = monitor;
  107. primary_output_ = DXGIOutputFromMonitor(monitor, d3d11_device_);
  108. }
  109. const base::TimeTicks wait_for_vblank_start_time = base::TimeTicks::Now();
  110. const bool wait_for_vblank_succeeded =
  111. primary_output_ && SUCCEEDED(primary_output_->WaitForVBlank());
  112. // WaitForVBlank returns very early instead of waiting until vblank when the
  113. // monitor goes to sleep. We use 1ms as a threshold for the duration of
  114. // WaitForVBlank and fallback to Sleep() if it returns before that. This
  115. // could happen during normal operation for the first call after the vsync
  116. // thread becomes non-idle, but it shouldn't happen often.
  117. constexpr auto kVBlankIntervalThreshold = base::Milliseconds(1);
  118. const base::TimeDelta wait_for_vblank_elapsed_time =
  119. base::TimeTicks::Now() - wait_for_vblank_start_time;
  120. if (!wait_for_vblank_succeeded ||
  121. wait_for_vblank_elapsed_time < kVBlankIntervalThreshold) {
  122. Sleep(static_cast<DWORD>(vsync_interval.InMillisecondsRoundedUp()));
  123. }
  124. base::AutoLock auto_lock(lock_);
  125. DCHECK(is_vsync_task_posted_);
  126. is_vsync_task_posted_ = false;
  127. PostTaskIfNeeded();
  128. const base::TimeTicks vsync_time = base::TimeTicks::Now();
  129. for (auto* obs : observers_)
  130. obs->OnVSync(vsync_time, vsync_interval);
  131. }
  132. } // namespace gl