| Index: gpu/ipc/service/gpu_vsync_provider_win.cc
|
| diff --git a/gpu/ipc/service/gpu_vsync_provider_win.cc b/gpu/ipc/service/gpu_vsync_provider_win.cc
|
| index dccb12248e123f7336f6f013324d3a6e37e9d151..cae8e38c197e6c03790bb113d50ca58346b1f1e8 100644
|
| --- a/gpu/ipc/service/gpu_vsync_provider_win.cc
|
| +++ b/gpu/ipc/service/gpu_vsync_provider_win.cc
|
| @@ -7,6 +7,7 @@
|
| #include <string>
|
|
|
| #include "base/atomicops.h"
|
| +#include "base/debug/alias.h"
|
| #include "base/strings/stringprintf.h"
|
| #include "base/threading/thread.h"
|
| #include "base/trace_event/trace_event.h"
|
| @@ -20,12 +21,16 @@
|
| namespace gpu {
|
|
|
| namespace {
|
| +// Default interval used when no v-sync interval comes from DWM.
|
| +const int kDefaultTimerBasedInterval = 16666;
|
| +
|
| // from <D3dkmthk.h>
|
| typedef LONG NTSTATUS;
|
| typedef UINT D3DKMT_HANDLE;
|
| typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID;
|
|
|
| #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
| +#define STATUS_GRAPHICS_PRESENT_OCCLUDED ((NTSTATUS)0xC01E0006L)
|
|
|
| typedef struct _D3DKMT_OPENADAPTERFROMHDC {
|
| HDC hDc;
|
| @@ -63,9 +68,6 @@ class GpuVSyncWorker : public base::Thread,
|
| void Enable(bool enabled);
|
| void StartRunningVSyncOnThread();
|
| void WaitForVSyncOnThread();
|
| - void SendVSyncUpdate(base::TimeTicks now,
|
| - base::TimeTicks timestamp,
|
| - base::TimeDelta interval);
|
| bool BelongsToWorkerThread();
|
|
|
| private:
|
| @@ -75,7 +77,15 @@ class GpuVSyncWorker : public base::Thread,
|
| void Reschedule();
|
| void OpenAdapter(const wchar_t* device_name);
|
| void CloseAdapter();
|
| - bool WaitForVBlankEvent();
|
| + NTSTATUS WaitForVBlankEvent();
|
| +
|
| + void SendGpuVSyncUpdate(base::TimeTicks now,
|
| + base::TimeTicks timestamp,
|
| + base::TimeDelta interval);
|
| + void InvokeCallbackAndReschedule(base::TimeTicks timestamp,
|
| + base::TimeDelta interval);
|
| + void ScheduleDelayBasedVSync(base::TimeTicks timebase,
|
| + base::TimeDelta interval);
|
|
|
| // Specifies whether background tasks are running.
|
| // This can be set on background thread only.
|
| @@ -190,19 +200,30 @@ void GpuVSyncWorker::WaitForVSyncOnThread() {
|
| OpenAdapter(monitor_info.szDevice);
|
| }
|
|
|
| - // Crash if WaitForVBlankEvent fails to avoid spinning the loop.
|
| - CHECK(WaitForVBlankEvent());
|
| + NTSTATUS wait_result = WaitForVBlankEvent();
|
| + if (wait_result != STATUS_SUCCESS) {
|
| + if (wait_result == STATUS_GRAPHICS_PRESENT_OCCLUDED) {
|
| + // This may be triggered by the monitor going into sleep.
|
| + // Use timer based mechanism as a backup, start with getting VSync
|
| + // parameters to determine timebase and interval.
|
| + // TODO(stanisc): Consider a slower v-sync rate in this particular case.
|
| + vsync_provider_->GetVSyncParameters(base::Bind(
|
| + &GpuVSyncWorker::ScheduleDelayBasedVSync, base::Unretained(this)));
|
| + return;
|
| + } else {
|
| + base::debug::Alias(&wait_result);
|
| + CHECK(false);
|
| + }
|
| + }
|
|
|
| vsync_provider_->GetVSyncParameters(
|
| - base::Bind(&GpuVSyncWorker::SendVSyncUpdate, base::Unretained(this),
|
| + base::Bind(&GpuVSyncWorker::SendGpuVSyncUpdate, base::Unretained(this),
|
| base::TimeTicks::Now()));
|
| -
|
| - Reschedule();
|
| }
|
|
|
| -void GpuVSyncWorker::SendVSyncUpdate(base::TimeTicks now,
|
| - base::TimeTicks timestamp,
|
| - base::TimeDelta interval) {
|
| +void GpuVSyncWorker::SendGpuVSyncUpdate(base::TimeTicks now,
|
| + base::TimeTicks timestamp,
|
| + base::TimeDelta interval) {
|
| base::TimeDelta adjustment;
|
|
|
| if (!(timestamp.is_null() || interval.is_zero())) {
|
| @@ -219,17 +240,17 @@ void GpuVSyncWorker::SendVSyncUpdate(base::TimeTicks now,
|
| timestamp = now;
|
| }
|
|
|
| - TRACE_EVENT1("gpu", "GpuVSyncWorker::SendVSyncUpdate", "adjustment",
|
| + TRACE_EVENT1("gpu", "GpuVSyncWorker::SendGpuVSyncUpdate", "adjustment",
|
| adjustment.ToInternalValue());
|
|
|
| - if (base::subtle::NoBarrier_Load(&enabled_)) {
|
| - callback_.Run(timestamp, interval);
|
| - }
|
| + InvokeCallbackAndReschedule(timestamp, interval);
|
| }
|
|
|
| -void GpuVSyncWorker::Reschedule() {
|
| - // Restart the task if still enabled.
|
| +void GpuVSyncWorker::InvokeCallbackAndReschedule(base::TimeTicks timestamp,
|
| + base::TimeDelta interval) {
|
| + // Send update and restart the task if still enabled.
|
| if (base::subtle::NoBarrier_Load(&enabled_)) {
|
| + callback_.Run(timestamp, interval);
|
| task_runner()->PostTask(FROM_HERE,
|
| base::Bind(&GpuVSyncWorker::WaitForVSyncOnThread,
|
| base::Unretained(this)));
|
| @@ -238,6 +259,24 @@ void GpuVSyncWorker::Reschedule() {
|
| }
|
| }
|
|
|
| +void GpuVSyncWorker::ScheduleDelayBasedVSync(base::TimeTicks timebase,
|
| + base::TimeDelta interval) {
|
| + // This is called only when WaitForVBlankEvent fails due to monitor going to
|
| + // sleep. Use a delay based v-sync as a back-up.
|
| + if (interval.is_zero()) {
|
| + interval = base::TimeDelta::FromMicroseconds(kDefaultTimerBasedInterval);
|
| + }
|
| +
|
| + base::TimeTicks now = base::TimeTicks::Now();
|
| + base::TimeTicks next_vsync = now.SnappedToNextTick(timebase, interval);
|
| +
|
| + task_runner()->PostDelayedTask(
|
| + FROM_HERE,
|
| + base::Bind(&GpuVSyncWorker::InvokeCallbackAndReschedule,
|
| + base::Unretained(this), next_vsync, interval),
|
| + next_vsync - now);
|
| +}
|
| +
|
| void GpuVSyncWorker::OpenAdapter(const wchar_t* device_name) {
|
| DCHECK_EQ(0u, current_adapter_handle_);
|
|
|
| @@ -269,16 +308,14 @@ void GpuVSyncWorker::CloseAdapter() {
|
| }
|
| }
|
|
|
| -bool GpuVSyncWorker::WaitForVBlankEvent() {
|
| +NTSTATUS GpuVSyncWorker::WaitForVBlankEvent() {
|
| D3DKMT_WAITFORVERTICALBLANKEVENT wait_for_vertical_blank_event_data;
|
| wait_for_vertical_blank_event_data.hAdapter = current_adapter_handle_;
|
| wait_for_vertical_blank_event_data.hDevice = 0;
|
| wait_for_vertical_blank_event_data.VidPnSourceId = current_source_id_;
|
|
|
| - NTSTATUS result =
|
| - wait_for_vertical_blank_event_ptr_(&wait_for_vertical_blank_event_data);
|
| -
|
| - return result == STATUS_SUCCESS;
|
| + return wait_for_vertical_blank_event_ptr_(
|
| + &wait_for_vertical_blank_event_data);
|
| }
|
|
|
| // MessageFilter class for sending and receiving IPC messages
|
|
|