Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi GraphicsContainer (https://www.delphipraxis.net/192180-graphicscontainer.html)

EWeiss 27. Mär 2017 01:30


GraphicsContainer
 
Zum zeichnen von GDI+ verwende ich im Moment als Container ein einfaches Window.
Das Problem dabei ist das dann nur die CPU ausgelastet wird.

Kann man nicht einfach OpenGL in einem Mix mit GDI+ verwenden und höher lastige Zeichnungsoperationen darauf ausführen?
In dem Fall würde man auch die GPU in Anspruch nehmen.

Oder gibt es eine Möglichkeit beim zeichnen direkt die GPU zu vereinnahmen?
Was müsste ich dafür tun?


Ich wüsste sonst keine andere Möglichkeit die CPU last herunter zu schrauben.
Textzeichnen funktioniert gut auf einen OpenGL Window mit GDI+ siehe TextSuite.

Ich habe im Moment bei einer 26,6MB großen mit 120 Frames ausgestatteten APNG eine Auslastung von 25%.
Das ist einfach zu viel.

gruss

jaenicke 27. Mär 2017 04:49

AW: GraphicsContainer
 
Zitat:

Zitat von EWeiss (Beitrag 1365595)
Ich habe im Moment bei einer 26,6MB großen mit 120 Frames ausgestatteten APNG eine Auslastung von 25%.
Das ist einfach zu viel.

Bei welcher Framerate denn? Ich meine in welcher Zeit werden die 120 Frames denn gezeichnet?

Denn GDI+ nutzt normalerweise bereits Hardware rendering.

Vielleicht wird die Zeit ja auch für das Aufbereiten der Bilder gebraucht.

EWeiss 27. Mär 2017 04:58

AW: GraphicsContainer
 
Zitat:

Zitat von jaenicke (Beitrag 1365597)
Zitat:

Zitat von EWeiss (Beitrag 1365595)
Ich habe im Moment bei einer 26,6MB großen mit 120 Frames ausgestatteten APNG eine Auslastung von 25%.
Das ist einfach zu viel.

Bei welcher Framerate denn? Ich meine in welcher Zeit werden die 120 Frames denn gezeichnet?

Denn GDI+ nutzt normalerweise bereits Hardware rendering.

Vielleicht wird die Zeit ja auch für das Aufbereiten der Bilder gebraucht.

Hmmm... bin mir da nicht so sicher das GDI+ die GPU verwendet denn bei mir sehe ich davon nichts.
Zu den Frameraten kann ich jetzt nichts sagen hab da nichts implementiert.

gruss

EWeiss 27. Mär 2017 05:09

AW: GraphicsContainer
 
Hier mal die Funktion.

Keine Ahnung was da soviel CPU last fabriziert

Delphi-Quellcode:
function TAnimatePng.DrawPngFrame(PngImage: Cardinal; FrameNum: integer; DC: HDC): integer;
var
  PngGraphics: Cardinal;
  PngFrameRect: TGPRectF;
  FrameWidth: integer;
  FrameHeight: integer;
  srcUint: GpUnit;
  rc: TRect;
  rw: TRect;
  TargetRatio: Double;
  IsRelationship: Double;
  w, h, X, Y: integer;
  hTempDC: HDC;
  hBitmapTemp: HBitmap;
  hBitmapOld: HBitmap;
  UseBackColor: ARGB;

begin

  Result := 1;

  srcUint := UnitPixel;
  hTempDC := CreateCompatibleDC(DC);

  If GDIP_GetImageBounds(PngImage, @PngFrameRect, srcUint) = OK then
  begin
    GetWindowRect(GraphicContainer, rw);
    GetClientRect(GraphicContainer, rc);

    hBitmapTemp := SKAERO_CreateDIBSection(hTempDC, rc.Right, rc.Bottom, 32);
    hBitmapOld := SelectObject(hTempDC, hBitmapTemp);

    UseBackColor := MakeColor(32, 0, 100, 128);

    GDIP_DrawGradientVertical(hTempDC, 1, 1, rc.Right, rc.Bottom, UseBackColor, UseBackColor);

    if GDIP_CreateFromHDC(hTempDC, PngGraphics) = OK then
    begin
      try
        // Gif Zentrieren
        if gp.FrameCount = 0 then
          FrameWidth := round(PngFrameRect.Width)
        else
          FrameWidth := round(PngFrameRect.Width) div gp.FrameCount;

        FrameHeight := round(PngFrameRect.Height);
        if (FrameWidth > rc.Right) or (FrameHeight > rc.Bottom) then
        begin
          IsRelationship := FrameWidth / FrameHeight;
          TargetRatio := rc.Right / rc.Bottom;

          if IsRelationship > TargetRatio then
          begin
            w := rc.Right;
            h := rc.Right div round(IsRelationship);
          end
          else if IsRelationship < TargetRatio then
          begin
            h := rc.Bottom;
            w := rc.Bottom * round(IsRelationship);
          end
          else
          begin
            w := rc.Right;
            h := rc.Bottom;
          end;
        end
        else
        begin
          w := FrameWidth;
          h := FrameHeight;
        end;

        X := (rc.Right - w) div 2;
        Y := (rc.Bottom - h) div 2;

        // einzelnes Bild auf Graphicsobjekt zeichnen
        if gp.FrameCount = 0 then
          GDIP_DrawImageRect(PngGraphics, PngImage, X, Y, w, h)
        else
          GDIP_DrawImageRectRectI(PngGraphics, PngImage, X, Y, w, h, w * integer
              (CurPngPlayInfo.PngFrame) - w, 0, w, h, 2, nil, False, nil);

        CurPngPlayInfo.PngDelay := TimerInterval;
        CurPngPlayInfo.PngFrame := FrameNum + 1;
        CurPngPlayInfo.PngLoop := LoopCount - 1;
        CurPngPlayInfo.PngLoops := FrameLoopCount;

        // Ausgabe von Infos
        ShowPngPlayInfos(CurPngPlayInfo);

        // Nur Rendern wenn die Classe AnimatePng initialisiert ist
        if Assigned(AnimatePng) then
        begin
          w := 150;
          h := 60;
          // String Zentrieren
          case Length(IntToStr(AnimatePng.TimerInterval)) of
            1:
              X := (rc.Right - w) div 2 + 45;
            2:
              X := (rc.Right - w) div 2 + 30;
            3:
              X := (rc.Right - w) div 2 + 10;
          end;
          Y := rc.Bottom - h;
          rw.Right := w;
          rw.Bottom := h;
          rw.Left := X;
          rw.Top := Y;

          if not(gp.FrameCount = 0) and (Alpha > 0) then
            // String zeichnen
            GDIP_DrawTextToDC(hTempDC, IntToStr(AnimatePng.TimerInterval), rw, MakeColor
                (Alpha, 3, 123, 250), 'LCDDisplayCapsSSK', 60, FontStyleBold, -1, 0,
              LCDDisplayCapsSSK);
        end;

        BitBlt(DC, rc.Left, 0, rc.Right, rc.Bottom, hTempDC, rc.Left, 0, SRCCOPY);

      finally
        // Graphicsobjekt löschen
        GDIP_DeleteGraphics(PngGraphics);

        SelectObject(hTempDC, hBitmapOld);
        DeleteObject(hBitmapTemp);
        DeleteDC(hTempDC);
      end;
    end;
  end;

end;
gruss

jaenicke 27. Mär 2017 05:20

AW: GraphicsContainer
 
Und wie oft wird diese Methode aufgerufen? Nur so oft wie nötig, sprich durch die Framerate vorgegeben, hoffe ich?

Jedenfalls machst du da ja relativ viel. Verglichen mit der Bibliothek, die wir uns angeschaut hatten, deutlich mehr glaube ich. Dort läuft es mit Hardwarebeschleunigung jedenfalls mit der CPU bei unter 5%. Zumindest mit meiner Testanimation.

Ziel muss sein möglichst viel vorzubereiten und nicht so viel bei jedem Frames zu machen. Ein Profiling Tool könnte helfen um zu sehen wo wie viel Zeit liegen bleibt.

Es kommt natürlich auch auf Betriebssystem und Grafikkartentreiber an, aber ab Windows 7 sollte die Hardware auch bei GDI immer direkt benutzt werden sofern Aero aktiv ist. (Ohne wird das Rendering generell zu einem großen Teil von der CPU übernommen.)

Da aber GDI nicht komplett in der Grafikkarte arbeitet, ist es trotzdem deutlich langsamer als Direct2D usw., genauer ist das hier dokumentiert:
https://msdn.microsoft.com/en-us/lib...n_in_windows_7

Deshalb wird GDI für neue Anwendungen ja auch nicht mehr empfohlen.

In aktuellen Delphiversionen gibt es aber einen schnellen TDirect2DCanvas als schnelle Lösung, der natürlich erst ab Windows 7 funktioniert (vorher fehlten in Windows die Grundlagen dafür):
http://docwiki.embarcadero.com/RADSt...-Zeichenfläche

EWeiss 27. Mär 2017 05:24

AW: GraphicsContainer
 
Zitat:

Und wie oft wird diese Methode aufgerufen? Nur so oft wie nötig, sprich durch die Framerate vorgegeben, hoffe ich?
Nein durch den Timer bedingt und zwar abhängig von der Anzahl der Frames.

Zitat:

Jedenfalls machst du da ja relativ viel. Verglichen mit der Bibliothek, die wir uns angeschaut hatten, deutlich mehr glaube ich. Dort läuft es mit Hardwarebeschleunigung jedenfalls mit der CPU bei unter 5%. Zumindest mit meiner Testanimation.
Du hast also eine APNG mit 27MB und 120 Frames bei einer Auflösung von 512x512 das du behaupten kannst die andere Anwendung wäre da besser ?
Das kann man nur dann behaupten wenn man unter fast gleichen Bedingungen vergleicht.

Zitat:

Jedenfalls machst du da ja relativ viel.
Was ist viel?
Das sind doch hauptsächlich nur Ermittlungen über die Position des Bildes im Bild und die Position auf dem Container wo gezeichnet wird.
Der Container im eigentlichen sinne ein Window macht nichts.
Er ist nur zum halten des DC zuständig.. bzw. das hole ich mir von dort.

Das Bild wird im Speicher abgelegt und aus dem Speicher heraus gezeichnet.

Was CPU lastig ist ist dies hier
Delphi-Quellcode:
       
// Ausgabe von Infos
ShowPngPlayInfos(CurPngPlayInfo);
macht etwa 1 > 2% aus.
Ich baue mal ne FPS Anzeige ein.

gruss

jaenicke 27. Mär 2017 05:46

AW: GraphicsContainer
 
Zitat:

Zitat von EWeiss (Beitrag 1365601)
Nein durch den Timer bedingt und zwar abhängig von der Anzahl der Frames.

Das ist ungünstig. Denn wenn du gleiche Inhalte mehrfach zeichnest, geht die CPU Last natürlich nach oben.

Zitat:

Zitat von EWeiss (Beitrag 1365601)
Du hast also eine APNG mit 27MB und 120 Frames bei einer Auflösung von 512x512 das du behaupten kannst die andere Anwendung wäre da besser ?
Das kann man nur dann behaupten wenn man unter fast gleichen Bedingungen vergleicht.

Nein, es sind 570 Frames und 640×480 Auflösung bei 15fps. Aber die Anzahl der Frames spielt gar keine Rolle, wenn man nur so oft zeichnet wie nötig.

EWeiss 27. Mär 2017 06:02

AW: GraphicsContainer
 
Zitat:

Nein, es sind 570 Frames und 640×480 Auflösung bei 15fps
Zeig mir die mal. Wo bekomme ich die her?
Wie groß ist die denn? MB

Zitat:

Aber die Anzahl der Frames spielt gar keine Rolle, wenn man nur so oft zeichnet wie nötig.
? Ich muss so lange zeichnen wie die Frames im Bild sind da führt doch wohl kein weg vorbei
schließlich müssen diese ja auch irgendwie aufs Papier gebracht werden.
Oder zeichnen die sich alleine ?

Also mit dem dicken Teil liege ich bei 12 Frames und einer FrameDimensionTime von 30ms (Wie lange ein Frame gezeichnet wird)
Irgendwas ist da im argen. Das kann ich nicht so lassen (Aber wie ändern)

Denke mal das ist so richtig.. TimerInterval = ms
1000 1 Sekunde
1000 / 60 = 16.666
1 ms = 16

Delphi-Quellcode:
FPScount := GetTickCount + 1000 + (TimerInterval * 16);


Der TimerInterval richtet sich nach der im APNG abgelegten FrameDimensionTime
Wie lange ein Frame abgespielt wird.
Bei Gif kann jedes Frame eine unterschiedliche länge haben bei APNG sind sie alle gleich.


Zitat:

Vielleicht wird die Zeit ja auch für das Aufbereiten der Bilder gebraucht.
Nö sicherlich nicht das liegt im Speicher ist ja auch nur ein Bild.
Oder kannst du da im Code etwas erkennen?

gruss

jaenicke 27. Mär 2017 07:23

AW: GraphicsContainer
 
Zitat:

Zitat von EWeiss (Beitrag 1365603)
Zeig mir die mal. Wo bekomme ich die her?
Wie groß ist die denn? MB

Das ist eine interne Grafik, die ich nicht hochladen kann. Ich werde nachher mal eine ähnlich große frei verfügbare Animation suchen und es damit testen.
Die ist nur 24 MiB groß. Die enthaltene Grafik ist auch nicht sonderlich aufwendig. Dass das an der Geschwindigkeit beim Zeichnen so viel ändert, vermute ich allerdings nicht.

EWeiss 27. Mär 2017 07:30

AW: GraphicsContainer
 
Zitat:

Zitat von jaenicke (Beitrag 1365604)
Zitat:

Zitat von EWeiss (Beitrag 1365603)
Zeig mir die mal. Wo bekomme ich die her?
Wie groß ist die denn? MB

Das ist eine interne Grafik, die ich nicht hochladen kann. Ich werde nachher mal eine ähnlich große frei verfügbare Animation suchen und es damit testen.
Die ist nur 24 MiB groß. Die enthaltene Grafik ist auch nicht sonderlich aufwendig. Dass das an der Geschwindigkeit beim Zeichnen so viel ändert, vermute ich allerdings nicht.

Das kannst du gerne tun.

Nur mit meinem Format (Programm) wird sie denke ich mal nicht abspielbar sein.
Wenn du normale APNG Dateien abspielst hast du kein Standbild sie werden sofort animiert.
Ich habe das Format dahingehen modifiziert so das du das APNG als normales Bild anzeigen kannst.
Animiert wird es dann nur mit dieser Anwendung wenn du die Datei mit dem Konvertierungs Tool (GIFtoAPNG) in meinen Archiv erstellt hast.

Ich habe aber jetzt immer noch das Problem das ich nicht weiß wie man das noch optimieren kann. ;)

gruss

EWeiss 27. Mär 2017 17:33

AW: GraphicsContainer
 
Kann es sein das hier niemand eine Ahnung hat (vielleicht abgesehen von Nico) wie man das System zum zeichnen effizient nutzen kann ohne das man soviel CPU last produziert.
Andere Leute können das doch auch.

Ich meine nicht der umstieg auf andere Bibliotheken sondern mit dem was man hat.
GDI+ und OpenGL mixen als Beispiel.

JA ich weis das GDI+ nicht so schnell ist wie GDI aber mit der GDI kann man nun mal nicht alles so machen wie mit GDI+
Ob man GDI+ noch verwenden soll mag dahin gestellt sein.

gruss

jaenicke 27. Mär 2017 17:56

AW: GraphicsContainer
 
Zitat:

Zitat von EWeiss (Beitrag 1365673)
GDI+ und OpenGL mixen als Beispiel.

Der große Vorteil von OpenGL ist, dass es wirklich komplett auf der Grafikhardware läuft und direkt alles in die Grafikpipeline geschickt werden kann, z.B. in Form von Primitiven oder Operationen wie Vertexoperationen.
GDI+ und OpenGL bzw. DirectX laufen aber komplett parallel, einer nutzt nicht Teile des anderen.

Das mit GDI+ zu mischen kann ich mir nur vorstellen indem du das Bild mit GDI+ vorbereitest und dann per OpenGL/DirectX in den Videospeicher packst um es dort direkt zu nutzen. Dann stellt sich mir aber die Frage warum du dann noch GDI+ nutzen willst, denn alleine mit OpenGL/DirectX sind die Operationen, die du da machst, ja auch nicht komplizierter als mit GDI+. Im Gegenteil, ich glaube die Konvertierung ist sogar noch komplizierter als das direkt ohne GDI+ zu machen.

EWeiss 27. Mär 2017 18:19

AW: GraphicsContainer
 
Zitat:

Im Gegenteil, ich glaube die Konvertierung ist sogar noch komplizierter als das direkt ohne GDI+ zu machen.
Hast du schon mal die TextSuite (GDI+) in Verbindung mit OpenGL verwendet?
Ich denke die arbeiten sehr gut zusammen.

Warum soll also wenn ich Text( am ende auch nur ein Bitmap) mit OpenGL Render das nicht auch mit GDI+ Images gehen.
Ich glaube nich das man da so viel konvertieren muss.

PNG bleibt PNG. ;)
Aber gut ich glaube für mein Problem gibt es im Moment keine Lösung daher stell ich das Teil erst mal aufs Abstellgleis.
Und spiele damit noch was rum.

Ich werde da noch still im Kämmerlein mit Threads experimentieren und herausfinden wie ich die CallBack von GDIPDrawImageRectRectI nutzen kann.
Eventuell bringt das einen Schub.
Sicher bin ich dahingehen aber nicht denn letztendlich müssen die Bilder gezeichnet werde.

Danke.

EDIT:
Letztendlich brauche ich einen Container der in der Lage ist verschiedene Zeichnungsoperationen über die Hardware zu schicken.
Scheint mir aber unmöglich zu sein.


gruss

Zacherl 27. Mär 2017 22:13

AW: GraphicsContainer
 
Ich kann dir nur so viel sagen, dass Mixen im Sinne von: "mal ein paar Zeichenoperationen per OpenGL/DirectX und zwischendurch ein Paar mit GDI+" nicht funktioniert. Was allerdings geht: Grafik per GDI+ laden, in Bitmap konvertieren und Dieses dann als OpenGL bzw. DirectX Textur verwenden.

Was mir auffällt ist, dass du bei jedem Zeichenvorgang zuerst einen temporäre DC und eine DIB Section erstellst. Kannst du nicht einfach direkt auf das Target DC zeichnen?

Ansonsten lass vielleicht mal einen Profiler laufen, während die Animation aktiv ist. Dann solltest du recht genau sehen, welche Aufrufe die meiste CPU Zeit verbraten.

EWeiss 27. Mär 2017 22:23

AW: GraphicsContainer
 
Zitat:

Was mir auffällt ist, dass du bei jedem Zeichenvorgang zuerst einen temporäre DC und eine DIB Section erstellst. Kannst du nicht einfach direkt auf das Target DC zeichnen?
Nein das gehört zur Performance.
Wenn ich das wegnehme geht alles drunter und drüber.

Mit diesem DC erstelle ich einen Temporären DoubleBuffer.
Also alles was in dieser Prozedure geschrieben wird, wird erst auf ein Bitmap gemalt und dann mit BitBlt auf das eigentliche DC des Containers.

Zitat:

Ansonsten lass vielleicht mal einen Profiler laufen, während die Animation aktiv ist. Dann solltest du recht genau sehen, welche Aufrufe die meiste CPU Zeit verbraten.
Kann ich mal machen aber ich weiß ja das GDIP_DrawImageRectRectI und ShowPngPlayInfos die meiste last verursachen.
Ich denke komme nicht drum herum das Bitmap zwischen zu speichern mit GdipCreateCachedBitmap

Ich schreibe mal den Konverter fertig danach kümmere ich mich nochmal darum.
Danke.

gruss

jaenicke 28. Mär 2017 03:16

AW: GraphicsContainer
 
Zitat:

Zitat von EWeiss (Beitrag 1365689)
Also alles was in dieser Prozedure geschrieben wird, wird erst auf ein Bitmap gemalt und dann mit BitBlt auf das eigentliche DC des Containers.

Das meinte ich ja damit, dass da zu viel passiert.
Kannst du nicht einfach zumindest testweise die Bitmap plus DC für jeden Frame vorhalten so wie du es da schon hast? Bei sehr vielen Frames wird das vielleicht zu viel, aber etwas Luft sollte da schon sein.

EWeiss 28. Mär 2017 03:26

AW: GraphicsContainer
 
Zitat:

Zitat von jaenicke (Beitrag 1365697)
Zitat:

Zitat von EWeiss (Beitrag 1365689)
Also alles was in dieser Prozedure geschrieben wird, wird erst auf ein Bitmap gemalt und dann mit BitBlt auf das eigentliche DC des Containers.

Das meinte ich ja damit, dass da zu viel passiert.
Kannst du nicht einfach zumindest testweise die Bitmap plus DC für jeden Frame vorhalten so wie du es da schon hast? Bei sehr vielen Frames wird das vielleicht zu viel, aber etwas Luft sollte da schon sein.

Schau mal überall im Internet jeder wird dir sagen das es ratsam ist ein Buffer-Bitmap zu erstellen alleine wegen dem Flickern schon.
Man kann nicht einfach alles wahllos auf das DC knallen.

gruss

jaenicke 28. Mär 2017 04:43

AW: GraphicsContainer
 
Das es am saubersten und schnellsten ist die Bilddaten direkt in den Arbeitsspeicher der Grafikkarte zu laden (OpenGL / DirectX), darüber brauchen wir uns nicht unterhalten. Aber da das mit GDI+ nun einmal nicht geht, wäre es trotzdem die schnellste Lösung zum Testen, einfach die schon jetzt erzeugten temporären Daten vorzuhalten.

Denn GdipCreateCachedBitmap hält die Daten ja auch nur im Arbeitsspeicher, nicht im Videospeicher. Zum reinen Performancetest hätte ich jetzt nicht die sauberste Lösung vorgezogen, sondern die am schnellsten implementierte.

Ich wollte damit jetzt nicht sagen, dass das eine bessere Lösung wäre.

EWeiss 28. Mär 2017 04:55

AW: GraphicsContainer
 
Zitat:

Zitat von jaenicke (Beitrag 1365701)
Das es am saubersten und schnellsten ist die Bilddaten direkt in den Arbeitsspeicher der Grafikkarte zu laden (OpenGL / DirectX), darüber brauchen wir uns nicht unterhalten. Aber da das mit GDI+ nun einmal nicht geht, wäre es trotzdem die schnellste Lösung zum Testen, einfach die schon jetzt erzeugten temporären Daten vorzuhalten.

Denn GdipCreateCachedBitmap hält die Daten ja auch nur im Arbeitsspeicher, nicht im Videospeicher. Zum reinen Performancetest hätte ich jetzt nicht die sauberste Lösung vorgezogen, sondern die am schnellsten implementierte.

Ich wollte damit jetzt nicht sagen, dass das eine bessere Lösung wäre.

Wir können da jetzt noch lange Diskutieren.
Schick mir deine Mail über PN und ich schicke dir den Source zu.
Damit du sehen kannst was da ab geht. (Aber bedenke ich bin Hobby Progger) ;)


gruss


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:19 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