From d0ea37e93a0b365346c9350212482767aa7df498 Mon Sep 17 00:00:00 2001 From: Moritz Zwerger Date: Sun, 22 Oct 2023 22:36:08 +0200 Subject: [PATCH] optimize Mat4 math, skeletal: reduce memory allocations --- .../buffer/uniform/DummyFloatUniformBuffer.kt | 4 +- .../buffer/uniform/DummyIntUniformBuffer.kt | 4 +- .../renderer/storage/chest/ChestAnimation.kt | 2 +- .../storage/shulker/ShulkerAnimation.kt | 2 +- .../gui/rendering/skeletal/SkeletalManager.kt | 5 +- .../skeletal/instance/TransformInstance.kt | 1 + .../keyframes/types/RotateKeyframe.kt | 2 +- .../base/buffer/uniform/UniformBuffer.kt | 4 +- .../uniform/FloatOpenGLUniformBuffer.kt | 10 +- .../buffer/uniform/IntOpenGLUniformBuffer.kt | 4 +- .../gui/rendering/util/mat/mat4/Mat4Util.kt | 138 ++++++++++++++++-- .../rendering/util/mat/mat4/Mat4UtilTest.kt | 47 ++++++ 12 files changed, 192 insertions(+), 31 deletions(-) create mode 100644 src/test/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4UtilTest.kt diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/buffer/uniform/DummyFloatUniformBuffer.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/buffer/uniform/DummyFloatUniformBuffer.kt index 64374093a..9019cf45c 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/buffer/uniform/DummyFloatUniformBuffer.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/buffer/uniform/DummyFloatUniformBuffer.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2022 Moritz Zwerger + * Copyright (C) 2020-2023 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. * @@ -24,7 +24,7 @@ class DummyFloatUniformBuffer( ) : FloatUniformBuffer { override val bindingIndex: Int = 0 - override fun upload(range: IntRange) { + override fun upload(start: Int, end: Int) { } override fun upload() { diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/buffer/uniform/DummyIntUniformBuffer.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/buffer/uniform/DummyIntUniformBuffer.kt index 8f5b5a254..4af8470bc 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/buffer/uniform/DummyIntUniformBuffer.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/system/dummy/buffer/uniform/DummyIntUniformBuffer.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2022 Moritz Zwerger + * Copyright (C) 2020-2023 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. * @@ -23,7 +23,7 @@ class DummyIntUniformBuffer( ) : IntUniformBuffer { override val bindingIndex: Int = 0 - override fun upload(range: IntRange) { + override fun upload(start: Int, end: Int) { } override fun upload() { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/entities/renderer/storage/chest/ChestAnimation.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/entities/renderer/storage/chest/ChestAnimation.kt index 027f894e7..28e4b70eb 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/entities/renderer/storage/chest/ChestAnimation.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/entities/renderer/storage/chest/ChestAnimation.kt @@ -36,7 +36,7 @@ class ChestAnimation( transform.value .translateAssign(transform.pivot) .rotateRadAssign(rotation) - .translateAssign(-transform.pivot) + .translateAssign(transform.nPivot) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/entities/renderer/storage/shulker/ShulkerAnimation.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/entities/renderer/storage/shulker/ShulkerAnimation.kt index 7ad9e34ef..8d74fe97b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/entities/renderer/storage/shulker/ShulkerAnimation.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/entities/renderer/storage/shulker/ShulkerAnimation.kt @@ -37,7 +37,7 @@ class ShulkerAnimation( .translateAssign(translation) .translateAssign(transform.pivot) .rotateRadAssign(rotation) - .translateAssign(-transform.pivot) + .translateAssign(transform.nPivot) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/SkeletalManager.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/SkeletalManager.kt index 45c736ea0..2aca44f7c 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/SkeletalManager.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/SkeletalManager.kt @@ -24,6 +24,7 @@ class SkeletalManager( ) { private val uniformBuffer = context.system.createFloatUniformBuffer(memAllocFloat(MAX_TRANSFORMS * Mat4.length)) val shader = context.system.createShader(minosoft("skeletal")) { SkeletalShader(it, uniformBuffer) } + private val temp = Mat4() fun init() { uniformBuffer.init() @@ -36,8 +37,8 @@ class SkeletalManager( } fun upload(instance: SkeletalInstance) { - instance.transform.pack(uniformBuffer.buffer, instance.position, Mat4()) - uniformBuffer.upload(0 until instance.model.transformCount * Mat4.length) + instance.transform.pack(uniformBuffer.buffer, instance.position, temp) + uniformBuffer.upload(0, instance.model.transformCount * Mat4.length) } fun draw(instance: SkeletalInstance, light: Int) { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/instance/TransformInstance.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/instance/TransformInstance.kt index 764505429..ce22be3b9 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/instance/TransformInstance.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/instance/TransformInstance.kt @@ -23,6 +23,7 @@ class TransformInstance( val pivot: Vec3, val children: Map, ) { + val nPivot = -pivot val value = Mat4() diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/model/animations/animators/keyframes/types/RotateKeyframe.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/model/animations/animators/keyframes/types/RotateKeyframe.kt index 2dc98f822..c0bb8d370 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/model/animations/animators/keyframes/types/RotateKeyframe.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/model/animations/animators/keyframes/types/RotateKeyframe.kt @@ -36,7 +36,7 @@ data class RotateKeyframe( override fun instance() = object : Vec3KeyframeInstance(data, loop, interpolation) { override fun apply(value: Vec3, transform: TransformInstance) { transform.value - .translateAssign(-transform.pivot) + .translateAssign(transform.nPivot) .rotateRadAssign(value) .translateAssign(transform.pivot) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/buffer/uniform/UniformBuffer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/buffer/uniform/UniformBuffer.kt index 702422402..5ebd67832 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/buffer/uniform/UniformBuffer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/buffer/uniform/UniformBuffer.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2022 Moritz Zwerger + * Copyright (C) 2020-2023 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. * @@ -19,7 +19,7 @@ import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader interface UniformBuffer : RenderableBuffer { val bindingIndex: Int - fun upload(range: IntRange) + fun upload(start: Int, end: Int) fun use(shader: NativeShader, bufferName: String) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/uniform/FloatOpenGLUniformBuffer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/uniform/FloatOpenGLUniformBuffer.kt index 58d3f0a71..16fd169d9 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/uniform/FloatOpenGLUniformBuffer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/uniform/FloatOpenGLUniformBuffer.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2022 Moritz Zwerger + * Copyright (C) 2020-2023 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. * @@ -42,13 +42,13 @@ class FloatOpenGLUniformBuffer(renderSystem: OpenGLRenderSystem, bindingIndex: I unbind() } - override fun upload(range: IntRange) { + override fun upload(start: Int, end: Int) { check(initialSize == size) { "Can not change buffer size!" } - if (range.first < 0 || range.last >= size) { - throw IndexOutOfBoundsException(range.first) + if (start < 0 || end >= size) { + throw IndexOutOfBoundsException(start) } bind() - nglBufferSubData(type.gl, range.first * 4L, Integer.toUnsignedLong(((range.last + 1) - range.first) * 4), MemoryUtil.memAddress(buffer, range.first)) + nglBufferSubData(type.gl, start * 4L, Integer.toUnsignedLong(((end + 1) - start) * 4), MemoryUtil.memAddress(buffer, start)) unbind() } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/uniform/IntOpenGLUniformBuffer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/uniform/IntOpenGLUniformBuffer.kt index b892b64be..22b364537 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/uniform/IntOpenGLUniformBuffer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/uniform/IntOpenGLUniformBuffer.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2022 Moritz Zwerger + * Copyright (C) 2020-2023 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. * @@ -37,5 +37,5 @@ class IntOpenGLUniformBuffer(renderSystem: OpenGLRenderSystem, bindingIndex: Int unbind() } - override fun upload(range: IntRange) = TODO("Unsupported") + override fun upload(start: Int, end: Int) = TODO("Unsupported") } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4Util.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4Util.kt index dbe84bb7d..ba51fa4f0 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4Util.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4Util.kt @@ -13,12 +13,10 @@ package de.bixilon.minosoft.gui.rendering.util.mat.mat4 +import de.bixilon.kotlinglm.GLM import de.bixilon.kotlinglm.func.rad import de.bixilon.kotlinglm.mat4x4.Mat4 import de.bixilon.kotlinglm.vec3.Vec3 -import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.X -import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.Y -import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.Z object Mat4Util { private val empty = Mat4() @@ -26,16 +24,16 @@ object Mat4Util { val Mat4.Companion.EMPTY_INSTANCE get() = empty fun Mat4.rotateDegreesAssign(rotation: Vec3): Mat4 { - if (rotation.x != 0.0f) rotateAssign(rotation.x.rad, Vec3.X) - if (rotation.y != 0.0f) rotateAssign(rotation.y.rad, Vec3.Y) - if (rotation.z != 0.0f) rotateAssign(rotation.z.rad, Vec3.Z) + if (rotation.x != 0.0f) rotateX(this, rotation.x.rad) + if (rotation.y != 0.0f) rotateY(this, rotation.y.rad) + if (rotation.z != 0.0f) rotateZ(this, rotation.z.rad) return this } fun Mat4.rotateRadAssign(rotation: Vec3): Mat4 { - if (rotation.x != 0.0f) rotateAssign(rotation.x, Vec3.X) - if (rotation.y != 0.0f) rotateAssign(rotation.y, Vec3.Y) - if (rotation.z != 0.0f) rotateAssign(rotation.z, Vec3.Z) + if (rotation.x != 0.0f) rotateX(this, rotation.x) + if (rotation.y != 0.0f) rotateY(this, rotation.y) + if (rotation.z != 0.0f) rotateZ(this, rotation.z) return this } @@ -53,9 +51,123 @@ object Mat4Util { fun Mat4.reset() { val array = this.array - array[0] = 1.0f; array[1] = 0.0f;array[2] = 0.0f;array[3] = 0.0f - array[4] = 0.0f; array[5] = 1.0f;array[6] = 0.0f;array[7] = 0.0f - array[8] = 0.0f; array[9] = 0.0f;array[10] = 1.0f;array[11] = 0.0f - array[12] = 0.0f; array[13] = 0.0f;array[14] = 0.0f;array[15] = 1.0f + System.arraycopy(empty.array, 0, array, 0, Mat4.length) + } + + fun rotateX(m: Mat4, angle: Float) { + val c = GLM.cos(angle) + val s = GLM.sin(angle) + + val dot = angle * angle + val inv = GLM.inverseSqrt(dot) + + val aX = angle * inv + + val tempX = (1f - c) * aX + + val rotate00 = c + tempX * aX + + val rotate12 = s * aX + val rotate11 = c + + val rotate21 = -s * aX + + m[0, 0] = m[0, 0] * rotate00 + m[0, 1] = m[0, 1] * rotate00 + m[0, 2] = m[0, 2] * rotate00 + m[0, 3] = m[0, 3] * rotate00 + + val res1x = m[1, 0] * rotate11 + m[2, 0] * rotate12 + val res1y = m[1, 1] * rotate11 + m[2, 1] * rotate12 + val res1z = m[1, 2] * rotate11 + m[2, 2] * rotate12 + val res1w = m[1, 3] * rotate11 + m[2, 3] * rotate12 + + m[2, 0] = m[1, 0] * rotate21 + m[2, 0] * c + m[2, 1] = m[1, 1] * rotate21 + m[2, 1] * c + m[2, 2] = m[1, 2] * rotate21 + m[2, 2] * c + m[2, 3] = m[1, 3] * rotate21 + m[2, 3] * c + + m[1, 0] = res1x + m[1, 1] = res1y + m[1, 2] = res1z + m[1, 3] = res1w + } + + fun rotateY(m: Mat4, angle: Float) { + val c = GLM.cos(angle) + val s = GLM.sin(angle) + + val dot = angle * angle + val inv = GLM.inverseSqrt(dot) + + val aY = angle * inv + + val tempY = (1f - c) * aY + + val rotate02 = -s * aY + + val rotate11 = c + tempY * aY + + val rotate20 = s * aY + + + val res0x = m[0, 0] * c + m[2, 0] * rotate02 + val res0y = m[0, 1] * c + m[2, 1] * rotate02 + val res0z = m[0, 2] * c + m[2, 2] * rotate02 + val res0w = m[0, 3] * c + m[2, 3] * rotate02 + + m[1, 0] = m[1, 0] * rotate11 + m[1, 1] = m[1, 1] * rotate11 + m[1, 2] = m[1, 2] * rotate11 + m[1, 3] = m[1, 3] * rotate11 + + m[2, 0] = m[0, 0] * rotate20 + m[2, 0] * c + m[2, 1] = m[0, 1] * rotate20 + m[2, 1] * c + m[2, 2] = m[0, 2] * rotate20 + m[2, 2] * c + m[2, 3] = m[0, 3] * rotate20 + m[2, 3] * c + + m[0, 0] = res0x + m[0, 1] = res0y + m[0, 2] = res0z + m[0, 3] = res0w + } + + fun rotateZ(m: Mat4, angle: Float) { + val c = GLM.cos(angle) + val s = GLM.sin(angle) + + val dot = angle * angle + val inv = GLM.inverseSqrt(dot) + + val aZ = angle * inv + + val tempZ = (1f - c) * aZ + + val rotate01 = s * aZ + + val rotate10 = -s * aZ + + val rotate22 = c + tempZ * aZ + + + val res0x = m[0, 0] * c + m[1, 0] * rotate01 + val res0y = m[0, 1] * c + m[1, 1] * rotate01 + val res0z = m[0, 2] * c + m[1, 2] * rotate01 + val res0w = m[0, 3] * c + m[1, 3] * rotate01 + + m[1, 0] = m[0, 0] * rotate10 + m[1, 0] * c + m[1, 1] = m[0, 1] * rotate10 + m[1, 1] * c + m[1, 2] = m[0, 2] * rotate10 + m[1, 2] * c + m[1, 3] = m[0, 3] * rotate10 + m[1, 3] * c + + m[2, 0] = m[2, 0] * rotate22 + m[2, 1] = m[2, 1] * rotate22 + m[2, 2] = m[2, 2] * rotate22 + m[2, 3] = m[2, 3] * rotate22 + + m[0, 0] = res0x + m[0, 1] = res0y + m[0, 2] = res0z + m[0, 3] = res0w } } diff --git a/src/test/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4UtilTest.kt b/src/test/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4UtilTest.kt new file mode 100644 index 000000000..4ba4487e4 --- /dev/null +++ b/src/test/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4UtilTest.kt @@ -0,0 +1,47 @@ +/* + * Minosoft + * Copyright (C) 2020-2023 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.gui.rendering.util.mat.mat4 + +import de.bixilon.kotlinglm.func.rad +import de.bixilon.kotlinglm.mat4x4.Mat4 +import de.bixilon.kotlinglm.vec3.Vec3 +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class Mat4UtilTest { + + @Test + fun `custom rotateX`() { + val expected = Mat4().rotateAssign(12.0f.rad, Vec3(1, 0, 0)) + val actual = Mat4().apply { Mat4Util.rotateX(this, 12.0f.rad) } + + assertEquals(expected, actual) + } + + @Test + fun `custom rotateY`() { + val expected = Mat4().rotateAssign(12.0f.rad, Vec3(0, 1, 0)) + val actual = Mat4().apply { Mat4Util.rotateY(this, 12.0f.rad) } + + assertEquals(expected, actual) + } + + @Test + fun `custom rotateZ`() { + val expected = Mat4().rotateAssign(12.0f.rad, Vec3(0, 0, 1)) + val actual = Mat4().apply { Mat4Util.rotateZ(this, 12.0f.rad) } + + assertEquals(expected, actual) + } +}