| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/surface/accelerated_surface_win.h" | 5 #include "ui/surface/accelerated_surface_win.h" |
| 6 | 6 |
| 7 #include <windows.h> | 7 #include <windows.h> |
| 8 #include <algorithm> | 8 #include <algorithm> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/bind_helpers.h" | 11 #include "base/bind_helpers.h" |
| 12 #include "base/callback.h" | 12 #include "base/callback.h" |
| 13 #include "base/command_line.h" | 13 #include "base/command_line.h" |
| 14 #include "base/debug/trace_event.h" | 14 #include "base/debug/trace_event.h" |
| 15 #include "base/file_path.h" | 15 #include "base/file_path.h" |
| 16 #include "base/lazy_instance.h" | 16 #include "base/lazy_instance.h" |
| 17 #include "base/memory/scoped_ptr.h" | 17 #include "base/memory/scoped_ptr.h" |
| 18 #include "base/message_loop_proxy.h" | 18 #include "base/message_loop_proxy.h" |
| 19 #include "base/scoped_native_library.h" | 19 #include "base/scoped_native_library.h" |
| 20 #include "base/stringprintf.h" | 20 #include "base/stringprintf.h" |
| 21 #include "base/synchronization/waitable_event.h" | 21 #include "base/synchronization/waitable_event.h" |
| 22 #include "base/threading/thread.h" | 22 #include "base/threading/thread.h" |
| 23 #include "base/threading/thread_restrictions.h" | 23 #include "base/threading/thread_restrictions.h" |
| 24 #include "base/time.h" | |
| 25 #include "base/win/wrapped_window_proc.h" | 24 #include "base/win/wrapped_window_proc.h" |
| 25 #include "media/base/video_frame.h" |
| 26 #include "media/base/video_util.h" |
| 26 #include "third_party/skia/include/core/SkBitmap.h" | 27 #include "third_party/skia/include/core/SkBitmap.h" |
| 27 #include "ui/base/win/dpi.h" | 28 #include "ui/base/win/dpi.h" |
| 28 #include "ui/base/win/hwnd_util.h" | 29 #include "ui/base/win/hwnd_util.h" |
| 29 #include "ui/base/win/shell.h" | 30 #include "ui/base/win/shell.h" |
| 30 #include "ui/gfx/rect.h" | 31 #include "ui/gfx/rect.h" |
| 31 #include "ui/gl/gl_switches.h" | 32 #include "ui/gl/gl_switches.h" |
| 32 #include "ui/surface/accelerated_surface_transformer_win.h" | 33 #include "ui/surface/accelerated_surface_transformer_win.h" |
| 33 #include "ui/surface/d3d9_utils_win.h" | 34 #include "ui/surface/d3d9_utils_win.h" |
| 34 #include "ui/surface/surface_switches.h" | 35 #include "ui/surface/surface_switches.h" |
| 35 | 36 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 47 bool DoFirstShowPresentWithGDI() { | 48 bool DoFirstShowPresentWithGDI() { |
| 48 return CommandLine::ForCurrentProcess()->HasSwitch( | 49 return CommandLine::ForCurrentProcess()->HasSwitch( |
| 49 switches::kDoFirstShowPresentWithGDI); | 50 switches::kDoFirstShowPresentWithGDI); |
| 50 } | 51 } |
| 51 | 52 |
| 52 bool DoAllShowPresentWithGDI() { | 53 bool DoAllShowPresentWithGDI() { |
| 53 return CommandLine::ForCurrentProcess()->HasSwitch( | 54 return CommandLine::ForCurrentProcess()->HasSwitch( |
| 54 switches::kDoAllShowPresentWithGDI); | 55 switches::kDoAllShowPresentWithGDI); |
| 55 } | 56 } |
| 56 | 57 |
| 58 // Lock a D3D surface, and invoke a VideoFrame copier on the result. |
| 59 bool LockAndCopyPlane(IDirect3DSurface9* src_surface, |
| 60 media::VideoFrame* dst_frame, |
| 61 size_t plane_id) { |
| 62 gfx::Size src_size = d3d_utils::GetSize(src_surface); |
| 63 |
| 64 D3DLOCKED_RECT locked_rect; |
| 65 { |
| 66 TRACE_EVENT0("gpu", "LockRect"); |
| 67 HRESULT hr = src_surface->LockRect(&locked_rect, NULL, |
| 68 D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK); |
| 69 if (FAILED(hr)) |
| 70 return false; |
| 71 } |
| 72 |
| 73 { |
| 74 TRACE_EVENT0("gpu", "memcpy"); |
| 75 uint8* src = reinterpret_cast<uint8*>(locked_rect.pBits); |
| 76 int src_stride = locked_rect.Pitch; |
| 77 media::CopyPlane(plane_id, src, src_stride, src_size.height(), dst_frame); |
| 78 } |
| 79 src_surface->UnlockRect(); |
| 80 return true; |
| 81 } |
| 82 |
| 83 // Return the largest centered rectangle with the same aspect ratio of |content| |
| 84 // that fits entirely inside of |bounds|. |
| 85 gfx::Rect ComputeLetterboxRegion( |
| 86 const gfx::Rect& bounds, |
| 87 const gfx::Size& content) { |
| 88 int64 x = static_cast<int64>(content.width()) * bounds.height(); |
| 89 int64 y = static_cast<int64>(bounds.width()) * content.height(); |
| 90 |
| 91 gfx::Size letterbox(bounds.width(), bounds.height()); |
| 92 if (y < x) |
| 93 letterbox.set_height(static_cast<int>(y / content.width())); |
| 94 else |
| 95 letterbox.set_width(static_cast<int>(x / content.height())); |
| 96 gfx::Rect result = bounds; |
| 97 result.ClampToCenteredSize(letterbox); |
| 98 return result; |
| 99 } |
| 100 |
| 57 } // namespace | 101 } // namespace |
| 58 | 102 |
| 59 // A PresentThread is a thread that is dedicated to presenting surfaces to a | 103 // A PresentThread is a thread that is dedicated to presenting surfaces to a |
| 60 // window. It owns a Direct3D device and a Direct3D query for this purpose. | 104 // window. It owns a Direct3D device and a Direct3D query for this purpose. |
| 61 class PresentThread : public base::Thread, | 105 class PresentThread : public base::Thread, |
| 62 public base::RefCountedThreadSafe<PresentThread> { | 106 public base::RefCountedThreadSafe<PresentThread> { |
| 63 public: | 107 public: |
| 64 explicit PresentThread(const char* name); | 108 explicit PresentThread(const char* name); |
| 65 | 109 |
| 66 IDirect3DDevice9Ex* device() { return device_.get(); } | 110 IDirect3DDevice9Ex* device() { return device_.get(); } |
| (...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 311 present_thread_->message_loop()->PostTask( | 355 present_thread_->message_loop()->PostTask( |
| 312 FROM_HERE, | 356 FROM_HERE, |
| 313 base::Bind(&AcceleratedPresenter::DoCopyToAndAcknowledge, | 357 base::Bind(&AcceleratedPresenter::DoCopyToAndAcknowledge, |
| 314 this, | 358 this, |
| 315 requested_src_subrect, | 359 requested_src_subrect, |
| 316 dst_size, | 360 dst_size, |
| 317 base::MessageLoopProxy::current(), | 361 base::MessageLoopProxy::current(), |
| 318 callback)); | 362 callback)); |
| 319 } | 363 } |
| 320 | 364 |
| 365 void AcceleratedPresenter::AsyncCopyToVideoFrame( |
| 366 const gfx::Rect& requested_src_subrect, |
| 367 const scoped_refptr<media::VideoFrame>& target, |
| 368 const base::Callback<void(bool)>& callback) { |
| 369 present_thread_->message_loop()->PostTask( |
| 370 FROM_HERE, |
| 371 base::Bind(&AcceleratedPresenter::DoCopyToVideoFrameAndAcknowledge, |
| 372 this, |
| 373 requested_src_subrect, |
| 374 target, |
| 375 base::MessageLoopProxy::current(), |
| 376 callback)); |
| 377 } |
| 378 |
| 321 void AcceleratedPresenter::DoCopyToAndAcknowledge( | 379 void AcceleratedPresenter::DoCopyToAndAcknowledge( |
| 322 const gfx::Rect& src_subrect, | 380 const gfx::Rect& src_subrect, |
| 323 const gfx::Size& dst_size, | 381 const gfx::Size& dst_size, |
| 324 scoped_refptr<base::SingleThreadTaskRunner> callback_runner, | 382 scoped_refptr<base::SingleThreadTaskRunner> callback_runner, |
| 325 const base::Callback<void(bool, const SkBitmap&)>& callback) { | 383 const base::Callback<void(bool, const SkBitmap&)>& callback) { |
| 326 | |
| 327 SkBitmap target; | 384 SkBitmap target; |
| 328 bool result = DoCopyTo(src_subrect, dst_size, &target); | 385 bool result = DoCopyToARGB(src_subrect, dst_size, &target); |
| 329 if (!result) | 386 if (!result) |
| 330 target.reset(); | 387 target.reset(); |
| 331 callback_runner->PostTask( | 388 callback_runner->PostTask(FROM_HERE, base::Bind(callback, result, target)); |
| 332 FROM_HERE, | |
| 333 base::Bind(callback, result, target)); | |
| 334 } | 389 } |
| 335 | 390 |
| 336 bool AcceleratedPresenter::DoCopyTo(const gfx::Rect& requested_src_subrect, | 391 void AcceleratedPresenter::DoCopyToVideoFrameAndAcknowledge( |
| 337 const gfx::Size& dst_size, | 392 const gfx::Rect& src_subrect, |
| 338 SkBitmap* bitmap) { | 393 const scoped_refptr<media::VideoFrame>& target, |
| 394 const scoped_refptr<base::SingleThreadTaskRunner>& callback_runner, |
| 395 const base::Callback<void(bool)>& callback) { |
| 396 |
| 397 bool result = DoCopyToYUV(src_subrect, target); |
| 398 callback_runner->PostTask(FROM_HERE, base::Bind(callback, result)); |
| 399 } |
| 400 |
| 401 bool AcceleratedPresenter::DoCopyToARGB(const gfx::Rect& requested_src_subrect, |
| 402 const gfx::Size& dst_size, |
| 403 SkBitmap* bitmap) { |
| 339 TRACE_EVENT2( | 404 TRACE_EVENT2( |
| 340 "gpu", "CopyTo", | 405 "gpu", "CopyTo", |
| 341 "width", dst_size.width(), | 406 "width", dst_size.width(), |
| 342 "height", dst_size.height()); | 407 "height", dst_size.height()); |
| 343 | 408 |
| 344 base::AutoLock locked(lock_); | 409 base::AutoLock locked(lock_); |
| 345 | 410 |
| 346 TRACE_EVENT0("gpu", "CopyTo_locked"); | |
| 347 | |
| 348 if (!swap_chain_) | 411 if (!swap_chain_) |
| 349 return false; | 412 return false; |
| 350 | 413 |
| 351 AcceleratedSurfaceTransformer* gpu_ops = | 414 AcceleratedSurfaceTransformer* gpu_ops = |
| 352 present_thread_->surface_transformer(); | 415 present_thread_->surface_transformer(); |
| 353 | 416 |
| 354 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer; | 417 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer; |
| 355 HRESULT hr = swap_chain_->GetBackBuffer(0, | 418 HRESULT hr = swap_chain_->GetBackBuffer(0, |
| 356 D3DBACKBUFFER_TYPE_MONO, | 419 D3DBACKBUFFER_TYPE_MONO, |
| 357 back_buffer.Receive()); | 420 back_buffer.Receive()); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 382 dst_size, | 445 dst_size, |
| 383 final_surface.Receive())) { | 446 final_surface.Receive())) { |
| 384 LOG(ERROR) << "Failed to create temporary lockable surface"; | 447 LOG(ERROR) << "Failed to create temporary lockable surface"; |
| 385 return false; | 448 return false; |
| 386 } | 449 } |
| 387 } | 450 } |
| 388 | 451 |
| 389 { | 452 { |
| 390 // Let the surface transformer start the resize into |final_surface|. | 453 // Let the surface transformer start the resize into |final_surface|. |
| 391 TRACE_EVENT0("gpu", "ResizeBilinear"); | 454 TRACE_EVENT0("gpu", "ResizeBilinear"); |
| 392 if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, final_surface)) { | 455 if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, |
| 456 final_surface, gfx::Rect(dst_size))) { |
| 393 LOG(ERROR) << "Failed to resize bilinear"; | 457 LOG(ERROR) << "Failed to resize bilinear"; |
| 394 return false; | 458 return false; |
| 395 } | 459 } |
| 396 } | 460 } |
| 397 | 461 |
| 398 D3DLOCKED_RECT locked_rect; | 462 D3DLOCKED_RECT locked_rect; |
| 399 | 463 |
| 400 // Empirical evidence seems to suggest that LockRect and memcpy are faster | 464 // Empirical evidence seems to suggest that LockRect and memcpy are faster |
| 401 // than would be GetRenderTargetData to an offscreen surface wrapping *buf. | 465 // than would be GetRenderTargetData to an offscreen surface wrapping *buf. |
| 402 { | 466 { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 423 | 487 |
| 424 memcpy(reinterpret_cast<int8*>(bitmap->getPixels()), | 488 memcpy(reinterpret_cast<int8*>(bitmap->getPixels()), |
| 425 reinterpret_cast<int8*>(locked_rect.pBits), | 489 reinterpret_cast<int8*>(locked_rect.pBits), |
| 426 locked_rect.Pitch * dst_size.height()); | 490 locked_rect.Pitch * dst_size.height()); |
| 427 } | 491 } |
| 428 final_surface->UnlockRect(); | 492 final_surface->UnlockRect(); |
| 429 | 493 |
| 430 return true; | 494 return true; |
| 431 } | 495 } |
| 432 | 496 |
| 497 bool AcceleratedPresenter::DoCopyToYUV( |
| 498 const gfx::Rect& requested_src_subrect, |
| 499 const scoped_refptr<media::VideoFrame>& frame) { |
| 500 gfx::Size dst_size = frame->coded_size(); |
| 501 TRACE_EVENT2( |
| 502 "gpu", "CopyToYUV", |
| 503 "width", dst_size.width(), |
| 504 "height", dst_size.height()); |
| 505 |
| 506 base::AutoLock locked(lock_); |
| 507 |
| 508 if (!swap_chain_) |
| 509 return false; |
| 510 |
| 511 AcceleratedSurfaceTransformer* gpu_ops = |
| 512 present_thread_->surface_transformer(); |
| 513 |
| 514 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer; |
| 515 HRESULT hr = swap_chain_->GetBackBuffer(0, |
| 516 D3DBACKBUFFER_TYPE_MONO, |
| 517 back_buffer.Receive()); |
| 518 if (FAILED(hr)) |
| 519 return false; |
| 520 |
| 521 D3DSURFACE_DESC desc; |
| 522 hr = back_buffer->GetDesc(&desc); |
| 523 if (FAILED(hr)) |
| 524 return false; |
| 525 |
| 526 const gfx::Size back_buffer_size(desc.Width, desc.Height); |
| 527 if (back_buffer_size.IsEmpty()) |
| 528 return false; |
| 529 |
| 530 // With window resizing, it's possible that the back buffer is smaller than |
| 531 // the requested src subset. Clip to the actual back buffer. |
| 532 gfx::Rect src_subrect = requested_src_subrect; |
| 533 src_subrect.Intersect(gfx::Rect(back_buffer_size)); |
| 534 |
| 535 base::win::ScopedComPtr<IDirect3DTexture9> resized_as_texture; |
| 536 base::win::ScopedComPtr<IDirect3DSurface9> resized; |
| 537 { |
| 538 TRACE_EVENT0("gpu", "CreateTemporaryRenderTargetTexture"); |
| 539 if (!d3d_utils::CreateTemporaryRenderTargetTexture( |
| 540 present_thread_->device(), |
| 541 dst_size, |
| 542 resized_as_texture.Receive(), |
| 543 resized.Receive())) { |
| 544 return false; |
| 545 } |
| 546 } |
| 547 |
| 548 // Shrink the source to fit entirely in the destination while preserving |
| 549 // aspect ratio. Fill in any margin with black. |
| 550 // TODO(nick): It would be more efficient all around to implement |
| 551 // letterboxing as a memset() on the dst. |
| 552 gfx::Rect letterbox = ComputeLetterboxRegion(gfx::Rect(dst_size), |
| 553 src_subrect.size()); |
| 554 if (letterbox != gfx::Rect(dst_size)) { |
| 555 TRACE_EVENT0("gpu", "Letterbox"); |
| 556 present_thread_->device()->ColorFill(resized, NULL, 0xFF000000); |
| 557 } |
| 558 |
| 559 { |
| 560 TRACE_EVENT0("gpu", "ResizeBilinear"); |
| 561 if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, resized, letterbox)) |
| 562 return false; |
| 563 } |
| 564 |
| 565 base::win::ScopedComPtr<IDirect3DSurface9> y, u, v; |
| 566 { |
| 567 TRACE_EVENT0("gpu", "TransformRGBToYV12"); |
| 568 if (!gpu_ops->TransformRGBToYV12(resized_as_texture, |
| 569 dst_size, |
| 570 y.Receive(), u.Receive(), v.Receive())) { |
| 571 return false; |
| 572 } |
| 573 } |
| 574 |
| 575 if (!LockAndCopyPlane(y, frame, media::VideoFrame::kYPlane)) |
| 576 return false; |
| 577 if (!LockAndCopyPlane(u, frame, media::VideoFrame::kUPlane)) |
| 578 return false; |
| 579 if (!LockAndCopyPlane(v, frame, media::VideoFrame::kVPlane)) |
| 580 return false; |
| 581 return true; |
| 582 } |
| 583 |
| 433 void AcceleratedPresenter::Suspend() { | 584 void AcceleratedPresenter::Suspend() { |
| 434 present_thread_->message_loop()->PostTask( | 585 present_thread_->message_loop()->PostTask( |
| 435 FROM_HERE, | 586 FROM_HERE, |
| 436 base::Bind(&AcceleratedPresenter::DoSuspend, | 587 base::Bind(&AcceleratedPresenter::DoSuspend, |
| 437 this)); | 588 this)); |
| 438 } | 589 } |
| 439 | 590 |
| 440 void AcceleratedPresenter::WasHidden() { | 591 void AcceleratedPresenter::WasHidden() { |
| 441 base::AutoLock locked(lock_); | 592 base::AutoLock locked(lock_); |
| 442 hidden_ = true; | 593 hidden_ = true; |
| (...skipping 409 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 852 presenter_->Present(dc); | 1003 presenter_->Present(dc); |
| 853 } | 1004 } |
| 854 | 1005 |
| 855 void AcceleratedSurface::AsyncCopyTo( | 1006 void AcceleratedSurface::AsyncCopyTo( |
| 856 const gfx::Rect& src_subrect, | 1007 const gfx::Rect& src_subrect, |
| 857 const gfx::Size& dst_size, | 1008 const gfx::Size& dst_size, |
| 858 const base::Callback<void(bool, const SkBitmap&)>& callback) { | 1009 const base::Callback<void(bool, const SkBitmap&)>& callback) { |
| 859 presenter_->AsyncCopyTo(src_subrect, dst_size, callback); | 1010 presenter_->AsyncCopyTo(src_subrect, dst_size, callback); |
| 860 } | 1011 } |
| 861 | 1012 |
| 1013 void AcceleratedSurface::AsyncCopyToVideoFrame( |
| 1014 const gfx::Rect& src_subrect, |
| 1015 const scoped_refptr<media::VideoFrame>& target, |
| 1016 const base::Callback<void(bool)>& callback) { |
| 1017 presenter_->AsyncCopyToVideoFrame(src_subrect, target, callback); |
| 1018 } |
| 1019 |
| 862 void AcceleratedSurface::Suspend() { | 1020 void AcceleratedSurface::Suspend() { |
| 863 presenter_->Suspend(); | 1021 presenter_->Suspend(); |
| 864 } | 1022 } |
| 865 | 1023 |
| 866 void AcceleratedSurface::WasHidden() { | 1024 void AcceleratedSurface::WasHidden() { |
| 867 presenter_->WasHidden(); | 1025 presenter_->WasHidden(); |
| 868 } | 1026 } |
| 869 | 1027 |
| 870 void AcceleratedSurface::SetIsSessionLocked(bool locked) { | 1028 void AcceleratedSurface::SetIsSessionLocked(bool locked) { |
| 871 presenter_->SetIsSessionLocked(locked); | 1029 presenter_->SetIsSessionLocked(locked); |
| 872 } | 1030 } |
| OLD | NEW |