compositing_iosurface_mac.h revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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
10#import <Cocoa/Cocoa.h>
11#import <QuartzCore/CVDisplayLink.h>
12#include <QuartzCore/QuartzCore.h>
13
14#include "base/callback.h"
15#include "base/mac/scoped_cftyperef.h"
16#include "base/memory/scoped_nsobject.h"
17#include "base/memory/scoped_ptr.h"
18#include "base/synchronization/lock.h"
19#include "base/time.h"
20#include "base/timer.h"
21#include "media/base/video_frame.h"
22#include "ui/gfx/native_widget_types.h"
23#include "ui/gfx/rect.h"
24#include "ui/gfx/rect_conversions.h"
25#include "ui/gfx/size.h"
26
27class IOSurfaceSupport;
28class SkBitmap;
29
30namespace gfx {
31class Rect;
32}
33
34namespace content {
35
36class CompositingIOSurfaceShaderPrograms;
37class CompositingIOSurfaceTransformer;
38class RenderWidgetHostViewFrameSubscriber;
39
40// This class manages an OpenGL context and IOSurface for the accelerated
41// compositing code path. The GL context is attached to
42// RenderWidgetHostViewCocoa for blitting the IOSurface.
43class CompositingIOSurfaceMac {
44 public:
45  // Passed to Create() to specify the ordering of the surface relative to the
46  // containing window.
47  enum SurfaceOrder {
48    SURFACE_ORDER_ABOVE_WINDOW,
49    SURFACE_ORDER_BELOW_WINDOW
50  };
51
52  // Returns NULL if IOSurface support is missing or GL APIs fail. Specify in
53  // |order| the desired ordering relationship of the surface to the containing
54  // window.
55  static CompositingIOSurfaceMac* Create(SurfaceOrder order);
56  ~CompositingIOSurfaceMac();
57
58  // Set IOSurface that will be drawn on the next NSView drawRect.
59  void SetIOSurface(uint64 io_surface_handle,
60                    const gfx::Size& size);
61
62  // Get the CGL renderer ID currently associated with this context.
63  int GetRendererID();
64
65  // Blit the IOSurface at the upper-left corner of the |view|. If |view| window
66  // size is larger than the IOSurface, the remaining right and bottom edges
67  // will be white. |scaleFactor| is 1 in normal views, 2 in HiDPI views.
68  // |frame_subscriber| listens to this draw event and provides output buffer
69  // for copying this frame into.
70  void DrawIOSurface(NSView* view, float scale_factor,
71                     RenderWidgetHostViewFrameSubscriber* frame_subscriber);
72
73  // Copy the data of the "live" OpenGL texture referring to this IOSurfaceRef
74  // into |out|. The copied region is specified with |src_pixel_subrect| and
75  // the data is transformed so that it fits in |dst_pixel_size|.
76  // |src_pixel_subrect| and |dst_pixel_size| are not in DIP but in pixel.
77  // Caller must ensure that |out| is allocated to dimensions that match
78  // dst_pixel_size, with no additional padding.
79  // |callback| is invoked when the operation is completed or failed.
80  // Do no call this method again before |callback| is invoked.
81  void CopyTo(const gfx::Rect& src_pixel_subrect,
82              float src_scale_factor,
83              const gfx::Size& dst_pixel_size,
84              const base::Callback<void(bool, const SkBitmap&)>& callback);
85
86  // Transfer the contents of the surface to an already-allocated YV12
87  // VideoFrame, and invoke a callback to indicate success or failure.
88  void CopyToVideoFrame(
89      const gfx::Rect& src_subrect,
90      float src_scale_factor,
91      const scoped_refptr<media::VideoFrame>& target,
92      const base::Callback<void(bool)>& callback);
93
94  // Unref the IOSurface and delete the associated GL texture. If the GPU
95  // process is no longer referencing it, this will delete the IOSurface.
96  void UnrefIOSurface();
97
98  // Call when globalFrameDidChange is received on the NSView.
99  void GlobalFrameDidChange();
100
101  // Disassociate the GL context with the NSView and unref the IOSurface. Do
102  // this to switch to software drawing mode.
103  void ClearDrawable();
104
105  bool HasIOSurface() { return !!io_surface_.get(); }
106
107  const gfx::Size& pixel_io_surface_size() const {
108    return pixel_io_surface_size_;
109  }
110  // In cocoa view units / DIPs.
111  const gfx::Size& io_surface_size() const { return io_surface_size_; }
112
113  bool is_vsync_disabled() const { return is_vsync_disabled_; }
114
115  // Get vsync scheduling parameters.
116  // |interval_numerator/interval_denominator| equates to fractional number of
117  // seconds between vsyncs.
118  void GetVSyncParameters(base::TimeTicks* timebase,
119                          uint32* interval_numerator,
120                          uint32* interval_denominator);
121
122  // Returns true if asynchronous readback is supported on this system.
123  bool IsAsynchronousReadbackSupported();
124
125 private:
126  friend CVReturn DisplayLinkCallback(CVDisplayLinkRef,
127                                      const CVTimeStamp*,
128                                      const CVTimeStamp*,
129                                      CVOptionFlags,
130                                      CVOptionFlags*,
131                                      void*);
132
133  // Vertex structure for use in glDraw calls.
134  struct SurfaceVertex {
135    SurfaceVertex() : x_(0.0f), y_(0.0f), tx_(0.0f), ty_(0.0f) { }
136    void set(float x, float y, float tx, float ty) {
137      x_ = x;
138      y_ = y;
139      tx_ = tx;
140      ty_ = ty;
141    }
142    void set_position(float x, float y) {
143      x_ = x;
144      y_ = y;
145    }
146    void set_texcoord(float tx, float ty) {
147      tx_ = tx;
148      ty_ = ty;
149    }
150    float x_;
151    float y_;
152    float tx_;
153    float ty_;
154  };
155
156  // Counter-clockwise verts starting from upper-left corner (0, 0).
157  struct SurfaceQuad {
158    void set_size(gfx::Size vertex_size, gfx::Size texcoord_size) {
159      // Texture coordinates are flipped vertically so they can be drawn on
160      // a projection with a flipped y-axis (origin is top left).
161      float vw = static_cast<float>(vertex_size.width());
162      float vh = static_cast<float>(vertex_size.height());
163      float tw = static_cast<float>(texcoord_size.width());
164      float th = static_cast<float>(texcoord_size.height());
165      verts_[0].set(0.0f, 0.0f, 0.0f, th);
166      verts_[1].set(0.0f, vh, 0.0f, 0.0f);
167      verts_[2].set(vw, vh, tw, 0.0f);
168      verts_[3].set(vw, 0.0f, tw, th);
169    }
170    void set_rect(float x1, float y1, float x2, float y2) {
171      verts_[0].set_position(x1, y1);
172      verts_[1].set_position(x1, y2);
173      verts_[2].set_position(x2, y2);
174      verts_[3].set_position(x2, y1);
175    }
176    void set_texcoord_rect(float tx1, float ty1, float tx2, float ty2) {
177      // Texture coordinates are flipped vertically so they can be drawn on
178      // a projection with a flipped y-axis (origin is top left).
179      verts_[0].set_texcoord(tx1, ty2);
180      verts_[1].set_texcoord(tx1, ty1);
181      verts_[2].set_texcoord(tx2, ty1);
182      verts_[3].set_texcoord(tx2, ty2);
183    }
184    SurfaceVertex verts_[4];
185  };
186
187  // Keeps track of states and buffers for readback of IOSurface.
188  struct CopyContext {
189    CopyContext();
190    ~CopyContext();
191    void CleanUp();
192
193    int num_outputs;
194    GLuint output_textures[3];
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      NSOpenGLContext* glContext,
209      CGLContextObj cglContext,
210      scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache,
211      bool is_vsync_disabled,
212      CVDisplayLinkRef display_link);
213
214  bool IsVendorIntel();
215
216  // Returns true if IOSurface is ready to render. False otherwise.
217  bool MapIOSurfaceToTexture(uint64 io_surface_handle);
218
219  void UnrefIOSurfaceWithContextCurrent();
220
221  void DrawQuad(const SurfaceQuad& quad);
222
223  // Called on display-link thread.
224  void DisplayLinkTick(CVDisplayLinkRef display_link,
225                       const CVTimeStamp* time);
226
227  void CalculateVsyncParametersLockHeld(const CVTimeStamp* time);
228
229  // Prevent from spinning on CGLFlushDrawable when it fails to throttle to
230  // VSync frequency.
231  void RateLimitDraws();
232
233  void StartOrContinueDisplayLink();
234  void StopDisplayLink();
235
236  // Copy current frame to |target| video frame. This method must be called
237  // within a CGL context. Returns a callback that should be called outside
238  // of the CGL context.
239  // If |called_within_draw| is true this method is called within a drawing
240  // operations. This allow certain optimizations.
241  base::Closure CopyToVideoFrameWithinContext(
242      const gfx::Rect& src_subrect,
243      float src_scale_factor,
244      bool called_within_draw,
245      const scoped_refptr<media::VideoFrame>& target,
246      const base::Callback<void(bool)>& callback);
247
248  // Common GPU-readback copy path.  Only one of |bitmap_output| or
249  // |video_frame_output| may be specified: Either ARGB is written to
250  // |bitmap_output| or letter-boxed YV12 is written to |video_frame_output|.
251  base::Closure CopyToSelectedOutputWithinContext(
252      const gfx::Rect& src_pixel_subrect,
253      float src_scale_factor,
254      const gfx::Rect& dst_pixel_rect,
255      bool called_within_draw,
256      const SkBitmap* bitmap_output,
257      const scoped_refptr<media::VideoFrame>& video_frame_output,
258      const base::Callback<void(bool)>& done_callback);
259
260  // TODO(hclam): These two methods should be static.
261  void AsynchronousReadbackForCopy(
262      const gfx::Rect& dst_pixel_rect,
263      bool called_within_draw,
264      CopyContext* copy_context,
265      const SkBitmap* bitmap_output,
266      const scoped_refptr<media::VideoFrame>& video_frame_output);
267  bool SynchronousReadbackForCopy(
268      const gfx::Rect& dst_pixel_rect,
269      CopyContext* copy_context,
270      const SkBitmap* bitmap_output,
271      const scoped_refptr<media::VideoFrame>& video_frame_output);
272
273  // Scan the list of started asynchronous copies and test if each one has
274  // completed.
275  void FinishAllCopies();
276  void FinishAllCopiesWithinContext(
277      std::vector<base::Closure>* done_callbacks);
278
279  void CleanupAllCopiesWithinContext();
280  void FailAllCopies();
281
282  gfx::Rect IntersectWithIOSurface(const gfx::Rect& rect,
283                                   float scale_factor) const;
284
285  // Cached pointer to IOSurfaceSupport Singleton.
286  IOSurfaceSupport* io_surface_support_;
287
288  // GL context
289  scoped_nsobject<NSOpenGLContext> glContext_;
290  CGLContextObj cglContext_;  // weak, backed by |glContext_|.
291
292  // IOSurface data.
293  uint64 io_surface_handle_;
294  base::mac::ScopedCFTypeRef<CFTypeRef> io_surface_;
295
296  // The width and height of the io surface.
297  gfx::Size pixel_io_surface_size_;  // In pixels.
298  gfx::Size io_surface_size_;  // In view units.
299
300  // The "live" OpenGL texture referring to this IOSurfaceRef. Note
301  // that per the CGLTexImageIOSurface2D API we do not need to
302  // explicitly update this texture's contents once created. All we
303  // need to do is ensure it is re-bound before attempting to draw
304  // with it.
305  GLuint texture_;
306
307  std::deque<CopyContext> copy_requests_;
308
309  // Timer for finishing a copy operation.
310  base::Timer finish_copy_timer_;
311
312  scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache_;
313  scoped_ptr<CompositingIOSurfaceTransformer> transformer_;
314
315  SurfaceQuad quad_;
316
317  bool is_vsync_disabled_;
318
319  // CVDisplayLink for querying Vsync timing info and throttling swaps.
320  CVDisplayLinkRef display_link_;
321
322  // Timer for stopping display link after a timeout with no swaps.
323  base::DelayTimer<CompositingIOSurfaceMac> display_link_stop_timer_;
324
325  // Lock for sharing data between UI thread and display-link thread.
326  base::Lock lock_;
327
328  // Counts for throttling swaps.
329  int64 vsync_count_;
330  int64 swap_count_;
331
332  // Vsync timing data.
333  base::TimeTicks vsync_timebase_;
334  uint32 vsync_interval_numerator_;
335  uint32 vsync_interval_denominator_;
336
337  bool initialized_is_intel_;
338  bool is_intel_;
339  GLint screen_;
340};
341
342}  // namespace content
343
344#endif  // CONTENT_BROWSER_RENDERER_HOST_COMPOSITING_IOSURFACE_MAC_H_
345