| OLD | NEW |
| 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "gpu/ipc/service/gpu_vsync_provider_win.h" | 5 #include "gpu/ipc/service/gpu_vsync_provider_win.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/atomicops.h" | 9 #include "base/atomicops.h" |
| 10 #include "base/debug/alias.h" |
| 10 #include "base/strings/stringprintf.h" | 11 #include "base/strings/stringprintf.h" |
| 11 #include "base/threading/thread.h" | 12 #include "base/threading/thread.h" |
| 12 #include "base/trace_event/trace_event.h" | 13 #include "base/trace_event/trace_event.h" |
| 13 #include "gpu/ipc/common/gpu_messages.h" | 14 #include "gpu/ipc/common/gpu_messages.h" |
| 14 #include "ipc/ipc_message_macros.h" | 15 #include "ipc/ipc_message_macros.h" |
| 15 #include "ipc/message_filter.h" | 16 #include "ipc/message_filter.h" |
| 16 #include "ui/gl/vsync_provider_win.h" | 17 #include "ui/gl/vsync_provider_win.h" |
| 17 | 18 |
| 18 #include <windows.h> | 19 #include <windows.h> |
| 19 | 20 |
| 20 namespace gpu { | 21 namespace gpu { |
| 21 | 22 |
| 22 namespace { | 23 namespace { |
| 24 // Default interval used when no v-sync interval comes from DWM. |
| 25 const int kDefaultTimerBasedInterval = 16666; |
| 26 |
| 23 // from <D3dkmthk.h> | 27 // from <D3dkmthk.h> |
| 24 typedef LONG NTSTATUS; | 28 typedef LONG NTSTATUS; |
| 25 typedef UINT D3DKMT_HANDLE; | 29 typedef UINT D3DKMT_HANDLE; |
| 26 typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID; | 30 typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID; |
| 27 | 31 |
| 28 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) | 32 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) |
| 33 #define STATUS_GRAPHICS_PRESENT_OCCLUDED ((NTSTATUS)0xC01E0006L) |
| 29 | 34 |
| 30 typedef struct _D3DKMT_OPENADAPTERFROMHDC { | 35 typedef struct _D3DKMT_OPENADAPTERFROMHDC { |
| 31 HDC hDc; | 36 HDC hDc; |
| 32 D3DKMT_HANDLE hAdapter; | 37 D3DKMT_HANDLE hAdapter; |
| 33 LUID AdapterLuid; | 38 LUID AdapterLuid; |
| 34 D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId; | 39 D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId; |
| 35 } D3DKMT_OPENADAPTERFROMHDC; | 40 } D3DKMT_OPENADAPTERFROMHDC; |
| 36 | 41 |
| 37 typedef struct _D3DKMT_CLOSEADAPTER { | 42 typedef struct _D3DKMT_CLOSEADAPTER { |
| 38 D3DKMT_HANDLE hAdapter; | 43 D3DKMT_HANDLE hAdapter; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 56 class GpuVSyncWorker : public base::Thread, | 61 class GpuVSyncWorker : public base::Thread, |
| 57 public base::RefCountedThreadSafe<GpuVSyncWorker> { | 62 public base::RefCountedThreadSafe<GpuVSyncWorker> { |
| 58 public: | 63 public: |
| 59 GpuVSyncWorker(const gfx::VSyncProvider::UpdateVSyncCallback& callback, | 64 GpuVSyncWorker(const gfx::VSyncProvider::UpdateVSyncCallback& callback, |
| 60 SurfaceHandle surface_handle); | 65 SurfaceHandle surface_handle); |
| 61 | 66 |
| 62 void CleanupAndStop(); | 67 void CleanupAndStop(); |
| 63 void Enable(bool enabled); | 68 void Enable(bool enabled); |
| 64 void StartRunningVSyncOnThread(); | 69 void StartRunningVSyncOnThread(); |
| 65 void WaitForVSyncOnThread(); | 70 void WaitForVSyncOnThread(); |
| 66 void SendVSyncUpdate(base::TimeTicks now, | |
| 67 base::TimeTicks timestamp, | |
| 68 base::TimeDelta interval); | |
| 69 bool BelongsToWorkerThread(); | 71 bool BelongsToWorkerThread(); |
| 70 | 72 |
| 71 private: | 73 private: |
| 72 friend class base::RefCountedThreadSafe<GpuVSyncWorker>; | 74 friend class base::RefCountedThreadSafe<GpuVSyncWorker>; |
| 73 ~GpuVSyncWorker() override; | 75 ~GpuVSyncWorker() override; |
| 74 | 76 |
| 75 void Reschedule(); | 77 void Reschedule(); |
| 76 void OpenAdapter(const wchar_t* device_name); | 78 void OpenAdapter(const wchar_t* device_name); |
| 77 void CloseAdapter(); | 79 void CloseAdapter(); |
| 78 bool WaitForVBlankEvent(); | 80 NTSTATUS WaitForVBlankEvent(); |
| 81 |
| 82 void SendGpuVSyncUpdate(base::TimeTicks now, |
| 83 base::TimeTicks timestamp, |
| 84 base::TimeDelta interval); |
| 85 void InvokeCallbackAndReschedule(base::TimeTicks timestamp, |
| 86 base::TimeDelta interval); |
| 87 void ScheduleDelayBasedVSync(base::TimeTicks timebase, |
| 88 base::TimeDelta interval); |
| 79 | 89 |
| 80 // Specifies whether background tasks are running. | 90 // Specifies whether background tasks are running. |
| 81 // This can be set on background thread only. | 91 // This can be set on background thread only. |
| 82 bool running_ = false; | 92 bool running_ = false; |
| 83 | 93 |
| 84 // Specified whether the worker is enabled. This is accessed from both | 94 // Specified whether the worker is enabled. This is accessed from both |
| 85 // threads but can be changed on the main thread only. | 95 // threads but can be changed on the main thread only. |
| 86 base::subtle::AtomicWord enabled_ = false; | 96 base::subtle::AtomicWord enabled_ = false; |
| 87 | 97 |
| 88 const gfx::VSyncProvider::UpdateVSyncCallback callback_; | 98 const gfx::VSyncProvider::UpdateVSyncCallback callback_; |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 183 monitor_info.cbSize = sizeof(MONITORINFOEX); | 193 monitor_info.cbSize = sizeof(MONITORINFOEX); |
| 184 BOOL success = GetMonitorInfo(monitor, &monitor_info); | 194 BOOL success = GetMonitorInfo(monitor, &monitor_info); |
| 185 CHECK(success); | 195 CHECK(success); |
| 186 | 196 |
| 187 if (current_device_name_.compare(monitor_info.szDevice) != 0) { | 197 if (current_device_name_.compare(monitor_info.szDevice) != 0) { |
| 188 // Monitor changed. Close the current adapter handle and open a new one. | 198 // Monitor changed. Close the current adapter handle and open a new one. |
| 189 CloseAdapter(); | 199 CloseAdapter(); |
| 190 OpenAdapter(monitor_info.szDevice); | 200 OpenAdapter(monitor_info.szDevice); |
| 191 } | 201 } |
| 192 | 202 |
| 193 // Crash if WaitForVBlankEvent fails to avoid spinning the loop. | 203 NTSTATUS wait_result = WaitForVBlankEvent(); |
| 194 CHECK(WaitForVBlankEvent()); | 204 if (wait_result != STATUS_SUCCESS) { |
| 205 if (wait_result == STATUS_GRAPHICS_PRESENT_OCCLUDED) { |
| 206 // This may be triggered by the monitor going into sleep. |
| 207 // Use timer based mechanism as a backup, start with getting VSync |
| 208 // parameters to determine timebase and interval. |
| 209 // TODO(stanisc): Consider a slower v-sync rate in this particular case. |
| 210 vsync_provider_->GetVSyncParameters(base::Bind( |
| 211 &GpuVSyncWorker::ScheduleDelayBasedVSync, base::Unretained(this))); |
| 212 return; |
| 213 } else { |
| 214 base::debug::Alias(&wait_result); |
| 215 CHECK(false); |
| 216 } |
| 217 } |
| 195 | 218 |
| 196 vsync_provider_->GetVSyncParameters( | 219 vsync_provider_->GetVSyncParameters( |
| 197 base::Bind(&GpuVSyncWorker::SendVSyncUpdate, base::Unretained(this), | 220 base::Bind(&GpuVSyncWorker::SendGpuVSyncUpdate, base::Unretained(this), |
| 198 base::TimeTicks::Now())); | 221 base::TimeTicks::Now())); |
| 199 | |
| 200 Reschedule(); | |
| 201 } | 222 } |
| 202 | 223 |
| 203 void GpuVSyncWorker::SendVSyncUpdate(base::TimeTicks now, | 224 void GpuVSyncWorker::SendGpuVSyncUpdate(base::TimeTicks now, |
| 204 base::TimeTicks timestamp, | 225 base::TimeTicks timestamp, |
| 205 base::TimeDelta interval) { | 226 base::TimeDelta interval) { |
| 206 base::TimeDelta adjustment; | 227 base::TimeDelta adjustment; |
| 207 | 228 |
| 208 if (!(timestamp.is_null() || interval.is_zero())) { | 229 if (!(timestamp.is_null() || interval.is_zero())) { |
| 209 // Timestamp comes from DwmGetCompositionTimingInfo and apparently it might | 230 // Timestamp comes from DwmGetCompositionTimingInfo and apparently it might |
| 210 // be up to 2-3 vsync cycles in the past or in the future. | 231 // be up to 2-3 vsync cycles in the past or in the future. |
| 211 // The adjustment formula was suggested here: | 232 // The adjustment formula was suggested here: |
| 212 // http://www.vsynctester.com/firefoxisbroken.html | 233 // http://www.vsynctester.com/firefoxisbroken.html |
| 213 adjustment = | 234 adjustment = |
| 214 ((now - timestamp + interval / 8) % interval + interval) % interval - | 235 ((now - timestamp + interval / 8) % interval + interval) % interval - |
| 215 interval / 8; | 236 interval / 8; |
| 216 timestamp = now - adjustment; | 237 timestamp = now - adjustment; |
| 217 } else { | 238 } else { |
| 218 // DWM must be disabled. | 239 // DWM must be disabled. |
| 219 timestamp = now; | 240 timestamp = now; |
| 220 } | 241 } |
| 221 | 242 |
| 222 TRACE_EVENT1("gpu", "GpuVSyncWorker::SendVSyncUpdate", "adjustment", | 243 TRACE_EVENT1("gpu", "GpuVSyncWorker::SendGpuVSyncUpdate", "adjustment", |
| 223 adjustment.ToInternalValue()); | 244 adjustment.ToInternalValue()); |
| 224 | 245 |
| 246 InvokeCallbackAndReschedule(timestamp, interval); |
| 247 } |
| 248 |
| 249 void GpuVSyncWorker::InvokeCallbackAndReschedule(base::TimeTicks timestamp, |
| 250 base::TimeDelta interval) { |
| 251 // Send update and restart the task if still enabled. |
| 225 if (base::subtle::NoBarrier_Load(&enabled_)) { | 252 if (base::subtle::NoBarrier_Load(&enabled_)) { |
| 226 callback_.Run(timestamp, interval); | 253 callback_.Run(timestamp, interval); |
| 227 } | |
| 228 } | |
| 229 | |
| 230 void GpuVSyncWorker::Reschedule() { | |
| 231 // Restart the task if still enabled. | |
| 232 if (base::subtle::NoBarrier_Load(&enabled_)) { | |
| 233 task_runner()->PostTask(FROM_HERE, | 254 task_runner()->PostTask(FROM_HERE, |
| 234 base::Bind(&GpuVSyncWorker::WaitForVSyncOnThread, | 255 base::Bind(&GpuVSyncWorker::WaitForVSyncOnThread, |
| 235 base::Unretained(this))); | 256 base::Unretained(this))); |
| 236 } else { | 257 } else { |
| 237 running_ = false; | 258 running_ = false; |
| 238 } | 259 } |
| 239 } | 260 } |
| 240 | 261 |
| 262 void GpuVSyncWorker::ScheduleDelayBasedVSync(base::TimeTicks timebase, |
| 263 base::TimeDelta interval) { |
| 264 // This is called only when WaitForVBlankEvent fails due to monitor going to |
| 265 // sleep. Use a delay based v-sync as a back-up. |
| 266 if (interval.is_zero()) { |
| 267 interval = base::TimeDelta::FromMicroseconds(kDefaultTimerBasedInterval); |
| 268 } |
| 269 |
| 270 base::TimeTicks now = base::TimeTicks::Now(); |
| 271 base::TimeTicks next_vsync = now.SnappedToNextTick(timebase, interval); |
| 272 |
| 273 task_runner()->PostDelayedTask( |
| 274 FROM_HERE, |
| 275 base::Bind(&GpuVSyncWorker::InvokeCallbackAndReschedule, |
| 276 base::Unretained(this), next_vsync, interval), |
| 277 next_vsync - now); |
| 278 } |
| 279 |
| 241 void GpuVSyncWorker::OpenAdapter(const wchar_t* device_name) { | 280 void GpuVSyncWorker::OpenAdapter(const wchar_t* device_name) { |
| 242 DCHECK_EQ(0u, current_adapter_handle_); | 281 DCHECK_EQ(0u, current_adapter_handle_); |
| 243 | 282 |
| 244 HDC hdc = CreateDC(NULL, device_name, NULL, NULL); | 283 HDC hdc = CreateDC(NULL, device_name, NULL, NULL); |
| 245 | 284 |
| 246 D3DKMT_OPENADAPTERFROMHDC open_adapter_data; | 285 D3DKMT_OPENADAPTERFROMHDC open_adapter_data; |
| 247 open_adapter_data.hDc = hdc; | 286 open_adapter_data.hDc = hdc; |
| 248 | 287 |
| 249 NTSTATUS result = open_adapter_from_hdc_ptr_(&open_adapter_data); | 288 NTSTATUS result = open_adapter_from_hdc_ptr_(&open_adapter_data); |
| 250 DeleteDC(hdc); | 289 DeleteDC(hdc); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 262 close_adapter_data.hAdapter = current_adapter_handle_; | 301 close_adapter_data.hAdapter = current_adapter_handle_; |
| 263 | 302 |
| 264 NTSTATUS result = close_adapter_ptr_(&close_adapter_data); | 303 NTSTATUS result = close_adapter_ptr_(&close_adapter_data); |
| 265 CHECK(result == STATUS_SUCCESS); | 304 CHECK(result == STATUS_SUCCESS); |
| 266 | 305 |
| 267 current_adapter_handle_ = 0; | 306 current_adapter_handle_ = 0; |
| 268 current_device_name_.clear(); | 307 current_device_name_.clear(); |
| 269 } | 308 } |
| 270 } | 309 } |
| 271 | 310 |
| 272 bool GpuVSyncWorker::WaitForVBlankEvent() { | 311 NTSTATUS GpuVSyncWorker::WaitForVBlankEvent() { |
| 273 D3DKMT_WAITFORVERTICALBLANKEVENT wait_for_vertical_blank_event_data; | 312 D3DKMT_WAITFORVERTICALBLANKEVENT wait_for_vertical_blank_event_data; |
| 274 wait_for_vertical_blank_event_data.hAdapter = current_adapter_handle_; | 313 wait_for_vertical_blank_event_data.hAdapter = current_adapter_handle_; |
| 275 wait_for_vertical_blank_event_data.hDevice = 0; | 314 wait_for_vertical_blank_event_data.hDevice = 0; |
| 276 wait_for_vertical_blank_event_data.VidPnSourceId = current_source_id_; | 315 wait_for_vertical_blank_event_data.VidPnSourceId = current_source_id_; |
| 277 | 316 |
| 278 NTSTATUS result = | 317 return wait_for_vertical_blank_event_ptr_( |
| 279 wait_for_vertical_blank_event_ptr_(&wait_for_vertical_blank_event_data); | 318 &wait_for_vertical_blank_event_data); |
| 280 | |
| 281 return result == STATUS_SUCCESS; | |
| 282 } | 319 } |
| 283 | 320 |
| 284 // MessageFilter class for sending and receiving IPC messages | 321 // MessageFilter class for sending and receiving IPC messages |
| 285 // directly, avoiding routing them through the main GPU thread. | 322 // directly, avoiding routing them through the main GPU thread. |
| 286 class GpuVSyncMessageFilter : public IPC::MessageFilter { | 323 class GpuVSyncMessageFilter : public IPC::MessageFilter { |
| 287 public: | 324 public: |
| 288 explicit GpuVSyncMessageFilter( | 325 explicit GpuVSyncMessageFilter( |
| 289 const scoped_refptr<GpuVSyncWorker>& vsync_worker, | 326 const scoped_refptr<GpuVSyncWorker>& vsync_worker, |
| 290 int32_t route_id) | 327 int32_t route_id) |
| 291 : vsync_worker_(vsync_worker), route_id_(route_id) {} | 328 : vsync_worker_(vsync_worker), route_id_(route_id) {} |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 385 void GpuVSyncProviderWin::OnVSync(base::TimeTicks timestamp, | 422 void GpuVSyncProviderWin::OnVSync(base::TimeTicks timestamp, |
| 386 base::TimeDelta interval) { | 423 base::TimeDelta interval) { |
| 387 DCHECK(vsync_worker_->BelongsToWorkerThread()); | 424 DCHECK(vsync_worker_->BelongsToWorkerThread()); |
| 388 | 425 |
| 389 message_filter_->Send( | 426 message_filter_->Send( |
| 390 base::MakeUnique<GpuCommandBufferMsg_UpdateVSyncParameters>( | 427 base::MakeUnique<GpuCommandBufferMsg_UpdateVSyncParameters>( |
| 391 message_filter_->route_id(), timestamp, interval)); | 428 message_filter_->route_id(), timestamp, interval)); |
| 392 } | 429 } |
| 393 | 430 |
| 394 } // namespace gpu | 431 } // namespace gpu |
| OLD | NEW |