Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(373)

Side by Side Diff: gpu/ipc/service/gpu_vsync_provider_win.cc

Issue 2681033011: Changed GpuVSyncProvider to implement gfx::VSyncProvider (Closed)
Patch Set: Addressed CR feedback Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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.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/strings/stringprintf.h" 10 #include "base/strings/stringprintf.h"
11 #include "base/threading/thread.h" 11 #include "base/threading/thread.h"
12 #include "base/trace_event/trace_event.h" 12 #include "base/trace_event/trace_event.h"
13 #include "gpu/ipc/common/gpu_messages.h"
14 #include "ipc/ipc_message_macros.h"
15 #include "ipc/message_filter.h"
16 #include "ui/gl/vsync_provider_win.h"
13 17
14 #include <windows.h> 18 #include <windows.h>
15 19
16 namespace gpu { 20 namespace gpu {
17 21
18 namespace { 22 namespace {
19 // from <D3dkmthk.h> 23 // from <D3dkmthk.h>
20 typedef LONG NTSTATUS; 24 typedef LONG NTSTATUS;
21 typedef UINT D3DKMT_HANDLE; 25 typedef UINT D3DKMT_HANDLE;
22 typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID; 26 typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID;
(...skipping 19 matching lines...) Expand all
42 46
43 typedef NTSTATUS(APIENTRY* PFND3DKMTOPENADAPTERFROMHDC)( 47 typedef NTSTATUS(APIENTRY* PFND3DKMTOPENADAPTERFROMHDC)(
44 D3DKMT_OPENADAPTERFROMHDC*); 48 D3DKMT_OPENADAPTERFROMHDC*);
45 typedef NTSTATUS(APIENTRY* PFND3DKMTCLOSEADAPTER)(D3DKMT_CLOSEADAPTER*); 49 typedef NTSTATUS(APIENTRY* PFND3DKMTCLOSEADAPTER)(D3DKMT_CLOSEADAPTER*);
46 typedef NTSTATUS(APIENTRY* PFND3DKMTWAITFORVERTICALBLANKEVENT)( 50 typedef NTSTATUS(APIENTRY* PFND3DKMTWAITFORVERTICALBLANKEVENT)(
47 D3DKMT_WAITFORVERTICALBLANKEVENT*); 51 D3DKMT_WAITFORVERTICALBLANKEVENT*);
48 } // namespace 52 } // namespace
49 53
50 // The actual implementation of background tasks plus any state that might be 54 // The actual implementation of background tasks plus any state that might be
51 // needed on the worker thread. 55 // needed on the worker thread.
52 class GpuVSyncWorker : public base::Thread { 56 class GpuVSyncWorker : public base::Thread,
57 public base::RefCountedThreadSafe<GpuVSyncWorker> {
53 public: 58 public:
54 GpuVSyncWorker(const GpuVSyncProvider::VSyncCallback& callback, 59 GpuVSyncWorker(const gfx::VSyncProvider::UpdateVSyncCallback& callback,
55 SurfaceHandle surface_handle); 60 SurfaceHandle surface_handle);
56 ~GpuVSyncWorker() override;
57 61
62 void CleanupAndStop();
58 void Enable(bool enabled); 63 void Enable(bool enabled);
59 void StartRunningVSyncOnThread(); 64 void StartRunningVSyncOnThread();
60 void WaitForVSyncOnThread(); 65 void WaitForVSyncOnThread();
61 void SendVSyncUpdate(base::TimeTicks timestamp); 66 void SendVSyncUpdate(base::TimeTicks now,
67 base::TimeTicks timestamp,
68 base::TimeDelta interval);
69 bool BelongsToWorkerThread();
62 70
63 private: 71 private:
72 friend class base::RefCountedThreadSafe<GpuVSyncWorker>;
73 ~GpuVSyncWorker() override;
74
64 void Reschedule(); 75 void Reschedule();
65 void OpenAdapter(const wchar_t* device_name); 76 void OpenAdapter(const wchar_t* device_name);
66 void CloseAdapter(); 77 void CloseAdapter();
67 bool WaitForVBlankEvent(); 78 bool WaitForVBlankEvent();
68 79
69 // Specifies whether background tasks are running. 80 // Specifies whether background tasks are running.
70 // This can be set on background thread only. 81 // This can be set on background thread only.
71 bool running_ = false; 82 bool running_ = false;
72 83
73 // Specified whether the worker is enabled. This is accessed from both 84 // Specified whether the worker is enabled. This is accessed from both
74 // threads but can be changed on the main thread only. 85 // threads but can be changed on the main thread only.
75 base::subtle::AtomicWord enabled_ = false; 86 base::subtle::AtomicWord enabled_ = false;
76 87
77 const GpuVSyncProvider::VSyncCallback callback_; 88 const gfx::VSyncProvider::UpdateVSyncCallback callback_;
78 const SurfaceHandle surface_handle_; 89 const SurfaceHandle surface_handle_;
79 90
91 // The actual timing and interval comes from the nested provider.
92 std::unique_ptr<gl::VSyncProviderWin> vsync_provider_;
93
80 PFND3DKMTOPENADAPTERFROMHDC open_adapter_from_hdc_ptr_; 94 PFND3DKMTOPENADAPTERFROMHDC open_adapter_from_hdc_ptr_;
81 PFND3DKMTCLOSEADAPTER close_adapter_ptr_; 95 PFND3DKMTCLOSEADAPTER close_adapter_ptr_;
82 PFND3DKMTWAITFORVERTICALBLANKEVENT wait_for_vertical_blank_event_ptr_; 96 PFND3DKMTWAITFORVERTICALBLANKEVENT wait_for_vertical_blank_event_ptr_;
83 97
84 std::wstring current_device_name_; 98 std::wstring current_device_name_;
85 D3DKMT_HANDLE current_adapter_handle_ = 0; 99 D3DKMT_HANDLE current_adapter_handle_ = 0;
86 D3DDDI_VIDEO_PRESENT_SOURCE_ID current_source_id_ = 0; 100 D3DDDI_VIDEO_PRESENT_SOURCE_ID current_source_id_ = 0;
87 }; 101 };
88 102
89 GpuVSyncWorker::GpuVSyncWorker(const GpuVSyncProvider::VSyncCallback& callback, 103 GpuVSyncWorker::GpuVSyncWorker(
90 SurfaceHandle surface_handle) 104 const gfx::VSyncProvider::UpdateVSyncCallback& callback,
105 SurfaceHandle surface_handle)
91 : base::Thread(base::StringPrintf("VSync-%d", surface_handle)), 106 : base::Thread(base::StringPrintf("VSync-%d", surface_handle)),
92 callback_(callback), 107 callback_(callback),
93 surface_handle_(surface_handle) { 108 surface_handle_(surface_handle),
109 vsync_provider_(new gl::VSyncProviderWin(surface_handle)) {
94 HMODULE gdi32 = GetModuleHandle(L"gdi32"); 110 HMODULE gdi32 = GetModuleHandle(L"gdi32");
95 if (!gdi32) { 111 if (!gdi32) {
96 NOTREACHED() << "Can't open gdi32.dll"; 112 NOTREACHED() << "Can't open gdi32.dll";
97 return; 113 return;
98 } 114 }
99 115
100 open_adapter_from_hdc_ptr_ = reinterpret_cast<PFND3DKMTOPENADAPTERFROMHDC>( 116 open_adapter_from_hdc_ptr_ = reinterpret_cast<PFND3DKMTOPENADAPTERFROMHDC>(
101 ::GetProcAddress(gdi32, "D3DKMTOpenAdapterFromHdc")); 117 ::GetProcAddress(gdi32, "D3DKMTOpenAdapterFromHdc"));
102 if (!open_adapter_from_hdc_ptr_) { 118 if (!open_adapter_from_hdc_ptr_) {
103 NOTREACHED() << "Can't find D3DKMTOpenAdapterFromHdc in gdi32.dll"; 119 NOTREACHED() << "Can't find D3DKMTOpenAdapterFromHdc in gdi32.dll";
104 return; 120 return;
105 } 121 }
106 122
107 close_adapter_ptr_ = reinterpret_cast<PFND3DKMTCLOSEADAPTER>( 123 close_adapter_ptr_ = reinterpret_cast<PFND3DKMTCLOSEADAPTER>(
108 ::GetProcAddress(gdi32, "D3DKMTCloseAdapter")); 124 ::GetProcAddress(gdi32, "D3DKMTCloseAdapter"));
109 if (!close_adapter_ptr_) { 125 if (!close_adapter_ptr_) {
110 NOTREACHED() << "Can't find D3DKMTCloseAdapter in gdi32.dll"; 126 NOTREACHED() << "Can't find D3DKMTCloseAdapter in gdi32.dll";
111 return; 127 return;
112 } 128 }
113 129
114 wait_for_vertical_blank_event_ptr_ = 130 wait_for_vertical_blank_event_ptr_ =
115 reinterpret_cast<PFND3DKMTWAITFORVERTICALBLANKEVENT>( 131 reinterpret_cast<PFND3DKMTWAITFORVERTICALBLANKEVENT>(
116 ::GetProcAddress(gdi32, "D3DKMTWaitForVerticalBlankEvent")); 132 ::GetProcAddress(gdi32, "D3DKMTWaitForVerticalBlankEvent"));
117 if (!wait_for_vertical_blank_event_ptr_) { 133 if (!wait_for_vertical_blank_event_ptr_) {
118 NOTREACHED() << "Can't find D3DKMTWaitForVerticalBlankEvent in gdi32.dll"; 134 NOTREACHED() << "Can't find D3DKMTWaitForVerticalBlankEvent in gdi32.dll";
119 return; 135 return;
120 } 136 }
121 } 137 }
122 138
123 GpuVSyncWorker::~GpuVSyncWorker() { 139 GpuVSyncWorker::~GpuVSyncWorker() = default;
140
141 void GpuVSyncWorker::CleanupAndStop() {
142 Enable(false);
124 // Thread::Close() call below will block until this task has finished running 143 // Thread::Close() call below will block until this task has finished running
125 // so it is safe to post it here and pass unretained pointer. 144 // so it is safe to post it here and pass unretained pointer.
126 task_runner()->PostTask(FROM_HERE, base::Bind(&GpuVSyncWorker::CloseAdapter, 145 task_runner()->PostTask(FROM_HERE, base::Bind(&GpuVSyncWorker::CloseAdapter,
127 base::Unretained(this))); 146 base::Unretained(this)));
128 Stop(); 147 Stop();
129 148
130 DCHECK_EQ(0u, current_adapter_handle_); 149 DCHECK_EQ(0u, current_adapter_handle_);
131 DCHECK(current_device_name_.empty()); 150 DCHECK(current_device_name_.empty());
132 } 151 }
133 152
134 void GpuVSyncWorker::Enable(bool enabled) { 153 void GpuVSyncWorker::Enable(bool enabled) {
135 auto was_enabled = base::subtle::NoBarrier_AtomicExchange(&enabled_, enabled); 154 auto was_enabled = base::subtle::NoBarrier_AtomicExchange(&enabled_, enabled);
136 155
137 if (enabled && !was_enabled) 156 if (enabled && !was_enabled)
138 task_runner()->PostTask( 157 task_runner()->PostTask(
139 FROM_HERE, base::Bind(&GpuVSyncWorker::StartRunningVSyncOnThread, 158 FROM_HERE, base::Bind(&GpuVSyncWorker::StartRunningVSyncOnThread,
140 base::Unretained(this))); 159 base::Unretained(this)));
141 } 160 }
142 161
162 bool GpuVSyncWorker::BelongsToWorkerThread() {
163 return base::PlatformThread::CurrentId() == GetThreadId();
164 }
165
143 void GpuVSyncWorker::StartRunningVSyncOnThread() { 166 void GpuVSyncWorker::StartRunningVSyncOnThread() {
144 DCHECK(base::PlatformThread::CurrentId() == GetThreadId()); 167 DCHECK(BelongsToWorkerThread());
145 168
146 if (!running_) { 169 if (!running_) {
147 running_ = true; 170 running_ = true;
148 WaitForVSyncOnThread(); 171 WaitForVSyncOnThread();
149 } 172 }
150 } 173 }
151 174
152 void GpuVSyncWorker::WaitForVSyncOnThread() { 175 void GpuVSyncWorker::WaitForVSyncOnThread() {
153 DCHECK(base::PlatformThread::CurrentId() == GetThreadId()); 176 DCHECK(BelongsToWorkerThread());
154 177
155 TRACE_EVENT0("gpu", "GpuVSyncWorker::WaitForVSyncOnThread"); 178 TRACE_EVENT0("gpu", "GpuVSyncWorker::WaitForVSyncOnThread");
156 179
157 HMONITOR monitor = 180 HMONITOR monitor =
158 MonitorFromWindow(surface_handle_, MONITOR_DEFAULTTONEAREST); 181 MonitorFromWindow(surface_handle_, MONITOR_DEFAULTTONEAREST);
159 MONITORINFOEX monitor_info; 182 MONITORINFOEX monitor_info;
160 monitor_info.cbSize = sizeof(MONITORINFOEX); 183 monitor_info.cbSize = sizeof(MONITORINFOEX);
161 BOOL success = GetMonitorInfo(monitor, &monitor_info); 184 BOOL success = GetMonitorInfo(monitor, &monitor_info);
162 CHECK(success); 185 CHECK(success);
163 186
164 if (current_device_name_.compare(monitor_info.szDevice) != 0) { 187 if (current_device_name_.compare(monitor_info.szDevice) != 0) {
165 // Monitor changed. Close the current adapter handle and open a new one. 188 // Monitor changed. Close the current adapter handle and open a new one.
166 CloseAdapter(); 189 CloseAdapter();
167 OpenAdapter(monitor_info.szDevice); 190 OpenAdapter(monitor_info.szDevice);
168 } 191 }
169 192
170 if (WaitForVBlankEvent()) { 193 if (WaitForVBlankEvent()) {
171 // Note: this sends update on background thread which the callback is 194 vsync_provider_->GetVSyncParameters(
172 // expected to handle. 195 base::Bind(&GpuVSyncWorker::SendVSyncUpdate, base::Unretained(this),
173 SendVSyncUpdate(base::TimeTicks::Now()); 196 base::TimeTicks::Now()));
174 } 197 }
175 198
176 Reschedule(); 199 Reschedule();
177 } 200 }
178 201
179 void GpuVSyncWorker::SendVSyncUpdate(base::TimeTicks timestamp) { 202 void GpuVSyncWorker::SendVSyncUpdate(base::TimeTicks now,
203 base::TimeTicks timestamp,
204 base::TimeDelta interval) {
205 base::TimeDelta adjustment;
206
207 if (!(timestamp.is_null() || interval.is_zero())) {
208 // Timestamp comes from DwmGetCompositionTimingInfo and apparently it might
209 // be up to 2-3 vsync cycles in the past or in the future.
210 // The adjustment formula was suggested here:
211 // http://www.vsynctester.com/firefoxisbroken.html
212 base::TimeDelta adjustment =
213 ((now - timestamp + interval / 8) % interval + interval) % interval -
214 interval / 8;
215 timestamp = now - adjustment;
216 } else {
217 // DWM must be disabled.
218 timestamp = now;
219 }
220
221 TRACE_EVENT1("gpu", "GpuVSyncWorker::SendVSyncUpdate", "adjustment",
222 adjustment.ToInternalValue());
223
180 if (base::subtle::NoBarrier_Load(&enabled_)) { 224 if (base::subtle::NoBarrier_Load(&enabled_)) {
181 TRACE_EVENT0("gpu", "GpuVSyncWorker::SendVSyncUpdate"); 225 callback_.Run(timestamp, interval);
182 callback_.Run(timestamp);
183 } 226 }
184 } 227 }
185 228
186 void GpuVSyncWorker::Reschedule() { 229 void GpuVSyncWorker::Reschedule() {
187 // Restart the task if still enabled. 230 // Restart the task if still enabled.
188 if (base::subtle::NoBarrier_Load(&enabled_)) { 231 if (base::subtle::NoBarrier_Load(&enabled_)) {
189 task_runner()->PostTask(FROM_HERE, 232 task_runner()->PostTask(FROM_HERE,
190 base::Bind(&GpuVSyncWorker::WaitForVSyncOnThread, 233 base::Bind(&GpuVSyncWorker::WaitForVSyncOnThread,
191 base::Unretained(this))); 234 base::Unretained(this)));
192 } else { 235 } else {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
230 wait_for_vertical_blank_event_data.hAdapter = current_adapter_handle_; 273 wait_for_vertical_blank_event_data.hAdapter = current_adapter_handle_;
231 wait_for_vertical_blank_event_data.hDevice = 0; 274 wait_for_vertical_blank_event_data.hDevice = 0;
232 wait_for_vertical_blank_event_data.VidPnSourceId = current_source_id_; 275 wait_for_vertical_blank_event_data.VidPnSourceId = current_source_id_;
233 276
234 NTSTATUS result = 277 NTSTATUS result =
235 wait_for_vertical_blank_event_ptr_(&wait_for_vertical_blank_event_data); 278 wait_for_vertical_blank_event_ptr_(&wait_for_vertical_blank_event_data);
236 279
237 return result == STATUS_SUCCESS; 280 return result == STATUS_SUCCESS;
238 } 281 }
239 282
240 /* static */ 283 // MessageFilter class for sending and receiving IPC messages
241 std::unique_ptr<GpuVSyncProvider> GpuVSyncProvider::Create( 284 // directly, avoiding routing them through the main GPU thread.
242 const VSyncCallback& callback, 285 class GpuVSyncMessageFilter : public IPC::MessageFilter {
243 SurfaceHandle surface_handle) { 286 public:
244 return std::unique_ptr<GpuVSyncProvider>( 287 explicit GpuVSyncMessageFilter(
245 new GpuVSyncProvider(callback, surface_handle)); 288 const scoped_refptr<GpuVSyncWorker>& vsync_worker,
289 int32_t route_id)
290 : vsync_worker_(vsync_worker), route_id_(route_id) {}
291
292 // IPC::MessageFilter overrides.
293 void OnChannelError() override { Reset(); }
294 void OnChannelClosing() override { Reset(); }
295 void OnFilterAdded(IPC::Channel* channel) override;
296 void OnFilterRemoved() override { Reset(); }
297 bool OnMessageReceived(const IPC::Message& msg) override;
298
299 // Send can be called from GpuVSyncWorker thread.
300 void Send(std::unique_ptr<IPC::Message> message);
301
302 int32_t route_id() const { return route_id_; }
303
304 private:
305 ~GpuVSyncMessageFilter() override = default;
306 void SendOnIOThread(std::unique_ptr<IPC::Message> message);
307 void Reset();
308
309 scoped_refptr<GpuVSyncWorker> vsync_worker_;
310 // The sender to which this filter was added.
311 IPC::Sender* sender_ = nullptr;
312 // The sender must be invoked on IO thread.
313 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
314 const int32_t route_id_;
315 };
316
317 void GpuVSyncMessageFilter::OnFilterAdded(IPC::Channel* channel) {
318 io_task_runner_ = base::ThreadTaskRunnerHandle::Get();
319 sender_ = channel;
246 } 320 }
247 321
248 GpuVSyncProvider::GpuVSyncProvider(const VSyncCallback& callback, 322 void GpuVSyncMessageFilter::Reset() {
249 SurfaceHandle surface_handle) 323 sender_ = nullptr;
250 : vsync_worker_(new GpuVSyncWorker(callback, surface_handle)) { 324 vsync_worker_->Enable(false);
325 }
326
327 bool GpuVSyncMessageFilter::OnMessageReceived(const IPC::Message& msg) {
328 if (msg.routing_id() != route_id_)
329 return false;
330
331 IPC_BEGIN_MESSAGE_MAP(GpuVSyncMessageFilter, msg)
332 IPC_MESSAGE_FORWARD(GpuCommandBufferMsg_SetNeedsVSync, vsync_worker_.get(),
333 GpuVSyncWorker::Enable);
334 IPC_MESSAGE_UNHANDLED(return false)
335 IPC_END_MESSAGE_MAP()
336 return true;
337 }
338
339 void GpuVSyncMessageFilter::Send(std::unique_ptr<IPC::Message> message) {
340 io_task_runner_->PostTask(
341 FROM_HERE, base::Bind(&GpuVSyncMessageFilter::SendOnIOThread, this,
342 base::Passed(&message)));
343 }
344
345 void GpuVSyncMessageFilter::SendOnIOThread(
346 std::unique_ptr<IPC::Message> message) {
347 DCHECK(io_task_runner_->BelongsToCurrentThread());
348 DCHECK(!message->is_sync());
349 if (!sender_)
350 return;
351
352 sender_->Send(message.release());
353 }
354
355 GpuVSyncProviderWin::GpuVSyncProviderWin(
356 base::WeakPtr<ImageTransportSurfaceDelegate> delegate,
357 SurfaceHandle surface_handle) {
358 vsync_worker_ = new GpuVSyncWorker(
359 base::Bind(&GpuVSyncProviderWin::OnVSync, base::Unretained(this)),
360 surface_handle);
361 message_filter_ =
362 new GpuVSyncMessageFilter(vsync_worker_, delegate->GetRouteID());
363 delegate->AddFilter(message_filter_.get());
364
251 // Start the thread. 365 // Start the thread.
252 base::Thread::Options options; 366 base::Thread::Options options;
253 // TODO(stanisc): might consider even higher priority - REALTIME_AUDIO. 367 // TODO(stanisc): might consider even higher priority - REALTIME_AUDIO.
254 options.priority = base::ThreadPriority::DISPLAY; 368 options.priority = base::ThreadPriority::DISPLAY;
255 vsync_worker_->StartWithOptions(options); 369 vsync_worker_->StartWithOptions(options);
256 } 370 }
257 371
258 GpuVSyncProvider::~GpuVSyncProvider() = default; 372 GpuVSyncProviderWin::~GpuVSyncProviderWin() {
373 vsync_worker_->CleanupAndStop();
374 }
259 375
260 void GpuVSyncProvider::EnableVSync(bool enabled) { 376 void GpuVSyncProviderWin::GetVSyncParameters(
261 vsync_worker_->Enable(enabled); 377 const UpdateVSyncCallback& callback) {
378 // This is ignored and the |callback| is never called back. The timestamp
379 // and interval are posted directly via
380 // GpuCommandBufferMsg_UpdateVSyncParameters message sent from the worker
381 // thread.
382 }
383
384 void GpuVSyncProviderWin::OnVSync(base::TimeTicks timestamp,
385 base::TimeDelta interval) {
386 DCHECK(vsync_worker_->BelongsToWorkerThread());
387
388 message_filter_->Send(
389 base::MakeUnique<GpuCommandBufferMsg_UpdateVSyncParameters>(
390 message_filter_->route_id(), timestamp, interval));
262 } 391 }
263 392
264 } // namespace gpu 393 } // namespace gpu
OLDNEW
« no previous file with comments | « gpu/ipc/service/gpu_vsync_provider_win.h ('k') | gpu/ipc/service/image_transport_surface_delegate.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698