diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 9026e2938af..a52e05deab8 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1251,30 +1251,10 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) { } // if we have no tracking or full tracking, invalidate the entire monitor - if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || pMonitor->forceFullFrames > 0 || damageBlinkCleanup > 0) { - damage = {0, 0, (int)pMonitor->vecTransformedSize.x * 10, (int)pMonitor->vecTransformedSize.y * 10}; - finalDamage = damage; - } else { - static auto PBLURENABLED = CConfigValue("decoration:blur:enabled"); - - // if we use blur we need to expand the damage for proper blurring - // if framebuffer was not offloaded we're not doing introspection aka not blurring so this is redundant and dumb - if (*PBLURENABLED == 1 && g_pHyprOpenGL->m_bOffloadedFramebuffer) { - // TODO: can this be optimized? - static auto PBLURSIZE = CConfigValue("decoration:blur:size"); - static auto PBLURPASSES = CConfigValue("decoration:blur:passes"); - const auto BLURRADIUS = - *PBLURPASSES > 10 ? pow(2, 15) : std::clamp(*PBLURSIZE, (int64_t)1, (int64_t)40) * pow(2, *PBLURPASSES); // is this 2^pass? I don't know but it works... I think. + if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || pMonitor->forceFullFrames > 0 || damageBlinkCleanup > 0) + damage = {0, 0, (int)pMonitor->vecTransformedSize.x * 10, (int)pMonitor->vecTransformedSize.y * 10}; - // now, prep the damage, get the extended damage region - damage.expand(BLURRADIUS); // expand for proper blurring - - finalDamage = damage; - - damage.expand(BLURRADIUS); // expand for proper blurring - } else - finalDamage = damage; - } + finalDamage = damage; // update damage in renderdata as we modified it g_pHyprOpenGL->setDamage(damage, finalDamage); @@ -1346,6 +1326,8 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) { endRender(); + finalDamage = g_pHyprOpenGL->m_RenderData.damage; + TRACY_GPU_COLLECT; if (!pMonitor->mirrors.empty()) { @@ -2560,7 +2542,7 @@ void CHyprRenderer::endRender() { PMONITOR->commitSeq++; - m_sRenderPass.render(g_pHyprOpenGL->m_RenderData.damage); + g_pHyprOpenGL->m_RenderData.damage = m_sRenderPass.render(g_pHyprOpenGL->m_RenderData.damage); auto cleanup = CScopeGuard([this]() { if (m_pCurrentRenderbuffer) diff --git a/src/render/pass/Pass.cpp b/src/render/pass/Pass.cpp index ada6ed0f67a..1cfc17f7d7b 100644 --- a/src/render/pass/Pass.cpp +++ b/src/render/pass/Pass.cpp @@ -2,6 +2,7 @@ #include "../OpenGL.hpp" #include #include +#include "../../config/ConfigValue.hpp" bool CRenderPass::empty() const { return false; @@ -20,6 +21,22 @@ void CRenderPass::add(SP el) { } void CRenderPass::simplify() { + // TODO: use precompute blur for instances where there is nothing in between + + // if there is live blur, we need to NOT occlude any area where it will be influenced + // TODO: do this better. This should be layered, cuz we don't need to check infringement + // if the blur is ABOVE our thing. + CRegion liveBlurRegion; + for (auto& el : m_vPassElements) { + if (!el->element->needsLiveBlur()) + continue; + + const auto BB = el->element->boundingBox(); + RASSERT(BB, "No bounding box for an element with live blur is illegal"); + + liveBlurRegion.add(*BB); + } + std::vector> toRemove; CRegion newDamage = damage.copy().intersect(CBox{{}, g_pHyprOpenGL->m_RenderData.pMonitor->vecSize}); for (auto& el : m_vPassElements | std::views::reverse) { @@ -40,10 +57,18 @@ void CRenderPass::simplify() { continue; } - const auto opaque = el->element->opaqueRegion(); + auto opaque = el->element->opaqueRegion(); - if (!opaque.empty()) + if (!opaque.empty()) { + // if this intersects the liveBlur region, allow live blur to operate correctly. + // do not occlude a border near it. + if (auto infringement = opaque.copy().intersect(liveBlurRegion); !infringement.empty()) { + // eh, this is not the correct solution, but it will do... + // TODO: is this *easily* fixable? + opaque.subtract(infringement); + } newDamage.subtract(opaque); + } } std::erase_if(m_vPassElements, [&toRemove](const auto& el) { return std::find(toRemove.begin(), toRemove.end(), el) != toRemove.end(); }); @@ -53,9 +78,36 @@ void CRenderPass::clear() { m_vPassElements.clear(); } -void CRenderPass::render(const CRegion& damage_) { +CRegion CRenderPass::render(const CRegion& damage_) { + const auto WILLBLUR = std::ranges::any_of(m_vPassElements, [](const auto& el) { return el->element->needsLiveBlur(); }); + damage = damage_.copy(); + if (WILLBLUR) { + // combine blur regions into one that will be expanded + CRegion blurRegion; + for (auto& el : m_vPassElements) { + if (!el->element->needsLiveBlur()) + continue; + + const auto BB = el->element->boundingBox(); + RASSERT(BB, "No bounding box for an element with live blur is illegal"); + + blurRegion.add(*BB); + } + + blurRegion.intersect(damage); + + blurRegion = expandRegionForBlur(blurRegion); + + g_pHyprOpenGL->m_RenderData.finalDamage = blurRegion.copy().add(damage); + + blurRegion = expandRegionForBlur(blurRegion); + + damage = blurRegion.copy().add(damage); + } else + g_pHyprOpenGL->m_RenderData.finalDamage = damage; + if (std::ranges::any_of(m_vPassElements, [](const auto& el) { return el->element->disableSimplification(); })) { for (auto& el : m_vPassElements) { el->elementDamage = damage; @@ -66,7 +118,7 @@ void CRenderPass::render(const CRegion& damage_) { g_pHyprOpenGL->m_RenderData.pCurrentMonData->blurFBShouldRender = std::ranges::any_of(m_vPassElements, [](const auto& el) { return el->element->needsPrecomputeBlur(); }); if (m_vPassElements.empty()) - return; + return {}; for (auto& el : m_vPassElements) { g_pHyprOpenGL->m_RenderData.damage = el->elementDamage; @@ -74,4 +126,20 @@ void CRenderPass::render(const CRegion& damage_) { } g_pHyprOpenGL->m_RenderData.damage = damage; + return damage; +} + +float CRenderPass::oneBlurRadius() { + // TODO: is this exact range correct? + static auto PBLURSIZE = CConfigValue("decoration:blur:size"); + static auto PBLURPASSES = CConfigValue("decoration:blur:passes"); + return *PBLURPASSES > 10 ? pow(2, 15) : std::clamp(*PBLURSIZE, (int64_t)1, (int64_t)40) * pow(2, *PBLURPASSES); // is this 2^pass? I don't know but it works... I think. +} + +// If we use live blur we need to expand the damage for proper blurring. +// This function expands only ONCE, while we need to expand twice. +// Once for final damage +// Once more for rendering +CRegion CRenderPass::expandRegionForBlur(const CRegion& rg) { + return rg.copy().expand(oneBlurRadius()); } diff --git a/src/render/pass/Pass.hpp b/src/render/pass/Pass.hpp index 7c64cca6dea..c5bd6c4fb31 100644 --- a/src/render/pass/Pass.hpp +++ b/src/render/pass/Pass.hpp @@ -7,14 +7,14 @@ class CGradientValueData; class CRenderPass { public: - bool empty() const; - bool single() const; - bool needsIntrospection() const; + bool empty() const; + bool single() const; + bool needsIntrospection() const; - void add(SP elem); - void clear(); + void add(SP elem); + void clear(); - void render(const CRegion& damage_); + CRegion render(const CRegion& damage_); private: CRegion damage; @@ -29,6 +29,8 @@ class CRenderPass { SP currentPassInfo = nullptr; void simplify(); + CRegion expandRegionForBlur(const CRegion& rg); + float oneBlurRadius(); friend class CHyprOpenGLImpl; }; diff --git a/src/render/pass/RectPassElement.cpp b/src/render/pass/RectPassElement.cpp index c85347cee45..73727e5858e 100644 --- a/src/render/pass/RectPassElement.cpp +++ b/src/render/pass/RectPassElement.cpp @@ -6,18 +6,18 @@ CRectPassElement::CRectPassElement(const CRectPassElement::SRectData& data_) : d } void CRectPassElement::draw(const CRegion& damage) { - if (data.color.a == 1.F) + if (data.color.a == 1.F || !data.blur) g_pHyprOpenGL->renderRectWithDamage(&data.box, data.color, damage, data.round); else g_pHyprOpenGL->renderRectWithBlur(&data.box, data.color, data.round, data.blurA, data.xray); } bool CRectPassElement::needsLiveBlur() { - return data.color.a < 1.F && !data.xray; + return data.color.a < 1.F && !data.xray && data.blur; } bool CRectPassElement::needsPrecomputeBlur() { - return data.color.a < 1.F && data.xray; + return data.color.a < 1.F && data.xray && data.blur; } std::optional CRectPassElement::boundingBox() {