Allocating and releasing the graphic buffer

So far in this chapter, we have discussed loading the Gralloc module and the open method provided by the Gralloc module. Let's now review the points when the upper layer loads, initializes, and uses the Gralloc module:

  • For example, the Gralloc module is used mostly by SurfaceFlinger. SurfaceFlinger uses Gralloc; when it creates an instance of FramebufferNativeWindow, in the FramebufferNativeWindow constructor, it will call hw_get_module to get an instance of hw_module_t.
  • In the hw_module_t data structure, it has a field called methods with data type hw_module_methods_t. In hw_module_methods_t, it has an open method that returns a hw_device_t data structure.
  • With hw_device_t, SurfaceFlinger can use the alloc and free methods inside hw_device_t to allocate or release graphic buffers.

Let's look at how the Gralloc module allocates and releases graphic buffers in this section. We will look at the source code of gralloc_alloc first:

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_RAW16:
bpp = 2;
break;
default:
return -EINVAL;
}

private_module_t* m = reinterpret_cast<private_module_t*>(
dev->common.module);

size_t bpr = usage & GRALLOC_USAGE_HW_FB ? m->finfo.line_length :
(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;
}

As we can see in the preceding code snippet, the alloc method is implemented in the gralloc_alloc function. gralloc_alloc has the following parameters:

  • dev: It has an alloc_device data type that inherits from hw_device_t.
  • w : It is the width of the graphic buffer.
  • h: It is the height of graphic buffer.
  • format : It defines the color format of pixels. For example, the format can be HAL_PIXEL_FORMAT_RGBA_8888, HAL_PIXEL_FORMAT_RGB_888, HAL_PIXEL_FORMAT_RGB_565, and so on.
  • usage : It defines the use of graphic buffer. For example, if the GRALLOC_USAGE_HW_FB bit is set, the buffer will be allocated from the framebuffer.
  • pHandle : It has a buffer_handle_t data type. We will discuss the details of this data structure. It is used to store the allocated buffer.
  • pStride : The number of pixels per line.

In gralloc_alloc, it checks the format of pixels to decide the size of pixels. It can be 32 bits, 24 bits, 16 bits, and so on. The size of the pixel is stored in the bpp variable. The bpr variable is the number of bytes per line and it is calculated using w multiplied by bpp. The bpr variable needs to be aligned to four bytes boundary for memory allocation. The size of the buffer can be calculated using h multiplied by bpr.

After the size of the buffer is calculated, it will call the gralloc_alloc_framebuffer or gralloc_alloc_buffer functions according to the GRALLOC_USAGE_HW_FB bit.

The graphic buffer that is allocated by gralloc_alloc is stored in the buffer_handle_t data type. buffer_handle_t is defined as a pointer of native_handle. native_handle is used as a parent class of private_handle_t. private_handle_t is the actual data type used to manage the graphic buffer and it is a hardware-dependent data structure.

Relationship between private_handle_t and native_handle

The preceding diagram shows the relationship between private_handle_t and native_handle. The following is the definition of native_handle:

typedef struct native_handle 
{
int version; /* sizeof(native_handle_t) */
int numFds; /* number of file-descriptors at &data[0] */
int numInts; /* number of ints at &data[numFds] */
int data[0]; /* numFds + numInts ints */
} native_handle_t;

The version field is set to the size of native_handle. The numFds and numInts fields describe the number of file descriptors and integers in the data array. The data array is used to store hardware-specific information, which we can see in the following definition of private_handle_t:

#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
uint64_t base __attribute__((aligned(8)));
int pid;

#ifdef __cplusplus
static inline int sNumInts() {
return (((sizeof(private_handle_t) -
sizeof(native_handle_t))/sizeof(int)) - sNumFds);
}
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)
{
ALOGE("invalid gralloc handle (at %p)", h);
return -EINVAL;
}
return 0;
}
#endif
};

The fd member variable is a file descriptor that is used to describe a framebuffer or shared memory region. The magic member variable is stored as a magic number defined in the sMagic static variable. The flags member variable is used to describe the type of graphic buffer. For example, if it is equal to PRIV_FLAGS_FRAMEBUFFER, this buffer is allocated from framebuffer. The size member variable is the size of the graphic buffer. The offset member variable is the offset from the starting address in memory. The base member variable is the address allocated for the buffer. The pid member variable is the process ID of the creator of the graphic buffer.

The constructor fills in the member variables of native_handle. The validate member function is used to validate whether the graphic buffer is an instance of private_handle_t or not.

As we mentioned previously, the Gralloc module that we are analyzing is the default implementation in AOSP, and is built as galloc.default.so. In this implementation, GPU is not used and the buffer will be allocated either in the framebuffer or shared memory. Even though this is not the ideal case for performance, it has the least hardware dependency, which is good as a reference to understand a more complicated Gralloc module implementation.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.103.27