diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt index 175532d5..121fdeaa 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt @@ -520,15 +520,19 @@ class StarboundClient : Closeable { else -> throw IllegalArgumentException("Unknown amount of channels in $it: ${data.amountOfChannels}") }) - tex.upload(when (data.amountOfChannels) { - 1 -> GL_RED - 3 -> GL_RGB - 4 -> GL_RGBA - else -> throw IllegalArgumentException("Unknown amount of channels in $it: ${data.amountOfChannels}") - }, GL_UNSIGNED_BYTE, data.data) + val fileFormat = when (data.amountOfChannels) { + 1 -> GL_RED + 3 -> GL_RGB + 4 -> GL_RGBA + else -> throw IllegalArgumentException("Unknown amount of channels in $it: ${data.amountOfChannels}") + } - tex.textureMinFilter = GL_NEAREST - tex.textureMagFilter = GL_NEAREST + data.data.thenApplyAsync({ + tex.upload(fileFormat, GL_UNSIGNED_BYTE, it) + + tex.textureMinFilter = GL_NEAREST + tex.textureMagFilter = GL_NEAREST + }, foregroundExecutor) tex } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt index 83bc874c..ce2560ef 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt @@ -1,7 +1,10 @@ package ru.dbotthepony.kstarbound.defs.image +import com.github.benmanes.caffeine.cache.AsyncLoadingCache import com.github.benmanes.caffeine.cache.Cache +import com.github.benmanes.caffeine.cache.CacheLoader import com.github.benmanes.caffeine.cache.Caffeine +import com.github.benmanes.caffeine.cache.LoadingCache import com.github.benmanes.caffeine.cache.Scheduler import com.google.common.collect.ImmutableList import com.google.gson.JsonArray @@ -32,6 +35,7 @@ import java.nio.ByteBuffer import java.time.Duration import java.util.Collections import java.util.Optional +import java.util.concurrent.CompletableFuture import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.locks.ReentrantLock @@ -68,11 +72,11 @@ class Image private constructor( } } - val data: ByteBuffer get() { + val data: CompletableFuture get() { var get = dataRef?.get() if (get != null) - return get + return CompletableFuture.completedFuture(get) lock.lock() @@ -80,31 +84,14 @@ class Image private constructor( get = dataRef?.get() if (get != null) - return get + return CompletableFuture.completedFuture(get) - get = dataCache.get(path) { - val getWidth = intArrayOf(0) - val getHeight = intArrayOf(0) - val components = intArrayOf(0) + val f = dataCache.get(source) - val data = STBImage.stbi_load_from_memory( - source.readDirect(), - getWidth, getHeight, - components, 0 - ) ?: throw IllegalArgumentException("File $source is not an image or it is corrupted") + if (f.isDone) + dataRef = WeakReference(f.get()) - val address = MemoryUtil.memAddress(data) - Starbound.CLEANER.register(data) { STBImage.nstbi_image_free(address) } - - check(getWidth[0] == width && getHeight[0] == height && components[0] == amountOfChannels) { - "Actual loaded image differs from constructed (this $width x $height with $amountOfChannels channels; loaded ${getWidth[0]} x ${getHeight[0]} with ${components[0]} channels)" - } - - data - } - - dataRef = WeakReference(get) - return get + return f.copy() } finally { lock.unlock() } @@ -149,7 +136,7 @@ class Image private constructor( require(x in 0 until width && y in 0 until height) { "Position out of bounds: $x $y" } val offset = (this.y + y) * this@Image.width * amountOfChannels + (this.x + x) * amountOfChannels - val data = data + val data = data.join() when (amountOfChannels) { 4 -> return data[offset].toInt() or @@ -273,12 +260,27 @@ class Image private constructor( private val imageCache = ConcurrentHashMap>() private val logger = LogManager.getLogger() - private val dataCache: Cache = Caffeine.newBuilder() + private val dataCache: AsyncLoadingCache = Caffeine.newBuilder() .expireAfterAccess(Duration.ofMinutes(1)) - .weigher { key, value -> value.capacity() } + .weigher { key, value -> value.capacity() } .maximumWeight(1_024L * 1_024L * 256L /* 256 МиБ */) .scheduler(Scheduler.systemScheduler()) - .build() + .buildAsync(CacheLoader { + val getWidth = intArrayOf(0) + val getHeight = intArrayOf(0) + val components = intArrayOf(0) + + val data = STBImage.stbi_load_from_memory( + it.readDirect(), + getWidth, getHeight, + components, 0 + ) ?: throw IllegalArgumentException("File $it is not an image or it is corrupted") + + val address = MemoryUtil.memAddress(data) + Starbound.CLEANER.register(data) { STBImage.nstbi_image_free(address) } + + data + }) @JvmStatic fun get(path: String): Image? {