amiga-news ENGLISH VERSION
.
Links| Forum| Kommentare| News melden
.
Chat| Umfragen| Newsticker| Archiv
.

amiga-news.de Forum > Programmierung > Grafik Geschwindigkeit [ - Suche - Neue Beiträge - Registrieren - Login - ]

-1- [ - Beitrag schreiben - ]

21.07.2004, 08:20 Uhr

Reth
Posts: 1858
Nutzer
Hallo allerseits,

ich habe mir ein ziemliches Problem aufeghalst, für das ich einige clevere Tipps brauchen könnte.

Bei meinem Programm sieht das Ganze wie folgt aus:
Ich habe Animationsobjekte, die in einer Liste liegen. Pro Zeiteinheit wird diese Liste durchlaufen und jedes Objekt gefragt, ob es neu gezeichnet werden muss.
Wenn das der Fall ist wird mit diesem Objekte gegen alle anderen Objekte geprüft, ob sie das neu zu zeichnende überdecken (fürs Clipping, da jedes Objekt vor dem Zeichnen seinen Hintergrund sichert, um ihn ggf. wieder herstellen zu können). D.h. die Liste wird nochmal durchlaufen, wobei ich den Objekten eine Tiefenstaffelung gegeben habe, so dass nicht alle geprüft werden müssen.
Jedes Animationsobjekt kann aus unterschiedlich vielen Frames bestehen, die eine Animation erzeugen (darum wird pro Zeiteinheit geprüft, welches Objekt neu gezeichnet werden muss, da die Animationen ja unterschiedlich ablaufen und einige z.T. schon beendet sind und nur noch als statisches Bild vorhanden sind). Vom Aussehen her gleiche Animationsobjekte (also Objekte mit den gleichen Grafiken) haben immer die gleiche Framezahl und auch identische Animationsparameter.

Das Ganze hab ich in C++ implementiert mit Hilfe der BltMaskBitMap-Funktionen. Jedes Objekt bekommt eine Bitmap für seinen Hintergrund beim Initialisieren. Die eigentlichen Grafiken liegen nur einmal im Speicher und werden teilweise von unterschiedlichen Objekten verwendet.

Nun ist das Ganze aber bei ca. 200 Objekten viel zu langsam.
Hat denn jemand ne gute Idee, wie man das Ganze optimieren kann? (Assembler scheidet aus, da ich das Ganze in C++ belassen möchte, um es ggf. leichter portieren zu können).

Ich kann mir denken, dass das Ganze ohne Codeausschnitte nur schwer zu beantworten ist, aber evtl. hat jemand grundlegende Ideen zur Verbesserung?

Ich hab in meinem Programm das Listenprinzip mit abzulaufenden Objekten an allen Ecken und Enden eingebaut (einfach verkettete Listen, die mit Hilfe von for-Schleifen abgelaufen werden), schätze das macht das Ganze so lahm.

Vielen Dank schon mal!

Ciao

[ - Antworten - Zitieren - Direktlink - ]

21.07.2004, 10:54 Uhr

whose
Posts: 2156
Nutzer
Zitat:
Original von Reth:
Hallo allerseits,

ich habe mir ein ziemliches Problem aufeghalst, für das ich einige clevere Tipps brauchen könnte.

Bei meinem Programm sieht das Ganze wie folgt aus:
Ich habe Animationsobjekte, die in einer Liste liegen. Pro Zeiteinheit wird diese Liste durchlaufen und jedes Objekt gefragt, ob es neu gezeichnet werden muss.
Wenn das der Fall ist wird mit diesem Objekte gegen alle anderen Objekte geprüft, ob sie das neu zu zeichnende überdecken (fürs Clipping, da jedes Objekt vor dem Zeichnen seinen Hintergrund sichert, um ihn ggf. wieder herstellen zu können). D.h. die Liste wird nochmal durchlaufen, wobei ich den Objekten eine Tiefenstaffelung gegeben habe, so dass nicht alle geprüft werden müssen.


Du kannst Dir diese zweite Überprüfung sparen. Da Du eine Überdeckungspriorität ja schon "eingebaut" hast, ist die Sicherung des Hintergrundes spätestens bei einem weiteren Objekt, das eins mit niedrigester Priorität überdeckt, nicht mehr von so großer Bedeutung, sofern Du die Objekte in jedem "Frame" neu zeichnen läßt.

Beim Zurückschreiben des gesicherten Hintergrundes solltest Du aber darauf achten, daß die Reihenfolge dann umgekehrt zu der des Neuzeichnens der Objekte ist, d.h. das "oberste" Objekt schreibt den gesicherten Hintergund zuerst wieder ins Bild. In diesem Fall stellt sich alles von ganz allein "korrekt" wieder her ;)

Grüße

[ - Antworten - Zitieren - Direktlink - ]

21.07.2004, 11:15 Uhr

whose
Posts: 2156
Nutzer
Hab noch was vergessen:

Wenn Du die Listen optimal planst, ist es auf keinen Fall lahm wegen der Listen. Das Listen-Prinzip wurde schon anno dunnemals bei PC-Spielen mit Software-Sprites verwendet, unter anderem, weils einem einen sehr zügigen Bildaufbau ermöglicht (und noch ein paar andere Sachen mehr). In meiner Artikelserie in der AmigaInsider kommt das auch zur Sprache, leider noch nicht in der in Kürze erscheinenden Ausgabe sondern erst später. Kannst mir zu dem Thema ja mailen, wenn Du noch Fragen hast.

Grüße

[ - Antworten - Zitieren - Direktlink - ]

21.07.2004, 11:30 Uhr

Reth
Posts: 1858
Nutzer
Hi, also die Listen sind von mir programmierte einfach verkettete Listen (hab ne Templateklasse gebaut, die das kann), hoffe das kam so rüber!?

Ich sag das Ganze nur, weil ich denke, dass es beim AmigaOS auch vom System her Listen für Grafiken gibt (bin mir grad nicht mehr sicher).

Also die Überdeckungsneuzeichnung wird nur aktiv, wenn sich ein Objekt ändert, dass von einem anderen momentan überdeckt wird.
Dann wird nach dem Neuzeichnen, des überdeckten Objektes das darüberliegende dazu gebracht, seinen Hintergrund zu aktualisieren.

Das Ganze würde aber entfallen, wenn man es so implementiert, wie Du es beschrieben hast.

Bin sehr an weiteren Infos/Hilfen interessiert. Meine Mail ist Rene.Thol(AT)gmx.de.

Danke
Ciao

[ - Antworten - Zitieren - Direktlink - ]

21.07.2004, 11:43 Uhr

whose
Posts: 2156
Nutzer
Zitat:
Original von Reth:
Hi, also die Listen sind von mir programmierte einfach verkettete Listen (hab ne Templateklasse gebaut, die das kann), hoffe das kam so rüber!?


Yep, ich hatte das jedenfalls so angenommen. Wäre auch die (vordergründig) einfachste Lösung. Sinnvoller wäre es aber, diese Liste je nach Bedarf in beiden Richtungen durchlaufen zu können (Hintergründe wieder zurückschreiben), also wäre eine doppelt verkettete Liste angebracht. Die benötigen nur etwas mehr Speicher, an der Geschwindigkeit ändert sich dadurch nichts.

Zitat:
Ich sag das Ganze nur, weil ich denke, dass es beim AmigaOS auch vom System her Listen für Grafiken gibt (bin mir grad nicht mehr sicher).

Bei AmigaOS gibts für beinahe Alles Listen :lach:

Zitat:
Also die Überdeckungsneuzeichnung wird nur aktiv, wenn sich ein Objekt ändert, dass von einem anderen momentan überdeckt wird.
Dann wird nach dem Neuzeichnen, des überdeckten Objektes das darüberliegende dazu gebracht, seinen Hintergrund zu aktualisieren.

Das Ganze würde aber entfallen, wenn man es so implementiert, wie Du es beschrieben hast.


Ja, wäre besser, generell für jedes Objekt ein Neuzeichnen vorzusehen. Es ist zwar etwas speicherintensiver aber dafür sparts Zeit. Sämtliche Überprüfungen bezüglich der Tiefenstaffelung fallen weg und ein paar andere Überprüfungen damit ebenfalls. Wenn Du diese eingesparte Zeit auf, sagen wir, 50 Frames hochrechnest kommt da einiges zusammen.

Zitat:
Bin sehr an weiteren Infos/Hilfen interessiert. Meine Mail ist Rene.Thol(AT)gmx.de.

Danke
Ciao


Wie gesagt, wenn Du noch weitere Frage hast, kannst Dich gern melden, ich versuch dann, Dir so gut wie irgend möglich weiter zu helfen. Animierte 2D-Grafik ist selten geworden auf dem Amiga, das muß man wieder ein wenig ausbauen ;)

Grüße

[ - Antworten - Zitieren - Direktlink - ]

25.07.2004, 00:34 Uhr

Reth
Posts: 1858
Nutzer
@whose

Meine Mail schon angekommen (frag nur, weil evtl. ist sie in Deinem SPAMFilter hängen geblieben?)?

[ - Antworten - Zitieren - Direktlink - ]

25.07.2004, 04:17 Uhr

whose
Posts: 2156
Nutzer
Du hast Post ;)

Grüße

[ - Antworten - Zitieren - Direktlink - ]

30.07.2004, 15:43 Uhr

bubblebobble
Posts: 707
Nutzer
Du rettest die Hintergründe der Objekte ?
Das ist nicht gut. Grafikkarten können sehr schnell
Daten auf die Graka transportieren, aber nur sehr
langsam lesen.

Deshalb:
Schreibe eine Funktion, die einen beliebigen Bildschirm
Ausschnitt refresht, und zwar komplett, also
Hintergundbild malen plus alle Objekte der Reihe nach.
(Dazu Layers & Regions verwenden!).
So musst du nie was retten, nur immer neu Zeichnen.
Das ist besonders auf Grafikkarten und vielen Objekten
wesentlich effizienter als Hintergrund retten.

Du machst das am besten so, dass du nach jedem Frame
eine "Damage" Liste erstellst, mit den rechteckigen
Bereichen die zu aktualisieren sind.
Wenn sich ein Objekt bewegt ist das also die komplette
alte Rechteck und die das neue Rechteck drumherum.
Wenn die Rechtecke sich überlappen kannst du noch eine
Optimierung einbauen, dass die Bereiche zusammengefasst
werden.
Wenn sich ein Objekt nur in sich verändert, also nicht
vortbewegt, genügt ein Refresh-Rechteck.
Nachdem alle Bewegungen durchgeführt wurden und alle
"zerstörten" Bereiche in der Damage List sind, kann diese
abgearbeitet werden.

So funktioniert ürigends AsteroidsTR, und damit sind
bei 50fps schon eine ganze Reihe Objekte möglich, auch
auf einem Classic und einer CyberVision 64 3D.

Wenn sich die Objekte fast in jedem Frame bewegen,
ist es vielleicht sogar noch besser immer komplett
alles neu zu zeichnen. Klingt am Anfang verschwenderisch,
ist aber auf jeden Fall schneller und logistisch
wesentlich einfacher als Hintergründe zu retten,
das ist ein Relikt aus OCS/AGA zeiten, wo lesen und
schrieben in den Graka RAM gleich schnell waren.

--
Thilo Köhler, Author von:
HD-Rec, Samplemanager, ArTKanoid, Monkeyscript, Toadies, AsteroidsTR, TuiTED, PosTED, UDM, TKPlayer, TKUnpacker
Homepage: http://www.hd-rec.de



[ - Antworten - Zitieren - Direktlink - ]

31.07.2004, 20:09 Uhr

Reth
Posts: 1858
Nutzer
Hallo bubblebobble,

Zitat:
Original von bubblebobble:
Du rettest die Hintergründe der Objekte ?
Das ist nicht gut. Grafikkarten können sehr schnell
Daten auf die Graka transportieren, aber nur sehr
langsam lesen.


Nehme dazu immer Blit-Funktionen des OS.

Zitat:
Deshalb:
Schreibe eine Funktion, die einen beliebigen Bildschirm
Ausschnitt refresht, und zwar komplett, also
Hintergundbild malen plus alle Objekte der Reihe nach.
(Dazu Layers & Regions verwenden!).
So musst du nie was retten, nur immer neu Zeichnen.
Das ist besonders auf Grafikkarten und vielen Objekten
wesentlich effizienter als Hintergrund retten.


Hm dann sollte ich am besten wohl immer alles neu zeichnen.
Kannst Du mir ein Beispiel für Layers und Regions schicken?

Zitat:
Du machst das am besten so, dass du nach jedem Frame
eine "Damage" Liste erstellst, mit den rechteckigen
Bereichen die zu aktualisieren sind.
Wenn sich ein Objekt bewegt ist das also die komplette
alte Rechteck und die das neue Rechteck drumherum.
Wenn die Rechtecke sich überlappen kannst du noch eine
Optimierung einbauen, dass die Bereiche zusammengefasst
werden.
Wenn sich ein Objekt nur in sich verändert, also nicht
vortbewegt, genügt ein Refresh-Rechteck.
Nachdem alle Bewegungen durchgeführt wurden und alle
"zerstörten" Bereiche in der Damage List sind, kann diese
abgearbeitet werden.


Klingt gut. Wenn ichs anders nicht in den Griff bekomme, werd ich das wohl mal probieren.

Danke für die Tips!
Ciao

[ Dieser Beitrag wurde von Reth am 31.07.2004 editiert. ]

[ - Antworten - Zitieren - Direktlink - ]

02.09.2004, 11:47 Uhr

Reth
Posts: 1858
Nutzer
Weiss nicht, ob ich einen neuen Thread erstellen soll, aber:

Habs nun mal soweit, dass ich in der Liste aller darstellbaren Objekte einmal pro Zeiteinheit durchlaufe. Dabei wird jedes Objekt gefragt, ob es aktualisiert werden muss. Wenn ja, wird dessen Hintergrund wiederhergestellt, und das Objekt selbst aktualisiert dargestellt.
Im Anschluss daran werden alle Objekte, die "darüber liegen" können abgefragt, ob sie das eben aktualisierte Objekt überdecken.
Ist das der Fall, wird deren Hintergrund erneut gesichert (da sich dieser ja verändert hat).

Das hat den Vorteil, dass nur sehr kleine Bereiche geblittet werden müssen, dafür aber mehrere. Mit zunehmender Anzahl zu animierender Objekte wird das Ganze aber auch zunehmend langsamer.

Wie kann man denn das Ganze optimieren? Einen ganz anderen Ansatz wählen (falls das Problem in diesem Thread schon "gelöst" wurde, einfach nochmal auf die Meldung hinweisen, denn dann hab ichs wohl nicht verstanden)?

Danke vielmals
Ciao

[ - Antworten - Zitieren - Direktlink - ]

03.09.2004, 11:13 Uhr

bubblebobble
Posts: 707
Nutzer
Wie gesagt, rette auf KEINEN Fall die Hintergründe!
Bei modernen Grakas ist das so:
Lesen von Graka ca. 2MB/sec schreiben auf Graka ca. 500MB/Sec,
schreiben innerhalb der Graka 2000MB/sec oder mehr.

Die Angaben sind qualitativ gemeint, schwanken natürlich
von System zu System, aber du siehst die Unterschiede.

Deshalb nochmal (beide Möglichkeiten sind
doppelt gepuffert und flackern nicht!):

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1. Möglichkeit: Komplettrefresh
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Das ist schneller bei vielen Objekten, erfordert aber
eine Mindestleistung der Grakas, sodass ein Classic
schon etwas ins Schwitzen kommt, es geht aber
noch mit 50fps. So funktioniert AsteroidsTR.

Init:
* Erstelle eine Bitmap für das Hintergrund Bild.
* Erstelle eine Bitmap für die "Arbeitsbitmap".
* Erstelle einen sichtbaren Screen in der Größe der Arbeitsbitmap.
Bei Asteroids ist das so gelöst, dass der Screen
einfach 3x so hoch ist wie sichtbar, dadurch ist garantiert
dass alle drei Bitmaps im Graka RAM liegen, was sie
aus Speedgründen auch dringend sollten. Eine Bitmap
mit "friend screen" zu erstellen ist zu unsicher.

Gameloop:
Blitte die Hintergrundbitmap komplett auf die Arbeitsbitmap.
Wenn es kein Hintergrund Bild gibt, dann musst du RecTFill()
nehmen.
Blitte alle Objekte neu auf die Arbeitsbitmap.
Bitte die Arbeitsbitmap auf die Screenbitmap -voila.

Zum Blitten immer BltBitmapRastPort() verwenden.
Für Objekte mit Maske BltBitmapMaskRastPort()

Diese Methode hat einen sehr hochen Grund-Aufwand,
geht dafür nicht so schnell in die Knie wenn es viele Objekte gibt.
Und normalerwiese ist es ja wichtig was bei vielen Objekten passiert,
nicht ob der Overhead bei wenig Objekten gross ist, die laufen sowieso
immer flüssig.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
2. Möglichkeit: Damagelist
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Das ist wesentlich schneller bei wenigen Objekten,
geht aber in die Knie bei vielen. Da aber die obige Methode
bei wenigen Objekten auch schnell genug für GRakas ist, sollte
man die eigentlich vor ziehen. Diese Methode kann man aber
nutzen wenn keine Hardwarebeschleunigung vorhanden ist oder
der Rechner sehr lahm ist. So funktioniert AsteróidsTR_CPU.

Init:
* Erstelle eine Bitmap für das Hintergrund Bild.
* Erstelle eine Bitmap für die "Arbeitsbitmap".
* Erstelle einen sichtbaren Screen in der Größe der Arbeitsbitmap.
* Erstelle eine Liste mit "damage" Regionen, die wir
uns merken wollen. Also eine Liste mit x1,y1,x2,y2 Koordinaten.

Gameloop:
Wenn sich ein Objekt bewegt, dann füge einen Eintrag in die DamageListe
dazu mit den (x1 y1) (x2 y2) Koordinaten des umgebenden Rechtecks
des Objekts. Es muss ein Eintrag gemacht werden für die alte
Position UND für die neue Position des Objekts. Bei Überlappung
(meistens so) kann man optimieren, indem man daraus einen einzigen
Listenentrag macht mit jeweils den Extremen Punkten.
Wenn sich nun alle Objekte "bewegt" haben, arbeiten wird die DamageList ab.
For Each DamageListItem {
Setze eine neue ClipRegion mit den Koordinaten des wiederherzustellenden
Bereiches auf die Arbeitsbitmap. (InstallClipRegion()).
Blitte Hintergrund und dann alle Objekte auf die Arbeitsbitmap.
Evtl. kann man mit "if" abfragen, welche überhaupt in Frage kommen,
ansonsten besorgt uns die ClipRegion das.
Blitte den Bereich von der Arbeitsbitmap auf die Screenbitmap.
Lösche die Region (InstallClipRegion() mit der alten Region).
}
fertig!


So, das wars. Denke immer dran, für jedesmal wo
du einen Rechteckigen Bereich AUS der Grafikkarte
ausliesst, könntest du 20x das gleiche hineinblitten.

--
Thilo Köhler, Author von:
HD-Rec, Samplemanager, ArTKanoid, Monkeyscript, Toadies, AsteroidsTR, TuiTED, PosTED, UDM, TKPlayer, TKUnpacker
Homepage: http://www.hd-rec.de



[ - Antworten - Zitieren - Direktlink - ]

03.09.2004, 13:24 Uhr

Reth
Posts: 1858
Nutzer
@bubblebobble

Heißen Dank!

Werde das Ganze mit den mehrfachen BitMaps mal probieren. Dachte mir halt, da ich nur sehr kleine Bereiche blitten muss (dafür aber manchmal umso mehr), machh ich das wie beschrieben, damit ich Speicher sparen kann.
Bei der Mehrfach-Bitmaplösung brauch ich halt ne Menge RAM (Minimum 1024x768x8x2).

Bin mir auch nicht sicher, inwieweit sich die INTUITICK bei mir als Bremser bemerkbar machen, da wenn das Ganze erstmal langsam ist und ich das Programm beende es manchmal mehrere Sekunden/Minuten dauert, bis das Programm zurückkehrt, obwohl Screen, Window, etc. schon alle geschlossen sind!

[ - Antworten - Zitieren - Direktlink - ]

03.09.2004, 19:27 Uhr

bubblebobble
Posts: 707
Nutzer
Zitat:
Original von Reth:
@bubblebobble

Bei der Mehrfach-Bitmaplösung brauch ich halt ne Menge RAM (Minimum 1024x768x8x2).

Wenn sich viele Objekte überlappen, dann brauchst du auch viel Speicher, unter Umständen sogar mehr. Rechne mal nach.
Auch kannst du davon ausgehen, dass ein Blitt immer serh schnell ist.
Ein Blitvorgang hat aber eine Initialisierung, und die braucht
recht lange. Wenn du also die gleiche Fläche blittest, und
in 200 Teile zerteilst, dann ist das wesentlich langsamer.

Zitat:
Bin mir auch nicht sicher, inwieweit sich die INTUITICK bei mir als Bremser bemerkbar machen, da wenn das Ganze erstmal langsam ist und ich das Programm beende es manchmal mehrere Sekunden/Minuten dauert, bis das Programm zurückkehrt, obwohl Screen, Window, etc. schon alle geschlossen sind!
Intuiticks sollte nichts damit zu tun haben. Das kostet dich
so gut wie eine Rechenleistung.


--
Thilo Köhler, Author von:
HD-Rec, Samplemanager, ArTKanoid, Monkeyscript, Toadies, AsteroidsTR, TuiTED, PosTED, UDM, TKPlayer, TKUnpacker
Homepage: http://www.hd-rec.de



[ - Antworten - Zitieren - Direktlink - ]


-1- [ - Beitrag schreiben - ]


amiga-news.de Forum > Programmierung > Grafik Geschwindigkeit [ - Suche - Neue Beiträge - Registrieren - Login - ]


.
Impressum | Datenschutzerklärung | Netiquette | Werbung | Kontakt
Copyright © 1998-2024 by amiga-news.de - alle Rechte vorbehalten.
.