Dear programmers,
before I can really start with refactoring the battle interface, the ground part have to be placed. The first part with the introduction of PCH and coding guidelines and the battle interface/ui reorganization is finsihed, but now let’s talk about the animation system. The question of supporting 32bit images in battle and if it could be possible to merge CCreatureAnim and CCreatureAnimation is not fully resolved.
I’ve started with creating a small project to measure the performance of the various techniques of displaying animations which are present at the moment and could be present in the future. I’ve mainly extracted the parts of the VCMI project to get some animation system working. If anyone is interested in that VS project, please let me know via PM.
Animation performance
I’ve measured three facts: The loading time, the amount of RAM used and the drawing performance.
There are 3 different systems present in VCMI, I call it the standard SDL animation system, the compressed RLE animation system and the direct draw animation system. For the tests, I’ve compared those systems with each other and a system which is not present in VCMI currently the Opengl animation system. It was interesting to see that every system resulted in different values which I measured. Let me show now the results first, I’ll talk about how the techniques work later.
Amount of RAM used:
Objective: I’ve loaded 20x times the same DEF file. Values are the arithmetic average of 3 test runs.
BOAR.DEF (around 88x103 pixels images with 12 frames, HOLDING, STANDING anim type)
Standard SDL: 2800KB Compressed RLE: 2031KB Direct draw: 7112KB
CAVALR.DEF (around 98x103 pixels images with 4 frames, HOLDING, STANDING anim type)
Standard SDL: 1250KB Compressed RLE: 884KB Direct draw: 7864KB
OGRE.DEF (around 43x85 pixels images with 8 frames, HOLDING, STANDING anim type)
Standard SDL: 1106KB Compressed RLE: 1072KB Direct draw: 4160KB
For the Opengl test, I’ve loaded a 1024x1024 pixels image(will explain later) 20x times:
Opengl: 82 764KB
Loading time:
Objective: Same as above, now measuring time.
BOAR.DEF
Standard SDL: 136ms Compressed RLE: 140ms Direct draw: 111ms
CAVALR.DEF
Standard SDL: 131ms Compressed RLE: 133ms Direct draw: 110ms
OGRE.DEF
Standard SDL: 117ms Compressed RLE: 117ms Direct draw: 95ms
Opengl: 897ms
Drawing time:
Objective: 15 000x calls to nextFrame, show,… to draw one frame of the animation
BOAR.DEF
Standard SDL: 1005ms Compressed RLE: 1349ms Direct draw: 1916ms
CAVALR.DEF
Standard SDL: 867ms Compressed RLE: 1428ms Direct draw: 2084ms
OGRE.DEF
Standard SDL: 497ms Compressed RLE: 777ms Direct draw: 954ms
Image to draw had consistently a size of 100x100 pixels.
Opengl: 53ms
Summary:
Compressed RLE has less RAM usage compared to standard SDL, but needs more time to draw. Direct draw has the highest RAM usage and the highest draw time(slow!), but has the fastest loading time. Opengl has a high RAM usage, slow loading time but a very short draw time.
Explanation of those different animation techniques
Standard SDL:
Capabilities: Can be used only for 8bit images. It needs to change the color palette to support player colored images. Can’t be used in battles, because the yellow and blue glowing effect can’t be realized efficiently. 24bit or even 32bit support would theoritically be possible, but definitely not practically. High RAM usage + slow drawing time, here is where hardware-accelerated graphics is needed.
Loading: For every frame of a group it creates a SDL_Surface object. Only one group or frame is loaded within the same time.
Drawing: Simple SDL Blit At
Compressed RLE:
Capabilities: Same as above.
Loading: For every frame of a group it creates a specific CompImage object. Only one group or frame is loaded within the same time.
Drawing: Special Blit At(perhaps decompress image)
Direct draw:
Capabilities: Can be used only for 8bit images. Supports shadow drawing + glue effect + player colored images.
Loading: Loads the whole DEF file (every frame, group)
Drawing: Decompress image, pick color from palette, put pixel
Opengl:
Capabilites: Support for 32bit images. Shadow effect can be directly applied to the image. Glue effect and player colored images has to be done with fragment/pixel shaders. You could even display bloodthirstiness effect and some other effects like display frames with embossed + black and white effects,…
Loading: Loads one big 1024x1024 image + ANIM definition file where all frames are placed. No color palette is needed, no overhead. Specific image+anim data file has to be generated once a time.
Drawing: Fast hardware blit, Shader effects(post processing)
Summary:
We need every technique in the short-mid-to-long term future. Opengl is definitely something we should look into, BUT not before release 1.0 as it’s much much work to fully support Opengl rendering. RAM usage is not that high which is the most critic point of that technique. Look: We have max 14 stacks in battle, that means 102410244*14 = around 57MB. That’s nothing today. Yesterday, I informed me about current graphics card, standard is 1GB video ram, better cards have 2GB RAM and the best even 4GB RAM. On mobile phones we could easily stick to the current animation approach of VCMI. I could talk much more about this topic, but I think it’s not the time yet.
So we can’t get rid of any of those explained animation systems, but it’s necessary to provide a common base for these techniques.
Suggestion to a new class design
Currently we have a IImage interface which gets derived by SDLImage and CompImage. CAnimation creates and manages those animations, it supports loading of frames/groups. Then we have CShowableAnim to show multiple frames of an animation and CAnimImage for displaying onnly one frame of an animation both get derived by CIntObject. CCreatureAnim has the possibility to display different animation types in a queue. CCreatureAnimation gets derived by CIntObject and implements DEF loading on its self.
My approach would be and I just talk about the raw picture of it, no details, would be too much:
IAnimation is an interface with a small bunch of methods: showFrame(frameNr, groupNr), loadFrame, unloadFrame, loadGroup, unloadGroup, loadAll, unloadAll, init(ibstream, will explain later). Those load/unload methods can also be empty e.g. for direct draw and opengl.
SDLAnim, CompAnim, DirectAnim, in the future OpenglAnim derive from that interface. They manage resource and drawing handling. CShowableAnim derives from CIntObject and has an instance of a anim class. This instance cannot be accessed from outside. That CShowableAnim class manages showing one frame, several frames and presenting several different animation types in queue altogether. It implements the IUpdatable interface aswell to support a time based animation system.
Input byte stream(ibstream):
It can be created from file or an ui8 array. It provides safe access and automatic scrolling of the read pointer. It also supports byte order handling(little endian, big endian) and the RAII principle. It could be used in many places, so I think such a class would be useful.
Tow: What’s about inserting using boost::shared_ptr to the Global.h? It’s awful to write boost::shared_ptr all the time, it has a unique identifier and when we switch some time from boost::shared_ptr to std::tr1::shared_ptr or std::shared_ptr it would be no problem.
Ivan: Does the resource handling of IImage with increaseRef and decreaseRef really work? In my tests, no loaded images got used a second time.
That’s for now the first part. A second part with URI handling, resource handling is coming in the next days, I don’t want to blow you away.
Have a nice day,
beegee