The project code was created with help of Artificial Intelligence.
An open source mod with mix of small optimizations for your Minecraft. Less stutters, slightly faster resource loading and world generation, less memory usage.
The main thing Lomka does is reduce the number of redundant garbage (memory) allocations and improves game calcs.
Optimizations
Frame time
- Fewer allocations in the render, tick and collision loops (vertex writes, pose stacks, entity block sweeps)
- Cached font glyph advances (deprecated)
- Cheaper math and color engine
- Faster texture atlas animation (lazily cycle)
- Playing a sound skips the wait for the sound thread
- Removing
Thread.yield()from the client tick loop
Loading and memory
- Bulk NBT array reads for world, player and structure data on the server side (chunks, heightmaps and saves load in blocks instead of per-element stream calls)
- Cheaper resource scanning and cached pack metadata reads
- Generated mip levels releases after upload
- Fewer native buffer reallocations (audio streaming and mesh building)
World and network
- Cached voxel shape boxes and block face-sturdiness
- Faster block and section traversal, heightmapping for world generation, and in-place worldgen RNG reseeds (also, check the incompatibilities list first)
- Leaner network buffers and packet compression (without affecting your connection)
And some other small bugfixes in the game's code.
Measured effect
Isolated microbenchmarks on JDK 25, one operation at a time.
That's not in game frame rates benchmarks:
| Patch | Measured | What you notice it in |
|---|---|---|
| Voxel shape decomposition | toAabbs(): 40 ns vs 8584 ns, 199x+ (14 ns vs 8879 ns on 368 cells) |
Every block an entity touches while moving |
MixinLegacyRandomSource |
next(int): 7.1x |
Every random draw a new chunk needs, on the client and dedicated server. |
MixinBlockPos.betweenClosed |
one 16³: 61 ms -> 11 ms, 5.61x | /fill and other volume edits, the lighting pass after a large build, structure and feature placement, explosion block lists. |
MixinBlockStateBaseCache |
isFaceSturdy: 4.1-5.8x |
Checks such as walking on slabs and stairs, collision resolution between mobs and players, support recomputation when a neighbour changes. |
MixinCompressionDecoder |
setupInflaterInput: 37.5 ns -> 18.1 ns and 128 B -> 0 B per packet |
Every incoming compressed packet |
MixinSoundEngine |
sound acquisition: 400 us -> 20 us, stall per tick 512 us -> 27 us | Client thread stops waiting for the sound thread. |
MixinAutoStorageIndexBuffer |
index buffer grows x4: 1.47-2.9x less time reallocating (85.4 ms -> 29.5 ms, 21 -> 11 reallocations; retains 40 MiB instead of 34) | Geometry spikes (for example, pulling the render distance) |
MixinFallbackResourceManager |
metadata lookup: 5.33x (2.71 ms -> 0.51 ms for 20k files) | Startup and every resource reload (F3+T, still slightly improving) |
MixinMth |
ceillog2 and log2: 3.93 ns -> 0.85 ns (4.6x), 4.83 ns -> 0.84 ns (5.7x) |
Palette sizes, section and buffer counts, every place vanilla asks for a bit length |
Versions
1.21.11 is the main version. It carries the full set of optimizations and gets the most testing.
Modern variants:
| Minecraft | Loaders |
|---|---|
| 1.16.5 | Fabric |
| 1.18.2 | Fabric, Forge |
| 1.19.2 | Fabric, Forge |
| 1.20-1.20.1 | Fabric, Forge |
| 1.21-1.21.1 | Fabric, NeoForge |
| 1.21.4 | Fabric, NeoForge |
| 1.21.6-1.21.8 | Fabric, NeoForge |
| 1.21.9-1.21.10 | Fabric, NeoForge |
| 1.21.11 | Fabric, NeoForge |
| 26.1-26.1.2 | Fabric, NeoForge |
| 26.2 | Fabric, NeoForge |
| 26.3 | Fabric, NeoForge |
Legacy builds cover 1.7.10, 1.8.9 and 1.12.2, on Forge and on Ornithe. These are a separate port with a much smaller patch set and their own patch list. The Forge variants need a mixin runtime installed alongside Lomka (MixinBooter 11.17 on 1.8.9 and 1.12.2, UniMixins on 1.7.10); the Ornithe variants need nothing, because the Ornithe loader ships the mixin runtime itself. See Mixin Configuration (legacy).
❗ Everything outside 1.21.11 is a port of the same or other ideas with a smaller improvement.
FAQ
I see performance drops with Lomka installed
Update to the latest build for your Minecraft version first.
Still bad? Report it (link below) with your game version, loader, mod list, and which config you had turned off.
Does it change vanilla behaviour?
Nope, of course, but if it does weird game changes reply in the issue tracker.
Is it server-side compatible?
Yes, it is.
Does it need any dependencies?
No. The only external requirement is the mixin runtime, and only on the legacy Forge builds (1.7.10-1.12.2). The Ornithe ones ship their own.
Can I add this mod in my modpack?
Sure. Lomka is free and open source. Check out how it goes on your modpack first.
Configuration
Lomka has no in-game GUI. Instead, on first launch it creates config/lomka-mixins.properties with basic mixin configuration.
The full list of mixins with descriptions lives in the wiki:
Mixin Configuration for 1.16.5-26.3.
Mixin Configuration (legacy) for 1.7.10-1.12.2.
Compatibility
This should work fine with most mods (like Sodium, Lithium, Iris, FerriteCore, ModernFix, ImmediatelyFast and even VulkanMod) but no guarantees at all.
Known conflicts are listed with Lomka versions in the incompatibilities list.
Bug reports
Found something that only happens with Lomka?
- Try reproducing it without Lomka first, and with the individual patches disabled.
- Still there? Post it on the issue tracker with an optional log attached.
Please don't report Lomka-related crashes to other mod authors, because Lomka's mixins are cruel that could break unrelated systems.
Credits
Lomka was created by Starlev and contributors.
Also, other mods credits for their's effecient work!
VecDeltaCodeczero-delta path adapted from Collections Of Optimizations by Misanthropy (LGPL-3.0).Vec3iPhiMix hashing credits EfficientHashing by ZZZank (CC0-1.0).- The legacy hardware-blit presentation path references from Tritium (MIT), but popularized in VulkanMod (LGPL-3.0-only).
All Versions
+ Add Version| Name | Game version | Platforms | Published | Downloads | Actions |
|---|---|---|---|---|---|
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 16d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 29d ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 1mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 2mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 3mo ago | 1 | ||||
| 4mo ago | 1 | ||||
| 4mo ago | 1 | ||||
| 4mo ago | 1 | ||||
| 4mo ago | 1 | ||||
| 4mo ago | 1 | ||||
| 4mo ago | 1 | ||||
| 4mo ago | 1 | ||||
| 4mo ago | 1 | ||||
| 4mo ago | 1 | ||||
| 4mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 7mo ago | 1 | ||||
| 8mo ago | 1 | ||||
| 8mo ago | 1 | ||||
| 8mo ago | 1 | ||||
| 8mo ago | 1 |
- New
DiscreteVoxelShapecache: the greedy box decomposition is memoised as cell indices (MixinDiscreteVoxelShape+IDiscreteVoxelShape) and replayed byVoxelShape#forAllBoxes, instead of cloning the whole BitSet and re-decomposing it on every call.VoxelShape#moveallocates a fresh wrapper each time, so a wrapper-keyed cache is cold on every call, while the decomposition itself is a pure function of the shape bits - boxes are recorded through vanilla's ownIntLineConsumer, so the order is whatever upstream produces. Shapes whose decomposition is too fragmented to retain (>32 boxes) answer with the empty sentinel and keep vanilla's path. This is the per-touched-block pathEntity.movewalks. (all versions) **NEW** - New
ModelPart#rotateByoverwrite: reuses two scratchMatrix3fand one scratchVector3f(thread-confined, same convention asMixinPose) instead of allocating per call. The Euler -> matrix -> Euler round trip is unchanged and bit-identical over 200k random rotations. Call sites are narrow (end crystals, player capes), so this is an allocation reduction, not a frame-time change. (1.21.4+) **NEW** - New
DisplayRendererredirect: the billboarding quaternion comes from a reusable scratch instead of a freshQuaternionfper display entity per frame. Every branch ofcalculateOrientationassigns rather than accumulates and the result is only read, so a shared instance is sound. (>=1.20) **NEW** - New
BlockGetter#forEachBlockIntersectedBetweenoverwrite plus theBlockStepSweephelper: replaces the per-cellIterable+AbstractIterator+MutableBlockPostrio and theOptional<Vec3>box clip of the DDA sweep with one reused cursor and a primitive clip returningt. Vanilla's arithmetic, iteration order, deduplication and abort semantics are reproduced exactly - this runs on every entity move viaEntity#checkInsideBlocks, and onGhast#tick. (>=1.21.9) **NEW** SoundEnginehandle pool now replenishes on hits as well as misses, gated on a live per-pool size counter and topping up below half the cap. A miss costs a fullcreateHandle().join()- exactly what vanilla costs - so a miss-only pool can improve the median but never the tail: its hit rate settled at 50% (30% on 32-sound bursts) and p99 was vanilla's. Topping up on every call reaches a 100% hit rate. The win holds while a tick plays at most about cap/2 sounds and decays to vanilla beyond that; it is never worse. (all versions) **REWORK**PalettedContainer's uniform-section cache moved into one immutableUniformPaletteCache(Data, value)holder behind a single volatile reference, so the hot path is still one volatile load, one identity check and one plain load. (all versions) **REWORK**- New
BlockModelLighterstate-read memo: while a section's immutable render region is meshed, the repeatedBlockAndTintGetter#getBlockStatereads of ambient occlusion are answered from a 512-slot open-addressed table keyed on the packed block position - one block's six faces read 90 states over 19 distinct positions. The window belongs to the constructing thread and is opened/closed with vanilla's ownenableCaching/clearCache, so a region change, a level change or a caller that forgets to close all invalidate it; collision-shape queries themselves stay vanilla, since ordinary states already cache their result. (>=26.1) **NEW** - New
SpriteContentsmip-level release (MixinSpriteContents+MixinTextureAtlasMip, bridged byISpriteContents): once the atlas has finished uploading, every non-animated sprite frees its generated mip levels above level 0 and truncates the chain, so they are not held for the lifetime of the pack. Level 0 is kept deliberately - it is whatisTransparent(and therefore item model generation and ambient occlusion) reads, what a debug atlas dump reads, and what Axiom'sBlockColourComputationholds;NativeImage#closeisnmemFree, so closing it would be a native use-after-free. The hook isTextureAtlas#uploadRETURN, notSpriteContents#uploadFirstFrame, which from 26.1 runs once per mip level inside the generation loop. Sodium and Iris already inject there, so coexistence is proven rather than assumed. (>=1.20) **NEW** - New
BlockableEventLoop#doRunTaskredirect: theSharedConstants.IS_RUNNING_IN_IDEargument Tracy'sbeginZone("Task", ...)hangs on is folded to a constant, so no profiler zone is opened per executed task. Costs the Tracy per-task breakdown from this site - a small win, recorded so the trade is visible. (>=1.21.4) **NEW** - New
WeightedList$Compact#getoverwrite: immutable cumulative weights and a branchless binary search replace the O(n) weight-subtraction scan - but only where it pays. The prefix table is built from 64 entries up, and only when a weighted draw would inspect at least 32 entries on average, so small and front-skewed selectors keep the vanilla scan, as do out-of-range selections, which still reproduce vanilla's overflow behaviour and exception text. (>=1.21.11) **NEW** - New
UberGpuBuffer#uploadStagedAllocationsredirect: the staged-allocation walk uses fastutil's reuse-modefastIterator()instead ofObjectSet#iterator, so the per-entry object is recycled rather than allocated per iteration. Starts after the preceding key iterator, which must still free the previous iteration's reuse objects. (>=26.2) **NEW** DynamicUniformStorage#writeUniform/DynamicGpuDataStorageMapped#writeData: the slow path builds itsGpuBufferSlicedirectly instead of probing the one-slot cache - that probe could only ever miss, since the cached slice belongs to the previous block - and on 26.2+ the returned slice is the mapping's own rather than a second identical record. The fresh slice is still stored, which is what primes the next fast-path hit. (>=1.21.6) **REWORK**InputConstants.Key#hashCodeoverwrite: the two-elementObjects.hashresult is computed directly instead of allocating the temporaryObject[]and boxing the key code, on every keybinding lookup. Null handling and the integer overflow of the original are preserved. This patch shipped before 0.5.0 and was dropped there as low-value; it is back and now widened down to every variant, 1.16.5 included. (all versions) **NEW from OLD**SectionPos.betweenClosedStream'sforEachRemainingsteps the cursor directly instead of astartedflag plus a separate sequential loop. (all versions)MixinBufferBuilderpriority raised to 2000 so ourbeginElementstore runs ahead of foreign patches on the same target.- Enabled JOML's
sinLookup(11-bit table) alongsidejoml.fastmath, both still overridable with-D.
### Bug Fixes
VertexConsumer.addVertex/addVertexWith2DPose/setNormal/putBulkData/putBakedQuadwere **not** transcriptions of the JOML operation vanilla performs, in two separate ways. The first computedwand divided by it wheneverw != 1, silently reimplementing perspective projection that vanilla never does. The second built the same sum from nestedjava.lang.Math.fma; JOML writes the identical expression throughorg.joml.Math.fma, and in every JOML release Minecraft shipsorg.joml.Runtime.HAS_Math_fmais false, so each of those is a plaina * b + c- the replica was off by up to one ulp on most vertices while looking like a faithful copy. Both the association (m00 * x + (m10 * y + (m20 * z + m30))for 3D, left-to-right for 2D) andnormalize()'s nested length-squared are now reproduced exactly, andputBulkData+putBakedQuadgained the bit-comparison coverage they were already claiming. (1.21+)- Fixed
ARGB.scaleRGBcorrupting out-of-range divisors: vanilla clamps the divisor to 0..255 before dividing, but the reciprocal form was applied to the raw value, so scaling white byInteger.MAX_VALUEoverflowed the long product past2^24 * Integer.MAX_VALUE, wrapped negative and clamped to black. The reciprocal is now used inside vanilla's domain and the clamp is reproduced directly outside it, where no division is needed at all. - Fixed the
Camerafluid-in-camera cache being keyed on camera position alone, which is unsound: a stationary camera keptFogType.WATERafter the water below it was removed and skipped the water query entirely once the cache heldFogType.NONE. The caches are now dropped at the head of everysetup(), which the game calls exactly once per rendered frame, so the memo is frame-scoped and a block, fluid, FOV or aspect change is always picked up. - Fixed
BitSetDiscreteVoxelShape.joinwriting contiguous runs withBitSet.set(from, to). That overload edits the backing long words directly and never routes through the single-bitset(int), so aBitSetsubclass that keeps derived state in an overriddenset(int)desynchronises - Palladium swaps this storage for aFastBitSetwhose reader mirror onlyset(int)/clear(int)maintain, which silently emptied every shape built throughShapes.join(bed shapes, stairs, slabs) while its bounds stayed correct. The bulk write is now enabled only for a stockjava.util.BitSet. - Fixed the
PalettedContaineruniform cache publishing a mismatched pair: with the value in a plain field next to a volatileDatafield, two concurrent fillers can interleave and leave one section'sDatapaired with another section's value, handing back a wrong block. One immutable holder removes the interleaving via the JMM final-field guarantee. - Fixed
AxisPoseRotate's axis fast path assuming a unit quaternion.Matrix4f.rotate(Quaternionfc)composes the rotation with a uniform scale of the norm squared, so a non-unit quaternion the axis path skipped left a different diagonal than JOML; anything that is not unit is now handed to JOML unchanged. - Fixed
Mth.ceillog2,Mth.log2andMth.smallestEncompassingPowerOfTwodisagreeing with vanilla onInteger.MIN_VALUE. Vanilla'sisPowerOfTwoaccepts it, soceillog2andlog2answer 31 there while the leading-zero body answered 0 and -1, and the smallest-power-of-two guard disagreed on the sign of its result. - Fixed
BlockStateBase#getCollisionShapeserving the cached per-state shape to *entity* collision. The cache is filled once from(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()), but the three-argument overload that entity collision uses delegates togetBlockShapeunconditionally - so any block whose collision shape varies per entity got the wrong one. Twenty-one Create blocks overridegetCollisionShapewithout declaringhasDynamicShape, so the cache existed for all of them;CrushingWheelControllerBlockin particular returns an empty shape for the entity it is crushing, so with a solid controller the item could never be shoved past the intake threshold and the wheel spun and crushed nothing. The cache is now served only for contexts that carry no entity, which is exact on every version: the empty sentinels all have a null entity, and the 26.2+PositionCollisionContextdoes not extendEntityCollisionContextat all, so a position-sensitive shape lands on the dispatch path anyway. - Fixed
Heightmap#primeHeightmapscarrying an unmatched pending-type mask between columns. The mask was initialised once for the whole chunk, so a water-only column that matched nothing left its bits set, and a later column could overwrite a height after vanilla's partial iterator rewind. Each column now starts from a fresh mask. - Forge/NeoForge manifests serialise the Minecraft range through one
mcRange(ctx)helper. The previous inlinewhenemitted an open[X,)wheneverdeps.minecraft_maxwas absent or equal to the compiled version - true for *every* variant, since each block setsdeps.minecraftto the version it builds - so the bounded branch was dead code and1.21.4-neoforge,26.2-neoforgeand26.3-neoforgeshipped unbounded, disagreeing with their own Fabric counterparts and letting an older patch set load on a version they were never built for. MixinFallbackResourceManager.listResourcesis a cancellable HEAD@Injectagain rather than an@Overwrite. An overwrite deletes the targetMethodNode, which breaks any foreign MixinExtras v2 injector anchored to it - owo'sFallbackResourceManagerMixinuses@WrapWithConditionon thePackResources.listResourcesINVOKE, which died withInvalidInjectionExceptionduringMultiPackResourceManager.<init>, i.e. a boot crash. The price is oneCallbackInfoReturnableper call, which is a data-reload path. (>=1.19.3)MixinChannel.setSelfPositionis a cancellable HEAD@Injectat default priority instead of apriority = 500@Overwrite, for the same reason.- Fixed comment and javadoc accuracy across the touched mixins (
Mth.hsvToArgbno longer claims to skip the modulo,AutoStorageIndexBufferwarmup counts indices rather than quads,ResourceEntryfield alignment).
### Removed
- Removed three
LightCoordsUtiloverwrites (max,lightCoordsWithEmission,smoothWeightedBlend, 26.1+): the equality fast path and the nibble-max packing only duplicated what vanilla already does in three branches, and the "blend" replaced vanilla's per-channellerpcalls with a weighted accumulation, so the saved call overhead was not worth re-deriving the interpolation by hand. - Removed
MixinGlStateManager,MixinWindowandutils/cache/GlStateCache. Sodium already owns the vertex upload path these deduped, and the cache is an unverifiable claim about driver state: Accelerated Rendering, ImmediatelyFast and VulkanMod all mutate scissor/viewport without telling it, and Iris has its own cancellable HEAD injection on_viewportthat can cancel before our cache sees the write. Every such case leaves the wrong rect or mode live. The<1.21.2FBO read/draw mirror backport that shared the file went with it. (all versions) - Removed
MixinVertexBuffer: Sodium already overwritesModelPart$Cube#compilewith an equally fast body, so ours was dead code under any popular rendering mod while adding conflict surface. - Removed the
Util.growByHalfoverwrite (>=1.21.6): three branches around aMath.min/Math.maxpair that HotSpot compiles better than the hand-rolled form.
### Changed
- New supported versions: **1.16.5** (Fabric) and **1.18.2** (Fabric + Forge). Both carry a real, reduced patch set rather than an empty config - 24 and 29 patches - and are the reason for most of the
//?gates listed below. 1.16.5 + Forge has no working toolchain:legacyforgedrives NeoForm Runtime, which refuses every pre-1.17 MCP config outright, so thematchline stays commented out and the variant is left out ofstonecutter.properties.tomlentirely — declared, it would be a variantpublish.pywaits for a jar that can never be built. - The legacy Forge / Ornithe builds (1.7.10, 1.8.9, 1.12.2) moved into the repository as two source trees,
src-legacy/for Forge andsrc-ornithe/for Ornithe, plus a separatelegacy/Gradle build driving six per-version sub-builds, and are tracked in [CHANGELOG_LEGACY.md](CHANGELOG_LEGACY.md) - they must not interleave with this file. - Renamed the
Transformation#inversemixin back fromMixinTransformationInversetoMixinTransformation(same target, same overwrite). The class name is its runtime config key, so an existingcom.mojang.math.MixinTransformationentry works again. - Mixin config gating tightened to what each patch actually needs:
MixinModelmoved from>=1.21.6to>=1.21.4,MixinBuilderfrom>=1.21.11to>=1.21.6,MixinPoseStackfrom<26.3to>=1.19 && <=26.2,MixinVertexFormatElement/MixinMultiple/MixinCameragained>=1.19floors, andMixinBlockStateBase+accessor.InvokerBlockBehaviourmoved into the<1.19block at>=1.18.MixinAutoStorageIndexBuffernow sits in the>=1.18block, which also serves 1.19+ - the nested class appears in 1.18.2, so the gate is the class, not the era, and a second>=1.19listing would duplicate the entry on every modern variant. - The
pluginkey is now omitted below 1.17: 1.16.5 is a Java 8 target shipping Gson 2.8.0, and the modernLomkaMixinPluginneeds a text block,varandJsonParser.parseReader. The legacy tree ships its own Java 8 + Log4j + no-Gson plugin instead, so the runtime toggle is not lost there. - Build outputs now stay clean: dev jars are deleted from
build/libsas soon as the shippable artifact exists, skipped on the moddev-based loaders where the pre-reobf jar is a build input (and whereRemapJar.inputis wired as a plainFile, so deleting it fails the build outright). Gradle heap raised 2G -> 3G. - Source comments no longer cite the benchmark harness or the vendored competitor trees. Figures rot with every JDK change and are invisible to players reading the code, and the whole
scripts/tree is gitignored, so such a path resolves for the author and for nobody else. - README no longer advertises an FPS percentage, and the sound/attribution lines were reworded.
- 6** on 1.7.10 Ornithe, **10** on 1.8.9, **11** on 1.12.2.
### New
- Forge support for **1.7.10**, **1.8.9** and **1.12.2**, in a separate tree with its own Gradle builds.
so a patch is version-gated by a
//$ if >=1.12 marker on line 1, processed by gradle/preprocess.gradle- the same directive syntax the modern
json5config uses. Each build's mixin config is **generated**
config.
- Ornithe support for the same three versions**, alongside the Forge ones:
lomka-<version>-ornithe+<mc>.jar. It is a second source tree rather than a loader switch, becauseFeather - the mapping set Ornithe compiles against - names classes the way 1.16+ does (
Direction,Box, NbtIntArray, Identifier), a different namespace from the MCP names the Forge tree has touse. The two trees share only the pure-math kernels; every
@Mixin shell is per-namespace.One toolchain covers all three (
fabric-loom + Ploceus), where Forge needs three: ForgeGradle 1.2on Gradle 6.9.1 for 1.7.10, ForgeGradle 2.1.6 on Gradle 2.14.1 for 1.8.9 (the only line that serves
1.8.9
userdev), and RetroFuturaGradle 2.0.6 on Gradle 9 for 1.12.2 - and RetroFuturaGradlesupports 1.7.10 and 1.12.2 only, never 1.8.9.
- No external mixin runtime on Ornithe.** The Ornithe loader ships sponge-mixin and
mcmod.info dependency block to get the modid:range format right, and nothing to ask players toinstall. Access widening is an access widener on all three versions, where 1.12.2 Forge has none.
The Ornithe entrypoint is OSL's
net.ornithemc.osl.entrypoints.api.ModInitializer - the Fabric APIclasses do not exist on these Minecraft versions - and the same runtime toggle and
//$ if gatingrun through the same preprocessor.
- The legacy Forge build directories are now
legacy/legacy-forge-<mc>rather than
legacy/legacy-<mc>, so they read as a pair with legacy/legacy-ornithe-<mc>.- Runtime per-mixin toggle (
config/lomka-mixins.properties) through a legacy-specific
LomkaMixinPlugin: Log4j instead of SLF4J (which only arrives in 1.17), no Gson (parseString needs2.8.6, 1.12.2 ships 2.8.0), and no JOML system properties (these versions use Mojang math).
- Mixin runtime is an external mod, never shaded: **MixinBooter 11.17** on 1.8.9 and 1.12.2,
- UniMixins 1.7.10-0.3.1** on 1.7.10. Registration is the
MixinConfigsmanifest attribute, so there
IFMLLoadingPlugin class. 1.12.2 ships lomka.refmap.json; 1.7.10 and 1.8.9 remap inthe reobf step and need no refmap.
- Per-version
mcmod.info, withlogoFileas a barelogo.pngin the jar root - 1.19.2's
AbstractPackResources#getRootResource(String) throws on any path containing /, and the same ruleapplies here. The
credits field is gone: the ports it named are not in this tree, and Forge shows itonly on the mod list entry.
- 1.8.9 and 1.12.2 declare their mixin runtime to the loader itself,
mixinbooter:[11.17,). FML parses
mcmod.info dependencies as modid:range with a lowercase id, so the display-name form is a loaderror. 1.7.10 declares nothing: UniMixins ships no
mcmod.info, so there is no id for FML to resolveand declaring one aborts the load. The 1.7.10 requirement is a platform-level one instead.
- An access transformer is shipped and declared (
FMLAT: accesstransformer.cfg) but has no entries yet:
when an
@Overwrite body calls a private method, or when an @Invoker/@Accessor is added.### Performance
MathHelper.smallestEncompassingPowerOfTwo/log2: the five-step bit spread and the De Bruijn table
Integer.numberOfLeadingZeros intrinsic, reproducing vanilla's edgecases exactly (
0 for n <= 0 on the first, -1 on the second). (all versions)MixinMinecraft: the per-frameThread.yield()inrunGameLoopis redirected away. It is a scheduler
AbstractResourcePack:pack.mcmetais parsed once per pack instead of on every metadata query - three
locationToName loses its String.format. Loading path,not frame path. (all versions)
AABB#calculateIntercepton 1.12.2, and the equivalentEnumFacing/int-returning body on 1.7.10 and
Vec3d per plane it tests, up to six per ray trace. The candidate pointis kept in locals and the planes are tested directly. (all versions)
EnumFacing#rotateAroundand its counter-clockwise sibling: flat lookup tables instead of nested enum
getOpposite and random are deliberately left alone - PolyPatcher already overwrites bothon 1.12.2 with byte-identical bodies. (1.8.9, 1.12.2)
Vec3i#hashCode: phi-mix (fromefficient_hashingby ZZZank, CC0) instead of vanilla's weak
(y + z * 31) * 31 + x, which collapses a large share of a dense 64^3 region onto few distinct values.1.12.2 carries the identical degree-2 polynomial, so the body is a transcription. (1.8.9, 1.12.2)
NBTTagIntArray/NBTTagLongArraybulkload: 8 KB blocks through a big-endianByteBufferinstead
read() calls per element. Taken only for a DataInputStream, i.e. the disk path (regionfiles, player data,
level.dat); Heightmaps long arrays are where it pays on a server. (1.8.9 intarrays, 1.12.2 both)
NettyCompressionEncoder/NettyCompressionDecoder: the per-packetnew byte[readableBytes]is
FallbackResourceManager: the.mcmetalocation is built only once a pack actually has metadata,
ResourceLocation per lookup before any pack is consulted. (1.12.2)Framebufferpresent: the fullscreen blit quad and its half-dozen state toggles are replaced with one
glBlitFramebuffer(NEAREST). (1.8.9, 1.12.2)- On Ornithe the same patches carry Feather names:
RenderTarget#draw(int, int, boolean)is the
Box#clip the ray trace, FallbackResourceManager unchanged. RenderTarget andDirection#clockwise(Axis) also ship on Ornithe, where the rotation is a flat lookup tableanchored on the three private
clockwiseX/Y/Z helpers rather than on clockwise itself.(Ornithe, 1.8.9 and 1.12.2)
Vec3i#hashCodephi-mix also runs on Ornithe on all three versions, atpriority = 999so a
### Notes
MixinDirectionand the OrnitheMixinMathHelperare cancellable HEAD@Injects rather than
@Overwrites. On those classes @Overwrite cannot resolve a target at all - Direction failsbecause it is first loaded *through*
Direction$Axis, MathHelper fails on 1.7.10 - while theidentical bodies expressed as injectors apply. The cost is one
CallbackInfoReturnable per call.- New
IntArrayTagandLongArrayTagmixins, where vanilla reads these arrays element by element, so every int costs four and every long eightread()calls on aDataInputStream. The array is now read in 8 KB blocks through a big-endianByteBuffer. The bulk path only triggers onDataInputStreaminputs, so in practice it accelerates server-side disk reads (chunk NBT with itsHeightmapslong arrays, player data,level.dat, saved data, structure templates). **NEW** - New
Vec3#normalizemixin: replaces three divisions with one reciprocal and three multiplications. Results may differ from vanilla by up to 1 ULP. (1.21.4+) **NEW** - New
ARGB#multiplyoverwrite that folds the four per-channel/255integer divisions into exact reciprocal multiplies, which runs on every entity's model tint each frame. Results are bit-identical to vanilla. (all versions) **NEW** - New
MixinSoundEnginechannel-handle pool. Playing a sound no longer makes the client threadjoin()a hand-off to the sound executor, so a sound plays as soon as its handle is already acquired. (all versions) **NEW**
### Bug Fixes
- Register common optimizations under the standard
mixinskey so they actually run. The key was spelledmain, which Mixin does not read, so every common patch (Mth,ARGB,BlockPos,Direction,AABB,PalettedContainer,Heightmap, NBT tags, ...) **was silently dropped in every release from 0.3.0 to 0.5.5**. Only theclientsection was live, so the mod has been running at roughly half its intended patch set for its entire published history. **CRITICAL** - Fixed the 1.19.2 manifests advertising Minecraft up to
1.19.4, which the 1.19.2 artifacts cannot run on: the 1.19.3 JOML transition is binary-incompatible. Both loaders now declare[1.19,1.19.2], and the range generator collapses to the single-element form when the minimum equals the maximum, because Forge's version parser rejects a range with identical boundaries. - Fixed a crash of the Forge mod list on 1.19.2, where the generated manifest pointed
logoFileatassets/lomka/icon.png, and 1.19.2 feeds that value toAbstractPackResources#getRootResource(String), which throwsIllegalArgumentExceptionon any name containing/, so clicking the mod in the mod list always crashed. The Forge manifest now uses a barelogo.pngat the jar root, which both 1.19.2 and 1.20.1 resolve. - Fixed missing mixin visibility and 26.3 API drift that aborted the launch on several versions (
MixinBitSetDiscreteVoxelShape,MixinPalettedContainer,MixinArrayListDeque,MixinUtil,MixinDirection,MixinFriendlyByteBuf). - Fixed
MixinTextureAtlason 1.21.6-1.21.10: the texture field is inherited fromAbstractTexture, so@Shadowcould not resolve it and aborted the launch. A newaccessor.AbstractTextureAccessorbridge reads it through the superclass. (1.21.6-1.21.10) - Fixed
MixinCompressionDecoderdropping the connection:ByteBuf#internalNioBufferis absent fromAbstractByteBufand fromAbstractUnpooledSlicedByteBuf, and the only available guard isnioBufferCount(), so a refused buffer would throw inside the packet decoder. The call is now wrapped in a non-throwingtry/catchthat falls back tonioBuffer. (1.21+) - Fixed a Forge/NeoForge-only
IllegalClassLoadError: helper types nested inside mixins produce classes in thelomka.starl.mixins.*package, which ModLauncher's Mixin plugin refuses to load. - Fix version-specific NBT overwrite signatures and returnable PalettedContainer callbacks.
- Restore vanilla defensive translation copies to preserve display interpolation.
- Restrict the 1.19.2 artifacts to Minecraft 1.19.2; later versions use incompatible math APIs.
- Hardened control-character escaping in generated Fabric JSON and Forge/NeoForge TOML metadata.
### Removed
- Removed
MixinStringRenderOutputand its one-slotFontSetmemo. (1.19.2-1.21.8) - Removed
MixinLevel,MixinBlockableEventLoop,MixinClientLeveland the Vulkan buffersMixinDirectpatch. - Removed the reentrant
FriendlyByteBuf.writeMaphelper and unsafeUtil/Mthoverwrites. - Removed
MixinBlockStateBaseandMixinMultiPackResourceManageron 1.19.2. - Removed
MixinVanillaPackResourceson 1.19.2 and 26.3. - Removed the
Direction#getApproximateNeareston 1.21.4+ overwrite: Sodium (Fabric) and Embeddium (Forge/NeoForge) already replace the vanilla dot-product loop with the same three-abs-compare chain. Our version was bit-exact where theirs is not (NaN / $\pm\infty$ / lone-Float.MIN_VALUEinputs), but paid for that exactness with extra ALU ops on the finite path. - Removed
MixinLeveledPriorityQueue: it swapped the priority-queue sets for fastutilSpatialLongSet, whose 4x4x4 outer key packing only holds for light positions, but the same queue backsDynamicGraphMinFixedPointwithChunkPoskeys. Distinct chunks collapsed into one bucket, the distance-update fixpoint never converged, and world generation or saving hung with the server thread at 100% CPU inDistanceManager.runAllUpdates. All versions affected.
### Changed
- Reorganized the shared helper packages:
DirectionTablesmoved toutils/constants/andResourceEntrytoutils/pack/, next toGlStateCacheinutils/cache/, replacing the catch-allutils/misc/. Internal only, no behavior change. (all versions) MixinPalettedContainercaches the uniform-section value in avolatile Datareference plus a plain value field, published by the volatile store, and invalidates it inread/onResize. Theset/getAndSet/getAndSetUncheckedhooks were dropped as provably redundant, since zero-bit storage validates writes as no-ops.MixinBuilderrestores the 26.1+addAllculled-face transfer.MixinPoseuses thread-local scratch matrices.- Renamed the
Transformation#inversemixin class fromMixinTransformationtoMixinTransformationInverse(same target, same overwrite). This is also its runtime config key, so an existingcom.mojang.math.MixinTransformationentry is now reported as an unknown key - rename it tocom.mojang.math.MixinTransformationInverse. - Expanded generated Fabric, Forge, and NeoForge metadata with CurseForge and attribution credits; Forge/NeoForge now also expose the Modrinth page and issue tracker, while Fabric keeps its Modrinth homepage.
- Updated README attribution and runtime mixin-configuration documentation for the added and removed patches.
- New KeyframeAnimation mixin: scratch
Vector3freused per animation instead ofnew Vector3f()perapply()(24 B/frame, measured), indexed entry walk instead of iterator, exact zero-scale early-out. (1.21.6+) **NEW** - New Entry bridge mixin:
IKeyframeAnimationEntryduck to the privateKeyframeAnimation$Entryrecord so the caller scratch vector flows into vanillaapply()search/interpolation stay vanilla. (1.21.6+) **NEW** - New Heightmap mixin:
primeHeightmapsrewritten without the per-YChunkAccess#getBlockStatewalk flatHeightmap[]/Predicate[]pair, int bitmask for pending types, section-local scans,O(1)skip of null/hasOnlyAirsections and one predicate test per uniform section. Bit-identical, no per-column alloc. (all versions) **NEW** - New PalettedContainer probe:
lomka$uniformValue()answers the Heightmap uniform-section check via the same(value, volatile Data)pair as the hotget()path, heterogeneous containers return null and are never cached. (all versions) **NEW** - New XoroshiroRandomSource mixin:
setSeedreseeds the liveXoroshiro128PlusPlusin place (seedLo/seedHiwrites +gaussianSource.reset()) instead ofnew Xoroshiro128PlusPlus(...)persetFeatureSeed/setDecorationSeedone generator alloc saved per structure/feature per chunk, sequence bit-identical. (all versions) **NEW** - New StringRenderOutput mixin: one-slot
FontSetmemo per laid-out string consecutive codepoints of a styled run skip the font-map walk, identity hit withequalsfallback. Ported from Collections Of Optimizations by Misanthropy (LGPL v3). (1.19.2-1.21.8) **NEW** - New VecDeltaCodec mixin: zero-delta fast path returns
Vec3.ZEROinstead ofsubtract+ newVec3one alloc saved per unchanged tracker delta. Ported from Collections Of Optimizations by Misanthropy (LGPLv3, with authority), priority 999 for CoO compat. (all versions) **NEW** - PoseStack on 1.19.2 (Mojang math): per-stack scratch
Matrix4f/Matrix3freuse instead of vanilla per-call allocations inmulPose(Quaternion); bit-identical. (1.19.2) **NEW** - Font mixins (
MixinFont,MixinGlyphSource,MixinFontSet,MixinSource) widened down to 1.21.9+ (were 1.21.11+). (1.21.9+) **NEW from OLD** - GlStateCache rework:
int[] STATE+get/setreplaced with plain public static fields; viewport/scissor/polygon compare via single XOR-OR, FBO read/write via direct field compare no array bounds checks / interface dispatch on the dedup path. (all versions) **REWORK**
### Bug Fixes
- Util
sanitizeNameparity fix: now runstoLowerCase(ROOT)on the whole string first (handles length-changing lowercases like U+0130) and only then patches rejected chars previous version lowercased per-char. Zero-alloc fast path for all-valid input kept. - Util
offsetByCodepointsbounds fix: out-of-range single steps returniunchanged instead of clamping tolen/0.
### Removed
- Removed low-value/parity-risky Util overwrites:
getPlatform/isAarch64OS-arch cache,makeDescriptionId,findNextInIterable/findPreviousInIterable,parseAndValidateUntrustedUri. - Removed
wiki/Performance-Presets.mdand its README/wiki links (stale RAM/FPS preset blocks). - Renamed
IFrameBufferCache->IFrameBufferCacheKey.
### Changed
- New supported version: **1.19.2** (Fabric + Forge)
stonecutter.properties.toml,settings.gradle.kts,deps.minecraft_min/maxranges,[20,)Forge range for<1.21, JOML 1.10.5 shim,MixinCamera/MixinClientLevel/MixinFriendlyByteBuf/MixinPoseStackversion branches, AT/CT gating.
### Performance
- VertexConsumer
putBulkData/putBakedQuad: pose-matrix loads hoisted out of the per-vertex loop and the 4-vertex loop fully unrolled - no per-vertex interface dispatch, friendlier to auto-vectorization. (1.21.11+ / 26.1+) - FriendlyByteBuf
writeMap: cached reusableBiConsumerkeepsHashMap.forEachinternal iteration (measured faster than an entry-set loop) with zero per-packet lambda allocations;writeEnumSet/readEnumSetrewritten as sparse bit packing (O(set size)instead ofO(enum length)), noBitSettemporaries. (all versions) - FallbackResourceManager
listResources: derived base->meta index built after pack filtering - zero-alloc metadata attach per file with exact vanilla filter semantics; logger handle cached instead of a registry lookup per call. (all versions)
### Removed
- Removed MixinDataLayer and MixinSoundEngine (rare scenarios).
- Removed Frustum
offsetToFullyIncludeCameraCube(branchless int casts measured slower thanMath.floor/ceilintrinsics). - Removed SectionPos
asLong/x/blockToSectionoverwrites.
### Changed
- ChunkStatus status-list cache is now published via
volatile- safe for worldgen worker threads at no steady-state cost. (1.21+) - Bench harness: standalone
scripts/benchrunners (world / core-util / packs / client) with correctness fuzz; source comments no longer carry benchmark figures perRULES.mdsection 4.
### Bug Fixes
- Mod dev version -> Normal mod version
### Performance
- New Level mixin:
ThreadLocalscratch forgetEntities(Entity,AABB,Predicate)— hottest entity query (all) **NEW** - New PalettedContainer mixin: uniform-section fast path via
volatile Dataidentity (all) **NEW** - New FrameBufferCache mixin: zero-alloc probe
CacheKeyforgetFbo(26.3) **NEW** - DynamicGpuDataStorageMapped mixin:
renderpearlsuccessor ofDynamicUniformStorage(26.3)
### Bug Fixes
- Refmaps fix for 1.20.1 Forge
- Lightmap profiler/pipeline +
RenderStateExtractorvectors - 26.3
ResourceManager.Selectorcompat for pack managers, WindowonResizesignature
### Changed
- 26.3
renderpearlreplacements (GpuBuffer,GlProgram, etc.) — 19 variants,PoseStack/GlProgramgated<26.3,neoforge-moddev2.0.147
- Refmaps fix for 1.20.1 Forge.
- VoxelShape.toAabbs is computed once per shape and cached —
clip()no longer allocates an ArrayList + AABBs on every raycast miss (crosshair raycast runs every frame). (all versions) **NEW** - BitSetDiscreteVoxelShape.join accumulates contiguous bit runs and flushes them with bulk
BitSet.set(from, to)word writes instead of oneset()per voxel. (all versions) - New AABB mixin: allocation-free
clippaths — nodouble[1]box, no per-iterationAABB.movein theIterablevariant, direct face tests. Keeps Lithium's AABB mixin intact. (all versions) **NEW** - New BlockPos mixin:
betweenClosedreplaces2 div + 2 modper block with incremental stepped counters, preserving the vanilla X→Y→Z order. (all versions) **NEW** - AutoStorageIndexBuffer warmup lands the first allocation at 16384 indices (~32 KB SHORT) with the x4 growth no early mid-frame regen hitches. (all versions) **NEW**
- New Vec3i mixin: exhaustive switch for
get(Axis), int-exactdistManhattan, phi-mixhashCode(fromefficient_hashingby ZZZank, CC0). (all versions) **NEW** - New LegacyRandomSource mixin: plain-field LCG draws instead of volatile + CAS per call; the vanilla seed-install guard stays, worldgen parity is bit-for-bit. (all versions) **NEW**
- New Util mixin: cached OS/arch detection, array-backed shuffles, allocation-free URI scheme checks, single-step
offsetByCodepoints, shared predicate constants. (all versions) **NEW** - New Direction mixin:
getClockWise/getCounterClockWisevia a flat 1D LUT instead of nested enum-switch dispatch; branch-freegetApproximateNearest. (all versions, nearest optimization one: 1.21.4+) **NEW** - New ByteBufferBuilder mixin: inlined
reservefast path, max growth step raised from 2 MB to 8 MB — no repeated native reallocations on chunk/batch surges. (1.21+) **NEW** - BufferBuilder per-vertex writes (
setColor(IIII),setUv(FF),uvShort) collapsed to single int/long stores; the packed-color path stays on BSWAP + ROR. (1.21+) **NEW** - VertexConsumer transforms rewritten with per-era coverage: scalar fma
addVertex(1.21+), 2DaddVertexWith2DPose(1.21.6+),setNormalvia the widenedtrustedNormalsfield (1.21-1.21.11),putBulkDatawithout bridge arrays (1.21.11+), fmaputBakedQuad(26.1+). **NEW** - Mth:
smallestEncompassingPowerOfTwovia a single LZCNT instruction, fmalerp(float/double) with single rounding — ported from SuperFastMath (elias, permission granted). (all versions) **NEW** - GlStateManager state dedup (viewport/scissor/polygonMode, cancellable HEAD injections) extended to all versions; GlStateCache now tracks bound read/write FBOs and dedups
_glBindFramebufferback to 1.20.1, replicating vanilla's own 1.21.2+ mirror logic. (all versions) **NEW from OLD** - Pose scratch-matrix optimization covers the outer
PoseStack.mulPose(Matrix4f)era (1.21-1.21.4) too, unified in MixinPose. (1.21+, not 26.x) **NEW from OLD** - Std140Builder / Std140SizeCalculator: blanket per-
put*overwrites replaced with a single power-of-twoalign()fast path, vanilla fallback for exotic alignments. (1.21.6+) - BitSetDiscreteVoxelShape.join accumulates contiguous bit runs and flushes them with bulk
BitSet.set(from, to)word writes instead of oneset()per voxel (all versions).
### Bug Fixes
- Supplier fix in VulkanGpuBuffer the
<init>handler'slabelparameter is typedSupplier<String>now, fixingInvalidInjectionExceptionat apply time. - MixinClientLevel causes crash.
- Fixed
betweenClosedStreamint overflow in the spliterator estimate and duplicate emission after a partialtryAdvance(SectionPos). FallbackResourceManagerkeeps thewrapForDebugwrapper when debug logging is on vanillaLeakedResourceWarningparity.- VanillaPackResources resolves root identifiers (empty path) to the namespace root instead of a malformed
namespace/path. - CompressionEncoder shrinks runaway retained buffers (>64 KB and 4x the current packet) back to a power-of-two size no 8 MB held forever after one huge chunk packet.
- BlockStateBaseCache publishes the face-sturdy mask through
volatilefields with a@Mutablearray no torn reads. - Mth.hsvToArgb clamps channels and throws exactly like vanilla on out-of-range hues.
### Removed
- Removed MixinStatType.
- Removed the Camera
getMaxZoomoverwrite (vanilla restored, possibly Sable compability yea). - Removed the BlockableEventLoop
submit()shared-future overwrite — only the leanersubmitAsyncremains. - Removed low-value Mth overwrites:
clampedLerp(float/double),lerpInt,wrapDegrees(int/float/double/long),packDegrees. - Removed the VertexFormatElement cached
hashCodeoverwrite. (1.20.1) - Removed the PoseStack
pushPose/popPosePose pooling (1.21-1.21.4) — superseded by vanilla's own pooled Pose model.
### Changed
- Unified entrypoint:
lomka.Lomkawith nestedFabric/Forge/NeoForgeadapters gated by Stonecutter loader constants;VERSIONis swapped fromstonecutter.properties.tomlautomatically. LomkaMixinPlugin.onLoad()setsjoml.fastmath=true(unless the user set it) before JOML classes initialize.GlRenderStateCacherenamed toGlStateCacheand moved toutils/cache/;AxisPoseRotatemoved toutils/math/.- ARGB LUTs are built inside the mixin; AT/AW entries for vanilla
SRGB_TO_LINEAR/LINEAR_TO_SRGBremoved. - Mixin priorities lowered where foreign patches are expected (BufferBuilder, ByteBufferBuilder, VulkanGpuBuffer.Direct, Minecraft, lightmap mixins).
- Frame-end present replaced with a single hardware
glBlitFramebuffer(NEAREST)call instead of the blit-shader fullscreen quad — no per-frame Matrix4f/Tesselator/MeshData allocations, no mid-frame shader program bind. (<1.21.4) **NEW**
### Bug Fixes
- Fixed entity tick crash (
ClassCastExceptionintickNonPassenger) — the cached profiler name path now casts to theLevelsuperclass directly instead of relying on a separate Level mixin being applied. - Mixin config (
lomka-mixins.properties) keys now match full/short/simple mixin names. Toggles are no longer silently ignored or falsely reported as unknown.
### Removed
- Removed MixinLevel and the ILevelProfiler duck interface (superseded by the direct superclass cast).
### Changed
- Removed duplicated license headers and stale javadoc tags in several mixins.
- Shared sequential index buffer grows x4 instead of x2, with an 8192-index warmup floor — no more mid-frame regen hitches on chunk/GUI/cloud spikes. (all versions) **NEW**
- GlStateManager skips redundant viewport/scissor/polygonMode driver calls, cache resets on resize. (1.21.6+) **NEW from OLD**
- Frustum setup uses branchless integer floor/ceil instead of Math calls. (all versions) **NEW**
- PoseStack push/pop pooled and mulPose runs through a scratch matrix. Zero allocations per entity part. (mulPose scratch: 1.21.6–1.21.11)
- VertexBuffer caches its GL primitive mode, format check by reference. (<1.21.6) **NEW**
- VertexFormatElement caches hashCode and drops Usage lambdas in attribute setup. (<1.21) **NEW**
- Multi-consumer fan-out walks delegates with direct indexed loops instead of per-vertex lambdas. (1.20.1–26.1) **NEW**
- VertexConsumer gains an affine fast path for applying pose matrices. (1.21+) **NEW**
- Material hash cached instead of allocating an Object[] per sprite lookup. (1.20.1–1.21.11) **NEW from OLD**
- SectionPos cheaper packed math with allocation-free stream traversal. (all versions) **NEW**
- StatType registration is a lambda-free get-then-put. (all versions) **NEW**
- ChunkStatus progression list cached instead of rebuilt per chunk layer. (1.21+) **NEW**
- Light storage reuses one consumer instead of allocating a lambda per block change. (all versions) **NEW**
- CompressionEncoder reuses its input buffer between packets. (all versions) **NEW**
- Entity tick profiler names cached per entity type, removing vanilla's capturing lambda. (<26.1) **NEW**
- Vulkan host-visible buffers map through a persistent address, no driver round-trip per frame. (26.2) **NEW**
### New
- Mixin configuration is REAL in
config/lomka-mixins.properties. - Mixin configs now declare minVersion: 0.8.
### Removed
- Removed low-value mixins: MixinKey, MixinNativeImage, MixinSoundBufferLibrary, MixinIdMapper.
- Removed accessors: AccessorCache, AccessorNearPlane, InvokerBuilder. (changed by aw/at)
### Changed
- License changed from **MIT** to **LGPL-3.0-only**.
- Mod icon, mod description.
- LomkaFabric, LomkaNeoForge and LomkaForge all-in-one in just Lomka with conditionals.
- GpuBuffer patch disabled on 26.2, vanilla caches the full-buffer slice natively.
- Numerous existing mixins received further tweaks and version-condition adjustments.
- Font glyph widths cached per codepoint/bold pair — text rendering does fewer glyph source lookups. (1.21.11+) **NEW**
- Resource pack loading overhaul:
- FallbackResourceManager listResources fast-path for
.mcmeta— eliminates thousands of useless Identifier allocations and map lookups. **NEW** - MultiPackResourceManager bypasses redundant secondary TreeMap allocation when only one namespace manager is present. **NEW**
- VanillaPackResources fast-path namespace check, zero-allocation path resolution, and cached parsed
pack.mcmeta. (<26.1) **NEW** - Block collision shapes now served from the cached shape instead of virtual
getCollisionShapedispatch. (non-dynamic blocks) **NEW** - Light queues swapped from
LongLinkedOpenHashSetto bit-packedSpatialLongSet— less heap churn during chunk loading. **NEW** - Shader uniform lookup via fastutil open-addressing map instead of HashMap. (1.21.6+) **NEW**
- ChunkedSampleByteBuf no longer allocates a fresh native buffer per chunk. (1.21+) **NEW**
- Lighting precomputes a buffer slice per entry and skips redundant GPU UBO writes when the cardinal light type is unchanged. (1.21.6+)
- NativeImage fillRect/copyRect rewritten with bulk MemoryUtil.memCopy — whole-image and whole-row transfers in a single call.
- Std140 builder and size calculator put* methods rewritten with bitwise alignment and absolute indexed writes. (1.21.6+)
- ModelPart rotateBy composes rotations through a single quaternion instead of a full 3x3 matrix multiply.
- ArrayListDeque removes from the front half by advancing head instead of shifting the tail.
- ARGB reciprocal lookup table computed in O(1) per entry instead of an inner loop.
### Bug Fixes
- NativeImage fillRect/copyRect now validate format and bounds explicitly instead of silently corrupting memory.
- Fixed Lightmap and LightmapRenderStateExtractor 26.2 API calls (
mapBuffer,getMainCamera,getBossOverlay,getNightVisionScale). - MixinMth clampedLerp is now version-conditional so the correct argument order is used on 1.21.11+ and older.
- Camera 26.1+ near-plane path kept on the lightweight custom structure.
### New
- New duck interfaces:
IBitSetDiscreteVoxelShape,IGlyphSource. - New accessors:
AccessorCache,InvokerBlockBehaviour.
### Compatibility
- Per-version mixin configs, access wideners and access transformers are now generated from Stonecutter templates (
src/stonecutter/) instead of committed per-version files. - Mixin source exclusions are auto-derived from the processed mixin config — no more hand-maintained exclude tables in build scripts.
### Removed
- Removed low-impact mixins: MixinSamplerCache, MixinSortState, MixinQuadrant, MixinItemTransform, MixinMipmapGenerator.
- Removed DiscreteVoxelShapeHelper — replaced with the IBitSetDiscreteVoxelShape duck interface.
- Removed per-version committed
lomka.mixins.json,*.accesswidenerandaccesstransformer.cfgfiles.
### Changed
- Build system refactored:
build-logiccomposite build →buildSrcwith auto-derived mixin exclusions. - 16 build variants now (added
1.20.1-fabricand1.20.1-forge).
- Sampler cache lookup rewritten — no per-call enum comparisons.
- Cursor3D advance rewritten — no divisions/modulos per voxel.
- Tiny holder sets use linear scan instead of Set allocation.
- Lighting coordinate calculations now applied. (26.1+)
- BlockableEventLoop submit runs inline with no future allocations.
- GameRenderer extractCamera removed null-conditional.
- Key inputs hashCode caching simplified.
- Removed Thread.yield() from runTick for lower frame latency. (before it was just commented I dunno why, bruh)
### Bug Fixes
- Fixed NativeImage region fill writing wrong color byte order.
- Fixed SoundEngine skipping camera updates before sound engine loads.
- Reverted Mth power-of-two calculation to vanilla.
- Raised ItemInHandRenderer and LightTexture(Lightmaps) mixins priority to avoid conflicts. [#18](https://github.com/Starlevka/Lomka/issues/18)
### Compatibility
- Light texture and light state extraction now use cancellable injects instead of overwrites.
### Removed
- Removed strict mixins: FaceBakery, SpriteContents, PalettedPermutations.
- Removed commented AccessorQuadCollection.
- Lightmap pipeline rewritten — less GPU/CPU allocations per frame.
- PoseStack uses pure
mulPoserotation with cached axis transforms. - Sound buffer library optimized.
- VertexFormat
hashCode()cached. - Lighting coordinate calculations optimized.
- TextureAtlas ported to new rendering API.
- ARGB and Mth math minor optimizations.
## Bug Fixes
- Fixed item in hand rendering desync. ([#15](https://github.com/Starlevka/Lomka/issues/15))
- Fixed mipmap generation issues.
- Fixed Mth math edge cases.
## Removed
- Removed RAM-heavy optimizations (Material and Resource caching).
- Removed incompatible mixins: TextureUtil, MeshData, KeyframeAnimation.
- Chunks load smoother — faster lighting and biome data access.
- FaceSturdy block state caching.
- Bitset shapes create slightly less garbage.
- Refactored more perfomously game calculations.
- Network packets create less garbage.
- No more per-frame allocation for items held by entity.
- Resource packs now free unused mip levels after loading. Saves 10-50 MB RAM.
- Improved faces building (1.21.11+).
## Bug Fixes
- Fixed a rare crash (
NegativeArraySizeException) in world loading. - Fixed lava fog appearing wrong in some camera positions.
- Fixed sound updates being skipped.
- Fixed game crash with Create Fly ([#10](https://github.com/Starlevka/Lomka/issues/10))
## New
- Added **1.21.4** and **1.21.6-1.21.8** support.
## Removed
- Old low-impact and uncareful optimizations to keep the mod cleaner.
All optimizations below apply to it. On other versions (1.21, 1.21.9, 26.1, 26.2) some changes are absent due to Minecraft code differences.
### Removed
- Entity culling** — the mod no longer checks which entities are off-screen and skips their rendering/ticks. Removed entirely (EntityCulling, BlockEntityCulling, EntityTickCull, Frustum, Octree, SmartLeaves + utilities). Too many bugs, and the FPS gain was inconsistent.
- Config** — config file, in-game config screen, ModMenu integration. The mod now works out of the box with no settings.
- OpenGL command cache** — three mixins that cached viewport/scissor/polygonMode calls. Negligible gain.
- NBT throttling** — thread limiting for NBT read/write + file cache. Complexity wasn't worth it.
- Platform abstraction layer** — ServiceLoader and PlatformHelper removed. Simpler now.
- Chunk log spam suppression, crash report guard, translucency resort throttle** — minor stuff.
### Added
The mod was rewritten from scratch. Focus shifted from entity culling to **hot-path acceleration** and **garbage elimination** (less GC pressure = fewer stutters):
- Block/model rendering** — faster face geometry calculations, model rotations, quaternion/matrix work. Some less allocations and less GC.
- Buffers & vertices** — direct memory writes via MemoryUtil, compact color packing (2 instructions instead of 7), no more Math.addExact, cached vertex format hashes. All of these run every frame.
- Camera** — fully reworked: reusable objects, unrolled loops, pre-computed near-plane vectors, cached fluid-in-camera check. Smoother movement.
- Math** — bitwise operations instead of division, FMA (fused multiply-add) for distance/slerp, pre-computed rotation tables, fast rounding. Small gains that add up every frame.
- ARGB (colors)** — all color operations (blending, greyscale, alpha) rewritten: no division, pre-computed lookup tables, bit shifts. Helps particles, banners, maps, etc.
- Audio** — optimized buffer creation, source positioning, OGG decoding. Fewer micro-stutters when sounds load.
- Textures** — unused mipmap levels get closed (frees VRAM), pre-computed filter weights for MipmapGenerator, palette caching for atlases.
- Fonts** — first 8192 characters cached in a flat array instead of HashMap. Every time the game renders text — faster.
- Collections** — ArrayListDeque rewritten from scratch (power-of-two capacity, bitwise masking, fixes a vanilla NegativeArraySizeException bug), ByIdMap uses Int2ObjectOpenHashMap. Faster, fewer allocations.
- Hashing** — cached toString/hashCode on Identifier/ResourceLocation, Material, Transformation, BakedQuad, InputConstants, VertexFormat, TextureSetup.
- Modifier keys** — Shift/Ctrl/Alt checked once per tick instead of every hasShiftDown() call.
- RenderSystem** — forces GL debug flags off (debug mode hurts FPS significantly).
- EventLoop** — CompletableFuture + LockSupport instead of Thread.yield().
- Shader uniforms** — cached GPU buffer slices, static constants for white/zero uniforms, skip redundant writes.
### Changed
- Build system** — old multi-module Gradle (common/fabric/neoforge) → Stonecutter. Shared codebase with version-specific overrides.
- Package** —
set.starl→lomka.starl. - Entry points** — separate LomkaFabric and LomkaNeoForge.
- Versions** — 1.21.11/Fabric+NeoForge (primary), plus additional variants for 1.21-1.21.1, 1.21.9-1.21.10, 26.1-26.1.2, 26.2.
(AI generated changelog)
- Texture decode off-thread (LazyLoadedImageOffThreadMixin, virtual threads)
- Network compression buffer reuse (CompressionDecoderDirectInputReuseMixin)
- Shader program binary cache (config)
- LomkaConfigScreen (in-game config UI, 272 lines)
- CullingContext record
- AOT cache / compact headers logging
- UploadSizedRunnable (dead code)
Updated:
- EntityCullingMixin: removed 3 unused fields (~24 bytes saved per entity)
- LevelRendererAccess: removed dead method isSectionCompiledAndVisible
- EntityRendererCullingMixin: camera hash now uses hash3 (better distribution)
- FrustumMixin: infinite loop replaced with 256 iteration cap (fixes freezes)
- GL caches: call GL11 directly instead of GlStateManager (faster)
- GlRenderPassReuseMixin: collection clearing at HEAD instead of TAIL
- clientTickCullIncludeDisplay: false → true by default
Added:
- NeoForge support (now works on Fabric + NeoForge)
- Virtual threads for texture decoding (less stutter)
- New entity culling system (2 mixins)
- 5 new render mixins + accessors
- Config options (Java 25+, culling, network)
Removed:
- GLDeviceCacheMixin (1 mixin)
Changed:
- Core logic restructured (LomkaCore)
- Section visibility cache (no longer resets every frame)
- Network compression (reused buffers for less memory usage)
- Math calculations optimized (fma usage)