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 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
137 NULL, | 179 NULL, |
138 device_.Receive()); | 180 device_.Receive()); |
139 if (FAILED(hr)) | 181 if (FAILED(hr)) |
140 return; | 182 return; |
141 | 183 |
142 hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, query_.Receive()); | 184 hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, query_.Receive()); |
143 if (FAILED(hr)) | 185 if (FAILED(hr)) |
144 device_ = NULL; | 186 device_ = NULL; |
145 } | 187 } |
146 | 188 |
| 189 bool AcceleratedPresenter::CopyTo(const gfx::Size& size, |
| 190 void* buf) { |
| 191 base::AutoLock locked(lock_); |
| 192 |
| 193 if (!swap_chain_) |
| 194 return false; |
| 195 |
| 196 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer; |
| 197 HRESULT hr = swap_chain_->GetBackBuffer(0, |
| 198 D3DBACKBUFFER_TYPE_MONO, |
| 199 back_buffer.Receive()); |
| 200 if (FAILED(hr)) |
| 201 return false; |
| 202 |
| 203 D3DSURFACE_DESC desc; |
| 204 hr = back_buffer->GetDesc(&desc); |
| 205 if (FAILED(hr)) |
| 206 return false; |
| 207 |
| 208 const gfx::Size back_buffer_size(desc.Width, desc.Height); |
| 209 if (back_buffer_size.IsEmpty()) |
| 210 return false; |
| 211 |
| 212 // Set up intermediate buffers needed for downsampling. |
| 213 const int resample_count = |
| 214 GetResampleCount(gfx::Size(desc.Width, desc.Height), size); |
| 215 base::win::ScopedComPtr<IDirect3DSurface9> final_surface; |
| 216 base::win::ScopedComPtr<IDirect3DSurface9> temp_buffer[2]; |
| 217 if (resample_count == 0) |
| 218 final_surface = back_buffer; |
| 219 if (resample_count > 0) { |
| 220 if (!CreateTemporarySurface(present_thread_->device(), |
| 221 size, |
| 222 final_surface.Receive())) |
| 223 return false; |
| 224 } |
| 225 const gfx::Size half_size = GetHalfSizeNoLessThan(back_buffer_size, size); |
| 226 if (resample_count > 1) { |
| 227 if (!CreateTemporarySurface(present_thread_->device(), |
| 228 half_size, |
| 229 temp_buffer[0].Receive())) |
| 230 return false; |
| 231 } |
| 232 if (resample_count > 2) { |
| 233 const gfx::Size quarter_size = GetHalfSizeNoLessThan(half_size, size); |
| 234 if (!CreateTemporarySurface(present_thread_->device(), |
| 235 quarter_size, |
| 236 temp_buffer[1].Receive())) |
| 237 return false; |
| 238 } |
| 239 |
| 240 // Repeat downsampling the surface until its size becomes identical to |
| 241 // |size|. We keep the factor of each downsampling no more than two because |
| 242 // using a factor more than two can introduce aliasing. |
| 243 gfx::Size read_size = back_buffer_size; |
| 244 gfx::Size write_size = half_size; |
| 245 int read_buffer_index = 1; |
| 246 int write_buffer_index = 0; |
| 247 for (int i = 0; i < resample_count; ++i) { |
| 248 base::win::ScopedComPtr<IDirect3DSurface9> read_buffer = |
| 249 (i == 0) ? back_buffer : temp_buffer[read_buffer_index]; |
| 250 base::win::ScopedComPtr<IDirect3DSurface9> write_buffer = |
| 251 (i == resample_count - 1) ? final_surface : |
| 252 temp_buffer[write_buffer_index]; |
| 253 RECT read_rect = {0, 0, read_size.width(), read_size.height()}; |
| 254 RECT write_rect = {0, 0, write_size.width(), write_size.height()}; |
| 255 hr = present_thread_->device()->StretchRect(read_buffer, |
| 256 &read_rect, |
| 257 write_buffer, |
| 258 &write_rect, |
| 259 D3DTEXF_LINEAR); |
| 260 if (FAILED(hr)) |
| 261 return false; |
| 262 read_size = write_size; |
| 263 write_size = GetHalfSizeNoLessThan(write_size, size); |
| 264 std::swap(read_buffer_index, write_buffer_index); |
| 265 } |
| 266 |
| 267 DCHECK(size == read_size); |
| 268 |
| 269 base::win::ScopedComPtr<IDirect3DSurface9> temp_surface; |
| 270 HANDLE handle = reinterpret_cast<HANDLE>(buf); |
| 271 hr = present_thread_->device()->CreateOffscreenPlainSurface( |
| 272 size.width(), |
| 273 size.height(), |
| 274 D3DFMT_A8R8G8B8, |
| 275 D3DPOOL_SYSTEMMEM, |
| 276 temp_surface.Receive(), |
| 277 &handle); |
| 278 if (FAILED(hr)) |
| 279 return false; |
| 280 |
| 281 // Copy the data in the temporary buffer to the surface backed by |buf|. |
| 282 hr = present_thread_->device()->GetRenderTargetData(final_surface, |
| 283 temp_surface); |
| 284 if (FAILED(hr)) |
| 285 return false; |
| 286 |
| 287 return true; |
| 288 } |
| 289 |
147 void PresentThread::Init() { | 290 void PresentThread::Init() { |
148 TRACE_EVENT0("surface", "PresentThread::Init"); | 291 TRACE_EVENT0("surface", "PresentThread::Init"); |
149 d3d_module_.Reset(base::LoadNativeLibrary(FilePath(kD3D9ModuleName), NULL)); | 292 d3d_module_.Reset(base::LoadNativeLibrary(FilePath(kD3D9ModuleName), NULL)); |
150 ResetDevice(); | 293 ResetDevice(); |
151 } | 294 } |
152 | 295 |
153 void PresentThread::CleanUp() { | 296 void PresentThread::CleanUp() { |
154 // The D3D device and query are leaked because destroying the associated D3D | 297 // The D3D device and query are leaked because destroying the associated D3D |
155 // query crashes some Intel drivers. | 298 // query crashes some Intel drivers. |
156 device_.Detach(); | 299 device_.Detach(); |
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
430 completion_task); | 573 completion_task); |
431 } | 574 } |
432 | 575 |
433 bool AcceleratedSurface::Present(HWND window) { | 576 bool AcceleratedSurface::Present(HWND window) { |
434 if (presenter_) | 577 if (presenter_) |
435 return presenter_->Present(window); | 578 return presenter_->Present(window); |
436 else | 579 else |
437 return false; | 580 return false; |
438 } | 581 } |
439 | 582 |
| 583 bool AcceleratedSurface::CopyTo(const gfx::Size& size, |
| 584 void* buf) { |
| 585 return presenter_->CopyTo(size, buf); |
| 586 } |
| 587 |
440 void AcceleratedSurface::Suspend() { | 588 void AcceleratedSurface::Suspend() { |
441 if (presenter_) | 589 if (presenter_) |
442 presenter_->Suspend(); | 590 presenter_->Suspend(); |
443 } | 591 } |
444 | |
OLD | NEW |