hud: improve memory usage

* Every HUDElement has its own mesh now
 * Reuse existing mesh data (don't allocate new memory)
 * Use Mesh data as cache for layouted elements (not a separate float array)
This commit is contained in:
Bixilon 2021-11-17 21:21:17 +01:00
parent e2f4170bb8
commit fc2b5a8502
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
12 changed files with 188 additions and 84 deletions

View File

@ -15,10 +15,10 @@ package de.bixilon.minosoft.gui.rendering.gui.elements
import de.bixilon.minosoft.gui.rendering.RenderConstants import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.gui.hud.HUDRenderer import de.bixilon.minosoft.gui.rendering.gui.hud.HUDRenderer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMesh
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMeshCache import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMeshCache
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.isGreater import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.isGreater
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.isSmaller import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.isSmaller
@ -26,6 +26,7 @@ import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.max
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.min import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.min
import de.bixilon.minosoft.gui.rendering.util.vec.vec4.Vec4iUtil.EMPTY import de.bixilon.minosoft.gui.rendering.util.vec.vec4.Vec4iUtil.EMPTY
import de.bixilon.minosoft.gui.rendering.util.vec.vec4.Vec4iUtil.spaceSize import de.bixilon.minosoft.gui.rendering.util.vec.vec4.Vec4iUtil.spaceSize
import de.bixilon.minosoft.util.collections.floats.DirectArrayFloatList
import glm_.vec2.Vec2i import glm_.vec2.Vec2i
import glm_.vec4.Vec4i import glm_.vec4.Vec4i
@ -41,7 +42,9 @@ abstract class Element(val hudRenderer: HUDRenderer) {
_parent = value _parent = value
silentApply() silentApply()
} }
protected var cache = GUIMeshCache(hudRenderer.matrix, Mesh.TRIANGLE_TO_QUAD_ORDER, 0)
@Deprecated("Warning: Should not be directly accessed!")
val cache = GUIMeshCache(hudRenderer.matrix, renderWindow.renderSystem.primitiveMeshOrder, 1000)
open var cacheEnabled: Boolean = true open var cacheEnabled: Boolean = true
open var initialCacheSize: Int = 100 open var initialCacheSize: Int = 100
open var cacheUpToDate: Boolean = false open var cacheUpToDate: Boolean = false
@ -123,21 +126,36 @@ abstract class Element(val hudRenderer: HUDRenderer) {
*/ */
fun render(offset: Vec2i, z: Int, consumer: GUIVertexConsumer, options: GUIVertexOptions?): Int { fun render(offset: Vec2i, z: Int, consumer: GUIVertexConsumer, options: GUIVertexOptions?): Int {
val offset = Vec2i(offset) val offset = Vec2i(offset)
var directRendering = false
if (consumer is GUIMesh && consumer.data == cache.data) {
directRendering = true
}
if (RenderConstants.DISABLE_GUI_CACHE || !cacheEnabled) { if (RenderConstants.DISABLE_GUI_CACHE || !cacheEnabled) {
return forceRender(offset, z, consumer, options) if (directRendering) {
cache.clear()
}
val maxZ = forceRender(offset, z, consumer, options)
if (directRendering) {
cache.revision++
}
return maxZ
} }
if (!cacheUpToDate || cache.offset != offset || hudRenderer.matrixChange || cache.matrix !== hudRenderer.matrix || z != cache.z) { if (!cacheUpToDate || cache.offset != offset || hudRenderer.matrixChange || cache.matrix !== hudRenderer.matrix || z != cache.z) {
val cache = GUIMeshCache(hudRenderer.matrix, renderWindow.renderSystem.primitiveMeshOrder) this.cache.clear()
cache.matrix = hudRenderer.matrix
cache.offset = Vec2i(offset) cache.offset = Vec2i(offset)
cache.z = z cache.z = z
val maxZ = forceRender(offset, z, cache, options) val maxZ = forceRender(offset, z, cache, options)
cache.maxZ = maxZ cache.maxZ = maxZ
if (cache.data !is DirectArrayFloatList) {
// raw mesh data
cache.data.finish() cache.data.finish()
this.cache = cache }
cacheUpToDate = true cacheUpToDate = true
} }
if (!directRendering) {
consumer.addCache(cache) consumer.addCache(cache)
}
return cache.maxZ return cache.maxZ
} }

View File

@ -37,7 +37,6 @@ import de.bixilon.minosoft.gui.rendering.gui.hud.elements.other.WorldInfoHUDElem
import de.bixilon.minosoft.gui.rendering.gui.hud.elements.scoreboard.ScoreboardHUDElement import de.bixilon.minosoft.gui.rendering.gui.hud.elements.scoreboard.ScoreboardHUDElement
import de.bixilon.minosoft.gui.rendering.gui.hud.elements.tab.TabListHUDElement import de.bixilon.minosoft.gui.rendering.gui.hud.elements.tab.TabListHUDElement
import de.bixilon.minosoft.gui.rendering.gui.hud.elements.title.TitleHUDElement import de.bixilon.minosoft.gui.rendering.gui.hud.elements.title.TitleHUDElement
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMesh
import de.bixilon.minosoft.gui.rendering.modding.events.ResizeWindowEvent import de.bixilon.minosoft.gui.rendering.modding.events.ResizeWindowEvent
import de.bixilon.minosoft.gui.rendering.system.base.IntegratedBufferTypes import de.bixilon.minosoft.gui.rendering.system.base.IntegratedBufferTypes
import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem
@ -49,7 +48,6 @@ import de.bixilon.minosoft.util.KUtil.synchronizedMapOf
import de.bixilon.minosoft.util.KUtil.toResourceLocation import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.KUtil.toSynchronizedMap import de.bixilon.minosoft.util.KUtil.toSynchronizedMap
import de.bixilon.minosoft.util.KUtil.unsafeCast import de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.collections.DirectArrayFloatList
import glm_.glm import glm_.glm
import glm_.mat4x4.Mat4 import glm_.mat4x4.Mat4
import glm_.vec2.Vec2 import glm_.vec2.Vec2
@ -61,7 +59,6 @@ class HUDRenderer(
) : Renderer, OtherDrawable { ) : Renderer, OtherDrawable {
override val renderSystem: RenderSystem = renderWindow.renderSystem override val renderSystem: RenderSystem = renderWindow.renderSystem
val shader = renderWindow.renderSystem.createShader("minosoft:hud".toResourceLocation()) val shader = renderWindow.renderSystem.createShader("minosoft:hud".toResourceLocation())
private lateinit var mesh: GUIMesh
var scaledSize: Vec2i = renderWindow.window.size var scaledSize: Vec2i = renderWindow.window.size
var matrix: Mat4 = Mat4() var matrix: Mat4 = Mat4()
private var enabled = true private var enabled = true
@ -144,6 +141,9 @@ class HUDRenderer(
for (element in this.hudElements.toSynchronizedMap().values) { for (element in this.hudElements.toSynchronizedMap().values) {
element.postInit() element.postInit()
if (element is LayoutedHUDElement<*>) {
element.initMesh()
}
} }
} }
@ -154,15 +154,6 @@ class HUDRenderer(
} }
override fun drawOther() { override fun drawOther() {
val data = if (this::mesh.isInitialized) {
mesh.unload()
mesh.data.buffer.clear()
mesh.data
} else {
DirectArrayFloatList()
}
mesh = GUIMesh(renderWindow, matrix, data)
val hudElements = hudElements.toSynchronizedMap().values val hudElements = hudElements.toSynchronizedMap().values
val time = System.currentTimeMillis() val time = System.currentTimeMillis()
@ -192,13 +183,18 @@ class HUDRenderer(
element.draw() element.draw()
} }
if (element is LayoutedHUDElement<*>) { if (element is LayoutedHUDElement<*>) {
z += element.layout.render(element.layoutOffset, z, mesh, null) z += element.prepare(z)
} }
} }
setup() setup()
mesh.load()
mesh.draw() for (element in hudElements) {
if (element !is LayoutedHUDElement<*> || !element.enabled || element.mesh.data.isEmpty) {
continue
}
element.mesh.draw()
}
if (matrixChange) { if (matrixChange) {
matrixChange = false matrixChange = false

View File

@ -17,11 +17,17 @@ import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.gui.elements.Element import de.bixilon.minosoft.gui.rendering.gui.elements.Element
import de.bixilon.minosoft.gui.rendering.gui.hud.HUDElement import de.bixilon.minosoft.gui.rendering.gui.hud.HUDElement
import de.bixilon.minosoft.gui.rendering.gui.hud.HUDRenderer import de.bixilon.minosoft.gui.rendering.gui.hud.HUDRenderer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMesh
import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh
import de.bixilon.minosoft.util.collections.floats.DirectArrayFloatList
import glm_.vec2.Vec2i import glm_.vec2.Vec2i
abstract class LayoutedHUDElement<T : Element>(final override val hudRenderer: HUDRenderer) : HUDElement { abstract class LayoutedHUDElement<T : Element>(final override val hudRenderer: HUDRenderer) : HUDElement {
override val renderWindow: RenderWindow = hudRenderer.renderWindow final override val renderWindow: RenderWindow = hudRenderer.renderWindow
override var enabled = true override var enabled = true
var mesh: GUIMesh = GUIMesh(renderWindow, hudRenderer.matrix, DirectArrayFloatList(1000))
private var lastRevision = 0L
abstract val layout: T abstract val layout: T
abstract val layoutOffset: Vec2i abstract val layoutOffset: Vec2i
@ -29,4 +35,32 @@ abstract class LayoutedHUDElement<T : Element>(final override val hudRenderer: H
override fun tick() { override fun tick() {
layout.tick() layout.tick()
} }
private fun createNewMesh() {
val mesh = this.mesh
if (mesh.state == Mesh.MeshStates.LOADED) {
mesh.unload()
}
this.mesh = GUIMesh(renderWindow, hudRenderer.matrix, mesh.data)
}
fun prepare(z: Int): Int {
val layoutOffset = layoutOffset
val usedZ = layout.render(layoutOffset, z, mesh, null)
val revision = layout.cache.revision
if (revision != lastRevision) {
createNewMesh()
this.mesh.load()
this.lastRevision = revision
}
return usedZ
}
fun initMesh() {
layout.cache.data = mesh.data
mesh.load()
}
} }

View File

@ -25,7 +25,7 @@ import de.bixilon.minosoft.gui.rendering.input.camera.hit.BlockRaycastHit
import de.bixilon.minosoft.gui.rendering.input.camera.hit.EntityRaycastHit import de.bixilon.minosoft.gui.rendering.input.camera.hit.EntityRaycastHit
import de.bixilon.minosoft.gui.rendering.system.base.BlendingFunctions import de.bixilon.minosoft.gui.rendering.system.base.BlendingFunctions
import de.bixilon.minosoft.util.KUtil.toResourceLocation import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.collections.DirectArrayFloatList import de.bixilon.minosoft.util.collections.floats.DirectArrayFloatList
class CrosshairHUDElement(hudRenderer: HUDRenderer) : CustomHUDElement(hudRenderer) { class CrosshairHUDElement(hudRenderer: HUDRenderer) : CustomHUDElement(hudRenderer) {
private lateinit var crosshairAtlasElement: HUDAtlasElement private lateinit var crosshairAtlasElement: HUDAtlasElement

View File

@ -19,7 +19,7 @@ import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh
import de.bixilon.minosoft.gui.rendering.util.mesh.MeshStruct import de.bixilon.minosoft.gui.rendering.util.mesh.MeshStruct
import de.bixilon.minosoft.util.collections.DirectArrayFloatList import de.bixilon.minosoft.util.collections.floats.DirectArrayFloatList
import glm_.mat4x4.Mat4 import glm_.mat4x4.Mat4
import glm_.vec2.Vec2 import glm_.vec2.Vec2
import glm_.vec2.Vec2t import glm_.vec2.Vec2t

View File

@ -16,27 +16,39 @@ package de.bixilon.minosoft.gui.rendering.gui.mesh
import de.bixilon.minosoft.data.text.RGBColor import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY
import de.bixilon.minosoft.util.collections.ArrayFloatList import de.bixilon.minosoft.util.collections.floats.AbstractFloatList
import de.bixilon.minosoft.util.collections.floats.HeapArrayFloatList
import glm_.mat4x4.Mat4 import glm_.mat4x4.Mat4
import glm_.vec2.Vec2 import glm_.vec2.Vec2
import glm_.vec2.Vec2i import glm_.vec2.Vec2i
import glm_.vec2.Vec2t import glm_.vec2.Vec2t
class GUIMeshCache( class GUIMeshCache(
val matrix: Mat4, var matrix: Mat4,
override val order: Array<Pair<Int, Int>>, override val order: Array<Pair<Int, Int>>,
initialCacheSize: Int = 1000, initialCacheSize: Int = 1000,
var data: AbstractFloatList = HeapArrayFloatList(initialCacheSize),
) : GUIVertexConsumer { ) : GUIVertexConsumer {
val data: ArrayFloatList = ArrayFloatList(initialCacheSize) var revision: Long = 0
var offset: Vec2i = Vec2i.EMPTY var offset: Vec2i = Vec2i.EMPTY
var z: Int = 0 var z: Int = 0
var maxZ: Int = 0 var maxZ: Int = 0
fun clear() {
if (data.finished) {
data = HeapArrayFloatList(initialSize = data.size)
} else {
data.clear()
}
}
override fun addVertex(position: Vec2t<*>, z: Int, texture: AbstractTexture, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) { override fun addVertex(position: Vec2t<*>, z: Int, texture: AbstractTexture, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) {
data.addAll(GUIMesh.createVertex(matrix, position, z, texture, uv, tint, options)) data.addAll(GUIMesh.createVertex(matrix, position, z, texture, uv, tint, options))
revision++
} }
override fun addCache(cache: GUIMeshCache) { override fun addCache(cache: GUIMeshCache) {
data.addAll(cache.data) data.addAll(cache.data)
revision++
} }
} }

View File

@ -27,9 +27,13 @@ class FloatOpenGLVertexBuffer(override val structure: MeshStruct, data: FloatBuf
glBindVertexArray(vao) glBindVertexArray(vao)
bind() bind()
val previousLimit = buffer.limit()
val previousPosition = buffer.position()
buffer.limit(buffer.position()) buffer.limit(buffer.position())
buffer.flip() buffer.flip()
glBufferData(type.gl, buffer, drawTypes.gl) glBufferData(type.gl, buffer, drawTypes.gl)
buffer.limit(previousLimit)
buffer.position(previousPosition)
state = RenderBufferStates.UPLOADED state = RenderBufferStates.UPLOADED
_data = null _data = null

View File

@ -16,7 +16,7 @@ package de.bixilon.minosoft.gui.rendering.util.mesh
import de.bixilon.minosoft.gui.rendering.RenderWindow import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.system.base.buffer.vertex.FloatVertexBuffer import de.bixilon.minosoft.gui.rendering.system.base.buffer.vertex.FloatVertexBuffer
import de.bixilon.minosoft.gui.rendering.system.base.buffer.vertex.PrimitiveTypes import de.bixilon.minosoft.gui.rendering.system.base.buffer.vertex.PrimitiveTypes
import de.bixilon.minosoft.util.collections.DirectArrayFloatList import de.bixilon.minosoft.util.collections.floats.DirectArrayFloatList
import glm_.vec2.Vec2 import glm_.vec2.Vec2
import glm_.vec3.Vec3 import glm_.vec3.Vec3
@ -53,6 +53,7 @@ abstract class Mesh(
_data = null _data = null
} }
vertices = buffer.vertices vertices = buffer.vertices
state = MeshStates.LOADED
} }
fun draw() { fun draw() {

View File

@ -0,0 +1,15 @@
package de.bixilon.minosoft.util.collections
abstract class AbstractPrimitiveList<T> : Clearable {
var finished: Boolean = false
protected set
abstract val limit: Int
abstract val size: Int
abstract val isEmpty: Boolean
protected abstract fun ensureSize(needed: Int)
abstract fun add(value: T)
abstract fun finish()
}

View File

@ -0,0 +1,11 @@
package de.bixilon.minosoft.util.collections.floats
import de.bixilon.minosoft.util.collections.AbstractPrimitiveList
abstract class AbstractFloatList : AbstractPrimitiveList<Float>() {
abstract fun addAll(floats: FloatArray)
abstract fun addAll(floatList: AbstractFloatList)
abstract fun toArray(): FloatArray
}

View File

@ -11,25 +11,24 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.util.collections package de.bixilon.minosoft.util.collections.floats
import de.bixilon.minosoft.util.KUtil import de.bixilon.minosoft.util.KUtil
import org.lwjgl.system.MemoryUtil.memAllocFloat import org.lwjgl.system.MemoryUtil.memAllocFloat
import org.lwjgl.system.MemoryUtil.memFree import org.lwjgl.system.MemoryUtil.memFree
import java.nio.BufferOverflowException
import java.nio.FloatBuffer import java.nio.FloatBuffer
class DirectArrayFloatList( class DirectArrayFloatList(
initialSize: Int = DEFAULT_INITIAL_SIZE, initialSize: Int = DEFAULT_INITIAL_SIZE,
) { ) : AbstractFloatList() {
var buffer: FloatBuffer = memAllocFloat(initialSize) // ToDo: Clear when disconnected var buffer: FloatBuffer = memAllocFloat(initialSize)
private set private set
var finalized: Boolean = false override val limit: Int
private set
val capacity: Int
get() = buffer.capacity() get() = buffer.capacity()
val size: Int override val size: Int
get() = buffer.position() get() = buffer.position()
val isEmpty: Boolean override val isEmpty: Boolean
get() = size == 0 get() = size == 0
private var unloaded = false private var unloaded = false
@ -43,17 +42,17 @@ class DirectArrayFloatList(
private var outputUpToDate = false private var outputUpToDate = false
private fun checkFinalized() { private fun checkFinalized() {
if (finalized) { if (finished) {
throw IllegalStateException("ArrayFloatList is already finalized!") throw IllegalStateException("ArrayFloatList is already finalized!")
} }
} }
private fun ensureSize(needed: Int) { override fun ensureSize(needed: Int) {
checkFinalized() checkFinalized()
if (capacity - size >= needed) { if (limit - size >= needed) {
return return
} }
var newSize = capacity var newSize = limit
while (newSize - size < needed) { while (newSize - size < needed) {
newSize += nextGrowStep newSize += nextGrowStep
} }
@ -70,19 +69,26 @@ class DirectArrayFloatList(
memFree(oldBuffer) memFree(oldBuffer)
} }
fun add(float: Float) { override fun add(value: Float) {
ensureSize(1) ensureSize(1)
buffer.put(float) buffer.put(value)
outputUpToDate = false outputUpToDate = false
} }
fun addAll(floats: FloatArray) { override fun addAll(floats: FloatArray) {
ensureSize(floats.size) ensureSize(floats.size)
try {
buffer.put(floats) buffer.put(floats)
} catch (exception: BufferOverflowException) {
ensureSize(floats.size)
exception.printStackTrace()
}
outputUpToDate = false outputUpToDate = false
} }
fun addAll(floatList: DirectArrayFloatList) { override fun addAll(floatList: AbstractFloatList) {
if (floatList is DirectArrayFloatList) {
ensureSize(floatList.size) ensureSize(floatList.size)
if (FLOAT_PUT_METHOD == null) { // Java < 16 if (FLOAT_PUT_METHOD == null) { // Java < 16
for (i in 0 until floatList.buffer.position()) { for (i in 0 until floatList.buffer.position()) {
@ -92,11 +98,9 @@ class DirectArrayFloatList(
FLOAT_PUT_METHOD.invoke(buffer, buffer.position(), floatList.buffer, 0, floatList.buffer.position()) FLOAT_PUT_METHOD.invoke(buffer, buffer.position(), floatList.buffer, 0, floatList.buffer.position())
buffer.position(buffer.position() + floatList.buffer.position()) buffer.position(buffer.position() + floatList.buffer.position())
} }
} else {
addAll(floatList.toArray())
} }
fun addAll(floatList: ArrayFloatList) {
ensureSize(floatList.size)
buffer.put(floatList.toArray())
} }
private fun checkOutputArray() { private fun checkOutputArray() {
@ -111,7 +115,7 @@ class DirectArrayFloatList(
outputUpToDate = true outputUpToDate = true
} }
fun toArray(): FloatArray { override fun toArray(): FloatArray {
checkOutputArray() checkOutputArray()
return output return output
} }
@ -119,12 +123,20 @@ class DirectArrayFloatList(
fun unload() { fun unload() {
check(!unloaded) { "Already unloaded!" } check(!unloaded) { "Already unloaded!" }
unloaded = true unloaded = true
finalized = true // Is unloaded finished = true // Is unloaded
memFree(buffer) memFree(buffer)
} }
fun finish() { override fun clear() {
finalized = true buffer.clear()
if (output.isNotEmpty()) {
output = FloatArray(0)
}
outputUpToDate = false
}
override fun finish() {
finished = true
val oldBuffer = buffer val oldBuffer = buffer
buffer = memAllocFloat(oldBuffer.position()) buffer = memAllocFloat(oldBuffer.position())
if (FLOAT_PUT_METHOD == null) { // Java < 16 if (FLOAT_PUT_METHOD == null) { // Java < 16

View File

@ -10,19 +10,16 @@
* *
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.util.collections package de.bixilon.minosoft.util.collections.floats
class ArrayFloatList( class HeapArrayFloatList(
initialSize: Int = DEFAULT_INITIAL_SIZE, initialSize: Int = DEFAULT_INITIAL_SIZE,
) { ) : AbstractFloatList() {
private var data: FloatArray = FloatArray(initialSize) private var data: FloatArray = FloatArray(initialSize)
var finalized: Boolean = false override val limit: Int
private set
val limit: Int
get() = data.size get() = data.size
var size = 0 override var size = 0
private set override val isEmpty: Boolean
val isEmpty: Boolean
get() = size == 0 get() = size == 0
private val nextGrowStep = when { private val nextGrowStep = when {
@ -35,19 +32,19 @@ class ArrayFloatList(
private var outputUpToDate = false private var outputUpToDate = false
private fun checkFinalized() { private fun checkFinalized() {
if (finalized) { if (finished) {
throw IllegalStateException("ArrayFloatList is already finalized!") throw IllegalStateException("ArrayFloatList is already finalized!")
} }
} }
fun clear() { override fun clear() {
checkFinalized() checkFinalized()
size = 0 size = 0
outputUpToDate = false outputUpToDate = false
output = FloatArray(0) output = FloatArray(0)
} }
private fun ensureSize(needed: Int) { override fun ensureSize(needed: Int) {
checkFinalized() checkFinalized()
if (limit - size >= needed) { if (limit - size >= needed) {
return return
@ -61,26 +58,30 @@ class ArrayFloatList(
System.arraycopy(oldData, 0, data, 0, oldData.size) System.arraycopy(oldData, 0, data, 0, oldData.size)
} }
fun add(float: Float) { override fun add(value: Float) {
ensureSize(1) ensureSize(1)
data[size++] = float data[size++] = value
outputUpToDate = false outputUpToDate = false
} }
fun addAll(floats: FloatArray) { override fun addAll(floats: FloatArray) {
ensureSize(floats.size) ensureSize(floats.size)
System.arraycopy(floats, 0, data, size, floats.size) System.arraycopy(floats, 0, data, size, floats.size)
size += floats.size size += floats.size
outputUpToDate = false outputUpToDate = false
} }
fun addAll(floatList: ArrayFloatList) { override fun addAll(floatList: AbstractFloatList) {
ensureSize(floatList.size) ensureSize(floatList.size)
val source = if (floatList.finalized) { val source: FloatArray = if (floatList is HeapArrayFloatList) {
if (floatList.finished) {
floatList.output floatList.output
} else { } else {
floatList.data floatList.data
} }
} else {
floatList.toArray()
}
System.arraycopy(source, 0, data, size, floatList.size) System.arraycopy(source, 0, data, size, floatList.size)
size += floatList.size size += floatList.size
} }
@ -94,13 +95,13 @@ class ArrayFloatList(
outputUpToDate = true outputUpToDate = true
} }
fun toArray(): FloatArray { override fun toArray(): FloatArray {
checkOutputArray() checkOutputArray()
return output return output
} }
fun finish() { override fun finish() {
finalized = true finished = true
checkOutputArray() checkOutputArray()
data = FloatArray(0) data = FloatArray(0)
} }