OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "ui/gfx/surface/accelerated_surface_win.h" | 5 #include "ui/gfx/surface/accelerated_surface_win.h" |
6 | 6 |
7 #include <windows.h> | 7 #include <windows.h> |
8 | 8 |
9 #include <list> | 9 #include <list> |
10 | 10 |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/callback.h" | 12 #include "base/callback.h" |
13 #include "base/command_line.h" | 13 #include "base/command_line.h" |
14 #include "base/debug/trace_event.h" | 14 #include "base/debug/trace_event.h" |
15 #include "base/file_path.h" | |
15 #include "base/lazy_instance.h" | 16 #include "base/lazy_instance.h" |
16 #include "base/memory/scoped_ptr.h" | 17 #include "base/memory/scoped_ptr.h" |
17 #include "base/memory/weak_ptr.h" | 18 #include "base/memory/weak_ptr.h" |
18 #include "base/stringprintf.h" | 19 #include "base/stringprintf.h" |
19 #include "base/threading/thread.h" | 20 #include "base/threading/thread.h" |
20 #include "base/tracked_objects.h" | 21 #include "base/tracked_objects.h" |
21 #include "base/win/wrapped_window_proc.h" | 22 #include "base/win/wrapped_window_proc.h" |
22 #include "ipc/ipc_message.h" | 23 #include "ipc/ipc_message.h" |
23 #include "ui/base/win/hwnd_util.h" | 24 #include "ui/base/win/hwnd_util.h" |
24 #include "ui/gfx/gl/gl_switches.h" | 25 #include "ui/gfx/gl/gl_switches.h" |
25 | 26 |
26 #pragma comment(lib, "d3d9.lib") | |
27 | |
28 namespace { | 27 namespace { |
29 | 28 |
30 typedef HRESULT (WINAPI *Direct3DCreate9ExFunc)(UINT sdk_version, | 29 typedef HRESULT (WINAPI *Direct3DCreate9ExFunc)(UINT sdk_version, |
31 IDirect3D9Ex **d3d); | 30 IDirect3D9Ex **d3d); |
32 | 31 |
33 const int64 kPollQueryInterval = 1; | |
34 | |
35 const wchar_t kD3D9ModuleName[] = L"d3d9.dll"; | 32 const wchar_t kD3D9ModuleName[] = L"d3d9.dll"; |
36 const char kCreate3D9DeviceExName[] = "Direct3DCreate9Ex"; | 33 const char kCreate3D9DeviceExName[] = "Direct3DCreate9Ex"; |
37 | 34 |
38 class QuerySyncThread | |
39 : public base::Thread, | |
40 public base::RefCounted<QuerySyncThread> { | |
41 public: | |
42 explicit QuerySyncThread(const char* name); | |
43 virtual ~QuerySyncThread(); | |
44 | |
45 // Invoke the completion task when the query completes. | |
46 void AcknowledgeQuery(const base::win::ScopedComPtr<IDirect3DQuery9>& query, | |
47 const base::Closure& completion_task); | |
48 | |
49 // Acknowledge all pending queries for the given device early then invoke | |
50 // the given task. | |
51 void AcknowledgeEarly( | |
52 const base::win::ScopedComPtr<IDirect3DDevice9Ex>& device, | |
53 const base::Closure& completion_task); | |
54 | |
55 private: | |
56 void PollQueries(); | |
57 | |
58 struct PendingQuery { | |
59 base::win::ScopedComPtr<IDirect3DQuery9> query; | |
60 base::Closure completion_task; | |
61 }; | |
62 | |
63 typedef std::list<PendingQuery> PendingQueries; | |
64 PendingQueries pending_queries_; | |
65 base::WeakPtrFactory<QuerySyncThread> poll_factory_; | |
66 | |
67 DISALLOW_COPY_AND_ASSIGN(QuerySyncThread); | |
68 }; | |
69 | |
70 class PresentThreadPool { | 35 class PresentThreadPool { |
71 public: | 36 public: |
72 static const int kNumPresentThreads = 4; | 37 static const int kNumPresentThreads = 4; |
73 | 38 |
74 PresentThreadPool(); | 39 PresentThreadPool(); |
75 | 40 |
76 int NextThread(); | 41 int NextThread(); |
77 | 42 |
78 void PostTask(int thread, | 43 void PostTask(int thread, |
79 const tracked_objects::Location& from_here, | 44 const tracked_objects::Location& from_here, |
80 const base::Closure& task); | 45 const base::Closure& task); |
81 | 46 |
82 void AcknowledgeQuery(const tracked_objects::Location& from_here, | |
83 const base::win::ScopedComPtr<IDirect3DQuery9>& query, | |
84 const base::Closure& completion_task); | |
85 | |
86 void AcknowledgeEarly( | |
87 const tracked_objects::Location& from_here, | |
88 const base::win::ScopedComPtr<IDirect3DDevice9Ex>& device, | |
89 const base::Closure& completion_task); | |
90 | |
91 private: | 47 private: |
92 int next_thread_; | 48 int next_thread_; |
93 scoped_ptr<base::Thread> present_threads_[kNumPresentThreads]; | 49 scoped_ptr<base::Thread> present_threads_[kNumPresentThreads]; |
94 scoped_refptr<QuerySyncThread> query_sync_thread_; | |
95 | 50 |
96 DISALLOW_COPY_AND_ASSIGN(PresentThreadPool); | 51 DISALLOW_COPY_AND_ASSIGN(PresentThreadPool); |
97 }; | 52 }; |
98 | 53 |
99 base::LazyInstance<PresentThreadPool> | 54 base::LazyInstance<PresentThreadPool> |
100 g_present_thread_pool = LAZY_INSTANCE_INITIALIZER; | 55 g_present_thread_pool = LAZY_INSTANCE_INITIALIZER; |
101 | 56 |
102 QuerySyncThread::QuerySyncThread(const char* name) | |
103 : base::Thread(name), | |
104 poll_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | |
105 } | |
106 | |
107 QuerySyncThread::~QuerySyncThread() { | |
108 } | |
109 | |
110 void QuerySyncThread::AcknowledgeQuery( | |
111 const base::win::ScopedComPtr<IDirect3DQuery9>& query, | |
112 const base::Closure& completion_task) { | |
113 PendingQuery pending_query; | |
114 pending_query.query = query; | |
115 pending_query.completion_task = completion_task; | |
116 pending_queries_.push_back(pending_query); | |
117 | |
118 // Cancel any pending poll tasks. There should only ever be one pending at a | |
119 // time. | |
120 poll_factory_.InvalidateWeakPtrs(); | |
121 | |
122 PollQueries(); | |
123 } | |
124 | |
125 void QuerySyncThread::AcknowledgeEarly( | |
126 const base::win::ScopedComPtr<IDirect3DDevice9Ex>& device, | |
127 const base::Closure& completion_task) { | |
128 TRACE_EVENT0("surface", "AcknowledgeEarly"); | |
129 | |
130 PendingQueries::iterator it = pending_queries_.begin(); | |
131 while (it != pending_queries_.end()) { | |
132 const PendingQuery& pending_query = *it; | |
133 | |
134 base::win::ScopedComPtr<IDirect3DDevice9> query_device; | |
135 pending_query.query->GetDevice(query_device.Receive()); | |
136 | |
137 base::win::ScopedComPtr<IDirect3DDevice9Ex> query_device_ex; | |
138 query_device_ex.QueryFrom(query_device.get()); | |
139 | |
140 if (query_device_ex.get() != device.get()) { | |
141 ++it; | |
142 } else { | |
143 pending_query.completion_task.Run(); | |
144 it = pending_queries_.erase(it); | |
145 } | |
146 } | |
147 | |
148 if (!completion_task.is_null()) | |
149 completion_task.Run(); | |
150 } | |
151 | |
152 | |
153 void QuerySyncThread::PollQueries() { | |
154 TRACE_EVENT0("surface", "PollQueries"); | |
155 | |
156 PendingQueries::iterator it = pending_queries_.begin(); | |
157 while (it != pending_queries_.end()) { | |
158 const PendingQuery& pending_query = *it; | |
159 | |
160 HRESULT hr = pending_query.query->GetData(NULL, 0, D3DGETDATA_FLUSH); | |
161 if (hr == S_FALSE) { | |
162 ++it; | |
163 } else { | |
164 pending_query.completion_task.Run(); | |
165 it = pending_queries_.erase(it); | |
166 } | |
167 } | |
168 | |
169 // Try again later if there are incomplete queries. Otherwise don't poll again | |
170 // until AcknowledgeQuery is called with a new query. | |
171 if (!pending_queries_.empty()) { | |
172 message_loop()->PostDelayedTask( | |
173 FROM_HERE, | |
174 base::Bind(&QuerySyncThread::PollQueries, poll_factory_.GetWeakPtr()), | |
175 kPollQueryInterval); | |
176 } | |
177 } | |
178 | |
179 PresentThreadPool::PresentThreadPool() : next_thread_(0) { | 57 PresentThreadPool::PresentThreadPool() : next_thread_(0) { |
180 for (int i = 0; i < kNumPresentThreads; ++i) { | 58 for (int i = 0; i < kNumPresentThreads; ++i) { |
181 present_threads_[i].reset(new base::Thread( | 59 present_threads_[i].reset(new base::Thread( |
182 base::StringPrintf("PresentThread #%d", i).c_str())); | 60 base::StringPrintf("PresentThread #%d", i).c_str())); |
183 present_threads_[i]->Start(); | 61 present_threads_[i]->Start(); |
184 } | 62 } |
185 | |
186 query_sync_thread_ = new QuerySyncThread("QuerySyncThread"); | |
187 query_sync_thread_->Start(); | |
188 } | 63 } |
189 | 64 |
190 int PresentThreadPool::NextThread() { | 65 int PresentThreadPool::NextThread() { |
191 next_thread_ = (next_thread_ + 1) % kNumPresentThreads; | 66 next_thread_ = (next_thread_ + 1) % kNumPresentThreads; |
192 return next_thread_; | 67 return next_thread_; |
193 } | 68 } |
194 | 69 |
195 void PresentThreadPool::PostTask(int thread, | 70 void PresentThreadPool::PostTask(int thread, |
196 const tracked_objects::Location& from_here, | 71 const tracked_objects::Location& from_here, |
197 const base::Closure& task) { | 72 const base::Closure& task) { |
198 DCHECK_GE(thread, 0); | 73 DCHECK_GE(thread, 0); |
199 DCHECK_LT(thread, kNumPresentThreads); | 74 DCHECK_LT(thread, kNumPresentThreads); |
200 | 75 |
201 present_threads_[thread]->message_loop()->PostTask(from_here, task); | 76 present_threads_[thread]->message_loop()->PostTask(from_here, task); |
202 } | 77 } |
203 | 78 |
204 void PresentThreadPool::AcknowledgeQuery( | |
205 const tracked_objects::Location& from_here, | |
206 const base::win::ScopedComPtr<IDirect3DQuery9>& query, | |
207 const base::Closure& completion_task) { | |
208 query_sync_thread_->message_loop()->PostTask( | |
209 from_here, | |
210 base::Bind(&QuerySyncThread::AcknowledgeQuery, | |
211 query_sync_thread_, | |
212 query, | |
213 completion_task)); | |
214 } | |
215 | |
216 void PresentThreadPool::AcknowledgeEarly( | |
217 const tracked_objects::Location& from_here, | |
218 const base::win::ScopedComPtr<IDirect3DDevice9Ex>& device, | |
219 const base::Closure& completion_task) { | |
220 query_sync_thread_->message_loop()->PostTask( | |
221 from_here, | |
222 base::Bind(&QuerySyncThread::AcknowledgeEarly, | |
223 query_sync_thread_, | |
224 device, | |
225 completion_task)); | |
226 } | |
227 | |
228 UINT GetPresentationInterval() { | 79 UINT GetPresentationInterval() { |
229 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync)) | 80 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync)) |
230 return D3DPRESENT_INTERVAL_IMMEDIATE; | 81 return D3DPRESENT_INTERVAL_IMMEDIATE; |
231 else | 82 else |
232 return D3DPRESENT_INTERVAL_ONE; | 83 return D3DPRESENT_INTERVAL_ONE; |
233 } | 84 } |
234 | 85 |
235 } // namespace anonymous | 86 } // namespace anonymous |
236 | 87 |
237 AcceleratedSurface::AcceleratedSurface(HWND parent) | 88 AcceleratedSurface::AcceleratedSurface(HWND parent) |
238 : thread_affinity_(g_present_thread_pool.Pointer()->NextThread()), | 89 : thread_affinity_(g_present_thread_pool.Pointer()->NextThread()), |
239 window_(parent), | 90 window_(parent), |
240 num_pending_resizes_(0) { | 91 num_pending_resizes_(0) { |
241 } | 92 } |
242 | 93 |
243 AcceleratedSurface::~AcceleratedSurface() { | 94 AcceleratedSurface::~AcceleratedSurface() { |
244 // Destroy should have been called prior to the last reference going away. | 95 // Destroy should have been called prior to the last reference going away. |
245 DCHECK(!device_); | 96 DCHECK(!device_); |
246 } | 97 } |
247 | 98 |
248 void AcceleratedSurface::Initialize() { | 99 void AcceleratedSurface::Initialize() { |
249 g_present_thread_pool.Pointer()->PostTask( | 100 g_present_thread_pool.Pointer()->PostTask( |
250 thread_affinity_, | 101 thread_affinity_, |
251 FROM_HERE, | 102 FROM_HERE, |
252 base::Bind(&AcceleratedSurface::DoInitialize, this)); | 103 base::Bind(&AcceleratedSurface::DoInitialize, this)); |
253 } | 104 } |
254 | 105 |
255 void AcceleratedSurface::Destroy() { | 106 void AcceleratedSurface::Destroy() { |
256 g_present_thread_pool.Pointer()->AcknowledgeEarly( | 107 g_present_thread_pool.Pointer()->PostTask( |
108 thread_affinity_, | |
257 FROM_HERE, | 109 FROM_HERE, |
258 device_, | 110 base::Bind(&AcceleratedSurface::DoDestroy, this)); |
259 base::Bind(&AcceleratedSurface::QueriesDestroyed, this)); | |
260 } | 111 } |
261 | 112 |
262 void AcceleratedSurface::AsyncPresentAndAcknowledge( | 113 void AcceleratedSurface::AsyncPresentAndAcknowledge( |
263 const gfx::Size& size, | 114 const gfx::Size& size, |
264 int64 surface_id, | 115 int64 surface_id, |
265 const base::Closure& completion_task) { | 116 const base::Closure& completion_task) { |
266 const int kRound = 64; | 117 const int kRound = 64; |
267 gfx::Size quantized_size( | 118 gfx::Size quantized_size( |
268 std::max(1, (size.width() + kRound - 1) / kRound * kRound), | 119 std::max(1, (size.width() + kRound - 1) / kRound * kRound), |
269 std::max(1, (size.height() + kRound - 1) / kRound * kRound)); | 120 std::max(1, (size.height() + kRound - 1) / kRound * kRound)); |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
329 Sleep(0); | 180 Sleep(0); |
330 } while (hr == S_FALSE); | 181 } while (hr == S_FALSE); |
331 } | 182 } |
332 } | 183 } |
333 | 184 |
334 void AcceleratedSurface::DoInitialize() { | 185 void AcceleratedSurface::DoInitialize() { |
335 TRACE_EVENT0("surface", "DoInitialize"); | 186 TRACE_EVENT0("surface", "DoInitialize"); |
336 | 187 |
337 HRESULT hr; | 188 HRESULT hr; |
338 | 189 |
339 HMODULE module = GetModuleHandle(kD3D9ModuleName); | 190 d3d_module_.Reset(base::LoadNativeLibrary(FilePath(kD3D9ModuleName), NULL)); |
340 if (!module) | |
341 return; | |
342 | 191 |
343 Direct3DCreate9ExFunc create_func = reinterpret_cast<Direct3DCreate9ExFunc>( | 192 Direct3DCreate9ExFunc create_func = reinterpret_cast<Direct3DCreate9ExFunc>( |
344 GetProcAddress(module, kCreate3D9DeviceExName)); | 193 d3d_module_.GetFunctionPointer(kCreate3D9DeviceExName)); |
345 if (!create_func) | 194 if (!create_func) |
346 return; | 195 return; |
347 | 196 |
348 base::win::ScopedComPtr<IDirect3D9Ex> d3d; | 197 base::win::ScopedComPtr<IDirect3D9Ex> d3d; |
349 hr = create_func(D3D_SDK_VERSION, d3d.Receive()); | 198 hr = create_func(D3D_SDK_VERSION, d3d.Receive()); |
350 if (FAILED(hr)) | 199 if (FAILED(hr)) |
351 return; | 200 return; |
352 | 201 |
353 D3DPRESENT_PARAMETERS parameters = { 0 }; | 202 D3DPRESENT_PARAMETERS parameters = { 0 }; |
354 parameters.BackBufferWidth = 1; | 203 parameters.BackBufferWidth = 1; |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
399 query_ = NULL; | 248 query_ = NULL; |
400 } | 249 } |
401 | 250 |
402 void AcceleratedSurface::DoResize(const gfx::Size& size) { | 251 void AcceleratedSurface::DoResize(const gfx::Size& size) { |
403 TRACE_EVENT0("surface", "DoResize"); | 252 TRACE_EVENT0("surface", "DoResize"); |
404 | 253 |
405 HRESULT hr; | 254 HRESULT hr; |
406 | 255 |
407 base::AtomicRefCountDec(&num_pending_resizes_); | 256 base::AtomicRefCountDec(&num_pending_resizes_); |
408 | 257 |
258 base::AutoLock locked(lock_); | |
259 | |
260 if (!device_) | |
261 return; | |
262 | |
409 D3DPRESENT_PARAMETERS parameters = { 0 }; | 263 D3DPRESENT_PARAMETERS parameters = { 0 }; |
410 parameters.BackBufferWidth = size.width(); | 264 parameters.BackBufferWidth = size.width(); |
411 parameters.BackBufferHeight = size.height(); | 265 parameters.BackBufferHeight = size.height(); |
412 parameters.BackBufferCount = 1; | 266 parameters.BackBufferCount = 1; |
413 parameters.BackBufferFormat = D3DFMT_A8R8G8B8; | 267 parameters.BackBufferFormat = D3DFMT_A8R8G8B8; |
414 parameters.hDeviceWindow = window_; | 268 parameters.hDeviceWindow = window_; |
415 parameters.Windowed = TRUE; | 269 parameters.Windowed = TRUE; |
416 parameters.Flags = 0; | 270 parameters.Flags = 0; |
417 parameters.PresentationInterval = GetPresentationInterval(); | 271 parameters.PresentationInterval = GetPresentationInterval(); |
418 parameters.SwapEffect = D3DSWAPEFFECT_COPY; | 272 parameters.SwapEffect = D3DSWAPEFFECT_COPY; |
(...skipping 13 matching lines...) Expand all Loading... | |
432 const base::Closure& completion_task) { | 286 const base::Closure& completion_task) { |
433 TRACE_EVENT1("surface", "DoPresentAndAcknowledge", "surface_id", surface_id); | 287 TRACE_EVENT1("surface", "DoPresentAndAcknowledge", "surface_id", surface_id); |
434 | 288 |
435 HRESULT hr; | 289 HRESULT hr; |
436 | 290 |
437 base::AutoLock locked(lock_); | 291 base::AutoLock locked(lock_); |
438 | 292 |
439 // Ensure the task is always run and while the lock is taken. | 293 // Ensure the task is always run and while the lock is taken. |
440 base::ScopedClosureRunner scoped_completion_runner(completion_task); | 294 base::ScopedClosureRunner scoped_completion_runner(completion_task); |
441 | 295 |
296 if (!device_) | |
297 return; | |
298 | |
442 if (!window_) | 299 if (!window_) |
443 return; | 300 return; |
444 | 301 |
445 HANDLE handle = reinterpret_cast<HANDLE>(surface_id); | 302 HANDLE handle = reinterpret_cast<HANDLE>(surface_id); |
446 if (!handle) | 303 if (!handle) |
447 return; | 304 return; |
448 | 305 |
449 base::win::ScopedComPtr<IDirect3DTexture9> source_texture; | 306 base::win::ScopedComPtr<IDirect3DTexture9> source_texture; |
450 { | 307 { |
451 TRACE_EVENT0("surface", "CreateTexture"); | 308 TRACE_EVENT0("surface", "CreateTexture"); |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
497 | 354 |
498 ::SetWindowPos( | 355 ::SetWindowPos( |
499 window_, | 356 window_, |
500 NULL, | 357 NULL, |
501 0, 0, | 358 0, 0, |
502 size.width(), size.height(), | 359 size.width(), size.height(), |
503 SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE |SWP_NOOWNERZORDER | | 360 SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE |SWP_NOOWNERZORDER | |
504 SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOSENDCHANGING | | 361 SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOSENDCHANGING | |
505 SWP_ASYNCWINDOWPOS); | 362 SWP_ASYNCWINDOWPOS); |
506 | 363 |
364 // Wait for the StretchRect to complete before notifying the GPU process | |
365 // that it is safe to write to its backing store again. | |
366 { | |
367 TRACE_EVENT0("surface", "spin"); | |
368 do { | |
369 hr = query_->GetData(NULL, 0, D3DGETDATA_FLUSH); | |
370 | |
371 if (hr == S_FALSE) | |
372 Sleep(0); | |
373 } while (hr == S_FALSE); | |
Ken Russell (switch to Gerrit)
2011/12/08 01:55:07
As an FYI a problem in a similarly structured loop
| |
374 } | |
375 | |
507 scoped_completion_runner.Release(); | 376 scoped_completion_runner.Release(); |
508 if (!completion_task.is_null()) { | 377 if (!completion_task.is_null()) |
509 g_present_thread_pool.Pointer()->AcknowledgeQuery(FROM_HERE, | 378 completion_task.Run(); |
510 query_, | |
511 completion_task); | |
512 } | |
513 | 379 |
514 { | 380 { |
515 TRACE_EVENT0("surface", "Present"); | 381 TRACE_EVENT0("surface", "Present"); |
516 hr = device_->Present(&rect, &rect, NULL, NULL); | 382 hr = device_->Present(&rect, &rect, NULL, NULL); |
517 if (FAILED(hr)) | 383 if (FAILED(hr)) |
518 return; | 384 return; |
519 } | 385 } |
520 } | 386 } |
OLD | NEW |