Aggressively cache RenderMaterials

This commit is contained in:
quat1024 2023-07-02 06:26:19 -04:00
parent ed42e4da9f
commit 97de0d1b9c
5 changed files with 73 additions and 53 deletions

View File

@ -5,6 +5,8 @@ import io.github.cottonmc.templates.model.TemplateModelVariantProvider;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry; import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener; import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
@ -13,10 +15,17 @@ import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.ResourceType; import net.minecraft.resource.ResourceType;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.ChunkSectionPos; import net.minecraft.util.math.ChunkSectionPos;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public class TemplatesClient implements ClientModInitializer { public class TemplatesClient implements ClientModInitializer {
public static TemplateModelVariantProvider provider = new TemplateModelVariantProvider(); public static TemplateModelVariantProvider provider = new TemplateModelVariantProvider();
public static @NotNull Renderer getFabricRenderer() {
return Objects.requireNonNull(RendererAccess.INSTANCE.getRenderer(), "A Fabric Rendering API implementation is required to use Templates!");
}
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
Templates.chunkRerenderProxy = (world, pos) -> { Templates.chunkRerenderProxy = (world, pos) -> {

View File

@ -1,5 +1,6 @@
package io.github.cottonmc.templates.model; package io.github.cottonmc.templates.model;
import io.github.cottonmc.templates.TemplatesClient;
import net.fabricmc.fabric.api.renderer.v1.Renderer; import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess; import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
@ -18,8 +19,7 @@ public class SlopeBaseMesh {
public static final int TAG_BOTTOM = Direction.DOWN.ordinal(); public static final int TAG_BOTTOM = Direction.DOWN.ordinal();
public static Mesh make() { public static Mesh make() {
Renderer renderer = RendererAccess.INSTANCE.getRenderer(); Renderer renderer = TemplatesClient.getFabricRenderer();
if(renderer == null) throw new IllegalStateException("RenderAccess.INSTANCE not populated - no Fabric Renderer API?");
MeshBuilder builder = renderer.meshBuilder(); MeshBuilder builder = renderer.meshBuilder();
QuadEmitter qu = builder.getEmitter(); QuadEmitter qu = builder.getEmitter();

View File

@ -1,19 +1,13 @@
package io.github.cottonmc.templates.model; package io.github.cottonmc.templates.model;
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry; import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView; import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView;
import net.fabricmc.fabric.api.util.TriState;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.client.color.block.BlockColorProvider; import net.minecraft.client.color.block.BlockColorProvider;
import net.minecraft.client.render.RenderLayers;
import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.Sprite;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@ -22,17 +16,15 @@ import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView; import net.minecraft.world.BlockRenderView;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.function.Supplier; import java.util.function.Supplier;
@SuppressWarnings("ClassCanBeRecord")
public class SlopeQuadTransformFactory implements TemplateQuadTransformFactory { public class SlopeQuadTransformFactory implements TemplateQuadTransformFactory {
public SlopeQuadTransformFactory(TemplateAppearanceManager tam) { public SlopeQuadTransformFactory(TemplateAppearanceManager tam) {
this.tam = tam; this.tam = tam;
this.r = Objects.requireNonNull(RendererAccess.INSTANCE.getRenderer(), "A Fabric Rendering API implementation is required");
} }
private final TemplateAppearanceManager tam; private final TemplateAppearanceManager tam;
private final Renderer r;
@Override @Override
public @NotNull RenderContext.QuadTransform blockTransformer(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier) { public @NotNull RenderContext.QuadTransform blockTransformer(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier) {
@ -41,40 +33,31 @@ public class SlopeQuadTransformFactory implements TemplateQuadTransformFactory {
Block block = template.getBlock(); Block block = template.getBlock();
TemplateAppearance appearance; TemplateAppearance appearance;
RenderMaterial material; int globalTint = 0xFFFFFF;
int globalTint;
if(block == Blocks.AIR) { if(block == Blocks.AIR) {
appearance = tam.getDefaultAppearance(); appearance = tam.getDefaultAppearance();
material = r.materialFinder().clear().blendMode(BlendMode.CUTOUT).find();
globalTint = 0xFFFFFF;
} else { } else {
appearance = tam.getAppearance(template); appearance = tam.getAppearance(template);
material = r.materialFinder().clear()
.disableDiffuse(false)
.ambientOcclusion(TriState.FALSE)
.blendMode(BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(template)))
.find();
BlockColorProvider tint = ColorProviderRegistry.BLOCK.get(block); BlockColorProvider tint = ColorProviderRegistry.BLOCK.get(block);
if(tint != null) globalTint = 0xFF000000 | tint.getColor(template, blockView, pos, 1); if(tint != null) globalTint = 0xFF000000 | tint.getColor(template, blockView, pos, 1);
else globalTint = 0xFFFFFF;
} }
return new Transformer(appearance, material, globalTint); return new Transformer(appearance, globalTint);
} }
@Override @Override
public @NotNull RenderContext.QuadTransform itemTransformer(ItemStack stack, Supplier<Random> randomSupplier) { public @NotNull RenderContext.QuadTransform itemTransformer(ItemStack stack, Supplier<Random> randomSupplier) {
return new Transformer(tam.getDefaultAppearance(), r.materialFinder().clear().find(), 0xFFFFFF); return new Transformer(tam.getDefaultAppearance(), 0xFFFFFF);
} }
public static record Transformer(TemplateAppearance appearance, RenderMaterial material, int color) implements RenderContext.QuadTransform { public static record Transformer(TemplateAppearance appearance, int color) implements RenderContext.QuadTransform {
private static final Direction[] DIRECTIONS = Direction.values(); private static final Direction[] DIRECTIONS = Direction.values();
@Override @Override
public boolean transform(MutableQuadView quad) { public boolean transform(MutableQuadView quad) {
quad.material(material); quad.material(appearance.getRenderMaterial());
//The quad tag numbers were selected so this magic trick works: //The quad tag numbers were selected so this magic trick works:
Direction dir = DIRECTIONS[quad.tag()]; Direction dir = DIRECTIONS[quad.tag()];

View File

@ -1,5 +1,6 @@
package io.github.cottonmc.templates.model; package io.github.cottonmc.templates.model;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -8,27 +9,10 @@ import java.util.Objects;
public interface TemplateAppearance { public interface TemplateAppearance {
@NotNull Sprite getParticleSprite(); //TODO: plug this in @NotNull Sprite getParticleSprite(); //TODO: plug this in
@NotNull RenderMaterial getRenderMaterial();
@NotNull Sprite getSprite(Direction dir); @NotNull Sprite getSprite(Direction dir);
boolean hasColor(Direction dir); boolean hasColor(Direction dir);
record SingleSprite(@NotNull Sprite defaultSprite) implements TemplateAppearance {
public SingleSprite(Sprite defaultSprite) {
this.defaultSprite = Objects.requireNonNull(defaultSprite);
}
@Override
public @NotNull Sprite getParticleSprite() {
return defaultSprite;
}
@Override
public @NotNull Sprite getSprite(Direction dir) {
return defaultSprite;
}
@Override
public boolean hasColor(Direction dir) {
return false;
}
}
} }

View File

@ -1,10 +1,15 @@
package io.github.cottonmc.templates.model; package io.github.cottonmc.templates.model;
import io.github.cottonmc.templates.TemplatesClient;
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.util.TriState;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderLayers;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedQuad; import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.texture.MissingSprite;
import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.screen.PlayerScreenHandler; import net.minecraft.screen.PlayerScreenHandler;
@ -13,22 +18,34 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random; import net.minecraft.util.math.random.Random;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.EnumMap;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function; import java.util.function.Function;
public class TemplateAppearanceManager { public class TemplateAppearanceManager {
public TemplateAppearanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) { public TemplateAppearanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) {
Sprite defaultSprite = spriteLookup.apply(DEFAULT_SPRITE_ID); SpriteIdentifier defaultSpriteId = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier("minecraft:block/scaffolding_top"));
if(defaultSprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_ID + " !"); Sprite defaultSprite = spriteLookup.apply(defaultSpriteId);
defaultAppearance = new TemplateAppearance.SingleSprite(defaultSprite); if(defaultSprite == null) throw new IllegalStateException("Couldn't locate " + defaultSpriteId + " !");
MaterialFinder finder = TemplatesClient.getFabricRenderer().materialFinder();
for(BlendMode blend : BlendMode.values()) {
blockMaterials.put(blend, finder.clear().disableDiffuse(false).ambientOcclusion(TriState.FALSE).blendMode(blend).find());
}
this.defaultAppearance = new SingleSpriteAppearance(defaultSprite, blockMaterials.get(BlendMode.CUTOUT));
} }
private static final SpriteIdentifier DEFAULT_SPRITE_ID = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier("minecraft:block/scaffolding_top"));
private final TemplateAppearance defaultAppearance; private final TemplateAppearance defaultAppearance;
//Mutable, append-only cache:
private final ConcurrentHashMap<BlockState, TemplateAppearance> appearanceCache = new ConcurrentHashMap<>(); private final ConcurrentHashMap<BlockState, TemplateAppearance> appearanceCache = new ConcurrentHashMap<>();
//Immutable contents:
private final EnumMap<BlendMode, RenderMaterial> blockMaterials = new EnumMap<>(BlendMode.class);
public TemplateAppearance getDefaultAppearance() { public TemplateAppearance getDefaultAppearance() {
return defaultAppearance; return defaultAppearance;
} }
@ -63,20 +80,25 @@ public class TemplateAppearanceManager {
//Just for space-usage purposes, we store the particle in sprites[6] instead of using another field. //Just for space-usage purposes, we store the particle in sprites[6] instead of using another field.
sprites[6] = model.getParticleSprite(); sprites[6] = model.getParticleSprite();
//Fill out any missing values in the sprites array //Fill out any missing values in the sprites array. Failure to pick textures shouldn't lead to NPEs later on.
for(int i = 0; i < sprites.length; i++) { for(int i = 0; i < sprites.length; i++) {
if(sprites[i] == null) sprites[i] = defaultAppearance.getParticleSprite(); if(sprites[i] == null) sprites[i] = defaultAppearance.getParticleSprite();
} }
return new ComputedApperance(sprites, hasColorMask); return new ComputedApperance(sprites, hasColorMask, blockMaterials.get(BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(state))));
} }
private static record ComputedApperance(@NotNull Sprite[] sprites, byte hasColorMask) implements TemplateAppearance { private static record ComputedApperance(@NotNull Sprite[] sprites, byte hasColorMask, RenderMaterial mat) implements TemplateAppearance {
@Override @Override
public @NotNull Sprite getParticleSprite() { public @NotNull Sprite getParticleSprite() {
return sprites[6]; return sprites[6];
} }
@Override
public @NotNull RenderMaterial getRenderMaterial() {
return mat;
}
@Override @Override
public @NotNull Sprite getSprite(Direction dir) { public @NotNull Sprite getSprite(Direction dir) {
return sprites[dir.ordinal()]; return sprites[dir.ordinal()];
@ -87,4 +109,26 @@ public class TemplateAppearanceManager {
return (hasColorMask & (1 << dir.ordinal())) != 0; return (hasColorMask & (1 << dir.ordinal())) != 0;
} }
} }
private static record SingleSpriteAppearance(@NotNull Sprite defaultSprite, RenderMaterial mat) implements TemplateAppearance {
@Override
public @NotNull Sprite getParticleSprite() {
return defaultSprite;
}
@Override
public @NotNull RenderMaterial getRenderMaterial() {
return mat;
}
@Override
public @NotNull Sprite getSprite(Direction dir) {
return defaultSprite;
}
@Override
public boolean hasColor(Direction dir) {
return false;
}
}
} }