| /* |
| Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Library General Public |
| License as published by the Free Software Foundation; either |
| version 2 of the License, or (at your option) any later version. |
| |
| This library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Library General Public License for more details. |
| |
| You should have received a copy of the GNU Library General Public License |
| along with this library; see the file COPYING.LIB. If not, write to |
| the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "config.h" |
| #include "GraphicsSurface.h" |
| |
| #if USE(GRAPHICS_SURFACE) |
| |
| #include "NotImplemented.h" |
| #include "TextureMapperGL.h" |
| |
| #if PLATFORM(QT) |
| // Qt headers must be included before glx headers. |
| #include <QGuiApplication> |
| #include <QOpenGLContext> |
| #include <qpa/qplatformnativeinterface.h> |
| #elif PLATFORM(EFL) |
| #include <GL/gl.h> |
| #endif |
| |
| #include <GL/glext.h> |
| #include <GL/glx.h> |
| #include <X11/extensions/Xcomposite.h> |
| #include <X11/extensions/Xrender.h> |
| |
| namespace WebCore { |
| |
| static long X11OverrideRedirect = 1L << 9; |
| |
| static PFNGLXBINDTEXIMAGEEXTPROC pGlXBindTexImageEXT = 0; |
| static PFNGLXRELEASETEXIMAGEEXTPROC pGlXReleaseTexImageEXT = 0; |
| static PFNGLBINDFRAMEBUFFERPROC pGlBindFramebuffer = 0; |
| static PFNGLBLITFRAMEBUFFERPROC pGlBlitFramebuffer = 0; |
| static PFNGLGENFRAMEBUFFERSPROC pGlGenFramebuffers = 0; |
| static PFNGLDELETEFRAMEBUFFERSPROC pGlDeleteFramebuffers = 0; |
| static PFNGLFRAMEBUFFERTEXTURE2DPROC pGlFramebufferTexture2D = 0; |
| |
| class OffScreenRootWindow { |
| public: |
| OffScreenRootWindow() |
| { |
| ++m_refCount; |
| } |
| |
| Window getXWindow() |
| { |
| if (!m_window) { |
| Display* dpy = display(); |
| m_window = XCreateSimpleWindow(dpy, XDefaultRootWindow(dpy), -1, -1, 1, 1, 0, BlackPixel(dpy, 0), WhitePixel(dpy, 0)); |
| XSetWindowAttributes attributes; |
| attributes.override_redirect = true; |
| XChangeWindowAttributes(dpy, m_window, X11OverrideRedirect, &attributes); |
| // Map window to the screen |
| XMapWindow(dpy, m_window); |
| } |
| |
| return m_window; |
| } |
| |
| Display* display() |
| { |
| if (!m_display) |
| m_display = XOpenDisplay(0); |
| return m_display; |
| } |
| |
| ~OffScreenRootWindow() |
| { |
| if (--m_refCount || !m_display) |
| return; |
| |
| if (m_window) { |
| XUnmapWindow(m_display, m_window); |
| XDestroyWindow(m_display, m_window); |
| m_window = 0; |
| } |
| |
| XCloseDisplay(m_display); |
| m_display = 0; |
| } |
| |
| private: |
| static int m_refCount; |
| static Window m_window; |
| static Display* m_display; |
| }; |
| |
| int OffScreenRootWindow::m_refCount = 0; |
| Window OffScreenRootWindow::m_window = 0; |
| Display* OffScreenRootWindow::m_display = 0; |
| |
| static const int glxSpec[] = { |
| // The specification is a set key value pairs stored in a simple array. |
| GLX_LEVEL, 0, |
| GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT, |
| GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, |
| GLX_BIND_TO_TEXTURE_RGB_EXT, TRUE, |
| 0 |
| }; |
| |
| static const int glxAttributes[] = { |
| GLX_TEXTURE_FORMAT_EXT, |
| GLX_TEXTURE_FORMAT_RGB_EXT, |
| GLX_TEXTURE_TARGET_EXT, |
| GLX_TEXTURE_2D_EXT, |
| 0 |
| }; |
| |
| struct GraphicsSurfacePrivate { |
| GraphicsSurfacePrivate(const PlatformGraphicsContext3D shareContext = 0) |
| : m_display(0) |
| , m_xPixmap(0) |
| , m_glxPixmap(0) |
| , m_glContext(0) |
| , m_detachedContext(0) |
| , m_detachedSurface(0) |
| , m_textureIsYInverted(false) |
| , m_hasAlpha(false) |
| { |
| GLXContext shareContextObject = 0; |
| m_display = m_offScreenWindow.display(); |
| |
| #if PLATFORM(QT) |
| if (shareContext) { |
| #if 0 |
| // This code path requires QXcbNativeInterface::nativeResourceForContext() which is not availble in Qt5 on the build bots yet. |
| QPlatformNativeInterface* nativeInterface = QGuiApplication::platformNativeInterface(); |
| shareContextObject = static_cast<GLXContext>(nativeInterface->nativeResourceForContext(QByteArrayLiteral("glxcontext"), shareContext)); |
| if (!shareContextObject) |
| return; |
| #else |
| // This code path should be removed as soon as QXcbNativeInterface::nativeResourceForContext() can provide the GLXContext. |
| QOpenGLContext* previousContext = QOpenGLContext::currentContext(); |
| QSurface* previousSurface = previousContext->surface(); |
| QSurface* currentSurface = shareContext->surface(); |
| shareContext->makeCurrent(currentSurface); |
| |
| shareContextObject = glXGetCurrentContext(); |
| |
| previousContext->makeCurrent(previousSurface); |
| #endif |
| } |
| #else |
| UNUSED_PARAM(shareContext); |
| #endif |
| |
| int attributes[] = { |
| GLX_LEVEL, 0, |
| GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, |
| GLX_RENDER_TYPE, GLX_RGBA_BIT, |
| GLX_RED_SIZE, 1, |
| GLX_GREEN_SIZE, 1, |
| GLX_BLUE_SIZE, 1, |
| GLX_DOUBLEBUFFER, True, |
| None |
| }; |
| |
| int numReturned; |
| m_fbConfigs = glXChooseFBConfig(m_display, DefaultScreen(m_display), attributes, &numReturned); |
| // Create a GLX context for OpenGL rendering |
| m_glContext = glXCreateNewContext(m_display, m_fbConfigs[0], GLX_RGBA_TYPE, shareContextObject, true); |
| } |
| |
| ~GraphicsSurfacePrivate() |
| { |
| if (m_glxPixmap) |
| glXDestroyPixmap(m_display, m_glxPixmap); |
| m_glxPixmap = 0; |
| |
| if (m_xPixmap) |
| XFreePixmap(m_display, m_xPixmap); |
| m_xPixmap = 0; |
| |
| if (m_fbConfigs) |
| XFree(m_fbConfigs); |
| |
| if (m_glContext) |
| glXDestroyContext(m_display, m_glContext); |
| } |
| |
| uint32_t createSurface(const IntSize& size) |
| { |
| XVisualInfo* visualInfo = glXGetVisualFromFBConfig(m_display, m_fbConfigs[0]); |
| if (!visualInfo) |
| return 0; |
| |
| Colormap cmap = XCreateColormap(m_display, m_offScreenWindow.getXWindow(), visualInfo->visual, AllocNone); |
| XSetWindowAttributes a; |
| a.background_pixel = WhitePixel(m_display, 0); |
| a.border_pixel = BlackPixel(m_display, 0); |
| a.colormap = cmap; |
| m_surface = XCreateWindow(m_display, m_offScreenWindow.getXWindow(), 0, 0, size.width(), size.height(), |
| 0, visualInfo->depth, InputOutput, visualInfo->visual, |
| CWBackPixel | CWBorderPixel | CWColormap, &a); |
| XSetWindowBackgroundPixmap(m_display, m_surface, 0); |
| XCompositeRedirectWindow(m_display, m_surface, CompositeRedirectManual); |
| XFree(visualInfo); |
| |
| // Make sure the XRender Extension is available. |
| int eventBasep, errorBasep; |
| if (!XRenderQueryExtension(m_display, &eventBasep, &errorBasep)) |
| return 0; |
| |
| XMapWindow(m_display, m_surface); |
| return m_surface; |
| } |
| |
| void createPixmap(uint32_t winId) |
| { |
| XWindowAttributes attr; |
| XGetWindowAttributes(m_display, winId, &attr); |
| |
| XRenderPictFormat* format = XRenderFindVisualFormat(m_display, attr.visual); |
| m_hasAlpha = (format->type == PictTypeDirect && format->direct.alphaMask); |
| m_size = IntSize(attr.width, attr.height); |
| |
| int numberOfConfigs; |
| GLXFBConfig* configs = glXChooseFBConfig(m_display, XDefaultScreen(m_display), glxSpec, &numberOfConfigs); |
| |
| m_xPixmap = XCompositeNameWindowPixmap(m_display, winId); |
| m_glxPixmap = glXCreatePixmap(m_display, *configs, m_xPixmap, glxAttributes); |
| |
| uint inverted = 0; |
| glXQueryDrawable(m_display, m_glxPixmap, GLX_Y_INVERTED_EXT, &inverted); |
| m_textureIsYInverted = !!inverted; |
| |
| XFree(configs); |
| } |
| |
| bool textureIsYInverted() |
| { |
| return m_textureIsYInverted; |
| } |
| |
| void makeCurrent() |
| { |
| m_detachedContext = glXGetCurrentContext(); |
| m_detachedSurface = glXGetCurrentDrawable(); |
| if (m_surface && m_glContext) |
| glXMakeCurrent(m_display, m_surface, m_glContext); |
| } |
| |
| void doneCurrent() |
| { |
| if (m_detachedContext) |
| glXMakeCurrent(m_display, m_detachedSurface, m_detachedContext); |
| m_detachedContext = 0; |
| } |
| |
| void swapBuffers() |
| { |
| // If there is a xpixmap, we are on the reading side and do not want to swap any buffers. |
| // The buffers are being switched on the writing side, the reading side just reads |
| // whatever texture the XWindow contains. |
| if (m_xPixmap) |
| return; |
| |
| GLXContext glContext = glXGetCurrentContext(); |
| |
| if (m_surface && glContext) { |
| GLint oldFBO; |
| glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO); |
| pGlBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glXSwapBuffers(m_display, m_surface); |
| pGlBindFramebuffer(GL_FRAMEBUFFER, oldFBO); |
| } |
| } |
| |
| void copyFromTexture(uint32_t texture, const IntRect& sourceRect) |
| { |
| makeCurrent(); |
| int x = sourceRect.x(); |
| int y = sourceRect.y(); |
| int width = sourceRect.width(); |
| int height = sourceRect.height(); |
| |
| glPushAttrib(GL_ALL_ATTRIB_BITS); |
| GLint previousFBO; |
| glGetIntegerv(GL_FRAMEBUFFER_BINDING, &previousFBO); |
| |
| GLuint originFBO; |
| pGlGenFramebuffers(1, &originFBO); |
| pGlBindFramebuffer(GL_READ_FRAMEBUFFER, originFBO); |
| glBindTexture(GL_TEXTURE_2D, texture); |
| pGlFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| |
| pGlBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); |
| pGlBlitFramebuffer(x, y, width, height, x, y, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR); |
| |
| pGlFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); |
| glBindTexture(GL_TEXTURE_2D, 0); |
| pGlBindFramebuffer(GL_FRAMEBUFFER, previousFBO); |
| pGlDeleteFramebuffers(1, &originFBO); |
| |
| glPopAttrib(); |
| doneCurrent(); |
| } |
| |
| Display* display() const { return m_display; } |
| |
| GLXPixmap glxPixmap() const { return m_glxPixmap; } |
| |
| IntSize size() const { return m_size; } |
| |
| private: |
| OffScreenRootWindow m_offScreenWindow; |
| IntSize m_size; |
| Display* m_display; |
| Pixmap m_xPixmap; |
| GLXPixmap m_glxPixmap; |
| Window m_surface; |
| GLXContext m_glContext; |
| GLXContext m_detachedContext; |
| GLXDrawable m_detachedSurface; |
| GLXFBConfig* m_fbConfigs; |
| bool m_textureIsYInverted; |
| bool m_hasAlpha; |
| }; |
| |
| static bool resolveGLMethods(GraphicsSurfacePrivate*) |
| { |
| static bool resolved = false; |
| if (resolved) |
| return true; |
| pGlXBindTexImageEXT = reinterpret_cast<PFNGLXBINDTEXIMAGEEXTPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glXBindTexImageEXT"))); |
| pGlXReleaseTexImageEXT = reinterpret_cast<PFNGLXRELEASETEXIMAGEEXTPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glXReleaseTexImageEXT"))); |
| pGlBindFramebuffer = reinterpret_cast<PFNGLBINDFRAMEBUFFERPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glBindFramebuffer"))); |
| pGlBlitFramebuffer = reinterpret_cast<PFNGLBLITFRAMEBUFFERPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glBlitFramebuffer"))); |
| |
| pGlGenFramebuffers = reinterpret_cast<PFNGLGENFRAMEBUFFERSPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glGenFramebuffers"))); |
| pGlDeleteFramebuffers = reinterpret_cast<PFNGLDELETEFRAMEBUFFERSPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glDeleteFramebuffers"))); |
| pGlFramebufferTexture2D = reinterpret_cast<PFNGLFRAMEBUFFERTEXTURE2DPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("glFramebufferTexture2D"))); |
| resolved = pGlBlitFramebuffer && pGlBindFramebuffer && pGlXBindTexImageEXT && pGlXReleaseTexImageEXT; |
| |
| return resolved; |
| } |
| |
| GraphicsSurfaceToken GraphicsSurface::platformExport() |
| { |
| return GraphicsSurfaceToken(m_platformSurface); |
| } |
| |
| uint32_t GraphicsSurface::platformGetTextureID() |
| { |
| if (!m_texture) { |
| glGenTextures(1, &m_texture); |
| glBindTexture(GL_TEXTURE_2D, m_texture); |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| pGlXBindTexImageEXT(m_private->display(), m_private->glxPixmap(), GLX_FRONT_EXT, 0); |
| } |
| |
| return m_texture; |
| } |
| |
| void GraphicsSurface::platformCopyToGLTexture(uint32_t /*target*/, uint32_t /*id*/, const IntRect& /*targetRect*/, const IntPoint& /*offset*/) |
| { |
| // This is not supported by GLX/Xcomposite. |
| } |
| |
| void GraphicsSurface::platformCopyFromTexture(uint32_t texture, const IntRect& sourceRect) |
| { |
| m_private->copyFromTexture(texture, sourceRect); |
| } |
| |
| |
| void GraphicsSurface::platformPaintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& transform, float opacity, BitmapTexture* mask) |
| { |
| TextureMapperGL* texMapGL = static_cast<TextureMapperGL*>(textureMapper); |
| TransformationMatrix adjustedTransform = transform; |
| adjustedTransform.multiply(TransformationMatrix::rectToRect(FloatRect(FloatPoint::zero(), m_size), targetRect)); |
| TextureMapperGL::Flags flags = m_private->textureIsYInverted() ? TextureMapperGL::ShouldFlipTexture : 0; |
| texMapGL->drawTexture(platformGetTextureID(), flags, m_size, targetRect, adjustedTransform, opacity, mask); |
| } |
| |
| uint32_t GraphicsSurface::platformFrontBuffer() const |
| { |
| return 0; |
| } |
| |
| uint32_t GraphicsSurface::platformSwapBuffers() |
| { |
| m_private->swapBuffers(); |
| return 0; |
| } |
| |
| PassRefPtr<GraphicsSurface> GraphicsSurface::platformCreate(const IntSize& size, Flags flags, const PlatformGraphicsContext3D shareContext) |
| { |
| // X11 does not support CopyToTexture, so we do not create a GraphicsSurface if this is requested. |
| // GraphicsSurfaceGLX uses an XWindow as native surface. This one always has a front and a back buffer. |
| // Therefore single buffered GraphicsSurfaces are not supported. |
| if (flags & SupportsCopyToTexture || flags & SupportsSingleBuffered) |
| return PassRefPtr<GraphicsSurface>(); |
| |
| RefPtr<GraphicsSurface> surface = adoptRef(new GraphicsSurface(size, flags)); |
| |
| surface->m_private = new GraphicsSurfacePrivate(shareContext); |
| if (!resolveGLMethods(surface->m_private)) |
| return PassRefPtr<GraphicsSurface>(); |
| |
| surface->m_platformSurface = surface->m_private->createSurface(size); |
| |
| return surface; |
| } |
| |
| PassRefPtr<GraphicsSurface> GraphicsSurface::platformImport(const IntSize& size, Flags flags, const GraphicsSurfaceToken& token) |
| { |
| // X11 does not support CopyToTexture, so we do not create a GraphicsSurface if this is requested. |
| // GraphicsSurfaceGLX uses an XWindow as native surface. This one always has a front and a back buffer. |
| // Therefore single buffered GraphicsSurfaces are not supported. |
| if (flags & SupportsCopyToTexture || flags & SupportsSingleBuffered) |
| return PassRefPtr<GraphicsSurface>(); |
| |
| RefPtr<GraphicsSurface> surface = adoptRef(new GraphicsSurface(size, flags)); |
| |
| surface->m_private = new GraphicsSurfacePrivate(); |
| if (!resolveGLMethods(surface->m_private)) |
| return PassRefPtr<GraphicsSurface>(); |
| |
| surface->m_platformSurface = token.frontBufferHandle; |
| |
| surface->m_private->createPixmap(surface->m_platformSurface); |
| surface->m_size = surface->m_private->size(); |
| |
| return surface; |
| } |
| |
| char* GraphicsSurface::platformLock(const IntRect&, int* /*outputStride*/, LockOptions) |
| { |
| // GraphicsSurface is currently only being used for WebGL, which does not require this locking mechanism. |
| return 0; |
| } |
| |
| void GraphicsSurface::platformUnlock() |
| { |
| // GraphicsSurface is currently only being used for WebGL, which does not require this locking mechanism. |
| } |
| |
| void GraphicsSurface::platformDestroy() |
| { |
| if (m_texture) { |
| pGlXReleaseTexImageEXT(m_private->display(), m_private->glxPixmap(), GLX_FRONT_EXT); |
| glDeleteTextures(1, &m_texture); |
| } |
| |
| delete m_private; |
| m_private = 0; |
| } |
| |
| #if !PLATFORM(QT) |
| PassOwnPtr<GraphicsContext> GraphicsSurface::platformBeginPaint(const IntSize&, char*, int) |
| { |
| notImplemented(); |
| return nullptr; |
| } |
| |
| PassRefPtr<Image> GraphicsSurface::createReadOnlyImage(const IntRect&) |
| { |
| notImplemented(); |
| return 0; |
| } |
| #endif |
| } |
| #endif |