From 6270b502babc366d3a2c177d4b565e2509f4c0d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Wed, 10 Nov 2010 18:25:40 +0000 Subject: [PATCH] ARB Shader fallback to Lanczos filter. This allows using Lanczos filter also on systems not supporting GLSL. See http://svn.reviewboard.kde.org/r/5777/ svn path=/trunk/KDE/kdebase/workspace/; revision=1195273 --- effects/blur/blurshader.cpp | 9 +- lanczosfilter.cpp | 185 ++++++++++++++++++++++++++++++------ lanczosfilter.h | 33 ++++++- 3 files changed, 189 insertions(+), 38 deletions(-) diff --git a/effects/blur/blurshader.cpp b/effects/blur/blurshader.cpp index 7e99dabb7b..7890f722d7 100644 --- a/effects/blur/blurshader.cpp +++ b/effects/blur/blurshader.cpp @@ -401,8 +401,13 @@ void ARBBlurShader::bind() void ARBBlurShader::unbind() { - glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0); - glDisable(GL_FRAGMENT_PROGRAM_ARB); + int boundObject; + glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_BINDING_ARB, &boundObject); + if( boundObject == program ) + { + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0); + glDisable(GL_FRAGMENT_PROGRAM_ARB); + } } int ARBBlurShader::maxKernelSize() const diff --git a/lanczosfilter.cpp b/lanczosfilter.cpp index 24b083d8f9..ac665aa732 100644 --- a/lanczosfilter.cpp +++ b/lanczosfilter.cpp @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2010 by Fredrik Höglund +Copyright (C) 2010 Martin Gräßlin 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 @@ -39,7 +40,7 @@ LanczosFilter::LanczosFilter( QObject* parent ) #ifdef KWIN_HAVE_OPENGL_COMPOSITING , m_offscreenTex( 0 ) , m_offscreenTarget( 0 ) - , m_shader( 0 ) + , m_shader( new LanczosShader( this ) ) #endif , m_inited( false) { @@ -50,7 +51,6 @@ LanczosFilter::~LanczosFilter() #ifdef KWIN_HAVE_OPENGL_COMPOSITING delete m_offscreenTarget; delete m_offscreenTex; - delete m_shader; #endif } @@ -69,24 +69,10 @@ void LanczosFilter::init() return; } - if ( GLShader::fragmentShaderSupported() && - GLShader::vertexShaderSupported() && - GLRenderTarget::supported() ) + if( !m_shader->init() ) { - m_shader = new GLShader(":/resources/lanczos-vertex.glsl", ":/resources/lanczos-fragment.glsl"); - if (m_shader->isValid()) - { - m_shader->bind(); - m_uTexUnit = m_shader->uniformLocation("texUnit"); - m_uKernel = m_shader->uniformLocation("kernel"); - m_uOffsets = m_shader->uniformLocation("offsets"); - m_shader->unbind(); - } - else - { - kDebug(1212) << "Shader is not valid"; - m_shader = 0; - } + delete m_shader; + m_shader = 0; } #endif } @@ -133,7 +119,8 @@ static float lanczos( float x, float a ) return sinc( x ) * sinc( x / a ); } -void LanczosFilter::createKernel( float delta, int *size ) +#ifdef KWIN_HAVE_OPENGL_COMPOSITING +void LanczosShader::createKernel( float delta, int *size ) { const float a = 2.0; @@ -164,7 +151,7 @@ void LanczosFilter::createKernel( float delta, int *size ) *size = kernelSize; } -void LanczosFilter::createOffsets( int count, float width, Qt::Orientation direction ) +void LanczosShader::createOffsets( int count, float width, Qt::Orientation direction ) { memset(m_offsets, 0, 25 * sizeof(QVector2D)); for ( int i = 0; i < count; i++ ) { @@ -172,6 +159,7 @@ void LanczosFilter::createOffsets( int count, float width, Qt::Orientation direc QVector2D( i / width, 0 ) : QVector2D( 0, i / width ); } } +#endif void LanczosFilter::performPaint( EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data ) { @@ -222,6 +210,7 @@ void LanczosFilter::performPaint( EffectWindowImpl* w, int mask, QRegion region, if( cachedTexture->width() == tw && cachedTexture->height() == th ) { cachedTexture->bind(); + data.opacity *= 0.99; prepareRenderStates( cachedTexture, data.opacity, data.brightness, data.saturation ); cachedTexture->render( textureRect, textureRect ); restoreRenderStates( cachedTexture, data.opacity, data.brightness, data.saturation ); @@ -265,13 +254,11 @@ void LanczosFilter::performPaint( EffectWindowImpl* w, int mask, QRegion region, // Set up the shader for horizontal scaling float dx = sw / float(tw); int kernelSize; - createKernel( dx, &kernelSize ); - createOffsets( kernelSize, sw, Qt::Horizontal ); + m_shader->createKernel( dx, &kernelSize ); + m_shader->createOffsets( kernelSize, sw, Qt::Horizontal ); m_shader->bind(); - glUniform1i( m_uTexUnit, 0 ); - glUniform2fv( m_uOffsets, 25, (const GLfloat*)m_offsets ); - glUniform4fv( m_uKernel, 25, (const GLfloat*)m_kernel ); + m_shader->setUniforms(); // Draw the window back into the FBO, this time scaled horizontally glClear( GL_COLOR_BUFFER_BIT ); @@ -297,11 +284,9 @@ void LanczosFilter::performPaint( EffectWindowImpl* w, int mask, QRegion region, // Set up the shader for vertical scaling float dy = sh / float(th); - createKernel( dy, &kernelSize ); - createOffsets( kernelSize, m_offscreenTex->height(), Qt::Vertical ); - - glUniform2fv( m_uOffsets, 25, (const GLfloat*)m_offsets ); - glUniform4fv( m_uKernel, 25, (const GLfloat*)m_kernel ); + m_shader->createKernel( dy, &kernelSize ); + m_shader->createOffsets( kernelSize, m_offscreenTex->height(), Qt::Vertical ); + m_shader->setUniforms(); // Now draw the horizontally scaled window in the FBO at the right // coordinates on the screen, while scaling it vertically and blending it. @@ -512,5 +497,143 @@ void LanczosFilter::restoreRenderStates( GLTexture* tex, double opacity, double #endif } +/************************************************ +* LanczosShader +************************************************/ +#ifdef KWIN_HAVE_OPENGL_COMPOSITING +LanczosShader::LanczosShader( QObject* parent ) + : QObject( parent ) + , m_shader( 0 ) + , m_arbProgram( 0 ) + { + } + +LanczosShader::~LanczosShader() + { + delete m_shader; + if( m_arbProgram ) + { + glDeleteProgramsARB(1, &m_arbProgram); + m_arbProgram = 0; + } + } + +void LanczosShader::bind() + { + if( m_shader ) + m_shader->bind(); + else + { + glEnable(GL_FRAGMENT_PROGRAM_ARB); + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_arbProgram); + } + } + +void LanczosShader::unbind() + { + if( m_shader ) + m_shader->unbind(); + else + { + int boundObject; + glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_BINDING_ARB, &boundObject); + if( boundObject == m_arbProgram ) + { + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0); + glDisable(GL_FRAGMENT_PROGRAM_ARB); + } + } + } + +void LanczosShader::setUniforms() + { + if( m_shader ) + { + glUniform1i( m_uTexUnit, 0 ); + glUniform2fv( m_uOffsets, 25, (const GLfloat*)m_offsets ); + glUniform4fv( m_uKernel, 25, (const GLfloat*)m_kernel ); + } + else + { + for( int i=0; i<25; ++i ) + { + glProgramLocalParameter4fARB( GL_FRAGMENT_PROGRAM_ARB, i, m_offsets[i].x(), m_offsets[i].y(), 0, 0 ); + } + for( int i=0; i<25; ++i ) + { + glProgramLocalParameter4fARB( GL_FRAGMENT_PROGRAM_ARB, i+25, m_kernel[i].x(), m_kernel[i].y(), m_kernel[i].z(), m_kernel[i].w() ); + } + } + } + +bool LanczosShader::init() + { + if ( GLShader::fragmentShaderSupported() && + GLShader::vertexShaderSupported() && + GLRenderTarget::supported() ) + { + m_shader = new GLShader(":/resources/lanczos-vertex.glsl", ":/resources/lanczos-fragment.glsl"); + if (m_shader->isValid()) + { + m_shader->bind(); + m_uTexUnit = m_shader->uniformLocation("texUnit"); + m_uKernel = m_shader->uniformLocation("kernel"); + m_uOffsets = m_shader->uniformLocation("offsets"); + m_shader->unbind(); + return true; + } + else + { + kDebug(1212) << "Shader is not valid"; + m_shader = 0; + // try ARB shader + } + } + + // try to create an ARB Shader + if( !hasGLExtension("GL_ARB_fragment_program") ) + return false; + + QByteArray text; + QTextStream stream(&text); + + stream << "!!ARBfp1.0\n"; + stream << "TEMP coord;\n"; // temporary variable to store texcoord + stream << "TEMP color;\n"; // temporary variable to store fetched texture colors + stream << "TEMP sum;\n"; // variable to render the final result + stream << "TEX sum, fragment.texcoord, texture[0], 2D;\n"; // sum = texture2D(texUnit, gl_TexCoord[0].st) + stream << "MUL sum, sum, program.local[25];\n"; // sum = sum * kernel[0] + for( int i=1; i<25; ++i ) + { + stream << "ADD coord, fragment.texcoord, program.local[" << i << "];\n"; // coord = gl_TexCoord[0] + offset[i] + stream << "TEX color, coord, texture[0], 2D;\n"; // color = texture2D(texUnit, coord) + stream << "MAD sum, color, program.local[" << (25+i) << "], sum;\n"; // sum += color * kernel[i] + stream << "SUB coord, fragment.texcoord, program.local[" << i << "];\n"; // coord = gl_TexCoord[0] - offset[i] + stream << "TEX color, coord, texture[0], 2D;\n"; // color = texture2D(texUnit, coord) + stream << "MAD sum, color, program.local[" << (25+i) << "], sum;\n"; // sum += color * kernel[i] + } + stream << "MOV result.color, sum;\n"; // gl_FragColor = sum + stream << "END\n"; + stream.flush(); + + glEnable(GL_FRAGMENT_PROGRAM_ARB); + glGenProgramsARB(1, &m_arbProgram); + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_arbProgram); + glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, text.length(), text.constData()); + + if( glGetError() ) + { + const char *error = (const char*)glGetString(GL_PROGRAM_ERROR_STRING_ARB); + kError() << "Failed to compile fragment program:" << error; + return false; + } + + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0); + glDisable(GL_FRAGMENT_PROGRAM_ARB); + kDebug( 1212 ) << "ARB Shader compiled, id: " << m_arbProgram; + return true; + } +#endif + } // namespace diff --git a/lanczosfilter.h b/lanczosfilter.h index fd9a327cc9..1f7a5ef06a 100644 --- a/lanczosfilter.h +++ b/lanczosfilter.h @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2010 by Fredrik Höglund +Copyright (C) 2010 Martin Gräßlin 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 @@ -37,6 +38,7 @@ class WindowPaintData; class GLTexture; class GLRenderTarget; class GLShader; +class LanczosShader; class LanczosFilter : public QObject @@ -53,23 +55,44 @@ class LanczosFilter private: void init(); void updateOffscreenSurfaces(); - void createKernel(float delta, int *kernelSize); - void createOffsets(int count, float width, Qt::Orientation direction); #ifdef KWIN_HAVE_OPENGL_COMPOSITING void prepareRenderStates( GLTexture* tex, double opacity, double brightness, double saturation ); void restoreRenderStates( GLTexture* tex, double opacity, double brightness, double saturation ); GLTexture *m_offscreenTex; GLRenderTarget *m_offscreenTarget; - GLShader *m_shader; + LanczosShader *m_shader; #endif QBasicTimer m_timer; - QVector2D m_offsets[25]; - QVector4D m_kernel[25]; bool m_inited; +}; + +#ifdef KWIN_HAVE_OPENGL_COMPOSITING +class LanczosShader + : public QObject +{ + Q_OBJECT + + public: + explicit LanczosShader( QObject* parent = 0 ); + virtual ~LanczosShader(); + bool init(); + void bind(); + void unbind(); + void setUniforms(); + + void createKernel(float delta, int *kernelSize); + void createOffsets(int count, float width, Qt::Orientation direction); + + private: + GLShader *m_shader; int m_uTexUnit; int m_uOffsets; int m_uKernel; + QVector2D m_offsets[25]; + QVector4D m_kernel[25]; + uint m_arbProgram; // TODO: GLuint }; +#endif } // namespace