我需要绘制动画帧,其中每个像素都是动态计算的算法结果。因此,全屏动画可能需要每帧数百万次操作。我希望尽可能达到最高的刷新率,最好是每秒20到30+帧。
有人能告诉我如何设计/编写高度优化的架构,以便在Java中实现极快的帧刷新吗?
这需要与平台无关,因此我无法利用硬件加速。代码将在每个用户的计算机上执行,而不是在中央服务器上执行。当然,从简化用于在每帧内生成像素值的算法的角度来看,我正在分别接近这一点,但是这个问题是关于高速逐帧刷新的架构帧间,独立于用于在每个帧内生成像素值的算法。例如,在此帖子的答案中,我正在寻找使用方法:
我在下面写了一些示例代码来模拟问题。在我的笔记本电脑上全屏显示,下面的代码使用唯一值单独刷新每帧1,300,000+像素。在具有四个处理器和8千兆字节内存的计算机上,每帧需要500毫秒。我怀疑我没有在下面正确使用BufferedImage,我真的想了解其他帧间,用于优化下面代码的架构级技术,独立于我最终将使用的算法计算每帧内的像素值。您的代码示例和文章链接将非常感激。
如何从逐帧(帧间)架构的角度改进下面的代码,而不是从帧内角度来看?
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class TestBuffer {
private static void createAndShowUI() {
TestPanel fastGraphicsPanel = new TestPanel();
JFrame frame = new JFrame("This Needs A Faster Architecture!");
frame.getContentPane().add(fastGraphicsPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(800,600));
frame.setResizable(true);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {createAndShowUI();}});}
}
@SuppressWarnings("serial")
class TestPanel extends JPanel {
int w, h;
private static int WIDTH = 700;
private static int HEIGHT = 500;
private static final Color BACKGROUND_COLOR = Color.white;
private BufferedImage bImg;
private Color color = Color.black;
public TestPanel() {
bImg = new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_INT_RGB);
Graphics g = bImg.getGraphics();
g.setColor(BACKGROUND_COLOR);
g.fillRect(0, 0, WIDTH, HEIGHT);
Timer myTimer = new Timer(10, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(w!=0&&h!=0){
if(WIDTH!=w&&HEIGHT!=h){
WIDTH = w; HEIGHT = h;
bImg = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
}
}
repaint();
}
});
myTimer.setInitialDelay(0);
myTimer.setRepeats(true);
myTimer.setCoalesce(true);
myTimer.start();
g.dispose();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
w = getWidth();
h = getHeight();
// System.out.println("w, h are: "+w+", "+h);
long startTime = System.currentTimeMillis();
g.drawImage(bImg, 0, 0, null);
long endDrawImageTime = System.currentTimeMillis();
Graphics2D g2 = (Graphics2D) g;
drawRandomScreen(g2);
long endDrawScreenTime = System.currentTimeMillis();
long stopTime = System.currentTimeMillis();
long drawImageTime = endDrawImageTime - startTime;
long drawScreenTime = endDrawScreenTime - endDrawImageTime;
long elapsedTime = stopTime - startTime;
System.out.println(drawImageTime+", "+drawScreenTime+", "+elapsedTime);
}
private void drawRandomScreen(Graphics2D g2) {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for(int i=0;i<WIDTH;i++){
for(int j=0;j<HEIGHT;j++){
color = new Color((int) (Math.random() * 255),(int) (Math.random() * 255),(int) (Math.random() * 255));
g2.setColor(color);
g2.drawLine(i, j, i, j);
}
}
}
}
答案 0 :(得分:2)
在给出的示例中,drawRandomScreen()
在事件派发线程上执行。作为替代方案,让模型使用observer pattern以可持续的速率(例如25 Hz)在单独的线程(或线程)和示例上进化。同步对共享映像缓冲区的访问。检查完整的示例here。 Profile验证正在进行的优化工作。
答案 1 :(得分:1)
而不是通过Graphics2D进行交互,您应该直接与图像数据进行交互。这是我的代码,我的笔记本电脑可以20帧/秒(全屏)运行
@SuppressWarnings("serial")
class TestPanel extends JPanel {
int w, h;
private static int WIDTH = 700;
private static int HEIGHT = 500;
private static final Color BACKGROUND_COLOR = Color.white;
private BufferedImage bImg;
private Color color = Color.black;
public TestPanel() {
bImg = new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_INT_RGB);
Graphics g = bImg.getGraphics();
g.setColor(BACKGROUND_COLOR);
g.fillRect(0, 0, WIDTH, HEIGHT);
Timer myTimer = new Timer(10, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(w!=0&&h!=0){
if(WIDTH!=w&&HEIGHT!=h){
WIDTH = w; HEIGHT = h;
System.out.println("create");
bImg = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
}
}
repaint();
}
});
myTimer.setInitialDelay(0);
myTimer.setRepeats(true);
myTimer.setCoalesce(true);
myTimer.start();
g.dispose();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
w = getWidth();
h = getHeight();
// System.out.println("w, h are: "+w+", "+h);
long startTime = System.currentTimeMillis();
long endDrawImageTime = System.currentTimeMillis();
// Graphics2D g2 = (Graphics2D) g;
drawRandomScreen(bImg);
g.drawImage(bImg, 0, 0, null);
long endDrawScreenTime = System.currentTimeMillis();
long stopTime = System.currentTimeMillis();
long drawImageTime = endDrawImageTime - startTime;
long drawScreenTime = endDrawScreenTime - endDrawImageTime;
long elapsedTime = stopTime - startTime;
System.out.println(drawImageTime+", "+drawScreenTime+", "+elapsedTime);
}
private void drawRandomScreen(BufferedImage image) {
final int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
final int width = image.getWidth();
final int height = image.getHeight();
long startTime = System.currentTimeMillis();
// g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
// RenderingHints.VALUE_ANTIALIAS_ON);
Random r = new Random();
for(int i=0;i<width;i++){
for(int j=0;j<height;j++){
color = new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255));
int pos = j*width+i;
pixels[pos] = color.hashCode();
}
}
long stopTime = System.currentTimeMillis();
System.out.println("time "+(stopTime-startTime));
}
}
答案 2 :(得分:1)
这是您的新代码。在我的笔记本电脑中,全屏可以150帧/秒的速度运行。你可以看到,drawRandomScreen函数的执行时间只有drawImageTime的1/2时间。
private void drawRandomScreen(BufferedImage image) {
final int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
final int width = image.getWidth();
final int height = image.getHeight();
long startTime = System.currentTimeMillis();
Random r = new Random();
int size = pixels.length;
//swap 100 times
int maxPixelsSwap = 1000;
size-=maxPixelsSwap;
for (int i = 0; i < 100; i++) {
int src = r.nextInt(size);
int des = src+r.nextInt(size-src);
int swapsize = r.nextInt(maxPixelsSwap); //maximium
int[] temp = new int[swapsize];
System.arraycopy(pixels, des, temp, 0, swapsize);
System.arraycopy(pixels, src, pixels, des, swapsize);
System.arraycopy(temp, 0, pixels, src, swapsize);
}
size = pixels.length;
int randomTimes = size/10; //only change 10% of pixels
size--;
for (int i = 0; i < randomTimes; i++) {
pixels[r.nextInt(size)]=r.nextInt();
}
long stopTime = System.currentTimeMillis();
System.out.println("time "+(stopTime-startTime));
}