PDA

View Full Version : buffer_storage



Alfonse Reinheart
09-04-2011, 02:19 PM
OK, so GL_ARB_texture_storage gave us immutable texture specification. You bind the texture, call glTexStorage* on it, and the storage is immutable.

Buffer objects should be able to have something similar. So there would be a glBufferStorage function that allocates X bytes for the buffer, but also marks it as immutable. So you cannot call glBufferData on it ever.

The main problem this avoids is someone trying to use glBufferData every frame to invalidate it, but maybe gets the hint or size parameters wrong. That's an improper invalidation of the buffer, and it results effectively in creating a new buffer object with theoretically new characteristics. So instead, you invalidate it with glMapBufferRange, and this extension would introduce a specific API for invalidating buffers: glBufferInvalidate.

The latter might only be usable on immutable buffers. But that's debatable.

However, this should also do some other things. Since it's an either/or process (either you use glBufferStorage or glBufferData, just like with glTexStorage), we can take the opportunity to correct some mistakes with buffer objects. Namely: hints.

Hints with glBufferData are optional, in the sense that you can more or less ignore them. If you use GL_STATIC_DRAW, you can call glBufferData as many times as you want.

With immutable storage however, hints should be requirements. I don't suggest keeping the previous hint enums around, as they're confusing to users and are too fuzzy (where is the dividing line between STREAM and DYNAMIC?). Whatever the new "hint" system is, it should absolutely affect the behavior of the buffer object.

If there is an equivalent to "DRAW", for example, then it should be enforced. DRAW means that you will write but not read. So it should be an INVALID_OPERATION to use glGetBufferSubData or glMapBufferRange for reading. Same goes for "READ" and "COPY".

I don't really have a fully developed suggestion for a new hint system, but whatever it is should specifically have a semantic component.

Alfonse Reinheart
09-04-2011, 11:30 PM
After thinking about it more, the "hints" should be behaviors. The possible options are as follows:

1: No touching. You create the storage with glBufferStorage and that's it. You may not call glBufferSubData, glMapBufferRange, or glGetBufferSubData. You can call glBufferInvalidate. This allows you to read into the buffer from OpenGL (say, via transform feedback) after an invalidate, but even that may not be a good idea.

2: Write exactly once. After calling glBufferStorage, you get exactly one glBufferSubData or glMapBufferRange call. After that, you get errors. Also, no invalidation. Obviously, glGetBufferSubData is out, as is read mapping.

3: Write only after invalidate. As write exactly once, except that you can keep doing it so long as you invalidate between each write. So you can call glBufferStorage, glBufferSubData, then glBufferInvalidate, then glBufferSubData, then glMapBufferRange with GL_INVALIDATE_BUFFER_BIT, etc. Every write goes to invalidated memory.

Note: I'm not particularly happy with this behavior, as it prevents you from mapping multiple times and writing to different locations. Perhaps allowances can be made for GL_INVALIDATE_RANGE_BIT.

4: Writeable. You can pretty much do whatever, whenever, so long as you are writing to the buffer. So no read mapping and no glGetBufferSubData calls.

5: Read only after invalidate. Basically, you compute some data on the GPU, read it back on the CPU, then invalidate the buffer, then do it over again. Your read accesses to the buffer must be bracketed by invalidates, as for "Write only after invalidate". The same notation applies.

I don't see much point in a "Read only once."

6: Readable: You can read pretty much willy-nilly. Just no writing at all.

7: Wild-west. Basically what we have now. Whatever, whenever, read, right, arithmetic, whatever it is you want to do, you can do it.

aqnuep
09-05-2011, 01:08 AM
Sounds very interesting. I have only two problems with it:

1. I would never restrict MapBufferRange calls to a single invocation, rather just limit the usable map flags because, as you said, mapping several subranges would make some headaches this way. Also the amount of validations would be pretty high. I would rather say we should introduce one more "undefined behavior" (yes, I know we hate those, but they are pretty useful to provide high performance in situations where there is no clear behavior across hardware vendors as an example). So if one writes a single range multiple times through buffer mapping in case of e.g. a "Write only after invalidate" buffer then the results would be undefined. This way we can force developers not to use it this way, I believe.

2. In case of the "No touching" behavior I think you mean something like STREAM_COPY? I don't totally understand what you meant there with the transform feedback example, but if you cannot even write to the buffer through transform feedback and/or image stores then what is the buffer good for? I'm pretty sure I've misunderstood something here but if I did maybe others will as well, so please can you clarify it?

Alfonse Reinheart
09-05-2011, 10:22 AM
Also the amount of validations would be pretty high.

Perhaps, but they would only be done on read/write operations, which are already fairly heavyweight.


This way we can force developers not to use it this way, I believe.

The problem with marking it as undefined is that it will likely work. If the implementation does not take active steps to detect this behavior and stop it, then the code will continue to function.


In case of the "No touching" behavior I think you mean something like STREAM_COPY?

More just COPY in general. These behaviors only specify the pattern of CPU-based reads/writes to buffers. Reads and writes from OpenGL commands would always work.

The only difference between STATIC_COPY and STREAM_COPY under my system is whether you ever invalidate the buffer or not. And I don't think it's worth it to introduce a new behavior just for that; the OpenGL implementation can detect if you invalidate a buffer.

aqnuep
09-05-2011, 10:34 AM
Okay, thanks for the clarification and, again, interesting proposal.

elFarto
09-06-2011, 06:06 AM
I understand what you are trying to propose, but I don't understand what problem it solves?

Regards
elFarto

aqnuep
09-06-2011, 06:44 AM
First, it adds immutable buffer objects in the same style ARB_texture_storage introduced immutable texture objects, and second, it gives more control over creating buffer objects for particular use case scenarios.

arekkusu
09-06-2011, 09:18 AM
Mutable textures have the very real problem that they can be mipmap/cubemap incomplete. Buffers don't have that problem.

Alfonse Reinheart
09-06-2011, 01:42 PM
I understand what you are trying to propose, but I don't understand what problem it solves?

One problem with buffer objects is the quantity of "hidden knowledge" about how to use them properly to achieve performance. Oh, it's easy if all you're doing is static data. But streamed data is very hard to get right, especially across platforms. Rob Barris had a couple of posts that were finally able to point us in a good direction surrounding buffer object streaming. But that's not exactly part of the API, is it?

What inspired me to make this proposal was a question on StackOverflow (http://stackoverflow.com/questions/7303000/opengl-es-2-0-the-most-efficient-setup-for-a-vbo-with-gl-stream-draw) from a new user about buffer objects. Every frame, he was generating new data and using glBufferData to upload it. The problem is that he would increase or decrease the size of the data uploaded. And therefore change the size of the buffer object. Which effectively means that the driver was not just invalidating memory, but reallocating it.

A good API should be easy to use correctly and hard to use incorrectly. Invalidation via glBufferData is hard to get right; it requires knowing that you should keep the size and usage hints the same. If you aren't on this forum and aren't an experience OpenGL programmer, then you do not know this. Indeed, odds are that you have no idea what buffer invalidation is if you aren't.

The other inspiration were a couple of facts about usage hints on NVIDIA and AMD. Did you know that AMD ignores usage hints entirely? They don't mean anything for them. And did you know that if you gave GL_DYNAMIC_DRAW to NVIDIA implementations, you killed upload performance? If one of the two major OpenGL implementations basically ignores the hint system, then there is a problem.

If you write primarily on AMD hardware, then you may be convinced that a certain hint is giving you the correct performance. That's going to be a rude awakening when you slide over to NVIDIA and find that your DYNAMIC hint basically killed your performance.

Now yes, performance is certainly going to be different cross-platform. Even with behavior-based usage. One platform might make "write anytime" perform as well as others. But at least there are guidelines about what the proper ways to use the buffer are.

mbentrup
09-07-2011, 12:55 AM
I think the GL_AMD_pinned_memory extension is a nice approach for uploading use-once data. (The way it is uses a new buffer target to create pinned memory objects may be debatable.)

You just point the driver to the data in your memory space and promise that you don't modify it until the driver has finished using it, which you can check via a sync object.

I think an updated buffer object should provide something similar, as it is IMHO much easier to use than GL_ARB_map_buffer_range.

Alfonse Reinheart
09-07-2011, 01:34 AM
I think the GL_AMD_pinned_memory extension is a nice approach for uploading use-once data.

But that's not what the extension does. It doesn't upload anything. It's purpose is to allow client memory to be used more or less like a buffer object. It's real purpose is to get around the fact that glTexImage* functions have to be finished with client memory when they return.

I wouldn't be adverse to a glBufferClient function, which like glBufferStorage would create an immutable buffer object with certain properties. But to be honest, I'd much rather have a special set of glTexSubImage* calls or something else than to overload buffer object functionality for this purpose.

mbentrup
09-07-2011, 01:58 AM
Well, afaik AMD hasn't published the extension spec yet, so it's all a bit of speculation, but though their presentation showed the extension on PBOs, there was nothing that indicates that is wouldn't work the same way for any other type of BO.

elFarto
09-07-2011, 02:34 AM
Ok, but I'm not sure how replacing one method that has a hint the driver (mostly) ignores, with another method that has a hint the driver could possibly ignore, improves things.

Also, do you have any more information on the difference between 'invalidating memory' and 'reallocating it'. Reading through Rob's suggestion, it seems invalidating the buffer means the driver will orphan the currently allocated memory and allocate another slab. I can't see how changing the size between calls is going to impact that.

*edit* Is glFlushMappedBufferRange (http://www.opengl.org/sdk/docs/man4/xhtml/glFlushMappedBufferRange.xml) meant to return a GLsync object..?

Regards
elFarto

Alfonse Reinheart
09-07-2011, 03:36 AM
Ok, but I'm not sure how replacing one method that has a hint the driver (mostly) ignores, with another method that has a hint the driver could possibly ignore, improves things.

The driver could ignore it, in the sense that it could choose not to do something more optimal in the restricted cases. But the primary difference is simple.

AMD's drivers ignore the hints, but they do not ignore usage patterns. Essentially, they pick optimal memory layouts and allocations based on how you actually use the buffer, not on how you say you're going to. If the API forces you to use the buffer a certain way, then it's really no different from AMD's perspective. There's just a nice API there to guarantee that you don't go back on your word. They don't have to look at what that word is when picking out where your memory goes.

Also, there's the fact that the API is self-documenting. You use the most restrictive form of buffer object that you can live with. You don't have to guess at what magical access pattern driver developers will be optimizing for, since the access patterns are enforced by the API.


I can't see how changing the size between calls is going to impact that.

It all depends on how it gets implemented. If a driver sees you doing lots of buffer invalidation, then it can basically allocate two pieces of memory the same size as the requested buffer and just swap back and forth between them. Or if you do a lot of invalidations, 3 pieces of memory. Or whatever.

It's all about giving implementations more information up-front about how you're going to use the buffer, and then forcing the user to live within those restrictions.

kRogue
09-07-2011, 03:57 AM
My only thoughts on this are that we already have a fair number of buffer object hints (in creation and binding) that are completely implementation defined in their effects, as Dark Photon[I think] once called it the "buffer object Ouija board", adding more flags, enumeration and such although in theory could help, I would suspect it might just make that Ouija board bigger.

Alfonse Reinheart
09-07-2011, 04:47 AM
The point is that they're not hints. A hint is not something you have to abide by. That's why they fail (besides being poorly specified): because you can set them to one thing and then do something else.

If you use the "write exactly once" behavior, you get exactly one chance to write to the buffer; all subsequent attempts will fail with a GL_INVALID_OPERATION error. The point of behaviors is that they would be enforced.

The only guesswork you might have to do is which behavior to use in which implementations. And even then, the behaviors are designed to be functional subsets of one another. So it would take a pretty poorly written implementation to make "write anytime" faster than "write once then invalidate". They could be the same speed, but there's no reason for the more restricted one to be slower.

And even in the unlikely event that each behavior had completely unknown performance characteristics on implementations relative to one another, the access patterns are well specified. And finite. Therefore, the only possible "Ouija board" issue would be if "write anytime" allowed access patterns not specified by behaviors that were somehow faster than one of the more restrictive ones. And that seems very unlikely; the list of behaviors is always open to debate if someone has an access pattern that would legitimately be faster.

l_belev
09-09-2011, 04:07 PM
as arekkusu said, textures have great problem because of the mipmaps.

imagine this situation:
the app creates a new texture,
then it specifies all even mips with some internal format (e.g. rgba8)
then it specifies all odd mips with different internal format (e.g. rgb5)

at this point the texture is incomplete but if the app does one of these 2 things: re-specify the odd mips with rgba8 or re-specify the even mips with rgb5, then the texture should become complete.

of course this would be quite idiotic behaviour by the app, but still it is completely valid and permitted by the api spec and so the implementation should handle it correctly.

thus the implementation should always save any image in any format you specify for any mip, even if there are no 2 mips with the same format.

imagine what nightmare is this for the implementation.

also these saved images should be placed in system memory - no point of uploading them to the video memory for at this point the texture is unusable anyway and we dont know what will be the final format when/if it eventually becomes complete.
so each texture must also have a backing system memory copy at least until it becomes complete, which can be avoided with the new immutable property.

this is the main reason for the new immutable texture thing.
the buffers don't have mips and this bad problem does not exist for them.

Alfonse Reinheart
09-09-2011, 04:36 PM
the buffers don't have mips and this bad problem does not exist for them.

I know. But buffers have other problems, which I addressed. I don't see how what you're talking about is relevant.

l_belev
09-09-2011, 07:53 PM
you say the main problem this would solve is to avoid using BufferData just for invalidation because the user may mess up the params.
i am very much against api designs that try to prevent the programmer from making mistakes, so for me this reason is invalid. but i wont go into a debate about it.

another problem you mention are the usage hints. i agree here, mandatory (not hint-only) usage flags would be good to have.
but i dont see what this has to do with immutability - why a buffer has to be immutable in order to have mandatory usage flags?

Alfonse Reinheart
09-09-2011, 10:40 PM
i am very much against api designs that try to prevent the programmer from making mistakes, so for me this reason is invalid.

OK, let's play this game.

Why does glTexStorage make the texture immutable? Preventing the programmer from making mistakes is the pretty much the entire point of making glTexStorage textures immutable.

After all:


imagine this situation:
the app creates a new texture,
then it specifies all even mips with some internal format (e.g. rgba8)
then it specifies all odd mips with different internal format (e.g. rgb5)

That is a mistake. That creates a texture which cannot be used. It is an incomplete texture. But the driver still has to accept it; just because the texture is incomplete now doesn't mean that it won't be complete in the future. The driver is required to live with this half-state.

Aside from making the texture immutable, glTexStorage does nothing that you could not do before in your own code. You could create all of your textures by calling glTexImage* on them in a loop, using NULL as the data, then upload with glTexSubImage* as needed. You could set the GL_TEXTURE_BASE/MAX_LEVEL parameters correctly, and thus ensure that your textures are mipmap complete. You could use sized internal formats instead of unsized ones. And you could make them "immutable" by never touching any of this stuff again.

So why does this extension exist, if it does not allow you to do anything that you couldn't before? What is it about glTexStorage that opens up optimization potential?

Consider this except from the the issues section of ARB_texture_storage:



4. Should use of these entry-points make the metadata (format and dimensions) immutable?

RESOLVED: Yes.

DISCUSSION: The benefits of knowing metadata can't change will probably outweigh the extra cost of checking the TEXTURE_IMMUTABLE_FORMAT flag on each texture specification call.

5. Should it be legal to completely replace the texture using a new call to TexStorage*?

RESOLVED. It will not be allowed.

DISCUSSION: This is useful to invalidate all levels of a texture. Allowing the metadata to be changed here seems easier than trying to define a portable definition of what it means to change the metadata (e.g. what if you used an unsized internal format the first time and the corresponding sized internal format the second time, or vice versa)?

However, while this is largely similar to deleting the old texture object and replacing it with a new one, it does lose some of the advantages of immutability. Specifically, because doing so does not reset bindings, it doesn't allow a migration path to an API that validates the texture format at bind time.


Immutability is valued by the ARB. By the driver developers. Why?

Because an API that allows the programmer to make mistakes also is an API that requires that the implementation accept those mistakes. It has to deal with it. It has to handle erroneous badness with a smile.

"api designs that try to prevent the programmer from making mistakes" are also API designs that make it easier for implementers to optimize. Because the user cannot put the system in an invalid state, there is no question about what the valid states are. And therefore, the driver doesn't have to worry about those cases where weirdness is happening. Everything is simple, regular, and expected. And once the driver developer knows how the user is going to go about their business, they can get to their business of making things fast.

I see no reason why that logic should not apply to buffer objects too. The ability to call glBufferData on an object multiple times with different sizes and hints, all without changing the object name, goes against the last part of the ARB_texture_storage quote. The part about the eventual migration to bind-time validation.

Making buffer objects immutable, but allowing for invalidation via a specialized function call, means that you can retain bind-time validation, since you know the size of it even after it is invalidated. A glBindBufferRange that was in-range before validation remains in-range after. While the GPU pointers may have changed, the range didn't.

Yes, it prevents mistakes, but it was allowing those mistakes to begin with that is a big reason for the "buffer object Ouija board", as Dark Photon would say. Preventing mistakes is good for everyone.


but i dont see what this has to do with immutability - why a buffer has to be immutable in order to have mandatory usage flags?

Because it makes it impossible to change the usage flags later. If you can only call glBufferStorage one time, then the implementation knows that this object name will be forever associated with a block of memory of known size and will always have a particular usage pattern. The driver doesn't have to be written to deal with changing this usage pattern in the future.

Again, the same argument for glTexStorage's immutability.

l_belev
09-10-2011, 06:44 AM
Why does glTexStorage make the texture immutable? Preventing the programmer from making mistakes is the pretty much the entire point of making glTexStorage textures immutable.


good try but not good enough :)
preventing the programmer from making mistakes is not the purpose of glTexStorage at all.
its purpose is to relieve the driver from the burden i explained, which is unavoidable otherwise.



Because it makes it impossible to change the usage flags later. If you can only call glBufferStorage one time, then the implementation knows that this object name will be forever associated with a block of memory of known size and will always have a particular usage pattern. The driver doesn't have to be written to deal with changing this usage pattern in the future.


when you call glBufferData second time on the same object, you effectively destroy the old one and create a new one.
why should not you be allowed to specify new parameters for the new object? does not make any sense.

the only difference between glBufferData on existing object and first destroying it and then creating new one is that in the first case you keep the same object id. do you really think this alone has any overhead or is in any way problematic for the driver?

the fundamental difference between glBufferData and glTexImage is that the first semantically destroys the old object and creates new one whereas the second hes much more complicated function which makes the lives of the driver writers harder.
this difference is the reason why the considerations leading to the need of glTexStorage do not apply in the case with the buffers

Eosie
09-20-2011, 02:40 PM
l_belev is right. buffer_storage would be useless, because the buffer API has none of the problems of the texture API. It's not just the mistakes that are painful for drivers, but pretty common cases as well. The real reason for texture_storage is to address the total awkwardness of OpenGL texture allocation. I'll give you an example:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

Ok, you would say that such a call creates a texture in VRAM. Let's assume you're right. Some drivers do that, some don't. Here comes a surprise from the user:

glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, data1);

Ouch! I have to reallocate my texture in VRAM now, because I only allocated space for level 0, and then copy the content of level 0 to its new location. Such a waste. Wait! The texture is incomplete now anyway, so I can defer the reallocation in case the user specifies the other mipmaps. In the meantime, I can hold a temporary copy of data1 in RAM.

glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2);

Again, just make a temporary copy of data2.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);

The texture is complete now, should we finally do the reallocation? Better not. We can wait until the texture is used for the first time and do it then.

Later in glDrawElements:
- Allocate a texture with 3 mipmaps.
- Copy level 0 from the first texture we allocated.
- Deallocate the first texture.
- Copy levels 1 and 2 from RAM.

Now you can see there is something very, very wrong with OpenGL. Why do things have to be so complicated for drivers? There is no way to know how much storage is needed for a texture before it's used for the first time. You can only guess until then.

ARB_texture_storage fixes this awkwardness by adding a way to allocate the storage in memory first. The immutability itself is not very useful, but the ARB somehow had to disallow the glTexImage calls which cause so much pain, and adding the immutability rule sounds like a good compromise. The extension pretty much ensures that texture specification is as fast as possible without unnecessary reallocations and temporary copies in RAM. So use it, love it.

malexander
09-20-2011, 06:09 PM
Now you can see there is something very, very wrong with OpenGL.

Something is very, very wrong with that implementation of the spec :) There's nothing in the GL spec that says that a mipmapped texture must have all its texture levels reside in one contiguous memory chunk - that is completely up to the implementors. The levels could just as easily be separate chunks of memory, with a small table containing the starting addresses of each. Each texture must have some sort of header information where the dimensions, format and mip range must be stored; this table could easily reside there.

I certainly wouldn't mind a houseclean of the usage hints to be clearer and more useful. The current setup has a lot of entries for questionable usage cases that seems to have sprung out of the fact that the spec writers wanted a nice 3x3 matrix.

Alfonse Reinheart
09-20-2011, 11:08 PM
preventing the programmer from making mistakes is not the purpose of glTexStorage at all.
its purpose is to relieve the driver from the burden i explained, which is unavoidable otherwise.

And it does so by preventing the programmer from doing things that would burden the driver, ie: making mistakes. Namely, calling glTexStorage multiple times. That would be a mistake, and the texture_storage extension rightfully stops you from doing it.

If texture_storage's purpose was solely to allocate all mipmaps in one go, then it wouldn't need to be immutable. It could just say that subsequent glTexStorage calls erase all previous mipmap levels and respecify the whole texture. And that you can't call glTexImage* on them. Doing that would make texture objects perfectly analogous to buffer object, since their storage would be allocated up front with glTexStorage, just like buffer objects are with glBufferData. You could change the object completely by calling glTexStorage again with different sizes, just like call glBufferData with a different size.

But the ARB didn't do that, for the reasons they explained.


when you call glBufferData second time on the same object, you effectively destroy the old one and create a new one.
why should not you be allowed to specify new parameters for the new object? does not make any sense.

Simple: it's not a new object. It's the same object with different storage on it. Drivers can't just pretend that it's a new object, because it isn't. They have to treat it differently, because the object could be in use somewhere. And I don't mean being pulled from or written to, but bound to the context or attached to a VAO.

If you create a new object with glGenBuffers, then the driver knows that it wasn't in use before.

And the reason you shouldn't be allowed to do so is because it confuses the API. It makes using the API harder, and nothing is gained by that. It makes writing drivers for the API harder, and nothing is gained by that either.

This is a relatively fast path:



for(each frame)
glBindBuffer(bufferName);
glBufferData(..., constantSize, ...);


This is a slow path:



for(each frame)
glBindBuffer(bufferName);
glBufferData(..., ComputeArbitrarySize(), ...);


Your argument is that this is a good thing. That it is a good thing for the difference between the fast path and the slow path to be based on whether a number changes between frames. That it is a good thing that this is documented precisely nowhere. That it is a good thing that users can stumble onto the slow path through no fault of their own. That it is a good thing that finding the fast path for buffer objects is a nightmare of random and arbitrary possibilities.

Your argument is that the above situation is better than this:



//fast
glBindBuffer(bufferName);
glBufferStorage(..., GL_INVALIDATE_THEN_WRITE_ONCE);
for(each frame)
glBindBuffer(bufferName);
glBufferInvalidate(...);
glBufferSubData(...);



//slow
glBindBuffer(bufferName);
glBufferStorage(..., GL_ANYTHING);
for(each frame)
glBindBuffer(bufferName);
glBufferData(..., ComputeArbitrarySize(), ...);


Just by looking at the function calls, it is obvious that the top one is more restricted in its buffer object use, and therefore likely faster, than the bottom one. It's immediately apparent, and impossible to ignore. And impossible to screw up, since the driver will throw an error at you.

Yet you are arguing that the current obfuscated API is the way it ought to be.

You cannot fix the problems with buffer objects without establishing a binding contract between the user and the driver. And in establishing that contract, you will necessarily make the API easier to use.

And the very first step in doing that is to make the storage immutable.


ARB_texture_storage fixes this awkwardness by adding a way to allocate the storage in memory first.

I thought I was quite clear about one of the purposes of my proposed extension:


The main problem this avoids is someone trying to use glBufferData every frame to invalidate it, but maybe gets the hint or size parameters wrong. That's an improper invalidation of the buffer, and it results effectively in creating a new buffer object with theoretically new characteristics.

My suggested extension makes this usage pattern impossible. Just like texture_storage makes it impossible to screw up texture allocation. Both of these ideas fix different problems, yes, but they both use immutability to solve their respective problems. The immutability in both cases is important, because it allows the user and the driver to have a binding contract about how the buffer is going to be used.

You cannot make buffer object hints binding unless the buffer object storage is to some degree immutable. For example, the "Write only after invalidate" style does not allow respecifying the buffer size. If you can respecify it, then you've broken the entire point of having restrictive hints. Namely, having a binding contract between the user and the implementation as to how the user will be using it.


I certainly wouldn't mind a houseclean of the usage hints to be clearer and more useful.

Making new hints would be meaningless without the power to enforce them via the API. And that power starts by knowing that the user cannot make the buffer change size or change what those hints are. IE: immutability.

Eosie
09-22-2011, 03:34 PM
Now you can see there is something very, very wrong with OpenGL.

Something is very, very wrong with that implementation of the spec :) There's nothing in the GL spec that says that a mipmapped texture must have all its texture levels reside in one contiguous memory chunk - that is completely up to the implementors. The levels could just as easily be separate chunks of memory ...

Are you saying that all hardware implementations are wrong? :) I have never seen hardware which can do what you describe. Maybe you can point me to one.

malexander
09-22-2011, 05:36 PM
Are you saying that all hardware implementations are wrong? smile I have never seen hardware which can do what you describe. Maybe you can point me to one.

I don't have to. The GPU hardware shouldn't be involved in the wrangling of a user's texture data into whatever format the hardware prefers, the client-side driver should do that for them. And so, it can store the data however it likes until it needs to send the texture to the GPU's memory. glTexStorage() makes a lot more sense now that unified CPU/GPU memory schemes are appearing in OpenGL (non-ES), but I don't believe the previous glTexImage() scheme was that inefficient.

The point I was attempting to make was that the situation you described with textures seemed overly dire, in an attempt to make immutable buffers seem less necessary.

Edit: Actually, let's say the texture situation was as awful as you describe and that immutable textures completely fixed it. How does that affect the merits of this particular proposal?

Eosie
09-26-2011, 11:20 AM
The ideal implementation for textures is that what you specify in glTex*Image should be directly copied into GPU-accessible memory. The driver can be even smart and allocate a RGBA8 texture for format=GL_RGBA and a BGRA8 texture for format=GL_BGRA, so that it can just memcpy your data. Doing anything else is a waste of time (e.g. CPU cycles etc).

Another reason for immutability in texture_storage was to simplify the texture completeness checking. (it's even mentioned in the spec)

I am not convinced that immutability would be any useful for buffers. Doing the complete reallocation of a buffer is merely just about changing the pointer to the buffer (both in a driver and in hardware), which means vertex arrays and texture buffers and whatnots must re-validated, so that the change of the pointer is propagated through different layers of the driver into the hardware. That's not very fast, but immutability wouldn't help here anyway. If you want to change the size of a buffer, this step is inevitable and it doesn't matter whether you setup a new vertex array state by yourself or your driver will do it for you in glBufferData (the former may be a little bit more efficient, depending on the implementation.. the question is: would it be even measurable?).

Performance-wise, I don't see where immutability would help (such that it's worth to introduce a new extension). The implementation of glBufferData is already pretty straightforward. Someone might argue that immutability would make for an easier implementation. This may not apply here because driver developers would have to maintain the current implementation for an unspecified time anyway (usually 15-20 years in the OpenGL world, or until everybody abandons OpenGL).

kyle_
09-26-2011, 12:40 PM
The ideal implementation for textures is that what you specify in glTex*Image should be directly copied into GPU-accessible memory.

So you want to map texture memory. Cant see why this cant be done - im sure khronos guys would be able to pull decent api for that.

kRogue
09-26-2011, 02:12 PM
Simple: it's not a new object. It's the same object with different storage on it. Drivers can't just pretend that it's a new object, because it isn't. They have to treat it differently, because the object could be in use somewhere. And I don't mean being pulled from or written to, but bound to the context or attached to a VAO.


Um. Err. If you call glBufferData in with say MagicNumber bound in thread A and MagicNumber is also bound in thread B (the contexts are in the same share group), then GL the original data of the buffer object is used in thread B still, until MagicNumber is rebound in thread B [I think]. This is because calling glBufferData does create a new object according to the specification:



The data store of a buffer object is created and initialized by calling
void BufferData( enum target, sizeiptr size, const
void *data, enum usage );


(my emphasis added). I's say that I_belev is spot on here. Besides, how many times for a fixed buffer object ID does one change the usage flags or size? That is the only reason to call glBufferData again on the same name.

Though I freely admit, I would not mind seeing a glResizeBufferObject API point, perhaps that is what you are after: a resize API with buffer object creation hints to state that you won't resize a buffer object.

Eosie
09-30-2011, 04:10 PM
The ideal implementation for textures is that what you specify in glTex*Image should be directly copied into GPU-accessible memory.

So you want to map texture memory. Cant see why this cant be done - im sure khronos guys would be able to pull decent api for that.

I didn't mean exactly that, as I was referring to the current OpenGL, but good point. I can already map texture memory. ;) But I agree that such a feature would be useful to have in OpenGL, however OpenGL isn't ready for that. The OpenGL internal format doesn't specify any channel ordering and it's allowed to fake one format with another (e.g. ALPHA8 using RGBA8). Even though there are lots of R/RG/RGB/RGBA/A/L/LA/I/D/DS/S internal formats, implementations may actually not support every one of them (e.g. most RGB formats are not). We would need a new set of internal formats that strictly describe how a pixel should look like in memory and we'd need an is-format-supported query too (pretty much what Direct3D has). I don't see this coming anytime soon. It would clean up the API though.

Alfonse Reinheart
08-17-2012, 01:10 AM
The ARB has provided more reason why fixing the problems with buffer objects requires we need immutable buffer storage.

glTextureView (http://www.opengl.org/wiki/GLAPI/glTextureView) requires an immutable texture. It doesn't work without immutable textures. It takes immutable textures and creates immutable textures.

Why? Because the API would not be possible otherwise.

If you can change the storage of the source texture, how does the view change? What happens if you modify the texture's storage such that the view's relative mipmap pyramid is invalid?

glTextureView is basically impossible without immutable textures. You couldn't do it, because important aspects of view textures could be changed by altering another texture. Oh, you could specify that views become incomplete if you do that or whatever, but the API would ultimately be far too fragile.

Something similar is true of buffer objects. If you want to have binding contracts for buffer object usage (rather than hints), if you want explicit requirements on use patterns that the API would throw errors when you violate, then you need some guarantees about buffer object storage. You need to know that someone can't just re-specify it at a whim. And there are only two ways to do that: 1) create a new object type, which requires lots of API additions, or 2) do what they did with textures and effectively create a new object type within the old API, which can have different behavior attached.

Immutable textures are a way of creating a new texture object without the API issues of creating a new texture object. Immutable textures have different properties and different APIs work on them. Similarly, immutable buffer objects could be used to create a new buffer object without the API issues of creating a new buffer.

kRogue
08-17-2012, 05:23 AM
Necromancy.