A Belated Gift — SlashBlade Rendering Optimization Mod
📖 What Is This?
This is a client-side rendering optimization mod for SlashBlade: Resharped, designed to eliminate severe FPS drops when holding a blade, swinging, rendering dropped blades, using shaders, or loading YSM models.
In one sentence: holding FPS doubles, swinging triples, and dropped high-poly blades get a 30x boost.
📊 Performance Comparison
Mod Version: 1.0.2
Test Configuration: AMD Ryzen 7 9700X + NVIDIA RTX 5070 Ti
Second-person perspective, BSL shaders, Blade: Alchemy Kingdom
Without Shaders
| Scenario | Vanilla | Accelerated Render | A Belated Gift | Improvement (vs Vanilla) |
|---|---|---|---|---|
| Holding Blade | 359 | 412 | 810 | +126% |
| Swinging | 220 | 248 | 620 | +182% |
| 17 High-Poly Dropped Blades | 61 | 425 | 730 | +1097% |
With Shaders (BSL)
| Scenario | Vanilla | Accelerated Render | A Belated Gift | Improvement (vs Vanilla) |
|---|---|---|---|---|
| Holding Blade | 208 | 330 | 381 | +83% |
| Swinging | 115 | 153 | 365 | +217% |
| 17 High-Poly Dropped Blades | 29 | 240 | 258 | +1134% |
YSM Compatibility
| Scenario | Vanilla | Accelerated Render | A Belated Gift | Improvement (vs Vanilla) |
|---|---|---|---|---|
| No Shaders · Holding | 374 | 398 | 724 | +94% |
| No Shaders · Swinging | 208 | 362 | 605 | +191% |
| Shaders · Holding | 215 | 301 | 374 | +74% |
| Shaders · Swinging | 172 | 261 | 345 | +101% |
⚙️ How Does It Work?
No More "Redraw Every Frame"
Vanilla rendering traverses every group, face, and vertex of the OBJ model on each frame. This mod "bakes" the data into a GPU-friendly format at load time, then simply reuses it every frame thereafter.
Static VBO Caching
Blade bodies, sheaths, item icons, blades on stands, and dropped blades all share a 96 MiB LRU cache. The second time the same blade appears, it reuses the data already uploaded to VRAM instead of resubmitting vertices.
Enchantment glints also get their own dedicated static cache, so they no longer drag down performance.
"Smart Savings" Under Shaders
- Uses a simplified 32-segment proxy mesh instead of the full high-poly model during shadow rendering
- Transparent effects (SlashEffects, Judgement Cut, etc.) skip the shadow map stage entirely
- Glowing blade bodies do not cast shadows repeatedly
Swing Animation — "Only What's Necessary"
Vanilla calculates full PMD vertex skinning for every swing, even though only two bone anchor points are actually used. This mod skips the useless vertex calculations and keeps only the bone poses.
LOD for Blade Effects
Full detail at close range, fewer layers at medium range, and only the core body at long range — with further simplification under shaders.
No First-Use Stutter
Models, textures, and RenderTypes are pre-warmed before you even enter the game. No sudden stutter when swinging a blade or opening your inventory for the first time.
Automatic Cache Cleanup
Caches are automatically cleared when you exit a world or reload resources — performance stays consistent over time.
Compatibility & Safe Fallback
Retains the RenderOverrideEvent. If dynamic UVs, dynamic transparency, or uncacheable meshes are detected, it automatically falls back to vanilla rendering — no crashes.
⚠️ Incompatibility
Not compatible with [Accelerated Render] — both mods overlap in functionality. Installing them together may cause rendering glitches or performance degradation.
Slash Effect Rendering API
This API is intended for SlashBlade addons that need to replace the slash effect model or texture. The addon only selects the resources, while A Belated Gift continues to handle distance-based LOD, shader compatibility, mesh optimization, and resource warm-up.
After adopting this API, an addon must not use a Mixin to cancel SlashEffectRenderer.render. Cancelling that method prevents the registry from being reached.
Replacing Only the Slash Texture
Register the rule from an enqueued FMLClientSetupEvent task:
import cn.star.a_belated_gift.api.client.SlashEffectRenderRegistry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
private static final ResourceLocation RULE_ID =
new ResourceLocation("example", "special_blade_slash");
private static final ResourceLocation SLASH_TEXTURE =
new ResourceLocation("example", "model/util/special_slash.png");
private static void onClientSetup(FMLClientSetupEvent event) {
event.enqueueWork(() -> SlashEffectRenderRegistry.registerTexture(
RULE_ID,
100,
effect -> effect.getOwner() instanceof Player player
&& player.getMainHandItem().is(EXAMPLE_BLADE.get()),
SLASH_TEXTURE
));
}
registerTexture continues to use SlashBlade's default model/util/slash.obj model.
Replacing Both the OBJ and Texture
import cn.star.a_belated_gift.api.client.SlashEffectRenderDefinition;
import cn.star.a_belated_gift.api.client.SlashEffectRenderRegistry;
private static final SlashEffectRenderDefinition SPECIAL_SLASH =
new SlashEffectRenderDefinition(
new ResourceLocation("example", "model/util/special_slash.obj"),
new ResourceLocation("example", "model/util/special_slash.png")
);
private static void onClientSetup(FMLClientSetupEvent event) {
event.enqueueWork(() -> SlashEffectRenderRegistry.register(
new ResourceLocation("example", "special_blade_slash"),
100,
effect -> effect.getOwner() instanceof Player player
&& player.getMainHandItem().is(EXAMPLE_BLADE.get()),
SPECIAL_SLASH
));
}
This register overload automatically adds the definition's OBJ and texture to the warm-up list.
Dynamic Providers
Implement SlashEffectRenderProvider when the definition must be selected dynamically from the effect state:
import cn.star.a_belated_gift.api.client.SlashEffectRenderDefinition;
import cn.star.a_belated_gift.api.client.SlashEffectRenderProvider;
import mods.flammpfeil.slashblade.entity.EntitySlashEffect;
import java.util.Collection;
import java.util.List;
public final class ExampleSlashProvider implements SlashEffectRenderProvider {
private static final SlashEffectRenderDefinition BLUE =
SlashEffectRenderDefinition.withTexture(
new ResourceLocation("example", "model/util/blue_slash.png"));
private static final SlashEffectRenderDefinition RED =
SlashEffectRenderDefinition.withTexture(
new ResourceLocation("example", "model/util/red_slash.png"));
@Override
public SlashEffectRenderDefinition resolve(EntitySlashEffect effect) {
if (!(effect.getOwner() instanceof Player player)
|| !player.getMainHandItem().is(EXAMPLE_BLADE.get())) {
return null;
}
return effect.getIsCritical() ? RED : BLUE;
}
@Override
public Collection<SlashEffectRenderDefinition> warmupDefinitions() {
return List.of(BLUE, RED);
}
}
event.enqueueWork(() -> SlashEffectRenderRegistry.register(
new ResourceLocation("example", "dynamic_slash"),
200,
new ExampleSlashProvider()
));
Returning null from resolve means that the current rule does not match. The registry then checks the next provider. Every resource that may be returned should be declared through warmupDefinitions.
Matching Order
- Rules with a higher numeric priority are evaluated first.
- Rules with the same priority are ordered by registration ID, making the result independent of mod loading order.
- The first provider that returns a non-
nulldefinition wins. - SlashBlade's default model and texture are used when no rule matches.
- Registering the same ID again replaces the previous rule.
- Provider exceptions are isolated and logged, allowing the remaining rules to continue matching.
Unregistering a Rule
boolean removed = SlashEffectRenderRegistry.unregister(
new ResourceLocation("example", "special_blade_slash")
);
Unregistering only affects subsequent slash effect selection. Resources that have already been warmed are managed by Minecraft's resource lifecycle.


