What is a suitable default value for buffers/vertex arrays/textures etc

Started by
27 comments, last by Nanoha 8 years, 4 months ago

I am struggling to choose an appropriate default value to assign for my buffers and so on. For example, I might have a class that has vertex array (unsigned int), what value should I assign by default before it is initialised? 0/-1? Later on I do use these values to check this/these values for sanity checks before using them.

Looking at my last and current project I am mostly using 0 but I see a few -1s popping up. Obviously that's not good. Is the first value that the 'Gen' functions will return 1? That seems to be the case as logically if they did return 0 I would notice the effects from that. -1 (unsigned int wrap around) just seems like a value that works purely based on the fact that it ends up being a very large integer that I am probably unlikely to ever have returned from one of these functions.

So what is the 'correct' value I should be using as default values before calling any of these functions:

glGenTextures

glGenBuffers

glGenFramebuffers

glGenVertexArrays

I realise I could also use glGetError for validation after calling the functions but I still need a default value and I am now confused what to use.

I checked some of the references e.g.

https://www.opengl.org/sdk/docs/man/html/glGenTextures.xhtml

and it doesn't really mention a range for the values returned.

Experimentally I am finding that the first value I get is 1 so 0 seems like the best choice, can anyone confirm this?

The reason I have gotten confused is glGetUniformLocation as a location of 0 is perfectly valid. What is a good default for that, -1?

Thanks.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

Advertisement

I don't think the glGen* functions can fail, since they don't actually do anything with the GPU. They simply generate a name, while any allocation happens at bind/upload time. The reason you can't get 0 back as a name is because 0 is the "name" used to unbind objects.

"So there you have it, ladies and gentlemen: the only API I’ve ever used that requires both elevated privileges and a dedicated user thread just to copy a block of structures from the kernel to the user." - Casey Muratori

boreal.aggydaggy.com

Use -1 because object 0 often has very specific meaning in OpenGL, and may give you unexpected behaviour, especially in compatibility contexts. It's not just for unbinding.

Texture object 0 is the "default" texture; in other words it reproduces the behaviour of GL 1.0 before texture objects were added to the core. You can bind it, you can glTexImage it, you can draw with it.

Buffer object 0 disables buffer objects as input and goes back to system/client memory - for certain types of buffer objects.

VAO 0 disables VAOs and goes back to GL 2/2.1 style drawing.

Program object 0 disables shaders and goes back to the fixed pipeline.

Etc.

To repeat: binding object 0 often has a very specific meaning that goes beyond just unbinding. If you bind object 0 you will get behaviour and side-effects, and if you're not aware of that, you'll have some nice debugging sessions ahead of you.

Defaulting to -1 will on the other hand give you a nice error if you try to use it, so if you're doing error-checking in your program (you are, aren't you) then you'll more easily catch unintended usage of uninitialized objects.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

Careful with this. There is absolutely zero guarantee in the standard that any of the glGen*() functions won't return the value GLuint(-1) as a valid handle. We've seen drivers that allocate texture handles starting at very high numbers before.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Careful with this. There is absolutely zero guarantee in the standard that any of the glGen*() functions won't return the value GLuint(-1) as a valid handle. We've seen drivers that allocate texture handles starting at very high numbers before.

That is actually true as well. In fact the only guarantees are (1) a generated name won't be one that is currently in use, and (2) a generated name won't be 0.

So with the benefit of this additional information, the only safe thing to do is to remove dependencies on a default/uninitialized state actually mattering.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

Thanks for the responses, given me something to think about.

So I guess the only sensible way to do this is perhaps use a flag of some sort rather than rely on using the value as an 'initialized' flag. Most of the time this is ok as I usually need a 'initialize' function to set everything up so returning false should mean that the object doesn't get used anyway but sometimes I do things 'just in time' and create things just before I use them.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

A great many engines just don't actually directly return GL names to the higher-level code.

Nothing in your engine should have any idea it's working with GL other than the low-level renderer. If your Sprite class has a texture, it should have a MyEngine::TextureHandle object, not a GLuint.

Your custom resource handles then can deal with handling uninitialized or failed resources. For instance, you might want all your textures to actually use some specific "default" GL texture if there isn't another texture mapped to it, e.g. some kind of bright and obnoxious pattern. Your high-level logic becomes much simpler as you don't have to constantly check for invalid or null handles, and instead you can just log an error on load failure and then not worry about it.

Buffers are a bit trickier, but the pattern still holds: wrap your buffer handles in a higher-level primitive that protects the users from buffer creation problems. You'll likely get garbled rendering or the like, but it's still probably better than the alternative during development.

Sean Middleditch – Game Systems Engineer – Join my team!

Just allocate texture/buffer/shader/etc handles as and when you need them. They are just handles - you aren't actually performing memory allocation when you create one.

Until you actually bind and write to one of these resources, they haven't been allocated.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

I always use zero, it's the only value that says "this is not an allocated name". -1 (0xffffffff) is a valid name that an OpenGL implementation could give at any time.

@Boreal Games, I think you're right that in general glGen* functions can't normally fail, but I have had instances where glGen* functions return zero, so it's always worth asserting. IIRC it happened when doing threaded loading on iOS and the OpenGL context I'd created for a thread was in a weird broken state because the system had created/leaked too many OpenGL contexts on prior loads. So it's probably worth an assert after a glGen* call to make sure you're not being given a zero.

I always use zero, it's the only value that says "this is not an allocated name". -1 (0xffffffff) is a valid name that an OpenGL implementation could give at any time.

zero is a valid name for texture objects.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

This topic is closed to new replies.

Advertisement