| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 // Copyright 2014 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 #include "content/common/gpu/image_transport_surface_calayer_mac.h" |  | 
| 6 |  | 
| 7 #include <IOSurface/IOSurface.h> |  | 
| 8 #include <OpenGL/CGLRenderers.h> |  | 
| 9 #include <OpenGL/CGLIOSurface.h> |  | 
| 10 |  | 
| 11 #include "base/command_line.h" |  | 
| 12 #include "base/mac/sdk_forward_declarations.h" |  | 
| 13 #include "base/trace_event/trace_event.h" |  | 
| 14 #include "gpu/config/gpu_info_collector.h" |  | 
| 15 #include "ui/accelerated_widget_mac/surface_handle_types.h" |  | 
| 16 #include "ui/base/cocoa/animation_utils.h" |  | 
| 17 #include "ui/gfx/geometry/dip_util.h" |  | 
| 18 #include "ui/gfx/geometry/size_conversions.h" |  | 
| 19 #include "ui/gl/gl_switches.h" |  | 
| 20 #include "ui/gl/gpu_switching_manager.h" |  | 
| 21 #include "ui/gl/scoped_api.h" |  | 
| 22 |  | 
| 23 namespace { |  | 
| 24 const size_t kFramesToKeepCAContextAfterDiscard = 2; |  | 
| 25 const size_t kCanDrawFalsesBeforeSwitchFromAsync = 4; |  | 
| 26 const base::TimeDelta kMinDeltaToSwitchToAsync = |  | 
| 27     base::TimeDelta::FromSecondsD(1. / 15.); |  | 
| 28 |  | 
| 29 |  | 
| 30 }  // namespace |  | 
| 31 |  | 
| 32 @interface ImageTransportCAOpenGLLayer : CAOpenGLLayer { |  | 
| 33   content::CALayerStorageProvider* storageProvider_; |  | 
| 34   base::Closure didDrawCallback_; |  | 
| 35 |  | 
| 36   // Used to determine if we should use setNeedsDisplay or setAsynchronous to |  | 
| 37   // animate. If the last swap time happened very recently, then |  | 
| 38   // setAsynchronous is used (which allows smooth animation, but comes with the |  | 
| 39   // penalty of the canDrawInCGLContext function waking up the process every |  | 
| 40   // vsync). |  | 
| 41   base::TimeTicks lastSynchronousSwapTime_; |  | 
| 42 |  | 
| 43   // A counter that is incremented whenever LayerCanDraw returns false. If this |  | 
| 44   // reaches a threshold, then |layer_| is switched to synchronous drawing to |  | 
| 45   // save CPU work. |  | 
| 46   uint32 canDrawReturnedFalseCount_; |  | 
| 47 |  | 
| 48   gfx::Size pixelSize_; |  | 
| 49 } |  | 
| 50 |  | 
| 51 - (id)initWithStorageProvider:(content::CALayerStorageProvider*)storageProvider |  | 
| 52                     pixelSize:(gfx::Size)pixelSize |  | 
| 53                   scaleFactor:(float)scaleFactor; |  | 
| 54 - (void)requestDrawNewFrame; |  | 
| 55 - (void)drawPendingFrameImmediately; |  | 
| 56 - (void)resetStorageProvider; |  | 
| 57 @end |  | 
| 58 |  | 
| 59 @implementation ImageTransportCAOpenGLLayer |  | 
| 60 |  | 
| 61 - (id)initWithStorageProvider: |  | 
| 62     (content::CALayerStorageProvider*)storageProvider |  | 
| 63                     pixelSize:(gfx::Size)pixelSize |  | 
| 64                   scaleFactor:(float)scaleFactor { |  | 
| 65   if (self = [super init]) { |  | 
| 66     gfx::Size dipSize = gfx::ConvertSizeToDIP(scaleFactor, pixelSize); |  | 
| 67     [self setContentsScale:scaleFactor]; |  | 
| 68     [self setFrame:CGRectMake(0, 0, dipSize.width(), dipSize.height())]; |  | 
| 69     storageProvider_ = storageProvider; |  | 
| 70     pixelSize_ = pixelSize; |  | 
| 71   } |  | 
| 72   return self; |  | 
| 73 } |  | 
| 74 |  | 
| 75 - (void)requestDrawNewFrame { |  | 
| 76   // This tracing would be more natural to do with a pseudo-thread for each |  | 
| 77   // layer, rather than a counter. |  | 
| 78   // http://crbug.com/366300 |  | 
| 79   // A trace value of 2 indicates that there is a pending swap ack. See |  | 
| 80   // canDrawInCGLContext for other value meanings. |  | 
| 81   TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 2); |  | 
| 82 |  | 
| 83   if (![self isAsynchronous]) { |  | 
| 84     // Switch to asynchronous drawing only if we get two frames in rapid |  | 
| 85     // succession. |  | 
| 86     base::TimeTicks this_swap_time = base::TimeTicks::Now(); |  | 
| 87     base::TimeDelta delta = this_swap_time - lastSynchronousSwapTime_; |  | 
| 88     if (delta <= kMinDeltaToSwitchToAsync) { |  | 
| 89       lastSynchronousSwapTime_ = base::TimeTicks(); |  | 
| 90       [self setAsynchronous:YES]; |  | 
| 91     } else { |  | 
| 92       lastSynchronousSwapTime_ = this_swap_time; |  | 
| 93       [self setNeedsDisplay]; |  | 
| 94     } |  | 
| 95   } |  | 
| 96 } |  | 
| 97 |  | 
| 98 - (void)drawPendingFrameImmediately { |  | 
| 99   DCHECK(storageProvider_->LayerHasPendingDraw()); |  | 
| 100   if ([self isAsynchronous]) |  | 
| 101     [self setAsynchronous:NO]; |  | 
| 102   [self setNeedsDisplay]; |  | 
| 103   [self displayIfNeeded]; |  | 
| 104 } |  | 
| 105 |  | 
| 106 - (void)resetStorageProvider { |  | 
| 107   storageProvider_ = NULL; |  | 
| 108 } |  | 
| 109 |  | 
| 110 - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask { |  | 
| 111   if (!storageProvider_) |  | 
| 112     return NULL; |  | 
| 113   return CGLRetainPixelFormat(CGLGetPixelFormat( |  | 
| 114       storageProvider_->LayerShareGroupContext())); |  | 
| 115 } |  | 
| 116 |  | 
| 117 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat { |  | 
| 118   if (!storageProvider_) |  | 
| 119     return NULL; |  | 
| 120   CGLContextObj context = NULL; |  | 
| 121   CGLError error = CGLCreateContext( |  | 
| 122       pixelFormat, storageProvider_->LayerShareGroupContext(), &context); |  | 
| 123   if (error != kCGLNoError) |  | 
| 124     LOG(ERROR) << "CGLCreateContext failed with CGL error: " << error; |  | 
| 125   return context; |  | 
| 126 } |  | 
| 127 |  | 
| 128 - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext |  | 
| 129                 pixelFormat:(CGLPixelFormatObj)pixelFormat |  | 
| 130                forLayerTime:(CFTimeInterval)timeInterval |  | 
| 131                 displayTime:(const CVTimeStamp*)timeStamp { |  | 
| 132   TRACE_EVENT0("gpu", "CALayerStorageProvider::LayerCanDraw"); |  | 
| 133 |  | 
| 134   if (!storageProvider_) |  | 
| 135     return NO; |  | 
| 136 |  | 
| 137   if (storageProvider_->LayerHasPendingDraw()) { |  | 
| 138     // If there is a draw pending then increase the signal from 2 to 3, to |  | 
| 139     // indicate that there is a swap pending, and CoreAnimation has asked to |  | 
| 140     // draw it. |  | 
| 141     TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 3); |  | 
| 142 |  | 
| 143     canDrawReturnedFalseCount_ = 0; |  | 
| 144     return YES; |  | 
| 145   } else { |  | 
| 146     // If there is not a draw pending, then give an instantaneous blip up from |  | 
| 147     // 0 to 1, indicating that CoreAnimation was ready to draw a frame but we |  | 
| 148     // were not (or didn't have new content to draw). |  | 
| 149     TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 1); |  | 
| 150     TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 0); |  | 
| 151 |  | 
| 152     if ([self isAsynchronous]) { |  | 
| 153       // If we are in asynchronous mode, we will be getting callbacks at every |  | 
| 154       // vsync, asking us if we have anything to draw. If we get many of these |  | 
| 155       // in a row, ask that we stop getting these callback for now, so that we |  | 
| 156       // don't waste CPU cycles. |  | 
| 157       if (canDrawReturnedFalseCount_ >= kCanDrawFalsesBeforeSwitchFromAsync) |  | 
| 158         [self setAsynchronous:NO]; |  | 
| 159       else |  | 
| 160         canDrawReturnedFalseCount_ += 1; |  | 
| 161     } |  | 
| 162     return NO; |  | 
| 163   } |  | 
| 164 } |  | 
| 165 |  | 
| 166 - (void)drawInCGLContext:(CGLContextObj)glContext |  | 
| 167              pixelFormat:(CGLPixelFormatObj)pixelFormat |  | 
| 168             forLayerTime:(CFTimeInterval)timeInterval |  | 
| 169              displayTime:(const CVTimeStamp*)timeStamp { |  | 
| 170   // While in this callback, CoreAnimation has set |glContext| to be current. |  | 
| 171   // Ensure that the GL calls that we make are made against the native GL API. |  | 
| 172   gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; |  | 
| 173 |  | 
| 174   if (storageProvider_) { |  | 
| 175     storageProvider_->LayerDoDraw(gfx::Rect(pixelSize_), false); |  | 
| 176     storageProvider_->LayerUnblockBrowserIfNeeded(); |  | 
| 177     // A trace value of 0 indicates that there is no longer a pending swap ack. |  | 
| 178     TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 0); |  | 
| 179   } else { |  | 
| 180     glClearColor(1, 1, 1, 1); |  | 
| 181     glClear(GL_COLOR_BUFFER_BIT); |  | 
| 182   } |  | 
| 183   [super drawInCGLContext:glContext |  | 
| 184               pixelFormat:pixelFormat |  | 
| 185              forLayerTime:timeInterval |  | 
| 186               displayTime:timeStamp]; |  | 
| 187 } |  | 
| 188 |  | 
| 189 @end |  | 
| 190 |  | 
| 191 namespace content { |  | 
| 192 |  | 
| 193 CALayerStorageProvider::CALayerStorageProvider( |  | 
| 194     ImageTransportSurfaceFBO* transport_surface) |  | 
| 195     : transport_surface_(transport_surface), |  | 
| 196       gpu_vsync_disabled_(base::CommandLine::ForCurrentProcess()->HasSwitch( |  | 
| 197           switches::kDisableGpuVsync)), |  | 
| 198       throttling_disabled_(false), |  | 
| 199       has_pending_ack_(false), |  | 
| 200       fbo_texture_(0), |  | 
| 201       fbo_scale_factor_(1), |  | 
| 202       program_(0), |  | 
| 203       vertex_shader_(0), |  | 
| 204       fragment_shader_(0), |  | 
| 205       position_location_(0), |  | 
| 206       tex_location_(0), |  | 
| 207       vertex_buffer_(0), |  | 
| 208       vertex_array_(0), |  | 
| 209       recreate_layer_after_gpu_switch_(false), |  | 
| 210       pending_draw_weak_factory_(this) { |  | 
| 211   ui::GpuSwitchingManager::GetInstance()->AddObserver(this); |  | 
| 212 } |  | 
| 213 |  | 
| 214 CALayerStorageProvider::~CALayerStorageProvider() { |  | 
| 215   ui::GpuSwitchingManager::GetInstance()->RemoveObserver(this); |  | 
| 216 } |  | 
| 217 |  | 
| 218 gfx::Size CALayerStorageProvider::GetRoundedSize(gfx::Size size) { |  | 
| 219   return size; |  | 
| 220 } |  | 
| 221 |  | 
| 222 bool CALayerStorageProvider::AllocateColorBufferStorage( |  | 
| 223     CGLContextObj context, const base::Closure& context_dirtied_callback, |  | 
| 224     GLuint texture, gfx::Size pixel_size, float scale_factor) { |  | 
| 225   // Allocate an ordinary OpenGL texture to back the FBO. |  | 
| 226   GLenum error; |  | 
| 227   while ((error = glGetError()) != GL_NO_ERROR) { |  | 
| 228     LOG(ERROR) << "OpenGL error hit but ignored before allocating buffer " |  | 
| 229                << "storage: " << error; |  | 
| 230   } |  | 
| 231 |  | 
| 232   if (gfx::GetGLImplementation() == |  | 
| 233       gfx::kGLImplementationDesktopGLCoreProfile) { |  | 
| 234     glTexImage2D(GL_TEXTURE_2D, |  | 
| 235                  0, |  | 
| 236                  GL_RGBA, |  | 
| 237                  pixel_size.width(), |  | 
| 238                  pixel_size.height(), |  | 
| 239                  0, |  | 
| 240                  GL_RGBA, |  | 
| 241                  GL_UNSIGNED_BYTE, |  | 
| 242                  NULL); |  | 
| 243     glFlush(); |  | 
| 244 |  | 
| 245     if (!vertex_shader_) { |  | 
| 246       const char* source = |  | 
| 247           "#version 150\n" |  | 
| 248           "in vec4 position;\n" |  | 
| 249           "out vec2 texcoord;\n" |  | 
| 250           "void main() {\n" |  | 
| 251           "    texcoord = vec2(position.x, position.y);\n" |  | 
| 252           "    gl_Position = vec4(2*position.x-1, 2*position.y-1,\n" |  | 
| 253           "        position.z, position.w);\n" |  | 
| 254           "}\n"; |  | 
| 255       vertex_shader_ = glCreateShader(GL_VERTEX_SHADER); |  | 
| 256       glShaderSource(vertex_shader_, 1, &source, NULL); |  | 
| 257       glCompileShader(vertex_shader_); |  | 
| 258 #if DCHECK_IS_ON() |  | 
| 259       GLint status = GL_FALSE; |  | 
| 260       glGetShaderiv(vertex_shader_, GL_COMPILE_STATUS, &status); |  | 
| 261       DCHECK(status == GL_TRUE); |  | 
| 262 #endif |  | 
| 263     } |  | 
| 264     if (!fragment_shader_) { |  | 
| 265       const char* source = |  | 
| 266           "#version 150\n" |  | 
| 267           "uniform sampler2D tex;\n" |  | 
| 268           "in vec2 texcoord;\n" |  | 
| 269           "out vec4 frag_color;\n" |  | 
| 270           "void main() {\n" |  | 
| 271           "    frag_color = texture(tex, texcoord);\n" |  | 
| 272           "}\n"; |  | 
| 273       fragment_shader_ = glCreateShader(GL_FRAGMENT_SHADER); |  | 
| 274       glShaderSource(fragment_shader_, 1, &source, NULL); |  | 
| 275       glCompileShader(fragment_shader_); |  | 
| 276 #if DCHECK_IS_ON() |  | 
| 277       GLint status = GL_FALSE; |  | 
| 278       glGetShaderiv(fragment_shader_, GL_COMPILE_STATUS, &status); |  | 
| 279       DCHECK(status == GL_TRUE); |  | 
| 280 #endif |  | 
| 281     } |  | 
| 282     if (!program_) { |  | 
| 283       program_ = glCreateProgram(); |  | 
| 284       glAttachShader(program_, vertex_shader_); |  | 
| 285       glAttachShader(program_, fragment_shader_); |  | 
| 286       glBindFragDataLocation(program_, 0, "frag_color"); |  | 
| 287       glLinkProgram(program_); |  | 
| 288 #if DCHECK_IS_ON() |  | 
| 289       GLint status = GL_FALSE; |  | 
| 290       glGetProgramiv(program_, GL_LINK_STATUS, &status); |  | 
| 291       DCHECK(status == GL_TRUE); |  | 
| 292 #endif |  | 
| 293       position_location_ = glGetAttribLocation(program_, "position"); |  | 
| 294       tex_location_ = glGetUniformLocation(program_, "tex"); |  | 
| 295     } |  | 
| 296     if (!vertex_buffer_) { |  | 
| 297       GLfloat vertex_data[24] = { |  | 
| 298         0, 0, 0, 1, |  | 
| 299         1, 0, 0, 1, |  | 
| 300         1, 1, 0, 1, |  | 
| 301         1, 1, 0, 1, |  | 
| 302         0, 1, 0, 1, |  | 
| 303         0, 0, 0, 1, |  | 
| 304       }; |  | 
| 305       glGenBuffersARB(1, &vertex_buffer_); |  | 
| 306       // If the allocation path used GLContext::RestoreStateIfDirtiedExternally |  | 
| 307       // as the draw path does, this manual state restoration would not be |  | 
| 308       // necessary. |  | 
| 309       GLint bound_buffer = 0; |  | 
| 310       glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &bound_buffer); |  | 
| 311       glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); |  | 
| 312       glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), |  | 
| 313                    vertex_data, GL_STATIC_DRAW); |  | 
| 314       glBindBuffer(GL_ARRAY_BUFFER, bound_buffer); |  | 
| 315     } |  | 
| 316     if (!vertex_array_) { |  | 
| 317       // If the allocation path used GLContext::RestoreStateIfDirtiedExternally |  | 
| 318       // as the draw path does, this manual state restoration would not be |  | 
| 319       // necessary. |  | 
| 320       GLint bound_vao = 0; |  | 
| 321       glGetIntegerv(GL_VERTEX_ARRAY_BINDING_OES, &bound_vao); |  | 
| 322       glGenVertexArraysOES(1, &vertex_array_); |  | 
| 323       glBindVertexArrayOES(vertex_array_); |  | 
| 324       { |  | 
| 325         glEnableVertexAttribArray(position_location_); |  | 
| 326         GLint bound_buffer = 0; |  | 
| 327         glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &bound_buffer); |  | 
| 328         glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); |  | 
| 329         glVertexAttribPointer(position_location_, 4, GL_FLOAT, GL_FALSE, 0, 0); |  | 
| 330         glBindBuffer(GL_ARRAY_BUFFER, bound_buffer); |  | 
| 331       } |  | 
| 332       glBindVertexArrayOES(bound_vao); |  | 
| 333     } |  | 
| 334   } else { |  | 
| 335     glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, |  | 
| 336                  0, |  | 
| 337                  GL_RGBA, |  | 
| 338                  pixel_size.width(), |  | 
| 339                  pixel_size.height(), |  | 
| 340                  0, |  | 
| 341                  GL_RGBA, |  | 
| 342                  GL_UNSIGNED_BYTE, |  | 
| 343                  NULL); |  | 
| 344     glFlush(); |  | 
| 345   } |  | 
| 346 |  | 
| 347   bool hit_error = false; |  | 
| 348   while ((error = glGetError()) != GL_NO_ERROR) { |  | 
| 349     LOG(ERROR) << "OpenGL error hit while trying to allocate buffer storage: " |  | 
| 350                << error; |  | 
| 351     hit_error = true; |  | 
| 352   } |  | 
| 353   if (hit_error) |  | 
| 354     return false; |  | 
| 355 |  | 
| 356   // Set the parameters that will be used to allocate the CALayer to draw the |  | 
| 357   // texture into. |  | 
| 358   share_group_context_.reset(CGLRetainContext(context)); |  | 
| 359   share_group_context_dirtied_callback_ = context_dirtied_callback; |  | 
| 360   fbo_texture_ = texture; |  | 
| 361   fbo_pixel_size_ = pixel_size; |  | 
| 362   fbo_scale_factor_ = scale_factor; |  | 
| 363   return true; |  | 
| 364 } |  | 
| 365 |  | 
| 366 void CALayerStorageProvider::FreeColorBufferStorage() { |  | 
| 367   if (gfx::GetGLImplementation() == |  | 
| 368       gfx::kGLImplementationDesktopGLCoreProfile) { |  | 
| 369     if (vertex_shader_) |  | 
| 370       glDeleteShader(vertex_shader_); |  | 
| 371     if (fragment_shader_) |  | 
| 372       glDeleteShader(fragment_shader_); |  | 
| 373     if (program_) |  | 
| 374       glDeleteProgram(program_); |  | 
| 375     if (vertex_buffer_) |  | 
| 376       glDeleteBuffersARB(1, &vertex_buffer_); |  | 
| 377     if (vertex_array_) |  | 
| 378       glDeleteVertexArraysOES(1, &vertex_array_); |  | 
| 379     vertex_shader_ = 0; |  | 
| 380     fragment_shader_ = 0; |  | 
| 381     program_ = 0; |  | 
| 382     vertex_buffer_ = 0; |  | 
| 383     vertex_array_ = 0; |  | 
| 384   } |  | 
| 385 |  | 
| 386   // Note that |context_| still holds a reference to |layer_|, and will until |  | 
| 387   // a new frame is swapped in. |  | 
| 388   ResetLayer(); |  | 
| 389 |  | 
| 390   share_group_context_.reset(); |  | 
| 391   share_group_context_dirtied_callback_ = base::Closure(); |  | 
| 392   fbo_texture_ = 0; |  | 
| 393   fbo_pixel_size_ = gfx::Size(); |  | 
| 394 } |  | 
| 395 |  | 
| 396 void CALayerStorageProvider::FrameSizeChanged(const gfx::Size& pixel_size, |  | 
| 397                                               float scale_factor) { |  | 
| 398   DCHECK_EQ(fbo_pixel_size_.ToString(), pixel_size.ToString()); |  | 
| 399   DCHECK_EQ(fbo_scale_factor_, scale_factor); |  | 
| 400 } |  | 
| 401 |  | 
| 402 void CALayerStorageProvider::SwapBuffers(const gfx::Rect& dirty_rect) { |  | 
| 403   TRACE_EVENT0("gpu", "CALayerStorageProvider::SwapBuffers"); |  | 
| 404   DCHECK(!has_pending_ack_); |  | 
| 405 |  | 
| 406   // Recreate the CALayer on the new GPU if a GPU switch has occurred. Note |  | 
| 407   // that the CAContext will retain a reference to the old CALayer until the |  | 
| 408   // call to -[CAContext setLayer:] replaces the old CALayer with the new one. |  | 
| 409   if (recreate_layer_after_gpu_switch_) { |  | 
| 410     ResetLayer(); |  | 
| 411     recreate_layer_after_gpu_switch_ = false; |  | 
| 412   } |  | 
| 413 |  | 
| 414   // Set the pending draw flag only after potentially destroying the old layer |  | 
| 415   // (otherwise destroying it will un-set the flag). |  | 
| 416   has_pending_ack_ = true; |  | 
| 417 |  | 
| 418   // Allocate a CAContext to use to transport the CALayer to the browser |  | 
| 419   // process, if needed. |  | 
| 420   if (!context_) { |  | 
| 421     base::scoped_nsobject<NSDictionary> dict([[NSDictionary alloc] init]); |  | 
| 422     CGSConnectionID connection_id = CGSMainConnectionID(); |  | 
| 423     context_.reset([CAContext contextWithCGSConnection:connection_id |  | 
| 424                                                options:dict]); |  | 
| 425     [context_ retain]; |  | 
| 426   } |  | 
| 427 |  | 
| 428   // Create the appropriate CALayer (if needed) and request that it draw. |  | 
| 429   bool should_draw_immediately = gpu_vsync_disabled_ || throttling_disabled_; |  | 
| 430   CreateLayerAndRequestDraw(should_draw_immediately, dirty_rect); |  | 
| 431 |  | 
| 432   // CoreAnimation may not call the function to un-block the browser in a |  | 
| 433   // timely manner (or ever). Post a task to force the draw and un-block |  | 
| 434   // the browser (at the next cycle through the run-loop if drawing is to |  | 
| 435   // be immediate, and at a timeout of 1/6th of a second otherwise). |  | 
| 436   if (has_pending_ack_) { |  | 
| 437     base::MessageLoop::current()->PostDelayedTask( |  | 
| 438         FROM_HERE, |  | 
| 439         base::Bind(&CALayerStorageProvider::DrawImmediatelyAndUnblockBrowser, |  | 
| 440                    pending_draw_weak_factory_.GetWeakPtr()), |  | 
| 441         should_draw_immediately ? base::TimeDelta() : |  | 
| 442                                   base::TimeDelta::FromSeconds(1) / 6); |  | 
| 443   } |  | 
| 444 } |  | 
| 445 |  | 
| 446 void CALayerStorageProvider::CreateLayerAndRequestDraw( |  | 
| 447     bool should_draw_immediately, const gfx::Rect& dirty_rect) { |  | 
| 448   if (!ca_opengl_layer_) { |  | 
| 449     ca_opengl_layer_.reset([[ImageTransportCAOpenGLLayer alloc] |  | 
| 450         initWithStorageProvider:this |  | 
| 451                       pixelSize:fbo_pixel_size_ |  | 
| 452                     scaleFactor:fbo_scale_factor_]); |  | 
| 453   } |  | 
| 454 |  | 
| 455   // -[CAOpenGLLayer drawInCGLContext] won't get called until we're in the |  | 
| 456   // visible layer hierarchy, so call setLayer: immediately, to make this |  | 
| 457   // happen. |  | 
| 458   [context_ setLayer:ca_opengl_layer_]; |  | 
| 459 |  | 
| 460   // Tell CoreAnimation to draw our frame. Note that sometimes, calling |  | 
| 461   // -[CAContext setLayer:] will result in the layer getting an immediate |  | 
| 462   // draw. If that happend, we're done. |  | 
| 463   if (!should_draw_immediately && has_pending_ack_) { |  | 
| 464     [ca_opengl_layer_ requestDrawNewFrame]; |  | 
| 465   } |  | 
| 466 } |  | 
| 467 |  | 
| 468 void CALayerStorageProvider::DrawImmediatelyAndUnblockBrowser() { |  | 
| 469   DCHECK(has_pending_ack_); |  | 
| 470 |  | 
| 471   if (ca_opengl_layer_) { |  | 
| 472     // Beware that sometimes, the setNeedsDisplay+displayIfNeeded pairs have no |  | 
| 473     // effect. This can happen if the NSView that this layer is attached to |  | 
| 474     // isn't in the window hierarchy (e.g, tab capture of a backgrounded tab). |  | 
| 475     // In this case, the frame will never be seen, so drop it. |  | 
| 476     [ca_opengl_layer_ drawPendingFrameImmediately]; |  | 
| 477   } |  | 
| 478 |  | 
| 479   UnblockBrowserIfNeeded(); |  | 
| 480 } |  | 
| 481 |  | 
| 482 void CALayerStorageProvider::WillWriteToBackbuffer() { |  | 
| 483   // The browser should always throttle itself so that there are no pending |  | 
| 484   // draws when the output surface is written to, but in the event of things |  | 
| 485   // like context lost, or changing context, this will not be true. If there |  | 
| 486   // exists a pending draw, flush it immediately to maintain a consistent |  | 
| 487   // state. |  | 
| 488   if (has_pending_ack_) |  | 
| 489     DrawImmediatelyAndUnblockBrowser(); |  | 
| 490 } |  | 
| 491 |  | 
| 492 void CALayerStorageProvider::DiscardBackbuffer() { |  | 
| 493   // If this surface's backbuffer is discarded, it is because this surface has |  | 
| 494   // been made non-visible. Ensure that the previous contents are not briefly |  | 
| 495   // flashed when this is made visible by creating a new CALayer and CAContext |  | 
| 496   // at the next swap. |  | 
| 497   ResetLayer(); |  | 
| 498 |  | 
| 499   // If we remove all references to the CAContext in this process, it will be |  | 
| 500   // blanked-out in the browser process (even if the browser process is inside |  | 
| 501   // a disable screen updates block). Ensure that the context is kept around |  | 
| 502   // until a fixed number of frames (determined empirically) have been acked. |  | 
| 503   // http://crbug.com/425819 |  | 
| 504   while (previously_discarded_contexts_.size() < |  | 
| 505       kFramesToKeepCAContextAfterDiscard) { |  | 
| 506     previously_discarded_contexts_.push_back( |  | 
| 507         base::scoped_nsobject<CAContext>()); |  | 
| 508   } |  | 
| 509   previously_discarded_contexts_.push_back(context_); |  | 
| 510 |  | 
| 511   context_.reset(); |  | 
| 512 } |  | 
| 513 |  | 
| 514 void CALayerStorageProvider::SwapBuffersAckedByBrowser( |  | 
| 515     bool disable_throttling) { |  | 
| 516   TRACE_EVENT0("gpu", "CALayerStorageProvider::SwapBuffersAckedByBrowser"); |  | 
| 517   throttling_disabled_ = disable_throttling; |  | 
| 518   if (!previously_discarded_contexts_.empty()) |  | 
| 519     previously_discarded_contexts_.pop_front(); |  | 
| 520 } |  | 
| 521 |  | 
| 522 CGLContextObj CALayerStorageProvider::LayerShareGroupContext() { |  | 
| 523   return share_group_context_; |  | 
| 524 } |  | 
| 525 |  | 
| 526 base::Closure CALayerStorageProvider::LayerShareGroupContextDirtiedCallback() { |  | 
| 527   return share_group_context_dirtied_callback_; |  | 
| 528 } |  | 
| 529 |  | 
| 530 void CALayerStorageProvider::LayerDoDraw( |  | 
| 531     const gfx::Rect& dirty_rect, bool flipped) { |  | 
| 532   TRACE_EVENT0("gpu", "CALayerStorageProvider::LayerDoDraw"); |  | 
| 533   if (gfx::GetGLImplementation() == |  | 
| 534       gfx::kGLImplementationDesktopGLCoreProfile) { |  | 
| 535     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |  | 
| 536     glClearColor(1, 0, 1, 1); |  | 
| 537     glClear(GL_COLOR_BUFFER_BIT); |  | 
| 538     glDisable(GL_BLEND); |  | 
| 539     glDisable(GL_CULL_FACE); |  | 
| 540     glDisable(GL_DEPTH_TEST); |  | 
| 541     glDisable(GL_STENCIL_TEST); |  | 
| 542     glDisable(GL_SCISSOR_TEST); |  | 
| 543 |  | 
| 544     DCHECK(glIsProgram(program_)); |  | 
| 545     glUseProgram(program_); |  | 
| 546     glBindVertexArrayOES(vertex_array_); |  | 
| 547 |  | 
| 548     glActiveTexture(GL_TEXTURE0); |  | 
| 549     glBindTexture(GL_TEXTURE_2D, fbo_texture_); |  | 
| 550     glUniform1i(tex_location_, 0); |  | 
| 551 |  | 
| 552     glDisable(GL_CULL_FACE); |  | 
| 553     glDrawArrays(GL_TRIANGLES, 0, 6); |  | 
| 554     glBindVertexArrayOES(0); |  | 
| 555     glUseProgram(0); |  | 
| 556   } else { |  | 
| 557     GLint viewport[4] = {0, 0, 0, 0}; |  | 
| 558     glGetIntegerv(GL_VIEWPORT, viewport); |  | 
| 559     gfx::Size viewport_size(viewport[2], viewport[3]); |  | 
| 560 |  | 
| 561     // Set the coordinate system to be one-to-one with pixels. |  | 
| 562     glMatrixMode(GL_PROJECTION); |  | 
| 563     glLoadIdentity(); |  | 
| 564     if (flipped) |  | 
| 565       glOrtho(0, viewport_size.width(), viewport_size.height(), 0, -1, 1); |  | 
| 566     else |  | 
| 567       glOrtho(0, viewport_size.width(), 0, viewport_size.height(), -1, 1); |  | 
| 568     glMatrixMode(GL_MODELVIEW); |  | 
| 569     glLoadIdentity(); |  | 
| 570 |  | 
| 571     // Reset drawing state and draw a fullscreen quad. |  | 
| 572     glUseProgram(0); |  | 
| 573     glDisable(GL_BLEND); |  | 
| 574     glDisable(GL_CULL_FACE); |  | 
| 575     glDisable(GL_DEPTH_TEST); |  | 
| 576     glDisable(GL_STENCIL_TEST); |  | 
| 577     glDisable(GL_SCISSOR_TEST); |  | 
| 578     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |  | 
| 579     glColor4f(1, 1, 1, 1); |  | 
| 580     glActiveTexture(GL_TEXTURE0); |  | 
| 581     glEnable(GL_TEXTURE_RECTANGLE_ARB); |  | 
| 582     glBindTexture(GL_TEXTURE_RECTANGLE_ARB, fbo_texture_); |  | 
| 583     glBegin(GL_QUADS); |  | 
| 584     { |  | 
| 585       glTexCoord2f(dirty_rect.x(), dirty_rect.y()); |  | 
| 586       glVertex2f(dirty_rect.x(), dirty_rect.y()); |  | 
| 587 |  | 
| 588       glTexCoord2f(dirty_rect.x(), dirty_rect.bottom()); |  | 
| 589       glVertex2f(dirty_rect.x(), dirty_rect.bottom()); |  | 
| 590 |  | 
| 591       glTexCoord2f(dirty_rect.right(), dirty_rect.bottom()); |  | 
| 592       glVertex2f(dirty_rect.right(), dirty_rect.bottom()); |  | 
| 593 |  | 
| 594       glTexCoord2f(dirty_rect.right(), dirty_rect.y()); |  | 
| 595       glVertex2f(dirty_rect.right(), dirty_rect.y()); |  | 
| 596     } |  | 
| 597     glEnd(); |  | 
| 598     glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); |  | 
| 599     glDisable(GL_TEXTURE_RECTANGLE_ARB); |  | 
| 600   } |  | 
| 601 |  | 
| 602   GLint current_renderer_id = 0; |  | 
| 603   if (CGLGetParameter(CGLGetCurrentContext(), |  | 
| 604                       kCGLCPCurrentRendererID, |  | 
| 605                       ¤t_renderer_id) == kCGLNoError) { |  | 
| 606     current_renderer_id &= kCGLRendererIDMatchingMask; |  | 
| 607     transport_surface_->SetRendererID(current_renderer_id); |  | 
| 608   } |  | 
| 609 |  | 
| 610   GLenum error; |  | 
| 611   while ((error = glGetError()) != GL_NO_ERROR) { |  | 
| 612     LOG(ERROR) << "OpenGL error hit while drawing frame: " << error; |  | 
| 613   } |  | 
| 614 } |  | 
| 615 |  | 
| 616 void CALayerStorageProvider::LayerUnblockBrowserIfNeeded() { |  | 
| 617   UnblockBrowserIfNeeded(); |  | 
| 618 } |  | 
| 619 |  | 
| 620 bool CALayerStorageProvider::LayerHasPendingDraw() const { |  | 
| 621   return has_pending_ack_; |  | 
| 622 } |  | 
| 623 |  | 
| 624 void CALayerStorageProvider::OnGpuSwitched() { |  | 
| 625   recreate_layer_after_gpu_switch_ = true; |  | 
| 626 } |  | 
| 627 |  | 
| 628 void CALayerStorageProvider::UnblockBrowserIfNeeded() { |  | 
| 629   if (!has_pending_ack_) |  | 
| 630     return; |  | 
| 631   pending_draw_weak_factory_.InvalidateWeakPtrs(); |  | 
| 632   has_pending_ack_ = false; |  | 
| 633   transport_surface_->SendSwapBuffers( |  | 
| 634       ui::SurfaceHandleFromCAContextID([context_ contextId]), |  | 
| 635       fbo_pixel_size_, |  | 
| 636       fbo_scale_factor_); |  | 
| 637 } |  | 
| 638 |  | 
| 639 void CALayerStorageProvider::ResetLayer() { |  | 
| 640   if (ca_opengl_layer_) { |  | 
| 641     [ca_opengl_layer_ resetStorageProvider]; |  | 
| 642     // If we are providing back-pressure by waiting for a draw, that draw will |  | 
| 643     // now never come, so release the pressure now. |  | 
| 644     UnblockBrowserIfNeeded(); |  | 
| 645     ca_opengl_layer_.reset(); |  | 
| 646   } |  | 
| 647 } |  | 
| 648 |  | 
| 649 }  //  namespace content |  | 
| OLD | NEW | 
|---|