OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 // A PresentThread is a thread that is dedicated to presenting surfaces to a | 43 // A PresentThread is a thread that is dedicated to presenting surfaces to a |
44 // window. It owns a Direct3D device and a Direct3D query for this purpose. | 44 // window. It owns a Direct3D device and a Direct3D query for this purpose. |
45 class PresentThread : public base::Thread { | 45 class PresentThread : public base::Thread { |
46 public: | 46 public: |
47 PresentThread(const char* name); | 47 PresentThread(const char* name); |
48 ~PresentThread(); | 48 ~PresentThread(); |
49 | 49 |
50 IDirect3DDevice9* device() { return device_.get(); } | 50 IDirect3DDevice9* device() { return device_.get(); } |
51 IDirect3DQuery9* query() { return query_.get(); } | 51 IDirect3DQuery9* query() { return query_.get(); } |
52 | 52 |
| 53 void InitDevice(); |
53 void ResetDevice(); | 54 void ResetDevice(); |
54 | 55 |
55 protected: | 56 protected: |
56 virtual void Init(); | |
57 virtual void CleanUp(); | 57 virtual void CleanUp(); |
58 | 58 |
59 private: | 59 private: |
60 base::ScopedNativeLibrary d3d_module_; | 60 base::ScopedNativeLibrary d3d_module_; |
61 base::win::ScopedComPtr<IDirect3DDevice9Ex> device_; | 61 base::win::ScopedComPtr<IDirect3DDevice9Ex> device_; |
62 | 62 |
63 // This query is used to wait until a certain amount of progress has been | 63 // This query is used to wait until a certain amount of progress has been |
64 // made by the GPU and it is safe for the producer to modify its shared | 64 // made by the GPU and it is safe for the producer to modify its shared |
65 // texture again. | 65 // texture again. |
66 base::win::ScopedComPtr<IDirect3DQuery9> query_; | 66 base::win::ScopedComPtr<IDirect3DQuery9> query_; |
(...skipping 20 matching lines...) Expand all Loading... |
87 base::LazyInstance<PresentThreadPool> | 87 base::LazyInstance<PresentThreadPool> |
88 g_present_thread_pool = LAZY_INSTANCE_INITIALIZER; | 88 g_present_thread_pool = LAZY_INSTANCE_INITIALIZER; |
89 | 89 |
90 PresentThread::PresentThread(const char* name) : base::Thread(name) { | 90 PresentThread::PresentThread(const char* name) : base::Thread(name) { |
91 } | 91 } |
92 | 92 |
93 PresentThread::~PresentThread() { | 93 PresentThread::~PresentThread() { |
94 Stop(); | 94 Stop(); |
95 } | 95 } |
96 | 96 |
| 97 void PresentThread::InitDevice() { |
| 98 if (device_) |
| 99 return; |
| 100 |
| 101 TRACE_EVENT0("surface", "PresentThread::Init"); |
| 102 d3d_module_.Reset(base::LoadNativeLibrary(FilePath(kD3D9ModuleName), NULL)); |
| 103 ResetDevice(); |
| 104 } |
| 105 |
97 void PresentThread::ResetDevice() { | 106 void PresentThread::ResetDevice() { |
98 TRACE_EVENT0("surface", "PresentThread::ResetDevice"); | 107 TRACE_EVENT0("surface", "PresentThread::ResetDevice"); |
99 | 108 |
100 // This will crash some Intel drivers but we can't render anything without | 109 // This will crash some Intel drivers but we can't render anything without |
101 // reseting the device, which would be disappointing. | 110 // reseting the device, which would be disappointing. |
102 query_ = NULL; | 111 query_ = NULL; |
103 device_ = NULL; | 112 device_ = NULL; |
104 | 113 |
105 Direct3DCreate9ExFunc create_func = reinterpret_cast<Direct3DCreate9ExFunc>( | 114 Direct3DCreate9ExFunc create_func = reinterpret_cast<Direct3DCreate9ExFunc>( |
106 d3d_module_.GetFunctionPointer(kCreate3D9DeviceExName)); | 115 d3d_module_.GetFunctionPointer(kCreate3D9DeviceExName)); |
(...skipping 30 matching lines...) Expand all Loading... |
137 NULL, | 146 NULL, |
138 device_.Receive()); | 147 device_.Receive()); |
139 if (FAILED(hr)) | 148 if (FAILED(hr)) |
140 return; | 149 return; |
141 | 150 |
142 hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, query_.Receive()); | 151 hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, query_.Receive()); |
143 if (FAILED(hr)) | 152 if (FAILED(hr)) |
144 device_ = NULL; | 153 device_ = NULL; |
145 } | 154 } |
146 | 155 |
147 void PresentThread::Init() { | |
148 TRACE_EVENT0("surface", "PresentThread::Init"); | |
149 d3d_module_.Reset(base::LoadNativeLibrary(FilePath(kD3D9ModuleName), NULL)); | |
150 ResetDevice(); | |
151 } | |
152 | |
153 void PresentThread::CleanUp() { | 156 void PresentThread::CleanUp() { |
154 // The D3D device and query are leaked because destroying the associated D3D | 157 // The D3D device and query are leaked because destroying the associated D3D |
155 // query crashes some Intel drivers. | 158 // query crashes some Intel drivers. |
156 device_.Detach(); | 159 device_.Detach(); |
157 query_.Detach(); | 160 query_.Detach(); |
158 } | 161 } |
159 | 162 |
160 PresentThreadPool::PresentThreadPool() : next_thread_(0) { | 163 PresentThreadPool::PresentThreadPool() : next_thread_(0) { |
| 164 // Do this in the constructor so present_threads_ is initialized before any |
| 165 // other thread sees it. See LazyInstance documentation. |
| 166 for (int i = 0; i < kNumPresentThreads; ++i) { |
| 167 present_threads_[i].reset(new PresentThread( |
| 168 base::StringPrintf("PresentThread #%d", i).c_str())); |
| 169 present_threads_[i]->Start(); |
| 170 } |
161 } | 171 } |
162 | 172 |
163 PresentThread* PresentThreadPool::NextThread() { | 173 PresentThread* PresentThreadPool::NextThread() { |
164 next_thread_ = (next_thread_ + 1) % kNumPresentThreads; | 174 next_thread_ = (next_thread_ + 1) % kNumPresentThreads; |
165 if (!present_threads_[next_thread_].get()) { | |
166 present_threads_[next_thread_].reset(new PresentThread( | |
167 base::StringPrintf("PresentThread #%d", next_thread_).c_str())); | |
168 present_threads_[next_thread_]->Start(); | |
169 } | |
170 return present_threads_[next_thread_].get(); | 175 return present_threads_[next_thread_].get(); |
171 } | 176 } |
172 | 177 |
173 AcceleratedPresenter::AcceleratedPresenter() | 178 AcceleratedPresenter::AcceleratedPresenter() |
174 : present_thread_(g_present_thread_pool.Pointer()->NextThread()) { | 179 : present_thread_(g_present_thread_pool.Pointer()->NextThread()) { |
175 } | 180 } |
176 | 181 |
177 void AcceleratedPresenter::AsyncPresentAndAcknowledge( | 182 void AcceleratedPresenter::AsyncPresentAndAcknowledge( |
178 gfx::NativeWindow window, | 183 gfx::NativeWindow window, |
179 const gfx::Size& size, | 184 const gfx::Size& size, |
180 int64 surface_id, | 185 int64 surface_id, |
181 const base::Callback<void(bool)>& completion_task) { | 186 const base::Callback<void(bool)>& completion_task) { |
182 present_thread_->message_loop()->PostTask( | 187 present_thread_->message_loop()->PostTask( |
183 FROM_HERE, | 188 FROM_HERE, |
184 base::Bind(&AcceleratedPresenter::DoPresentAndAcknowledge, | 189 base::Bind(&AcceleratedPresenter::DoPresentAndAcknowledge, |
185 this, | 190 this, |
186 window, | 191 window, |
187 size, | 192 size, |
188 surface_id, | 193 surface_id, |
189 completion_task)); | 194 completion_task)); |
190 } | 195 } |
191 | 196 |
192 bool AcceleratedPresenter::Present(gfx::NativeWindow window) { | 197 bool AcceleratedPresenter::Present(gfx::NativeWindow window) { |
193 TRACE_EVENT0("surface", "Present"); | 198 TRACE_EVENT0("surface", "Present"); |
194 | 199 |
195 HRESULT hr; | 200 HRESULT hr; |
196 | 201 |
197 base::AutoLock locked(lock_); | 202 base::AutoLock locked(lock_); |
198 | 203 |
199 // Signal the caller to recomposite if the presenter has been suspended. | 204 // Signal the caller to recomposite if the presenter has been suspended or no |
| 205 // surface has ever been presented. |
200 if (!swap_chain_) | 206 if (!swap_chain_) |
201 return false; | 207 return false; |
202 | 208 |
203 RECT rect = { | 209 RECT rect = { |
204 0, 0, | 210 0, 0, |
205 size_.width(), size_.height() | 211 size_.width(), size_.height() |
206 }; | 212 }; |
207 | 213 |
208 { | 214 { |
209 TRACE_EVENT0("surface", "PresentEx"); | 215 TRACE_EVENT0("surface", "PresentEx"); |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
259 gfx::NativeWindow window, | 265 gfx::NativeWindow window, |
260 const gfx::Size& size, | 266 const gfx::Size& size, |
261 int64 surface_id, | 267 int64 surface_id, |
262 const base::Callback<void(bool)>& completion_task) { | 268 const base::Callback<void(bool)>& completion_task) { |
263 TRACE_EVENT1("surface", "DoPresentAndAcknowledge", "surface_id", surface_id); | 269 TRACE_EVENT1("surface", "DoPresentAndAcknowledge", "surface_id", surface_id); |
264 | 270 |
265 HRESULT hr; | 271 HRESULT hr; |
266 | 272 |
267 base::AutoLock locked(lock_); | 273 base::AutoLock locked(lock_); |
268 | 274 |
| 275 // Initialize the device lazily since calling Direct3D can crash bots. |
| 276 present_thread_->InitDevice(); |
| 277 |
| 278 // A surface with ID zero is presented even when shared surfaces are not in |
| 279 // use for synchronization purposes. In that case, just acknowledge. |
| 280 if (!surface_id) { |
| 281 if (!completion_task.is_null()) |
| 282 completion_task.Run(true); |
| 283 return; |
| 284 } |
| 285 |
269 if (!present_thread_->device()) { | 286 if (!present_thread_->device()) { |
270 if (!completion_task.is_null()) | 287 if (!completion_task.is_null()) |
271 completion_task.Run(false); | 288 completion_task.Run(false); |
272 return; | 289 return; |
273 } | 290 } |
274 | 291 |
275 // Ensure the task is always run and while the lock is taken. | 292 // Ensure the task is always run and while the lock is taken. |
276 base::ScopedClosureRunner scoped_completion_runner(base::Bind(completion_task, | 293 base::ScopedClosureRunner scoped_completion_runner(base::Bind(completion_task, |
277 true)); | 294 true)); |
278 | 295 |
(...skipping 22 matching lines...) Expand all Loading... |
301 parameters.SwapEffect = D3DSWAPEFFECT_COPY; | 318 parameters.SwapEffect = D3DSWAPEFFECT_COPY; |
302 | 319 |
303 swap_chain_ = NULL; | 320 swap_chain_ = NULL; |
304 HRESULT hr = present_thread_->device()->CreateAdditionalSwapChain( | 321 HRESULT hr = present_thread_->device()->CreateAdditionalSwapChain( |
305 ¶meters, | 322 ¶meters, |
306 swap_chain_.Receive()); | 323 swap_chain_.Receive()); |
307 if (FAILED(hr)) | 324 if (FAILED(hr)) |
308 return; | 325 return; |
309 } | 326 } |
310 | 327 |
311 HANDLE handle = reinterpret_cast<HANDLE>(surface_id); | |
312 if (!handle) | |
313 return; | |
314 | |
315 base::win::ScopedComPtr<IDirect3DTexture9> source_texture; | 328 base::win::ScopedComPtr<IDirect3DTexture9> source_texture; |
316 { | 329 { |
317 TRACE_EVENT0("surface", "CreateTexture"); | 330 TRACE_EVENT0("surface", "CreateTexture"); |
| 331 HANDLE handle = reinterpret_cast<HANDLE>(surface_id); |
318 hr = present_thread_->device()->CreateTexture(size.width(), | 332 hr = present_thread_->device()->CreateTexture(size.width(), |
319 size.height(), | 333 size.height(), |
320 1, | 334 1, |
321 D3DUSAGE_RENDERTARGET, | 335 D3DUSAGE_RENDERTARGET, |
322 D3DFMT_A8R8G8B8, | 336 D3DFMT_A8R8G8B8, |
323 D3DPOOL_DEFAULT, | 337 D3DPOOL_DEFAULT, |
324 source_texture.Receive(), | 338 source_texture.Receive(), |
325 &handle); | 339 &handle); |
326 if (FAILED(hr)) | 340 if (FAILED(hr)) |
327 return; | 341 return; |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
394 if (FAILED(hr)) | 408 if (FAILED(hr)) |
395 present_thread_->ResetDevice(); | 409 present_thread_->ResetDevice(); |
396 } | 410 } |
397 } | 411 } |
398 | 412 |
399 void AcceleratedPresenter::DoSuspend() { | 413 void AcceleratedPresenter::DoSuspend() { |
400 base::AutoLock locked(lock_); | 414 base::AutoLock locked(lock_); |
401 swap_chain_ = NULL; | 415 swap_chain_ = NULL; |
402 } | 416 } |
403 | 417 |
404 AcceleratedSurface::AcceleratedSurface(){ | 418 AcceleratedSurface::AcceleratedSurface() |
| 419 : presenter_(new AcceleratedPresenter) { |
405 } | 420 } |
406 | 421 |
407 AcceleratedSurface::~AcceleratedSurface() { | 422 AcceleratedSurface::~AcceleratedSurface() { |
408 if (presenter_) { | 423 // Ensure that the swap chain is destroyed on the PresentThread in case |
409 // Ensure that the swap chain is destroyed on the PresentThread in case | 424 // there are still pending presents. |
410 // there are still pending presents. | 425 presenter_->Suspend(); |
411 presenter_->Suspend(); | 426 presenter_->WaitForPendingTasks(); |
412 presenter_->WaitForPendingTasks(); | |
413 } | |
414 } | 427 } |
415 | 428 |
416 void AcceleratedSurface::AsyncPresentAndAcknowledge( | 429 void AcceleratedSurface::AsyncPresentAndAcknowledge( |
417 HWND window, | 430 HWND window, |
418 const gfx::Size& size, | 431 const gfx::Size& size, |
419 int64 surface_id, | 432 int64 surface_id, |
420 const base::Callback<void(bool)>& completion_task) { | 433 const base::Callback<void(bool)>& completion_task) { |
421 if (!surface_id) | 434 if (!surface_id) |
422 return; | 435 return; |
423 | 436 |
424 if (!presenter_) | |
425 presenter_ = new AcceleratedPresenter; | |
426 | |
427 presenter_->AsyncPresentAndAcknowledge(window, | 437 presenter_->AsyncPresentAndAcknowledge(window, |
428 size, | 438 size, |
429 surface_id, | 439 surface_id, |
430 completion_task); | 440 completion_task); |
431 } | 441 } |
432 | 442 |
433 bool AcceleratedSurface::Present(HWND window) { | 443 bool AcceleratedSurface::Present(HWND window) { |
434 if (presenter_) | 444 return presenter_->Present(window); |
435 return presenter_->Present(window); | |
436 else | |
437 return false; | |
438 } | 445 } |
439 | 446 |
440 void AcceleratedSurface::Suspend() { | 447 void AcceleratedSurface::Suspend() { |
441 if (presenter_) | 448 presenter_->Suspend(); |
442 presenter_->Suspend(); | |
443 } | 449 } |
444 | 450 |
OLD | NEW |