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