Canvas zwischenspeichern (DoubleBuffering langsamer?) gelöst

Rund um die LCL und andere Komponenten
RSE
Beiträge: 462
Registriert: Mi 30. Jul 2008, 13:11
OS, Lazarus, FPC: WinXP SP3 (L 0.9.28.2 FPC 2.2.4)
CPU-Target: 32Bit
Kontaktdaten:

Re: Inhalt von Canvas zwischenspeichern

Beitrag von RSE »

pluto hat geschrieben:Das Dauert so lange, weil du alles doppelt machen musst:
Du musst ja erst alles in den Buffer zeichnen und ihn anschließend wieder auf den Anzeige-Canvas Kopieren. Aber anders geht es nicht.
Das ist eben offenbar nicht die Ursache. Ich habe jetzt viel an diesem einfachen Code herumprobiert. Dabei habe ich folgende Änderungen vorgenommen:
  • Zeichnen auf Buf.Canvas oder self.Canvas
  • Buf.SetSize auskommentiert
  • BitBlt auskommentiert
Ergebnisse:
1. Zeichnen auf self.Canvas, BitBlt auskommentiert, Buf.SetSize auskommentiert: läuft schnell
2. Zeichnen auf self.Canvas, BitBlt auskommentiert, Buf.SetSize nicht auskommentiert: läuft schnell
3. Zeichnen auf Buf.Canvas, BitBlt auskommentiert, Buf.SetSize nicht auskommentiert: läuft langsam

Dass es langsam läuft sehe ich beim horizintalen Vergrößern, wenn das Fenster relativ groß ist (Ich halte den Finger am Rand meines Touchpads und vergrößere so gleichmäßig schnell). Schnell heißt es ruckelt fast nicht (keine weißen Flächen auszumachen). Langsam heißt man sieht, wie der Bereich neben der Toolbar über der Listbox (Die Listbox ist formfüllend unter der Toolbar angeordnet) erst etwa 20px breit(!!!) weiß gezeichnet wird (EraseBackground ist also schon durch?), bevor er dann (in der Paint-Methode?) mit clBtnFace gefüllt wird. Die Zeit, die vergeht, während dieser weiße Kasten angezeigt wird, wird wohl die Zeit sein, die meine Paint-Methode schluckt. Je länger diese Zeit ist, desto weniger oft wird neu gezeichnet und desto breiter (und damit auffälliger) ist der neuzuzeichnende Bereich in der Toolbar.

So jetzt interpretiert bitte diese Ergebnisse. Meiner Meinung nach ist das Zeichnen auf Buf.Canvas allein der zeitraubende Vorgang. Gibt es eine andere Klasse mit Canvas, die schneller ist als TBitmap?
Seit er seinen neuen Computer hat, löst er alle Probleme, die er vorher nicht hatte!

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Re: Canvas zwischenspeichern (DoubleBuffering langsamer?)

Beitrag von Christian »

Ich weiss nicht warum da Bitblt sinn machen soll, aber ein einfaches Canvas.Draw(0,0,FBitmap); tuts auch.
Sieh zu das du in paint nur das machst nichts weiter
Wenn du was am Control ändern willst machst du das und rufst invalidate auf damits neu gezeichnet wird.
In der Lazarus Wiki gibts gute beispiele dazu.
Hier wurde wieder viel irreführendes geschrieben das du ganz schnell wider vergessen kannst.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: Canvas zwischenspeichern (DoubleBuffering langsamer?)

Beitrag von pluto »

Ich weiss nicht warum da Bitblt sinn machen soll, aber ein einfaches Canvas.Draw(0,0,FBitmap); tuts auch.
Nun ja, ein Canvas.Draw führt zwei Forschleifen aus, und zeichnet Pixel weise. Gehen tut es ja. Allerdings heiß es das BitBlt schneller sein soll als ein normales Draw, weil es API Funktionen nutzt, unter Windows war das jedenfalls so, ob das jetzt auch für GTK2 auch gilt, weiß ich nicht und du kannst damit ein Bereich Kopieren, dass geht zwar mit CopyRect auch, CopyRect nutzt intern ja auch BitBlt, allerdings haben wir ja fest gestellt das CopyRect Fehlerhaft bei bestimmen Angaben ist.
3. Zeichnen auf Buf.Canvas, BitBlt auskommentiert, Buf.SetSize nicht auskommentiert: läuft langsam
Das kann ich Irgendwie nicht glauben. In Etwa mache ich das so bei meinem Projekt auch und es läuft ausreichend schnell. Wobei ich ein Buff.SetSize nur aufrufe wenn sich die Größe des Buffers geändert hat. Vielleicht liegt es daran.
Folgender Code zeigt das:

Code: Alles auswählen

if (not Assigned(Parent)) and (DefaultContainer.isScroll) then begin
    c:=buffer.Canvas; Style.ToCanvas(c);
    if (buffer.Width <> ScrollRect.Right) or (Buffer.Height <> ScrollRect.Bottom) then
      buffer.SetSize(ScrollRect.Right,ScrollRect.Bottom);
    c.Rectangle(sx-2,sy-2,sx+Width+4,sy+Height+4);
  end
  else begin
    if DefaultContainer.isScroll then
      c:=DefaultContainer.Buffer.Canvas
    else
      c:=DefaultContainer.Outcanvas;
    Style.ToCanvas(c);
    c.Rectangle(sx-2,sy-2,sx+Width+4,(sy+Height));
  end;
Hier wurde wieder viel irreführendes geschrieben das du ganz schnell wider vergessen kannst.
Das Sehe ich anders. Es kommt immer drauf an wie man Zeichnet. Hier noch ein Beispiel:

Code: Alles auswählen

r.Left:=ScrollRect.Left; r.Top:=ScrollRect.Top; r.Right:=R.Left+ScrollRect.Right; r.Bottom:=R.Top+ScrollRect.Bottom;
  for i:=0 to Count-1 do begin
    r3.Left:=items[i].Left; r3.Top:=items[i].Top; r3.Right:=r3.left+items[i].Width; r3.Bottom:=r3.top+items[i].Height;
    if IntersectRect(R2, r,r3) then begin
      Items[i].ScrollRect:=ScrollRect;
      items[i].Canvas:=c;
      Items[i].Paint;
    end
  end;
Dieser Code wird nach dem ersten Code ausgeführt den ich gepostet habe. Hier siehst du das ich nur das Zeichne was sich auch im Sichtbereich befindet. Wenn du jetzt sehr viele Einträge hast, brauchst du ja nur die zu zeichnen, die sich auch im Sichtbereich findet.

@Christian
Ich habe nur Sachen gesagt, wie das z.b. die LCL auch macht. z.b. das HTML Panel nutzt BITBLT und Tausch den BufferCanvas aus. Andere Komponenten machen das auf ähnliche Art und Weise.
MFG
Michael Springwald

RSE
Beiträge: 462
Registriert: Mi 30. Jul 2008, 13:11
OS, Lazarus, FPC: WinXP SP3 (L 0.9.28.2 FPC 2.2.4)
CPU-Target: 32Bit
Kontaktdaten:

Re: Canvas zwischenspeichern (DoubleBuffering langsamer?)

Beitrag von RSE »

Boah, ich war wirklich von meiner Aussage oben überzeugt, dass es an TBitmap liegt. Also habe ich eine Beispielapplication implementiert und es damit probiert zu reproduzieren. Dabei stellte sich aber heraus, dass es doch an SetSize liegt. Das langsame Element ist somit wohl die Speicherallokation. Gleiches Verhalten in meinem Projekt. *besenfress*
Also setze ich die Größe des Canvas auf Screen.Width und Screen.Height und vergrößere, falls jemand das Teil über mehrere Bildschirme ziehen sollte... was besseres fällt mir jetzt nicht ein.
Vielen Dank für deine Standfestigkeit, pluto!!!!
Seit er seinen neuen Computer hat, löst er alle Probleme, die er vorher nicht hatte!

Antworten