Games have the wonderful property of communicating well visually, and so when I develop games I want to be able to share videos of my work anywhere. This is the problem statement. The most obvious solution is to use some recording software, record your game and publish that, but this laptop that I have can't handle that. Turns out it's probably some bug in older Samsung SSDs and so my disk write speed is abyssmal.
Conveniently I program my own games, so a simple glReadPixels call gives me the final render result, and that's where I started. My first solution was simply to store these render results in memory, and then when done recording, a thread would write them to disks, each one as a PNG. I would then use FFMPEG to create the video. I used this system to put out two development updates, but the memory requirement was high and so I couldn't record many frames. This is when I got the idea to compress the video while it's in heap memory, and then writing it would be faster because the data is smaller and maybe the encoding could be done fast enough. I first found a single header library called jo_mpeg, which creates MPEG2 files. Quality-wise the results where too bad for me, and the speed wasn't good at 20 FPS if I remember correctly. I then decided to try THEORA from Xiph.org, because I hoped that maybe it had enough options for me to make it fast enough. I stuck with this problem of getting my frames through the theora codec and into an ogg container for about 10 hours straight. I had no experience with this at the start and now I feel like I understand the fundamentals of creating video files. The conversion into YCbCr colorspace was what spun me the most, because I did not know if I did that transformation correctly, which meant that I couldn't verify if the changes I made were a step in the correct direction, until I got the entire pipeline correct. I tried to use Imagemagick to create a YCbCr image, so that I could at least trust that I got the correct input, but it turned out that I had done that incorrectly, so while I was going through my code, trying to figure things out and getting nowhere, I somewhere along the way got it right, but now my input was incorrect, so I couldn't know. Eventually this was discovered, and then I could learn that I couldn't make theora fast enough.
I went to bed and the morning after went straight to work again, because I had got an idea just as I was heading into bed: my laptop can't handle video data, but it can store all user input and recreate a play session. I wrote the code to store all user input immediately, which was something I intended to do anyway, because replays are very useful. After that added a "replay" mode, which shows the last play you made. Then I wrote a YCbCr transformation in a fragment shader, and the game uses this shader if it is in recording mode. I then wrote again some Theora + ogg code, which I had previously deleted, and BAM it all came together. Now when I pass "record-replay" to my game, it playes the last play, but uses a YCbCr fragment shader, and then uses glReadPixels to read each frame, which it encodes and writes to disk. This process is way too slow to keep an acceptable framerate, but I don't need that when I'm playing a replay!
Come the conclusion, this was a trip for me, and I'm noticing now how I fail to put it down in words in a way I feel satisfied about. But I can only get better at that and here's a video showing the result:
The code is valid C and compiles, but notice that in "e_types_names", "TYPE_TWO" doesn't end with a comma. C considers it an array of two elements, "TYPE_ONE" and "TYPE_TWOTYPE_O_NEGATIVE". My compiler didn't warn me that my explicit array size wasn't matched, but it probably would with the correct warning flags, I just can't deal with "const correctness" warnings, and I don't want to figure out the correct combination of flags that suit me.
To add some ethically produced meat onto this post, I want to write about a relevant idea of mine.
In practice the compiler complains that those are not integer constants. There might be a way to make it work, but I'm currently at a place without internet, so I'll just assume it doesn't, but I will argue that the idea behind it is sound. Each of the strings will be stored in the data section of the binary, where it will have a constant and unique address, which means it qualifies for the function of an enum.
EDIT (02 July 2019): Removed "TYPE_END" from very last enum example, because it was confusing. [Link] tags: Cresponsegamejam 08 June 2019