Communicating with pooled objects 2

So, my last post on this topic talked about adapting my component pool into a more generic packed-array object pool. It turns out that my implementation of this was absolutely littered with bugs. So many bugs that I’m surprised the game functioned at all, let alone in a remotely similar way to how it worked before.

The main reason for all the bugs was that I was still letting the object pool return a pointer to the actual new object that was just created. And I had objects holding on to this pointer, all the while the object pool was rearranging memory every time anything was deleted. This led, of course, to objects pointing to and manipulating incorrect data.

So the first thing to fix was making sure the Create function would return a handle rather than a pointer. But what to return when the object pool is full? I had been returning a nullptr. My initial fix for this had been to return the capacity of the pool, but of course that number is still a valid handle, and this caused a couple of tricky bugs that took me a while to track down. So I am now returning 0, since that is never a valid handle (unless the unsigned int variable overflows, but then I’ve got other problems..)

However I decided that Components still do need to store an actual pointer to their owning GameObjects. So if that GameObject moves as part of an object pool rearrangement, I need to fix the pointers in each of the Components. In order to do this I created a “post move” function for anything that can be pooled (hmm, should I make things inherit from a Poolable abstract class to make sure people don’t leave out the post move function? Or would the multiple inheritance become too much, since many of these things will also be Subjects for the Observer pattern too?) Anyway, in this post move function I simply loop through the component list for a game object and call the “SetGameObject” function from Component.

Once I’d pooled my bullets, I realised that the Init function wasn’t clearing the Component and Observer lists from the GameObject class. This resulted in a Bullet thinking it had more Observers than it really did, as well as too many Components. I ended up clearing these lists in the Bullet Init, but reading over this now, this should really be done in the GameObject Init and called by Bullet.

It also became obvious that I would need to pass the Object pools around to multiple different places. I ended up creating a “helper” class for this, since that meant I could just pass a pointer to this helper class, rather than needing to pass each individual object pool (my function argument lists had been getting ridiculously long). I tried my best to generalise this helper class, but once again my lack of practice with templates came back to bite me, and I started down a few idiotic paths without getting anywhere. This post on Stack Overflow seems similar to what I wanted, but for now I just hard coded in the 4 different components I’m using. If I start to add more components to my engine I will really need to fix this up properly.

So, once I’d fixed up all these bugs things returned a bit more to normal. I’ve actually done another series of changes since these (I’m behind on blogging, but I have been coding!) but I’ll leave those for another post. As always you can see my code on GitHub.

Advertisements

Location specifiers in vertex shaders

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.