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.
5 #ifndef CONTENT_BROWSER_RENDERER_HOST_COMPOSITING_IOSURFACE_MAC_H_
6 #define CONTENT_BROWSER_RENDERER_HOST_COMPOSITING_IOSURFACE_MAC_H_
12 #import <Cocoa/Cocoa.h>
13 #include <IOSurface/IOSurfaceAPI.h>
14 #include <QuartzCore/QuartzCore.h>
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"
37 class CompositingIOSurfaceContext
;
38 class CompositingIOSurfaceShaderPrograms
;
39 class CompositingIOSurfaceTransformer
;
40 class RenderWidgetHostViewFrameSubscriber
;
41 class RenderWidgetHostViewMac
;
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
> {
49 // Returns NULL if IOSurface or GL API calls fail.
50 static scoped_refptr
<CompositingIOSurfaceMac
> Create();
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
;
59 // Get the CGL renderer ID currently associated with this context.
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.
67 scoped_refptr
<CompositingIOSurfaceContext
> drawing_context
,
68 const gfx::Rect
& window_rect
,
69 float window_scale_factor
) WARN_UNUSED_RESULT
;
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
);
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
);
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();
94 bool HasIOSurface() { return !!io_surface_
.get(); }
96 const gfx::Size
& pixel_io_surface_size() const {
97 return pixel_io_surface_size_
;
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_
; }
103 // Returns true if asynchronous readback is supported on this system.
104 bool IsAsynchronousReadbackSupported();
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
);
111 // Returns true if the offscreen context used by this surface has been
113 bool HasBeenPoisoned() const;
116 friend class base::RefCounted
<CompositingIOSurfaceMac
>;
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
) {
127 void set_position(float x
, float y
) {
131 void set_texcoord(float tx
, float ty
) {
141 // Counter-clockwise verts starting from upper-left corner (0, 0).
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
);
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
);
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
);
169 SurfaceVertex verts_
[4];
172 // Keeps track of states and buffers for readback of IOSurface.
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:
178 explicit CopyContext(const scoped_refptr
<CompositingIOSurfaceContext
>& ctx
);
181 // Delete any references to owned OpenGL objects. This must be called
182 // within the OpenGL context just before destruction.
183 void ReleaseCachedGLObjects();
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();
190 const scoped_ptr
<CompositingIOSurfaceTransformer
> transformer
;
191 GLenum output_readback_format
;
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.
201 base::Callback
<bool(const void*, int)> map_buffer_callback
;
202 base::Callback
<void(bool)> done_callback
;
205 CompositingIOSurfaceMac(
206 const scoped_refptr
<CompositingIOSurfaceContext
>& context
);
207 ~CompositingIOSurfaceMac();
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
,
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
,
220 IOSurfaceID io_surface_handle
) WARN_UNUSED_RESULT
;
222 void UnrefIOSurfaceWithContextCurrent();
224 void DrawQuad(const SurfaceQuad
& quad
);
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
);
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
);
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
);
261 void CheckIfAllCopiesAreFinishedWithinContext(
262 bool block_until_finished
,
263 std::vector
<base::Closure
>* done_callbacks
);
265 void FailAllCopies();
266 void DestroyAllCopyContextsWithinContext();
268 // Check for GL errors and store the result in error_. Only return new
270 GLenum
GetAndSaveGLError();
272 gfx::Rect
IntersectWithIOSurface(const gfx::Rect
& rect
) const;
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_
;
280 IOSurfaceID io_surface_handle_
;
281 base::ScopedCFTypeRef
<IOSurfaceRef
> io_surface_
;
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.
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
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_
;
299 // CopyContexts being used for in-flight copy operations.
300 std::deque
<CopyContext
*> copy_requests_
;
302 // Timer for finishing a copy operation.
303 base::Timer finish_copy_timer_
;
305 // Error saved by GetAndSaveGLError
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.
315 kMaximumUnevictedSurfaces
= 8,
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_
;
323 static void EvictionScheduleDoEvict();
324 static void EvictionDoEvict();
325 static base::LazyInstance
<EvictionQueue
> eviction_queue_
;
326 static bool eviction_scheduled_
;
329 } // namespace content
331 #endif // CONTENT_BROWSER_RENDERER_HOST_COMPOSITING_IOSURFACE_MAC_H_