Reputation: 9211
I need to plot animated frames in which each pixel is calculated on the fly as the result of an algorithm. Full screen animations may thus require many millions of operations per frame. I would like to achieve the highest refresh rate possible, preferably 20 to 30+ frames per second, if possible.
Can someone show me how to design/write a highly optimized architecture for extremely fast frame refreshment in java?
This needs to be platform independent, so I cannot take advantage of hardware acceleration. The code will be executed on each individual user's computer, NOT on a central server. Of course, I am separately approaching this from the standpoint of simplifying the algorithms for generating the pixel values within-each-frame, but this question is about architecture for high speed frame-by-frame refreshment between-frames, independent of the algorithm used to generate pixel values within each frame. For example, in answers to this posting, I am looking for methods such as using:
I wrote some sample code below to simulate the problem. At full screen on my notebook computer, the code below individually refreshes 1,300,000+ pixels per frame with unique values. This takes 500 milliseconds per frame on a machine with four processors and 8 gigabytes of memory. I suspect that I am not using BufferedImage correctly below, and I would really like to learn about other between-frame, architecture-level techniques for optimizing the code below, independent of the algorithms I will end up using to calculate pixel values within-each-frame. Your code samples and links to articles would be much appreciated.
How can I improve the code below, from an frame-by-frame (between-frames) architecture standpoint, not from a within-frame standpoint?
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);
}
}
}
}
Upvotes: 0
Views: 387
Reputation: 76
This is your new code. In my laptop, fullscreen can run at 150 frames/ second. At you can see, time execution of function drawRandomScreen only 1/2 time of drawImageTime.
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));
}
Upvotes: 1
Reputation: 205885
In the example given, drawRandomScreen()
executes on the event dispatch thread. As an alternative, let the model evolve on a separate thread (or threads) and sample the resulting image at a sustainable rate, say 25 Hz, using the observer pattern. Synchronize access to the shared image buffer. A complete example is examined here. Profile to verify ongoing optimization efforts.
Upvotes: 2
Reputation: 76
instead of interacting through Graphics2D you should interact direct with image data. This is my code, with my laptop can run with 20 frame/second (fullscreen)
@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));
}
}
Upvotes: 1