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 #ifndef CONTENT_BROWSER_RENDERER_HOST_COMPOSITING_IOSURFACE_MAC_H_
      6 #define CONTENT_BROWSER_RENDERER_HOST_COMPOSITING_IOSURFACE_MAC_H_
      7 
      8 #include <deque>
      9 #include <list>
     10 #include <vector>
     11 
     12 #import <Cocoa/Cocoa.h>
     13 #include <IOSurface/IOSurfaceAPI.h>
     14 #include <QuartzCore/QuartzCore.h>
     15 
     16 #include "base/callback.h"
     17 #include "base/lazy_instance.h"
     18 #include "base/mac/scoped_cftyperef.h"
     19 #include "base/memory/ref_counted.h"
     20 #include "base/memory/scoped_ptr.h"
     21 #include "base/time/time.h"
     22 #include "base/timer/timer.h"
     23 #include "media/base/video_frame.h"
     24 #include "ui/gfx/native_widget_types.h"
     25 #include "ui/gfx/rect.h"
     26 #include "ui/gfx/rect_conversions.h"
     27 #include "ui/gfx/size.h"
     28 
     29 class SkBitmap;
     30 
     31 namespace gfx {
     32 class Rect;
     33 }
     34 
     35 namespace content {
     36 
     37 class CompositingIOSurfaceContext;
     38 class CompositingIOSurfaceShaderPrograms;
     39 class CompositingIOSurfaceTransformer;
     40 class RenderWidgetHostViewFrameSubscriber;
     41 class RenderWidgetHostViewMac;
     42 
     43 // This class manages an OpenGL context and IOSurface for the accelerated
     44 // compositing code path. The GL context is attached to
     45 // RenderWidgetHostViewCocoa for blitting the IOSurface.
     46 class CompositingIOSurfaceMac
     47     : public base::RefCounted<CompositingIOSurfaceMac> {
     48  public:
     49   // Returns NULL if IOSurface or GL API calls fail.
     50   static scoped_refptr<CompositingIOSurfaceMac> Create();
     51 
     52   // Set IOSurface that will be drawn on the next NSView drawRect.
     53   bool SetIOSurfaceWithContextCurrent(
     54       scoped_refptr<CompositingIOSurfaceContext> current_context,
     55       IOSurfaceID io_surface_handle,
     56       const gfx::Size& size,
     57       float scale_factor) WARN_UNUSED_RESULT;
     58 
     59   // Get the CGL renderer ID currently associated with this context.
     60   int GetRendererID();
     61 
     62   // Blit the IOSurface to the rectangle specified by |window_rect| in DIPs,
     63   // with the origin in the lower left corner. If the window rect's size is
     64   // larger than the IOSurface, the remaining right and bottom edges will be
     65   // white. |window_scale_factor| is 1 in normal views, 2 in HiDPI views.
     66   bool DrawIOSurface(
     67       scoped_refptr<CompositingIOSurfaceContext> drawing_context,
     68       const gfx::Rect& window_rect,
     69       float window_scale_factor) WARN_UNUSED_RESULT;
     70 
     71   // Copy the data of the "live" OpenGL texture referring to this IOSurfaceRef
     72   // into |out|. The copied region is specified with |src_pixel_subrect| and
     73   // the data is transformed so that it fits in |dst_pixel_size|.
     74   // |src_pixel_subrect| and |dst_pixel_size| are not in DIP but in pixel.
     75   // Caller must ensure that |out| is allocated to dimensions that match
     76   // dst_pixel_size, with no additional padding.
     77   // |callback| is invoked when the operation is completed or failed.
     78   // Do no call this method again before |callback| is invoked.
     79   void CopyTo(const gfx::Rect& src_pixel_subrect,
     80               const gfx::Size& dst_pixel_size,
     81               const base::Callback<void(bool, const SkBitmap&)>& callback);
     82 
     83   // Transfer the contents of the surface to an already-allocated YV12
     84   // VideoFrame, and invoke a callback to indicate success or failure.
     85   void CopyToVideoFrame(
     86       const gfx::Rect& src_subrect,
     87       const scoped_refptr<media::VideoFrame>& target,
     88       const base::Callback<void(bool)>& callback);
     89 
     90   // Unref the IOSurface and delete the associated GL texture. If the GPU
     91   // process is no longer referencing it, this will delete the IOSurface.
     92   void UnrefIOSurface();
     93 
     94   bool HasIOSurface() { return !!io_surface_.get(); }
     95 
     96   const gfx::Size& pixel_io_surface_size() const {
     97     return pixel_io_surface_size_;
     98   }
     99   // In cocoa view units / DIPs.
    100   const gfx::Size& dip_io_surface_size() const { return dip_io_surface_size_; }
    101   float scale_factor() const { return scale_factor_; }
    102 
    103   // Returns true if asynchronous readback is supported on this system.
    104   bool IsAsynchronousReadbackSupported();
    105 
    106   // Scan the list of started asynchronous copies and test if each one has
    107   // completed. If |block_until_finished| is true, then block until all
    108   // pending copies are finished.
    109   void CheckIfAllCopiesAreFinished(bool block_until_finished);
    110 
    111   // Returns true if the offscreen context used by this surface has been
    112   // poisoned.
    113   bool HasBeenPoisoned() const;
    114 
    115  private:
    116   friend class base::RefCounted<CompositingIOSurfaceMac>;
    117 
    118   // Vertex structure for use in glDraw calls.
    119   struct SurfaceVertex {
    120     SurfaceVertex() : x_(0.0f), y_(0.0f), tx_(0.0f), ty_(0.0f) { }
    121     void set(float x, float y, float tx, float ty) {
    122       x_ = x;
    123       y_ = y;
    124       tx_ = tx;
    125       ty_ = ty;
    126     }
    127     void set_position(float x, float y) {
    128       x_ = x;
    129       y_ = y;
    130     }
    131     void set_texcoord(float tx, float ty) {
    132       tx_ = tx;
    133       ty_ = ty;
    134     }
    135     float x_;
    136     float y_;
    137     float tx_;
    138     float ty_;
    139   };
    140 
    141   // Counter-clockwise verts starting from upper-left corner (0, 0).
    142   struct SurfaceQuad {
    143     void set_size(gfx::Size vertex_size, gfx::Size texcoord_size) {
    144       // Texture coordinates are flipped vertically so they can be drawn on
    145       // a projection with a flipped y-axis (origin is top left).
    146       float vw = static_cast<float>(vertex_size.width());
    147       float vh = static_cast<float>(vertex_size.height());
    148       float tw = static_cast<float>(texcoord_size.width());
    149       float th = static_cast<float>(texcoord_size.height());
    150       verts_[0].set(0.0f, 0.0f, 0.0f, th);
    151       verts_[1].set(0.0f, vh, 0.0f, 0.0f);
    152       verts_[2].set(vw, vh, tw, 0.0f);
    153       verts_[3].set(vw, 0.0f, tw, th);
    154     }
    155     void set_rect(float x1, float y1, float x2, float y2) {
    156       verts_[0].set_position(x1, y1);
    157       verts_[1].set_position(x1, y2);
    158       verts_[2].set_position(x2, y2);
    159       verts_[3].set_position(x2, y1);
    160     }
    161     void set_texcoord_rect(float tx1, float ty1, float tx2, float ty2) {
    162       // Texture coordinates are flipped vertically so they can be drawn on
    163       // a projection with a flipped y-axis (origin is top left).
    164       verts_[0].set_texcoord(tx1, ty2);
    165       verts_[1].set_texcoord(tx1, ty1);
    166       verts_[2].set_texcoord(tx2, ty1);
    167       verts_[3].set_texcoord(tx2, ty2);
    168     }
    169     SurfaceVertex verts_[4];
    170   };
    171 
    172   // Keeps track of states and buffers for readback of IOSurface.
    173   //
    174   // TODO(miu): Major code refactoring is badly needed!  To be done in a
    175   // soon-upcoming change.  For now, we blatantly violate the style guide with
    176   // respect to struct vs. class usage:
    177   struct CopyContext {
    178     explicit CopyContext(const scoped_refptr<CompositingIOSurfaceContext>& ctx);
    179     ~CopyContext();
    180 
    181     // Delete any references to owned OpenGL objects.  This must be called
    182     // within the OpenGL context just before destruction.
    183     void ReleaseCachedGLObjects();
    184 
    185     // The following two methods assume |num_outputs| has been set, and are
    186     // being called within the OpenGL context.
    187     void PrepareReadbackFramebuffers();
    188     void PrepareForAsynchronousReadback();
    189 
    190     const scoped_ptr<CompositingIOSurfaceTransformer> transformer;
    191     GLenum output_readback_format;
    192     int num_outputs;
    193     GLuint output_textures[3];  // Not owned.
    194     // Note: For YUV, the |output_texture_sizes| widths are in terms of 4-byte
    195     // quads, not pixels.
    196     gfx::Size output_texture_sizes[3];
    197     GLuint frame_buffers[3];
    198     GLuint pixel_buffers[3];
    199     GLuint fence;  // When non-zero, doing an asynchronous copy.
    200     int cycles_elapsed;
    201     base::Callback<bool(const void*, int)> map_buffer_callback;
    202     base::Callback<void(bool)> done_callback;
    203   };
    204 
    205   CompositingIOSurfaceMac(
    206       const scoped_refptr<CompositingIOSurfaceContext>& context);
    207   ~CompositingIOSurfaceMac();
    208 
    209   // If this IOSurface has moved to a different window, use that window's
    210   // GL context (if multiple visible windows are using the same GL context
    211   // then call to setView call can stall and prevent reaching 60fps).
    212   void SwitchToContextOnNewWindow(NSView* view,
    213                                   int window_number);
    214 
    215   // Returns true if IOSurface is ready to render. False otherwise.
    216   bool MapIOSurfaceToTextureWithContextCurrent(
    217       const scoped_refptr<CompositingIOSurfaceContext>& current_context,
    218       const gfx::Size pixel_size,
    219       float scale_factor,
    220       IOSurfaceID io_surface_handle) WARN_UNUSED_RESULT;
    221 
    222   void UnrefIOSurfaceWithContextCurrent();
    223 
    224   void DrawQuad(const SurfaceQuad& quad);
    225 
    226   // Copy current frame to |target| video frame. This method must be called
    227   // within a CGL context. Returns a callback that should be called outside
    228   // of the CGL context.
    229   // If |called_within_draw| is true this method is called within a drawing
    230   // operations. This allow certain optimizations.
    231   base::Closure CopyToVideoFrameWithinContext(
    232       const gfx::Rect& src_subrect,
    233       bool called_within_draw,
    234       const scoped_refptr<media::VideoFrame>& target,
    235       const base::Callback<void(bool)>& callback);
    236 
    237   // Common GPU-readback copy path.  Only one of |bitmap_output| or
    238   // |video_frame_output| may be specified: Either ARGB is written to
    239   // |bitmap_output| or letter-boxed YV12 is written to |video_frame_output|.
    240   base::Closure CopyToSelectedOutputWithinContext(
    241       const gfx::Rect& src_pixel_subrect,
    242       const gfx::Rect& dst_pixel_rect,
    243       bool called_within_draw,
    244       const SkBitmap* bitmap_output,
    245       const scoped_refptr<media::VideoFrame>& video_frame_output,
    246       const base::Callback<void(bool)>& done_callback);
    247 
    248   // TODO(hclam): These two methods should be static.
    249   void AsynchronousReadbackForCopy(
    250       const gfx::Rect& dst_pixel_rect,
    251       bool called_within_draw,
    252       CopyContext* copy_context,
    253       const SkBitmap* bitmap_output,
    254       const scoped_refptr<media::VideoFrame>& video_frame_output);
    255   bool SynchronousReadbackForCopy(
    256       const gfx::Rect& dst_pixel_rect,
    257       CopyContext* copy_context,
    258       const SkBitmap* bitmap_output,
    259       const scoped_refptr<media::VideoFrame>& video_frame_output);
    260 
    261   void CheckIfAllCopiesAreFinishedWithinContext(
    262       bool block_until_finished,
    263       std::vector<base::Closure>* done_callbacks);
    264 
    265   void FailAllCopies();
    266   void DestroyAllCopyContextsWithinContext();
    267 
    268   // Check for GL errors and store the result in error_. Only return new
    269   // errors
    270   GLenum GetAndSaveGLError();
    271 
    272   gfx::Rect IntersectWithIOSurface(const gfx::Rect& rect) const;
    273 
    274   // Offscreen context used for all operations other than drawing to the
    275   // screen. This is in the same share group as the contexts used for
    276   // drawing, and is the same for all IOSurfaces in all windows.
    277   scoped_refptr<CompositingIOSurfaceContext> offscreen_context_;
    278 
    279   // IOSurface data.
    280   IOSurfaceID io_surface_handle_;
    281   base::ScopedCFTypeRef<IOSurfaceRef> io_surface_;
    282 
    283   // The width and height of the io surface.
    284   gfx::Size pixel_io_surface_size_;  // In pixels.
    285   gfx::Size dip_io_surface_size_;  // In view / density independent pixels.
    286   float scale_factor_;
    287 
    288   // The "live" OpenGL texture referring to this IOSurfaceRef. Note
    289   // that per the CGLTexImageIOSurface2D API we do not need to
    290   // explicitly update this texture's contents once created. All we
    291   // need to do is ensure it is re-bound before attempting to draw
    292   // with it.
    293   GLuint texture_;
    294 
    295   // A pool of CopyContexts with OpenGL objects ready for re-use.  Prefer to
    296   // pull one from the pool before creating a new CopyContext.
    297   std::vector<CopyContext*> copy_context_pool_;
    298 
    299   // CopyContexts being used for in-flight copy operations.
    300   std::deque<CopyContext*> copy_requests_;
    301 
    302   // Timer for finishing a copy operation.
    303   base::Timer finish_copy_timer_;
    304 
    305   // Error saved by GetAndSaveGLError
    306   GLint gl_error_;
    307 
    308   // Aggressive IOSurface eviction logic. When using CoreAnimation, IOSurfaces
    309   // are used only transiently to transfer from the GPU process to the browser
    310   // process. Once the IOSurface has been drawn to its CALayer, the CALayer
    311   // will not need updating again until its view is hidden and re-shown.
    312   // Aggressively evict surfaces when more than 8 (the number allowed by the
    313   // memory manager for fast tab switching) are allocated.
    314   enum {
    315     kMaximumUnevictedSurfaces = 8,
    316   };
    317   typedef std::list<CompositingIOSurfaceMac*> EvictionQueue;
    318   void EvictionMarkUpdated();
    319   void EvictionMarkEvicted();
    320   EvictionQueue::iterator eviction_queue_iterator_;
    321   bool eviction_has_been_drawn_since_updated_;
    322 
    323   static void EvictionScheduleDoEvict();
    324   static void EvictionDoEvict();
    325   static base::LazyInstance<EvictionQueue> eviction_queue_;
    326   static bool eviction_scheduled_;
    327 };
    328 
    329 }  // namespace content
    330 
    331 #endif  // CONTENT_BROWSER_RENDERER_HOST_COMPOSITING_IOSURFACE_MAC_H_
    332