LWJGL / lwjgl3

LWJGL is a Java library that enables cross-platform access to popular native APIs useful in the development of graphics (OpenGL, Vulkan, bgfx), audio (OpenAL, Opus), parallel computing (OpenCL, CUDA) and XR (OpenVR, LibOVR, OpenXR) applications.
https://www.lwjgl.org
BSD 3-Clause "New" or "Revised" License
4.82k stars 639 forks source link

glVertexAttribPointer crashes the JVM #598

Closed PhoenixXKN closed 4 years ago

PhoenixXKN commented 4 years ago

Environment

Description

So, i played around a bit and wanted to draw a simple triangle using org.lwjgl.opengl.GL33C (The version of OpenGL that was used in the book i use to learn OpenGL). But since the book is based on C++, there have been a few complications. I could solve most of them, but one keeps staying: The JVM crashes when i call either glVertexAttribPointer or glDrawArrays. Here's my code(it's a bit messy, i playes around with different things trying to solve the issue):

package de.phoenixxkn.games.deckedouttry;
import de.phoenixxkn.games.deckedouttry.helpers.*;
import de.phoenixxkn.games.deckedouttry.shaders.ShaderLoader;
import de.phoenixxkn.games.deckedouttry.throwables.*;

import java.io.IOException;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.file.*;
import java.util.logging.*;

import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWKeyCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.BufferUtils;

import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.opengl.GL20C.GL_COMPILE_STATUS;
import static org.lwjgl.opengl.GL20C.glCompileShader;
import static org.lwjgl.opengl.GL20C.glGetShaderInfoLog;
import static org.lwjgl.opengl.GL20C.glShaderSource;
import static org.lwjgl.opengl.GL33C.*;
import static org.lwjgl.system.MemoryStack.*;

public class MainGame {

    private static final long NULL = 0;
    // The window handle
    private long window;
    private static Logger log = LogGenerator.generateLogger("Main");

    public void run() {
        log.info("Instance created. Calling method run().");
    //  System.out.println("Hello LWJGL " + Version.getVersion() + "!");
        try {
            init();
        }catch(IllegalStateException e) {
            log.severe(e.getMessage());
        }
        loop();

        // Free the window callbacks and destroy the window
        glfwFreeCallbacks(window);
        glfwDestroyWindow(window);

        // Terminate GLFW and free the error callback
        glfwTerminate();
        glfwSetErrorCallback(null).free();
    }

    private void init() {
        log.info("calling method init()");
        // Setup an error callback. The default implementation
        // will print the error message in System.err.
        GLFWErrorCallback.createPrint(System.err).set();
        log.info("Error Callback initualized.");
        // Initialize GLFW. Most GLFW functions will not work before doing this.
        if ( !glfwInit() ) {
            throw new IllegalStateException("Unable to initialize GLFW");
        }
        // Configure GLFW
        glfwDefaultWindowHints(); // optional, the current window hints are already the default
        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
        glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable

        // Create the window
        window = glfwCreateWindow(300, 300, "Hello World!", NULL, NULL);
        if ( window == NULL )
            throw new RuntimeException("Failed to create the GLFW window");

        // Setup a key callback. It will be called every time a key is pressed, repeated or released.
        glfwSetKeyCallback(window, new GLFWKeyCallback() {

            @Override
            public void invoke(long window, int key,int scancode, int action, int mods){
            if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
                glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
        }});

        // Get the thread stack and push a new frame
        try ( MemoryStack stack = stackPush() ) {
            IntBuffer pWidth = stack.mallocInt(1); // int*
            IntBuffer pHeight = stack.mallocInt(1); // int*

            // Get the window size passed to glfwCreateWindow
            glfwGetWindowSize(window, pWidth, pHeight);

            // Get the resolution of the primary monitor
            GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());

            // Center the window
            glfwSetWindowPos(
                window,
                (vidmode.width() - pWidth.get(0)) / 2,
                (vidmode.height() - pHeight.get(0)) / 2
            );
        } // the stack frame is popped automatically

        // Make the OpenGL context current
        glfwMakeContextCurrent(window);
        // Enable v-sync
        glfwSwapInterval(1);

        // Make the window visible
        glfwShowWindow(window);
    }

    private void loop() {
        // This line is critical for LWJGL's interoperation with GLFW's
        // OpenGL context, or any context that is managed externally.
        // LWJGL detects the context that is current in the current thread,
        // creates the GLCapabilities instance and makes the OpenGL
        // bindings available for use.
        GL.createCapabilities();

        // Set the clear color;
        glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
        int vertexShader = glCreateShader(GL_VERTEX_SHADER);
        int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        try {
            String vshaderSource, fshaderSource;
            vshaderSource = ShaderLoader.loadShaderSource(Path.of("main.vs"));
            glShaderSource(vertexShader, vshaderSource);
            fshaderSource = ShaderLoader.loadShaderSource(Path.of("main.fs"));
            glShaderSource(fragmentShader, fshaderSource);
        } catch (IOException e) {
            log.severe(String.format("Loading of Shaders failed due to IOException: %s", e.getMessage()));
            return;
        } catch(ShaderCompilationException e) {
            log.severe(String.format("Compilation of Shaders failed due to a ShaderCompilatonException: %s", e.getMessage()));
        }
        glCompileShader(vertexShader);
        int success = glGetShaderi(vertexShader, GL_COMPILE_STATUS);
        String infoLog;
        if(success == 0) {
            infoLog = glGetShaderInfoLog(vertexShader);
            log.severe("Shader Compilation failed: " + infoLog);
        }
        log.info("" + success);
        glCompileShader(fragmentShader);
        success = glGetShaderi(fragmentShader, GL_COMPILE_STATUS);
        if(success == 0) {
            infoLog = glGetShaderInfoLog(fragmentShader);
            log.severe("Shader Compilation failed:" + infoLog);
        }
        log.info("" + success);
        int shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);
        float[] vertices = {
                -0.5f, -0.5f, -0.0f,    
                0.5f, 0.5f, 0.0f,
                0.0f,0.5f,0.0f
            };
        FloatBuffer b = BufferUtils.createFloatBuffer(Float.SIZE * 9);
        b.put(vertices);
        System.out.println(b);
            int VBO = glGenBuffers();
            int VAO = glGenVertexArrays();
            log.info("VBO:" + VBO + "VAO: " + VAO);
            // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
            glBindVertexArray(VBO);

            glBindBuffer(GL_ARRAY_BUFFER, VBO);
            glVertexAttribPointer(0, Float.SIZE * 9, GL_FLOAT, false, 12,  b);
            glEnableVertexAttribArray(0);
            glBindBuffer(GL_ARRAY_BUFFER, VBO);
            glBindVertexArray(0);       

            // uncomment this call to draw in wireframe polygons.
            //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

        // Run the rendering loop until the user has attempted to close
        // the window or has pressed the ESCAPE key.
            while (!glfwWindowShouldClose(window))
            {
                // input
                // -----

                // render
                // ------
                glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
                glClear(GL_COLOR_BUFFER_BIT);

                // draw our first triangle
                glUseProgram(shaderProgram);
                glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
                glDrawArrays(GL_TRIANGLES, 0, 3);
                 glBindVertexArray(0); // no need to unbind it every time 

                // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
                // -------------------------------------------------------------------------------
                glfwSwapBuffers(window);
                glfwPollEvents();
            }

    }

    public static void main(String[] args) {
        log.info("Creating and running new MainGame instance...");
        new MainGame().run();
    }

}

The Referenced ShaderLoader is just a helper class that reads shader files, and i can confirm that the issue is not caused by it.

What am i doing wrong here?

octylFractal commented 4 years ago

You're doing a relative put on the FloatBuffer without flipping it back, so it's going to be 0 bytes long.

httpdigest commented 4 years ago

You have multiple issues in your code. Let's dissect the following:

glBindVertexArray(VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glVertexAttribPointer(0, Float.SIZE * 9, GL_FLOAT, false, 12,  b);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBindVertexArray(0); 
  1. You bind a name/handle VBO generated via glGenBuffers as a VAO via glBindVertexArray. This is wrong. You should bind VAO (generated via glGenVertexArrays()) via glBindVertexArray().
  2. You bind your buffer object to GL_ARRAY_BUFFER but you never upload any data to it.
  3. You are calling the glVertexAttribPointer() by supplying the Java NIO ByteBuffer as the last argument. This is also wrong, because it will call the underlying glVertexAttribPointer() OpenGL function with the address of the Java NIO ByteBuffer. This overload is meant for client-side arrays, which you are not using. You use server-side buffer objects, however because you have a buffer object bound to GL_ARRAY_BUFFER, this address will be treated as an offset into that buffer object (which has no data uploaded to it).
  4. The second argument of glVertexAttribPointer() is not the number of bytes! (which by the way is also not what you supply. Please read what Float.SIZE actually means!)

In general: Please read the OpenGL reference documentation of all functions you are calling!

PhoenixXKN commented 4 years ago

So, I looked at the code again and it was a combination of typos and my attempt to translate C++ Code into Java(The reason i used Float.SIZE, for example). I also somehow managed to overlook the call to glBufferData, which i added back into the code. After the direct translation from C++ didn't work, i played around with a lot of values and changed them to whatever might've worked... The original glVertexAttribPointer() call was glVertexAttribPointer(0, 3, GL_FLOAT, false, 12, 0);, which i changed it back to, but it still crashes. I know this sounds like a stupid question, but what do I need to change? I:

  1. Changed glBindVertexArray(VBO); to glBindVertexArray(VAO); 2.added a glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW); 3.Restored the original glVertexAttribPointer(0, 3, GL_FLOAT, false, 12, 0); But it still keeps crashing. I looked at the OpenGL documentation for glVertexAttribPointer but i didn't see anything wrong with the form it has now. I am fairly new to OpenGL and can understand if you deem this thread the wrong place to ask such questions, in that case if you could recommend another useful resource, i would be very thankful.
httpdigest commented 4 years ago

Stackoverflow is generally preferred for "why does my code not work?" kind of questions. I highly recommend you pose your question there and you will definitely get an answer quickly.

PhoenixXKN commented 4 years ago

@httpdigest Thanks, i'll be sure to ask there from now on.