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