最简单的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) 的相关文章

  • 在 RESTful Web 服务中实现注销

    我正在开发一个需要注销服务的移动应用程序 登录服务是通过数据库验证来完成的 现在我陷入了注销状态 退一步 您没有提供有关如何在应用程序中执行身份验证的详细信息 并且很难猜测您在做什么 但是 需要注意的是 在 REST 应用程序中 不能有会话
  • Antlr 解析器运算符优先级

    考虑以下语法 我对运算符优先级有疑问 例如 res 2 a b有一个类似的解析树res 2 a b 我知道问题出在哪里 但我没有想到没有相互左递归的 漂亮 解决方案 你能帮我一点忙吗 该语法与自定义访问者一起使用 grammar Math
  • MySQL 查询计算上个月

    我想计算上个月的订单总额 我收到了从当前日期获取当月数据的查询 SELECT SUM goods total AS Total Amount FROM orders WHERE order placed date gt date sub c
  • 没有输入的 jQuery 日期选择器

    我有一个相当复杂的网络应用程序 我想向其中添加一些日期选择 UI 我遇到的问题是我无法从文档中弄清楚如何真正控制日期选择器的出现方式和时间 不涉及任何表单元素 不 我不会添加秘密表单字段 因此简单的开箱即用方法根本行不通 我希望有人可以提供
  • PrimeFaces 对话框参考父级

    我有一个 xhtml 页面 显示带有条目的数据表 我还有一个用于插入新条目的按钮 该按钮显示一个包含表单的对话框 插入表格用作
  • 类型或命名空间“MyNamespace”不存在等

    我有通常的类型或命名空间名称不存在错误 除了我引用了程序集 using 语句没有显示为不正确 并且我引用的类是公共的 事实上 我在不同的解决方案中引用并使用相同的程序集来执行相同的操作 并且效果很好 顺便说一句 这是VS2010 有人有什么
  • php 数组中出现意外的 json 输出结构

    我正在尝试转换动态数据 如何从 PHP 获取此 JSON JSON 122240cb 253c 4046 adcd ae81266709a6 item 0 3 这就是我所做的 但它不起作用 PHP json array 122240cb 2
  • 现代编译器是否优化乘以 1 和 -1

    如果我写 template
  • 将第三个表链接到多对多关联中的桥接表

    设计这个数据库的正确方法是什么 这是我设置表格的方式 我在名为 教师 的表和名为 仪器 的表之间存在多对多关系 然后我有一个连接两者的桥接表 我想将另一个表与 BRIDGE 表关联起来 意思是乐器 老师的组合 该表有 3 行 指定老师可以教
  • NSArrayController 无需将大型数据集加载到数组中

    我想使用 NSArrayController 向 NSTableView 提供数据 我面临的问题是我不想将所有数据预先加载到数组中 然后使用数组控制器setContent 方法 我的数据模型是一个管理数百万条记录的大型现有代码库 它包含有效
  • 一种无需 JavaScript 即可在 PHP 中确定浏览器宽度的方法?

    首先有吗 或者我必须使用javascript 我希望能够更改使用的 CSS 因此 frex 我可以为移动设备或其他设备加载较小的字体 不幸的是 仅使用 PHP 无法检测用户分辨率 如果您使用 Javascript 则可以在 cookie 中
  • GUI Java 程序 - 绘图程序

    我一直试图找出我的代码有什么问题 这个想法是创建一个小的 Paint 程序并具有红色 绿色 蓝色和透明按钮 我拥有我能想到的让它工作的一切 但无法弄清楚代码有什么问题 该程序打开 然后立即关闭 import java awt import
  • 如何在 Angular 4 中翻译 mat-paginator?

    你知道如何在 Angular 中翻译 每页项目 吗mat paginator标签 这mat paginator是材料设计中的一个元素 您可以使用MatPaginatorIntl为了这 威尔 豪厄尔制作 https github com an
  • 从 mvc 控制器使用 Web api 控制器操作

    我有两个控制器 一个mvc控制器和一个api控制器 它们都在同一个项目中 HomeController Controller DataController ApiController 如果我想从 HomeController 中使用 Dat
  • 使用velocity.js制作可拖动元素的动画

    我正在使用velocity js 为用户拖动的可拖动 SVG 元素设置动画 然而 velocity js 将先前的 mousemove 坐标排队并通过所有后续的 mousemove 坐标进行动画处理 我想要的是velocity js 不要对
  • 如何确定 CultureInfo 实例是否支持拉丁字符

    是否可以确定是否CultureInfo http msdn microsoft com en us library system globalization cultureinfo aspx我正在使用的实例是否基于拉丁字符集 我相信你可以使
  • 如何修复:“无法解析类型 java.lang.CharSequence。它是从所需的 .class 文件间接引用的”消息? [复制]

    这个问题在这里已经有答案了 我正在尝试使用这个字符串 amountStr amountStr replace replace replace 但我收到一条错误消息 我知道我收到的错误消息是因为我刚刚发布的字符串已过时 所以我想知道该字符串的
  • 如何在 OSX 上安装 LaTeX .sty 文件?

    我设置了一个 LaTeX 项目 tex documents some file tex support todonotes sty where some file tex uses todonotes usepackage colorinl
  • 禁用允许文本选择的

    残疾人可以吗
  • PyAudio ErrNo 输入溢出 -9981

    我遇到了与用户相同的错误 Python 使用 Pyaudio 以 16000Hz 录制音频时出错 https stackoverflow com questions 12994981 python error audio recording

随机推荐

  • 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