You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

关于通过HDMI从GPU内存快速绘制OpenGL ES渲染内容(60 fps、FullHD)的Linux子系统选型及DRM适用性的技术问询

Linux Subsystem Selection & OpenGL ES HDMI Rendering Guide

Which Linux Subsystem to Use?

Great question—if you’re targeting low-latency, FullHD@60fps OpenGL ES rendering over HDMI, you don’t need a heavy window system like X11 or Wayland. The optimal stack combines three lightweight, purpose-built components:

  • DRM (Direct Rendering Manager): The core subsystem for controlling display hardware (mode setting, page flipping, buffer scanning to HDMI)
  • libgbm (Generic Buffer Management): Bridges GPU memory (where OpenGL ES renders) and DRM (which displays the content) by allocating shared buffers
  • libEGL (with DRM/GBM backend): Creates an OpenGL ES context that can render directly into GBM-managed buffers

This stack is the standard for embedded Linux and high-performance display scenarios, delivering exactly the performance you need.

Is DRM Alone Sufficient?

Short answer: No, but it’s the foundation. DRM handles low-level hardware interaction, but it has no understanding of OpenGL ES rendering. You need GBM to manage GPU-accessible buffers that both OpenGL ES and DRM can use, plus EGL to tie the OpenGL ES context to those buffers. Together, DRM + GBM + EGL form a minimal, overhead-free pipeline that avoids window system bloat.

General Architecture Overview

Here’s the step-by-step workflow to achieve your goal:

  1. DRM Initialization
    • Open the DRM device node (typically /dev/dri/card0)
    • Enumerate display resources (connectors, CRTCs, planes)
    • Set the desired FullHD@60fps mode on the HDMI connector
  2. GBM Setup
    • Create a GBM device from the DRM file descriptor
    • Allocate 2+ GBM buffers (double/triple buffering to eliminate tearing)
  3. EGL Context Creation
    • Initialize EGL using the GBM device as the display
    • Select an EGL configuration compatible with OpenGL ES 2.0+ and your display format
    • Create EGL surfaces tied to each GBM buffer
    • Create and activate the OpenGL ES context
  4. OpenGL ES Rendering Loop
    • Bind the current EGL surface
    • Perform rendering (clear screen, draw primitives, etc.)
    • Trigger a buffer swap to queue the rendered frame for display
  5. DRM Page Flipping
    • Sync with the display’s vertical blank (VSYNC) to ensure 60fps without tearing
    • Wait for the page flip completion event before rendering the next frame

Example Source Code (Minimal Implementation)

Below is a stripped-down C example demonstrating the core flow (error handling is simplified for brevity):

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <drm/drm.h>
#include <drm/drm_mode.h>
#include <gbm.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>

// Helper: Find connected HDMI connector
static drmModeConnector* find_hdmi_connector(int drm_fd) {
    drmModeRes* res = drmModeGetResources(drm_fd);
    if (!res) return NULL;

    for (int i = 0; i < res->count_connectors; i++) {
        drmModeConnector* conn = drmModeGetConnector(drm_fd, res->connectors[i]);
        if (conn && conn->connector_type == DRM_MODE_CONNECTOR_HDMIA && conn->connection == DRM_MODE_CONNECTED) {
            drmModeFreeResources(res);
            return conn;
        }
        if (conn) drmModeFreeConnector(conn);
    }

    drmModeFreeResources(res);
    return NULL;
}

int main() {
    // Step 1: Initialize DRM
    int drm_fd = open("/dev/dri/card0", O_RDWR);
    if (drm_fd < 0) { perror("Failed to open DRM device"); return 1; }

    drmModeConnector* conn = find_hdmi_connector(drm_fd);
    if (!conn) { fprintf(stderr, "No connected HDMI connector found\n"); return 1; }
    drmModeModeInfo* mode = &conn->modes[0]; // Assume first mode is FullHD@60

    // Set display mode (simplified)
    if (drmModeSetCrtc(drm_fd, conn->crtc_id, conn->encoder_id, 0, 0, &conn->connector_id, 1, mode) < 0) {
        perror("Failed to set CRTC mode");
        drmModeFreeConnector(conn);
        close(drm_fd);
        return 1;
    }

    // Step 2: Initialize GBM
    struct gbm_device* gbm = gbm_create_device(drm_fd);
    if (!gbm) { perror("Failed to create GBM device"); return 1; }

    struct gbm_surface* gbm_surf = gbm_surface_create(
        gbm, mode->hdisplay, mode->vdisplay,
        GBM_FORMAT_XRGB8888,
        GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT
    );
    if (!gbm_surf) { perror("Failed to create GBM surface"); return 1; }

    // Step 3: Initialize EGL
    EGLDisplay egl_dpy = eglGetDisplay((EGLNativeDisplayType)gbm);
    if (!egl_dpy) { fprintf(stderr, "Failed to get EGL display\n"); return 1; }

    EGLint major, minor;
    if (!eglInitialize(egl_dpy, &major, &minor)) { fprintf(stderr, "EGL initialization failed\n"); return 1; }

    // Choose EGL config
    EGLConfig config;
    EGLint num_configs;
    EGLint config_attribs[] = {
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_NONE
    };
    if (!eglChooseConfig(egl_dpy, config_attribs, &config, 1, &num_configs)) {
        fprintf(stderr, "Failed to choose EGL config\n");
        return 1;
    }

    // Create EGL surface and context
    EGLSurface egl_surf = eglCreateWindowSurface(egl_dpy, config, (EGLNativeWindowType)gbm_surf, NULL);
    EGLContext egl_ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, NULL);
    if (!egl_surf || !egl_ctx) { fprintf(stderr, "Failed to create EGL surface/context\n"); return 1; }

    eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx);

    // Step 4: OpenGL ES Rendering Loop
    while (1) {
        // Clear screen to blue
        glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Swap buffers (triggers DRM page flip via EGL_GBM extension)
        eglSwapBuffers(egl_dpy, egl_surf);

        // Wait for VSYNC (use DRM events for precise sync in production)
        usleep(16667); // ~60fps interval
    }

    // Cleanup (omitted for brevity in example)
    eglDestroyContext(egl_dpy, egl_ctx);
    eglDestroySurface(egl_dpy, egl_surf);
    eglTerminate(egl_dpy);
    gbm_surface_destroy(gbm_surf);
    gbm_device_destroy(gbm);
    drmModeFreeConnector(conn);
    close(drm_fd);
    return 0;
}

Compilation Note:

Link against required libraries with:

gcc -o gles_drm_render gles_drm_render.c -ldrm -lgbm -lEGL -lGLES2

内容的提问来源于stack exchange,提问作者Andrii Omelchenko

火山引擎 最新活动