Results 1 to 8 of 8
- 05-07-2010, 05:24 AM #1
Member
- Join Date
- May 2010
- Posts
- 26
- Rep Power
- 0
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.
- 05-07-2010, 06:29 AM #2
Senior Member
- Join Date
- Jan 2009
- Posts
- 671
- Rep Power
- 5
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?
- 05-07-2010, 06:40 AM #3
Member
- Join Date
- May 2010
- Posts
- 26
- Rep Power
- 0
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
- 05-07-2010, 07:28 AM #4
Senior Member
- Join Date
- Jan 2009
- Posts
- 671
- Rep Power
- 5
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.
- 05-07-2010, 08:08 AM #5
Member
- Join Date
- May 2010
- Posts
- 26
- Rep Power
- 0
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:
I don't detect the possibility of dead-lock in the above code.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) { } } } }
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.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(); } }); } }); } }Last edited by JavaRulez; 05-07-2010 at 08:12 AM. Reason: ""
- 05-07-2010, 08:42 AM #6
Member
- Join Date
- May 2010
- Posts
- 26
- Rep Power
- 0
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.
- 05-08-2010, 05:30 AM #7
Senior Member
- Join Date
- Jan 2009
- Posts
- 671
- Rep Power
- 5
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).
- 05-09-2010, 08:34 AM #8
Member
- Join Date
- May 2010
- Posts
- 26
- Rep Power
- 0
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
-
Images don't painting
By daniel.henrique in forum New To JavaReplies: 2Last Post: 02-12-2010, 11:48 AM -
Objects painting themselves?
By martypapa in forum Java 2DReplies: 19Last Post: 02-06-2010, 04:08 AM -
painting problem
By hannes in forum New To JavaReplies: 3Last Post: 01-17-2010, 11:44 AM -
JPanel not always painting everything
By ekted in forum AWT / SwingReplies: 0Last Post: 11-26-2009, 11:24 AM -
Jpanel painting problem
By kcakir in forum AWT / SwingReplies: 3Last Post: 04-15-2009, 10:21 PM


LinkBack URL
About LinkBacks
Reply With Quote
Bookmarks