pqnet
pqnet

Reputation: 6588

Custom allocator to store stl vector in OpenGL buffer objects

I want to customize std::vector class in order to use an OpenGL buffer object as storage.

Is it possible doing so without relying in a specific implementation of STL, by writing a custom allocator and/or subclassing the container?

My problem is how to create wrapper methods for glMapBuffer/glUnmapBuffer in order to user the buffer object for rendering which leave the container in a consistent state.

Upvotes: 1

Views: 1119

Answers (3)

Nicol Bolas
Nicol Bolas

Reputation: 473407

Is it possible doing so without relying in a specific implementation of STL, by writing a custom allocator and/or subclassing the container?

You can, but that doesn't make it a good idea. And even that you can is dependent on your compiler/standard library.

Before C++11, allocators can not have state. They can cannot have useful members, because the containers are not required to actually use the allocator you pass it. They are allowed to create their own. So you can set the type of allocator, but you cannot give it a specific allocator instance and expect that instance to always be used.

So your allocator cannot just create a buffer object and store the object internally. It would have to use a global (or private static or whatever) buffer. And even then, multiple instances would be using the same buffer.

You could get around this by having the allocator stores (in private static variables) a series of buffer objects and mapped pointers. This would allow you to allocate a buffer object of a particular size, and you get a mapped pointer back. The deallocator would use the pointer to figure out which buffer object it came from and do the appropriate cleanup for it.

Of course, this would be utterly useless for actually doing anything with those buffers. You can't use a buffer that is currently mapped. And if your allocator deletes the buffer once the vector is done with the memory, then you can never actually use that buffer object to do something.

Also, don't forget: unmapping a buffer can fail for unspecified reasons. If it does fail, you have no way of knowing that it did, because the unmap call is wrapped up in the allocator. And destructors shouldn't throw exceptions.

C++11 does make it so that allocators can have state. Which means that it is more or less possible. You can have the allocator survive the std::vector that built the data, and therefore, you can query the allocator for the buffer object post-mapping. You can also store whether the unmap failed.

That still doesn't make it a good idea. It'll be much easier overall to just use a regular old std::vector and use glBufferSubData to upload it. After all, mapping a buffer with READ_WRITE almost guarantees that it's going to be regular memory rather than a GPU address. Which means that unmapping is just going to perform a DMA, which glBufferSubData does. You won't gain much performance by mapping.

Reallocation with buffer objects is going to be much more painful. Since the std::vector object is the one that decides how much extra memory to store, you can't play games like allocating a large buffer object and then just expanding the amount of memory that the container uses. Every time the std::vector thinks that it needs more memory, you're going to have to create a new buffer object name, and the std::Vector will do an element-wise copy from mapped memory to mapped memory.

Not very fast.

Really, what you want is to just make your own container class. It isn't that hard. And it'll be much easier to control when it is mapped and when it is not.

Upvotes: 2

user1149224
user1149224

Reputation:

Is it possible doing so without relying in a specific implementation of STL, by writing a custom allocator and/or subclassing the container?

If you are using Microsoft Visual C++, there is a blog post describing how to define a custom STL allocator: "The Mallocator". I think writing custom allocators is STL-implementation-specific.

Upvotes: 1

datenwolf
datenwolf

Reputation: 162164

I want to customize std::vector class in order to use an OpenGL buffer object as storage.

While this certainly is possible, I strongly discourage doing so. Mapped buffer objects must be unmapped before they can be used by OpenGL as data input. Thus such a derived, let's call it glbuffervector would have to map/unmap the buffer object for each and every access. Also taking an address of a dereferenced element will not work, since after dereferencing the buffer object would be unmapped again.

Instead of trying to make a vector that stores in a buffer object, I'd implement a referencing container, which can be created from an existing buffer object, together with a layout, so that iterators can be obtained. Following an RAII scheme the buffer object would be mapped creating an instance, and unmapped with instance deallocation.

Upvotes: 2

Related Questions