not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kwin / lib / kwinglutils.cpp
blob5595b723a8d9a2ab7b10ecf6a7d639a7c529932d
1 /********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
5 Copyright (C) 2006-2007 Rivo Laks <rivolaks@hot.ee>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *********************************************************************/
21 #include "kwinglutils.h"
23 #ifdef KWIN_HAVE_OPENGL
25 #include "kwinglobals.h"
26 #include "kwineffects.h"
28 #include "kdebug.h"
29 #include <kstandarddirs.h>
31 #include <QPixmap>
32 #include <QImage>
33 #include <QHash>
34 #include <QFile>
38 #define MAKE_GL_VERSION(major, minor, release) ( ((major) << 16) | ((minor) << 8) | (release) )
41 namespace KWin
43 // Variables
44 // GL version, use MAKE_GL_VERSION() macro for comparing with a specific version
45 static int glVersion;
46 // GLX version, use MAKE_GL_VERSION() macro for comparing with a specific version
47 static int glXVersion;
48 // List of all supported GL and GLX extensions
49 static QStringList glExtensions;
50 static QStringList glxExtensions;
52 int glTextureUnitsCount;
55 // Functions
56 void initGLX()
58 // Get GLX version
59 int major, minor;
60 glXQueryVersion( display(), &major, &minor );
61 glXVersion = MAKE_GL_VERSION( major, minor, 0 );
62 // Get list of supported GLX extensions
63 glxExtensions = QString((const char*)glXQueryExtensionsString(
64 display(), DefaultScreen( display()))).split(" ");
66 glxResolveFunctions();
69 void initGL()
71 // Get OpenGL version
72 QString glversionstring = QString((const char*)glGetString(GL_VERSION));
73 QStringList glversioninfo = glversionstring.left(glversionstring.indexOf(' ')).split('.');
74 glVersion = MAKE_GL_VERSION(glversioninfo[0].toInt(), glversioninfo[1].toInt(),
75 glversioninfo.count() > 2 ? glversioninfo[2].toInt() : 0);
76 // Get list of supported OpenGL extensions
77 glExtensions = QString((const char*)glGetString(GL_EXTENSIONS)).split(" ");
79 // handle OpenGL extensions functions
80 glResolveFunctions();
82 GLTexture::initStatic();
83 GLShader::initStatic();
84 GLRenderTarget::initStatic();
87 bool hasGLVersion(int major, int minor, int release)
89 return glVersion >= MAKE_GL_VERSION(major, minor, release);
92 bool hasGLXVersion(int major, int minor, int release)
94 return glXVersion >= MAKE_GL_VERSION(major, minor, release);
97 bool hasGLExtension(const QString& extension)
99 return glExtensions.contains(extension) || glxExtensions.contains(extension);
102 bool checkGLError( const char* txt )
104 GLenum err = glGetError();
105 if( err != GL_NO_ERROR )
107 kWarning(1212) << "GL error (" << txt << "): 0x" << QString::number( err, 16 ) ;
108 return true;
110 return false;
113 int nearestPowerOfTwo( int x )
115 // This method had been copied from Qt's nearest_gl_texture_size()
116 int n = 0, last = 0;
117 for (int s = 0; s < 32; ++s) {
118 if (((x>>s) & 1) == 1) {
119 ++n;
120 last = s;
123 if (n > 1)
124 return 1 << (last+1);
125 return 1 << last;
128 void renderGLGeometry( int count, const float* vertices, const float* texture, const float* color,
129 int dim, int stride )
131 return renderGLGeometry( infiniteRegion(), count, vertices, texture, color, dim, stride );
134 void renderGLGeometry( const QRegion& region, int count,
135 const float* vertices, const float* texture, const float* color,
136 int dim, int stride )
138 // Using arrays only makes sense if we have larger number of vertices.
139 // Otherwise overhead of enabling/disabling them is too big.
140 bool use_arrays = (count > 5);
142 if( use_arrays )
144 glPushAttrib( GL_ENABLE_BIT );
145 glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT );
146 // Enable arrays
147 glEnableClientState( GL_VERTEX_ARRAY );
148 glVertexPointer( dim, GL_FLOAT, stride, vertices );
149 if( texture != NULL )
151 glEnableClientState( GL_TEXTURE_COORD_ARRAY );
152 glTexCoordPointer( 2, GL_FLOAT, stride, texture );
154 if( color != NULL )
156 glEnableClientState( GL_COLOR_ARRAY );
157 glColorPointer( 4, GL_FLOAT, stride, color );
161 // Clip using scissoring
162 PaintClipper pc( region );
163 for( PaintClipper::Iterator iterator;
164 !iterator.isDone();
165 iterator.next())
167 if( use_arrays )
168 glDrawArrays( GL_QUADS, 0, count );
169 else
170 renderGLGeometryImmediate( count, vertices, texture, color, dim, stride );
173 if( use_arrays )
175 glPopClientAttrib();
176 glPopAttrib();
180 void renderGLGeometryImmediate( int count, const float* vertices, const float* texture, const float* color,
181 int dim, int stride )
183 // Find out correct glVertex*fv function according to dim parameter.
184 void ( *glVertexFunc )( const float* ) = glVertex2fv;
185 if( dim == 3 )
186 glVertexFunc = glVertex3fv;
187 else if( dim == 4 )
188 glVertexFunc = glVertex4fv;
190 // These are number of _floats_ per item, not _bytes_ per item as opengl uses.
191 int vsize, tsize, csize;
192 vsize = tsize = csize = stride / sizeof(float);
193 if( !stride )
195 // 0 means that arrays are tightly packed. This gives us different
196 // strides for different arrays
197 vsize = dim;
198 tsize = 2;
199 csize = 4;
202 glBegin( GL_QUADS );
203 // This sucks. But makes it faster.
204 if( texture && color )
206 for( int i = 0; i < count; i++ )
208 glTexCoord2fv( texture + i*tsize );
209 glColor4fv( color + i*csize );
210 glVertexFunc( vertices + i*vsize );
213 else if( texture )
215 for( int i = 0; i < count; i++ )
217 glTexCoord2fv( texture + i*tsize );
218 glVertexFunc( vertices + i*vsize );
221 else if( color )
223 for( int i = 0; i < count; i++ )
225 glColor4fv( color + i*csize );
226 glVertexFunc( vertices + i*vsize );
229 else
231 for( int i = 0; i < count; i++ )
232 glVertexFunc( vertices + i*vsize );
234 glEnd();
237 void addQuadVertices(QVector<float>& verts, float x1, float y1, float x2, float y2)
239 verts << x1 << y1;
240 verts << x1 << y2;
241 verts << x2 << y2;
242 verts << x2 << y1;
245 void renderRoundBox( const QRect& area, float roundness, GLTexture* texture )
247 static GLTexture* circleTexture = 0;
248 if( !texture && !circleTexture )
250 QString texturefile = KGlobal::dirs()->findResource("data", "kwin/circle.png");
251 circleTexture = new GLTexture(texturefile);
253 if( !texture )
255 texture = circleTexture;
258 glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT );
259 glEnable( GL_BLEND );
260 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
262 glPushMatrix();
264 QVector<float> verts, texcoords;
265 verts.reserve(80);
266 texcoords.reserve(80);
267 // center
268 addQuadVertices(verts, area.left() + roundness, area.top() + roundness, area.right() - roundness, area.bottom() - roundness);
269 addQuadVertices(texcoords, 0.5, 0.5, 0.5, 0.5);
270 // sides
271 // left
272 addQuadVertices(verts, area.left(), area.top() + roundness, area.left() + roundness, area.bottom() - roundness);
273 addQuadVertices(texcoords, 0.0, 0.5, 0.5, 0.5);
274 // top
275 addQuadVertices(verts, area.left() + roundness, area.top(), area.right() - roundness, area.top() + roundness);
276 addQuadVertices(texcoords, 0.5, 0.0, 0.5, 0.5);
277 // right
278 addQuadVertices(verts, area.right() - roundness, area.top() + roundness, area.right(), area.bottom() - roundness);
279 addQuadVertices(texcoords, 0.5, 0.5, 1.0, 0.5);
280 // bottom
281 addQuadVertices(verts, area.left() + roundness, area.bottom() - roundness, area.right() - roundness, area.bottom());
282 addQuadVertices(texcoords, 0.5, 0.5, 0.5, 1.0);
283 // corners
284 // top-left
285 addQuadVertices(verts, area.left(), area.top(), area.left() + roundness, area.top() + roundness);
286 addQuadVertices(texcoords, 0.0, 0.0, 0.5, 0.5);
287 // top-right
288 addQuadVertices(verts, area.right() - roundness, area.top(), area.right(), area.top() + roundness);
289 addQuadVertices(texcoords, 0.5, 0.0, 1.0, 0.5);
290 // bottom-left
291 addQuadVertices(verts, area.left(), area.bottom() - roundness, area.left() + roundness, area.bottom());
292 addQuadVertices(texcoords, 0.0, 0.5, 0.5, 1.0);
293 // bottom-right
294 addQuadVertices(verts, area.right() - roundness, area.bottom() - roundness, area.right(), area.bottom());
295 addQuadVertices(texcoords, 0.5, 0.5, 1.0, 1.0);
297 texture->bind();
298 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
299 // We have two elements per vertex in the verts array
300 int verticesCount = verts.count() / 2;
301 texture->enableNormalizedTexCoords();
302 renderGLGeometry( verticesCount, verts.data(), texcoords.data() );
303 texture->disableNormalizedTexCoords();
304 texture->unbind();
306 glPopMatrix();
307 glPopAttrib();
310 void renderRoundBoxWithEdge( const QRect& area, float roundness )
312 static GLTexture* texture = 0;
313 if( !texture )
315 QString texturefile = KGlobal::dirs()->findResource("data", "kwin/circle-edgy.png");
316 texture = new GLTexture(texturefile);
318 renderRoundBox( area, roundness, texture );
321 //****************************************
322 // GLTexture
323 //****************************************
325 bool GLTexture::mNPOTTextureSupported = false;
326 bool GLTexture::mFramebufferObjectSupported = false;
327 bool GLTexture::mSaturationSupported = false;
329 GLTexture::GLTexture()
331 init();
334 GLTexture::GLTexture( const QImage& image, GLenum target )
336 init();
337 load( image, target );
340 GLTexture::GLTexture( const QPixmap& pixmap, GLenum target )
342 init();
343 load( pixmap, target );
346 GLTexture::GLTexture( const QString& fileName )
348 init();
349 load( fileName );
352 GLTexture::GLTexture( int width, int height )
354 init();
356 if( NPOTTextureSupported() || ( isPowerOfTwo( width ) && isPowerOfTwo( height )))
358 mTarget = GL_TEXTURE_2D;
359 mScale.setWidth( 1.0 / width);
360 mScale.setHeight( 1.0 / height);
361 can_use_mipmaps = true;
363 glGenTextures( 1, &mTexture );
364 bind();
365 glTexImage2D( mTarget, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
366 unbind();
370 GLTexture::~GLTexture()
372 discard();
373 assert( mUnnormalizeActive == 0 );
374 assert( mNormalizeActive == 0 );
377 void GLTexture::init()
379 mTexture = None;
380 mTarget = 0;
381 mFilter = 0;
382 y_inverted = false;
383 can_use_mipmaps = false;
384 has_valid_mipmaps = false;
385 mUnnormalizeActive = 0;
386 mNormalizeActive = 0;
389 void GLTexture::initStatic()
391 mNPOTTextureSupported = hasGLExtension( "GL_ARB_texture_non_power_of_two" );
392 mFramebufferObjectSupported = hasGLExtension( "GL_EXT_framebuffer_object" );
393 mSaturationSupported = ((hasGLExtension("GL_ARB_texture_env_crossbar")
394 && hasGLExtension("GL_ARB_texture_env_dot3")) || hasGLVersion(1, 4))
395 && (glTextureUnitsCount >= 4) && glActiveTexture != NULL;
398 bool GLTexture::isNull() const
400 return mTexture == None;
403 QSize GLTexture::size() const
405 return mSize;
408 bool GLTexture::load( const QImage& image, GLenum target )
410 if( image.isNull())
411 return false;
412 QImage img = image;
413 mTarget = target;
414 if( mTarget != GL_TEXTURE_RECTANGLE_ARB )
416 if( !NPOTTextureSupported()
417 && ( !isPowerOfTwo( image.width()) || !isPowerOfTwo( image.height())))
418 { // non-rectangular target requires POT texture
419 img = img.scaled( nearestPowerOfTwo( image.width()),
420 nearestPowerOfTwo( image.height()));
422 mScale.setWidth( 1.0 / img.width());
423 mScale.setHeight( 1.0 / img.height());
424 can_use_mipmaps = true;
426 else
428 mScale.setWidth( 1.0 );
429 mScale.setHeight( 1.0 );
430 can_use_mipmaps = false;
432 setFilter( GL_LINEAR );
433 mSize = img.size();
434 y_inverted = false;
436 img = convertToGLFormat( img );
438 setDirty();
439 if( isNull())
440 glGenTextures( 1, &mTexture );
441 bind();
442 glTexImage2D( mTarget, 0, GL_RGBA, img.width(), img.height(), 0,
443 GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
444 unbind();
445 return true;
448 bool GLTexture::load( const QPixmap& pixmap, GLenum target )
450 if( pixmap.isNull())
451 return false;
452 return load( pixmap.toImage(), target );
455 bool GLTexture::load( const QString& fileName )
457 if( fileName.isEmpty())
458 return false;
459 return load( QImage( fileName ));
462 void GLTexture::discard()
464 setDirty();
465 if( mTexture != None )
466 glDeleteTextures( 1, &mTexture );
467 mTexture = None;
470 void GLTexture::bind()
472 glEnable( mTarget );
473 glBindTexture( mTarget, mTexture );
474 enableFilter();
477 void GLTexture::unbind()
479 glBindTexture( mTarget, 0 );
480 glDisable( mTarget );
483 void GLTexture::render( QRegion region, const QRect& rect )
485 const float verts[ 4 * 2 ] =
487 rect.x(), rect.y(),
488 rect.x(), rect.y() + rect.height(),
489 rect.x() + rect.width(), rect.y() + rect.height(),
490 rect.x() + rect.width(), rect.y()
492 const float texcoords[ 4 * 2 ] =
494 0, 1, // y needs to be swapped (normalized coords)
495 0, 0,
496 1, 0,
497 1, 1
499 enableNormalizedTexCoords();
500 renderGLGeometry( region, 4, verts, texcoords );
501 disableNormalizedTexCoords();
504 void GLTexture::enableUnnormalizedTexCoords()
506 assert( mNormalizeActive == 0 );
507 if( mUnnormalizeActive++ != 0 )
508 return;
509 // update texture matrix to handle GL_TEXTURE_2D and GL_TEXTURE_RECTANGLE
510 glMatrixMode( GL_TEXTURE );
511 glPushMatrix();
512 glLoadIdentity();
513 glScalef( mScale.width(), mScale.height(), 1 );
514 if( !y_inverted )
516 // Modify texture matrix so that we could always use non-opengl
517 // coordinates for textures
518 glScalef( 1, -1, 1 );
519 glTranslatef( 0, -mSize.height(), 0 );
521 glMatrixMode( GL_MODELVIEW );
524 void GLTexture::disableUnnormalizedTexCoords()
526 if( --mUnnormalizeActive != 0 )
527 return;
528 // Restore texture matrix
529 glMatrixMode( GL_TEXTURE );
530 glPopMatrix();
531 glMatrixMode( GL_MODELVIEW );
534 void GLTexture::enableNormalizedTexCoords()
536 assert( mUnnormalizeActive == 0 );
537 if( mNormalizeActive++ != 0 )
538 return;
539 // update texture matrix to handle GL_TEXTURE_2D and GL_TEXTURE_RECTANGLE
540 glMatrixMode( GL_TEXTURE );
541 glPushMatrix();
542 glLoadIdentity();
543 glScalef( mSize.width() * mScale.width(), mSize.height() * mScale.height(), 1 );
544 if( y_inverted )
546 // Modify texture matrix so that we could always use non-opengl
547 // coordinates for textures
548 glScalef( 1, -1, 1 );
549 glTranslatef( 0, -1, 0 );
551 glMatrixMode( GL_MODELVIEW );
554 void GLTexture::disableNormalizedTexCoords()
556 if( --mNormalizeActive != 0 )
557 return;
558 // Restore texture matrix
559 glMatrixMode( GL_TEXTURE );
560 glPopMatrix();
561 glMatrixMode( GL_MODELVIEW );
564 GLuint GLTexture::texture() const
566 return mTexture;
569 GLenum GLTexture::target() const
571 return mTarget;
574 GLenum GLTexture::filter() const
576 return mFilter;
579 bool GLTexture::isDirty() const
581 return has_valid_mipmaps;
584 void GLTexture::setTexture( GLuint texture )
586 discard();
587 mTexture = texture;
590 void GLTexture::setTarget( GLenum target )
592 mTarget = target;
595 void GLTexture::setFilter( GLenum filter )
597 mFilter = filter;
600 void GLTexture::setWrapMode( GLenum mode )
602 bind();
603 glTexParameteri( mTarget, GL_TEXTURE_WRAP_S, mode );
604 glTexParameteri( mTarget, GL_TEXTURE_WRAP_T, mode );
605 unbind();
608 void GLTexture::setDirty()
610 has_valid_mipmaps = false;
614 void GLTexture::enableFilter()
616 if( mFilter == GL_LINEAR_MIPMAP_LINEAR )
617 { // trilinear filtering requested, but is it possible?
618 if( NPOTTextureSupported()
619 && framebufferObjectSupported()
620 && can_use_mipmaps )
622 glTexParameteri( mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
623 glTexParameteri( mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
624 if( !has_valid_mipmaps )
626 glGenerateMipmap( mTarget );
627 has_valid_mipmaps = true;
630 else
631 { // can't use trilinear, so use bilinear
632 setFilter( GL_LINEAR );
633 glTexParameteri( mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
634 glTexParameteri( mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
637 else if( mFilter == GL_LINEAR )
639 glTexParameteri( mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
640 glTexParameteri( mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
642 else
643 { // if neither trilinear nor bilinear, default to fast filtering
644 setFilter( GL_NEAREST );
645 glTexParameteri( mTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
646 glTexParameteri( mTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
650 QImage GLTexture::convertToGLFormat( const QImage& img ) const
652 // This method has been copied from Qt's QGLWidget::convertToGLFormat()
653 QImage res = img.convertToFormat(QImage::Format_ARGB32);
654 res = res.mirrored();
656 if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
657 // Qt has ARGB; OpenGL wants RGBA
658 for (int i=0; i < res.height(); i++) {
659 uint *p = (uint*)res.scanLine(i);
660 uint *end = p + res.width();
661 while (p < end) {
662 *p = (*p << 8) | ((*p >> 24) & 0xFF);
663 p++;
667 else {
668 // Qt has ARGB; OpenGL wants ABGR (i.e. RGBA backwards)
669 res = res.rgbSwapped();
671 return res;
674 //****************************************
675 // GLShader
676 //****************************************
678 bool GLShader::mFragmentShaderSupported = false;
679 bool GLShader::mVertexShaderSupported = false;
681 void GLShader::initStatic()
683 mFragmentShaderSupported = mVertexShaderSupported =
684 hasGLExtension("GL_ARB_shader_objects") && hasGLExtension("GL_ARB_shading_language_100");
685 mVertexShaderSupported &= hasGLExtension("GL_ARB_vertex_shader");
686 mFragmentShaderSupported &= hasGLExtension("GL_ARB_fragment_shader");
690 GLShader::GLShader(const QString& vertexfile, const QString& fragmentfile)
692 mValid = false;
693 mVariableLocations = 0;
694 mProgram = 0;
696 loadFromFiles(vertexfile, fragmentfile);
699 GLShader::~GLShader()
701 if(mVariableLocations)
703 mVariableLocations->clear();
704 delete mVariableLocations;
707 if(mProgram)
709 glDeleteProgram(mProgram);
713 bool GLShader::loadFromFiles(const QString& vertexfile, const QString& fragmentfile)
715 QFile vf(vertexfile);
716 if(!vf.open(QIODevice::ReadOnly))
718 kError(1212) << "Couldn't open '" << vertexfile << "' for reading!" << endl;
719 return false;
721 QString vertexsource(vf.readAll());
723 QFile ff(fragmentfile);
724 if(!ff.open(QIODevice::ReadOnly))
726 kError(1212) << "Couldn't open '" << fragmentfile << "' for reading!" << endl;
727 return false;
729 QString fragsource(ff.readAll());
731 return load(vertexsource, fragsource);
734 bool GLShader::load(const QString& vertexsource, const QString& fragmentsource)
736 // Make sure shaders are actually supported
737 if(( !vertexsource.isEmpty() && !vertexShaderSupported() ) ||
738 ( !fragmentsource.isEmpty() && !fragmentShaderSupported() ))
740 kDebug(1212) << "Shaders not supported";
741 return false;
744 GLuint vertexshader;
745 GLuint fragmentshader;
747 GLsizei logsize, logarraysize;
748 char* log = 0;
750 // Create program object
751 mProgram = glCreateProgram();
752 if(!vertexsource.isEmpty())
754 // Create shader object
755 vertexshader = glCreateShader(GL_VERTEX_SHADER);
756 // Load it
757 const QByteArray& srcba = vertexsource.toLatin1();
758 const char* src = srcba.data();
759 glShaderSource(vertexshader, 1, &src, NULL);
760 // Compile the shader
761 glCompileShader(vertexshader);
762 // Make sure it compiled correctly
763 int compiled;
764 glGetShaderiv(vertexshader, GL_COMPILE_STATUS, &compiled);
765 // Get info log
766 glGetShaderiv(vertexshader, GL_INFO_LOG_LENGTH, &logarraysize);
767 log = new char[logarraysize];
768 glGetShaderInfoLog(vertexshader, logarraysize, &logsize, log);
769 if(!compiled)
771 kError(1212) << "Couldn't compile vertex shader! Log:" << endl << log << endl;
772 delete[] log;
773 return false;
775 else if(logsize > 0)
776 kDebug(1212) << "Vertex shader compilation log:"<< log;
777 // Attach the shader to the program
778 glAttachShader(mProgram, vertexshader);
779 // Delete shader
780 glDeleteShader(vertexshader);
781 delete[] log;
785 if(!fragmentsource.isEmpty())
787 fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
788 // Load it
789 const QByteArray& srcba = fragmentsource.toLatin1();
790 const char* src = srcba.data();
791 glShaderSource(fragmentshader, 1, &src, NULL);
792 //glShaderSource(fragmentshader, 1, &fragmentsrc.latin1(), NULL);
793 // Compile the shader
794 glCompileShader(fragmentshader);
795 // Make sure it compiled correctly
796 int compiled;
797 glGetShaderiv(fragmentshader, GL_COMPILE_STATUS, &compiled);
798 // Get info log
799 glGetShaderiv(fragmentshader, GL_INFO_LOG_LENGTH, &logarraysize);
800 log = new char[logarraysize];
801 glGetShaderInfoLog(fragmentshader, logarraysize, &logsize, log);
802 if(!compiled)
804 kError(1212) << "Couldn't compile fragment shader! Log:" << endl << log << endl;
805 delete[] log;
806 return false;
808 else if(logsize > 0)
809 kDebug(1212) << "Fragment shader compilation log:"<< log;
810 // Attach the shader to the program
811 glAttachShader(mProgram, fragmentshader);
812 // Delete shader
813 glDeleteShader(fragmentshader);
814 delete[] log;
818 // Link the program
819 glLinkProgram(mProgram);
820 // Make sure it linked correctly
821 int linked;
822 glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
823 // Get info log
824 glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &logarraysize);
825 log = new char[logarraysize];
826 glGetProgramInfoLog(mProgram, logarraysize, &logsize, log);
827 if(!linked)
829 kError(1212) << "Couldn't link the program! Log" << endl << log << endl;
830 delete[] log;
831 return false;
833 else if(logsize > 0)
834 kDebug(1212) << "Shader linking log:"<< log;
835 delete[] log;
837 mVariableLocations = new QHash<QString, int>;
839 mValid = true;
840 return true;
843 void GLShader::bind()
845 glUseProgram(mProgram);
848 void GLShader::unbind()
850 glUseProgram(0);
853 int GLShader::uniformLocation(const QString& name)
855 if(!mVariableLocations)
857 return -1;
859 if(!mVariableLocations->contains(name))
861 int location = glGetUniformLocation(mProgram, name.toLatin1().data());
862 mVariableLocations->insert(name, location);
864 return mVariableLocations->value(name);
867 bool GLShader::setUniform(const QString& name, float value)
869 int location = uniformLocation(name);
870 if(location >= 0)
872 glUniform1f(location, value);
874 return (location >= 0);
877 bool GLShader::setUniform(const QString& name, int value)
879 int location = uniformLocation(name);
880 if(location >= 0)
882 glUniform1i(location, value);
884 return (location >= 0);
887 int GLShader::attributeLocation(const QString& name)
889 if(!mVariableLocations)
891 return -1;
893 if(!mVariableLocations->contains(name))
895 int location = glGetAttribLocation(mProgram, name.toLatin1().data());
896 mVariableLocations->insert(name, location);
898 return mVariableLocations->value(name);
901 bool GLShader::setAttribute(const QString& name, float value)
903 int location = attributeLocation(name);
904 if(location >= 0)
906 glVertexAttrib1f(location, value);
908 return (location >= 0);
913 /*** GLRenderTarget ***/
914 bool GLRenderTarget::mSupported = false;
916 void GLRenderTarget::initStatic()
918 mSupported = hasGLExtension("GL_EXT_framebuffer_object") && glFramebufferTexture2D;
921 GLRenderTarget::GLRenderTarget(GLTexture* color)
923 // Reset variables
924 mValid = false;
926 mTexture = color;
928 // Make sure FBO is supported
929 if(mSupported && mTexture && !mTexture->isNull())
931 initFBO();
933 else
934 kError(1212) << "Render targets aren't supported!" << endl;
937 GLRenderTarget::~GLRenderTarget()
939 if(mValid)
941 glDeleteFramebuffers(1, &mFramebuffer);
945 bool GLRenderTarget::enable()
947 if(!valid())
949 kError(1212) << "Can't enable invalid render target!" << endl;
950 return false;
953 glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFramebuffer);
954 mTexture->setDirty();
956 return true;
959 bool GLRenderTarget::disable()
961 if(!valid())
963 kError(1212) << "Can't disable invalid render target!" << endl;
964 return false;
967 glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
968 mTexture->setDirty();
970 return true;
973 void GLRenderTarget::initFBO()
975 glGenFramebuffers(1, &mFramebuffer);
976 glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFramebuffer);
978 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
979 mTexture->target(), mTexture->texture(), 0);
981 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
982 if(status != GL_FRAMEBUFFER_COMPLETE_EXT)
984 kError(1212) << "Invalid fb status: " << status << endl;
987 glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
989 mValid = true;
992 } // namespace
994 #endif