瀏覽代碼

REVIEWED: DRM cache buffers support #4988

Ray 1 天之前
父節點
當前提交
c9f9219fa6
共有 1 個文件被更改,包括 136 次插入131 次删除
  1. 136 131
      src/platforms/rcore_drm.c

+ 136 - 131
src/platforms/rcore_drm.c

@@ -48,11 +48,11 @@
 *
 **********************************************************************************************/
 
-#include <fcntl.h>   // POSIX file control definitions - open(), creat(), fcntl()
-#include <unistd.h>  // POSIX standard function definitions - read(), close(), STDIN_FILENO
-#include <termios.h> // POSIX terminal control definitions - tcgetattr(), tcsetattr()
-#include <pthread.h> // POSIX threads management (inputs reading)
-#include <dirent.h>  // POSIX directory browsing
+#include <fcntl.h>          // POSIX file control definitions - open(), creat(), fcntl()
+#include <unistd.h>         // POSIX standard function definitions - read(), close(), STDIN_FILENO
+#include <termios.h>        // POSIX terminal control definitions - tcgetattr(), tcsetattr()
+#include <pthread.h>        // POSIX threads management (inputs reading)
+#include <dirent.h>         // POSIX directory browsing
 
 #include <sys/ioctl.h>      // Required for: ioctl() - UNIX System call for device-specific input/output operations
 #include <linux/kd.h>       // Linux: KDSKBMODE, K_MEDIUMRAM constants definition
@@ -71,23 +71,13 @@
 #include "EGL/egl.h"        // Native platform windowing system interface
 #include "EGL/eglext.h"     // EGL extensions
 
+// NOTE: DRM cache enables triple buffered DRM caching
 #if defined(SUPPORT_DRM_CACHE)
-#include <poll.h>  // for drmHandleEvent poll
-#include <errno.h> //for EBUSY, EAGAIN
+    #include <poll.h>       // Required for: drmHandleEvent() poll
+    #include <errno.h>      // Required for: EBUSY, EAGAIN
 
-#define MAX_CACHED_BOS 3
-
-typedef struct {
-    struct gbm_bo *bo;
-    uint32_t fbId;  // DRM framebuffer ID
-} FramebufferCache;
-
-static FramebufferCache fbCache[MAX_CACHED_BOS] = {0};
-static volatile int fbCacheCount = 0;
-static volatile bool pendingFlip = false;
-static bool crtcSet = false;
-
-#endif //SUPPORT_DRM_CACHE
+    #define MAX_DRM_CACHED_BUFFERS  3
+#endif // SUPPORT_DRM_CACHE
 
 #ifndef EGL_OPENGL_ES3_BIT
     #define EGL_OPENGL_ES3_BIT  0x40
@@ -96,18 +86,16 @@ static bool crtcSet = false;
 //----------------------------------------------------------------------------------
 // Defines and Macros
 //----------------------------------------------------------------------------------
-#define USE_LAST_TOUCH_DEVICE       // When multiple touchscreens are connected, only use the one with the highest event<N> number
+#define USE_LAST_TOUCH_DEVICE           // When multiple touchscreens are connected, only use the one with the highest event<N> number
 
-#define DEFAULT_EVDEV_PATH       "/dev/input/"      // Path to the linux input events
+#define DEFAULT_EVDEV_PATH "/dev/input/"    // Path to the linux input events
 
-// So actually the biggest key is KEY_CNT but we only really map the keys up to
-// KEY_ALS_TOGGLE
+// Actually biggest key is KEY_CNT but we only really map the keys up to KEY_ALS_TOGGLE
 #define KEYMAP_SIZE KEY_ALS_TOGGLE
 
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
-
 typedef struct {
     // Display data
     int fd;                             // File descriptor for /dev/dri/...
@@ -147,6 +135,18 @@ typedef struct {
     int gamepadCount;                   // The number of gamepads registered
 } PlatformData;
 
+#if defined(SUPPORT_DRM_CACHE)
+typedef struct {
+    struct gbm_bo *bo;      // Graphics buffer object
+    uint32_t fbId;          // DRM framebuffer ID
+} FramebufferCache;
+
+static FramebufferCache fbCache[MAX_DRM_CACHED_BUFFERS] = { 0 };
+static volatile int fbCacheCount = 0;
+static volatile bool pendingFlip = false;
+static bool crtcSet = false;
+#endif // SUPPORT_DRM_CACHE
+
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
@@ -570,18 +570,23 @@ void DisableCursor(void)
 }
 
 #if defined(SUPPORT_DRM_CACHE)
-//callback to destroy cached framebuffer, set by gbm_bo_set_user_data()
-static void DestroyFrameBufferCallback(struct gbm_bo *bo, void *data) {
+
+// Destroy cached framebuffer callback, set by gbm_bo_set_user_data()
+static void DestroyFrameBufferCallback(struct gbm_bo *bo, void *data)
+{
     uint32_t fbId = (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)fbId);
+    for (int i = 0; i < fbCacheCount; i++)
+    {
+        if (fbCache[i].bo == bo)
+        {
+            TRACELOG(LOG_INFO, "DISPLAY: DRM: Framebuffer removed [%u]", (uintptr_t)fbId);
             drmModeRmFB(platform.fd, fbCache[i].fbId); // Release DRM FB
+
             // Shift remaining entries
-            for (int j = i; j < fbCacheCount - 1; j++) {
-                fbCache[j] = fbCache[j + 1];
-            }
+            for (int j = i; j < fbCacheCount - 1; j++) fbCache[j] = fbCache[j + 1];
+
             fbCacheCount--;
             break;
         }
@@ -589,56 +594,53 @@ static void DestroyFrameBufferCallback(struct gbm_bo *bo, void *data) {
 }
 
 // Create or retrieve cached DRM FB for BO
-static uint32_t GetOrCreateFbForBo(struct gbm_bo *bo) {
+static uint32_t GetOrCreateFbForBo(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].fbId;
-        }
+    for (int i = 0; i < fbCacheCount; i++)
+    {
+        if (fbCache[i].bo == bo) return fbCache[i].fbId;
     }
 
     // Create new entry if cache not full
-    if (fbCacheCount >= MAX_CACHED_BOS) {
-        //FB cache full!
-        return 0;
-    }
+    if (fbCacheCount >= MAX_DRM_CACHED_BUFFERS) return 0; // FB cache full
 
     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 fbId;
-    if (drmModeAddFB(platform.fd, width, height, 24, 32, stride, handle, &fbId)) {
-        //rmModeAddFB failed
-        return 0;
-    }
+    uint32_t fbId = 0;
+    if (drmModeAddFB(platform.fd, width, height, 24, 32, stride, handle, &fbId)) return 0;
 
     // Store in cache
     fbCache[fbCacheCount] = (FramebufferCache){ .bo = bo, .fbId = fbId };
     fbCacheCount++;
 
     // Set destroy callback to auto-cleanup
-    gbm_bo_set_user_data(bo, (void*)(uintptr_t)fbId, DestroyFrameBufferCallback);
+    gbm_bo_set_user_data(bo, (void *)(uintptr_t)fbId, DestroyFrameBufferCallback);
+
+    TRACELOG(LOG_INFO, "DISPLAY: DRM: Added new buffer object [%u]" , (uintptr_t)fbId);
 
-    TRACELOG(LOG_INFO, "DRM: added new bo %u" , (uintptr_t)fbId);
     return fbId;
 }
 
 // Renders a blank frame to allocate initial buffers
-void RenderBlankFrame() {
+// TODO: WARNING: Platform layers do not include OpenGL code!
+void RenderBlankFrame()
+{
     glClearColor(0, 0, 0, 1);
     glClear(GL_COLOR_BUFFER_BIT);
     eglSwapBuffers(platform.device, platform.surface);
-    
-    // Ensure the buffer is processed
-    glFinish();
+    glFinish(); // Ensure the buffer is processed
 }
 
 // Initialize with first buffer only
-int InitSwapScreenBuffer() {
-    if (!platform.gbmSurface || platform.fd < 0) {
-        TRACELOG(LOG_ERROR, "DRM not initialized");
+int InitSwapScreenBuffer()
+{
+    if (!platform.gbmSurface || (platform.fd < 0))
+    {
+        TRACELOG(LOG_ERROR, "DISPLAY: DRM: Swap buffers can not be initialized");
         return -1;
     }
 
@@ -647,23 +649,24 @@ int InitSwapScreenBuffer() {
 
     // Get first buffer
     struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface);
-    if (!bo) {
-        TRACELOG(LOG_ERROR, "Failed to lock initial buffer");
+    if (!bo)
+    {
+        TRACELOG(LOG_ERROR, "DISPLAY: DRM: Failed to lock initial swap buffer");
         return -1;
     }
 
     // Create FB for first buffer
     uint32_t fbId = GetOrCreateFbForBo(bo);
-    if (!fbId) {
+    if (!fbId)
+    {
         gbm_surface_release_buffer(platform.gbmSurface, bo);
         return -1;
     }
 
     // Initial CRTC setup
-    if (drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fbId,
-                       0, 0, &platform.connector->connector_id, 1,
-                       &platform.connector->modes[platform.modeIndex])) {
-        TRACELOG(LOG_ERROR, "Initial CRTC setup failed: %s", strerror(errno));
+    if (drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fbId, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]))
+    {
+        TRACELOG(LOG_ERROR, "DISPLAY: DRM: Failed to initialize CRTC setup. ERROR: %s", strerror(errno));
         gbm_surface_release_buffer(platform.gbmSurface, bo);
         return -1;
     }
@@ -671,32 +674,38 @@ int InitSwapScreenBuffer() {
     // 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 PageFlipHandler(int fd, unsigned int frame, 
-                              unsigned int sec, unsigned int usec, 
-                              void *data) {
-    (void)fd; (void)frame; (void)sec; (void)usec; // Unused
+// Static page flip handler
+// NOTE: Called once the drmModePageFlip() finished from the drmHandleEvent() context
+static void PageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data)
+{
+    // Unused inputs
+    (void)fd;
+    (void)frame;
+    (void)sec;
+    (void)usec;
+
     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.
+    struct gbm_bo *boToRelease = (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);
-    }
+    if (boToRelease) gbm_surface_release_buffer(platform.gbmSurface, boToRelease);
 }
 
 // Swap implementation with proper caching
-void SwapScreenBuffer() {
+void SwapScreenBuffer()
+{
+    if (!crtcSet || !platform.gbmSurface) return;
+
     static int loopCnt = 0;
-    loopCnt++;
     static int errCnt[5] = {0};
-    if (!crtcSet || !platform.gbmSurface) return;
+    loopCnt++;
 
-    //call this only, if pendingFlip is not set
+    // Call this only, if pendingFlip is not set
     eglSwapBuffers(platform.device, platform.surface);
 
     // Process pending events non-blocking
@@ -704,76 +713,68 @@ void SwapScreenBuffer() {
         .version = DRM_EVENT_CONTEXT_VERSION,
         .page_flip_handler = PageFlipHandler
     };
-    
+
     struct pollfd pfd = { .fd = platform.fd, .events = POLLIN };
-    //polling for event for 0ms
-    while (poll(&pfd, 1, 0) > 0) {
-        drmHandleEvent(platform.fd, &evctx);
-    }
+
+    // Polling for event for 0ms
+    while (poll(&pfd, 1, 0) > 0) drmHandleEvent(platform.fd, &evctx);
 
     // Skip if previous flip pending
-    if (pendingFlip) {
-        //Skip frame: flip pending
-        errCnt[0]++;
+    if (pendingFlip)
+    {
+        errCnt[0]++; // Skip frame: flip pending
         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
+    struct gbm_bo *nextBO = gbm_surface_lock_front_buffer(platform.gbmSurface);
+    if (!nextBO) // Failed to lock front buffer
+    {
         errCnt[1]++;
         return;
     }
 
     // Get FB ID (creates new one if needed)
-    uint32_t fbId = GetOrCreateFbForBo(next_bo);
-    if (!fbId) {
-        gbm_surface_release_buffer(platform.gbmSurface, next_bo);
+    uint32_t fbId = GetOrCreateFbForBo(nextBO);
+    if (!fbId)
+    {
+        gbm_surface_release_buffer(platform.gbmSurface, nextBO);
         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, fbId,
-                       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);
+    // NOTE: 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, fbId, DRM_MODE_PAGE_FLIP_EVENT, platform.prevBO))
+    {
+        if (errno == EBUSY) errCnt[3]++; // Display busy - skip flip
+        else errCnt[4]++; // Page flip failed
+
+        gbm_surface_release_buffer(platform.gbmSurface, nextBO);
         return;
     }
 
     // Success: update state
     pendingFlip = true;
-    
-    platform.prevBO = next_bo;
-    //successful usage, do benchmarking
-    //in every 10 sec, at 60FPS 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;
-        }
+    platform.prevBO = nextBO;
+
+/*
+    // Some benchmarking code
+    if (loopCnt >= 600)
+    {
+        TRACELOG(LOG_INFO, "DRM: Error counters: %d, %d, %d, %d, %d, %d", errCnt[0], errCnt[1], errCnt[2], errCnt[3], errCnt[4], loopCnt);
+        for (int i = 0; i < 5; i++) errCnt[i] = 0;
         loopCnt = 0;
     }
+*/
 }
-#else //SUPPORT_DRM_CACHE is not defined
+
+#else // !SUPPORT_DRM_CACHE
+
 // Swap back buffer with front buffer (screen drawing)
 void SwapScreenBuffer(void)
 {
@@ -803,7 +804,8 @@ void SwapScreenBuffer(void)
 
     platform.prevBO = bo;
 }
-#endif //SUPPORT_DRM_CACHE
+#endif // SUPPORT_DRM_CACHE
+
 //----------------------------------------------------------------------------------
 // Module Functions Definition: Misc
 //----------------------------------------------------------------------------------
@@ -1307,17 +1309,20 @@ int InitPlatform(void)
     //----------------------------------------------------------------------------
 
 #if defined(SUPPORT_DRM_CACHE)
-    if(InitSwapScreenBuffer() == 0) {
-#endif//SUPPORT_DRM_CACHE
+    if (InitSwapScreenBuffer() == 0)
+    {
         TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully");
         return 0;
-#if defined(SUPPORT_DRM_CACHE)
-    } else {
+    }
+    else
+    {
         TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized failed");
         return -1;
     }
-#endif //SUPPORT_DRM_CACHE
-
+#else // !SUPPORT_DRM_CACHE
+    TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully");
+    return 0;
+#endif
 }
 
 // Close platform