diff --git a/effects/blur/blur.h b/effects/blur/blur.h --- a/effects/blur/blur.h +++ b/effects/blur/blur.h @@ -83,10 +83,12 @@ QRegion blurRegion(const EffectWindow *w) const; bool shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data) const; void updateBlurRegion(EffectWindow *w) const; - void doBlur(const QRegion &shape, const QRect &screen, const float opacity, const QMatrix4x4 &screenProjection, bool isDock); + void doBlur(const QRegion &shape, const QRect &screen, const float opacity, const QMatrix4x4 &screenProjection, bool isDock, QRect windowRect); 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, QRect windowShape, QPoint windowPosition); void downSampleTexture(GLVertexBuffer *vbo, int blurRectCount); void upSampleTexture(GLVertexBuffer *vbo, int blurRectCount); void copyScreenSampleTexture(GLVertexBuffer *vbo, int blurRectCount, QRegion blurShape, QSize screenSize, QMatrix4x4 screenProjection); @@ -96,6 +98,9 @@ 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 +110,8 @@ 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 @@ -27,12 +27,16 @@ #include #include +#include // for QGuiApplication +#include #include // for ceil() #include #include #include #include +#include +#include namespace KWin { @@ -143,7 +147,7 @@ // Prepare the stack for the rendering m_renderTargetStack.clear(); - m_renderTargets.reserve(m_downSampleIterations * 2 - 1); + m_renderTargetStack.reserve(m_downSampleIterations * 2); // Upsample for (int i = 1; i < m_downSampleIterations; i++) { @@ -157,6 +161,9 @@ // Copysample m_renderTargetStack.push(m_renderTargets[0]); + + // Generate the noise helper texture + generateNoiseTexture(); } void BlurEffect::initBlurStrengthValues() @@ -227,6 +234,9 @@ m_downSampleIterations = blurStrengthValues[blurStrength].iteration; m_offset = blurStrengthValues[blurStrength].offset; m_expandSize = blurOffsets[m_downSampleIterations - 1].expandSize; + m_noiseStrength = BlurConfig::noiseStrength(); + + m_scalingFactor = QGuiApplication::primaryScreen()->logicalDotsPerInch() / 96.0; updateTexture(); @@ -550,7 +560,7 @@ } if (!shape.isEmpty()) { - doBlur(shape, screen, data.opacity(), data.screenProjectionMatrix(), w->isDock()); + doBlur(shape, screen, data.opacity(), data.screenProjectionMatrix(), w->isDock(), w->geometry()); } } @@ -566,12 +576,39 @@ QRegion shape = frame->geometry().adjusted(-borderSize, -borderSize, borderSize, borderSize) & screen; if (valid && !shape.isEmpty() && region.intersects(shape.boundingRect()) && frame->style() != EffectFrameNone) { - doBlur(shape, screen, opacity * frameOpacity, frame->screenProjectionMatrix(), false); + doBlur(shape, screen, opacity * frameOpacity, frame->screenProjectionMatrix(), false, frame->geometry()); } effects->paintEffectFrame(frame, region, opacity, frameOpacity); } -void BlurEffect::doBlur(const QRegion& shape, const QRect& screen, const float opacity, const QMatrix4x4 &screenProjection, bool isDock) +void BlurEffect::generateNoiseTexture() +{ + if (m_noiseStrength == 0) { + return; + } + + // Init randomness based on time + qsrand((uint)QTime::currentTime().msec()); + + QImage noiseImage(QSize(256, 256), QImage::Format_Grayscale8); + + for (int y = 0; y < noiseImage.height(); y++) { + uint8_t *noiseImageLine = (uint8_t *) noiseImage.scanLine(y); + + for (int x = 0; x < noiseImage.width(); x++) { + noiseImageLine[x] = qrand() % m_noiseStrength + (128 - m_noiseStrength / 2); + } + } + + // The noise texture looks distorted when not scaled with integer + noiseImage = noiseImage.scaled(noiseImage.size() * m_scalingFactor); + + 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, QRect windowRect) { QRegion expandedBlurRegion = expand(shape) & expand(screen); @@ -628,24 +665,40 @@ glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); } - //Final upscale to the screen - m_shader->bind(BlurShader::UpSampleType); - m_shader->setOffset(m_offset); + upscaleRenderToScreen(vbo, blurRectCount * (m_downSampleIterations + 1), shape.rectCount() * 6, screenProjection, shape.boundingRect(), windowRect.topLeft()); - m_shader->setModelViewProjectionMatrix(screenProjection); - m_shader->setTargetSize(m_renderTextures[0].size()); + if (opacity < 1.0) { + glDisable(GL_BLEND); + } - //Copy the image from this texture + vbo->unbindArrays(); +} + +void BlurEffect::upscaleRenderToScreen(GLVertexBuffer *vbo, int vboStart, int blurRectCount, QMatrix4x4 screenProjection, QRect windowShape, QPoint windowPosition) +{ + glActiveTexture(GL_TEXTURE0); m_renderTextures[1].bind(); - //Render to the screen - vbo->draw(GL_TRIANGLES, blurRectCount * (m_downSampleIterations + 1), shape.rectCount() * 6); + if (m_noiseStrength > 0) { + m_shader->bind(BlurShader::NoiseSampleType); + m_shader->setTargetTextureSize(m_renderTextures[0].size()); + m_shader->setNoiseTextureSize(m_noiseTexture.size()); + m_shader->setTexturePosition(windowPosition); - if (opacity < 1.0) { - glDisable(GL_BLEND); + glActiveTexture(GL_TEXTURE1); + m_noiseTexture.bind(); + } else { + m_shader->bind(BlurShader::UpSampleType); + m_shader->setTargetTextureSize(m_renderTextures[0].size()); } - vbo->unbindArrays(); + m_shader->setOffset(m_offset); + m_shader->setModelViewProjectionMatrix(screenProjection); + + //Render to the screen + vbo->draw(GL_TRIANGLES, vboStart, blurRectCount); + + glActiveTexture(GL_TEXTURE0); m_shader->unbind(); } @@ -661,7 +714,7 @@ modelViewProjectionMatrix.ortho(0, m_renderTextures[i].width(), m_renderTextures[i].height(), 0 , 0, 65535); m_shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); - m_shader->setTargetSize(m_renderTextures[i].size()); + m_shader->setTargetTextureSize(m_renderTextures[i].size()); //Copy the image from this texture m_renderTextures[i - 1].bind(); @@ -680,12 +733,12 @@ m_shader->bind(BlurShader::UpSampleType); m_shader->setOffset(m_offset); - for (int i = m_downSampleIterations - 1; i > 0; i--) { + for (int i = m_downSampleIterations - 1; i >= 1; i--) { modelViewProjectionMatrix.setToIdentity(); modelViewProjectionMatrix.ortho(0, m_renderTextures[i].width(), m_renderTextures[i].height(), 0 , 0, 65535); m_shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); - m_shader->setTargetSize(m_renderTextures[i].size()); + m_shader->setTargetTextureSize(m_renderTextures[i].size()); //Copy the image from this texture m_renderTextures[i + 1].bind(); @@ -702,7 +755,7 @@ m_shader->bind(BlurShader::CopySampleType); m_shader->setModelViewProjectionMatrix(screenProjection); - m_shader->setTargetSize(screenSize); + m_shader->setTargetTextureSize(screenSize); /* * This '1' sized adjustment is necessary do avoid windows affecting the blur that are 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 + + 5 + 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 + 184 - + - Strength of the effect: + Blur strength: @@ -37,7 +37,7 @@ - + Light @@ -69,7 +69,70 @@ - + + + Strong + + + + + + + + + Noise strength: + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Light + + + + + + + 14 + + + 5 + + + 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 @@ -45,13 +45,16 @@ virtual void setModelViewProjectionMatrix(const QMatrix4x4 &matrix) = 0; virtual void setOffset(float offset) = 0; - virtual void setTargetSize(QSize renderTextureSize) = 0; + virtual void setTargetTextureSize(QSize renderTextureSize) = 0; + virtual void setNoiseTextureSize(QSize noiseTextureSize) = 0; + virtual void setTexturePosition(QPoint texPos) = 0; virtual void setBlurRect(QRect blurRect, QSize screenSize) = 0; enum SampleType { DownSampleType, UpSampleType, - CopySampleType + CopySampleType, + NoiseSampleType }; virtual void bind(SampleType sampleType) = 0; @@ -83,7 +86,9 @@ void unbind() override final; void setModelViewProjectionMatrix(const QMatrix4x4 &matrix) override final; void setOffset(float offset) override final; - void setTargetSize(QSize renderTextureSize) override final; + void setTargetTextureSize(QSize renderTextureSize) override final; + void setNoiseTextureSize(QSize noiseTextureSize) override final; + void setTexturePosition(QPoint texPos) override final; void setBlurRect(QRect blurRect, QSize screenSize) override final; protected: @@ -94,6 +99,7 @@ GLShader *m_shaderDownsample = nullptr; GLShader *m_shaderUpsample = nullptr; GLShader *m_shaderCopysample = nullptr; + GLShader *m_shaderNoisesample = nullptr; int m_mvpMatrixLocationDownsample; int m_offsetLocationDownsample; @@ -109,22 +115,30 @@ int m_renderTextureSizeLocationCopysample; int m_blurRectLocationCopysample; + int m_mvpMatrixLocationNoisesample; + int m_offsetLocationNoisesample; + int m_renderTextureSizeLocationNoisesample; + int m_noiseTextureSizeLocationNoisesample; + int m_texStartPosLocationNoisesample; + int m_halfpixelLocationNoisesample; + //Caching uniform values to aviod unnecessary setUniform calls int m_activeSampleType; float m_offsetDownsample; QMatrix4x4 m_matrixDownsample; - QSize m_renderTextureSizeDownsample; float m_offsetUpsample; QMatrix4x4 m_matrixUpsample; - QSize m_renderTextureSizeUpsample; QMatrix4x4 m_matrixCopysample; - QSize m_renderTextureSizeCopysample; QRect m_blurRectCopysample; + float m_offsetNoisesample; + QVector2D m_noiseTextureSizeNoisesample; + QMatrix4x4 m_matrixNoisesample; + }; } // 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; } } @@ -129,45 +140,61 @@ m_offsetDownsample = offset; m_shaderDownsample->setUniform(m_offsetLocationDownsample, offset); break; + + case NoiseSampleType: + if (offset == m_offsetNoisesample) + return; + + m_offsetNoisesample = offset; + m_shaderNoisesample->setUniform(m_offsetLocationNoisesample, offset); + break; } } -void GLSLBlurShader::setTargetSize(QSize renderTextureSize) +void GLSLBlurShader::setTargetTextureSize(QSize renderTextureSize) { if (!isValid()) return; QVector2D texSize = QVector2D(renderTextureSize.width(), renderTextureSize.height()); switch (m_activeSampleType) { case CopySampleType: - if (renderTextureSize == m_renderTextureSizeCopysample) - return; - - m_renderTextureSizeCopysample = renderTextureSize; m_shaderCopysample->setUniform(m_renderTextureSizeLocationCopysample, texSize); break; case UpSampleType: - if (renderTextureSize == m_renderTextureSizeUpsample) - return; - - m_renderTextureSizeUpsample = renderTextureSize; m_shaderUpsample->setUniform(m_renderTextureSizeLocationUpsample, texSize); m_shaderUpsample->setUniform(m_halfpixelLocationUpsample, QVector2D(0.5 / texSize.x(), 0.5 / texSize.y())); break; case DownSampleType: - if (renderTextureSize == m_renderTextureSizeDownsample) - return; - - m_renderTextureSizeDownsample = renderTextureSize; m_shaderDownsample->setUniform(m_renderTextureSizeLocationDownsample, texSize); m_shaderDownsample->setUniform(m_halfpixelLocationDownsample, QVector2D(0.5 / texSize.x(), 0.5 / texSize.y())); break; + + case NoiseSampleType: + m_shaderNoisesample->setUniform(m_renderTextureSizeLocationNoisesample, texSize); + m_shaderNoisesample->setUniform(m_halfpixelLocationNoisesample, QVector2D(0.5 / texSize.x(), 0.5 / texSize.y())); + break; } } +void GLSLBlurShader::setNoiseTextureSize(QSize noiseTextureSize) +{ + QVector2D noiseTexSize = QVector2D(noiseTextureSize.width(), noiseTextureSize.height()); + + if (noiseTexSize != m_noiseTextureSizeNoisesample) { + m_noiseTextureSizeNoisesample = noiseTexSize; + m_shaderNoisesample->setUniform(m_noiseTextureSizeLocationNoisesample, noiseTexSize); + } +} + +void GLSLBlurShader::setTexturePosition(QPoint texPos) +{ + m_shaderNoisesample->setUniform(m_texStartPosLocationNoisesample, QVector2D(-texPos.x(), texPos.y())); +} + void GLSLBlurShader::setBlurRect(QRect blurRect, QSize screenSize) { if (!isValid() || blurRect == m_blurRectCopysample) @@ -177,9 +204,9 @@ 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); @@ -202,6 +229,10 @@ case DownSampleType: ShaderManager::instance()->pushShader(m_shaderDownsample); break; + + case NoiseSampleType: + ShaderManager::instance()->pushShader(m_shaderNoisesample); + break; } m_activeSampleType = sampleType; @@ -222,6 +253,7 @@ QByteArray fragmentDownSource; QByteArray fragmentUpSource; QByteArray fragmentCopySource; + QByteArray fragmentNoiseSource; const QByteArray attribute = core ? "in" : "attribute"; const QByteArray texture2D = core ? "texture" : "texture2D"; @@ -331,12 +363,46 @@ streamFragCopy.flush(); - - m_shaderDownsample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentDownSource); - m_shaderUpsample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentUpSource); - m_shaderCopysample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentCopySource); - - bool areShadersValid = m_shaderDownsample->isValid() && m_shaderUpsample->isValid() && m_shaderCopysample->isValid(); + // Fragment shader - Noise texture + // =================================================================== + QTextStream streamFragNoise(&fragmentNoiseSource); + + streamFragNoise << glHeaderString << glUniformString; + + streamFragNoise << "uniform sampler2D noiseTexUnit;\n"; + streamFragNoise << "uniform vec2 noiseTextureSize;\n"; + streamFragNoise << "uniform vec2 texStartPos;\n"; + + // Upsampling + Noise + streamFragNoise << "void main(void)\n"; + streamFragNoise << "{\n"; + streamFragNoise << " vec2 uv = vec2(gl_FragCoord.xy / renderTextureSize);\n"; + streamFragNoise << " vec2 uvNoise = vec2((texStartPos.xy + gl_FragCoord.xy) / noiseTextureSize);\n"; + streamFragNoise << " \n"; + streamFragNoise << " vec4 sum = " << texture2D << "(texUnit, uv + vec2(-halfpixel.x * 2.0, 0.0) * offset);\n"; + streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(-halfpixel.x, halfpixel.y) * offset) * 2.0;\n"; + streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(0.0, halfpixel.y * 2.0) * offset);\n"; + streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x, halfpixel.y) * offset) * 2.0;\n"; + streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x * 2.0, 0.0) * offset);\n"; + streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x, -halfpixel.y) * offset) * 2.0;\n"; + streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(0.0, -halfpixel.y * 2.0) * offset);\n"; + streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(-halfpixel.x, -halfpixel.y) * offset) * 2.0;\n"; + streamFragNoise << " \n"; + streamFragNoise << " " << fragColor << " = sum / 12.0 - (vec4(0.5, 0.5, 0.5, 0) - vec4(" << texture2D << "(noiseTexUnit, uvNoise).rrr, 0));\n"; + streamFragNoise << "}\n"; + + streamFragNoise.flush(); + + + 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 +420,13 @@ m_renderTextureSizeLocationCopysample = m_shaderCopysample->uniformLocation("renderTextureSize"); m_blurRectLocationCopysample = m_shaderCopysample->uniformLocation("blurRect"); + m_mvpMatrixLocationNoisesample = m_shaderNoisesample->uniformLocation("modelViewProjectionMatrix"); + m_offsetLocationNoisesample = m_shaderNoisesample->uniformLocation("offset"); + m_renderTextureSizeLocationNoisesample = m_shaderNoisesample->uniformLocation("renderTextureSize"); + m_noiseTextureSizeLocationNoisesample = m_shaderNoisesample->uniformLocation("noiseTextureSize"); + m_texStartPosLocationNoisesample = m_shaderNoisesample->uniformLocation("texStartPos"); + m_halfpixelLocationNoisesample = m_shaderNoisesample->uniformLocation("halfpixel"); + QMatrix4x4 modelViewProjection; const QSize screenSize = effects->virtualScreenSize(); modelViewProjection.ortho(0, screenSize.width(), screenSize.height(), 0, 0, 65535); @@ -379,6 +452,32 @@ 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_offsetLocationNoisesample, float(1.0)); + m_shaderNoisesample->setUniform(m_renderTextureSizeLocationNoisesample, QVector2D(1.0, 1.0)); + m_shaderNoisesample->setUniform(m_noiseTextureSizeLocationNoisesample, QVector2D(1.0, 1.0)); + m_shaderNoisesample->setUniform(m_texStartPosLocationNoisesample, QVector2D(1.0, 1.0)); + m_shaderNoisesample->setUniform(m_halfpixelLocationNoisesample, QVector2D(1.0, 1.0)); + + glUniform1i(m_shaderNoisesample->uniformLocation("texUnit"), 0); + glUniform1i(m_shaderNoisesample->uniformLocation("noiseTexUnit"), 1); + + ShaderManager::instance()->popShader(); + m_activeSampleType = -1; + + m_offsetDownsample = 0.0; + m_matrixDownsample = QMatrix4x4(); + + m_offsetUpsample = 0.0; + m_matrixUpsample = QMatrix4x4(); + + m_matrixCopysample = QMatrix4x4(); + m_blurRectCopysample = QRect(); + + m_offsetNoisesample = 0.0; + m_noiseTextureSizeNoisesample = QVector2D(); + m_matrixNoisesample = QMatrix4x4(); } } diff --git a/libkwineffects/kwingltexture.cpp b/libkwineffects/kwingltexture.cpp --- a/libkwineffects/kwingltexture.cpp +++ b/libkwineffects/kwingltexture.cpp @@ -125,6 +125,8 @@ { GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV }, // QImage::Format_A2BGR30_Premultiplied { GL_RGB10, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV }, // QImage::Format_RGB30 { GL_RGB10_A2, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV }, // QImage::Format_A2RGB30_Premultiplied + { GL_R8, GL_RED, GL_UNSIGNED_BYTE }, // QImage::Format_Alpha8 + { GL_R8, GL_RED, GL_UNSIGNED_BYTE }, // QImage::Format_Grayscale8 }; QImage im;