Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ExtTextOutW x50 performance drop on QHD/4K screens after Windows Creators edition update

For some strange reason, calling WinAPI's ExtTextOutW function to draw clipped text on a high resolution bitmap (2560x1440/3840x2160) results in an ~x50 performance hit after updating Windows 10 with the Creators edition update. From my user's testing and debug logs, it appears that a slight difference in the bitmap's or possibly the font size may trigger the performance hit.

Here is a debug log showing the performance hit:

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

As you can see from the log, a single call to ExtTextgOutW takes ~9.5ms while this same call took well under 1ms prior to the creators update.

Here is the actual code which you can compare to the debug output above:

  {$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}

This code does a very simple drop-shadow effect by rendering the same text twice with a slight difference in the Y-Offset and color.

Here is the complete discussion with my forum users where we try to debug the issue on a wide variety of hardware (additional debug logs are included in the post) : http://forum.inmatrix.com/index.php?showtopic=14995&page=2

We tested with DPI set to 100% to make sure the trigger is not related to DPI changes introduced in the Creators Edition.

Does anyone know what triggers this? and is there a work-around?

***** update 1 *****

At least in initial testing, "DrawTextExW" also seems to be affected by the performance loss. The font in use is during testing is Arial and the performance issues seems to be related to the font's size as a user reported that adding more lower-sized lines to the screen (more text is rendered at a lower resolution) improves performance greatly.

***** update 2 *****

I wrote a small tool to profile this issue which you can find in this GitHub repository: https://github.com/bLightZP/WindowsTextRenderingProfiler

It seems the issue depends on the font size, for example, on an 2560x1440 screen, rendering a line of "Arial" font text with a size of "35" 21ms to render while at a size of "34" it took 2ms.

This is rendered to an HDC of a Delphi TBitmap with a 32bit pixel format and disabling clipping only has a minor effect on performance.

***** update 3 *****

Sebastian Z's answer below does restore pre-creators edition's level of performance and I have updated the sample code on GitHub to reflect his answer, but I have since been able to reproduce the issue with Windows 7 64bit and on an 1920x1080 screen, so it is not restricted to Windows 10 creators edition or high resolution displays, simply the trigger threshold is higher when the font quality is set to ANTIALIASED. In my test under Windows 7, using the font Arial, the trigger point was a font size of "109" (fast) vs a font size of "110" (x10 slower or worse performance). And this same trigger threshold exists in Windows 10 after using Sebastian Z's answer to disable cleartype.

like image 248
bLight Avatar asked May 10 '17 14:05

bLight


1 Answers

Delphi creates the font with lfQuality := DEFAULT_QUALITY;. Default quality used to be antialiased quality. But since Windows 10 Creators update this now defaults to cleartype. And this is quite slow. So the solution is to manually force antialiased quality.

If you're using a current Delphi version then you can simply set the Font.Quality property:

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;

In older Delphi versions it is a bit more complicated:

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;

This is quite a gotcha in Windows 10 Creators Update, because ClearType text is not always appropriate, and can result in unexpected results.

like image 186
Sebastian Z Avatar answered Sep 18 '22 12:09

Sebastian Z