Wednesday, March 16, 2016

dw Engine Update Two

dw Engine is my project on QML-based game facilities that extend Qt to form a 2D Game Engine for desktop and mobile. I have been taking my sweet time bringing the engine to higher standards of ease of usage and performance and I would like to share some of it and some thoughts on Qt performance and suitability for such a project.

New Graphics and New Test Level

Tiles were painted on Krita
I decided to take the plunge and convert the demostration game to HD. Along with that came a new, original test level, new sprites for the common gameplay objects, most painted in Krita, and a few rendered in Blender. Sonic's 60-something poses still need to be remade though. Maybe I can get some help with that...


Texture and Spritesheet Managment

The most noticeable problem during the transition to HD was the constant uploading of textures to the GPU. It seemed to happen whenever an Image item was made visible, or when the properties of an Animated Sprite object changed. This was destroying the framerate on mobile, and highlighted the need for more explicit control of the texture lifecycle if one wants to make an action game in QML.  A solution was to create specialized classes (dwTextureCache, dwTexture, and dwImageItem) for managing and displaying textures on the screen. It already supports online conversion of texture to 16bpp and allows for future extension to support texture compression. This will be important on mobile because the game's rendering performance is constrained mostly by memory badwidth limitations, and compression helps with that greatly.

New spritesheet system is simple to use
There was also the need to replace the usage of AnimatedSprite. It has a cumbersome interface and most of the problems of Image. So a new subsystem was introduced with a Spritesheet class and a cache for it, just like with the textures, that describe the animations contained in a texture atlas in terms of sequences. They can have various properties, such as parametric animation speeds and automatic transition to other sequences. Spritesheets are described by a JSON file in a simple format.

Sprite instances are a specialized subclass of dwImageItem, that keep track of the animation data and are updated by a centralized animation controller. It keeps track of multiple animation domains and allows animation control in a global fashion.

Level Editor

The in-game level editor that was mostly stuck in development hell was rethought and is now good enough for my use case. It allows editing of the object layout for a stage, and has specialized modes for adding tile and geometry object types. It is modular and is not even loaded if a level is not started in debug mode, but otherwise can put the entire field in "edit mode" instantly by a single press of the escape key.
Editing the test stage mid-gameplay

Other Small Stuff

Support for game controllers was added, on all desktop platforms, courtesy of SDL2. This was thoroughly tested using my trusty 360 controller, and fleetingly using a couple other controllers as well. It should work on mobile too but this was not tested. 

Moreover, there was a need to render water as a simple colored quad that would multiply the colors underneath it, simulating what a 90's console could accomplish via palette changes mid-hblank. However, there are no blending modes available in QtQuick besides common alpha blending. The way to circumvent this was to create a special QSGMaterialShader subclass that executes custom OpenGL code upon activation, and change the blending mode this way. Thanks to Giuseppe D'Angelo from KDAB for this great tip! To use this material an entire chain of classes had to be created culminating in a special node type. I plan to extend this special node in the future to allow it to render arbitrary textures in arbitrary blending modes, and maybe even point particles.

QtQuick and Performance

Sometimes I wonder if I have chosen the right platform for this project by choosing Qt. Certainly QtQuick is more than sufficient for creating casual games and even some more involved examples, but is it ready for a platformer with sprawling levels and tens of objects onscreen at once? After having to implement a lot of custom infrastructure that replaces core use cases of QtQuick (images, sprites, etc), I would say no, it is not. 

Object creation is very slow, frame sync is a bit wonky on all platforms I test except for Android, the JavaScript engine is a source of major and unexplainable frame time variability, and so on and so forth. I still need to implement some sort of object pooling, otherwise frame skips are going to be a frequent and sore sight on an eventual mobile release.

However, even with all those shortcomings, QtQuick and QML allowed me to accomplish far more than I expected initially when beginning this project. If one understands some of the QML engine's inner workings and good patterns for performance, the level of achieved productivity can be high. And there is the undisputable truth, right here in front of me, that the QSG renderer can churn out 150 frames per second at 1080p on Intel on Mesa. It is a 2D game, sure, but these are performance levels that I would expect from a game engine.

I guess at some point I should go see what can be done with Qt3D... :)