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