Seite 2 von 2
Verfasst: 05.06.2009 13:47
von Rokur
Auf bei Vollbildscreens kann es vorkommen das die Grafikausgabe durch andere Programme überdeckt wird und daher neu gezeichnet werden muss, z.B. wenn der Anwender per Alt+Tab wechselt, oder sich eine andere Anwendung per Popup in den Vordergrund drängt.
Was ist daran nicht "ökologisch"? Wenn du pro Schleifendurchlauf ein Delay(1) einbaust, dann frisst das Programm auch nicht die ganze CPU-Leistung (ohne den Delay hättest du allerdings eine Auslastung von 100%). Der Umstand das CPU und Grafikkarte dennoch ständig was zu tun haben stört auch nicht weiter, denn wenn dein Programm im Vordergrund läuft benötigt eh kein anderes Programm die Grafikkarte.
Wenn du etwas zeitgesteuert machen willst brauchst du so oder so einen Timer.
/edit: Hier mal ein Beispiel mit Doublebuffering und Zeitsteuerung. Ich habe zwar statt Sprites einfache Rechtecke per Box() ausgegeben, aber das Prinzip dürfte klar sein.
Code: Alles auswählen
#app = "Doublebuffering Beispiel"
InitSprite()
InitKeyboard()
ExamineDesktops()
OpenScreen(DesktopWidth(0),DesktopHeight(0),DesktopDepth(0),#app)
Define quit.l = #False
LoadFont(0,"Courier New",12)
Structure sprite_daten
x.l
y.l
width.l
height.l
color.l
EndStructure
Define sprites_gezeichnet.l = -1
Define sprite_delay.l = 2000
Define sprite_timer.l = ElapsedMilliseconds() + sprite_delay
Dim sprites.sprite_daten(3)
sprites(0)\x = 55
sprites(0)\y = 85
sprites(0)\width = 30
sprites(0)\height = 10
sprites(0)\color = RGB(255,0,0)
sprites(1)\x = 90
sprites(1)\y = 90
sprites(1)\width = 40
sprites(1)\height = 50
sprites(1)\color = RGB(0,255,0)
sprites(2)\x = 155
sprites(2)\y = 105
sprites(2)\width = 25
sprites(2)\height = 90
sprites(2)\color = RGB(0,0,255)
sprites(3)\x = 200
sprites(3)\y = 120
sprites(3)\width = 40
sprites(3)\height = 50
sprites(3)\color = RGB(0,0,0)
Repeat
Delay(1) ;wg. CPU-Auslastung
FlipBuffers()
ClearScreen(RGB(127,127,127))
;Fenster zeichnen
StartDrawing(ScreenOutput())
DrawingMode(#PB_2DDrawing_Default)
Box(50,50,500,300,RGB(100,100,100))
DrawingMode(#PB_2DDrawing_Outlined)
Box(50,50,500,300,RGB(0,0,0))
DrawingMode(#PB_2DDrawing_Transparent)
DrawingFont(FontID(0))
DrawText(55,55,#app,RGB(255,255,255))
;Fensterinhalte zeichnen
For i = 0 To sprites_gezeichnet - 1
Box(sprites(i)\x,sprites(i)\y,sprites(i)\width,sprites(i)\height,sprites(i)\color)
Next i
If ElapsedMilliseconds() >= sprite_timer And sprites_gezeichnet <= ArraySize(sprites(),1)
sprites_gezeichnet+1
sprite_timer = ElapsedMilliseconds() + sprite_delay
EndIf
StopDrawing()
;Benutzereingaben verarbeiten
ExamineKeyboard()
If KeyboardPushed(#PB_Key_Escape)
quit = #True
EndIf
Until quit = #True
Verfasst: 05.06.2009 15:57
von Kurzer
Also die Aussage "ökologisch" bezog sich für meinen Teil auf das "Bauchgefühl eines Puristen/Minimalisten", der ich mal war [Amiga, lange her usw.].
Wieviel mehr an Strom durch diese Schleife verbraucht wird, wird vermutlich nur schwer meßbar sein, aber "es fühlt sich für mich einfach nicht gut an", wenn ich ein Standbild 75 mal pro Sekunde hinzeichnen muß, um es sofort danach wieder zu löschen, wieder zu zeichnen usw...
Doubble buffering ist in vielen Anwendungsfällen die bessere Methode, aber meiner Erfahrung nach eben nicht immer. Es kann Dinge auch verkomplizieren und die CPU-Last in die Höhe treiben.
Okay, wenn der Anwender den Desktop nach vorn geholt hat und der Screen dadurch überschrieben wurde, dann muß man natürlich alles neuzeichnen.
Aber um mal bei meinem Beispiel des Graphen zu bleiben.
Angenommen, Du hasten einen Screen mit 800 x 600 Pixel und willst darauf gemessene Temperaturwerte als fortlaufen Graphen anzeigen (Oszilloscope, EKG, Krankenhaus.. ich denke es ist klar was gemeint ist).
Der Graph soll beginnend am linken Rand des Screens hin zum rechten Rand gezeichnet werden (X-Achse). Die höhe der Temperatur wird dabei auf der Y-Achse eingetragen. Weiterhin wird jede Sekunde eine Messung vorgenommen.
Nach "alter Methode" würde man den gemessenen Wert nun einfach an aktueller X Position gemäß Temperaturwert auf der Y-Achse plotten und den X Zähler um einen Pixel erhöhen. Dann wieder eine Messung durchführen, den Wert eintragen (jetzt an X Position 1), den X Zähler erhöhen usw.
Am rechten Rand angekommen setzt man den X Zähler zurück.
(Vor dem plotten des Pixels wird natürlich die aktuelle Spalte gelöscht, also quasi eine schwarze Linie von X,Ymin nach X,Ymax gezogen, damit der vorherige Pixel gelöscht wird.
Mit doubble buffering musst Du Deine Messwerte erst in ein internes Array schreiben (Speicherbedarf steigt) und dann mußt Du bei jedem Framedurchlauf (nicht nur bei jeder Messung, nein bei jedem Framedurchlauf!) den gesamten Graphen von X=0 bis zur aktuellen X Position aus dem Array lesen und ihn auf den backbuffer plotten.
Man spart sich zwar das Löschen des alten Pixels, aber um welchen Preis? -> Ein vielfaches an CPU-Last und komplizierter Code.
Du mußt dafür extra noch eine Schleife schreiben, die Dir die Pixel wieder restauriert, welche Du gezwungenermaßen gerade eben erst mit einem ClearScreen gelöscht hast.
Von daher stehe ich noch immer dazu: Es wäre schön, wenn man auch die Möglichkeit hätte ohne double buffer zu arbeiten.
Verfasst: 05.06.2009 16:42
von Rokur
Und wenn der Benutzer zum Desktop wechselt oder sich ein anderes Programm in den Vordergrund drängt, dann sind alle Messwerte weg.
Ein Array mit einer Hand von Zahlen zu durchlaufen und die paar Pixel zu zeichnen, das schafft auch ein 386er noch ohne Mühe, die CPU-Auslastung dürfte mit dem Delay(1) wenige Prozent nicht übersteigen.
Der zusätzliche Speicherbedarf für ein paar Werte beträgt auch nur einen Bruchteil des restlichen Programms, weil du den Grafikspeicher usw. ja sowiso brauchst, egal wie oft du darauf zeichnest.
Ein Array hätte übrigens den Vorteil, jederzeit auf vergangene Messwerte zugreifen zu können, wenn du z.B. den Durchschnittswert der letzten 10 Messungen mit anzeigen willst usw., das brauchst du vielleicht am Anfang nicht, aber wenn das Programm wächst bist du froh wenn du nicht wieder alles komplett umbauen musst.
Aber ich verstehe was du meinst. Der Umstieg auf neue Technologien oder Prinzipien bedarf erst mal einer Eingewöhnung. Das ist das Gleiche wie wenn man jahrelang prozedural programmiert und dann auf eine objektorientierte Sprache umsteigst. Da klatscht man seinen Spaghetti-Code auch erstmal in wenige, sehr lange Methoden, so wie man das gewöhnt ist. Die Aufteilung in viele Objekte aus nur wenigen Zeilen mit Vererbungen usw. kommt einem erstmal wahnsinnig sinnlos und kompliziert vor. Aber nach und nach erschließt sich einem dann der Nutzen und irgendwann ist es ganz normal so zu arbeiten.
Wenn du ohne Doublebuffering arbeiten möchtest, wieso benutzt du dann einen Screen? Ein Rahmenloses, maximiertes Fenster würds da genauso tun.
Verfasst: 05.06.2009 17:05
von Kurzer
Rokur hat geschrieben:Wenn du ohne Doublebuffering arbeiten möchtest, wieso benutzt du dann einen Screen? Ein Rahmenloses, maximiertes Fenster würds da genauso tun.
Will ich momentan ja gar nicht, der Originalposter in diesem Beitrag ist CptGreenwood.
Aber ich kann seine Beweggründe verstehen und hab mich einfach bewogen gefühlt, sein Anliegen mit eigenen Worten noch einmal zu verdeutlichen.
Klar geht das auch mit einem rahmenlosen Fenster, aber ich denke die Diskussion wird jetzt zu philosophisch.
Es geht nicht und damit ist der Fisch nunmal geputzt.
Verfasst: 06.06.2009 02:45
von Kaeru Gaman
einfach mit einem rahmenlosen fenster ist disfug,
denn der fensterhintergrund
wird definitiv gelöscht wenn man ein fremdfenster drüberzieht.
also, wenn man auf eine Fensteroberfläche darstellen will, plotte man auf ein Image und stellt das in einem ImageGadget dar,
da fällt dann das DoubleBuffering komplett weg, und es treten auch keine timing-probleme auf.
http://www.purebasic.fr/german/viewtopic.php?t=19394
das DoubleBuffering umgeht man "under the hood" ebenfalls, wenn man auf einem modernen system (XP oder höher, DX7 oder höher) einen Windowedscreen verwendet!
der benutzt nämlich letztendlich gar keinen DoubleBuffer mehr, sondern
simuliert ihn nur!
Code: Alles auswählen
#TimeStep = 50
#Width = 800
#Height = 600
InitSprite()
OpenWindow(0, 0,0, #Width,#Height, "Windowed Screen")
OpenWindowedScreen( WindowID(0), 0,0, #Width,#Height, 0,0,0)
Timer = ElapsedMilliseconds() + #TimeStep
Repeat
Event = WaitWindowEvent(10)
Select Event
Case #Null
If Timer < ElapsedMilliseconds()
Timer + #TimeStep
N + #Width / 100
X = N % #Width
Y = #Height / 2 + #Height / 2.5 * Sin( N / ( #Width / 10 ) )
StartDrawing( ScreenOutput() )
Circle( X, Y, #Height / 40 , $C04020 )
Circle( X, Y, #Height / 60 , $40C0F0 )
StopDrawing()
FlipBuffers()
EndIf
Case #PB_Event_CloseWindow
EXIT = 1
EndSelect
Until EXIT
Verfasst: 06.06.2009 11:13
von Burstnibbler
Nur mal so aus Spaß : Bedingungsabhängiges Flipbuffern
Code: Alles auswählen
EnableExplicit
;
Enumeration
#Sprite_BG
#Sprite_Ovl1
#Sprite_Ovl2
EndEnumeration
#Trigger=2000 ; Millisekunden
;
Define.l Y, Timer, Counter, FlipCounter, Draw=#True
;
InitSprite()
InitKeyboard()
;
OpenScreen(800,600,32,"Flipper")
CreateSprite(#Sprite_BG,800,600,0)
CreateSprite(#Sprite_Ovl1,300,150,0)
CreateSprite(#Sprite_Ovl2,250,100,0)
;
StartDrawing(SpriteOutput(#Sprite_BG))
For Y=0 To 599
Line(0,Y,800,1,RGB(255*Y/599,0,0))
Next
StopDrawing()
StartDrawing(SpriteOutput(#Sprite_Ovl1))
Box(0,0,300,150,#Blue)
StopDrawing()
StartDrawing(SpriteOutput(#Sprite_Ovl2))
Box(0,0,250,100,#White)
StopDrawing()
;
Timer=ElapsedMilliseconds()
;
Repeat
ExamineKeyboard()
;
If Draw
FlipCounter+1 : Draw!1
DisplaySprite(#Sprite_BG,0,0)
StartDrawing(ScreenOutput())
DrawingMode(#PB_2DDrawing_Transparent)
DrawText(0,0,"Flipbuffers-Aufrufe: "+Str(FlipCounter),#White)
StopDrawing()
Select Counter
Case 1
DisplaySprite(#Sprite_Ovl1,250,225)
Case 2
DisplaySprite(#Sprite_Ovl1,250,225)
DisplaySprite(#Sprite_Ovl2,275,250)
EndSelect
FlipBuffers(2)
EndIf
;
If ElapsedMilliseconds()-Timer>=#Trigger
Timer=ElapsedMilliseconds()
Draw!1 : Counter+1 : Counter%3
EndIf
;
Until KeyboardPushed(#PB_Key_Escape)
Mfg,
Burstnibbler
Verfasst: 06.06.2009 22:55
von Kurzer
Kaeru Gaman hat geschrieben:...das DoubleBuffering umgeht man "under the hood" ebenfalls, wenn man auf einem modernen system (XP oder höher, DX7 oder höher) einen Windowedscreen verwendet!
der benutzt nämlich letztendlich gar keinen DoubleBuffer mehr, sondern simuliert ihn nur!

Alter! Das isses doch.

Gut zu wissen!
Und guck mal, wenn wir das noch genau so groß machen wie den Desktop und das Fenster rahmenlos setzen... was hamma dann?
Einen Quasi-Screen ohne Dubble-Buffer. Geil-o-mat!
Code: Alles auswählen
ExamineDesktops()
#TimeStep = 50
DWidth.i = DesktopWidth(0)
DHeight.i = DesktopHeight(0)
InitSprite()
OpenWindow(0, 0,0, DWidth,DHeight, "Windowed Screen", #PB_Window_BorderLess)
OpenWindowedScreen( WindowID(0), 0,0, DWidth,DHeight, 0,0,0)
Timer = ElapsedMilliseconds() + #TimeStep
Repeat
Event = WaitWindowEvent(10)
If Timer < ElapsedMilliseconds()
Timer + #TimeStep
N + DWidth / 100
X = N % DWidth
Y = DHeight / 2 + DHeight / 2.5 * Sin( N / ( DWidth / 10 ) )
StartDrawing( ScreenOutput() )
Circle( X, Y, DHeight / 40 , $C04020 )
Circle( X, Y, DHeight / 60 , $40C0F0 )
StopDrawing()
FlipBuffers()
EndIf
Select Event
Case #PB_Event_CloseWindow
EXIT = 1
EndSelect
Until EXIT
Beenden mit ALT + F4
PS: Die Eventabfrage #Null hab ich mal rausgenommen und das drawen vor das Select gesetzt, dann ruckelts nicht mehr, wenn man die Maus bewegt.
PPS: Und man kann sogar locker mit ALT + TAB zwischen "Screen" und anderen Fenstern wechseln, ohne das was von der Grafik zerpflückt wird.
Schön, daß wir drüber geredet haben.

Verfasst: 07.06.2009 11:23
von Kaeru Gaman
> Beenden mit ALT + F4
ahja, da machste halt noch n examinekeyboard rein, und setzt EXIT bei ESC...
> Die Eventabfrage #Null hab ich mal rausgenommen und das drawen vor das Select gesetzt, dann ruckelts nicht mehr, wenn man die Maus bewegt.
besser timern, aber so geht das schon für quick'n'dirty...
nur niemals vergessen, alle events zu processen...
mein beispiel war für geringe framerate bzw. sporadisches neuzeichnen ausgelegt.
bei "normaler" framerate von 30+ muss man die schleife etwas dynamischer gestalten.