searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

linux云桌面2d渲染的全链路流程

2023-07-28 08:26:41
180
0

整个2d渲染的流程图如下:

渲染操作从上层图形软件向xserver发起绘图请求;

X协议使用称为Drawing Primitives的原语操作执行所有图形。
这些包括直线绘制、区域填充、弧线和文本绘制。
 
xorg在framebuffer上执行完最基本的绘图操作之后,通过调用ScreenRec结构体中的BlockHandler方法,发起绘图脏区的刷新出去,驱动gpu进行渲染显示;
 
BlockHandler方法的具体声明:
typedef void (*ScreenBlockHandlerProcPtr) (ScreenPtr pScreen,void*timeout);
 
typedef struct _Screen {
...........
ScreenBlockHandlerProcPtr BlockHandler;
...........
} ScreenRec;
 
 
目前liunx云桌面,开源的虚拟化gpu类型主要两种:qxl和virtiogpu;本例以virtiogpu来分析;
 
在virtiogpu模式下,Xorg会选择modesetting作为DDX驱动程序,故BlockHandler方法会被注册到
modesetting模块的msBlockHandler_oneshot函数;
 
相关代码如下:
 
static void
ms_setup_scrn_hooks(ScrnInfoPtr scrn)
{
scrn->driverVersion=1;
scrn->driverName="modesetting";
scrn->name="modeset";

scrn->Probe=NULL;
scrn->PreInit=PreInit;
scrn->ScreenInit=ScreenInit;
scrn->SwitchMode=SwitchMode;
scrn->AdjustFrame=AdjustFrame;
scrn->EnterVT=EnterVT;
scrn->LeaveVT=LeaveVT;
scrn->FreeScreen=FreeScreen;
scrn->ValidMode=ValidMode;
}
 
static Bool
ScreenInit(ScreenPtr pScreen, int argc, char **argv)
{
................
pScreen->BlockHandler = msBlockHandler_oneshot;
................
 
}
 
modesetting用户态驱动为Xorg的一部分,具体位置:/usr/lib/xorg/modules/drivers/modesetting_drv.so
 
msBlockHandler_oneshot函数作为modesetting的脏区刷新操作,最底层会调用drmModeDirtyFB函数,完成脏区刷新区域数据发送到内核驱动的操作;
 
msBlockHandler_oneshot的具体调用层次如下:
 
 
drmModeDirtyFB函数是libdrm用户态的DRM_IOCTL_MODE_DIRTYFB系统调用接口,实现了脏区图块信息的下发;
此函数具体代码如下:
int drmModeDirtyFB(int fd, uint32_t bufferId,
drmModeClipPtrclips, uint32_tnum_clips)
{
structdrm_mode_fb_dirty_cmddirty= { 0 };

dirty.fb_id=bufferId;
dirty.clips_ptr=VOID2U64(clips);
dirty.num_clips=num_clips;

returnDRM_IOCTL(fd, DRM_IOCTL_MODE_DIRTYFB, &dirty);
}
 
接下来分析内核驱动drm:
DRM_IOCTL_MODE_DIRTYFB作为drm驱动的脏区刷新命令,对应的ioctl操作函数为:drm_mode_dirtyfb_ioctl
此函数的核心是调用struct drm_framebuffer_funcs的通用方法dirty;
具体代码如下:
struct drm_framebuffer_funcs {
.......
int (*dirty)(struct drm_framebuffer *framebuffer,
structdrm_file*file_priv, unsignedflags,
unsignedcolor, structdrm_clip_rect*clips,
unsignednum_clips);
};
 
int drm_mode_dirtyfb_ioctl(struct drm_device *dev,void*data, structdrm_file*file_priv)
{
.........
    struct drm_framebuffer *fb;
........
    fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id);
    if (!fb)
        return-ENOENT;
.........
    if (fb->funcs->dirty) {
         ret=fb->funcs->dirty(fb, file_priv, flags, r->color,clips, num_clips);
    } else {
        ret=-ENOSYS;
    }
........
}
 
而struct drm_framebuffer_funcs的通用方法dirty具体挂接的函数实现,是根据内核注册的gpu驱动类型来实现回调的;
根据virtiogpu驱动类型,可以找到dirty方法的具体实现函数为:virtio_gpu_framebuffer_surface_dirty
具体代码如下:
static const struct drm_framebuffer_funcs virtio_gpu_fb_funcs = {
.create_handle = drm_gem_fb_create_handle,
.destroy = drm_gem_fb_destroy,
.dirty = virtio_gpu_framebuffer_surface_dirty,
};
 
而virtio_gpu_framebuffer_surface_dirty主要实现是virtio_gpu_surface_dirty;
virtio_gpu_surface_dirty函数则完成脏区信息发送到virtio_gpu_queue队列,从而被qemu侧获取;
具体代码如下:
static int virtio_gpu_dirty_update(struct virtio_gpu_framebuffer *fb,
boolstore, intx, inty,
intwidth, intheight)
{
..........
virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle,
x, y, x2-x+1, y2-y+1);
return0;
}
 
void virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev,
uint32_tresource_id,
uint32_tx, uint32_ty,
uint32_twidth, uint32_theight)
{
structvirtio_gpu_resource_flush*cmd_p;
structvirtio_gpu_vbuffer*vbuf;

cmd_p=virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
memset(cmd_p, 0, sizeof(*cmd_p));

cmd_p->hdr.type=cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_FLUSH);
cmd_p->resource_id=cpu_to_le32(resource_id);
cmd_p->r.width=cpu_to_le32(width);
cmd_p->r.height=cpu_to_le32(height);
cmd_p->r.x=cpu_to_le32(x);
cmd_p->r.y=cpu_to_le32(y);

virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
}
 
qemu侧通过响应VIRTIO_GPU_CMD_RESOURCE_FLUSH命令,实现脏区信息的更新;
0条评论
0 / 1000
汤****银
1文章数
0粉丝数
汤****银
1 文章 | 0 粉丝
汤****银
1文章数
0粉丝数
汤****银
1 文章 | 0 粉丝
原创

linux云桌面2d渲染的全链路流程

2023-07-28 08:26:41
180
0

整个2d渲染的流程图如下:

渲染操作从上层图形软件向xserver发起绘图请求;

X协议使用称为Drawing Primitives的原语操作执行所有图形。
这些包括直线绘制、区域填充、弧线和文本绘制。
 
xorg在framebuffer上执行完最基本的绘图操作之后,通过调用ScreenRec结构体中的BlockHandler方法,发起绘图脏区的刷新出去,驱动gpu进行渲染显示;
 
BlockHandler方法的具体声明:
typedef void (*ScreenBlockHandlerProcPtr) (ScreenPtr pScreen,void*timeout);
 
typedef struct _Screen {
...........
ScreenBlockHandlerProcPtr BlockHandler;
...........
} ScreenRec;
 
 
目前liunx云桌面,开源的虚拟化gpu类型主要两种:qxl和virtiogpu;本例以virtiogpu来分析;
 
在virtiogpu模式下,Xorg会选择modesetting作为DDX驱动程序,故BlockHandler方法会被注册到
modesetting模块的msBlockHandler_oneshot函数;
 
相关代码如下:
 
static void
ms_setup_scrn_hooks(ScrnInfoPtr scrn)
{
scrn->driverVersion=1;
scrn->driverName="modesetting";
scrn->name="modeset";

scrn->Probe=NULL;
scrn->PreInit=PreInit;
scrn->ScreenInit=ScreenInit;
scrn->SwitchMode=SwitchMode;
scrn->AdjustFrame=AdjustFrame;
scrn->EnterVT=EnterVT;
scrn->LeaveVT=LeaveVT;
scrn->FreeScreen=FreeScreen;
scrn->ValidMode=ValidMode;
}
 
static Bool
ScreenInit(ScreenPtr pScreen, int argc, char **argv)
{
................
pScreen->BlockHandler = msBlockHandler_oneshot;
................
 
}
 
modesetting用户态驱动为Xorg的一部分,具体位置:/usr/lib/xorg/modules/drivers/modesetting_drv.so
 
msBlockHandler_oneshot函数作为modesetting的脏区刷新操作,最底层会调用drmModeDirtyFB函数,完成脏区刷新区域数据发送到内核驱动的操作;
 
msBlockHandler_oneshot的具体调用层次如下:
 
 
drmModeDirtyFB函数是libdrm用户态的DRM_IOCTL_MODE_DIRTYFB系统调用接口,实现了脏区图块信息的下发;
此函数具体代码如下:
int drmModeDirtyFB(int fd, uint32_t bufferId,
drmModeClipPtrclips, uint32_tnum_clips)
{
structdrm_mode_fb_dirty_cmddirty= { 0 };

dirty.fb_id=bufferId;
dirty.clips_ptr=VOID2U64(clips);
dirty.num_clips=num_clips;

returnDRM_IOCTL(fd, DRM_IOCTL_MODE_DIRTYFB, &dirty);
}
 
接下来分析内核驱动drm:
DRM_IOCTL_MODE_DIRTYFB作为drm驱动的脏区刷新命令,对应的ioctl操作函数为:drm_mode_dirtyfb_ioctl
此函数的核心是调用struct drm_framebuffer_funcs的通用方法dirty;
具体代码如下:
struct drm_framebuffer_funcs {
.......
int (*dirty)(struct drm_framebuffer *framebuffer,
structdrm_file*file_priv, unsignedflags,
unsignedcolor, structdrm_clip_rect*clips,
unsignednum_clips);
};
 
int drm_mode_dirtyfb_ioctl(struct drm_device *dev,void*data, structdrm_file*file_priv)
{
.........
    struct drm_framebuffer *fb;
........
    fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id);
    if (!fb)
        return-ENOENT;
.........
    if (fb->funcs->dirty) {
         ret=fb->funcs->dirty(fb, file_priv, flags, r->color,clips, num_clips);
    } else {
        ret=-ENOSYS;
    }
........
}
 
而struct drm_framebuffer_funcs的通用方法dirty具体挂接的函数实现,是根据内核注册的gpu驱动类型来实现回调的;
根据virtiogpu驱动类型,可以找到dirty方法的具体实现函数为:virtio_gpu_framebuffer_surface_dirty
具体代码如下:
static const struct drm_framebuffer_funcs virtio_gpu_fb_funcs = {
.create_handle = drm_gem_fb_create_handle,
.destroy = drm_gem_fb_destroy,
.dirty = virtio_gpu_framebuffer_surface_dirty,
};
 
而virtio_gpu_framebuffer_surface_dirty主要实现是virtio_gpu_surface_dirty;
virtio_gpu_surface_dirty函数则完成脏区信息发送到virtio_gpu_queue队列,从而被qemu侧获取;
具体代码如下:
static int virtio_gpu_dirty_update(struct virtio_gpu_framebuffer *fb,
boolstore, intx, inty,
intwidth, intheight)
{
..........
virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle,
x, y, x2-x+1, y2-y+1);
return0;
}
 
void virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev,
uint32_tresource_id,
uint32_tx, uint32_ty,
uint32_twidth, uint32_theight)
{
structvirtio_gpu_resource_flush*cmd_p;
structvirtio_gpu_vbuffer*vbuf;

cmd_p=virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
memset(cmd_p, 0, sizeof(*cmd_p));

cmd_p->hdr.type=cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_FLUSH);
cmd_p->resource_id=cpu_to_le32(resource_id);
cmd_p->r.width=cpu_to_le32(width);
cmd_p->r.height=cpu_to_le32(height);
cmd_p->r.x=cpu_to_le32(x);
cmd_p->r.y=cpu_to_le32(y);

virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
}
 
qemu侧通过响应VIRTIO_GPU_CMD_RESOURCE_FLUSH命令,实现脏区信息的更新;
文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0