Chromium Code Reviews| 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 copier function on the result. | |
| 59 bool LockAndCopyPlane(IDirect3DSurface9* src_surface, | |
| 60 media::VideoFrame* dst_frame, | |
| 61 void (*copier)(const uint8* source, | |
|
scherkus (not reviewing)
2013/02/05 22:40:44
nit: typedef + docs?
typedef void(*CopyPlaneFunc)(
ncarter (slow)
2013/02/06 23:54:44
I got rid of the function pointer altogether by si
| |
| 62 int stride, | |
| 63 int rows, | |
| 64 media::VideoFrame* frame)) { | |
| 65 gfx::Size src_size = d3d_utils::GetSize(src_surface); | |
| 66 | |
| 67 D3DLOCKED_RECT locked_rect; | |
| 68 { | |
| 69 TRACE_EVENT0("gpu", "LockRect"); | |
| 70 HRESULT hr = src_surface->LockRect(&locked_rect, NULL, | |
| 71 D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK); | |
| 72 if (FAILED(hr)) | |
| 73 return false; | |
| 74 } | |
| 75 | |
| 76 { | |
| 77 TRACE_EVENT0("gpu", "memcpy"); | |
| 78 uint8* src = reinterpret_cast<uint8*>(locked_rect.pBits); | |
| 79 int src_stride = locked_rect.Pitch; | |
| 80 copier(src, src_stride, src_size.height(), dst_frame); | |
| 81 } | |
| 82 src_surface->UnlockRect(); | |
| 83 return true; | |
| 84 } | |
| 85 | |
| 86 // Return the largest centered rectangle with the same aspect ratio of |content| | |
| 87 // that fits entirely inside of |bounds|. | |
| 88 gfx::Rect ComputeLetterboxRegion( | |
| 89 const gfx::Rect& bounds, | |
| 90 const gfx::Size& content) { | |
| 91 int64 x = static_cast<int64>(content.width()) * bounds.height(); | |
| 92 int64 y = static_cast<int64>(bounds.width()) * content.height(); | |
| 93 | |
| 94 gfx::Size letterbox(bounds.width(), bounds.height()); | |
| 95 if (y < x) | |
| 96 letterbox.set_height(static_cast<int>(y / content.width())); | |
| 97 else | |
| 98 letterbox.set_width(static_cast<int>(x / content.height())); | |
| 99 gfx::Rect result = bounds; | |
| 100 result.ClampToCenteredSize(letterbox); | |
| 101 return result; | |
| 102 } | |
| 103 | |
| 57 } // namespace | 104 } // namespace |
| 58 | 105 |
| 59 // A PresentThread is a thread that is dedicated to presenting surfaces to a | 106 // 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. | 107 // window. It owns a Direct3D device and a Direct3D query for this purpose. |
| 61 class PresentThread : public base::Thread, | 108 class PresentThread : public base::Thread, |
| 62 public base::RefCountedThreadSafe<PresentThread> { | 109 public base::RefCountedThreadSafe<PresentThread> { |
| 63 public: | 110 public: |
| 64 explicit PresentThread(const char* name); | 111 explicit PresentThread(const char* name); |
| 65 | 112 |
| 66 IDirect3DDevice9Ex* device() { return device_.get(); } | 113 IDirect3DDevice9Ex* device() { return device_.get(); } |
| (...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 311 present_thread_->message_loop()->PostTask( | 358 present_thread_->message_loop()->PostTask( |
| 312 FROM_HERE, | 359 FROM_HERE, |
| 313 base::Bind(&AcceleratedPresenter::DoCopyToAndAcknowledge, | 360 base::Bind(&AcceleratedPresenter::DoCopyToAndAcknowledge, |
| 314 this, | 361 this, |
| 315 requested_src_subrect, | 362 requested_src_subrect, |
| 316 dst_size, | 363 dst_size, |
| 317 base::MessageLoopProxy::current(), | 364 base::MessageLoopProxy::current(), |
| 318 callback)); | 365 callback)); |
| 319 } | 366 } |
| 320 | 367 |
| 368 void AcceleratedPresenter::AsyncCopyToVideoFrame( | |
| 369 const gfx::Rect& requested_src_subrect, | |
| 370 const gfx::Size& dst_size, | |
| 371 const base::Callback<void(media::VideoFrame*)>& callback) { | |
| 372 present_thread_->message_loop()->PostTask( | |
| 373 FROM_HERE, | |
| 374 base::Bind(&AcceleratedPresenter::DoCopyToVideoFrameAndAcknowledge, | |
| 375 this, | |
| 376 requested_src_subrect, | |
| 377 dst_size, | |
| 378 base::MessageLoopProxy::current(), | |
| 379 callback)); | |
| 380 } | |
| 381 | |
| 321 void AcceleratedPresenter::DoCopyToAndAcknowledge( | 382 void AcceleratedPresenter::DoCopyToAndAcknowledge( |
| 322 const gfx::Rect& src_subrect, | 383 const gfx::Rect& src_subrect, |
| 323 const gfx::Size& dst_size, | 384 const gfx::Size& dst_size, |
| 324 scoped_refptr<base::SingleThreadTaskRunner> callback_runner, | 385 scoped_refptr<base::SingleThreadTaskRunner> callback_runner, |
| 325 const base::Callback<void(bool, const SkBitmap&)>& callback) { | 386 const base::Callback<void(bool, const SkBitmap&)>& callback) { |
| 326 | 387 |
| 327 SkBitmap target; | 388 SkBitmap target; |
| 328 bool result = DoCopyTo(src_subrect, dst_size, &target); | 389 bool result = DoCopyToARGB(src_subrect, dst_size, &target); |
| 329 if (!result) | 390 if (!result) |
| 330 target.reset(); | 391 target.reset(); |
| 331 callback_runner->PostTask( | 392 callback_runner->PostTask( |
| 332 FROM_HERE, | 393 FROM_HERE, |
| 333 base::Bind(callback, result, target)); | 394 base::Bind(callback, result, target)); |
| 334 } | 395 } |
| 335 | 396 |
| 336 bool AcceleratedPresenter::DoCopyTo(const gfx::Rect& requested_src_subrect, | 397 void AcceleratedPresenter::DoCopyToVideoFrameAndAcknowledge( |
| 337 const gfx::Size& dst_size, | 398 const gfx::Rect& src_subrect, |
| 338 SkBitmap* bitmap) { | 399 const gfx::Size& dst_size, |
| 400 scoped_refptr<base::SingleThreadTaskRunner> callback_runner, | |
| 401 const base::Callback<void(media::VideoFrame*)>& callback) { | |
| 402 | |
|
scherkus (not reviewing)
2013/02/05 22:40:44
nit: remove blank line
ncarter (slow)
2013/02/06 23:54:44
Done.
| |
| 403 scoped_refptr<media::VideoFrame> target( | |
| 404 media::VideoFrame::CreateFrame( | |
| 405 media::VideoFrame::YV12, | |
| 406 dst_size, | |
| 407 gfx::Rect(dst_size), | |
| 408 dst_size, | |
| 409 base::TimeDelta())); | |
| 410 bool result = DoCopyToYUV(src_subrect, dst_size, target); | |
| 411 if (!result) | |
| 412 target = media::VideoFrame::CreateEmptyFrame(); | |
|
scherkus (not reviewing)
2013/02/05 22:40:44
I've never been a huge fan of the whole EMPTY fram
ncarter (slow)
2013/02/06 23:54:44
I'm moving to a model where VideoFrame is allocate
| |
| 413 callback_runner->PostTask(FROM_HERE, base::Bind(callback, target)); | |
| 414 } | |
| 415 | |
| 416 bool AcceleratedPresenter::DoCopyToARGB(const gfx::Rect& requested_src_subrect, | |
| 417 const gfx::Size& dst_size, | |
| 418 SkBitmap* bitmap) { | |
| 339 TRACE_EVENT2( | 419 TRACE_EVENT2( |
| 340 "gpu", "CopyTo", | 420 "gpu", "CopyTo", |
| 341 "width", dst_size.width(), | 421 "width", dst_size.width(), |
| 342 "height", dst_size.height()); | 422 "height", dst_size.height()); |
| 343 | 423 |
| 344 base::AutoLock locked(lock_); | 424 base::AutoLock locked(lock_); |
| 345 | 425 |
| 346 TRACE_EVENT0("gpu", "CopyTo_locked"); | |
| 347 | |
| 348 if (!swap_chain_) | 426 if (!swap_chain_) |
| 349 return false; | 427 return false; |
| 350 | 428 |
| 351 AcceleratedSurfaceTransformer* gpu_ops = | 429 AcceleratedSurfaceTransformer* gpu_ops = |
| 352 present_thread_->surface_transformer(); | 430 present_thread_->surface_transformer(); |
| 353 | 431 |
| 354 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer; | 432 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer; |
| 355 HRESULT hr = swap_chain_->GetBackBuffer(0, | 433 HRESULT hr = swap_chain_->GetBackBuffer(0, |
| 356 D3DBACKBUFFER_TYPE_MONO, | 434 D3DBACKBUFFER_TYPE_MONO, |
| 357 back_buffer.Receive()); | 435 back_buffer.Receive()); |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 382 dst_size, | 460 dst_size, |
| 383 final_surface.Receive())) { | 461 final_surface.Receive())) { |
| 384 LOG(ERROR) << "Failed to create temporary lockable surface"; | 462 LOG(ERROR) << "Failed to create temporary lockable surface"; |
| 385 return false; | 463 return false; |
| 386 } | 464 } |
| 387 } | 465 } |
| 388 | 466 |
| 389 { | 467 { |
| 390 // Let the surface transformer start the resize into |final_surface|. | 468 // Let the surface transformer start the resize into |final_surface|. |
| 391 TRACE_EVENT0("gpu", "ResizeBilinear"); | 469 TRACE_EVENT0("gpu", "ResizeBilinear"); |
| 392 if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, final_surface)) { | 470 if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, |
| 471 final_surface, gfx::Rect(dst_size))) { | |
| 393 LOG(ERROR) << "Failed to resize bilinear"; | 472 LOG(ERROR) << "Failed to resize bilinear"; |
| 394 return false; | 473 return false; |
| 395 } | 474 } |
| 396 } | 475 } |
| 397 | 476 |
| 398 D3DLOCKED_RECT locked_rect; | 477 D3DLOCKED_RECT locked_rect; |
| 399 | 478 |
| 400 // Empirical evidence seems to suggest that LockRect and memcpy are faster | 479 // Empirical evidence seems to suggest that LockRect and memcpy are faster |
| 401 // than would be GetRenderTargetData to an offscreen surface wrapping *buf. | 480 // than would be GetRenderTargetData to an offscreen surface wrapping *buf. |
| 402 { | 481 { |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 423 | 502 |
| 424 memcpy(reinterpret_cast<int8*>(bitmap->getPixels()), | 503 memcpy(reinterpret_cast<int8*>(bitmap->getPixels()), |
| 425 reinterpret_cast<int8*>(locked_rect.pBits), | 504 reinterpret_cast<int8*>(locked_rect.pBits), |
| 426 locked_rect.Pitch * dst_size.height()); | 505 locked_rect.Pitch * dst_size.height()); |
| 427 } | 506 } |
| 428 final_surface->UnlockRect(); | 507 final_surface->UnlockRect(); |
| 429 | 508 |
| 430 return true; | 509 return true; |
| 431 } | 510 } |
| 432 | 511 |
| 512 bool AcceleratedPresenter::DoCopyToYUV(const gfx::Rect& requested_src_subrect, | |
| 513 const gfx::Size& dst_size, | |
| 514 media::VideoFrame* frame) { | |
| 515 TRACE_EVENT2( | |
| 516 "gpu", "CopyToYUV", | |
| 517 "width", dst_size.width(), | |
| 518 "height", dst_size.height()); | |
| 519 | |
| 520 base::AutoLock locked(lock_); | |
| 521 | |
| 522 if (!swap_chain_) | |
| 523 return false; | |
| 524 | |
| 525 AcceleratedSurfaceTransformer* gpu_ops = | |
| 526 present_thread_->surface_transformer(); | |
| 527 | |
| 528 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer; | |
| 529 HRESULT hr = swap_chain_->GetBackBuffer(0, | |
| 530 D3DBACKBUFFER_TYPE_MONO, | |
| 531 back_buffer.Receive()); | |
| 532 if (FAILED(hr)) | |
| 533 return false; | |
| 534 | |
| 535 D3DSURFACE_DESC desc; | |
| 536 hr = back_buffer->GetDesc(&desc); | |
| 537 if (FAILED(hr)) | |
| 538 return false; | |
| 539 | |
| 540 const gfx::Size back_buffer_size(desc.Width, desc.Height); | |
| 541 if (back_buffer_size.IsEmpty()) | |
| 542 return false; | |
| 543 | |
| 544 // With window resizing, it's possible that the back buffer is smaller than | |
| 545 // the requested src subset. Clip to the actual back buffer. | |
| 546 gfx::Rect src_subrect = requested_src_subrect; | |
| 547 src_subrect.Intersect(gfx::Rect(back_buffer_size)); | |
| 548 | |
| 549 base::win::ScopedComPtr<IDirect3DTexture9> resized_as_texture; | |
| 550 base::win::ScopedComPtr<IDirect3DSurface9> resized; | |
| 551 { | |
| 552 TRACE_EVENT0("gpu", "CreateTemporaryRenderTargetTexture"); | |
| 553 if (!d3d_utils::CreateTemporaryRenderTargetTexture( | |
| 554 present_thread_->device(), | |
| 555 dst_size, | |
| 556 resized_as_texture.Receive(), | |
| 557 resized.Receive())) { | |
| 558 return false; | |
| 559 } | |
| 560 } | |
| 561 | |
| 562 // Shrink the source to fit entirely in the destination while preserving | |
| 563 // aspect ratio. Fill in any margin with black. | |
| 564 // TODO(nick): It would be more efficient all around to implement | |
| 565 // letterboxing as a memset() on the dst. | |
| 566 gfx::Rect letterbox = ComputeLetterboxRegion(gfx::Rect(dst_size), | |
| 567 src_subrect.size()); | |
| 568 if (letterbox != gfx::Rect(dst_size)) { | |
| 569 TRACE_EVENT0("gpu", "Letterbox"); | |
| 570 present_thread_->device()->ColorFill(resized, NULL, 0xFF000000); | |
| 571 } | |
| 572 | |
| 573 { | |
| 574 TRACE_EVENT0("gpu", "ResizeBilinear"); | |
| 575 if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, resized, letterbox)) | |
| 576 return false; | |
| 577 } | |
| 578 | |
| 579 base::win::ScopedComPtr<IDirect3DSurface9> y, u, v; | |
| 580 { | |
| 581 TRACE_EVENT0("gpu", "TransformRGBToYV12"); | |
| 582 if (!gpu_ops->TransformRGBToYV12(resized_as_texture, | |
| 583 dst_size, | |
| 584 y.Receive(), u.Receive(), v.Receive())) { | |
| 585 return false; | |
| 586 } | |
| 587 } | |
| 588 | |
| 589 if (!LockAndCopyPlane(y, frame, &media::CopyYPlane)) | |
| 590 return false; | |
| 591 if (!LockAndCopyPlane(u, frame, &media::CopyUPlane)) | |
| 592 return false; | |
| 593 if (!LockAndCopyPlane(v, frame, &media::CopyVPlane)) | |
| 594 return false; | |
| 595 return true; | |
| 596 } | |
| 597 | |
| 433 void AcceleratedPresenter::Suspend() { | 598 void AcceleratedPresenter::Suspend() { |
| 434 present_thread_->message_loop()->PostTask( | 599 present_thread_->message_loop()->PostTask( |
| 435 FROM_HERE, | 600 FROM_HERE, |
| 436 base::Bind(&AcceleratedPresenter::DoSuspend, | 601 base::Bind(&AcceleratedPresenter::DoSuspend, |
| 437 this)); | 602 this)); |
| 438 } | 603 } |
| 439 | 604 |
| 440 void AcceleratedPresenter::WasHidden() { | 605 void AcceleratedPresenter::WasHidden() { |
| 441 base::AutoLock locked(lock_); | 606 base::AutoLock locked(lock_); |
| 442 hidden_ = true; | 607 hidden_ = true; |
| (...skipping 409 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 852 presenter_->Present(dc); | 1017 presenter_->Present(dc); |
| 853 } | 1018 } |
| 854 | 1019 |
| 855 void AcceleratedSurface::AsyncCopyTo( | 1020 void AcceleratedSurface::AsyncCopyTo( |
| 856 const gfx::Rect& src_subrect, | 1021 const gfx::Rect& src_subrect, |
| 857 const gfx::Size& dst_size, | 1022 const gfx::Size& dst_size, |
| 858 const base::Callback<void(bool, const SkBitmap&)>& callback) { | 1023 const base::Callback<void(bool, const SkBitmap&)>& callback) { |
| 859 presenter_->AsyncCopyTo(src_subrect, dst_size, callback); | 1024 presenter_->AsyncCopyTo(src_subrect, dst_size, callback); |
| 860 } | 1025 } |
| 861 | 1026 |
| 1027 void AcceleratedSurface::AsyncCopyToVideoFrame( | |
| 1028 const gfx::Rect& src_subrect, | |
| 1029 const gfx::Size& dst_size, | |
| 1030 const base::Callback<void(media::VideoFrame*)>& callback) { | |
| 1031 presenter_->AsyncCopyToVideoFrame(src_subrect, dst_size, callback); | |
| 1032 } | |
| 1033 | |
| 862 void AcceleratedSurface::Suspend() { | 1034 void AcceleratedSurface::Suspend() { |
| 863 presenter_->Suspend(); | 1035 presenter_->Suspend(); |
| 864 } | 1036 } |
| 865 | 1037 |
| 866 void AcceleratedSurface::WasHidden() { | 1038 void AcceleratedSurface::WasHidden() { |
| 867 presenter_->WasHidden(); | 1039 presenter_->WasHidden(); |
| 868 } | 1040 } |
| 869 | 1041 |
| 870 void AcceleratedSurface::SetIsSessionLocked(bool locked) { | 1042 void AcceleratedSurface::SetIsSessionLocked(bool locked) { |
| 871 presenter_->SetIsSessionLocked(locked); | 1043 presenter_->SetIsSessionLocked(locked); |
| 872 } | 1044 } |
| OLD | NEW |