/hardware/libhardware/modules/gralloc/Gralloc.cpp
/hardware/libhardware/modules/gralloc/Gralloc_priv.cpp
/hardware/libhardware/include/hardware/Gralloc.h
class SimpleBestFitAllocator 的程式碼在
/hardware/libhardware/modules/gralloc/Allocator.cpp
/hardware/libhardware/modules/gralloc/Allocator.h
/hardware/libhardware/modules/gralloc/Allocator.h
gralloc_alloc() 函式
01 static int 02 gralloc_alloc(alloc_device_t* dev, int w, int h, int format, int usage, 03 buffer_handle_t* pHandle, int* pStride) 04 { 05 if (!pHandle || !pStride) 06 return -EINVAL; 07 08 size_t size, stride; 09 10 int align = 4; 11 int bpp = 0; 12 switch (format) { 13 case HAL_PIXEL_FORMAT_RGBA_8888: 14 case HAL_PIXEL_FORMAT_RGBX_8888: 15 case HAL_PIXEL_FORMAT_BGRA_8888: 16 bpp = 4; 17 break; 18 case HAL_PIXEL_FORMAT_RGB_888: 19 bpp = 3; 20 break; 21 case HAL_PIXEL_FORMAT_RGB_565: 22 case HAL_PIXEL_FORMAT_RGBA_5551: 23 case HAL_PIXEL_FORMAT_RGBA_4444: 24 bpp = 2; 25 break; 26 default: 27 return -EINVAL; 28 } 29 size_t bpr = (w*bpp + (align-1)) & ~(align-1); 30 size = bpr * h; 31 stride = bpr / bpp; 32 33 int err; 34 35 private_module_t* m = reinterpret_cast<private_module_t*>( 36 dev->common.module); 37 38 // 39 // 40 // 41 // 42 43 if (usage & GRALLOC_USAGE_HW_FB) { 44 err = gralloc_alloc_framebuffer(dev, size, usage, pHandle); 45 } 46 else { 47 g_w = w; g_h = h; g_fmt = format; 48 err = gralloc_alloc_buffer(dev, size, usage, pHandle); 49 } 50 if (err < 0) { 51 return err; 52 } 53 *pStride = stride; 54 return 0; 55 }
gralloc_alloc() 函式 使用 color format (format參數) 決定 byte per pixel(bpp), 以及 byte per row(bpr), 使用的是 4 bytes alignment. 並且根據 傳入的高度 h, 決定 buffer size.
Android 定義buffer usage 的種類如下:
/* buffer will be used as an OpenGL ES texture */
GRALLOC_USAGE_HW_TEXTURE
= 0x00000100,
/* buffer will be used as an OpenGL ES render target */
GRALLOC_USAGE_HW_RENDER = 0x00000200,
/* buffer will be used by the 2D hardware blitter */
GRALLOC_USAGE_HW_2D = 0x00000C00,
/* buffer will be used with the framebuffer device */
GRALLOC_USAGE_HW_FB = 0x00001000,
/* mask for the software usage bit-mask */
GRALLOC_USAGE_HW_MASK = 0x00001F00,
在 gralloc_alloc() 函式中, 除了 usage==GRALLOC_USAGE_HW_FB 時是調用gralloc_alloc_framebuffer()函式之外, 其餘 usage 的種類都會調用gralloc_alloc_buffer()函式. gralloc_alloc_framebuffer() 會調用到mapFrameBufferLocked(), 這個函式在 part1 的時候介紹過了, 現在來看 gralloc_alloc_buffer(). 完整的程式碼如下
在 gralloc_alloc() 函式中, 除了 usage==GRALLOC_USAGE_HW_FB 時是調用gralloc_alloc_framebuffer()函式之外, 其餘 usage 的種類都會調用gralloc_alloc_buffer()函式. gralloc_alloc_framebuffer() 會調用到mapFrameBufferLocked(), 這個函式在 part1 的時候介紹過了, 現在來看 gralloc_alloc_buffer(). 完整的程式碼如下
01 static int 02 gralloc_alloc_buffer(alloc_device_t* dev, 03 size_t size, int usage, buffer_handle_t* pHandle ) 04 { 05 int err = 0; 06 int flags = 0; 07 08 int fd = -1; 09 void* base = 0; 10 unsigned long phys_base = 0 ; 11 int offset = 0; 12 LOGD_IF(my_debug,"----->> %s , size = %d , usage = 0x%x\n", 13 __FUNCTION__,size,usage); 14 size = roundUpToPageSize(size); 15 16 #if HAVE_ANDROID_OS // should probably define HAVE_PMEM somewhere 17 18 if (usage & GRALLOC_USAGE_HW_TEXTURE) { 19 flags |= private_handle_t::PRIV_FLAGS_USES_PMEM; 20 } 21 22 if (usage & GRALLOC_USAGE_HW_2D) { 23 flags |= private_handle_t::PRIV_FLAGS_USES_PMEM; 24 } 25 26 if ((flags & private_handle_t::PRIV_FLAGS_USES_PMEM) == 0) { 27 try_ashmem: 28 fd = ashmem_create_region("gralloc-buffer", size); 29 if (fd < 0) { 30 LOGE("couldn't create ashmem (%s)", strerror(-errno)); 31 err = -errno; 32 } 33 } else { 34 private_module_t* m = reinterpret_cast<private_module_t*>( 35 dev->common.module); 36 37 err = init_pmem_area(m); 38 if (err == 0) { 39 // PMEM buffers are always mmapped 40 base = m->pmem_master_base; 41 // New added 42 phys_base = m->pmem_master_physbase ; 43 44 LOGI("ERR0 : sAllocator.allocate : size=%d\n", size); 45 offset = sAllocator.allocate(size); 46 if (offset < 0) { 47 // no more pmem memory 48 LOGW("ERR01 : OUT OF MEMORY : sAllocator.allocate : size=%d\n", size); 49 err = -ENOMEM; 50 } else { 51 struct pmem_region sub = { offset, size }; 52 53 // now create the "sub-heap" 54 fd = open("/dev/pmem", O_RDWR, 0); 55 err = fd < 0 ? fd : 0; 56 57 // and connect to it 58 if (err == 0) 59 err = ioctl(fd, PMEM_CONNECT, m->pmem_master); 60 61 // and make it available to the client process 62 if (err == 0) 63 err = ioctl(fd, PMEM_MAP, &sub); 64 65 if (err < 0) { 66 err = -errno; 67 close(fd); 68 sAllocator.deallocate(offset); 69 fd = -1; 70 } 71 memset((char*)base + offset, 0, size); 72 } 73 } else { 74 if ((usage & GRALLOC_USAGE_HW_2D) == 0) { 75 // the caller didn't request PMEM, so we can try something else 76 flags &= ~private_handle_t::PRIV_FLAGS_USES_PMEM; 77 err = 0; 78 goto try_ashmem; 79 } else { 80 LOGE("couldn't open pmem (%s)", strerror(-errno)); 81 } 82 } 83 } 84 85 #else // HAVE_ANDROID_OS 86 87 fd = ashmem_create_region("Buffer", size); 88 if (fd < 0) { 89 LOGE("couldn't create ashmem (%s)", strerror(-errno)); 90 err = -errno; 91 } 92 93 #endif // HAVE_ANDROID_OS 94 95 if (err == 0) { 96 private_handle_t* hnd = new private_handle_t(fd, size, flags); 97 if (base == 0) { 98 gralloc_module_t* module = reinterpret_cast<gralloc_module_t*>(dev->common.module); 99 err = mapBuffer(module, hnd); 100 if (err == 0) { 101 *pHandle = hnd; 102 } 103 } else { 104 hnd->offset = offset; 105 hnd->base = int(base)+offset; 106 phys_base += offset ; 107 hnd->sign_bit = 0x80000000 & phys_base ; 108 hnd->phys_base = 0x7FFFFFFF & phys_base ; 109 LOGD_IF(my_debug,"->> PMEM phys : 0x%x, fd=%d , virt=0x%x, pid=%d\n", 110 hnd->phys_base,hnd->fd,hnd->base,hnd->pid) ; 111 *pHandle = hnd; 112 } 113 } 114 return err; 115 }
18~34行
如果 usage 的設定有包含 GRALLOC_USAGE_HW_TEXTURE 或是 GRALLOC_USAGE_HW_2D則會使用 PMEM, 否則使用 ashmem.
PMEM與Ashmem都通過mmap實現共用,區別是PMEM的共用區域是一段連續的實體記憶體,GPU 或是 DSP使用的記憶體必須是連續的實體記憶體, 另外, PMEM不受 linux MM 的管理而是由 Android 提供了一個簡單的管理機制, 這一點後面會說明. Ashmem則在虛擬位址空間是連續的,但實體記憶體卻不需要連續. Ashmem 的管理由 linux 的 MM 管理機制負責.
回到 gralloc_alloc_buffer()函式, 以page alignment計算 buffer size 後, 第一個重要的函式是 init_pmem_area(), 以下是 init_pmem_area()
函式的程式碼.
01 static int init_pmem_area(private_module_t* m) 02 { 03 pthread_mutex_lock(&m->lock); 04 int err = m->pmem_master; 05 if (err == -1) { 06 // first time, try to initialize pmem 07 err = init_pmem_area_locked(m); 08 if (err) { 09 m->pmem_master = err; 10 } 11 } else if (err < 0) { 12 // pmem couldn't be initialized, never use it 13 } else { 14 // pmem OK 15 err = 0; 16 } 17 pthread_mutex_unlock(&m->lock); 18 return err; 19 }
第 4 行, m->pmem_master 中會紀錄是否第一次執行, 若是第一次執行則調用init_pmem_area_locked() 函式.此函式會 open PMEME device,
query PMEM total size, 並取得 virtual
address 和 physical address. 另外一個關鍵就是調用 SimpleBestFitAllocator::setSize(), 調用此函式效用等同初始化SimpleBestFitAllocator 物件, 這部分請參考 /hardware/libhardware/modules/gralloc/Allocator.cpp.
至於 gralloc_alloc() 又是被哪個模組在什麼樣的狀況下調用的呢? 這個問題和 Android 的 surfaceflinger 的運作有關係, 比較複雜, 需要另闢文章說明. 這裡先簡單點到即可 .Android
surfaceflinger 負責管理Android 顯示系統, 包括 Layer buffer 更新的同步控制. 計算layer 之間的 Z-order, 裁剪(crop), 底層 HAL 的初始化, 以及更新 framebuffer 等等. Surfaceflinger 採用的是 client-server 的架構. Surfaceflinger 需同時面對許多的客戶(client), 像是luncher, Android system process 或是其它應用程式並且服務這些客戶的需求. 客戶端透過 binder(一種IPC機制) 和 surfaceflinger 互相通信, 每個客戶開始使用 surfaceflinger 的服務之前, 需和 surfaceflinger 建立 connection, 也就是在 surfaceflinger端建立代理者. 所謂的代理者BClient 物件. 我們打開 \frameworks\base\libs\surfaceflinger\SurfaceFlinger.cpp,
這裡定義了一個 class : BClient , 看到BClient:: createSurface() 函式, 我們可以把這個函式當作 gralloc_alloc 被呼叫的起源.
BClient::createSurface() 會調用
surfaceflinger::createSurface() , 此函式根據不同的 surface type 調用不同的函式, 我們先看到 SurfaceFlinger::createNormalSurfaceLocked(),
此函式會產生 Layer 物件並調用 Layer::setBuffers() 函式, Layer::setBuffers() 函式產生 GraphicBuffer 物件並調用
GraphicBuffer::initSize(), 請看 \frameworks\base\libs\ui\GraphicBuffer.cpp,
GraphicBuffer::initSize()會調用
GraphicBufferAllocator::alloc() 函式, 此函式會調用到 gralloc::gralloc_alloc().
PMEM 的記憶體管理
在 Android 中透過SimpleBestFitAllocator
物件為 PMEM 提供管理機制, 程式碼在
/hardware/libhardware/modules/gralloc/Allocator.cpp
/hardware/libhardware/modules/gralloc/Allocator.h
class SimpleBestFitAllocator 比較關鍵的函式是 alloc() 以及 dealloc()
SimpleBestFitAllocator::alloc() 函式如下
01 ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags) 02 { 03 if (size == 0) { 04 return 0; 05 } 06 size = (size + kMemoryAlign-1) / kMemoryAlign; 07 chunk_t* free_chunk = 0; 08 chunk_t* cur = mList.head(); 09 10 size_t pagesize = getpagesize(); 11 while (cur) { 12 int extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ; 13 14 // best fit 15 if (cur->free && (cur->size >= (size+extra))) { 16 if ((!free_chunk) || (cur->size < free_chunk->size)) { 17 free_chunk = cur; 18 } 19 if (cur->size == size) { 20 break; 21 } 22 } 23 cur = cur->next; 24 } 25 26 if (free_chunk) { 27 const size_t free_size = free_chunk->size; 28 free_chunk->free = 0; 29 free_chunk->size = size; 30 // Richard 31 free_chunk->w = g_w; 32 free_chunk->h = g_h; 33 free_chunk->fmt = g_fmt; 34 35 if (free_size > size) { 36 int extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ; 37 if (extra) { 38 chunk_t* split = new chunk_t(free_chunk->start, extra, 0,0,0); 39 free_chunk->start += extra; 40 mList.insertBefore(free_chunk, split); 41 } 42 43 LOGE_IF(((free_chunk->start*kMemoryAlign)&(pagesize-1)), 44 "page is not aligned!!!"); 45 46 const ssize_t tail_free = free_size - (size+extra); 47 if (tail_free > 0) { 48 chunk_t* split = new chunk_t( 49 free_chunk->start + free_chunk->size, tail_free, 0, 0, 0); 50 mList.insertAfter(free_chunk, split); 51 } 52 } 53 statistic_alloc( size*kMemoryAlign ); // Richard statistic. 54 return (free_chunk->start)*kMemoryAlign; 55 } 56 return -ENOMEM; 57 }
SimpleBestFitAllocator::alloc() 函式使用 best fit 演算法管理 PMEM, 11~24行, best fit 演算法會尋找最接近需求大小的可用記憶體區塊. 因為選擇出來的記憶體區塊大小一定大於且最接近需求, 因此在第 40 和 50 行 best fit 演算法會將選到的記憶體區塊分割成兩塊, 一塊符合需求大小的區塊供做使用, 另一塊分割後則變成新的未使用記憶體區塊. best fit 演算法的缺點是比較容易產生因為較小而無法被使用的小區塊.
圖一是 reboot, 進入 home screen 後的 PMEM 使用狀況, 屏的尺寸是WVGA(800x480), PMEM size =24MB, 尚未被使用的記憶體區塊標示為 FREE, 被佔用的記憶體區塊標示為 OCCU.
另外我們來看 fragmentation rate, 這裡計算fragmentation rate 的方式是:
圖一 |
fragmentation : 3.059894 %
fragmentation : 6.119794 %
fragmentation : 6.412763 %
fragmentation : 11.897784 %
fragmentation : 17.724609 %
fragmentation : 23.209637 %
fragmentation : 28.694660 %
fragmentation : 28.694660 %
fragmentation : 28.694660 %
fragmentation : 28.694660 %
fragmentation : 31.754559 %
fragmentation : 34.814453 %
fragmentation : 34.814453 %
fragmentation : 34.814453 %
fragmentation : 34.814453 %
fragmentation : 40.641277 %
fragmentation : 40.641277 %
fragmentation : 43.701172 %
fragmentation : 46.761066 %
fragmentation : 52.246094 %
fragmentation : 52.246094 %
fragmentation : 52.246094 %
fragmentation : 52.246094 %
fragmentation : 52.246094 %
fragmentation : 52.246094 %
fragmentation : 52.246094 %
fragmentation : 52.246094 %
fragmentation : 52.246094 %
fragmentation : 52.246094 %
fragmentation : 52.246094 %
所以,完成開機後, fragment rate 大約是 52.2%.
沒有留言:
張貼留言