Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[question] ISoloTask中如何更快获取截屏数据? #978

Open
masm611 opened this issue Jan 7, 2025 · 10 comments
Open

[question] ISoloTask中如何更快获取截屏数据? #978

masm611 opened this issue Jan 7, 2025 · 10 comments

Comments

@masm611
Copy link
Contributor

masm611 commented Jan 7, 2025

  • Problem Description / 问题描述

我想要写一个可能更加精确的千音雅集算法,但是遇到了api速度的问题:
抓屏使用WGC,即使首页已经设置抓屏延迟为1ms,抓屏的api返回被我测得大约要100ms,代码如下:

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
ImageRegion captureArea = CaptureToRectArea();
stopwatch.Stop();

Logger.LogInformation("一轮:" + stopwatch.ElapsedMilliseconds.ToString() + "ms");

输出为“一轮:103ms
这让我无法继续完成贡献

我看到有一个回调OnCapture,但是仅能在ITaskTrigger中使用。千音雅集被设计在ISoloTask下,无法使用。
如何加快我获取截屏的速度?还是说我使用的方法与项目的设计相违背、有更好的方法?

@NONAME-2121237
Copy link

  • Problem Description / 问题描述

我想要写一个可能更加精确的千音雅集算法,但是遇到了api速度的问题: 抓屏使用WGC,即使首页已经设置抓屏延迟为1ms,抓屏的api返回被我测得大约要100ms,代码如下:

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
ImageRegion captureArea = CaptureToRectArea();
stopwatch.Stop();

Logger.LogInformation("一轮:" + stopwatch.ElapsedMilliseconds.ToString() + "ms");

输出为“一轮:103ms” 这让我无法继续完成贡献

我看到有一个回调OnCapture,但是仅能在ITaskTrigger中使用。千音雅集被设计在ISoloTask下,无法使用。 如何加快我获取截屏的速度?还是说我使用的方法与项目的设计相违背、有更好的方法?

说一个和api无关的事(我发现改了触发器延迟能提高连击准确性)Image
一般我是改成3ms再去打那种9星的谱

@huiyadanli
Copy link
Member

@NONAME-2121237 是你的幻觉,这里的间隔速度完全不影响千音雅集

@huiyadanli
Copy link
Member

@masm611 截图的方法性能不够的。ISoloTask直接使用 CaptureToRectArea(true); 强制获取最新图像,性能是最佳的

音游使用了直接GDI+获取像素的方式来达到高性能
https://github.com/babalae/better-genshin-impact/blob/main/BetterGenshinImpact/GameTask/AutoMusicGame/AutoMusicGameTask.cs

@masm611
Copy link
Contributor Author

masm611 commented Jan 7, 2025

我前两天已经查看了之前的实现,只不过对于连在一起的按钮效果不好想要尝试优化

并且gdi的getpixel无法在云原神上使用,这对我和很多用户来说不太友好

@masm611
Copy link
Contributor Author

masm611 commented Jan 7, 2025

不过我也蛮惊艳的,居然能够观察到判定线被挡住这种事,思路非常清晰并且观察很仔细,我觉得很厉害。

@masm611 masm611 closed this as completed Jan 7, 2025
@masm611 masm611 reopened this Jan 7, 2025
@masm611
Copy link
Contributor Author

masm611 commented Jan 7, 2025

设置forceNew为true的性能仍然很差
我跟踪了一下截图函数,发现了这个:

public static Bitmap CaptureGameBitmap(IGameCapture? gameCapture)
{
    var bitmap = gameCapture?.Capture();
    // wgc 缓冲区设置的2 所以至少截图3次
    if (gameCapture?.Mode == CaptureModes.WindowsGraphicsCapture)
    {
        for (int i = 0; i < 2; i++)
        {
            bitmap = gameCapture?.Capture();
            Sleep(50);
        }
    }

我不太理解为什么会出现Sleep(50)的情况,并且还截了3次图,我觉得这就是为什么截图要103ms左右的原因

@masm611
Copy link
Contributor Author

masm611 commented Jan 7, 2025

虽然说我会尽量尝试降低对高频截图的依赖,但是100多ms的截图似乎有点困难?因为要更好地区分多个连键(尤其是1/16拍的低于连续按键),个人认为需要把按键下落速度开到2.5倍以上,所以我觉得至少要50ms一次的截图来保证完整识别

@huiyadanli
Copy link
Member

huiyadanli commented Jan 7, 2025

设置forceNew为true的性能仍然很差 我跟踪了一下截图函数,发现了这个:

public static Bitmap CaptureGameBitmap(IGameCapture? gameCapture)
{
    var bitmap = gameCapture?.Capture();
    // wgc 缓冲区设置的2 所以至少截图3次
    if (gameCapture?.Mode == CaptureModes.WindowsGraphicsCapture)
    {
        for (int i = 0; i < 2; i++)
        {
            bitmap = gameCapture?.Capture();
            Sleep(50);
        }
    }

我不太理解为什么会出现Sleep(50)的情况,并且还截了3次图,我觉得这就是为什么截图要103ms左右的原因

这个是解决 WindowsGraphicsCapture 一些历史问题的。后续会优化。

BitBlt 理论上会在20ms内获得一张截图,1080p的情况下

@masm611
Copy link
Contributor Author

masm611 commented Jan 7, 2025

我可以自己调用更加底层一点点的库来截图吗?
就像这样:

// 获取窗口句柄
var hWnd = _hWnd;
// 初始化截图器
_capture = GameCaptureFactory.Create(TaskContext.Instance().Config.CaptureMode.ToCaptureMode());

// 启动截图
_capture.Start(hWnd,
    new Dictionary<string, object>()
    {
            { "useBitmapCache", false },
            { "autoFixWin11BitBlt", TaskContext.Instance().Config.AutoFixWin11BitBlt }
    }
);
try
{
    while (!ct.IsCancellationRequested)
    {
        //防止过量资源消耗
        await Task.Delay(75, ct);

        //获取截屏
        Bitmap? bitmap = _capture?.Capture();
        if (bitmap == null)
        {
            Logger.LogInformation("空bitmap");
            continue;
        }
        Mat originMat = BitmapConverter.ToMat(bitmap);

        //   做一些我的识别
    }
}
finally
{
    _capture.Stop();
}

我已经写了一点点识别逻辑了,但是突然想到不调用标准的接口会不会导致某些问题(比如在我的机器上发现不了的),所以问一下
这个截图很快并且较为稳定,用stopwatch测得时长在3ms到7ms之间

(不过确实出现了空bitmap并被我过滤掉,所以会不会有别的问题?)

@huiyadanli
Copy link
Member

同时启动多个应该会有问题,这个确实比BitBlt快,他来图的速度和当前游戏帧率有关

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants