Commit 432ffabd authored by Sunny Sachanandani's avatar Sunny Sachanandani Committed by Commit Bot

Reland "gpu: Sync to primary monitor's vblank (windows)"

This reverts commit d332af51.

Fixed crash due to missing member var initialization.

TEST=ran failing gl_unittests locally

Original change's description:
> Revert "gpu: Sync to primary monitor's vblank (windows)"
>
> This reverts commit bd0cae50.
>
> Reason for revert: Cause failures in DirectCompositionSurfaceTest.*
> Builder: https://ci.chromium.org/p/chromium/builders/ci/Win10%20Debug%20%28NVIDIA%29?limit=200
> First build: https://ci.chromium.org/p/chromium/builders/ci/Win10%20Debug%20%28NVIDIA%29/22377?blamelist=1#overview-tab
>
> Crash example:
> ...snip...
>
> Original change's description:
> > gpu: Sync to primary monitor's vblank (windows)
> >
> > DWM composition is limited to primary monitor's refresh rate.  Prior to
> > GPU vsync we would use DWM composition rate to drive begin frames.
> >
> > With GPU vsync we started using per monitor vblank to limit begin frames
> > in case primary monitor had higher refresh rate, but this might be
> > causing draw duration and omnibar latency regressions.  MS platform team
> > also suggested syncing to primary monitor only.  In any case the correct
> > approach would be to further limit begin frames based on event queries
> > from 2 frames back.
> >
> > This CL makes the GPU vsync begin frames sync to primary monitor, and
> > also makes the vsync thread a singleton since it doesn't have per-window
> > state any more.
> >
> > Bug: 953970
> > Change-Id: Id3d3f043cb847172b1d6ba4bd38b087ceb2d8631
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1711215
> > Reviewed-by: Zhenyao Mo <zmo@chromium.org>
> > Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#679832}
>
> TBR=zmo@chromium.org,sunnyps@chromium.org
>
> Change-Id: If4a762325952dd33283bf41a13321e759de51025
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Bug: 953970
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1713510
> Reviewed-by: Maxim Kolosovskiy <kolos@chromium.org>
> Commit-Queue: Maxim Kolosovskiy <kolos@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#679915}

TBR=zmo@chromium.org,sunnyps@chromium.org,kolos@chromium.org

Change-Id: I743026c218932916aba9d9c7722d8343e9fdbdd9
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 953970
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1716211
Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
Reviewed-by: default avatarSunny Sachanandani <sunnyps@chromium.org>
Cr-Commit-Position: refs/heads/master@{#680276}
parent bcb37046
...@@ -307,6 +307,7 @@ jumbo_component("gl") { ...@@ -307,6 +307,7 @@ jumbo_component("gl") {
"gl_wgl_api_implementation.h", "gl_wgl_api_implementation.h",
"swap_chain_presenter.cc", "swap_chain_presenter.cc",
"swap_chain_presenter.h", "swap_chain_presenter.h",
"vsync_observer.h",
"vsync_provider_win.cc", "vsync_provider_win.cc",
"vsync_provider_win.h", "vsync_provider_win.h",
"vsync_thread_win.cc", "vsync_thread_win.cc",
......
...@@ -107,7 +107,9 @@ bool DirectCompositionChildSurfaceWin::UseSwapChainFrameStatistics() { ...@@ -107,7 +107,9 @@ bool DirectCompositionChildSurfaceWin::UseSwapChainFrameStatistics() {
return base::FeatureList::IsEnabled(features::kSwapChainFrameStatistics); return base::FeatureList::IsEnabled(features::kSwapChainFrameStatistics);
} }
DirectCompositionChildSurfaceWin::DirectCompositionChildSurfaceWin() {} DirectCompositionChildSurfaceWin::DirectCompositionChildSurfaceWin(
gfx::VSyncProvider* vsync_provider)
: vsync_provider_(vsync_provider) {}
DirectCompositionChildSurfaceWin::~DirectCompositionChildSurfaceWin() { DirectCompositionChildSurfaceWin::~DirectCompositionChildSurfaceWin() {
Destroy(); Destroy();
...@@ -258,6 +260,8 @@ gfx::SwapResult DirectCompositionChildSurfaceWin::SwapBuffers( ...@@ -258,6 +260,8 @@ gfx::SwapResult DirectCompositionChildSurfaceWin::SwapBuffers(
bool succeeded = ReleaseDrawTexture(false /* will_discard */); bool succeeded = ReleaseDrawTexture(false /* will_discard */);
if (UseSwapChainFrameStatistics()) { if (UseSwapChainFrameStatistics()) {
vsync_provider_->GetVSyncParametersIfAvailable(&last_vsync_time_,
&last_vsync_interval_);
CheckPendingFrames(); CheckPendingFrames();
// Enqueue callback after retiring previous callbacks so that it's called // Enqueue callback after retiring previous callbacks so that it's called
// after SwapBuffers() returns. // after SwapBuffers() returns.
...@@ -503,17 +507,6 @@ bool DirectCompositionChildSurfaceWin::SetEnableDCLayers(bool enable) { ...@@ -503,17 +507,6 @@ bool DirectCompositionChildSurfaceWin::SetEnableDCLayers(bool enable) {
return true; return true;
} }
void DirectCompositionChildSurfaceWin::UpdateVSyncParameters(
base::TimeTicks vsync_time,
base::TimeDelta vsync_interval) {
last_vsync_time_ = vsync_time;
last_vsync_interval_ = vsync_interval;
}
bool DirectCompositionChildSurfaceWin::HasPendingFrames() const {
return !pending_frames_.empty();
}
void DirectCompositionChildSurfaceWin::CheckPendingFrames() { void DirectCompositionChildSurfaceWin::CheckPendingFrames() {
DCHECK(UseSwapChainFrameStatistics()); DCHECK(UseSwapChainFrameStatistics());
......
...@@ -12,11 +12,15 @@ ...@@ -12,11 +12,15 @@
#include "ui/gl/gl_surface_egl.h" #include "ui/gl/gl_surface_egl.h"
namespace gfx {
class VSyncProvider;
} // namespace gfx
namespace gl { namespace gl {
class DirectCompositionChildSurfaceWin : public GLSurfaceEGL { class DirectCompositionChildSurfaceWin : public GLSurfaceEGL {
public: public:
DirectCompositionChildSurfaceWin(); explicit DirectCompositionChildSurfaceWin(gfx::VSyncProvider* vsync_provider);
static bool UseSwapChainFrameStatistics(); static bool UseSwapChainFrameStatistics();
...@@ -40,11 +44,6 @@ class DirectCompositionChildSurfaceWin : public GLSurfaceEGL { ...@@ -40,11 +44,6 @@ class DirectCompositionChildSurfaceWin : public GLSurfaceEGL {
bool has_alpha) override; bool has_alpha) override;
bool SetEnableDCLayers(bool enable) override; bool SetEnableDCLayers(bool enable) override;
void UpdateVSyncParameters(base::TimeTicks vsync_time,
base::TimeDelta vsync_interval);
bool HasPendingFrames() const;
void CheckPendingFrames();
const Microsoft::WRL::ComPtr<IDCompositionSurface>& dcomp_surface() const { const Microsoft::WRL::ComPtr<IDCompositionSurface>& dcomp_surface() const {
return dcomp_surface_; return dcomp_surface_;
} }
...@@ -76,6 +75,7 @@ class DirectCompositionChildSurfaceWin : public GLSurfaceEGL { ...@@ -76,6 +75,7 @@ class DirectCompositionChildSurfaceWin : public GLSurfaceEGL {
// Presentation callback enqueued in SwapBuffers(). // Presentation callback enqueued in SwapBuffers().
PresentationCallback callback; PresentationCallback callback;
}; };
void CheckPendingFrames();
void EnqueuePendingFrame(PresentationCallback callback); void EnqueuePendingFrame(PresentationCallback callback);
void ClearPendingFrames(); void ClearPendingFrames();
...@@ -109,6 +109,7 @@ class DirectCompositionChildSurfaceWin : public GLSurfaceEGL { ...@@ -109,6 +109,7 @@ class DirectCompositionChildSurfaceWin : public GLSurfaceEGL {
// be called on the device. // be called on the device.
uint64_t dcomp_surface_serial_ = 0; uint64_t dcomp_surface_serial_ = 0;
gfx::VSyncProvider* const vsync_provider_;
base::TimeTicks last_vsync_time_; base::TimeTicks last_vsync_time_;
base::TimeDelta last_vsync_interval_ = base::TimeDelta::FromSecondsD(1. / 60); base::TimeDelta last_vsync_interval_ = base::TimeDelta::FromSecondsD(1. / 60);
......
...@@ -172,15 +172,14 @@ DirectCompositionSurfaceWin::DirectCompositionSurfaceWin( ...@@ -172,15 +172,14 @@ DirectCompositionSurfaceWin::DirectCompositionSurfaceWin(
: GLSurfaceEGL(), : GLSurfaceEGL(),
child_window_(parent_window), child_window_(parent_window),
task_runner_(base::ThreadTaskRunnerHandle::Get()), task_runner_(base::ThreadTaskRunnerHandle::Get()),
root_surface_(new DirectCompositionChildSurfaceWin()), root_surface_(new DirectCompositionChildSurfaceWin(vsync_provider.get())),
layer_tree_(std::make_unique<DCLayerTree>( layer_tree_(std::make_unique<DCLayerTree>(
settings.disable_nv12_dynamic_textures, settings.disable_nv12_dynamic_textures,
settings.disable_larger_than_screen_overlays)), settings.disable_larger_than_screen_overlays)),
vsync_provider_(std::move(vsync_provider)),
vsync_callback_(std::move(vsync_callback)),
presentation_helper_( presentation_helper_(
std::make_unique<GLSurfacePresentationHelper>(vsync_provider_.get())), std::make_unique<GLSurfacePresentationHelper>(vsync_provider.get())),
weak_ptr_factory_(this) {} vsync_provider_(std::move(vsync_provider)),
vsync_callback_(std::move(vsync_callback)) {}
DirectCompositionSurfaceWin::~DirectCompositionSurfaceWin() { DirectCompositionSurfaceWin::~DirectCompositionSurfaceWin() {
Destroy(); Destroy();
...@@ -396,27 +395,17 @@ bool DirectCompositionSurfaceWin::Initialize(GLSurfaceFormat format) { ...@@ -396,27 +395,17 @@ bool DirectCompositionSurfaceWin::Initialize(GLSurfaceFormat format) {
if (!root_surface_->Initialize(GLSurfaceFormat())) if (!root_surface_->Initialize(GLSurfaceFormat()))
return false; return false;
if (root_surface_->UseSwapChainFrameStatistics()) { if (SupportsGpuVSync() && vsync_callback_)
// Save weak ptr on main thread before any use on vsync thread. vsync_thread_ = VSyncThreadWin::GetInstance();
main_thread_vsync_callback_ = base::BindRepeating(
&DirectCompositionSurfaceWin::HandleVSyncOnMainThread,
weak_ptr_factory_.GetWeakPtr());
}
if ((SupportsGpuVSync() && vsync_callback_) || main_thread_vsync_callback_) {
vsync_thread_ = std::make_unique<VSyncThreadWin>(
window_, d3d11_device_,
base::BindRepeating(
&DirectCompositionSurfaceWin::HandleVSyncOnVSyncThread,
base::Unretained(this)));
}
return true; return true;
} }
void DirectCompositionSurfaceWin::Destroy() { void DirectCompositionSurfaceWin::Destroy() {
// Destroy vsync thread because joining it could issue callbacks. if (vsync_thread_) {
vsync_thread_ = nullptr; vsync_thread_->RemoveObserver(this);
vsync_thread_ = nullptr;
}
// Destroy presentation helper first because its dtor calls GetHandle. // Destroy presentation helper first because its dtor calls GetHandle.
presentation_helper_ = nullptr; presentation_helper_ = nullptr;
root_surface_->Destroy(); root_surface_->Destroy();
...@@ -459,14 +448,11 @@ gfx::SwapResult DirectCompositionSurfaceWin::SwapBuffers( ...@@ -459,14 +448,11 @@ gfx::SwapResult DirectCompositionSurfaceWin::SwapBuffers(
callback.Reset(); callback.Reset();
} }
gfx::SwapResult swap_result = root_surface_->SwapBuffers(std::move(callback)); gfx::SwapResult swap_result;
if (root_surface_->SwapBuffers(std::move(callback)) ==
if (swap_result == gfx::SwapResult::SWAP_ACK && gfx::SwapResult::SWAP_ACK &&
layer_tree_->CommitAndClearPendingOverlays(root_surface_.get())) { layer_tree_->CommitAndClearPendingOverlays(root_surface_.get())) {
if (vsync_thread_) { swap_result = gfx::SwapResult::SWAP_ACK;
vsync_thread_->SetEnabled(root_surface_->HasPendingFrames() ||
vsync_callback_enabled_);
}
} else { } else {
swap_result = gfx::SwapResult::SWAP_FAILED; swap_result = gfx::SwapResult::SWAP_FAILED;
} }
...@@ -547,34 +533,17 @@ bool DirectCompositionSurfaceWin::SupportsGpuVSync() const { ...@@ -547,34 +533,17 @@ bool DirectCompositionSurfaceWin::SupportsGpuVSync() const {
void DirectCompositionSurfaceWin::SetGpuVSyncEnabled(bool enabled) { void DirectCompositionSurfaceWin::SetGpuVSyncEnabled(bool enabled) {
DCHECK(vsync_thread_); DCHECK(vsync_thread_);
if (vsync_callback_enabled_ == enabled) if (enabled) {
return; vsync_thread_->AddObserver(this);
vsync_callback_enabled_ = enabled; } else {
vsync_thread_->SetEnabled(root_surface_->HasPendingFrames() || vsync_thread_->RemoveObserver(this);
vsync_callback_enabled_);
}
void DirectCompositionSurfaceWin::HandleVSyncOnVSyncThread(
base::TimeTicks vsync_time,
base::TimeDelta vsync_interval) {
if (vsync_callback_)
vsync_callback_.Run(vsync_time, vsync_interval);
if (main_thread_vsync_callback_) {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(main_thread_vsync_callback_, vsync_time,
vsync_interval));
} }
} }
void DirectCompositionSurfaceWin::HandleVSyncOnMainThread( void DirectCompositionSurfaceWin::OnVSync(base::TimeTicks vsync_time,
base::TimeTicks vsync_time, base::TimeDelta interval) {
base::TimeDelta vsync_interval) { DCHECK(vsync_callback_);
// Check pending frames in root surface in case client stops issuing swaps. vsync_callback_.Run(vsync_time, interval);
root_surface_->UpdateVSyncParameters(vsync_time, vsync_interval);
root_surface_->CheckPendingFrames();
vsync_thread_->SetEnabled(root_surface_->HasPendingFrames() ||
vsync_callback_enabled_);
} }
scoped_refptr<base::TaskRunner> scoped_refptr<base::TaskRunner>
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "ui/gl/child_window_win.h" #include "ui/gl/child_window_win.h"
#include "ui/gl/gl_export.h" #include "ui/gl/gl_export.h"
#include "ui/gl/gl_surface_egl.h" #include "ui/gl/gl_surface_egl.h"
#include "ui/gl/vsync_observer.h"
namespace gl { namespace gl {
class DCLayerTree; class DCLayerTree;
...@@ -23,7 +24,8 @@ class DirectCompositionChildSurfaceWin; ...@@ -23,7 +24,8 @@ class DirectCompositionChildSurfaceWin;
class GLSurfacePresentationHelper; class GLSurfacePresentationHelper;
class VSyncThreadWin; class VSyncThreadWin;
class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL { class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL,
public VSyncObserver {
public: public:
using VSyncCallback = using VSyncCallback =
base::RepeatingCallback<void(base::TimeTicks, base::TimeDelta)>; base::RepeatingCallback<void(base::TimeTicks, base::TimeDelta)>;
...@@ -105,7 +107,6 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL { ...@@ -105,7 +107,6 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL {
gfx::Vector2d GetDrawOffset() const override; gfx::Vector2d GetDrawOffset() const override;
bool SupportsGpuVSync() const override; bool SupportsGpuVSync() const override;
void SetGpuVSyncEnabled(bool enabled) override; void SetGpuVSyncEnabled(bool enabled) override;
// This schedules an overlay plane to be displayed on the next SwapBuffers // This schedules an overlay plane to be displayed on the next SwapBuffers
// or PostSubBuffer call. Overlay planes must be scheduled before every swap // or PostSubBuffer call. Overlay planes must be scheduled before every swap
// to remain in the layer tree. This surface's backbuffer doesn't have to be // to remain in the layer tree. This surface's backbuffer doesn't have to be
...@@ -113,6 +114,9 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL { ...@@ -113,6 +114,9 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL {
// tree at z-order 0. // tree at z-order 0.
bool ScheduleDCLayer(const ui::DCRendererLayerParams& params) override; bool ScheduleDCLayer(const ui::DCRendererLayerParams& params) override;
// VSyncObserver implementation.
void OnVSync(base::TimeTicks vsync_time, base::TimeDelta interval) override;
HWND window() const { return window_; } HWND window() const { return window_; }
scoped_refptr<base::TaskRunner> GetWindowTaskRunnerForTesting(); scoped_refptr<base::TaskRunner> GetWindowTaskRunnerForTesting();
...@@ -127,33 +131,21 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL { ...@@ -127,33 +131,21 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL {
~DirectCompositionSurfaceWin() override; ~DirectCompositionSurfaceWin() override;
private: private:
void HandleVSyncOnVSyncThread(base::TimeTicks vsync_time,
base::TimeDelta vsync_interval);
void HandleVSyncOnMainThread(base::TimeTicks vsync_time,
base::TimeDelta vsync_interval);
HWND window_ = nullptr; HWND window_ = nullptr;
ChildWindowWin child_window_; ChildWindowWin child_window_;
scoped_refptr<base::SequencedTaskRunner> task_runner_; scoped_refptr<base::SequencedTaskRunner> task_runner_;
scoped_refptr<DirectCompositionChildSurfaceWin> root_surface_; scoped_refptr<DirectCompositionChildSurfaceWin> root_surface_;
std::unique_ptr<DCLayerTree> layer_tree_; std::unique_ptr<DCLayerTree> layer_tree_;
std::unique_ptr<GLSurfacePresentationHelper> presentation_helper_;
std::unique_ptr<VSyncThreadWin> vsync_thread_;
std::unique_ptr<gfx::VSyncProvider> vsync_provider_; std::unique_ptr<gfx::VSyncProvider> vsync_provider_;
const VSyncCallback vsync_callback_; const VSyncCallback vsync_callback_;
bool vsync_callback_enabled_ = false; VSyncThreadWin* vsync_thread_ = nullptr;
std::unique_ptr<GLSurfacePresentationHelper> presentation_helper_;
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_; Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_;
Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device_; Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device_;
VSyncCallback main_thread_vsync_callback_;
base::WeakPtrFactory<DirectCompositionSurfaceWin> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(DirectCompositionSurfaceWin); DISALLOW_COPY_AND_ASSIGN(DirectCompositionSurfaceWin);
}; };
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_GL_VSYNC_OBSERVER_H_
#define UI_GL_VSYNC_OBSERVER_H_
namespace gl {
class GL_EXPORT VSyncObserver {
public:
// Called on vsync thread.
virtual void OnVSync(base::TimeTicks vsync_time,
base::TimeDelta interval) = 0;
protected:
virtual ~VSyncObserver() {}
};
} // namespace gl
#endif // UI_GL_VSYNC_OBSERVER_H_
...@@ -5,6 +5,10 @@ ...@@ -5,6 +5,10 @@
#include "ui/gl/vsync_thread_win.h" #include "ui/gl/vsync_thread_win.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/memory/singleton.h"
#include "base/stl_util.h"
#include "ui/gl/gl_angle_util_win.h"
#include "ui/gl/vsync_observer.h"
namespace gl { namespace gl {
namespace { namespace {
...@@ -43,45 +47,51 @@ Microsoft::WRL::ComPtr<IDXGIOutput> DXGIOutputFromMonitor( ...@@ -43,45 +47,51 @@ Microsoft::WRL::ComPtr<IDXGIOutput> DXGIOutputFromMonitor(
} }
} // namespace } // namespace
VSyncThreadWin::VSyncThreadWin( // static
HWND window, VSyncThreadWin* VSyncThreadWin::GetInstance() {
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device, return base::Singleton<VSyncThreadWin>::get();
VSyncCallback callback) }
VSyncThreadWin::VSyncThreadWin()
: vsync_thread_("GpuVSyncThread"), : vsync_thread_("GpuVSyncThread"),
window_(window), d3d11_device_(QueryD3D11DeviceObjectFromANGLE()) {
d3d11_device_(std::move(d3d11_device)), DCHECK(d3d11_device_);
callback_(std::move(callback)) {
DCHECK(window_);
DCHECK(callback_);
base::Thread::Options options; base::Thread::Options options;
// Inherit priority from GPU main thread which depends on finch flags. options.priority = base::ThreadPriority::DISPLAY;
options.priority = base::PlatformThread::GetCurrentThreadPriority();
vsync_thread_.StartWithOptions(std::move(options)); vsync_thread_.StartWithOptions(std::move(options));
} }
VSyncThreadWin::~VSyncThreadWin() { VSyncThreadWin::~VSyncThreadWin() {
SetEnabled(false); {
base::AutoLock auto_lock(lock_);
observers_.clear();
}
vsync_thread_.Stop(); vsync_thread_.Stop();
} }
void VSyncThreadWin::SetEnabled(bool enabled) { void VSyncThreadWin::AddObserver(VSyncObserver* obs) {
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
if (enabled_ == enabled) observers_.insert(obs);
return; if (is_idle_) {
enabled_ = enabled; is_idle_ = false;
if (enabled_ && !started_) {
started_ = true;
vsync_thread_.task_runner()->PostTask( vsync_thread_.task_runner()->PostTask(
FROM_HERE, FROM_HERE,
base::BindOnce(&VSyncThreadWin::WaitForVSync, base::Unretained(this))); base::BindOnce(&VSyncThreadWin::WaitForVSync, base::Unretained(this)));
} }
} }
void VSyncThreadWin::RemoveObserver(VSyncObserver* obs) {
base::AutoLock auto_lock(lock_);
observers_.erase(obs);
}
void VSyncThreadWin::WaitForVSync() { void VSyncThreadWin::WaitForVSync() {
HMONITOR monitor = MonitorFromWindow(window_, MONITOR_DEFAULTTONEAREST); // From Raymond Chen's blog "How do I get a handle to the primary monitor?"
if (window_monitor_ != monitor) { // https://devblogs.microsoft.com/oldnewthing/20141106-00/?p=43683
window_monitor_ = monitor; HMONITOR monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
window_output_ = DXGIOutputFromMonitor(monitor, d3d11_device_); if (primary_monitor_ != monitor) {
primary_monitor_ = monitor;
primary_output_ = DXGIOutputFromMonitor(monitor, d3d11_device_);
} }
base::TimeDelta interval = base::TimeDelta::FromSecondsD(1.0 / 60); base::TimeDelta interval = base::TimeDelta::FromSecondsD(1.0 / 60);
...@@ -102,13 +112,13 @@ void VSyncThreadWin::WaitForVSync() { ...@@ -102,13 +112,13 @@ void VSyncThreadWin::WaitForVSync() {
base::TimeTicks wait_for_vblank_start_time = base::TimeTicks::Now(); base::TimeTicks wait_for_vblank_start_time = base::TimeTicks::Now();
bool wait_for_vblank_succeeded = bool wait_for_vblank_succeeded =
window_output_ && SUCCEEDED(window_output_->WaitForVBlank()); primary_output_ && SUCCEEDED(primary_output_->WaitForVBlank());
// WaitForVBlank returns very early instead of waiting until vblank when the // WaitForVBlank returns very early instead of waiting until vblank when the
// monitor goes to sleep. We use 1ms as a threshold for the duration of // monitor goes to sleep. We use 1ms as a threshold for the duration of
// WaitForVBlank and fallback to Sleep() if it returns before that. This // WaitForVBlank and fallback to Sleep() if it returns before that. This
// could happen during normal operation for the first call after the vsync // could happen during normal operation for the first call after the vsync
// callback is enabled, but it shouldn't happen often. // thread becomes non-idle, but it shouldn't happen often.
const auto kVBlankIntervalThreshold = base::TimeDelta::FromMilliseconds(1); const auto kVBlankIntervalThreshold = base::TimeDelta::FromMilliseconds(1);
base::TimeDelta wait_for_vblank_elapsed_time = base::TimeDelta wait_for_vblank_elapsed_time =
base::TimeTicks::Now() - wait_for_vblank_start_time; base::TimeTicks::Now() - wait_for_vblank_start_time;
...@@ -119,17 +129,15 @@ void VSyncThreadWin::WaitForVSync() { ...@@ -119,17 +129,15 @@ void VSyncThreadWin::WaitForVSync() {
} }
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
DCHECK(started_); if (!observers_.empty()) {
if (enabled_) {
vsync_thread_.task_runner()->PostTask( vsync_thread_.task_runner()->PostTask(
FROM_HERE, FROM_HERE,
base::BindOnce(&VSyncThreadWin::WaitForVSync, base::Unretained(this))); base::BindOnce(&VSyncThreadWin::WaitForVSync, base::Unretained(this)));
// Release lock before running callback to guard against any reentracny base::TimeTicks vsync_time = base::TimeTicks::Now();
// deadlock. for (auto* obs : observers_)
base::AutoUnlock auto_unlock(lock_); obs->OnVSync(vsync_time, interval);
callback_.Run(base::TimeTicks::Now(), interval);
} else { } else {
started_ = false; is_idle_ = true;
} }
} }
......
...@@ -9,48 +9,53 @@ ...@@ -9,48 +9,53 @@
#include <windows.h> #include <windows.h>
#include <wrl/client.h> #include <wrl/client.h>
#include "base/containers/flat_set.h"
#include "base/threading/thread.h" #include "base/threading/thread.h"
#include "ui/gl/gl_export.h" #include "ui/gl/gl_export.h"
namespace gl { namespace base {
template <typename T>
struct DefaultSingletonTraits;
} // namespace base
// Helper class that manages a thread for calling IDXGIOutput::WaitForVBlank() namespace gl {
// for the output corresponding to the given |window|, and runs |callback| on class VSyncObserver;
// the same thread. The callback can be enabled or disabled via SetEnabled(). // Helper singleton that wraps a thread for calling IDXGIOutput::WaitForVBlank()
// This is used by DirectCompositionSurfaceWin to plumb vsync signal back to the // for the primary monitor, and notifies observers on the same thread. Observers
// display compositor's BeginFrameSource. // can be added or removed on the main thread, and the vsync thread goes to
// sleep if there are no observers. This is used by DirectCompositionSurfaceWin
// to plumb vsync signal back to the display compositor's BeginFrameSource.
class GL_EXPORT VSyncThreadWin { class GL_EXPORT VSyncThreadWin {
public: public:
using VSyncCallback = static VSyncThreadWin* GetInstance();
base::RepeatingCallback<void(base::TimeTicks, base::TimeDelta)>;
VSyncThreadWin(HWND window,
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
VSyncCallback callback);
~VSyncThreadWin();
void SetEnabled(bool enabled); // These methods are not rentrancy safe, and shouldn't be called inside
// VSyncObserver::OnVSync. It's safe to assume that these can be called only
// from the main thread.
void AddObserver(VSyncObserver* obs);
void RemoveObserver(VSyncObserver* obs);
private: private:
friend struct base::DefaultSingletonTraits<VSyncThreadWin>;
VSyncThreadWin();
~VSyncThreadWin();
void WaitForVSync(); void WaitForVSync();
base::Thread vsync_thread_; base::Thread vsync_thread_;
// Used on vsync thread only after initialization. // Used on vsync thread only after initialization.
const HWND window_;
const Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_; const Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_;
const VSyncCallback callback_; HMONITOR primary_monitor_ = nullptr;
Microsoft::WRL::ComPtr<IDXGIOutput> primary_output_;
// Used on vsync thread exclusively.
HMONITOR window_monitor_ = nullptr;
Microsoft::WRL::ComPtr<IDXGIOutput> window_output_;
base::Lock lock_; base::Lock lock_;
bool GUARDED_BY(lock_) enabled_ = false; bool GUARDED_BY(lock_) is_idle_ = true;
bool GUARDED_BY(lock_) started_ = false; base::flat_set<VSyncObserver*> GUARDED_BY(lock_) observers_;
DISALLOW_COPY_AND_ASSIGN(VSyncThreadWin); DISALLOW_COPY_AND_ASSIGN(VSyncThreadWin);
}; };
} // namespace gl } // namespace gl
#endif // UI_GL_VSYNC_THREAD_WIN_H_ #endif // UI_GL_VSYNC_THREAD_WIN_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment