display test
我们将在三个层面来分析:am-tests
中的display test
是如何输出相应的动画效果的
应用程序
首先在文件am-kernels/tests/am-tests/src/tests/video.c
中,定义了画布canvas的大小为32*32
。
static uint32_t canvas[N][N];
调用函数update()
来更新画布的内容,这样就会产生一张新的图片。
更新完画布内容后,就需要将画布的内容,加载到屏幕上。
这里函数redraw()
的作用就是将画布的像素数据存储到显存中,并且刷新屏幕展示画布内容。
/** * @brief 将画布 canvas 中的像素数据按行优先的顺序存储到显存中,并通过 io_write 将其绘制到屏幕上 * 参数: * - canvas[y][x]:表示画布canvas中(y , x)坐标的像素块。y 是行索引,x 是列索引 * - color_buf:一个缓冲区,它的大小是固定的 32 * 32,用来存储单个块的像素数据 * * 步骤: * 1. 获取屏幕的宽度和高度,并计算每个块的尺寸 * 2. 遍历 canvas 中的每个像素点,将像素数据填充到 color_buf 中 * 3. 通过 io_write 将 color_buf 中的像素数据传输到显存,绘制每个块 * 4. 最后调用同步操作,刷新屏幕。 * * @date 2024-11-25 */ void redraw() { // 将屏幕分割为 32x32 个小块 int w = io_read(AM_GPU_CONFIG).width / N; // 每个小块的宽度为 w int h = io_read(AM_GPU_CONFIG).height / N; // 每个小块的高度为 h int block_size = w * h; // 小块的面积 assert((uint32_t)block_size <= LENGTH(color_buf)); int x, y, k; // 将画布canvas[32][32] 的像素点,映射到屏幕分割的每个块 for (y = 0; y < N; y ++) { for (x = 0; x < N; x ++) { for (k = 0; k < block_size; k ++) { color_buf[k] = canvas[y][x]; // 将 canvas[y][x] 的颜色值重复填充到 color_buf 中 } // color_buf保存的是每个屏幕块的像素集合 io_write(AM_GPU_FBDRAW, x * w, y * h, color_buf, w, h, false); } } // 同步操作,通常用来通知显存操作完成并刷新屏幕 io_write(AM_GPU_FBDRAW, 0, 0, NULL, 0, 0, true); }
运行库IOE
我们可以看到,程序用到了IOE提供的相关接口来实现了以下操作:
-
读取屏幕宽和高的数据
io_read(AM_GPU_CONFIG) -
将像素块填充到显存的操作
io_write(AM_GPU_FBDRAW, x * w, y * h, color_buf, w, h, false) -
通知显存操作刷新屏幕的操作
io_write(AM_GPU_FBDRAW, 0, 0, NULL, 0, 0, true);
这里将用到的定义放在这:
#define io_read(reg) ({ reg ## _T __io_param; ioe_read(reg, &__io_param); __io_param; }) enum { AM_GPU_FBDRAW = (11) }; typedef struct { int x, y; void *pixels; int w, h; bool sync; } AM_GPU_FBDRAW_T; enum { AM_GPU_CONFIG = (9) }; typedef struct { bool present, has_accel; int width, height, vmemsz; } AM_GPU_CONFIG_T; #define SYNC_ADDR (VGACTL_ADDR + 4)
首先读取屏幕信息的操作展开为:
// io_read ( { AM_GPU_CONFIG_T __io_param; ioe_read(AM_GPU_CONFIG, &__io_param); __io_param; } )
而ioe_read(AM_GPU_CONFIG, &__io_param)
则是
void __am_gpu_config(AM_GPU_CONFIG_T *cfg) { *cfg = (AM_GPU_CONFIG_T) { .present = true, .has_accel = false, .width = BITS(inl(VGACTL_ADDR), 31, 16), .height = BITS(inl(VGACTL_ADDR), 15, 0), .vmemsz = inl(FB_ADDR) }; }
此代码从vga控制寄存器所在的内存中,读取对应的高2字节作为宽度,低2字节作为高度。
然后就是将像素块填充到显存的操作展开为:
// io_write(AM_GPU_FBDRAW, x * w, y * h, color_buf, w, h, false); ( { AM_GPU_FBDRAW_T __io_param = (AM_GPU_FBDRAW_T) { x * w, y * h, color_buf, w, h, false }; ioe_write(AM_GPU_FBDRAW, &__io_param); } )
即首先声明一个AM_GPU_FBDRAW_T
类型的变量__io_param
,其内部成员为:
int x = x * w; int y = y * h; void *pixels = color_buf; int w = w; int h = h; bool sync = false;
而向显存写操作ioe_write(AM_GPU_FBDRAW, &__io_param)
对应调用
/** * @brief 从AM帧缓冲控制器中读取图像信息,写入到显存中 * AM_GPU_FBDRAW_T参数:int x, y; void *pixels; int w, h; bool sync; * @param AM_GPU_FBDRAW_T *ctl * @date 2024-11-25 */ void __am_gpu_fbdraw(AM_GPU_FBDRAW_T *ctl) { // 获取图像在屏幕的位置信息(x, y) 和图像大小信息(w, h) int x = ctl->x, y = ctl->y, w = ctl->w, h = ctl->h; // 如果同步寄存器为false 并且 要没有要输出的图像(宽高任意为0),则不做任何操作 if (!ctl->sync && (w == 0 || h == 0)) return; uint32_t *pixels = ctl->pixels; // 将图像的像素信息 pixels,写到显存中 uint32_t *fb = (uint32_t *)(uintptr_t)FB_ADDR; uint32_t screen_w = BITS(inl(VGACTL_ADDR), 31, 16); for (int j = y; j < y+h; j++) { // 固定行的高度为j for (int i = x; i < x+w; i++) { // 按列遍历此行的每一个像素点 fb[i + j*screen_w] = pixels[(i-x) + (j - y)*w]; } } // 若`sync`为`true`, 则马上将帧缓冲中的内容同步到屏幕上 if (ctl->sync) { outl(SYNC_ADDR, 1); } }
这个函数还蕴含着同步寄存器就在控制寄存器的高4位字节上。
本文作者:上山砍大树
本文链接:https://www.cnblogs.com/shangshankandashu/p/18568409
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步