2024-07-11 20:41:53 +02:00
# include "Renderer.hpp"
# include <xf86drm.h>
# include <xf86drmMode.h>
2025-01-23 02:39:30 +05:00
# include <algorithm>
2024-07-11 20:41:53 +02:00
# include <cstring>
# include <fcntl.h>
2024-08-06 20:06:30 +00:00
# include <unistd.h>
2024-07-11 20:41:53 +02:00
# include "Math.hpp"
# include "Shared.hpp"
2024-07-11 22:17:19 +02:00
# include "FormatUtils.hpp"
2024-07-12 23:23:39 +02:00
# include <aquamarine/allocator/GBM.hpp>
2024-07-11 20:41:53 +02:00
using namespace Aquamarine ;
using namespace Hyprutils : : Memory ;
using namespace Hyprutils : : Math ;
# define SP CSharedPointer
# define WP CWeakPointer
2025-02-01 09:17:49 -06:00
// macros
# define GLCALL(__CALL__) \
{ \
__CALL__ ; \
auto err = glGetError ( ) ; \
if ( err ! = GL_NO_ERROR ) { \
backend - > log ( AQ_LOG_ERROR , \
std : : format ( " [GLES] Error in call at {}@{}: 0x{:x} " , __LINE__ , \
( [ ] ( ) constexpr - > std : : string { return std : : string ( __FILE__ ) . substr ( std : : string ( __FILE__ ) . find_last_of ( ' / ' ) + 1 ) ; } ) ( ) , err ) ) ; \
} \
}
2024-07-11 20:41:53 +02:00
// static funcs
2025-02-01 09:17:49 -06:00
static WP < CBackend > gBackend ;
2024-07-11 20:41:53 +02:00
// ------------------- shader utils
2025-02-01 09:17:49 -06:00
static GLuint compileShader ( const GLuint & type , std : : string src ) {
2024-07-11 20:41:53 +02:00
auto shader = glCreateShader ( type ) ;
auto shaderSource = src . c_str ( ) ;
glShaderSource ( shader , 1 , ( const GLchar * * ) & shaderSource , nullptr ) ;
glCompileShader ( shader ) ;
GLint ok ;
glGetShaderiv ( shader , GL_COMPILE_STATUS , & ok ) ;
if ( ok = = GL_FALSE )
return 0 ;
return shader ;
}
2025-02-01 09:17:49 -06:00
static GLuint createProgram ( const std : : string & vert , const std : : string & frag ) {
2024-07-11 20:41:53 +02:00
auto vertCompiled = compileShader ( GL_VERTEX_SHADER , vert ) ;
if ( vertCompiled = = 0 )
return 0 ;
auto fragCompiled = compileShader ( GL_FRAGMENT_SHADER , frag ) ;
if ( fragCompiled = = 0 )
return 0 ;
auto prog = glCreateProgram ( ) ;
glAttachShader ( prog , vertCompiled ) ;
glAttachShader ( prog , fragCompiled ) ;
glLinkProgram ( prog ) ;
glDetachShader ( prog , vertCompiled ) ;
glDetachShader ( prog , fragCompiled ) ;
glDeleteShader ( vertCompiled ) ;
glDeleteShader ( fragCompiled ) ;
GLint ok ;
glGetProgramiv ( prog , GL_LINK_STATUS , & ok ) ;
if ( ok = = GL_FALSE )
return 0 ;
return prog ;
}
inline const std : : string VERT_SRC = R " #(
uniform mat3 proj ;
attribute vec2 pos ;
attribute vec2 texcoord ;
varying vec2 v_texcoord ;
void main ( ) {
gl_Position = vec4 ( proj * vec3 ( pos , 1.0 ) , 1.0 ) ;
v_texcoord = texcoord ;
} ) # " ;
inline const std : : string FRAG_SRC = R " #(
precision highp float ;
varying vec2 v_texcoord ; // is in 0-1
uniform sampler2D tex ;
void main ( ) {
gl_FragColor = texture2D ( tex , v_texcoord ) ;
} ) # " ;
2024-07-12 17:20:08 +02:00
inline const std : : string FRAG_SRC_EXT = R " #(
# extension GL_OES_EGL_image_external : require
precision highp float ;
varying vec2 v_texcoord ; // is in 0-1
uniform samplerExternalOES texture0 ;
void main ( ) {
gl_FragColor = texture2D ( texture0 , v_texcoord ) ;
} ) # " ;
2024-07-11 20:41:53 +02:00
// ------------------- egl stuff
2025-02-01 09:17:49 -06:00
static inline void loadGLProc ( void * pProc , const char * name ) {
2024-07-11 20:41:53 +02:00
void * proc = ( void * ) eglGetProcAddress ( name ) ;
2025-01-23 02:39:30 +05:00
if ( ! proc ) {
2025-02-01 09:17:49 -06:00
gBackend - > log ( AQ_LOG_ERROR , std : : format ( " eglGetProcAddress({}) failed, the display driver doesn't support it " , name ) ) ;
2024-07-11 20:41:53 +02:00
abort ( ) ;
}
* ( void * * ) pProc = proc ;
}
2025-02-01 09:17:49 -06:00
static enum eBackendLogLevel eglLogToLevel ( EGLint type ) {
switch ( type ) {
case EGL_DEBUG_MSG_CRITICAL_KHR : return AQ_LOG_CRITICAL ;
case EGL_DEBUG_MSG_ERROR_KHR : return AQ_LOG_ERROR ;
case EGL_DEBUG_MSG_WARN_KHR : return AQ_LOG_WARNING ;
case EGL_DEBUG_MSG_INFO_KHR : return AQ_LOG_DEBUG ;
default : return AQ_LOG_DEBUG ;
}
}
static const char * eglErrorToString ( EGLint error ) {
switch ( error ) {
case EGL_SUCCESS : return " EGL_SUCCESS " ;
case EGL_NOT_INITIALIZED : return " EGL_NOT_INITIALIZED " ;
case EGL_BAD_ACCESS : return " EGL_BAD_ACCESS " ;
case EGL_BAD_ALLOC : return " EGL_BAD_ALLOC " ;
case EGL_BAD_ATTRIBUTE : return " EGL_BAD_ATTRIBUTE " ;
case EGL_BAD_CONTEXT : return " EGL_BAD_CONTEXT " ;
case EGL_BAD_CONFIG : return " EGL_BAD_CONFIG " ;
case EGL_BAD_CURRENT_SURFACE : return " EGL_BAD_CURRENT_SURFACE " ;
case EGL_BAD_DISPLAY : return " EGL_BAD_DISPLAY " ;
case EGL_BAD_DEVICE_EXT : return " EGL_BAD_DEVICE_EXT " ;
case EGL_BAD_SURFACE : return " EGL_BAD_SURFACE " ;
case EGL_BAD_MATCH : return " EGL_BAD_MATCH " ;
case EGL_BAD_PARAMETER : return " EGL_BAD_PARAMETER " ;
case EGL_BAD_NATIVE_PIXMAP : return " EGL_BAD_NATIVE_PIXMAP " ;
case EGL_BAD_NATIVE_WINDOW : return " EGL_BAD_NATIVE_WINDOW " ;
case EGL_CONTEXT_LOST : return " EGL_CONTEXT_LOST " ;
default : return " Unknown " ;
}
}
static void eglLog ( EGLenum error , const char * command , EGLint type , EGLLabelKHR thread , EGLLabelKHR obj , const char * msg ) {
gBackend - > log ( eglLogToLevel ( type ) , std : : format ( " [EGL] Command {} errored out with {} (0x{}): {} " , command , eglErrorToString ( error ) , error , msg ) ) ;
}
static bool drmDeviceHasName ( const drmDevice * device , const std : : string & name ) {
for ( size_t i = 0 ; i < DRM_NODE_MAX ; i + + ) {
if ( ! ( device - > available_nodes & ( 1 < < i ) ) )
continue ;
if ( device - > nodes [ i ] = = name )
return true ;
}
return false ;
}
2024-07-11 20:41:53 +02:00
// -------------------
2025-02-01 09:17:49 -06:00
EGLDeviceEXT CDRMRenderer : : eglDeviceFromDRMFD ( int drmFD ) {
EGLint nDevices = 0 ;
if ( ! proc . eglQueryDevicesEXT ( 0 , nullptr , & nDevices ) ) {
backend - > log ( AQ_LOG_ERROR , " CDRMRenderer(drm): eglQueryDevicesEXT failed " ) ;
return EGL_NO_DEVICE_EXT ;
}
if ( nDevices < = 0 ) {
backend - > log ( AQ_LOG_ERROR , " CDRMRenderer(drm): no devices " ) ;
return EGL_NO_DEVICE_EXT ;
}
std : : vector < EGLDeviceEXT > devices ;
devices . resize ( nDevices ) ;
if ( ! proc . eglQueryDevicesEXT ( nDevices , devices . data ( ) , & nDevices ) ) {
backend - > log ( AQ_LOG_ERROR , " CDRMRenderer(drm): eglQueryDevicesEXT failed (2) " ) ;
return EGL_NO_DEVICE_EXT ;
}
drmDevice * drmDev = nullptr ;
if ( int ret = drmGetDevice ( drmFD , & drmDev ) ; ret < 0 ) {
backend - > log ( AQ_LOG_ERROR , " CDRMRenderer(drm): drmGetDevice failed " ) ;
drmFreeDevice ( & drmDev ) ;
return EGL_NO_DEVICE_EXT ;
}
for ( auto const & d : devices ) {
auto devName = proc . eglQueryDeviceStringEXT ( d , EGL_DRM_DEVICE_FILE_EXT ) ;
if ( ! devName )
continue ;
if ( drmDeviceHasName ( drmDev , devName ) ) {
backend - > log ( AQ_LOG_DEBUG , std : : format ( " CDRMRenderer(drm): Using device {} " , devName ) ) ;
drmFreeDevice ( & drmDev ) ;
return d ;
}
}
drmFreeDevice ( & drmDev ) ;
return EGL_NO_DEVICE_EXT ;
}
2024-07-12 11:16:47 +02:00
std : : optional < std : : vector < std : : pair < uint64_t , bool > > > CDRMRenderer : : getModsForFormat ( EGLint format ) {
// TODO: return std::expected when clang supports it
EGLint len = 0 ;
2025-02-01 09:17:49 -06:00
if ( ! proc . eglQueryDmaBufModifiersEXT ( egl . display , format , 0 , nullptr , nullptr , & len ) ) {
2024-07-12 11:16:47 +02:00
backend - > log ( AQ_LOG_ERROR , std : : format ( " EGL: eglQueryDmaBufModifiersEXT failed for format {} " , fourccToName ( format ) ) ) ;
return std : : nullopt ;
}
if ( len < = 0 )
return std : : vector < std : : pair < uint64_t , bool > > { } ;
std : : vector < uint64_t > mods ;
std : : vector < EGLBoolean > external ;
mods . resize ( len ) ;
external . resize ( len ) ;
2025-02-01 09:17:49 -06:00
proc . eglQueryDmaBufModifiersEXT ( egl . display , format , len , mods . data ( ) , external . data ( ) , & len ) ;
2024-07-12 11:16:47 +02:00
std : : vector < std : : pair < uint64_t , bool > > result ;
2025-01-23 02:39:30 +05:00
result . reserve ( mods . size ( ) ) ;
2025-02-01 09:17:49 -06:00
bool linearIsExternal = false ;
2024-07-12 11:16:47 +02:00
for ( size_t i = 0 ; i < mods . size ( ) ; + + i ) {
2025-02-01 09:17:49 -06:00
if ( external . at ( i ) & & mods . at ( i ) = = DRM_FORMAT_MOD_LINEAR )
linearIsExternal = true ;
2025-01-23 02:39:30 +05:00
result . emplace_back ( mods . at ( i ) , external . at ( i ) ) ;
2024-07-12 11:16:47 +02:00
}
2025-02-01 09:17:49 -06:00
// if the driver doesn't mark linear as external, add it. It's allowed unless the driver says otherwise. (e.g. nvidia)
if ( ! linearIsExternal & & std : : ranges : : find ( mods , DRM_FORMAT_MOD_LINEAR ) = = mods . end ( ) & & mods . size ( ) = = 0 )
2025-01-23 02:39:30 +05:00
result . emplace_back ( DRM_FORMAT_MOD_LINEAR , true ) ;
2024-07-12 11:16:47 +02:00
return result ;
}
bool CDRMRenderer : : initDRMFormats ( ) {
std : : vector < EGLint > formats ;
EGLint len = 0 ;
2025-02-01 09:17:49 -06:00
proc . eglQueryDmaBufFormatsEXT ( egl . display , 0 , nullptr , & len ) ;
2024-07-12 11:16:47 +02:00
formats . resize ( len ) ;
2025-02-01 09:17:49 -06:00
proc . eglQueryDmaBufFormatsEXT ( egl . display , len , formats . data ( ) , & len ) ;
2024-07-12 11:16:47 +02:00
if ( formats . size ( ) = = 0 ) {
backend - > log ( AQ_LOG_ERROR , " EGL: Failed to get formats " ) ;
return false ;
}
TRACE ( backend - > log ( AQ_LOG_TRACE , " EGL: Supported formats: " ) ) ;
2024-07-24 17:41:13 +01:00
std : : vector < SGLFormat > dmaFormats ;
2025-02-01 09:17:49 -06:00
dmaFormats . reserve ( formats . size ( ) ) ;
2024-07-12 11:16:47 +02:00
2024-08-27 20:04:26 +02:00
for ( auto const & fmt : formats ) {
2024-07-12 11:16:47 +02:00
std : : vector < std : : pair < uint64_t , bool > > mods ;
2025-02-01 09:17:49 -06:00
if ( exts . EXT_image_dma_buf_import_modifiers ) {
auto ret = getModsForFormat ( fmt ) ;
if ( ! ret . has_value ( ) )
continue ;
2024-07-12 11:16:47 +02:00
2025-02-01 09:17:49 -06:00
mods = * ret ;
}
2024-07-12 11:16:47 +02:00
hasModifiers = hasModifiers | | mods . size ( ) > 0 ;
// EGL can always do implicit modifiers.
2025-01-23 02:39:30 +05:00
mods . emplace_back ( DRM_FORMAT_MOD_INVALID , true ) ;
2024-07-12 11:16:47 +02:00
2024-08-27 20:04:26 +02:00
for ( auto const & [ mod , external ] : mods ) {
2024-07-24 17:41:13 +01:00
dmaFormats . push_back ( SGLFormat {
2024-07-12 11:16:47 +02:00
. drmFormat = ( uint32_t ) fmt ,
. modifier = mod ,
. external = external ,
} ) ;
}
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " EGL: GPU Supports Format {} (0x{:x}) " , fourccToName ( ( uint32_t ) fmt ) , fmt ) ) ) ;
2024-08-27 20:04:26 +02:00
for ( auto const & [ mod , external ] : mods ) {
2024-07-12 11:16:47 +02:00
auto modName = drmGetFormatModifierName ( mod ) ;
2024-07-12 17:20:08 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " EGL: | {}with modifier 0x{:x}: {} " , ( external ? " external only " : " " ) , mod , modName ? modName : " ?unknown? " ) ) ) ;
2024-07-12 11:16:47 +02:00
free ( modName ) ;
}
}
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " EGL: Found {} formats " , dmaFormats . size ( ) ) ) ) ;
if ( dmaFormats . empty ( ) ) {
backend - > log ( AQ_LOG_ERROR , " EGL: No formats " ) ;
return false ;
}
this - > formats = dmaFormats ;
return true ;
}
2025-01-10 20:39:08 +02:00
Aquamarine : : CDRMRenderer : : ~ CDRMRenderer ( ) {
2025-02-01 09:17:49 -06:00
if ( egl . display )
eglMakeCurrent ( egl . display , EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT ) ;
if ( egl . display & & egl . context ! = EGL_NO_CONTEXT & & egl . context ! = nullptr )
eglDestroyContext ( egl . display , egl . context ) ;
2025-01-10 20:39:08 +02:00
2025-02-01 09:17:49 -06:00
if ( egl . display )
eglTerminate ( egl . display ) ;
2025-01-10 20:39:08 +02:00
eglReleaseThread ( ) ;
}
2025-02-01 09:17:49 -06:00
void CDRMRenderer : : loadEGLAPI ( ) {
2025-01-23 02:39:30 +05:00
const std : : string EGLEXTENSIONS = eglQueryString ( EGL_NO_DISPLAY , EGL_EXTENSIONS ) ;
2024-07-11 20:41:53 +02:00
2025-02-01 09:17:49 -06:00
backend - > log ( AQ_LOG_DEBUG , std : : format ( " Supported EGL client extensions: ({}) {} " , std : : count ( EGLEXTENSIONS . begin ( ) , EGLEXTENSIONS . end ( ) , ' ' ) , EGLEXTENSIONS ) ) ;
exts . KHR_display_reference = EGLEXTENSIONS . contains ( " KHR_display_reference " ) ;
exts . EXT_platform_device = EGLEXTENSIONS . contains ( " EXT_platform_device " ) ;
exts . KHR_platform_gbm = EGLEXTENSIONS . contains ( " KHR_platform_gbm " ) ;
loadGLProc ( & proc . eglGetPlatformDisplayEXT , " eglGetPlatformDisplayEXT " ) ;
loadGLProc ( & proc . eglCreateImageKHR , " eglCreateImageKHR " ) ;
loadGLProc ( & proc . eglDestroyImageKHR , " eglDestroyImageKHR " ) ;
loadGLProc ( & proc . eglQueryDmaBufFormatsEXT , " eglQueryDmaBufFormatsEXT " ) ;
loadGLProc ( & proc . eglQueryDmaBufModifiersEXT , " eglQueryDmaBufModifiersEXT " ) ;
loadGLProc ( & proc . glEGLImageTargetTexture2DOES , " glEGLImageTargetTexture2DOES " ) ;
loadGLProc ( & proc . glEGLImageTargetRenderbufferStorageOES , " glEGLImageTargetRenderbufferStorageOES " ) ;
loadGLProc ( & proc . eglDestroySyncKHR , " eglDestroySyncKHR " ) ;
loadGLProc ( & proc . eglWaitSyncKHR , " eglWaitSyncKHR " ) ;
loadGLProc ( & proc . eglCreateSyncKHR , " eglCreateSyncKHR " ) ;
loadGLProc ( & proc . eglDupNativeFenceFDANDROID , " eglDupNativeFenceFDANDROID " ) ;
2025-03-01 20:07:00 -06:00
loadGLProc ( & proc . glReadnPixelsEXT , " glReadnPixelsEXT " ) ;
2025-02-01 09:17:49 -06:00
if ( EGLEXTENSIONS . contains ( " EGL_EXT_device_base " ) | | EGLEXTENSIONS . contains ( " EGL_EXT_device_enumeration " ) )
loadGLProc ( & proc . eglQueryDevicesEXT , " eglQueryDevicesEXT " ) ;
if ( EGLEXTENSIONS . contains ( " EGL_EXT_device_base " ) | | EGLEXTENSIONS . contains ( " EGL_EXT_device_query " ) )
loadGLProc ( & proc . eglQueryDeviceStringEXT , " eglQueryDeviceStringEXT " ) ;
if ( EGLEXTENSIONS . contains ( " EGL_KHR_debug " ) ) {
loadGLProc ( & proc . eglDebugMessageControlKHR , " eglDebugMessageControlKHR " ) ;
static const EGLAttrib debugAttrs [ ] = {
EGL_DEBUG_MSG_CRITICAL_KHR , EGL_TRUE , EGL_DEBUG_MSG_ERROR_KHR , EGL_TRUE , EGL_DEBUG_MSG_WARN_KHR , EGL_TRUE , EGL_DEBUG_MSG_INFO_KHR , EGL_TRUE , EGL_NONE ,
} ;
proc . eglDebugMessageControlKHR ( : : eglLog , debugAttrs ) ;
2024-07-11 20:41:53 +02:00
}
2025-02-01 09:17:49 -06:00
if ( EGLEXTENSIONS . contains ( " EXT_platform_device " ) ) {
loadGLProc ( & proc . eglQueryDevicesEXT , " eglQueryDevicesEXT " ) ;
loadGLProc ( & proc . eglQueryDeviceStringEXT , " eglQueryDeviceStringEXT " ) ;
2024-07-11 20:41:53 +02:00
}
2025-02-01 09:17:49 -06:00
RASSERT ( eglBindAPI ( EGL_OPENGL_ES_API ) ! = EGL_FALSE , " Couldn't bind to EGL's opengl ES API. This means your gpu driver f'd up. This is not a Hyprland or Aquamarine issue. " ) ;
}
2024-08-05 00:27:09 +02:00
2025-02-01 09:17:49 -06:00
void CDRMRenderer : : initContext ( bool GLES2 ) {
RASSERT ( egl . display ! = nullptr & & egl . display ! = EGL_NO_DISPLAY , " CDRMRenderer: Can't create EGL context without display " ) ;
2024-07-11 20:41:53 +02:00
2025-02-01 09:17:49 -06:00
EGLint major , minor ;
if ( eglInitialize ( egl . display , & major , & minor ) = = EGL_FALSE ) {
backend - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, eglInitialize failed " ) ;
return ;
2024-07-11 20:41:53 +02:00
}
2025-02-01 09:17:49 -06:00
std : : string EGLEXTENSIONS = eglQueryString ( egl . display , EGL_EXTENSIONS ) ;
exts . IMG_context_priority = EGLEXTENSIONS . contains ( " IMG_context_priority " ) ;
exts . EXT_create_context_robustness = EGLEXTENSIONS . contains ( " EXT_create_context_robustness " ) ;
exts . EXT_image_dma_buf_import = EGLEXTENSIONS . contains ( " EXT_image_dma_buf_import " ) ;
exts . EXT_image_dma_buf_import_modifiers = EGLEXTENSIONS . contains ( " EXT_image_dma_buf_import_modifiers " ) ;
std : : vector < EGLint > attrs ;
if ( exts . IMG_context_priority ) {
backend - > log ( AQ_LOG_DEBUG , " CDRMRenderer: IMG_context_priority supported, requesting high " ) ;
attrs . push_back ( EGL_CONTEXT_PRIORITY_LEVEL_IMG ) ;
attrs . push_back ( EGL_CONTEXT_PRIORITY_HIGH_IMG ) ;
2024-07-11 20:41:53 +02:00
}
2025-02-01 09:17:49 -06:00
if ( exts . EXT_create_context_robustness ) {
backend - > log ( AQ_LOG_DEBUG , " CDRMRenderer: EXT_create_context_robustness supported, requesting lose on reset " ) ;
attrs . push_back ( EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT ) ;
attrs . push_back ( EGL_LOSE_CONTEXT_ON_RESET_EXT ) ;
2024-07-12 11:16:47 +02:00
}
2025-02-01 09:17:49 -06:00
attrs . push_back ( EGL_CONTEXT_OPENGL_DEBUG ) ;
attrs . push_back ( Aquamarine : : isTrace ( ) ? EGL_TRUE : EGL_FALSE ) ;
auto attrsNoVer = attrs ;
if ( GLES2 ) {
attrs . push_back ( EGL_CONTEXT_MAJOR_VERSION ) ;
attrs . push_back ( 2 ) ;
attrs . push_back ( EGL_CONTEXT_MINOR_VERSION ) ;
attrs . push_back ( 0 ) ;
} else {
attrs . push_back ( EGL_CONTEXT_MAJOR_VERSION ) ;
attrs . push_back ( 3 ) ;
attrs . push_back ( EGL_CONTEXT_MINOR_VERSION ) ;
attrs . push_back ( 2 ) ;
2024-07-12 11:16:47 +02:00
}
2025-02-01 09:17:49 -06:00
attrs . push_back ( EGL_NONE ) ;
egl . context = eglCreateContext ( egl . display , EGL_NO_CONFIG_KHR , EGL_NO_CONTEXT , attrs . data ( ) ) ;
if ( egl . context = = EGL_NO_CONTEXT ) {
if ( GLES2 ) {
backend - > log ( AQ_LOG_ERROR , " CDRMRenderer: Can't create renderer, eglCreateContext failed with GLES 2.0 " ) ;
return ;
}
backend - > log ( AQ_LOG_ERROR , " CDRMRenderer: eglCreateContext failed with GLES 3.2, retrying GLES 3.0 " ) ;
attrs = attrsNoVer ;
attrs . push_back ( EGL_CONTEXT_MAJOR_VERSION ) ;
attrs . push_back ( 3 ) ;
attrs . push_back ( EGL_CONTEXT_MINOR_VERSION ) ;
attrs . push_back ( 0 ) ;
attrs . push_back ( EGL_NONE ) ;
egl . context = eglCreateContext ( egl . display , EGL_NO_CONFIG_KHR , EGL_NO_CONTEXT , attrs . data ( ) ) ;
if ( egl . context = = EGL_NO_CONTEXT ) {
backend - > log ( AQ_LOG_ERROR , " CDRMRenderer: Can't create renderer, eglCreateContext failed with both GLES 3.2 and GLES 3.0 " ) ;
return ;
}
2024-07-11 20:41:53 +02:00
}
2025-02-01 09:17:49 -06:00
if ( exts . IMG_context_priority ) {
EGLint priority = EGL_CONTEXT_PRIORITY_MEDIUM_IMG ;
eglQueryContext ( egl . display , egl . context , EGL_CONTEXT_PRIORITY_LEVEL_IMG , & priority ) ;
if ( priority ! = EGL_CONTEXT_PRIORITY_HIGH_IMG )
backend - > log ( AQ_LOG_DEBUG , " CDRMRenderer: Failed to get a high priority context " ) ;
else
backend - > log ( AQ_LOG_DEBUG , " CDRMRenderer: Got a high priority context " ) ;
2024-07-11 20:41:53 +02:00
}
2025-02-27 19:32:20 -06:00
CEglContextGuard eglContext ( * this ) ;
2024-07-11 20:41:53 +02:00
2025-02-01 09:17:49 -06:00
EGLEXTENSIONS = ( const char * ) glGetString ( GL_EXTENSIONS ) ;
2024-07-11 20:41:53 +02:00
2025-02-01 09:17:49 -06:00
std : : string gpuName = " unknown " ;
char * drmName = drmGetDeviceNameFromFd2 ( drmFD ) ;
if ( drmName ! = nullptr ) {
gpuName = std : : string { drmName } ;
free ( drmName ) ;
2024-07-11 20:41:53 +02:00
}
2025-02-01 09:17:49 -06:00
backend - > log ( AQ_LOG_DEBUG , std : : format ( " Creating {}CDRMRenderer on gpu {} " , GLES2 ? " GLES2 " : " " , gpuName ) ) ;
backend - > log ( AQ_LOG_DEBUG , std : : format ( " Using: {} " , ( char * ) glGetString ( GL_VERSION ) ) ) ;
backend - > log ( AQ_LOG_DEBUG , std : : format ( " Vendor: {} " , ( char * ) glGetString ( GL_VENDOR ) ) ) ;
backend - > log ( AQ_LOG_DEBUG , std : : format ( " Renderer: {} " , ( char * ) glGetString ( GL_RENDERER ) ) ) ;
backend - > log ( AQ_LOG_DEBUG , std : : format ( " Supported context extensions: ({}) {} " , std : : count ( EGLEXTENSIONS . begin ( ) , EGLEXTENSIONS . end ( ) , ' ' ) , EGLEXTENSIONS ) ) ;
exts . EXT_read_format_bgra = EGLEXTENSIONS . contains ( " GL_EXT_read_format_bgra " ) ;
exts . EXT_texture_format_BGRA8888 = EGLEXTENSIONS . contains ( " GL_EXT_texture_format_BGRA8888 " ) ;
}
void CDRMRenderer : : initResources ( ) {
2025-02-27 19:32:20 -06:00
CEglContextGuard eglContext ( * this ) ;
2025-02-01 09:17:49 -06:00
if ( ! exts . EXT_image_dma_buf_import | | ! initDRMFormats ( ) )
backend - > log ( AQ_LOG_ERROR , " CDRMRenderer: initDRMFormats failed, dma-buf won't work " ) ;
gl . shader . program = createProgram ( VERT_SRC , FRAG_SRC ) ;
if ( gl . shader . program = = 0 )
backend - > log ( AQ_LOG_ERROR , " CDRMRenderer: texture shader failed " ) ;
2024-07-11 23:00:50 +02:00
2025-02-01 09:17:49 -06:00
gl . shader . proj = glGetUniformLocation ( gl . shader . program , " proj " ) ;
gl . shader . posAttrib = glGetAttribLocation ( gl . shader . program , " pos " ) ;
gl . shader . texAttrib = glGetAttribLocation ( gl . shader . program , " texcoord " ) ;
gl . shader . tex = glGetUniformLocation ( gl . shader . program , " tex " ) ;
gl . shaderExt . program = createProgram ( VERT_SRC , FRAG_SRC_EXT ) ;
if ( gl . shaderExt . program = = 0 )
backend - > log ( AQ_LOG_ERROR , " CDRMRenderer: external texture shader failed " ) ;
gl . shaderExt . proj = glGetUniformLocation ( gl . shaderExt . program , " proj " ) ;
gl . shaderExt . posAttrib = glGetAttribLocation ( gl . shaderExt . program , " pos " ) ;
gl . shaderExt . texAttrib = glGetAttribLocation ( gl . shaderExt . program , " texcoord " ) ;
gl . shaderExt . tex = glGetUniformLocation ( gl . shaderExt . program , " tex " ) ;
}
SP < CDRMRenderer > CDRMRenderer : : attempt ( SP < CBackend > backend_ , int drmFD , bool GLES2 ) {
SP < CDRMRenderer > renderer = SP < CDRMRenderer > ( new CDRMRenderer ( ) ) ;
renderer - > drmFD = drmFD ;
renderer - > backend = backend_ ;
gBackend = backend_ ;
renderer - > loadEGLAPI ( ) ;
if ( ! renderer - > exts . EXT_platform_device ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer(drm): Can't create renderer, EGL doesn't support EXT_platform_device " ) ;
2024-07-12 11:16:47 +02:00
return nullptr ;
}
2025-02-01 09:17:49 -06:00
EGLDeviceEXT device = renderer - > eglDeviceFromDRMFD ( drmFD ) ;
if ( device = = EGL_NO_DEVICE_EXT ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer(drm): Can't create renderer, no matching devices found " ) ;
2024-07-12 11:16:47 +02:00
return nullptr ;
}
2025-02-01 09:17:49 -06:00
std : : vector < EGLint > attrs ;
if ( renderer - > exts . KHR_display_reference ) {
attrs . push_back ( EGL_TRACK_REFERENCES_KHR ) ;
attrs . push_back ( EGL_TRUE ) ;
}
2024-07-11 20:41:53 +02:00
attrs . push_back ( EGL_NONE ) ;
2025-02-01 09:17:49 -06:00
renderer - > egl . display = renderer - > proc . eglGetPlatformDisplayEXT ( EGL_PLATFORM_DEVICE_EXT , device , attrs . data ( ) ) ;
if ( renderer - > egl . display = = EGL_NO_DISPLAY ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, eglGetPlatformDisplayEXT failed " ) ;
2024-07-11 20:41:53 +02:00
return nullptr ;
}
2025-02-01 09:17:49 -06:00
renderer - > initContext ( GLES2 ) ;
if ( renderer - > egl . context = = nullptr | | renderer - > egl . context = = EGL_NO_CONTEXT )
return nullptr ;
renderer - > initResources ( ) ;
2024-07-11 20:41:53 +02:00
2025-02-01 09:17:49 -06:00
return renderer ;
}
SP < CDRMRenderer > CDRMRenderer : : attempt ( SP < CBackend > backend_ , Hyprutils : : Memory : : CSharedPointer < CGBMAllocator > allocator_ , bool GLES2 ) {
SP < CDRMRenderer > renderer = SP < CDRMRenderer > ( new CDRMRenderer ( ) ) ;
renderer - > drmFD = allocator_ - > drmFD ( ) ;
renderer - > backend = backend_ ;
gBackend = backend_ ;
2024-07-11 20:41:53 +02:00
2025-02-01 09:17:49 -06:00
renderer - > loadEGLAPI ( ) ;
2024-07-11 20:41:53 +02:00
2025-02-01 09:17:49 -06:00
if ( ! renderer - > exts . KHR_platform_gbm ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer(gbm): Can't create renderer, EGL doesn't support KHR_platform_gbm " ) ;
2024-07-12 11:16:47 +02:00
return nullptr ;
}
2025-02-01 09:17:49 -06:00
std : : vector < EGLint > attrs ;
if ( renderer - > exts . KHR_display_reference ) {
attrs . push_back ( EGL_TRACK_REFERENCES_KHR ) ;
attrs . push_back ( EGL_TRUE ) ;
2024-07-11 20:41:53 +02:00
}
2025-02-01 09:17:49 -06:00
attrs . push_back ( EGL_NONE ) ;
2024-07-11 20:41:53 +02:00
2025-02-01 09:17:49 -06:00
renderer - > egl . display = renderer - > proc . eglGetPlatformDisplayEXT ( EGL_PLATFORM_GBM_KHR , allocator_ - > gbmDevice , attrs . data ( ) ) ;
if ( renderer - > egl . display = = EGL_NO_DISPLAY ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, eglGetPlatformDisplayEXT failed " ) ;
2024-07-12 17:20:08 +02:00
return nullptr ;
}
2025-02-01 09:17:49 -06:00
renderer - > initContext ( GLES2 ) ;
if ( renderer - > egl . context = = nullptr | | renderer - > egl . context = = EGL_NO_CONTEXT )
return nullptr ;
2024-07-11 20:41:53 +02:00
2025-02-01 09:17:49 -06:00
renderer - > initResources ( ) ;
2024-07-11 20:41:53 +02:00
return renderer ;
}
2025-02-27 19:32:20 -06:00
CEglContextGuard : : CEglContextGuard ( const CDRMRenderer & renderer_ ) : renderer ( renderer_ ) {
2024-07-11 20:41:53 +02:00
savedEGLState . display = eglGetCurrentDisplay ( ) ;
savedEGLState . context = eglGetCurrentContext ( ) ;
savedEGLState . draw = eglGetCurrentSurface ( EGL_DRAW ) ;
savedEGLState . read = eglGetCurrentSurface ( EGL_READ ) ;
2025-02-27 19:32:20 -06:00
if ( ! eglMakeCurrent ( renderer . egl . display , EGL_NO_SURFACE , EGL_NO_SURFACE , renderer . egl . context ) )
renderer . backend - > log ( AQ_LOG_WARNING , " CDRMRenderer: setEGL eglMakeCurrent failed " ) ;
2024-07-11 20:41:53 +02:00
}
2025-02-27 19:32:20 -06:00
CEglContextGuard : : ~ CEglContextGuard ( ) {
EGLDisplay dpy = savedEGLState . display ? savedEGLState . display : renderer . egl . display ;
2024-07-11 20:41:53 +02:00
// egl can't handle this
if ( dpy = = EGL_NO_DISPLAY )
return ;
if ( ! eglMakeCurrent ( dpy , savedEGLState . draw , savedEGLState . read , savedEGLState . context ) )
2025-02-27 19:32:20 -06:00
renderer . backend - > log ( AQ_LOG_WARNING , " CDRMRenderer: restoreEGL eglMakeCurrent failed " ) ;
2024-07-11 20:41:53 +02:00
}
EGLImageKHR CDRMRenderer : : createEGLImage ( const SDMABUFAttrs & attrs ) {
std : : vector < uint32_t > attribs ;
attribs . push_back ( EGL_WIDTH ) ;
attribs . push_back ( attrs . size . x ) ;
attribs . push_back ( EGL_HEIGHT ) ;
attribs . push_back ( attrs . size . y ) ;
attribs . push_back ( EGL_LINUX_DRM_FOURCC_EXT ) ;
attribs . push_back ( attrs . format ) ;
2024-07-12 15:56:32 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " EGL: createEGLImage: size {} with format {} and modifier 0x{:x} " , attrs . size , fourccToName ( attrs . format ) , attrs . modifier ) ) ) ;
2024-07-11 22:17:19 +02:00
2024-07-11 20:41:53 +02:00
struct {
EGLint fd ;
EGLint offset ;
EGLint pitch ;
EGLint modlo ;
EGLint modhi ;
2025-01-28 23:34:09 +00:00
} attrNames [ 4 ] = { { . fd = EGL_DMA_BUF_PLANE0_FD_EXT ,
. offset = EGL_DMA_BUF_PLANE0_OFFSET_EXT ,
. pitch = EGL_DMA_BUF_PLANE0_PITCH_EXT ,
. modlo = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT ,
. modhi = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT } ,
{ . fd = EGL_DMA_BUF_PLANE1_FD_EXT ,
. offset = EGL_DMA_BUF_PLANE1_OFFSET_EXT ,
. pitch = EGL_DMA_BUF_PLANE1_PITCH_EXT ,
. modlo = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT ,
. modhi = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT } ,
{ . fd = EGL_DMA_BUF_PLANE2_FD_EXT ,
. offset = EGL_DMA_BUF_PLANE2_OFFSET_EXT ,
. pitch = EGL_DMA_BUF_PLANE2_PITCH_EXT ,
. modlo = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT ,
. modhi = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT } ,
{ . fd = EGL_DMA_BUF_PLANE3_FD_EXT ,
. offset = EGL_DMA_BUF_PLANE3_OFFSET_EXT ,
. pitch = EGL_DMA_BUF_PLANE3_PITCH_EXT ,
. modlo = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT ,
. modhi = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT } } ;
2024-07-11 20:41:53 +02:00
for ( int i = 0 ; i < attrs . planes ; i + + ) {
attribs . push_back ( attrNames [ i ] . fd ) ;
attribs . push_back ( attrs . fds [ i ] ) ;
attribs . push_back ( attrNames [ i ] . offset ) ;
attribs . push_back ( attrs . offsets [ i ] ) ;
attribs . push_back ( attrNames [ i ] . pitch ) ;
attribs . push_back ( attrs . strides [ i ] ) ;
2024-07-12 11:16:47 +02:00
if ( hasModifiers & & attrs . modifier ! = DRM_FORMAT_MOD_INVALID ) {
2024-07-11 20:41:53 +02:00
attribs . push_back ( attrNames [ i ] . modlo ) ;
attribs . push_back ( attrs . modifier & 0xFFFFFFFF ) ;
attribs . push_back ( attrNames [ i ] . modhi ) ;
attribs . push_back ( attrs . modifier > > 32 ) ;
}
}
attribs . push_back ( EGL_IMAGE_PRESERVED_KHR ) ;
attribs . push_back ( EGL_TRUE ) ;
attribs . push_back ( EGL_NONE ) ;
2025-02-01 09:17:49 -06:00
EGLImageKHR image = proc . eglCreateImageKHR ( egl . display , EGL_NO_CONTEXT , EGL_LINUX_DMA_BUF_EXT , nullptr , ( int * ) attribs . data ( ) ) ;
2024-07-11 20:41:53 +02:00
if ( image = = EGL_NO_IMAGE_KHR ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " EGL: EGLCreateImageKHR failed: {} " , eglGetError ( ) ) ) ;
return EGL_NO_IMAGE_KHR ;
}
return image ;
}
2024-07-12 17:31:28 +02:00
SGLTex CDRMRenderer : : glTex ( Hyprutils : : Memory : : CSharedPointer < IBuffer > buffa ) {
SGLTex tex ;
2024-07-11 20:41:53 +02:00
2024-07-12 11:16:47 +02:00
const auto dma = buffa - > dmabuf ( ) ;
tex . image = createEGLImage ( dma ) ;
2024-07-11 20:41:53 +02:00
if ( tex . image = = EGL_NO_IMAGE_KHR ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " EGL (glTex): createEGLImage failed: {} " , eglGetError ( ) ) ) ;
return tex ;
}
2024-07-12 11:16:47 +02:00
bool external = false ;
2024-08-27 20:04:26 +02:00
for ( auto const & fmt : formats ) {
2024-07-12 11:16:47 +02:00
if ( fmt . drmFormat ! = dma . format | | fmt . modifier ! = dma . modifier )
continue ;
2024-07-11 20:41:53 +02:00
2024-07-12 17:31:28 +02:00
backend - > log ( AQ_LOG_DEBUG , std : : format ( " CDRMRenderer::glTex: found format+mod, external = {} " , fmt . external ) ) ;
2024-07-12 11:16:47 +02:00
external = fmt . external ;
break ;
}
2024-07-11 20:41:53 +02:00
2024-07-12 11:16:47 +02:00
tex . target = external ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D ;
GLCALL ( glGenTextures ( 1 , & tex . texid ) ) ;
GLCALL ( glBindTexture ( tex . target , tex . texid ) ) ;
GLCALL ( glTexParameteri ( tex . target , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE ) ) ;
GLCALL ( glTexParameteri ( tex . target , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE ) ) ;
2025-02-01 09:17:49 -06:00
GLCALL ( proc . glEGLImageTargetTexture2DOES ( tex . target , tex . image ) ) ;
2024-07-12 11:16:47 +02:00
GLCALL ( glBindTexture ( tex . target , 0 ) ) ;
2024-07-11 23:00:50 +02:00
2024-07-11 20:41:53 +02:00
return tex ;
}
2025-03-01 20:07:00 -06:00
constexpr GLenum PIXEL_BUFFER_FORMAT = GL_RGBA ;
void CDRMRenderer : : readBuffer ( Hyprutils : : Memory : : CSharedPointer < IBuffer > buf , std : : span < uint8_t > out ) {
CEglContextGuard eglContext ( * this ) ;
2025-03-02 15:32:33 -06:00
auto att = buf - > attachments . get < CDRMRendererBufferAttachment > ( ) ;
if ( ! att ) {
att = makeShared < CDRMRendererBufferAttachment > ( self , buf , nullptr , 0 , 0 , SGLTex { } , std : : vector < uint8_t > ( ) ) ;
buf - > attachments . add ( att ) ;
2025-03-01 20:07:00 -06:00
}
auto dma = buf - > dmabuf ( ) ;
if ( ! att - > eglImage ) {
att - > eglImage = createEGLImage ( dma ) ;
if ( att - > eglImage = = EGL_NO_IMAGE_KHR ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " EGL (readBuffer): createEGLImage failed: {} " , eglGetError ( ) ) ) ;
return ;
}
GLCALL ( glGenRenderbuffers ( 1 , & att - > rbo ) ) ;
GLCALL ( glBindRenderbuffer ( GL_RENDERBUFFER , att - > rbo ) ) ;
GLCALL ( proc . glEGLImageTargetRenderbufferStorageOES ( GL_RENDERBUFFER , ( GLeglImageOES ) att - > eglImage ) ) ;
GLCALL ( glBindRenderbuffer ( GL_RENDERBUFFER , 0 ) ) ;
GLCALL ( glGenFramebuffers ( 1 , & att - > fbo ) ) ;
GLCALL ( glBindFramebuffer ( GL_FRAMEBUFFER , att - > fbo ) ) ;
GLCALL ( glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_RENDERBUFFER , att - > rbo ) ) ;
if ( glCheckFramebufferStatus ( GL_FRAMEBUFFER ) ! = GL_FRAMEBUFFER_COMPLETE ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " EGL (readBuffer): glCheckFramebufferStatus failed: {} " , glGetError ( ) ) ) ;
return ;
}
}
GLCALL ( glBindFramebuffer ( GL_FRAMEBUFFER , att - > fbo ) ) ;
GLCALL ( proc . glReadnPixelsEXT ( 0 , 0 , dma . size . x , dma . size . y , GL_RGBA , GL_UNSIGNED_BYTE , out . size ( ) , out . data ( ) ) ) ;
GLCALL ( glBindFramebuffer ( GL_FRAMEBUFFER , 0 ) ) ;
}
2024-07-11 20:41:53 +02:00
inline const float fullVerts [ ] = {
1 , 0 , // top right
0 , 0 , // top left
1 , 1 , // bottom right
0 , 1 , // bottom left
} ;
2024-08-05 00:27:09 +02:00
void CDRMRenderer : : waitOnSync ( int fd ) {
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " EGL (waitOnSync): attempting to wait on fd {} " , fd ) ) ) ;
std : : vector < EGLint > attribs ;
int dupFd = fcntl ( fd , F_DUPFD_CLOEXEC , 0 ) ;
if ( dupFd < 0 ) {
backend - > log ( AQ_LOG_TRACE , " EGL (waitOnSync): failed to dup fd for wait " ) ;
return ;
}
attribs . push_back ( EGL_SYNC_NATIVE_FENCE_FD_ANDROID ) ;
attribs . push_back ( dupFd ) ;
attribs . push_back ( EGL_NONE ) ;
2025-02-01 09:17:49 -06:00
EGLSyncKHR sync = proc . eglCreateSyncKHR ( egl . display , EGL_SYNC_NATIVE_FENCE_ANDROID , attribs . data ( ) ) ;
2024-08-05 00:27:09 +02:00
if ( sync = = EGL_NO_SYNC_KHR ) {
TRACE ( backend - > log ( AQ_LOG_TRACE , " EGL (waitOnSync): failed to create an egl sync for explicit " ) ) ;
if ( dupFd > = 0 )
close ( dupFd ) ;
return ;
}
// we got a sync, now we just tell egl to wait before sampling
2025-02-01 09:17:49 -06:00
if ( proc . eglWaitSyncKHR ( egl . display , sync , 0 ) ! = EGL_TRUE ) {
if ( proc . eglDestroySyncKHR ( egl . display , sync ) ! = EGL_TRUE )
2024-08-05 00:27:09 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE , " EGL (waitOnSync): failed to destroy sync " ) ) ;
TRACE ( backend - > log ( AQ_LOG_TRACE , " EGL (waitOnSync): failed to wait on the sync object " ) ) ;
return ;
}
2025-02-01 09:17:49 -06:00
if ( proc . eglDestroySyncKHR ( egl . display , sync ) ! = EGL_TRUE )
2024-08-05 00:27:09 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE , " EGL (waitOnSync): failed to destroy sync " ) ) ;
}
int CDRMRenderer : : recreateBlitSync ( ) {
TRACE ( backend - > log ( AQ_LOG_TRACE , " EGL (recreateBlitSync): recreating blit sync " ) ) ;
if ( egl . lastBlitSync ) {
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " EGL (recreateBlitSync): cleaning up old sync (fd {}) " , egl . lastBlitSyncFD ) ) ) ;
// cleanup last sync
2025-02-01 09:17:49 -06:00
if ( proc . eglDestroySyncKHR ( egl . display , egl . lastBlitSync ) ! = EGL_TRUE )
2024-08-05 00:27:09 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE , " EGL (recreateBlitSync): failed to destroy old sync " ) ) ;
if ( egl . lastBlitSyncFD > = 0 )
close ( egl . lastBlitSyncFD ) ;
egl . lastBlitSyncFD = - 1 ;
egl . lastBlitSync = nullptr ;
}
2025-02-01 09:17:49 -06:00
EGLSyncKHR sync = proc . eglCreateSyncKHR ( egl . display , EGL_SYNC_NATIVE_FENCE_ANDROID , nullptr ) ;
2024-08-05 00:27:09 +02:00
if ( sync = = EGL_NO_SYNC_KHR ) {
TRACE ( backend - > log ( AQ_LOG_TRACE , " EGL (recreateBlitSync): failed to create an egl sync for explicit " ) ) ;
return - 1 ;
}
// we need to flush otherwise we might not get a valid fd
glFlush ( ) ;
2025-02-01 09:17:49 -06:00
int fd = proc . eglDupNativeFenceFDANDROID ( egl . display , sync ) ;
2024-08-05 00:27:09 +02:00
if ( fd = = EGL_NO_NATIVE_FENCE_FD_ANDROID ) {
TRACE ( backend - > log ( AQ_LOG_TRACE , " EGL (recreateBlitSync): failed to dup egl fence fd " ) ) ;
2025-02-01 09:17:49 -06:00
if ( proc . eglDestroySyncKHR ( egl . display , sync ) ! = EGL_TRUE )
2024-08-05 00:27:09 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE , " EGL (recreateBlitSync): failed to destroy new sync " ) ) ;
return - 1 ;
}
egl . lastBlitSync = sync ;
egl . lastBlitSyncFD = fd ;
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " EGL (recreateBlitSync): success, new fence exported with fd {} " , fd ) ) ) ;
return fd ;
}
2024-08-17 19:42:51 +01:00
void CDRMRenderer : : clearBuffer ( IBuffer * buf ) {
2025-02-27 19:32:20 -06:00
CEglContextGuard eglContext ( * this ) ;
auto dmabuf = buf - > dmabuf ( ) ;
GLuint rboID = 0 , fboID = 0 ;
2024-08-17 19:42:51 +01:00
if ( ! dmabuf . success ) {
backend - > log ( AQ_LOG_ERROR , " EGL (clear): cannot clear a non-dmabuf " ) ;
return ;
}
auto rboImage = createEGLImage ( dmabuf ) ;
if ( rboImage = = EGL_NO_IMAGE_KHR ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " EGL (clear): createEGLImage failed: {} " , eglGetError ( ) ) ) ;
return ;
}
GLCALL ( glGenRenderbuffers ( 1 , & rboID ) ) ;
GLCALL ( glBindRenderbuffer ( GL_RENDERBUFFER , rboID ) ) ;
2025-02-01 09:17:49 -06:00
GLCALL ( proc . glEGLImageTargetRenderbufferStorageOES ( GL_RENDERBUFFER , ( GLeglImageOES ) rboImage ) ) ;
2024-08-17 19:42:51 +01:00
GLCALL ( glBindRenderbuffer ( GL_RENDERBUFFER , 0 ) ) ;
GLCALL ( glGenFramebuffers ( 1 , & fboID ) ) ;
GLCALL ( glBindFramebuffer ( GL_FRAMEBUFFER , fboID ) ) ;
GLCALL ( glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_RENDERBUFFER , rboID ) ) ;
GLCALL ( glBindRenderbuffer ( GL_RENDERBUFFER , rboID ) ) ;
GLCALL ( glBindFramebuffer ( GL_FRAMEBUFFER , fboID ) ) ;
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " EGL (clear): fbo {} rbo {} " , fboID , rboID ) ) ) ;
glClearColor ( 0.F , 0.F , 0.F , 1.F ) ;
glClear ( GL_COLOR_BUFFER_BIT ) ;
glFlush ( ) ;
GLCALL ( glBindFramebuffer ( GL_FRAMEBUFFER , 0 ) ) ;
GLCALL ( glBindRenderbuffer ( GL_RENDERBUFFER , 0 ) ) ;
glDeleteFramebuffers ( 1 , & fboID ) ;
glDeleteRenderbuffers ( 1 , & rboID ) ;
2025-02-01 09:17:49 -06:00
proc . eglDestroyImageKHR ( egl . display , rboImage ) ;
2024-08-17 19:42:51 +01:00
}
2025-03-01 20:07:00 -06:00
CDRMRenderer : : SBlitResult CDRMRenderer : : blit ( SP < IBuffer > from , SP < IBuffer > to , SP < CDRMRenderer > primaryRenderer , int waitFD ) {
2025-02-27 19:32:20 -06:00
CEglContextGuard eglContext ( * this ) ;
2024-07-11 20:41:53 +02:00
if ( from - > dmabuf ( ) . size ! = to - > dmabuf ( ) . size ) {
backend - > log ( AQ_LOG_ERROR , " EGL (blit): buffer sizes mismatched " ) ;
2024-08-05 00:27:09 +02:00
return { } ;
}
if ( waitFD > = 0 ) {
// wait on a provided explicit fence
waitOnSync ( waitFD ) ;
2024-07-11 20:41:53 +02:00
}
// firstly, get a texture from the from buffer
2024-07-11 22:17:19 +02:00
// if it has an attachment, use that
// both from and to have the same AQ_ATTACHMENT_DRM_RENDERER_DATA.
// Those buffers always come from different swapchains, so it's OK.
2025-03-01 20:07:00 -06:00
SGLTex fromTex ;
auto fromDma = from - > dmabuf ( ) ;
std : : span < uint8_t > intermediateBuf ;
2024-07-11 22:17:19 +02:00
{
2025-03-02 15:32:33 -06:00
auto attachment = from - > attachments . get < CDRMRendererBufferAttachment > ( ) ;
2024-07-11 22:17:19 +02:00
if ( attachment ) {
TRACE ( backend - > log ( AQ_LOG_TRACE , " EGL (blit): From attachment found " ) ) ;
2025-03-02 15:32:33 -06:00
fromTex = attachment - > tex ;
intermediateBuf = attachment - > intermediateBuf ;
2024-07-11 22:17:19 +02:00
}
2025-03-01 20:07:00 -06:00
if ( ! fromTex . image & & intermediateBuf . empty ( ) ) {
2024-07-11 22:17:19 +02:00
backend - > log ( AQ_LOG_DEBUG , " EGL (blit): No attachment in from, creating a new image " ) ;
fromTex = glTex ( from ) ;
2025-03-02 15:32:33 -06:00
attachment = makeShared < CDRMRendererBufferAttachment > ( self , from , nullptr , 0 , 0 , fromTex , std : : vector < uint8_t > ( ) ) ;
from - > attachments . add ( attachment ) ;
2025-03-01 20:07:00 -06:00
if ( ! fromTex . image & & primaryRenderer ) {
backend - > log ( AQ_LOG_DEBUG , " EGL (blit): Failed to create image from source buffer directly, allocating intermediate buffer " ) ;
static_assert ( PIXEL_BUFFER_FORMAT = = GL_RGBA ) ; // If the pixel buffer format changes, the below size calculation probably needs to as well.
2025-03-02 15:32:33 -06:00
attachment - > intermediateBuf . resize ( fromDma . size . x * fromDma . size . y * 4 ) ;
intermediateBuf = attachment - > intermediateBuf ;
2025-03-01 20:07:00 -06:00
fromTex . target = GL_TEXTURE_2D ;
GLCALL ( glGenTextures ( 1 , & fromTex . texid ) ) ;
}
}
if ( ! intermediateBuf . empty ( ) & & primaryRenderer ) {
// Note: this might modify from's attachments
primaryRenderer - > readBuffer ( from , intermediateBuf ) ;
2024-07-11 22:17:19 +02:00
}
}
2024-07-11 20:41:53 +02:00
2024-07-12 17:20:08 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE ,
std : : format ( " EGL (blit): fromTex id {}, image 0x{:x}, target {} " , fromTex . texid , ( uintptr_t ) fromTex . image ,
fromTex . target = = GL_TEXTURE_2D ? " GL_TEXTURE_2D " : " GL_TEXTURE_EXTERNAL_OES " ) ) ) ;
2024-07-11 20:41:53 +02:00
// then, get a rbo from our to buffer
2024-07-11 22:17:19 +02:00
// if it has an attachment, use that
EGLImageKHR rboImage = nullptr ;
GLuint rboID = 0 , fboID = 0 ;
auto toDma = to - > dmabuf ( ) ;
2024-07-12 16:18:27 +02:00
if ( ! verifyDestinationDMABUF ( toDma ) ) {
backend - > log ( AQ_LOG_ERROR , " EGL (blit): failed to blit: destination dmabuf unsupported " ) ;
2024-08-05 00:27:09 +02:00
return { } ;
2024-07-12 16:18:27 +02:00
}
2024-07-11 22:17:19 +02:00
{
2025-03-02 15:32:33 -06:00
auto attachment = to - > attachments . get < CDRMRendererBufferAttachment > ( ) ;
2024-07-11 22:17:19 +02:00
if ( attachment ) {
TRACE ( backend - > log ( AQ_LOG_TRACE , " EGL (blit): To attachment found " ) ) ;
2025-03-02 15:32:33 -06:00
rboImage = attachment - > eglImage ;
fboID = attachment - > fbo ;
rboID = attachment - > rbo ;
2024-07-11 22:17:19 +02:00
}
2024-07-11 20:41:53 +02:00
2024-07-11 22:17:19 +02:00
if ( ! rboImage ) {
backend - > log ( AQ_LOG_DEBUG , " EGL (blit): No attachment in to, creating a new image " ) ;
2024-07-11 20:41:53 +02:00
2024-07-11 22:17:19 +02:00
rboImage = createEGLImage ( toDma ) ;
if ( rboImage = = EGL_NO_IMAGE_KHR ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " EGL (blit): createEGLImage failed: {} " , eglGetError ( ) ) ) ;
2024-08-05 00:27:09 +02:00
return { } ;
2024-07-11 22:17:19 +02:00
}
2024-07-11 20:41:53 +02:00
2024-07-11 22:17:19 +02:00
GLCALL ( glGenRenderbuffers ( 1 , & rboID ) ) ;
GLCALL ( glBindRenderbuffer ( GL_RENDERBUFFER , rboID ) ) ;
2025-02-01 09:17:49 -06:00
GLCALL ( proc . glEGLImageTargetRenderbufferStorageOES ( GL_RENDERBUFFER , ( GLeglImageOES ) rboImage ) ) ;
2024-07-11 22:17:19 +02:00
GLCALL ( glBindRenderbuffer ( GL_RENDERBUFFER , 0 ) ) ;
2024-07-11 20:41:53 +02:00
2024-07-11 22:17:19 +02:00
GLCALL ( glGenFramebuffers ( 1 , & fboID ) ) ;
GLCALL ( glBindFramebuffer ( GL_FRAMEBUFFER , fboID ) ) ;
GLCALL ( glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_RENDERBUFFER , rboID ) ) ;
2024-07-11 20:41:53 +02:00
2024-07-11 22:17:19 +02:00
if ( glCheckFramebufferStatus ( GL_FRAMEBUFFER ) ! = GL_FRAMEBUFFER_COMPLETE ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " EGL (blit): glCheckFramebufferStatus failed: {} " , glGetError ( ) ) ) ;
2024-08-05 00:27:09 +02:00
return { } ;
2024-07-11 22:17:19 +02:00
}
2024-07-11 20:41:53 +02:00
2025-03-01 20:07:00 -06:00
to - > attachments . add ( makeShared < CDRMRendererBufferAttachment > ( self , to , rboImage , fboID , rboID , SGLTex { } , std : : vector < uint8_t > ( ) ) ) ;
2024-07-11 22:17:19 +02:00
}
2024-07-11 20:41:53 +02:00
}
2024-07-11 23:00:50 +02:00
glFlush ( ) ;
2024-07-11 22:17:19 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " EGL (blit): rboImage 0x{:x} " , ( uintptr_t ) rboImage ) ) ) ;
2024-07-11 20:41:53 +02:00
GLCALL ( glBindRenderbuffer ( GL_RENDERBUFFER , rboID ) ) ;
GLCALL ( glBindFramebuffer ( GL_FRAMEBUFFER , fboID ) ) ;
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " EGL (blit): fbo {} rbo {} " , fboID , rboID ) ) ) ;
glClearColor ( 0.77F , 0.F , 0.74F , 1.F ) ;
glClear ( GL_COLOR_BUFFER_BIT ) ;
// done, let's render the texture to the rbo
CBox renderBox = { { } , toDma . size } ;
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " EGL (blit): box size {} " , renderBox . size ( ) ) ) ) ;
float mtx [ 9 ] ;
2024-07-11 22:17:19 +02:00
float base [ 9 ] ;
2024-07-11 20:41:53 +02:00
float monitorProj [ 9 ] ;
2024-07-11 22:17:19 +02:00
matrixIdentity ( base ) ;
2024-07-12 17:20:08 +02:00
auto & SHADER = fromTex . target = = GL_TEXTURE_2D ? gl . shader : gl . shaderExt ;
2024-07-11 22:17:19 +02:00
// KMS uses flipped y, we have to do FLIPPED_180
matrixTranslate ( base , toDma . size . x / 2.0 , toDma . size . y / 2.0 ) ;
matrixTransform ( base , HYPRUTILS_TRANSFORM_FLIPPED_180 ) ;
matrixTranslate ( base , - toDma . size . x / 2.0 , - toDma . size . y / 2.0 ) ;
2024-07-11 20:41:53 +02:00
2024-07-11 22:17:19 +02:00
projectBox ( mtx , renderBox , HYPRUTILS_TRANSFORM_FLIPPED_180 , 0 , base ) ;
matrixProjection ( monitorProj , toDma . size . x , toDma . size . y , HYPRUTILS_TRANSFORM_FLIPPED_180 ) ;
2024-07-11 20:41:53 +02:00
float glMtx [ 9 ] ;
matrixMultiply ( glMtx , monitorProj , mtx ) ;
GLCALL ( glViewport ( 0 , 0 , toDma . size . x , toDma . size . y ) ) ;
GLCALL ( glActiveTexture ( GL_TEXTURE0 ) ) ;
2024-07-12 11:16:47 +02:00
GLCALL ( glBindTexture ( fromTex . target , fromTex . texid ) ) ;
2024-07-11 22:17:19 +02:00
2024-07-12 11:16:47 +02:00
GLCALL ( glTexParameteri ( fromTex . target , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ) ;
GLCALL ( glTexParameteri ( fromTex . target , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ) ;
2024-07-11 22:17:19 +02:00
2025-03-01 20:07:00 -06:00
if ( ! intermediateBuf . empty ( ) )
GLCALL ( glTexImage2D ( fromTex . target , 0 , PIXEL_BUFFER_FORMAT , fromDma . size . x , fromDma . size . y , 0 , PIXEL_BUFFER_FORMAT , GL_UNSIGNED_BYTE , intermediateBuf . data ( ) ) ) ;
2024-07-12 17:20:08 +02:00
GLCALL ( glUseProgram ( SHADER . program ) ) ;
2024-07-11 20:41:53 +02:00
GLCALL ( glDisable ( GL_BLEND ) ) ;
GLCALL ( glDisable ( GL_SCISSOR_TEST ) ) ;
matrixTranspose ( glMtx , glMtx ) ;
2024-07-12 17:20:08 +02:00
GLCALL ( glUniformMatrix3fv ( SHADER . proj , 1 , GL_FALSE , glMtx ) ) ;
2024-07-11 20:41:53 +02:00
2024-07-12 17:20:08 +02:00
GLCALL ( glUniform1i ( SHADER . tex , 0 ) ) ;
2024-07-11 20:41:53 +02:00
2024-07-12 17:20:08 +02:00
GLCALL ( glVertexAttribPointer ( SHADER . posAttrib , 2 , GL_FLOAT , GL_FALSE , 0 , fullVerts ) ) ;
GLCALL ( glVertexAttribPointer ( SHADER . texAttrib , 2 , GL_FLOAT , GL_FALSE , 0 , fullVerts ) ) ;
2024-07-11 20:41:53 +02:00
2024-07-12 17:20:08 +02:00
GLCALL ( glEnableVertexAttribArray ( SHADER . posAttrib ) ) ;
GLCALL ( glEnableVertexAttribArray ( SHADER . texAttrib ) ) ;
2024-07-11 20:41:53 +02:00
GLCALL ( glDrawArrays ( GL_TRIANGLE_STRIP , 0 , 4 ) ) ;
2024-07-12 17:20:08 +02:00
GLCALL ( glDisableVertexAttribArray ( SHADER . posAttrib ) ) ;
GLCALL ( glDisableVertexAttribArray ( SHADER . texAttrib ) ) ;
2024-07-11 20:41:53 +02:00
2024-07-12 11:16:47 +02:00
GLCALL ( glBindTexture ( fromTex . target , 0 ) ) ;
2024-07-11 20:41:53 +02:00
// rendered, cleanup
2024-07-11 22:17:19 +02:00
glFlush ( ) ;
2024-08-05 00:27:09 +02:00
// get an explicit sync fd for the secondary gpu.
// when we pass buffers between gpus we should always use explicit sync,
// as implicit is not guaranteed at all
int explicitFD = recreateBlitSync ( ) ;
2024-07-11 20:41:53 +02:00
GLCALL ( glBindFramebuffer ( GL_FRAMEBUFFER , 0 ) ) ;
2024-07-11 22:17:19 +02:00
GLCALL ( glBindRenderbuffer ( GL_RENDERBUFFER , 0 ) ) ;
2024-07-11 20:41:53 +02:00
2025-01-23 02:39:30 +05:00
return { . success = true , . syncFD = explicitFD = = - 1 ? std : : nullopt : std : : optional < int > { explicitFD } } ;
2024-07-11 22:17:19 +02:00
}
2024-07-11 20:41:53 +02:00
2024-07-11 22:17:19 +02:00
void CDRMRenderer : : onBufferAttachmentDrop ( CDRMRendererBufferAttachment * attachment ) {
2025-02-27 19:32:20 -06:00
CEglContextGuard eglContext ( * this ) ;
2024-07-11 22:17:19 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE ,
std : : format ( " EGL (onBufferAttachmentDrop): dropping fbo {} rbo {} image 0x{:x} " , attachment - > fbo , attachment - > rbo , ( uintptr_t ) attachment - > eglImage ) ) ) ;
2024-07-11 20:41:53 +02:00
2024-07-12 17:31:28 +02:00
if ( attachment - > tex . texid )
GLCALL ( glDeleteTextures ( 1 , & attachment - > tex . texid ) ) ;
2024-07-11 22:17:19 +02:00
if ( attachment - > rbo )
GLCALL ( glDeleteRenderbuffers ( 1 , & attachment - > rbo ) ) ;
if ( attachment - > fbo )
GLCALL ( glDeleteFramebuffers ( 1 , & attachment - > fbo ) ) ;
if ( attachment - > eglImage )
2025-02-01 09:17:49 -06:00
proc . eglDestroyImageKHR ( egl . display , attachment - > eglImage ) ;
2024-07-12 17:31:28 +02:00
if ( attachment - > tex . image )
2025-02-01 09:17:49 -06:00
proc . eglDestroyImageKHR ( egl . display , attachment - > tex . image ) ;
2024-07-11 22:17:19 +02:00
}
2024-07-11 20:41:53 +02:00
2024-07-12 16:18:27 +02:00
bool CDRMRenderer : : verifyDestinationDMABUF ( const SDMABUFAttrs & attrs ) {
2024-08-27 20:04:26 +02:00
for ( auto const & fmt : formats ) {
2024-07-12 16:18:27 +02:00
if ( fmt . drmFormat ! = attrs . format )
continue ;
if ( fmt . modifier ! = attrs . modifier )
continue ;
2025-02-03 13:40:52 -06:00
if ( fmt . modifier ! = DRM_FORMAT_INVALID & & fmt . external ) {
backend - > log ( AQ_LOG_ERROR , " EGL (verifyDestinationDMABUF): FAIL, format is external-only " ) ;
return false ;
}
2024-07-12 16:18:27 +02:00
return true ;
}
backend - > log ( AQ_LOG_ERROR , " EGL (verifyDestinationDMABUF): FAIL, format is unsupported by EGL " ) ;
return false ;
}
2024-07-11 22:17:19 +02:00
CDRMRendererBufferAttachment : : CDRMRendererBufferAttachment ( Hyprutils : : Memory : : CWeakPointer < CDRMRenderer > renderer_ , Hyprutils : : Memory : : CSharedPointer < IBuffer > buffer ,
2025-03-01 20:07:00 -06:00
EGLImageKHR image , GLuint fbo_ , GLuint rbo_ , SGLTex tex_ , std : : vector < uint8_t > intermediateBuf_ ) :
eglImage ( image ) , fbo ( fbo_ ) , rbo ( rbo_ ) , tex ( tex_ ) , intermediateBuf ( intermediateBuf_ ) , renderer ( renderer_ ) {
2024-07-11 22:17:19 +02:00
bufferDestroy = buffer - > events . destroy . registerListener ( [ this ] ( std : : any d ) { renderer - > onBufferAttachmentDrop ( this ) ; } ) ;
2024-07-11 20:41:53 +02:00
}