OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "ui/gfx/surface/accelerated_surface_win.h" | |
6 | |
7 #include <windows.h> | |
8 #include <algorithm> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/bind_helpers.h" | |
12 #include "base/callback.h" | |
13 #include "base/command_line.h" | |
14 #include "base/string_number_conversions.h" | |
15 #include "base/time.h" | |
16 #include "base/debug/trace_event.h" | |
17 #include "base/file_path.h" | |
18 #include "base/lazy_instance.h" | |
19 #include "base/memory/scoped_ptr.h" | |
20 #include "base/scoped_native_library.h" | |
21 #include "base/stringprintf.h" | |
22 #include "base/synchronization/waitable_event.h" | |
23 #include "base/threading/thread.h" | |
24 #include "base/threading/thread_restrictions.h" | |
25 #include "base/tracked_objects.h" | |
26 #include "base/win/wrapped_window_proc.h" | |
27 #include "ui/base/win/hwnd_util.h" | |
28 #include "ui/gfx/gl/gl_switches.h" | |
29 | |
30 namespace { | |
31 | |
32 typedef HRESULT (WINAPI *Direct3DCreate9ExFunc)(UINT sdk_version, | |
33 IDirect3D9Ex **d3d); | |
34 | |
35 const wchar_t kD3D9ModuleName[] = L"d3d9.dll"; | |
36 const char kCreate3D9DeviceExName[] = "Direct3DCreate9Ex"; | |
37 | |
38 UINT GetPresentationInterval() { | |
39 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync)) | |
40 return D3DPRESENT_INTERVAL_IMMEDIATE; | |
41 else | |
42 return D3DPRESENT_INTERVAL_ONE; | |
43 } | |
44 | |
45 // Calculate the number necessary to transform |source_size| into |dest_size| | |
46 // by repeating downsampling of the image of |source_size| by a factor no more | |
47 // than 2. | |
48 int GetResampleCount(const gfx::Size& source_size, const gfx::Size& dest_size) { | |
49 int width_count = 0; | |
50 int width = source_size.width(); | |
51 while (width > dest_size.width()) { | |
52 ++width_count; | |
53 width >>= 1; | |
54 } | |
55 int height_count = 0; | |
56 int height = source_size.height(); | |
57 while (height > dest_size.height()) { | |
58 ++height_count; | |
59 height >>= 1; | |
60 } | |
61 return std::max(width_count, height_count); | |
62 } | |
63 | |
64 // Returns half the size of |size| no smaller than |min_size|. | |
65 gfx::Size GetHalfSizeNoLessThan(const gfx::Size& size, | |
66 const gfx::Size& min_size) { | |
67 return gfx::Size(std::max(min_size.width(), size.width() / 2), | |
68 std::max(min_size.height(), size.height() / 2)); | |
69 } | |
70 | |
71 bool CreateTemporarySurface(IDirect3DDevice9* device, | |
72 const gfx::Size& size, | |
73 IDirect3DSurface9** surface) { | |
74 HRESULT hr = device->CreateRenderTarget( | |
75 size.width(), | |
76 size.height(), | |
77 D3DFMT_A8R8G8B8, | |
78 D3DMULTISAMPLE_NONE, | |
79 0, | |
80 TRUE, | |
81 surface, | |
82 NULL); | |
83 return SUCCEEDED(hr); | |
84 } | |
85 | |
86 } // namespace anonymous | |
87 | |
88 // A PresentThread is a thread that is dedicated to presenting surfaces to a | |
89 // window. It owns a Direct3D device and a Direct3D query for this purpose. | |
90 class PresentThread : public base::Thread, | |
91 public base::RefCountedThreadSafe<PresentThread> { | |
92 public: | |
93 explicit PresentThread(const char* name); | |
94 | |
95 IDirect3DDevice9Ex* device() { return device_.get(); } | |
96 IDirect3DQuery9* query() { return query_.get(); } | |
97 | |
98 void InitDevice(); | |
99 void ResetDevice(); | |
100 | |
101 protected: | |
102 virtual void CleanUp(); | |
103 | |
104 private: | |
105 friend class base::RefCountedThreadSafe<PresentThread>; | |
106 | |
107 ~PresentThread(); | |
108 | |
109 base::ScopedNativeLibrary d3d_module_; | |
110 base::win::ScopedComPtr<IDirect3DDevice9Ex> device_; | |
111 | |
112 // This query is used to wait until a certain amount of progress has been | |
113 // made by the GPU and it is safe for the producer to modify its shared | |
114 // texture again. | |
115 base::win::ScopedComPtr<IDirect3DQuery9> query_; | |
116 | |
117 DISALLOW_COPY_AND_ASSIGN(PresentThread); | |
118 }; | |
119 | |
120 // There is a fixed sized pool of PresentThreads and therefore the maximum | |
121 // number of Direct3D devices owned by those threads is bounded. | |
122 class PresentThreadPool { | |
123 public: | |
124 static const int kNumPresentThreads = 4; | |
125 | |
126 PresentThreadPool(); | |
127 PresentThread* NextThread(); | |
128 | |
129 private: | |
130 int next_thread_; | |
131 scoped_refptr<PresentThread> present_threads_[kNumPresentThreads]; | |
132 | |
133 DISALLOW_COPY_AND_ASSIGN(PresentThreadPool); | |
134 }; | |
135 | |
136 // A thread safe map of presenters by surface ID that returns presenters via | |
137 // a scoped_refptr to keep them alive while they are referenced. | |
138 class AcceleratedPresenterMap { | |
139 public: | |
140 AcceleratedPresenterMap(); | |
141 scoped_refptr<AcceleratedPresenter> CreatePresenter(gfx::NativeWindow window); | |
142 void RemovePresenter(const scoped_refptr<AcceleratedPresenter>& presenter); | |
143 scoped_refptr<AcceleratedPresenter> GetPresenter(gfx::NativeWindow window); | |
144 private: | |
145 base::Lock lock_; | |
146 typedef std::map<gfx::NativeWindow, AcceleratedPresenter*> PresenterMap; | |
147 PresenterMap presenters_; | |
148 DISALLOW_COPY_AND_ASSIGN(AcceleratedPresenterMap); | |
149 }; | |
150 | |
151 base::LazyInstance<PresentThreadPool> | |
152 g_present_thread_pool = LAZY_INSTANCE_INITIALIZER; | |
153 | |
154 base::LazyInstance<AcceleratedPresenterMap> | |
155 g_accelerated_presenter_map = LAZY_INSTANCE_INITIALIZER; | |
156 | |
157 PresentThread::PresentThread(const char* name) : base::Thread(name) { | |
158 } | |
159 | |
160 void PresentThread::InitDevice() { | |
161 if (device_) | |
162 return; | |
163 | |
164 TRACE_EVENT0("surface", "PresentThread::Init"); | |
165 d3d_module_.Reset(base::LoadNativeLibrary(FilePath(kD3D9ModuleName), NULL)); | |
166 ResetDevice(); | |
167 } | |
168 | |
169 void PresentThread::ResetDevice() { | |
170 TRACE_EVENT0("surface", "PresentThread::ResetDevice"); | |
171 | |
172 // This will crash some Intel drivers but we can't render anything without | |
173 // reseting the device, which would be disappointing. | |
174 query_ = NULL; | |
175 device_ = NULL; | |
176 | |
177 Direct3DCreate9ExFunc create_func = reinterpret_cast<Direct3DCreate9ExFunc>( | |
178 d3d_module_.GetFunctionPointer(kCreate3D9DeviceExName)); | |
179 if (!create_func) | |
180 return; | |
181 | |
182 base::win::ScopedComPtr<IDirect3D9Ex> d3d; | |
183 HRESULT hr = create_func(D3D_SDK_VERSION, d3d.Receive()); | |
184 if (FAILED(hr)) | |
185 return; | |
186 | |
187 // Any old window will do to create the device. In practice the window to | |
188 // present to is an argument to IDirect3DDevice9::Present. | |
189 HWND window = GetShellWindow(); | |
190 | |
191 D3DPRESENT_PARAMETERS parameters = { 0 }; | |
192 parameters.BackBufferWidth = 1; | |
193 parameters.BackBufferHeight = 1; | |
194 parameters.BackBufferCount = 1; | |
195 parameters.BackBufferFormat = D3DFMT_A8R8G8B8; | |
196 parameters.hDeviceWindow = window; | |
197 parameters.Windowed = TRUE; | |
198 parameters.Flags = 0; | |
199 parameters.PresentationInterval = GetPresentationInterval(); | |
200 parameters.SwapEffect = D3DSWAPEFFECT_COPY; | |
201 | |
202 hr = d3d->CreateDeviceEx( | |
203 D3DADAPTER_DEFAULT, | |
204 D3DDEVTYPE_HAL, | |
205 window, | |
206 D3DCREATE_FPU_PRESERVE | D3DCREATE_SOFTWARE_VERTEXPROCESSING | | |
207 D3DCREATE_DISABLE_PSGP_THREADING | D3DCREATE_MULTITHREADED, | |
208 ¶meters, | |
209 NULL, | |
210 device_.Receive()); | |
211 if (FAILED(hr)) | |
212 return; | |
213 | |
214 hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, query_.Receive()); | |
215 if (FAILED(hr)) | |
216 device_ = NULL; | |
217 } | |
218 | |
219 void PresentThread::CleanUp() { | |
220 // The D3D device and query are leaked because destroying the associated D3D | |
221 // query crashes some Intel drivers. | |
222 device_.Detach(); | |
223 query_.Detach(); | |
224 } | |
225 | |
226 PresentThread::~PresentThread() { | |
227 Stop(); | |
228 } | |
229 | |
230 PresentThreadPool::PresentThreadPool() : next_thread_(0) { | |
231 // Do this in the constructor so present_threads_ is initialized before any | |
232 // other thread sees it. See LazyInstance documentation. | |
233 for (int i = 0; i < kNumPresentThreads; ++i) { | |
234 present_threads_[i] = new PresentThread( | |
235 base::StringPrintf("PresentThread #%d", i).c_str()); | |
236 present_threads_[i]->Start(); | |
237 } | |
238 } | |
239 | |
240 PresentThread* PresentThreadPool::NextThread() { | |
241 next_thread_ = (next_thread_ + 1) % kNumPresentThreads; | |
242 return present_threads_[next_thread_].get(); | |
243 } | |
244 | |
245 AcceleratedPresenterMap::AcceleratedPresenterMap() { | |
246 } | |
247 | |
248 scoped_refptr<AcceleratedPresenter> AcceleratedPresenterMap::CreatePresenter( | |
249 gfx::NativeWindow window) { | |
250 scoped_refptr<AcceleratedPresenter> presenter( | |
251 new AcceleratedPresenter(window)); | |
252 | |
253 base::AutoLock locked(lock_); | |
254 DCHECK(presenters_.find(window) == presenters_.end()); | |
255 presenters_[window] = presenter.get(); | |
256 | |
257 return presenter; | |
258 } | |
259 | |
260 void AcceleratedPresenterMap::RemovePresenter( | |
261 const scoped_refptr<AcceleratedPresenter>& presenter) { | |
262 base::AutoLock locked(lock_); | |
263 for (PresenterMap::iterator it = presenters_.begin(); | |
264 it != presenters_.end(); | |
265 ++it) { | |
266 if (it->second == presenter.get()) { | |
267 presenters_.erase(it); | |
268 return; | |
269 } | |
270 } | |
271 | |
272 NOTREACHED(); | |
273 } | |
274 | |
275 scoped_refptr<AcceleratedPresenter> AcceleratedPresenterMap::GetPresenter( | |
276 gfx::NativeWindow window) { | |
277 base::AutoLock locked(lock_); | |
278 PresenterMap::iterator it = presenters_.find(window); | |
279 if (it == presenters_.end()) | |
280 return scoped_refptr<AcceleratedPresenter>(); | |
281 | |
282 return it->second; | |
283 } | |
284 | |
285 AcceleratedPresenter::AcceleratedPresenter(gfx::NativeWindow window) | |
286 : present_thread_(g_present_thread_pool.Pointer()->NextThread()), | |
287 window_(window), | |
288 event_(false, false) { | |
289 } | |
290 | |
291 scoped_refptr<AcceleratedPresenter> AcceleratedPresenter::GetForWindow( | |
292 gfx::NativeWindow window) { | |
293 return g_accelerated_presenter_map.Pointer()->GetPresenter(window); | |
294 } | |
295 | |
296 void AcceleratedPresenter::AsyncPresentAndAcknowledge( | |
297 const gfx::Size& size, | |
298 int64 surface_handle, | |
299 const base::Callback<void(bool)>& completion_task) { | |
300 if (!surface_handle) { | |
301 completion_task.Run(true); | |
302 return; | |
303 } | |
304 | |
305 present_thread_->message_loop()->PostTask( | |
306 FROM_HERE, | |
307 base::Bind(&AcceleratedPresenter::DoPresentAndAcknowledge, | |
308 this, | |
309 size, | |
310 surface_handle, | |
311 completion_task)); | |
312 } | |
313 | |
314 bool AcceleratedPresenter::Present() { | |
315 TRACE_EVENT0("surface", "Present"); | |
316 | |
317 bool result; | |
318 | |
319 present_thread_->message_loop()->PostTask( | |
320 FROM_HERE, | |
321 base::Bind(&AcceleratedPresenter::DoPresent, | |
322 this, | |
323 &result)); | |
324 // http://crbug.com/125391 | |
325 base::ThreadRestrictions::ScopedAllowWait allow_wait; | |
326 event_.Wait(); | |
327 return result; | |
328 } | |
329 | |
330 void AcceleratedPresenter::DoPresent(bool* result) | |
331 { | |
332 *result = DoRealPresent(); | |
333 event_.Signal(); | |
334 } | |
335 | |
336 bool AcceleratedPresenter::DoRealPresent() | |
337 { | |
338 TRACE_EVENT0("surface", "DoRealPresent"); | |
339 HRESULT hr; | |
340 | |
341 base::AutoLock locked(lock_); | |
342 | |
343 // Signal the caller to recomposite if the presenter has been suspended or no | |
344 // surface has ever been presented. | |
345 if (!swap_chain_) | |
346 return false; | |
347 | |
348 // If invalidated, do nothing. The window is gone. | |
349 if (!window_) | |
350 return true; | |
351 | |
352 RECT rect = { | |
353 0, 0, | |
354 size_.width(), size_.height() | |
355 }; | |
356 | |
357 { | |
358 TRACE_EVENT0("surface", "PresentEx"); | |
359 hr = swap_chain_->Present(&rect, | |
360 &rect, | |
361 window_, | |
362 NULL, | |
363 D3DPRESENT_INTERVAL_IMMEDIATE); | |
364 if (FAILED(hr)) | |
365 return false; | |
366 } | |
367 | |
368 return true; | |
369 } | |
370 | |
371 bool AcceleratedPresenter::CopyTo(const gfx::Size& size, void* buf) { | |
372 base::AutoLock locked(lock_); | |
373 | |
374 if (!swap_chain_) | |
375 return false; | |
376 | |
377 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer; | |
378 HRESULT hr = swap_chain_->GetBackBuffer(0, | |
379 D3DBACKBUFFER_TYPE_MONO, | |
380 back_buffer.Receive()); | |
381 if (FAILED(hr)) | |
382 return false; | |
383 | |
384 D3DSURFACE_DESC desc; | |
385 hr = back_buffer->GetDesc(&desc); | |
386 if (FAILED(hr)) | |
387 return false; | |
388 | |
389 const gfx::Size back_buffer_size(desc.Width, desc.Height); | |
390 if (back_buffer_size.IsEmpty()) | |
391 return false; | |
392 | |
393 // Set up intermediate buffers needed for downsampling. | |
394 const int resample_count = | |
395 GetResampleCount(gfx::Size(desc.Width, desc.Height), size); | |
396 base::win::ScopedComPtr<IDirect3DSurface9> final_surface; | |
397 base::win::ScopedComPtr<IDirect3DSurface9> temp_buffer[2]; | |
398 if (resample_count == 0) | |
399 final_surface = back_buffer; | |
400 if (resample_count > 0) { | |
401 if (!CreateTemporarySurface(present_thread_->device(), | |
402 size, | |
403 final_surface.Receive())) | |
404 return false; | |
405 } | |
406 const gfx::Size half_size = GetHalfSizeNoLessThan(back_buffer_size, size); | |
407 if (resample_count > 1) { | |
408 if (!CreateTemporarySurface(present_thread_->device(), | |
409 half_size, | |
410 temp_buffer[0].Receive())) | |
411 return false; | |
412 } | |
413 if (resample_count > 2) { | |
414 const gfx::Size quarter_size = GetHalfSizeNoLessThan(half_size, size); | |
415 if (!CreateTemporarySurface(present_thread_->device(), | |
416 quarter_size, | |
417 temp_buffer[1].Receive())) | |
418 return false; | |
419 } | |
420 | |
421 // Repeat downsampling the surface until its size becomes identical to | |
422 // |size|. We keep the factor of each downsampling no more than two because | |
423 // using a factor more than two can introduce aliasing. | |
424 gfx::Size read_size = back_buffer_size; | |
425 gfx::Size write_size = half_size; | |
426 int read_buffer_index = 1; | |
427 int write_buffer_index = 0; | |
428 for (int i = 0; i < resample_count; ++i) { | |
429 base::win::ScopedComPtr<IDirect3DSurface9> read_buffer = | |
430 (i == 0) ? back_buffer : temp_buffer[read_buffer_index]; | |
431 base::win::ScopedComPtr<IDirect3DSurface9> write_buffer = | |
432 (i == resample_count - 1) ? final_surface : | |
433 temp_buffer[write_buffer_index]; | |
434 RECT read_rect = {0, 0, read_size.width(), read_size.height()}; | |
435 RECT write_rect = {0, 0, write_size.width(), write_size.height()}; | |
436 hr = present_thread_->device()->StretchRect(read_buffer, | |
437 &read_rect, | |
438 write_buffer, | |
439 &write_rect, | |
440 D3DTEXF_LINEAR); | |
441 if (FAILED(hr)) | |
442 return false; | |
443 read_size = write_size; | |
444 write_size = GetHalfSizeNoLessThan(write_size, size); | |
445 std::swap(read_buffer_index, write_buffer_index); | |
446 } | |
447 | |
448 DCHECK(size == read_size); | |
449 | |
450 base::win::ScopedComPtr<IDirect3DSurface9> temp_surface; | |
451 HANDLE handle = reinterpret_cast<HANDLE>(buf); | |
452 hr = present_thread_->device()->CreateOffscreenPlainSurface( | |
453 size.width(), | |
454 size.height(), | |
455 D3DFMT_A8R8G8B8, | |
456 D3DPOOL_SYSTEMMEM, | |
457 temp_surface.Receive(), | |
458 &handle); | |
459 if (FAILED(hr)) | |
460 return false; | |
461 | |
462 // Copy the data in the temporary buffer to the surface backed by |buf|. | |
463 hr = present_thread_->device()->GetRenderTargetData(final_surface, | |
464 temp_surface); | |
465 if (FAILED(hr)) | |
466 return false; | |
467 | |
468 return true; | |
469 } | |
470 | |
471 void AcceleratedPresenter::Suspend() { | |
472 present_thread_->message_loop()->PostTask( | |
473 FROM_HERE, | |
474 base::Bind(&AcceleratedPresenter::DoSuspend, | |
475 this)); | |
476 } | |
477 | |
478 void AcceleratedPresenter::ReleaseSurface() { | |
479 present_thread_->message_loop()->PostTask( | |
480 FROM_HERE, | |
481 base::Bind(&AcceleratedPresenter::DoReleaseSurface, | |
482 this)); | |
483 } | |
484 | |
485 void AcceleratedPresenter::Invalidate() { | |
486 // Make any pending or future presentation tasks do nothing. Once the last | |
487 // last pending task has been ignored, the reference count on the presenter | |
488 // will go to zero and the presenter, and potentially also the present thread | |
489 // it has a reference count on, will be destroyed. | |
490 base::AutoLock locked(lock_); | |
491 window_ = NULL; | |
492 } | |
493 | |
494 AcceleratedPresenter::~AcceleratedPresenter() { | |
495 } | |
496 | |
497 static base::TimeDelta GetSwapDelay() { | |
498 CommandLine* cmd_line = CommandLine::ForCurrentProcess(); | |
499 int delay = 0; | |
500 if (cmd_line->HasSwitch(switches::kGpuSwapDelay)) { | |
501 base::StringToInt(cmd_line->GetSwitchValueNative( | |
502 switches::kGpuSwapDelay).c_str(), &delay); | |
503 } | |
504 return base::TimeDelta::FromMilliseconds(delay); | |
505 } | |
506 | |
507 void AcceleratedPresenter::DoPresentAndAcknowledge( | |
508 const gfx::Size& size, | |
509 int64 surface_handle, | |
510 const base::Callback<void(bool)>& completion_task) { | |
511 TRACE_EVENT1( | |
512 "surface", "DoPresentAndAcknowledge", "surface_handle", surface_handle); | |
513 | |
514 HRESULT hr; | |
515 | |
516 base::AutoLock locked(lock_); | |
517 | |
518 // Initialize the device lazily since calling Direct3D can crash bots. | |
519 present_thread_->InitDevice(); | |
520 | |
521 if (!present_thread_->device()) { | |
522 if (!completion_task.is_null()) | |
523 completion_task.Run(false); | |
524 return; | |
525 } | |
526 | |
527 // Ensure the task is always run and while the lock is taken. | |
528 base::ScopedClosureRunner scoped_completion_runner(base::Bind(completion_task, | |
529 true)); | |
530 | |
531 // If invalidated, do nothing, the window is gone. | |
532 if (!window_) | |
533 return; | |
534 | |
535 // Round up size so the swap chain is not continuously resized with the | |
536 // surface, which could lead to memory fragmentation. | |
537 const int kRound = 64; | |
538 gfx::Size quantized_size( | |
539 std::max(1, (size.width() + kRound - 1) / kRound * kRound), | |
540 std::max(1, (size.height() + kRound - 1) / kRound * kRound)); | |
541 | |
542 // Ensure the swap chain exists and is the same size (rounded up) as the | |
543 // surface to be presented. | |
544 if (!swap_chain_ || size_ != quantized_size) { | |
545 TRACE_EVENT0("surface", "CreateAdditionalSwapChain"); | |
546 size_ = quantized_size; | |
547 | |
548 D3DPRESENT_PARAMETERS parameters = { 0 }; | |
549 parameters.BackBufferWidth = quantized_size.width(); | |
550 parameters.BackBufferHeight = quantized_size.height(); | |
551 parameters.BackBufferCount = 1; | |
552 parameters.BackBufferFormat = D3DFMT_A8R8G8B8; | |
553 parameters.hDeviceWindow = GetShellWindow(); | |
554 parameters.Windowed = TRUE; | |
555 parameters.Flags = 0; | |
556 parameters.PresentationInterval = GetPresentationInterval(); | |
557 parameters.SwapEffect = D3DSWAPEFFECT_COPY; | |
558 | |
559 swap_chain_ = NULL; | |
560 HRESULT hr = present_thread_->device()->CreateAdditionalSwapChain( | |
561 ¶meters, | |
562 swap_chain_.Receive()); | |
563 if (FAILED(hr)) | |
564 return; | |
565 } | |
566 | |
567 if (!source_texture_.get()) { | |
568 TRACE_EVENT0("surface", "CreateTexture"); | |
569 HANDLE handle = reinterpret_cast<HANDLE>(surface_handle); | |
570 hr = present_thread_->device()->CreateTexture(size.width(), | |
571 size.height(), | |
572 1, | |
573 D3DUSAGE_RENDERTARGET, | |
574 D3DFMT_A8R8G8B8, | |
575 D3DPOOL_DEFAULT, | |
576 source_texture_.Receive(), | |
577 &handle); | |
578 if (FAILED(hr)) | |
579 return; | |
580 } | |
581 | |
582 base::win::ScopedComPtr<IDirect3DSurface9> source_surface; | |
583 hr = source_texture_->GetSurfaceLevel(0, source_surface.Receive()); | |
584 if (FAILED(hr)) | |
585 return; | |
586 | |
587 base::win::ScopedComPtr<IDirect3DSurface9> dest_surface; | |
588 hr = swap_chain_->GetBackBuffer(0, | |
589 D3DBACKBUFFER_TYPE_MONO, | |
590 dest_surface.Receive()); | |
591 if (FAILED(hr)) | |
592 return; | |
593 | |
594 RECT rect = { | |
595 0, 0, | |
596 size.width(), size.height() | |
597 }; | |
598 | |
599 { | |
600 TRACE_EVENT0("surface", "StretchRect"); | |
601 hr = present_thread_->device()->StretchRect(source_surface, | |
602 &rect, | |
603 dest_surface, | |
604 &rect, | |
605 D3DTEXF_NONE); | |
606 if (FAILED(hr)) | |
607 return; | |
608 } | |
609 | |
610 hr = present_thread_->query()->Issue(D3DISSUE_END); | |
611 if (FAILED(hr)) | |
612 return; | |
613 | |
614 // Flush so the StretchRect can be processed by the GPU while the window is | |
615 // being resized. | |
616 present_thread_->query()->GetData(NULL, 0, D3DGETDATA_FLUSH); | |
617 | |
618 ::SetWindowPos( | |
619 window_, | |
620 NULL, | |
621 0, 0, | |
622 size.width(), size.height(), | |
623 SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE |SWP_NOOWNERZORDER | | |
624 SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOSENDCHANGING | | |
625 SWP_ASYNCWINDOWPOS | SWP_NOZORDER); | |
626 | |
627 // Wait for the StretchRect to complete before notifying the GPU process | |
628 // that it is safe to write to its backing store again. | |
629 { | |
630 TRACE_EVENT0("surface", "spin"); | |
631 do { | |
632 hr = present_thread_->query()->GetData(NULL, 0, D3DGETDATA_FLUSH); | |
633 | |
634 if (hr == S_FALSE) | |
635 Sleep(1); | |
636 } while (hr == S_FALSE); | |
637 } | |
638 | |
639 static const base::TimeDelta swap_delay = GetSwapDelay(); | |
640 if (swap_delay.ToInternalValue()) | |
641 base::PlatformThread::Sleep(swap_delay); | |
642 | |
643 { | |
644 TRACE_EVENT0("surface", "Present"); | |
645 hr = swap_chain_->Present(&rect, &rect, window_, NULL, 0); | |
646 if (FAILED(hr) && | |
647 FAILED(present_thread_->device()->CheckDeviceState(window_))) { | |
648 present_thread_->ResetDevice(); | |
649 } | |
650 } | |
651 } | |
652 | |
653 void AcceleratedPresenter::DoSuspend() { | |
654 base::AutoLock locked(lock_); | |
655 swap_chain_ = NULL; | |
656 } | |
657 | |
658 void AcceleratedPresenter::DoReleaseSurface() { | |
659 base::AutoLock locked(lock_); | |
660 source_texture_.Release(); | |
661 } | |
662 | |
663 AcceleratedSurface::AcceleratedSurface(gfx::NativeWindow window) | |
664 : presenter_(g_accelerated_presenter_map.Pointer()->CreatePresenter( | |
665 window)) { | |
666 } | |
667 | |
668 AcceleratedSurface::~AcceleratedSurface() { | |
669 g_accelerated_presenter_map.Pointer()->RemovePresenter(presenter_); | |
670 presenter_->Invalidate(); | |
671 } | |
672 | |
673 bool AcceleratedSurface::Present() { | |
674 return presenter_->Present(); | |
675 } | |
676 | |
677 bool AcceleratedSurface::CopyTo(const gfx::Size& size, void* buf) { | |
678 return presenter_->CopyTo(size, buf); | |
679 } | |
680 | |
681 void AcceleratedSurface::Suspend() { | |
682 presenter_->Suspend(); | |
683 } | |
OLD | NEW |