Windows 创作者更新后,在 QHD/4K 屏幕上使用 ExtTextOutW x50 时性能下降了。

9

由于更新Windows 10的创作者版本后,调用WinAPI的ExtTextOutW函数在高分辨率位图(2560x1440/3840x2160)上绘制裁剪文本会导致性能下降约50倍。根据用户测试和调试日志显示,位图或字体大小的微小差异可能会触发性能下降。

以下是显示性能下降的调试日志:

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

从日志中可以看出,单个 ExtTextgOutW 调用需要 ~9.5ms 的时间,而在创建者更新之前,这个调用仅需要不到1ms的时间。

以下是实际代码,您可以与上面的调试输出进行比较:

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

这段代码通过以略微不同的Y偏移和颜色渲染相同的文本两次来实现非常简单的投影效果。

以下是我与论坛用户完整讨论,我们尝试在各种硬件上调试问题(帖子中包含其他调试日志): http://forum.inmatrix.com/index.php?showtopic=14995&page=2

我们测试了DPI设置为100%,以确保触发器与创作者版引入的DPI更改无关。

有人知道是什么触发了这个问题吗?是否有解决方法?

***** 更新1 *****

至少在最初的测试中,“DrawTextExW”似乎也受到性能损失的影响。在测试期间使用的字体是Arial,性能问题似乎与字体大小有关,因为一位用户报告称,在屏幕上添加更多的低分辨率行(更多的文本以较低的分辨率呈现)可以极大地提高性能。

***** 更新2 *****

我编写了一个小工具来分析此问题,您可以在此GitHub存储库中找到它: https://github.com/bLightZP/WindowsTextRenderingProfiler

似乎该问题取决于字体大小,例如,在2560x1440屏幕上,使用“35”大小的“Arial”字体文本渲染一行需要21ms,而在“34”大小时只需2ms。

这是渲染到具有32位像素格式的Delphi TBitmap的HDC中,并且禁用裁剪对性能仅有轻微影响。

***** 更新3 *****

Sebastian Z下面的答案确实恢复了创作者版之前的性能水平,我已更新GitHub上的示例代码以反映他的答案,但是我后来也能够在Windows 7 64位和1920x1080屏幕上重现此问题,因此它不限于Windows 10创建者版或高分辨率显示器,只是当字体质量设置为ANTIALIASED时触发门槛更高。在Windows 7的测试中,使用Arial字体,触发点是“109”号字体大小(快速)与“110”号字体大小(性能变差10倍或更多)。在使用Sebastian Z的答案禁用ClearType后,Windows 10中存在相同的触发门槛。


4
去掉 ETO_CLIPPED 标记后,性能是否会下降?位图的色深是多少(例如 24bpp 还是 32bpp)?它是 DIB 还是 DDB?最好加上 [mcve],这样 Stack Overflow 的用户就可以尝试重现。 - zett42
我已经更新了原始帖子,并附上了一个GitHub项目的链接,其中包括源代码和可执行文件,您可以使用它来测试。更新内容包括其他细节。 - bLight
1个回答

4

Delphi 使用 lfQuality := DEFAULT_QUALITY; 创建字体。默认质量曾经是抗锯齿质量。但自从 Windows 10 创作者更新后,这现在默认为 ClearType。这相当慢。因此解决方案是手动强制使用抗锯齿质量。

如果您使用当前的 Delphi 版本,则可以简单地设置 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;

在旧版Delphi中,这会有点更加复杂:
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;

这在Windows 10创意者更新中是一个非常棘手的问题,因为ClearType文本并不总是合适的,可能会导致意外结果。

你的答案可以正常工作并返回预创建版的性能水平,但经过更深入的测试,似乎问题仍然存在,甚至可以追溯到Windows 7,但阈值更高,请参见更新3(大约5分钟后)。 - bLight

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接