drm-memory

范志勇
2023-12-01
  • 名称

       drm-memory, drm-mm, drm-gem, drm-ttm - DRM Memory Management

  • 头文件

       #include <xf86drm.h>

  • 描述

很多现代高端GPUs 都带有自己的内存管理器。它们甚至包含有多个不同cache,这些cache在访问时需要同步。纹理,帧buffer和命令buffer,还有其它数据,它们需要被GPU快速的访问。因此,GPU上的内存管理是高度driver依赖和硬件依赖的。

然而,内核中有多个机制被多个驱动程序使用。他们是通用的,不依赖于驱动代码。然而,为了进行硬件加速,需要阅读驱动手册来优化它。

  •  Dumb-Buffers

几乎所有内核内的DRM硬件驱动都会支持一个称为“Dumb-Buffers”的API。这个API允许创建随机大小的buffer,来用于数据输出。这些buffers可以通过mmap(2)进行内存映射,因此,可以在CPU上访问。然而,GPU经常不能正常访问这些buffers。因此,它们对简单任务比较好,对不适合复杂任务。

DRM_IOCTL_MODE_CREATE_DUMB ioctl 可以被用于创建一个“dump buffer”。这个kernel会返回一个32bit的handle,这个handle用于使用DRM API来管理buffer。你可以使用“drmModeAddFB(3) ”来创建“frame buffers”,并可以使用它来进行模式设置和输出。为了访问buffers,你应该首先需要获取buffer的偏移。

DRM_IOCTL_MODE_MAP_DUMB ioctl 请求drm 子系统准备好mmap的buffer,并且返回一个“fake-offset ”,“fake-offset ”在mmap(2)时使用。

DRM_IOCTL_MODE_CREATE_DUMB ioctl 使用如下类型“drm_mode_create_dumb” struct 作为参数;

           struct drm_mode_create_dumb {
                __u32 height;
                __u32 width;
                __u32 bpp;
                __u32 flags;

                __u32 handle;
                __u32 pitch;
                __u64 size;
           };

其中,“height, width, bpp, flags”必须由调用者提供。其它的域由kernel填充并返回“return value”。height和width是要创建的buffer的矩形维度。bpp是“bits-per-pixel ”,必须是8的倍数。大多数情况下,bpp设置为32。flags当前没有使用,必须设置为“0”。不同flags会用于将来改变行为。在调用ioctl后,“handle,pitch 和size”会由kernel填充并返回。handle是个“32 bit gem handle”,它标识buffer。在其它的调用中,会使用一个“gem-handle”或“mem-buffer”作为参数。“picth”域是一个新的buffer的pitch(或stride)。大多数驱动使用了32bit或64bit对齐的步长(“stride-values”)。size 域包含了该buffer的绝对大小(以字节为单位)。它通常可以通过 (height * pitch + width) * bpp / 4 计算得出。

为了准备用于 mmap(2)的buffer,需要调用DRM_IOCTL_MODE_MAP_DUMB ioctl。它使用drm_mode_map_dumb结构体作为参数:

           struct drm_mode_map_dumb {
                __u32 handle;
                __u32 pad;

                __u64 offset;
           };

需要把通过DRM_IOCTL_MODE_CREATE_DUMB获取的“gem-handle” 填充到该结构体的handle域。pad域没有使用,必须设置为0。调用结束后,offset域包含有一个偏移,并且同 mmap(2)一起使用在“DRM file-descriptor”上。

如果不再需要“dumb-buffer”,必须使用DRM_IOCTL_MODE_DESTROY_DUMB ioctl来释放它。如果close DRM file-descriptor ,所有打开的“dumb-buffer”会自动释放掉。

 DRM_IOCTL_MODE_DESTROY_DUMB ioctl使用如下的 drm_mode_destroy_dumb结构体作为参数:

           struct drm_mode_destroy_dumb {
                __u32 handle;
           };

只需要把“gem-handle” 填充到该结构体的handle域。调用结束后,handle非法了,可以被 dumb-API重用到一个新的buffer上。

 

  • TTM

 TTM stands for Translation Table Manager and is a generic memory-manager provided by the kernel. It does not provide a common user-space API so you need to look at each driver interface if
       you want to use it. See for instance the radeon manpages for more information on memory-management with radeon and TTM.

  •    GEM

       GEM stands for Graphics Execution Manager and is a generic DRM memory-management framework in the kernel, that is used by many different drivers. Gem is designed to manage graphics memory,
       control access to the graphics device execution context and handle essentially NUMA environment unique to modern graphics hardware. Gem allows multiple applications to share graphics device
       resources without the need to constantly reload the entire graphics card. Data may be shared between multiple applications with gem ensuring that the correct memory synchronization occurs.

       Gem provides simple mechanisms to manage graphics data and control execution flow within the linux DRM subsystem. However, gem is not a complete framework that is fully driver independent.
       Instead, if provides many functions that are shared between many drivers, but each driver has to implement most of memory-management with driver-dependent ioctls. This manpage tries to
       describe the semantics (and if it applies, the syntax) that is shared between all drivers that use gem.

       All GEM APIs are defined as ioctl(2) on the DRM file descriptor. An application must be authorized via drmAuthMagic(3) to the current DRM-Master to access the GEM subsystem. A driver that
       does not support gem will return ENODEV for all these ioctls. Invalid object handles return EINVAL and invalid object names return ENOENT.

       Gem provides explicit memory management primitives. System pages are allocated when the object is created, either as the fundamental storage for hardware where system memory is used by the
       graphics processor directly, or as backing store for graphics-processor resident memory.

       Objects are referenced from user-space using handles. These are, for all intents and purposes, equivalent to file descriptors but avoid the overhead. Newer kernel drivers also support the
       drm-prime(7) infrastructure which can return real file-descriptor for gem-handles using the linux dma-buf API. Objects may be published with a name so that other applications and processes
       can access them. The name remains valid as long as the object exists. Gem-objects are reference counted in the kernel. The object is only destroyed when all handles from user-space were
       closed.

       Gem-buffers cannot be created with a generic API. Each driver provides its own API to create gem-buffers. See for example DRM_I915_GEM_CREATE, DRM_NOUVEAU_GEM_NEW or DRM_RADEON_GEM_CREATE.
       Each of these ioctls returns a gem-handle that can be passed to different generic ioctls. The libgbm library from the mesa3D distribution tries to provide a driver-independent API to create
       gbm buffers and retrieve a gbm-handle to them. It allows to create buffers for different use-cases including scanout, rendering, cursors and CPU-access. See the libgbm library for more
       information or look at the driver-dependent man-pages (for example drm-intel(7) or drm-radeon(7)).

       Gem-buffers can be closed with the DRM_IOCTL_GEM_CLOSE ioctl. It takes as argument a structure of type struct drm_gem_close:

           struct drm_gem_close {
                __u32 handle;
                __u32 pad;
           };

       The handle field is the gem-handle to be closed. The pad field is unused padding. It must be zeroed. After this call the gem handle cannot be used by this process anymore and may be reused
       for new gem objects by the gem API.

       If you want to share gem-objects between different processes, you can create a name for them and pass this name to other processes which can then open this gem-object. Names are currently
       32bit integer IDs and have no special protection. That is, if you put a name on your gem-object, every other client that has access to the DRM device and is authenticated via drmAuthMagic(3)
       to the current DRM-Master, can guess the name and open or access the gem-object. If you want more fine-grained access control, you can use the new drm-prime(7) API to retrieve
       file-descriptors for gem-handles. To create a name for a gem-handle, you use the DRM_IOCTL_GEM_FLINK ioctl. It takes as argument a structure of type struct drm_gem_flink:

           struct drm_gem_flink {
                __u32 handle;
                __u32 name;
           };

       You have to put your handle into the handle field. After completion, the kernel has put the new unique name into the name field. You can now pass this name to other processes which can then
       import the name with the DRM_IOCTL_GEM_OPEN ioctl. It takes as argument a structure of type struct drm_gem_open:

           struct drm_gem_open {
                __u32 name;

                __u32 handle;
                __u32 size;
           };

       You have to fill in the name field with the name of the gem-object that you want to open. The kernel will fill in the handle and size fields with the new handle and size of the gem-object.
       You can now access the gem-object via the handle as if you created it with the gem API.

       Besides generic buffer management, the GEM API does not provide any generic access. Each driver implements its own functionality on top of this API. This includes execution-buffers, GTT
       management, context creation, CPU access, GPU I/O and more. The next higher-level API is OpenGL. So if you want to use more GPU features, you should use the mesa3D library to create OpenGL
       contexts on DRM devices. This does not require any windowing-system like X11, but can also be done on raw DRM devices. However, this is beyond the scope of this man-page. You may have a look
       at other mesa3D manpages, including libgbm and libEGL. 2D software-rendering (rendering with the CPU) can be achieved with the dumb-buffer-API in a driver-independent fashion, however, for
       hardware-accelerated 2D or 3D rendering you must use OpenGL. Any other API that tries to abstract the driver-internals to access GEM-execution-buffers and other GPU internals, would simply
       reinvent OpenGL so it is not provided. But if you need more detailed information for a specific driver, you may have a look into the driver-manpages, including drm-intel(7), drm-radeon(7) and
       drm-nouveau(7). However, the drm-prime(7) infrastructure and the generic gem API as described here allow display-managers to handle graphics-buffers and render-clients without any deeper
       knowledge of the GPU that is used. Moreover, it allows to move objects between GPUs and implement complex display-servers that don't do any rendering on their own. See its man-page for more
       information.

  • EXAMPLES

       This section includes examples for basic memory-management tasks.

  1.    Dumb-Buffers

       This examples shows how to create a dumb-buffer via the generic DRM API. This is driver-independent (as long as the driver supports dumb-buffers) and provides memory-mapped buffers that can
       be used for scanout. This example creates a full-HD 1920x1080 buffer with 32 bits-per-pixel and a color-depth of 24 bits. The buffer is then bound to a framebuffer which can be used for
       scanout with the KMS API (see drm-kms(7)).

           struct drm_mode_create_dumb creq;
           struct drm_mode_destroy_dumb dreq;
           struct drm_mode_map_dumb mreq;
           uint32_t fb;
           int ret;
           void *map;

           /* create dumb buffer */
           memset(&creq, 0, sizeof(creq));
           creq.width = 1920;
           creq.height = 1080;
           creq.bpp = 32;
           ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
           if (ret < 0) {
                /* buffer creation failed; see "errno" for more error codes */
                ...
           }
           /* creq.pitch, creq.handle and creq.size are filled by this ioctl with
            * the requested values and can be used now. */

           /* create framebuffer object for the dumb-buffer */
           ret = drmModeAddFB(fd, 1920, 1080, 24, 32, creq.pitch, creq.handle, &fb);
           if (ret) {
                /* frame buffer creation failed; see "errno" */
                ...
           }
           /* the framebuffer "fb" can now used for scanout with KMS */

           /* prepare buffer for memory mapping */
           memset(&mreq, 0, sizeof(mreq));
           mreq.handle = creq.handle;
           ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
           if (ret) {
                /* DRM buffer preparation failed; see "errno" */
                ...
           }
           /* mreq.offset now contains the new offset that can be used with mmap() */

           /* perform actual memory mapping */
           map = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mreq.offset);
           if (map == MAP_FAILED) {
                /* memory-mapping failed; see "errno" */
                ...
           }

           /* clear the framebuffer to 0 */
           memset(map, 0, creq.size);

  • REPORTING BUGS

       Bugs in this manual should be reported to https://bugs.freedesktop.org/enter_bug.cgi?product=DRI&component=libdrm under the "DRI" product, component "libdrm"

  • SEE ALSO

       drm(7), drm-kms(7), drm-prime(7), drmAvailable(3), drmOpen(3), drm-intel(7), drm-radeon(7), drm-nouveau(7)

libdrm                                                                                      September 2012                                                                               DRM-MEMORY(7)

 类似资料: