GL.createCapabilities() leaking memory

Started by maxhawaii, January 22, 2019, 15:10:27

Previous topic - Next topic

maxhawaii

Hi,

First of all, thank you for a great and powerful library!

I turned on
Configuration.DEBUG_MEMORY_ALLOCATOR.set(true);
and it is reporting a memory leak related to the GLCapabilities object returned by GL.createCapabilities().

Quote
[LWJGL] 1872 bytes leaked, thread 1 (main), address: 0x7FA9341B7800
   at org.lwjgl.system.ThreadLocalUtil.setEnv(ThreadLocalUtil.java:164)
   at org.lwjgl.opengl.GL.setCapabilities(GL.java:242)
   at org.lwjgl.opengl.GL.createCapabilities(GL.java:467)
   at org.lwjgl.opengl.GL.createCapabilities(GL.java:321)
   at dk.kollision.motor.misc.Window.createWindow(Window.java:52)
   at dk.kollision.motor.Main.main(Main.java:81)

However, I haven't been able to find any information on how or when to free this object. What am I missing?

Here's the code for my Window class:

package dk.kollision.motor.misc;

import dk.kollision.motor.Main;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GLCapabilities;

import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.glClearColor;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL20.GL_POINT_SPRITE;
import static org.lwjgl.opengl.GL20.GL_VERTEX_PROGRAM_POINT_SIZE;
import static org.lwjgl.system.MemoryUtil.NULL;

public class Window {

    private int id;
    public long handle;

    public int width, height;
    public int framebufferWidth, framebufferHeight;

    private GLFWKeyCallbackI keyCallback;
    private GLFWCursorPosCallbackI cursorPosCallback;
    private GLFWMouseButtonCallbackI mouseButtonCallback;
    private GLFWWindowSizeCallbackI windowSizeCallback;
    private GLFWFramebufferSizeCallbackI framebufferSizeCallback;

    private GLCapabilities capabilities;

    public void createWindow(int id, int width, int height, long pointerToMonitor, long pointerToFirstWindow){
        this.id = id;
        this.width = width;
        this.height = height;

        glfwDefaultWindowHints();
        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
        glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
        //glfwWindowHint(GLFW_CONTEXT_RELEASE_BEHAVIOR, GLFW_RELEASE_BEHAVIOR_NONE);

        handle = glfwCreateWindow(this.width, this.height, "House Of Green", pointerToMonitor, pointerToFirstWindow);
        if ( handle == NULL )
            throw new RuntimeException("Failed to create the GLFW window");


        glfwSetInputMode(handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

        glfwMakeContextCurrent(handle);

        capabilities = GL.createCapabilities();

        glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
        glEnable(GL_TEXTURE_2D);
        glEnable(GL_POINT_SPRITE);
        glClearColor(0.0f, 0.0f, 0.0f, 0);

        glfwSwapInterval(1);

        fetchFramebufferDimensions();

        glfwShowWindow(handle);
    }

    public void focus(){
        glfwFocusWindow(handle);
    }

    public void hideWindow(){
        glfwHideWindow(handle);
    }

    public void destroyWindow(){
        glfwFreeCallbacks(handle);
        glfwDestroyWindow(handle);
        GL.destroy();
    }

    public void registerCallbacks(){
        keyCallback = (window, key, scancode, action, mods) -> {
            if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE ) {
                glfwSetWindowShouldClose(window, true);
            } else {
                boolean shift = (mods & GLFW_MOD_SHIFT) != 0;
                boolean alt = (mods & GLFW_MOD_ALT) != 0;
                boolean ctrl = (mods & GLFW_MOD_CONTROL) != 0;

                if(action == GLFW_RELEASE){
                    Main.onKeyReleased(window, key, action, shift, alt, ctrl);
                } else if(action == GLFW_PRESS) {
                    Main.onKeyPressed(window, key, action, shift, alt, ctrl);
                }
            }
        };
        glfwSetKeyCallback(handle, keyCallback);

        cursorPosCallback = (window, xpos, ypos) -> {
            double mouseX = xpos / width;
            double mouseY = ypos / height;
            Main.onMouseMoved(id, mouseX, mouseY);
        };
        glfwSetCursorPosCallback(handle, cursorPosCallback);

        mouseButtonCallback = (window, button, action, mods) -> {
            boolean mousePressed = (button == GLFW_MOUSE_BUTTON_1) && (action == GLFW_PRESS);
            if(mousePressed){
                Main.onMousePressed();
            } else {
                Main.onMouseReleased();
            }
        };
        glfwSetMouseButtonCallback(handle, mouseButtonCallback);

        windowSizeCallback = (window, w, h) -> {
            width = w;
            height = h;
        };
        glfwSetWindowSizeCallback(handle, windowSizeCallback);

        framebufferSizeCallback = (window, w, h) -> {
            framebufferWidth = w;
            framebufferHeight = h;
        };
        glfwSetFramebufferSizeCallback(handle, framebufferSizeCallback);
    }

    public void prepareRendering(){

        glfwMakeContextCurrent(handle);

        GL.setCapabilities(capabilities);
    }

    public void fetchFramebufferDimensions(){

        int[] widthBuffer = new int[1];
        int[] heightBuffer = new int[1];

        glfwMakeContextCurrent(handle);

        glfwGetFramebufferSize(handle, widthBuffer, heightBuffer);

        framebufferWidth = widthBuffer[0];
        framebufferHeight = heightBuffer[0];
    }
}


Sincerely,

spasi

Hey maxhawaii,

You need to call GL.setCapabilities(null) when the current OpenGL context of a thread is cleared, which happens when you call glfwMakeContextCurrent(NULL) or glfwDestroyWindow(handle).

maxhawaii

Quote from: spasi on January 23, 2019, 09:40:10
Hey maxhawaii,

You need to call GL.setCapabilities(null) when the current OpenGL context of a thread is cleared, which happens when you call glfwMakeContextCurrent(NULL) or glfwDestroyWindow(handle).

Hi spasi, thank you for your quick reply -- that solved it!