Performance-Messung des Frame-Zeichnens

wer will sich schon gerne blocken lassen, etwa wenn der AWT-Thread gerade einen ActionListener bearbeiten muss,
wobei das im sinnvollen Programmen nicht zu lange dauert,

Spielschleife schneller als Zeichnen? zumindest denkbar,

mehrere repaint()-Aufrufe in Spielschleife, in verschiedenen ifs? wird wohl vorkommen

ist letztlich schlicht ok: repaint fordert zum Zeichnen auf, nicht mehr und nicht weniger,
gäbe es das nicht, müsste man es vielleicht mit eigenen Thread extra bauen,
aber auf die Schnelle fällt mir zumindest kein eigenes Programm ein

Das „coalescing“ (d.h. Zusammenfassen der Paint-Events) ist wohl recht kompliziert, ich hatte da mal ein bißchen im Code geschaut aber die ultimativ-relevante Stelle IIRC nicht gefunden/erkannt (ist schon ne Weile her).

Bei der Frage, was man (ganz oberflächlich betrachtet) misst, gibt es zwei Ansätze:

  • Vom Anfang des Paintens bis zum Ende des Paintens
  • Vom Anfang des Paintens bis zum Anfang des nächsten Paintens

@Spacerat Wenn ich’s richtig sehe, machst du das zweite. Ich hatte das erste versucht. Man könnte drüber streiten, was „sinnvoller“ ist. Mit dem zweiten mißt man wohl eher (und auch deutlich leichter) das, was man als „FPS“ bezeichnet. Allerdings kann zwischen den Frames ja theoretisch wer-weiß-was passieren, was nichts mit dem Zeichnen zu tun hat.

Meine Intention war, zu messen, wie lange tatsächlich die Abarbeitung der Zeichenbefehle braucht - was natürlich die Frage aufwirft, was DAS wiederum genau bedeutet, da es von einem „g.drawLine“ aus der JVM über irgendwelche ShapePipes in die nativen Teile und die GL-Implementierung bis zum Treiber und letztendlich dem Grafikkartenspeicher ja schon ein laaaanger Weg ist.

Das Toolkit#sync muss übrigens nicht notwendigerweise etwas mit dem „vsync“ zu tun haben. Ich gehe mal ganz unfundiert davon aus, dass das letztendlich ein glFlush/glFinish auslöst (hatte kurz geschaut, aber den entsprechenden Code vorhin auf die Schnelle nicht gefunden - nur gesehen, dass der Aufruf explizit an die OpenGL-Rendering-Pipieline weitergereicht wird). (Aber… was genau ein glFlush/glFinish bewirken wird in über die lapidare Doku hinausgehender Genauigkeit hier vermutlich spontan nur einer beantworten können :wink: )

Also ich wüsste nicht, was man ausser fertig gerenderte Framebuffer noch anderes synchronisieren müsste. In der Doku steht auch nicht mehr und VSync macht das selbe. mit glFlush und glFinish sagt man dem glContext, dass man einen Buffer fertig gezeichnet hat und dieser an den Monitor übertragen werden kann.

Also die Zeit, die ein einzelner Grafikbefehl zur Ausführung braucht ist eh nicht messbar, weil sie allesamt “sofort” returnen (nachdem das Ganze an einen RenderThread übertragen wurde), und das eigentliche Zeichnen noch nichtmal begonnen hat. Wollte man z.B. darauf warten, ob ein Bild komplett in einen Context übertragen wurde, müsste man einen ImageObserver verwenden, den boolschen Wert von “drawImage” abfragen, ggf. warten und im Observer weiterlaufen lassen.

  if(!g.drawImage(image, 0, 0, this)) {
    synchronized(this) {
      wait();
    }
  }
}

public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
  synchronized (this) {
    notifyAll();
  }
  return true;
}```
Solche Dinger richtig zu messen ist ohnehin nie wirklich korrekt, selbst das von Fancy erwähnte NVidia-Teil misst nur ganze Frames und das ist ja eigentlich auch das, worauf es ankommt. Zumindest gilt hier immer, je größer der Messintervall ist, desto beruhigter jedoch ungenauer sind die Messergebnisse. Man kann auch durchaus Messintervalle von 20ms festlegen, nur hat man dann Ergebnisse, die man kaum noch lesen kann, weil die Anzeige dauernd zappelt.

Einen einzelnen Befehl meinte ich nicht. Aber es ist ja definitiv so, dass wenn man z.B. 100000 Linien zeichnet das sehr schnell geht, aber wenn man Antialiasing anschaltet wird es gääähnend langsam - solche Unterschiede zu messen oder anderweitig erkennen zu können wäre schon nicht schlecht…

Ich habe keinen Schimmer, was Swing und AWT da macht. Im Allgemeinen blockiert ein aktives Vsync aber erstmal nicht die CPU. Erst wenn zu viele Kommandos kommen, die den Back Frame Buffer beeinflussen würden, blockiert die GPU den zugehörigen CPU Thread. Das passiert dann aber irgendwo in irgendeinem (vielleicht) asynchronen Kommando und nicht zwangsweise während des Swappings.

Erst Vsync mit glFinish() blockiert die CPU und GPU bis zum vollständigen Swap. Da dadurch CPU und GPU mit jedem Frame synchronisiert werden, wird das Timing im Render Loop einfacher. Allerdings hebelt man damit auch die Pipeline-Vorteile der Grafikkarte aus und reduziert den Durchsatz. Auserderm hat es negative Auswirkungen auf die Latenz.

Bei Anwendungen im Fenster wird es auch nicht einfacher, da das Betriebssystem dann auch noch mitspielen möchte.

Beim Messen kommt es darauf an, was man messen möchte und messen kann. Mit FCAT lassen sich auch Dropped- und Runt- Frames messen, imho gibt es dazu derzeit eben keine Alternative. Ansonsten lässt sich aber mit OpenGL auch die Ausführungsdauer einzelner Kommandos/Blöcke messen, dazu wird ein passender (ebenfalls asynchroner) Timer Query mit in die Pipeline geschoben. Aber auch das ist nicht einfach und es greift nicht weit genug in die Pipeline ein, sodass sich Jitter / Mikroruckler darüber nicht messen lassen.

Viele Grüße
Fancy