驱动开发系列57 - Linux Graphics QXL显卡驱动代码分析(四)显示区域更新

一:概述

        前面在介绍了显示模式设置(分辨率,刷新率)之后,本文继续分析下,显示区域的绘制,详细看看虚拟机的画面是如何由QXL显卡绘制出来的。 

二:相关数据结构介绍 

struct qxl_monitors_config {uint16_t count;         // 当前启用的显示器数量(head 数量)uint16_t max_allowed;   // 驱动允许的最大显示器数量,如果为 0,表示没有固定限制,由驱动动态决定struct qxl_head heads[]; // 每个显示器的配置数组(实际大小为 count)
};
struct qxl_head {uint32_t id;           // 显示头(显示器)IDuint32_t surface_id;   // 绑定的 QXLSurface IDuint32_t width;        // 显示区域的宽度(像素)uint32_t height;       // 显示区域的高度(像素)uint32_t x;            // 在绑定 surface 上的 X 偏移(起始坐标)uint32_t y;            // 在绑定 surface 上的 Y 偏移uint32_t flags;        // 标志位,用于额外配置(比如是否启用、旋转等)
};
struct qxl_surface_id {uint32_t surface_id; // 表示 QXL 表面(Surface)的唯一标识符。每个表面都会有一个唯一的 ID,用于区分和识别不同的表面。
};
struct qxl_image {struct qxl_image_descriptor descriptor;  // 图像的描述符,包含图像的基本信息(例如图像类型、大小等)union {  /* 可变长度的数据,根据图像类型选择使用 */struct qxl_bitmap bitmap;        // 图像数据,作为位图(bitmap)表示struct qxl_encoder_data quic;    // 图像数据,作为 QUIC 编码数据表示(用于高效传输)struct qxl_surface_id surface_image; // 图像数据,作为表面图像的 ID 表示(指向一个渲染表面)} u;
};
struct qxl_image_descriptor {uint64_t id;           // 图像的唯一标识符,用于标识该图像在系统中的位置uint8_t type;          // 图像类型,用于指示该图像的具体格式或用途(例如位图、编码图像等)uint8_t flags;         // 图像的标志位,通常用于表示图像的特定属性(例如是否是透明图像等)uint32_t width;        // 图像的宽度,以像素为单位uint32_t height;       // 图像的高度,以像素为单位
};
struct qxl_encoder_data {uint32_t data_size;   // 编码数据的大小,单位是字节。uint8_t data[];       // 可变长度的编码数据,存储编码后的图像或图形数据。
};
struct qxl_palette {uint64_t unique;      // 唯一标识符,用于区分不同的调色板。每个调色板都会有一个唯一的 ID。uint16_t num_ents;    // 调色板条目的数量,即调色板中包含的颜色数。uint32_t ents[];      // 可变长度数组,存储调色板中的颜色值。每个条目代表一个颜色,通常是32位颜色值(例如 RGBA)。
};
struct qxl_bitmap {uint8_t format;        // 位图的格式,表示图像数据的颜色深度或编码方式。uint8_t flags;         // 位图的标志字段,指示位图的附加属性或状态。uint32_t x;            // 位图的 X 坐标,表示位图左上角的位置。uint32_t y;            // 位图的 Y 坐标,表示位图左上角的位置。uint32_t stride;       // 位图每行的字节数,即图像数据的跨度。用于访问像素数据时的步长。QXLPHYSICAL palette;   // 调色板的物理地址,用于与调色板相关的数据。通常这是指向 `qxl_palette` 结构体的指针。QXLPHYSICAL data;      // 图像数据的物理地址。指向存储位图像素数据的内存区域。
};
struct qxl_surface {uint32_t format;      // 图形表面的格式(例如 RGBA,BGRA 等)uint32_t width;       // 表面的宽度uint32_t height;      // 表面的高度int32_t stride;       // 行跨度,表示表面每行数据占用的字节数QXLPHYSICAL data;     // 指向表面数据的物理地址
};
struct qxl_surface_cmd {union qxl_release_info release_info;  // 用于释放的额外信息uint32_t surface_id;                  // 表面ID,标识一个具体的图形表面uint8_t type;                         // 命令类型,指示是创建表面还是销毁表面uint32_t flags;                       // 命令标志,可能用于控制命令的行为union {struct qxl_surface surface_create;  // 创建表面时的相关信息} u;
};
struct qxl_clip_rects {uint32_t num_rects;       // 包含的矩形数量struct qxl_data_chunk chunk;  // 包含矩形数据的内容
};
struct qxl_drawable {// 释放信息,用于释放渲染资源时的管理union qxl_release_info release_info;// 该可绘制对象使用的表面IDuint32_t surface_id;// 绘制效果类型,用于标识渲染操作的效果类型(例如:透明、混合等)uint8_t effect;// 绘制对象类型,定义该对象的类型(例如:填充、文本、复制等)uint8_t type;// 标记是否是自带位图数据,如果是,则会直接使用该位图uint8_t self_bitmap;// 如果是自带位图,定义位图的有效区域struct qxl_rect self_bitmap_area;// 可绘制对象的边界框,定义了渲染的边界范围struct qxl_rect bbox;// 裁剪区域,定义了在渲染时应考虑的区域,超出该区域的部分将被裁剪掉struct qxl_clip clip;// 内存管理时间戳,用于标识该对象的渲染时间uint32_t mm_time;// 目标表面数组,最多支持三个目标表面int32_t surfaces_dest[3];// 每个目标表面的渲染区域矩形struct qxl_rect surfaces_rects[3];// 根据绘制类型选择不同的绘制操作,这里使用联合体来支持多种操作union {// 填充操作,例如使用颜色填充区域struct qxl_fill fill;// 不透明操作,可能表示不透明的填充区域struct qxl_opaque opaque;// 复制操作,将源区域复制到目标区域struct qxl_copy copy;// 透明操作,可能表示透明背景或区域struct qxl_transparent transparent;// alpha混合操作,支持透明度的混合效果struct qxl_alpha_blend alpha_blend;// 复制位图操作,支持特定的位图复制struct qxl_copy_bits copy_bits;// 复制操作的另一种形式struct qxl_copy blend;// 3元逻辑操作,用于图像处理中基于位的像素操作struct qxl_rop_3 rop3;// 描边操作,用于绘制路径的边框struct qxl_stroke stroke;// 文本渲染操作,用于绘制文本struct qxl_text text;// 黑色掩码操作,支持黑色掩码效果struct qxl_mask blackness;// 反转掩码操作,用于像素的反色效果struct qxl_mask invers;// 白色掩码操作,支持白色掩码效果struct qxl_mask whiteness;// 合成操作,用于合成多个图像层struct qxl_composite composite;} u;
};
struct qxl_cursor_header {uint64_t unique;          // 唯一标识符,标识一个光标uint16_t type;            // 光标类型(例如:标准光标、自定义光标)uint16_t width;           // 光标的宽度(以像素为单位)uint16_t height;          // 光标的高度(以像素为单位)uint16_t hot_spot_x;      // 热点位置的 X 坐标(相对于光标左上角)uint16_t hot_spot_y;      // 热点位置的 Y 坐标(相对于光标左上角)
};
struct qxl_cursor {struct qxl_cursor_header header;  // 光标的基本信息uint32_t data_size;              // 光标数据的大小struct qxl_data_chunk chunk;     // 包含光标数据的内容(例如:像素数据)
};
struct qxl_cursor_cmd {union qxl_release_info release_info;  // 释放信息,用于同步或更新uint8_t type;                         // 光标操作类型(例如:设置、移动、隐藏)union {struct {  struct qxl_point_1_6 position;   // 光标的新位置uint8_t visible;                 // 光标是否可见QXLPHYSICAL shape;               // 光标形状数据} set;                              // 设置光标操作struct {  uint16_t length;                 // 光标轨迹的长度uint16_t frequency;              // 光标轨迹的频率} trail;                            // 设置光标轨迹操作struct qxl_point_1_6 position;      // 光标的新位置(在移动或其他命令中使用)} u;uint8_t device_data[QXL_CURSOR_DEVICE_DATA_SIZE];  // 设备特定数据,大小为 128 字节
};
struct qxl_ram_header {uint32_t magic;                            // 魔术数字,用于验证数据结构是否正确uint32_t int_pending;                      // 当前挂起的中断uint32_t int_mask;                         // 中断屏蔽uint8_t log_buf[QXL_LOG_BUF_SIZE];         // 日志缓冲区,用于记录日志struct qxl_ring_header cmd_ring_hdr;       // 命令环头,用于管理命令队列struct qxl_command cmd_ring[QXL_COMMAND_RING_SIZE]; // 命令环,用于存储具体的命令struct qxl_ring_header cursor_ring_hdr;    // 光标环头,用于管理光标队列struct qxl_command cursor_ring[QXL_CURSOR_RING_SIZE]; // 光标环,用于存储光标命令struct qxl_ring_header release_ring_hdr;   // 释放环头,用于管理释放命令uint64_t release_ring[QXL_RELEASE_RING_SIZE]; // 释放环,用于存储释放命令struct qxl_rect update_area;               // 更新区域,表示图形更新的区域/* appended for qxl-2 */uint32_t update_surface;                   // 更新的表面 ID,用于指定目标表面struct qxl_mem_slot mem_slot;              // 内存槽,用于分配图形内存struct qxl_surface_create create_surface;  // 表面创建结构,表示新创建的图形表面uint64_t flags;                            // 标志位,用于存储相关的配置信息/* appended for qxl-4 *//* used by QXL_IO_MONITORS_CONFIG_ASYNC */QXLPHYSICAL monitors_config;               // 显示器配置,用于存储显示器相关的配置信息uint8_t guest_capabilities[64];            // 客户端能力,存储与虚拟机环境相关的信息
};
/* qxl-1 compat: append only */
struct qxl_rom {uint32_t magic;                         // 魔术数,用于验证这是一个有效的 QXL ROMuint32_t id;                            // ROM 的唯一标识符uint32_t update_id;                     // 更新标识符,用于标记 ROM 版本或更新次数uint32_t compression_level;             // 压缩级别,可能指示 ROM 数据的压缩程度uint32_t log_level;                     // 日志级别,控制 QXL 驱动的日志输出详细程度uint32_t mode;                          // 驱动的工作模式(qxl-1 特定)uint32_t modes_offset;                  // 显示模式的偏移量,指向模式数据的位置uint32_t num_io_pages;                  // I/O 页的数量,表示 ROM 中的 I/O 页数量uint32_t pages_offset;                  // 页面的偏移量,指向页面数据的位置(qxl-1 特定)uint32_t draw_area_offset;              // 绘制区域的偏移量,表示绘制区域在 ROM 中的位置(qxl-1 特定)uint32_t surface0_area_size;            // 表面0区域的大小,用于描述第一个图形表面的内存大小(qxl-1 特定,名为 draw_area_size)uint32_t ram_header_offset;             // RAM 头部的偏移量,指向 RAM 头部数据的位置uint32_t mm_clock;                      // 内存时钟频率,控制 RAM 访问的时序,影响性能/* qxl-2 特有字段 */uint32_t n_surfaces;                   // 表面数量,表示支持的图形表面数量uint64_t flags;                         // 标志位,存储驱动的配置特性uint8_t slots_start;                    // 起始槽位,内存槽位的起始位置uint8_t slots_end;                      // 结束槽位,内存槽位的结束位置uint8_t slot_gen_bits;                  // 槽生成位数,用于标识槽位的生成位uint8_t slot_id_bits;                   // 槽 ID 位数,表示槽位的唯一标识符的位数uint8_t slot_generation;                // 槽代号,用于表示槽位的代次(内存管理、版本控制)/* qxl-4 特有字段 */uint8_t client_present;                 // 客户端存在标志,指示客户端是否存在uint8_t client_capabilities[58];        // 客户端的能力位图,描述客户端支持的功能或特性uint32_t client_monitors_config_crc;    // 客户端显示配置的 CRC 校验和,用于验证配置的有效性struct {uint16_t count;                     // 显示器配置的数量uint16_t padding;                   // 填充,保持对齐struct qxl_urect heads[64];         // 显示器配置,最多支持 64 个显示器} client_monitors_config;               // 客户端监视器配置,描述显示器的位置和尺寸
};

三:显示区域更新介绍

        下面这段代码是用户空间通过 ioctl 发来的更新显示区域命令,这个代码的主要逻辑如下:

        1. 首先解析用户空间输入的更新区域,并检查区域是否有效。

        2. 然后根据用户空间传递的GEM对象句柄,查找到显存中的缓冲区对象。

        3. 将缓冲区对象固定在显存中,并映射到内核虚拟空间,加锁。

        4. 调用 qxl_io_update_area 更新指定的区域,这个函数的意思是给QXL 显卡发送命令,并等待命令完成。

        5. 更新完成后,解锁,释放相关资源。


int qxl_io_update_area(struct qxl_device *qdev, struct qxl_bo *surf,const struct qxl_rect *area)
{int surface_id;uint32_t surface_width, surface_height;int ret;if (!surf->hw_surf_alloc)DRM_ERROR("got io update area with no hw surface\n");if (surf->is_primary)surface_id = 0;elsesurface_id = surf->surface_id;surface_width = surf->surf.width;surface_height = surf->surf.height;if (area->left < 0 || area->top < 0 ||area->right > surface_width || area->bottom > surface_height)return -EINVAL;mutex_lock(&qdev->update_area_mutex);qdev->ram_header->update_area = *area;qdev->ram_header->update_surface = surface_id;ret = wait_for_io_cmd_user(qdev, 0, QXL_IO_UPDATE_AREA_ASYNC, true);mutex_unlock(&qdev->update_area_mutex);return ret;
}

四:dma_fence 介绍

        dma_fence 是 Linux 内核中用于同步GPU 或其他设备访问共享内存(如 buffer object)的机制。 比如 CPU 将渲染任务交给GPU(命令提交),GPU可能几毫秒后完成,所以此时就会建一个fence,意思是说别动这块内存,GPU正在使用,等GPU完成后,这个“牌子”就会被撤掉,CPU就可以安全访问了。

        在qxl驱动中,qxl_release 表示一次命令提交,当用户空间在命令提交时,qxl的 qxl_release_fence_buffer_objects 就会将一个buffer object 关联上一个dma_fence, 这样命令执行完成时,可以设置dma_fence 状态,用于通知用户空间了;下面是dma_fence 函数介绍和一个流程图:

        

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

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

相关文章

远程调用负载均衡LoadBalancer

1. 什么是负载均衡 负载均衡就是将负载&#xff08;工作任务&#xff0c;访问请求&#xff09;进行分摊到多个操作单元&#xff08;服务器,组件&#xff09;上进行执行。 根据负载均衡发生位置的不同,一般分为服务端负载均衡和客户端负载均衡。 服务端负载均衡&#xff1a;指的…

【深度学习】【目标检测】【Ultralytics-YOLO系列】YOLOV3核心文件detect.py解读

【深度学习】【目标检测】【Ultralytics-YOLO系列】YOLOV3核心文件detect.py解读 文章目录 【深度学习】【目标检测】【Ultralytics-YOLO系列】YOLOV3核心文件detect.py解读前言if name ‘main’parse_opt函数main函数run函数不同命令参数的推理结果常规推理命令推理命令(新增…

NextPolish1.4.1 安装与使用-bioinformatics tools54

01 简介 NextPolish 是一个用于修正由低准确度长读段&#xff08;如 ONT 或 CLR&#xff09;组装出来的基因组序列中碱基错误&#xff08;SNV/Indel&#xff09;的工具。它支持&#xff1a; 仅使用短读段 仅使用长读段 同时使用短读段与长读段 NextPolish 包含两个核心模块…

Vue3 el-tree:全选时只返回父节点,半选只返回勾选中的节点(省-市区-县-镇-乡-村-街道)

需求原因&#xff1a;全选时&#xff0c;传给接口的code数据太多了&#xff1b; 如果加上 check-strictly 父节点与子节点无关联&#xff0c;可以初步满足需求 效果如下使用了check-strictly的话&#xff0c;tree就没有了半选效果 不好的地方&#xff1a;用户体验感不好&#x…

使用 docker 安装 nacos3.x

一、安装 nacos 1.拉取镜像 使用如下指令拉取镜像 docker pull nacos/nacos-server 拉取完成后&#xff0c;可以使用以下命令查看是否拉取到对应的镜像&#xff0c;默认拉取最新镜像 docker images 2.新建挂载文件目录 mkdir -p /home/ubuntu/nacos/conf/mkdir -p /home/…

高性能Python Web 框架--FastAPI 学习「基础 → 进阶 → 生产级」

以下是针对 FastAPI 的保姆级教程&#xff0c;包含核心概念、完整案例和关键注意事项&#xff0c;采用「基础 → 进阶 → 生产级」的三阶段教学法&#xff1a; 一、FastAPI介绍 FastAPI 是一个现代化的、高性能的 Python Web 框架&#xff0c;专门用于构建 APIs&#xff08;应…

H2 Database Select 语句执行流程

H2 Database Select 语句执行流程 使用 // CREATE TABLE IF NOT EXISTS test(id INT primary key, name VARCHAR(255)) // insert into test(id, name) values(1, name1), (2, name2), (3, name3), (4, name4); String sql "SELECT * FROM test where id > 1 and na…

理解 Envoy 的架构

理解 Envoy 的架构对于深入理解 Istio 至关重要&#xff0c;因为 Envoy 是 Istio 数据平面的核心。Envoy 是一个高性能的 C 分布式代理&#xff0c;设计为云原生应用和大规模微服务架构的网络基础。 以下是 Envoy 架构的关键组成部分和核心理念&#xff1a; 核心设计理念&…

Android开发-常用布局

在Android应用开发中&#xff0c;布局决定了用户界面的结构和元素之间的相对位置。选择合适的布局不仅能够提升用户体验&#xff0c;还能提高代码的可维护性和灵活性。本文将介绍几种最常用的Android布局方式&#xff0c;包括LinearLayout、RelativeLayout、ConstraintLayout以…

如何在MySQL中实现类似Redis的PING命令的功能来检测连接状态?

要在MySQL中实现类似Redis的PING命令的功能来检测连接状态&#xff0c;可以采用以下方法&#xff1a; 方法一&#xff1a;使用简单的SQL查询 最直接的方法是通过执行一个简单的查询来检测连接状态&#xff0c;例如&#xff1a; SELECT 1;如果查询成功并返回结果&#xff08;…

Vue 系列之:defineProps、defineEmits、...

defineProps 用于接收父组件传递的属性值。 父组件&#xff1a; <!-- 父组件 --> <template><Child1 str"字符串" :num"num" />-----------------<Child2 str"字符串" :num"num" /> </template><…

windows服务器部署Gitlab

代码托管,如果对工具功能要求不高,Gitea也可以满足需要,只是功能相对比较简单。 通常GltLab是部署在linux服务器上的,windows版本已经不维护了。不过现在windows10 11已经可以实现部署了,一个是windows本机部署linux虚拟机(windows商店直接安装或者其他虚拟机平台都可以)…

剖析 FFmpeg:从基本功能到过滤器,实现音视频处理的灵活性

目录 1.解复用2 解码2.1 音频解码2.2 视频解码 3 修饰3.1 avio3.2 重采样 4 过滤器4.1 过滤器基本知识4.2 简单过滤器4.3 复杂滤镜图 1.解复用 解复用就是把容器中的媒体流分离出来&#xff0c;方便我们对媒体流处理。 step1&#xff1a;对媒体文件上下文初始化 AVFormatCont…

kafka学习笔记(四、生产者、消费者(客户端)深入研究(三)——事务详解及代码实例)

1.事务简介 Kafka事务是Apache Kafka在流处理场景中实现Exactly-Once语义的核心机制。它允许生产者在跨多个分区和主题的操作中&#xff0c;以原子性&#xff08;Atomicity&#xff09;的方式提交或回滚消息&#xff0c;确保数据处理的最终一致性。例如&#xff0c;在流处理中…

Missashe计网复习笔记(随时更新)

Missashe计算机网络复习笔记 前言&#xff1a;这篇笔记用于博主对计网这门课所学进行记录和总结&#xff0c;也包括一些个人的理解。正在更新当中…… 第一章 计算机网络体系结构 考纲内容 (一) 计算机网络概述 计算机网络的概念、组成与功能;计算机网络的分类; 计算机网络…

PVP鼠标推荐(deepseek)

下面有不懂的自行百度查找&#x1f44d; ❤️ 以下是几款在 双击性能&#xff08;DBC&#xff09; 和 拖拽点击&#xff08;DC&#xff09; 方面表现优秀的游戏鼠标推荐&#xff0c;结合了硬件性能、微动寿命以及玩家口碑&#xff1a; 1. 罗技 G102/G203 Lightsync 特点&#…

ABP vNext + EF Core 实战性能调优指南

ABP vNext EF Core 实战性能调优指南 &#x1f680; 目标 本文面向中大型 ABP vNext 项目&#xff0c;围绕查询性能、事务隔离、批量操作、缓存与诊断&#xff0c;系统性地给出优化策略和最佳实践&#xff0c;帮助读者快速定位性能瓶颈并落地改进。 &#x1f4d1; 目录 ABP vN…

为啥大模型一般将kv进行缓存,而q不需要

1. 自回归生成的特点 大模型&#xff08;如 GPT 等&#xff09;在推理时通常采用自回归生成的方式&#xff1a; 模型逐个生成 token&#xff0c;每次生成一个新 token 时&#xff0c;需要重新计算注意力。在生成第 t 个 token 时&#xff0c;模型需要基于前 t-1 个已生成的 t…

3DGS-slam:splatam公式

配套讲解视频&#xff1a;https://www.bilibili.com/video/BV1ZgfBYdEpg/?spm_id_from333.1387.homepage.video_card.click&vd_sourced4c3e747c32049ddd90dcce17208f4e0 1、多维高斯分布公式: 对于多维&#xff08;多变量&#xff09;高斯分布&#xff0c;概率密度函数的…

从Dockerfile 构建docker镜像——保姆级教程

从Dockfile开始 dockerfile简介开始构建1、编辑dockerfile2、构建镜像3、拉取镜像4、推送到镜像仓库 镜像的优化1、优化的基本原则2、多阶段构建 dockerfile简介 开始构建 1、编辑dockerfile # 使用官方的 Python 3.8 镜像作为基础镜像 FROM python:3.8-slim# 设置工作目录 …