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 /content/common/gpu/client/gl_helper.cc | |
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
Diffstat (limited to 'content/common/gpu/client/gl_helper.cc')
-rw-r--r-- | content/common/gpu/client/gl_helper.cc | 387 |
1 files changed, 341 insertions, 46 deletions
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 |