With the advent of Apple’s new line of iPads, the time felt right to bring Divinity: Original Sin 2: Definitive Edition to iOS. For those not familiar with the game DOS2 is a huge and complex 100 hour+ CRPG, so bringing it to mobile devices without compromising on anything was never going to be a straightforward task.
Partnering with the team at Elverils, we sought to bring the game to iPad in its best-ever form. With a lot of technical and design challenges ahead of us, it was a daunting brief. Here’s how we did it in 7 key steps.
The challenge of translating the full experience into mobile starts way before the implementation phase. It was never going to be a simple case of porting the code across to run on Metal and hoping for the best. Mobile devices, by their very nature, are handled and controlled very differently from PCs or consoles. Thus, the game had to adapt at a quite fundamental level, even down to how the player interacts with the device itself.
Although a key part of the brief for the iPad version was to support controller functionality, we had to start with the standard UI for the device, the touchscreen. There is no cursor on the iPad, which means that there is no hover state - you either tap or you don’t. When your RPG is designed first and foremost for a keyboard and mouse, the hover functionality is key.
The iPad boasts a full suite of gestures that bring an edge of physical dynamism to the game. Rotating, pinching, swiping, and even the humble tap and drag all had to be adopted to meet the design demands of DOS2. The long press is a good example: holding down on an in-game button would now bring up some information on that button. Adopting the long press was something we had to do specifically for this platform, along with adding some hints for iOS-specific buttons.
Thus, when we designed the UI for iPad, we always had a choice of what to use for each action: a gesture, or a button. We didn’t want to run the risk of cluttering the screen with buttons for the entire gamut of possible actions in DOS2, so the decision was taken to hide some elements of the HUD to eliminate screen clutter. There’s always the risk that players might think we had too much going on on-screen, or conversely that the HUD was too sparse, creating confusion. It was a delicate balancing act, but we believe we arrived at a solid solution for this.
Even just launching a game of such a scale to the main menu is a big target, and being able to play the whole game from beginning to end is quite another.
To achieve the latter we needed to satisfy rather strict RAM constraints: one of the biggest levels of the game was using ~4.2 GB of RAM + GPU memory combined on the Windows version, and we needed to bring that number down to a 2.6-2.7 GB at the absolute maximum.
Exceeding this would just cause crash after crash, and there is no workaround like a 'swap memory' on iOS devices.
One common approach to resolve this issue is to tune graphics quality down, probably by quite a lot - but that’s a big compromise, and we didn't want to entertain it.
Instead of reducing graphics quality, we've gone the other way: using hardware-efficient compressed textures and compressed models. We also hand-picked and optimized a lot of engine code by squeezing bytes wherever possible. Using aggressive struct alignment alone won tens of MBs. It's very lucky and rare to win memory that easily. And turning uint32_t into uint8_t can really create magic sometimes...
We actually had to revert some of our optimizations during later stages of development because they led to odd and scary bugs – acid/water surfaces becoming fire, or disappearing items in the world. Those bugs made the game unplayable and corrupted save files, which you can’t undo, so it was important to carefully select only the optimizations we were certain about.
Sometimes we would want to bring out the big guns, enabling features like level streaming, which could bring a much bigger impact. Originally on Windows this feature wasn’t finished, so it was a risk using it. Elverils had to talk to Larian developers and fix all these issues together. In the end, we think it was worth the risk - nothing catastrophically bad happened, and we saved a lot of memory in the process.
Render resources aliasing using Metal MTLHeap API played a major role in squeezing the game memory footprint. Aliasing of resources essentially allows us to use the same memory for objects like textures, buffers, render targets etc. We mainly used it to reduce the memory footprint of the render targets. Some render targets used for intermediate render passes are not used in the beginning of the frame or not needed in the frame later. This allowed us to reuse the same memory region to serve for render targets of different render passes, sizes, and formats and saved us a decent amount of megabytes.
A hugely important pillar of DOS2 is its solid multiplayer. It supports up to 4 people at the same time, users can connect at any time, there are public lobbies and heavy usage of invites, user icons, and chat.
The multiplayer functionality is separated to an abstract multiplayer API, and every service (Steam, GOG f.ex.) implements their own API usage. We had to do the same for Apple’s GameCenter API, which is exclusive to the AppStore.
Thankfully we already had the implementation from macOS, and we leaned on this significantly. DOS2 iOS can cross-play with Steam, GOG (PC/Mac) by using the ‘Direct Connect’ feature, and can search for online lobbies from Mac and iOS devices. GameCenter API is not a ‘lobby-centric’ API, which means there is no lobby concept inside the API, while it is promptly present in all other APIs. To overcome this we have to create a virtual lobby inside the code and advertise this lobby using GameCenter’s ‘matches’ concept.
We ran into a few limitations, but this worked. All other elements (like achievements) were implemented 1-1, as in other APIs with iTunes Connect and XML files used for faster uploads. For DOS2 iOS we wanted to go even further, and thus we used one more communication method - Local play with MultiPeerFramework API, which allows the creation of small Wi-Fi networks on the fly. This API can’t co-exist with GameCenter, so you either play using MultiPeer or GameCenter.
Other cool twists include seamless invites which automatically launch the game for users and transitions them straight to multiplayer. The one thing that we always wanted to add but ran into a few issues was GameCenter Voice Chat, it’s a cool feature but unfortunately it can be available only in pure GameCenter sessions which meant it didn’t quite work for our purposes here. Overall though, after a lot of work and more than a few challenges, we think multiplayer on iPad turned out to be exactly the experience we were going for.
At the project’s inception we had PC and console versions of the interface and we had to adapt it to the touch screen. It seemed that we could take the PC version, tweak it a little and voila, this is done, everyone is happy, let’s go home. This is where we were mistaken, because in the end we literally changed almost everything. Every window, every button. When someone mentioned this we had to smile, because the initial brief was to change as little as possible. Ultimately though, it just wasn’t going to be possible - this was a case of optimizing for the platform, rather than making a compromise.
We had access to interfaces tailored for a computer and a console, from which we needed to assemble a UI for a tablet. Our adaptation is based mostly on the PC version because it is closer to the iPad platform than the console equivalent. So we took the PC interface as a basis, since it is made in such a way that most of the options can be reached using only a mouse, which is quite similar in terms of interaction to a tablet. 1-1 copying introduced a little confusion
in navigation, so we had to avoid some of the confusion by avoiding direct copying and, with that, the style for the whole game was formed.
Due to the specifics of the platform, it was necessary to introduce new mechanics, like two-finger scrolling (more on that later…) and swiping. Fitting all these innovations into the game’s UI was the most interesting part. Swiping is a good example - we use that in the hotbar, and it was such a natural fit. For experienced iPad users the swipe left/right is an intuitive gesture, so we’re really pleased with how that was implemented.
Almost all the new features were related to the HUD, so this made the task more complicated. In implementing all the UIs, including the HUD, we found that we had to resize every element from the PC interface. Not only because of Apple’s guidelines, but purely because a finger pressing a button doesn’t have the precision of a mouse cursor. At all times, we had to remember that it was necessary to take into account the needs of the player by not overloading the interface.
Porting games to a new platform like iOS means there will be challenges when adapting the rendering pipeline to mobile architecture, but there can also be straight out-of-the-box benefits. One such example is having Hidden Surface Removal (HSR), which completely eliminates the need for a popular approach in desktop games - Depth First Pass.
The idea with Depth First Pass usually is to draw a simple and cheap representation of the scene early in the frame, and use it later to reduce overdraw and invocation of complex shaders by rejecting most of the hidden pixels with depth-test. HSR does the same at a much lower cost by keeping track of the frontmost visible layer for each pixel, though this only applies to TBDR architecture ( the GPU architecture for iOS devices).
So for us this meant we can reduce CPU and GPU load by removing a pass that renders the whole scene - quite a big win for doing almost nothing on our side!
One particular issue we ran into were the stutters and freezes on playback of special FX (when casting spells or when things exploded in the scene) – a high amount of shaders were compiling on the fly and it’s not possible to precompile everything due to memory limitations. As a solution to that, we collected shader information by manually casting all spells in the game and putting shader binaries into binary archives (a new feature of iOS14). The result you see at the first launch of the game, is that it takes 30 seconds to generate the shader cache. Once it’s done, all the consecutive launches use it on-demand, and you don’t see any freezes.
Some might ask why we didn’t precompile those and deliver the game with it – this is because the shader cache format may depend on your OS version. Thus, you’ll have to consider every possible combination of hardware/OS beforehand which is not an option - just imagine that new hardware appears on the market and you have to do something about it!
We tried to encourage usage of half types wherever possible. In model vertex data, in shaders, memory packing, and so on. Half-float ALU usage means more registers are available, which means more threads can run in parallel - essentially completing things a lot faster.
The main challenge when adapting games like DOS2 on mobile platforms is that they usually rely on the 'Deferred Rendering' approach, which is very heavy on GPU bandwidth. One solution is to switch to a 'Forward Rendering' approach that is much more lightweight, but it's not always a viable option - and wasn't in our case, purely due to the fact that the game used a ton of different materials and lights.
So our way was optimizing the existing Deferred Renderer. The main problem we encountered with this renderer was thermal throttling. The frame time was already in acceptable bounds, so our primary target was to reduce memory transfer operations to leverage bandwidth usage, which in our case was the main reason for device overheating.
The whole rendering process of the scene was analyzed and many render passes were re-grouped, batched together or removed at all (like the previously-mentioned Depth First pass). To give some examples, the most profitable ones were:
- Batching skylight & lightprobes rendering into one render pass (by ensuring that both passes use the same set of render targets)
- Rewriting engine code to render shadowed lights more efficiently. As seen on Figure 1. originally the game was rendering light after light, and each light is rendered in 3 stages: shadowmap, light volume, the light itself. Each of the stages changed render targets, so it was pretty inefficient bandwidth-wise. The solution was to rearrange rendering commands so that we render each stage, and for each stage we render all light data as shown on the image below.
- Using one big “ubershader” which combined all post-process shaders into one, instead of having several fullscreen render passes
Aside from big optimizations like that, carefully placed Load/Store operations for each render target can also bring huge gains. For example, setting proper Load/Store operations for UI passes removed several fullscreen memory transfers between on-chip and system memory
The overall conclusion is that you need to very carefully track your Load/Store operations for render targets and keep an eye on render passes that can be grouped together, as only these two simple things can severely reduce the device load and thermal throttling
Figure 1. Re-arranging rendering commands reduced render pass count.
As we mentioned before, the UI was a key thing we had to get right with DOS2 on iOS.
At first, we were inspired by the iPad’s Home Screen design, but we quickly realized that wouldn’t work well for us - dragging items around is an essential part of controlling the game, and the home screen requires the user to hold on an item for a second or two before they can drag. We needed to be able to move things quickly, so this took some thought.
One design decision we ran into early on was whether we should implement one or two-finger scrolling. After weighing up the pros and cons of each, we settled on a hybrid system, which sees two-finger scrolling employed for those UI elements where one-finger scrolling would conflict with dragging items. The ‘Dragging Problem’ was always something we had to work around. For example, [this came up with] the inventory or crafting windows. Dragging items in or out of inventories is something that happens more often, so in that case sacrificing one-finger scrolling made sense to us.
DOS2’s combat is a well-known and established system, which we had to bring as seamlessly as possible to the iPad. This is a huge part of the game, and one we knew we had to get right.
We found we ran into three main problems in combat - Precision, Misclicks, and Awareness.
We attempted to solve the Precision Problem by introducing a new feature to the game - players want to be able to interact with the world of Rivellon in precise detail, and so to make sure that their clicks were hitting the exact right spot, we introduced the Magnifying Glass. Fun fact: This was actually inspired by iOS’ native text input feature, which goes to show how enthusiastic we got about this hardware!
Misclickswere another hurdle, and one that sparked the endless debate: what’s better, a double click or a confirm button? Ultimately, we decided to go with the button, because it separated aiming spells from casting them. This way, you know 100% percent that you fire your spell to the exact point you picked. A double-click system has the precision flaw – you just can't tap twice on the same point in the world. This would have lead to confusion and complaints – a theory that was borne out by our playtesting data.
It’s vitally important that players are totally aware of all their options in a game with the complexity of DOS2. With so many abilities, spells, and actions available in the game, we found we needed to add hints to make sure players knew how to activate whatever they were trying to do. With so many choices, it was important to treat every one separately.
It took a lot of QA, and a lot of feedback from extensive playtests, but after tussling with these three main issues, we really believe we arrived at the right approach to combat on the iPad.
Looking back on the 2 year development of DOS2 on iPad, those seven points above are just some of the challenges that we faced, but they were certainly some of the biggest. To summarise, these main targets were:
- Adapting the game’s controls
- Making it work with the device’s memory
- Getting multiplayer to be as seamless as we wanted it to be
- Making a series of Art adaptations
- Rendering the game effectively on mobile
- Re-designing the game’s UI
- Adaptations to combat
The reception’s been good since launch, and we’re very proud of what we’ve released. Between the two studios and Apple themselves, we really think we’ve brought in-depth CRPG gaming to mobile in a way we’ve not seen before. It’s an exciting time in the mobile space thanks to this latest generation of tech, and the chance to be at the vanguard of that was one that we didn’t want to miss out on.