2024-12-22 17:12:09 +01:00
# include "Pass.hpp"
# include "../OpenGL.hpp"
# include <algorithm>
# include <ranges>
# include "../../config/ConfigValue.hpp"
bool CRenderPass : : empty ( ) const {
return false ;
}
bool CRenderPass : : single ( ) const {
return m_vPassElements . size ( ) = = 1 ;
}
bool CRenderPass : : needsIntrospection ( ) const {
return true ;
}
void CRenderPass : : add ( SP < IPassElement > el ) {
m_vPassElements . emplace_back ( makeShared < SPassElementData > ( CRegion { } , el ) ) ;
}
void CRenderPass : : simplify ( ) {
2024-12-22 17:20:33 +00:00
static auto PDEBUGPASS = CConfigValue < Hyprlang : : INT > ( " debug:pass " ) ;
2024-12-22 17:12:09 +01:00
// 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
const auto WILLBLUR = std : : ranges : : any_of ( m_vPassElements , [ ] ( const auto & el ) { return el - > element - > needsLiveBlur ( ) ; } ) ;
CRegion newDamage = damage . copy ( ) . intersect ( CBox { { } , g_pHyprOpenGL - > m_RenderData . pMonitor - > vecTransformedSize } ) ;
for ( auto & el : m_vPassElements | std : : views : : reverse ) {
if ( newDamage . empty ( ) ) {
el - > discard = true ;
continue ;
}
el - > elementDamage = newDamage ;
auto bb1 = el - > element - > boundingBox ( ) ;
if ( ! bb1 )
continue ;
auto bb = bb1 - > scale ( g_pHyprOpenGL - > m_RenderData . pMonitor - > scale ) ;
// drop if empty
if ( CRegion copy = newDamage . copy ( ) ; copy . intersect ( bb ) . empty ( ) ) {
el - > discard = true ;
continue ;
}
auto opaque = el - > element - > opaqueRegion ( ) ;
if ( ! opaque . empty ( ) ) {
opaque . scale ( g_pHyprOpenGL - > m_RenderData . pMonitor - > scale ) ;
// if this intersects the liveBlur region, allow live blur to operate correctly.
// do not occlude a border near it.
if ( WILLBLUR ) {
CRegion liveBlurRegion ;
for ( auto & el2 : m_vPassElements ) {
// if we reach self, no problem, we can break.
// if the blur is above us, we don't care, it will work fine.
if ( el2 = = el )
break ;
if ( ! el2 - > element - > needsLiveBlur ( ) )
continue ;
const auto BB = el2 - > element - > boundingBox ( ) ;
RASSERT ( BB , " No bounding box for an element with live blur is illegal " ) ;
liveBlurRegion . add ( * BB ) ;
}
2024-12-25 00:24:57 +01:00
// expand the region: this area needs to be proper to blur it right.
2024-12-27 17:49:05 +01:00
liveBlurRegion . scale ( g_pHyprOpenGL - > m_RenderData . pMonitor - > scale ) . expand ( oneBlurRadius ( ) * 2.F ) ;
2024-12-25 00:24:57 +01:00
2024-12-22 17:12:09 +01:00
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?
2024-12-25 00:24:57 +01:00
opaque . subtract ( infringement ) ;
2024-12-22 17:12:09 +01:00
}
}
newDamage . subtract ( opaque ) ;
2024-12-22 17:20:33 +00:00
if ( * PDEBUGPASS )
occludedRegion . add ( opaque ) ;
}
}
if ( * PDEBUGPASS ) {
for ( auto & el2 : m_vPassElements ) {
if ( ! el2 - > element - > needsLiveBlur ( ) )
continue ;
const auto BB = el2 - > element - > boundingBox ( ) ;
RASSERT ( BB , " No bounding box for an element with live blur is illegal " ) ;
2024-12-27 17:49:05 +01:00
totalLiveBlurRegion . add ( BB - > copy ( ) . scale ( g_pHyprOpenGL - > m_RenderData . pMonitor - > scale ) ) ;
2024-12-22 17:12:09 +01:00
}
}
}
void CRenderPass : : clear ( ) {
m_vPassElements . clear ( ) ;
}
CRegion CRenderPass : : render ( const CRegion & damage_ ) {
2024-12-22 17:20:33 +00:00
static auto PDEBUGPASS = CConfigValue < Hyprlang : : INT > ( " debug:pass " ) ;
2024-12-22 17:12:09 +01:00
2024-12-22 17:20:33 +00:00
const auto WILLBLUR = std : : ranges : : any_of ( m_vPassElements , [ ] ( const auto & el ) { return el - > element - > needsLiveBlur ( ) ; } ) ;
damage = * PDEBUGPASS ? CRegion { CBox { { } , { INT32_MAX , INT32_MAX } } } : damage_ . copy ( ) ;
occludedRegion = CRegion { } ;
totalLiveBlurRegion = CRegion { } ;
2024-12-22 17:12:09 +01:00
if ( damage . empty ( ) ) {
g_pHyprOpenGL - > m_RenderData . damage = damage ;
g_pHyprOpenGL - > m_RenderData . finalDamage = damage ;
return damage ;
}
2024-12-25 00:24:57 +01:00
if ( WILLBLUR & & ! * PDEBUGPASS ) {
2024-12-22 17:12:09 +01:00
// 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 ) ;
}
2024-12-29 12:56:58 +01:00
blurRegion . scale ( g_pHyprOpenGL - > m_RenderData . pMonitor - > scale ) ;
2024-12-22 17:12:09 +01:00
blurRegion . intersect ( damage ) . expand ( oneBlurRadius ( ) ) ;
g_pHyprOpenGL - > m_RenderData . finalDamage = blurRegion . copy ( ) . add ( damage ) ;
// FIXME: why does this break on * 1.F ?
// used to work when we expand all the damage... I think? Well, before pass.
// moving a window over blur shows the edges being wonk.
blurRegion . expand ( oneBlurRadius ( ) * 1.5F ) ;
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 ;
}
} else
simplify ( ) ;
g_pHyprOpenGL - > m_RenderData . pCurrentMonData - > blurFBShouldRender = std : : ranges : : any_of ( m_vPassElements , [ ] ( const auto & el ) { return el - > element - > needsPrecomputeBlur ( ) ; } ) ;
if ( m_vPassElements . empty ( ) )
return { } ;
for ( auto & el : m_vPassElements ) {
if ( el - > discard ) {
el - > element - > discard ( ) ;
continue ;
}
g_pHyprOpenGL - > m_RenderData . damage = el - > elementDamage ;
el - > element - > draw ( el - > elementDamage ) ;
}
2024-12-22 17:20:33 +00:00
if ( * PDEBUGPASS ) {
CBox monbox = { { } , g_pHyprOpenGL - > m_RenderData . pMonitor - > vecTransformedSize } ;
g_pHyprOpenGL - > renderRectWithDamage ( & monbox , CHyprColor { 1.F , 0.1F , 0.1F , 0.5F } , occludedRegion ) ;
g_pHyprOpenGL - > renderRectWithDamage ( & monbox , CHyprColor { 0.1F , 1.F , 0.1F , 0.5F } , totalLiveBlurRegion ) ;
}
2024-12-22 17:12:09 +01:00
g_pHyprOpenGL - > m_RenderData . damage = damage ;
return damage ;
}
float CRenderPass : : oneBlurRadius ( ) {
// TODO: is this exact range correct?
static auto PBLURSIZE = CConfigValue < Hyprlang : : INT > ( " decoration:blur:size " ) ;
static auto PBLURPASSES = CConfigValue < Hyprlang : : INT > ( " 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.
}