Thursday, June 29, 2023

Month 4-Week 1 of Gateware: The Beginning of the End

    We are in the final stretch now, and it's all coming to an end. The showcase project for the presentation was completed in time for the presentation on Monday, though there still is a bit of a problem with it, and it definitely leaves more to be desired. We also began looking into setting up the automatic build process so the Windows runners and build and run the UWP implementation. At first, this really seemed to be a huge issue, and we were kind of at a standstill for a bit, but I believe we've made a bit of a breakthrough, and there should really only be one more hurdle to get over.

    For the showcase project, I ended up taking a DX11 texturing example that just had a spinning 2D plane, and I was able to turn it into a cube that you could spin around with either the keyboard or a controller. I was then able to add in some file I/O so that we could save the position of the cube when the application closes and then read it back when the application is opened. The last thing I added was that music now plays when the application opens. It may not be much, but the application does show off DX11 rendering, both controller and keyboard input, file I/O, audio, and it runs on Xbox.


    One of the lab instructors here also creating something to load in GLTF files which would be able to get us much more advanced 3D models and basic materials for the time being. We're hoping that it will also be able to give us more data in the future, like textures and normals. I want to try and implement this in the showcase project, but before I'm able to start working on that, I need to get this build process down. 

    Speaking of which, the build process in presenting a very interesting issue because, of course, UWP has to be that way. For all of the other platforms, we can simply just have the yml run the CMake the create the project, have CMake then build the project to create .exe files, and then we can just run the .exe files. But UWP can't be that simple because that would just be too easy. While this same process does make a .exe file for UWP, we can't actually use it since UWP just doesn't work that way. Instead, we have to do a couple extra steps to get it working. 

    It starts off the same by running the CMake file to create the project in the build folder and then having CMake build the project. But then, from here, we have to find a way to actually deploy the application so that it will create the AppX install folder. Once we have that install folder, we can then use it to create a .appx file, which can then be used to fully side-load the project so it can run separately from Visual Studio. I believe we currently have all of these steps figured out individually, except for the deployment step to actually get the AppX folder created. But once we have that figured out, we can begin to start fitting all the pieces together and get the Windows runner building and running UWP.

    As long as progress is able to keep moving like it has today and yesterday, I believe we can get this more or less wrapped up by the end of next week, and then I'll be able to go in-depth on how we're tackling every step of this build, install, and run process. But until then, we'll just have to keep our fingers crossed and hope that no more major roadblocks appear.

Thursday, June 22, 2023

Month 3-Week 4 of Gateware: Creating a Showcase Project for Future Students.

     This week's blog post is probably not going to be quite as long as the past couple of weeks, as not quite as much has happened in the past week. This upcoming Monday, the new capstone tracks are going to begin rolling out, and with them, we will be trying to entice more students to make their own game engine using Gateware. And a big selling point we want to provide for using Gateware is that it is compatible with UWP, meaning it can run on various Microsoft devices, including newer generations of Xbox. So Lari And Carlos are wanting me to have an example project made and running on Xbox by the big presentation on Monday.

    I began working on a showcase project last Friday a bit, but so far, I have had to completely restart two or three times so far for various. One of the first restarts happened because I was using an assignment from a previous class and building off of that and adding extra functionality to show off more of the capabilities of Gateware, but I later learned that they don't want me to use previous assignments in case future students somehow find the template and use it complete their projects. After that, I started using examples already provided to students and building off of them. The last restart, however, happened because I learned a little too late that the example uses external libraries that aren't compatible with UWP, so we had to scrap that one.

    But now we're on to the most recent showcase project, and so far, it is looking far more promising than the previous attempts. I was able to create a CMake to build the project as a UWP project, and just today, I was finally able to get it running on an Xbox, with some slight problems, though. Currently, the project is just a cube floating in space that you are able to spin around with some controls, but I'm hoping to at least add some file I/O and some music playing to show off those capabilities. The problem I mentioned earlier is that when the application runs on Xbox, the image gets extremely blown up, so it's like the camera is super zoomed in. I haven't had much time to look into it yet, so maybe it's not really that big of an issue.

    In more exciting news, we were able to manually compile the UWP version of Gateware into its single header form! It took some time to get working and some finagling with GCompiler to get it working, but now it is. Also, because of some of the said finagling, GCompiler will hopefully now be more accepting of the UWP version of Gateware when the time for automatic compiling comes along, but we're still a week or two away from that. 

    Well's pretty much it for this week. Not quite as exciting as the past couple of weeks, but we've still got some progress being made, and we're still moving forward. Hopefully, next week's post will have a bit more excitement to it as we enter into the final month of my capstone with Gateware. Hopefully, I'll be able to get this UWP thing fully integrated into the main branch before I check out. Fingers crossed. 

Friday, June 16, 2023

Month 3-Week 3 of Gateware: Getting Close to Gateware UWP's Launch!

     Well, the storm I talked about last week has been averted; for the moment, at least. Since UWP applications can run on a wide array of devices, it has many limitations on what it can and cannot do for security reasons. One of these limitations is that it cannot access any files outside its own install folder. This is quite a big contrast to traditional desktop applications that can access files just about anywhere on the device it is on. This creates a huge discrepancy between the capabilities of GFile on Linux, MacOS, and Win32 and of GFile on UWP. We could heavily limit the capabilities of GFile so that it's consistent across all platforms; we don't want to limit users that are developing games and applications for desktop use only. So we've just decided to leave it as is for now, but we have plans to revisit this in the future to allow the user to put GFile in a sort of mode that will make it more consistent across all platforms.

    But the original problem that brought this to our attention still remains. A few of our unit tests, such as GFile, GAudio, and GBlitter, use external resource files to conduct their unit tests, which of course, exist outside of the install folder since the install folder isn't created until the application is built and runs the first time. So we have to find some way to get the files from the external resource folder to our local resource folder inside of the install folder. Luckily, Chase Richards has already done this for the files in GFile and GAudio, and I just needed to copy what he did for those and then do it for the GBlitter files. 

First, we grab all the files we want to copy and store them into a Global Resource. For GBlitter, we are copying both the png and tga files.


We then want to copy those Global Resources into stand CMake variables.


Next, we want to group these files altogether into one variable (we'll come back to this later).

Next, we want to have the application copy these files into the deployment location (install folder).


Finally, we'll take the grouped variable from earlier and add those to the executable and source group.


    While this does get the job done, it can be very annoying to do this every time you want to add something new to the resource folder. So I began looking into a way to copy over the entire resource folder in one go, and I got about halfway there. I managed to find a way to copy an entire directory with everything inside of it and paste it wherever I wanted, except for the local install folder. Since the install folder is created when the application runs, not when it is built, CMake doesn't have direct access to the install folder. That's why we had to use the "set_property" function above. But I couldn't find a way to use that function along with the "add_custom_target" function that I'm using to copy the whole directory. So, for now, I will just be leaving it in the CMake file and let someone who knows more about CMake than me come along and finish what I started.


    After this all got sorted out, the UWP implementation was finally passing all of the unit tests. Well, except for GWindow. So we'll go and tackle that real quick. While it took a bit to figure out exactly what the problem with the GWindow unit tests was, we did eventually find it. The problem is that since the functionality of GWindow is heavily limited to mobile applications (I know, big shocker), it isn't actually able to pass the vast majority of the unit tests made for GWindow. So someone in the past had created a separate group of GWindow unit tests that only get ran Gateware is being run in an application environment. While these tests only got compiled and ran on UWP, the standard unit tests were still getting compiled. So I was just able to separate the desktop unit test and the application unit tests so that they're only ran on the associated environment. Now, the UWP libraries are completed, and it's passing all of the Unit Tests! Now we need to go back and fix what is wrong with the other platforms. 

    Since this week's post is already so long and these last problems aren't really that major, I'm going to try and run through these fairly quickly. GAudio unit tests were failing on Linux because it is, for some reason, considered part of the APP Windows API Family and was running some code that is meant only for UWP, so I was just able to specify that the platform also needed to be a Windows platform and that fixed that problem. The MacOS CMake had some code that was specific to iOS, but since the iOS implementation is incomplete, it was causing some errors, so I commented it out (that way, when someone revisits iOS in the future, that code will still be there for them) and now MacOS was getting to the end of the build process. The final problem was the .yml file that tells the runners how to build and run the unit tests. On the UWP branch, a certain folder inside of all the platforms' build folders had its name changed, and we just needed to update the .yml file to look for the new file name. 

    And now, probably for the first time in about three years since its development began, the UWP branch finally has green check marks all across the board! 

    But the work is not over yet. I now need to begin working on a demo application that uses Gateware so that we can get it running on an Xbox console as a proof of concept for future capstone students that want to create a game for consoles. And then, after that, it will be working on the UWP build process so that it can finally be merged into the main branch after being on its own for about three years.














Friday, June 9, 2023

Month 3-Week 2 of Gateware: The End of GRasterSurface (Out of the Frying Pan and into the Fire)

    After nearly three weeks of working on it, GRasterSurface is finally complete! It's been a bumpy rider full of head scratches, reworks, and "hmmm"s. But it's finally over and ready to go. After finishing that, I could check to see if the universal implementation of GBlitter still works, and... of course, it doesn't. In fact, it has opened up a massive rabbit of possibly major flaws in multiple UWP libraries. But before we talk about the raging storm that lies ahead of us, let's take a moment to reflect on the success of GRasterSurface.

    After last week's blog post, I realized that there was a major problem with UpdateSurfaceSubset(). To fully understand this problem, I first need to explain what exactly we were doing before. Whenever the Resize event for the window gets called, we have to create a new texture to match the new screen width and height, and since creating a new texture can be very expensive, this is the only time we ever create a new texture. 


    So now we have our texture, but how do we change the texture? That's where Map() and Unmap() come in. The map function allows you to use a mapped subresource to pull information from the GPU, manipulate it, and then send it back to the GPU. A limitation of mobile platforms like UWP is that when you pull the information from the GPU, you have to immediately discard it. This makes it so that you can't keep any previous information that the texture did have.

    So anytime we want to update the texture (Clear, UpdateSurface, UpdateSurfaceSubset, and SmartUpdateSurface), we must use Map/Unmap with the discard. The problem that this created became apparent when we got to UpdateSurfaceSubset. How this function works is the user is able to specify a rectangle in the texture and then gives the function to update only that rectangle. However, because of the discard that happens with the Map, changes to the rest of the texture aren't kept. So when the UpdateSurfaceSubset is drawn, the changes rectangle is simply overlayed on top of the previously drawn frame, which kinda sucks. 

    So to counteract this, we need to have a local buffer that we update in all of the functions that write to the texture and then only update the texture on the GPU once we are ready to draw to the screen. On the one hand, this has made it so that we can't really do any hardware acceleration when it comes to the SmartUpdateSurface (though it shouldn't out weight all the time we save with the hardware-accelerated drawing), but on the other hand, we're able to largely just copy code from the Win32 functions that modify the texture and just make a few modifications to fit our needs since we don't need both a front and back buffer. 

    So that's pretty much it for GRasterSurface. So glad to finally have that behind me. But now it's time to face the storm that lies ahead. As much as I'm sure you're just dying to hear what the problem is, you'll have to wait a bit longer. The extents of this problem have yet to be fully explored, so I can't say for certain what is going on and how we plan to fix it cause we don't know. But I promise you'll hear all about it next week cause it is likely going to be all that I do next week. Until then, wish me luck!

Friday, June 2, 2023

Month 3-Week 1 of Gateware: GRasterSurface Cont.

     Another week of GRasterSurface has come and gone, but much more progress has been made toward finishing it. We've finally got it passing all of the tests* and drawing images to the screen. The only thing that should be left to do is modify SmartUpdateSurface() so that it can utilize hardware acceleration, and then we don't have to worry about unnecessary thread locking and unlocking.

    As I mentioned last week, I decided to just implement GRasterSurface using DirectX11 to avoid using weird external libraries, and it'll be a much more efficient implementation. I started by doing some research into how to set up the DX11 surface along with everything that goes along with it, like the shaders, buffers, descriptions, and the texture itself. The two main resources that proved to be helpful in getting all of this set up were the API examples from Full Sail's 3D Content Creation class and a Stack Overflow post. 

    The class examples have an example of a renderer that utilizes texturing, and this provided the main foundation for setting up everything. I had to modify the vertex shader a bit since we don't have to worry about matrix math and putting things in world space, but the pixel shader was able to work right out of the box. 

    Along with the standard setup process, it also showed the process for sending a Texture2D to the GPU using a ShaderResourceView and SamplerState. This part was definitely much more of a mystery to me beforehand since I haven't done any sort of texturing in graphics before, let alone in DirectX11, which I have virtually no experience with. But the main problem with this example is how it is actually making its Texture2D. The example is creating textures from DDS files which requires a completely different method of creating the texture from what you would do to create a texture from an array of pixels.
    
    So this is where the second main resource comes into play. Since I couldn't use the DDS method that is used in the example, I did some research into how to create a Texture2D from an array of pixels, and I stumbled across the Stack Overflow post linked below. This showed how to set up the texture description, create a Texture2D using that, and create a ShaderResourceView using the texture. However, creating a Texture2D every time we have a new image to display can be very expensive, so we're only re-creating it when the window is resized. So how do we get new images to the Texture2D?

https://stackoverflow.com/questions/41627317/directx-11-how-to-create-a-very-simple-2d-texture

    This is where my third, secret resource comes from; people! Lari Norri was able to help me out with being able to Map and Unmap the texture's data so that we can update the texture in a very low-cost way. This was able to get a lot of the universal functions finished, like the lock/unlock functions and UpdateSurface and UpdateSurfaceSubset. I was also able to get some help from some other people at Full Sail with the occasional DirectX11 thing.

    So now GRasterSurface is just about finished, but as you may remember from the beginning of this post, SmartUpdateSurface() still needs to be done and that's also kind of the asterisk to all the unit tests passed. Since SmartUpdateSurface() isn't doing anything at the moment, GRasterSurface can't really pass the tests that require that function, so they're commented out at the moment. But I'm hoping to be able to get SmartUpdateSurface done by early next week so that I can stay on schedule with what I've got planned for this month. But only time will tell.