Add model predicate flattening

This commit is contained in:
embeddedt 2023-01-22 20:23:41 -05:00
parent 6b28cb5ebc
commit f2de12329e
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
13 changed files with 447 additions and 2 deletions

View File

@ -35,6 +35,7 @@ public class ModernFixEarlyConfig {
this.addMixinRule("perf.parallel_blockstate_cache_rebuild", true);
this.addMixinRule("perf.compress_biome_container", true);
this.addMixinRule("perf.nuke_empty_chunk_sections", true);
this.addMixinRule("perf.flatten_model_predicates", true);
this.addMixinRule("perf.deduplicate_location", true);
this.addMixinRule("safety", true);
this.addMixinRule("launch.transformer_cache", false);

View File

@ -0,0 +1,32 @@
package org.embeddedt.modernfix.mixin.perf.flatten_model_predicates;
import com.google.common.collect.Streams;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.model.multipart.AndCondition;
import net.minecraft.client.renderer.model.multipart.ICondition;
import net.minecraft.state.StateContainer;
import org.embeddedt.modernfix.predicate.StatePropertyPredicateHelper;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@Mixin(AndCondition.class)
public class AndConditionMixin {
@Shadow @Final private Iterable<? extends ICondition> conditions;
/**
* @author JellySquid
* @reason Flatten predicates
*/
@Overwrite
public Predicate<BlockState> getPredicate(StateContainer<Block, BlockState> stateManager) {
return StatePropertyPredicateHelper.allMatch(Streams.stream(this.conditions).map((multipartModelSelector) -> {
return multipartModelSelector.getPredicate(stateManager);
}).collect(Collectors.toList()));
}
}

View File

@ -0,0 +1,32 @@
package org.embeddedt.modernfix.mixin.perf.flatten_model_predicates;
import com.google.common.collect.Streams;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.model.multipart.ICondition;
import net.minecraft.client.renderer.model.multipart.OrCondition;
import net.minecraft.state.StateContainer;
import org.embeddedt.modernfix.predicate.StatePropertyPredicateHelper;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@Mixin(OrCondition.class)
public class OrConditionMixin {
@Shadow @Final private Iterable<? extends ICondition> conditions;
/**
* @author JellySquid
* @reason Flatten predicates
*/
@Overwrite
public Predicate<BlockState> getPredicate(StateContainer<Block, BlockState> stateManager) {
return StatePropertyPredicateHelper.anyMatch(Streams.stream(this.conditions).map((multipartModelSelector) -> {
return multipartModelSelector.getPredicate(stateManager);
}).collect(Collectors.toList()));
}
}

View File

@ -0,0 +1,77 @@
package org.embeddedt.modernfix.mixin.perf.flatten_model_predicates;
import com.google.common.base.Splitter;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.model.multipart.PropertyValueCondition;
import net.minecraft.state.Property;
import net.minecraft.state.StateContainer;
import org.embeddedt.modernfix.predicate.single.SingleMatchAny;
import org.embeddedt.modernfix.predicate.single.SingleMatchOne;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@Mixin(PropertyValueCondition.class)
public class PropertyValueConditionMixin {
@Shadow @Final private String key;
@Shadow @Final private String value;
@Shadow @Final private static Splitter PIPE_SPLITTER;
/**
* @author JellySquid
* @reason De-duplication
*/
@Overwrite
public Predicate<BlockState> getPredicate(StateContainer<Block, BlockState> stateManager) {
Property<?> property = stateManager.getProperty(this.key);
if (property == null) {
throw new RuntimeException(String.format("Unknown property '%s' on '%s'", this.key, stateManager.getOwner().toString()));
}
String valueString = this.value;
boolean negate = !valueString.isEmpty() && valueString.charAt(0) == '!';
if (negate) {
valueString = valueString.substring(1);
}
List<String> split = PIPE_SPLITTER.splitToList(valueString);
if (split.isEmpty()) {
throw new RuntimeException(String.format("Empty value '%s' for property '%s' on '%s'", this.value, this.key, stateManager.getOwner().toString()));
}
Predicate<BlockState> predicate;
if (split.size() == 1) {
predicate = new SingleMatchOne(property, this.getPropertyValue(stateManager, property, valueString));
} else {
predicate = SingleMatchAny.create(property, split.stream()
.map(str -> this.getPropertyValue(stateManager, property, str))
.collect(Collectors.toList()));
}
return negate ? predicate.negate() : predicate;
}
private Object getPropertyValue(StateContainer<Block, BlockState> stateFactory, Property<?> property, String valueString) {
Object value = property.getValue(valueString)
.orElse(null);
if (value == null) {
throw new RuntimeException(String.format("Unknown value '%s' for property '%s' on '%s' in '%s'",
valueString, this.key, stateFactory.getOwner().toString(), this.value));
}
return value;
}
}

View File

@ -0,0 +1,22 @@
package org.embeddedt.modernfix.predicate;
import java.util.function.Predicate;
public class AllPredicate<T> implements Predicate<T> {
private final Predicate<T>[] predicates;
public AllPredicate(Predicate<T>[] predicates) {
this.predicates = predicates;
}
@Override
public boolean test(T t) {
for (Predicate<T> predicate : this.predicates) {
if (!predicate.test(t)) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,22 @@
package org.embeddedt.modernfix.predicate;
import java.util.function.Predicate;
public class AnyPredicate<T> implements Predicate<T> {
private final Predicate<T>[] predicates;
public AnyPredicate(Predicate<T>[] predicates) {
this.predicates = predicates;
}
@Override
public boolean test(T t) {
for (Predicate<T> predicate : this.predicates) {
if (predicate.test(t)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,33 @@
package org.embeddedt.modernfix.predicate;
import me.jellysquid.mods.hydrogen.common.state.all.AllMatchOneBoolean;
import net.minecraft.block.BlockState;
import org.embeddedt.modernfix.predicate.all.AllMatchOneObject;
import org.embeddedt.modernfix.predicate.any.AllMatchAnyObject;
import org.embeddedt.modernfix.predicate.single.SingleMatchAny;
import org.embeddedt.modernfix.predicate.single.SingleMatchOne;
import java.util.List;
import java.util.function.Predicate;
public class StatePropertyPredicateHelper {
@SuppressWarnings("unchecked")
public static Predicate<BlockState> allMatch(List<Predicate<BlockState>> predicates) {
if (SingleMatchOne.areOfType(predicates)) {
if (SingleMatchOne.valuesMatchType(predicates, Boolean.class)) {
return new AllMatchOneBoolean(predicates);
}
return new AllMatchOneObject(predicates);
} else if (SingleMatchAny.areOfType(predicates)) {
return new AllMatchAnyObject(predicates);
}
return new AllPredicate<>(predicates.toArray(new Predicate[0]));
}
@SuppressWarnings("unchecked")
public static Predicate<BlockState> anyMatch(List<Predicate<BlockState>> predicates) {
return new AnyPredicate<>(predicates.toArray(new Predicate[0]));
}
}

View File

@ -0,0 +1,47 @@
package me.jellysquid.mods.hydrogen.common.state.all;
import net.minecraft.block.BlockState;
import net.minecraft.state.Property;
import org.embeddedt.modernfix.predicate.single.SingleMatchOne;
import java.util.List;
import java.util.function.Predicate;
public class AllMatchOneBoolean implements Predicate<BlockState> {
private final Property<?>[] properties;
private final boolean[] values;
public AllMatchOneBoolean(List<Predicate<BlockState>> list) {
int size = list.size();
this.properties = new Property[size];
this.values = new boolean[size];
for (int i = 0; i < size; i++) {
SingleMatchOne predicate = (SingleMatchOne) list.get(i);
this.properties[i] = predicate.property;
this.values[i] = (boolean) predicate.value;
}
}
public static boolean canReplace(List<Predicate<BlockState>> list) {
return list.stream()
.allMatch(p -> {
return p instanceof SingleMatchOne && ((SingleMatchOne) p).value instanceof Boolean;
});
}
@Override
public boolean test(BlockState blockState) {
for (int i = 0; i < this.properties.length; i++) {
Boolean value = (Boolean) blockState.getValue(this.properties[i]);
if (value != this.values[i]) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,38 @@
package org.embeddedt.modernfix.predicate.all;
import net.minecraft.block.BlockState;
import net.minecraft.state.Property;
import org.embeddedt.modernfix.predicate.single.SingleMatchOne;
import java.util.List;
import java.util.function.Predicate;
public class AllMatchOneObject implements Predicate<BlockState> {
private final Property<?>[] properties;
private final Object[] values;
public AllMatchOneObject(List<Predicate<BlockState>> list) {
int size = list.size();
this.properties = new Property[size];
this.values = new Object[size];
for (int i = 0; i < size; i++) {
SingleMatchOne predicate = (SingleMatchOne) list.get(i);
this.properties[i] = predicate.property;
this.values[i] = predicate.value;
}
}
@Override
public boolean test(BlockState blockState) {
for (int i = 0; i < this.properties.length; i++) {
if (blockState.getValue(this.properties[i]) != this.values[i]) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,39 @@
package org.embeddedt.modernfix.predicate.any;
import net.minecraft.block.BlockState;
import net.minecraft.state.Property;
import org.apache.commons.lang3.ArrayUtils;
import org.embeddedt.modernfix.predicate.single.SingleMatchAny;
import java.util.List;
import java.util.function.Predicate;
public class AllMatchAnyObject implements Predicate<BlockState> {
private final Property<?>[] properties;
private final Object[][] values;
public AllMatchAnyObject(List<Predicate<BlockState>> list) {
int size = list.size();
this.properties = new Property[size];
this.values = new Object[size][];
for (int i = 0; i < size; i++) {
SingleMatchAny predicate = (SingleMatchAny) list.get(i);
this.properties[i] = predicate.property;
this.values[i] = predicate.values;
}
}
@Override
public boolean test(BlockState blockState) {
for (int i = 0; i < this.properties.length; i++) {
if (!ArrayUtils.contains(this.values[i], blockState.getValue(this.properties[i]))) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,63 @@
package org.embeddedt.modernfix.predicate.single;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.block.BlockState;
import net.minecraft.state.Property;
import org.apache.commons.lang3.ArrayUtils;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
public class SingleMatchAny implements Predicate<BlockState> {
public static final ObjectOpenHashSet<SingleMatchAny> PREDICATES = new ObjectOpenHashSet<>();
public final Property<?> property;
public final Object[] values;
private SingleMatchAny(Property<?> property, List<Object> values) {
this.property = property;
this.values = values.toArray();
}
public static SingleMatchAny create(Property<?> property, List<Object> values) {
return PREDICATES.addOrGet(new SingleMatchAny(property, values));
}
public static boolean areOfType(List<Predicate<BlockState>> predicates) {
return predicates.stream()
.allMatch(p -> {
return p instanceof SingleMatchAny;
});
}
public static boolean valuesMatchType(List<Predicate<BlockState>> predicates, Class<?> type) {
return predicates.stream()
.allMatch(p -> {
return p instanceof SingleMatchAny &&
Arrays.stream(((SingleMatchAny) p).values).allMatch(t -> type.isInstance(p));
});
}
@Override
public boolean test(BlockState blockState) {
return ArrayUtils.contains(this.values, blockState.getValue(this.property));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SingleMatchAny that = (SingleMatchAny) o;
return Objects.equals(property, that.property) &&
Arrays.equals(values, that.values);
}
@Override
public int hashCode() {
int result = Objects.hash(property);
result = 31 * result + Arrays.hashCode(values);
return result;
}
}

View File

@ -0,0 +1,36 @@
package org.embeddedt.modernfix.predicate.single;
import net.minecraft.block.BlockState;
import net.minecraft.state.Property;
import java.util.List;
import java.util.function.Predicate;
public class SingleMatchOne implements Predicate<BlockState> {
public final Property<?> property;
public final Object value;
public SingleMatchOne(Property<?> property, Object value) {
this.property = property;
this.value = value;
}
public static boolean areOfType(List<Predicate<BlockState>> predicates) {
return predicates.stream()
.allMatch(p -> {
return p instanceof SingleMatchOne;
});
}
public static boolean valuesMatchType(List<Predicate<BlockState>> predicates, Class<?> type) {
return predicates.stream()
.allMatch(p -> {
return p instanceof SingleMatchOne && type.isInstance(((SingleMatchOne) p).value);
});
}
@Override
public boolean test(BlockState blockState) {
return blockState.getValue(this.property) == this.value;
}
}

View File

@ -7,7 +7,6 @@
"refmap": "modernfix.refmap.json",
"mixins": [
"bugfix.edge_chunk_not_saved.ChunkManagerMixin",
"feature.measure_time.MinecraftMixin",
"perf.remove_biome_temperature_cache.BiomeMixin",
"perf.resourcepacks.ModFileResourcePackMixin",
"perf.resourcepacks.VanillaPackMixin",
@ -28,6 +27,7 @@
"perf.nuke_empty_chunk_sections.MixinChunk"
],
"client": [
"feature.measure_time.MinecraftMixin",
"perf.skip_first_datapack_reload.MinecraftMixin",
"bugfix.concurrency.RenderTypeMixin",
"bugfix.concurrency.MinecraftMixin",
@ -40,7 +40,10 @@
"perf.async_jei.PluginCallerMixin",
"perf.async_jei.RecipeManagerInternalMixin",
"perf.thread_priorities.IntegratedServerMixin",
"safety.BlockColorsMixin"
"safety.BlockColorsMixin",
"perf.flatten_model_predicates.AndConditionMixin",
"perf.flatten_model_predicates.OrConditionMixin",
"perf.flatten_model_predicates.PropertyValueConditionMixin"
],
"injectors": {
"defaultRequire": 1