[Monkey] Performance Lösung unter Html5
Übersicht

![]() |
MidimasterBetreff: Performance Lösung unter Html5 |
![]() Antworten mit Zitat ![]() |
---|---|---|
Hier ein Beispielcode, der Möglichkeiten aufzeigt auch unter HTML5 flüssige Spiele zu erstellen und dabei unnötige Bildschirmaufbauerei unterdrückt.
Da ich ja noch aus einer Generation komme, die mit sehr langsamen Computern zurrechtkommen musste, setzten wir oft eine Technik ein, die den Bildschirmaufbau seltener machten als die Refresh-Rate. So ist es möglich die Spielfiguren mit gleichmäßiger Refresh-Rate 1/60 sek zu bewegen, auch wenn der Bildschirmaufbau über 1/60 sek dauern würde. Unter HTML5 wird es wieder wichtig, sich so den einen oder anderen Bildschirmaufbau zu sparen. z.b. alleine ein CLS verbraucht 50% der gesamten Renderzeit. Drei Techniken wären dabei zu nennen: A. Reduzierung des Bildschirmaufbaus auf 15Hz B. Reduzierung des Bildschirmaufbaus auf Momente, in denen wirklich etwas geschehen ist. C. Reduzierung des Bildschirmaufbaus auf Bereiche, die sich geändert haben A. Reduzierung des Bildschirmaufbaus auf 15Hz dies geht relativ einfach: BlitzBasic: [AUSKLAPPEN] Import mojo B. Reduzierung des Bildschirmaufbaus auf Momente, in denen wirklich etwas geschehen ist. Dies würde so ablaufen, dass eine UnterKlasse an die Hauptklasse eine Bitte sendet, dass beim nächsten Mal ein Bildschirmaufbau nötig ist. Verwirklichen läßt sich das mit einer Variablen Noetig% in der Hauptklasse. Diese Variable wird dann in den Unterklassen einfach auf 1 gesetzt. Die OnRender() checked nun diese Variable und bricht den Rendervorgang gegebenenfalls sofort ab. Hilft natürlich viel, wenn es um "ruhige" Bildschirme geht (Game-Editor, Startmenü, etc..) BlitzMax: [AUSKLAPPEN] Import mojo Hinweis: In den Unterklassen wird die MalenNoetig auf 2 gesetzt, weil dies sicherer ist. D.h. bei jeder Änderungen erfolgen 2 Bildschirmaufbauten. In der Create() wird sogar 20 gewählt, um bei ev. Loading-Verzögerungen zu einem trotzdem perfekten 1.Bildaufbau zu kommen. Hier mal ein Testprogramm, mit dem Ihr die Performance im Windows Task-Manager testen könnt: BlitzMax: [AUSKLAPPEN] Strict C. Reduzierung des Bildschirmaufbaus auf Bereiche, die sich geändert haben ist schon weitaus komplizierter zu verwirklichen und oft auch wirkungslos, wenn sich überall im Spiel was bewegt. Vielleicht schreib ich da später mal was dazu... |
||
![]() |
Lord StweccysBetreff: Re: Performance Lösung unter Html5 |
![]() Antworten mit Zitat ![]() |
---|---|---|
Midimaster hat Folgendes geschrieben: A. Reduzierung des Bildschirmaufbaus auf 15Hz
Geht das nicht einfacher, indem man bloß BlitzBasic: [AUSKLAPPEN] SetUpdateRate 15
macht? |
||
![]() |
Midimaster |
![]() Antworten mit Zitat ![]() |
---|---|---|
naja, schon, aber dann bewegen sich die Figuren ja plötzlich nur noch 15x pro Sekunde. Dazu müssten dann die Schrittweiten pro Schritt erhöht werden. Damit legst du dich auf ein groberes Raster fest. Auf der nächsten Maschine könnte aber das Programm vielleicht schon wieder gleichmäßiger laufen.
Mit der geteilten Methode bist Du unabhängig und kannst Spielrate und Bildrate unabhängig voneinander laufen lassen. Da dies hier ja nur ein Ansatz ist (mit fixer 60Hz/4 Bildrate), könnte man die Idee jetzt weiterspinnen und tatsächlich bei Start des Spiels beim User die vorhandene Leistung des Systems testen und dann dementsprechend die Bildrate optimieren. |
||
![]() |
Lord Stweccys |
![]() Antworten mit Zitat ![]() |
---|---|---|
Oh, natürlich, wie dumm von mir XD | ||
Macintosh |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
15Hz ruckelt doch aber ;) seiht es nicht erst ab 30Hz flüssig aus? oder irre ich mich. | ||
![]() |
Midimaster |
![]() Antworten mit Zitat ![]() |
---|---|---|
bei dem Beispiel geht es darum, die Bildaufbau-Rate zu drosseln, ohne die Spiel-Rate zu ändern.
Die 15Hz ruckeln, weil ich ja auch einen extremen Wert verwenden wollte, um zu zeigen, dass dennoch das Rechteck gleich schnell wie im 60Hz Beispiel bewegt wird. Auf den meisten Computer wird wehrscheinlich auch im 30Hz Mode die Leistungsgrenze des Computers nicht erreicht. Bei mir kommt HTML5 aber bei 60Hz bereits an seine Grenze (obwohl ja nur ein Rechteck zu zeichnen ist!) Ob du nun 15 Hz oder 30 Hz nimmst, hängt doch stark von den Möglichkeiten des Zielgerätes ab. "60Hz nehmen und dann bei der Leistungsgrenze 100% des Rechners anstoßen", bedeutet, dass real gar keine 60 Refreshs mehr vorgenommen werden. Folge: das Spiel wird auch bei den Figuren langsamer. In einem solchen Fall reduziertst Du mit dem Beispiel-Algo nun ausschließlich die Bildrate. Meinetwegen auch nur auf 30Hz oder 45Hz. Eben so weit, wie es die Hardware des Rechners zuläßt. Die Refresh-Rate des Spiels ist jedenfalls davon nicht betroffen. Zwar gibt es für solche einen Erhalt auch bessere Algos. Die müssen aber im gesamten Spiel mitbedacht werden und sind daher ungleich komplizierter. Der hier vorliegende Algo ist superunkompliziert und trotzdem wirkungsvoll. für eine Veränderung auf 30Hz müßte nur soviel geändert werden: BlitzMax: [AUSKLAPPEN] Function BildRate%() für eine Veränderung auf 40Hz müßte nur soviel geändert werden: BlitzMax: [AUSKLAPPEN] Function BildRate%() |
||
![]() |
MidimasterBetreff: Reduzierung des Bildschirmaufbaus auf Bereiche |
![]() Antworten mit Zitat ![]() |
---|---|---|
So nun kommt hier das versprochene Beispiel zum Thema:
C. Reduzierung des Bildschirmaufbaus auf Bereiche, die sich geändert haben Das Beispiel zeigt ein Arkanoid, dass ohne Optimierung unter HTML5 und Firefox 3.6 auf meinem alten Rechner immer zwischen 75% und 100% Rechnerleistung verbrauchen würde. Auch dann, wenn sich gar nichts bewegt. Mit der Optimierung bleibt man unter 20%. Zeitweise sogar unter 1%. Gespielt wird mit der Maus, der Ball steht zunächst über dem Schläger. Gestartet wird mit Mausklick. Der Schläger wird auch mit der Maus bewegt. Der Optimierungs-Algo "weiß" immer, welches Objet sich wo verändert hat und ersetzt den Hintergrund nur dort, wo es nötig ist. Auch die Vordergrundelemente wie Steine, der Ball und der Schläger werden nur dann aktualisiert, wenn sie sich bewegt oder verändert haben oder der Hintergrund in ihrer Nähe erneutert werden mußte. Ich habe für Demozwecke eine Konstante MODE% eingebaut, die Ihr auf 0 setzt: das zeigt was der User später sehen würde: ein scheinbar ganz normaler Spieldfeldaufbau. Erst wenn man MODE% auf 1 setzt, erkennt man, wo wirklich immer wieder mal was gezeichnet wird. Mit dem Ball wird irgenwann fast jeder Bereich des Bildschirms mal "restauriert". (sieht abgefahren aus, soll aber ja nur die Arbeitsweise zeigen.) Richtig abgefahren wird es aber erst auf MODE=2. Jetzt ist nur noch zu sehen, was bei jedem "FLIP" wirklich gezeichnet wird: nämlich fast gar nix! Daher bleibt die Systemleistung im Schnitt unter 20%. Bei Stillstand get sie sogar auf 0 zurück. ![]() Das komplette Spiel: BlitzMax: [AUSKLAPPEN] Strict Erklärung: Ich teile das Bild in 3 Regionen Region[1] ist der Bereich der Steine Region[2] ist das meist leere Feld, wo nur der Ball fliegt Region[3] ist der Bereich vom Schläger abwärts Wird bei den Check()'s der Spielelemente festgestellt, das sich in einem der Bereiche etwas getan hat... BlitzMax: [AUSKLAPPEN] Method CheckOne%() ... geht das Flag Region[..] auf 1. Dadurch wird der Hintergrund restauriert... BlitzMax: [AUSKLAPPEN] Method OnRender%() ...und dann in den Draw()'s die Objekte wirklich gezeichnet. BlitzMax: [AUSKLAPPEN] Method DrawOne%() Das virtuelle Region[9]-Flag dient nur dazu am Spielbeginn das komplette Zeichnen aller Elemente einmal zu erzwingen: BlitzMax: [AUSKLAPPEN] Method OnRender%() In den nächsten Tagen werde ich dann jetzt mal eine Optimierung durch [i]Scissors vorstellen, die ist sehr leicht zu realisieren, aber bringt bei vielen bewegten Objekten auch nicht den erhofften Performancegewinn. |
||
![]() |
MidimasterBetreff: Scissor sind leider keine Lösung |
![]() Antworten mit Zitat ![]() |
---|---|---|
Schlechte Nachrichten:
Ich habe versucht, eine Leistungsoptimierung für MONKEY mit den dort angebotenen Befehl Scissor zu schreiben. Aber leider lässt sich damit keine Performance verbessern. Was sind Scissors? Scissor erstellt auf dem Bildschirm einen Clipping-Bereich. Bei allen nachfolgenden Grafikbefehlen wird nur der Teil sichtbar, der innerhalb des Clipping-Bereichs liegt. Der entsprechende BlitzMax-Befehl wäre SetViewport. Was wollte ich damit erreichen? Da ja weniger gemalt werden muss, erwartete ich, dass sich damit der Bildschirmaufbau beschleunigen lässt. Hierbei muss man drei Fälle unterscheiden: 1.) das Objekt liegt komplett innerhalb des Clipping-Bereichs Hier dauert die Ausgabe der Grafik in der Regel genauso lange wie ohne Clipping. 2.) das Objekt liegt komplett ausserhalb des Clipping-Bereichs Hier könnte man erwarten, dass die Bearbeitungszeit komplett auf Null fällt, weil sich sehr leicht ohne Grafikbefehle mit reiner Mathematik feststellen läßt, dass alle Ecken der Grafik ausserhalb liegen. 3.) das Objekt muss teilweise gezeichnet werden Hier muss man damit rechnen, dass die Bearbeitungszeit kürzer wird, da ja ein weitaus kleinerer Teil der Grafik gezeichet werden muss. In seltenen Fällen kann es vorkommen, dass das Berechnen des Clipping-Bereichs sogar länger dauert als wenn man das Objekt hätte komplett zeichnen dürfen. Wie beweist man, dass bei Scissors kein Performancegewinn entsteht? Ich erstelle ein normales Spiel. Es benötigt 100% Performance. BlitzMax: [AUSKLAPPEN] Method OnRender%() Jetzt schalte ich die OnRender() komplett ab, indem ich ein RETURN 0 einsetze: BlitzMax: [AUSKLAPPEN] Method OnRender%() Die verbrauchte Leistung sinkt auf 0%. Das beweist, dass die Aktionen des Spiels, die sich bei OnUpdate() befinden, kaum Zeit benötigen. Nun schalte ich einen Scissor ein, der nur 100x100 Pixel groß ist: BlitzMax: [AUSKLAPPEN] Method OnRender%() Leider steigt die verbrauchte Leistung wieder auf 100% Das ist "suboptimal". Ich werde mal mit Mark Kontakt aufnehmen, hier kann man noch nachbessern. Hier noch der komplette Code zum Selbertesten. Es handelt sich wieder um das Arkanoid. Ihr könnt selbst den Scissor ein/ausschalten: BlitzMax: [AUSKLAPPEN] Strict |
||
BBPro2 |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
auch oder gerade weil der Thread bereits ein paar Tage aufm Buckel hat hätte
ich noch ein paar Fragen dazu ![]() 1) ist das noch ein aktuelles Problem ? Ist Monkey immer noch so langsam mit Grafikbefehlen unter HTML5 ? 2) was bringen uns "updates" ohne "rendering" ? also angenommen wir berechnen 60 mal pro sekunde die unterschiede des rechtecks, der figuren und was weiß ich seit dem letzten mal - aber zeichnen sie nur 30 mal pro sekunde. was bringen uns bitte die zwischenberechnungen ? solange sie nicht gezeichnet werden sieht der user sie nicht und sie sind für ihn daher irrelevant. jedes update macht also nur sinn wenn es mit einem entsprechenden render-befehl kommt. vorausgesetzt natürlich die update-funktionen sind halbwegs vernünftig programmiert - sprich frameunabhängig. das ist aber so oder so absolute pflicht, alles andere wäre pfusch. wir sagen also nicht RechteckX = RechteckX + 0.1 pro aufruf, sondern "RechteckX = RechteckX + 5" pro Sekunde (indem wir millisecs abfragen benutzen etc.) sonst ist das ergebnis unseres programms ja ausschließlich von der rechenpower des geräts abhängig und in 5 jahren unspielbar weil alles rum ist bevor es angefangen hat. seh ich da was falsch oder übersehe ich etwas ? |
||
FWeinbehemals "ich" |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Ich kenne mich zwar nicht mit Monkey aus, dafür aber ein wenig mit HTML5 spezieller mit javascript. Ich weiß nicht in wie weit das mit Monkey möglich ist, aber in Javascript macht es durchaus Sinn das Spiel abhängig von requestAnimationFrame zu machen. Damit der Browser bestimmen kann, wann er einen neuen Frame "braucht". Dazu hat Paul Irish einen cross Browser Shim geschrieben:
Code: [AUSKLAPPEN] // shim layer with setTimeout fallback
window.requestAnimFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function( callback ){ window.setTimeout(callback, 1000 / 60); }; })(); Das ganze hat einen Fallback auf einen ganz normalen Timer in Javascript, damit es auch in älteren Browsern funktioniert. Wer mehr darüber lesen will kann das hier tun: http://paulirish.com/2011/requ...animating/ Als Beispiel hier mal eine Navier Stokes Implementation in Javascript von mir. Wenn man dort in einem modernen Browser (aktueller WebKit bzw. Firefox oder auch IE10) den Tab wechselt wird die Berechnung gestoppt. Gruß, ich |
||
"Wenn die Menschen nur über das sprächen, was sie begreifen, dann würde es sehr still auf der Welt sein." Albert Einstein (1879-1955)
"If you live each day as if it was your last, someday you'll most certainly be right." Steve Jobs |
![]() |
Midimaster |
![]() Antworten mit Zitat ![]() |
---|---|---|
Verbesserte Html5-Engines:
Hierzu meine neuen Erfahrungen der letzten 18 Monate: Tatsächlich rendern die modernen Browser schneller als noch vor einem Jahr. Dennoch gibt es immer wieder Monkey-JavaScript/html5 Spiele, bei denen die Performance des Computers bei 100% anschlägt. Ab diesem Moment wird das Spiel langsamer ablaufen, als es vom Autor geplant war. 30fps besser als 60? Ich gebe auch zu bedenken, dass ihr das Zeitverhalten eines eigenen Spiels auf eurem Intel-Super-I7 nicht als Maßstab für die vielen anderen Rechner "draußen im Lande" (Atom, Smartphone, Tablet, etc.., ) hernehmen dürft. Das Entkoppeln bringt hier definitiv gleichmäßigere Ergebnisse auf verschieden leistungsfähigen Geräten. Gerade unter Html5 führt das Entkoppeln zu keinem Flimmern (siehe nächster Absatz) Android-Flimmern Es hat sich auch gezeigt, dass meine im ersten Beitrag beschriebene Technik in einem Monkey-Android-App in einem nicht erträglichen Flimmern endet. Hier muss man also pro OnUpdate() ein OnRender() mit kompletten Bildschirmaufbau durchführen. Leider führt jedes OnUpdate() in Mokey zu einem OnRender(), auch wenn auf dem Bildschirm eigentlich nichts neues zu zeichnen ist. Lässt man nun z.b. jedes zweite Zeichnen eigenmächtig aus, flipped Android trotzdem auf einen zweiten Backbuffer, der einen Zufallsinhalt früherer OnRender() enthält, wodurch sich das Flimmern erklärt. Läßt man z.b. 2 von 3 OnRender() aus wird das Flimmer wieder unsichtbar, da abwechwlnd der eine dann der andere Backbuffer gefüllt wird. Gründe OnUpdate() von OnRender() zu entkoppeln Das Updaten in einer höheren Frequenz als das Rendern bringt ein "quasi"-Delta-Step-Timing. Die Action der Spielfiguren wird nicht durch die fehlende Leistung der Grafikengine gebremst. Bei meinen Games spielt auch immer Musik, die in Echtzeit aus Einzelsounds (Noten) zusammengesetzt wird, eine große Rolle. Auch hier ist ein OnUpdate() mit hoher und stabiler Frequenz (meist 120fps) von Vorteil. Delta-Timing Das echte Delta-Timing hat ja immer den "Nachteil", dass im ungünstigen Fall statt der geplanten X=X+0.5 pro Step auch mal die 10fache Menge addiert (X=X+Delta*0.5) werden kann. Und damit kommt mancher Akteur plötzlich "durch die Mauer durch", weil seine nächster Step schon "hinter" der Mauer liegt. |
||
Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe |
BBPro2 |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
@30fps besser als 60?
Nein den Maßstab sollte man auf keinen Fall anlegen. Das ist richtig. Aber ein System abzubremsen obwohl es mehr schaffen würde ist auch nich unbedingt immer sinnvoll. Zumindest nicht wenn diese künstliche Grenze bei 15 fps liegt. Bei 60 ists ja egal,weil mehr ohnehin nicht wahrgenommen werden würde. Einen User eines High-End Gerätes mit 15fps, Kopfschmerzen und Geruckel zu foltern ist alles andere als optimal @Gründe OnUpdate() von OnRender() zu entkoppeln Ok das mit der Musik ist tatsächlich etwas woran ich nicht gedacht habe. Grunsätzlich wäre es wohl am besten Musik komplett unabhängig von Update und Render zu halten (sprich Threads.) ob und wie das in Monkey möglich ist weiß ich jetzt noch gar nicht, bin noch am reinarbeiten und bei Sound bin ich noch nicht angekommen. Ich bleibe aber dabei, dass ein reines Datenupdate ohne Render sinnfrei ist wenn der Code vernünftig programmiert ist. Das entkräftet auch dein Argument bei Delta-Timing. Der Code MUSS so geschrieben sein, dass eine Figur NICHT durchrutscht. egal wie viel FPS das programm gerade hat. Sonst kann ein einzelner Ruckler immer dazu führen. Wenn das passieren kann ist der Code schlichtweg unfassbar schlecht geschrieben und nicht durchdacht. Das Verhalten eines Programms MUSS gänzlich unabhängig von den fps sein. Basta. ![]() |
||
FWeinbehemals "ich" |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Grade in einen Webbrowser machen spiele mit mehr als 60 FPS keine Sinn, da der Browser selbst nur max. 60FPS Zeichnet.
Wäre schon daran interessiert ob der Ansatz mit requestAnimFrame in Monkey überhaupt möglich ist. Gruß, Ich |
||
"Wenn die Menschen nur über das sprächen, was sie begreifen, dann würde es sehr still auf der Welt sein." Albert Einstein (1879-1955)
"If you live each day as if it was your last, someday you'll most certainly be right." Steve Jobs |
Übersicht


Powered by phpBB © 2001 - 2006, phpBB Group