Search code examples
glslwebgl2

Is it safe to assume that unset sampler2D uniforms will read texture unit 0?


I have multiple textures, one of which gets bound to TEXTURE0 before drawing.

In my fragment shaders, I have a single sampler2D uniform such as:

uniform sampler2D diffuse;

I used to call uniform1f([uniform location], 0) every frame, every shader to explicitly set the uniform to read texture unit zero but, after removing those calls, my app still works on the Linux, Windows, and Mac systems I tested. This is not conclusive evidence that not setting the uniform is safe, hence the question: Is it safe to assume that unset sampler2D uniforms will read texture unit 0?

The motivation for removing the uniform1f calls was to reduce draw calls, which are especially expensive when working in webassembly because there is an additional overhead to call javascript.

I didn't see any mention of a default in the spec, aside from a user settable default that is only supported in opengl 4 (not WebGL 2). I should note that there were no errors in console after the change.


Solution

  • Is it safe to assume that unset sampler2D uniforms will read texture unit 0?

    Yes it is safe.

    Uniforms default to 0 in WebGL and WebGL2

    From the spec section 2.12.16

    When a program is successfully linked, all active uniforms belonging to the program object’s default uniform block are initialized: to 0.0 for floating-point uniforms, to 0 for integer uniforms, and to FALSE for boolean uniforms

    There are tests for this in the conformance tests and workarounds in the browsers for bad drivers.

    And while we're at it attributes default to 0, 0, 0, 1.

    Section 2.8

    The initial values for all generic vertex attributes are (0.0, 0.0, 0.0, 1.0).

    Both features I've used in nearly every program I've written since WebGL shipped in 2011 and continue to use in WebGL2

    As for reducing WebGL calls, it seems unlikely that not setting a uniform to 0 is going to be the difference between performant and not-performant. If the CPU is your bottleneck (many GPU based programs the GPU itself is the bottleneck) and if the bottleneck is in calls to WebGL then things you can do

    • Don't do anything at render time you can do at init time. For example look up uniform locations at init time.

    • don't change attributes at render time, setup vertex array objects at init time.

    • don't use sampler objects if you don't need to. Hundreds of thousands of apps shipped without them as they didn't exist until WebGL2/OpenGL ES 3.0

    • Use uniform buffer objects. Ideally you probably want to split uniform buffer objects into groups like (1) things shared among all shaders for example the view, projection, viewProjection matrices, any camera info, etc. (2) things shared by many objects for example lights and material settings (3) things that are object specific. For the things that don't change per model that means you can set them all with 1 webgl call per uniform buffer object.

    • Consider batching solutions like instanced drawing or texture based batching.