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"
29 #include <kstandarddirs.h>
38 #define MAKE_GL_VERSION(major, minor, release) ( ((major) << 16) | ((minor) << 8) | (release) )
44 // GL version, use MAKE_GL_VERSION() macro for comparing with a specific version
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
;
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();
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
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() << "GL error (" << txt
<< "): 0x" << QString::number( err
, 16 ) ;
113 int nearestPowerOfTwo( int x
)
115 // This method had been copied from Qt's nearest_gl_texture_size()
117 for (int s
= 0; s
< 32; ++s
) {
118 if (((x
>>s
) & 1) == 1) {
124 return 1 << (last
+1);
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);
144 glPushAttrib( GL_ENABLE_BIT
);
145 glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT
);
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
);
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
;
168 glDrawArrays( GL_QUADS
, 0, count
);
170 renderGLGeometryImmediate( count
, vertices
, texture
, color
, dim
, stride
);
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
;
186 glVertexFunc
= glVertex3fv
;
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);
195 // 0 means that arrays are tightly packed. This gives us different
196 // strides for different arrays
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
);
215 for( int i
= 0; i
< count
; i
++ )
217 glTexCoord2fv( texture
+ i
*tsize
);
218 glVertexFunc( vertices
+ i
*vsize
);
223 for( int i
= 0; i
< count
; i
++ )
225 glColor4fv( color
+ i
*csize
);
226 glVertexFunc( vertices
+ i
*vsize
);
231 for( int i
= 0; i
< count
; i
++ )
232 glVertexFunc( vertices
+ i
*vsize
);
237 void addQuadVertices(QVector
<float>& verts
, float x1
, float y1
, float x2
, float y2
)
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
);
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
);
264 QVector
<float> verts
, texcoords
;
266 texcoords
.reserve(80);
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);
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);
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);
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);
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);
285 addQuadVertices(verts
, area
.left(), area
.top(), area
.left() + roundness
, area
.top() + roundness
);
286 addQuadVertices(texcoords
, 0.0, 0.0, 0.5, 0.5);
288 addQuadVertices(verts
, area
.right() - roundness
, area
.top(), area
.right(), area
.top() + roundness
);
289 addQuadVertices(texcoords
, 0.5, 0.0, 1.0, 0.5);
291 addQuadVertices(verts
, area
.left(), area
.bottom() - roundness
, area
.left() + roundness
, area
.bottom());
292 addQuadVertices(texcoords
, 0.0, 0.5, 0.5, 1.0);
294 addQuadVertices(verts
, area
.right() - roundness
, area
.bottom() - roundness
, area
.right(), area
.bottom());
295 addQuadVertices(texcoords
, 0.5, 0.5, 1.0, 1.0);
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 renderGLGeometry( verticesCount
, verts
.data(), texcoords
.data() );
308 void renderRoundBoxWithEdge( const QRect
& area
, float roundness
)
310 static GLTexture
* texture
= 0;
313 QString texturefile
= KGlobal::dirs()->findResource("data", "kwin/circle-edgy.png");
314 texture
= new GLTexture(texturefile
);
316 renderRoundBox( area
, roundness
, texture
);
319 //****************************************
321 //****************************************
323 bool GLTexture::mNPOTTextureSupported
= false;
324 bool GLTexture::mFramebufferObjectSupported
= false;
325 bool GLTexture::mSaturationSupported
= false;
327 GLTexture::GLTexture()
332 GLTexture::GLTexture( const QImage
& image
, GLenum target
)
335 load( image
, target
);
338 GLTexture::GLTexture( const QPixmap
& pixmap
, GLenum target
)
341 load( pixmap
, target
);
344 GLTexture::GLTexture( const QString
& fileName
)
350 GLTexture::GLTexture( int width
, int height
)
354 if( NPOTTextureSupported() || ( isPowerOfTwo( width
) && isPowerOfTwo( height
)))
356 mTarget
= GL_TEXTURE_2D
;
357 mScale
.setWidth( 1.0 / width
);
358 mScale
.setHeight( 1.0 / height
);
359 can_use_mipmaps
= true;
361 glGenTextures( 1, &mTexture
);
363 glTexImage2D( mTarget
, 0, GL_RGBA
, width
, height
, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, 0);
368 GLTexture::~GLTexture()
373 void GLTexture::init()
379 can_use_mipmaps
= false;
380 has_valid_mipmaps
= false;
383 void GLTexture::initStatic()
385 mNPOTTextureSupported
= hasGLExtension( "GL_ARB_texture_non_power_of_two" );
386 mFramebufferObjectSupported
= hasGLExtension( "GL_EXT_framebuffer_object" );
387 mSaturationSupported
= ((hasGLExtension("GL_ARB_texture_env_crossbar")
388 && hasGLExtension("GL_ARB_texture_env_dot3")) || hasGLVersion(1, 4))
389 && (glTextureUnitsCount
>= 4) && glActiveTexture
!= NULL
;
392 bool GLTexture::isNull() const
394 return mTexture
== None
;
397 bool GLTexture::load( const QImage
& image
, GLenum target
)
403 if( mTarget
!= GL_TEXTURE_RECTANGLE_ARB
)
405 if( !NPOTTextureSupported()
406 && ( !isPowerOfTwo( image
.width()) || !isPowerOfTwo( image
.height())))
407 { // non-rectangular target requires POT texture
408 img
= img
.scaled( nearestPowerOfTwo( image
.width()),
409 nearestPowerOfTwo( image
.height()));
411 mScale
.setWidth( 1.0 / img
.width());
412 mScale
.setHeight( 1.0 / img
.height());
413 can_use_mipmaps
= true;
417 mScale
.setWidth( 1.0 );
418 mScale
.setHeight( 1.0 );
419 can_use_mipmaps
= false;
421 setFilter( GL_LINEAR
);
425 img
= convertToGLFormat( img
);
429 glGenTextures( 1, &mTexture
);
431 glTexImage2D( mTarget
, 0, GL_RGBA
, img
.width(), img
.height(), 0,
432 GL_RGBA
, GL_UNSIGNED_BYTE
, img
.bits());
437 bool GLTexture::load( const QPixmap
& pixmap
, GLenum target
)
441 return load( pixmap
.toImage(), target
);
444 bool GLTexture::load( const QString
& fileName
)
446 if( fileName
.isEmpty())
448 return load( QImage( fileName
));
451 void GLTexture::discard()
454 if( mTexture
!= None
)
455 glDeleteTextures( 1, &mTexture
);
459 void GLTexture::bind()
462 glBindTexture( mTarget
, mTexture
);
466 void GLTexture::unbind()
468 glBindTexture( mTarget
, 0 );
469 glDisable( mTarget
);
472 void GLTexture::render( QRegion region
, const QRect
& rect
)
474 const float verts
[ 4 * 2 ] =
477 rect
.x(), rect
.y() + rect
.height(),
478 rect
.x() + rect
.width(), rect
.y() + rect
.height(),
479 rect
.x() + rect
.width(), rect
.y()
481 const float texcoords
[ 4 * 2 ] =
488 renderGLGeometry( region
, 4, verts
, texcoords
);
491 void GLTexture::enableUnnormalizedTexCoords()
493 // update texture matrix to handle GL_TEXTURE_2D and GL_TEXTURE_RECTANGLE
494 glMatrixMode( GL_TEXTURE
);
497 glScalef( mScale
.width(), mScale
.height(), 1 );
500 // Modify texture matrix so that we could always use non-opengl
501 // coordinates for textures
502 glScalef( 1, -1, 1 );
503 glTranslatef( 0, -mSize
.height(), 0 );
505 glMatrixMode( GL_MODELVIEW
);
508 void GLTexture::disableUnnormalizedTexCoords()
510 // Restore texture matrix
511 glMatrixMode( GL_TEXTURE
);
513 glMatrixMode( GL_MODELVIEW
);
516 GLuint
GLTexture::texture() const
521 GLenum
GLTexture::target() const
526 GLenum
GLTexture::filter() const
531 bool GLTexture::isDirty() const
533 return has_valid_mipmaps
;
536 void GLTexture::setTexture( GLuint texture
)
542 void GLTexture::setTarget( GLenum target
)
547 void GLTexture::setFilter( GLenum filter
)
552 void GLTexture::setWrapMode( GLenum mode
)
555 glTexParameteri( mTarget
, GL_TEXTURE_WRAP_S
, mode
);
556 glTexParameteri( mTarget
, GL_TEXTURE_WRAP_T
, mode
);
560 void GLTexture::setDirty()
562 has_valid_mipmaps
= false;
566 void GLTexture::enableFilter()
568 if( mFilter
== GL_LINEAR_MIPMAP_LINEAR
)
569 { // trilinear filtering requested, but is it possible?
570 if( NPOTTextureSupported()
571 && framebufferObjectSupported()
574 glTexParameteri( mTarget
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR_MIPMAP_LINEAR
);
575 glTexParameteri( mTarget
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
576 if( !has_valid_mipmaps
)
578 glGenerateMipmap( mTarget
);
579 has_valid_mipmaps
= true;
583 { // can't use trilinear, so use bilinear
584 setFilter( GL_LINEAR
);
585 glTexParameteri( mTarget
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
586 glTexParameteri( mTarget
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
589 else if( mFilter
== GL_LINEAR
)
591 glTexParameteri( mTarget
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
592 glTexParameteri( mTarget
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
595 { // if neither trilinear nor bilinear, default to fast filtering
596 setFilter( GL_NEAREST
);
597 glTexParameteri( mTarget
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
598 glTexParameteri( mTarget
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
602 QImage
GLTexture::convertToGLFormat( const QImage
& img
) const
604 // This method has been copied from Qt's QGLWidget::convertToGLFormat()
605 QImage res
= img
.convertToFormat(QImage::Format_ARGB32
);
606 res
= res
.mirrored();
608 if (QSysInfo::ByteOrder
== QSysInfo::BigEndian
) {
609 // Qt has ARGB; OpenGL wants RGBA
610 for (int i
=0; i
< res
.height(); i
++) {
611 uint
*p
= (uint
*)res
.scanLine(i
);
612 uint
*end
= p
+ res
.width();
614 *p
= (*p
<< 8) | ((*p
>> 24) & 0xFF);
620 // Qt has ARGB; OpenGL wants ABGR (i.e. RGBA backwards)
621 res
= res
.rgbSwapped();
626 //****************************************
628 //****************************************
630 bool GLShader::mFragmentShaderSupported
= false;
631 bool GLShader::mVertexShaderSupported
= false;
633 void GLShader::initStatic()
635 mFragmentShaderSupported
= mVertexShaderSupported
=
636 hasGLExtension("GL_ARB_shader_objects") && hasGLExtension("GL_ARB_shading_language_100");
637 mVertexShaderSupported
&= hasGLExtension("GL_ARB_vertex_shader");
638 mFragmentShaderSupported
&= hasGLExtension("GL_ARB_fragment_shader");
642 GLShader::GLShader(const QString
& vertexfile
, const QString
& fragmentfile
)
645 mVariableLocations
= 0;
648 loadFromFiles(vertexfile
, fragmentfile
);
651 GLShader::~GLShader()
653 if(mVariableLocations
)
655 mVariableLocations
->clear();
656 delete mVariableLocations
;
661 glDeleteProgram(mProgram
);
665 bool GLShader::loadFromFiles(const QString
& vertexfile
, const QString
& fragmentfile
)
667 QFile
vf(vertexfile
);
668 if(!vf
.open(QIODevice::ReadOnly
))
670 kError(1212) << "Couldn't open '" << vertexfile
<< "' for reading!" << endl
;
673 QString
vertexsource(vf
.readAll());
675 QFile
ff(fragmentfile
);
676 if(!ff
.open(QIODevice::ReadOnly
))
678 kError(1212) << "Couldn't open '" << fragmentfile
<< "' for reading!" << endl
;
681 QString
fragsource(ff
.readAll());
683 return load(vertexsource
, fragsource
);
686 bool GLShader::load(const QString
& vertexsource
, const QString
& fragmentsource
)
688 // Make sure shaders are actually supported
689 if(( !vertexsource
.isEmpty() && !vertexShaderSupported() ) ||
690 ( !fragmentsource
.isEmpty() && !fragmentShaderSupported() ))
692 kDebug(1212) << "Shaders not supported";
697 GLuint fragmentshader
;
699 GLsizei logsize
, logarraysize
;
702 // Create program object
703 mProgram
= glCreateProgram();
704 if(!vertexsource
.isEmpty())
706 // Create shader object
707 vertexshader
= glCreateShader(GL_VERTEX_SHADER
);
709 const QByteArray
& srcba
= vertexsource
.toLatin1();
710 const char* src
= srcba
.data();
711 glShaderSource(vertexshader
, 1, &src
, NULL
);
712 // Compile the shader
713 glCompileShader(vertexshader
);
714 // Make sure it compiled correctly
716 glGetShaderiv(vertexshader
, GL_COMPILE_STATUS
, &compiled
);
718 glGetShaderiv(vertexshader
, GL_INFO_LOG_LENGTH
, &logarraysize
);
719 log
= new char[logarraysize
];
720 glGetShaderInfoLog(vertexshader
, logarraysize
, &logsize
, log
);
723 kError(1212) << "Couldn't compile vertex shader! Log:" << endl
<< log
<< endl
;
728 kDebug(1212) << "Vertex shader compilation log:"<< log
;
729 // Attach the shader to the program
730 glAttachShader(mProgram
, vertexshader
);
732 glDeleteShader(vertexshader
);
737 if(!fragmentsource
.isEmpty())
739 fragmentshader
= glCreateShader(GL_FRAGMENT_SHADER
);
741 const QByteArray
& srcba
= fragmentsource
.toLatin1();
742 const char* src
= srcba
.data();
743 glShaderSource(fragmentshader
, 1, &src
, NULL
);
744 //glShaderSource(fragmentshader, 1, &fragmentsrc.latin1(), NULL);
745 // Compile the shader
746 glCompileShader(fragmentshader
);
747 // Make sure it compiled correctly
749 glGetShaderiv(fragmentshader
, GL_COMPILE_STATUS
, &compiled
);
751 glGetShaderiv(fragmentshader
, GL_INFO_LOG_LENGTH
, &logarraysize
);
752 log
= new char[logarraysize
];
753 glGetShaderInfoLog(fragmentshader
, logarraysize
, &logsize
, log
);
756 kError(1212) << "Couldn't compile fragment shader! Log:" << endl
<< log
<< endl
;
761 kDebug(1212) << "Fragment shader compilation log:"<< log
;
762 // Attach the shader to the program
763 glAttachShader(mProgram
, fragmentshader
);
765 glDeleteShader(fragmentshader
);
771 glLinkProgram(mProgram
);
772 // Make sure it linked correctly
774 glGetProgramiv(mProgram
, GL_LINK_STATUS
, &linked
);
776 glGetProgramiv(mProgram
, GL_INFO_LOG_LENGTH
, &logarraysize
);
777 log
= new char[logarraysize
];
778 glGetProgramInfoLog(mProgram
, logarraysize
, &logsize
, log
);
781 kError(1212) << "Couldn't link the program! Log" << endl
<< log
<< endl
;
786 kDebug(1212) << "Shader linking log:"<< log
;
789 mVariableLocations
= new QHash
<QString
, int>;
795 void GLShader::bind()
797 glUseProgram(mProgram
);
800 void GLShader::unbind()
805 int GLShader::uniformLocation(const QString
& name
)
807 if(!mVariableLocations
)
811 if(!mVariableLocations
->contains(name
))
813 int location
= glGetUniformLocation(mProgram
, name
.toLatin1().data());
814 mVariableLocations
->insert(name
, location
);
816 return mVariableLocations
->value(name
);
819 bool GLShader::setUniform(const QString
& name
, float value
)
821 int location
= uniformLocation(name
);
824 glUniform1f(location
, value
);
826 return (location
>= 0);
829 bool GLShader::setUniform(const QString
& name
, int value
)
831 int location
= uniformLocation(name
);
834 glUniform1i(location
, value
);
836 return (location
>= 0);
839 int GLShader::attributeLocation(const QString
& name
)
841 if(!mVariableLocations
)
845 if(!mVariableLocations
->contains(name
))
847 int location
= glGetAttribLocation(mProgram
, name
.toLatin1().data());
848 mVariableLocations
->insert(name
, location
);
850 return mVariableLocations
->value(name
);
853 bool GLShader::setAttribute(const QString
& name
, float value
)
855 int location
= attributeLocation(name
);
858 glVertexAttrib1f(location
, value
);
860 return (location
>= 0);
865 /*** GLRenderTarget ***/
866 bool GLRenderTarget::mSupported
= false;
868 void GLRenderTarget::initStatic()
870 mSupported
= hasGLExtension("GL_EXT_framebuffer_object") && glFramebufferTexture2D
;
873 GLRenderTarget::GLRenderTarget(GLTexture
* color
)
880 // Make sure FBO is supported
881 if(mSupported
&& mTexture
&& !mTexture
->isNull())
886 kError(1212) << "Render targets aren't supported!" << endl
;
889 GLRenderTarget::~GLRenderTarget()
893 glDeleteFramebuffers(1, &mFramebuffer
);
897 bool GLRenderTarget::enable()
901 kError(1212) << "Can't enable invalid render target!" << endl
;
905 glBindFramebuffer(GL_FRAMEBUFFER_EXT
, mFramebuffer
);
906 mTexture
->setDirty();
911 bool GLRenderTarget::disable()
915 kError(1212) << "Can't disable invalid render target!" << endl
;
919 glBindFramebuffer(GL_FRAMEBUFFER_EXT
, 0);
920 mTexture
->setDirty();
925 void GLRenderTarget::initFBO()
927 glGenFramebuffers(1, &mFramebuffer
);
928 glBindFramebuffer(GL_FRAMEBUFFER_EXT
, mFramebuffer
);
930 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT
, GL_COLOR_ATTACHMENT0_EXT
,
931 mTexture
->target(), mTexture
->texture(), 0);
933 GLenum status
= glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT
);
934 if(status
!= GL_FRAMEBUFFER_COMPLETE_EXT
)
936 kError(1212) << "Invalid fb status: " << status
<< endl
;
939 glBindFramebuffer(GL_FRAMEBUFFER_EXT
, 0);