a tight game loop in Swing

  • Follow


  For many years I often have wondered, what the Swing equivalence
  of a classic tight game loop might be. What I am thinking of is:

      as fast as possible, repeat this:
        if there is user input pending, then handle it
        update the state to the next generation and redraw it
        end repeat

  (redraws might be omitted when know to be invisble due to a
  limited screen refresh rate, this is the meaning of
  �possiblyRepaint�, below.)

  Now I have heard that often 1 ms timer events are recommended
  with the idea that Swing will coalesc multiple timer events into
  a single one and that they have lower priority than user inputs.
  (Theoretically, there also is the possibility that 1 ms is to
  slow, so that the program might become idle.)

  Recently, I had another idea of:

      public class NextGeneration implements java.lang.Runnable
      { public void run()
        { calculateNextGeneration();
          possiblyRepaint();
          invokeLater( this ); }}

  . I am hoping that the recursion via �invokeLater( this )�
  will effectively give me a loop (but not eat stack, since
  it's not a real recursion) and at the same time user input
  events IIRC have a higher priority than invokeLater events,
  so that they will still be handled. What do you think about
  such an �invokeLater� loop?

  
0
Reply ram (2829) 6/4/2012 12:31:06 PM

On 6/4/12 5:31 AM, Stefan Ram wrote:
>    For many years I often have wondered, what the Swing equivalence
>    of a classic tight game loop might be. What I am thinking of is:
>
>        as fast as possible, repeat this:
>          if there is user input pending, then handle it
>          update the state to the next generation and redraw it
>          end repeat
>
>    (redraws might be omitted when know to be invisble due to a
>    limited screen refresh rate, this is the meaning of
>    �possiblyRepaint�, below.)
>
>    Now I have heard that often 1 ms timer events are recommended
>    with the idea that Swing will coalesc multiple timer events into
>    a single one and that they have lower priority than user inputs.
>    (Theoretically, there also is the possibility that 1 ms is to
>    slow, so that the program might become idle.)
>
>    Recently, I had another idea of:
>
>        public class NextGeneration implements java.lang.Runnable
>        { public void run()
>          { calculateNextGeneration();
>            possiblyRepaint();
>            invokeLater( this ); }}
>
>    . I am hoping that the recursion via �invokeLater( this )�
>    will effectively give me a loop (but not eat stack, since
>    it's not a real recursion) and at the same time user input
>    events IIRC have a higher priority than invokeLater events,
>    so that they will still be handled. What do you think about
>    such an �invokeLater� loop?
>
>
AWT (not swing) has a method called "Exclusive Mode" which can bypass 
the event-queue and you actively render your game. AFAIK it's the only 
way you can access full screen mode, but I don't think full-screen was 
required.

A quick search for java exclusive mode brought me to this link:
<http://docs.oracle.com/javase/tutorial/extra/fullscreen/index.html>

HTH,
Daniel.
0
Reply newsgroup.nospam (532) 6/4/2012 2:34:12 PM


In article <tight-game-loop-20120604142027@ram.dialup.fu-berlin.de>,
 ram@zedat.fu-berlin.de (Stefan Ram) wrote:

> For many years I often have wondered, what the Swing equivalence of 
> a classic tight game loop might be. What I am thinking of is:
> 
>       as fast as possible, repeat this:
>         if there is user input pending, then handle it
>         update the state to the next generation and redraw it
>         end repeat
> 
> (redraws might be omitted when know to be invisble due to a
> limited screen refresh rate, this is the meaning of
> »possiblyRepaint«, below.)
> 
> Now I have heard that often 1 ms timer events are recommended
> with the idea that Swing will coalesc multiple timer events into
> a single one and that they have lower priority than user inputs.
> (Theoretically, there also is the possibility that 1 ms is to
> slow, so that the program might become idle.)

The available resolution varies by platform:

<http://mindprod.com/jgloss/time.html#ACCURACY>

>   Recently, I had another idea of:
> 
>       public class NextGeneration implements java.lang.Runnable
>       { public void run()
>         { calculateNextGeneration();
>           possiblyRepaint();
>           invokeLater( this ); }}
> 
> I am hoping that the recursion via »invokeLater( this )«
> will effectively give me a loop (but not eat stack, since
> it's not a real recursion) and at the same time user input
> events IIRC have a higher priority than invokeLater events,
> so that they will still be handled. What do you think about
> such an »invokeLater« loop?

This reminds of an example adduced by Knute Johnson:

<https://groups.google.com/d/msg/comp.lang.java.gui/aBy_DZFvg2M/-T9aWOwBM-QJ>

-- 
John B. Matthews
trashgod at gmail dot com
<http://sites.google.com/site/drjohnbmatthews>
0
Reply nospam59 (9800) 6/4/2012 3:01:26 PM

John B. Matthews wrote:
> This reminds of an example adduced by Knute Johnson:
> 
> <https://groups.google.com/d/msg/comp.lang.java.gui/aBy_DZFvg2M/-T9aWOwBM-QJ>

Ten points for using the word "adduced".

There are some EDT violations in the cited code's 'main()' routine.

-- 
Lew
0
Reply lewbloch (1312) 6/4/2012 11:39:54 PM

On 6/4/2012 4:39 PM, Lew wrote:
> John B. Matthews wrote:
>> This reminds of an example adduced by Knute Johnson:
>>
>> <https://groups.google.com/d/msg/comp.lang.java.gui/aBy_DZFvg2M/-T9aWOwBM-QJ>
>
> Ten points for using the word "adduced".
>
> There are some EDT violations in the cited code's 'main()' routine.
>

Here's the updated code with that fixed.

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.lang.reflect.*;
import javax.swing.*;

public class test3 extends JPanel implements Runnable {
     volatile BufferedImage bi;
     volatile long then;
     long now,time;
     final Thread thread;
     double angle,rate;
     int n;

     public test3() {
         super(false);
         setPreferredSize(new Dimension(400,300));

         thread = new Thread(this);

         addComponentListener(new ComponentAdapter() {
             public void componentResized(ComponentEvent ce) {
                 GraphicsConfiguration gc = getGraphicsConfiguration();
                 bi = gc.createCompatibleImage(getWidth(),getHeight());
             }
         });
     }

     public void start() {
         then = System.nanoTime();
         thread.start();
     }

     public void stop() {
         thread.interrupt();
     }

     public void run() {
         try {
             long now = 0;
             long then = System.nanoTime();

             while (true) {
                 render();
                 try {
                     EventQueue.invokeAndWait(new Runnable() {
                         public void run() {
                             paintImmediately(getBounds());
                         }
                     });
                 } catch (InvocationTargetException ite) {
                     System.out.println(ite);
                 }

                 /*
                 while (now < then + 10000000)
                     now = System.nanoTime();
                 then = now;
                 */
             }
         } catch (InterruptedException ie) {
             System.out.println(ie);
         }
     }

     public void render() {
         int w = getWidth();
         int h = getHeight();

         Graphics2D g = bi.createGraphics();
         g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
          RenderingHints.VALUE_ANTIALIAS_ON);

         g.setColor(Color.WHITE);
         g.fillRect(0,0,w,h);

         if (++n % 100 == 0) {
             now = System.nanoTime();
             time = now - then;
             then = now;
             rate = 100000000000.0 / time;
         }
         g.setColor(Color.RED);
         g.drawString(String.format("%5.1f",rate),10,12);

         angle += 0.001;
         g.rotate(angle,w/2,h/2);
         g.setColor(Color.BLUE);
         g.fillRect(w/2 - 100,h/2 - 100,200,200);

         g.dispose();
     }

     public void paintComponent(Graphics g) {
         g.drawImage(bi,0,0,null);
     }

     public static void main(String[] args) {
         EventQueue.invokeLater(new Runnable() {
             public void run() {
                 final test3 t3 = new test3();
                 final JFrame f = new JFrame();
                 f.addWindowListener(new WindowAdapter() {
                     public void windowOpened(WindowEvent we) {
                         t3.start();
                     }
                     public void windowClosing(WindowEvent we) {
                         t3.stop();
                         f.dispose();
                     }
                 });
                 f.add(t3,BorderLayout.CENTER);
                 f.pack();
                 f.setVisible(true);
             }
         });

     }
}



0
Reply nospam8071 (917) 6/6/2012 4:51:11 AM

Knute Johnson wrote:
> Lew wrote:
>> John B. Matthews wrote:
>>> This reminds of an example adduced by Knute Johnson:
>>>
>>> <https://groups.google.com/d/msg/comp.lang.java.gui/aBy_DZFvg2M/-T9aWOwBM-QJ>
>>
>> Ten points for using the word "adduced".
>>
>> There are some EDT violations in the cited code's 'main()' routine.
>>
>
> Here's the updated code with that fixed.
>
> import java.awt.*;
> import java.awt.event.*;
> import java.awt.geom.*;
> import java.awt.image.*;
> import java.lang.reflect.*;
> import javax.swing.*;
>
> public class test3 extends JPanel implements Runnable {
>     volatile BufferedImage bi;
>     volatile long then;

I am curious what motivated the choice of 'volatile'.

In my own case I will sometimes speculatively use 'volatile' to mark fields 
not essential to state despite that the class does not implement 
'Serializable'. While this violates the rigid rule prohibiting superfluity, I 
aver that the marker aids reasoning about the state in such cases.

This doesn't seem to be that.

-- 
Lew
Honi soit qui mal y pense.
http://upload.wikimedia.org/wikipedia/commons/c/cf/Friz.jpg
0
Reply noone7 (3512) 6/7/2012 6:58:37 AM

On 06/05/2012 09:51 PM, Knute Johnson wrote:
>    public void run() {
>          try {
>              long now = 0;
>              long then = System.nanoTime();
>
>              while (true) {
>                  render();
>                  try {
>                      EventQueue.invokeAndWait(new Runnable() {
>                          public void run() {
>                              paintImmediately(getBounds());
>                          }
>                      });
>                  } catch (InvocationTargetException ite) {
>                      System.out.println(ite);
>                  }
>
>                  /*
>                  while (now < then + 10000000)
>                      now = System.nanoTime();
>                  then = now;
>                  */
>              }
>          } catch (InterruptedException ie) {
>              System.out.println(ie);
>          }
>      }
>

How about this formulation?

   @Override
   public void run() {
     for (boolean active = true; active; ) {
       try {
         render();
         try {
           EventQueue.invokeAndWait(new Runnable() {
               public void run() {
                 paintImmediately(getBounds());
               }
             });
         } catch (InvocationTargetException ite) {
           System.out.println(ite);
         }

         /*
         for (long pause = System.nanoTime() + 10000000L;
              System.nanoTime() < pause;
              ) {
         }
         */
       } catch (InterruptedException ie) {
         System.out.println(ie);
         active = false;
       }
     }
   }

I base this on vague, hence possibly superstitiously encoded memories of 
Goetz's (et al.) _Java Concurrency in Practice_ and other sources.

He goes to some length to re-educate about 'InterruptedException'. I may have 
got it wrong, but I seem to recall something about setting a loop condition 
rather than leaping out pell-mell, and a tickle about a rethrow of the 
interruption but that might be for specialized circumstances. Like when the 
interruptee isn't the end of the line for handling that interrupt as it is here.

I read a book. I'm not an authority.

I loathe 'System.out' for logging. OTOH, in this case the point of the program 
arguably is the exception output, so it's legit, right?

-- 
Lew
Honi soit qui mal y pense.
http://upload.wikimedia.org/wikipedia/commons/c/cf/Friz.jpg
0
Reply noone7 (3512) 6/7/2012 7:36:06 AM

On 6/6/12 11:58 PM, Lew wrote:
> Knute Johnson wrote:
>> Lew wrote:
>>> John B. Matthews wrote:
>>>> This reminds of an example adduced by Knute Johnson:
>>>>
>>>> <https://groups.google.com/d/msg/comp.lang.java.gui/aBy_DZFvg2M/-T9aWOwBM-QJ>
>>>>
>>>
>>> Ten points for using the word "adduced".
>>>
>>> There are some EDT violations in the cited code's 'main()' routine.
>>>
>>
>> Here's the updated code with that fixed.
>>
>> import java.awt.*;
>> import java.awt.event.*;
>> import java.awt.geom.*;
>> import java.awt.image.*;
>> import java.lang.reflect.*;
>> import javax.swing.*;
>>
>> public class test3 extends JPanel implements Runnable {
>> volatile BufferedImage bi;
>> volatile long then;
>
> I am curious what motivated the choice of 'volatile'.
>
> In my own case I will sometimes speculatively use 'volatile' to mark
> fields not essential to state despite that the class does not implement
> 'Serializable'. While this violates the rigid rule prohibiting
> superfluity, I aver that the marker aids reasoning about the state in
> such cases.
>
> This doesn't seem to be that.
>
Volatile has nothing to do with Serializable. Perhaps you're having a 
moment of confusing volatile with transient?  Volatile is necessary when 
you want to force a happens-before relationship to reads/writes to a 
field.  It also guarantees the you won't have a situation where writes 
aren't flushed to main memory before the next attempted read.

In other words, it is one safe way to publish a value across threads.
0
Reply newsgroup.nospam (532) 6/7/2012 4:20:55 PM

Daniel Pitts wrote:
> On 6/6/12 11:58 PM, Lew wrote:
> > I am curious what motivated the choice of 'volatile'.
> >
> > In my own case I will sometimes speculatively use 'volatile' to mark
> > fields not essential to state despite that the class does not implement
> > 'Serializable'. While this violates the rigid rule prohibiting
> > superfluity, I aver that the marker aids reasoning about the state in
> > such cases.
> >
> > This doesn't seem to be that.
> >
> Volatile has nothing to do with Serializable. Perhaps you're having a 
> moment of confusing volatile with transient?  Volatile is necessary when 
> you want to force a happens-before relationship to reads/writes to a 
> field.  It also guarantees the you won't have a situation where writes 
> aren't flushed to main memory before the next attempted read.
> 
> In other words, it is one safe way to publish a value across threads.

Duhy. My foolishness.

-- 
Lew

0
Reply lewbloch (1312) 6/8/2012 7:28:52 PM

8 Replies
69 Views

(page loaded in 0.12 seconds)

Similiar Articles:








7/23/2012 6:01:33 AM


Reply: