Add message_center_unittests.
[chromium-blink-merge.git] / ui / surface / accelerated_surface_win.cc
blobacd8c64fe39d7601ba6a3b394a105914dcd101e5
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.
5 #include "ui/surface/accelerated_surface_win.h"
7 #include <dwmapi.h>
8 #include <windows.h>
9 #include <algorithm>
11 #include "accelerated_surface_win_hlsl_compiled.h"
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/callback.h"
15 #include "base/command_line.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/message_loop_proxy.h"
21 #include "base/scoped_native_library.h"
22 #include "base/string_number_conversions.h"
23 #include "base/stringprintf.h"
24 #include "base/synchronization/waitable_event.h"
25 #include "base/threading/thread.h"
26 #include "base/threading/thread_restrictions.h"
27 #include "base/time.h"
28 #include "base/tracked_objects.h"
29 #include "base/win/wrapped_window_proc.h"
30 #include "ui/base/win/hwnd_util.h"
31 #include "ui/gfx/rect.h"
32 #include "ui/gl/gl_switches.h"
35 using ui_surface::AcceleratedSurfaceWinHLSL::kVsOneTexture;
36 using ui_surface::AcceleratedSurfaceWinHLSL::kPsOneTexture;
39 namespace {
41 typedef HRESULT (WINAPI *Direct3DCreate9ExFunc)(UINT sdk_version,
42 IDirect3D9Ex **d3d);
44 const wchar_t kD3D9ModuleName[] = L"d3d9.dll";
45 const char kCreate3D9DeviceExName[] = "Direct3DCreate9Ex";
47 const char kUseOcclusionQuery[] = "use-occlusion-query";
49 struct Vertex {
50 float x, y, z, w;
51 float u, v;
54 const static D3DVERTEXELEMENT9 g_vertexElements[] = {
55 { 0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITION, 0 },
56 { 0, 16, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 0 },
57 D3DDECL_END()
60 UINT GetPresentationInterval() {
61 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync))
62 return D3DPRESENT_INTERVAL_IMMEDIATE;
63 else
64 return D3DPRESENT_INTERVAL_ONE;
67 bool UsingOcclusionQuery() {
68 return CommandLine::ForCurrentProcess()->HasSwitch(kUseOcclusionQuery);
71 // Calculate the number necessary to transform |src_subrect| into |dst_size|
72 // by repeating downsampling of the image of |src_subrect| by a factor no more
73 // than 2.
74 int GetResampleCount(const gfx::Rect& src_subrect,
75 const gfx::Size& dst_size,
76 const gfx::Size& back_buffer_size) {
77 // At least one copy is required, since the back buffer itself is not
78 // lockable.
79 int min_resample_count = 1;
80 int width_count = 0;
81 int width = src_subrect.width();
82 while (width > dst_size.width()) {
83 ++width_count;
84 width >>= 1;
86 int height_count = 0;
87 int height = src_subrect.height();
88 while (height > dst_size.height()) {
89 ++height_count;
90 height >>= 1;
92 return std::max(std::max(width_count, height_count),
93 min_resample_count);
96 // Returns half the size of |size| no smaller than |min_size|.
97 gfx::Size GetHalfSizeNoLessThan(const gfx::Size& size,
98 const gfx::Size& min_size) {
99 return gfx::Size(std::max(min_size.width(), size.width() / 2),
100 std::max(min_size.height(), size.height() / 2));
103 bool CreateTemporarySurface(IDirect3DDevice9* device,
104 const gfx::Size& size,
105 IDirect3DSurface9** surface) {
106 HRESULT hr = device->CreateRenderTarget(
107 size.width(),
108 size.height(),
109 D3DFMT_A8R8G8B8,
110 D3DMULTISAMPLE_NONE,
112 TRUE,
113 surface,
114 NULL);
115 return SUCCEEDED(hr);
118 } // namespace
120 // A PresentThread is a thread that is dedicated to presenting surfaces to a
121 // window. It owns a Direct3D device and a Direct3D query for this purpose.
122 class PresentThread : public base::Thread,
123 public base::RefCountedThreadSafe<PresentThread> {
124 public:
125 explicit PresentThread(const char* name);
127 IDirect3DDevice9Ex* device() { return device_.get(); }
128 IDirect3DQuery9* query() { return query_.get(); }
130 void InitDevice();
131 void ResetDevice();
133 protected:
134 virtual void Init();
135 virtual void CleanUp();
137 private:
138 friend class base::RefCountedThreadSafe<PresentThread>;
140 ~PresentThread();
142 base::ScopedNativeLibrary d3d_module_;
143 base::win::ScopedComPtr<IDirect3DDevice9Ex> device_;
145 // This query is used to wait until a certain amount of progress has been
146 // made by the GPU and it is safe for the producer to modify its shared
147 // texture again.
148 base::win::ScopedComPtr<IDirect3DQuery9> query_;
150 DISALLOW_COPY_AND_ASSIGN(PresentThread);
153 // There is a fixed sized pool of PresentThreads and therefore the maximum
154 // number of Direct3D devices owned by those threads is bounded.
155 class PresentThreadPool {
156 public:
157 static const int kNumPresentThreads = 4;
159 PresentThreadPool();
160 PresentThread* NextThread();
162 private:
163 int next_thread_;
164 scoped_refptr<PresentThread> present_threads_[kNumPresentThreads];
166 DISALLOW_COPY_AND_ASSIGN(PresentThreadPool);
169 // A thread safe map of presenters by surface ID that returns presenters via
170 // a scoped_refptr to keep them alive while they are referenced.
171 class AcceleratedPresenterMap {
172 public:
173 AcceleratedPresenterMap();
174 scoped_refptr<AcceleratedPresenter> CreatePresenter(
175 gfx::PluginWindowHandle window);
176 void RemovePresenter(const scoped_refptr<AcceleratedPresenter>& presenter);
177 scoped_refptr<AcceleratedPresenter> GetPresenter(
178 gfx::PluginWindowHandle window);
180 private:
181 base::Lock lock_;
182 typedef std::map<gfx::PluginWindowHandle, AcceleratedPresenter*> PresenterMap;
183 PresenterMap presenters_;
184 DISALLOW_COPY_AND_ASSIGN(AcceleratedPresenterMap);
187 base::LazyInstance<PresentThreadPool>
188 g_present_thread_pool = LAZY_INSTANCE_INITIALIZER;
190 base::LazyInstance<AcceleratedPresenterMap>
191 g_accelerated_presenter_map = LAZY_INSTANCE_INITIALIZER;
193 PresentThread::PresentThread(const char* name) : base::Thread(name) {
196 void PresentThread::InitDevice() {
197 if (device_)
198 return;
200 TRACE_EVENT0("gpu", "PresentThread::Init");
201 d3d_module_.Reset(base::LoadNativeLibrary(FilePath(kD3D9ModuleName), NULL));
202 ResetDevice();
205 void PresentThread::ResetDevice() {
206 TRACE_EVENT0("gpu", "PresentThread::ResetDevice");
208 // This will crash some Intel drivers but we can't render anything without
209 // reseting the device, which would be disappointing.
210 query_ = NULL;
211 device_ = NULL;
213 Direct3DCreate9ExFunc create_func = reinterpret_cast<Direct3DCreate9ExFunc>(
214 d3d_module_.GetFunctionPointer(kCreate3D9DeviceExName));
215 if (!create_func)
216 return;
218 base::win::ScopedComPtr<IDirect3D9Ex> d3d;
219 HRESULT hr = create_func(D3D_SDK_VERSION, d3d.Receive());
220 if (FAILED(hr))
221 return;
223 // Any old window will do to create the device. In practice the window to
224 // present to is an argument to IDirect3DDevice9::Present.
225 HWND window = GetShellWindow();
227 D3DPRESENT_PARAMETERS parameters = { 0 };
228 parameters.BackBufferWidth = 1;
229 parameters.BackBufferHeight = 1;
230 parameters.BackBufferCount = 1;
231 parameters.BackBufferFormat = D3DFMT_A8R8G8B8;
232 parameters.hDeviceWindow = window;
233 parameters.Windowed = TRUE;
234 parameters.Flags = 0;
235 parameters.PresentationInterval = GetPresentationInterval();
236 parameters.SwapEffect = D3DSWAPEFFECT_COPY;
238 hr = d3d->CreateDeviceEx(
239 D3DADAPTER_DEFAULT,
240 D3DDEVTYPE_HAL,
241 window,
242 D3DCREATE_FPU_PRESERVE | D3DCREATE_SOFTWARE_VERTEXPROCESSING |
243 D3DCREATE_DISABLE_PSGP_THREADING | D3DCREATE_MULTITHREADED,
244 &parameters,
245 NULL,
246 device_.Receive());
247 if (FAILED(hr))
248 return;
250 if (UsingOcclusionQuery()) {
251 hr = device_->CreateQuery(D3DQUERYTYPE_OCCLUSION, query_.Receive());
252 if (FAILED(hr)) {
253 device_ = NULL;
254 return;
256 } else {
257 hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, query_.Receive());
258 if (FAILED(hr)) {
259 device_ = NULL;
260 return;
264 base::win::ScopedComPtr<IDirect3DVertexShader9> vertex_shader;
265 hr = device_->CreateVertexShader(
266 reinterpret_cast<const DWORD*>(kVsOneTexture),
267 vertex_shader.Receive());
268 if (FAILED(hr)) {
269 device_ = NULL;
270 query_ = NULL;
271 return;
274 device_->SetVertexShader(vertex_shader);
276 base::win::ScopedComPtr<IDirect3DPixelShader9> pixel_shader;
277 hr = device_->CreatePixelShader(
278 reinterpret_cast<const DWORD*>(kPsOneTexture),
279 pixel_shader.Receive());
281 if (FAILED(hr)) {
282 device_ = NULL;
283 query_ = NULL;
284 return;
287 device_->SetPixelShader(pixel_shader);
289 base::win::ScopedComPtr<IDirect3DVertexDeclaration9> vertex_declaration;
290 hr = device_->CreateVertexDeclaration(g_vertexElements,
291 vertex_declaration.Receive());
292 if (FAILED(hr)) {
293 device_ = NULL;
294 query_ = NULL;
295 return;
298 device_->SetVertexDeclaration(vertex_declaration);
301 void PresentThread::Init() {
302 TRACE_EVENT0("gpu", "Initialize thread");
305 void PresentThread::CleanUp() {
306 // The D3D device and query are leaked because destroying the associated D3D
307 // query crashes some Intel drivers.
308 device_.Detach();
309 query_.Detach();
312 PresentThread::~PresentThread() {
313 Stop();
316 PresentThreadPool::PresentThreadPool() : next_thread_(0) {
319 PresentThread* PresentThreadPool::NextThread() {
320 next_thread_ = (next_thread_ + 1) % kNumPresentThreads;
321 PresentThread* thread = present_threads_[next_thread_].get();
322 if (!thread) {
323 thread = new PresentThread(
324 base::StringPrintf("PresentThread #%d", next_thread_).c_str());
325 thread->Start();
326 present_threads_[next_thread_] = thread;
329 return thread;
332 AcceleratedPresenterMap::AcceleratedPresenterMap() {
335 scoped_refptr<AcceleratedPresenter> AcceleratedPresenterMap::CreatePresenter(
336 gfx::PluginWindowHandle window) {
337 scoped_refptr<AcceleratedPresenter> presenter(
338 new AcceleratedPresenter(window));
340 base::AutoLock locked(lock_);
341 DCHECK(presenters_.find(window) == presenters_.end());
342 presenters_[window] = presenter.get();
344 return presenter;
347 void AcceleratedPresenterMap::RemovePresenter(
348 const scoped_refptr<AcceleratedPresenter>& presenter) {
349 base::AutoLock locked(lock_);
350 for (PresenterMap::iterator it = presenters_.begin();
351 it != presenters_.end();
352 ++it) {
353 if (it->second == presenter.get()) {
354 presenters_.erase(it);
355 return;
359 NOTREACHED();
362 scoped_refptr<AcceleratedPresenter> AcceleratedPresenterMap::GetPresenter(
363 gfx::PluginWindowHandle window) {
364 base::AutoLock locked(lock_);
366 #if defined(USE_AURA)
367 if (!window)
368 return presenters_.begin()->second;
369 #endif
371 PresenterMap::iterator it = presenters_.find(window);
372 if (it == presenters_.end())
373 return scoped_refptr<AcceleratedPresenter>();
375 return it->second;
378 AcceleratedPresenter::AcceleratedPresenter(gfx::PluginWindowHandle window)
379 : present_thread_(g_present_thread_pool.Pointer()->NextThread()),
380 window_(window),
381 event_(false, false),
382 hidden_(true) {
385 scoped_refptr<AcceleratedPresenter> AcceleratedPresenter::GetForWindow(
386 gfx::PluginWindowHandle window) {
387 return g_accelerated_presenter_map.Pointer()->GetPresenter(window);
390 void AcceleratedPresenter::AsyncPresentAndAcknowledge(
391 const gfx::Size& size,
392 int64 surface_handle,
393 const CompletionTask& completion_task) {
394 if (!surface_handle) {
395 TRACE_EVENT1("gpu", "EarlyOut_ZeroSurfaceHandle",
396 "surface_handle", surface_handle);
397 completion_task.Run(true, base::TimeTicks(), base::TimeDelta());
398 return;
401 present_thread_->message_loop()->PostTask(
402 FROM_HERE,
403 base::Bind(&AcceleratedPresenter::DoPresentAndAcknowledge,
404 this,
405 size,
406 surface_handle,
407 completion_task));
410 void AcceleratedPresenter::Present(HDC dc) {
411 TRACE_EVENT0("gpu", "Present");
413 base::AutoLock locked(lock_);
415 // If invalidated, do nothing. The window is gone.
416 if (!window_)
417 return;
419 // Suspended or nothing has ever been presented.
420 if (!swap_chain_)
421 return;
423 PresentWithGDI(dc);
426 void AcceleratedPresenter::AsyncCopyTo(
427 const gfx::Rect& requested_src_subrect,
428 const gfx::Size& dst_size,
429 void* buf,
430 const base::Callback<void(bool)>& callback) {
431 present_thread_->message_loop()->PostTask(
432 FROM_HERE,
433 base::Bind(&AcceleratedPresenter::DoCopyToAndAcknowledge,
434 this,
435 requested_src_subrect,
436 dst_size,
437 buf,
438 base::MessageLoopProxy::current(),
439 callback));
442 void AcceleratedPresenter::DoCopyToAndAcknowledge(
443 const gfx::Rect& src_subrect,
444 const gfx::Size& dst_size,
445 void* buf,
446 scoped_refptr<base::SingleThreadTaskRunner> callback_runner,
447 const base::Callback<void(bool)>& callback) {
449 bool result = DoCopyTo(src_subrect, dst_size, buf);
450 callback_runner->PostTask(
451 FROM_HERE,
452 base::Bind(callback, result));
455 bool AcceleratedPresenter::DoCopyTo(const gfx::Rect& requested_src_subrect,
456 const gfx::Size& dst_size,
457 void* buf) {
458 TRACE_EVENT2(
459 "gpu", "CopyTo",
460 "width", dst_size.width(),
461 "height", dst_size.height());
463 base::AutoLock locked(lock_);
465 TRACE_EVENT0("gpu", "CopyTo_locked");
467 if (!swap_chain_)
468 return false;
470 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer;
471 HRESULT hr = swap_chain_->GetBackBuffer(0,
472 D3DBACKBUFFER_TYPE_MONO,
473 back_buffer.Receive());
474 if (FAILED(hr))
475 return false;
477 D3DSURFACE_DESC desc;
478 hr = back_buffer->GetDesc(&desc);
479 if (FAILED(hr))
480 return false;
482 const gfx::Size back_buffer_size(desc.Width, desc.Height);
483 if (back_buffer_size.IsEmpty())
484 return false;
486 // With window resizing, it's possible that the back buffer is smaller than
487 // the requested src subset. Clip to the actual back buffer.
488 gfx::Rect src_subrect = requested_src_subrect;
489 src_subrect.Intersect(gfx::Rect(back_buffer_size));
491 // Set up intermediate buffers needed for downsampling.
492 const int resample_count =
493 GetResampleCount(src_subrect, dst_size, back_buffer_size);
494 base::win::ScopedComPtr<IDirect3DSurface9> final_surface;
495 base::win::ScopedComPtr<IDirect3DSurface9> temp_buffer[2];
496 if (resample_count == 0)
497 final_surface = back_buffer;
498 if (resample_count > 0) {
499 TRACE_EVENT0("gpu", "CreateTemporarySurface");
500 if (!CreateTemporarySurface(present_thread_->device(),
501 dst_size,
502 final_surface.Receive()))
503 return false;
505 const gfx::Size half_size =
506 GetHalfSizeNoLessThan(src_subrect.size(), dst_size);
507 if (resample_count > 1) {
508 TRACE_EVENT0("gpu", "CreateTemporarySurface");
509 if (!CreateTemporarySurface(present_thread_->device(),
510 half_size,
511 temp_buffer[0].Receive()))
512 return false;
514 if (resample_count > 2) {
515 TRACE_EVENT0("gpu", "CreateTemporarySurface");
516 const gfx::Size quarter_size = GetHalfSizeNoLessThan(half_size, dst_size);
517 if (!CreateTemporarySurface(present_thread_->device(),
518 quarter_size,
519 temp_buffer[1].Receive()))
520 return false;
523 // Repeat downsampling the surface until its size becomes identical to
524 // |dst_size|. We keep the factor of each downsampling no more than two
525 // because using a factor more than two can introduce aliasing.
526 RECT read_rect = src_subrect.ToRECT();
527 gfx::Size write_size = half_size;
528 int read_buffer_index = 1;
529 int write_buffer_index = 0;
530 for (int i = 0; i < resample_count; ++i) {
531 TRACE_EVENT0("gpu", "StretchRect");
532 base::win::ScopedComPtr<IDirect3DSurface9> read_buffer =
533 (i == 0) ? back_buffer : temp_buffer[read_buffer_index];
534 base::win::ScopedComPtr<IDirect3DSurface9> write_buffer =
535 (i == resample_count - 1) ? final_surface :
536 temp_buffer[write_buffer_index];
537 RECT write_rect = gfx::Rect(write_size).ToRECT();
538 hr = present_thread_->device()->StretchRect(read_buffer,
539 &read_rect,
540 write_buffer,
541 &write_rect,
542 D3DTEXF_LINEAR);
543 if (FAILED(hr))
544 return false;
545 read_rect = write_rect;
546 write_size = GetHalfSizeNoLessThan(write_size, dst_size);
547 std::swap(read_buffer_index, write_buffer_index);
549 D3DLOCKED_RECT locked_rect;
551 // Empirical evidence seems to suggest that LockRect and memcpy are faster
552 // than would be GetRenderTargetData to an offscreen surface wrapping *buf.
554 TRACE_EVENT0("gpu", "LockRect");
555 hr = final_surface->LockRect(&locked_rect, NULL,
556 D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK);
557 if (FAILED(hr))
558 return false;
562 TRACE_EVENT0("gpu", "memcpy");
563 size_t bytesPerDstRow = 4 * dst_size.width();
564 size_t bytesPerSrcRow = locked_rect.Pitch;
565 if (bytesPerDstRow == bytesPerSrcRow) {
566 memcpy(reinterpret_cast<int8*>(buf),
567 reinterpret_cast<int8*>(locked_rect.pBits),
568 bytesPerDstRow * dst_size.height());
569 } else {
570 for (int i = 0; i < dst_size.height(); ++i) {
571 memcpy(reinterpret_cast<int8*>(buf) + bytesPerDstRow * i,
572 reinterpret_cast<int8*>(locked_rect.pBits) + bytesPerSrcRow * i,
573 bytesPerDstRow);
577 final_surface->UnlockRect();
579 return true;
582 void AcceleratedPresenter::Suspend() {
583 present_thread_->message_loop()->PostTask(
584 FROM_HERE,
585 base::Bind(&AcceleratedPresenter::DoSuspend,
586 this));
589 void AcceleratedPresenter::WasHidden() {
590 base::AutoLock locked(lock_);
591 hidden_ = true;
594 void AcceleratedPresenter::ReleaseSurface() {
595 present_thread_->message_loop()->PostTask(
596 FROM_HERE,
597 base::Bind(&AcceleratedPresenter::DoReleaseSurface,
598 this));
601 void AcceleratedPresenter::Invalidate() {
602 // Make any pending or future presentation tasks do nothing. Once the last
603 // last pending task has been ignored, the reference count on the presenter
604 // will go to zero and the presenter, and potentially also the present thread
605 // it has a reference count on, will be destroyed.
606 base::AutoLock locked(lock_);
607 window_ = NULL;
610 #if defined(USE_AURA)
611 void AcceleratedPresenter::SetNewTargetWindow(gfx::PluginWindowHandle window) {
612 window_ = window;
614 #endif
616 AcceleratedPresenter::~AcceleratedPresenter() {
619 static base::TimeDelta GetSwapDelay() {
620 CommandLine* cmd_line = CommandLine::ForCurrentProcess();
621 int delay = 0;
622 if (cmd_line->HasSwitch(switches::kGpuSwapDelay)) {
623 base::StringToInt(cmd_line->GetSwitchValueNative(
624 switches::kGpuSwapDelay).c_str(), &delay);
626 return base::TimeDelta::FromMilliseconds(delay);
629 void AcceleratedPresenter::DoPresentAndAcknowledge(
630 const gfx::Size& size,
631 int64 surface_handle,
632 const CompletionTask& completion_task) {
633 TRACE_EVENT2(
634 "gpu", "DoPresentAndAcknowledge",
635 "width", size.width(),
636 "height", size.height());
638 HRESULT hr;
640 base::AutoLock locked(lock_);
642 // Initialize the device lazily since calling Direct3D can crash bots.
643 present_thread_->InitDevice();
645 if (!present_thread_->device()) {
646 if (!completion_task.is_null())
647 completion_task.Run(false, base::TimeTicks(), base::TimeDelta());
648 TRACE_EVENT0("gpu", "EarlyOut_NoDevice");
649 return;
652 // Ensure the task is always run and while the lock is taken.
653 base::ScopedClosureRunner scoped_completion_runner(
654 base::Bind(completion_task, true, base::TimeTicks(), base::TimeDelta()));
656 // If invalidated, do nothing, the window is gone.
657 if (!window_) {
658 TRACE_EVENT0("gpu", "EarlyOut_NoWindow");
659 return;
662 #if !defined(USE_AURA)
663 // If the window is a different size than the swap chain that is being
664 // presented then drop the frame.
665 gfx::Size window_size = GetWindowSize();
666 if (hidden_ && size != window_size) {
667 TRACE_EVENT2("gpu", "EarlyOut_WrongWindowSize",
668 "backwidth", size.width(), "backheight", size.height());
669 TRACE_EVENT2("gpu", "EarlyOut_WrongWindowSize2",
670 "windowwidth", window_size.width(),
671 "windowheight", window_size.height());
672 return;
674 #endif
676 // Round up size so the swap chain is not continuously resized with the
677 // surface, which could lead to memory fragmentation.
678 const int kRound = 64;
679 gfx::Size quantized_size(
680 std::max(1, (size.width() + kRound - 1) / kRound * kRound),
681 std::max(1, (size.height() + kRound - 1) / kRound * kRound));
683 // Ensure the swap chain exists and is the same size (rounded up) as the
684 // surface to be presented.
685 if (!swap_chain_ || quantized_size_ != quantized_size) {
686 TRACE_EVENT0("gpu", "CreateAdditionalSwapChain");
687 quantized_size_ = quantized_size;
689 D3DPRESENT_PARAMETERS parameters = { 0 };
690 parameters.BackBufferWidth = quantized_size.width();
691 parameters.BackBufferHeight = quantized_size.height();
692 parameters.BackBufferCount = 1;
693 parameters.BackBufferFormat = D3DFMT_A8R8G8B8;
694 parameters.hDeviceWindow = GetShellWindow();
695 parameters.Windowed = TRUE;
696 parameters.Flags = 0;
697 parameters.PresentationInterval = GetPresentationInterval();
698 parameters.SwapEffect = D3DSWAPEFFECT_COPY;
700 swap_chain_ = NULL;
701 HRESULT hr = present_thread_->device()->CreateAdditionalSwapChain(
702 &parameters,
703 swap_chain_.Receive());
704 if (FAILED(hr))
705 return;
708 if (!source_texture_.get()) {
709 TRACE_EVENT0("gpu", "CreateTexture");
710 HANDLE handle = reinterpret_cast<HANDLE>(surface_handle);
711 hr = present_thread_->device()->CreateTexture(size.width(),
712 size.height(),
714 D3DUSAGE_RENDERTARGET,
715 D3DFMT_A8R8G8B8,
716 D3DPOOL_DEFAULT,
717 source_texture_.Receive(),
718 &handle);
719 if (FAILED(hr))
720 return;
723 base::win::ScopedComPtr<IDirect3DSurface9> source_surface;
724 hr = source_texture_->GetSurfaceLevel(0, source_surface.Receive());
725 if (FAILED(hr)) {
726 TRACE_EVENT0("gpu", "EarlyOut_NoSurfaceLevel");
727 return;
730 base::win::ScopedComPtr<IDirect3DSurface9> dest_surface;
731 hr = swap_chain_->GetBackBuffer(0,
732 D3DBACKBUFFER_TYPE_MONO,
733 dest_surface.Receive());
734 if (FAILED(hr)) {
735 TRACE_EVENT0("gpu", "EarlyOut_NoBackbuffer");
736 return;
739 RECT rect = {
740 0, 0,
741 size.width(), size.height()
745 TRACE_EVENT0("gpu", "Copy");
747 // Use a simple pixel / vertex shader pair to render a quad that flips the
748 // source texture on the vertical axis.
749 IDirect3DSurface9 *default_render_target = NULL;
750 present_thread_->device()->GetRenderTarget(0, &default_render_target);
752 present_thread_->device()->SetRenderTarget(0, dest_surface);
753 present_thread_->device()->SetTexture(0, source_texture_);
755 D3DVIEWPORT9 viewport = {
756 0, 0,
757 size.width(), size.height(),
758 0, 1
760 present_thread_->device()->SetViewport(&viewport);
762 float halfPixelX = -1.0f / size.width();
763 float halfPixelY = 1.0f / size.height();
764 Vertex vertices[] = {
765 { halfPixelX - 1, halfPixelY + 1, 0.5f, 1, 0, 1 },
766 { halfPixelX + 1, halfPixelY + 1, 0.5f, 1, 1, 1 },
767 { halfPixelX + 1, halfPixelY - 1, 0.5f, 1, 1, 0 },
768 { halfPixelX - 1, halfPixelY - 1, 0.5f, 1, 0, 0 }
771 if (UsingOcclusionQuery()) {
772 present_thread_->query()->Issue(D3DISSUE_BEGIN);
775 present_thread_->device()->BeginScene();
776 present_thread_->device()->DrawPrimitiveUP(D3DPT_TRIANGLEFAN,
778 vertices,
779 sizeof(vertices[0]));
780 present_thread_->device()->EndScene();
782 present_thread_->device()->SetTexture(0, NULL);
783 present_thread_->device()->SetRenderTarget(0, default_render_target);
784 default_render_target->Release();
787 hr = present_thread_->query()->Issue(D3DISSUE_END);
788 if (FAILED(hr))
789 return;
791 present_size_ = size;
793 static const base::TimeDelta swap_delay = GetSwapDelay();
794 if (swap_delay.ToInternalValue())
795 base::PlatformThread::Sleep(swap_delay);
797 // If it is expected that Direct3D cannot be used reliably because the window
798 // is resizing, fall back to presenting with GDI.
799 if (CheckDirect3DWillWork()) {
800 TRACE_EVENT0("gpu", "PresentD3D");
802 hr = swap_chain_->Present(&rect, &rect, window_, NULL, 0);
804 // For latency_tests.cc:
805 UNSHIPPED_TRACE_EVENT_INSTANT0("test_gpu", "CompositorSwapBuffersComplete");
807 if (FAILED(hr) &&
808 FAILED(present_thread_->device()->CheckDeviceState(window_))) {
809 present_thread_->ResetDevice();
811 } else {
812 HDC dc = GetDC(window_);
813 PresentWithGDI(dc);
814 ReleaseDC(window_, dc);
817 // Early out if failed to reset device.
818 if (!present_thread_->device())
819 return;
821 hidden_ = false;
823 D3DDISPLAYMODE display_mode;
824 hr = present_thread_->device()->GetDisplayMode(0, &display_mode);
825 if (FAILED(hr))
826 return;
828 D3DRASTER_STATUS raster_status;
829 hr = swap_chain_->GetRasterStatus(&raster_status);
830 if (FAILED(hr))
831 return;
833 // I can't figure out how to determine how many scanlines are in the
834 // vertical blank so clamp it such that scanline / height <= 1.
835 int clamped_scanline = std::min(raster_status.ScanLine, display_mode.Height);
837 // The Internet says that on some GPUs, the scanline is not available
838 // while in the vertical blank.
839 if (raster_status.InVBlank)
840 clamped_scanline = display_mode.Height;
842 base::TimeTicks current_time = base::TimeTicks::HighResNow();
844 // Figure out approximately how far back in time the last vsync was based on
845 // the ratio of the raster scanline to the display height.
846 base::TimeTicks last_vsync_time;
847 base::TimeDelta refresh_period;
848 if (display_mode.Height) {
849 last_vsync_time = current_time -
850 base::TimeDelta::FromMilliseconds((clamped_scanline * 1000) /
851 (display_mode.RefreshRate * display_mode.Height));
852 refresh_period = base::TimeDelta::FromMicroseconds(
853 1000000 / display_mode.RefreshRate);
856 // Wait for the StretchRect to complete before notifying the GPU process
857 // that it is safe to write to its backing store again.
859 TRACE_EVENT0("gpu", "spin");
860 do {
861 hr = present_thread_->query()->GetData(NULL, 0, D3DGETDATA_FLUSH);
863 if (hr == S_FALSE)
864 Sleep(1);
865 } while (hr == S_FALSE);
868 scoped_completion_runner.Release();
869 completion_task.Run(true, last_vsync_time, refresh_period);
872 void AcceleratedPresenter::DoSuspend() {
873 base::AutoLock locked(lock_);
874 swap_chain_ = NULL;
877 void AcceleratedPresenter::DoReleaseSurface() {
878 base::AutoLock locked(lock_);
879 present_thread_->InitDevice();
880 source_texture_.Release();
883 void AcceleratedPresenter::PresentWithGDI(HDC dc) {
884 TRACE_EVENT0("gpu", "PresentWithGDI");
886 if (!present_thread_->device())
887 return;
889 if (!swap_chain_)
890 return;
892 base::win::ScopedComPtr<IDirect3DTexture9> system_texture;
894 TRACE_EVENT0("gpu", "CreateSystemTexture");
895 HRESULT hr = present_thread_->device()->CreateTexture(
896 quantized_size_.width(),
897 quantized_size_.height(),
900 D3DFMT_A8R8G8B8,
901 D3DPOOL_SYSTEMMEM,
902 system_texture.Receive(),
903 NULL);
904 if (FAILED(hr))
905 return;
908 base::win::ScopedComPtr<IDirect3DSurface9> system_surface;
909 HRESULT hr = system_texture->GetSurfaceLevel(0, system_surface.Receive());
910 DCHECK(SUCCEEDED(hr));
912 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer;
913 hr = swap_chain_->GetBackBuffer(0,
914 D3DBACKBUFFER_TYPE_MONO,
915 back_buffer.Receive());
916 DCHECK(SUCCEEDED(hr));
919 TRACE_EVENT0("gpu", "GetRenderTargetData");
920 hr = present_thread_->device()->GetRenderTargetData(back_buffer,
921 system_surface);
922 DCHECK(SUCCEEDED(hr));
925 D3DLOCKED_RECT locked_surface;
926 hr = system_surface->LockRect(&locked_surface, NULL, D3DLOCK_READONLY);
927 DCHECK(SUCCEEDED(hr));
929 BITMAPINFO bitmap_info = {
931 sizeof(BITMAPINFOHEADER),
932 quantized_size_.width(),
933 -quantized_size_.height(),
934 1, // planes
935 32, // bitcount
936 BI_RGB
939 {0, 0, 0, 0}
944 TRACE_EVENT0("gpu", "StretchDIBits");
945 StretchDIBits(dc,
946 0, 0,
947 present_size_.width(),
948 present_size_.height(),
949 0, 0,
950 present_size_.width(),
951 present_size_.height(),
952 locked_surface.pBits,
953 &bitmap_info,
954 DIB_RGB_COLORS,
955 SRCCOPY);
958 system_surface->UnlockRect();
960 // For latency_tests.cc:
961 UNSHIPPED_TRACE_EVENT_INSTANT0("test_gpu", "CompositorSwapBuffersComplete");
964 gfx::Size AcceleratedPresenter::GetWindowSize() {
965 RECT rect;
966 GetClientRect(window_, &rect);
967 return gfx::Rect(rect).size();
970 bool AcceleratedPresenter::CheckDirect3DWillWork() {
971 gfx::Size window_size = GetWindowSize();
972 if (window_size != last_window_size_ && last_window_size_.GetArea() != 0) {
973 last_window_size_ = window_size;
974 last_window_resize_time_ = base::Time::Now();
975 return false;
978 return base::Time::Now() - last_window_resize_time_ >
979 base::TimeDelta::FromMilliseconds(100);
982 AcceleratedSurface::AcceleratedSurface(gfx::PluginWindowHandle window)
983 : presenter_(g_accelerated_presenter_map.Pointer()->CreatePresenter(
984 window)) {
987 AcceleratedSurface::~AcceleratedSurface() {
988 g_accelerated_presenter_map.Pointer()->RemovePresenter(presenter_);
989 presenter_->Invalidate();
992 void AcceleratedSurface::Present(HDC dc) {
993 presenter_->Present(dc);
996 void AcceleratedSurface::AsyncCopyTo(
997 const gfx::Rect& src_subrect,
998 const gfx::Size& dst_size,
999 void* buf,
1000 const base::Callback<void(bool)>& callback) {
1001 presenter_->AsyncCopyTo(src_subrect, dst_size, buf, callback);
1004 void AcceleratedSurface::Suspend() {
1005 presenter_->Suspend();
1008 void AcceleratedSurface::WasHidden() {
1009 presenter_->WasHidden();