Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions src/blur.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,16 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi
#else
m_blurCache->preparePaintData(m_currentView, w, &dirtyRegion, renderInfo.framebuffers[0].get(), &backgroundRect, &scaledBackgroundRect, renderInfo.cache);

// BBDX: If the dirtyRegion doesn't intersect backgroundRect nothing behind the
// window was repainted this frame - the renderTarget only holds stale data
// there. Without a cache entry to fall back on there is nothing valid to
// draw or build a cache entry from, so bail. The on-screen draw would be
// clipped to the deviceRegion anyway.
if (dirtyRegion.isEmpty() && !renderInfo.cache.get()) {
return;
}

if (!m_blurCache->useCachedOnly()) {
// BBDX: Always blit the entire backgroundRect to avoid subtle rounding errors on scaled RenderViews.
// It took me way too many hours to figure out that this is what's causing sporadic
// pixel mismatches during textureCompare...
Expand All @@ -1037,6 +1047,7 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi
if (!renderInfo.cache.get() || renderInfo.cache.get()->isFlushing) {
renderInfo.framebuffers[0]->blitFromRenderTarget(renderTarget, viewport, backgroundRect, backgroundRect.translated(-backgroundRect.topLeft()));
}
} // indent intentional for KWin diff
#endif

// Upload the geometry: the first 6 vertices are used when downsampling and upsampling offscreen,
Expand Down Expand Up @@ -1160,6 +1171,16 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi
return;
}

#if KWIN_VERSION < KWIN_VERSION_CODE(6, 6, 90)
const QMatrix4x4 &colorMatrix = blurInfo.colorMatrix ? *blurInfo.colorMatrix : m_colorMatrix;
#else
const QMatrix4x4 &colorMatrix = m_colorMatrix;
#endif
const float modulation = opacity * opacity;

// BBDX: without fresh data there is nothing to rebuild the cache from -
// skip all blur passes and only draw the cached texture below
if (!m_blurCache->useCachedOnly()) {
// The downsample pass of the dual Kawase algorithm: the background will be scaled down 50% every iteration.
{
ShaderManager::instance()->pushShader(m_downsamplePass.shader.get());
Expand Down Expand Up @@ -1221,13 +1242,6 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi
ShaderManager::instance()->popShader();
}

#if KWIN_VERSION < KWIN_VERSION_CODE(6, 6, 90)
const QMatrix4x4 &colorMatrix = blurInfo.colorMatrix ? *blurInfo.colorMatrix : m_colorMatrix;
#else
const QMatrix4x4 &colorMatrix = m_colorMatrix;
#endif
const float modulation = opacity * opacity;

#if BETTERBLUR_NOT_NEEDED
if (const BorderRadius cornerRadius = w->window()->borderRadius(); !cornerRadius.isNull()) {
ShaderManager::instance()->pushShader(m_roundedOnscreenPass.shader.get());
Expand Down Expand Up @@ -1362,6 +1376,7 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi
if (const BorderRadius cornerRadius = m_windowManager->getEffectiveBorderRadius(w); !cornerRadius.isNull()) {
m_roundedCornersPass->apply(cornerRadius, viewport, scaledBackgroundRect, renderInfo, w, data, vbo, m_blurCache.get());
}
} // indent intentional for KWin diff

// BBDX:
m_blurCache->drawCached(viewport, renderInfo, vbo, vertexCount, modulation);
Expand Down
14 changes: 14 additions & 0 deletions src/blur_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,11 @@ void BBDX::BlurCache::preparePaintData(const KWin::RenderView *view,
m_paintData.scaledBackgroundRect = scaledBackgroundRect;
m_paintData.glBeginConditionalRenderCalled = false;

// BBDX: nothing inside backgroundRect was repainted this frame but we have a
// cache entry - there are no fresh pixels to compare against or blur,
// so BlurEffect::blur() only draws the cached texture (useCachedOnly()).
m_paintData.useCachedOnly = cache.get() && dirtyRegion->isEmpty();

// the cache entry needs to stay in sync
// so BlurCacheEntry::localDirtyRegion() returns
// correct info
Expand Down Expand Up @@ -304,6 +309,15 @@ void BBDX::BlurCache::prepareCache(BBDX::BlurCacheLRU &cache) {
return;
}

// BBDX: a flush frame can still have an empty dirtyRegion when KWin paints
// the window with damage that doesn't intersect backgroundRect (e.g.
// shadow-only damage on focus change). There is no blitted data to
// compare against - skip the compare and conditional render setup,
// BlurEffect::blur() only draws the cached texture in this case.
if (useCachedOnly()) {
return;
}

auto textureCompareWindowData = cache.textureCompareWindowData();
if (!textureCompareWindowData) [[unlikely]] {
// GL resource alloc failed
Expand Down
18 changes: 18 additions & 0 deletions src/blur_cache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ class BlurCache {
// on first paint before we have a usuable cache entry
// we won't call glBeginConditionalRender
bool glBeginConditionalRenderCalled{false};

// nothing inside backgroundRect is repainted this paint
// but we have a valid cache entry - there is no fresh data
// to compare against or blur so just draw the cached texture
bool useCachedOnly{false};
} m_paintData;

public:
Expand All @@ -208,6 +213,19 @@ class BlurCache {
const KWin::Rect *scaledBackgroundRect,
BlurCacheLRU &cache);

/**
* Whether this paint should skip the blit and blur passes
* and only draw the existing cached texture.
*
* True when the dirtyRegion doesn't intersect backgroundRect
* while a valid cache entry exists. Without fresh pixels there
* is nothing to compare against or blur - rebuilding anyway would
* recreate the cache from stale data.
*
* Only valid after preparePaintData() was called.
*/
bool useCachedOnly() const { return m_paintData.useCachedOnly; }

/**
* Injects the geometry used for the cache, in logical pixels
* but scaled to what would be drawn on the device.
Expand Down