diff --git a/effects/blur/blur.h b/effects/blur/blur.h --- a/effects/blur/blur.h +++ b/effects/blur/blur.h @@ -86,16 +86,22 @@ void doBlur(const QRegion &shape, const QRect &screen, const float opacity, const QMatrix4x4 &screenProjection, bool isDock); void uploadRegion(QVector2D *&map, const QRegion ®ion, const int downSampleIterations); void uploadGeometry(GLVertexBuffer *vbo, const QRegion &blurRegion, const QRegion &windowRegion); + void generateNoiseTexture(); + void upscaleRenderToScreen(GLVertexBuffer *vbo, int vboStart, int blurRectCount, QMatrix4x4 screenProjection); void downSampleTexture(GLVertexBuffer *vbo, int blurRectCount); void upSampleTexture(GLVertexBuffer *vbo, int blurRectCount); void copyScreenSampleTexture(GLVertexBuffer *vbo, int blurRectCount, QRegion blurShape, QSize screenSize, QMatrix4x4 screenProjection); + void noiseSampleTexture(GLVertexBuffer *vbo, int vboStart, int blurRectCount); private: BlurShader *m_shader; QVector m_renderTargets; QVector m_renderTextures; QStack m_renderTargetStack; + + GLTexture m_noiseTexture; + bool m_renderTargetsValid; long net_wm_blur_region; QRegion m_damagedArea; // keeps track of the area which has been damaged (from bottom to top) @@ -105,6 +111,9 @@ int m_downSampleIterations; // number of times the texture will be downsized to half size int m_offset; int m_expandSize; + int m_noiseStrength; + + int m_scalingFactor; struct OffsetStruct { float minOffset; diff --git a/effects/blur/blur.cpp b/effects/blur/blur.cpp --- a/effects/blur/blur.cpp +++ b/effects/blur/blur.cpp @@ -33,6 +33,8 @@ #include #include #include +#include +#include namespace KWin { @@ -143,10 +145,16 @@ // Prepare the stack for the rendering m_renderTargetStack.clear(); - m_renderTargets.reserve(m_downSampleIterations * 2 - 1); + m_renderTargets.reserve(m_downSampleIterations * 2); + + int upSampleEnd = 0; + + if (m_noiseStrength == 0) { + upSampleEnd = 1; + } // Upsample - for (int i = 1; i < m_downSampleIterations; i++) { + for (int i = upSampleEnd; i < m_downSampleIterations; i++) { m_renderTargetStack.push(m_renderTargets[i]); } @@ -157,6 +165,9 @@ // Copysample m_renderTargetStack.push(m_renderTargets[0]); + + // Generate the noise helper texture + generateNoiseTexture(); } void BlurEffect::initBlurStrengthValues() @@ -227,6 +238,9 @@ m_downSampleIterations = blurStrengthValues[blurStrength].iteration; m_offset = blurStrengthValues[blurStrength].offset; m_expandSize = blurOffsets[m_downSampleIterations - 1].expandSize; + m_noiseStrength = BlurConfig::noiseStrength(); + + m_scalingFactor = KSharedConfig::openConfig(QStringLiteral("kdeglobals"))->group("KScreen").readEntry("ScaleFactor", 1.0); updateTexture(); @@ -571,6 +585,32 @@ effects->paintEffectFrame(frame, region, opacity, frameOpacity); } +void BlurEffect::generateNoiseTexture() +{ + if (m_noiseStrength == 0) { + return; + } + + QImage noiseImage(QSize(128, 128), QImage::Format_RGBA8888); + int randomNumber; + + for (int y = 0; y < noiseImage.height(); y++) { + for (int x = 0; x < noiseImage.width(); x++) { + randomNumber = qrand() % m_noiseStrength; + noiseImage.setPixelColor(x, y, QColor( + randomNumber, // red + randomNumber, // green + randomNumber, // blue + 0 + )); + } + } + + m_noiseTexture = GLTexture(noiseImage); + m_noiseTexture.setFilter(GL_NEAREST); + m_noiseTexture.setWrapMode(GL_REPEAT); +} + void BlurEffect::doBlur(const QRegion& shape, const QRect& screen, const float opacity, const QMatrix4x4 &screenProjection, bool isDock) { QRegion expandedBlurRegion = expand(shape) & expand(screen); @@ -628,25 +668,34 @@ glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); } - //Final upscale to the screen - m_shader->bind(BlurShader::UpSampleType); - m_shader->setOffset(m_offset); - - m_shader->setModelViewProjectionMatrix(screenProjection); - m_shader->setTargetSize(m_renderTextures[0].size()); - - //Copy the image from this texture - m_renderTextures[1].bind(); - - //Render to the screen - vbo->draw(GL_TRIANGLES, blurRectCount * (m_downSampleIterations + 1), shape.rectCount() * 6); + if (m_noiseStrength == 0) { + upscaleRenderToScreen(vbo, blurRectCount * (m_downSampleIterations + 1), shape.rectCount() * 6, screenProjection); + } else { + noiseSampleTexture(vbo, blurRectCount * (m_downSampleIterations + 1), shape.rectCount() * 6); + } if (opacity < 1.0) { glDisable(GL_BLEND); } vbo->unbindArrays(); - m_shader->unbind(); +} + +void BlurEffect::upscaleRenderToScreen(GLVertexBuffer *vbo, int vboStart, int blurRectCount, QMatrix4x4 screenProjection) +{ + m_shader->bind(BlurShader::UpSampleType); + m_shader->setOffset(m_offset); + + m_shader->setModelViewProjectionMatrix(screenProjection); + m_shader->setTargetSize(m_renderTextures[0].size()); + + //Copy the image from this texture + m_renderTextures[1].bind(); + + //Render to the screen + vbo->draw(GL_TRIANGLES, vboStart, blurRectCount); + + m_shader->unbind(); } void BlurEffect::downSampleTexture(GLVertexBuffer *vbo, int blurRectCount) @@ -680,7 +729,13 @@ m_shader->bind(BlurShader::UpSampleType); m_shader->setOffset(m_offset); - for (int i = m_downSampleIterations - 1; i > 0; i--) { + int upSampleEnd = 0; + + if (m_noiseStrength == 0) { + upSampleEnd = 1; + } + + for (int i = m_downSampleIterations - 1; i >= upSampleEnd; i--) { modelViewProjectionMatrix.setToIdentity(); modelViewProjectionMatrix.ortho(0, m_renderTextures[i].width(), m_renderTextures[i].height(), 0 , 0, 65535); @@ -716,5 +771,29 @@ m_shader->unbind(); } +void BlurEffect::noiseSampleTexture(GLVertexBuffer *vbo, int vboStart, int blurRectCount) +{ + m_shader->bind(BlurShader::NoiseSampleType); + + QMatrix4x4 modelViewProjectionMatrix; + modelViewProjectionMatrix.setToIdentity(); + modelViewProjectionMatrix.ortho(0, m_renderTextures[0].width(), m_renderTextures[0].height(), 0 , 0, 65535); + + m_shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); + m_shader->setTargetSize(m_renderTextures[0].size()); + m_shader->setNoiseTextureRatio((float)m_renderTextures[0].width() / m_noiseTexture.width() / m_scalingFactor); + + glActiveTexture(GL_TEXTURE0); + m_renderTextures[0].bind(); + + glActiveTexture(GL_TEXTURE1); + m_noiseTexture.bind(); + + vbo->draw(GL_TRIANGLES, vboStart, blurRectCount); + + glActiveTexture(GL_TEXTURE0); + m_shader->unbind(); +} + } // namespace KWin diff --git a/effects/blur/blur.kcfg b/effects/blur/blur.kcfg --- a/effects/blur/blur.kcfg +++ b/effects/blur/blur.kcfg @@ -8,5 +8,8 @@ 10 + + 0 + diff --git a/effects/blur/blur_config.ui b/effects/blur/blur_config.ui --- a/effects/blur/blur_config.ui +++ b/effects/blur/blur_config.ui @@ -7,14 +7,14 @@ 0 0 480 - 95 + 145 - + - Strength of the effect: + Blur strength: @@ -37,7 +37,7 @@ - + Light @@ -69,7 +69,67 @@ - + + + Strong + + + + + + + + + Noise strength: + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Light + + + + + + + 14 + + + 5 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 1 + + + + + Strong diff --git a/effects/blur/blurshader.h b/effects/blur/blurshader.h --- a/effects/blur/blurshader.h +++ b/effects/blur/blurshader.h @@ -47,11 +47,13 @@ virtual void setOffset(float offset) = 0; virtual void setTargetSize(QSize renderTextureSize) = 0; virtual void setBlurRect(QRect blurRect, QSize screenSize) = 0; + virtual void setNoiseTextureRatio(float ratio) = 0; enum SampleType { DownSampleType, UpSampleType, - CopySampleType + CopySampleType, + NoiseSampleType }; virtual void bind(SampleType sampleType) = 0; @@ -85,6 +87,7 @@ void setOffset(float offset) override final; void setTargetSize(QSize renderTextureSize) override final; void setBlurRect(QRect blurRect, QSize screenSize) override final; + void setNoiseTextureRatio(float ratio) override final; protected: void init() override final; @@ -94,6 +97,7 @@ GLShader *m_shaderDownsample = nullptr; GLShader *m_shaderUpsample = nullptr; GLShader *m_shaderCopysample = nullptr; + GLShader *m_shaderNoisesample = nullptr; int m_mvpMatrixLocationDownsample; int m_offsetLocationDownsample; @@ -109,6 +113,10 @@ int m_renderTextureSizeLocationCopysample; int m_blurRectLocationCopysample; + int m_mvpMatrixLocationNoisesample; + int m_renderTextureSizeLocationNoisesample; + int m_noiseTexRatioLocationNoisesample; + //Caching uniform values to aviod unnecessary setUniform calls int m_activeSampleType; @@ -125,6 +133,10 @@ QSize m_renderTextureSizeCopysample; QRect m_blurRectCopysample; + QMatrix4x4 m_matrixNoisesample; + QSize m_renderTextureSizeNoisesample; + float m_noiseTexRatioNoisesample; + }; } // namespace KWin diff --git a/effects/blur/blurshader.cpp b/effects/blur/blurshader.cpp --- a/effects/blur/blurshader.cpp +++ b/effects/blur/blurshader.cpp @@ -73,6 +73,9 @@ delete m_shaderCopysample; m_shaderCopysample = nullptr; + delete m_shaderNoisesample; + m_shaderNoisesample = nullptr; + setIsValid(false); } @@ -105,6 +108,14 @@ m_matrixDownsample = matrix; m_shaderDownsample->setUniform(m_mvpMatrixLocationDownsample, matrix); break; + + case NoiseSampleType: + if (matrix == m_matrixNoisesample) + return; + + m_matrixNoisesample = matrix; + m_shaderNoisesample->setUniform(m_mvpMatrixLocationNoisesample, matrix); + break; } } @@ -165,6 +176,14 @@ m_shaderDownsample->setUniform(m_renderTextureSizeLocationDownsample, texSize); m_shaderDownsample->setUniform(m_halfpixelLocationDownsample, QVector2D(0.5 / texSize.x(), 0.5 / texSize.y())); break; + + case NoiseSampleType: + if (renderTextureSize == m_renderTextureSizeNoisesample) + return; + + m_renderTextureSizeNoisesample = renderTextureSize; + m_shaderNoisesample->setUniform(m_renderTextureSizeLocationNoisesample, texSize); + break; } } @@ -177,14 +196,23 @@ QVector4D rect = QVector4D( blurRect.bottomLeft().x() / float(screenSize.width()), - 1.0 - blurRect.bottomLeft().y() / float(screenSize.height()), - blurRect.topRight().x() / float(screenSize.width()), - 1.0 - blurRect.topRight().y() / float(screenSize.height()) + 1.0 - blurRect.bottomLeft().y() / float(screenSize.height()), + blurRect.topRight().x() / float(screenSize.width()), + 1.0 - blurRect.topRight().y() / float(screenSize.height()) ); m_shaderCopysample->setUniform(m_blurRectLocationCopysample, rect); } +void GLSLBlurShader::setNoiseTextureRatio(float ratio) +{ + if (!isValid() || ratio == m_noiseTexRatioNoisesample) + return; + + m_noiseTexRatioNoisesample = ratio; + m_shaderCopysample->setUniform(m_noiseTexRatioLocationNoisesample, ratio); +} + void GLSLBlurShader::bind(SampleType sampleType) { if (!isValid()) @@ -202,6 +230,10 @@ case DownSampleType: ShaderManager::instance()->pushShader(m_shaderDownsample); break; + + case NoiseSampleType: + ShaderManager::instance()->pushShader(m_shaderNoisesample); + break; } m_activeSampleType = sampleType; @@ -222,6 +254,7 @@ QByteArray fragmentDownSource; QByteArray fragmentUpSource; QByteArray fragmentCopySource; + QByteArray fragmentNoiseSource; const QByteArray attribute = core ? "in" : "attribute"; const QByteArray texture2D = core ? "texture" : "texture2D"; @@ -331,12 +364,38 @@ streamFragCopy.flush(); + // Fragment shader - Noise texture + // =================================================================== + QTextStream streamFragNoise(&fragmentNoiseSource); + + streamFragNoise << glHeaderString; + + streamFragNoise << "uniform sampler2D texUnit;\n"; + streamFragNoise << "uniform sampler2D noiseTexUnit;\n"; + streamFragNoise << "uniform vec2 renderTextureSize;\n"; + streamFragNoise << "uniform float noiseTexRatio;\n"; + + if (core) + streamFragNoise << "out vec4 fragColor;\n\n"; + + streamFragNoise << "void main(void)\n"; + streamFragNoise << "{\n"; + streamFragNoise << " vec2 uv = vec2(gl_FragCoord.xy / renderTextureSize);\n"; + streamFragNoise << " " << fragColor << " = " << texture2D << "(texUnit, uv) - " << texture2D << "(noiseTexUnit, uv * noiseTexRatio);\n"; + streamFragNoise << "}\n"; - m_shaderDownsample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentDownSource); - m_shaderUpsample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentUpSource); - m_shaderCopysample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentCopySource); + streamFragNoise.flush(); - bool areShadersValid = m_shaderDownsample->isValid() && m_shaderUpsample->isValid() && m_shaderCopysample->isValid(); + + m_shaderDownsample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentDownSource); + m_shaderUpsample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentUpSource); + m_shaderCopysample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentCopySource); + m_shaderNoisesample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentNoiseSource); + + bool areShadersValid = m_shaderDownsample->isValid() && + m_shaderUpsample->isValid() && + m_shaderCopysample->isValid() && + m_shaderNoisesample->isValid(); setIsValid(areShadersValid); if (areShadersValid) { @@ -354,6 +413,10 @@ m_renderTextureSizeLocationCopysample = m_shaderCopysample->uniformLocation("renderTextureSize"); m_blurRectLocationCopysample = m_shaderCopysample->uniformLocation("blurRect"); + m_mvpMatrixLocationNoisesample = m_shaderNoisesample->uniformLocation("modelViewProjectionMatrix"); + m_renderTextureSizeLocationNoisesample = m_shaderNoisesample->uniformLocation("renderTextureSize"); + m_noiseTexRatioLocationNoisesample = m_shaderNoisesample->uniformLocation("noiseTexRatio"); + QMatrix4x4 modelViewProjection; const QSize screenSize = effects->virtualScreenSize(); modelViewProjection.ortho(0, screenSize.width(), screenSize.height(), 0, 0, 65535); @@ -379,6 +442,16 @@ m_shaderCopysample->setUniform(m_blurRectLocationCopysample, QVector4D(1.0, 1.0, 1.0, 1.0)); ShaderManager::instance()->popShader(); + ShaderManager::instance()->pushShader(m_shaderNoisesample); + m_shaderNoisesample->setUniform(m_mvpMatrixLocationNoisesample, modelViewProjection); + m_shaderNoisesample->setUniform(m_renderTextureSizeLocationNoisesample, QVector2D(1.0, 1.0)); + m_shaderNoisesample->setUniform(m_noiseTexRatioLocationNoisesample, float(1.0)); + + glUniform1i(m_shaderNoisesample->uniformLocation("texUnit"), 0); + glUniform1i(m_shaderNoisesample->uniformLocation("noiseTexUnit"), 1); + + ShaderManager::instance()->popShader(); + m_activeSampleType = -1; } }