Victor STINNER a écrit :
Hey,
(Very long email, if you don't have time to read it, go to "PROPOSITION"
at the end)
I would like to improve Wormux speed, because like a friend said to me:
"Worms 1 ran on a 486 [ and Wormux need 2 GHz ]" !!! For sure, Wormux
need too much CPU and memory.
So I wrote a tool to benchmark Wormux: view_stat.py, written in Python.
You have to add StatStart() and StatStop() functions in the code, run
Wormux, and then read result using this tools. Example:
StatStart("Draw:sky");
sky.Draw();
StatStop("Draw:sky");
...
StatStart("Draw:map");
map.Draw();
StatStop("Draw:map");
...
It would be possible to disable stats (so, it doesn't impact of
performance), I prepared #define, but doesn't work with ./configure yet.
So, most important result: in game loop, Draw() eat 99% of CPU and
Refresh() 1% !!! WTF !? It means that *all* CPU is used only to display.
I tried to improve Draw() function. Most interesting point: only draw
sky+ground once (or when the camera moved) make Wormux really faster.
- On my computer : 21 fps => with the patch (*) : 75 fps !!!
- On a friend computer : 40 => 120 fps !!!
So I "think" that we have to work around ... no ? :) My patch can't be
applied because when the sky+ground isn't refreshed, objects aren't
removed when they move ...
(*) "The patch" => in map/ground.cpp and map/sky.cpp, replace "#if 0"
with "#if 1" (in Draw() functions).
--- PROPOSITION ---
Idea: I propose to write a cache for the whole screen. We just need one
big cache which would know "what have to be draw". The game would not
ever ask object to draw them, but ask the cache to draw objects which
"have moved" (or if another object is removed/moved at same position).
The cache would be a list of rectangles where the screen has to be
redrawn.
Start of game and when the camera move:
cache.Invalidate(<whole screen>)
When an object move:
cache.Invalidate(<old rectangle>)
cache.Invalidate(<new rectangle>)
When an object is removed:
cache.Invalidate(<old rectangle>)
(...)
The cache have to manage a list of rectangle and do intersection of two
rectangles. Example:
+---+ +---+
| A | | B |
+---+ +---+
=> can be stored as (A, B) or
+----------+
| big A+B |
+----------+
We have to test if it's faster to draw A and then draw B (which means
call Draw() function of each object twice), or just draw a bigger
rectangle.
Draw function of a sprite/object would become:
void Object::Draw(Rectangle draw)
{
Rectangle intersection = Intersection(this.rect, draw);
if (intersection.isEmpty()) return;
Blit(surface, intersection, ...);
}
What do you think about the cache? I think that I would be "easy" to
write the cache, but very difficult to "upgrade" all Draw()
functions ...
Bye, Haypo
Hello,
I have quite the same idea, but mine is simpliest (but perhaps less
efficient). In fact in the game, early only physical objects move. So
when a object moves, it can register his previous position in a list of
rectangles. So we just have to redraw the sky and ground at this
position and to redraw the physical object itself but completely. But in
fact, writing this email, I understand that it is not sufficient because
sometimes, physical objects are up to other physical objects (for
instance, characters can be on top of wind particles) :'(.
So I totally agree with you proposition :) (without the big A+B, see
explanation of Jean-Christophe)
I don't think it will be so difficult to upgrade all Draw() functions,
let's go ! :)
Bye,
Matt