1/*
2 Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
3 Copyright (C) 2013 Intel Corporation.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB.  If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19 */
20
21#include "config.h"
22#include "GraphicsSurface.h"
23
24#if USE(GRAPHICS_SURFACE)
25
26#include "NotImplemented.h"
27#include "TextureMapperGL.h"
28
29#include <opengl/GLDefs.h>
30
31#include "GLXConfigSelector.h"
32
33namespace WebCore {
34
35static PFNGLXBINDTEXIMAGEEXTPROC pGlXBindTexImageEXT = 0;
36static PFNGLXRELEASETEXIMAGEEXTPROC pGlXReleaseTexImageEXT = 0;
37static PFNGLBINDFRAMEBUFFERPROC pGlBindFramebuffer = 0;
38static PFNGLBLITFRAMEBUFFERPROC pGlBlitFramebuffer = 0;
39static PFNGLGENFRAMEBUFFERSPROC pGlGenFramebuffers = 0;
40static PFNGLDELETEFRAMEBUFFERSPROC pGlDeleteFramebuffers = 0;
41static PFNGLFRAMEBUFFERTEXTURE2DPROC pGlFramebufferTexture2D = 0;
42
43static int glxAttributes[] = {
44    GLX_TEXTURE_FORMAT_EXT,
45    GLX_TEXTURE_FORMAT_RGBA_EXT,
46    GLX_TEXTURE_TARGET_EXT,
47    GLX_TEXTURE_2D_EXT,
48    0
49};
50
51static bool isMesaGLX()
52{
53    static bool isMesa = !!strstr(glXGetClientString(X11Helper::nativeDisplay(), GLX_VENDOR), "Mesa");
54    return isMesa;
55}
56
57struct GraphicsSurfacePrivate {
58    GraphicsSurfacePrivate(const PlatformGraphicsContext3D shareContext = 0)
59        : m_xPixmap(0)
60        , m_glxPixmap(0)
61        , m_surface(0)
62        , m_glxSurface(0)
63        , m_glContext(0)
64        , m_detachedContext(0)
65        , m_detachedSurface(0)
66        , m_isReceiver(false)
67        , m_texture(0)
68    {
69        GLXContext shareContextObject = 0;
70
71        UNUSED_PARAM(shareContext);
72
73        GLPlatformSurface::SurfaceAttributes sharedSurfaceAttributes = GLPlatformSurface::DoubleBuffered |
74            GLPlatformSurface::SupportAlpha;
75        m_configSelector = adoptPtr(new GLXConfigSelector(sharedSurfaceAttributes));
76
77        if (!m_configSelector->surfaceContextConfig()) {
78            clear();
79            return;
80        }
81
82        // Create a GLX context for OpenGL rendering
83        m_glContext = glXCreateNewContext(display(), m_configSelector->surfaceContextConfig(), GLX_RGBA_TYPE, shareContextObject, true);
84    }
85
86    GraphicsSurfacePrivate(uint32_t winId)
87        : m_xPixmap(0)
88        , m_glxPixmap(0)
89        , m_surface(winId)
90        , m_glxSurface(0)
91        , m_glContext(0)
92        , m_detachedContext(0)
93        , m_detachedSurface(0)
94        , m_isReceiver(true)
95        , m_texture(0)
96    {
97    }
98
99    ~GraphicsSurfacePrivate()
100    {
101        clear();
102    }
103
104    uint32_t createSurface(const IntSize& size)
105    {
106        if (!display() || !m_configSelector)
107            return 0;
108
109        GLXFBConfig config = m_configSelector->surfaceContextConfig();
110        OwnPtrX11<XVisualInfo> visInfo(m_configSelector->visualInfo(config));
111
112        if (!visInfo.get()) {
113            clear();
114            return 0;
115        }
116
117        X11Helper::createOffScreenWindow(&m_surface, *visInfo.get(), size);
118
119        if (!m_surface) {
120            clear();
121            return 0;
122        }
123
124        m_glxSurface = glXCreateWindow(display(), config, m_surface, 0);
125        return m_surface;
126    }
127
128    void createPixmap(uint32_t winId)
129    {
130        XWindowAttributes attr;
131        if (!XGetWindowAttributes(display(), winId, &attr))
132            return;
133
134        // Ensure that the window is mapped.
135        if (attr.map_state == IsUnmapped || attr.map_state == IsUnviewable)
136            return;
137
138        ScopedXPixmapCreationErrorHandler handler;
139        m_size = IntSize(attr.width, attr.height);
140
141        XRenderPictFormat* format = XRenderFindVisualFormat(display(), attr.visual);
142        bool hasAlpha = (format->type == PictTypeDirect && format->direct.alphaMask);
143        m_xPixmap = XCompositeNameWindowPixmap(display(), winId);
144        glxAttributes[1] = (format->depth == 32 && hasAlpha) ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT;
145
146        GLPlatformSurface::SurfaceAttributes sharedSurfaceAttributes = GLPlatformSurface::Default;
147        if (hasAlpha)
148            sharedSurfaceAttributes = GLPlatformSurface::SupportAlpha;
149
150        if (!m_configSelector)
151            m_configSelector = adoptPtr(new GLXConfigSelector(sharedSurfaceAttributes));
152
153        m_glxPixmap = glXCreatePixmap(display(), m_configSelector->surfaceClientConfig(format->depth, XVisualIDFromVisual(attr.visual)), m_xPixmap, glxAttributes);
154
155        if (!handler.isValidOperation())
156            clear();
157        else {
158            uint inverted = 0;
159            glXQueryDrawable(display(), m_glxPixmap, GLX_Y_INVERTED_EXT, &inverted);
160            m_flags = !!inverted ? TextureMapperGL::ShouldFlipTexture : 0;
161
162            if (hasAlpha)
163                m_flags |= TextureMapperGL::ShouldBlend;
164        }
165    }
166
167    void makeCurrent()
168    {
169        m_detachedContext = glXGetCurrentContext();
170        m_detachedSurface = glXGetCurrentDrawable();
171        if (m_surface && m_glContext)
172            glXMakeCurrent(display(), m_surface, m_glContext);
173    }
174
175    void doneCurrent()
176    {
177        if (m_detachedContext)
178            glXMakeCurrent(display(), m_detachedSurface, m_detachedContext);
179        m_detachedContext = 0;
180    }
181
182    void swapBuffers()
183    {
184        if (isReceiver()) {
185            if (isMesaGLX() && textureID()) {
186                glBindTexture(GL_TEXTURE_2D, textureID());
187                // Mesa doesn't re-bind texture to the front buffer on glXSwapBufer
188                // Manually release previous lock and rebind texture to surface to ensure frame updates.
189                pGlXReleaseTexImageEXT(display(), glxPixmap(), GLX_FRONT_EXT);
190                pGlXBindTexImageEXT(display(), glxPixmap(), GLX_FRONT_EXT, 0);
191            }
192
193            return;
194        }
195
196        GLXContext glContext = glXGetCurrentContext();
197
198        if (m_surface && glContext) {
199            GLint oldFBO;
200            glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO);
201            pGlBindFramebuffer(GL_FRAMEBUFFER, 0);
202            glXSwapBuffers(display(), m_surface);
203            pGlBindFramebuffer(GL_FRAMEBUFFER, oldFBO);
204        }
205    }
206
207    void copyFromTexture(uint32_t texture, const IntRect& sourceRect)
208    {
209        makeCurrent();
210        int x = sourceRect.x();
211        int y = sourceRect.y();
212        int width = sourceRect.width();
213        int height = sourceRect.height();
214
215        glPushAttrib(GL_ALL_ATTRIB_BITS);
216        GLint previousFBO;
217        glGetIntegerv(GL_FRAMEBUFFER_BINDING, &previousFBO);
218
219        GLuint originFBO;
220        pGlGenFramebuffers(1, &originFBO);
221        pGlBindFramebuffer(GL_READ_FRAMEBUFFER, originFBO);
222        glBindTexture(GL_TEXTURE_2D, texture);
223        pGlFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
224
225        pGlBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
226        pGlBlitFramebuffer(x, y, width, height, x, y, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
227
228        pGlFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
229        glBindTexture(GL_TEXTURE_2D, 0);
230        pGlBindFramebuffer(GL_FRAMEBUFFER, previousFBO);
231        pGlDeleteFramebuffers(1, &originFBO);
232
233        glPopAttrib();
234
235        swapBuffers();
236        doneCurrent();
237    }
238
239    Display* display() const { return X11Helper::nativeDisplay(); }
240
241    GLXPixmap glxPixmap() const
242    {
243        if (!m_glxPixmap && m_surface)
244            const_cast<GraphicsSurfacePrivate*>(this)->createPixmap(m_surface);
245        return m_glxPixmap;
246    }
247
248    IntSize size() const
249    {
250        if (m_size.isEmpty()) {
251            XWindowAttributes attr;
252            if (XGetWindowAttributes(display(), m_surface, &attr))
253                const_cast<GraphicsSurfacePrivate*>(this)->m_size = IntSize(attr.width, attr.height);
254        }
255        return m_size;
256    }
257
258    bool isReceiver() const { return m_isReceiver; }
259
260    TextureMapperGL::Flags flags() const { return m_flags; }
261
262    Window surface() const { return m_surface; }
263
264    GLuint textureID() const
265    {
266        if (m_texture)
267            return m_texture;
268
269        GLXPixmap pixmap = glxPixmap();
270        if (!pixmap)
271            return 0;
272
273        GLuint texture;
274        glGenTextures(1, &texture);
275        glBindTexture(GL_TEXTURE_2D, texture);
276        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
277        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_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        pGlXBindTexImageEXT(display(), pixmap, GLX_FRONT_EXT, 0);
281        const_cast<GraphicsSurfacePrivate*>(this)->m_texture = texture;
282
283        return texture;
284    }
285private:
286    void clear()
287    {
288        if (m_texture) {
289            pGlXReleaseTexImageEXT(display(), glxPixmap(), GLX_FRONT_EXT);
290            glDeleteTextures(1, &m_texture);
291        }
292
293        if (m_glxPixmap) {
294            glXDestroyPixmap(display(), m_glxPixmap);
295            m_glxPixmap = 0;
296        }
297
298        if (m_xPixmap) {
299            XFreePixmap(display(), m_xPixmap);
300            m_xPixmap = 0;
301        }
302
303        // Client doesn't own the window. Delete surface only on writing side.
304        if (!m_isReceiver && m_surface) {
305            XDestroyWindow(display(), m_surface);
306            m_surface = 0;
307        }
308
309        if (m_glContext) {
310            glXDestroyContext(display(), m_glContext);
311            m_glContext = 0;
312        }
313
314        if (m_configSelector)
315            m_configSelector = nullptr;
316    }
317
318    IntSize m_size;
319    Pixmap m_xPixmap;
320    GLXPixmap m_glxPixmap;
321    uint32_t m_surface;
322    Window m_glxSurface;
323    GLXContext m_glContext;
324    GLXContext m_detachedContext;
325    GLXDrawable m_detachedSurface;
326    OwnPtr<GLXConfigSelector> m_configSelector;
327    bool m_isReceiver;
328    TextureMapperGL::Flags m_flags;
329    GLuint m_texture;
330};
331
332static bool resolveGLMethods()
333{
334    static bool resolved = false;
335    if (resolved)
336        return true;
337    pGlXBindTexImageEXT = reinterpret_cast<PFNGLXBINDTEXIMAGEEXTPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glXBindTexImageEXT")));
338    pGlXReleaseTexImageEXT = reinterpret_cast<PFNGLXRELEASETEXIMAGEEXTPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glXReleaseTexImageEXT")));
339    pGlBindFramebuffer = reinterpret_cast<PFNGLBINDFRAMEBUFFERPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glBindFramebuffer")));
340    pGlBlitFramebuffer = reinterpret_cast<PFNGLBLITFRAMEBUFFERPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glBlitFramebuffer")));
341
342    pGlGenFramebuffers = reinterpret_cast<PFNGLGENFRAMEBUFFERSPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glGenFramebuffers")));
343    pGlDeleteFramebuffers = reinterpret_cast<PFNGLDELETEFRAMEBUFFERSPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glDeleteFramebuffers")));
344    pGlFramebufferTexture2D = reinterpret_cast<PFNGLFRAMEBUFFERTEXTURE2DPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glFramebufferTexture2D")));
345    resolved = pGlBlitFramebuffer && pGlBindFramebuffer && pGlXBindTexImageEXT && pGlXReleaseTexImageEXT;
346
347    return resolved;
348}
349
350GraphicsSurfaceToken GraphicsSurface::platformExport()
351{
352    return GraphicsSurfaceToken(m_private->surface());
353}
354
355uint32_t GraphicsSurface::platformGetTextureID()
356{
357    return m_private->textureID();
358}
359
360void GraphicsSurface::platformCopyToGLTexture(uint32_t /*target*/, uint32_t /*id*/, const IntRect& /*targetRect*/, const IntPoint& /*offset*/)
361{
362    // This is not supported by GLX/Xcomposite.
363}
364
365void GraphicsSurface::platformCopyFromTexture(uint32_t texture, const IntRect& sourceRect)
366{
367    m_private->copyFromTexture(texture, sourceRect);
368}
369
370void GraphicsSurface::platformPaintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& transform, float opacity)
371{
372    IntSize size = m_private->size();
373    if (size.isEmpty())
374        return;
375    uint32_t texture = platformGetTextureID();
376    if (!texture)
377        return;
378
379    FloatRect rectOnContents(FloatPoint::zero(), size);
380    TransformationMatrix adjustedTransform = transform;
381    adjustedTransform.multiply(TransformationMatrix::rectToRect(rectOnContents, targetRect));
382    static_cast<TextureMapperGL*>(textureMapper)->drawTexture(texture, m_private->flags(), size, rectOnContents, adjustedTransform, opacity);
383}
384
385uint32_t GraphicsSurface::platformFrontBuffer() const
386{
387    return 0;
388}
389
390uint32_t GraphicsSurface::platformSwapBuffers()
391{
392    m_private->swapBuffers();
393    return 0;
394}
395
396IntSize GraphicsSurface::platformSize() const
397{
398    return m_private->size();
399}
400
401PassRefPtr<GraphicsSurface> GraphicsSurface::platformCreate(const IntSize& size, Flags flags, const PlatformGraphicsContext3D shareContext)
402{
403    // X11 does not support CopyToTexture, so we do not create a GraphicsSurface if this is requested.
404    // GraphicsSurfaceGLX uses an XWindow as native surface. This one always has a front and a back buffer.
405    // Therefore single buffered GraphicsSurfaces are not supported.
406    if (flags & SupportsCopyToTexture || flags & SupportsSingleBuffered)
407        return PassRefPtr<GraphicsSurface>();
408
409    RefPtr<GraphicsSurface> surface = adoptRef(new GraphicsSurface(size, flags));
410
411    surface->m_private = new GraphicsSurfacePrivate(shareContext);
412    if (!resolveGLMethods())
413        return PassRefPtr<GraphicsSurface>();
414
415    surface->m_private->createSurface(size);
416
417    return surface;
418}
419
420PassRefPtr<GraphicsSurface> GraphicsSurface::platformImport(const IntSize& size, Flags flags, const GraphicsSurfaceToken& token)
421{
422    // X11 does not support CopyToTexture, so we do not create a GraphicsSurface if this is requested.
423    // GraphicsSurfaceGLX uses an XWindow as native surface. This one always has a front and a back buffer.
424    // Therefore single buffered GraphicsSurfaces are not supported.
425    if (flags & SupportsCopyToTexture || flags & SupportsSingleBuffered)
426        return PassRefPtr<GraphicsSurface>();
427
428    RefPtr<GraphicsSurface> surface = adoptRef(new GraphicsSurface(size, flags));
429
430    surface->m_private = new GraphicsSurfacePrivate(token.frontBufferHandle);
431    if (!resolveGLMethods())
432        return PassRefPtr<GraphicsSurface>();
433
434    return surface;
435}
436
437char* GraphicsSurface::platformLock(const IntRect&, int* /*outputStride*/, LockOptions)
438{
439    // GraphicsSurface is currently only being used for WebGL, which does not require this locking mechanism.
440    return 0;
441}
442
443void GraphicsSurface::platformUnlock()
444{
445    // GraphicsSurface is currently only being used for WebGL, which does not require this locking mechanism.
446}
447
448void GraphicsSurface::platformDestroy()
449{
450    delete m_private;
451    m_private = 0;
452}
453
454std::unique_ptr<GraphicsContext> GraphicsSurface::platformBeginPaint(const IntSize&, char*, int)
455{
456    notImplemented();
457    return nullptr;
458}
459
460PassRefPtr<Image> GraphicsSurface::createReadOnlyImage(const IntRect&)
461{
462    notImplemented();
463    return 0;
464}
465}
466#endif
467