diff --git a/.arcconfig b/.arcconfig deleted file mode 100644 --- a/.arcconfig +++ /dev/null @@ -1,4 +0,0 @@ -{ - "phabricator.uri" : "https://phabricator.kde.org/" -} - diff --git a/effects/blur/blur.h b/effects/blur/blur.h --- a/effects/blur/blur.h +++ b/effects/blur/blur.h @@ -26,6 +26,7 @@ #include #include +#include namespace KWayland { @@ -43,7 +44,6 @@ class BlurEffect : public KWin::Effect { Q_OBJECT - Q_PROPERTY(int blurRadius READ blurRadius) Q_PROPERTY(bool cacheTexture READ isCacheTexture) public: BlurEffect(); @@ -59,7 +59,6 @@ void paintEffectFrame(EffectFrame *frame, QRegion region, double opacity, double frameOpacity); // for dynamic setting extraction - int blurRadius() const; bool isCacheTexture() const { return m_shouldCache; } @@ -76,36 +75,54 @@ void slotScreenGeometryChanged(); private: + void initBlurStrengthData(); void updateTexture(); QRect expand(const QRect &rect) const; QRegion expand(const QRegion ®ion) const; QRegion blurRegion(const EffectWindow *w) const; - bool shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data) const; + bool shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data); void updateBlurRegion(EffectWindow *w) const; void doSimpleBlur(EffectWindow *w, const float opacity, const QMatrix4x4 &screenProjection); void doBlur(const QRegion &shape, const QRect &screen, const float opacity, const QMatrix4x4 &screenProjection); void doCachedBlur(EffectWindow *w, const QRegion& region, const float opacity, const QMatrix4x4 &screenProjection); - void uploadRegion(QVector2D *&map, const QRegion ®ion); - void uploadGeometry(GLVertexBuffer *vbo, const QRegion &horizontal, const QRegion &vertical); + void uploadRegion(QVector2D *&map, const QRegion ®ion, const int downSampleIterations); + void uploadGeometry(GLVertexBuffer *vbo, const QRegion &blurRegion, const QRegion &renderRegion, const QRegion &windowRegion); + bool renderTargetsValid(); private: - BlurShader *shader; GLShader *m_simpleShader; - GLRenderTarget *target = nullptr; - GLTexture tex; + GLRenderTarget *m_simpleTarget; + + BlurShader *shader; + QVector renderTargets; + QVector renderTextures; long net_wm_blur_region; QRegion m_damagedArea; // keeps track of the area which has been damaged (from bottom to top) QRegion m_paintedArea; // actually painted area which is greater than m_damagedArea QRegion m_currentBlur; // keeps track of the currently blured area of non-caching windows(from bottom to top) bool m_shouldCache; + bool m_useSimpleBlur; + + int m_downSampleIterations; // number of times the texture will be downsized to half size + int m_offset; + int m_expandSize; struct BlurWindowInfo { - GLTexture blurredBackground; // keeps the horizontally blurred background + GLTexture blurredBackground; + QSize windowSize; QRegion damagedRegion; QPoint windowPos; bool dropCache; QMetaObject::Connection blurChangedConnection; }; + + struct BlurStrenghtData { + int downSampleIterations; + int offset; + int expandSize; + }; + + QVector blurConfigData; QHash< const EffectWindow*, BlurWindowInfo > windows; typedef QHash::iterator CacheEntry; diff --git a/effects/blur/blur.cpp b/effects/blur/blur.cpp --- a/effects/blur/blur.cpp +++ b/effects/blur/blur.cpp @@ -32,6 +32,8 @@ #include #include +#define BORDER_SIZE 5 + namespace KWin { @@ -42,16 +44,18 @@ initConfig(); shader = BlurShader::create(); m_simpleShader = ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture, QString(), QStringLiteral("logout-blur.frag")); + m_simpleTarget = new GLRenderTarget(GLTexture(GL_RGBA8, 1, 1)); + if (!m_simpleShader->isValid()) { qCDebug(KWINEFFECTS) << "Simple blur shader failed to load"; } - updateTexture(); + initBlurStrengthData(); reconfigure(ReconfigureAll); // ### Hackish way to announce support. // Should be included in _NET_SUPPORTED instead. - if (shader && shader->isValid() && target->valid()) { + if (shader && shader->isValid() && renderTargetsValid()) { net_wm_blur_region = effects->announceSupportProperty(s_blurAtomName, this); KWayland::Server::Display *display = effects->waylandDisplay(); if (display) { @@ -68,7 +72,7 @@ connect(effects, SIGNAL(screenGeometryChanged(QSize)), this, SLOT(slotScreenGeometryChanged())); connect(effects, &EffectsHandler::xcbConnectionChanged, this, [this] { - if (shader && shader->isValid() && target->valid()) { + if (shader && shader->isValid() && renderTargetsValid()) { net_wm_blur_region = effects->announceSupportProperty(s_blurAtomName, this); } } @@ -84,8 +88,20 @@ windows.clear(); delete m_simpleShader; + m_simpleShader = NULL; + delete shader; - delete target; + shader = NULL; + + for (int i = 0; i < renderTargets.size(); i++) { + delete renderTargets[i]; + renderTargets[i] = NULL; + + renderTextures[i].discard(); + } + + renderTargets.clear(); + renderTextures.clear(); } void BlurEffect::slotScreenGeometryChanged() @@ -98,28 +114,111 @@ effects->doneOpenGLContextCurrent(); } -void BlurEffect::updateTexture() { - delete target; - // Offscreen texture that's used as the target for the horizontal blur pass - // and the source for the vertical pass. - tex = GLTexture(GL_RGBA8, effects->virtualScreenSize()); - tex.setFilter(GL_LINEAR); - tex.setWrapMode(GL_CLAMP_TO_EDGE); +bool BlurEffect::renderTargetsValid() +{ + if (renderTargets.isEmpty()) { + return false; + } + + for (int i = 0; i < renderTargets.size(); i++) { + if (!renderTargets[i]->valid()) { + return false; + } + } + + return true; +} +void BlurEffect::initBlurStrengthData() +{ + /** + * Explanation for the magic numbers: + * + * The texture blur amount is depends on the downsampling iterations and the offset value. + * By changing the offset we can alter the blur amount without relying on further downsampling. + * But there is a maximum value of offset per downsample iteration before we get artifacts. + * For example the maximum value of offset before there is almost clearly visible artifacts: + * - for 1 iteration is 2 + * - for 2 iterations is 4 + * - for 3 iterations is 8 + * + * For good amount of blur (at current common resolutions) we need at least 4 iterations. + * Offset value of 1 on 4 iterations is still too much blur to consider it using it as minimum. + * This means we can't have constant iteration value. + * If we left the offset value constant and only changed the iteration number then we would + * have good minimum and maximum blur amount, but there would be around only 5 blur strength + * steps. + * This means we can't have constant offset value. + * As a compromise we have to use hardcoded magic numbers. This works for common resolutions. + * In the future as we enter the age of high resolution monitors (8K and above) we have to + * either increase the iterations or add more blur strength steps with increased iterations. + * For now it would be pointless to add more blur strength, as the currently defined maximum + * is (in my opinion) more than enough. + **/ + + // Dummy data needed, because the blur strength range is 1-15 + blurConfigData.append({0, 0, 0}); + + blurConfigData.append({1, 1, 5}); + blurConfigData.append({2, 1, 10}); + blurConfigData.append({2, 2, 15}); + blurConfigData.append({2, 3, 25}); + blurConfigData.append({2, 4, 30}); + blurConfigData.append({3, 3, 40}); + blurConfigData.append({3, 4, 50}); + blurConfigData.append({3, 5, 60}); + blurConfigData.append({3, 6, 80}); + blurConfigData.append({3, 7, 100}); + blurConfigData.append({3, 8, 120}); + blurConfigData.append({4, 5, 140}); + blurConfigData.append({4, 6, 155}); + blurConfigData.append({4, 7, 170}); + blurConfigData.append({4, 8, 200}); +} - target = new GLRenderTarget(tex); +void BlurEffect::updateTexture() +{ + for (int i = 0; i < renderTargets.size(); i++) { + delete renderTargets[i]; + renderTargets[i] = NULL; + + renderTextures[i].discard(); + } + + renderTargets.clear(); + renderTextures.clear(); + + for (int i = 0; i <= m_downSampleIterations; i++) { + renderTextures.append(GLTexture(GL_RGBA8, effects->virtualScreenSize() / qPow(2, i))); + renderTextures.last().setFilter(GL_LINEAR); + renderTextures.last().setWrapMode(GL_CLAMP_TO_EDGE); + + renderTargets.append(new GLRenderTarget(renderTextures.last())); + } + + // This last set is used as a temporary helper texture + renderTextures.append(GLTexture(GL_RGBA8, effects->virtualScreenSize())); + renderTextures.last().setFilter(GL_LINEAR); + renderTextures.last().setWrapMode(GL_CLAMP_TO_EDGE); + + renderTargets.append(new GLRenderTarget(renderTextures.last())); } void BlurEffect::reconfigure(ReconfigureFlags flags) { Q_UNUSED(flags) - + BlurConfig::self()->read(); - int radius = qBound(2, BlurConfig::blurRadius(), 14); - if (shader) - shader->setRadius(radius); - + m_shouldCache = BlurConfig::cacheTexture(); - + m_useSimpleBlur = BlurConfig::useSimpleBlur(); + + int blurStrength = BlurConfig::blurRadius(); + m_downSampleIterations = blurConfigData[blurStrength].downSampleIterations; + m_offset = blurConfigData[blurStrength].offset; + m_expandSize = blurConfigData[blurStrength].expandSize; + + updateTexture(); + windows.clear(); if (!shader || !shader->isValid()) { @@ -171,7 +270,7 @@ if (surf) { windows[w].blurChangedConnection = connect(surf, &KWayland::Server::SurfaceInterface::blurChanged, this, [this, w] () { - + if (w) { updateBlurRegion(w); } @@ -230,8 +329,7 @@ QRect BlurEffect::expand(const QRect &rect) const { - const int radius = shader->radius(); - return rect.adjusted(-radius, -radius, radius, radius); + return rect.adjusted(-m_expandSize, -m_expandSize, m_expandSize, m_expandSize); } QRegion BlurEffect::expand(const QRegion ®ion) const @@ -274,35 +372,43 @@ return region; } -void BlurEffect::uploadRegion(QVector2D *&map, const QRegion ®ion) +void BlurEffect::uploadRegion(QVector2D *&map, const QRegion ®ion, const int downSampleIterations) { - for (const QRect &r : region) { - const QVector2D topLeft(r.x(), r.y()); - const QVector2D topRight(r.x() + r.width(), r.y()); - const QVector2D bottomLeft(r.x(), r.y() + r.height()); - const QVector2D bottomRight(r.x() + r.width(), r.y() + r.height()); - - // First triangle - *(map++) = topRight; - *(map++) = topLeft; - *(map++) = bottomLeft; - - // Second triangle - *(map++) = bottomLeft; - *(map++) = bottomRight; - *(map++) = topRight; + for (int i = 0; i <= downSampleIterations; i++) { + const int divisionRatio = qPow(2, i); + + for (const QRect &r : region) { + const QVector2D topLeft( r.x() / divisionRatio, r.y() / divisionRatio); + const QVector2D topRight( (r.x() + r.width()) / divisionRatio, r.y() / divisionRatio); + const QVector2D bottomLeft( r.x() / divisionRatio, (r.y() + r.height()) / divisionRatio); + const QVector2D bottomRight((r.x() + r.width()) / divisionRatio, (r.y() + r.height()) / divisionRatio); + + // First triangle + *(map++) = topRight; + *(map++) = topLeft; + *(map++) = bottomLeft; + + // Second triangle + *(map++) = bottomLeft; + *(map++) = bottomRight; + *(map++) = topRight; + } } } -void BlurEffect::uploadGeometry(GLVertexBuffer *vbo, const QRegion &horizontal, const QRegion &vertical) +void BlurEffect::uploadGeometry(GLVertexBuffer *vbo, const QRegion &blurRegion, const QRegion &renderRegion, const QRegion &windowRegion) { - const int vertexCount = (horizontal.rectCount() + vertical.rectCount()) * 6; + const int vertexCount = ((blurRegion.rectCount() * (m_downSampleIterations + 1)) + renderRegion.rectCount() + windowRegion.rectCount()) * 6; + if (!vertexCount) return; QVector2D *map = (QVector2D *) vbo->map(vertexCount * sizeof(QVector2D)); - uploadRegion(map, horizontal); - uploadRegion(map, vertical); + + uploadRegion(map, blurRegion, m_downSampleIterations); + uploadRegion(map, renderRegion, 0); + uploadRegion(map, windowRegion, 0); + vbo->unmap(); const GLVertexAttrib layout[] = { @@ -338,9 +444,8 @@ // to blur an area partially we have to shrink the opaque area of a window QRegion newClip; const QRegion oldClip = data.clip; - const int radius = shader->radius(); for (const QRect &rect : data.clip) { - newClip |= rect.adjusted(radius,radius,-radius,-radius); + newClip |= rect.adjusted(m_expandSize, m_expandSize, -m_expandSize, -m_expandSize); } data.clip = newClip; @@ -360,15 +465,15 @@ const QRegion expandedBlur = expand(blurArea) & screen; if (m_shouldCache && !w->isDeleted()) { - // we are caching the horizontally blurred background texture + // we are caching the blurred background texture // if a window underneath the blurred area is damaged we have to // update the cached texture QRegion damagedCache; CacheEntry it = windows.find(w); if (it != windows.end() && !it->dropCache && it->windowPos == w->pos() && - it->blurredBackground.size() == expandedBlur.boundingRect().size()) { + it->windowSize == expandedBlur.boundingRect().size()) { damagedCache = (expand(expandedBlur & m_damagedArea) | (it->damagedRegion & data.paint)) & expandedBlur; } else { @@ -426,9 +531,9 @@ m_paintedArea |= data.paint; } -bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data) const +bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data) { - if (!target->valid() || !shader || !shader->isValid()) + if (!renderTargetsValid() || !shader || !shader->isValid()) return false; if (effects->activeFullScreenEffect() && !w->data(WindowForceBlurRole).toBool()) @@ -483,7 +588,11 @@ if (!shape.isEmpty()) { if (w->isFullScreen() && GLRenderTarget::blitSupported() && m_simpleShader->isValid() && !GLPlatform::instance()->supports(LimitedNPOT) && shape.boundingRect() == w->geometry()) { - doSimpleBlur(w, data.opacity(), data.screenProjectionMatrix()); + if (m_useSimpleBlur) { + doSimpleBlur(w, data.opacity(), data.screenProjectionMatrix()); + } else { + doBlur(shape, screen, data.opacity(), data.screenProjectionMatrix()); + } } else if (m_shouldCache && !translated && !w->isDeleted()) { doCachedBlur(w, region, data.opacity(), data.screenProjectionMatrix()); } else { @@ -499,8 +608,9 @@ void BlurEffect::paintEffectFrame(EffectFrame *frame, QRegion region, double opacity, double frameOpacity) { const QRect screen = effects->virtualScreenGeometry(); - bool valid = target->valid() && shader && shader->isValid(); - QRegion shape = frame->geometry().adjusted(-5, -5, 5, 5) & screen; + bool valid = renderTargetsValid() && shader && shader->isValid(); + QRegion shape = frame->geometry().adjusted(-BORDER_SIZE, -BORDER_SIZE, BORDER_SIZE, BORDER_SIZE) & screen; + if (valid && !shape.isEmpty() && region.intersects(shape.boundingRect()) && frame->style() != EffectFrameNone) { doBlur(shape, screen, opacity * frameOpacity, frame->screenProjectionMatrix()); } @@ -514,8 +624,8 @@ blurTexture.setFilter(GL_LINEAR_MIPMAP_LINEAR); blurTexture.setWrapMode(GL_CLAMP_TO_EDGE); - target->attachTexture(blurTexture); - target->blitFromFramebuffer(w->geometry(), QRect(QPoint(0, 0), w->size())); + m_simpleTarget->attachTexture(blurTexture); + m_simpleTarget->blitFromFramebuffer(w->geometry(), QRect(QPoint(0, 0), w->size())); // Unmodified base image ShaderBinder binder(m_simpleShader); @@ -534,61 +644,85 @@ void BlurEffect::doBlur(const QRegion& shape, const QRect& screen, const float opacity, const QMatrix4x4 &screenProjection) { - const QRegion expanded = expand(shape) & screen; - const QRect r = expanded.boundingRect(); - - // Upload geometry for the horizontal and vertical passes + QRegion expandedBlurRegion = expand(shape) & expand(screen); + QRect expandedBlurRect = expandedBlurRegion.boundingRect() & screen; + + // Upload geometry for the down and upsample iterations GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); - uploadGeometry(vbo, expanded, shape); + uploadGeometry(vbo, expandedBlurRegion, QRegion(), shape); vbo->bindArrays(); - - const qreal scale = GLRenderTarget::virtualScreenScale(); - - // Create a scratch texture and copy the area in the back buffer that we're - // going to blur into it - // for HIGH DPI scratch is captured in native resolution, it is then implicitly downsampled - // when rendering into tex - GLTexture scratch(GL_RGBA8, r.width() * scale, r.height() * scale); - scratch.setFilter(GL_LINEAR); - scratch.setWrapMode(GL_CLAMP_TO_EDGE); - scratch.bind(); - - const QRect sg = GLRenderTarget::virtualScreenGeometry(); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, (r.x() - sg.x()) * scale, (sg.height() - sg.y() - r.y() - r.height()) * scale, - scratch.width(), scratch.height()); - - // Draw the texture on the offscreen framebuffer object, while blurring it horizontally - target->attachTexture(tex); - GLRenderTarget::pushRenderTarget(target); - - shader->bind(); - shader->setDirection(Qt::Horizontal); - shader->setPixelDistance(1.0 / r.width()); - - QMatrix4x4 modelViewProjectionMatrix; - modelViewProjectionMatrix.ortho(0, tex.width(), tex.height(), 0 , 0, 65535); - shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); - - // Set up the texture matrix to transform from screen coordinates - // to texture coordinates. - QMatrix4x4 textureMatrix; - textureMatrix.scale(1.0 / r.width(), -1.0 / r.height(), 1); - textureMatrix.translate(-r.x(), (-r.height() - r.y()), 0); - shader->setTextureMatrix(textureMatrix); - - vbo->draw(GL_TRIANGLES, 0, expanded.rectCount() * 6); - + + int blurRectCount = expandedBlurRegion.rectCount() * 6; + + // Copy part of the screen into this texture + renderTextures.last().bind(); + glCopyTexSubImage2D( + GL_TEXTURE_2D, + 0, + expandedBlurRect.x(), + effects->virtualScreenSize().height() - expandedBlurRect.y() - expandedBlurRect.height(), + expandedBlurRect.x(), + effects->virtualScreenSize().height() - expandedBlurRect.y() - expandedBlurRect.height(), + expandedBlurRect.width(), + expandedBlurRect.height() + ); + + shader->bind(BlurShader::copySampleType); + + shader->setModelViewProjectionMatrix(screenProjection); + shader->setTextureSize(screen.size()); + shader->setBlurRect(shape.boundingRect().adjusted(BORDER_SIZE, BORDER_SIZE, -BORDER_SIZE, -BORDER_SIZE), screen.size()); + + GLRenderTarget::pushRenderTarget(renderTargets[0]); + vbo->draw(GL_TRIANGLES, 0, blurRectCount); GLRenderTarget::popRenderTarget(); - scratch.unbind(); - scratch.discard(); - - // Now draw the horizontally blurred area back to the backbuffer, while - // blurring it vertically and clipping it to the window shape. - tex.bind(); - - shader->setDirection(Qt::Vertical); - shader->setPixelDistance(1.0 / tex.height()); + + shader->unbind(); + QMatrix4x4 modelViewProjectionMatrix; + + //Downsample + shader->bind(BlurShader::downSampleType); + shader->setOffset(m_offset); + + for (int i = 1; i <= m_downSampleIterations; i++) { + modelViewProjectionMatrix.setToIdentity(); + modelViewProjectionMatrix.ortho(0, renderTextures[i].width(), renderTextures[i].height(), 0 , 0, 65535); + + shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); + shader->setTextureSize(renderTextures[i].size()); + + //Copy the image from this texture + renderTextures[i - 1].bind(); + + //Render to the texture attached to this FrameBufferObject + GLRenderTarget::pushRenderTarget(renderTargets[i]); + vbo->draw(GL_TRIANGLES, blurRectCount * i, blurRectCount); + GLRenderTarget::popRenderTarget(); + } + + shader->unbind(); + + //Upsample + shader->bind(BlurShader::upSampleType); + shader->setOffset(m_offset); + + for (int i = m_downSampleIterations - 1; i > 0; i--) { + modelViewProjectionMatrix.setToIdentity(); + modelViewProjectionMatrix.ortho(0, renderTextures[i].width(), renderTextures[i].height(), 0 , 0, 65535); + + shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); + shader->setTextureSize(renderTextures[i].size()); + + //Copy the image from this texture + renderTextures[i + 1].bind(); + + //Render to the texture attached to this FrameBufferObject + GLRenderTarget::pushRenderTarget(renderTargets[i]); + vbo->draw(GL_TRIANGLES, blurRectCount * i, blurRectCount); + GLRenderTarget::popRenderTarget(); + } + // Modulate the blurred texture with the window opacity if the window isn't opaque if (opacity < 1.0) { glEnable(GL_BLEND); @@ -603,58 +737,50 @@ glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); } - // Set the up the texture matrix to transform from screen coordinates - // to texture coordinates. - textureMatrix.setToIdentity(); - textureMatrix.scale(1.0 / tex.width(), -1.0 / tex.height(), 1); - textureMatrix.translate(0, -tex.height(), 0); - shader->setTextureMatrix(textureMatrix); + //Final upscale to the screen shader->setModelViewProjectionMatrix(screenProjection); + shader->setTextureSize(renderTextures[0].size()); + + //Copy the image from this texture + renderTextures[1].bind(); - vbo->draw(GL_TRIANGLES, expanded.rectCount() * 6, shape.rectCount() * 6); - vbo->unbindArrays(); + //Render to the screen + vbo->draw(GL_TRIANGLES, blurRectCount * (m_downSampleIterations + 1), shape.rectCount() * 6); if (opacity < 1.0) { glDisable(GL_BLEND); } - tex.unbind(); + vbo->unbindArrays(); shader->unbind(); } void BlurEffect::doCachedBlur(EffectWindow *w, const QRegion& region, const float opacity, const QMatrix4x4 &screenProjection) { - const QRect screen = effects->virtualScreenGeometry(); - const QRegion blurredRegion = blurRegion(w).translated(w->pos()) & screen; - const QRegion expanded = expand(blurredRegion) & screen; - const QRect r = expanded.boundingRect(); - - // The background texture we get is only partially valid. - + const QRect screenRect = effects->virtualScreenGeometry(); + const QRegion blurredRegion = blurRegion(w).translated(w->pos()) & screenRect; + + QRegion expandedBlurredRegion = expand(blurredRegion) & expand(screenRect); + QRect expandedBlurredRect = expandedBlurredRegion.boundingRect() & screenRect; + CacheEntry it = windows.find(w); if (it == windows.end()) { BlurWindowInfo bwi; - bwi.blurredBackground = GLTexture(GL_RGBA8, r.width(),r.height()); - bwi.damagedRegion = expanded; + bwi.blurredBackground = GLTexture(GL_RGBA8, screenRect.width(),screenRect.height()); + bwi.windowSize = expandedBlurredRect.size(); + bwi.damagedRegion = expandedBlurredRegion; bwi.dropCache = false; bwi.windowPos = w->pos(); it = windows.insert(w, bwi); - } else if (it->blurredBackground.size() != r.size()) { - it->blurredBackground = GLTexture(GL_RGBA8, r.width(),r.height()); + } else if (it->windowSize != expandedBlurredRect.size()) { + it->windowSize = expandedBlurredRect.size(); it->dropCache = false; it->windowPos = w->pos(); } else if (it->windowPos != w->pos()) { it->dropCache = false; it->windowPos = w->pos(); } - - GLTexture targetTexture = it->blurredBackground; - targetTexture.setFilter(GL_LINEAR); - targetTexture.setWrapMode(GL_CLAMP_TO_EDGE); - shader->bind(); - QMatrix4x4 textureMatrix; - QMatrix4x4 modelViewProjectionMatrix; - + /** * Which part of the background texture can be updated ? * @@ -686,99 +812,137 @@ * can do so (e.g. SlidingPopups). Hence we have to make the compromise that we update * "damagedRegion & region" of the cache but only mark "validUpdate" as valid. **/ - const QRegion damagedRegion = it->damagedRegion; - const QRegion updateBackground = damagedRegion & region; - const QRegion validUpdate = damagedRegion - expand(damagedRegion - region); - - const QRegion horizontal = validUpdate.isEmpty() ? QRegion() : (updateBackground & screen); - const QRegion vertical = blurredRegion & region; - - const int horizontalOffset = 0; - const int horizontalCount = horizontal.rectCount() * 6; - - const int verticalOffset = horizontalCount; - const int verticalCount = vertical.rectCount() * 6; - + + const QRegion validUpdateRegion = it->damagedRegion - expand(it->damagedRegion - region); + + QRegion windowRegion = blurredRegion & region; + QRegion renderRegion = it->damagedRegion & region & windowRegion; + QRegion blurRegion = expand(renderRegion); + GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); - uploadGeometry(vbo, horizontal, vertical); - + uploadGeometry(vbo, blurRegion, renderRegion, windowRegion); vbo->bindArrays(); - - if (!validUpdate.isEmpty()) { - const QRect updateRect = (expand(updateBackground) & expanded).boundingRect(); - // First we have to copy the background from the frontbuffer - // into a scratch texture (in this case "tex"). - tex.bind(); - - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, updateRect.x(), effects->virtualScreenSize().height() - updateRect.y() - updateRect.height(), - updateRect.width(), updateRect.height()); - - // Draw the texture on the offscreen framebuffer object, while blurring it horizontally - target->attachTexture(targetTexture); - GLRenderTarget::pushRenderTarget(target); - - shader->setDirection(Qt::Horizontal); - shader->setPixelDistance(1.0 / tex.width()); - - modelViewProjectionMatrix.ortho(0, r.width(), r.height(), 0 , 0, 65535); - modelViewProjectionMatrix.translate(-r.x(), -r.y(), 0); + + int blurRectCount = blurRegion.rectCount() * 6; + + if (!validUpdateRegion.isEmpty()) { + QMatrix4x4 modelViewProjectionMatrix; + QRect blurRect = blurRegion.boundingRect() & screenRect; + + // Copy part of the screen into this texture + renderTextures.last().bind(); + glCopyTexSubImage2D( + GL_TEXTURE_2D, + 0, + blurRect.x(), + effects->virtualScreenSize().height() - blurRect.y() - blurRect.height(), + blurRect.x(), + effects->virtualScreenSize().height() - blurRect.y() - blurRect.height(), + blurRect.width(), + blurRect.height() + ); + + shader->bind(BlurShader::copySampleType); + + shader->setModelViewProjectionMatrix(screenProjection); + shader->setTextureSize(screenRect.size()); + shader->setBlurRect(blurredRegion.boundingRect().adjusted(BORDER_SIZE, BORDER_SIZE, -BORDER_SIZE, -BORDER_SIZE), screenRect.size()); + + GLRenderTarget::pushRenderTarget(renderTargets[0]); + vbo->draw(GL_TRIANGLES, 0, blurRectCount); + GLRenderTarget::popRenderTarget(); + + shader->unbind(); + + // Downsample + shader->bind(BlurShader::downSampleType); + shader->setOffset(m_offset); + + for (int i = 1; i <= m_downSampleIterations; i++) { + modelViewProjectionMatrix.setToIdentity(); + modelViewProjectionMatrix.ortho(0, renderTextures[i].width(), renderTextures[i].height(), 0 , 0, 65535); + + shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); + shader->setTextureSize(renderTextures[i].size()); + + // Copy the image from this texture + renderTextures[i - 1].bind(); + + // Render to the texture attachment of this FrameBufferObject + GLRenderTarget::pushRenderTarget(renderTargets[i]); + vbo->draw(GL_TRIANGLES, blurRectCount * i, blurRectCount); + GLRenderTarget::popRenderTarget(); + } + + shader->unbind(); + + // Upsample + shader->bind(BlurShader::upSampleType); + shader->setOffset(m_offset); + + for (int i = m_downSampleIterations - 1; i > 0; i--) { + modelViewProjectionMatrix.setToIdentity(); + modelViewProjectionMatrix.ortho(0, renderTextures[i].width(), renderTextures[i].height(), 0 , 0, 65535); + + shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); + shader->setTextureSize(renderTextures[i].size()); + + // Copy the image from this texture + renderTextures[i + 1].bind(); + + // Render to the texture attachment of this FrameBufferObject + GLRenderTarget::pushRenderTarget(renderTargets[i]); + vbo->draw(GL_TRIANGLES, blurRectCount * i, blurRectCount); + GLRenderTarget::popRenderTarget(); + } + + //Final upsample to the texture that is used for rendering to the screen + modelViewProjectionMatrix.setToIdentity(); + modelViewProjectionMatrix.ortho(0, it->blurredBackground.width(), it->blurredBackground.height(), 0 , 0, 65535); + shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); - - // Set up the texture matrix to transform from screen coordinates - // to texture coordinates. - textureMatrix.scale(1.0 / tex.width(), -1.0 / tex.height(), 1); - textureMatrix.translate(-updateRect.x(), -updateRect.height() - updateRect.y(), 0); - shader->setTextureMatrix(textureMatrix); - - vbo->draw(GL_TRIANGLES, horizontalOffset, horizontalCount); - + shader->setTextureSize(it->blurredBackground.size()); + + // Copy the image from this texture + renderTextures[1].bind(); + + renderTargets.last()->attachTexture(it->blurredBackground); + + // Render to the texture attachment of this FrameBufferObject + GLRenderTarget::pushRenderTarget(renderTargets.last()); + vbo->draw(GL_TRIANGLES, (blurRegion.rectCount() * (m_downSampleIterations + 1)) * 6, renderRegion.rectCount() * 6); GLRenderTarget::popRenderTarget(); - tex.unbind(); + + shader->unbind(); + // mark the updated region as valid - it->damagedRegion -= validUpdate; + it->damagedRegion -= validUpdateRegion; } - // Now draw the horizontally blurred area back to the backbuffer, while - // blurring it vertically and clipping it to the window shape. - targetTexture.bind(); - - shader->setDirection(Qt::Vertical); - shader->setPixelDistance(1.0 / targetTexture.height()); - // Modulate the blurred texture with the window opacity if the window isn't opaque if (opacity < 1.0) { glEnable(GL_BLEND); glBlendColor(0, 0, 0, opacity); glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); } - + + // Render to screen + shader->bind(BlurShader::copySampleType); + it->blurredBackground.bind(); + shader->setModelViewProjectionMatrix(screenProjection); - - // Set the up the texture matrix to transform from screen coordinates - // to texture coordinates. - textureMatrix.setToIdentity(); - textureMatrix.scale(1.0 / targetTexture.width(), -1.0 / targetTexture.height(), 1); - textureMatrix.translate(-r.x(), -targetTexture.height() - r.y(), 0); - shader->setTextureMatrix(textureMatrix); - - vbo->draw(GL_TRIANGLES, verticalOffset, verticalCount); - vbo->unbindArrays(); + shader->setTextureSize(screenRect.size()); + shader->setBlurRect(windowRegion.boundingRect(), screenRect.size()); + + vbo->draw(GL_TRIANGLES, (blurRectCount * (m_downSampleIterations + 1)) + renderRegion.rectCount() * 6, windowRegion.rectCount() * 6); if (opacity < 1.0) { glDisable(GL_BLEND); } - - targetTexture.unbind(); + + vbo->unbindArrays(); shader->unbind(); } -int BlurEffect::blurRadius() const -{ - if (!shader) { - return 0; - } - return shader->radius(); -} - } // namespace KWin diff --git a/effects/blur/blur.kcfg b/effects/blur/blur.kcfg --- a/effects/blur/blur.kcfg +++ b/effects/blur/blur.kcfg @@ -6,10 +6,13 @@ - 12 + 5 true + + false + 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 @@ -6,8 +6,8 @@ 0 0 - 396 - 103 + 480 + 123 @@ -37,7 +37,7 @@ - + Light @@ -49,16 +49,16 @@ 1 - 14 + 15 - 2 + 1 - 2 + 1 - 12 + 5 Qt::Horizontal @@ -69,7 +69,7 @@ - + Strong @@ -88,6 +88,16 @@ + + + Use simple fullscreen blur + + + false + + + + Qt::Vertical diff --git a/effects/blur/blurshader.h b/effects/blur/blurshader.h --- a/effects/blur/blurshader.h +++ b/effects/blur/blurshader.h @@ -21,22 +21,15 @@ #define BLURSHADER_H #include +#include +#include + class QMatrix4x4; namespace KWin { -struct KernelValue -{ - KernelValue() {} - KernelValue(float x, float g) : x(x), g(g) {} - bool operator < (const KernelValue &other) const { return x < other.x; } - - float x; - float g; -}; - class BlurShader { public: @@ -49,39 +42,28 @@ return mValid; } - // Sets the radius in pixels - void setRadius(int radius); - int radius() const { - return mRadius; - } - - // Sets the blur direction - void setDirection(Qt::Orientation direction); - Qt::Orientation direction() const { - return mDirection; - } - - // Sets the distance between two pixels - virtual void setPixelDistance(float val) = 0; - virtual void setTextureMatrix(const QMatrix4x4 &matrix) = 0; virtual void setModelViewProjectionMatrix(const QMatrix4x4 &matrix) = 0; + virtual void setOffset(float offset) = 0; + virtual void setTextureSize(QSize textureSize) = 0; + virtual void setBlurRect(QRect blurRect, QSize screenSize) = 0; - virtual void bind() = 0; + virtual void bind(int sampleType) = 0; virtual void unbind() = 0; + + enum sampleTypeEnum { + downSampleType, + upSampleType, + copySampleType + }; protected: - float gaussian(float x, float sigma) const; - QList gaussianKernel() const; void setIsValid(bool value) { mValid = value; } virtual void init() = 0; virtual void reset() = 0; - virtual int maxKernelSize() const = 0; private: - int mRadius; - Qt::Orientation mDirection; bool mValid; }; @@ -96,25 +78,52 @@ GLSLBlurShader(); ~GLSLBlurShader(); - void setPixelDistance(float val); - void bind(); + void bind(int sampleType); void unbind(); - void setTextureMatrix(const QMatrix4x4 &matrix); void setModelViewProjectionMatrix(const QMatrix4x4 &matrix); + void setOffset(float offset); + void setTextureSize(QSize textureSize); + void setBlurRect(QRect blurRect, QSize screenSize); protected: void init(); void reset(); - int maxKernelSize() const; private: - GLShader *shader; - int mvpMatrixLocation; - int textureMatrixLocation; - int pixelSizeLocation; + GLShader *shaderDownsample; + GLShader *shaderUpsample; + GLShader *shaderCopysample; + + int mvpMatrixLocationDownsample; + int offsetLocationDownsample; + int textureSizeLocationDownsample; + + int mvpMatrixLocationUpsample; + int offsetLocationUpsample; + int textureSizeLocationUpsample; + + int mvpMatrixLocationCopysample; + int textureSizeLocationCopysample; + int blurRectLocationCopysample; + + + //Caching uniform values to aviod unnecessary setUniform calls + int activeSampleType; + + float m_offsetDownsample; + QMatrix4x4 m_matrixDownsample; + QSize m_textureSizeDownsample; + + float m_offsetUpsample; + QMatrix4x4 m_matrixUpsample; + QSize m_textureSizeUpsample; + + QMatrix4x4 m_matrixCopysample; + QSize m_textureSizeCopysample; + QRect m_blurRectCopysample; + }; } // namespace KWin #endif - diff --git a/effects/blur/blurshader.cpp b/effects/blur/blurshader.cpp --- a/effects/blur/blurshader.cpp +++ b/effects/blur/blurshader.cpp @@ -20,6 +20,7 @@ #include "blurshader.h" #include +#include "kwinglutils.h" #include #include @@ -33,7 +34,7 @@ BlurShader::BlurShader() - : mRadius(0), mValid(false) + : mValid(false) { } @@ -46,73 +47,14 @@ return new GLSLBlurShader(); } -void BlurShader::setRadius(int radius) -{ - const int r = qMax(radius, 2); - - if (mRadius != r) { - mRadius = r; - reset(); - init(); - } -} - -void BlurShader::setDirection(Qt::Orientation direction) -{ - mDirection = direction; -} - -float BlurShader::gaussian(float x, float sigma) const -{ - return (1.0 / std::sqrt(2.0 * M_PI) * sigma) - * std::exp(-((x * x) / (2.0 * sigma * sigma))); -} - -QList BlurShader::gaussianKernel() const -{ - int size = qMin(mRadius | 1, maxKernelSize()); - if (!(size & 0x1)) - size -= 1; - - QList kernel; - const int center = size / 2; - const qreal sigma = (size - 1) / 2.5; - - kernel << KernelValue(0.0, gaussian(0.0, sigma)); - float total = kernel[0].g; - - for (int x = 1; x <= center; x++) { - const float fx = (x - 1) * 2 + 1.5; - const float g1 = gaussian(fx - 0.5, sigma); - const float g2 = gaussian(fx + 0.5, sigma); - - // Offset taking the contribution of both pixels into account - const float offset = .5 - g1 / (g1 + g2); - - kernel << KernelValue(fx + offset, g1 + g2); - kernel << KernelValue(-(fx + offset), g1 + g2); - - total += (g1 + g2) * 2; - } - - qSort(kernel); - - // Normalize the kernel - for (int i = 0; i < kernel.count(); i++) - kernel[i].g /= total; - - return kernel; -} - - - // ---------------------------------------------------------------------------- GLSLBlurShader::GLSLBlurShader() - : BlurShader(), shader(NULL) + : BlurShader(), shaderDownsample(NULL), shaderUpsample(NULL), shaderCopysample(NULL) { + init(); } GLSLBlurShader::~GLSLBlurShader() @@ -122,185 +64,341 @@ void GLSLBlurShader::reset() { - delete shader; - shader = NULL; + delete shaderDownsample; + shaderDownsample = NULL; + + delete shaderUpsample; + shaderUpsample = NULL; + + delete shaderCopysample; + shaderCopysample = NULL; setIsValid(false); } -void GLSLBlurShader::setPixelDistance(float val) +void GLSLBlurShader::setModelViewProjectionMatrix(const QMatrix4x4 &matrix) { if (!isValid()) return; - - QVector2D pixelSize(0.0, 0.0); - if (direction() == Qt::Horizontal) - pixelSize.setX(val); - else - pixelSize.setY(val); - - shader->setUniform(pixelSizeLocation, pixelSize); + + switch (activeSampleType) { + case copySampleType: + if (matrix == m_matrixCopysample) + return; + + m_matrixCopysample = matrix; + shaderCopysample->setUniform(mvpMatrixLocationCopysample, matrix); + break; + + case upSampleType: + if (matrix == m_matrixUpsample) + return; + + m_matrixUpsample = matrix; + shaderUpsample->setUniform(mvpMatrixLocationUpsample, matrix); + break; + + case downSampleType: + if (matrix == m_matrixDownsample) + return; + + m_matrixDownsample = matrix; + shaderDownsample->setUniform(mvpMatrixLocationDownsample, matrix); + break; + } } -void GLSLBlurShader::setTextureMatrix(const QMatrix4x4 &matrix) +void GLSLBlurShader::setOffset(float offset) { if (!isValid()) return; - - shader->setUniform(textureMatrixLocation, matrix); + + switch (activeSampleType) { + case upSampleType: + if (offset == m_offsetUpsample) + return; + + m_offsetUpsample = offset; + shaderUpsample->setUniform(offsetLocationUpsample, offset); + break; + + case downSampleType: + if (offset == m_offsetDownsample) + return; + + m_offsetDownsample = offset; + shaderDownsample->setUniform(offsetLocationDownsample, offset); + break; + } } -void GLSLBlurShader::setModelViewProjectionMatrix(const QMatrix4x4 &matrix) +void GLSLBlurShader::setTextureSize(QSize textureSize) { if (!isValid()) return; + + QVector2D texSize = QVector2D(textureSize.width(), textureSize.height()); + + switch (activeSampleType) { + case copySampleType: + if (textureSize == m_textureSizeCopysample) + return; + + m_textureSizeCopysample = textureSize; + shaderCopysample->setUniform(textureSizeLocationCopysample, texSize); + break; + + case upSampleType: + if (textureSize == m_textureSizeUpsample) + return; + + m_textureSizeUpsample = textureSize; + shaderUpsample->setUniform(textureSizeLocationUpsample, texSize); + break; + + case downSampleType: + if (textureSize == m_textureSizeDownsample) + return; + + m_textureSizeDownsample = textureSize; + shaderDownsample->setUniform(textureSizeLocationDownsample, texSize); + break; + } +} - shader->setUniform(mvpMatrixLocation, matrix); +void GLSLBlurShader::setBlurRect(QRect blurRect, QSize screenSize) +{ + if (!isValid() || blurRect == m_blurRectCopysample) + return; + + m_blurRectCopysample = blurRect; + + 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()) + ); + + shaderCopysample->setUniform(blurRectLocationCopysample, rect); } -void GLSLBlurShader::bind() +void GLSLBlurShader::bind(int sampleType) { if (!isValid()) return; - ShaderManager::instance()->pushShader(shader); + switch (sampleType) { + case copySampleType: + ShaderManager::instance()->pushShader(shaderCopysample); + break; + + case upSampleType: + ShaderManager::instance()->pushShader(shaderUpsample); + break; + + case downSampleType: + ShaderManager::instance()->pushShader(shaderDownsample); + break; + } + + activeSampleType = sampleType; } void GLSLBlurShader::unbind() { ShaderManager::instance()->popShader(); } -int GLSLBlurShader::maxKernelSize() const -{ - if (GLPlatform::instance()->isGLES()) { - // GL_MAX_VARYING_FLOATS not available in GLES - // querying for GL_MAX_VARYING_VECTORS crashes on nouveau - // using the minimum value of 8 - return 8 * 2; - } else { - int value; - glGetIntegerv(GL_MAX_VARYING_FLOATS, &value); - // Maximum number of vec4 varyings * 2 - // The code generator will pack two vec2's into each vec4. - return value / 2; - } -} - void GLSLBlurShader::init() { - QList kernel = gaussianKernel(); - const int size = kernel.size(); - const int center = size / 2; - - QList offsets; - for (int i = 0; i < kernel.size(); i += 2) { - QVector4D vec4(0, 0, 0, 0); - - vec4.setX(kernel[i].x); - vec4.setY(kernel[i].x); - - if (i < kernel.size() - 1) { - vec4.setZ(kernel[i + 1].x); - vec4.setW(kernel[i + 1].x); - } - - offsets << vec4; - } - const bool gles = GLPlatform::instance()->isGLES(); const bool glsl_140 = !gles && GLPlatform::instance()->glslVersion() >= kVersionNumber(1, 40); const bool core = glsl_140 || (gles && GLPlatform::instance()->glslVersion() >= kVersionNumber(3, 0)); QByteArray vertexSource; - QByteArray fragmentSource; + QByteArray fragmentDownSource; + QByteArray fragmentUpSource; + QByteArray fragmentCopySource; - const QByteArray attribute = core ? "in" : "attribute"; - const QByteArray varying_in = core ? (gles ? "in" : "noperspective in") : "varying"; - const QByteArray varying_out = core ? (gles ? "out" : "noperspective out") : "varying"; - const QByteArray texture2D = core ? "texture" : "texture2D"; - const QByteArray fragColor = core ? "fragColor" : "gl_FragColor"; + const QByteArray attribute = core ? "in" : "attribute"; + const QByteArray texture2D = core ? "texture" : "texture2D"; + const QByteArray fragColor = core ? "fragColor" : "gl_FragColor"; // Vertex shader // =================================================================== - QTextStream stream(&vertexSource); + QTextStream streamVert(&vertexSource); if (gles) { if (core) { - stream << "#version 300 es\n\n"; + streamVert << "#version 300 es\n\n"; } - stream << "precision highp float;\n"; + + streamVert << "precision highp float;\n"; } else if (glsl_140) { - stream << "#version 140\n\n"; + streamVert << "#version 140\n\n"; } - stream << "uniform mat4 modelViewProjectionMatrix;\n"; - stream << "uniform mat4 textureMatrix;\n"; - stream << "uniform vec2 pixelSize;\n\n"; - stream << attribute << " vec4 vertex;\n\n"; - stream << varying_out << " vec4 samplePos[" << std::ceil(size / 2.0) << "];\n"; - stream << "\n"; - stream << "void main(void)\n"; - stream << "{\n"; - stream << " vec4 center = vec4(textureMatrix * vertex).stst;\n"; - stream << " vec4 ps = pixelSize.stst;\n\n"; - for (int i = 0; i < offsets.size(); i++) { - stream << " samplePos[" << i << "] = center + ps * vec4(" - << offsets[i].x() << ", " << offsets[i].y() << ", " - << offsets[i].z() << ", " << offsets[i].w() << ");\n"; + streamVert << "uniform mat4 modelViewProjectionMatrix;\n"; + streamVert << attribute << " vec4 vertex;\n\n"; + streamVert << "\n"; + streamVert << "void main(void)\n"; + streamVert << "{\n"; + streamVert << " gl_Position = modelViewProjectionMatrix * vertex;\n"; + streamVert << "}\n"; + + streamVert.flush(); + + // Fragment shader (Dual Kawase Blur) - Downsample + // =================================================================== + QTextStream streamFragDown(&fragmentDownSource); + + if (gles) { + if (core) { + streamFragDown << "#version 300 es\n\n"; + } + + streamFragDown << "precision highp float;\n"; + } else if (glsl_140) { + streamFragDown << "#version 140\n\n"; } - stream << "\n"; - stream << " gl_Position = modelViewProjectionMatrix * vertex;\n"; - stream << "}\n"; - stream.flush(); - // Fragment shader + streamFragDown << "uniform sampler2D texUnit;\n"; + streamFragDown << "uniform float offset;\n"; + streamFragDown << "uniform vec2 textureSize;\n"; + + if (core) + streamFragDown << "out vec4 fragColor;\n\n"; + + streamFragDown << "void main(void)\n"; + streamFragDown << "{\n"; + streamFragDown << " vec2 uv = vec2(gl_FragCoord.xy / textureSize);\n"; + streamFragDown << " vec2 halfpixel = vec2(0.5 / textureSize);\n"; + streamFragDown << " \n"; + streamFragDown << " vec4 sum = " << texture2D << "(texUnit, uv) * 4.0;\n"; + streamFragDown << " sum += " << texture2D << "(texUnit, uv - halfpixel.xy * offset);\n"; + streamFragDown << " sum += " << texture2D << "(texUnit, uv + halfpixel.xy * offset);\n"; + streamFragDown << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x, -halfpixel.y) * offset);\n"; + streamFragDown << " sum += " << texture2D << "(texUnit, uv - vec2(halfpixel.x, -halfpixel.y) * offset);\n"; + streamFragDown << " \n"; + streamFragDown << " " << fragColor << " = sum / 8.0;\n"; + streamFragDown << "}\n"; + + streamFragDown.flush(); + + // Fragment shader (Dual Kawase Blur) - Upsample // =================================================================== - QTextStream stream2(&fragmentSource); + QTextStream streamFragUp(&fragmentUpSource); if (gles) { if (core) { - stream2 << "#version 300 es\n\n"; + streamFragUp << "#version 300 es\n\n"; } - stream2 << "precision highp float;\n"; + + streamFragUp << "precision highp float;\n"; } else if (glsl_140) { - stream2 << "#version 140\n\n"; + streamFragUp << "#version 140\n\n"; } - stream2 << "uniform sampler2D texUnit;\n"; - stream2 << varying_in << " vec4 samplePos[" << std::ceil(size / 2.0) << "];\n\n"; + streamFragUp << "uniform sampler2D texUnit;\n"; + streamFragUp << "uniform float offset;\n"; + streamFragUp << "uniform vec2 textureSize;\n"; - for (int i = 0; i <= center; i++) - stream2 << "const float kernel" << i << " = " << kernel[i].g << ";\n"; - stream2 << "\n"; + if (core) + streamFragUp << "out vec4 fragColor;\n\n"; + + streamFragUp << "void main(void)\n"; + streamFragUp << "{\n"; + streamFragUp << " vec2 uv = vec2(gl_FragCoord.xy / textureSize);\n"; + streamFragUp << " vec2 halfpixel = vec2(0.5 / textureSize);\n"; + streamFragUp << " \n"; + streamFragUp << " vec4 sum = " << texture2D << "(texUnit, uv + vec2(-halfpixel.x * 2.0, 0.0) * offset);\n"; + streamFragUp << " sum += " << texture2D << "(texUnit, uv + vec2(-halfpixel.x, halfpixel.y) * offset) * 2.0;\n"; + streamFragUp << " sum += " << texture2D << "(texUnit, uv + vec2(0.0, halfpixel.y * 2.0) * offset);\n"; + streamFragUp << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x, halfpixel.y) * offset) * 2.0;\n"; + streamFragUp << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x * 2.0, 0.0) * offset);\n"; + streamFragUp << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x, -halfpixel.y) * offset) * 2.0;\n"; + streamFragUp << " sum += " << texture2D << "(texUnit, uv + vec2(0.0, -halfpixel.y * 2.0) * offset);\n"; + streamFragUp << " sum += " << texture2D << "(texUnit, uv + vec2(-halfpixel.x, -halfpixel.y) * offset) * 2.0;\n"; + streamFragUp << " \n"; + streamFragUp << " " << fragColor << " = sum / 12.0;\n"; + streamFragUp << "}\n"; + + streamFragUp.flush(); + + // Fragment shader - Copy texture + // =================================================================== + QTextStream streamFragCopy(&fragmentCopySource); + + if (gles) { + if (core) { + streamFragCopy << "#version 300 es\n\n"; + } + + streamFragCopy << "precision highp float;\n"; + } else if (glsl_140) { + streamFragCopy << "#version 140\n\n"; + } + + streamFragCopy << "uniform sampler2D texUnit;\n"; + streamFragCopy << "uniform vec2 textureSize;\n"; + streamFragCopy << "uniform vec4 blurRect;\n"; if (core) - stream2 << "out vec4 fragColor;\n\n"; - - stream2 << "void main(void)\n"; - stream2 << "{\n"; - stream2 << " vec4 sum = " << texture2D << "(texUnit, samplePos[0].st) * kernel0;\n"; - for (int i = 1, j = -center + 1; i < size; i++, j++) - stream2 << " sum = sum + " << texture2D << "(texUnit, samplePos[" << i / 2 - << ((i % 2) ? "].pq)" : "].st)") << " * kernel" << center - qAbs(j) << ";\n"; - stream2 << " " << fragColor << " = sum;\n"; - stream2 << "}\n"; - stream2.flush(); - - shader = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentSource); - if (shader->isValid()) { - pixelSizeLocation = shader->uniformLocation("pixelSize"); - textureMatrixLocation = shader->uniformLocation("textureMatrix"); - mvpMatrixLocation = shader->uniformLocation("modelViewProjectionMatrix"); + streamFragCopy << "out vec4 fragColor;\n\n"; + + streamFragCopy << "void main(void)\n"; + streamFragCopy << "{\n"; + streamFragCopy << " vec2 uv = vec2(gl_FragCoord.xy / textureSize);\n"; + streamFragCopy << " " << fragColor << " = " << texture2D << "(texUnit, clamp(uv, blurRect.xy, blurRect.zw));\n"; + streamFragCopy << "}\n"; + + streamFragCopy.flush(); + + + + shaderDownsample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentDownSource); + shaderUpsample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentUpSource); + shaderCopysample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentCopySource); + + bool areShadersValid = shaderDownsample->isValid() && shaderUpsample->isValid() && shaderCopysample->isValid(); + setIsValid(areShadersValid); + + if (areShadersValid) { + mvpMatrixLocationDownsample = shaderDownsample->uniformLocation("modelViewProjectionMatrix"); + offsetLocationDownsample = shaderDownsample->uniformLocation("offset"); + textureSizeLocationDownsample = shaderDownsample->uniformLocation("textureSize"); + + mvpMatrixLocationUpsample = shaderUpsample->uniformLocation("modelViewProjectionMatrix"); + offsetLocationUpsample = shaderUpsample->uniformLocation("offset"); + textureSizeLocationUpsample = shaderUpsample->uniformLocation("textureSize"); + + mvpMatrixLocationCopysample = shaderCopysample->uniformLocation("modelViewProjectionMatrix"); + textureSizeLocationCopysample = shaderCopysample->uniformLocation("textureSize"); + blurRectLocationCopysample = shaderCopysample->uniformLocation("blurRect"); QMatrix4x4 modelViewProjection; const QSize screenSize = effects->virtualScreenSize(); modelViewProjection.ortho(0, screenSize.width(), screenSize.height(), 0, 0, 65535); - ShaderManager::instance()->pushShader(shader); - shader->setUniform(textureMatrixLocation, QMatrix4x4()); - shader->setUniform(mvpMatrixLocation, modelViewProjection); + + //Add default values to the uniforms of the shaders + ShaderManager::instance()->pushShader(shaderDownsample); + shaderDownsample->setUniform(mvpMatrixLocationDownsample, modelViewProjection); + shaderDownsample->setUniform(offsetLocationDownsample, float(1.0)); + shaderDownsample->setUniform(textureSizeLocationDownsample, QVector2D(1.0, 1.0)); + ShaderManager::instance()->popShader(); + + ShaderManager::instance()->pushShader(shaderUpsample); + shaderUpsample->setUniform(mvpMatrixLocationUpsample, modelViewProjection); + shaderUpsample->setUniform(offsetLocationUpsample, float(1.0)); + shaderUpsample->setUniform(textureSizeLocationUpsample, QVector2D(1.0, 1.0)); ShaderManager::instance()->popShader(); + + ShaderManager::instance()->pushShader(shaderCopysample); + shaderCopysample->setUniform(mvpMatrixLocationCopysample, modelViewProjection); + shaderCopysample->setUniform(textureSizeLocationCopysample, QVector2D(1.0, 1.0)); + ShaderManager::instance()->popShader(); + + activeSampleType = -1; } - - setIsValid(shader->isValid()); }