Disp_manager.c
为了显示更流畅,事先malloc分配多块缓存,设置好页面内容,然后再把缓存内容全部memcpy复制进framebuffer显存里。每块缓存用VideoMem结构体来描述。
typedef struct VideoMem {
int iID; // ID值,用于标识不同的页面
int bDevFrameBuffer; //判断:1:这个VideoMem是显示设备的显存; 0:只是一个普通缓存
E_VideoMemState eVideoMemState; // 这个VideoMem的状态:空闲/用于预先准备显示内容/用于当前线程 */
E_PicState ePicState; // VideoMem中内存里图片的状态:空白/正在生成/已经生成
T_PixelDatas tPixelDatas; // 内存:用来存储图像
struct VideoMem *ptNext;
}T_VideoMem, *PT_VideoMem;
分配 iNum块缓存
为了考虑内存较少的设备, iNum可以为0,直接使用显存framebuffe,所以先将framebuffer放入链表。如果分配了缓存,分配iNum个内存都依次放入链表。若分配缓存,将framebuffer状态改为占用,这样不会被用作缓存分配。对于lcd的显存framebuffer,可以直接在上面描绘数据,而对于分配的缓存,设置好上面的内容后需要将它刷进framebuffer,所以设置了标记位bDevFrameBuffer,是framebuffer就不用刷了。
VideoMem: 为加快显示速度,我们事先在缓存中构造好显示的页面的数据,(这个缓存称为VideoMem)显示时再把VideoMem中的数据复制到设备的显存上
int AllocVideoMem(int iNum){
int i;
int iXres = 0;
int iYres = 0;
int iBpp = 0;
int iVMSize;
int iLineBytes;
PT_VideoMem ptNew;
/* 确定VideoMem的大小*/
GetDispResolution(&iXres, &iYres, &iBpp); //获取LCD分辨率
iVMSize = iXres * iYres * iBpp / 8; //分配内存大小
iLineBytes = iXres * iBpp / 8; //分配内存一行的字节数
/* 先把设备本身的framebuffer放入链表,因为可能不使用缓存
* 分配一个T_VideoMem结构体, 注意我们没有分配里面的tPixelDatas.aucPixelDatas
* 而是让tPixelDatas.aucPixelDatas指向显示设备的framebuffer*/
ptNew = malloc(sizeof(T_VideoMem));
ptNew->tPixelDatas.aucPixelDatas = g_ptDefaultDispOpr->pucDispMem; /* 指向framebuffer */
ptNew->iID = 0;
ptNew->bDevFrameBuffer = 1; //表示这个VideoMem是设备本身的framebuffer, 而不是用作缓存作用的VideoMem
ptNew->eVideoMemState = VMS_FREE;
ptNew->ePicState = PS_BLANK;
ptNew->tPixelDatas.iWidth = iXres;
ptNew->tPixelDatas.iHeight = iYres;
ptNew->tPixelDatas.iBpp = iBpp;
ptNew->tPixelDatas.iLineBytes = iLineBytes;
ptNew->tPixelDatas.iTotalBytes = iVMSize;
if (iNum != 0){
/* 如果下面要分配用于缓存的VideoMem,
* 把设备本身framebuffer对应的VideoMem状态设置为VMS_USED_FOR_CUR,
* 表示这个VideoMem不会被作为缓存分配出去*/
ptNew->eVideoMemState = VMS_USED_FOR_CUR;
}
ptNew->ptNext = g_ptVideoMemHead; // 放入链表
g_ptVideoMemHead = ptNew;
//分配用于缓存的VideoMem
for (i = 0; i < iNum; i++){
/* 分配T_VideoMem结构体本身和"跟framebuffer同样大小的缓存" */
ptNew = malloc(sizeof(T_VideoMem) + iVMSize);
/* 在T_VideoMem结构体里记录上面分配的"跟framebuffer同样大小的缓存" */
ptNew->tPixelDatas.aucPixelDatas = (unsigned char *)(ptNew + 1);
ptNew->iID = 0; //ID
ptNew->bDevFrameBuffer = 0; //普通缓存
ptNew->eVideoMemState = VMS_FREE; //状态:空闲
ptNew->ePicState = PS_BLANK; //图片状态:空
ptNew->tPixelDatas.iWidth = iXres; //设置PixelDatas结构体
ptNew->tPixelDatas.iHeight = iYres;
ptNew->tPixelDatas.iBpp = iBpp;
ptNew->tPixelDatas.iLineBytes = iLineBytes;
ptNew->tPixelDatas.iTotalBytes = iVMSize;
ptNew->ptNext = g_ptVideoMemHead; /* 放入链表 */
g_ptVideoMemHead = ptNew;
}
return 0;
}
获得一块可操作的VideoMem(它用于存储要显示的数据),用完后用PutVideoMem来释放。
根据ID从链表中取出缓存,
优先级:
1.取出空闲的、ID相同的VideoMem,
2.取出任意一个空闲且没有数据的VideoMem,
3.没有就取出任意一个空闲的VideoMem,
4. 取出任意一个VideoMem。
释放缓存就是将VideoMem的状态改为空闲,内容还在。每个页面的内容具有唯一的ID(根据字符串的assic码相加,基本上是唯一),下次获取VideoMem时,如果相同ID的VideoMem还在,就直接使用省去了内存描绘的过程。
PT_VideoMem GetVideoMem(int iID, int bCur){ //获得显存,参数:ID,给主/子线程使用
/* 1. 优先: 取出空闲的、ID相同的videomem */
PT_VideoMem ptTmp = g_ptVideoMemHead;
while (ptTmp){
if ((ptTmp->eVideoMemState == VMS_FREE) && (ptTmp->iID == iID)){
ptTmp->eVideoMemState = bCur ? VMS_USED_FOR_CUR : VMS_USED_FOR_PREPARE;
return ptTmp;
}
ptTmp = ptTmp->ptNext;
}
/* 2. 如果前面不成功, 取出一个空闲的并且里面没有数据(ptVideoMem->ePicState = PS_BLANK)的VideoMem */
ptTmp = g_ptVideoMemHead;
while (ptTmp){
if ((ptTmp->eVideoMemState == VMS_FREE) && (ptTmp->ePicState == PS_BLANK)){
ptTmp->iID = iID;
ptTmp->eVideoMemState = bCur ? VMS_USED_FOR_CUR : VMS_USED_FOR_PREPARE;
return ptTmp;
}
ptTmp = ptTmp->ptNext;
}
/* 3. 如果前面不成功: 取出任意一个空闲的VideoMem */
ptTmp = g_ptVideoMemHead;
while (ptTmp){
if (ptTmp->eVideoMemState == VMS_FREE){
ptTmp->iID = iID;
ptTmp->ePicState = PS_BLANK;
ptTmp->eVideoMemState = bCur ? VMS_USED_FOR_CUR : VMS_USED_FOR_PREPARE;
return ptTmp;
}
ptTmp = ptTmp->ptNext;
}
/* 4. 如果没有空闲的VideoMem并且bCur为1, 则取出任意一个VideoMem(不管它是否空闲) */
if (bCur){
ptTmp = g_ptVideoMemHead;
ptTmp->iID = iID;
ptTmp->ePicState = PS_BLANK;
ptTmp->eVideoMemState = bCur ? VMS_USED_FOR_CUR : VMS_USED_FOR_PREPARE;
return ptTmp;
}
return NULL;
}
唯一的ID,根据字符串的前4个字符的assic码相加
int ID(char *strName){
return (int)(strName[0]) + (int)(strName[1]) + (int)(strName[2]) + (int)(strName[3]);
}
使用GetVideoMem获得的VideoMem, 用完时用PutVideoMem释放掉
不用将ptVideoMem内规划好的内存释放掉,而是将状态改为空闲,这样下次还要用到该ID的页面时可以直接使用,省去了页面的生成。
void PutVideoMem(PT_VideoMem ptVideoMem){
ptVideoMem->eVideoMemState = VMS_FREE; /* 设置VideoMem状态为空闲 */
if (ptVideoMem->iID == -1)
ptVideoMem->ePicState = PS_BLANK; /* 表示里面的数据没有用了 */
}
rander.c
void FlushVideoMemToDev(PT_VideoMem ptVideoMem)
将VideoMem的内容memcpy刷进显存framebuffer,获得默认的显示设备,调用该设备的ShowPage函数。
void FlushVideoMemToDev(PT_VideoMem ptVideoMem){
if (!ptVideoMem->bDevFrameBuffer){
GetDefaultDispDev()->ShowPage(ptVideoMem);
}
}
fb.c
FBShowPage函数,将VideoMem的内从显示到显存framebuffer中。
static int FBShowPage(PT_VideoMem ptVideoMem){
memcpy(g_tFBOpr.pucDispMem, ptVideoMem->tPixelDatas.aucPixelDatas, ptVideoMem->tPixelDatas.iTotalBytes);
return 0;
}