0%

Android外部imgui绘制(三)

在获取系统触摸后,剩下的就是解决数据传递和画框子的问题了.

数据传递

由于 jni 是没有 root 权限的,所以只能由 java 端运行一个有 root 权限的进程,然后在 root 进程里将读取到的数据传回 imgui 进程.

匿名共享内存

这是我比较推荐的一个做法,首先用 open "/dev/ashmem" 打开匿名共享内存,设置大小,然后 mmap 函数映射到本地内存.

1
2
3
fd =  open("/dev/ashmem", O_RDWR);
ioctl(fd, ASHMEM_SET_SIZE, sizeof(AshmemData));
mmap(NULL, sizeof(AshmemData), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

之后通过 tcp 或 udp unix socket 将 fd 传给 imgui 进程里.

1
send_asheme_fd(fd);

imgui 进程同样使用 mmap 来映射匿名共享内存

1
mmap(NULL, sizeof(AshmemData), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

映射建立后,双方就可以使用匿名共享内存通信了.

共享内存结构

一般来说,共享内存包括了需要绘制的人物坐标,载具坐标,物品坐标,还需要额外传递一个 fps 值,这个 fps 值用来读取内存,画面同步.

1
2
3
4
5
6
7
8
9
struct AshmemData{
PlayerData Player[1000]; //人物数据
int Playersize; //人物总数
VehicleData Vehicle[1000]; //载具数据
int Vehiclesize; //载具数量
ItemData Item[1000]; // 物品数据
int Itemsize; // 物品数量
float fps; //fps,帧率
};

读取数据

如果是用外部读写的方案,那么可以选择如下方法

  • 读取 /proc/pid/mem 文件.
  • 读取 /proc/pid/pagemap 文件.
  • ptrace 读内存
  • syscall(SYS_process_vm_readv, pid, local, 1, remote, 1, 0);

如果用 Magisk 注入的 imgui, 那就比较简单,直接读取即可.

这里建议有几类数据就开几个线程,比如这里有 3 类数据 (人物,载具,物品), 就开三个线程.

数据读取速率与画面刷新速率同步

屏幕刷新速率通常是固定的,比如 120hz 的屏幕,每更新 1 帧的时间大约为 0.0083 秒,但是你并不能保证读取数据的速率也是 0.0083 秒。如果不加限制,就会造成性能的浪费,
比如屏幕刷新率比数据读取率快,那么就会导致几帧刷新同一画面.
如果屏幕刷新率比数据读取率慢,就会导致损失几帧的数据.

通常的做法是计算出每次数据读取的时间.

1
2
clock_gettime(CLOCK_MONOTONIC,&old);
clock_gettime(CLOCK_MONOTONIC,&now);

clock_gettime (CLOCK_MONOTONIC,&now); 可以读取到当前的时间.
在所有线程开始读取前先调用这个函数,读取数据后调用。然后相减即可得到读取一次数据的时间.

同样的,在 imgui 渲染线程里同样需要调用这个函数来计算渲染一次所需要的时间

如果谁的时间少,就需要调用 usleep 函数来等待到 fps 值.

1
usleep(time)

imgui 画框子,文字

1.imgui 画框子

1
2
3
4
void DrawTool::DrawRect(int x, int y, int w, int h, RGBA *color, int thickness)
{
ImGui::GetOverlayDrawList()->AddRect(ImVec2(x, y), ImVec2(x + w, y + h), ImGui::ColorConvertFloat4ToU32(ImVec4(color->R / 255.0, color->G / 255.0, color->B / 255.0, color->A / 255.0)), 0, 0, thickness);
}

2.imgui 画文字

1
2
3
4
void DrawTool::DrawNewText(int x, int y, RGBA *color, const char *str)
{
ImGui::GetOverlayDrawList()->AddText(ImVec2(x, y), ImGui::ColorConvertFloat4ToU32(ImVec4(color->R / 255.0, color->G / 255.0, color->B / 255.0, color->A / 255.0)), std::string(str).c_str());
}

pic