[SOLVED] JVM crashes on repeated calls to glTexImage2D

Started by lscosta, January 12, 2019, 07:48:43

Previous topic - Next topic

lscosta

Hi!
I am working on a fluid simulation that involves updating a GL texture frequently (say, every frame). I have a float[] whose contents I mutate before calling glTexImage2D(). I'm not exactly what flavor of problem I am experiencing (error on my part? JVM/system bug? LWJGL issue?), but the symptom is that my program works correctly for several seconds, then the JVM crashes. My hunch is that some memory is being mismanaged (again, not sure if it's by me/Java/LWJGL). Here are some of my thoughts so far:


  • I've counted and it seems to take the same number of calls to reach the crash every time: the 384th call causes a SIGSEGV. This does not seem to vary with the dimensions of the texture.
  • GL does not report an error immediately before or after any of the calls.
  • Since the content I am attempting to upload is generated randomly, I don't think this issue is dependent on the data passed in.
  • I develop using IntelliJ IDEA Ultimate. If I have IntelliJ generate a JAR and then run in from the command line, the issue persists. I haven't yet tried compiling from scratch (lotsa dependencies, y'know).

Here's a pared down version of my code. It's spread across several files, so I'll just paste the relevant bits here but if it would help I can put in more detail later. There's also an error log file attached.

My Texture class:
package graphics;
import org.joml.Vector4f;
import java.util.Arrays;

import static org.lwjgl.opengl.GL41.*;

public class Texture {
    private int width;
    private int height;
    private int textureID;
    private int textureUnit;
    private float[] data;

    public Texture(int width, int height, int textureUnit) {
        this.textureUnit = textureUnit;
        this.width = width;
        this.height = height;
        this.data = new float[width * height * 4];
        Arrays.fill(this.data, 0.f);

        textureID = glGenTextures();
        bind();
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        pushToGL();

        unbind();
        checkError();
    }

    private void checkError() {
        int error = glGetError();
        if (error != GL_NO_ERROR) {
            System.out.println("Error: " + error);
        }
    }

    public void pushToGL() {
        checkError();
        // this seems to be the problem, doc:
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, data);
        checkError();
    }

    public void setPixel(int x, int y, float r, float g, float b, float a) {
        if (x < 0 || x >= width || y < 0 || y >= height)
            throw new IndexOutOfBoundsException();
        else {
            int index = 4 * (x + y * width);
            data[index] = r;
            data[index + 1] = g;
            data[index + 2] = b;
            data[index + 3] = a;
        }
    }

    public void setPixel(int x, int y, Vector4f color) {
        if (x < 0 || x >= width || y < 0 || y >= height)
            throw new IndexOutOfBoundsException();
        else {
            int index = 4 * (x + y * width);
            data[index] = color.x;
            data[index + 1] = color.y;
            data[index + 2] = color.z;
            data[index + 3] = color.w;
        }
    }

    public Vector4f getPixel(int x, int y) {
        if (x < 0 || x >= width || y < 0 || y >= height)
            throw new IndexOutOfBoundsException();
        else {
            int index = 4 * (x + y * width);
            return new Vector4f(data[index],
                    data[index + 1],
                    data[index + 2],
                    data[index + 3]);
        }
    }

    public void bind() {
        glActiveTexture(textureUnit);
        glBindTexture(GL_TEXTURE_2D, textureID);
    }

    public void unbind() {
        glBindTexture(GL_TEXTURE_2D, 0);
    }

    // also some getters...
}


Here's the caller:
import graphics.Shader;

import java.io.IOException;

public class OasisWindow extends Fenster {

    private Shader geometryShader;
    private PoolShape poolShape;
    private CausticShape causticShape; // thing that owns a texture

    public OasisWindow() {
        windowTitle = "Oasis";
    }

    @Override
    protected void preLoop() { // this gets run once after the GL context exists
        // create texture
        try {
            geometryShader = new Shader("res/shaders/preview.vert", "res/shaders/preview.frag");
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
        poolShape = new PoolShape();
        causticShape = new CausticShape(poolShape);
        geometryShader.bind();
        geometryShader.setUniform("tex", 0);
        causticShape.causticTexture.bind(); // only the one texture so bind it at the start
    }

    @Override
    protected void loopBody(float dt) { // this gets run in the glfw loop
        poolShape.frame(dt);
        causticShape.update(); // calls Texture::setPixel() a bunch of times
        causticShape.causticTexture.pushToGL(); // this is the problem

        defaultCameraUpdate(dt);
        applyDefaultCamera(geometryShader);

        poolShape.draw(geometryShader);
        causticShape.draw(geometryShader);
    }

    // there's also some cleanup code but the JVM helps me out by crashing first!
}


Thank you very much for your time!
-Lael


spasi

Hey Lael,

Getting a crash after a certain number of calls suggests an issue related to GC or JIT compilation. You could try the following:

- Run your application as is with -XX:-CriticalJNINatives.
- Replace the data array with a FloatBuffer.

Is the crash fixed with either option?

lscosta


spasi

Hmm, sounds like JDK-8167409. LWJGL skips Critical Natives for affected functions on Linux & macOS, but functions like glTexImage2D are not in that set. Could you please try with Java 10 or 11 (without -XX:-CriticalJNINatives) to see if the issue is indeed resolved?