Blur ported to GLES.

It now uses a GLShader for GLSL shaders and pushes it using the
ShaderManager.

It does not work with the nouveau driver plus GLES, but it works
with fglrx + desktop GL 2.x, so I assume it is a driver problem here.
This commit is contained in:
Martin Gräßlin 2011-01-08 19:56:22 +01:00
parent a478fc7ff3
commit 8c4fc28e1a
5 changed files with 110 additions and 122 deletions

View file

@ -89,6 +89,7 @@ endif( NOT KWIN_HAVE_OPENGLES_COMPOSITING )
# OpenGL-specific effects
if( KWIN_HAVE_OPENGL_COMPOSITING )
include( blur/CMakeLists.txt )
include( coverswitch/CMakeLists.txt )
include( cube/CMakeLists.txt )
include( explosion/CMakeLists.txt )
@ -106,7 +107,6 @@ if( KWIN_HAVE_OPENGL_COMPOSITING )
include( wobblywindows/CMakeLists.txt )
endif( KWIN_HAVE_OPENGL_COMPOSITING )
if( KWIN_HAVE_OPENGL_COMPOSITING AND NOT KWIN_HAVE_OPENGLES_COMPOSITING )
include( blur/CMakeLists.txt )
include( sharpen/CMakeLists.txt )
include( snow/CMakeLists.txt )
endif( KWIN_HAVE_OPENGL_COMPOSITING AND NOT KWIN_HAVE_OPENGLES_COMPOSITING )

View file

@ -22,6 +22,7 @@
#include <X11/Xatom.h>
#include <QMatrix4x4>
#include <KDebug>
namespace KWin
@ -191,34 +192,23 @@ QRegion BlurEffect::blurRegion(const EffectWindow *w) const
void BlurEffect::drawRegion(const QRegion &region)
{
const int vertexCount = region.rectCount() * 4;
const int vertexCount = region.rectCount() * 6;
if (vertices.size() < vertexCount)
vertices.resize(vertexCount);
int i = 0;
foreach (const QRect &r, region.rects()) {
vertices[i++] = QVector2D(r.x(), r.y());
vertices[i++] = QVector2D(r.x() + r.width(), r.y());
vertices[i++] = QVector2D(r.x() + r.width(), r.y() + r.height());
vertices[i++] = QVector2D(r.x(), r.y() + r.height());
}
if (vertexCount > 1000) {
glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, (float*)vertices.constData());
glVertexPointer(2, GL_FLOAT, 0, (float*)vertices.constData());
glDrawArrays(GL_QUADS, 0, vertexCount);
glPopClientAttrib();
} else {
glBegin(GL_QUADS);
for (int i = 0; i < vertexCount; i++) {
glTexCoord2fv((const float*)&vertices[i]);
glVertex2fv((const float*)&vertices[i]);
}
glEnd();
vertices[i++] = QVector2D(r.x() + r.width(), r.y());
vertices[i++] = QVector2D(r.x(), r.y());
vertices[i++] = QVector2D(r.x(), r.y() + r.height());
vertices[i++] = QVector2D(r.x(), r.y() + r.height());
vertices[i++] = QVector2D(r.x() + r.width(), r.y() + r.height());
vertices[i++] = QVector2D(r.x() + r.width(), r.y());
}
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
vbo->reset();
vbo->setData(vertexCount, 2, (float*)vertices.constData(), (float*)vertices.constData());
vbo->render(GL_TRIANGLES);
}
void BlurEffect::paintScreen(int mask, QRegion region, ScreenPaintData &data)
@ -293,11 +283,15 @@ void BlurEffect::doBlur(const QRegion& shape, const QRect& screen, const float o
// Set up the texture matrix to transform from screen coordinates
// to texture coordinates.
#ifndef KWIN_HAVE_OPENGLES
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glLoadIdentity();
glScalef(1.0 / scratch.width(), -1.0 / scratch.height(), 1);
glTranslatef(-r.x(), -scratch.height() - r.y(), 0);
#endif
pushMatrix();
QMatrix4x4 textureMatrix;
textureMatrix.scale(1.0 / scratch.width(), -1.0 / scratch.height(), 1);
textureMatrix.translate(-r.x(), -scratch.height() - r.y(), 0);
loadMatrix(textureMatrix);
shader->setTextureMatrix(textureMatrix);
drawRegion(expanded);
@ -314,7 +308,9 @@ void BlurEffect::doBlur(const QRegion& shape, const QRect& screen, const float o
// Modulate the blurred texture with the window opacity if the window isn't opaque
if (opacity < 1.0) {
#ifndef KWIN_HAVE_OPENGLES
glPushAttrib(GL_COLOR_BUFFER_BIT);
#endif
glEnable(GL_BLEND);
glBlendColor(0, 0, 0, opacity);
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
@ -322,17 +318,25 @@ void BlurEffect::doBlur(const QRegion& shape, const QRect& screen, const float o
// Set the up the texture matrix to transform from screen coordinates
// to texture coordinates.
glLoadIdentity();
glScalef(1.0 / tex->width(), -1.0 / tex->height(), 1);
glTranslatef(0, -tex->height(), 0);
textureMatrix.setToIdentity();
textureMatrix.scale(1.0 / tex->width(), -1.0 / tex->height(), 1);
textureMatrix.translate(0, -tex->height(), 0);
loadMatrix(textureMatrix);
shader->setTextureMatrix(textureMatrix);
drawRegion(shape);
glPopMatrix();
popMatrix();
#ifndef KWIN_HAVE_OPENGLES
glMatrixMode(GL_MODELVIEW);
#endif
if (opacity < 1.0)
if (opacity < 1.0) {
glDisable(GL_BLEND);
#ifndef KWIN_HAVE_OPENGLES
glPopAttrib();
#endif
}
tex->unbind();
shader->unbind();

View file

@ -19,8 +19,12 @@
#include "blurshader.h"
#include <kwineffects.h>
#include <QByteArray>
#include <QMatrix4x4>
#include <QTextStream>
#include <QVector2D>
#include <KDebug>
#include <cmath>
@ -103,7 +107,7 @@ QVector<float> BlurShader::gaussianKernel() const
GLSLBlurShader::GLSLBlurShader()
: BlurShader(), program(0)
: BlurShader(), shader(NULL)
{
}
@ -114,10 +118,8 @@ GLSLBlurShader::~GLSLBlurShader()
void GLSLBlurShader::reset()
{
if (program) {
glDeleteProgram(program);
program = 0;
}
delete shader;
shader = NULL;
setIsValid(false);
}
@ -129,6 +131,7 @@ bool GLSLBlurShader::supported()
(void) glGetError(); // Clear the error state
#ifndef KWIN_HAVE_OPENGLES
// These are the minimum values the implementation is required to support
int value = 0;
@ -143,6 +146,7 @@ bool GLSLBlurShader::supported()
glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &value);
if (value < 512)
return false;
#endif
if (glGetError() != GL_NO_ERROR)
return false;
@ -155,14 +159,20 @@ void GLSLBlurShader::setPixelDistance(float val)
if (!isValid())
return;
float pixelSize[2] = { 0.0, 0.0 };
QVector2D pixelSize(0.0, 0.0);
if (direction() == Qt::Horizontal)
pixelSize[0] = val;
pixelSize.setX(val);
else
pixelSize[1] = val;
pixelSize.setY(val);
shader->setUniform("pixelSize", pixelSize);
}
glUniform2fv(uPixelSize, 1, pixelSize);
void GLSLBlurShader::setTextureMatrix(const QMatrix4x4 &matrix)
{
if (!isValid()) {
return;
}
shader->setUniform("u_textureMatrix", matrix);
}
void GLSLBlurShader::bind()
@ -170,74 +180,30 @@ void GLSLBlurShader::bind()
if (!isValid())
return;
glUseProgram(program);
glUniform1i(uTexUnit, 0);
ShaderManager::instance()->pushShader(shader);
}
void GLSLBlurShader::unbind()
{
glUseProgram(0);
ShaderManager::instance()->popShader();
}
int GLSLBlurShader::maxKernelSize() const
{
int value;
#ifdef KWIN_HAVE_OPENGLES
// GL_MAX_VARYING_FLOATS not available in GLES
// querying for GL_MAX_VARYING_VECTORS crashes on nouveau
// using the minimum value of 8
return 8;
#else
glGetIntegerv(GL_MAX_VARYING_FLOATS, &value);
// Note: In theory the driver could pack two vec2's in one vec4,
// but we'll assume it doesn't do that
return value / 4; // Max number of vec4 varyings
#endif
}
GLuint GLSLBlurShader::compile(GLenum type, const QByteArray &source)
{
const char *sourceData = source.constData();
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &sourceData, 0);
glCompileShader(shader);
int status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
GLsizei size, length;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size);
QByteArray log(size, 0);
glGetShaderInfoLog(shader, size, &length, log.data());
kError() << "Failed to compile shader: " << log;
glDeleteShader(shader);
shader = 0;
}
return shader;
}
GLuint GLSLBlurShader::link(GLuint vertexShader, GLuint fragmentShader)
{
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
int status;
glGetProgramiv(program, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
GLsizei size, length;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &size);
QByteArray log(size, 0);
glGetProgramInfoLog(program, size, &length, log.data());
kError() << "Failed to link shader: " << log;
glDeleteProgram(program);
program = 0;
}
return program;
}
void GLSLBlurShader::init()
{
@ -252,13 +218,17 @@ void GLSLBlurShader::init()
// ===================================================================
QTextStream stream(&vertexSource);
stream << "uniform mat4 u_modelViewProjectionMatrix;\n";
stream << "uniform mat4 u_textureMatrix;\n";
stream << "uniform vec2 pixelSize;\n\n";
stream << "attribute vec4 vertex;\n";
stream << "attribute vec4 texCoord;\n\n";
for (int i = 0; i < size; i++)
stream << "varying vec2 samplePos" << i << ";\n";
stream << "\n";
stream << "void main(void)\n";
stream << "{\n";
stream << " vec2 center = vec4(gl_TextureMatrix[0] * gl_MultiTexCoord0).st;\n\n";
stream << " vec2 center = vec4(texCoord * u_textureMatrix).st;\n\n";
for (int i = 0; i < center; i++)
stream << " samplePos" << i << " = center + pixelSize * vec2("
@ -268,7 +238,7 @@ void GLSLBlurShader::init()
stream << " samplePos" << i << " = center + pixelSize * vec2("
<< 1.5 + (i - center - 1) * 2.0 << ");\n";
stream << "\n";
stream << " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n";
stream << " gl_Position = vertex * u_modelViewProjectionMatrix;\n";
stream << "}\n";
stream.flush();
@ -294,24 +264,18 @@ void GLSLBlurShader::init()
stream2 << "}\n";
stream2.flush();
GLuint vertexShader = compile(GL_VERTEX_SHADER, vertexSource);
GLuint fragmentShader = compile(GL_FRAGMENT_SHADER, fragmentSource);
if (vertexShader && fragmentShader)
program = link(vertexShader, fragmentShader);
if (vertexShader)
glDeleteShader(vertexShader);
if (fragmentShader)
glDeleteShader(fragmentShader);
if (program) {
uTexUnit = glGetUniformLocation(program, "texUnit");
uPixelSize = glGetUniformLocation(program, "pixelSize");
shader = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentSource);
if (shader->isValid()) {
QMatrix4x4 modelViewProjection;
modelViewProjection.ortho(0, displayWidth(), displayHeight(), 0, 0, 65535);
ShaderManager::instance()->pushShader(shader);
shader->setUniform("texUnit", 0);
shader->setUniform("u_textureMatrix", QMatrix4x4());
shader->setUniform("u_modelViewProjectionMatrix", modelViewProjection);
ShaderManager::instance()->popShader();
}
setIsValid(program != 0);
setIsValid(shader->isValid());
}
@ -332,16 +296,21 @@ ARBBlurShader::~ARBBlurShader()
void ARBBlurShader::reset()
{
#ifndef KWIN_HAVE_OPENGLES
if (program) {
glDeleteProgramsARB(1, &program);
program = 0;
}
setIsValid(false);
#endif
}
bool ARBBlurShader::supported()
{
#ifdef KWIN_HAVE_OPENGLES
return false;
#else
if (!hasGLExtension("GL_ARB_fragment_program"))
return false;
@ -374,10 +343,14 @@ bool ARBBlurShader::supported()
return false;
return true;
#endif
}
void ARBBlurShader::setPixelDistance(float val)
{
#ifdef KWIN_HAVE_OPENGLES
Q_UNUSED(val)
#else
float firstStep = val * 1.5;
float nextStep = val * 2.0;
@ -388,19 +361,23 @@ void ARBBlurShader::setPixelDistance(float val)
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, 0, firstStep, 0, 0);
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, 0, nextStep, 0, 0);
}
#endif
}
void ARBBlurShader::bind()
{
#ifndef KWIN_HAVE_OPENGLES
if (!isValid())
return;
glEnable(GL_FRAGMENT_PROGRAM_ARB);
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, program);
#endif
}
void ARBBlurShader::unbind()
{
#ifndef KWIN_HAVE_OPENGLES
int boundObject;
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_BINDING_ARB, &boundObject);
if( boundObject == program )
@ -408,10 +385,14 @@ void ARBBlurShader::unbind()
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
glDisable(GL_FRAGMENT_PROGRAM_ARB);
}
#endif
}
int ARBBlurShader::maxKernelSize() const
{
#ifdef KWIN_HAVE_OPENGLES
return 0;
#else
int value;
int result;
@ -422,10 +403,12 @@ int ARBBlurShader::maxKernelSize() const
result = qMin(result, value / 3); // We need 3 instructions / sample
return result;
#endif
}
void ARBBlurShader::init()
{
#ifndef KWIN_HAVE_OPENGLES
QVector<float> kernel = gaussianKernel();
const int size = kernel.size();
const int center = size / 2;
@ -481,6 +464,7 @@ void ARBBlurShader::init()
} else
setIsValid(true);
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
#endif
}

View file

@ -22,6 +22,8 @@
#include <kwinglutils.h>
class QMatrix4x4;
namespace KWin
{
@ -45,6 +47,7 @@ public:
// Sets the distance between two pixels
virtual void setPixelDistance(float val) = 0;
virtual void setTextureMatrix(const QMatrix4x4 &matrix) = 0;
virtual void bind() = 0;
virtual void unbind() = 0;
@ -77,6 +80,7 @@ public:
void setPixelDistance(float val);
void bind();
void unbind();
void setTextureMatrix(const QMatrix4x4 &matrix);
static bool supported();
@ -85,13 +89,8 @@ protected:
void reset();
int maxKernelSize() const;
GLuint compile(GLenum type, const QByteArray &source);
GLuint link(GLuint vertexShader, GLuint fragmentShader);
private:
GLuint program;
int uTexUnit;
int uPixelSize;
GLShader *shader;
};
@ -109,6 +108,7 @@ public:
void setPixelDistance(float val);
void bind();
void unbind();
void setTextureMatrix(const QMatrix4x4 &) {};
static bool supported();

View file

@ -39,6 +39,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#endif
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
#include "blur/blur_config.h"
#include "coverswitch/coverswitch_config.h"
#include "cube/cube_config.h"
#include "cube/cubeslide_config.h"
@ -51,7 +52,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "trackmouse/trackmouse_config.h"
#include "wobblywindows/wobblywindows_config.h"
#ifndef KWIN_HAVE_OPENGLES
#include "blur/blur_config.h"
#include "sharpen/sharpen_config.h"
#include "snow/snow_config.h"
#endif
@ -81,6 +81,7 @@ KWIN_EFFECT_CONFIG_MULTIPLE( builtins,
#endif
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
KWIN_EFFECT_CONFIG_SINGLE( blur, BlurEffectConfig )
KWIN_EFFECT_CONFIG_SINGLE( coverswitch, CoverSwitchEffectConfig )
KWIN_EFFECT_CONFIG_SINGLE( cube, CubeEffectConfig )
KWIN_EFFECT_CONFIG_SINGLE( cubeslide, CubeSlideEffectConfig )
@ -93,7 +94,6 @@ KWIN_EFFECT_CONFIG_MULTIPLE( builtins,
KWIN_EFFECT_CONFIG_SINGLE( trackmouse, TrackMouseEffectConfig )
KWIN_EFFECT_CONFIG_SINGLE( wobblywindows, WobblyWindowsEffectConfig )
#ifndef KWIN_HAVE_OPENGLES
KWIN_EFFECT_CONFIG_SINGLE( blur, BlurEffectConfig )
KWIN_EFFECT_CONFIG_SINGLE( sharpen, SharpenEffectConfig )
KWIN_EFFECT_CONFIG_SINGLE( snow, SnowEffectConfig )
#endif