compositing_iosurface_mac.h revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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
29class SkBitmap;
30
31namespace gfx {
32class Rect;
33}
34
35namespace content {
36
37class CompositingIOSurfaceContext;
38class CompositingIOSurfaceShaderPrograms;
39class CompositingIOSurfaceTransformer;
40class RenderWidgetHostViewFrameSubscriber;
41class 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.
46class 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  CompositingIOSurfaceMac(
173      const scoped_refptr<CompositingIOSurfaceContext>& context);
174  ~CompositingIOSurfaceMac();
175
176  // Returns true if IOSurface is ready to render. False otherwise.
177  bool MapIOSurfaceToTextureWithContextCurrent(
178      const scoped_refptr<CompositingIOSurfaceContext>& current_context,
179      const gfx::Size pixel_size,
180      float scale_factor,
181      IOSurfaceID io_surface_handle) WARN_UNUSED_RESULT;
182
183  void UnrefIOSurfaceWithContextCurrent();
184
185  void DrawQuad(const SurfaceQuad& quad);
186
187  // Check for GL errors and store the result in error_. Only return new
188  // errors
189  GLenum GetAndSaveGLError();
190
191  // Offscreen context used for all operations other than drawing to the
192  // screen. This is in the same share group as the contexts used for
193  // drawing, and is the same for all IOSurfaces in all windows.
194  scoped_refptr<CompositingIOSurfaceContext> offscreen_context_;
195
196  // IOSurface data.
197  IOSurfaceID io_surface_handle_;
198  base::ScopedCFTypeRef<IOSurfaceRef> io_surface_;
199
200  // The width and height of the io surface.
201  gfx::Size pixel_io_surface_size_;  // In pixels.
202  gfx::Size dip_io_surface_size_;  // In view / density independent pixels.
203  float scale_factor_;
204
205  // The "live" OpenGL texture referring to this IOSurfaceRef. Note
206  // that per the CGLTexImageIOSurface2D API we do not need to
207  // explicitly update this texture's contents once created. All we
208  // need to do is ensure it is re-bound before attempting to draw
209  // with it.
210  GLuint texture_;
211
212  // Error saved by GetAndSaveGLError
213  GLint gl_error_;
214
215  // Aggressive IOSurface eviction logic. When using CoreAnimation, IOSurfaces
216  // are used only transiently to transfer from the GPU process to the browser
217  // process. Once the IOSurface has been drawn to its CALayer, the CALayer
218  // will not need updating again until its view is hidden and re-shown.
219  // Aggressively evict surfaces when more than 8 (the number allowed by the
220  // memory manager for fast tab switching) are allocated.
221  enum {
222    kMaximumUnevictedSurfaces = 8,
223  };
224  typedef std::list<CompositingIOSurfaceMac*> EvictionQueue;
225  void EvictionMarkUpdated();
226  void EvictionMarkEvicted();
227  EvictionQueue::iterator eviction_queue_iterator_;
228  bool eviction_has_been_drawn_since_updated_;
229
230  static void EvictionScheduleDoEvict();
231  static void EvictionDoEvict();
232  static base::LazyInstance<EvictionQueue> eviction_queue_;
233  static bool eviction_scheduled_;
234};
235
236}  // namespace content
237
238#endif  // CONTENT_BROWSER_RENDERER_HOST_COMPOSITING_IOSURFACE_MAC_H_
239