331 lines
9.6 KiB
C++
331 lines
9.6 KiB
C++
/********************************************************************
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
Copyright (C) 2010 Martin Gräßlin <kde@martin-graesslin.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*********************************************************************/
|
|
|
|
// This file is included in scene_opengl.cpp
|
|
//#include "scene_opengl.h"
|
|
#include <QX11Info>
|
|
|
|
EGLDisplay dpy;
|
|
EGLConfig config;
|
|
EGLSurface surface;
|
|
EGLContext ctx;
|
|
|
|
SceneOpenGL::SceneOpenGL( Workspace* ws )
|
|
: Scene( ws )
|
|
, init_ok( false )
|
|
, selfCheckDone( true )
|
|
, m_sceneShader( NULL )
|
|
, m_genericSceneShader( NULL )
|
|
, m_colorShader( NULL )
|
|
{
|
|
if( !initRenderingContext() )
|
|
return;
|
|
|
|
initGL();
|
|
debug = qstrcmp( qgetenv( "KWIN_GL_DEBUG" ), "1" ) == 0;
|
|
if (!setupSceneShaders()) {
|
|
kError( 1212 ) << "Shaders not valid, ES compositing not possible";
|
|
return;
|
|
}
|
|
|
|
|
|
if( checkGLError( "Init" ))
|
|
{
|
|
kError( 1212 ) << "OpenGL compositing setup failed";
|
|
return; // error
|
|
}
|
|
init_ok = true;
|
|
}
|
|
|
|
SceneOpenGL::~SceneOpenGL()
|
|
{
|
|
foreach( Window* w, windows )
|
|
delete w;
|
|
// do cleanup after initBuffer()
|
|
eglMakeCurrent( dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
|
|
eglDestroyContext( dpy, ctx );
|
|
eglDestroySurface( dpy, surface );
|
|
eglTerminate( dpy );
|
|
eglReleaseThread();
|
|
delete m_sceneShader;
|
|
SceneOpenGL::EffectFrame::cleanup();
|
|
checkGLError( "Cleanup" );
|
|
}
|
|
|
|
bool SceneOpenGL::initTfp()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool SceneOpenGL::initRenderingContext()
|
|
{
|
|
dpy = eglGetDisplay( display() );
|
|
if( dpy == EGL_NO_DISPLAY )
|
|
return false;
|
|
EGLint major, minor;
|
|
if( eglInitialize( dpy, &major, &minor ) == EGL_FALSE )
|
|
return false;
|
|
eglBindAPI( EGL_OPENGL_ES_API );
|
|
initBufferConfigs();
|
|
if( !wspace->createOverlay() )
|
|
{
|
|
kError( 1212 ) << "Could not get overlay window";
|
|
return false;
|
|
}
|
|
surface = eglCreateWindowSurface( dpy, config, wspace->overlayWindow(), 0 );
|
|
|
|
const EGLint context_attribs[] = {
|
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
|
EGL_NONE
|
|
};
|
|
|
|
ctx = eglCreateContext( dpy, config, EGL_NO_CONTEXT, context_attribs );
|
|
if( ctx == EGL_NO_CONTEXT )
|
|
return false;
|
|
if( eglMakeCurrent( dpy, surface, surface, ctx ) == EGL_FALSE )
|
|
return false;
|
|
kDebug( 1212 ) << "EGL version: " << major << "." << minor;
|
|
EGLint error = eglGetError();
|
|
if( error != EGL_SUCCESS )
|
|
{
|
|
kWarning( 1212 ) << "Error occurred while creating context " << error;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SceneOpenGL::initBuffer()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool SceneOpenGL::initBufferConfigs()
|
|
{
|
|
const EGLint config_attribs[] = {
|
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
|
EGL_RED_SIZE, 1,
|
|
EGL_GREEN_SIZE, 1,
|
|
EGL_BLUE_SIZE, 1,
|
|
EGL_ALPHA_SIZE, 0,
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
|
EGL_CONFIG_CAVEAT, EGL_NONE,
|
|
EGL_NONE,
|
|
};
|
|
|
|
EGLint count;
|
|
EGLConfig configs[1024];
|
|
eglChooseConfig(dpy, config_attribs, configs, 1024, &count);
|
|
|
|
EGLint visualId = XVisualIDFromVisual((Visual*)QX11Info::appVisual());
|
|
|
|
config = configs[0];
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
EGLint val;
|
|
eglGetConfigAttrib(dpy, configs[i], EGL_NATIVE_VISUAL_ID, &val);
|
|
if (visualId == val)
|
|
{
|
|
config = configs[i];
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SceneOpenGL::initDrawableConfigs()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void SceneOpenGL::selfCheckSetup()
|
|
{
|
|
// not used in EGL
|
|
}
|
|
|
|
bool SceneOpenGL::selfCheckFinish()
|
|
{
|
|
// not used in EGL
|
|
return true;
|
|
}
|
|
|
|
// the entry function for painting
|
|
void SceneOpenGL::paint( QRegion damage, ToplevelList toplevels )
|
|
{
|
|
QTime t = QTime::currentTime();
|
|
foreach( Toplevel* c, toplevels )
|
|
{
|
|
assert( windows.contains( c ));
|
|
stacking_order.append( windows[ c ] );
|
|
}
|
|
grabXServer();
|
|
int mask = 0;
|
|
paintScreen( &mask, &damage ); // call generic implementation
|
|
ungrabXServer(); // ungrab before flushBuffer(), it may wait for vsync
|
|
if( wspace->overlayWindow()) // show the window only after the first pass, since
|
|
wspace->showOverlay(); // that pass may take long
|
|
lastRenderTime = t.elapsed();
|
|
flushBuffer( mask, damage );
|
|
// do cleanup
|
|
stacking_order.clear();
|
|
checkGLError( "PostPaint" );
|
|
}
|
|
|
|
void SceneOpenGL::waitSync()
|
|
{
|
|
// not used in EGL
|
|
}
|
|
|
|
void SceneOpenGL::flushBuffer( int mask, QRegion damage )
|
|
{
|
|
glFlush();
|
|
if( mask & PAINT_SCREEN_REGION )
|
|
{
|
|
// TODO: implement me properly
|
|
eglSwapBuffers( dpy, surface );
|
|
}
|
|
else
|
|
{
|
|
eglSwapBuffers( dpy, surface );
|
|
}
|
|
eglWaitGL();
|
|
// TODO: remove for wayland
|
|
XFlush( display());
|
|
}
|
|
|
|
void SceneOpenGL::paintGenericScreen(int mask, ScreenPaintData data)
|
|
{
|
|
if (mask & PAINT_SCREEN_TRANSFORMED) {
|
|
// apply screen transformations
|
|
QMatrix4x4 screenTransformation;
|
|
screenTransformation.translate(data.xTranslate, data.yTranslate, data.zTranslate);
|
|
if (data.rotation) {
|
|
screenTransformation.translate(data.rotation->xRotationPoint, data.rotation->yRotationPoint, data.rotation->zRotationPoint);
|
|
// translate to rotation point, rotate, translate back
|
|
qreal xAxis = 0.0;
|
|
qreal yAxis = 0.0;
|
|
qreal zAxis = 0.0;
|
|
switch (data.rotation->axis) {
|
|
case RotationData::XAxis:
|
|
xAxis = 1.0;
|
|
break;
|
|
case RotationData::YAxis:
|
|
yAxis = 1.0;
|
|
break;
|
|
case RotationData::ZAxis:
|
|
zAxis = 1.0;
|
|
break;
|
|
}
|
|
screenTransformation.rotate(data.rotation->angle, xAxis, yAxis, zAxis);
|
|
screenTransformation.translate(-data.rotation->xRotationPoint, -data.rotation->yRotationPoint, -data.rotation->zRotationPoint);
|
|
}
|
|
screenTransformation.scale(data.xScale, data.yScale, data.zScale);
|
|
m_genericSceneShader->bind();
|
|
m_genericSceneShader->setUniform("screenTransformation", screenTransformation);
|
|
} else if ((mask & PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS) || (mask & PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_WITHOUT_FULL_REPAINTS)) {
|
|
m_genericSceneShader->bind();
|
|
m_genericSceneShader->setUniform("screenTransformation", QMatrix4x4());
|
|
}
|
|
Scene::paintGenericScreen(mask, data);
|
|
}
|
|
|
|
void SceneOpenGL::paintBackground(QRegion region)
|
|
{
|
|
PaintClipper pc(region);
|
|
if (!PaintClipper::clip()) {
|
|
glClearColor(0, 0, 0, 1);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
return;
|
|
}
|
|
if (pc.clip() && pc.paintArea().isEmpty())
|
|
return; // no background to paint
|
|
QVector<float> verts;
|
|
for (PaintClipper::Iterator iterator; !iterator.isDone(); iterator.next()) {
|
|
QRect r = iterator.boundingRect();
|
|
verts << r.x() + r.width() << r.y();
|
|
verts << r.x() << r.y();
|
|
verts << r.x() << r.y() + r.height();
|
|
verts << r.x() << r.y() + r.height();
|
|
verts << r.x() + r.width() << r.y() + r.height();
|
|
verts << r.x() + r.width() << r.y();
|
|
}
|
|
GLVertexBuffer vbo(GLVertexBuffer::Static);
|
|
vbo.setUseColor(true);
|
|
vbo.setUseShader(true);
|
|
vbo.setData(verts.count() / 2, 2, verts.data(), NULL);
|
|
m_colorShader->bind();
|
|
vbo.render(GL_TRIANGLES);
|
|
}
|
|
|
|
//****************************************
|
|
// SceneOpenGL::Texture
|
|
//****************************************
|
|
|
|
void SceneOpenGL::Texture::init()
|
|
{
|
|
findTarget();
|
|
}
|
|
|
|
void SceneOpenGL::Texture::release()
|
|
{
|
|
mTexture = None;
|
|
}
|
|
|
|
void SceneOpenGL::Texture::findTarget()
|
|
{
|
|
mTarget = GL_TEXTURE_2D;
|
|
}
|
|
|
|
bool SceneOpenGL::Texture::load( const Pixmap& pix, const QSize& size,
|
|
int depth, QRegion region )
|
|
{
|
|
Q_UNUSED(size)
|
|
Q_UNUSED(depth)
|
|
Q_UNUSED(region)
|
|
if( mTexture == None )
|
|
{
|
|
createTexture();
|
|
bind();
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
const EGLint attribs[] = {
|
|
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
|
|
EGL_NONE
|
|
};
|
|
EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
|
|
(EGLClientBuffer)pix, attribs);
|
|
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
|
|
eglDestroyImageKHR( dpy, image );
|
|
unbind();
|
|
checkGLError("load texture");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void SceneOpenGL::Texture::bind()
|
|
{
|
|
GLTexture::bind();
|
|
}
|
|
|
|
void SceneOpenGL::Texture::unbind()
|
|
{
|
|
GLTexture::unbind();
|
|
}
|