Fix possible memory leaks regarding unused resources, and tighten cache store times

This commit is contained in:
DBotThePony 2023-09-23 22:59:30 +07:00
parent 178953bce7
commit 40782fd18f
Signed by: DBot
GPG Key ID: DCC23B5715498507
4 changed files with 37 additions and 19 deletions

View File

@ -254,7 +254,7 @@ class StarboundClient : Closeable {
// минимальное время хранения 5 минут и...
private val named2DTextures0: Cache<String, GLTexture2D> = Caffeine.newBuilder()
.expireAfterAccess(Duration.ofMinutes(5))
.expireAfterAccess(Duration.ofMinutes(1))
.build()
// ...бесконечное хранение пока кто-то все ещё использует текстуру

View File

@ -1,9 +1,11 @@
package ru.dbotthepony.kstarbound.client.gl.shader
import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.Scheduler
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import it.unimi.dsi.fastutil.objects.Reference2ObjectFunction
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
import org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER
import org.lwjgl.opengl.GL20.GL_VERTEX_SHADER
import ru.dbotthepony.kstarbound.client.StarboundClient
@ -15,6 +17,7 @@ import ru.dbotthepony.kstarbound.client.render.RenderConfig
import ru.dbotthepony.kvector.api.IStruct4f
import ru.dbotthepony.kvector.arrays.Matrix3f
import ru.dbotthepony.kvector.vector.RGBAColor
import java.time.Duration
import java.util.Collections
import java.util.EnumSet
import java.util.function.Function
@ -48,18 +51,22 @@ class UberShader private constructor(
colorMultiplier = RGBAColor.WHITE
}
private val textureConfigs = Reference2ObjectOpenHashMap<GLTexture2D, RenderConfig<UberShader>>()
private val textureConfigs: Cache<GLTexture2D, RenderConfig<UberShader>> = Caffeine.newBuilder()
.weakValues()
.weakKeys()
.expireAfterAccess(Duration.ofMinutes(1))
.build()
fun config(texture: GLTexture2D): RenderConfig<UberShader> {
return textureConfigs.computeIfAbsent(texture, Reference2ObjectFunction {
return textureConfigs.get(texture) {
object : RenderConfig<UberShader>(this) {
override fun setup() {
super.setup()
client.textures2D[0] = texture
client.textures2D[0] = it
}
}
})
}
}
class Builder {

View File

@ -39,9 +39,10 @@ object OneShotGeometryLayer : IGeometryLayer {
class LayeredRenderer {
class Layer(val layer: RenderLayer.Point) : IGeometryLayer {
private data class Tracker(val builder: StreamVertexBuilder, var emptyFrames: Int = 0)
private val meshes = ArrayList<ConfiguredMesh<*>>()
private val callbacks = ArrayList<() -> Unit>()
private val builders = Reference2ObjectLinkedOpenHashMap<RenderConfig<*>, StreamVertexBuilder>()
private val builders = Reference2ObjectLinkedOpenHashMap<RenderConfig<*>, Tracker>()
override fun add(renderer: () -> Unit) {
callbacks.add(renderer)
@ -49,17 +50,27 @@ class LayeredRenderer {
override fun getBuilder(config: RenderConfig<*>): VertexBuilder {
return builders.computeIfAbsent(config, Function {
StreamVertexBuilder(config.program.attributes, initialCapacity = config.initialBuilderCapacity)
}).builder
Tracker(StreamVertexBuilder(config.program.attributes, initialCapacity = config.initialBuilderCapacity))
}).builder.builder
}
override fun render() {
builders.entries.forEach {
if (it.value.builder.isNotEmpty()) {
it.key.setup()
it.value.upload()
it.value.draw()
it.key.uninstall()
val iterator = builders.entries.iterator()
for ((k, v) in iterator) {
if (v.builder.builder.isNotEmpty()) {
v.emptyFrames = 0
k.setup()
v.builder.upload()
v.builder.draw()
k.uninstall()
} else {
v.emptyFrames++
if (v.emptyFrames >= 600) {
// ~10 seconds of inactivity, free resources
iterator.remove()
}
}
}
@ -73,7 +84,7 @@ class LayeredRenderer {
}
override fun clear() {
builders.values.forEach { it.builder.begin() }
builders.values.forEach { it.builder.builder.begin() }
meshes.clear()
callbacks.clear()
}
@ -83,8 +94,8 @@ class LayeredRenderer {
throw IllegalStateException("This layer (index $layer) contains preconfigured meshes and/or callbacks")
return builders.entries.stream()
.filter { it.value.builder.isNotEmpty() }
.map { ConfiguredMesh(it.key, Mesh(it.value.builder)) to layer }
.filter { it.value.builder.builder.isNotEmpty() }
.map { ConfiguredMesh(it.key, Mesh(it.value.builder.builder)) to layer }
}
}

View File

@ -275,7 +275,7 @@ class Image private constructor(
private val dataCache: Cache<String, ByteBuffer> = Caffeine.newBuilder()
.softValues()
.expireAfterAccess(Duration.ofMinutes(20))
.expireAfterAccess(Duration.ofMinutes(5))
.weigher<String, ByteBuffer> { key, value -> value.capacity() }
.maximumWeight(1_024L * 1_024L * 256L /* 256 МиБ */)
.build()