AutoTrader crashed while browsing search results
[WebKit.git] / Source / WebCore / platform / graphics / cocoa / WebGLLayer.mm
1 /*
2  * Copyright (C) 2009-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27
28 #if ENABLE(WEBGL)
29 #import "WebGLLayer.h"
30
31 #import "GraphicsContextCG.h"
32 #import "GraphicsContextGLOpenGL.h"
33 #import "GraphicsLayer.h"
34 #import "GraphicsLayerCA.h"
35 #import "ImageBufferUtilitiesCG.h"
36 #import "NotImplemented.h"
37 #import "PlatformCALayer.h"
38 #import <pal/spi/cocoa/QuartzCoreSPI.h>
39 #import <wtf/FastMalloc.h>
40 #import <wtf/RetainPtr.h>
41
42 #if USE(OPENGL)
43 #import <OpenGL/OpenGL.h>
44 #import <OpenGL/gl.h>
45 #endif
46
47 #if USE(ANGLE)
48 #define EGL_EGL_PROTOTYPES 0
49 #import <ANGLE/egl.h>
50 #import <ANGLE/eglext.h>
51 #import <ANGLE/eglext_angle.h>
52 #import <ANGLE/entry_points_egl.h>
53 #import <ANGLE/entry_points_gles_2_0_autogen.h>
54 // Skip the inclusion of ANGLE's explicit context entry points for now.
55 #define GL_ANGLE_explicit_context
56 #import <ANGLE/gl2ext.h>
57 #import <ANGLE/gl2ext_angle.h>
58 #endif
59
60 namespace {
61     class ScopedRestoreTextureBinding {
62         WTF_MAKE_NONCOPYABLE(ScopedRestoreTextureBinding);
63     public:
64         ScopedRestoreTextureBinding(GLenum bindingPointQuery, GLenum bindingPoint)
65             : m_bindingPoint(bindingPoint)
66         {
67             gl::GetIntegerv(bindingPointQuery, &m_bindingValue);
68         }
69
70         ~ScopedRestoreTextureBinding()
71         {
72             gl::BindTexture(m_bindingPoint, m_bindingValue);
73         }
74
75     private:
76         GLint m_bindingPoint { 0 };
77         GLint m_bindingValue { 0 };
78     };
79 }
80
81 @implementation WebGLLayer
82
83 @synthesize context=_context;
84
85 -(id)initWithGraphicsContextGL:(NakedPtr<WebCore::GraphicsContextGLOpenGL>)context
86 {
87     _context = context;
88     self = [super init];
89     auto attributes = context->contextAttributes();
90     _devicePixelRatio = attributes.devicePixelRatio;
91 #if USE(OPENGL) || USE(ANGLE)
92     self.contentsOpaque = !attributes.alpha;
93     self.transform = CATransform3DIdentity;
94     self.contentsScale = _devicePixelRatio;
95 #else
96     self.opaque = !attributes.alpha;
97 #endif
98     return self;
99 }
100
101 #if USE(OPENGL) || USE(ANGLE)
102 // When using an IOSurface as layer contents, we need to flip the
103 // layer to take into account that the IOSurface provides content
104 // in Y-up. This means that any incoming transform (unlikely, since
105 // this is a contents layer) and anchor point must add a Y scale of
106 // -1 and make sure the transform happens from the top.
107
108 - (void)setTransform:(CATransform3D)t
109 {
110     [super setTransform:CATransform3DScale(t, 1, -1, 1)];
111 }
112
113 - (void)setAnchorPoint:(CGPoint)p
114 {
115     [super setAnchorPoint:CGPointMake(p.x, 1.0 - p.y)];
116 }
117 #endif // USE(OPENGL) || USE(ANGLE)
118
119 #if USE(OPENGL)
120 static void freeData(void *, const void *data, size_t /* size */)
121 {
122     fastFree(const_cast<void *>(data));
123 }
124 #endif // USE(OPENGL)
125
126 - (CGImageRef)copyImageSnapshotWithColorSpace:(CGColorSpaceRef)colorSpace
127 {
128     if (!_context)
129         return nullptr;
130
131 #if USE(OPENGL)
132     CGLContextObj cglContext = static_cast<CGLContextObj>(_context->platformGraphicsContextGL());
133     CGLSetCurrentContext(cglContext);
134
135     RetainPtr<CGColorSpaceRef> imageColorSpace = colorSpace;
136     if (!imageColorSpace)
137         imageColorSpace = WebCore::sRGBColorSpaceRef();
138
139     CGRect layerBounds = CGRectIntegral([self bounds]);
140
141     size_t width = layerBounds.size.width * _devicePixelRatio;
142     size_t height = layerBounds.size.height * _devicePixelRatio;
143
144     size_t rowBytes = (width * 4 + 15) & ~15;
145     size_t dataSize = rowBytes * height;
146     void* data = fastMalloc(dataSize);
147     if (!data)
148         return nullptr;
149
150     glPixelStorei(GL_PACK_ROW_LENGTH, rowBytes / 4);
151     glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
152
153     WebCore::verifyImageBufferIsBigEnough((uint8_t*)data, dataSize);
154     CGDataProviderRef provider = CGDataProviderCreateWithData(0, data, dataSize, freeData);
155     CGImageRef image = CGImageCreate(width, height, 8, 32, rowBytes, imageColorSpace.get(),
156         kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, provider, 0, true, kCGRenderingIntentDefault);
157     CGDataProviderRelease(provider);
158     return image;
159 #else
160     // FIXME: implement.
161     UNUSED_PARAM(colorSpace);
162     return nullptr;
163 #endif
164 }
165
166 - (void)prepareForDisplay
167 {
168     if (!_context)
169         return;
170
171     // To avoid running any OpenGL code in `display`, this method should be called
172     // at the end of the rendering task. We will flush all painting commands
173     // leaving the buffers ready to composite.
174
175 #if USE(OPENGL)
176     _context->prepareTexture();
177     if (_drawingBuffer) {
178         std::swap(_contentsBuffer, _drawingBuffer);
179         [self bindFramebufferToNextAvailableSurface];
180     }
181 #elif USE(ANGLE)
182     if (!_context->makeContextCurrent()) {
183         // Context is likely being torn down.
184         return;
185     }
186     _context->prepareTexture();
187     if (_drawingBuffer) {
188         if (_latchedPbuffer) {
189             WTF::Optional<ScopedRestoreTextureBinding> restoreBinding;
190             // We don't need to restore GL_TEXTURE_RECTANGLE because it's not accessible from user code.
191             if (WebCore::GraphicsContextGL::IOSurfaceTextureTarget != WebCore::GraphicsContextGL::TEXTURE_RECTANGLE_ARB)
192                 restoreBinding.emplace(WebCore::GraphicsContextGL::IOSurfaceTextureTargetQuery, WebCore::GraphicsContextGL::IOSurfaceTextureTarget);
193             GCGLenum texture = _context->platformTexture();
194             gl::BindTexture(WebCore::GraphicsContextGL::IOSurfaceTextureTarget, texture);
195             if (!EGL_ReleaseTexImage(_eglDisplay, _latchedPbuffer, EGL_BACK_BUFFER)) {
196                 // FIXME: report error.
197                 notImplemented();
198             }
199             _latchedPbuffer = nullptr;
200         }
201
202         std::swap(_contentsBuffer, _drawingBuffer);
203         std::swap(_contentsPbuffer, _drawingPbuffer);
204         [self bindFramebufferToNextAvailableSurface];
205     }
206 #endif
207
208     _prepared = YES;
209 }
210
211 - (void)display
212 {
213     if (!_context)
214         return;
215
216     // At this point we've painted into the _drawingBuffer and swapped it with the old _contentsBuffer,
217     // so all we need to do here is tickle the CALayer to let it know it has new contents.
218     // This avoids running any OpenGL code in this method.
219
220 #if USE(OPENGL) || USE(ANGLE)
221     if (_contentsBuffer && _prepared) {
222         self.contents = _contentsBuffer->asLayerContents();
223         [self reloadValueForKeyPath:@"contents"];
224     }
225 #elif USE(OPENGL_ES)
226     _context->presentRenderbuffer();
227 #endif
228
229     _context->markLayerComposited();
230     auto layer = WebCore::PlatformCALayer::platformCALayerForLayer((__bridge void*)self);
231     if (layer && layer->owner())
232         layer->owner()->platformCALayerLayerDidDisplay(layer.get());
233
234     _prepared = NO;
235 }
236
237 #if USE(ANGLE)
238 - (void)setEGLDisplay:(void*)display config:(void*)config
239 {
240     _eglDisplay = display;
241     _eglConfig = config;
242 }
243
244 - (void)releaseGLResources
245 {
246     if (!_context)
247         return;
248
249     if (_context->makeContextCurrent() && _latchedPbuffer) {
250         EGL_ReleaseTexImage(_eglDisplay, _latchedPbuffer, EGL_BACK_BUFFER);
251         _latchedPbuffer = nullptr;
252     }
253
254     EGL_DestroySurface(_eglDisplay, _contentsPbuffer);
255     EGL_DestroySurface(_eglDisplay, _drawingPbuffer);
256     EGL_DestroySurface(_eglDisplay, _sparePbuffer);
257 }
258 #endif
259
260 #if USE(OPENGL) || USE(ANGLE)
261 - (void)allocateIOSurfaceBackingStoreWithSize:(WebCore::IntSize)size usingAlpha:(BOOL)usingAlpha
262 {
263     _bufferSize = size;
264     _usingAlpha = usingAlpha;
265     _contentsBuffer = WebCore::IOSurface::create(size, WebCore::sRGBColorSpaceRef());
266     _drawingBuffer = WebCore::IOSurface::create(size, WebCore::sRGBColorSpaceRef());
267     _spareBuffer = WebCore::IOSurface::create(size, WebCore::sRGBColorSpaceRef());
268
269     ASSERT(_contentsBuffer);
270     ASSERT(_drawingBuffer);
271     ASSERT(_spareBuffer);
272
273     _contentsBuffer->migrateColorSpaceToProperties();
274     _drawingBuffer->migrateColorSpaceToProperties();
275     _spareBuffer->migrateColorSpaceToProperties();
276
277 #if USE(ANGLE)
278     const EGLint surfaceAttributes[] = {
279         EGL_WIDTH, size.width(),
280         EGL_HEIGHT, size.height(),
281         EGL_IOSURFACE_PLANE_ANGLE, 0,
282         EGL_TEXTURE_TARGET, WebCore::GraphicsContextGL::EGLIOSurfaceTextureTarget,
283         EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, usingAlpha ? GL_BGRA_EXT : GL_RGB,
284         EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
285         EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
286         // Only has an effect on the iOS Simulator.
287         EGL_IOSURFACE_USAGE_HINT_ANGLE, EGL_IOSURFACE_WRITE_HINT_ANGLE,
288         EGL_NONE, EGL_NONE
289     };
290
291     _contentsPbuffer = EGL_CreatePbufferFromClientBuffer(_eglDisplay, EGL_IOSURFACE_ANGLE, _contentsBuffer->surface(), _eglConfig, surfaceAttributes);
292     _drawingPbuffer = EGL_CreatePbufferFromClientBuffer(_eglDisplay, EGL_IOSURFACE_ANGLE, _drawingBuffer->surface(), _eglConfig, surfaceAttributes);
293     _sparePbuffer = EGL_CreatePbufferFromClientBuffer(_eglDisplay, EGL_IOSURFACE_ANGLE, _spareBuffer->surface(), _eglConfig, surfaceAttributes);
294 #endif
295 }
296
297 - (void)bindFramebufferToNextAvailableSurface
298 {
299 #if USE(OPENGL)
300     GCGLenum texture = _context->platformTexture();
301     glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture);
302
303     if (_drawingBuffer && _drawingBuffer->isInUse())
304         std::swap(_drawingBuffer, _spareBuffer);
305
306     IOSurfaceRef ioSurface = _drawingBuffer->surface();
307     GCGLenum internalFormat = _usingAlpha ? GL_RGBA : GL_RGB;
308
309     // Link the IOSurface to the texture.
310     CGLContextObj cglContext = static_cast<CGLContextObj>(_context->platformGraphicsContextGL());
311     CGLError error = CGLTexImageIOSurface2D(cglContext, GL_TEXTURE_RECTANGLE_ARB, internalFormat, _bufferSize.width(), _bufferSize.height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, ioSurface, 0);
312     ASSERT_UNUSED(error, error == kCGLNoError);
313 #elif USE(ANGLE)
314     WTF::Optional<ScopedRestoreTextureBinding> restoreBinding;
315     // We don't need to restore GL_TEXTURE_RECTANGLE because it's not accessible from user code.
316     if (WebCore::GraphicsContextGL::IOSurfaceTextureTarget != WebCore::GraphicsContextGL::TEXTURE_RECTANGLE_ARB)
317         restoreBinding.emplace(WebCore::GraphicsContextGL::IOSurfaceTextureTargetQuery, WebCore::GraphicsContextGL::IOSurfaceTextureTarget);
318
319     GCGLenum texture = _context->platformTexture();
320
321     gl::BindTexture(WebCore::GraphicsContextGL::IOSurfaceTextureTarget, texture);
322
323     if (_latchedPbuffer) {
324         if (!EGL_ReleaseTexImage(_eglDisplay, _latchedPbuffer, EGL_BACK_BUFFER)) {
325             // FIXME: report error.
326             notImplemented();
327         }
328         _latchedPbuffer = nullptr;
329     }
330
331     if (_drawingBuffer && _drawingBuffer->isInUse()) {
332         std::swap(_drawingBuffer, _spareBuffer);
333         std::swap(_drawingPbuffer, _sparePbuffer);
334     }
335
336     // Link the IOSurface to the texture via the previously-created pbuffer.
337     if (!EGL_BindTexImage(_eglDisplay, _drawingPbuffer, EGL_BACK_BUFFER)) {
338         // FIXME: report error.
339         notImplemented();
340     }
341     _latchedPbuffer = _drawingPbuffer;
342 #endif
343 }
344 #endif // USE(OPENGL) || USE(ANGLE)
345
346 @end
347
348 #endif // ENABLE(WEBGL)