3 Basic Questions about the Render pipeline -> my project stopped working (Java)

Good day all you beautiful people,
About a week ago, i did a major refractor in my main project, trying to create abstract java classes for all my renderers and shaders, so i wouldn’t have to do this all over and over again for everything.
(Terrain, Entities, Clouds, Water, UI, …)

So, I very stupidly spent a whole day redoing all the renderers without debugging at any stage… Well, of course it stopped working and i only got part of my UI renderer to work, so I decided to try and
create a small project to test all my render pipeline stuff out safely

(Source Code: )

//The main method just calls the GameLoop() constructor
 
//GameLoop.java:
 
public class GameLoop {
    
    Display dm;
    Renderer renderer;
    
    public GameLoop() {
        init();
        loop();
        cleanUp();
    }
    
    private void init() {
        dm = new Display();
        renderer = new Renderer();
    }
    
    private void loop() {
        while(!dm.shouldClose()) {
            dm.render();
            renderer.render();
        }
    }
    
    private void cleanUp() {
        dm.close();
        renderer.cleanUp();
        dm.destroy();
    }
    
}
 
///////////////////////////////////////
//Display.java:     (Straight from the lwjgl.com website, so this class SHOULD be working correctly)
 
public class Display {
    
    public static int WIDTH = 1080, HEIGHT = 720;
    public static final float NEAR_PLANE = 0.1f, FAR_PLANE = 1000;
    public static final float FOV = 70;
    private static final String TITLE = "MPS";
    private static final Vector3f bgColor = new Vector3f(0.4f, 0.8f, 0.78f);
    
    public static long window = -1l;
    
    public Display() {
        System.out.println("Starting " + TITLE + ",,,,");
        
        init();
        GL.createCapabilities();
        
        GL11.glViewport(0, 0, WIDTH, HEIGHT);
        GLFW.glfwSetFramebufferSizeCallback(window, (long window, int width, int height) -> {
            GL11.glViewport(0, 0, width, height);
            Display.WIDTH = width; Display.HEIGHT = height;
        });
        
        GL43.glEnable(GL43.GL_DEBUG_OUTPUT);
        
        GL11.glClearColor(bgColor.x, bgColor.y, bgColor.z, 0);
    }
    
    
    
    
    private void init() {
        // Setup an error callback. The default implementation
        // will print the error message in System.err.
        GLFWErrorCallback.createPrint(System.err).set();
 
        // Initialize GLFW. Most GLFW functions will not work before doing this.
        if ( !GLFW.glfwInit() )
            throw new IllegalStateException("Unable to initialize GLFW");
 
        // Configure GLFW
        GLFW.glfwDefaultWindowHints(); // optional, the current window hints are already the default
        GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE); // the window will stay hidden after creation
        GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE); // the window will be resizable
        
        // Create the window
        window = GLFW.glfwCreateWindow(WIDTH, HEIGHT, TITLE, 0, 0);
        if ( window == -1l )
            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.
        GLFW.glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
            if ( key == GLFW.GLFW_KEY_TAB && action == GLFW.GLFW_RELEASE )
                GLFW.glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
            
            
            //[a bunch of callbacks to set the pressed keys in my static Controls class]
            
            
            if(key == GLFW.GLFW_KEY_ESCAPE && action == GLFW.GLFW_PRESS) {
                Controls.helper = true;
            } if(key == GLFW.GLFW_KEY_ESCAPE && action == GLFW.GLFW_RELEASE) {
                if(Controls.inMenu && Controls.helper) { Controls.helper = Controls.inMenu = false; }
                if(!Controls.inMenu && Controls.helper) { Controls.helper = false; Controls.inMenu = true; }
            }
            
        });
        
        GLFW.glfwSetCursorPosCallback(window, (window, xpos, ypos) -> {
            Controls.mX = xpos;
            Controls.mY = ypos;
        });
 
        // Get the thread stack and push a new frame
        try ( MemoryStack stack = MemoryStack.stackPush() ) {
            IntBuffer pWidth = stack.mallocInt(1); // int*
            IntBuffer pHeight = stack.mallocInt(1); // int*
 
            // Get the window size passed to glfwCreateWindow
            GLFW.glfwGetWindowSize(window, pWidth, pHeight);
 
            // Get the resolution of the primary monitor
            GLFWVidMode vidmode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor());
 
            // Center the window
            GLFW.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
        GLFW.glfwMakeContextCurrent(window);
        // Enable v-sync
        GLFW.glfwSwapInterval(1);
 
        // Make the window visible
        GLFW.glfwShowWindow(window);
    }
    
    public void render() {
        GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_COLOR_BUFFER_BIT);
        GLFW.glfwSwapBuffers(window);
        GLFW.glfwPollEvents();
        doCursorStuff();
    }
    
    
    
    public void doCursorStuff() {
        if(!Controls.inMenu) {
            GLFW.glfwSetInputMode(window, GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED);
        } else {
            GLFW.glfwSetInputMode(window, GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL);
        }
    }
    
    
    
    
    public boolean shouldClose() { return GLFW.glfwWindowShouldClose(window); }
    
    public void close() {
        GLFW.glfwSetWindowShouldClose(window, true);
    }
    
    public void destroy() {
        // Free the window callbacks and destroy the window
        //GLFW.glfwFreeCallbacks(window);
        GLFW.glfwDestroyWindow(window);
 
        // Terminate GLFW and free the error callback
        GLFW.glfwTerminate();
        GLFW.glfwSetErrorCallback(null).free();
    }
    
}
 
//////////////////////////////////////////////////
//Renderer.java     (This is where i suspect the error is -> perhaps something about the model loading/ binding the buffers)
//                  (All the unbind calls have been commented out to ensure that they're not responsible)
 
public class Renderer {
    
    private List<Integer> bfers = new ArrayList<Integer>();
    private List<Integer> textures = new ArrayList<Integer>();
    
    float vertices[] = {
            -0.5f, -0.5f, 0.0f,
             0.5f, -0.5f, 0.0f,
             0.0f,  0.5f, 0.0f
        }; 
    
    protected GeometryShader shader;
    protected Model model;
    
    public Renderer() {
        this.shader = new GeometryShader();
        this.model = loadModel(vertices);
    }
    
    private Model loadModel(float[] vertices) {
        int vao = createVAO();
        storeDataInAttributeList(vao, 0, 3, vertices, true);
        //GL30.glBindVertexArray(0);
        return new Model(vao, vertices.length/3, true);
    }
    
    public void render() {
        shader.start();
        GL30.glBindVertexArray(model.vaoID());
        GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, model.vertexCount());
        //GL30.glBindVertexArray(0);
        shader.stop();
    }
    
    /**
     * Creates and binds VAO
     */
    
    private int createVAO() {
        int vaoID = GL30.glGenVertexArrays();
        bfers.add(vaoID);
        GL30.glBindVertexArray(vaoID);
        return vaoID;
        //GL20.glBufferData(GL20.GL_ARRAY_BUFFER, null, st);
    }
    
    private int createVBO() {
        int vboID = GL30.glGenBuffers();
        bfers.add(vboID);
        GL20.glBindBuffer(GL20.GL_ARRAY_BUFFER, vboID);
        return vboID;
    }
    
    private void storeDataInAttributeList(int vaoID, int attribNumber, int coordSize, float[] data, boolean staticDraw) {
        int vbo = createVBO();
        //GL20.glBindBuffer(GL20.GL_ARRAY_BUFFER, vbo);
        GL20.glBufferData(GL20.GL_ARRAY_BUFFER, storeDataInFloatBuffer(data), staticDraw ? GL20.GL_STATIC_DRAW : GL20.GL_DYNAMIC_DRAW);
        GL20.glVertexAttribPointer(attribNumber, coordSize, GL11.GL_FLOAT, false, 0, 0);
        GL20.glEnableVertexAttribArray(attribNumber);
        //GL20.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);
    }
    
    private FloatBuffer storeDataInFloatBuffer(float[] data) {
        FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length);
        buffer.put(data);
        buffer.flip();  //write mode -> read mode
        return buffer;
    }
    
    public void cleanUp() {
        for(int i : bfers)
            GL30.glDeleteVertexArrays(i);
        for(int i : textures)
            GL15.glDeleteTextures(i);
    }
    
}
 
///////////////////////////
//vertexShader
#version 330 core
 
layout (location = 0) in vec3 position;
 
void main(void) {
    gl_Position = vec4(position, 1);
}
 
////////////////////////////
//fragmentShader
 
#version 330 core
 
out vec4 out_Color;
 
void main(void) {
    out_Color = vec4(1, 0, 0, 1);
}

/////////////////////
//GeometryShader.java
public class GeometryShader {
	
	private static final String VERTEX_FILE = "src/main/java/de/magiczerda/geometry/impl_renderer/geometry_vertexShader.glsl";
	private static final String GEOMETRY_FILE = "src/main/java/de/magiczerda/geometry/impl_renderer/geometry_geometryShader.glsl";
	private static final String FRAGMENT_FILE = "src/main/java/de/magiczerda/geometry/impl_renderer/geometry_fragmentShader.glsl";
	
	protected int vsID = -1, gsID = -1, fsID = -1, pID = -1;
	
	public GeometryShader() {
		this.vsID = loadShader(VERTEX_FILE, GL20.GL_VERTEX_SHADER);
		//this.gsID = loadShader(GEOMETRY_FILE, GL40.GL_GEOMETRY_SHADER);
		this.fsID = loadShader(FRAGMENT_FILE, GL20.GL_FRAGMENT_SHADER);
		
		this.pID = GL20.glCreateProgram();
		GL20.glAttachShader(pID, vsID);
		//GL20.glAttachShader(pID, gsID);
		GL20.glAttachShader(pID, fsID);
		GL20.glValidateProgram(pID);
		
		GL20.glLinkProgram(pID);
		
		int ls = GL20.glGetProgrami(pID, GL20.GL_LINK_STATUS);
		System.out.println("Shader Link status: " + ls);      //1
		if(ls == GL11.GL_FALSE) {
			String info = GL20.glGetProgramInfoLog(pID);
			System.out.println(info);
			GL20.glDeleteProgram(pID);
		}
		
		/*start();
		bindAttributes();
		stop();*/
	}
	
	public void start() {
		GL20.glUseProgram(pID);
	}
	
	public void stop() {
		GL20.glUseProgram(0);
	}
	
	/*protected void bindAttribute(int attribute, String variableName) {
		GL20.glBindAttribLocation(pID, attribute, variableName);
	}
	
	protected void bindAttributes() {
		for(String s : new String[] { "0 position" })
			bindAttribute(Integer.parseInt(s.split(" ")[0]), s.split(" ")[1]);
	}*/
	
	private int loadShader(String file, int type) {
		StringBuilder shaderSource = new StringBuilder();
		try {
			BufferedReader reader = new BufferedReader(new FileReader(file));
			String line;
			while ((line = reader.readLine()) != null) {
				shaderSource.append(line).append("//\n");
			}
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(-1);
		}
		int shaderID = GL20.glCreateShader(type);
		GL20.glShaderSource(shaderID, shaderSource);
		//System.out.println(shaderSource);
		GL20.glCompileShader(shaderID);
		if (GL20.glGetShaderi(shaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
			System.err.println("Could not compile shader!");
			System.out.println(GL20.glGetShaderInfoLog(shaderID, 5000));
			System.exit(-1);
		}
		return shaderID;
	}
	
	public void cleanUp() {
		stop();	//stop the shaders
		GL20.glDetachShader(pID, vsID);	//detach the vertex shader from the shader program
		if(gsID != -1)
			GL20.glDetachShader(pID, gsID);
		GL20.glDetachShader(pID, fsID);	//detach the fragment shader from the shader program
		GL20.glDeleteProgram(vsID);	//delete the vertex shader
		if(gsID != -1)
			GL20.glDeleteShader(gsID);
		GL20.glDeleteShader(fsID);	//delete the fragment shader
		GL20.glDeleteProgram(pID);		//delete the shader program
	}
}

GLFW and OpenGL initialization is successful
(no i didn’t forget the glfwMakeContextCurrent() or createCapabilities() method calls)
The shaders are compiled correctly, the shader program id is never -1 (as specified)
And the vaoID is set as well, but the triangle never shows up. (Neither with GL_POINTS, GL_LINES or any of the STRIP variants)
By exclusion, i think the mistake must be occurring while i am loading the model, but I really don’t know for sure. OpenGL does not give me any error messages either, no log is created, no nothing at all

I got three questions about all this:

1.: This is how I understand the process:
1.1. Init GLFW & OpenGL Context
1.2. Compile the shaders
1.3. Load the model (2-3 order should not matter)
1.4. Start the render loop
=> glfwPollEvents(), glfwSwapBuffers(),
start shader, (send uniform data), render, stop shader
1.5. Clean all shaders, vaos, vbos, fbos and textures up

=> Did i understand this wrong or is my implementation wrong?

Does the shader only need to be started once or does it need to be started and stopped per render call?
I suppose the latter is only true when you have multiple shader stages to deal with

Most importantly though, what am i doing wrong here? I honestly don’t think i can possibly find the answer myself because I’ve been redoing this over and over again and at this point, the error must be completely engrained into my brain ^^

I’m feeling real stupid right now because I’ve set up a bunch of lwjgl projects, but i guess this is just dunning-kruger hitting me with full force and shoving me into the valley of despair (which is a good thing)

Thank y’all for your time and patience with me ^^