diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/faster_item_rendering/ItemRendererMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/faster_item_rendering/ItemRendererMixin.java index 987f0235..9f936496 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/faster_item_rendering/ItemRendererMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/faster_item_rendering/ItemRendererMixin.java @@ -11,7 +11,9 @@ import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.SimpleBakedModel; import net.minecraft.core.Direction; +import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; +import org.embeddedt.modernfix.render.FastItemRenderType; import org.embeddedt.modernfix.render.RenderState; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -38,6 +40,17 @@ public abstract class ItemRendererMixin { this.transformType = transformType; } + private static final Direction[] ITEM_DIRECTIONS = new Direction[] { Direction.SOUTH }; + private static final Direction[] BLOCK_DIRECTIONS = new Direction[] { Direction.UP, Direction.EAST, Direction.NORTH }; + + private boolean isCorrectDirectionForType(FastItemRenderType type, Direction direction) { + if(type == FastItemRenderType.SIMPLE_ITEM) + return direction == Direction.SOUTH; + else { + return direction == Direction.UP || direction == Direction.EAST || direction == Direction.NORTH; + } + } + /** * If a model * - is a vanilla item model (SimpleBakedModel), @@ -48,25 +61,42 @@ public abstract class ItemRendererMixin { */ @Inject(method = "renderModelLists", at = @At("HEAD"), cancellable = true) private void fasterItemRender(BakedModel model, ItemStack stack, int combinedLight, int combinedOverlay, PoseStack matrixStack, VertexConsumer buffer, CallbackInfo ci) { - if(!RenderState.IS_RENDERING_LEVEL && !stack.isEmpty() && model.getClass() == SimpleBakedModel.class && transformType == ItemTransforms.TransformType.GUI && model.getTransforms().gui == ItemTransform.NO_TRANSFORM) { + if(!RenderState.IS_RENDERING_LEVEL && !stack.isEmpty() && model.getClass() == SimpleBakedModel.class && transformType == ItemTransforms.TransformType.GUI) { + FastItemRenderType type; + ItemTransform transform = model.getTransforms().gui; + if(transform == ItemTransform.NO_TRANSFORM) + type = FastItemRenderType.SIMPLE_ITEM; + else if(stack.getItem() instanceof BlockItem && isBlockTransforms(transform)) + type = FastItemRenderType.SIMPLE_BLOCK; + else + return; ci.cancel(); PoseStack.Pose pose = matrixStack.last(); int[] combinedLights = new int[] {combinedLight, combinedLight, combinedLight, combinedLight}; - List culledFaces = model.getQuads(null, Direction.SOUTH, dummyRandom); - List unculledFaces = model.getQuads(null, null, dummyRandom); - /* check size to avoid instantiating iterator when the list is empty */ - if(culledFaces.size() > 0) { - for(BakedQuad quad : culledFaces) { - render2dItemFace(quad, stack, buffer, pose, combinedLights, combinedOverlay); + Direction[] directions = type == FastItemRenderType.SIMPLE_ITEM ? ITEM_DIRECTIONS : BLOCK_DIRECTIONS; + for(Direction direction : directions) { + List culledFaces = model.getQuads(null, direction, dummyRandom); + /* check size to avoid instantiating iterator when the list is empty */ + if(culledFaces.size() > 0) { + for(BakedQuad quad : culledFaces) { + render2dItemFace(quad, stack, buffer, pose, combinedLights, combinedOverlay); + } } } + List unculledFaces = model.getQuads(null, null, dummyRandom); for(BakedQuad quad : unculledFaces) { - if(quad.getDirection() == Direction.SOUTH) + if(isCorrectDirectionForType(type, quad.getDirection())) render2dItemFace(quad, stack, buffer, pose, combinedLights, combinedOverlay); } } } + private boolean isBlockTransforms(ItemTransform transform) { + return transform.rotation.x() == 30f + && transform.rotation.y() == 225f + && transform.rotation.z() == 0f; + } + private void render2dItemFace(BakedQuad quad, ItemStack stack, VertexConsumer buffer, PoseStack.Pose pose, int[] combinedLights, int combinedOverlay) { int i = -1; if (quad.isTinted()) { diff --git a/common/src/main/java/org/embeddedt/modernfix/render/FastItemRenderType.java b/common/src/main/java/org/embeddedt/modernfix/render/FastItemRenderType.java new file mode 100644 index 00000000..31b02d27 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/render/FastItemRenderType.java @@ -0,0 +1,6 @@ +package org.embeddedt.modernfix.render; + +public enum FastItemRenderType { + SIMPLE_ITEM, + SIMPLE_BLOCK +} diff --git a/common/src/main/java/org/embeddedt/modernfix/resources/PackResourcesCacheEngine.java b/common/src/main/java/org/embeddedt/modernfix/resources/PackResourcesCacheEngine.java index 8e2ced85..6c0d2235 100644 --- a/common/src/main/java/org/embeddedt/modernfix/resources/PackResourcesCacheEngine.java +++ b/common/src/main/java/org/embeddedt/modernfix/resources/PackResourcesCacheEngine.java @@ -1,16 +1,19 @@ package org.embeddedt.modernfix.resources; +import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.PackType; +import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.util.PackTypeHelper; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; @@ -26,6 +29,7 @@ public class PackResourcesCacheEngine { private final Map> namespacesByType; private final Set containedPaths; private final EnumMap>> resourceListings; + private CompletableFuture cacheFuture; public PackResourcesCacheEngine(Function> namespacesRetriever, BiFunction basePathRetriever) { this.namespacesByType = new EnumMap<>(PackType.class); @@ -36,32 +40,50 @@ public class PackResourcesCacheEngine { } this.containedPaths = new ObjectOpenHashSet<>(); this.resourceListings = new EnumMap<>(PackType.class); + CompletableFuture future = CompletableFuture.completedFuture(null); + Stopwatch watch = Stopwatch.createStarted(); + // used for log message + Path debugPath = basePathRetriever.apply(PackType.CLIENT_RESOURCES, "minecraft").toAbsolutePath(); for(PackType type : PackType.values()) { Collection namespaces = PackTypeHelper.isVanillaPackType(type) ? this.namespacesByType.get(type) : namespacesRetriever.apply(type); - ImmutableMap.Builder> packTypedMap = ImmutableMap.builder(); - for(String namespace : namespaces) { - try { - ImmutableList.Builder namespacedList = ImmutableList.builder(); - Path root = basePathRetriever.apply(type, namespace).toAbsolutePath(); - String[] prefix = new String[] { type.getDirectory(), namespace }; - try (Stream stream = Files.walk(root)) { - stream - .map(path -> root.relativize(path.toAbsolutePath())) - .filter(PackResourcesCacheEngine::isValidCachedResourcePath) - .forEach(path -> { - CachedResourcePath cachedPath = new CachedResourcePath(prefix, path); - this.containedPaths.add(cachedPath); - if(!cachedPath.getFileName().endsWith(".mcmeta")) - namespacedList.add(cachedPath); - }); + future = future.thenRunAsync(() -> { + ImmutableMap.Builder> packTypedMap = ImmutableMap.builder(); + for(String namespace : namespaces) { + try { + ImmutableList.Builder namespacedList = ImmutableList.builder(); + Path root = basePathRetriever.apply(type, namespace).toAbsolutePath(); + String[] prefix = new String[] { type.getDirectory(), namespace }; + try (Stream stream = Files.walk(root)) { + stream + .map(path -> root.relativize(path.toAbsolutePath())) + .filter(PackResourcesCacheEngine::isValidCachedResourcePath) + .forEach(path -> { + CachedResourcePath cachedPath = new CachedResourcePath(prefix, path); + synchronized (this.containedPaths) { + this.containedPaths.add(cachedPath); + } + if(!cachedPath.getFileName().endsWith(".mcmeta")) + namespacedList.add(cachedPath); + }); + } + packTypedMap.put(namespace, namespacedList.build()); + } catch(IOException ignored) { } - packTypedMap.put(namespace, namespacedList.build()); - } catch(IOException ignored) { } - } - this.resourceListings.put(type, packTypedMap.build()); + synchronized (this.resourceListings) { + this.resourceListings.put(type, packTypedMap.build()); + } + }, ModernFix.resourceReloadExecutor()); } - ((ObjectOpenHashSet)this.containedPaths).trim(); + future = future.thenRunAsync(() -> { + ((ObjectOpenHashSet)this.containedPaths).trim(); + watch.stop(); + }, ModernFix.resourceReloadExecutor()); + this.cacheFuture = future; + // print debug message in separate task to prevent slowing down rest of load + future.thenRunAsync(() -> { + ModernFix.LOGGER.debug("Generated cache for {} in {}", debugPath, watch); + }, ModernFix.resourceReloadExecutor()); } private static boolean isValidCachedResourcePath(Path path) { @@ -86,13 +108,22 @@ public class PackResourcesCacheEngine { return null; } + private void awaitLoad() { + if(this.cacheFuture != null) { + this.cacheFuture.join(); + this.cacheFuture = null; + } + } + public boolean hasResource(String path) { + awaitLoad(); return this.containedPaths.contains(new CachedResourcePath(path)); } public Collection getResources(PackType type, String resourceNamespace, String pathIn, int maxDepth, Predicate filter) { if(!PackTypeHelper.isVanillaPackType(type)) throw new IllegalArgumentException("Only vanilla PackTypes are supported"); + awaitLoad(); List paths = resourceListings.get(type).getOrDefault(resourceNamespace, Collections.emptyList()); if(paths.isEmpty()) return Collections.emptyList();