Reputation: 942
I am playing around a little with Delphi + openGL. Because I am lazy, I wanted to use FireMonkey to make a form for me.
So I made a FireMonkeyHD application, initialized GL, rendered a basic cube... and found some strange behavior. When I do not move my mouse, I get about 10FPS. When I move my mouse, performance rises easily to 500FPS and (obviously) more. What could that be?
*Note: I start rendering with an onKeyDown Event in the Main Thread...
For a better understanding, two pics:
Some code:
unit Unit1;
interface
uses
{ ... }
;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word; var KeyChar: Char;
Shift: TShiftState);
private
degen
: IDeGEn;
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.FormCreate(Sender: TObject);
var
DeGEnFactory
: TDeGEnFactory;
begin
{ ... }
// Load DeGEn
degen := DeGEnFactory.newDeGEn(WindowHandleToPlatform(Form1.Handle).Wnd);
// Initialize
degen.get3D.init(600, 800);
degen.get3D.setOnRender(function : Boolean
var
v3d
: R3DVector;
begin
Result := true;
self.Caption := IntToStr(degen.get3D.getFPS);
v3d.z := 0.01;
degen.get3D.getCamera.move(v3d);
degen.get3D.renderTest;
end);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
// Shut down DeGEn
{ ... }
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; var KeyChar: Char;
Shift: TShiftState);
begin
// Start rendering
degen.startRendering;
end;
end.
And startRendering
looks like this:
procedure TDeGEn.startRendering;
var
msg
: TMsg;
begin
if isRendering then
begin
Exit;
end;
isRendering := true;
while GetMessage(msg, 0, 0, 0) do
begin
TranslateMessage(msg);
DispatchMessage(msg);
if not degen3D.render then
begin
Break;
end;
end;
isRendering := false;
end;
As you might easily notice, the camera just moves away from the cube with a speed dependent on the FPS. Also I get the FPS displayed as form caption.
Upvotes: 1
Views: 976
Reputation: 76537
The GetMessage
waits for a message.
If you don't move the mouse very little messages get into the message-queue and rendering will be slow, because the CPU is stuck waiting for GetMessage to return.
When you move the mouse lots of messages get created; the message-queue is full and GetMessage returns almost instantly.
Note that doing a messageloop like this has not been necessary since Windows 3.1.
Also note that Microsoft warns against implementing the messageloop like this.
From: http://msdn.microsoft.com/en-us/library/windows/desktop/ms644936%28v=vs.85%29.aspx
Because the return value can be nonzero, zero, or -1, avoid code like this:
while (GetMessage( lpMsg, hWnd, 0, 0)) ...
The possibility of a -1 return value in the case that hWnd is an invalid parameter (such as referring to a window that has already been destroyed) means that such code can lead to fatal application errors. Instead, use code like this:
BOOL bRet;`
while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0) {
if (bRet == -1)` ` {
// handle the error and possibly exit
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Anyway there is no need to do the loop like that.
Instead put a timer on the form, and put the code inside the OnTimer
event.
procedure TForm1.Timer1Timer(Sender: TObject);
begin
//DoRendering
end;
If the normal timer is too slow, there are a great many high resolution timers out there. JVCL has done and unDelphiX has one too.
See here: http://delphi.about.com/od/windowsshellapi/a/delphi-high-performance-timer-tstopwatch.htm
or here: http://wiki.delphi-jedi.org/wiki/JVCL_Help:TJvTimer
Handling Windows messages in a CPU intensive loop
We do not muck about with the messageloop anymore (not since Delphi 1.0).
Use Application.ProcessMessages
instead if you find that the application is unresponsive due to your loop hogging all CPU time.
WM_TIMER messages are low priority
If you use the default timer, you will run into unreliability issues.
This is because Windows regards WM_TIMER
messages (the messages that TTimer looks for) as low priority.
If Windows is busy with other tasks it will compress multiple waiting WM_TIMER
messages into one to avoid creating a backlog of timer messages.
It does the same thing with WM_PAINT
messages.
See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms644902%28v=vs.85%29.aspx
One trick to avoid this is to structure the loop using a high resolution timer (this does not depend on the message loop), or to use a simple endless loop with Application.ProcessMessages
and a sleep()
delay.
Upvotes: 5