





🌟 Special thanks to our amazing supporters:
✨ $10 Tier: [Geeks Love Detail]
🌈 $5 Tier: [Arch Toasty][Benedikt][David Martínez Martí]
Hey!
It's me, Robert, again. Last week I talked about porting The Hand of Merlin to Linux. Today, let's continue the story with macOS.
Mac certainly turned out to be a tougher challenge than Linux, but hey, I love roguelikes and challenges, so that's not a complaint!
Remember how I said SeriousEngine 4.0 dropped support for OpenGL entirely? You know, the nice, cozy old graphics API that worked on pretty much any platform? Well, that leaves us with Metal, the new and modern API developed by Apple specifically for iOS and macOS. Now, SeriousEngine did have native support for Metal, back in version 3.5, when we made The Talos Principle run on iOS. But that was 3-5 years ago, and our Metal code wasn't maintained since then.
You can imagine my dismay! Half the graphics functions had changed signatures. A quarter were never even implemented. It took me pretty much a whole week just to refactor the old code in order to make it even compile. That was the easy part.
The tougher part was writing out missing code. Luckily, we do have a working Vulkan renderer, so I was able to examine how Vulkan code works, and then scan through Apple's documentation to figure out how to remap things. Since both APIs are pretty low level, they do have fairly similar concepts. Oh, and by the way, Apple uses Objective-C instead of standard C++... so that's an extra hard mode there. :)
Finally, a week-and-a-half later, I had a Metal renderer with all the holes plugged. It compiled, I ran it, and... Black screen. Sigh.
Just like with Linux, the problem now was missing shaders. Metal can only work with shaders written in MSL, the Metal Shading Language. Rewriting the entire shader code base from HLSL to MSL was not an option I wanted to think about. I knew I could get DirectX Shader Compiler to work on macOS, since that library is cross-platform. That can get me SPIR-V (Vulkan bytecode) on macOS, but then what? After some research, I found an answer: SPIRV-Cross, a cross-platform library that can decompile SPIR-V into a number of other human-readable languages, including - yes! - MSL.
So let me paint you the full picture. We start with shaders written by a programmer in HLSL. Then we compile those into SPIR-V bytecode, decompile them into MSL, then re-compile MSL into Metal's bytecode. Phew! Talk about making sausages, right? But hey, at least none of this happens on players' machines. That distributed build system I briefly mentioned in the last post, that handles the entire shader creation process. So, when you download the game on Steam, the game data package already contains all of the shaders precreated into the final bytecode form that's directly loadable by your local operating system.
So after all that, I ran the game again, and... Black screen. Again. Sigh.
Back to the drawing board! I stripped down the first scene in the game to a bare minimum, a colored 3D cube. The simplest shader, the simplest possible rendering state. Once I could get that running, I'd add more complexity.
-- Xcode --
At this point, let me introduce you to my new friend - Xcode. That's Apple's main dev tool suite, and the features I was most interested in are: Metal API call validation, and GPU tracing.
The API validation thing is super useful since it tells you as soon as you call a function if you messed something up with the parameters. I caught a solid number of bugs just by examining the validation errors.
The second feature is the real winner, though. By recording a single frame of rendering, Xcode can trace all of the GPU calls you did, along with the entire pipeline state, allowing you to inspect everything. A pixel is black when it should be colored? Just click it, and hit the Debug button. You get a listing of all the textures you're using, the exact shader code that's running, heck you can even step through the shader code.
With this power tool, I was able to really get to the bottom of all the issues I saw, and finally, I could see my beautiful cube. :)
From that point on, it was like climbing a staircase. Add a bit of complexity to the scene, run a GPU trace, figure out what's wrong, fix it. Rinse and repeat. Slowly but surely I got all the way to dynamic geometry (fonts, particles), post-processing (tone mapping, bloom, ambient occlusion), shadow mapping, antialiasing (MSAA), all the jazz.
To round it all off, I also made sure we have proper detection of high-DPI / 4K displays on Mac, too.
-- Performance --
Oh - did I mention the game not only runs on Mac, but also runs pretty darn well? This is where the native Metal implementation really pays off! On my M1 Mac mini, which has an integrated GPU, I'm able to get a solid 60 FPS at 1080p resolution. And that's using Balanced quality presets - so no sacrificing visual quality.
So, there you have it. All in all, several weeks of work, and a satisfying feeling of a port done right.
If you have any questions, stop by the forums or our Discord server. We're pretty much always there.
Robert
https://store.steampowered.com/app/600610/The_Hand_of_Merlin/
[ 6084 ]
[ 1345 ]
[ 4067 ]