Нельзя использовать оконную систему, так как это привыкли в полно-экранной графике.
В чём тут проблема. В сообщении 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 не имеет приоритета, а значит другие сообщения будут обрабатываться.
Добавив счётчик посланных/обработанных сообщений, можно избежать забивания очереди.