Once again it’s been quite a while between posts. I have actually been working though – it turns out that my switch to a packed array object pool was far from over. I’m surprised that I had anything at all by my last blog post that behaved in a similar way to the game pre-packed array. So I wanted to untangle this mess before making a new blog post.
While I was trying to fix this, I discovered a meetup near me where a bunch of female coders get together to work in a pub/cafe. A bit of company while I worked on my frustrating spaghetti code sounded nice, so I set up my laptop with all the tools I needed, downloaded the source from github and off I went.
Up until that point I’d been developing this only on my home desktop PC. Surprise, surprise, when I tried to run my project on my laptop all I got was a black screen. Great. Thus commenced a week-long (I was still working at my day job at this point) journey into working out why nothing was drawing.
So I started by having a look to see if OpenGL was giving me any errors. Which meant I ended up calling glGetError after every OpenGL call. I should really have written this blog post closer to the time of these errors (got distracted by GDC), but I can see I tweeted that I was receiving a GL_INVALID_OPERATION error after my call to glDrawElements. Which at least seemed to narrow things down, but wasn’t super helpful.
So, this helpful tweet led me down the path of trying to get some more verbose info out of my error. This apparently gave me some more useful error messages, but since I didn’t write them down I can’t post them here. Still no dice with getting anything to draw though.
I then looked through this awesome presentation by Elizabeth Baumel. I think I’d already done quite a few steps on her list, and I found myself up to the “Frame Capture and Analysis” slide. Alongside that, the creator of RenderDoc, Baldur Karlsson had appeared in my Twitter mentions with offers of help (Twitter is sometimes really amazing).
So I downloaded RenderDoc and started to work out how to use it. RenderDoc does not work with compatibility mode OpenGL (basically pre OpenGL 3.2). I had thought I was using modern OpenGL, but it turns out that wasn’t the case. You really need to explicitly specify that you only want to create a core context for that to be the case. After quite a lot of faffing about I worked out the correct code to do this was:
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); window = glfwCreateWindow(800, 600, "Practice", NULL, NULL);
Once that was done I discovered I quickly got errors about vertex array objects (VAO). The compatibility profile had just been creating a default VAO for me, thus I’d never even really realised that I even needed one. Right, so created my own VAO that I also needed to pass around to my drawable objects and Sprite components, hooray. Now I no longer received that error, but I was still not seeing anything on screen.
OK, so it was time to actually learn how to use RenderDoc to get some more info about what was going on. Baumel’s presentation had a pretty decent walkthrough, and after an evening of staring at each of the different windows in RenderDoc, I started to realise that the vertex shader input looked a bit off. I spent quite a bit of time calculating by hand what it should look like (so glad I took those linear algebra classes at university), and then decided the values were definitely wrong.
Eventually I cottoned on to the fact that the inputs were just out of order. The correct numbers were being sent from my C++ code, but when we get into the GLSL they were being sent to the incorrect variables. This was because I had not used any location specifiers in my vertex shader.
layout (location = 0) in vec4 vertexPosition; layout (location = 1) in vec4 vertexColour; layout (location = 2) in vec2 vertexTexCoord;
So, why did this work on my desktop PC and not my laptop? Shader compilers don’t have any specification for assigning locations to parameters if not specified by the programmer. So the shader compiler on my desktop was assigning locations seemingly in the order I’d declared them (no idea if this was by design or chance), while the shader compiler on my laptop was assigning locations in a different order.
Once I had added these location specifiers… yes! It worked!
While frustrating, I am unlikely to make this particular mistake again. Also I learned a lot about the differences between core and compatibility OpenGL, and also the necessity of VAOs. AND how to use RenderDoc to debug things. So, not the worst bug I’d ever encountered.
I have also mostly untangled the original mess I created with my packed array object pools, but I’ll leave that for another blog post. You can also take a sneak peek by having a look at the current source on github.