|
OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "ui/gfx/surface/accelerated_surface_win.h" | |
6 | |
7 #include <windows.h> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/callback.h" | |
11 #include "base/debug/trace_event.h" | |
12 #include "base/lazy_instance.h" | |
13 #include "base/memory/scoped_ptr.h" | |
14 #include "base/stringprintf.h" | |
15 #include "base/threading/thread.h" | |
16 #include "base/tracked_objects.h" | |
17 #include "base/win/wrapped_window_proc.h" | |
18 #include "ipc/ipc_message.h" | |
19 #include "ui/base/win/hwnd_util.h" | |
20 | |
21 #pragma comment(lib, "d3d9.lib") | |
22 | |
23 namespace { | |
24 | |
25 class PresentThreadPool { | |
26 public: | |
27 static const int kNumPresentThreads = 4; | |
28 | |
29 PresentThreadPool(); | |
30 | |
31 int NextThread(); | |
32 | |
33 void PostTask(int thread, | |
34 const tracked_objects::Location& from_here, | |
35 base::Closure task); | |
36 private: | |
37 int next_thread_; | |
38 scoped_ptr<base::Thread> present_threads_[kNumPresentThreads]; | |
39 | |
40 DISALLOW_COPY_AND_ASSIGN(PresentThreadPool); | |
41 }; | |
42 | |
43 base::LazyInstance<PresentThreadPool> | |
44 g_present_thread_pool(base::LINKER_INITIALIZED); | |
45 | |
46 PresentThreadPool::PresentThreadPool() : next_thread_(0) { | |
47 for (int i = 0; i < kNumPresentThreads; ++i) { | |
48 present_threads_[i].reset(new base::Thread( | |
49 base::StringPrintf("PresentThread #%d", i).c_str())); | |
50 present_threads_[i]->Start(); | |
51 } | |
52 } | |
53 | |
54 int PresentThreadPool::NextThread() { | |
55 next_thread_ = (next_thread_ + 1) % kNumPresentThreads; | |
56 return next_thread_; | |
57 } | |
58 | |
59 void PresentThreadPool::PostTask(int thread, | |
60 const tracked_objects::Location& from_here, | |
61 base::Closure task) { | |
62 DCHECK_GE(thread, 0); | |
63 DCHECK_LT(thread, kNumPresentThreads); | |
64 | |
65 present_threads_[thread]->message_loop()->PostTask(from_here, task); | |
66 } | |
67 | |
68 } // namespace anonymous | |
69 | |
70 AcceleratedSurface::AcceleratedSurface(HWND parent) | |
71 : thread_affinity_(g_present_thread_pool.Pointer()->NextThread()), | |
72 window_(parent), | |
73 num_pending_resizes_(0) { | |
74 } | |
75 | |
76 AcceleratedSurface::~AcceleratedSurface() { | |
77 // Destroy should have been called prior to the last reference going away. | |
78 DCHECK(!device_); | |
79 } | |
80 | |
81 void AcceleratedSurface::Initialize() { | |
82 g_present_thread_pool.Pointer()->PostTask( | |
83 thread_affinity_, | |
84 FROM_HERE, | |
85 base::Bind(&AcceleratedSurface::DoInitialize, this)); | |
86 } | |
87 | |
88 void AcceleratedSurface::Destroy() { | |
89 g_present_thread_pool.Pointer()->PostTask( | |
90 thread_affinity_, | |
91 FROM_HERE, | |
92 base::Bind(&AcceleratedSurface::DoDestroy, | |
93 this, | |
94 MessageLoop::current()->message_loop_proxy())); | |
95 } | |
96 | |
97 void AcceleratedSurface::AsyncPresentAndAcknowledge( | |
98 const gfx::Size& size, | |
99 int64 surface_id, | |
100 base::Closure completion_task) { | |
101 const int kRound = 64; | |
102 gfx::Size quantized_size( | |
103 std::max(1, (size.width() + kRound - 1) / kRound * kRound), | |
104 std::max(1, (size.height() + kRound - 1) / kRound * kRound)); | |
105 | |
106 if (pending_size_ != quantized_size) { | |
107 pending_size_ = quantized_size; | |
108 base::AtomicRefCountInc(&num_pending_resizes_); | |
109 | |
110 g_present_thread_pool.Pointer()->PostTask( | |
111 thread_affinity_, | |
112 FROM_HERE, | |
113 base::Bind(&AcceleratedSurface::DoResize, this, quantized_size)); | |
114 } | |
115 | |
116 // This might unnecessarily post to the thread with which the swap chain has | |
117 // affinity. This will only result in potentially delaying the present. | |
118 g_present_thread_pool.Pointer()->PostTask( | |
119 num_pending_resizes_ ? | |
120 thread_affinity_ : g_present_thread_pool.Pointer()->NextThread(), | |
121 FROM_HERE, | |
122 base::Bind(&AcceleratedSurface::DoPresentAndAcknowledge, | |
123 this, | |
124 size, | |
125 surface_id, | |
126 completion_task)); | |
127 } | |
128 | |
129 void AcceleratedSurface::Present() { | |
130 TRACE_EVENT0("surface", "Present"); | |
131 | |
132 HRESULT hr; | |
133 | |
134 base::AutoLock locked(lock_); | |
135 | |
136 if (!device_) | |
137 return; | |
138 | |
139 RECT rect; | |
140 if (!GetClientRect(window_, &rect)) | |
141 return; | |
142 | |
143 { | |
144 TRACE_EVENT0("surface", "PresentEx"); | |
145 hr = device_->PresentEx(&rect, | |
146 &rect, | |
147 NULL, | |
148 NULL, | |
149 D3DPRESENT_INTERVAL_IMMEDIATE); | |
150 if (FAILED(hr)) | |
151 return; | |
152 } | |
153 | |
154 hr = query_->Issue(D3DISSUE_END); | |
155 if (FAILED(hr)) | |
156 return; | |
157 | |
158 { | |
159 TRACE_EVENT0("surface", "spin"); | |
160 do { | |
161 hr = query_->GetData(NULL, 0, D3DGETDATA_FLUSH); | |
162 | |
163 if (hr == S_FALSE) | |
164 Sleep(0); | |
165 } while (hr == S_FALSE); | |
166 } | |
167 } | |
168 | |
169 void AcceleratedSurface::DoInitialize() { | |
170 TRACE_EVENT0("surface", "DoInitialize"); | |
171 | |
172 HRESULT hr; | |
173 | |
174 base::win::ScopedComPtr<IDirect3D9Ex> d3d; | |
175 hr = Direct3DCreate9Ex(D3D_SDK_VERSION, d3d.Receive()); | |
176 if (FAILED(hr)) | |
177 return; | |
178 | |
179 D3DPRESENT_PARAMETERS parameters = { 0 }; | |
180 parameters.BackBufferWidth = 1; | |
181 parameters.BackBufferHeight = 1; | |
182 parameters.BackBufferCount = 1; | |
183 parameters.BackBufferFormat = D3DFMT_A8R8G8B8; | |
184 parameters.hDeviceWindow = window_; | |
185 parameters.Windowed = TRUE; | |
186 parameters.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; | |
jbauman
2011/10/27 01:26:35
Is this necessary? Also, what presentation interva
apatrick_chromium
2011/10/27 17:44:32
I was messing with using LockRect on the back buff
| |
187 parameters.SwapEffect = D3DSWAPEFFECT_COPY; | |
188 | |
189 hr = d3d->CreateDeviceEx( | |
190 D3DADAPTER_DEFAULT, | |
191 D3DDEVTYPE_HAL, | |
192 window_, | |
193 D3DCREATE_FPU_PRESERVE | D3DCREATE_SOFTWARE_VERTEXPROCESSING | | |
194 D3DCREATE_MULTITHREADED, | |
195 ¶meters, | |
196 NULL, | |
197 device_.Receive()); | |
198 if (FAILED(hr)) | |
199 return; | |
200 | |
201 hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, query_.Receive()); | |
202 if (FAILED(hr)) { | |
203 device_ = NULL; | |
204 return; | |
205 } | |
206 | |
207 return; | |
208 } | |
209 | |
210 void AcceleratedSurface::DoDestroy( | |
211 const scoped_refptr<base::MessageLoopProxy>& ui_message_loop) { | |
212 TRACE_EVENT0("surface", "DoDestroy"); | |
213 | |
214 base::AutoLock locked(lock_); | |
215 | |
216 device_ = NULL; | |
217 query_ = NULL; | |
218 } | |
219 | |
220 void AcceleratedSurface::DoResize(const gfx::Size& size) { | |
221 TRACE_EVENT0("surface", "DoResize"); | |
222 | |
223 HRESULT hr; | |
224 | |
225 base::AtomicRefCountDec(&num_pending_resizes_); | |
226 | |
227 D3DPRESENT_PARAMETERS parameters = { 0 }; | |
228 parameters.BackBufferWidth = size.width(); | |
229 parameters.BackBufferHeight = size.height(); | |
230 parameters.BackBufferCount = 1; | |
231 parameters.BackBufferFormat = D3DFMT_A8R8G8B8; | |
232 parameters.hDeviceWindow = window_; | |
233 parameters.Windowed = TRUE; | |
234 parameters.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; | |
235 parameters.SwapEffect = D3DSWAPEFFECT_COPY; | |
236 | |
237 hr = device_->ResetEx(¶meters, NULL); | |
238 if (FAILED(hr)) | |
239 return; | |
240 | |
241 size_ = size; | |
242 | |
243 device_->Clear(0, NULL, D3DCLEAR_TARGET, 0xFFFFFFFF, 0, 0); | |
244 } | |
245 | |
246 void AcceleratedSurface::DoPresentAndAcknowledge( | |
247 const gfx::Size& size, | |
248 int64 surface_id, | |
249 base::Closure completion_task) { | |
250 TRACE_EVENT1("surface", "DoPresentAndAcknowledge", "surface_id", surface_id); | |
251 | |
252 HRESULT hr; | |
253 | |
254 base::AutoLock locked(lock_); | |
255 | |
256 // Ensure the task is always run and while the lock is taken. | |
257 base::ScopedClosureRunner scoped_completion_runner(completion_task); | |
258 | |
259 if (!window_) | |
260 return; | |
261 | |
262 HANDLE handle = reinterpret_cast<HANDLE>(surface_id); | |
263 if (!handle) | |
264 return; | |
265 | |
266 base::win::ScopedComPtr<IDirect3DTexture9> source_texture; | |
267 { | |
268 TRACE_EVENT0("surface", "CreateTexture"); | |
269 hr = device_->CreateTexture(size.width(), | |
270 size.height(), | |
271 1, | |
272 D3DUSAGE_RENDERTARGET, | |
273 D3DFMT_A8R8G8B8, | |
274 D3DPOOL_DEFAULT, | |
275 source_texture.Receive(), | |
276 &handle); | |
277 if (FAILED(hr)) | |
278 return; | |
279 } | |
280 | |
281 base::win::ScopedComPtr<IDirect3DSurface9> source_surface; | |
282 hr = source_texture->GetSurfaceLevel(0, source_surface.Receive()); | |
283 if (FAILED(hr)) | |
284 return; | |
285 | |
286 base::win::ScopedComPtr<IDirect3DSurface9> dest_surface; | |
287 hr = device_->GetRenderTarget(0, dest_surface.Receive()); | |
288 if (FAILED(hr)) | |
289 return; | |
290 | |
291 RECT rect = { | |
292 0, 0, | |
293 size.width(), size.height() | |
294 }; | |
295 | |
296 { | |
297 TRACE_EVENT0("surface", "StretchRect"); | |
298 hr = device_->StretchRect(source_surface, | |
299 &rect, | |
300 dest_surface, | |
301 &rect, | |
302 D3DTEXF_NONE); | |
303 if (FAILED(hr)) | |
304 return; | |
305 } | |
306 | |
307 hr = query_->Issue(D3DISSUE_END); | |
308 if (FAILED(hr)) | |
309 return; | |
310 | |
311 // Flush so the StretchRect can be processed by the GPU while the window is | |
312 // being resized. | |
313 query_->GetData(NULL, 0, D3DGETDATA_FLUSH); | |
314 | |
315 ::SetWindowPos( | |
316 window_, | |
317 NULL, | |
318 0, 0, | |
319 size.width(), size.height(), | |
320 SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE |SWP_NOOWNERZORDER | | |
321 SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOSENDCHANGING | | |
322 SWP_ASYNCWINDOWPOS); | |
323 | |
324 // Wait for the StretchRect to complete before notifying the GPU process | |
325 // that it is safe to write to its backing store again. | |
326 { | |
327 TRACE_EVENT0("surface", "spin"); | |
328 do { | |
329 hr = query_->GetData(NULL, 0, D3DGETDATA_FLUSH); | |
330 | |
331 if (hr == S_FALSE) | |
332 Sleep(0); | |
333 } while (hr == S_FALSE); | |
jbauman
2011/10/27 01:26:35
I'm a bit worried about all the extra latency we'r
apatrick_chromium
2011/10/27 17:44:32
Good idea. I'll try it.
| |
334 } | |
335 | |
336 scoped_completion_runner.Release(); | |
337 if (!completion_task.is_null()) | |
338 completion_task.Run(); | |
339 | |
340 { | |
341 TRACE_EVENT0("surface", "Present"); | |
342 hr = device_->Present(&rect, &rect, NULL, NULL); | |
343 if (FAILED(hr)) | |
344 return; | |
345 } | |
346 } | |
OLD | NEW |