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

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

Issue 2596123002: GpuVSyncProvider with unit test (Closed)
Patch Set: Added comment for GpuVSyncWorker destructor 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 // 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.
126 task_runner()->PostTask(FROM_HERE, base::Bind(&GpuVSyncWorker::CloseAdapter,
127 base::Unretained(this)));
128 Stop();
129
130 DCHECK_EQ(0u, current_adapter_handle_);
131 DCHECK(current_device_name_.empty());
132 }
133
134 void GpuVSyncWorker::Enable(bool enabled) {
135 auto was_enabled = base::subtle::NoBarrier_AtomicExchange(&enabled_, enabled);
136
137 if (enabled && !was_enabled)
138 task_runner()->PostTask(
139 FROM_HERE, base::Bind(&GpuVSyncWorker::StartRunningVSyncOnThread,
140 base::Unretained(this)));
141 }
142
143 void GpuVSyncWorker::StartRunningVSyncOnThread() {
144 DCHECK(base::PlatformThread::CurrentId() == GetThreadId());
145
146 if (!running_) {
147 running_ = true;
148 WaitForVSyncOnThread();
149 }
150 }
151
152 void GpuVSyncWorker::WaitForVSyncOnThread() {
153 DCHECK(base::PlatformThread::CurrentId() == GetThreadId());
154
155 TRACE_EVENT0("gpu", "GpuVSyncWorker::WaitForVSyncOnThread");
156
157 HMONITOR monitor =
158 MonitorFromWindow(surface_handle_, MONITOR_DEFAULTTONEAREST);
159 MONITORINFOEX monitor_info;
160 monitor_info.cbSize = sizeof(MONITORINFOEX);
161 BOOL success = GetMonitorInfo(monitor, &monitor_info);
162 CHECK(success);
163
164 if (current_device_name_.compare(monitor_info.szDevice) != 0) {
165 // Monitor changed. Close the current adapter handle and open a new one.
166 CloseAdapter();
167 OpenAdapter(monitor_info.szDevice);
168 }
169
170 if (WaitForVBlankEvent()) {
171 // Note: this sends update on background thread which the callback is
172 // expected to handle.
173 SendVSyncUpdate(base::TimeTicks::Now());
174 }
175
176 Reschedule();
177 }
178
179 void GpuVSyncWorker::SendVSyncUpdate(base::TimeTicks timestamp) {
180 if (base::subtle::NoBarrier_Load(&enabled_)) {
181 TRACE_EVENT0("gpu", "GpuVSyncWorker::SendVSyncUpdate");
182 callback_.Run(timestamp);
183 }
184 }
185
186 void GpuVSyncWorker::Reschedule() {
187 // Restart the task if still enabled.
188 if (base::subtle::NoBarrier_Load(&enabled_)) {
189 task_runner()->PostTask(FROM_HERE,
190 base::Bind(&GpuVSyncWorker::WaitForVSyncOnThread,
191 base::Unretained(this)));
192 } else {
193 running_ = false;
194 }
195 }
196
197 void GpuVSyncWorker::OpenAdapter(const wchar_t* device_name) {
198 DCHECK_EQ(0u, current_adapter_handle_);
199
200 HDC hdc = CreateDC(NULL, device_name, NULL, NULL);
201
202 D3DKMT_OPENADAPTERFROMHDC open_adapter_data;
203 open_adapter_data.hDc = hdc;
204
205 NTSTATUS result = open_adapter_from_hdc_ptr_(&open_adapter_data);
206 DeleteDC(hdc);
207
208 CHECK(result == STATUS_SUCCESS);
209
210 current_device_name_ = device_name;
211 current_adapter_handle_ = open_adapter_data.hAdapter;
212 current_source_id_ = open_adapter_data.VidPnSourceId;
213 }
214
215 void GpuVSyncWorker::CloseAdapter() {
216 if (current_adapter_handle_ != 0) {
217 D3DKMT_CLOSEADAPTER close_adapter_data;
218 close_adapter_data.hAdapter = current_adapter_handle_;
219
220 NTSTATUS result = close_adapter_ptr_(&close_adapter_data);
221 CHECK(result == STATUS_SUCCESS);
222
223 current_adapter_handle_ = 0;
224 current_device_name_.clear();
225 }
226 }
227
228 bool GpuVSyncWorker::WaitForVBlankEvent() {
229 D3DKMT_WAITFORVERTICALBLANKEVENT wait_for_vertical_blank_event_data;
230 wait_for_vertical_blank_event_data.hAdapter = current_adapter_handle_;
231 wait_for_vertical_blank_event_data.hDevice = 0;
232 wait_for_vertical_blank_event_data.VidPnSourceId = current_source_id_;
233
234 NTSTATUS result =
235 wait_for_vertical_blank_event_ptr_(&wait_for_vertical_blank_event_data);
236
237 return result == STATUS_SUCCESS;
238 }
239
240 /* static */
241 std::unique_ptr<GpuVSyncProvider> GpuVSyncProvider::Create(
242 const VSyncCallback& callback,
243 SurfaceHandle surface_handle) {
244 return std::unique_ptr<GpuVSyncProvider>(
245 new GpuVSyncProvider(callback, surface_handle));
246 }
247
248 GpuVSyncProvider::GpuVSyncProvider(const VSyncCallback& callback,
249 SurfaceHandle surface_handle)
250 : vsync_worker_(new GpuVSyncWorker(callback, surface_handle)) {
251 // Start the thread.
252 base::Thread::Options options;
253 // TODO(stanisc): might consider even higher priority - REALTIME_AUDIO.
254 options.priority = base::ThreadPriority::DISPLAY;
255 vsync_worker_->StartWithOptions(options);
256 }
257
258 GpuVSyncProvider::~GpuVSyncProvider() = default;
259
260 void GpuVSyncProvider::EnableVSync(bool enabled) {
261 vsync_worker_->Enable(enabled);
262 }
263
264 } // 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