Refactor the blur shader code a bit, and add a GLSL version.
svn path=/trunk/KDE/kdebase/workspace/; revision=1100848
This commit is contained in:
parent
17a38a7cdb
commit
3de8c53f21
3 changed files with 334 additions and 69 deletions
|
@ -38,7 +38,7 @@ KWIN_EFFECT_SUPPORTED(blur, BlurEffect::supported())
|
|||
BlurEffect::BlurEffect()
|
||||
: radius(12)
|
||||
{
|
||||
shader = new BlurShader;
|
||||
shader = BlurShader::create();
|
||||
shader->setRadius(radius);
|
||||
|
||||
// Offscreen texture that's used as the target for the horizontal blur pass
|
||||
|
@ -101,7 +101,8 @@ void BlurEffect::propertyNotify(EffectWindow *w, long atom)
|
|||
bool BlurEffect::supported()
|
||||
{
|
||||
return GLRenderTarget::supported() && GLTexture::NPOTTextureSupported() &&
|
||||
hasGLExtension("GL_ARB_fragment_program");
|
||||
((GLShader::vertexShaderSupported() && GLShader::fragmentShaderSupported()) ||
|
||||
hasGLExtension("GL_ARB_fragment_program"));
|
||||
}
|
||||
|
||||
QRect BlurEffect::expand(const QRect &rect) const
|
||||
|
|
|
@ -29,69 +29,35 @@ using namespace KWin;
|
|||
|
||||
|
||||
BlurShader::BlurShader()
|
||||
: program(0), radius(12)
|
||||
: mRadius(12)
|
||||
{
|
||||
}
|
||||
|
||||
BlurShader::~BlurShader()
|
||||
{
|
||||
if (program) {
|
||||
glDeleteProgramsARB(1, &program);
|
||||
program = 0;
|
||||
}
|
||||
|
||||
BlurShader *BlurShader::create()
|
||||
{
|
||||
if (GLShader::vertexShaderSupported() && GLShader::fragmentShaderSupported())
|
||||
return new GLSLBlurShader();
|
||||
|
||||
return new ARBBlurShader();
|
||||
}
|
||||
|
||||
void BlurShader::setRadius(int radius)
|
||||
{
|
||||
const int r = qMax(radius, 2);
|
||||
|
||||
if (mRadius != r) {
|
||||
mRadius = r;
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
void BlurShader::setRadius(int _radius)
|
||||
void BlurShader::setDirection(Qt::Orientation direction)
|
||||
{
|
||||
int r = qMax(_radius, 2);
|
||||
|
||||
if (radius != r) {
|
||||
radius = r;
|
||||
|
||||
if (program) {
|
||||
glDeleteProgramsARB(1, &program);
|
||||
program = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlurShader::setDirection(Qt::Orientation orientation)
|
||||
{
|
||||
direction = orientation;
|
||||
}
|
||||
|
||||
void BlurShader::setPixelDistance(float val)
|
||||
{
|
||||
float firstStep = val * 1.5;
|
||||
float nextStep = val * 2.0;
|
||||
|
||||
if (direction == Qt::Horizontal) {
|
||||
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, firstStep, 0, 0, 0);
|
||||
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, nextStep, 0, 0, 0);
|
||||
} else {
|
||||
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, 0, firstStep, 0, 0);
|
||||
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, 0, nextStep, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void BlurShader::setOpacity(float val)
|
||||
{
|
||||
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 2, val, val, val, val);
|
||||
}
|
||||
|
||||
void BlurShader::bind()
|
||||
{
|
||||
if (!program)
|
||||
init();
|
||||
|
||||
glEnable(GL_FRAGMENT_PROGRAM_ARB);
|
||||
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, program);
|
||||
}
|
||||
|
||||
void BlurShader::unbind()
|
||||
{
|
||||
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
|
||||
glDisable(GL_FRAGMENT_PROGRAM_ARB);
|
||||
mDirection = direction;
|
||||
}
|
||||
|
||||
float BlurShader::gaussian(float x, float sigma) const
|
||||
|
@ -102,9 +68,9 @@ float BlurShader::gaussian(float x, float sigma) const
|
|||
|
||||
QVector<float> BlurShader::gaussianKernel() const
|
||||
{
|
||||
const int size = radius | 1;
|
||||
const int size = mRadius | 1;
|
||||
QVector<float> kernel(size);
|
||||
const qreal sigma = radius / 2.5;
|
||||
const qreal sigma = mRadius / 2.5;
|
||||
const int center = size / 2;
|
||||
|
||||
// Generate the gaussian kernel
|
||||
|
@ -126,7 +92,246 @@ QVector<float> BlurShader::gaussianKernel() const
|
|||
return kernel;
|
||||
}
|
||||
|
||||
void BlurShader::init()
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
GLSLBlurShader::GLSLBlurShader()
|
||||
: BlurShader(), program(0)
|
||||
{
|
||||
}
|
||||
|
||||
GLSLBlurShader::~GLSLBlurShader()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void GLSLBlurShader::reset()
|
||||
{
|
||||
if (program) {
|
||||
glDeleteProgram(program);
|
||||
program = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GLSLBlurShader::setPixelDistance(float val)
|
||||
{
|
||||
float pixelSize[2] = { 0.0, 0.0 };
|
||||
|
||||
if (direction() == Qt::Horizontal)
|
||||
pixelSize[0] = val;
|
||||
else
|
||||
pixelSize[1] = val;
|
||||
|
||||
glUniform2fv(uPixelSize, 1, pixelSize);
|
||||
}
|
||||
|
||||
void GLSLBlurShader::setOpacity(float val)
|
||||
{
|
||||
const float opacity[4] = { val, val, val, val };
|
||||
glUniform4fv(uOpacity, 1, opacity);
|
||||
}
|
||||
|
||||
void GLSLBlurShader::bind()
|
||||
{
|
||||
if (!program)
|
||||
init();
|
||||
|
||||
glUseProgram(program);
|
||||
glUniform1i(uTexUnit, 0);
|
||||
}
|
||||
|
||||
void GLSLBlurShader::unbind()
|
||||
{
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
QVector<float> kernel = gaussianKernel();
|
||||
const int size = kernel.size();
|
||||
const int center = size / 2;
|
||||
|
||||
QByteArray vertexSource;
|
||||
QByteArray fragmentSource;
|
||||
|
||||
// Vertex shader
|
||||
// ===================================================================
|
||||
QTextStream stream(&vertexSource);
|
||||
|
||||
stream << "uniform vec2 pixelSize;\n\n";
|
||||
for (int i = 0; i < size; i++)
|
||||
stream << "varying vec2 samplePos" << i << ";\n";
|
||||
stream << "\n";
|
||||
stream << "void main(void)\n";
|
||||
stream << "{\n";
|
||||
|
||||
for (int i = 0; i < center; i++)
|
||||
stream << " samplePos" << i << " = gl_MultiTexCoord0.st + pixelSize * vec2("
|
||||
<< -(1.5 + (center - i - 1) * 2.0) << ");\n";
|
||||
stream << " samplePos" << center << " = gl_MultiTexCoord0.st;\n";
|
||||
for (int i = center + 1; i < size; i++)
|
||||
stream << " samplePos" << i << " = gl_MultiTexCoord0.st + pixelSize * vec2("
|
||||
<< 1.5 + (i - center - 1) * 2.0 << ");\n";
|
||||
stream << "\n";
|
||||
stream << " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n";
|
||||
stream << "}\n";
|
||||
stream.flush();
|
||||
|
||||
// Fragment shader
|
||||
// ===================================================================
|
||||
QTextStream stream2(&fragmentSource);
|
||||
|
||||
stream2 << "uniform sampler2D texUnit;\n";
|
||||
stream2 << "uniform vec4 opacity;\n\n";
|
||||
for (int i = 0; i < size; i++)
|
||||
stream2 << "varying vec2 samplePos" << i << ";\n";
|
||||
stream2 << "\n";
|
||||
for (int i = 0; i <= center; i++)
|
||||
stream2 << "const vec4 kernel" << i << " = vec4(" << kernel[i] << ");\n";
|
||||
stream2 << "\n";
|
||||
|
||||
stream2 << "void main(void)\n";
|
||||
stream2 << "{\n";
|
||||
stream2 << " vec4 sum = texture2D(texUnit, samplePos0) * kernel0;\n";
|
||||
for (int i = 1; i < size; i++)
|
||||
stream2 << " sum += texture2D(texUnit, samplePos" << i << ") * kernel"
|
||||
<< (i > center ? size - i - 1 : i) << ";\n";
|
||||
stream2 << " gl_FragColor = sum * opacity;\n";
|
||||
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");
|
||||
uOpacity = glGetUniformLocation(program, "opacity");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
ARBBlurShader::ARBBlurShader()
|
||||
: BlurShader(), program(0)
|
||||
{
|
||||
}
|
||||
|
||||
ARBBlurShader::~ARBBlurShader()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void ARBBlurShader::reset()
|
||||
{
|
||||
if (program) {
|
||||
glDeleteProgramsARB(1, &program);
|
||||
program = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ARBBlurShader::setPixelDistance(float val)
|
||||
{
|
||||
float firstStep = val * 1.5;
|
||||
float nextStep = val * 2.0;
|
||||
|
||||
if (direction() == Qt::Horizontal) {
|
||||
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, firstStep, 0, 0, 0);
|
||||
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, nextStep, 0, 0, 0);
|
||||
} else {
|
||||
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, 0, firstStep, 0, 0);
|
||||
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, 0, nextStep, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ARBBlurShader::setOpacity(float val)
|
||||
{
|
||||
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 2, val, val, val, val);
|
||||
}
|
||||
|
||||
void ARBBlurShader::bind()
|
||||
{
|
||||
if (!program)
|
||||
init();
|
||||
|
||||
glEnable(GL_FRAGMENT_PROGRAM_ARB);
|
||||
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, program);
|
||||
}
|
||||
|
||||
void ARBBlurShader::unbind()
|
||||
{
|
||||
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
|
||||
glDisable(GL_FRAGMENT_PROGRAM_ARB);
|
||||
}
|
||||
|
||||
void ARBBlurShader::init()
|
||||
{
|
||||
QVector<float> kernel = gaussianKernel();
|
||||
const int size = kernel.size();
|
||||
|
@ -180,7 +385,7 @@ void BlurShader::init()
|
|||
|
||||
if (glGetError()) {
|
||||
const char *error = (const char*)glGetString(GL_PROGRAM_ERROR_STRING_ARB);
|
||||
kError() << "Error when compiling fragment program:" << error;
|
||||
kError() << "Failed to compile fragment program:" << error;
|
||||
}
|
||||
|
||||
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
|
||||
|
|
|
@ -29,32 +29,91 @@ class BlurShader
|
|||
{
|
||||
public:
|
||||
BlurShader();
|
||||
~BlurShader();
|
||||
virtual ~BlurShader();
|
||||
|
||||
static BlurShader *create();
|
||||
|
||||
// Sets the radius in pixels
|
||||
void setRadius(int radius);
|
||||
int radius() const { return mRadius; }
|
||||
|
||||
// Sets the blur direction
|
||||
void setDirection(Qt::Orientation orientation);
|
||||
void setDirection(Qt::Orientation direction);
|
||||
Qt::Orientation direction() const { return mDirection; }
|
||||
|
||||
// Sets the distance between two pixels
|
||||
void setPixelDistance(float val);
|
||||
virtual void setPixelDistance(float val) = 0;
|
||||
|
||||
// The opacity of the resulting pixels is multiplied by this value
|
||||
void setOpacity(float val);
|
||||
virtual void setOpacity(float val) = 0;
|
||||
|
||||
virtual void bind() = 0;
|
||||
virtual void unbind() = 0;
|
||||
|
||||
protected:
|
||||
float gaussian(float x, float sigma) const;
|
||||
QVector<float> gaussianKernel() const;
|
||||
virtual void init() = 0;
|
||||
virtual void reset() = 0;
|
||||
|
||||
private:
|
||||
int mRadius;
|
||||
Qt::Orientation mDirection;
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
class GLSLBlurShader : public BlurShader
|
||||
{
|
||||
public:
|
||||
GLSLBlurShader();
|
||||
~GLSLBlurShader();
|
||||
|
||||
void setPixelDistance(float val);
|
||||
void setOpacity(float val);
|
||||
void bind();
|
||||
void unbind();
|
||||
|
||||
private:
|
||||
float gaussian(float x, float sigma) const;
|
||||
QVector<float> gaussianKernel() const;
|
||||
protected:
|
||||
void init();
|
||||
void reset();
|
||||
|
||||
GLuint compile(GLenum type, const QByteArray &source);
|
||||
GLuint link(GLuint vertexShader, GLuint fragmentShader);
|
||||
|
||||
private:
|
||||
GLuint program;
|
||||
int uTexUnit;
|
||||
int uPixelSize;
|
||||
int uOpacity;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
class ARBBlurShader : public BlurShader
|
||||
{
|
||||
public:
|
||||
ARBBlurShader();
|
||||
~ARBBlurShader();
|
||||
|
||||
void setPixelDistance(float val);
|
||||
void setOpacity(float val);
|
||||
void bind();
|
||||
void unbind();
|
||||
|
||||
protected:
|
||||
void init();
|
||||
void reset();
|
||||
|
||||
private:
|
||||
GLuint program;
|
||||
int radius;
|
||||
Qt::Orientation direction;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
|
|
Loading…
Reference in a new issue