// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CONTENT_BROWSER_RENDERER_HOST_COMPOSITING_IOSURFACE_MAC_H_ #define CONTENT_BROWSER_RENDERER_HOST_COMPOSITING_IOSURFACE_MAC_H_ #include #include #import #import #include #include "base/callback.h" #include "base/mac/scoped_cftyperef.h" #include "base/memory/scoped_ptr.h" #include "base/synchronization/lock.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "media/base/video_frame.h" #include "ui/events/latency_info.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/rect.h" #include "ui/gfx/rect_conversions.h" #include "ui/gfx/size.h" class IOSurfaceSupport; class SkBitmap; namespace gfx { class Rect; } namespace content { class CompositingIOSurfaceContext; class CompositingIOSurfaceShaderPrograms; class CompositingIOSurfaceTransformer; class RenderWidgetHostViewFrameSubscriber; class RenderWidgetHostViewMac; // This class manages an OpenGL context and IOSurface for the accelerated // compositing code path. The GL context is attached to // RenderWidgetHostViewCocoa for blitting the IOSurface. class CompositingIOSurfaceMac { public: // Returns NULL if IOSurface support is missing or GL APIs fail. static CompositingIOSurfaceMac* Create(); ~CompositingIOSurfaceMac(); // Set IOSurface that will be drawn on the next NSView drawRect. bool SetIOSurfaceWithContextCurrent( scoped_refptr current_context, uint64 io_surface_handle, const gfx::Size& size, float scale_factor, const std::vector& latency_info); // Get the CGL renderer ID currently associated with this context. int GetRendererID(); // Blit the IOSurface to the rectangle specified by |window_rect| in DIPs, // with the origin in the lower left corner. If the window rect's size is // larger than the IOSurface, the remaining right and bottom edges will be // white. |window_scale_factor| is 1 in normal views, 2 in HiDPI views. bool DrawIOSurface( scoped_refptr drawing_context, const gfx::Rect& window_rect, float window_scale_factor, bool flush_drawable); // Copy the data of the "live" OpenGL texture referring to this IOSurfaceRef // into |out|. The copied region is specified with |src_pixel_subrect| and // the data is transformed so that it fits in |dst_pixel_size|. // |src_pixel_subrect| and |dst_pixel_size| are not in DIP but in pixel. // Caller must ensure that |out| is allocated to dimensions that match // dst_pixel_size, with no additional padding. // |callback| is invoked when the operation is completed or failed. // Do no call this method again before |callback| is invoked. void CopyTo(const gfx::Rect& src_pixel_subrect, const gfx::Size& dst_pixel_size, const base::Callback& callback); // Transfer the contents of the surface to an already-allocated YV12 // VideoFrame, and invoke a callback to indicate success or failure. void CopyToVideoFrame( const gfx::Rect& src_subrect, const scoped_refptr& target, const base::Callback& callback); // Unref the IOSurface and delete the associated GL texture. If the GPU // process is no longer referencing it, this will delete the IOSurface. void UnrefIOSurface(); bool HasIOSurface() { return !!io_surface_.get(); } const gfx::Size& pixel_io_surface_size() const { return pixel_io_surface_size_; } // In cocoa view units / DIPs. const gfx::Size& dip_io_surface_size() const { return dip_io_surface_size_; } float scale_factor() const { return scale_factor_; } // Get vsync scheduling parameters. // |interval_numerator/interval_denominator| equates to fractional number of // seconds between vsyncs. void GetVSyncParameters(base::TimeTicks* timebase, uint32* interval_numerator, uint32* interval_denominator); // Returns true if asynchronous readback is supported on this system. bool IsAsynchronousReadbackSupported(); private: friend CVReturn DisplayLinkCallback(CVDisplayLinkRef, const CVTimeStamp*, const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void*); // Vertex structure for use in glDraw calls. struct SurfaceVertex { SurfaceVertex() : x_(0.0f), y_(0.0f), tx_(0.0f), ty_(0.0f) { } void set(float x, float y, float tx, float ty) { x_ = x; y_ = y; tx_ = tx; ty_ = ty; } void set_position(float x, float y) { x_ = x; y_ = y; } void set_texcoord(float tx, float ty) { tx_ = tx; ty_ = ty; } float x_; float y_; float tx_; float ty_; }; // Counter-clockwise verts starting from upper-left corner (0, 0). struct SurfaceQuad { void set_size(gfx::Size vertex_size, gfx::Size texcoord_size) { // Texture coordinates are flipped vertically so they can be drawn on // a projection with a flipped y-axis (origin is top left). float vw = static_cast(vertex_size.width()); float vh = static_cast(vertex_size.height()); float tw = static_cast(texcoord_size.width()); float th = static_cast(texcoord_size.height()); verts_[0].set(0.0f, 0.0f, 0.0f, th); verts_[1].set(0.0f, vh, 0.0f, 0.0f); verts_[2].set(vw, vh, tw, 0.0f); verts_[3].set(vw, 0.0f, tw, th); } void set_rect(float x1, float y1, float x2, float y2) { verts_[0].set_position(x1, y1); verts_[1].set_position(x1, y2); verts_[2].set_position(x2, y2); verts_[3].set_position(x2, y1); } void set_texcoord_rect(float tx1, float ty1, float tx2, float ty2) { // Texture coordinates are flipped vertically so they can be drawn on // a projection with a flipped y-axis (origin is top left). verts_[0].set_texcoord(tx1, ty2); verts_[1].set_texcoord(tx1, ty1); verts_[2].set_texcoord(tx2, ty1); verts_[3].set_texcoord(tx2, ty2); } SurfaceVertex verts_[4]; }; // Keeps track of states and buffers for readback of IOSurface. // // TODO(miu): Major code refactoring is badly needed! To be done in a // soon-upcoming change. For now, we blatantly violate the style guide with // respect to struct vs. class usage: struct CopyContext { explicit CopyContext(const scoped_refptr& ctx); ~CopyContext(); // Delete any references to owned OpenGL objects. This must be called // within the OpenGL context just before destruction. void ReleaseCachedGLObjects(); // The following two methods assume |num_outputs| has been set, and are // being called within the OpenGL context. void PrepareReadbackFramebuffers(); void PrepareForAsynchronousReadback(); const scoped_ptr transformer; GLenum output_readback_format; int num_outputs; GLuint output_textures[3]; // Not owned. // Note: For YUV, the |output_texture_sizes| widths are in terms of 4-byte // quads, not pixels. gfx::Size output_texture_sizes[3]; GLuint frame_buffers[3]; GLuint pixel_buffers[3]; GLuint fence; // When non-zero, doing an asynchronous copy. int cycles_elapsed; base::Callback map_buffer_callback; base::Callback done_callback; }; CompositingIOSurfaceMac( IOSurfaceSupport* io_surface_support, const scoped_refptr& context); void SetupCVDisplayLink(); // If this IOSurface has moved to a different window, use that window's // GL context (if multiple visible windows are using the same GL context // then call to setView call can stall and prevent reaching 60fps). void SwitchToContextOnNewWindow(NSView* view, int window_number); bool IsVendorIntel(); // Returns true if IOSurface is ready to render. False otherwise. bool MapIOSurfaceToTextureWithContextCurrent( const scoped_refptr& current_context, uint64 io_surface_handle); void UnrefIOSurfaceWithContextCurrent(); void DrawQuad(const SurfaceQuad& quad); // Called on display-link thread. void DisplayLinkTick(CVDisplayLinkRef display_link, const CVTimeStamp* time); void CalculateVsyncParametersLockHeld(const CVTimeStamp* time); // Prevent from spinning on CGLFlushDrawable when it fails to throttle to // VSync frequency. void RateLimitDraws(); void StartOrContinueDisplayLink(); void StopDisplayLink(); // Copy current frame to |target| video frame. This method must be called // within a CGL context. Returns a callback that should be called outside // of the CGL context. // If |called_within_draw| is true this method is called within a drawing // operations. This allow certain optimizations. base::Closure CopyToVideoFrameWithinContext( const gfx::Rect& src_subrect, bool called_within_draw, const scoped_refptr& target, const base::Callback& callback); // Common GPU-readback copy path. Only one of |bitmap_output| or // |video_frame_output| may be specified: Either ARGB is written to // |bitmap_output| or letter-boxed YV12 is written to |video_frame_output|. base::Closure CopyToSelectedOutputWithinContext( const gfx::Rect& src_pixel_subrect, const gfx::Rect& dst_pixel_rect, bool called_within_draw, const SkBitmap* bitmap_output, const scoped_refptr& video_frame_output, const base::Callback& done_callback); // TODO(hclam): These two methods should be static. void AsynchronousReadbackForCopy( const gfx::Rect& dst_pixel_rect, bool called_within_draw, CopyContext* copy_context, const SkBitmap* bitmap_output, const scoped_refptr& video_frame_output); bool SynchronousReadbackForCopy( const gfx::Rect& dst_pixel_rect, CopyContext* copy_context, const SkBitmap* bitmap_output, const scoped_refptr& video_frame_output); // Scan the list of started asynchronous copies and test if each one has // completed. If |block_until_finished| is true, then block until all // pending copies are finished. void CheckIfAllCopiesAreFinished(bool block_until_finished); void CheckIfAllCopiesAreFinishedWithinContext( bool block_until_finished, std::vector* done_callbacks); void FailAllCopies(); void DestroyAllCopyContextsWithinContext(); // Check for GL errors and store the result in error_. Only return new // errors GLenum GetAndSaveGLError(); gfx::Rect IntersectWithIOSurface(const gfx::Rect& rect) const; // Cached pointer to IOSurfaceSupport Singleton. IOSurfaceSupport* io_surface_support_; // Offscreen context used for all operations other than drawing to the // screen. This is in the same share group as the contexts used for // drawing, and is the same for all IOSurfaces in all windows. scoped_refptr offscreen_context_; // IOSurface data. uint64 io_surface_handle_; base::ScopedCFTypeRef io_surface_; // The width and height of the io surface. gfx::Size pixel_io_surface_size_; // In pixels. gfx::Size dip_io_surface_size_; // In view / density independent pixels. float scale_factor_; // The "live" OpenGL texture referring to this IOSurfaceRef. Note // that per the CGLTexImageIOSurface2D API we do not need to // explicitly update this texture's contents once created. All we // need to do is ensure it is re-bound before attempting to draw // with it. GLuint texture_; // A pool of CopyContexts with OpenGL objects ready for re-use. Prefer to // pull one from the pool before creating a new CopyContext. std::vector copy_context_pool_; // CopyContexts being used for in-flight copy operations. std::deque copy_requests_; // Timer for finishing a copy operation. base::Timer finish_copy_timer_; // CVDisplayLink for querying Vsync timing info and throttling swaps. CVDisplayLinkRef display_link_; // Timer for stopping display link after a timeout with no swaps. base::DelayTimer display_link_stop_timer_; // Lock for sharing data between UI thread and display-link thread. base::Lock lock_; // Vsync timing data. base::TimeTicks vsync_timebase_; uint32 vsync_interval_numerator_; uint32 vsync_interval_denominator_; // Error saved by GetAndSaveGLError GLint gl_error_; std::vector latency_info_; }; } // namespace content #endif // CONTENT_BROWSER_RENDERER_HOST_COMPOSITING_IOSURFACE_MAC_H_