Lookups on incomplete textures

In fixed-pipeline GL, if a texture unit is bound to an incomplete texture then it is as if texturing was disabled. Is there a way to find out inside a fragment shader if the sampler I’m about to pass to texture2D points to a valid texture? What I want to do is this: instead of using just a texture I want to use a material/texture pair where the material color modulates the texture color. The problem is that if I load the material color into the diffuse material color, bind the texture to a sampler and use the fragment shader below:

...
uniform sampler2D texture;

void main (void)
{
    ...
    gl_FragColor = gl_FrontMaterial.diffuse * texture2D(texture, gl_TexCoord[0].st);
}

then all works well as long as there’s a complete texture bound to the sampler but if an incomplete texture (say 0) is bound then the texture2D() call returns (0, 0, 0, 1) and so the fragment becomes completetly black. The problem is that I want the fragment color to be equal to gl_FrontMaterial.diffuse when no texture is bound to the sampler (which was the casewith the fixed pipeline and the MODULATE texture function). I can not know if texture2D returns (0, 0, 0, 1) because the texture is black or because no texture is bound but is thereany other way to do this in GLSL apart from keeping track myself using a uniform boolean flag?

The problem is that I want the fragment color to be equal to gl_FrontMaterial.diffuse when no texture is bound to the sampler (which was the casewith the fixed pipeline and the MODULATE texture function).
A shader is not told whether or not a texture is bound to a texture unit. You’ll just have to use a new shader when you haven’t bound that texture.

I thought about that. Would it be best to have different shaders for each job (where I’d have to rebind shaders a lot) or defining a boolean like:

uniform bool b;
uniform sampler2D texture;

void main()
{
    if(b)
        gl_FragColor = gl_FrontMaterial.diffuse * texture2D(texture, gl_TexCoord[0].st);
    else
        gl_FragColor = gl_FrontMaterial.diffuse;
}

and loading the boolean with the correct value before rendering?

NO! If statements are -slow-. Use some sort of preprocessor selection (#ifdef) and compile it to two different programs.

But this can quickly get out of hand. If a have n material/texture pairs for a given program then I need to have 2^n vaiations of this program depending on which textures will actually be present. Won’t the program binding cost start to get high? I suppose n won’t become too big (2 or 3 probably) but still, is there no other way?

But this can quickly get out of hand. If a have n material/texture pairs for a given program then I need to have 2^n vaiations of this program depending on which textures will actually be present. Won’t the program binding cost start to get high? I suppose n won’t become too big (2 or 3 probably) but still, is there no other way?
Anything you come up with in the fragment program would require a conditional statement. Conditionals on 6xxx nVidia hardware are not terribly slow (especially if all your fragments take the same path, as they would in this case). However, on all other hardware, the program will have to execute both and only store the results for the one that passed. If you care about supporting such hardware, avoid conditionals.

Now, it may be that the conditional cost isn’t so bad (compared to more texture/program bindings). The cost, of course, isn’t the conditional itself; it’s the cost of running both sides of the conditional. The key, therefore, is to put as little code into the conditionals as possible. Do all of the common math beforehand, and only put the texture access and that math into the conditional block.

The only way to know for sure is to test it.

Originally posted by zen:
But this can quickly get out of hand. If a have n material/texture pairs for a given program then I need to have 2^n vaiations of this program depending on which textures will actually be present.
If you’re dealing with material/texture pairs, there are only two possibilities: either the texture is valid or it’s not. It should be easy enough to check this and choose one out of two different shaders accordingly.

Things do get more nasty if you were to have multiple textures per material. In this case, it might be easier and faster to just replace the invalid textures with a single-pixel white texture at load-time.

– Tom

So doing something like:

color = gl_FrontMaterial.diffuse;
if(mapped)
    color *= texture2D(texture, gl_TexCoord[0].st);

gl_FragColor = color;

would probably be not that bad right? But according to what Korval says it wouldn’t save me the texture lookup, so I might as well do what Tom says and save me the trouble to set the mapped flag according to whether the texture is valid or not. How expensive is a NEAREST texture lookup anyway?

Don’t use any conditionals (expensive). Don’t switch shaders (expensive). Instead, just bind a 4x4 texture that’s all white when you don’t want to use a specific texture unit, and set filtering to NEAREST. Pipeline happy; problem solved.

If you have a texture that’s incomplete, and you don’t KNOW it’s incomplete when you bind it, then you need to know more about your textures before you call GL.

Don’t use any conditionals (expensive)
A conditional statement is not expensive intrinsically. The condition statement itself is just an opcode. The problem comes from how the hardware handles the condition:

1: On hardware without actual jump opcodes, it must execute all paths and resolve the results at the end.
2: On hardware with jump opcodes, you can get performance loss if neighboring fragments take different paths.

Either of these cases work just fine for him. Case 1 doesn’t cost anything for him, except for doing the conditional comparison. Case 2 actually saves time by not doing the lookup. Because all fragments of all primitives will be the same (mapped is a uniform, so it doesn’t change unless texture state does), there’s no issue of having to split up the different jumping paths.

Instead, just bind a 4x4 texture that’s all white when you don’t want to use a specific texture unit, and set filtering to NEAREST. Pipeline happy; problem solved.
And texture changes aren’t expensive? Remove probably isn’t so bad, but having to bind these small, but live, textures costs something. Also, sampling from them costs performance too; in theory, hardware can easily handle the case of sampling from nothing.

Besides some texture lookups might require some potentially expensive coordinate calculations (reflections etc.). I suppose it would be best to put these into the conditional since it would save time on nvidia 6xxxx and future hw as opposed to calculating the coordinates and looking up a white texture. Of course using multiple shaders would be cheapest but ,as I have said, this would quickly result in way too many shaders. Actually I’ll probably also have different codepaths where shaders without the lookups (and ifs) will be used for some features for older hardware where possible.

Changing to, and sampling from, a 4x4 white texture has a cost that I can’t really measure, it’s that low. I think they optimize this quite well in current drivers. A 4x4 teture with NEAREST filtering should live entirely in a single texture cache line, too, so it’s all local in the GPU, for next to zero latency.

Of course, your mileage may vary. But if you really want to be able to use the same shader no matter whether you have a texture image or not, the small white texture is likely going to be easiest to implement, and perform as well as any other solution you’ve implemented on the vast majority of hardware. (This is my prediction – I may of course be off)

Why a 4x4 texture instead of a 1x1 texture?

A 4x4 texture is a single DXT1 color block. I upload all my textures as DXT1 or -5, so I don’t think any smaller than that. In all likelihood, a 1x1 would work the same in all respects, at least if it’s not compressed.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.