[Monkey] Performance Lösung unter Html5

Übersicht Andere Programmiersprachen Codearchiv & Module

Neue Antwort erstellen

Midimaster

Betreff: Performance Lösung unter Html5

BeitragSo, März 13, 2011 17:02
Antworten mit Zitat
Benutzer-Profile anzeigen
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
Class ErstesSpiel Extends App

Global Rate%

Method OnCreate% ()
SetUpdateRate 60
Return 0
End

Method OnUpdate% ()
Return 0
End

Method OnRender% ()
If BildRate()>0 Then Return 0
' irgendwas malen
Return 0
End

Function BildRate%()
Rate=(Rate +1) Mod 4
Return Rate
End
End

Function Main% ()
New ErstesSpiel
Return 0
End





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
Class ErstesSpiel Extends App

Global MalenNoetig%

Method OnCreate% ()
BildElemente.CreateAlle()
SetUpdateRate 60
Return 0
End

Method OnUpdate% ()
BildElemente.CheckAlle()
Return 0
End

Method OnRender% ()
If CheckMalenNoetig()=0 Then Return 0
Cls 0,111,0
SetColor 255,0,0
BildElemente.DrawAlle()
Return 0
End

Function CheckMalenNoetig%()
MalenNoetig=MalenNoetig-1
If MalenNoetig< 0 Then
MalenNoetig=0
EndIf
Return MalenNoetig
End

End

Function Main% ()
New ErstesSpiel
Return 0
End

Class BildElemente

Global X%,Xadd%

Function CreateAlle%()
ErstesSpiel.MalenNoetig=20
Return 0
End

Function CheckAlle%()
If KeyHit(KEY_1) Then
Xadd=50
EndIf
If Xadd>0 Then
X=X+1
Xadd=Xadd-1
ErstesSpiel.MalenNoetig=2
EndIf
Return 0
End

Function DrawAlle%()
SetColor 0,0,255
DrawRect X,100,100,100
Return 0
End

End


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
Import mojo

Class ErstesSpiel Extends App

Global MalenNoetig%=20, Rate%, Hertz%, Immer%=1

Method OnCreate% ()
New BildElemente
BildElemente.CreateAlle()
SetUpdateRate 60
Return 0
End

Method OnUpdate% ()
BildElemente.CheckAlle()
If KeyHit(KEY_2) Then
Hertz=1-Hertz
MalenNoetig=2
EndIf
If KeyHit(KEY_3) Then
Immer=1-Immer
MalenNoetig=2
EndIf
Return 0
End

Method OnRender% ()
If BildRate()>0 Then Return 0
If CheckMalenNoetig()=0 Then Return 0

Cls 0,111,0
SetColor 255,0,0
BildElemente.DrawAlle()
DrawText "Press 1 to move",40,40
If Hertz=1 Then
DrawText "Press 2 to switch 60Hz/15Hz akt= 15Hz",40,60
Else
DrawText "Press 2 to switch 60Hz/15Hz akt= 60Hz",40,60
EndIf
If Immer=0 Then
DrawText "Press 3 to switch Malen-Nötig-Check akt= ON",40,80
Else
DrawText "Press 3 to switch Malen-Nötig-Check akt= OFF",40,80
EndIf
Return 0
End

Function CheckMalenNoetig%()
If Immer=1 Then Return 1
MalenNoetig=MalenNoetig-1
If MalenNoetig< 0 Then
MalenNoetig=0
EndIf
Return MalenNoetig
End

Function BildRate%()
Rate=(Rate +1) Mod (Hertz*3+1) ' Mod4 oder Mod1
Return Rate
End
End

Function Main% ()
New ErstesSpiel
Return 0
End



Class BildElemente

Global X%,Xadd%

Function CreateAlle%()
ErstesSpiel.MalenNoetig=20
Return 0
End

Function CheckAlle%()
If KeyHit(KEY_1) Then
Xadd=50
EndIf
If Xadd>0 Then
X=X+1
Xadd=Xadd-1
ErstesSpiel.MalenNoetig=2
EndIf
Return 0
End

Function DrawAlle%()
SetColor 0,0,255
DrawRect X,100,100,100
Return 0
End

End


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 Stweccys

Betreff: Re: Performance Lösung unter Html5

BeitragSo, März 13, 2011 17:20
Antworten mit Zitat
Benutzer-Profile anzeigen
Midimaster hat Folgendes geschrieben:
A. Reduzierung des Bildschirmaufbaus auf 15Hz


Geht das nicht einfacher, indem man bloß
BlitzBasic: [AUSKLAPPEN]
SetUpdateRate 15

macht?

Midimaster

BeitragSo, März 13, 2011 17:29
Antworten mit Zitat
Benutzer-Profile anzeigen
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

BeitragSo, März 13, 2011 18:08
Antworten mit Zitat
Benutzer-Profile anzeigen
Oh, natürlich, wie dumm von mir XD
 

Macintosh

BeitragMo, März 14, 2011 2:04
Antworten mit Zitat
Benutzer-Profile anzeigen
15Hz ruckelt doch aber ;) seiht es nicht erst ab 30Hz flüssig aus? oder irre ich mich.

Midimaster

BeitragMo, März 14, 2011 9:01
Antworten mit Zitat
Benutzer-Profile anzeigen
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%()
Rate=(Rate +1) Mod 2
Return Rate
End


für eine Veränderung auf 40Hz müßte nur soviel geändert werden:
BlitzMax: [AUSKLAPPEN]
	Function BildRate%()
Rate=(Rate+1) Mod 2
If Rate=0 Then Return 1
Return 0
End

Midimaster

Betreff: Reduzierung des Bildschirmaufbaus auf Bereiche

BeitragDi, März 15, 2011 20:40
Antworten mit Zitat
Benutzer-Profile anzeigen
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. Wink Und das spart dann auch (Atom-)Energie!

Das komplette Spiel:

BlitzMax: [AUSKLAPPEN]
Strict
Import mojo

Class Haupt Extends App

Global X_MAX%=500,Y_MAX%=400,X_OFF%=10,Y_OFF%=10
Global Region%[10]

Global Mode%=0 ' hier den Demo-Mode umschalten
' 0 = Spiel läuft wie bei User

' 1 = Spiel zeigt nur dazukommende Grafikbefehle

' 2 = Spiel zeigt nur was jeweils während eines "Resfresh"
' gezeichnet wird. (die Performance wird hier durch Cls
' verhagelt. Echte Performance siehe Mode 0)


Method OnCreate%()
Seed = MilliSecs()
New All
All.Create
SetUpdateRate 60
Region[9]=1
Return 0
End



Method OnUpdate%()
All.Check
Return 0
End



Method OnRender%()

If Region[9]=1 And Mode=0
Cls 55,55,55
SetColor 0,111,0
DrawRect X_OFF,Y_OFF,X_MAX,Y_MAX
SetColor 0,0,111
For Local i%=1 To 9
Region[i]=1
Next
All.Draw
SetColor 111,0,0
DrawRect X_OFF,Y_OFF+Y_MAX,X_MAX,20
SetColor 55,55,55
EndIf
If Mode=2 Then Cls
SetScissor X_OFF,Y_OFF,X_MAX,Y_MAX
SetColor 0,111,0
If Region[1]=1 Then
SetColor 0,111,0
DrawRect All.BallX-48,All.BallY-9-20,126,40+30
'DrawRect All.BallX-5,All.BallY-9,30,38
Region[2]=0
EndIf
If Region[2]=1 Then
SetColor 0,111,0
DrawRect All.BallX-5,All.BallY-9,30,38
EndIf
If Region[3]=1 Then
SetColor 0,111,0
DrawRect All.AltX-50,Y_OFF+Y_MAX-21,All.SCHLAEGERBREIT+100,16
SetColor 55,55,55
EndIf
All.Draw
Return 0
End



End



Function Main%()
New Haupt
Return 0
End




Class All

Field X#,Y#,Xadd#,Yadd#,Typ%,Speed#,Farbe%
Const SCHLAEGERBREIT%=60, STEIN_BREIT%=50
Global Liste: List <All>
Const BALL%=1, SCHLAEGER%=2, STEIN%=3

Global SperrX%,AltX%,X_Max%, Y_Max%,X_Off%,Y_Off%, Cont%,Level%=1

Global BallX%,BallY%





Function Create%()
Liste = New List <All>
X_Max=Haupt.X_MAX
Y_Max=Haupt.Y_MAX
X_Off=Haupt.X_OFF
Y_Off=Haupt.Y_OFF
CreateOne 50,30,BALL
CreateOne 50,180,SCHLAEGER
For Local j%=0 To Level
For Local i%=0 To 8
CreateOne 10+i*53,100-Level*20+j*20,STEIN
Next
Next
Return 0
End




Function CreateOne:All(X%,Y%,Typ%)
Local loc:All= New All()
loc.X=X
loc.Y=Y
loc.Typ=Typ
Select Typ
Case BALL
loc.Yadd=5
loc.Y=300
Case SCHLAEGER

Case STEIN
loc.Farbe=Rnd(Level+1)
End Select

Liste.AddLast loc
Return loc
End




Function Check%()
If MouseHit()
Cont=1
End
For Local loc:All = EachIn Liste
loc.CheckOne
Next
Return 0
End



Method CheckOne%()
Select Typ
Case BALL
Local VorX%=Int(X)
Local VorY%=Int(Y)
If Cont=0 Then Return 0
Yadd=Yadd+0.04
X=X+Xadd*1
Y=Y+Yadd*1
If (X<4) Or X>(X_Max-4)
Haupt.Region[4]=1
Haupt.Region[5]=1
Xadd=-Xadd
X=X+Xadd
EndIf
If (Y<0) Then
Yadd=-Yadd
Y=Y+Yadd
End
If (Y<120) Then
Local Summe%
For Local loc:All = EachIn Liste


If loc.Typ=STEIN
Summe=Summe+1
If (X>loc.X-4) And (X<loc.X+STEIN_BREIT+4) Then
If (Y>loc.Y-4) And (Y<loc.Y+17+4) Then


If (X>loc.X+4) And (X<loc.X+STEIN_BREIT-4) Then
Yadd=-Yadd
Y=Y+Yadd
Else If (Y>loc.Y+4) And (Y<loc.Y+17-4)
Xadd=-Xadd
X=X+Xadd
Else
If (Xadd<0) And (X>loc.X+STEIN_BREIT/2)
Xadd=-Xadd
X=X+Xadd
Else If (Xadd>0) And (X<loc.X+STEIN_BREIT/2)
Xadd=-Xadd
X=X+Xadd
EndIf
If (Yadd<0) And (Y>loc.Y+8)
Yadd=-Yadd
Y=Y+Yadd
Else If (Yadd>0) And (Y<loc.Y+8)
Yadd=-Yadd
Y=Y+Yadd
EndIf
EndIf
loc.Farbe=loc.Farbe-1
If loc.Farbe=-1 Then
Liste.Remove loc
EndIf
'Cont=0
Haupt.Region[1]=1
Return 0


EndIf
EndIf
EndIf


Next
If Summe=0 Then
Level=Level+1
Create()
EndIf

End
If (Y>Y_Max+300) Then
Y=Y_Max-50
X=SperrX+SCHLAEGERBREIT/2
Yadd=5.3
Xadd=Rnd(2)-1
Cont=0

End
If (Y>Y_Max-20-4) And (Y<Y_Max-15) And
(X>SperrX) And X<(SperrX+SCHLAEGERBREIT)
Yadd=-Yadd+0.05
Xadd=(X-SperrX-SCHLAEGERBREIT/2)*0.1
Y=Y+Yadd
EndIf
If Y>Y_Max-40
If Y < Y_Max+21+16
Haupt.Region[3]=1
End
End
If (Y<140)
Haupt.Region[1]=1
EndIf
If (Y>130) And (Y<Y_Max+30)
If (VorX<>Int(X)) Or (VorY<>Int(Y))
Haupt.Region[2]=1
End
EndIf
BallX=Int(X)
BallY=Int(Y)
Case SCHLAEGER
AltX=Int(X)
Local RealMaus%
RealMaus=MouseX()-X_Off-SCHLAEGERBREIT/2
X=X-(X-RealMaus)/10

If X<0 Then
X=0
EndIf
If X>X_Max-SCHLAEGERBREIT Then
X=X_Max-SCHLAEGERBREIT
EndIf
SperrX=X
If AltX<>Int(X) Then
Haupt.Region[3]=1
EndIf
Case STEIN

End Select
Return 0
End




Function Draw%()
For Local loc:All = EachIn Liste
loc.DrawOne
Next
Haupt.Region[1]=0
Haupt.Region[9]=0
Return 0
End



Method DrawOne%()
Select Typ
Case BALL
If Haupt.Region[2]=1 Or Haupt.Region[1]=1
Haupt.Region[2]=0

SetAlpha 0.3
SetColor 1,1,1
DrawCircle X+X_Off+3,Y+Y_Off+3,5
SetAlpha 1
SetColor 0,0,255
DrawCircle X+X_Off,Y+Y_Off,5
SetColor 111,111,255
DrawCircle X+X_Off-1,Y+Y_Off-1,3
SetColor 222,222,255
DrawCircle X+X_Off-2,Y+Y_Off-2,1
EndIf
Case SCHLAEGER
If Haupt.Region[3]=1
Haupt.Region[3]=0
SetAlpha 0.3
SetColor 1,1,1
DrawRect X+X_Off+3,Y_Max+Y_Off-20+3,SCHLAEGERBREIT,10
SetAlpha 1
SetColor 222,222,222
DrawRect X+X_Off,Y_Max+Y_Off-20,SCHLAEGERBREIT,10
SetColor 155,155,155
DrawRect X+X_Off+1,Y_Max+Y_Off-20+1,SCHLAEGERBREIT-1,9
SetColor 199,199,199
DrawRect X+X_Off+2,Y_Max+Y_Off-20+2,SCHLAEGERBREIT-3,7
EndIf
Case STEIN
If Haupt.Region[1]=0 Then Return 0
If (X<BallX-110) Or (X>BallX+110) And (Haupt.Region[9]=0)
Return 0
EndIf
SetColor 0,111,0
DrawRect X+X_Off,Y_Off+Y,STEIN_BREIT+3,17+3
SetAlpha 0.3
SetColor 1,1,1
DrawRect X+X_Off+3,Y_Off+Y+3,STEIN_BREIT,17
SetAlpha 1
SetColor 155,155,222
DrawRect X+X_Off,Y_Off+Y,STEIN_BREIT,17
Select Farbe
Case 0
SetColor 0,111,111
Case 1
SetColor 111,0,111
Case 2
SetColor 111,111,0
Default
SetColor 111,0,0
End Select
DrawRect X+X_Off+1,Y_Off+Y+1,STEIN_BREIT-1,16
Select Farbe
Case 0
SetColor 0,188,188
Case 1
SetColor 188,0,188
Case 2
SetColor 188,188,0
Default
SetColor 188,0,0
End Select
DrawRect X+X_Off+2,Y_Off+Y+2,STEIN_BREIT-3,15
End Select

Return 0
End

End



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%()
Select Typ
Case BALL
.....
Case SCHLAEGER
AltX=Int(X)
X=Mouse....
If AltX<>Int(X) Then
Haupt.Region[3]=1
EndIf
Case ....


... geht das Flag Region[..] auf 1. Dadurch wird der Hintergrund restauriert...
BlitzMax: [AUSKLAPPEN]
Method OnRender%()
.....
If Region[3]=1 Then
SetColor 0,111,0
DrawRect All.AltX-50,Y_OFF+Y_MAX-21,All.SCHLAEGERBREIT+100,16
SetColor 55,55,55
EndIf

...und dann in den Draw()'s die Objekte wirklich gezeichnet.
BlitzMax: [AUSKLAPPEN]
Method DrawOne%()
Select Typ
Case BALL
.....
Case SCHLAEGER
If Haupt.Region[3]=1
Haupt.Region[3]=0
SetAlpha 0.3
SetColor 1,1,1
DrawRect X+X_Off+3,Y_Max+Y_Off-20+3,SCHLAEGERBREIT,10
......
EndIf
Case .......


Das virtuelle Region[9]-Flag dient nur dazu am Spielbeginn das komplette Zeichnen aller Elemente einmal zu erzwingen:
BlitzMax: [AUSKLAPPEN]
    Method OnRender%()

If Region[9]=1 .....
Cls 55,55,55
SetColor 0,111,0
DrawRect X_OFF,Y_OFF,X_MAX,Y_MAX
SetColor 0,0,111
For Local i%=1 To 9
Region[i]=1
Next
All.Draw
SetColor 111,0,0
DrawRect X_OFF,Y_OFF+Y_MAX,X_MAX,20
SetColor 55,55,55
EndIf



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.

Midimaster

Betreff: Scissor sind leider keine Lösung

BeitragMi, März 16, 2011 12:19
Antworten mit Zitat
Benutzer-Profile anzeigen
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%()
'Return 0
'SetScissor 200,50,100,100
Cls 55,55,55
SetColor 0,111,0
DrawRect X_OFF,Y_OFF,X_MAX,Y_MAX
SetColor 0,0,111
All.Draw....
SetColor 111,0,0
DrawRect X_OFF,Y_OFF+Y_MAX,X_MAX,20
SetColor 55,55,55
Return 0
End


Jetzt schalte ich die OnRender() komplett ab, indem ich ein RETURN 0 einsetze:
BlitzMax: [AUSKLAPPEN]
     Method OnRender%()
Return 0
'SetScissor 200,50,100,100
Cls 55,55,55
SetColor 0,111,0
DrawRect X_OFF,Y_OFF,X_MAX,Y_MAX
SetColor 0,0,111
All.Draw
SetColor 111,0,0
DrawRect X_OFF,Y_OFF+Y_MAX,X_MAX,20
SetColor 55,55,55
Return 0
End

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%()
'Return 0
SetScissor 200,50,100,100
Cls 55,55,55
SetColor 0,111,0
DrawRect X_OFF,Y_OFF,X_MAX,Y_MAX
SetColor 0,0,111
All.Draw
SetColor 111,0,0
DrawRect X_OFF,Y_OFF+Y_MAX,X_MAX,20
SetColor 55,55,55
Return 0
End

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
Import mojo




Class Haupt Extends App

Global X_MAX%=500,Y_MAX%=400,X_OFF%=10,Y_OFF%=10

Method OnCreate%()
Seed = MilliSecs()
New All
All.Create
SetUpdateRate 60
Return 0
End



Method OnUpdate%()
All.Check
Return 0
End



Method OnRender%()
'Return 0
'SetScissor 200,50,100,100
Cls 55,55,55
SetColor 0,111,0
DrawRect X_OFF,Y_OFF,X_MAX,Y_MAX
SetColor 0,0,111
All.Draw
SetColor 111,0,0
DrawRect X_OFF,Y_OFF+Y_MAX,X_MAX,20
SetColor 55,55,55
Return 0
End




End



Function Main%()
New Haupt
Return 0
End




Class All

Field X#,Y#,Xadd#,Yadd#,Typ%,Speed#,Farbe%
Const SCHLAEGERBREIT%=60, STEIN_BREIT%=50
Global Liste: List <All>
Const BALL%=1, SCHLAEGER%=2, STEIN%=3

Global SperrX%,AltX%,X_Max%, Y_Max%,X_Off%,Y_Off%, Cont%,Level%=1

Global BallX%,BallY%





Function Create%()
Liste = New List <All>
X_Max=Haupt.X_MAX
Y_Max=Haupt.Y_MAX
X_Off=Haupt.X_OFF
Y_Off=Haupt.Y_OFF
CreateOne 50,30,BALL
CreateOne 50,180,SCHLAEGER
For Local j%=0 To Level
For Local i%=0 To 8
CreateOne 10+i*53,100-Level*20+j*20,STEIN
Next
Next
Return 0
End




Function CreateOne:All(X%,Y%,Typ%)
Local loc:All= New All()
loc.X=X
loc.Y=Y
loc.Typ=Typ
Select Typ
Case BALL
loc.Yadd=5
loc.Y=300
Case SCHLAEGER

Case STEIN
loc.Farbe=Rnd(Level+1)
End Select

Liste.AddLast loc
Return loc
End




Function Check%()
If MouseHit()
Cont=1
End
For Local loc:All = EachIn Liste
loc.CheckOne
Next
Return 0
End



Method CheckOne%()
Select Typ
Case BALL
Local VorX%=Int(X)
Local VorY%=Int(Y)
If Cont=0 Then Return 0
Yadd=Yadd+0.04
X=X+Xadd*1
Y=Y+Yadd*1
If (X<4) Or X>(X_Max-4)
Xadd=-Xadd
X=X+Xadd
EndIf
If (Y<0) Then
Yadd=-Yadd
Y=Y+Yadd
End
If (Y<120) Then
Local Summe%
For Local loc:All = EachIn Liste


If loc.Typ=STEIN
Summe=Summe+1
If (X>loc.X-4) And (X<loc.X+STEIN_BREIT+4) Then
If (Y>loc.Y-4) And (Y<loc.Y+17+4) Then


If (X>loc.X+4) And (X<loc.X+STEIN_BREIT-4) Then
Yadd=-Yadd
Y=Y+Yadd
Else If (Y>loc.Y+4) And (Y<loc.Y+17-4)
Xadd=-Xadd
X=X+Xadd
Else
If (Xadd<0) And (X>loc.X+STEIN_BREIT/2)
Xadd=-Xadd
X=X+Xadd
Else If (Xadd>0) And (X<loc.X+STEIN_BREIT/2)
Xadd=-Xadd
X=X+Xadd
EndIf
If (Yadd<0) And (Y>loc.Y+8)
Yadd=-Yadd
Y=Y+Yadd
Else If (Yadd>0) And (Y<loc.Y+8)
Yadd=-Yadd
Y=Y+Yadd
EndIf
EndIf
loc.Farbe=loc.Farbe-1
If loc.Farbe=-1 Then
Liste.Remove loc
EndIf
'Cont=0
Return 0


EndIf
EndIf
EndIf


Next
If Summe=0 Then
Level=Level+1
Create()
EndIf

End
If (Y>Y_Max+300) Then
Y=Y_Max-50
X=SperrX+SCHLAEGERBREIT/2
Yadd=5.3
Xadd=Rnd(2)-1
Cont=0

End
If (Y>Y_Max-20-4) And (Y<Y_Max-15) And
(X>SperrX) And X<(SperrX+SCHLAEGERBREIT)
Yadd=-Yadd+0.05
Xadd=(X-SperrX-SCHLAEGERBREIT/2)*0.1
Y=Y+Yadd
EndIf
BallX=Int(X)
BallY=Int(Y)
Case SCHLAEGER
AltX=Int(X)
Local RealMaus%
RealMaus=MouseX()-X_Off-SCHLAEGERBREIT/2
X=X-(X-RealMaus)/10

If X<0 Then
X=0
EndIf
If X>X_Max-SCHLAEGERBREIT Then
X=X_Max-SCHLAEGERBREIT
EndIf
SperrX=X
Case STEIN

End Select
Return 0
End




Function Draw%()
For Local loc:All = EachIn Liste
loc.DrawOne
Next
Return 0
End



Method DrawOne%()
Select Typ
Case BALL
SetAlpha 0.3
SetColor 1,1,1
DrawCircle X+X_Off+3,Y+Y_Off+3,5
SetAlpha 1
SetColor 0,0,255
DrawCircle X+X_Off,Y+Y_Off,5
SetColor 111,111,255
DrawCircle X+X_Off-1,Y+Y_Off-1,3
SetColor 222,222,255
DrawCircle X+X_Off-2,Y+Y_Off-2,1
Case SCHLAEGER
SetAlpha 0.3
SetColor 1,1,1
DrawRect X+X_Off+3,Y_Max+Y_Off-20+3,SCHLAEGERBREIT,10
SetAlpha 1
SetColor 222,222,222
DrawRect X+X_Off,Y_Max+Y_Off-20,SCHLAEGERBREIT,10
SetColor 155,155,155
DrawRect X+X_Off+1,Y_Max+Y_Off-20+1,SCHLAEGERBREIT-1,9
SetColor 199,199,199
DrawRect X+X_Off+2,Y_Max+Y_Off-20+2,SCHLAEGERBREIT-3,7
Case STEIN
SetColor 0,111,0
DrawRect X+X_Off,Y_Off+Y,STEIN_BREIT+3,17+3
SetAlpha 0.3
SetColor 1,1,1
DrawRect X+X_Off+3,Y_Off+Y+3,STEIN_BREIT,17
SetAlpha 1
SetColor 155,155,222
DrawRect X+X_Off,Y_Off+Y,STEIN_BREIT,17
Select Farbe
Case 0
SetColor 0,111,111
Case 1
SetColor 111,0,111
Case 2
SetColor 111,111,0
Default
SetColor 111,0,0
End Select
DrawRect X+X_Off+1,Y_Off+Y+1,STEIN_BREIT-1,16
Select Farbe
Case 0
SetColor 0,188,188
Case 1
SetColor 188,0,188
Case 2
SetColor 188,188,0
Default
SetColor 188,0,0
End Select
DrawRect X+X_Off+2,Y_Off+Y+2,STEIN_BREIT-3,15
End Select

Return 0
End

End
 

BBPro2

BeitragFr, Aug 17, 2012 4:48
Antworten mit Zitat
Benutzer-Profile anzeigen
auch oder gerade weil der Thread bereits ein paar Tage aufm Buckel hat hätte
ich noch ein paar Fragen dazu Wink

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 ?
 

FWeinb

ehemals "ich"

BeitragFr, Aug 17, 2012 11:33
Antworten mit Zitat
Benutzer-Profile anzeigen
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

BeitragSa, Aug 18, 2012 1:57
Antworten mit Zitat
Benutzer-Profile anzeigen
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

BeitragSa, Aug 18, 2012 4:14
Antworten mit Zitat
Benutzer-Profile anzeigen
@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. Wink
 

FWeinb

ehemals "ich"

BeitragSo, Aug 19, 2012 12:10
Antworten mit Zitat
Benutzer-Profile anzeigen
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

Neue Antwort erstellen


Übersicht Andere Programmiersprachen Codearchiv & Module

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group