TTimer bzw. Delay deutlich zu langsam im Milisekundenbereich

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
Hunnsrigga Bu
Beiträge: 2
Registriert: Mo 18. Apr 2016, 18:05

TTimer bzw. Delay deutlich zu langsam im Milisekundenbereich

Beitrag von Hunnsrigga Bu »

Hallo Leute!

Früher habe ich hier und da ein wenig mit Turbo Pascal programmiert, nun bin ich dabei Lazarus kennenzulernen.
Folgende Ausgangssituation: Ich lasse in meinem Programm auf 'nem TImage-Feld einen aus um die 2.000 Linien bestehenden Farbübergang zeichnen (durch For-Schleifen), was soweit auch problemlos klappt - ich klicke auf den Button und das Bild erscheint.
Da das Bild jedoch direkt "in voller Pracht" zu sehen ist, wollte ich eine Delay-Funktion einbauen um zu sehen, wie sich die einzelnen Linien aufbauen. Schnell stellte ich fest, dass es die in Lazarus leider so nicht gibt. Ich bin dann auf die Sleep-Funktion gestoßen, die ich dann in die For-Schleifen eingebaut habe. Leider klappte das nicht, da Lazarus zuerst die komplette Zeit in den For-Schleifen zusammenrechnete, solange wartete und dann das komplette Bild wieder übergangslos anzeigte, nicht jedoch die sich aufbauenden einzelnen Linien.
Danach probierte ich den TTimer aus. Das klappte auch, hier stellte ich jedoch fest, dass es bei der kleinstmöglichen Einstellung (1 Millisekunde) über 30 Sekunden dauerte bis die 2.000 Linien sich zum kompletten Bild aufgebaut hatten, also ein Schritt tatsächlich ca. 16-17 Millisekunden dauerte.
Im Anschluss suchte ich in den Foren weiter und stieß auf diese Prozedur:

Code: Alles auswählen

procedure Delay(msec: integer);
var start, stop: LongInt;
begin
  start := GetTickCount64;
  repeat
    stop := GetTickCount64;
    Application.ProcessMessages;
  until (stop-start)>=msec;
end; 
Leider dauert auch hier das Aufbauen einer Linie ca. 16-17 Millisekunden, genau wie beim TTimer.

Dann versuchte ich mir meine eigene Delay-Funktion zu bauen, indem ich mir eine Prozedur mache, in der eine For-Schleife einfach ein paar hunderttausend mal ohne weiteren Inhalt durchlaufen wird. Das Ergebnis war leider dasselbe wie in der Sleep-Funktion: Zuerst machte das Programm ein paar Sekunden garnichts, um danach das Bild übergangslos und sofort aufzubauen.

Zu guter Letzt nahm ich den Befehl "Application.ProcessMessages" aus der Delay-Funktion und baute ihn statt der kompletten Funktion in die For-Schleifen ein, die die Linien zeichnen. Das brachte weiteren Fortschritt: Das Aufbauen einer Linie dauert nun ca. 3-4 Millisekunden, sodass alle Linien und damit das komplette Bild in ca. 6-7 Sekunden aufgebaut ist. Damit bin ich allerdings noch nicht ganz zufrieden, das sollte eigentlich noch kürzer sein - allerdings wiegesagt nicht so kurz, dass man garnicht sieht, wie sich das Bild aufbaut - und genau da komme ich nicht weiter.

Hat jemand 'ne Idee? Vielen Dank schonmal!

Hunnsrigga Bu

Mathias
Beiträge: 6918
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: TTimer bzw. Delay deutlich zu langsam im Milisekundenber

Beitrag von Mathias »

Wieso zeichnest du nicht direkt auf den Canvas, das sieht man jede Linie einzeln.

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
begin
  for i := 0 to ClientWidth div 3 do begin
    Canvas.Line(0, ClientWidth - i * 3, i * 3, 0);
    Canvas.Pen.Color := i;
    Sleep(10);
    Application.ProcessMessages;
  end;
end;   
Man müsste nur noch eine Variable einbauen für einen frühzeitigen Abbruch.

Ich hoffe das du dies gesucht hast.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

wp_xyz
Beiträge: 5153
Registriert: Fr 8. Apr 2011, 09:01

Re: TTimer bzw. Delay deutlich zu langsam im Milisekundenber

Beitrag von wp_xyz »

Warum musst du denn schon nach jeder Linie ein Wartepause einlegen? Bei Fernsehen hat man 50 Bilder pro Sekunde und nimmt das als flüssige Bewegung wahr. 50 Bilder pro Sekunde, das heißt 20 ms pro Bild. Wenn du deine 2000 Linien in 100 ms darstellen willst (das ist schon verdammt schnell), kannst du das in 5 Gruppen zu je 400 Linien machen und nach jeder Gruppe 20 ms warten (5 x 20 ms = 100 ms; 2000 Linien / 5 Gruppen = 400 Linien pro Gruppe):

Code: Alles auswählen

 
  for i:=1 to 2000 do begin
    <linie zeichnen>
    if i mod 400 = 0 then Sleep(20);
  end;
Die Rechenaufgabe, welcher Divisor zu nehmen ist, wenn das gesamte Bild z.B. 300 ms benötigen soll, überlasse ich dir.
Zuletzt geändert von wp_xyz am Di 19. Apr 2016, 10:10, insgesamt 2-mal geändert.

Komoluna
Beiträge: 565
Registriert: So 26. Aug 2012, 09:03
OS, Lazarus, FPC: Windows(10), Linux(Arch)
CPU-Target: 64Bit

Re: TTimer bzw. Delay deutlich zu langsam im Milisekundenber

Beitrag von Komoluna »

Zeichnest du nur eine Linie pro Zyklus und willst das Bild dann Anzeigen?
Ich würde jedes Mal mehrere Linien zeichnen. Du arbeitest ja mit dem TCanvas aus der LCL, d.h. alle Berechnungen werden im Prozessor durchgeführt und die Daten im RAM gespeichert. Das ist ziemlich ineffizient. Grafikkarten sind deshalb schneller, da sie auf Grafikberechnungen ausgelegt sind(ne wirklich?).

Wenn du 2k Linien in weniger als 5 Sekunden zeichnen willst(kommt natürlich auf die Ausmaße des Bildes an), dann dauert das natürlich so seine Zeit, da bei jedem Frame/Update des Images alle Pixel neu verarbeitet werden müssen.
Ich würde Folgendes vorschlagen:
Du benutzt das "Application.OnIdle" Event, welches immer ausgeführt wird, wenn das Programm grad nix anderes zu tun hat(z.B. User Inputs haben Vorrang).

Code: Alles auswählen

 
var
  last: LongInt;
const
  LinesPerSecond: Integer = 1000;
var
  i: Integer;
  t: LongInt;
  delta: double;
begin
  t := Gettickcount64;
  delta:=min(t-last,1000)/1000;
  last := t;
  for i:=1 to round(Lines*delta) do
  begin
    DrawNextLine();
  end;
end;
Habs nicht getestet...

MFG

Komoluna
Programmer: A device to convert coffee into software.

Rekursion: siehe Rekursion.

Hunnsrigga Bu
Beiträge: 2
Registriert: Mo 18. Apr 2016, 18:05

Re: TTimer bzw. Delay deutlich zu langsam im Milisekundenber

Beitrag von Hunnsrigga Bu »

Donnerwetter, das geht ja flott hier mit den Antworten! ;-)

@wp_xyz: 133. ;-) Und ja, du hast völlig Recht, dein Ansatz ist die Lösung. Auch ohne Wartepause nach jeder Linie wirkt der Bildaufbau fürs menschliche Auge immer noch flüssig, hat wunderbar geklappt.

@Mathias: Hatte garnicht gewusst, dass man die TImage-Komponente garnicht braucht, um zeichnen zu können. Nochmal was dazugelernt. Auch das Muster was dein Programmbeispiel zeichnet ist echt cool.

@Komoluna: Dein Code läuft fehlerfrei, aber um ehrlich zu sein komm ich nicht so wirklich hinter dessen Sinn. Deine Erklärung mit dem TCanvas war aber sehr aufschlussreich, das (hab ich eben getestet) ist bei gleicher Sleep-Einstellung tatsächlich ca. 4-5 mal langsamer wie wenn man direkt ins Fenster zeichnet. Schon wieder was gelernt. ;-)

Ihr habt mir sehr geholfen - noch mal ein ganz großes Dankeschön! :D

Antworten