2024-07-07 19:05:05 +03:00
# include "aquamarine/output/Output.hpp"
2024-06-23 19:40:40 +02:00
# include <aquamarine/backend/DRM.hpp>
2024-06-24 23:22:02 +02:00
# include <aquamarine/backend/drm/Legacy.hpp>
2024-06-27 21:48:01 +02:00
# include <aquamarine/backend/drm/Atomic.hpp>
2024-07-11 20:41:53 +02:00
# include <aquamarine/allocator/GBM.hpp>
2024-06-28 17:13:03 +02:00
# include <hyprutils/string/VarList.hpp>
2024-06-23 19:40:40 +02:00
# include <chrono>
# include <thread>
# include <deque>
# include <cstring>
2024-06-24 23:22:02 +02:00
# include <sys/mman.h>
2024-07-05 19:21:33 +02:00
# include <fcntl.h>
2024-06-23 19:40:40 +02:00
extern " C " {
# include <libseat.h>
# include <libudev.h>
# include <xf86drm.h>
# include <xf86drmMode.h>
2024-06-24 23:22:02 +02:00
# include <libdisplay-info/cvt.h>
2024-07-02 13:12:47 +02:00
# include <libdisplay-info/info.h>
# include <libdisplay-info/edid.h>
2024-06-23 19:40:40 +02:00
}
# include "Props.hpp"
2024-06-24 23:22:02 +02:00
# include "FormatUtils.hpp"
2024-06-27 21:48:01 +02:00
# include "Shared.hpp"
2024-07-02 13:12:47 +02:00
# include "hwdata.hpp"
2024-07-11 20:41:53 +02:00
# include "Renderer.hpp"
2024-06-23 19:40:40 +02:00
using namespace Aquamarine ;
using namespace Hyprutils : : Memory ;
using namespace Hyprutils : : Math ;
# define SP CSharedPointer
Aquamarine : : CDRMBackend : : CDRMBackend ( SP < CBackend > backend_ ) : backend ( backend_ ) {
2024-06-25 13:23:31 +02:00
listeners . sessionActivate = backend - > session - > events . changeActive . registerListener ( [ this ] ( std : : any d ) {
if ( backend - > session - > active ) {
// session got activated, we need to restore
restoreAfterVT ( ) ;
}
} ) ;
2024-06-23 19:40:40 +02:00
}
static udev_enumerate * enumDRMCards ( udev * udev ) {
auto enumerate = udev_enumerate_new ( udev ) ;
if ( ! enumerate )
return nullptr ;
udev_enumerate_add_match_subsystem ( enumerate , " drm " ) ;
udev_enumerate_add_match_sysname ( enumerate , DRM_PRIMARY_MINOR_NAME " [0-9]* " ) ;
if ( udev_enumerate_scan_devices ( enumerate ) ) {
udev_enumerate_unref ( enumerate ) ;
return nullptr ;
}
return enumerate ;
}
static std : : vector < SP < CSessionDevice > > scanGPUs ( SP < CBackend > backend ) {
auto enumerate = enumDRMCards ( backend - > session - > udevHandle ) ;
if ( ! enumerate ) {
backend - > log ( AQ_LOG_ERROR , " drm: couldn't enumerate gpus with udev " ) ;
return { } ;
}
if ( ! udev_enumerate_get_list_entry ( enumerate ) ) {
// TODO: wait for them.
backend - > log ( AQ_LOG_ERROR , " drm: No gpus in scanGPUs. " ) ;
return { } ;
}
udev_list_entry * entry = nullptr ;
size_t i = 0 ;
std : : deque < SP < CSessionDevice > > devices ;
udev_list_entry_foreach ( entry , udev_enumerate_get_list_entry ( enumerate ) ) {
auto path = udev_list_entry_get_name ( entry ) ;
auto device = udev_device_new_from_syspath ( backend - > session - > udevHandle , path ) ;
if ( ! device ) {
backend - > log ( AQ_LOG_WARNING , std : : format ( " drm: Skipping device {} " , path ? path : " unknown " ) ) ;
continue ;
}
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Enumerated device {} " , path ? path : " unknown " ) ) ;
auto seat = udev_device_get_property_value ( device , " ID_SEAT " ) ;
if ( ! seat )
seat = " seat0 " ;
if ( ! backend - > session - > seatName . empty ( ) & & backend - > session - > seatName ! = seat ) {
backend - > log ( AQ_LOG_WARNING , std : : format ( " drm: Skipping device {} because seat {} doesn't match our {} " , path ? path : " unknown " , seat , backend - > session - > seatName ) ) ;
udev_device_unref ( device ) ;
continue ;
}
auto pciDevice = udev_device_get_parent_with_subsystem_devtype ( device , " pci " , nullptr ) ;
bool isBootVGA = false ;
if ( pciDevice ) {
auto id = udev_device_get_sysattr_value ( pciDevice , " boot_vga " ) ;
isBootVGA = id & & id = = std : : string { " 1 " } ;
}
if ( ! udev_device_get_devnode ( device ) ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: Skipping device {}, no devnode " , path ? path : " unknown " ) ) ;
udev_device_unref ( device ) ;
continue ;
}
auto sessionDevice = CSessionDevice : : openIfKMS ( backend - > session , udev_device_get_devnode ( device ) ) ;
if ( ! sessionDevice ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: Skipping device {}, not a KMS device " , path ? path : " unknown " ) ) ;
udev_device_unref ( device ) ;
continue ;
}
udev_device_unref ( device ) ;
if ( isBootVGA )
devices . push_front ( sessionDevice ) ;
else
devices . push_back ( sessionDevice ) ;
+ + i ;
}
udev_enumerate_unref ( enumerate ) ;
std : : vector < SP < CSessionDevice > > vecDevices ;
2024-06-28 17:13:03 +02:00
auto explicitGpus = getenv ( " AQ_DRM_DEVICES " ) ;
if ( explicitGpus ) {
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Explicit device list {} " , explicitGpus ) ) ;
2024-07-07 23:19:00 +02:00
Hyprutils : : String : : CVarList explicitDevices ( explicitGpus , 0 , ' : ' , true ) ;
2024-06-28 17:13:03 +02:00
for ( auto & d : explicitDevices ) {
bool found = false ;
for ( auto & vd : devices ) {
if ( vd - > path = = d ) {
vecDevices . emplace_back ( vd ) ;
found = true ;
break ;
}
}
if ( found )
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Explicit device {} found " , d ) ) ;
else
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: Explicit device {} not found " , d ) ) ;
}
} else {
for ( auto & d : devices ) {
vecDevices . push_back ( d ) ;
}
2024-06-23 19:40:40 +02:00
}
return vecDevices ;
}
2024-06-28 17:13:03 +02:00
std : : vector < SP < CDRMBackend > > Aquamarine : : CDRMBackend : : attempt ( SP < CBackend > backend ) {
2024-06-23 19:40:40 +02:00
if ( ! backend - > session )
backend - > session = CSession : : attempt ( backend ) ;
if ( ! backend - > session ) {
backend - > log ( AQ_LOG_ERROR , " Failed to open a session " ) ;
2024-06-28 17:13:03 +02:00
return { } ;
2024-06-23 19:40:40 +02:00
}
if ( ! backend - > session - > active ) {
backend - > log ( AQ_LOG_DEBUG , " Session is not active, waiting for 5s " ) ;
auto started = std : : chrono : : system_clock : : now ( ) ;
while ( ! backend - > session - > active ) {
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 250 ) ) ;
backend - > session - > dispatchPendingEventsAsync ( ) ;
if ( std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( std : : chrono : : system_clock : : now ( ) - started ) . count ( ) > = 5000 ) {
backend - > log ( AQ_LOG_DEBUG , " Session timeout reached " ) ;
break ;
}
}
if ( ! backend - > session - > active ) {
backend - > log ( AQ_LOG_DEBUG , " Session could not be activated in time " ) ;
2024-06-28 17:13:03 +02:00
return { } ;
2024-06-23 19:40:40 +02:00
}
}
auto gpus = scanGPUs ( backend ) ;
if ( gpus . empty ( ) ) {
backend - > log ( AQ_LOG_ERROR , " drm: Found no gpus to use, cannot continue " ) ;
2024-06-28 17:13:03 +02:00
return { } ;
2024-06-23 19:40:40 +02:00
}
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Found {} GPUs " , gpus . size ( ) ) ) ;
2024-06-28 17:13:03 +02:00
std : : vector < SP < CDRMBackend > > backends ;
2024-06-28 20:14:56 +02:00
SP < CDRMBackend > newPrimary ;
2024-06-23 19:40:40 +02:00
2024-06-28 17:13:03 +02:00
for ( auto & gpu : gpus ) {
auto drmBackend = SP < CDRMBackend > ( new CDRMBackend ( backend ) ) ;
drmBackend - > self = drmBackend ;
2024-06-23 19:40:40 +02:00
2024-06-28 20:14:56 +02:00
if ( ! drmBackend - > registerGPU ( gpu , newPrimary ) ) {
2024-06-28 17:13:03 +02:00
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: Failed to register gpu {} " , gpu - > path ) ) ;
continue ;
} else
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Registered gpu {} " , gpu - > path ) ) ;
2024-06-23 19:40:40 +02:00
2024-06-28 17:13:03 +02:00
// TODO: consider listening for new devices
// But if you expect me to handle gpu hotswaps you are probably insane LOL
2024-06-23 19:40:40 +02:00
2024-06-28 17:13:03 +02:00
if ( ! drmBackend - > checkFeatures ( ) ) {
backend - > log ( AQ_LOG_ERROR , " drm: Failed checking features " ) ;
continue ;
}
if ( ! drmBackend - > initResources ( ) ) {
backend - > log ( AQ_LOG_ERROR , " drm: Failed initializing resources " ) ;
continue ;
}
2024-06-23 19:40:40 +02:00
2024-06-28 17:13:03 +02:00
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Basic init pass for gpu {} " , gpu - > path ) ) ;
2024-06-23 19:40:40 +02:00
2024-06-28 17:13:03 +02:00
drmBackend - > grabFormats ( ) ;
2024-06-23 19:40:40 +02:00
2024-06-28 17:13:03 +02:00
drmBackend - > scanConnectors ( ) ;
2024-07-08 15:57:16 +02:00
drmBackend - > recheckCRTCs ( ) ;
2024-06-28 20:14:56 +02:00
if ( ! newPrimary ) {
2024-06-28 17:13:03 +02:00
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: gpu {} becomes primary drm " , gpu - > path ) ) ;
2024-06-28 20:14:56 +02:00
newPrimary = drmBackend ;
2024-06-28 17:13:03 +02:00
}
backends . emplace_back ( drmBackend ) ;
2024-07-15 13:03:00 -05:00
// so that session can handle udev change/remove events for this gpu
backend - > session - > sessionDevices . push_back ( gpu ) ;
2024-06-28 17:13:03 +02:00
}
2024-06-23 19:40:40 +02:00
2024-06-28 17:13:03 +02:00
return backends ;
2024-06-23 19:40:40 +02:00
}
Aquamarine : : CDRMBackend : : ~ CDRMBackend ( ) {
;
}
2024-06-24 23:22:02 +02:00
void Aquamarine : : CDRMBackend : : log ( eBackendLogLevel l , const std : : string & s ) {
backend - > log ( l , s ) ;
}
bool Aquamarine : : CDRMBackend : : sessionActive ( ) {
return backend - > session - > active ;
}
2024-06-25 13:23:31 +02:00
void Aquamarine : : CDRMBackend : : restoreAfterVT ( ) {
backend - > log ( AQ_LOG_DEBUG , " drm: Restoring after VT switch " ) ;
2024-06-25 13:36:21 +02:00
scanConnectors ( ) ;
2024-07-08 15:57:16 +02:00
recheckCRTCs ( ) ;
2024-06-25 13:36:21 +02:00
backend - > log ( AQ_LOG_DEBUG , " drm: Rescanned connectors " ) ;
2024-06-30 20:54:11 +02:00
if ( ! impl - > reset ( ) )
backend - > log ( AQ_LOG_ERROR , " drm: failed reset " ) ;
2024-06-25 13:23:31 +02:00
2024-07-10 12:28:38 +02:00
std : : vector < SP < SDRMConnector > > noMode ;
2024-06-25 13:23:31 +02:00
for ( auto & c : connectors ) {
2024-07-06 11:30:47 +02:00
if ( ! c - > crtc | | ! c - > output )
2024-06-25 13:23:31 +02:00
continue ;
SDRMConnectorCommitData data = {
. mainFB = nullptr ,
. modeset = true ,
. blocking = true ,
. flags = 0 ,
. test = false ,
} ;
2024-07-02 23:05:50 +02:00
auto & STATE = c - > output - > state - > state ( ) ;
2024-07-10 12:28:38 +02:00
if ( ! STATE . customMode & & ! STATE . mode ) {
backend - > log ( AQ_LOG_WARNING , " drm: Connector {} has output but state has no mode, will send a reset state event later. " ) ;
noMode . emplace_back ( c ) ;
continue ;
}
2024-07-02 23:05:50 +02:00
if ( STATE . mode & & STATE . mode - > modeInfo . has_value ( ) )
data . modeInfo = * STATE . mode - > modeInfo ;
2024-06-25 13:23:31 +02:00
else
data . calculateMode ( c ) ;
2024-07-02 23:05:50 +02:00
if ( STATE . buffer ) {
SP < CDRMFB > drmFB ;
auto buf = STATE . buffer ;
bool isNew = false ;
drmFB = CDRMFB : : create ( buf , self , & isNew ) ;
if ( ! drmFB )
backend - > log ( AQ_LOG_ERROR , " drm: Buffer failed to import to KMS " ) ;
data . mainFB = drmFB ;
}
if ( c - > crtc - > pendingCursor )
data . cursorFB = c - > crtc - > pendingCursor ;
if ( data . cursorFB & & data . cursorFB - > buffer - > dmabuf ( ) . modifier = = DRM_FORMAT_MOD_INVALID )
data . cursorFB = nullptr ;
2024-06-25 13:23:31 +02:00
backend - > log ( AQ_LOG_DEBUG ,
std : : format ( " drm: Restoring crtc {} with clock {} hdisplay {} vdisplay {} vrefresh {} " , c - > crtc - > id , data . modeInfo . clock , data . modeInfo . hdisplay ,
data . modeInfo . vdisplay , data . modeInfo . vrefresh ) ) ;
if ( ! impl - > commit ( c , data ) )
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: crtc {} failed restore " , c - > crtc - > id ) ) ;
}
2024-07-10 12:28:38 +02:00
for ( auto & c : noMode ) {
if ( ! c - > output )
continue ;
// tell the consumer to re-set a state because we had no mode
c - > output - > events . state . emit ( IOutput : : SStateEvent { } ) ;
}
2024-06-25 13:23:31 +02:00
}
2024-06-23 19:40:40 +02:00
bool Aquamarine : : CDRMBackend : : checkFeatures ( ) {
uint64_t curW = 0 , curH = 0 ;
if ( drmGetCap ( gpu - > fd , DRM_CAP_CURSOR_WIDTH , & curW ) )
curW = 64 ;
if ( drmGetCap ( gpu - > fd , DRM_CAP_CURSOR_HEIGHT , & curH ) )
curH = 64 ;
drmProps . cursorSize = Hyprutils : : Math : : Vector2D { ( double ) curW , ( double ) curH } ;
uint64_t cap = 0 ;
if ( drmGetCap ( gpu - > fd , DRM_CAP_PRIME , & cap ) | | ! ( cap & DRM_PRIME_CAP_IMPORT ) ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: DRM_PRIME_CAP_IMPORT unsupported " ) ) ;
return false ;
}
if ( drmGetCap ( gpu - > fd , DRM_CAP_CRTC_IN_VBLANK_EVENT , & cap ) | | ! cap ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: DRM_CAP_CRTC_IN_VBLANK_EVENT unsupported " ) ) ;
return false ;
}
if ( drmGetCap ( gpu - > fd , DRM_CAP_TIMESTAMP_MONOTONIC , & cap ) | | ! cap ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: DRM_PRIME_CAP_IMPORT unsupported " ) ) ;
return false ;
}
if ( drmSetClientCap ( gpu - > fd , DRM_CLIENT_CAP_UNIVERSAL_PLANES , 1 ) ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: DRM_CLIENT_CAP_UNIVERSAL_PLANES unsupported " ) ) ;
return false ;
}
drmProps . supportsAsyncCommit = drmGetCap ( gpu - > fd , DRM_CAP_ASYNC_PAGE_FLIP , & cap ) = = 0 & & cap = = 1 ;
drmProps . supportsAddFb2Modifiers = drmGetCap ( gpu - > fd , DRM_CAP_ADDFB2_MODIFIERS , & cap ) = = 0 & & cap = = 1 ;
2024-07-06 17:45:47 +02:00
drmProps . supportsTimelines = drmGetCap ( gpu - > fd , DRM_CAP_SYNCOBJ_TIMELINE , & cap ) = = 0 & & cap = = 1 ;
2024-06-23 19:40:40 +02:00
2024-07-02 12:41:32 +02:00
if ( envEnabled ( " AQ_NO_ATOMIC " ) ) {
backend - > log ( AQ_LOG_WARNING , " drm: AQ_NO_ATOMIC enabled, using the legacy drm iface " ) ;
2024-06-27 21:48:01 +02:00
impl = makeShared < CDRMLegacyImpl > ( self . lock ( ) ) ;
} else if ( drmSetClientCap ( gpu - > fd , DRM_CLIENT_CAP_ATOMIC , 1 ) ) {
backend - > log ( AQ_LOG_WARNING , " drm: failed to set DRM_CLIENT_CAP_ATOMIC, falling back to legacy " ) ;
impl = makeShared < CDRMLegacyImpl > ( self . lock ( ) ) ;
} else {
backend - > log ( AQ_LOG_DEBUG , " drm: Atomic supported, using atomic for modesetting " ) ;
impl = makeShared < CDRMAtomicImpl > ( self . lock ( ) ) ;
drmProps . supportsAsyncCommit = drmGetCap ( gpu - > fd , DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP , & cap ) = = 0 & & cap = = 1 ;
2024-07-02 12:41:32 +02:00
atomic = true ;
2024-06-27 21:48:01 +02:00
}
2024-06-24 23:22:02 +02:00
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: drmProps.supportsAsyncCommit: {} " , drmProps . supportsAsyncCommit ) ) ;
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: drmProps.supportsAddFb2Modifiers: {} " , drmProps . supportsAddFb2Modifiers ) ) ;
2024-07-06 17:45:47 +02:00
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: drmProps.supportsTimelines: {} " , drmProps . supportsTimelines ) ) ;
2024-06-24 23:22:02 +02:00
2024-06-23 19:40:40 +02:00
// TODO: allow no-modifiers?
return true ;
}
bool Aquamarine : : CDRMBackend : : initResources ( ) {
auto resources = drmModeGetResources ( gpu - > fd ) ;
if ( ! resources ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: drmModeGetResources failed " ) ) ;
return false ;
}
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: found {} CRTCs " , resources - > count_crtcs ) ) ;
for ( size_t i = 0 ; i < resources - > count_crtcs ; + + i ) {
auto CRTC = makeShared < SDRMCRTC > ( ) ;
CRTC - > id = resources - > crtcs [ i ] ;
CRTC - > backend = self ;
auto drmCRTC = drmModeGetCrtc ( gpu - > fd , CRTC - > id ) ;
if ( ! drmCRTC ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: drmModeGetCrtc for crtc {} failed " , CRTC - > id ) ) ;
drmModeFreeResources ( resources ) ;
crtcs . clear ( ) ;
return false ;
}
CRTC - > legacy . gammaSize = drmCRTC - > gamma_size ;
drmModeFreeCrtc ( drmCRTC ) ;
if ( ! getDRMCRTCProps ( gpu - > fd , CRTC - > id , & CRTC - > props ) ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: getDRMCRTCProps for crtc {} failed " , CRTC - > id ) ) ;
drmModeFreeResources ( resources ) ;
crtcs . clear ( ) ;
return false ;
}
crtcs . emplace_back ( CRTC ) ;
}
if ( crtcs . size ( ) > 32 ) {
backend - > log ( AQ_LOG_CRITICAL , " drm: Cannot support more than 32 CRTCs " ) ;
return false ;
}
// initialize planes
auto planeResources = drmModeGetPlaneResources ( gpu - > fd ) ;
if ( ! planeResources ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: drmModeGetPlaneResources failed " ) ) ;
return false ;
}
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: found {} planes " , planeResources - > count_planes ) ) ;
for ( uint32_t i = 0 ; i < planeResources - > count_planes ; + + i ) {
auto id = planeResources - > planes [ i ] ;
auto plane = drmModeGetPlane ( gpu - > fd , id ) ;
if ( ! plane ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: drmModeGetPlane for plane {} failed " , id ) ) ;
drmModeFreeResources ( resources ) ;
crtcs . clear ( ) ;
planes . clear ( ) ;
return false ;
}
auto aqPlane = makeShared < SDRMPlane > ( ) ;
aqPlane - > backend = self ;
aqPlane - > self = aqPlane ;
if ( ! aqPlane - > init ( ( drmModePlane * ) plane ) ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: aqPlane->init for plane {} failed " , id ) ) ;
drmModeFreeResources ( resources ) ;
crtcs . clear ( ) ;
planes . clear ( ) ;
return false ;
}
2024-06-24 23:22:02 +02:00
planes . emplace_back ( aqPlane ) ;
2024-06-23 19:40:40 +02:00
drmModeFreePlane ( plane ) ;
}
drmModeFreePlaneResources ( planeResources ) ;
drmModeFreeResources ( resources ) ;
2024-06-24 23:22:02 +02:00
2024-06-23 19:40:40 +02:00
return true ;
}
2024-07-11 20:41:53 +02:00
bool Aquamarine : : CDRMBackend : : shouldBlit ( ) {
return primary ;
}
bool Aquamarine : : CDRMBackend : : initMgpu ( ) {
if ( ! primary )
return true ;
2024-07-12 23:23:39 +02:00
auto newAllocator = CGBMAllocator : : create ( backend - > reopenDRMNode ( gpu - > fd ) , backend ) ;
mgpu . allocator = newAllocator ;
2024-07-11 20:41:53 +02:00
if ( ! mgpu . allocator ) {
backend - > log ( AQ_LOG_ERROR , " drm: initMgpu: no allocator " ) ;
return false ;
}
2024-07-12 23:23:39 +02:00
mgpu . renderer = CDRMRenderer : : attempt ( newAllocator , backend . lock ( ) ) ;
2024-07-11 20:41:53 +02:00
if ( ! mgpu . renderer ) {
backend - > log ( AQ_LOG_ERROR , " drm: initMgpu: no renderer " ) ;
return false ;
}
2024-07-11 22:17:19 +02:00
mgpu . renderer - > self = mgpu . renderer ;
2024-07-24 17:41:13 +01:00
buildGlFormats ( mgpu . renderer - > formats ) ;
2024-07-11 20:41:53 +02:00
return true ;
}
2024-07-24 17:41:13 +01:00
void Aquamarine : : CDRMBackend : : buildGlFormats ( const std : : vector < SGLFormat > & fmts ) {
std : : vector < SDRMFormat > result ;
for ( auto & fmt : fmts ) {
if ( fmt . external )
continue ;
if ( auto it = std : : find_if ( result . begin ( ) , result . end ( ) , [ fmt ] ( const auto & e ) { return fmt . drmFormat = = e . drmFormat ; } ) ; it ! = result . end ( ) ) {
it - > modifiers . emplace_back ( fmt . modifier ) ;
continue ;
}
result . emplace_back ( SDRMFormat {
fmt . drmFormat ,
{ fmt . modifier } ,
} ) ;
}
glFormats = result ;
}
2024-07-08 15:57:16 +02:00
void Aquamarine : : CDRMBackend : : recheckCRTCs ( ) {
if ( connectors . empty ( ) | | crtcs . empty ( ) )
return ;
backend - > log ( AQ_LOG_DEBUG , " drm: Rechecking CRTCs " ) ;
std : : vector < SP < SDRMConnector > > recheck , changed ;
for ( auto & c : connectors ) {
if ( c - > crtc & & c - > status = = DRM_MODE_CONNECTED ) {
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Skipping connector {}, has crtc {} and is connected " , c - > szName , c - > crtc - > id ) ) ;
continue ;
}
recheck . emplace_back ( c ) ;
2024-07-09 10:31:01 +02:00
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: connector {}, has crtc {}, will be rechecked " , c - > szName , c - > crtc ? ( int ) c - > crtc - > id : - 1 ) ) ;
2024-07-08 15:57:16 +02:00
}
for ( size_t i = 0 ; i < crtcs . size ( ) ; + + i ) {
bool taken = false ;
for ( auto & c : connectors ) {
if ( c - > crtc ! = crtcs . at ( i ) )
continue ;
if ( c - > status ! = DRM_MODE_CONNECTED )
continue ;
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: slot {} crtc {} taken by {}, skipping " , i , c - > crtc - > id , c - > szName ) ) ;
taken = true ;
break ;
}
if ( taken )
continue ;
bool assigned = false ;
2024-07-18 22:52:57 +02:00
// try to use a connected connector
2024-07-08 15:57:16 +02:00
for ( auto & c : recheck ) {
if ( ! ( c - > possibleCrtcs & ( 1 < < i ) ) )
continue ;
2024-07-18 22:52:57 +02:00
if ( c - > status ! = DRM_MODE_CONNECTED )
continue ;
2024-07-08 15:57:16 +02:00
// deactivate old output
if ( c - > output & & c - > output - > state & & c - > output - > state - > state ( ) . enabled ) {
c - > output - > state - > setEnabled ( false ) ;
c - > output - > commit ( ) ;
}
2024-07-18 22:52:57 +02:00
backend - > log ( AQ_LOG_DEBUG ,
std : : format ( " drm: connected slot {} crtc {} assigned to {}{} " , i , crtcs . at ( i ) - > id , c - > szName , c - > crtc ? std : : format ( " (old {}) " , c - > crtc - > id ) : " " ) ) ;
2024-07-08 18:37:49 +02:00
c - > crtc = crtcs . at ( i ) ;
2024-07-08 15:57:16 +02:00
assigned = true ;
changed . emplace_back ( c ) ;
2024-07-08 17:20:11 +02:00
std : : erase ( recheck , c ) ;
2024-07-08 15:57:16 +02:00
break ;
}
if ( ! assigned )
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: slot {} crtc {} unassigned " , i , crtcs . at ( i ) - > id ) ) ;
}
2024-07-18 22:52:57 +02:00
for ( auto & c : connectors ) {
if ( c - > status = = DRM_MODE_CONNECTED )
continue ;
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Connector {} is not connected{} " , c - > szName , c - > crtc ? std : : format ( " , removing old crtc {} " , c - > crtc - > id ) : " " ) ) ;
}
2024-07-08 15:57:16 +02:00
// if any connectors get a crtc and are connected, we need to rescan to assign them outputs.
bool rescan = false ;
for ( auto & c : changed ) {
if ( ! c - > output & & c - > status = = DRM_MODE_CONNECTED ) {
rescan = true ;
continue ;
}
// tell the user to re-assign a valid mode etc
2024-07-08 17:20:41 +02:00
if ( c - > output )
c - > output - > events . state . emit ( IOutput : : SStateEvent { } ) ;
2024-07-08 15:57:16 +02:00
}
2024-07-09 10:31:01 +02:00
backend - > log ( AQ_LOG_DEBUG , " drm: rescanning after realloc " ) ;
scanConnectors ( ) ;
2024-07-08 15:57:16 +02:00
}
2024-06-23 19:40:40 +02:00
bool Aquamarine : : CDRMBackend : : grabFormats ( ) {
// FIXME: do this properly maybe?
return true ;
}
bool Aquamarine : : CDRMBackend : : registerGPU ( SP < CSessionDevice > gpu_ , SP < CDRMBackend > primary_ ) {
gpu = gpu_ ;
primary = primary_ ;
auto drmName = drmGetDeviceNameFromFd2 ( gpu - > fd ) ;
auto drmVer = drmGetVersion ( gpu - > fd ) ;
2024-06-25 13:36:21 +02:00
gpuName = drmName ;
2024-07-25 04:14:29 +10:00
auto drmVerName = drmVer - > name ? drmVer - > name : " unknown " ;
if ( std : : string_view ( drmVerName ) = = " evdi " )
primary = { } ;
2024-06-28 20:14:56 +02:00
backend - > log ( AQ_LOG_DEBUG ,
2024-07-25 04:14:29 +10:00
std : : format ( " drm: Starting backend for {}, with driver {}{} " , drmName ? drmName : " unknown " , drmVerName ,
2024-06-28 20:14:56 +02:00
( primary ? std : : format ( " with primary {} " , primary - > gpu - > path ) : " " ) ) ) ;
2024-06-23 19:40:40 +02:00
drmFreeVersion ( drmVer ) ;
2024-06-25 13:36:21 +02:00
listeners . gpuChange = gpu - > events . change . registerListener ( [ this ] ( std : : any d ) {
auto E = std : : any_cast < CSessionDevice : : SChangeEvent > ( d ) ;
if ( E . type = = CSessionDevice : : AQ_SESSION_EVENT_CHANGE_HOTPLUG ) {
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Got a hotplug event for {} " , gpuName ) ) ;
scanConnectors ( ) ;
2024-07-18 22:52:57 +02:00
recheckCRTCs ( ) ;
2024-07-05 19:21:33 +02:00
} else if ( E . type = = CSessionDevice : : AQ_SESSION_EVENT_CHANGE_LEASE ) {
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Got a lease event for {} " , gpuName ) ) ;
scanLeases ( ) ;
2024-06-25 13:36:21 +02:00
}
} ) ;
listeners . gpuRemove = gpu - > events . remove . registerListener (
[ this ] ( std : : any d ) { backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: !!!!FIXME: Got a remove event for {}, this is not handled properly!!!!! " , gpuName ) ) ; } ) ;
2024-06-23 19:40:40 +02:00
return true ;
}
eBackendType Aquamarine : : CDRMBackend : : type ( ) {
return eBackendType : : AQ_BACKEND_DRM ;
}
void Aquamarine : : CDRMBackend : : scanConnectors ( ) {
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Scanning connectors for {} " , gpu - > path ) ) ;
auto resources = drmModeGetResources ( gpu - > fd ) ;
if ( ! resources ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: Scanning connectors for {} failed " , gpu - > path ) ) ;
return ;
}
for ( size_t i = 0 ; i < resources - > count_connectors ; + + i ) {
uint32_t connectorID = resources - > connectors [ i ] ;
SP < SDRMConnector > conn ;
auto drmConn = drmModeGetConnector ( gpu - > fd , connectorID ) ;
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Scanning connector id {} " , connectorID ) ) ;
if ( ! drmConn ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: Failed to get connector id {} " , connectorID ) ) ;
continue ;
}
auto it = std : : find_if ( connectors . begin ( ) , connectors . end ( ) , [ connectorID ] ( const auto & e ) { return e - > id = = connectorID ; } ) ;
if ( it = = connectors . end ( ) ) {
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Initializing connector id {} " , connectorID ) ) ;
conn = connectors . emplace_back ( SP < SDRMConnector > ( new SDRMConnector ( ) ) ) ;
conn - > self = conn ;
conn - > backend = self ;
2024-06-28 18:03:04 +02:00
conn - > id = connectorID ;
2024-06-23 19:40:40 +02:00
if ( ! conn - > init ( drmConn ) ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: Connector id {} failed initializing " , connectorID ) ) ;
connectors . pop_back ( ) ;
continue ;
}
2024-06-28 18:03:04 +02:00
} else {
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Connector id {} already initialized " , connectorID ) ) ;
2024-06-23 19:40:40 +02:00
conn = * it ;
2024-06-28 18:03:04 +02:00
}
2024-06-23 19:40:40 +02:00
2024-07-18 22:52:57 +02:00
conn - > status = drmConn - > connection ;
2024-06-25 23:01:11 +02:00
if ( ! conn - > crtc ) {
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Ignoring connector {} because it has no CRTC " , connectorID ) ) ;
continue ;
}
2024-06-24 23:22:02 +02:00
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Connector {} connection state: {} " , connectorID , ( int ) drmConn - > connection ) ) ;
2024-06-23 19:40:40 +02:00
2024-07-18 22:52:57 +02:00
if ( conn - > status = = DRM_MODE_CONNECTED & & ! conn - > output ) {
2024-06-23 19:40:40 +02:00
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Connector {} connected " , conn - > szName ) ) ;
conn - > connect ( drmConn ) ;
2024-07-18 22:52:57 +02:00
} else if ( conn - > status ! = DRM_MODE_CONNECTED & & conn - > output ) {
2024-06-23 19:40:40 +02:00
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Connector {} disconnected " , conn - > szName ) ) ;
conn - > disconnect ( ) ;
}
drmModeFreeConnector ( drmConn ) ;
}
drmModeFreeResources ( resources ) ;
}
2024-07-05 19:21:33 +02:00
void Aquamarine : : CDRMBackend : : scanLeases ( ) {
auto lessees = drmModeListLessees ( gpu - > fd ) ;
if ( ! lessees ) {
backend - > log ( AQ_LOG_ERROR , " drmModeListLessees failed " ) ;
return ;
}
for ( auto & c : connectors ) {
if ( ! c - > output | | ! c - > output - > lease )
continue ;
bool has = false ;
for ( size_t i = 0 ; i < lessees - > count ; + + i ) {
if ( lessees - > lessees [ i ] = = c - > output - > lease - > lesseeID ) {
has = true ;
break ;
}
}
if ( has )
continue ;
backend - > log ( AQ_LOG_DEBUG , std : : format ( " lessee {} gone, removing " , c - > output - > lease - > lesseeID ) ) ;
// don't terminate
c - > output - > lease - > active = false ;
auto l = c - > output - > lease ;
for ( auto & c2 : connectors ) {
if ( ! c2 - > output | | c2 - > output - > lease ! = c - > output - > lease )
continue ;
c2 - > output - > lease . reset ( ) ;
}
l - > destroy ( ) ;
}
drmFree ( lessees ) ;
}
2024-06-23 19:40:40 +02:00
bool Aquamarine : : CDRMBackend : : start ( ) {
2024-06-30 20:54:11 +02:00
impl - > reset ( ) ;
2024-06-23 19:40:40 +02:00
return true ;
}
2024-06-27 00:07:59 +02:00
std : : vector < Hyprutils : : Memory : : CSharedPointer < SPollFD > > Aquamarine : : CDRMBackend : : pollFDs ( ) {
return { makeShared < SPollFD > ( gpu - > fd , [ this ] ( ) { dispatchEvents ( ) ; } ) } ;
2024-06-23 19:40:40 +02:00
}
int Aquamarine : : CDRMBackend : : drmFD ( ) {
return gpu - > fd ;
}
static void handlePF ( int fd , unsigned seq , unsigned tv_sec , unsigned tv_usec , unsigned crtc_id , void * data ) {
2024-06-24 23:22:02 +02:00
auto pageFlip = ( SDRMPageFlip * ) data ;
if ( ! pageFlip - > connector )
return ;
pageFlip - > connector - > isPageFlipPending = false ;
const auto & BACKEND = pageFlip - > connector - > backend ;
2024-07-02 14:42:24 +02:00
TRACE ( BACKEND - > log ( AQ_LOG_TRACE , std : : format ( " drm: pf event seq {} sec {} usec {} crtc {} " , seq , tv_sec , tv_usec , crtc_id ) ) ) ;
2024-06-24 23:22:02 +02:00
if ( pageFlip - > connector - > status ! = DRM_MODE_CONNECTED | | ! pageFlip - > connector - > crtc ) {
BACKEND - > log ( AQ_LOG_DEBUG , " drm: Ignoring a pf event from a disabled crtc / connector " ) ;
return ;
}
pageFlip - > connector - > onPresent ( ) ;
uint32_t flags = IOutput : : AQ_OUTPUT_PRESENT_VSYNC | IOutput : : AQ_OUTPUT_PRESENT_HW_CLOCK | IOutput : : AQ_OUTPUT_PRESENT_HW_COMPLETION | IOutput : : AQ_OUTPUT_PRESENT_ZEROCOPY ;
2024-07-13 22:56:46 +02:00
timespec presented = { . tv_sec = ( time_t ) tv_sec , . tv_nsec = ( long ) ( tv_usec * 1000 ) } ;
2024-06-24 23:22:02 +02:00
pageFlip - > connector - > output - > events . present . emit ( IOutput : : SPresentEvent {
. presented = BACKEND - > sessionActive ( ) ,
. when = & presented ,
. seq = seq ,
. refresh = ( int ) ( pageFlip - > connector - > refresh ? ( 1000000000000LL / pageFlip - > connector - > refresh ) : 0 ) ,
. flags = flags ,
} ) ;
2024-06-28 14:02:56 +02:00
if ( BACKEND - > sessionActive ( ) & & ! pageFlip - > connector - > frameEventScheduled )
2024-06-24 23:22:02 +02:00
pageFlip - > connector - > output - > events . frame . emit ( ) ;
2024-06-23 19:40:40 +02:00
}
bool Aquamarine : : CDRMBackend : : dispatchEvents ( ) {
drmEventContext event = {
. version = 3 ,
. page_flip_handler2 = : : handlePF ,
} ;
if ( drmHandleEvent ( gpu - > fd , & event ) ! = 0 )
2024-06-30 19:04:14 +02:00
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: Failed to handle event on fd {} " , gpu - > fd ) ) ;
2024-06-24 23:22:02 +02:00
2024-06-23 19:40:40 +02:00
return true ;
}
uint32_t Aquamarine : : CDRMBackend : : capabilities ( ) {
return eBackendCapabilities : : AQ_BACKEND_CAPABILITY_POINTER ;
}
bool Aquamarine : : CDRMBackend : : setCursor ( SP < IBuffer > buffer , const Hyprutils : : Math : : Vector2D & hotspot ) {
return false ;
}
void Aquamarine : : CDRMBackend : : onReady ( ) {
2024-06-24 23:22:02 +02:00
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Connectors size2 {} " , connectors . size ( ) ) ) ;
2024-07-24 17:41:13 +01:00
// init a drm renderer to gather gl formats.
// if we are secondary, initMgpu will have done that
if ( ! primary ) {
auto a = CGBMAllocator : : create ( backend - > reopenDRMNode ( gpu - > fd ) , backend ) ;
if ( ! a )
backend - > log ( AQ_LOG_ERROR , " drm: onReady: no renderer for gl formats " ) ;
else {
auto r = CDRMRenderer : : attempt ( a , backend . lock ( ) ) ;
if ( ! r )
backend - > log ( AQ_LOG_ERROR , " drm: onReady: no renderer for gl formats " ) ;
else {
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " drm: onReady: gathered {} gl formats " , r - > formats . size ( ) ) ) ) ;
buildGlFormats ( r - > formats ) ;
r . reset ( ) ;
a . reset ( ) ;
}
}
}
2024-06-24 23:22:02 +02:00
for ( auto & c : connectors ) {
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: onReady: connector {} " , c - > id ) ) ;
if ( ! c - > output )
continue ;
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: onReady: connector {} has output name {} " , c - > id , c - > output - > name ) ) ;
// swapchain has to be created here because allocator is absent in connect if not ready
2024-07-11 20:41:53 +02:00
c - > output - > swapchain = CSwapchain : : create ( backend - > primaryAllocator , self . lock ( ) ) ;
2024-07-09 14:10:52 +02:00
c - > output - > swapchain - > reconfigure ( SSwapchainOptions { . length = 0 , . scanout = true , . multigpu = ! ! primary } ) ; // mark the swapchain for scanout
2024-06-24 23:22:02 +02:00
c - > output - > needsFrame = true ;
backend - > events . newOutput . emit ( SP < IOutput > ( c - > output ) ) ;
}
2024-07-11 20:41:53 +02:00
if ( ! initMgpu ( ) ) {
backend - > log ( AQ_LOG_ERROR , " drm: Failed initializing mgpu " ) ;
return ;
}
2024-06-23 19:40:40 +02:00
}
std : : vector < SDRMFormat > Aquamarine : : CDRMBackend : : getRenderFormats ( ) {
for ( auto & p : planes ) {
if ( p - > type ! = DRM_PLANE_TYPE_PRIMARY )
continue ;
return p - > formats ;
}
return { } ;
}
2024-07-24 17:41:13 +01:00
std : : vector < SDRMFormat > Aquamarine : : CDRMBackend : : getRenderableFormats ( ) {
return glFormats ;
}
2024-06-23 19:40:40 +02:00
std : : vector < SDRMFormat > Aquamarine : : CDRMBackend : : getCursorFormats ( ) {
for ( auto & p : planes ) {
if ( p - > type ! = DRM_PLANE_TYPE_CURSOR )
continue ;
2024-06-28 17:13:03 +02:00
if ( primary ) {
2024-07-02 14:42:24 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " drm: getCursorFormats on secondary {} " , gpu - > path ) ) ) ;
2024-06-28 17:13:03 +02:00
// this is a secondary GPU renderer. In order to receive buffers,
// we'll force linear modifiers.
// TODO: don't. Find a common maybe?
auto fmts = p - > formats ;
for ( auto & fmt : fmts ) {
fmt . modifiers = { DRM_FORMAT_MOD_LINEAR } ;
}
return fmts ;
}
2024-06-23 19:40:40 +02:00
return p - > formats ;
}
return { } ;
}
2024-07-01 14:52:50 +02:00
bool Aquamarine : : CDRMBackend : : createOutput ( const std : : string & ) {
2024-07-01 14:33:05 +02:00
return false ;
}
2024-07-05 19:21:33 +02:00
int Aquamarine : : CDRMBackend : : getNonMasterFD ( ) {
int fd = open ( gpuName . c_str ( ) , O_RDWR | O_CLOEXEC ) ;
if ( fd < 0 ) {
backend - > log ( AQ_LOG_ERROR , " drm: couldn't dupe fd for non master " ) ;
return - 1 ;
}
if ( drmIsMaster ( fd ) & & drmDropMaster ( fd ) < 0 ) {
backend - > log ( AQ_LOG_ERROR , " drm: couldn't drop master from duped fd " ) ;
return - 1 ;
}
return fd ;
}
2024-07-09 14:10:52 +02:00
SP < IAllocator > Aquamarine : : CDRMBackend : : preferredAllocator ( ) {
2024-07-11 20:41:53 +02:00
return backend - > primaryAllocator ;
2024-07-09 14:10:52 +02:00
}
2024-06-23 19:40:40 +02:00
bool Aquamarine : : SDRMPlane : : init ( drmModePlane * plane ) {
id = plane - > plane_id ;
if ( ! getDRMPlaneProps ( backend - > gpu - > fd , id , & props ) )
return false ;
if ( ! getDRMProp ( backend - > gpu - > fd , id , props . type , & type ) )
return false ;
initialID = id ;
2024-06-24 23:22:02 +02:00
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Plane {} has type {} " , id , ( int ) type ) ) ;
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Plane {} has {} formats " , id , plane - > count_formats ) ) ;
2024-06-23 19:40:40 +02:00
for ( size_t i = 0 ; i < plane - > count_formats ; + + i ) {
if ( type ! = DRM_PLANE_TYPE_CURSOR )
formats . emplace_back ( SDRMFormat { . drmFormat = plane - > formats [ i ] , . modifiers = { DRM_FORMAT_MOD_LINEAR , DRM_FORMAT_MOD_INVALID } } ) ;
else
formats . emplace_back ( SDRMFormat { . drmFormat = plane - > formats [ i ] , . modifiers = { DRM_FORMAT_MOD_LINEAR } } ) ;
2024-06-24 23:22:02 +02:00
2024-07-02 14:42:24 +02:00
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE , std : : format ( " drm: | Format {} " , fourccToName ( plane - > formats [ i ] ) ) ) ) ;
2024-06-23 19:40:40 +02:00
}
if ( props . in_formats & & backend - > drmProps . supportsAddFb2Modifiers ) {
2024-06-24 23:22:02 +02:00
backend - > backend - > log ( AQ_LOG_DEBUG , " drm: Plane: checking for modifiers " ) ;
2024-06-23 19:40:40 +02:00
uint64_t blobID = 0 ;
2024-06-24 23:22:02 +02:00
if ( ! getDRMProp ( backend - > gpu - > fd , id , props . in_formats , & blobID ) ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Plane: No blob id " ) ;
2024-06-23 19:40:40 +02:00
return false ;
2024-06-24 23:22:02 +02:00
}
2024-06-23 19:40:40 +02:00
auto blob = drmModeGetPropertyBlob ( backend - > gpu - > fd , blobID ) ;
2024-06-24 23:22:02 +02:00
if ( ! blob ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Plane: No property " ) ;
2024-06-23 19:40:40 +02:00
return false ;
2024-06-24 23:22:02 +02:00
}
2024-06-23 19:40:40 +02:00
drmModeFormatModifierIterator iter = { 0 } ;
while ( drmModeFormatModifierBlobIterNext ( blob , & iter ) ) {
auto it = std : : find_if ( formats . begin ( ) , formats . end ( ) , [ iter ] ( const auto & e ) { return e . drmFormat = = iter . fmt ; } ) ;
2024-07-02 14:42:24 +02:00
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE , std : : format ( " drm: | Modifier {} with format {} " , iter . mod , fourccToName ( iter . fmt ) ) ) ) ;
2024-06-24 23:22:02 +02:00
2024-06-23 19:40:40 +02:00
if ( it = = formats . end ( ) )
formats . emplace_back ( SDRMFormat { . drmFormat = iter . fmt , . modifiers = { iter . mod } } ) ;
else
it - > modifiers . emplace_back ( iter . mod ) ;
}
drmModeFreePropertyBlob ( blob ) ;
}
for ( size_t i = 0 ; i < backend - > crtcs . size ( ) ; + + i ) {
uint32_t crtcBit = ( 1 < < i ) ;
if ( ! ( plane - > possible_crtcs & crtcBit ) )
continue ;
auto CRTC = backend - > crtcs . at ( i ) ;
if ( type = = DRM_PLANE_TYPE_PRIMARY & & ! CRTC - > primary ) {
CRTC - > primary = self . lock ( ) ;
break ;
}
if ( type = = DRM_PLANE_TYPE_CURSOR & & ! CRTC - > cursor ) {
CRTC - > cursor = self . lock ( ) ;
break ;
}
}
return true ;
}
SP < SDRMCRTC > Aquamarine : : SDRMConnector : : getCurrentCRTC ( const drmModeConnector * connector ) {
uint32_t crtcID = 0 ;
if ( props . crtc_id ) {
2024-07-02 14:42:24 +02:00
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE , " drm: Using crtc_id for finding crtc " ) ) ;
2024-06-23 19:40:40 +02:00
uint64_t value = 0 ;
if ( ! getDRMProp ( backend - > gpu - > fd , id , props . crtc_id , & value ) ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Failed to get CRTC_ID " ) ;
return nullptr ;
}
crtcID = static_cast < uint32_t > ( value ) ;
} else if ( connector - > encoder_id ) {
2024-07-02 14:42:24 +02:00
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE , " drm: Using encoder_id for finding crtc " ) ) ;
2024-06-23 19:40:40 +02:00
auto encoder = drmModeGetEncoder ( backend - > gpu - > fd , connector - > encoder_id ) ;
if ( ! encoder ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: drmModeGetEncoder failed " ) ;
return nullptr ;
}
crtcID = encoder - > crtc_id ;
drmModeFreeEncoder ( encoder ) ;
2024-06-28 18:03:04 +02:00
} else {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Connector has neither crtc_id nor encoder_id " ) ;
2024-06-23 19:40:40 +02:00
return nullptr ;
2024-06-28 18:03:04 +02:00
}
if ( crtcID = = 0 ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: getCurrentCRTC: No CRTC 0 " ) ;
return nullptr ;
}
2024-06-23 19:40:40 +02:00
auto it = std : : find_if ( backend - > crtcs . begin ( ) , backend - > crtcs . end ( ) , [ crtcID ] ( const auto & e ) { return e - > id = = crtcID ; } ) ;
if ( it = = backend - > crtcs . end ( ) ) {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: Failed to find a CRTC with ID {} " , crtcID ) ) ;
return nullptr ;
}
return * it ;
}
bool Aquamarine : : SDRMConnector : : init ( drmModeConnector * connector ) {
2024-06-24 23:22:02 +02:00
pendingPageFlip . connector = self . lock ( ) ;
2024-06-23 19:40:40 +02:00
if ( ! getDRMConnectorProps ( backend - > gpu - > fd , id , & props ) )
return false ;
auto name = drmModeGetConnectorTypeName ( connector - > connector_type ) ;
if ( ! name )
name = " ERROR " ;
szName = std : : format ( " {}-{} " , name , connector - > connector_type_id ) ;
2024-06-28 18:03:04 +02:00
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Connector gets name {} " , szName ) ) ;
2024-06-23 19:40:40 +02:00
possibleCrtcs = drmModeConnectorGetPossibleCrtcs ( backend - > gpu - > fd , connector ) ;
if ( ! possibleCrtcs )
backend - > backend - > log ( AQ_LOG_ERROR , " drm: No CRTCs possible " ) ;
crtc = getCurrentCRTC ( connector ) ;
return true ;
}
Aquamarine : : SDRMConnector : : ~ SDRMConnector ( ) {
disconnect ( ) ;
}
static int32_t calculateRefresh ( const drmModeModeInfo & mode ) {
int32_t refresh = ( mode . clock * 1000000LL / mode . htotal + mode . vtotal / 2 ) / mode . vtotal ;
if ( mode . flags & DRM_MODE_FLAG_INTERLACE )
refresh * = 2 ;
if ( mode . flags & DRM_MODE_FLAG_DBLSCAN )
refresh / = 2 ;
if ( mode . vscan > 1 )
refresh / = mode . vscan ;
return refresh ;
}
drmModeModeInfo * Aquamarine : : SDRMConnector : : getCurrentMode ( ) {
if ( ! crtc )
return nullptr ;
if ( crtc - > props . mode_id ) {
size_t size = 0 ;
return ( drmModeModeInfo * ) getDRMPropBlob ( backend - > gpu - > fd , crtc - > id , crtc - > props . mode_id , & size ) ;
}
auto drmCrtc = drmModeGetCrtc ( backend - > gpu - > fd , crtc - > id ) ;
if ( ! drmCrtc )
return nullptr ;
if ( ! drmCrtc - > mode_valid ) {
drmModeFreeCrtc ( drmCrtc ) ;
return nullptr ;
}
drmModeModeInfo * modeInfo = ( drmModeModeInfo * ) malloc ( sizeof ( drmModeModeInfo ) ) ;
if ( ! modeInfo ) {
drmModeFreeCrtc ( drmCrtc ) ;
return nullptr ;
}
* modeInfo = drmCrtc - > mode ;
drmModeFreeCrtc ( drmCrtc ) ;
return modeInfo ;
}
void Aquamarine : : SDRMConnector : : parseEDID ( std : : vector < uint8_t > data ) {
2024-07-02 13:12:47 +02:00
auto info = di_info_parse_edid ( data . data ( ) , data . size ( ) ) ;
if ( ! info ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: failed to parse edid " ) ;
return ;
}
auto edid = di_info_get_edid ( info ) ;
auto venProduct = di_edid_get_vendor_product ( edid ) ;
auto pnpID = std : : string { venProduct - > manufacturer , 3 } ;
if ( PNPIDS . contains ( pnpID ) )
make = PNPIDS . at ( pnpID ) ;
else
make = pnpID ;
auto mod = di_info_get_model ( info ) ;
auto ser = di_info_get_serial ( info ) ;
model = mod ? mod : " " ;
serial = ser ? ser : " " ;
di_info_destroy ( info ) ;
2024-06-23 19:40:40 +02:00
}
void Aquamarine : : SDRMConnector : : connect ( drmModeConnector * connector ) {
if ( output ) {
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Not connecting connector {} because it's already connected " , szName ) ) ;
return ;
}
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Connecting connector {}, CRTC ID {} " , szName , crtc ? crtc - > id : - 1 ) ) ;
output = SP < CDRMOutput > ( new CDRMOutput ( szName , backend , self . lock ( ) ) ) ;
output - > self = output ;
output - > connector = self . lock ( ) ;
backend - > backend - > log ( AQ_LOG_DEBUG , " drm: Dumping detected modes: " ) ;
auto currentModeInfo = getCurrentMode ( ) ;
for ( int i = 0 ; i < connector - > count_modes ; + + i ) {
auto & drmMode = connector - > modes [ i ] ;
if ( drmMode . flags & DRM_MODE_FLAG_INTERLACE ) {
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Skipping mode {} because it's interlaced " , i ) ) ;
continue ;
}
auto aqMode = makeShared < SOutputMode > ( ) ;
aqMode - > pixelSize = { drmMode . hdisplay , drmMode . vdisplay } ;
aqMode - > refreshRate = calculateRefresh ( drmMode ) ;
aqMode - > preferred = ( drmMode . type & DRM_MODE_TYPE_PREFERRED ) ;
2024-06-24 23:22:02 +02:00
aqMode - > modeInfo = drmMode ;
2024-06-23 19:40:40 +02:00
2024-07-15 13:03:00 -05:00
if ( i = = 1 )
fallbackMode = aqMode ;
2024-06-23 19:40:40 +02:00
output - > modes . emplace_back ( aqMode ) ;
if ( currentModeInfo & & std : : memcmp ( & drmMode , currentModeInfo , sizeof ( drmModeModeInfo ) ) ) {
output - > state - > setMode ( aqMode ) ;
//uint64_t modeID = 0;
// getDRMProp(backend->gpu->fd, crtc->id, crtc->props.mode_id, &modeID);
crtc - > refresh = calculateRefresh ( drmMode ) ;
}
backend - > backend - > log ( AQ_LOG_DEBUG ,
std : : format ( " drm: Mode {}: {}x{}@{:.2f}Hz {} " , i , ( int ) aqMode - > pixelSize . x , ( int ) aqMode - > pixelSize . y , aqMode - > refreshRate / 1000.0 ,
aqMode - > preferred ? " (preferred) " : " " ) ) ;
}
2024-07-18 10:09:17 +01:00
if ( ! currentModeInfo & & fallbackMode ) {
2024-07-15 13:03:00 -05:00
output - > state - > setMode ( fallbackMode ) ;
crtc - > refresh = calculateRefresh ( fallbackMode - > modeInfo . value ( ) ) ;
}
2024-06-23 19:40:40 +02:00
output - > physicalSize = { ( double ) connector - > mmWidth , ( double ) connector - > mmHeight } ;
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Physical size {} (mm) " , output - > physicalSize ) ) ;
switch ( connector - > subpixel ) {
case DRM_MODE_SUBPIXEL_NONE : output - > subpixel = eSubpixelMode : : AQ_SUBPIXEL_NONE ; break ;
case DRM_MODE_SUBPIXEL_UNKNOWN : output - > subpixel = eSubpixelMode : : AQ_SUBPIXEL_UNKNOWN ; break ;
case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB : output - > subpixel = eSubpixelMode : : AQ_SUBPIXEL_HORIZONTAL_RGB ; break ;
case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR : output - > subpixel = eSubpixelMode : : AQ_SUBPIXEL_HORIZONTAL_BGR ; break ;
case DRM_MODE_SUBPIXEL_VERTICAL_RGB : output - > subpixel = eSubpixelMode : : AQ_SUBPIXEL_VERTICAL_RGB ; break ;
case DRM_MODE_SUBPIXEL_VERTICAL_BGR : output - > subpixel = eSubpixelMode : : AQ_SUBPIXEL_VERTICAL_BGR ; break ;
default : output - > subpixel = eSubpixelMode : : AQ_SUBPIXEL_UNKNOWN ;
}
uint64_t prop = 0 ;
if ( getDRMProp ( backend - > gpu - > fd , id , props . non_desktop , & prop ) ) {
if ( prop = = 1 )
backend - > backend - > log ( AQ_LOG_DEBUG , " drm: Non-desktop connector " ) ;
output - > nonDesktop = prop ;
}
2024-07-04 15:33:40 +02:00
canDoVrr = props . vrr_capable & & crtc - > props . vrr_enabled & & getDRMProp ( backend - > gpu - > fd , id , props . vrr_capable , & prop ) & & prop ;
2024-06-24 23:22:02 +02:00
output - > vrrCapable = canDoVrr ;
2024-07-02 12:11:54 +02:00
backend - > backend - > log ( AQ_LOG_DEBUG ,
std : : format ( " drm: crtc is {} of vrr: props.vrr_capable -> {}, crtc->props.vrr_enabled -> {} " , ( canDoVrr ? " capable " : " incapable " ) , props . vrr_capable ,
crtc - > props . vrr_enabled ) ) ;
2024-06-23 19:40:40 +02:00
maxBpcBounds . fill ( 0 ) ;
if ( props . max_bpc & & ! introspectDRMPropRange ( backend - > gpu - > fd , props . max_bpc , maxBpcBounds . data ( ) , & maxBpcBounds [ 1 ] ) )
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Failed to check max_bpc " ) ;
size_t edidLen = 0 ;
uint8_t * edidData = ( uint8_t * ) getDRMPropBlob ( backend - > gpu - > fd , id , props . edid , & edidLen ) ;
std : : vector < uint8_t > edid { edidData , edidData + edidLen } ;
parseEDID ( edid ) ;
free ( edidData ) ;
edid = { } ;
// TODO: subconnectors
2024-07-06 17:38:13 +02:00
output - > make = make ;
output - > model = model ;
output - > serial = serial ;
output - > description = std : : format ( " {} {} {} ({}) " , make , model , serial , szName ) ;
output - > needsFrame = true ;
2024-07-06 17:45:47 +02:00
output - > supportsExplicit = backend - > drmProps . supportsTimelines & & crtc - > props . out_fence_ptr & & crtc - > primary - > props . in_fence_fd ;
2024-07-06 17:38:13 +02:00
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Explicit sync {} " , output - > supportsExplicit ? " supported " : " unsupported " ) ) ;
2024-06-23 19:40:40 +02:00
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Description {} " , output - > description ) ) ;
status = DRM_MODE_CONNECTED ;
2024-06-24 23:22:02 +02:00
if ( ! backend - > backend - > ready )
return ;
2024-07-11 20:41:53 +02:00
output - > swapchain = CSwapchain : : create ( backend - > backend - > primaryAllocator , backend - > self . lock ( ) ) ;
2024-07-15 13:03:00 -05:00
backend - > backend - > events . newOutput . emit ( SP < IOutput > ( output ) ) ;
2024-07-07 19:05:05 +03:00
output - > scheduleFrame ( IOutput : : AQ_SCHEDULE_NEW_CONNECTOR ) ;
2024-06-23 19:40:40 +02:00
}
void Aquamarine : : SDRMConnector : : disconnect ( ) {
if ( ! output ) {
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Not disconnecting connector {} because it's already disconnected " , szName ) ) ;
return ;
}
output - > events . destroy . emit ( ) ;
output . reset ( ) ;
status = DRM_MODE_DISCONNECTED ;
}
2024-06-28 14:02:56 +02:00
bool Aquamarine : : SDRMConnector : : commitState ( SDRMConnectorCommitData & data ) {
2024-06-24 23:22:02 +02:00
const bool ok = backend - > impl - > commit ( self . lock ( ) , data ) ;
if ( ok & & ! data . test )
applyCommit ( data ) ;
else
rollbackCommit ( data ) ;
return ok ;
}
void Aquamarine : : SDRMConnector : : applyCommit ( const SDRMConnectorCommitData & data ) {
2024-06-25 20:30:00 +02:00
crtc - > primary - > back = data . mainFB ;
2024-06-26 19:26:38 +02:00
if ( crtc - > cursor & & data . cursorFB )
2024-06-25 20:30:00 +02:00
crtc - > cursor - > back = data . cursorFB ;
2024-06-24 23:22:02 +02:00
2024-07-19 11:29:50 +02:00
if ( data . mainFB )
data . mainFB - > buffer - > lockedByBackend = true ;
if ( crtc - > cursor & & data . cursorFB )
data . cursorFB - > buffer - > lockedByBackend = true ;
2024-06-24 23:22:02 +02:00
pendingCursorFB . reset ( ) ;
if ( output - > state - > state ( ) . committed & COutputState : : AQ_OUTPUT_STATE_MODE )
refresh = calculateRefresh ( data . modeInfo ) ;
}
void Aquamarine : : SDRMConnector : : rollbackCommit ( const SDRMConnectorCommitData & data ) {
2024-06-26 19:26:38 +02:00
// cursors are applied regardless.
if ( crtc - > cursor & & data . cursorFB )
crtc - > cursor - > back = data . cursorFB ;
crtc - > pendingCursor . reset ( ) ;
2024-06-24 23:22:02 +02:00
}
void Aquamarine : : SDRMConnector : : onPresent ( ) {
2024-06-25 20:30:00 +02:00
crtc - > primary - > last = crtc - > primary - > front ;
crtc - > primary - > front = crtc - > primary - > back ;
2024-07-19 11:29:50 +02:00
if ( crtc - > primary - > last & & crtc - > primary - > last - > buffer ) {
crtc - > primary - > last - > buffer - > lockedByBackend = false ;
crtc - > primary - > last - > buffer - > events . backendRelease . emit ( ) ;
}
2024-07-18 12:30:43 +02:00
if ( crtc - > cursor ) {
2024-06-25 20:30:00 +02:00
crtc - > cursor - > last = crtc - > cursor - > front ;
crtc - > cursor - > front = crtc - > cursor - > back ;
2024-07-19 11:29:50 +02:00
if ( crtc - > cursor - > last & & crtc - > cursor - > last - > buffer ) {
crtc - > cursor - > last - > buffer - > lockedByBackend = false ;
crtc - > cursor - > last - > buffer - > events . backendRelease . emit ( ) ;
}
2024-06-25 20:30:00 +02:00
}
2024-06-24 23:22:02 +02:00
}
2024-06-23 19:40:40 +02:00
Aquamarine : : CDRMOutput : : ~ CDRMOutput ( ) {
2024-06-30 19:04:14 +02:00
backend - > backend - > removeIdleEvent ( frameIdle ) ;
connector - > isPageFlipPending = false ;
connector - > frameEventScheduled = false ;
2024-06-23 19:40:40 +02:00
}
bool Aquamarine : : CDRMOutput : : commit ( ) {
2024-06-24 23:22:02 +02:00
return commitState ( ) ;
2024-06-23 19:40:40 +02:00
}
bool Aquamarine : : CDRMOutput : : test ( ) {
2024-06-24 23:22:02 +02:00
return commitState ( true ) ;
}
2024-06-26 19:26:38 +02:00
void Aquamarine : : CDRMOutput : : setCursorVisible ( bool visible ) {
cursorVisible = visible ;
2024-07-07 19:05:05 +03:00
scheduleFrame ( AQ_SCHEDULE_CURSOR_VISIBLE ) ;
2024-06-26 19:26:38 +02:00
}
2024-06-24 23:22:02 +02:00
bool Aquamarine : : CDRMOutput : : commitState ( bool onlyTest ) {
if ( ! backend - > backend - > session - > active ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Session inactive " ) ;
return false ;
}
if ( ! connector - > crtc ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: No CRTC attached to output " ) ;
return false ;
}
const auto & STATE = state - > state ( ) ;
const uint32_t COMMITTED = STATE . committed ;
if ( ( COMMITTED & COutputState : : eOutputStateProperties : : AQ_OUTPUT_STATE_ENABLED ) & & STATE . enabled ) {
if ( ! STATE . mode & & STATE . customMode ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: No mode on enable commit " ) ;
return false ;
}
}
if ( STATE . adaptiveSync & & ! connector - > canDoVrr ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: No Adaptive sync support for output " ) ;
return false ;
}
if ( STATE . presentationMode = = AQ_OUTPUT_PRESENTATION_IMMEDIATE & & ! backend - > drmProps . supportsAsyncCommit ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: No Immediate presentation support in the backend " ) ;
return false ;
}
2024-06-26 22:25:45 +02:00
if ( ( COMMITTED & COutputState : : eOutputStateProperties : : AQ_OUTPUT_STATE_BUFFER ) & & ! STATE . buffer ) {
2024-06-24 23:22:02 +02:00
backend - > backend - > log ( AQ_LOG_ERROR , " drm: No buffer committed " ) ;
return false ;
}
2024-06-26 22:25:45 +02:00
if ( ( COMMITTED & COutputState : : eOutputStateProperties : : AQ_OUTPUT_STATE_BUFFER ) & & STATE . buffer - > attachments . has ( AQ_ATTACHMENT_DRM_KMS_UNIMPORTABLE ) ) {
2024-07-02 14:42:24 +02:00
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE , " drm: Cannot commit a KMS-unimportable buffer. " ) ) ;
2024-06-26 22:25:13 +02:00
return false ;
}
2024-06-24 23:22:02 +02:00
// If we are changing the rendering format, we may need to reconfigure the output (aka modeset)
// which may result in some glitches
const bool NEEDS_RECONFIG = COMMITTED &
( COutputState : : eOutputStateProperties : : AQ_OUTPUT_STATE_ENABLED | COutputState : : eOutputStateProperties : : AQ_OUTPUT_STATE_FORMAT |
COutputState : : eOutputStateProperties : : AQ_OUTPUT_STATE_MODE ) ;
const bool BLOCKING = NEEDS_RECONFIG | | ! ( COMMITTED & COutputState : : eOutputStateProperties : : AQ_OUTPUT_STATE_BUFFER ) ;
const auto MODE = STATE . mode ? STATE . mode : STATE . customMode ;
2024-07-20 16:58:42 +02:00
if ( ! MODE ) // modeless commits are invalid
return false ;
2024-07-24 17:41:13 +01:00
uint32_t flags = 0 ;
2024-06-24 23:22:02 +02:00
if ( ! onlyTest ) {
if ( NEEDS_RECONFIG ) {
if ( STATE . enabled )
backend - > backend - > log ( AQ_LOG_DEBUG ,
std : : format ( " drm: Modesetting {} with {}x{}@{:.2f}Hz " , name , ( int ) MODE - > pixelSize . x , ( int ) MODE - > pixelSize . y , MODE - > refreshRate / 1000.F ) ) ;
else
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Disabling output {} " , name ) ) ;
}
2024-06-26 23:21:02 +02:00
if ( ( NEEDS_RECONFIG | | ( COMMITTED & COutputState : : eOutputStateProperties : : AQ_OUTPUT_STATE_BUFFER ) ) & & connector - > isPageFlipPending ) {
2024-06-24 23:22:02 +02:00
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Cannot commit when a page-flip is awaiting " ) ;
return false ;
}
2024-06-26 19:26:38 +02:00
if ( STATE . enabled & & ( COMMITTED & COutputState : : eOutputStateProperties : : AQ_OUTPUT_STATE_BUFFER ) )
2024-06-24 23:22:02 +02:00
flags | = DRM_MODE_PAGE_FLIP_EVENT ;
2024-06-26 19:26:38 +02:00
if ( STATE . presentationMode = = AQ_OUTPUT_PRESENTATION_IMMEDIATE & & ( COMMITTED & COutputState : : eOutputStateProperties : : AQ_OUTPUT_STATE_BUFFER ) )
2024-06-24 23:22:02 +02:00
flags | = DRM_MODE_PAGE_FLIP_ASYNC ;
}
2024-07-17 23:13:39 +02:00
// we can't go further without a blit
if ( backend - > primary & & onlyTest )
return true ;
2024-06-24 23:22:02 +02:00
SDRMConnectorCommitData data ;
if ( STATE . buffer ) {
2024-07-02 14:42:24 +02:00
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE , " drm: Committed a buffer, updating state " ) ) ;
2024-06-24 23:22:02 +02:00
SP < CDRMFB > drmFB ;
2024-07-11 20:41:53 +02:00
if ( backend - > shouldBlit ( ) ) {
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE , " drm: Backend requires blit, blitting " ) ) ;
2024-07-12 09:34:30 +02:00
if ( ! mgpu . swapchain ) {
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE , " drm: No swapchain for blit, creating " ) ) ;
mgpu . swapchain = CSwapchain : : create ( backend - > mgpu . allocator , backend . lock ( ) ) ;
}
2024-07-18 00:06:04 +02:00
auto OPTIONS = swapchain - > currentOptions ( ) ;
auto bufDma = STATE . buffer - > dmabuf ( ) ;
OPTIONS . size = STATE . buffer - > size ;
if ( OPTIONS . format = = DRM_FORMAT_INVALID )
OPTIONS . format = bufDma . format ;
2024-07-11 20:41:53 +02:00
OPTIONS . multigpu = false ; // this is not a shared swapchain, and additionally, don't make it linear, nvidia would be mad
2024-07-12 16:18:27 +02:00
OPTIONS . cursor = false ;
OPTIONS . scanout = true ;
2024-07-12 09:34:30 +02:00
if ( ! mgpu . swapchain - > reconfigure ( OPTIONS ) ) {
2024-07-11 20:41:53 +02:00
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Backend requires blit, but the mgpu swapchain failed reconfiguring " ) ;
return false ;
}
2024-07-12 09:34:30 +02:00
auto NEWAQBUF = mgpu . swapchain - > next ( nullptr ) ;
2024-07-11 20:41:53 +02:00
if ( ! backend - > mgpu . renderer - > blit ( STATE . buffer , NEWAQBUF ) ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Backend requires blit, but blit failed " ) ;
return false ;
}
drmFB = CDRMFB : : create ( NEWAQBUF , backend , nullptr ) ; // will return attachment if present
} else
drmFB = CDRMFB : : create ( STATE . buffer , backend , nullptr ) ; // will return attachment if present
2024-06-24 23:22:02 +02:00
if ( ! drmFB ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Buffer failed to import to KMS " ) ;
return false ;
}
2024-07-08 18:45:59 +02:00
if ( drmFB - > dead ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: KMS buffer is dead?! " ) ;
return false ;
}
2024-06-24 23:22:02 +02:00
data . mainFB = drmFB ;
}
2024-07-11 12:19:41 +02:00
// sometimes, our consumer could f up the swapchain format and change it without the state changing
bool formatMismatch = false ;
if ( data . mainFB ) {
if ( const auto params = data . mainFB - > buffer - > dmabuf ( ) ; params . success & & params . format ! = STATE . drmFormat ) {
// formats mismatch. Update the state format and roll with it
backend - > backend - > log ( AQ_LOG_WARNING ,
std : : format ( " drm: Formats mismatch in commit, buffer is {} but output is set to {}. Modesetting to {} " , fourccToName ( params . format ) ,
fourccToName ( STATE . drmFormat ) , fourccToName ( params . format ) ) ) ;
state - > setFormat ( params . format ) ;
formatMismatch = true ;
flags & = ~ DRM_MODE_PAGE_FLIP_ASYNC ; // we cannot modeset with async pf
}
}
2024-06-26 19:26:38 +02:00
if ( connector - > crtc - > pendingCursor )
data . cursorFB = connector - > crtc - > pendingCursor ;
2024-07-20 15:55:18 +02:00
else if ( connector - > crtc - > cursor )
2024-07-08 21:22:56 +02:00
data . cursorFB = connector - > crtc - > cursor - > front ;
2024-06-26 19:26:38 +02:00
2024-06-29 19:28:51 +02:00
if ( data . cursorFB ) {
// verify cursor format. This might be wrong on NVIDIA where linear buffers
// fail to be created from gbm
// TODO: add an API to detect this and request drm_dumb linear buffers. Or do something,
// idk
2024-07-15 13:03:00 -05:00
if ( data . cursorFB - > dead | | data . cursorFB - > buffer - > dmabuf ( ) . modifier = = DRM_FORMAT_MOD_INVALID ) {
2024-07-02 14:42:24 +02:00
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE , " drm: Dropping invalid buffer for cursor plane " ) ) ;
2024-06-29 19:28:51 +02:00
data . cursorFB = nullptr ;
}
}
2024-07-11 12:19:41 +02:00
data . blocking = BLOCKING | | formatMismatch ;
data . modeset = NEEDS_RECONFIG | | lastCommitNoBuffer | | formatMismatch ;
2024-06-24 23:22:02 +02:00
data . flags = flags ;
data . test = onlyTest ;
if ( MODE - > modeInfo . has_value ( ) )
data . modeInfo = * MODE - > modeInfo ;
else
data . calculateMode ( connector ) ;
bool ok = connector - > commitState ( data ) ;
2024-07-18 00:06:04 +02:00
if ( ! ok & & ! data . modeset & & ! connector - > commitTainted ) {
// attempt to re-modeset, however, flip a tainted flag if the modesetting fails
// to avoid doing this over and over.
data . modeset = true ;
data . blocking = true ;
data . flags = DRM_MODE_PAGE_FLIP_EVENT ;
ok = connector - > commitState ( data ) ;
if ( ! ok )
connector - > commitTainted = true ;
}
2024-07-04 23:11:18 +02:00
if ( onlyTest | | ! ok )
2024-06-30 19:04:14 +02:00
return ok ;
2024-06-24 23:22:02 +02:00
2024-06-30 19:04:14 +02:00
events . commit . emit ( ) ;
2024-06-24 23:22:02 +02:00
state - > onCommit ( ) ;
2024-06-30 19:04:14 +02:00
lastCommitNoBuffer = ! data . mainFB ;
2024-07-05 14:24:10 +02:00
needsFrame = false ;
2024-06-30 19:04:14 +02:00
2024-07-18 00:06:04 +02:00
if ( ok )
connector - > commitTainted = false ;
2024-06-24 23:22:02 +02:00
return ok ;
2024-06-23 19:40:40 +02:00
}
SP < IBackendImplementation > Aquamarine : : CDRMOutput : : getBackend ( ) {
return backend . lock ( ) ;
}
bool Aquamarine : : CDRMOutput : : setCursor ( SP < IBuffer > buffer , const Vector2D & hotspot ) {
2024-07-12 15:52:23 +02:00
if ( buffer & & ! buffer - > dmabuf ( ) . success ) {
2024-07-12 09:34:30 +02:00
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Cursor buffer has to be a dmabuf " ) ;
return false ;
}
2024-06-26 19:26:38 +02:00
if ( ! buffer )
setCursorVisible ( false ) ;
else {
2024-07-12 09:34:30 +02:00
SP < CDRMFB > fb ;
if ( backend - > primary ) {
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE , " drm: Backend requires cursor blit, blitting " ) ) ;
if ( ! mgpu . cursorSwapchain ) {
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE , " drm: No cursorSwapchain for blit, creating " ) ) ;
mgpu . cursorSwapchain = CSwapchain : : create ( backend - > mgpu . allocator , backend . lock ( ) ) ;
}
auto OPTIONS = mgpu . cursorSwapchain - > currentOptions ( ) ;
OPTIONS . multigpu = false ;
OPTIONS . scanout = true ;
OPTIONS . cursor = true ;
OPTIONS . format = buffer - > dmabuf ( ) . format ;
OPTIONS . size = buffer - > dmabuf ( ) . size ;
OPTIONS . length = 2 ;
if ( ! mgpu . cursorSwapchain - > reconfigure ( OPTIONS ) ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Backend requires blit, but the mgpu cursorSwapchain failed reconfiguring " ) ;
return false ;
}
auto NEWAQBUF = mgpu . cursorSwapchain - > next ( nullptr ) ;
if ( ! backend - > mgpu . renderer - > blit ( buffer , NEWAQBUF ) ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Backend requires blit, but cursor blit failed " ) ;
return false ;
}
fb = CDRMFB : : create ( NEWAQBUF , backend , nullptr ) ; // will return attachment if present
} else
fb = CDRMFB : : create ( buffer , backend , nullptr ) ;
2024-06-26 19:26:38 +02:00
if ( ! fb ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Cursor buffer failed to import to KMS " ) ;
return false ;
}
2024-07-12 09:34:30 +02:00
cursorHotspot = hotspot ;
2024-06-26 19:26:38 +02:00
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm: Cursor buffer imported into KMS with id {} " , fb - > id ) ) ;
connector - > crtc - > pendingCursor = fb ;
2024-06-28 21:34:31 +02:00
cursorVisible = true ;
2024-06-26 19:26:38 +02:00
}
2024-07-07 19:05:05 +03:00
scheduleFrame ( AQ_SCHEDULE_CURSOR_SHAPE ) ;
2024-06-26 19:26:38 +02:00
return true ;
2024-06-23 19:40:40 +02:00
}
2024-07-20 21:56:18 +03:00
void Aquamarine : : CDRMOutput : : moveCursor ( const Vector2D & coord , bool skipShedule ) {
cursorPos = coord ;
// cursorVisible = true;
backend - > impl - > moveCursor ( connector , skipShedule ) ;
2024-06-23 19:40:40 +02:00
}
2024-07-07 19:05:05 +03:00
void Aquamarine : : CDRMOutput : : scheduleFrame ( const scheduleFrameReason reason ) {
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE ,
std : : format ( " CDRMOutput::scheduleFrame: reason {}, needsFrame {}, isPageFlipPending {}, frameEventScheduled {} " , ( uint32_t ) reason , needsFrame ,
connector - > isPageFlipPending , connector - > frameEventScheduled ) ) ) ;
2024-07-06 11:26:42 +02:00
needsFrame = true ;
2024-06-28 14:02:56 +02:00
if ( connector - > isPageFlipPending | | connector - > frameEventScheduled )
2024-06-24 23:22:02 +02:00
return ;
2024-06-28 14:02:56 +02:00
connector - > frameEventScheduled = true ;
2024-06-30 19:04:14 +02:00
backend - > backend - > addIdleEvent ( frameIdle ) ;
2024-06-23 19:40:40 +02:00
}
2024-06-26 19:26:38 +02:00
Vector2D Aquamarine : : CDRMOutput : : cursorPlaneSize ( ) {
2024-06-23 19:40:40 +02:00
return backend - > drmProps . cursorSize ;
}
2024-07-02 12:41:32 +02:00
size_t Aquamarine : : CDRMOutput : : getGammaSize ( ) {
if ( ! backend - > atomic ) {
backend - > log ( AQ_LOG_ERROR , " No support for gamma on the legacy iface " ) ;
return 0 ;
}
uint64_t size = 0 ;
if ( ! getDRMProp ( backend - > gpu - > fd , connector - > crtc - > id , connector - > crtc - > props . gamma_lut_size , & size ) ) {
backend - > log ( AQ_LOG_ERROR , " Couldn't get the gamma_size prop " ) ;
return 0 ;
}
return size ;
}
2024-07-14 04:06:13 -05:00
std : : vector < SDRMFormat > Aquamarine : : CDRMOutput : : getRenderFormats ( ) {
return connector - > crtc - > primary - > formats ;
}
2024-07-05 19:21:33 +02:00
int Aquamarine : : CDRMOutput : : getConnectorID ( ) {
return connector - > id ;
}
2024-06-24 23:22:02 +02:00
Aquamarine : : CDRMOutput : : CDRMOutput ( const std : : string & name_ , Hyprutils : : Memory : : CWeakPointer < CDRMBackend > backend_ , SP < SDRMConnector > connector_ ) :
2024-06-23 19:40:40 +02:00
backend ( backend_ ) , connector ( connector_ ) {
name = name_ ;
2024-06-30 19:04:14 +02:00
frameIdle = makeShared < std : : function < void ( void ) > > ( [ this ] ( ) {
connector - > frameEventScheduled = false ;
if ( connector - > isPageFlipPending )
return ;
events . frame . emit ( ) ;
} ) ;
2024-06-23 19:40:40 +02:00
}
2024-06-24 23:22:02 +02:00
2024-06-28 20:14:56 +02:00
SP < CDRMFB > Aquamarine : : CDRMFB : : create ( SP < IBuffer > buffer_ , Hyprutils : : Memory : : CWeakPointer < CDRMBackend > backend_ , bool * isNew ) {
2024-06-26 19:26:38 +02:00
SP < CDRMFB > fb ;
2024-06-28 20:14:56 +02:00
if ( isNew )
* isNew = true ;
2024-06-26 19:26:38 +02:00
if ( buffer_ - > attachments . has ( AQ_ATTACHMENT_DRM_BUFFER ) ) {
auto at = ( CDRMBufferAttachment * ) buffer_ - > attachments . get ( AQ_ATTACHMENT_DRM_BUFFER ) . get ( ) ;
fb = at - > fb ;
2024-07-02 14:42:24 +02:00
TRACE ( backend_ - > log ( AQ_LOG_TRACE , std : : format ( " drm: CDRMFB: buffer has drmfb attachment with fb {:x} " , ( uintptr_t ) fb . get ( ) ) ) ) ;
2024-06-26 19:26:38 +02:00
}
2024-06-28 20:14:56 +02:00
if ( fb ) {
if ( isNew )
* isNew = false ;
2024-06-26 19:26:38 +02:00
return fb ;
2024-06-28 20:14:56 +02:00
}
2024-06-26 19:26:38 +02:00
fb = SP < CDRMFB > ( new CDRMFB ( buffer_ , backend_ ) ) ;
2024-06-24 23:22:02 +02:00
if ( ! fb - > id )
return nullptr ;
2024-06-26 19:26:38 +02:00
buffer_ - > attachments . add ( makeShared < CDRMBufferAttachment > ( fb ) ) ;
2024-06-24 23:22:02 +02:00
return fb ;
}
Aquamarine : : CDRMFB : : CDRMFB ( SP < IBuffer > buffer_ , Hyprutils : : Memory : : CWeakPointer < CDRMBackend > backend_ ) : buffer ( buffer_ ) , backend ( backend_ ) {
2024-06-28 20:14:56 +02:00
import ( ) ;
}
void Aquamarine : : CDRMFB : : import ( ) {
2024-06-24 23:22:02 +02:00
auto attrs = buffer - > dmabuf ( ) ;
if ( ! attrs . success ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Buffer submitted has no dmabuf " ) ;
return ;
}
if ( buffer - > attachments . has ( AQ_ATTACHMENT_DRM_KMS_UNIMPORTABLE ) ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Buffer submitted is unimportable " ) ;
return ;
}
// TODO: check format
for ( int i = 0 ; i < attrs . planes ; + + i ) {
2024-06-26 19:26:38 +02:00
int ret = drmPrimeFDToHandle ( backend - > gpu - > fd , attrs . fds . at ( i ) , & boHandles [ i ] ) ;
2024-06-24 23:22:02 +02:00
if ( ret ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: drmPrimeFDToHandle failed " ) ;
drop ( ) ;
return ;
}
2024-07-02 14:42:24 +02:00
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE , std : : format ( " drm: CDRMFB: plane {} has fd {}, got handle {} " , i , attrs . fds . at ( i ) , boHandles . at ( i ) ) ) ) ;
2024-06-24 23:22:02 +02:00
}
id = submitBuffer ( ) ;
if ( ! id ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Failed to submit a buffer to KMS " ) ;
buffer - > attachments . add ( makeShared < CDRMBufferUnimportable > ( ) ) ;
drop ( ) ;
return ;
}
2024-07-02 14:42:24 +02:00
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE , std : : format ( " drm: new buffer {} " , id ) ) ) ;
2024-06-24 23:22:02 +02:00
2024-06-26 19:26:38 +02:00
// FIXME: why does this implode when it doesnt on wlroots or kwin?
2024-07-12 20:53:11 +02:00
closeHandles ( ) ;
2024-07-08 18:45:59 +02:00
listeners . destroyBuffer = buffer - > events . destroy . registerListener ( [ this ] ( std : : any d ) {
drop ( ) ;
dead = true ;
id = 0 ;
boHandles = { 0 , 0 , 0 , 0 } ;
} ) ;
2024-06-24 23:22:02 +02:00
}
2024-06-28 20:14:56 +02:00
void Aquamarine : : CDRMFB : : reimport ( ) {
drop ( ) ;
dropped = false ;
handlesClosed = false ;
boHandles = { 0 , 0 , 0 , 0 } ;
import ( ) ;
}
2024-06-24 23:22:02 +02:00
Aquamarine : : CDRMFB : : ~ CDRMFB ( ) {
drop ( ) ;
}
void Aquamarine : : CDRMFB : : closeHandles ( ) {
if ( handlesClosed )
return ;
handlesClosed = true ;
2024-06-26 19:26:38 +02:00
std : : vector < uint32_t > closed ;
for ( size_t i = 0 ; i < 4 ; + + i ) {
if ( boHandles . at ( i ) = = 0 )
continue ;
bool exists = false ;
for ( size_t j = 0 ; j < i ; + + j ) {
if ( boHandles . at ( i ) = = boHandles . at ( j ) ) {
exists = true ;
break ;
}
}
if ( exists )
2024-06-24 23:22:02 +02:00
continue ;
2024-06-26 19:26:38 +02:00
if ( drmCloseBufferHandle ( backend - > gpu - > fd , boHandles . at ( i ) ) )
2024-06-24 23:22:02 +02:00
backend - > backend - > log ( AQ_LOG_ERROR , " drm: drmCloseBufferHandle failed " ) ;
}
2024-07-08 23:04:12 +02:00
boHandles = { 0 , 0 , 0 , 0 } ;
2024-06-24 23:22:02 +02:00
}
void Aquamarine : : CDRMFB : : drop ( ) {
if ( dropped )
return ;
dropped = true ;
if ( ! id )
return ;
2024-06-28 20:14:56 +02:00
closeHandles ( ) ;
2024-07-02 14:42:24 +02:00
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE , std : : format ( " drm: dropping buffer {} " , id ) ) ) ;
2024-06-24 23:22:02 +02:00
int ret = drmModeCloseFB ( backend - > gpu - > fd , id ) ;
if ( ret = = - EINVAL )
ret = drmModeRmFB ( backend - > gpu - > fd , id ) ;
if ( ret )
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " drm: Failed to close a buffer: {} " , strerror ( - ret ) ) ) ;
}
uint32_t Aquamarine : : CDRMFB : : submitBuffer ( ) {
auto attrs = buffer - > dmabuf ( ) ;
uint32_t newID = 0 ;
2024-06-26 19:26:38 +02:00
std : : array < uint64_t , 4 > mods = { 0 , 0 , 0 , 0 } ;
2024-06-24 23:22:02 +02:00
for ( size_t i = 0 ; i < attrs . planes ; + + i ) {
2024-06-26 19:26:38 +02:00
mods [ i ] = attrs . modifier ;
2024-06-24 23:22:02 +02:00
}
if ( backend - > drmProps . supportsAddFb2Modifiers & & attrs . modifier ! = DRM_FORMAT_MOD_INVALID ) {
2024-07-02 14:42:24 +02:00
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE ,
std : : format ( " drm: Using drmModeAddFB2WithModifiers to import buffer into KMS: Size {} with format {} and mod {} " , attrs . size ,
fourccToName ( attrs . format ) , attrs . modifier ) ) ) ;
2024-06-24 23:22:02 +02:00
if ( drmModeAddFB2WithModifiers ( backend - > gpu - > fd , attrs . size . x , attrs . size . y , attrs . format , boHandles . data ( ) , attrs . strides . data ( ) , attrs . offsets . data ( ) , mods . data ( ) ,
& newID , DRM_MODE_FB_MODIFIERS ) ) {
2024-06-26 19:26:38 +02:00
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Failed to submit a buffer with drmModeAddFB2WithModifiers " ) ;
2024-06-24 23:22:02 +02:00
return 0 ;
}
} else {
if ( attrs . modifier ! = DRM_FORMAT_MOD_INVALID & & attrs . modifier ! = DRM_FORMAT_MOD_LINEAR ) {
backend - > backend - > log ( AQ_LOG_ERROR , " drm: drmModeAddFB2WithModifiers unsupported and buffer has explicit modifiers " ) ;
return 0 ;
}
2024-07-02 14:42:24 +02:00
TRACE ( backend - > backend - > log (
2024-06-24 23:22:02 +02:00
AQ_LOG_TRACE ,
2024-07-02 14:42:24 +02:00
std : : format ( " drm: Using drmModeAddFB2 to import buffer into KMS: Size {} with format {} and mod {} " , attrs . size , fourccToName ( attrs . format ) , attrs . modifier ) ) ) ;
2024-06-24 23:22:02 +02:00
if ( drmModeAddFB2 ( backend - > gpu - > fd , attrs . size . x , attrs . size . y , attrs . format , boHandles . data ( ) , attrs . strides . data ( ) , attrs . offsets . data ( ) , & newID , 0 ) ) {
2024-06-26 19:26:38 +02:00
backend - > backend - > log ( AQ_LOG_ERROR , " drm: Failed to submit a buffer with drmModeAddFB2 " ) ;
2024-06-24 23:22:02 +02:00
return 0 ;
}
}
return newID ;
}
void Aquamarine : : SDRMConnectorCommitData : : calculateMode ( Hyprutils : : Memory : : CSharedPointer < SDRMConnector > connector ) {
2024-07-11 23:06:26 +02:00
if ( ! connector | | ! connector - > output | | ! connector - > output - > state )
return ;
2024-07-10 12:28:38 +02:00
const auto & STATE = connector - > output - > state - > state ( ) ;
const auto MODE = STATE . mode ? STATE . mode : STATE . customMode ;
if ( ! MODE ) {
connector - > backend - > log ( AQ_LOG_ERROR , " drm: no mode in calculateMode?? " ) ;
return ;
}
2024-06-24 23:22:02 +02:00
di_cvt_options options = {
. red_blank_ver = DI_CVT_REDUCED_BLANKING_NONE ,
. h_pixels = ( int ) MODE - > pixelSize . x ,
. v_lines = ( int ) MODE - > pixelSize . y ,
. ip_freq_rqd = MODE - > refreshRate ? MODE - > refreshRate / 1000.0 : 60.0 ,
} ;
di_cvt_timing timing ;
di_cvt_compute ( & timing , & options ) ;
uint16_t hsync_start = ( int ) MODE - > pixelSize . y + timing . h_front_porch ;
uint16_t vsync_start = timing . v_lines_rnd + timing . v_front_porch ;
uint16_t hsync_end = hsync_start + timing . h_sync ;
uint16_t vsync_end = vsync_start + timing . v_sync ;
modeInfo = ( drmModeModeInfo ) {
. clock = ( uint32_t ) std : : round ( timing . act_pixel_freq * 1000 ) ,
. hdisplay = ( uint16_t ) MODE - > pixelSize . y ,
. hsync_start = hsync_start ,
. hsync_end = hsync_end ,
. htotal = ( uint16_t ) ( hsync_end + timing . h_back_porch ) ,
. vdisplay = ( uint16_t ) timing . v_lines_rnd ,
. vsync_start = vsync_start ,
. vsync_end = vsync_end ,
. vtotal = ( uint16_t ) ( vsync_end + timing . v_back_porch ) ,
. vrefresh = ( uint32_t ) std : : round ( timing . act_frame_rate ) ,
. flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC ,
} ;
snprintf ( modeInfo . name , sizeof ( modeInfo . name ) , " %dx%d " , ( int ) MODE - > pixelSize . x , ( int ) MODE - > pixelSize . y ) ;
}
2024-06-26 19:26:38 +02:00
Aquamarine : : CDRMBufferAttachment : : CDRMBufferAttachment ( SP < CDRMFB > fb_ ) : fb ( fb_ ) {
;
}
2024-07-05 19:21:33 +02:00
SP < CDRMLease > Aquamarine : : CDRMLease : : create ( std : : vector < SP < IOutput > > outputs ) {
if ( outputs . empty ( ) )
return nullptr ;
if ( outputs . at ( 0 ) - > getBackend ( ) - > type ( ) ! = AQ_BACKEND_DRM )
return nullptr ;
auto backend = ( ( CDRMBackend * ) outputs . at ( 0 ) - > getBackend ( ) . get ( ) ) - > self . lock ( ) ;
for ( auto & o : outputs ) {
if ( o - > getBackend ( ) ! = backend ) {
backend - > log ( AQ_LOG_ERROR , " drm lease: Mismatched backends " ) ;
return nullptr ;
}
}
std : : vector < uint32_t > objects ;
auto lease = SP < CDRMLease > ( new CDRMLease ) ;
for ( auto & o : outputs ) {
auto drmo = ( ( CDRMOutput * ) o . get ( ) ) - > self . lock ( ) ;
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm lease: output {}, connector {} " , drmo - > name , drmo - > connector - > id ) ) ;
// FIXME: do we have to alloc a crtc here?
if ( ! drmo - > connector - > crtc ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " drm lease: output {} has no crtc " , drmo - > name ) ) ;
return nullptr ;
}
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm lease: crtc {}, primary {} " , drmo - > connector - > crtc - > id , drmo - > connector - > crtc - > primary - > id ) ) ;
objects . push_back ( drmo - > connector - > id ) ;
objects . push_back ( drmo - > connector - > crtc - > id ) ;
objects . push_back ( drmo - > connector - > crtc - > primary - > id ) ;
if ( drmo - > connector - > crtc - > cursor )
objects . push_back ( drmo - > connector - > crtc - > cursor - > id ) ;
lease - > outputs . emplace_back ( drmo ) ;
}
backend - > log ( AQ_LOG_DEBUG , " drm lease: issuing a lease " ) ;
int leaseFD = drmModeCreateLease ( backend - > gpu - > fd , objects . data ( ) , objects . size ( ) , O_CLOEXEC , & lease - > lesseeID ) ;
if ( leaseFD < 0 ) {
backend - > log ( AQ_LOG_ERROR , " drm lease: drm rejected a lease " ) ;
return nullptr ;
}
for ( auto & o : lease - > outputs ) {
o - > lease = lease ;
}
lease - > leaseFD = leaseFD ;
backend - > log ( AQ_LOG_DEBUG , std : : format ( " drm lease: lease granted with lessee id {} " , lease - > lesseeID ) ) ;
return lease ;
}
Aquamarine : : CDRMLease : : ~ CDRMLease ( ) {
if ( active )
terminate ( ) ;
else
destroy ( ) ;
}
void Aquamarine : : CDRMLease : : terminate ( ) {
active = false ;
if ( drmModeRevokeLease ( backend - > gpu - > fd , lesseeID ) < 0 )
backend - > log ( AQ_LOG_ERROR , " drm lease: Failed to revoke lease " ) ;
destroy ( ) ;
}
void Aquamarine : : CDRMLease : : destroy ( ) {
events . destroy . emit ( ) ;
}