Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
Aries
Beiträge: 18
Registriert: Fr 26. Okt 2012, 01:41

Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Beitrag von Aries »

Hallo,

In einem Programm verwende ich einen Timer um regelmäßig etwas zu berechnen und zu zeichnen. Leider bin ich häufig mit dem Zeichnen noch nicht fertig, wenn der Bildschirm das nächste Bild zeichnet, sodass nur ein Teil des Gewollten gezeichnet wird, und es zu Flackern kommt. Mein Bildschirm zeigt 60 Bilder pro Sekunde an. Also würde ich gerne jede Sechzigstelsekunde nach einer Bildschirmausgabe komplett zum Zeichnen ausnutzen. Dazu sind meines Erachtens folgende zwei Bedingungen zu erfüllen:

1. Das Starten des Timers muss unverzüglich nach einer Bildschirmausgabe erfolgen.
2. Der Timerinterval muss der Hertz-Zahl des Bildschirm entsprechen. 1000 durch 60 sind 16 2/3. Timer.Interval erfordert aber leider eine ganze Zahl bzw. ein "LongWord".

Bei diesen beiden Problemen weiß ich nicht weiter. Daher hoffe ich auf eure Hilfe.

So sieht es bisher aus:

Code: Alles auswählen

Timer1:=TTimer.Create(self);
Timer1.OnTimer:=@Timer1Timer;
Timer1.Interval:=1000/60;

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2813
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Beitrag von m.fuchs »

Hallo,
  • Die Tatsache dass dein Bildschirm 60 Bilder pro Sekunde anzeigt (60 Hz?), bedeutet nicht dass du auch 60 Bilder in einer Sekunde malen kannst. Die Zeichenoperationen nehmen ja auch ein bisschen Zeit weg.
  • Auch wenn du die Intervalzeit deines Timers auf 1000/60 setzt (was übrigens mit deinem Code gar nicht geht), bedeutet das nicht dass das Event alle 17 Millisekunden aufgeerufen wird. Dei minimale Einstellung unter Windows ist beispielsweise meines Wissens 25 ms (bitte korrigiert mich). Außerdem bräuchtest du ein Echtzeitbetriebssystem um garantierte Aufrufzeitpunkte zu erhalten.
  • Du musst also mit größeren Abständen leben. Wenn du nicht willst, dass dir ein neuer Timeraufruf zwischen deinen laufenden funkt:

    Code: Alles auswählen

    procedure Timer1Timer(Sender: TObject);
    begin
      Timer1.Enabled := False;
      (* mach irgendetwas *)
      Timer1.Enabled := True;
    end;
Was ist denn dein Endziel? Vielleicht (bestimmt) gibt es eine andere Lösung.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Re: Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Beitrag von Scotty »

Unter DOS konnte man auf den vertikalen Rücksprung des Kathodenstrahls warten. Suchst du so was?

Code: Alles auswählen

procedure WaitVRetrace; ASSEMBLER;
asm
  mov dx,03DAh
 @@1: in al,dx
  test al,08h
  jz @@1
@@2: in al,dx
  test al,08h
  jnz @@2
end;
 

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Beitrag von mschnell »

Scotty hat geschrieben:Unter DOS konnte man auf den vertikalen Rücksprung des Kathodenstrahls warten. Suchst du so was?
In einem System das nicht einem Programme allein den Zugriff auf den Bildschirm erlaubt ist sowas natürlich nicht möglich.

Es ist denkbar, dass eine Synchronisation mit dem passenden Tool im Full-Screen modus auch unter Windows/Linux/Mac geht. Am besten mal bei SDL suchen !

-Michael
Zuletzt geändert von mschnell am Fr 26. Okt 2012, 14:16, insgesamt 3-mal geändert.

Heinrich Wolf
Beiträge: 323
Registriert: Di 12. Apr 2011, 13:21
OS, Lazarus, FPC: WinXP + VMWare Player mit Fedora14, L 1.1, FPC 2.7.1
CPU-Target: 1core 1,8GHz 32Bit
Wohnort: Fürth
Kontaktdaten:

Re: Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Beitrag von Heinrich Wolf »

Scotty hat geschrieben:Unter DOS konnte man auf den vertikalen Rücksprung des Kathodenstrahls warten. Suchst du so was?

Code: Alles auswählen

procedure WaitVRetrace; ASSEMBLER;
asm
  mov dx,03DAh
 @@1: in al,dx
  test al,08h
  jz @@1
@@2: in al,dx
  test al,08h
  jnz @@2
end;
 
Unter Windows / Linux / ... ist das Programm unbrauchbar, weil es zum Warten auf den Strahl gnadelos Rechenzeit mit 100% CPU Last vernichtet. Wahrscheinlich blocken die Kernels dieser Betriebssysteme auch den Zugriff auf Register 03DAh ab.

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2813
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Beitrag von m.fuchs »

Scotty hat geschrieben:Unter DOS konnte man auf den vertikalen Rücksprung des Kathodenstrahls warten. Suchst du so was?
Wie funktioniert das dann bei TFT-Monitoren?
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

MAC
Beiträge: 770
Registriert: Sa 21. Feb 2009, 13:46
OS, Lazarus, FPC: Windows 7 (L 1.3 Built 43666 FPC 2.6.2)
CPU-Target: 32Bit

Re: Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Beitrag von MAC »

Also hier mal ein paar Tatsachen:

1) Um so genau einen Timer zu setzen, würde ich dir empfehlen dir, in einem eigenen Thread ein programm laufen zu lassen, welches sozusagen dauernt die Zeit misst (alle 0.1 ms) und dann schaut ob diese 1/60 Sekunde vorbei ist. Dann könnte das ein Event feuern. So ein Timer ist im grunde recht einfach zu bauen.

2) Was Zeichnest du ? Zeichnest du auf dein Fenster ? Zeichnest du auf ein Image ? Zeichnest du auf das Windows fenster ? Zeichnest du 3D mit hilfe deiner Graphikkarte.
Wieviel zeichnest du ? Wenig ? Viel ?


Wenn du mit hilfe deiner Graphikkarte zeichnest, kann es sein das darauf automatisch geachtet wird (NVidia, ob ATI das auch macht, ka). Beim Zeichnen auf ein Image, dein Fenster oder Windows kannst du dein Bild einfach Double Buffern und dann immer nur das schon fertig gezeichnete Bild draufklatschen, das dauert vlt noch ne tausenstel sek, sollte aber kein Flackern ergeben, weil alles am stück gemacht wird...

Code: Alles auswählen

Signatur := nil;

Cybermonkey342
Beiträge: 109
Registriert: Sa 1. Mär 2008, 15:19
OS, Lazarus, FPC: openSUSE Leap 15.6 (FPC 3.2.2) / Windows 11 Pro (FPC 3.2.2)
CPU-Target: x64
Kontaktdaten:

Re: Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Beitrag von Cybermonkey342 »

MAC hat geschrieben:Also hier mal ein paar Tatsachen:
Wenn du mit hilfe deiner Graphikkarte zeichnest, kann es sein das darauf automatisch geachtet wird (NVidia, ob ATI das auch macht, ka). Beim Zeichnen auf ein Image, dein Fenster oder Windows kannst du dein Bild einfach Double Buffern und dann immer nur das schon fertig gezeichnete Bild draufklatschen, das dauert vlt noch ne tausenstel sek, sollte aber kein Flackern ergeben, weil alles am stück gemacht wird...
Das wäre auch meine Empfehlung: Double Buffer.
Mit besten Grüßen,
Cybermonkey

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Beitrag von mschnell »

Cybermonkey342 hat geschrieben:Das wäre auch meine Empfehlung: Double Buffer.
Double-Buffering löst zwar ein Problem, aber ein anderes als die Synchronisierung mit der Scan-Frequenz. Vermutlich ist das aber tatsächlich das Problem das der OP hat.

Synchronisierung mit der Scan-Frequenz ist auch nur bei Verwendung eines CRT-Monitors wirklich sinnvoll. Und wer hat schon noch so einen. Bei LCD etc. kommt noch eine Monit-Interne Refresah-Rate dazu, die man sowieso nicht im Griff hat. Dafür versuchen solche Geräte aber Flimmer-Effekte durch Bildraten-Interferenzen auszugleichen.

-Michael

Aries
Beiträge: 18
Registriert: Fr 26. Okt 2012, 01:41

Re: Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Beitrag von Aries »

Danke für die Antworten.
MAC hat geschrieben:2) Was Zeichnest du ? Zeichnest du auf dein Fenster ? Zeichnest du auf ein Image ? Zeichnest du auf das Windows fenster ? Zeichnest du 3D mit hilfe deiner Graphikkarte.
Wieviel zeichnest du ? Wenig ? Viel ?

Wenn du mit hilfe deiner Graphikkarte zeichnest, kann es sein das darauf automatisch geachtet wird (NVidia, ob ATI das auch macht, ka). Beim Zeichnen auf ein Image, dein Fenster oder Windows kannst du dein Bild einfach Double Buffern und dann immer nur das schon fertig gezeichnete Bild draufklatschen, das dauert vlt noch ne tausenstel sek, sollte aber kein Flackern ergeben, weil alles am stück gemacht wird...
Das mit dem Double Buffer hört sich nach der perfekten Lösung an. Ich weiß leider nicht wie das geht.

Ich zeichne nach folgendem Muster und relativ viel:

Code: Alles auswählen

Form1.Canvas.Polygon([Punkt[1],Punkt[2],Punkt[3],Punkt[4]]); 
Wie sähe es aus, wenn man das mit einem Double Buffer machen würde?

Benutzeravatar
theo
Beiträge: 10872
Registriert: Mo 11. Sep 2006, 19:01

Re: Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Beitrag von theo »

Aries hat geschrieben: Wie sähe es aus, wenn man das mit einem Double Buffer machen würde?
Du zeichnest zuerst alles auf ein Bitmap.

Bmp:=TBitmap.Create;
Bmp.Width:=....
etc.
Bmp.Canvas.Polygon.....
etc.
Wenn alles gezeichnet ist, kopierst du das fertige Bild auf den sichtbaren Canvas z.B. mit Draw
Form1.Canvas.Draw(0,0,Bmp);
Bmp.Free;

Aries
Beiträge: 18
Registriert: Fr 26. Okt 2012, 01:41

Re: Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Beitrag von Aries »

theo hat geschrieben:
Aries hat geschrieben: Wie sähe es aus, wenn man das mit einem Double Buffer machen würde?
Du zeichnest zuerst alles auf ein Bitmap.

Bmp:=TBitmap.Create;
Bmp.Width:=....
etc.
Bmp.Canvas.Polygon.....
etc.
Wenn alles gezeichnet ist, kopierst du das fertige Bild auf den sichtbaren Canvas z.B. mit Draw
Form1.Canvas.Draw(0,0,Bmp);
Bmp.Free;
Ich habe das mal getestet:

Code: Alles auswählen

  var
    Bitmap1: TBitmap;
 
  ...
 
  Bitmap1:=TBitmap.Create;
  Bitmap1.Width:=Form1.Width;
  Bitmap1.Height:=Form1.Height;
  Bitmap1.Canvas.Pen.Color:=RGBtoColor(255,0,0);
  Bitmap1.Canvas.Brush.Color:=RGBtoColor(255,0,0);
  Bitmap1.Canvas.Rectangle(0,0,100,100);
  Form1.Canvas.Draw(0,0,Bitmap1);
  Bitmap1.Free;
Bei "Create", "Width", "Height", "Canvas" und "Free" meckert er "unknown record field identifier".

Bei "Form1.Canvas.Draw(0,0,Bitmap1);" meckert er "Incompatible type for arg no. 3: Got "BITMAP", expected "TGraphic""

Woran könnten die Fehlermeldungen liegen?

Benutzeravatar
theo
Beiträge: 10872
Registriert: Mo 11. Sep 2006, 19:01

Re: Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Beitrag von theo »

Hast du die Unit Windows hinter der Unit Graphics eingebunden in "Uses"? Falls ja, ändere das oder entferne Unit Windows ganz.

Aries
Beiträge: 18
Registriert: Fr 26. Okt 2012, 01:41

Re: Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Beitrag von Aries »

theo hat geschrieben:Hast du die Unit Windows hinter der Unit Graphics eingebunden in "Uses"? Falls ja, ändere das oder entferne Unit Windows ganz.
Danke, das hat das Problem gelöst.

Der Double Buffer funktioniert jetzt. Eine zufriedenstellende Lösung.

soerensen3
Beiträge: 104
Registriert: Fr 22. Jun 2012, 01:51
OS, Lazarus, FPC: Fedora-Linux 23 (Korora) Lazarus 1.6 FPC 3.0
CPU-Target: 64Bit
Wohnort: Bonn

Re: Timer-Startzeitpunkt an Bildanzeigezeitpunkt koppeln

Beitrag von soerensen3 »

Noch schneller wäre es bestimmt, wenn du das bitmap beim starten des programms erstellst und am ende erst wieder freigibst. Weiß aber nicht ob das viel ausmacht.

Antworten