Gralloc is the HAL layer module in Android that is responsible for applying and releasing graphicBuffers. It is implemented by hardware drivers and provides the basis for BufferQueue mechanism. The graphic Buffer allocated by Gralloc is shared between processes and supports reads and writes on different hardware devices according to its Flag.
At the system level,gralloc
HAL layer module belongs to the lowest layer and is the upper layerlibui
The library provides services, and the overall hierarchy is as follows:
- The bottom is
gralloc
HAL module. - Up is
libui
A library whose main function is to encapsulate pairsgralloc
HAL layer call. The code directory isframeworks/native/include/uiandframeworks/native/libs/ui. - Is up
libgui
Library, whose main function is to encapsulate the connection between SF client and serverBufferQueue
And down depends on philibui
. The code directory isframeworks/native/include/guiandframeworks/native/libs/gui. - At the top is the user,
Skia
,Hwui
andOpenGL ES
isBufferQueue
The manufacturer of,SurfaceFlinger
isBufferQueue
The consumer of.
This article focuses on gralloc and LiBUI layers.
gralloc
The grallocHAL module structure is defined in gralloc. H:
// The module ID of gralloc
#define GRALLOC_HARDWARE_MODULE_ID "gralloc"
// Gralloc device ID
#define GRALLOC_HARDWARE_GPU0 "gpu0"
// Gralloc extends the HAL layer module structure
typedef struct gralloc_module_t {
Hw_module_t represents a generic hardware module, which is the soul of HAL layer and inherits hw_module_T
struct hw_module_t common;
// When graphicBuffers allocated by other processes are passed to the current process, this method needs to be mapped to the current process to prepare for subsequent locks
int (*registerBuffer)(struct gralloc_module_t const* module.buffer_handle_t handle);
// Cancel GraphicBuffer mapping in the current process, can not call lock later
int (*unregisterBuffer)(struct gralloc_module_t const* module.buffer_handle_t handle);
If usage specifies GRALLOC_USAGE_SW_* flag, vaddr will be filled into the virtual memory address of the graph Buffer, and the user can directly write pixel data to this address
int (*lock)(struct gralloc_module_t const* module.buffer_handle_t handle, int usage,
int l, int t, int w, int h,
void** vaddr);
// After writing to the graph Buffer, unlock commits data
int (*unlock)(struct gralloc_module_t const* module.buffer_handle_t handle);
// Other functions......
} gralloc_module_t;
// Gralloc extends HAL layer device structure
typedef struct alloc_device_t {
Hw_device_t indicates a common hardware device, which inherits the HW_device_t structure
struct hw_device_t common;
// Apply a graphic buffer and identify it with buffer_HANDLE_t
int (*alloc)(struct alloc_device_t* dev,
int w, int h, int format, int usage,
buffer_handle_t* handle, int* stride);
// Release a graphic buffer identified by buffer_HANDLE_t
int (*free)(struct alloc_device_t* dev,
buffer_handle_t handle);
// Other functions......
}
Copy the code
As mentioned earlier in the HWC module, every HAL layer module implementation defines a HAL_MODULE_INFO_SYM data structure, and the first field of that structure must be hw_module_T. Gralloc. CPP provides the default implementation of gralloc, and the corresponding shared library is gralloc.default.so. Qualcomm MSM8994 also provides the implementation, the corresponding shared library is gralloc.mSM8994. So, the following is the definition of default:
struct private_module_t HAL_MODULE_INFO_SYM = {
// base indicates the gralloc_module_T structure
.base = {
// common indicates the hw_module_T structure
.common = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = GRALLOC_HARDWARE_MODULE_ID,
.name = "Graphics Memory Allocator Module",
.author = "The Android Open Source Project".// Open the gralloc device alloc_device_t function
.methods = &gralloc_module_methods
},
// Extended field for gralloc_module_t
.registerBuffer = gralloc_register_buffer,
.unregisterBuffer = gralloc_unregister_buffer,
.lock = gralloc_lock,
.unlock = gralloc_unlock,
},
.framebuffer = 0,
.flags = 0,
.numBuffers = 0,
.bufferMask = 0,
.lock = PTHREAD_MUTEX_INITIALIZER,
.currentBuffer = 0};Copy the code
libui
Libui library mainly encapsulates the call to grallocHAL module, manages the allocation and release of GraphicBuffer and the mapping between different processes. It mainly contains three core classes, as shown in the following class diagram:
GraphicBuffer
: the correspondinggralloc
The allocated graphic Buffer (or plain memory, depending on the gralloc implementation) inheritsANativeWindowBuffer
Structure, the core is a pointer to the graphics memory (buffer_handle_t
), and the graphic Buffer itself is shared by multiple processes, and is transferred across processesGraphicBuffer
Key attributes so that the process can be reconstructed in useGraphicBuffer
And points to the same graphic Buffer.GraphicBufferAllocator
: Downward dockinggralloc
HAL modulealloc_device_t
The device, which is an in-process singleton, is responsible for allocating graphics buffers shared between processes, externally i.eGraphicBuffer
.GraphicBufferMapper
: Downward dockinggralloc
HAL modulegralloc_module_t
A module, which is an in-process singleton, is responsible for puttingGraphicBufferAllocator
The distribution ofGraphicBuffer
Map to the current process space.
Next, let’s look at the timing logic for applying and releasing GraphicBuffer in the current process:
The attributes of the GraphicBuffer successfully applied will be saved in the corresponding field of the GraphicBuffer parent class ANativeWindowBuffer:
Stride * height * number of bytes per pixel
typedef struct ANativeWindowBuffer {
// The width of the graph Buffer
int width;
// The height of the graph Buffer
int height;
// The step size of the graph Buffer, to handle alignment issues, may be different from height
int stride;
// The pixel format of the graphic Buffer
int format;
Gralloc allocates graphics buffers with different attributes.
int usage;
// Point to a graphic Buffer
buffer_handle_t handle;
} ANativeWindowBuffer_t;
Copy the code
Figure Buffer Size = Stride * height * number of bytes per pixel
In addition to applying a GraphicBuffer directly, you can also create a GraphicBuffer based on the different forms of the existing GraphicBuffer:
ANativeWindowBuffer
native_handle_t
(i.e.buffer_handle_t
)
The logic is simple, and you can refer directly to the GraphicBuffer overloaded constructor, which is not described here.
Above, ultimately through GraphicBufferAllocator. MAllocDev (alloc_device_t) distribution graphics Buffer, mAllocDev is initialized in GraphicBufferAllocator constructor:
// GraphicBufferAllocator is an in-process singleton
GraphicBufferAllocator::GraphicBufferAllocator(): mAllocDev(0)
{
hw_module_t const* module;
// As with HWC, open the gralloc module
int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
if (err == 0) {
// Open the gralloc device and save it in mAllocDev
gralloc_open(module, &mAllocDev); }}Copy the code
As with turning on the HWC device, turning on the Gralloc module first and then the Gralloc device is a general process for manipulating the HAL module.
In addition to the width, height, and pixel formats, there is also a usage parameter, which represents the application behavior of GraphicBuffer. Gralloc can optimize the usage according to the usage. Gralloc defines the usage enumeration value:
- GRALLOC_USAGE_SW_READ_NEVER: CPU does not read GraphicBuffer
- GRALLOC_USAGE_SW_READ_RARELY: CPU rarely reads GraphicBuffer
- GRALLOC_USAGE_SW_READ_OFTEN: The CPU reads GraphicBuffer frequently
- GRALLOC_USAGE_SW_WRITE_NEVER: CPU will not write GraphicBuffer
- GRALLOC_USAGE_SW_WRITE_RARELY writes GraphicBuffer
- GRALLOC_USAGE_SW_WRITE_OFTEN: CPU often writes GraphicBuffer
- GRALLOC_USAGE_HW_TEXTURE: GraphicBuffer can be uploaded as OpenGL ES texture, equivalent to GPU reading GraphicBuffer
- GRALLOC_USAGE_HW_RENDER: GraphicBuffer can be used as a rendering target for OpenGL ES, equivalent to GPU writing GraphicBuffer
- GRALLOC_USAGE_HW_2D: GraphicBuffer will be used by the 2D Hardware blitter
- GRALLOC_USAGE_HW_COMPOSER: HWC can be synthesized directly using GraphicBuffer
- GRALLOC_USAGE_HW_VIDEO_ENCODER: GraphicBuffer can be used as an input object for the Video hard encoder
- GRALLOC_USAGE_HW_CAMERA_WRITE: GraphicBuffer can be used as a camera rendering target, the same way the camera writes GraphicBuffer
- GRALLOC_USAGE_HW_CAMERA_READ: The camera can read GraphicBuffer
Graphicbuffer applicants can use different usage combinations depending on the scenario.
The above analyzed the scenario of allocating and releasing graphic buffers in the same process, thenGraphicBuffer
How do you share that between processes? It can be summarized by a flow chart:
- First, the creation process passes
GraphicBuffer::flatten
theANativeWindowBuffer
Key attributes are stored in two arrays:buffer
andfds
. - Second, cross-process transport
buffer
andfds
. - Then, use the process to pass through
GraphicBuffer::unflatten
Rebuild on your ownANativeWindowBuffer
The key is rebuildingANativeWindowBuffer.handle
Structure, equivalent to the creation processGraphicBuffer
Mapped to the consuming process. - Finally, follow registerBuffer-> Lock -> read and write
GraphicBuffer
-> UNLOCK ->unregisterBuffer basic process operationGraphicBuffer
Will do.
Binder simply transmits the ANativeWindowBuffer properties, and the real underlying graphics memory (memory) is shared between processes. As you can see from the context, GraphicBufferAllocator is responsible for applying and releasing GraphicBuffer when creating the process, and GraphicBufferMapper is responsible for manipulating GraphicBuffer when using the process.
All operations performed by GraphicBufferMapper on GraphicBuffer are implemented by grallocHAL module, specifically gralloc_module_t. If you are interested, see GraphicBufferMapper.cpp. Here is just a look at the initialization logic of the Gralloc module:
GraphicBufferMapper::GraphicBufferMapper(): mAllocMod(0) {
hw_module_t const* module;
// As with HWC, open the gralloc module
int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
if (err == 0) {
mAllocMod = reinterpret_cast<gralloc_module_t const* > (module); }}Copy the code
Finally, the key code for GraphicBuffer cross-process transmission is shown in the above flowchart:
/ / calculate transmission GraphicBuffer need Size size_t GraphicBuffer: : getFlattenedSize const () {return static_cast < size_t > (11 + (handle? handle->numInts : 0)) * sizeof(int); } / / retrieves the file descriptor number size_t GraphicBuffer: : getFdCount const () {return static_cast < size_t > (handle? handle->numFds : 0); } // Save GraphicBuffer key attributes in buffer and FDS for Binder transfer // size indicates the available length of buffer array, Status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& FDS, Const size_t & count) {/ / whether length of buffer available enough size_t sizeNeeded = GraphicBuffer: : getFlattenedSize (); if (size < sizeNeeded) return NO_MEMORY; / / whether the FDS array length enough size_t fdCountNeeded = GraphicBuffer: : getFdCount (); if (count < fdCountNeeded) return NO_MEMORY; // Store the key attributes of the current GraphicBuffer in buffer int32_t* buf = static_cast<int32_t*>(buffer); // Store identifier buf[0] = 'GBFR'; buf[1] = width; buf[2] = height; buf[3] = stride; buf[4] = format; buf[5] = usage; buf[6] = static_cast<int32_t>(mId >> 32); buf[7] = static_cast<int32_t>(mId & 0xFFFFFFFFull); buf[8] = static_cast<int32_t>(mGenerationNumber); buf[9] = 0; buf[10] = 0; Buf [9] = handle->numFds; Buf [10] = handle->numInts; // copy an array of file descriptors to FDS memcpy(FDS, handle->data, static_cast<size_t>(handle->numFds) * sizeof(int)); // copy an int array to buffer memcpy(&buf[11], handle->data + handle->numFds, static_cast<size_t>(handle->numInts) * sizeof(int)); } // Modify buffer address and available length buffer = static_cast<void*>(static_cast<uint8_t*>(buffer) + sizenneeded); size -= sizeNeeded; If (handle) {FDS += handle->numFds; count -= static_cast<size_t>(handle->numFds); } return NO_ERROR; } // According to buffer and FDS transmitted by Binder, Status_t GraphicBuffer::unflatten(void const* &buffer, size_t& size, int const* &fds, Size_t & count) {if (size < 11 * sizeof(int)) return NO_MEMORY; int const* buf = static_cast<int const*>(buffer); if (buf[0] ! = 'GBFR') return BAD_TYPE; Const size_t numFds = static_cast<size_t>(buf[9]); const size_t numFds = static_cast<size_t>(buf[9]); const size_t numInts = static_cast<size_t>(buf[10]); Const size_t sizenneeded = (11 + numInts) * sizeof(int); if (size < sizeNeeded) return NO_MEMORY; Size_t fdCountNeeded = numFds; if (count < fdCountNeeded) return NO_MEMORY; If (handle) {/ / if there is, first release before ANativeWindowBuffer. Handle free_handle (); } if (numFds || numInts) { width = buf[1]; height = buf[2]; stride = buf[3]; format = buf[4]; usage = buf[5]; / / create ANativeWindowBuffer. Handle, Native_handle_create is defined in native_handle.c native_handle* h = native_handle_CREATE (static_cast<int>(numFds), static_cast<int>(numInts)); if (! h) { width = height = stride = format = usage = 0; handle = NULL; ALOGE("unflatten: native_handle_create failed"); return NO_MEMORY; } / / from the FDS and buffer copy file descriptors and int array to ANativeWindowBuffer. The handle structure memcpy (h - > data, FDS, numFds * sizeof (int)); memcpy(h->data + numFds, &buf[11], numInts * sizeof(int)); handle = h; } else { width = height = stride = format = usage = 0; handle = NULL; } mId = static_cast<uint64_t>(buf[6]) << 32; mId |= static_cast<uint32_t>(buf[7]); mGenerationNumber = static_cast<uint32_t>(buf[8]); MOwner = ownHandle; mOwner = ownHandle; if (handle ! = 0) {/ / register to the current thread status_t err. = mBufferMapper registerBuffer (handle); } // Adjust the address and available length of buffer and FDS arrays buffer = static_cast<void const*>(static_cast<uint8_t const*>(buffer) + sizenneeded); size -= sizeNeeded; fds += numFds; count -= numFds; return NO_ERROR; } / / ANativeWindowBuffer. Handle structure typedef struct native_handle {/ * sizeof (native_handle_t) * / int version; /* number of file-descriptors at &data[0] */ int numFds; /* number of ints at &data[numFds] */ int numInts; /* numFds + numInts ints */ int data[0]; } native_handle_t; typedef const native_handle_t* buffer_handle_tCopy the code
The above code is long, but it is the key code, you can refer to the comments to read. ANativeWindowBuffer. The handle is the core of GraphicBuffer fields, defined on the handle. H. Native_handle. c defines the create, close, and delete native_handle methods.
GraphicBuffer is applied and freed by a GraphicBufferAllocator. The GraphicBuffer was rebuilt using the unflatten process, so how does the process release the GraphicBuffer? After all, the actual graphic Buffer is not created in the current process. We can look directly at the GraphicBuffer release code:
// The GraphicBuffer destructor is called here
void GraphicBuffer::free_handle()
{
if (mOwner == ownHandle) { // Indicates that the graphic Buffer is not created by itself, but is mapped from the creating process, i.e. in the using process
mBufferMapper.unregisterBuffer(handle);
/ / close to delete ANativeWindowBuffer. Handle, specific method implementation can see native_handle. C
native_handle_close(handle);
native_handle_delete(const_cast<native_handle*>(handle));
} else if (mOwner == ownData) { // Indicates that the graphic Buffer is created by itself and needs to be released by itself, i.e. in the creation process
GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
allocator.free(handle);
}
handle = NULL;
mWrappedBuffer = 0;
}
Copy the code
Graphicbuffers pointing to the same GraphicBuffer can have multiple instances, but the underlying GraphicBuffer is the same.
In addition, GraphicBufferAllocator keeps track of all graphicBuffers allocated by adb shell Dumpsys SurfaceFlinger:
// Allocated buffers: // respectively represent buffer_HANDle_t address, size of graph Buffer, stride * height, pixel format, usage, etc. 0x76FD25A070: 459 (512) x 1546.00 KiB | 773 | | 1 | 0 x10000900 | PopupWindow: e2334a2 # 0 0 x76fd25a7e0: 1080 (1088) x 9945.00 KiB | 2340 | 1 | 1 | 0 x10000900 | StatusBar# 0 0 x76fd837220: 1080 (1088) x 9945.00 KiB | 2340 | | 1 | 0 x10001a00 | FramebufferSurface / / the graph of allocated Buffer the Total size of the Total allocated (estimate) : 112834.50 KBCopy the code
Here’s another question: In which process is the GraphicBuffer allocated? To explain this, you need to understand the BufferQueue logic, which I won’t go into here, but will examine in a separate article. Give a direct conclusion: The BufferQueueProducer creates the GraphicBuffer from the IGraphicBufferAlloc that the BufferQueueCore holds, GraphicBufferAlloc, the implementation of IGraphicBufferAlloc, runs in the SurfaceFlinger process. That is, the Surfaceflinger process is the only one that actually allocates graphicBuffers, and the other processes are just mapping operations.
conclusion
This paper analyzes grallocHAL module and main logic of LiBUI library. The next article will take a closer look at the libGUI library’s main logic.