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