需要在 Linux 帧缓冲区中绘制光标
Need to draw cursor in Linux framebuffer
我需要在 Linux 没有 X11 的情况下在屏幕上绘制光标(鼠标指针)。这用于嵌入式系统,其中所有其他绘图直接发生在帧缓冲区中 (/dev/fb0
)。
- 我目前查看的 GUI 库不提供任何光标支持。
- 我可以自己做 blitting,但我担心外观和性能,部分原因是我似乎无法与 vsync 同步 (
FBIO_WAITFORVSYNC
)。
- 我知道几乎所有的图形芯片都支持硬件光标,但 DirectFB 已死,libdrm 需要 X11,Mesa 也一样。
在 What is hardware cursor and how does it work? 中,OP 声称通过 ioctl
调用实现了这一点,并表示这很简单,但拒绝提供更多细节,因为他的代码是专有的。我知道 FBIO_CURSOR
,但它似乎是非标准的,并且在我的 3.10.0 内核上总是 returns EINVAL
。
在没有 X11 的情况下绘制帧缓冲区光标的正确方法是什么?
我最终推出了自己的光标支持,因为内核支持似乎取决于特定视频驱动程序所支持的内容。表演最终对我的目的来说很棒。这是我所做的:
- 打开/dev/fb0帧缓冲区,根据需要调整vinfo,
mmap
帧缓冲区,malloc
两个与帧缓冲区大小相同的缓冲区。其中一个缓冲区是我的后台缓冲区,所有绘图都在这里进行。另一个是我的 "cursor" 绘制光标的缓冲区。
- 打开相应的 /dev/input/eventX 准备读取鼠标事件。
- 定义一个 "refresh" 函数,以便在有内容被绘制到后台缓冲区或有鼠标时调用 activity。
poll
用于具有合理超时的鼠标事件。我使用了 500 毫秒的超时并将其放在 pthread
中,这样它的性能开销就很小。
- "refresh"函数
memcpy
'将后台缓冲区放入游标缓冲区,并在其上绘制游标。 (我根据图像 here 擦除光标下的掩码位并绘制光标位。)然后光标缓冲区 memcpy
进入帧缓冲区。
- (我用两个互斥锁保护刷新功能以获得更好的性能。我在将后台缓冲区复制到游标缓冲区之前获取第一个并在绘制光标后释放它。我在绘制光标并释放之前获取第二个它是在将光标缓冲区复制到帧缓冲区之后。在进行大量非常快速的绘图时,这会显着提高性能。)
我做出某些决定的几个原因:
- 写入帧缓冲区相当快,但从中读取要慢得多,因此使用常规
malloc
'ed 内存作为后备缓冲区和游标缓冲区。
memcpy
比我能写的任何东西都快得多,而且是线程安全的。
- 并发访问帧缓冲区很慢,大概是因为
memcpy
在尝试访问当前正在使用的区域时锁定区域和块。这就是为什么我使用两个互斥锁来保护从后台缓冲区到游标缓冲区以及从游标缓冲区到帧缓冲区的副本。
具有 0
超时的 poll
相当于使用大量 CPU 周期的紧密循环,因此使用非零超时。但是poll
returns一输入就有activity,所以响应性很好。
在我的硬件上,我没有找到与垂直消隐同步的可用方法(某些 ioctl
显然是无操作),但上述方法没有表现出特别的撕裂.是的,这种方法使用 两个 屏幕外缓冲区,在我的 1920 x 1080 16-bit/pixel 屏幕上每个缓冲区需要 4 MB,但它非常简单并且足以满足我的需要。
我需要在 Linux 没有 X11 的情况下在屏幕上绘制光标(鼠标指针)。这用于嵌入式系统,其中所有其他绘图直接发生在帧缓冲区中 (/dev/fb0
)。
- 我目前查看的 GUI 库不提供任何光标支持。
- 我可以自己做 blitting,但我担心外观和性能,部分原因是我似乎无法与 vsync 同步 (
FBIO_WAITFORVSYNC
)。 - 我知道几乎所有的图形芯片都支持硬件光标,但 DirectFB 已死,libdrm 需要 X11,Mesa 也一样。
在 What is hardware cursor and how does it work? 中,OP 声称通过 ioctl
调用实现了这一点,并表示这很简单,但拒绝提供更多细节,因为他的代码是专有的。我知道 FBIO_CURSOR
,但它似乎是非标准的,并且在我的 3.10.0 内核上总是 returns EINVAL
。
在没有 X11 的情况下绘制帧缓冲区光标的正确方法是什么?
我最终推出了自己的光标支持,因为内核支持似乎取决于特定视频驱动程序所支持的内容。表演最终对我的目的来说很棒。这是我所做的:
- 打开/dev/fb0帧缓冲区,根据需要调整vinfo,
mmap
帧缓冲区,malloc
两个与帧缓冲区大小相同的缓冲区。其中一个缓冲区是我的后台缓冲区,所有绘图都在这里进行。另一个是我的 "cursor" 绘制光标的缓冲区。 - 打开相应的 /dev/input/eventX 准备读取鼠标事件。
- 定义一个 "refresh" 函数,以便在有内容被绘制到后台缓冲区或有鼠标时调用 activity。
poll
用于具有合理超时的鼠标事件。我使用了 500 毫秒的超时并将其放在pthread
中,这样它的性能开销就很小。- "refresh"函数
memcpy
'将后台缓冲区放入游标缓冲区,并在其上绘制游标。 (我根据图像 here 擦除光标下的掩码位并绘制光标位。)然后光标缓冲区memcpy
进入帧缓冲区。 - (我用两个互斥锁保护刷新功能以获得更好的性能。我在将后台缓冲区复制到游标缓冲区之前获取第一个并在绘制光标后释放它。我在绘制光标并释放之前获取第二个它是在将光标缓冲区复制到帧缓冲区之后。在进行大量非常快速的绘图时,这会显着提高性能。)
我做出某些决定的几个原因:
- 写入帧缓冲区相当快,但从中读取要慢得多,因此使用常规
malloc
'ed 内存作为后备缓冲区和游标缓冲区。 memcpy
比我能写的任何东西都快得多,而且是线程安全的。- 并发访问帧缓冲区很慢,大概是因为
memcpy
在尝试访问当前正在使用的区域时锁定区域和块。这就是为什么我使用两个互斥锁来保护从后台缓冲区到游标缓冲区以及从游标缓冲区到帧缓冲区的副本。
具有 poll
相当于使用大量 CPU 周期的紧密循环,因此使用非零超时。但是poll
returns一输入就有activity,所以响应性很好。
0
超时的 在我的硬件上,我没有找到与垂直消隐同步的可用方法(某些 ioctl
显然是无操作),但上述方法没有表现出特别的撕裂.是的,这种方法使用 两个 屏幕外缓冲区,在我的 1920 x 1080 16-bit/pixel 屏幕上每个缓冲区需要 4 MB,但它非常简单并且足以满足我的需要。