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 #include <algorithm> |
8 | 9 |
9 #include "base/bind.h" | 10 #include "base/bind.h" |
10 #include "base/bind_helpers.h" | 11 #include "base/bind_helpers.h" |
11 #include "base/callback.h" | 12 #include "base/callback.h" |
12 #include "base/command_line.h" | 13 #include "base/command_line.h" |
13 #include "base/debug/trace_event.h" | 14 #include "base/debug/trace_event.h" |
14 #include "base/file_path.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/scoped_native_library.h" | 18 #include "base/scoped_native_library.h" |
(...skipping 13 matching lines...) Expand all Loading... |
31 const wchar_t kD3D9ModuleName[] = L"d3d9.dll"; | 32 const wchar_t kD3D9ModuleName[] = L"d3d9.dll"; |
32 const char kCreate3D9DeviceExName[] = "Direct3DCreate9Ex"; | 33 const char kCreate3D9DeviceExName[] = "Direct3DCreate9Ex"; |
33 | 34 |
34 UINT GetPresentationInterval() { | 35 UINT GetPresentationInterval() { |
35 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync)) | 36 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync)) |
36 return D3DPRESENT_INTERVAL_IMMEDIATE; | 37 return D3DPRESENT_INTERVAL_IMMEDIATE; |
37 else | 38 else |
38 return D3DPRESENT_INTERVAL_ONE; | 39 return D3DPRESENT_INTERVAL_ONE; |
39 } | 40 } |
40 | 41 |
| 42 // Calculate the number necessary to transform |source_size| into |dest_size| |
| 43 // by repeating downsampling of the image of |source_size| by a factor no more |
| 44 // than 2. |
| 45 int GetResampleCount(const gfx::Size& source_size, const gfx::Size& dest_size) { |
| 46 int width_count = 0; |
| 47 int width = source_size.width(); |
| 48 while (width > dest_size.width()) { |
| 49 ++width_count; |
| 50 width >>= 1; |
| 51 } |
| 52 int height_count = 0; |
| 53 int height = source_size.height(); |
| 54 while (height > dest_size.height()) { |
| 55 ++height_count; |
| 56 height >>= 1; |
| 57 } |
| 58 return std::max(width_count, height_count); |
| 59 } |
| 60 |
| 61 // Returns half the size of |size| no smaller than |min_size|. |
| 62 gfx::Size GetHalfSizeNoLessThan(const gfx::Size& size, |
| 63 const gfx::Size& min_size) { |
| 64 return gfx::Size(std::max(min_size.width(), size.width() / 2), |
| 65 std::max(min_size.height(), size.height() / 2)); |
| 66 } |
| 67 |
| 68 bool CreateTemporarySurface(IDirect3DDevice9* device, |
| 69 const gfx::Size& size, |
| 70 IDirect3DSurface9** surface) { |
| 71 HRESULT hr = device->CreateRenderTarget( |
| 72 size.width(), |
| 73 size.height(), |
| 74 D3DFMT_A8R8G8B8, |
| 75 D3DMULTISAMPLE_NONE, |
| 76 0, |
| 77 TRUE, |
| 78 surface, |
| 79 NULL); |
| 80 return SUCCEEDED(hr); |
| 81 } |
| 82 |
41 } // namespace anonymous | 83 } // namespace anonymous |
42 | 84 |
43 // A PresentThread is a thread that is dedicated to presenting surfaces to a | 85 // 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. | 86 // window. It owns a Direct3D device and a Direct3D query for this purpose. |
45 class PresentThread : public base::Thread { | 87 class PresentThread : public base::Thread { |
46 public: | 88 public: |
47 PresentThread(const char* name); | 89 PresentThread(const char* name); |
48 ~PresentThread(); | 90 ~PresentThread(); |
49 | 91 |
50 IDirect3DDevice9* device() { return device_.get(); } | 92 IDirect3DDevice9* device() { return device_.get(); } |
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
233 hr = present_thread_->query()->GetData(NULL, 0, D3DGETDATA_FLUSH); | 275 hr = present_thread_->query()->GetData(NULL, 0, D3DGETDATA_FLUSH); |
234 | 276 |
235 if (hr == S_FALSE) | 277 if (hr == S_FALSE) |
236 Sleep(0); | 278 Sleep(0); |
237 } while (hr == S_FALSE); | 279 } while (hr == S_FALSE); |
238 } | 280 } |
239 | 281 |
240 return true; | 282 return true; |
241 } | 283 } |
242 | 284 |
| 285 bool AcceleratedPresenter::CopyTo(const gfx::Size& size, void* buf) { |
| 286 base::AutoLock locked(lock_); |
| 287 |
| 288 if (!swap_chain_) |
| 289 return false; |
| 290 |
| 291 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer; |
| 292 HRESULT hr = swap_chain_->GetBackBuffer(0, |
| 293 D3DBACKBUFFER_TYPE_MONO, |
| 294 back_buffer.Receive()); |
| 295 if (FAILED(hr)) |
| 296 return false; |
| 297 |
| 298 D3DSURFACE_DESC desc; |
| 299 hr = back_buffer->GetDesc(&desc); |
| 300 if (FAILED(hr)) |
| 301 return false; |
| 302 |
| 303 const gfx::Size back_buffer_size(desc.Width, desc.Height); |
| 304 if (back_buffer_size.IsEmpty()) |
| 305 return false; |
| 306 |
| 307 // Set up intermediate buffers needed for downsampling. |
| 308 const int resample_count = |
| 309 GetResampleCount(gfx::Size(desc.Width, desc.Height), size); |
| 310 base::win::ScopedComPtr<IDirect3DSurface9> final_surface; |
| 311 base::win::ScopedComPtr<IDirect3DSurface9> temp_buffer[2]; |
| 312 if (resample_count == 0) |
| 313 final_surface = back_buffer; |
| 314 if (resample_count > 0) { |
| 315 if (!CreateTemporarySurface(present_thread_->device(), |
| 316 size, |
| 317 final_surface.Receive())) |
| 318 return false; |
| 319 } |
| 320 const gfx::Size half_size = GetHalfSizeNoLessThan(back_buffer_size, size); |
| 321 if (resample_count > 1) { |
| 322 if (!CreateTemporarySurface(present_thread_->device(), |
| 323 half_size, |
| 324 temp_buffer[0].Receive())) |
| 325 return false; |
| 326 } |
| 327 if (resample_count > 2) { |
| 328 const gfx::Size quarter_size = GetHalfSizeNoLessThan(half_size, size); |
| 329 if (!CreateTemporarySurface(present_thread_->device(), |
| 330 quarter_size, |
| 331 temp_buffer[1].Receive())) |
| 332 return false; |
| 333 } |
| 334 |
| 335 // Repeat downsampling the surface until its size becomes identical to |
| 336 // |size|. We keep the factor of each downsampling no more than two because |
| 337 // using a factor more than two can introduce aliasing. |
| 338 gfx::Size read_size = back_buffer_size; |
| 339 gfx::Size write_size = half_size; |
| 340 int read_buffer_index = 1; |
| 341 int write_buffer_index = 0; |
| 342 for (int i = 0; i < resample_count; ++i) { |
| 343 base::win::ScopedComPtr<IDirect3DSurface9> read_buffer = |
| 344 (i == 0) ? back_buffer : temp_buffer[read_buffer_index]; |
| 345 base::win::ScopedComPtr<IDirect3DSurface9> write_buffer = |
| 346 (i == resample_count - 1) ? final_surface : |
| 347 temp_buffer[write_buffer_index]; |
| 348 RECT read_rect = {0, 0, read_size.width(), read_size.height()}; |
| 349 RECT write_rect = {0, 0, write_size.width(), write_size.height()}; |
| 350 hr = present_thread_->device()->StretchRect(read_buffer, |
| 351 &read_rect, |
| 352 write_buffer, |
| 353 &write_rect, |
| 354 D3DTEXF_LINEAR); |
| 355 if (FAILED(hr)) |
| 356 return false; |
| 357 read_size = write_size; |
| 358 write_size = GetHalfSizeNoLessThan(write_size, size); |
| 359 std::swap(read_buffer_index, write_buffer_index); |
| 360 } |
| 361 |
| 362 DCHECK(size == read_size); |
| 363 |
| 364 base::win::ScopedComPtr<IDirect3DSurface9> temp_surface; |
| 365 HANDLE handle = reinterpret_cast<HANDLE>(buf); |
| 366 hr = present_thread_->device()->CreateOffscreenPlainSurface( |
| 367 size.width(), |
| 368 size.height(), |
| 369 D3DFMT_A8R8G8B8, |
| 370 D3DPOOL_SYSTEMMEM, |
| 371 temp_surface.Receive(), |
| 372 &handle); |
| 373 if (FAILED(hr)) |
| 374 return false; |
| 375 |
| 376 // Copy the data in the temporary buffer to the surface backed by |buf|. |
| 377 hr = present_thread_->device()->GetRenderTargetData(final_surface, |
| 378 temp_surface); |
| 379 if (FAILED(hr)) |
| 380 return false; |
| 381 |
| 382 return true; |
| 383 } |
| 384 |
243 void AcceleratedPresenter::Suspend() { | 385 void AcceleratedPresenter::Suspend() { |
244 present_thread_->message_loop()->PostTask( | 386 present_thread_->message_loop()->PostTask( |
245 FROM_HERE, | 387 FROM_HERE, |
246 base::Bind(&AcceleratedPresenter::DoSuspend, | 388 base::Bind(&AcceleratedPresenter::DoSuspend, |
247 this)); | 389 this)); |
248 } | 390 } |
249 | 391 |
250 void AcceleratedPresenter::WaitForPendingTasks() { | 392 void AcceleratedPresenter::WaitForPendingTasks() { |
251 base::WaitableEvent event(true, false); | 393 base::WaitableEvent event(true, false); |
252 present_thread_->message_loop()->PostTask( | 394 present_thread_->message_loop()->PostTask( |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
437 presenter_->AsyncPresentAndAcknowledge(window, | 579 presenter_->AsyncPresentAndAcknowledge(window, |
438 size, | 580 size, |
439 surface_id, | 581 surface_id, |
440 completion_task); | 582 completion_task); |
441 } | 583 } |
442 | 584 |
443 bool AcceleratedSurface::Present(HWND window) { | 585 bool AcceleratedSurface::Present(HWND window) { |
444 return presenter_->Present(window); | 586 return presenter_->Present(window); |
445 } | 587 } |
446 | 588 |
| 589 bool AcceleratedSurface::CopyTo(const gfx::Size& size, void* buf) { |
| 590 return presenter_->CopyTo(size, buf); |
| 591 } |
| 592 |
447 void AcceleratedSurface::Suspend() { | 593 void AcceleratedSurface::Suspend() { |
448 presenter_->Suspend(); | 594 presenter_->Suspend(); |
449 } | 595 } |
450 | |
OLD | NEW |