Перейти к содержанию
Fire Monkey от А до Я
  • 0

Целесообразно ли использовать Synchronize для отрисовки


serser

Вопрос

Всем известно, что используя Vcl под Windows нельзя делать делать отрисовку не из главного потока ввиду особенностей канвы и vcl в целом.

Как ведет себя Firemonkey в этом случае на разных платформах? Может уже можно делать эти вещи из других потоков?

(опустим то, что это может привести к размазанности логики отрисовки).

 

Пробовал манипулировать ProgressBar-ом из потока - вроде никаких ошибок не было.

 

Нашел ответ от Brovin Yaroslav,

 

Вся отрисовка выполняется в отдельном потоке (известный как UI Thread), из других потоков рисовать в нем запрещено. При попытке это сделать, вы получите исключение. На андроиде оно несет название "ALOOPER_POLL_ERROR". На других платформах будет немного другой текст ошибки.  Чтобы отрисовка происходила в главном потоке (UI Thread) нужно добавить синхронизацию потоков через 

TThread.Synchronize

или 

TThread.Queen

 

Получается под андроидом это в порядке вещей или  UI Thread = Main Thread?

Изменено пользователем serser
Ссылка на комментарий

Рекомендуемые сообщения

  • 0
  • Администраторы

Под андроидом нужно понимать, что есть следующие потоки:

  1. Main Thread - поток в Delphi, в котором все выполняется. Он же UI Thread в Delphi, в котором происходит отрисовка.
  2. UI Thread Java - поток на стороне Java, в котором выполняется отрисовка и работа со всеми нативными контролами.
  3. UI Thread Java <> UI Thread Delphi.

То есть, если вы работаете с нативными контролами из Java, то вам нужно с ними работать из Java UI Thread. Для это нужно использовать методы из FMX.Helpers.Android:

procedure CallInUIThread(AMethod: TMethodCallback); overload;
procedure CallInUIThread(AMethod: TCallBack); overload;
procedure CallInUIThreadAndWaitFinishing(AMethod: TMethodCallback); overload;
procedure CallInUIThreadAndWaitFinishing(AMethod: TCallBack); overload;

Если же вы работаете с FMX графикой, до достаточно выполнять обычный TThread.Synchronize и Queen

Ссылка на комментарий
  • 0

Спасибо, интересные подробности про андроид и яву. Я пока только под настольный mac os x пробую и только с контролами FMX.

 

Если же вы работаете с FMX графикой, до достаточно выполнять обычный TThread.Synchronize и Queen

Собственно про это и вопрос - а нужно ли? Времена VCL-ной однопоточной отрисовки в этом случае прошли. Вся начинка FMX уже другая. Так вот Может я хочу свой (не главный) поток для отрисовки. Или может быть даже два потока. А не синхронизировать все через TThread.Synchronize. Поэтому и спрашиваю, можно ли так?)

Изменено пользователем serser
Ссылка на комментарий
  • 0
  • Модераторы

Ну вам выше вроде ответили, если не хотите лагов интерфейса то используюте синхронизацию

Ссылка на комментарий
  • 0

Ну вам выше вроде ответили, если не хотите лагов интерфейса то используюте синхронизацию

 

Вы про ALOOPER_POLL_ERROR на андроиде? Да, видимо андроид сам ограничивает этот процесс и мы никогда не узнаем, что будет, если эту проверку в нем убрать. Но что будет происходить на других платформах? На windows и mac os x я не заметил никаких глюков. Может быть при интенсивной отрисовке и будут артефакты (наподобие тех, когда отключаешь вертикальную синхронизацию в играх или двойную буферизацию на компоненте на форме), но их, наверное, можно избежать - например, один поток рисует на одной половине, другой на другой.

Кто-нибудь сталкивался с более фатальными ошибками при отрисовке из НЕглавного потока без синхронизации?

Ссылка на комментарий
  • 0

Сталкивались. По началу очень много))

 

Приложение, активно использующее GUI, то бишь всякие прогресс-бары, видео, большое кол-во элементов с картинками и т.д. и т.п., требует обязательного использования TThread.Synchronize.

Иначе вы можете сначала ничего не заметить, может не быть никаких глюков и т.п.. А потом они могут так же внезапно появиться. Может быть такое, что у Вас их не будет (глюков), а на другом компе - будут. Может зависеть от множества факторов.

НО!

Стоит отметить, что если тот или иной элемент интерфейса не требует именно "моментального" отображения информации (например простое отображение нового значения TLabel), и в данный момент у Вас не происходит ничего активного на форме приложения (например - вывод видео или частая смена картинок и т.п.), то, конечно, делать синхронизацию не обязательно. Однако, это может привести в дальнейшем к непониманию возникновения багов, в том случае если ваш код изменится и появится что-то "тяжелое" для отрисовки.

Эти баги могут быть не явными. Т.е. вы, например, делаете обновление TLabel при "загрузке" чего-либо и обновлении TProgressBar, а текст (новое значение) не отображается. А вот если сделать обновление TLabel и TProgressBar в TThread.Synchronize, то все будет ок.

Тут есть еще одно НО )))

Оно заключается в том, чтобы процесс "обновления" был оптимизирован. Это как слишком частый вызов Application.ProcessMessage в процедуре выполняющий вычисления - будут приличные тормоза!

Пишите код оптимально. Не делайте лишних вызовов TThread.Synchronize, но и не забывайте о них...

 

Как-то так)

Ссылка на комментарий
  • 0

AlexG, спасибо за столь развернутый ответ =)

Выходит, что в каком-то приближении отрисовку в FMX можно назвать потокобезопасной, но не многопоточной. Т.е. AV, как в VCL, не произойдет, но лаги быть могут.

Ссылка на комментарий
  • 0

Выходит, что в каком-то приближении отрисовку в FMX можно назвать потокобезопасной

Выходит, если мы рисуем на одной канве из двух потоков и у нас одновременно открыты в каждом Canvas.BeginScene, то после прорисовки по Canvas.EndScene на канве отобразиться суммарная картинка? 

Ссылка на комментарий
  • 0

 

Выходит, что в каком-то приближении отрисовку в FMX можно назвать потокобезопасной

Выходит, если мы рисуем на одной канве из двух потоков и у нас одновременно открыты в каждом Canvas.BeginScene, то после прорисовки по Canvas.EndScene на канве отобразиться суммарная картинка? 

 

 

Исходя из реализации TCanvasGpu:

function TCanvasGpu.DoBeginScene(const AClipRects: PClipRects; AContextHandle: THandle): Boolean;
begin
  if FGlobalBeginScene = 0 then
  begin
    FCanvasHelper.SetContext(Context);
    FCanvasHelper.BeginRender;
    TTextLayoutNG.BeginRender;
  end
  else
  begin
    FCanvasHelper.Flush;
    FCanvasHelper.SetContext(Context);

    FContext.SetMatrix(TMatrix3D.Identity);
    FContext.SetContextState(TContextState.cs2DScene);
    FContext.SetContextState(TContextState.csAllFace);
    FContext.SetContextState(TContextState.csZWriteOff);
    FContext.SetContextState(TContextState.csZTestOff);
  end;
  Inc(FGlobalBeginScene);
  FSaveCanvas := FCurrentCanvas;
  if Assigned(FSaveCanvas) and FSaveCanvas.FClippingEnabled then
    FSavedScissorRect := FCanvasHelper.ScissorRect;

  FCurrentCanvas := Self;

  Result := inherited DoBeginScene(AClipRects) and Assigned(FContext) and FContext.BeginScene;
  if Result then
  begin
    FClippingEnabled := False;
    FCurrentClipRect := TRect.Create(0, 0, Width, Height);
    FCanvasHelper.ResetScissorRect;
    FCanvasHelper.UpdateDrawingMode;
  end;
end;

В частности из условия второго и последующих вхождений в DoBeginScene: if FGlobalBeginScene = 0 then ... else ...

выходит, что если вошли второй раз, то происходит какой-то FCanvasHelper.Flush куда-то. Возможно и суммарная картинка отобразится. Надо пробовать.

 

Но скорее всего сначала все-таки промелькнет первая отрисованная сцена, а за ней вторая, но уже без изменений в первой сцене. Да, для одновременного рисования на одной канве наверное нужны методы блокировки части изображения. По-моему такие то ли в winapi, то ли в gdi видел.

Изменено пользователем serser
Ссылка на комментарий
  • 0

Но скорее всего сначала все-таки промелькнет первая отрисованная сцена, а за ней вторая, но уже без изменений в первой сцене.

 

Скорее всего нет, если посмотреть это,

procedure TCanvas.EndScene;
begin
  if FBeginSceneCount = 1 then
    DoEndScene;
  if FBeginSceneCount > 0 then
    dec(FBeginSceneCount);
end;

то при одновременном открытии нескольких сцен перерисовка произойдет от последней закрытой, а какой поток ее закроет последний не ясно. Т.е. говорить о наличие потокобезопасности как-то сомнительно.

Изменено пользователем Brovin Yaroslav
Ссылка на комментарий
  • 0
  • Администраторы

Не везде можно использовать канву в другом треде. Все зависит от используемой библиотеки для реализации канвы. Open GL требует отдельный контекст для каждого треда. Так что лучше отрисовку делать в главном потоке.

Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...