@ -71,6 +71,21 @@
# include "EGL/egl.h" // Native platform windowing system interface
# include "EGL/eglext.h" // EGL extensions
# include <poll.h> // for drmHandleEvent poll
# include <errno.h> //for EBUSY, EAGAIN
# define MAX_CACHED_BOS 3
typedef struct {
struct gbm_bo * bo ;
uint32_t fb_id ; / / DRM framebuffer ID
} FramebufferCache ;
static FramebufferCache fbCache [ MAX_CACHED_BOS ] ;
static volatile int fbCacheCount = 0 ;
static volatile bool pendingFlip = false ;
static bool crtcSet = false ;
# ifndef EGL_OPENGL_ES3_BIT
# define EGL_OPENGL_ES3_BIT 0x40
# endif
@ -217,6 +232,7 @@ static const short linuxToRaylibMap[KEYMAP_SIZE] = {
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/ / Module Internal Functions Declaration
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int InitSwapScreenBuffer ( void ) ;
int InitPlatform ( void ) ; / / Initialize platform ( graphics , inputs and more )
void ClosePlatform ( void ) ; / / Close platform
@ -551,34 +567,207 @@ void DisableCursor(void)
CORE . Input . Mouse . cursorHidden = true ;
}
/ / Swap back buffer with front buffer ( screen drawing )
void SwapScreenBuffer ( void )
{
static void drm_fb_destroy_callback ( struct gbm_bo * bo , void * data ) {
uint32_t fb_id = ( uintptr_t ) data ;
/ / Remove from cache
for ( int i = 0 ; i < fbCacheCount ; i + + ) {
if ( fbCache [ i ] . bo = = bo ) {
TRACELOG ( LOG_INFO , " DRM: fb removed %u " , ( uintptr_t ) fb_id ) ;
drmModeRmFB ( platform . fd , fbCache [ i ] . fb_id ) ; / / Release DRM FB
/ / Shift remaining entries
for ( int j = i ; j < fbCacheCount - 1 ; j + + ) {
fbCache [ j ] = fbCache [ j + 1 ] ;
}
fbCacheCount - - ;
break ;
}
}
}
/ / Create or retrieve cached DRM FB for BO
static uint32_t get_or_create_fb_for_bo ( struct gbm_bo * bo ) {
/ / Try to find existing cache entry
for ( int i = 0 ; i < fbCacheCount ; i + + ) {
if ( fbCache [ i ] . bo = = bo ) {
return fbCache [ i ] . fb_id ;
}
}
/ / Create new entry if cache not full
if ( fbCacheCount > = MAX_CACHED_BOS ) {
/ / FB cache full !
return 0 ;
}
uint32_t handle = gbm_bo_get_handle ( bo ) . u32 ;
uint32_t stride = gbm_bo_get_stride ( bo ) ;
uint32_t width = gbm_bo_get_width ( bo ) ;
uint32_t height = gbm_bo_get_height ( bo ) ;
uint32_t fb_id ;
if ( drmModeAddFB ( platform . fd , width , height , 24 , 32 , stride , handle , & fb_id ) ) {
/ / rmModeAddFB failed
return 0 ;
}
/ / Store in cache
fbCache [ fbCacheCount ] = ( FramebufferCache ) { . bo = bo , . fb_id = fb_id } ;
fbCacheCount + + ;
/ / Set destroy callback to auto - cleanup
gbm_bo_set_user_data ( bo , ( void * ) ( uintptr_t ) fb_id , drm_fb_destroy_callback ) ;
TRACELOG ( LOG_INFO , " DRM: added new bo %u " , ( uintptr_t ) fb_id ) ;
return fb_id ;
}
/ / Renders a blank frame to allocate initial buffers
void RenderBlankFrame ( ) {
glClearColor ( 0 , 0 , 0 , 1 ) ;
glClear ( GL_COLOR_BUFFER_BIT ) ;
eglSwapBuffers ( platform . device , platform . surface ) ;
/ / Ensure the buffer is processed
glFinish ( ) ;
}
/ / Initialize with first buffer only
int InitSwapScreenBuffer ( ) {
if ( ! platform . gbmSurface | | platform . fd < 0 ) {
TRACELOG ( LOG_ERROR , " DRM not initialized " ) ;
return - 1 ;
}
if ( ! platform . gbmSurface | | ( - 1 = = platform . fd ) | | ! platform . connector | | ! platform . crtc ) TRACELOG ( LOG_ERROR , " DISPLAY: DRM initialization failed to swap " ) ;
/ / Render a blank frame to allocate buffers
RenderBlankFrame ( ) ;
/ / Get first buffer
struct gbm_bo * bo = gbm_surface_lock_front_buffer ( platform . gbmSurface ) ;
if ( ! bo ) TRACELOG ( LOG_ERROR , " DISPLAY: Failed GBM to lock front buffer " ) ;
if ( ! bo ) {
TRACELOG ( LOG_ERROR , " Failed to lock initial buffer " ) ;
return - 1 ;
}
uint32_t fb = 0 ;
int result = drmModeAddFB ( platform . fd , platform . connector - > modes [ platform . modeIndex ] . hdisplay , platform . connector - > modes [ platform . modeIndex ] . vdisplay , 24 , 32 , gbm_bo_get_stride ( bo ) , gbm_bo_get_handle ( bo ) . u32 , & fb ) ;
if ( result ! = 0 ) TRACELOG ( LOG_ERROR , " DISPLAY: drmModeAddFB() failed with result: %d " , result ) ;
/ / Create FB for first buffer
uint32_t fb_id = get_or_create_fb_for_bo ( bo ) ;
if ( ! fb_id ) {
gbm_surface_release_buffer ( platform . gbmSurface , bo ) ;
return - 1 ;
}
result = drmModeSetCrtc ( platform . fd , platform . crtc - > crtc_id , fb , 0 , 0 , & platform . connector - > connector_id , 1 , & platform . connector - > modes [ platform . modeIndex ] ) ;
if ( result ! = 0 ) TRACELOG ( LOG_ERROR , " DISPLAY: drmModeSetCrtc() failed with result: %d " , result ) ;
/ / Initial CRTC setup
if ( drmModeSetCrtc ( platform . fd , platform . crtc - > crtc_id , fb_id ,
0 , 0 , & platform . connector - > connector_id , 1 ,
& platform . connector - > modes [ platform . modeIndex ] ) ) {
TRACELOG ( LOG_ERROR , " Initial CRTC setup failed: %s " , strerror ( errno ) ) ;
gbm_surface_release_buffer ( platform . gbmSurface , bo ) ;
return - 1 ;
}
if ( platform . prevFB )
{
result = drmModeRmFB ( platform . fd , platform . prevFB ) ;
if ( result ! = 0 ) TRACELOG ( LOG_ERROR , " DISPLAY: drmModeRmFB() failed with result: %d " , result ) ;
/ / Keep first buffer locked until flipped
platform . prevBO = bo ;
crtcSet = true ;
return 0 ;
}
/ / Static page flip handler
/ / this will be called once the drmModePageFlip ( ) finished from the drmHandleEvent ( platform . fd , & evctx ) ; context
static void page_flip_handler ( int fd , unsigned int frame ,
unsigned int sec , unsigned int usec ,
void * data ) {
( void ) fd ; ( void ) frame ; ( void ) sec ; ( void ) usec ; / / Unused
pendingFlip = false ;
struct gbm_bo * bo_to_release = ( struct gbm_bo * ) data ;
/ / Buffers are released after the flip completes ( via page_flip_handler ) , ensuring they ' re no longer in use .
/ / Prevents the GPU from writing to a buffer being scanned out
if ( bo_to_release ) {
gbm_surface_release_buffer ( platform . gbmSurface , bo_to_release ) ;
}
}
platform . prevFB = fb ;
/ / Swap implementation with proper caching
void SwapScreenBuffer ( ) {
static int loopCnt = 0 ;
loopCnt + + ;
static int errCnt [ 5 ] = { 0 } ;
if ( ! crtcSet | | ! platform . gbmSurface ) return ;
if ( platform . prevBO ) gbm_surface_release_buffer ( platform . gbmSurface , platform . prevBO ) ;
/ / call this only , if pendingFlip is not set
eglSwapBuffers ( platform . device , platform . surface ) ;
platform . prevBO = bo ;
/ / Process pending events non - blocking
drmEventContext evctx = {
. version = DRM_EVENT_CONTEXT_VERSION ,
. page_flip_handler = page_flip_handler
} ;
struct pollfd pfd = { . fd = platform . fd , . events = POLLIN } ;
/ / polling for event for 0 ms
while ( poll ( & pfd , 1 , 0 ) > 0 ) {
drmHandleEvent ( platform . fd , & evctx ) ;
}
/ / Skip if previous flip pending
if ( pendingFlip ) {
/ / Skip frame : flip pending
errCnt [ 0 ] + + ;
return ;
}
/ / Get new front buffer
struct gbm_bo * next_bo = gbm_surface_lock_front_buffer ( platform . gbmSurface ) ;
if ( ! next_bo ) {
/ / Failed to lock front buffer
errCnt [ 1 ] + + ;
return ;
}
/ / Get FB ID ( creates new one if needed )
uint32_t fb_id = get_or_create_fb_for_bo ( next_bo ) ;
if ( ! fb_id ) {
gbm_surface_release_buffer ( platform . gbmSurface , next_bo ) ;
errCnt [ 2 ] + + ;
return ;
}
/ / Attempt page flip
/* rmModePageFlip() schedules a buffer-flip for the next vblank and then
* notifies us about it . It takes a CRTC - id , fb - id and an arbitrary
* data - pointer and then schedules the page - flip . This is fully asynchronous and
* When the page - flip happens , the DRM - fd will become readable and we can call
* drmHandleEvent ( ) . This will read all vblank / page - flip events and call our
* modeset_page_flip_event ( ) callback with the data - pointer that we passed to
* drmModePageFlip ( ) . We simply call modeset_draw_dev ( ) then so the next frame
* is rendered . .
* returns immediately .
*/
if ( drmModePageFlip ( platform . fd , platform . crtc - > crtc_id , fb_id ,
DRM_MODE_PAGE_FLIP_EVENT , platform . prevBO ) ) {
if ( errno = = EBUSY ) {
/ / Display busy - skip flip
errCnt [ 3 ] + + ;
} else {
/ / Page flip failed
errCnt [ 4 ] + + ;
}
gbm_surface_release_buffer ( platform . gbmSurface , next_bo ) ;
return ;
}
/ / Success : update state
pendingFlip = true ;
platform . prevBO = next_bo ;
/ / successful usage , do benchmarking
/ / in every 10 sec , at 60F PS 60 * 10 - > 600
if ( loopCnt > = 600 ) {
TRACELOG ( LOG_INFO , " DRM err counters: %d, %d, %d, %d, %d, %d " , errCnt [ 0 ] , errCnt [ 1 ] , errCnt [ 2 ] , errCnt [ 3 ] , errCnt [ 4 ] , loopCnt ) ;
/ / reinit the errors
for ( int i = 0 ; i < 5 ; i + + ) {
errCnt [ i ] = 0 ;
}
loopCnt = 0 ;
}
}
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -910,7 +1099,8 @@ int InitPlatform(void)
EGL_BLUE_SIZE , 8 , / / BLUE color bit depth ( alternative : 5 )
EGL_ALPHA_SIZE , 8 , / / ALPHA bit depth ( required for transparent framebuffer )
/ / EGL_TRANSPARENT_TYPE , EGL_NONE , / / Request transparent framebuffer ( EGL_TRANSPARENT_RGB does not work on RPI )
EGL_DEPTH_SIZE , 24 , / / Depth buffer size ( Required to use Depth testing ! )
/ / ToDo : verify this . In 5.5 it is 16 , in master it was 24
EGL_DEPTH_SIZE , 16 , / / Depth buffer size ( Required to use Depth testing ! )
/ / EGL_STENCIL_SIZE , 8 , / / Stencil buffer size
EGL_SAMPLE_BUFFERS , sampleBuffer , / / Activate MSAA
EGL_SAMPLES , samples , / / 4 x Antialiasing if activated ( Free on MALI GPUs )
@ -1083,9 +1273,14 @@ int InitPlatform(void)
CORE . Storage . basePath = GetWorkingDirectory ( ) ;
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TRACELOG ( LOG_INFO , " PLATFORM: DRM: Initialized successfully " ) ;
if ( InitSwapScreenBuffer ( ) = = 0 ) {
TRACELOG ( LOG_INFO , " PLATFORM: DRM: Initialized successfully " ) ;
return 0 ;
} else {
TRACELOG ( LOG_INFO , " PLATFORM: DRM: Initialized failed " ) ;
return - 1 ;
}
return 0 ;
}
/ / Close platform