From 66507121583995ed66e09dca23ac95e6366d56b0 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Thu, 15 Dec 2022 12:58:05 +0100 Subject: [PATCH] fragmented array list --- .../world/overlay/overlays/arm/ArmMesh.kt | 4 +- .../gui/rendering/gui/elements/Element.kt | 4 +- .../gui/rendering/gui/gui/GUIMeshElement.kt | 9 +- .../hud/elements/other/CrosshairHUDElement.kt | 4 +- .../gui/rendering/gui/mesh/GUIMesh.kt | 5 +- .../gui/rendering/gui/mesh/GUIMeshCache.kt | 2 +- .../gui/rendering/particle/ParticleMesh.kt | 8 +- .../rendering/particle/ParticleRenderer.kt | 6 +- .../gui/rendering/sky/box/SkyboxMesh.kt | 40 +- .../rendering/sky/box/SkyboxTextureMesh.kt | 2 +- .../gui/rendering/sky/clouds/CloudMesh.kt | 2 +- .../sky/planet/scatter/SunScatterMesh.kt | 2 +- .../gui/rendering/system/base/RenderSystem.kt | 9 + .../system/opengl/buffer/FloatOpenGLBuffer.kt | 9 +- .../minosoft/gui/rendering/util/mesh/Mesh.kt | 20 +- .../rendering/world/border/WorldBorderMesh.kt | 39 +- .../rendering/world/mesh/SingleWorldMesh.kt | 4 +- .../gui/rendering/world/mesh/WorldMesh.kt | 10 +- .../minosoft/util/collections/DirectList.kt | 16 + .../collections/floats/AbstractFloatList.kt | 9 +- .../floats/BufferedArrayFloatList.kt | 152 ++++++ .../floats/DirectArrayFloatList.kt | 156 +----- .../util/collections/floats/FloatListUtil.kt | 62 +++ .../floats/FragmentedArrayFloatList.kt | 231 +++++++++ .../collections/floats/HeapArrayFloatList.kt | 47 +- .../floats/AbstractFloatListTest.kt | 449 ++++++++++++++++++ .../floats/BufferedFloatListTest.kt | 21 + .../collections/floats/DirectFloatListTest.kt | 45 ++ .../floats/FragmentedFloatListTest.kt | 47 ++ .../collections/floats/HeapFloatListTest.kt | 21 + 30 files changed, 1184 insertions(+), 251 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/util/collections/DirectList.kt create mode 100644 src/main/java/de/bixilon/minosoft/util/collections/floats/BufferedArrayFloatList.kt create mode 100644 src/main/java/de/bixilon/minosoft/util/collections/floats/FloatListUtil.kt create mode 100644 src/main/java/de/bixilon/minosoft/util/collections/floats/FragmentedArrayFloatList.kt create mode 100644 src/test/java/de/bixilon/minosoft/util/collections/floats/AbstractFloatListTest.kt create mode 100644 src/test/java/de/bixilon/minosoft/util/collections/floats/BufferedFloatListTest.kt create mode 100644 src/test/java/de/bixilon/minosoft/util/collections/floats/DirectFloatListTest.kt create mode 100644 src/test/java/de/bixilon/minosoft/util/collections/floats/FragmentedFloatListTest.kt create mode 100644 src/test/java/de/bixilon/minosoft/util/collections/floats/HeapFloatListTest.kt diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/framebuffer/world/overlay/overlays/arm/ArmMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/framebuffer/world/overlay/overlays/arm/ArmMesh.kt index 8994b337c..cdb51ad2c 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/framebuffer/world/overlay/overlays/arm/ArmMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/framebuffer/world/overlay/overlays/arm/ArmMesh.kt @@ -29,8 +29,8 @@ open class ArmMesh(renderWindow: RenderWindow, primitiveType: PrimitiveTypes = r fun addVertex(position: FloatArray, uv: Vec2) { - data.addAll(position) - data.addAll(uv.array) + data.add(position) + data.add(uv.array) } override fun addVertex(position: FloatArray, transformedUV: Vec2, transform: Float, textureShaderId: Float, flags: Float) { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/Element.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/Element.kt index a1701d695..89eb64e01 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/Element.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/Element.kt @@ -30,7 +30,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.vec4.Vec4iUtil.EMPTY import de.bixilon.minosoft.gui.rendering.util.vec.vec4.Vec4iUtil.spaceSize -import de.bixilon.minosoft.util.collections.floats.DirectArrayFloatList +import de.bixilon.minosoft.util.collections.DirectList abstract class Element(val guiRenderer: GUIRenderer, initialCacheSize: Int = 1000) : InputElement, DragTarget { var ignoreDisplaySize = false @@ -168,7 +168,7 @@ abstract class Element(val guiRenderer: GUIRenderer, initialCacheSize: Int = 100 cache.offset = Vec2i(offset) cache.options = options forceRender(offset, cache, options) - if (cache.data !is DirectArrayFloatList) { + if (cache.data !is DirectList) { // not raw mesh data cache.data.finish() } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/GUIMeshElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/GUIMeshElement.kt index 04cf03122..18fb96106 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/GUIMeshElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/GUIMeshElement.kt @@ -16,6 +16,7 @@ package de.bixilon.minosoft.gui.rendering.gui.gui import de.bixilon.kotlinglm.vec2.Vec2d import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kutil.time.TimeUtil +import de.bixilon.kutil.time.TimeUtil.millis import de.bixilon.minosoft.config.key.KeyCodes import de.bixilon.minosoft.gui.rendering.RenderWindow import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer @@ -33,7 +34,7 @@ import de.bixilon.minosoft.gui.rendering.renderer.drawable.Drawable import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY -import de.bixilon.minosoft.util.collections.floats.DirectArrayFloatList +import de.bixilon.minosoft.util.collections.floats.FloatListUtil open class GUIMeshElement( val element: T, @@ -41,7 +42,7 @@ open class GUIMeshElement( override val guiRenderer: GUIRenderer = element.guiRenderer override val renderWindow: RenderWindow = guiRenderer.renderWindow private val clickCounter = MouseClickCounter() - var mesh: GUIMesh = GUIMesh(renderWindow, guiRenderer.matrix, DirectArrayFloatList(1000)) + var mesh: GUIMesh = GUIMesh(renderWindow, guiRenderer.matrix, FloatListUtil.direct(1000)) override val skipDraw: Boolean get() = if (element is BaseDrawable) element.skipDraw else false protected var lastRevision = 0L @@ -144,7 +145,7 @@ open class GUIMeshElement( val mouseAction = MouseActions[type] ?: return false - return element.onMouseAction(position, mouseButton, mouseAction, clickCounter.getClicks(mouseButton, mouseAction, position, TimeUtil.millis)) + return element.onMouseAction(position, mouseButton, mouseAction, clickCounter.getClicks(mouseButton, mouseAction, position, millis())) } override fun onScroll(scrollOffset: Vec2d): Boolean { @@ -167,7 +168,7 @@ open class GUIMeshElement( val mouseAction = MouseActions[type] ?: return null - return element.onDragMouseAction(position, mouseButton, mouseAction, clickCounter.getClicks(mouseButton, mouseAction, position, TimeUtil.millis), dragged) + return element.onDragMouseAction(position, mouseButton, mouseAction, clickCounter.getClicks(mouseButton, mouseAction, position, millis()), dragged) } override fun onDragScroll(scrollOffset: Vec2d, dragged: Dragged): Element? { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/CrosshairHUDElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/CrosshairHUDElement.kt index 8f44fe3c0..68ee26607 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/CrosshairHUDElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/CrosshairHUDElement.kt @@ -27,7 +27,7 @@ import de.bixilon.minosoft.gui.rendering.gui.hud.elements.HUDBuilder import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMesh import de.bixilon.minosoft.gui.rendering.system.base.BlendingFunctions import de.bixilon.minosoft.util.KUtil.toResourceLocation -import de.bixilon.minosoft.util.collections.floats.DirectArrayFloatList +import de.bixilon.minosoft.util.collections.floats.BufferedArrayFloatList class CrosshairHUDElement(guiRenderer: GUIRenderer) : CustomHUDElement(guiRenderer) { private val profile = guiRenderer.connection.profiles.gui @@ -90,7 +90,7 @@ class CrosshairHUDElement(guiRenderer: GUIRenderer) : CustomHUDElement(guiRender mesh?.unload() this.mesh = null - val mesh = GUIMesh(renderWindow, guiRenderer.matrix, DirectArrayFloatList(42)) + val mesh = GUIMesh(renderWindow, guiRenderer.matrix, BufferedArrayFloatList(42)) val start = (guiRenderer.scaledSize - CROSSHAIR_SIZE) / 2 mesh.addQuad(start, start + CROSSHAIR_SIZE, crosshairAtlasElement, crosshairProfile.color, null) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/mesh/GUIMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/mesh/GUIMesh.kt index 05e1e50c0..cc7a047af 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/mesh/GUIMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/mesh/GUIMesh.kt @@ -24,12 +24,11 @@ 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.vec.vec2.Vec2iUtil.orthoTimes import de.bixilon.minosoft.util.collections.floats.AbstractFloatList -import de.bixilon.minosoft.util.collections.floats.DirectArrayFloatList class GUIMesh( renderWindow: RenderWindow, val matrix: Mat4, - data: DirectArrayFloatList, + data: AbstractFloatList, ) : Mesh(renderWindow, GUIMeshStruct, initialCacheSize = 40000, clearOnLoad = false, data = data), GUIVertexConsumer { override fun addVertex(position: Vec2t<*>, texture: ShaderIdentifiable, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) { @@ -37,7 +36,7 @@ class GUIMesh( } override fun addCache(cache: GUIMeshCache) { - data.addAll(cache.data) + data.add(cache.data) } data class GUIMeshStruct( diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/mesh/GUIMeshCache.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/mesh/GUIMeshCache.kt index 76ecda6b3..5431f5e11 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/mesh/GUIMeshCache.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/mesh/GUIMeshCache.kt @@ -47,7 +47,7 @@ class GUIMeshCache( } override fun addCache(cache: GUIMeshCache) { - data.addAll(cache.data) + data.add(cache.data) revision++ } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleMesh.kt index 7fbb8d1a4..c939de43e 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleMesh.kt @@ -23,9 +23,9 @@ import de.bixilon.minosoft.gui.rendering.system.base.buffer.vertex.PrimitiveType 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.MeshStruct -import de.bixilon.minosoft.util.collections.floats.DirectArrayFloatList +import de.bixilon.minosoft.util.collections.floats.AbstractFloatList -class ParticleMesh(renderWindow: RenderWindow, data: DirectArrayFloatList) : Mesh(renderWindow, ParticleMeshStruct, PrimitiveTypes.POINT, -1, clearOnLoad = false, data = data) { +class ParticleMesh(renderWindow: RenderWindow, data: AbstractFloatList) : Mesh(renderWindow, ParticleMeshStruct, PrimitiveTypes.POINT, -1, clearOnLoad = false, data = data) { fun addVertex(position: Vec3d, scale: Float, texture: AbstractTexture, tintColor: RGBColor, uvMin: FloatArray? = null, uvMax: FloatArray? = null, light: Int) { val minTransformedUV = if (uvMin == null) { @@ -38,8 +38,8 @@ class ParticleMesh(renderWindow: RenderWindow, data: DirectArrayFloatList) : Mes data.add(position.x.toFloat()) data.add(position.y.toFloat()) data.add(position.z.toFloat()) - data.addAll(minTransformedUV) - data.addAll(maxTransformedUV) + data.add(minTransformedUV) + data.add(maxTransformedUV) data.add(texture.renderData.shaderTextureId.buffer()) data.add(scale) data.add(tintColor.rgba.buffer()) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt index 9dee2e54b..c71168d99 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt @@ -39,7 +39,7 @@ import de.bixilon.minosoft.protocol.network.connection.play.PlayConnectionStates import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.util.KUtil.minosoft import de.bixilon.minosoft.util.chunk.ChunkUtil.isInViewDistance -import de.bixilon.minosoft.util.collections.floats.DirectArrayFloatList +import de.bixilon.minosoft.util.collections.floats.BufferedArrayFloatList class ParticleRenderer( @@ -52,8 +52,8 @@ class ParticleRenderer( private val translucentShader = renderSystem.createShader(minosoft("particle")) { ParticleShader(it, false) } // There is no opaque mesh because it is simply not needed (every particle has transparency) - private var transparentMesh = ParticleMesh(renderWindow, DirectArrayFloatList(RenderConstants.MAXIMUM_PARTICLE_AMOUNT * ParticleMesh.ParticleMeshStruct.FLOATS_PER_VERTEX)) - private var translucentMesh = ParticleMesh(renderWindow, DirectArrayFloatList(RenderConstants.MAXIMUM_PARTICLE_AMOUNT * ParticleMesh.ParticleMeshStruct.FLOATS_PER_VERTEX)) + private var transparentMesh = ParticleMesh(renderWindow, BufferedArrayFloatList(RenderConstants.MAXIMUM_PARTICLE_AMOUNT * ParticleMesh.ParticleMeshStruct.FLOATS_PER_VERTEX)) + private var translucentMesh = ParticleMesh(renderWindow, BufferedArrayFloatList(RenderConstants.MAXIMUM_PARTICLE_AMOUNT * ParticleMesh.ParticleMeshStruct.FLOATS_PER_VERTEX)) private val particlesLock = SimpleLock() private var particles: MutableList = mutableListOf() diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/sky/box/SkyboxMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/sky/box/SkyboxMesh.kt index 57919326b..30f96b499 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/sky/box/SkyboxMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/sky/box/SkyboxMesh.kt @@ -14,7 +14,6 @@ package de.bixilon.minosoft.gui.rendering.sky.box import de.bixilon.minosoft.gui.rendering.RenderWindow -import de.bixilon.minosoft.gui.rendering.system.base.MeshUtil.buffer import de.bixilon.minosoft.gui.rendering.system.base.buffer.vertex.PrimitiveTypes import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh import de.bixilon.minosoft.gui.rendering.util.mesh.PositionOnlyMeshStruct @@ -22,27 +21,28 @@ import de.bixilon.minosoft.gui.rendering.util.mesh.PositionOnlyMeshStruct class SkyboxMesh(renderWindow: RenderWindow) : Mesh(renderWindow, PositionOnlyMeshStruct, PrimitiveTypes.TRIANGLE, initialCacheSize = 6 * 2 * 3 * PositionOnlyMeshStruct.FLOATS_PER_VERTEX) { init { - data.addAll(floatArrayOf( - -1.0f, +1.0f, -1.0f, - -1.0f, -1.0f, -1.0f, - +1.0f, -1.0f, -1.0f, - +1.0f, -1.0f, -1.0f, - +1.0f, +1.0f, -1.0f, - -1.0f, +1.0f, -1.0f, + data.add( + floatArrayOf( + -1.0f, +1.0f, -1.0f, + -1.0f, -1.0f, -1.0f, + +1.0f, -1.0f, -1.0f, + +1.0f, -1.0f, -1.0f, + +1.0f, +1.0f, -1.0f, + -1.0f, +1.0f, -1.0f, - -1.0f, -1.0f, +1.0f, - -1.0f, -1.0f, -1.0f, - -1.0f, +1.0f, -1.0f, - -1.0f, +1.0f, -1.0f, - -1.0f, +1.0f, +1.0f, - -1.0f, -1.0f, +1.0f, + -1.0f, -1.0f, +1.0f, + -1.0f, -1.0f, -1.0f, + -1.0f, +1.0f, -1.0f, + -1.0f, +1.0f, -1.0f, + -1.0f, +1.0f, +1.0f, + -1.0f, -1.0f, +1.0f, - +1.0f, -1.0f, -1.0f, - +1.0f, -1.0f, +1.0f, - +1.0f, +1.0f, +1.0f, - +1.0f, +1.0f, +1.0f, - +1.0f, +1.0f, -1.0f, - +1.0f, -1.0f, -1.0f, + +1.0f, -1.0f, -1.0f, + +1.0f, -1.0f, +1.0f, + +1.0f, +1.0f, +1.0f, + +1.0f, +1.0f, +1.0f, + +1.0f, +1.0f, -1.0f, + +1.0f, -1.0f, -1.0f, -1.0f, -1.0f, +1.0f, -1.0f, +1.0f, +1.0f, diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/sky/box/SkyboxTextureMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/sky/box/SkyboxTextureMesh.kt index 9535b68ba..f42fc85d5 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/sky/box/SkyboxTextureMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/sky/box/SkyboxTextureMesh.kt @@ -23,7 +23,7 @@ import de.bixilon.minosoft.gui.rendering.util.mesh.MeshStruct class SkyboxTextureMesh(renderWindow: RenderWindow) : Mesh(renderWindow, SkyboxTextureMeshStruct, PrimitiveTypes.TRIANGLE, initialCacheSize = 6 * 2 * 3 * SkyboxTextureMeshStruct.FLOATS_PER_VERTEX) { init { - data.addAll( + data.add( floatArrayOf( -1.0f, +1.0f, -1.0f, 0.buffer(), -1.0f, -1.0f, -1.0f, 3.buffer(), diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/sky/clouds/CloudMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/sky/clouds/CloudMesh.kt index 61bcb1995..256f07f98 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/sky/clouds/CloudMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/sky/clouds/CloudMesh.kt @@ -25,7 +25,7 @@ import de.bixilon.minosoft.gui.rendering.util.mesh.MeshStruct class CloudMesh(renderWindow: RenderWindow) : Mesh(renderWindow, CloudMeshStruct, renderWindow.renderSystem.preferredPrimitiveType) { fun addVertex(start: Vec3, side: Directions) { - data.addAll(start.array) + data.add(start.array) data.add(side.ordinal.buffer()) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/sky/planet/scatter/SunScatterMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/sky/planet/scatter/SunScatterMesh.kt index 044768508..6c47cf186 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/sky/planet/scatter/SunScatterMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/sky/planet/scatter/SunScatterMesh.kt @@ -21,7 +21,7 @@ import de.bixilon.minosoft.gui.rendering.util.mesh.PositionOnlyMeshStruct class SunScatterMesh(renderWindow: RenderWindow) : Mesh(renderWindow, PositionOnlyMeshStruct, PrimitiveTypes.TRIANGLE, initialCacheSize = 6 * 2 * 3 * PositionOnlyMeshStruct.FLOATS_PER_VERTEX) { init { - data.addAll( + data.add( floatArrayOf( -1.0f, +0.2f, -1.0f, -1.0f, -0.2f, -1.0f, diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/RenderSystem.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/RenderSystem.kt index 5f6844114..85a61eb51 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/RenderSystem.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/RenderSystem.kt @@ -29,6 +29,8 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureManager import de.bixilon.minosoft.gui.rendering.system.opengl.OpenGLRenderSystem import de.bixilon.minosoft.gui.rendering.util.mesh.MeshStruct import de.bixilon.minosoft.util.KUtil.toResourceLocation +import de.bixilon.minosoft.util.collections.floats.AbstractFloatList +import de.bixilon.minosoft.util.collections.floats.DirectArrayFloatList import java.nio.ByteBuffer import java.nio.FloatBuffer @@ -117,6 +119,13 @@ interface RenderSystem { fun createNativeShader(vertex: ResourceLocation, geometry: ResourceLocation? = null, fragment: ResourceLocation): NativeShader fun createVertexBuffer(structure: MeshStruct, data: FloatBuffer, primitiveType: PrimitiveTypes = preferredPrimitiveType): FloatVertexBuffer + fun createVertexBuffer(structure: MeshStruct, data: AbstractFloatList, primitiveType: PrimitiveTypes = preferredPrimitiveType): FloatVertexBuffer { + if (data is DirectArrayFloatList) { + return createVertexBuffer(structure, data.toBuffer(), primitiveType) + } + return createVertexBuffer(structure, FloatBuffer.wrap(data.toArray()), primitiveType) + } + fun createIntUniformBuffer(data: IntArray = IntArray(0)): IntUniformBuffer fun createFloatUniformBuffer(data: FloatBuffer): FloatUniformBuffer fun createFramebuffer(): Framebuffer diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/FloatOpenGLBuffer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/FloatOpenGLBuffer.kt index 2af8216b5..9f3b25978 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/FloatOpenGLBuffer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/FloatOpenGLBuffer.kt @@ -18,8 +18,9 @@ import de.bixilon.minosoft.gui.rendering.system.base.buffer.RenderableBufferDraw import de.bixilon.minosoft.gui.rendering.system.base.buffer.RenderableBufferStates import de.bixilon.minosoft.gui.rendering.system.base.buffer.RenderableBufferTypes import de.bixilon.minosoft.gui.rendering.system.opengl.OpenGLRenderSystem -import org.lwjgl.opengl.GL15.glBufferData import org.lwjgl.opengl.GL15.glBufferSubData +import org.lwjgl.opengl.GL15C +import org.lwjgl.system.MemoryUtil.memAddress import java.nio.FloatBuffer open class FloatOpenGLBuffer(renderSystem: OpenGLRenderSystem, protected var _data: FloatBuffer?) : OpenGLRenderableBuffer(renderSystem, RenderableBufferTypes.ARRAY_BUFFER), RenderFloatBuffer { @@ -33,7 +34,7 @@ open class FloatOpenGLBuffer(renderSystem: OpenGLRenderSystem, protected var _da override fun initialUpload() { bind() buffer.flip() - glBufferData(type.gl, buffer, drawTypes.gl) + nglBufferData(type.gl, buffer, drawTypes.gl) unbind() state = RenderableBufferStates.UPLOADED } @@ -43,4 +44,8 @@ open class FloatOpenGLBuffer(renderSystem: OpenGLRenderSystem, protected var _da glBufferSubData(type.gl, 0, buffer) unbind() } + + private fun nglBufferData(target: Int, buffer: FloatBuffer, usage: Int) { + GL15C.nglBufferData(target, Integer.toUnsignedLong(buffer.remaining()) shl 2, memAddress(buffer), usage) + } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/util/mesh/Mesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/util/mesh/Mesh.kt index 17b56896e..923cb9fac 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/util/mesh/Mesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/util/mesh/Mesh.kt @@ -15,10 +15,13 @@ package de.bixilon.minosoft.gui.rendering.util.mesh import de.bixilon.kotlinglm.vec2.Vec2 import de.bixilon.kotlinglm.vec3.Vec3 +import de.bixilon.kutil.cast.CastUtil.unsafeCast 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.PrimitiveTypes +import de.bixilon.minosoft.util.collections.floats.AbstractFloatList import de.bixilon.minosoft.util.collections.floats.DirectArrayFloatList +import de.bixilon.minosoft.util.collections.floats.FloatListUtil abstract class Mesh( val renderWindow: RenderWindow, @@ -26,18 +29,18 @@ abstract class Mesh( private val primitiveType: PrimitiveTypes = renderWindow.renderSystem.preferredPrimitiveType, var initialCacheSize: Int = 10000, val clearOnLoad: Boolean = true, - data: DirectArrayFloatList? = null, + data: AbstractFloatList? = null, val onDemand: Boolean = false, ) : AbstractVertexConsumer { override val order = renderWindow.renderSystem.primitiveMeshOrder val reversedOrder = order.reversedArray() - private var _data: DirectArrayFloatList? = data ?: if (onDemand) null else DirectArrayFloatList(initialCacheSize) - var data: DirectArrayFloatList + private var _data: AbstractFloatList? = data ?: if (onDemand) null else FloatListUtil.direct(initialCacheSize) + var data: AbstractFloatList get() { if (_data == null && onDemand) { - _data = DirectArrayFloatList(initialCacheSize) + _data = FloatListUtil.direct(initialCacheSize) } - return _data as DirectArrayFloatList + return _data.unsafeCast() } set(value) { _data = value @@ -53,10 +56,13 @@ abstract class Mesh( fun load() { - buffer = renderWindow.renderSystem.createVertexBuffer(struct, data.buffer, primitiveType) + val data = this.data + buffer = renderWindow.renderSystem.createVertexBuffer(struct, data, primitiveType) buffer.init() if (clearOnLoad) { - data.unload() + if (data is DirectArrayFloatList) { + data.unload() + } _data = null } vertices = buffer.vertices diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/world/border/WorldBorderMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/world/border/WorldBorderMesh.kt index b8a4b038f..793d85148 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/world/border/WorldBorderMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/world/border/WorldBorderMesh.kt @@ -23,27 +23,28 @@ import de.bixilon.minosoft.gui.rendering.util.mesh.MeshStruct class WorldBorderMesh(renderWindow: RenderWindow) : Mesh(renderWindow, WorldBorderMeshStruct, PrimitiveTypes.TRIANGLE, initialCacheSize = 6 * 2 * 3 * WorldBorderMeshStruct.FLOATS_PER_VERTEX) { init { - data.addAll(floatArrayOf( - -1.0f, +1.0f, -1.0f, 1.buffer(), - +1.0f, -1.0f, -1.0f, 0.buffer(), - -1.0f, -1.0f, -1.0f, 3.buffer(), - +1.0f, -1.0f, -1.0f, 0.buffer(), - -1.0f, +1.0f, -1.0f, 1.buffer(), - +1.0f, +1.0f, -1.0f, 2.buffer(), + data.add( + floatArrayOf( + -1.0f, +1.0f, -1.0f, 1.buffer(), + +1.0f, -1.0f, -1.0f, 0.buffer(), + -1.0f, -1.0f, -1.0f, 3.buffer(), + +1.0f, -1.0f, -1.0f, 0.buffer(), + -1.0f, +1.0f, -1.0f, 1.buffer(), + +1.0f, +1.0f, -1.0f, 2.buffer(), - -1.0f, -1.0f, +1.0f, 3.buffer(), - -1.0f, +1.0f, -1.0f, 2.buffer(), - -1.0f, -1.0f, -1.0f, 0.buffer(), - -1.0f, +1.0f, -1.0f, 2.buffer(), - -1.0f, -1.0f, +1.0f, 3.buffer(), - -1.0f, +1.0f, +1.0f, 1.buffer(), + -1.0f, -1.0f, +1.0f, 3.buffer(), + -1.0f, +1.0f, -1.0f, 2.buffer(), + -1.0f, -1.0f, -1.0f, 0.buffer(), + -1.0f, +1.0f, -1.0f, 2.buffer(), + -1.0f, -1.0f, +1.0f, 3.buffer(), + -1.0f, +1.0f, +1.0f, 1.buffer(), - +1.0f, -1.0f, -1.0f, 3.buffer(), - +1.0f, +1.0f, +1.0f, 2.buffer(), - +1.0f, -1.0f, +1.0f, 0.buffer(), - +1.0f, +1.0f, +1.0f, 2.buffer(), - +1.0f, -1.0f, -1.0f, 3.buffer(), - +1.0f, +1.0f, -1.0f, 1.buffer(), + +1.0f, -1.0f, -1.0f, 3.buffer(), + +1.0f, +1.0f, +1.0f, 2.buffer(), + +1.0f, -1.0f, +1.0f, 0.buffer(), + +1.0f, +1.0f, +1.0f, 2.buffer(), + +1.0f, -1.0f, -1.0f, 3.buffer(), + +1.0f, +1.0f, -1.0f, 1.buffer(), -1.0f, -1.0f, +1.0f, 0.buffer(), +1.0f, +1.0f, +1.0f, 1.buffer(), diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/world/mesh/SingleWorldMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/world/mesh/SingleWorldMesh.kt index 4ac0ce087..4cfabee11 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/world/mesh/SingleWorldMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/world/mesh/SingleWorldMesh.kt @@ -30,7 +30,7 @@ class SingleWorldMesh(renderWindow: RenderWindow, initialCacheSize: Int, onDeman data.add(position[0]) data.add(position[1]) data.add(position[2]) - data.addAll(transformedUV) + data.add(transformedUV) data.add(texture.renderData.shaderTextureId.buffer()) data.add((tintColor or (light shl 24)).buffer()) } @@ -41,7 +41,7 @@ class SingleWorldMesh(renderWindow: RenderWindow, initialCacheSize: Int, onDeman data.add(x) data.add(y) data.add(z) - data.addAll(transformedUV) + data.add(transformedUV) data.add(shaderTextureId) data.add(tintLight) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/world/mesh/WorldMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/world/mesh/WorldMesh.kt index 15dfe8c83..eddc0917b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/world/mesh/WorldMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/world/mesh/WorldMesh.kt @@ -19,6 +19,7 @@ import de.bixilon.kotlinglm.vec3.Vec3i import de.bixilon.minosoft.gui.rendering.RenderWindow import de.bixilon.minosoft.gui.rendering.util.VecUtil.of import de.bixilon.minosoft.gui.rendering.world.entities.BlockEntityRenderer +import de.bixilon.minosoft.util.collections.floats.DirectArrayFloatList class WorldMesh( renderWindow: RenderWindow, @@ -30,7 +31,7 @@ class WorldMesh( var opaqueMesh: SingleWorldMesh? = SingleWorldMesh(renderWindow, if (smallMesh) 1000 else 100000) var translucentMesh: SingleWorldMesh? = SingleWorldMesh(renderWindow, if (smallMesh) 1000 else 10000) var transparentMesh: SingleWorldMesh? = SingleWorldMesh(renderWindow, if (smallMesh) 1000 else 20000) - var textMesh: SingleWorldMesh? = SingleWorldMesh(renderWindow, if (smallMesh) 1000 else 200000, onDemand = true) + var textMesh: SingleWorldMesh? = SingleWorldMesh(renderWindow, if (smallMesh) 1000 else 50000, onDemand = true) var blockEntities: Set>? = null // used for frustum culling @@ -59,8 +60,11 @@ class WorldMesh( if (mesh == null) { return false } - if (mesh.data.isEmpty) { - mesh.data.unload() + val data = mesh.data + if (data.isEmpty) { + if (data is DirectArrayFloatList) { + data.unload() + } return true } meshes++ diff --git a/src/main/java/de/bixilon/minosoft/util/collections/DirectList.kt b/src/main/java/de/bixilon/minosoft/util/collections/DirectList.kt new file mode 100644 index 000000000..6f42fd933 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/util/collections/DirectList.kt @@ -0,0 +1,16 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.util.collections + +interface DirectList diff --git a/src/main/java/de/bixilon/minosoft/util/collections/floats/AbstractFloatList.kt b/src/main/java/de/bixilon/minosoft/util/collections/floats/AbstractFloatList.kt index 8971d557a..d0746d66c 100644 --- a/src/main/java/de/bixilon/minosoft/util/collections/floats/AbstractFloatList.kt +++ b/src/main/java/de/bixilon/minosoft/util/collections/floats/AbstractFloatList.kt @@ -14,11 +14,16 @@ package de.bixilon.minosoft.util.collections.floats import de.bixilon.minosoft.util.collections.AbstractPrimitiveList +import java.nio.FloatBuffer abstract class AbstractFloatList : AbstractPrimitiveList() { - abstract fun addAll(floats: FloatArray) - abstract fun addAll(floatList: AbstractFloatList) + abstract fun add(array: FloatArray) + operator fun plusAssign(array: FloatArray) = add(array) + abstract fun add(floatList: AbstractFloatList) + operator fun plusAssign(floatList: AbstractFloatList) = add(floatList) + abstract fun add(buffer: FloatBuffer) + operator fun plusAssign(buffer: FloatBuffer) = add(buffer) abstract fun toArray(): FloatArray } diff --git a/src/main/java/de/bixilon/minosoft/util/collections/floats/BufferedArrayFloatList.kt b/src/main/java/de/bixilon/minosoft/util/collections/floats/BufferedArrayFloatList.kt new file mode 100644 index 000000000..8d3402c1f --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/util/collections/floats/BufferedArrayFloatList.kt @@ -0,0 +1,152 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.util.collections.floats + +import de.bixilon.minosoft.util.collections.floats.FloatListUtil.copy +import de.bixilon.minosoft.util.collections.floats.FloatListUtil.finish +import org.lwjgl.system.MemoryUtil.memAllocFloat +import org.lwjgl.system.MemoryUtil.memFree +import java.nio.FloatBuffer + +class BufferedArrayFloatList( + initialSize: Int = FloatListUtil.DEFAULT_INITIAL_SIZE, +) : AbstractFloatList(), DirectArrayFloatList { + var buffer: FloatBuffer = memAllocFloat(initialSize) + private set + override var limit: Int = buffer.limit() + private set + override val size: Int + get() = buffer.position() + override val isEmpty: Boolean + get() = size == 0 + private var unloaded = false + + private val nextGrowStep = when { + initialSize <= 0 -> FloatListUtil.DEFAULT_INITIAL_SIZE + initialSize <= 100 -> 100 + else -> initialSize + } + + private var output: FloatArray = FloatArray(0) + private var outputUpToDate = false + + override fun ensureSize(needed: Int) { + checkFinished() + if (limit - size >= needed) { + return + } + var newSize = limit + newSize += if (nextGrowStep < needed) { + (needed / nextGrowStep + 1) * nextGrowStep + } else { + nextGrowStep + } + grow(newSize) + } + + private fun grow(size: Int) { + val buffer = buffer + this.buffer = memAllocFloat(size) + limit = size + buffer.copy(this.buffer) + memFree(buffer) + } + + override fun add(value: Float) { + ensureSize(1) + buffer.put(value) + invalidateOutput() + } + + override fun add(array: FloatArray) { + ensureSize(array.size) + buffer.put(array) + invalidateOutput() + } + + override fun add(buffer: FloatBuffer) { + ensureSize(buffer.position()) + buffer.copy(this.buffer) + } + + override fun add(floatList: AbstractFloatList) { + ensureSize(floatList.size) + when (floatList) { + is FragmentedArrayFloatList -> { + for (buffer in floatList.fragments) { + buffer.copy(this.buffer) + } + } + + is DirectArrayFloatList -> floatList.toBuffer().copy(this.buffer) + else -> add(floatList.toArray()) + } + invalidateOutput() + } + + private fun checkOutputArray() { + if (outputUpToDate) { + return + } + val position = buffer.position() + output = FloatArray(position) + buffer.position(0) + buffer.get(output, 0, position) + buffer.position(position) + outputUpToDate = true + } + + override fun toArray(): FloatArray { + checkOutputArray() + return output + } + + override fun unload() { + check(!unloaded) { "Already unloaded!" } + unloaded = true + finished = true // Is unloaded + memFree(buffer) + } + + override fun clear() { + buffer.clear() + if (output.isNotEmpty()) { + output = FloatArray(0) + } + invalidateOutput() + } + + override fun finish() { + finished = true + limit = buffer.limit() + this.buffer = buffer.finish() + } + + protected fun finalize() { + if (unloaded) { + return + } + memFree(buffer) + } + + override fun toBuffer(): FloatBuffer { + return this.buffer + } + + private fun invalidateOutput() { + if (outputUpToDate) { + outputUpToDate = false + } + } +} diff --git a/src/main/java/de/bixilon/minosoft/util/collections/floats/DirectArrayFloatList.kt b/src/main/java/de/bixilon/minosoft/util/collections/floats/DirectArrayFloatList.kt index 0ec97b795..7da24ead7 100644 --- a/src/main/java/de/bixilon/minosoft/util/collections/floats/DirectArrayFloatList.kt +++ b/src/main/java/de/bixilon/minosoft/util/collections/floats/DirectArrayFloatList.kt @@ -13,159 +13,11 @@ package de.bixilon.minosoft.util.collections.floats -import de.bixilon.kutil.exception.ExceptionUtil.catchAll -import org.lwjgl.system.MemoryUtil.memAllocFloat -import org.lwjgl.system.MemoryUtil.memFree -import java.nio.BufferOverflowException +import de.bixilon.minosoft.util.collections.DirectList import java.nio.FloatBuffer -class DirectArrayFloatList( - initialSize: Int = DEFAULT_INITIAL_SIZE, -) : AbstractFloatList() { - var buffer: FloatBuffer = memAllocFloat(initialSize) - private set - override var limit: Int = buffer.limit() - private set - override val size: Int - get() = buffer.position() - override val isEmpty: Boolean - get() = size == 0 - private var unloaded = false +interface DirectArrayFloatList : DirectList { - private val nextGrowStep = when { - initialSize <= 0 -> DEFAULT_INITIAL_SIZE - initialSize <= 100 -> 100 - else -> initialSize - } - - private var output: FloatArray = FloatArray(0) - private var outputUpToDate = false - - override fun ensureSize(needed: Int) { - checkFinished() - if (limit - size >= needed) { - return - } - var newSize = limit - newSize += if (nextGrowStep < needed) { - (needed / nextGrowStep + 1) * nextGrowStep - } else { - nextGrowStep - } - grow(newSize) - } - - private fun grow(size: Int) { - val oldBuffer = buffer - buffer = memAllocFloat(size) - limit = size - if (FLOAT_PUT_METHOD == null) { // Java < 16 - for (i in 0 until oldBuffer.position()) { - buffer.put(oldBuffer.get(i)) - } - } else { - FLOAT_PUT_METHOD.invoke(buffer, 0, oldBuffer, 0, oldBuffer.position()) - buffer.position(oldBuffer.position()) - } - memFree(oldBuffer) - } - - override fun add(value: Float) { - ensureSize(1) - buffer.put(value) - if (outputUpToDate) { - outputUpToDate = false - } - } - - override fun addAll(floats: FloatArray) { - ensureSize(floats.size) - try { - buffer.put(floats) - } catch (exception: BufferOverflowException) { - ensureSize(floats.size) - - exception.printStackTrace() - } - if (outputUpToDate) { - outputUpToDate = false - } - } - - override fun addAll(floatList: AbstractFloatList) { - if (floatList is DirectArrayFloatList) { - ensureSize(floatList.size) - if (FLOAT_PUT_METHOD == null) { // Java < 16 - for (i in 0 until floatList.buffer.position()) { - buffer.put(floatList.buffer.get(i)) - } - } else { - FLOAT_PUT_METHOD.invoke(buffer, buffer.position(), floatList.buffer, 0, floatList.buffer.position()) - buffer.position(buffer.position() + floatList.buffer.position()) - } - } else { - addAll(floatList.toArray()) - } - } - - private fun checkOutputArray() { - if (outputUpToDate) { - return - } - val position = buffer.position() - output = FloatArray(position) - buffer.position(0) - buffer.get(output, 0, position) - buffer.position(position) - outputUpToDate = true - } - - override fun toArray(): FloatArray { - checkOutputArray() - return output - } - - fun unload() { - check(!unloaded) { "Already unloaded!" } - unloaded = true - finished = true // Is unloaded - memFree(buffer) - } - - override fun clear() { - buffer.clear() - if (output.isNotEmpty()) { - output = FloatArray(0) - } - outputUpToDate = false - } - - override fun finish() { - finished = true - val oldBuffer = buffer - buffer = memAllocFloat(oldBuffer.position()) - limit = buffer.limit() - if (FLOAT_PUT_METHOD == null) { // Java < 16 - for (i in 0 until oldBuffer.position()) { - buffer.put(oldBuffer.get(i)) - } - } else { - FLOAT_PUT_METHOD.invoke(buffer, 0, oldBuffer, 0, oldBuffer.position()) - buffer.position(buffer.limit()) - } - memFree(oldBuffer) - } - - protected fun finalize() { - if (unloaded) { - return - } - memFree(buffer) - } - - - private companion object { - private val FLOAT_PUT_METHOD = catchAll { FloatBuffer::class.java.getMethod("put", Int::class.java, FloatBuffer::class.java, Int::class.java, Int::class.java) } - private const val DEFAULT_INITIAL_SIZE = 1000 - } + fun toBuffer(): FloatBuffer + fun unload() } diff --git a/src/main/java/de/bixilon/minosoft/util/collections/floats/FloatListUtil.kt b/src/main/java/de/bixilon/minosoft/util/collections/floats/FloatListUtil.kt new file mode 100644 index 000000000..f23422475 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/util/collections/floats/FloatListUtil.kt @@ -0,0 +1,62 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.util.collections.floats + +import de.bixilon.kutil.exception.ExceptionUtil +import org.lwjgl.system.MemoryUtil +import org.lwjgl.system.MemoryUtil.memAllocFloat +import java.nio.FloatBuffer + +object FloatListUtil { + const val PREFER_FRAGMENTED = true + + val FLOAT_PUT_METHOD = ExceptionUtil.catchAll { FloatBuffer::class.java.getMethod("put", Int::class.java, FloatBuffer::class.java, Int::class.java, Int::class.java) } + const val DEFAULT_INITIAL_SIZE = 1000 + + fun direct(initialSize: Int = DEFAULT_INITIAL_SIZE): AbstractFloatList { + return if (PREFER_FRAGMENTED) FragmentedArrayFloatList(initialSize) else BufferedArrayFloatList(initialSize) + } + + fun FloatBuffer.finish(): FloatBuffer { + val buffer = memAllocFloat(position()) + if (FLOAT_PUT_METHOD == null) { // Java < 16 + for (i in 0 until position()) { + buffer.put(get(i)) + } + } else { + FLOAT_PUT_METHOD.invoke(buffer, 0, this, 0, position()) + buffer.position(buffer.limit()) + } + MemoryUtil.memFree(this) + return buffer + } + + fun FloatBuffer.copy(sourceOffset: Int, destination: FloatBuffer, destinationOffset: Int, length: Int) { + if (length == 0) { + return + } + if (FLOAT_PUT_METHOD == null) { // Java < 16 + for (i in 0 until length) { + destination.put(destinationOffset + i, this.get(sourceOffset + i)) + } + return + } + FLOAT_PUT_METHOD.invoke(destination, destinationOffset, this, sourceOffset, length) + destination.position(destination.position() + length) + } + + fun FloatBuffer.copy(destination: FloatBuffer) { + copy(0, destination, destination.position(), position()) + } +} diff --git a/src/main/java/de/bixilon/minosoft/util/collections/floats/FragmentedArrayFloatList.kt b/src/main/java/de/bixilon/minosoft/util/collections/floats/FragmentedArrayFloatList.kt new file mode 100644 index 000000000..554a8c08f --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/util/collections/floats/FragmentedArrayFloatList.kt @@ -0,0 +1,231 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.util.collections.floats + +import de.bixilon.minosoft.util.collections.floats.FloatListUtil.copy +import org.lwjgl.system.MemoryUtil.memAllocFloat +import org.lwjgl.system.MemoryUtil.memFree +import java.nio.FloatBuffer + +class FragmentedArrayFloatList( + initialSize: Int = FloatListUtil.DEFAULT_INITIAL_SIZE, +) : AbstractFloatList(), DirectArrayFloatList { + var fragments: MutableList = mutableListOf() + override var limit: Int = 0 + private set + override var size: Int = 0 + private set + override val isEmpty: Boolean + get() = size == 0 + private var unloaded = false + + private val nextGrowStep = when { + initialSize <= 0 -> FloatListUtil.DEFAULT_INITIAL_SIZE + initialSize <= 100 -> 100 + else -> initialSize + } + + private var output: FloatArray? = null + private var buffer: FloatBuffer? = null + + + override fun ensureSize(needed: Int) { + grow(needed) + } + + private fun grow(size: Int): FloatBuffer { + checkFinished() + if (limit - this.size >= size) { + return this.fragments.last() + } + val grow = if (nextGrowStep < size) { + (size / nextGrowStep + 1) * nextGrowStep + } else { + nextGrowStep + } + return forceGrow(grow) + } + + private fun forceGrow(size: Int): FloatBuffer { + val last = fragments.lastOrNull() + val buffer = memAllocFloat(size) + fragments += buffer + limit += size + return buffer + } + + override fun add(value: Float) { + val buffer = grow(1) + if (buffer.position() >= buffer.limit()) { + println() + } + buffer.put(value) + size += 1 + invalidateOutput() + } + + override fun add(array: FloatArray) { + if (array.isEmpty()) return + invalidateOutput() + + var offset = 0 + size += array.size + if (fragments.isNotEmpty()) { + val fragment = fragments.last() + val remaining = fragment.limit() - fragment.position() + val copy = minOf(array.size, remaining) + fragment.put(array, 0, copy) + offset += copy + + if (array.size <= remaining) { + // everything copied + return + } + } + val length = array.size - offset + val next = grow(length) + next.put(array, offset, length) + next.position(length) + } + + override fun add(buffer: FloatBuffer) { + if (buffer.position() == 0) return + invalidateOutput() + + var offset = 0 + val position = buffer.position() + size += position + if (fragments.isNotEmpty()) { + val fragment = fragments.last() + val remaining = fragment.limit() - fragment.position() + val copy = minOf(position, remaining) + buffer.copy(0, fragment, fragment.position(), copy) + offset += copy + + if (position <= remaining) { + // everything copied + return + } + } + val length = position - offset + val next = grow(length) + buffer.copy(offset, next, 0, length) + next.position(length) + } + + override fun add(floatList: AbstractFloatList) { + when (floatList) { + is FragmentedArrayFloatList -> { + // TODO: add dirty method (just adding their fragments to our list of fragments) + for (buffer in floatList.fragments) { + add(buffer) + } + } + + is DirectArrayFloatList -> add(floatList.toBuffer()) + else -> add(floatList.toArray()) + } + invalidateOutput() + } + + private fun checkOutputArray(): FloatArray { + this.output?.let { return it } + val output = FloatArray(size) + var offset = 0 + for (buffer in fragments) { + val position = buffer.position() + buffer.position(0) + buffer.get(output, offset, position) + offset += position + buffer.position(position) + } + this.output = output + return output + } + + override fun toArray(): FloatArray { + return checkOutputArray() + } + + override fun unload() { + check(!unloaded) { "Already unloaded!" } + unloaded = true + finished = true // Is unloaded + for (buffer in fragments) { + memFree(buffer) + } + this.output = null + val buffer = this.buffer + if (buffer != null) { + memFree(buffer) + this.buffer = null + } + fragments.clear() + } + + override fun clear() { + size = 0 + for (buffer in fragments) { + buffer.clear() + } + if (output != null) { + output = null + } + invalidateOutput() + } + + override fun finish() { + finished = true + if (fragments.isEmpty()) { + return + } + val last = fragments.removeLast() + val next = memAllocFloat(last.position()) + last.copy(next) + fragments += next + } + + protected fun finalize() { + if (unloaded) { + return + } + for (buffer in fragments) { + memFree(buffer) + } + fragments.clear() + } + + private fun invalidateOutput() { + if (this.output != null) { + this.output = null + } + + if (this.buffer != null) { + this.buffer = null + } + } + + override fun toBuffer(): FloatBuffer { + this.buffer?.let { return it } + if (fragments.size == 1) { + return fragments.first() + } + val buffer = memAllocFloat(size) + for (fragment in fragments) { + fragment.copy(buffer) + } + this.buffer = buffer + return buffer + } +} diff --git a/src/main/java/de/bixilon/minosoft/util/collections/floats/HeapArrayFloatList.kt b/src/main/java/de/bixilon/minosoft/util/collections/floats/HeapArrayFloatList.kt index 818d76c35..cebb78591 100644 --- a/src/main/java/de/bixilon/minosoft/util/collections/floats/HeapArrayFloatList.kt +++ b/src/main/java/de/bixilon/minosoft/util/collections/floats/HeapArrayFloatList.kt @@ -12,8 +12,10 @@ */ package de.bixilon.minosoft.util.collections.floats +import java.nio.FloatBuffer + class HeapArrayFloatList( - initialSize: Int = DEFAULT_INITIAL_SIZE, + initialSize: Int = FloatListUtil.DEFAULT_INITIAL_SIZE, ) : AbstractFloatList() { private var data: FloatArray = FloatArray(initialSize) override val limit: Int @@ -23,7 +25,7 @@ class HeapArrayFloatList( get() = size == 0 private val nextGrowStep = when { - initialSize <= 0 -> DEFAULT_INITIAL_SIZE + initialSize <= 0 -> FloatListUtil.DEFAULT_INITIAL_SIZE initialSize <= 50 -> 50 else -> initialSize } @@ -40,7 +42,7 @@ class HeapArrayFloatList( override fun clear() { checkFinalized() size = 0 - outputUpToDate = false + invalidateOutput() output = FloatArray(0) } @@ -65,21 +67,27 @@ class HeapArrayFloatList( override fun add(value: Float) { ensureSize(1) data[size++] = value - if (outputUpToDate) { - outputUpToDate = false - } + invalidateOutput() } - override fun addAll(floats: FloatArray) { - ensureSize(floats.size) - System.arraycopy(floats, 0, data, size, floats.size) - size += floats.size - if (outputUpToDate) { - outputUpToDate = false - } + override fun add(array: FloatArray) { + ensureSize(array.size) + System.arraycopy(array, 0, data, size, array.size) + size += array.size + invalidateOutput() } - override fun addAll(floatList: AbstractFloatList) { + override fun add(buffer: FloatBuffer) { + val position = buffer.position() + ensureSize(position) + for (i in 0 until position) { + data[size + i] = buffer.get(i) + } + size += position + invalidateOutput() + } + + override fun add(floatList: AbstractFloatList) { ensureSize(floatList.size) val source: FloatArray = if (floatList is HeapArrayFloatList) { if (floatList.finished) { @@ -92,9 +100,7 @@ class HeapArrayFloatList( } System.arraycopy(source, 0, data, size, floatList.size) size += floatList.size - if (outputUpToDate) { - outputUpToDate = false - } + invalidateOutput() } private fun checkOutputArray() { @@ -117,8 +123,9 @@ class HeapArrayFloatList( data = FloatArray(0) } - - private companion object { - private const val DEFAULT_INITIAL_SIZE = 1000 + private fun invalidateOutput() { + if (outputUpToDate) { + outputUpToDate = false + } } } diff --git a/src/test/java/de/bixilon/minosoft/util/collections/floats/AbstractFloatListTest.kt b/src/test/java/de/bixilon/minosoft/util/collections/floats/AbstractFloatListTest.kt new file mode 100644 index 000000000..a1bde06ff --- /dev/null +++ b/src/test/java/de/bixilon/minosoft/util/collections/floats/AbstractFloatListTest.kt @@ -0,0 +1,449 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.util.collections.floats + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import java.nio.FloatBuffer +import kotlin.test.assertContentEquals + +abstract class AbstractFloatListTest { + + abstract fun create(initialSize: Int = 1): AbstractFloatList + + @Test + fun initialListSize() { + val list = create() + assertEquals(0, list.size) + } + + @Test + fun addSingleFloat() { + val list = create() + list.add(189.0f) + assertEquals(1, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f), array) + } + + @Test + fun addMultipleFloats() { + val list = create() + list.add(189.0f) + list.add(289.0f) + list.add(389.0f) + assertEquals(3, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f, 289.0f, 389.0f), array) + } + + @Test + fun addVastFloats() { + val list = create() + for (i in 0 until 1000) { + list.add(i.toFloat() * 2.0f) + } + assertEquals(1000, list.size) + val array = list.toArray() + for (i in 0 until 1000) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + @Test + fun addSingleArray() { + val list = create() + list.add(floatArrayOf(189.0f)) + assertEquals(1, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f), array) + } + + @Test + fun addMultipleArrays() { + val list = create() + list.add(floatArrayOf(189.0f)) + list.add(floatArrayOf(289.0f)) + list.add(floatArrayOf(389.0f)) + assertEquals(3, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f, 289.0f, 389.0f), array) + } + + @Test + fun addMultipleBigArrays() { + val list = create() + list.add(floatArrayOf(189.0f)) + list.add(floatArrayOf(289.0f, 389.0f)) + list.add(floatArrayOf(489.0f, 589.0f, 689.0f)) + assertEquals(6, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f, 289.0f, 389.0f, 489.0f, 589.0f, 689.0f), array) + } + + @Test + fun addVastArrays() { + val list = create() + + for (i in 0 until 1000) { + list.add(floatArrayOf(i.toFloat() * 2.0f)) + } + assertEquals(1000, list.size) + val array = list.toArray() + for (i in 0 until 1000) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + @Test + fun addSingleBuffer() { + val list = create() + list.add(wrap(189.0f)) + assertEquals(1, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f), array) + } + + @Test + fun addMultipleBuffers() { + val list = create() + list.add(wrap(189.0f)) + list.add(wrap(289.0f)) + list.add(wrap(389.0f)) + assertEquals(3, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f, 289.0f, 389.0f), array) + } + + @Test + fun addMultipleBigBuffers() { + val list = create() + list.add(wrap(189.0f)) + list.add(wrap(289.0f, 389.0f)) + list.add(wrap(489.0f, 589.0f, 689.0f)) + assertEquals(6, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f, 289.0f, 389.0f, 489.0f, 589.0f, 689.0f), array) + } + + @Test + fun addVastBuffers() { + val list = create() + + for (i in 0 until 1000) { + list.add(wrap(i.toFloat() * 2.0f)) + } + assertEquals(1000, list.size) + val array = list.toArray() + for (i in 0 until 1000) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + + @Test + fun addSingleHeapList() { + val list = create() + list.add(HeapArrayFloatList(1).apply { add(189.0f) }) + assertEquals(1, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f), array) + } + + @Test + fun addMultipleHeapLists() { + val list = create() + list.add(HeapArrayFloatList(1).apply { add(189.0f) }) + list.add(HeapArrayFloatList(1).apply { add(289.0f) }) + list.add(HeapArrayFloatList(1).apply { add(389.0f) }) + assertEquals(3, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f, 289.0f, 389.0f), array) + } + + @Test + fun addMultipleBigHeapLists() { + val list = create() + list.add(HeapArrayFloatList(1).apply { add(189.0f) }) + list.add(HeapArrayFloatList(1).apply { add(289.0f); add(389.0f) }) + list.add(HeapArrayFloatList(1).apply { add(489.0f); add(589.0f); add(689.0f) }) + + assertEquals(6, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f, 289.0f, 389.0f, 489.0f, 589.0f, 689.0f), array) + } + + @Test + fun addVastHeapLists() { + val list = create() + + for (i in 0 until 1000) { + list.add(HeapArrayFloatList(1).apply { add(i.toFloat() * 2.0f) }) + } + assertEquals(1000, list.size) + val array = list.toArray() + for (i in 0 until 1000) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + + @Test + fun addSingleBufferedList() { + val list = create() + list.add(BufferedArrayFloatList(1).apply { add(189.0f) }) + assertEquals(1, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f), array) + } + + @Test + fun addMultipleBufferedLists() { + val list = create() + list.add(BufferedArrayFloatList(1).apply { add(189.0f) }) + list.add(BufferedArrayFloatList(1).apply { add(289.0f) }) + list.add(BufferedArrayFloatList(1).apply { add(389.0f) }) + assertEquals(3, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f, 289.0f, 389.0f), array) + } + + @Test + fun addMultipleBigBufferedLists() { + val list = create() + list.add(BufferedArrayFloatList(1).apply { add(189.0f) }) + list.add(BufferedArrayFloatList(1).apply { add(289.0f); add(389.0f) }) + list.add(BufferedArrayFloatList(1).apply { add(489.0f); add(589.0f); add(689.0f) }) + + assertEquals(6, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f, 289.0f, 389.0f, 489.0f, 589.0f, 689.0f), array) + } + + @Test + fun addVastBufferedLists() { + val list = create() + + for (i in 0 until 1000) { + list.add(BufferedArrayFloatList(1).apply { add(i.toFloat() * 2.0f) }) + } + assertEquals(1000, list.size) + val array = list.toArray() + for (i in 0 until 1000) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + @Test + fun addSingleFragmentedList() { + val list = create() + list.add(FragmentedArrayFloatList(1).apply { add(189.0f) }) + assertEquals(1, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f), array) + } + + @Test + fun addMultipleFragmentedLists() { + val list = create() + list.add(FragmentedArrayFloatList(1).apply { add(189.0f) }) + list.add(FragmentedArrayFloatList(1).apply { add(289.0f) }) + list.add(FragmentedArrayFloatList(1).apply { add(389.0f) }) + assertEquals(3, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f, 289.0f, 389.0f), array) + } + + @Test + fun addMultipleBigFragmentedLists() { + val list = create() + list.add(FragmentedArrayFloatList(1).apply { add(189.0f) }) + list.add(FragmentedArrayFloatList(1).apply { add(289.0f); add(389.0f) }) + list.add(FragmentedArrayFloatList(1).apply { add(489.0f); add(589.0f); add(689.0f) }) + + assertEquals(6, list.size) + val array = list.toArray() + assertContentEquals(floatArrayOf(189.0f, 289.0f, 389.0f, 489.0f, 589.0f, 689.0f), array) + } + + @Test + fun addVastFragmentedLists() { + val list = create() + + for (i in 0 until 1000) { + list.add(FragmentedArrayFloatList(1).apply { add(i.toFloat() * 2.0f) }) + } + assertEquals(1000, list.size) + val array = list.toArray() + for (i in 0 until 1000) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + @Test + fun fragmentedArray() { + val list = create(120) + list.add(FloatArray(110) { it.toFloat() * 2 }) + list.add(FloatArray(50) { 220 + it.toFloat() * 2 }) + list.add(320.0f) + + val array = list.toArray() + for (i in 0 until 161) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + @Test + fun fragmentedBuffer() { + val list = create(120) + list.add(wrap(*FloatArray(110) { it.toFloat() * 2 })) + list.add(wrap(*FloatArray(50) { 220 + it.toFloat() * 2 })) + list.add(320.0f) + + val array = list.toArray() + for (i in 0 until 161) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + @Test + fun fragmentedHeapList() { + val list = create(120) + list.add(HeapArrayFloatList().apply { add(FloatArray(110) { it.toFloat() * 2 }) }) + list.add(HeapArrayFloatList().apply { add(FloatArray(50) { 220 + it.toFloat() * 2 }) }) + list.add(320.0f) + + val array = list.toArray() + for (i in 0 until 161) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + @Test + fun fragmentedBufferedList() { + val list = create(120) + list.add(BufferedArrayFloatList().apply { add(FloatArray(110) { it.toFloat() * 2 }) }) + list.add(BufferedArrayFloatList().apply { add(FloatArray(50) { 220 + it.toFloat() * 2 }) }) + list.add(320.0f) + + val array = list.toArray() + for (i in 0 until 161) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + @Test + fun fragmentedFragmentedList() { + val list = create(120) + list.add(FragmentedArrayFloatList().apply { add(FloatArray(110) { it.toFloat() * 2 }) }) + list.add(FragmentedArrayFloatList().apply { add(FloatArray(50) { 220 + it.toFloat() * 2 }) }) + list.add(320.0f) + + val array = list.toArray() + for (i in 0 until 161) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + @Test + fun fragmentedFragmentedList2() { + val list = create(120) + list.add(FragmentedArrayFloatList(10).apply { add(FloatArray(110) { it.toFloat() * 2 }) }) + list.add(FragmentedArrayFloatList(14).apply { add(FloatArray(50) { 220 + it.toFloat() * 2 }) }) + list.add(320.0f) + + val array = list.toArray() + for (i in 0 until 161) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + + @Test + fun ensureSizeArray() { + val list = create(120) + list.add(0.0f) + list.ensureSize(1000) + list.ensureSize(2000) + list.add(FloatArray(1000) { 2.0f + it.toFloat() * 2 }) + + val array = list.toArray() + for (i in 0 until 1001) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + @Test + fun ensureSizeBuffer() { + val list = create(120) + list.add(0.0f) + list.ensureSize(1000) + list.ensureSize(2000) + list.add(wrap(*FloatArray(1000) { 2.0f + it.toFloat() * 2 })) + + val array = list.toArray() + for (i in 0 until 1001) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + @Test + fun ensureSizeHeapList() { + val list = create(120) + list.add(0.0f) + list.ensureSize(1000) + list.ensureSize(2000) + list.add(HeapArrayFloatList().apply { add(FloatArray(1000) { 2.0f + it.toFloat() * 2 }) }) + + val array = list.toArray() + for (i in 0 until 1001) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + @Test + fun ensureSizeBufferedList() { + val list = create(120) + list.add(0.0f) + list.ensureSize(1000) + list.ensureSize(2000) + list.add(BufferedArrayFloatList().apply { add(FloatArray(1000) { 2.0f + it.toFloat() * 2 }) }) + + val array = list.toArray() + for (i in 0 until 1001) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + @Test + fun ensureSizeFragmentedList() { + val list = create(120) + list.add(0.0f) + list.ensureSize(1000) + list.ensureSize(2000) + list.add(FragmentedArrayFloatList().apply { add(FloatArray(1000) { 2.0f + it.toFloat() * 2 }) }) + + val array = list.toArray() + for (i in 0 until 1001) { + assertEquals(array[i], i.toFloat() * 2.0f) + } + } + + private fun wrap(vararg array: Float): FloatBuffer { + val buffer = FloatBuffer.wrap(array) + buffer.position(array.size) + + return buffer + } +} diff --git a/src/test/java/de/bixilon/minosoft/util/collections/floats/BufferedFloatListTest.kt b/src/test/java/de/bixilon/minosoft/util/collections/floats/BufferedFloatListTest.kt new file mode 100644 index 000000000..0635cf107 --- /dev/null +++ b/src/test/java/de/bixilon/minosoft/util/collections/floats/BufferedFloatListTest.kt @@ -0,0 +1,21 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.util.collections.floats + +class BufferedFloatListTest : DirectFloatListTest() { + + override fun create(initialSize: Int): AbstractFloatList { + return BufferedArrayFloatList(initialSize) + } +} diff --git a/src/test/java/de/bixilon/minosoft/util/collections/floats/DirectFloatListTest.kt b/src/test/java/de/bixilon/minosoft/util/collections/floats/DirectFloatListTest.kt new file mode 100644 index 000000000..58e721d82 --- /dev/null +++ b/src/test/java/de/bixilon/minosoft/util/collections/floats/DirectFloatListTest.kt @@ -0,0 +1,45 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.util.collections.floats + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +abstract class DirectFloatListTest : AbstractFloatListTest() { + @Test + fun singleToBuffer() { + val list = create() + if (list !is DirectArrayFloatList) { + return + } + list.add(189.0f) + val buffer = list.toBuffer() + Assertions.assertEquals(buffer.get(0), 189.0f) + Assertions.assertEquals(buffer.position(), 1) + } + + @Test + fun multipleToBuffer() { + val list = create() + if (list !is DirectArrayFloatList) { + return + } + list.add(189.0f) + list.add(289.0f) + val buffer = list.toBuffer() + Assertions.assertEquals(buffer.get(0), 189.0f) + Assertions.assertEquals(buffer.get(1), 289.0f) + Assertions.assertEquals(buffer.position(), 2) + } +} diff --git a/src/test/java/de/bixilon/minosoft/util/collections/floats/FragmentedFloatListTest.kt b/src/test/java/de/bixilon/minosoft/util/collections/floats/FragmentedFloatListTest.kt new file mode 100644 index 000000000..f954fc4f9 --- /dev/null +++ b/src/test/java/de/bixilon/minosoft/util/collections/floats/FragmentedFloatListTest.kt @@ -0,0 +1,47 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.util.collections.floats + +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class FragmentedFloatListTest : DirectFloatListTest() { + + override fun create(initialSize: Int): AbstractFloatList { + return FragmentedArrayFloatList(initialSize) + } + + + private fun AbstractFloatList.putMixed() { + ensureSize(7) + add(1.0f) + add(2.0f) + add(floatArrayOf(3.0f, 4.0f)) + add(5.0f) + add(6.0f) + add(floatArrayOf(7.0f)) + } + + @Test + fun testMixed() { + val list = FragmentedArrayFloatList(1000) + for (i in 0 until 2000) { + println(i) + list.putMixed() + } + assertEquals(14000, list.size) + assertEquals(14000, list.toArray().size) + assertEquals(14, list.fragments.size) + } +} diff --git a/src/test/java/de/bixilon/minosoft/util/collections/floats/HeapFloatListTest.kt b/src/test/java/de/bixilon/minosoft/util/collections/floats/HeapFloatListTest.kt new file mode 100644 index 000000000..596ccdc28 --- /dev/null +++ b/src/test/java/de/bixilon/minosoft/util/collections/floats/HeapFloatListTest.kt @@ -0,0 +1,21 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.util.collections.floats + +class HeapFloatListTest : AbstractFloatListTest() { + + override fun create(initialSize: Int): AbstractFloatList { + return HeapArrayFloatList(initialSize) + } +}