From 26f172df71a095e3070b83c4948ae9ffae494acb Mon Sep 17 00:00:00 2001 From: Bixilon Date: Sat, 17 Jun 2023 01:45:16 +0200 Subject: [PATCH] texture array: only load image properties when needed, allow unnamed textures --- .../font/types/bitmap/BitmapFontTypeTest.kt | 3 +- .../dummy/texture/DummyStaticTextureArray.kt | 10 ++- .../font/types/bitmap/BitmapFontType.kt | 2 +- .../unicode/legacy/LegacyUnicodeFontType.kt | 2 +- .../base/texture/array/StaticTextureArray.kt | 8 +-- .../opengl/texture/OpenGLTextureArray.kt | 63 +++++++++++++------ 6 files changed, 58 insertions(+), 30 deletions(-) diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/font/types/bitmap/BitmapFontTypeTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/font/types/bitmap/BitmapFontTypeTest.kt index b5ae88db3..3ba92ed57 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/font/types/bitmap/BitmapFontTypeTest.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/font/types/bitmap/BitmapFontTypeTest.kt @@ -3,6 +3,7 @@ package de.bixilon.minosoft.gui.rendering.font.types.bitmap import de.bixilon.kotlinglm.vec2.Vec2 import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.minosoft.gui.rendering.font.types.empty.EmptyCodeRenderer +import de.bixilon.minosoft.gui.rendering.system.base.texture.data.TextureData import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture import de.bixilon.minosoft.gui.rendering.system.dummy.texture.DummyTexture import org.testng.Assert.* @@ -47,7 +48,7 @@ class BitmapFontTypeTest { val texture = DummyTexture() texture.size = size - texture.data = buffer + texture.data = TextureData(size, buffer) return texture } diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/texture/DummyStaticTextureArray.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/texture/DummyStaticTextureArray.kt index 724218511..1e39aafbd 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/texture/DummyStaticTextureArray.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/texture/DummyStaticTextureArray.kt @@ -25,11 +25,17 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.sprite.SpriteAnimat import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture class DummyStaticTextureArray(renderSystem: RenderSystem) : StaticTextureArray { - override val textures: MutableMap = synchronizedMapOf() + private val textures: MutableMap = synchronizedMapOf() override val animator: SpriteAnimator = SpriteAnimator(renderSystem) override val state: TextureArrayStates = TextureArrayStates.DECLARED - override fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean, default: (mipmaps: Boolean) -> Texture): Texture { + override fun get(resourceLocation: ResourceLocation): Texture? = textures[resourceLocation] + + override fun pushTexture(texture: Texture) { + TODO("Not yet implemented") + } + + override fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean, properties: Boolean, default: (mipmaps: Boolean) -> Texture): Texture { return textures.getOrPut(resourceLocation) { DummyTexture() } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/font/types/bitmap/BitmapFontType.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/font/types/bitmap/BitmapFontType.kt index ece16da15..538a0572a 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/font/types/bitmap/BitmapFontType.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/font/types/bitmap/BitmapFontType.kt @@ -72,7 +72,7 @@ class BitmapFontType( private fun load(file: ResourceLocation, height: Int, ascent: Int, chars: List, context: RenderContext): BitmapFontType? { if (chars.isEmpty() || height <= 0) return null - val texture = context.textureManager.staticTextures.createTexture(file, mipmaps = false) + val texture = context.textureManager.staticTextures.createTexture(file, mipmaps = false, properties = false) texture.load(context) // force load it, we need to calculate the width of every char return load(texture, height, ascent, chars.codePoints()) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/font/types/unicode/legacy/LegacyUnicodeFontType.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/font/types/unicode/legacy/LegacyUnicodeFontType.kt index 8a0543544..5d25b6c75 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/font/types/unicode/legacy/LegacyUnicodeFontType.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/font/types/unicode/legacy/LegacyUnicodeFontType.kt @@ -83,7 +83,7 @@ class LegacyUnicodeFontType( sizes.skip(PAGE_SIZE.toLong()) return } - val texture = textures.createTexture(textureFile, mipmaps = false) + val texture = textures.createTexture(textureFile, mipmaps = false, properties = false) loadPage(pageId, texture, chars, sizes) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/array/StaticTextureArray.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/array/StaticTextureArray.kt index 880248ada..e76dca9db 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/array/StaticTextureArray.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/array/StaticTextureArray.kt @@ -20,15 +20,13 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.file.PNGTexture interface StaticTextureArray : TextureArray { - val textures: MutableMap val animator: SpriteAnimator val state: TextureArrayStates - fun get(resourceLocation: ResourceLocation): Texture? { - return textures[resourceLocation] - } + fun get(resourceLocation: ResourceLocation): Texture? - fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean = true, default: (mipmaps: Boolean) -> Texture = { PNGTexture(resourceLocation, mipmaps = it) }): Texture + fun pushTexture(texture: Texture) + fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean = true, properties: Boolean = true, default: (mipmaps: Boolean) -> Texture = { PNGTexture(resourceLocation, mipmaps = it) }): Texture fun preLoad(latch: AbstractLatch) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt index 120faa40c..65c75e9d0 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt @@ -44,32 +44,45 @@ import org.lwjgl.opengl.GL13.GL_TEXTURE0 import org.lwjgl.opengl.GL13.glActiveTexture import org.lwjgl.opengl.GL30.GL_TEXTURE_2D_ARRAY import java.nio.ByteBuffer +import java.util.concurrent.atomic.AtomicInteger class OpenGLTextureArray( val context: RenderContext, private val loadTexturesAsync: Boolean = true, - override val textures: MutableMap = mutableMapOf(), ) : StaticTextureArray { - override val animator = SpriteAnimator(context.renderSystem) + private val namedTextures: MutableMap = mutableMapOf() + private val otherTextures: MutableSet = mutableSetOf() private var textureIds = IntArray(TEXTURE_RESOLUTION_ID_MAP.size) { -1 } + override val animator = SpriteAnimator(context.renderSystem) override var state: TextureArrayStates = TextureArrayStates.DECLARED private set private val texturesByResolution = Array>(TEXTURE_RESOLUTION_ID_MAP.size) { mutableListOf() } private val lastTextureId = IntArray(TEXTURE_RESOLUTION_ID_MAP.size) + override fun get(resourceLocation: ResourceLocation): Texture? { + return this.namedTextures[resourceLocation] + } @Synchronized - override fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean, default: (mipmaps: Boolean) -> Texture): Texture { - textures[resourceLocation]?.let { return it } + override fun pushTexture(texture: Texture) { + otherTextures += texture + if (texture.state != TextureStates.LOADED && loadTexturesAsync) { + DefaultThreadPool += ForcePooledRunnable { texture.load(context) } + } + } + + @Synchronized + override fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean, properties: Boolean, default: (mipmaps: Boolean) -> Texture): Texture { + namedTextures[resourceLocation]?.let { return it } // load .mcmeta - val properties = readImageProperties(resourceLocation) ?: ImageProperties() // TODO: That kills performance + val properties = if (properties) readImageProperties(resourceLocation) ?: ImageProperties.DEFAULT else ImageProperties.DEFAULT // TODO: That kills performance - val texture = if (properties.animation == null) default(mipmaps) else SpriteTexture(default(mipmaps)) + val texture = if (properties.animation == null) default(mipmaps) else SpriteTexture(default(false)) texture.properties = properties - textures[resourceLocation] = texture + namedTextures[resourceLocation] = texture if (loadTexturesAsync) { DefaultThreadPool += ForcePooledRunnable { texture.load(context) } } @@ -88,23 +101,18 @@ class OpenGLTextureArray( } - @Synchronized - override fun preLoad(latch: AbstractLatch) { - if (state == TextureArrayStates.LOADED || state == TextureArrayStates.PRE_LOADED) { - return - } - val preLoadLatch = SimpleLatch(textures.size) - for (texture in textures.values) { + private fun preLoad(latch: AbstractLatch, textures: Collection) { + for (texture in textures) { if (texture.state != TextureStates.DECLARED) { - preLoadLatch.dec() + latch.dec() continue } DefaultThreadPool += SimplePoolRunnable(ThreadPool.HIGH) { texture.load(context) } } - preLoadLatch.await() + } - var lastAnimationIndex = 0 - for (texture in textures.values) { + private fun preLoad(animationIndex: AtomicInteger, textures: Collection) { + for (texture in textures) { check(texture.size.x <= TEXTURE_MAX_RESOLUTION) { "Texture's width exceeds $TEXTURE_MAX_RESOLUTION (${texture.size.x}" } check(texture.size.y <= TEXTURE_MAX_RESOLUTION) { "Texture's height exceeds $TEXTURE_MAX_RESOLUTION (${texture.size.y}" } @@ -130,7 +138,7 @@ class OpenGLTextureArray( val array = TextureArrayProperties(uvEnd ?: Vec2(1.0f, 1.0f), arrayResolution, singlePixelSize) if (texture is SpriteTexture) { - val animationIndex = lastAnimationIndex++ + val animationIndex = animationIndex.getAndIncrement() val animation = TextureAnimation(texture) animator.animations += animation texture.renderData = OpenGLTextureData(-1, -1, uvEnd, animationIndex) @@ -148,6 +156,21 @@ class OpenGLTextureArray( texture.array = array } } + } + + @Synchronized + override fun preLoad(latch: AbstractLatch) { + if (state == TextureArrayStates.LOADED || state == TextureArrayStates.PRE_LOADED) { + return + } + val preLoadLatch = SimpleLatch(namedTextures.size + otherTextures.size) + preLoad(preLoadLatch, namedTextures.values) + preLoad(preLoadLatch, otherTextures) + preLoadLatch.await() + + val animationIndex = AtomicInteger() + preLoad(animationIndex, namedTextures.values) + preLoad(animationIndex, otherTextures) state = TextureArrayStates.PRE_LOADED } @@ -186,7 +209,7 @@ class OpenGLTextureArray( textureIds[index] = loadSingleArray(TEXTURE_RESOLUTION_ID_MAP[index], textures) totalLayers += textures.size } - Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Loaded ${textures.size} textures containing ${animator.animations.size} animated ones, split into $totalLayers layers!" } + Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Loaded ${namedTextures.size} textures containing ${animator.animations.size} animated ones, split into $totalLayers layers!" } animator.init() state = TextureArrayStates.LOADED