[Java]animatie schokt afhankelijk van muislocatie/platform *

Pagina: 1
Acties:
  • 129 views sinds 30-01-2008
  • Reageer

  • Remenic
  • Registratie: Juni 2001
  • Laatst online: 12-12-2025
Ik heb een probleem met JAVA. Ik ben bezig met een project voor school, waarin wij een animatie moeten tekenen. Het viel mij op een gegeven moment op dat de animatie alles behalve soepel liep op twee Linux laptops in onze project groep. De snelste machine van de twee is een 1GHz duron. Ik heb hetzelfde programma daarna onder Windows geprobeerd, op een 366MHz (niet lachen) laptop. Wat mij opviel was dat dat animatie een stuk vloeiender en strakker verliep dan in Windows. Zelfs in vergelijking met Linux op een 1GHz systeem!

Ik heb daarna thuis nog eens onderzocht wat de oorzaak precies was. Ik kwam er achter dat wanneer ik met mijn muis in het venster beweeg, de animatie ineens soepel loopt (net zo soepel als in Windows). Het programma registreert overigens geen mouse movements.

Ik heb een klein test programma gemaakt, zodat jullie zelf kunnen zien wat ik bedoel.

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import java.awt.*;
import javax.swing.JFrame;

/**
 * Draws a bouncing line
 */
class DrawSurface extends Component implements Runnable
{
  double y;
  double angle;
  
  public DrawSurface()
  {
    new Thread(this).start();
  }

  public void paint(Graphics g)
  {
    g.drawRect(1,(int)(getHeight()-y*getHeight()),100,1);
    g.drawOval(  5, 50, 80,80 );
  }
  
  public void run()
  {
    while ( true )
    {
      try {
        Thread.sleep(30);
      }
      catch (InterruptedException ex) {
        System.err.println("beetje vervelend");
      }
      
      if ( angle++ > 180 ) angle = 0;
      y = Math.sin( Math.toRadians(angle) );
      
      repaint();
    }
  }
}

/**
 * Main Class
 */
class TheApp
    extends JFrame
{
  DrawSurface draw = new DrawSurface();

  public TheApp() throws HeadlessException
  {
    try {
      jbInit();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
    setBounds(100, 100, 100, 200);
  }
  
  public static void main(String[] args) throws HeadlessException
  {
    TheApp theApp1 = new TheApp();
    theApp1.show();
  }

  private void jbInit() throws Exception
  {
    this.setDefaultCloseOperation(EXIT_ON_CLOSE);
    this.getContentPane().add(draw, BorderLayout.CENTER);
  }
}

Compileren met:
code:
1
javac TheApp.java

Runnen met:
code:
1
java TheApp

Laat de animatie eerst even lopen zonder met je muis te bewegen. Volg daarna langzaam met je muis de lijn van de cirkel in het midden van het venster. Let nu ondertussen weer op de animatie.

Wanneer de muis met rust gelaten wordt, lijkt het redrawen met variabele intervallen te gaan. Ik heb met getTime() (niet het meest nauwkeurige nee) gecontroleerd hoeveel tijd elke frame (vanaf de sleep()) er over doet, maar de waarden zijn vrij stabiel (scheelt af en toe een miliseconde of 2).

Kan iemand vertellen waarom de animatie zo slecht loopt, tenzij er muis activiteit plaatsvindt? Het lijkt mij alsof de mainloop van JAVA erg onnauwkeurig is ofzo.

[ Voor 3% gewijzigd door Remenic op 26-05-2004 23:17 ]


  • Zwerver
  • Registratie: Februari 2001
  • Niet online

Woonachtig Down Under. Ik negeer je insults niet, maar tegen de tijd dat ik ze lees zijn ze meestal niet relevant meer


  • NMe
  • Registratie: Februari 2004
  • Laatst online: 24-05 14:53

NMe

Quia Ego Sic Dico.

Op hoeveel/welke Linux versies en window managers heb je die code geprobeerd? Ik denk dat dat wel handige info is om erbij te vermelden. :)

Ik weet niet geweldig veel van Linux, maar ik vermoed dat je window manager door de beweging van de muis vaker de inhoud van je scherm overschrijft met de buffer, maar dat is pure speculatie. :) Ik zou het in ieder geval eens met een andere window manager proberen.

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 12:00

Janoz

Moderator Devschuur®

!litemod

Probeer de gettime niet bij de sleep maar bij de paint te doen. Of eventueel een paar extra system.out.println toevoegen. Ik heb het vermoeden namelijk dat door met de muis over het scherm te bewegen een redraw ook enkele keren binnen die 20ms wordt aangeroepen. Niet omdat de animatie verplaatst is, maar omdat de muiscursor opnieuw getekend moet worden.


---------

Hmm.. slecht gelezen * Janoz neemt meer koffie. Maar misschien is het toch handig om dit even te doen om te kijken op welke manier alles getriggered wordt (Misschien is thread.sleep in linux minder nauwkeurig? )

[ Voor 24% gewijzigd door Janoz op 27-05-2004 12:21 ]

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-05 23:07

.oisyn

Moderator Devschuur®

Demotivational Speaker

Janoz: nou heb ik niet zoveel verstand van linux' windowmanagers, maar ze zijn wel echt volslagen idioot als ze voor elke mousemove een repaint gaan aanroepen om dat wat achter de cursor zat te hertekenen :). Over het algemeen houdt het muissysteem zelf een buffertje bij van wat onder de cursor zat voor de cursor daar stond, en bij verplaatsing wordt dat stukje gewoon opnieuw getekend. Bij een tekenactie die plaats vindt onder de cursor wordt de cursor eerst uitgeschakeld, getekend, de buffer geupdate en dan weer ingeschakeld.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

.oisyn schreef op 27 mei 2004 @ 12:52:
Janoz: nou heb ik niet zoveel verstand van linux' windowmanagers, maar ze zijn wel echt volslagen idioot als ze voor elke mousemove een repaint gaan aanroepen om dat wat achter de cursor zat te hertekenen :). Over het algemeen houdt het muissysteem zelf een buffertje bij van wat onder de cursor zat voor de cursor daar stond, en bij verplaatsing wordt dat stukje gewoon opnieuw getekend. Bij een tekenactie die plaats vindt onder de cursor wordt de cursor eerst uitgeschakeld, getekend, de buffer geupdate en dan weer ingeschakeld.
Dat doen window managers ook niet. Enige wat ze doen is de stacking order van de windows bepalen en de evt. randen tekenen.

Verwijderd

Weet je zeker dat repaint() ook een echte repaint veroorzaakt ipv een dirty flag?
OF het ziet er na uit naar dat niet alle X calls worden gesynched. (door de muis te bewegen gebeurt dat wel en krijgt de X-Server ook die repaint requests binnen)

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-05 23:07

.oisyn

Moderator Devschuur®

Demotivational Speaker

What's in a name, mijn punt blijft staan :)

.edit: dat laatste is idd ook zo, een repaint () scheduled een repaint, maar dat hoeft nog niet meteen te gebeuren. Er is ook een repaint (int), waarmee je een tijd op kan geven wanneer de volgende repaint maximaal plaats mag vinden. Roep die eens aan

[ Voor 79% gewijzigd door .oisyn op 27-05-2004 13:19 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Remenic
  • Registratie: Juni 2001
  • Laatst online: 12-12-2025
Ik heb dit programma op drie Linux machines geprobeerd (2x Gentoo en RedHat 9), onder KDE 3.2, GNOME 2.2 en IceWM 1.2.14 oid.

Ik heb dus gekeken hoelang elke frame er over doet, en dat is redelijk stabiel 32ms, inclusief de 30ms sleep(). Geen enorme uitschieters enzo, dus de redraw() wordt wel steeds op tijd aangeroepen.

Ik heb redraw(0) en redraw(5) geprobeerd, maar nog steeds is het niet zo soepel als het op een Windows machine draait. Pas zodra ik de muis beweeg verloopt de animatie met kleine stabiele stappen, wat voor een vloeiend beeld zorgt.

CPU gebruik is laag, dus daar ligt het ook niet aan... :|

---------- edit ----------
Ik heb deze regels toegevoegd na de repaint() call:
Java:
1
2
3
4
     System.out.print("\r");
      int i = 0;
      while ( i++ < 70 )
        System.out.print( (i == (int)(y*70)) ? "-" : " " );


In een terminal zie ik wel een vloeiende animatie, dus de sleep() methode lijkt netjes zijn werk goed te doen. Het zit hem echt in de repaint()....

[ Voor 24% gewijzigd door Remenic op 27-05-2004 19:30 ]


Verwijderd

Remenic schreef op 27 mei 2004 @ 19:17:
Ik heb dit programma op drie Linux machines geprobeerd (2x Gentoo en RedHat 9), onder KDE 3.2, GNOME 2.2 en IceWM 1.2.14 oid.

Ik heb dus gekeken hoelang elke frame er over doet, en dat is redelijk stabiel 32ms, inclusief de 30ms sleep(). Geen enorme uitschieters enzo, dus de redraw() wordt wel steeds op tijd aangeroepen.

Ik heb redraw(0) en redraw(5) geprobeerd, maar nog steeds is het niet zo soepel als het op een Windows machine draait. Pas zodra ik de muis beweeg verloopt de animatie met kleine stabiele stappen, wat voor een vloeiend beeld zorgt.

CPU gebruik is laag, dus daar ligt het ook niet aan... :|

---------- edit ----------
Ik heb deze regels toegevoegd na de repaint() call:
Java:
1
2
3
4
     System.out.print("\r");
      int i = 0;
      while ( i++ < 70 )
        System.out.print( (i == (int)(y*70)) ? "-" : " " );


In een terminal zie ik wel een vloeiende animatie, dus de sleep() methode lijkt netjes zijn werk goed te doen. Het zit hem echt in de repaint()....
Ok... gokje....

Probeer eens een:

getToolkit().sync();

  • Remenic
  • Registratie: Juni 2001
  • Laatst online: 12-12-2025
Verwijderd schreef op 27 mei 2004 @ 20:55:
[...]


Ok... gokje....

Probeer eens een:

getToolkit().sync();
Jaaaaa, dat was het! Na elke repaint() roep ik nu getToolkit().sync() aan, en nu loopt de animatie altijd vloeiend. Bedankt!

  • CyBoB
  • Registratie: Januari 2001
  • Laatst online: 24-12-2025

CyBoB

.::BURB::.

had je niet gewoon de repaint kunnen overloaden en die weer meteen de paint methoden laten aanroepen? over het algemeen is het dan ook opgelost

  • Remenic
  • Registratie: Juni 2001
  • Laatst online: 12-12-2025
CyBoB schreef op 28 mei 2004 @ 12:54:
had je niet gewoon de repaint kunnen overloaden en die weer meteen de paint methoden laten aanroepen? over het algemeen is het dan ook opgelost
Ik heb niet de repaint methode ge-overload, maar ik heb wel ipv repaint() paint(getGraphics()) aangeroepen. Dat zou toch hetzelfde resultaat moeten hebben?

  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 16-05 13:05
Beetje een vieze manier om te synchen met je refresh rate.

Probeer dit eens in je paint methode:

Java:
1
2
3
4
5
BufferStrategy strategy = frame.getBufferStrategy();
Graphics g = strategy.getDrawGraphics();
// hier lekker op de graphics kliederen.
g.dispose();
strategy.show();


Double buffering, retecool ;)

Edit:
Of wacht.... Shit... zit ik nu een oplossing op te hoesten die juist werkt voor animatie buiten de AWT dispatch thread. (Bijvoorbeeld in een game loop.)

[ Voor 20% gewijzigd door The - DDD op 29-05-2004 00:11 ]

Pagina: 1