2024-07-11 20:41:53 +02:00
# include "Renderer.hpp"
# include <xf86drm.h>
# include <xf86drmMode.h>
# include <cstring>
# include <fcntl.h>
# include "Math.hpp"
# include "Shared.hpp"
2024-07-11 22:17:19 +02:00
# include "FormatUtils.hpp"
2024-07-12 11:16:47 +02:00
# include <xf86drm.h>
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
// static funcs
WP < CBackend > gBackend ;
// ------------------- shader utils
GLuint compileShader ( const GLuint & type , std : : string src ) {
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 ;
}
GLuint createProgram ( const std : : string & vert , const std : : string & frag ) {
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 ) ;
} ) # " ;
// ------------------- gbm stuff
static int openRenderNode ( int drmFd ) {
auto renderName = drmGetRenderDeviceNameFromFd ( drmFd ) ;
if ( ! renderName ) {
// This can happen on split render/display platforms, fallback to
// primary node
renderName = drmGetPrimaryDeviceNameFromFd ( drmFd ) ;
if ( ! renderName ) {
gBackend - > log ( AQ_LOG_ERROR , " drmRenderer: drmGetPrimaryDeviceNameFromFd failed " ) ;
return - 1 ;
}
gBackend - > log ( AQ_LOG_WARNING , std : : format ( " DRM dev {} has no render node, falling back to primary " , renderName ) ) ;
drmVersion * render_version = drmGetVersion ( drmFd ) ;
if ( render_version & & render_version - > name ) {
if ( strcmp ( render_version - > name , " evdi " ) = = 0 ) {
free ( renderName ) ;
renderName = ( char * ) malloc ( sizeof ( char ) * 15 ) ;
strcpy ( renderName , " /dev/dri/card0 " ) ;
}
drmFreeVersion ( render_version ) ;
}
}
int renderFD = open ( renderName , O_RDWR | O_CLOEXEC ) ;
if ( renderFD < 0 )
gBackend - > log ( AQ_LOG_ERROR , std : : format ( " openRenderNode failed to open drm device {} " , renderName ) ) ;
free ( renderName ) ;
return renderFD ;
}
// ------------------- egl stuff
inline void loadGLProc ( void * pProc , const char * name ) {
void * proc = ( void * ) eglGetProcAddress ( name ) ;
if ( proc = = NULL ) {
gBackend - > log ( AQ_LOG_ERROR , std : : format ( " eglGetProcAddress({}) failed " , name ) ) ;
abort ( ) ;
}
* ( void * * ) pProc = proc ;
}
// -------------------
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 ;
if ( ! egl . eglQueryDmaBufModifiersEXT ( egl . display , format , 0 , nullptr , nullptr , & len ) ) {
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 ) ;
egl . eglQueryDmaBufModifiersEXT ( egl . display , format , len , mods . data ( ) , external . data ( ) , & len ) ;
std : : vector < std : : pair < uint64_t , bool > > result ;
bool linearIsExternal = false ;
for ( size_t i = 0 ; i < mods . size ( ) ; + + i ) {
if ( external . at ( i ) ) {
if ( mods . at ( i ) = = DRM_FORMAT_MOD_LINEAR )
linearIsExternal = true ;
}
result . push_back ( { mods . at ( i ) , external . at ( i ) } ) ;
}
// 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 : : find ( mods . begin ( ) , mods . end ( ) , DRM_FORMAT_MOD_LINEAR ) = = mods . end ( ) & & mods . size ( ) = = 0 )
result . push_back ( { DRM_FORMAT_MOD_LINEAR , false } ) ;
return result ;
}
bool CDRMRenderer : : initDRMFormats ( ) {
std : : vector < EGLint > formats ;
EGLint len = 0 ;
egl . eglQueryDmaBufFormatsEXT ( egl . display , 0 , nullptr , & len ) ;
formats . resize ( len ) ;
egl . eglQueryDmaBufFormatsEXT ( egl . display , len , formats . data ( ) , & len ) ;
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: " ) ) ;
std : : vector < GLFormat > dmaFormats ;
for ( auto & fmt : formats ) {
std : : vector < std : : pair < uint64_t , bool > > mods ;
auto ret = getModsForFormat ( fmt ) ;
if ( ! ret . has_value ( ) )
continue ;
mods = * ret ;
hasModifiers = hasModifiers | | mods . size ( ) > 0 ;
// EGL can always do implicit modifiers.
mods . push_back ( { DRM_FORMAT_MOD_INVALID , true } ) ;
for ( auto & [ mod , external ] : mods ) {
dmaFormats . push_back ( GLFormat {
. 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 ) ) ) ;
for ( auto & [ mod , external ] : mods ) {
auto modName = drmGetFormatModifierName ( mod ) ;
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " EGL: | with modifier 0x{:x}: {} " , mod , modName ? modName : " ?unknown? " ) ) ) ;
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 ;
}
2024-07-11 20:41:53 +02:00
SP < CDRMRenderer > CDRMRenderer : : attempt ( int drmfd , SP < CBackend > backend_ ) {
SP < CDRMRenderer > renderer = SP < CDRMRenderer > ( new CDRMRenderer ( ) ) ;
renderer - > drmFD = drmfd ;
renderer - > backend = backend_ ;
gBackend = backend_ ;
const std : : string EGLEXTENSIONS = ( const char * ) eglQueryString ( EGL_NO_DISPLAY , EGL_EXTENSIONS ) ;
if ( ! EGLEXTENSIONS . contains ( " KHR_platform_gbm " ) ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, no gbm support " ) ;
return nullptr ;
}
// init gbm stuff
renderer - > gbm . fd = openRenderNode ( drmfd ) ;
if ( ! renderer - > gbm . fd ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, no gbm fd " ) ;
return nullptr ;
}
renderer - > gbm . device = gbm_create_device ( renderer - > gbm . fd ) ;
if ( ! renderer - > gbm . device ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, no gbm device " ) ;
return nullptr ;
}
// init egl
if ( eglBindAPI ( EGL_OPENGL_ES_API ) = = EGL_FALSE ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, eglBindAPI failed " ) ;
return nullptr ;
}
loadGLProc ( & renderer - > egl . eglGetPlatformDisplayEXT , " eglGetPlatformDisplayEXT " ) ;
loadGLProc ( & renderer - > egl . eglCreateImageKHR , " eglCreateImageKHR " ) ;
loadGLProc ( & renderer - > egl . eglDestroyImageKHR , " eglDestroyImageKHR " ) ;
loadGLProc ( & renderer - > egl . glEGLImageTargetTexture2DOES , " glEGLImageTargetTexture2DOES " ) ;
loadGLProc ( & renderer - > egl . glEGLImageTargetRenderbufferStorageOES , " glEGLImageTargetRenderbufferStorageOES " ) ;
2024-07-12 11:16:47 +02:00
loadGLProc ( & renderer - > egl . eglQueryDmaBufFormatsEXT , " eglQueryDmaBufFormatsEXT " ) ;
loadGLProc ( & renderer - > egl . eglQueryDmaBufModifiersEXT , " eglQueryDmaBufModifiersEXT " ) ;
2024-07-11 20:41:53 +02:00
if ( ! renderer - > egl . eglGetPlatformDisplayEXT ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, no eglGetPlatformDisplayEXT " ) ;
return nullptr ;
}
if ( ! renderer - > egl . eglCreateImageKHR ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, no eglCreateImageKHR " ) ;
return nullptr ;
}
2024-07-12 11:16:47 +02:00
if ( ! renderer - > egl . eglQueryDmaBufFormatsEXT ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, no eglQueryDmaBufFormatsEXT " ) ;
return nullptr ;
}
if ( ! renderer - > egl . eglQueryDmaBufModifiersEXT ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, no eglQueryDmaBufModifiersEXT " ) ;
return nullptr ;
}
2024-07-11 20:41:53 +02:00
std : : vector < EGLint > attrs = { EGL_NONE } ;
renderer - > egl . display = renderer - > egl . eglGetPlatformDisplayEXT ( EGL_PLATFORM_GBM_KHR , renderer - > gbm . device , attrs . data ( ) ) ;
if ( renderer - > egl . display = = EGL_NO_DISPLAY ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, eglGetPlatformDisplayEXT failed " ) ;
return nullptr ;
}
EGLint major , minor ;
if ( eglInitialize ( renderer - > egl . display , & major , & minor ) = = EGL_FALSE ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, eglInitialize failed " ) ;
return nullptr ;
}
attrs . clear ( ) ;
const std : : string EGLEXTENSIONS2 = ( const char * ) eglQueryString ( renderer - > egl . display , EGL_EXTENSIONS ) ;
if ( EGLEXTENSIONS2 . contains ( " IMG_context_priority " ) ) {
attrs . push_back ( EGL_CONTEXT_PRIORITY_LEVEL_IMG ) ;
attrs . push_back ( EGL_CONTEXT_PRIORITY_HIGH_IMG ) ;
}
2024-07-11 23:00:50 +02:00
if ( EGLEXTENSIONS2 . contains ( " EXT_create_context_robustness " ) ) {
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
if ( ! EGLEXTENSIONS2 . contains ( " EXT_image_dma_buf_import_modifiers " ) ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, no EXT_image_dma_buf_import_modifiers ext " ) ;
return nullptr ;
}
if ( ! EGLEXTENSIONS2 . contains ( " EXT_image_dma_buf_import " ) ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, no EXT_image_dma_buf_import ext " ) ;
return nullptr ;
}
2024-07-11 20:41:53 +02:00
attrs . push_back ( EGL_CONTEXT_MAJOR_VERSION ) ;
attrs . push_back ( 2 ) ;
attrs . push_back ( EGL_CONTEXT_MINOR_VERSION ) ;
attrs . push_back ( 0 ) ;
attrs . push_back ( EGL_CONTEXT_OPENGL_DEBUG ) ;
attrs . push_back ( EGL_FALSE ) ;
attrs . push_back ( EGL_NONE ) ;
renderer - > egl . context = eglCreateContext ( renderer - > egl . display , EGL_NO_CONFIG_KHR , EGL_NO_CONTEXT , attrs . data ( ) ) ;
if ( renderer - > egl . context = = EGL_NO_CONTEXT ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, eglCreateContext failed " ) ;
return nullptr ;
}
if ( EGLEXTENSIONS2 . contains ( " IMG_context_priority " ) ) {
EGLint priority = EGL_CONTEXT_PRIORITY_MEDIUM_IMG ;
eglQueryContext ( renderer - > egl . display , renderer - > egl . context , EGL_CONTEXT_PRIORITY_LEVEL_IMG , & priority ) ;
if ( priority ! = EGL_CONTEXT_PRIORITY_HIGH_IMG )
backend_ - > log ( AQ_LOG_DEBUG , " CDRMRenderer: didnt get a high priority context " ) ;
else
backend_ - > log ( AQ_LOG_DEBUG , " CDRMRenderer: got a high priority context " ) ;
}
// init shaders
renderer - > setEGL ( ) ;
2024-07-12 11:16:47 +02:00
if ( ! renderer - > initDRMFormats ( ) ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, initDRMFormats failed " ) ;
return nullptr ;
}
2024-07-11 20:41:53 +02:00
renderer - > gl . shader . program = createProgram ( VERT_SRC , FRAG_SRC ) ;
if ( renderer - > gl . shader . program = = 0 ) {
backend_ - > log ( AQ_LOG_ERROR , " CDRMRenderer: fail, shader failed " ) ;
return nullptr ;
}
renderer - > gl . shader . proj = glGetUniformLocation ( renderer - > gl . shader . program , " proj " ) ;
renderer - > gl . shader . posAttrib = glGetAttribLocation ( renderer - > gl . shader . program , " pos " ) ;
renderer - > gl . shader . texAttrib = glGetAttribLocation ( renderer - > gl . shader . program , " texcoord " ) ;
renderer - > gl . shader . tex = glGetUniformLocation ( renderer - > gl . shader . program , " tex " ) ;
renderer - > restoreEGL ( ) ;
backend_ - > log ( AQ_LOG_DEBUG , " CDRMRenderer: success " ) ;
return renderer ;
}
void CDRMRenderer : : setEGL ( ) {
savedEGLState . display = eglGetCurrentDisplay ( ) ;
savedEGLState . context = eglGetCurrentContext ( ) ;
savedEGLState . draw = eglGetCurrentSurface ( EGL_DRAW ) ;
savedEGLState . read = eglGetCurrentSurface ( EGL_READ ) ;
if ( ! eglMakeCurrent ( egl . display , EGL_NO_SURFACE , EGL_NO_SURFACE , egl . context ) )
backend - > log ( AQ_LOG_WARNING , " CDRMRenderer: setEGL eglMakeCurrent failed " ) ;
}
void CDRMRenderer : : restoreEGL ( ) {
EGLDisplay dpy = savedEGLState . display ? savedEGLState . display : egl . display ;
// egl can't handle this
if ( dpy = = EGL_NO_DISPLAY )
return ;
if ( ! eglMakeCurrent ( dpy , savedEGLState . draw , savedEGLState . read , savedEGLState . context ) )
backend - > log ( AQ_LOG_WARNING , " CDRMRenderer: restoreEGL eglMakeCurrent failed " ) ;
}
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 ;
} attrNames [ 4 ] = {
{ EGL_DMA_BUF_PLANE0_FD_EXT , EGL_DMA_BUF_PLANE0_OFFSET_EXT , EGL_DMA_BUF_PLANE0_PITCH_EXT , EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT , EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT } ,
{ EGL_DMA_BUF_PLANE1_FD_EXT , EGL_DMA_BUF_PLANE1_OFFSET_EXT , EGL_DMA_BUF_PLANE1_PITCH_EXT , EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT , EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT } ,
{ EGL_DMA_BUF_PLANE2_FD_EXT , EGL_DMA_BUF_PLANE2_OFFSET_EXT , EGL_DMA_BUF_PLANE2_PITCH_EXT , EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT , EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT } ,
{ EGL_DMA_BUF_PLANE3_FD_EXT , EGL_DMA_BUF_PLANE3_OFFSET_EXT , EGL_DMA_BUF_PLANE3_PITCH_EXT , EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT , EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT } } ;
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 ) ;
EGLImageKHR image = egl . eglCreateImageKHR ( egl . display , EGL_NO_CONTEXT , EGL_LINUX_DMA_BUF_EXT , nullptr , ( int * ) attribs . data ( ) ) ;
if ( image = = EGL_NO_IMAGE_KHR ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " EGL: EGLCreateImageKHR failed: {} " , eglGetError ( ) ) ) ;
return EGL_NO_IMAGE_KHR ;
}
return image ;
}
# 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 ) ) ; \
} \
}
CDRMRenderer : : GLTex CDRMRenderer : : glTex ( Hyprutils : : Memory : : CSharedPointer < IBuffer > buffa ) {
2024-07-12 11:16:47 +02:00
GLTex 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 ;
for ( auto & fmt : formats ) {
if ( fmt . drmFormat ! = dma . format | | fmt . modifier ! = dma . modifier )
continue ;
2024-07-11 20:41:53 +02:00
2024-07-12 11:16:47 +02:00
backend - > log ( AQ_LOG_DEBUG , std : : format ( " CDRMRenderer::glTex: found format+mod, external = {} " , external ) ) ;
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 ) ) ;
GLCALL ( egl . glEGLImageTargetTexture2DOES ( tex . target , tex . image ) ) ;
GLCALL ( glBindTexture ( tex . target , 0 ) ) ;
2024-07-11 23:00:50 +02:00
2024-07-11 20:41:53 +02:00
return tex ;
}
inline const float fullVerts [ ] = {
1 , 0 , // top right
0 , 0 , // top left
1 , 1 , // bottom right
0 , 1 , // bottom left
} ;
bool CDRMRenderer : : blit ( SP < IBuffer > from , SP < IBuffer > to ) {
setEGL ( ) ;
if ( from - > dmabuf ( ) . size ! = to - > dmabuf ( ) . size ) {
backend - > log ( AQ_LOG_ERROR , " EGL (blit): buffer sizes mismatched " ) ;
return false ;
}
// 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.
GLTex fromTex ;
{
auto attachment = from - > attachments . get ( AQ_ATTACHMENT_DRM_RENDERER_DATA ) ;
if ( attachment ) {
TRACE ( backend - > log ( AQ_LOG_TRACE , " EGL (blit): From attachment found " ) ) ;
auto att = ( CDRMRendererBufferAttachment * ) attachment . get ( ) ;
fromTex = { att - > eglImage , att - > texid } ;
}
if ( ! fromTex . image ) {
backend - > log ( AQ_LOG_DEBUG , " EGL (blit): No attachment in from, creating a new image " ) ;
fromTex = glTex ( from ) ;
// should never remove anything, but JIC. We'll leak an EGLImage if this removes anything.
from - > attachments . removeByType ( AQ_ATTACHMENT_DRM_RENDERER_DATA ) ;
from - > attachments . add ( makeShared < CDRMRendererBufferAttachment > ( self , from , fromTex . image , 0 , 0 , fromTex . texid ) ) ;
}
}
2024-07-11 20:41:53 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " EGL (blit): fromTex id {}, image 0x{:x} " , fromTex . texid , ( uintptr_t ) fromTex . image ) ) ) ;
// 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 ( ) ;
{
auto attachment = to - > attachments . get ( AQ_ATTACHMENT_DRM_RENDERER_DATA ) ;
if ( attachment ) {
TRACE ( backend - > log ( AQ_LOG_TRACE , " EGL (blit): To attachment found " ) ) ;
auto att = ( CDRMRendererBufferAttachment * ) attachment . get ( ) ;
rboImage = att - > eglImage ;
fboID = att - > fbo ;
rboID = att - > rbo ;
}
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 ( ) ) ) ;
return false ;
}
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 ) ) ;
egl . glEGLImageTargetRenderbufferStorageOES ( GL_RENDERBUFFER , ( GLeglImageOES ) rboImage ) ;
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 ( ) ) ) ;
return false ;
}
2024-07-11 20:41:53 +02:00
2024-07-11 22:17:19 +02:00
// should never remove anything, but JIC. We'll leak an RBO and FBO if this removes anything.
to - > attachments . removeByType ( AQ_ATTACHMENT_DRM_RENDERER_DATA ) ;
to - > attachments . add ( makeShared < CDRMRendererBufferAttachment > ( self , to , rboImage , fboID , rboID , 0 ) ) ;
}
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 ) ;
// 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
2024-07-11 20:41:53 +02:00
GLCALL ( glUseProgram ( gl . shader . program ) ) ;
GLCALL ( glDisable ( GL_BLEND ) ) ;
GLCALL ( glDisable ( GL_SCISSOR_TEST ) ) ;
matrixTranspose ( glMtx , glMtx ) ;
GLCALL ( glUniformMatrix3fv ( gl . shader . proj , 1 , GL_FALSE , glMtx ) ) ;
GLCALL ( glUniform1i ( gl . shader . tex , 0 ) ) ;
GLCALL ( glVertexAttribPointer ( gl . shader . posAttrib , 2 , GL_FLOAT , GL_FALSE , 0 , fullVerts ) ) ;
GLCALL ( glVertexAttribPointer ( gl . shader . texAttrib , 2 , GL_FLOAT , GL_FALSE , 0 , fullVerts ) ) ;
GLCALL ( glEnableVertexAttribArray ( gl . shader . posAttrib ) ) ;
GLCALL ( glEnableVertexAttribArray ( gl . shader . texAttrib ) ) ;
GLCALL ( glDrawArrays ( GL_TRIANGLE_STRIP , 0 , 4 ) ) ;
GLCALL ( glDisableVertexAttribArray ( gl . shader . posAttrib ) ) ;
GLCALL ( glDisableVertexAttribArray ( gl . shader . texAttrib ) ) ;
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-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
2024-07-11 22:17:19 +02:00
restoreEGL ( ) ;
return true ;
}
2024-07-11 20:41:53 +02:00
2024-07-11 22:17:19 +02:00
void CDRMRenderer : : onBufferAttachmentDrop ( CDRMRendererBufferAttachment * attachment ) {
setEGL ( ) ;
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-11 22:17:19 +02:00
if ( attachment - > texid )
GLCALL ( glDeleteTextures ( 1 , & attachment - > texid ) ) ;
if ( attachment - > rbo )
GLCALL ( glDeleteRenderbuffers ( 1 , & attachment - > rbo ) ) ;
if ( attachment - > fbo )
GLCALL ( glDeleteFramebuffers ( 1 , & attachment - > fbo ) ) ;
if ( attachment - > eglImage )
egl . eglDestroyImageKHR ( egl . display , attachment - > eglImage ) ;
2024-07-11 20:41:53 +02:00
restoreEGL ( ) ;
2024-07-11 22:17:19 +02:00
}
2024-07-11 20:41:53 +02:00
2024-07-11 22:17:19 +02:00
CDRMRendererBufferAttachment : : CDRMRendererBufferAttachment ( Hyprutils : : Memory : : CWeakPointer < CDRMRenderer > renderer_ , Hyprutils : : Memory : : CSharedPointer < IBuffer > buffer ,
EGLImageKHR image , GLuint fbo_ , GLuint rbo_ , GLuint texid_ ) :
eglImage ( image ) , fbo ( fbo_ ) , rbo ( rbo_ ) , renderer ( renderer_ ) , texid ( texid_ ) {
bufferDestroy = buffer - > events . destroy . registerListener ( [ this ] ( std : : any d ) { renderer - > onBufferAttachmentDrop ( this ) ; } ) ;
2024-07-11 20:41:53 +02:00
}