【问题标题】:How Java precise sleep under 1msJava 如何在 1ms 内精确睡眠
【发布时间】:2021-12-19 04:56:34
【问题描述】:

我正在研究 swing 和 graphics2D,我遇到了一些问题,如何精确睡眠?

因为对于 144 或 240 fps 的显示器,ms 太长了。使用 4.1667ms Thread.sleep 将导致非常不稳定的结果和时间被缩放。问题是有什么方法可以在 1 毫秒内实现真正的睡眠吗?

我做了一些研究,发现类似Thread.Sleep for less than 1 millisecond

根据帖子,事件睡眠(1)不会准确睡眠1ms,因为我的测试有100次结果:

551900
1932500
994600
939200
1903000
935000
958700
944200
938700
950800
953600
1920600
932500
954000
961700
1927400
930900
1936800
1913000
926500
1976200
957800
952000
940600
961000
971700
994800
1889500
1989800
907600
966100
1983600
1914300
1943300
999900
1911200
1988600
932500
1836100
1955500
913500
970200
956400
1902200
970300
1985300
1926100
990900
1804000
947700
1881400
933500
1963600
963000
928300
1913600
1016900
1824700
951600
1959900
938400
1951400
1957300
1965600
969400
1933800
936700
980000
961900
929500
1910000
936700
940100
976100
1975600
952100
986000
1959900
919300
1973500
1998700
1852000
931400
967500
1954200
2005800
1897200
968700
1925500
1945700
1917600
1946300
933200
957100
966400
944900
916000
934300
1887200
934200

没有一个是接近的,至少会导致10ms,其中一些甚至超过20ms!

而且 20 毫秒对于 60FPS 的显示器来说也是不可接受的。因为16.67ms是帧率。

我正在寻找 Thread.sleep

public static void sleep(long millis, int nanos)
throws InterruptedException {
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
                            "nanosecond timeout value out of range");
    }

    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
        millis++;
    }

    sleep(millis);
}

我什至不敢相信 java 只是将纳秒视为毫秒,如果超过 0.5 毫秒,而其他诸如 TimeUnit.sleep 或 Object.wait 之类的东西只需调用 thread.sleep 或转换为内部毫秒。那么这是否意味着在 windows 或 linux 或任何非实时操作系统中,没有办法精确睡眠?游戏引擎是如何做到的?

【问题讨论】:

  • 与其尝试针对特定的刷新率进行编程,不如让您的代码尽可能快地运行并传递帧。您应该关注的是确保底层游戏模型与时钟(而不是 fps)同步,请参阅此处了解更多信息:gamedev.stackexchange.com/a/43349 和其他阅读:gamedev.stackexchange.com/questions/1589/…
  • 您的下一个问题将是系统时钟本身。它通常仅准确 +/- 15 毫秒。你不需要这么精确。
  • 你的问题的答案是:你不能。睡眠是不精确的。正如您所指出的,这部分是因为 Linux 不是 RT 操作系统。但即使是这样,在图形应用程序中使用 sleep 通常也是错误的方法。
  • 改看swing Timer类(我觉得不是更精确,只是更合适)
  • @JFan - 你没抓住重点。请参阅我的评论。

标签: java


【解决方案1】:

你不需要自己睡觉。您应该尽可能快地运行您的代码。监视器将帮助您同步视频输出,您不必让代码等待监视器。

【讨论】:

    【解决方案2】:

    游戏引擎依赖 Delta 时间来使游戏帧速率独立,Delta 时间不过是上一帧和当前帧之间经过的时间。 您可以将增量时间测量为

    DeltaTime = 1/FPS

    因此,您可以使用此变量通过将其与您的游戏变量相乘来使游戏独立,例如,如果您想移动独立于帧速率的玩家,您可以执行类似的操作

    Player.position = Player.position + MovementSpeed * DeltaTime

    如果初始 position = 10 , MovementSpeed = 1 如果FPS = 60,DeltaTime = 0.016666667那么1秒后的距离是

    Player.position = 10 + 1 * (0.016666667*60) 是 '11' (这里它乘以 60 只是为了适应这样一个事实,即当游戏以 60 FPS 运行时,相同的计算将运行 60 次,因此 Player.position 将更新 0.016666666 60 次)

    如果 FPS = 30 , DeltaTime = 0.03333 则 1 秒后移动的距离为 Player.position = 10 + 1*(0.03333*30) 是“11” 所以游戏可以独立于帧率运行

    您可以尝试像这样计算 DeltaTime:

    //In main Game loop
    current time = getSystemTime()
    Delta time = current time - old time 
    old time = current time // save old time for next loop
    

    【讨论】:

    • 这很有帮助,毕竟我尝试使用 deltaTime 渲染 + selfspin 进行一些持续的逻辑更新以使其工作。但我还有另一个问题:如何实现 VSync 或限制帧速率或“笔记本电脑保存模式”没有自旋的精确睡眠?
    • 搜索双缓冲渲染以获取有关 Vsync 的更多信息。
    猜你喜欢
    • 2021-12-23
    • 1970-01-01
    • 2010-10-23
    • 1970-01-01
    • 2010-11-16
    • 2012-11-03
    • 2019-07-02
    • 2020-08-16
    相关资源
    最近更新 更多