diff --git a/effects/blur/blur.cpp b/effects/blur/blur.cpp index 33511d30a..1e7945076 100644 --- a/effects/blur/blur.cpp +++ b/effects/blur/blur.cpp @@ -1,774 +1,771 @@ /* * Copyright © 2010 Fredrik Höglund * Copyright © 2011 Philipp Knechtges * Copyright © 2018 Alex Nemeth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. if not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "blur.h" #include "effects.h" #include "blurshader.h" // KConfigSkeleton #include "blurconfig.h" #include #include #include // for QGuiApplication #include #include // for ceil() #include #include #include #include #include #include namespace KWin { static const QByteArray s_blurAtomName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION"); BlurEffect::BlurEffect() { initConfig(); - m_shader = BlurShader::create(); + m_shader = new BlurShader(this); initBlurStrengthValues(); reconfigure(ReconfigureAll); // ### Hackish way to announce support. // Should be included in _NET_SUPPORTED instead. if (m_shader && m_shader->isValid() && m_renderTargetsValid) { net_wm_blur_region = effects->announceSupportProperty(s_blurAtomName, this); KWayland::Server::Display *display = effects->waylandDisplay(); if (display) { m_blurManager = display->createBlurManager(this); m_blurManager->create(); } } else { net_wm_blur_region = 0; } connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), this, SLOT(slotWindowAdded(KWin::EffectWindow*))); connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*))); connect(effects, SIGNAL(propertyNotify(KWin::EffectWindow*,long)), this, SLOT(slotPropertyNotify(KWin::EffectWindow*,long))); connect(effects, SIGNAL(screenGeometryChanged(QSize)), this, SLOT(slotScreenGeometryChanged())); connect(effects, &EffectsHandler::xcbConnectionChanged, this, [this] { if (m_shader && m_shader->isValid() && m_renderTargetsValid) { net_wm_blur_region = effects->announceSupportProperty(s_blurAtomName, this); } } ); // Fetch the blur regions for all windows foreach (EffectWindow *window, effects->stackingOrder()) updateBlurRegion(window); } BlurEffect::~BlurEffect() { deleteFBOs(); - - delete m_shader; - m_shader = nullptr; } void BlurEffect::slotScreenGeometryChanged() { effects->makeOpenGLContextCurrent(); updateTexture(); // Fetch the blur regions for all windows foreach (EffectWindow *window, effects->stackingOrder()) updateBlurRegion(window); effects->doneOpenGLContextCurrent(); } 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() { qDeleteAll(m_renderTargets); m_renderTargets.clear(); m_renderTextures.clear(); } void BlurEffect::updateTexture() { deleteFBOs(); /* Reserve memory for: * - The original sized texture (1) * - The downsized textures (m_downSampleIterations) * - The helper texture (1) */ m_renderTargets.reserve(m_downSampleIterations + 2); m_renderTextures.reserve(m_downSampleIterations + 2); for (int i = 0; i <= m_downSampleIterations; i++) { m_renderTextures.append(GLTexture(GL_RGBA8, effects->virtualScreenSize() / (1 << 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())); m_renderTargetsValid = renderTargetsValid(); // Prepare the stack for the rendering m_renderTargetStack.clear(); m_renderTargetStack.reserve(m_downSampleIterations * 2); // Upsample for (int i = 1; i < m_downSampleIterations; i++) { m_renderTargetStack.push(m_renderTargets[i]); } // Downsample for (int i = m_downSampleIterations; i > 0; i--) { m_renderTargetStack.push(m_renderTargets[i]); } // Copysample m_renderTargetStack.push(m_renderTargets[0]); // Generate the noise helper texture generateNoiseTexture(); } void BlurEffect::initBlurStrengthValues() { // This function creates an array of blur strength values that are evenly distributed // The range of the slider on the blur settings UI int numOfBlurSteps = 15; int remainingSteps = numOfBlurSteps; /* * Explanation for these numbers: * * The texture blur amount 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 minimum and maximum value of offset per downsample iteration before we * get artifacts. * * The minOffset variable is the minimum offset value for an iteration before we * get blocky artifacts because of the downsampling. * * The maxOffset value is the maximum offset value for an iteration before we * get diagonal line artifacts because of the nature of the dual kawase blur algorithm. * * The expandSize value is the minimum value for an iteration before we reach the end * of a texture in the shader and sample outside of the area that was copied into the * texture from the screen. */ // {minOffset, maxOffset, expandSize} blurOffsets.append({1.0, 2.0, 10}); // Down sample size / 2 blurOffsets.append({2.0, 3.0, 20}); // Down sample size / 4 blurOffsets.append({2.0, 5.0, 50}); // Down sample size / 8 blurOffsets.append({3.0, 8.0, 150}); // Down sample size / 16 //blurOffsets.append({5.0, 10.0, 400}); // Down sample size / 32 //blurOffsets.append({7.0, ?.0}); // Down sample size / 64 float offsetSum = 0; for (int i = 0; i < blurOffsets.size(); i++) { offsetSum += blurOffsets[i].maxOffset - blurOffsets[i].minOffset; } for (int i = 0; i < blurOffsets.size(); i++) { int iterationNumber = std::ceil((blurOffsets[i].maxOffset - blurOffsets[i].minOffset) / offsetSum * numOfBlurSteps); remainingSteps -= iterationNumber; if (remainingSteps < 0) { iterationNumber += remainingSteps; } float offsetDifference = blurOffsets[i].maxOffset - blurOffsets[i].minOffset; for (int j = 1; j <= iterationNumber; j++) { // {iteration, offset} blurStrengthValues.append({i + 1, blurOffsets[i].minOffset + (offsetDifference / iterationNumber) * j}); } } } void BlurEffect::reconfigure(ReconfigureFlags flags) { Q_UNUSED(flags) BlurConfig::self()->read(); int blurStrength = BlurConfig::blurStrength() - 1; m_downSampleIterations = blurStrengthValues[blurStrength].iteration; m_offset = blurStrengthValues[blurStrength].offset; m_expandSize = blurOffsets[m_downSampleIterations - 1].expandSize; m_noiseStrength = BlurConfig::noiseStrength(); m_scalingFactor = qMax(1.0, QGuiApplication::primaryScreen()->logicalDotsPerInch() / 96.0); updateTexture(); if (!m_shader || !m_shader->isValid()) { effects->removeSupportProperty(s_blurAtomName, this); delete m_blurManager; m_blurManager = nullptr; } // Update all windows for the blur to take effect effects->addRepaintFull(); } void BlurEffect::updateBlurRegion(EffectWindow *w) const { QRegion region; QByteArray value; if (net_wm_blur_region != XCB_ATOM_NONE) { value = w->readProperty(net_wm_blur_region, XCB_ATOM_CARDINAL, 32); if (value.size() > 0 && !(value.size() % (4 * sizeof(uint32_t)))) { const uint32_t *cardinals = reinterpret_cast(value.constData()); for (unsigned int i = 0; i < value.size() / sizeof(uint32_t);) { int x = cardinals[i++]; int y = cardinals[i++]; int w = cardinals[i++]; int h = cardinals[i++]; region += QRect(x, y, w, h); } } } KWayland::Server::SurfaceInterface *surf = w->surface(); if (surf && surf->blur()) { region = surf->blur()->region(); } //!value.isNull() full window in X11 case, surf->blur() //valid, full window in wayland case if (region.isEmpty() && (!value.isNull() || (surf && surf->blur()))) { // Set the data to a dummy value. // This is needed to be able to distinguish between the value not // being set, and being set to an empty region. w->setData(WindowBlurBehindRole, 1); } else w->setData(WindowBlurBehindRole, region); } void BlurEffect::slotWindowAdded(EffectWindow *w) { KWayland::Server::SurfaceInterface *surf = w->surface(); if (surf) { windowBlurChangedConnections[w] = connect(surf, &KWayland::Server::SurfaceInterface::blurChanged, this, [this, w] () { if (w) { updateBlurRegion(w); } }); } updateBlurRegion(w); } void BlurEffect::slotWindowDeleted(EffectWindow *w) { auto it = windowBlurChangedConnections.find(w); if (it == windowBlurChangedConnections.end()) { return; } disconnect(*it); windowBlurChangedConnections.erase(it); } void BlurEffect::slotPropertyNotify(EffectWindow *w, long atom) { if (w && atom == net_wm_blur_region && net_wm_blur_region != XCB_ATOM_NONE) { updateBlurRegion(w); } } bool BlurEffect::enabledByDefault() { GLPlatform *gl = GLPlatform::instance(); if (gl->isIntel() && gl->chipClass() < SandyBridge) return false; if (gl->isSoftwareEmulation()) { return false; } return true; } bool BlurEffect::supported() { bool supported = effects->isOpenGLCompositing() && GLRenderTarget::supported() && GLRenderTarget::blitSupported(); if (supported) { int maxTexSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); const QSize screenSize = effects->virtualScreenSize(); if (screenSize.width() > maxTexSize || screenSize.height() > maxTexSize) supported = false; } return supported; } QRect BlurEffect::expand(const QRect &rect) const { return rect.adjusted(-m_expandSize, -m_expandSize, m_expandSize, m_expandSize); } QRegion BlurEffect::expand(const QRegion ®ion) const { QRegion expanded; for (const QRect &rect : region) { expanded += expand(rect); } return expanded; } QRegion BlurEffect::blurRegion(const EffectWindow *w) const { QRegion region; const QVariant value = w->data(WindowBlurBehindRole); if (value.isValid()) { const QRegion appRegion = qvariant_cast(value); if (!appRegion.isEmpty()) { if (w->decorationHasAlpha() && effects->decorationSupportsBlurBehind()) { region = w->shape(); region -= w->decorationInnerRect(); } region |= appRegion.translated(w->contentsRect().topLeft()) & w->decorationInnerRect(); } else { // An empty region means that the blur effect should be enabled // for the whole window. region = w->shape(); } } else if (w->decorationHasAlpha() && effects->decorationSupportsBlurBehind()) { // If the client hasn't specified a blur region, we'll only enable // the effect behind the decoration. region = w->shape(); region -= w->decorationInnerRect(); } return region; } void BlurEffect::uploadRegion(QVector2D *&map, const QRegion ®ion, const int downSampleIterations) { for (int i = 0; i <= downSampleIterations; i++) { const int divisionRatio = (1 << 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 &blurRegion, const QRegion &windowRegion) { const int vertexCount = ((blurRegion.rectCount() * (m_downSampleIterations + 1)) + windowRegion.rectCount()) * 6; if (!vertexCount) return; QVector2D *map = (QVector2D *) vbo->map(vertexCount * sizeof(QVector2D)); uploadRegion(map, blurRegion, m_downSampleIterations); uploadRegion(map, windowRegion, 0); vbo->unmap(); const GLVertexAttrib layout[] = { { VA_Position, 2, GL_FLOAT, 0 }, { VA_TexCoord, 2, GL_FLOAT, 0 } }; vbo->setAttribLayout(layout, 2, sizeof(QVector2D)); } void BlurEffect::prePaintScreen(ScreenPrePaintData &data, int time) { m_damagedArea = QRegion(); m_paintedArea = QRegion(); m_currentBlur = QRegion(); effects->prePaintScreen(data, time); } void BlurEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { // this effect relies on prePaintWindow being called in the bottom to top order effects->prePaintWindow(w, data, time); if (!w->isPaintingEnabled()) { return; } 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; for (const QRect &rect : data.clip) { newClip |= rect.adjusted(m_expandSize, m_expandSize, -m_expandSize, -m_expandSize); } data.clip = newClip; const QRegion oldPaint = data.paint; // we don't have to blur a region we don't see m_currentBlur -= newClip; // if we have to paint a non-opaque part of this window that intersects with the // currently blurred region we have to redraw the whole region if ((data.paint - oldClip).intersects(m_currentBlur)) { data.paint |= m_currentBlur; } // in case this window has regions to be blurred const QRect screen = effects->virtualScreenGeometry(); const QRegion blurArea = blurRegion(w).translated(w->pos()) & screen; const QRegion expandedBlur = (w->isDock() ? blurArea : expand(blurArea)) & screen; // if this window or a window underneath the blurred area is painted again we have to // blur everything if (m_paintedArea.intersects(expandedBlur) || data.paint.intersects(blurArea)) { data.paint |= expandedBlur; // we keep track of the "damage propagation" m_damagedArea |= (w->isDock() ? (expandedBlur & m_damagedArea) : expand(expandedBlur & m_damagedArea)) & blurArea; // we have to check again whether we do not damage a blurred area // of a window if (expandedBlur.intersects(m_currentBlur)) { data.paint |= m_currentBlur; } } m_currentBlur |= expandedBlur; // we don't consider damaged areas which are occluded and are not // explicitly damaged by this window m_damagedArea -= data.clip; m_damagedArea |= oldPaint; // in contrast to m_damagedArea does m_paintedArea keep track of all repainted areas m_paintedArea -= data.clip; m_paintedArea |= data.paint; } bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data) const { if (!m_renderTargetsValid || !m_shader || !m_shader->isValid()) return false; if (effects->activeFullScreenEffect() && !w->data(WindowForceBlurRole).toBool()) return false; if (w->isDesktop()) return false; bool scaled = !qFuzzyCompare(data.xScale(), 1.0) && !qFuzzyCompare(data.yScale(), 1.0); bool translated = data.xTranslation() || data.yTranslation(); if ((scaled || (translated || (mask & PAINT_WINDOW_TRANSFORMED))) && !w->data(WindowForceBlurRole).toBool()) return false; bool blurBehindDecos = effects->decorationsHaveAlpha() && effects->decorationSupportsBlurBehind(); if (!w->hasAlpha() && w->opacity() >= 1.0 && !(blurBehindDecos && w->hasDecoration())) return false; return true; } void BlurEffect::drawWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) { const QRect screen = GLRenderTarget::virtualScreenGeometry(); if (shouldBlur(w, mask, data)) { QRegion shape = region & blurRegion(w).translated(w->pos()) & screen; // let's do the evil parts - someone wants to blur behind a transformed window const bool translated = data.xTranslation() || data.yTranslation(); const bool scaled = data.xScale() != 1 || data.yScale() != 1; if (scaled) { QPoint pt = shape.boundingRect().topLeft(); QVector shapeRects = shape.rects(); shape = QRegion(); // clear foreach (QRect r, shapeRects) { r.moveTo(pt.x() + (r.x() - pt.x()) * data.xScale() + data.xTranslation(), pt.y() + (r.y() - pt.y()) * data.yScale() + data.yTranslation()); r.setWidth(r.width() * data.xScale()); r.setHeight(r.height() * data.yScale()); shape |= r; } shape = shape & region; //Only translated, not scaled } else if (translated) { shape = shape.translated(data.xTranslation(), data.yTranslation()); shape = shape & region; } if (!shape.isEmpty()) { doBlur(shape, screen, data.opacity(), data.screenProjectionMatrix(), w->isDock(), w->geometry()); } } // Draw the window over the blurred area effects->drawWindow(w, mask, region, data); } void BlurEffect::paintEffectFrame(EffectFrame *frame, QRegion region, double opacity, double frameOpacity) { const QRect screen = effects->virtualScreenGeometry(); bool valid = m_renderTargetsValid && m_shader && m_shader->isValid(); QRegion shape = frame->geometry().adjusted(-borderSize, -borderSize, borderSize, borderSize) & screen; if (valid && !shape.isEmpty() && region.intersects(shape.boundingRect()) && frame->style() != EffectFrameNone) { doBlur(shape, screen, opacity * frameOpacity, frame->screenProjectionMatrix(), false, frame->geometry()); } effects->paintEffectFrame(frame, region, opacity, frameOpacity); } void BlurEffect::generateNoiseTexture() { if (m_noiseStrength == 0) { return; } // Init randomness based on time qsrand((uint)QTime::currentTime().msec()); QImage noiseImage(QSize(256, 256), QImage::Format_Grayscale8); for (int y = 0; y < noiseImage.height(); y++) { uint8_t *noiseImageLine = (uint8_t *) noiseImage.scanLine(y); for (int x = 0; x < noiseImage.width(); x++) { noiseImageLine[x] = qrand() % m_noiseStrength + (128 - m_noiseStrength / 2); } } // The noise texture looks distorted when not scaled with integer noiseImage = noiseImage.scaled(noiseImage.size() * m_scalingFactor); m_noiseTexture = GLTexture(noiseImage); m_noiseTexture.setFilter(GL_NEAREST); m_noiseTexture.setWrapMode(GL_REPEAT); } void BlurEffect::doBlur(const QRegion& shape, const QRect& screen, const float opacity, const QMatrix4x4 &screenProjection, bool isDock, QRect windowRect) { // Blur would not render correctly on a secondary monitor because of wrong coordinates // BUG: 393723 const int xTranslate = -screen.x(); const int yTranslate = effects->virtualScreenSize().height() - screen.height() - screen.y(); const QRegion expandedBlurRegion = expand(shape) & expand(screen); // Upload geometry for the down and upsample iterations GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); uploadGeometry(vbo, expandedBlurRegion.translated(xTranslate, yTranslate), shape); vbo->bindArrays(); const QRect sourceRect = expandedBlurRegion.boundingRect() & screen; const QRect destRect = sourceRect.translated(xTranslate, yTranslate); GLRenderTarget::pushRenderTargets(m_renderTargetStack); int blurRectCount = expandedBlurRegion.rectCount() * 6; /* * If the window is a dock or panel we avoid the "extended blur" effect. * Extended blur is when windows that are not under the blurred area affect * the final blur result. * We want to avoid this on panels, because it looks really weird and ugly * when maximized windows or windows near the panel affect the dock blur. */ if (isDock) { m_renderTargets.last()->blitFromFramebuffer(sourceRect, destRect); copyScreenSampleTexture(vbo, blurRectCount, shape.translated(xTranslate, yTranslate), screenProjection); } else { m_renderTargets.first()->blitFromFramebuffer(sourceRect, destRect); // Remove the m_renderTargets[0] from the top of the stack that we will not use GLRenderTarget::popRenderTarget(); } downSampleTexture(vbo, blurRectCount); upSampleTexture(vbo, blurRectCount); // Modulate the blurred texture with the window opacity if the window isn't opaque if (opacity < 1.0) { glEnable(GL_BLEND); #if 1 // bow shape, always above y = x float o = 1.0f-opacity; o = 1.0f - o*o; #else // sigmoid shape, above y = x for x > 0.5, below y = x for x < 0.5 float o = 2.0f*opacity - 1.0f; o = 0.5f + o / (1.0f + qAbs(o)); #endif glBlendColor(0, 0, 0, o); glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); } upscaleRenderToScreen(vbo, blurRectCount * (m_downSampleIterations + 1), shape.rectCount() * 6, screenProjection, windowRect.topLeft()); if (opacity < 1.0) { glDisable(GL_BLEND); } vbo->unbindArrays(); } void BlurEffect::upscaleRenderToScreen(GLVertexBuffer *vbo, int vboStart, int blurRectCount, QMatrix4x4 screenProjection, QPoint windowPosition) { glActiveTexture(GL_TEXTURE0); m_renderTextures[1].bind(); if (m_noiseStrength > 0) { m_shader->bind(BlurShader::NoiseSampleType); m_shader->setTargetTextureSize(m_renderTextures[0].size() * GLRenderTarget::virtualScreenScale()); m_shader->setNoiseTextureSize(m_noiseTexture.size() * GLRenderTarget::virtualScreenScale()); m_shader->setTexturePosition(windowPosition * GLRenderTarget::virtualScreenScale()); glActiveTexture(GL_TEXTURE1); m_noiseTexture.bind(); } else { m_shader->bind(BlurShader::UpSampleType); m_shader->setTargetTextureSize(m_renderTextures[0].size() * GLRenderTarget::virtualScreenScale()); } m_shader->setOffset(m_offset); m_shader->setModelViewProjectionMatrix(screenProjection); //Render to the screen vbo->draw(GL_TRIANGLES, vboStart, blurRectCount); glActiveTexture(GL_TEXTURE0); m_shader->unbind(); } void BlurEffect::downSampleTexture(GLVertexBuffer *vbo, int blurRectCount) { 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->setTargetTextureSize(m_renderTextures[i].size()); //Copy the image from this texture m_renderTextures[i - 1].bind(); vbo->draw(GL_TRIANGLES, blurRectCount * i, blurRectCount); GLRenderTarget::popRenderTarget(); } 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 >= 1; i--) { modelViewProjectionMatrix.setToIdentity(); modelViewProjectionMatrix.ortho(0, m_renderTextures[i].width(), m_renderTextures[i].height(), 0 , 0, 65535); m_shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); m_shader->setTargetTextureSize(m_renderTextures[i].size()); //Copy the image from this texture m_renderTextures[i + 1].bind(); vbo->draw(GL_TRIANGLES, blurRectCount * i, blurRectCount); GLRenderTarget::popRenderTarget(); } m_shader->unbind(); } void BlurEffect::copyScreenSampleTexture(GLVertexBuffer *vbo, int blurRectCount, QRegion blurShape, QMatrix4x4 screenProjection) { m_shader->bind(BlurShader::CopySampleType); m_shader->setModelViewProjectionMatrix(screenProjection); m_shader->setTargetTextureSize(effects->virtualScreenSize()); /* * This '1' sized adjustment is necessary do avoid windows affecting the blur that are * right next to this window. */ m_shader->setBlurRect(blurShape.boundingRect().adjusted(1, 1, -1, -1), effects->virtualScreenSize()); m_renderTextures.last().bind(); vbo->draw(GL_TRIANGLES, 0, blurRectCount); GLRenderTarget::popRenderTarget(); m_shader->unbind(); } } // namespace KWin diff --git a/effects/blur/blurshader.cpp b/effects/blur/blurshader.cpp index c07974dd3..2495cd4d7 100644 --- a/effects/blur/blurshader.cpp +++ b/effects/blur/blurshader.cpp @@ -1,480 +1,445 @@ /* * Copyright © 2010 Fredrik Höglund * Copyright © 2018 Alex Nemeth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. if not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "blurshader.h" #include -#include "kwinglutils.h" #include +#include #include -#include #include -#include -#include - -using namespace KWin; - - -BlurShader::BlurShader() - : mValid(false) -{ -} - -BlurShader::~BlurShader() -{ -} - -BlurShader *BlurShader::create() -{ - return new GLSLBlurShader(); -} - -// ---------------------------------------------------------------------------- - - - -GLSLBlurShader::GLSLBlurShader() -{ - init(); -} - -GLSLBlurShader::~GLSLBlurShader() -{ - reset(); -} - -void GLSLBlurShader::reset() -{ - delete m_shaderDownsample; - m_shaderDownsample = nullptr; - - delete m_shaderUpsample; - m_shaderUpsample = nullptr; - - delete m_shaderCopysample; - m_shaderCopysample = nullptr; - - delete m_shaderNoisesample; - m_shaderNoisesample = nullptr; - - setIsValid(false); -} - -void GLSLBlurShader::setModelViewProjectionMatrix(const QMatrix4x4 &matrix) -{ - if (!isValid()) - return; - - switch (m_activeSampleType) { - case CopySampleType: - if (matrix == m_matrixCopysample) - return; - - m_matrixCopysample = matrix; - m_shaderCopysample->setUniform(m_mvpMatrixLocationCopysample, matrix); - break; - - case UpSampleType: - if (matrix == m_matrixUpsample) - return; - - m_matrixUpsample = matrix; - m_shaderUpsample->setUniform(m_mvpMatrixLocationUpsample, matrix); - break; - - case DownSampleType: - if (matrix == m_matrixDownsample) - return; - - m_matrixDownsample = matrix; - m_shaderDownsample->setUniform(m_mvpMatrixLocationDownsample, matrix); - break; - - case NoiseSampleType: - if (matrix == m_matrixNoisesample) - return; - - m_matrixNoisesample = matrix; - m_shaderNoisesample->setUniform(m_mvpMatrixLocationNoisesample, matrix); - break; - } -} - -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; - - m_offsetDownsample = offset; - m_shaderDownsample->setUniform(m_offsetLocationDownsample, offset); - break; - - case NoiseSampleType: - if (offset == m_offsetNoisesample) - return; - - m_offsetNoisesample = offset; - m_shaderNoisesample->setUniform(m_offsetLocationNoisesample, offset); - break; - } -} - -void GLSLBlurShader::setTargetTextureSize(QSize renderTextureSize) -{ - if (!isValid()) - return; - - QVector2D texSize = QVector2D(renderTextureSize.width(), renderTextureSize.height()); - - switch (m_activeSampleType) { - case CopySampleType: - m_shaderCopysample->setUniform(m_renderTextureSizeLocationCopysample, texSize); - break; - - case UpSampleType: - m_shaderUpsample->setUniform(m_renderTextureSizeLocationUpsample, texSize); - m_shaderUpsample->setUniform(m_halfpixelLocationUpsample, QVector2D(0.5 / texSize.x(), 0.5 / texSize.y())); - break; - - case DownSampleType: - m_shaderDownsample->setUniform(m_renderTextureSizeLocationDownsample, texSize); - m_shaderDownsample->setUniform(m_halfpixelLocationDownsample, QVector2D(0.5 / texSize.x(), 0.5 / texSize.y())); - break; - - case NoiseSampleType: - m_shaderNoisesample->setUniform(m_renderTextureSizeLocationNoisesample, texSize); - m_shaderNoisesample->setUniform(m_halfpixelLocationNoisesample, QVector2D(0.5 / texSize.x(), 0.5 / texSize.y())); - break; - } -} - -void GLSLBlurShader::setNoiseTextureSize(QSize noiseTextureSize) -{ - QVector2D noiseTexSize = QVector2D(noiseTextureSize.width(), noiseTextureSize.height()); - - if (noiseTexSize != m_noiseTextureSizeNoisesample) { - m_noiseTextureSizeNoisesample = noiseTexSize; - m_shaderNoisesample->setUniform(m_noiseTextureSizeLocationNoisesample, noiseTexSize); - } -} - -void GLSLBlurShader::setTexturePosition(QPoint texPos) -{ - m_shaderNoisesample->setUniform(m_texStartPosLocationNoisesample, QVector2D(-texPos.x(), texPos.y())); -} - -void GLSLBlurShader::setBlurRect(QRect blurRect, QSize screenSize) -{ - if (!isValid()) - return; - - 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(SampleType sampleType) -{ - if (!isValid()) - return; - - 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; - - case NoiseSampleType: - ShaderManager::instance()->pushShader(m_shaderNoisesample); - break; - } - - m_activeSampleType = sampleType; -} - -void GLSLBlurShader::unbind() +namespace KWin { - ShaderManager::instance()->popShader(); -} -void GLSLBlurShader::init() +BlurShader::BlurShader(QObject *parent) + : QObject(parent) { 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 fragmentDownSource; QByteArray fragmentUpSource; QByteArray fragmentCopySource; QByteArray fragmentNoiseSource; const QByteArray attribute = core ? "in" : "attribute"; const QByteArray texture2D = core ? "texture" : "texture2D"; const QByteArray fragColor = core ? "fragColor" : "gl_FragColor"; QString glHeaderString; if (gles) { if (core) { glHeaderString += "#version 300 es\n\n"; } glHeaderString += "precision highp float;\n"; } else if (glsl_140) { glHeaderString += "#version 140\n\n"; } QString glUniformString = "uniform sampler2D texUnit;\n" "uniform float offset;\n" "uniform vec2 renderTextureSize;\n" "uniform vec2 halfpixel;\n"; if (core) { glUniformString += "out vec4 fragColor;\n\n"; } - // Vertex shader - // =================================================================== QTextStream streamVert(&vertexSource); 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 / renderTextureSize);\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 / renderTextureSize);\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); streamFragCopy << glHeaderString; streamFragCopy << "uniform sampler2D texUnit;\n"; streamFragCopy << "uniform vec2 renderTextureSize;\n"; streamFragCopy << "uniform vec4 blurRect;\n"; - if (core) + if (core) { streamFragCopy << "out vec4 fragColor;\n\n"; + } streamFragCopy << "void main(void)\n"; streamFragCopy << "{\n"; streamFragCopy << " vec2 uv = vec2(gl_FragCoord.xy / renderTextureSize);\n"; streamFragCopy << " " << fragColor << " = " << texture2D << "(texUnit, clamp(uv, blurRect.xy, blurRect.zw));\n"; streamFragCopy << "}\n"; streamFragCopy.flush(); // Fragment shader - Noise texture - // =================================================================== QTextStream streamFragNoise(&fragmentNoiseSource); streamFragNoise << glHeaderString << glUniformString; streamFragNoise << "uniform sampler2D noiseTexUnit;\n"; streamFragNoise << "uniform vec2 noiseTextureSize;\n"; streamFragNoise << "uniform vec2 texStartPos;\n"; // Upsampling + Noise streamFragNoise << "void main(void)\n"; streamFragNoise << "{\n"; streamFragNoise << " vec2 uv = vec2(gl_FragCoord.xy / renderTextureSize);\n"; streamFragNoise << " vec2 uvNoise = vec2((texStartPos.xy + gl_FragCoord.xy) / noiseTextureSize);\n"; streamFragNoise << " \n"; streamFragNoise << " vec4 sum = " << texture2D << "(texUnit, uv + vec2(-halfpixel.x * 2.0, 0.0) * offset);\n"; streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(-halfpixel.x, halfpixel.y) * offset) * 2.0;\n"; streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(0.0, halfpixel.y * 2.0) * offset);\n"; streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x, halfpixel.y) * offset) * 2.0;\n"; streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x * 2.0, 0.0) * offset);\n"; streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x, -halfpixel.y) * offset) * 2.0;\n"; streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(0.0, -halfpixel.y * 2.0) * offset);\n"; streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(-halfpixel.x, -halfpixel.y) * offset) * 2.0;\n"; streamFragNoise << " \n"; streamFragNoise << " " << fragColor << " = sum / 12.0 - (vec4(0.5, 0.5, 0.5, 0) - vec4(" << texture2D << "(noiseTexUnit, uvNoise).rrr, 0));\n"; streamFragNoise << "}\n"; streamFragNoise.flush(); + m_shaderDownsample.reset(ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentDownSource)); + m_shaderUpsample.reset(ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentUpSource)); + m_shaderCopysample.reset(ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentCopySource)); + m_shaderNoisesample.reset(ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentNoiseSource)); - m_shaderDownsample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentDownSource); - m_shaderUpsample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentUpSource); - m_shaderCopysample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentCopySource); - m_shaderNoisesample = ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentNoiseSource); - - bool areShadersValid = m_shaderDownsample->isValid() && + m_valid = m_shaderDownsample->isValid() && m_shaderUpsample->isValid() && m_shaderCopysample->isValid() && m_shaderNoisesample->isValid(); - setIsValid(areShadersValid); - if (areShadersValid) { + if (m_valid) { m_mvpMatrixLocationDownsample = m_shaderDownsample->uniformLocation("modelViewProjectionMatrix"); m_offsetLocationDownsample = m_shaderDownsample->uniformLocation("offset"); m_renderTextureSizeLocationDownsample = m_shaderDownsample->uniformLocation("renderTextureSize"); m_halfpixelLocationDownsample = m_shaderDownsample->uniformLocation("halfpixel"); m_mvpMatrixLocationUpsample = m_shaderUpsample->uniformLocation("modelViewProjectionMatrix"); m_offsetLocationUpsample = m_shaderUpsample->uniformLocation("offset"); m_renderTextureSizeLocationUpsample = m_shaderUpsample->uniformLocation("renderTextureSize"); m_halfpixelLocationUpsample = m_shaderUpsample->uniformLocation("halfpixel"); m_mvpMatrixLocationCopysample = m_shaderCopysample->uniformLocation("modelViewProjectionMatrix"); m_renderTextureSizeLocationCopysample = m_shaderCopysample->uniformLocation("renderTextureSize"); m_blurRectLocationCopysample = m_shaderCopysample->uniformLocation("blurRect"); m_mvpMatrixLocationNoisesample = m_shaderNoisesample->uniformLocation("modelViewProjectionMatrix"); m_offsetLocationNoisesample = m_shaderNoisesample->uniformLocation("offset"); m_renderTextureSizeLocationNoisesample = m_shaderNoisesample->uniformLocation("renderTextureSize"); m_noiseTextureSizeLocationNoisesample = m_shaderNoisesample->uniformLocation("noiseTextureSize"); m_texStartPosLocationNoisesample = m_shaderNoisesample->uniformLocation("texStartPos"); m_halfpixelLocationNoisesample = m_shaderNoisesample->uniformLocation("halfpixel"); QMatrix4x4 modelViewProjection; const QSize screenSize = effects->virtualScreenSize(); modelViewProjection.ortho(0, screenSize.width(), screenSize.height(), 0, 0, 65535); //Add default values to the uniforms of the shaders - ShaderManager::instance()->pushShader(m_shaderDownsample); + ShaderManager::instance()->pushShader(m_shaderDownsample.data()); m_shaderDownsample->setUniform(m_mvpMatrixLocationDownsample, modelViewProjection); m_shaderDownsample->setUniform(m_offsetLocationDownsample, float(1.0)); m_shaderDownsample->setUniform(m_renderTextureSizeLocationDownsample, QVector2D(1.0, 1.0)); m_shaderDownsample->setUniform(m_halfpixelLocationDownsample, QVector2D(1.0, 1.0)); ShaderManager::instance()->popShader(); - ShaderManager::instance()->pushShader(m_shaderUpsample); + ShaderManager::instance()->pushShader(m_shaderUpsample.data()); m_shaderUpsample->setUniform(m_mvpMatrixLocationUpsample, modelViewProjection); m_shaderUpsample->setUniform(m_offsetLocationUpsample, float(1.0)); m_shaderUpsample->setUniform(m_renderTextureSizeLocationUpsample, QVector2D(1.0, 1.0)); m_shaderUpsample->setUniform(m_halfpixelLocationUpsample, QVector2D(1.0, 1.0)); ShaderManager::instance()->popShader(); - ShaderManager::instance()->pushShader(m_shaderCopysample); + ShaderManager::instance()->pushShader(m_shaderCopysample.data()); m_shaderCopysample->setUniform(m_mvpMatrixLocationCopysample, modelViewProjection); m_shaderCopysample->setUniform(m_renderTextureSizeLocationCopysample, QVector2D(1.0, 1.0)); m_shaderCopysample->setUniform(m_blurRectLocationCopysample, QVector4D(1.0, 1.0, 1.0, 1.0)); ShaderManager::instance()->popShader(); - ShaderManager::instance()->pushShader(m_shaderNoisesample); + ShaderManager::instance()->pushShader(m_shaderNoisesample.data()); m_shaderNoisesample->setUniform(m_mvpMatrixLocationNoisesample, modelViewProjection); m_shaderNoisesample->setUniform(m_offsetLocationNoisesample, float(1.0)); m_shaderNoisesample->setUniform(m_renderTextureSizeLocationNoisesample, QVector2D(1.0, 1.0)); m_shaderNoisesample->setUniform(m_noiseTextureSizeLocationNoisesample, QVector2D(1.0, 1.0)); m_shaderNoisesample->setUniform(m_texStartPosLocationNoisesample, QVector2D(1.0, 1.0)); m_shaderNoisesample->setUniform(m_halfpixelLocationNoisesample, QVector2D(1.0, 1.0)); glUniform1i(m_shaderNoisesample->uniformLocation("texUnit"), 0); glUniform1i(m_shaderNoisesample->uniformLocation("noiseTexUnit"), 1); ShaderManager::instance()->popShader(); + } +} + +BlurShader::~BlurShader() +{ +} + +void BlurShader::setModelViewProjectionMatrix(const QMatrix4x4 &matrix) +{ + if (!isValid()) { + return; + } - m_activeSampleType = -1; + switch (m_activeSampleType) { + case CopySampleType: + if (matrix == m_matrixCopysample) { + return; + } + + m_matrixCopysample = matrix; + m_shaderCopysample->setUniform(m_mvpMatrixLocationCopysample, matrix); + break; - m_offsetDownsample = 0.0; - m_matrixDownsample = QMatrix4x4(); + case UpSampleType: + if (matrix == m_matrixUpsample) { + return; + } + + m_matrixUpsample = matrix; + m_shaderUpsample->setUniform(m_mvpMatrixLocationUpsample, matrix); + break; - m_offsetUpsample = 0.0; - m_matrixUpsample = QMatrix4x4(); + case DownSampleType: + if (matrix == m_matrixDownsample) { + return; + } - m_matrixCopysample = QMatrix4x4(); + m_matrixDownsample = matrix; + m_shaderDownsample->setUniform(m_mvpMatrixLocationDownsample, matrix); + break; - m_offsetNoisesample = 0.0; - m_noiseTextureSizeNoisesample = QVector2D(); - m_matrixNoisesample = QMatrix4x4(); + case NoiseSampleType: + if (matrix == m_matrixNoisesample) { + return; + } + + m_matrixNoisesample = matrix; + m_shaderNoisesample->setUniform(m_mvpMatrixLocationNoisesample, matrix); + break; + + default: + Q_UNREACHABLE(); + break; } } + +void BlurShader::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; + } + + m_offsetDownsample = offset; + m_shaderDownsample->setUniform(m_offsetLocationDownsample, offset); + break; + + case NoiseSampleType: + if (offset == m_offsetNoisesample) { + return; + } + + m_offsetNoisesample = offset; + m_shaderNoisesample->setUniform(m_offsetLocationNoisesample, offset); + break; + + default: + Q_UNREACHABLE(); + break; + } +} + +void BlurShader::setTargetTextureSize(const QSize &renderTextureSize) +{ + if (!isValid()) { + return; + } + + const QVector2D texSize(renderTextureSize.width(), renderTextureSize.height()); + + switch (m_activeSampleType) { + case CopySampleType: + m_shaderCopysample->setUniform(m_renderTextureSizeLocationCopysample, texSize); + break; + + case UpSampleType: + m_shaderUpsample->setUniform(m_renderTextureSizeLocationUpsample, texSize); + m_shaderUpsample->setUniform(m_halfpixelLocationUpsample, QVector2D(0.5 / texSize.x(), 0.5 / texSize.y())); + break; + + case DownSampleType: + m_shaderDownsample->setUniform(m_renderTextureSizeLocationDownsample, texSize); + m_shaderDownsample->setUniform(m_halfpixelLocationDownsample, QVector2D(0.5 / texSize.x(), 0.5 / texSize.y())); + break; + + case NoiseSampleType: + m_shaderNoisesample->setUniform(m_renderTextureSizeLocationNoisesample, texSize); + m_shaderNoisesample->setUniform(m_halfpixelLocationNoisesample, QVector2D(0.5 / texSize.x(), 0.5 / texSize.y())); + break; + + default: + Q_UNREACHABLE(); + break; + } +} + +void BlurShader::setNoiseTextureSize(const QSize &noiseTextureSize) +{ + const QVector2D noiseTexSize(noiseTextureSize.width(), noiseTextureSize.height()); + + if (noiseTexSize != m_noiseTextureSizeNoisesample) { + m_noiseTextureSizeNoisesample = noiseTexSize; + m_shaderNoisesample->setUniform(m_noiseTextureSizeLocationNoisesample, noiseTexSize); + } +} + +void BlurShader::setTexturePosition(const QPoint &texPos) +{ + m_shaderNoisesample->setUniform(m_texStartPosLocationNoisesample, QVector2D(-texPos.x(), texPos.y())); +} + +void BlurShader::setBlurRect(const QRect &blurRect, const QSize &screenSize) +{ + if (!isValid()) { + return; + } + + const QVector4D rect( + blurRect.left() / float(screenSize.width()), + 1.0 - blurRect.bottom() / float(screenSize.height()), + blurRect.right() / float(screenSize.width()), + 1.0 - blurRect.top() / float(screenSize.height()) + ); + + m_shaderCopysample->setUniform(m_blurRectLocationCopysample, rect); +} + +void BlurShader::bind(SampleType sampleType) +{ + if (!isValid()) { + return; + } + + switch (sampleType) { + case CopySampleType: + ShaderManager::instance()->pushShader(m_shaderCopysample.data()); + break; + + case UpSampleType: + ShaderManager::instance()->pushShader(m_shaderUpsample.data()); + break; + + case DownSampleType: + ShaderManager::instance()->pushShader(m_shaderDownsample.data()); + break; + + case NoiseSampleType: + ShaderManager::instance()->pushShader(m_shaderNoisesample.data()); + break; + + default: + Q_UNREACHABLE(); + break; + } + + m_activeSampleType = sampleType; +} + +void BlurShader::unbind() +{ + ShaderManager::instance()->popShader(); +} + +} // namespace KWin diff --git a/effects/blur/blurshader.h b/effects/blur/blurshader.h index 440f526f4..7158e2b4b 100644 --- a/effects/blur/blurshader.h +++ b/effects/blur/blurshader.h @@ -1,145 +1,116 @@ /* * Copyright © 2010 Fredrik Höglund * Copyright © 2018 Alex Nemeth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. if not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef BLURSHADER_H #define BLURSHADER_H #include + +#include +#include +#include #include #include - -class QMatrix4x4; - namespace KWin { -class BlurShader +class BlurShader : public QObject { -public: - BlurShader(); - virtual ~BlurShader(); - - static BlurShader *create(); + Q_OBJECT - bool isValid() const { - return mValid; - } +public: + BlurShader(QObject *parent = nullptr); + ~BlurShader() override; - virtual void setModelViewProjectionMatrix(const QMatrix4x4 &matrix) = 0; - virtual void setOffset(float offset) = 0; - virtual void setTargetTextureSize(QSize renderTextureSize) = 0; - virtual void setNoiseTextureSize(QSize noiseTextureSize) = 0; - virtual void setTexturePosition(QPoint texPos) = 0; - virtual void setBlurRect(QRect blurRect, QSize screenSize) = 0; + bool isValid() const; enum SampleType { DownSampleType, UpSampleType, CopySampleType, NoiseSampleType }; - virtual void bind(SampleType sampleType) = 0; - virtual void unbind() = 0; - -protected: - void setIsValid(bool value) { - mValid = value; - } - virtual void init() = 0; - virtual void reset() = 0; - -private: - bool mValid; -}; - - -// ---------------------------------------------------------------------------- - + void bind(SampleType sampleType); + void unbind(); - -class GLSLBlurShader : public BlurShader -{ -public: - GLSLBlurShader(); - ~GLSLBlurShader(); - - void bind(SampleType sampleType) override final; - void unbind() override final; - void setModelViewProjectionMatrix(const QMatrix4x4 &matrix) override final; - void setOffset(float offset) override final; - void setTargetTextureSize(QSize renderTextureSize) override final; - void setNoiseTextureSize(QSize noiseTextureSize) override final; - void setTexturePosition(QPoint texPos) override final; - void setBlurRect(QRect blurRect, QSize screenSize) override final; - -protected: - void init() override final; - void reset() override final; + void setModelViewProjectionMatrix(const QMatrix4x4 &matrix); + void setOffset(float offset); + void setTargetTextureSize(const QSize &renderTextureSize); + void setNoiseTextureSize(const QSize &noiseTextureSize); + void setTexturePosition(const QPoint &texPos); + void setBlurRect(const QRect &blurRect, const QSize &screenSize); private: - GLShader *m_shaderDownsample = nullptr; - GLShader *m_shaderUpsample = nullptr; - GLShader *m_shaderCopysample = nullptr; - GLShader *m_shaderNoisesample = nullptr; + QScopedPointer m_shaderDownsample; + QScopedPointer m_shaderUpsample; + QScopedPointer m_shaderCopysample; + QScopedPointer m_shaderNoisesample; int m_mvpMatrixLocationDownsample; int m_offsetLocationDownsample; int m_renderTextureSizeLocationDownsample; int m_halfpixelLocationDownsample; int m_mvpMatrixLocationUpsample; int m_offsetLocationUpsample; int m_renderTextureSizeLocationUpsample; int m_halfpixelLocationUpsample; int m_mvpMatrixLocationCopysample; int m_renderTextureSizeLocationCopysample; int m_blurRectLocationCopysample; int m_mvpMatrixLocationNoisesample; int m_offsetLocationNoisesample; int m_renderTextureSizeLocationNoisesample; int m_noiseTextureSizeLocationNoisesample; int m_texStartPosLocationNoisesample; int m_halfpixelLocationNoisesample; - //Caching uniform values to aviod unnecessary setUniform calls - int m_activeSampleType; + int m_activeSampleType = -1; - float m_offsetDownsample; + float m_offsetDownsample = 0.0; QMatrix4x4 m_matrixDownsample; - float m_offsetUpsample; + float m_offsetUpsample = 0.0; QMatrix4x4 m_matrixUpsample; QMatrix4x4 m_matrixCopysample; - float m_offsetNoisesample; + float m_offsetNoisesample = 0.0; QVector2D m_noiseTextureSizeNoisesample; QMatrix4x4 m_matrixNoisesample; + bool m_valid = false; + + Q_DISABLE_COPY(BlurShader); }; +inline bool BlurShader::isValid() const +{ + return m_valid; +} + } // namespace KWin #endif