Loading

数码相框-主界面MainPage显存管理

打开驱动程序得到显存。

写显示操作结构体

#ifndef _DISP_MANAGER_H
#define _DISP_MANAGER_H

// 包含显示操作相关的定义和结构体
#include <pic_operation.h>

// 显示操作结构体,定义了显示设备的操作接口
typedef struct DispOpr {
	char *name; // 设备名称
	int iXres; // 水平分辨率
	int iYres; // 垂直分辨率
	int iBpp;  // 每像素位数
	unsigned char *pucDispMem; // 显示内存指针
	int (*DeviceInit)(void); // 设备初始化函数
	int (*ShowPixel)(int iPenX, int iPenY, unsigned int dwColor); // 显示单个像素点
	int (*CleanScreen)(unsigned int dwBackColor); // 清屏函数
	int (*ShowPage)(PT_VideoMem ptVideoMem); // 显示一页图像
	struct DispOpr *ptNext; // 指向下一个显示设备操作的指针
}T_DispOpr, *PT_DispOpr;

// 视频内存状态枚举,标识视频内存的不同状态
typedef enum {
	VMS_FREE = 0, // 未使用
	VMS_USED_FOR_PREPARE, // 给子线程使用
	VMS_USED_FOR_CUR, // 当前主线程使用
}E_VideoMemState;

// 图片状态枚举,标识图片的不同生成状态
typedef enum {
	PS_BLANK = 0, // 为空
	PS_GENERATING, // 正在生成中
	PS_GENERATED, // 已生成
}E_PicState;

// 视频内存结构体,定义了视频内存的相关信息和操作
typedef struct VideoMem {
	int iID; // 视频内存ID
	int bDevFrameBuffer; // 是否为设备帧缓冲
	E_VideoMemState eVideoMemState; // 视频内存状态
	E_PicState ePicState; // 图片状态
	T_PixelDatas tPixelDatas; // 像素数据
	struct VideoMem *ptNext; // 指向下一个视频内存的指针
}T_VideoMem, *PT_VideoMem;


// 注册显示操作
int RegisterDispOpr(PT_DispOpr ptDispOpr);
// 展示所有已注册的显示操作
void ShowDispOpr(void);
// 初始化显示系统
int DisplayInit(void);
// 选择默认的显示设备
void SelectDefaultDispDev(char *name);
// 获取当前显示设备的分辨率和位深度
int GetDispResolution(int *piXres, int *piYres, int *piBpp);

// 初始化帧缓冲
int FBInit(void);

#endif /* _DISP_MANAGER_H */

分配显存


/*
 * 分配视频内存
 * 参数:
 *   iNum: 需要分配的视频内存块的数量(不包括设备本身的framebuffer)
 * 返回值:
 *   成功返回0,失败返回-1
 */

int AllocVideoMem(int iNum)
{
    int i;

    int iXres;  // 显示分辨率的宽度
    int iYres;  // 显示分辨率的高度
    int iBpp;   // 每像素位数

    int iVMSize;   // 视频内存大小
    int iLineBytes; // 每行字节

    PT_VideoMem ptNew; // 新的视频内存结构体指针

    // 获取显示分辨率和每像素位数
    GetDispResolution(&iXres, &iYres, &iBpp);
    // 计算视频内存大小和每行字节
    iVMSize = iXres * iYres * iBpp / 8;
    iLineBytes = iXres * iBpp / 8;

    /* 分配内存给新的视频内存结构体,并设置为设备本身的framebuffer */
    ptNew = malloc(sizeof(T_VideoMem));
    if (ptNew == NULL)
    {
        return -1;
    }   
    ptNew->tPixelDatas.aucPixelDatas = g_ptDefaultDispOpr->pucDispMem;
  
    // 初始化新分配的视频内存块信息
    ptNew->iID = 0;
    ptNew->bDevFrameBuffer = 1;//是帧缓冲区
    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)
    {
        ptNew->eVideoMemState = VMS_USED_FOR_CUR;//确保获取getVideoMem显存时,不会获得fb。
    }
  
    /* 将新的视频内存块链接到链表头部 */
    ptNew->ptNext = g_ptVideoMemHead;
    g_ptVideoMemHead = ptNew;
  
  
    // 为额外请求的视频内存块重复分配和初始化
    for (i = 0; i < iNum; i++)
    {
        ptNew = malloc(sizeof(T_VideoMem) + iVMSize);
        if (ptNew == NULL)
        {
            return -1;
        }
        ptNew->tPixelDatas.aucPixelDatas = (unsigned char *)(ptNew + 1);

        // 初始化新分配的视频内存块信息
        ptNew->iID = 0;
        ptNew->bDevFrameBuffer = 0;
        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;

        /* 将新的视频内存块链接到链表头部 */
        ptNew->ptNext = g_ptVideoMemHead;
        g_ptVideoMemHead = ptNew;
    }
  
    return 0;
}

image

虽然设备本身有固定的 framebuffer,但应用程序可能需要动态管理额外的视频内存资源以满足特定需求。数码相框可能需要同时处理多张图片的加载、预览、编辑或动画效果,这些操作可能超出设备原生 framebuffer 的容量。通过 AllocVideoMem ​函数,应用程序可以按需分配额外的视频内存块,用于存放这些额外的图像数据,而不必局限于设备固有的 framebuffer 大小。

AllocVideoMem ​允许为每个任务分配专属的视频内存块,并通过链表结构进行组织管理,便于任务间的切换和同步。

AllocVideoMem ​函数不仅分配内存,还初始化了与视频内存块关联的各种状态信息(如 eVideoMemState​、ePicState ​等)。这些状态信息有助于应用程序跟踪内存块的使用情况(如是否正在被当前画面使用、是否为空白等),便于进行高效的内存调度和更新策略。

即使设备初始设计时提供了足够的 framebuffer 空间,未来可能因为软件升级、功能增强或用户需求变化而需要更多视频内存。使用这样的函数可以灵活应对未来可能的变化,无需修改大量现有代码,只需调整内存分配策略或参数即可。

头插法的链表操作:

image

在函数中,每次新分配的视频内存块(如 ptNew​)被插入到链表头部的操作如下:

ptNew->ptNext = g_ptVideoMemHead;
g_ptVideoMemHead = ptNew;

这里,首先将新节点的 ptNext ​指针指向当前链表头节点(g_ptVideoMemHead​),然后将全局链表头指针 g_ptVideoMemHead ​更新为新节点 ptNew​。这种操作方式确保新节点始终成为链表新的第一个元素,即头插法。

获得显存


/**
 * 获取视频内存块
 * 
 * 本函数用于从视频内存链表中获取一个合适的视频内存块。首先尝试找到一个空闲的且ID匹配的内存块,
 * 如果没有找到,则尝试找到任何一个空闲的内存块。如果找到,会将其状态标记为使用中。ptTmp->ePicState = PS_BLANK;为空白,是为了后续更新照片。
 * 
 * @param iID 要求的视频内存块的ID。如果为匹配ID,则优先使用该ID的内存块。
 * @param bCur 标识当前视频帧还是下一帧视频帧需要使用的内存,true表示当前帧,false表示下一帧。
 * @return 返回获取到的视频内存块的指针,如果没有找到可用的内存块则返回NULL。
 */
PT_VideoMem GetVideoMem(int iID, int bCur)
{
	PT_VideoMem ptTmp = g_ptVideoMemHead;

	/* 1. 优先: 取出空闲的、ID相同的videomem */
	while (ptTmp)
	{
		if ((ptTmp->eVideoMemState == VMS_FREE) && (ptTmp>iID == iID))
		{
			ptTmp->eVideoMemState = bCur ? VMS_USED_FOR_CUR : VMS_USED_FOR_PREPARE;//分配一个空闲且ID相同的视频内存块时,可以认为其图片状态默认为PS_BLANK,无需额外显式设置。
			return ptTmp;
		}
		ptTmp = ptTmp->ptNext;
	}

	/* 2. 优先: 取出任意一个空闲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;
	}

	return NULL;
}

释放显存

/**
 * 将视频内存状态设置为可用
 * 
 * @param ptVideoMem 指向视频内存结构体的指针,该结构体描述了视频内存的状态。
 * 
 * 本函数用于将指定的视频内存状态设置为可用(VMS_FREE),通常在释放视频内存资源时调用。
 */
void PutVideoMem(PT_VideoMem ptVideoMem)
{
    ptVideoMem->eVideoMemState = VMS_FREE; // 设置视频内存状态为可用
}

显示主页面

写一个生成 ID 的宏:

#define ID(name) (int(name[0]) + int(name[1]) + int(name[2]) + int(name[3]))

将 ptVideoMem 中的数据刷新到设备显示缓冲区:

/**
 * 将视频内存中的数据刷新到设备显示缓冲区。
 * 
 * @param ptVideoMem 指向视频内存结构体的指针,该结构体包含要显示的视频帧数据。
 * 
 * 此函数首先检查视频内存中的数据是否已经映射到设备帧缓冲区。如果没有,则通过调用
 * 默认显示设备的ShowPage函数将视频数据显示到屏幕上。
 */
void FlushVideoMemToDev(PT_VideoMem ptVideoMem)
{
    // 如果视频内存没有映射到设备帧缓冲区,则调用显示设备的显示函数
    if (!ptVideoMem->bDevFrameBuffer)
    {
        GetDefaultDispDev()->ShowPage(ptVideoMem);
    }
}

VideoMem 内存与设备显示缓冲区之间存在映射关系(即 ptVideoMem->bDevFrameBuffer ​为真),则可能无需通过函数调用复制数据,而是可以直接访问同一块物理内存,实现零拷贝渲染。然而,对于未映射的情况,必须通过特定接口(如 GetDefaultDispDev()->ShowPage​)将视频内存数据推送到显示设备,以实现画面更新。FlushVideoMemToDev ​函数根据实际情况选择合适的方法,保证了数据传输的高效性。

显示页面到帧缓冲区:


/**
 * @brief 显示页面到帧缓冲区
 * 
 * 该函数用于将指定的视频内存内容复制到显示内存中,以更新显示内容。
 * 
 * @param ptVideoMem 指向包含视频内存数据结构的指针,该数据结构包含了要显示的像素数据。
 * @return int 函数没有返回值。
 */
static int FBShowPage(PT_VideoMem ptVideoMem)
{
    // 将视频内存中的像素数据复制到显示内存
    memcpy(g_tFBOpr->pucDispMem, ptVideoMem->tPixelDatas.aucPixelDatas, ptVideoMem->tPixelDatas.iTotalBytes);
}

posted @ 2024-04-11 15:00  阿四与你  阅读(13)  评论(0编辑  收藏  举报