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

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

Issue 2681033011: Changed GpuVSyncProvider to implement gfx::VSyncProvider (Closed)
Patch Set: Fixed clang build errors 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() {
124 // Thread::Close() call below will block until this task has finished running 142 // 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. 143 // so it is safe to post it here and pass unretained pointer.
126 task_runner()->PostTask(FROM_HERE, base::Bind(&GpuVSyncWorker::CloseAdapter, 144 task_runner()->PostTask(FROM_HERE, base::Bind(&GpuVSyncWorker::CloseAdapter,
127 base::Unretained(this))); 145 base::Unretained(this)));
128 Stop(); 146 Stop();
129 147
130 DCHECK_EQ(0u, current_adapter_handle_); 148 DCHECK_EQ(0u, current_adapter_handle_);
131 DCHECK(current_device_name_.empty()); 149 DCHECK(current_device_name_.empty());
132 } 150 }
133 151
134 void GpuVSyncWorker::Enable(bool enabled) { 152 void GpuVSyncWorker::Enable(bool enabled) {
135 auto was_enabled = base::subtle::NoBarrier_AtomicExchange(&enabled_, enabled); 153 auto was_enabled = base::subtle::NoBarrier_AtomicExchange(&enabled_, enabled);
136 154
137 if (enabled && !was_enabled) 155 if (enabled && !was_enabled)
138 task_runner()->PostTask( 156 task_runner()->PostTask(
139 FROM_HERE, base::Bind(&GpuVSyncWorker::StartRunningVSyncOnThread, 157 FROM_HERE, base::Bind(&GpuVSyncWorker::StartRunningVSyncOnThread,
140 base::Unretained(this))); 158 base::Unretained(this)));
141 } 159 }
142 160
161 bool GpuVSyncWorker::BelongsToWorkerThread() {
162 return base::PlatformThread::CurrentId() == GetThreadId();
163 }
164
143 void GpuVSyncWorker::StartRunningVSyncOnThread() { 165 void GpuVSyncWorker::StartRunningVSyncOnThread() {
144 DCHECK(base::PlatformThread::CurrentId() == GetThreadId()); 166 DCHECK(BelongsToWorkerThread());
145 167
146 if (!running_) { 168 if (!running_) {
147 running_ = true; 169 running_ = true;
148 WaitForVSyncOnThread(); 170 WaitForVSyncOnThread();
149 } 171 }
150 } 172 }
151 173
152 void GpuVSyncWorker::WaitForVSyncOnThread() { 174 void GpuVSyncWorker::WaitForVSyncOnThread() {
153 DCHECK(base::PlatformThread::CurrentId() == GetThreadId()); 175 DCHECK(BelongsToWorkerThread());
154 176
155 TRACE_EVENT0("gpu", "GpuVSyncWorker::WaitForVSyncOnThread"); 177 TRACE_EVENT0("gpu", "GpuVSyncWorker::WaitForVSyncOnThread");
156 178
157 HMONITOR monitor = 179 HMONITOR monitor =
158 MonitorFromWindow(surface_handle_, MONITOR_DEFAULTTONEAREST); 180 MonitorFromWindow(surface_handle_, MONITOR_DEFAULTTONEAREST);
159 MONITORINFOEX monitor_info; 181 MONITORINFOEX monitor_info;
160 monitor_info.cbSize = sizeof(MONITORINFOEX); 182 monitor_info.cbSize = sizeof(MONITORINFOEX);
161 BOOL success = GetMonitorInfo(monitor, &monitor_info); 183 BOOL success = GetMonitorInfo(monitor, &monitor_info);
162 CHECK(success); 184 CHECK(success);
163 185
164 if (current_device_name_.compare(monitor_info.szDevice) != 0) { 186 if (current_device_name_.compare(monitor_info.szDevice) != 0) {
165 // Monitor changed. Close the current adapter handle and open a new one. 187 // Monitor changed. Close the current adapter handle and open a new one.
166 CloseAdapter(); 188 CloseAdapter();
167 OpenAdapter(monitor_info.szDevice); 189 OpenAdapter(monitor_info.szDevice);
168 } 190 }
169 191
170 if (WaitForVBlankEvent()) { 192 if (WaitForVBlankEvent()) {
171 // Note: this sends update on background thread which the callback is 193 vsync_provider_->GetVSyncParameters(
172 // expected to handle. 194 base::Bind(&GpuVSyncWorker::SendVSyncUpdate, base::Unretained(this),
173 SendVSyncUpdate(base::TimeTicks::Now()); 195 base::TimeTicks::Now()));
174 } 196 }
175 197
176 Reschedule(); 198 Reschedule();
177 } 199 }
178 200
179 void GpuVSyncWorker::SendVSyncUpdate(base::TimeTicks timestamp) { 201 void GpuVSyncWorker::SendVSyncUpdate(base::TimeTicks now,
202 base::TimeTicks timestamp,
203 base::TimeDelta interval) {
204 base::TimeDelta adjustment;
205
206 if (!timestamp.is_null()) {
207 // Timestamp comes from DwmGetCompositionTimingInfo and apparently it might
208 // be up to 2-3 vsync cycles in the past or in the future.
209 // The adjustment formula was suggested here:
210 // http://www.vsynctester.com/firefoxisbroken.html
211 base::TimeDelta adjustment =
212 ((now - timestamp + interval / 8) % interval + interval) % interval -
213 interval / 8;
214 timestamp = now - adjustment;
215 } else {
216 // DWM must be disabled.
217 timestamp = now;
218 }
219
220 TRACE_EVENT1("gpu", "GpuVSyncWorker::SendVSyncUpdate", "adjustment",
221 adjustment.ToInternalValue());
222
180 if (base::subtle::NoBarrier_Load(&enabled_)) { 223 if (base::subtle::NoBarrier_Load(&enabled_)) {
181 TRACE_EVENT0("gpu", "GpuVSyncWorker::SendVSyncUpdate"); 224 callback_.Run(timestamp, interval);
182 callback_.Run(timestamp);
183 } 225 }
184 } 226 }
185 227
186 void GpuVSyncWorker::Reschedule() { 228 void GpuVSyncWorker::Reschedule() {
187 // Restart the task if still enabled. 229 // Restart the task if still enabled.
188 if (base::subtle::NoBarrier_Load(&enabled_)) { 230 if (base::subtle::NoBarrier_Load(&enabled_)) {
189 task_runner()->PostTask(FROM_HERE, 231 task_runner()->PostTask(FROM_HERE,
190 base::Bind(&GpuVSyncWorker::WaitForVSyncOnThread, 232 base::Bind(&GpuVSyncWorker::WaitForVSyncOnThread,
191 base::Unretained(this))); 233 base::Unretained(this)));
192 } else { 234 } else {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
230 wait_for_vertical_blank_event_data.hAdapter = current_adapter_handle_; 272 wait_for_vertical_blank_event_data.hAdapter = current_adapter_handle_;
231 wait_for_vertical_blank_event_data.hDevice = 0; 273 wait_for_vertical_blank_event_data.hDevice = 0;
232 wait_for_vertical_blank_event_data.VidPnSourceId = current_source_id_; 274 wait_for_vertical_blank_event_data.VidPnSourceId = current_source_id_;
233 275
234 NTSTATUS result = 276 NTSTATUS result =
235 wait_for_vertical_blank_event_ptr_(&wait_for_vertical_blank_event_data); 277 wait_for_vertical_blank_event_ptr_(&wait_for_vertical_blank_event_data);
236 278
237 return result == STATUS_SUCCESS; 279 return result == STATUS_SUCCESS;
238 } 280 }
239 281
240 /* static */ 282 // MessageFilter class for sending and receiving IPC messages
241 std::unique_ptr<GpuVSyncProvider> GpuVSyncProvider::Create( 283 // directly, avoiding routing them through the main GPU thread.
242 const VSyncCallback& callback, 284 class GpuVSyncMessageFilter : public IPC::MessageFilter {
243 SurfaceHandle surface_handle) { 285 public:
244 return std::unique_ptr<GpuVSyncProvider>( 286 explicit GpuVSyncMessageFilter(
245 new GpuVSyncProvider(callback, surface_handle)); 287 const scoped_refptr<GpuVSyncWorker>& vsync_worker,
288 int32_t route_id)
289 : vsync_worker_(vsync_worker), route_id_(route_id) {}
290
291 // IPC::MessageFilter overrides.
292 void OnChannelError() override { Reset(); }
293 void OnChannelClosing() override { Reset(); }
294 void OnFilterAdded(IPC::Channel* channel) override;
295 void OnFilterRemoved() override { Reset(); }
296 bool OnMessageReceived(const IPC::Message& msg) override;
297
298 // Send can be called from GpuVSyncWorker thread.
299 void Send(std::unique_ptr<IPC::Message> message);
300
301 int32_t route_id() const { return route_id_; }
302
303 private:
304 ~GpuVSyncMessageFilter() override = default;
305 void SendOnIOThread(std::unique_ptr<IPC::Message> message);
306 void Reset();
307
308 scoped_refptr<GpuVSyncWorker> vsync_worker_;
309 // The sender to which this filter was added.
310 IPC::Sender* sender_ = nullptr;
311 // The sender must be invoked on IO thread.
312 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
313 int32_t route_id_;
314 };
315
316 void GpuVSyncMessageFilter::OnFilterAdded(IPC::Channel* channel) {
317 io_task_runner_ = base::ThreadTaskRunnerHandle::Get();
318 sender_ = channel;
246 } 319 }
247 320
248 GpuVSyncProvider::GpuVSyncProvider(const VSyncCallback& callback, 321 void GpuVSyncMessageFilter::Reset() {
249 SurfaceHandle surface_handle) 322 sender_ = nullptr;
250 : vsync_worker_(new GpuVSyncWorker(callback, surface_handle)) { 323 vsync_worker_->Enable(false);
324 }
325
326 bool GpuVSyncMessageFilter::OnMessageReceived(const IPC::Message& msg) {
327 if (msg.routing_id() != route_id_)
328 return false;
329
330 IPC_BEGIN_MESSAGE_MAP(GpuVSyncMessageFilter, msg)
331 IPC_MESSAGE_FORWARD(GpuCommandBufferMsg_SetNeedsVSync, vsync_worker_.get(),
332 GpuVSyncWorker::Enable);
333 IPC_MESSAGE_UNHANDLED(return false)
334 IPC_END_MESSAGE_MAP()
335 return true;
336 }
337
338 void GpuVSyncMessageFilter::Send(std::unique_ptr<IPC::Message> message) {
339 CHECK(io_task_runner_->PostTask(
340 FROM_HERE, base::Bind(&GpuVSyncMessageFilter::SendOnIOThread, this,
341 base::Passed(&message))));
342 }
343
344 void GpuVSyncMessageFilter::SendOnIOThread(
345 std::unique_ptr<IPC::Message> message) {
346 DCHECK(io_task_runner_->BelongsToCurrentThread());
347 if (!sender_ || message->is_sync()) {
348 DCHECK(!message->is_sync());
349 return;
350 }
351 sender_->Send(message.release());
352 }
353
354 GpuVSyncProviderWin::GpuVSyncProviderWin(
355 base::WeakPtr<ImageTransportSurfaceDelegate> delegate,
356 SurfaceHandle surface_handle) {
357 vsync_worker_ = new GpuVSyncWorker(
358 base::Bind(&GpuVSyncProviderWin::OnVSync, base::Unretained(this)),
359 surface_handle);
360 message_filter_ =
361 new GpuVSyncMessageFilter(vsync_worker_, delegate->GetRouteID());
362 delegate->AddFilter(message_filter_.get());
363
251 // Start the thread. 364 // Start the thread.
252 base::Thread::Options options; 365 base::Thread::Options options;
253 // TODO(stanisc): might consider even higher priority - REALTIME_AUDIO. 366 // TODO(stanisc): might consider even higher priority - REALTIME_AUDIO.
254 options.priority = base::ThreadPriority::DISPLAY; 367 options.priority = base::ThreadPriority::DISPLAY;
255 vsync_worker_->StartWithOptions(options); 368 vsync_worker_->StartWithOptions(options);
256 } 369 }
257 370
258 GpuVSyncProvider::~GpuVSyncProvider() = default; 371 GpuVSyncProviderWin::~GpuVSyncProviderWin() {
372 vsync_worker_->CleanupAndStop();
373 }
259 374
260 void GpuVSyncProvider::EnableVSync(bool enabled) { 375 void GpuVSyncProviderWin::GetVSyncParameters(
261 vsync_worker_->Enable(enabled); 376 const UpdateVSyncCallback& callback) {
377 // This is ignored and the |callback| is never called back. The timestamp
378 // and interval are posted directly via
379 // GpuCommandBufferMsg_UpdateVSyncParameters message sent from the worker
380 // thread.
381 }
382
383 void GpuVSyncProviderWin::OnVSync(base::TimeTicks timestamp,
384 base::TimeDelta interval) {
385 DCHECK(vsync_worker_->BelongsToWorkerThread());
386
387 message_filter_->Send(std::unique_ptr<IPC::Message>(
dcheng 2017/02/13 21:10:39 Nit: base::MakeUnique
stanisc 2017/02/14 00:42:35 Done.
388 new GpuCommandBufferMsg_UpdateVSyncParameters(message_filter_->route_id(),
389 timestamp, interval)));
262 } 390 }
263 391
264 } // namespace gpu 392 } // 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