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") {
"gl_wgl_api_implementation.h",
"swap_chain_presenter.cc",
"swap_chain_presenter.h",
"vsync_observer.h",
"vsync_provider_win.cc",
"vsync_provider_win.h",
"vsync_thread_win.cc",
......
......@@ -107,7 +107,9 @@ bool DirectCompositionChildSurfaceWin::UseSwapChainFrameStatistics() {
return base::FeatureList::IsEnabled(features::kSwapChainFrameStatistics);
}
DirectCompositionChildSurfaceWin::DirectCompositionChildSurfaceWin() {}
DirectCompositionChildSurfaceWin::DirectCompositionChildSurfaceWin(
gfx::VSyncProvider* vsync_provider)
: vsync_provider_(vsync_provider) {}
DirectCompositionChildSurfaceWin::~DirectCompositionChildSurfaceWin() {
Destroy();
......@@ -258,6 +260,8 @@ gfx::SwapResult DirectCompositionChildSurfaceWin::SwapBuffers(
bool succeeded = ReleaseDrawTexture(false /* will_discard */);
if (UseSwapChainFrameStatistics()) {
vsync_provider_->GetVSyncParametersIfAvailable(&last_vsync_time_,
&last_vsync_interval_);
CheckPendingFrames();
// Enqueue callback after retiring previous callbacks so that it's called
// after SwapBuffers() returns.
......@@ -503,17 +507,6 @@ bool DirectCompositionChildSurfaceWin::SetEnableDCLayers(bool enable) {
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() {
DCHECK(UseSwapChainFrameStatistics());
......
......@@ -12,11 +12,15 @@
#include "ui/gl/gl_surface_egl.h"
namespace gfx {
class VSyncProvider;
} // namespace gfx
namespace gl {
class DirectCompositionChildSurfaceWin : public GLSurfaceEGL {
public:
DirectCompositionChildSurfaceWin();
explicit DirectCompositionChildSurfaceWin(gfx::VSyncProvider* vsync_provider);
static bool UseSwapChainFrameStatistics();
......@@ -40,11 +44,6 @@ class DirectCompositionChildSurfaceWin : public GLSurfaceEGL {
bool has_alpha) 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 {
return dcomp_surface_;
}
......@@ -76,6 +75,7 @@ class DirectCompositionChildSurfaceWin : public GLSurfaceEGL {
// Presentation callback enqueued in SwapBuffers().
PresentationCallback callback;
};
void CheckPendingFrames();
void EnqueuePendingFrame(PresentationCallback callback);
void ClearPendingFrames();
......@@ -109,6 +109,7 @@ class DirectCompositionChildSurfaceWin : public GLSurfaceEGL {
// be called on the device.
uint64_t dcomp_surface_serial_ = 0;
gfx::VSyncProvider* const vsync_provider_;
base::TimeTicks last_vsync_time_;
base::TimeDelta last_vsync_interval_ = base::TimeDelta::FromSecondsD(1. / 60);
......
......@@ -172,15 +172,14 @@ DirectCompositionSurfaceWin::DirectCompositionSurfaceWin(
: GLSurfaceEGL(),
child_window_(parent_window),
task_runner_(base::ThreadTaskRunnerHandle::Get()),
root_surface_(new DirectCompositionChildSurfaceWin()),
root_surface_(new DirectCompositionChildSurfaceWin(vsync_provider.get())),
layer_tree_(std::make_unique<DCLayerTree>(
settings.disable_nv12_dynamic_textures,
settings.disable_larger_than_screen_overlays)),
vsync_provider_(std::move(vsync_provider)),
vsync_callback_(std::move(vsync_callback)),
presentation_helper_(
std::make_unique<GLSurfacePresentationHelper>(vsync_provider_.get())),
weak_ptr_factory_(this) {}
std::make_unique<GLSurfacePresentationHelper>(vsync_provider.get())),
vsync_provider_(std::move(vsync_provider)),
vsync_callback_(std::move(vsync_callback)) {}
DirectCompositionSurfaceWin::~DirectCompositionSurfaceWin() {
Destroy();
......@@ -396,27 +395,17 @@ bool DirectCompositionSurfaceWin::Initialize(GLSurfaceFormat format) {
if (!root_surface_->Initialize(GLSurfaceFormat()))
return false;
if (root_surface_->UseSwapChainFrameStatistics()) {
// Save weak ptr on main thread before any use on vsync thread.
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)));
}
if (SupportsGpuVSync() && vsync_callback_)
vsync_thread_ = VSyncThreadWin::GetInstance();
return true;
}
void DirectCompositionSurfaceWin::Destroy() {
// Destroy vsync thread because joining it could issue callbacks.
if (vsync_thread_) {
vsync_thread_->RemoveObserver(this);
vsync_thread_ = nullptr;
}
// Destroy presentation helper first because its dtor calls GetHandle.
presentation_helper_ = nullptr;
root_surface_->Destroy();
......@@ -459,14 +448,11 @@ gfx::SwapResult DirectCompositionSurfaceWin::SwapBuffers(
callback.Reset();
}
gfx::SwapResult swap_result = root_surface_->SwapBuffers(std::move(callback));
if (swap_result == gfx::SwapResult::SWAP_ACK &&
gfx::SwapResult swap_result;
if (root_surface_->SwapBuffers(std::move(callback)) ==
gfx::SwapResult::SWAP_ACK &&
layer_tree_->CommitAndClearPendingOverlays(root_surface_.get())) {
if (vsync_thread_) {
vsync_thread_->SetEnabled(root_surface_->HasPendingFrames() ||
vsync_callback_enabled_);
}
swap_result = gfx::SwapResult::SWAP_ACK;
} else {
swap_result = gfx::SwapResult::SWAP_FAILED;
}
......@@ -547,34 +533,17 @@ bool DirectCompositionSurfaceWin::SupportsGpuVSync() const {
void DirectCompositionSurfaceWin::SetGpuVSyncEnabled(bool enabled) {
DCHECK(vsync_thread_);
if (vsync_callback_enabled_ == enabled)
return;
vsync_callback_enabled_ = enabled;
vsync_thread_->SetEnabled(root_surface_->HasPendingFrames() ||
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));
if (enabled) {
vsync_thread_->AddObserver(this);
} else {
vsync_thread_->RemoveObserver(this);
}
}
void DirectCompositionSurfaceWin::HandleVSyncOnMainThread(
base::TimeTicks vsync_time,
base::TimeDelta vsync_interval) {
// Check pending frames in root surface in case client stops issuing swaps.
root_surface_->UpdateVSyncParameters(vsync_time, vsync_interval);
root_surface_->CheckPendingFrames();
vsync_thread_->SetEnabled(root_surface_->HasPendingFrames() ||
vsync_callback_enabled_);
void DirectCompositionSurfaceWin::OnVSync(base::TimeTicks vsync_time,
base::TimeDelta interval) {
DCHECK(vsync_callback_);
vsync_callback_.Run(vsync_time, interval);
}
scoped_refptr<base::TaskRunner>
......
......@@ -16,6 +16,7 @@
#include "ui/gl/child_window_win.h"
#include "ui/gl/gl_export.h"
#include "ui/gl/gl_surface_egl.h"
#include "ui/gl/vsync_observer.h"
namespace gl {
class DCLayerTree;
......@@ -23,7 +24,8 @@ class DirectCompositionChildSurfaceWin;
class GLSurfacePresentationHelper;
class VSyncThreadWin;
class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL {
class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL,
public VSyncObserver {
public:
using VSyncCallback =
base::RepeatingCallback<void(base::TimeTicks, base::TimeDelta)>;
......@@ -105,7 +107,6 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL {
gfx::Vector2d GetDrawOffset() const override;
bool SupportsGpuVSync() const override;
void SetGpuVSyncEnabled(bool enabled) override;
// This schedules an overlay plane to be displayed on the next SwapBuffers
// 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
......@@ -113,6 +114,9 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL {
// tree at z-order 0.
bool ScheduleDCLayer(const ui::DCRendererLayerParams& params) override;
// VSyncObserver implementation.
void OnVSync(base::TimeTicks vsync_time, base::TimeDelta interval) override;
HWND window() const { return window_; }
scoped_refptr<base::TaskRunner> GetWindowTaskRunnerForTesting();
......@@ -127,33 +131,21 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL {
~DirectCompositionSurfaceWin() override;
private:
void HandleVSyncOnVSyncThread(base::TimeTicks vsync_time,
base::TimeDelta vsync_interval);
void HandleVSyncOnMainThread(base::TimeTicks vsync_time,
base::TimeDelta vsync_interval);
HWND window_ = nullptr;
ChildWindowWin child_window_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
scoped_refptr<DirectCompositionChildSurfaceWin> root_surface_;
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_;
const VSyncCallback vsync_callback_;
bool vsync_callback_enabled_ = false;
std::unique_ptr<GLSurfacePresentationHelper> presentation_helper_;
VSyncThreadWin* vsync_thread_ = nullptr;
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_;
Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device_;
VSyncCallback main_thread_vsync_callback_;
base::WeakPtrFactory<DirectCompositionSurfaceWin> weak_ptr_factory_;
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 @@
#include "ui/gl/vsync_thread_win.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 {
......@@ -43,45 +47,51 @@ Microsoft::WRL::ComPtr<IDXGIOutput> DXGIOutputFromMonitor(
}
} // namespace
VSyncThreadWin::VSyncThreadWin(
HWND window,
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
VSyncCallback callback)
// static
VSyncThreadWin* VSyncThreadWin::GetInstance() {
return base::Singleton<VSyncThreadWin>::get();
}
VSyncThreadWin::VSyncThreadWin()
: vsync_thread_("GpuVSyncThread"),
window_(window),
d3d11_device_(std::move(d3d11_device)),
callback_(std::move(callback)) {
DCHECK(window_);
DCHECK(callback_);
d3d11_device_(QueryD3D11DeviceObjectFromANGLE()) {
DCHECK(d3d11_device_);
base::Thread::Options options;
// Inherit priority from GPU main thread which depends on finch flags.
options.priority = base::PlatformThread::GetCurrentThreadPriority();
options.priority = base::ThreadPriority::DISPLAY;
vsync_thread_.StartWithOptions(std::move(options));
}
VSyncThreadWin::~VSyncThreadWin() {
SetEnabled(false);
{
base::AutoLock auto_lock(lock_);
observers_.clear();
}
vsync_thread_.Stop();
}
void VSyncThreadWin::SetEnabled(bool enabled) {
void VSyncThreadWin::AddObserver(VSyncObserver* obs) {
base::AutoLock auto_lock(lock_);
if (enabled_ == enabled)
return;
enabled_ = enabled;
if (enabled_ && !started_) {
started_ = true;
observers_.insert(obs);
if (is_idle_) {
is_idle_ = false;
vsync_thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&VSyncThreadWin::WaitForVSync, base::Unretained(this)));
}
}
void VSyncThreadWin::RemoveObserver(VSyncObserver* obs) {
base::AutoLock auto_lock(lock_);
observers_.erase(obs);
}
void VSyncThreadWin::WaitForVSync() {
HMONITOR monitor = MonitorFromWindow(window_, MONITOR_DEFAULTTONEAREST);
if (window_monitor_ != monitor) {
window_monitor_ = monitor;
window_output_ = DXGIOutputFromMonitor(monitor, d3d11_device_);
// From Raymond Chen's blog "How do I get a handle to the primary monitor?"
// https://devblogs.microsoft.com/oldnewthing/20141106-00/?p=43683
HMONITOR monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
if (primary_monitor_ != monitor) {
primary_monitor_ = monitor;
primary_output_ = DXGIOutputFromMonitor(monitor, d3d11_device_);
}
base::TimeDelta interval = base::TimeDelta::FromSecondsD(1.0 / 60);
......@@ -102,13 +112,13 @@ void VSyncThreadWin::WaitForVSync() {
base::TimeTicks wait_for_vblank_start_time = base::TimeTicks::Now();
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
// 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
// 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);
base::TimeDelta wait_for_vblank_elapsed_time =
base::TimeTicks::Now() - wait_for_vblank_start_time;
......@@ -119,17 +129,15 @@ void VSyncThreadWin::WaitForVSync() {
}
base::AutoLock auto_lock(lock_);
DCHECK(started_);
if (enabled_) {
if (!observers_.empty()) {
vsync_thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&VSyncThreadWin::WaitForVSync, base::Unretained(this)));
// Release lock before running callback to guard against any reentracny
// deadlock.
base::AutoUnlock auto_unlock(lock_);
callback_.Run(base::TimeTicks::Now(), interval);
base::TimeTicks vsync_time = base::TimeTicks::Now();
for (auto* obs : observers_)
obs->OnVSync(vsync_time, interval);
} else {
started_ = false;
is_idle_ = true;
}
}
......
......@@ -9,48 +9,53 @@
#include <windows.h>
#include <wrl/client.h>
#include "base/containers/flat_set.h"
#include "base/threading/thread.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()
// for the output corresponding to the given |window|, and runs |callback| on
// the same thread. The callback can be enabled or disabled via SetEnabled().
// This is used by DirectCompositionSurfaceWin to plumb vsync signal back to the
// display compositor's BeginFrameSource.
namespace gl {
class VSyncObserver;
// Helper singleton that wraps a thread for calling IDXGIOutput::WaitForVBlank()
// for the primary monitor, and notifies observers on the same thread. Observers
// 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 {
public:
using VSyncCallback =
base::RepeatingCallback<void(base::TimeTicks, base::TimeDelta)>;
VSyncThreadWin(HWND window,
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
VSyncCallback callback);
~VSyncThreadWin();
static VSyncThreadWin* GetInstance();
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:
friend struct base::DefaultSingletonTraits<VSyncThreadWin>;
VSyncThreadWin();
~VSyncThreadWin();
void WaitForVSync();
base::Thread vsync_thread_;
// Used on vsync thread only after initialization.
const HWND window_;
const Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_;
const VSyncCallback callback_;
// Used on vsync thread exclusively.
HMONITOR window_monitor_ = nullptr;
Microsoft::WRL::ComPtr<IDXGIOutput> window_output_;
HMONITOR primary_monitor_ = nullptr;
Microsoft::WRL::ComPtr<IDXGIOutput> primary_output_;
base::Lock lock_;
bool GUARDED_BY(lock_) enabled_ = false;
bool GUARDED_BY(lock_) started_ = false;
bool GUARDED_BY(lock_) is_idle_ = true;
base::flat_set<VSyncObserver*> GUARDED_BY(lock_) observers_;
DISALLOW_COPY_AND_ASSIGN(VSyncThreadWin);
};
} // namespace gl
#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