SHM mode, using MIT-SHM extension for Pixmap->image data conversions
and glTexImage texture creation. Stolen from Beryl (stolen from Looking Glass). Not faster than TFP but faster then the original fallback glCopyTexImage mode from glcompmgr. svn path=/branches/work/kwin_composite/; revision=605283
This commit is contained in:
parent
f33683df3f
commit
9d25cf5166
2 changed files with 117 additions and 11 deletions
122
scene_opengl.cpp
122
scene_opengl.cpp
|
@ -55,11 +55,16 @@ Sources and other compositing managers:
|
|||
|
||||
#include "scene_opengl.h"
|
||||
|
||||
#include <kxerrorhandler.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "client.h"
|
||||
#include "effects.h"
|
||||
#include "glutils.h"
|
||||
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
|
||||
namespace KWinInternal
|
||||
{
|
||||
|
||||
|
@ -80,7 +85,8 @@ bool SceneOpenGL::strict_binding; // intended for AIGLX
|
|||
bool SceneOpenGL::db; // destination drawable is double-buffered
|
||||
bool SceneOpenGL::copy_buffer_hack; // workaround for nvidia < 1.0-9xxx drivers
|
||||
bool SceneOpenGL::supports_saturation;
|
||||
|
||||
bool SceneOpenGL::shm_mode;
|
||||
XShmSegmentInfo SceneOpenGL::shm;
|
||||
|
||||
|
||||
// detect OpenGL error (add to various places in code to pinpoint the place)
|
||||
|
@ -155,12 +161,10 @@ SceneOpenGL::SceneOpenGL( Workspace* ws )
|
|||
// check for FBConfig support
|
||||
if( !hasGLXVersion( 1, 3 ) && !hasGLExtension( "GLX_SGIX_fbconfig" ))
|
||||
return;
|
||||
tfp_mode = ( glXBindTexImageEXT != NULL && glXReleaseTexImageEXT != NULL );
|
||||
strict_binding = false; // not needed now
|
||||
// use copy buffer hack from glcompmgr (called COPY_BUFFER there) - nvidia drivers older than
|
||||
// 1.0-9xxx don't update pixmaps properly, so do a copy first
|
||||
copy_buffer_hack = !tfp_mode; // TODO detect that it's nvidia < 1.0-9xxx driver
|
||||
initBuffer(); // create destination buffer
|
||||
// select mode - try TFP first, then SHM, otherwise fallback mode
|
||||
shm_mode = false;
|
||||
tfp_mode = ( glXBindTexImageEXT != NULL && glXReleaseTexImageEXT != NULL );
|
||||
if( tfp_mode )
|
||||
{
|
||||
if( !findConfig( drawable_tfp_attrs, &fbcdrawable ))
|
||||
|
@ -173,13 +177,19 @@ SceneOpenGL::SceneOpenGL( Workspace* ws )
|
|||
else
|
||||
if( !findConfig( drawable_attrs, &fbcdrawable ))
|
||||
assert( false );
|
||||
if( !tfp_mode && initShm())
|
||||
shm_mode = true;
|
||||
// use copy buffer hack from glcompmgr (called COPY_BUFFER there) - nvidia drivers older than
|
||||
// 1.0-9xxx don't update pixmaps properly, so do a copy first
|
||||
copy_buffer_hack = !tfp_mode && !shm_mode; // TODO detect that it's nvidia < 1.0-9xxx driver
|
||||
initBuffer(); // create destination buffer
|
||||
int vis_buffer, vis_drawable;
|
||||
glXGetFBConfigAttrib( display(), fbcbuffer, GLX_VISUAL_ID, &vis_buffer );
|
||||
glXGetFBConfigAttrib( display(), fbcdrawable, GLX_VISUAL_ID, &vis_drawable );
|
||||
kDebug() << "Buffer visual: 0x" << QString::number( vis_buffer, 16 ) << ", drawable visual: 0x"
|
||||
<< QString::number( vis_drawable, 16 ) << endl;
|
||||
ctxbuffer = glXCreateNewContext( display(), fbcbuffer, GLX_RGBA_TYPE, NULL, GL_FALSE );
|
||||
if( !tfp_mode )
|
||||
if( !tfp_mode && !shm_mode )
|
||||
ctxdrawable = glXCreateNewContext( display(), fbcdrawable, GLX_RGBA_TYPE, ctxbuffer, GL_FALSE );
|
||||
if( !glXMakeContextCurrent( display(), glxbuffer, glxbuffer, ctxbuffer ) )
|
||||
assert( false );
|
||||
|
@ -202,7 +212,7 @@ SceneOpenGL::SceneOpenGL( Workspace* ws )
|
|||
glMatrixMode( GL_MODELVIEW );
|
||||
glLoadIdentity();
|
||||
checkGLError( "Init" );
|
||||
kDebug() << "DB:" << db << ", TFP:" << tfp_mode << endl;
|
||||
kDebug() << "DB:" << db << ", TFP:" << tfp_mode << ", SHM:" << shm_mode << endl;
|
||||
}
|
||||
|
||||
SceneOpenGL::~SceneOpenGL()
|
||||
|
@ -225,7 +235,9 @@ SceneOpenGL::~SceneOpenGL()
|
|||
XFreeGC( display(), gcroot );
|
||||
XFreePixmap( display(), buffer );
|
||||
}
|
||||
if( !tfp_mode )
|
||||
if( shm_mode )
|
||||
cleanupShm();
|
||||
if( !tfp_mode && !shm_mode )
|
||||
{
|
||||
if( last_pixmap != None )
|
||||
glXDestroyPixmap( display(), last_pixmap );
|
||||
|
@ -235,6 +247,52 @@ SceneOpenGL::~SceneOpenGL()
|
|||
checkGLError( "Cleanup" );
|
||||
}
|
||||
|
||||
bool SceneOpenGL::initShm()
|
||||
{
|
||||
int major, minor;
|
||||
Bool pixmaps;
|
||||
if( !XShmQueryVersion( display(), &major, &minor, &pixmaps ) || !pixmaps )
|
||||
return false;
|
||||
if( XShmPixmapFormat( display()) != ZPixmap )
|
||||
return false;
|
||||
const int MAXSIZE = 4096 * 2048 * 4; // TODO check there are not larger windows
|
||||
// TODO check that bytes_per_line doesn't involve padding?
|
||||
shm.readOnly = False;
|
||||
shm.shmid = shmget( IPC_PRIVATE, MAXSIZE, IPC_CREAT | 0600 );
|
||||
if( shm.shmid < 0 )
|
||||
return false;
|
||||
shm.shmaddr = ( char* ) shmat( shm.shmid, NULL, 0 );
|
||||
if( shm.shmaddr == ( void * ) -1 )
|
||||
{
|
||||
shmctl( shm.shmid, IPC_RMID, 0 );
|
||||
return false;
|
||||
}
|
||||
#ifdef __linux__
|
||||
// mark as deleted to automatically free the memory in case
|
||||
// of a crash (but this doesn't work e.g. on Solaris ... oh well)
|
||||
shmctl( shm.shmid, IPC_RMID, 0 );
|
||||
#endif
|
||||
KXErrorHandler errs;
|
||||
XShmAttach( display(), &shm );
|
||||
if( errs.error( true ))
|
||||
{
|
||||
#ifndef __linux__
|
||||
shmctl( shm.shmid, IPC_RMID, 0 );
|
||||
#endif
|
||||
shmdt( shm.shmaddr );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SceneOpenGL::cleanupShm()
|
||||
{
|
||||
shmdt( shm.shmaddr );
|
||||
#ifndef __linux__
|
||||
shmctl( shm.shmid, IPC_RMID, 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
// create destination buffer
|
||||
void SceneOpenGL::initBuffer()
|
||||
{
|
||||
|
@ -504,7 +562,49 @@ void SceneOpenGL::Window::bindTexture()
|
|||
}
|
||||
if( copy_buffer || alpha_clear )
|
||||
glXWaitX();
|
||||
if( tfp_mode )
|
||||
if( shm_mode )
|
||||
{ // non-tfp case, copy pixmap contents to a texture
|
||||
if( texture == None )
|
||||
{
|
||||
glGenTextures( 1, &texture );
|
||||
glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texture );
|
||||
texture_y_inverted = false;
|
||||
glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, toplevel->width(), toplevel->height(),
|
||||
0, GL_BGRA, GL_UNSIGNED_BYTE, NULL );
|
||||
// TODO hasAlpha() ?
|
||||
// glCopyTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0,
|
||||
// toplevel->hasAlpha() ? GL_RGBA : GL_RGB,
|
||||
// 0, 0, toplevel->width(), toplevel->height(), 0 );
|
||||
}
|
||||
else
|
||||
glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texture );
|
||||
if( !toplevel->damage().isEmpty())
|
||||
{
|
||||
XGCValues xgcv;
|
||||
xgcv.graphics_exposures = False;
|
||||
xgcv.subwindow_mode = IncludeInferiors;
|
||||
GC gc = XCreateGC( display(), pix, GCGraphicsExposures | GCSubwindowMode, &xgcv );
|
||||
foreach( QRect r, toplevel->damage().rects())
|
||||
{ // TODO for small areas it might be faster to not use SHM to avoid the XSync()
|
||||
Pixmap p = XShmCreatePixmap( display(), rootWindow(), shm.shmaddr, &shm,
|
||||
r.width(), r.height(), toplevel->depth());
|
||||
XCopyArea( display(), pix, p, gc, r.x(), r.y(), r.width(), r.height(), 0, 0 );
|
||||
XSync( display(), False );
|
||||
glXWaitX();
|
||||
glTexSubImage2D( GL_TEXTURE_RECTANGLE_ARB, 0,
|
||||
r.x(), r.y(), r.width(), r.height(), GL_BGRA, GL_UNSIGNED_BYTE, shm.shmaddr );
|
||||
glXWaitGL();
|
||||
XFreePixmap( display(), p );
|
||||
}
|
||||
XFreeGC( display(), gc );
|
||||
}
|
||||
// the pixmap is no longer needed, the texture will be updated
|
||||
// only when the window changes anyway, so no need to cache
|
||||
// the pixmap
|
||||
XFreePixmap( display(), pix );
|
||||
texture_y_inverted = true;
|
||||
}
|
||||
else if( tfp_mode )
|
||||
{ // tfp mode, simply bind the pixmap to texture
|
||||
if( texture == None )
|
||||
glGenTextures( 1, &texture );
|
||||
|
@ -559,7 +659,6 @@ void SceneOpenGL::Window::bindTexture()
|
|||
// the pixmap to a texture, this is not affected
|
||||
// by using glOrtho() for the OpenGL scene)
|
||||
int gly = toplevel->height() - r.y() - r.height();
|
||||
texture_y_inverted = false;
|
||||
glCopyTexSubImage2D( GL_TEXTURE_RECTANGLE_ARB, 0,
|
||||
r.x(), gly, r.x(), gly, r.width(), r.height());
|
||||
}
|
||||
|
@ -574,6 +673,7 @@ void SceneOpenGL::Window::bindTexture()
|
|||
glDrawBuffer( GL_BACK );
|
||||
glXMakeContextCurrent( display(), glxbuffer, glxbuffer, ctxbuffer );
|
||||
glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texture );
|
||||
texture_y_inverted = false;
|
||||
}
|
||||
if( copy_buffer )
|
||||
XFreePixmap( display(), window_pix );
|
||||
|
|
|
@ -16,6 +16,8 @@ License. See the file "COPYING" for the exact licensing terms.
|
|||
#include <GL/gl.h>
|
||||
#include <GL/glx.h>
|
||||
|
||||
#include <X11/extensions/XShm.h>
|
||||
|
||||
namespace KWinInternal
|
||||
{
|
||||
|
||||
|
@ -36,6 +38,8 @@ class SceneOpenGL
|
|||
virtual void paintSimpleScreen( int mask, QRegion region );
|
||||
virtual void paintBackground( QRegion region );
|
||||
private:
|
||||
bool initShm();
|
||||
void cleanupShm();
|
||||
void initBuffer();
|
||||
bool findConfig( const int* attrs, GLXFBConfig* config, VisualID visual = None );
|
||||
typedef GLuint Texture;
|
||||
|
@ -49,11 +53,13 @@ class SceneOpenGL
|
|||
static GLXContext ctxdrawable;
|
||||
static GLXDrawable last_pixmap; // for a workaround in bindTexture()
|
||||
static bool tfp_mode;
|
||||
static bool shm_mode;
|
||||
static bool strict_binding;
|
||||
static bool copy_buffer_hack;
|
||||
static bool supports_saturation;
|
||||
class Window;
|
||||
QMap< Toplevel*, Window > windows;
|
||||
static XShmSegmentInfo shm;
|
||||
};
|
||||
|
||||
class SceneOpenGL::Window
|
||||
|
|
Loading…
Reference in a new issue