Notice: This post is marked as dated. Some of its contents are no longer correct. Take with a grain of salt.

What exactly does FoamFix do?

This is a pretty common question, and I've been reminded of it after having created my unofficial FoamFix downloads page. I'm going to try to answer this question in a reasonable amount of detail. Obviously, I have a bit of a programming background, so these descriptions might end up a bit technical. I'll try to keep them understandable.

There's a lot of ground to cover, so let's get right to it.

As you probably know if you've read FoamFix's config file, it does a lot of things. Here's a copy of the config file from Radon, as an example (with the comments removed, to save space):

client { B:clearDuplicateModelRegistry=true B:deduplicateModels=true I:deduplicateModelsMaxRecursion=6 B:disableTextureAnimations=false B:fixRecipeToastCrash=true B:initOptions=true } coremod { B:blacklistLibraryTransformers=true B:dynamicItemModels=true B:fasterAirItemLookup=true B:fasterCollisionBoxes=true B:fasterEntityDataManager=true B:fasterEntityLookup=true B:fasterPropertyComparisons=true B:forceDisable=false B:optimizedBlockPos=true B:replaceWorldSimpleName=true B:smallPropertyStorage=true } experimental { B:fasterVertexLighter=true B:parallelModelBaking=true } launchwrapper { B:weakenResourceCache=true } tweaks { B:disableRedstoneLight=false B:immediateLightingUpdates=false }

We'll go over these in order. I'll also include a summary of each option's impact, in the form of a "Kind" and an "Incompatibility Risk".

client

clearDuplicateModelRegistry

Kind : Startup Memory Reduction

: Startup Memory Reduction Incompatibility Risk: Low

This one's pretty simple, and only results in a memory reduction during loading, not after.

I'm not very well versed on how model loading works in Forge, but the gist of it is that models are loaded in two passes. Normally, the junk generated by the first pass is cleared after the second pass finishes, meaning there's twice the model data in memory as is needed, With this option enabled, FoamFix will clear the junk before the second pass starts.

deduplicateModels

Kind : Memory Reduction

: Memory Reduction Incompatibility Risk: Medium-Low

This is FoamFix's big main feature, and was pretty much its only feature when it was initially released.

As of 1.8, Minecraft loads the model (the "shape") of every single possible block up-front, instead of generating them on-the-fly like in 1.7 and earlier. It also happens to take the... naive approach to this.

Every single block in the game that is a normal cube, like a dirt block, results in a duplicate cube model. Same goes for, say, a vanilla anvil and a Dark Steel anvil. So FoamFix goes through all the model data, finds duplicates, and makes them all point to a common copy.

This doesn't work if the owner of the model attempts to modify it at runtime, but that doesn't really ever happen. Models are usually treated as immutable, so this works very well.

The deduplicateModelsMaxRecursion option is related, and basically decides how hard FoamFix will work to find duplicates. Models can refer to other models, and it takes extra time for FoamFix to look at all the models that refer to models that refer to models that... you get the point.

disableTextureAnimations

Kind : Speedup

: Speedup Incompatibility Risk: Medium

This one's off by default. As you probably know, Minecraft supports animated textures! However, the way they work isn't stellar.

Modern rendering is based on sending data from the CPU to the GPU. They have completely separate areas of memory, so it takes time for them to communicate. So, generally, a game will get all of its data together, shove it on the GPU, and instead of having to describe the shape of everything every frame, it just tells the GPU "you know shape #85745? draw that at 0, 3, 1 with texture #485".

It's a little more complex than that, obviously. Minecraft does this too, for the most part. When the game starts, it takes all the block and item textures and stitches them together into a big "atlas", and then shoves that on the GPU.

So here's the problem. Rather than send animated textures to the GPU, Minecraft keeps them in CPU memory, and modifies the GPU texture atlas every tick, which is to say, 20 times a second. This works great in vanilla, which has around 21 animated textures, but modpacks may have hundreds of these. Uploading all the animated textures every tick in this case is really, really expensive.

A lot of computers handle this fine, but some (like mine) really don't. I get 6 FPS with this option off, and a locked 60 with it on.

fixRecipeToastCrash

I can't find any references to this option in the source code. I'm guessing this was necessary at one point, but the fix got rolled into Forge.

initOptions

Kind : Convenience

: Convenience Incompatibility Risk: Very Low

If enabled, every time the game starts, FoamFix will set alwaysSetupTerrainOffThread in forge.cfg to true and turn off mipmapping.

coremod

All options in this section are exclusive to the Anarchy edition of FoamFix. Lawful does not have any of these options if you let it generate a config, and it won't use them if you copy over an Anarchy config file.

blacklistLibraryTransformers

Kind : Startup Speedup

: Startup Speedup Incompatibility Risk: Low

You may have heard the term "ASM" get thrown around when talking about Minecraft modding, usually about how it's bad or invasive.

ASM refers to ObjectWeb ASM, not assembly code. It's a library used by Forge to rewrite Minecraft's code on-the-fly, instead of making you modify the jar. (This is, in a nutshell, why you don't have to delete META-INF anymore.)

How this works is LaunchWrapper, a Mojang library mainly used to patch old Minecraft versions that wouldn't run on modern Java (or with modern Mojang services) otherwise, stands in front of the Java ClassLoader (which handles reading all the code from disk), and passes it through a series of "class transformers".

Here's the issue. Minecraft has a lot of classes, a lot of which coming from other people's code it uses ("libraries"), which are things you don't want to patch. But it still spends time asking all the class transformers if it wants to modify them.

Enabling this option adds a bunch of these libraries to the LaunchWrapper "transformer exclusions" list, which is much faster to check than just asking all the transformers. As such, this is a modest speedup during startup with no ill effects in the majority of cases.

dynamicItemModels

Kind : Memory Reduction

: Memory Reduction Incompatibility Risk: Medium

As established earlier, Minecraft loads the models for all blocks up-front instead of on-the-fly. It does this for items too, which use all the same systems as blocks, unlike 1.7 where they were pretty much entirely separate.

The thing is though, there can be a lot of variants for items. Have you ever scrolled through JEI without filtering? There's a lot of stuff there!

So what this does, is FoamFix uses a special "lazy" model for items that doesn't actually contain any data. The first time it's rendered, FoamFix will go ahead and "bake" the model that needs to be used. This means it may actually result in a slowdown, but it will reduce memory usage, and in most cases the slowdown is negligible.

fasterAirItemLookup

Kind : Speedup

: Speedup Incompatibility Risk: Very Low

This one was discovered by Aikar of PaperMC, so kudos to them.

Did you know blocks and items in Minecraft are totally distinct? When you break a block and pick up what it drops, that is an item representing the block, not the block itself. So, Minecraft maintains a mapping of what blocks turn into what items when in an inventory.

The issue is, air counts as a block too! So there's some places in Minecraft where the air block needs to get turned into an item. And Minecraft keeps track of these mappings in a hashtable (which we will be covering a bit later), which is fast as far as tables or maps go, but it's slow compared to a field lookup.

FoamFix goes through and finds all places where this is done, and replaces the map lookup with a field reference. The first time it's done, it still results in a map lookup, but FoamFix caches the result of the lookup and puts it into a field.

fasterCollisionBoxes

Kind : Speedup

: Speedup Incompatibility Risk: Medium-High

Forge adds events in a lot of places to vanilla code to make it possible to modify with, well, a mod. But events aren't free! They're actually pretty expensive, honestly.

But Forge has an event for every time a block's collision boxes are retrieved. As you can imagine, this is really frequent, as every entity needs to know what the shape of the blocks around it is. So every entity, possibly multiple times a tick, calls this method.

The functionality offered by having this hook is definitely great, but it does result in a slowdown. So if this is enabled, FoamFix tries to make that hook a bit more clever, avoiding many of the cases where the event would be unnecessary.

I've put the incompatibility risk here as medium-high because this slightly changes the semantics of the GetCollisionBoxes event. I don't believe any mods rely on the default Forge behavior, but it's definitely possible.

fasterEntityDataManager

Kind : Speedup

: Speedup Incompatibility Risk: Medium-Low

Every single entity in Minecraft has a "data manager". This handles automatically sending a variety of data to the client, such as the entity's health if it has it, whether or not it's invisible, if it's currently using elytra, if a pig has a saddle, the armor a zombie is wearing, a sheep's color... lots and lots of things.

The problem lies in how Minecraft stores this information. It uses a hashtable. Hashtables are optimized for fast lookups on large amounts of noncontiguous keys, not low memory usage. However, data manager keys are contiguous and there's not a lot of them! This is something much better served by an array, which has fast lookups for contiguous or noncontiguous keys plus low memory usage if keys are contiguous and there's a relatively small amount of them.

So FoamFix goes in and replaces the hashtable with an array. If a mod assumes the backing store of the data manager is a hashtable, it will cause a crash, so I've put the incompatibility risk as medium-low instead of low.

fasterEntityLookup

Kind : Speedup

: Speedup Incompatibility Risk: Very Low

Minecraft stores entities in a way that lets you get them by type, so you can ask for all the Creepers in a world, or just all the living entities. This works great for the latter case, but in the former case it's less efficient than it could be. FoamFix skips a filtering step on the returned list of entities if the entity you asked for is the same kind as the storage used for it.

More simply, if you have a box that contains green-colored cubes, and a box that contains red-colored cubes, and someone asks for a specific shade of green, you'd have to sort through the whole green box and find all the cubes of that color. You at least don't have to sort through all the red cubes as well.

However, there's no point in being so thorough if someone just asks you for green cubes! Minecraft normally is, though, so FoamFix makes it just give up the green box.

fasterPropertyComparisons

Kind : Speedup

: Speedup Incompatibility Risk: Low

Every object in Java has a hash code, which is used to speed up lookups in a hash table. However, calculating a hash can sometimes be slow, depending on what kind of object it is. Java doesn't know whether or not the object can change, though, so it recomputes it every time it needs to know it.

However, block properties are set in stone when the game starts. You can't change the possible values. So FoamFix caches the hash code for these property values, and also makes boolean properties a bit faster.

forceDisable

If enabled, this makes FoamFix Anarchy pretend to be FoamFix Lawful, by disabling the coremod.

optimizedBlockPos

Kind : Theoretical Speedup

: Theoretical Speedup Incompatibility Risk: Low (known incompatible with Sponge, which makes similar changes)

Minecraft has two distinct kinds of blockpos, objects that describe the location of a block in the world. Mutable and immutable.

Most cases use the immutable variant, which means they have to be thrown away immediately and can't be reused. This results in a lot of memory churn and is commonly cited as one of the reasons Minecraft 1.8 and later perform so poorly.

However, some high-traffic code paths use mutable blockposes instead, so they don't have to generate so much garbage. But, mutable blockposes are based on immutable ones, so they have to use their own fields that can be changed. This also means they have their own methods to get the X, Y, and Z coordinates.

The problem lies in the fact the JVM is a JIT (just-in-time) compiler, and it also features optimizations. One of these optimizations is "inlining", where it will take the code from a small method and move it into the class calling it, thus removing the need for the CPU to move to a new portion of memory. But it won't do this if the method isn't final, because it may point to different code based on which class is being referenced. So this optimization is disabled for blockpos, because the JVM can't tell if a blockpos will be mutable or immutable ahead of time.

What this option does is makes mutable and immutable blockposes use the same methods to get the X, Y, and Z coordinates. Thus, theoretically, the JVM may inline them and make the game faster.

replaceWorldSimpleName

Kind : Speedup

: Speedup Incompatibility Risk: Very Low

Every class in Java has a name, such as "java.lang.NullPointerException" or "net.minecraft.client.Minecraft". There's also some convenience methods, such as getSimpleName, which gives you just the last part, like "NullPointerException" or "Minecraft".

However, the simple name isn't cached. It's computed from the full name every time. Minecraft calls getSimpleName for every entity in the world every tick, but for the way it uses it, the full name works fine too, and the full name is cached! So FoamFix goes through and replaces usages of getSimpleName in the entity tick method with getName.

EDIT: It turns out this fix made it into vanilla for 1.12.2!

smallPropertyStorage

Kind : Memory Reduction

: Memory Reduction Incompatibility Risk: Low

Surprise, we're replacing hash tables with arrays again. But this time it's for blocks, not entities.

This option is most infamous for implicating FoamFix in crash reports similar to "Cannot set property <blah> as it does not exist in somemod:block1". These crashes are almost never FoamFix, the exception is from FoamFix emulating vanilla behavior.

experimental

These are extra-fun options that are either risky or just not well tested.

fasterVertexLighter

Kind : Unknown

: Unknown Incompatibility Risk: Unknown

Did you know Forge replaces the vanilla lighting system? It does! It's a bit slower, but for good reason, and if you disable it mods like Chisels & Bits stop working.

This patch is based on work by thecodewarrior and bs2609, and what it does kind of escapes me.

parallelModelBaking

Kind : Speedup

: Speedup Incompatibility Risk: High Medium

This one's fun! Minecraft's model loading system is single-threaded, but it's also fairly self contained (if no mods mess with it), so what would happen if we made it multithreaded!?

This usually works fine in vanilla, but in large modpacks it can fail spectacularly. Never mind the fact that Forge's overhead exceeds FoamFix's optimizations with no mods installed.

EDIT: Hold on, this is enabled by default? And it works in Radon? I seem to remember back when asie was testing this it was really buggy and incompatible. Apparently it works well now! I've bumped down the Incompatibility Risk to Medium.

launchwrapper

This section is for stuff that makes changes to just Mojang's LaunchWrapper, and not the game itself.

weakenResourceCache

Kind : Memory Reduction

: Memory Reduction Incompatibility Risk: Very Low

As established earlier, LaunchWrapper stands in front of the Java class loader so it can modify code at runtime. Forge and a variety of mods make extensive use of this.

However, LaunchWrapper keeps a cache of the raw data of every class, for use by transformers, since they work on raw data. Once all the transformations are finished, this is a waste of memory! The Java garbage collector would take care of this, but LaunchWrapper uses "strong" references (which are the default), so the GC assumes the cache is needed and doesn't touch it.

This option makes FoamFix replace all the strong references with weak references, letting the GC know that these objects can be cleaned up if the cache is the last thing using them.

tweaks

This section is for things that aren't strictly performance patches, but still are within the scope of FoamFix.

disableRedstoneLight

Kind : Tweak

: Tweak Incompatibility Risk: Very Low

If enabled, FoamFix will make all the lit redstone components not emit light.

That's it. That's the whole tweak. Lighting updates are expensive, so this can reduce lag a lot if you have lots of redstone around.

immediateLightingUpdates

Kind : Tweak

: Tweak Incompatibility Risk: Very Low

This basically just makes lighting updates happen immediately, instead of being delayed in preference of stuff like tile entity updates. Since it shuffles around existing code rather than optimizing it, it's considered a tweak.

Well. That's all the features. Man, this is a long post. This took me just over an hour and a half to write, and I'm kind of supposed to be doing work. Hm.

Either way, now you know how FoamFix works. This is the writeup asie had been promising but never got around to, and it will probably also work as a neat reference.

Also, please note I wrote all of this independently, based on my interpretation of the source code. I don't know the intent of the code, just what it does, so my assessment may be inaccurate in places.

Support me

Take me to The Donatatron