Dein Hauptprobel ist das das Canvas nicht auf so hochfrequente zeichenoperationen ausgelegt, und regiert es kann passieren das es einfach nicht schnell genug reagieren kann. Unter Windows passiert wenn du Canvas.XXX aufrufst das folgende, der command wird als "Message" an das Windows control gesendet. Windows klappert dann in regelmäßigen abständen alle gesendeten Messages ab und führt die Zeichenoperation aus. Wann die Zeichenoperation ausgeführt wird ist also abhängig wann windows "zeit findet" diese operationen abzuklappern. Windows versucht zwar das das mehr oder weniger zügig passiert, doch es gibt keine garantien, die Zeichendauer ist abhängig von CPU last, anzahl der gesendeten Messages von allen Anwendungen auf dem System (also mehr oder weniger abbhängig von der Anzahl und komplixität an offenen Fenstern) und natürlich Leistungsstärke der CPU.
Wenn dein Programm also das "überzeichnen" zu schnell anfordert Kommen sie beide in die selbe Abarbeitungsprozedur und werden beide gleichzeitig ausgeführt und du siehst nur den effekt der letzten operation.
Alles unter so 100 ms ist nicht sehr verlässlich.
Du musst dir also alternativen außerhalb des Canvas suchen. Browser zum beispiel zeichnen alle komponenten selbst und umgehen dabei die OS MessageQueue, weshalb das folgende beispiel in Javascript wunderbar funktioniert:
Code: Alles auswählen
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
/** @param {number} timeout */
function draw_on_canvas(timeout) {
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById("canvas");
/** @type {CanvasRenderingContext2D} */
const ctx = canvas.getContext("2d");
ctx.fillStyle = "white"
ctx.font = "12px Arial";
ctx.strokeText("Test", canvas.width / 2, canvas.height / 2);
ctx.stroke();
window.setTimeout(() => {
ctx.fillRect(0, 0, canvas.width, canvas.height);
}, timeout);
}
</script>
</head>
<body>
<canvas width="200" height="200" id="canvas"></canvas>
<br />
<button onclick="draw_on_canvas(10)">10 ms</button>
<button onclick="draw_on_canvas(30)">30 ms</button>
<button onclick="draw_on_canvas(50)">50 ms</button>
<button onclick="draw_on_canvas(100)">100 ms</button>
</body>
</html>
(Mit Pas2JS auch in Pascal möglich eine Website zu bauen). Ansonsten wenns nativ bleiben soll kannst du dir mal BGRABitmaps anschauen, es kann sein dasdie direkt über OpenGL oder ähnliches zeichnen, wenn nicht musst du dir die Hände schmutzlig machen und selbst mit Low-Level bibliotheken wie OpenGL oder SDL anfangen.
Abgesehen davon ist natürlich noch das Problem der Zeitlichen Auflösung da. Du hast kein Real-Time betriebsystem, also bist du hämmungslos dem Scheduler ausgeliefert. Und wenn der Scheduler entscheidet das dein Prozess von der CPU genommen wird, kann es mehrere Millisekunden dauern (bei mir c.a. 15-20) bis dein Code weiterlaufen kann. Richtig schlimm ist das wenn du Timer benutzt (wie in deinem code oder auch das Javascript beispiel oben), da Timer den event loop benutzen, welcher wenn er durch läuft die CPU zurückgibt (da ansonsten die CPU auslastung immer bei 100% wäre), und damit sehr ungenau wird. Sowohl mit dem Timer als auch mit dem Javascript code oben ist bei mir die Zeitdifferenz bei 30 ms delay z.T. auf 50 ms hochgegangen, mehr als 50% ungenauigkeit.
Mit einem Busywaiting approach wie in Winni vorgeschlagen hat (wenn man es richtig macht und >= statt <= verwendet), ist das zwar an sich besser, da du nicht unbedingt die CPU abgibst, aber der Scheduler kann auch so entscheiden das er deinen Prozess runter nehmen will.
Mit diesem kleinen Beispiel:
Code: Alles auswählen
var
i: Integer;
start: QWord;
begin
for i:=0 to 100 do
begin
start := GetTickCount64;
repeat
until GetTickCount64-start >= 30;
WriteLn(GetTickCount64 - start);
end;
ReadLn;
end.
Kann man das sehr schön sehen. Wenn sonst grade nix auf dem rechner läuft, er also eine CPU für sich alleine haben kann (und für das OS under der andere kram die anderen CPUs ausreichen) ist die Zeitdifferenz die ausgegeben wird immer 31 oder 32. Wenn ich jetzt mal ein paar Prozesse starte die die CPU last etwas hochtreiben, und dann das ganze noch mal ausführe, geht auch hier die Messung ab wieder ab und an auf 50 ms hoch, da der Scheduler den Prozess von der CPU schmeißt.
Langer Rede kurzer Sinn, mit dem Canvas kommst du nicht weit, du brauchst eine schnellere Rendering API (z.b. OpenGL oder SDL). Gleichzeitig musst du die chance veringern da dein Prozess währenddessen von der CPU geworfen wird, also nicht den Event Loop benutzen (also Busywaiting statt Timer) und wenn möglich nur auf einem rechner mit vielen CPUs (Kernen) und wenig anderen prozessen laufen zu lassen.
Aber selbst dann, kannst du für nichts garantieren, denn ein Nicht-Echtzeit betriebsystem ist inherent ungeignet um Präzise Zeitbedingungen einzuhalten