2022-03-17 20:22:29 +01:00
# include "Renderer.hpp"
# include "../Compositor.hpp"
2024-06-19 16:20:06 +02:00
# include "../helpers/math/Math.hpp"
2024-08-06 14:52:19 +01:00
# include "../helpers/sync/SyncReleaser.hpp"
2023-11-12 14:14:05 +01:00
# include <algorithm>
2024-07-21 13:09:54 +02:00
# include <aquamarine/output/Output.hpp>
2024-08-06 14:52:19 +01:00
# include <filesystem>
2024-03-03 18:39:20 +00:00
# include "../config/ConfigValue.hpp"
2024-03-09 16:52:59 +00:00
# include "../managers/CursorManager.hpp"
2024-05-05 22:18:10 +01:00
# include "../managers/PointerManager.hpp"
2025-01-17 15:21:35 +00:00
# include "../managers/input/InputManager.hpp"
# include "../managers/HookSystemManager.hpp"
# include "../managers/AnimationManager.hpp"
# include "../managers/LayoutManager.hpp"
2024-03-20 01:44:51 +00:00
# include "../desktop/Window.hpp"
2024-04-30 02:41:27 +01:00
# include "../desktop/LayerSurface.hpp"
2024-04-30 16:32:05 +01:00
# include "../protocols/SessionLock.hpp"
2024-05-09 21:47:21 +01:00
# include "../protocols/LayerShell.hpp"
2024-05-10 23:28:33 +01:00
# include "../protocols/XDGShell.hpp"
2024-05-10 02:27:54 +01:00
# include "../protocols/PresentationTime.hpp"
2024-05-11 17:13:20 +01:00
# include "../protocols/core/DataDevice.hpp"
2024-06-08 10:07:59 +02:00
# include "../protocols/core/Compositor.hpp"
2024-07-21 13:09:54 +02:00
# include "../protocols/DRMSyncobj.hpp"
# include "../protocols/LinuxDMABUF.hpp"
# include "../helpers/sync/SyncTimeline.hpp"
2025-01-17 15:21:35 +00:00
# include "../hyprerror/HyprError.hpp"
# include "../debug/HyprDebugOverlay.hpp"
# include "../debug/HyprNotificationOverlay.hpp"
2024-12-22 17:12:09 +01:00
# include "pass/TexPassElement.hpp"
# include "pass/ClearPassElement.hpp"
# include "pass/RectPassElement.hpp"
2025-01-01 14:11:21 +01:00
# include "pass/RendererHintsPassElement.hpp"
2024-12-22 17:12:09 +01:00
# include "pass/SurfacePassElement.hpp"
2024-07-21 13:09:54 +02:00
# include "debug/Log.hpp"
2025-03-14 02:15:18 +03:00
# include "../protocols/ColorManagement.hpp"
# include "../protocols/types/ContentType.hpp"
2022-03-17 20:22:29 +01:00
2024-09-21 00:33:48 +01:00
# include <hyprutils/utils/ScopeGuard.hpp>
using namespace Hyprutils : : Utils ;
2025-01-30 12:30:12 +01:00
using namespace Hyprutils : : OS ;
2025-02-02 22:25:29 +03:00
using enum NContentType : : eContentType ;
2025-02-26 17:56:37 +03:00
using namespace NColorManagement ;
2024-09-21 00:33:48 +01:00
2023-11-25 17:45:08 +00:00
extern " C " {
# include <xf86drm.h>
}
2024-03-28 02:04:30 +00:00
static int cursorTicker ( void * data ) {
g_pHyprRenderer - > ensureCursorRenderingMode ( ) ;
wl_event_source_timer_update ( g_pHyprRenderer - > m_pCursorTicker , 500 ) ;
return 0 ;
}
2023-11-25 17:45:08 +00:00
2024-03-28 02:04:30 +00:00
CHyprRenderer : : CHyprRenderer ( ) {
2024-07-21 13:09:54 +02:00
if ( g_pCompositor - > m_pAqBackend - > hasSession ( ) ) {
2024-08-26 20:24:30 +02:00
for ( auto const & dev : g_pCompositor - > m_pAqBackend - > session - > sessionDevices ) {
2024-07-21 13:09:54 +02:00
const auto DRMV = drmGetVersion ( dev - > fd ) ;
if ( ! DRMV )
continue ;
2023-11-25 17:45:08 +00:00
std : : string name = std : : string { DRMV - > name , DRMV - > name_len } ;
std : : transform ( name . begin ( ) , name . end ( ) , name . begin ( ) , tolower ) ;
if ( name . contains ( " nvidia " ) )
m_bNvidia = true ;
Debug : : log ( LOG , " DRM driver information: {} v{}.{}.{} from {} description {} " , name , DRMV - > version_major , DRMV - > version_minor , DRMV - > version_patchlevel ,
2023-11-25 17:56:38 +00:00
std : : string { DRMV - > date , DRMV - > date_len } , std : : string { DRMV - > desc , DRMV - > desc_len } ) ;
2023-11-25 17:45:08 +00:00
drmFreeVersion ( DRMV ) ;
}
} else {
2024-07-21 13:09:54 +02:00
Debug : : log ( LOG , " Aq backend has no session, omitting full DRM node checks " ) ;
2023-11-25 17:45:08 +00:00
2024-03-15 07:28:14 -07:00
const auto DRMV = drmGetVersion ( g_pCompositor - > m_iDRMFD ) ;
2023-11-25 17:45:08 +00:00
2024-03-15 07:28:14 -07:00
if ( DRMV ) {
std : : string name = std : : string { DRMV - > name , DRMV - > name_len } ;
std : : transform ( name . begin ( ) , name . end ( ) , name . begin ( ) , tolower ) ;
2023-11-25 17:45:08 +00:00
2024-03-15 07:28:14 -07:00
if ( name . contains ( " nvidia " ) )
m_bNvidia = true ;
2023-11-25 17:45:08 +00:00
2024-03-15 07:28:14 -07:00
Debug : : log ( LOG , " Primary DRM driver information: {} v{}.{}.{} from {} description {} " , name , DRMV - > version_major , DRMV - > version_minor , DRMV - > version_patchlevel ,
std : : string { DRMV - > date , DRMV - > date_len } , std : : string { DRMV - > desc , DRMV - > desc_len } ) ;
} else {
Debug : : log ( LOG , " No primary DRM driver information found " ) ;
}
2023-11-25 17:45:08 +00:00
drmFreeVersion ( DRMV ) ;
}
if ( m_bNvidia )
Debug : : log ( WARN , " NVIDIA detected, please remember to follow nvidia instructions on the wiki " ) ;
2024-03-28 02:04:30 +00:00
// cursor hiding stuff
2024-04-20 20:16:42 +01:00
static auto P = g_pHookSystem - > hookDynamic ( " keyPress " , [ & ] ( void * self , SCallbackInfo & info , std : : any param ) {
2024-03-28 02:04:30 +00:00
if ( m_sCursorHiddenConditions . hiddenOnKeyboard )
return ;
m_sCursorHiddenConditions . hiddenOnKeyboard = true ;
ensureCursorRenderingMode ( ) ;
} ) ;
2024-04-20 20:16:42 +01:00
static auto P2 = g_pHookSystem - > hookDynamic ( " mouseMove " , [ & ] ( void * self , SCallbackInfo & info , std : : any param ) {
2024-03-28 02:04:30 +00:00
if ( ! m_sCursorHiddenConditions . hiddenOnKeyboard & & m_sCursorHiddenConditions . hiddenOnTouch = = g_pInputManager - > m_bLastInputTouch & &
! m_sCursorHiddenConditions . hiddenOnTimeout )
return ;
m_sCursorHiddenConditions . hiddenOnKeyboard = false ;
m_sCursorHiddenConditions . hiddenOnTimeout = false ;
m_sCursorHiddenConditions . hiddenOnTouch = g_pInputManager - > m_bLastInputTouch ;
ensureCursorRenderingMode ( ) ;
} ) ;
2024-10-08 21:20:25 +01:00
static auto P3 = g_pHookSystem - > hookDynamic ( " focusedMon " , [ & ] ( void * self , SCallbackInfo & info , std : : any param ) {
g_pEventLoopManager - > doLater ( [ this ] ( ) {
if ( ! g_pHyprError - > active ( ) )
return ;
for ( auto & m : g_pCompositor - > m_vMonitors ) {
arrangeLayersForMonitor ( m - > ID ) ;
}
} ) ;
} ) ;
2024-03-28 02:04:30 +00:00
m_pCursorTicker = wl_event_loop_add_timer ( g_pCompositor - > m_sWLEventLoop , cursorTicker , nullptr ) ;
wl_event_source_timer_update ( m_pCursorTicker , 500 ) ;
2024-08-30 17:37:52 +02:00
m_tRenderUnfocusedTimer = makeShared < CEventLoopTimer > (
std : : nullopt ,
[ this ] ( SP < CEventLoopTimer > self , void * data ) {
static auto PFPS = CConfigValue < Hyprlang : : INT > ( " misc:render_unfocused_fps " ) ;
if ( m_vRenderUnfocused . empty ( ) )
return ;
timespec now ;
clock_gettime ( CLOCK_MONOTONIC , & now ) ;
bool dirty = false ;
for ( auto & w : m_vRenderUnfocused ) {
if ( ! w ) {
dirty = true ;
continue ;
}
if ( ! w - > m_pWLSurface | | ! w - > m_pWLSurface - > resource ( ) | | shouldRenderWindow ( w . lock ( ) ) )
continue ;
w - > m_pWLSurface - > resource ( ) - > frame ( & now ) ;
auto FEEDBACK = makeShared < CQueuedPresentationData > ( w - > m_pWLSurface - > resource ( ) ) ;
FEEDBACK - > attachMonitor ( g_pCompositor - > m_pLastMonitor . lock ( ) ) ;
FEEDBACK - > discarded ( ) ;
PROTO : : presentation - > queueData ( FEEDBACK ) ;
}
if ( dirty )
std : : erase_if ( m_vRenderUnfocused , [ ] ( const auto & e ) { return ! e | | ! e - > m_sWindowData . renderUnfocused . valueOr ( false ) ; } ) ;
if ( ! m_vRenderUnfocused . empty ( ) )
m_tRenderUnfocusedTimer - > updateTimeout ( std : : chrono : : milliseconds ( 1000 / * PFPS ) ) ;
} ,
nullptr ) ;
g_pEventLoopManager - > addTimer ( m_tRenderUnfocusedTimer ) ;
2023-09-28 21:48:33 +01:00
}
2024-06-13 12:08:02 +02:00
CHyprRenderer : : ~ CHyprRenderer ( ) {
if ( m_pCursorTicker )
wl_event_source_remove ( m_pCursorTicker ) ;
}
2024-10-19 23:03:29 +01:00
bool CHyprRenderer : : shouldRenderWindow ( PHLWINDOW pWindow , PHLMONITOR pMonitor ) {
2024-05-05 22:18:10 +01:00
if ( ! pWindow - > visibleOnMonitor ( pMonitor ) )
2022-03-20 18:49:40 +01:00
return false ;
2024-04-02 20:32:39 +01:00
if ( ! pWindow - > m_pWorkspace & & ! pWindow - > m_bFadingOut )
2023-04-12 13:05:57 +01:00
return false ;
2024-04-03 20:42:38 +01:00
if ( ! pWindow - > m_pWorkspace & & pWindow - > m_bFadingOut )
return pWindow - > workspaceID ( ) = = pMonitor - > activeWorkspaceID ( ) ;
2022-09-10 13:11:02 +02:00
if ( pWindow - > m_bPinned )
return true ;
2024-08-28 21:54:49 +02:00
// if the window is being moved to a workspace that is not invisible, and the alpha is > 0.F, render it.
2025-01-07 17:55:14 +00:00
if ( pWindow - > m_iMonitorMovedFrom ! = - 1 & & pWindow - > m_fMovingToWorkspaceAlpha - > isBeingAnimated ( ) & & pWindow - > m_fMovingToWorkspaceAlpha - > value ( ) > 0.F & & pWindow - > m_pWorkspace & &
2024-11-22 16:01:02 +00:00
! pWindow - > m_pWorkspace - > isVisible ( ) )
2024-08-28 21:54:49 +02:00
return true ;
2024-04-02 20:32:39 +01:00
const auto PWINDOWWORKSPACE = pWindow - > m_pWorkspace ;
2024-10-27 18:45:38 +00:00
if ( PWINDOWWORKSPACE & & PWINDOWWORKSPACE - > m_pMonitor = = pMonitor ) {
2025-01-07 17:55:14 +00:00
if ( PWINDOWWORKSPACE - > m_vRenderOffset - > isBeingAnimated ( ) | | PWINDOWWORKSPACE - > m_fAlpha - > isBeingAnimated ( ) | | PWINDOWWORKSPACE - > m_bForceRendering )
2022-08-18 12:42:21 +02:00
return true ;
2024-04-01 08:16:18 -07:00
// if hidden behind fullscreen
2024-07-31 17:55:52 +00:00
if ( PWINDOWWORKSPACE - > m_bHasFullscreenWindow & & ! pWindow - > isFullscreen ( ) & & ( ! pWindow - > m_bIsFloating | | ! pWindow - > m_bCreatedOverFullscreen ) & &
2025-01-07 17:55:14 +00:00
pWindow - > m_fAlpha - > value ( ) = = 0 )
2024-04-01 08:16:18 -07:00
return false ;
2025-01-07 17:55:14 +00:00
if ( ! PWINDOWWORKSPACE - > m_vRenderOffset - > isBeingAnimated ( ) & & ! PWINDOWWORKSPACE - > m_fAlpha - > isBeingAnimated ( ) & & ! PWINDOWWORKSPACE - > isVisible ( ) )
2024-04-01 08:16:18 -07:00
return false ;
2022-07-28 16:33:45 +02:00
}
2024-10-27 18:45:38 +00:00
if ( pWindow - > m_pMonitor = = pMonitor )
2022-03-20 18:49:40 +01:00
return true ;
2024-11-27 09:17:45 -05:00
if ( ( ! pWindow - > m_pWorkspace | | ! pWindow - > m_pWorkspace - > isVisible ( ) ) & & pWindow - > m_pMonitor ! = pMonitor )
2024-03-30 18:14:26 -07:00
return false ;
2022-08-18 12:41:10 +02:00
// if not, check if it maybe is active on a different monitor.
2024-11-22 16:01:02 +00:00
if ( pWindow - > m_pWorkspace & & pWindow - > m_pWorkspace - > isVisible ( ) & & pWindow - > m_bIsFloating /* tiled windows can't be multi-ws */ )
2024-07-31 17:55:52 +00:00
return ! pWindow - > isFullscreen ( ) ; // Do not draw fullscreen windows on other monitors
2022-03-20 18:49:40 +01:00
2024-04-02 20:32:39 +01:00
if ( pMonitor - > activeSpecialWorkspace = = pWindow - > m_pWorkspace )
2022-05-31 14:01:00 +02:00
return true ;
2024-03-30 18:14:26 -07:00
// if window is tiled and it's flying in, don't render on other mons (for slide)
2025-01-07 17:55:14 +00:00
if ( ! pWindow - > m_bIsFloating & & pWindow - > m_vRealPosition - > isBeingAnimated ( ) & & pWindow - > m_bAnimatingIn & & pWindow - > m_pMonitor ! = pMonitor )
2024-03-30 18:14:26 -07:00
return false ;
2025-01-07 17:55:14 +00:00
if ( pWindow - > m_vRealPosition - > isBeingAnimated ( ) ) {
if ( PWINDOWWORKSPACE & & ! PWINDOWWORKSPACE - > m_bIsSpecialWorkspace & & PWINDOWWORKSPACE - > m_vRenderOffset - > isBeingAnimated ( ) )
2024-03-23 15:14:50 -07:00
return false ;
2024-03-16 07:49:34 -07:00
// render window if window and monitor intersect
// (when moving out of or through a monitor)
2024-03-30 18:14:26 -07:00
CBox windowBox = pWindow - > getFullWindowBoundingBox ( ) ;
2025-01-07 17:55:14 +00:00
if ( PWINDOWWORKSPACE & & PWINDOWWORKSPACE - > m_vRenderOffset - > isBeingAnimated ( ) )
windowBox . translate ( PWINDOWWORKSPACE - > m_vRenderOffset - > value ( ) ) ;
2024-03-30 18:14:26 -07:00
windowBox . translate ( pWindow - > m_vFloatingOffset ) ;
2024-03-16 07:49:34 -07:00
const CBox monitorBox = { pMonitor - > vecPosition , pMonitor - > vecSize } ;
2024-11-07 00:30:17 +01:00
if ( ! windowBox . intersection ( monitorBox ) . empty ( ) & & ( pWindow - > workspaceID ( ) = = pMonitor - > activeWorkspaceID ( ) | | pWindow - > m_iMonitorMovedFrom ! = - 1 ) )
2024-03-16 07:49:34 -07:00
return true ;
}
2022-03-20 18:49:40 +01:00
return false ;
}
2024-04-27 12:43:12 +01:00
bool CHyprRenderer : : shouldRenderWindow ( PHLWINDOW pWindow ) {
2022-05-31 22:16:13 +02:00
2024-04-27 12:43:12 +01:00
if ( ! validMapped ( pWindow ) )
2022-05-31 22:16:13 +02:00
return false ;
2024-04-02 20:32:39 +01:00
const auto PWORKSPACE = pWindow - > m_pWorkspace ;
2022-05-31 22:16:13 +02:00
2024-04-02 20:32:39 +01:00
if ( ! pWindow - > m_pWorkspace )
2023-04-12 13:05:57 +01:00
return false ;
2022-09-12 15:33:20 +02:00
if ( pWindow - > m_bPinned | | PWORKSPACE - > m_bForceRendering )
2022-09-10 13:11:02 +02:00
return true ;
2024-11-22 16:01:02 +00:00
if ( PWORKSPACE & & PWORKSPACE - > isVisible ( ) )
2022-05-31 22:16:13 +02:00
return true ;
2024-08-26 17:25:39 +02:00
for ( auto const & m : g_pCompositor - > m_vMonitors ) {
2025-01-07 17:55:14 +00:00
if ( PWORKSPACE & & PWORKSPACE - > m_pMonitor = = m & & ( PWORKSPACE - > m_vRenderOffset - > isBeingAnimated ( ) | | PWORKSPACE - > m_fAlpha - > isBeingAnimated ( ) ) )
2022-05-31 22:16:13 +02:00
return true ;
2024-04-02 20:32:39 +01:00
if ( m - > activeSpecialWorkspace & & pWindow - > onSpecialWorkspace ( ) )
2022-05-31 22:16:13 +02:00
return true ;
}
return false ;
}
2024-10-19 23:03:29 +01:00
void CHyprRenderer : : renderWorkspaceWindowsFullscreen ( PHLMONITOR pMonitor , PHLWORKSPACE pWorkspace , timespec * time ) {
2024-04-27 12:43:12 +01:00
PHLWINDOW pWorkspaceWindow = nullptr ;
2022-03-30 20:16:23 +02:00
2023-04-17 23:45:03 +01:00
EMIT_HOOK_EVENT ( " render " , RENDER_PRE_WINDOWS ) ;
2022-09-19 17:26:11 +01:00
// loop over the tiled windows that are fading out
2024-08-26 20:24:30 +02:00
for ( auto const & w : g_pCompositor - > m_vWindows ) {
2024-04-27 12:43:12 +01:00
if ( ! shouldRenderWindow ( w , pMonitor ) )
2022-09-19 17:26:11 +01:00
continue ;
2025-01-07 17:55:14 +00:00
if ( w - > m_fAlpha - > value ( ) = = 0.f )
2022-09-19 17:26:11 +01:00
continue ;
2024-07-31 17:55:52 +00:00
if ( w - > isFullscreen ( ) | | w - > m_bIsFloating )
2022-09-19 17:26:11 +01:00
continue ;
2024-04-02 20:32:39 +01:00
if ( pWorkspace - > m_bIsSpecialWorkspace ! = w - > onSpecialWorkspace ( ) )
2023-11-24 21:18:50 +00:00
continue ;
2024-04-27 12:43:12 +01:00
renderWindow ( w , pMonitor , time , true , RENDER_PASS_ALL ) ;
2022-09-19 17:26:11 +01:00
}
// and floating ones too
2024-08-26 20:24:30 +02:00
for ( auto const & w : g_pCompositor - > m_vWindows ) {
2024-04-27 12:43:12 +01:00
if ( ! shouldRenderWindow ( w , pMonitor ) )
2022-09-19 17:26:11 +01:00
continue ;
2025-01-07 17:55:14 +00:00
if ( w - > m_fAlpha - > value ( ) = = 0.f )
2022-09-19 17:26:11 +01:00
continue ;
2024-07-31 17:55:52 +00:00
if ( w - > isFullscreen ( ) | | ! w - > m_bIsFloating )
2022-09-19 17:26:11 +01:00
continue ;
2024-10-27 18:45:38 +00:00
if ( w - > m_pMonitor = = pWorkspace - > m_pMonitor & & pWorkspace - > m_bIsSpecialWorkspace ! = w - > onSpecialWorkspace ( ) )
2023-11-24 21:18:50 +00:00
continue ;
2024-10-27 18:45:38 +00:00
if ( pWorkspace - > m_bIsSpecialWorkspace & & w - > m_pMonitor ! = pWorkspace - > m_pMonitor )
2023-11-24 21:18:50 +00:00
continue ; // special on another are rendered as a part of the base pass
2024-04-27 12:43:12 +01:00
renderWindow ( w , pMonitor , time , true , RENDER_PASS_ALL ) ;
2022-09-19 17:26:11 +01:00
}
2023-11-24 21:18:50 +00:00
// TODO: this pass sucks
2024-08-26 20:24:30 +02:00
for ( auto const & w : g_pCompositor - > m_vWindows ) {
2024-04-02 20:32:39 +01:00
const auto PWORKSPACE = w - > m_pWorkspace ;
2022-07-28 15:56:55 +02:00
2024-07-31 17:55:52 +00:00
if ( w - > m_pWorkspace ! = pWorkspace | | ! w - > isFullscreen ( ) ) {
2025-01-07 17:55:14 +00:00
if ( ! ( PWORKSPACE & & ( PWORKSPACE - > m_vRenderOffset - > isBeingAnimated ( ) | | PWORKSPACE - > m_fAlpha - > isBeingAnimated ( ) | | PWORKSPACE - > m_bForceRendering ) ) )
2022-07-28 15:56:55 +02:00
continue ;
2022-08-17 12:12:16 +02:00
2024-10-27 18:45:38 +00:00
if ( w - > m_pMonitor ! = pMonitor )
2022-08-17 12:12:16 +02:00
continue ;
2022-07-28 15:56:55 +02:00
}
2022-03-21 19:18:33 +01:00
2024-07-31 17:55:52 +00:00
if ( ! w - > isFullscreen ( ) )
2022-07-28 16:33:45 +02:00
continue ;
2024-10-27 18:45:38 +00:00
if ( w - > m_pMonitor = = pWorkspace - > m_pMonitor & & pWorkspace - > m_bIsSpecialWorkspace ! = w - > onSpecialWorkspace ( ) )
2024-03-04 15:29:45 -08:00
continue ;
2024-04-27 12:43:12 +01:00
if ( shouldRenderWindow ( w , pMonitor ) )
2024-07-31 17:55:52 +00:00
renderWindow ( w , pMonitor , time , pWorkspace - > m_efFullscreenMode ! = FSMODE_FULLSCREEN , RENDER_PASS_ALL ) ;
2022-03-30 20:16:23 +02:00
2024-04-02 20:32:39 +01:00
if ( w - > m_pWorkspace ! = pWorkspace )
2024-01-09 20:42:07 +01:00
continue ;
2024-04-27 12:43:12 +01:00
pWorkspaceWindow = w ;
2022-03-30 20:16:23 +02:00
}
2022-10-11 20:29:37 +01:00
if ( ! pWorkspaceWindow ) {
// ?? happens sometimes...
pWorkspace - > m_bHasFullscreenWindow = false ;
return ; // this will produce one blank frame. Oh well.
}
2022-09-19 17:26:11 +01:00
// then render windows over fullscreen.
2024-08-26 20:24:30 +02:00
for ( auto const & w : g_pCompositor - > m_vWindows ) {
2025-01-27 13:41:38 +00:00
if ( w - > m_pWorkspace ! = pWorkspaceWindow - > m_pWorkspace | | ! w - > m_bIsFloating | | ( ! w - > m_bCreatedOverFullscreen & & ! w - > m_bPinned ) | | ( ! w - > m_bIsMapped & & ! w - > m_bFadingOut ) | |
w - > isFullscreen ( ) )
2022-03-30 20:16:23 +02:00
continue ;
2024-10-27 18:45:38 +00:00
if ( w - > m_pMonitor = = pWorkspace - > m_pMonitor & & pWorkspace - > m_bIsSpecialWorkspace ! = w - > onSpecialWorkspace ( ) )
2023-11-24 21:18:50 +00:00
continue ;
2024-10-27 18:45:38 +00:00
if ( pWorkspace - > m_bIsSpecialWorkspace & & w - > m_pMonitor ! = pWorkspace - > m_pMonitor )
2023-11-24 21:18:50 +00:00
continue ; // special on another are rendered as a part of the base pass
2024-04-27 12:43:12 +01:00
renderWindow ( w , pMonitor , time , true , RENDER_PASS_ALL ) ;
2022-05-31 14:20:41 +02:00
}
2023-09-04 15:07:56 +02:00
}
2022-05-31 14:20:41 +02:00
2024-10-19 23:03:29 +01:00
void CHyprRenderer : : renderWorkspaceWindows ( PHLMONITOR pMonitor , PHLWORKSPACE pWorkspace , timespec * time ) {
2024-04-27 12:43:12 +01:00
PHLWINDOW lastWindow ;
2022-05-31 14:20:41 +02:00
2023-09-04 15:07:56 +02:00
EMIT_HOOK_EVENT ( " render " , RENDER_PRE_WINDOWS ) ;
2022-05-31 14:20:41 +02:00
2024-10-31 00:20:32 +01:00
std : : vector < PHLWINDOWREF > windows ;
windows . reserve ( g_pCompositor - > m_vWindows . size ( ) ) ;
2024-08-26 20:24:30 +02:00
for ( auto const & w : g_pCompositor - > m_vWindows ) {
2024-03-14 18:25:28 +00:00
if ( w - > isHidden ( ) | | ( ! w - > m_bIsMapped & & ! w - > m_bFadingOut ) )
2023-09-04 15:07:56 +02:00
continue ;
2022-05-31 14:20:41 +02:00
2024-04-27 12:43:12 +01:00
if ( ! shouldRenderWindow ( w , pMonitor ) )
2023-09-04 15:07:56 +02:00
continue ;
2024-12-22 17:12:09 +01:00
windows . emplace_back ( w ) ;
2024-10-31 00:20:32 +01:00
}
// Non-floating main
for ( auto & w : windows ) {
if ( w - > m_bIsFloating )
continue ; // floating are in the second pass
2025-02-06 17:45:25 +00:00
// some things may force us to ignore the special/not special disparity
const bool IGNORE_SPECIAL_CHECK = w - > m_iMonitorMovedFrom ! = - 1 & & ( w - > m_pWorkspace & & ! w - > m_pWorkspace - > isVisible ( ) ) ;
if ( ! IGNORE_SPECIAL_CHECK & & pWorkspace - > m_bIsSpecialWorkspace ! = w - > onSpecialWorkspace ( ) )
2023-11-22 20:05:50 +00:00
continue ;
2023-09-04 15:07:56 +02:00
// render active window after all others of this pass
2024-05-05 17:16:00 +01:00
if ( w = = g_pCompositor - > m_pLastWindow ) {
2024-10-31 00:20:32 +01:00
lastWindow = w . lock ( ) ;
2023-09-04 15:07:56 +02:00
continue ;
}
// render the bad boy
2024-10-31 00:20:32 +01:00
renderWindow ( w . lock ( ) , pMonitor , time , true , RENDER_PASS_MAIN ) ;
2025-02-06 17:45:25 +00:00
w . reset ( ) ;
2022-05-29 15:44:30 +02:00
}
2023-09-04 15:07:56 +02:00
if ( lastWindow )
renderWindow ( lastWindow , pMonitor , time , true , RENDER_PASS_MAIN ) ;
2025-02-06 17:45:25 +00:00
lastWindow . reset ( ) ;
2023-09-04 15:07:56 +02:00
// Non-floating popup
2024-10-31 00:20:32 +01:00
for ( auto & w : windows ) {
if ( ! w )
2023-09-04 15:07:56 +02:00
continue ;
if ( w - > m_bIsFloating )
continue ; // floating are in the second pass
2025-02-06 17:45:25 +00:00
// some things may force us to ignore the special/not special disparity
const bool IGNORE_SPECIAL_CHECK = w - > m_iMonitorMovedFrom ! = - 1 & & ( w - > m_pWorkspace & & ! w - > m_pWorkspace - > isVisible ( ) ) ;
if ( ! IGNORE_SPECIAL_CHECK & & pWorkspace - > m_bIsSpecialWorkspace ! = w - > onSpecialWorkspace ( ) )
2023-11-22 20:05:50 +00:00
continue ;
2023-09-04 15:07:56 +02:00
// render the bad boy
2024-10-31 00:20:32 +01:00
renderWindow ( w . lock ( ) , pMonitor , time , true , RENDER_PASS_POPUP ) ;
w . reset ( ) ;
2022-04-25 21:49:45 +02:00
}
2023-09-04 15:07:56 +02:00
// floating on top
2024-10-31 00:20:32 +01:00
for ( auto & w : windows ) {
if ( ! w )
2023-09-04 15:07:56 +02:00
continue ;
if ( ! w - > m_bIsFloating | | w - > m_bPinned )
continue ;
2025-02-06 17:45:25 +00:00
// some things may force us to ignore the special/not special disparity
const bool IGNORE_SPECIAL_CHECK = w - > m_iMonitorMovedFrom ! = - 1 & & ( w - > m_pWorkspace & & ! w - > m_pWorkspace - > isVisible ( ) ) ;
if ( ! IGNORE_SPECIAL_CHECK & & pWorkspace - > m_bIsSpecialWorkspace ! = w - > onSpecialWorkspace ( ) )
2023-11-18 19:53:45 +00:00
continue ;
2024-10-27 18:45:38 +00:00
if ( pWorkspace - > m_bIsSpecialWorkspace & & w - > m_pMonitor ! = pWorkspace - > m_pMonitor )
2023-11-24 21:18:50 +00:00
continue ; // special on another are rendered as a part of the base pass
2023-09-04 15:07:56 +02:00
// render the bad boy
2024-10-31 00:20:32 +01:00
renderWindow ( w . lock ( ) , pMonitor , time , true , RENDER_PASS_ALL ) ;
2023-09-02 20:32:10 +02:00
}
2022-03-21 19:18:33 +01:00
}
2025-01-12 09:10:36 -08:00
void CHyprRenderer : : renderWindow ( PHLWINDOW pWindow , PHLMONITOR pMonitor , timespec * time , bool decorate , eRenderPassMode mode , bool ignorePosition , bool standalone ) {
if ( pWindow - > isHidden ( ) & & ! standalone )
2022-04-12 16:44:18 +02:00
return ;
2022-04-05 20:49:15 +02:00
if ( pWindow - > m_bFadingOut ) {
2024-10-27 18:45:38 +00:00
if ( pMonitor = = pWindow - > m_pMonitor ) // TODO: fix this
2024-12-22 17:12:09 +01:00
renderSnapshot ( pWindow ) ;
2022-04-05 20:49:15 +02:00
return ;
}
2022-09-25 20:07:48 +02:00
2024-03-14 18:25:28 +00:00
if ( ! pWindow - > m_bIsMapped )
return ;
2023-07-20 17:47:49 +02:00
TRACY_GPU_ZONE ( " RenderWindow " ) ;
2024-12-22 17:12:09 +01:00
const auto PWORKSPACE = pWindow - > m_pWorkspace ;
2025-01-07 17:55:14 +00:00
const auto REALPOS = pWindow - > m_vRealPosition - > value ( ) + ( pWindow - > m_bPinned ? Vector2D { } : PWORKSPACE - > m_vRenderOffset - > value ( ) ) ;
2024-12-22 17:12:09 +01:00
static auto PDIMAROUND = CConfigValue < Hyprlang : : FLOAT > ( " decoration:dim_around " ) ;
static auto PBLUR = CConfigValue < Hyprlang : : INT > ( " decoration:blur:enabled " ) ;
2022-06-21 22:54:41 +02:00
2024-12-22 17:12:09 +01:00
CSurfacePassElement : : SRenderData renderdata = { pMonitor , time } ;
2025-01-07 17:55:14 +00:00
CBox textureBox = { REALPOS . x , REALPOS . y , std : : max ( pWindow - > m_vRealSize - > value ( ) . x , 5.0 ) , std : : max ( pWindow - > m_vRealSize - > value ( ) . y , 5.0 ) } ;
2023-11-04 17:03:05 +00:00
2024-12-22 17:12:09 +01:00
renderdata . pos . x = textureBox . x ;
renderdata . pos . y = textureBox . y ;
renderdata . w = textureBox . w ;
renderdata . h = textureBox . h ;
2023-11-04 17:03:05 +00:00
2022-11-06 17:52:09 +00:00
if ( ignorePosition ) {
2024-12-22 17:12:09 +01:00
renderdata . pos . x = pMonitor - > vecPosition . x ;
renderdata . pos . y = pMonitor - > vecPosition . y ;
2025-02-18 15:10:40 +00:00
} else {
const bool ANR = pWindow - > isNotResponding ( ) ;
if ( ANR & & pWindow - > m_notRespondingTint - > goal ( ) ! = 0.2F )
* pWindow - > m_notRespondingTint = 0.2F ;
else if ( ! ANR & & pWindow - > m_notRespondingTint - > goal ( ) ! = 0.F )
* pWindow - > m_notRespondingTint = 0.F ;
2022-11-06 17:52:09 +00:00
}
2025-01-12 09:10:36 -08:00
if ( standalone )
2022-12-05 17:05:15 +00:00
decorate = false ;
2024-08-28 21:54:49 +02:00
// whether to use m_fMovingToWorkspaceAlpha, only if fading out into an invisible ws
2024-11-22 16:01:02 +00:00
const bool USE_WORKSPACE_FADE_ALPHA = pWindow - > m_iMonitorMovedFrom ! = - 1 & & ( ! PWORKSPACE | | ! PWORKSPACE - > isVisible ( ) ) ;
2024-11-17 16:42:30 +00:00
const bool DONT_BLUR = pWindow - > m_sWindowData . noBlur . valueOrDefault ( ) | | pWindow - > m_sWindowData . RGBX . valueOrDefault ( ) | | pWindow - > opaque ( ) ;
2024-08-28 21:54:49 +02:00
2024-06-08 10:07:59 +02:00
renderdata . surface = pWindow - > m_pWLSurface - > resource ( ) ;
2024-07-31 17:55:52 +00:00
renderdata . dontRound = pWindow - > isEffectiveInternalFSMode ( FSMODE_FULLSCREEN ) | | pWindow - > m_sWindowData . noRounding . valueOrDefault ( ) ;
2025-01-07 17:55:14 +00:00
renderdata . fadeAlpha = pWindow - > m_fAlpha - > value ( ) * ( pWindow - > m_bPinned | | USE_WORKSPACE_FADE_ALPHA ? 1.f : PWORKSPACE - > m_fAlpha - > value ( ) ) *
( USE_WORKSPACE_FADE_ALPHA ? pWindow - > m_fMovingToWorkspaceAlpha - > value ( ) : 1.F ) * pWindow - > m_fMovingFromWorkspaceAlpha - > value ( ) ;
renderdata . alpha = pWindow - > m_fActiveInactiveAlpha - > value ( ) ;
2025-01-05 12:38:49 -06:00
renderdata . decorate = decorate & & ! pWindow - > m_bX11DoesntWantBorders & & ! pWindow - > isEffectiveInternalFSMode ( FSMODE_FULLSCREEN ) ;
2025-01-12 09:10:36 -08:00
renderdata . rounding = standalone | | renderdata . dontRound ? 0 : pWindow - > rounding ( ) * pMonitor - > scale ;
renderdata . roundingPower = standalone | | renderdata . dontRound ? 2.0f : pWindow - > roundingPower ( ) ;
renderdata . blur = ! standalone & & * PBLUR & & ! DONT_BLUR ;
2025-01-05 12:38:49 -06:00
renderdata . pWindow = pWindow ;
2022-03-21 19:18:33 +01:00
2025-01-12 09:10:36 -08:00
if ( standalone ) {
2022-12-16 17:17:31 +00:00
renderdata . alpha = 1.f ;
2023-01-05 19:25:45 +01:00
renderdata . fadeAlpha = 1.f ;
2022-12-05 18:00:57 +00:00
}
2022-07-28 12:07:41 +02:00
// apply opaque
2024-07-11 14:10:42 +00:00
if ( pWindow - > m_sWindowData . opaque . valueOrDefault ( ) )
2022-07-28 12:07:41 +02:00
renderdata . alpha = 1.f ;
2024-12-22 17:12:09 +01:00
renderdata . pWindow = pWindow ;
2022-03-21 19:18:33 +01:00
2023-05-26 13:07:45 +02:00
EMIT_HOOK_EVENT ( " render " , RENDER_PRE_WINDOW ) ;
2024-07-11 14:10:42 +00:00
if ( * PDIMAROUND & & pWindow - > m_sWindowData . dimAround . valueOrDefault ( ) & & ! m_bRenderingSnapshot & & mode ! = RENDER_PASS_POPUP ) {
2024-12-22 17:12:09 +01:00
CBox monbox = { 0 , 0 , g_pHyprOpenGL - > m_RenderData . pMonitor - > vecTransformedSize . x , g_pHyprOpenGL - > m_RenderData . pMonitor - > vecTransformedSize . y } ;
CRectPassElement : : SRectData data ;
data . color = CHyprColor ( 0 , 0 , 0 , * PDIMAROUND * renderdata . alpha * renderdata . fadeAlpha ) ;
data . box = monbox ;
m_sRenderPass . add ( makeShared < CRectPassElement > ( data ) ) ;
2022-12-28 15:39:17 +01:00
}
2024-12-22 17:12:09 +01:00
renderdata . pos . x + = pWindow - > m_vFloatingOffset . x ;
renderdata . pos . y + = pWindow - > m_vFloatingOffset . y ;
2022-12-16 17:17:31 +00:00
2023-11-16 20:20:41 +00:00
// if window is floating and we have a slide animation, clip it to its full bb
2025-01-07 17:55:14 +00:00
if ( ! ignorePosition & & pWindow - > m_bIsFloating & & ! pWindow - > isFullscreen ( ) & & PWORKSPACE - > m_vRenderOffset - > isBeingAnimated ( ) & & ! pWindow - > m_bPinned ) {
2024-03-30 18:14:26 -07:00
CRegion rg =
2025-01-07 17:55:14 +00:00
pWindow - > getFullWindowBoundingBox ( ) . translate ( - pMonitor - > vecPosition + PWORKSPACE - > m_vRenderOffset - > value ( ) + pWindow - > m_vFloatingOffset ) . scale ( pMonitor - > scale ) ;
2024-12-22 17:12:09 +01:00
renderdata . clipBox = rg . getExtents ( ) ;
2023-11-18 17:00:24 +00:00
}
2023-11-16 20:20:41 +00:00
2022-06-28 15:45:38 +02:00
// render window decorations first, if not fullscreen full
2022-07-12 09:49:56 +02:00
if ( mode = = RENDER_PASS_ALL | | mode = = RENDER_PASS_MAIN ) {
2023-10-21 14:15:48 +01:00
const bool TRANSFORMERSPRESENT = ! pWindow - > m_vTransformers . empty ( ) ;
2023-10-21 19:25:44 +01:00
if ( TRANSFORMERSPRESENT ) {
2023-10-21 14:15:48 +01:00
g_pHyprOpenGL - > bindOffMain ( ) ;
2024-08-26 20:24:30 +02:00
for ( auto const & t : pWindow - > m_vTransformers ) {
2023-10-21 19:25:44 +01:00
t - > preWindowRender ( & renderdata ) ;
}
}
2024-04-13 23:16:26 +00:00
if ( renderdata . decorate ) {
2024-08-26 20:24:30 +02:00
for ( auto const & wd : pWindow - > m_dWindowDecorations ) {
2023-11-04 13:10:52 +00:00
if ( wd - > getDecorationLayer ( ) ! = DECORATION_LAYER_BOTTOM )
continue ;
2024-03-30 18:14:26 -07:00
wd - > draw ( pMonitor , renderdata . alpha * renderdata . fadeAlpha ) ;
2023-11-04 13:10:52 +00:00
}
2024-08-26 20:24:30 +02:00
for ( auto const & wd : pWindow - > m_dWindowDecorations ) {
2023-11-04 13:10:52 +00:00
if ( wd - > getDecorationLayer ( ) ! = DECORATION_LAYER_UNDER )
continue ;
2024-03-30 18:14:26 -07:00
wd - > draw ( pMonitor , renderdata . alpha * renderdata . fadeAlpha ) ;
2023-11-04 13:10:52 +00:00
}
}
2022-06-22 15:45:56 +02:00
2024-03-03 18:39:20 +00:00
static auto PXWLUSENN = CConfigValue < Hyprlang : : INT > ( " xwayland:use_nearest_neighbor " ) ;
2024-07-11 14:10:42 +00:00
if ( ( pWindow - > m_bIsX11 & & * PXWLUSENN ) | | pWindow - > m_sWindowData . nearestNeighbor . valueOrDefault ( ) )
2024-12-22 17:12:09 +01:00
renderdata . useNearestNeighbor = true ;
2023-06-11 21:33:50 +02:00
2024-07-11 14:10:42 +00:00
if ( ! pWindow - > m_sWindowData . noBlur . valueOrDefault ( ) & & pWindow - > m_pWLSurface - > small ( ) & & ! pWindow - > m_pWLSurface - > m_bFillIgnoreSmall & & renderdata . blur & & * PBLUR ) {
2024-12-22 17:12:09 +01:00
CBox wb = { renderdata . pos . x - pMonitor - > vecPosition . x , renderdata . pos . y - pMonitor - > vecPosition . y , renderdata . w , renderdata . h } ;
2023-11-04 17:03:05 +00:00
wb . scale ( pMonitor - > scale ) . round ( ) ;
2024-12-22 17:12:09 +01:00
CRectPassElement : : SRectData data ;
data . color = CHyprColor ( 0 , 0 , 0 , 0 ) ;
data . box = wb ;
data . round = renderdata . dontRound ? 0 : renderdata . rounding - 1 ;
2024-12-30 20:00:34 +01:00
data . blur = true ;
2024-12-22 17:12:09 +01:00
data . blurA = renderdata . fadeAlpha ;
data . xray = g_pHyprOpenGL - > shouldUseNewBlurOptimizations ( nullptr , pWindow ) ;
m_sRenderPass . add ( makeShared < CRectPassElement > ( data ) ) ;
2023-10-25 22:20:58 +01:00
renderdata . blur = false ;
}
2024-06-17 12:42:32 +02:00
renderdata . surfaceCounter = 0 ;
2024-12-22 17:12:09 +01:00
pWindow - > m_pWLSurface - > resource ( ) - > breadthfirst (
[ this , & renderdata , & pWindow ] ( SP < CWLSurfaceResource > s , const Vector2D & offset , void * data ) {
renderdata . localPos = offset ;
renderdata . texture = s - > current . texture ;
renderdata . surface = s ;
renderdata . mainSurface = s = = pWindow - > m_pWLSurface - > resource ( ) ;
m_sRenderPass . add ( makeShared < CSurfacePassElement > ( renderdata ) ) ;
renderdata . surfaceCounter + + ;
} ,
nullptr ) ;
2022-07-12 09:49:56 +02:00
2024-12-22 22:07:08 +00:00
renderdata . useNearestNeighbor = false ;
2023-06-11 21:33:50 +02:00
2024-04-13 23:16:26 +00:00
if ( renderdata . decorate ) {
2024-08-26 20:24:30 +02:00
for ( auto const & wd : pWindow - > m_dWindowDecorations ) {
2023-12-11 16:32:00 +00:00
if ( wd - > getDecorationLayer ( ) ! = DECORATION_LAYER_OVER )
continue ;
2023-11-04 13:10:52 +00:00
2024-03-30 18:14:26 -07:00
wd - > draw ( pMonitor , renderdata . alpha * renderdata . fadeAlpha ) ;
2023-12-11 16:32:00 +00:00
}
2023-11-04 13:10:52 +00:00
}
2023-10-21 14:15:48 +01:00
if ( TRANSFORMERSPRESENT ) {
CFramebuffer * last = g_pHyprOpenGL - > m_RenderData . currentFB ;
2024-08-26 20:24:30 +02:00
for ( auto const & t : pWindow - > m_vTransformers ) {
2023-10-21 14:15:48 +01:00
last = t - > transform ( last ) ;
}
g_pHyprOpenGL - > bindBackOnMain ( ) ;
g_pHyprOpenGL - > renderOffToMain ( last ) ;
}
2022-06-22 15:45:56 +02:00
}
2023-11-16 20:20:41 +00:00
g_pHyprOpenGL - > m_RenderData . clipBox = CBox ( ) ;
2022-07-12 09:49:56 +02:00
if ( mode = = RENDER_PASS_ALL | | mode = = RENDER_PASS_POPUP ) {
if ( ! pWindow - > m_bIsX11 ) {
2024-05-10 23:28:33 +01:00
CBox geom = pWindow - > m_pXDGSurface - > current . geometry ;
2022-08-28 10:14:43 +02:00
2024-12-22 17:12:09 +01:00
renderdata . pos - = geom . pos ( ) ;
2022-12-16 17:17:31 +00:00
renderdata . dontRound = true ; // don't round popups
renderdata . pMonitor = pMonitor ;
2022-07-15 19:07:06 +02:00
renderdata . squishOversized = false ; // don't squish popups
2023-12-25 18:04:00 +01:00
renderdata . popup = true ;
2023-10-24 21:28:55 +01:00
2024-03-25 16:08:55 +00:00
static CConfigValue PBLURPOPUPS = CConfigValue < Hyprlang : : INT > ( " decoration:blur:popups " ) ;
static CConfigValue PBLURIGNOREA = CConfigValue < Hyprlang : : FLOAT > ( " decoration:blur:popups_ignorealpha " ) ;
2024-11-17 16:42:30 +00:00
renderdata . blur = * PBLURPOPUPS & & * PBLUR ;
2024-03-25 16:08:55 +00:00
if ( renderdata . blur ) {
2024-12-22 17:12:09 +01:00
renderdata . discardMode | = DISCARD_ALPHA ;
renderdata . discardOpacity = * PBLURIGNOREA ;
2024-03-25 16:08:55 +00:00
}
2024-07-11 14:10:42 +00:00
if ( pWindow - > m_sWindowData . nearestNeighbor . valueOrDefault ( ) )
2024-12-22 17:12:09 +01:00
renderdata . useNearestNeighbor = true ;
2023-10-24 21:28:55 +01:00
2024-06-17 12:42:32 +02:00
renderdata . surfaceCounter = 0 ;
2024-05-10 23:28:33 +01:00
pWindow - > m_pPopupHead - > breadthfirst (
2025-01-26 12:54:32 +00:00
[ this , & renderdata ] ( WP < CPopup > popup , void * data ) {
2024-11-17 16:16:49 +00:00
if ( ! popup - > m_pWLSurface | | ! popup - > m_pWLSurface - > resource ( ) | | ! popup - > m_bMapped )
2024-05-10 23:28:33 +01:00
return ;
2024-11-17 16:42:30 +00:00
const auto pos = popup - > coordsRelativeToParent ( ) ;
2024-12-22 17:12:09 +01:00
const Vector2D oldPos = renderdata . pos ;
renderdata . pos + = pos ;
popup - > m_pWLSurface - > resource ( ) - > breadthfirst (
[ this , & renderdata ] ( SP < CWLSurfaceResource > s , const Vector2D & offset , void * data ) {
renderdata . localPos = offset ;
renderdata . texture = s - > current . texture ;
renderdata . surface = s ;
renderdata . mainSurface = false ;
m_sRenderPass . add ( makeShared < CSurfacePassElement > ( renderdata ) ) ;
renderdata . surfaceCounter + + ;
} ,
data ) ;
renderdata . pos = oldPos ;
2024-05-10 23:28:33 +01:00
} ,
& renderdata ) ;
2022-07-12 09:49:56 +02:00
}
2023-11-04 13:10:52 +00:00
2023-12-11 16:32:00 +00:00
if ( decorate ) {
2024-08-26 20:24:30 +02:00
for ( auto const & wd : pWindow - > m_dWindowDecorations ) {
2023-12-11 16:32:00 +00:00
if ( wd - > getDecorationLayer ( ) ! = DECORATION_LAYER_OVERLAY )
continue ;
2023-11-04 13:10:52 +00:00
2024-03-30 18:14:26 -07:00
wd - > draw ( pMonitor , renderdata . alpha * renderdata . fadeAlpha ) ;
2023-12-11 16:32:00 +00:00
}
2023-11-04 13:10:52 +00:00
}
2022-05-17 13:16:37 +02:00
}
2024-12-22 22:07:08 +00:00
// for plugins
g_pHyprOpenGL - > m_RenderData . currentWindow = pWindow ;
2023-05-26 13:07:45 +02:00
EMIT_HOOK_EVENT ( " render " , RENDER_POST_WINDOW ) ;
2024-12-22 22:07:08 +00:00
g_pHyprOpenGL - > m_RenderData . currentWindow . reset ( ) ;
2022-03-21 19:18:33 +01:00
}
2024-10-19 23:03:29 +01:00
void CHyprRenderer : : renderLayer ( PHLLS pLayer , PHLMONITOR pMonitor , timespec * time , bool popups ) {
2024-08-07 13:31:27 +02:00
if ( ! pLayer )
return ;
2024-04-11 21:41:18 +10:00
static auto PDIMAROUND = CConfigValue < Hyprlang : : FLOAT > ( " decoration:dim_around " ) ;
if ( * PDIMAROUND & & pLayer - > dimAround & & ! m_bRenderingSnapshot & & ! popups ) {
2024-12-22 17:12:09 +01:00
CRectPassElement : : SRectData data ;
data . box = { 0 , 0 , g_pHyprOpenGL - > m_RenderData . pMonitor - > vecTransformedSize . x , g_pHyprOpenGL - > m_RenderData . pMonitor - > vecTransformedSize . y } ;
2025-01-07 17:55:14 +00:00
data . color = CHyprColor ( 0 , 0 , 0 , * PDIMAROUND * pLayer - > alpha - > value ( ) ) ;
2024-12-22 17:12:09 +01:00
m_sRenderPass . add ( makeShared < CRectPassElement > ( data ) ) ;
2024-04-11 21:41:18 +10:00
}
2022-05-14 17:23:46 +02:00
if ( pLayer - > fadingOut ) {
2024-04-04 22:43:57 +01:00
if ( ! popups )
2024-12-22 17:12:09 +01:00
renderSnapshot ( pLayer ) ;
2022-05-14 17:23:46 +02:00
return ;
}
2024-11-17 16:42:30 +00:00
static auto PBLUR = CConfigValue < Hyprlang : : INT > ( " decoration:blur:enabled " ) ;
2023-07-20 17:47:49 +02:00
TRACY_GPU_ZONE ( " RenderLayer " ) ;
2025-01-07 17:55:14 +00:00
const auto REALPOS = pLayer - > realPosition - > value ( ) ;
const auto REALSIZ = pLayer - > realSize - > value ( ) ;
2023-03-17 23:16:13 +00:00
2024-12-22 17:12:09 +01:00
CSurfacePassElement : : SRenderData renderdata = { pMonitor , time , REALPOS } ;
2025-01-07 17:55:14 +00:00
renderdata . fadeAlpha = pLayer - > alpha - > value ( ) ;
2024-12-22 17:12:09 +01:00
renderdata . blur = pLayer - > forceBlur & & * PBLUR ;
renderdata . surface = pLayer - > surface - > resource ( ) ;
renderdata . decorate = false ;
renderdata . w = REALSIZ . x ;
renderdata . h = REALSIZ . y ;
renderdata . pLS = pLayer ;
renderdata . blockBlurOptimization = pLayer - > layer = = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM | | pLayer - > layer = = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND ;
2024-02-28 15:00:34 +00:00
2024-12-22 17:12:09 +01:00
renderdata . clipBox = CBox { 0 , 0 , pMonitor - > vecSize . x , pMonitor - > vecSize . y } . scale ( pMonitor - > scale ) ;
2024-03-25 16:08:55 +00:00
if ( renderdata . blur & & pLayer - > ignoreAlpha ) {
2024-12-22 17:12:09 +01:00
renderdata . discardMode | = DISCARD_ALPHA ;
renderdata . discardOpacity = pLayer - > ignoreAlphaValue ;
2023-06-12 00:30:31 +07:00
}
2024-03-25 16:08:55 +00:00
2024-03-25 16:20:30 +00:00
if ( ! popups )
2024-12-22 17:12:09 +01:00
pLayer - > surface - > resource ( ) - > breadthfirst (
[ this , & renderdata , & pLayer ] ( SP < CWLSurfaceResource > s , const Vector2D & offset , void * data ) {
renderdata . localPos = offset ;
renderdata . texture = s - > current . texture ;
renderdata . surface = s ;
renderdata . mainSurface = s = = pLayer - > surface - > resource ( ) ;
m_sRenderPass . add ( makeShared < CSurfacePassElement > ( renderdata ) ) ;
renderdata . surfaceCounter + + ;
} ,
& renderdata ) ;
2022-07-27 18:02:20 +02:00
2022-12-16 17:17:31 +00:00
renderdata . squishOversized = false ; // don't squish popups
renderdata . dontRound = true ;
2023-12-25 18:04:00 +01:00
renderdata . popup = true ;
2024-03-25 16:08:55 +00:00
renderdata . blur = pLayer - > forceBlurPopups ;
2024-06-17 12:42:32 +02:00
renderdata . surfaceCounter = 0 ;
2024-05-09 21:47:21 +01:00
if ( popups ) {
pLayer - > popupHead - > breadthfirst (
2025-01-26 12:54:32 +00:00
[ this , & renderdata ] ( WP < CPopup > popup , void * data ) {
2024-11-17 16:16:49 +00:00
if ( ! popup - > m_pWLSurface | | ! popup - > m_pWLSurface - > resource ( ) | | ! popup - > m_bMapped )
2024-05-09 21:47:21 +01:00
return ;
2024-12-22 17:12:09 +01:00
Vector2D pos = popup - > coordsRelativeToParent ( ) ;
renderdata . localPos = pos ;
renderdata . texture = popup - > m_pWLSurface - > resource ( ) - > current . texture ;
renderdata . surface = popup - > m_pWLSurface - > resource ( ) ;
renderdata . mainSurface = false ;
m_sRenderPass . add ( makeShared < CSurfacePassElement > ( renderdata ) ) ;
renderdata . surfaceCounter + + ;
2024-05-09 21:47:21 +01:00
} ,
& renderdata ) ;
}
2022-05-14 17:23:46 +02:00
}
2024-10-19 23:03:29 +01:00
void CHyprRenderer : : renderIMEPopup ( CInputPopup * pPopup , PHLMONITOR pMonitor , timespec * time ) {
2024-12-22 17:12:09 +01:00
const auto POS = pPopup - > globalBox ( ) . pos ( ) ;
2024-03-24 16:08:25 +00:00
2024-12-22 17:12:09 +01:00
CSurfacePassElement : : SRenderData renderdata = { pMonitor , time , POS } ;
2024-03-24 16:08:25 +00:00
2024-12-22 17:12:09 +01:00
const auto SURF = pPopup - > getSurface ( ) ;
2022-08-05 17:07:01 +02:00
2024-03-24 16:08:25 +00:00
renderdata . surface = SURF ;
2022-08-05 17:07:01 +02:00
renderdata . decorate = false ;
2024-06-08 10:07:59 +02:00
renderdata . w = SURF - > current . size . x ;
renderdata . h = SURF - > current . size . y ;
2022-08-05 17:07:01 +02:00
2024-11-23 09:29:29 -05:00
static auto PBLUR = CConfigValue < Hyprlang : : INT > ( " decoration:blur:enabled " ) ;
static auto PBLURIMES = CConfigValue < Hyprlang : : INT > ( " decoration:blur:input_methods " ) ;
static auto PBLURIGNOREA = CConfigValue < Hyprlang : : FLOAT > ( " decoration:blur:input_methods_ignorealpha " ) ;
renderdata . blur = * PBLURIMES & & * PBLUR ;
if ( renderdata . blur ) {
2024-12-22 17:12:09 +01:00
renderdata . discardMode | = DISCARD_ALPHA ;
renderdata . discardOpacity = * PBLURIGNOREA ;
}
SURF - > breadthfirst (
[ this , & renderdata , & SURF ] ( SP < CWLSurfaceResource > s , const Vector2D & offset , void * data ) {
renderdata . localPos = offset ;
renderdata . texture = s - > current . texture ;
renderdata . surface = s ;
renderdata . mainSurface = s = = SURF ;
m_sRenderPass . add ( makeShared < CSurfacePassElement > ( renderdata ) ) ;
renderdata . surfaceCounter + + ;
} ,
& renderdata ) ;
2022-08-05 17:07:01 +02:00
}
2025-01-25 12:44:13 -06:00
void CHyprRenderer : : renderSessionLockSurface ( WP < SSessionLockSurface > pSurface , PHLMONITOR pMonitor , timespec * time ) {
2024-12-22 17:12:09 +01:00
CSurfacePassElement : : SRenderData renderdata = { pMonitor , time , pMonitor - > vecPosition , pMonitor - > vecPosition } ;
2023-02-03 11:58:55 +00:00
renderdata . blur = false ;
2024-05-05 17:16:00 +01:00
renderdata . surface = pSurface - > surface - > surface ( ) ;
2023-02-03 11:58:55 +00:00
renderdata . decorate = false ;
renderdata . w = pMonitor - > vecSize . x ;
renderdata . h = pMonitor - > vecSize . y ;
2024-12-22 17:12:09 +01:00
renderdata . surface - > breadthfirst (
[ this , & renderdata , & pSurface ] ( SP < CWLSurfaceResource > s , const Vector2D & offset , void * data ) {
renderdata . localPos = offset ;
renderdata . texture = s - > current . texture ;
renderdata . surface = s ;
renderdata . mainSurface = s = = pSurface - > surface - > surface ( ) ;
m_sRenderPass . add ( makeShared < CSurfacePassElement > ( renderdata ) ) ;
renderdata . surfaceCounter + + ;
} ,
& renderdata ) ;
2023-02-03 11:58:55 +00:00
}
2024-10-19 23:03:29 +01:00
void CHyprRenderer : : renderAllClientsForWorkspace ( PHLMONITOR pMonitor , PHLWORKSPACE pWorkspace , timespec * time , const Vector2D & translate , const float & scale ) {
2025-01-01 14:11:21 +01:00
static auto PDIMSPECIAL = CConfigValue < Hyprlang : : FLOAT > ( " decoration:dim_special " ) ;
static auto PBLURSPECIAL = CConfigValue < Hyprlang : : INT > ( " decoration:blur:special " ) ;
static auto PBLUR = CConfigValue < Hyprlang : : INT > ( " decoration:blur:enabled " ) ;
static auto PRENDERTEX = CConfigValue < Hyprlang : : INT > ( " misc:disable_hyprland_logo " ) ;
static auto PBACKGROUNDCOLOR = CConfigValue < Hyprlang : : INT > ( " misc:background_color " ) ;
static auto PXPMODE = CConfigValue < Hyprlang : : INT > ( " render:xp_mode " ) ;
2023-04-12 12:41:23 +01:00
if ( ! pMonitor )
2022-03-17 20:22:29 +01:00
return ;
2024-04-30 16:32:05 +01:00
if ( g_pSessionLockManager - > isSessionLocked ( ) & & ! g_pSessionLockManager - > isSessionLockPresent ( ) ) {
2023-02-03 11:58:55 +00:00
// locked with no exclusive, draw only red
2024-08-29 23:30:12 +02:00
renderSessionLockMissing ( pMonitor ) ;
2023-02-03 11:58:55 +00:00
return ;
}
2023-04-12 12:41:23 +01:00
// todo: matrices are buggy atm for some reason, but probably would be preferable in the long run
// g_pHyprOpenGL->saveMatrix();
// g_pHyprOpenGL->setMatrixScaleTranslate(translate, scale);
2025-01-01 14:11:21 +01:00
SRenderModifData RENDERMODIFDATA ;
if ( translate ! = Vector2D { 0 , 0 } )
RENDERMODIFDATA . modifs . emplace_back ( std : : make_pair < > ( SRenderModifData : : eRenderModifType : : RMOD_TYPE_TRANSLATE , translate ) ) ;
if ( scale ! = 1.f )
RENDERMODIFDATA . modifs . emplace_back ( std : : make_pair < > ( SRenderModifData : : eRenderModifType : : RMOD_TYPE_SCALE , scale ) ) ;
if ( ! RENDERMODIFDATA . modifs . empty ( ) ) {
g_pHyprRenderer - > m_sRenderPass . add ( makeShared < CRendererHintsPassElement > ( CRendererHintsPassElement : : SData { RENDERMODIFDATA } ) ) ;
}
2023-04-12 12:41:23 +01:00
2025-01-02 06:51:59 -05:00
CScopeGuard x ( [ & RENDERMODIFDATA ] {
if ( ! RENDERMODIFDATA . modifs . empty ( ) ) {
g_pHyprRenderer - > m_sRenderPass . add ( makeShared < CRendererHintsPassElement > ( CRendererHintsPassElement : : SData { SRenderModifData { } } ) ) ;
}
} ) ;
2024-04-03 14:09:58 +01:00
if ( ! pWorkspace ) {
// allow rendering without a workspace. In this case, just render layers.
2024-12-22 17:12:09 +01:00
if ( * PRENDERTEX /* inverted cfg flag */ )
m_sRenderPass . add ( makeShared < CClearPassElement > ( CClearPassElement : : SClearData { CHyprColor ( * PBACKGROUNDCOLOR ) } ) ) ;
else
g_pHyprOpenGL - > clearWithTex ( ) ; // will apply the hypr "wallpaper"
2024-04-03 14:09:58 +01:00
2024-08-26 20:24:30 +02:00
for ( auto const & ls : pMonitor - > m_aLayerSurfaceLayers [ ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND ] ) {
2024-05-09 21:47:21 +01:00
renderLayer ( ls . lock ( ) , pMonitor , time ) ;
2024-04-03 14:09:58 +01:00
}
2024-08-26 20:24:30 +02:00
for ( auto const & ls : pMonitor - > m_aLayerSurfaceLayers [ ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM ] ) {
2024-05-09 21:47:21 +01:00
renderLayer ( ls . lock ( ) , pMonitor , time ) ;
2024-04-03 14:09:58 +01:00
}
2024-08-26 20:24:30 +02:00
for ( auto const & ls : pMonitor - > m_aLayerSurfaceLayers [ ZWLR_LAYER_SHELL_V1_LAYER_TOP ] ) {
2024-05-09 21:47:21 +01:00
renderLayer ( ls . lock ( ) , pMonitor , time ) ;
2024-04-03 14:09:58 +01:00
}
2024-08-26 20:24:30 +02:00
for ( auto const & ls : pMonitor - > m_aLayerSurfaceLayers [ ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY ] ) {
2024-05-09 21:47:21 +01:00
renderLayer ( ls . lock ( ) , pMonitor , time ) ;
2024-04-03 14:09:58 +01:00
}
return ;
}
2024-12-22 17:12:09 +01:00
if ( ! * PXPMODE ) {
if ( * PRENDERTEX /* inverted cfg flag */ )
m_sRenderPass . add ( makeShared < CClearPassElement > ( CClearPassElement : : SClearData { CHyprColor ( * PBACKGROUNDCOLOR ) } ) ) ;
else
g_pHyprOpenGL - > clearWithTex ( ) ; // will apply the hypr "wallpaper"
2023-11-26 15:06:42 +00:00
2024-08-26 20:24:30 +02:00
for ( auto const & ls : pMonitor - > m_aLayerSurfaceLayers [ ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND ] ) {
2024-05-09 21:47:21 +01:00
renderLayer ( ls . lock ( ) , pMonitor , time ) ;
2023-04-22 12:36:50 +01:00
}
2024-08-26 20:24:30 +02:00
for ( auto const & ls : pMonitor - > m_aLayerSurfaceLayers [ ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM ] ) {
2024-05-09 21:47:21 +01:00
renderLayer ( ls . lock ( ) , pMonitor , time ) ;
2023-04-22 12:36:50 +01:00
}
2022-03-19 13:35:04 +01:00
}
2022-08-01 12:23:09 +02:00
// pre window pass
g_pHyprOpenGL - > preWindowPass ( ) ;
2023-09-04 15:07:56 +02:00
if ( pWorkspace - > m_bHasFullscreenWindow )
renderWorkspaceWindowsFullscreen ( pMonitor , pWorkspace , time ) ;
else
renderWorkspaceWindows ( pMonitor , pWorkspace , time ) ;
2022-05-31 14:01:00 +02:00
// and then special
2024-08-26 17:25:39 +02:00
for ( auto const & ws : g_pCompositor - > m_vWorkspaces ) {
2025-01-07 17:55:14 +00:00
if ( ws - > m_pMonitor = = pMonitor & & ws - > m_fAlpha - > value ( ) > 0.f & & ws - > m_bIsSpecialWorkspace ) {
const auto SPECIALANIMPROGRS = ws - > m_vRenderOffset - > isBeingAnimated ( ) ? ws - > m_vRenderOffset - > getCurveValue ( ) : ws - > m_fAlpha - > getCurveValue ( ) ;
2024-04-02 20:32:39 +01:00
const bool ANIMOUT = ! pMonitor - > activeSpecialWorkspace ;
2022-12-28 15:18:23 +01:00
2024-03-03 18:39:20 +00:00
if ( * PDIMSPECIAL ! = 0.f ) {
2024-12-22 17:12:09 +01:00
CRectPassElement : : SRectData data ;
data . box = { translate . x , translate . y , pMonitor - > vecTransformedSize . x * scale , pMonitor - > vecTransformedSize . y * scale } ;
data . color = CHyprColor ( 0 , 0 , 0 , * PDIMSPECIAL * ( ANIMOUT ? ( 1.0 - SPECIALANIMPROGRS ) : SPECIALANIMPROGRS ) ) ;
g_pHyprRenderer - > m_sRenderPass . add ( makeShared < CRectPassElement > ( data ) ) ;
2022-12-28 15:18:23 +01:00
}
2024-03-03 18:39:20 +00:00
if ( * PBLURSPECIAL & & * PBLUR ) {
2024-12-22 17:12:09 +01:00
CRectPassElement : : SRectData data ;
data . box = { translate . x , translate . y , pMonitor - > vecTransformedSize . x * scale , pMonitor - > vecTransformedSize . y * scale } ;
data . color = CHyprColor ( 0 , 0 , 0 , 0 ) ;
data . blur = true ;
data . blurA = ( ANIMOUT ? ( 1.0 - SPECIALANIMPROGRS ) : SPECIALANIMPROGRS ) ;
g_pHyprRenderer - > m_sRenderPass . add ( makeShared < CRectPassElement > ( data ) ) ;
2023-08-25 17:43:23 +02:00
}
2023-08-25 18:05:08 +02:00
break ;
2022-12-28 15:18:23 +01:00
}
2023-08-25 18:05:08 +02:00
}
2023-11-18 17:00:24 +00:00
// special
2024-08-26 17:25:39 +02:00
for ( auto const & ws : g_pCompositor - > m_vWorkspaces ) {
2025-01-07 17:55:14 +00:00
if ( ws - > m_fAlpha - > value ( ) > 0.f & & ws - > m_bIsSpecialWorkspace ) {
2024-03-04 15:29:45 -08:00
if ( ws - > m_bHasFullscreenWindow )
2024-04-02 20:32:39 +01:00
renderWorkspaceWindowsFullscreen ( pMonitor , ws , time ) ;
2024-03-04 15:29:45 -08:00
else
2024-04-02 20:32:39 +01:00
renderWorkspaceWindows ( pMonitor , ws , time ) ;
2024-03-04 15:29:45 -08:00
}
2022-03-18 20:03:39 +01:00
}
2022-03-19 13:35:04 +01:00
2023-11-22 20:05:50 +00:00
// pinned always above
2024-08-26 20:24:30 +02:00
for ( auto const & w : g_pCompositor - > m_vWindows ) {
2023-11-22 20:05:50 +00:00
if ( w - > isHidden ( ) & & ! w - > m_bIsMapped & & ! w - > m_bFadingOut )
continue ;
if ( ! w - > m_bPinned | | ! w - > m_bIsFloating )
continue ;
2024-04-27 12:43:12 +01:00
if ( ! shouldRenderWindow ( w , pMonitor ) )
2023-11-22 20:05:50 +00:00
continue ;
// render the bad boy
2024-04-27 12:43:12 +01:00
renderWindow ( w , pMonitor , time , true , RENDER_PASS_ALL ) ;
2023-11-22 20:05:50 +00:00
}
2023-04-17 23:45:03 +01:00
EMIT_HOOK_EVENT ( " render " , RENDER_POST_WINDOWS ) ;
2022-03-19 13:35:04 +01:00
// Render surfaces above windows for monitor
2024-08-26 20:24:30 +02:00
for ( auto const & ls : pMonitor - > m_aLayerSurfaceLayers [ ZWLR_LAYER_SHELL_V1_LAYER_TOP ] ) {
2024-05-09 21:47:21 +01:00
renderLayer ( ls . lock ( ) , pMonitor , time ) ;
2022-03-19 13:35:04 +01:00
}
2022-08-05 17:07:01 +02:00
// Render IME popups
2024-08-26 20:24:30 +02:00
for ( auto const & imep : g_pInputManager - > m_sIMERelay . m_vIMEPopups ) {
2024-03-24 16:08:25 +00:00
renderIMEPopup ( imep . get ( ) , pMonitor , time ) ;
2022-08-05 17:07:01 +02:00
}
2024-08-26 20:24:30 +02:00
for ( auto const & ls : pMonitor - > m_aLayerSurfaceLayers [ ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY ] ) {
2024-05-09 21:47:21 +01:00
renderLayer ( ls . lock ( ) , pMonitor , time ) ;
2022-03-19 13:35:04 +01:00
}
2022-03-31 17:25:23 +02:00
2024-08-26 20:24:30 +02:00
for ( auto const & lsl : pMonitor - > m_aLayerSurfaceLayers ) {
for ( auto const & ls : lsl ) {
2024-05-09 21:47:21 +01:00
renderLayer ( ls . lock ( ) , pMonitor , time , true ) ;
2024-03-25 16:20:30 +00:00
}
}
2023-04-12 12:41:23 +01:00
renderDragIcon ( pMonitor , time ) ;
//g_pHyprOpenGL->restoreMatrix();
}
2023-02-03 11:58:55 +00:00
2024-10-19 23:03:29 +01:00
void CHyprRenderer : : renderLockscreen ( PHLMONITOR pMonitor , timespec * now , const CBox & geometry ) {
2023-07-20 17:47:49 +02:00
TRACY_GPU_ZONE ( " RenderLockscreen " ) ;
2023-02-03 11:58:55 +00:00
if ( g_pSessionLockManager - > isSessionLocked ( ) ) {
2024-03-15 17:17:13 +01:00
Vector2D translate = { geometry . x , geometry . y } ;
2023-02-03 11:58:55 +00:00
2024-03-15 17:17:13 +01:00
const auto PSLS = g_pSessionLockManager - > getSessionLockSurfaceForMonitor ( pMonitor - > ID ) ;
2024-11-17 15:58:18 +00:00
if ( ! PSLS ) {
if ( g_pSessionLockManager - > shallConsiderLockMissing ( ) )
renderSessionLockMissing ( pMonitor ) ;
} else {
2023-04-12 12:41:23 +01:00
renderSessionLockSurface ( PSLS , pMonitor , now ) ;
2024-07-13 18:32:08 +08:00
g_pSessionLockManager - > onLockscreenRenderedOnMonitor ( pMonitor - > ID ) ;
2023-02-03 11:58:55 +00:00
}
}
2022-03-17 20:22:29 +01:00
}
2022-03-19 14:07:18 +01:00
2024-10-19 23:03:29 +01:00
void CHyprRenderer : : renderSessionLockMissing ( PHLMONITOR pMonitor ) {
2024-08-29 23:30:12 +02:00
const auto ALPHA = g_pSessionLockManager - > getRedScreenAlphaForMonitor ( pMonitor - > ID ) ;
CBox monbox = { { } , pMonitor - > vecPixelSize } ;
const bool ANY_PRESENT = g_pSessionLockManager - > anySessionLockSurfacesPresent ( ) ;
if ( ANY_PRESENT ) {
// render image2, without instructions. Lock still "alive", unless texture dead
2025-01-26 15:05:34 +00:00
g_pHyprOpenGL - > renderTexture ( g_pHyprOpenGL - > m_pLockDead2Texture , monbox , ALPHA ) ;
2024-08-29 23:30:12 +02:00
} else {
// render image, with instructions. Lock is gone.
2025-01-26 15:05:34 +00:00
g_pHyprOpenGL - > renderTexture ( g_pHyprOpenGL - > m_pLockDeadTexture , monbox , ALPHA ) ;
2024-08-29 23:30:12 +02:00
2024-11-01 15:52:03 +00:00
// also render text for the tty number
if ( g_pHyprOpenGL - > m_pLockTtyTextTexture ) {
CBox texbox = { { } , g_pHyprOpenGL - > m_pLockTtyTextTexture - > m_vSize } ;
2025-01-26 15:05:34 +00:00
g_pHyprOpenGL - > renderTexture ( g_pHyprOpenGL - > m_pLockTtyTextTexture , texbox , 1.F ) ;
2024-11-01 15:52:03 +00:00
}
2024-08-29 23:30:12 +02:00
}
if ( ALPHA < 1.f ) /* animate */
damageMonitor ( pMonitor ) ;
else
g_pSessionLockManager - > onLockscreenRenderedOnMonitor ( pMonitor - > ID ) ;
}
2024-10-26 02:06:13 +01:00
void CHyprRenderer : : calculateUVForSurface ( PHLWINDOW pWindow , SP < CWLSurfaceResource > pSurface , PHLMONITOR pMonitor , bool main , const Vector2D & projSize ,
2024-10-05 00:52:53 +01:00
const Vector2D & projSizeUnscaled , bool fixMisalignedFSV1 ) {
2023-01-29 13:58:47 +00:00
if ( ! pWindow | | ! pWindow - > m_bIsX11 ) {
2024-11-04 19:42:47 +00:00
static auto PEXPANDEDGES = CConfigValue < Hyprlang : : INT > ( " render:expand_undersized_textures " ) ;
Vector2D uvTL ;
Vector2D uvBR = Vector2D ( 1 , 1 ) ;
2022-08-28 10:14:43 +02:00
2024-06-08 10:07:59 +02:00
if ( pSurface - > current . viewport . hasSource ) {
2023-01-20 19:44:30 +01:00
// we stretch it to dest. if no dest, to 1,1
2024-10-31 00:20:32 +01:00
Vector2D const & bufferSize = pSurface - > current . bufferSize ;
auto const & bufferSource = pSurface - > current . viewport . source ;
2022-08-28 10:14:43 +02:00
2023-01-29 15:58:36 +00:00
// calculate UV for the basic src_box. Assume dest == size. Scale to dest later
2023-01-20 19:44:30 +01:00
uvTL = Vector2D ( bufferSource . x / bufferSize . x , bufferSource . y / bufferSize . y ) ;
uvBR = Vector2D ( ( bufferSource . x + bufferSource . width ) / bufferSize . x , ( bufferSource . y + bufferSource . height ) / bufferSize . y ) ;
2022-08-28 10:14:43 +02:00
2022-08-28 14:32:06 +02:00
if ( uvBR . x < 0.01f | | uvBR . y < 0.01f ) {
uvTL = Vector2D ( ) ;
2022-12-16 17:17:31 +00:00
uvBR = Vector2D ( 1 , 1 ) ;
2022-08-28 14:32:06 +02:00
}
2022-08-28 10:14:43 +02:00
}
2024-01-28 23:42:39 +00:00
if ( projSize ! = Vector2D { } & & fixMisalignedFSV1 ) {
// instead of nearest_neighbor (we will repeat / skip)
// just cut off / expand surface
2024-07-31 20:47:26 +01:00
const Vector2D PIXELASUV = Vector2D { 1 , 1 } / pSurface - > current . bufferSize ;
const Vector2D MISALIGNMENT = pSurface - > current . bufferSize - projSize ;
2024-01-28 23:42:39 +00:00
if ( MISALIGNMENT ! = Vector2D { } )
uvBR - = MISALIGNMENT * PIXELASUV ;
}
2024-10-05 00:52:53 +01:00
// if the surface is smaller than our viewport, extend its edges.
// this will break if later on xdg geometry is hit, but we really try
// to let the apps know to NOT add CSD. Also if source is there.
// there is no way to fix this if that's the case
2024-11-04 19:42:47 +00:00
if ( * PEXPANDEDGES ) {
const auto MONITOR_WL_SCALE = std : : ceil ( pMonitor - > scale ) ;
const bool SCALE_UNAWARE = MONITOR_WL_SCALE ! = pSurface - > current . scale & & ! pSurface - > current . viewport . hasDestination ;
const auto EXPECTED_SIZE =
( ( pSurface - > current . viewport . hasDestination ? pSurface - > current . viewport . destination : pSurface - > current . bufferSize / pSurface - > current . scale ) * pMonitor - > scale )
. round ( ) ;
if ( ! SCALE_UNAWARE & & ( EXPECTED_SIZE . x < projSize . x | | EXPECTED_SIZE . y < projSize . y ) ) {
// this will not work with shm AFAIK, idk why.
// NOTE: this math is wrong if we have a source... or geom updates later, but I don't think we can do much
const auto FIX = projSize / EXPECTED_SIZE ;
uvBR = uvBR * FIX ;
}
2024-10-05 00:52:53 +01:00
}
2022-12-16 17:17:31 +00:00
g_pHyprOpenGL - > m_RenderData . primarySurfaceUVTopLeft = uvTL ;
2022-08-28 10:14:43 +02:00
g_pHyprOpenGL - > m_RenderData . primarySurfaceUVBottomRight = uvBR ;
if ( g_pHyprOpenGL - > m_RenderData . primarySurfaceUVTopLeft = = Vector2D ( ) & & g_pHyprOpenGL - > m_RenderData . primarySurfaceUVBottomRight = = Vector2D ( 1 , 1 ) ) {
// No special UV mods needed
2022-12-16 17:17:31 +00:00
g_pHyprOpenGL - > m_RenderData . primarySurfaceUVTopLeft = Vector2D ( - 1 , - 1 ) ;
2022-08-28 10:14:43 +02:00
g_pHyprOpenGL - > m_RenderData . primarySurfaceUVBottomRight = Vector2D ( - 1 , - 1 ) ;
}
2023-01-29 13:58:47 +00:00
if ( ! main | | ! pWindow )
2023-01-20 19:44:30 +01:00
return ;
2022-08-28 10:14:43 +02:00
2024-05-10 23:28:33 +01:00
CBox geom = pWindow - > m_pXDGSurface - > current . geometry ;
2023-01-20 19:44:30 +01:00
// ignore X and Y, adjust uv
2024-10-05 00:52:53 +01:00
if ( geom . x ! = 0 | | geom . y ! = 0 | | geom . width > projSizeUnscaled . x | | geom . height > projSizeUnscaled . y ) {
2024-06-08 10:07:59 +02:00
const auto XPERC = ( double ) geom . x / ( double ) pSurface - > current . size . x ;
const auto YPERC = ( double ) geom . y / ( double ) pSurface - > current . size . y ;
const auto WPERC = ( double ) ( geom . x + geom . width ) / ( double ) pSurface - > current . size . x ;
const auto HPERC = ( double ) ( geom . y + geom . height ) / ( double ) pSurface - > current . size . y ;
2022-08-28 10:14:43 +02:00
2023-01-20 19:44:30 +01:00
const auto TOADDTL = Vector2D ( XPERC * ( uvBR . x - uvTL . x ) , YPERC * ( uvBR . y - uvTL . y ) ) ;
2024-10-03 22:00:44 +00:00
uvBR = uvBR - Vector2D ( ( 1.0 - WPERC ) * ( uvBR . x - uvTL . x ) , ( 1.0 - HPERC ) * ( uvBR . y - uvTL . y ) ) ;
2023-01-20 19:44:30 +01:00
uvTL = uvTL + TOADDTL ;
2022-08-28 10:14:43 +02:00
2024-10-05 00:52:53 +01:00
auto maxSize = projSizeUnscaled ;
2023-11-12 23:57:53 +00:00
2024-06-08 10:07:59 +02:00
if ( pWindow - > m_pWLSurface - > small ( ) & & ! pWindow - > m_pWLSurface - > m_bFillIgnoreSmall )
maxSize = pWindow - > m_pWLSurface - > getViewporterCorrectedSize ( ) ;
2023-11-12 23:57:53 +00:00
if ( geom . width > maxSize . x )
uvBR . x = uvBR . x * ( maxSize . x / geom . width ) ;
if ( geom . height > maxSize . y )
uvBR . y = uvBR . y * ( maxSize . y / geom . height ) ;
2023-01-20 19:44:30 +01:00
}
g_pHyprOpenGL - > m_RenderData . primarySurfaceUVTopLeft = uvTL ;
g_pHyprOpenGL - > m_RenderData . primarySurfaceUVBottomRight = uvBR ;
if ( g_pHyprOpenGL - > m_RenderData . primarySurfaceUVTopLeft = = Vector2D ( ) & & g_pHyprOpenGL - > m_RenderData . primarySurfaceUVBottomRight = = Vector2D ( 1 , 1 ) ) {
// No special UV mods needed
g_pHyprOpenGL - > m_RenderData . primarySurfaceUVTopLeft = Vector2D ( - 1 , - 1 ) ;
g_pHyprOpenGL - > m_RenderData . primarySurfaceUVBottomRight = Vector2D ( - 1 , - 1 ) ;
2022-08-28 10:14:43 +02:00
}
} else {
2022-12-16 17:17:31 +00:00
g_pHyprOpenGL - > m_RenderData . primarySurfaceUVTopLeft = Vector2D ( - 1 , - 1 ) ;
2022-08-28 10:14:43 +02:00
g_pHyprOpenGL - > m_RenderData . primarySurfaceUVBottomRight = Vector2D ( - 1 , - 1 ) ;
}
}
2024-10-19 23:03:29 +01:00
void CHyprRenderer : : renderMonitor ( PHLMONITOR pMonitor ) {
2023-11-24 10:54:21 +00:00
static std : : chrono : : high_resolution_clock : : time_point renderStart = std : : chrono : : high_resolution_clock : : now ( ) ;
static std : : chrono : : high_resolution_clock : : time_point renderStartOverlay = std : : chrono : : high_resolution_clock : : now ( ) ;
2023-03-24 19:23:16 +00:00
static std : : chrono : : high_resolution_clock : : time_point endRenderOverlay = std : : chrono : : high_resolution_clock : : now ( ) ;
2024-03-03 18:39:20 +00:00
static auto PDEBUGOVERLAY = CConfigValue < Hyprlang : : INT > ( " debug:overlay " ) ;
static auto PDAMAGETRACKINGMODE = CConfigValue < Hyprlang : : INT > ( " debug:damage_tracking " ) ;
static auto PDAMAGEBLINK = CConfigValue < Hyprlang : : INT > ( " debug:damage_blink " ) ;
2024-08-06 14:52:19 +01:00
static auto PDIRECTSCANOUT = CConfigValue < Hyprlang : : INT > ( " render:direct_scanout " ) ;
2024-03-03 18:39:20 +00:00
static auto PVFR = CConfigValue < Hyprlang : : INT > ( " misc:vfr " ) ;
2024-05-09 22:23:01 +01:00
static auto PZOOMFACTOR = CConfigValue < Hyprlang : : FLOAT > ( " cursor:zoom_factor " ) ;
2024-03-03 18:39:20 +00:00
static auto PANIMENABLED = CConfigValue < Hyprlang : : INT > ( " animations:enabled " ) ;
static auto PFIRSTLAUNCHANIM = CConfigValue < Hyprlang : : INT > ( " animations:first_launch_animation " ) ;
static auto PTEARINGENABLED = CConfigValue < Hyprlang : : INT > ( " general:allow_tearing " ) ;
2023-03-24 19:23:16 +00:00
static int damageBlinkCleanup = 0 ; // because double-buffered
2024-03-03 18:39:20 +00:00
if ( ! * PDAMAGEBLINK )
2023-03-24 19:23:16 +00:00
damageBlinkCleanup = 0 ;
2023-05-24 15:49:27 +02:00
static bool firstLaunch = true ;
2024-03-03 18:39:20 +00:00
static bool firstLaunchAnimActive = * PFIRSTLAUNCHANIM ;
2023-05-01 02:49:41 +01:00
float zoomInFactorFirstLaunch = 1.f ;
if ( firstLaunch ) {
firstLaunch = false ;
m_tRenderTimer . reset ( ) ;
}
2023-05-24 15:49:27 +02:00
if ( m_tRenderTimer . getSeconds ( ) < 1.5f & & firstLaunchAnimActive ) { // TODO: make the animation system more damage-flexible so that this can be migrated to there
2024-03-03 18:39:20 +00:00
if ( ! * PANIMENABLED ) {
2023-07-22 19:26:39 +02:00
zoomInFactorFirstLaunch = 1.f ;
firstLaunchAnimActive = false ;
} else {
zoomInFactorFirstLaunch = 2.f - g_pAnimationManager - > getBezier ( " default " ) - > getYForPoint ( m_tRenderTimer . getSeconds ( ) / 1.5 ) ;
damageMonitor ( pMonitor ) ;
}
2023-05-24 15:49:27 +02:00
} else {
firstLaunchAnimActive = false ;
2023-05-01 02:49:41 +01:00
}
2023-11-24 10:54:21 +00:00
renderStart = std : : chrono : : high_resolution_clock : : now ( ) ;
2023-03-24 19:23:16 +00:00
2024-03-03 18:39:20 +00:00
if ( * PDEBUGOVERLAY = = 1 )
2023-03-24 19:23:16 +00:00
g_pDebugOverlay - > frameData ( pMonitor ) ;
2024-12-29 17:19:12 -06:00
if ( ! g_pCompositor - > m_bSessionActive )
2023-03-24 19:23:16 +00:00
return ;
if ( pMonitor - > ID = = m_pMostHzMonitor - > ID | |
2024-03-03 18:39:20 +00:00
* PVFR = = 1 ) { // unfortunately with VFR we don't have the guarantee mostHz is going to be updated all the time, so we have to ignore that
2023-03-24 19:23:16 +00:00
g_pCompositor - > sanityCheckWorkspaces ( ) ;
g_pConfigManager - > dispatchExecOnce ( ) ; // We exec-once when at least one monitor starts refreshing, meaning stuff has init'd
if ( g_pConfigManager - > m_bWantsMonitorReload )
g_pConfigManager - > performMonitorReload ( ) ;
}
if ( pMonitor - > scheduledRecalc ) {
pMonitor - > scheduledRecalc = false ;
g_pLayoutManager - > getCurrentLayout ( ) - > recalculateMonitor ( pMonitor - > ID ) ;
}
2024-07-21 13:09:54 +02:00
if ( ! pMonitor - > output - > needsFrame & & pMonitor - > forceFullFrames = = 0 )
return ;
2023-09-28 21:48:33 +01:00
// tearing and DS first
bool shouldTear = false ;
2023-09-30 17:07:50 +01:00
if ( pMonitor - > tearingState . nextRenderTorn ) {
pMonitor - > tearingState . nextRenderTorn = false ;
2023-09-28 21:48:33 +01:00
2024-03-03 18:39:20 +00:00
if ( ! * PTEARINGENABLED ) {
2023-09-28 21:48:33 +01:00
Debug : : log ( WARN , " Tearing commit requested but the master switch general:allow_tearing is off, ignoring " ) ;
return ;
}
if ( g_pHyprOpenGL - > m_RenderData . mouseZoomFactor ! = 1.0 ) {
Debug : : log ( WARN , " Tearing commit requested but scale factor is not 1, ignoring " ) ;
return ;
}
2023-09-30 17:07:50 +01:00
if ( ! pMonitor - > tearingState . canTear ) {
2023-09-28 21:48:33 +01:00
Debug : : log ( WARN , " Tearing commit requested but monitor doesn't support it, ignoring " ) ;
return ;
}
2024-04-27 12:43:12 +01:00
if ( ! pMonitor - > solitaryClient . expired ( ) )
2023-09-28 21:48:33 +01:00
shouldTear = true ;
}
2024-07-30 15:50:14 +02:00
pMonitor - > tearingState . activelyTearing = shouldTear ;
2025-02-05 09:53:09 -05:00
if ( ( * PDIRECTSCANOUT = = 1 | |
( * PDIRECTSCANOUT = = 2 & & pMonitor - > activeWorkspace - > getFullscreenWindow ( ) & & pMonitor - > activeWorkspace - > getFullscreenWindow ( ) - > getContentType ( ) = = CONTENT_TYPE_GAME ) ) & &
! shouldTear ) {
2024-07-21 13:09:54 +02:00
if ( pMonitor - > attemptDirectScanout ( ) ) {
2023-03-24 19:23:16 +00:00
return ;
2024-07-21 13:09:54 +02:00
} else if ( ! pMonitor - > lastScanout . expired ( ) ) {
2023-03-24 19:23:16 +00:00
Debug : : log ( LOG , " Left a direct scanout. " ) ;
2024-07-21 13:09:54 +02:00
pMonitor - > lastScanout . reset ( ) ;
2024-08-17 19:27:11 +02:00
2024-12-29 17:19:12 -06:00
// reset DRM format, but only if needed since it might modeset
if ( pMonitor - > output - > state - > state ( ) . drmFormat ! = pMonitor - > prevDrmFormat )
pMonitor - > output - > state - > setFormat ( pMonitor - > prevDrmFormat ) ;
2024-08-31 08:07:52 -05:00
pMonitor - > drmFormat = pMonitor - > prevDrmFormat ;
2023-03-24 19:23:16 +00:00
}
}
EMIT_HOOK_EVENT ( " preRender " , pMonitor ) ;
timespec now ;
clock_gettime ( CLOCK_MONOTONIC , & now ) ;
// check the damage
2024-07-21 13:09:54 +02:00
bool hasChanged = pMonitor - > output - > needsFrame | | pMonitor - > damage . hasChanged ( ) ;
2023-03-24 19:23:16 +00:00
2024-03-03 18:39:20 +00:00
if ( ! hasChanged & & * PDAMAGETRACKINGMODE ! = DAMAGE_TRACKING_NONE & & pMonitor - > forceFullFrames = = 0 & & damageBlinkCleanup = = 0 )
2023-04-09 17:59:24 +01:00
return ;
2024-03-03 18:39:20 +00:00
if ( * PDAMAGETRACKINGMODE = = - 1 ) {
2023-03-24 19:23:16 +00:00
Debug : : log ( CRIT , " Damage tracking mode -1 ???? " ) ;
return ;
}
2023-04-17 23:45:03 +01:00
EMIT_HOOK_EVENT ( " render " , RENDER_PRE ) ;
2023-03-24 19:23:16 +00:00
pMonitor - > renderingActive = true ;
2024-02-19 18:17:32 +00:00
// we need to cleanup fading out when rendering the appropriate context
g_pCompositor - > cleanupFadingOut ( pMonitor - > ID ) ;
// TODO: this is getting called with extents being 0,0,0,0 should it be?
// potentially can save on resources.
2024-02-18 16:04:08 +00:00
TRACY_GPU_ZONE ( " Render " ) ;
2024-06-12 12:28:52 -05:00
static bool zoomLock = false ;
if ( zoomLock & & * PZOOMFACTOR = = 1.f ) {
g_pPointerManager - > unlockSoftwareAll ( ) ;
zoomLock = false ;
} else if ( ! zoomLock & & * PZOOMFACTOR ! = 1.f ) {
g_pPointerManager - > lockSoftwareAll ( ) ;
zoomLock = true ;
}
2024-02-18 16:04:08 +00:00
if ( pMonitor = = g_pCompositor - > getMonitorFromCursor ( ) )
2024-03-03 18:39:20 +00:00
g_pHyprOpenGL - > m_RenderData . mouseZoomFactor = std : : clamp ( * PZOOMFACTOR , 1.f , INFINITY ) ;
2024-02-18 16:04:08 +00:00
else
g_pHyprOpenGL - > m_RenderData . mouseZoomFactor = 1.f ;
if ( zoomInFactorFirstLaunch > 1.f ) {
g_pHyprOpenGL - > m_RenderData . mouseZoomFactor = zoomInFactorFirstLaunch ;
g_pHyprOpenGL - > m_RenderData . mouseZoomUseMouse = false ;
g_pHyprOpenGL - > m_RenderData . useNearestNeighbor = false ;
2024-02-29 19:04:40 +00:00
pMonitor - > forceFullFrames = 10 ;
2024-02-18 16:04:08 +00:00
}
2024-02-23 01:02:32 +00:00
CRegion damage , finalDamage ;
2024-02-18 16:04:08 +00:00
if ( ! beginRender ( pMonitor , damage , RENDER_MODE_NORMAL ) ) {
Debug : : log ( ERR , " renderer: couldn't beginRender()! " ) ;
return ;
}
2023-03-24 19:23:16 +00:00
// if we have no tracking or full tracking, invalidate the entire monitor
2024-12-22 17:12:09 +01:00
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 } ;
2023-03-24 19:23:16 +00:00
2024-12-22 17:12:09 +01:00
finalDamage = damage ;
2023-03-24 19:23:16 +00:00
2024-02-19 18:17:32 +00:00
// update damage in renderdata as we modified it
2024-02-23 01:02:32 +00:00
g_pHyprOpenGL - > setDamage ( damage , finalDamage ) ;
2023-03-24 19:23:16 +00:00
2024-02-19 18:17:32 +00:00
if ( pMonitor - > forceFullFrames > 0 ) {
pMonitor - > forceFullFrames - = 1 ;
if ( pMonitor - > forceFullFrames > 10 )
pMonitor - > forceFullFrames = 0 ;
}
2023-04-17 23:45:03 +01:00
EMIT_HOOK_EVENT ( " render " , RENDER_BEGIN ) ;
2023-09-29 17:51:07 +01:00
bool renderCursor = true ;
2024-04-23 12:29:01 +01:00
if ( ! finalDamage . empty ( ) ) {
2024-04-27 12:43:12 +01:00
if ( pMonitor - > solitaryClient . expired ( ) ) {
2024-04-23 12:29:01 +01:00
if ( pMonitor - > isMirror ( ) ) {
g_pHyprOpenGL - > blend ( false ) ;
g_pHyprOpenGL - > renderMirrored ( ) ;
g_pHyprOpenGL - > blend ( true ) ;
EMIT_HOOK_EVENT ( " render " , RENDER_POST_MIRROR ) ;
renderCursor = false ;
} else {
CBox renderBox = { 0 , 0 , ( int ) pMonitor - > vecPixelSize . x , ( int ) pMonitor - > vecPixelSize . y } ;
renderWorkspace ( pMonitor , pMonitor - > activeWorkspace , & now , renderBox ) ;
renderLockscreen ( pMonitor , & now , renderBox ) ;
2024-10-19 23:03:29 +01:00
if ( pMonitor = = g_pCompositor - > m_pLastMonitor ) {
2024-04-23 12:29:01 +01:00
g_pHyprNotificationOverlay - > draw ( pMonitor ) ;
g_pHyprError - > draw ( ) ;
}
// for drawing the debug overlay
2024-10-19 23:03:29 +01:00
if ( pMonitor = = g_pCompositor - > m_vMonitors . front ( ) & & * PDEBUGOVERLAY = = 1 ) {
2024-04-23 12:29:01 +01:00
renderStartOverlay = std : : chrono : : high_resolution_clock : : now ( ) ;
g_pDebugOverlay - > draw ( ) ;
endRenderOverlay = std : : chrono : : high_resolution_clock : : now ( ) ;
}
if ( * PDAMAGEBLINK & & damageBlinkCleanup = = 0 ) {
2024-12-22 17:12:09 +01:00
CRectPassElement : : SRectData data ;
data . box = { 0 , 0 , pMonitor - > vecTransformedSize . x , pMonitor - > vecTransformedSize . y } ;
data . color = CHyprColor ( 1.0 , 0.0 , 1.0 , 100.0 / 255.0 ) ;
m_sRenderPass . add ( makeShared < CRectPassElement > ( data ) ) ;
2024-04-23 12:29:01 +01:00
damageBlinkCleanup = 1 ;
} else if ( * PDAMAGEBLINK ) {
damageBlinkCleanup + + ;
if ( damageBlinkCleanup > 3 )
damageBlinkCleanup = 0 ;
}
2023-09-29 17:51:07 +01:00
}
2024-04-23 16:08:54 +01:00
} else
2024-12-22 17:12:09 +01:00
renderWindow ( pMonitor - > solitaryClient . lock ( ) , pMonitor , & now , false , RENDER_PASS_MAIN /* solitary = no popups */ ) ;
2024-06-09 22:28:51 +02:00
} else if ( ! pMonitor - > isMirror ( ) ) {
2024-04-23 16:08:54 +01:00
sendFrameEventsToWorkspace ( pMonitor , pMonitor - > activeWorkspace , & now ) ;
if ( pMonitor - > activeSpecialWorkspace )
sendFrameEventsToWorkspace ( pMonitor , pMonitor - > activeSpecialWorkspace , & now ) ;
2023-09-29 17:51:07 +01:00
}
2023-03-24 19:23:16 +00:00
2023-09-29 17:51:07 +01:00
renderCursor = renderCursor & & shouldRenderCursor ( ) ;
2023-11-24 10:54:21 +00:00
if ( renderCursor ) {
2023-09-29 17:51:07 +01:00
TRACY_GPU_ZONE ( " RenderCursor " ) ;
2024-05-10 23:38:46 +01:00
g_pPointerManager - > renderSoftwareCursorsFor ( pMonitor - > self . lock ( ) , & now , g_pHyprOpenGL - > m_RenderData . damage ) ;
2023-05-01 02:49:41 +01:00
}
2023-04-17 23:45:03 +01:00
EMIT_HOOK_EVENT ( " render " , RENDER_LAST_MOMENT ) ;
2023-11-24 10:54:21 +00:00
endRender ( ) ;
2023-03-24 19:23:16 +00:00
2023-07-20 17:47:49 +02:00
TRACY_GPU_COLLECT ;
2024-12-29 17:19:12 -06:00
CRegion frameDamage { g_pHyprOpenGL - > m_RenderData . damage } ;
2023-03-24 19:23:16 +00:00
2024-12-29 17:19:12 -06:00
const auto TRANSFORM = invertTransform ( pMonitor - > transform ) ;
frameDamage . transform ( wlTransformToHyprutils ( TRANSFORM ) , pMonitor - > vecTransformedSize . x , pMonitor - > vecTransformedSize . y ) ;
2023-03-24 19:23:16 +00:00
2024-12-29 17:19:12 -06:00
if ( * PDAMAGETRACKINGMODE = = DAMAGE_TRACKING_NONE | | * PDAMAGETRACKINGMODE = = DAMAGE_TRACKING_MONITOR )
frameDamage . add ( 0 , 0 , ( int ) pMonitor - > vecTransformedSize . x , ( int ) pMonitor - > vecTransformedSize . y ) ;
2023-03-24 19:23:16 +00:00
2024-12-29 17:19:12 -06:00
if ( * PDAMAGEBLINK )
frameDamage . add ( damage ) ;
2023-03-24 19:23:16 +00:00
2024-12-29 17:19:12 -06:00
if ( ! pMonitor - > mirrors . empty ( ) )
2024-12-22 17:12:09 +01:00
damageMirrorsWith ( pMonitor , frameDamage ) ;
2024-07-30 15:46:35 +02:00
2023-03-24 19:23:16 +00:00
pMonitor - > renderingActive = false ;
2023-04-17 23:45:03 +01:00
EMIT_HOOK_EVENT ( " render " , RENDER_POST ) ;
2024-12-29 17:19:12 -06:00
pMonitor - > output - > state - > addDamage ( frameDamage ) ;
2024-07-21 13:09:54 +02:00
pMonitor - > output - > state - > setPresentationMode ( shouldTear ? Aquamarine : : eOutputPresentationMode : : AQ_OUTPUT_PRESENTATION_IMMEDIATE :
Aquamarine : : eOutputPresentationMode : : AQ_OUTPUT_PRESENTATION_VSYNC ) ;
2023-09-28 21:48:33 +01:00
2024-07-21 13:09:54 +02:00
commitPendingAndDoExplicitSync ( pMonitor ) ;
2023-04-03 17:01:05 +01:00
2023-09-28 21:48:33 +01:00
if ( shouldTear )
2023-09-30 17:07:50 +01:00
pMonitor - > tearingState . busy = true ;
2023-09-28 21:48:33 +01:00
2024-03-03 18:39:20 +00:00
if ( * PDAMAGEBLINK | | * PVFR = = 0 | | pMonitor - > pendingFrame )
2024-07-21 13:09:54 +02:00
g_pCompositor - > scheduleFrameForMonitor ( pMonitor , Aquamarine : : IOutput : : AQ_SCHEDULE_RENDER_MONITOR ) ;
2023-03-24 19:23:16 +00:00
pMonitor - > pendingFrame = false ;
2024-08-12 19:19:03 +02:00
const float durationUs = std : : chrono : : duration_cast < std : : chrono : : nanoseconds > ( std : : chrono : : high_resolution_clock : : now ( ) - renderStart ) . count ( ) / 1000.f ;
g_pDebugOverlay - > renderData ( pMonitor , durationUs ) ;
2023-03-24 19:23:16 +00:00
2024-03-03 18:39:20 +00:00
if ( * PDEBUGOVERLAY = = 1 ) {
2024-10-19 23:03:29 +01:00
if ( pMonitor = = g_pCompositor - > m_vMonitors . front ( ) ) {
2024-08-12 19:19:03 +02:00
const float noOverlayUs = durationUs - std : : chrono : : duration_cast < std : : chrono : : nanoseconds > ( endRenderOverlay - renderStartOverlay ) . count ( ) / 1000.f ;
g_pDebugOverlay - > renderDataNoOverlay ( pMonitor , noOverlayUs ) ;
2024-12-22 17:12:09 +01:00
} else
2024-08-12 19:19:03 +02:00
g_pDebugOverlay - > renderDataNoOverlay ( pMonitor , durationUs ) ;
2023-03-24 19:23:16 +00:00
}
}
2025-03-14 02:15:18 +03:00
static const hdr_output_metadata NO_HDR_METADATA = { . hdmi_metadata_type1 = hdr_metadata_infoframe { . eotf = 0 } } ;
2025-01-07 21:32:50 +03:00
2025-03-14 02:15:18 +03:00
static hdr_output_metadata createHDRMetadata ( SImageDescription settings , Aquamarine : : IOutput : : SParsedEDID edid ) {
2025-02-26 17:56:37 +03:00
if ( settings . transferFunction ! = CM_TRANSFER_FUNCTION_ST2084_PQ )
2025-03-14 02:15:18 +03:00
return NO_HDR_METADATA ; // empty metadata for SDR
2025-01-07 21:32:50 +03:00
const auto toNits = [ ] ( uint32_t value ) { return uint16_t ( std : : round ( value ) ) ; } ;
const auto to16Bit = [ ] ( uint32_t value ) { return uint16_t ( std : : round ( value * 50000 ) ) ; } ;
2025-03-14 02:15:18 +03:00
auto colorimetry = settings . primariesNameSet | | settings . primaries = = SPCPRimaries { } ? getPrimaries ( settings . primariesNamed ) : settings . primaries ;
2025-01-07 21:32:50 +03:00
auto luminances = settings . masteringLuminances . max > 0 ?
2025-03-14 02:15:18 +03:00
settings . masteringLuminances :
SImageDescription : : SPCMasteringLuminances { . min = edid . hdrMetadata - > desiredContentMinLuminance , . max = edid . hdrMetadata - > desiredContentMaxLuminance } ;
2025-01-07 21:32:50 +03:00
Debug : : log ( TRACE , " ColorManagement primaries {},{} {},{} {},{} {},{} " , colorimetry . red . x , colorimetry . red . y , colorimetry . green . x , colorimetry . green . y , colorimetry . blue . x ,
2025-03-14 02:15:18 +03:00
colorimetry . blue . y , colorimetry . white . x , colorimetry . white . y ) ;
2025-01-07 21:32:50 +03:00
Debug : : log ( TRACE , " ColorManagement min {}, max {}, cll {}, fall {} " , luminances . min , luminances . max , settings . maxCLL , settings . maxFALL ) ;
return hdr_output_metadata {
2025-03-14 02:15:18 +03:00
. metadata_type = 0 ,
. hdmi_metadata_type1 =
2025-01-07 21:32:50 +03:00
hdr_metadata_infoframe {
2025-03-14 02:15:18 +03:00
. eotf = 2 ,
. metadata_type = 0 ,
. display_primaries =
{
2025-01-07 21:32:50 +03:00
{ . x = to16Bit ( colorimetry . red . x ) , . y = to16Bit ( colorimetry . red . y ) } ,
{ . x = to16Bit ( colorimetry . green . x ) , . y = to16Bit ( colorimetry . green . y ) } ,
{ . x = to16Bit ( colorimetry . blue . x ) , . y = to16Bit ( colorimetry . blue . y ) } ,
} ,
2025-03-14 02:15:18 +03:00
. white_point = { . x = to16Bit ( colorimetry . white . x ) , . y = to16Bit ( colorimetry . white . y ) } ,
. max_display_mastering_luminance = toNits ( luminances . max ) ,
. min_display_mastering_luminance = toNits ( luminances . min * 10000 ) ,
. max_cll = toNits ( settings . maxCLL ) ,
. max_fall = toNits ( settings . maxFALL ) ,
2025-01-07 21:32:50 +03:00
} ,
} ;
}
2024-10-19 23:03:29 +01:00
bool CHyprRenderer : : commitPendingAndDoExplicitSync ( PHLMONITOR pMonitor ) {
2025-03-08 13:24:22 -06:00
pMonitor - > commitSeq + + ;
2025-03-14 02:15:18 +03:00
static auto PPASS = CConfigValue < Hyprlang : : INT > ( " render:cm_fs_passthrough " ) ;
const bool PHDR = pMonitor - > imageDescription . transferFunction = = CM_TRANSFER_FUNCTION_ST2084_PQ ;
2025-01-07 21:32:50 +03:00
2025-01-08 10:28:55 +00:00
const bool SUPPORTSPQ = pMonitor - > output - > parsedEDID . hdrMetadata . has_value ( ) ? pMonitor - > output - > parsedEDID . hdrMetadata - > supportsPQ : false ;
Debug : : log ( TRACE , " ColorManagement supportsBT2020 {}, supportsPQ {} " , pMonitor - > output - > parsedEDID . supportsBT2020 , SUPPORTSPQ ) ;
2025-03-14 02:15:18 +03:00
2025-01-08 10:28:55 +00:00
if ( pMonitor - > output - > parsedEDID . supportsBT2020 & & SUPPORTSPQ ) {
2025-03-14 02:15:18 +03:00
if ( * PPASS & & pMonitor - > activeWorkspace & & pMonitor - > activeWorkspace - > m_bHasFullscreenWindow & & pMonitor - > activeWorkspace - > m_efFullscreenMode = = FSMODE_FULLSCREEN ) {
2025-02-26 17:56:37 +03:00
const auto WINDOW = pMonitor - > activeWorkspace - > getFullscreenWindow ( ) ;
const auto ROOT_SURF = WINDOW - > m_pWLSurface - > resource ( ) ;
const auto SURF =
ROOT_SURF - > findFirstPreorder ( [ ROOT_SURF ] ( SP < CWLSurfaceResource > surf ) { return surf - > colorManagement . valid ( ) & & surf - > extends ( ) = = ROOT_SURF - > extends ( ) ; } ) ;
if ( SURF & & SURF - > colorManagement . valid ( ) & & SURF - > colorManagement - > hasImageDescription ( ) ) {
2025-01-17 23:23:57 +03:00
bool needsHdrMetadataUpdate = SURF - > colorManagement - > needsHdrMetadataUpdate ( ) | | pMonitor - > m_previousFSWindow ! = WINDOW ;
2025-01-12 20:02:41 +03:00
if ( SURF - > colorManagement - > needsHdrMetadataUpdate ( ) )
2025-02-26 17:56:37 +03:00
SURF - > colorManagement - > setHDRMetadata ( createHDRMetadata ( SURF - > colorManagement - > imageDescription ( ) , pMonitor - > output - > parsedEDID ) ) ;
2025-01-12 20:02:41 +03:00
if ( needsHdrMetadataUpdate )
pMonitor - > output - > state - > setHDRMetadata ( SURF - > colorManagement - > hdrMetadata ( ) ) ;
2025-03-14 02:15:18 +03:00
} else if ( ( pMonitor - > output - > state - > state ( ) . hdrMetadata . hdmi_metadata_type1 . eotf = = 2 ) ! = PHDR )
pMonitor - > output - > state - > setHDRMetadata ( PHDR ? createHDRMetadata ( pMonitor - > imageDescription , pMonitor - > output - > parsedEDID ) : NO_HDR_METADATA ) ;
2025-01-17 23:23:57 +03:00
pMonitor - > m_previousFSWindow = WINDOW ;
2025-01-12 20:02:41 +03:00
} else {
2025-03-14 02:15:18 +03:00
if ( ( pMonitor - > output - > state - > state ( ) . hdrMetadata . hdmi_metadata_type1 . eotf = = 2 ) ! = PHDR )
pMonitor - > output - > state - > setHDRMetadata ( PHDR ? createHDRMetadata ( pMonitor - > imageDescription , pMonitor - > output - > parsedEDID ) : NO_HDR_METADATA ) ;
2025-01-17 23:23:57 +03:00
pMonitor - > m_previousFSWindow . reset ( ) ;
}
}
2025-03-14 02:15:18 +03:00
const bool needsWCG = pMonitor - > output - > state - > state ( ) . hdrMetadata . hdmi_metadata_type1 . eotf = = 2 | | pMonitor - > imageDescription . primariesNamed = = CM_PRIMARIES_BT2020 ;
2025-01-17 23:23:57 +03:00
if ( pMonitor - > output - > state - > state ( ) . wideColorGamut ! = needsWCG ) {
Debug : : log ( TRACE , " Setting wide color gamut {} " , needsWCG ? " on " : " off " ) ;
pMonitor - > output - > state - > setWideColorGamut ( needsWCG ) ;
// FIXME do not trust enabled10bit, auto switch to 10bit and back if needed
if ( needsWCG & & ! pMonitor - > enabled10bit ) {
Debug : : log ( WARN , " Wide color gamut is enabled but the display is not in 10bit mode " ) ;
static bool shown = false ;
if ( ! shown ) {
g_pHyprNotificationOverlay - > addNotification ( " Wide color gamut is enabled but the display is not in 10bit mode " , CHyprColor { } , 15000 , ICON_WARNING ) ;
shown = true ;
}
2025-01-12 20:02:41 +03:00
}
2025-01-07 21:32:50 +03:00
}
2025-02-02 22:25:29 +03:00
if ( pMonitor - > activeWorkspace & & pMonitor - > activeWorkspace - > m_bHasFullscreenWindow & & pMonitor - > activeWorkspace - > m_efFullscreenMode = = FSMODE_FULLSCREEN ) {
const auto WINDOW = pMonitor - > activeWorkspace - > getFullscreenWindow ( ) ;
pMonitor - > output - > state - > setContentType ( NContentType : : toDRM ( WINDOW - > getContentType ( ) ) ) ;
} else
pMonitor - > output - > state - > setContentType ( NContentType : : toDRM ( CONTENT_TYPE_NONE ) ) ;
2024-10-08 16:59:15 +01:00
if ( pMonitor - > ctmUpdated ) {
pMonitor - > ctmUpdated = false ;
pMonitor - > output - > state - > setCTM ( pMonitor - > ctm ) ;
}
2024-08-06 14:52:19 +01:00
bool ok = pMonitor - > state . commit ( ) ;
if ( ! ok ) {
syncobj: use eventfd instead of stalling fd checks (#9437)
* syncobj: cleanup and use uniqueptrs
cleanup a bit missing removals if resource not good, erasing from
containers etc. make use of unique ptrs instead. and add default
destructors.
* syncobj: rework syncobj entirerly
remove early buffer release that was breaking explicit sync, the buffer
needs to exist until the surface commit event has been emitted and draw
calls added egl sync points, move to eventfd signaling instead of
stalling sync point checks, and recommit pending commits if waiting on a
signal. add a CDRMSyncPointState helper class. move a few weak pointers
to shared pointers so they dont destruct before we need to use them.
* syncobj: queue pending states for eventfd
eventfd requires us to queue pending stats until ready and then apply to
current, and also when no ready state exist commit the client commit on
the current existing buffer, if there is one.
* syncobj: clear current buffer damage
clear current buffer damage on current buffer commits.
* syncobj: cleanup code and fix hyprlock
remove unused code, and ensure we dont commit a empty texture causing
locksession protocol and gtk4-layer-shell misbehaving.
* syncobj: ensure buffers are cleaned up
ensure the containers having the various buffers actually gets cleaned
up from their containers, incase the CSignal isnt signaled because of
expired smart pointers or just wrong order destruction because mishaps.
also move the acquire/point setting to buffer attaching. instead of on
precommit.
* syncobj: remove unused code, optimize
remove unused code and merge sync fds if fence is valid, remove manual
directscanout buffer dropping that signals release point on pageflip, it
can cause us to signal the release point while still keeping the current
buffer and rendering it yet again causing wrong things.
* syncobj: delay buffer release on non syncobj
delay buffer releases on non syncobj surfaces until next commit, and
check on async buffers if syncobj and drop and signal the release point
on backend buffer release.
* syncobj: ensure we follow protocol
ensure we follow protocol by replacing acquire/release points if they
arrive late and replace already existing ones. also remove unneded
brackets, and dont try to manual lock/release buffers when it comes to
explicit protocol. it doesnt care about buffer releases only about
acquire and release points and signaling them.
* syncobj: lets not complicate things
set points in precommit, before checking protocol errors and we catch
any pending acquire/release points arriving late.
* syncobj: move SSurfaceState to types
remove destructor resource destroying, let resources destroys them on
their events, and move SSurfaceStates to types/SurfaceState.hpp
* syncobj: actually store the merged fd
have to actually store the mergedfd to use it.
* syncobj: cleanup a bit around fences
ensure the current asynchronous buffer is actually released on pageflip
not the previous. cleanup a bit FD handling in
commitPendingAndDoExplicitSync, and reuse the in fence when syncing
surfaces.
* syncobjs: ensure fence FD doesnt leak
calling resetexplicitfence without properly ensuring the FD is closed
before will leak it, store it per monitor and let it close itself with
the CFileDescriptor class.
* syncobj: ensure buffers are actually released
buffers were never being sent released properly.
* types: Defer buffer sync releaser until unlock
* syncobj: store directscanout fence in monitor
ensure the infence fd survives the scope of attemptdirectscanout so it
doesnt close before it should have.
* syncobj: check if if acquire is expired
we might hit a race to finish on exit where the timeline just has
destructed but the buffer waiter is still pending. and such we
removeAllWaiters null dereferences.
* syncobj: code style changes
remove quack comment, change to m_foo and use a std::vector and
weakpointer in the waiter for removal instead of a std::list.
* syncobj: remove unused async buffer drop
remove unused async buffer drop, only related to directscanout and is
handled elsewhere.
---------
Co-authored-by: Lee Bousfield <ljbousfield@gmail.com>
2025-03-14 15:08:20 +01:00
if ( pMonitor - > inFence . isValid ( ) ) {
2024-08-06 14:52:19 +01:00
Debug : : log ( TRACE , " Monitor state commit failed, retrying without a fence " ) ;
pMonitor - > output - > state - > resetExplicitFences ( ) ;
ok = pMonitor - > state . commit ( ) ;
2024-07-21 13:09:54 +02:00
}
2024-08-06 14:52:19 +01:00
if ( ! ok ) {
Debug : : log ( TRACE , " Monitor state commit failed " ) ;
// rollback the buffer to avoid writing to the front buffer that is being
// displayed
pMonitor - > output - > swapchain - > rollback ( ) ;
pMonitor - > damage . damageEntire ( ) ;
}
2024-07-21 13:09:54 +02:00
}
2025-02-13 12:09:25 +01:00
auto explicitOptions = getExplicitSyncSettings ( pMonitor - > output ) ;
2024-08-06 14:52:19 +01:00
if ( ! explicitOptions . explicitEnabled )
2024-07-21 13:09:54 +02:00
return ok ;
2024-08-06 14:52:19 +01:00
Debug : : log ( TRACE , " Explicit: {} presented " , explicitPresented . size ( ) ) ;
2025-03-20 11:39:55 +01:00
if ( ! pMonitor - > eglSync )
Debug : : log ( TRACE , " Explicit: can't add sync, monitor has no EGLSync " ) ;
2024-08-06 14:52:19 +01:00
else {
2024-08-26 20:24:30 +02:00
for ( auto const & e : explicitPresented ) {
2025-03-20 06:08:47 -05:00
if ( ! e - > current . buffer | | ! e - > current . buffer - > buffer - > syncReleaser )
2024-08-06 14:52:19 +01:00
continue ;
2025-03-20 11:39:55 +01:00
e - > current . buffer - > buffer - > syncReleaser - > addReleaseSync ( pMonitor - > eglSync ) ;
2024-08-06 14:52:19 +01:00
}
2024-07-21 13:09:54 +02:00
}
2024-08-06 14:52:19 +01:00
explicitPresented . clear ( ) ;
2024-07-21 13:09:54 +02:00
return ok ;
}
2024-10-19 23:03:29 +01:00
void CHyprRenderer : : renderWorkspace ( PHLMONITOR pMonitor , PHLWORKSPACE pWorkspace , timespec * now , const CBox & geometry ) {
2023-04-12 12:41:23 +01:00
Vector2D translate = { geometry . x , geometry . y } ;
float scale = ( float ) geometry . width / pMonitor - > vecPixelSize . x ;
2023-07-20 17:47:49 +02:00
TRACY_GPU_ZONE ( " RenderWorkspace " ) ;
2023-04-12 12:50:20 +01:00
if ( ! DELTALESSTHAN ( ( double ) geometry . width / ( double ) geometry . height , pMonitor - > vecPixelSize . x / pMonitor - > vecPixelSize . y , 0.01 ) ) {
2023-04-12 12:41:23 +01:00
Debug : : log ( ERR , " Ignoring geometry in renderWorkspace: aspect ratio mismatch " ) ;
scale = 1.f ;
translate = Vector2D { } ;
}
2023-04-12 13:05:57 +01:00
renderAllClientsForWorkspace ( pMonitor , pWorkspace , now , translate , scale ) ;
2023-04-12 12:41:23 +01:00
}
2024-10-19 23:03:29 +01:00
void CHyprRenderer : : sendFrameEventsToWorkspace ( PHLMONITOR pMonitor , PHLWORKSPACE pWorkspace , timespec * now ) {
2024-08-26 17:25:39 +02:00
for ( auto const & w : g_pCompositor - > m_vWindows ) {
2024-06-08 10:07:59 +02:00
if ( w - > isHidden ( ) | | ! w - > m_bIsMapped | | w - > m_bFadingOut | | ! w - > m_pWLSurface - > resource ( ) )
2024-04-23 16:08:54 +01:00
continue ;
2024-04-27 12:43:12 +01:00
if ( ! shouldRenderWindow ( w , pMonitor ) )
2024-04-23 16:08:54 +01:00
continue ;
2024-06-08 10:07:59 +02:00
w - > m_pWLSurface - > resource ( ) - > breadthfirst ( [ now ] ( SP < CWLSurfaceResource > r , const Vector2D & offset , void * d ) { r - > frame ( now ) ; } , nullptr ) ;
2024-04-23 16:08:54 +01:00
}
2024-08-26 17:25:39 +02:00
for ( auto const & lsl : pMonitor - > m_aLayerSurfaceLayers ) {
for ( auto const & ls : lsl ) {
2024-06-08 10:07:59 +02:00
if ( ls - > fadingOut | | ! ls - > surface - > resource ( ) )
2024-04-23 21:15:37 +01:00
continue ;
2024-06-08 10:07:59 +02:00
ls - > surface - > resource ( ) - > breadthfirst ( [ now ] ( SP < CWLSurfaceResource > r , const Vector2D & offset , void * d ) { r - > frame ( now ) ; } , nullptr ) ;
2024-04-23 16:08:54 +01:00
}
}
}
2024-10-26 02:06:13 +01:00
void CHyprRenderer : : setSurfaceScanoutMode ( SP < CWLSurfaceResource > surface , PHLMONITOR monitor ) {
2024-07-21 13:09:54 +02:00
if ( ! PROTO : : linuxDma )
return ;
2022-11-05 12:50:47 +00:00
2024-07-21 13:09:54 +02:00
PROTO : : linuxDma - > updateScanoutTranche ( surface , monitor ) ;
2022-11-05 12:50:47 +00:00
}
2022-03-21 17:00:17 +01:00
// taken from Sway.
// this is just too much of a spaghetti for me to understand
2024-07-22 23:36:58 +02:00
static void applyExclusive ( CBox & usableArea , uint32_t anchor , int32_t exclusive , uint32_t exclusiveEdge , int32_t marginTop , int32_t marginRight , int32_t marginBottom ,
int32_t marginLeft ) {
2022-03-21 17:00:17 +01:00
if ( exclusive < = 0 ) {
return ;
}
struct {
uint32_t singular_anchor ;
uint32_t anchor_triplet ;
2024-06-19 16:20:06 +02:00
double * positive_axis ;
double * negative_axis ;
2022-12-16 17:17:31 +00:00
int margin ;
2022-03-21 17:00:17 +01:00
} edges [ ] = {
// Top
{
. singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP ,
2022-12-16 17:17:31 +00:00
. anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP ,
2023-12-19 11:55:56 +00:00
. positive_axis = & usableArea . y ,
. negative_axis = & usableArea . height ,
. margin = marginTop ,
2022-03-21 17:00:17 +01:00
} ,
// Bottom
{
. singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM ,
2022-12-16 17:17:31 +00:00
. anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM ,
2024-12-07 18:51:18 +01:00
. positive_axis = nullptr ,
2023-12-19 11:55:56 +00:00
. negative_axis = & usableArea . height ,
. margin = marginBottom ,
2022-03-21 17:00:17 +01:00
} ,
// Left
{
. singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT ,
2022-12-16 17:17:31 +00:00
. anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM ,
2023-12-19 11:55:56 +00:00
. positive_axis = & usableArea . x ,
. negative_axis = & usableArea . width ,
. margin = marginLeft ,
2022-03-21 17:00:17 +01:00
} ,
// Right
{
. singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT ,
2022-12-16 17:17:31 +00:00
. anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM ,
2024-12-07 18:51:18 +01:00
. positive_axis = nullptr ,
2023-12-19 11:55:56 +00:00
. negative_axis = & usableArea . width ,
. margin = marginRight ,
2022-03-21 17:00:17 +01:00
} ,
} ;
for ( size_t i = 0 ; i < sizeof ( edges ) / sizeof ( edges [ 0 ] ) ; + + i ) {
2024-07-22 23:36:58 +02:00
if ( ( exclusiveEdge = = edges [ i ] . singular_anchor | | anchor = = edges [ i ] . singular_anchor | | anchor = = edges [ i ] . anchor_triplet ) & & exclusive + edges [ i ] . margin > 0 ) {
2022-03-21 17:00:17 +01:00
if ( edges [ i ] . positive_axis ) {
* edges [ i ] . positive_axis + = exclusive + edges [ i ] . margin ;
}
if ( edges [ i ] . negative_axis ) {
* edges [ i ] . negative_axis - = exclusive + edges [ i ] . margin ;
}
break ;
}
}
}
2022-03-19 14:37:40 +01:00
2024-10-19 23:03:29 +01:00
void CHyprRenderer : : arrangeLayerArray ( PHLMONITOR pMonitor , const std : : vector < PHLLSREF > & layerSurfaces , bool exclusiveZone , CBox * usableArea ) {
2023-11-04 17:03:05 +00:00
CBox full_area = { pMonitor - > vecPosition . x , pMonitor - > vecPosition . y , pMonitor - > vecSize . x , pMonitor - > vecSize . y } ;
2022-03-19 14:37:40 +01:00
2024-08-26 20:24:30 +02:00
for ( auto const & ls : layerSurfaces ) {
2024-07-21 13:09:54 +02:00
if ( ! ls | | ls - > fadingOut | | ls - > readyToDelete | | ! ls - > layerSurface | | ls - > noProcess )
2022-05-14 17:23:46 +02:00
continue ;
2022-03-21 17:00:17 +01:00
const auto PLAYER = ls - > layerSurface ;
const auto PSTATE = & PLAYER - > current ;
2024-05-09 21:47:21 +01:00
if ( exclusiveZone ! = ( PSTATE - > exclusive > 0 ) )
2022-03-21 17:00:17 +01:00
continue ;
2022-03-19 14:37:40 +01:00
2023-11-04 17:03:05 +00:00
CBox bounds ;
2024-05-09 21:47:21 +01:00
if ( PSTATE - > exclusive = = - 1 )
2022-03-21 17:00:17 +01:00
bounds = full_area ;
2023-10-06 14:00:05 +01:00
else
2022-03-21 17:00:17 +01:00
bounds = * usableArea ;
2022-03-19 14:37:40 +01:00
2023-10-06 14:00:05 +01:00
const Vector2D OLDSIZE = { ls - > geometry . width , ls - > geometry . height } ;
2024-05-09 21:47:21 +01:00
CBox box = { { } , PSTATE - > desiredSize } ;
2022-03-21 17:00:17 +01:00
// Horizontal axis
const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT ;
2024-10-19 23:47:28 +01:00
if ( box . width = = 0 )
2022-03-21 17:00:17 +01:00
box . x = bounds . x ;
2024-10-19 23:47:28 +01:00
else if ( ( PSTATE - > anchor & both_horiz ) = = both_horiz )
2022-03-21 17:00:17 +01:00
box . x = bounds . x + ( ( bounds . width / 2 ) - ( box . width / 2 ) ) ;
2024-10-19 23:47:28 +01:00
else if ( ( PSTATE - > anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT ) )
2022-03-21 17:00:17 +01:00
box . x = bounds . x ;
2024-10-19 23:47:28 +01:00
else if ( ( PSTATE - > anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT ) )
2022-03-21 17:00:17 +01:00
box . x = bounds . x + ( bounds . width - box . width ) ;
2024-10-19 23:47:28 +01:00
else
2022-03-21 17:00:17 +01:00
box . x = bounds . x + ( ( bounds . width / 2 ) - ( box . width / 2 ) ) ;
2024-10-19 23:47:28 +01:00
2022-03-21 17:00:17 +01:00
// Vertical axis
const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM ;
2024-10-19 23:47:28 +01:00
if ( box . height = = 0 )
2022-03-21 17:00:17 +01:00
box . y = bounds . y ;
2024-10-19 23:47:28 +01:00
else if ( ( PSTATE - > anchor & both_vert ) = = both_vert )
2022-03-21 17:00:17 +01:00
box . y = bounds . y + ( ( bounds . height / 2 ) - ( box . height / 2 ) ) ;
2024-10-19 23:47:28 +01:00
else if ( ( PSTATE - > anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP ) )
2022-03-21 17:00:17 +01:00
box . y = bounds . y ;
2024-10-19 23:47:28 +01:00
else if ( ( PSTATE - > anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM ) )
2022-03-21 17:00:17 +01:00
box . y = bounds . y + ( bounds . height - box . height ) ;
2024-10-19 23:47:28 +01:00
else
2022-03-21 17:00:17 +01:00
box . y = bounds . y + ( ( bounds . height / 2 ) - ( box . height / 2 ) ) ;
2024-10-19 23:47:28 +01:00
2022-03-21 17:00:17 +01:00
// Margin
if ( box . width = = 0 ) {
box . x + = PSTATE - > margin . left ;
2022-12-16 17:17:31 +00:00
box . width = bounds . width - ( PSTATE - > margin . left + PSTATE - > margin . right ) ;
2024-10-19 23:47:28 +01:00
} else if ( ( PSTATE - > anchor & both_horiz ) = = both_horiz )
; // don't apply margins
else if ( ( PSTATE - > anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT ) )
2022-03-21 17:00:17 +01:00
box . x + = PSTATE - > margin . left ;
2024-10-19 23:47:28 +01:00
else if ( ( PSTATE - > anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT ) )
2022-03-21 17:00:17 +01:00
box . x - = PSTATE - > margin . right ;
2024-10-19 23:47:28 +01:00
2022-03-21 17:00:17 +01:00
if ( box . height = = 0 ) {
box . y + = PSTATE - > margin . top ;
2022-12-16 17:17:31 +00:00
box . height = bounds . height - ( PSTATE - > margin . top + PSTATE - > margin . bottom ) ;
2024-10-19 23:47:28 +01:00
} else if ( ( PSTATE - > anchor & both_vert ) = = both_vert )
; // don't apply margins
else if ( ( PSTATE - > anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP ) )
2022-03-21 17:00:17 +01:00
box . y + = PSTATE - > margin . top ;
2024-10-19 23:47:28 +01:00
else if ( ( PSTATE - > anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM ) )
2022-03-21 17:00:17 +01:00
box . y - = PSTATE - > margin . bottom ;
2024-10-19 23:47:28 +01:00
2022-03-21 17:00:17 +01:00
if ( box . width < = 0 | | box . height < = 0 ) {
2023-09-06 12:51:36 +02:00
Debug : : log ( ERR , " LayerSurface {:x} has a negative/zero w/h??? " , ( uintptr_t ) ls . get ( ) ) ;
2022-03-21 17:00:17 +01:00
continue ;
2022-03-20 14:52:23 +01:00
}
2024-10-19 23:45:51 +01:00
box . round ( ) ; // fix rounding errors
2022-03-21 17:00:17 +01:00
ls - > geometry = box ;
2022-03-20 14:52:23 +01:00
2024-07-22 23:36:58 +02:00
applyExclusive ( * usableArea , PSTATE - > anchor , PSTATE - > exclusive , PSTATE - > exclusiveEdge , PSTATE - > margin . top , PSTATE - > margin . right , PSTATE - > margin . bottom , PSTATE - > margin . left ) ;
2022-03-19 14:37:40 +01:00
2023-10-06 14:00:05 +01:00
if ( Vector2D { box . width , box . height } ! = OLDSIZE )
2024-05-09 21:47:21 +01:00
ls - > layerSurface - > configure ( box . size ( ) ) ;
2024-03-27 16:30:08 +00:00
2025-01-07 17:55:14 +00:00
* ls - > realPosition = box . pos ( ) ;
* ls - > realSize = box . size ( ) ;
2022-03-19 14:37:40 +01:00
}
}
2024-08-08 21:01:50 +02:00
void CHyprRenderer : : arrangeLayersForMonitor ( const MONITORID & monitor ) {
2024-10-08 21:58:40 +01:00
const auto PMONITOR = g_pCompositor - > getMonitorFromID ( monitor ) ;
static auto BAR_POSITION = CConfigValue < Hyprlang : : INT > ( " debug:error_position " ) ;
2022-03-19 14:37:40 +01:00
if ( ! PMONITOR )
return ;
// Reset the reserved
2022-12-16 17:17:31 +00:00
PMONITOR - > vecReservedBottomRight = Vector2D ( ) ;
PMONITOR - > vecReservedTopLeft = Vector2D ( ) ;
2022-03-19 14:37:40 +01:00
2023-11-04 17:03:05 +00:00
CBox usableArea = { PMONITOR - > vecPosition . x , PMONITOR - > vecPosition . y , PMONITOR - > vecSize . x , PMONITOR - > vecSize . y } ;
2022-03-21 17:00:17 +01:00
2024-10-08 21:20:25 +01:00
if ( g_pHyprError - > active ( ) & & g_pCompositor - > m_pLastMonitor = = PMONITOR - > self ) {
2024-10-08 21:58:40 +01:00
const auto HEIGHT = g_pHyprError - > height ( ) ;
if ( * BAR_POSITION = = 0 ) {
PMONITOR - > vecReservedTopLeft . y = HEIGHT ;
usableArea . y + = HEIGHT ;
usableArea . h - = HEIGHT ;
} else {
PMONITOR - > vecReservedBottomRight . y = HEIGHT ;
usableArea . h - = HEIGHT ;
}
2024-10-08 21:20:25 +01:00
}
2024-09-09 11:10:08 +02:00
for ( auto & la : PMONITOR - > m_aLayerSurfaceLayers ) {
std : : stable_sort ( la . begin ( ) , la . end ( ) , [ ] ( const PHLLSREF & a , const PHLLSREF & b ) { return a - > order > b - > order ; } ) ;
}
2024-08-26 17:25:39 +02:00
for ( auto const & la : PMONITOR - > m_aLayerSurfaceLayers )
2022-03-21 17:00:17 +01:00
arrangeLayerArray ( PMONITOR , la , true , & usableArea ) ;
2024-08-26 17:25:39 +02:00
for ( auto const & la : PMONITOR - > m_aLayerSurfaceLayers )
2022-03-21 17:00:17 +01:00
arrangeLayerArray ( PMONITOR , la , false , & usableArea ) ;
2022-12-16 17:17:31 +00:00
PMONITOR - > vecReservedTopLeft = Vector2D ( usableArea . x , usableArea . y ) - PMONITOR - > vecPosition ;
2022-03-21 17:00:17 +01:00
PMONITOR - > vecReservedBottomRight = PMONITOR - > vecSize - Vector2D ( usableArea . width , usableArea . height ) - PMONITOR - > vecReservedTopLeft ;
2022-10-17 23:23:01 +01:00
auto ADDITIONALRESERVED = g_pConfigManager - > m_mAdditionalReservedAreas . find ( PMONITOR - > szName ) ;
if ( ADDITIONALRESERVED = = g_pConfigManager - > m_mAdditionalReservedAreas . end ( ) ) {
2022-12-16 17:17:31 +00:00
ADDITIONALRESERVED = g_pConfigManager - > m_mAdditionalReservedAreas . find ( " " ) ; // glob wildcard
2022-10-17 23:23:01 +01:00
}
if ( ADDITIONALRESERVED ! = g_pConfigManager - > m_mAdditionalReservedAreas . end ( ) ) {
2022-12-16 17:17:31 +00:00
PMONITOR - > vecReservedTopLeft = PMONITOR - > vecReservedTopLeft + Vector2D ( ADDITIONALRESERVED - > second . left , ADDITIONALRESERVED - > second . top ) ;
2022-10-17 23:23:01 +01:00
PMONITOR - > vecReservedBottomRight = PMONITOR - > vecReservedBottomRight + Vector2D ( ADDITIONALRESERVED - > second . right , ADDITIONALRESERVED - > second . bottom ) ;
}
2022-04-27 17:46:07 +02:00
2022-05-04 15:23:30 +02:00
// damage the monitor if can
2023-04-07 12:18:40 +01:00
damageMonitor ( PMONITOR ) ;
2022-05-04 15:23:30 +02:00
2022-09-08 14:11:32 +02:00
g_pLayoutManager - > getCurrentLayout ( ) - > recalculateMonitor ( monitor ) ;
2022-03-19 16:13:19 +01:00
}
2024-06-08 10:07:59 +02:00
void CHyprRenderer : : damageSurface ( SP < CWLSurfaceResource > pSurface , double x , double y , double scale ) {
2022-04-14 16:43:29 +02:00
if ( ! pSurface )
return ; // wut?
2022-03-21 16:13:43 +01:00
2022-08-10 23:14:53 +02:00
if ( g_pCompositor - > m_bUnsafeState )
return ;
2024-06-08 10:07:59 +02:00
const auto WLSURF = CWLSurface : : fromResource ( pSurface ) ;
2024-07-22 12:37:54 +02:00
CRegion damageBox = WLSURF ? WLSURF - > computeDamage ( ) : CRegion { } ;
2024-02-19 11:34:55 +00:00
if ( ! WLSURF ) {
2024-02-19 11:24:54 +00:00
Debug : : log ( ERR , " BUG THIS: No CWLSurface for surface in damageSurface!!! " ) ;
2024-06-08 10:07:59 +02:00
return ;
2024-02-19 11:34:55 +00:00
}
2024-04-08 15:33:02 +01:00
2023-06-11 21:52:13 +02:00
if ( scale ! = 1.0 )
2024-02-19 11:24:54 +00:00
damageBox . scale ( scale ) ;
2022-03-21 16:13:43 +01:00
2022-07-04 17:55:33 +02:00
// schedule frame events
2024-07-21 13:09:54 +02:00
g_pCompositor - > scheduleFrameForMonitor ( g_pCompositor - > getMonitorFromVector ( Vector2D ( x , y ) ) , Aquamarine : : IOutput : : AQ_SCHEDULE_DAMAGE ) ;
2022-07-04 17:55:33 +02:00
2023-07-19 20:09:49 +02:00
if ( damageBox . empty ( ) )
2022-06-28 15:30:46 +02:00
return ;
2024-02-19 11:24:54 +00:00
damageBox . translate ( { x , y } ) ;
2023-07-19 20:09:49 +02:00
CRegion damageBoxForEach ;
2022-07-28 22:15:56 +02:00
2024-08-26 20:24:30 +02:00
for ( auto const & m : g_pCompositor - > m_vMonitors ) {
2023-03-16 16:32:03 +00:00
if ( ! m - > output )
continue ;
2024-02-19 11:24:54 +00:00
damageBoxForEach . set ( damageBox ) ;
damageBoxForEach . translate ( { - m - > vecPosition . x , - m - > vecPosition . y } ) . scale ( m - > scale ) ;
2022-07-28 22:15:56 +02:00
2025-01-26 15:05:34 +00:00
m - > addDamage ( damageBoxForEach ) ;
2022-03-21 16:13:43 +01:00
}
2024-03-03 18:39:20 +00:00
static auto PLOGDAMAGE = CConfigValue < Hyprlang : : INT > ( " debug:log_damage " ) ;
2022-06-07 20:41:40 +02:00
2024-03-03 18:39:20 +00:00
if ( * PLOGDAMAGE )
2023-09-06 12:51:36 +02:00
Debug : : log ( LOG , " Damage: Surface (extents): xy: {}, {} wh: {}, {} " , damageBox . pixman ( ) - > extents . x1 , damageBox . pixman ( ) - > extents . y1 ,
2023-07-19 20:09:49 +02:00
damageBox . pixman ( ) - > extents . x2 - damageBox . pixman ( ) - > extents . x1 , damageBox . pixman ( ) - > extents . y2 - damageBox . pixman ( ) - > extents . y1 ) ;
2022-04-14 16:43:29 +02:00
}
2022-03-21 16:13:43 +01:00
2024-04-27 12:43:12 +01:00
void CHyprRenderer : : damageWindow ( PHLWINDOW pWindow , bool forceFull ) {
2022-08-10 23:14:53 +02:00
if ( g_pCompositor - > m_bUnsafeState )
return ;
2024-03-30 18:14:26 -07:00
CBox windowBox = pWindow - > getFullWindowBoundingBox ( ) ;
2024-04-02 20:32:39 +01:00
const auto PWINDOWWORKSPACE = pWindow - > m_pWorkspace ;
2025-01-07 17:55:14 +00:00
if ( PWINDOWWORKSPACE & & PWINDOWWORKSPACE - > m_vRenderOffset - > isBeingAnimated ( ) & & ! pWindow - > m_bPinned )
windowBox . translate ( PWINDOWWORKSPACE - > m_vRenderOffset - > value ( ) ) ;
2024-03-30 18:14:26 -07:00
windowBox . translate ( pWindow - > m_vFloatingOffset ) ;
2024-08-26 20:24:30 +02:00
for ( auto const & m : g_pCompositor - > m_vMonitors ) {
2024-12-22 17:12:09 +01:00
if ( forceFull | | shouldRenderWindow ( pWindow , m ) ) { // only damage if window is rendered on monitor
2024-03-30 18:14:26 -07:00
CBox fixedDamageBox = { windowBox . x - m - > vecPosition . x , windowBox . y - m - > vecPosition . y , windowBox . width , windowBox . height } ;
fixedDamageBox . scale ( m - > scale ) ;
2025-01-26 15:05:34 +00:00
m - > addDamage ( fixedDamageBox ) ;
2024-03-30 18:14:26 -07:00
}
2022-06-29 11:21:42 +02:00
}
2022-05-05 15:09:26 +02:00
2024-08-26 20:24:30 +02:00
for ( auto const & wd : pWindow - > m_dWindowDecorations )
2023-06-27 13:23:53 +02:00
wd - > damageEntire ( ) ;
2024-03-03 18:39:20 +00:00
static auto PLOGDAMAGE = CConfigValue < Hyprlang : : INT > ( " debug:log_damage " ) ;
2022-06-07 20:41:40 +02:00
2024-03-03 18:39:20 +00:00
if ( * PLOGDAMAGE )
2024-03-30 18:14:26 -07:00
Debug : : log ( LOG , " Damage: Window ({}): xy: {}, {} wh: {}, {} " , pWindow - > m_szTitle , windowBox . x , windowBox . y , windowBox . width , windowBox . height ) ;
2022-03-21 16:13:43 +01:00
}
2024-10-19 23:03:29 +01:00
void CHyprRenderer : : damageMonitor ( PHLMONITOR pMonitor ) {
2022-09-13 15:25:42 +02:00
if ( g_pCompositor - > m_bUnsafeState | | pMonitor - > isMirror ( ) )
2022-08-10 23:14:53 +02:00
return ;
2023-11-04 17:03:05 +00:00
CBox damageBox = { 0 , 0 , INT16_MAX , INT16_MAX } ;
2025-01-26 15:05:34 +00:00
pMonitor - > addDamage ( damageBox ) ;
2022-05-05 15:09:26 +02:00
2024-03-03 18:39:20 +00:00
static auto PLOGDAMAGE = CConfigValue < Hyprlang : : INT > ( " debug:log_damage " ) ;
2022-06-07 20:41:40 +02:00
2024-03-03 18:39:20 +00:00
if ( * PLOGDAMAGE )
2023-09-06 21:45:37 +02:00
Debug : : log ( LOG , " Damage: Monitor {} " , pMonitor - > szName ) ;
2022-04-14 16:43:29 +02:00
}
2022-03-21 16:13:43 +01:00
2025-01-26 15:05:34 +00:00
void CHyprRenderer : : damageBox ( const CBox & box , bool skipFrameSchedule ) {
2022-08-10 23:14:53 +02:00
if ( g_pCompositor - > m_bUnsafeState )
return ;
2024-08-26 20:24:30 +02:00
for ( auto const & m : g_pCompositor - > m_vMonitors ) {
2022-09-13 15:25:42 +02:00
if ( m - > isMirror ( ) )
continue ; // don't damage mirrors traditionally
2024-10-31 00:20:32 +01:00
if ( ! skipFrameSchedule ) {
2025-01-26 15:05:34 +00:00
CBox damageBox = box . copy ( ) . translate ( - m - > vecPosition ) . scale ( m - > scale ) ;
m - > addDamage ( damageBox ) ;
2024-10-31 00:20:32 +01:00
}
2022-04-17 18:47:10 +02:00
}
2022-05-05 15:09:26 +02:00
2024-03-03 18:39:20 +00:00
static auto PLOGDAMAGE = CConfigValue < Hyprlang : : INT > ( " debug:log_damage " ) ;
2022-06-07 20:41:40 +02:00
2024-03-03 18:39:20 +00:00
if ( * PLOGDAMAGE )
2025-01-26 15:05:34 +00:00
Debug : : log ( LOG , " Damage: Box: xy: {}, {} wh: {}, {} " , box . x , box . y , box . w , box . h ) ;
2022-03-31 17:25:23 +02:00
}
2022-05-05 14:02:30 +02:00
void CHyprRenderer : : damageBox ( const int & x , const int & y , const int & w , const int & h ) {
2023-11-04 17:03:05 +00:00
CBox box = { x , y , w , h } ;
2025-01-26 15:05:34 +00:00
damageBox ( box ) ;
2022-05-05 14:02:30 +02:00
}
2023-07-19 20:09:49 +02:00
void CHyprRenderer : : damageRegion ( const CRegion & rg ) {
2024-08-26 17:25:39 +02:00
for ( auto const & RECT : rg . getRects ( ) ) {
2022-07-16 12:44:45 +02:00
damageBox ( RECT . x1 , RECT . y1 , RECT . x2 - RECT . x1 , RECT . y2 - RECT . y1 ) ;
}
}
2024-10-19 23:03:29 +01:00
void CHyprRenderer : : damageMirrorsWith ( PHLMONITOR pMonitor , const CRegion & pRegion ) {
2024-08-26 20:24:30 +02:00
for ( auto const & mirror : pMonitor - > mirrors ) {
2022-09-13 15:25:42 +02:00
2024-04-24 17:29:41 +02:00
// transform the damage here, so it won't get clipped by the monitor damage ring
2024-12-07 18:51:18 +01:00
auto monitor = mirror ;
2024-04-24 17:29:41 +02:00
CRegion transformed { pRegion } ;
// we want to transform to the same box as in CHyprOpenGLImpl::renderMirrored
2024-12-07 18:51:18 +01:00
double scale = std : : min ( monitor - > vecTransformedSize . x / pMonitor - > vecTransformedSize . x , monitor - > vecTransformedSize . y / pMonitor - > vecTransformedSize . y ) ;
CBox monbox = { 0 , 0 , pMonitor - > vecTransformedSize . x * scale , pMonitor - > vecTransformedSize . y * scale } ;
2024-04-24 17:29:41 +02:00
monbox . x = ( monitor - > vecTransformedSize . x - monbox . w ) / 2 ;
monbox . y = ( monitor - > vecTransformedSize . y - monbox . h ) / 2 ;
2024-07-21 13:09:54 +02:00
transformed . scale ( scale ) ;
2024-12-07 18:51:18 +01:00
transformed . transform ( wlTransformToHyprutils ( pMonitor - > transform ) , pMonitor - > vecPixelSize . x * scale , pMonitor - > vecPixelSize . y * scale ) ;
2024-04-24 17:29:41 +02:00
transformed . translate ( Vector2D ( monbox . x , monbox . y ) ) ;
2025-01-26 15:05:34 +00:00
mirror - > addDamage ( transformed ) ;
2023-03-04 00:48:02 +00:00
2024-10-19 23:03:29 +01:00
g_pCompositor - > scheduleFrameForMonitor ( mirror . lock ( ) , Aquamarine : : IOutput : : AQ_SCHEDULE_DAMAGE ) ;
2022-09-13 15:25:42 +02:00
}
}
2024-10-19 23:03:29 +01:00
void CHyprRenderer : : renderDragIcon ( PHLMONITOR pMonitor , timespec * time ) {
2024-05-11 17:13:20 +01:00
PROTO : : data - > renderDND ( pMonitor , time ) ;
2022-04-14 16:43:29 +02:00
}
2024-06-08 10:07:59 +02:00
void CHyprRenderer : : setCursorSurface ( SP < CWLSurface > surf , int hotspotX , int hotspotY , bool force ) {
2023-09-29 17:51:07 +01:00
m_bCursorHasSurface = surf ;
2023-12-18 16:06:06 +00:00
m_sLastCursorData . name = " " ;
m_sLastCursorData . surf = surf ;
m_sLastCursorData . hotspotX = hotspotX ;
m_sLastCursorData . hotspotY = hotspotY ;
2023-10-29 18:09:05 +00:00
2024-02-21 13:48:48 +00:00
if ( m_bCursorHidden & & ! force )
return ;
2024-05-05 22:18:10 +01:00
g_pCursorManager - > setCursorSurface ( surf , { hotspotX , hotspotY } ) ;
2023-09-29 17:51:07 +01:00
}
2023-12-20 13:00:27 +01:00
2023-12-20 21:40:44 +01:00
void CHyprRenderer : : setCursorFromName ( const std : : string & name , bool force ) {
2023-09-29 17:51:07 +01:00
m_bCursorHasSurface = true ;
2024-02-21 13:48:48 +00:00
if ( name = = m_sLastCursorData . name & & ! force )
2023-10-29 18:09:05 +00:00
return ;
m_sLastCursorData . name = name ;
2023-10-30 00:18:40 +00:00
m_sLastCursorData . surf . reset ( ) ;
2023-10-29 18:09:05 +00:00
2024-02-21 13:48:48 +00:00
if ( m_bCursorHidden & & ! force )
return ;
2024-03-09 16:52:59 +00:00
g_pCursorManager - > setCursorFromName ( name ) ;
2023-09-29 17:51:07 +01:00
}
2022-06-24 23:27:02 +02:00
void CHyprRenderer : : ensureCursorRenderingMode ( ) {
2024-08-10 13:42:45 -07:00
static auto PCURSORTIMEOUT = CConfigValue < Hyprlang : : FLOAT > ( " cursor:inactive_timeout " ) ;
2024-05-09 22:23:01 +01:00
static auto PHIDEONTOUCH = CConfigValue < Hyprlang : : INT > ( " cursor:hide_on_touch " ) ;
static auto PHIDEONKEY = CConfigValue < Hyprlang : : INT > ( " cursor:hide_on_key_press " ) ;
2022-06-24 23:27:02 +02:00
2024-03-28 02:04:30 +00:00
if ( * PCURSORTIMEOUT < = 0 )
m_sCursorHiddenConditions . hiddenOnTimeout = false ;
if ( * PHIDEONTOUCH = = 0 )
m_sCursorHiddenConditions . hiddenOnTouch = false ;
if ( * PHIDEONKEY = = 0 )
m_sCursorHiddenConditions . hiddenOnKeyboard = false ;
2023-01-17 11:47:39 +01:00
2024-03-28 02:04:30 +00:00
if ( * PCURSORTIMEOUT > 0 )
m_sCursorHiddenConditions . hiddenOnTimeout = * PCURSORTIMEOUT < g_pInputManager - > m_tmrLastCursorMovement . getSeconds ( ) ;
2022-06-24 23:27:02 +02:00
2024-03-28 02:04:30 +00:00
const bool HIDE = m_sCursorHiddenConditions . hiddenOnTimeout | | m_sCursorHiddenConditions . hiddenOnTouch | | m_sCursorHiddenConditions . hiddenOnKeyboard ;
2022-06-24 23:27:02 +02:00
2024-03-28 02:04:30 +00:00
if ( HIDE = = m_bCursorHidden )
return ;
2023-12-20 21:40:44 +01:00
2024-03-28 02:04:30 +00:00
if ( HIDE ) {
Debug : : log ( LOG , " Hiding the cursor (hl-mandated) " ) ;
2022-06-24 23:27:02 +02:00
2024-08-26 20:24:30 +02:00
for ( auto const & m : g_pCompositor - > m_vMonitors ) {
2024-07-21 13:09:54 +02:00
if ( ! g_pPointerManager - > softwareLockedFor ( m ) )
2024-03-28 02:04:30 +00:00
continue ;
2023-12-20 21:40:44 +01:00
2024-12-22 17:12:09 +01:00
damageMonitor ( m ) ; // TODO: maybe just damage the cursor area?
2022-06-24 23:27:02 +02:00
}
2024-03-28 02:04:30 +00:00
setCursorHidden ( true ) ;
2022-06-24 23:27:02 +02:00
} else {
2024-03-28 02:04:30 +00:00
Debug : : log ( LOG , " Showing the cursor (hl-mandated) " ) ;
2024-08-26 20:24:30 +02:00
for ( auto const & m : g_pCompositor - > m_vMonitors ) {
2024-07-21 13:09:54 +02:00
if ( ! g_pPointerManager - > softwareLockedFor ( m ) )
2024-03-28 02:04:30 +00:00
continue ;
2024-12-22 17:12:09 +01:00
damageMonitor ( m ) ; // TODO: maybe just damage the cursor area?
2024-03-28 02:04:30 +00:00
}
2023-12-20 21:40:44 +01:00
setCursorHidden ( false ) ;
2022-06-24 23:27:02 +02:00
}
2022-06-26 13:43:32 +02:00
}
2023-12-20 21:40:44 +01:00
void CHyprRenderer : : setCursorHidden ( bool hide ) {
if ( hide = = m_bCursorHidden )
return ;
m_bCursorHidden = hide ;
if ( hide ) {
2024-05-05 22:18:10 +01:00
g_pPointerManager - > resetCursorImage ( ) ;
2023-12-20 21:40:44 +01:00
return ;
}
if ( m_sLastCursorData . surf . has_value ( ) )
setCursorSurface ( m_sLastCursorData . surf . value ( ) , m_sLastCursorData . hotspotX , m_sLastCursorData . hotspotY , true ) ;
else if ( ! m_sLastCursorData . name . empty ( ) )
setCursorFromName ( m_sLastCursorData . name , true ) ;
else
setCursorFromName ( " left_ptr " , true ) ;
}
2022-06-26 13:43:32 +02:00
bool CHyprRenderer : : shouldRenderCursor ( ) {
2023-12-29 00:04:01 +01:00
return ! m_bCursorHidden & & m_bCursorHasSurface ;
2022-07-11 23:38:10 +02:00
}
2023-03-24 19:23:16 +00:00
2024-10-19 23:03:29 +01:00
std : : tuple < float , float , float > CHyprRenderer : : getRenderTimes ( PHLMONITOR pMonitor ) {
2023-03-24 19:23:16 +00:00
const auto POVERLAY = & g_pDebugOverlay - > m_mMonitorOverlays [ pMonitor ] ;
float avgRenderTime = 0 ;
float maxRenderTime = 0 ;
float minRenderTime = 9999 ;
2024-08-26 17:25:39 +02:00
for ( auto const & rt : POVERLAY - > m_dLastRenderTimes ) {
2023-03-24 19:23:16 +00:00
if ( rt > maxRenderTime )
maxRenderTime = rt ;
if ( rt < minRenderTime )
minRenderTime = rt ;
avgRenderTime + = rt ;
}
avgRenderTime / = POVERLAY - > m_dLastRenderTimes . size ( ) = = 0 ? 1 : POVERLAY - > m_dLastRenderTimes . size ( ) ;
return std : : make_tuple < > ( avgRenderTime , maxRenderTime , minRenderTime ) ;
}
2023-04-04 14:49:58 +01:00
static int handleCrashLoop ( void * data ) {
2024-12-03 18:58:24 +00:00
g_pHyprNotificationOverlay - > addNotification ( " Hyprland will crash in " + std : : to_string ( 10 - ( int ) ( g_pHyprRenderer - > m_fCrashingDistort * 2.f ) ) + " s. " , CHyprColor ( 0 ) , 5000 ,
2023-04-04 14:49:58 +01:00
ICON_INFO ) ;
g_pHyprRenderer - > m_fCrashingDistort + = 0.5f ;
if ( g_pHyprRenderer - > m_fCrashingDistort > = 5.5f )
2023-08-31 20:52:02 +00:00
raise ( SIGABRT ) ;
2023-04-04 14:49:58 +01:00
wl_event_source_timer_update ( g_pHyprRenderer - > m_pCrashingLoop , 1000 ) ;
return 1 ;
}
void CHyprRenderer : : initiateManualCrash ( ) {
2024-12-03 18:58:24 +00:00
g_pHyprNotificationOverlay - > addNotification ( " Manual crash initiated. Farewell... " , CHyprColor ( 0 ) , 5000 , ICON_INFO ) ;
2023-04-04 14:49:58 +01:00
m_pCrashingLoop = wl_event_loop_add_timer ( g_pCompositor - > m_sWLEventLoop , handleCrashLoop , nullptr ) ;
wl_event_source_timer_update ( m_pCrashingLoop , 1000 ) ;
m_bCrashingInProgress = true ;
m_fCrashingDistort = 0.5 ;
2023-04-04 22:04:32 +01:00
g_pHyprOpenGL - > m_tGlobalTimer . reset ( ) ;
2024-03-03 18:39:20 +00:00
static auto PDT = ( Hyprlang : : INT * const * ) ( g_pConfigManager - > getConfigValuePtr ( " debug:damage_tracking " ) ) ;
2024-02-18 15:00:34 +00:00
* * PDT = 0 ;
2023-04-14 15:16:43 +01:00
}
2023-07-19 16:13:55 +02:00
2024-10-19 23:03:29 +01:00
void CHyprRenderer : : recheckSolitaryForMonitor ( PHLMONITOR pMonitor ) {
2024-04-27 12:43:12 +01:00
pMonitor - > solitaryClient . reset ( ) ; // reset it, if we find one it will be set.
2023-09-28 21:48:33 +01:00
2024-05-06 02:24:11 +01:00
if ( g_pHyprNotificationOverlay - > hasAny ( ) | | g_pSessionLockManager - > isSessionLocked ( ) )
2024-02-14 11:09:18 +00:00
return ;
2024-04-02 20:32:39 +01:00
const auto PWORKSPACE = pMonitor - > activeWorkspace ;
2023-09-28 21:48:33 +01:00
2025-01-07 17:55:14 +00:00
if ( ! PWORKSPACE | | ! PWORKSPACE - > m_bHasFullscreenWindow | | PROTO : : data - > dndActive ( ) | | pMonitor - > activeSpecialWorkspace | | PWORKSPACE - > m_fAlpha - > value ( ) ! = 1.f | |
PWORKSPACE - > m_vRenderOffset - > value ( ) ! = Vector2D { } )
2023-09-28 21:48:33 +01:00
return ;
2024-11-22 16:01:02 +00:00
const auto PCANDIDATE = PWORKSPACE - > getFullscreenWindow ( ) ;
2023-09-28 21:48:33 +01:00
if ( ! PCANDIDATE )
return ; // ????
if ( ! PCANDIDATE - > opaque ( ) )
return ;
2025-01-07 17:55:14 +00:00
if ( PCANDIDATE - > m_vRealSize - > value ( ) ! = pMonitor - > vecSize | | PCANDIDATE - > m_vRealPosition - > value ( ) ! = pMonitor - > vecPosition | | PCANDIDATE - > m_vRealPosition - > isBeingAnimated ( ) | |
PCANDIDATE - > m_vRealSize - > isBeingAnimated ( ) )
2023-09-28 21:48:33 +01:00
return ;
if ( ! pMonitor - > m_aLayerSurfaceLayers [ ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY ] . empty ( ) )
return ;
2024-08-26 17:25:39 +02:00
for ( auto const & topls : pMonitor - > m_aLayerSurfaceLayers [ ZWLR_LAYER_SHELL_V1_LAYER_TOP ] ) {
2025-01-07 17:55:14 +00:00
if ( topls - > alpha - > value ( ) ! = 0.f )
2023-09-28 21:48:33 +01:00
return ;
}
2024-08-26 17:25:39 +02:00
for ( auto const & w : g_pCompositor - > m_vWindows ) {
2024-04-27 12:43:12 +01:00
if ( w = = PCANDIDATE | | ( ! w - > m_bIsMapped & & ! w - > m_bFadingOut ) | | w - > isHidden ( ) )
2024-03-14 18:25:28 +00:00
continue ;
2024-04-02 20:32:39 +01:00
if ( w - > m_pWorkspace = = PCANDIDATE - > m_pWorkspace & & w - > m_bIsFloating & & w - > m_bCreatedOverFullscreen & & w - > visibleOnMonitor ( pMonitor ) )
2023-10-04 09:44:03 +01:00
return ;
}
2024-04-02 20:32:39 +01:00
if ( pMonitor - > activeSpecialWorkspace )
2023-10-04 09:44:03 +01:00
return ;
2023-09-28 21:48:33 +01:00
// check if it did not open any subsurfaces or shit
int surfaceCount = 0 ;
2024-12-22 17:12:09 +01:00
if ( PCANDIDATE - > m_bIsX11 )
2023-09-28 21:48:33 +01:00
surfaceCount = 1 ;
2024-12-22 17:12:09 +01:00
else
2024-05-10 23:28:33 +01:00
surfaceCount = PCANDIDATE - > popupsCount ( ) + PCANDIDATE - > surfacesCount ( ) ;
2023-09-28 21:48:33 +01:00
2023-10-02 14:02:15 +01:00
if ( surfaceCount > 1 )
return ;
2023-09-28 21:48:33 +01:00
// found one!
pMonitor - > solitaryClient = PCANDIDATE ;
}
2023-11-24 10:54:21 +00:00
2024-07-21 13:09:54 +02:00
SP < CRenderbuffer > CHyprRenderer : : getOrCreateRenderbuffer ( SP < Aquamarine : : IBuffer > buffer , uint32_t fmt ) {
auto it = std : : find_if ( m_vRenderbuffers . begin ( ) , m_vRenderbuffers . end ( ) , [ & ] ( const auto & other ) { return other - > m_pHLBuffer = = buffer ; } ) ;
2023-11-24 10:54:21 +00:00
if ( it ! = m_vRenderbuffers . end ( ) )
2024-07-21 13:09:54 +02:00
return * it ;
2023-11-24 10:54:21 +00:00
2024-07-21 13:09:54 +02:00
auto buf = makeShared < CRenderbuffer > ( buffer , fmt ) ;
2023-11-24 10:54:21 +00:00
2024-07-21 13:09:54 +02:00
if ( ! buf - > good ( ) )
return nullptr ;
2024-06-08 10:07:59 +02:00
2024-07-21 13:09:54 +02:00
m_vRenderbuffers . emplace_back ( buf ) ;
return buf ;
2024-06-08 10:07:59 +02:00
}
2023-11-30 01:18:55 +00:00
void CHyprRenderer : : makeEGLCurrent ( ) {
2024-07-21 13:09:54 +02:00
if ( ! g_pCompositor | | ! g_pHyprOpenGL )
2023-12-06 14:46:18 +00:00
return ;
2024-07-21 13:09:54 +02:00
if ( eglGetCurrentContext ( ) ! = g_pHyprOpenGL - > m_pEglContext )
eglMakeCurrent ( g_pHyprOpenGL - > m_pEglDisplay , EGL_NO_SURFACE , EGL_NO_SURFACE , g_pHyprOpenGL - > m_pEglContext ) ;
2023-11-30 01:18:55 +00:00
}
void CHyprRenderer : : unsetEGL ( ) {
2024-07-21 13:09:54 +02:00
if ( ! g_pHyprOpenGL )
return ;
eglMakeCurrent ( g_pHyprOpenGL - > m_pEglDisplay , EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT ) ;
2023-11-30 01:18:55 +00:00
}
2024-10-19 23:03:29 +01:00
bool CHyprRenderer : : beginRender ( PHLMONITOR pMonitor , CRegion & damage , eRenderMode mode , SP < IHLBuffer > buffer , CFramebuffer * fb , bool simple ) {
2023-11-30 01:18:55 +00:00
makeEGLCurrent ( ) ;
2023-11-24 10:54:21 +00:00
2024-12-22 17:12:09 +01:00
m_sRenderPass . clear ( ) ;
2023-11-24 10:54:21 +00:00
m_eRenderMode = mode ;
g_pHyprOpenGL - > m_RenderData . pMonitor = pMonitor ; // has to be set cuz allocs
if ( mode = = RENDER_MODE_FULL_FAKE ) {
2023-11-30 01:18:55 +00:00
RASSERT ( fb , " Cannot render FULL_FAKE without a provided fb! " ) ;
fb - > bind ( ) ;
2024-05-05 22:18:10 +01:00
if ( simple )
g_pHyprOpenGL - > beginSimple ( pMonitor , damage , nullptr , fb ) ;
else
g_pHyprOpenGL - > begin ( pMonitor , damage , fb ) ;
2023-11-24 10:54:21 +00:00
return true ;
}
2024-11-17 15:58:18 +00:00
/* This is a constant expression, as we always use double-buffering in our swapchain
2024-07-21 13:09:54 +02:00
TODO : Rewrite the CDamageRing to take advantage of that maybe ? It ' s made to support longer swapchains atm because we used to do wlroots */
static constexpr const int HL_BUFFER_AGE = 2 ;
2024-06-19 18:25:20 +02:00
2023-11-30 01:18:55 +00:00
if ( ! buffer ) {
2024-07-21 13:09:54 +02:00
m_pCurrentBuffer = pMonitor - > output - > swapchain - > next ( nullptr ) ;
if ( ! m_pCurrentBuffer ) {
2024-02-23 00:02:48 +00:00
Debug : : log ( ERR , " Failed to acquire swapchain buffer for {} " , pMonitor - > szName ) ;
2023-11-24 10:54:21 +00:00
return false ;
2024-02-22 23:50:56 +00:00
}
} else
2024-07-21 13:09:54 +02:00
m_pCurrentBuffer = buffer ;
2023-11-24 10:54:21 +00:00
try {
2024-07-21 13:09:54 +02:00
m_pCurrentRenderbuffer = getOrCreateRenderbuffer ( m_pCurrentBuffer , pMonitor - > output - > state - > state ( ) . drmFormat ) ;
2023-11-24 10:54:21 +00:00
} catch ( std : : exception & e ) {
2024-02-23 00:02:48 +00:00
Debug : : log ( ERR , " getOrCreateRenderbuffer failed for {} " , pMonitor - > szName ) ;
2024-07-21 13:09:54 +02:00
return false ;
}
if ( ! m_pCurrentRenderbuffer ) {
Debug : : log ( ERR , " failed to start a render pass for output {}, no RBO could be obtained " , pMonitor - > szName ) ;
2023-11-24 10:54:21 +00:00
return false ;
}
2024-06-19 18:25:20 +02:00
if ( mode = = RENDER_MODE_NORMAL ) {
2024-07-21 13:09:54 +02:00
damage = pMonitor - > damage . getBufferDamage ( HL_BUFFER_AGE ) ;
2024-06-19 18:25:20 +02:00
pMonitor - > damage . rotate ( ) ;
}
2024-02-19 18:17:32 +00:00
2023-11-24 10:54:21 +00:00
m_pCurrentRenderbuffer - > bind ( ) ;
2024-05-05 22:18:10 +01:00
if ( simple )
g_pHyprOpenGL - > beginSimple ( pMonitor , damage , m_pCurrentRenderbuffer ) ;
else
g_pHyprOpenGL - > begin ( pMonitor , damage ) ;
2023-11-24 10:54:21 +00:00
return true ;
}
void CHyprRenderer : : endRender ( ) {
2024-03-03 18:39:20 +00:00
const auto PMONITOR = g_pHyprOpenGL - > m_RenderData . pMonitor ;
static auto PNVIDIAANTIFLICKER = CConfigValue < Hyprlang : : INT > ( " opengl:nvidia_anti_flicker " ) ;
2024-07-21 13:09:54 +02:00
2024-12-22 17:12:09 +01:00
g_pHyprOpenGL - > m_RenderData . damage = m_sRenderPass . render ( g_pHyprOpenGL - > m_RenderData . damage ) ;
2024-08-06 14:52:19 +01:00
auto cleanup = CScopeGuard ( [ this ] ( ) {
if ( m_pCurrentRenderbuffer )
m_pCurrentRenderbuffer - > unbind ( ) ;
m_pCurrentRenderbuffer = nullptr ;
m_pCurrentBuffer = nullptr ;
} ) ;
2023-11-30 10:14:35 +00:00
if ( m_eRenderMode ! = RENDER_MODE_TO_BUFFER_READ_ONLY )
g_pHyprOpenGL - > end ( ) ;
else {
2024-10-19 23:03:29 +01:00
g_pHyprOpenGL - > m_RenderData . pMonitor . reset ( ) ;
2023-11-30 10:14:35 +00:00
g_pHyprOpenGL - > m_RenderData . mouseZoomFactor = 1.f ;
g_pHyprOpenGL - > m_RenderData . mouseZoomUseMouse = true ;
}
2023-11-24 10:54:21 +00:00
2023-11-30 10:14:35 +00:00
if ( m_eRenderMode = = RENDER_MODE_FULL_FAKE )
2023-11-24 10:54:21 +00:00
return ;
2023-11-30 01:18:55 +00:00
if ( m_eRenderMode = = RENDER_MODE_NORMAL ) {
2024-07-21 13:09:54 +02:00
PMONITOR - > output - > state - > setBuffer ( m_pCurrentBuffer ) ;
2025-02-13 12:09:25 +01:00
auto explicitOptions = getExplicitSyncSettings ( PMONITOR - > output ) ;
2024-08-06 14:52:19 +01:00
if ( PMONITOR - > inTimeline & & explicitOptions . explicitEnabled & & explicitOptions . explicitKMSEnabled ) {
2025-03-20 11:39:55 +01:00
PMONITOR - > eglSync = g_pHyprOpenGL - > createEGLSync ( ) ;
if ( ! PMONITOR - > eglSync ) {
2024-07-21 13:09:54 +02:00
Debug : : log ( ERR , " renderer: couldn't create an EGLSync for out in endRender " ) ;
return ;
}
2025-03-20 11:39:55 +01:00
bool ok = PMONITOR - > inTimeline - > importFromSyncFileFD ( PMONITOR - > commitSeq , PMONITOR - > eglSync - > fd ( ) ) ;
2024-08-06 14:52:19 +01:00
if ( ! ok ) {
Debug : : log ( ERR , " renderer: couldn't import from sync file fd in endRender " ) ;
2024-07-21 13:09:54 +02:00
return ;
}
2023-11-24 10:54:21 +00:00
syncobj: use eventfd instead of stalling fd checks (#9437)
* syncobj: cleanup and use uniqueptrs
cleanup a bit missing removals if resource not good, erasing from
containers etc. make use of unique ptrs instead. and add default
destructors.
* syncobj: rework syncobj entirerly
remove early buffer release that was breaking explicit sync, the buffer
needs to exist until the surface commit event has been emitted and draw
calls added egl sync points, move to eventfd signaling instead of
stalling sync point checks, and recommit pending commits if waiting on a
signal. add a CDRMSyncPointState helper class. move a few weak pointers
to shared pointers so they dont destruct before we need to use them.
* syncobj: queue pending states for eventfd
eventfd requires us to queue pending stats until ready and then apply to
current, and also when no ready state exist commit the client commit on
the current existing buffer, if there is one.
* syncobj: clear current buffer damage
clear current buffer damage on current buffer commits.
* syncobj: cleanup code and fix hyprlock
remove unused code, and ensure we dont commit a empty texture causing
locksession protocol and gtk4-layer-shell misbehaving.
* syncobj: ensure buffers are cleaned up
ensure the containers having the various buffers actually gets cleaned
up from their containers, incase the CSignal isnt signaled because of
expired smart pointers or just wrong order destruction because mishaps.
also move the acquire/point setting to buffer attaching. instead of on
precommit.
* syncobj: remove unused code, optimize
remove unused code and merge sync fds if fence is valid, remove manual
directscanout buffer dropping that signals release point on pageflip, it
can cause us to signal the release point while still keeping the current
buffer and rendering it yet again causing wrong things.
* syncobj: delay buffer release on non syncobj
delay buffer releases on non syncobj surfaces until next commit, and
check on async buffers if syncobj and drop and signal the release point
on backend buffer release.
* syncobj: ensure we follow protocol
ensure we follow protocol by replacing acquire/release points if they
arrive late and replace already existing ones. also remove unneded
brackets, and dont try to manual lock/release buffers when it comes to
explicit protocol. it doesnt care about buffer releases only about
acquire and release points and signaling them.
* syncobj: lets not complicate things
set points in precommit, before checking protocol errors and we catch
any pending acquire/release points arriving late.
* syncobj: move SSurfaceState to types
remove destructor resource destroying, let resources destroys them on
their events, and move SSurfaceStates to types/SurfaceState.hpp
* syncobj: actually store the merged fd
have to actually store the mergedfd to use it.
* syncobj: cleanup a bit around fences
ensure the current asynchronous buffer is actually released on pageflip
not the previous. cleanup a bit FD handling in
commitPendingAndDoExplicitSync, and reuse the in fence when syncing
surfaces.
* syncobjs: ensure fence FD doesnt leak
calling resetexplicitfence without properly ensuring the FD is closed
before will leak it, store it per monitor and let it close itself with
the CFileDescriptor class.
* syncobj: ensure buffers are actually released
buffers were never being sent released properly.
* types: Defer buffer sync releaser until unlock
* syncobj: store directscanout fence in monitor
ensure the infence fd survives the scope of attemptdirectscanout so it
doesnt close before it should have.
* syncobj: check if if acquire is expired
we might hit a race to finish on exit where the timeline just has
destructed but the buffer waiter is still pending. and such we
removeAllWaiters null dereferences.
* syncobj: code style changes
remove quack comment, change to m_foo and use a std::vector and
weakpointer in the waiter for removal instead of a std::list.
* syncobj: remove unused async buffer drop
remove unused async buffer drop, only related to directscanout and is
handled elsewhere.
---------
Co-authored-by: Lee Bousfield <ljbousfield@gmail.com>
2025-03-14 15:08:20 +01:00
PMONITOR - > inFence = CFileDescriptor { PMONITOR - > inTimeline - > exportAsSyncFileFD ( PMONITOR - > commitSeq ) } ;
if ( ! PMONITOR - > inFence . isValid ( ) ) {
2024-08-06 14:52:19 +01:00
Debug : : log ( ERR , " renderer: couldn't export from sync timeline in endRender " ) ;
2024-07-21 13:09:54 +02:00
return ;
}
2024-08-06 14:52:19 +01:00
syncobj: use eventfd instead of stalling fd checks (#9437)
* syncobj: cleanup and use uniqueptrs
cleanup a bit missing removals if resource not good, erasing from
containers etc. make use of unique ptrs instead. and add default
destructors.
* syncobj: rework syncobj entirerly
remove early buffer release that was breaking explicit sync, the buffer
needs to exist until the surface commit event has been emitted and draw
calls added egl sync points, move to eventfd signaling instead of
stalling sync point checks, and recommit pending commits if waiting on a
signal. add a CDRMSyncPointState helper class. move a few weak pointers
to shared pointers so they dont destruct before we need to use them.
* syncobj: queue pending states for eventfd
eventfd requires us to queue pending stats until ready and then apply to
current, and also when no ready state exist commit the client commit on
the current existing buffer, if there is one.
* syncobj: clear current buffer damage
clear current buffer damage on current buffer commits.
* syncobj: cleanup code and fix hyprlock
remove unused code, and ensure we dont commit a empty texture causing
locksession protocol and gtk4-layer-shell misbehaving.
* syncobj: ensure buffers are cleaned up
ensure the containers having the various buffers actually gets cleaned
up from their containers, incase the CSignal isnt signaled because of
expired smart pointers or just wrong order destruction because mishaps.
also move the acquire/point setting to buffer attaching. instead of on
precommit.
* syncobj: remove unused code, optimize
remove unused code and merge sync fds if fence is valid, remove manual
directscanout buffer dropping that signals release point on pageflip, it
can cause us to signal the release point while still keeping the current
buffer and rendering it yet again causing wrong things.
* syncobj: delay buffer release on non syncobj
delay buffer releases on non syncobj surfaces until next commit, and
check on async buffers if syncobj and drop and signal the release point
on backend buffer release.
* syncobj: ensure we follow protocol
ensure we follow protocol by replacing acquire/release points if they
arrive late and replace already existing ones. also remove unneded
brackets, and dont try to manual lock/release buffers when it comes to
explicit protocol. it doesnt care about buffer releases only about
acquire and release points and signaling them.
* syncobj: lets not complicate things
set points in precommit, before checking protocol errors and we catch
any pending acquire/release points arriving late.
* syncobj: move SSurfaceState to types
remove destructor resource destroying, let resources destroys them on
their events, and move SSurfaceStates to types/SurfaceState.hpp
* syncobj: actually store the merged fd
have to actually store the mergedfd to use it.
* syncobj: cleanup a bit around fences
ensure the current asynchronous buffer is actually released on pageflip
not the previous. cleanup a bit FD handling in
commitPendingAndDoExplicitSync, and reuse the in fence when syncing
surfaces.
* syncobjs: ensure fence FD doesnt leak
calling resetexplicitfence without properly ensuring the FD is closed
before will leak it, store it per monitor and let it close itself with
the CFileDescriptor class.
* syncobj: ensure buffers are actually released
buffers were never being sent released properly.
* types: Defer buffer sync releaser until unlock
* syncobj: store directscanout fence in monitor
ensure the infence fd survives the scope of attemptdirectscanout so it
doesnt close before it should have.
* syncobj: check if if acquire is expired
we might hit a race to finish on exit where the timeline just has
destructed but the buffer waiter is still pending. and such we
removeAllWaiters null dereferences.
* syncobj: code style changes
remove quack comment, change to m_foo and use a std::vector and
weakpointer in the waiter for removal instead of a std::list.
* syncobj: remove unused async buffer drop
remove unused async buffer drop, only related to directscanout and is
handled elsewhere.
---------
Co-authored-by: Lee Bousfield <ljbousfield@gmail.com>
2025-03-14 15:08:20 +01:00
PMONITOR - > output - > state - > setExplicitInFence ( PMONITOR - > inFence . get ( ) ) ;
2024-07-21 13:09:54 +02:00
} else {
if ( isNvidia ( ) & & * PNVIDIAANTIFLICKER )
glFinish ( ) ;
else
glFlush ( ) ;
}
}
2023-11-24 10:54:21 +00:00
}
void CHyprRenderer : : onRenderbufferDestroy ( CRenderbuffer * rb ) {
std : : erase_if ( m_vRenderbuffers , [ & ] ( const auto & rbo ) { return rbo . get ( ) = = rb ; } ) ;
}
2024-07-21 13:09:54 +02:00
SP < CRenderbuffer > CHyprRenderer : : getCurrentRBO ( ) {
2023-11-24 10:54:21 +00:00
return m_pCurrentRenderbuffer ;
2023-11-25 17:45:08 +00:00
}
bool CHyprRenderer : : isNvidia ( ) {
return m_bNvidia ;
2023-12-18 16:06:06 +00:00
}
2024-08-06 14:52:19 +01:00
2025-02-13 12:09:25 +01:00
SExplicitSyncSettings CHyprRenderer : : getExplicitSyncSettings ( SP < Aquamarine : : IOutput > output ) {
2024-08-06 14:52:19 +01:00
static auto PENABLEEXPLICIT = CConfigValue < Hyprlang : : INT > ( " render:explicit_sync " ) ;
static auto PENABLEEXPLICITKMS = CConfigValue < Hyprlang : : INT > ( " render:explicit_sync_kms " ) ;
SExplicitSyncSettings settings ;
settings . explicitEnabled = * PENABLEEXPLICIT ;
settings . explicitKMSEnabled = * PENABLEEXPLICITKMS ;
2025-02-13 12:09:25 +01:00
if ( ! output - > supportsExplicit ) {
settings . explicitEnabled = false ;
settings . explicitKMSEnabled = false ;
return settings ;
}
2024-08-06 14:52:19 +01:00
if ( * PENABLEEXPLICIT = = 2 /* auto */ )
settings . explicitEnabled = true ;
if ( * PENABLEEXPLICITKMS = = 2 /* auto */ ) {
if ( ! m_bNvidia )
settings . explicitKMSEnabled = true ;
else {
// check nvidia version. Explicit KMS is supported in >=560
// in the case of an error, driverMajor will stay 0 and explicit KMS will be disabled
2024-08-07 18:54:45 +02:00
static int driverMajor = 0 ;
2024-08-06 14:52:19 +01:00
static bool once = true ;
if ( once ) {
once = false ;
Debug : : log ( LOG , " Renderer: checking for explicit KMS support for nvidia " ) ;
if ( std : : filesystem : : exists ( " /sys/module/nvidia_drm/version " ) ) {
Debug : : log ( LOG , " Renderer: Nvidia version file exists " ) ;
std : : ifstream ifs ( " /sys/module/nvidia_drm/version " ) ;
if ( ifs . good ( ) ) {
try {
std : : string driverInfo ( ( std : : istreambuf_iterator < char > ( ifs ) ) , ( std : : istreambuf_iterator < char > ( ) ) ) ;
Debug : : log ( LOG , " Renderer: Read nvidia version {} " , driverInfo ) ;
CVarList ver ( driverInfo , 0 , ' . ' , true ) ;
driverMajor = std : : stoi ( ver [ 0 ] ) ;
Debug : : log ( LOG , " Renderer: Parsed nvidia major version: {} " , driverMajor ) ;
} catch ( std : : exception & e ) { settings . explicitKMSEnabled = false ; }
ifs . close ( ) ;
}
}
}
settings . explicitKMSEnabled = driverMajor > = 560 ;
}
}
return settings ;
}
2024-08-30 17:37:52 +02:00
void CHyprRenderer : : addWindowToRenderUnfocused ( PHLWINDOW window ) {
static auto PFPS = CConfigValue < Hyprlang : : INT > ( " misc:render_unfocused_fps " ) ;
if ( std : : find ( m_vRenderUnfocused . begin ( ) , m_vRenderUnfocused . end ( ) , window ) ! = m_vRenderUnfocused . end ( ) )
return ;
m_vRenderUnfocused . emplace_back ( window ) ;
if ( ! m_tRenderUnfocusedTimer - > armed ( ) )
m_tRenderUnfocusedTimer - > updateTimeout ( std : : chrono : : milliseconds ( 1000 / * PFPS ) ) ;
}
2024-12-22 17:12:09 +01:00
void CHyprRenderer : : makeRawWindowSnapshot ( PHLWINDOW pWindow , CFramebuffer * pFramebuffer ) {
// we trust the window is valid.
const auto PMONITOR = pWindow - > m_pMonitor . lock ( ) ;
if ( ! PMONITOR | | ! PMONITOR - > output | | PMONITOR - > vecPixelSize . x < = 0 | | PMONITOR - > vecPixelSize . y < = 0 )
return ;
// we need to "damage" the entire monitor
// so that we render the entire window
// this is temporary, doesnt mess with the actual damage
CRegion fakeDamage { 0 , 0 , ( int ) PMONITOR - > vecTransformedSize . x , ( int ) PMONITOR - > vecTransformedSize . y } ;
makeEGLCurrent ( ) ;
pFramebuffer - > alloc ( PMONITOR - > vecPixelSize . x , PMONITOR - > vecPixelSize . y , PMONITOR - > output - > state - > state ( ) . drmFormat ) ;
pFramebuffer - > addStencil ( g_pHyprOpenGL - > m_RenderData . pCurrentMonData - > stencilTex ) ;
beginRender ( PMONITOR , fakeDamage , RENDER_MODE_FULL_FAKE , nullptr , pFramebuffer ) ;
g_pHyprOpenGL - > clear ( CHyprColor ( 0 , 0 , 0 , 0 ) ) ; // JIC
timespec now ;
clock_gettime ( CLOCK_MONOTONIC , & now ) ;
// this is a hack but it works :P
// we need to disable blur or else we will get a black background, as the shader
// will try to copy the bg to apply blur.
// this isn't entirely correct, but like, oh well.
// small todo: maybe make this correct? :P
static auto * const PBLUR = ( Hyprlang : : INT * const * ) ( g_pConfigManager - > getConfigValuePtr ( " decoration:blur:enabled " ) ) ;
const auto BLURVAL = * * PBLUR ;
* * PBLUR = 0 ;
// TODO: how can we make this the size of the window? setting it to window's size makes the entire screen render with the wrong res forever more. odd.
glViewport ( 0 , 0 , PMONITOR - > vecPixelSize . x , PMONITOR - > vecPixelSize . y ) ;
g_pHyprOpenGL - > m_RenderData . currentFB = pFramebuffer ;
g_pHyprOpenGL - > clear ( CHyprColor ( 0 , 0 , 0 , 0 ) ) ; // JIC
renderWindow ( pWindow , PMONITOR , & now , false , RENDER_PASS_ALL , true ) ;
* * PBLUR = BLURVAL ;
endRender ( ) ;
}
void CHyprRenderer : : makeWindowSnapshot ( PHLWINDOW pWindow ) {
// we trust the window is valid.
const auto PMONITOR = pWindow - > m_pMonitor . lock ( ) ;
if ( ! PMONITOR | | ! PMONITOR - > output | | PMONITOR - > vecPixelSize . x < = 0 | | PMONITOR - > vecPixelSize . y < = 0 )
return ;
if ( ! shouldRenderWindow ( pWindow ) )
return ; // ignore, window is not being rendered
// we need to "damage" the entire monitor
// so that we render the entire window
// this is temporary, doesnt mess with the actual damage
CRegion fakeDamage { 0 , 0 , ( int ) PMONITOR - > vecTransformedSize . x , ( int ) PMONITOR - > vecTransformedSize . y } ;
PHLWINDOWREF ref { pWindow } ;
makeEGLCurrent ( ) ;
const auto PFRAMEBUFFER = & g_pHyprOpenGL - > m_mWindowFramebuffers [ ref ] ;
PFRAMEBUFFER - > alloc ( PMONITOR - > vecPixelSize . x , PMONITOR - > vecPixelSize . y , PMONITOR - > output - > state - > state ( ) . drmFormat ) ;
beginRender ( PMONITOR , fakeDamage , RENDER_MODE_FULL_FAKE , nullptr , PFRAMEBUFFER ) ;
m_bRenderingSnapshot = true ;
g_pHyprOpenGL - > clear ( CHyprColor ( 0 , 0 , 0 , 0 ) ) ; // JIC
timespec now ;
clock_gettime ( CLOCK_MONOTONIC , & now ) ;
// this is a hack but it works :P
// we need to disable blur or else we will get a black background, as the shader
// will try to copy the bg to apply blur.
// this isn't entirely correct, but like, oh well.
// small todo: maybe make this correct? :P
static auto * const PBLUR = ( Hyprlang : : INT * const * ) ( g_pConfigManager - > getConfigValuePtr ( " decoration:blur:enabled " ) ) ;
const auto BLURVAL = * * PBLUR ;
* * PBLUR = 0 ;
g_pHyprOpenGL - > clear ( CHyprColor ( 0 , 0 , 0 , 0 ) ) ; // JIC
renderWindow ( pWindow , PMONITOR , & now , ! pWindow - > m_bX11DoesntWantBorders , RENDER_PASS_ALL ) ;
* * PBLUR = BLURVAL ;
endRender ( ) ;
m_bRenderingSnapshot = false ;
}
void CHyprRenderer : : makeLayerSnapshot ( PHLLS pLayer ) {
// we trust the window is valid.
const auto PMONITOR = pLayer - > monitor . lock ( ) ;
if ( ! PMONITOR | | ! PMONITOR - > output | | PMONITOR - > vecPixelSize . x < = 0 | | PMONITOR - > vecPixelSize . y < = 0 )
return ;
// we need to "damage" the entire monitor
// so that we render the entire window
// this is temporary, doesnt mess with the actual damage
CRegion fakeDamage { 0 , 0 , ( int ) PMONITOR - > vecTransformedSize . x , ( int ) PMONITOR - > vecTransformedSize . y } ;
makeEGLCurrent ( ) ;
const auto PFRAMEBUFFER = & g_pHyprOpenGL - > m_mLayerFramebuffers [ pLayer ] ;
PFRAMEBUFFER - > alloc ( PMONITOR - > vecPixelSize . x , PMONITOR - > vecPixelSize . y , PMONITOR - > output - > state - > state ( ) . drmFormat ) ;
beginRender ( PMONITOR , fakeDamage , RENDER_MODE_FULL_FAKE , nullptr , PFRAMEBUFFER ) ;
m_bRenderingSnapshot = true ;
g_pHyprOpenGL - > clear ( CHyprColor ( 0 , 0 , 0 , 0 ) ) ; // JIC
timespec now ;
clock_gettime ( CLOCK_MONOTONIC , & now ) ;
const auto BLURLSSTATUS = pLayer - > forceBlur ;
pLayer - > forceBlur = false ;
// draw the layer
renderLayer ( pLayer , PMONITOR , & now ) ;
pLayer - > forceBlur = BLURLSSTATUS ;
endRender ( ) ;
m_bRenderingSnapshot = false ;
}
void CHyprRenderer : : renderSnapshot ( PHLWINDOW pWindow ) {
static auto PDIMAROUND = CConfigValue < Hyprlang : : FLOAT > ( " decoration:dim_around " ) ;
PHLWINDOWREF ref { pWindow } ;
if ( ! g_pHyprOpenGL - > m_mWindowFramebuffers . contains ( ref ) )
return ;
const auto FBDATA = & g_pHyprOpenGL - > m_mWindowFramebuffers . at ( ref ) ;
if ( ! FBDATA - > getTexture ( ) )
return ;
const auto PMONITOR = pWindow - > m_pMonitor . lock ( ) ;
CBox windowBox ;
// some mafs to figure out the correct box
// the originalClosedPos is relative to the monitor's pos
2025-01-07 17:55:14 +00:00
Vector2D scaleXY = Vector2D ( ( PMONITOR - > scale * pWindow - > m_vRealSize - > value ( ) . x / ( pWindow - > m_vOriginalClosedSize . x * PMONITOR - > scale ) ) ,
( PMONITOR - > scale * pWindow - > m_vRealSize - > value ( ) . y / ( pWindow - > m_vOriginalClosedSize . y * PMONITOR - > scale ) ) ) ;
2024-12-22 17:12:09 +01:00
windowBox . width = PMONITOR - > vecTransformedSize . x * scaleXY . x ;
windowBox . height = PMONITOR - > vecTransformedSize . y * scaleXY . y ;
2025-01-07 17:55:14 +00:00
windowBox . x = ( ( pWindow - > m_vRealPosition - > value ( ) . x - PMONITOR - > vecPosition . x ) * PMONITOR - > scale ) - ( ( pWindow - > m_vOriginalClosedPos . x * PMONITOR - > scale ) * scaleXY . x ) ;
windowBox . y = ( ( pWindow - > m_vRealPosition - > value ( ) . y - PMONITOR - > vecPosition . y ) * PMONITOR - > scale ) - ( ( pWindow - > m_vOriginalClosedPos . y * PMONITOR - > scale ) * scaleXY . y ) ;
2024-12-22 17:12:09 +01:00
CRegion fakeDamage { 0 , 0 , PMONITOR - > vecTransformedSize . x , PMONITOR - > vecTransformedSize . y } ;
if ( * PDIMAROUND & & pWindow - > m_sWindowData . dimAround . valueOrDefault ( ) ) {
CRectPassElement : : SRectData data ;
data . box = { 0 , 0 , g_pHyprOpenGL - > m_RenderData . pMonitor - > vecPixelSize . x , g_pHyprOpenGL - > m_RenderData . pMonitor - > vecPixelSize . y } ;
2025-01-07 17:55:14 +00:00
data . color = CHyprColor ( 0 , 0 , 0 , * PDIMAROUND * pWindow - > m_fAlpha - > value ( ) ) ;
2024-12-22 17:12:09 +01:00
m_sRenderPass . add ( makeShared < CRectPassElement > ( data ) ) ;
damageMonitor ( PMONITOR ) ;
}
CTexPassElement : : SRenderData data ;
data . flipEndFrame = true ;
data . tex = FBDATA - > getTexture ( ) ;
data . box = windowBox ;
2025-01-07 17:55:14 +00:00
data . a = pWindow - > m_fAlpha - > value ( ) ;
2024-12-22 17:12:09 +01:00
data . damage = fakeDamage ;
m_sRenderPass . add ( makeShared < CTexPassElement > ( data ) ) ;
}
void CHyprRenderer : : renderSnapshot ( PHLLS pLayer ) {
if ( ! g_pHyprOpenGL - > m_mLayerFramebuffers . contains ( pLayer ) )
return ;
const auto FBDATA = & g_pHyprOpenGL - > m_mLayerFramebuffers . at ( pLayer ) ;
if ( ! FBDATA - > getTexture ( ) )
return ;
const auto PMONITOR = pLayer - > monitor . lock ( ) ;
CBox layerBox ;
// some mafs to figure out the correct box
// the originalClosedPos is relative to the monitor's pos
2025-01-07 17:55:14 +00:00
Vector2D scaleXY = Vector2D ( ( PMONITOR - > scale * pLayer - > realSize - > value ( ) . x / ( pLayer - > geometry . w * PMONITOR - > scale ) ) ,
( PMONITOR - > scale * pLayer - > realSize - > value ( ) . y / ( pLayer - > geometry . h * PMONITOR - > scale ) ) ) ;
2024-12-22 17:12:09 +01:00
layerBox . width = PMONITOR - > vecTransformedSize . x * scaleXY . x ;
layerBox . height = PMONITOR - > vecTransformedSize . y * scaleXY . y ;
2025-01-07 17:55:14 +00:00
layerBox . x = ( ( pLayer - > realPosition - > value ( ) . x - PMONITOR - > vecPosition . x ) * PMONITOR - > scale ) - ( ( ( pLayer - > geometry . x - PMONITOR - > vecPosition . x ) * PMONITOR - > scale ) * scaleXY . x ) ;
layerBox . y = ( ( pLayer - > realPosition - > value ( ) . y - PMONITOR - > vecPosition . y ) * PMONITOR - > scale ) - ( ( ( pLayer - > geometry . y - PMONITOR - > vecPosition . y ) * PMONITOR - > scale ) * scaleXY . y ) ;
2024-12-22 17:12:09 +01:00
CRegion fakeDamage { 0 , 0 , PMONITOR - > vecTransformedSize . x , PMONITOR - > vecTransformedSize . y } ;
CTexPassElement : : SRenderData data ;
data . flipEndFrame = true ;
data . tex = FBDATA - > getTexture ( ) ;
data . box = layerBox ;
2025-01-07 17:55:14 +00:00
data . a = pLayer - > alpha - > value ( ) ;
2024-12-22 17:12:09 +01:00
data . damage = fakeDamage ;
m_sRenderPass . add ( makeShared < CTexPassElement > ( data ) ) ;
}