2 Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
3 Copyright (C) 2013 Intel Corporation.
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.
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.
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.
22 #include "GraphicsSurface.h"
24 #if USE(GRAPHICS_SURFACE)
26 #include "NotImplemented.h"
27 #include "TextureMapperGL.h"
30 // Qt headers must be included before glx headers.
31 #include <QGuiApplication>
32 #include <QOpenGLContext>
33 #include <qpa/qplatformnativeinterface.h>
37 #include <opengl/GLDefs.h>
40 #include "GLXConfigSelector.h"
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;
52 static int glxAttributes[] = {
53 GLX_TEXTURE_FORMAT_EXT,
54 GLX_TEXTURE_FORMAT_RGBA_EXT,
55 GLX_TEXTURE_TARGET_EXT,
60 static bool isMesaGLX()
62 static bool isMesa = !!strstr(glXGetClientString(X11Helper::nativeDisplay(), GLX_VENDOR), "Mesa");
66 struct GraphicsSurfacePrivate {
67 GraphicsSurfacePrivate(const PlatformGraphicsContext3D shareContext = 0)
73 , m_detachedContext(0)
74 , m_detachedSurface(0)
77 GLXContext shareContextObject = 0;
81 QPlatformNativeInterface* nativeInterface = QGuiApplication::platformNativeInterface();
82 shareContextObject = static_cast<GLXContext>(nativeInterface->nativeResourceForContext(QByteArrayLiteral("glxcontext"), shareContext));
83 if (!shareContextObject)
87 UNUSED_PARAM(shareContext);
90 m_configSelector = adoptPtr(new GLXConfigSelector());
92 if (!m_configSelector->surfaceContextConfig()) {
97 // Create a GLX context for OpenGL rendering
98 m_glContext = glXCreateNewContext(display(), m_configSelector->surfaceContextConfig(), GLX_RGBA_TYPE, shareContextObject, true);
101 GraphicsSurfacePrivate(uint32_t winId)
107 , m_detachedContext(0)
108 , m_detachedSurface(0)
111 m_configSelector = adoptPtr(new GLXConfigSelector());
114 ~GraphicsSurfacePrivate()
119 uint32_t createSurface(const IntSize& size)
121 if (!display() || !m_configSelector)
124 OwnPtrX11<XVisualInfo> visInfo(m_configSelector->visualInfo());
126 if (!visInfo.get()) {
131 X11Helper::createOffScreenWindow(&m_surface, *visInfo.get(), size);
138 m_glxSurface = glXCreateWindow(display(), m_configSelector->surfaceContextConfig(), m_surface, 0);
142 void createPixmap(uint32_t winId)
144 if (!m_configSelector)
147 XWindowAttributes attr;
148 if (!XGetWindowAttributes(display(), winId, &attr))
151 // Ensure that the window is mapped.
152 if (attr.map_state == IsUnmapped || attr.map_state == IsUnviewable)
155 ScopedXPixmapCreationErrorHandler handler;
156 m_size = IntSize(attr.width, attr.height);
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);
164 if (!handler.isValidOperation())
168 glXQueryDrawable(display(), m_glxPixmap, GLX_Y_INVERTED_EXT, &inverted);
169 m_flags = !!inverted ? TextureMapperGL::ShouldFlipTexture : 0;
172 m_flags |= TextureMapperGL::ShouldBlend;
178 m_detachedContext = glXGetCurrentContext();
179 m_detachedSurface = glXGetCurrentDrawable();
180 if (m_surface && m_glContext)
181 glXMakeCurrent(display(), m_surface, m_glContext);
186 if (m_detachedContext)
187 glXMakeCurrent(display(), m_detachedSurface, m_detachedContext);
188 m_detachedContext = 0;
193 // The buffers are being switched on the writing side, the reading side just reads
194 // whatever texture the XWindow contains.
198 GLXContext glContext = glXGetCurrentContext();
200 if (m_surface && glContext) {
202 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO);
203 pGlBindFramebuffer(GL_FRAMEBUFFER, 0);
204 glXSwapBuffers(display(), m_surface);
205 pGlBindFramebuffer(GL_FRAMEBUFFER, oldFBO);
209 void copyFromTexture(uint32_t texture, const IntRect& sourceRect)
212 int x = sourceRect.x();
213 int y = sourceRect.y();
214 int width = sourceRect.width();
215 int height = sourceRect.height();
217 glPushAttrib(GL_ALL_ATTRIB_BITS);
219 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &previousFBO);
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);
227 pGlBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
228 pGlBlitFramebuffer(x, y, width, height, x, y, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
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);
241 Display* display() const { return X11Helper::nativeDisplay(); }
243 GLXPixmap glxPixmap() const
245 if (!m_glxPixmap && m_surface)
246 const_cast<GraphicsSurfacePrivate*>(this)->createPixmap(m_surface);
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);
260 bool isReceiver() const { return m_isReceiver; }
262 TextureMapperGL::Flags flags() const { return m_flags; }
268 glXDestroyPixmap(display(), m_glxPixmap);
273 XFreePixmap(display(), m_xPixmap);
277 // Client doesn't own the window. Delete surface only on writing side.
278 if (!m_isReceiver && m_surface) {
279 XDestroyWindow(display(), m_surface);
284 glXDestroyContext(display(), m_glContext);
288 if (m_configSelector)
289 m_configSelector = nullptr;
294 GLXPixmap m_glxPixmap;
297 GLXContext m_glContext;
298 GLXContext m_detachedContext;
299 GLXDrawable m_detachedSurface;
300 OwnPtr<GLXConfigSelector> m_configSelector;
302 TextureMapperGL::Flags m_flags;
305 static bool resolveGLMethods()
307 static bool resolved = false;
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")));
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;
323 GraphicsSurfaceToken GraphicsSurface::platformExport()
325 return GraphicsSurfaceToken(m_platformSurface);
328 uint32_t GraphicsSurface::platformGetTextureID()
331 GLXPixmap pixmap = m_private->glxPixmap();
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);
347 void GraphicsSurface::platformCopyToGLTexture(uint32_t /*target*/, uint32_t /*id*/, const IntRect& /*targetRect*/, const IntPoint& /*offset*/)
349 // This is not supported by GLX/Xcomposite.
352 void GraphicsSurface::platformCopyFromTexture(uint32_t texture, const IntRect& sourceRect)
354 m_private->copyFromTexture(texture, sourceRect);
357 void GraphicsSurface::platformPaintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& transform, float opacity, BitmapTexture* mask)
359 IntSize size = m_private->size();
362 uint32_t texture = platformGetTextureID();
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);
372 uint32_t GraphicsSurface::platformFrontBuffer() const
377 uint32_t GraphicsSurface::platformSwapBuffers()
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);
390 m_private->swapBuffers();
394 IntSize GraphicsSurface::platformSize() const
396 return m_private->size();
399 PassRefPtr<GraphicsSurface> GraphicsSurface::platformCreate(const IntSize& size, Flags flags, const PlatformGraphicsContext3D shareContext)
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>();
407 RefPtr<GraphicsSurface> surface = adoptRef(new GraphicsSurface(size, flags));
409 surface->m_private = new GraphicsSurfacePrivate(shareContext);
410 if (!resolveGLMethods())
411 return PassRefPtr<GraphicsSurface>();
413 surface->m_platformSurface = surface->m_private->createSurface(size);
418 PassRefPtr<GraphicsSurface> GraphicsSurface::platformImport(const IntSize& size, Flags flags, const GraphicsSurfaceToken& token)
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>();
426 RefPtr<GraphicsSurface> surface = adoptRef(new GraphicsSurface(size, flags));
427 surface->m_platformSurface = token.frontBufferHandle;
429 surface->m_private = new GraphicsSurfacePrivate(surface->m_platformSurface);
430 if (!resolveGLMethods())
431 return PassRefPtr<GraphicsSurface>();
436 char* GraphicsSurface::platformLock(const IntRect&, int* /*outputStride*/, LockOptions)
438 // GraphicsSurface is currently only being used for WebGL, which does not require this locking mechanism.
442 void GraphicsSurface::platformUnlock()
444 // GraphicsSurface is currently only being used for WebGL, which does not require this locking mechanism.
447 void GraphicsSurface::platformDestroy()
450 pGlXReleaseTexImageEXT(m_private->display(), m_private->glxPixmap(), GLX_FRONT_EXT);
451 glDeleteTextures(1, &m_texture);
459 PassOwnPtr<GraphicsContext> GraphicsSurface::platformBeginPaint(const IntSize&, char*, int)
465 PassRefPtr<Image> GraphicsSurface::createReadOnlyImage(const IntRect&)