feat: Improve performance of slime particles and armour with texture packs
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
package moe.nea.firmament.events
|
||||
|
||||
import java.util.Objects
|
||||
import java.util.Optional
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.component.DataComponentTypes
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.util.collections.WeakCache
|
||||
import moe.nea.firmament.util.collections.WeakCache.CacheFunction
|
||||
import moe.nea.firmament.util.mc.IntrospectableItemModelManager
|
||||
|
||||
// TODO: assert an order on these events
|
||||
@@ -14,7 +17,36 @@ data class CustomItemModelEvent(
|
||||
var overrideModel: Identifier? = null,
|
||||
) : FirmamentEvent() {
|
||||
companion object : FirmamentEventBus<CustomItemModelEvent>() {
|
||||
val cache = WeakCache.memoize("ItemModelIdentifier", ::getModelIdentifier0)
|
||||
val weakCache =
|
||||
object : WeakCache<ItemStack, IntrospectableItemModelManager, Optional<Identifier>>("ItemModelIdentifier") {
|
||||
override fun mkRef(
|
||||
key: ItemStack,
|
||||
extraData: IntrospectableItemModelManager
|
||||
): WeakCache<ItemStack, IntrospectableItemModelManager, Optional<Identifier>>.Ref {
|
||||
return IRef(key, extraData)
|
||||
}
|
||||
|
||||
inner class IRef(weakInstance: ItemStack, data: IntrospectableItemModelManager) :
|
||||
Ref(weakInstance, data) {
|
||||
override fun shouldBeEvicted(): Boolean = false
|
||||
val isSimpleStack = weakInstance.componentChanges.isEmpty || (weakInstance.componentChanges.size() == 1 && weakInstance.get(
|
||||
DataComponentTypes.CUSTOM_DATA)?.isEmpty == true)
|
||||
val item = weakInstance.item
|
||||
override fun hashCode(): Int {
|
||||
if (isSimpleStack)
|
||||
return Objects.hash(item, extraData)
|
||||
return super.hashCode()
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other is IRef && isSimpleStack) {
|
||||
return other.isSimpleStack && item == other.item
|
||||
}
|
||||
return super.equals(other)
|
||||
}
|
||||
}
|
||||
}
|
||||
val cache = CacheFunction.WithExtraData(weakCache, ::getModelIdentifier0)
|
||||
|
||||
@JvmStatic
|
||||
fun getModelIdentifier(itemStack: ItemStack?, itemModelManager: IntrospectableItemModelManager): Identifier? {
|
||||
|
||||
@@ -10,6 +10,7 @@ class DebugLogger(val tag: String) {
|
||||
companion object {
|
||||
val allInstances = InstanceList<DebugLogger>("DebugLogger")
|
||||
}
|
||||
|
||||
object EnabledLogs : DataHolder<MutableSet<String>>(serializer(), "DebugLogs", ::mutableSetOf)
|
||||
|
||||
init {
|
||||
@@ -17,6 +18,7 @@ class DebugLogger(val tag: String) {
|
||||
}
|
||||
|
||||
fun isEnabled() = DeveloperFeatures.isEnabled && EnabledLogs.data.contains(tag)
|
||||
fun log(text: String) = log { text }
|
||||
fun log(text: () -> String) {
|
||||
if (!isEnabled()) return
|
||||
MC.sendChat(Text.literal(text()))
|
||||
|
||||
@@ -9,102 +9,108 @@ import moe.nea.firmament.features.debug.DebugLogger
|
||||
* the key. Each key can have additional extra data that is used to look up values. That extra data is not required to
|
||||
* be a life reference. The main Key is compared using strict reference equality. This map is not synchronized.
|
||||
*/
|
||||
class WeakCache<Key : Any, ExtraKey : Any, Value : Any>(val name: String) {
|
||||
private val queue = object : ReferenceQueue<Key>() {}
|
||||
private val map = mutableMapOf<Ref, Value>()
|
||||
open class WeakCache<Key : Any, ExtraKey : Any, Value : Any>(val name: String) {
|
||||
private val queue = object : ReferenceQueue<Key>() {}
|
||||
private val map = mutableMapOf<Ref, Value>()
|
||||
|
||||
val size: Int
|
||||
get() {
|
||||
clearOldReferences()
|
||||
return map.size
|
||||
}
|
||||
val size: Int
|
||||
get() {
|
||||
clearOldReferences()
|
||||
return map.size
|
||||
}
|
||||
|
||||
fun clearOldReferences() {
|
||||
var successCount = 0
|
||||
var totalCount = 0
|
||||
while (true) {
|
||||
val reference = queue.poll() ?: break
|
||||
totalCount++
|
||||
if (map.remove(reference) != null)
|
||||
successCount++
|
||||
}
|
||||
if (totalCount > 0)
|
||||
logger.log { "Cleared $successCount/$totalCount references from queue" }
|
||||
}
|
||||
fun clearOldReferences() {
|
||||
var successCount = 0
|
||||
var totalCount = 0
|
||||
while (true) {
|
||||
val reference = queue.poll() as WeakCache<*, *, *>.Ref? ?: break
|
||||
totalCount++
|
||||
if (reference.shouldBeEvicted() && map.remove(reference) != null)
|
||||
successCount++
|
||||
}
|
||||
if (totalCount > 0)
|
||||
logger.log("Cleared $successCount/$totalCount references from queue")
|
||||
}
|
||||
|
||||
fun get(key: Key, extraData: ExtraKey): Value? {
|
||||
clearOldReferences()
|
||||
return map[Ref(key, extraData)]
|
||||
}
|
||||
open fun mkRef(key: Key, extraData: ExtraKey): Ref {
|
||||
return Ref(key, extraData)
|
||||
}
|
||||
|
||||
fun put(key: Key, extraData: ExtraKey, value: Value) {
|
||||
clearOldReferences()
|
||||
map[Ref(key, extraData)] = value
|
||||
}
|
||||
fun get(key: Key, extraData: ExtraKey): Value? {
|
||||
clearOldReferences()
|
||||
return map[mkRef(key, extraData)]
|
||||
}
|
||||
|
||||
fun getOrPut(key: Key, extraData: ExtraKey, value: (Key, ExtraKey) -> Value): Value {
|
||||
clearOldReferences()
|
||||
return map.getOrPut(Ref(key, extraData)) { value(key, extraData) }
|
||||
}
|
||||
fun put(key: Key, extraData: ExtraKey, value: Value) {
|
||||
clearOldReferences()
|
||||
map[mkRef(key, extraData)] = value
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
map.clear()
|
||||
}
|
||||
fun getOrPut(key: Key, extraData: ExtraKey, value: (Key, ExtraKey) -> Value): Value {
|
||||
clearOldReferences()
|
||||
return map.getOrPut(mkRef(key, extraData)) { value(key, extraData) }
|
||||
}
|
||||
|
||||
init {
|
||||
allInstances.add(this)
|
||||
}
|
||||
fun clear() {
|
||||
map.clear()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val allInstances = InstanceList<WeakCache<*, *, *>>("WeakCaches")
|
||||
private val logger = DebugLogger("WeakCache")
|
||||
fun <Key : Any, Value : Any> memoize(name: String, function: (Key) -> Value):
|
||||
CacheFunction.NoExtraData<Key, Value> {
|
||||
return CacheFunction.NoExtraData(WeakCache(name), function)
|
||||
}
|
||||
init {
|
||||
allInstances.add(this)
|
||||
}
|
||||
|
||||
fun <Key : Any, ExtraKey : Any, Value : Any> memoize(name: String, function: (Key, ExtraKey) -> Value):
|
||||
CacheFunction.WithExtraData<Key, ExtraKey, Value> {
|
||||
return CacheFunction.WithExtraData(WeakCache(name), function)
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
val allInstances = InstanceList<WeakCache<*, *, *>>("WeakCaches")
|
||||
private val logger = DebugLogger("WeakCache")
|
||||
fun <Key : Any, Value : Any> memoize(name: String, function: (Key) -> Value):
|
||||
CacheFunction.NoExtraData<Key, Value> {
|
||||
return CacheFunction.NoExtraData(WeakCache(name), function)
|
||||
}
|
||||
|
||||
inner class Ref(
|
||||
weakInstance: Key,
|
||||
val extraData: ExtraKey,
|
||||
) : WeakReference<Key>(weakInstance, queue) {
|
||||
val hashCode = System.identityHashCode(weakInstance) * 31 + extraData.hashCode()
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is WeakCache<*, *, *>.Ref) return false
|
||||
return other.hashCode == this.hashCode
|
||||
&& other.get() === this.get()
|
||||
&& other.extraData == this.extraData
|
||||
}
|
||||
fun <Key : Any, ExtraKey : Any, Value : Any> dontMemoize(name: String, function: (Key, ExtraKey) -> Value) = function
|
||||
fun <Key : Any, ExtraKey : Any, Value : Any> memoize(name: String, function: (Key, ExtraKey) -> Value):
|
||||
CacheFunction.WithExtraData<Key, ExtraKey, Value> {
|
||||
return CacheFunction.WithExtraData(WeakCache(name), function)
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return hashCode
|
||||
}
|
||||
}
|
||||
open inner class Ref(
|
||||
weakInstance: Key,
|
||||
val extraData: ExtraKey,
|
||||
) : WeakReference<Key>(weakInstance, queue) {
|
||||
open fun shouldBeEvicted() = true
|
||||
val hashCode = System.identityHashCode(weakInstance) * 31 + extraData.hashCode()
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is WeakCache<*, *, *>.Ref) return false
|
||||
return other.hashCode == this.hashCode
|
||||
&& other.get() === this.get()
|
||||
&& other.extraData == this.extraData
|
||||
}
|
||||
|
||||
interface CacheFunction {
|
||||
val cache: WeakCache<*, *, *>
|
||||
override fun hashCode(): Int {
|
||||
return hashCode
|
||||
}
|
||||
}
|
||||
|
||||
data class NoExtraData<Key : Any, Value : Any>(
|
||||
override val cache: WeakCache<Key, Unit, Value>,
|
||||
val wrapped: (Key) -> Value,
|
||||
) : CacheFunction, (Key) -> Value {
|
||||
override fun invoke(p1: Key): Value {
|
||||
return cache.getOrPut(p1, Unit, { a, _ -> wrapped(a) })
|
||||
}
|
||||
}
|
||||
interface CacheFunction {
|
||||
val cache: WeakCache<*, *, *>
|
||||
|
||||
data class WithExtraData<Key : Any, ExtraKey : Any, Value : Any>(
|
||||
override val cache: WeakCache<Key, ExtraKey, Value>,
|
||||
val wrapped: (Key, ExtraKey) -> Value,
|
||||
) : CacheFunction, (Key, ExtraKey) -> Value {
|
||||
override fun invoke(p1: Key, p2: ExtraKey): Value {
|
||||
return cache.getOrPut(p1, p2, wrapped)
|
||||
}
|
||||
}
|
||||
}
|
||||
data class NoExtraData<Key : Any, Value : Any>(
|
||||
override val cache: WeakCache<Key, Unit, Value>,
|
||||
val wrapped: (Key) -> Value,
|
||||
) : CacheFunction, (Key) -> Value {
|
||||
override fun invoke(p1: Key): Value {
|
||||
return cache.getOrPut(p1, Unit, { a, _ -> wrapped(a) })
|
||||
}
|
||||
}
|
||||
|
||||
data class WithExtraData<Key : Any, ExtraKey : Any, Value : Any>(
|
||||
override val cache: WeakCache<Key, ExtraKey, Value>,
|
||||
val wrapped: (Key, ExtraKey) -> Value,
|
||||
) : CacheFunction, (Key, ExtraKey) -> Value {
|
||||
override fun invoke(p1: Key, p2: ExtraKey): Value {
|
||||
return cache.getOrPut(p1, p2, wrapped)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,16 +85,19 @@ object CustomGlobalArmorOverrides {
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: BipedEntityRenderer.getEquippedStack create copies of itemstacks for rendering. This means this cache is essentially useless
|
||||
// If i figure out how to circumvent this (maybe track the origin of those copied itemstacks in some sort of variable in the itemstack to track back the original instance) i should reenable this cache.
|
||||
// Then also re add this to the cache clearing function
|
||||
val overrideCache =
|
||||
WeakCache.memoize<ItemStack, EquipmentSlot, Optional<EquippableComponent>>("ArmorOverrides") { stack, slot ->
|
||||
val id = stack.skyBlockId ?: return@memoize Optional.empty()
|
||||
val override = overrides[id.neuItem] ?: return@memoize Optional.empty()
|
||||
WeakCache.dontMemoize<ItemStack, EquipmentSlot, Optional<EquippableComponent>>("ArmorOverrides") { stack, slot ->
|
||||
val id = stack.skyBlockId ?: return@dontMemoize Optional.empty()
|
||||
val override = overrides[id.neuItem] ?: return@dontMemoize Optional.empty()
|
||||
for (suboverride in override.overrides) {
|
||||
if (suboverride.predicate.test(stack)) {
|
||||
return@memoize resolveComponent(slot, suboverride.modelIdentifier).intoOptional()
|
||||
return@dontMemoize resolveComponent(slot, suboverride.modelIdentifier).intoOptional()
|
||||
}
|
||||
}
|
||||
return@memoize resolveComponent(slot, override.modelIdentifier).intoOptional()
|
||||
return@dontMemoize resolveComponent(slot, override.modelIdentifier).intoOptional()
|
||||
}
|
||||
|
||||
var overrides: Map<String, ArmorOverride> = mapOf()
|
||||
|
||||
@@ -45,7 +45,7 @@ object CustomSkyBlockTextures : FirmamentFeature {
|
||||
listOf(
|
||||
skullTextureCache.cache,
|
||||
CustomItemModelEvent.cache.cache,
|
||||
CustomGlobalArmorOverrides.overrideCache.cache
|
||||
// TODO: re-add this once i figure out how to make the cache useful again CustomGlobalArmorOverrides.overrideCache.cache
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user