// 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_ACCELERATED_COMPOSITING_VIEW_MAC_H #define CONTENT_BROWSER_RENDERER_HOST_ACCELERATED_COMPOSITING_VIEW_MAC_H #import <Cocoa/Cocoa.h> #import <QuartzCore/CVDisplayLink.h> #include <QuartzCore/QuartzCore.h> #include "base/callback.h" #include "base/mac/scoped_cftyperef.h" #include "base/memory/scoped_nsobject.h" #include "base/synchronization/lock.h" #include "base/time.h" #include "base/timer.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/rect.h" #include "ui/gfx/size.h" class IOSurfaceSupport; namespace gfx { class Rect; } namespace content { // 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: // Passed to Create() to specify the ordering of the surface relative to the // containing window. enum SurfaceOrder { SURFACE_ORDER_ABOVE_WINDOW, SURFACE_ORDER_BELOW_WINDOW }; // Returns NULL if IOSurface support is missing or GL APIs fail. Specify in // |order| the desired ordering relationship of the surface to the containing // window. static CompositingIOSurfaceMac* Create(SurfaceOrder order); ~CompositingIOSurfaceMac(); // Set IOSurface that will be drawn on the next NSView drawRect. void SetIOSurface(uint64 io_surface_handle, const gfx::Size& size); // Blit the IOSurface at the upper-left corner of the |view|. If |view| window // size is larger than the IOSurface, the remaining right and bottom edges // will be white. |scaleFactor| is 1 in normal views, 2 in HiDPI views. void DrawIOSurface(NSView* view, float scale_factor); // 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 with the size no less than // |4 * dst_pixel_size.width() * dst_pixel_size.height()| bytes. // |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, void* out, const base::Callback<void(bool)>& 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(); // Call when globalFrameDidChange is received on the NSView. void GlobalFrameDidChange(); // Disassociate the GL context with the NSView and unref the IOSurface. Do // this to switch to software drawing mode. void ClearDrawable(); 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& io_surface_size() const { return io_surface_size_; } bool is_vsync_disabled() const { return is_vsync_disabled_; } // 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); 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<float>(vertex_size.width()); float vh = static_cast<float>(vertex_size.height()); float tw = static_cast<float>(texcoord_size.width()); float th = static_cast<float>(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 asynchronous readback of IOSurface. struct CopyContext { CopyContext(); ~CopyContext(); void Reset() { started = false; cycles_elapsed = 0; frame_buffer = 0; frame_buffer_texture = 0; pixel_buffer = 0; use_fence = false; fence = 0; out_buf = NULL; callback.Reset(); } bool started; int cycles_elapsed; GLuint frame_buffer; GLuint frame_buffer_texture; GLuint pixel_buffer; bool use_fence; GLuint fence; gfx::Rect src_rect; gfx::Size dest_size; void* out_buf; base::Callback<void(bool)> callback; }; CompositingIOSurfaceMac(IOSurfaceSupport* io_surface_support, NSOpenGLContext* glContext, CGLContextObj cglContext, GLuint shader_program_blit_rgb, GLint blit_rgb_sampler_location, GLuint shader_program_white, bool is_vsync_disabled, CVDisplayLinkRef display_link); // Returns true if IOSurface is ready to render. False otherwise. bool MapIOSurfaceToTexture(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(); // Two implementations of CopyTo() in synchronous and asynchronous mode. bool SynchronousCopyTo(const gfx::Rect& src_pixel_subrect, const gfx::Size& dst_pixel_size, void* out); bool AsynchronousCopyTo(const gfx::Rect& src_pixel_subrect, const gfx::Size& dst_pixel_size, void* out, const base::Callback<void(bool)>& callback); void FinishCopy(); void CleanupResourcesForCopy(); // Cached pointer to IOSurfaceSupport Singleton. IOSurfaceSupport* io_surface_support_; // GL context scoped_nsobject<NSOpenGLContext> glContext_; CGLContextObj cglContext_; // weak, backed by |glContext_|. // IOSurface data. uint64 io_surface_handle_; base::mac::ScopedCFTypeRef<CFTypeRef> io_surface_; // The width and height of the io surface. gfx::Size pixel_io_surface_size_; // In pixels. gfx::Size io_surface_size_; // In view units. // 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_; CopyContext copy_context_; // Timer for finishing a copy operation. base::RepeatingTimer<CompositingIOSurfaceMac> copy_timer_; // Shader parameters. GLuint shader_program_blit_rgb_; GLint blit_rgb_sampler_location_; GLuint shader_program_white_; SurfaceQuad quad_; bool is_vsync_disabled_; // 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<CompositingIOSurfaceMac> display_link_stop_timer_; // Lock for sharing data between UI thread and display-link thread. base::Lock lock_; // Counts for throttling swaps. int64 vsync_count_; int64 swap_count_; // Vsync timing data. base::TimeTicks vsync_timebase_; uint32 vsync_interval_numerator_; uint32 vsync_interval_denominator_; }; } // namespace content #endif // CONTENT_BROWSER_RENDERER_HOST_ACCELERATED_COMPOSITING_VIEW_MAC_H