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
|
package moe.nea.firmament.events
|
||||||
|
|
||||||
|
import java.util.Objects
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
import kotlin.jvm.optionals.getOrNull
|
import kotlin.jvm.optionals.getOrNull
|
||||||
|
import net.minecraft.component.DataComponentTypes
|
||||||
import net.minecraft.item.ItemStack
|
import net.minecraft.item.ItemStack
|
||||||
import net.minecraft.util.Identifier
|
import net.minecraft.util.Identifier
|
||||||
import moe.nea.firmament.util.collections.WeakCache
|
import moe.nea.firmament.util.collections.WeakCache
|
||||||
|
import moe.nea.firmament.util.collections.WeakCache.CacheFunction
|
||||||
import moe.nea.firmament.util.mc.IntrospectableItemModelManager
|
import moe.nea.firmament.util.mc.IntrospectableItemModelManager
|
||||||
|
|
||||||
// TODO: assert an order on these events
|
// TODO: assert an order on these events
|
||||||
@@ -14,7 +17,36 @@ data class CustomItemModelEvent(
|
|||||||
var overrideModel: Identifier? = null,
|
var overrideModel: Identifier? = null,
|
||||||
) : FirmamentEvent() {
|
) : FirmamentEvent() {
|
||||||
companion object : FirmamentEventBus<CustomItemModelEvent>() {
|
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
|
@JvmStatic
|
||||||
fun getModelIdentifier(itemStack: ItemStack?, itemModelManager: IntrospectableItemModelManager): Identifier? {
|
fun getModelIdentifier(itemStack: ItemStack?, itemModelManager: IntrospectableItemModelManager): Identifier? {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class DebugLogger(val tag: String) {
|
|||||||
companion object {
|
companion object {
|
||||||
val allInstances = InstanceList<DebugLogger>("DebugLogger")
|
val allInstances = InstanceList<DebugLogger>("DebugLogger")
|
||||||
}
|
}
|
||||||
|
|
||||||
object EnabledLogs : DataHolder<MutableSet<String>>(serializer(), "DebugLogs", ::mutableSetOf)
|
object EnabledLogs : DataHolder<MutableSet<String>>(serializer(), "DebugLogs", ::mutableSetOf)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -17,6 +18,7 @@ class DebugLogger(val tag: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isEnabled() = DeveloperFeatures.isEnabled && EnabledLogs.data.contains(tag)
|
fun isEnabled() = DeveloperFeatures.isEnabled && EnabledLogs.data.contains(tag)
|
||||||
|
fun log(text: String) = log { text }
|
||||||
fun log(text: () -> String) {
|
fun log(text: () -> String) {
|
||||||
if (!isEnabled()) return
|
if (!isEnabled()) return
|
||||||
MC.sendChat(Text.literal(text()))
|
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
|
* 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.
|
* 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) {
|
open class WeakCache<Key : Any, ExtraKey : Any, Value : Any>(val name: String) {
|
||||||
private val queue = object : ReferenceQueue<Key>() {}
|
private val queue = object : ReferenceQueue<Key>() {}
|
||||||
private val map = mutableMapOf<Ref, Value>()
|
private val map = mutableMapOf<Ref, Value>()
|
||||||
|
|
||||||
val size: Int
|
val size: Int
|
||||||
get() {
|
get() {
|
||||||
clearOldReferences()
|
clearOldReferences()
|
||||||
return map.size
|
return map.size
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearOldReferences() {
|
fun clearOldReferences() {
|
||||||
var successCount = 0
|
var successCount = 0
|
||||||
var totalCount = 0
|
var totalCount = 0
|
||||||
while (true) {
|
while (true) {
|
||||||
val reference = queue.poll() ?: break
|
val reference = queue.poll() as WeakCache<*, *, *>.Ref? ?: break
|
||||||
totalCount++
|
totalCount++
|
||||||
if (map.remove(reference) != null)
|
if (reference.shouldBeEvicted() && map.remove(reference) != null)
|
||||||
successCount++
|
successCount++
|
||||||
}
|
}
|
||||||
if (totalCount > 0)
|
if (totalCount > 0)
|
||||||
logger.log { "Cleared $successCount/$totalCount references from queue" }
|
logger.log("Cleared $successCount/$totalCount references from queue")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun get(key: Key, extraData: ExtraKey): Value? {
|
open fun mkRef(key: Key, extraData: ExtraKey): Ref {
|
||||||
clearOldReferences()
|
return Ref(key, extraData)
|
||||||
return map[Ref(key, extraData)]
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun put(key: Key, extraData: ExtraKey, value: Value) {
|
fun get(key: Key, extraData: ExtraKey): Value? {
|
||||||
clearOldReferences()
|
clearOldReferences()
|
||||||
map[Ref(key, extraData)] = value
|
return map[mkRef(key, extraData)]
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getOrPut(key: Key, extraData: ExtraKey, value: (Key, ExtraKey) -> Value): Value {
|
fun put(key: Key, extraData: ExtraKey, value: Value) {
|
||||||
clearOldReferences()
|
clearOldReferences()
|
||||||
return map.getOrPut(Ref(key, extraData)) { value(key, extraData) }
|
map[mkRef(key, extraData)] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clear() {
|
fun getOrPut(key: Key, extraData: ExtraKey, value: (Key, ExtraKey) -> Value): Value {
|
||||||
map.clear()
|
clearOldReferences()
|
||||||
}
|
return map.getOrPut(mkRef(key, extraData)) { value(key, extraData) }
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
fun clear() {
|
||||||
allInstances.add(this)
|
map.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
init {
|
||||||
val allInstances = InstanceList<WeakCache<*, *, *>>("WeakCaches")
|
allInstances.add(this)
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <Key : Any, ExtraKey : Any, Value : Any> memoize(name: String, function: (Key, ExtraKey) -> Value):
|
companion object {
|
||||||
CacheFunction.WithExtraData<Key, ExtraKey, Value> {
|
val allInstances = InstanceList<WeakCache<*, *, *>>("WeakCaches")
|
||||||
return CacheFunction.WithExtraData(WeakCache(name), function)
|
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(
|
fun <Key : Any, ExtraKey : Any, Value : Any> dontMemoize(name: String, function: (Key, ExtraKey) -> Value) = function
|
||||||
weakInstance: Key,
|
fun <Key : Any, ExtraKey : Any, Value : Any> memoize(name: String, function: (Key, ExtraKey) -> Value):
|
||||||
val extraData: ExtraKey,
|
CacheFunction.WithExtraData<Key, ExtraKey, Value> {
|
||||||
) : WeakReference<Key>(weakInstance, queue) {
|
return CacheFunction.WithExtraData(WeakCache(name), function)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
open inner class Ref(
|
||||||
return hashCode
|
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 {
|
override fun hashCode(): Int {
|
||||||
val cache: WeakCache<*, *, *>
|
return hashCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class NoExtraData<Key : Any, Value : Any>(
|
interface CacheFunction {
|
||||||
override val cache: WeakCache<Key, Unit, Value>,
|
val cache: WeakCache<*, *, *>
|
||||||
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>(
|
data class NoExtraData<Key : Any, Value : Any>(
|
||||||
override val cache: WeakCache<Key, ExtraKey, Value>,
|
override val cache: WeakCache<Key, Unit, Value>,
|
||||||
val wrapped: (Key, ExtraKey) -> Value,
|
val wrapped: (Key) -> Value,
|
||||||
) : CacheFunction, (Key, ExtraKey) -> Value {
|
) : CacheFunction, (Key) -> Value {
|
||||||
override fun invoke(p1: Key, p2: ExtraKey): Value {
|
override fun invoke(p1: Key): Value {
|
||||||
return cache.getOrPut(p1, p2, wrapped)
|
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 =
|
val overrideCache =
|
||||||
WeakCache.memoize<ItemStack, EquipmentSlot, Optional<EquippableComponent>>("ArmorOverrides") { stack, slot ->
|
WeakCache.dontMemoize<ItemStack, EquipmentSlot, Optional<EquippableComponent>>("ArmorOverrides") { stack, slot ->
|
||||||
val id = stack.skyBlockId ?: return@memoize Optional.empty()
|
val id = stack.skyBlockId ?: return@dontMemoize Optional.empty()
|
||||||
val override = overrides[id.neuItem] ?: return@memoize Optional.empty()
|
val override = overrides[id.neuItem] ?: return@dontMemoize Optional.empty()
|
||||||
for (suboverride in override.overrides) {
|
for (suboverride in override.overrides) {
|
||||||
if (suboverride.predicate.test(stack)) {
|
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()
|
var overrides: Map<String, ArmorOverride> = mapOf()
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ object CustomSkyBlockTextures : FirmamentFeature {
|
|||||||
listOf(
|
listOf(
|
||||||
skullTextureCache.cache,
|
skullTextureCache.cache,
|
||||||
CustomItemModelEvent.cache.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