While an emulator's primary job is to emulate, there's usually a lot more that goes into a good emulator. For Dolphin, it may feel like a lot of work has gone toward luxury features and optimizations rather than improving accuracy and compatibility. For example, Ubershaders is a wonderful, game-changing feature, but it can't fix any bugs in emulation. With another of those huge features on the brink, it's important to highlight that no one has forgotten about Dolphin's weaknesses - it's just getting harder to fix them. Most of the games that no longer work in Dolphin either require better timings (which slow emulation and need to be hardware tested,) or rely on undocumented behaviors that have to be painstakingly sought out, rather than stumbled upon.
Case in point, fixing one of those cases could require weeks of devotion and every development tool in our arsenal just to locate the bug. With Red Steel, developers ended up having to reverse engineer why an engine bug affected Dolphin but not the Wii.
We're going to cheat a bit here - these two changes were separately developed nearly a month apart with completely different aspirations. Yet, because these fixes targeted the same area of code and was developed by the same developer, it's a lot easier to explain everything together. leoetlino has outdone himself this time. Just fixing Red Steel alone would be cause for fanfare, but he went the extra mile and smashed the DSP Accelerator with hardware tests to perfect Dolphin's emulation of it.
The DSP accelerator is a relatively obscure piece of hardware. On console, it is used by the DSP core for all memory accesses. The accelerator is also responsible for part of the looping audio behaviour and for decoding ADPCM.
Essentially, three parameters are used to configure the accelerator for reads: the starting (or loop) address, the ending address and the current address. Every time the DSP wants to read data from the main memory (MEM1 or MEM2) or ARAM, it configures these parameters then starts issuing reads in a loop. Every read automatically adds one to the current address value. If the next address points right past the ending address, the accelerator raises an overflow exception and resets the current address back to the loop address.
Emulation of the DSP accelerator hasn't changed too much in the recent years, because the core functionality is quite simple, and Dolphin already got most of it right. But what are the behaviours that Dolphin did not implement?
Unmasking a Longstanding Bug¶
Solving Red Steel was a journey that took many twists and turns. Red Steel shipped without symbols, making it very difficult to do anything without extremely good reverse engineering skills and complete knowledge on PowerPC assembly. But, when searching the disc, leoetlino discovered two different game executables,
WarfareRevoReader.elf. These unstripped binaries had full symbols. If he could get them running it'd jumpstart any debugging efforts: he'd be working on a version with complete symbol information, including variable names for internal state!
Although it was possible to convert the .elf executables to .dols and use Neogamma's alt-dol feature to test them, leoetlino decided to create a homebrew called SimpleLoad instead. By not using an existing loader, it was much easier to make any changes he needed for testing, including loading elfs directly on both console and Dolphin. The first debug elf,
WarfareRevoRetail.elf crashed earlier on because the file tree on the game disc differentiated from what it expected, however
WarfareRevoReader.elf contained a fully functional older version of the game using the filesystem on the disc!
SimpleLoad had a second feature implemented that proved invaluable — Gecko hooking support. With Alt-Elf plus Gecko hooking, he was able to enlist JMC47 to dig into the game and try to find where things go wrong. After several days of debugging, they ended up finding the code path that was responsible for playing sound streams, and in particular the function that told the DSP where to get sound data from for the main stream voice. By hijacking the function with custom input, they determined that the DSP hardware was masking voice addresses.
This is where things went south. Because Red Steel relies on the DSP to signal the current playback position and Dolphin wasn't masking the value at all, the game would get very confused: it had no idea how much of the voice had been played, so it would never fill the audio buffer. Not only did this cause streaming audio to be completely broken, but events that required streams to be done playing could never trigger, as they couldn't play! As a result, the game would softlock every time it waited for voice acting to finish.
After correcting the behavior through rigorous hardware tests, Red Steel and Far Cry Vengeance (another Ubisoft game using the same sound library) finally work correctly.
Further investigation reveals that both games pass PowerPC effective addresses to the DSP, which doesn't make sense, as the DSP uses physical addresses to access memory. Given that only these two games do so among the entire Wii library, it is likely that the Wii was covering up for a bug in the games. For more information about fixing Red Steel, JMC47 kept a dev diary going on the project to fix Red Steel on his blog.
Knowing When to Stop¶
Fixing two games that have never worked in Dolphin may sound great, but, JMC47 is greedy and started trying out the Red Steel fix on other games hoping for more. Two notable games with long-standing audio hangs, Hikaru no Go 3 and Harry Potter and the Sorcerer's Stone, haven't worked since Dolphin 3.5-33 in Dolphin's old async HLE.
Unfortunately, the masking fix did not affect these games. While he is next to useless at coding, JMC47 had the skill of being exceedingly stubborn and continued to poke random changes in Dolphin to try and bypass the hangs.
After four days, he succeeded (at least to his standards) by completely breaking audio in Hikaru no Go 3. Breaking the audio completely fixed the hang, allowing the game to function normally. What was he modifying? The DSP accelerator, the same area where Red Steel was having issues! With an assist from booto, he eventually was able to relay what he discovered to leoetlino. Having just finished working with the DSP accelerator, he reluctantly started doing more tests on the DSP accelerator. He made plenty of discoveries.
- When the accelerator reached the end address, it would loop back to the start address and immediately stop reads. Reads would only get resumed if the AX ucode wrote to a hardware register — and it only did so for looped voices. Dolphin only looped back to the start address. Sometimes the game or the ucode eventually realizes the voice is finished and stops playing it, but that's often too late.
- Incorrect address skipping. When an address ends with 0xf, console adds three to the current playback address when calculating the next one. Dolphin was off by one read and only added three when the address was 16-byte aligned.
- Edge cases when looping. The accelerator does not always loop back to the start address or trigger an overflow exception when it loops.
Fixing these issues with Dolphin's emulation of the DSP accelerator caused a ton of longstanding audio issues to suddenly vanish! Games that use streaming audio used to suffer from audio crackling as they read past valid data due to these bugs. Finally, titles such as Harry Potter and the Chamber of Secrets, Conflict: Desert Storm and many other titles will play audio without annoying crackles at the end of every sound. Harry Potter and the Sorcerer's Stone sees huge benefits from this, as the garbage audio loads (shown in the video above) are now fixed. As well, a game hang right before the final boss is also fixed. Last but not least, Hikaru no Go 3 finally works again, no longer hanging whenever a character speaks.
Note - while many of these GameCube titles worked in Dolphin 3.5 in Dolphin's old HLE audio, we should treat them like Red Steel. The only reason they worked in 3.5 was that audio emulation was so wrong that it managed to avoid these issues. Case and point - DSP-LLE never worked correctly for any of these titles until now. As an added bonus, leoetlino added a ton of unit tests, to ensure any future changes to Dolphin never break these fixes. Then again, that's never stopped us from breaking something before.
For the longest time, setting up controls on macOS has involved a lot of guesswork. A laptop without any external devices connected would list many keyboards, and only one was the real device!
This quirk was brought to you by how IOKit represents basically everything that isn't a gamepad as a generic "Keyboard/Trackpad". For example, a trackpad, a keyboard, a headset with volume controls, and an external mouse would appear in Dolphin's list as Keyboard/Trackpad, Keyboard/Trackpad, Keyboard/Trackpad, and Keyboard/Trackpad, respectively. The only way to find which was actually the keyboard was to just try each device, and the one that responded to keyboard presses was the real keyboard.
Annoyed by this issue, a year ago ligfx added Quartz to Dolphin's macOS interfaces. Quartz not only identifies devices better, but it also combines like devices (keyboard and trackpad for example) and excludes useless system devices. ligfx planned to remove IOKit keyboard devices at a later date to solve the many devices problem once and for all, but... forgot. For the past year, the addition of Quartz just added yet another set of devices to Dolphin's device list, which didn't really help the situation.
Enter Progress Report newcomer khg8m3r, who stepped in to finally chop IOKit's keyboard and trackpad support to use Quartz exclusively for those devices, resulting in the clean, easy to use device list Dolphin on macOS so desperately needed.
There are a few consequences to this change, however. IOKit allowed users to plug in multiple keyboards and map each keyboard to a different controller. Quartz combines all keyboards into one, like Windows does. This was a very niche use case, and it's better to use controllers for multiplayer anyway, so we're happy with the tradeoff. Also, sadly, you can no longer use your headset control buttons or IR remote as a controller in Dolphin. Thus, the only advantage that macOS had is finally gone...
There is one small bonus to this change. Instead of using IOKit Joystick for controller input, we have switched to using IOKit GamePad. This should fix some issues with some controllers, and just give us better controller support overall.
Last month, we asked for some help figuring out why Zapper: One Wicked Cricket was crashing in such a strange way, spamming DOLPHINSLOTA in hexadecimal. Well, we managed to solve why, and it's less interesting than you'd hope — that was just another Dolphin bug, and unrelated to the crash.
Fog's bisection ended up in the worst possible place - phire's CoreTiming fixes. At the very least, a regression caused by better timings is very difficult to debug. JMC47, on the other hand, bisected to another build though — the more accurate DVD timings. What gives?
Well, it turns out this was a double regression — there was a second hang mere seconds after the first hang, and when JMC47 was bisecting, he literally only checked to see if the game reached the loading screen. Fog on the other hand was actually seeing if the result screen loaded. CoreTiming fixes caused the loading screen to hang infinitely, DVD timing fixes caused the loading screen to never show up!
JosJuice stepped in to help out and made a pull request that reverted a part of the DVD timing changes (chunking) that shouldn't affect actual timings or behavior. Suddenly, the game was working again!... albeit with some annoying popups. So, Dolphin's DVD code had an odd decision - it declared any 0 byte read an error, even if it was perfectly valid. On the other hand, Dolphin normally wasn't throwing this panic alert... because the read chunking was silently failing 0 byte reads!
Fixing that allowed the game to proceed to the load screen... and load the results screen. What happened with the CoreTiming change regression? It turns out it exposed a timing issue with the old DVD timings. If they had bisected before chunking was finished, it's likely a ton of time would have been wasted investigating a change that actually worked correctly.
Sometimes, it takes luck to solve bugs. In this case, they fell ass-backwards into the solution due to improper bisecting.
The ArtX GPUs in the GameCube and Wii support Logical Operations (LogicOps) based on OpenGL's implementation. Just as in OpenGL, LogicOps are a more complex version of Blending operations, allowing the developer to perform a wide variety of binary commands in the blending stage. While it allowed a lot of control and options, they were options most games didn't need, so LogicOps are not used by the majority of the GameCube and Wii library.
Supporting LogicOps in Dolphin is easy - in OpenGL. The ArtX GPUs logical operations are literally the same as those in OpenGL, allowing Dolphin to just 1:1 transfer the commands to the host GPU and it work perfectly. Vulkan also natively supports LogicOps, though driver support is optional ...which means that everything supports it except Adreno and Mali. On D3D however, it's been a different story. D3D didn't support LogicOps, so we used D3D blending operations to emulate as many of the Logical Operations as we could - a hack that gave us "good enough" LogicOps emulation. It worked... most of the time.
One of the most notorious of LogicOp hack bugs is the shadowing in Star Fox Adventures, mostly because it results in a funny glitch. For whatever reason, the game renders the shadows of every character and object on screen underneath all of them at the same time, and uses LogicOps to select the right shadow for each object and ignore the rest. With the D3D LogicOps hack, this check constantly bounced between different objects, resulting in shadow flickering and even the shadows being mixed up!
But times have changed! A new version of D3D11.1 actually has Logical Operations, allowing us to properly support it in our D3D backend and axe a bunch of annoying D3D11 only bugs from the LogicOps hack.
Getting LogicOps working in Direct3D is a dream come true for those wanting parity between all the backends. But for those stuck on Windows 7... we're sorry but, that old OS is screwing you a bit here. While Microsoft did bring Direct3D 11.1 to the eight year old operating system, it didn't bring Windows Display Driver Model 1.2 with it. And guess what new D3D feature requires the newer driver model? So even though Windows 7 has D3D 11.1, it did not get LogicOps support in D3D. This was certainly frustrating for us, but in the interest of code cleanliness, and since the majority (74.75%) of our userbase is now on Windows 10, we decided it'd be best to drop the old LogicOp hack that had several LogicOp games partially working. Without the hack, titles that use LogicOps not only aren't getting better in D3D under Windows 7, but they may actually be getting worse. There's not much we can do about this except recommend users use OpenGL or Vulkan, or upgrade to a newer version of Windows. If you are really attached to Windows 7 and D3D and this change broke your favorite game, versions older than 5.0-5404 retain the LogicOp hack and are available on our download list.
Before we move on, there's one curious thing about how Star Fox Adventures uses LogicOps. Even with correct LogicOps support, if the internal resolution is set high enough, the faint outline of incorrect shadows can be seen in addition to the fully rendered proper shadow. We don't think this is a problem with Dolphin, but a quirk of very high internal resolutions combined with the odd behavior the game is doing.
Symbol maps are a wonderful thing for debugging and profiling. By default, Dolphin attempts to generate JIT blocks with more developer-friendly names taken from a symbol database, which means that each block requires one database lookup. This is normally fast enough to not be an issue at all.
However, it turned out Dolphin had a very poor implementation of the symbol lookup function, which looped through every single known symbol most of the time. As the number of symbols in the database increased, performance would slow to a crawl with huge JIT stuttering. leoetlino's motivation for fixing this was that the slowdown became unbearable as he had a full symbol map for Red Steel with several thousand entries, but it actually started affecting games once Dolphin's ability to recognize symbols in Wii games improved. As such, some games have recently started to slowdown thanks to the symbol lookup function, even if they aren't using debug features!
This makes it so the JIT will not look up symbols when generating blocks by default, unless profiling is enabled. And if symbols are used, that case has been optimized to work faster, making things a bit easier on developers.
Reset settings is usually a way to get things back to default to fix potential errors. Netplay has an option to reset traversal settings to help with people having issues connecting to the traversal server. Unfortunately, hitting that button would prevent you from connecting to the traversal server unless you completely wiped out the saved traversal settings. This is one of those frustrating problems that's existed for quite a while - at least since the current configuration rewrite began. Dolphin was saving u16 values as hex, which would normally save unless you wiped your configuration files.
This tiny, insignificant, totally hard to spot bug slipped in while merging Dolphin's new config system. New builds no longer have the issue, and if you're one of the users affected, hitting reset again in the latest development builds should fix the issue without you needing to delete any INI files.
This is the dawn of a new era. The most requested feature of all time, a revolution in gameplay! You can now eject discs in Dolphin!
...again. This feature was in Dolphin in the long long ago but disappeared once the change-disc functionality was implemented. But, sometimes you don't want to change a disc. Sometimes you're a glitch-hunter trying to mess up a game's timings, or sometimes you're just trying to crash Dolphin. And for those times, you now have Eject Disc in the UI next to Change Disc, and also available to be assigned as a hotkey.