Split SceneOpenGL into a concrete SceneOpenGL1 and SceneOpenGL2

SceneOpenGL turns into an abstract class with two concrete subclasses:
* SceneOpenGL1
* SceneOpenGL2

It provides a factory method which first creates either the GLX or EGL
backend which is passed to a static supported() method in the concrete
sub classes. These method can test whether the backend is sufficient to
be used for the OpenGL version in question. E.g. the OpenGL 2 scene
checks whether the context is direct.

The actual rendering is moved into the subclasses with specific OpenGL 1
and OpenGL 2 code. This should make the code more readable and requires
less checks whether a Shader is bound. This is now known through the
Scene: the OpenGL1 scene will never have a shader bound, the OpenGL2 scene
will always have a shader bound.

To make this more reliable the ShaderManager is extended by a disable
method used by SceneOpenGL1 to ensure that the ShaderManager will never
be used. This also obsoletes the need to read the KWin configuration
whether legacy GL is enabled. The check is moved into the supported
method of the OpenGL2 scene.

REVIEW: 106357
icc-effect-5.14.5
Martin Gräßlin 2012-09-06 17:13:22 +02:00
parent db9368fc26
commit 5a6d9400b2
7 changed files with 304 additions and 132 deletions

View File

@ -191,13 +191,13 @@ void Compositor::slotCompositingOptionsInitialized()
}
#endif
m_scene = new SceneOpenGL(Workspace::self());
m_scene = SceneOpenGL::createScene();
// TODO: Add 30 second delay to protect against screen freezes as well
unsafeConfig.writeEntry("OpenGLIsUnsafe", false);
unsafeConfig.sync();
if (!m_scene->initFailed())
if (m_scene && !m_scene->initFailed())
break; // -->
delete m_scene;
m_scene = NULL;

View File

@ -61,7 +61,6 @@ static int eglVersion;
static QStringList glExtensions;
static QStringList glxExtensions;
static QStringList eglExtension;
static bool legacyGl;
int glTextureUnitsCount;
@ -102,11 +101,7 @@ void initGL()
QStringList glversioninfo = glversionstring.left(glversionstring.indexOf(' ')).split('.');
while (glversioninfo.count() < 3)
glversioninfo << "0";
#ifdef KWIN_HAVE_OPENGLES
legacyGl = false;
#else
KConfigGroup config(KGlobal::config(), "Compositing");
legacyGl = config.readEntry<bool>("GLLegacy", false);
#ifndef KWIN_HAVE_OPENGLES
glVersion = MAKE_GL_VERSION(glversioninfo[0].toInt(), glversioninfo[1].toInt(), glversioninfo[2].toInt());
#endif
// Get list of supported OpenGL extensions
@ -627,6 +622,16 @@ ShaderManager *ShaderManager::instance()
return s_shaderManager;
}
void ShaderManager::disable()
{
// for safety do a Cleanup first
ShaderManager::cleanup();
// create a new ShaderManager and set it to inited without calling init
// that will ensure that the ShaderManager is not valid
s_shaderManager = new ShaderManager();
s_shaderManager->m_inited = true;
}
void ShaderManager::cleanup()
{
delete s_shaderManager;
@ -797,10 +802,6 @@ GLShader *ShaderManager::loadShaderFromCode(const QByteArray &vertexSource, cons
void ShaderManager::initShaders()
{
if (legacyGl) {
kDebug(1212) << "OpenGL Shaders disabled by config option";
return;
}
m_orthoShader = new GLShader(":/resources/scene-vertex.glsl", ":/resources/scene-fragment.glsl");
if (m_orthoShader->isValid()) {
pushShader(SimpleShader, true);

View File

@ -355,6 +355,15 @@ public:
* @return a pointer to the ShaderManager instance
**/
static ShaderManager *instance();
/**
* @brief Ensures that the ShaderManager is disabled.
*
* Used only by an OpenGL 1 Scene which does not use OpenGL 2 Shaders.
*
* @internal
* @since 4.10
**/
static void disable();
/**
* @internal

View File

@ -155,6 +155,7 @@ Options::Options(QObject *parent)
, m_glDirect(Options::defaultGlDirect())
, m_glStrictBinding(Options::defaultGlStrictBinding())
, m_glStrictBindingFollowsDriver(Options::defaultGlStrictBindingFollowsDriver())
, m_glLegacy(Options::defaultGlLegacy())
, OpTitlebarDblClick(Options::defaultOperationTitlebarDblClick())
, CmdActiveTitlebar1(Options::defaultCommandActiveTitlebar1())
, CmdActiveTitlebar2(Options::defaultCommandActiveTitlebar2())
@ -761,6 +762,15 @@ void Options::setGlStrictBindingFollowsDriver(bool glStrictBindingFollowsDriver)
emit glStrictBindingFollowsDriverChanged();
}
void Options::setGlLegacy(bool glLegacy)
{
if (m_glLegacy == glLegacy) {
return;
}
m_glLegacy = glLegacy;
emit glLegacyChanged();
}
void Options::setElectricBorders(int borders)
{
if (electric_borders == borders) {
@ -985,6 +995,7 @@ void Options::reloadCompositingSettings(bool force)
if (!isGlStrictBindingFollowsDriver()) {
setGlStrictBinding(config.readEntry("GLStrictBinding", Options::defaultGlStrictBinding()));
}
setGlLegacy(config.readEntry("GLLegacy", Options::defaultGlLegacy()));
m_xrenderSmoothScale = config.readEntry("XRenderSmoothScale", false);

View File

@ -191,6 +191,10 @@ class Options : public QObject, public KDecorationOptions
* If @c false @link glStrictBinding is set from a config value and not updated during scene initialization.
**/
Q_PROPERTY(bool glStrictBindingFollowsDriver READ isGlStrictBindingFollowsDriver WRITE setGlStrictBindingFollowsDriver NOTIFY glStrictBindingFollowsDriverChanged)
/**
* Whether legacy OpenGL should be used or OpenGL (ES) 2
**/
Q_PROPERTY(bool glLegacy READ isGlLegacy WRITE setGlLegacy NOTIFY glLegacyChanged)
public:
Options(QObject *parent = NULL);
@ -562,6 +566,9 @@ public:
bool isGlStrictBindingFollowsDriver() const {
return m_glStrictBindingFollowsDriver;
}
bool isGlLegacy() const {
return m_glLegacy;
}
// setters
void setFocusPolicy(FocusPolicy focusPolicy);
@ -624,6 +631,7 @@ public:
void setGlDirect(bool glDirect);
void setGlStrictBinding(bool glStrictBinding);
void setGlStrictBindingFollowsDriver(bool glStrictBindingFollowsDriver);
void setGlLegacy(bool glLegacy);
// default values
static FocusPolicy defaultFocusPolicy() {
@ -843,6 +851,9 @@ public:
static bool defaultGlStrictBindingFollowsDriver() {
return true;
}
static bool defaultGlLegacy() {
return false;
}
static int defaultAnimationSpeed() {
return 3;
}
@ -923,6 +934,7 @@ Q_SIGNALS:
void glDirectChanged();
void glStrictBindingChanged();
void glStrictBindingFollowsDriverChanged();
void glLegacyChanged();
private:
void setElectricBorders(int borders);
@ -966,6 +978,7 @@ private:
bool m_glDirect;
bool m_glStrictBinding;
bool m_glStrictBindingFollowsDriver;
bool m_glLegacy;
WindowOperation OpTitlebarDblClick;

View File

@ -137,15 +137,10 @@ void OpenGLBackend::idle()
* SceneOpenGL
***********************************************/
SceneOpenGL::SceneOpenGL(Workspace* ws)
SceneOpenGL::SceneOpenGL(Workspace* ws, OpenGLBackend *backend)
: Scene(ws)
, m_resetModelViewProjectionMatrix(true)
, init_ok(false)
#ifdef KWIN_HAVE_OPENGLES
, m_backend(new EglOnXBackend())
#else
, m_backend(new GlxBackend())
#endif
, m_backend(backend)
{
if (m_backend->isFailed()) {
return;
@ -158,11 +153,13 @@ SceneOpenGL::SceneOpenGL(Workspace* ws)
QTimer::singleShot(0, Workspace::self(), SLOT(fallbackToXRenderCompositing()));
return;
}
#ifndef KWIN_HAVE_OPENGLES
if (!hasGLExtension("GL_ARB_texture_non_power_of_two")
&& !hasGLExtension("GL_ARB_texture_rectangle")) {
kError(1212) << "GL_ARB_texture_non_power_of_two and GL_ARB_texture_rectangle missing";
return; // error
}
#endif
if (glPlatform->isMesaDriver() && glPlatform->mesaVersion() < kVersionNumber(7, 10)) {
kError(1212) << "KWin requires at least Mesa 7.10 for OpenGL compositing.";
return;
@ -174,37 +171,18 @@ SceneOpenGL::SceneOpenGL(Workspace* ws)
debug = qstrcmp(qgetenv("KWIN_GL_DEBUG"), "1") == 0;
// scene shader setup
if (GLPlatform::instance()->supports(GLSL)) {
if (!ShaderManager::instance()->isValid()) {
kDebug(1212) << "No Scene Shaders available";
#ifdef KWIN_HAVE_OPENGLES
// with OpenGL ES we need shaders, so let's break here
return;
#endif
} else {
// push one shader on the stack so that one is always bound
ShaderManager::instance()->pushShader(ShaderManager::SimpleShader);
}
}
// OpenGL scene setup
setupModelViewProjectionMatrix();
if (checkGLError("Init")) {
kError(1212) << "OpenGL compositing setup failed";
return; // error
}
// set strict binding
if (options->isGlStrictBindingFollowsDriver()) {
options->setGlStrictBinding(!glPlatform->supports(LooseBinding));
}
init_ok = true;
}
SceneOpenGL::~SceneOpenGL()
{
delete m_backend;
if (init_ok) {
// backend might be still needed for a different scene
delete m_backend;
}
foreach (Window * w, windows) {
delete w;
}
@ -213,6 +191,45 @@ SceneOpenGL::~SceneOpenGL()
checkGLError("Cleanup");
}
SceneOpenGL *SceneOpenGL::createScene()
{
OpenGLBackend *backend = NULL;
#ifdef KWIN_HAVE_OPENGLES
backend = new EglOnXBackend();
#else
backend = new GlxBackend();
#endif
if (!backend || backend->isFailed()) {
delete backend;
return NULL;
}
SceneOpenGL *scene = NULL;
// first let's try an OpenGL 2 scene
if (SceneOpenGL2::supported(backend)) {
scene = new SceneOpenGL2(backend);
if (scene->initFailed()) {
delete scene;
scene = NULL;
} else {
return scene;
}
}
#ifndef KWIN_HAVE_OPENGLES
if (SceneOpenGL1::supported(backend)) {
scene = new SceneOpenGL1(backend);
if (scene->initFailed()) {
delete scene;
scene = NULL;
}
}
#endif
if (!scene) {
delete backend;
}
return scene;
}
OverlayWindow *SceneOpenGL::overlayWindow()
{
return m_backend->overlayWindow();
@ -244,10 +261,6 @@ int SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
}
m_backend->prepareRenderingFrame();
if (m_resetModelViewProjectionMatrix) {
// reset model view projection matrix if required
setupModelViewProjectionMatrix();
}
int mask = 0;
#ifdef CHECK_GL_ERROR
checkGLError("Paint1");
@ -265,30 +278,6 @@ int SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
return m_backend->renderTime();
}
void SceneOpenGL::setupModelViewProjectionMatrix()
{
#ifndef KWIN_HAVE_OPENGLES
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float fovy = 60.0f;
float aspect = 1.0f;
float zNear = 0.1f;
float zFar = 100.0f;
float ymax = zNear * tan(fovy * M_PI / 360.0f);
float ymin = -ymax;
float xmin = ymin * aspect;
float xmax = ymax * aspect;
// swap top and bottom to have OpenGL coordinate system match X system
glFrustum(xmin, xmax, ymin, ymax, zNear, zFar);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
float scaleFactor = 1.1 * tan(fovy * M_PI / 360.0f) / ymax;
glTranslatef(xmin * scaleFactor, ymax * scaleFactor, -1.1);
glScalef((xmax - xmin)*scaleFactor / displayWidth(), -(ymax - ymin)*scaleFactor / displayHeight(), 0.001);
#endif
m_resetModelViewProjectionMatrix = false;
}
QMatrix4x4 SceneOpenGL::transformation(int mask, const ScreenPaintData &data) const
{
QMatrix4x4 matrix;
@ -312,27 +301,6 @@ QMatrix4x4 SceneOpenGL::transformation(int mask, const ScreenPaintData &data) co
return matrix;
}
void SceneOpenGL::paintGenericScreen(int mask, ScreenPaintData data)
{
ShaderManager *shaderManager = ShaderManager::instance();
const bool useShader = shaderManager->isValid();
const QMatrix4x4 matrix = transformation(mask, data);
if (useShader) {
GLShader *shader = shaderManager->pushShader(ShaderManager::GenericShader);
shader->setUniform(GLShader::ScreenTransformation, matrix);
} else {
pushMatrix(matrix);
}
Scene::paintGenericScreen(mask, data);
if (useShader)
shaderManager->popShader();
else
popMatrix();
}
void SceneOpenGL::paintBackground(QRegion region)
{
PaintClipper pc(region);
@ -353,25 +321,13 @@ void SceneOpenGL::paintBackground(QRegion region)
verts << r.x() + r.width() << r.y() + r.height();
verts << r.x() + r.width() << r.y();
}
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
vbo->reset();
vbo->setUseColor(true);
vbo->setData(verts.count() / 2, 2, verts.data(), NULL);
const bool useShader = ShaderManager::instance()->isValid();
if (useShader) {
GLShader *shader = ShaderManager::instance()->pushShader(ShaderManager::ColorShader);
shader->setUniform(GLShader::Offset, QVector2D(0, 0));
}
vbo->render(GL_TRIANGLES);
if (useShader) {
ShaderManager::instance()->popShader();
}
doPaintBackground(verts);
}
void SceneOpenGL::windowAdded(Toplevel* c)
{
assert(!windows.contains(c));
Window *w = Window::createWindow(c);
Window *w = createWindow(c);
windows[ c ] = w;
w->setScene(this);
connect(c, SIGNAL(opacityChanged(KWin::Toplevel*,qreal)), SLOT(windowOpacityChanged(KWin::Toplevel*)));
@ -444,11 +400,172 @@ void SceneOpenGL::screenGeometryChanged(const QSize &size)
glViewport(0,0, size.width(), size.height());
m_backend->screenGeometryChanged(size);
ShaderManager::instance()->resetAllShaders();
#ifndef KWIN_HAVE_OPENGLES
m_resetModelViewProjectionMatrix = true;
#endif
}
//****************************************
// SceneOpenGL2
//****************************************
bool SceneOpenGL2::supported(OpenGLBackend *backend)
{
if (!backend->isDirectRendering()) {
return false;
}
#ifndef KWIN_HAVE_OPENGLES
if (!GLPlatform::instance()->supports(GLSL) || GLPlatform::instance()->supports(LimitedNPOT)) {
return false;
}
#endif
if (options->isGlLegacy()) {
kDebug(1212) << "OpenGL 2 disabled by config option";
return false;
}
return true;
}
SceneOpenGL2::SceneOpenGL2(OpenGLBackend *backend)
: SceneOpenGL(Workspace::self(), backend)
{
if (!ShaderManager::instance()->isValid()) {
kDebug(1212) << "No Scene Shaders available";
return;
}
// push one shader on the stack so that one is always bound
ShaderManager::instance()->pushShader(ShaderManager::SimpleShader);
if (checkGLError("Init")) {
kError(1212) << "OpenGL 2 compositing setup failed";
return; // error
}
kDebug(1212) << "OpenGL 2 compositing successfully initialized";
init_ok = true;
}
SceneOpenGL2::~SceneOpenGL2()
{
}
void SceneOpenGL2::paintGenericScreen(int mask, ScreenPaintData data)
{
ShaderManager *shaderManager = ShaderManager::instance();
GLShader *shader = shaderManager->pushShader(ShaderManager::GenericShader);
shader->setUniform(GLShader::ScreenTransformation, transformation(mask, data));
Scene::paintGenericScreen(mask, data);
shaderManager->popShader();
}
void SceneOpenGL2::doPaintBackground(const QVector< float >& vertices)
{
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
vbo->reset();
vbo->setUseColor(true);
vbo->setData(vertices.count() / 2, 2, vertices.data(), NULL);
GLShader *shader = ShaderManager::instance()->pushShader(ShaderManager::ColorShader);
shader->setUniform(GLShader::Offset, QVector2D(0, 0));
vbo->render(GL_TRIANGLES);
ShaderManager::instance()->popShader();
}
SceneOpenGL::Window *SceneOpenGL2::createWindow(Toplevel *t)
{
return new SceneOpenGL2Window(t);
}
//****************************************
// SceneOpenGL1
//****************************************
#ifndef KWIN_HAVE_OPENGLES
bool SceneOpenGL1::supported(OpenGLBackend *backend)
{
// any OpenGL context will do
return !backend->isFailed();
}
SceneOpenGL1::SceneOpenGL1(OpenGLBackend *backend)
: SceneOpenGL(Workspace::self(), backend)
, m_resetModelViewProjectionMatrix(true)
{
ShaderManager::disable();
setupModelViewProjectionMatrix();
if (checkGLError("Init")) {
kError(1212) << "OpenGL 1 compositing setup failed";
return; // error
}
kDebug(1212) << "OpenGL 1 compositing successfully initialized";
init_ok = true;
}
SceneOpenGL1::~SceneOpenGL1()
{
}
int SceneOpenGL1::paint(QRegion damage, ToplevelList windows)
{
if (m_resetModelViewProjectionMatrix) {
// reset model view projection matrix if required
setupModelViewProjectionMatrix();
}
return SceneOpenGL::paint(damage, windows);
}
void SceneOpenGL1::paintGenericScreen(int mask, ScreenPaintData data)
{
pushMatrix(transformation(mask, data));
Scene::paintGenericScreen(mask, data);
popMatrix();
}
void SceneOpenGL1::doPaintBackground(const QVector< float >& vertices)
{
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
vbo->reset();
vbo->setUseColor(true);
vbo->setData(vertices.count() / 2, 2, vertices.data(), NULL);
vbo->render(GL_TRIANGLES);
}
void SceneOpenGL1::setupModelViewProjectionMatrix()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float fovy = 60.0f;
float aspect = 1.0f;
float zNear = 0.1f;
float zFar = 100.0f;
float ymax = zNear * tan(fovy * M_PI / 360.0f);
float ymin = -ymax;
float xmin = ymin * aspect;
float xmax = ymax * aspect;
// swap top and bottom to have OpenGL coordinate system match X system
glFrustum(xmin, xmax, ymin, ymax, zNear, zFar);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
float scaleFactor = 1.1 * tan(fovy * M_PI / 360.0f) / ymax;
glTranslatef(xmin * scaleFactor, ymax * scaleFactor, -1.1);
glScalef((xmax - xmin)*scaleFactor / displayWidth(), -(ymax - ymin)*scaleFactor / displayHeight(), 0.001);
m_resetModelViewProjectionMatrix = false;
}
void SceneOpenGL1::screenGeometryChanged(const QSize &size)
{
SceneOpenGL::screenGeometryChanged(size);
m_resetModelViewProjectionMatrix = true;
}
SceneOpenGL::Window *SceneOpenGL1::createWindow(Toplevel *t)
{
return new SceneOpenGL1Window(t);
}
#endif
//****************************************
// SceneOpenGL::Texture
//****************************************
@ -561,17 +678,6 @@ SceneOpenGL::Window::~Window()
delete bottomTexture;
}
SceneOpenGL::Window *SceneOpenGL::Window::createWindow(Toplevel *t)
{
if (ShaderManager::instance()->isValid()) {
return new SceneOpenGL2Window(t);
}
#ifndef KWIN_HAVE_OPENGLES
return new SceneOpenGL1Window(t);
#endif
return NULL;
}
// Bind the window pixmap to an OpenGL texture.
bool SceneOpenGL::Window::bindTexture()
{

View File

@ -41,7 +41,6 @@ public:
class Texture;
class TexturePrivate;
class Window;
SceneOpenGL(Workspace* ws);
virtual ~SceneOpenGL();
virtual bool initFailed() const;
virtual CompositingType compositingType() const {
@ -65,23 +64,63 @@ public:
Texture *createTexture();
Texture *createTexture(const QPixmap& pix, GLenum target = GL_TEXTURE_2D);
static SceneOpenGL *createScene();
protected:
virtual void paintGenericScreen(int mask, ScreenPaintData data);
SceneOpenGL(Workspace* ws, OpenGLBackend *backend);
virtual void paintBackground(QRegion region);
QMatrix4x4 transformation(int mask, const ScreenPaintData &data) const;
virtual void doPaintBackground(const QVector<float> &vertices) = 0;
virtual SceneOpenGL::Window *createWindow(Toplevel *t) = 0;
public Q_SLOTS:
virtual void windowOpacityChanged(KWin::Toplevel* c);
virtual void windowGeometryShapeChanged(KWin::Toplevel* c);
virtual void windowClosed(KWin::Toplevel* c, KWin::Deleted* deleted);
private:
void setupModelViewProjectionMatrix();
bool m_resetModelViewProjectionMatrix;
QHash< Toplevel*, Window* > windows;
protected:
bool init_ok;
private:
QHash< Toplevel*, Window* > windows;
bool debug;
OpenGLBackend *m_backend;
};
class SceneOpenGL2 : public SceneOpenGL
{
public:
SceneOpenGL2(OpenGLBackend *backend);
virtual ~SceneOpenGL2();
static bool supported(OpenGLBackend *backend);
protected:
virtual void paintGenericScreen(int mask, ScreenPaintData data);
virtual void doPaintBackground(const QVector< float >& vertices);
virtual SceneOpenGL::Window *createWindow(Toplevel *t);
};
#ifndef KWIN_HAVE_OPENGLES
class SceneOpenGL1 : public SceneOpenGL
{
public:
SceneOpenGL1(OpenGLBackend *backend);
virtual ~SceneOpenGL1();
virtual void screenGeometryChanged(const QSize &size);
virtual int paint(QRegion damage, ToplevelList windows);
static bool supported(OpenGLBackend *backend);
protected:
virtual void paintGenericScreen(int mask, ScreenPaintData data);
virtual void doPaintBackground(const QVector< float >& vertices);
virtual SceneOpenGL::Window *createWindow(Toplevel *t);
private:
void setupModelViewProjectionMatrix();
bool m_resetModelViewProjectionMatrix;
};
#endif
class SceneOpenGL::TexturePrivate
: public GLTexturePrivate
{
@ -141,13 +180,6 @@ public:
void setScene(SceneOpenGL *scene) {
m_scene = scene;
}
/**
* @brief Factory method to create a Window taking the OpenGL version into account.
*
* @param t The Toplevel for which a Scene Window should be created
* @return :SceneOpenGL::Window* OpenGL version aware Window
**/
static SceneOpenGL::Window *createWindow(Toplevel *t);
protected:
Window(Toplevel* c);