Lots of missing frames in YouTube360 when fullscreen on MacBook
authordino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 6 Oct 2017 06:37:16 +0000 (06:37 +0000)
committerdino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 6 Oct 2017 06:37:16 +0000 (06:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=177903
<rdar://problem/33273300>

Reviewed by Sam Weinig.

Source/WebCore:

Our compositing path for WebGL on macOS was too slow, requiring a copy
of the framebuffer into another GL context. Replace this by having
WebGL render into a texture that is backed by an IOSurface, and then
set the WebGLLayer to use the IOSurface as contents.

Covered by the existing WebGL tests.

* platform/graphics/GraphicsContext3D.h:
(WebCore::GraphicsContext3D::platformTexture const): We no longer use the
framebuffer object outside the class, so change this to return the GL texture
that the framebuffer is rendering in to. It was kind-of strange that it was
named this way originally.
Also make endPaint available on macOS, and add the definitions for
createIOSurfaceBackingStore and updateFramebufferTextureBackingStoreFromLayer.

* platform/graphics/cocoa/GraphicsContext3DCocoa.mm:
(WebCore::GraphicsContext3D::GraphicsContext3D): Now that we're using an IOSurface,
we're binding to a new attachment point, GL_TEXTURE_RECTANGLE.
(WebCore::GraphicsContext3D::endPaint): This is now being called on macOS and iOS,
so add a comment that explains the extra work that iOS needs to do. At some future
point it would be nice to make this slightly cleaner, so that iOS and macOS are
more similar.
(WebCore::GraphicsContext3D::allocateIOSurfaceBackingStore): New function that calls
into the corresponding WebGLLayer function.
(WebCore::GraphicsContext3D::updateFramebufferTextureBackingStoreFromLayer): Ditto.

* platform/graphics/opengl/GraphicsContext3DOpenGL.cpp:
(WebCore::wipeAlphaChannelFromPixels): Both readPixels and drawing a WebGL context
into another buffer need to fill out the alpha channel if this context was
created without one, otherwise the IOSurface backing store will happily provide
what might be non-zero values.
(WebCore::GraphicsContext3D::readPixelsAndConvertToBGRAIfNecessary): Call the helper above.
(WebCore::GraphicsContext3D::reshapeFBOs): Add more code to call into the macOS-specific
function to use an IOSurface as the framebuffer texture.
(WebCore::GraphicsContext3D::readPixels): Call the helper above.

* platform/graphics/ca/cocoa/PlatformCALayerCocoa.mm:
(PlatformCALayerCocoa::copyContentsFromLayer): Replace the use of the
deprecated setContentsChanged with reloadValueForKeyPath.

* platform/graphics/cocoa/WebGLLayer.h: The macOS implementation now
inherits from CALayer directly rather than CAOpenGLLayer. It also adds
a few member variables to handle the IOSurfaces used for triple buffering.

* platform/graphics/cocoa/WebGLLayer.mm:
(-[WebGLLayer initWithGraphicsContext3D:]): If we were created without an
alpha channel, tell CA that we're an opaque layer. Also set the layer's transform
to identity, so that it calls into the code below to flip the contents.
(-[WebGLLayer setTransform:]): Because an IOSurface is used for the layer contents,
we don't get a chance to flip the drawing the way we do via the drawInContext delegate.
Instead we have to apply a scale(1, -1) transform on top of the layer transform to
make sure the layer is rendered right-way up.
(-[WebGLLayer setAnchorPoint:]): Ditto, except we have to assume the anchor point is
at the bottom of the layer, so flip the Y value.
(-[WebGLLayer display]): Swap between the drawing buffer and the contents buffer, and
then get a new buffer ready for display.
(createAppropriateIOSurface): Helper.
(-[WebGLLayer allocateIOSurfaceBackingStoreWithSize:usingAlpha:]): Initializes the
IOSurfaces used for drawing buffers.
(-[WebGLLayer bindFramebufferToNextAvailableSurface]): Take the next available IOSurface and
make it the drawing buffer (binding in to WebGL at the same time).
(-[WebGLLayer copyCGLPixelFormatForDisplayMask:]): Deleted.
(-[WebGLLayer copyCGLContextForPixelFormat:]): Deleted.
(-[WebGLLayer drawInCGLContext:pixelFormat:forLayerTime:displayTime:]): Deleted.

* platform/graphics/mac/WebLayer.mm: Remove the definition of reloadValueForKeyPath.

Source/WebCore/PAL:

Add reloadValueForKeyPath to replace setContentsChanged on CALayer.

* pal/spi/cocoa/QuartzCoreSPI.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@222961 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/PAL/ChangeLog
Source/WebCore/PAL/pal/spi/cocoa/QuartzCoreSPI.h
Source/WebCore/platform/graphics/GraphicsContext3D.h
Source/WebCore/platform/graphics/ca/cocoa/PlatformCALayerCocoa.mm
Source/WebCore/platform/graphics/cocoa/GraphicsContext3DCocoa.mm
Source/WebCore/platform/graphics/cocoa/WebGLLayer.h
Source/WebCore/platform/graphics/cocoa/WebGLLayer.mm
Source/WebCore/platform/graphics/mac/WebLayer.mm
Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp

index 6c852c3..7a3d9b1 100644 (file)
@@ -1513,3 +1513,5 @@ imported/w3c/web-platform-tests/css/css-pseudo-4/first-letter-002.html [ ImageOn
 imported/w3c/web-platform-tests/css/css-pseudo-4/first-letter-003.html [ ImageOnlyFailure ]
 
 webkit.org/b/177799 accessibility/table-detection.html [ Pass Failure ]
+
+webkit.org/b/177997 webgl/1.0.2/conformance/textures/copy-tex-image-2d-formats.html [ Pass Failure ]
index 60a36e2..38c96cd 100644 (file)
@@ -1,3 +1,78 @@
+2017-10-05  Dean Jackson  <dino@apple.com>
+
+        Lots of missing frames in YouTube360 when fullscreen on MacBook
+        https://bugs.webkit.org/show_bug.cgi?id=177903
+        <rdar://problem/33273300>
+
+        Reviewed by Sam Weinig.
+
+        Our compositing path for WebGL on macOS was too slow, requiring a copy
+        of the framebuffer into another GL context. Replace this by having
+        WebGL render into a texture that is backed by an IOSurface, and then
+        set the WebGLLayer to use the IOSurface as contents.
+
+        Covered by the existing WebGL tests.
+
+        * platform/graphics/GraphicsContext3D.h:
+        (WebCore::GraphicsContext3D::platformTexture const): We no longer use the
+        framebuffer object outside the class, so change this to return the GL texture
+        that the framebuffer is rendering in to. It was kind-of strange that it was
+        named this way originally.
+        Also make endPaint available on macOS, and add the definitions for
+        createIOSurfaceBackingStore and updateFramebufferTextureBackingStoreFromLayer.
+
+        * platform/graphics/cocoa/GraphicsContext3DCocoa.mm:
+        (WebCore::GraphicsContext3D::GraphicsContext3D): Now that we're using an IOSurface,
+        we're binding to a new attachment point, GL_TEXTURE_RECTANGLE.
+        (WebCore::GraphicsContext3D::endPaint): This is now being called on macOS and iOS,
+        so add a comment that explains the extra work that iOS needs to do. At some future
+        point it would be nice to make this slightly cleaner, so that iOS and macOS are
+        more similar.
+        (WebCore::GraphicsContext3D::allocateIOSurfaceBackingStore): New function that calls
+        into the corresponding WebGLLayer function.
+        (WebCore::GraphicsContext3D::updateFramebufferTextureBackingStoreFromLayer): Ditto.
+
+        * platform/graphics/opengl/GraphicsContext3DOpenGL.cpp:
+        (WebCore::wipeAlphaChannelFromPixels): Both readPixels and drawing a WebGL context
+        into another buffer need to fill out the alpha channel if this context was
+        created without one, otherwise the IOSurface backing store will happily provide
+        what might be non-zero values.
+        (WebCore::GraphicsContext3D::readPixelsAndConvertToBGRAIfNecessary): Call the helper above.
+        (WebCore::GraphicsContext3D::reshapeFBOs): Add more code to call into the macOS-specific
+        function to use an IOSurface as the framebuffer texture.
+        (WebCore::GraphicsContext3D::readPixels): Call the helper above.
+
+        * platform/graphics/ca/cocoa/PlatformCALayerCocoa.mm:
+        (PlatformCALayerCocoa::copyContentsFromLayer): Replace the use of the
+        deprecated setContentsChanged with reloadValueForKeyPath.
+
+        * platform/graphics/cocoa/WebGLLayer.h: The macOS implementation now
+        inherits from CALayer directly rather than CAOpenGLLayer. It also adds
+        a few member variables to handle the IOSurfaces used for triple buffering.
+
+        * platform/graphics/cocoa/WebGLLayer.mm:
+        (-[WebGLLayer initWithGraphicsContext3D:]): If we were created without an
+        alpha channel, tell CA that we're an opaque layer. Also set the layer's transform
+        to identity, so that it calls into the code below to flip the contents.
+        (-[WebGLLayer setTransform:]): Because an IOSurface is used for the layer contents,
+        we don't get a chance to flip the drawing the way we do via the drawInContext delegate.
+        Instead we have to apply a scale(1, -1) transform on top of the layer transform to
+        make sure the layer is rendered right-way up.
+        (-[WebGLLayer setAnchorPoint:]): Ditto, except we have to assume the anchor point is
+        at the bottom of the layer, so flip the Y value.
+        (-[WebGLLayer display]): Swap between the drawing buffer and the contents buffer, and
+        then get a new buffer ready for display.
+        (createAppropriateIOSurface): Helper.
+        (-[WebGLLayer allocateIOSurfaceBackingStoreWithSize:usingAlpha:]): Initializes the
+        IOSurfaces used for drawing buffers.
+        (-[WebGLLayer bindFramebufferToNextAvailableSurface]): Take the next available IOSurface and
+        make it the drawing buffer (binding in to WebGL at the same time).
+        (-[WebGLLayer copyCGLPixelFormatForDisplayMask:]): Deleted.
+        (-[WebGLLayer copyCGLContextForPixelFormat:]): Deleted.
+        (-[WebGLLayer drawInCGLContext:pixelFormat:forLayerTime:displayTime:]): Deleted.
+
+        * platform/graphics/mac/WebLayer.mm: Remove the definition of reloadValueForKeyPath.
+
 2017-10-05  Frederic Wang  <fwang@igalia.com>
 
         Update Source/ThirdParty/woff2 to 22c256bc457777744ba14b7325a6e8e0e7dec91c
index 9c6bacd..acfc0bf 100644 (file)
@@ -1,3 +1,15 @@
+2017-10-05  Dean Jackson  <dino@apple.com>
+
+        Lots of missing frames in YouTube360 when fullscreen on MacBook
+        https://bugs.webkit.org/show_bug.cgi?id=177903
+        <rdar://problem/33273300>
+
+        Reviewed by Sam Weinig.
+
+        Add reloadValueForKeyPath to replace setContentsChanged on CALayer.
+
+        * pal/spi/cocoa/QuartzCoreSPI.h:
+
 2017-10-05  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r222951 and r222952.
index 83b233a..2a1906b 100644 (file)
@@ -91,7 +91,7 @@ typedef struct _CARenderContext CARenderContext;
 - (void)setContextId:(uint32_t)contextID;
 - (CGSize)size;
 - (void *)regionBeingDrawn;
-- (void)setContentsChanged;
+- (void)reloadValueForKeyPath:(NSString *)keyPath;
 @property BOOL allowsGroupBlending;
 @property BOOL canDrawConcurrently;
 @property BOOL contentsOpaque;
index 666dd84..d901010 100644 (file)
@@ -747,7 +747,7 @@ public:
 
 #if PLATFORM(COCOA)
     PlatformGraphicsContext3D platformGraphicsContext3D() const { return m_contextObj; }
-    Platform3DObject platformTexture() const { return m_fbo; }
+    Platform3DObject platformTexture() const { return m_texture; }
     CALayer* platformLayer() const { return reinterpret_cast<CALayer*>(m_webGLLayer.get()); }
 #else
     PlatformGraphicsContext3D platformGraphicsContext3D();
@@ -1147,12 +1147,16 @@ public:
     RefPtr<ImageData> paintRenderingResultsToImageData();
     bool paintCompositedResultsToCanvas(ImageBuffer*);
 
-#if PLATFORM(IOS)
+#if PLATFORM(COCOA)
     void endPaint();
 #endif
+
 #if PLATFORM(MAC)
+    void allocateIOSurfaceBackingStore(IntSize);
+    void updateFramebufferTextureBackingStoreFromLayer();
     void updateCGLContext();
 #endif
+
     void setContextVisibility(bool);
 
     GraphicsContext3DPowerPreference powerPreferenceUsedForCreation() const { return m_powerPreferenceUsedForCreation; }
index 56ed009..68dc577 100644 (file)
@@ -411,7 +411,7 @@ void PlatformCALayerCocoa::copyContentsFromLayer(PlatformCALayer* layer)
     if ([m_layer contents] != [caLayer contents])
         [m_layer setContents:[caLayer contents]];
     else
-        [m_layer setContentsChanged];
+        [m_layer reloadValueForKeyPath:@"contents"];
     END_BLOCK_OBJC_EXCEPTIONS
 }
 
index 1325781..c0f84bf 100644 (file)
@@ -479,20 +479,22 @@ GraphicsContext3D::GraphicsContext3D(GraphicsContext3DAttributes attrs, HostWind
         ::glEnable(GL_MULTISAMPLE);
 #endif
 
+    // Create the texture that will be used for the framebuffer.
 #if PLATFORM(IOS)
     ::glGenRenderbuffers(1, &m_texture);
 #else
-    // create a texture to render into
     ::glGenTextures(1, &m_texture);
-    ::glBindTexture(GL_TEXTURE_2D, m_texture);
-    ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-    ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-    ::glBindTexture(GL_TEXTURE_2D, 0);
+    // We bind to GL_TEXTURE_RECTANGLE_EXT rather than TEXTURE_2D because
+    // that's what is required for a texture backed by IOSurface.
+    ::glBindTexture(GL_TEXTURE_RECTANGLE_EXT, m_texture);
+    ::glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    ::glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    ::glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    ::glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    ::glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0);
 #endif
 
-    // create an FBO
+    // Create the framebuffer object.
     ::glGenFramebuffersEXT(1, &m_fbo);
     ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo);
 
@@ -500,7 +502,7 @@ GraphicsContext3D::GraphicsContext3D(GraphicsContext3DAttributes attrs, HostWind
     if (!m_attrs.antialias && (m_attrs.stencil || m_attrs.depth))
         ::glGenRenderbuffersEXT(1, &m_depthStencilBuffer);
 
-    // create an multisample FBO
+    // If necessary, create another framebuffer for the multisample results.
     if (m_attrs.antialias) {
         ::glGenFramebuffersEXT(1, &m_multisampleFBO);
         ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO);
@@ -654,20 +656,35 @@ void GraphicsContext3D::checkGPUStatus()
 #endif
 }
 
-#if PLATFORM(IOS)
 void GraphicsContext3D::endPaint()
 {
     makeContextCurrent();
     if (m_attrs.antialias)
         resolveMultisamplingIfNecessary();
+#if PLATFORM(IOS)
+    // This is the place where we actually push our current rendering
+    // results to the compositor on iOS. On macOS it comes from the
+    // calling function, which is inside WebGLLayer.
     ::glFlush();
     ::glBindRenderbuffer(GL_RENDERBUFFER, m_texture);
     [static_cast<EAGLContext*>(m_contextObj) presentRenderbuffer:GL_RENDERBUFFER];
     [EAGLContext setCurrentContext:nil];
-}
 #endif
+}
 
 #if PLATFORM(MAC)
+void GraphicsContext3D::allocateIOSurfaceBackingStore(IntSize size)
+{
+    LOG(WebGL, "GraphicsContext3D::allocateIOSurfaceBackingStore at %d x %d. (%p)", size.width(), size.height(), this);
+    [m_webGLLayer allocateIOSurfaceBackingStoreWithSize:size usingAlpha:m_attrs.alpha];
+}
+
+void GraphicsContext3D::updateFramebufferTextureBackingStoreFromLayer()
+{
+    LOG(WebGL, "GraphicsContext3D::updateFramebufferTextureBackingStoreFromLayer(). (%p)", this);
+    [m_webGLLayer bindFramebufferToNextAvailableSurface];
+}
+
 void GraphicsContext3D::updateCGLContext()
 {
     if (!m_contextObj)
index 0e02e17..5b4b518 100644 (file)
@@ -25,6 +25,8 @@
 
 #pragma once
 
+#import "IOSurface.h"
+#import "IntSize.h"
 #import <QuartzCore/QuartzCore.h>
 
 namespace WebCore {
@@ -32,14 +34,21 @@ class GraphicsLayer;
 class GraphicsContext3D;
 }
 
-#if PLATFORM(IOS)
-@interface WebGLLayer : CAEAGLLayer
+#if PLATFORM(MAC)
+@interface WebGLLayer : CALayer
 #else
-@interface WebGLLayer : CAOpenGLLayer
+@interface WebGLLayer : CAEAGLLayer
 #endif
 {
     WebCore::GraphicsContext3D* _context;
     float _devicePixelRatio;
+#if PLATFORM(MAC)
+    std::unique_ptr<WebCore::IOSurface> _contentsBuffer;
+    std::unique_ptr<WebCore::IOSurface> _drawingBuffer;
+    std::unique_ptr<WebCore::IOSurface> _spareBuffer;
+    WebCore::IntSize _bufferSize;
+    BOOL _usingAlpha;
+#endif
 }
 
 @property (nonatomic) WebCore::GraphicsContext3D* context;
@@ -48,5 +57,10 @@ class GraphicsContext3D;
 
 - (CGImageRef)copyImageSnapshotWithColorSpace:(CGColorSpaceRef)colorSpace;
 
+#if PLATFORM(MAC)
+- (void)allocateIOSurfaceBackingStoreWithSize:(WebCore::IntSize)size usingAlpha:(BOOL)usingAlpha;
+- (void)bindFramebufferToNextAvailableSurface;
+#endif
+
 @end
 
index 36a7c49..3962c1c 100644 (file)
 #import "GraphicsLayer.h"
 #import "GraphicsLayerCA.h"
 #import "PlatformCALayer.h"
+#import <pal/spi/cocoa/QuartzCoreSPI.h>
 #import <wtf/FastMalloc.h>
 #import <wtf/RetainPtr.h>
 
-#if !PLATFORM(IOS)
+#if PLATFORM(MAC)
 #import <OpenGL/OpenGL.h>
 #import <OpenGL/gl.h>
 #endif
@@ -53,106 +54,30 @@ using namespace WebCore;
     self = [super init];
     _devicePixelRatio = context->getContextAttributes().devicePixelRatio;
 #if PLATFORM(MAC)
+    if (!context->getContextAttributes().alpha)
+        self.opaque = YES;
+    self.transform = CATransform3DIdentity;
     self.contentsScale = _devicePixelRatio;
-    self.colorspace = sRGBColorSpaceRef();
 #endif
     return self;
 }
 
-#if !PLATFORM(IOS)
--(CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask
+#if PLATFORM(MAC)
+// On Mac, we need to flip the layer to take into account
+// that the IOSurface provides content in Y-up. This
+// means that any incoming transform (unlikely, since this
+// is a contents layer) and anchor point must add a
+// Y scale of -1 and make sure the transform happens from
+// the top.
+
+- (void)setTransform:(CATransform3D)t
 {
-    // We're basically copying the pixel format object from the existing
-    // WebGL context, so we don't need to use the display mask.
-    UNUSED_PARAM(mask);
-
-    CGLPixelFormatObj webglPixelFormat = CGLGetPixelFormat(_context->platformGraphicsContext3D());
-
-    Vector<CGLPixelFormatAttribute> attribs;
-    GLint value;
-
-    CGLDescribePixelFormat(webglPixelFormat, 0, kCGLPFAColorSize, &value);
-    attribs.append(kCGLPFAColorSize);
-    attribs.append(static_cast<CGLPixelFormatAttribute>(value));
-
-    // We don't need to specify a depth size since we're only
-    // using this context as a 2d blit destination for the WebGL FBO.
-    attribs.append(kCGLPFADepthSize);
-    attribs.append(static_cast<CGLPixelFormatAttribute>(0));
-
-    CGLDescribePixelFormat(webglPixelFormat, 0, kCGLPFAAllowOfflineRenderers, &value);
-    if (value)
-        attribs.append(kCGLPFAAllowOfflineRenderers);
-
-    CGLDescribePixelFormat(webglPixelFormat, 0, kCGLPFAAccelerated, &value);
-    if (value)
-        attribs.append(kCGLPFAAccelerated);
-
-    CGLDescribePixelFormat(webglPixelFormat, 0, kCGLPFAOpenGLProfile, &value);
-    if (value) {
-        attribs.append(kCGLPFAOpenGLProfile);
-        attribs.append(static_cast<CGLPixelFormatAttribute>(value));
-    }
-
-    attribs.append(static_cast<CGLPixelFormatAttribute>(0));
-
-    CGLPixelFormatObj pixelFormat;
-    GLint numPixelFormats = 0;
-    CGLChoosePixelFormat(attribs.data(), &pixelFormat, &numPixelFormats);
-
-    ASSERT(pixelFormat);
-    ASSERT(numPixelFormats);
-
-    return pixelFormat;
+    [super setTransform:CATransform3DScale(t, 1, -1, 1)];
 }
 
--(CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat
+- (void)setAnchorPoint:(CGPoint)p
 {
-    CGLContextObj contextObj;
-    CGLCreateContext(pixelFormat, _context->platformGraphicsContext3D(), &contextObj);
-    return contextObj;
-}
-
--(void)drawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
-{
-    if (!_context)
-        return;
-
-    _context->prepareTexture();
-
-    CGLSetCurrentContext(glContext);
-
-    CGRect frame = [self frame];
-    frame.size.width *= _devicePixelRatio;
-    frame.size.height *= _devicePixelRatio;
-
-    // draw the FBO into the layer
-    glViewport(0, 0, frame.size.width, frame.size.height);
-    glMatrixMode(GL_PROJECTION);
-    glLoadIdentity();
-    glOrtho(-1, 1, -1, 1, -1, 1);
-    glMatrixMode(GL_MODELVIEW);
-    glLoadIdentity();
-
-    glEnable(GL_TEXTURE_2D);
-    glBindTexture(GL_TEXTURE_2D, _context->platformTexture());
-
-    glBegin(GL_TRIANGLE_FAN);
-        glTexCoord2f(0, 0);
-        glVertex2f(-1, -1);
-        glTexCoord2f(1, 0);
-        glVertex2f(1, -1);
-        glTexCoord2f(1, 1);
-        glVertex2f(1, 1);
-        glTexCoord2f(0, 1);
-        glVertex2f(-1, 1);
-    glEnd();
-
-    glBindTexture(GL_TEXTURE_2D, 0);
-    glDisable(GL_TEXTURE_2D);
-
-    // Call super to finalize the drawing. By default all it does is call glFlush().
-    [super drawInCGLContext:glContext pixelFormat:pixelFormat forLayerTime:timeInterval displayTime:timeStamp];
+    [super setAnchorPoint:CGPointMake(p.x, 1.0 - p.y)];
 }
 
 static void freeData(void *, const void *data, size_t /* size */)
@@ -203,17 +128,55 @@ static void freeData(void *, const void *data, size_t /* size */)
     if (!_context)
         return;
 
-#if PLATFORM(IOS)
     _context->endPaint();
-#else
-    [super display];
+
+#if PLATFORM(MAC)
+    _context->prepareTexture();
+    if (_drawingBuffer) {
+        std::swap(_contentsBuffer, _drawingBuffer);
+        self.contents = _contentsBuffer->asLayerContents();
+        [self reloadValueForKeyPath:@"contents"];
+        [self bindFramebufferToNextAvailableSurface];
+    }
 #endif
+
     _context->markLayerComposited();
     PlatformCALayer* layer = PlatformCALayer::platformCALayer(self);
     if (layer && layer->owner())
         layer->owner()->platformCALayerLayerDidDisplay(layer);
 }
 
+#if PLATFORM(MAC)
+- (void)allocateIOSurfaceBackingStoreWithSize:(IntSize)size usingAlpha:(BOOL)usingAlpha
+{
+    _bufferSize = size;
+    _usingAlpha = usingAlpha;
+    _contentsBuffer = WebCore::IOSurface::create(size, sRGBColorSpaceRef());
+    _drawingBuffer = WebCore::IOSurface::create(size, sRGBColorSpaceRef());
+    ASSERT(_contentsBuffer);
+    ASSERT(_drawingBuffer);
+}
+
+- (void)bindFramebufferToNextAvailableSurface
+{
+    GC3Denum texture = _context->platformTexture();
+    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture);
+
+    if (_drawingBuffer && _drawingBuffer->isInUse()) {
+        if (!_spareBuffer)
+            _spareBuffer = WebCore::IOSurface::create(_bufferSize, sRGBColorSpaceRef());
+        std::swap(_drawingBuffer, _spareBuffer);
+    }
+
+    IOSurfaceRef ioSurface = _drawingBuffer->surface();
+    GC3Denum internalFormat = _usingAlpha ? GL_RGBA : GL_RGB;
+
+    // Link the IOSurface to the texture.
+    CGLError error = CGLTexImageIOSurface2D(_context->platformGraphicsContext3D(), GL_TEXTURE_RECTANGLE_ARB, internalFormat, _bufferSize.width(), _bufferSize.height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, ioSurface, 0);
+    ASSERT_UNUSED(error, error == kCGLNoError);
+}
+#endif
+
 @end
 
 #endif // ENABLE(WEBGL)
index 3fc76c9..98338ab 100644 (file)
@@ -30,6 +30,7 @@
 #import "GraphicsLayerCA.h"
 #import "PlatformCALayer.h"
 #import <QuartzCore/QuartzCore.h>
+#import <pal/spi/cocoa/QuartzCoreSPI.h>
 #import <wtf/SetForScope.h>
 
 #if PLATFORM(IOS)
 #import "WebCoreThread.h"
 #endif
 
-@interface CALayer(WebCoreCALayerPrivate)
-- (void)reloadValueForKeyPath:(NSString *)keyPath;
-@end
-
 using namespace WebCore;
 
 #if PLATFORM(IOS)
index 553a897..94faf44 100644 (file)
 #if ENABLE(GRAPHICS_CONTEXT_3D)
 
 #include "GraphicsContext3D.h"
+
 #if PLATFORM(IOS)
 #include "GraphicsContext3DIOS.h"
 #endif
-
 #include "Extensions3DOpenGL.h"
 #include "IntRect.h"
 #include "IntSize.h"
 #include "NotImplemented.h"
 #include "TemporaryOpenGLSetting.h"
-
 #include <algorithm>
 #include <cstring>
 #include <wtf/MainThread.h>
@@ -70,6 +69,17 @@ void GraphicsContext3D::releaseShaderCompiler()
     notImplemented();
 }
 
+#if PLATFORM(MAC)
+static void wipeAlphaChannelFromPixels(int width, int height, unsigned char* pixels)
+{
+    // We can assume this doesn't overflow because the calling functions
+    // use checked arithmetic.
+    int totalBytes = width * height * 4;
+    for (int i = 0; i < totalBytes; i += 4)
+        pixels[i + 3] = 255;
+}
+#endif
+
 void GraphicsContext3D::readPixelsAndConvertToBGRAIfNecessary(int x, int y, int width, int height, unsigned char* pixels)
 {
     // NVIDIA drivers have a bug where calling readPixels in BGRA can return the wrong values for the alpha channel when the alpha is off for the context.
@@ -98,6 +108,11 @@ void GraphicsContext3D::readPixelsAndConvertToBGRAIfNecessary(int x, int y, int
 #endif
     } else
         ::glReadPixels(x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
+
+#if PLATFORM(MAC)
+    if (!m_attrs.alpha)
+        wipeAlphaChannelFromPixels(width, height, pixels);
+#endif
 }
 
 void GraphicsContext3D::validateAttributes()
@@ -170,6 +185,10 @@ bool GraphicsContext3D::reshapeFBOs(const IntSize& size)
     ::glBindRenderbuffer(GL_RENDERBUFFER, m_texture);
     ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_texture);
     setRenderbufferStorageFromDrawable(m_currentWidth, m_currentHeight);
+#elif PLATFORM(MAC)
+    allocateIOSurfaceBackingStore(IntSize(width, height));
+    updateFramebufferTextureBackingStoreFromLayer();
+    ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, m_texture, 0);
 #else
     ::glBindTexture(GL_TEXTURE_2D, m_texture);
     ::glTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0);
@@ -442,6 +461,11 @@ void GraphicsContext3D::readPixels(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsi
     ::glReadPixels(x, y, width, height, format, type, data);
     if (m_attrs.antialias && m_state.boundFBO == m_multisampleFBO)
         ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
+
+#if PLATFORM(MAC)
+    if (!m_attrs.alpha && (format == GraphicsContext3D::RGBA || format == GraphicsContext3D::BGRA) && (m_state.boundFBO == m_fbo || (m_attrs.antialias && m_state.boundFBO == m_multisampleFBO)))
+        wipeAlphaChannelFromPixels(width, height, static_cast<unsigned char*>(data));
+#endif
 }
 
 }