Problem with displaying simple triangle with Anton's OpenGL Tutorial

Started by hejcz, October 10, 2015, 23:43:53

Previous topic - Next topic

hejcz

Hello!

I've recently started to learning OpenGL with LWJGL3. I've found nice tutorial but it doesn't seem to work in my version ( modified to lwjgl ). Could you help me and tell what is wrong? I have normal black window but the triangle doesn't appear. Thanks for help :)

Edit:
On SO I got advice to change  "in vec3 vp;" to "layout(location=0) in vec3 vp;" however it didn't change anything.


import org.lwjgl.BufferUtils;
import org.lwjgl.Sys;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;
 
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL12.*;
import static org.lwjgl.opengl.GL13.*;
import static org.lwjgl.opengl.GL14.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL21.*;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.opengl.GL31.*;
import static org.lwjgl.opengl.GL32.*;
import static org.lwjgl.opengl.GL33.*;
import static org.lwjgl.opengl.GL40.*;
import static org.lwjgl.opengl.GL41.*;
import static org.lwjgl.opengl.GL42.*;
import static org.lwjgl.opengl.GL43.*;
import static org.lwjgl.opengl.GL44.*;
import static org.lwjgl.opengl.GL45.*;
import static org.lwjgl.system.MemoryUtil.*;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;
 
public class HelloWorld 
{
 
    public static void main(String[] args) 
    {
        long window = NULL;
        int vao, vbo;
        
        final float[] points = 
        {
             0.0f,  0.5f,   0.0f, 
             0.5f, -0.5f,   0.0f,
            -0.5f, -0.5f,   0.0f
        };
        
        final String vertex_shader =
        "#version 410\n" +
        "in vec3 vp;" +
        "void main () {" +
        "   gl_Position = vec4 (vp, 1.0);" +
        "}";

        final String fragment_shader =
        "#version 410\n" +
        "out vec4 frag_colour;" +
        "void main () {" +
        "   frag_colour = vec4 (0.5, 0.0, 0.5, 1.0);" +
        "}";
        
        int vs, fs, shader_programme;
        
        if( glfwInit() == GL_FALSE )
        {
            System.err.println("Can't initialize glfw!");
            return;
        }
        
        window = glfwCreateWindow (640, 480, "Hello Triangle", NULL, NULL);
        if (window == 0) {
            glfwTerminate();
            return;
        }
        glfwMakeContextCurrent (window);
        
        
        GL.createCapabilities();
        

        glEnable (GL_DEPTH_TEST); 
        glDepthFunc (GL_LESS);
        
        vbo = glGenBuffers();
        glBindBuffer (GL_ARRAY_BUFFER, vbo);
        glBufferData (GL_ARRAY_BUFFER, FloatBuffer.wrap(points),
                      GL_STATIC_DRAW);
        
        vao = glGenVertexArrays();
        glBindVertexArray (vao);
        glEnableVertexAttribArray (0);
        glBindBuffer (GL_ARRAY_BUFFER, vbo);
        glVertexAttribPointer (0, 3, GL_FLOAT, false, 0, 0);
        
        vs = glCreateShader (GL_VERTEX_SHADER);
        glShaderSource (vs, vertex_shader);
        glCompileShader (vs);

        fs = glCreateShader (GL_FRAGMENT_SHADER);
        glShaderSource (fs, fragment_shader);
        glCompileShader (fs);
        
        shader_programme = glCreateProgram ();
        glAttachShader (shader_programme, fs);
        glAttachShader (shader_programme, vs);
        glLinkProgram (shader_programme);

        while ( glfwWindowShouldClose(window) == GL_FALSE ) {

            glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            glUseProgram (shader_programme);
            glBindVertexArray (vao);

            glDrawArrays (GL_TRIANGLES, 0, 3);

            glfwPollEvents ();

            glfwSwapBuffers (window);
        }
        

        glfwTerminate();
        return;
    }
 
}

SHC

There, the error is on line 84:

glBufferData (GL_ARRAY_BUFFER, FloatBuffer.wrap(points),
                      GL_STATIC_DRAW);


LWJGL doesn't work with on heap buffers, it requires direct buffers. Do this instead.

FloatBuffer buffer = BufferUtils.createFloatBuffer(points.length);
buffer.put(points);
buffer.rewind();

glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);


Hope this helps.

hejcz

Thank you very much. It works now. You made me smile as I suspected the error in this line :D Even though I wasn't prepared for this kind of explanation.

Could you explain me the difference between heap and direct buffer?

SHC

An on heap buffer is nothing but a buffer whose memory is nothing but a byte array. This array is stored in java's heap memory and hence the name on heap buffer. The issue with this is because arrays are treated like objects, i.e., it is not guaranteed that their memory is contiguous in memory, and can vary depending on the JVM implementation.

On the other hand, in native system code, (like in languages C/C++) arrays are contiguous blocks of memory. This is actually not possible in Java because the garbage collector can move the memory anytime during the execution. So for this reason, native code from OpenGL driver implementation cannot access that memory accurately and it will not work.

So there came direct buffers, whose memory comes directly from OS and is present outside the JVM. This memory is native, and hence it is possible for OpenGL driver to access the right information that you have in that memory. This memory is also guaranteed to be contiguous. You can create a direct ByteBuffer like this in Java.

ByteBuffer buffer = BufferUtils.createByteBuffer(numBytes);

// Which is the same as this
ByteBuffer buffer = ByteBuffer.allocateDirect(numBytes)
                                 .order(ByteOrder.nativeOrder());


And the code for creating a direct float buffer is like this.

FloatBuffer buffer = BufferUtils.createFloatBuffer(numFloats);

// Which is the same as this
FloatBuffer buffer = ByteBuffer.allocateDirect(numFloats * Float.BYTES)
                                .order(ByteOrder.nativeOrder())
                                .asFloatBuffer();


As you can see, there is no method called as allocateDirect for float buffers and also any other buffer types. The reason for this behaviour is that in the OS terms, memory is nothing but a large area of bytes. A FloatBuffer just operates on bytes, but is a view that views a byte memory as floats.

However, the allocation of these direct buffers takes some cost more than the allocation of the on heap buffers, and for this reason, you should only allocate these buffers once before using in initialization step, and reuse them every frame if necessary.

Cornix

Quote from: SHC on October 11, 2015, 12:59:22
However, the allocation of these direct buffers takes some cost more than the allocation of the on heap buffers, and for this reason, you should only allocate these buffers once before using in initialization step, and reuse them every frame if necessary.
They might also be slower in read/write speeds since data has to travel across JVM borders into system memory.
You shouldnt use direct buffers for anything other then communicating with native libraries.

hejcz


spasi

Quote from: Cornix on October 11, 2015, 13:21:52They might also be slower in read/write speeds since data has to travel across JVM borders into system memory.
You shouldnt use direct buffers for anything other then communicating with native libraries.

This is wrong, there's no difference between on-heap and off-heap memory. Both are just memory and the JVM can access either without crossing any "border". The JIT compiler produces the same instructions for accessing fields, arrays and off-heap buffers.

The only difference is that the JVM can depend on certain invariants for on-heap memory access, that in many cases lead to more efficient code generation. This is unrelated to the memory accesses themselves, which are just as fast (or slow) for either on- or off- heap memory.

Cornix

Quote from: spasi on October 11, 2015, 20:09:53
Quote from: Cornix on October 11, 2015, 13:21:52They might also be slower in read/write speeds since data has to travel across JVM borders into system memory.
You shouldnt use direct buffers for anything other then communicating with native libraries.

This is wrong, there's no difference between on-heap and off-heap memory. Both are just memory and the JVM can access either without crossing any "border". The JIT compiler produces the same instructions for accessing fields, arrays and off-heap buffers.

The only difference is that the JVM can depend on certain invariants for on-heap memory access, that in many cases lead to more efficient code generation. This is unrelated to the memory accesses themselves, which are just as fast (or slow) for either on- or off- heap memory.
I have never tested this myself, I have only read it online somewhere. Do you have any statistics for this? (not that I have, I am just curious)

spasi

Quote from: Cornix on October 11, 2015, 20:15:17I have never tested this myself, I have only read it online somewhere. Do you have any statistics for this? (not that I have, I am just curious)

The easiest way is to use JITWatch with the hsdis library installed. It's very simple to generate a log and then explore it in JITWatch's TriView (source left, bytecode middle, assembly right).

An example where off-heap access can be slower is math-heavy operations with long dependency chains. The JVM can optimize such operations aggressively and reorder memory accesses in such a way that reduces register pressure on the CPU (=faster execution). It won't do that for off-heap access and the final assembly will look exactly like the source (same order of access).