最简单的DRM应用程序 (page-flip)

2023-10-30

在上一篇 最简单的DRM应用程序 (double-buffer)中,我们了解了DRM更新图像的一个重要接口drmModeSetCrtc()。在本篇文章中,我们将一起来学习DRM另一个重要的刷图接口:drmModePageFlip()

drmModePageFlip()的功能也是用于更新显示内容的,但是它和drmModeSetCrtc()最大的区别在于,drmModePageFlip()只会等到VSYNC到来后才会真正执行framebuffer切换动作,而drmModeSetCrtc()则会立即执行framebuffer切换动作。很明显,drmModeSetCrtc()对于某些硬件来说,很容易造成 撕裂(tear effect)问题,而drmModePageFlip()则不会造成这种问题。

由于drmModePageFlip()本身是基于VSYNC事件机制的,因此底层DRM驱动必须支持VBLANK事件

伪代码:

void my_page_flip_handler(...)
{
	drmModePageFlip(DRM_MODE_PAGE_FLIP_EVENT);
	...
}

int main(void)
{
	drmEventContext ev = {};

	ev.version = DRM_EVENT_CONTEXT_VERSION;
	ev.page_flip_handler = my_page_flip_handler;
	...

	drmModePageFlip(DRM_MODE_PAGE_FLIP_EVENT);
	
	while (1) {
		drmHandleEvent(&ev);
	}
}

详细参考代码如下:

modeset-page-flip.c

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <xf86drm.h>
#include <xf86drmMode.h>

struct buffer_object {
	uint32_t width;
	uint32_t height;
	uint32_t pitch;
	uint32_t handle;
	uint32_t size;
	uint32_t *vaddr;
	uint32_t fb_id;
};

struct buffer_object buf[2];
static int terminate;

static int modeset_create_fb(int fd, struct buffer_object *bo, uint32_t color)
{
	struct drm_mode_create_dumb create = {};
 	struct drm_mode_map_dumb map = {};
	uint32_t i;

	create.width = bo->width;
	create.height = bo->height;
	create.bpp = 32;
	drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);

	bo->pitch = create.pitch;
	bo->size = create.size;
	bo->handle = create.handle;
	drmModeAddFB(fd, bo->width, bo->height, 24, 32, bo->pitch,
			   bo->handle, &bo->fb_id);

	map.handle = create.handle;
	drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);

	bo->vaddr = mmap(0, create.size, PROT_READ | PROT_WRITE,
			MAP_SHARED, fd, map.offset);

	for (i = 0; i < (bo->size / 4); i++)
		bo->vaddr[i] = color;

	return 0;
}

static void modeset_destroy_fb(int fd, struct buffer_object *bo)
{
	struct drm_mode_destroy_dumb destroy = {};

	drmModeRmFB(fd, bo->fb_id);

	munmap(bo->vaddr, bo->size);

	destroy.handle = bo->handle;
	drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
}

static void modeset_page_flip_handler(int fd, uint32_t frame,
				    uint32_t sec, uint32_t usec,
				    void *data)
{
	static int i = 0;
	uint32_t crtc_id = *(uint32_t *)data;

	i ^= 1;

	drmModePageFlip(fd, crtc_id, buf[i].fb_id,
			DRM_MODE_PAGE_FLIP_EVENT, data);

	usleep(500000);
}

static void sigint_handler(int arg)
{
	terminate = 1;
}

int main(int argc, char **argv)
{
	int fd;
	drmEventContext ev = {};
	drmModeConnector *conn;
	drmModeRes *res;
	uint32_t conn_id;
	uint32_t crtc_id;

	/* register CTRL+C terminate interrupt */
	signal(SIGINT, sigint_handler);

	ev.version = DRM_EVENT_CONTEXT_VERSION;
	ev.page_flip_handler = modeset_page_flip_handler;

	fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);

	res = drmModeGetResources(fd);
	crtc_id = res->crtcs[0];
	conn_id = res->connectors[0];

	conn = drmModeGetConnector(fd, conn_id);
	buf[0].width = conn->modes[0].hdisplay;
	buf[0].height = conn->modes[0].vdisplay;
	buf[1].width = conn->modes[0].hdisplay;
	buf[1].height = conn->modes[0].vdisplay;

	modeset_create_fb(fd, &buf[0], 0xff0000);
	modeset_create_fb(fd, &buf[1], 0x0000ff);

	drmModeSetCrtc(fd, crtc_id, buf[0].fb_id,
			0, 0, &conn_id, 1, &conn->modes[0]);

	drmModePageFlip(fd, crtc_id, buf[0].fb_id,
			DRM_MODE_PAGE_FLIP_EVENT, &crtc_id);

	while (!terminate) {
		drmHandleEvent(fd, &ev);
	}

	modeset_destroy_fb(fd, &buf[1]);
	modeset_destroy_fb(fd, &buf[0]);

	drmModeFreeConnector(conn);
	drmModeFreeResources(res);

	close(fd);

	return 0;
}

以上代码是基于David Herrmann 所写的 drm-howto/modeset-vsync.c 文件修改的。和前两篇的案例一样,为了方便大家关注重点,以上代码删除了许多异常错误处理,并对程序功能做了简化。

从上面的代码可以看出,要使用drmModePageFlip(),就必须依赖drmHandleEvent()函数,该函数内部以阻塞的形式等待底层驱动返回相应的vblank事件,以确保和VSYNC同步。需要注意的是,drmModePageFlip()不允许在1个VSYNC周期内被调用多次,否则只有第一次调用有效,后面几次调用都会返回-EBUSY错误(-16)

运行结果:(模拟效果)

在这里插入图片描述

描述:程序运行后,屏幕在红色和蓝色之间来回切换;当输入CTRL+C后,程序退出。

注意:程序运行之前,请确保没有其它应用或服务占用/dev/dri/card0节点,否则将出现 Permission Denied 错误。

源码下载:modeset-page-flip

参考资料:
David Herrmann’s Blog: Advanced DRM Mode-Setting API
David Herrmann’s Github: drm-howto/modeset-vsync.c

文章汇总: DRM (Direct Rendering Manager) 学习简介

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

最简单的DRM应用程序 (page-flip) 的相关文章

随机推荐

  • Spring 的 IOC 容器和 SpringMVC 的 IOC 容器 关系

    需要进行 Spring 整合 SpringMVC 吗 还是否需要再加入 Spring 的 IOC 容器 是否需要再 web xml 文件中配置启动 Spring IOC 容器的 ContextLoaderListener 1 需要 通常情况
  • 配置宝塔,搭建网站

    配置宝塔面板 一 获取虚拟机的IP地址 打开虚拟机 输入命令 ip addr获取IP地址 二 使用cmd运行获取宝塔面板信息 1 运行cmd 并输入命令ssh root 获取的IP地址 2 输入登录密码 3 获取宝塔的命令行 4 输入14命
  • 跨专业计算机研究生如何毕业论文,跨专业考研论文要求

    跨专业考研写几篇论文 跨专业考研论文要求 近几年 跨专业考研得的队伍愈发壮大 许多考生不仅会跨相关专业 甚至还有人进行文理科间得的 大转行 跨专业报考得的难度系数在考研中是比较大得的 既有 满盘皆输 得的危险 也可能会带来出奇制胜得的效果
  • Column ‘status_type‘ in where clause is ambiguous

    多表查询时 由于字段名字有重复 所以需要指定那张表的字段
  • 学习笔记《你不知道的JavaScript上卷》第一章 作用域是什么 1.2理解作用域

    引擎 从头到尾负责整个 JavaScript 程序的编译及执行过程 编译器 引擎的好朋友之一 负责语法分析及代码生成等脏活累活 详见前一节的内容 作用域 引擎的另一位好朋友 负责收集并维护由所有声明的标识符 变量 组成的一系列查 询 并实施
  • 小程序开发工具全新上线

    导语 为了让开发者更高效地开发和发布小程序 微信开发者工具全新改版上线 并新增测试系统 腾讯云工具 运维性能监控 小程序分阶段发布 WXS脚本语言等 01 微信开发者工具 为提升小程序开发体验 帮助开发者更高效地开发小程序代码 微信开发者工
  • JDBC自定义框架

    自定义JDBC框架 定义必要的信息 获取数据库的连接 释放资源都是重复的代码 在操作JDBC时通常都是执行SQL语句就可以了 所以需要抽取出来一个模板类来封装一些方法 Update Query 专门执行增删改查的SQL语句 简化使用步骤 D
  • 字符串转LocalDateTime异常

    文章目录 现象 解决方案 拓展测试 总结 现象 当使用LocalDateTime 与字符串互转异常 使用LocalDateTime 字符串必须带时分秒 public static void main String args DateTime
  • vcruntime140.dll重新安装的方法(最佳推荐方法)

    vcruntime140 dll是什么什么文件呢 为什么电脑在运行一些游戏的时候会出现丢失vcruntime140 dll 然后游戏运行失败 这个dll文件是电脑重要的运行库文件 丢失了会导致很多程序无法运行 下面将介绍vcruntime1
  • 在vue项目中使用高德地图

    1 安装高德地图插件 npm install vue amap save 2 申请高德地图账号和key 官网地址 高德开放平台 高德地图API 3 在main js中引入 引入vue amap import VueAMap from vue
  • 三维包围盒碰撞检测算法-Python(OBB-SAT)

    想实现一个检测三维包围盒是否发生碰撞的功能 因为目标是任意方向的三维包围盒 即没有和坐标轴对齐的旋转包围盒 所以考虑采用检测OBB碰撞的SAT算法 但找了很久没找到现成的python代码 就比着别人写的C 版本自己写了python的代码 1
  • CAT3、CAT4、CAT5、CAT5E、CAT6、CAT6A、CAT7和CAT8网线的介绍

    目录 1 CAT3网线 10Mbps 2 CAT4网线已淘汰 3 CAT5 网线 100MHz 100Mbps 4 CAT5E网线 100MHz 1000Mbps 5 CAT6网线 250MHz 1000Mbps 6 CAT6A 网线 50
  • C#程序中进行FTP上传下载时出现的问题

    在C 程序中 进行ftp操作时容易出现的问题 The remote server returned an error 550 File unavailable e g file not found no access 解决方法 首先 查看登
  • matlab中的twomodegauss函数-双峰高斯函数

    文章搬运于 http blog sina com cn s blog 4fc818ea0101l8kn html function p twomodegauss m1 sig1 m2 sig2 A1 A2 k TWOMODEGAUSS Ge
  • STM32在FREEOS进行IAP跳转死机

    现象 STM32使用串口IAP进行跳转 发现APP程序用freeos编写的时候 程序跳转完成后就死机了 IAP程序在跳转前都关闭了中断和复位了使用的外设 已经验证跳转到裸机程序没有问题 FREEOS的程序在main函数开始就进行了开启中断和
  • wireshark取证案例学习笔记

    此文对应wireshark取证分析练习题前5道 题目来源 及PACP包下载地址 自己学习的一点笔记和心得 记录下来以免遗忘 练习题1的任务书解答 某公司怀疑其雇员张小花是其竞争对手派来的商业间谍 张小花访问了公司的一个机密配方 安保人员担心
  • Windows删除本地svn项目文件夹

    在window下 打开DOS命令窗口 进入需要清除svn的文件目录 输入如下命令 for r a in do if exist a svn rd s q a svn
  • ecs云服务器网站迁移,ecs云服务器网站迁移

    ecs云服务器网站迁移 内容精选 换一换 备案是中国大陆的一项法规 使用大陆节点服务器提供互联网信息服务的用户 需要在服务器提供商处提交备案申请 根据工信部 互联网信息服务管理办法 国务院292号令 和工信部令第33号 非经营性互联网信息服
  • GNU协议条款

    感谢原文作者 http v266 yo2 cn articles gnu E7 99 BE E7 A7 91 E5 90 8D E8 AF 8D E8 A7 A3 E9 87 8A html GNU 包含3个协议条款 GPL GNU通用公共
  • 最简单的DRM应用程序 (page-flip)

    在上一篇 最简单的DRM应用程序 double buffer 中 我们了解了DRM更新图像的一个重要接口drmModeSetCrtc 在本篇文章中 我们将一起来学习DRM另一个重要的刷图接口 drmModePageFlip drmModeP