После обновления до Delphi 12 многие разработчики столкнулись с проблемой значительного замедления интерфейса FireMonkey (FMX) в Windows-приложениях. В этой статье мы рассмотрим возможные причины этой проблемы и предложим решения, основанные на опыте сообщества Delphi.
Описание проблемы:
Пользователи сообщают о снижении производительности, особенно при работе с графическими элементами, такими как TImage и TRectangle. Операции, которые ранее выполнялись быстро, теперь занимают неприемлемо много времени, делая приложения практически неюзабельными.
Первоначальное предположение и его опровержение:
Первоначально проблема была связана с отрисовкой TImage, но дальнейшее исследование показало, что виновником является процедура BringToFront, которая стала значительно медленнее в Delphi 12.1 по сравнению с Delphi 11.
Решение 1: Использование TLayout в качестве родительского элемента:
Один из пользователей обнаружил, что перенос элементов управления (например, TRectangle) с формы (TForm) на TLayout значительно повышает производительность. Это связано с тем, как TForm обрабатывает обновление и Z-порядок своих компонентов.
Пример кода:
var
i, x, y: Integer;
l: TLayout;
begin
SetLength(arrRect,961);
l := TLayout.Create(Self);
l.Parent := Self;
l.Align := TAlignLayout.Client;
i := 0;
for x := 0 to 30 do begin
for y := 0 to 30 do begin
arrRect[i] := TRectangle.Create(Self);
arrRect[i].Parent := l; // Parent to TLayout instead of TForm
arrRect[i].SetBounds(x * 30,y * 30,20,20);
Inc(i)
end
end;
for j := 0 to 1000 do begin
l.BeginUpdate;
for i := 0 to 960 do begin
arrRect[i].Position.X := arrRect[i].Position.X + 1
end;
l.EndUpdate;
Application.ProcessMessages
end
end;
Альтернативное решение 1: Избегайте прямого изменения Position.X:
Вместо прямого изменения свойства Position.X, используйте метод SetBounds для обновления позиции и размеров элемента.
Это может значительно повысить производительность по сравнению с прямым изменением Position.X.
Решение 2: Оптимизация отрисовки с использованием Skia и других графических библиотек:
Хотя изначально сообщалось о замедлении при использовании Skia, правильная настройка может улучшить производительность. Если вы используете Skia, рассмотрите возможность включения других графических библиотек на других платформах и отключения растризации Skia:
Решение 3: Использование многопоточности (Parallel.For) для обновления UI:
Хотя работа с визуальными компонентами в потоках обычно не рекомендуется, в данном случае, когда обновление UI приостановлено с помощью BeginUpdate/EndUpdate, можно использовать Parallel.For из модуля System.Threading для параллельного обновления позиций элементов.
Пример кода:
uses
System.Threading;
...
for j := 0 to 1000 do
begin
BeginUpdate;
try
TParallel.For(0, 960,
procedure(i: Integer)
begin
arrRect[i].Position.x := arrRect[i].Position.x + 1
end);
finally
EndUpdate;
end;
Application.ProcessMessages;
end;
Важно: Использование Application.ProcessMessages может быть нежелательным. Рассмотрите альтернативные способы обработки событий, например, использование TThread.ForceQueue для асинхронного обновления UI.
Альтернативное решение 3: Асинхронное обновление UI с использованием TThread.ForceQueue:
unit Unit1;
interface
uses
System.SysUtils,
System.Types,
System.UITypes,
System.Classes,
System.Variants,
FMX.Types,
FMX.Controls,
FMX.Forms,
FMX.Graphics,
FMX.Dialogs,
FMX.Controls.Presentation,
FMX.StdCtrls,
FMX.Objects;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
arrRect: TArray<TRectangle>;
Counter: integer;
procedure DoMove;
public
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
uses
System.Threading;
procedure TForm1.Button1Click(Sender: TObject);
var
i, j, x, y: integer;
begin
Button1.Enabled := false;
SetLength(arrRect, 961);
BeginUpdate;
try
i := 0;
for x := 0 to 30 do
begin
for y := 0 to 30 do
begin
arrRect := TRectangle.Create(Self);
arrRect.Parent := Self;
arrRect.SetBounds(x * 30, y * 30, 20, 20);
Inc(i)
end
end;
finally
EndUpdate;
end;
Counter := 1000;
DoMove;
end;
procedure TForm1.DoMove;
begin
BeginUpdate;
try
TParallel.For(0, 960,
procedure(i: integer)
begin
arrRect[i].Position.x := arrRect[i].Position.x + 1
end);
finally
EndUpdate;
end;
dec(Counter);
if (Counter > 0) then
TThread.ForceQueue(nil,
procedure
begin
DoMove;
end);
end;
end.
Решение 4: Избегайте частого использования BringToFront:
Процедура BringToFront может быть ресурсоемкой, особенно при большом количестве компонентов. По возможности, избегайте ее частого использования или оптимизируйте логику, чтобы минимизировать количество вызовов.
Анализ проблемы с использованием профилировщика:
Использование профилировщика (например, VTune) может помочь выявить узкие места в коде. В данном случае было обнаружено, что функция FMX.Types.AlignObjects занимает большую часть времени при обновлении UI, особенно когда элементы управления расположены непосредственно на форме (TForm).
Причина замедления: TCustomForm.Realign и AlignObjects:
Когда прямоугольники находятся на форме, AlignObjects вызывается из TCustomForm.Realign, что приводит к значительным задержкам. AlignObjects().DoAlign() перечисляет все дочерние элементы формы (Form.Children), что может быть медленным при большом количестве компонентов.
Вывод:
Замедление FMX UI в Delphi 12 может быть вызвано несколькими факторами, включая неоптимальное использование BringToFront, прямое изменение Position.X, и особенности обработки компонентов на форме (TForm). Использование TLayout в качестве родительского элемента, оптимизация отрисовки с помощью Skia и других графических библиотек, а также использование многопоточности могут помочь повысить производительность. Также важно анализировать код с помощью профилировщика для выявления узких мест и оптимизации критических участков. Важно помнить, что каждое приложение уникально, и оптимальное решение может зависеть от конкретной структуры и логики вашего кода.
В статье рассматриваются причины замедления интерфейса FireMonkey (FMX) в Delphi 12 и предлагаются решения для повышения производительности, включая использование TLayout, оптимизацию отрисовки, многопоточность и избежание частого использования BringToFr
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.