2 Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
21 #include "GraphicsSurface.h"
23 #if USE(GRAPHICS_SURFACE)
25 #include "NotImplemented.h"
26 #include "TextureMapperGL.h"
29 // Qt headers must be included before glx headers.
30 #include <QGuiApplication>
31 #include <QOpenGLContext>
32 #include <qpa/qplatformnativeinterface.h>
40 #include <X11/extensions/Xcomposite.h>
41 #include <X11/extensions/Xrender.h>
45 static long X11OverrideRedirect = 1L << 9;
47 static PFNGLXBINDTEXIMAGEEXTPROC pGlXBindTexImageEXT = 0;
48 static PFNGLXRELEASETEXIMAGEEXTPROC pGlXReleaseTexImageEXT = 0;
49 static PFNGLBINDFRAMEBUFFERPROC pGlBindFramebuffer = 0;
50 static PFNGLBLITFRAMEBUFFERPROC pGlBlitFramebuffer = 0;
51 static PFNGLGENFRAMEBUFFERSPROC pGlGenFramebuffers = 0;
52 static PFNGLDELETEFRAMEBUFFERSPROC pGlDeleteFramebuffers = 0;
53 static PFNGLFRAMEBUFFERTEXTURE2DPROC pGlFramebufferTexture2D = 0;
55 // Used for handling XError.
56 static bool validOperation = true;
57 static int handleXPixmapCreationError(Display*, XErrorEvent* event)
59 if (event->error_code == BadMatch || event->error_code == BadWindow || event->error_code == BadAlloc) {
60 validOperation = false;
62 switch (event->error_code) {
64 LOG_ERROR("BadMatch.");
67 LOG_ERROR("BadWindow.");
70 LOG_ERROR("BadAlloc.");
80 static int attributes[] = {
82 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
83 GLX_RENDER_TYPE, GLX_RGBA_BIT,
89 GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
90 GLX_DOUBLEBUFFER, True,
94 class ScopedXPixmapCreationErrorHandler {
97 ScopedXPixmapCreationErrorHandler(Display* display)
100 // XSync must be called to ensure that current errors are handled by the original handler.
101 XSync(m_display, false);
102 m_previousErrorHandler = XSetErrorHandler(handleXPixmapCreationError);
105 ~ScopedXPixmapCreationErrorHandler()
107 // Restore the original handler.
108 XSetErrorHandler(m_previousErrorHandler);
111 bool isValidOperation() const
113 validOperation = true;
114 // XSync is needed to catch possible errors as they are generated asynchronously.
115 XSync(m_display, false);
116 return validOperation;
120 XErrorHandler m_previousErrorHandler;
124 // FIXME: Take X11WindowResources and GLXConfigSelector into use.
125 class OffScreenRootWindow {
127 OffScreenRootWindow()
135 Display* dpy = display();
136 m_window = XCreateSimpleWindow(dpy, XDefaultRootWindow(dpy), -1, -1, 1, 1, 0, BlackPixel(dpy, 0), WhitePixel(dpy, 0));
137 XSetWindowAttributes attributes;
138 attributes.override_redirect = true;
139 XChangeWindowAttributes(dpy, m_window, X11OverrideRedirect, &attributes);
140 // Map window to the screen
141 XMapWindow(dpy, m_window);
148 struct DisplayConnection {
151 m_display = XOpenDisplay(0);
156 XCloseDisplay(m_display);
159 Display* display() { return m_display; }
165 static Display* display()
167 // Display connection will only be broken at program shutdown.
168 static DisplayConnection displayConnection;
169 return displayConnection.display();
172 ~OffScreenRootWindow()
178 XUnmapWindow(display(), m_window);
179 XDestroyWindow(display(), m_window);
184 static bool isMesaGLX()
186 static bool isMesa = !!strstr(glXGetClientString(display(), GLX_VENDOR), "Mesa");
191 static int m_refCount;
192 static Window m_window;
195 int OffScreenRootWindow::m_refCount = 0;
196 Window OffScreenRootWindow::m_window = 0;
198 static const int glxSpec[] = {
199 // The specification is a set key value pairs stored in a simple array.
201 GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT,
202 GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
203 GLX_BIND_TO_TEXTURE_RGBA_EXT, TRUE,
207 static const int glxAttributes[] = {
208 GLX_TEXTURE_FORMAT_EXT,
209 GLX_TEXTURE_FORMAT_RGBA_EXT,
210 GLX_TEXTURE_TARGET_EXT,
215 struct GraphicsSurfacePrivate {
216 GraphicsSurfacePrivate(const PlatformGraphicsContext3D shareContext = 0)
217 : m_offScreenWindow(adoptPtr(new OffScreenRootWindow()))
223 , m_detachedContext(0)
224 , m_detachedSurface(0)
226 , m_textureIsYInverted(false)
228 , m_isReceiver(false)
230 GLXContext shareContextObject = 0;
234 QPlatformNativeInterface* nativeInterface = QGuiApplication::platformNativeInterface();
235 shareContextObject = static_cast<GLXContext>(nativeInterface->nativeResourceForContext(QByteArrayLiteral("glxcontext"), shareContext));
236 if (!shareContextObject)
240 UNUSED_PARAM(shareContext);
244 GLXFBConfig* fbConfigs = glXChooseFBConfig(display(), DefaultScreen(display()), attributes, &numReturned);
246 // Make sure that we choose a configuration that supports an alpha mask.
247 m_fbConfig = findFBConfigWithAlpha(fbConfigs, numReturned);
251 // Create a GLX context for OpenGL rendering
252 m_glContext = glXCreateNewContext(display(), m_fbConfig, GLX_RGBA_TYPE, shareContextObject, true);
255 GraphicsSurfacePrivate(uint32_t winId)
261 , m_detachedContext(0)
262 , m_detachedSurface(0)
264 , m_textureIsYInverted(false)
269 ~GraphicsSurfacePrivate()
274 uint32_t createSurface(const IntSize& size)
276 XVisualInfo* visualInfo = glXGetVisualFromFBConfig(display(), m_fbConfig);
280 Colormap cmap = XCreateColormap(display(), m_offScreenWindow->getXWindow(), visualInfo->visual, AllocNone);
282 XSetWindowAttributes a;
283 a.background_pixel = WhitePixel(display(), 0);
284 a.border_pixel = BlackPixel(display(), 0);
286 m_surface = XCreateWindow(display(), m_offScreenWindow->getXWindow(), 0, 0, size.width(), size.height(),
287 0, visualInfo->depth, InputOutput, visualInfo->visual,
288 CWBackPixel | CWBorderPixel | CWColormap, &a);
289 XSetWindowBackgroundPixmap(display(), m_surface, 0);
290 XCompositeRedirectWindow(display(), m_surface, CompositeRedirectManual);
291 m_glxSurface = glXCreateWindow(display(), m_fbConfig, m_surface, 0);
294 // Make sure the XRender Extension is available.
295 int eventBasep, errorBasep;
296 if (!XRenderQueryExtension(display(), &eventBasep, &errorBasep))
299 XMapWindow(display(), m_surface);
303 void createPixmap(uint32_t winId)
305 XWindowAttributes attr;
306 if (!XGetWindowAttributes(display(), winId, &attr))
309 // Ensure that the window is mapped.
310 if (attr.map_state == IsUnmapped || attr.map_state == IsUnviewable)
313 ScopedXPixmapCreationErrorHandler handler(display());
314 m_size = IntSize(attr.width, attr.height);
316 XRenderPictFormat* format = XRenderFindVisualFormat(display(), attr.visual);
317 m_hasAlpha = (format->type == PictTypeDirect && format->direct.alphaMask);
320 GLXFBConfig* configs = glXChooseFBConfig(display(), XDefaultScreen(display()), glxSpec, &numberOfConfigs);
322 // If origin window has alpha then find config with alpha.
323 GLXFBConfig& config = m_hasAlpha ? findFBConfigWithAlpha(configs, numberOfConfigs) : configs[0];
325 m_xPixmap = XCompositeNameWindowPixmap(display(), winId);
326 m_glxPixmap = glXCreatePixmap(display(), config, m_xPixmap, glxAttributes);
328 if (!handler.isValidOperation())
332 glXQueryDrawable(display(), m_glxPixmap, GLX_Y_INVERTED_EXT, &inverted);
333 m_textureIsYInverted = !!inverted;
339 bool textureIsYInverted()
341 return m_textureIsYInverted;
346 m_detachedContext = glXGetCurrentContext();
347 m_detachedSurface = glXGetCurrentDrawable();
348 if (m_surface && m_glContext)
349 glXMakeCurrent(display(), m_surface, m_glContext);
354 if (m_detachedContext)
355 glXMakeCurrent(display(), m_detachedSurface, m_detachedContext);
356 m_detachedContext = 0;
361 // The buffers are being switched on the writing side, the reading side just reads
362 // whatever texture the XWindow contains.
366 GLXContext glContext = glXGetCurrentContext();
368 if (m_surface && glContext) {
370 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO);
371 pGlBindFramebuffer(GL_FRAMEBUFFER, 0);
372 glXSwapBuffers(display(), m_surface);
373 pGlBindFramebuffer(GL_FRAMEBUFFER, oldFBO);
377 void copyFromTexture(uint32_t texture, const IntRect& sourceRect)
380 int x = sourceRect.x();
381 int y = sourceRect.y();
382 int width = sourceRect.width();
383 int height = sourceRect.height();
385 glPushAttrib(GL_ALL_ATTRIB_BITS);
387 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &previousFBO);
390 pGlGenFramebuffers(1, &originFBO);
391 pGlBindFramebuffer(GL_READ_FRAMEBUFFER, originFBO);
392 glBindTexture(GL_TEXTURE_2D, texture);
393 pGlFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
395 pGlBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
396 pGlBlitFramebuffer(x, y, width, height, x, y, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
398 pGlFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
399 glBindTexture(GL_TEXTURE_2D, 0);
400 pGlBindFramebuffer(GL_FRAMEBUFFER, previousFBO);
401 pGlDeleteFramebuffers(1, &originFBO);
409 Display* display() const { return OffScreenRootWindow::display(); }
411 GLXPixmap glxPixmap() const
413 if (!m_glxPixmap && m_surface)
414 const_cast<GraphicsSurfacePrivate*>(this)->createPixmap(m_surface);
420 if (m_size.isEmpty()) {
421 XWindowAttributes attr;
422 if (XGetWindowAttributes(display(), m_surface, &attr))
423 const_cast<GraphicsSurfacePrivate*>(this)->m_size = IntSize(attr.width, attr.height);
428 bool isReceiver() const { return m_isReceiver; }
430 GLXFBConfig& findFBConfigWithAlpha(GLXFBConfig* fbConfigs, int numberOfConfigs)
432 for (int i = 0; i < numberOfConfigs; ++i) {
433 XVisualInfo* visualInfo = glXGetVisualFromFBConfig(display(), fbConfigs[i]);
437 XRenderPictFormat* format = XRenderFindVisualFormat(display(), visualInfo->visual);
440 if (format && format->direct.alphaMask > 0)
444 // Return 1st config as a fallback with no alpha support.
451 glXDestroyPixmap(display(), m_glxPixmap);
456 XFreePixmap(display(), m_xPixmap);
460 // Client doesn't own the window. Delete surface only on writing side.
461 if (!m_isReceiver && m_surface) {
462 XDestroyWindow(display(), m_surface);
467 glXDestroyContext(display(), m_glContext);
472 OwnPtr<OffScreenRootWindow> m_offScreenWindow;
475 GLXPixmap m_glxPixmap;
478 GLXContext m_glContext;
479 GLXContext m_detachedContext;
480 GLXDrawable m_detachedSurface;
481 GLXFBConfig m_fbConfig;
482 bool m_textureIsYInverted;
487 static bool resolveGLMethods()
489 static bool resolved = false;
492 pGlXBindTexImageEXT = reinterpret_cast<PFNGLXBINDTEXIMAGEEXTPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glXBindTexImageEXT")));
493 pGlXReleaseTexImageEXT = reinterpret_cast<PFNGLXRELEASETEXIMAGEEXTPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glXReleaseTexImageEXT")));
494 pGlBindFramebuffer = reinterpret_cast<PFNGLBINDFRAMEBUFFERPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glBindFramebuffer")));
495 pGlBlitFramebuffer = reinterpret_cast<PFNGLBLITFRAMEBUFFERPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glBlitFramebuffer")));
497 pGlGenFramebuffers = reinterpret_cast<PFNGLGENFRAMEBUFFERSPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glGenFramebuffers")));
498 pGlDeleteFramebuffers = reinterpret_cast<PFNGLDELETEFRAMEBUFFERSPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glDeleteFramebuffers")));
499 pGlFramebufferTexture2D = reinterpret_cast<PFNGLFRAMEBUFFERTEXTURE2DPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glFramebufferTexture2D")));
500 resolved = pGlBlitFramebuffer && pGlBindFramebuffer && pGlXBindTexImageEXT && pGlXReleaseTexImageEXT;
505 GraphicsSurfaceToken GraphicsSurface::platformExport()
507 return GraphicsSurfaceToken(m_platformSurface);
510 uint32_t GraphicsSurface::platformGetTextureID()
513 GLXPixmap pixmap = m_private->glxPixmap();
517 glGenTextures(1, &m_texture);
518 glBindTexture(GL_TEXTURE_2D, m_texture);
519 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
520 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
521 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
522 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
523 pGlXBindTexImageEXT(m_private->display(), pixmap, GLX_FRONT_EXT, 0);
529 void GraphicsSurface::platformCopyToGLTexture(uint32_t /*target*/, uint32_t /*id*/, const IntRect& /*targetRect*/, const IntPoint& /*offset*/)
531 // This is not supported by GLX/Xcomposite.
534 void GraphicsSurface::platformCopyFromTexture(uint32_t texture, const IntRect& sourceRect)
536 m_private->copyFromTexture(texture, sourceRect);
539 void GraphicsSurface::platformPaintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& transform, float opacity, BitmapTexture* mask)
541 IntSize size = m_private->size();
544 uint32_t texture = platformGetTextureID();
548 TextureMapperGL::Flags flags = m_private->textureIsYInverted() ? TextureMapperGL::ShouldFlipTexture : 0;
549 flags |= TextureMapperGL::ShouldBlend;
551 FloatRect rectOnContents(FloatPoint::zero(), size);
552 TransformationMatrix adjustedTransform = transform;
553 adjustedTransform.multiply(TransformationMatrix::rectToRect(rectOnContents, targetRect));
554 static_cast<TextureMapperGL*>(textureMapper)->drawTexture(texture, flags, size, rectOnContents, adjustedTransform, opacity, mask);
557 uint32_t GraphicsSurface::platformFrontBuffer() const
562 uint32_t GraphicsSurface::platformSwapBuffers()
564 if (m_private->isReceiver()) {
565 if (OffScreenRootWindow::isMesaGLX() && platformGetTextureID()) {
566 glBindTexture(GL_TEXTURE_2D, platformGetTextureID());
567 // Mesa doesn't re-bind texture to the front buffer on glXSwapBufer
568 // Manually release previous lock and rebind texture to surface to get frame update.
569 pGlXReleaseTexImageEXT(m_private->display(), m_private->glxPixmap(), GLX_FRONT_EXT);
570 pGlXBindTexImageEXT(m_private->display(), m_private->glxPixmap(), GLX_FRONT_EXT, 0);
575 m_private->swapBuffers();
579 IntSize GraphicsSurface::platformSize() const
581 return m_private->size();
584 PassRefPtr<GraphicsSurface> GraphicsSurface::platformCreate(const IntSize& size, Flags flags, const PlatformGraphicsContext3D shareContext)
586 // X11 does not support CopyToTexture, so we do not create a GraphicsSurface if this is requested.
587 // GraphicsSurfaceGLX uses an XWindow as native surface. This one always has a front and a back buffer.
588 // Therefore single buffered GraphicsSurfaces are not supported.
589 if (flags & SupportsCopyToTexture || flags & SupportsSingleBuffered)
590 return PassRefPtr<GraphicsSurface>();
592 RefPtr<GraphicsSurface> surface = adoptRef(new GraphicsSurface(size, flags));
594 surface->m_private = new GraphicsSurfacePrivate(shareContext);
595 if (!resolveGLMethods())
596 return PassRefPtr<GraphicsSurface>();
598 surface->m_platformSurface = surface->m_private->createSurface(size);
603 PassRefPtr<GraphicsSurface> GraphicsSurface::platformImport(const IntSize& size, Flags flags, const GraphicsSurfaceToken& token)
605 // X11 does not support CopyToTexture, so we do not create a GraphicsSurface if this is requested.
606 // GraphicsSurfaceGLX uses an XWindow as native surface. This one always has a front and a back buffer.
607 // Therefore single buffered GraphicsSurfaces are not supported.
608 if (flags & SupportsCopyToTexture || flags & SupportsSingleBuffered)
609 return PassRefPtr<GraphicsSurface>();
611 RefPtr<GraphicsSurface> surface = adoptRef(new GraphicsSurface(size, flags));
612 surface->m_platformSurface = token.frontBufferHandle;
614 surface->m_private = new GraphicsSurfacePrivate(surface->m_platformSurface);
615 if (!resolveGLMethods())
616 return PassRefPtr<GraphicsSurface>();
621 char* GraphicsSurface::platformLock(const IntRect&, int* /*outputStride*/, LockOptions)
623 // GraphicsSurface is currently only being used for WebGL, which does not require this locking mechanism.
627 void GraphicsSurface::platformUnlock()
629 // GraphicsSurface is currently only being used for WebGL, which does not require this locking mechanism.
632 void GraphicsSurface::platformDestroy()
635 pGlXReleaseTexImageEXT(m_private->display(), m_private->glxPixmap(), GLX_FRONT_EXT);
636 glDeleteTextures(1, &m_texture);
644 PassOwnPtr<GraphicsContext> GraphicsSurface::platformBeginPaint(const IntSize&, char*, int)
650 PassRefPtr<Image> GraphicsSurface::createReadOnlyImage(const IntRect&)