kwinglutils.cpp 50.8 KB
Newer Older
1
/********************************************************************
2
3
4
5
6
 KWin - the KDE window manager
 This file is part of the KDE project.

Copyright (C) 2006-2007 Rivo Laks <rivolaks@hot.ee>

7
8
9
10
11
12
13
14
15
16
17
18
19
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
20
21
22

#include "kwinglutils.h"

23
#ifdef KWIN_HAVE_OPENGL
24
25
26
27
#include "kwinglobals.h"
#include "kwineffects.h"

#include "kdebug.h"
28
#include <kstandarddirs.h>
29
30
31
32
33

#include <QPixmap>
#include <QImage>
#include <QHash>
#include <QFile>
34
35
36
#include <QVector2D>
#include <QVector3D>
#include <QVector4D>
37
#include <QMatrix4x4>
38

Martin Flöser's avatar
Martin Flöser committed
39
#include <math.h>
40

41
#define DEBUG_GLRENDERTARGET 0
42
43
44
45
46
47
48
49
50
51

#define MAKE_GL_VERSION(major, minor, release)  ( ((major) << 16) | ((minor) << 8) | (release) )

namespace KWin
{
// Variables
// GL version, use MAKE_GL_VERSION() macro for comparing with a specific version
static int glVersion;
// GLX version, use MAKE_GL_VERSION() macro for comparing with a specific version
static int glXVersion;
52
53
54
// EGL version, use MAKE_GL_VERSION() macro for comparing with a specific version
static int eglVersion;
// List of all supported GL, EGL and GLX extensions
55
static QStringList glExtensions;
56
static QStringList glxExtensions;
57
static QStringList eglExtension;
58
59
60
61
62
63
64

int glTextureUnitsCount;


// Functions
void initGLX()
    {
65
#ifndef KWIN_HAVE_OPENGLES
66
67
68
69
    // Get GLX version
    int major, minor;
    glXQueryVersion( display(), &major, &minor );
    glXVersion = MAKE_GL_VERSION( major, minor, 0 );
70
71
    // Get list of supported GLX extensions
    glxExtensions = QString((const char*)glXQueryExtensionsString(
Lucas Murray's avatar
Lucas Murray committed
72
        display(), DefaultScreen( display()))).split(' ');
73
74

    glxResolveFunctions();
75
#endif
76
77
    }

78
79
80
81
82
83
84
85
void initEGL()
{
#ifdef KWIN_HAVE_OPENGLES
    EGLDisplay dpy = eglGetCurrentDisplay();
    int major, minor;
    eglInitialize(dpy, &major, &minor);
    eglVersion = MAKE_GL_VERSION(major, minor, 0);
    eglExtension = QString((const char*)eglQueryString(dpy, EGL_EXTENSIONS)).split(' ');
86
87

    eglResolveFunctions();
88
89
90
#endif
}

91
92
93
94
95
void initGL()
    {
    // Get OpenGL version
    QString glversionstring = QString((const char*)glGetString(GL_VERSION));
    QStringList glversioninfo = glversionstring.left(glversionstring.indexOf(' ')).split('.');
Martin Gräßlin's avatar
Martin Gräßlin committed
96
#ifndef KWIN_HAVE_OPENGLES
97
98
    glVersion = MAKE_GL_VERSION(glversioninfo[0].toInt(), glversioninfo[1].toInt(),
                                    glversioninfo.count() > 2 ? glversioninfo[2].toInt() : 0);
Martin Gräßlin's avatar
Martin Gräßlin committed
99
#endif
100
    // Get list of supported OpenGL extensions
Lucas Murray's avatar
Lucas Murray committed
101
    glExtensions = QString((const char*)glGetString(GL_EXTENSIONS)).split(' ');
102
103
104
105
106
107
108

    // handle OpenGL extensions functions
    glResolveFunctions();

    GLTexture::initStatic();
    GLShader::initStatic();
    GLRenderTarget::initStatic();
109
    GLVertexBuffer::initStatic();
110
111
112
113
114
115
116
117
118
119
120
121
    }

bool hasGLVersion(int major, int minor, int release)
    {
    return glVersion >= MAKE_GL_VERSION(major, minor, release);
    }

bool hasGLXVersion(int major, int minor, int release)
    {
    return glXVersion >= MAKE_GL_VERSION(major, minor, release);
    }

122
123
124
125
126
bool hasEGLVersion(int major, int minor, int release)
{
    return eglVersion >= MAKE_GL_VERSION(major, minor, release);
}

127
128
bool hasGLExtension(const QString& extension)
    {
129
    return glExtensions.contains(extension) || glxExtensions.contains(extension) || eglExtension.contains(extension);
130
131
    }

132
133
134
135
136
137
138
139
static QString formatGLError( GLenum err )
    {
    switch ( err )
        {
        case GL_NO_ERROR:          return "GL_NO_ERROR";
        case GL_INVALID_ENUM:      return "GL_INVALID_ENUM";
        case GL_INVALID_VALUE:     return "GL_INVALID_VALUE";
        case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
140
#ifndef KWIN_HAVE_OPENGLES
141
142
        case GL_STACK_OVERFLOW:    return "GL_STACK_OVERFLOW";
        case GL_STACK_UNDERFLOW:   return "GL_STACK_UNDERFLOW";
143
#endif
144
145
146
147
148
        case GL_OUT_OF_MEMORY:     return "GL_OUT_OF_MEMORY";
        default: return QString( "0x" ) + QString::number( err, 16 );
        }
    }

149
bool checkGLError( const char* txt )
150
151
152
    {
    GLenum err = glGetError();
    if( err != GL_NO_ERROR )
153
        {
154
        kWarning(1212) << "GL error (" << txt << "): " << formatGLError( err );
155
156
157
        return true;
        }
    return false;
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
    }

int nearestPowerOfTwo( int x )
    {
    // This method had been copied from Qt's nearest_gl_texture_size()
    int n = 0, last = 0;
    for (int s = 0; s < 32; ++s) {
        if (((x>>s) & 1) == 1) {
            ++n;
            last = s;
        }
    }
    if (n > 1)
        return 1 << (last+1);
    return 1 << last;
    }

175
176
void renderGLGeometry( int count, const float* vertices, const float* texture, const float* color,
    int dim, int stride )
177
    {
178
    return renderGLGeometry( infiniteRegion(), count, vertices, texture, color, dim, stride );
179
180
    }

181
void renderGLGeometry( const QRegion& region, int count,
182
    const float* vertices, const float* texture, const float* color,
183
184
    int dim, int stride )
    {
185
186
187
188
189
190
191
192
193
#ifdef KWIN_HAVE_OPENGLES
    Q_UNUSED(region)
    Q_UNUSED(count)
    Q_UNUSED(vertices)
    Q_UNUSED(texture)
    Q_UNUSED(color)
    Q_UNUSED(dim)
    Q_UNUSED(stride)
#else
194
195
196
197
198
    // Using arrays only makes sense if we have larger number of vertices.
    //  Otherwise overhead of enabling/disabling them is too big.
    bool use_arrays = (count > 5);

    if( use_arrays )
199
        {
200
201
202
203
204
205
206
207
208
209
210
211
212
        glPushAttrib( GL_ENABLE_BIT );
        glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT );
        // Enable arrays
        glEnableClientState( GL_VERTEX_ARRAY );
        glVertexPointer( dim, GL_FLOAT, stride, vertices );
        if( texture != NULL )
            {
            glEnableClientState( GL_TEXTURE_COORD_ARRAY );
            glTexCoordPointer( 2, GL_FLOAT, stride, texture );
            }
        if( color != NULL )
            {
            glEnableClientState( GL_COLOR_ARRAY );
Rivo Laks's avatar
Rivo Laks committed
213
            glColorPointer( 4, GL_FLOAT, stride, color );
214
            }
215
        }
216

217
    // Clip using scissoring
218
219
220
221
222
223
224
225
226
227
228
229
230
231
    if( !effects->isRenderTargetBound() )
        {
        PaintClipper pc( region );
        for( PaintClipper::Iterator iterator;
            !iterator.isDone();
            iterator.next())
            {
            if( use_arrays )
                glDrawArrays( GL_QUADS, 0, count );
            else
                renderGLGeometryImmediate( count, vertices, texture, color, dim, stride );
            }
        }
    else
232
233
234
235
236
237
238
239
240
241
242
243
        {
        if( use_arrays )
            glDrawArrays( GL_QUADS, 0, count );
        else
            renderGLGeometryImmediate( count, vertices, texture, color, dim, stride );
        }

    if( use_arrays )
        {
        glPopClientAttrib();
        glPopAttrib();
        }
244
#endif
245
246
    }

247
248
249
void renderGLGeometryImmediate( int count, const float* vertices, const float* texture, const float* color,
      int dim, int stride )
{
250
251
252
253
254
255
256
257
#ifdef KWIN_HAVE_OPENGLES
    Q_UNUSED(count)
    Q_UNUSED(vertices)
    Q_UNUSED(texture)
    Q_UNUSED(color)
    Q_UNUSED(dim)
    Q_UNUSED(stride)
#else
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
    // Find out correct glVertex*fv function according to dim parameter.
    void ( *glVertexFunc )( const float* ) = glVertex2fv;
    if( dim == 3 )
        glVertexFunc = glVertex3fv;
    else if( dim == 4 )
        glVertexFunc = glVertex4fv;

    // These are number of _floats_ per item, not _bytes_ per item as opengl uses.
    int vsize, tsize, csize;
    vsize = tsize = csize = stride / sizeof(float);
    if( !stride )
        {
        // 0 means that arrays are tightly packed. This gives us different
        //  strides for different arrays
        vsize = dim;
        tsize = 2;
        csize = 4;
        }

    glBegin( GL_QUADS );
    // This sucks. But makes it faster.
    if( texture && color )
        {
        for( int i = 0; i < count; i++ )
            {
            glTexCoord2fv( texture + i*tsize );
            glColor4fv( color + i*csize );
            glVertexFunc( vertices + i*vsize );
            }
        }
    else if( texture )
        {
        for( int i = 0; i < count; i++ )
            {
            glTexCoord2fv( texture + i*tsize );
            glVertexFunc( vertices + i*vsize );
            }
        }
    else if( color )
        {
        for( int i = 0; i < count; i++ )
            {
            glColor4fv( color + i*csize );
            glVertexFunc( vertices + i*vsize );
            }
        }
    else
        {
        for( int i = 0; i < count; i++ )
            glVertexFunc( vertices + i*vsize );
        }
    glEnd();
310
#endif
311
312
}

313
314
315
316
317
318
319
320
void addQuadVertices(QVector<float>& verts, float x1, float y1, float x2, float y2)
{
    verts << x1 << y1;
    verts << x1 << y2;
    verts << x2 << y2;
    verts << x2 << y1;
}

321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
void pushMatrix()
{
#ifndef KWIN_HAVE_OPENGLES
    glPushMatrix();
#endif
}

void pushMatrix(const QMatrix4x4 &matrix)
{
#ifdef KWIN_HAVE_OPENGLES
    Q_UNUSED(matrix)
#else
    glPushMatrix();
    multiplyMatrix(matrix);
#endif
}

void multiplyMatrix(const QMatrix4x4 &matrix)
{
#ifdef KWIN_HAVE_OPENGLES
    Q_UNUSED(matrix)
#else
    GLfloat m[16];
    const qreal *data = matrix.constData();
    for (int i = 0; i < 4; ++i) {
        for (int j=0; j < 4; ++j) {
            m[i*4+j] = data[i*4+j];
        }
    }
    glMultMatrixf(m);
#endif
}

354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
void loadMatrix(const QMatrix4x4 &matrix)
{
#ifdef KWIN_HAVE_OPENGLES
    Q_UNUSED(matrix)
#else
    GLfloat m[16];
    const qreal *data = matrix.constData();
    for (int i = 0; i < 4; ++i) {
        for (int j=0; j < 4; ++j) {
            m[i*4+j] = data[i*4+j];
        }
    }
    glLoadMatrixf(m);
#endif
}

370
371
372
373
374
375
376
void popMatrix()
{
#ifndef KWIN_HAVE_OPENGLES
    glPopMatrix();
#endif
}

377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
//****************************************
// GLTexture
//****************************************

bool GLTexture::mNPOTTextureSupported = false;
bool GLTexture::mFramebufferObjectSupported = false;
bool GLTexture::mSaturationSupported = false;

GLTexture::GLTexture()
    {
    init();
    }

GLTexture::GLTexture( const QImage& image, GLenum target )
    {
    init();
    load( image, target );
    }

GLTexture::GLTexture( const QPixmap& pixmap, GLenum target )
    {
    init();
    load( pixmap, target );
    }

GLTexture::GLTexture( const QString& fileName )
    {
    init();
    load( fileName );
    }

GLTexture::GLTexture( int width, int height )
    {
    init();

    if( NPOTTextureSupported() || ( isPowerOfTwo( width ) && isPowerOfTwo( height )))
        {
        mTarget = GL_TEXTURE_2D;
        mScale.setWidth( 1.0 / width);
        mScale.setHeight( 1.0 / height);
417
        mSize = QSize( width, height );
418
419
420
421
        can_use_mipmaps = true;

        glGenTextures( 1, &mTexture );
        bind();
422
423
424
425
426
#ifdef KWIN_HAVE_OPENGLES
        // format and internal format have to match in ES, GL_RGBA8 and GL_BGRA are not available
        // see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml
        glTexImage2D( mTarget, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
#else
427
        glTexImage2D( mTarget, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
428
#endif
429
430
431
432
433
434
        unbind();
        }
    }

GLTexture::~GLTexture()
    {
435
    delete m_vbo;
436
    discard();
437
438
    assert( mUnnormalizeActive == 0 );
    assert( mNormalizeActive == 0 );
439
440
441
442
443
444
445
446
447
448
    }

void GLTexture::init()
    {
    mTexture = None;
    mTarget = 0;
    mFilter = 0;
    y_inverted = false;
    can_use_mipmaps = false;
    has_valid_mipmaps = false;
449
450
    mUnnormalizeActive = 0;
    mNormalizeActive = 0;
451
    m_vbo = 0;
452
453
454
455
    }

void GLTexture::initStatic()
    {
Martin Gräßlin's avatar
Martin Gräßlin committed
456
457
458
459
460
#ifdef KWIN_HAVE_OPENGLES
    mNPOTTextureSupported = true;
    mFramebufferObjectSupported = true;
    mSaturationSupported = true;
#else
461
462
463
464
465
    mNPOTTextureSupported = hasGLExtension( "GL_ARB_texture_non_power_of_two" );
    mFramebufferObjectSupported = hasGLExtension( "GL_EXT_framebuffer_object" );
    mSaturationSupported = ((hasGLExtension("GL_ARB_texture_env_crossbar")
        && hasGLExtension("GL_ARB_texture_env_dot3")) || hasGLVersion(1, 4))
        && (glTextureUnitsCount >= 4) && glActiveTexture != NULL;
Martin Gräßlin's avatar
Martin Gräßlin committed
466
#endif
467
468
469
470
471
472
473
    }

bool GLTexture::isNull() const
    {
    return mTexture == None;
    }

474
475
476
477
478
QSize GLTexture::size() const
    {
    return mSize;
    }

479
480
481
482
483
484
bool GLTexture::load( const QImage& image, GLenum target )
    {
    if( image.isNull())
        return false;
    QImage img = image;
    mTarget = target;
485
#ifndef KWIN_HAVE_OPENGLES
486
487
    if( mTarget != GL_TEXTURE_RECTANGLE_ARB )
        {
Martin Gräßlin's avatar
Martin Gräßlin committed
488
#endif
489
490
491
492
493
494
495
496
497
        if( !NPOTTextureSupported()
            && ( !isPowerOfTwo( image.width()) || !isPowerOfTwo( image.height())))
            { // non-rectangular target requires POT texture
            img = img.scaled( nearestPowerOfTwo( image.width()),
                    nearestPowerOfTwo( image.height()));
            }
        mScale.setWidth( 1.0 / img.width());
        mScale.setHeight( 1.0 / img.height());
        can_use_mipmaps = true;
Martin Gräßlin's avatar
Martin Gräßlin committed
498
#ifndef KWIN_HAVE_OPENGLES
499
500
501
502
503
504
505
        }
    else
        {
        mScale.setWidth( 1.0 );
        mScale.setHeight( 1.0 );
        can_use_mipmaps = false;
        }
506
#endif
507
508
509
510
511
512
513
514
515
516
    setFilter( GL_LINEAR );
    mSize = img.size();
    y_inverted = false;

    img = convertToGLFormat( img );

    setDirty();
    if( isNull())
        glGenTextures( 1, &mTexture );
    bind();
517
518
519
520
521
#ifdef KWIN_HAVE_OPENGLES
    // format and internal format have to match in ES, GL_RGBA8 and GL_BGRA are not available
    // see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml
    glTexImage2D( mTarget, 0, GL_RGBA, img.width(), img.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
#else
522
523
    glTexImage2D( mTarget, 0, GL_RGBA8, img.width(), img.height(), 0,
        GL_BGRA, GL_UNSIGNED_BYTE, img.bits());
524
#endif
525
526
527
528
529
530
531
532
    unbind();
    return true;
    }

bool GLTexture::load( const QPixmap& pixmap, GLenum target )
    {
    if( pixmap.isNull())
        return false;
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
#ifdef KWIN_HAVE_OPENGLES
    if( isNull() )
        glGenTextures( 1, &mTexture );
    mTarget = target;
    bind();
    const EGLint attribs[] = {
        EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
        EGL_NONE
    };
    EGLDisplay dpy = eglGetCurrentDisplay();
    EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
                            (EGLClientBuffer)pixmap.handle(), attribs);
    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
    eglDestroyImageKHR(dpy, image);
    unbind();
    return true;
#else
550
    return load( pixmap.toImage(), target );
551
#endif
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
    }

bool GLTexture::load( const QString& fileName )
    {
    if( fileName.isEmpty())
        return false;
    return load( QImage( fileName ));
    }

void GLTexture::discard()
    {
    setDirty();
    if( mTexture != None )
        glDeleteTextures( 1, &mTexture );
    mTexture = None;
    }

void GLTexture::bind()
    {
Martin Gräßlin's avatar
Martin Gräßlin committed
571
#ifndef KWIN_HAVE_OPENGLES
572
    glEnable( mTarget );
Martin Gräßlin's avatar
Martin Gräßlin committed
573
#endif
574
575
576
577
578
579
580
    glBindTexture( mTarget, mTexture );
    enableFilter();
    }

void GLTexture::unbind()
    {
    glBindTexture( mTarget, 0 );
Martin Gräßlin's avatar
Martin Gräßlin committed
581
#ifndef KWIN_HAVE_OPENGLES
582
    glDisable( mTarget );
Martin Gräßlin's avatar
Martin Gräßlin committed
583
#endif
584
585
    }

586
void GLTexture::render( QRegion region, const QRect& rect )
587
    {
588
    if( rect.size() != m_cachedSize )
589
        {
590
591
592
        m_cachedSize = rect.size();
        QRect r(rect);
        r.moveTo(0,0);
593
        if( !m_vbo )
594
595
596
597
            {
            m_vbo = new GLVertexBuffer( KWin::GLVertexBuffer::Static );
            }
        const float verts[ 4 * 2 ] =
598
599
600
601
602
            {   // NOTICE: r.x/y could be replaced by "0", but that would make it unreadable...
            r.x(), r.y(),
            r.x(), r.y() + rect.height(),
            r.x() + rect.width(), r.y(),
            r.x() + rect.width(), r.y() + rect.height()
603
604
605
            };
        const float texcoords[ 4 * 2 ] =
            {
606
607
            0.0f, y_inverted ? 0.0f : 1.0f, // y needs to be swapped (normalized coords)
            0.0f, y_inverted ? 1.0f : 0.0f,
608
609
            1.0f, y_inverted ? 0.0f : 1.0f,
            1.0f, y_inverted ? 1.0f : 0.0f
610
            };
611
        m_vbo->setData( 4, 2, verts, texcoords );
612
        }
613
614
615
616
617
    if (ShaderManager::instance()->isShaderBound()) {
        GLShader *shader = ShaderManager::instance()->getBoundShader();
        shader->setUniform("offset", QVector2D(rect.x(), rect.y()));
        shader->setUniform("textureWidth", 1.0f);
        shader->setUniform("textureHeight", 1.0f);
618
619
620
621
622
    } else {
#ifndef KWIN_HAVE_OPENGLES
        glTranslatef( rect.x(), rect.y(), 0.0f );
#endif
    }
623
    m_vbo->render( region, GL_TRIANGLE_STRIP );
624
    if (!ShaderManager::instance()->isShaderBound()) {
625
626
627
628
#ifndef KWIN_HAVE_OPENGLES
        glTranslatef( -rect.x(), -rect.y(), 0.0f );
#endif
    }
629
630
631
632
    }

void GLTexture::enableUnnormalizedTexCoords()
    {
633
#ifndef KWIN_HAVE_OPENGLES
634
635
636
    assert( mNormalizeActive == 0 );
    if( mUnnormalizeActive++ != 0 )
        return;
637
638
639
640
641
642
643
644
645
646
647
648
649
    // update texture matrix to handle GL_TEXTURE_2D and GL_TEXTURE_RECTANGLE
    glMatrixMode( GL_TEXTURE );
    glPushMatrix();
    glLoadIdentity();
    glScalef( mScale.width(), mScale.height(), 1 );
    if( !y_inverted )
        {
        // Modify texture matrix so that we could always use non-opengl
        //  coordinates for textures
        glScalef( 1, -1, 1 );
        glTranslatef( 0, -mSize.height(), 0 );
        }
    glMatrixMode( GL_MODELVIEW );
650
#endif
651
652
653
654
    }

void GLTexture::disableUnnormalizedTexCoords()
    {
655
#ifndef KWIN_HAVE_OPENGLES
656
657
658
659
660
661
    if( --mUnnormalizeActive != 0 )
        return;
    // Restore texture matrix
    glMatrixMode( GL_TEXTURE );
    glPopMatrix();
    glMatrixMode( GL_MODELVIEW );
662
#endif
663
664
665
666
    }

void GLTexture::enableNormalizedTexCoords()
    {
667
#ifndef KWIN_HAVE_OPENGLES
668
669
670
671
672
673
674
675
676
677
678
679
680
    assert( mUnnormalizeActive == 0 );
    if( mNormalizeActive++ != 0 )
        return;
    // update texture matrix to handle GL_TEXTURE_2D and GL_TEXTURE_RECTANGLE
    glMatrixMode( GL_TEXTURE );
    glPushMatrix();
    glLoadIdentity();
    glScalef( mSize.width() * mScale.width(), mSize.height() * mScale.height(), 1 );
    if( y_inverted )
        {
        // Modify texture matrix so that we could always use non-opengl
        //  coordinates for textures
        glScalef( 1, -1, 1 );
681
        glTranslatef( 0, -1, 0 );
682
683
        }
    glMatrixMode( GL_MODELVIEW );
684
#endif
685
686
687
688
    }

void GLTexture::disableNormalizedTexCoords()
    {
689
#ifndef KWIN_HAVE_OPENGLES
690
691
    if( --mNormalizeActive != 0 )
        return;
692
693
694
695
    // Restore texture matrix
    glMatrixMode( GL_TEXTURE );
    glPopMatrix();
    glMatrixMode( GL_MODELVIEW );
696
#endif
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
    }

GLuint GLTexture::texture() const
    {
    return mTexture;
    }

GLenum GLTexture::target() const
    {
    return mTarget;
    }

GLenum GLTexture::filter() const
    {
    return mFilter;
    }

bool GLTexture::isDirty() const
    {
    return has_valid_mipmaps;
    }

void GLTexture::setTexture( GLuint texture )
    {
    discard();
    mTexture = texture;
    }

void GLTexture::setTarget( GLenum target )
    {
    mTarget = target;
    }

void GLTexture::setFilter( GLenum filter )
    {
    mFilter = filter;
    }

void GLTexture::setWrapMode( GLenum mode )
    {
    bind();
    glTexParameteri( mTarget, GL_TEXTURE_WRAP_S, mode );
    glTexParameteri( mTarget, GL_TEXTURE_WRAP_T, mode );
    unbind();
    }

void GLTexture::setDirty()
    {
    has_valid_mipmaps = false;
    }


void GLTexture::enableFilter()
    {
    if( mFilter == GL_LINEAR_MIPMAP_LINEAR )
        { // trilinear filtering requested, but is it possible?
        if( NPOTTextureSupported()
            && framebufferObjectSupported()
            && can_use_mipmaps )
            {
            glTexParameteri( mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
            glTexParameteri( mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
            if( !has_valid_mipmaps )
                {
                glGenerateMipmap( mTarget );
                has_valid_mipmaps = true;
                }
            }
        else
            { // can't use trilinear, so use bilinear
            setFilter( GL_LINEAR );
            glTexParameteri( mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
            glTexParameteri( mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
            }
        }
    else if( mFilter == GL_LINEAR )
        {
        glTexParameteri( mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
        glTexParameteri( mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
        }
    else
        { // if neither trilinear nor bilinear, default to fast filtering
        setFilter( GL_NEAREST );
        glTexParameteri( mTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
        glTexParameteri( mTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
        }
    }

785
static void convertToGLFormatHelper(QImage &dst, const QImage &img, GLenum texture_format)
786
787
788
789
790
    {
#ifdef KWIN_HAVE_OPENGLES
    Q_UNUSED(texture_format)
#endif
    // Copied from Qt
791
792
793
794
795
796
797
798
799
    Q_ASSERT(dst.size() == img.size());
    Q_ASSERT(dst.depth() == 32);
    Q_ASSERT(img.depth() == 32);

    const int width = img.width();
    const int height = img.height();
    const uint *p = (const uint*) img.scanLine(img.height() - 1);
    uint *q = (uint*) dst.scanLine(0);

800
#ifndef KWIN_HAVE_OPENGLES
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
    if (texture_format == GL_BGRA) {
        if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
            // mirror + swizzle
            for (int i=0; i < height; ++i) {
                const uint *end = p + width;
                while (p < end) {
                    *q = ((*p << 24) & 0xff000000)
                         | ((*p >> 24) & 0x000000ff)
                         | ((*p << 8) & 0x00ff0000)
                         | ((*p >> 8) & 0x0000ff00);
                    p++;
                    q++;
                }
                p -= 2 * width;
            }
        } else {
            const uint bytesPerLine = img.bytesPerLine();
            for (int i=0; i < height; ++i) {
                memcpy(q, p, bytesPerLine);
                q += width;
                p -= width;
            }
        }
    } else {
825
#endif
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
        if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
            for (int i=0; i < height; ++i) {
                const uint *end = p + width;
                while (p < end) {
                    *q = (*p << 8) | ((*p >> 24) & 0xFF);
                    p++;
                    q++;
                }
                p -= 2 * width;
            }
        } else {
            for (int i=0; i < height; ++i) {
                const uint *end = p + width;
                while (p < end) {
                    *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00);
                    p++;
                    q++;
                }
                p -= 2 * width;
845
846
            }
        }
847
#ifndef KWIN_HAVE_OPENGLES
848
    }
849
#endif
850
851
852
853
854
}

QImage GLTexture::convertToGLFormat( const QImage& img ) const
    { // Copied from Qt's QGLWidget::convertToGLFormat()
    QImage res(img.size(), QImage::Format_ARGB32);
855
856
857
#ifdef KWIN_HAVE_OPENGLES
    convertToGLFormatHelper(res, img.convertToFormat(QImage::Format_ARGB32), GL_RGBA);
#else
858
    convertToGLFormatHelper(res, img.convertToFormat(QImage::Format_ARGB32), GL_BGRA);
859
#endif
860
861
862
863
864
865
866
867
868
869
870
871
    return res;
    }

//****************************************
// GLShader
//****************************************

bool GLShader::mFragmentShaderSupported = false;
bool GLShader::mVertexShaderSupported = false;

void GLShader::initStatic()
{
Martin Gräßlin's avatar
Martin Gräßlin committed
872
873
874
#ifdef KWIN_HAVE_OPENGLES
    mFragmentShaderSupported = mVertexShaderSupported = true;
#else
875
876
877
878
    mFragmentShaderSupported = mVertexShaderSupported =
            hasGLExtension("GL_ARB_shader_objects") && hasGLExtension("GL_ARB_shading_language_100");
    mVertexShaderSupported &= hasGLExtension("GL_ARB_vertex_shader");
    mFragmentShaderSupported &= hasGLExtension("GL_ARB_fragment_shader");
Martin Gräßlin's avatar
Martin Gräßlin committed
879
#endif
880
881
882
883
884
885
886
}


GLShader::GLShader(const QString& vertexfile, const QString& fragmentfile)
    {
    mValid = false;
    mProgram = 0;
887
888
    mTextureWidth = -1.0f;
    mTextureHeight = -1.0f;
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903

    loadFromFiles(vertexfile, fragmentfile);
    }

GLShader::~GLShader()
{
    if(mProgram)
    {
        glDeleteProgram(mProgram);
    }
}

bool GLShader::loadFromFiles(const QString& vertexfile, const QString& fragmentfile)
    {
    QFile vf(vertexfile);
Pino Toscano's avatar
Pino Toscano committed
904
    if(!vf.open(QIODevice::ReadOnly))
905
        {
Thiago Macieira's avatar
Thiago Macieira committed
906
        kError(1212) << "Couldn't open '" << vertexfile << "' for reading!" << endl;
907
908
909
910
911
        return false;
        }
    QString vertexsource(vf.readAll());

    QFile ff(fragmentfile);
Pino Toscano's avatar
Pino Toscano committed
912
    if(!ff.open(QIODevice::ReadOnly))
913
        {
Thiago Macieira's avatar
Thiago Macieira committed
914
        kError(1212) << "Couldn't open '" << fragmentfile << "' for reading!" << endl;
915
916
917
918
919
920
921
922
923
924
925
926
927
        return false;
        }
    QString fragsource(ff.readAll());

    return load(vertexsource, fragsource);
    }

bool GLShader::load(const QString& vertexsource, const QString& fragmentsource)
    {
    // Make sure shaders are actually supported
    if(( !vertexsource.isEmpty() && !vertexShaderSupported() ) ||
          ( !fragmentsource.isEmpty() && !fragmentShaderSupported() ))
        {
Thiago Macieira's avatar
Thiago Macieira committed
928
        kDebug(1212) << "Shaders not supported";
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
        return false;
        }

    GLuint vertexshader;
    GLuint fragmentshader;

    GLsizei logsize, logarraysize;
    char* log = 0;

    // Create program object
    mProgram = glCreateProgram();
    if(!vertexsource.isEmpty())
        {
        // Create shader object
        vertexshader = glCreateShader(GL_VERTEX_SHADER);
        // Load it
945
946
947
948
949
        QByteArray srcba;
#ifdef KWIN_HAVE_OPENGLES
        srcba.append("#ifdef GL_ES\nprecision highp float;\n#endif\n");
#endif
        srcba.append(vertexsource.toLatin1());
950
951
952
953
954
955
956
957
958
959
960
961
962
        const char* src = srcba.data();
        glShaderSource(vertexshader, 1, &src, NULL);
        // Compile the shader
        glCompileShader(vertexshader);
        // Make sure it compiled correctly
        int compiled;
        glGetShaderiv(vertexshader, GL_COMPILE_STATUS, &compiled);
        // Get info log
        glGetShaderiv(vertexshader, GL_INFO_LOG_LENGTH, &logarraysize);
        log = new char[logarraysize];
        glGetShaderInfoLog(vertexshader, logarraysize, &logsize, log);
        if(!compiled)
            {
Thiago Macieira's avatar
Thiago Macieira committed
963
            kError(1212) << "Couldn't compile vertex shader! Log:" << endl << log << endl;
964
965
966
967
            delete[] log;
            return false;
            }
        else if(logsize > 0)
968
            kDebug(1212) << "Vertex shader compilation log:"<< log;
969
970
971
972
973
974
975
976
977
978
979
980
        // Attach the shader to the program
        glAttachShader(mProgram, vertexshader);
        // Delete shader
        glDeleteShader(vertexshader);
        delete[] log;
        }


    if(!fragmentsource.isEmpty())
        {
        fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
        // Load it
981
982
983
984
985
        QByteArray srcba;
#ifdef KWIN_HAVE_OPENGLES
        srcba.append("#ifdef GL_ES\nprecision highp float;\n#endif\n");
#endif
        srcba.append(fragmentsource.toLatin1());
986
987
988
989
990
991
992
993
994
995
996
997
998
999
        const char* src = srcba.data();
        glShaderSource(fragmentshader, 1, &src, NULL);
        //glShaderSource(fragmentshader, 1, &fragmentsrc.latin1(), NULL);
        // Compile the shader
        glCompileShader(fragmentshader);
        // Make sure it compiled correctly
        int compiled;
        glGetShaderiv(fragmentshader, GL_COMPILE_STATUS, &compiled);
        // Get info log
        glGetShaderiv(fragmentshader, GL_INFO_LOG_LENGTH, &logarraysize);
        log = new char[logarraysize];
        glGetShaderInfoLog(fragmentshader, logarraysize, &logsize, log);
        if(!compiled)
            {
Thiago Macieira's avatar
Thiago Macieira committed
1000
            kError(1212) << "Couldn't compile fragment shader! Log:" << endl << log << endl;
1001
1002
1003
1004
            delete[] log;
            return false;
            }
        else if(logsize > 0)
1005
            kDebug(1212) << "Fragment shader compilation log:"<< log;
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
        // Attach the shader to the program
        glAttachShader(mProgram, fragmentshader);
        // Delete shader
        glDeleteShader(fragmentshader);
        delete[] log;
        }


    // Link the program
    glLinkProgram(mProgram);
    // Make sure it linked correctly
    int linked;
    glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
    // Get info log
    glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &logarraysize);
    log = new char[logarraysize];
    glGetProgramInfoLog(mProgram, logarraysize, &logsize, log);
    if(!linked)
        {
Thiago Macieira's avatar
Thiago Macieira committed
1025
        kError(1212) << "Couldn't link the program! Log" << endl << log << endl;
1026
1027
1028
1029
        delete[] log;
        return false;
        }
    else if(logsize > 0)
1030
        kDebug(1212) << "Shader linking log:"<< log;
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
    delete[] log;

    mValid = true;
    return true;
    }

void GLShader::bind()
    {
    glUseProgram(mProgram);
    }

void GLShader::unbind()
    {
    glUseProgram(0);
    }

1047
int GLShader::uniformLocation(const char* name)
1048
    {
1049
1050
    int location = glGetUniformLocation(mProgram, name);
    return location;
1051
1052
    }

1053
bool GLShader::setUniform(const char* name, float value)
1054
1055
1056
1057
1058
1059
1060
1061
1062
    {
    int location = uniformLocation(name);
    if(location >= 0)
        {
        glUniform1f(location, value);
        }
    return (location >= 0);
    }

1063
bool GLShader::setUniform(const char* name, int value)
1064
1065
1066
1067
1068
1069
1070
1071
1072
    {
    int location = uniformLocation(name);
    if(location >= 0)
        {
        glUniform1i(location, value);
        }
    return (location >= 0);
    }

1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
bool GLShader::setUniform(const char* name, const QVector2D& value)
    {
    const int location = uniformLocation(name);
    if(location >= 0)
        {
        glUniform2f(location, value.x(), value.y());
        }
    return (location >= 0);
    }

bool GLShader::setUniform(const char* name, const QVector3D& value)
    {
    const int location = uniformLocation(name);
    if(location >= 0)
        {
        glUniform3f(location, value.x(), value.y(), value.z());
        }
    return (location >= 0);
    }

bool GLShader::setUniform(const char* name, const QVector4D& value)
    {
    const int location = uniformLocation(name);
    if(location >= 0)
        {
        glUniform4f(location, value.x(), value.y(), value.z(), value.w());
        }
    return (location >= 0);
    }

1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
bool GLShader::setUniform(const char* name, const QMatrix4x4& value)
{
    const int location = uniformLocation(name);
    if (location >= 0) {
        GLfloat m[16];
        const qreal *data = value.constData();
        // i is column, j is row for m
        for (int i = 0; i < 4; ++i) {
            for (int j=0; j < 4; ++j) {
                m[i*4+j] = data[j*4+i];
            }
        }
        glUniformMatrix4fv(location, 1, GL_FALSE, m);
    }
    return (location >= 0);
}

Martin Flöser's avatar
Martin Flöser committed
1120
1121
1122
1123
1124
1125
1126
1127
1128
bool GLShader::setUniform(const char* name, const QColor& color)
{
    const int location = uniformLocation(name);
    if (location >= 0) {
        glUniform4f(location, color.redF(), color.greenF(), color.blueF(), color.alphaF());
    }
    return (location >= 0);
}

1129
int GLShader::attributeLocation(const char* name)
1130
    {
1131
1132
    int location = glGetAttribLocation(mProgram, name);
    return location;
1133
1134
    }

1135
bool GLShader::setAttribute(const char* name, float value)
1136
1137
1138
1139
1140
1141
1142
1143
1144
    {
    int location = attributeLocation(name);
    if(location >= 0)
        {
        glVertexAttrib1f(location, value);
        }
    return (location >= 0);
    }

1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
void GLShader::setTextureHeight(float height)
    {
    mTextureHeight = height;
    }

void GLShader::setTextureWidth(float width)
    {
    mTextureWidth = width;
    }

float GLShader::textureHeight()
    {
    return mTextureHeight;
    }

float GLShader::textureWidth()
    {
    return mTextureWidth;
    }
1164

1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
QMatrix4x4 GLShader::getUniformMatrix4x4(const char* name)
{
    int location = uniformLocation(name);
    if (location >= 0) {
        GLfloat m[16];
        glGetUniformfv(mProgram, location, m);
        QMatrix4x4 matrix(m[0], m[1],  m[2], m[3],
                          m[4], m[5],  m[6], m[7],
                          m[8], m[9], m[10], m[11],
                          m[12], m[13], m[14], m[15]);
        matrix.optimize();
        return matrix;
    } else {
        return QMatrix4x4();
    }
}

Martin Flöser's avatar
Martin Flöser committed
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
//****************************************
// ShaderManager
//****************************************
ShaderManager *ShaderManager::s_shaderManager = NULL;

ShaderManager *ShaderManager::instance()
{
    if (!s_shaderManager) {
        s_shaderManager = new ShaderManager();
    }
    return s_shaderManager;
}

void ShaderManager::cleanup()
{
    delete s_shaderManager;
}

ShaderManager::ShaderManager()
    : m_orthoShader(NULL)
    , m_genericShader(NULL)
    , m_colorShader(NULL)
    , m_inited(false)
    , m_valid(false)
{
    initShaders();
    m_inited = true;
}

ShaderManager::~ShaderManager()
{
    while (!m_boundShaders.isEmpty()) {
        popShader();
    }
    delete m_orthoShader;
    delete m_genericShader;
    delete m_colorShader;
}

GLShader *ShaderManager::getBoundShader() const
{
    if (m_boundShaders.isEmpty()) {
        return NULL;
    } else {
        return m_boundShaders.top();
    }
}

bool ShaderManager::isShaderBound() const
{
    return !m_boundShaders.isEmpty();
}

bool ShaderManager::isValid() const
{
    return m_valid;
}

GLShader *ShaderManager::pushShader(ShaderType type, bool reset)
{
    if (m_inited && !m_valid) {
        return NULL;
    }
    GLShader *shader;
    switch (type) {
    case SimpleShader:
        shader = m_orthoShader;
        break;
    case GenericShader:
        shader = m_genericShader;
        break;
    case ColorShader:
        shader = m_colorShader;
        break;
    default:
        return NULL;
    }

    pushShader(shader);
    if (reset) {
        resetShader(type);
    }

    return shader;
}

void ShaderManager::pushShader(GLShader *shader)
{
    // only bind shader if it is not already bound
    if (shader != getBoundShader()) {
        shader->bind();
    }
    m_boundShaders.push(shader);
}

void ShaderManager::popShader()
{
    if (m_boundShaders.isEmpty()) {
        return;
    }
    GLShader *shader = m_boundShaders.pop();
    if (m_boundShaders.isEmpty()) {
        // no more shader bound - unbind
        shader->unbind();
    } else if (shader != m_boundShaders.top()) {
        // only rebind if a different shader is on top of stack
        m_boundShaders.top()->bind();
    }
}

1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
GLShader *ShaderManager::loadFragmentShader(ShaderType vertex, const QString &fragmentFile)
{
    QString vertexShader;
    switch (vertex) {
    case SimpleShader:
        vertexShader = ":/resources/scene-vertex.glsl";
        break;
    case GenericShader:
        vertexShader = ":/resources/scene-generic-vertex.glsl";
        break;
    case ColorShader:
        vertexShader = ":/resources/scene-color-vertex.glsl";
        break;
    }
    GLShader *shader = new GLShader(vertexShader, fragmentFile);
    if (shader->isValid()) {
        pushShader(shader);
        resetShader(vertex);
        popShader();
    }
    return shader;
}

Martin Flöser's avatar
Martin Flöser committed
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
void ShaderManager::initShaders()
{
    m_orthoShader = new GLShader(":/resources/scene-vertex.glsl", ":/resources/scene-fragment.glsl");
    if (m_orthoShader->isValid()) {
        pushShader(SimpleShader, true);
        popShader();
        kDebug(1212) << "Ortho Shader is valid";
    }
    else {
        delete m_orthoShader;
        m_orthoShader = NULL;
        kDebug(1212) << "Orho Shader is not valid";
        return;
    }
    m_genericShader = new GLShader( ":/resources/scene-generic-vertex.glsl", ":/resources/scene-fragment.glsl" );
    if (m_genericShader->isValid()) {
        pushShader(GenericShader, true);
        popShader();
        kDebug(1212) << "Generic Shader is valid";
    }
    else {
        delete m_genericShader;
        m_genericShader = NULL;
        delete m_orthoShader;
        m_orthoShader = NULL;
        kDebug(1212) << "Generic Shader is not valid";
        return;
    }
    m_colorShader = new GLShader(":/resources/scene-color-vertex.glsl", ":/resources/scene-color-fragment.glsl");
    if (m_colorShader->isValid()) {
        pushShader(ColorShader, true);
        popShader();
        kDebug(1212) << "Color Shader is valid";
    } else {
        delete m_genericShader;
        m_genericShader = NULL;
        delete m_orthoShader;
        m_orthoShader = NULL;
        delete m_colorShader;
        m_colorShader = NULL;
        kDebug(1212) << "Color Scene Shader is not valid";
        return;
    }
    m_valid = true;
}

void ShaderManager::resetShader(ShaderType type)
{
    // resetShader is either called from init or from push, we know that a built-in shader is bound
    GLShader *shader = getBoundShader();
    switch (type) {
1366
1367
1368
1369
1370
    case SimpleShader: {
        QMatrix4x4 projection;
        projection.ortho(0, displayWidth(), displayHeight(), 0, 0, 65535);
        shader->setUniform("projection", projection);
        shader->setUniform("offset", QVector2D(0, 0));
Martin Flöser's avatar
Martin Flöser committed
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
        shader->setUniform("debug", 0);
        shader->setUniform("sample", 0);
        // TODO: has to become textureSize
        shader->setUniform("textureWidth", 1.0f);
        shader->setUniform("textureHeight", 1.0f);
        // TODO: has to become colorManiuplation
        shader->setUniform("opacity", 1.0f);
        shader->setUniform("brightness", 1.0f);
        shader->setUniform("saturation", 1.0f);
        break;
1381
    }
Martin Flöser's avatar
Martin Flöser committed
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
    case GenericShader: {
        shader->setUniform("debug", 0);
        shader->setUniform("sample", 0);
        QMatrix4x4 projection;
        float fovy = 60.0f;
        float aspect = 1.0f;
        float zNear = 0.1f;
        float zFar = 100.0f;
        float ymax = zNear * tan(fovy  * M_PI / 360.0f);
        float ymin = -ymax;
        float xmin =  ymin * aspect;
        float xmax = ymax * aspect;
        projection.frustum(xmin, xmax, ymin, ymax, zNear, zFar);
        shader->setUniform("projection", projection);
        QMatrix4x4 modelview;
        float scaleFactor = 1.1 * tan( fovy * M_PI / 360.0f )/ymax;
        modelview.translate(xmin*scaleFactor, ymax*scaleFactor, -1.1);
        modelview.scale((xmax-xmin)*scaleFactor/displayWidth(), -(ymax-ymin)*scaleFactor/displayHeight(), 0.001);
        shader->setUniform("modelview", modelview);
        const QMatrix4x4 identity;
        shader->setUniform("screenTransformation", identity);
        shader->setUniform("windowTransformation", identity);
        // TODO: has to become textureSize
        shader->setUniform("textureWidth", 1.0f);
        shader->setUniform("textureHeight", 1.0f);
        // TODO: has to become colorManiuplation
        shader->setUniform("opacity", 1.0f);
        shader->setUniform("brightness", 1.0f);
        shader->setUniform("saturation", 1.0f);
        break;
    }
1413
1414
1415
1416
1417
    case ColorShader: {
        QMatrix4x4 projection;
        projection.ortho(0, displayWidth(), displayHeight(), 0, 0, 65535);
        shader->setUniform("projection", projection);
        shader->setUniform("offset", QVector2D(0, 0));
Martin Flöser's avatar
Martin Flöser committed
1418
1419
1420
        shader->setUniform("geometryColor", QVector4D(0, 0, 0, 1));
        break;
    }
1421
    }
Martin Flöser's avatar
Martin Flöser committed
1422
1423
}

1424
1425
1426
1427
1428
/***  GLRenderTarget  ***/
bool GLRenderTarget::mSupported = false;

void GLRenderTarget::initStatic()
    {
Martin Gräßlin's avatar
Martin Gräßlin committed
1429
1430
1431
#ifdef KWIN_HAVE_OPENGLES
    mSupported = true;
#else
1432
    mSupported = hasGLExtension("GL_EXT_framebuffer_object") && glFramebufferTexture2D;
Martin Gräßlin's avatar
Martin Gräßlin committed
1433
#endif
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
    }

GLRenderTarget::GLRenderTarget(GLTexture* color)
    {
    // Reset variables
    mValid = false;

    mTexture = color;

    // Make sure FBO is supported
    if(mSupported && mTexture && !mTexture->isNull())
        {
        initFBO();
        }
    else
Thiago Macieira's avatar
Thiago Macieira committed
1449
        kError(1212) << "Render targets aren't supported!" << endl;
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
    }

GLRenderTarget::~GLRenderTarget()
    {
    if(mValid)
        {
        glDeleteFramebuffers(1, &mFramebuffer);
        }
    }

bool GLRenderTarget::enable()
    {
    if(!valid())
        {
Thiago Macieira's avatar
Thiago Macieira committed
1464
        kError(1212) << "Can't enable invalid render target!" << endl;
1465
1466
1467
        return false;
        }

1468
    glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
1469
1470
1471
1472
1473
1474
1475
1476
1477
    mTexture->setDirty();

    return true;
    }

bool GLRenderTarget::disable()
    {
    if(!valid())
        {
Thiago Macieira's avatar
Thiago Macieira committed
1478
        kError(1212) << "Can't disable invalid render target!" << endl;
1479
1480
1481
        return false;
        }

1482
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
1483
1484
1485
1486
1487
    mTexture->setDirty();

    return true;
    }

1488
1489
1490
1491
static QString formatFramebufferStatus( GLenum status )
    {
    switch( status )
        {
1492
        case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
1493
1494
            // An attachment is the wrong type / is invalid / has 0 width or height
            return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
1495
        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
1496
1497
            // There are no images attached to the framebuffer
            return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
1498
        case GL_FRAMEBUFFER_UNSUPPORTED:
1499
1500
            // A format or the combination of formats of the attachments is unsupported
            return "GL_FRAMEBUFFER_UNSUPPORTED";
1501
#ifndef KWIN_HAVE_OPENGLES
1502
1503
1504
        case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
            // Not all attached images have the same width and height
            return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT";
1505
1506
1507
        case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
            // The color attachments don't have the same format
            return "GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT";
1508
        case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
1509
1510
            // The attachments don't have the same number of samples
            return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
1511
        case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
1512
1513
            // The draw buffer is missing
            return "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER";
1514
        case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
1515
1516
            // The read buffer is missing
            return "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER";
1517
#endif
1518
        default:
1519
            return "Unknown (0x" + QString::number(status, 16) + ')';
1520
1521
1522
        }
    }

1523
1524
void GLRenderTarget::initFBO()
    {
1525
1526
1527
1528
1529
1530
#if DEBUG_GLRENDERTARGET
    GLenum err = glGetError();
    if( err != GL_NO_ERROR )
        kError(1212) << "Error status when entering GLRenderTarget::initFBO: " << formatGLError( err );
#endif

1531
    glGenFramebuffers(1, &mFramebuffer);
1532
1533
1534
1535
1536
1537
1538
1539
1540

#if DEBUG_GLRENDERTARGET
    if( (err = glGetError()) != GL_NO_ERROR )
        {
        kError(1212) << "glGenFramebuffers failed: " << formatGLError( err );
        return;
        }
#endif

1541
    glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
1542

1543
1544
1545
1546
1547
1548
1549
1550
1551
#if DEBUG_GLRENDERTARGET
    if( (err = glGetError()) != GL_NO_ERROR )
        {
        kError(1212) << "glBindFramebuffer failed: " << formatGLError( err );
        glDeleteFramebuffers(1, &mFramebuffer);
        return;
        }
#endif

1552
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1553
1554
                           mTexture->target(), mTexture->texture(), 0);

1555
1556
1557
1558
#if DEBUG_GLRENDERTARGET
    if( (err = glGetError()) != GL_NO_ERROR )
        {
        kError(1212) << "glFramebufferTexture2D failed: " << formatGLError( err );
1559
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
1560
1561
1562
1563
1564
        glDeleteFramebuffers(1, &mFramebuffer);
        return;
        }
#endif

1565
    const GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
1566

1567
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
1568

1569
    if( status != GL_FRAMEBUFFER_COMPLETE )
1570
        { // We have an incomplete framebuffer, consider it invalid
1571
1572
1573
1574
        if (status == 0)
            kError(1212) << "glCheckFramebufferStatus failed: " << formatGLError( glGetError() );
        else
            kError(1212) << "Invalid framebuffer status: " << formatFramebufferStatus( status );
1575
1576
1577
1578
        glDeleteFramebuffers(1, &mFramebuffer);
        return;
        }

1579
1580
1581
    mValid = true;
    }

1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592

//*********************************
// GLVertexBufferPrivate
//*********************************
class GLVertexBufferPrivate
    {
    public:
        GLVertexBufferPrivate( GLVertexBuffer::UsageHint usageHint )
            : hint( usageHint )
            , numberVertices( 0 )
            , dimension( 2 )
Martin Gräßlin's avatar
Martin Gräßlin committed
1593
            , useColor( false )
Martin Flöser's avatar
Martin Flöser committed
1594
            , useTexCoords( true )
Martin Gräßlin's avatar
Martin Gräßlin committed
1595
            , color( 0, 0, 0, 255 )