我需要为没有连接真实显示器的虚拟GPU设备显示基于RAM的帧缓冲。我所拥有的是在RGB32格式的DRM_IOCTL_MODE_MAP_DUMB之后的mmap'ed内存块。目前我正在使用通过XShmCreatePixmap()创建的MIT-SHM共享像素图,如下所示:
shminfo.shmid = shmget(IPC_PRIVATE, bytes, IPC_CREAT|0777);
shminfo.readOnly = False;
shminfo.shmaddr = shmat(shminfo.shmid, 0, 0);
shmctl(shminfo.shmid, IPC_RMID, 0);
XShmAttach(dpy, &shminfo);
XShmCreatePixmap(dpy, window, shminfo.shmaddr, &shminfo, width, height, 24);
然后简单地
while (1) {
struct timespec ts = {0, 999999999L / 30};
nanosleep(&ts, NULL);
memcpy(shminfo.shmaddr, mem, bytes);
XCopyArea(dpy, pixmap, window, gc, 0, 0, width, height, 0, 0);
XFlush(dpy);
}
因此它每秒循环30次,在XCopyArea之后执行memcpy。问题是它在强大的机器上使用了大量的CPU:50%。有没有更好的方法?我可以想到两个可能的改进:
摆脱memcpy并将mmap的内存传递给MIT-SHM,但看起来MIT-SHM API不支持此功能。
获取某种“内容已更改”通知,以摆脱愚蠢的睡眠(但我找不到合适的内容)。
有什么想法吗?
更新: 瓶颈是'memcpy',如果删除CPU使用率可以忽略不计。问题似乎是没有办法分享以前mmap的内存(如果我正确理解API)所以我每次都被迫复制整个缓冲区。 我也尝试过glDrawPixels()和SDL曲面,两者看起来都比MIT-SHM慢。
更新:事实证明MIT-SHM不适合这样的任务。它的主要目的是创建缓冲区并以X IPC的开销写入(渲染)它。我不需要写任何东西,只需将现有缓冲区“转发”到X.在这种情况下,共享像素图,共享图像和常规X图像(XCreateImage)之间没有性能差异。
结论:到目前为止,我还没有找到允许渲染现有缓冲区的API,无需每次都复制数据。
答案 0 :(得分:1)
这可能是一项固有的昂贵操作 - 您需要将240 MB / s从程序(系统)内存移动到视频卡(设备)板载帧缓冲区。它不仅必须进行物理复制,还必须穿过设备总线。主存储器复制速度以GB /秒为单位,但设备总线相对慢得多。
除非你使用的是一个低端视频芯片,它使用系统内存作为帧缓冲器......具有讽刺意味的是,在这种情况下可能会更快。
你能让虚拟显示屏更小吗?
答案 1 :(得分:0)
我不是图形,Linux或优化专家,但我认为如果在更新时完全重绘源,此解决方案应该有效。
问题是您需要在更新后立即复制帧缓冲区。帧缓冲区很大(1920x1080x4字节),如果更新,您需要每1/30秒检查一次。
我建议在源缓冲区中写一个标志,并检查标志是否仍然存在的每1/30秒。如果不是,则更改源并且您需要重新复制到目标并重新设置标志。
作为标志,您可以使用单个像素(角落中的白色像素),或者您可以隐藏许多像素中的标志(如BMP中的隐藏消息)。另一个想法是使用任何像素RGB值的第四个字节,如果源是真彩色,第四个字节仅用于存储器对齐目的。
答案 2 :(得分:0)
对于X11使用XShmCreateImage
,请写信至XImage.data
并使XShmPutImage
显示,确保False
send_event
参数。您可能还想禁用当前GC的曝光事件;设置PointerMotionHintMask
也可以提供帮助。
SDL1执行上述大部分操作,但如果用户和显示格式不匹配,则会使用阴影表面,并且可能会执行意外的颜色转换。 SDL2尝试使用硬件加速,并可能执行意外的缩放和/或过滤。确保你得到了你要求的东西以避免隐藏的操作。
%50 cpu使用对于这个blit以30fps听起来很多,为了以防万一,我会重写睡眠功能。
do
errno = 0;
while ( nanosleep(&ts, &ts) && errno == EINTR );