 Chromium Code Reviews
 Chromium Code Reviews Issue 490393002:
  Simplify IOSurface CoreAnimation code: Part 2  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 490393002:
  Simplify IOSurface CoreAnimation code: Part 2  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| Index: content/browser/compositor/io_surface_layer_mac.mm | 
| diff --git a/content/browser/compositor/io_surface_layer_mac.mm b/content/browser/compositor/io_surface_layer_mac.mm | 
| index 6693dc19b3d9ba9d4f55ea033eb2a2ffaa97bcc5..1262386d41e544e367754db266939bec00d6b511 100644 | 
| --- a/content/browser/compositor/io_surface_layer_mac.mm | 
| +++ b/content/browser/compositor/io_surface_layer_mac.mm | 
| @@ -5,32 +5,67 @@ | 
| #include "content/browser/compositor/io_surface_layer_mac.h" | 
| #include <CoreFoundation/CoreFoundation.h> | 
| -#include <OpenGL/gl.h> | 
| +#include <OpenGL/CGLIOSurface.h> | 
| +#include <OpenGL/CGLRenderers.h> | 
| +#include <OpenGL/OpenGL.h> | 
| #include "base/mac/mac_util.h" | 
| #include "base/mac/sdk_forward_declarations.h" | 
| #include "content/browser/renderer_host/render_widget_host_impl.h" | 
| #include "content/browser/renderer_host/render_widget_host_view_mac.h" | 
| -#include "content/browser/renderer_host/compositing_iosurface_context_mac.h" | 
| -#include "content/browser/renderer_host/compositing_iosurface_mac.h" | 
| #include "ui/base/cocoa/animation_utils.h" | 
| #include "ui/gfx/size_conversions.h" | 
| +#include "ui/gl/scoped_cgl.h" | 
| #include "ui/gl/gpu_switching_manager.h" | 
| +// Convenience macro for checking errors in the below code. | 
| +#define CHECK_GL_ERROR() do { \ | 
| + GLenum gl_error = glGetError(); \ | 
| + LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error: " << gl_error; \ | 
| + } while (0) | 
| + | 
| +//////////////////////////////////////////////////////////////////////////////// | 
| +// IOSurfaceLayer(Private) | 
| + | 
| +@interface IOSurfaceLayer(Private) | 
| +// Force a draw immediately, but only if one was requested. | 
| +- (void)displayIfNeededAndAck; | 
| + | 
| +// Called when it has been a fixed interval of time and a frame has yet to be | 
| +// drawn. | 
| +- (void)timerFired; | 
| +@end | 
| + | 
| //////////////////////////////////////////////////////////////////////////////// | 
| // IOSurfaceLayerHelper | 
| namespace content { | 
| +// IOSurfaceLayerHelper provides C++ functionality needed for the | 
| +// IOSurfaceLayer class (interfacing with base::DelayTimer). | 
| +class IOSurfaceLayerHelper { | 
| + public: | 
| + IOSurfaceLayerHelper(IOSurfaceLayer* layer); | 
| + ~IOSurfaceLayerHelper(); | 
| + void ResetTimer(); | 
| + | 
| + private: | 
| + void TimerFired(); | 
| + | 
| + // The layer that owns this helper. | 
| + IOSurfaceLayer* const layer_; | 
| + | 
| + // The browser places back-pressure on the GPU by not acknowledging swap | 
| + // calls until they appear on the screen. This can lead to hangs if the | 
| + // view is moved offscreen (among other things). Prevent hangs by always | 
| + // acknowledging the frame after timeout of 1/6th of a second has passed. | 
| + base::DelayTimer<IOSurfaceLayerHelper> timer_; | 
| +}; | 
| + | 
| + | 
| IOSurfaceLayerHelper::IOSurfaceLayerHelper( | 
| - IOSurfaceLayerClient* client, | 
| IOSurfaceLayer* layer) | 
| - : client_(client), | 
| - layer_(layer), | 
| - needs_display_(false), | 
| - has_pending_frame_(false), | 
| - did_not_draw_counter_(0), | 
| - is_pumping_frames_(false), | 
| + : layer_(layer), | 
| timer_( | 
| FROM_HERE, | 
| base::TimeDelta::FromSeconds(1) / 6, | 
| @@ -38,37 +73,115 @@ IOSurfaceLayerHelper::IOSurfaceLayerHelper( | 
| &IOSurfaceLayerHelper::TimerFired) {} | 
| IOSurfaceLayerHelper::~IOSurfaceLayerHelper() { | 
| +} | 
| + | 
| +void IOSurfaceLayerHelper::ResetTimer() { | 
| + timer_.Reset(); | 
| +} | 
| + | 
| +void IOSurfaceLayerHelper::TimerFired() { | 
| + [layer_ timerFired]; | 
| +} | 
| + | 
| +} // namespace content | 
| + | 
| +//////////////////////////////////////////////////////////////////////////////// | 
| +// IOSurfaceLayer | 
| + | 
| +@implementation IOSurfaceLayer | 
| + | 
| +- (id)initWithClient:(content::IOSurfaceLayerClient*)client | 
| + withScaleFactor:(float)scale_factor { | 
| + if (self = [super init]) { | 
| + client_ = client; | 
| + helper_.reset(new content::IOSurfaceLayerHelper(self)); | 
| + needs_display_ = false; | 
| + has_pending_frame_ = false; | 
| + did_not_draw_counter_ = 0; | 
| + is_pumping_frames_ = false; | 
| + io_surface_texture_ = 0; | 
| + io_surface_texture_dirty_ = false; | 
| + cgl_renderer_id_ = 0; | 
| + | 
| + [self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)]; | 
| + [self setAnchorPoint:CGPointMake(0, 0)]; | 
| + // Setting contents gravity is necessary to prevent the layer from being | 
| + // scaled during dyanmic resizes (especially with devtools open). | 
| + [self setContentsGravity:kCAGravityTopLeft]; | 
| + if ([self respondsToSelector:(@selector(setContentsScale:))]) | 
| + [self setContentsScale:scale_factor]; | 
| + } | 
| + return self; | 
| +} | 
| + | 
| +- (void)dealloc { | 
| + DCHECK(!helper_ && !client_); | 
| + [super dealloc]; | 
| +} | 
| + | 
| +- (float)scaleFactor { | 
| + if ([self respondsToSelector:(@selector(contentsScale))]) | 
| + return [self contentsScale]; | 
| + return 1; | 
| +} | 
| + | 
| +- (int)rendererID { | 
| + return cgl_renderer_id_; | 
| +} | 
| + | 
| +- (void)timerFired { | 
| + [self displayIfNeededAndAck]; | 
| +} | 
| + | 
| +- (void)resetClient { | 
| // Any acks that were waiting on this layer to draw will not occur, so ack | 
| // them now to prevent blocking the renderer. | 
| - AckPendingFrame(true); | 
| + [self ackPendingFrame]; | 
| + helper_.reset(); | 
| + client_ = NULL; | 
| } | 
| -void IOSurfaceLayerHelper::GotNewFrame() { | 
| +- (void)gotFrameWithIOSurface:(IOSurfaceID)io_surface_id | 
| + withPixelSize:(gfx::Size)pixel_size | 
| + withScaleFactor:(float)scale_factor { | 
| // A trace value of 2 indicates that there is a pending swap ack. See | 
| // canDrawInCGLContext for other value meanings. | 
| - TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, 2); | 
| - | 
| + TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, 2); | 
| has_pending_frame_ = true; | 
| needs_display_ = true; | 
| - timer_.Reset(); | 
| + helper_->ResetTimer(); | 
| + | 
| + frame_pixel_size_ = pixel_size; | 
| + | 
| + // If this is a new IOSurface, open the IOSurface and mark that the | 
| + // GL texture needs to bind to the new surface. | 
| + if (!io_surface_ || io_surface_id != IOSurfaceGetID(io_surface_)) { | 
| + io_surface_.reset(IOSurfaceLookup(io_surface_id)); | 
| + io_surface_texture_dirty_ = true; | 
| + if (!io_surface_) { | 
| + LOG(ERROR) << "Failed to open IOSurface for frame"; | 
| + if (client_) | 
| + client_->IOSurfaceLayerHitError(); | 
| + } | 
| + } | 
| // If reqested, draw immediately and don't bother trying to use the | 
| // isAsynchronous property to ensure smooth animation. If this is while | 
| // frames are being pumped then ack and display immediately to get a | 
| // correct-sized frame displayed as soon as possible. | 
| - if (is_pumping_frames_ || client_->IOSurfaceLayerShouldAckImmediately()) { | 
| - SetNeedsDisplayAndDisplayAndAck(); | 
| + if (is_pumping_frames_ || | 
| + (client_ && client_->IOSurfaceLayerShouldAckImmediately())) { | 
| + [self setNeedsDisplayAndDisplayAndAck]; | 
| } else { | 
| - if (![layer_ isAsynchronous]) | 
| - [layer_ setAsynchronous:YES]; | 
| + if (![self isAsynchronous]) | 
| + [self setAsynchronous:YES]; | 
| } | 
| } | 
| -void IOSurfaceLayerHelper::SetNeedsDisplay() { | 
| - needs_display_ = true; | 
| -} | 
| - | 
| -bool IOSurfaceLayerHelper::CanDraw() { | 
| +- (BOOL)canDrawInCGLContext:(CGLContextObj)glContext | 
| + pixelFormat:(CGLPixelFormatObj)pixelFormat | 
| + forLayerTime:(CFTimeInterval)timeInterval | 
| + displayTime:(const CVTimeStamp*)timeStamp { | 
| // If we return NO 30 times in a row, switch to being synchronous to avoid | 
| // burning CPU cycles on this callback. | 
| if (needs_display_) { | 
| @@ -76,7 +189,7 @@ bool IOSurfaceLayerHelper::CanDraw() { | 
| } else { | 
| did_not_draw_counter_ += 1; | 
| if (did_not_draw_counter_ == 30) | 
| - [layer_ setAsynchronous:NO]; | 
| + [self setAsynchronous:NO]; | 
| } | 
| // Add an instantaneous blip to the PendingSwapAck state to indicate | 
| @@ -86,239 +199,222 @@ bool IOSurfaceLayerHelper::CanDraw() { | 
| // pending swap ack) indicates that we did not request a draw. This would | 
| // be more natural to do with a tracing pseudo-thread | 
| // http://crbug.com/366300 | 
| - TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, needs_display_ ? 3 : 1); | 
| - TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, | 
| + TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, needs_display_ ? 3 : 1); | 
| + TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, | 
| has_pending_frame_ ? 2 : 0); | 
| return needs_display_; | 
| } | 
| -void IOSurfaceLayerHelper::DidDraw(bool success) { | 
| - needs_display_ = false; | 
| - AckPendingFrame(success); | 
| -} | 
| - | 
| -void IOSurfaceLayerHelper::AckPendingFrame(bool success) { | 
| +- (void)ackPendingFrame { | 
| if (!has_pending_frame_) | 
| return; | 
| has_pending_frame_ = false; | 
| - if (success) | 
| + if (client_) | 
| client_->IOSurfaceLayerDidDrawFrame(); | 
| - else | 
| - client_->IOSurfaceLayerHitError(); | 
| // A trace value of 0 indicates that there is no longer a pending swap ack. | 
| - TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, 0); | 
| + TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, 0); | 
| } | 
| -void IOSurfaceLayerHelper::SetNeedsDisplayAndDisplayAndAck() { | 
| +- (void)setNeedsDisplayAndDisplayAndAck { | 
| // Drawing using setNeedsDisplay and displayIfNeeded will result in | 
| // subsequent canDrawInCGLContext callbacks getting dropped, and jerky | 
| // animation. Disable asynchronous drawing before issuing these calls as a | 
| // workaround. | 
| // http://crbug.com/395827 | 
| - if ([layer_ isAsynchronous]) | 
| - [layer_ setAsynchronous:NO]; | 
| + if ([self isAsynchronous]) | 
| + [self setAsynchronous:NO]; | 
| - [layer_ setNeedsDisplay]; | 
| - DisplayIfNeededAndAck(); | 
| + [self setNeedsDisplay]; | 
| + [self displayIfNeededAndAck]; | 
| } | 
| -void IOSurfaceLayerHelper::DisplayIfNeededAndAck() { | 
| +- (void)displayIfNeededAndAck { | 
| if (!needs_display_) | 
| return; | 
| // As in SetNeedsDisplayAndDisplayAndAck, disable asynchronous drawing before | 
| // issuing displayIfNeeded. | 
| // http://crbug.com/395827 | 
| - if ([layer_ isAsynchronous]) | 
| - [layer_ setAsynchronous:NO]; | 
| + if ([self isAsynchronous]) | 
| + [self setAsynchronous:NO]; | 
| // Do not bother drawing while pumping new frames -- wait until the waiting | 
| // block ends to draw any of the new frames. | 
| if (!is_pumping_frames_) | 
| - [layer_ displayIfNeeded]; | 
| + [self displayIfNeeded]; | 
| // Calls to setNeedsDisplay can sometimes be ignored, especially if issued | 
| // rapidly (e.g, with vsync off). This is unacceptable because the failure | 
| // to ack a single frame will hang the renderer. Ensure that the renderer | 
| // not be blocked by lying and claiming that we drew the frame. | 
| - AckPendingFrame(true); | 
| -} | 
| - | 
| -void IOSurfaceLayerHelper::TimerFired() { | 
| - SetNeedsDisplayAndDisplayAndAck(); | 
| 
ccameron
2014/09/21 09:16:59
*** this is wrong -- it needs to be DisplayIfNeede
 | 
| -} | 
| - | 
| -void IOSurfaceLayerHelper::BeginPumpingFrames() { | 
| - is_pumping_frames_ = true; | 
| -} | 
| - | 
| -void IOSurfaceLayerHelper::EndPumpingFrames() { | 
| - is_pumping_frames_ = false; | 
| - DisplayIfNeededAndAck(); | 
| -} | 
| - | 
| -} // namespace content | 
| - | 
| -//////////////////////////////////////////////////////////////////////////////// | 
| -// IOSurfaceLayer | 
| - | 
| -@implementation IOSurfaceLayer | 
| - | 
| -- (content::CompositingIOSurfaceMac*)iosurface { | 
| - return iosurface_.get(); | 
| -} | 
| - | 
| -- (content::CompositingIOSurfaceContext*)context { | 
| - return context_.get(); | 
| -} | 
| - | 
| -- (id)initWithClient:(content::IOSurfaceLayerClient*)client | 
| - withScaleFactor:(float)scale_factor { | 
| - if (self = [super init]) { | 
| - helper_.reset(new content::IOSurfaceLayerHelper(client, self)); | 
| - | 
| - iosurface_ = content::CompositingIOSurfaceMac::Create(); | 
| - context_ = content::CompositingIOSurfaceContext::Get( | 
| - content::CompositingIOSurfaceContext::kCALayerContextWindowNumber); | 
| - if (!iosurface_ || !context_) { | 
| - LOG(ERROR) << "Failed create CompositingIOSurface or context"; | 
| - [self resetClient]; | 
| - [self release]; | 
| - return nil; | 
| - } | 
| - | 
| - [self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)]; | 
| - [self setAnchorPoint:CGPointMake(0, 0)]; | 
| - // Setting contents gravity is necessary to prevent the layer from being | 
| - // scaled during dyanmic resizes (especially with devtools open). | 
| - [self setContentsGravity:kCAGravityTopLeft]; | 
| - if ([self respondsToSelector:(@selector(setContentsScale:))]) { | 
| - [self setContentsScale:scale_factor]; | 
| - } | 
| - } | 
| - return self; | 
| -} | 
| - | 
| -- (void)dealloc { | 
| - DCHECK(!helper_); | 
| - [super dealloc]; | 
| -} | 
| - | 
| -- (bool)gotFrameWithIOSurface:(IOSurfaceID)io_surface_id | 
| - withPixelSize:(gfx::Size)pixel_size | 
| - withScaleFactor:(float)scale_factor { | 
| - bool result = true; | 
| - gfx::ScopedCGLSetCurrentContext scoped_set_current_context( | 
| - context_->cgl_context()); | 
| - result = iosurface_->SetIOSurfaceWithContextCurrent( | 
| - context_, io_surface_id, pixel_size, scale_factor); | 
| - return result; | 
| -} | 
| - | 
| -- (void)poisonContextAndSharegroup { | 
| - context_->PoisonContextAndSharegroup(); | 
| -} | 
| - | 
| -- (bool)hasBeenPoisoned { | 
| - return context_->HasBeenPoisoned(); | 
| -} | 
| - | 
| -- (float)scaleFactor { | 
| - return iosurface_->scale_factor(); | 
| -} | 
| - | 
| -- (int)rendererID { | 
| - return iosurface_->GetRendererID(); | 
| -} | 
| - | 
| -- (void)resetClient { | 
| - helper_.reset(); | 
| -} | 
| - | 
| -- (void)gotNewFrame { | 
| - helper_->GotNewFrame(); | 
| -} | 
| - | 
| -- (void)setNeedsDisplayAndDisplayAndAck { | 
| - helper_->SetNeedsDisplayAndDisplayAndAck(); | 
| -} | 
| - | 
| -- (void)displayIfNeededAndAck { | 
| - helper_->DisplayIfNeededAndAck(); | 
| + [self ackPendingFrame]; | 
| } | 
| - (void)beginPumpingFrames { | 
| - helper_->BeginPumpingFrames(); | 
| + is_pumping_frames_ = true; | 
| } | 
| - (void)endPumpingFrames { | 
| - helper_->EndPumpingFrames(); | 
| + is_pumping_frames_ = false; | 
| + [self displayIfNeededAndAck]; | 
| } | 
| // The remaining methods implement the CAOpenGLLayer interface. | 
| - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask { | 
| - if (!context_) | 
| - return [super copyCGLPixelFormatForDisplayMask:mask]; | 
| - return CGLRetainPixelFormat(CGLGetPixelFormat(context_->cgl_context())); | 
| + // Create the pixel format object for the context. | 
| + std::vector<CGLPixelFormatAttribute> attribs; | 
| + attribs.push_back(kCGLPFADepthSize); | 
| + attribs.push_back(static_cast<CGLPixelFormatAttribute>(0)); | 
| + if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus()) { | 
| + attribs.push_back(kCGLPFAAllowOfflineRenderers); | 
| + attribs.push_back(static_cast<CGLPixelFormatAttribute>(1)); | 
| + } | 
| + attribs.push_back(static_cast<CGLPixelFormatAttribute>(0)); | 
| + GLint number_virtual_screens = 0; | 
| + base::ScopedTypeRef<CGLPixelFormatObj> pixel_format; | 
| + CGLError error = CGLChoosePixelFormat( | 
| + &attribs.front(), pixel_format.InitializeInto(), &number_virtual_screens); | 
| + if (error != kCGLNoError) { | 
| + LOG(ERROR) << "Failed to create pixel format object."; | 
| + return NULL; | 
| + } | 
| + return CGLRetainPixelFormat(pixel_format); | 
| } | 
| -- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat { | 
| - if (!context_) | 
| - return [super copyCGLContextForPixelFormat:pixelFormat]; | 
| - return CGLRetainContext(context_->cgl_context()); | 
| +- (void)releaseCGLContext:(CGLContextObj)glContext { | 
| + // The GL context is being destroyed, so mark the resources as needing to be | 
| + // recreated. | 
| + io_surface_texture_ = 0; | 
| + io_surface_texture_dirty_ = true; | 
| + cgl_renderer_id_ = 0; | 
| + [super releaseCGLContext:glContext]; | 
| } | 
| - (void)setNeedsDisplay { | 
| - if (helper_) | 
| - helper_->SetNeedsDisplay(); | 
| + needs_display_ = true; | 
| [super setNeedsDisplay]; | 
| } | 
| -- (BOOL)canDrawInCGLContext:(CGLContextObj)glContext | 
| - pixelFormat:(CGLPixelFormatObj)pixelFormat | 
| - forLayerTime:(CFTimeInterval)timeInterval | 
| - displayTime:(const CVTimeStamp*)timeStamp { | 
| - if (helper_) | 
| - return helper_->CanDraw(); | 
| - return NO; | 
| -} | 
| - | 
| - (void)drawInCGLContext:(CGLContextObj)glContext | 
| pixelFormat:(CGLPixelFormatObj)pixelFormat | 
| forLayerTime:(CFTimeInterval)timeInterval | 
| displayTime:(const CVTimeStamp*)timeStamp { | 
| TRACE_EVENT0("browser", "IOSurfaceLayer::drawInCGLContext"); | 
| - if (!iosurface_->HasIOSurface() || context_->cgl_context() != glContext) { | 
| + // Create the texture if it has not been created in this context yet. | 
| + if (!io_surface_texture_) { | 
| + glGenTextures(1, &io_surface_texture_); | 
| + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, io_surface_texture_); | 
| + glTexParameteri( | 
| + GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 
| + glTexParameteri( | 
| + GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | 
| + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); | 
| + io_surface_texture_dirty_ = true; | 
| + } | 
| + | 
| + // Associate the IOSurface with this texture, if the underlying IOSurface has | 
| + // been changed. | 
| + if (io_surface_texture_dirty_ && io_surface_) { | 
| + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, io_surface_texture_); | 
| + CGLError cgl_error = CGLTexImageIOSurface2D( | 
| + glContext, | 
| + GL_TEXTURE_RECTANGLE_ARB, | 
| + GL_RGBA, | 
| + IOSurfaceGetWidth(io_surface_), | 
| + IOSurfaceGetHeight(io_surface_), | 
| + GL_BGRA, | 
| + GL_UNSIGNED_INT_8_8_8_8_REV, | 
| + io_surface_.get(), | 
| + 0 /* plane */); | 
| + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); | 
| + if (cgl_error != kCGLNoError) { | 
| + LOG(ERROR) << "CGLTexImageIOSurface2D failed with " << cgl_error; | 
| + glDeleteTextures(1, &io_surface_texture_); | 
| + io_surface_texture_ = 0; | 
| + if (client_) | 
| + client_->IOSurfaceLayerHitError(); | 
| + } | 
| + } else if (io_surface_texture_) { | 
| + glDeleteTextures(1, &io_surface_texture_); | 
| + io_surface_texture_ = 0; | 
| + } | 
| + | 
| + // Fill the viewport with the texture. The viewport must be smaller or equal | 
| + // than the texture, because it is resized as frames arrive. | 
| + if (io_surface_texture_) { | 
| + GLint viewport[4]; | 
| + glGetIntegerv(GL_VIEWPORT, viewport); | 
| + gfx::Size viewport_pixel_size(viewport[2], viewport[3]); | 
| + DCHECK_LE( | 
| + viewport_pixel_size.width(), | 
| + frame_pixel_size_.width()); | 
| + DCHECK_LE( | 
| + viewport_pixel_size.height(), | 
| + frame_pixel_size_.height()); | 
| + | 
| + glMatrixMode(GL_PROJECTION); | 
| + glLoadIdentity(); | 
| + glOrtho(0, viewport_pixel_size.width(), | 
| + 0, viewport_pixel_size.height(), -1, 1); | 
| + glMatrixMode(GL_MODELVIEW); | 
| + glLoadIdentity(); | 
| + | 
| + glColor4f(1, 1, 1, 1); | 
| + glEnable(GL_TEXTURE_RECTANGLE_ARB); | 
| + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, io_surface_texture_); | 
| + glBegin(GL_QUADS); | 
| + glTexCoord2f(0, 0); | 
| + glVertex2f(0, 0); | 
| + glTexCoord2f(viewport[2], 0); | 
| + glVertex2f(frame_pixel_size_.width(), 0); | 
| + glTexCoord2f(viewport[2], viewport[3]); | 
| + glVertex2f(frame_pixel_size_.width(), frame_pixel_size_.height()); | 
| + glTexCoord2f(0, viewport[3]); | 
| + glVertex2f(0, frame_pixel_size_.height()); | 
| + glEnd(); | 
| + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); | 
| + glDisable(GL_TEXTURE_RECTANGLE_ARB); | 
| + | 
| + // Workaround for issue 158469. Issue a dummy draw call with | 
| + // io_surface_texture_ not bound to a texture, in order to shake all | 
| + // references to the IOSurface out of the driver. | 
| + glBegin(GL_TRIANGLES); | 
| + glEnd(); | 
| + } else { | 
| glClearColor(1, 1, 1, 1); | 
| glClear(GL_COLOR_BUFFER_BIT); | 
| - return; | 
| } | 
| - // The correct viewport to cover the layer will be set up by the caller. | 
| - // Transform this into a window size for DrawIOSurface, where it will be | 
| - // transformed back into this viewport. | 
| - GLint viewport[4]; | 
| - glGetIntegerv(GL_VIEWPORT, viewport); | 
| - gfx::Rect window_rect(viewport[0], viewport[1], viewport[2], viewport[3]); | 
| - float window_scale_factor = 1.f; | 
| - if ([self respondsToSelector:(@selector(contentsScale))]) | 
| - window_scale_factor = [self contentsScale]; | 
| - window_rect = ToNearestRect( | 
| - gfx::ScaleRect(window_rect, 1.f/window_scale_factor)); | 
| - | 
| - bool draw_succeeded = iosurface_->DrawIOSurface( | 
| - context_, window_rect, window_scale_factor); | 
| + // Query the current GL renderer to send back to the GPU process. | 
| + { | 
| + CGLError cgl_error = CGLGetParameter( | 
| + glContext, kCGLCPCurrentRendererID, &cgl_renderer_id_); | 
| + if (cgl_error == kCGLNoError) { | 
| + cgl_renderer_id_ &= kCGLRendererIDMatchingMask; | 
| + } else { | 
| + LOG(ERROR) << "CGLGetParameter for kCGLCPCurrentRendererID failed with " | 
| + << cgl_error; | 
| + cgl_renderer_id_ = 0; | 
| + } | 
| + } | 
| - if (helper_) | 
| - helper_->DidDraw(draw_succeeded); | 
| + // If we hit any errors, tell the client. | 
| + while (GLenum gl_error = glGetError()) { | 
| + LOG(ERROR) << "Hit GL error " << gl_error; | 
| + if (client_) | 
| + client_->IOSurfaceLayerHitError(); | 
| + } | 
| + needs_display_ = false; | 
| [super drawInCGLContext:glContext | 
| pixelFormat:pixelFormat | 
| forLayerTime:timeInterval | 
| displayTime:timeStamp]; | 
| + | 
| + [self ackPendingFrame]; | 
| } | 
| @end |