2
* Copyright © 2010 Fredrik Höglund <fredrik@kde.org>
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; see the file COPYING. if not, write to
16
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
* Boston, MA 02110-1301, USA.
20
#include "blurshader.h"
22
#include <kwineffects.h>
23
#include <kwinglplatform.h>
27
#include <QTextStream>
36
BlurShader::BlurShader()
37
: mRadius(0), mValid(false)
41
BlurShader::~BlurShader()
45
BlurShader *BlurShader::create()
47
if (GLSLBlurShader::supported())
48
return new GLSLBlurShader();
50
return new ARBBlurShader();
53
void BlurShader::setRadius(int radius)
55
const int r = qMax(radius, 2);
64
void BlurShader::setDirection(Qt::Orientation direction)
66
mDirection = direction;
69
float BlurShader::gaussian(float x, float sigma) const
71
return (1.0 / std::sqrt(2.0 * M_PI) * sigma)
72
* std::exp(-((x * x) / (2.0 * sigma * sigma)));
75
QVector<float> BlurShader::gaussianKernel() const
77
int size = qMin(mRadius | 1, maxKernelSize());
81
QVector<float> kernel(size);
82
const int center = size / 2;
83
const qreal sigma = (size - 1) / 2.5;
85
// Generate the gaussian kernel
86
kernel[center] = gaussian(0, sigma) * .5;
87
for (int i = 1; i <= center; i++) {
88
const float val = gaussian(1.5 + (i - 1) * 2.0, sigma);
89
kernel[center + i] = val;
90
kernel[center - i] = val;
93
// Normalize the kernel
95
for (int i = 0; i < size; i++)
98
for (int i = 0; i < size; i++)
106
// ----------------------------------------------------------------------------
110
GLSLBlurShader::GLSLBlurShader()
111
: BlurShader(), shader(NULL)
115
GLSLBlurShader::~GLSLBlurShader()
120
void GLSLBlurShader::reset()
128
bool GLSLBlurShader::supported()
130
if (!GLPlatform::instance()->supports(GLSL))
132
if (!ShaderManager::instance()->isValid())
135
(void) glGetError(); // Clear the error state
137
#ifndef KWIN_HAVE_OPENGLES
138
// These are the minimum values the implementation is required to support
141
glGetIntegerv(GL_MAX_VARYING_FLOATS, &value);
145
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &value);
149
glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &value);
154
if (glGetError() != GL_NO_ERROR)
160
void GLSLBlurShader::setPixelDistance(float val)
165
QVector2D pixelSize(0.0, 0.0);
166
if (direction() == Qt::Horizontal)
170
shader->setUniform("pixelSize", pixelSize);
173
void GLSLBlurShader::setTextureMatrix(const QMatrix4x4 &matrix)
178
shader->setUniform("u_textureMatrix", matrix);
181
void GLSLBlurShader::bind()
186
ShaderManager::instance()->pushShader(shader);
189
void GLSLBlurShader::unbind()
191
ShaderManager::instance()->popShader();
194
int GLSLBlurShader::maxKernelSize() const
197
#ifdef KWIN_HAVE_OPENGLES
198
// GL_MAX_VARYING_FLOATS not available in GLES
199
// querying for GL_MAX_VARYING_VECTORS crashes on nouveau
200
// using the minimum value of 8
203
glGetIntegerv(GL_MAX_VARYING_FLOATS, &value);
204
// Maximum number of vec4 varyings * 2
205
// The code generator will pack two vec2's into each vec4.
211
void GLSLBlurShader::init()
213
QVector<float> kernel = gaussianKernel();
214
const int size = kernel.size();
215
const int center = size / 2;
217
QByteArray vertexSource;
218
QByteArray fragmentSource;
221
// ===================================================================
222
QTextStream stream(&vertexSource);
224
stream << "uniform mat4 u_modelViewProjectionMatrix;\n";
225
stream << "uniform mat4 u_textureMatrix;\n";
226
stream << "uniform vec2 pixelSize;\n\n";
227
stream << "attribute vec4 vertex;\n";
228
stream << "attribute vec4 texCoord;\n\n";
229
stream << "varying vec4 samplePos[" << std::ceil(size / 2.0) << "];\n";
231
stream << "void main(void)\n";
233
stream << " vec4 center = vec4(u_textureMatrix * texCoord).stst;\n";
234
stream << " vec4 ps = pixelSize.stst;\n\n";
235
for (int i = 0; i < size; i += 2) {
236
float offset1, offset2;
238
offset1 = -(1.5 + (center - i - 1) * 2.0);
239
offset2 = (i + 1) == center ? 0 : offset1 + 2;
240
} else if (i > center) {
241
offset1 = 1.5 + (i - center - 1) * 2.0;
242
offset2 = (i + 1) == size ? 0 : offset1 + 2;
247
stream << " samplePos[" << i / 2 << "] = center + ps * vec4("
248
<< offset1 << ", " << offset1 << ", " << offset2 << ", " << offset2 << ");\n";
251
stream << " gl_Position = u_modelViewProjectionMatrix * vertex;\n";
256
// ===================================================================
257
QTextStream stream2(&fragmentSource);
259
stream2 << "uniform sampler2D texUnit;\n";
260
stream2 << "varying vec4 samplePos[" << std::ceil(size / 2.0) << "];\n\n";
262
for (int i = 0; i <= center; i++)
263
stream2 << "const vec4 kernel" << i << " = vec4(" << kernel[i] << ");\n";
265
stream2 << "void main(void)\n";
267
stream2 << " vec4 sum = texture2D(texUnit, samplePos[0].st) * kernel0;\n";
268
for (int i = 1; i < size; i++)
269
stream2 << " sum = sum + texture2D(texUnit, samplePos[" << i / 2 << ((i % 2) ? "].pq)" : "].st)")
270
<< " * kernel" << (i > center ? size - i - 1 : i) << ";\n";
271
stream2 << " gl_FragColor = sum;\n";
275
shader = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentSource);
276
if (shader->isValid()) {
277
QMatrix4x4 modelViewProjection;
278
modelViewProjection.ortho(0, displayWidth(), displayHeight(), 0, 0, 65535);
279
ShaderManager::instance()->pushShader(shader);
280
shader->setUniform("texUnit", 0);
281
shader->setUniform("u_textureMatrix", QMatrix4x4());
282
shader->setUniform("u_modelViewProjectionMatrix", modelViewProjection);
283
ShaderManager::instance()->popShader();
286
setIsValid(shader->isValid());
291
// ----------------------------------------------------------------------------
295
ARBBlurShader::ARBBlurShader()
296
: BlurShader(), program(0)
300
ARBBlurShader::~ARBBlurShader()
305
void ARBBlurShader::reset()
307
#ifndef KWIN_HAVE_OPENGLES
309
glDeleteProgramsARB(1, &program);
317
bool ARBBlurShader::supported()
319
#ifdef KWIN_HAVE_OPENGLES
322
if (!hasGLExtension("GL_ARB_fragment_program"))
325
(void) glGetError(); // Clear the error state
327
// These are the minimum values the implementation is required to support
330
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_PARAMETERS_ARB, &value);
334
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_TEMPORARIES_ARB, &value);
338
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_INSTRUCTIONS_ARB, &value);
342
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB, &value);
346
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB, &value);
350
if (glGetError() != GL_NO_ERROR)
357
void ARBBlurShader::setPixelDistance(float val)
359
#ifdef KWIN_HAVE_OPENGLES
362
float firstStep = val * 1.5;
363
float nextStep = val * 2.0;
365
if (direction() == Qt::Horizontal) {
366
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, firstStep, 0, 0, 0);
367
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, nextStep, 0, 0, 0);
369
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, 0, firstStep, 0, 0);
370
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, 0, nextStep, 0, 0);
375
void ARBBlurShader::bind()
377
#ifndef KWIN_HAVE_OPENGLES
381
glEnable(GL_FRAGMENT_PROGRAM_ARB);
382
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, program);
386
void ARBBlurShader::unbind()
388
#ifndef KWIN_HAVE_OPENGLES
390
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_BINDING_ARB, &boundObject);
391
if (boundObject == program) {
392
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
393
glDisable(GL_FRAGMENT_PROGRAM_ARB);
398
int ARBBlurShader::maxKernelSize() const
400
#ifdef KWIN_HAVE_OPENGLES
406
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_PARAMETERS_ARB, &value);
407
result = (value - 1) * 2; // We only need to store half the kernel, since it's symmetrical
409
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_INSTRUCTIONS_ARB, &value);
410
result = qMin(result, value / 3); // We need 3 instructions / sample
416
void ARBBlurShader::init()
418
#ifndef KWIN_HAVE_OPENGLES
419
QVector<float> kernel = gaussianKernel();
420
const int size = kernel.size();
421
const int center = size / 2;
424
QTextStream stream(&text);
426
stream << "!!ARBfp1.0\n";
428
// The kernel values are hardcoded into the program
429
for (int i = 0; i <= center; i++)
430
stream << "PARAM kernel" << i << " = " << kernel[center + i] << ";\n";
432
stream << "PARAM firstSample = program.local[0];\n"; // Distance from gl_TexCoord[0] to the next sample
433
stream << "PARAM nextSample = program.local[1];\n"; // Distance to the subsequent sample
435
// Temporary variables to hold coordinates and texture samples
436
for (int i = 0; i < size; i++)
437
stream << "TEMP temp" << i << ";\n";
439
// Compute the texture coordinates
440
stream << "ADD temp1, fragment.texcoord[0], firstSample;\n"; // temp1 = gl_TexCoord[0] + firstSample
441
stream << "SUB temp2, fragment.texcoord[0], firstSample;\n"; // temp2 = gl_TexCoord[0] - firstSample
442
for (int i = 1, j = 3; i < center; i++, j += 2) {
443
stream << "ADD temp" << j + 0 << ", temp" << j - 2 << ", nextSample;\n";
444
stream << "SUB temp" << j + 1 << ", temp" << j - 1 << ", nextSample;\n";
447
// Sample the texture coordinates
448
stream << "TEX temp0, fragment.texcoord[0], texture[0], 2D;\n";
449
for (int i = 1; i < size; i++)
450
stream << "TEX temp" << i << ", temp" << i << ", texture[0], 2D;\n";
452
// Multiply the samples with the kernel values and compute the sum
453
stream << "MUL temp0, temp0, kernel0;\n";
454
for (int i = 0, j = 1; i < center; i++) {
455
stream << "MAD temp0, temp" << j++ << ", kernel" << i + 1 << ", temp0;\n";
456
stream << "MAD temp0, temp" << j++ << ", kernel" << i + 1 << ", temp0;\n";
459
stream << "MOV result.color, temp0;\n"; // gl_FragColor = temp0
463
glGenProgramsARB(1, &program);
464
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, program);
465
glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, text.length(), text.constData());
468
const char *error = (const char*)glGetString(GL_PROGRAM_ERROR_STRING_ARB);
469
kError() << "Failed to compile fragment program:" << error;
474
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);