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

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

Issue 2596123002: GpuVSyncProvider with unit test (Closed)
Patch Set: Optimized opening/closing adapter Created 3 years, 11 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
« no previous file with comments | « gpu/ipc/service/gpu_vsync_provider_unittest_win.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 <string>
8
9 #include "base/atomicops.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/threading/thread.h"
12 #include "base/trace_event/trace_event.h"
13
14 #include <windows.h>
15
16 namespace gpu {
17
18 namespace {
19 // from <D3dkmthk.h>
20 typedef LONG NTSTATUS;
21 typedef UINT D3DKMT_HANDLE;
22 typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID;
23
24 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
25
26 typedef struct _D3DKMT_OPENADAPTERFROMHDC {
27 HDC hDc;
28 D3DKMT_HANDLE hAdapter;
29 LUID AdapterLuid;
30 D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
31 } D3DKMT_OPENADAPTERFROMHDC;
32
33 typedef struct _D3DKMT_CLOSEADAPTER {
34 D3DKMT_HANDLE hAdapter;
35 } D3DKMT_CLOSEADAPTER;
36
37 typedef struct _D3DKMT_WAITFORVERTICALBLANKEVENT {
38 D3DKMT_HANDLE hAdapter;
39 D3DKMT_HANDLE hDevice;
40 D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
41 } D3DKMT_WAITFORVERTICALBLANKEVENT;
42
43 typedef NTSTATUS(APIENTRY* PFND3DKMTOPENADAPTERFROMHDC)(
44 D3DKMT_OPENADAPTERFROMHDC*);
45 typedef NTSTATUS(APIENTRY* PFND3DKMTCLOSEADAPTER)(D3DKMT_CLOSEADAPTER*);
46 typedef NTSTATUS(APIENTRY* PFND3DKMTWAITFORVERTICALBLANKEVENT)(
47 D3DKMT_WAITFORVERTICALBLANKEVENT*);
48 } // namespace
49
50 // The actual implementation of background tasks plus any state that might be
51 // needed on the worker thread.
52 class GpuVSyncWorker : public base::Thread {
53 public:
54 GpuVSyncWorker(const GpuVSyncProvider::VSyncCallback& callback,
55 SurfaceHandle surface_handle);
56 ~GpuVSyncWorker() override;
57
58 void Enable(bool enabled);
59 void StartRunningVSyncOnThread();
60 void WaitForVSyncOnThread();
61 void SendVSyncUpdate(base::TimeTicks timestamp);
62
63 private:
64 void Reschedule();
65 void OpenAdapter(const wchar_t* device_name);
66 void CloseAdapter();
67 bool WaitForVBlankEvent();
68
69 // Specifies whether background tasks are running.
70 // This can be set on background thread only.
71 bool running_ = false;
72
73 // Specified whether the worker is enabled. This is accessed from both
74 // threads but can be changed on the main thread only.
75 base::subtle::AtomicWord enabled_ = false;
76
77 const GpuVSyncProvider::VSyncCallback callback_;
78 const SurfaceHandle surface_handle_;
79
80 PFND3DKMTOPENADAPTERFROMHDC open_adapter_from_hdc_ptr_;
81 PFND3DKMTCLOSEADAPTER close_adapter_ptr_;
82 PFND3DKMTWAITFORVERTICALBLANKEVENT wait_for_vertical_blank_event_ptr_;
83
84 std::wstring current_device_name_;
85 D3DKMT_HANDLE current_adapter_handle_ = 0;
86 D3DDDI_VIDEO_PRESENT_SOURCE_ID current_source_id_ = 0;
87 };
88
89 GpuVSyncWorker::GpuVSyncWorker(const GpuVSyncProvider::VSyncCallback& callback,
90 SurfaceHandle surface_handle)
91 : base::Thread(base::StringPrintf("VSync-%d", surface_handle)),
92 callback_(callback),
93 surface_handle_(surface_handle) {
94 HMODULE gdi32 = GetModuleHandle(L"gdi32");
95 if (!gdi32) {
96 NOTREACHED() << "Can't open gdi32.dll";
97 return;
98 }
99
100 open_adapter_from_hdc_ptr_ = reinterpret_cast<PFND3DKMTOPENADAPTERFROMHDC>(
101 ::GetProcAddress(gdi32, "D3DKMTOpenAdapterFromHdc"));
102 if (!open_adapter_from_hdc_ptr_) {
103 NOTREACHED() << "Can't find D3DKMTOpenAdapterFromHdc in gdi32.dll";
104 return;
105 }
106
107 close_adapter_ptr_ = reinterpret_cast<PFND3DKMTCLOSEADAPTER>(
108 ::GetProcAddress(gdi32, "D3DKMTCloseAdapter"));
109 if (!close_adapter_ptr_) {
110 NOTREACHED() << "Can't find D3DKMTCloseAdapter in gdi32.dll";
111 return;
112 }
113
114 wait_for_vertical_blank_event_ptr_ =
115 reinterpret_cast<PFND3DKMTWAITFORVERTICALBLANKEVENT>(
116 ::GetProcAddress(gdi32, "D3DKMTWaitForVerticalBlankEvent"));
117 if (!wait_for_vertical_blank_event_ptr_) {
118 NOTREACHED() << "Can't find D3DKMTWaitForVerticalBlankEvent in gdi32.dll";
119 return;
120 }
121 }
122
123 GpuVSyncWorker::~GpuVSyncWorker() {
124 task_runner()->PostTask(FROM_HERE, base::Bind(&GpuVSyncWorker::CloseAdapter,
125 base::Unretained(this)));
brucedawson 2017/01/09 18:51:54 This looks dangerous. CloseAdapter depends on the
stanisc 2017/01/09 19:13:37 This should be safe because Thread::Stop posts "Qu
126 Stop();
127 }
128
129 void GpuVSyncWorker::Enable(bool enabled) {
130 auto was_enabled = base::subtle::NoBarrier_AtomicExchange(&enabled_, enabled);
131
132 if (enabled && !was_enabled)
133 task_runner()->PostTask(
134 FROM_HERE, base::Bind(&GpuVSyncWorker::StartRunningVSyncOnThread,
135 base::Unretained(this)));
136 }
137
138 void GpuVSyncWorker::StartRunningVSyncOnThread() {
139 DCHECK(base::PlatformThread::CurrentId() == GetThreadId());
140
141 if (!running_) {
142 running_ = true;
143 WaitForVSyncOnThread();
144 }
145 }
146
147 void GpuVSyncWorker::WaitForVSyncOnThread() {
148 DCHECK(base::PlatformThread::CurrentId() == GetThreadId());
149
150 TRACE_EVENT0("gpu", "GpuVSyncWorker::WaitForVSyncOnThread");
151
152 HMONITOR monitor =
153 MonitorFromWindow(surface_handle_, MONITOR_DEFAULTTONEAREST);
154 MONITORINFOEX monitor_info;
155 monitor_info.cbSize = sizeof(MONITORINFOEX);
156 BOOL success = GetMonitorInfo(monitor, &monitor_info);
157 CHECK(success);
158
159 if (current_device_name_.compare(monitor_info.szDevice) != 0) {
160 // Monitor changed. Close the current adapter handle and open a new one.
161 CloseAdapter();
162 OpenAdapter(monitor_info.szDevice);
163 }
164
165 if (WaitForVBlankEvent()) {
166 // Note: this sends update on background thread which the callback is
167 // expected to handle.
168 SendVSyncUpdate(base::TimeTicks::Now());
169 }
170
171 Reschedule();
172 }
173
174 void GpuVSyncWorker::SendVSyncUpdate(base::TimeTicks timestamp) {
175 if (base::subtle::NoBarrier_Load(&enabled_)) {
176 TRACE_EVENT0("gpu", "GpuVSyncWorker::SendVSyncUpdate");
177 callback_.Run(timestamp);
178 }
179 }
180
181 void GpuVSyncWorker::Reschedule() {
182 // Restart the task if still enabled.
183 if (base::subtle::NoBarrier_Load(&enabled_)) {
184 task_runner()->PostTask(FROM_HERE,
185 base::Bind(&GpuVSyncWorker::WaitForVSyncOnThread,
186 base::Unretained(this)));
187 } else {
188 running_ = false;
189 }
190 }
191
192 void GpuVSyncWorker::OpenAdapter(const wchar_t* device_name) {
193 DCHECK_EQ(0u, current_adapter_handle_);
194
195 HDC hdc = CreateDC(NULL, device_name, NULL, NULL);
196
197 D3DKMT_OPENADAPTERFROMHDC open_adapter_data;
198 open_adapter_data.hDc = hdc;
199
200 NTSTATUS result = open_adapter_from_hdc_ptr_(&open_adapter_data);
201 DeleteDC(hdc);
202
203 CHECK(result == STATUS_SUCCESS);
204
205 current_device_name_ = device_name;
206 current_adapter_handle_ = open_adapter_data.hAdapter;
207 current_source_id_ = open_adapter_data.VidPnSourceId;
208 }
209
210 void GpuVSyncWorker::CloseAdapter() {
211 if (current_adapter_handle_ != 0) {
212 D3DKMT_CLOSEADAPTER close_adapter_data;
213 close_adapter_data.hAdapter = current_adapter_handle_;
214
215 NTSTATUS result = close_adapter_ptr_(&close_adapter_data);
216 CHECK(result == STATUS_SUCCESS);
217
218 current_adapter_handle_ = 0;
219 current_device_name_.clear();
220 }
221 }
222
223 bool GpuVSyncWorker::WaitForVBlankEvent() {
224 D3DKMT_WAITFORVERTICALBLANKEVENT wait_for_vertical_blank_event_data;
225 wait_for_vertical_blank_event_data.hAdapter = current_adapter_handle_;
226 wait_for_vertical_blank_event_data.hDevice = 0;
227 wait_for_vertical_blank_event_data.VidPnSourceId = current_source_id_;
228
229 NTSTATUS result =
230 wait_for_vertical_blank_event_ptr_(&wait_for_vertical_blank_event_data);
231
232 return result == STATUS_SUCCESS;
233 }
234
235 /* static */
236 std::unique_ptr<GpuVSyncProvider> GpuVSyncProvider::Create(
237 const VSyncCallback& callback,
238 SurfaceHandle surface_handle) {
239 return std::unique_ptr<GpuVSyncProvider>(
240 new GpuVSyncProvider(callback, surface_handle));
241 }
242
243 GpuVSyncProvider::GpuVSyncProvider(const VSyncCallback& callback,
244 SurfaceHandle surface_handle)
245 : vsync_worker_(new GpuVSyncWorker(callback, surface_handle)) {
246 // Start the thread.
247 base::Thread::Options options;
248 // TODO(stanisc): might consider even higher priority - REALTIME_AUDIO.
249 options.priority = base::ThreadPriority::DISPLAY;
250 vsync_worker_->StartWithOptions(options);
251 }
252
253 GpuVSyncProvider::~GpuVSyncProvider() = default;
254
255 void GpuVSyncProvider::EnableVSync(bool enabled) {
256 vsync_worker_->Enable(enabled);
257 }
258
259 } // namespace gpu
OLDNEW
« no previous file with comments | « gpu/ipc/service/gpu_vsync_provider_unittest_win.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698