AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Cross-Platform-Entwicklung [Fmx, Vcl] Verhalten von TBitmap in Threads
Thema durchsuchen
Ansicht
Themen-Optionen

[Fmx, Vcl] Verhalten von TBitmap in Threads

Ein Thema von Rollo62 · begonnen am 12. Apr 2016 · letzter Beitrag vom 4. Okt 2018
Antwort Antwort
Rollo62

Registriert seit: 15. Mär 2007
4.116 Beiträge
 
Delphi 12 Athens
 
#1

[Fmx, Vcl] Verhalten von TBitmap in Threads

  Alt 12. Apr 2016, 10:18
Hallo zusammen,

ich habe mal generellen Klärungsbedarf:
Welche Operationen darf man unten allen Platformen mit einem Bitmap im Thread machen ?

VCL:
Unter VCL verstehe ich das so das Kopieren, Malen über den Bitmap.Canvas im Thread generell möglich ist.
Lediglich das Übergeben an ein Control zum Anzeigen muss im UI-Thread passieren.

FMX:
Hier vermute ich mal das Bitmap.Canvas auch eines rein im Speicher angelegten Bitmaps schon mit UI zu tun hat.
Womöglich wegen des Canvas, der unter FMX in verschiedenen Ausführungen erscheint, je nach Plattform.

Ich würde gerne mal klären welche Operation grundsätzlich auf allen Platformen OK sind, und welche nicht:

Delphi-Quellcode:
---------------- UI-Komponente <-- zu ---> Speicher-Bitmap im Thread
01. Create --------# im Thread bmpThread.Create(100, 100); ---- Ok: VCL-Ja, Fmx-?
02. Clear ---------# im Thread bmpThread.Clear(0); ------------ Ok: VCL-Ja, Fmx-?
03. Bearbeiten ----# im Thread bmpThread.Canvas.Draw... ------- Ok: VCL-Ja, Fmx-? -mit oder ohne Map/UnMap ?
04. Kopieren ------# im Thread bmpThread.Assign( bmpThread2); - Ok: VCL-Ja, Fmx-?
05. Filelesen -----> im Thread bmpThread.LoadFromFile(); ------ ??
06. Fileschreiben -> im Thread bmpThread.SaveToFile(); -------- ??
07. Streamlesen ---> im Thread bmpThread.LoadFromStream(); ---- ??
08. Streamschreib. > im Thread bmpThread.SaveToStream(); ------ ??
09. Assign --------> im Thread bmpThread.Assign( bmpUI ); ----- Nicht OK, muss syncronisiert werden
10. Zurückgeben ---< im Thread bmpUI.Assign( bmpThread ); ----- Nicht OK, muss syncronisiert werden
Habe ich was vergessen, kann man was ergänzen ?
Wie verhält sich das unter Fmx, so das es optimal unter allen Platformen läuft ?

Ich würde gerne alles bis auf 09/10 im Thread ohne grosse Verrenkungen benutzen, ist das denkbar ?

Vielleicht könnte man eine TBitmapThreadSafe Klasse bauen, das dies alles möglichst kapseln würde wenn man die
im Thread benutzt ?

Wäre schön wenn sich damit jemand auskennen und mir auf die Sprünge helfen könnte.

Rollo

Geändert von Rollo62 (12. Apr 2016 um 10:21 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#2

AW: [Fmx, Vcl] Verhalten von TBitmap in Threads

  Alt 12. Apr 2016, 19:18
Der Zugriff auf den Canvas bringt das non-thread-safe.

Die TBitmap Klasse ist nur für die Verwendung im MainThread gedacht gewesen und sollte daher auch nur in diesem Kontext verwendet werden.

Wenn jemand das anders verwendet und es funktioniert, dann diese Code-Zeilen in das Nachgebet einschliessen (auf das es weiter funktioniert). Zugesichert wird die Funktion in keiner Weise.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.686 Beiträge
 
Delphi 2007 Enterprise
 
#3

AW: [Fmx, Vcl] Verhalten von TBitmap in Threads

  Alt 12. Apr 2016, 21:47
Das stimmt für die VCL, und dann auch nur bei Zugriffen über Canvas. So lange ich via Scanline in einem TBitmap rumfuchtel sollte alles okay sein.

Aber: Ist das bei FMX noch genau so? Ich weiss es mangels Delphi-Version mit FMX nicht*, aber FMX arbeitet gerade beim Thema Grafik explizit nicht mit der WinAPI, welche die Schnittstelle zum Canvas bereitstellt. Von daher kann hier alles anders aussehen. Eine offizielle oder zumindest gut informierte und begründete Aussage wäre da in der Tat wünschenswert.

*) Und ich bin bei Bitmaps komplett auf die Graphics32 umgestiegen, welche in Threads keinerlei Probleme bereitet.
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#4

AW: [Fmx, Vcl] Verhalten von TBitmap in Threads

  Alt 12. Apr 2016, 22:21
Zitat von Marco Cantu:
In general, bitmap operations in background threads have never been allowed, although they do occasionally work.
Quelle http://community.embarcadero.com/for...working-in-xe8
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Benutzerbild von Harry Stahl
Harry Stahl

Registriert seit: 2. Apr 2004
Ort: Bonn
2.538 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: [Fmx, Vcl] Verhalten von TBitmap in Threads

  Alt 12. Apr 2016, 23:12
Remmy Lebeau hat es mal bezogen auf das VCL-TBitmap zutreffend so beschrieben:

"Technically, nothing about it is thread-safe. You should always provide a
lock around multi-threaded access to shared resources. But as long as
neither bitmap is ever being resized or having its underlying handles
regenerated, in other thread while you are reading the Scanline[property] or
modifying its contents, then you should be ok."


Bei FMX ist es im Prinzip ähnlich, Bitmap / Canvas ist auch nicht Threadsafe, das oben gesagte gilt aber auch hier.

Zusätzlich helfen Dir aber BeginScene und Endscene die Zugriffe auf den Canvas Kollisionsfrei hinzubekommen (viele nutzen allerdings Beginscene quasi als Procedur-Aufruf und werten das Ergebnis nicht aus) bzw. Map und Unmap beim Zugriff auf die Pixeldaten des Bitmaps.

Insofern macht so eine Auflistung hier m.E., wenig Sinn, hängt alles vom Kontext Deines Threads bzw. der evtl. gemeinsam benutzten Ressourcen ab.
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.686 Beiträge
 
Delphi 2007 Enterprise
 
#6

AW: [Fmx, Vcl] Verhalten von TBitmap in Threads

  Alt 13. Apr 2016, 01:21
Ja, ach. Ole ole. Dass man ein und dieselbe Ressource nicht in 2 Threads gleichzeitig verändern darf ist jetzt aber auch nicht gerade eine exklusive Domäne von TBitmap, oder? Das selbe gilt ebenso für TList, Arrays, und jeden anderen Typen, bei dem Manipulation nicht in atomaren Operationen geschieht, bzw. wo Referenzen existieren die ggf. bei "fremden" Akteuren nachgeführt werden müssen.
Bei Bitmaps kommt (unter Win32 zumindest!) lediglich dazu, dass hier u.U. Handles vom OS geändert werden, die ein Thread möööglicherweise mal irgendwo gepuffert hat. (Lies: Ich übergebe einem Thread ein HBITMAP oder einen Canvas-Handle statt der Referenz auf das TBitmap-Objekt.) Aber genau diese 2 Dinge existieren unter FMX möglicherweise gar nicht.
Diese Quellen alleine sind grob simplifizierend, und wenden eine eigentlich sehr allgemeine Regel unberechtigt spezifisch auf TBitmap an. Dass man bei Threads und gemeinsamem Zugriff auf was auch immer eine Form von Synchronisation braucht sollte wohl jedem klar sein, der Threads 2-3 Mal in der Hand hatte. Spätestens.
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Rollo62

Registriert seit: 15. Mär 2007
4.116 Beiträge
 
Delphi 12 Athens
 
#7

AW: [Fmx, Vcl] Verhalten von TBitmap in Threads

  Alt 13. Apr 2016, 08:10
Dankesehr erstmal für die Antworten.

Zitat:
Insofern macht so eine Auflistung hier m.E., wenig Sinn, ...
Genau darum geht es mir, was man darf und was nicht, am Besten unter Allen Plattformen und Frameworks.
Zumindest für die Basics.

Ich habe grosse Unterschiede zw. iOS und Android, OSX und Win genauso.
Ich möchte aber einen Source für Alles benutzen können, und suche nach Möglichkeiten das zu kapseln.

@Harry
Der Hinweis mit Scanline ist sehr gut, ich bin bisher davon ausgegangen das es immer auch mit dem Canvas zu tun hat.
Aber es scheint wirklich nur den Speicher anzufassen.
Vielleicht ist das ein Ansatz zumindest Bitmaps 1:1 zu kopieren, um von einem Bitmap z.B. an ein TListView Image weiterzugeben.
Immerhin.

Aber ich vermute das TBitmap.Create() im Hauptprozess wird auch schon einiges an Performance brauchen, und das Kopieren am Ende ist dann
nur ein Teil.


Delphi-Quellcode:
function TBitmapData.GetPixelAddr(const I, J: Integer): Pointer;
begin
  Result := Pointer(NativeInt(GetScanline(J)) + I * BytesPerPixel);
end;

function TBitmapData.GetScanline(const I: Integer): Pointer;
begin
  Result := Pointer(NativeInt(Data) + I * Pitch);
end;

procedure TBitmapData.Copy(const Source: TBitmapData);
var
  I: Integer;
begin
  for I := 0 to Height - 1 do
    Move(Source.GetScanline(I)^, GetScanline(I)^, BytesPerLine);
end;
@Meduim
+1 für Graphics32: Nur gibt es das ja leider (noch) nicht für Fmx. Unter VCL benutze ich das auch ohne Probleme.

@Sir Rufo
Wenn man niemals einen Canvas anfassen darf heisst das doch das man noch nicht mal ein TBitmap.Create im Thread machen darf ?
Das Bitmap.Canvas muss dann doch irgendwie angelegt werden.
Ich habe unter FMX öfters festgestellt das TBitmap.CanvasClass nicht gesetzt oder sich anders verhält (skaliert, transformiert) als z.B. ein Image.Bitmap.Canvas.
Ich hätte gehofft das interne TBitmap.Canvas nur eine Simulation eines Canvas ist, und nicht weiter mit dem UI-Thread kommuniziert.

Unter Fmx gibt es auch ein TListViewItem.BitmapRef was wohl
nur einen Pointer auf das eigentlich Bitmap setzt, wenn ich das richtig verstehe.

Also könnte man versuchen so etwas in der Art zu machen:
- erst alle neuen, leeren Bitmaps im UI Thread anlegen, aber mit korrektem Size
- dann evtl. im Thread mit Scanline kopieren, bearbeiten (wird aber womöglich weniger effizient sein)
- danach könnten die Bitmaps im UI-Thread mit der TListView BitmapRef verbunden werden
- der Owner wäre danach nicht die ListView, dann muss das auch entsprechend verwaltet werden

Das gefällt mir aber gar nicht, gibts nichts besseres ?

Rollo
  Mit Zitat antworten Zitat
Rollo62

Registriert seit: 15. Mär 2007
4.116 Beiträge
 
Delphi 12 Athens
 
#8

AW: [Fmx, Vcl] Verhalten von TBitmap in Threads

  Alt 13. Apr 2016, 15:52
Hier ist mal ein kleiner Test.

@Sir Rufo
Als Basis habe ich deinen TBitmapProducerThread gefunden und ich habe versucht das zusammen mit einer ImageList auszuprobieren.
Der TBitmapProducerThread war wohl nur für VCL gedacht, aber auch mit FMX könnte das ja Laufen ?!

Ich erzeuge im Thread neue Bitmaps, die ich dann in die ImageList speichere
- Thread Erzeugen eines leeren Bitmaps
- UI-Thread per Syncronize Ändern der Größe (das wird mit dem Canvas in Berührung kommen)
- UI-Thread Übergabe des neuen Bitmaps

Im DoProduce rufe ich das ändernde SetSize mit Synchronize auf, und ändere dann mit Map/Unmap.
Anders komme ich doch an die Pixel nicht heran, ich könnte es noch mit Scanline probieren aber
Map/Unmap brauche ich womöglich trotzdem.

Delphi-Quellcode:
procedure TBitmapProducerThread.DoProduce(const LBitmap : TBitmap; LParams : TBitmapParameters);
var
  Data: TBitmapData;
  I: Integer;
  col: TAlphaColor;
begin

  TThread.Synchronize(nil,
                      procedure
                      begin
                        // This must be in Main Thread, to be treadsafe
                        LBitmap.Width := LParams.Width;
                        LBitmap.Height := LParams.Height;
                      end
                     );

  // Bitmap erstellen
  if LBitmap.Map(TMapAccess.ReadWrite, Data) then
  begin

    col := TAlphaColorF.Create(128+Random(127), 128+Random(127), 128+Random(127)).ToAlphacolor;

    // // 5000 xxx Pixel auf ein Bitmap malen
    for I := 1 to 5000 do
    begin
      Data.SetPixel(Random( LBitmap.Width ), //Muss noch Scanline probieren, als Alternative
                    Random( LBitmap.Height ),
                    col //TAlphaColorRec.Red
                   );
    end;

    LBitmap.Unmap(Data);
  end;

end;
Unter Windows läuft alles gut bis jetzt,
unter OSX gab es schon Exception, geht aber auch meistens
und unter Android geht es gar nicht.
Unter iOS habe ichs noch garnicht versucht ...

Aber zumindest habe ich mal getestet wie man Bitmaps einer ImageList zuordnen kann, über den Namen:
Delphi-Quellcode:
//
// wird von TBitmapProducerThread aufgerufen, im UI-Thread
//
procedure TForm1.EvOnBmpProdOutputChanged(Sender: TObject);
var
  isi: TSourceItem;
  mbi: TBitmapItem;
  idi: TDestinationItem;
  di: TDestinationItem;
  lay: TLayer;
  sX: string;
  bmi: TBitmapItem;
  ms: TMemoryStream;
  bmp: TBitmap;

begin

  bmp := TBitmap.Create;

  try
    FBmpProd.Get( bmp ); // Hier wird die Bitmap aus dem Thread geholt

    // Und hier wird ein Neuer ImageList-Eintrag angelegt
    isi := ImageList1.Source. Add as TSourceItem;
    if isi.MultiResBitmap.Count = 0 then
    begin
      bmi := isi.MultiResBitmap.Add as TBitmapItem;

      ms := TMemoryStream.Create;

      try
        bmp.SaveToStream( ms );
        ms.Position := 0;

        bmi.Bitmap.CreateFromStream( ms );

      finally
        ms.Free;
      end;

    end;

    sX := isi.Name;
    idi := ImageList1.Destination.Add as TDestinationItem;
    lay := idi.Layers.Add;
    lay.Name := sX; // Setup the link the the Source Bitmap here
    sX := lay.Name;
    lay.SourceRect.Rect := RectF(0, 0, 256, 256); // Region within the soure-Rect

  finally
    bmp.Free;
  end;

  ScrollBar1.Max := ImageList1.Destination.Count-1;
end;

Benutzung:
Append/Insert/Delete/Clear - Legt Items im Listview an, und benutzt ImageList als Basis.
Das geht recht fix, so das man keinen Thread braucht (ich zumindest noch nicht)).

Produce erzeugt neue Bitmaps in dem ImageView, die ich dann s.o. zum Erzeugen benutzen kann.


Rollo
Angehängte Dateien
Dateityp: zip S002_ImageListMag.zip (629,6 KB, 6x aufgerufen)
  Mit Zitat antworten Zitat
Benutzerbild von Christoph Schneider
Christoph Schneider

Registriert seit: 7. Okt 2008
Ort: CH-Baar
54 Beiträge
 
Delphi 11 Alexandria
 
#9

AW: [Fmx, Vcl] Verhalten von TBitmap in Threads

  Alt 3. Okt 2018, 10:43
Bei FMX sollte TBitmap heute aber Threadsafe sein (Stand Delphi 10.2 Tokyo):

http://docwiki.embarcadero.com/RADSt...and_TContext3D

Dabei aber unbedingt peinlich darauf achten, dass das Bitmap-Objekt nicht gleichzeitig aus mehreren Threads verwendet wird!
Wenn diese Regel verletzt wird sind die Folgen schmerzhaft: Ich kämpfte gerade mit sporadisch leeren TImage denen ein Bitmap zu gewiesen wurden, welches in einem Thread erstellt wurde.
Christoph Schneider
  Mit Zitat antworten Zitat
Benutzerbild von Christoph Schneider
Christoph Schneider

Registriert seit: 7. Okt 2008
Ort: CH-Baar
54 Beiträge
 
Delphi 11 Alexandria
 
#10

AW: [Fmx, Vcl] Verhalten von TBitmap in Threads

  Alt 4. Okt 2018, 09:37
Offenbar gibt's unter FMX auch unter Delphi 10.2.3 immer noch Probleme.

Siehe RSP-19673
Christoph Schneider
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:42 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz