java—如何在不等待线程完成的情况下精确渲染帧

slmsl1lt  于 2021-07-03  发布在  Java
关注(0)|答案(1)|浏览(334)

我有一个java应用程序,我正在加载3个gif文件到BuffereImage数组中,并希望显示它们。
我有一个简单的线程,使用thread.sleep()每16毫秒(约60帧/秒)执行一次
在这个方法中,我调用paintcomponent方法来绘制gui组件。但是我不希望线程(runnable)等待方法结束渲染,因为延迟超过16ms,即使它是不可测量的。
下面是我的runnable jlabel(目前是快速/高效的方法)中的一个片段

@Override
public void run() {
    while (active) {
        repaint(); //Takes 0ms
        i++; // Counter used inside paintComponent()
        Thread.sleep(speed * multiplier));
        if (i + 1 == frames.length) {
            i = 0;
        }
    }
}

@Override
public void paintComponent(Graphics g) {
    g.drawImage(frames[i], 0, 0, x, y, observerFrame);
    g.dispose();
}

我测试过了,重新上漆的方法需要0毫秒。可能无法测量。但这还不够精确吗?或者效率低下?
所以(简单的)数学是在线程内部完成的。但我如何确保时间的精确性呢?我的意思是它仍然足够精确,但它仍然取决于电脑的速度
在回答@reto h之后ö我做了一些研究,尝试了另一种方法。但在渲染图像时仍然效率低下且滞后(顺便说一句,BuffereImage):
这是我做的一个很大的尝试,我认为这样会更好:

Timer timer = new Timer(16, this);

Executor executor = Executors.newCachedThreadPool(); 

//called by executor
@Override
public void run() {
    i++; // Counter used inside paintComponent()
    Thread.sleep(speed * multiplier));
    if (i + 1 == frames.length) {
        i = 0;
    }
}

//called by timer & runs on EDT
@Override
public void actionPerformed(ActionEvent e) {
    repaint();
    executor.execute(this); //Costs no time and has enought time to complete
}

@Override
public void paintComponent(Graphics g) {
    g.drawImage(frames[i], 0, 0, x, y, observerFrame);
    g.dispose();
}

所以这样我保证
重新上漆是在edt中完成的
数学是由另一个线程使用执行器完成的
即使我在编译和运行之后感到自豪,结果还是让我哭了。。。
对于第一种方法,jvm使用大约10%的cpu。一切都很好,在fps上运行的所有3帧。
第二次尝试使用较少的cpu,但性能较差。我不知道为什么,但图形用户界面是响应,但渲染速度慢了近3倍。
目标是以每秒60帧的速度渲染图像而不延迟。
这里的重点是,我尝试了一个尝试,我认为这是更好的概念,但它做得更糟。
所以我的问题是,是否有更好更精确的方法来实现这一点。否则,我将继续使用简单的基于线程的解决方案。
问候nur1

szqfcxe2

szqfcxe21#

首先,稍微了解一下java事件调度线程(或awt事件队列)的概念可能会有所帮助。
我的建议是使用一个只负责调用 .repaint() -方法。考虑使用 javax.swing.Timer 为此,它已经在幕后使用了一个线程,并且能够在多个事件到达太快时合并它们。
然后使用另一个只负责计算绘制所需信息的线程。一种方法是渲染到缓冲区图像。这个 .paint() -或者 .paintComponent() -显示组件的方法只需绘制该图像。

相关问题