[amarok] /: Rewrite Block Analyzer to use pure OpenGL instead of QGLPaintEngine2.

Mark Kretschmann kretschmann at kde.org
Sat Oct 19 11:26:12 UTC 2013


Git commit f4a3f4fcf59cc0c592bd6d703542cb162a19721c by Mark Kretschmann.
Committed on 19/07/2013 at 08:37.
Pushed by markey into branch 'master'.

Rewrite Block Analyzer to use pure OpenGL instead of QGLPaintEngine2.

This rewrite should fix a number of issues that users had, especially
with Intel drivers. The issues, including crashes, are all due to driver
bugs, but essentially they were triggered by Qt using some uncommon
features for texture drawing (stencil operations, etc).

This commit should be backported to 2.8.1.

CCMAIL: amarok-devel at kde.org
CCBUG: 323635
BACKPORT

M  +1    -0    ChangeLog
M  +12   -22   src/context/applets/analyzer/AnalyzerBase.cpp
M  +3    -19   src/context/applets/analyzer/AnalyzerBase.h
M  +1    -1    src/context/applets/analyzer/BallsAnalyzer.cpp
M  +1    -1    src/context/applets/analyzer/BallsAnalyzer.h
M  +82   -23   src/context/applets/analyzer/BlockAnalyzer.cpp
M  +9    -10   src/context/applets/analyzer/BlockAnalyzer.h
M  +1    -1    src/context/applets/analyzer/DiscoAnalyzer.cpp
M  +1    -1    src/context/applets/analyzer/DiscoAnalyzer.h

http://commits.kde.org/amarok/f4a3f4fcf59cc0c592bd6d703542cb162a19721c

diff --git a/ChangeLog b/ChangeLog
index 520f427..b6ad644 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -10,6 +10,7 @@ VERSION 2.9-Beta 1
   CHANGES:
 
   BUGFIXES:
+   * Fixed crashes with Intel drivers due to Analyzer applet. (BR 323635)
    * Fixed performance problem with large podcast feeds.
      Patch by Frank Meerkoetter <frank at meerkoetter.org>. (BR 283022)
    * Fixed issue with Amarok sometimes not finding its plugins after an upgrade.
diff --git a/src/context/applets/analyzer/AnalyzerBase.cpp b/src/context/applets/analyzer/AnalyzerBase.cpp
index c413d37..0e649f8 100644
--- a/src/context/applets/analyzer/AnalyzerBase.cpp
+++ b/src/context/applets/analyzer/AnalyzerBase.cpp
@@ -42,7 +42,8 @@ Analyzer::Base::Base( QWidget *parent )
 {
     connect( EngineController::instance(), SIGNAL( playbackStateChanged() ), this, SLOT( playbackStateChanged() ) );
 
-    m_demoTimer->setInterval( 33 );
+    setFps( 60 ); // Default unless changed by subclass
+    m_demoTimer->setInterval( 33 );  // ~30 fps
 
     enableDemo( !EngineController::instance()->isPlaying() );
 
@@ -50,6 +51,13 @@ Analyzer::Base::Base( QWidget *parent )
     connect( KWindowSystem::self(), SIGNAL( currentDesktopChanged( int ) ), this, SLOT( currentDesktopChanged() ) );
 #endif
 
+    connect( m_renderTimer, SIGNAL( timeout() ), this, SLOT( updateGL() ) );
+
+    //initialize openGL context before managing GL calls
+    makeCurrent();
+
+    initializeGLFunctions();
+
     connectSignals();
 }
 
@@ -213,28 +221,10 @@ Analyzer::Base::interpolate( const QVector<float> &inVec, QVector<float> &outVec
     }
 }
 
-
-
-
-Analyzer::Base2D::Base2D( QWidget *parent )
-    : Base( parent )
-{
-    m_renderTimer->setInterval( 20 ); //~50 FPS
-    connect( m_renderTimer, SIGNAL( timeout() ), this, SLOT( update() ) );
-}
-
-
-
-Analyzer::Base3D::Base3D( QWidget *parent )
-    : Base( parent )
+void
+Analyzer::Base::setFps( int fps )
 {
-    m_renderTimer->setInterval( 17 ); //~60 FPS
-    connect( m_renderTimer, SIGNAL( timeout() ), this, SLOT( updateGL() ) );
-
-    //initialize openGL context before managing GL calls
-    makeCurrent();
-
-    initializeGLFunctions();
+    m_renderTimer->setInterval( 1000 / fps );
 }
 
 
diff --git a/src/context/applets/analyzer/AnalyzerBase.h b/src/context/applets/analyzer/AnalyzerBase.h
index df81152..0497428 100644
--- a/src/context/applets/analyzer/AnalyzerBase.h
+++ b/src/context/applets/analyzer/AnalyzerBase.h
@@ -36,7 +36,7 @@
 namespace Analyzer
 {
 
-class Base : public QGLWidget
+class Base : public QGLWidget, protected QGLFunctions
 {
     Q_OBJECT
 
@@ -49,6 +49,8 @@ protected:
     virtual void transform( QVector<float>& );
     virtual void analyze( const QVector<float>& ) = 0;
 
+    void setFps( int fps );
+
     FHT    *m_fht;
     QTimer *m_renderTimer;
 
@@ -71,24 +73,6 @@ private:
 };
 
 
-class Base2D : public Base
-{
-    Q_OBJECT
-
-protected:
-    Base2D( QWidget* );
-};
-
-
-class Base3D : public Base, protected QGLFunctions
-{
-    Q_OBJECT
-
-protected:
-    Base3D( QWidget* );
-};
-
-
 } //END namespace Analyzer
 
 
diff --git a/src/context/applets/analyzer/BallsAnalyzer.cpp b/src/context/applets/analyzer/BallsAnalyzer.cpp
index 2d633f8..6b4512f 100644
--- a/src/context/applets/analyzer/BallsAnalyzer.cpp
+++ b/src/context/applets/analyzer/BallsAnalyzer.cpp
@@ -118,7 +118,7 @@ private:
 
 
 BallsAnalyzer::BallsAnalyzer( QWidget *parent ):
-    Analyzer::Base3D( parent )
+    Analyzer::Base( parent )
 {
     setObjectName( "Balls" );
 
diff --git a/src/context/applets/analyzer/BallsAnalyzer.h b/src/context/applets/analyzer/BallsAnalyzer.h
index b606814..f7d7365 100644
--- a/src/context/applets/analyzer/BallsAnalyzer.h
+++ b/src/context/applets/analyzer/BallsAnalyzer.h
@@ -24,7 +24,7 @@ class QWidget;
 class Ball;
 class Paddle;
 
-class BallsAnalyzer : public Analyzer::Base3D
+class BallsAnalyzer : public Analyzer::Base
 {
 public:
     BallsAnalyzer( QWidget * );
diff --git a/src/context/applets/analyzer/BlockAnalyzer.cpp b/src/context/applets/analyzer/BlockAnalyzer.cpp
index 404e26a..03fefae 100644
--- a/src/context/applets/analyzer/BlockAnalyzer.cpp
+++ b/src/context/applets/analyzer/BlockAnalyzer.cpp
@@ -31,28 +31,47 @@ static inline uint myMax( uint v1, uint v2 )
 }
 
 BlockAnalyzer::BlockAnalyzer( QWidget *parent )
-    : Analyzer::Base2D( parent )
+    : Analyzer::Base( parent )
     , m_columns( 0 )         //int
     , m_rows( 0 )            //int
     , m_y( 0 )               //uint
-    , m_topBarPixmap( BLOCK_WIDTH, BLOCK_HEIGHT )
+    , m_barTexture( 0 )
+    , m_topBarTexture( 0 )
     , m_fade_bars( FADE_SIZE ) //vector<QPixmap>
     , m_fade_pos( MAX_COLUMNS, 50 ) //vector<uint>
     , m_fade_intensity( MAX_COLUMNS, 32 ) //vector<uint>
+    , m_background( 0 )
 {
     setObjectName( "Blocky" );
     setMaximumWidth( MAX_COLUMNS * ( BLOCK_WIDTH + 1 ) - 1 );
+    setFps( 50 );
 }
 
 BlockAnalyzer::~BlockAnalyzer()
-{}
+{
+    deleteTexture( m_barTexture );
+    deleteTexture( m_topBarTexture );
+
+    foreach( GLuint id, m_fade_bars )
+        deleteTexture( id );
+}
+
+void
+BlockAnalyzer::initializeGL()
+{
+    // Disable depth test (all is drawn on a 2d plane)
+    glDisable( GL_DEPTH_TEST );
+}
 
 void
-BlockAnalyzer::resizeEvent( QResizeEvent *e )
+BlockAnalyzer::resizeGL( int w, int h )
 {
-    Analyzer::Base2D::resizeEvent( e );
+    glViewport( 0, 0, (GLint)w, (GLint)h );
 
-    m_background = QPixmap( size() );
+    // Set up a 2D projection matrix
+    glMatrixMode( GL_PROJECTION );
+    glLoadIdentity();
+    glOrtho( 0.0, (GLdouble)w, (GLdouble)h, 0.0, 0.0, 1.0 );
 
     const int oldRows = m_rows;
 
@@ -70,9 +89,6 @@ BlockAnalyzer::resizeEvent( QResizeEvent *e )
     {
         m_barPixmap = QPixmap( BLOCK_WIDTH, m_rows * ( BLOCK_HEIGHT + 1 ) );
 
-        for( int i = 0; i < FADE_SIZE; ++i )
-            m_fade_bars[i] = QPixmap( BLOCK_WIDTH, m_rows * ( BLOCK_HEIGHT + 1 ) );
-
         m_yscale.resize( m_rows + 1 );
 
         const float PRE = 1, PRO = 1; //PRE and PRO allow us to restrict the range somewhat
@@ -123,7 +139,7 @@ BlockAnalyzer::analyze( const QVector<float> &s )
 }
 
 void
-BlockAnalyzer::paintEvent( QPaintEvent* )
+BlockAnalyzer::paintGL()
 {
     // y = 2 3 2 1 0 2
     //     . . . . # .
@@ -138,10 +154,11 @@ BlockAnalyzer::paintEvent( QPaintEvent* )
     // m_yscale looks similar to: { 0.7, 0.5, 0.25, 0.15, 0.1, 0 }
     // if it contains 6 elements there are 5 rows in the analyzer
 
-    QPainter p( this );
+    glMatrixMode( GL_MODELVIEW );
+    glLoadIdentity();
 
     // Paint the background
-    p.drawPixmap( 0, 0, m_background );
+    drawTexture( m_background, 0, 0, 0, 0, width(), height() );
 
     for( uint y, x = 0; x < (uint)m_scope.size(); ++x )
     {
@@ -169,36 +186,66 @@ BlockAnalyzer::paintEvent( QPaintEvent* )
             const uint offset = --m_fade_intensity[x];
             const uint y = m_y + ( m_fade_pos[x] * ( BLOCK_HEIGHT + 1 ) );
             if( y < (uint)height() )
-                p.drawPixmap( x * ( BLOCK_WIDTH + 1 ), y, m_fade_bars[offset], 0, 0, BLOCK_WIDTH, height() - y );
+                drawTexture( m_fade_bars[offset], x * ( BLOCK_WIDTH + 1 ), y, 0, 0, BLOCK_WIDTH, height() );
         }
 
         if( m_fade_intensity[x] == 0 )
             m_fade_pos[x] = m_rows;
 
         // REMEMBER: y is a number from 0 to m_rows, 0 means all blocks are glowing, m_rows means none are
-        p.drawPixmap( x * ( BLOCK_WIDTH + 1 ), y * ( BLOCK_HEIGHT + 1 ) + m_y, *bar(), 0, y * ( BLOCK_HEIGHT + 1 ), -1, -1 );
+        drawTexture( m_barTexture, x * ( BLOCK_WIDTH + 1 ), y * ( BLOCK_HEIGHT + 1 ) + m_y, 0, y * ( BLOCK_HEIGHT + 1 ), m_barPixmap.width(), m_barPixmap.height() );
 
-        // Draw top pixmaps
-        p.drawPixmap( x * ( BLOCK_WIDTH + 1 ), int( m_store[x] ) * ( BLOCK_HEIGHT + 1 ) + m_y, m_topBarPixmap );
+        // Draw top bar
+        drawTexture( m_topBarTexture, x * ( BLOCK_WIDTH + 1 ), int( m_store[x] ) * ( BLOCK_HEIGHT + 1 ) + m_y, 0, 0, BLOCK_WIDTH, BLOCK_HEIGHT );
     }
 }
 
 void
+BlockAnalyzer::drawTexture( GLuint textureId, int x, int y, int sx, int sy, int w, int h )
+{
+    const GLfloat xf = x;
+    const GLfloat yf = y;
+    const GLfloat sxf = (GLfloat)sx / m_barPixmap.width();
+    const GLfloat syf = (GLfloat)sy / m_barPixmap.height();
+    const GLfloat wf = w - sx;
+    const GLfloat hf = h - sy;
+
+    glEnable( GL_TEXTURE_2D );
+    glBindTexture( GL_TEXTURE_2D, textureId );
+
+    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+
+    // Draw a textured quad
+    glBegin(GL_QUADS);
+    glTexCoord2f( sxf, syf ); glVertex2f( xf, yf );
+    glTexCoord2f( sxf, 1.0 ); glVertex2f( xf, yf + hf );
+    glTexCoord2f( 1.0, 1.0 ); glVertex2f( xf + wf, yf + hf );
+    glTexCoord2f( 1.0, syf ); glVertex2f( xf + wf, yf );
+    glEnd();
+
+    glDisable( GL_TEXTURE_2D );
+}
+
+void
 BlockAnalyzer::paletteChange( const QPalette& ) //virtual
 {
-    QPainter p( bar() );
+    QPainter p( &m_barPixmap );
 
     const QColor bg = The::paletteHandler()->backgroundColor();
     const QColor fg = palette().color( QPalette::Active, QPalette::Highlight );
 
-    m_topBarPixmap.fill( fg );
+    QPixmap topBar( BLOCK_WIDTH, BLOCK_HEIGHT );
+    topBar.fill( fg );
+    deleteTexture( m_topBarTexture );
+    m_topBarTexture = bindTexture( topBar );
 
     const double dr = 15 * double( bg.red()   - fg.red() )   / ( m_rows * 16 );
     const double dg = 15 * double( bg.green() - fg.green() ) / ( m_rows * 16 );
     const double db = 15 * double( bg.blue()  - fg.blue() )  / ( m_rows * 16 );
     const int r = fg.red(), g = fg.green(), b = fg.blue();
 
-    bar()->fill( bg );
+    m_barPixmap.fill( bg );
 
     for( int y = 0; y < m_rows; ++y )
         //graduate the fg color
@@ -220,14 +267,23 @@ BlockAnalyzer::paletteChange( const QPalette& ) //virtual
         // Precalculate all fade-bar pixmaps
         for( int y = 0; y < FADE_SIZE; ++y )
         {
-            m_fade_bars[y].fill( palette().color( QPalette::Active, QPalette::Window ) );
+            QPixmap fadeBar( BLOCK_WIDTH, m_rows * ( BLOCK_HEIGHT + 1 ) );
+
+            fadeBar.fill( palette().color( QPalette::Active, QPalette::Window ) );
             const double Y = 1.0 - ( log10( ( FADE_SIZE ) - y ) / log10( ( FADE_SIZE ) ) );
-            QPainter f( &m_fade_bars[y] );
+            QPainter f( &fadeBar );
             for( int z = 0; z < m_rows; ++z )
                 f.fillRect( 0, z * ( BLOCK_HEIGHT + 1 ), BLOCK_WIDTH, BLOCK_HEIGHT, QColor( r + int( dr * Y ), g + int( dg * Y ), b + int( db * Y ) ) );
+
+            deleteTexture( m_fade_bars[y] );
+            m_fade_bars[y] = bindTexture( fadeBar );
         }
     }
 
+    const QImage image = m_barPixmap.toImage();
+    deleteTexture( m_barTexture );
+    m_barTexture = bindTexture( image.mirrored() ); // Flip vertically because OpenGL has inverted y coordinates
+
     drawBackground();
 }
 
@@ -237,11 +293,14 @@ BlockAnalyzer::drawBackground()
     const QColor bg = palette().color( QPalette::Active, QPalette::Window );
     const QColor bgdark = bg.dark( 112 );
 
-    m_background.fill( bg );
+    QPixmap background( size() );
+    background.fill( bg );
 
-    QPainter p( &m_background );
+    QPainter p( &background );
     for( int x = 0; x < m_columns; ++x )
         for( int y = 0; y < m_rows; ++y )
             p.fillRect( x * ( BLOCK_WIDTH + 1 ), y * ( BLOCK_HEIGHT + 1 ) + m_y, BLOCK_WIDTH, BLOCK_HEIGHT, bgdark );
 
+    deleteTexture( m_background );
+    m_background = bindTexture( background );
 }
diff --git a/src/context/applets/analyzer/BlockAnalyzer.h b/src/context/applets/analyzer/BlockAnalyzer.h
index 6b14e4e..318289a 100644
--- a/src/context/applets/analyzer/BlockAnalyzer.h
+++ b/src/context/applets/analyzer/BlockAnalyzer.h
@@ -24,7 +24,7 @@ class QMouseEvent;
 class QPalette;
 class QResizeEvent;
 
-class BlockAnalyzer : public Analyzer::Base2D
+class BlockAnalyzer : public Analyzer::Base
 {
 public:
     BlockAnalyzer( QWidget* );
@@ -39,33 +39,32 @@ public:
     static const int FADE_SIZE    = 90;
 
 protected:
+    virtual void initializeGL();
+    virtual void paintGL();
+    virtual void resizeGL( int w, int h );
     virtual void transform( QVector<float>& );
     virtual void analyze( const QVector<float>& );
-    virtual void paintEvent( QPaintEvent* );
-    virtual void resizeEvent( QResizeEvent* );
     virtual void paletteChange( const QPalette& );
 
     void drawBackground();
     void determineStep();
 
 private:
-    QPixmap* bar()
-    {
-        return &m_barPixmap;
-    }
+    void drawTexture( GLuint textureId, int x, int y, int sx, int sy, int w, int h );
 
     int m_columns, m_rows;      //number of rows and columns of blocks
     uint m_y;                    //y-offset from top of widget
+    GLuint m_barTexture;
+    GLuint m_topBarTexture;
     QPixmap m_barPixmap;
-    QPixmap m_topBarPixmap;
     QVector<float> m_scope;      //so we don't create a vector every frame
     QVector<float> m_store;  //current bar heights
     QVector<float> m_yscale;
 
-    QVector<QPixmap> m_fade_bars;
+    QVector<GLuint>  m_fade_bars;
     QVector<uint>    m_fade_pos;
     QVector<int>     m_fade_intensity;
-    QPixmap          m_background;
+    GLuint           m_background;
 
     float m_step; //rows to fall per frame
 };
diff --git a/src/context/applets/analyzer/DiscoAnalyzer.cpp b/src/context/applets/analyzer/DiscoAnalyzer.cpp
index ce3f56d..a81cde4 100644
--- a/src/context/applets/analyzer/DiscoAnalyzer.cpp
+++ b/src/context/applets/analyzer/DiscoAnalyzer.cpp
@@ -25,7 +25,7 @@
 
 
 DiscoAnalyzer::DiscoAnalyzer( QWidget *parent ):
-    Analyzer::Base3D( parent )
+    Analyzer::Base( parent )
 {
     setObjectName( "Disco" );
 
diff --git a/src/context/applets/analyzer/DiscoAnalyzer.h b/src/context/applets/analyzer/DiscoAnalyzer.h
index 3d54f77..d41b46c 100644
--- a/src/context/applets/analyzer/DiscoAnalyzer.h
+++ b/src/context/applets/analyzer/DiscoAnalyzer.h
@@ -23,7 +23,7 @@
 
 class QPaintEvent;
 
-class DiscoAnalyzer : public Analyzer::Base3D
+class DiscoAnalyzer : public Analyzer::Base
 {
 public:
     DiscoAnalyzer( QWidget * );


More information about the Amarok-devel mailing list