diff options
author | nick@chromium.org <nick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-11 23:06:27 +0000 |
---|---|---|
committer | nick@chromium.org <nick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-11 23:06:27 +0000 |
commit | c02ccccaf7ddfbd5f84dd9f8f057918561660f80 (patch) | |
tree | b25a87a2fdca3c999bf55953bace656202604dcd /ui/surface/accelerated_surface_win.cc | |
parent | a6b73c65a2a8838206b804a65bbbc654c662e934 (diff) | |
download | chromium_src-c02ccccaf7ddfbd5f84dd9f8f057918561660f80.zip chromium_src-c02ccccaf7ddfbd5f84dd9f8f057918561660f80.tar.gz chromium_src-c02ccccaf7ddfbd5f84dd9f8f057918561660f80.tar.bz2 |
Tab Capture: Backing store readbacks to YV12 VideoFrames.
Goal here is performance; in tab capture there are
significant benefits to doing the readback as YV12:
it's less data to copy back, and it's cheap to do
on the GPU.
VideoFrame is used because it's the most capable YV12
container.
[render_widget_host.h]
Add a new flavor of CopyFromBackingStore, called
CopyFromBackingStoreToVideoFrame. The semantics are
slightly different from the RGBA copy in ways that are
video-appropriate: aspect ratio is preserved via
letterboxing, meaning the output is returned at the target's
allocated size, no matter what (whereas CopyFromBackingStore
returns whatever it wants, treating |dst_size| as a hint).
Callers may only call CopyFromBackingStoreToVideoFrame
after checking the result of CanCopyToVideoFrame().
Support is only on Windows now, and only while accelerated
compositing is active. But the interface defined
here should make it possible to implement VideoFrame
readbacks on other platforms without having to touch
the callers.
[video_capture_controller.h]
Amend the interface to allow a VideoFrame to
be passed in, as an alternative to void*.
The buffer-based interface was inadequate for
our needs since stride was not handled. Using
VideoFrame allows strides to be accomodated,
and paves the way for the interface to
pre-reserve the VideoFrames.
[web_contents_video_capture_device.cc]
Start using CopyFromBackingStoreToVideoFrame. Handling
both copy flavors requires a bifurcation of much
of the processing pipeline. When dealing with a VideoFrame,
the Render stage can is bypassed completely.
[accelerated_surface_win.h]
Implementation of VideoFrame YV12 readback, using
AcceleratedSurfaceTransformer's d3d-accelerated routines.
BUG=161537
Review URL: https://chromiumcodereview.appspot.com/12090109
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@181785 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/surface/accelerated_surface_win.cc')
-rw-r--r-- | ui/surface/accelerated_surface_win.cc | 182 |
1 files changed, 170 insertions, 12 deletions
diff --git a/ui/surface/accelerated_surface_win.cc b/ui/surface/accelerated_surface_win.cc index 8523493..a3ecb03 100644 --- a/ui/surface/accelerated_surface_win.cc +++ b/ui/surface/accelerated_surface_win.cc @@ -21,8 +21,9 @@ #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "base/threading/thread_restrictions.h" -#include "base/time.h" #include "base/win/wrapped_window_proc.h" +#include "media/base/video_frame.h" +#include "media/base/video_util.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/win/dpi.h" #include "ui/base/win/hwnd_util.h" @@ -54,6 +55,49 @@ bool DoAllShowPresentWithGDI() { switches::kDoAllShowPresentWithGDI); } +// Lock a D3D surface, and invoke a VideoFrame copier on the result. +bool LockAndCopyPlane(IDirect3DSurface9* src_surface, + media::VideoFrame* dst_frame, + size_t plane_id) { + gfx::Size src_size = d3d_utils::GetSize(src_surface); + + D3DLOCKED_RECT locked_rect; + { + TRACE_EVENT0("gpu", "LockRect"); + HRESULT hr = src_surface->LockRect(&locked_rect, NULL, + D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK); + if (FAILED(hr)) + return false; + } + + { + TRACE_EVENT0("gpu", "memcpy"); + uint8* src = reinterpret_cast<uint8*>(locked_rect.pBits); + int src_stride = locked_rect.Pitch; + media::CopyPlane(plane_id, src, src_stride, src_size.height(), dst_frame); + } + src_surface->UnlockRect(); + return true; +} + +// Return the largest centered rectangle with the same aspect ratio of |content| +// that fits entirely inside of |bounds|. +gfx::Rect ComputeLetterboxRegion( + const gfx::Rect& bounds, + const gfx::Size& content) { + int64 x = static_cast<int64>(content.width()) * bounds.height(); + int64 y = static_cast<int64>(bounds.width()) * content.height(); + + gfx::Size letterbox(bounds.width(), bounds.height()); + if (y < x) + letterbox.set_height(static_cast<int>(y / content.width())); + else + letterbox.set_width(static_cast<int>(x / content.height())); + gfx::Rect result = bounds; + result.ClampToCenteredSize(letterbox); + return result; +} + } // namespace // A PresentThread is a thread that is dedicated to presenting surfaces to a @@ -318,24 +362,45 @@ void AcceleratedPresenter::AsyncCopyTo( callback)); } +void AcceleratedPresenter::AsyncCopyToVideoFrame( + const gfx::Rect& requested_src_subrect, + const scoped_refptr<media::VideoFrame>& target, + const base::Callback<void(bool)>& callback) { + present_thread_->message_loop()->PostTask( + FROM_HERE, + base::Bind(&AcceleratedPresenter::DoCopyToVideoFrameAndAcknowledge, + this, + requested_src_subrect, + target, + base::MessageLoopProxy::current(), + callback)); +} + void AcceleratedPresenter::DoCopyToAndAcknowledge( const gfx::Rect& src_subrect, const gfx::Size& dst_size, scoped_refptr<base::SingleThreadTaskRunner> callback_runner, const base::Callback<void(bool, const SkBitmap&)>& callback) { - SkBitmap target; - bool result = DoCopyTo(src_subrect, dst_size, &target); + bool result = DoCopyToARGB(src_subrect, dst_size, &target); if (!result) target.reset(); - callback_runner->PostTask( - FROM_HERE, - base::Bind(callback, result, target)); + callback_runner->PostTask(FROM_HERE, base::Bind(callback, result, target)); } -bool AcceleratedPresenter::DoCopyTo(const gfx::Rect& requested_src_subrect, - const gfx::Size& dst_size, - SkBitmap* bitmap) { +void AcceleratedPresenter::DoCopyToVideoFrameAndAcknowledge( + const gfx::Rect& src_subrect, + const scoped_refptr<media::VideoFrame>& target, + const scoped_refptr<base::SingleThreadTaskRunner>& callback_runner, + const base::Callback<void(bool)>& callback) { + + bool result = DoCopyToYUV(src_subrect, target); + callback_runner->PostTask(FROM_HERE, base::Bind(callback, result)); +} + +bool AcceleratedPresenter::DoCopyToARGB(const gfx::Rect& requested_src_subrect, + const gfx::Size& dst_size, + SkBitmap* bitmap) { TRACE_EVENT2( "gpu", "CopyTo", "width", dst_size.width(), @@ -343,8 +408,6 @@ bool AcceleratedPresenter::DoCopyTo(const gfx::Rect& requested_src_subrect, base::AutoLock locked(lock_); - TRACE_EVENT0("gpu", "CopyTo_locked"); - if (!swap_chain_) return false; @@ -389,7 +452,8 @@ bool AcceleratedPresenter::DoCopyTo(const gfx::Rect& requested_src_subrect, { // Let the surface transformer start the resize into |final_surface|. TRACE_EVENT0("gpu", "ResizeBilinear"); - if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, final_surface)) { + if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, + final_surface, gfx::Rect(dst_size))) { LOG(ERROR) << "Failed to resize bilinear"; return false; } @@ -430,6 +494,93 @@ bool AcceleratedPresenter::DoCopyTo(const gfx::Rect& requested_src_subrect, return true; } +bool AcceleratedPresenter::DoCopyToYUV( + const gfx::Rect& requested_src_subrect, + const scoped_refptr<media::VideoFrame>& frame) { + gfx::Size dst_size = frame->coded_size(); + TRACE_EVENT2( + "gpu", "CopyToYUV", + "width", dst_size.width(), + "height", dst_size.height()); + + base::AutoLock locked(lock_); + + if (!swap_chain_) + return false; + + AcceleratedSurfaceTransformer* gpu_ops = + present_thread_->surface_transformer(); + + base::win::ScopedComPtr<IDirect3DSurface9> back_buffer; + HRESULT hr = swap_chain_->GetBackBuffer(0, + D3DBACKBUFFER_TYPE_MONO, + back_buffer.Receive()); + if (FAILED(hr)) + return false; + + D3DSURFACE_DESC desc; + hr = back_buffer->GetDesc(&desc); + if (FAILED(hr)) + return false; + + const gfx::Size back_buffer_size(desc.Width, desc.Height); + if (back_buffer_size.IsEmpty()) + return false; + + // With window resizing, it's possible that the back buffer is smaller than + // the requested src subset. Clip to the actual back buffer. + gfx::Rect src_subrect = requested_src_subrect; + src_subrect.Intersect(gfx::Rect(back_buffer_size)); + + base::win::ScopedComPtr<IDirect3DTexture9> resized_as_texture; + base::win::ScopedComPtr<IDirect3DSurface9> resized; + { + TRACE_EVENT0("gpu", "CreateTemporaryRenderTargetTexture"); + if (!d3d_utils::CreateTemporaryRenderTargetTexture( + present_thread_->device(), + dst_size, + resized_as_texture.Receive(), + resized.Receive())) { + return false; + } + } + + // Shrink the source to fit entirely in the destination while preserving + // aspect ratio. Fill in any margin with black. + // TODO(nick): It would be more efficient all around to implement + // letterboxing as a memset() on the dst. + gfx::Rect letterbox = ComputeLetterboxRegion(gfx::Rect(dst_size), + src_subrect.size()); + if (letterbox != gfx::Rect(dst_size)) { + TRACE_EVENT0("gpu", "Letterbox"); + present_thread_->device()->ColorFill(resized, NULL, 0xFF000000); + } + + { + TRACE_EVENT0("gpu", "ResizeBilinear"); + if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, resized, letterbox)) + return false; + } + + base::win::ScopedComPtr<IDirect3DSurface9> y, u, v; + { + TRACE_EVENT0("gpu", "TransformRGBToYV12"); + if (!gpu_ops->TransformRGBToYV12(resized_as_texture, + dst_size, + y.Receive(), u.Receive(), v.Receive())) { + return false; + } + } + + if (!LockAndCopyPlane(y, frame, media::VideoFrame::kYPlane)) + return false; + if (!LockAndCopyPlane(u, frame, media::VideoFrame::kUPlane)) + return false; + if (!LockAndCopyPlane(v, frame, media::VideoFrame::kVPlane)) + return false; + return true; +} + void AcceleratedPresenter::Suspend() { present_thread_->message_loop()->PostTask( FROM_HERE, @@ -859,6 +1010,13 @@ void AcceleratedSurface::AsyncCopyTo( presenter_->AsyncCopyTo(src_subrect, dst_size, callback); } +void AcceleratedSurface::AsyncCopyToVideoFrame( + const gfx::Rect& src_subrect, + const scoped_refptr<media::VideoFrame>& target, + const base::Callback<void(bool)>& callback) { + presenter_->AsyncCopyToVideoFrame(src_subrect, target, callback); +} + void AcceleratedSurface::Suspend() { presenter_->Suspend(); } |