Проблемы с циклической перерисовкой(Win32)

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Проблемы с циклической перерисовкой(Win32)

Сообщение @!!ex » 12.04.2008 12:27:27

Пишу обычное приложение(просмоторщик моделей). С формой.
Нужно форму постоянно перерисовывать. И отображать модель.
Как делал в дельфи:
OnPaint:
begin
//Render
SwapBuffers(DC);
InvalidateRect(Handle,nil,false);
end;
Попробовал сделать тоже самое в Lazaruse.

Во-первых - видимо форма перед отрисовкой заливается цветом. В итоге я наблюдаю жесткое мерцание. ПРичем преимущественно вижу не результат отрисовки, а форму залитую цветом. Понятно, что это EraseBackground отрабатывает, но непонятно почему. Ибо в InvalidateRect говорю, что стирать ничего не надо.

Второй момент - это то, что прога перестает реагировать на закрытие. Даже на Halt;

Пробовал заменять InvalidateRect на SendMessage(Handle,WM_PAINT,0,0) - в принципе работает как надо, но при этом очень сильно тормозит обработка других сообщений, и с заливкой фона проблема остается.

Lazarus v0.9.22
FPC 2.0.4
@!!ex
новенький
 
Сообщения: 35
Зарегистрирован: 12.04.2008 11:55:32

Re: Проблемы с циклической перерисовкой(Win32)

Сообщение alaken » 29.04.2010 12:09:57

UP
похоже тема древняя, может уже нашлось решение?
alaken
постоялец
 
Сообщения: 221
Зарегистрирован: 18.02.2010 09:02:13

Re: Проблемы с циклической перерисовкой(Win32)

Сообщение скалогрыз » 29.04.2010 14:50:11

Нельзя использовать оконную систему, так как это привыкли в полно-экранной графике.

В чём тут проблема. В сообщении OnPaint вызвается процедура Инвалидации, которая говорит, что нужно перерисовать срочно! (что само, по себе не логично, т.к. работает процедура отрисовки)

Некоторые оконные системы (например OSX) просто игнорируют такие вызовы во время отрисовки. (и желаемый эффект цикличной отрисовки не будет достигнут)
Винда же, пытается отрисовать ещё раз. А так как сообщение WM_PAINT приоритетное система хочет обработать его в первую очередь, как итог очередь сообщений забивается WM_PAINT, и ничто другое не обрабатывается.

1) Лекарство - не делать "циклическую отрисовку":

SwapBuffers(DC); Заменяет содержимое окна (не зависимо от того пришло сообщение OnPaint или нет) Потому что OpenGL находится на более низком уровне, чем оконная система. Использовать это можно очень просто - отрисовкой по таймеру! (например каждые 50 милисекунд).

Т.к. такая отрисовка идёт в обход WM_PAINT проблемы с мерцанием не будет (не выполняется LCL код, который закрашивает окно в цвет фона).

Кроме того, сообщение таймера, не является приоритетным, а значит другие действия (мышка, клавиатура) будут обрабатываться. Если процедура рисования сцены достаточно быстрая, то никаких тормозов и/или мерцаний в приложении не будет.

примерный код такой:
Код: Выделить всё
procedure TForm1.RenderGLScene;
begin
  ... рисуем 3d графику...
  SwapBuffers(DC);
end;

procedure TForm1.FormPaint;
begin
  // система требует отрисовки
  RenderGLScene;
end;

procedure TForm1.Timer1Timer
begin
  // таймер требует отрисовки
  RenderGLScene;
end;


На современных композитных движках (опять же OSX), если установить достаточно быстрое время Таймера, процедура RenderGLScene может "затормаживаться" самой системой, чтобы попадать в вертикальную синхронизацию монитора.

Системный таймер можно заменить потоком, который с определённым интервалом, будет оповещать основной поток о необходимости отрисовки. Плюс в таком подходе в том, что таймер не знает о том обработалось предыдущее его сообщение или нет, если возникли какие-то тормоза, то таймер можно "забить" очередь своими сообщениями (WM_TIMER). А грамотно написанный поток, может делать оповещение об отрисовке лишь один раз, и посылать вторичное оповещение только в том случае, если предыдущее сообщение обработалось.

2) Делать "циклическую отрисовку", но "своими силами".

Как уже было сказано выше, сообщение WM_PAINT на Windows имеет приоритет (что может забить очередь сообщений), а Invalidate вызывает мерцание.
Как, опять же, было сказано выше SwapBuffer можно делать ВНЕ обработчика рисования, при обработке любого сообщения.

Естественный выход - использовать своё сообщение, как сигнал к отрисовке:
Код: Выделить всё
const
  WM_MYPAINT = WM_USER;
type
  TForm1 = class(TForm)
  protected
    procedure WMMyPaint(var m: TLMessage); message WM_MYPAINT;
  end;

procedure TForm1.RenderGLScene;
begin
  ... рисуем 3d графику...
  SwapBuffers(DC);
  PostMessage(Self.Handle, LM_USER, 0, 0); // зацикливание
end;

procedure TForm1.FormPaint;
begin
  // система требует отрисовки
  RenderGLScene;
end;

procedure TFrom1.WMMyPaint(var m: TLMessage);
begin
  // своё опевещение
  RenderGLScene;
end;


т.к. сообщение WM_MYPAINT не имеет приоритета, а значит другие сообщения будут обрабатываться.
Добавив счётчик посланных/обработанных сообщений, можно избежать забивания очереди.
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Проблемы с циклической перерисовкой(Win32)

Сообщение alaken » 29.04.2010 20:48:25

спасибо за подробный ответ,
сам смотрю в сторону OpenGL, но думаю что слишком жирно будет для визуального компонета...
к тому же вопрос стоит еще и в кроссплатформенности.
alaken
постоялец
 
Сообщения: 221
Зарегистрирован: 18.02.2010 09:02:13

Re: Проблемы с циклической перерисовкой(Win32)

Сообщение скалогрыз » 29.04.2010 21:25:51

alaken писал(а):но думаю что слишком жирно будет для визуального компонета...

компоненты не должны использовать SwapBuffer или циклическую перерисовку :) таймеры...таймеры...
рисовать компоненту нужно в Paint методе.

alaken писал(а):к тому же вопрос стоит еще и в кроссплатформенности.

ура! OpenGL есть на всех платформах.
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Проблемы с циклической перерисовкой(Win32)

Сообщение alaken » 30.04.2010 07:27:40

скалогрыз писал(а):
alaken писал(а):но думаю что слишком жирно будет для визуального компонета...

компоненты не должны использовать SwapBuffer или циклическую перерисовку :) таймеры...таймеры...
рисовать компоненту нужно в Paint методе.

именно таймерами и организовываю анимацию, но мерцание при перерисовке...
мой компонент наследник от TGraphicControl, в виду того что некоторые регионы компонента должны быть прозрачны.
пробовал TCustomControl в нем свой Canvas организовать прозрачность я не смог. Буду рад если поможете.
В TCustomControl можно подавлять очистку Canvas'а и тогда никакого мерцания, но и никакой прозрачности.

В Paint у меня выводится через TCanvas.Draw уже отрисованный TBitmap
Код: Выделить всё
Canvas.Draw(0, 0, bgBitMap)


скалогрыз писал(а):
alaken писал(а):к тому же вопрос стоит еще и в кроссплатформенности.

ура! OpenGL есть на всех платформах.

думаю что слишком жирно в плане ресурсов системы отрисовывать кнопочку или любой другой компонент с помощью OpenGL, или я не прав?
Тем более компонент будет привязан к OpenGl компоненту.
alaken
постоялец
 
Сообщения: 221
Зарегистрирован: 18.02.2010 09:02:13


Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 245

Рейтинг@Mail.ru
cron