Recent posts

#21
OpenGL / Re: First steps with LWJGL and...
Last post by stuck1a - March 24, 2024, 21:03:01
Heureka, I finally found the issue myself  :)

The problem was, that I've called glfwSwapBuffers() before the renderer (instead of after) within the renderloop.

For all those who have similar problems and end up here via google, I'd like to point out the following excellent tutorial. which uses OpenGL under C++, but which can be easily transferred to Java/LWJGL and is the only tutorial I know that shows the complete render process in detail.

Best regards,
stuck1a
#22
OpenGL / Re: First steps with LWJGL and...
Last post by stuck1a - March 24, 2024, 09:27:43
In the meantime, I implemented a few more validations and combined all the code into a single class for the sake of simplicity.
Compiling and linking of the shaders works, but for some reason, the JVM crashes with exit code -1073741819 (0xC0000005) everytime it comes to  glDrawArrays(), but I don't know why. As you can see in the code, both, the buffer and the shader is set.

This is the complete sequence:


Code: Class "Deadzone"
public class Deadzone {
  public static void main(String[] args) throws InterruptedException {
    TestForOpenGL testing = new TestForOpenGL();
    try {
      testing.executeTest();
    } catch (java.lang.Exception e) {
      System.err.println(e.getMessage());
    }
  }
}


Code: Class "TestForOpenGL"
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.system.MemoryStack;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Objects;

import static java.lang.Thread.sleep;
import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.system.MemoryStack.stackPush;
import static org.lwjgl.system.MemoryUtil.NULL;

public class TestForOpenGL {
  private long windowId;
  
  private int shaderProgramId = 0;
  private int vertexShaderId = 0;
  private int fragmentShaderId = 0;
  
  private int vaoId = 0;
  private int vboId = 0;
  
  
  public void executeTest() throws Exception {
    // Initialize LWJGL
    GLFWErrorCallback errorCallback = GLFWErrorCallback.createPrint(System.err);
    glfwSetErrorCallback(errorCallback);
    if ( !glfwInit() ) throw new IllegalStateException("Unable to initialize GLFW");
    
    // Create render context (the application window)
    glfwDefaultWindowHints();
    glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
    glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
    windowId = glfwCreateWindow(800, 600, "Render test", NULL, NULL);
    if ( windowId == NULL ) {
      glfwTerminate();
      throw new RuntimeException("Failed to create the GLFW window");
    }
    try ( MemoryStack stack = stackPush() ) {
      IntBuffer pWidth = stack.mallocInt(1);
      IntBuffer pHeight = stack.mallocInt(1);
      glfwGetWindowSize(windowId, pWidth, pHeight);
      GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
      assert vidmode != null;
      glfwSetWindowPos(windowId, (vidmode.width() - pWidth.get(0)) / 2, (vidmode.height() - pHeight.get(0)) / 2);
    }
    glfwMakeContextCurrent(windowId);
    glfwSwapInterval(1);
    glfwShowWindow(windowId);
    GL.createCapabilities();
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    
    // Render loop
    while ( !glfwWindowShouldClose(windowId) ) {
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      glfwSwapBuffers(windowId);
      glfwPollEvents();
      render();
      sleep(1000L / 30L);
    }

    // Shutdown
    glfwFreeCallbacks(windowId);
    glfwDestroyWindow(windowId);
    glfwTerminate();
    Objects.requireNonNull(glfwSetErrorCallback(null)).free();
  }
  
  
  private void render() throws Exception {
    /* Set up the shader program, if not done yet */
    if (shaderProgramId == 0) {
      try {
        shaderProgramId = glCreateProgram();
        if (shaderProgramId == 0) throw new Exception("Could not create shader program");
        
        // Create, compile and attach vertex shader
        String vertexShaderCode =
          "#version 150 core\n" +
          "in vec2 position;\n" +
          "in vec3 color;\n" +
          "out vec3 vertexColor;\n" +
          "void main() { vertexColor = color; gl_Position = vec4(position, 0.0, 1.0); }";
        vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
        if (vertexShaderId == 0) throw new RuntimeException("Failed to create vertex shader!");
        glShaderSource(vertexShaderId, vertexShaderCode);
        glCompileShader(vertexShaderId);
        if (glGetShaderi(vertexShaderId, GL_COMPILE_STATUS) == 0) throw new RuntimeException("Error compiling vertex shader!\n" + glGetShaderInfoLog(vertexShaderId, 1024));
        glAttachShader(shaderProgramId, vertexShaderId);
        
        // Create, compile and attach fragment shader
        String fragmentShaderCode =
          "#version 150 core\n\n" +
          "in vec3 vertexColor;\n\n" +
          "out vec4 outColor;\n\n" +
          "void main() { outColor = vec4(vertexColor, 1.0);}";
        fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
        if (fragmentShaderId == 0) throw new RuntimeException("Failed to create fragment shader!");
        glShaderSource(fragmentShaderId, fragmentShaderCode);
        glCompileShader(fragmentShaderId);
        if (glGetShaderi(fragmentShaderId, GL_COMPILE_STATUS) == 0) throw new RuntimeException("Error compiling fragment shader!\n" + glGetShaderInfoLog(fragmentShaderId, 1024));
        glAttachShader(shaderProgramId, fragmentShaderId);
      } catch (Exception e) {
        System.err.println("Could not initialize shaders.\n" + e.getMessage());
      }
    }
    
    // Link shader program and detach shaders from RAM
    glLinkProgram(shaderProgramId);
    if (glGetProgrami(shaderProgramId, GL_LINK_STATUS) == 0) throw new Exception("Error linking shader");
    glDetachShader(shaderProgramId, vertexShaderId);
    glDetachShader(shaderProgramId, fragmentShaderId);
  
    // Validate shaders
    glValidateProgram(shaderProgramId);
    if (glGetProgrami(shaderProgramId, GL_VALIDATE_STATUS) == 0) {
      System.err.println("Failed to validate shaders");
    }
    
    // Prepare and use shader program
    glBindFragDataLocation(shaderProgramId, 0, "outColor");
    glUseProgram(shaderProgramId);
    
    /* VAO */
    vaoId = glGenVertexArrays();
    glBindVertexArray(vaoId);
    
    /* VBO */
    float[] triangleInput = {
      // x     y   R   G   B
      0.5f, 0.9f, 1f, 0f, 0f,  // Point A
      0.1f, 0.1f, 1f, 0f, 0f,  // Point B
      0.9f, 0.1f, 1f, 0f, 0f   // Point C
    };
    MemoryStack stack = MemoryStack.stackPush();
    FloatBuffer vertices = stack.mallocFloat(triangleInput.length);
    for (float val : triangleInput) {
      vertices.put(val);
    }
    vertices.flip();
    
    // Bind VBO
    vboId = glGenBuffers();
    glBindBuffer(GL_ARRAY_BUFFER, vboId);
    
    // Upload vertices to the GPU
    glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
    MemoryStack.stackPop();
  
    // Specify vertex attributes
    int positionAttribute = glGetAttribLocation(shaderProgramId, "position");
    glEnableVertexAttribArray(positionAttribute);
    glVertexAttribPointer(positionAttribute, 2, GL_FLOAT, false, 0, 0);
    int colorAttribute = glGetAttribLocation(shaderProgramId, "color");
    glEnableVertexAttribArray(colorAttribute);
    glVertexAttribPointer(colorAttribute, 3, GL_FLOAT, false, 0, 2 * Float.BYTES);
    
    /* Draw the triangle */
    glClear(GL_COLOR_BUFFER_BIT);
    glDrawArrays(GL_TRIANGLES, 0, 3);
  
    /* Free resources */
    glDeleteBuffers(vboId);
    glDeleteVertexArrays(vaoId);
    if (vertexShaderId != 0) glDeleteShader(vertexShaderId);
    if (fragmentShaderId != 0) glDeleteShader(fragmentShaderId);
    glUseProgram(0);
    if (shaderProgramId != 0) glDeleteProgram(shaderProgramId);
  }
  
}


Here you can see the Test Class with syntax highlighting:
https://github.com/stuck1a/deadzone/blob/main/src/deadzone/TestForOpenGL.java
#23
OpenGL / [SOLVED] First steps with LWJG...
Last post by stuck1a - March 23, 2024, 18:33:23
Hello everyone,

I'm actually a web developer, so I haven't had much to do with OpenGL yet.
But I want to get into game development and because I already know Java, it was obvious for me to make my first attempts with LWJGL.

I have implemented the basic structure for my game project and am now trying to render a triangle with the help of thistutorial. No exception is thrown during execution, but nothing is rendered either. The tutorial mentions that you have to bind the VertexBufferObjects to a VertexArrayObject, but unfortunately the tutorial does not explain how this is done. The linked repository differs greatly from the tutorial, so I cannot derive this from it.
Since I'm stuck at this point for two days now, I hope someone here can help me out.


I've shortened the code as good as possible. In my render loop, I simply call drawTriangle() once.
The renderer object is created once on game initialization. As you can see, it will compile and bind the shaders.

Code: Class Renderer
public class Renderer {
  protected ShaderProgram shaders;
  ArrayList<VertexBufferObject> registeredVboObjects = new ArrayList<>();
  ArrayList<VertexArrayObject> registeredVaoObjects = new ArrayList<>();

  public Renderer() {
    if (shaders == null) {
      try {
        shaders = new ShaderProgram();
        shaders.initializeBaseShaders();
        shaders.bind();
      } catch (Exception e) {
        System.err.println("Could not initialize shaders.\n" + e.getMessage());
        return;
      }
    }
  }

  private void drawTriangle() {
    VertexArrayObject vaoTriangle1 = new VertexArrayObject();
    vaoTriangle1.initialize();

    VertexBufferObject vboTriangle1 = new VertexBufferObject(
      new float[]{
        0.5f, 1.0f, 0.0f, 1f, 0f, 0f,
        0.0f, 0.0f, 0.0f, 0f, 1f, 0f,
        1.0f, 0.0f, 0.0f, 0f, 0f, 1f
      }
    );
    vboTriangle1.initialize();
    
    // TODO: Bind the VBO to the VAO ???
    
    // Register all objects related to the triangle
    registeredVboObjects.add(vboTriangle1);
    registeredVaoObjects.add(vaoTriangle1);
    
    // Draw the Triangle
    vboTriangle1.render();
  }
}



Code: Class ShaderProgram
public class ShaderProgram {
  private final int programId;
  private int vertexShaderId;
  private int fragmentShaderId;
  
  public ShaderProgram() throws Exception {
    programId = glCreateProgram();
    if (programId == 0) {
      throw new Exception("Could not create Shader");
    }
  }
  
  public int getProgramId() {
    return programId;
  }
  
  public int getVertexShaderId() {
    return vertexShaderId;
  }
  
  public int getFragmentShaderId() {
    return fragmentShaderId;
  }
  
  public void bind() {
    glUseProgram(programId);
  }
  
  public void unbind() {
    glUseProgram(0);
  }
  
  public void link() throws Exception {
    glLinkProgram(programId);
    if (glGetProgrami(programId, GL_LINK_STATUS) == 0) {
      throw new Exception("Error linking Shader code: " + glGetProgramInfoLog(programId, 1024));
    }
    if (vertexShaderId != 0) glDetachShader(programId, vertexShaderId);
    if (fragmentShaderId != 0) glDetachShader(programId, fragmentShaderId);
  }
  
  public void initializeBaseShaders() throws Exception {
    String shadersDir = Util.getShadersDir();
    String vertexShaderCode = Util.readFullFile(shadersDir + "vertex.glsl");
    vertexShaderId = compileShader(vertexShaderCode, GL_VERTEX_SHADER);
    String fragmentShaderCode = Util.readFullFile(shadersDir + "fragment.glsl");
    fragmentShaderId = compileShader(fragmentShaderCode, GL_FRAGMENT_SHADER);
    link();
  }
  
  public int compileShader(String shaderCode, int shaderType) throws Exception {
    int shaderId = glCreateShader(shaderType);
    if (shaderId == 0) throw new RuntimeException("Error creating shader. Type: " + shaderType);
    glShaderSource(shaderId, shaderCode);
    glCompileShader(shaderId);
    if (glGetShaderi(shaderId, GL_COMPILE_STATUS) == 0) {
      throw new RuntimeException("Error compiling Shader code: " + glGetShaderInfoLog(shaderId, 1024));
    }
    glAttachShader(programId, shaderId);
    return shaderId;
  }
}



Code: Class VertexArrayObject
public class VertexArrayObject {
  private final int vaoId;
  
  public VertexArrayObject() {
    vaoId = glGenVertexArrays();
  }
  
  public void initialize() {
    bind();
  }
  
  public int getID() {
    return vaoId;
  }
  
  public void delete() {
    glDeleteVertexArrays(vaoId);
  }
  
  private void bind() {
    glBindVertexArray(vaoId);
  }
}



Code: Class VertexBufferObject
public class VertexBufferObject {
  private int vboId;
  MemoryStack stack;
  FloatBuffer vertices;
  
  public VertexBufferObject(float[] vertexData) {
    stack = MemoryStack.stackPush();
    vertices = stack.mallocFloat(vertexData.length);
    for (float val : vertexData) {
      vertices.put(val);
    }
    vertices.flip();
  }
  
  public void initialize() {
    vboId = glGenBuffers();
    bind(GL_ARRAY_BUFFER);
    uploadData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
    MemoryStack.stackPop();
    specifyVertexAttributes();
  }
  
  public void render() {
    glClear(GL_COLOR_BUFFER_BIT);
    glDrawArrays(GL_TRIANGLES, 0, 3);
  }
  
  public void delete() {
    glDeleteBuffers(vboId);
  }
  
  public int getID() {
    return vboId;
  }
  
  private void bind(int target) {
    glBindBuffer(target, vboId);
  }
  
  private void uploadData(int target, FloatBuffer data, int usage) {
    glBufferData(target, data, usage);
  }
  
  private void specifyVertexAttributes() {
    int shaderProgram = Deadzone.getApplication().getRenderer().getShaders().getProgramId();
    // position attribute
    int positionAttribute = glGetAttribLocation(shaderProgram, "position");
    glEnableVertexAttribArray(positionAttribute);
    glVertexAttribPointer(positionAttribute, 3, GL_FLOAT, false, 6 * Float.BYTES, 0);
    // color attribute
    int colorAttribute = glGetAttribLocation(shaderProgram, "color");
    glEnableVertexAttribArray(colorAttribute);
    glVertexAttribPointer(colorAttribute, 3, GL_FLOAT, false, 6 * Float.BYTES, 3 * Float.BYTES);
  }
}




Those are my shaders:

Code: vertex.glsl
#version 150 core

in vec2 position;
in vec3 color;

out vec3 vertexColor;

void main() {
  vertexColor = color;
  gl_Position = vec3(position, 1.0);
}



Code: fragment.glsl
#version 150 core

in vec3 vertexColor;

out vec4 outColor;

void main() {
  outColor = vec4(vertexColor, 1.0);
}



Perhaps somone here can help me and explain what is missing or what I am doing wrong?
The complete project repository can be viewed here.

Many thanks in advance and best regards,
stuck1a
#24
Lightweight Java Gaming Library / Re: Multisampling in OpenXR
Last post by Richtea - March 16, 2024, 21:23:01
I understand now I think. I need to render to a seperate frame buffer (that supports multisampling) then blit into a second frame buffers for OpenXR to use as its swapchain image
#25
Bug Reports / RFE / Re: [BUG] Rare and Random cras...
Last post by Lankyware - March 09, 2024, 13:02:34
I was able to stop the crashes. There were two different animation effects being batch rendered together, I split it into 2 separate draw calls and that got rid of the crashes for some reason.
#26
Lightweight Java Gaming Library / Re: Multisampling in OpenXR
Last post by Richtea - March 09, 2024, 11:56:34
edit; sorry, some how managed to quote reply myself rather than editing  :-\ Please ignore this reply
#27
Lightweight Java Gaming Library / Multisampling in OpenXR
Last post by Richtea - March 08, 2024, 12:07:26
I've been looking into trying to get MSAA antialiasing working within an OpenXR VR application.

I'm looking at how the swapchain images get created

                XrSwapchainCreateInfo swapchainCreateInfo = XrSwapchainCreateInfo.malloc(stack)
                        .type$Default()
                        .next(NULL)
                        .createFlags(0)
                        .usageFlags(XR10.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR10.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT)
                        .format(glColorFormat)
                        .sampleCount(viewConfig.recommendedSwapchainSampleCount()) //<--- interesting
                        .width(viewConfig.recommendedImageRectWidth())
                        .height(viewConfig.recommendedImageRectHeight())
                        .faceCount(1)
                        .arraySize(1)
                        .mipCount(1);

                PointerBuffer swapchainHanglePointerBuffer = stack.mallocPointer(1);
                checkResponseCode(XR10.xrCreateSwapchain(xrSession, swapchainCreateInfo, swapchainHanglePointerBuffer));

And looking at where recommendedSwapchainSampleCount comes from it is from asking the runtime for its opinion

            checkResponseCode(XR10.xrEnumerateViewConfigurationViews(xrInstance, systemID, viewConfigType, viewCountPointer, null));
            viewConfigs = XrUtils.fill(
                    XrViewConfigurationView.calloc(viewCountPointer.get(0)), // use calloc() rather than malloc() to ensure the next field is correctly initialized
                    XrViewConfigurationView.TYPE,
                    XR10.XR_TYPE_VIEW_CONFIGURATION_VIEW
            );

            checkResponseCode(XR10.xrEnumerateViewConfigurationViews(xrInstance, systemID, viewConfigType, viewCountPointer, viewConfigs));

And for my system `viewConfig.recommendedSwapchainSampleCount()` returns 1 (And setting anything else gives me a XR_ERROR_RUNTIME_FAILURE error code). But I don't understand why it's 1. My system aught to be able to support more than that (Its all done locally on the PC right, the headsets not doing the sampling right?); in non VR contexts antialiasing all works fine. (viewConfig.maxSwapchainSampleCount() also returns 1)

My system is:
Oculus Quest 2 running over Virtual Desktop
Windows 10
NVidea GeForce RTX 3070


Am I on the right track here? Any ideas as to how I can get MSAA  antialiasing working?
#28
Bug Reports / RFE / Re: [BUG] Rare and Random cras...
Last post by Lankyware - March 07, 2024, 23:09:43
Oh, I stopped using ByteBuffers. When I call glBufferData() I put in an array of floats. IIRC I did this because for some things in my game, I put new data in before every draw call, and using Buffer objects for that made it GC more often, causing microlags.
#29
Bug Reports / RFE / Re: [BUG] Rare and Random cras...
Last post by spasi - March 07, 2024, 18:16:43
Hey Lankyware,

Does it happen much earlier if you add System.gc() calls (say, once per frame) in your application? It would indicate that a direct ByteBuffer that is still in use is being garbage-collected, and its associated native memory freed, by mistake. Note that a major issue with GC-managed ByteBuffers (allocated with ByteBuffer.allocateDirect or BufferUtils in LWJGL) is that they are not freed unless full GC cycles happen. The JDK uses Cleaner for this, which is a PhantomReference. Weak/soft/phantom reference processing happens during full GC pauses with most GCs. Such pauses are kind of rare on modern JVMs and that can explain unpredictable crashes at a later time.

I hope this helps.
#30
Bug Reports / RFE / [BUG] Rare and Random crashes ...
Last post by Lankyware - March 07, 2024, 12:46:46
Ok so in my indie game, the game will randomly crash like once every 3 hours or so on average. It gives the message: A fatal error has been detected by the Java Runtime Environment. And it makes a log file. I've looked into it a lot and haven't been able to find out what causes this. This article is the closest I've found to an answer: https://inside.java/2020/12/03/crash-outside-the-jvm/
Error log file: https://gist.github.com/dfu1/253cc2f51b372027109dc99604b2c4ed
I've taken a look in the log file, and in the Java frames part it shows there was a call of org.lwjgl.opengl.GL11C.glDrawArrays(III)V.
And in the Internal exceptions, it says there's a java/lang/NoSuchMethodError exception.
I cannot figure out what the cause is, and I don't understand why it would be happening so rarely and randomly.
Any explanations/ideas are welcome.