8f0245fa3ae124471d917bd604e6fc35a9bc917b
[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)display
167 {
168     if (!_context)
169         return;
170
171 #if USE(OPENGL)
172     _context->prepareTexture();
173     if (_drawingBuffer) {
174         std::swap(_contentsBuffer, _drawingBuffer);
175         self.contents = _contentsBuffer->asLayerContents();
176         [self reloadValueForKeyPath:@"contents"];
177         [self bindFramebufferToNextAvailableSurface];
178     }
179 #elif USE(OPENGL_ES)
180     _context->presentRenderbuffer();
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         std::swap(_contentsBuffer, _drawingBuffer);
202         std::swap(_contentsPbuffer, _drawingPbuffer);
203         self.contents = _contentsBuffer->asLayerContents();
204         [self reloadValueForKeyPath:@"contents"];
205         [self bindFramebufferToNextAvailableSurface];
206     }
207 #endif
208
209     _context->markLayerComposited();
210     auto layer = WebCore::PlatformCALayer::platformCALayerForLayer((__bridge void*)self);
211     if (layer && layer->owner())
212         layer->owner()->platformCALayerLayerDidDisplay(layer.get());
213 }
214
215 #if USE(ANGLE)
216 - (void)setEGLDisplay:(void*)display config:(void*)config
217 {
218     _eglDisplay = display;
219     _eglConfig = config;
220 }
221
222 - (void)releaseGLResources
223 {
224     if (!_context)
225         return;
226
227     if (_context->makeContextCurrent() && _latchedPbuffer) {
228         EGL_ReleaseTexImage(_eglDisplay, _latchedPbuffer, EGL_BACK_BUFFER);
229         _latchedPbuffer = nullptr;
230     }
231
232     EGL_DestroySurface(_eglDisplay, _contentsPbuffer);
233     EGL_DestroySurface(_eglDisplay, _drawingPbuffer);
234     EGL_DestroySurface(_eglDisplay, _sparePbuffer);
235 }
236 #endif
237
238 #if USE(OPENGL) || USE(ANGLE)
239 - (void)allocateIOSurfaceBackingStoreWithSize:(WebCore::IntSize)size usingAlpha:(BOOL)usingAlpha
240 {
241     _bufferSize = size;
242     _usingAlpha = usingAlpha;
243     _contentsBuffer = WebCore::IOSurface::create(size, WebCore::sRGBColorSpaceRef());
244     _drawingBuffer = WebCore::IOSurface::create(size, WebCore::sRGBColorSpaceRef());
245     _spareBuffer = WebCore::IOSurface::create(size, WebCore::sRGBColorSpaceRef());
246
247     ASSERT(_contentsBuffer);
248     ASSERT(_drawingBuffer);
249     ASSERT(_spareBuffer);
250
251     _contentsBuffer->migrateColorSpaceToProperties();
252     _drawingBuffer->migrateColorSpaceToProperties();
253     _spareBuffer->migrateColorSpaceToProperties();
254
255 #if USE(ANGLE)
256     const EGLint surfaceAttributes[] = {
257         EGL_WIDTH, size.width(),
258         EGL_HEIGHT, size.height(),
259         EGL_IOSURFACE_PLANE_ANGLE, 0,
260         EGL_TEXTURE_TARGET, WebCore::GraphicsContextGL::EGLIOSurfaceTextureTarget,
261         EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, usingAlpha ? GL_BGRA_EXT : GL_RGB,
262         EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
263         EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
264         // Only has an effect on the iOS Simulator.
265         EGL_IOSURFACE_USAGE_HINT_ANGLE, EGL_IOSURFACE_WRITE_HINT_ANGLE,
266         EGL_NONE, EGL_NONE
267     };
268
269     _contentsPbuffer = EGL_CreatePbufferFromClientBuffer(_eglDisplay, EGL_IOSURFACE_ANGLE, _contentsBuffer->surface(), _eglConfig, surfaceAttributes);
270     _drawingPbuffer = EGL_CreatePbufferFromClientBuffer(_eglDisplay, EGL_IOSURFACE_ANGLE, _drawingBuffer->surface(), _eglConfig, surfaceAttributes);
271     _sparePbuffer = EGL_CreatePbufferFromClientBuffer(_eglDisplay, EGL_IOSURFACE_ANGLE, _spareBuffer->surface(), _eglConfig, surfaceAttributes);
272 #endif
273 }
274
275 - (void)bindFramebufferToNextAvailableSurface
276 {
277 #if USE(OPENGL)
278     GCGLenum texture = _context->platformTexture();
279     glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture);
280
281     if (_drawingBuffer && _drawingBuffer->isInUse())
282         std::swap(_drawingBuffer, _spareBuffer);
283
284     IOSurfaceRef ioSurface = _drawingBuffer->surface();
285     GCGLenum internalFormat = _usingAlpha ? GL_RGBA : GL_RGB;
286
287     // Link the IOSurface to the texture.
288     CGLContextObj cglContext = static_cast<CGLContextObj>(_context->platformGraphicsContextGL());
289     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);
290     ASSERT_UNUSED(error, error == kCGLNoError);
291 #elif USE(ANGLE)
292     WTF::Optional<ScopedRestoreTextureBinding> restoreBinding;
293     // We don't need to restore GL_TEXTURE_RECTANGLE because it's not accessible from user code.
294     if (WebCore::GraphicsContextGL::IOSurfaceTextureTarget != WebCore::GraphicsContextGL::TEXTURE_RECTANGLE_ARB)
295         restoreBinding.emplace(WebCore::GraphicsContextGL::IOSurfaceTextureTargetQuery, WebCore::GraphicsContextGL::IOSurfaceTextureTarget);
296
297     GCGLenum texture = _context->platformTexture();
298
299     gl::BindTexture(WebCore::GraphicsContextGL::IOSurfaceTextureTarget, texture);
300
301     if (_latchedPbuffer) {
302         if (!EGL_ReleaseTexImage(_eglDisplay, _latchedPbuffer, EGL_BACK_BUFFER)) {
303             // FIXME: report error.
304             notImplemented();
305         }
306         _latchedPbuffer = nullptr;
307     }
308
309     if (_drawingBuffer && _drawingBuffer->isInUse()) {
310         std::swap(_drawingBuffer, _spareBuffer);
311         std::swap(_drawingPbuffer, _sparePbuffer);
312     }
313
314     // Link the IOSurface to the texture via the previously-created pbuffer.
315     if (!EGL_BindTexImage(_eglDisplay, _drawingPbuffer, EGL_BACK_BUFFER)) {
316         // FIXME: report error.
317         notImplemented();
318     }
319     _latchedPbuffer = _drawingPbuffer;
320 #endif
321 }
322 #endif // USE(OPENGL) || USE(ANGLE)
323
324 @end
325
326 #endif // ENABLE(WEBGL)