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