we choose to go to the moon!�|

上山砍大树

园龄:5年3个月粉丝:13关注:3

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 中国大陆许可协议进行许可。

posted @   上山砍大树  阅读(43)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起