Optimize atlas skin element

Achieved with CryCry 3
This commit is contained in:
DBotThePony 2022-10-08 14:17:50 +07:00
parent f7c3f5efeb
commit 980e2a8f30
Signed by: DBot
GPG Key ID: DCC23B5715498507
4 changed files with 94 additions and 23 deletions

View File

@ -12,6 +12,7 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth import net.minecraft.util.Mth
import org.lwjgl.opengl.GL11.GL_ALWAYS import org.lwjgl.opengl.GL11.GL_ALWAYS
import ru.dbotthepony.mc.otm.isClient import ru.dbotthepony.mc.otm.isClient
import java.lang.ref.WeakReference
import java.util.stream.Stream import java.util.stream.Stream
class AtlasSkinElement( class AtlasSkinElement(
@ -30,6 +31,30 @@ class AtlasSkinElement(
} }
} }
} }
// optimistic: we should not create a lot of instances of this class
synchronized(listeners) {
listeners.add(WeakReference(this))
val iterator = listeners.listIterator()
for (ref in iterator) {
val value = ref.get()
if (value == null) {
iterator.remove()
}
}
}
if (isWidgetAtlasAvailable) {
earlyBindingImpl()
}
}
private fun earlyBindingImpl() {
if (WidgetAtlasHolder.INSTANCE.once) {
textureAtlasSpriteImpl
}
} }
private var changeset = -1 private var changeset = -1
@ -39,10 +64,20 @@ class AtlasSkinElement(
check(isClient) { "Invalid realm" } check(isClient) { "Invalid realm" }
val _textureAtlasSprite = _textureAtlasSprite val _textureAtlasSprite = _textureAtlasSprite
if (_textureAtlasSprite == null || _textureAtlasSprite.name.let { it.namespace == "minecraft" && it.path == "missingno" } || changeset != WidgetAtlasHolder.INSTANCE.changeset || WidgetAtlasHolder.INSTANCE.speculateIfMinecraftEngineIsDumDum()) { if (
_textureAtlasSprite == null ||
_textureAtlasSprite.name.let { it.namespace == "minecraft" && it.path == "missingno" } ||
changeset != WidgetAtlasHolder.INSTANCE.changeset
) {
val get = WidgetAtlasHolder.INSTANCE.getSprite(location) val get = WidgetAtlasHolder.INSTANCE.getSprite(location)
this._textureAtlasSprite = get this._textureAtlasSprite = get
changeset = WidgetAtlasHolder.INSTANCE.changeset changeset = WidgetAtlasHolder.INSTANCE.changeset
u0 = get.u0
v0 = get.v0
u1 = get.u1 - (get.u1 - get.u0) * (1f - width / spriteWidth)
v1 = get.v1 - (get.v1 - get.v0) * (1f - height / spriteHeight)
return get return get
} }
@ -54,10 +89,15 @@ class AtlasSkinElement(
return textureAtlasSpriteImpl return textureAtlasSpriteImpl
} }
override val u0 get() = textureAtlasSprite.u0 override var u0 = 0f
override val v0 get() = textureAtlasSprite.v0 private set
override val u1 get() = textureAtlasSprite.u1 - (textureAtlasSprite.u1 - textureAtlasSprite.u0) * (1f - width / spriteWidth) override var v0 = 0f
override val v1 get() = textureAtlasSprite.v1 - (textureAtlasSprite.v1 - textureAtlasSprite.v0) * (1f - height / spriteHeight) private set
override var u1 = 0f
private set
override var v1 = 0f
private set
override val texture: ResourceLocation get() = WidgetAtlasHolder.LOCATION override val texture: ResourceLocation get() = WidgetAtlasHolder.LOCATION
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@ -80,8 +120,25 @@ class AtlasSkinElement(
companion object { companion object {
private val keys = HashSet<ResourceLocation>() private val keys = HashSet<ResourceLocation>()
private val listeners = ArrayList<WeakReference<AtlasSkinElement>>()
val keysStream: Stream<ResourceLocation> get() = keys.stream() val keysStream: Stream<ResourceLocation> get() = keys.stream()
fun notifyListeners() {
synchronized(listeners) {
val iterator = listeners.listIterator()
for (ref in iterator) {
val value = ref.get()
if (value == null) {
iterator.remove()
} else {
value.textureAtlasSpriteImpl
}
}
}
}
private fun queueRebuild() { private fun queueRebuild() {
WidgetAtlasHolder.INSTANCE.queueRebuild() WidgetAtlasHolder.INSTANCE.queueRebuild()
} }

View File

@ -384,7 +384,7 @@ class DynamicBufferSource(val minimalBufferSize: Int = 0) : MultiBufferSource {
companion object { companion object {
val GUI = DynamicBufferSource() val GUI = DynamicBufferSource()
val WORLD = DynamicBufferSource(8192) val WORLD = DynamicBufferSource(2 shl 16)
/** /**
* Called from LevelRenderer * Called from LevelRenderer

View File

@ -28,20 +28,14 @@ class WidgetAtlasHolder private constructor(manager: TextureManager) : TextureAt
private set private set
var demandsRebuild = false var demandsRebuild = false
private set private set
var demandsRebuildAt = 0L
private set
var dirtyUntil = 0L
private set
private var resourceManager by Delegates.notNull<ResourceManager>() private var resourceManager by Delegates.notNull<ResourceManager>()
private var profileManager by Delegates.notNull<ProfilerFiller>() private var profileManager by Delegates.notNull<ProfilerFiller>()
override fun prepare(p_118891_: ResourceManager, p_118892_: ProfilerFiller): TextureAtlas.Preparations { override fun prepare(p_118891_: ResourceManager, p_118892_: ProfilerFiller): TextureAtlas.Preparations {
once = true
resourceManager = p_118891_ resourceManager = p_118891_
profileManager = p_118892_ profileManager = p_118892_
changeset++ changeset++
dirtyUntil = System.nanoTime() + 100_000_000L
return super.prepare(p_118891_, p_118892_) return super.prepare(p_118891_, p_118892_)
} }
@ -50,36 +44,28 @@ class WidgetAtlasHolder private constructor(manager: TextureManager) : TextureAt
resourceManager = p_118895_ resourceManager = p_118895_
profileManager = p_118896_ profileManager = p_118896_
changeset++ changeset++
dirtyUntil = System.nanoTime() + 100_000_000L
super.apply(p_118894_, p_118895_, p_118896_) super.apply(p_118894_, p_118895_, p_118896_)
AtlasSkinElement.notifyListeners()
} }
fun rebuildIfRequired(): Boolean { fun rebuildIfRequired(): Boolean {
if (once && demandsRebuild && demandsRebuildAt <= System.nanoTime() && isClientThread()) { if (once && demandsRebuild && isClientThread()) {
apply(prepare(resourceManager, profileManager), resourceManager, profileManager) apply(prepare(resourceManager, profileManager), resourceManager, profileManager)
} }
return demandsRebuild return demandsRebuild
} }
fun speculateIfMinecraftEngineIsDumDum(): Boolean {
return demandsRebuild || dirtyUntil >= System.nanoTime()
}
public override fun getSprite(p_118902_: ResourceLocation): TextureAtlasSprite { public override fun getSprite(p_118902_: ResourceLocation): TextureAtlasSprite {
if (!once) { if (!once) {
throw IllegalStateException("Trying to get sprite too early") throw IllegalStateException("Trying to get sprite too early")
} }
rebuildIfRequired()
return super.getSprite(p_118902_) return super.getSprite(p_118902_)
} }
fun queueRebuild() { fun queueRebuild() {
if (!demandsRebuild) { demandsRebuild = true
demandsRebuild = true
demandsRebuildAt = System.nanoTime() + 100_000_000L
}
} }
override fun getResourcesToLoad(): Stream<ResourceLocation> { override fun getResourcesToLoad(): Stream<ResourceLocation> {
@ -101,5 +87,12 @@ class WidgetAtlasHolder private constructor(manager: TextureManager) : TextureAt
event.registerReloadListener(INSTANCE) event.registerReloadListener(INSTANCE)
isWidgetAtlasAvailable = true isWidgetAtlasAvailable = true
} }
@JvmStatic
fun renderGameHook() {
if (isWidgetAtlasAvailable) {
INSTANCE.rebuildIfRequired()
}
}
} }
} }

View File

@ -685,6 +685,27 @@ function initializeCoreMod() {
} }
}, },
'GameRenderer#render hook': {
'target': {
'type': 'METHOD',
'class': 'net.minecraft.client.renderer.GameRenderer',
'methodName': ASMAPI.mapMethod('m_109093_'), // render
'methodDesc': '(FJZ)V'
},
'transformer': function(node) {
node.instructions.insert(new MethodInsnNode(
opcodesRemapped.invokestatic,
'ru/dbotthepony/mc/otm/client/render/WidgetAtlasHolder',
'renderGameHook',
'()V',
false
))
return node
}
},
'LevelRenderer DynamicBufferSource callback': { 'LevelRenderer DynamicBufferSource callback': {
'target': { 'target': {
'type': 'METHOD', 'type': 'METHOD',