User-space applications first load the Gralloc module and acquire a Gralloc device and a Fb device before using the frame buffer. With a Gralloc device, an application in user space can request the allocation of a graphics buffer and map the graphics buffer to the application’s address space so that it can write the contents of the screen to be drawn. Finally, the application in user space uses the FB device to render the previously prepared graphics buffer into the frame buffer, drawing the contents of the graphics buffer onto the display. Accordingly, when an application in user space no longer needs to use a graphics buffer, the gralloc device can release it and unmap it from the address space.

Gralloc devices are used to allocate and release graphics buffers

The Gralloc module is responsible for registering the graphics buffer,

Fb devices are primarily used to render graphics buffers

1; The frame buffer

Structure private_module_t defined in the file the hardware/libhardware/modules/gralloc/gralloc_priv h, it is mainly used to describe the frame buffer properties, as shown below:

struct private_module_t {  
    gralloc_module_t base;  
  
    private_handle_t* framebuffer;  
    uint32_t flags;  
    uint32_t numBuffers;  
    uint32_t bufferMask;  
    pthread_mutex_t lock;  
    buffer_handle_t currentBuffer;  
    int pmem_master;  
    void* pmem_master_base;  
  
    struct fb_var_screeninfo info;  
    struct fb_fix_screeninfo finfo;  
    float xdpi;  
    float ydpi;  
    float fps;  
};  
Copy the code
  • The framebuffer member variable is of type private_handle_t, which is a handle to the system framebuffer. We will examine the structure private_handle_t later.
  • The flags member variable is used to indicate whether the system frame buffer supports double buffering. The PAGE_FLIP bit is equal to 1 if supported, and 0 if not.
  • The numBuffers member variable indicates how many graph buffers the system frame buffer contains. How many graphics buffers a frame buffer contains depends on its visual resolution and the size of its virtual resolution. For example, if a frame buffer has a visual resolution of 800 x 600 and a virtual resolution of 1600 x 600, the frame buffer can contain two graphics buffers.
  • The member variable bufferMask is used to record the use of the graphics buffer in the system frame buffer. For example, assume that the system has two graphics frame buffer buffer, then the member variable bufferMask have four values, respectively is binary 00, 01, 10 and 11, among them, 00 respectively two figure buffer is free, 01 said first graphics buffer has been assigned to go out, and the second graphics buffer is free, 10 means that the first graph buffer is free, the second graph buffer has been allocated, and 11 means that both graph buffers have been allocated.
  • The member variable LOCK is a mutex that protects parallel access to the private_module_t structure.
  • The currentBuffer member variable is of type BUFFer_HANDLE_T and is used to describe the graph buffer currently being rendered, which is defined later.
  • The member variables pmem_master and pmem_master_base are not currently in use.
  • The member variables info and finfo are of fb_VAR_screeninfo and fb_fix_screeninfo types respectively. They are used to store the property information of the device display screen. The property information of the member variable info can be dynamically set. The attribute information held by the member variable finfo is read-only. The values of these two member variables can be obtained from the frame buffer driver module via the IO control commands FBIOGET_VSCREENINFO and FBIOGET_FSCREENINFO.
  • The member variables xdPI and ydpi are used to describe the density of the device’s display in width and height, that is, the number of pixels per inch.
  • The FPS member variable is used to describe the refresh rate of the display in units of FPS, or frames per second.

2: graphic buffer

The private_handLE_t structure is used to describe a graphic buffer that may be allocated in the frame buffer or in memory, as the case may be. It defines the hardware in the file/libhardware/modules/gralloc gralloc_priv. H file, as shown below:

#ifdef __cplusplus struct private_handle_t : public native_handle { #else struct private_handle_t { struct native_handle nativeHandle; #endif enum { PRIV_FLAGS_FRAMEBUFFER = 0x00000001 }; // file-descriptors int fd; // ints int magic; int flags; int size; int offset; // FIXME: the attributes below should be out-of-line int base; int pid; #ifdef __cplusplus static const int sNumInts = 6; static const int sNumFds = 1; static const int sMagic = 0x3141592; private_handle_t(int fd, int size, int flags) : fd(fd), magic(sMagic), flags(flags), size(size), offset(0), base(0), pid(getpid()) { version = sizeof(native_handle); numInts = sNumInts; numFds = sNumFds; } ~private_handle_t() { magic = 0; } static int validate(const native_handle* h) { const private_handle_t* hnd = (const private_handle_t*)h; if (! h || h->version ! = sizeof(native_handle) || h->numInts ! = sNumInts || h->numFds ! = sNumFds || hnd->magic ! = sMagic) { LOGE("invalid gralloc handle (at %p)", h); return -EINVAL; } return 0; } #endif };Copy the code

For the sake of description, let’s assume that we compile the file gralloc_priv.h in a C++ environment that defines the macro __cplusplus. Thus, the private_handle_t structure is inherited from the native_handle_t structure, which contains one file descriptor, six integers, and three static member variables.

  • The member variable fd points to a file descriptor that points either to a frame buffer device or to a block of anonymous shared memory, depending on whether a graphic buffer described by its host structure private_handle_T is allocated in the frame buffer or in memory.
  • The member variable magic refers to a magic number whose value is specified by the static member variable sMagic to identify a private_HANDLE_T structure.
  • The member variable FLAGS is used to describe a graphic buffer with a value equal to either 0 or PRIV_FLAGS_FRAMEBUFFER. A graphics buffer is allocated in the frame buffer when its flag value is equal to PRIV_FLAGS_FRAMEBUFFER.
  • The size member variable is used to describe the size of a graphics buffer.
  • The member variable offset is used to describe the offset address of a graph buffer. For example, when a graphics buffer is partitioned in a block of memory, assuming the address of the block is start, the start address of the graphics buffer is start + offset.
  • The member variable base is used to describe the actual address of a graphics buffer, which is computed by the member variable offset. For example, the value of start + offset computed above is stored in the member variable base.
  • The member variable PID is used to describe the PID of the creator of a graph buffer. For example, if a graphics buffer is created in a process with an ID of 1000, the member pid of the private_HANDLE_T structure that describes the graphics buffer has a value of 1000.

The private_handLE_t static member variables sMagic have been described previously. The other two static member variables sNumInts and sNumFds have values of 1 and 6 respectively, indicating that private_handLE_T contains one file descriptor and six integers. They are used to initialize the member variables numInts and numFds of native_handLE_t, the parent of private_handle_t, as shown in the constructor of private_handle_t. And as you can see here, Private_handle_t’s member variables FDS, MAGIC, FLAGS, size, offset, base, and PID point to the buffer referred to by the native_handle_t member variables data Contiguous memory blocks, a total of 7 integers.

The private_handLE_t structure also defines a static member function, validate, that verifies that an native_handLE_T pointer points to a private_handLE_t structure.

So far, the loading process of Gralloc module and related data structures are introduced here. Next, we analyze the opening process of Gralloc and FB devices defined in Gralloc module respectively.

3. Open the gralloc device

In the Gralloc module, the ID value of the Gralloc device is defined as GRALLOC_HARDWARE_GPU0. GRALLOC_HARDWARE_GPU0 is a macro, defined in the file the hardware/libhardware/include/hardware/gralloc. J h, as shown below:

#define GRALLOC_HARDWARE_GPU0 "gpu0"  
Copy the code

Gralloc devices are described using the structure alloc_device_t. The alloc_device_t structure has two member functions, alloc and free, to allocate and free the graphics buffer, respectively.

Structure alloc_device_t is defined in the file the hardware/libhardware/include/hardware/gralloc h, as shown below:

typedef struct alloc_device_t {  
    struct hw_device_t common;  
  
    int (*alloc)(struct alloc_device_t* dev,  
            int w, int h, int format, int usage,  
            buffer_handle_t* handle, int* stride);  
  
    int (*free)(struct alloc_device_t* dev,  
            buffer_handle_t handle);  
  
} alloc_device_t;  
Copy the code

Gralloc module in the hardware in the file/libhardware/include/hardware/Gralloc gralloc_open h defines a help function, used to open Gralloc devices, as shown below:

static inline int gralloc_open(const struct hw_module_t* module,  
        struct alloc_device_t** device) {  
    return module->methods->open(module,  
            GRALLOC_HARDWARE_GPU0, (struct hw_device_t**)device);  
}  
Copy the code

The module argument points to an hw_module_T structure that describes the Gralloc module, A member function open of the hw_module_methods_t structure pointed to by its member methods refers to the function gralloc_device_open in the Gralloc module.

Function gralloc_device_open defined in the file the hardware/libhardware/modules/gralloc/gralloc CPP file, as shown below:

struct gralloc_context_t { alloc_device_t device; /* our private data here */ }; . int gralloc_device_open(const hw_module_t* module, const char* name, hw_device_t** device) { int status = -EINVAL; if (! strcmp(name, GRALLOC_HARDWARE_GPU0)) { gralloc_context_t *dev; dev = (gralloc_context_t*)malloc(sizeof(*dev)); /* initialize our state here */ memset(dev, 0, sizeof(*dev)); /* initialize the procs */ dev->device.common.tag = HARDWARE_DEVICE_TAG; dev->device.common.version = 0; dev->device.common.module = const_cast<hw_module_t*>(module); dev->device.common.close = gralloc_close; dev->device.alloc = gralloc_alloc; dev->device.free = gralloc_free; *device = &dev->device.common; status = 0; }... return status;Copy the code

This function creates a gralloc_context_t structure and initializes its member, device. The member of the gralloc_context_t structure, device, is of type Gralloc_device_t and describes a Gralloc device. As mentioned earlier, the gralloc device is used to allocate and free graphics buffers by calling its member functions alloc and free. It can be seen that the member functions alloc and free of the gralloc device opened by the function gralloc_device_open are respectively set to the functions gralloc_ALLOc and gralloc_free in the Gralloc module. We will analyze their implementation in detail later.

You can see from the gralloc device’s two functions, alloc and free, that the Gralloc device allocates and frees graphics buffers

4. Opening process of FB equipment

In the Gralloc module, the ID value of fb device is defined as GRALLOC_HARDWARE_FB0. GRALLOC_HARDWARE_FB0 is a macro, defined in the file the hardware/libhardware/include/hardware/gralloc. J h, as shown below:

#define GRALLOC_HARDWARE_FB0 "fb0"  
Copy the code

Fb devices are described using the structure frameBuffer_device_t. Structure framebuffer_device_t frame buffer is used to describe the system of information, it defines the hardware in the file/libhardware/include/hardware/gralloc h, as shown below:

typedef struct framebuffer_device_t {  
    struct hw_device_t common;  
  
    /* flags describing some attributes of the framebuffer */  
    const uint32_t  flags;  
  
    /* dimensions of the framebuffer in pixels */  
    const uint32_t  width;  
    const uint32_t  height;  
  
    /* frambuffer stride in pixels */  
    const int       stride;  
  
    /* framebuffer pixel format */  
    const int       format;  
  
    /* resolution of the framebuffer's display panel in pixel per inch*/  
    const float     xdpi;  
    const float     ydpi;  
  
    /* framebuffer's display panel refresh rate in frames per second */  
    const float     fps;  
  
    /* min swap interval supported by this framebuffer */  
    const int       minSwapInterval;  
  
    /* max swap interval supported by this framebuffer */  
    const int       maxSwapInterval;  
  
    int reserved[8];  
  
    int (*setSwapInterval)(struct framebuffer_device_t* window,  
            int interval);  
  
    int (*setUpdateRect)(struct framebuffer_device_t* window,  
            int left, int top, int width, int height);  
  
    int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);  
  
    int (*compositionComplete)(struct framebuffer_device_t* dev);  
  
    void* reserved_proc[8];  
  
} framebuffer_device_t;  
Copy the code
  • The flags member variable is used to record the flags of the system frame buffer. This member variable is not currently used and its value is set to 0.
  • The member variables width and height describe the width and height of the device display, respectively, in pixels.
  • The stride member variable is used to describe how many pixels there are in a line of the device display screen.
  • The format member variable is used to describe the pixel format of the system frame buffer. The supported pixel formats are HAL_PIXEL_FORMAT_RGBX_8888 and HAL_PIXEL_FORMAT_RGB_565. HAL_PIXEL_FORMAT_RGBX_8888 indicates that a pixel is described using 32 bits, with R, G, and B occupying 8 bits each and the remaining 8 bits unused. HAL_PIXEL_FORMAT_RGB_565 indicates that a pixel is described using 16 bits, with R, G and B accounting for 5, 6 and 5 bits respectively.
  • The member variables xdPI and ydpi are used to describe the density of the device’s display in width and height, that is, the number of pixels per inch.
  • The FPS member variable describes the refresh rate of the device’s display in frames per second.
  • The member variables minSwapInterval and maxSwapInterval are used to describe the minimum and maximum time interval between two graphics buffers before and after the frame buffer exchange.
  • Member variables reserved are reserved for future use.
  • The member function setSwapInterval is used to set the minimum and maximum interval between two graphics buffers before and after the frame buffer exchange.
  • The member function setUpdateRect is used to set the update region of the frame buffer.
  • The member function POST is used to render the contents of the graphics buffer to the frame buffer, that is, to display on the device display.
  • The member function compositionComplete notifies the FB device that the graphics buffer composition is complete and is not currently in use.
  • The reserved member variable is an array of function Pointers that are reserved for future use.

Post is the most important of the series of member functions on the framebuffer_device_t structure. It is called by user-space applications to render the specified screen/on the device display

5. Allocate the graphics buffer

Mentioned above, the user space applications use graphics buffer is allocated by gralloc_alloc Gralloc module of function, this function is to realize the hardware in the file/libhardware/modules/Gralloc/Gralloc CPP, as shown below:

static int gralloc_alloc(alloc_device_t* dev, int w, int h, int format, int usage, buffer_handle_t* pHandle, int* pStride) { if (! pHandle || ! pStride) return -EINVAL; size_t size, stride; int align = 4; int bpp = 0; switch (format) { case HAL_PIXEL_FORMAT_RGBA_8888: case HAL_PIXEL_FORMAT_RGBX_8888: case HAL_PIXEL_FORMAT_BGRA_8888: bpp = 4; break; case HAL_PIXEL_FORMAT_RGB_888: bpp = 3; break; case HAL_PIXEL_FORMAT_RGB_565: case HAL_PIXEL_FORMAT_RGBA_5551: case HAL_PIXEL_FORMAT_RGBA_4444: bpp = 2; break; default: return -EINVAL; } size_t bpr = (w*bpp + (align-1)) & ~(align-1); size = bpr * h; stride = bpr / bpp; int err; if (usage & GRALLOC_USAGE_HW_FB) { err = gralloc_alloc_framebuffer(dev, size, usage, pHandle); } else { err = gralloc_alloc_buffer(dev, size, usage, pHandle); } if (err < 0) { return err; } *pStride = stride; return 0; }Copy the code

The gralloc_alloc_framebuffer function is used to allocate the graphics buffer within the system framebuffer, and the gralloc_alloc_buffer function is used to allocate the graphics buffer within the system framebuffer,

5.1 Function gralloc_alloc_framebuffer is implemented in files

The hardware/libhardware/modules/gralloc/gralloc CPP, as shown below:

static int gralloc_alloc_framebuffer(alloc_device_t* dev,  
        size_t size, int usage, buffer_handle_t* pHandle)  
{  
    private_module_t* m = reinterpret_cast<private_module_t*>(  
            dev->common.module);  
    pthread_mutex_lock(&m->lock);  
    int err = gralloc_alloc_framebuffer_locked(dev, size, usage, pHandle);  
    pthread_mutex_unlock(&m->lock);  
    return err;  
}  
Copy the code

This function calls another function, gralloc_alloc_frameBUFFer_locked, to allocate the graph buffer.

Gralloc_alloc_framebuffer_locked () function is implemented in the file the hardware/libhardware/modules/gralloc/gralloc CPP, as shown below:

static int gralloc_alloc_framebuffer_locked(alloc_device_t* dev, size_t size, int usage, buffer_handle_t* pHandle) { private_module_t* m = reinterpret_cast<private_module_t*>( dev->common.module); // allocate the framebuffer if (m->framebuffer == NULL) { // initialize the framebuffer, the framebuffer is mapped once // and forever. int err = mapFrameBufferLocked(m); if (err < 0) { return err; } } const uint32_t bufferMask = m->bufferMask; const uint32_t numBuffers = m->numBuffers; const size_t bufferSize = m->finfo.line_length * m->info.yres; if (numBuffers == 1) { // If we have only one buffer, we never use page-flipping. Instead, // we return a regular buffer which will be memcpy'ed to the main // screen when post is called. int newUsage = (usage &  ~GRALLOC_USAGE_HW_FB) | GRALLOC_USAGE_HW_2D; return gralloc_alloc_buffer(dev, bufferSize, newUsage, pHandle); } if (bufferMask >= ((1LU<<numBuffers)-1)) { // We ran out of buffers. return -ENOMEM; } // create a "fake" handles for it intptr_t vaddr = intptr_t(m->framebuffer->base); private_handle_t* hnd = new private_handle_t(dup(m->framebuffer->fd), size, private_handle_t::PRIV_FLAGS_FRAMEBUFFER); // find a free slot for (uint32_t i=0 ; i<numBuffers ; i++) { if ((bufferMask & (1LU<<i)) == 0) { m->bufferMask |= (1LU<<i); break; } vaddr += bufferSize; } hnd->base = vaddr; hnd->offset = vaddr - intptr_t(m->framebuffer->base); *pHandle = hnd; return 0; }Copy the code

Before the system framebuffer allocates the graphics buffer, the system framebuffer must first be initialized so that the value of the framebuffer, a member of the private_module_t structure, referred to by the variable m, cannot be NULL. If NULL, another function, mapFrameBufferLocked, must be called to initialize the system frame buffer. The procedure for initializing the system frame buffer is described in Part 3 above.

BufferMask is used to describe the use of the frame buffer, numBuffers is used to describe how many graphics buffers the frame buffer can be divided into, and bufferSize is used to describe the amount of memory occupied by the contents of a screen.

If the system frame buffer has only one graph buffer size, that is, numBuffers is equal to 1, then the graph buffer is always used as the system main graph buffer. In this case, we cannot allocate the graphics buffer in the system frame buffer for use by user-space applications, so we turn to memory to allocate the graphics buffer by calling the function gralloc_alloc_buffer. Note that the size of the allocated graphics buffer is the size of a screen of content, i.e. BufferSize.

If bufferMask is greater than or equal to the value of ((1LU<<numBuffers)-1), then all the graphics buffers in the system frame buffer are allocated, and allocating the graphics buffer fails. For example, if the number of graph buffers is 2, the value of ((1LU<<numBuffers)-1) is equal to 3, or binary 0x11. If bufferMask is equal to 0x11, then both the first and second graphics buffers have been allocated. Therefore, the graphics buffer can no longer be allocated in the system frame buffer.

Assuming there is any free graphics buffer in the system frame buffer, the function creates a private_handle_t structure HND to describe the graphics buffer to be allocated. Note that the graphics buffer flag value is equal to PRIV_FLAGS_FRAMEBUFFER, which indicates that this is a graphics buffer allocated in the system frame buffer.

The frame buffer is allocated as follows

The next for loop checks the value of the variable bufferMask from low to high, and finds the first bit with a value equal to 0, so it knows which graphics buffer is free in the system frame buffer. Note that the value of the variable vadRR starts at the base address of the system frame buffer, and in the following for loop its value increases bufferSize with each loop. From this you can see that the size of the graphics buffer allocated from the system frame buffer is exactly equal to the size of one screen of content.

The starting address of the last allocated graphic buffer is stored in the member variable BASE of the private_HANDLE_T structure HND created earlier. In this way, user-space applications can directly copy the rendered graphic content to this address, which is equivalent to rendering the graphic directly into the system frame buffer.

Before returning the private_HANDLE_T structure HND to the caller, you also need to set its member variable offset so that you know the offset of the starting address of the graphics buffer it describes relative to the base address of the system frame buffer.

The 5.2 function gralloc_alloc_buffer is also implemented in files

The hardware/libhardware/modules/gralloc/gralloc CPP, as shown below:

static int gralloc_alloc_buffer(alloc_device_t* dev,  
        size_t size, int usage, buffer_handle_t* pHandle)  
{  
    int err = 0;  
    int fd = -1;  
  
    size = roundUpToPageSize(size);  
  
    fd = ashmem_create_region("gralloc-buffer", size);  
    if (fd < 0) {  
        LOGE("couldn't create ashmem (%s)", strerror(-errno));  
        err = -errno;  
    }  
  
    if (err == 0) {  
        private_handle_t* hnd = new private_handle_t(fd, size, 0);  
        gralloc_module_t* module = reinterpret_cast<gralloc_module_t*>(  
                dev->common.module);  
        err = mapBuffer(module, hnd);  
        if (err == 0) {  
            *pHandle = hnd;  
        }  
    }  
  
    LOGE_IF(err, "gralloc failed err=%s", strerror(-err));  
  
    return err;  
}  
Copy the code

The implementation of this function is simple. It first calls ashmem_create_region to create a block of anonymous shared memory, and then allocates a graphics buffer on the block. Note that this graphic buffer is also described using a private_HANDLE_T structure, but the graphic buffer flag value is zero to distinguish it from the graphic buffer allocated in the system frame buffer. For more information about Anonymous Shared Memory, please refer to the introduction and learning plan of Ashmem (Anonymous Shared Memory) of the Android system. And Android system Anonymous Shared Memory C++ call interface analysis this article.

The graphics buffer allocated from anonymous shared memory also needs to be mapped to the process’s address space before it can be used, by calling the function mapBuffer.

Function mapBuffer realize the hardware in the file/libhardware/modules/gralloc/mapper. The CPP, as shown below:

int mapBuffer(gralloc_module_t const* module,  
        private_handle_t* hnd)  
{  
    void* vaddr;  
    return gralloc_map(module, hnd, &vaddr);  
}  
Copy the code

It maps a graph buffer described by HND to the address space of the current process by calling another function, gralloc_map. Later, we will analyze the implementation of the function gralloc_map when we analyze the registration process of the graph buffer.

Note that on Android, the graphics buffer allocated in the system frame buffer is used by the SurfaceFlinger service, while the graphics buffer allocated in memory can be used by the SurfaceFlinger service as well as other applications. When other applications need to use the graphics buffer, they request the SurfaceFlinger service to allocate it to them. Therefore, all other applications need to do is map the graphics buffer returned by the SurfaceFlinger service to their own process address space. This is the registration process for the graphics buffer that we will examine later.

At this point, the graphics buffer allocation process analysis is complete, next we continue to analyze the graphics buffer release process.