Results 1 to 8 of 8
  1. #1
    JavaRulez is offline Member
    Join Date
    May 2010
    Posts
    26
    Rep Power
    0

    Default Need help with synchronous painting

    I'm wary of posting now that I've read your forum rules which explicitly forbid cross-posting. I don't agree with that policy, but I need help and I'm not getting it anywhere else.

    If you'd like to see my question without "fracturing" the discussion, look here:
    Java 2D - Trouble with synchronous painting. Two SSCE's one AWT, one Swing

    If anyone wants to post help in this thread, I'll post a link to here in my other thread. I don't want to waste anyone's time, but I can't seem to locate where the experts are.

  2. #2
    toadaly is offline Senior Member
    Join Date
    Jan 2009
    Posts
    671
    Rep Power
    6

    Default

    I really don't feel like analyzing your code line by line to figure out what you're trying to do and why it works with one approach but not the other. What are you trying to accomplish, and why are you worried about the overhead of synchronization...are you runnign on a TRS-80?

  3. #3
    JavaRulez is offline Member
    Join Date
    May 2010
    Posts
    26
    Rep Power
    0

    Default

    So sorry, I guess some comments may have been more helpful? Would you like me to decorate the code with comments and repost it? (that might sound sarcastic but I am being sincere)

    I'm trying to ensure that changes in state are synchronized with the display. I know synchronizing isn't hurting my performance much, but I know it slows it down slightly. This isn't too critical, however being a perfectionist, I'll feel disappointed if I can't optimize this.

    The actual project is an API (or extension, I don't know which term is more appropriate) for developing 2D graphics games. The problem I foresee is that a person (The Player) is playing my game with less than desirable graphics acceleration. Lots of sprites on the screen could cause slow down in rendering. If the logic portion of the program moves a "monster" close to The Player, the player should react within one cycle. If however, the rendering is lagging behind, the player won't react (which is appropriate for their knowledge of the game state). Upon the next iteration, the "monster" will kill the player. At this point The Player may have been made aware of the previous game state (graphics is lagging) and react to the impending danger. Subsequently the player is made aware of dieing despite reacting appropriately. Clearly I could attempt to prevent players from playing the game if it detects that it is cycling too slowly, but I'd rather not. Old console games frequently slowed down when too many sprites were on the screen and the game was still perfectly playable.

    I had previously downloaded someone else's "game engine" and it suffered from this very problem (graphics and game state not synchronized). I revisited some of my previous work with AWT and Swing and noticed that my code too demonstrated these problems. This is why I developed the AWT algorithm that uses synchronization. After reading a tutorial on Painting in AWT and Swing located at sun's site I noticed a section describing this problem related to JScrollPane, it mentioned paintImmediately(); I'm trying to adapt my code to use this mechanism so that it will be single-threaded. You may notice that my AWT example uses 2 threads (what I wish to avoid).
    Last edited by JavaRulez; 05-07-2010 at 06:45 AM. Reason: added a bit more background

  4. #4
    toadaly is offline Senior Member
    Join Date
    Jan 2009
    Posts
    671
    Rep Power
    6

    Default

    Ok. "repaint" queues an asynchronous paint event, it does not cause an immediate update of the screen. I noticed you're using that along with SwingUtilities.invokeLater, which is also asynchronous. If you use 'paint' and 'paintImmediately' instead, you can make things much more responsive. A word of caution though, don't synchronize around swing calls. Swing is not thread safe and you will almost assuredly end up with a hard to track deadlock.

    If your goal is a realtime game, I'd consider something other than Java. If you want portability and realtime graphics, use OpenGL. If you don't care about portability and need even higher performance, use a platform specific optimized graphics engine such as DirectX on Windows, or X on linux.

    However, I've found that swing is more than acceptable in most cases. I write graphical programs in Java all the time that are much faster than a human eye can even see on moderate machines. It's easier to specify a minimum machine configuration than it is to try to make your program work on anything that anyone might have.

  5. #5
    JavaRulez is offline Member
    Join Date
    May 2010
    Posts
    26
    Rep Power
    0

    Default

    I hear what you are saying, but the ability to deploy this as a downloadable application, applet, or webstart application is what interests me most. The computer I'm developing this on uses Linux on an x86_64 processor, graphics hardware is onboard (nearly no acceleration). The games that would use this code shouldn't be processor intensive, yet the code is designed to be an abstraction, removing graphics from the game module with the added bonus of improving the appeal of the game by adding richer effects (full alpha vs bitmask, etc) on a system which has the power to provide it. I believe my code may have been a bit unreadable, and I apologize for not providing more comments. Following is the same code with liberal commenting applied:

    Java Code:
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class SynchronizedPainting {
    
      private static long last = System.currentTimeMillis();
      private static long logictimer = 50;
      private static boolean painted = false;
      private static Color bg_color = Color.black;
      private static PaintingPanel pp;
    
      public void openDisplay() {
        pp = new PaintingPanel();
        pp.addKeyListener(new InputHandler());
        JFrame frame = new JFrame("Synchronized Painting");
        JPanel panel = (JPanel) frame.getContentPane();
        panel.add(pp);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
      }
    
    //Our common synchronized method allows only the EDT or our main execution thread
    //to be in this method, not both
      public static synchronized void sync(boolean logic, Graphics g) {
    //  If we need to run logic but haven't, yet we have already painted at least once
    //  prevent EDT from entering our method
        if (painted && !logic && logictimer < 1) return;
    //  If we need to run logic but we haven't yet painted the last change of state to
    //  the display, force logic to wait until EDT has a chance to paint
        if (!painted && logic && logictimer < 1) {
    //    This call to repaint is asynchronous and may be coalesced, we simply want to assure that if
    //    previous calls were coalesced, we queue a new one so we don't wait forever
          pp.repaint();
          return;
        }
    //  Simple calculation of elapsed time since either thread has passed through this
    //  method
        long now = System.currentTimeMillis();
        long delta = now - last;
        last = now;
        logictimer -= delta;
    //  main() called this method with logic == true, following is the section of this
    //  method that will run for that thread
        if (logic) {
    //    50 milliseconds have passed, our logic should be cycling at as close to 20hz
    //    as possible
          if (logictimer < 0) {
            logictimer += 50;
    //      Once logic has occured the display is invalidated, signify that condition so
    //      that we may not proceed with the next logic iteration until the display
    //      reflects the change in state
            painted = false;
    //      ...     <- Put a call to some method which contains our logic here.
          } else pp.repaint();
    //             This call to repaint() allows smaller granularity for graphics vs
    //             logic.  Perhaps sprites animate at 30hz while logic runs at 20hz
    //
    //  paint() called this method with logic == false, following is the code that would
    //  normally appear in paint() but must be synchronized with logic
        } else {
          g.setColor(bg_color);
          g.fillRect(0, 0, 640, 480);
    //    By setting painted == true, we can assure that logic may run when it is time to
    //    do so, or if that time has already elapsed, logic can now pass our test at the
    //    beginning of this method and is not prevented from entering
          painted = true;
        }
      }
    
      private class PaintingPanel extends Canvas {
    
        public PaintingPanel() {
          super();
          this.setPreferredSize(new Dimension(640, 480));
        }
    
    //  Sloppy update override, force repainting completely
        public void update(Graphics g) {
          paint(g);
        }
    
    //  AWT paint() override, method body is technically located in our
    //  synchronized method.
        public void paint(Graphics g) {
          sync(false, g);
        }
    
      }
    
      private class InputHandler implements KeyListener {
    
        public void keyPressed(KeyEvent e) {
    //    With every key press we change the paint color.  If everything goes
    //    according to plan, there will be no latency for this state change to
    //    appear in the display
          if (bg_color == Color.black) bg_color = Color.white;
          else bg_color = Color.black;
        }
    
        public void keyReleased(KeyEvent e) {
        }
    
        public void keyTyped(KeyEvent e) {
        }
    
      }
      public static void main(String[] args) {
    //  Instantiate our class
        SynchronizedPainting sp = new SynchronizedPainting();
    //  Create the GUI
        sp.openDisplay();
    //  Continously loop (this thread runs concurrently with the EventDispatchThread
        while (true) {
    //    Enter our common synchronized method, signifying logic thread, and passing
    //    a null pointer (we won't use a reference for our logic thread)
          sync(true, null);
    //    Pause momentarily, to allow the EDT to process pending events
          try { Thread.sleep(1); } catch (Exception e) { }
        }
      }
    
    }
    I don't detect the possibility of dead-lock in the above code.

    Java Code:
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.*;
    
    class SynchronousPainting {
    
      private static PaintingPanel pp;
      private static long lastexecution = System.currentTimeMillis();
      private static long logictimer = 50;
      private static boolean painted = false;
      private static Color bg_color = Color.black;
    
      public void openDisplay() {
        pp = new PaintingPanel();
        pp.addKeyListener(new InputHandler());
        JFrame frame = new JFrame("Synchronous Painting [Failure]");
        JPanel panel = (JPanel) frame.getContentPane();
        panel.add(pp);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
      }
    
      private class PaintingPanel extends JPanel {
    
        public PaintingPanel() {
          super();
          this.setPreferredSize(new Dimension(640, 480));
        }
    
        public void paintComponent(Graphics g) {
          g.setColor(bg_color);
          g.fillRect(0, 0, 640, 480);
        }
    
    //  This method was scheduled with SwingUtilities.invokeLater() from main()
    //  and subsequently schedules successive execution before exiting.
        protected void executionHook() {
    //    calculate elapsed time, just as before
          long now = System.currentTimeMillis();
          long delta = now - lastexecution;
          lastexecution = now;
          logictimer -= delta;
    //    Run logic when it is time, but switch to painting if we haven't yet
          if (logictimer < 1 && painted) {
            logictimer += 50;
    //      State has changed, we must paint before we can enter this block again
            painted = false;
          } else {
    //      If it is not time for logic or we have yet to paint, paint
    //      according to the documentation, this should execute synchronously,
    //      performing our paint operation (including a call to paintComponent())
            paintImmediately(0, 0, 640, 480);
            painted = true;
          }
    //    Schedule this method to run again, possibly immediately, or possibly after the
    //    EDT has dispatched keyEvents
          SwingUtilities.invokeLater(new Runnable() {
            public void run() {
              pp.executionHook();
            }
          });
        }
    
      }
    
      private class InputHandler implements KeyListener {
    
        public void keyPressed(KeyEvent e) {
          if (bg_color == Color.black) bg_color = Color.white;
          else bg_color = Color.black;
        }
    
        public void keyReleased(KeyEvent e) {
        }
    
        public void keyTyped(KeyEvent e) {
        }
    
      }
    
      public static void main(String[] args) {
    // Pushing all our initialization onto the EDT.  The EDT guarantees these invocations will be
    // executed serially, this simply allows this thread to exit quickly and cleanly and leaves
    // all code running in one thread, the EDT.
        SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            new SynchronousPainting().openDisplay();
            SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                pp.executionHook();
              }
            });
          }
        });
      }
    
    }
    And that code example is single threaded. Clearly I've made a mistake somewhere in the second example, because my understanding of the documentation leads me to believe that the second example should produce an identical result as the first, yet it does not.
    Last edited by JavaRulez; 05-07-2010 at 08:12 AM. Reason: ""

  6. #6
    JavaRulez is offline Member
    Join Date
    May 2010
    Posts
    26
    Rep Power
    0

    Default

    PS I appreciate your help. I as well have been programming since I was quite young, started in BASIC, then Pascal, C, C++, and Java since around '99. I'm not old enough to remember a TRS-80, but I did have an 8086.

  7. #7
    toadaly is offline Senior Member
    Join Date
    Jan 2009
    Posts
    671
    Rep Power
    6

    Default

    Your first listing has a potential deadlock condition, and it's almost certain it will lock up from time to time. You are calling several swing methods within a synchronized method (sync), and that method is itself called from another method overriding a swing call (paint).

  8. #8
    JavaRulez is offline Member
    Join Date
    May 2010
    Posts
    26
    Rep Power
    0

    Default

    I still don't see it. As far as I know, repaint(), Graphics.setColor(), and Graphics.fillRect() are not Swing methods. Also, is not paint() more of an AWT method? The lock is associated with my main class, not the components, and neither thread will ever try to acquire another lock, so they have nothing to wait on. I still don't see how calling repaint() could cause dead-lock. Either way, it doesn't matter as I don't want to use that example, I want to get the second one to work, and since it is single-threaded, the possibility of dead-lock disappears if I can get it to work.

Similar Threads

  1. Images don't painting
    By daniel.henrique in forum New To Java
    Replies: 2
    Last Post: 02-12-2010, 11:48 AM
  2. Objects painting themselves?
    By martypapa in forum Java 2D
    Replies: 19
    Last Post: 02-06-2010, 04:08 AM
  3. painting problem
    By hannes in forum New To Java
    Replies: 3
    Last Post: 01-17-2010, 11:44 AM
  4. JPanel not always painting everything
    By ekted in forum AWT / Swing
    Replies: 0
    Last Post: 11-26-2009, 11:24 AM
  5. Jpanel painting problem
    By kcakir in forum AWT / Swing
    Replies: 3
    Last Post: 04-15-2009, 10:21 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •