The Derydoca Engine has been in development now for a little over a year and it has come a long way from rendering it’s first colorful triangle. It now can render complex scenes, load many file formats, supports GLSL shaders, and even some simple particle systems! This has been a side project of mine that I work on in my spare time. Considering all of that, it has moved at a quick pace. One thing that has taken a back seat in this process was code quality and organization. If you have been following my blog, you will know there has been a lack of posts in the last few months. That is because I am finally eating my proverbial programming veggies and cleaning up the codebase.
I have split the solution into multiple projects, implemented pre-compiled headers, started creating unit tests among other things. The code base is looking much better than it did a few months ago. Unfortunately it is hard to put together a flashy blog post with just project maintenance, so I have saved my writing for this one post. Come with me as I detail how my pet project is growing into a well groomed beast!
Splitting up the project
For those who have been in the codebase before, you would have noticed that all of the code exists in one single project. It was simple, and effective. You could build the code and see pretty things drawn to your screen. What more could you want?
Well, for starters, I needed a way to have editor specific objects stripped away from any final builds. If everything is in one project, it is hard to make sure the compiler is ignoring all of the things you don’t want it to know about. Having a separate editor project allows me to write code that I can ensure will not make it into release builds of any projects using the Derydoca Engine. This project is pretty simple as there is not a lot of editor specific code yet, but it will grow as time goes on.
After I extracted all of the editor code, I had to make the engine project compile as a library there is no ‘Main’ function to call. The solution compiled and ran, but when I loaded a scene, only the Camera and skybox were rendering. What gives? Well, the way level files are loaded, components are loaded via a unique string identifier that defines what component belongs on which components. I may delve into the system deeper in a future blog post, but I will give a brief explanation here. A factory in the code takes the string value of a component name and constructs that component type where it is then attached to the related GameObject. That factory relied on static initializers to automatically register each component type. Well, since the compiler was trying to be smarter now that we are compiling as a library, it wanted to strip out all objects not referenced directly in the project. Because of that, those static initializers were no longer being called.
In order to force them to be included, I had to use the /WHOLEARCHIVE command line argument to tell the compiler to include all objects of the engine’s library. This of course bloats the executable because now we are including every bit of code in the project, regardless if it is derived from GameComponent. This led me to my other project I created in the solution in order to better organize the code.
I created a project specifically for the components in the engine. The reason for this is so that I can use the /WHOLEARCHIVE argument on just the engine components and nothing more. It also has the benefit of keeping a clear line in the sand of where GameComponent descendants should exist.
The final project in the solution is the unit test project. This project will be getting the most new code in the coming months as I have been a bad boy and haven’t been unit testing the engine. I will be creating tests for existing code as well as future code changes made to the engine.
As already mentioned, I have been a bad boy and have neglected my unit tests. I have chosen to use the Google Test framework for this unit test project. Google Test seemed to be widely used, and really any modern testing framework would have sufficed.
I have taken the time to write some unit tests for the Transform component, Color and GameObject classes. It has already been a boon to my productivity because I was able to write a unit test that failed on a bug I knew existed and I just needed to run the test after making changes to the code to see if I have solved the issue. I am kicking myself for being lazy on this issue, but at least I have now seen the light and am on the right path!
Speaking of kicking myself, I wish I would have taken the time to implement pre-compiled headers a long time ago. As the code base has grown, so has the time it takes to build the engine. For those who do not know, a pre-compiled header is a header file that gets compiled into a binary object that contains all tokenized code so that the compiler doesn’t need to re-parse many commonly used header files. This is commonly used for the standard template library headers and other third-party headers because they don’t change often.
Of course, the whole point of using pre-compiled headers is to improve your build time. As with any performance optimization tasks, you should always measure your changes to know if you are moving in the right direction. Well, I timed how long it took a clean build to compile and compared it to how long it took after implementing pre-compiled headers. I ended up with a huge speed increase! My build times are now 287% faster! Of course build times are even faster when doing incremental code changes. Shortening the time from code change to seeing it on screen has allowed me to solve issues more quickly.
There were other changes I made along the way to make life easier for me and those looking at the codebase. One of the first things I did was namespace all of my objects. Unlike my other changes, namespacing my objects was a deliberate omission from my early code. My reasoning here was that since this is my first game engine of a respectable size, I most likely wouldn’t know the best namespace each object should be in. Once I had a more solid idea of what components were necessary and how it related to the engine, I was able to organize them.
My namespace change also prompted me to organize my source code on disk. Previously, I was dealing with the default behavior in Visual Studio where it would just dump the new file in the source code directory. The repository looked unwieldy when you opened up the source code directory since there were many objects all with one or two source files associated with them all in one directory. Since all of my objects existed in namespaces now, I created a folder structure matching the namespaces. After updating all of the includes in each code file, the project was compiling and it was easier than ever to find the file you were looking for.
Even though I spent a considerable amount of time on the engine and don’t have any new particle or shader features to show, I am happy I took this time. This has allowed me to focus on my coding standards and solidify what they are. I will employ all that learned in the process to not just the Derydoca Engine, but to my other projects.
I encourage any other developers reading this blog to dig into any of the things mentioned before to improve your iteration times, and code organization. Working on those two things alone can get you a speed up in whatever project you are working on and it is always a worthwhile investment.
Let me know in the comments below what organizational or efficiency related changes you have made that have made a positive impact in your own projects.