|
71 | 71 | #include "EGL/egl.h" // Native platform windowing system interface
|
72 | 72 | #include "EGL/eglext.h" // EGL extensions
|
73 | 73 |
|
| 74 | +#if defined(SUPPORT_DRM_CACHE) |
| 75 | +#include <poll.h> // for drmHandleEvent poll |
| 76 | +#include <errno.h> //for EBUSY, EAGAIN |
| 77 | + |
| 78 | +#define MAX_CACHED_BOS 3 |
| 79 | + |
| 80 | +typedef struct { |
| 81 | + struct gbm_bo *bo; |
| 82 | + uint32_t fbId; // DRM framebuffer ID |
| 83 | +} FramebufferCache; |
| 84 | + |
| 85 | +static FramebufferCache fbCache[MAX_CACHED_BOS] = {0}; |
| 86 | +static volatile int fbCacheCount = 0; |
| 87 | +static volatile bool pendingFlip = false; |
| 88 | +static bool crtcSet = false; |
| 89 | + |
| 90 | +#endif //SUPPORT_DRM_CACHE |
| 91 | + |
74 | 92 | #ifndef EGL_OPENGL_ES3_BIT
|
75 | 93 | #define EGL_OPENGL_ES3_BIT 0x40
|
76 | 94 | #endif
|
@@ -551,6 +569,211 @@ void DisableCursor(void)
|
551 | 569 | CORE.Input.Mouse.cursorHidden = true;
|
552 | 570 | }
|
553 | 571 |
|
| 572 | +#if defined(SUPPORT_DRM_CACHE) |
| 573 | +//callback to destroy cached framebuffer, set by gbm_bo_set_user_data() |
| 574 | +static void DestroyFrameBufferCallback(struct gbm_bo *bo, void *data) { |
| 575 | + uint32_t fbId = (uintptr_t)data; |
| 576 | + // Remove from cache |
| 577 | + for (int i = 0; i < fbCacheCount; i++) { |
| 578 | + if (fbCache[i].bo == bo) { |
| 579 | + TRACELOG(LOG_INFO, "DRM: fb removed %u", (uintptr_t)fbId); |
| 580 | + drmModeRmFB(platform.fd, fbCache[i].fbId); // Release DRM FB |
| 581 | + // Shift remaining entries |
| 582 | + for (int j = i; j < fbCacheCount - 1; j++) { |
| 583 | + fbCache[j] = fbCache[j + 1]; |
| 584 | + } |
| 585 | + fbCacheCount--; |
| 586 | + break; |
| 587 | + } |
| 588 | + } |
| 589 | +} |
| 590 | + |
| 591 | +// Create or retrieve cached DRM FB for BO |
| 592 | +static uint32_t GetOrCreateFbForBo(struct gbm_bo *bo) { |
| 593 | + // Try to find existing cache entry |
| 594 | + for (int i = 0; i < fbCacheCount; i++) { |
| 595 | + if (fbCache[i].bo == bo) { |
| 596 | + return fbCache[i].fbId; |
| 597 | + } |
| 598 | + } |
| 599 | + |
| 600 | + // Create new entry if cache not full |
| 601 | + if (fbCacheCount >= MAX_CACHED_BOS) { |
| 602 | + //FB cache full! |
| 603 | + return 0; |
| 604 | + } |
| 605 | + |
| 606 | + uint32_t handle = gbm_bo_get_handle(bo).u32; |
| 607 | + uint32_t stride = gbm_bo_get_stride(bo); |
| 608 | + uint32_t width = gbm_bo_get_width(bo); |
| 609 | + uint32_t height = gbm_bo_get_height(bo); |
| 610 | + |
| 611 | + uint32_t fbId; |
| 612 | + if (drmModeAddFB(platform.fd, width, height, 24, 32, stride, handle, &fbId)) { |
| 613 | + //rmModeAddFB failed |
| 614 | + return 0; |
| 615 | + } |
| 616 | + |
| 617 | + // Store in cache |
| 618 | + fbCache[fbCacheCount] = (FramebufferCache){ .bo = bo, .fbId = fbId }; |
| 619 | + fbCacheCount++; |
| 620 | + |
| 621 | + // Set destroy callback to auto-cleanup |
| 622 | + gbm_bo_set_user_data(bo, (void*)(uintptr_t)fbId, DestroyFrameBufferCallback); |
| 623 | + |
| 624 | + TRACELOG(LOG_INFO, "DRM: added new bo %u" , (uintptr_t)fbId); |
| 625 | + return fbId; |
| 626 | +} |
| 627 | + |
| 628 | +// Renders a blank frame to allocate initial buffers |
| 629 | +void RenderBlankFrame() { |
| 630 | + glClearColor(0, 0, 0, 1); |
| 631 | + glClear(GL_COLOR_BUFFER_BIT); |
| 632 | + eglSwapBuffers(platform.device, platform.surface); |
| 633 | + |
| 634 | + // Ensure the buffer is processed |
| 635 | + glFinish(); |
| 636 | +} |
| 637 | + |
| 638 | +// Initialize with first buffer only |
| 639 | +int InitSwapScreenBuffer() { |
| 640 | + if (!platform.gbmSurface || platform.fd < 0) { |
| 641 | + TRACELOG(LOG_ERROR, "DRM not initialized"); |
| 642 | + return -1; |
| 643 | + } |
| 644 | + |
| 645 | + // Render a blank frame to allocate buffers |
| 646 | + RenderBlankFrame(); |
| 647 | + |
| 648 | + // Get first buffer |
| 649 | + struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface); |
| 650 | + if (!bo) { |
| 651 | + TRACELOG(LOG_ERROR, "Failed to lock initial buffer"); |
| 652 | + return -1; |
| 653 | + } |
| 654 | + |
| 655 | + // Create FB for first buffer |
| 656 | + uint32_t fbId = GetOrCreateFbForBo(bo); |
| 657 | + if (!fbId) { |
| 658 | + gbm_surface_release_buffer(platform.gbmSurface, bo); |
| 659 | + return -1; |
| 660 | + } |
| 661 | + |
| 662 | + // Initial CRTC setup |
| 663 | + if (drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fbId, |
| 664 | + 0, 0, &platform.connector->connector_id, 1, |
| 665 | + &platform.connector->modes[platform.modeIndex])) { |
| 666 | + TRACELOG(LOG_ERROR, "Initial CRTC setup failed: %s", strerror(errno)); |
| 667 | + gbm_surface_release_buffer(platform.gbmSurface, bo); |
| 668 | + return -1; |
| 669 | + } |
| 670 | + |
| 671 | + // Keep first buffer locked until flipped |
| 672 | + platform.prevBO = bo; |
| 673 | + crtcSet = true; |
| 674 | + return 0; |
| 675 | +} |
| 676 | + |
| 677 | +// Static page flip handler |
| 678 | +// this will be called once the drmModePageFlip() finished from the drmHandleEvent(platform.fd, &evctx); context |
| 679 | +static void PageFlipHandler(int fd, unsigned int frame, |
| 680 | + unsigned int sec, unsigned int usec, |
| 681 | + void *data) { |
| 682 | + (void)fd; (void)frame; (void)sec; (void)usec; // Unused |
| 683 | + pendingFlip = false; |
| 684 | + struct gbm_bo *bo_to_release = (struct gbm_bo *)data; |
| 685 | + //Buffers are released after the flip completes (via page_flip_handler), ensuring they're no longer in use. |
| 686 | + // Prevents the GPU from writing to a buffer being scanned out |
| 687 | + if (bo_to_release) { |
| 688 | + gbm_surface_release_buffer(platform.gbmSurface, bo_to_release); |
| 689 | + } |
| 690 | +} |
| 691 | + |
| 692 | +// Swap implementation with proper caching |
| 693 | +void SwapScreenBuffer() { |
| 694 | + static int loopCnt = 0; |
| 695 | + loopCnt++; |
| 696 | + static int errCnt[5] = {0}; |
| 697 | + if (!crtcSet || !platform.gbmSurface) return; |
| 698 | + |
| 699 | + //call this only, if pendingFlip is not set |
| 700 | + eglSwapBuffers(platform.device, platform.surface); |
| 701 | + |
| 702 | + // Process pending events non-blocking |
| 703 | + drmEventContext evctx = { |
| 704 | + .version = DRM_EVENT_CONTEXT_VERSION, |
| 705 | + .page_flip_handler = PageFlipHandler |
| 706 | + }; |
| 707 | + |
| 708 | + struct pollfd pfd = { .fd = platform.fd, .events = POLLIN }; |
| 709 | + //polling for event for 0ms |
| 710 | + while (poll(&pfd, 1, 0) > 0) { |
| 711 | + drmHandleEvent(platform.fd, &evctx); |
| 712 | + } |
| 713 | + |
| 714 | + // Skip if previous flip pending |
| 715 | + if (pendingFlip) { |
| 716 | + //Skip frame: flip pending |
| 717 | + errCnt[0]++; |
| 718 | + return; |
| 719 | + } |
| 720 | + |
| 721 | + // Get new front buffer |
| 722 | + struct gbm_bo *next_bo = gbm_surface_lock_front_buffer(platform.gbmSurface); |
| 723 | + if (!next_bo) { |
| 724 | + //Failed to lock front buffer |
| 725 | + errCnt[1]++; |
| 726 | + return; |
| 727 | + } |
| 728 | + |
| 729 | + // Get FB ID (creates new one if needed) |
| 730 | + uint32_t fbId = GetOrCreateFbForBo(next_bo); |
| 731 | + if (!fbId) { |
| 732 | + gbm_surface_release_buffer(platform.gbmSurface, next_bo); |
| 733 | + errCnt[2]++; |
| 734 | + return; |
| 735 | + } |
| 736 | + |
| 737 | + // Attempt page flip |
| 738 | + /* rmModePageFlip() schedules a buffer-flip for the next vblank and then |
| 739 | + * notifies us about it. It takes a CRTC-id, fb-id and an arbitrary |
| 740 | + * data-pointer and then schedules the page-flip. This is fully asynchronous and |
| 741 | + * When the page-flip happens, the DRM-fd will become readable and we can call |
| 742 | + * drmHandleEvent(). This will read all vblank/page-flip events and call our |
| 743 | + * modeset_page_flip_event() callback with the data-pointer that we passed to |
| 744 | + * drmModePageFlip(). We simply call modeset_draw_dev() then so the next frame |
| 745 | + * is rendered.. |
| 746 | + * returns immediately. |
| 747 | + */ |
| 748 | + if (drmModePageFlip(platform.fd, platform.crtc->crtc_id, fbId, |
| 749 | + DRM_MODE_PAGE_FLIP_EVENT, platform.prevBO)) { |
| 750 | + if (errno == EBUSY) { |
| 751 | + //Display busy - skip flip |
| 752 | + errCnt[3]++; |
| 753 | + } else { |
| 754 | + //Page flip failed |
| 755 | + errCnt[4]++; |
| 756 | + } |
| 757 | + gbm_surface_release_buffer(platform.gbmSurface, next_bo); |
| 758 | + return; |
| 759 | + } |
| 760 | + |
| 761 | + // Success: update state |
| 762 | + pendingFlip = true; |
| 763 | + |
| 764 | + platform.prevBO = next_bo; |
| 765 | + //successful usage, do benchmarking |
| 766 | + //in every 10 sec, at 60FPS 60*10 -> 600 |
| 767 | + if(loopCnt >= 600) { |
| 768 | + TRACELOG(LOG_INFO, "DRM err counters: %d, %d, %d, %d, %d, %d",errCnt[0],errCnt[1],errCnt[2],errCnt[3],errCnt[4], loopCnt); |
| 769 | + //reinit the errors |
| 770 | + for(int i=0;i<5;i++) { |
| 771 | + errCnt[i] = 0; |
| 772 | + } |
| 773 | + loopCnt = 0; |
| 774 | + } |
| 775 | +} |
| 776 | +#else //SUPPORT_DRM_CACHE is not defined |
554 | 777 | // Swap back buffer with front buffer (screen drawing)
|
555 | 778 | void SwapScreenBuffer(void)
|
556 | 779 | {
|
@@ -580,7 +803,7 @@ void SwapScreenBuffer(void)
|
580 | 803 |
|
581 | 804 | platform.prevBO = bo;
|
582 | 805 | }
|
583 |
| - |
| 806 | +#endif //SUPPORT_DRM_CACHE |
584 | 807 | //----------------------------------------------------------------------------------
|
585 | 808 | // Module Functions Definition: Misc
|
586 | 809 | //----------------------------------------------------------------------------------
|
@@ -1083,9 +1306,18 @@ int InitPlatform(void)
|
1083 | 1306 | CORE.Storage.basePath = GetWorkingDirectory();
|
1084 | 1307 | //----------------------------------------------------------------------------
|
1085 | 1308 |
|
1086 |
| - TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully"); |
| 1309 | +#if defined(SUPPORT_DRM_CACHE) |
| 1310 | + if(InitSwapScreenBuffer() == 0) { |
| 1311 | +#endif//SUPPORT_DRM_CACHE |
| 1312 | + TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully"); |
| 1313 | + return 0; |
| 1314 | +#if defined(SUPPORT_DRM_CACHE) |
| 1315 | + } else { |
| 1316 | + TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized failed"); |
| 1317 | + return -1; |
| 1318 | + } |
| 1319 | +#endif //SUPPORT_DRM_CACHE |
1087 | 1320 |
|
1088 |
| - return 0; |
1089 | 1321 | }
|
1090 | 1322 |
|
1091 | 1323 | // Close platform
|
|
0 commit comments