delphi - ExtTextOutW x50 performance drop på QHD/4K skærme efter opdatering af Windows Creators edition

Indlæg af Hanne Mølgaard Plasc

Problem



Af en eller anden mærkelig grund resulterer kaldet WinAPIs ExtTextOutW-funktion for at trække kliptekst på en bitmap med høj opløsning (2560x1440/3840x2160) i et ~ x50-resultat efter opdateringen af ​​Windows 10 med opdateringen af ​​Creators edition. Fra min brugers test og fejlfinding logger, ser det ud til at en lille forskel i bitmappen eller muligvis skriftstørrelsen kan udløse præstationsfeltet.


Her er en fejlfindingslog, der viser resultatet:


10/05/2017 15:51:50 [   63227,186] : Calculate Rect
10/05/2017 15:51:50 [   63227,190] : Rect : Left=263, Top=504, Right=3561, Bottom=2155
10/05/2017 15:51:50 [   63227,193] : Set Shadow Color
10/05/2017 15:51:50 [   63227,198] : Render Text Shadow
10/05/2017 15:51:50 [   63236,650] : Set Text Color
10/05/2017 15:51:50 [   63236,661] : Render Text "Kingdom come Deliverance"
10/05/2017 15:51:50 [   63246,062] : Rendering complete


Som du kan se fra logfilen, tager et enkelt opkald til ExtTextgOutW ~ 9,5 ms, mens det samme opkald tog godt under 1 ms før ophavsmandens opdatering.


Her er den egentlige kode, som du kan sammenligne med debug output ovenstående:


  {$IFDEF TEXTRENDERTRACE}DebugMsgFT('c:log.TextRender.txt','Calculate Rect');{$ENDIF}
  cRect    := Rect(X,Y,Width+X,MainForm.Monitor.Height-(1+(MainForm.Monitor.Height div 540)));
  {$IFDEF TEXTRENDERTRACE}DebugMsgFT('c:log.TextRender.txt','Rect : Left='+IntToStr(cRect.Left)+', Top='+IntToStr(cRect.Top)+', Right='+IntToStr(cRect.Right)+', Bottom='+IntToStr(cRect.Bottom));{$ENDIF}
  {$IFDEF TEXTRENDERTRACE}DebugMsgFT('c:log.TextRender.txt','Set Shadow Color');{$ENDIF}
  srcColor := txtCanvas.Font.Color;
  txtCanvas.Font.Color := OutLineColor;
  {$IFDEF TEXTRENDERTRACE}DebugMsgFT('c:log.TextRender.txt','Render Text Shadow');{$ENDIF}
  Windows.ExtTextOutW(txtCanvas.Handle,X  ,Y+(MainForm.Monitor.Height div 540),ETO\_CLIPPED,@cRect,@S[1],I,nil);
  {$IFDEF TEXTRENDERTRACE}DebugMsgFT('c:log.TextRender.txt','Set Text Color');{$ENDIF}
  txtCanvas.Font.Color := srcColor;
  {$IFDEF TEXTRENDERTRACE}DebugMsgFT('c:log.TextRender.txt','Render Text "'+S+'"');{$ENDIF}
  Windows.ExtTextOutW(txtCanvas.Handle,X  ,Y  ,ETO\_CLIPPED,@cRect,@S[1],I,nil);
  {$IFDEF TEXTRENDERTRACE}DebugMsgFT('c:log.TextRender.txt','Rendering complete'+CRLF);{$ENDIF}


Denne kode gør en meget enkel drop-shadow effekt ved at gøre den samme tekst to gange med en lille forskel i Y-Offset og farve.


Her er den komplette diskussion med mine forumbrugere, hvor vi forsøger at debugge problemet på en lang række hardware (yderligere fejlfindingslogfiler er inkluderet i posten):
http://forum.inmatrix.com/index.php?showtopic=14995u0026amp;page=2[6]


Vi testede med DPI sat til 100\% for at sikre, at udløseren ikke er relateret til DPI-ændringer, der blev introduceret i Creators Edition.


Er der nogen der ved, hvad der udløser dette? og er der et arbejde rundt?


***** opdatering 1 *****


I det mindste ved første test synes 'DrawTextExW' også at være påvirket af præstationstabet. Skrifttypen, der bruges under testningen, er Arial, og udførelsesproblemerne synes at være relateret til skrifttypens størrelse, da en bruger rapporterede, at der tilføjes flere mindre linjer til skærmen (mere tekst gengives med en lavere opløsning) forbedrer ydeevnen meget .


***** opdatering 2 *****


Jeg skrev et lille værktøj til profilering af dette problem, som du kan finde i dette GitHub-depot:
https://github.com/bLightZP/WindowsTextRenderingProfiler[7]


Det ser ud til, at spørgsmålet afhænger af skrifttypestørrelsen, f.eks. På en 2560x1440 skærm, hvilket giver en linje med 'Arial' -teksttekst med en størrelse på '35' 21ms til gengæld i en størrelse på '34', det tog 2ms.


Dette gøres til en HDC af en Delphi TBitmap med et 32bit pixelformat, og deaktivering af klipning har kun en mindre effekt på ydeevnen.


***** opdatering 3 *****


Sebastian Zs svar nedenfor gør genoprettelsen af ​​præ-creators udgave nøjagtigt, og jeg har opdateret prøvekoden på GitHub for at afspejle sit svar, men jeg har siden været i stand til at reproducere problemet med Windows 7 64bit og på en 1920x1080 skærm , så det er ikke begrænset til Windows 10-udgivere eller højopløsningsdisplayer. Simpelthen er udløsertærsklen højere, når skrifttypekvaliteten er indstillet til ANTIALISERET. I min test under Windows 7, ved hjælp af skrifttypen Arial, var triggerpunktet en skriftstørrelse på '109' (hurtig) versus en skriftstørrelse på '110' (x10 langsommere eller dårligere ydeevne). Og denne samme udløsertærskel findes i Windows 10, efter at du har brugt Sebastian Zs svar til at deaktivere cleartype.

Bedste reference


Delphi opretter skrifttypen med lfQuality := DEFAULT\_QUALITY;. Standardkvalitet plejede at være antialiased kvalitet. Men siden Windows 10 Creators opdaterer dette nu standard til cleartype. Og det er ret langsomt. Så løsningen er at manuelt tvinge antialiased kvalitet.


Hvis du bruger en nuværende Delphi-version, kan du simpelthen angive egenskaben Font.Quality:


Procedure RenderText(oBitmap : TBitmap; X,Y : Integer; cRect : TRect; S : WideString; testFunction : Integer; TxtEffect : Integer; EffectColor : TColor; Clipping : Boolean);
// [...]
begin
  obitmap.Canvas.Font.Quality := fqClearType;


I ældre Delphi-versioner er det lidt mere kompliceret:


var
  lf: TLogFont;
begin
  if GetObject(oBitmap.Canvas.Font.Handle, SizeOf(TLogFont), @lf) = sizeof(TLogFont) then
  begin
    lf.lfQuality := ANTIALIASED\_QUALITY;
    oBitmap.Canvas.Font.Handle := CreateFontIndirect(lf);
  end;


Dette er ret gotcha i Windows 10 Creators Update, fordi ClearType-teksten ikke altid er passende, og det kan resultere i uventede resultater.