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 |