Monday, April 30, 2007

R11 almost ready

Following on from last week's work, I've been busy fixing a few more issues relating to the way Daedalus handles textures.

To start with, I refactored the way that n64-format textures are converted to psp-format textures. Previously, I was converting the textures at the point at which they were loaded into texture memory on the n64. A result of this was that in order to support mirroring, I needed to keep a copy of the converted pixels in RAM. By moving the conversion process closer to the point that the textures are actually used on the psp, I've been able to remove this buffer and perform conversion and mirroring in the same step. This has approximately halved the memory needed for each texture, and is slightly faster than the previous approach.

The most significant change I've made is to fix a memory leak in the texture cache. I mentioned last week that I'd discovered that the texture cache was the biggest culprit for soaking up memory. It turned out that despite my fixes and support for 4-bit and 8-bit palettised textures, I was still running out of memory in certain situations. I did a bit more investigating, and discovered a resource leak that had been in the texture cache since it was first written (probably 7 or 8 years ago now!)

It turned out that in certain situations, several textures would hash to the same bucket in the hash table that I was using. This wasn't normally a problem, but occasionally the process which purges old textures from the cache was accidentally leaking textures. This was wasting video memory and causing the leaked textures to be re-converted on next frame.

The fix ended up being a very simple one-line change, and as a result the texture cache is now 100% leak-free. As an added bonus, it seems that the change has resulted in a nice 4-5% speedup - I suspect this is because the leaked textures are now no-longer being unnecessarily reconverted.

The final bit of work I've been doing is setting up a fixed-size pool for allocating textures from (well, there are actually two pools - one for fast VRAM and another of standard RAM for when this runs out). Despite the various improvements and fixes I've made to reduce the amount of memory being consumed by the texture cache, I wanted to put a hard limit on how much memory can ever be used. This change means that if the limit is ever reached, I just display white textures until some texture memory is freed up a little later. Previously Daedalus would just keep allocating RAM until it ran out of memory and crashed, so the new solution is much nicer :)

All in all I'm very happy with the state of R11. The changes I've made mean that I can permanently allocate 8MB for the Expansion Pak, and not worry about running out of memory. A welcome side-effect to all the texture changes I've made is an approximate 5-10% speedup over R11. On top of this Daedalus now remembers your preferences for each rom, so it's a little nicer to use.

I've still got a few small things to polish, but I'm hoping to release R11 by the weekend.


Sunday, April 22, 2007

R11 Update

Wow, it's been far longer than I'd intended to give this update. I had a great time in Spain, and came back and spent a few evenings playing through Crackdown. It was quite nice to take a break for a few days :)

Since then I've been working on the various features I promised for R11.

I talked a bit about the texture cache being the main culprit for gobbling up memory while the emulator is running. When I started profiling this in detail, I realised that one of the worst offenders for memory consumption was mirrored texture support for 4 or 8-bit palettised textures. There were actually two main problems which were exacerbating the problem.

Firstly, I've never handled palettised textures directly in Daedalus. By that, I mean that rather than converting the palettised texture on the n64 to a palettised texture on the PSP, I've been converting it to a 32-bit RGBA texture. This means that on the n64, a 64x64 pixel 4-bit palettised texture would take up 2KiB. By converting this up to a 32-bit RGBA texture on the PSP, it takes 16KiB - an eightfold increase in memory usage.

The second issue which was compounding the problem was that the PSP doesn't have support for mirrored textures. In order to support this feature I have to manually duplicate and mirror the texture. This means that a 64x64 texture mirrored along the S and T axes on the n64 will become a 128x128 texture on the PSP.

The main problem I was getting due to running out of memory was due to the heavy use of mirrored 4-bit palettised textures in some games. A 64x64 4-bit palettised texture that took up 2KiB on the n64 would consume a huge 64KiB on the PSP - a 32-fold increase! The problem was that certain games were using dozens of such textures in a single display list, and the available memory was rapidly being exhausted.

So over the past couple of weeks I've been working on rewriting Daedalus's texture handling so that it supports 4-bit and 8-bit palettised textures directly. This has taken a lot more time than I'd anticipated because of the number of places in the codebase which have to fiddle around with texture data directly. I also spent a week trying to track down two horrible bugs (both of which turned out to be brain-dead logic errors on my part).

The great thing about this work is that not only does supporting palettised textures directly save a lot of memory, it also has a number of small performance benefits. Generating less texture data generally means a bit less work for the CPU (well, less cache usage), so converting palettised textures is now a bit quicker. Palettised textures are also a lot more efficient to render with (mostly due to the fact that they consume less bus bandwidth and can make better use of the PSP's texture cache.)

The other big chunk of work I've been bashing away at is improving the way that I handle preferences for individual roms. One of the big problems with the current setup with Daedalus is that the main daedalus.ini file consists of both global rom-specific details (such as the rom's name, save type, comments etc) and local user-defined preferences (such as whether to enable speed sync, disable dynarec etc). This means that I can't ship a new version of daedalus.ini without wiping out people's local preferences.

What I've done now is split daedalus.ini into two files. roms.ini will contain global rom-specific details, and an updated version will be distributed with every version of Daedalus from now on. If I know dynarec causes a certain rom to fail to run, I can add a setting for this in roms.ini, and everyone will be able to pick up the change in the next release. Another good example is the SaveType field; every version of Super Mario 64 uses a 4k EEPROM, and so once this is set up in roms.ini it should never need tweaking.

The other file I created is called preferences.ini. This file won't ship with Daedalus - the emulator will create this the first time you change some settings when playing a rom, and update it with any further changes that you make. This means that when you copy a fresh build of Daedalus across to your memory stick, the new build will pick up your existing preferences.ini file and so remember all of your settings.

The settings that Daedalus will currently remember for each rom are:

  • Texture Update Check
  • Frameskip
  • Limit Framerate
  • Dynamic Recompilation (used to override the setting in roms.ini if you're having problems with dynarec)
  • Audio
  • Adjust Frequency
  • Controller

I'll be adding to this list over the coming months, as more options become configurable.

One other thing that will be stored in preferences.ini (which I've not coded yet) is all of the options from the 'Global Settings' page - stuff like the viewport size, stick deadzone, whether to display the framerate or not, etc.

So R11 is shaping up pretty nicely, even if it's taken a bit longer than I'd hoped. I have a few more things to work on, but I'll try to keep you all updated a bit more frequently as I work towards the next release.


Thursday, April 05, 2007

R11 Plans

I had planned on posting this update on Monday, but I've spent the last couple of nights catching up on a few games that I've not had chance to play since they were released (Crackdown on the 360 and Afterburner : Black Falcon on the PSP for the curious :)

For R11 I'm hoping to fix the Expansion Pak support for once and for all. Memory is currently very tight, so I need to look at shaving off a few hundered KB here and there. Almost all the memory Daedalus uses is allocated up front - there are very few dynamic allocations at runtime, with the biggest culprit being the texture cache. So for R11 I want to introduce a pooled allocator for the texture cache, which will mean that when it runs out of memory, you just get white textures appearing rather than a hard crash.

The other feature I want to improve in R11 is the way that global settings and per-rom settings are handled. At the moment Daedalus discards any changes to settings that you make when you restart your PSP, and it can be incredibly tedious setting things up every time you run a game. This is quite a small feature to add, but I think it will make the emulator much nicer to use.

Behind the scenes, I want to spend a short while working on some scripts to handle packaging new releases for distribution. Current I do all of this by hand, and it's one of the things I least enjoy about the project. Hopefully, the more automated I can make this, the more frequently I'm likely to release builds :)

I'd like to get all these changes done and release R11 within a couple of weeks. I have a couple of busy weekends coming up, so I'm not going to specify an exact date. You can expect it before May though (I'll try not to be a couple of days late this time :)

R11 is essentially going to be a compatibility release - hopefully the Expansion Pak support will lead to a few more roms running (Majora's Mask being one I've already verified). Looking further ahead, R12 will be back to concentrating on speed (as it happened, R10 achieved a 10-15% speedup without having implemented the majority of the optimisations I had planned :)


PS Off to Spain again over the Easter bank holiday weekend. Back on Tuesday :)

R10 Reception

I just wanted to say a quick thank you for all the feedback I've received on the R10 release. I'd been looking at it as quite a small update, but people seem to be very happy about the incremental speedup and new features such as frameskip and so on. I found releasing R10 within 4-5 weeks of R9 was really hard work, but I'm now convinced that releasing smaller updates more regularly is the right way to go. I'll be following this post with some details about the plans for R11.

Finally, it turns out that this blog had over 40,000 visits over Sunday/Monday, which is incredible - thanks for continuing to read :)


Monday, April 02, 2007

Daedalus R10

This is a few hours later than I'd hoped, but here's R10:

Daedalus PSP R10 for v1.00 Firmware
Daedalus PSP R10 for v1.50+ Firmware
Daedalus PSP R10 Source

This is the changelist:

[+] Added frameskip option.
[+] Added framerate limiting option.
[+] Added adjustable stick deadzone.
[+] Allow pause menu to be activated even when rom emulation has hung.
[!] Fix crash when no roms found, added explanatory message.
[!] Fix flickering when small number of roms found.
[^] Fix texture hash frequency check.
[^] Finally got code compiling with -O3 optimisation flag in GCC.
[^] Use VFPU for InvSqrt (thanks hlide!)
[^] Improved VFPU code for clipping and tnl (thanks Raphael!)
[^] Improved inlining of AddTri.
[^] Reduced time spent searching for overridden blend modes.
[~] Remove debug console in public release builds (smaller footprint and slight speedup.)

I'll post tomorrow with details on what I want to work on for R11. In the meantime, enjoy!