OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "gpu/ipc/service/gpu_vsync_provider.h" | |
6 | |
7 #include "base/atomicops.h" | |
8 #include "base/strings/stringprintf.h" | |
9 #include "base/threading/thread.h" | |
10 #include "base/trace_event/trace_event.h" | |
11 | |
12 #include <windows.h> | |
13 | |
14 namespace gpu { | |
15 | |
16 namespace { | |
17 // from <D3dkmthk.h> | |
18 typedef LONG NTSTATUS; | |
19 typedef UINT D3DKMT_HANDLE; | |
20 typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID; | |
21 | |
22 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) | |
23 | |
24 typedef struct _D3DKMT_OPENADAPTERFROMHDC { | |
25 HDC hDc; | |
26 D3DKMT_HANDLE hAdapter; | |
27 LUID AdapterLuid; | |
28 D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId; | |
29 } D3DKMT_OPENADAPTERFROMHDC; | |
30 | |
31 typedef struct _D3DKMT_CLOSEADAPTER { | |
32 D3DKMT_HANDLE hAdapter; | |
33 } D3DKMT_CLOSEADAPTER; | |
34 | |
35 typedef struct _D3DKMT_WAITFORVERTICALBLANKEVENT { | |
36 D3DKMT_HANDLE hAdapter; | |
37 D3DKMT_HANDLE hDevice; | |
38 D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId; | |
39 } D3DKMT_WAITFORVERTICALBLANKEVENT; | |
40 | |
41 typedef NTSTATUS(APIENTRY* PFND3DKMTOPENADAPTERFROMHDC)( | |
42 D3DKMT_OPENADAPTERFROMHDC*); | |
43 typedef NTSTATUS(APIENTRY* PFND3DKMTCLOSEADAPTER)(D3DKMT_CLOSEADAPTER*); | |
44 typedef NTSTATUS(APIENTRY* PFND3DKMTWAITFORVERTICALBLANKEVENT)( | |
45 D3DKMT_WAITFORVERTICALBLANKEVENT*); | |
46 } // namespace | |
47 | |
48 // The actual implementation of background tasks plus any state that might be | |
49 // needed on the worker thread. | |
50 class GpuVSyncWorker : public base::Thread { | |
51 public: | |
52 GpuVSyncWorker(const GpuVSyncProvider::VSyncCallback& callback, | |
53 SurfaceHandle surface_handle); | |
54 ~GpuVSyncWorker() override; | |
55 | |
56 void Enable(bool enabled); | |
57 void StartRunningVSyncOnThread(); | |
58 void WaitForVSyncOnThread(); | |
59 void SendVSyncUpdate(base::TimeTicks timestamp); | |
60 void Reschedule(); | |
61 | |
62 private: | |
63 // Specifies whether background tasks are running. | |
64 // This can be set on background thread only. | |
65 bool running_ = false; | |
66 | |
67 // Specified whether the worker is enabled. This is accessed from both | |
68 // threads but can be changed on the main thread only. | |
69 base::subtle::AtomicWord enabled_ = false; | |
70 | |
71 const GpuVSyncProvider::VSyncCallback callback_; | |
72 const SurfaceHandle surface_handle_; | |
73 | |
74 PFND3DKMTOPENADAPTERFROMHDC open_adapter_from_hdc_ptr_; | |
75 PFND3DKMTCLOSEADAPTER close_adapter_ptr_; | |
76 PFND3DKMTWAITFORVERTICALBLANKEVENT wait_for_vertical_blank_event_ptr_; | |
77 }; | |
78 | |
79 GpuVSyncWorker::GpuVSyncWorker(const GpuVSyncProvider::VSyncCallback& callback, | |
80 SurfaceHandle surface_handle) | |
81 : base::Thread(base::StringPrintf("VSync-%d", surface_handle)), | |
82 callback_(callback), | |
83 surface_handle_(surface_handle) { | |
84 HMODULE gdi32 = GetModuleHandle(L"gdi32"); | |
85 if (!gdi32) { | |
86 NOTREACHED() << "Can't open gdi32.dll"; | |
87 return; | |
88 } | |
89 | |
90 open_adapter_from_hdc_ptr_ = reinterpret_cast<PFND3DKMTOPENADAPTERFROMHDC>( | |
91 ::GetProcAddress(gdi32, "D3DKMTOpenAdapterFromHdc")); | |
92 if (!open_adapter_from_hdc_ptr_) { | |
93 NOTREACHED() << "Can't find D3DKMTOpenAdapterFromHdc in gdi32.dll"; | |
94 return; | |
95 } | |
96 | |
97 close_adapter_ptr_ = reinterpret_cast<PFND3DKMTCLOSEADAPTER>( | |
98 ::GetProcAddress(gdi32, "D3DKMTCloseAdapter")); | |
99 if (!close_adapter_ptr_) { | |
100 NOTREACHED() << "Can't find D3DKMTCloseAdapter in gdi32.dll"; | |
101 return; | |
102 } | |
103 | |
104 wait_for_vertical_blank_event_ptr_ = | |
105 reinterpret_cast<PFND3DKMTWAITFORVERTICALBLANKEVENT>( | |
106 ::GetProcAddress(gdi32, "D3DKMTWaitForVerticalBlankEvent")); | |
107 if (!wait_for_vertical_blank_event_ptr_) { | |
108 NOTREACHED() << "Can't find D3DKMTWaitForVerticalBlankEvent in gdi32.dll"; | |
109 return; | |
110 } | |
111 } | |
112 | |
113 GpuVSyncWorker::~GpuVSyncWorker() { | |
114 Stop(); | |
115 } | |
116 | |
117 void GpuVSyncWorker::Enable(bool enabled) { | |
118 auto was_enabled = base::subtle::NoBarrier_AtomicExchange(&enabled_, enabled); | |
119 | |
120 if (enabled && !was_enabled) | |
121 task_runner()->PostTask( | |
122 FROM_HERE, base::Bind(&GpuVSyncWorker::StartRunningVSyncOnThread, | |
123 base::Unretained(this))); | |
124 } | |
125 | |
126 void GpuVSyncWorker::StartRunningVSyncOnThread() { | |
127 DCHECK(base::PlatformThread::CurrentId() == GetThreadId()); | |
128 | |
129 if (!running_) { | |
130 running_ = true; | |
131 WaitForVSyncOnThread(); | |
132 } | |
133 } | |
134 | |
135 void GpuVSyncWorker::WaitForVSyncOnThread() { | |
136 DCHECK(base::PlatformThread::CurrentId() == GetThreadId()); | |
137 | |
138 TRACE_EVENT0("gpu", "GpuVSyncWorker::WaitForVSyncOnThread"); | |
139 | |
140 HMONITOR monitor = | |
141 MonitorFromWindow(surface_handle_, MONITOR_DEFAULTTONEAREST); | |
142 MONITORINFOEX monitor_info; | |
143 monitor_info.cbSize = sizeof(MONITORINFOEX); | |
144 BOOL success = GetMonitorInfo(monitor, &monitor_info); | |
145 CHECK(success); | |
146 | |
147 HDC hdc = CreateDC(NULL, monitor_info.szDevice, NULL, NULL); | |
148 | |
149 D3DKMT_OPENADAPTERFROMHDC open_adapter_data; | |
150 open_adapter_data.hDc = hdc; | |
151 | |
152 NTSTATUS result = open_adapter_from_hdc_ptr_(&open_adapter_data); | |
jbauman
2017/01/06 03:25:50
I guess we'll see how the performance of this look
stanisc
2017/01/06 21:35:51
I did some tracing and this didn't look like a not
brucedawson
2017/01/06 21:43:52
Maybe do some timing measurements on a few machine
| |
153 DeleteDC(hdc); | |
154 | |
155 CHECK(result == STATUS_SUCCESS); | |
156 | |
157 D3DKMT_WAITFORVERTICALBLANKEVENT wait_for_vertical_blank_event_data; | |
158 wait_for_vertical_blank_event_data.hAdapter = open_adapter_data.hAdapter; | |
159 wait_for_vertical_blank_event_data.hDevice = 0; | |
160 wait_for_vertical_blank_event_data.VidPnSourceId = | |
161 open_adapter_data.VidPnSourceId; | |
162 | |
163 result = | |
164 wait_for_vertical_blank_event_ptr_(&wait_for_vertical_blank_event_data); | |
165 if (result == STATUS_SUCCESS) { | |
166 // Note: this sends update on background thread which the callback is | |
167 // expected to handle. | |
168 SendVSyncUpdate(base::TimeTicks::Now()); | |
169 } | |
170 | |
171 D3DKMT_CLOSEADAPTER close_adapter_data; | |
172 close_adapter_data.hAdapter = open_adapter_data.hAdapter; | |
173 | |
174 result = close_adapter_ptr_(&close_adapter_data); | |
175 CHECK(result == STATUS_SUCCESS); | |
176 | |
177 Reschedule(); | |
178 } | |
179 | |
180 void GpuVSyncWorker::SendVSyncUpdate(base::TimeTicks timestamp) { | |
181 if (base::subtle::NoBarrier_Load(&enabled_)) { | |
182 TRACE_EVENT0("gpu", "GpuVSyncWorker::SendVSyncUpdate"); | |
183 callback_.Run(timestamp); | |
184 } | |
185 } | |
186 | |
187 void GpuVSyncWorker::Reschedule() { | |
188 // Restart the task if still enabled. | |
189 if (base::subtle::NoBarrier_Load(&enabled_)) { | |
190 task_runner()->PostTask(FROM_HERE, | |
191 base::Bind(&GpuVSyncWorker::WaitForVSyncOnThread, | |
192 base::Unretained(this))); | |
193 } else { | |
194 running_ = false; | |
195 } | |
196 } | |
197 | |
198 /* static */ | |
199 std::unique_ptr<GpuVSyncProvider> GpuVSyncProvider::Create( | |
200 const VSyncCallback& callback, | |
201 SurfaceHandle surface_handle) { | |
202 return std::unique_ptr<GpuVSyncProvider>( | |
203 new GpuVSyncProvider(callback, surface_handle)); | |
204 } | |
205 | |
206 GpuVSyncProvider::GpuVSyncProvider(const VSyncCallback& callback, | |
207 SurfaceHandle surface_handle) | |
208 : vsync_worker_(new GpuVSyncWorker(callback, surface_handle)) { | |
209 // Start the thread. | |
210 base::Thread::Options options; | |
211 // TODO(stanisc): might consider even higher priority - REALTIME_AUDIO. | |
212 options.priority = base::ThreadPriority::DISPLAY; | |
213 vsync_worker_->StartWithOptions(options); | |
214 } | |
215 | |
216 GpuVSyncProvider::~GpuVSyncProvider() = default; | |
217 | |
218 void GpuVSyncProvider::EnableVSync(bool enabled) { | |
219 vsync_worker_->Enable(enabled); | |
220 } | |
221 | |
222 } // namespace gpu | |
OLD | NEW |