1/*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 * Copyright (C) 2011 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28
29#if USE(3D_GRAPHICS)
30
31#include "GraphicsContext3D.h"
32
33#include "Extensions3DOpenGL.h"
34#include "IntRect.h"
35#include "IntSize.h"
36#include "NotImplemented.h"
37
38#include <algorithm>
39#include <cstring>
40#include <wtf/MainThread.h>
41#include <wtf/text/CString.h>
42
43#if PLATFORM(MAC)
44#include <OpenGL/gl.h>
45#elif PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(QT)
46#include "OpenGLShims.h"
47#endif
48
49namespace WebCore {
50
51void GraphicsContext3D::releaseShaderCompiler()
52{
53    makeContextCurrent();
54    notImplemented();
55}
56
57void GraphicsContext3D::readPixelsAndConvertToBGRAIfNecessary(int x, int y, int width, int height, unsigned char* pixels)
58{
59    ::glReadPixels(x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
60}
61
62void GraphicsContext3D::validateAttributes()
63{
64    validateDepthStencil("GL_EXT_packed_depth_stencil");
65}
66
67bool GraphicsContext3D::reshapeFBOs(const IntSize& size)
68{
69    const int width = size.width();
70    const int height = size.height();
71    GLuint colorFormat, internalDepthStencilFormat = 0;
72    if (m_attrs.alpha) {
73        m_internalColorFormat = GL_RGBA8;
74        colorFormat = GL_RGBA;
75    } else {
76        m_internalColorFormat = GL_RGB8;
77        colorFormat = GL_RGB;
78    }
79    if (m_attrs.stencil || m_attrs.depth) {
80        // We don't allow the logic where stencil is required and depth is not.
81        // See GraphicsContext3D::validateAttributes.
82
83        Extensions3D* extensions = getExtensions();
84        // Use a 24 bit depth buffer where we know we have it.
85        if (extensions->supports("GL_EXT_packed_depth_stencil"))
86            internalDepthStencilFormat = GL_DEPTH24_STENCIL8_EXT;
87        else
88            internalDepthStencilFormat = GL_DEPTH_COMPONENT;
89    }
90
91    bool mustRestoreFBO = false;
92
93    // Resize multisample FBO.
94    if (m_attrs.antialias) {
95        GLint maxSampleCount;
96        ::glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSampleCount);
97        GLint sampleCount = std::min(8, maxSampleCount);
98        if (sampleCount > maxSampleCount)
99            sampleCount = maxSampleCount;
100        if (m_state.boundFBO != m_multisampleFBO) {
101            ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO);
102            mustRestoreFBO = true;
103        }
104        ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_multisampleColorBuffer);
105        ::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, sampleCount, m_internalColorFormat, width, height);
106        ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, m_multisampleColorBuffer);
107        if (m_attrs.stencil || m_attrs.depth) {
108            ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_multisampleDepthStencilBuffer);
109            ::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, sampleCount, internalDepthStencilFormat, width, height);
110            if (m_attrs.stencil)
111                ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_multisampleDepthStencilBuffer);
112            if (m_attrs.depth)
113                ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_multisampleDepthStencilBuffer);
114        }
115        ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
116        if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) {
117            // FIXME: cleanup.
118            notImplemented();
119        }
120    }
121
122    // resize regular FBO
123    if (m_state.boundFBO != m_fbo) {
124        mustRestoreFBO = true;
125        ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo);
126    }
127    ::glBindTexture(GL_TEXTURE_2D, m_texture);
128    ::glTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0);
129    ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_texture, 0);
130    ::glBindTexture(GL_TEXTURE_2D, m_compositorTexture);
131    ::glTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0);
132    ::glBindTexture(GL_TEXTURE_2D, 0);
133    if (!m_attrs.antialias && (m_attrs.stencil || m_attrs.depth)) {
134        ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_depthStencilBuffer);
135        ::glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, internalDepthStencilFormat, width, height);
136        if (m_attrs.stencil)
137            ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_depthStencilBuffer);
138        if (m_attrs.depth)
139            ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_depthStencilBuffer);
140        ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
141    }
142    if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) {
143        // FIXME: cleanup
144        notImplemented();
145    }
146
147    if (m_attrs.antialias) {
148        ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO);
149        if (m_state.boundFBO == m_multisampleFBO)
150            mustRestoreFBO = false;
151    }
152
153    return mustRestoreFBO;
154}
155
156void GraphicsContext3D::resolveMultisamplingIfNecessary(const IntRect& rect)
157{
158    ::glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_multisampleFBO);
159    ::glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_fbo);
160
161    IntRect resolveRect = rect;
162    if (rect.isEmpty())
163        resolveRect = IntRect(0, 0, m_currentWidth, m_currentHeight);
164
165    ::glBlitFramebufferEXT(resolveRect.x(), resolveRect.y(), resolveRect.maxX(), resolveRect.maxY(), resolveRect.x(), resolveRect.y(), resolveRect.maxX(), resolveRect.maxY(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
166}
167
168void GraphicsContext3D::renderbufferStorage(GC3Denum target, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height)
169{
170    makeContextCurrent();
171    switch (internalformat) {
172    case DEPTH_STENCIL:
173        internalformat = GL_DEPTH24_STENCIL8_EXT;
174        break;
175    case DEPTH_COMPONENT16:
176        internalformat = GL_DEPTH_COMPONENT;
177        break;
178    case RGBA4:
179    case RGB5_A1:
180        internalformat = GL_RGBA;
181        break;
182    case RGB565:
183        internalformat = GL_RGB;
184        break;
185    }
186    ::glRenderbufferStorageEXT(target, internalformat, width, height);
187}
188
189void GraphicsContext3D::getIntegerv(GC3Denum pname, GC3Dint* value)
190{
191    // Need to emulate MAX_FRAGMENT/VERTEX_UNIFORM_VECTORS and MAX_VARYING_VECTORS
192    // because desktop GL's corresponding queries return the number of components
193    // whereas GLES2 return the number of vectors (each vector has 4 components).
194    // Therefore, the value returned by desktop GL needs to be divided by 4.
195    makeContextCurrent();
196    switch (pname) {
197    case MAX_FRAGMENT_UNIFORM_VECTORS:
198        ::glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, value);
199        *value /= 4;
200        break;
201    case MAX_VERTEX_UNIFORM_VECTORS:
202        ::glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, value);
203        *value /= 4;
204        break;
205    case MAX_VARYING_VECTORS:
206        ::glGetIntegerv(GL_MAX_VARYING_FLOATS, value);
207        *value /= 4;
208        break;
209    default:
210        ::glGetIntegerv(pname, value);
211    }
212}
213
214void GraphicsContext3D::getShaderPrecisionFormat(GC3Denum shaderType, GC3Denum precisionType, GC3Dint* range, GC3Dint* precision)
215{
216    UNUSED_PARAM(shaderType);
217    ASSERT(range);
218    ASSERT(precision);
219
220    makeContextCurrent();
221
222    switch (precisionType) {
223    case GraphicsContext3D::LOW_INT:
224    case GraphicsContext3D::MEDIUM_INT:
225    case GraphicsContext3D::HIGH_INT:
226        // These values are for a 32-bit twos-complement integer format.
227        range[0] = 31;
228        range[1] = 30;
229        precision[0] = 0;
230        break;
231    case GraphicsContext3D::LOW_FLOAT:
232    case GraphicsContext3D::MEDIUM_FLOAT:
233    case GraphicsContext3D::HIGH_FLOAT:
234        // These values are for an IEEE single-precision floating-point format.
235        range[0] = 127;
236        range[1] = 127;
237        precision[0] = 23;
238        break;
239    default:
240        ASSERT_NOT_REACHED();
241        break;
242    }
243}
244
245bool GraphicsContext3D::texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dint border, GC3Denum format, GC3Denum type, const void* pixels)
246{
247    if (width && height && !pixels) {
248        synthesizeGLError(INVALID_VALUE);
249        return false;
250    }
251
252    GC3Denum openGLInternalFormat = internalformat;
253    if (type == GL_FLOAT) {
254        if (format == GL_RGBA)
255            openGLInternalFormat = GL_RGBA32F_ARB;
256        else if (format == GL_RGB)
257            openGLInternalFormat = GL_RGB32F_ARB;
258    } else if (type == HALF_FLOAT_OES) {
259        if (format == GL_RGBA)
260            openGLInternalFormat = GL_RGBA16F_ARB;
261        else if (format == GL_RGB)
262            openGLInternalFormat = GL_RGB16F_ARB;
263        else if (format == GL_LUMINANCE)
264            openGLInternalFormat = GL_LUMINANCE16F_ARB;
265        else if (format == GL_ALPHA)
266            openGLInternalFormat = GL_ALPHA16F_ARB;
267        else if (format == GL_LUMINANCE_ALPHA)
268            openGLInternalFormat = GL_LUMINANCE_ALPHA16F_ARB;
269        type = GL_HALF_FLOAT_ARB;
270    }
271    texImage2DDirect(target, level, openGLInternalFormat, width, height, border, format, type, pixels);
272    return true;
273}
274
275void GraphicsContext3D::depthRange(GC3Dclampf zNear, GC3Dclampf zFar)
276{
277    makeContextCurrent();
278    ::glDepthRange(zNear, zFar);
279}
280
281void GraphicsContext3D::clearDepth(GC3Dclampf depth)
282{
283    makeContextCurrent();
284    ::glClearDepth(depth);
285}
286
287Extensions3D* GraphicsContext3D::getExtensions()
288{
289    if (!m_extensions)
290        m_extensions = adoptPtr(new Extensions3DOpenGL(this));
291    return m_extensions.get();
292}
293
294void GraphicsContext3D::readPixels(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, void* data)
295{
296    // FIXME: remove the two glFlush calls when the driver bug is fixed, i.e.,
297    // all previous rendering calls should be done before reading pixels.
298    makeContextCurrent();
299    ::glFlush();
300    if (m_attrs.antialias && m_state.boundFBO == m_multisampleFBO) {
301        resolveMultisamplingIfNecessary(IntRect(x, y, width, height));
302        ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_fbo);
303        ::glFlush();
304    }
305    ::glReadPixels(x, y, width, height, format, type, data);
306    if (m_attrs.antialias && m_state.boundFBO == m_multisampleFBO)
307        ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
308}
309
310}
311
312#endif // USE(3D_GRAPHICS)
313