| 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..71427f084a1fccb5f92c4ba7e395244e81c87b7f 100644
|
| --- a/content/browser/compositor/io_surface_layer_mac.mm
|
| +++ b/content/browser/compositor/io_surface_layer_mac.mm
|
| @@ -5,32 +5,66 @@
|
| #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/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 +72,113 @@ 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();
|
| +
|
| + // 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 +186,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 +196,190 @@ 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();
|
| -}
|
| -
|
| -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()));
|
| -}
|
| -
|
| -- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
|
| - if (!context_)
|
| - return [super copyCGLContextForPixelFormat:pixelFormat];
|
| - return CGLRetainContext(context_->cgl_context());
|
| +- (void)releaseCGLContext:(CGLContextObj)glContext {
|
| + io_surface_texture_ = 0;
|
| + cgl_renderer_id_ = 0;
|
| }
|
|
|
| - (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);
|
| + DCHECK_LE(viewport[2], static_cast<GLint>(IOSurfaceGetWidth(io_surface_)));
|
| + DCHECK_LE(viewport[3], static_cast<GLint>(IOSurfaceGetHeight(io_surface_)));
|
| +
|
| + glMatrixMode(GL_PROJECTION);
|
| + glLoadIdentity();
|
| + glOrtho(0, 1, 0, 1, -1, 1);
|
| + glMatrixMode(GL_MODELVIEW);
|
| + glLoadIdentity();
|
| +
|
| + 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(1, 0);
|
| + glTexCoord2f(viewport[2], viewport[3]);
|
| + glVertex2f(1, 1);
|
| + glTexCoord2f(0, viewport[3]);
|
| + glVertex2f(0, 1);
|
| + 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
|
|
|