LWJGL 上的 glUniform1fv() 引起的 JVM 崩溃

JVM crash caused by glUniform1fv() on LWJGL

我和我的一个朋友正在使用 LWJGL 制作 3D 引擎,在尝试将浮点数组作为统一传递给我的片段着色器后,JVM 开始崩溃。这是 JVM 崩溃日志的相关部分:

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  org.lwjgl.opengl.GL20C.nglUniform1fv(IIJ)V+0
j  org.lwjgl.opengl.GL20C.glUniform1fv(ILjava/nio/FloatBuffer;)V+9
j  org.lwjgl.opengl.GL20.glUniform1fv(ILjava/nio/FloatBuffer;)V+2
j  com.alyxferrari.neo3d.obj.Environment3D.rebuildLights()V+118
j  com.alyxferrari.neo3d.gfx.NEOEngine.startRender()V+69
j  com.alyxferrari.neo3d.example.NEOExample.main([Ljava/lang/String;)V+65
v  ~StubRoutines::call_stub

据此,我显然认为问题源于在此方法中分配 float[] 制服:

public void rebuildLights() {
        float[] xs = new float[DirectionalLight.MAX_IN_SHADER];
        float[] ys = new float[DirectionalLight.MAX_IN_SHADER];
        float[] zs = new float[DirectionalLight.MAX_IN_SHADER];
        float[] strengths = new float[DirectionalLight.MAX_IN_SHADER];
        for (int i = 0; i < lights.length; i++) {
            xs[i] = lights[i].getDirection().x;
            ys[i] = lights[i].getDirection().y;
            zs[i] = lights[i].getDirection().z;
            strengths[i] = lights[i].getStrength();
        }
        glUniform1fv(glGetUniformLocation(NEOEngine.getShader(), "lightXs"), FloatBuffer.wrap(xs));
        glUniform1fv(glGetUniformLocation(NEOEngine.getShader(), "lightYs"), FloatBuffer.wrap(ys));
        glUniform1fv(glGetUniformLocation(NEOEngine.getShader(), "lightZs"), FloatBuffer.wrap(zs));
        glUniform1fv(glGetUniformLocation(NEOEngine.getShader(), "strengths"), FloatBuffer.wrap(strengths));
        glUniform1f(glGetUniformLocation(NEOEngine.getShader(), "ambience"), ambience);
        glUniform1i(glGetUniformLocation(NEOEngine.getShader(), "count"), lights.length);
    }

如您所见,我根据灯光对象中的数据填充了一些数组,并将它们包装在 FloatBuffer 中以传递给 glUniform1fv()。我唯一能想到的是,这可能不是我应该制作 FloatBuffer 的方式。当我在 LWJGL 中看到 glUniform1fv() 采用 FloatBuffer 而不是 float[] 时,我查看了 Java 的 FloatBuffer class 文档并看到 FloatBuffer.wrap(float[ ]) 似乎是从 float[] 制作 FloatBuffer 的好方法。有没有一种特定的方法可以为 LWJGL 的 OpenGL 绑定制作一个你需要做的方法?或者我的代码还有其他问题吗?提前致谢:)

LWJGL 仅适用于 direct NIO 缓冲区,即不受堆上 Java 数组支持但由分配的堆外虚拟内存支持的字节缓冲区在 JVM 的进程中,但在 JVM 管理的垃圾收集堆之外。这是为了有效地将本机虚拟内存与低级库(例如 OpenGL)通信,而不必在将指向它的指针传递给本机库之前潜在地“固定”garbage-collectable/moveable 内存。

请参阅 https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/nio/ByteBuffer.html

中的“直接缓冲区与非直接缓冲区”部分

崩溃的原因是您提供的非直接ByteBuffer(由.wrap(array) 创建的那个)的内部地址字段值为0。这是LWJGL 在实际调用时查看的内容本机 OpenGL 函数。它读取该地址字段值并期望这是要上传到制服的客户端内存的虚拟内存地址,然后 OpenGL 将这样处理。

所以,本质上:当您使用 LWJGL 时,您必须始终只使用直接 NIO 缓冲区,而不是数组包装器!

您应该先阅读这篇 LWJGL 3 博客 post,了解 LWJGL 3 中的高效内存管理:https://blog.lwjgl.org/memory-management-in-lwjgl-3/