用于在 OpenGL 缓冲区对象中存储 stl 向量的自定义分配器 [英] Custom allocator to store stl vector in OpenGL buffer objects

查看:39
本文介绍了用于在 OpenGL 缓冲区对象中存储 stl 向量的自定义分配器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想自定义 std::vector 类,以便使用 OpenGL 缓冲区对象作为存储.

是否可以在不依赖 STL 的特定实现的情况下通过编写自定义分配器和/或对容器进行子类化来实现?

我的问题是如何为 glMapBuffer/glUnmapBuffer 创建包装方法,以便使用缓冲区对象进行渲染,从而使容器保持一致状态.

解决方案

是否可以在不依赖 STL 的特定实现的情况下通过编写自定义分配器和/或对容器进行子类化来实现?

你可以,但这并不是一个好主意.甚至你可以依赖你的编译器/标准库.

在 C++11 之前,分配器不能有状态.它们不能有有用的成员,因为容器不需要实际使用您传递给它的分配器.他们被允许创建自己的.所以你可以设置分配器的类型,但你不能给它一个特定的分配器实例并期望该实例总是被使用.

所以你的分配器不能只是创建一个缓冲区对象并在内部存储该对象.它必须使用全局(或私有静态或其他)缓冲区.即便如此,多个实例将使用相同缓冲区.

您可以通过让分配器存储(在私有静态变量中)一系列缓冲区对象和映射指针来解决这个问题.这将允许您分配特定大小的缓冲区对象,并返回映射指针.解除分配器将使用该指针来确定它来自哪个缓冲区对象并对其进行适当的清理.

当然,这对于实际使用这些缓冲区执行任何操作完全没用.您不能使用当前映射的缓冲区.并且如果您的分配器在向量用完内存后删除缓冲区,那么您实际上永远无法使用该缓冲区对象来某事.

另外,不要忘记:取消映射缓冲区可能会失败,原因不明.如果它确实失败了,您将无法知道它确实失败了,因为 unmap 调用包含在分配器中.并且析构函数不应抛出异常.

C++11 确实让分配器可以拥有状态.这意味着它或多或少是可能的.您可以让分配器在构建数据的 std::vector 之后继续存在,因此,您可以查询分配器以获取缓冲区对象后映射.您还可以存储取消映射是否失败.

这仍然不是一个好主意.总体而言,只使用常规的旧 std::vector 并使用 glBufferSubData 上传它会容易得多.毕竟,使用 READ_WRITE 映射缓冲区几乎可以保证它将是常规内存而不是 GPU 地址.这意味着取消映射只会执行 DMA,而 glBufferSubData 就是这样做的.您不会通过映射获得太多性能.

使用缓冲区对象重新分配将更加痛苦.由于 std::vector 对象是决定要存储多少额外内存的对象,因此您不能玩游戏,例如分配一个大缓冲区对象然后只是扩展容器使用的内存量.每次 std::vector 认为它需要更多内存时,您将不得不创建一个新的缓冲区对象名称,并且 std::Vector 会从映射内存到映射内存进行逐元素复制.

不是很快.

实际上,您想要的只是制作自己的容器类.没那么难.并且更容易控制何时映射和何时不映射.

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.

解决方案

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.

这篇关于用于在 OpenGL 缓冲区对象中存储 stl 向量的自定义分配器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆