summaryrefslogtreecommitdiffstats
path: root/ui/surface
diff options
context:
space:
mode:
authornick@chromium.org <nick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-11 23:06:27 +0000
committernick@chromium.org <nick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-11 23:06:27 +0000
commitc02ccccaf7ddfbd5f84dd9f8f057918561660f80 (patch)
treeb25a87a2fdca3c999bf55953bace656202604dcd /ui/surface
parenta6b73c65a2a8838206b804a65bbbc654c662e934 (diff)
downloadchromium_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')
-rw-r--r--ui/surface/accelerated_surface_transformer_win.cc27
-rw-r--r--ui/surface/accelerated_surface_transformer_win.h3
-rw-r--r--ui/surface/accelerated_surface_transformer_win_unittest.cc3
-rw-r--r--ui/surface/accelerated_surface_win.cc182
-rw-r--r--ui/surface/accelerated_surface_win.h36
-rw-r--r--ui/surface/surface.gyp1
6 files changed, 219 insertions, 33 deletions
diff --git a/ui/surface/accelerated_surface_transformer_win.cc b/ui/surface/accelerated_surface_transformer_win.cc
index 694f169..30fd1af 100644
--- a/ui/surface/accelerated_surface_transformer_win.cc
+++ b/ui/surface/accelerated_surface_transformer_win.cc
@@ -69,8 +69,7 @@ class ScopedRenderTargetRestorer {
// by repeating downsampling of the image of |src_subrect| by a factor no more
// than 2.
int GetResampleCount(const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- const gfx::Size& back_buffer_size) {
+ const gfx::Size& dst_size) {
// At least one copy is required, since the back buffer itself is not
// lockable.
int min_resample_count = 1;
@@ -319,17 +318,17 @@ void AcceleratedSurfaceTransformer::DrawScreenAlignedQuad(
bool AcceleratedSurfaceTransformer::ResizeBilinear(
IDirect3DSurface9* src_surface,
const gfx::Rect& src_subrect,
- IDirect3DSurface9* dst_surface) {
- gfx::Size src_size = d3d_utils::GetSize(src_surface);
- gfx::Size dst_size = d3d_utils::GetSize(dst_surface);
+ IDirect3DSurface9* dst_surface,
+ const gfx::Rect& dst_rect) {
+ gfx::Size src_size = src_subrect.size();
+ gfx::Size dst_size = dst_rect.size();
if (src_size.IsEmpty() || dst_size.IsEmpty())
return false;
HRESULT hr = S_OK;
// Set up intermediate buffers needed for downsampling.
- const int resample_count =
- GetResampleCount(src_subrect, dst_size, src_size);
+ const int resample_count = GetResampleCount(src_subrect, dst_size);
base::win::ScopedComPtr<IDirect3DSurface9> temp_buffer[2];
const gfx::Size half_size =
GetHalfSizeNoLessThan(src_subrect.size(), dst_size);
@@ -360,10 +359,16 @@ bool AcceleratedSurfaceTransformer::ResizeBilinear(
TRACE_EVENT0("gpu", "StretchRect");
IDirect3DSurface9* read_buffer =
(i == 0) ? src_surface : temp_buffer[read_buffer_index];
- IDirect3DSurface9* write_buffer =
- (i == resample_count - 1) ? dst_surface :
- temp_buffer[write_buffer_index];
- RECT write_rect = gfx::Rect(write_size).ToRECT();
+ IDirect3DSurface9* write_buffer;
+ RECT write_rect;
+ if (i == resample_count - 1) {
+ write_buffer = dst_surface;
+ write_rect = dst_rect.ToRECT();
+ } else {
+ write_buffer = temp_buffer[write_buffer_index];
+ write_rect = gfx::Rect(write_size).ToRECT();
+ }
+
hr = device()->StretchRect(read_buffer,
&read_rect,
write_buffer,
diff --git a/ui/surface/accelerated_surface_transformer_win.h b/ui/surface/accelerated_surface_transformer_win.h
index 97793c1..10bee08 100644
--- a/ui/surface/accelerated_surface_transformer_win.h
+++ b/ui/surface/accelerated_surface_transformer_win.h
@@ -61,7 +61,8 @@ class SURFACE_EXPORT AcceleratedSurfaceTransformer {
bool ResizeBilinear(
IDirect3DSurface9* src_surface,
const gfx::Rect& src_subrect,
- IDirect3DSurface9* dst_surface);
+ IDirect3DSurface9* dst_surface,
+ const gfx::Rect& dst_subrect);
// Color format conversion from RGB to planar YV12 (also known as YUV420).
//
diff --git a/ui/surface/accelerated_surface_transformer_win_unittest.cc b/ui/surface/accelerated_surface_transformer_win_unittest.cc
index b8b1eb6..e911281 100644
--- a/ui/surface/accelerated_surface_transformer_win_unittest.cc
+++ b/ui/surface/accelerated_surface_transformer_win_unittest.cc
@@ -355,7 +355,8 @@ bool AssertSameColor(uint8 color_a, uint8 color_b) {
FillSymmetricRandomCheckerboard(src, src_size, checkerboard_size);
- ASSERT_TRUE(gpu_ops->ResizeBilinear(src, gfx::Rect(src_size), dst));
+ ASSERT_TRUE(gpu_ops->ResizeBilinear(src, gfx::Rect(src_size), dst,
+ gfx::Rect(dst_size)));
AssertSymmetry(dst, dst_size);
}
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();
}
diff --git a/ui/surface/accelerated_surface_win.h b/ui/surface/accelerated_surface_win.h
index 1af2482..eebaf1a 100644
--- a/ui/surface/accelerated_surface_win.h
+++ b/ui/surface/accelerated_surface_win.h
@@ -12,6 +12,7 @@
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
+#include "base/time.h"
#include "base/win/scoped_comptr.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/size.h"
@@ -23,6 +24,10 @@ namespace gfx {
class Rect;
}
+namespace media {
+class VideoFrame;
+}
+
class SURFACE_EXPORT AcceleratedPresenter
: public base::RefCountedThreadSafe<AcceleratedPresenter> {
public:
@@ -65,6 +70,10 @@ class SURFACE_EXPORT AcceleratedPresenter
void AsyncCopyTo(const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
const base::Callback<void(bool, const SkBitmap&)>& callback);
+ void AsyncCopyToVideoFrame(
+ const gfx::Rect& src_subrect,
+ const scoped_refptr<media::VideoFrame>& target,
+ const base::Callback<void(bool)>& callback);
void Invalidate();
#if defined(USE_AURA)
@@ -92,9 +101,16 @@ class SURFACE_EXPORT AcceleratedPresenter
const gfx::Size& dst_size,
scoped_refptr<base::SingleThreadTaskRunner> callback_runner,
const base::Callback<void(bool, const SkBitmap&)>& callback);
- bool DoCopyTo(const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- SkBitmap* bitmap);
+ void 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 DoCopyToYUV(const gfx::Rect& src_subrect,
+ const scoped_refptr<media::VideoFrame>& frame);
+ bool DoCopyToARGB(const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ SkBitmap* bitmap);
void PresentWithGDI(HDC dc);
gfx::Size GetWindowSize();
@@ -165,15 +181,19 @@ class SURFACE_EXPORT AcceleratedSurface {
// Synchronously present a frame with no acknowledgement.
void Present(HDC dc);
- // Copies the surface data to |buf|. The copied region is specified with
- // |src_subrect| and the image data is transformed so that it fits in
- // |dst_size|.
- // Caller must ensure that |buf| is allocated with the size no less than
- // |4 * dst_size.width() * dst_size.height()| bytes.
+ // Transfer the contents of the surface to an SkBitmap, and invoke a callback
+ // with the result.
void AsyncCopyTo(const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
const base::Callback<void(bool, const SkBitmap&)>& callback);
+ // Transfer the contents of the surface to an already-allocated YV12
+ // VideoFrame, and invoke a callback to indicate success or failure.
+ void AsyncCopyToVideoFrame(
+ const gfx::Rect& src_subrect,
+ const scoped_refptr<media::VideoFrame>& target,
+ const base::Callback<void(bool)>& callback);
+
// Temporarily release resources until a new surface is asynchronously
// presented. Present will not be able to represent the last surface after
// calling this and will return false.
diff --git a/ui/surface/surface.gyp b/ui/surface/surface.gyp
index 7ba677f..903ea49 100644
--- a/ui/surface/surface.gyp
+++ b/ui/surface/surface.gyp
@@ -59,6 +59,7 @@
'dependencies': [
'<(DEPTH)/base/base.gyp:base',
'<(DEPTH)/base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '<(DEPTH)/media/media.gyp:media',
'<(DEPTH)/skia/skia.gyp:skia',
'<(DEPTH)/ui/gl/gl.gyp:gl',
'<(DEPTH)/ui/ui.gyp:ui',