GraphicsSurfaceGLX.cpp [plain text]
#include "config.h"
#include "GraphicsSurface.h"
#if USE(GRAPHICS_SURFACE)
#include "NotImplemented.h"
#include "TextureMapperGL.h"
#include <opengl/GLDefs.h>
#include "GLXConfigSelector.h"
namespace WebCore {
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;
static int glxAttributes[] = {
GLX_TEXTURE_FORMAT_EXT,
GLX_TEXTURE_FORMAT_RGBA_EXT,
GLX_TEXTURE_TARGET_EXT,
GLX_TEXTURE_2D_EXT,
0
};
static bool isMesaGLX()
{
static bool isMesa = !!strstr(glXGetClientString(X11Helper::nativeDisplay(), GLX_VENDOR), "Mesa");
return isMesa;
}
struct GraphicsSurfacePrivate {
GraphicsSurfacePrivate(const PlatformGraphicsContext3D shareContext = 0)
: m_xPixmap(0)
, m_glxPixmap(0)
, m_surface(0)
, m_glxSurface(0)
, m_glContext(0)
, m_detachedContext(0)
, m_detachedSurface(0)
, m_isReceiver(false)
, m_texture(0)
{
GLXContext shareContextObject = 0;
UNUSED_PARAM(shareContext);
GLPlatformSurface::SurfaceAttributes sharedSurfaceAttributes = GLPlatformSurface::DoubleBuffered |
GLPlatformSurface::SupportAlpha;
m_configSelector = adoptPtr(new GLXConfigSelector(sharedSurfaceAttributes));
if (!m_configSelector->surfaceContextConfig()) {
clear();
return;
}
m_glContext = glXCreateNewContext(display(), m_configSelector->surfaceContextConfig(), GLX_RGBA_TYPE, shareContextObject, true);
}
GraphicsSurfacePrivate(uint32_t winId)
: m_xPixmap(0)
, m_glxPixmap(0)
, m_surface(winId)
, m_glxSurface(0)
, m_glContext(0)
, m_detachedContext(0)
, m_detachedSurface(0)
, m_isReceiver(true)
, m_texture(0)
{
}
~GraphicsSurfacePrivate()
{
clear();
}
uint32_t createSurface(const IntSize& size)
{
if (!display() || !m_configSelector)
return 0;
GLXFBConfig config = m_configSelector->surfaceContextConfig();
OwnPtrX11<XVisualInfo> visInfo(m_configSelector->visualInfo(config));
if (!visInfo.get()) {
clear();
return 0;
}
X11Helper::createOffScreenWindow(&m_surface, *visInfo.get(), size);
if (!m_surface) {
clear();
return 0;
}
m_glxSurface = glXCreateWindow(display(), config, m_surface, 0);
return m_surface;
}
void createPixmap(uint32_t winId)
{
XWindowAttributes attr;
if (!XGetWindowAttributes(display(), winId, &attr))
return;
if (attr.map_state == IsUnmapped || attr.map_state == IsUnviewable)
return;
ScopedXPixmapCreationErrorHandler handler;
m_size = IntSize(attr.width, attr.height);
XRenderPictFormat* format = XRenderFindVisualFormat(display(), attr.visual);
bool hasAlpha = (format->type == PictTypeDirect && format->direct.alphaMask);
m_xPixmap = XCompositeNameWindowPixmap(display(), winId);
glxAttributes[1] = (format->depth == 32 && hasAlpha) ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT;
GLPlatformSurface::SurfaceAttributes sharedSurfaceAttributes = GLPlatformSurface::Default;
if (hasAlpha)
sharedSurfaceAttributes = GLPlatformSurface::SupportAlpha;
if (!m_configSelector)
m_configSelector = adoptPtr(new GLXConfigSelector(sharedSurfaceAttributes));
m_glxPixmap = glXCreatePixmap(display(), m_configSelector->surfaceClientConfig(format->depth, XVisualIDFromVisual(attr.visual)), m_xPixmap, glxAttributes);
if (!handler.isValidOperation())
clear();
else {
uint inverted = 0;
glXQueryDrawable(display(), m_glxPixmap, GLX_Y_INVERTED_EXT, &inverted);
m_flags = !!inverted ? TextureMapperGL::ShouldFlipTexture : 0;
if (hasAlpha)
m_flags |= TextureMapperGL::ShouldBlend;
}
}
void makeCurrent()
{
m_detachedContext = glXGetCurrentContext();
m_detachedSurface = glXGetCurrentDrawable();
if (m_surface && m_glContext)
glXMakeCurrent(display(), m_surface, m_glContext);
}
void doneCurrent()
{
if (m_detachedContext)
glXMakeCurrent(display(), m_detachedSurface, m_detachedContext);
m_detachedContext = 0;
}
void swapBuffers()
{
if (isReceiver()) {
if (isMesaGLX() && textureID()) {
glBindTexture(GL_TEXTURE_2D, textureID());
pGlXReleaseTexImageEXT(display(), glxPixmap(), GLX_FRONT_EXT);
pGlXBindTexImageEXT(display(), glxPixmap(), GLX_FRONT_EXT, 0);
}
return;
}
GLXContext glContext = glXGetCurrentContext();
if (m_surface && glContext) {
GLint oldFBO;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO);
pGlBindFramebuffer(GL_FRAMEBUFFER, 0);
glXSwapBuffers(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();
swapBuffers();
doneCurrent();
}
Display* display() const { return X11Helper::nativeDisplay(); }
GLXPixmap glxPixmap() const
{
if (!m_glxPixmap && m_surface)
const_cast<GraphicsSurfacePrivate*>(this)->createPixmap(m_surface);
return m_glxPixmap;
}
IntSize size() const
{
if (m_size.isEmpty()) {
XWindowAttributes attr;
if (XGetWindowAttributes(display(), m_surface, &attr))
const_cast<GraphicsSurfacePrivate*>(this)->m_size = IntSize(attr.width, attr.height);
}
return m_size;
}
bool isReceiver() const { return m_isReceiver; }
TextureMapperGL::Flags flags() const { return m_flags; }
Window surface() const { return m_surface; }
GLuint textureID() const
{
if (m_texture)
return m_texture;
GLXPixmap pixmap = glxPixmap();
if (!pixmap)
return 0;
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, 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(display(), pixmap, GLX_FRONT_EXT, 0);
const_cast<GraphicsSurfacePrivate*>(this)->m_texture = texture;
return texture;
}
private:
void clear()
{
if (m_texture) {
pGlXReleaseTexImageEXT(display(), glxPixmap(), GLX_FRONT_EXT);
glDeleteTextures(1, &m_texture);
}
if (m_glxPixmap) {
glXDestroyPixmap(display(), m_glxPixmap);
m_glxPixmap = 0;
}
if (m_xPixmap) {
XFreePixmap(display(), m_xPixmap);
m_xPixmap = 0;
}
if (!m_isReceiver && m_surface) {
XDestroyWindow(display(), m_surface);
m_surface = 0;
}
if (m_glContext) {
glXDestroyContext(display(), m_glContext);
m_glContext = 0;
}
if (m_configSelector)
m_configSelector = nullptr;
}
IntSize m_size;
Pixmap m_xPixmap;
GLXPixmap m_glxPixmap;
uint32_t m_surface;
Window m_glxSurface;
GLXContext m_glContext;
GLXContext m_detachedContext;
GLXDrawable m_detachedSurface;
OwnPtr<GLXConfigSelector> m_configSelector;
bool m_isReceiver;
TextureMapperGL::Flags m_flags;
GLuint m_texture;
};
static bool resolveGLMethods()
{
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_private->surface());
}
uint32_t GraphicsSurface::platformGetTextureID()
{
return m_private->textureID();
}
void GraphicsSurface::platformCopyToGLTexture(uint32_t , uint32_t , const IntRect& , const IntPoint& )
{
}
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)
{
IntSize size = m_private->size();
if (size.isEmpty())
return;
uint32_t texture = platformGetTextureID();
if (!texture)
return;
FloatRect rectOnContents(FloatPoint::zero(), size);
TransformationMatrix adjustedTransform = transform;
adjustedTransform.multiply(TransformationMatrix::rectToRect(rectOnContents, targetRect));
static_cast<TextureMapperGL*>(textureMapper)->drawTexture(texture, m_private->flags(), size, rectOnContents, adjustedTransform, opacity);
}
uint32_t GraphicsSurface::platformFrontBuffer() const
{
return 0;
}
uint32_t GraphicsSurface::platformSwapBuffers()
{
m_private->swapBuffers();
return 0;
}
IntSize GraphicsSurface::platformSize() const
{
return m_private->size();
}
PassRefPtr<GraphicsSurface> GraphicsSurface::platformCreate(const IntSize& size, Flags flags, const PlatformGraphicsContext3D shareContext)
{
if (flags & SupportsCopyToTexture || flags & SupportsSingleBuffered)
return PassRefPtr<GraphicsSurface>();
RefPtr<GraphicsSurface> surface = adoptRef(new GraphicsSurface(size, flags));
surface->m_private = new GraphicsSurfacePrivate(shareContext);
if (!resolveGLMethods())
return PassRefPtr<GraphicsSurface>();
surface->m_private->createSurface(size);
return surface;
}
PassRefPtr<GraphicsSurface> GraphicsSurface::platformImport(const IntSize& size, Flags flags, const GraphicsSurfaceToken& token)
{
if (flags & SupportsCopyToTexture || flags & SupportsSingleBuffered)
return PassRefPtr<GraphicsSurface>();
RefPtr<GraphicsSurface> surface = adoptRef(new GraphicsSurface(size, flags));
surface->m_private = new GraphicsSurfacePrivate(token.frontBufferHandle);
if (!resolveGLMethods())
return PassRefPtr<GraphicsSurface>();
return surface;
}
char* GraphicsSurface::platformLock(const IntRect&, int* , LockOptions)
{
return 0;
}
void GraphicsSurface::platformUnlock()
{
}
void GraphicsSurface::platformDestroy()
{
delete m_private;
m_private = 0;
}
std::unique_ptr<GraphicsContext> GraphicsSurface::platformBeginPaint(const IntSize&, char*, int)
{
notImplemented();
return nullptr;
}
PassRefPtr<Image> GraphicsSurface::createReadOnlyImage(const IntRect&)
{
notImplemented();
return 0;
}
}
#endif