refactor dynamic textures

Kinda broke, ik
This commit is contained in:
Moritz Zwerger 2023-10-27 20:26:00 +02:00
parent 61f54dacb9
commit 45bcfde492
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
15 changed files with 338 additions and 310 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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<EntityRen
}
}
private fun Entity.createRenderer() = when {
this is EntityModelFactory<*> -> create(this@EntityRendererManager.renderer)
type.modelFactory != null -> type.modelFactory?.nullCast<RegisteredEntityModelFactory<Entity>>()?.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<RegisteredEntityModelFactory<Entity>>()?.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 {

View File

@ -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()
}

View File

@ -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

View File

@ -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)
}

View File

@ -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<DynamicStateChangeCallback> = 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()
}
}

View File

@ -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<WeakReference<DynamicTexture>?> = arrayOfNulls(initialSize)
protected val shaders: MutableSet<NativeShader> = mutableSetOf()
private val lock = ThreadLock()
private var reload = false
val capacity get() = textures.size
interface DynamicTextureArray : TextureArray {
val size: Int
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() }
get() {
var size = 0
for (texture in textures) {
if (texture == null) {
continue
}
size++
}
return size
}
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<WeakReference<DynamicTexture>?> = 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
}

View File

@ -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? {

View File

@ -29,7 +29,6 @@ class DefaultSkinProvider(
private val array: DynamicTextureArray,
private val assets: AssetsManager,
) {
private var defaultId = 0
private val slim: MutableMap<ResourceLocation, DynamicTexture> = mutableMapOf()
private val wide: MutableMap<ResourceLocation, DynamicTexture> = 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 {

View File

@ -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<ByteBuffer>? = null
private val callbacks: MutableSet<DynamicStateChangeCallback> = 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()
}
}

View File

@ -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<WeakReference<OpenGLDynamicTexture>?> = arrayOfNulls(initialSize)
private val lock = ThreadLock()
private var textureId = -1
var shaders: MutableSet<NativeShader> = 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<ByteBuffer>) {
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
}
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)
}
}
override fun upload() {
if (handle >= 0) throw MemoryLeakException("Texture was not unloaded!")
val textureId = OpenGLTextureUtil.createTextureArray()
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?)
}
for ((index, textureReference) in textures.withIndex()) {
val texture = textureReference?.get() ?: continue
unsafeUpload(index, texture)
}
for (shader in shaders) {
unsafeUse(shader)
}
context.textures.staticTextures.activate()
this.handle = textureId
}
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
}
}
val index = getNextIndex()
val texture = OpenGLDynamicTexture(identifier, createShaderIdentifier(index = index))
textures[index] = WeakReference(texture)
texture.state = DynamicTextureState.LOADING
fun load() {
val data = data()
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}!" }
override fun activate() {
glActiveTexture(GL_TEXTURE0 + index)
glBindTexture(GL_TEXTURE_2D_ARRAY, handle)
}
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) }
}
override fun unsafeUse(shader: NativeShader, name: String) {
shader.use()
activate()
shader.setTexture("$name[$index]", index)
}
if (force) {
renderSystem.assertThread()
load()
} else {
DefaultThreadPool += { load() }
}
lock.unlock()
return texture
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<WeakReference<OpenGLDynamicTexture>?> = 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))
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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();
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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;
}