Home | History | Annotate | Download | only in renderer_host
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/browser/renderer_host/compositing_iosurface_mac.h"
      6 
      7 #include <OpenGL/CGLRenderers.h>
      8 #include <OpenGL/OpenGL.h>
      9 
     10 #include "base/bind.h"
     11 #include "base/bind_helpers.h"
     12 #include "base/command_line.h"
     13 #include "base/debug/trace_event.h"
     14 #include "base/logging.h"
     15 #include "base/mac/mac_util.h"
     16 #include "base/message_loop/message_loop.h"
     17 #include "base/threading/platform_thread.h"
     18 #include "content/browser/renderer_host/compositing_iosurface_context_mac.h"
     19 #include "content/browser/renderer_host/compositing_iosurface_shader_programs_mac.h"
     20 #include "content/browser/renderer_host/compositing_iosurface_transformer_mac.h"
     21 #include "content/browser/renderer_host/render_widget_host_impl.h"
     22 #include "content/browser/renderer_host/render_widget_host_view_mac.h"
     23 #include "content/common/content_constants_internal.h"
     24 #include "content/port/browser/render_widget_host_view_frame_subscriber.h"
     25 #include "gpu/command_buffer/service/gpu_switches.h"
     26 #include "media/base/video_util.h"
     27 #include "third_party/skia/include/core/SkBitmap.h"
     28 #include "ui/gfx/rect.h"
     29 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
     30 #include "ui/gfx/size_conversions.h"
     31 #include "ui/gl/gl_context.h"
     32 #include "ui/gl/io_surface_support_mac.h"
     33 
     34 #ifdef NDEBUG
     35 #define CHECK_GL_ERROR()
     36 #define CHECK_AND_SAVE_GL_ERROR()
     37 #else
     38 #define CHECK_GL_ERROR() do {                                           \
     39     GLenum gl_error = glGetError();                                     \
     40     LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error :" << gl_error; \
     41   } while (0)
     42 #define CHECK_AND_SAVE_GL_ERROR() do {                                  \
     43     GLenum gl_error = GetAndSaveGLError();                              \
     44     LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error :" << gl_error; \
     45   } while (0)
     46 #endif
     47 
     48 namespace content {
     49 namespace {
     50 
     51 // How many times to test if asynchronous copy has completed.
     52 // This value is chosen such that we allow at most 1 second to finish a copy.
     53 const int kFinishCopyRetryCycles = 100;
     54 
     55 // Time in milliseconds to allow asynchronous copy to finish.
     56 // This value is shorter than 16ms such that copy can complete within a vsync.
     57 const int kFinishCopyPollingPeriodMs = 10;
     58 
     59 bool HasAppleFenceExtension() {
     60   static bool initialized_has_fence = false;
     61   static bool has_fence = false;
     62 
     63   if (!initialized_has_fence) {
     64     has_fence =
     65         strstr(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)),
     66                "GL_APPLE_fence") != NULL;
     67     initialized_has_fence = true;
     68   }
     69   return has_fence;
     70 }
     71 
     72 bool HasPixelBufferObjectExtension() {
     73   static bool initialized_has_pbo = false;
     74   static bool has_pbo = false;
     75 
     76   if (!initialized_has_pbo) {
     77     has_pbo =
     78         strstr(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)),
     79                "GL_ARB_pixel_buffer_object") != NULL;
     80     initialized_has_pbo = true;
     81   }
     82   return has_pbo;
     83 }
     84 
     85 // Helper function to reverse the argument order.  Also takes ownership of
     86 // |bitmap_output| for the life of the binding.
     87 void ReverseArgumentOrder(
     88     const base::Callback<void(bool, const SkBitmap&)>& callback,
     89     scoped_ptr<SkBitmap> bitmap_output, bool success) {
     90   callback.Run(success, *bitmap_output);
     91 }
     92 
     93 // Called during an async GPU readback with a pointer to the pixel buffer.  In
     94 // the snapshot path, we just memcpy the data into our output bitmap since the
     95 // width, height, and stride should all be equal.
     96 bool MapBufferToSkBitmap(const SkBitmap* output, const void* buf, int ignored) {
     97   TRACE_EVENT0("browser", "MapBufferToSkBitmap");
     98 
     99   if (buf) {
    100     SkAutoLockPixels output_lock(*output);
    101     memcpy(output->getPixels(), buf, output->getSize());
    102   }
    103   return buf != NULL;
    104 }
    105 
    106 // Copies tightly-packed scanlines from |buf| to |region_in_frame| in the given
    107 // |target| VideoFrame's |plane|.  Assumption: |buf|'s width is
    108 // |region_in_frame.width()| and its stride is always in 4-byte alignment.
    109 //
    110 // TODO(miu): Refactor by moving this function into media/video_util.
    111 // http://crbug.com/219779
    112 bool MapBufferToVideoFrame(
    113     const scoped_refptr<media::VideoFrame>& target,
    114     const gfx::Rect& region_in_frame,
    115     const void* buf,
    116     int plane) {
    117   COMPILE_ASSERT(media::VideoFrame::kYPlane == 0, VideoFrame_kYPlane_mismatch);
    118   COMPILE_ASSERT(media::VideoFrame::kUPlane == 1, VideoFrame_kUPlane_mismatch);
    119   COMPILE_ASSERT(media::VideoFrame::kVPlane == 2, VideoFrame_kVPlane_mismatch);
    120 
    121   TRACE_EVENT1("browser", "MapBufferToVideoFrame", "plane", plane);
    122 
    123   // Apply black-out in the regions surrounding the view area (for
    124   // letterboxing/pillarboxing).  Only do this once, since this is performed on
    125   // all planes in the VideoFrame here.
    126   if (plane == 0)
    127     media::LetterboxYUV(target.get(), region_in_frame);
    128 
    129   if (buf) {
    130     int packed_width = region_in_frame.width();
    131     int packed_height = region_in_frame.height();
    132     // For planes 1 and 2, the width and height are 1/2 size (rounded up).
    133     if (plane > 0) {
    134       packed_width = (packed_width + 1) / 2;
    135       packed_height = (packed_height + 1) / 2;
    136     }
    137     const uint8* src = reinterpret_cast<const uint8*>(buf);
    138     const int src_stride = (packed_width % 4 == 0 ?
    139                                 packed_width :
    140                                 (packed_width + 4 - (packed_width % 4)));
    141     const uint8* const src_end = src + packed_height * src_stride;
    142 
    143     // Calculate starting offset and stride into the destination buffer.
    144     const int dst_stride = target->stride(plane);
    145     uint8* dst = target->data(plane);
    146     if (plane == 0)
    147       dst += (region_in_frame.y() * dst_stride) + region_in_frame.x();
    148     else
    149       dst += (region_in_frame.y() / 2 * dst_stride) + (region_in_frame.x() / 2);
    150 
    151     // Copy each row, accounting for strides in the source and destination.
    152     for (; src < src_end; src += src_stride, dst += dst_stride)
    153       memcpy(dst, src, packed_width);
    154   }
    155   return buf != NULL;
    156 }
    157 
    158 }  // namespace
    159 
    160 CVReturn DisplayLinkCallback(CVDisplayLinkRef display_link,
    161                              const CVTimeStamp* now,
    162                              const CVTimeStamp* output_time,
    163                              CVOptionFlags flags_in,
    164                              CVOptionFlags* flags_out,
    165                              void* context) {
    166   CompositingIOSurfaceMac* surface =
    167       static_cast<CompositingIOSurfaceMac*>(context);
    168   surface->DisplayLinkTick(display_link, output_time);
    169   return kCVReturnSuccess;
    170 }
    171 
    172 CompositingIOSurfaceMac::CopyContext::CopyContext(
    173     const scoped_refptr<CompositingIOSurfaceContext>& context)
    174   : transformer(new CompositingIOSurfaceTransformer(
    175         GL_TEXTURE_RECTANGLE_ARB, true, context->shader_program_cache())),
    176     output_readback_format(GL_BGRA),
    177     num_outputs(0),
    178     fence(0),
    179     cycles_elapsed(0) {
    180   memset(output_textures, 0, sizeof(output_textures));
    181   memset(frame_buffers, 0, sizeof(frame_buffers));
    182   memset(pixel_buffers, 0, sizeof(pixel_buffers));
    183 }
    184 
    185 CompositingIOSurfaceMac::CopyContext::~CopyContext() {
    186   DCHECK_EQ(frame_buffers[0], 0u) << "Failed to call ReleaseCachedGLObjects().";
    187 }
    188 
    189 void CompositingIOSurfaceMac::CopyContext::ReleaseCachedGLObjects() {
    190   // No outstanding callbacks should be pending.
    191   DCHECK(map_buffer_callback.is_null());
    192   DCHECK(done_callback.is_null());
    193 
    194   // For an asynchronous read-back, there are more objects to delete:
    195   if (fence) {
    196     glDeleteBuffers(arraysize(pixel_buffers), pixel_buffers); CHECK_GL_ERROR();
    197     memset(pixel_buffers, 0, sizeof(pixel_buffers));
    198     glDeleteFencesAPPLE(1, &fence); CHECK_GL_ERROR();
    199     fence = 0;
    200   }
    201 
    202   glDeleteFramebuffersEXT(arraysize(frame_buffers), frame_buffers);
    203   CHECK_GL_ERROR();
    204   memset(frame_buffers, 0, sizeof(frame_buffers));
    205 
    206   // Note: |output_textures| are owned by the transformer.
    207   if (transformer)
    208     transformer->ReleaseCachedGLObjects();
    209 }
    210 
    211 void CompositingIOSurfaceMac::CopyContext::PrepareReadbackFramebuffers() {
    212   for (int i = 0; i < num_outputs; ++i) {
    213     if (!frame_buffers[i]) {
    214       glGenFramebuffersEXT(1, &frame_buffers[i]); CHECK_GL_ERROR();
    215     }
    216   }
    217 }
    218 
    219 void CompositingIOSurfaceMac::CopyContext::PrepareForAsynchronousReadback() {
    220   PrepareReadbackFramebuffers();
    221   if (!fence) {
    222     glGenFencesAPPLE(1, &fence); CHECK_GL_ERROR();
    223   }
    224   for (int i = 0; i < num_outputs; ++i) {
    225     if (!pixel_buffers[i]) {
    226       glGenBuffersARB(1, &pixel_buffers[i]); CHECK_GL_ERROR();
    227     }
    228   }
    229 }
    230 
    231 
    232 // static
    233 CompositingIOSurfaceMac* CompositingIOSurfaceMac::Create() {
    234   IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize();
    235   if (!io_surface_support) {
    236     LOG(ERROR) << "No IOSurface support";
    237     return NULL;
    238   }
    239 
    240   scoped_refptr<CompositingIOSurfaceContext> offscreen_context =
    241       CompositingIOSurfaceContext::Get(
    242           CompositingIOSurfaceContext::kOffscreenContextWindowNumber);
    243   if (!offscreen_context) {
    244     LOG(ERROR) << "Failed to create context for offscreen operations";
    245     return NULL;
    246   }
    247 
    248   return new CompositingIOSurfaceMac(io_surface_support,
    249                                      offscreen_context);
    250 }
    251 
    252 CompositingIOSurfaceMac::CompositingIOSurfaceMac(
    253     IOSurfaceSupport* io_surface_support,
    254     const scoped_refptr<CompositingIOSurfaceContext>& offscreen_context)
    255     : io_surface_support_(io_surface_support),
    256       offscreen_context_(offscreen_context),
    257       io_surface_handle_(0),
    258       scale_factor_(1.f),
    259       texture_(0),
    260       finish_copy_timer_(
    261           FROM_HERE,
    262           base::TimeDelta::FromMilliseconds(kFinishCopyPollingPeriodMs),
    263           base::Bind(&CompositingIOSurfaceMac::CheckIfAllCopiesAreFinished,
    264                      base::Unretained(this),
    265                      false),
    266           true),
    267       display_link_(0),
    268       display_link_stop_timer_(FROM_HERE, base::TimeDelta::FromSeconds(1),
    269                                this, &CompositingIOSurfaceMac::StopDisplayLink),
    270       vsync_interval_numerator_(0),
    271       vsync_interval_denominator_(0),
    272       gl_error_(GL_NO_ERROR) {
    273   CHECK(offscreen_context_);
    274 }
    275 
    276 void CompositingIOSurfaceMac::SetupCVDisplayLink() {
    277   if (display_link_) {
    278     LOG(ERROR) << "DisplayLink already setup";
    279     return;
    280   }
    281 
    282   CVDisplayLinkRef display_link;
    283   CVReturn ret = CVDisplayLinkCreateWithActiveCGDisplays(&display_link);
    284   if (ret != kCVReturnSuccess) {
    285     LOG(WARNING) << "CVDisplayLinkCreateWithActiveCGDisplays failed: " << ret;
    286     return;
    287   }
    288 
    289   display_link_ = display_link;
    290 
    291   ret = CVDisplayLinkSetOutputCallback(display_link_,
    292                                        &DisplayLinkCallback, this);
    293   DCHECK(ret == kCVReturnSuccess)
    294       << "CVDisplayLinkSetOutputCallback failed: " << ret;
    295 
    296   StartOrContinueDisplayLink();
    297 
    298   CVTimeStamp cv_time;
    299   ret = CVDisplayLinkGetCurrentTime(display_link_, &cv_time);
    300   DCHECK(ret == kCVReturnSuccess)
    301       << "CVDisplayLinkGetCurrentTime failed: " << ret;
    302 
    303   {
    304     base::AutoLock lock(lock_);
    305     CalculateVsyncParametersLockHeld(&cv_time);
    306   }
    307 
    308   // Stop display link for now, it will be started when needed during Draw.
    309   StopDisplayLink();
    310 }
    311 
    312 void CompositingIOSurfaceMac::GetVSyncParameters(base::TimeTicks* timebase,
    313                                                  uint32* interval_numerator,
    314                                                  uint32* interval_denominator) {
    315   base::AutoLock lock(lock_);
    316   *timebase = vsync_timebase_;
    317   *interval_numerator = vsync_interval_numerator_;
    318   *interval_denominator = vsync_interval_denominator_;
    319 }
    320 
    321 CompositingIOSurfaceMac::~CompositingIOSurfaceMac() {
    322   FailAllCopies();
    323   CVDisplayLinkRelease(display_link_);
    324   CGLSetCurrentContext(offscreen_context_->cgl_context());
    325   DestroyAllCopyContextsWithinContext();
    326   UnrefIOSurfaceWithContextCurrent();
    327   CGLSetCurrentContext(0);
    328   offscreen_context_ = NULL;
    329 }
    330 
    331 bool CompositingIOSurfaceMac::SetIOSurfaceWithContextCurrent(
    332     scoped_refptr<CompositingIOSurfaceContext> current_context,
    333     uint64 io_surface_handle,
    334     const gfx::Size& size,
    335     float scale_factor,
    336     const ui::LatencyInfo& latency_info) {
    337   pixel_io_surface_size_ = size;
    338   scale_factor_ = scale_factor;
    339   dip_io_surface_size_ = gfx::ToFlooredSize(
    340       gfx::ScaleSize(pixel_io_surface_size_, 1.0 / scale_factor_));
    341   bool result = MapIOSurfaceToTextureWithContextCurrent(
    342       current_context, io_surface_handle);
    343   latency_info_.MergeWith(latency_info);
    344   return result;
    345 }
    346 
    347 int CompositingIOSurfaceMac::GetRendererID() {
    348   GLint current_renderer_id = -1;
    349   if (CGLGetParameter(offscreen_context_->cgl_context(),
    350                       kCGLCPCurrentRendererID,
    351                       &current_renderer_id) == kCGLNoError)
    352     return current_renderer_id & kCGLRendererIDMatchingMask;
    353   return -1;
    354 }
    355 
    356 bool CompositingIOSurfaceMac::DrawIOSurface(
    357     scoped_refptr<CompositingIOSurfaceContext> drawing_context,
    358     const gfx::Rect& window_rect,
    359     float window_scale_factor,
    360     bool flush_drawable) {
    361   DCHECK_EQ(CGLGetCurrentContext(), drawing_context->cgl_context());
    362 
    363   if (display_link_ == NULL)
    364     SetupCVDisplayLink();
    365 
    366   bool has_io_surface = HasIOSurface();
    367   TRACE_EVENT1("browser", "CompositingIOSurfaceMac::DrawIOSurface",
    368                "has_io_surface", has_io_surface);
    369 
    370   gfx::Rect pixel_window_rect =
    371       ToNearestRect(gfx::ScaleRect(window_rect, window_scale_factor));
    372   glViewport(
    373       pixel_window_rect.x(), pixel_window_rect.y(),
    374       pixel_window_rect.width(), pixel_window_rect.height());
    375 
    376   SurfaceQuad quad;
    377   quad.set_size(dip_io_surface_size_, pixel_io_surface_size_);
    378 
    379   glMatrixMode(GL_PROJECTION);
    380   glLoadIdentity();
    381 
    382   // Note that the projection keeps things in view units, so the use of
    383   // window_rect / dip_io_surface_size_ (as opposed to the pixel_ variants)
    384   // below is correct.
    385   glOrtho(0, window_rect.width(), window_rect.height(), 0, -1, 1);
    386   glMatrixMode(GL_MODELVIEW);
    387   glLoadIdentity();
    388 
    389   glDisable(GL_DEPTH_TEST);
    390   glDisable(GL_BLEND);
    391 
    392   if (has_io_surface) {
    393     drawing_context->shader_program_cache()->UseBlitProgram();
    394     glActiveTexture(GL_TEXTURE0);
    395     glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_);
    396 
    397     DrawQuad(quad);
    398 
    399     glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); CHECK_AND_SAVE_GL_ERROR();
    400 
    401     // Fill the resize gutters with white.
    402     if (window_rect.width() > dip_io_surface_size_.width() ||
    403         window_rect.height() > dip_io_surface_size_.height()) {
    404       drawing_context->shader_program_cache()->UseSolidWhiteProgram();
    405       SurfaceQuad filler_quad;
    406       if (window_rect.width() > dip_io_surface_size_.width()) {
    407         // Draw right-side gutter down to the bottom of the window.
    408         filler_quad.set_rect(dip_io_surface_size_.width(), 0.0f,
    409                              window_rect.width(), window_rect.height());
    410         DrawQuad(filler_quad);
    411       }
    412       if (window_rect.height() > dip_io_surface_size_.height()) {
    413         // Draw bottom gutter to the width of the IOSurface.
    414         filler_quad.set_rect(
    415             0.0f, dip_io_surface_size_.height(),
    416             dip_io_surface_size_.width(), window_rect.height());
    417         DrawQuad(filler_quad);
    418       }
    419     }
    420 
    421     // Workaround for issue 158469. Issue a dummy draw call with texture_ not
    422     // bound to blit_rgb_sampler_location_, in order to shake all references
    423     // to the IOSurface out of the driver.
    424     glBegin(GL_TRIANGLES);
    425     glEnd();
    426 
    427     glUseProgram(0); CHECK_AND_SAVE_GL_ERROR();
    428   } else {
    429     // Should match the clear color of RenderWidgetHostViewMac.
    430     glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    431     glClear(GL_COLOR_BUFFER_BIT);
    432   }
    433 
    434   static bool initialized_workaround = false;
    435   static bool force_on_workaround = false;
    436   static bool force_off_workaround = false;
    437   if (!initialized_workaround) {
    438     force_on_workaround = CommandLine::ForCurrentProcess()->HasSwitch(
    439         switches::kForceGLFinishWorkaround);
    440     force_off_workaround = CommandLine::ForCurrentProcess()->HasSwitch(
    441         switches::kDisableGpuDriverBugWorkarounds);
    442 
    443     initialized_workaround = true;
    444   }
    445 
    446   const bool workaround_needed =
    447       drawing_context->IsVendorIntel() &&
    448       (!base::mac::IsOSMountainLionOrLater() || base::mac::IsOSMavericks());
    449   const bool use_glfinish_workaround =
    450       (workaround_needed || force_on_workaround) && !force_off_workaround;
    451 
    452   if (use_glfinish_workaround) {
    453     TRACE_EVENT0("gpu", "glFinish");
    454     // http://crbug.com/123409 : work around bugs in graphics driver on
    455     // MacBook Air with Intel HD graphics, and possibly on other models,
    456     // by forcing the graphics pipeline to be completely drained at this
    457     // point. This workaround is not necessary on Mountain Lion.
    458     // http://crbug.com/318877 : work around a bug where the window does
    459     // not finish rendering its contents before displaying them on Mavericks
    460     // on Retina MacBook Pro when using the Intel HD graphics GPU.
    461     glFinish();
    462   }
    463 
    464   bool result = true;
    465   if (flush_drawable) {
    466     CGLError cgl_error =  CGLFlushDrawable(drawing_context->cgl_context());
    467     if (cgl_error != kCGLNoError) {
    468       LOG(ERROR) << "CGLFlushDrawable error in DrawIOSurface: " << cgl_error;
    469       result = false;
    470     }
    471   }
    472 
    473   // Check if any of the drawing calls result in an error.
    474   GetAndSaveGLError();
    475   if (gl_error_ != GL_NO_ERROR) {
    476     LOG(ERROR) << "GL error in DrawIOSurface: " << gl_error_;
    477     result = false;
    478   }
    479 
    480   latency_info_.AddLatencyNumber(
    481       ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
    482   RenderWidgetHostImpl::CompositorFrameDrawn(latency_info_);
    483   latency_info_.Clear();
    484 
    485   // Try to finish previous copy requests after flush to get better pipelining.
    486   CheckIfAllCopiesAreFinished(false);
    487 
    488   StartOrContinueDisplayLink();
    489 
    490   return result;
    491 }
    492 
    493 void CompositingIOSurfaceMac::CopyTo(
    494       const gfx::Rect& src_pixel_subrect,
    495       const gfx::Size& dst_pixel_size,
    496       const base::Callback<void(bool, const SkBitmap&)>& callback) {
    497   scoped_ptr<SkBitmap> output(new SkBitmap());
    498   output->setConfig(SkBitmap::kARGB_8888_Config,
    499                     dst_pixel_size.width(),
    500                     dst_pixel_size.height(),
    501                     0,
    502                     kOpaque_SkAlphaType);
    503 
    504   if (!output->allocPixels()) {
    505     DLOG(ERROR) << "Failed to allocate SkBitmap pixels!";
    506     callback.Run(false, *output);
    507     return;
    508   }
    509   DCHECK_EQ(output->rowBytesAsPixels(), dst_pixel_size.width())
    510       << "Stride is required to be equal to width for GPU readback.";
    511 
    512   CGLSetCurrentContext(offscreen_context_->cgl_context());
    513   const base::Closure copy_done_callback = CopyToSelectedOutputWithinContext(
    514       src_pixel_subrect, gfx::Rect(dst_pixel_size), false,
    515       output.get(), NULL,
    516       base::Bind(&ReverseArgumentOrder, callback, base::Passed(&output)));
    517   CGLSetCurrentContext(0);
    518   if (!copy_done_callback.is_null())
    519     copy_done_callback.Run();
    520 }
    521 
    522 void CompositingIOSurfaceMac::CopyToVideoFrame(
    523     const gfx::Rect& src_pixel_subrect,
    524     const scoped_refptr<media::VideoFrame>& target,
    525     const base::Callback<void(bool)>& callback) {
    526   CGLSetCurrentContext(offscreen_context_->cgl_context());
    527   const base::Closure copy_done_callback = CopyToVideoFrameWithinContext(
    528       src_pixel_subrect, false, target, callback);
    529   CGLSetCurrentContext(0);
    530   if (!copy_done_callback.is_null())
    531     copy_done_callback.Run();
    532 }
    533 
    534 base::Closure CompositingIOSurfaceMac::CopyToVideoFrameWithinContext(
    535     const gfx::Rect& src_pixel_subrect,
    536     bool called_within_draw,
    537     const scoped_refptr<media::VideoFrame>& target,
    538     const base::Callback<void(bool)>& callback) {
    539   gfx::Rect region_in_frame = media::ComputeLetterboxRegion(
    540       gfx::Rect(target->coded_size()), src_pixel_subrect.size());
    541   // Make coordinates and sizes even because we letterbox in YUV space right
    542   // now (see CopyRGBToVideoFrame). They need to be even for the UV samples to
    543   // line up correctly.
    544   region_in_frame = gfx::Rect(region_in_frame.x() & ~1,
    545                               region_in_frame.y() & ~1,
    546                               region_in_frame.width() & ~1,
    547                               region_in_frame.height() & ~1);
    548   DCHECK_LE(region_in_frame.right(), target->coded_size().width());
    549   DCHECK_LE(region_in_frame.bottom(), target->coded_size().height());
    550 
    551   return CopyToSelectedOutputWithinContext(
    552       src_pixel_subrect, region_in_frame, called_within_draw,
    553       NULL, target, callback);
    554 }
    555 
    556 bool CompositingIOSurfaceMac::MapIOSurfaceToTextureWithContextCurrent(
    557     const scoped_refptr<CompositingIOSurfaceContext>& current_context,
    558     uint64 io_surface_handle) {
    559   if (io_surface_.get() && io_surface_handle == io_surface_handle_)
    560     return true;
    561 
    562   TRACE_EVENT0("browser", "CompositingIOSurfaceMac::MapIOSurfaceToTexture");
    563   UnrefIOSurfaceWithContextCurrent();
    564 
    565   io_surface_.reset(io_surface_support_->IOSurfaceLookup(
    566       static_cast<uint32>(io_surface_handle)));
    567   // Can fail if IOSurface with that ID was already released by the gpu
    568   // process.
    569   if (!io_surface_) {
    570     UnrefIOSurfaceWithContextCurrent();
    571     return false;
    572   }
    573 
    574   io_surface_handle_ = io_surface_handle;
    575 
    576   // Actual IOSurface size is rounded up to reduce reallocations during window
    577   // resize. Get the actual size to properly map the texture.
    578   gfx::Size rounded_size(
    579       io_surface_support_->IOSurfaceGetWidth(io_surface_),
    580       io_surface_support_->IOSurfaceGetHeight(io_surface_));
    581 
    582   glGenTextures(1, &texture_);
    583   glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_);
    584   glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    585   glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    586   CHECK_AND_SAVE_GL_ERROR();
    587   GLuint plane = 0;
    588   CGLError cgl_error = io_surface_support_->CGLTexImageIOSurface2D(
    589       current_context->cgl_context(),
    590       GL_TEXTURE_RECTANGLE_ARB,
    591       GL_RGBA,
    592       rounded_size.width(),
    593       rounded_size.height(),
    594       GL_BGRA,
    595       GL_UNSIGNED_INT_8_8_8_8_REV,
    596       io_surface_.get(),
    597       plane);
    598   glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
    599   if (cgl_error != kCGLNoError) {
    600     LOG(ERROR) << "CGLTexImageIOSurface2D: " << cgl_error;
    601     UnrefIOSurfaceWithContextCurrent();
    602     return false;
    603   }
    604   GetAndSaveGLError();
    605   if (gl_error_ != GL_NO_ERROR) {
    606     LOG(ERROR) << "GL error in MapIOSurfaceToTexture: " << gl_error_;
    607     UnrefIOSurfaceWithContextCurrent();
    608     return false;
    609   }
    610   return true;
    611 }
    612 
    613 void CompositingIOSurfaceMac::UnrefIOSurface() {
    614   CGLSetCurrentContext(offscreen_context_->cgl_context());
    615   UnrefIOSurfaceWithContextCurrent();
    616   CGLSetCurrentContext(0);
    617 }
    618 
    619 void CompositingIOSurfaceMac::DrawQuad(const SurfaceQuad& quad) {
    620   glEnableClientState(GL_VERTEX_ARRAY); CHECK_AND_SAVE_GL_ERROR();
    621   glEnableClientState(GL_TEXTURE_COORD_ARRAY); CHECK_AND_SAVE_GL_ERROR();
    622 
    623   glVertexPointer(2, GL_FLOAT, sizeof(SurfaceVertex), &quad.verts_[0].x_);
    624   glTexCoordPointer(2, GL_FLOAT, sizeof(SurfaceVertex), &quad.verts_[0].tx_);
    625   glDrawArrays(GL_QUADS, 0, 4); CHECK_AND_SAVE_GL_ERROR();
    626 
    627   glDisableClientState(GL_VERTEX_ARRAY);
    628   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    629 }
    630 
    631 void CompositingIOSurfaceMac::UnrefIOSurfaceWithContextCurrent() {
    632   if (texture_) {
    633     glDeleteTextures(1, &texture_);
    634     texture_ = 0;
    635   }
    636 
    637   io_surface_.reset();
    638 
    639   // Forget the ID, because even if it is still around when we want to use it
    640   // again, OSX may have reused the same ID for a new tab and we don't want to
    641   // blit random tab contents.
    642   io_surface_handle_ = 0;
    643 }
    644 
    645 void CompositingIOSurfaceMac::DisplayLinkTick(CVDisplayLinkRef display_link,
    646                                               const CVTimeStamp* time) {
    647   TRACE_EVENT0("gpu", "CompositingIOSurfaceMac::DisplayLinkTick");
    648   base::AutoLock lock(lock_);
    649   CalculateVsyncParametersLockHeld(time);
    650 }
    651 
    652 void CompositingIOSurfaceMac::CalculateVsyncParametersLockHeld(
    653     const CVTimeStamp* time) {
    654   lock_.AssertAcquired();
    655   vsync_interval_numerator_ = static_cast<uint32>(time->videoRefreshPeriod);
    656   vsync_interval_denominator_ = time->videoTimeScale;
    657   // Verify that videoRefreshPeriod is 32 bits.
    658   DCHECK((time->videoRefreshPeriod & ~0xffffFFFFull) == 0ull);
    659 
    660   vsync_timebase_ =
    661       base::TimeTicks::FromInternalValue(time->hostTime / 1000);
    662 }
    663 
    664 void CompositingIOSurfaceMac::StartOrContinueDisplayLink() {
    665   if (display_link_ == NULL)
    666     return;
    667 
    668   if (!CVDisplayLinkIsRunning(display_link_)) {
    669     CVDisplayLinkStart(display_link_);
    670   }
    671   display_link_stop_timer_.Reset();
    672 }
    673 
    674 void CompositingIOSurfaceMac::StopDisplayLink() {
    675   if (display_link_ == NULL)
    676     return;
    677 
    678   if (CVDisplayLinkIsRunning(display_link_))
    679     CVDisplayLinkStop(display_link_);
    680 }
    681 
    682 bool CompositingIOSurfaceMac::IsAsynchronousReadbackSupported() {
    683   const bool forced_synchronous = CommandLine::ForCurrentProcess()->HasSwitch(
    684       switches::kForceSynchronousGLReadPixels);
    685   if (forced_synchronous)
    686     return false;
    687   if (!HasAppleFenceExtension() && HasPixelBufferObjectExtension())
    688     return false;
    689   // Using PBO crashes on Intel drivers but not on newer Mountain Lion
    690   // systems. See bug http://crbug.com/152225.
    691   if (offscreen_context_->IsVendorIntel() &&
    692       !base::mac::IsOSMountainLionOrLater())
    693     return false;
    694   return true;
    695 }
    696 
    697 base::Closure CompositingIOSurfaceMac::CopyToSelectedOutputWithinContext(
    698     const gfx::Rect& src_pixel_subrect,
    699     const gfx::Rect& dst_pixel_rect,
    700     bool called_within_draw,
    701     const SkBitmap* bitmap_output,
    702     const scoped_refptr<media::VideoFrame>& video_frame_output,
    703     const base::Callback<void(bool)>& done_callback) {
    704   DCHECK_NE(bitmap_output != NULL, video_frame_output.get() != NULL);
    705   DCHECK(!done_callback.is_null());
    706 
    707   // SWIZZLE_RGBA_FOR_ASYNC_READPIXELS workaround: Fall-back to synchronous
    708   // readback for SkBitmap output since the Blit shader program doesn't support
    709   // switchable output formats.
    710   const bool require_sync_copy_for_workaround = bitmap_output &&
    711       offscreen_context_->shader_program_cache()->rgb_to_yv12_output_format() ==
    712           GL_RGBA;
    713   const bool async_copy = !require_sync_copy_for_workaround &&
    714       IsAsynchronousReadbackSupported();
    715   TRACE_EVENT2(
    716       "browser", "CompositingIOSurfaceMac::CopyToSelectedOutputWithinContext",
    717       "output", bitmap_output ? "SkBitmap (ARGB)" : "VideoFrame (YV12)",
    718       "async_readback", async_copy);
    719 
    720   const gfx::Rect src_rect = IntersectWithIOSurface(src_pixel_subrect);
    721   if (src_rect.IsEmpty() || dst_pixel_rect.IsEmpty())
    722     return base::Bind(done_callback, false);
    723 
    724   CopyContext* copy_context;
    725   if (copy_context_pool_.empty()) {
    726     // Limit the maximum number of simultaneous copies to two.  Rationale:
    727     // Really, only one should ever be in-progress at a time, as we should
    728     // depend on the speed of the hardware to rate-limit the copying naturally.
    729     // In the asynchronous read-back case, the one currently in-flight copy is
    730     // highly likely to have finished by this point (i.e., it's just waiting for
    731     // us to make a glMapBuffer() call).  Therefore, we allow a second copy to
    732     // be started here.
    733     if (copy_requests_.size() >= 2)
    734       return base::Bind(done_callback, false);
    735     copy_context = new CopyContext(offscreen_context_);
    736   } else {
    737     copy_context = copy_context_pool_.back();
    738     copy_context_pool_.pop_back();
    739   }
    740 
    741   if (!HasIOSurface())
    742     return base::Bind(done_callback, false);
    743 
    744   // Send transform commands to the GPU.
    745   copy_context->num_outputs = 0;
    746   if (bitmap_output) {
    747     if (copy_context->transformer->ResizeBilinear(
    748             texture_, src_rect, dst_pixel_rect.size(),
    749             &copy_context->output_textures[0])) {
    750       copy_context->output_readback_format = GL_BGRA;
    751       copy_context->num_outputs = 1;
    752       copy_context->output_texture_sizes[0] = dst_pixel_rect.size();
    753     }
    754   } else {
    755     if (copy_context->transformer->TransformRGBToYV12(
    756             texture_, src_rect, dst_pixel_rect.size(),
    757             &copy_context->output_textures[0],
    758             &copy_context->output_textures[1],
    759             &copy_context->output_textures[2],
    760             &copy_context->output_texture_sizes[0],
    761             &copy_context->output_texture_sizes[1])) {
    762       copy_context->output_readback_format =
    763           offscreen_context_->shader_program_cache()->
    764               rgb_to_yv12_output_format();
    765       copy_context->num_outputs = 3;
    766       copy_context->output_texture_sizes[2] =
    767           copy_context->output_texture_sizes[1];
    768     }
    769   }
    770   if (!copy_context->num_outputs)
    771     return base::Bind(done_callback, false);
    772 
    773   // In the asynchronous case, issue commands to the GPU and return a null
    774   // closure here.  In the synchronous case, perform a blocking readback and
    775   // return a callback to be run outside the CGL context to indicate success.
    776   if (async_copy) {
    777     copy_context->done_callback = done_callback;
    778     AsynchronousReadbackForCopy(
    779         dst_pixel_rect, called_within_draw, copy_context, bitmap_output,
    780         video_frame_output);
    781     copy_requests_.push_back(copy_context);
    782     if (!finish_copy_timer_.IsRunning())
    783       finish_copy_timer_.Reset();
    784     return base::Closure();
    785   } else {
    786     const bool success = SynchronousReadbackForCopy(
    787         dst_pixel_rect, copy_context, bitmap_output, video_frame_output);
    788     return base::Bind(done_callback, success);
    789   }
    790 }
    791 
    792 void CompositingIOSurfaceMac::AsynchronousReadbackForCopy(
    793     const gfx::Rect& dst_pixel_rect,
    794     bool called_within_draw,
    795     CopyContext* copy_context,
    796     const SkBitmap* bitmap_output,
    797     const scoped_refptr<media::VideoFrame>& video_frame_output) {
    798   copy_context->PrepareForAsynchronousReadback();
    799 
    800   // Copy the textures to their corresponding PBO.
    801   for (int i = 0; i < copy_context->num_outputs; ++i) {
    802     TRACE_EVENT1(
    803         "browser", "CompositingIOSurfaceMac::AsynchronousReadbackForCopy",
    804         "plane", i);
    805 
    806     // Attach the output texture to the FBO.
    807     glBindFramebufferEXT(
    808         GL_READ_FRAMEBUFFER_EXT, copy_context->frame_buffers[i]);
    809     glFramebufferTexture2DEXT(
    810         GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
    811         GL_TEXTURE_RECTANGLE_ARB, copy_context->output_textures[i], 0);
    812     DCHECK(glCheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT) ==
    813                GL_FRAMEBUFFER_COMPLETE_EXT);
    814 
    815     // Create a PBO and issue an asynchronous read-back.
    816     glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, copy_context->pixel_buffers[i]);
    817     CHECK_AND_SAVE_GL_ERROR();
    818     glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB,
    819                     copy_context->output_texture_sizes[i].GetArea() * 4,
    820                     NULL, GL_STREAM_READ_ARB);
    821     CHECK_AND_SAVE_GL_ERROR();
    822     glReadPixels(0, 0,
    823                  copy_context->output_texture_sizes[i].width(),
    824                  copy_context->output_texture_sizes[i].height(),
    825                  copy_context->output_readback_format,
    826                  GL_UNSIGNED_INT_8_8_8_8_REV, 0);
    827     CHECK_AND_SAVE_GL_ERROR();
    828   }
    829 
    830   glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); CHECK_AND_SAVE_GL_ERROR();
    831   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); CHECK_AND_SAVE_GL_ERROR();
    832 
    833   glSetFenceAPPLE(copy_context->fence); CHECK_GL_ERROR();
    834   copy_context->cycles_elapsed = 0;
    835 
    836   // When this asynchronous copy happens in a draw operaton there is no need
    837   // to explicitly flush because there will be a swap buffer and this flush
    838   // hurts performance.
    839   if (!called_within_draw) {
    840     glFlush(); CHECK_AND_SAVE_GL_ERROR();
    841   }
    842 
    843   copy_context->map_buffer_callback = bitmap_output ?
    844       base::Bind(&MapBufferToSkBitmap, bitmap_output) :
    845       base::Bind(&MapBufferToVideoFrame, video_frame_output, dst_pixel_rect);
    846 }
    847 
    848 void CompositingIOSurfaceMac::CheckIfAllCopiesAreFinished(
    849     bool block_until_finished) {
    850   if (copy_requests_.empty())
    851     return;
    852 
    853   std::vector<base::Closure> done_callbacks;
    854   CGLContextObj previous_context = CGLGetCurrentContext();
    855   CGLSetCurrentContext(offscreen_context_->cgl_context());
    856   CheckIfAllCopiesAreFinishedWithinContext(
    857       block_until_finished, &done_callbacks);
    858   CGLSetCurrentContext(previous_context);
    859   for (size_t i = 0; i < done_callbacks.size(); ++i)
    860     done_callbacks[i].Run();
    861 }
    862 
    863 void CompositingIOSurfaceMac::CheckIfAllCopiesAreFinishedWithinContext(
    864     bool block_until_finished,
    865     std::vector<base::Closure>* done_callbacks) {
    866   while (!copy_requests_.empty()) {
    867     CopyContext* const copy_context = copy_requests_.front();
    868 
    869     if (copy_context->fence && !glTestFenceAPPLE(copy_context->fence)) {
    870       CHECK_AND_SAVE_GL_ERROR();
    871       // Doing a glFinishFenceAPPLE can cause transparent window flashes when
    872       // switching tabs, so only do it when required.
    873       if (block_until_finished) {
    874         glFinishFenceAPPLE(copy_context->fence);
    875         CHECK_AND_SAVE_GL_ERROR();
    876       } else if (copy_context->cycles_elapsed < kFinishCopyRetryCycles) {
    877         ++copy_context->cycles_elapsed;
    878         // This copy has not completed there is no need to test subsequent
    879         // requests.
    880         break;
    881       }
    882     }
    883     CHECK_AND_SAVE_GL_ERROR();
    884 
    885     bool success = true;
    886     for (int i = 0; success && i < copy_context->num_outputs; ++i) {
    887       TRACE_EVENT1(
    888         "browser",
    889         "CompositingIOSurfaceMac::CheckIfAllCopiesAreFinishedWithinContext",
    890         "plane", i);
    891 
    892       glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, copy_context->pixel_buffers[i]);
    893       CHECK_AND_SAVE_GL_ERROR();
    894 
    895       void* buf = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);
    896       CHECK_AND_SAVE_GL_ERROR();
    897       success &= copy_context->map_buffer_callback.Run(buf, i);
    898       glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); CHECK_AND_SAVE_GL_ERROR();
    899     }
    900     copy_context->map_buffer_callback.Reset();
    901     glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); CHECK_AND_SAVE_GL_ERROR();
    902 
    903     copy_requests_.pop_front();
    904     done_callbacks->push_back(base::Bind(copy_context->done_callback, success));
    905     copy_context->done_callback.Reset();
    906     copy_context_pool_.push_back(copy_context);
    907   }
    908   if (copy_requests_.empty())
    909     finish_copy_timer_.Stop();
    910 
    911   CHECK(copy_requests_.empty() || !block_until_finished);
    912 }
    913 
    914 bool CompositingIOSurfaceMac::SynchronousReadbackForCopy(
    915     const gfx::Rect& dst_pixel_rect,
    916     CopyContext* copy_context,
    917     const SkBitmap* bitmap_output,
    918     const scoped_refptr<media::VideoFrame>& video_frame_output) {
    919   bool success = true;
    920   copy_context->PrepareReadbackFramebuffers();
    921   for (int i = 0; i < copy_context->num_outputs; ++i) {
    922     TRACE_EVENT1(
    923         "browser", "CompositingIOSurfaceMac::SynchronousReadbackForCopy",
    924         "plane", i);
    925 
    926     // Attach the output texture to the FBO.
    927     glBindFramebufferEXT(
    928         GL_READ_FRAMEBUFFER_EXT, copy_context->frame_buffers[i]);
    929     glFramebufferTexture2DEXT(
    930         GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
    931         GL_TEXTURE_RECTANGLE_ARB, copy_context->output_textures[i], 0);
    932     DCHECK(glCheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT) ==
    933                GL_FRAMEBUFFER_COMPLETE_EXT);
    934 
    935     // Blocking read-back of pixels from textures.
    936     void* buf;
    937     // When data must be transferred into a VideoFrame one scanline at a time,
    938     // it is necessary to allocate a separate buffer for glReadPixels() that can
    939     // be populated one-shot.
    940     //
    941     // TODO(miu): Don't keep allocating/deleting this buffer for every frame.
    942     // Keep it cached, allocated on first use.
    943     scoped_ptr<uint32[]> temp_readback_buffer;
    944     if (bitmap_output) {
    945       // The entire SkBitmap is populated, never a region within.  So, read the
    946       // texture directly into the bitmap's pixel memory.
    947       buf = bitmap_output->getPixels();
    948     } else {
    949       // Optimization: If the VideoFrame is letterboxed (not pillarboxed), and
    950       // its stride is equal to the stride of the data being read back, then
    951       // readback directly into the VideoFrame's buffer to save a round of
    952       // memcpy'ing.
    953       //
    954       // TODO(miu): Move these calculations into VideoFrame (need a CalcOffset()
    955       // method).  http://crbug.com/219779
    956       const int src_stride = copy_context->output_texture_sizes[i].width() * 4;
    957       const int dst_stride = video_frame_output->stride(i);
    958       if (src_stride == dst_stride && dst_pixel_rect.x() == 0) {
    959         const int y_offset = dst_pixel_rect.y() / (i == 0 ? 1 : 2);
    960         buf = video_frame_output->data(i) + y_offset * dst_stride;
    961       } else {
    962         // Create and readback into a temporary buffer because the data must be
    963         // transferred to VideoFrame's pixel memory one scanline at a time.
    964         temp_readback_buffer.reset(
    965             new uint32[copy_context->output_texture_sizes[i].GetArea()]);
    966         buf = temp_readback_buffer.get();
    967       }
    968     }
    969     glReadPixels(0, 0,
    970                  copy_context->output_texture_sizes[i].width(),
    971                  copy_context->output_texture_sizes[i].height(),
    972                  copy_context->output_readback_format,
    973                  GL_UNSIGNED_INT_8_8_8_8_REV, buf);
    974     CHECK_AND_SAVE_GL_ERROR();
    975     if (video_frame_output.get()) {
    976       if (!temp_readback_buffer) {
    977         // Apply letterbox black-out around view region.
    978         media::LetterboxYUV(video_frame_output.get(), dst_pixel_rect);
    979       } else {
    980         // Copy from temporary buffer and fully render the VideoFrame.
    981         success &= MapBufferToVideoFrame(video_frame_output, dst_pixel_rect,
    982                                          temp_readback_buffer.get(), i);
    983       }
    984     }
    985   }
    986 
    987   glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); CHECK_AND_SAVE_GL_ERROR();
    988   copy_context_pool_.push_back(copy_context);
    989   return success;
    990 }
    991 
    992 void CompositingIOSurfaceMac::FailAllCopies() {
    993   for (size_t i = 0; i < copy_requests_.size(); ++i) {
    994     copy_requests_[i]->map_buffer_callback.Reset();
    995 
    996     base::Callback<void(bool)>& done_callback =
    997         copy_requests_[i]->done_callback;
    998     if (!done_callback.is_null()) {
    999       done_callback.Run(false);
   1000       done_callback.Reset();
   1001     }
   1002   }
   1003 }
   1004 
   1005 void CompositingIOSurfaceMac::DestroyAllCopyContextsWithinContext() {
   1006   // Move all in-flight copies, if any, back into the pool.  Then, destroy all
   1007   // the CopyContexts in the pool.
   1008   copy_context_pool_.insert(copy_context_pool_.end(),
   1009                             copy_requests_.begin(), copy_requests_.end());
   1010   copy_requests_.clear();
   1011   while (!copy_context_pool_.empty()) {
   1012     scoped_ptr<CopyContext> copy_context(copy_context_pool_.back());
   1013     copy_context_pool_.pop_back();
   1014     copy_context->ReleaseCachedGLObjects();
   1015   }
   1016 }
   1017 
   1018 gfx::Rect CompositingIOSurfaceMac::IntersectWithIOSurface(
   1019     const gfx::Rect& rect) const {
   1020   return gfx::IntersectRects(rect,
   1021       gfx::ToEnclosingRect(gfx::Rect(pixel_io_surface_size_)));
   1022 }
   1023 
   1024 GLenum CompositingIOSurfaceMac::GetAndSaveGLError() {
   1025   GLenum gl_error = glGetError();
   1026   if (gl_error_ == GL_NO_ERROR)
   1027     gl_error_ = gl_error;
   1028   return gl_error;
   1029 }
   1030 
   1031 }  // namespace content
   1032