Fix possible memory leaks regarding unused resources, and tighten cache store times
This commit is contained in:
parent
178953bce7
commit
40782fd18f
@ -254,7 +254,7 @@ class StarboundClient : Closeable {
|
|||||||
|
|
||||||
// минимальное время хранения 5 минут и...
|
// минимальное время хранения 5 минут и...
|
||||||
private val named2DTextures0: Cache<String, GLTexture2D> = Caffeine.newBuilder()
|
private val named2DTextures0: Cache<String, GLTexture2D> = Caffeine.newBuilder()
|
||||||
.expireAfterAccess(Duration.ofMinutes(5))
|
.expireAfterAccess(Duration.ofMinutes(1))
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
// ...бесконечное хранение пока кто-то все ещё использует текстуру
|
// ...бесконечное хранение пока кто-то все ещё использует текстуру
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl.shader
|
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.Object2ObjectArrayMap
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectFunction
|
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_FRAGMENT_SHADER
|
||||||
import org.lwjgl.opengl.GL20.GL_VERTEX_SHADER
|
import org.lwjgl.opengl.GL20.GL_VERTEX_SHADER
|
||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
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.api.IStruct4f
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix3f
|
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||||
|
import java.time.Duration
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import java.util.EnumSet
|
import java.util.EnumSet
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
@ -48,18 +51,22 @@ class UberShader private constructor(
|
|||||||
colorMultiplier = RGBAColor.WHITE
|
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> {
|
fun config(texture: GLTexture2D): RenderConfig<UberShader> {
|
||||||
return textureConfigs.computeIfAbsent(texture, Reference2ObjectFunction {
|
return textureConfigs.get(texture) {
|
||||||
object : RenderConfig<UberShader>(this) {
|
object : RenderConfig<UberShader>(this) {
|
||||||
override fun setup() {
|
override fun setup() {
|
||||||
super.setup()
|
super.setup()
|
||||||
|
|
||||||
client.textures2D[0] = texture
|
client.textures2D[0] = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Builder {
|
class Builder {
|
||||||
|
@ -39,9 +39,10 @@ object OneShotGeometryLayer : IGeometryLayer {
|
|||||||
|
|
||||||
class LayeredRenderer {
|
class LayeredRenderer {
|
||||||
class Layer(val layer: RenderLayer.Point) : IGeometryLayer {
|
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 meshes = ArrayList<ConfiguredMesh<*>>()
|
||||||
private val callbacks = ArrayList<() -> Unit>()
|
private val callbacks = ArrayList<() -> Unit>()
|
||||||
private val builders = Reference2ObjectLinkedOpenHashMap<RenderConfig<*>, StreamVertexBuilder>()
|
private val builders = Reference2ObjectLinkedOpenHashMap<RenderConfig<*>, Tracker>()
|
||||||
|
|
||||||
override fun add(renderer: () -> Unit) {
|
override fun add(renderer: () -> Unit) {
|
||||||
callbacks.add(renderer)
|
callbacks.add(renderer)
|
||||||
@ -49,17 +50,27 @@ class LayeredRenderer {
|
|||||||
|
|
||||||
override fun getBuilder(config: RenderConfig<*>): VertexBuilder {
|
override fun getBuilder(config: RenderConfig<*>): VertexBuilder {
|
||||||
return builders.computeIfAbsent(config, Function {
|
return builders.computeIfAbsent(config, Function {
|
||||||
StreamVertexBuilder(config.program.attributes, initialCapacity = config.initialBuilderCapacity)
|
Tracker(StreamVertexBuilder(config.program.attributes, initialCapacity = config.initialBuilderCapacity))
|
||||||
}).builder
|
}).builder.builder
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render() {
|
override fun render() {
|
||||||
builders.entries.forEach {
|
val iterator = builders.entries.iterator()
|
||||||
if (it.value.builder.isNotEmpty()) {
|
|
||||||
it.key.setup()
|
for ((k, v) in iterator) {
|
||||||
it.value.upload()
|
if (v.builder.builder.isNotEmpty()) {
|
||||||
it.value.draw()
|
v.emptyFrames = 0
|
||||||
it.key.uninstall()
|
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() {
|
override fun clear() {
|
||||||
builders.values.forEach { it.builder.begin() }
|
builders.values.forEach { it.builder.builder.begin() }
|
||||||
meshes.clear()
|
meshes.clear()
|
||||||
callbacks.clear()
|
callbacks.clear()
|
||||||
}
|
}
|
||||||
@ -83,8 +94,8 @@ class LayeredRenderer {
|
|||||||
throw IllegalStateException("This layer (index $layer) contains preconfigured meshes and/or callbacks")
|
throw IllegalStateException("This layer (index $layer) contains preconfigured meshes and/or callbacks")
|
||||||
|
|
||||||
return builders.entries.stream()
|
return builders.entries.stream()
|
||||||
.filter { it.value.builder.isNotEmpty() }
|
.filter { it.value.builder.builder.isNotEmpty() }
|
||||||
.map { ConfiguredMesh(it.key, Mesh(it.value.builder)) to layer }
|
.map { ConfiguredMesh(it.key, Mesh(it.value.builder.builder)) to layer }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +275,7 @@ class Image private constructor(
|
|||||||
|
|
||||||
private val dataCache: Cache<String, ByteBuffer> = Caffeine.newBuilder()
|
private val dataCache: Cache<String, ByteBuffer> = Caffeine.newBuilder()
|
||||||
.softValues()
|
.softValues()
|
||||||
.expireAfterAccess(Duration.ofMinutes(20))
|
.expireAfterAccess(Duration.ofMinutes(5))
|
||||||
.weigher<String, ByteBuffer> { key, value -> value.capacity() }
|
.weigher<String, ByteBuffer> { key, value -> value.capacity() }
|
||||||
.maximumWeight(1_024L * 1_024L * 256L /* 256 МиБ */)
|
.maximumWeight(1_024L * 1_024L * 256L /* 256 МиБ */)
|
||||||
.build()
|
.build()
|
||||||
|
Loading…
Reference in New Issue
Block a user