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