Wait, what’s this?

This is the development blog for ChronoWatch v2.

ChronoWatch is a solo DigiPen project, designed to be a drop-in C++ Code/Routine Profiler. It’s primary purpose is to calculate the costs of systems in relation to other systems and how long the full frame to process. For instance, the Graphics engine may be 65% of your game engine’s frame time, the sprite-loading system may be 30% of the Graphics system’s frame time, so on and so forth.

It is specifically for DigiPen students building a custom engine in GAM courses at the 15X/2XX levels. DigiPen does not currently offer a fully-fledged Game Engine Architecture course, and as such, these students tend to be inexperienced. While I do not intend for ChronoWatch to fill that role, I do feel that it will be of great help to students that are building their first game engine.

It’s worth noting that more than 8 days were spend developing this project – about 300 hours in total were clocked – the 8 days are those in which a prominent feature was implemented, or an interesting thought was had. Many hours went into code refactors, debugging, consultations, the configuration of this website, and so on.

Regards

~Nolan T Yoo

Development Day 9: Release

I am excited to announce the first release of ChronoWatch v2.

Version 2.0: Orange Juice on my Head, is now available for download here. It also includes ChronoDog, finally making it possible to read ChronoWatch’s output in a simple format… kind of.

chronowatch_pic01

Sorting systems by running time, and other user-experience improvements are planned, but I wanted to release what I had since it was officially in a usable state.

Please, enjoy. Feel free to contact me (Nolan@projectexist.net) if there are any issues.

~Nolan T Yoo

Development Day 7: dear, beloved imgui

Perhaps for the better, there is no method to blindly and agnostically draw text onto the game engine’s window:

  • OpenGL or DirectX?
    • Which version?
  • SDL or GLFW?
  • Windows, Mac OS X, or Linux?

There are simply too many variables. For this reason, I have chosen to use dear imgui to help leverage my need to draw data on the screen. However, I quickly realized that adding a helper library to dear imgui would allow:

  • The user to use his own dear imgui helper library.
  • Me to save time when looking up/binding imgui GUI elements.

… This was back when GGEngine was initially being developed, of course. So ChronoWatch depended on GGEngine’s dear imgui helper.

This helper has now been reimplemented in a separate library so that the following is possible:

simplecwi

Simply, calling ChronoWatch::SysInfo.ImguiCommands() before ImGui::Render() will have ChronoWatch draw all of its profiling data. Cool!

Time spent: 8 hours. (Dependencies are hard!)

Development Day 6: String Pool

My first language was Java. So C++ does a few things where I said “why is C++ that way”, and there are other things where I saw “Java should’ve been this way”.

Java uses a String pool – for a touch more detail, see this StackOverflow question. The basic idea is that Strings are saved ahead of time in order to save memory.

ChronoWatch does things “by name”, meaning that it certainly would be nice if we used a String pool. But C++ doesn’t provide one.

The following code provides ChronoWatch with a String pool-like implementation. It’s a little slow because it’s a set, but it helps drastically lower the profiler’s memory usage.

#include <string>
#include <set>

using CW_Atom = const char*;

namespace ChronoTools
{
  static std::set<std::string> interned;

  inline CW_Atom atom_str(std::string const& value)
  {
    return ChronoTools::interned.insert(value).first->c_str();
  }
}

Time spent: 5 hours. (Other minor architecture changes were made to allow the string pool to work in the first place.)

Development Day 5: Time versus Timer

Imagine eight classes defined in the same *.cpp file.

… You can stop crying now.

ChronoWatch was thrown into GGEngine’s TimeUtils implementation, including functionality for telling a thread to sleep… and that’s about it.

Later, StopWatch was also added. This meant that the TimeUtils files (which had been renamed to ChronoSupport.cpp and ChronoSupport.hpp) ran at an approximate total of 840 lines of code. Oops. So much for lightweight.

So today, this happened:

DecoupleAgain

Yes, StopWatch no longer ships with ChronoWatch.

Additionally, ChronoWatch’s initial variables have been moved to their own file, so you can freely change them without looking at the actual implementation:ChronoVars

Time spent: 5.5 hours. (Other minor architecture changes were made in preparation for upcoming/additional functionality.)

~Nolan T Yoo

Development Day 3+4: Readable Output

Commit history for the curious, as far as this work segment goes:

OctProgressReport

So as I re-remember, just because something can update every frame, doesn’t mean that it should. Any text that changes every frame probably isn’t going to be that readable. At least, I know that I can’t read 60 numbers per second.

… So why did ChronoWatch v1.0 insist on updating text every frame? Oops!

While ChronoWatch isn’t outputting profile information into a text file, the text that it does display should probably be readable. As such, it is now possible to change how often ChronoWatch updates its display.

MeterDispFeature

Unfortunately that is a number of frames: the display will update every 12 frames rather than every frame… and rather than every 12 milliseconds.

Oh well. This is approximately 5 updates per second assuming 60 FPS. Obviously that isn’t reality, but I’m happy with this achievement nonetheless.

 

Time spent: 8.5 hours. (Other engine decoupling changes were made.)

~Nolan T Yoo

Development Day 2: What’s a Lambda?

As I prepare for the rewrite (however partial), it’s important to get ChronoWatch v1.0 into a readable state. This means:

  • Adding comments
  • Removing lambdas

“What’s a lambda?” you may ask. My answer – and this is why I’m removing lambdas – is “ask someone else”. I initially used them for writing in-place comparison functions. But that’s, to put it lightly, messy. Especially since the lambda I’m most interested in doesn’t actually change in terms of functionality. But because it’s used in-place, it has to be written twice. Here’s ChronoWatch’s sorting with lambdas:

CW_Lambdas

Notice that the left.second > right.second line doesn’t change between the two std::sort calls.

The solution is to write lambda as an actual function, template it, and now remove the lambdas:

CW_NoLambdas

Disclaimer: There are times and places for lambdas – if there weren’t, they never would’ve been passed into the standard. However, in my case they make the code messier, and I don’t have a complete understanding of them anyway.

Time spent: 5.5 hours. (Other efficiency tweaks were made.)

~Nolan T Yoo

Development Day 1: Decoupling from the Engine

ChronoWatch v1.0 suffers (well, suffered) from two primary problems:

  • It’s actually part of the game engine (GGEngine).
  • It ships with its own macros.

It was originally part of the game engine because, while modular, ChronoWatch wasn’t intended to be used in other game engines. The work that must be done here isn’t the prevent the game engine from depending on ChronoWatch but the other way around – prevent ChronoWatch from depending on the game engine. This specifically means:

  • Cease using GGEngine’s exceptions.
  • Cease using GGEngine’s libraries.
  • Cease providing GGEngine with that awkward macro, which shouldn’t be ChronoWatch specific. It is a design decision that – should the macro be desired – the game team should re-implement it.

GGEngine’s exceptions are no longer used as of today.

CW_Exception


Macros were originally used because it appeared to make sense that a macro should exist for quickly getting the “real” current time. This is no longer the case.

Bye-Bye Macro

Time spent: 2.5 hours. (Admittedly, some tweaks were made to GGEngine as well.)

~Nolan T Yoo

 

Hello world!

This will be the blog for ChronoWatch… or ChronoSuite… or ChronoSweetSuite.
I’m nearly certain I’m going to settle on ChronoWatch (v2.0) as the working title. At least until I feel like doing a non-DigiPen version rewrite.

Until further development, I’d like to close with a joke:

Him: Wow. There's a lot of junk in that trunk.
Me: You're still calling it a trunk? Are you using SVN? We're in the age of decentralized development, you know!
Him: ... I'm leaving.


I’m funny, I swear.

~Nolan T Yoo