Categories
Uncategorized

GSoC summary and final report

Hi all, my name is Kartik Agarwala(hax0kartik) and in the past three months, I worked as a GSoC student under the mentorship of Eugene Sandulenko(sev) and Strangerke with ScummVM. The main focus of my project was to integrate the CRAB engine, developed by Pyrodactyl Games, into ScummVM. This blog serves as a final report of what I accomplished in this three-month period.

What’s been done?

The original milestones as per the project proposal were:

  1. The skeleton engine with some of the main classes is compilable within ScummVM.
  2. Skeleton engine is extended to fully include the first component. This includes loading and parsing XML files.
  3. Work on the graphics component.
  4. Get Unrest into a somewhat working state by integrating the essential components.
  5. All the components are integrated aside from audio.
  6. Integrate audio and ensure that the game is playable.

All of these milestones have been met.

CRAB has been fully integrated into ScummVM and Unrest is playable from start to end.

My changes were merged in the main ScummVM branch with PR #5229. You can also see the “feat-CRAB” branch in my fork to view the most recent changes I made.

Accepted changes

I created a number of PRs in the last three months, all of which were accepted. The following are some of the more important PRs that I created with a brief description.

  • PR #4753: Made text selectable in the input boxes.
  • PR #4766: Implement MKV decoding.
  • PR #4801: Added ProjectorArchive for the Director Engine.
  • PR #5229: Integrate CRAB engine into ScummVM. This was the main PR for this project.

What work is left?

The following list of things need to be improved or are missing:

  1. Filenames are inconsistent.
  2. Unimplemented “fade-in” effect of music.
  3. Saving/Loading isΒ onlyΒ available through GMM load/save menus.
  4. “Options” menu selectable from the main menu is broken.
  5. Keymapper needs improvement to make the game fully playable using only a keyboard.
  6. Unimplemented “ironman” saves.

Conclusion

My time as a GSoC student has been fantastic. In the last three months,Β  I have learned a lot from my mentors and the community. I want to note that the ScummVM community particularly has been very welcoming and helpful to me.

I would like to extend a special thanks to my mentors, Eugene Sandulenko(sev) and Strangerke who answered a lot of my questions and provided continuous feedback on the changes I was making. Their expertise helped me complete the project successfully.

Oh, and, I was given the offer to join the ScummVM team, which I have accepted πŸ™‚ I hope to continue working on the CRAB engine and help maintain other engines in the time to come.

Thanks for reading!

Categories
Uncategorized

Getting CRAB merged into main branch

Hi all, welcome back to my progress blog for CRAB. As we get closer to the end of the GSoC period, I am happy to inform you, that the PR for integrating CRAB into ScummVM has been merged! πŸ™‚ In this blog post, I would like to detail what changes and improvements I made last week.

While most of my commits were around code formatting fixes and code cleanup, I did make a few exciting changes. My mentor, Sev, pointed out that Unrest had a demo version available on Steam, and I was able to add support for the demo version as well. This means that all of you can at least play one stage of Unrest for “quick feels” of how CRAB works on ScummVM!

Unrest – Demo running in ScummVM

Initially, the demo crashed due to bugs in MusicManager but I was able to fix it and now you can play the whole demo. As an engine developer, the consequences of having a demo version are really exciting because now a larger audience would be able to test out the engine. πŸ™‚

Next, I have made the debuglines that are drawn on the screen togglable using ScummVM’s debug console. You need to press CTRL+ALT+D and then type in the draw `XXX` command where XXX is the feature(TMX, Pathfinding, etc.) for which you want to draw debug lines.

Possible arguments for the draw command
Possible arguments for the draw command
Screenshot from the demo with draw TMX on
Screenshot from the demo with TMX debuglines on

I made some other small fixes here and there but that is all I did this week. The major progress of this week was that I was finally able to get CRAB merged, phew. Oh, and in other news, I have joined the ScummVM team. πŸ™‚ I plan to keep working on the CRAB engine and I’m also thinking of helping out with the SLUDGE engine. We’ll see.

Thanks for reading as always!

P.S., here are some links for announcements of CRAB getting merged into ScummVM:

Twitter: https://twitter.com/ScummVM/status/1689658519749013504

Facebook: https://www.facebook.com/ScummVM/posts/pfbid0d9pQ1LCKKsgH9JZ6EF5tdbKuJVpNXgYLryHmmLg5VG6vh7zzAPVAikuwym61YKKZl

 

Categories
Uncategorized

Finishing audio and polishing the engine code for PR

Hi all, welcome back to my progress blog for CRAB. This week, I worked on finishing the audio and made many other minor changes to gear up for getting my fork merged into the main branch.

The audio subsystem is fully in-place aside from the background music’s “fade-in” effect. You are able to set the SFX volume and the music volume from ScummVM’s game options menu. I fixed a variety of bugs in the audio system which could lead to crashes sometimes.

Next, I made various other minor tweaks such as moving some warnings to debugs and cleaning up the codebase as suggested by my mentor Strangerke. Wanting to have some fun, I compiled ScummVM with the CRAB engine for my Android phone to check whether it would run and if yes, to what stage I could proceed. To my delight, it ran wonderfully – till stage 5 where it just crashed without any logs. πŸ™‚

Unrest running on Android 13

Furthermore, I had a function stubbed out which made my saves unplayable. After un-stubbing the function and performing some little fixes, I tried again on my laptop and this time I made it all the way to the end!

I did notice some small issues in my gameplay which I was able to fix eventually. There was this interesting graphical glitch in stage 5 where the layering was broken.

If you look at this screenshot again, the character is supposed to be in a room but instead, due to the broken layering, she appears over the wall. This was happening due to incorrect handling of “Autohide” layers, and once I fixed the mechanism, it worked perfectly.

The room is drawn correctly.

Once you enter the room, these walls are supposed to simply disappear, so that you can see the characters in the room.

Yay! Fixed πŸ™‚

This is all that I achieved this week, and since the game is working without any crashes I decided to open a PR for merging my branch into the main branch. PR #5229. I should note that it might take a while for the PR to get merged, but once merged, you all should be able to play Unrest using ScummVM on your favourite devices.

Thanks for reading!

 

Categories
Uncategorized

Finishing refactoring and implementing audio

Hi all, this week, I finally finished refactoring the code and feeling a bit adventurous, I went on and started work on implementing the last remaining subsystem – audio. In this blog post, I would like to detail to some extent what has been implemented in the audio subsystem and what is left to do.

A short introduction to the audio subsystem

CRAB utilized the SDL2_Mixer library previously, to play the background music and the various sound effects. Before reaching the main menu, the engine initializes the audio subsystem during which all the sound effects are read into memory. On the other hand, the background music files are only read as per need and are “decoded on demand“. It should be noted that the audio subsystem “fades-in” the background music, i.e., it starts playing the background music at silence and then gradually increases the volume to normal.

The audio subsystem also allows one to set the volume of sound effects and music channels independently according to their own taste.

The process of porting

Initially, I found the task to be daunting because I have never been able to fully grasp the different terms and concepts related to digital audio. I went through the documentation of SDL2_Mixer, and once I had a basic idea, I tried figuring out what equivalents were there in ScummVM code.

Decoders for both the OGG and WAV files were already present which saved me a lot of effort. Additionally, the mixer class seemed to fulfil most of my requirements. I did encounter a bunch of crashes at first and after a bit of trial and error, I was able to get both music and sound effects to work!

In the upcoming week, I’ll try implementing the “fade-in” effect of the background music and the ability to set the volume for the sound effects and background music. Thanks for reading!

 

 

 

 

Categories
Uncategorized

Refactoring code and misc. changes

Hi all, this week I worked on formatting the code to match the code formatting conventions set by ScummVM. In my opinion, this is a step in the correct direction as it would get the repo in a state where it could potentially be merged into main ScummVM branch.

What needed to be changed?

While most of the formatting issues could be fixed by running tooling such as clang-format, others need to be fixed manually. Majority of my commits in the last week have been around converting the function names to camelCase and prefixing member variables with underscore.

Initially, I thought it would be an easy job but once I actually got to it, it turned out to be really taxing and frankly – boring.

I started out by manually making changes in the class definition,compiling, going through the compilation errors, and then fixing the references and as you can imagine it took a lot of time.I could not shake the feeling that there should be some tooling that could help me out and make the process easier. This is where I came across the “refactoring” feature in CLion, which could automatically rename/fix the references. I tried it out and although not perfect, it has definately made the process easier.

As of writing, I have fixed the formatting issues of atleast 5 components – ai, animation, event, input, and item . Next week, I will be working on fixing the rest of the components. That is all for this week. Thanks for reading!

 

Categories
Uncategorized

Working on save game support

Hi all, past week’s progress has also been slow due to health issues. However, I have started the work on adding savegame support.

The rationale behind this step was that it would help cover more stages/levels during tests, since till now, I have not tested past stage 3. Once the savegame implementation is done, I and my mentors would be able to create savegames at different stages which we could share and test if every stage is working as expected.

Coming to the topic of CRAB’s save system, it works by dumping the state of all the game objects to an XML file which is later read to instantiate the various objects according to the values when the savefile is loaded. This includes dialogues of various characters present in the level. I found this to be weird because if I had to design a save system, I would only save the level number and when that would be loaded, the character dialogues would get loaded in as well.

Anyways, I was able to port the save system to be compatible with ScummVM but immediately there were some noticeable problems.

The very first problem was that font rendering in the loading menu is broken due to some reason(The glyphs for some strings are not rendered properly)

Broken font rendering

Next, due to the way keymapper has been implemented, when inputting the save name, certain keys get skipped. For example, when typing in “kartik” only “kark” gets processed since both “T” and “I” are bound to some game actions.

To remedy these bugs, I decided that I would simply replace both the save menu and load menu with ScummVM’s GMM load/save menus. This required a lot of changes to the save system but I was able to get it done. Pressing the save/load button would now open ScummVM’s load/save menus and not the original ones.

Improved load/save menus

While this does work as expected, my mentors want me to keep the original menus so that the user can use the original menus if they wish to do so.

This is all that I was able to accomplish this week. While savegames do work, a lot of changes still need to be made before it can be called complete. Thanks for reading!

PS: On a personal note, I would like to dedicate all the further work and the work till now to my grandmother whom I lost this week. I hope I continue to make you proud!

Categories
Uncategorized

More Graphical fixes

Hi all, the blog post for this week will be relatively short as I have been sick lately, however, I did fix a few things.

Fixing texture rotation

At the start of the week, I tackled the problem of the missing texture rotation due to which tiles were incorrectly rendered as below.

Incorrectly rendered tiles

I tried fixing this using the rotoscale function, however, that gave a weird “boxy” look to the rotated tiles. For example, in the following picture, it is particularly noticeable in the grass texture at the bottom left of the screen.

This was intriguing as this function was already being used by other engines such as WinterMute and they did not seem to have come across this problem. To dig deeper, I dumped the texture to a png file after rotation and – apparently the rotated texture had different dimensions(32×33) as compared to the original texture(32×32). I was, however, not able to fix this.

FractureHill, the developer for the “Nancy Drew” engine confirmed that he had the same issue and ended up rolling his own implementation for rotating textures. So, I copied the relevant code, optimized it a bit, and voila – it worked perfectly! πŸ™‚

No more “boxy” look!

Miscellaneous other graphical fixes

There are some other small fixes that I made this week, most noticeably I fixed a bug due to which border tiles were missing from some scenes.

Missing tiles at the bottom
No more missing tiles!

That is all for this week. Next week I will focus on getting saves to work correctly. Please look forward to my future blog posts and thanks for reading! πŸ™‚

Categories
Uncategorized

Getting keyboard events to work using keymapper

Hi all, I’ve spent my time implementing keyboard controls using Keymapper this week. In this blog, we’ll talk about a bit of the process and the final result.

A short introduction

By utilizing the key mapper, you can allow users to assign actions to keyboard keys or shortcuts, or to mouse or joystick buttons.

Users can go into the “keymaps” tab and change their keymappings from there.

In the original game, the ability to remap keys was already present.

As you can see Unrest allowed you to remap two “categories” of keys – Gameplay and Interface.

Process

Initially, I faced problems in implementing the keymaps correctly due to the way the keymap architecture worked.

From what I understand, the Keymapper architecture is primarily made of a collection of keymaps called a KeymapArray, where every keymap has a collection of actions.

Almost all the pre-existing engines utilized a single Keymap. However, our case required at least two Keymap(s) – one for the gameplay and one for the UI components.

You cannot simply add two Keymaps to the Keymapper because of the way inputs are handled in Unrest. Only a single Keymap needs to be active at any given time.

After some deliberation with my mentors, we came up with a simple solution. All I needed to do was simply switch to the UI Keymap when a UI component was visible on the screen and then switch back to the gameplay one once the UI component was closed. There was one small problem in this though, the game didn’t keep track of whether a UI element was being drawn on screen or not.

I went through the source code of the engine and noticed that a majority of UI components were inherited from the menu class. I utilized this to my advantage and simply added the Keymap flipping logic to the menu class. This worked wonderfully, and now I was able to control most of the game using my keyboard!

Results

You are now able to remap three different categories of keymaps – Game, UI and HUD.

These are the default keymappings which you will be able to change as per your wish! πŸ™‚

Thanks for reading!

Categories
Uncategorized

Getting rid of STL and a troublesome bug.

Hi all, this week I worked on replacing STL(Standard Library) with the alternatives provided by ScummVM. I will be talking about the same today.

Why do we need to replace STL

The answer to this is that ScummVM needs to support many platforms, and every platform may not provide a modern enough compiler for the codebase to compile. For example, an engine may require features introduced in c++17, but some platforms’ compilers might only support c++11 at max. Therefore, to maintain compatibility ScummVM provides its own replacements for STL.

How I replaced STL in CRAB

During the writing of my proposal, I had already gone through the codebase of CRAB and made a note of what STL containers CRAB used and the replacements that existed for them in ScummVM.

Alternatives for almost all the STL containers/algorithms needed for CRAB were already available, thus, the process was basically effortless. The only thing which did not exist already in ScummVM and that I added was Common::remove().

With commit #03b75da the engine became fully independent of any STL headers which were not permitted by ScummVM(Remember, we had previously used FORBIDDEN_SYMBOL_ALLOW_ALL to get past this requirement.)

Bonus: A troublesome bug

After I had made a significant number of commits replacing STL with their alternatives, I switched my OS back to Windows and tried compiling with mingw64 and guess what, the engine would fail on an assert statement. Now, this is where things got interesting. When the engine was compiled using any other compiler(gcc, msvc, clang++), the assertion never failed and it continued as normal. πŸ™‚

Mingw64 did not support ubsan as of writing and the trace produced by gdb looked.. valid and okay. This is where I took the good old debugging-via-printf route and generated a log file which I compared to the logs produced by the original game and Viola! I noticed a difference.

The GameEvent class has an enum member state which decides what animation frame for a particular character is drawn. In the logs produced by the original game, the value for state was suspiciously large for the first few frames whereas the other log showed that the value was pretty small always (1 – 3). The question that immediately arose in my mind was why would the value be so large in the original game as this was an index number and it is supposed to be small. I added some more printfs and soon I discovered the core issue – state variable was never initialized as the parametrized constructor did not delegate to the default constructor and thus it contained garbage values. πŸ˜‰

And with this single-line fix, the engine started to work again when compiled using mingw64!

My primary purpose in mentioning this bug was to make future Engine-Porters aware that:

  • Do not assume that the original engine does not contain any bugs.
  • Small irrelevant changes can have a larger cascade effect and make engines crash at seemingly unintuitive places.

and probably the most important:

  • Always test your changes with different compilers.

That is all for this blog. Thanks for reading!

 

Categories
Uncategorized

Optimizing performance

Hi all! As promised, in this week’s blog post we’ll look more into the performance issues I faced while porting.

The progress so far

For those who did not read my previous blog post, I’ll quickly go through what the performance looked like in my previous blog post.

We had the game running at 10 fps when in reality it should run at 60 fps. I tried optimizing some minor things but did not gain any massive advantage in fps. This prompted a deeper look into my blitting code and how the game blits the level itself.

A deeper look into the problem.

Before we dive into the solutions, we must first ensure that we understand the root cause of the problem and create a mental idea of what we reallyΒ can optimize and what we can’t.

Let’s look at the video again.

Notice, how the camera moves around with the player? This means that everything on the screen(every tile, every object, etc.) needs to be redrawn every frame. Now as you can imagine, this is really costly.

By looking at the video we can think of following optimizations:

  • This game did work at 60 fps in the stock build, which means ScummVM’s blitting code is slow.Β If we could somehow optimize the blitting code, we could gain a lot of performance.
  • Most of the objects on the screen appear to be static, i.e., props and tiles do not seem to change a lot. We could pre-render the stage/level to a texture and move that texture around as per our needs.

Let’s talk about these ideas in a slightly bit more detail.

The important thing to ask in the first idea is why.Β Why are ScummVM’s blitting calls slower when compared to SDL? The answer is vectorization. SDL has vectorized blitting routines which are orders of magnitude faster. I suggest you to read Eklipsed’s excellent blog on the same subject where he talks about this in a more technical fashion. “Overall the worst culprit was converting the pixel formats, and then blending.” If we could somehow optimize this, we would definitely get better performance.

Coming to the next idea, the instant question that pops into mind is that how is it any better if one still has to redraw the whole screen every time. The thing is that every level is divided into several layersΒ and each of these layers is drawn over other layers to render the full level. The bottom layers could have pixels that are then overdrawn by pixels of the upper layer which wastes a lot of CPU cycles. We could simply pre-render this to a texture and move that texture around. Since we won’t have to redraw every layer again and again, we can expect to gain some performance.

Now, we’ll see how I actually implemented these ideas.

Solutions:

Idea 1(Optimize pixel conversion and alpha blending/testing)

Both our screen and our textures are RGBA8888, so we actually don’t need to perform this step atΒ all. πŸ˜‰

However, we still need to access every individual pixel because of alpha blending/testing. I implemented alpha testing for a quick test as per a suggestion by sev since all that needs to be done in alpha testing is to check whether the specified pixel has some alpha value.

Sadly, this did not give us the massive improvement which I was hoping for.

15 fps only :/ and with artefacts

Idea 2(Optimize drawing code)

At the cost of increasing the load times, we could pre-render a level with its different layers to a texture. We can actually combine both idea 1 and idea 2 to create a more-optimized solution. Theoretically, once you have the texture of the level, you just need to copy it to the screen. You do not need to perform any sort of blending or pixel conversion! This sounds like a perfect use-case for memcpy and guess what? Yup, you guessed it right, memcpy is vectorized.

Implementing this was a bit harder, but at last, I was able to get it to work.

We finally get the glorious 60 fps, we had been waiting for. But as some of you might have already noticed, layering is broken. πŸ™‚

Uh-oh!

Since we’re pre-rendering the objects and prop layer, the character is simply drawn over the texture totally breaking layering. There is one possible solution to this situation: Calculate what object is colliding with the player and redraw that.

Fixed layering – the character is drawn behind the pillar as intended!

However, while this works for objects, this does not work with the normal layer.

What I ended up doing was, re-rendering only the parts of the normal layer and the objects which were colliding with the character. This performs even better since we redraw only parts and not the whole texture!

I would like to note that the solution is not perfect but it does work quite nicely.

TL;DR: I was able to optimize the code and get it to render at 60 fps albeit with some small issues.

Thanks for reading and please look forward to future blog posts!