KWin、libdrm、DRM从上到下全过程 —— drmModeAddFBxxx(1)

news2024/11/18 23:41:00

序言

最近在研究libdrm、DRM以及KWin,发现要真正理解Linux图形栈从上到下的机制,最好的、最易于理解的方法是将KWin、libdrm和DRM由上到下的调用过程暨代码统一进行研究,这样才能更好地理清其中的关系,把握总体全貌,因此笔者决定开始做这件事。这无疑是一个庞大的工程(目前来看前无古人),笔者不敢奢望能全部完成,但希望能够尽量走得远一些。闲言少叙,开始漫长的旅程……

一切从framebuffer开始

这段旅程从哪里开始?笔者选择从drmModeAddFBxxx函数族开始(包括drmModeAddFB、drmModeAddFB2和drmModeAddFB2WithModifiers函数)。一方面,笔者前段时间刚刚对于这部分做过研究(“DRM全解析 —— ADD_FB”和“DRM全解析 —— ADD_FB2”系列文章);另一方面,framebuffer也是DRM的“最左侧”内容,如下图所示:

上层 —— KWin代码

上层KWin的相关代码在KWin源码更目录下的src/backends/drm/drm_buffer.cpp中,代码如下:

std::shared_ptr<DrmFramebuffer> DrmFramebuffer::createFramebuffer(const std::shared_ptr<DrmGpuBuffer> &buffer)
{
    const auto size = buffer->size();
    const auto handles = buffer->handles();
    const auto strides = buffer->strides();
    const auto offsets = buffer->offsets();

    uint32_t framebufferId = 0;
    int ret;
    if (buffer->gpu()->addFB2ModifiersSupported() && buffer->modifier() != DRM_FORMAT_MOD_INVALID) {
        uint64_t modifier[4];
        for (uint32_t i = 0; i < 4; i++) {
            modifier[i] = i < buffer->planeCount() ? buffer->modifier() : 0;
        }
        ret = drmModeAddFB2WithModifiers(buffer->gpu()->fd(), size.width(), size.height(), buffer->format(), handles.data(), strides.data(), offsets.data(), modifier, &framebufferId, DRM_MODE_FB_MODIFIERS);
    } else {
        ret = drmModeAddFB2(buffer->gpu()->fd(), size.width(), size.height(), buffer->format(), handles.data(), strides.data(), offsets.data(), &framebufferId, 0);
        if (ret == EOPNOTSUPP && handles.size() == 1) {
            ret = drmModeAddFB(buffer->gpu()->fd(), size.width(), size.height(), 24, 32, strides[0], handles[0], &framebufferId);
        }
    }
    if (ret == 0) {
        return std::make_shared<DrmFramebuffer>(buffer, framebufferId);
    } else {
        return nullptr;
    }
}

在这里,我们的关注重点不放在KWin代码本身的机制,而是聚焦于与libdrm相关的接口函数,即上边提到的3个函数:drmModeAddFB、drmModeAddFB2和drmModeAddFB2WithModifiers。重点关注一下三个函数的接口区别:

drmModeAddFB(buffer->gpu()->fd(), size.width(), size.height(), 24, 32, strides[0], handles[0], &framebufferId);

drmModeAddFB2(buffer->gpu()->fd(), size.width(), size.height(), buffer->format(), handles.data(), strides.data(), offsets.data(), &framebufferId, 0);

drmModeAddFB2WithModifiers(buffer->gpu()->fd(), size.width(), size.height(), buffer->format(), handles.data(), strides.data(), offsets.data(), modifier, &framebufferId, DRM_MODE_FB_MODIFIERS);

中间层 —— libdrm代码

drmModeAddFB、drmModeAddFB2和drmModeAddFB2WithModifiers三个函数都在libdrm源码根目录的xf86drmMode.c文件中,代码分别如下:

  • drmModeAddFB
drm_public drmModeFBPtr drmModeGetFB(int fd, uint32_t buf)
{
	struct drm_mode_fb_cmd info;
	drmModeFBPtr r;

	memclear(info);
	info.fb_id = buf;

	if (drmIoctl(fd, DRM_IOCTL_MODE_GETFB, &info))
		return NULL;

	if (!(r = drmMalloc(sizeof(*r))))
		return NULL;

	r->fb_id = info.fb_id;
	r->width = info.width;
	r->height = info.height;
	r->pitch = info.pitch;
	r->bpp = info.bpp;
	r->handle = info.handle;
	r->depth = info.depth;

	return r;
}
  • drmModeAddFB2
drm_public int drmModeAddFB2(int fd, uint32_t width, uint32_t height,
		uint32_t pixel_format, const uint32_t bo_handles[4],
		const uint32_t pitches[4], const uint32_t offsets[4],
		uint32_t *buf_id, uint32_t flags)
{
	return drmModeAddFB2WithModifiers(fd, width, height,
					  pixel_format, bo_handles,
					  pitches, offsets, NULL,
					  buf_id, flags);
}
  • drmModeAddFB2WithModifiers
drm_public int drmModeAddFB2WithModifiers(int fd, uint32_t width,
		uint32_t height, uint32_t pixel_format, const uint32_t bo_handles[4],
		const uint32_t pitches[4], const uint32_t offsets[4],
		const uint64_t modifier[4], uint32_t *buf_id, uint32_t flags)
{
	struct drm_mode_fb_cmd2 f;
	int ret;

	memclear(f);
	f.width  = width;
	f.height = height;
	f.pixel_format = pixel_format;
	f.flags = flags;
	memcpy(f.handles, bo_handles, 4 * sizeof(bo_handles[0]));
	memcpy(f.pitches, pitches, 4 * sizeof(pitches[0]));
	memcpy(f.offsets, offsets, 4 * sizeof(offsets[0]));
	if (modifier)
		memcpy(f.modifier, modifier, 4 * sizeof(modifier[0]));

	if ((ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ADDFB2, &f)))
		return ret;

	*buf_id = f.fb_id;
	return 0;
}

从代码上就能看出来,drmModeAddFB2()和drmModeAddFB2WithModifiers()走的是一路,走的是DRM_IOCTL_MODE_ADDFB2;而drmModeAddFB是单一路,走的是DRM_IOCTL_MODE_ADDFB(其实这也就是笔者之前有两个系列文章“DRM全解析 —— ADD_FB”和“DRM全解析 —— ADD_FB2”的原因)。

底层 —— DRM代码

DRM_IOCTL_MODE_ADDFB和DRM_IOCTL_MODE_ADDFB2分别对应到底层内核DRM的代码。在Linux内核源码根目录下的drivers/gpu/drm/drm_ioctl.c中,代码如下:

DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb_ioctl, 0),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2_ioctl, 0),

drm_mode_addfb_ioctl和drm_mode_addfb2_ioctl函数都在Linux内核源码根目录下的drivers/gpu/drm/drm_framebuffer.c中,代码分别如下:

  • drm_mode_addfb_ioctl
int drm_mode_addfb_ioctl(struct drm_device *dev,
			 void *data, struct drm_file *file_priv)
{
	return drm_mode_addfb(dev, data, file_priv);
}
/**
 * drm_mode_addfb - add an FB to the graphics configuration
 * @dev: drm device for the ioctl
 * @or: pointer to request structure
 * @file_priv: drm file
 *
 * Add a new FB to the specified CRTC, given a user request. This is the
 * original addfb ioctl which only supported RGB formats.
 *
 * Called by the user via ioctl, or by an in-kernel client.
 *
 * Returns:
 * Zero on success, negative errno on failure.
 */
int drm_mode_addfb(struct drm_device *dev, struct drm_mode_fb_cmd *or,
		   struct drm_file *file_priv)
{
	struct drm_mode_fb_cmd2 r = {};
	int ret;

	if (!drm_core_check_feature(dev, DRIVER_MODESET))
		return -EOPNOTSUPP;

	r.pixel_format = drm_driver_legacy_fb_format(dev, or->bpp, or->depth);
	if (r.pixel_format == DRM_FORMAT_INVALID) {
		drm_dbg_kms(dev, "bad {bpp:%d, depth:%d}\n", or->bpp, or->depth);
		return -EINVAL;
	}

	/* convert to new format and call new ioctl */
	r.fb_id = or->fb_id;
	r.width = or->width;
	r.height = or->height;
	r.pitches[0] = or->pitch;
	r.handles[0] = or->handle;

	ret = drm_mode_addfb2(dev, &r, file_priv);
	if (ret)
		return ret;

	or->fb_id = r.fb_id;

	return 0;
}
  • drm_mode_addfb2_ioctl
int drm_mode_addfb2_ioctl(struct drm_device *dev,
			  void *data, struct drm_file *file_priv)
{
#ifdef __BIG_ENDIAN
	if (!dev->mode_config.quirk_addfb_prefer_host_byte_order) {
		/*
		 * Drivers must set the
		 * quirk_addfb_prefer_host_byte_order quirk to make
		 * the drm_mode_addfb() compat code work correctly on
		 * bigendian machines.
		 *
		 * If they don't they interpret pixel_format values
		 * incorrectly for bug compatibility, which in turn
		 * implies the ADDFB2 ioctl does not work correctly
		 * then.  So block it to make userspace fallback to
		 * ADDFB.
		 */
		drm_dbg_kms(dev, "addfb2 broken on bigendian");
		return -EOPNOTSUPP;
	}
#endif
	return drm_mode_addfb2(dev, data, file_priv);
}
/**
 * drm_mode_addfb2 - add an FB to the graphics configuration
 * @dev: drm device for the ioctl
 * @data: data pointer for the ioctl
 * @file_priv: drm file for the ioctl call
 *
 * Add a new FB to the specified CRTC, given a user request with format. This is
 * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
 * and uses fourcc codes as pixel format specifiers.
 *
 * Called by the user via ioctl.
 *
 * Returns:
 * Zero on success, negative errno on failure.
 */
int drm_mode_addfb2(struct drm_device *dev,
		    void *data, struct drm_file *file_priv)
{
	struct drm_mode_fb_cmd2 *r = data;
	struct drm_framebuffer *fb;

	if (!drm_core_check_feature(dev, DRIVER_MODESET))
		return -EOPNOTSUPP;

	fb = drm_internal_framebuffer_create(dev, r, file_priv);
	if (IS_ERR(fb))
		return PTR_ERR(fb);

	drm_dbg_kms(dev, "[FB:%d]\n", fb->base.id);
	r->fb_id = fb->base.id;

	/* Transfer ownership to the filp for reaping on close */
	mutex_lock(&file_priv->fbs_lock);
	list_add(&fb->filp_head, &file_priv->fbs);
	mutex_unlock(&file_priv->fbs_lock);

	return 0;
}

到这里,可以说是“三分归一统”了。在应用层,drmModeAddFB2()和drmModeAddFB2WithModifiers()并在一起,统一使用DRM_IOCTL_MODE_ADDFB2;而在内核层,drmModeAddFB()也最终合并在了一起,统一调用了drm_mode_addfb2函数。

欲知后事如何,且看下回分解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1076362.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【WebService】C#搭建的标准WebService接口,在使ESB模版作为参数无法获取参数数据

一、问题说明 1.1 问题描述 使用C# 搭建WebService接口&#xff0c;并按照ESB平台人员的要求&#xff0c;将命名空间改为"http://esb.webservice",使用PostmanESB平台人员提供的入参示例进行测试时&#xff0c;callBussiness接口参数message始终为null。 以下是ES…

实验2.1.1 交换机的管理方式

实验2.1.1 交换机的管理方式 一、具体要求二、实验拓扑三、任务实施1.在PC机中使用默然参数连接交换机。3.通过system-view命令进入系统视图4.使用sysname重命名交换机为SWA。5.使用display current-configuration 命令查看交换机的配置信息。6.使用save命令保存配置信息。 一、…

35 WEB漏洞-逻辑越权之找回机制及接口安全

目录 找回重置机制接口调用乱用演示案例绑定手机验证码逻辑-Rep状态值篡改-实例某APP短信轰炸接口乱用-实例接口调用发包 文章分享&#xff1a;https://www.cnblogs.com/zhengna/p/15655691.html 有支付接口、短信发送接口&#xff0c;邮箱的发送接口等等&#xff0c;在接口这…

【算法挨揍日记】day12——153. 寻找旋转排序数组中的最小值、LCR 173. 点名

153. 寻找旋转排序数组中的最小值 153. 寻找旋转排序数组中的最小值 题目描述&#xff1a; 已知一个长度为 n 的数组&#xff0c;预先按照升序排列&#xff0c;经由 1 到 n 次 旋转 后&#xff0c;得到输入数组。例如&#xff0c;原数组 nums [0,1,2,4,5,6,7] 在变化后可能得…

嵌入式行业有无年龄危机?算不算青春饭?

今日话题&#xff0c;嵌入式行业到底算不算青春饭呢&#xff1f;在嵌入式行业工作两年半&#xff0c;我深刻体会到这个领域对技术的广泛要求&#xff0c;需要具备深厚而广泛的知识。因此&#xff0c;嵌入式行业的入门门槛相对较高。你需要了解软件和硬件&#xff0c;同时熟悉各…

使用EasyExcel导出复杂报表

1.效果图 2.请引入EasyExcel相关依赖&#xff0c;自行百度 3.controller Api(tags "下载中心接口") RestController RequestMapping("/basic/download") public class CspDownloadCenter extends BaseController {ApiOperation(value "复杂的exce…

Lesson10---list

lesson18: 一、 1.list是一个XX链表&#xff08;0&#xff1a;19&#xff1a;26&#xff09; 2.链表里面&#xff0c;单独存储数据最适合的结构是XX&#xff08;0&#xff1a;19&#xff1a;53&#xff09; 3.list的任意位置插入删除的时间复杂度是O(N)吗&#xff1f;&…

每日leetcode_2441_对应负数同时存在的最大整数

Leetcode每日一题_2441_对应负数同时存在的最大整数 记录自己的成长&#xff0c;加油。 题目 解题 class Solution {public int findMaxK(int[] nums) {int k -1;Set<Integer> set new HashSet<Integer>();for (int x : nums) {set.add(x);}for (int x : nums) …

springboot整合websocket开箱即用

springboot-websocket整合项目开箱即用&#xff0c;ws服务端&#xff0c;包含了在线客户监听&#xff0c;发送指定客户端数据&#xff0c;发送所有客户端数据。还可以根据指定转发给某个客户端。 WebSocketConfig配置缓存大小设置&#xff0c;设置异步发送操作的超时时间&#…

HTML5的新增表单元素

HTML5 有以下新的表单元素: <datalist> <keygen> <output> datalist datalist 元素规定输入域的选项列表。 datalist属性规定 form 或 input 域应该拥有自动完成功能。当用户在自动完成域中开始输入时&#xff0c;浏览器应该在该域中显示填写的选项&…

OSPF的原理与配置

第1章 OSPF[1] 本章阐述了OSPF协议的特征、术语&#xff0c;OSPF的路由器类型、网络类型、区域类型、LSA类型&#xff0c;OSPF报文的具体内容及作用&#xff0c;描述了OSPF的邻居关系&#xff0c;通过实例让读者掌握OSPF在各种场景中的配置。 本章包含以下内容&#xff1a; …

docker创建nginx容器

前言&#xff1a;当直接run运行nginx容器时&#xff0c;如果命令带有-v 映射出配置文件目录&#xff0c;则会报错&#xff0c;提示无法初始化&#xff0c;原因是没有配置文件&#xff0c;docker会同步主机文件到容器内&#xff0c;而主机文件又是空白的&#xff0c;所以无法启动…

选择工业交换机的外壳有什么要求?

在选择工业交换机时&#xff0c;我们通常更加注意工业交换机的温度范围、EMC电磁兼容性、安装方式和电源等方面&#xff0c;而对于工业交换机的外壳关注较少&#xff0c;除非客户有防水要求&#xff0c;那么&#xff0c;选择外壳有什么要求呢&#xff1f; 工业交换机的外壳采用…

华测监测预警系统 2.2 存在任意文件读取漏洞

华测监测预警系统 2.2 存在任意文件读取漏洞 一、 华测监测预警系统 2.2 简介二、漏洞描述三、影响版本四、fofa查询语句五、漏洞复现1、手动复现2、自动复现 六、修复建议 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信…

mac 版hadoop3.2.4 解决 Unable to load native-hadoop library 缺失文件

mac 版hadoop3.2.4或其他版本 Unable to load native-hadoop library 缺失文件 Native 包报错缺失&#xff1a; 1. hadoop-3.2.4/lib/native里加*.dylib 2. hadoop-3.2.4/etc/hadoop/hadoop-env.sh 加或修改 export HADOOP_OPTS"-Djava.library.path/Users/lvan/Documen…

a16z公布AI产品流量排名,ChatGPT占据榜首

&#x1f989; AI新闻 &#x1f680; a16z公布AI产品流量排名&#xff0c;ChatGPT占据榜首 摘要&#xff1a;美国风投公司a16z公布了针对C端用户的AI产品流量排名榜单。根据SimilarWeb的网站流量数据&#xff0c;ChatGPT位居榜首&#xff0c;占总流量的60%。排名第二的是Char…

设计模式 - 结构型模式考点篇:装饰者模式(概念 | 案例实现 | 优缺点 | 使用场景)

目录 一、结构型模式 1.1、装饰者模式 1.1.1、概念 1.1.2、案例实现 1.1.3、优缺点 1.1.4、使用场景 一、结构型模式 1.1、装饰者模式 1.1.1、概念 装饰者模式就是指在不改变现有对象结构的情况下&#xff0c;动态的给该对象增加一些职责&#xff08;增加额外功能&#…

IsaacGym四足机器人sim to real全过程详细解析(walk these ways方法)

四足机器人强化学习控制第三篇 第一篇 Isaac Gym环境安装和四足机器人模型的训练 第二篇 docker中安装ROS 在宇树四足机器人运行 强化学习GenLoco算法 目录 四足机器人强化学习控制第三篇前言一、安装和配置二、训练1.训练2.恢复训练3.训练结果 三、sim to real1.部署模型注意…

重生奇迹MU天空之城玩转攻略

现在依然有众多玩家喜欢重生奇迹MU 游戏&#xff0c;游戏设计非常完善&#xff0c;后续推出了重生奇迹MU 版本&#xff0c;在设计方面更加炫酷&#xff0c;各种游戏玩法以及模式&#xff0c;让众多玩家感受到不错的游戏体验。 游戏由很多的地图组成&#xff0c;重生奇迹MU 大陆…

【Redis】List列表相关的命令

目录 命令LPUSHLPUSHXLRANGERPUSHRPUSHXLPOPRPOPLINDEXLINSERTLLENblpop和brpop 命令 LPUSH 将⼀个或者多个元素从左侧放⼊&#xff08;头插&#xff09;到list中。 LPUSH key element [element ...]LPUSHX 在key存在时&#xff0c;将⼀个或者多个元素从左侧放⼊&#xff08…