# VolatileImage



## Evolver (16. Okt 2007)

Ich habe mal angefangen, nebenbei an einem kleinen Spielchen zu arbeiten. Zum Anzeigen renderte ich erst ein Image und gab das dann auf dem Bildschirm aus. Nachdem ich dann Hintergrundbild und ein paar teilweise transparente Objekte gezeichnet habe, ging die Performance in den Keller und ich habe ein bisschen gegooglt, was ich machen kann. Ich bin auf VolatileImage gestoßen. Mein Zeichcode sieht so etwa so aus:

```
private VolatileImage mScreen;

	public void renderScreen()
	{
		if(mScreen==null || mScreen.validate(this.getGraphicsConfiguration())==VolatileImage.IMAGE_INCOMPATIBLE)
			mScreen = this.createVolatileImage(PANEL_WIDTH, PANEL_HEIGHT);
		Graphics2D tG2D = (Graphics2D)mScreen.getGraphics();

		mBG.renderObject(tG2D);
		for(int i=0; i<6; ++i)
			mCities[i].renderObject(tG2D);
		mClouds.renderObject(tG2D);n
		this.drawFPS(tG2D);
		
		tG2D.dispose();
	}
	
	public void printScreen()
	{
		if(mScreen.validate(this.getGraphicsConfiguration())==VolatileImage.IMAGE_OK) {
			try {
				Graphics2D tG2D = (Graphics2D)this.getGraphics();
				tG2D.drawImage(mScreen, 0, 0, this);
				tG2D.dispose();
			}
			catch(Exception pE) {
				System.out.println(pE);
			}
		}
	}
```
Vorher war mScreen ein Image, die Geschwindigkeit ist jetzt ca. 2,5mal so hoch, also was ist an VolatileImage so besonders, warum ist es schneller?

Und meine zweite Frage: Für die einzelnen Grafikobjekte (wie z.B. Hintergrundbild) verwende ich noch Image. Sollte ich das auch umstellen?


----------



## Wildcard (16. Okt 2007)

Eins vorweg, das getGraphics sieht schonmal falsch aus.
VolatileImage ist deshalb schneller, weil es im Speicher der Grafikkarte liegt und daher direkt durch dessen Pipeline gerendert werden kann. Das Programm selbst wird dadurch jedoch nicht unbedingt schneller. Es gilt zwischen gefühlter und tatsächlicher Performance zu unterscheiden.


----------



## Evolver (17. Okt 2007)

1. Die Klasse erbt von JPanel. Ich habe gelesen, dass ich mir dort problemlos getGraphics() verwenden dürfte. Wenn nicht, wie komme ich "besser" an das Graphics-Objekt?

2. Wenn du's mit der _Begrifflichkeit_ so genau nimmst: Das Programm wird vielleicht nicht schneller, aber dessen Ausführung vielleicht? Gibt es denn beim VolatileImage noch Nachteile, die ich beachten/bedenken sollte?


----------



## Wildcard (17. Okt 2007)

1) nein, du darfst auch dort kein getGraphics verwenden. getGraphics ist im Prinzip immer falsch. Das Graphics Objekt bekommst du in paintComponent zugeteilt und nur dort wird gezeichnet. 
Die einzige Ausnahme ist das sog. active Rendering. Ob das bei dir Sinn macht hängt von der Anwendung ab. (i.d.R. wird das nur für Fullscreen Programme verwende).

2)Das Rendern auf den Screen wird schneller, dafür passiert mehr, bevor es überhaupt auf den Screen kommt. Subjektiv geht aber der Bildaufbau schneller und das ist ja keine schlechte Sache. 
Nachteile? naja, sie sind eben volatile  :wink: 
Mit den Dingern hat man mehr Arbeit. In den aller meisten Fällen genügt eine normale BufferStrategy völlig.


----------



## Marco13 (17. Okt 2007)

Wildcard hat gesagt.:
			
		

> 1) nein, du darfst auch dort kein getGraphics verwenden. getGraphics ist im Prinzip immer falsch.


Da im geposteten Code zweimal "getGraphics" vorkommt, und es einmal NICHT falsch ist, sollte man das vielleicht nochmal genauer sagen:

getGraphics ist auf Components im Prinzip immer falsch (d.h. panel.getGraphics() ist böse). Auf einem BufferedImage ist es aber vollkommen OK!


----------



## Evolver (17. Okt 2007)

Ja toll, nur ist es bei einem Spiel schon sinnvoll, aktiv zu rendern, schließlich will ich ja, das gezeichnet wird, wenn ich das Zeichnen aufrufe, unteranderem eben auch um die für's Zeichnen benötigte Zeit zu ermitteln. Ich habe mit ein paar Tutorials zur Java-Spieleprogrammierung angesehen und alle gehen so vor wie ich.

Also was ist so schlimm daran, bei einer Component _getGraphics_ zu verwenden? Und wie bekomme ich - wenn nicht so - mein PufferBild korrekt auf meine Komponente?


----------



## Wildcard (17. Okt 2007)

Marco13 hat gesagt.:
			
		

> getGraphics ist auf Components im Prinzip immer falsch (d.h. panel.getGraphics() ist böse). Auf einem BufferedImage ist es aber vollkommen OK!


Naja, bei images sollte man createGraphics aufrufen.


----------



## Evolver (17. Okt 2007)

Schön und gut, also habe ich in _renderScreen()_ nun getGraphics durch createGraphics ersetzt, meinetwegen. Meine frage bleibt aber bestehen: Wie soll ich denn bei aktivem Rendern mein Pufferbild 'korrekt' auf meine Component (JPanel) bringen?


----------



## Wildcard (17. Okt 2007)

Wie gesagt, wenn du wirklich active rendering verwendest, ist getGraphics auf einer Component schon ok. Das active Rendering im non-exclusive mode nicht ganz unproblematisch ist, muss dir allerdings klar sein.


----------



## Evolver (17. Okt 2007)

> getGraphics ist im Prinzip immer falsch.





> Wie gesagt, wenn du wirklich active rendering verwendest, ist getGraphics auf einer Component schon ok.


 :roll:  :bae:


Noch zu meiner anderen Frage: Dort wo ich für graphische Objekte eine Bilddatei in ein awt.Image lade und daran auch nichts im Spielverlauf ändere, kann ich getrost awt.Image (oder eben Buffered...) lassen, da Volatile nur spürbare Verbesserungen bei sich häufig ändernden Grafiken bring. Habe ich das so richtig verstanden?


----------



## Wildcard (17. Okt 2007)

Ein VolatileImage macht absolut nur Sinn als Offscreen Image für eine BufferStrategy!


----------



## Evolver (17. Okt 2007)

OK, danke.


----------



## Gast (18. Okt 2007)

Evolver, um die Geschwindigkeit des Codes wirklich zu erhöhen, würde es sich eventuell lohnen, mBg, mCities und mClouds zu buffern, sprich in BufferedImages zu rendern, die nur dann "neu" gemacht werden, wenn sich etwas an ihnen verändert. 

die entsprechenden renderObject() methoden würden dann zuerst mal überprüfen, ob ein bereits gerendertes Bild des Objektes vorliegt. Wenn ja würden sie dieses über ein drawImage() direkt ausgeben, wenn nein würden sie es neu generieren. Hier wären noch Optimierungsmöglichkeiten, indem du das neu generieren bspw. in nen anderen Thread auslagerst (würde natürlich zur folge habne, dasss die neu generierten bilder nicht sofort up2date sind, sondern erst ein paar frames später) 

Eine weitere Performancemöglichkeit wäre: starte dein Spiel doch mal mit dem Parameter: -Dsun.java2d.opengl=True (funktioniert NUR unter windows, unter MacOS kann es zum absturz des Programms führen). Dadurch aktivierst du die Swing OpenGL Rendering Pipeline, die um einiges schneller in der Darstellung ist. Damit wirst du auch mehrere zusätzliche FPS herausholen können.

Ansonsten: überprüft die renderObject() methoden, ob sie performant sind: erzeugst du in jedem renderObject() aufruf Objekte? wie viele? Ist das wirklich nochtwendig? (kannst du die nicht beim initialisieren erzeugen?). Versuche drawLines durch drawRect aufrufe zu ersetzen, soweit das möglich ist, drawRect scheint performanter zu sein (im Java6 Quellcode von Swing wird drawRect teilweise sogar als ersatz für drawLine verwendet. Ich schätze dass liegt daran, dass die zu bemalenden Pixel bei drawLine über die Geradengleichung errechnet werden, während drawRect weiß, dass es nur zeilen oder spalten füllen soll) 

Wenn du willst, poste auch mal die renderObject() methoden, vielleicht zeigen die noch mehr möglichkeiten!


----------



## Wildcard (18. Okt 2007)

Gast hat gesagt.:
			
		

> Evolver, um die Geschwindigkeit des Codes wirklich zu erhöhen, würde es sich eventuell lohnen, mBg, mCities und mClouds zu buffern, sprich in BufferedImages zu rendern, die nur dann "neu" gemacht werden, wenn sich etwas an ihnen verändert.


Sollte dann allerdings ein compatible Image sein. http://java.sun.com/javase/6/docs/a...ion.html#createCompatibleImage(int, int, int)


----------

