2024-06-18 11:38:26 +02:00
# include <aquamarine/backend/Backend.hpp>
2024-06-18 18:45:05 +02:00
# include <aquamarine/backend/Wayland.hpp>
2024-07-01 14:33:05 +02:00
# include <aquamarine/backend/Headless.hpp>
2024-06-23 19:40:40 +02:00
# include <aquamarine/backend/DRM.hpp>
2024-06-19 22:40:23 +02:00
# include <aquamarine/allocator/GBM.hpp>
2025-01-11 04:12:51 +00:00
# include <ranges>
2024-06-30 19:04:14 +02:00
# include <sys/timerfd.h>
2025-01-23 02:39:30 +05:00
# include <ctime>
# include <cstring>
2024-07-12 20:53:11 +02:00
# include <xf86drm.h>
# include <fcntl.h>
2024-07-16 09:53:20 +00:00
# include <unistd.h>
2024-06-18 11:38:26 +02:00
using namespace Hyprutils : : Memory ;
using namespace Aquamarine ;
# define SP CSharedPointer
2024-08-25 17:48:53 +02:00
# define TIMESPEC_NSEC_PER_SEC 1000000000LL
2024-06-30 19:04:14 +02:00
static void timespecAddNs ( timespec * pTimespec , int64_t delta ) {
int delta_ns_low = delta % TIMESPEC_NSEC_PER_SEC ;
int delta_s_high = delta / TIMESPEC_NSEC_PER_SEC ;
pTimespec - > tv_sec + = delta_s_high ;
pTimespec - > tv_nsec + = ( long ) delta_ns_low ;
if ( pTimespec - > tv_nsec > = TIMESPEC_NSEC_PER_SEC ) {
pTimespec - > tv_nsec - = TIMESPEC_NSEC_PER_SEC ;
+ + pTimespec - > tv_sec ;
}
}
2024-06-18 18:45:05 +02:00
static const char * backendTypeToName ( eBackendType type ) {
switch ( type ) {
case AQ_BACKEND_DRM : return " drm " ;
case AQ_BACKEND_HEADLESS : return " headless " ;
case AQ_BACKEND_WAYLAND : return " wayland " ;
default : break ;
}
return " invalid " ;
}
2024-06-18 11:38:26 +02:00
Aquamarine : : CBackend : : CBackend ( ) {
;
}
2025-01-23 02:39:30 +05:00
Aquamarine : : SBackendImplementationOptions : : SBackendImplementationOptions ( ) : backendType ( AQ_BACKEND_WAYLAND ) , backendRequestMode ( AQ_BACKEND_REQUEST_IF_AVAILABLE ) {
;
2024-06-18 18:45:05 +02:00
}
2025-01-23 02:39:30 +05:00
Aquamarine : : SBackendOptions : : SBackendOptions ( ) : logFunction ( nullptr ) {
;
2024-06-18 18:45:05 +02:00
}
2024-06-18 11:38:26 +02:00
Hyprutils : : Memory : : CSharedPointer < CBackend > Aquamarine : : CBackend : : create ( const std : : vector < SBackendImplementationOptions > & backends , const SBackendOptions & options ) {
auto backend = SP < CBackend > ( new CBackend ( ) ) ;
2024-06-18 18:45:05 +02:00
backend - > options = options ;
backend - > implementationOptions = backends ;
2024-06-19 22:40:23 +02:00
backend - > self = backend ;
if ( backends . size ( ) < = 0 )
return nullptr ;
2024-06-18 11:38:26 +02:00
2024-06-18 18:45:05 +02:00
backend - > log ( AQ_LOG_DEBUG , " Creating an Aquamarine backend! " ) ;
2024-08-27 20:04:26 +02:00
for ( auto const & b : backends ) {
2024-06-18 18:45:05 +02:00
if ( b . backendType = = AQ_BACKEND_WAYLAND ) {
auto ref = SP < CWaylandBackend > ( new CWaylandBackend ( backend ) ) ;
backend - > implementations . emplace_back ( ref ) ;
ref - > self = ref ;
2024-06-23 19:40:40 +02:00
} else if ( b . backendType = = AQ_BACKEND_DRM ) {
auto ref = CDRMBackend : : attempt ( backend ) ;
2024-06-28 17:13:03 +02:00
if ( ref . empty ( ) ) {
2024-06-23 19:40:40 +02:00
backend - > log ( AQ_LOG_ERROR , " DRM Backend failed " ) ;
continue ;
}
2024-06-28 17:13:03 +02:00
2024-08-27 20:04:26 +02:00
for ( auto const & r : ref ) {
2024-06-28 17:13:03 +02:00
backend - > implementations . emplace_back ( r ) ;
}
2024-07-01 14:33:05 +02:00
} else if ( b . backendType = = AQ_BACKEND_HEADLESS ) {
auto ref = SP < CHeadlessBackend > ( new CHeadlessBackend ( backend ) ) ;
backend - > implementations . emplace_back ( ref ) ;
ref - > self = ref ;
2024-06-18 18:45:05 +02:00
} else {
backend - > log ( AQ_LOG_ERROR , std : : format ( " Unknown backend id: {} " , ( int ) b . backendType ) ) ;
continue ;
}
}
2024-06-18 11:38:26 +02:00
2024-06-30 19:04:14 +02:00
// create a timerfd for idle events
backend - > idle . fd = timerfd_create ( CLOCK_MONOTONIC , TFD_CLOEXEC ) ;
2024-06-18 11:38:26 +02:00
return backend ;
}
Aquamarine : : CBackend : : ~ CBackend ( ) {
2024-06-18 18:45:05 +02:00
;
2024-06-18 11:38:26 +02:00
}
bool Aquamarine : : CBackend : : start ( ) {
2024-06-18 18:45:05 +02:00
log ( AQ_LOG_DEBUG , " Starting the Aquamarine backend! " ) ;
2025-03-17 07:07:53 -05:00
int started = 0 ;
2024-06-18 18:45:05 +02:00
2024-06-30 19:04:14 +02:00
auto optionsForType = [ this ] ( eBackendType type ) - > SBackendImplementationOptions {
2024-08-27 20:04:26 +02:00
for ( auto const & o : implementationOptions ) {
2024-06-28 17:35:43 +02:00
if ( o . backendType = = type )
return o ;
}
return SBackendImplementationOptions { } ;
} ;
2024-06-18 18:45:05 +02:00
for ( size_t i = 0 ; i < implementations . size ( ) ; + + i ) {
const bool ok = implementations . at ( i ) - > start ( ) ;
if ( ! ok ) {
2024-06-28 17:35:43 +02:00
log ( AQ_LOG_ERROR , std : : format ( " Requested backend ({}) could not start, enabling fallbacks " , backendTypeToName ( implementations . at ( i ) - > type ( ) ) ) ) ;
if ( optionsForType ( implementations . at ( i ) - > type ( ) ) . backendRequestMode = = AQ_BACKEND_REQUEST_MANDATORY ) {
2024-06-30 19:04:14 +02:00
log ( AQ_LOG_CRITICAL , std : : format ( " Requested backend ({}) could not start and it's mandatory, cannot continue! " , backendTypeToName ( implementations . at ( i ) - > type ( ) ) ) ) ;
2024-06-18 18:45:05 +02:00
implementations . clear ( ) ;
return false ;
}
} else
started + + ;
}
if ( implementations . empty ( ) | | started < = 0 ) {
log ( AQ_LOG_CRITICAL , " No backend could be opened. Make sure there was a correct backend passed to CBackend, and that your environment supports at least one of them. " ) ;
return false ;
}
// erase failed impls
2024-06-24 23:22:02 +02:00
std : : erase_if ( implementations , [ this ] ( const auto & i ) {
2024-06-27 00:07:59 +02:00
bool failed = i - > pollFDs ( ) . empty ( ) ;
2024-06-24 23:22:02 +02:00
if ( failed )
log ( AQ_LOG_ERROR , std : : format ( " Implementation {} failed, erasing. " , backendTypeToName ( i - > type ( ) ) ) ) ;
return failed ;
} ) ;
2024-06-18 18:45:05 +02:00
2024-06-19 22:40:23 +02:00
// TODO: obviously change this when (if) we add different allocators.
2024-08-27 20:04:26 +02:00
for ( auto const & b : implementations ) {
2024-06-19 22:40:23 +02:00
if ( b - > drmFD ( ) > = 0 ) {
2024-07-12 20:53:11 +02:00
auto fd = reopenDRMNode ( b - > drmFD ( ) ) ;
if ( fd < 0 ) {
// this is critical, we cannot create an allocator properly
log ( AQ_LOG_CRITICAL , " Failed to create an allocator (reopenDRMNode failed) " ) ;
return false ;
}
primaryAllocator = CGBMAllocator : : create ( fd , self ) ;
2024-07-11 20:41:53 +02:00
break ;
2024-06-19 22:40:23 +02:00
}
}
2025-02-11 18:27:39 +00:00
if ( ! primaryAllocator ) {
log ( AQ_LOG_CRITICAL , " Cannot open backend: no allocator available " ) ;
2024-06-19 22:40:23 +02:00
return false ;
2025-02-11 18:27:39 +00:00
}
2024-06-19 22:40:23 +02:00
2024-06-21 15:49:28 +02:00
ready = true ;
2024-08-27 20:04:26 +02:00
for ( auto const & b : implementations ) {
2024-06-20 19:24:43 +02:00
b - > onReady ( ) ;
}
2024-09-24 00:48:00 +01:00
if ( session )
session - > onReady ( ) ;
2024-06-27 00:07:59 +02:00
sessionFDs = session ? session - > pollFDs ( ) : std : : vector < Hyprutils : : Memory : : CSharedPointer < SPollFD > > { } ;
2024-06-23 19:40:40 +02:00
2024-06-18 11:38:26 +02:00
return true ;
}
void Aquamarine : : CBackend : : log ( eBackendLogLevel level , const std : : string & msg ) {
if ( ! options . logFunction )
return ;
options . logFunction ( level , msg ) ;
}
2024-06-18 18:45:05 +02:00
2024-06-27 00:07:59 +02:00
std : : vector < Hyprutils : : Memory : : CSharedPointer < SPollFD > > Aquamarine : : CBackend : : getPollFDs ( ) {
std : : vector < Hyprutils : : Memory : : CSharedPointer < SPollFD > > result ;
2024-08-27 20:04:26 +02:00
for ( auto const & i : implementations ) {
2024-06-27 00:07:59 +02:00
auto pollfds = i - > pollFDs ( ) ;
2024-08-27 20:04:26 +02:00
for ( auto const & p : pollfds ) {
2024-07-02 14:32:41 +02:00
log ( AQ_LOG_DEBUG , std : : format ( " backend: poll fd {} for implementation {} " , p - > fd , backendTypeToName ( i - > type ( ) ) ) ) ;
2024-06-27 00:07:59 +02:00
result . emplace_back ( p ) ;
2024-06-18 18:45:05 +02:00
}
}
2024-06-20 19:24:43 +02:00
2024-08-27 20:04:26 +02:00
for ( auto const & sfd : sessionFDs ) {
2024-07-02 14:32:41 +02:00
log ( AQ_LOG_DEBUG , std : : format ( " backend: poll fd {} for session " , sfd - > fd ) ) ;
2024-06-27 00:07:59 +02:00
result . emplace_back ( sfd ) ;
2024-06-23 19:40:40 +02:00
}
2024-07-02 14:32:41 +02:00
log ( AQ_LOG_DEBUG , std : : format ( " backend: poll fd {} for idle " , idle . fd ) ) ;
2024-06-30 19:04:14 +02:00
result . emplace_back ( makeShared < SPollFD > ( idle . fd , [ this ] ( ) { dispatchIdle ( ) ; } ) ) ;
2024-06-20 19:24:43 +02:00
return result ;
}
int Aquamarine : : CBackend : : drmFD ( ) {
2024-08-27 20:04:26 +02:00
for ( auto const & i : implementations ) {
2024-06-20 19:24:43 +02:00
int fd = i - > drmFD ( ) ;
if ( fd < 0 )
continue ;
return fd ;
}
return - 1 ;
}
2025-07-21 20:57:00 +02:00
int Aquamarine : : CBackend : : drmRenderNodeFD ( ) {
for ( auto const & i : implementations ) {
int fd = i - > drmRenderNodeFD ( ) ;
if ( fd < 0 )
continue ;
return fd ;
}
return - 1 ;
}
2024-06-20 19:24:43 +02:00
bool Aquamarine : : CBackend : : hasSession ( ) {
2024-06-24 23:22:02 +02:00
return session ;
}
std : : vector < SDRMFormat > Aquamarine : : CBackend : : getPrimaryRenderFormats ( ) {
2024-08-27 20:04:26 +02:00
for ( auto const & b : implementations ) {
2024-06-24 23:22:02 +02:00
if ( b - > type ( ) ! = AQ_BACKEND_DRM & & b - > type ( ) ! = AQ_BACKEND_WAYLAND )
continue ;
return b - > getRenderFormats ( ) ;
}
2024-08-27 20:04:26 +02:00
for ( auto const & b : implementations ) {
2024-06-24 23:22:02 +02:00
return b - > getRenderFormats ( ) ;
}
return { } ;
2024-06-18 18:45:05 +02:00
}
2024-06-26 22:35:14 +02:00
2024-06-30 19:04:14 +02:00
const std : : vector < SP < IBackendImplementation > > & Aquamarine : : CBackend : : getImplementations ( ) {
2024-06-26 22:35:14 +02:00
return implementations ;
}
2024-06-30 19:04:14 +02:00
void Aquamarine : : CBackend : : addIdleEvent ( SP < std : : function < void ( void ) > > fn ) {
auto r = idle . pending . emplace_back ( fn ) ;
2024-07-02 14:32:41 +02:00
updateIdleTimer ( ) ;
}
void Aquamarine : : CBackend : : updateIdleTimer ( ) {
uint64_t ADD_NS = idle . pending . empty ( ) ? TIMESPEC_NSEC_PER_SEC * 240ULL /* 240s, 4 mins */ : 0 ;
2024-06-30 19:04:14 +02:00
timespec now ;
clock_gettime ( CLOCK_MONOTONIC , & now ) ;
2024-07-02 14:32:41 +02:00
timespecAddNs ( & now , ADD_NS ) ;
2024-06-30 19:04:14 +02:00
itimerspec ts = { . it_value = now } ;
if ( timerfd_settime ( idle . fd , TFD_TIMER_ABSTIME , & ts , nullptr ) )
log ( AQ_LOG_ERROR , std : : format ( " backend: failed to arm timerfd: {} " , strerror ( errno ) ) ) ;
}
void Aquamarine : : CBackend : : removeIdleEvent ( SP < std : : function < void ( void ) > > pfn ) {
std : : erase ( idle . pending , pfn ) ;
}
void Aquamarine : : CBackend : : dispatchIdle ( ) {
auto cpy = idle . pending ;
idle . pending . clear ( ) ;
2024-08-27 20:04:26 +02:00
for ( auto const & i : cpy ) {
2024-06-30 19:04:14 +02:00
if ( i & & * i )
( * i ) ( ) ;
}
2024-07-02 14:32:41 +02:00
updateIdleTimer ( ) ;
2024-06-30 19:04:14 +02:00
}
2024-07-12 20:53:11 +02:00
2025-01-10 20:39:08 +02:00
void Aquamarine : : CBackend : : onNewGpu ( std : : string path ) {
const auto primary = std : : ranges : : find_if ( implementations , [ ] ( SP < IBackendImplementation > value ) { return value - > type ( ) = = Aquamarine : : AQ_BACKEND_DRM ; } ) ;
const auto primaryDrm = primary ! = implementations . end ( ) ? ( ( Aquamarine : : CDRMBackend * ) ( * primary ) . get ( ) ) - > self . lock ( ) : nullptr ;
auto ref = CDRMBackend : : fromGpu ( path , self . lock ( ) , primaryDrm ) ;
if ( ! ref ) {
log ( AQ_LOG_ERROR , std : : format ( " DRM Backend failed for device {} " , path ) ) ;
return ;
}
if ( ! ref - > start ( ) ) {
log ( AQ_LOG_ERROR , std : : format ( " Couldn't start DRM Backend for device {} " , path ) ) ;
return ;
}
implementations . emplace_back ( ref ) ;
events . pollFDsChanged . emit ( ) ;
ref - > onReady ( ) ; // Renderer created here
ref - > recheckOutputs ( ) ; // Now we can recheck outputs
}
2024-07-12 20:53:11 +02:00
// Yoinked from wlroots, render/allocator/allocator.c
// Ref-counting reasons, see https://gitlab.freedesktop.org/mesa/drm/-/merge_requests/110
int Aquamarine : : CBackend : : reopenDRMNode ( int drmFD , bool allowRenderNode ) {
if ( drmIsMaster ( drmFD ) ) {
// Only recent kernels support empty leases
uint32_t lesseeID = 0 ;
int leaseFD = drmModeCreateLease ( drmFD , nullptr , 0 , O_CLOEXEC , & lesseeID ) ;
if ( leaseFD > = 0 ) {
return leaseFD ;
} else if ( leaseFD ! = - EINVAL & & leaseFD ! = - EOPNOTSUPP ) {
log ( AQ_LOG_ERROR , " reopenDRMNode: drmModeCreateLease failed " ) ;
return - 1 ;
}
log ( AQ_LOG_DEBUG , " reopenDRMNode: drmModeCreateLease failed, falling back to open " ) ;
}
char * name = nullptr ;
if ( allowRenderNode )
name = drmGetRenderDeviceNameFromFd ( drmFD ) ;
if ( ! name ) {
// primary node or no name
name = drmGetDeviceNameFromFd2 ( drmFD ) ;
if ( ! name ) {
log ( AQ_LOG_ERROR , " reopenDRMNode: drmGetDeviceNameFromFd2 failed " ) ;
return - 1 ;
}
}
log ( AQ_LOG_DEBUG , std : : format ( " reopenDRMNode: opening node {} " , name ) ) ;
int newFD = open ( name , O_RDWR | O_CLOEXEC ) ;
if ( newFD < 0 ) {
log ( AQ_LOG_ERROR , std : : format ( " reopenDRMNode: failed to open node {} " , name ) ) ;
free ( name ) ;
return - 1 ;
}
free ( name ) ;
// We need to authenticate if we are using a DRM primary node and are the master
2024-07-16 09:53:50 +00:00
if ( drmIsMaster ( drmFD ) & & drmGetNodeTypeFromFd ( newFD ) = = DRM_NODE_PRIMARY ) {
2024-07-12 20:53:11 +02:00
drm_magic_t magic ;
if ( int ret = drmGetMagic ( newFD , & magic ) ; ret < 0 ) {
log ( AQ_LOG_ERROR , std : : format ( " reopenDRMNode: drmGetMagic failed: {} " , strerror ( - ret ) ) ) ;
close ( newFD ) ;
return - 1 ;
}
2024-07-16 09:53:50 +00:00
if ( int ret = drmAuthMagic ( drmFD , magic ) ; ret < 0 ) {
2024-07-12 20:53:11 +02:00
log ( AQ_LOG_ERROR , std : : format ( " reopenDRMNode: drmAuthMagic failed: {} " , strerror ( - ret ) ) ) ;
close ( newFD ) ;
return - 1 ;
}
}
return newFD ;
}
2024-07-24 17:41:13 +01:00
std : : vector < SDRMFormat > Aquamarine : : IBackendImplementation : : getRenderableFormats ( ) {
return { } ;
}