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 <vector> 10 11 #import <Cocoa/Cocoa.h> 12 #import <QuartzCore/CVDisplayLink.h> 13 #include <QuartzCore/QuartzCore.h> 14 15 #include "base/callback.h" 16 #include "base/mac/scoped_cftyperef.h" 17 #include "base/memory/scoped_ptr.h" 18 #include "base/synchronization/lock.h" 19 #include "base/time/time.h" 20 #include "base/timer/timer.h" 21 #include "media/base/video_frame.h" 22 #include "ui/base/latency_info.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 28 class IOSurfaceSupport; 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: 48 // Returns NULL if IOSurface support is missing or GL APIs fail. Specify in 49 // |order| the desired ordering relationship of the surface to the containing 50 // window. 51 static CompositingIOSurfaceMac* Create( 52 const scoped_refptr<CompositingIOSurfaceContext>& context); 53 ~CompositingIOSurfaceMac(); 54 55 // Set IOSurface that will be drawn on the next NSView drawRect. 56 bool SetIOSurface(uint64 io_surface_handle, 57 const gfx::Size& size, 58 float scale_factor, 59 const ui::LatencyInfo& latency_info); 60 61 // Get the CGL renderer ID currently associated with this context. 62 int GetRendererID(); 63 64 // Blit the IOSurface at the upper-left corner of the of the specified 65 // window_size. If the window size is larger than the IOSurface, the 66 // remaining right and bottom edges will be white. |scaleFactor| is 1 67 // in normal views, 2 in HiDPI views. |frame_subscriber| listens to 68 // this draw event and provides output buffer for copying this frame into. 69 bool DrawIOSurface(const gfx::Size& window_size, 70 float window_scale_factor, 71 RenderWidgetHostViewFrameSubscriber* frame_subscriber, 72 bool using_core_animation); 73 74 // Copy the data of the "live" OpenGL texture referring to this IOSurfaceRef 75 // into |out|. The copied region is specified with |src_pixel_subrect| and 76 // the data is transformed so that it fits in |dst_pixel_size|. 77 // |src_pixel_subrect| and |dst_pixel_size| are not in DIP but in pixel. 78 // Caller must ensure that |out| is allocated to dimensions that match 79 // dst_pixel_size, with no additional padding. 80 // |callback| is invoked when the operation is completed or failed. 81 // Do no call this method again before |callback| is invoked. 82 void CopyTo(const gfx::Rect& src_pixel_subrect, 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 const scoped_refptr<media::VideoFrame>& target, 91 const base::Callback<void(bool)>& callback); 92 93 // Unref the IOSurface and delete the associated GL texture. If the GPU 94 // process is no longer referencing it, this will delete the IOSurface. 95 void UnrefIOSurface(); 96 97 bool HasIOSurface() { return !!io_surface_.get(); } 98 99 const gfx::Size& pixel_io_surface_size() const { 100 return pixel_io_surface_size_; 101 } 102 // In cocoa view units / DIPs. 103 const gfx::Size& dip_io_surface_size() const { return dip_io_surface_size_; } 104 float scale_factor() const { return scale_factor_; } 105 106 bool is_vsync_disabled() const; 107 108 void SetContext( 109 const scoped_refptr<CompositingIOSurfaceContext>& new_context); 110 111 const scoped_refptr<CompositingIOSurfaceContext>& context() { 112 return context_; 113 } 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 // 189 // TODO(miu): Major code refactoring is badly needed! To be done in a 190 // soon-upcoming change. For now, we blatantly violate the style guide with 191 // respect to struct vs. class usage: 192 struct CopyContext { 193 explicit CopyContext(const scoped_refptr<CompositingIOSurfaceContext>& ctx); 194 ~CopyContext(); 195 196 // Delete any references to owned OpenGL objects. This must be called 197 // within the OpenGL context just before destruction. 198 void ReleaseCachedGLObjects(); 199 200 // The following two methods assume |num_outputs| has been set, and are 201 // being called within the OpenGL context. 202 void PrepareReadbackFramebuffers(); 203 void PrepareForAsynchronousReadback(); 204 205 const scoped_ptr<CompositingIOSurfaceTransformer> transformer; 206 GLenum output_readback_format; 207 int num_outputs; 208 GLuint output_textures[3]; // Not owned. 209 // Note: For YUV, the |output_texture_sizes| widths are in terms of 4-byte 210 // quads, not pixels. 211 gfx::Size output_texture_sizes[3]; 212 GLuint frame_buffers[3]; 213 GLuint pixel_buffers[3]; 214 GLuint fence; // When non-zero, doing an asynchronous copy. 215 int cycles_elapsed; 216 base::Callback<bool(const void*, int)> map_buffer_callback; 217 base::Callback<void(bool)> done_callback; 218 }; 219 220 CompositingIOSurfaceMac( 221 IOSurfaceSupport* io_surface_support, 222 const scoped_refptr<CompositingIOSurfaceContext>& context); 223 224 void SetupCVDisplayLink(); 225 226 // If this IOSurface has moved to a different window, use that window's 227 // GL context (if multiple visible windows are using the same GL context 228 // then call to setView call can stall and prevent reaching 60fps). 229 void SwitchToContextOnNewWindow(NSView* view, 230 int window_number); 231 232 bool IsVendorIntel(); 233 234 // Returns true if IOSurface is ready to render. False otherwise. 235 bool MapIOSurfaceToTexture(uint64 io_surface_handle); 236 237 void UnrefIOSurfaceWithContextCurrent(); 238 239 void DrawQuad(const SurfaceQuad& quad); 240 241 // Called on display-link thread. 242 void DisplayLinkTick(CVDisplayLinkRef display_link, 243 const CVTimeStamp* time); 244 245 void CalculateVsyncParametersLockHeld(const CVTimeStamp* time); 246 247 // Prevent from spinning on CGLFlushDrawable when it fails to throttle to 248 // VSync frequency. 249 void RateLimitDraws(); 250 251 void StartOrContinueDisplayLink(); 252 void StopDisplayLink(); 253 254 // Copy current frame to |target| video frame. This method must be called 255 // within a CGL context. Returns a callback that should be called outside 256 // of the CGL context. 257 // If |called_within_draw| is true this method is called within a drawing 258 // operations. This allow certain optimizations. 259 base::Closure CopyToVideoFrameWithinContext( 260 const gfx::Rect& src_subrect, 261 bool called_within_draw, 262 const scoped_refptr<media::VideoFrame>& target, 263 const base::Callback<void(bool)>& callback); 264 265 // Common GPU-readback copy path. Only one of |bitmap_output| or 266 // |video_frame_output| may be specified: Either ARGB is written to 267 // |bitmap_output| or letter-boxed YV12 is written to |video_frame_output|. 268 base::Closure CopyToSelectedOutputWithinContext( 269 const gfx::Rect& src_pixel_subrect, 270 const gfx::Rect& dst_pixel_rect, 271 bool called_within_draw, 272 const SkBitmap* bitmap_output, 273 const scoped_refptr<media::VideoFrame>& video_frame_output, 274 const base::Callback<void(bool)>& done_callback); 275 276 // TODO(hclam): These two methods should be static. 277 void AsynchronousReadbackForCopy( 278 const gfx::Rect& dst_pixel_rect, 279 bool called_within_draw, 280 CopyContext* copy_context, 281 const SkBitmap* bitmap_output, 282 const scoped_refptr<media::VideoFrame>& video_frame_output); 283 bool SynchronousReadbackForCopy( 284 const gfx::Rect& dst_pixel_rect, 285 CopyContext* copy_context, 286 const SkBitmap* bitmap_output, 287 const scoped_refptr<media::VideoFrame>& video_frame_output); 288 289 // Scan the list of started asynchronous copies and test if each one has 290 // completed. If |block_until_finished| is true, then block until all 291 // pending copies are finished. 292 void CheckIfAllCopiesAreFinished(bool block_until_finished); 293 void CheckIfAllCopiesAreFinishedWithinContext( 294 bool block_until_finished, 295 std::vector<base::Closure>* done_callbacks); 296 297 void FailAllCopies(); 298 void DestroyAllCopyContextsWithinContext(); 299 300 // Check for GL errors and store the result in error_. Only return new 301 // errors 302 GLenum GetAndSaveGLError(); 303 304 gfx::Rect IntersectWithIOSurface(const gfx::Rect& rect) const; 305 306 // Cached pointer to IOSurfaceSupport Singleton. 307 IOSurfaceSupport* io_surface_support_; 308 309 // GL context, and parameters for context sharing. This may change when 310 // moving between windows, but will never be NULL. 311 scoped_refptr<CompositingIOSurfaceContext> context_; 312 313 // IOSurface data. 314 uint64 io_surface_handle_; 315 base::ScopedCFTypeRef<CFTypeRef> io_surface_; 316 317 // The width and height of the io surface. 318 gfx::Size pixel_io_surface_size_; // In pixels. 319 gfx::Size dip_io_surface_size_; // In view / density independent pixels. 320 float scale_factor_; 321 322 // The "live" OpenGL texture referring to this IOSurfaceRef. Note 323 // that per the CGLTexImageIOSurface2D API we do not need to 324 // explicitly update this texture's contents once created. All we 325 // need to do is ensure it is re-bound before attempting to draw 326 // with it. 327 GLuint texture_; 328 329 // A pool of CopyContexts with OpenGL objects ready for re-use. Prefer to 330 // pull one from the pool before creating a new CopyContext. 331 std::vector<CopyContext*> copy_context_pool_; 332 333 // CopyContexts being used for in-flight copy operations. 334 std::deque<CopyContext*> copy_requests_; 335 336 // Timer for finishing a copy operation. 337 base::Timer finish_copy_timer_; 338 339 // CVDisplayLink for querying Vsync timing info and throttling swaps. 340 CVDisplayLinkRef display_link_; 341 342 // Timer for stopping display link after a timeout with no swaps. 343 base::DelayTimer<CompositingIOSurfaceMac> display_link_stop_timer_; 344 345 // Lock for sharing data between UI thread and display-link thread. 346 base::Lock lock_; 347 348 // Vsync timing data. 349 base::TimeTicks vsync_timebase_; 350 uint32 vsync_interval_numerator_; 351 uint32 vsync_interval_denominator_; 352 353 bool initialized_is_intel_; 354 bool is_intel_; 355 GLint screen_; 356 357 // Error saved by GetAndSaveGLError 358 GLint gl_error_; 359 360 ui::LatencyInfo latency_info_; 361 }; 362 363 } // namespace content 364 365 #endif // CONTENT_BROWSER_RENDERER_HOST_COMPOSITING_IOSURFACE_MAC_H_ 366