diff options
author | hubbe@chromium.org <hubbe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-31 03:37:46 +0000 |
---|---|---|
committer | hubbe@chromium.org <hubbe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-31 03:37:46 +0000 |
commit | 9d2839eead7e39387a950b87d626296f9a1f2e51 (patch) | |
tree | 8dcb3a3069fdbd848cf8a9b28e2058b018ce554c | |
parent | 49d69ba3a43aa6da00f672d6178dad11d197e94a (diff) | |
download | chromium_src-9d2839eead7e39387a950b87d626296f9a1f2e51.zip chromium_src-9d2839eead7e39387a950b87d626296f9a1f2e51.tar.gz chromium_src-9d2839eead7e39387a950b87d626296f9a1f2e51.tar.bz2 |
GPU RGBA -> Planar YUV conversion + uses new scaler pipeline
Review URL: https://chromiumcodereview.appspot.com/15944003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@203322 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/chromeos/login/chrome_restart_request.cc | 2 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host_view_aura.cc | 76 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host_view_aura.h | 5 | ||||
-rw-r--r-- | content/common/gpu/client/gl_helper.cc | 387 | ||||
-rw-r--r-- | content/common/gpu/client/gl_helper.h | 41 | ||||
-rw-r--r-- | content/common/gpu/client/gl_helper_scaling.cc | 89 | ||||
-rw-r--r-- | content/common/gpu/client/gl_helper_scaling.h | 8 | ||||
-rw-r--r-- | content/common/gpu/client/gl_helper_unittests.cc | 301 | ||||
-rw-r--r-- | content/public/common/content_switches.cc | 7 | ||||
-rw-r--r-- | content/public/common/content_switches.h | 2 |
10 files changed, 827 insertions, 91 deletions
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc index 7e52fee..57e05b2 100644 --- a/chrome/browser/chromeos/login/chrome_restart_request.cc +++ b/chrome/browser/chromeos/login/chrome_restart_request.cc @@ -108,6 +108,8 @@ std::string DeriveCommandLine(const GURL& start_url, ::switches::kPpapiInProcess, ::switches::kRendererStartupDialog, ::switches::kEnableShareGroupAsyncTextureUpload, + ::switches::kTabCaptureUpscaleQuality, + ::switches::kTabCaptureDownscaleQuality, #if defined(USE_XI2_MT) ::switches::kTouchCalibration, #endif diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index fe288f5..1195f9c 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -345,28 +345,6 @@ void AcknowledgeBufferForGpu( route_id, gpu_host_id, ack); } -void CopySnapshotToVideoFrame(const scoped_refptr<media::VideoFrame>& target, - const gfx::Rect& region_in_frame, - const base::Callback<void(bool)>& callback, - bool succeed, - const SkBitmap& bitmap) { - if (!succeed) { - callback.Run(false); - return; - } - - DCHECK(region_in_frame.size() == gfx::Size(bitmap.width(), bitmap.height())); - { - SkAutoLockPixels lock(bitmap); - media::CopyRGBToVideoFrame( - reinterpret_cast<const uint8*>(bitmap.getPixels()), - bitmap.rowBytes(), - region_in_frame, - target.get()); - } - callback.Run(true); -} - } // namespace // We need to watch for mouse events outside a Web Popup or its parent @@ -1255,15 +1233,55 @@ void RenderWidgetHostViewAura::CopyFromCompositingSurfaceToVideoFrame( region_in_frame.y() & ~1, region_in_frame.width() & ~1, region_in_frame.height() & ~1); + ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); + GLHelper* gl_helper = factory->GetGLHelper(); + if (!gl_helper) { + return; + } + // Convert |src_subrect| from the views coordinate (upper-left origin) into + // the OpenGL coordinate (lower-left origin). + gfx::Rect src_subrect_in_gl = src_subrect; + src_subrect_in_gl.set_y(GetViewBounds().height() - src_subrect.bottom()); + gfx::Rect src_subrect_in_pixel = + ConvertRectToPixel(current_surface_->device_scale_factor(), + src_subrect_in_gl); - scoped_callback_runner.Release(); + if (!yuv_readback_pipeline_ || + yuv_readback_pipeline_->scaler()->SrcSize() != current_surface_->size() || + yuv_readback_pipeline_->scaler()->SrcSubrect() != src_subrect_in_pixel || + yuv_readback_pipeline_->scaler()->DstSize() != region_in_frame.size()) { + GLHelper::ScalerQuality quality = GLHelper::SCALER_QUALITY_FAST; + std::string quality_switch = switches::kTabCaptureDownscaleQuality; + // If we're scaling up, we can use the "best" quality. + if (current_surface_->size().width() < region_in_frame.size().width() && + current_surface_->size().height() < region_in_frame.size().height()) { + quality_switch = switches::kTabCaptureUpscaleQuality; + } - CopyFromCompositingSurfaceHelper(src_subrect, - region_in_frame.size(), - base::Bind(CopySnapshotToVideoFrame, - target, - region_in_frame, - callback)); + std::string switch_value = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII(quality_switch); + if (switch_value == "fast") { + quality = GLHelper::SCALER_QUALITY_FAST; + } else if (switch_value == "good") { + quality = GLHelper::SCALER_QUALITY_GOOD; + } else if (switch_value == "best") { + quality = GLHelper::SCALER_QUALITY_BEST; + } + + yuv_readback_pipeline_.reset( + gl_helper->CreateReadbackPipelineYUV(quality, + current_surface_->size(), + src_subrect_in_pixel, + target->coded_size(), + region_in_frame, + true)); + } + + scoped_callback_runner.Release(); + yuv_readback_pipeline_->ReadbackYUV( + current_surface_->PrepareTexture(), + target, + callback); } bool RenderWidgetHostViewAura::CanCopyToVideoFrame() const { diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h index 091cbd1..3d376d1 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.h +++ b/content/browser/renderer_host/render_widget_host_view_aura.h @@ -19,6 +19,7 @@ #include "content/browser/renderer_host/image_transport_factory.h" #include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/common/content_export.h" +#include "content/common/gpu/client/gl_helper.h" #include "third_party/skia/include/core/SkRegion.h" #include "ui/aura/client/activation_change_observer.h" #include "ui/aura/client/activation_delegate.h" @@ -670,6 +671,10 @@ class RenderWidgetHostViewAura // Subscriber that listens to frame presentation events. scoped_ptr<RenderWidgetHostViewFrameSubscriber> frame_subscriber_; + // YUV readback pipeline. + scoped_ptr<content::ReadbackYUVInterface> + yuv_readback_pipeline_; + TouchEditingClient* touch_editing_client_; DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAura); diff --git a/content/common/gpu/client/gl_helper.cc b/content/common/gpu/client/gl_helper.cc index de9822e..79a2ca36 100644 --- a/content/common/gpu/client/gl_helper.cc +++ b/content/common/gpu/client/gl_helper.cc @@ -16,6 +16,8 @@ #include "base/time.h" #include "cc/resources/sync_point_helper.h" #include "content/common/gpu/client/gl_helper_scaling.h" +#include "media/base/video_frame.h" +#include "media/base/video_util.h" #include "third_party/WebKit/public/platform/WebCString.h" #include "third_party/skia/include/core/SkRegion.h" #include "ui/gfx/rect.h" @@ -25,6 +27,57 @@ using WebKit::WebGLId; using WebKit::WebGraphicsContext3D; +namespace { + +// Helper class for holding a scaler, a texture for the output of that +// scaler and an associated frame buffer. This is inteded to be used +// when the output of a scaler is t be sent to a readback. +class ScalerHolder { + public: + ScalerHolder(WebGraphicsContext3D* context, + content::GLHelper::ScalerInterface *scaler) + : scaler_(scaler), + texture_(context, context->createTexture()), + framebuffer_(context, context->createFramebuffer()) { + content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context, + texture_); + context->texImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + scaler_->DstSize().width(), + scaler_->DstSize().height(), + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + NULL); + content::ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder( + context, + framebuffer_); + context->framebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + texture_, + 0); + } + + void Scale(WebKit::WebGLId src_texture) { + scaler_->Scale(src_texture, texture_); + } + + content::GLHelper::ScalerInterface* scaler() const { return scaler_.get(); } + WebGLId texture() const { return texture_.id(); } + WebGLId framebuffer() const { return framebuffer_.id(); } + + public: + scoped_ptr<content::GLHelper::ScalerInterface> scaler_; + content::ScopedTexture texture_; + content::ScopedFramebuffer framebuffer_; + + DISALLOW_COPY_AND_ASSIGN(ScalerHolder); +}; + +} // namespace + namespace content { // Implements GLHelper::CropScaleReadbackAndCleanTexture and encapsulates @@ -56,12 +109,29 @@ class GLHelper::CopyTextureToImpl : const gfx::Rect& src_rect, unsigned char* out); + // Reads back bytes from the currently bound frame buffer. + // Note that dst_size is specified in bytes, not pixels. + void ReadbackAsync( + const gfx::Size& dst_size, + int32 bytes_per_row, // generally dst_size.width() * 4 + int32 row_stride_bytes, // generally dst_size.width() * 4 + unsigned char* out, + const base::Callback<void(bool)>& callback); + WebKit::WebGLId CopyAndScaleTexture(WebGLId texture, const gfx::Size& src_size, const gfx::Size& dst_size, bool vertically_flip_texture, GLHelper::ScalerQuality quality); + ReadbackYUVInterface* CreateReadbackPipelineYUV( + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + const gfx::Rect& dst_subrect, + bool flip_vertically); + private: // A single request to CropScaleReadbackAndCleanTexture. // The main thread can cancel the request, before it's handled by the helper @@ -71,25 +141,66 @@ class GLHelper::CopyTextureToImpl : // In either case, the callback must be called exactly once, and the texture // must be deleted by the main thread context. struct Request { - Request(WebGLId texture_, - const gfx::Size& size_, + Request(const gfx::Size& size_, + int32 bytes_per_row_, + int32 row_stride_bytes_, unsigned char* pixels_, const base::Callback<void(bool)>& callback_) : size(size_), - callback(callback_), - texture(texture_), + bytes_per_row(bytes_per_row_), + row_stride_bytes(row_stride_bytes_), pixels(pixels_), + callback(callback_), buffer(0) { } gfx::Size size; - base::Callback<void(bool)> callback; - - WebGLId texture; + int bytes_per_row; + int row_stride_bytes; unsigned char* pixels; + base::Callback<void(bool)> callback; GLuint buffer; }; + // A readback pipeline that also converts the data to YUV before + // reading it back. + class ReadbackYUVImpl : public ReadbackYUVInterface { + public: + ReadbackYUVImpl(WebGraphicsContext3D* context, + CopyTextureToImpl* copy_impl, + GLHelperScaling* scaler_impl, + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + const gfx::Rect& dst_subrect, + bool flip_vertically); + + virtual void ReadbackYUV( + WebKit::WebGLId src_texture, + media::VideoFrame* target, + const base::Callback<void(bool)>& callback) OVERRIDE; + + virtual ScalerInterface* scaler() OVERRIDE { + return scaler_.scaler(); + } + + private: + void ReadbackPlane(ScalerHolder* scaler, + media::VideoFrame* target, + int plane, + int size_shift, + const base::Callback<void(bool)>& callback); + + WebGraphicsContext3D* context_; + CopyTextureToImpl* copy_impl_; + gfx::Size dst_size_; + gfx::Rect dst_subrect_; + ScalerHolder scaler_; + ScalerHolder y_; + ScalerHolder u_; + ScalerHolder v_; + }; // Copies the block of pixels specified with |src_subrect| from |src_texture|, // scales it to |dst_size|, writes it into a texture, and returns its ID. @@ -102,13 +213,14 @@ class GLHelper::CopyTextureToImpl : bool swizzle, GLHelper::ScalerQuality quality); + static void nullcallback(bool success) {} void ReadbackDone(Request* request); void FinishRequest(Request* request, bool result); void CancelRequests(); - // Interleaved array of 2-dimentional vertex positions (x, y) and - // 2-dimentional texture coordinates (s, t). - static const WebKit::WGC3Dfloat kVertexAttributes[]; + static const float kRGBtoYColorWeights[]; + static const float kRGBtoUColorWeights[]; + static const float kRGBtoVColorWeights[]; WebGraphicsContext3D* context_; GLHelper* helper_; @@ -173,6 +285,36 @@ WebGLId GLHelper::CopyTextureToImpl::ScaleTexture( return dst_texture; } +void GLHelper::CopyTextureToImpl::ReadbackAsync( + const gfx::Size& dst_size, + int32 bytes_per_row, + int32 row_stride_bytes, + unsigned char* out, + const base::Callback<void(bool)>& callback) { + Request* request = new Request(dst_size, + bytes_per_row, + row_stride_bytes, + out, + callback); + request_queue_.push(request); + request->buffer = context_->createBuffer(); + context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, + request->buffer); + context_->bufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, + 4 * dst_size.GetArea(), + NULL, + GL_STREAM_READ); + + context_->readPixels(0, 0, dst_size.width(), dst_size.height(), + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); + cc::SyncPointHelper::SignalSyncPoint( + context_, + context_->insertSyncPoint(), + base::Bind(&CopyTextureToImpl::ReadbackDone, AsWeakPtr(), request)); +} + + void GLHelper::CopyTextureToImpl::CropScaleReadbackAndCleanTexture( WebGLId src_texture, const gfx::Size& src_size, @@ -192,38 +334,21 @@ void GLHelper::CopyTextureToImpl::CropScaleReadbackAndCleanTexture( false, #endif quality); - context_->flush(); - Request* request = new Request(texture, dst_size, out, callback); - request_queue_.push(request); - - ScopedFlush flush(context_); ScopedFramebuffer dst_framebuffer(context_, context_->createFramebuffer()); - gfx::Size size; - size = request->size; - ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(context_, dst_framebuffer); - ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, request->texture); + ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture); context_->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - request->texture, + texture, 0); - request->buffer = context_->createBuffer(); - context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, - request->buffer); - context_->bufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, - 4 * size.GetArea(), - NULL, - GL_STREAM_READ); - - context_->readPixels(0, 0, size.width(), size.height(), - GL_RGBA, GL_UNSIGNED_BYTE, NULL); - context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); - cc::SyncPointHelper::SignalSyncPoint( - context_, - context_->insertSyncPoint(), - base::Bind(&CopyTextureToImpl::ReadbackDone, AsWeakPtr(), request)); + ReadbackAsync(dst_size, + dst_size.width() * 4, + dst_size.width() * 4, + out, + callback); + context_->deleteTexture(texture); } void GLHelper::CopyTextureToImpl::ReadbackTextureSync( @@ -265,19 +390,29 @@ WebKit::WebGLId GLHelper::CopyTextureToImpl::CopyAndScaleTexture( void GLHelper::CopyTextureToImpl::ReadbackDone(Request* request) { TRACE_EVENT0("mirror", - "GLHelper::CopyTextureToImpl::CheckReadBackFramebufferComplete"); + "GLHelper::CopyTextureToImpl::CheckReadbackFramebufferComplete"); DCHECK(request == request_queue_.front()); bool result = false; if (request->buffer != 0) { context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer); - void* data = context_->mapBufferCHROMIUM( - GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY); - + unsigned char* data = static_cast<unsigned char *>( + context_->mapBufferCHROMIUM( + GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY)); if (data) { result = true; - memcpy(request->pixels, data, request->size.GetArea() * 4); + if (request->bytes_per_row == request->size.width() * 4 && + request->bytes_per_row == request->row_stride_bytes) { + memcpy(request->pixels, data, request->size.GetArea() * 4); + } else { + unsigned char* out = request->pixels; + for (int y = 0; y < request->size.height(); y++) { + memcpy(out, data, request->bytes_per_row); + out += request->row_stride_bytes; + data += request->size.width() * 4; + } + } context_->unmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM); } context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); @@ -292,10 +427,6 @@ void GLHelper::CopyTextureToImpl::FinishRequest(Request* request, request_queue_.pop(); request->callback.Run(result); ScopedFlush flush(context_); - if (request->texture != 0) { - context_->deleteTexture(request->texture); - request->texture = 0; - } if (request->buffer != 0) { context_->deleteBuffer(request->buffer); request->buffer = 0; @@ -336,8 +467,8 @@ void GLHelper::CropScaleReadbackAndCleanTexture( } void GLHelper::ReadbackTextureSync(WebKit::WebGLId texture, - const gfx::Rect& src_rect, - unsigned char* out) { + const gfx::Rect& src_rect, + unsigned char* out) { InitCopyTextToImpl(); copy_texture_to_impl_->ReadbackTextureSync(texture, src_rect, @@ -423,4 +554,168 @@ void GLHelper::CopySubBufferDamage(WebKit::WebGLId texture, } } +const float GLHelper::CopyTextureToImpl::kRGBtoYColorWeights[] = { + 0.257f, 0.504f, 0.098f, 0.0625f +}; +const float GLHelper::CopyTextureToImpl::kRGBtoUColorWeights[] = { + -0.148f, -0.291f, 0.439f, 0.5f +}; +const float GLHelper::CopyTextureToImpl::kRGBtoVColorWeights[] = { + 0.439f, -0.368f, -0.071f, 0.5f +}; + +// YUV readback constructors. Initiates the main scaler pipeline and +// one planar scaler for each of the Y, U and V planes. +GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUVImpl( + WebGraphicsContext3D* context, + CopyTextureToImpl* copy_impl, + GLHelperScaling* scaler_impl, + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + const gfx::Rect& dst_subrect, + bool flip_vertically) + : context_(context), + copy_impl_(copy_impl), + dst_size_(dst_size), + dst_subrect_(dst_subrect), + scaler_(context, scaler_impl->CreateScaler( + quality, + src_size, + src_subrect, + dst_subrect.size(), + flip_vertically, + false)), + y_(context, scaler_impl->CreatePlanarScaler( + dst_subrect.size(), + gfx::Rect(0, 0, + (dst_subrect.width() + 3) & ~3, + dst_subrect.height()), + gfx::Size((dst_subrect.width() + 3) / 4, + dst_subrect.height()), + false, + kRGBtoYColorWeights)), + u_(context, scaler_impl->CreatePlanarScaler( + dst_subrect.size(), + gfx::Rect(0, 0, + (dst_subrect.width() + 7) & ~7, + (dst_subrect.height() + 1) & ~1), + gfx::Size((dst_subrect.width() + 7) / 8, + (dst_subrect.height() + 1) / 2), + false, + kRGBtoUColorWeights)), + v_(context, scaler_impl->CreatePlanarScaler( + dst_subrect.size(), + gfx::Rect(0, 0, + (dst_subrect.width() + 7) & ~7, + (dst_subrect.height() + 1) & ~1), + gfx::Size((dst_subrect.width() + 7) / 8, + (dst_subrect.height() + 1) / 2), + false, + kRGBtoVColorWeights)) { + DCHECK(!(dst_size.width() & 1)); + DCHECK(!(dst_size.height() & 1)); + DCHECK(!(dst_subrect.width() & 1)); + DCHECK(!(dst_subrect.height() & 1)); + DCHECK(!(dst_subrect.x() & 1)); + DCHECK(!(dst_subrect.y() & 1)); +} + +void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUV( + WebKit::WebGLId src_texture, + media::VideoFrame *target, + const base::Callback<void(bool)>& callback) { + // Scale texture to right size. + scaler_.Scale(src_texture); + + // Convert the scaled texture in to Y, U and V planes. + y_.Scale(scaler_.texture_); + u_.Scale(scaler_.texture_); + v_.Scale(scaler_.texture_); + + if (target->coded_size() != dst_size_) { + DCHECK(target->coded_size() == dst_size_); + LOG(ERROR) << "ReadbackYUV size error!"; + callback.Run(false); + return; + } + + // Read back planes, one at a time. + ReadbackPlane(&y_, + target, + media::VideoFrame::kYPlane, + 0, + base::Bind(&nullcallback)); + ReadbackPlane(&u_, + target, + media::VideoFrame::kUPlane, + 1, + base::Bind(&nullcallback)); + ReadbackPlane(&v_, + target, + media::VideoFrame::kVPlane, + 1, + callback); + context_->bindFramebuffer(GL_FRAMEBUFFER, 0); + media::LetterboxYUV(target, dst_subrect_); +} + +void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackPlane( + ScalerHolder *scaler, + media::VideoFrame *target, + int plane, + int size_shift, + const base::Callback<void(bool)>& callback) { + context_->bindFramebuffer(GL_FRAMEBUFFER, scaler->framebuffer()); + copy_impl_->ReadbackAsync( + scaler->scaler()->DstSize(), + dst_subrect_.width() >> size_shift, + target->stride(plane), + target->data(plane) + + target->stride(plane) * (dst_subrect_.y() >> size_shift) + + (dst_subrect_.x() >> size_shift), + callback); +} + + +ReadbackYUVInterface* +GLHelper::CopyTextureToImpl::CreateReadbackPipelineYUV( + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + const gfx::Rect& dst_subrect, + bool flip_vertically) { + helper_->InitScalerImpl(); + return new ReadbackYUVImpl( + context_, + this, + helper_->scaler_impl_.get(), + quality, + src_size, + src_subrect, + dst_size, + dst_subrect, + flip_vertically); +} + +ReadbackYUVInterface* +GLHelper::CreateReadbackPipelineYUV( + ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + const gfx::Rect& dst_subrect, + bool flip_vertically) { + InitCopyTextToImpl(); + return copy_texture_to_impl_->CreateReadbackPipelineYUV( + quality, + src_size, + src_subrect, + dst_size, + dst_subrect, + flip_vertically); +} + } // namespace content diff --git a/content/common/gpu/client/gl_helper.h b/content/common/gpu/client/gl_helper.h index 7e4112c..95e27e6 100644 --- a/content/common/gpu/client/gl_helper.h +++ b/content/common/gpu/client/gl_helper.h @@ -17,6 +17,10 @@ class Rect; class Size; } +namespace media { +class VideoFrame; +}; + class SkRegion; namespace content { @@ -178,6 +182,9 @@ class ScopedFlush { DISALLOW_COPY_AND_ASSIGN(ScopedFlush); }; + +class ReadbackYUVInterface; + // Provides higher level operations on top of the WebKit::WebGraphicsContext3D // interfaces. class CONTENT_EXPORT GLHelper { @@ -284,6 +291,20 @@ class CONTENT_EXPORT GLHelper { bool vertically_flip_texture, bool swizzle); + // Create a readback pipeline that will scale a subsection of the source + // texture, then convert it to YUV422 planar form and then read back that. + // This reduces the amount of memory read from GPU to CPU memory by a factor + // 2.6, which can be quite handy since readbacks have very limited speed + // on some platforms. All values in |dst_size| and |dst_subrect| must be + // a multiple of two. + ReadbackYUVInterface* CreateReadbackPipelineYUV( + ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + const gfx::Rect& dst_subrect, + bool flip_vertically); + protected: class CopyTextureToImpl; @@ -299,6 +320,26 @@ class CONTENT_EXPORT GLHelper { DISALLOW_COPY_AND_ASSIGN(GLHelper); }; +// Similar to a ScalerInterface, a yuv readback pipeline will +// cache a scaler and all intermediate textures and frame buffers +// needed to scale, crop, letterbox and read back a texture from +// the GPU into CPU-accessible RAM. A single readback pipeline +// can handle multiple outstanding readbacks at the same time, but +// if the source or destination sizes change, you'll need to create +// a new readback pipeline. +class CONTENT_EXPORT ReadbackYUVInterface { +public: + ReadbackYUVInterface() {} + virtual ~ReadbackYUVInterface() {} + + // Note that |target| must use YV12 format. + virtual void ReadbackYUV( + WebKit::WebGLId texture, + media::VideoFrame* target, + const base::Callback<void(bool)>& callback) = 0; + virtual GLHelper::ScalerInterface* scaler() = 0; +}; + } // namespace content #endif // CONTENT_COMMON_GPU_CLIENT_GL_HELPER_H_ diff --git a/content/common/gpu/client/gl_helper_scaling.cc b/content/common/gpu/client/gl_helper_scaling.cc index 4983f22..220845e 100644 --- a/content/common/gpu/client/gl_helper_scaling.cc +++ b/content/common/gpu/client/gl_helper_scaling.cc @@ -61,7 +61,8 @@ class ShaderProgram : public base::RefCounted<ShaderProgram> { const gfx::Rect& src_subrect, const gfx::Size& dst_size, bool scale_x, - bool flip_y); + bool flip_y, + GLfloat color_weights[4]); private: friend class base::RefCounted<ShaderProgram>; @@ -88,6 +89,8 @@ class ShaderProgram : public base::RefCounted<ShaderProgram> { WebKit::WGC3Dint dst_pixelsize_location_; // Location of vector for scaling direction. WebKit::WGC3Dint scaling_vector_location_; + // Location of color weights. + WebKit::WGC3Dint color_weights_location_; DISALLOW_COPY_AND_ASSIGN(ShaderProgram); }; @@ -107,16 +110,30 @@ class ScalerImpl : public GLHelper::ScalerInterface { // If we are scaling in both X and Y, |scale_x| is ignored. // If |vertically_flip_texture| is true, output will be upside-down. // If |swizzle| is true, RGBA will be transformed into BGRA. + // |color_weights| are only used together with SHADER_PLANAR to specify + // how to convert RGB colors into a single value. ScalerImpl(WebGraphicsContext3D* context, GLHelperScaling* scaler_helper, const GLHelperScaling::ScalerStage &scaler_stage, - ScalerImpl* subscaler) : + ScalerImpl* subscaler, + const float* color_weights) : context_(context), scaler_helper_(scaler_helper), spec_(scaler_stage), intermediate_texture_(0), dst_framebuffer_(context, context_->createFramebuffer()), subscaler_(subscaler) { + if (color_weights) { + color_weights_[0] = color_weights[0]; + color_weights_[1] = color_weights[1]; + color_weights_[2] = color_weights[2]; + color_weights_[3] = color_weights[3]; + } else { + color_weights_[0] = 0.0; + color_weights_[1] = 0.0; + color_weights_[2] = 0.0; + color_weights_[3] = 0.0; + } shader_program_ = scaler_helper_->GetShaderProgram(spec_.shader, spec_.swizzle); @@ -182,7 +199,8 @@ class ScalerImpl : public GLHelper::ScalerInterface { spec_.src_subrect, spec_.dst_size, spec_.scale_x, - spec_.vertically_flip_texture); + spec_.vertically_flip_texture, + color_weights_); context_->viewport(0, 0, spec_.dst_size.width(), spec_.dst_size.height()); // Conduct texture mapping by drawing a quad composed of two triangles. @@ -209,6 +227,7 @@ class ScalerImpl : public GLHelper::ScalerInterface { WebGraphicsContext3D* context_; GLHelperScaling* scaler_helper_; GLHelperScaling::ScalerStage spec_; + GLfloat color_weights_[4]; WebKit::WebGLId intermediate_texture_; scoped_refptr<ShaderProgram> shader_program_; ScopedFramebuffer dst_framebuffer_; @@ -427,11 +446,28 @@ GLHelperScaling::CreateScaler(GLHelper::ScalerQuality quality, ScalerImpl* ret = NULL; for (unsigned int i = 0; i < scaler_stages.size(); i++) { - ret = new ScalerImpl(context_, this, scaler_stages[i], ret); + ret = new ScalerImpl(context_, this, scaler_stages[i], ret, NULL); } return ret; } +GLHelper::ScalerInterface* +GLHelperScaling::CreatePlanarScaler( + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + const float color_weights[4]) { + ScalerStage stage(SHADER_PLANAR, + src_size, + src_subrect, + dst_size, + true, + vertically_flip_texture, + false); + return new ScalerImpl(context_, this, stage, NULL, color_weights); +} + const WebKit::WGC3Dfloat GLHelperScaling::kVertexAttributes[] = { -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, @@ -478,7 +514,8 @@ GLHelperScaling::GetShaderProgram(ShaderType type, vertex_program.append( " gl_Position = vec4(a_position, 0.0, 1.0);\n" - " vec2 texcoord = src_subrect.xy + a_texcoord * src_subrect.zw;\n"); + " vec2 texcoord = src_subrect.xy + a_texcoord * src_subrect.zw;\n" + " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"); switch (type) { case SHADER_BILINEAR: @@ -495,7 +532,7 @@ GLHelperScaling::GetShaderProgram(ShaderType type, shared_variables.append( "varying vec4 v_texcoords;\n"); // 2 texcoords packed in one quad vertex_program.append( - " vec2 step = scaling_vector / dst_pixelsize / 4.0;\n" + " step /= 4.0;\n" " v_texcoords.xy = texcoord + step;\n" " v_texcoords.zw = texcoord - step;\n"); @@ -511,7 +548,7 @@ GLHelperScaling::GetShaderProgram(ShaderType type, "varying vec4 v_texcoords1;\n" // 2 texcoords packed in one quad "varying vec2 v_texcoords2;\n"); vertex_program.append( - " vec2 step = scaling_vector / dst_pixelsize / 3.0;\n" + " step /= 3.0;\n" " v_texcoords1.xy = texcoord + step;\n" " v_texcoords1.zw = texcoord;\n" " v_texcoords2 = texcoord - step;\n"); @@ -527,7 +564,7 @@ GLHelperScaling::GetShaderProgram(ShaderType type, shared_variables.append( "varying vec4 v_texcoords[2];\n"); vertex_program.append( - " vec2 step = scaling_vector / dst_pixelsize / 8.0;\n" + " step /= 8.0;\n" " v_texcoords[0].xy = texcoord - step * 3.0;\n" " v_texcoords[0].zw = texcoord - step;\n" " v_texcoords[1].xy = texcoord + step;\n" @@ -548,7 +585,7 @@ GLHelperScaling::GetShaderProgram(ShaderType type, shared_variables.append( "varying vec4 v_texcoords[2];\n"); vertex_program.append( - " vec2 step = 1.0 / 4.0 / dst_pixelsize;\n" + " step = src_subrect.zw / 4.0 / dst_pixelsize;\n" " v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n" " v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n" " v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n" @@ -572,7 +609,7 @@ GLHelperScaling::GetShaderProgram(ShaderType type, "const float LobeWeight = -3.0 / 64.0;\n" "varying vec4 v_texcoords[2];\n"); vertex_program.append( - " vec2 step = scaling_vector / src_pixelsize;\n" + " step = src_subrect.zw * scaling_vector / src_pixelsize;\n" " v_texcoords[0].xy = texcoord - LobeDist * step;\n" " v_texcoords[0].zw = texcoord - CenterDist * step;\n" " v_texcoords[1].xy = texcoord + CenterDist * step;\n" @@ -627,6 +664,32 @@ GLHelperScaling::GetShaderProgram(ShaderType type, " vec2 step = scaling_vector / src_pixelsize;\n" " gl_FragColor = pixels_x(base, step) * filt4(frac);\n"); break; + + case SHADER_PLANAR: + // Converts four RGBA pixels into one pixel. Each RGBA + // pixel will be dot-multiplied with the color weights and + // then placed into a component of the output. This is used to + // convert RGBA textures into Y, U and V textures. We do this + // because single-component textures are not renderable on all + // architectures. + shared_variables.append( + "varying vec4 v_texcoords[2];\n" + "uniform vec4 color_weights;\n"); + vertex_program.append( + " step /= 4.0;\n" + " v_texcoords[0].xy = texcoord - step * 1.5;\n" + " v_texcoords[0].zw = texcoord - step * 0.5;\n" + " v_texcoords[1].xy = texcoord + step * 0.5;\n" + " v_texcoords[1].zw = texcoord + step * 1.5;\n"); + fragment_program.append( + " gl_FragColor = color_weights * mat4(\n" + " vec4(texture2D(s_texture, v_texcoords[0].xy).rgb, 1.0),\n" + " vec4(texture2D(s_texture, v_texcoords[0].zw).rgb, 1.0),\n" + " vec4(texture2D(s_texture, v_texcoords[1].xy).rgb, 1.0),\n" + " vec4(texture2D(s_texture, v_texcoords[1].zw).rgb, 1.0));\n"); + // Swizzle makes no sense for this shader. + DCHECK(!swizzle); + break; } if (swizzle) { fragment_program.append(" gl_FragColor = gl_FragColor.bgra;\n"); @@ -688,6 +751,8 @@ bool ShaderProgram::Setup(const WebKit::WGC3Dchar* vertex_shader_text, "dst_pixelsize"); scaling_vector_location_ = context_->getUniformLocation(program_, "scaling_vector"); + color_weights_location_ = context_->getUniformLocation(program_, + "color_weights"); return true; } @@ -696,7 +761,8 @@ void ShaderProgram::UseProgram( const gfx::Rect& src_subrect, const gfx::Size& dst_size, bool scale_x, - bool flip_y) { + bool flip_y, + GLfloat color_weights[4]) { context_->useProgram(program_); WebKit::WGC3Dintptr offset = 0; @@ -742,6 +808,7 @@ void ShaderProgram::UseProgram( context_->uniform2f(scaling_vector_location_, scale_x ? 1.0 : 0.0, scale_x ? 0.0 : 1.0); + context_->uniform4fv(color_weights_location_, 1, color_weights); } } // namespace content diff --git a/content/common/gpu/client/gl_helper_scaling.h b/content/common/gpu/client/gl_helper_scaling.h index ff5ba7c..097dfb6 100644 --- a/content/common/gpu/client/gl_helper_scaling.h +++ b/content/common/gpu/client/gl_helper_scaling.h @@ -28,6 +28,7 @@ class CONTENT_EXPORT GLHelperScaling { SHADER_BILINEAR2X2, SHADER_BICUBIC_UPSCALE, SHADER_BICUBIC_HALF_1D, + SHADER_PLANAR, }; typedef std::pair<ShaderType, bool> ShaderProgramKeyType; @@ -45,6 +46,13 @@ class CONTENT_EXPORT GLHelperScaling { bool vertically_flip_texture, bool swizzle); + GLHelper::ScalerInterface* CreatePlanarScaler( + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + const float color_weights[4]); + private: // A ScaleOp represents a pass in a scaler pipeline, in one dimension. // Note that when quality is GOOD, multiple scaler passes will be diff --git a/content/common/gpu/client/gl_helper_unittests.cc b/content/common/gpu/client/gl_helper_unittests.cc index 21a77f7..4e53258 100644 --- a/content/common/gpu/client/gl_helper_unittests.cc +++ b/content/common/gpu/client/gl_helper_unittests.cc @@ -12,14 +12,18 @@ #include <GLES2/gl2extchromium.h> #include "base/at_exit.h" +#include "base/bind.h" #include "base/command_line.h" #include "base/file_util.h" +#include "base/message_loop.h" +#include "base/run_loop.h" #include "base/stringprintf.h" #include "base/time.h" #include "content/common/gpu/client/gl_helper.h" #include "content/common/gpu/client/gl_helper_scaling.h" #include "content/public/test/unittest_test_suite.h" #include "content/test/content_test_suite.h" +#include "media/base/video_frame.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkTypes.h" @@ -59,7 +63,6 @@ class GLHelperTest : public testing::Test { webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl:: CreateOffscreenContext(attributes)); context_->makeContextCurrent(); - helper_.reset(new content::GLHelper(context_.get())); helper_scaling_.reset(new content::GLHelperScaling( context_.get(), @@ -156,6 +159,9 @@ class GLHelperTest : public testing::Test { ret.append("bicubic 1/2"); xy_matters = true; break; + case GLHelperScaling::SHADER_PLANAR: + ret.append("planar"); + break; } if (xy_matters) { @@ -233,6 +239,10 @@ class GLHelperTest : public testing::Test { // Codify valid scale operations. switch (scaler_stages[i].shader) { + case GLHelperScaling::SHADER_PLANAR: + EXPECT_TRUE(false) << "Invalid shader."; + break; + case GLHelperScaling::SHADER_BILINEAR: if (quality != content::GLHelper::SCALER_QUALITY_FAST) { x_samples = 1; @@ -503,19 +513,19 @@ class GLHelperTest : public testing::Test { for (int x = 0; x < xsize; ++x) { for (int y = 0; y < ysize; ++y) { switch (test_pattern) { - case 0: // Smooth test pattern + case 0: // Smooth test pattern SetChannel(&input_pixels, x, y, 0, x * 10); SetChannel(&input_pixels, x, y, 1, y * 10); SetChannel(&input_pixels, x, y, 2, (x + y) * 10); SetChannel(&input_pixels, x, y, 3, 255); break; - case 1: // Small blocks + case 1: // Small blocks SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0); SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0); SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0); SetChannel(&input_pixels, x, y, 3, 255); break; - case 2: // Medium blocks + case 2: // Medium blocks SetChannel(&input_pixels, x, y, 0, 10 + x/2 * 50); SetChannel(&input_pixels, x, y, 1, 10 + y/3 * 50); SetChannel(&input_pixels, x, y, 2, (x + y)/5 * 50 + 5); @@ -647,6 +657,249 @@ class GLHelperTest : public testing::Test { EXPECT_EQ(PrintStages(stages), description); } + // Note: Left/Right means Top/Bottom when used for Y dimension. + enum Margin { + MarginLeft, + MarginMiddle, + MarginRight, + MarginInvalid, + }; + + static Margin NextMargin(Margin m) { + switch (m) { + case MarginLeft: + return MarginMiddle; + case MarginMiddle: + return MarginRight; + case MarginRight: + return MarginInvalid; + default: + return MarginInvalid; + } + } + + int compute_margin(int insize, int outsize, Margin m) { + int available = outsize - insize; + switch (m) { + default: + EXPECT_TRUE(false) << "This should not happen."; + return 0; + case MarginLeft: + return 0; + case MarginMiddle: + return (available / 2) & ~1; + case MarginRight: + return available; + } + } + + // Convert 0.0 - 1.0 to 0 - 255 + int float_to_byte(float v) { + int ret = static_cast<int>(floorf(v * 255.0f + 0.5f)); + if (ret < 0) { + return 0; + } + if (ret > 255) { + return 255; + } + return ret; + } + + static void callcallback(const base::Callback<void()>& callback, + bool result) { + callback.Run(); + } + + void PrintPlane(unsigned char *plane, int xsize, int stride, int ysize) { + for (int y = 0; y < ysize; y++) { + for (int x = 0; x < xsize ; x++) { + printf("%3d, ", plane[y * stride + x]); + } + printf(" (%p)\n", plane + y * stride); + } + } + + // Compare two planes make sure that each component of each pixel + // is no more than |maxdiff| apart. + void ComparePlane(unsigned char* truth, + unsigned char* other, + int maxdiff, + int xsize, + int stride, + int ysize, + SkBitmap* source, + std::string message) { + for (int x = 0; x < xsize; x++) { + for (int y = 0; y < ysize; y++) { + int a = other[y * stride + x]; + int b = truth[y * stride + x]; + EXPECT_NEAR(a, b, maxdiff) + << " x=" << x + << " y=" << y + << " " << message; + if (std::abs(a - b) > maxdiff) { + printf("-------expected--------\n"); + PrintPlane(truth, xsize, stride, ysize); + printf("-------actual--------\n"); + PrintPlane(other, xsize, stride, ysize); + if (source) { + printf("-------before yuv conversion: red--------\n"); + PrintChannel(source, 0); + printf("-------before yuv conversion: green------\n"); + PrintChannel(source, 1); + printf("-------before yuv conversion: blue-------\n"); + PrintChannel(source, 2); + } + return; + } + } + } + } + + // YUV readback test. Create a test pattern, convert to YUV + // with reference implementation and compare to what gl_helper + // returns. + void TestYUVReadback(int xsize, + int ysize, + int output_xsize, + int output_ysize, + int xmargin, + int ymargin, + int test_pattern) { + WebGLId src_texture = context_->createTexture(); + SkBitmap input_pixels; + input_pixels.setConfig(SkBitmap::kARGB_8888_Config, xsize, ysize); + input_pixels.allocPixels(); + SkAutoLockPixels lock(input_pixels); + + for (int x = 0; x < xsize; ++x) { + for (int y = 0; y < ysize; ++y) { + switch (test_pattern) { + case 0: // Smooth test pattern + SetChannel(&input_pixels, x, y, 0, x * 10); + SetChannel(&input_pixels, x, y, 1, y * 10); + SetChannel(&input_pixels, x, y, 2, (x + y) * 10); + SetChannel(&input_pixels, x, y, 3, 255); + break; + case 1: // Small blocks + SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0); + SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0); + SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0); + SetChannel(&input_pixels, x, y, 3, 255); + break; + case 2: // Medium blocks + SetChannel(&input_pixels, x, y, 0, 10 + x/2 * 50); + SetChannel(&input_pixels, x, y, 1, 10 + y/3 * 50); + SetChannel(&input_pixels, x, y, 2, (x + y)/5 * 50 + 5); + SetChannel(&input_pixels, x, y, 3, 255); + break; + } + } + } + + context_->bindTexture(GL_TEXTURE_2D, src_texture); + context_->texImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + xsize, + ysize, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + input_pixels.getPixels()); + + std::string message = base::StringPrintf("input size: %dx%d " + "output size: %dx%d " + "margin: %dx%d " + "pattern: %d", + xsize, ysize, + output_xsize, output_ysize, + xmargin, ymargin, + test_pattern); + scoped_ptr<ReadbackYUVInterface> yuv_reader( + helper_->CreateReadbackPipelineYUV( + content::GLHelper::SCALER_QUALITY_GOOD, + gfx::Size(xsize, ysize), + gfx::Rect(0, 0, xsize, ysize), + gfx::Size(output_xsize, output_ysize), + gfx::Rect(xmargin, ymargin, xsize, ysize), + false)); + + scoped_refptr<media::VideoFrame> output_frame = + media::VideoFrame::CreateFrame( + media::VideoFrame::YV12, + gfx::Size(output_xsize, output_ysize), + gfx::Rect(0, 0, output_xsize, output_ysize), + gfx::Size(output_xsize, output_ysize), + base::TimeDelta::FromSeconds(0)); + scoped_refptr<media::VideoFrame> truth_frame = + media::VideoFrame::CreateFrame( + media::VideoFrame::YV12, + gfx::Size(output_xsize, output_ysize), + gfx::Rect(0, 0, output_xsize, output_ysize), + gfx::Size(output_xsize, output_ysize), + base::TimeDelta::FromSeconds(0)); + + base::RunLoop run_loop; + yuv_reader->ReadbackYUV( + src_texture, + output_frame.get(), + base::Bind(&callcallback, run_loop.QuitClosure())); + run_loop.Run(); + + unsigned char* Y = truth_frame->data(media::VideoFrame::kYPlane); + unsigned char* U = truth_frame->data(media::VideoFrame::kUPlane); + unsigned char* V = truth_frame->data(media::VideoFrame::kVPlane); + int32 y_stride = truth_frame->stride(media::VideoFrame::kYPlane); + int32 u_stride = truth_frame->stride(media::VideoFrame::kUPlane); + int32 v_stride = truth_frame->stride(media::VideoFrame::kVPlane); + memset(Y, 0x00, y_stride * output_ysize); + memset(U, 0x80, u_stride * output_ysize / 2); + memset(V, 0x80, v_stride * output_ysize / 2); + + for (int y = 0; y < ysize; y++) { + for (int x = 0; x < xsize; x++) { + Y[(y + ymargin) * y_stride + x + xmargin] = float_to_byte( + ChannelAsFloat(&input_pixels, x, y, 0) * 0.257 + + ChannelAsFloat(&input_pixels, x, y, 1) * 0.504 + + ChannelAsFloat(&input_pixels, x, y, 2) * 0.098 + + 0.0625); + } + } + + for (int y = 0; y < ysize / 2; y++) { + for (int x = 0; x < xsize / 2; x++) { + U[(y + ymargin / 2) * u_stride + x + xmargin / 2] = + float_to_byte( + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) * -0.148 + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) * -0.291 + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) * 0.439 + + 0.5); + V[(y + ymargin / 2) * v_stride + x + xmargin / 2] = + float_to_byte( + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) * 0.439 + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) * -0.368 + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) * -0.071 + + 0.5); + } + } + + ComparePlane(Y, output_frame->data(media::VideoFrame::kYPlane), 2, + output_xsize, y_stride, output_ysize, + &input_pixels, + message + " Y plane"); + ComparePlane(U, output_frame->data(media::VideoFrame::kUPlane), 2, + output_xsize / 2, u_stride, output_ysize / 2, + &input_pixels, + message + " U plane"); + ComparePlane(V, output_frame->data(media::VideoFrame::kVPlane), 2, + output_xsize / 2, v_stride, output_ysize / 2, + &input_pixels, + message + " V plane"); + + context_->deleteTexture(src_texture); + } + void TestAddOps(int src, int dst, bool scale_x, @@ -844,6 +1097,42 @@ class GLHelperTest : public testing::Test { std::deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_; }; +TEST_F(GLHelperTest, YUVReadbackTest) { + int sizes[] = { 2, 4, 14 }; + for (unsigned int x = 0; x < arraysize(sizes); x++) { + for (unsigned int y = 0; y < arraysize(sizes); y++) { + for (unsigned int ox = x; ox < arraysize(sizes); ox++) { + for (unsigned int oy = y; oy < arraysize(sizes); oy++) { + // If output is a subsection of the destination frame, (letterbox) + // then try different variations of where the subsection goes. + for (Margin xm = x < ox ? MarginLeft : MarginRight; + xm <= MarginRight; + xm = NextMargin(xm)) { + for (Margin ym = y < oy ? MarginLeft : MarginRight; + ym <= MarginRight; + ym = NextMargin(ym)) { + for (int pattern = 0; pattern < 3; pattern++) { + TestYUVReadback( + sizes[x], + sizes[y], + sizes[ox], + sizes[oy], + compute_margin(sizes[x], sizes[ox], xm), + compute_margin(sizes[y], sizes[oy], ym), + pattern); + if (HasFailure()) { + return; + } + } + } + } + } + } + } + } +} + + // Per pixel tests, all sizes are small so that we can print // out the generated bitmaps. TEST_F(GLHelperTest, ScaleTest) { @@ -947,5 +1236,7 @@ int main(int argc, char** argv) { #endif gfx::GLSurface::InitializeOneOff(); - return content::UnitTestTestSuite(suite).Run(); + content::UnitTestTestSuite runner(suite); + base::MessageLoop message_loop; + return runner.Run(); } diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc index a2c93f3..de747d1c 100644 --- a/content/public/common/content_switches.cc +++ b/content/public/common/content_switches.cc @@ -685,6 +685,13 @@ const char kSitePerProcess[] = "site-per-process"; // content. The switch is intended only for tests. const char kSkipGpuDataLoading[] = "skip-gpu-data-loading"; +// Scaling quality for capturing tab. Should be one of "fast", "good" or "best". +// One flag for upscaling, one for downscaling. +// Upscale defaults to "best". +const char kTabCaptureUpscaleQuality[] = "tab-capture-upscale-quality"; +// Upscale defaults to "good". +const char kTabCaptureDownscaleQuality[] = "tab-capture-downscale-quality"; + // GestureTapDown events are deferred by this many miillseconds before // sending them to the renderer. const char kTapDownDeferralTimeMs[] = "tap-down-deferral-time"; diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h index 00a6c75..d3c4443 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h @@ -205,6 +205,8 @@ CONTENT_EXPORT extern const char kSimulateTouchScreenWithMouse[]; CONTENT_EXPORT extern const char kSingleProcess[]; CONTENT_EXPORT extern const char kSitePerProcess[]; CONTENT_EXPORT extern const char kSkipGpuDataLoading[]; +extern const char kTabCaptureUpscaleQuality[]; +extern const char kTabCaptureDownscaleQuality[]; extern const char kTapDownDeferralTimeMs[]; CONTENT_EXPORT extern const char kTestSandbox[]; CONTENT_EXPORT extern const char kTestingFixedHttpPort[]; |