Commit 351d723d authored by Mark Kretschmann's avatar Mark Kretschmann Committed by Vedant Agarwala
Browse files

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@kde.org
CCBUG: 323635
BACKPORT
parent c3459d6a
......@@ -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@meerkoetter.org>. (BR 283022)
* Fixed issue with Amarok sometimes not finding its plugins after an upgrade.
......
......@@ -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 );
}
......
......@@ -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
......
......@@ -118,7 +118,7 @@ private:
BallsAnalyzer::BallsAnalyzer( QWidget *parent ):
Analyzer::Base3D( parent )
Analyzer::Base( parent )
{
setObjectName( "Balls" );
......
......@@ -24,7 +24,7 @@ class QWidget;
class Ball;
class Paddle;
class BallsAnalyzer : public Analyzer::Base3D
class BallsAnalyzer : public Analyzer::Base
{
public:
BallsAnalyzer( QWidget * );
......
......@@ -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 );
}
......@@ -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
};
......
......@@ -25,7 +25,7 @@
DiscoAnalyzer::DiscoAnalyzer( QWidget *parent ):
Analyzer::Base3D( parent )
Analyzer::Base( parent )
{
setObjectName( "Disco" );
......
......@@ -23,7 +23,7 @@
class QPaintEvent;
class DiscoAnalyzer : public Analyzer::Base3D
class DiscoAnalyzer : public Analyzer::Base
{
public:
DiscoAnalyzer( QWidget * );
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment