[WebIDL] WebGLContextAttributes should be a dictionary
[WebKit-https.git] / Source / WebCore / platform / graphics / mac / GraphicsContext3DMac.mm
1 /*
2  * Copyright (C) 2009 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 #include "config.h"
27
28 #if ENABLE(GRAPHICS_CONTEXT_3D)
29
30 #include "GraphicsContext3D.h"
31 #if PLATFORM(IOS)
32 #include "GraphicsContext3DIOS.h"
33 #endif
34
35 #import <wtf/BlockObjCExceptions.h>
36
37 #include "CanvasRenderingContext.h"
38 #include <CoreGraphics/CGBitmapContext.h>
39 #include "Extensions3DOpenGL.h"
40 #include "GraphicsContext.h"
41 #include "HTMLCanvasElement.h"
42 #include "ImageBuffer.h"
43 #include "Logging.h"
44 #if PLATFORM(IOS)
45 #import "OpenGLESSPI.h"
46 #import <OpenGLES/ES2/glext.h>
47 #import <OpenGLES/EAGL.h>
48 #import <OpenGLES/EAGLDrawable.h>
49 #import <QuartzCore/QuartzCore.h>
50 #else
51 #include <OpenGL/CGLRenderers.h>
52 #include <OpenGL/gl.h>
53 #endif
54 #include "WebGLLayer.h"
55 #include "WebGLObject.h"
56 #include "WebGLRenderingContextBase.h"
57 #include <sysexits.h>
58 #include <wtf/text/CString.h>
59
60 namespace WebCore {
61
62 static Vector<GraphicsContext3D*>& activeContexts()
63 {
64     static NeverDestroyed<Vector<GraphicsContext3D*>> s_activeContexts;
65     return s_activeContexts;
66 }
67
68 const int MaxActiveContexts = 16;
69 const int GPUStatusCheckThreshold = 5;
70 int GraphicsContext3D::GPUCheckCounter = 0;
71
72 // FIXME: This class is currently empty on Mac, but will get populated as 
73 // the restructuring in https://bugs.webkit.org/show_bug.cgi?id=66903 is done
74 class GraphicsContext3DPrivate {
75 public:
76     GraphicsContext3DPrivate(GraphicsContext3D*) { }
77     
78     ~GraphicsContext3DPrivate() { }
79 };
80
81 #if !PLATFORM(IOS)
82 static void setPixelFormat(Vector<CGLPixelFormatAttribute>& attribs, int colorBits, int depthBits, bool accelerated, bool supersample, bool closest, bool antialias, bool allowOffline, bool useGLES3)
83 {
84     attribs.clear();
85     
86     attribs.append(kCGLPFAColorSize);
87     attribs.append(static_cast<CGLPixelFormatAttribute>(colorBits));
88     attribs.append(kCGLPFADepthSize);
89     attribs.append(static_cast<CGLPixelFormatAttribute>(depthBits));
90
91     // This attribute, while mentioning offline renderers, is actually
92     // allowing us to request the integrated graphics on a dual GPU
93     // system, and not force the discrete GPU.
94     // See https://developer.apple.com/library/mac/technotes/tn2229/_index.html
95     if (allowOffline)
96         attribs.append(kCGLPFAAllowOfflineRenderers);
97
98     if (accelerated)
99         attribs.append(kCGLPFAAccelerated);
100     else {
101         attribs.append(kCGLPFARendererID);
102         attribs.append(static_cast<CGLPixelFormatAttribute>(kCGLRendererGenericFloatID));
103     }
104         
105     if (supersample && !antialias)
106         attribs.append(kCGLPFASupersample);
107         
108     if (closest)
109         attribs.append(kCGLPFAClosestPolicy);
110
111     if (antialias) {
112         attribs.append(kCGLPFAMultisample);
113         attribs.append(kCGLPFASampleBuffers);
114         attribs.append(static_cast<CGLPixelFormatAttribute>(1));
115         attribs.append(kCGLPFASamples);
116         attribs.append(static_cast<CGLPixelFormatAttribute>(4));
117     }
118
119     if (useGLES3) {
120         // FIXME: Instead of backing a WebGL2 GraphicsContext3D with a OpenGL 3.2 context, we should instead back it with ANGLE.
121         // Use an OpenGL 3.2 context for now until the ANGLE backend is ready.
122         attribs.append(kCGLPFAOpenGLProfile);
123         attribs.append(static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_3_2_Core));
124     }
125         
126     attribs.append(static_cast<CGLPixelFormatAttribute>(0));
127 }
128 #endif // !PLATFORM(IOS)
129
130 RefPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3DAttributes attrs, HostWindow* hostWindow, GraphicsContext3D::RenderStyle renderStyle)
131 {
132     // This implementation doesn't currently support rendering directly to the HostWindow.
133     if (renderStyle == RenderDirectlyToHostWindow)
134         return nullptr;
135
136     Vector<GraphicsContext3D*>& contexts = activeContexts();
137     
138     if (contexts.size() >= MaxActiveContexts)
139         contexts.at(0)->recycleContext();
140     
141     // Calling recycleContext() above should have lead to the graphics context being
142     // destroyed and thus removed from the active contexts list.
143     if (contexts.size() >= MaxActiveContexts)
144         return nullptr;
145
146     RefPtr<GraphicsContext3D> context = adoptRef(new GraphicsContext3D(attrs, hostWindow, renderStyle));
147
148     if (!context->m_contextObj)
149         return nullptr;
150
151     contexts.append(context.get());
152
153     return context;
154 }
155
156 GraphicsContext3D::GraphicsContext3D(GraphicsContext3DAttributes attrs, HostWindow* hostWindow, GraphicsContext3D::RenderStyle renderStyle)
157     : m_currentWidth(0)
158     , m_currentHeight(0)
159     , m_contextObj(0)
160 #if PLATFORM(IOS)
161     , m_compiler(SH_ESSL_OUTPUT)
162 #endif
163     , m_attrs(attrs)
164     , m_texture(0)
165     , m_compositorTexture(0)
166     , m_fbo(0)
167     , m_depthStencilBuffer(0)
168     , m_layerComposited(false)
169     , m_internalColorFormat(0)
170     , m_multisampleFBO(0)
171     , m_multisampleDepthStencilBuffer(0)
172     , m_multisampleColorBuffer(0)
173     , m_private(std::make_unique<GraphicsContext3DPrivate>(this))
174     , m_webglContext(0)
175 {
176     UNUSED_PARAM(hostWindow);
177     UNUSED_PARAM(renderStyle);
178
179 #if PLATFORM(IOS)
180     EAGLRenderingAPI api = m_attrs.useGLES3 ? kEAGLRenderingAPIOpenGLES3 : kEAGLRenderingAPIOpenGLES2;
181     m_contextObj = [[EAGLContext alloc] initWithAPI:api];
182     makeContextCurrent();
183 #else
184     Vector<CGLPixelFormatAttribute> attribs;
185     CGLPixelFormatObj pixelFormatObj = 0;
186     GLint numPixelFormats = 0;
187     
188     // If we're configured to demand the software renderer, we'll
189     // do so. We attempt to create contexts in this order:
190     //
191     // 1) 32 bit RGBA/32 bit depth/supersampled
192     // 2) 32 bit RGBA/32 bit depth
193     // 3) 32 bit RGBA/16 bit depth
194     //
195     // If we were not forced into software mode already, our final attempt is
196     // to try that:
197     //
198     // 4) closest to 32 bit RGBA/16 bit depth/software renderer
199     //
200     // If none of that works, we simply fail and set m_contextObj to 0.
201
202     bool useMultisampling = m_attrs.antialias;
203
204     setPixelFormat(attribs, 32, 32, !attrs.forceSoftwareRenderer, true, false, useMultisampling, attrs.preferLowPowerToHighPerformance, attrs.useGLES3);
205     CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats);
206
207     if (!numPixelFormats) {
208         setPixelFormat(attribs, 32, 32, !attrs.forceSoftwareRenderer, false, false, useMultisampling, attrs.preferLowPowerToHighPerformance, attrs.useGLES3);
209         CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats);
210
211         if (!numPixelFormats) {
212             setPixelFormat(attribs, 32, 16, !attrs.forceSoftwareRenderer, false, false, useMultisampling, attrs.preferLowPowerToHighPerformance, attrs.useGLES3);
213             CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats);
214
215             if (!attrs.forceSoftwareRenderer && !numPixelFormats) {
216                 setPixelFormat(attribs, 32, 16, false, false, true, false, false, attrs.useGLES3);
217                 CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats);
218                 useMultisampling = false;
219             }
220         }
221     }
222
223     if (numPixelFormats == 0)
224         return;
225
226     CGLError err = CGLCreateContext(pixelFormatObj, 0, &m_contextObj);
227     GLint abortOnBlacklist = 0;
228 #if PLATFORM(MAC)
229     CGLSetParameter(m_contextObj, kCGLCPAbortOnGPURestartStatusBlacklisted, &abortOnBlacklist);
230 #elif PLATFORM(IOS)
231     CGLSetParameter(m_contextObj, kEAGLCPAbortOnGPURestartStatusBlacklisted, &abortOnBlacklist);
232 #endif
233
234     CGLDestroyPixelFormat(pixelFormatObj);
235     
236     if (err != kCGLNoError || !m_contextObj) {
237         // We were unable to create the context.
238         m_contextObj = 0;
239         return;
240     }
241
242     m_isForWebGL2 = attrs.useGLES3;
243
244     // Set the current context to the one given to us.
245     CGLSetCurrentContext(m_contextObj);
246
247 #endif // !PLATFORM(IOS)
248     
249     validateAttributes();
250
251     // Create the WebGLLayer
252     BEGIN_BLOCK_OBJC_EXCEPTIONS
253         m_webGLLayer = adoptNS([[WebGLLayer alloc] initWithGraphicsContext3D:this]);
254 #ifndef NDEBUG
255         [m_webGLLayer setName:@"WebGL Layer"];
256 #endif
257     END_BLOCK_OBJC_EXCEPTIONS
258
259 #if !PLATFORM(IOS)
260     if (useMultisampling)
261         ::glEnable(GL_MULTISAMPLE);
262 #endif
263
264 #if PLATFORM(IOS)
265     ::glGenRenderbuffers(1, &m_texture);
266 #else
267     // create a texture to render into
268     ::glGenTextures(1, &m_texture);
269     ::glBindTexture(GL_TEXTURE_2D, m_texture);
270     ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
271     ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
272     ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
273     ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
274     ::glGenTextures(1, &m_compositorTexture);
275     ::glBindTexture(GL_TEXTURE_2D, m_compositorTexture);
276     ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
277     ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
278     ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
279     ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
280     ::glBindTexture(GL_TEXTURE_2D, 0);
281 #endif
282
283     // create an FBO
284     ::glGenFramebuffersEXT(1, &m_fbo);
285     ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo);
286
287     m_state.boundFBO = m_fbo;
288     if (!m_attrs.antialias && (m_attrs.stencil || m_attrs.depth))
289         ::glGenRenderbuffersEXT(1, &m_depthStencilBuffer);
290
291     // create an multisample FBO
292     if (m_attrs.antialias) {
293         ::glGenFramebuffersEXT(1, &m_multisampleFBO);
294         ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO);
295         m_state.boundFBO = m_multisampleFBO;
296         ::glGenRenderbuffersEXT(1, &m_multisampleColorBuffer);
297         if (m_attrs.stencil || m_attrs.depth)
298             ::glGenRenderbuffersEXT(1, &m_multisampleDepthStencilBuffer);
299     }
300     
301     // ANGLE initialization.
302
303     ShBuiltInResources ANGLEResources;
304     ShInitBuiltInResources(&ANGLEResources);
305
306     getIntegerv(GraphicsContext3D::MAX_VERTEX_ATTRIBS, &ANGLEResources.MaxVertexAttribs);
307     getIntegerv(GraphicsContext3D::MAX_VERTEX_UNIFORM_VECTORS, &ANGLEResources.MaxVertexUniformVectors);
308     getIntegerv(GraphicsContext3D::MAX_VARYING_VECTORS, &ANGLEResources.MaxVaryingVectors);
309     getIntegerv(GraphicsContext3D::MAX_VERTEX_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxVertexTextureImageUnits);
310     getIntegerv(GraphicsContext3D::MAX_COMBINED_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxCombinedTextureImageUnits);
311     getIntegerv(GraphicsContext3D::MAX_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxTextureImageUnits);
312     getIntegerv(GraphicsContext3D::MAX_FRAGMENT_UNIFORM_VECTORS, &ANGLEResources.MaxFragmentUniformVectors);
313
314     // Always set to 1 for OpenGL ES.
315     ANGLEResources.MaxDrawBuffers = 1;
316     
317     GC3Dint range[2], precision;
318     getShaderPrecisionFormat(GraphicsContext3D::FRAGMENT_SHADER, GraphicsContext3D::HIGH_FLOAT, range, &precision);
319     ANGLEResources.FragmentPrecisionHigh = (range[0] || range[1] || precision);
320
321     m_compiler.setResources(ANGLEResources);
322     
323 #if !PLATFORM(IOS)
324     ::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
325     if (!isGLES2Compliant())
326         ::glEnable(GL_POINT_SPRITE);
327 #endif
328
329     ::glClearColor(0, 0, 0, 0);
330 }
331
332 GraphicsContext3D::~GraphicsContext3D()
333 {
334     if (m_contextObj) {
335 #if PLATFORM(IOS)
336         makeContextCurrent();
337         [m_contextObj renderbufferStorage:GL_RENDERBUFFER fromDrawable:nil];
338         ::glDeleteRenderbuffers(1, &m_texture);
339 #else
340         CGLSetCurrentContext(m_contextObj);
341         ::glDeleteTextures(1, &m_texture);
342         ::glDeleteTextures(1, &m_compositorTexture);
343 #endif
344         if (m_attrs.antialias) {
345             ::glDeleteRenderbuffersEXT(1, &m_multisampleColorBuffer);
346             if (m_attrs.stencil || m_attrs.depth)
347                 ::glDeleteRenderbuffersEXT(1, &m_multisampleDepthStencilBuffer);
348             ::glDeleteFramebuffersEXT(1, &m_multisampleFBO);
349         } else {
350             if (m_attrs.stencil || m_attrs.depth)
351                 ::glDeleteRenderbuffersEXT(1, &m_depthStencilBuffer);
352         }
353         ::glDeleteFramebuffersEXT(1, &m_fbo);
354 #if PLATFORM(IOS)
355         [EAGLContext setCurrentContext:0];
356         [static_cast<EAGLContext*>(m_contextObj) release];
357 #else
358         CGLSetCurrentContext(0);
359         CGLDestroyContext(m_contextObj);
360 #endif
361         [m_webGLLayer setContext:nullptr];
362     }
363
364     ASSERT(activeContexts().contains(this));
365     activeContexts().removeFirst(this);
366 }
367
368 #if PLATFORM(IOS)
369 void GraphicsContext3D::setRenderbufferStorageFromDrawable(GC3Dsizei width, GC3Dsizei height)
370 {
371     // We need to make a call to setBounds below to update the backing store size but we also
372     // do not want to clobber the bounds set during layout.
373     CGRect previousBounds = [m_webGLLayer.get() bounds];
374
375     [m_webGLLayer setBounds:CGRectMake(0, 0, width, height)];
376     [m_webGLLayer setOpaque:(m_internalColorFormat != GL_RGBA8)];
377
378     [m_contextObj renderbufferStorage:GL_RENDERBUFFER fromDrawable:static_cast<id<EAGLDrawable>>(m_webGLLayer.get())];
379
380     [m_webGLLayer setBounds:previousBounds];
381 }
382 #endif
383
384 bool GraphicsContext3D::makeContextCurrent()
385 {
386     if (!m_contextObj)
387         return false;
388
389 #if PLATFORM(IOS)
390     if ([EAGLContext currentContext] != m_contextObj)
391         return [EAGLContext setCurrentContext:static_cast<EAGLContext*>(m_contextObj)];
392 #else
393     CGLContextObj currentContext = CGLGetCurrentContext();
394     if (currentContext != m_contextObj)
395         return CGLSetCurrentContext(m_contextObj) == kCGLNoError;
396 #endif
397     return true;
398 }
399
400 void GraphicsContext3D::checkGPUStatusIfNecessary()
401 {
402     bool needsCheck = !GPUCheckCounter;
403     GPUCheckCounter = (GPUCheckCounter + 1) % GPUStatusCheckThreshold;
404
405     if (!needsCheck)
406         return;
407
408     GLint restartStatus = 0;
409 #if PLATFORM(MAC)
410     CGLGetParameter(platformGraphicsContext3D(), kCGLCPGPURestartStatus, &restartStatus);
411     if (restartStatus == kCGLCPGPURestartStatusBlacklisted) {
412         LOG(WebGL, "The GPU has blacklisted us. Terminating.");
413         exit(EX_OSERR);
414     }
415     if (restartStatus == kCGLCPGPURestartStatusCaused) {
416         LOG(WebGL, "The GPU has reset us. Lose the context.");
417         forceContextLost();
418         CGLSetCurrentContext(0);
419     }
420 #elif PLATFORM(IOS)
421     EAGLContext* currentContext = static_cast<EAGLContext*>(PlatformGraphicsContext3D());
422     [currentContext getParameter:kEAGLCPGPURestartStatus to:&restartStatus];
423     if (restartStatus == kEAGLCPGPURestartStatusCaused || restartStatus == kEAGLCPGPURestartStatusBlacklisted) {
424         LOG(WebGL, "The GPU has either reset or blacklisted us. Lose the context.");
425         forceContextLost();
426         [EAGLContext setCurrentContext:0];
427     }
428 #endif
429 }
430
431 #if PLATFORM(IOS)
432 void GraphicsContext3D::endPaint()
433 {
434     makeContextCurrent();
435     if (m_attrs.antialias)
436         resolveMultisamplingIfNecessary();
437     ::glFlush();
438     ::glBindRenderbuffer(GL_RENDERBUFFER, m_texture);
439     [static_cast<EAGLContext*>(m_contextObj) presentRenderbuffer:GL_RENDERBUFFER];
440     [EAGLContext setCurrentContext:nil];
441 }
442 #endif
443
444 bool GraphicsContext3D::isGLES2Compliant() const
445 {
446     return m_isForWebGL2;
447 }
448
449 void GraphicsContext3D::setContextLostCallback(std::unique_ptr<ContextLostCallback>)
450 {
451 }
452
453 void GraphicsContext3D::setErrorMessageCallback(std::unique_ptr<ErrorMessageCallback>)
454 {
455 }
456
457 }
458
459 #endif // ENABLE(GRAPHICS_CONTEXT_3D)