optimize Mat4 math, skeletal: reduce memory allocations

This commit is contained in:
Moritz Zwerger 2023-10-22 22:36:08 +02:00
parent 8c61605c88
commit d0ea37e93a
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
12 changed files with 192 additions and 31 deletions

View File

@ -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() {

View File

@ -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() {

View File

@ -36,7 +36,7 @@ class ChestAnimation(
transform.value
.translateAssign(transform.pivot)
.rotateRadAssign(rotation)
.translateAssign(-transform.pivot)
.translateAssign(transform.nPivot)
}

View File

@ -37,7 +37,7 @@ class ShulkerAnimation(
.translateAssign(translation)
.translateAssign(transform.pivot)
.rotateRadAssign(rotation)
.translateAssign(-transform.pivot)
.translateAssign(transform.nPivot)
}

View File

@ -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) {

View File

@ -23,6 +23,7 @@ class TransformInstance(
val pivot: Vec3,
val children: Map<String, TransformInstance>,
) {
val nPivot = -pivot
val value = Mat4()

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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()
}
}

View File

@ -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")
}

View File

@ -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
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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)
}
}