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,6 +75,9 @@ void slotScreenGeometryChanged(); private: + bool renderTargetsValid() const; + void deleteFBOs(); + void initBlurStrengthData(); void updateTexture(); QRect expand(const QRect &rect) const; QRegion expand(const QRegion ®ion) const; @@ -85,28 +87,48 @@ 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); + + void downSampleTexture(GLVertexBuffer *vbo, int blurRectCount); + void upSampleTexture(GLVertexBuffer *vbo, int blurRectCount); + void copyScreenSampleTexture(GLVertexBuffer *vbo, int blurRectCount, QRect copyRect, QRegion blurShape, QSize screenSize, QMatrix4x4 screenProjection); private: - BlurShader *shader; GLShader *m_simpleShader; - GLRenderTarget *target = nullptr; - GLTexture tex; + GLRenderTarget *m_simpleTarget; + + BlurShader *m_shader; + QVector m_renderTargets; + QVector m_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 BlurStrengthData { + int downSampleIterations; + int offset; + int expandSize; + }; + + QVector m_blurConfigData; + QHash< const EffectWindow*, BlurWindowInfo > windows; typedef QHash::iterator CacheEntry; KWayland::Server::BlurManagerInterface *m_blurManager = nullptr; diff --git a/effects/blur/blur.cpp b/effects/blur/blur.cpp --- a/effects/blur/blur.cpp +++ b/effects/blur/blur.cpp @@ -32,26 +32,30 @@ #include #include +#define BORDER_SIZE 5 + namespace KWin { static const QByteArray s_blurAtomName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION"); BlurEffect::BlurEffect() { initConfig(); - shader = BlurShader::create(); + m_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 (m_shader && m_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 (m_shader && m_shader->isValid() && renderTargetsValid()) { net_wm_blur_region = effects->announceSupportProperty(s_blurAtomName, this); } } @@ -82,47 +86,139 @@ BlurEffect::~BlurEffect() { windows.clear(); + deleteFBOs(); + + delete m_simpleTarget; + m_simpleTarget = nullptr; delete m_simpleShader; - delete shader; - delete target; + m_simpleShader = nullptr; + + delete m_shader; + m_shader = nullptr; } void BlurEffect::slotScreenGeometryChanged() { effects->makeOpenGLContextCurrent(); updateTexture(); + windows.clear(); + // Fetch the blur regions for all windows foreach (EffectWindow *window, effects->stackingOrder()) updateBlurRegion(window); 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() const +{ + return !m_renderTargets.isEmpty() && std::find_if(m_renderTargets.cbegin(), m_renderTargets.cend(), + [](const GLRenderTarget *target) { + return !target->valid(); + }) == m_renderTargets.cend(); +} + +void BlurEffect::deleteFBOs() +{ + for (int i = 0; i < m_renderTargets.size(); i++) { + delete m_renderTargets[i]; + m_renderTargets[i] = nullptr; + + m_renderTextures[i].discard(); + } + + m_renderTargets.clear(); + m_renderTextures.clear(); +} + +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. + **/ + - target = new GLRenderTarget(tex); + // Dummy data needed, because the blur strength range is 1-15 + m_blurConfigData.append({0, 0, 0}); + + //Structure format: {downSampleIterations, offset, expandSize} + m_blurConfigData.append({1, 1, 5}); + m_blurConfigData.append({2, 1, 10}); + m_blurConfigData.append({2, 2, 15}); + m_blurConfigData.append({2, 3, 25}); + m_blurConfigData.append({2, 4, 30}); + m_blurConfigData.append({3, 3, 40}); + m_blurConfigData.append({3, 4, 50}); + m_blurConfigData.append({3, 5, 60}); + m_blurConfigData.append({3, 6, 80}); + m_blurConfigData.append({3, 7, 100}); + m_blurConfigData.append({3, 8, 120}); + m_blurConfigData.append({4, 5, 140}); + m_blurConfigData.append({4, 6, 155}); + m_blurConfigData.append({4, 7, 170}); + m_blurConfigData.append({4, 8, 200}); +} + +void BlurEffect::updateTexture() +{ + deleteFBOs(); + + for (int i = 0; i <= m_downSampleIterations; i++) { + m_renderTextures.append(GLTexture(GL_RGBA8, effects->virtualScreenSize() / qPow(2, i))); + m_renderTextures.last().setFilter(GL_LINEAR); + m_renderTextures.last().setWrapMode(GL_CLAMP_TO_EDGE); + + m_renderTargets.append(new GLRenderTarget(m_renderTextures.last())); + } + + // This last set is used as a temporary helper texture + m_renderTextures.append(GLTexture(GL_RGBA8, effects->virtualScreenSize())); + m_renderTextures.last().setFilter(GL_LINEAR); + m_renderTextures.last().setWrapMode(GL_CLAMP_TO_EDGE); + + m_renderTargets.append(new GLRenderTarget(m_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::blurStrength(); + m_downSampleIterations = m_blurConfigData[blurStrength].downSampleIterations; + m_offset = m_blurConfigData[blurStrength].offset; + m_expandSize = m_blurConfigData[blurStrength].expandSize; + + updateTexture(); windows.clear(); - if (!shader || !shader->isValid()) { + if (!m_shader || !m_shader->isValid()) { effects->removeSupportProperty(s_blurAtomName, this); delete m_blurManager; m_blurManager = nullptr; @@ -171,7 +267,6 @@ if (surf) { windows[w].blurChangedConnection = connect(surf, &KWayland::Server::SurfaceInterface::blurChanged, this, [this, w] () { - if (w) { updateBlurRegion(w); } @@ -230,8 +325,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 +368,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[] = { @@ -331,16 +433,15 @@ if (!w->isPaintingEnabled()) { return; } - if (!shader || !shader->isValid()) { + if (!m_shader || !m_shader->isValid()) { return; } // 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 +461,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 { @@ -428,7 +529,7 @@ bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data) const { - if (!target->valid() || !shader || !shader->isValid()) + if (!renderTargetsValid() || !m_shader || !m_shader->isValid()) return false; if (effects->activeFullScreenEffect() && !w->data(WindowForceBlurRole).toBool()) @@ -483,7 +584,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 +604,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() && m_shader && m_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 +620,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,60 +640,19 @@ 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(); + QRegion expandedBlurRegion = expand(shape) & expand(screen); + QRect expandedBlurRect = expandedBlurRegion.boundingRect() & screen; - // Upload geometry for the horizontal and vertical passes + // 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; - 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()); + copyScreenSampleTexture(vbo, blurRectCount, expandedBlurRect, shape, screen.size(), screenProjection); + downSampleTexture(vbo, blurRectCount); + upSampleTexture(vbo, blurRectCount); // Modulate the blurred texture with the window opacity if the window isn't opaque if (opacity < 1.0) { @@ -603,58 +668,53 @@ 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); - shader->setModelViewProjectionMatrix(screenProjection); + //Final upscale to the screen + m_shader->bind(BlurShader::upSampleType); + m_shader->setOffset(m_offset); - vbo->draw(GL_TRIANGLES, expanded.rectCount() * 6, shape.rectCount() * 6); - vbo->unbindArrays(); + m_shader->setModelViewProjectionMatrix(screenProjection); + m_shader->setTextureSize(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 (opacity < 1.0) { glDisable(GL_BLEND); } - tex.unbind(); - shader->unbind(); + vbo->unbindArrays(); + m_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(); + const QRect screenRect = effects->virtualScreenGeometry(); + const QRegion blurredRegion = blurRegion(w).translated(w->pos()) & screenRect; - // The background texture we get is only partially valid. + 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,98 +746,155 @@ * 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 QRegion validUpdateRegion = it->damagedRegion - expand(it->damagedRegion - region); - const int verticalOffset = horizontalCount; - const int verticalCount = vertical.rectCount() * 6; + 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(); + int blurRectCount = blurRegion.rectCount() * 6; + if (!validUpdateRegion.isEmpty()) { + QRect blurRect = blurRegion.boundingRect() & screenRect; - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, updateRect.x(), effects->virtualScreenSize().height() - updateRect.y() - updateRect.height(), - updateRect.width(), updateRect.height()); + copyScreenSampleTexture(vbo, blurRectCount, blurRect, blurredRegion, screenRect.size(), screenProjection); + downSampleTexture(vbo, blurRectCount); + upSampleTexture(vbo, blurRectCount); - // Draw the texture on the offscreen framebuffer object, while blurring it horizontally - target->attachTexture(targetTexture); - GLRenderTarget::pushRenderTarget(target); + //Final upsample to the texture that is used for rendering to the screen + m_shader->bind(BlurShader::upSampleType); + m_shader->setOffset(m_offset); - shader->setDirection(Qt::Horizontal); - shader->setPixelDistance(1.0 / tex.width()); + QMatrix4x4 modelViewProjectionMatrix; + modelViewProjectionMatrix.setToIdentity(); + modelViewProjectionMatrix.ortho(0, it->blurredBackground.width(), it->blurredBackground.height(), 0 , 0, 65535); - modelViewProjectionMatrix.ortho(0, r.width(), r.height(), 0 , 0, 65535); - modelViewProjectionMatrix.translate(-r.x(), -r.y(), 0); - shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); + m_shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); + m_shader->setTextureSize(it->blurredBackground.size()); - // 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); + // Copy the image from this texture + m_renderTextures[1].bind(); - vbo->draw(GL_TRIANGLES, horizontalOffset, horizontalCount); + m_renderTargets.last()->attachTexture(it->blurredBackground); + // Render to the texture attachment of this FrameBufferObject + GLRenderTarget::pushRenderTarget(m_renderTargets.last()); + vbo->draw(GL_TRIANGLES, (blurRectCount * (m_downSampleIterations + 1)), renderRegion.rectCount() * 6); GLRenderTarget::popRenderTarget(); - tex.unbind(); - // mark the updated region as valid - it->damagedRegion -= validUpdate; - } - // Now draw the horizontally blurred area back to the backbuffer, while - // blurring it vertically and clipping it to the window shape. - targetTexture.bind(); + m_shader->unbind(); - shader->setDirection(Qt::Vertical); - shader->setPixelDistance(1.0 / targetTexture.height()); + // mark the updated region as valid + it->damagedRegion -= validUpdateRegion; + } // 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); } - shader->setModelViewProjectionMatrix(screenProjection); + // Render to screen + m_shader->bind(BlurShader::copySampleType); + it->blurredBackground.bind(); - // 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); + m_shader->setModelViewProjectionMatrix(screenProjection); + m_shader->setTextureSize(screenRect.size()); + m_shader->setBlurRect(windowRegion.boundingRect(), screenRect.size()); - vbo->draw(GL_TRIANGLES, verticalOffset, verticalCount); - vbo->unbindArrays(); + vbo->draw(GL_TRIANGLES, (blurRectCount * (m_downSampleIterations + 1)) + renderRegion.rectCount() * 6, windowRegion.rectCount() * 6); if (opacity < 1.0) { glDisable(GL_BLEND); } - targetTexture.unbind(); - shader->unbind(); + vbo->unbindArrays(); + m_shader->unbind(); } -int BlurEffect::blurRadius() const +void BlurEffect::downSampleTexture(GLVertexBuffer *vbo, int blurRectCount) { - if (!shader) { - return 0; + QMatrix4x4 modelViewProjectionMatrix; + + m_shader->bind(BlurShader::downSampleType); + m_shader->setOffset(m_offset); + + for (int i = 1; i <= m_downSampleIterations; i++) { + modelViewProjectionMatrix.setToIdentity(); + modelViewProjectionMatrix.ortho(0, m_renderTextures[i].width(), m_renderTextures[i].height(), 0 , 0, 65535); + + m_shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); + m_shader->setTextureSize(m_renderTextures[i].size()); + + //Copy the image from this texture + m_renderTextures[i - 1].bind(); + + //Render to the texture attached to this FrameBufferObject + GLRenderTarget::pushRenderTarget(m_renderTargets[i]); + vbo->draw(GL_TRIANGLES, blurRectCount * i, blurRectCount); + GLRenderTarget::popRenderTarget(); } - return shader->radius(); + + m_shader->unbind(); +} + +void BlurEffect::upSampleTexture(GLVertexBuffer *vbo, int blurRectCount) +{ + QMatrix4x4 modelViewProjectionMatrix; + + m_shader->bind(BlurShader::upSampleType); + m_shader->setOffset(m_offset); + + for (int i = m_downSampleIterations - 1; i > 0; i--) { + modelViewProjectionMatrix.setToIdentity(); + modelViewProjectionMatrix.ortho(0, m_renderTextures[i].width(), m_renderTextures[i].height(), 0 , 0, 65535); + + m_shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); + m_shader->setTextureSize(m_renderTextures[i].size()); + + //Copy the image from this texture + m_renderTextures[i + 1].bind(); + + //Render to the texture attached to this FrameBufferObject + GLRenderTarget::pushRenderTarget(m_renderTargets[i]); + vbo->draw(GL_TRIANGLES, blurRectCount * i, blurRectCount); + GLRenderTarget::popRenderTarget(); + } + + m_shader->unbind(); +} + +void BlurEffect::copyScreenSampleTexture(GLVertexBuffer *vbo, int blurRectCount, QRect copyRect, QRegion blurShape, QSize screenSize, QMatrix4x4 screenProjection) +{ + m_renderTextures.last().bind(); + + glCopyTexSubImage2D( + GL_TEXTURE_2D, + 0, + copyRect.x(), + effects->virtualScreenSize().height() - copyRect.y() - copyRect.height(), + copyRect.x(), + effects->virtualScreenSize().height() - copyRect.y() - copyRect.height(), + copyRect.width(), + copyRect.height() + ); + + m_shader->bind(BlurShader::copySampleType); + + m_shader->setModelViewProjectionMatrix(screenProjection); + m_shader->setTextureSize(screenSize); + m_shader->setBlurRect(blurShape.boundingRect().adjusted(BORDER_SIZE, BORDER_SIZE, -BORDER_SIZE, -BORDER_SIZE), screenSize); + + GLRenderTarget::pushRenderTarget(m_renderTargets[0]); + vbo->draw(GL_TRIANGLES, 0, blurRectCount); + GLRenderTarget::popRenderTarget(); + + 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 @@ -5,11 +5,14 @@ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > - - 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,13 +6,13 @@ 0 0 - 396 - 103 + 480 + 123 - + Strength of the effect: @@ -37,28 +37,28 @@ - + Light - + 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 *m_shaderDownsample; + GLShader *m_shaderUpsample; + GLShader *m_shaderCopysample; + + int m_mvpMatrixLocationDownsample; + int m_offsetLocationDownsample; + int m_textureSizeLocationDownsample; + + int m_mvpMatrixLocationUpsample; + int m_offsetLocationUpsample; + int m_textureSizeLocationUpsample; + + int m_mvpMatrixLocationCopysample; + int m_textureSizeLocationCopysample; + int m_blurRectLocationCopysample; + + + //Caching uniform values to aviod unnecessary setUniform calls + int m_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,261 +47,336 @@ 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) +GLSLBlurShader::GLSLBlurShader() { - mDirection = direction; + GLShader *m_shaderDownsample = nullptr; + GLShader *m_shaderUpsample = nullptr; + GLShader *m_shaderCopysample = nullptr; + + init(); } -float BlurShader::gaussian(float x, float sigma) const +GLSLBlurShader::~GLSLBlurShader() { - return (1.0 / std::sqrt(2.0 * M_PI) * sigma) - * std::exp(-((x * x) / (2.0 * sigma * sigma))); + reset(); } -QList BlurShader::gaussianKernel() const +void GLSLBlurShader::reset() { - int size = qMin(mRadius | 1, maxKernelSize()); - if (!(size & 0x1)) - size -= 1; + delete m_shaderDownsample; + m_shaderDownsample = nullptr; + + delete m_shaderUpsample; + m_shaderUpsample = nullptr; - QList kernel; - const int center = size / 2; - const qreal sigma = (size - 1) / 2.5; + delete m_shaderCopysample; + m_shaderCopysample = nullptr; - kernel << KernelValue(0.0, gaussian(0.0, sigma)); - float total = kernel[0].g; + setIsValid(false); +} - 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); +void GLSLBlurShader::setModelViewProjectionMatrix(const QMatrix4x4 &matrix) +{ + if (!isValid()) + return; - // Offset taking the contribution of both pixels into account - const float offset = .5 - g1 / (g1 + g2); + switch (m_activeSampleType) { + case copySampleType: + if (matrix == m_matrixCopysample) + return; - kernel << KernelValue(fx + offset, g1 + g2); - kernel << KernelValue(-(fx + offset), g1 + g2); + m_matrixCopysample = matrix; + m_shaderCopysample->setUniform(m_mvpMatrixLocationCopysample, matrix); + break; - total += (g1 + g2) * 2; - } + case upSampleType: + if (matrix == m_matrixUpsample) + return; - qSort(kernel); + m_matrixUpsample = matrix; + m_shaderUpsample->setUniform(m_mvpMatrixLocationUpsample, matrix); + break; - // Normalize the kernel - for (int i = 0; i < kernel.count(); i++) - kernel[i].g /= total; + case downSampleType: + if (matrix == m_matrixDownsample) + return; - return kernel; + m_matrixDownsample = matrix; + m_shaderDownsample->setUniform(m_mvpMatrixLocationDownsample, matrix); + break; + } } +void GLSLBlurShader::setOffset(float offset) +{ + if (!isValid()) + return; + switch (m_activeSampleType) { + case upSampleType: + if (offset == m_offsetUpsample) + return; -// ---------------------------------------------------------------------------- - + m_offsetUpsample = offset; + m_shaderUpsample->setUniform(m_offsetLocationUpsample, offset); + break; + case downSampleType: + if (offset == m_offsetDownsample) + return; -GLSLBlurShader::GLSLBlurShader() - : BlurShader(), shader(NULL) -{ + m_offsetDownsample = offset; + m_shaderDownsample->setUniform(m_offsetLocationDownsample, offset); + break; + } } -GLSLBlurShader::~GLSLBlurShader() +void GLSLBlurShader::setTextureSize(QSize textureSize) { - reset(); -} + if (!isValid()) + return; -void GLSLBlurShader::reset() -{ - delete shader; - shader = NULL; + QVector2D texSize = QVector2D(textureSize.width(), textureSize.height()); - setIsValid(false); -} + switch (m_activeSampleType) { + case copySampleType: + if (textureSize == m_textureSizeCopysample) + return; -void GLSLBlurShader::setPixelDistance(float val) -{ - if (!isValid()) - return; + m_textureSizeCopysample = textureSize; + m_shaderCopysample->setUniform(m_textureSizeLocationCopysample, texSize); + break; - QVector2D pixelSize(0.0, 0.0); - if (direction() == Qt::Horizontal) - pixelSize.setX(val); - else - pixelSize.setY(val); + case upSampleType: + if (textureSize == m_textureSizeUpsample) + return; - shader->setUniform(pixelSizeLocation, pixelSize); -} + m_textureSizeUpsample = textureSize; + m_shaderUpsample->setUniform(m_textureSizeLocationUpsample, texSize); + break; -void GLSLBlurShader::setTextureMatrix(const QMatrix4x4 &matrix) -{ - if (!isValid()) - return; + case downSampleType: + if (textureSize == m_textureSizeDownsample) + return; - shader->setUniform(textureMatrixLocation, matrix); + m_textureSizeDownsample = textureSize; + m_shaderDownsample->setUniform(m_textureSizeLocationDownsample, texSize); + break; + } } -void GLSLBlurShader::setModelViewProjectionMatrix(const QMatrix4x4 &matrix) +void GLSLBlurShader::setBlurRect(QRect blurRect, QSize screenSize) { - if (!isValid()) + if (!isValid() || blurRect == m_blurRectCopysample) return; - shader->setUniform(mvpMatrixLocation, matrix); + 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()) + ); + + m_shaderCopysample->setUniform(m_blurRectLocationCopysample, rect); } -void GLSLBlurShader::bind() +void GLSLBlurShader::bind(int sampleType) { if (!isValid()) return; - ShaderManager::instance()->pushShader(shader); + switch (sampleType) { + case copySampleType: + ShaderManager::instance()->pushShader(m_shaderCopysample); + break; + + case upSampleType: + ShaderManager::instance()->pushShader(m_shaderUpsample); + break; + + case downSampleType: + ShaderManager::instance()->pushShader(m_shaderDownsample); + break; + } + + m_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); + QString glHeaderString; if (gles) { if (core) { - stream << "#version 300 es\n\n"; + glHeaderString += "#version 300 es\n\n"; } - stream << "precision highp float;\n"; + + glHeaderString += "precision highp float;\n"; } else if (glsl_140) { - stream << "#version 140\n\n"; + glHeaderString += "#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"; + QString glUniformString = "uniform sampler2D texUnit;\n" + "uniform float offset;\n" + "uniform vec2 textureSize;\n"; + + if (core) { + glUniformString += "out vec4 fragColor;\n\n"; } - stream << "\n"; - stream << " gl_Position = modelViewProjectionMatrix * vertex;\n"; - stream << "}\n"; - stream.flush(); - // Fragment shader + + // Vertex shader // =================================================================== - QTextStream stream2(&fragmentSource); + QTextStream streamVert(&vertexSource); - if (gles) { - if (core) { - stream2 << "#version 300 es\n\n"; - } - stream2 << "precision highp float;\n"; - } else if (glsl_140) { - stream2 << "#version 140\n\n"; - } + streamVert << glHeaderString; + + 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); + + streamFragDown << glHeaderString << glUniformString; + + 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 streamFragUp(&fragmentUpSource); + + streamFragUp << glHeaderString << glUniformString; + + 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); - stream2 << "uniform sampler2D texUnit;\n"; - stream2 << varying_in << " vec4 samplePos[" << std::ceil(size / 2.0) << "];\n\n"; + streamFragCopy << glHeaderString; - for (int i = 0; i <= center; i++) - stream2 << "const float kernel" << i << " = " << kernel[i].g << ";\n"; - stream2 << "\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(); + + + 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(); + setIsValid(areShadersValid); + + if (areShadersValid) { + m_mvpMatrixLocationDownsample = m_shaderDownsample->uniformLocation("modelViewProjectionMatrix"); + m_offsetLocationDownsample = m_shaderDownsample->uniformLocation("offset"); + m_textureSizeLocationDownsample = m_shaderDownsample->uniformLocation("textureSize"); + + m_mvpMatrixLocationUpsample = m_shaderUpsample->uniformLocation("modelViewProjectionMatrix"); + m_offsetLocationUpsample = m_shaderUpsample->uniformLocation("offset"); + m_textureSizeLocationUpsample = m_shaderUpsample->uniformLocation("textureSize"); + + m_mvpMatrixLocationCopysample = m_shaderCopysample->uniformLocation("modelViewProjectionMatrix"); + m_textureSizeLocationCopysample = m_shaderCopysample->uniformLocation("textureSize"); + m_blurRectLocationCopysample = m_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(m_shaderDownsample); + m_shaderDownsample->setUniform(m_mvpMatrixLocationDownsample, modelViewProjection); + m_shaderDownsample->setUniform(m_offsetLocationDownsample, float(1.0)); + m_shaderDownsample->setUniform(m_textureSizeLocationDownsample, QVector2D(1.0, 1.0)); ShaderManager::instance()->popShader(); - } - setIsValid(shader->isValid()); + ShaderManager::instance()->pushShader(m_shaderUpsample); + m_shaderUpsample->setUniform(m_mvpMatrixLocationUpsample, modelViewProjection); + m_shaderUpsample->setUniform(m_offsetLocationUpsample, float(1.0)); + m_shaderUpsample->setUniform(m_textureSizeLocationUpsample, QVector2D(1.0, 1.0)); + ShaderManager::instance()->popShader(); + + ShaderManager::instance()->pushShader(m_shaderCopysample); + m_shaderCopysample->setUniform(m_mvpMatrixLocationCopysample, modelViewProjection); + m_shaderCopysample->setUniform(m_textureSizeLocationCopysample, QVector2D(1.0, 1.0)); + m_shaderCopysample->setUniform(m_blurRectLocationCopysample, QVector4D(1.0, 1.0, 1.0, 1.0)); + ShaderManager::instance()->popShader(); + + m_activeSampleType = -1; + } }