Building a 3D Engine in Perl, Part 4
by Geoff BroadwellAugust 04, 2005
At the end of the last article, the engine was quite slow. This article shows how to locate the performance problem and what to do about it. Then it demonstrates how to apply the same new OpenGL technique a different way to create an on-screen frame rate counter. As usual, you can follow along with the code by downloading the sample code.
SDL_perl Developments
First, there is some good news--Win32 users are no longer left out in the cold. Thanks to Wayne Keenan, SDL_perl 1.x now fully supports OpenGL on Win32, and prebuilt binaries are available. There are more details at the new SDL_perl 1.x page on my site; browse the Subversion repository at svn.openfoundry.org/sdlperl1.
|
Related Reading
Games, Diversions & Perl Culture |
If you'd like to help in the efforts to improve SDL_perl 1.x, please come
visit the SDL_perl 1.x
page, check out the code
and send me comments or patches, or ping me in #sdlperl on
irc.freenode.net.
Benchmarking the Engine
As I mentioned in the introduction, when last I left off, the engine pretty
much crawled. It's time to figure out why and figure out what to do about it.
The right tool for the first job is a profiler, which watches a
running program and keeps track of the performance of each part of it. Perl's native
profiler is dprofpp, which tracks time spent and call count for
every subroutine in the program. Examining these numbers will reveal if the
engine spends most of its time in one routine, which will then be the focus for
optimization.
It's best if these numbers are relatively repeatable from run to run, making
it easy to compare profiles before and after a change. For a rendering engine,
the easiest solution is a benchmark mode. In benchmark mode, the engine runs for
a set period of time or number of frames, displaying a predefined scene or
sequence. I chose to enable benchmark mode with a new setting in
init_conf:
benchmark => 1,
The engine already displays a constant scene as long as the user doesn't press any keys; the remaining requirement is to quit after a set period.
In previous articles I've simply hardcoded an out-of-time check into the rendering loop, but this time I opted for a more general approach, using triggered events. Engine events so far have always come from SDL in response to external input, such as key presses and window close events. In contrast, the engine itself produces triggered events in response to changes in the state of the simulated world, such as a player attempting to open a door or attack an enemy.
To gather these events, I added two new lines to the beginning of
do_events; the opening lines are now:
sub do_events
{
my $self = shift;
my $queue = $self->process_events;
my $triggered = $self->triggered_events;
push @$queue, @$triggered;
After processing the SDL events with process_events and
stuffing the resulting commands into the $queue,
do_events calls triggered_events to gather commands
from any pending internally generated events and adds them to the
$queue. triggered_events can be pretty simple for
now:
sub triggered_events
{
my $self = shift;
my @queue;
push @queue, 'quit' if $self->{conf}{benchmark} and
$self->{world}{time} >= 5;
return \@queue;
}
This is pretty much a direct translation of the old hardcoded timeout code
to the command queue concept. Normally triggered_events simply
returns an empty arrayref, indicating no events were triggered, and therefore
no commands generated. Benchmark mode adds a quit command to the queue as soon
as the world time reaches 5 seconds. Normal command processing in
do_events will take care of the rest.

