// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/compositor/gl_helper.h" #include #include #include #include #include "base/bind.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/message_loop/message_loop.h" #include "base/strings/string_util.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "content/browser/compositor/gl_helper_readback_support.h" #include "content/browser/compositor/gl_helper_scaling.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/context_support.h" #include "gpu/command_buffer/common/mailbox.h" #include "gpu/command_buffer/common/mailbox_holder.h" #include "media/base/video_frame.h" #include "media/base/video_util.h" #include "third_party/skia/include/core/SkRegion.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" using gpu::gles2::GLES2Interface; namespace { class ScopedFlush { public: explicit ScopedFlush(gpu::gles2::GLES2Interface* gl) : gl_(gl) {} ~ScopedFlush() { gl_->Flush(); } private: gpu::gles2::GLES2Interface* gl_; DISALLOW_COPY_AND_ASSIGN(ScopedFlush); }; // Helper class for allocating and holding an RGBA texture of a given // size and an associated framebuffer. class TextureFrameBufferPair { public: TextureFrameBufferPair(GLES2Interface* gl, gfx::Size size) : texture_(gl), framebuffer_(gl), size_(size) { content::ScopedTextureBinder texture_binder(gl, texture_); gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); content::ScopedFramebufferBinder framebuffer_binder( gl, framebuffer_); gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_, 0); } GLuint texture() const { return texture_.id(); } GLuint framebuffer() const { return framebuffer_.id(); } gfx::Size size() const { return size_; } private: content::ScopedTexture texture_; content::ScopedFramebuffer framebuffer_; gfx::Size size_; DISALLOW_COPY_AND_ASSIGN(TextureFrameBufferPair); }; // 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 to be sent to a readback. class ScalerHolder { public: ScalerHolder(GLES2Interface* gl, content::GLHelper::ScalerInterface* scaler) : texture_and_framebuffer_(gl, scaler->DstSize()), scaler_(scaler) {} void Scale(GLuint src_texture) { scaler_->Scale(src_texture, texture_and_framebuffer_.texture()); } content::GLHelper::ScalerInterface* scaler() const { return scaler_.get(); } TextureFrameBufferPair* texture_and_framebuffer() { return &texture_and_framebuffer_; } GLuint texture() const { return texture_and_framebuffer_.texture(); } private: TextureFrameBufferPair texture_and_framebuffer_; scoped_ptr scaler_; DISALLOW_COPY_AND_ASSIGN(ScalerHolder); }; } // namespace namespace content { typedef GLHelperReadbackSupport::FormatSupport FormatSupport; // Implements GLHelper::CropScaleReadbackAndCleanTexture and encapsulates // the data needed for it. class GLHelper::CopyTextureToImpl : public base::SupportsWeakPtr { public: CopyTextureToImpl(GLES2Interface* gl, gpu::ContextSupport* context_support, GLHelper* helper) : gl_(gl), context_support_(context_support), helper_(helper), flush_(gl), max_draw_buffers_(0) { const GLubyte* extensions = gl_->GetString(GL_EXTENSIONS); if (!extensions) return; std::string extensions_string = " " + std::string(reinterpret_cast(extensions)) + " "; if (extensions_string.find(" GL_EXT_draw_buffers ") != std::string::npos) { gl_->GetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &max_draw_buffers_); } } ~CopyTextureToImpl() { CancelRequests(); } GLuint ConsumeMailboxToTexture(const gpu::Mailbox& mailbox, const gpu::SyncToken& sync_token) { return helper_->ConsumeMailboxToTexture(mailbox, sync_token); } void CropScaleReadbackAndCleanTexture( GLuint src_texture, const gfx::Size& src_size, const gfx::Rect& src_subrect, const gfx::Size& dst_size, unsigned char* out, const SkColorType out_color_type, const base::Callback& callback, GLHelper::ScalerQuality quality); void ReadbackTextureSync(GLuint texture, const gfx::Rect& src_rect, unsigned char* out, SkColorType format); void ReadbackTextureAsync(GLuint texture, const gfx::Size& dst_size, unsigned char* out, SkColorType color_type, const base::Callback& callback); // 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_t bytes_per_row, // generally dst_size.width() * 4 int32_t row_stride_bytes, // generally dst_size.width() * 4 unsigned char* out, GLenum format, GLenum type, size_t bytes_per_pixel, const base::Callback& callback); void ReadbackPlane(TextureFrameBufferPair* source, const scoped_refptr& target, int plane, int size_shift, const gfx::Rect& paste_rect, ReadbackSwizzle swizzle, const base::Callback& callback); GLuint CopyAndScaleTexture(GLuint 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, bool flip_vertically, bool use_mrt); // Returns the maximum number of draw buffers available, // 0 if GL_EXT_draw_buffers is not available. GLint MaxDrawBuffers() const { return max_draw_buffers_; } FormatSupport GetReadbackConfig(SkColorType color_type, bool can_swizzle, GLenum* format, GLenum* type, size_t* bytes_per_pixel); private: // A single request to CropScaleReadbackAndCleanTexture. // The main thread can cancel the request, before it's handled by the helper // thread, by resetting the texture and pixels fields. Alternatively, the // thread marks that it handles the request by resetting the pixels field // (meaning it guarantees that the callback with be called). // In either case, the callback must be called exactly once, and the texture // must be deleted by the main thread gl. struct Request { Request(const gfx::Size& size_, int32_t bytes_per_row_, int32_t row_stride_bytes_, unsigned char* pixels_, const base::Callback& callback_) : done(false), size(size_), bytes_per_row(bytes_per_row_), row_stride_bytes(row_stride_bytes_), pixels(pixels_), callback(callback_), buffer(0), query(0) {} bool done; bool result; gfx::Size size; int bytes_per_row; int row_stride_bytes; unsigned char* pixels; base::Callback callback; GLuint buffer; GLuint query; }; // We must take care to call the callbacks last, as they may // end up destroying the gl_helper and make *this invalid. // We stick the finished requests in a stack object that calls // the callbacks when it goes out of scope. class FinishRequestHelper { public: FinishRequestHelper() {} ~FinishRequestHelper() { while (!requests_.empty()) { Request* request = requests_.front(); requests_.pop(); request->callback.Run(request->result); delete request; } } void Add(Request* r) { requests_.push(r); } private: std::queue requests_; DISALLOW_COPY_AND_ASSIGN(FinishRequestHelper); }; // A readback pipeline that also converts the data to YUV before // reading it back. class ReadbackYUVImpl : public ReadbackYUVInterface { public: ReadbackYUVImpl(GLES2Interface* gl, CopyTextureToImpl* copy_impl, GLHelperScaling* scaler_impl, GLHelper::ScalerQuality quality, const gfx::Size& src_size, const gfx::Rect& src_subrect, const gfx::Size& dst_size, bool flip_vertically, ReadbackSwizzle swizzle); void ReadbackYUV(const gpu::Mailbox& mailbox, const gpu::SyncToken& sync_token, const scoped_refptr& target, const gfx::Point& paste_location, const base::Callback& callback) override; ScalerInterface* scaler() override { return scaler_.scaler(); } private: GLES2Interface* gl_; CopyTextureToImpl* copy_impl_; gfx::Size dst_size_; ReadbackSwizzle swizzle_; ScalerHolder scaler_; ScalerHolder y_; ScalerHolder u_; ScalerHolder v_; DISALLOW_COPY_AND_ASSIGN(ReadbackYUVImpl); }; // A readback pipeline that also converts the data to YUV before // reading it back. This one uses Multiple Render Targets, which // may not be supported on all platforms. class ReadbackYUV_MRT : public ReadbackYUVInterface { public: ReadbackYUV_MRT(GLES2Interface* gl, CopyTextureToImpl* copy_impl, GLHelperScaling* scaler_impl, GLHelper::ScalerQuality quality, const gfx::Size& src_size, const gfx::Rect& src_subrect, const gfx::Size& dst_size, bool flip_vertically, ReadbackSwizzle swizzle); void ReadbackYUV(const gpu::Mailbox& mailbox, const gpu::SyncToken& sync_token, const scoped_refptr& target, const gfx::Point& paste_location, const base::Callback& callback) override; ScalerInterface* scaler() override { return scaler_.scaler(); } private: GLES2Interface* gl_; CopyTextureToImpl* copy_impl_; gfx::Size dst_size_; GLHelper::ScalerQuality quality_; ReadbackSwizzle swizzle_; ScalerHolder scaler_; scoped_ptr pass1_shader_; scoped_ptr pass2_shader_; TextureFrameBufferPair y_; ScopedTexture uv_; TextureFrameBufferPair u_; TextureFrameBufferPair v_; DISALLOW_COPY_AND_ASSIGN(ReadbackYUV_MRT); }; // 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. // |src_size| is the size of |src_texture|. GLuint ScaleTexture(GLuint src_texture, const gfx::Size& src_size, const gfx::Rect& src_subrect, const gfx::Size& dst_size, bool vertically_flip_texture, bool swizzle, SkColorType color_type, GLHelper::ScalerQuality quality); // Converts each four consecutive pixels of the source texture into one pixel // in the result texture with each pixel channel representing the grayscale // color of one of the four original pixels: // R1G1B1A1 R2G2B2A2 R3G3B3A3 R4G4B4A4 -> X1X2X3X4 // The resulting texture is still an RGBA texture (which is ~4 times narrower // than the original). If rendered directly, it wouldn't show anything useful, // but the data in it can be used to construct a grayscale image. // |encoded_texture_size| is the exact size of the resulting RGBA texture. It // is equal to src_size.width()/4 rounded upwards. Some channels in the last // pixel ((-src_size.width()) % 4) to be exact) are padding and don't contain // useful data. // If swizzle is set to true, the transformed pixels are reordered: // R1G1B1A1 R2G2B2A2 R3G3B3A3 R4G4B4A4 -> X3X2X1X4. GLuint EncodeTextureAsGrayscale(GLuint src_texture, const gfx::Size& src_size, gfx::Size* const encoded_texture_size, bool vertically_flip_texture, bool swizzle); static void nullcallback(bool success) {} void ReadbackDone(Request* request, int bytes_per_pixel); void FinishRequest(Request* request, bool result, FinishRequestHelper* helper); void CancelRequests(); static const float kRGBtoYColorWeights[]; static const float kRGBtoUColorWeights[]; static const float kRGBtoVColorWeights[]; static const float kRGBtoGrayscaleColorWeights[]; GLES2Interface* gl_; gpu::ContextSupport* context_support_; GLHelper* helper_; // A scoped flush that will ensure all resource deletions are flushed when // this object is destroyed. Must be declared before other Scoped* fields. ScopedFlush flush_; std::queue request_queue_; GLint max_draw_buffers_; }; GLHelper::ScalerInterface* GLHelper::CreateScaler(ScalerQuality quality, const gfx::Size& src_size, const gfx::Rect& src_subrect, const gfx::Size& dst_size, bool vertically_flip_texture, bool swizzle) { InitScalerImpl(); return scaler_impl_->CreateScaler(quality, src_size, src_subrect, dst_size, vertically_flip_texture, swizzle); } GLuint GLHelper::CopyTextureToImpl::ScaleTexture( GLuint src_texture, const gfx::Size& src_size, const gfx::Rect& src_subrect, const gfx::Size& dst_size, bool vertically_flip_texture, bool swizzle, SkColorType color_type, GLHelper::ScalerQuality quality) { GLuint dst_texture = 0u; gl_->GenTextures(1, &dst_texture); { GLenum format = GL_RGBA, type = GL_UNSIGNED_BYTE; ScopedTextureBinder texture_binder(gl_, dst_texture); // Use GL_RGBA for destination/temporary texture unless we're working with // 16-bit data if (color_type == kRGB_565_SkColorType) { format = GL_RGB; type = GL_UNSIGNED_SHORT_5_6_5; } gl_->TexImage2D(GL_TEXTURE_2D, 0, format, dst_size.width(), dst_size.height(), 0, format, type, NULL); } scoped_ptr scaler( helper_->CreateScaler(quality, src_size, src_subrect, dst_size, vertically_flip_texture, swizzle)); scaler->Scale(src_texture, dst_texture); return dst_texture; } GLuint GLHelper::CopyTextureToImpl::EncodeTextureAsGrayscale( GLuint src_texture, const gfx::Size& src_size, gfx::Size* const encoded_texture_size, bool vertically_flip_texture, bool swizzle) { GLuint dst_texture = 0u; gl_->GenTextures(1, &dst_texture); // The size of the encoded texture. *encoded_texture_size = gfx::Size((src_size.width() + 3) / 4, src_size.height()); { ScopedTextureBinder texture_binder(gl_, dst_texture); gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, encoded_texture_size->width(), encoded_texture_size->height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); } helper_->InitScalerImpl(); scoped_ptr grayscale_scaler( helper_->scaler_impl_.get()->CreatePlanarScaler( src_size, gfx::Rect(0, 0, (src_size.width() + 3) & ~3, src_size.height()), *encoded_texture_size, vertically_flip_texture, swizzle, kRGBtoGrayscaleColorWeights)); grayscale_scaler->Scale(src_texture, dst_texture); return dst_texture; } void GLHelper::CopyTextureToImpl::ReadbackAsync( const gfx::Size& dst_size, int32_t bytes_per_row, int32_t row_stride_bytes, unsigned char* out, GLenum format, GLenum type, size_t bytes_per_pixel, const base::Callback& callback) { TRACE_EVENT0("gpu.capture", "GLHelper::CopyTextureToImpl::ReadbackAsync"); Request* request = new Request(dst_size, bytes_per_row, row_stride_bytes, out, callback); request_queue_.push(request); request->buffer = 0u; gl_->GenBuffers(1, &request->buffer); gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer); gl_->BufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, bytes_per_pixel * dst_size.GetArea(), NULL, GL_STREAM_READ); request->query = 0u; gl_->GenQueriesEXT(1, &request->query); gl_->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, request->query); gl_->ReadPixels(0, 0, dst_size.width(), dst_size.height(), format, type, NULL); gl_->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM); gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); context_support_->SignalQuery( request->query, base::Bind(&CopyTextureToImpl::ReadbackDone, AsWeakPtr(), request, bytes_per_pixel)); } void GLHelper::CopyTextureToImpl::CropScaleReadbackAndCleanTexture( GLuint src_texture, const gfx::Size& src_size, const gfx::Rect& src_subrect, const gfx::Size& dst_size, unsigned char* out, const SkColorType out_color_type, const base::Callback& callback, GLHelper::ScalerQuality quality) { GLenum format, type; size_t bytes_per_pixel; SkColorType readback_color_type = out_color_type; // Single-component textures are not supported by all GPUs, so we implement // kAlpha_8_SkColorType support here via a special encoding (see below) using // a 32-bit texture to represent an 8-bit image. // Thus we use generic 32-bit readback in this case. if (out_color_type == kAlpha_8_SkColorType) { readback_color_type = kRGBA_8888_SkColorType; } FormatSupport supported = GetReadbackConfig(readback_color_type, true, &format, &type, &bytes_per_pixel); if (supported == GLHelperReadbackSupport::NOT_SUPPORTED) { callback.Run(false); return; } GLuint texture = src_texture; // Scale texture if needed // Optimization: SCALER_QUALITY_FAST is just a single bilinear pass, which we // can do just as well in EncodeTextureAsGrayscale, which we will do if // out_color_type is kAlpha_8_SkColorType, so let's skip the scaling step // in that case. bool scale_texture = out_color_type != kAlpha_8_SkColorType || quality != GLHelper::SCALER_QUALITY_FAST; if (scale_texture) { // Don't swizzle during the scale step for kAlpha_8_SkColorType. // We will swizzle in the encode step below if needed. bool scale_swizzle = out_color_type == kAlpha_8_SkColorType ? false : supported == GLHelperReadbackSupport::SWIZZLE; texture = ScaleTexture(src_texture, src_size, src_subrect, dst_size, true, scale_swizzle, out_color_type == kAlpha_8_SkColorType ? kN32_SkColorType : out_color_type, quality); DCHECK(texture); } gfx::Size readback_texture_size = dst_size; // Encode texture to grayscale if needed. if (out_color_type == kAlpha_8_SkColorType) { // Do the vertical flip here if we haven't already done it when we scaled // the texture. bool encode_as_grayscale_vertical_flip = !scale_texture; // EncodeTextureAsGrayscale by default creates a texture which should be // read back as RGBA, so need to swizzle if the readback format is BGRA. bool encode_as_grayscale_swizzle = format == GL_BGRA_EXT; GLuint tmp_texture = EncodeTextureAsGrayscale( texture, dst_size, &readback_texture_size, encode_as_grayscale_vertical_flip, encode_as_grayscale_swizzle); // If the scaled texture was created - delete it if (scale_texture) gl_->DeleteTextures(1, &texture); texture = tmp_texture; DCHECK(texture); } // Readback the pixels of the resulting texture ScopedFramebuffer dst_framebuffer(gl_); ScopedFramebufferBinder framebuffer_binder(gl_, dst_framebuffer); ScopedTextureBinder texture_binder(gl_, texture); gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); int32_t bytes_per_row = out_color_type == kAlpha_8_SkColorType ? dst_size.width() : dst_size.width() * bytes_per_pixel; ReadbackAsync(readback_texture_size, bytes_per_row, bytes_per_row, out, format, type, bytes_per_pixel, callback); gl_->DeleteTextures(1, &texture); } void GLHelper::CopyTextureToImpl::ReadbackTextureSync(GLuint texture, const gfx::Rect& src_rect, unsigned char* out, SkColorType color_type) { GLenum format, type; size_t bytes_per_pixel; FormatSupport supported = GetReadbackConfig(color_type, false, &format, &type, &bytes_per_pixel); if (supported == GLHelperReadbackSupport::NOT_SUPPORTED) { return; } ScopedFramebuffer dst_framebuffer(gl_); ScopedFramebufferBinder framebuffer_binder(gl_, dst_framebuffer); ScopedTextureBinder texture_binder(gl_, texture); gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); gl_->ReadPixels(src_rect.x(), src_rect.y(), src_rect.width(), src_rect.height(), format, type, out); } void GLHelper::CopyTextureToImpl::ReadbackTextureAsync( GLuint texture, const gfx::Size& dst_size, unsigned char* out, SkColorType color_type, const base::Callback& callback) { GLenum format, type; size_t bytes_per_pixel; FormatSupport supported = GetReadbackConfig(color_type, false, &format, &type, &bytes_per_pixel); if (supported == GLHelperReadbackSupport::NOT_SUPPORTED) { callback.Run(false); return; } ScopedFramebuffer dst_framebuffer(gl_); ScopedFramebufferBinder framebuffer_binder(gl_, dst_framebuffer); ScopedTextureBinder texture_binder(gl_, texture); gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); ReadbackAsync(dst_size, dst_size.width() * bytes_per_pixel, dst_size.width() * bytes_per_pixel, out, format, type, bytes_per_pixel, callback); } GLuint GLHelper::CopyTextureToImpl::CopyAndScaleTexture( GLuint src_texture, const gfx::Size& src_size, const gfx::Size& dst_size, bool vertically_flip_texture, GLHelper::ScalerQuality quality) { return ScaleTexture(src_texture, src_size, gfx::Rect(src_size), dst_size, vertically_flip_texture, false, kRGBA_8888_SkColorType, // GL_RGBA quality); } void GLHelper::CopyTextureToImpl::ReadbackDone(Request* finished_request, int bytes_per_pixel) { TRACE_EVENT0("gpu.capture", "GLHelper::CopyTextureToImpl::CheckReadbackFramebufferComplete"); finished_request->done = true; FinishRequestHelper finish_request_helper; // We process transfer requests in the order they were received, regardless // of the order we get the callbacks in. while (!request_queue_.empty()) { Request* request = request_queue_.front(); if (!request->done) { break; } bool result = false; if (request->buffer != 0) { gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer); unsigned char* data = static_cast(gl_->MapBufferCHROMIUM( GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY)); if (data) { result = true; if (request->bytes_per_row == request->size.width() * bytes_per_pixel && request->bytes_per_row == request->row_stride_bytes) { memcpy(request->pixels, data, request->size.GetArea() * bytes_per_pixel); } 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() * bytes_per_pixel; } } gl_->UnmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM); } gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); } FinishRequest(request, result, &finish_request_helper); } } void GLHelper::CopyTextureToImpl::FinishRequest( Request* request, bool result, FinishRequestHelper* finish_request_helper) { TRACE_EVENT0("gpu.capture", "GLHelper::CopyTextureToImpl::FinishRequest"); DCHECK(request_queue_.front() == request); request_queue_.pop(); request->result = result; ScopedFlush flush(gl_); if (request->query != 0) { gl_->DeleteQueriesEXT(1, &request->query); request->query = 0; } if (request->buffer != 0) { gl_->DeleteBuffers(1, &request->buffer); request->buffer = 0; } finish_request_helper->Add(request); } void GLHelper::CopyTextureToImpl::CancelRequests() { FinishRequestHelper finish_request_helper; while (!request_queue_.empty()) { Request* request = request_queue_.front(); FinishRequest(request, false, &finish_request_helper); } } FormatSupport GLHelper::CopyTextureToImpl::GetReadbackConfig( SkColorType color_type, bool can_swizzle, GLenum* format, GLenum* type, size_t* bytes_per_pixel) { return helper_->readback_support_->GetReadbackConfig( color_type, can_swizzle, format, type, bytes_per_pixel); } GLHelper::GLHelper(GLES2Interface* gl, gpu::ContextSupport* context_support) : gl_(gl), context_support_(context_support), readback_support_(new GLHelperReadbackSupport(gl)) {} GLHelper::~GLHelper() {} void GLHelper::CropScaleReadbackAndCleanTexture( GLuint src_texture, const gfx::Size& src_size, const gfx::Rect& src_subrect, const gfx::Size& dst_size, unsigned char* out, const SkColorType out_color_type, const base::Callback& callback, GLHelper::ScalerQuality quality) { InitCopyTextToImpl(); copy_texture_to_impl_->CropScaleReadbackAndCleanTexture( src_texture, src_size, src_subrect, dst_size, out, out_color_type, callback, quality); } void GLHelper::CropScaleReadbackAndCleanMailbox( const gpu::Mailbox& src_mailbox, const gpu::SyncToken& sync_token, const gfx::Size& src_size, const gfx::Rect& src_subrect, const gfx::Size& dst_size, unsigned char* out, const SkColorType out_color_type, const base::Callback& callback, GLHelper::ScalerQuality quality) { GLuint mailbox_texture = ConsumeMailboxToTexture(src_mailbox, sync_token); CropScaleReadbackAndCleanTexture(mailbox_texture, src_size, src_subrect, dst_size, out, out_color_type, callback, quality); gl_->DeleteTextures(1, &mailbox_texture); } void GLHelper::ReadbackTextureSync(GLuint texture, const gfx::Rect& src_rect, unsigned char* out, SkColorType format) { InitCopyTextToImpl(); copy_texture_to_impl_->ReadbackTextureSync(texture, src_rect, out, format); } void GLHelper::ReadbackTextureAsync( GLuint texture, const gfx::Size& dst_size, unsigned char* out, SkColorType color_type, const base::Callback& callback) { InitCopyTextToImpl(); copy_texture_to_impl_->ReadbackTextureAsync(texture, dst_size, out, color_type, callback); } GLuint GLHelper::CopyTexture(GLuint texture, const gfx::Size& size) { InitCopyTextToImpl(); return copy_texture_to_impl_->CopyAndScaleTexture( texture, size, size, false, GLHelper::SCALER_QUALITY_FAST); } GLuint GLHelper::CopyAndScaleTexture(GLuint texture, const gfx::Size& src_size, const gfx::Size& dst_size, bool vertically_flip_texture, ScalerQuality quality) { InitCopyTextToImpl(); return copy_texture_to_impl_->CopyAndScaleTexture( texture, src_size, dst_size, vertically_flip_texture, quality); } GLuint GLHelper::CompileShaderFromSource(const GLchar* source, GLenum type) { GLuint shader = gl_->CreateShader(type); GLint length = strlen(source); gl_->ShaderSource(shader, 1, &source, &length); gl_->CompileShader(shader); GLint compile_status = 0; gl_->GetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); if (!compile_status) { GLint log_length = 0; gl_->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); if (log_length) { scoped_ptr log(new GLchar[log_length]); GLsizei returned_log_length = 0; gl_->GetShaderInfoLog(shader, log_length, &returned_log_length, log.get()); LOG(ERROR) << std::string(log.get(), returned_log_length); } gl_->DeleteShader(shader); return 0; } return shader; } void GLHelper::InitCopyTextToImpl() { // Lazily initialize |copy_texture_to_impl_| if (!copy_texture_to_impl_) copy_texture_to_impl_.reset( new CopyTextureToImpl(gl_, context_support_, this)); } void GLHelper::InitScalerImpl() { // Lazily initialize |scaler_impl_| if (!scaler_impl_) scaler_impl_.reset(new GLHelperScaling(gl_, this)); } GLint GLHelper::MaxDrawBuffers() { InitCopyTextToImpl(); return copy_texture_to_impl_->MaxDrawBuffers(); } void GLHelper::CopySubBufferDamage(GLenum target, GLuint texture, GLuint previous_texture, const SkRegion& new_damage, const SkRegion& old_damage) { SkRegion region(old_damage); if (region.op(new_damage, SkRegion::kDifference_Op)) { ScopedFramebuffer dst_framebuffer(gl_); ScopedFramebufferBinder framebuffer_binder(gl_, dst_framebuffer); gl_->BindTexture(target, texture); gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, previous_texture, 0); for (SkRegion::Iterator it(region); !it.done(); it.next()) { const SkIRect& rect = it.rect(); gl_->CopyTexSubImage2D(target, 0, rect.x(), rect.y(), rect.x(), rect.y(), rect.width(), rect.height()); } gl_->BindTexture(target, 0); gl_->Flush(); } } GLuint GLHelper::CreateTexture() { GLuint texture = 0u; gl_->GenTextures(1, &texture); content::ScopedTextureBinder texture_binder(gl_, texture); gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); return texture; } void GLHelper::DeleteTexture(GLuint texture_id) { gl_->DeleteTextures(1, &texture_id); } void GLHelper::GenerateSyncToken(gpu::SyncToken* sync_token) { const uint64_t fence_sync = gl_->InsertFenceSyncCHROMIUM(); gl_->ShallowFlushCHROMIUM(); gl_->GenSyncTokenCHROMIUM(fence_sync, sync_token->GetData()); } void GLHelper::WaitSyncToken(const gpu::SyncToken& sync_token) { gl_->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); } gpu::MailboxHolder GLHelper::ProduceMailboxHolderFromTexture( GLuint texture_id) { gpu::Mailbox mailbox; gl_->GenMailboxCHROMIUM(mailbox.name); gl_->ProduceTextureDirectCHROMIUM(texture_id, GL_TEXTURE_2D, mailbox.name); gpu::SyncToken sync_token; GenerateSyncToken(&sync_token); return gpu::MailboxHolder(mailbox, sync_token, GL_TEXTURE_2D); } GLuint GLHelper::ConsumeMailboxToTexture(const gpu::Mailbox& mailbox, const gpu::SyncToken& sync_token) { if (mailbox.IsZero()) return 0; if (sync_token.HasData()) WaitSyncToken(sync_token); GLuint texture = gl_->CreateAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); return texture; } void GLHelper::ResizeTexture(GLuint texture, const gfx::Size& size) { content::ScopedTextureBinder texture_binder(gl_, texture); gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width(), size.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); } void GLHelper::CopyTextureSubImage(GLuint texture, const gfx::Rect& rect) { content::ScopedTextureBinder texture_binder(gl_, texture); gl_->CopyTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.x(), rect.y(), rect.width(), rect.height()); } void GLHelper::CopyTextureFullImage(GLuint texture, const gfx::Size& size) { content::ScopedTextureBinder texture_binder(gl_, texture); gl_->CopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, size.width(), size.height(), 0); } void GLHelper::Flush() { gl_->Flush(); } void GLHelper::InsertOrderingBarrier() { gl_->OrderingBarrierCHROMIUM(); } void GLHelper::CopyTextureToImpl::ReadbackPlane( TextureFrameBufferPair* source, const scoped_refptr& target, int plane, int size_shift, const gfx::Rect& paste_rect, ReadbackSwizzle swizzle, const base::Callback& callback) { gl_->BindFramebuffer(GL_FRAMEBUFFER, source->framebuffer()); const size_t offset = target->stride(plane) * (paste_rect.y() >> size_shift) + (paste_rect.x() >> size_shift); ReadbackAsync(source->size(), paste_rect.width() >> size_shift, target->stride(plane), target->data(plane) + offset, (swizzle == kSwizzleBGRA) ? GL_BGRA_EXT : GL_RGBA, GL_UNSIGNED_BYTE, 4, callback); } 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}; const float GLHelper::CopyTextureToImpl::kRGBtoGrayscaleColorWeights[] = { 0.213f, 0.715f, 0.072f, 0.0f}; // 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( GLES2Interface* gl, CopyTextureToImpl* copy_impl, GLHelperScaling* scaler_impl, GLHelper::ScalerQuality quality, const gfx::Size& src_size, const gfx::Rect& src_subrect, const gfx::Size& dst_size, bool flip_vertically, ReadbackSwizzle swizzle) : gl_(gl), copy_impl_(copy_impl), dst_size_(dst_size), swizzle_(swizzle), scaler_(gl, scaler_impl->CreateScaler(quality, src_size, src_subrect, dst_size, flip_vertically, false)), y_(gl, scaler_impl->CreatePlanarScaler( dst_size, gfx::Rect(0, 0, (dst_size.width() + 3) & ~3, dst_size.height()), gfx::Size((dst_size.width() + 3) / 4, dst_size.height()), false, (swizzle == kSwizzleBGRA), kRGBtoYColorWeights)), u_(gl, scaler_impl->CreatePlanarScaler( dst_size, gfx::Rect(0, 0, (dst_size.width() + 7) & ~7, (dst_size.height() + 1) & ~1), gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2), false, (swizzle == kSwizzleBGRA), kRGBtoUColorWeights)), v_(gl, scaler_impl->CreatePlanarScaler( dst_size, gfx::Rect(0, 0, (dst_size.width() + 7) & ~7, (dst_size.height() + 1) & ~1), gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2), false, (swizzle == kSwizzleBGRA), kRGBtoVColorWeights)) { DCHECK(!(dst_size.width() & 1)); DCHECK(!(dst_size.height() & 1)); } static void CallbackKeepingVideoFrameAlive( scoped_refptr video_frame, const base::Callback& callback, bool success) { callback.Run(success); } void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUV( const gpu::Mailbox& mailbox, const gpu::SyncToken& sync_token, const scoped_refptr& target, const gfx::Point& paste_location, const base::Callback& callback) { DCHECK(!(paste_location.x() & 1)); DCHECK(!(paste_location.y() & 1)); GLuint mailbox_texture = copy_impl_->ConsumeMailboxToTexture(mailbox, sync_token); // Scale texture to right size. scaler_.Scale(mailbox_texture); gl_->DeleteTextures(1, &mailbox_texture); // Convert the scaled texture in to Y, U and V planes. y_.Scale(scaler_.texture()); u_.Scale(scaler_.texture()); v_.Scale(scaler_.texture()); const gfx::Rect paste_rect(paste_location, dst_size_); if (!target->visible_rect().Contains(paste_rect)) { LOG(DFATAL) << "Paste rect not inside VideoFrame's visible rect!"; callback.Run(false); return; } // Read back planes, one at a time. Keep the video frame alive while doing the // readback. copy_impl_->ReadbackPlane(y_.texture_and_framebuffer(), target, media::VideoFrame::kYPlane, 0, paste_rect, swizzle_, base::Bind(&nullcallback)); copy_impl_->ReadbackPlane(u_.texture_and_framebuffer(), target, media::VideoFrame::kUPlane, 1, paste_rect, swizzle_, base::Bind(&nullcallback)); copy_impl_->ReadbackPlane( v_.texture_and_framebuffer(), target, media::VideoFrame::kVPlane, 1, paste_rect, swizzle_, base::Bind(&CallbackKeepingVideoFrameAlive, target, callback)); gl_->BindFramebuffer(GL_FRAMEBUFFER, 0); media::LetterboxYUV(target.get(), paste_rect); } // YUV readback constructors. Initiates the main scaler pipeline and // one planar scaler for each of the Y, U and V planes. GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV_MRT( GLES2Interface* gl, CopyTextureToImpl* copy_impl, GLHelperScaling* scaler_impl, GLHelper::ScalerQuality quality, const gfx::Size& src_size, const gfx::Rect& src_subrect, const gfx::Size& dst_size, bool flip_vertically, ReadbackSwizzle swizzle) : gl_(gl), copy_impl_(copy_impl), dst_size_(dst_size), quality_(quality), swizzle_(swizzle), scaler_(gl, scaler_impl->CreateScaler(quality, src_size, src_subrect, dst_size, false, false)), pass1_shader_(scaler_impl->CreateYuvMrtShader( dst_size, gfx::Rect(0, 0, (dst_size.width() + 3) & ~3, dst_size.height()), gfx::Size((dst_size.width() + 3) / 4, dst_size.height()), flip_vertically, (swizzle == kSwizzleBGRA), GLHelperScaling::SHADER_YUV_MRT_PASS1)), pass2_shader_(scaler_impl->CreateYuvMrtShader( gfx::Size((dst_size.width() + 3) / 4, dst_size.height()), gfx::Rect(0, 0, (dst_size.width() + 7) / 8 * 2, dst_size.height()), gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2), false, (swizzle == kSwizzleBGRA), GLHelperScaling::SHADER_YUV_MRT_PASS2)), y_(gl, gfx::Size((dst_size.width() + 3) / 4, dst_size.height())), uv_(gl), u_(gl, gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2)), v_(gl, gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2)) { DCHECK(!(dst_size.width() & 1)); DCHECK(!(dst_size.height() & 1)); content::ScopedTextureBinder texture_binder(gl, uv_); gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (dst_size.width() + 3) / 4, dst_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); } void GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV( const gpu::Mailbox& mailbox, const gpu::SyncToken& sync_token, const scoped_refptr& target, const gfx::Point& paste_location, const base::Callback& callback) { DCHECK(!(paste_location.x() & 1)); DCHECK(!(paste_location.y() & 1)); GLuint mailbox_texture = copy_impl_->ConsumeMailboxToTexture(mailbox, sync_token); GLuint texture; if (quality_ == GLHelper::SCALER_QUALITY_FAST) { // Optimization: SCALER_QUALITY_FAST is just a single bilinear // pass, which pass1_shader_ can do just as well, so let's skip // the actual scaling in that case. texture = mailbox_texture; } else { // Scale texture to right size. scaler_.Scale(mailbox_texture); texture = scaler_.texture(); } std::vector outputs(2); // Convert the scaled texture in to Y, U and V planes. outputs[0] = y_.texture(); outputs[1] = uv_; pass1_shader_->Execute(texture, outputs); gl_->DeleteTextures(1, &mailbox_texture); outputs[0] = u_.texture(); outputs[1] = v_.texture(); pass2_shader_->Execute(uv_, outputs); const gfx::Rect paste_rect(paste_location, dst_size_); if (!target->visible_rect().Contains(paste_rect)) { LOG(DFATAL) << "Paste rect not inside VideoFrame's visible rect!"; callback.Run(false); return; } // Read back planes, one at a time. copy_impl_->ReadbackPlane(&y_, target, media::VideoFrame::kYPlane, 0, paste_rect, swizzle_, base::Bind(&nullcallback)); copy_impl_->ReadbackPlane(&u_, target, media::VideoFrame::kUPlane, 1, paste_rect, swizzle_, base::Bind(&nullcallback)); copy_impl_->ReadbackPlane( &v_, target, media::VideoFrame::kVPlane, 1, paste_rect, swizzle_, base::Bind(&CallbackKeepingVideoFrameAlive, target, callback)); gl_->BindFramebuffer(GL_FRAMEBUFFER, 0); media::LetterboxYUV(target.get(), paste_rect); } bool GLHelper::IsReadbackConfigSupported(SkColorType color_type) { DCHECK(readback_support_.get()); GLenum format, type; size_t bytes_per_pixel; FormatSupport support = readback_support_->GetReadbackConfig( color_type, false, &format, &type, &bytes_per_pixel); return (support == GLHelperReadbackSupport::SUPPORTED); } ReadbackYUVInterface* GLHelper::CopyTextureToImpl::CreateReadbackPipelineYUV( GLHelper::ScalerQuality quality, const gfx::Size& src_size, const gfx::Rect& src_subrect, const gfx::Size& dst_size, bool flip_vertically, bool use_mrt) { helper_->InitScalerImpl(); // Just query if the best readback configuration needs a swizzle In // ReadbackPlane() we will choose GL_RGBA/GL_BGRA_EXT based on swizzle GLenum format, type; size_t bytes_per_pixel; FormatSupport supported = GetReadbackConfig(kRGBA_8888_SkColorType, true, &format, &type, &bytes_per_pixel); DCHECK((format == GL_RGBA || format == GL_BGRA_EXT) && type == GL_UNSIGNED_BYTE); ReadbackSwizzle swizzle = kSwizzleNone; if (supported == GLHelperReadbackSupport::SWIZZLE) swizzle = kSwizzleBGRA; if (max_draw_buffers_ >= 2 && use_mrt) { return new ReadbackYUV_MRT(gl_, this, helper_->scaler_impl_.get(), quality, src_size, src_subrect, dst_size, flip_vertically, swizzle); } return new ReadbackYUVImpl(gl_, this, helper_->scaler_impl_.get(), quality, src_size, src_subrect, dst_size, flip_vertically, swizzle); } ReadbackYUVInterface* GLHelper::CreateReadbackPipelineYUV( ScalerQuality quality, const gfx::Size& src_size, const gfx::Rect& src_subrect, const gfx::Size& dst_size, bool flip_vertically, bool use_mrt) { InitCopyTextToImpl(); return copy_texture_to_impl_->CreateReadbackPipelineYUV( quality, src_size, src_subrect, dst_size, flip_vertically, use_mrt); } } // namespace content