Statless texture specification and access

Hi all, below is an attempt to give a starting ground for
a specification to provide texture image specifications and reads
without being affect by GL state.

Comments welcome. Flames mostly welcome.

Name
EXT_stateless_texture_access

Name Strings

GL_EXT_stateless_texture_access

Contributors

Kevin Rogovin, Nomovok

Contact

Kevin Rogovin, Nomovok (kevin.rogovin 'at' nomovok.com)

Status

Draft

Version

Last Modified Date:         11/30/2010
Author revision:            1
Version                     0.1

Number

 TBD, if ever.

Dependencies

This extension is written against the OpenGL 3.3 specification.

Overview

This extension introduces a set of commands to specify texture

data which are not affected by global GL state. To this end, a
new objec type is introduced so that specifying texture data does
not depend on current GL state, only the state of those objects.
A great deal of the functionality to reduce the affect of GL state
on GL command can be found in GL_EXT_direct_state_access. This
extension provides the following functions:

New Procedures and Functions

uint AllocateTexture1D(enum target, boolean allocate_mipmaps, 
                       int internalformat,
                       sizei width);
uint AllocateTexture2D(enum target, boolean allocate_mipmaps, 
                       int internalformat,
                       sizei width, sizei height);
uint AllocateTexture3D(enum target, boolean allocate_mipmaps, 
                       int internalformat,
                       sizei width, sizei height, sizei depth);
uint AllocateTexture2DMultiSample(enum target, sizei samples, 
                                  int internalformat,
                                  sizei width);
uint AllocateTexture3DMultiSample(enum target, sizei samples, 
                                  int internalformat,
                                  sizei width);
void TextureData1D(uint texture, 
                   uint mipmap,
                   int x, sizei width, 
                   uint packing_specification,
                   enum format, enum type, 
                   uint buffer_object,
                   const void *pixels);
void TextureData2D(uint texture, 
                   uint mipmap,
                   int x, int y, sizei width, sizei height,
                   uint packing_specification,
                   uint buffer_object,
                   enum format, enum type, const void *pixels);
void TextureData3D(uint texture, 
                   uint mipmap,
                   int x, int y, int z, 
                   sizei width, sizei height, sizei depth,
                   uint packing_specification,
                   uint buffer_object,
                   enum format, enum type, const void *pixels);
void GetTextureData1D(uint texture,
                      int x, 
                      sizei width, 
                      uint unpacking_specification,
                      uint buffer_object,
                      enum format, enum type, void *data);
void GetTextureData2D(uint texture,
                      int x, int y, 
                      sizei width, sizei height, 
                      uint unpacking_specification,
                      uint buffer_object,
                      enum format, enum type, void *data);
void GetTextureData3D(uint texture,
                      int x, int y, int z, 
                      sizei width, sizei height, sizei depth,
                      uint unpacking_specification,
                      uint buffer_object,
                      enum format, enum type, void *data);

enum GetTextureTarget(uint texture);
void UseTexture(uint texture_unit, uint texture);
void UseBufferObjectAsTexture(uint texture_unit, uint buffer_object, sizei offset, GLenum format);

uint CreatePixelStoreSpecification(void);
void DeletePixelStoreSpecification(uint);
void PixelStoreSpecificationParamf(enum pname, float value);
void PixelStoreSpecificationParami(enum pname, int value);

The following functions from GL_EXT_direct_state_access are to be

imported by GL_EXT_stateless_texture_access (shamelessly copy-pasted from the
GL_EXT_direct_state_access extension):

<EXT_texture_integer: New integer texture object commands and queries
replace "Tex" in name with "Texture" and add initial "uint texture"
parameter> 
    void TextureParameterIivEXT(uint texture, enum target,
                                enum pname, const int *params);
    void TextureParameterIuivEXT(uint texture, enum target,
                                 enum pname, const uint *params);
    void GetTextureParameterIivEXT(uint texture, enum target,
                                   enum pname, int *params);
    void GetTextureParameterIuivEXT(uint texture, enum target,
                                    enum pname, uint *params);
<OpenGL 3.0: New texture commands add "Texture" within name and
replace "enum target" with "uint texture">
    void GenerateTextureMipmapEXT(uint texture, enum target);
<OpenGL 3.0: New texture commands add "MultiTex" within name and
replace "enum target" with "enum texunit">
    void GenerateMultiTexMipmapEXT(enum texunit, enum target);

Additions to Chapter 3 of the OpenGL 3.3 Specification (OpenGL Operation)

Memory for textures maybe allocated through:

uint AllocateTexture1D(enum target, boolean allocate_mipmaps, 
                       int internalformat,
                       sizei width);
uint AllocateTexture2D(enum target, boolean allocate_mipmaps, 
                       int internalformat,
                       sizei width, sizei height);
uint AllocateTexture3D(enum target, boolean allocate_mipmaps, 
                       int internalformat,
                       sizei width, sizei height, sizei depth);
uint AllocateTexture2DMultiSample(enum target, sizei samples, 
                                  int internalformat,
                                  sizei width);
uint AllocateTexture3DMultiSample(enum target, sizei samples, 
                                  int internalformat,
                                  sizei width);

where target refers to a texture target where the allocated texture may
be bound to, internalformat is the internal representation of the texture,
width, height and depth are the dimensions of the texture. For non-multisample
textures, the parameter allocate_mipmaps indicates is to allocate the memory
necessary to store mipmaps. For multisample textures, the parameter samples
refers to the number of samples. Calls to TexImage1D, TexImage2D, TexImage3D,
will generate an GL_INVALID_SOMETHING error if a texture allocated with
one of the above calls is bound to the current texture unit.

GL facilitates the creation and deletion of objects, called
PixelStoreSpecification objects, that store how GL is to
pack and unpack pixel data.

uint CreatePixelStoreSpecification(void);

and destroyed with:

void DeletePixelStoreSpecification(uint);

When created, the PixelStoreSpecification state vector is initiliazed
with the defaults values of GL of PixelStore.

The calls

void PixelStoreSpecificationParamf(uint object, enum pname, float value);
void PixelStoreSpecificationParami(uint object, enum pname, int value);

sets a parameter of the PixelStoreSpecification object value named by pname
to the specified value, pname and value pairs are as in PixelStore.

The name 0 is used for the “default” PixelStoreSpecification of GL, it’s values
are unchangeable and is initialized with the default packing and unpacking
value of GL.

Texture may have their texture data modified from the GL client with the commands:
<or should be only those allocated with AllocateTexture1D, AllocateTexture2D, AllocateTexture3D>?

void TextureData1D(uint texture, 
                   uint mipmap,
                   int x, sizei width, 
                   uint unpacking_specification,
                   enum format, enum type, 
                   uint buffer_object,
                   const void *pixels);
void TextureData2D(uint texture, 
                   uint mipmap,
                   int x, int y, sizei width, sizei height,
                   uint unpacking_specification,
                   uint buffer_object,
                   enum format, enum type, const void *pixels);
void TextureData3D(uint texture, 
                   uint mipmap,
                   int x, int y, int z, 
                   sizei width, sizei height, sizei depth,
                   uint unpacking_specification,
                   uint buffer_object,
                   enum format, enum type, const void *pixels);

where texture is the name of the texture, mipmap is the mipmap lod to be modified,
x,y and z are the location of the region to be modified (as in TexSubImage famlily),
width, height and depth specify the size of the region to be modified (as in TexSubImage famlily),
The unpacking_specification is the name of a PixelStoreSpecification parameter determines
how pixels are to be unpacked by GL. The parameter buffer_object is the name of a buffer object
to copy from, with format and type specifying how the data is formated,
pixels refers to an offset with the buffer object to copy data from. If buffer_object
is 0, then pixels is a pointer to client address space.

Texture data may also be read back to a buffer object or client address space via:

void GetTextureData1D(uint texture,
                      int x, 
                      sizei width, 
                      uint unpacking_specification,
                      uint buffer_object,
                      enum format, enum type, void *data);
void GetTextureData2D(uint texture,
                      int x, int y, 
                      sizei width, sizei height, 
                      uint unpacking_specification,
                      uint buffer_object,
                      enum format, enum type, void *data);
void GetTextureData3D(uint texture,
                      int x, int y, int z, 
                      sizei width, sizei height, sizei depth,
                      uint unpacking_specification,
                      uint buffer_object,
                      enum format, enum type, void *data);

Additionally, pixel data my be read back from the current framebuffer
by specifying texture_name as 0.

The command:

void UseTexture(uint texture_unit, uint texture);

specifies for the named texture_unit to source data from the named texture, texture. The
texture target where a texture is bound to via UseTexture is determined at allocation
of the texture object and can be queried with

enum GetTextureTarget(uint texture);

The command:

void UseBufferObjectAsTexture(uint texture_unit, uint buffer_object, sizei offset, GLenum format);

specifies for the named texture unit to sample data (i.e. sampleBuffer of GLSL)
from the named buffer object starting at the specified offset, format determines
the format of buffer object to sample from.

Issues

(0) What is the status of this extension?

This extension is a proposal from a developer, not from an IHV, as such should
be taken with a huge grain of salt.

(1) What is the purpose of this extension?

The main purpose of the extension is to provide a means to specify texture
data without needing to be aware of any GL state.

(2) Is the state of the PixelStoreSpecification named 0 affected by command PixelStore?

No. The purpose of the PixelStoreSpecification named 0 is to provide a “default”
way for data to be packed and unpacked by GL. Additionally, by having that object
affected by PixelStore commands, then the interpretation of TextureData depends on
the current GL state, which this extension is aiming to remove.

(3) What is the point to UseBufferObjectAsTexture?

It’s main purpose is for a developer to see directly in code the source of data.
Additionally, it also provides an offset into the buffer object to… one can argue
that one can add such to GL_EXT_texture_buffer_object as well, but UseBufferObjectAsTexture
removes a layer of (I feel) unnecessary indirection. In brutal honesty, the call
is inspired by NV_shader_buffer_load 's ability to set uniforms to “pointers”.

(4) How does the GenerateMipmap family of functions interact with texture allocated
by the new entry points? I.e. what is the expected behavour of calling GenerateMipmap
on a texture allocated with the new call but with allocated so that mipmaps are not?

Unresolved. There are several options:

i) Textures allocated with one of the new calls can also have
the glTexImage family of functions affect them. The question then
becomes, do GL implementors lose potential performance or does
the GL implementation burden increase?

ii) GenerateMipmap acts as always, i.e. if a texture was allocated
without mipmaps, then GenerateMipmap will allocate and generate them.
This though violates the idea that the texture allocation call
specifies the memory required by the texture object for it’s lifetime.

iii) GenerateMiapmap generates an error for those textures allocated
with the new allocation calls that specify to not allocate mipmaps.
The issue with the solution is that then GenerateMiapmap acts differently
on how the texture was created.

iv) GenerateMiapmap cannot be used with such textures, instead a new call
to generate the mipmaps must be used. This has the advantage of being
self consistent. The disadvantage is that it again places textures into
2 classes: those allocated with the new calls and those allocated with
TexImage.

(5) Given that the texture target (which is really a texture type) is specified
at allocation, it seems unnecessary that the direct_state_access TexParameter
family of functions need the texture target when manipulating a texture allocated
with one of the new calls.

The simple lazy answer is: “don’t worry about it and use GetTextureTarget” for
such textures. Potentially added new TexParamter calls that don’t require
a texture target parameter.

(6) How does GetTextureTarget behave for those texture names retrieved
from GenTextures that have not been allocated via a TexImage call yet?

Returns GL_INVALID_ENUM.

(7) How does TextureData family of functions behave for such texture
names as asked about in Issue (6)?

Generate an error analogous to calling TexSubImage acting on such textures.
More generally speaking, one can view the TextureData entry points as
a “stateless” version of the TexSubImage calls.

uint AllocateTexture1D(enum target, boolean allocate_mipmaps,
int internalformat,
sizei width);

uint AllocateTexture2D(enum target, boolean allocate_mipmaps,
int internalformat,
sizei width, sizei height);

uint AllocateTexture3D(enum target, boolean allocate_mipmaps,
int internalformat,
sizei width, sizei height, sizei depth);

uint AllocateTexture2DMultiSample(enum target, sizei samples,
int internalformat,
sizei width);

uint AllocateTexture3DMultiSample(enum target, sizei samples,
int internalformat,
sizei width);

If you’re going to effectively rewrite OpenGL texture code, it seems a terrible waste to keep things exactly the same. Particularly the terribly unnecessary 1D/2D/3D calls.

If you’re going this route, rather than the Direct State Access route of just porting functions, it’d be better to do this:


uint glCreateTexture(enum target, enum format sizei width, sizei height, sizei depth, uint samples);

Just have one function. The target parameter will tell the function which other parameters matter. If you’re creating a GL_TEXTURE_2D_MULTISAMPLE, then width, height and samples matter.

Similarly, functions that take a texture should not need a target parameter. The whole notion of a “target” should be excised outside of the texture’s creation. That’s simply the texture’s type.

I’m not a fan of the binary choice for mipmaps; I’d prefer a range.

Also, you forget functions for compressed texture uploading, which requires special handling since it comes pre-formatted.

If you are insistent on wrapping the pixel pack/unpack stuff into an object, consider folding the “format” and “type” parameters that the texture upload/download functions take. Though personally, I’d prefer some form of immutable object creation. Where the object creation takes an array containing the parameter data the way, the way that wglCreateContextAttribs does.

The main purpose of the pixel pack/unpack objects is to avoid the most common source of texture image specification in a GL application: packing of data. Folding the format and type is NOT a good idea, as it hides the data conversion.

I’m not a fan of the binary choice for mipmaps; I’d prefer a range.

HUH? Why in the world would you want only some of the mipmaps allocated and not all? Mipmap completion is an all or none affair. You could make a very weak argument with texelFetch in naming the LOD, but it is a pretty weak argument.

Similarly, functions that take a texture should not need a target parameter. The whole notion of a “target” should be excised outside of the texture’s creation. That’s simply the texture’s type.

HUH? The texture target specifies the type of the texture, so it had better be there when the texture is allocated, just as in the TexImage calls. Additionally, the texture type likely influences how GL is to store a texture as well.

Also, you forget functions for compressed texture uploading, which requires special handling since it comes pre-formatted.

In truth I did not “forget”, rather I deliberately left it out as I am not too sure what is the best way to proceed. One option is to, like the current GL, add 3 new calls to allocate compressed textures: CreateCompressedTexture1D, CreateCompressedTexture2D and CreateCompressedTexture3D which takes parameters specifying the compressed texture data as well, i.e. CreateCompressedTexture allocates and sets the data, one can also argue to add such a function for other textures too: CreateTexture1D, CreateTexture2D and CreateTexture3D, but that rubs against me a great deal: a function is now doing two thinges: allocating and setting, though it is arguable that is perfectly fine too. Debatable both ways :cool:

f you’re going to effectively rewrite OpenGL texture code, it seems a terrible waste to keep things exactly the same. Particularly the terribly unnecessary 1D/2D/3D calls.

If you’re going this route, rather than the Direct State Access route of just porting functions, it’d be better to do this:


uint glCreateTexture(enum target, enum format sizei width, sizei height, sizei depth, uint samples);

Epic HUH? Firstly I am not “effectively rewrit[ing] OpenGL texture code”, as for the above, it is okay-ish until you talk about mipmap allocation, so then one would have:


uint glCreateTexture(enum target, enum format sizei width, sizei height, sizei depth, uint samples, bool generate_mipmaps);
//or give a range of lod's to generate in place of a bool, gross.

But that just smells bad, as there are more and more icky rules to determine if the call is valid, besides how readable is this:


tex=glCreateTexture(GL_TEXTURE_1D, GL_RGBA8, 100, 1, 1, 1, GL_TRUE);
//vs
tex=glAllocateTexture1D(GL_TEXTURE_1D, GL_TRUE, GL_RGBA8, 100);

I’d make a bet the second is epically easier to read.

One thing I epically hate: one function doing many things with more and more complicated rules to determine if the arguments are valid. Much easier to implement and use in having a family of functions, hence the 1D, 2D, 3D. The rules are simpler and at any rate, epic chance that any GL implementation has to make a switch statement anyways to an internal call. By making separate calls each with a more narrowly defined purpose, the GL implementation is not going to be any harder and the developer has an easier time too.

Firstly I am not “effectively rewrit[ing] OpenGL texture code”, as for the above

Of course you are. You specifically state that it is an error to use objects created with your API as textures in the regular OpenGL API and vice-versa. This is a separate path that uses its own APIs. It does not create OpenGL texture objects, because OpenGL texture objects can be bound with glBindTexture. This proposal creates a new object type that cannot be used the way regular texture objects do.

You are making a new API to create and manage a new object that is in no way interchangeable with other OpenGL objects. I don’t know how that can be called anything other than rewriting OpenGL’s texture code.

Why in the world would you want only some of the mipmaps allocated and not all? Mipmap completion is an all or none affair.

Mipmap completion is a controlled by the base level/max level settings. And those can very much be changed.

The reason to want only some of the mipmaps is so that you can load the lower mipmaps first, then load the higher ones in as you stream them.

The texture target specifies the type of the texture, so it had better be there when the texture is allocated, just as in the TexImage calls.

You may have missed the part where I said, “outside of the texture’s creation”. I was referring to the use of the target in the TextureParameterIivEXT calls.

I’d make a bet the second is epically easier to read.

Well, the second call contains the same information twice. You’re allocate a 1D texture of type GL_TEXTURE_1D.

No. You allocate a texture of type GL_TEXTURE_1D. You only need to say it is a 1D texture once.

A counter-example. Which makes more sense:

tex = glCreateTexture(GL_TEXTURE_CUBE_MAP, GL_RGBA8, 256, 256, 0, 0, GL_TRUE);
or
tex = glAllocateTexture2D(GL_TEXTURE_CUBE_MAP, GL_TRUE, GL_RGBA8, 256, 256);

I’d say it’s the one that isn’t trying to confuse 2D texture creation with cube map texture creation. Same goes for using glAllocateTexture3D for array textures. It’s one of those places in the API where they did the wrong thing just to have fewer entrypoints. They didn’t make a glTexImageCubeMap because it would have had the same interface as glTexImage2D. So they just overloaded it.

I’m simply taking it to its logical conclusion. They concatenated glFramebufferTexture1D, 2D, 3D, and Layer all into a single glFramebufferTexture call (except for when you don’t want to use layered rendering).

Speaking of cube maps, you forget a way to upload data to the different faces of a cube map.

One thing I epically hate: one function doing many things with more and more complicated rules to determine if the arguments are valid.

You must really hate OpenGL, because they do that everywhere :wink:

Much easier to implement and use in having a family of functions, hence the 1D, 2D, 3D.

But there already are a family of functions behind the scenes. Cubemaps certainly do not use the same allocator as 2D textures, even though they’re created with glTexImage2D. Same with rectangle textures. The functions have already been overloaded, so you may as well do it fully.

Well, the second call contains the same information twice. You’re allocate a 1D texture of type GL_TEXTURE_1D.

No. You allocate a texture of type GL_TEXTURE_1D. You only need to say it is a 1D texture once.

A counter-example. Which makes more sense:


tex = glCreateTexture(GL_TEXTURE_CUBE_MAP, GL_RGBA8, 256, 256, 0, 0, GL_TRUE);
or
tex = glAllocateTexture2D(GL_TEXTURE_CUBE_MAP, GL_TRUE, GL_RGBA8, 256, 256);

The first call is fishy: 0 for samples and 0 for depth?? A cubemap texture is
a 3D texture with depth 6 so the second (correct) call should be


tex = glAllocateTexture3D(GL_TEXTURE_CUBE_MAP, GL_TRUE, GL_RGBA8, 256, 256, 6);

Mipmap completion is a controlled by the base level/max level settings. And those can very much be changed.

I do conceded that those levels change, but lets be honest, how often does anyone really do that? At any rate, allocating the data just means allocating it, if you change the base/max levels that just means you will not refer to uninitialized allocated memory.

Speaking of cube maps, you forget a way to upload data to the different faces of a cube map.

A cubemap is a 3D texture, it is an array (of 6) 2D textures. Allocating and specifying a cubemap texture would be done via the 3D calls.

You must really hate OpenGL, because they do that everywhere

Drifting into flame/troll territory here.

But there already are a family of functions behind the scenes. Cubemaps certainly do not use the same allocator as 2D textures, even though they’re created with glTexImage2D. Same with rectangle textures. The functions have already been overloaded, so you may as well do it fully.

You miss my point, which is not shocking. If it is a non-trivial task to check that a combination of arguments is valid, then that case of overloading makes life harder for the developer and possibly the implementor.

I’m simply taking it to its logical conclusion. They concatenated glFramebufferTexture1D, 2D, 3D, and Layer all into a single glFramebufferTexture call (except for when you don’t want to use layered rendering).

AND they kept the 1D, 2D and 3D calls too! It is a debatable point to potentially also provide a AllocateTexture and TextureData calls without a dimension suffix that checks the dimension values against the texture type. The main use case though is for middleware template C++ code though.

You may have missed the part where I said, “outside of the texture’s creation”. I was referring to the use of the target in the TextureParameterIivEXT calls.

See issue (5).

You are making a new API to create and manage a new object that is in no way interchangeable with other OpenGL objects. I don’t know how that can be called anything other than rewriting OpenGL’s texture code.

Could you freaking read it a touch closer?? In it says that it is not clear if these texture objects should be regarded as different or the same. I freely admit to tweaking the original post to edit’s, but before your bile.

Lastly Alfhonse, you really need to quit being this way, it takes effort to sift through your posts to find something of value, there was one or two, but beyond that it is just seems that your posts are by someone that has to say something, anything to criticize. Genuine constructive criticism is a good thing, but much of what come out of you is just noise.

Lastly Alfhonse, you really need to quit being this way, it takes effort to sift through your posts to find something of value, there was one or two, but beyond that it is just seems that your posts are by someone that has to say something, anything to criticize. Genuine constructive criticism is a good thing], but much of what come out of you is just noise.

Speak your reader language kRogue.

Lets not let my temper :o at Alfhonse ruin this thread. I must be some kind of online-social misfit, I can’t tell if you think my behavior is acceptable or unacceptable :o … or worse, that what I wrote in that last bit falls under exactly what it said… shudders… self referring irony.

Sorry, I am spoiling your post… Looks like my today’s mood. I don’t even think anything about Alfhonse being an a****** all the time neither than being an a****** is fundamentally wrong… actually on contrary. Anyway, it certainly [censored] people off.

Sorry again, I am off spoiling…

My notes about the extension (and Alfonse’s comments :wink: ):

  1. I agree with Alfonse and we should try to depart from the several commands for 1D, 2D, 3D, etc. so rather have a generic texture creation function.

  2. I disagree with Alfonse statement about the type and format arguments to be included in the pixel pack/unpack objects. I agree that these information are somewhat related but type and format changes much more often than pixel unpack rules so I would not tie them together.

  3. Agree with Alfonse about mipmap generation. It should not be an all or nothing decision as playing with base and max levels you can spare some memory.

  4. I think this could be a great extension as this is the one major problem that the DSA extension doesn’t even seem to care about. So thanks for the proposal!

  5. This one is a minor, subjective and cosmetic note: I would change the names as they don’t really fit into the GL language (actually this is true also for some of the functions introduced by the DSA extension) and not use too long words like “Specification” :slight_smile:

I am trying to think of a good rule/something on the mipmap allocation issue. One of the core ideas of the extension was that allocation happens at creation, so that included mipmaps. If we want to change what mipmaps are to be allocated, then there are a few choices:

[ul][li] Pass a maximum and minimum mipmap level to allocate[/li]OR[li] Allow for texture data to be re-allocated after creation.[/ul] [/li]
I freely admit I do not like the second option, but for potentially just because I am stubborn :o With that in mind, lets look how the spec would then look like using the second option. Doing the second option then means that AllocateTexture is essentially GenNames and TexImage rolled into one call, passing NULL as pixel data (and not having a buffer object bound to PIXEL_WHATEVER). That has the advantage that it completely removes the difference between textures made with the new calls and textures made the old style of calls. In terms of consistency that is great. Though I am still beyond hesitant in having the memory allocated for a texture changing. Additionally the memory savings of not allocating mipmap levels seems tiny… after all, a full pyramid of mipmaps only increases the total memory consumption by (for 2D textures) 33%, and the first mipmap by itself is 25% (for 2D textures) so not allocating all the levels once you allocate the base texture and mipmap level 1 is quite small, at 8% the size of the base texture (for example for a 1024x1024 RGBA8, we are now talking 335KB savings once the base and mipmap level 1 are allocated which together take up 5248KB).

On the issue of the overloading, I can see the want for the overloaded calls, so I see no harm in adding them, but I definitely want to keep the not-so-overloaded calls too :slight_smile:

This one is a minor, subjective and cosmetic note: I would change the names as they don’t really fit into the GL language (actually this is true also for some of the functions introduced by the DSA extension) and not use too long words like “Specification”

My names most definitely do suck.

I will most likely wait a few days and see the comments (and flames) that collect here and post a version from that feedback.

A cubemap texture is a 3D texture with depth 6 so the second (correct) call should be

That’s even more confusing that calling glTexImage2D. A cubemap texture is no more a 3D texture than it is a 2D texture. It is its own texture type which is fundamentally different from any other texture type. And while yes, it certainly can be used in layered rendering, and it may actually be implemented these days as a modified form of a 3D texture, to the user, it has nothing to do with the concept of a 3D texture.

Also, I would point out that calling it a 3D texture violates your own rule about function parameters. Calling glAllocateTexture3D(GL_TEXTURE_CUBE_MAP, …) with anything except a 6 as the depth is an error. So if you don’t like functions that change the meaning and validity of parameters based on other parameters, then by your own rules, this is a bad function and must be split into a separate glAllocateTextureCubeMap call :wink:

So basically, you’re going to have parameters who’s validity depend on other parameters one way or the other. At least with one function, it’s simple and direct.

The principle reason for my opposition to having multiple functions lies here. I wrote that page. And in doing so, I had to explain the convoluted logic that says that cubemaps, though they are a distinct texture type, must be uploaded with glTexImage2d just like 2D textures.

Here is what my explanation would have been like with one function:

One paragraph and a simple table is all it takes. It is easily understood by anyone who reads it.

Having both sets of functions is rather antithetical to this utility.

Additionally the memory savings of not allocating mipmap levels seems tiny… after all, a full pyramid of mipmaps only increases the total memory consumption by (for 2D textures) 33%, and the first mipmap by itself is 25% (for 2D textures) so not allocating all the levels once you allocate the base texture and mipmap level 1 is quite small, at 8% the size of the base texture (for example for a 1024x1024 RGBA8, we are now talking 335KB savings once the base and mipmap level 1 are allocated which together take up 5248KB).

It depends on what part of the mipmap pyramid you’re not allocating. As you point out, the lion’s share of the data is in the upper mip levels.

When you’re doing serious texture streaming, you load the low levels first. That way, you can draw something even if it all isn’t there yet. Once those are all in, then you load the big mip levels.

The real question that needs to be answered is this:

What do OpenGL implementations do when the first glTexImage call is not for the 0 mipmap level?

For example, if I allocate a 64x64 texture as level 1, does that automatically cause the allocation of a 128x128 texture and the full mipmap pyramid?

I don’t think it can. If I recall the spec correctly, thanks to NPOTs, a 129x129 texture at the 0 mipmap level can have a 64x64 level 1. So the OpenGL implementation cannot know yet whether you want a 129x129 or 128x128 (or 129x128, etc) texture at the 0 mipmap level. So it can’t really allocate anything but all of the lower mipmaps.

It is entirely possible that drivers simply guess. So it would pick 128x128. And if later you try to put a 129x129 in there, it will stop and do some unfortunate reallocation gymnastics behind the scenes.

If that is indeed the case, then the API most certainly should not expose a range. That would be making a promise that could not be kept. But if that is not the case, if it is widely implemented (meaning across ATI and NVIDIA hardware and drivers) that you can have the smaller mipmaps in one area and then allocate a big one without moving things around in graphics memory behind the scenes, then the API should expose that behavior.

So it’s really predicated on things we don’t know.

It is entirely possible that drivers simply guess. So it would pick 128x128. And if later you try to put a 129x129 in there, it will stop and do some unfortunate reallocation gymnastics behind the scenes.

If that is indeed the case, then the API most certainly should not expose a range. That would be making a promise that could not be kept. But if that is not the case, if it is widely implemented (meaning across ATI and NVIDIA hardware and drivers) that you can have the smaller mipmaps in one area and then allocate a big one without moving things around in graphics memory behind the scenes, then the API should expose that behavior.

Though I freely admit that I do not have first hand data, I would expect that a driver would want to keep the mipmap data allocated near to each other, since the typical use case has neighboring pixels potentially use different LOD’s. However, the point is this: I polish this up, take into account for user feedback, and then hopefully later an IHV sees the spec, then takes a look at it, takes a look at their driver and proceeds to make their own spec which in turn hopefully maybe finds it’s way to GL core after much debate by those that have been dealing with GL and implementation of GL for the longest of times.

For the texture streaming use case you describe, the developer already knows how big the textures need to be. So here separating allocation and image data setting works well here. One could allocate, but not set the data, and set the max and base lod accordingly. I have to admit, I find it awfully fishy to not want to allocate image level 0, at which point why not just make the texture base size the size of the first mipmap you are going to use?

For the mipmap allocation, I would just like to see a real use case where only allocating a range of mipmap levels instead of all makes a real difference. One simple way out is to add another API entry point:


void
AllocateMipmapLevel(uint texture, int mipmap_level);

which would allocate the named mipmap level, if that mipmap_level is already allocated then the function is a no-op. I want to avoid having TexImage calls interact, since such calls allocate, and one motivation for this thing was to have it so that the memory needed for a texture is determined in one call, with that in mind, then the suggested function, AllocateMipmapLevel, is not a good idea either :whistle:.

To repeat the question: are there any use cases where one does only wishes to allocate a range of mipmaps instead of all or none of them? Keep in mind that the proposal breaks up allocation and setting of texel values as two separate calls. I just keep thinking that that once you have have the base image and the first mipmap level, you have already allocated (for 2D textures) over 93% of the memory used vs allocating all mipmaps (for 1D textures it is 75% and for 3D textures it is over 98%). If one wants to start the pyramid base at a level besides zero, why not just make the image size the dimensions of that level? These are my thoughts for the all or none mipmap allocation.

So basically, you’re going to have parameters who’s validity depend on other parameters one way or the other. At least with one function, it’s simple and direct.

Or more like for one function has more bits to check, like I posted before it is debatable. Additionally, having arguments that are sometimes ignored smells bad. At any rate, like I said before, I will be adding a dimensionless overloaded call, but I will also keep the dimensioned calls, since in production code, that is more readable. This spec does NOT add any real functionality anyways, just as direct_state_access does not really, it essentially makes the API more manageable and calls understandable without needing to know any global state. For what it is worth I debated having a dimensionless call which takes a pointer to dimensions and a number of dimensions:



uint
AllocateTexture(enum texture_target, bool generate_mipmaps, enum internal_format, 
                sizei number_dimensions, const sizei *dimensions);

void
TextureData(uint texture, 
            uint mipmap,
            sizei number_dimensions, 
            const int *region_location, const sizei *region_size,            
            uint packing_specification,
            enum format, enum type, 
            uint buffer_object,
            const void *pixels);

void 
GetTextureData(uint texture,
               sizei number_dimensions, 
               const int *region_location, const sizei *region_size,  
               uint unpacking_specification,
               uint buffer_object,
               enum format, enum type, void *data);



Even though such an API points are more future proof-ish in terms of if texture data ever goes beyond 3 dimensions, I doubt the usefulness of such an API point and that just looks so awkward to me :smiley:

That’s even more confusing that calling glTexImage2D. A cubemap texture is no more a 3D texture than it is a 2D texture. It is its own texture type which is fundamentally different from any other texture type. And while yes, it certainly can be used in layered rendering, and it may actually be implemented these days as a modified form of a 3D texture, to the user, it has nothing to do with the concept of a 3D texture.

Maybe to you ;). A cube map texture is a 3D texture, it is 6 2D textures in layers. Looking at the traditional GL calls, the 1D, 2D, 3D are about the number of dimensions of an array, that array being the raw texture data. How that data is used is determined by the texture target type. With that in mind, a cube map texture is a 3-dimensional array of pixel with dimension NxNx6. Likewise a cube map array is a 3-dimensional array with dimensions NxNx(6L) where L is number of layers.

Also note that the Get calls of the proposal can read from the currently bound FBO, which means one can select from which render layer to read.

No, it isn’t, not at all. You don’t seem to understand what the difference between a 2D texture and a 3D texture really is. In particular, think about what the mipmap levels mean for a 3D texture, or more specifically what magnification/minification means for a 3D texture. You’ll quickly realize that a cubemap is not in any way at all equivalent to a 3D texture.

What does make sense is to equate a cubemap with a texture array of 6 2D textures. That is in fact exactly how D3D 10 does it, which indicates that’s exactly how hardware actually works with cubemaps these days.

I would do away with the let’s pass many parameters to “CreateTexture”.
For example :


void TextureData2D(uint texture,
uint mipmap,
int x, int y, sizei width, sizei height,
uint unpacking_specification,
uint buffer_object,
enum format, enum type, const void *pixels);

would turn into


GLTextureObject1 object;
object.Version=GL_TEXTURE_STRUCT_VERSION_1;
object.Type=GL_TEXTURE_2D;
object.TextureID=0;
object.MinMipmap=0;
object.MaxMipmap=0;
object.Width=256;
object.Height=128;
object.Pixels=NULL;
object.otherstuff=otherstuff;
glCreateTexture(&object);

The driver checks object.Version and it will know what variables to expect. Later version of GL can have newer versions like GLTextureObject2 and GLTextureObject3, etc.

You can also have a function to allocate mipmaps, one by one


AllocateMipmap(&object, 5); //Allocate mipmap 5
AllocateMipmap(&object, 4); //Allocate mipmap 4

object.MinMipmap and object.MaxMipmap get updated by the driver. The driver can also store whether the texture state is valid.

It needs more work, but I prefer the passing of a structure.

OpenGL API does not like C-like structures. Mostly for portability issues. But something like this would probably be feasible:


  GLint params[] = {
    GL_TEXTURE_WIDTH, w,
    GL_TEXTURE_HEIGHT, h,
    GL_TEXTURE_INTERNAL_FORMAT, i,
    ...
    0 };
  glCreateTexture(textureObject, GL_TEXTURE_2D, params);

What portability issues? I imagine that some compilers might pad the structure which can be turned off in the compiler.

Also, why is there 1D textures? That seems useless because they are basically a 2D texture. If we are going to have 1D texture, then why not have 0D texture whose dimension is 0. It is a singularity, a hole in space and time. If you try to access it, you get sucked into it.

Why have 2D textures if you already have 3D textures?

Because it saves an interpolator.

No, it isn’t, not at all. You don’t seem to understand what the difference between a 2D texture and a 3D texture really is. In particular, think about what the mipmap levels mean for a 3D texture, or more specifically what magnification/minification means for a 3D texture. You’ll quickly realize that a cubemap is not in any way at all equivalent to a 3D texture.

I likely should have been more clear and succinct: when specifying only mipmap level 0 (and letting glGenerateMipmaps generate all other mipmaps), then from a data point of viewer, a 3D texture, a 2D texture array and a cube map texture are the same, a 3-dimensional array. Going one step further to specifying mipmaps, for both 2D texture arrays vs cubemap textures, it is the dimensions of the mipmaps that are not the same: for a “usual 3D texture” all dimensions are cut in half, for the 2D texture arrays only the first two dimensions are (and likewise only the 1st dimension for 1D texture arrays). So even looking at setting the image data of mipmaps, the API points are the same, only the rules for the expected values is different.

What I really had in mind was exactly what you said at the end:

What does make sense is to equate a cubemap with a texture array of 6 2D textures. That is in fact exactly how D3D 10 does it, which indicates that’s exactly how hardware actually works with cubemaps these days.

Going on:

My main worry is not in the realm of 32bits, but in the realm of 64bits… if memory serves correctly what is meant by long depends on the compiler in the 64bit world (though once can say use int32_t, etc instead). Naturally padding is a compiler specific thing, which can be a big deal on some boxes. Under x86, unaligned access is ok (but horrible slow) where as for ARM unaligned access crashes (with bus error if memory is correct).

Lastly, by not giving a structure, but doing it the X11 way one gains the following:
[ul][li]default values to unspecified arguments[*]one can add entries without updating any header files[/ul][/li]
Though I freely admit that it sucks to use the X11 way.

Saves what?

What do you mean by “long” and 32 bit and 64 bit?
A structure is a structure in C and C++. Ditto for VB and Java.

Default values is not a good idea. It is lazy programming, IMO.

Structures is the DirectX way and it is pretty good (although they don’t use it everywhere).
Even if you aren’t doing 3D programming, most likely a program will have a structure to group together values for something. You will likely pass your object to some function for processing.