diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/texture/DummyDynamicTexture.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/texture/DummyDynamicTexture.kt index 935882a60..8ab02530d 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/texture/DummyDynamicTexture.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/texture/DummyDynamicTexture.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2022 Moritz Zwerger + * Copyright (C) 2020-2023 Moritz Zwerger * * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * @@ -13,34 +13,9 @@ package de.bixilon.minosoft.gui.rendering.system.dummy.texture -import de.bixilon.kotlinglm.vec2.Vec2 -import de.bixilon.kotlinglm.vec2.Vec2i -import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicStateChangeCallback import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture -import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureState import java.util.* -import java.util.concurrent.atomic.AtomicInteger - -object DummyDynamicTexture : DynamicTexture { - override val uuid: UUID = UUID(0L, 0L) - override val size: Vec2i = Vec2i(1, 1) - override val usages: AtomicInteger = AtomicInteger(0) - override val state: DynamicTextureState = DynamicTextureState.LOADED - - override fun addListener(callback: DynamicStateChangeCallback) { - } - - override fun removeListener(callback: DynamicStateChangeCallback) { - } - - override fun transformUV(end: Vec2?): Vec2 { - return end ?: Vec2(1.0f) - } - - override fun transformUV(end: FloatArray?): FloatArray { - return end ?: floatArrayOf(1.0f, 1.0f) - - } +object DummyDynamicTexture : DynamicTexture(UUID(0L, 0L)) { override val shaderId: Int = 0 } diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/texture/DummyDynamicTextureArray.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/texture/DummyDynamicTextureArray.kt index 6bfd2cf69..971c27d75 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/texture/DummyDynamicTextureArray.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/texture/DummyDynamicTextureArray.kt @@ -13,26 +13,17 @@ package de.bixilon.minosoft.gui.rendering.system.dummy.texture -import de.bixilon.kutil.latch.AbstractLatch +import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader -import de.bixilon.minosoft.gui.rendering.system.base.texture.data.TextureData import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureArray -import java.util.* -class DummyDynamicTextureArray : DynamicTextureArray { - override val size: Int = 0 +class DummyDynamicTextureArray(context: RenderContext) : DynamicTextureArray(context, 1) { + override fun createTexture(identifier: Any, index: Int) = DummyDynamicTexture - override fun pushBuffer(identifier: UUID, force: Boolean, data: () -> TextureData): DynamicTexture { - return DummyDynamicTexture - } - - override fun upload(latch: AbstractLatch?) { - } - - override fun activate() { - } - - override fun use(shader: NativeShader, name: String) { - } + override fun unload() = Unit + override fun unsafeUse(shader: NativeShader, name: String) = Unit + override fun activate() = Unit + override fun upload() = Unit + override fun upload(index: Int, texture: DynamicTexture) = Unit } diff --git a/src/main/java/de/bixilon/minosoft/gui/RenderLoop.kt b/src/main/java/de/bixilon/minosoft/gui/RenderLoop.kt index ff8123be7..b67229a36 100644 --- a/src/main/java/de/bixilon/minosoft/gui/RenderLoop.kt +++ b/src/main/java/de/bixilon/minosoft/gui/RenderLoop.kt @@ -41,7 +41,7 @@ class RenderLoop( var paused = false context::state.observe(this) { paused = if (paused) { - context.queue.clear() + // context.queue.clear() // TODO: That might cause bugs and memory leaks false } else { it == RenderingStates.PAUSED diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/entities/EntityRendererManager.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/entities/EntityRendererManager.kt index 6618bd1e3..070b46869 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/entities/EntityRendererManager.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/entities/EntityRendererManager.kt @@ -18,6 +18,7 @@ import de.bixilon.kutil.collections.iterator.async.ConcurrentIterator import de.bixilon.kutil.collections.map.LockMap import de.bixilon.kutil.concurrent.lock.simple.SimpleLock import de.bixilon.kutil.concurrent.pool.ThreadPool +import de.bixilon.kutil.exception.ExceptionUtil.ignoreAll import de.bixilon.kutil.observer.set.SetObserver.Companion.observeSet import de.bixilon.minosoft.data.entities.entities.Entity import de.bixilon.minosoft.gui.rendering.entities.factory.EntityModelFactory @@ -46,15 +47,17 @@ class EntityRendererManager(val renderer: EntitiesRenderer) : Iterable -> create(this@EntityRendererManager.renderer) + type.modelFactory != null -> type.modelFactory?.nullCast>()?.create(this@EntityRendererManager.renderer, this) + else -> DummyEntityRenderer(this@EntityRendererManager.renderer, this) + } + operator fun plusAssign(entity: Entity) = add(entity) fun add(entity: Entity) { try { renderers.lock.lock() - val renderer = when { - entity is EntityModelFactory<*> -> entity.create(this.renderer) - entity.type.modelFactory != null -> entity.type.modelFactory?.nullCast>()?.create(this.renderer, entity) - else -> DummyEntityRenderer(this.renderer, entity) - } ?: return + val renderer = ignoreAll { entity.createRenderer() } ?: return entity.renderer?.let { onReplace(it) } this.renderers.unsafe.put(entity, renderer)?.let { onReplace(it) } } finally { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/primitive/DynamicImageElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/primitive/DynamicImageElement.kt index b1becbe56..3fd1e6af3 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/primitive/DynamicImageElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/primitive/DynamicImageElement.kt @@ -39,9 +39,7 @@ open class DynamicImageElement( var texture: DynamicTexture? = null set(value) { - field?.usages?.decrementAndGet() field?.removeListener(this) - value?.usages?.incrementAndGet() value?.addListener(this) field = value cacheUpToDate = false @@ -97,11 +95,7 @@ open class DynamicImageElement( override fun forceSilentApply() = Unit override fun silentApply(): Boolean = false - protected fun finalize() { - texture?.usages?.decrementAndGet() - } - - override fun onStateChange(texture: DynamicTexture, state: DynamicTextureState) { + override fun onDynamicTextureChange(texture: DynamicTexture) { if (texture === this.texture) { forceApply() } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/debug/DebugHUDElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/debug/DebugHUDElement.kt index b05f6acc1..09faaa0c4 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/debug/DebugHUDElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/debug/DebugHUDElement.kt @@ -251,6 +251,10 @@ class DebugHUDElement(guiRenderer: GUIRenderer) : Element(guiRenderer), Layouted layout += LineSpacerElement(guiRenderer) + layout += AutoTextElement(guiRenderer, 20, properties = RIGHT) { "Dynamic textures ${context.textures.dynamicTextures.size.format()}/${context.textures.dynamicTextures.capacity.format()}" } + + layout += LineSpacerElement(guiRenderer) + context.connection.camera.target.apply { layout += AutoTextElement(guiRenderer, 1, properties = RIGHT) { // ToDo: Tags diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicStateChangeCallback.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicStateChangeCallback.kt index ba7e782ed..22477901d 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicStateChangeCallback.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicStateChangeCallback.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2022 Moritz Zwerger + * Copyright (C) 2020-2023 Moritz Zwerger * * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * @@ -13,7 +13,7 @@ package de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic -interface DynamicStateChangeCallback { +fun interface DynamicStateChangeCallback { - fun onStateChange(texture: DynamicTexture, state: DynamicTextureState) + fun onDynamicTextureChange(texture: DynamicTexture) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTexture.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTexture.kt index e5c93a5cf..b210e6de7 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTexture.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTexture.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2022 Moritz Zwerger + * Copyright (C) 2020-2023 Moritz Zwerger * * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * @@ -13,22 +13,55 @@ package de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic -import de.bixilon.kotlinglm.vec2.Vec2i +import de.bixilon.kotlinglm.vec2.Vec2 +import de.bixilon.kutil.concurrent.lock.simple.SimpleLock +import de.bixilon.kutil.exception.ExceptionUtil.ignoreAll +import de.bixilon.minosoft.gui.rendering.system.base.texture.data.MipmapTextureData import de.bixilon.minosoft.gui.rendering.system.base.texture.shader.ShaderTexture -import java.util.* -import java.util.concurrent.atomic.AtomicInteger -interface DynamicTexture : ShaderTexture { - val uuid: UUID - val size: Vec2i - val usages: AtomicInteger +abstract class DynamicTexture( + val identifier: Any, +) : ShaderTexture { + private val callbacks: MutableSet = mutableSetOf() + private val lock = SimpleLock() - val state: DynamicTextureState + var data: MipmapTextureData? = null + var state: DynamicTextureState = DynamicTextureState.WAITING + set(value) { + if (field == value) { + return + } + field = value + lock.acquire() + for (callback in callbacks) { + ignoreAll { callback.onDynamicTextureChange(this) } + } + lock.release() + } + override fun toString(): String { + return identifier.toString() + } + + override fun transformUV(end: Vec2?): Vec2 { + return end ?: Vec2(1.0f) // TODO: memory + } + + override fun transformUV(end: FloatArray?): FloatArray { + return end ?: floatArrayOf(1.0f, 1.0f) // TODO: memory + } - fun addListener(callback: DynamicStateChangeCallback) operator fun plusAssign(callback: DynamicStateChangeCallback) = addListener(callback) + fun addListener(callback: DynamicStateChangeCallback) { + lock.lock() + callbacks += callback + lock.unlock() + } operator fun minusAssign(callback: DynamicStateChangeCallback) = removeListener(callback) - fun removeListener(callback: DynamicStateChangeCallback) + fun removeListener(callback: DynamicStateChangeCallback) { + lock.lock() + callbacks -= callback + lock.unlock() + } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTextureArray.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTextureArray.kt index 6d2be9145..2d962411b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTextureArray.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTextureArray.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2022 Moritz Zwerger + * Copyright (C) 2020-2023 Moritz Zwerger * * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * @@ -13,18 +13,151 @@ package de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic +import de.bixilon.kutil.concurrent.lock.thread.ThreadLock +import de.bixilon.kutil.concurrent.pool.DefaultThreadPool +import de.bixilon.kutil.concurrent.pool.runnable.ForcePooledRunnable +import de.bixilon.kutil.latch.AbstractLatch +import de.bixilon.minosoft.gui.rendering.RenderContext +import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader +import de.bixilon.minosoft.gui.rendering.system.base.shader.ShaderUniforms import de.bixilon.minosoft.gui.rendering.system.base.texture.array.TextureArray +import de.bixilon.minosoft.gui.rendering.system.base.texture.data.MipmapTextureData import de.bixilon.minosoft.gui.rendering.system.base.texture.data.TextureData import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.readTexture import java.io.ByteArrayInputStream -import java.util.* +import java.lang.ref.WeakReference + +abstract class DynamicTextureArray( + val context: RenderContext, + val initialSize: Int, +) : TextureArray { + protected var textures: Array?> = arrayOfNulls(initialSize) + protected val shaders: MutableSet = mutableSetOf() + private val lock = ThreadLock() + private var reload = false + + val capacity get() = textures.size -interface DynamicTextureArray : TextureArray { val size: Int + get() { + var size = 0 + for (texture in textures) { + if (texture == null) { + continue + } + size++ + } + return size + } - fun pushBuffer(identifier: UUID, force: Boolean = false, data: () -> TextureData): DynamicTexture - fun pushRawArray(identifier: UUID, force: Boolean = false, data: () -> ByteArray): DynamicTexture { - return pushBuffer(identifier, force) { ByteArrayInputStream(data()).readTexture() } + operator fun get(identifier: Any): DynamicTexture? { + lock.acquire() + val texture = unsafeGet(identifier) + lock.release() + return texture } + + fun unsafeGet(identifier: Any): DynamicTexture? { + for (reference in textures) { + val texture = reference?.get() ?: continue + if (texture.identifier == identifier) { + return texture + } + } + return null + } + + private fun DynamicTexture.load(index: Int, creator: () -> TextureData) { + val data = creator.invoke() + + this.data = MipmapTextureData(data.size, data.buffer) + context.queue += { upload(index, this) } + } + + fun pushRaw(identifier: Any, async: Boolean = true, creator: () -> ByteArray): DynamicTexture { + return push(identifier, async) { ByteArrayInputStream(creator()).readTexture() } + } + + fun push(identifier: Any, async: Boolean = true, creator: () -> TextureData): DynamicTexture { + lock.lock() + cleanup() + unsafeGet(identifier)?.let { lock.unlock(); return it } + + val index = getNextIndex() + + val texture = createTexture(identifier, index) + textures[index] = WeakReference(texture) + texture.state = DynamicTextureState.LOADING + + if (async) { + DefaultThreadPool += ForcePooledRunnable { texture.load(index, creator) } + } else { + texture.load(index, creator) + } + + lock.unlock() + return texture + } + + + override fun use(shader: NativeShader, name: String) { + shaders += shader + unsafeUse(shader, name) + } + + + private fun getNextIndex(): Int { + lock.lock() + for ((index, texture) in textures.withIndex()) { + if (texture?.get() == null) { + lock.unlock() + return index + } + } + val nextIndex = textures.size + grow() + lock.unlock() + return nextIndex + } + + override fun upload(latch: AbstractLatch?) = upload() + + private fun grow() { + lock.lock() + val textures: Array?> = arrayOfNulls(textures.size + initialSize) + for ((index, texture) in this.textures.withIndex()) { + if (texture == null) continue + textures[index] = texture + } + this.textures = textures + + this.reload = true + context.queue += { reload() } + lock.unlock() + } + + private fun cleanup() { + lock.lock() + for ((index, reference) in textures.withIndex()) { + if (reference == null) continue + val texture = reference.get() + texture?.state = DynamicTextureState.UNLOADED + textures[index] = null + } + lock.unlock() + } + + private fun reload() { + unload() + upload() + this.reload = false + } + + + protected abstract fun upload(index: Int, texture: DynamicTexture) + protected abstract fun upload() + protected abstract fun unload() + protected abstract fun unsafeUse(shader: NativeShader, name: String = ShaderUniforms.TEXTURES) + protected abstract fun createTexture(identifier: Any, index: Int): DynamicTexture } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/skin/SkinManager.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/skin/SkinManager.kt index 045ec1094..9f5fc69f8 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/skin/SkinManager.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/skin/SkinManager.kt @@ -32,7 +32,7 @@ class SkinManager(private val textureManager: TextureManager) { fun initialize(account: Account, assets: AssetsManager) { default = DefaultSkinProvider(this.textureManager.dynamicTextures, assets) default.initialize() - skin = getSkin(account.uuid, account.properties, fetch = true, force = true)?.apply { texture.usages.incrementAndGet() } + skin = getSkin(account.uuid, account.properties, fetch = true, force = true) } private fun getAccountProperties(uuid: UUID): PlayerProperties? { @@ -51,7 +51,7 @@ class SkinManager(private val textureManager: TextureManager) { private fun getSkin(uuid: UUID, properties: PlayerProperties?, force: Boolean = false): PlayerSkin? { val texture = properties?.textures?.skin ?: return default[uuid] - return PlayerSkin(textureManager.dynamicTextures.pushRawArray(uuid, force) { texture.read() }, texture.metadata.model) + return PlayerSkin(textureManager.dynamicTextures.pushRaw(uuid, force) { texture.read() }, texture.metadata.model) } fun getSkin(player: PlayerEntity, properties: PlayerProperties? = null, fetch: Boolean = true, force: Boolean = false): PlayerSkin? { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/skin/vanilla/DefaultSkinProvider.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/skin/vanilla/DefaultSkinProvider.kt index 1382fca8a..825c73723 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/skin/vanilla/DefaultSkinProvider.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/skin/vanilla/DefaultSkinProvider.kt @@ -29,7 +29,6 @@ class DefaultSkinProvider( private val array: DynamicTextureArray, private val assets: AssetsManager, ) { - private var defaultId = 0 private val slim: MutableMap = mutableMapOf() private val wide: MutableMap = mutableMapOf() private var fallback: PlayerSkin? = null @@ -66,9 +65,7 @@ class DefaultSkinProvider( private fun load(path: ResourceLocation): DynamicTexture? { val data = assets.getOrNull(path)?.readTexture() ?: return null - val texture = array.pushBuffer(UUID(0L, defaultId++.toLong()), true) { data } - texture.usages.incrementAndGet() - return texture + return array.push(path, true) { data } } private fun ResourceLocation.skin(prefix: String): ResourceLocation { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/dynamic/OpenGLDynamicTexture.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/dynamic/OpenGLDynamicTexture.kt index 44d58e236..728d4e405 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/dynamic/OpenGLDynamicTexture.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/dynamic/OpenGLDynamicTexture.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2022 Moritz Zwerger + * Copyright (C) 2020-2023 Moritz Zwerger * * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * @@ -13,75 +13,15 @@ package de.bixilon.minosoft.gui.rendering.system.opengl.texture.dynamic -import de.bixilon.kotlinglm.vec2.Vec2 -import de.bixilon.kotlinglm.vec2.Vec2i -import de.bixilon.kutil.concurrent.lock.simple.SimpleLock -import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicStateChangeCallback import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureState -import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY -import java.nio.ByteBuffer -import java.util.* -import java.util.concurrent.atomic.AtomicInteger -class OpenGLDynamicTexture( - override val uuid: UUID, - shaderId: Int, -) : DynamicTexture { - var data: Array? = null - private val callbacks: MutableSet = mutableSetOf() - private val callbackLock = SimpleLock() - override val usages = AtomicInteger() - override var state: DynamicTextureState = DynamicTextureState.WAITING - set(value) { - if (field == value) { - return - } - field = value - callbackLock.acquire() - for (callback in callbacks) { - callback.onStateChange(this, value) - } - callbackLock.release() - } - override var size: Vec2i = Vec2i.EMPTY +class OpenGLDynamicTexture(identifier: Any, shaderId: Int) : DynamicTexture(identifier) { override var shaderId: Int = shaderId get() { - if (state == DynamicTextureState.UNLOADED) { - throw IllegalStateException("Texture was garbage collected!") - } - if (usages.get() == 0) { - throw IllegalStateException("Texture could be garbage collected every time!") - } - if (state == DynamicTextureState.LOADING) { - throw IllegalStateException("Texture was not loaded yet!") - } + if (state == DynamicTextureState.UNLOADED) throw IllegalStateException("Texture was unloaded!") + if (state == DynamicTextureState.LOADING) throw IllegalStateException("Texture was not loaded yet!") return field } - - override fun toString(): String { - return uuid.toString() - } - - override fun transformUV(end: Vec2?): Vec2 { - return end ?: Vec2(1.0f) - } - - override fun transformUV(end: FloatArray?): FloatArray { - return end ?: floatArrayOf(1.0f, 1.0f) - } - - override fun addListener(callback: DynamicStateChangeCallback) { - callbackLock.lock() - callbacks += callback - callbackLock.unlock() - } - - override fun removeListener(callback: DynamicStateChangeCallback) { - callbackLock.lock() - callbacks -= callback - callbackLock.unlock() - } } - diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/dynamic/OpenGLDynamicTextureArray.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/dynamic/OpenGLDynamicTextureArray.kt index 97b29d6ab..9aec8dbaf 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/dynamic/OpenGLDynamicTextureArray.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/dynamic/OpenGLDynamicTextureArray.kt @@ -13,107 +13,83 @@ package de.bixilon.minosoft.gui.rendering.system.opengl.texture.dynamic -import de.bixilon.kutil.concurrent.lock.thread.ThreadLock -import de.bixilon.kutil.concurrent.pool.DefaultThreadPool -import de.bixilon.kutil.latch.AbstractLatch import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader -import de.bixilon.minosoft.gui.rendering.system.base.shader.ShaderUniforms -import de.bixilon.minosoft.gui.rendering.system.base.texture.data.TextureData +import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureArray import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureState +import de.bixilon.minosoft.gui.rendering.system.opengl.MemoryLeakException import de.bixilon.minosoft.gui.rendering.system.opengl.OpenGLRenderSystem import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureUtil -import de.bixilon.minosoft.util.logging.Log -import de.bixilon.minosoft.util.logging.LogLevels -import de.bixilon.minosoft.util.logging.LogMessageType import org.lwjgl.opengl.GL11.* import org.lwjgl.opengl.GL12.glTexImage3D import org.lwjgl.opengl.GL12.glTexSubImage3D import org.lwjgl.opengl.GL13.GL_TEXTURE0 import org.lwjgl.opengl.GL13.glActiveTexture import org.lwjgl.opengl.GL30.GL_TEXTURE_2D_ARRAY -import java.lang.ref.WeakReference import java.nio.ByteBuffer -import java.util.* class OpenGLDynamicTextureArray( - val context: RenderContext, + context: RenderContext, val renderSystem: OpenGLRenderSystem, val index: Int = renderSystem.textureBindingIndex++, - val initialSize: Int = 32, + initialSize: Int = 32, val resolution: Int, -) : DynamicTextureArray { - private var textures: Array?> = arrayOfNulls(initialSize) - private val lock = ThreadLock() - private var textureId = -1 - var shaders: MutableSet = mutableSetOf() +) : DynamicTextureArray(context, initialSize) { + private var handle = -1 - override val size: Int - get() { - var size = 0 - for (texture in textures) { - if (texture == null) { - continue - } - size++ - } - return size - } - - private fun load(texture: OpenGLDynamicTexture, index: Int, mipmaps: Array) { - glBindTexture(GL_TEXTURE_2D_ARRAY, textureId) - - for ((level, mipmap) in mipmaps.withIndex()) { - // glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, index, texture.size.x shr level, texture.size.y shr level, 1, GL_RGBA, GL_UNSIGNED_BYTE, mipmap) - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, index, resolution shr level, resolution shr level, 1, GL_RGBA, GL_UNSIGNED_BYTE, mipmap) - } + override fun upload(index: Int, texture: DynamicTexture) { + glBindTexture(GL_TEXTURE_2D_ARRAY, handle) + unsafeUpload(index, texture) texture.state = DynamicTextureState.LOADED - context.textures.staticTextures.activate() } - override fun pushBuffer(identifier: UUID, force: Boolean, data: () -> TextureData): OpenGLDynamicTexture { - lock.lock() - check(textureId >= 0) { "Dynamic texture array not yet initialized!" } - cleanup() - for (textureReference in textures) { - val texture = textureReference?.get() - if (texture?.uuid == identifier) { - lock.unlock() - return texture - } + private fun unsafeUpload(index: Int, texture: DynamicTexture) { + val data = texture.data ?: throw IllegalArgumentException("No texture data?") + for ((level, buffer) in data.collect().withIndex()) { + // glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, index, texture.size.x shr level, texture.size.y shr level, 1, GL_RGBA, GL_UNSIGNED_BYTE, mipmap) + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, index, resolution shr level, resolution shr level, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer) } - val index = getNextIndex() - val texture = OpenGLDynamicTexture(identifier, createShaderIdentifier(index = index)) - textures[index] = WeakReference(texture) - texture.state = DynamicTextureState.LOADING + } - fun load() { - val data = data() + override fun upload() { + if (handle >= 0) throw MemoryLeakException("Texture was not unloaded!") + val textureId = OpenGLTextureUtil.createTextureArray() - if (data.buffer.limit() > resolution * resolution * 4 || data.buffer.limit() < resolution * 4) { // allow anything in 1..resolution for y size - Log.log(LogMessageType.ASSETS, LogLevels.WARN) { "Dynamic texture $texture, has not a size of ${resolution}x${resolution}!" } - } - - val mipmaps = OpenGLTextureUtil.generateMipMaps(data.buffer, data.size) - texture.data = mipmaps - texture.size = data.size - if (force) { - load(texture, index, mipmaps) // thread check already done - } else { - context.queue += { load(texture, index, mipmaps) } - } + for (level in 0 until OpenGLTextureUtil.MAX_MIPMAP_LEVELS) { + glTexImage3D(GL_TEXTURE_2D_ARRAY, level, GL_RGBA, resolution shr level, resolution shr level, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?) } - if (force) { - renderSystem.assertThread() - load() - } else { - DefaultThreadPool += { load() } + for ((index, textureReference) in textures.withIndex()) { + val texture = textureReference?.get() ?: continue + unsafeUpload(index, texture) } - lock.unlock() - return texture + + for (shader in shaders) { + unsafeUse(shader) + } + + context.textures.staticTextures.activate() + + this.handle = textureId + } + + override fun activate() { + glActiveTexture(GL_TEXTURE0 + index) + glBindTexture(GL_TEXTURE_2D_ARRAY, handle) + } + + override fun unsafeUse(shader: NativeShader, name: String) { + shader.use() + activate() + shader.setTexture("$name[$index]", index) + } + + override fun unload() { + if (handle < 0) throw IllegalStateException("Not loaded!") + glDeleteTextures(handle) + this.handle = -1 } private fun createShaderIdentifier(array: Int = this.index, index: Int): Int { @@ -121,96 +97,7 @@ class OpenGLDynamicTextureArray( return (array shl 28) or (index shl 12) or 0 } - - override fun upload(latch: AbstractLatch?) { - val textureId = OpenGLTextureUtil.createTextureArray() - this.textureId = textureId - - - for (level in 0 until OpenGLTextureUtil.MAX_MIPMAP_LEVELS) { - glTexImage3D(GL_TEXTURE_2D_ARRAY, level, GL_RGBA, resolution shr level, resolution shr level, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?) - } - - this.textureId = textureId - } - - override fun activate() { - glActiveTexture(GL_TEXTURE0 + index) - glBindTexture(GL_TEXTURE_2D_ARRAY, textureId) - } - - override fun use(shader: NativeShader, name: String) { - shaders += shader - _use(shader, name) - } - - private fun _use(shader: NativeShader, name: String = ShaderUniforms.TEXTURES) { - shader.use() - - activate() - - shader.setTexture("$name[$index]", index) - } - - private fun getNextIndex(): Int { - lock.lock() - for ((index, texture) in textures.withIndex()) { - if (texture == null) { - lock.unlock() - return index - } - } - val nextIndex = textures.size - grow() - lock.unlock() - return nextIndex - } - - private fun reload() { - lock.lock() - glDeleteTextures(textureId) - upload(null) - - for ((index, textureReference) in textures.withIndex()) { - val texture = textureReference?.get() ?: continue - load(texture, index, texture.data ?: continue) - } - - for (shader in shaders) { - _use(shader) - } - lock.unlock() - } - - private fun grow() { - lock.lock() - val textures: Array?> = arrayOfNulls(textures.size + initialSize) - for ((index, texture) in this.textures.withIndex()) { - textures[index] = texture - } - this.textures = textures - context.queue += { reload() } - lock.unlock() - } - - @Synchronized - private fun cleanup() { - lock.lock() - for ((index, reference) in textures.withIndex()) { - if (reference == null) { - continue - } - val texture = reference.get() - if (texture == null) { - textures[index] = null - continue - } - if (texture.usages.get() > 0) { - continue - } - texture.state = DynamicTextureState.UNLOADED - textures[index] = null - } - lock.unlock() + override fun createTexture(identifier: Any, index: Int): DynamicTexture { + return OpenGLDynamicTexture(identifier, createShaderIdentifier(index = index)) } } diff --git a/src/main/resources/assets/minosoft/rendering/shader/includes/entities/player/player.fsh b/src/main/resources/assets/minosoft/rendering/shader/includes/entities/player/player.fsh new file mode 100644 index 000000000..fbfb5b31e --- /dev/null +++ b/src/main/resources/assets/minosoft/rendering/shader/includes/entities/player/player.fsh @@ -0,0 +1,37 @@ +/* + * Minosoft + * Copyright (C) 2020 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +#version 330 core + +out vec4 foutColor; + +#define FOG + + +#include "minosoft:texture" +#include "minosoft:alpha" +#include "minosoft:fog" + +flat in bool finSkinLayer; + +uniform uint uTextureIndex; +uniform vec3 uTextureCoordinates; + +void main() { + foutColor = getTexture(uTextureIndex, uTextureCoordinates); + if (finSkinLayer) { + if (foutColor.a < 0.5) discard; + } + foutColor.a = 1.0f; + set_fog(); +} diff --git a/src/main/resources/assets/minosoft/rendering/shader/includes/entities/player/player.vsh b/src/main/resources/assets/minosoft/rendering/shader/includes/entities/player/player.vsh new file mode 100644 index 000000000..555a7df62 --- /dev/null +++ b/src/main/resources/assets/minosoft/rendering/shader/includes/entities/player/player.vsh @@ -0,0 +1,34 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +#version 330 core + +layout (location = 0) in vec3 vinPosition; +layout (location = 1) in vec2 vinUV; +layout (location = 2) in float vinTransformNormal; // transform (0x7F000), normal (0xFFF) + +#include "minosoft:animation/header_vertex" +#include "minosoft:skeletal/vertex" + + +#include "minosoft:animation/buffer" +#include "minosoft:animation/main_vertex" + +flat out bool finSkinLayer; + + +void main() { + run_skeletal(floatBitsToUint(vinTransformNormal), vinPosition); + run_animation(); + finSkinLayer = false; +}