AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Optimierung von Pixel

Ein Thema von EWeiss · begonnen am 7. Apr 2016 · letzter Beitrag vom 12. Apr 2016
Antwort Antwort
Medium

Registriert seit: 23. Jan 2008
3.688 Beiträge
 
Delphi 2007 Enterprise
 
#1

AW: Optimierung von Pixel

  Alt 10. Apr 2016, 22:32
Ich habe das Ganze mal unter Lazarus ans Laufen bekommen, und auch gefunden warum ich keinen gültigen Channel bekam: Du übergibst das Flag BASS_UNICODE ohne Wenn und Aber, aber ich habe nur Delphi 2007 im Büro. Daher war der Dateiname bei mir nicht in Unicode. Da wäre ggf. eine Fallunterscheidung nicht übel, die du zum Abspielen weiter unten sogar schon drin hast.

Die Ergebnisse sind zwischen Delphi und FPC sicherlich kaum zu vergleichen. Ich habe jetzt z.B. mit der 64Bit Variante der Bass.dll gearbeitet, weil Lazarus offenbar keinen Win32 Compiler installiert hat (und ich keine Lust hatte zu suchen wie ich das zurechtstückeln muss).
Ich habe ein fast genau 2min langes MP3 geladen, und brauchte trotz Verwendung von TCanvas.Pixels[] nur 2 Sekunden. Allerdings arbeitet TCanvas.Pixels[] in FPC auch komplett anders als unter Delphi, und Scanline gibt es erst gar nicht. Von daher praktisch keine Chance da für dich brauchbare Zeiten zu messen.

Was ich aber gemacht habe, ist zu messen wie lange die gesamte Prozedur dauerte, und wie viel davon auf das reine Malen entfielen. Bei 2sek gesamt wurden 1,25sek zum Zeichnen gebraucht. Der Rest war Bass-Daten abfragen, die Schleifen und Farbumrechnungen. (Was übrigens zumindest unter Lazarus sehr viel gefressen hat, war das immer wieder Abfragen von Radiobutton1.Checked!)

Hier mal, wie du ähnlich messen kannst. (Erheblich präziser als mit GetTickCount!) Die entsprechenden Zeilen habe ich mal via Kommentar markiert, damit sie besser auffallen.
Delphi-Quellcode:
procedure TForm1.SampleAudioStream(FileName: String);
Const
  BUFFER_SIZE = 256;
var
  Buffer: array of Single;
  Info: BASS_CHANNELINFO;
  i: integer;
  Progress: integer;
  H, S, L: double;
  Value: double;
  StreamLength: Int64;
  ScanLines: TArray<PRGBTriple>;
  P: PRGBTriple;
  R, G, B: Byte;
  pcFrequency: Int64; // <--------------------- Zeitmessung
  timeAll0, timeAll1, timeAll: Int64; // <----- Zeitmessung
  timeDraw0, timeDraw1, timeDraw: Int64; // <-- Zeitmessung
begin
  Channel := BASS_MusicLoad(false, PChar(FileName), 0, 0,
    BASS_MUSIC_DECODE OR BASS_MUSIC_STOPBACK OR BASS_MUSIC_CALCLEN {$IFDEF UNICODE} OR BASS_UNICODE {$ENDIF} OR BASS_SAMPLE_FLOAT, 0);
  if Channel = 0 then
  begin
    Channel := BASS_StreamCreateFile(false, PChar(FileName), 0, 0,
      BASS_STREAM_DECODE OR BASS_MP3_SETPOS OR BASS_SAMPLE_FLOAT {$IFDEF UNICODE} OR BASS_UNICODE {$ENDIF});
  end;

  if Channel = 0 then
  begin
    Exit;
  end;

  Form2.ProgressBar1.position := 0;
  Form2.Show;

  BASS_ChannelGetInfo(Channel, Info);
  StreamLength := BASS_ChannelGetLength(Channel, BASS_POS_BYTE);
  Bitmap.Height := 256;
  Bitmap.Width := (StreamLength div 512 div 4) div Info.chans;

  PB.Width := Bitmap.Width;
  PB.Height := Bitmap.Height;
  PB.Parent.DoubleBuffered := true;

  S := 0.9;

  SetLength(Buffer, BUFFER_SIZE);
  ColumnCounter := 0;

  QueryPerformanceFrequency(pcFrequency); // <------ Zeitmessung
  QueryPerformanceCounter(timeAll0); // <----------- Zeitmessung
  timeDraw := 0; // <------------------------------- Zeitmessung

  if RadioButton1.Checked then
  begin
    SetLength(ScanLines, Bitmap.Height);
    for i := 0 to Length(ScanLines) - 1 do
      ScanLines[i] := Bitmap.Scanline[i];
  end;

  while BASS_ChannelIsActive(Channel) <> BASS_ACTIVE_STOPPED do
  begin
    BASS_ChannelGetData(Channel, Pointer(Buffer), BASS_DATA_FFT512);
    for i := 0 to BUFFER_SIZE - 1 do
    begin
      Value := SQRT(SQRT(Buffer[i]));
      H := 0 + Value / 1.5;
      L := Value;

      if RadioButton1.Checked then
      begin
        QueryPerformanceCounter(timeDraw0); // <-------------------- Zeitmessung
        P := ScanLines[BUFFER_SIZE - i];
        if Assigned(P) then
          HSLtoRGB(H, S, L, P^.rgbtRed, P^.rgbtGreen, P^.rgbtBlue);
        QueryPerformanceCounter(timeDraw1); // <------------------- Zeitmessung
        timeDraw := timeDraw + (timeDraw1-timeDraw0); // <--------- Zeitmessung
      end
      else
      begin
        QueryPerformanceCounter(timeDraw0); // <------------------- Zeitmessung
        HSLtoRGB(H, S, L, R, G, B);
        Bitmap.Canvas.Pixels[ColumnCounter, BUFFER_SIZE - i] := RGB(R, G, B)
        QueryPerformanceCounter(timeDraw1); // <------------------- Zeitmessung
        timeDraw := timeDraw + (timeDraw1-timeDraw0); // <--------- Zeitmessung
      end;
    end;

    if RadioButton1.Checked then
    begin
      for i := 0 to Length(ScanLines) - 1 do
        Inc(ScanLines[i]);
    end
    else
      Inc(ColumnCounter);

    Progress := trunc(100 * (Bass_ChannelGetPosition(Channel, BASS_POS_BYTE) / BASS_ChannelGetLength
          (Channel, BASS_POS_BYTE)));

    if (Form2.ProgressBar1.position <> Progress) then
    begin
      Form2.ProgressBar1.position := Progress;
      PB.Refresh;
    end;
  end;

  QueryPerformanceCounter(timeAll1); // <-------------------------------------------------- Zeitmessung
  timeAll := timeAll1-timeAll0; // <------------------------------------------------------- Zeitmessung

  Label1.Caption := 'Gesamtzeit: '+FloatToStrF(timeAll/pcFrequency, ffFixed, 8, 3); // <--- Zeitmessung
  Label2.Caption := 'Draw-Zeit: '+FloatToStrF(timeDraw/pcFrequency, ffFixed, 8, 3); // <--- Zeitmessung

  Form2.Hide;
  Bass_StreamFree(Channel);

  Channel := BASS_StreamCreateFile(false, PChar(FileName), 0, 0, 0 {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF});
  if Channel = 0 then
  begin
    Channel := BASS_MusicLoad(false, PChar(FileName), 0, 0,
      BASS_MUSIC_RAMPS or BASS_MUSIC_POSRESET or BASS_MUSIC_PRESCAN {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF}, 0);
    if (Channel = 0) then
      Exit;
  end;

  DestBitmap.Width := PB.Width;
  DestBitmap.Height := PB.Height;
  BitBlt(DestBitmap.Canvas.handle, 0, 0, PB.Width, PB.Height, Bitmap.Canvas.handle, 0, 0, SrcCopy);
  bpp := BASS_ChannelGetLength(Channel, BASS_POS_BYTE) div PB.Width;
  BASS_ChannelSetSync(Channel, BASS_SYNC_END or BASS_SYNC_MIXTIME, 0, LoopSyncProc, nil);
  BASS_ChannelPlay(Channel, false);
  Timer1.Enabled := true;
end;
Die Zeit wird hierbei in Sekunden angegeben. (Deshalb die 3 Nachkommastellen um die Anzeige bis auf Millisekunden genau zu haben.)
Das könnte gut dabei helfen zu ermitteln, ob das eigentliche Zeichnen wirklich der Flaschenhals ist, und ob es sich lohnt in dessen Optimierung überhaupt noch viel Zeit zu stecken. (Ich kann mir gut vorstellen, dass das Berechnen des Fortschritts wegen der vielen Calls zur Bass.dll auch ganz schön zu Buche schlägt. Tut's zumindest unter Lazarus nicht. Der Unterschied ist im normalen Schwankungsbereich.)
"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)

Geändert von Medium (10. Apr 2016 um 22:38 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#2

AW: Optimierung von Pixel

  Alt 11. Apr 2016, 00:59
(Ich kann mir gut vorstellen, dass das Berechnen des Fortschritts wegen der vielen Calls zur Bass.dll auch ganz schön zu Buche schlägt. Tut's zumindest unter Lazarus nicht. Der Unterschied ist im normalen Schwankungsbereich.)
Ganz normale Calls sind in der Regel zu vernachlässigen. Hat man natürlich bisschen Overhead durch das Pushen der Rücksprungadresse und je nach Aufrufkonvention der Parameter, aber Performance-kritisch wird das nicht.
Frequente Aufrufe bestimmter Windows APIs können allerdings durch den Kontext-Switch in Kernel-Mode und wieder zurück (SYSENTER) extrem teuer sein.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
EWeiss
(Gast)

n/a Beiträge
 
#3

AW: Optimierung von Pixel

  Alt 11. Apr 2016, 06:16
Zitat:
Du übergibst das Flag BASS_UNICODE ohne Wenn und Aber, aber ich habe nur Delphi 2007 im Büro. Daher war der Dateiname bei mir nicht in Unicode. Da wäre ggf. eine Fallunterscheidung nicht übel, die du zum Abspielen weiter unten sogar schon drin hast.
Oh.. ja habe ich im ersten BASS_StreamCreateFile Call vergessen.
Man schaut immer zu sehr auf seine eigene Developer Umgebung.

Zitat:
Von daher praktisch keine Chance da für dich brauchbare Zeiten zu messen.
Würde auch nicht viel sinn machen mit unterschiedlichen Sprachen.

Zitat:
Was übrigens zumindest unter Lazarus sehr viel gefressen hat, war das immer wieder Abfragen von Radiobutton1.Checked
Sollte das kleinste Problem sein
Werde da mal eine Variable für anlegen um den zugriff auf das Control zu verhindern.

QueryPerformanceCounter ist natürlich etwas genauer als GeTickCount.. wird umgebaut.

Danke für das Testen wenn auch unter Lazarus.

Anbei vergleich von Pixel/ScanLine als Bilder und neue Version mit deiner Zeitmessung incl. Variable anstelle von Radiobutton1.Checked.
BASS_UNICODE ist abgesichert..

gruss

Geändert von EWeiss (11. Jul 2019 um 16:07 Uhr)
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.688 Beiträge
 
Delphi 2007 Enterprise
 
#4

AW: Optimierung von Pixel

  Alt 11. Apr 2016, 08:22
Man schaut immer zu sehr auf seine eigene Developer Umgebung.
Kenn ich was von *schäm*

Zitat:
Würde auch nicht viel sinn machen mit unterschiedlichen Sprachen.
Hab jetzt leider einen Aussentermin, aber heute Nachmittag mal schauen wie es hier im Büro aussieht. Jetzt weiss ich ja wie ich an einen gültigen Kanal komme

Zitat:
QueryPerformanceCounter ist natürlich etwas genauer als GeTickCount.. wird umgebaut.
Ich finde an dem so praktisch, dass es damit tatsächlich aussagekräftige Ergebnisse gibt, selbst wenn man nur eine kleine Zeile misst. Ohne das wäre das Aufsummieren der Zeichenaufrufe kaum drin. Hab mir den mittlerweile angewöhnt.

Zitat:
Anbei vergleich von Pixel/ScanLine als Bilder und neue Version mit deiner Zeitmessung incl. Variable anstelle von Radiobutton1.Checked.
Das sind SEHR ähnliche Werte wie bei mir (Bei Nutzung von Pixel[])
300ms zum Zeichnen sind schon gar nicht so übel. Vor allem weil es ja nur ca. 30% der Gesamtzeit ausmacht. So ganz so viel ist da dann gar nicht mehr herauszuholen.
"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
EWeiss
(Gast)

n/a Beiträge
 
#5

AW: Optimierung von Pixel

  Alt 11. Apr 2016, 08:29
Zitat:
Das sind SEHR ähnliche Werte wie bei mir (Bei Nutzung von Pixel[])
300ms zum Zeichnen sind schon gar nicht so übel. Vor allem weil es ja nur ca. 30% der Gesamtzeit ausmacht. So ganz so viel ist da dann gar nicht mehr herauszuholen.
Denke ich auch..
Die Ergebnisse sind schon mal nicht schlecht
Danke für deine Zeit die du geopfert hast.
Vielleicht ist es ja auch etwas für andere die so was schon immer mal realisieren wollten.

gruss
  Mit Zitat antworten Zitat
Benutzerbild von Memnarch
Memnarch

Registriert seit: 24. Sep 2010
737 Beiträge
 
#6

AW: Optimierung von Pixel

  Alt 11. Apr 2016, 10:18
Kurze Frage(vllt hab ich das übersehen):
Warum puffert ihr die Scanlines? Beim Abfragen einer Scanline bekommt ihr doch nur einen Pointer. Von dort aus kann man sich selbstständig durch die Bitmap wühlen o.O.

Habe mich immer daran gehalten:
http://edn.embarcadero.com/article/29173

Um es damals in meinem SoftwareRenderer zu implementieren:
https://github.com/Memnarch/Software...ter/Shader.pas
https://github.com/Memnarch/Software...olorShader.pas
Da man Trunc nicht auf einen Integer anwenden kann, muss dieser zuerst in eine Float kopiert werden
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.676 Beiträge
 
Delphi 12 Athens
 
#7

AW: Optimierung von Pixel

  Alt 11. Apr 2016, 10:27
Dazu müsste man wissen, ob die Zeilen in aufsteigender oder absteigender Reihenfolge vorliegen. Dann könnte man das so machen.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#8

AW: Optimierung von Pixel

  Alt 11. Apr 2016, 17:10
Kurze Frage(vllt hab ich das übersehen):
Warum puffert ihr die Scanlines? Beim Abfragen einer Scanline bekommt ihr doch nur einen Pointer. Von dort aus kann man sich selbstständig durch die Bitmap wühlen o.O.
Das hatte ich irgendwo weiter vorne auch schon in Betracht gezogen. Sprich: ScanLine von der ersten Zeile anfordern und danach den Begin er weiteren Zeilen im zurückgelieferten Zeiger manuell berechnen.

Allerdings gibt es da ein paar Edge-Cases, die man beachten müsste. Windows erlaubt z.b. Bottom-up Bitmaps, bei denen man die Indizes der Zeilen invertieren müsste. Auch kann man die Zeilen nicht einfach in der Form CurrentLinePointer := Pointer(PByte(ScanLine0Pointer) + ((Bitmap.Width * 3{pf24Bit}) * CurrentLineNumber)) berechnen, weil die Daten im Speicher unter Umständen aligned werden. Das kann man aber theoretisch alles ermitteln, also machbar wäre es schon.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  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 01:18 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz