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 38 namespace d3d_utils = ui_surface_d3d9_utils; 39 40 namespace { 41 42 UINT GetPresentationInterval() { 43 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync)) 44 return D3DPRESENT_INTERVAL_IMMEDIATE; 45 else 46 return D3DPRESENT_INTERVAL_ONE; 47 } 48 49 bool DoFirstShowPresentWithGDI() { 50 return CommandLine::ForCurrentProcess()->HasSwitch( 51 switches::kDoFirstShowPresentWithGDI); 52 } 53 54 bool DoAllShowPresentWithGDI() { 55 return CommandLine::ForCurrentProcess()->HasSwitch( 56 switches::kDoAllShowPresentWithGDI); 57 } 58 59 // Use a SurfaceReader to copy into one plane of the VideoFrame. 60 bool 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. 74 class 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. 123 class 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. 143 class 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 164 base::LazyInstance<PresentThreadPool> 165 g_present_thread_pool = LAZY_INSTANCE_INITIALIZER; 166 167 base::LazyInstance<AcceleratedPresenterMap> 168 g_accelerated_presenter_map = LAZY_INSTANCE_INITIALIZER; 169 170 PresentThread::PresentThread(const char* name, uint64 adapter_luid) 171 : base::Thread(name), 172 adapter_luid_(adapter_luid) { 173 } 174 175 void 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 188 void 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 199 void PresentThread::LockAndResetDevice() { 200 base::AutoLock locked(lock_); 201 ResetDevice(); 202 } 203 204 void 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 243 bool PresentThread::IsDeviceLost() { 244 lock_.AssertAcquired(); 245 246 HRESULT hr = device_->CheckDeviceState(NULL); 247 return FAILED(hr) || hr == S_PRESENT_MODE_CHANGED; 248 } 249 250 void PresentThread::Init() { 251 TRACE_EVENT0("gpu", "Initialize thread"); 252 } 253 254 void 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 262 PresentThread::~PresentThread() { 263 Stop(); 264 } 265 266 PresentThreadPool::PresentThreadPool() : next_thread_(0) { 267 } 268 269 PresentThread* 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 285 void 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 302 AcceleratedPresenterMap::AcceleratedPresenterMap() { 303 } 304 305 scoped_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 317 void 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 332 scoped_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 348 void 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 359 AcceleratedPresenter::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 370 void AcceleratedPresenter::SetAdapterLUID(uint64 adapter_luid) { 371 return g_present_thread_pool.Pointer()->SetAdapterLUID(adapter_luid); 372 } 373 374 375 // static 376 scoped_refptr<AcceleratedPresenter> AcceleratedPresenter::GetForWindow( 377 gfx::PluginWindowHandle window) { 378 return g_accelerated_presenter_map.Pointer()->GetPresenter(window); 379 } 380 381 void 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 404 void 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 420 void 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 434 void 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 448 void 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 460 void 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 470 bool 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 544 bool 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 628 void AcceleratedPresenter::Suspend() { 629 present_thread_->message_loop()->PostTask( 630 FROM_HERE, 631 base::Bind(&AcceleratedPresenter::DoSuspend, 632 this)); 633 } 634 635 void AcceleratedPresenter::WasHidden() { 636 base::AutoLock locked(*present_thread_->lock()); 637 hidden_ = true; 638 } 639 640 void AcceleratedPresenter::ReleaseSurface() { 641 present_thread_->message_loop()->PostTask( 642 FROM_HERE, 643 base::Bind(&AcceleratedPresenter::DoReleaseSurface, 644 this)); 645 } 646 647 void AcceleratedPresenter::SetIsSessionLocked(bool locked) { 648 is_session_locked_ = locked; 649 } 650 651 void 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 660 void 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) 676 void AcceleratedPresenter::SetNewTargetWindow(gfx::PluginWindowHandle window) { 677 window_ = window; 678 swap_chain_ = NULL; 679 } 680 #endif 681 682 AcceleratedPresenter::~AcceleratedPresenter() { 683 } 684 685 bool AcceleratedPresenter::IsSwapChainInitialized() const { 686 base::AutoLock locked(*present_thread_->lock()); 687 688 return !!swap_chain_; 689 } 690 691 void 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 the window is a different size than the swap chain that is being 732 // presented then drop the frame. 733 gfx::Size window_size = GetWindowSize(); 734 bool size_mismatch = size != window_size; 735 if (ui::IsInHighDPIMode()) { 736 // Check if the size mismatch is within allowable round off or truncation 737 // error. 738 gfx::Size dip_size = ui::win::ScreenToDIPSize(window_size); 739 gfx::Size pixel_size = ui::win::DIPToScreenSize(dip_size); 740 size_mismatch = abs(window_size.width() - size.width()) > 741 abs(window_size.width() - pixel_size.width()) || 742 abs(window_size.height() - size.height()) > 743 abs(window_size.height() - pixel_size.height()); 744 } 745 if (hidden_ && size_mismatch) { 746 TRACE_EVENT2("gpu", "EarlyOut_WrongWindowSize", 747 "backwidth", size.width(), "backheight", size.height()); 748 TRACE_EVENT2("gpu", "EarlyOut_WrongWindowSize2", 749 "windowwidth", window_size.width(), 750 "windowheight", window_size.height()); 751 return; 752 } 753 754 // Round up size so the swap chain is not continuously resized with the 755 // surface, which could lead to memory fragmentation. 756 const int kRound = 64; 757 gfx::Size quantized_size( 758 std::max(1, (size.width() + kRound - 1) / kRound * kRound), 759 std::max(1, (size.height() + kRound - 1) / kRound * kRound)); 760 761 // Ensure the swap chain exists and is the same size (rounded up) as the 762 // surface to be presented. 763 if (!swap_chain_ || quantized_size_ != quantized_size) { 764 TRACE_EVENT0("gpu", "CreateAdditionalSwapChain"); 765 quantized_size_ = quantized_size; 766 767 D3DPRESENT_PARAMETERS parameters = { 0 }; 768 parameters.BackBufferWidth = quantized_size.width(); 769 parameters.BackBufferHeight = quantized_size.height(); 770 parameters.BackBufferCount = 1; 771 parameters.BackBufferFormat = D3DFMT_A8R8G8B8; 772 parameters.hDeviceWindow = window_; 773 parameters.Windowed = TRUE; 774 parameters.Flags = 0; 775 parameters.PresentationInterval = GetPresentationInterval(); 776 parameters.SwapEffect = D3DSWAPEFFECT_COPY; 777 778 swap_chain_ = NULL; 779 HRESULT hr = present_thread_->device()->CreateAdditionalSwapChain( 780 ¶meters, 781 swap_chain_.Receive()); 782 if (FAILED(hr)) { 783 LOG(ERROR) << "Failed to create swap chain " 784 << quantized_size.width() << " x " <<quantized_size.height(); 785 return; 786 } 787 } 788 789 if (!source_texture_.get()) { 790 TRACE_EVENT0("gpu", "OpenSharedTexture"); 791 if (!d3d_utils::OpenSharedTexture(present_thread_->device(), 792 surface_handle, 793 size, 794 source_texture_.Receive())) { 795 LOG(ERROR) << "Failed to open shared texture"; 796 return; 797 } 798 } 799 800 base::win::ScopedComPtr<IDirect3DSurface9> source_surface; 801 hr = source_texture_->GetSurfaceLevel(0, source_surface.Receive()); 802 if (FAILED(hr)) { 803 TRACE_EVENT0("gpu", "EarlyOut_NoSurfaceLevel"); 804 LOG(ERROR) << "Failed to get source surface"; 805 return; 806 } 807 808 base::win::ScopedComPtr<IDirect3DSurface9> dest_surface; 809 hr = swap_chain_->GetBackBuffer(0, 810 D3DBACKBUFFER_TYPE_MONO, 811 dest_surface.Receive()); 812 if (FAILED(hr)) { 813 TRACE_EVENT0("gpu", "EarlyOut_NoBackbuffer"); 814 LOG(ERROR) << "Failed to get back buffer"; 815 return; 816 } 817 818 RECT rect = { 819 0, 0, 820 size.width(), size.height() 821 }; 822 823 { 824 TRACE_EVENT0("gpu", "Copy"); 825 826 // Copy while flipping the source texture on the vertical axis. 827 bool result = present_thread_->surface_transformer()->CopyInverted( 828 source_texture_, dest_surface, size); 829 if (!result) { 830 LOG(ERROR) << "Failed to copy shared texture"; 831 return; 832 } 833 } 834 835 hr = present_thread_->query()->Issue(D3DISSUE_END); 836 if (FAILED(hr)) { 837 LOG(ERROR) << "Failed to issue query"; 838 return; 839 } 840 841 present_size_ = size; 842 843 // If it is expected that Direct3D cannot be used reliably because the window 844 // is resizing, fall back to presenting with GDI. 845 if (CheckDirect3DWillWork()) { 846 TRACE_EVENT0("gpu", "PresentD3D"); 847 848 hr = swap_chain_->Present(&rect, &rect, window_, NULL, 0); 849 850 if (FAILED(hr)) { 851 if (present_thread_->IsDeviceLost()) 852 present_thread_->ResetDevice(); 853 return; 854 } 855 } else { 856 HDC dc = GetDC(window_); 857 PresentWithGDI(dc); 858 ReleaseDC(window_, dc); 859 } 860 861 latency_info_.swap_timestamp = base::TimeTicks::HighResNow(); 862 863 hidden_ = false; 864 865 D3DDISPLAYMODE display_mode; 866 hr = present_thread_->device()->GetDisplayMode(0, &display_mode); 867 if (FAILED(hr)) { 868 LOG(ERROR) << "Failed to get display mode"; 869 return; 870 } 871 872 D3DRASTER_STATUS raster_status; 873 hr = swap_chain_->GetRasterStatus(&raster_status); 874 if (FAILED(hr)) { 875 LOG(ERROR) << "Failed to get raster status"; 876 return; 877 } 878 879 // I can't figure out how to determine how many scanlines are in the 880 // vertical blank so clamp it such that scanline / height <= 1. 881 int clamped_scanline = std::min(raster_status.ScanLine, display_mode.Height); 882 883 // The Internet says that on some GPUs, the scanline is not available 884 // while in the vertical blank. 885 if (raster_status.InVBlank) 886 clamped_scanline = display_mode.Height; 887 888 base::TimeTicks current_time = base::TimeTicks::HighResNow(); 889 890 // Figure out approximately how far back in time the last vsync was based on 891 // the ratio of the raster scanline to the display height. 892 base::TimeTicks last_vsync_time; 893 base::TimeDelta refresh_period; 894 if (display_mode.Height) { 895 last_vsync_time = current_time - 896 base::TimeDelta::FromMilliseconds((clamped_scanline * 1000) / 897 (display_mode.RefreshRate * display_mode.Height)); 898 refresh_period = base::TimeDelta::FromMicroseconds( 899 1000000 / display_mode.RefreshRate); 900 } 901 902 // Wait for the StretchRect to complete before notifying the GPU process 903 // that it is safe to write to its backing store again. 904 { 905 TRACE_EVENT0("gpu", "spin"); 906 907 do { 908 hr = present_thread_->query()->GetData(NULL, 0, D3DGETDATA_FLUSH); 909 if (hr == S_FALSE) { 910 Sleep(1); 911 912 if (present_thread_->IsDeviceLost()) { 913 present_thread_->ResetDevice(); 914 return; 915 } 916 } 917 } while (hr == S_FALSE); 918 } 919 920 scoped_completion_runner.Release(); 921 completion_task.Run(true, last_vsync_time, refresh_period, latency_info_); 922 latency_info_.Clear(); 923 } 924 925 void AcceleratedPresenter::DoSuspend() { 926 base::AutoLock locked(*present_thread_->lock()); 927 swap_chain_ = NULL; 928 } 929 930 void AcceleratedPresenter::DoReleaseSurface() { 931 base::AutoLock locked(*present_thread_->lock()); 932 present_thread_->InitDevice(); 933 source_texture_.Release(); 934 } 935 936 void AcceleratedPresenter::PresentWithGDI(HDC dc) { 937 TRACE_EVENT0("gpu", "PresentWithGDI"); 938 939 if (!present_thread_->device()) { 940 LOG(ERROR) << "No device"; 941 return; 942 } 943 944 if (!swap_chain_) { 945 LOG(ERROR) << "No swap chain"; 946 return; 947 } 948 949 base::win::ScopedComPtr<IDirect3DTexture9> system_texture; 950 { 951 TRACE_EVENT0("gpu", "CreateSystemTexture"); 952 HRESULT hr = present_thread_->device()->CreateTexture( 953 quantized_size_.width(), 954 quantized_size_.height(), 955 1, 956 0, 957 D3DFMT_A8R8G8B8, 958 D3DPOOL_SYSTEMMEM, 959 system_texture.Receive(), 960 NULL); 961 if (FAILED(hr)) { 962 LOG(ERROR) << "Failed to create system memory texture"; 963 return; 964 } 965 } 966 967 base::win::ScopedComPtr<IDirect3DSurface9> system_surface; 968 HRESULT hr = system_texture->GetSurfaceLevel(0, system_surface.Receive()); 969 DCHECK(SUCCEEDED(hr)); 970 971 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer; 972 hr = swap_chain_->GetBackBuffer(0, 973 D3DBACKBUFFER_TYPE_MONO, 974 back_buffer.Receive()); 975 DCHECK(SUCCEEDED(hr)); 976 977 { 978 TRACE_EVENT0("gpu", "GetRenderTargetData"); 979 hr = present_thread_->device()->GetRenderTargetData(back_buffer, 980 system_surface); 981 982 if (FAILED(hr)) { 983 if (present_thread_->IsDeviceLost()) { 984 present_thread_->message_loop()->PostTask( 985 FROM_HERE, 986 base::Bind(&PresentThread::LockAndResetDevice, present_thread_)); 987 } 988 return; 989 } 990 991 DCHECK(SUCCEEDED(hr)); 992 } 993 994 D3DLOCKED_RECT locked_surface; 995 hr = system_surface->LockRect(&locked_surface, NULL, D3DLOCK_READONLY); 996 DCHECK(SUCCEEDED(hr)); 997 998 BITMAPINFO bitmap_info = { 999 { 1000 sizeof(BITMAPINFOHEADER), 1001 quantized_size_.width(), 1002 -quantized_size_.height(), 1003 1, // planes 1004 32, // bitcount 1005 BI_RGB 1006 }, 1007 { 1008 {0, 0, 0, 0} 1009 } 1010 }; 1011 1012 { 1013 TRACE_EVENT0("gpu", "StretchDIBits"); 1014 StretchDIBits(dc, 1015 0, 0, 1016 present_size_.width(), 1017 present_size_.height(), 1018 0, 0, 1019 present_size_.width(), 1020 present_size_.height(), 1021 locked_surface.pBits, 1022 &bitmap_info, 1023 DIB_RGB_COLORS, 1024 SRCCOPY); 1025 } 1026 1027 system_surface->UnlockRect(); 1028 } 1029 1030 gfx::Size AcceleratedPresenter::GetWindowSize() { 1031 RECT rect; 1032 GetClientRect(window_, &rect); 1033 return gfx::Rect(rect).size(); 1034 } 1035 1036 bool AcceleratedPresenter::CheckDirect3DWillWork() { 1037 // On a composited desktop, when the screen saver or logon screen are 1038 // active, D3D presents never make it to the window but GDI presents 1039 // do. If the session is locked GDI presents can be avoided since 1040 // the window gets a message on unlock and forces a repaint. 1041 if (!is_session_locked_ && ui::win::IsAeroGlassEnabled()) { 1042 // Failure to open the input desktop is a sign of running with a non-default 1043 // desktop. 1044 HDESK input_desktop = ::OpenInputDesktop(0, 0, GENERIC_READ); 1045 if (!input_desktop) 1046 return false; 1047 ::CloseDesktop(input_desktop); 1048 } 1049 1050 gfx::Size window_size = GetWindowSize(); 1051 if (window_size != last_window_size_ && last_window_size_.GetArea() != 0) { 1052 last_window_size_ = window_size; 1053 last_window_resize_time_ = base::Time::Now(); 1054 return false; 1055 } 1056 1057 if (do_present_with_GDI_ && hidden_) { 1058 if (DoFirstShowPresentWithGDI()) 1059 do_present_with_GDI_ = false; 1060 1061 return false; 1062 } 1063 1064 return base::Time::Now() - last_window_resize_time_ > 1065 base::TimeDelta::FromMilliseconds(100); 1066 } 1067 1068 AcceleratedSurface::AcceleratedSurface(gfx::PluginWindowHandle window) 1069 : presenter_(g_accelerated_presenter_map.Pointer()->CreatePresenter( 1070 window)) { 1071 } 1072 1073 AcceleratedSurface::~AcceleratedSurface() { 1074 g_accelerated_presenter_map.Pointer()->RemovePresenter(presenter_); 1075 presenter_->Invalidate(); 1076 } 1077 1078 void AcceleratedSurface::Present(HDC dc) { 1079 presenter_->Present(dc); 1080 } 1081 1082 bool AcceleratedSurface::IsReadyForCopy() const { 1083 return !!presenter_ && presenter_->IsSwapChainInitialized(); 1084 } 1085 1086 1087 void AcceleratedSurface::AsyncCopyTo( 1088 const gfx::Rect& src_subrect, 1089 const gfx::Size& dst_size, 1090 const base::Callback<void(bool, const SkBitmap&)>& callback) { 1091 presenter_->AsyncCopyTo(src_subrect, dst_size, callback); 1092 } 1093 1094 void AcceleratedSurface::AsyncCopyToVideoFrame( 1095 const gfx::Rect& src_subrect, 1096 const scoped_refptr<media::VideoFrame>& target, 1097 const base::Callback<void(bool)>& callback) { 1098 presenter_->AsyncCopyToVideoFrame(src_subrect, target, callback); 1099 } 1100 1101 void AcceleratedSurface::Suspend() { 1102 presenter_->Suspend(); 1103 } 1104 1105 void AcceleratedSurface::WasHidden() { 1106 presenter_->WasHidden(); 1107 } 1108 1109 void AcceleratedSurface::SetIsSessionLocked(bool locked) { 1110 presenter_->SetIsSessionLocked(locked); 1111 } 1112