Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(99)

Side by Side Diff: ui/gfx/surface/accelerated_surface_win.cc

Issue 8395012: Implemented AcceleratedSurface for Windows. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « ui/gfx/surface/accelerated_surface_win.h ('k') | ui/gfx/surface/surface.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(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 <list>
10
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/command_line.h"
14 #include "base/debug/trace_event.h"
15 #include "base/lazy_instance.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/stringprintf.h"
19 #include "base/threading/thread.h"
20 #include "base/tracked_objects.h"
21 #include "base/win/wrapped_window_proc.h"
22 #include "ipc/ipc_message.h"
23 #include "ui/base/win/hwnd_util.h"
24 #include "ui/gfx/gl/gl_switches.h"
25
26 #pragma comment(lib, "d3d9.lib")
27
28 namespace {
29
30 const int64 kPollQueryInterval = 1;
31
32 class QuerySyncThread
33 : public base::Thread,
34 public base::RefCounted<QuerySyncThread> {
35 public:
36 explicit QuerySyncThread(const char* name);
37 virtual ~QuerySyncThread();
38
39 // Invoke the completion task when the query completes.
40 void AcknowledgeQuery(const base::win::ScopedComPtr<IDirect3DQuery9>& query,
41 const base::Closure& completion_task);
42
43 // Acknowledge all pending queries for the given device early then invoke
44 // the given task.
45 void AcknowledgeEarly(
46 const base::win::ScopedComPtr<IDirect3DDevice9Ex>& device,
47 const base::Closure& completion_task);
48
49 private:
50 void PollQueries();
51
52 struct PendingQuery {
53 base::win::ScopedComPtr<IDirect3DQuery9> query;
54 base::Closure completion_task;
55 };
56
57 typedef std::list<PendingQuery> PendingQueries;
58 PendingQueries pending_queries_;
59 base::WeakPtrFactory<QuerySyncThread> poll_factory_;
60
61 DISALLOW_COPY_AND_ASSIGN(QuerySyncThread);
62 };
63
64 class PresentThreadPool {
65 public:
66 static const int kNumPresentThreads = 4;
67
68 PresentThreadPool();
69
70 int NextThread();
71
72 void PostTask(int thread,
73 const tracked_objects::Location& from_here,
74 const base::Closure& task);
75
76 void AcknowledgeQuery(const tracked_objects::Location& from_here,
77 const base::win::ScopedComPtr<IDirect3DQuery9>& query,
78 const base::Closure& completion_task);
79
80 void AcknowledgeEarly(
81 const tracked_objects::Location& from_here,
82 const base::win::ScopedComPtr<IDirect3DDevice9Ex>& device,
83 const base::Closure& completion_task);
84
85 private:
86 int next_thread_;
87 scoped_ptr<base::Thread> present_threads_[kNumPresentThreads];
88 scoped_refptr<QuerySyncThread> query_sync_thread_;
89
90 DISALLOW_COPY_AND_ASSIGN(PresentThreadPool);
91 };
92
93 base::LazyInstance<PresentThreadPool>
94 g_present_thread_pool(base::LINKER_INITIALIZED);
95
96 QuerySyncThread::QuerySyncThread(const char* name)
97 : base::Thread(name),
98 poll_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
99 }
100
101 QuerySyncThread::~QuerySyncThread() {
102 }
103
104 void QuerySyncThread::AcknowledgeQuery(
105 const base::win::ScopedComPtr<IDirect3DQuery9>& query,
106 const base::Closure& completion_task) {
107 PendingQuery pending_query;
108 pending_query.query = query;
109 pending_query.completion_task = completion_task;
110 pending_queries_.push_back(pending_query);
111
112 // Cancel any pending poll tasks. There should only ever be one pending at a
113 // time.
114 poll_factory_.InvalidateWeakPtrs();
115
116 PollQueries();
117 }
118
119 void QuerySyncThread::AcknowledgeEarly(
120 const base::win::ScopedComPtr<IDirect3DDevice9Ex>& device,
121 const base::Closure& completion_task) {
122 TRACE_EVENT0("surface", "AcknowledgeEarly");
123
124 PendingQueries::iterator it = pending_queries_.begin();
125 while (it != pending_queries_.end()) {
126 const PendingQuery& pending_query = *it;
127
128 base::win::ScopedComPtr<IDirect3DDevice9> query_device;
129 pending_query.query->GetDevice(query_device.Receive());
130
131 base::win::ScopedComPtr<IDirect3DDevice9Ex> query_device_ex;
132 query_device_ex.QueryFrom(query_device.get());
133
134 if (query_device_ex.get() != device.get()) {
135 ++it;
136 } else {
137 pending_query.completion_task.Run();
138 it = pending_queries_.erase(it);
139 }
140 }
141
142 if (!completion_task.is_null())
143 completion_task.Run();
144 }
145
146
147 void QuerySyncThread::PollQueries() {
148 TRACE_EVENT0("surface", "PollQueries");
149
150 PendingQueries::iterator it = pending_queries_.begin();
151 while (it != pending_queries_.end()) {
152 const PendingQuery& pending_query = *it;
153
154 HRESULT hr = pending_query.query->GetData(NULL, 0, D3DGETDATA_FLUSH);
155 if (hr == S_FALSE) {
156 ++it;
157 } else {
158 pending_query.completion_task.Run();
159 it = pending_queries_.erase(it);
160 }
161 }
162
163 // Try again later if there are incomplete queries. Otherwise don't poll again
164 // until AcknowledgeQuery is called with a new query.
165 if (!pending_queries_.empty()) {
166 message_loop()->PostDelayedTask(
167 FROM_HERE,
168 base::Bind(&QuerySyncThread::PollQueries, poll_factory_.GetWeakPtr()),
169 kPollQueryInterval);
170 }
171 }
172
173 PresentThreadPool::PresentThreadPool() : next_thread_(0) {
174 for (int i = 0; i < kNumPresentThreads; ++i) {
175 present_threads_[i].reset(new base::Thread(
176 base::StringPrintf("PresentThread #%d", i).c_str()));
177 present_threads_[i]->Start();
178 }
179
180 query_sync_thread_ = new QuerySyncThread("QuerySyncThread");
181 query_sync_thread_->Start();
182 }
183
184 int PresentThreadPool::NextThread() {
185 next_thread_ = (next_thread_ + 1) % kNumPresentThreads;
186 return next_thread_;
187 }
188
189 void PresentThreadPool::PostTask(int thread,
190 const tracked_objects::Location& from_here,
191 const base::Closure& task) {
192 DCHECK_GE(thread, 0);
193 DCHECK_LT(thread, kNumPresentThreads);
194
195 present_threads_[thread]->message_loop()->PostTask(from_here, task);
196 }
197
198 void PresentThreadPool::AcknowledgeQuery(
199 const tracked_objects::Location& from_here,
200 const base::win::ScopedComPtr<IDirect3DQuery9>& query,
201 const base::Closure& completion_task) {
202 query_sync_thread_->message_loop()->PostTask(
203 from_here,
204 base::Bind(&QuerySyncThread::AcknowledgeQuery,
205 query_sync_thread_,
206 query,
207 completion_task));
208 }
209
210 void PresentThreadPool::AcknowledgeEarly(
211 const tracked_objects::Location& from_here,
212 const base::win::ScopedComPtr<IDirect3DDevice9Ex>& device,
213 const base::Closure& completion_task) {
214 query_sync_thread_->message_loop()->PostTask(
215 from_here,
216 base::Bind(&QuerySyncThread::AcknowledgeEarly,
217 query_sync_thread_,
218 device,
219 completion_task));
220 }
221
222 UINT GetPresentationInterval() {
223 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync))
224 return D3DPRESENT_INTERVAL_IMMEDIATE;
225 else
226 return D3DPRESENT_INTERVAL_ONE;
227 }
228
229 } // namespace anonymous
230
231 AcceleratedSurface::AcceleratedSurface(HWND parent)
232 : thread_affinity_(g_present_thread_pool.Pointer()->NextThread()),
233 window_(parent),
234 num_pending_resizes_(0) {
235 }
236
237 AcceleratedSurface::~AcceleratedSurface() {
238 // Destroy should have been called prior to the last reference going away.
239 DCHECK(!device_);
240 }
241
242 void AcceleratedSurface::Initialize() {
243 g_present_thread_pool.Pointer()->PostTask(
244 thread_affinity_,
245 FROM_HERE,
246 base::Bind(&AcceleratedSurface::DoInitialize, this));
247 }
248
249 void AcceleratedSurface::Destroy() {
250 g_present_thread_pool.Pointer()->AcknowledgeEarly(
251 FROM_HERE,
252 device_,
253 base::Bind(&AcceleratedSurface::QueriesDestroyed, this));
254 }
255
256 void AcceleratedSurface::AsyncPresentAndAcknowledge(
257 const gfx::Size& size,
258 int64 surface_id,
259 base::Closure completion_task) {
260 const int kRound = 64;
261 gfx::Size quantized_size(
262 std::max(1, (size.width() + kRound - 1) / kRound * kRound),
263 std::max(1, (size.height() + kRound - 1) / kRound * kRound));
264
265 if (pending_size_ != quantized_size) {
266 pending_size_ = quantized_size;
267 base::AtomicRefCountInc(&num_pending_resizes_);
268
269 g_present_thread_pool.Pointer()->PostTask(
270 thread_affinity_,
271 FROM_HERE,
272 base::Bind(&AcceleratedSurface::DoResize, this, quantized_size));
273 }
274
275 // This might unnecessarily post to the thread with which the swap chain has
276 // affinity. This will only result in potentially delaying the present.
277 g_present_thread_pool.Pointer()->PostTask(
278 num_pending_resizes_ ?
279 thread_affinity_ : g_present_thread_pool.Pointer()->NextThread(),
280 FROM_HERE,
281 base::Bind(&AcceleratedSurface::DoPresentAndAcknowledge,
282 this,
283 size,
284 surface_id,
285 completion_task));
286 }
287
288 void AcceleratedSurface::Present() {
289 TRACE_EVENT0("surface", "Present");
290
291 HRESULT hr;
292
293 base::AutoLock locked(lock_);
294
295 if (!device_)
296 return;
297
298 RECT rect;
299 if (!GetClientRect(window_, &rect))
300 return;
301
302 {
303 TRACE_EVENT0("surface", "PresentEx");
304 hr = device_->PresentEx(&rect,
305 &rect,
306 NULL,
307 NULL,
308 D3DPRESENT_INTERVAL_IMMEDIATE);
309 if (FAILED(hr))
310 return;
311 }
312
313 hr = query_->Issue(D3DISSUE_END);
314 if (FAILED(hr))
315 return;
316
317 {
318 TRACE_EVENT0("surface", "spin");
319 do {
320 hr = query_->GetData(NULL, 0, D3DGETDATA_FLUSH);
321
322 if (hr == S_FALSE)
323 Sleep(0);
324 } while (hr == S_FALSE);
325 }
326 }
327
328 void AcceleratedSurface::DoInitialize() {
329 TRACE_EVENT0("surface", "DoInitialize");
330
331 HRESULT hr;
332
333 base::win::ScopedComPtr<IDirect3D9Ex> d3d;
334 hr = Direct3DCreate9Ex(D3D_SDK_VERSION, d3d.Receive());
335 if (FAILED(hr))
336 return;
337
338 D3DPRESENT_PARAMETERS parameters = { 0 };
339 parameters.BackBufferWidth = 1;
340 parameters.BackBufferHeight = 1;
341 parameters.BackBufferCount = 1;
342 parameters.BackBufferFormat = D3DFMT_A8R8G8B8;
343 parameters.hDeviceWindow = window_;
344 parameters.Windowed = TRUE;
345 parameters.Flags = 0;
346 parameters.PresentationInterval = GetPresentationInterval();
347 parameters.SwapEffect = D3DSWAPEFFECT_COPY;
348
349 hr = d3d->CreateDeviceEx(
350 D3DADAPTER_DEFAULT,
351 D3DDEVTYPE_HAL,
352 window_,
353 D3DCREATE_FPU_PRESERVE | D3DCREATE_SOFTWARE_VERTEXPROCESSING |
354 D3DCREATE_MULTITHREADED,
355 &parameters,
356 NULL,
357 device_.Receive());
358 if (FAILED(hr))
359 return;
360
361 hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, query_.Receive());
362 if (FAILED(hr)) {
363 device_ = NULL;
364 return;
365 }
366
367 return;
368 }
369
370 void AcceleratedSurface::QueriesDestroyed() {
371 g_present_thread_pool.Pointer()->PostTask(
372 thread_affinity_,
373 FROM_HERE,
374 base::Bind(&AcceleratedSurface::DoDestroy,
375 this));
376 }
377
378 void AcceleratedSurface::DoDestroy() {
379 TRACE_EVENT0("surface", "DoDestroy");
380
381 base::AutoLock locked(lock_);
382
383 device_ = NULL;
384 query_ = NULL;
385 }
386
387 void AcceleratedSurface::DoResize(const gfx::Size& size) {
388 TRACE_EVENT0("surface", "DoResize");
389
390 HRESULT hr;
391
392 base::AtomicRefCountDec(&num_pending_resizes_);
393
394 D3DPRESENT_PARAMETERS parameters = { 0 };
395 parameters.BackBufferWidth = size.width();
396 parameters.BackBufferHeight = size.height();
397 parameters.BackBufferCount = 1;
398 parameters.BackBufferFormat = D3DFMT_A8R8G8B8;
399 parameters.hDeviceWindow = window_;
400 parameters.Windowed = TRUE;
401 parameters.Flags = 0;
402 parameters.PresentationInterval = GetPresentationInterval();
403 parameters.SwapEffect = D3DSWAPEFFECT_COPY;
404
405 hr = device_->ResetEx(&parameters, NULL);
406 if (FAILED(hr))
407 return;
408
409 size_ = size;
410
411 device_->Clear(0, NULL, D3DCLEAR_TARGET, 0xFFFFFFFF, 0, 0);
412 }
413
414 void AcceleratedSurface::DoPresentAndAcknowledge(
415 const gfx::Size& size,
416 int64 surface_id,
417 base::Closure completion_task) {
418 TRACE_EVENT1("surface", "DoPresentAndAcknowledge", "surface_id", surface_id);
419
420 HRESULT hr;
421
422 base::AutoLock locked(lock_);
423
424 // Ensure the task is always run and while the lock is taken.
425 base::ScopedClosureRunner scoped_completion_runner(completion_task);
426
427 if (!window_)
428 return;
429
430 HANDLE handle = reinterpret_cast<HANDLE>(surface_id);
431 if (!handle)
432 return;
433
434 base::win::ScopedComPtr<IDirect3DTexture9> source_texture;
435 {
436 TRACE_EVENT0("surface", "CreateTexture");
437 hr = device_->CreateTexture(size.width(),
438 size.height(),
439 1,
440 D3DUSAGE_RENDERTARGET,
441 D3DFMT_A8R8G8B8,
442 D3DPOOL_DEFAULT,
443 source_texture.Receive(),
444 &handle);
445 if (FAILED(hr))
446 return;
447 }
448
449 base::win::ScopedComPtr<IDirect3DSurface9> source_surface;
450 hr = source_texture->GetSurfaceLevel(0, source_surface.Receive());
451 if (FAILED(hr))
452 return;
453
454 base::win::ScopedComPtr<IDirect3DSurface9> dest_surface;
455 hr = device_->GetRenderTarget(0, dest_surface.Receive());
456 if (FAILED(hr))
457 return;
458
459 RECT rect = {
460 0, 0,
461 size.width(), size.height()
462 };
463
464 {
465 TRACE_EVENT0("surface", "StretchRect");
466 hr = device_->StretchRect(source_surface,
467 &rect,
468 dest_surface,
469 &rect,
470 D3DTEXF_NONE);
471 if (FAILED(hr))
472 return;
473 }
474
475 hr = query_->Issue(D3DISSUE_END);
476 if (FAILED(hr))
477 return;
478
479 // Flush so the StretchRect can be processed by the GPU while the window is
480 // being resized.
481 query_->GetData(NULL, 0, D3DGETDATA_FLUSH);
482
483 ::SetWindowPos(
484 window_,
485 NULL,
486 0, 0,
487 size.width(), size.height(),
488 SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE |SWP_NOOWNERZORDER |
489 SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOSENDCHANGING |
490 SWP_ASYNCWINDOWPOS);
491
492 scoped_completion_runner.Release();
493 if (!completion_task.is_null()) {
494 g_present_thread_pool.Pointer()->AcknowledgeQuery(FROM_HERE,
495 query_,
496 completion_task);
497 }
498
499 {
500 TRACE_EVENT0("surface", "Present");
501 hr = device_->Present(&rect, &rect, NULL, NULL);
502 if (FAILED(hr))
503 return;
504 }
505 }
OLDNEW
« no previous file with comments | « ui/gfx/surface/accelerated_surface_win.h ('k') | ui/gfx/surface/surface.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698