accelerated_surface_win.cc revision 3551c9c881056c480085172ff9840cab31610854
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. 4 5#include "ui/surface/accelerated_surface_win.h" 6 7#include <windows.h> 8#include <algorithm> 9 10#include "base/bind.h" 11#include "base/bind_helpers.h" 12#include "base/callback.h" 13#include "base/command_line.h" 14#include "base/debug/trace_event.h" 15#include "base/files/file_path.h" 16#include "base/lazy_instance.h" 17#include "base/memory/scoped_ptr.h" 18#include "base/message_loop/message_loop_proxy.h" 19#include "base/scoped_native_library.h" 20#include "base/strings/stringprintf.h" 21#include "base/synchronization/waitable_event.h" 22#include "base/threading/thread.h" 23#include "base/threading/thread_restrictions.h" 24#include "base/win/wrapped_window_proc.h" 25#include "media/base/video_frame.h" 26#include "media/base/video_util.h" 27#include "third_party/skia/include/core/SkBitmap.h" 28#include "ui/base/latency_info.h" 29#include "ui/base/win/dpi.h" 30#include "ui/base/win/hwnd_util.h" 31#include "ui/base/win/shell.h" 32#include "ui/gfx/rect.h" 33#include "ui/gl/gl_switches.h" 34#include "ui/surface/accelerated_surface_transformer_win.h" 35#include "ui/surface/d3d9_utils_win.h" 36#include "ui/surface/surface_switches.h" 37 38namespace d3d_utils = ui_surface_d3d9_utils; 39 40namespace { 41 42UINT GetPresentationInterval() { 43 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync)) 44 return D3DPRESENT_INTERVAL_IMMEDIATE; 45 else 46 return D3DPRESENT_INTERVAL_ONE; 47} 48 49bool DoFirstShowPresentWithGDI() { 50 return CommandLine::ForCurrentProcess()->HasSwitch( 51 switches::kDoFirstShowPresentWithGDI); 52} 53 54bool DoAllShowPresentWithGDI() { 55 return CommandLine::ForCurrentProcess()->HasSwitch( 56 switches::kDoAllShowPresentWithGDI); 57} 58 59// Use a SurfaceReader to copy into one plane of the VideoFrame. 60bool CopyPlane(AcceleratedSurfaceTransformer* gpu_ops, 61 IDirect3DSurface9* src_surface, 62 media::VideoFrame* dst_frame, 63 size_t plane_id) { 64 int width_in_bytes = dst_frame->row_bytes(plane_id); 65 return gpu_ops->ReadFast(src_surface, dst_frame->data(plane_id), 66 width_in_bytes, dst_frame->rows(plane_id), 67 dst_frame->row_bytes(plane_id)); 68} 69 70} // namespace 71 72// A PresentThread is a thread that is dedicated to presenting surfaces to a 73// window. It owns a Direct3D device and a Direct3D query for this purpose. 74class PresentThread : public base::Thread, 75 public base::RefCountedThreadSafe<PresentThread> { 76 public: 77 PresentThread(const char* name, uint64 adapter_luid); 78 79 IDirect3DDevice9Ex* device() { return device_.get(); } 80 IDirect3DQuery9* query() { return query_.get(); } 81 AcceleratedSurfaceTransformer* surface_transformer() { 82 return &surface_transformer_; 83 } 84 85 void SetAdapterLUID(uint64 adapter_luid); 86 void InitDevice(); 87 void LockAndResetDevice(); 88 void ResetDevice(); 89 bool IsDeviceLost(); 90 91 base::Lock* lock() { 92 return &lock_; 93 } 94 95 protected: 96 virtual void Init(); 97 virtual void CleanUp(); 98 99 private: 100 friend class base::RefCountedThreadSafe<PresentThread>; 101 102 ~PresentThread(); 103 104 // The lock is taken while any thread is calling an AcceleratedPresenter 105 // associated with this thread. 106 base::Lock lock_; 107 108 base::ScopedNativeLibrary d3d_module_; 109 uint64 adapter_luid_; 110 base::win::ScopedComPtr<IDirect3DDevice9Ex> device_; 111 112 // This query is used to wait until a certain amount of progress has been 113 // made by the GPU and it is safe for the producer to modify its shared 114 // texture again. 115 base::win::ScopedComPtr<IDirect3DQuery9> query_; 116 AcceleratedSurfaceTransformer surface_transformer_; 117 118 DISALLOW_COPY_AND_ASSIGN(PresentThread); 119}; 120 121// There is a fixed sized pool of PresentThreads and therefore the maximum 122// number of Direct3D devices owned by those threads is bounded. 123class PresentThreadPool { 124 public: 125 static const int kNumPresentThreads = 4; 126 127 PresentThreadPool(); 128 PresentThread* NextThread(); 129 130 void SetAdapterLUID(uint64 adapter_luid); 131 132 private: 133 base::Lock lock_; 134 int next_thread_; 135 scoped_refptr<PresentThread> present_threads_[kNumPresentThreads]; 136 uint64 adapter_luid_; 137 138 DISALLOW_COPY_AND_ASSIGN(PresentThreadPool); 139}; 140 141// A thread safe map of presenters by surface ID that returns presenters via 142// a scoped_refptr to keep them alive while they are referenced. 143class AcceleratedPresenterMap { 144 public: 145 AcceleratedPresenterMap(); 146 scoped_refptr<AcceleratedPresenter> CreatePresenter( 147 gfx::PluginWindowHandle window); 148 void RemovePresenter(const scoped_refptr<AcceleratedPresenter>& presenter); 149 scoped_refptr<AcceleratedPresenter> GetPresenter( 150 gfx::PluginWindowHandle window); 151 152 // Destroy any D3D resources owned by the given present thread. Called on 153 // the given present thread. 154 void ResetPresentThread(PresentThread* present_thread); 155 156 private: 157 base::Lock lock_; 158 typedef std::map<gfx::PluginWindowHandle, AcceleratedPresenter*> PresenterMap; 159 PresenterMap presenters_; 160 uint64 adapter_luid_; 161 DISALLOW_COPY_AND_ASSIGN(AcceleratedPresenterMap); 162}; 163 164base::LazyInstance<PresentThreadPool> 165 g_present_thread_pool = LAZY_INSTANCE_INITIALIZER; 166 167base::LazyInstance<AcceleratedPresenterMap> 168 g_accelerated_presenter_map = LAZY_INSTANCE_INITIALIZER; 169 170PresentThread::PresentThread(const char* name, uint64 adapter_luid) 171 : base::Thread(name), 172 adapter_luid_(adapter_luid) { 173} 174 175void PresentThread::SetAdapterLUID(uint64 adapter_luid) { 176 base::AutoLock locked(lock_); 177 178 CHECK(message_loop() == base::MessageLoop::current()); 179 180 if (adapter_luid_ == adapter_luid) 181 return; 182 183 adapter_luid_ = adapter_luid; 184 if (device_) 185 ResetDevice(); 186} 187 188void PresentThread::InitDevice() { 189 lock_.AssertAcquired(); 190 191 if (device_) 192 return; 193 194 TRACE_EVENT0("gpu", "PresentThread::Init"); 195 d3d_utils::LoadD3D9(&d3d_module_); 196 ResetDevice(); 197} 198 199void PresentThread::LockAndResetDevice() { 200 base::AutoLock locked(lock_); 201 ResetDevice(); 202} 203 204void PresentThread::ResetDevice() { 205 TRACE_EVENT0("gpu", "PresentThread::ResetDevice"); 206 207 lock_.AssertAcquired(); 208 209 // The D3D device must be created on the present thread. 210 CHECK(message_loop() == base::MessageLoop::current()); 211 212 // This will crash some Intel drivers but we can't render anything without 213 // reseting the device, which would be disappointing. 214 query_ = NULL; 215 device_ = NULL; 216 surface_transformer_.ReleaseAll(); 217 218 g_accelerated_presenter_map.Pointer()->ResetPresentThread(this); 219 220 if (!d3d_utils::CreateDevice(d3d_module_, 221 adapter_luid_, 222 D3DDEVTYPE_HAL, 223 GetPresentationInterval(), 224 device_.Receive())) { 225 return; 226 } 227 228 HRESULT hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, query_.Receive()); 229 if (FAILED(hr)) { 230 LOG(ERROR) << "Failed to create query"; 231 device_ = NULL; 232 return; 233 } 234 235 if (!surface_transformer_.Init(device_)) { 236 LOG(ERROR) << "Failed to initialize surface transformer"; 237 query_ = NULL; 238 device_ = NULL; 239 return; 240 } 241} 242 243bool PresentThread::IsDeviceLost() { 244 lock_.AssertAcquired(); 245 246 HRESULT hr = device_->CheckDeviceState(NULL); 247 return FAILED(hr) || hr == S_PRESENT_MODE_CHANGED; 248} 249 250void PresentThread::Init() { 251 TRACE_EVENT0("gpu", "Initialize thread"); 252} 253 254void PresentThread::CleanUp() { 255 // The D3D device and query are leaked because destroying the associated D3D 256 // query crashes some Intel drivers. 257 surface_transformer_.DetachAll(); 258 device_.Detach(); 259 query_.Detach(); 260} 261 262PresentThread::~PresentThread() { 263 Stop(); 264} 265 266PresentThreadPool::PresentThreadPool() : next_thread_(0) { 267} 268 269PresentThread* PresentThreadPool::NextThread() { 270 base::AutoLock locked(lock_); 271 272 next_thread_ = (next_thread_ + 1) % kNumPresentThreads; 273 PresentThread* thread = present_threads_[next_thread_].get(); 274 if (!thread) { 275 thread = new PresentThread( 276 base::StringPrintf("PresentThread #%d", next_thread_).c_str(), 277 adapter_luid_); 278 thread->Start(); 279 present_threads_[next_thread_] = thread; 280 } 281 282 return thread; 283} 284 285void PresentThreadPool::SetAdapterLUID(uint64 adapter_luid) { 286 base::AutoLock locked(lock_); 287 288 adapter_luid_ = adapter_luid; 289 290 for (int i = 0; i < kNumPresentThreads; ++i) { 291 if (!present_threads_[i]) 292 continue; 293 294 present_threads_[i]->message_loop()->PostTask( 295 FROM_HERE, 296 base::Bind(&PresentThread::SetAdapterLUID, 297 present_threads_[i], 298 adapter_luid)); 299 } 300} 301 302AcceleratedPresenterMap::AcceleratedPresenterMap() { 303} 304 305scoped_refptr<AcceleratedPresenter> AcceleratedPresenterMap::CreatePresenter( 306 gfx::PluginWindowHandle window) { 307 scoped_refptr<AcceleratedPresenter> presenter( 308 new AcceleratedPresenter(window)); 309 310 base::AutoLock locked(lock_); 311 DCHECK(presenters_.find(window) == presenters_.end()); 312 presenters_[window] = presenter.get(); 313 314 return presenter; 315} 316 317void AcceleratedPresenterMap::RemovePresenter( 318 const scoped_refptr<AcceleratedPresenter>& presenter) { 319 base::AutoLock locked(lock_); 320 for (PresenterMap::iterator it = presenters_.begin(); 321 it != presenters_.end(); 322 ++it) { 323 if (it->second == presenter.get()) { 324 presenters_.erase(it); 325 return; 326 } 327 } 328 329 NOTREACHED(); 330} 331 332scoped_refptr<AcceleratedPresenter> AcceleratedPresenterMap::GetPresenter( 333 gfx::PluginWindowHandle window) { 334 base::AutoLock locked(lock_); 335 336#if defined(USE_AURA) 337 if (!window) 338 return presenters_.begin()->second; 339#endif 340 341 PresenterMap::iterator it = presenters_.find(window); 342 if (it == presenters_.end()) 343 return scoped_refptr<AcceleratedPresenter>(); 344 345 return it->second; 346} 347 348void AcceleratedPresenterMap::ResetPresentThread( 349 PresentThread* present_thread) { 350 base::AutoLock locked(lock_); 351 352 for (PresenterMap::iterator it = presenters_.begin(); 353 it != presenters_.end(); 354 ++it) { 355 it->second->ResetPresentThread(present_thread); 356 } 357} 358 359AcceleratedPresenter::AcceleratedPresenter(gfx::PluginWindowHandle window) 360 : present_thread_(g_present_thread_pool.Pointer()->NextThread()), 361 window_(window), 362 event_(false, false), 363 hidden_(true), 364 do_present_with_GDI_(DoAllShowPresentWithGDI() || 365 DoFirstShowPresentWithGDI()), 366 is_session_locked_(false) { 367} 368 369// static 370void AcceleratedPresenter::SetAdapterLUID(uint64 adapter_luid) { 371 return g_present_thread_pool.Pointer()->SetAdapterLUID(adapter_luid); 372} 373 374 375// static 376scoped_refptr<AcceleratedPresenter> AcceleratedPresenter::GetForWindow( 377 gfx::PluginWindowHandle window) { 378 return g_accelerated_presenter_map.Pointer()->GetPresenter(window); 379} 380 381void AcceleratedPresenter::AsyncPresentAndAcknowledge( 382 const gfx::Size& size, 383 int64 surface_handle, 384 const ui::LatencyInfo& latency_info, 385 const CompletionTask& completion_task) { 386 if (!surface_handle) { 387 TRACE_EVENT1("gpu", "EarlyOut_ZeroSurfaceHandle", 388 "surface_handle", surface_handle); 389 completion_task.Run( 390 true, base::TimeTicks(), base::TimeDelta(), ui::LatencyInfo()); 391 return; 392 } 393 394 present_thread_->message_loop()->PostTask( 395 FROM_HERE, 396 base::Bind(&AcceleratedPresenter::DoPresentAndAcknowledge, 397 this, 398 size, 399 surface_handle, 400 latency_info, 401 completion_task)); 402} 403 404void AcceleratedPresenter::Present(HDC dc) { 405 TRACE_EVENT0("gpu", "Present"); 406 407 base::AutoLock locked(*present_thread_->lock()); 408 409 // If invalidated, do nothing. The window is gone. 410 if (!window_) 411 return; 412 413 // Suspended or nothing has ever been presented. 414 if (!swap_chain_) 415 return; 416 417 PresentWithGDI(dc); 418} 419 420void AcceleratedPresenter::AsyncCopyTo( 421 const gfx::Rect& requested_src_subrect, 422 const gfx::Size& dst_size, 423 const base::Callback<void(bool, const SkBitmap&)>& callback) { 424 present_thread_->message_loop()->PostTask( 425 FROM_HERE, 426 base::Bind(&AcceleratedPresenter::DoCopyToAndAcknowledge, 427 this, 428 requested_src_subrect, 429 dst_size, 430 base::MessageLoopProxy::current(), 431 callback)); 432} 433 434void AcceleratedPresenter::AsyncCopyToVideoFrame( 435 const gfx::Rect& requested_src_subrect, 436 const scoped_refptr<media::VideoFrame>& target, 437 const base::Callback<void(bool)>& callback) { 438 present_thread_->message_loop()->PostTask( 439 FROM_HERE, 440 base::Bind(&AcceleratedPresenter::DoCopyToVideoFrameAndAcknowledge, 441 this, 442 requested_src_subrect, 443 target, 444 base::MessageLoopProxy::current(), 445 callback)); 446} 447 448void AcceleratedPresenter::DoCopyToAndAcknowledge( 449 const gfx::Rect& src_subrect, 450 const gfx::Size& dst_size, 451 scoped_refptr<base::SingleThreadTaskRunner> callback_runner, 452 const base::Callback<void(bool, const SkBitmap&)>& callback) { 453 SkBitmap target; 454 bool result = DoCopyToARGB(src_subrect, dst_size, &target); 455 if (!result) 456 target.reset(); 457 callback_runner->PostTask(FROM_HERE, base::Bind(callback, result, target)); 458} 459 460void AcceleratedPresenter::DoCopyToVideoFrameAndAcknowledge( 461 const gfx::Rect& src_subrect, 462 const scoped_refptr<media::VideoFrame>& target, 463 const scoped_refptr<base::SingleThreadTaskRunner>& callback_runner, 464 const base::Callback<void(bool)>& callback) { 465 466 bool result = DoCopyToYUV(src_subrect, target); 467 callback_runner->PostTask(FROM_HERE, base::Bind(callback, result)); 468} 469 470bool AcceleratedPresenter::DoCopyToARGB(const gfx::Rect& requested_src_subrect, 471 const gfx::Size& dst_size, 472 SkBitmap* bitmap) { 473 TRACE_EVENT2( 474 "gpu", "CopyTo", 475 "width", dst_size.width(), 476 "height", dst_size.height()); 477 478 base::AutoLock locked(*present_thread_->lock()); 479 480 if (!swap_chain_) 481 return false; 482 483 AcceleratedSurfaceTransformer* gpu_ops = 484 present_thread_->surface_transformer(); 485 486 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer; 487 HRESULT hr = swap_chain_->GetBackBuffer(0, 488 D3DBACKBUFFER_TYPE_MONO, 489 back_buffer.Receive()); 490 if (FAILED(hr)) { 491 LOG(ERROR) << "Failed to get back buffer"; 492 return false; 493 } 494 495 D3DSURFACE_DESC desc; 496 hr = back_buffer->GetDesc(&desc); 497 if (FAILED(hr)) { 498 LOG(ERROR) << "Failed to get buffer description"; 499 return false; 500 } 501 502 const gfx::Size back_buffer_size(desc.Width, desc.Height); 503 if (back_buffer_size.IsEmpty()) 504 return false; 505 506 // With window resizing, it's possible that the back buffer is smaller than 507 // the requested src subset. Clip to the actual back buffer. 508 gfx::Rect src_subrect = requested_src_subrect; 509 src_subrect.Intersect(gfx::Rect(back_buffer_size)); 510 base::win::ScopedComPtr<IDirect3DSurface9> final_surface; 511 { 512 if (!d3d_utils::CreateOrReuseLockableSurface(present_thread_->device(), 513 dst_size, 514 &final_surface)) { 515 LOG(ERROR) << "Failed to create temporary lockable surface"; 516 return false; 517 } 518 } 519 520 { 521 // Let the surface transformer start the resize into |final_surface|. 522 TRACE_EVENT0("gpu", "ResizeBilinear"); 523 if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, 524 final_surface, gfx::Rect(dst_size))) { 525 LOG(ERROR) << "Failed to resize bilinear"; 526 return false; 527 } 528 } 529 530 bitmap->setConfig(SkBitmap::kARGB_8888_Config, 531 dst_size.width(), dst_size.height()); 532 if (!bitmap->allocPixels()) 533 return false; 534 bitmap->setIsOpaque(true); 535 536 // Copy |final_surface| to |bitmap|. This is always a synchronous operation. 537 return gpu_ops->ReadFast(final_surface, 538 reinterpret_cast<uint8*>(bitmap->getPixels()), 539 bitmap->width() * bitmap->bytesPerPixel(), 540 bitmap->height(), 541 static_cast<int>(bitmap->rowBytes())); 542} 543 544bool AcceleratedPresenter::DoCopyToYUV( 545 const gfx::Rect& requested_src_subrect, 546 const scoped_refptr<media::VideoFrame>& frame) { 547 gfx::Size dst_size = frame->coded_size(); 548 TRACE_EVENT2( 549 "gpu", "CopyToYUV", 550 "width", dst_size.width(), 551 "height", dst_size.height()); 552 553 base::AutoLock locked(*present_thread_->lock()); 554 555 if (!swap_chain_) 556 return false; 557 558 AcceleratedSurfaceTransformer* gpu_ops = 559 present_thread_->surface_transformer(); 560 561 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer; 562 HRESULT hr = swap_chain_->GetBackBuffer(0, 563 D3DBACKBUFFER_TYPE_MONO, 564 back_buffer.Receive()); 565 if (FAILED(hr)) 566 return false; 567 568 D3DSURFACE_DESC desc; 569 hr = back_buffer->GetDesc(&desc); 570 if (FAILED(hr)) 571 return false; 572 573 const gfx::Size back_buffer_size(desc.Width, desc.Height); 574 if (back_buffer_size.IsEmpty()) 575 return false; 576 577 // With window resizing, it's possible that the back buffer is smaller than 578 // the requested src subset. Clip to the actual back buffer. 579 gfx::Rect src_subrect = requested_src_subrect; 580 src_subrect.Intersect(gfx::Rect(back_buffer_size)); 581 if (src_subrect.IsEmpty()) 582 return false; 583 584 base::win::ScopedComPtr<IDirect3DSurface9> resized; 585 base::win::ScopedComPtr<IDirect3DTexture9> resized_as_texture; 586 if (!gpu_ops->GetIntermediateTexture(dst_size, 587 resized_as_texture.Receive(), 588 resized.Receive())) { 589 return false; 590 } 591 592 // Shrink the source to fit entirely in the destination while preserving 593 // aspect ratio. Fill in any margin with black. 594 // TODO(nick): It would be more efficient all around to implement 595 // letterboxing as a memset() on the dst. 596 gfx::Rect letterbox = media::ComputeLetterboxRegion(gfx::Rect(dst_size), 597 src_subrect.size()); 598 if (letterbox != gfx::Rect(dst_size)) { 599 TRACE_EVENT0("gpu", "Letterbox"); 600 present_thread_->device()->ColorFill(resized, NULL, 0xFF000000); 601 } 602 603 { 604 TRACE_EVENT0("gpu", "ResizeBilinear"); 605 if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, resized, letterbox)) 606 return false; 607 } 608 609 base::win::ScopedComPtr<IDirect3DSurface9> y, u, v; 610 { 611 TRACE_EVENT0("gpu", "TransformRGBToYV12"); 612 if (!gpu_ops->TransformRGBToYV12(resized_as_texture, 613 dst_size, 614 y.Receive(), u.Receive(), v.Receive())) { 615 return false; 616 } 617 } 618 619 if (!CopyPlane(gpu_ops, y, frame, media::VideoFrame::kYPlane)) 620 return false; 621 if (!CopyPlane(gpu_ops, u, frame, media::VideoFrame::kUPlane)) 622 return false; 623 if (!CopyPlane(gpu_ops, v, frame, media::VideoFrame::kVPlane)) 624 return false; 625 return true; 626} 627 628void AcceleratedPresenter::Suspend() { 629 present_thread_->message_loop()->PostTask( 630 FROM_HERE, 631 base::Bind(&AcceleratedPresenter::DoSuspend, 632 this)); 633} 634 635void AcceleratedPresenter::WasHidden() { 636 base::AutoLock locked(*present_thread_->lock()); 637 hidden_ = true; 638} 639 640void AcceleratedPresenter::ReleaseSurface() { 641 present_thread_->message_loop()->PostTask( 642 FROM_HERE, 643 base::Bind(&AcceleratedPresenter::DoReleaseSurface, 644 this)); 645} 646 647void AcceleratedPresenter::SetIsSessionLocked(bool locked) { 648 is_session_locked_ = locked; 649} 650 651void AcceleratedPresenter::Invalidate() { 652 // Make any pending or future presentation tasks do nothing. Once the last 653 // last pending task has been ignored, the reference count on the presenter 654 // will go to zero and the presenter, and potentially also the present thread 655 // it has a reference count on, will be destroyed. 656 base::AutoLock locked(*present_thread_->lock()); 657 window_ = NULL; 658} 659 660void AcceleratedPresenter::ResetPresentThread( 661 PresentThread* present_thread) { 662 TRACE_EVENT0("gpu", "ResetPresentThread"); 663 664 // present_thread_ can be accessed without the lock because it is immutable. 665 if (present_thread_ != present_thread) 666 return; 667 668 present_thread_->lock()->AssertAcquired(); 669 670 source_texture_ = NULL; 671 swap_chain_ = NULL; 672 quantized_size_ = gfx::Size(); 673} 674 675#if defined(USE_AURA) 676void AcceleratedPresenter::SetNewTargetWindow(gfx::PluginWindowHandle window) { 677 window_ = window; 678 swap_chain_ = NULL; 679} 680#endif 681 682AcceleratedPresenter::~AcceleratedPresenter() { 683} 684 685bool AcceleratedPresenter::IsSwapChainInitialized() const { 686 base::AutoLock locked(*present_thread_->lock()); 687 688 return !!swap_chain_; 689} 690 691void AcceleratedPresenter::DoPresentAndAcknowledge( 692 const gfx::Size& size, 693 int64 surface_handle, 694 const ui::LatencyInfo& latency_info, 695 const CompletionTask& completion_task) { 696 TRACE_EVENT2( 697 "gpu", "DoPresentAndAcknowledge", 698 "width", size.width(), 699 "height", size.height()); 700 701 HRESULT hr; 702 703 base::AutoLock locked(*present_thread_->lock()); 704 705 latency_info_.MergeWith(latency_info); 706 707 // Initialize the device lazily since calling Direct3D can crash bots. 708 present_thread_->InitDevice(); 709 710 if (!present_thread_->device()) { 711 completion_task.Run( 712 false, base::TimeTicks(), base::TimeDelta(), ui::LatencyInfo()); 713 TRACE_EVENT0("gpu", "EarlyOut_NoDevice"); 714 return; 715 } 716 717 // Ensure the task is acknowledged on early out after this point. 718 base::ScopedClosureRunner scoped_completion_runner( 719 base::Bind(completion_task, 720 true, 721 base::TimeTicks(), 722 base::TimeDelta(), 723 ui::LatencyInfo())); 724 725 // If invalidated, do nothing, the window is gone. 726 if (!window_) { 727 TRACE_EVENT0("gpu", "EarlyOut_NoWindow"); 728 return; 729 } 730 731#if !defined(USE_AURA) 732 // If the window is a different size than the swap chain that is being 733 // presented then drop the frame. 734 gfx::Size window_size = GetWindowSize(); 735 bool size_mismatch = size != window_size; 736 if (ui::IsInHighDPIMode()) { 737 // Check if the size mismatch is within allowable round off or truncation 738 // error. 739 gfx::Size dip_size = ui::win::ScreenToDIPSize(window_size); 740 gfx::Size pixel_size = ui::win::DIPToScreenSize(dip_size); 741 size_mismatch = abs(window_size.width() - size.width()) > 742 abs(window_size.width() - pixel_size.width()) || 743 abs(window_size.height() - size.height()) > 744 abs(window_size.height() - pixel_size.height()); 745 } 746 if (hidden_ && size_mismatch) { 747 TRACE_EVENT2("gpu", "EarlyOut_WrongWindowSize", 748 "backwidth", size.width(), "backheight", size.height()); 749 TRACE_EVENT2("gpu", "EarlyOut_WrongWindowSize2", 750 "windowwidth", window_size.width(), 751 "windowheight", window_size.height()); 752 return; 753 } 754#endif 755 // Round up size so the swap chain is not continuously resized with the 756 // surface, which could lead to memory fragmentation. 757 const int kRound = 64; 758 gfx::Size quantized_size( 759 std::max(1, (size.width() + kRound - 1) / kRound * kRound), 760 std::max(1, (size.height() + kRound - 1) / kRound * kRound)); 761 762 // Ensure the swap chain exists and is the same size (rounded up) as the 763 // surface to be presented. 764 if (!swap_chain_ || quantized_size_ != quantized_size) { 765 TRACE_EVENT0("gpu", "CreateAdditionalSwapChain"); 766 quantized_size_ = quantized_size; 767 768 D3DPRESENT_PARAMETERS parameters = { 0 }; 769 parameters.BackBufferWidth = quantized_size.width(); 770 parameters.BackBufferHeight = quantized_size.height(); 771 parameters.BackBufferCount = 1; 772 parameters.BackBufferFormat = D3DFMT_A8R8G8B8; 773 parameters.hDeviceWindow = window_; 774 parameters.Windowed = TRUE; 775 parameters.Flags = 0; 776 parameters.PresentationInterval = GetPresentationInterval(); 777 parameters.SwapEffect = D3DSWAPEFFECT_COPY; 778 779 swap_chain_ = NULL; 780 HRESULT hr = present_thread_->device()->CreateAdditionalSwapChain( 781 ¶meters, 782 swap_chain_.Receive()); 783 if (FAILED(hr)) { 784 LOG(ERROR) << "Failed to create swap chain " 785 << quantized_size.width() << " x " <<quantized_size.height(); 786 return; 787 } 788 } 789 790 if (!source_texture_.get()) { 791 TRACE_EVENT0("gpu", "OpenSharedTexture"); 792 if (!d3d_utils::OpenSharedTexture(present_thread_->device(), 793 surface_handle, 794 size, 795 source_texture_.Receive())) { 796 LOG(ERROR) << "Failed to open shared texture"; 797 return; 798 } 799 } 800 801 base::win::ScopedComPtr<IDirect3DSurface9> source_surface; 802 hr = source_texture_->GetSurfaceLevel(0, source_surface.Receive()); 803 if (FAILED(hr)) { 804 TRACE_EVENT0("gpu", "EarlyOut_NoSurfaceLevel"); 805 LOG(ERROR) << "Failed to get source surface"; 806 return; 807 } 808 809 base::win::ScopedComPtr<IDirect3DSurface9> dest_surface; 810 hr = swap_chain_->GetBackBuffer(0, 811 D3DBACKBUFFER_TYPE_MONO, 812 dest_surface.Receive()); 813 if (FAILED(hr)) { 814 TRACE_EVENT0("gpu", "EarlyOut_NoBackbuffer"); 815 LOG(ERROR) << "Failed to get back buffer"; 816 return; 817 } 818 819 RECT rect = { 820 0, 0, 821 size.width(), size.height() 822 }; 823 824 { 825 TRACE_EVENT0("gpu", "Copy"); 826 827 // Copy while flipping the source texture on the vertical axis. 828 bool result = present_thread_->surface_transformer()->CopyInverted( 829 source_texture_, dest_surface, size); 830 if (!result) { 831 LOG(ERROR) << "Failed to copy shared texture"; 832 return; 833 } 834 } 835 836 hr = present_thread_->query()->Issue(D3DISSUE_END); 837 if (FAILED(hr)) { 838 LOG(ERROR) << "Failed to issue query"; 839 return; 840 } 841 842 present_size_ = size; 843 844 // If it is expected that Direct3D cannot be used reliably because the window 845 // is resizing, fall back to presenting with GDI. 846 if (CheckDirect3DWillWork()) { 847 TRACE_EVENT0("gpu", "PresentD3D"); 848 849 hr = swap_chain_->Present(&rect, &rect, window_, NULL, 0); 850 851 if (FAILED(hr)) { 852 if (present_thread_->IsDeviceLost()) 853 present_thread_->ResetDevice(); 854 return; 855 } 856 } else { 857 HDC dc = GetDC(window_); 858 PresentWithGDI(dc); 859 ReleaseDC(window_, dc); 860 } 861 862 latency_info_.swap_timestamp = base::TimeTicks::HighResNow(); 863 864 hidden_ = false; 865 866 D3DDISPLAYMODE display_mode; 867 hr = present_thread_->device()->GetDisplayMode(0, &display_mode); 868 if (FAILED(hr)) { 869 LOG(ERROR) << "Failed to get display mode"; 870 return; 871 } 872 873 D3DRASTER_STATUS raster_status; 874 hr = swap_chain_->GetRasterStatus(&raster_status); 875 if (FAILED(hr)) { 876 LOG(ERROR) << "Failed to get raster status"; 877 return; 878 } 879 880 // I can't figure out how to determine how many scanlines are in the 881 // vertical blank so clamp it such that scanline / height <= 1. 882 int clamped_scanline = std::min(raster_status.ScanLine, display_mode.Height); 883 884 // The Internet says that on some GPUs, the scanline is not available 885 // while in the vertical blank. 886 if (raster_status.InVBlank) 887 clamped_scanline = display_mode.Height; 888 889 base::TimeTicks current_time = base::TimeTicks::HighResNow(); 890 891 // Figure out approximately how far back in time the last vsync was based on 892 // the ratio of the raster scanline to the display height. 893 base::TimeTicks last_vsync_time; 894 base::TimeDelta refresh_period; 895 if (display_mode.Height) { 896 last_vsync_time = current_time - 897 base::TimeDelta::FromMilliseconds((clamped_scanline * 1000) / 898 (display_mode.RefreshRate * display_mode.Height)); 899 refresh_period = base::TimeDelta::FromMicroseconds( 900 1000000 / display_mode.RefreshRate); 901 } 902 903 // Wait for the StretchRect to complete before notifying the GPU process 904 // that it is safe to write to its backing store again. 905 { 906 TRACE_EVENT0("gpu", "spin"); 907 908 do { 909 hr = present_thread_->query()->GetData(NULL, 0, D3DGETDATA_FLUSH); 910 if (hr == S_FALSE) { 911 Sleep(1); 912 913 if (present_thread_->IsDeviceLost()) { 914 present_thread_->ResetDevice(); 915 return; 916 } 917 } 918 } while (hr == S_FALSE); 919 } 920 921 scoped_completion_runner.Release(); 922 completion_task.Run(true, last_vsync_time, refresh_period, latency_info_); 923 latency_info_.Clear(); 924} 925 926void AcceleratedPresenter::DoSuspend() { 927 base::AutoLock locked(*present_thread_->lock()); 928 swap_chain_ = NULL; 929} 930 931void AcceleratedPresenter::DoReleaseSurface() { 932 base::AutoLock locked(*present_thread_->lock()); 933 present_thread_->InitDevice(); 934 source_texture_.Release(); 935} 936 937void AcceleratedPresenter::PresentWithGDI(HDC dc) { 938 TRACE_EVENT0("gpu", "PresentWithGDI"); 939 940 if (!present_thread_->device()) { 941 LOG(ERROR) << "No device"; 942 return; 943 } 944 945 if (!swap_chain_) { 946 LOG(ERROR) << "No swap chain"; 947 return; 948 } 949 950 base::win::ScopedComPtr<IDirect3DTexture9> system_texture; 951 { 952 TRACE_EVENT0("gpu", "CreateSystemTexture"); 953 HRESULT hr = present_thread_->device()->CreateTexture( 954 quantized_size_.width(), 955 quantized_size_.height(), 956 1, 957 0, 958 D3DFMT_A8R8G8B8, 959 D3DPOOL_SYSTEMMEM, 960 system_texture.Receive(), 961 NULL); 962 if (FAILED(hr)) { 963 LOG(ERROR) << "Failed to create system memory texture"; 964 return; 965 } 966 } 967 968 base::win::ScopedComPtr<IDirect3DSurface9> system_surface; 969 HRESULT hr = system_texture->GetSurfaceLevel(0, system_surface.Receive()); 970 DCHECK(SUCCEEDED(hr)); 971 972 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer; 973 hr = swap_chain_->GetBackBuffer(0, 974 D3DBACKBUFFER_TYPE_MONO, 975 back_buffer.Receive()); 976 DCHECK(SUCCEEDED(hr)); 977 978 { 979 TRACE_EVENT0("gpu", "GetRenderTargetData"); 980 hr = present_thread_->device()->GetRenderTargetData(back_buffer, 981 system_surface); 982 983 if (FAILED(hr)) { 984 if (present_thread_->IsDeviceLost()) { 985 present_thread_->message_loop()->PostTask( 986 FROM_HERE, 987 base::Bind(&PresentThread::LockAndResetDevice, present_thread_)); 988 } 989 return; 990 } 991 992 DCHECK(SUCCEEDED(hr)); 993 } 994 995 D3DLOCKED_RECT locked_surface; 996 hr = system_surface->LockRect(&locked_surface, NULL, D3DLOCK_READONLY); 997 DCHECK(SUCCEEDED(hr)); 998 999 BITMAPINFO bitmap_info = { 1000 { 1001 sizeof(BITMAPINFOHEADER), 1002 quantized_size_.width(), 1003 -quantized_size_.height(), 1004 1, // planes 1005 32, // bitcount 1006 BI_RGB 1007 }, 1008 { 1009 {0, 0, 0, 0} 1010 } 1011 }; 1012 1013 { 1014 TRACE_EVENT0("gpu", "StretchDIBits"); 1015 StretchDIBits(dc, 1016 0, 0, 1017 present_size_.width(), 1018 present_size_.height(), 1019 0, 0, 1020 present_size_.width(), 1021 present_size_.height(), 1022 locked_surface.pBits, 1023 &bitmap_info, 1024 DIB_RGB_COLORS, 1025 SRCCOPY); 1026 } 1027 1028 system_surface->UnlockRect(); 1029} 1030 1031gfx::Size AcceleratedPresenter::GetWindowSize() { 1032 RECT rect; 1033 GetClientRect(window_, &rect); 1034 return gfx::Rect(rect).size(); 1035} 1036 1037bool AcceleratedPresenter::CheckDirect3DWillWork() { 1038 // On a composited desktop, when the screen saver or logon screen are 1039 // active, D3D presents never make it to the window but GDI presents 1040 // do. If the session is locked GDI presents can be avoided since 1041 // the window gets a message on unlock and forces a repaint. 1042 if (!is_session_locked_ && ui::win::IsAeroGlassEnabled()) { 1043 // Failure to open the input desktop is a sign of running with a non-default 1044 // desktop. 1045 HDESK input_desktop = ::OpenInputDesktop(0, 0, GENERIC_READ); 1046 if (!input_desktop) 1047 return false; 1048 ::CloseDesktop(input_desktop); 1049 } 1050 1051 gfx::Size window_size = GetWindowSize(); 1052 if (window_size != last_window_size_ && last_window_size_.GetArea() != 0) { 1053 last_window_size_ = window_size; 1054 last_window_resize_time_ = base::Time::Now(); 1055 return false; 1056 } 1057 1058 if (do_present_with_GDI_ && hidden_) { 1059 if (DoFirstShowPresentWithGDI()) 1060 do_present_with_GDI_ = false; 1061 1062 return false; 1063 } 1064 1065 return base::Time::Now() - last_window_resize_time_ > 1066 base::TimeDelta::FromMilliseconds(100); 1067} 1068 1069AcceleratedSurface::AcceleratedSurface(gfx::PluginWindowHandle window) 1070 : presenter_(g_accelerated_presenter_map.Pointer()->CreatePresenter( 1071 window)) { 1072} 1073 1074AcceleratedSurface::~AcceleratedSurface() { 1075 g_accelerated_presenter_map.Pointer()->RemovePresenter(presenter_); 1076 presenter_->Invalidate(); 1077} 1078 1079void AcceleratedSurface::Present(HDC dc) { 1080 presenter_->Present(dc); 1081} 1082 1083bool AcceleratedSurface::IsReadyForCopy() const { 1084 return !!presenter_ && presenter_->IsSwapChainInitialized(); 1085} 1086 1087 1088void AcceleratedSurface::AsyncCopyTo( 1089 const gfx::Rect& src_subrect, 1090 const gfx::Size& dst_size, 1091 const base::Callback<void(bool, const SkBitmap&)>& callback) { 1092 presenter_->AsyncCopyTo(src_subrect, dst_size, callback); 1093} 1094 1095void AcceleratedSurface::AsyncCopyToVideoFrame( 1096 const gfx::Rect& src_subrect, 1097 const scoped_refptr<media::VideoFrame>& target, 1098 const base::Callback<void(bool)>& callback) { 1099 presenter_->AsyncCopyToVideoFrame(src_subrect, target, callback); 1100} 1101 1102void AcceleratedSurface::Suspend() { 1103 presenter_->Suspend(); 1104} 1105 1106void AcceleratedSurface::WasHidden() { 1107 presenter_->WasHidden(); 1108} 1109 1110void AcceleratedSurface::SetIsSessionLocked(bool locked) { 1111 presenter_->SetIsSessionLocked(locked); 1112} 1113