Saturday, January 24, 2026

Month 1 - Part 1 - Implementing IntersectOBBToOBB

 Over the past few weeks I've gotten introduced to the Gateware project and started contributing. I've discovered a new Vulkan bug (by accident), implemented a function that outputs an OBB given a transform, and began implementation on another function that finds the minimum penetration distance between two intersecting oriented bounding boxes and returns it to the user. I've learned a lot during this time which I will share in the following post. 

During my first week I focused on getting my environment set up, reading through the code base and trying to compile. Given that this was my first time compiling and running the project I expected all tests to pass. Much to my surprise I was greeted with my first test failure. The issue was related to Vulkan and wasn't reproducible on others machines. After some debugging with Lari we found that it was related to the newer versions of Vulkan. We decided to table this for now as I was just getting started with the project and it worked fine on recent versions of the Vulkan SDK. 

The next week consisted of implementing a function that would output to the user an oriented bounding box given a transform. An oriented bounding box consists of a center, extents (width, height and depth), and rotation. I was familiar with OBBs on a high level, but never worked with them directly before thus began my research. I learned about the difference between Axis-Aligned Bounding Boxes and Oriented Bounding Boxes and their different use cases. I learned what defined an Oriented Bounding Box and how, given a transform, one can generate the center, extent and rotation of a bounding box. This turned out to be pretty simple and my solution is as follows:

Luckily, Gateware's math library contains functions that already compute the data we need given a transform. This ended up being as easy as plugging the transform into these functions to get the center and rotation of the desired bounding box. The one extra step I needed to do was take the limits vector given by the user and scale it by half since the extents of the bounding box are half width, half height and half depth. After calculating these, I set the appropriate properties of the output bounding box and return SUCCESS to let the user know it was created. 

The great thing about Gateware is that we are required to practice test driven development which means my work was not finished there. I then proceeded to create test cases for this function which consisted of:

  1. Given an identity transform, we output a centered OBB
  2. Translating an identity transform and then creating the OBB correctly sets the OBB center
  3. Given a transform with rotation correctly sets the OBBs rotation
  4. Given non-uniform extents still produces the correct extents for the OBB
  5. Given zero limits will produce an OBB with zero extents

I then did the same for a double variant, added to the interface and added a fallback function. After this my work was complete! I submitted a merge request, received some feedback and finally merged into the main branch. It was an awesome feeling knowing my code, however small, was live and possibly being used by people building games.

After this I decided to move onto another OBB function. This time the function was supposed to return the minimum penetration distance between two intersecting OBBs. Again, I started with research. I found that in order to detect two intersecting OBBs I would need to implement something called Separate Axis Theorem which luckily is well documented but also much more complex than the method you would use to detect collision between two AABBs. I read Real-Time Collision Detection by Christer Ericson and started doing other research on topics that were fuzzy to me such as vector projection, cross products and how to use these to derive the information needed to complete this function. 

After a talk with Lari I decided to see if there was anything in the codebase that could help me finish this function. Low and behold, I found a little (not so little) piece of code that was commented out with the function name IntersectOBBToOBB. Well that's awesome, someone had already started this and not only that but the function was practically finished! This was a relief but at the same time I felt like I had wasted so much time looking elsewhere and holding up development which would turn into a learning for me. I decided to start dissecting this function and figure out why it wasn't working the way the author intended. 

Through debugging I found that we were initializing our out distance to 0.0f and then using this out distance in future comparisons when finding a new penetration depth in the form of (x < out distance) which meant that we were simply never updating out distance because the new penetration depth would never be less than the initialized zero! To solve this issue I created a variable local to the function that initialized to FLT_MAX. I then used this in our comparison so that the first time we found a new penetration distance it would be guaranteed to be less and on subsequent comparisons we would then be comparing against the current minimum penetration distance. There were a couple other issues that popped up after this but this was the root issue. We finally were returning the true minimum distance between these two objects.

I added some tests, clapped my hands together, but wait.. there was a failure. In one of my tests I simulate a separation of the two objects by moving the object by the minimum penetration distance found and then recheck for a collision, expecting no collision. The test was returning collision? After further inspection I realized it's because we add on an epsilon value to each axis. This epsilon value exists to prevent numerical instability when the boxes are near parallel. The problem with this is that we get an inflated projected radii so on the next call to the function (after we've separated the objects) our function will detect another collision even though the objects are geometrically separate. This is the issue I've been stuck on for the past week and I'm not sure what the best way to solve it is just yet. I don't want to just return an estimate as this could cause jitter for the user. I am currently researching open source physics libraries like box2d and qu3e to see how they handle similar situations as well as reading and watching material from other collision experts such as Dirk Gregorius, Erin Catto, and Randy Gaul in hopes to find an elegant solution to this problem. 

If I can't resolve this soon, I plan on tabling it while I tackle the original Vulkan issue I encountered during my first week of set up as this is a more pressing issue that is currently affecting users on newer Vulkan SDKs. On to the next week of development!

Wednesday, May 28, 2025

Gateware : The End of the Second Month

Another two weeks have passed since my last post, so it has now been two months since I started working. In the past two weeks, I have fully implemented a raytracing flag to the graphics options. Also in this blog, I will be reflecting on my overall time working on Gateware.

The main focus, as I previously mentioned, was implementing the raytracing flag as an option to make it easier for users to use raytracing, as it will set up all the extensions for them. This meant that the first thing I did was simply add the raytracing option to the graphics options enum in graphics define.

After that, I went into the GVulkanSurface_core.hpp and started by adding the if check for the flag and added the extensions if the if check was successful.

The next part I implemented was in the device creation. Again, we checked for the flag, and if it was true, then the needed extensions were implemented. These included:  

  • VkPhysicalDeviceRayTracingPipelineFeaturesKHR
  • VkPhysicalDeviceAccelerationStructureFeaturesKHR
  • VkPhysicalDeviceVulkan12Features
  • VkPhysicalDeviceFeatures2
After they were implemented, I also modified the get best gpu function to make it so that a gpu with raytracing support would be higher scored.
The last part was adding the unit test, which was a bit tricky as I thought it would be pretty much the same as the bindless one, but it actually required an extra check for extensions, otherwise it would fail on the CI/CD pipeline on all the runners except for one.

That leads us to the code review, which left me with a bit more to do before the feature was implemented. The problem now was that the additional extensions added to the device extension count were hardcoded, so I changed it to be based on the flags instead of hardcoding it to the max amount of possible extensions.
That finished off the raytracing flag addition, and it was finally ready to be implemented!

That now brings me to the end of my open portfolio classes and the end of my time here, so reflecting on the time I worked on Gateware, I can say that I learned a lot. It was my first time really working on such a large codebase. So, there were lots of things that I encountered for the first time and that were challenging to me, so it was a great learning experience, and it feels like I learned a lot that will be helpful moving forward, and when working in the industry. Overall, I am really grateful for this opportunity and am excited to see where these skills will take me next.

Friday, May 16, 2025

Gateware : The Start of the Second Month

Another two weeks have passed since my last post, so it has now been a month and a half since I started working. In the past two weeks, I have completed adding compute queue support to GVulkanSurface so you can now use compute shaders and also started to work on adding a raytracing flag to the graphics options.

The main focus of the last two weeks has been on fully implementing the compute queue support and the matching unit test, so let's start by going over that. The first thing I did was create a new function for getting the compute queue. This was quite simple as it followed the same pattern as getting the graphics or present queue. 

Then I also updated the function that got all of the queue family indexes (more on that later). The last thing to update before working on the unit testing side was to update the get gpu function to add more weight to a gpu that had compute queue support.

To test the compute queue support I had to write a glsl shader, for the compute queue test I was told to test it away from graphics, so I focused on testing with actual computing/math by writing my shader to take an input buffer square the values and return them in an output buffer. I chose to use two seperate buffers instead of just one for better readability.

The test was a success! Which meant I had successfully written compute queue support for Vulkan, and a successful unit test as well. After a code review, I just needed to add back a deprecated version of a function using Doxygen's deprecated feature to prevent any problems on the back end. And then the merge was successful! Meaning that Gateware now supports compute queues and shaders in Vulkan.

Now That leaves us with what I am currently working on which is adding a raytracing flag to the graphics options so that it will be easier to work with raytracing using Gateware. I just started this so I haven't got much to say yet, but will be posting about that in the next post!

Thursday, May 1, 2025

Gateware: the first month

Two weeks have passed since my last post, making the total time working in Gateware one month!! In the past two weeks I have worked on various things, including finishing up the derive a plane from a given point + normal, and also fixed an issue where the GVulkanSurface test for checking for compute queue support was failing.

Starting with the math function implementation, I was able to finish of the function and unit testing in the first few days following my last post. The main thing I had left to write and clean up was the unit testing itself. Following that the merge request went into code review and after getting some feedback I cleaned up the function itself by removing a redundant magnitude check and changing the formatting a bit for clarity. I also added more fail tests to cover more cases to ensure a full test suite for the function.
After that, another code review left me with some changes I forgot to make to the Doxygen documentation, which was luckily really quick, and then the merge request was approved!

After that was successfully merged, I started work on a new issue, which was GVulkanSurface not allowing compute queue support. The test was already written to check, but it was crashing. There were two main problems in the code, firstly there was a cleanup call at the end of the create function that was deallocating everything, which was causing GetDevice to fail. After commenting that out, the GetDevice succeeded, I was able to identify the next problem. The queueFamilyIndex was being used without being initialized. This was causing problems because it was being used to get the index of an array. Luckily, the fix for this was simple! I just needed to add a for loop to loop through the queueFamilyCount and then set the queueFamilyIndex equal to the current iteration and then it was able to be successfully used. The test was now running with no crashes and succeeding.
And that has been the past two weeks! I am now going to be working on actually adding the compute queues and so look out for that in another two weeks!


Friday, April 18, 2025

Gateware: The first two weeks

 It has been two weeks since I started working with Gatware, and time definitely moves fast it feels like it has been only a few days. But I have been very productive over these two weeks, with my introduction being just a simple change by adding the OUT_OF_BOUNDS failure code. My first real task was reviewing the bmp addition to the GBlitter. The issue said there was a memory leak, however there were none when I looked through the code. Although this did lead to the discovery of a problem with the UWP runner, as the pipeline kept failing. So I helped try and look into that issue with the other members of the team. It was successfully resolved towards the end of the first week. I then looked through the remaining comments/threads left on the merge request and fixed up some messy commits and then added macros to replace the magic number if checks for the decompression in GBlitter to finish off my work with the bmp addition to the GBlitter.



Then for the remaining time in the second week I started working on an issue from four years ago to derive a plane from given point and a normal, I have gotten it pretty much finished and will be going through and cleaning it up a bit before sending it to be reviewed. The logic was relatively simple, though I almost forgot the magnitude 0 check, lol! But besides that it went pretty smoothly. I have written the unit tests out and tested them a couple times and they are all passing, yay!



Friday, May 3, 2024

Month 5 - Week 4 - Fixing Arch Linux X11 Bug

 Last week we touched upon a bug that was occurring with X11 for Arch Linux. The bug was specifically when creating a window for a test. The window would fail to create and now show up at all, failing the test. I was only modifying the unit tests at the time but took time this week to look more deeply into the actual code to see what could be going wrong.

One of the first things I decided to do was enable the macros inside of GWindow to see the X11 debug output. This would allow me to watch in real time what was failing, and better help towards finding the problem child in the code.


As you can see, the errors being reported are talking about a bad window. This normally means that the pixel map for the internal window is bad, or the window itself failed to create and can’t have anything done to it. I knew immediately the first place to check was the create function for GWindow.


Here we see two functions are called. The first function is just initializing some basic fields, the second function, however, is the one we’re interested in. `OpenWindow()` is the function responsible for creating the window and initializing all the information that GWindow needs to properly manage it.

Remember, we can’t debug, or the error won’t occur. So, instead, I decided to inspect some documentation and play around with the code, probing it to see which function was giving problems. Upon inspection, I noticed that anytime I messed with the `XInitThreads()` function, it had a change in the functionality itself. It seems this might be the problem child I mentioned earlier. I decided to put a `std::this_thread::sleep_for()` call right after calling for the initialization of threads. The sleep time was small, only 10ms, however, doing just this resulted in all of my tests passing consistently.


I found the bug that was occurring. So, how do I fix this then? Well, the moment I discovered this, I made a commit with the sleep call change as the fix. However, using this kind of call to fix the bug isn’t exactly good. Some issues can occur. For example:

  • How would this code react on faster or slower hardware? We can’t be certain that this sleep fix will work for all systems, it might even only work on mine.
  • Is this truly a threading issue, or a mutex issue? The display for X11 is multithreading capable, so if I don’t lock it, it could be other X11 functions are modifying or trying to modify the variable I’m reading immediately.
  • Lastly, what if this fix is temporary like the event system bug months prior? This is a real concern; it would not be the first time I “fixed” a bug only to have it come back spontaneously because I wasn’t doing the idiomatic approach expected.

So, what I have to do is test for different solutions and see what I can find. This was, unfortunately, all I could do for now. I wanted to do more but was constrained on time with due dates and graduation fast approaching. While this is going to be my last blog post, it will not be the last of the changes I make to Gateware. I have full intention of continuing work on Gateware to not only fix this bug, but also to see what I can do to fix, implement, and update features for the project. This is in the hope that I learn more from it over time, that and I simply enjoy working at a low level. So, with that, this is my last blog post.

Sunday, April 28, 2024

Month 5 - Week 3 - X11 Fullscreen Bug

 After the final touches to audio, my focus has now shifted to a new and rather interesting bug involving X11 window events and, arguably more importantly, Arch Linux. This bug has shown itself to me for a little while but was largely inconsistent in how it appeared. It would show in the unit tests ~80% of the time, and the source of it seems to be unknown currently. This bug will be my last bug to fix for the project as part of FSU, however, if I can't fix this bug by the deadline, I will be continuing work to fix it after. This is largely because this is the only bug left to fix to finally add Arch Linux as a fully supported platform. This will be brief as I didn't have a lot of time to fully inspect the bug like I wanted to, but there will be enough here to mark my current progress. So, with that, let's get started.

The first thing to note is the description of the bug.


Here are the things to note:

  • The bug has a visual aspect (the window will sometimes show, sometimes stay hidden, or show completely but be transparent).
  • It fails specifically when the check is run to see if the window is fullscreen.
  • Previous calls in the said test don't do anything.

Here is the test.


We can see on the overview that the window is initially created with a basic position (0, 0) and dimension (800, 500). It then reconfigures itself to a different position with different dimensions. Something else you might have taken notice of is the usage of `GiveWindowTimeBeforeRequiringPass`. From what I've gathered, this is here because sometimes X11 requires a bit of time to process events before it can check for what we want. So this is here to effectively wait out the processing of events to check if a function has passed properly. From here, this is where I started testing.

First off, breakpoint debugging is useless for this bug. Because if I placed a breakpoint and walked the function line by line, nothing would go wrong. Debugging would mainly be useful here if I could catch the bug failing in action, however, I'm not able to do that here. So, this leads me to test a little more conventionally. The thing I checked for immediately was timing. It could be that the code is going too quick and preventing X11 from processing everything in time. So I added small delays to check for this, however, it did not work. I also tried using the exact delays used in other tests, but the results were the same.

Interestingly enough, if I duplicated the exact same test and ran the full unit test suite, only the first test would fail, with the duplicate passing. Odd behavior.


From the discord messages above (which are posted in the Gateware Development channel), I described my difficulties with this bug as well as what I feel the bug is.

I feel this bug is a timing bug. Basically, one thread is doing things so quickly that the other thread can't keep up in time. This would explain the odd behavior, and why the test doesn't always fail. Of course, there is more testing and inspecting of the code that will be needed, but in total, this seems to be the main issue. So now, what has to happen, is we have to specialize the probes for this bug to target the timing over the functionality. If I can pinpoint that it is timing-related, then I focus my efforts on fixing that. However, if this isn't the case, I will have to inspect further to see what is exactly happening.

Currently, this is all that I have done for this bug. Next week should hopefully go into a deeper discussion on how I found the fix for it. But for now, this is all.