2 programmable texture access enhancements.

1 - textureComputedLod

When doing some programmable texture access, textureQueryLod is very useful. However, textureQueryLod has a dependency on sampler object state (or texture object state).

The result of textureQueryLod is defined as follow according to the GLSL specification:

float ComputeAccessedLod(float computedLod)
{
    // Clamp the computed LOD according to the texture LOD clamps.
    if (computedLod < TEXTURE_MIN_LOD) computedLod = TEXTURE_MIN_LOD;
    if (computedLod > TEXTURE_MAX_LOD) computedLod = TEXTURE_MAX_LOD;
    // Clamp the computed LOD to the range of accessible levels.
    if (computedLod < 0)
        computedLod = 0.0;
    if (computedLod > (float)
        maxAccessibleLevel) computedLod = (float) maxAccessibleLevel;
    // Return a value according to the min filter.
    if (TEXTURE_MIN_FILTER is LINEAR or NEAREST) {
        return 0.0;
    } else if (TEXTURE_MIN_FILTER is NEAREST_MIPMAP_NEAREST
               or LINEAR_MIPMAP_NEAREST) {
        return ceil(computedLod + 0.5) - 1.0;
    } else {
        return computedLod;
{    

In some cases, this dependency is quite annoying when going programmable as we have to manage the sampler state in the C++ code to ensure that the filtering is performed correctly.

There is a way to by-pass this code by calling:

	
glSamplerParameteri(Sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
glSamplerParameteri(Sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

That the parameter that makes me feel the most comfortable but still, if the idea is to program my texture access, why should I setup a sampler object?

My suggestion is to create a function (textureComputedLod?) that removes the dependency with the sampler object.

2 - textureLevels
Currently with GLSL it is possible to compute the number of mipmaps level of a texture like this:

ivec2 TextureSize = textureSize(Sampler, 0);
int Levels = int(log2(float(max(TextureSize.x, TextureSize.y)))) + 1;

Another, probably cheaper, alternative is to use an uniform to pass the level count.

I wonder if a GLSL function (textureLevels?) would not be appropriate for this duty.

How is this different from second (first?) value returned by textureQueryLod?
Edit: the second one (on y)

That the parameter that makes me feel the most comfortable but still, if the idea is to program my texture access, why should I setup a sampler object?

I imagine the expectation is that you will be accessing the texture in accord with the sampling parameters that the texture uses. So it won’t return values outside of the min/max LOD range, because those are illegal values given the sampling parameters of the texture. And for textures that don’t have mipmap filtering set, then it makes no sense to return LOD values between mipmap levels.

I don’t think it’s meant for you to use for do-it-yourself filtering. It allows you to adjust the LOD with an arbitrary function, rather than with a simple bias value.

How is this different from second (first?) value returned by textureQueryLod?
Edit: the second one (on y) [/QUOTE]

textureComputedLod would just return the value given to ComputeAccessedLod’s parameter and skip the checking.

When I am using, texture* GLSL functions, I expect to use the filtering setup associate to the sampler. When I am using texelFetch, I don’t use them. I am actually happy with textureQueryLod, the way it is specfied, I just would like an extra function that ignore, just like texelFetch, the filtering setup. If the lod is out of boundaries, I can do something about it, it would be interesting for example to know when we are under-sampling of over-sampling to that the rendering technique can deal with these issues in anyway that the programmer see it fit.

textureLevels would cope with the incorrect level access, or at least, allow the programmer to check if the level is for cheaper than int(log2(float(max(TextureSize.x, TextureSize.y)))) + 1;

Wait a minute … im either very confused about query lod or you are suggesting addition of strict subset of textureQueryLod functionality.

How is this different from second (first?) value returned by textureQueryLod?
Edit: the second one (on y) [/QUOTE]

textureComputedLod would just return the value given to ComputeAccessedLod’s parameter and skip the checking.[/QUOTE]
The value passed to ComputeAccessedLod is textureQeuryLod().y


      The returned value is then:

        vec2(ComputeAccessedLod(lambda_prime), lambda_prime);

/!\ I totally missed out the role of the parameter y (and it’s existence actually!)

My mistake, feature request dismissed!

(textureLevels would remain convenient)

More likely what you’d like is really a call to query GL_TEXTURE_MIN_LOD and GL_TEXTURE_MIN_LOD:


int
textureMinLODQuery(gsampler);

int
textureMaxLODQuery(gsampler);


which I’d think should be a small thing for a GL implementation to add.
since the code of doing log2(sampler dimensions) just indicates the maximum
mipmap leve the texture could support, but nothing about what it actually does…

Arrrgghh, not this LOD vs Lod again :wink:

I think it is as cheap to do it yourself (through uniforms). Drivers would probably do just that.

If I follow your reasoning which make me thing that you believe that you think that texture min and max are texture state then, yes but this is in practice a significant increase in the code complexity as this implies a dependence between the texture and the program. It could seems not such a big deal if the higher level of the rendering engine works on a material base, meaning that the material include the programs (if multipass) and the textures and that everything is “rebinded” each time with change the material. However, this is repeating the VAO design mistake which without careful design consideration can introduce dependence between a program (including a the fragment shader!), a set of buffer and a vertex layout… which is not so bad if we consider that every mesh is a program, a vertex layout and a set of buffers and everything is rebinded altogether.

Fortunately with OpenGL 4.1, all the dependence have been broken (if it need to be broken), no need to introduce new one.

However, I have serious doubt about the interest of the texture min and max values as texture min and max are not texture state but sampler states! As the GLSL specification shows when describing textureQueryLod, the max lod value is clamped to the number of mipmap levels. In this condition, the only effect of max is to use a value lower than the mipmaps count with can changed in the shader. This is also true with texture min which is clamp to zero. Also, the base level can change and neither texture min or max reflect this change but the mipmap count change accordingly to the base level. Finally conceptually min and max are sampler state and the scenario I describe dismissed fixed function samplers.

The mipmap count is really all we need and it’s a different thing than texture min and max. As I tried to demonstrate, min and max being sampler parameter, when going programmable they become program state which is effectively uniforms. However this doesn’t allow to compute the mipmap count which is an texture state and its this texture state that I would like to retrieve with textureLevels and I would like to retrieve it for free (without having to compute it), just like the lod value with textureQueryLod (even if we could compute it).

It could seems not such a big deal if the higher level of the rendering engine works on a material base, meaning that the material include the programs (if multipass) and the textures and that everything is “rebinded” each time with change the material.

But that’s how it happens now. Or do you think that binding samplers/textures to texture image units doesn’t take up resources? We don’t know if those resources are entirely external to the program’s state, partially internal to the program’s state, or entirely program state. But it does take up state somewhere.

All he’s saying is that, when you would have simply bound a texture, you also set a couple of uniforms in whatever program(s) you use to render with that texture. And even if you personally do not, the use of any such function likely will simply make the driver do it for you.

Now granted, I think it’s better for the driver to do it for you, since, much like textureSize, it will be faster and more efficient overall. But either way is functionally equivalent, even if it means more work for you the other way.

Currently we can design renderers where programs as no knowledge at all and no requirement to know anything about the texture.

The programmer would have to ensure that the texture target and the sampler type match but in the code itself we don’t have to access to one or the other the setup the renderer. The programmer can make that match by “contract”, in a similar way that attrib location match with array attributes.

For the mipmap count, I don’t think it is reasonable to expect the user to set it using uniform because he would probably have to query the texture object for the base level and the number of mipmaps in memory as the effective mipmap count in the program is “total texture mipmap count” - “texture base level”. The dependence here is obvious and event if manage with ghosting it create a hard dependence with both texture and program objects. Not cool.

Computing ourself the effective number of mipmaps or a textureLevels function seems to me the only way to ensure object independence plus textureQueryLod requires this value already so this value is “in there”, it should be a trivial thing to add.

Arrrgghh, not this LOD vs Lod again

sorry about that, I should have written Lod, but atleast I did not typo all the way to LOL.

I am going to ask a potentially dumb question: why do you want the number of mipmap levels of a texture? GLSL won’t let you use anything outside of the min and max lod’s anyways, and yes what I a suggested is pure sampler state. I totally agree with Groovounet that adding uniforms to get the mipmap counts or texture state is silly and a hack to get around an API short coming.

lol

In the calculation of textureQueryLod().x:

float ComputeAccessedLod(float computedLod)
{
    // Clamp the computed LOD according to the texture LOD clamps.
    if (computedLod < TEXTURE_MIN_LOD) computedLod = TEXTURE_MIN_LOD;
    if (computedLod > TEXTURE_MAX_LOD) computedLod = TEXTURE_MAX_LOD;
    // Clamp the computed LOD to the range of accessible levels.
    if (computedLod < 0)
        computedLod = 0.0;
[i][b]    if (computedLod > (float)
        maxAccessibleLevel) computedLod = (float) maxAccessibleLevel;[/b][/i]
    // Return a value according to the min filter.
    if (TEXTURE_MIN_FILTER is LINEAR or NEAREST) {
        return 0.0;
    } else if (TEXTURE_MIN_FILTER is NEAREST_MIPMAP_NEAREST
               or LINEAR_MIPMAP_NEAREST) {
        return ceil(computedLod + 0.5) - 1.0;
    } else {
        return computedLod;
}

GLSL ensure that the lod value is not more than maxAccessibleLevel but with textureQueryLod().y the lod value isn’t clamp to this maxAccessibleLevel value (which is actually the effective mipmap count) so that pixels becomes black when the sampling rate is to high. Mix and max have another purpose (I guess but I still don’t know which one in practice), they allow to over-sample and under-sample and it’s value is independent from the mipmap count.

Without clamping to the right number of level, some black pixel appears.