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 USE(3D_GRAPHICS)
29
30#include "GraphicsContext3D.h"
31#if PLATFORM(IOS)
32#include "GraphicsContext3DIOS.h"
33#endif
34
35#import "BlockExceptions.h"
36
37#include "ANGLE/ShaderLang.h"
38#include "CanvasRenderingContext.h"
39#include <CoreGraphics/CGBitmapContext.h>
40#include "Extensions3DOpenGL.h"
41#include "GraphicsContext.h"
42#include "HTMLCanvasElement.h"
43#include "ImageBuffer.h"
44#if PLATFORM(IOS)
45#import <OpenGLES/ES2/glext.h>
46#import <OpenGLES/EAGL.h>
47#import <OpenGLES/EAGLDrawable.h>
48#import <QuartzCore/QuartzCore.h>
49#else
50#include <OpenGL/CGLRenderers.h>
51#include <OpenGL/gl.h>
52#endif
53#include "WebGLLayer.h"
54#include "WebGLObject.h"
55#include <runtime/ArrayBuffer.h>
56#include <runtime/ArrayBufferView.h>
57#include <runtime/Int32Array.h>
58#include <runtime/Float32Array.h>
59#include <runtime/Uint8Array.h>
60#include <wtf/text/CString.h>
61
62namespace WebCore {
63
64const int maxActiveContexts = 16;
65int GraphicsContext3D::numActiveContexts = 0;
66
67// FIXME: This class is currently empty on Mac, but will get populated as
68// the restructuring in https://bugs.webkit.org/show_bug.cgi?id=66903 is done
69class GraphicsContext3DPrivate {
70public:
71    GraphicsContext3DPrivate(GraphicsContext3D*) { }
72
73    ~GraphicsContext3DPrivate() { }
74};
75
76#if !PLATFORM(IOS)
77static void setPixelFormat(Vector<CGLPixelFormatAttribute>& attribs, int colorBits, int depthBits, bool accelerated, bool supersample, bool closest, bool antialias)
78{
79    attribs.clear();
80
81    attribs.append(kCGLPFAColorSize);
82    attribs.append(static_cast<CGLPixelFormatAttribute>(colorBits));
83    attribs.append(kCGLPFADepthSize);
84    attribs.append(static_cast<CGLPixelFormatAttribute>(depthBits));
85
86    if (accelerated)
87        attribs.append(kCGLPFAAccelerated);
88    else {
89        attribs.append(kCGLPFARendererID);
90        attribs.append(static_cast<CGLPixelFormatAttribute>(kCGLRendererGenericFloatID));
91    }
92
93    if (supersample && !antialias)
94        attribs.append(kCGLPFASupersample);
95
96    if (closest)
97        attribs.append(kCGLPFAClosestPolicy);
98
99    if (antialias) {
100        attribs.append(kCGLPFAMultisample);
101        attribs.append(kCGLPFASampleBuffers);
102        attribs.append(static_cast<CGLPixelFormatAttribute>(1));
103        attribs.append(kCGLPFASamples);
104        attribs.append(static_cast<CGLPixelFormatAttribute>(4));
105    }
106
107    attribs.append(static_cast<CGLPixelFormatAttribute>(0));
108}
109#endif // !PLATFORM(IOS)
110
111PassRefPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3D::Attributes attrs, HostWindow* hostWindow, GraphicsContext3D::RenderStyle renderStyle)
112{
113    // This implementation doesn't currently support rendering directly to the HostWindow.
114    if (renderStyle == RenderDirectlyToHostWindow)
115        return nullptr;
116
117    if (numActiveContexts >= maxActiveContexts)
118        return nullptr;
119
120    RefPtr<GraphicsContext3D> context = adoptRef(new GraphicsContext3D(attrs, hostWindow, renderStyle));
121
122    if (!context->m_contextObj)
123        return nullptr;
124
125    numActiveContexts++;
126
127    return context.release();
128}
129
130GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attrs, HostWindow* hostWindow, GraphicsContext3D::RenderStyle renderStyle)
131    : m_currentWidth(0)
132    , m_currentHeight(0)
133    , m_contextObj(0)
134#if PLATFORM(IOS)
135    , m_compiler(SH_ESSL_OUTPUT)
136#endif
137    , m_attrs(attrs)
138    , m_texture(0)
139    , m_compositorTexture(0)
140    , m_fbo(0)
141    , m_depthStencilBuffer(0)
142    , m_layerComposited(false)
143    , m_internalColorFormat(0)
144    , m_multisampleFBO(0)
145    , m_multisampleDepthStencilBuffer(0)
146    , m_multisampleColorBuffer(0)
147    , m_private(std::make_unique<GraphicsContext3DPrivate>(this))
148{
149    UNUSED_PARAM(hostWindow);
150    UNUSED_PARAM(renderStyle);
151
152#if PLATFORM(IOS)
153    m_contextObj = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
154    makeContextCurrent();
155#else
156    Vector<CGLPixelFormatAttribute> attribs;
157    CGLPixelFormatObj pixelFormatObj = 0;
158    GLint numPixelFormats = 0;
159
160    // If we're configured to demand the software renderer, we'll
161    // do so. We attempt to create contexts in this order:
162    //
163    // 1) 32 bit RGBA/32 bit depth/supersampled
164    // 2) 32 bit RGBA/32 bit depth
165    // 3) 32 bit RGBA/16 bit depth
166    //
167    // If we were not forced into software mode already, our final attempt is
168    // to try that:
169    //
170    // 4) closest to 32 bit RGBA/16 bit depth/software renderer
171    //
172    // If none of that works, we simply fail and set m_contextObj to 0.
173
174    bool useMultisampling = m_attrs.antialias;
175
176    setPixelFormat(attribs, 32, 32, !attrs.forceSoftwareRenderer, true, false, useMultisampling);
177    CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats);
178
179    if (numPixelFormats == 0) {
180        setPixelFormat(attribs, 32, 32, !attrs.forceSoftwareRenderer, false, false, useMultisampling);
181        CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats);
182
183        if (numPixelFormats == 0) {
184            setPixelFormat(attribs, 32, 16, !attrs.forceSoftwareRenderer, false, false, useMultisampling);
185            CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats);
186
187             if (!attrs.forceSoftwareRenderer && numPixelFormats == 0) {
188                 setPixelFormat(attribs, 32, 16, false, false, true, false);
189                 CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats);
190                 useMultisampling = false;
191            }
192        }
193    }
194
195    if (numPixelFormats == 0)
196        return;
197
198    CGLError err = CGLCreateContext(pixelFormatObj, 0, &m_contextObj);
199    CGLDestroyPixelFormat(pixelFormatObj);
200
201    if (err != kCGLNoError || !m_contextObj) {
202        // We were unable to create the context.
203        m_contextObj = 0;
204        return;
205    }
206
207    // Set the current context to the one given to us.
208    CGLSetCurrentContext(m_contextObj);
209
210    if (attrs.multithreaded) {
211        err = CGLEnable(m_contextObj, kCGLCEMPEngine);
212        if (err != kCGLNoError) {
213            // We could not create a multi-threaded context.
214            m_contextObj = 0;
215            return;
216        }
217    }
218#endif // !PLATFORM(IOS)
219
220    validateAttributes();
221
222    // Create the WebGLLayer
223    BEGIN_BLOCK_OBJC_EXCEPTIONS
224        m_webGLLayer = adoptNS([[WebGLLayer alloc] initWithGraphicsContext3D:this]);
225#if PLATFORM(IOS)
226        [m_webGLLayer setOpaque:0];
227#endif
228#ifndef NDEBUG
229        [m_webGLLayer setName:@"WebGL Layer"];
230#endif
231    END_BLOCK_OBJC_EXCEPTIONS
232
233#if !PLATFORM(IOS)
234    if (useMultisampling)
235        ::glEnable(GL_MULTISAMPLE);
236#endif
237
238#if PLATFORM(IOS)
239    ::glGenRenderbuffers(1, &m_texture);
240#else
241    // create a texture to render into
242    ::glGenTextures(1, &m_texture);
243    ::glBindTexture(GL_TEXTURE_2D, m_texture);
244    ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
245    ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
246    ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
247    ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
248    ::glGenTextures(1, &m_compositorTexture);
249    ::glBindTexture(GL_TEXTURE_2D, m_compositorTexture);
250    ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
251    ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
252    ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
253    ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
254    ::glBindTexture(GL_TEXTURE_2D, 0);
255#endif
256
257    // create an FBO
258    ::glGenFramebuffersEXT(1, &m_fbo);
259    ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo);
260
261    m_state.boundFBO = m_fbo;
262    if (!m_attrs.antialias && (m_attrs.stencil || m_attrs.depth))
263        ::glGenRenderbuffersEXT(1, &m_depthStencilBuffer);
264
265    // create an multisample FBO
266    if (m_attrs.antialias) {
267        ::glGenFramebuffersEXT(1, &m_multisampleFBO);
268        ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO);
269        m_state.boundFBO = m_multisampleFBO;
270        ::glGenRenderbuffersEXT(1, &m_multisampleColorBuffer);
271        if (m_attrs.stencil || m_attrs.depth)
272            ::glGenRenderbuffersEXT(1, &m_multisampleDepthStencilBuffer);
273    }
274
275    // ANGLE initialization.
276
277    ShBuiltInResources ANGLEResources;
278    ShInitBuiltInResources(&ANGLEResources);
279
280    getIntegerv(GraphicsContext3D::MAX_VERTEX_ATTRIBS, &ANGLEResources.MaxVertexAttribs);
281    getIntegerv(GraphicsContext3D::MAX_VERTEX_UNIFORM_VECTORS, &ANGLEResources.MaxVertexUniformVectors);
282    getIntegerv(GraphicsContext3D::MAX_VARYING_VECTORS, &ANGLEResources.MaxVaryingVectors);
283    getIntegerv(GraphicsContext3D::MAX_VERTEX_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxVertexTextureImageUnits);
284    getIntegerv(GraphicsContext3D::MAX_COMBINED_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxCombinedTextureImageUnits);
285    getIntegerv(GraphicsContext3D::MAX_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxTextureImageUnits);
286    getIntegerv(GraphicsContext3D::MAX_FRAGMENT_UNIFORM_VECTORS, &ANGLEResources.MaxFragmentUniformVectors);
287
288    // Always set to 1 for OpenGL ES.
289    ANGLEResources.MaxDrawBuffers = 1;
290
291    GC3Dint range[2], precision;
292    getShaderPrecisionFormat(GraphicsContext3D::FRAGMENT_SHADER, GraphicsContext3D::HIGH_FLOAT, range, &precision);
293    ANGLEResources.FragmentPrecisionHigh = (range[0] || range[1] || precision);
294
295    m_compiler.setResources(ANGLEResources);
296
297#if !PLATFORM(IOS)
298    ::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
299    ::glEnable(GL_POINT_SPRITE);
300#endif
301
302    ::glClearColor(0, 0, 0, 0);
303}
304
305GraphicsContext3D::~GraphicsContext3D()
306{
307    if (m_contextObj) {
308#if PLATFORM(IOS)
309        makeContextCurrent();
310        [m_contextObj renderbufferStorage:GL_RENDERBUFFER fromDrawable:nil];
311        ::glDeleteRenderbuffers(1, &m_texture);
312        ::glDeleteRenderbuffers(1, &m_compositorTexture);
313#else
314        CGLSetCurrentContext(m_contextObj);
315        ::glDeleteTextures(1, &m_texture);
316        ::glDeleteTextures(1, &m_compositorTexture);
317#endif
318        if (m_attrs.antialias) {
319            ::glDeleteRenderbuffersEXT(1, &m_multisampleColorBuffer);
320            if (m_attrs.stencil || m_attrs.depth)
321                ::glDeleteRenderbuffersEXT(1, &m_multisampleDepthStencilBuffer);
322            ::glDeleteFramebuffersEXT(1, &m_multisampleFBO);
323        } else {
324            if (m_attrs.stencil || m_attrs.depth)
325                ::glDeleteRenderbuffersEXT(1, &m_depthStencilBuffer);
326        }
327        ::glDeleteFramebuffersEXT(1, &m_fbo);
328#if PLATFORM(IOS)
329        [EAGLContext setCurrentContext:0];
330        [static_cast<EAGLContext*>(m_contextObj) release];
331#else
332        CGLSetCurrentContext(0);
333        CGLDestroyContext(m_contextObj);
334#endif
335        [m_webGLLayer setContext:nullptr];
336        numActiveContexts--;
337    }
338}
339
340#if PLATFORM(IOS)
341bool GraphicsContext3D::setRenderbufferStorageFromDrawable(GC3Dsizei width, GC3Dsizei height)
342{
343    [m_webGLLayer setBounds:CGRectMake(0, 0, width, height)];
344    return [m_contextObj renderbufferStorage:GL_RENDERBUFFER fromDrawable:static_cast<NSObject<EAGLDrawable>*>(m_webGLLayer.get())];
345}
346#endif
347
348bool GraphicsContext3D::makeContextCurrent()
349{
350    if (!m_contextObj)
351        return false;
352
353#if PLATFORM(IOS)
354    if ([EAGLContext currentContext] != m_contextObj)
355        return [EAGLContext setCurrentContext:static_cast<EAGLContext*>(m_contextObj)];
356#else
357    CGLContextObj currentContext = CGLGetCurrentContext();
358    if (currentContext != m_contextObj)
359        return CGLSetCurrentContext(m_contextObj) == kCGLNoError;
360#endif
361    return true;
362}
363
364#if PLATFORM(IOS)
365void GraphicsContext3D::endPaint()
366{
367    makeContextCurrent();
368    ::glFlush();
369    ::glBindRenderbuffer(GL_RENDERBUFFER, m_texture);
370    [static_cast<EAGLContext*>(m_contextObj) presentRenderbuffer:GL_RENDERBUFFER];
371    [EAGLContext setCurrentContext:nil];
372}
373#endif
374
375bool GraphicsContext3D::isGLES2Compliant() const
376{
377    return false;
378}
379
380void GraphicsContext3D::setContextLostCallback(std::unique_ptr<ContextLostCallback>)
381{
382}
383
384void GraphicsContext3D::setErrorMessageCallback(std::unique_ptr<ErrorMessageCallback>)
385{
386}
387
388}
389
390#endif // USE(3D_GRAPHICS)
391