diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AtlasSkinElement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AtlasSkinElement.kt index a48a6582a..b129d29e0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AtlasSkinElement.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AtlasSkinElement.kt @@ -12,6 +12,7 @@ import net.minecraft.resources.ResourceLocation import net.minecraft.util.Mth import org.lwjgl.opengl.GL11.GL_ALWAYS import ru.dbotthepony.mc.otm.isClient +import java.lang.ref.WeakReference import java.util.stream.Stream 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 @@ -39,10 +64,20 @@ class AtlasSkinElement( check(isClient) { "Invalid realm" } 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) this._textureAtlasSprite = get 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 } @@ -54,10 +89,15 @@ class AtlasSkinElement( return textureAtlasSpriteImpl } - override val u0 get() = textureAtlasSprite.u0 - override val v0 get() = textureAtlasSprite.v0 - override val u1 get() = textureAtlasSprite.u1 - (textureAtlasSprite.u1 - textureAtlasSprite.u0) * (1f - width / spriteWidth) - override val v1 get() = textureAtlasSprite.v1 - (textureAtlasSprite.v1 - textureAtlasSprite.v0) * (1f - height / spriteHeight) + override var u0 = 0f + private set + override var v0 = 0f + private set + override var u1 = 0f + private set + override var v1 = 0f + private set + override val texture: ResourceLocation get() = WidgetAtlasHolder.LOCATION override fun equals(other: Any?): Boolean { @@ -80,8 +120,25 @@ class AtlasSkinElement( companion object { private val keys = HashSet() + private val listeners = ArrayList>() val keysStream: Stream 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() { WidgetAtlasHolder.INSTANCE.queueRebuild() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/DynamicBufferSource.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/DynamicBufferSource.kt index 0b03fcb19..6ab203b59 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/DynamicBufferSource.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/DynamicBufferSource.kt @@ -384,7 +384,7 @@ class DynamicBufferSource(val minimalBufferSize: Int = 0) : MultiBufferSource { companion object { val GUI = DynamicBufferSource() - val WORLD = DynamicBufferSource(8192) + val WORLD = DynamicBufferSource(2 shl 16) /** * Called from LevelRenderer diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetAtlasHolder.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetAtlasHolder.kt index 00d78601d..5ff4019a6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetAtlasHolder.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetAtlasHolder.kt @@ -28,20 +28,14 @@ class WidgetAtlasHolder private constructor(manager: TextureManager) : TextureAt private set var demandsRebuild = false private set - var demandsRebuildAt = 0L - private set - var dirtyUntil = 0L - private set private var resourceManager by Delegates.notNull() private var profileManager by Delegates.notNull() override fun prepare(p_118891_: ResourceManager, p_118892_: ProfilerFiller): TextureAtlas.Preparations { - once = true resourceManager = p_118891_ profileManager = p_118892_ changeset++ - dirtyUntil = System.nanoTime() + 100_000_000L return super.prepare(p_118891_, p_118892_) } @@ -50,36 +44,28 @@ class WidgetAtlasHolder private constructor(manager: TextureManager) : TextureAt resourceManager = p_118895_ profileManager = p_118896_ changeset++ - dirtyUntil = System.nanoTime() + 100_000_000L super.apply(p_118894_, p_118895_, p_118896_) + AtlasSkinElement.notifyListeners() } fun rebuildIfRequired(): Boolean { - if (once && demandsRebuild && demandsRebuildAt <= System.nanoTime() && isClientThread()) { + if (once && demandsRebuild && isClientThread()) { apply(prepare(resourceManager, profileManager), resourceManager, profileManager) } return demandsRebuild } - fun speculateIfMinecraftEngineIsDumDum(): Boolean { - return demandsRebuild || dirtyUntil >= System.nanoTime() - } - public override fun getSprite(p_118902_: ResourceLocation): TextureAtlasSprite { if (!once) { throw IllegalStateException("Trying to get sprite too early") } - rebuildIfRequired() return super.getSprite(p_118902_) } fun queueRebuild() { - if (!demandsRebuild) { - demandsRebuild = true - demandsRebuildAt = System.nanoTime() + 100_000_000L - } + demandsRebuild = true } override fun getResourcesToLoad(): Stream { @@ -101,5 +87,12 @@ class WidgetAtlasHolder private constructor(manager: TextureManager) : TextureAt event.registerReloadListener(INSTANCE) isWidgetAtlasAvailable = true } + + @JvmStatic + fun renderGameHook() { + if (isWidgetAtlasAvailable) { + INSTANCE.rebuildIfRequired() + } + } } } diff --git a/src/main/resources/coremods/code_injector.js b/src/main/resources/coremods/code_injector.js index a775279e7..7edb145e0 100644 --- a/src/main/resources/coremods/code_injector.js +++ b/src/main/resources/coremods/code_injector.js @@ -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': { 'target': { 'type': 'METHOD',