floooh / sokol

minimal cross-platform standalone C headers
https://floooh.github.io/sokol-html5
zlib License
6.93k stars 487 forks source link

Help: Sampler type on per-texture basis #956

Closed danielchasehooper closed 9 months ago

danielchasehooper commented 9 months ago

We have a shader with 12 texture2Ds we use to batch draws of different textures.

The shader in question

```glsl uniform texture2D texture0; uniform texture2D texture1; uniform texture2D texture2; uniform texture2D texture3; uniform texture2D texture4; uniform texture2D texture5; uniform texture2D texture6; uniform texture2D texture7; uniform texture2D texture8; uniform texture2D texture9; uniform texture2D texture10; uniform texture2D texture11; uniform sampler layer_sampler; in vec2 coord; in vec2 uv; in vec2 half_size; in float cornerRadius; in float samplerIndex; out vec4 FragColor; void main(void) { vec4 image_color; if (samplerIndex < 0.5) {image_color = texture(sampler2D(texture0, layer_sampler), uv);} else if (samplerIndex < 1.5) {image_color = texture(sampler2D(texture1, layer_sampler), uv);} else if (samplerIndex < 2.5) {image_color = texture(sampler2D(texture2, layer_sampler), uv);} else if (samplerIndex < 3.5) {image_color = texture(sampler2D(texture3, layer_sampler), uv);} else if (samplerIndex < 4.5) {image_color = texture(sampler2D(texture4, layer_sampler), uv);} else if (samplerIndex < 5.5) {image_color = texture(sampler2D(texture5, layer_sampler), uv);} else if (samplerIndex < 6.5) {image_color = texture(sampler2D(texture6, layer_sampler), uv);} else if (samplerIndex < 7.5) {image_color = texture(sampler2D(texture7, layer_sampler), uv);} else if (samplerIndex < 8.5) {image_color = texture(sampler2D(texture8, layer_sampler), uv);} else if (samplerIndex < 9.5) {image_color = texture(sampler2D(texture9, layer_sampler), uv);} else if (samplerIndex < 10.5) {image_color = texture(sampler2D(texture10, layer_sampler), uv);} else if (samplerIndex < 11.5) {image_color = texture(sampler2D(texture11, layer_sampler), uv);} FragColor = image_color; } ```

we recently wanted to start dynamically generating mipmaps for individual textures when we detect one is being drawn scaled down. so we have a situation now where the 12 texture slots could be bound to a random combination of linear and mipmapped textures.

we can't set it to use a mipmap sampler for all textures, because we get black rects if we use a the below sampler on non-mipmapped textures

sg_make_sampler(&(sg_sampler_desc){
        .mag_filter = SG_FILTER_NEAREST,
        .min_filter = SG_FILTER_LINEAR,
        .mipmap_filter = SG_FILTER_LINEAR,
        .wrap_u = SG_WRAP_CLAMP_TO_EDGE,
        .wrap_v = SG_WRAP_CLAMP_TO_EDGE,
    });

So we're wondering how we should go about this. In pre-sampler OpenGL, this isn't a problem because some textures could be marked as mipmapped, while others could be set to linear and the shader code was none-the-wiser. Whats the equivalent of this in the new sampler api?

We are considering three approaches:

  1. reduce the max textures from 12 to 8, and have a sampler slot per texture. We're unsure if having a different sampler binding combination per draw will cause performance issues. (does the gpu driver have to recompile shader when samplers change?)
  2. somehow dynamically select between two samplers per-texture like samplerattribute > 1 ? texture(sampler2D(texture11, mipmap_sampler), uv) : texture(sampler2D(texture11, linear_sampler), uv) but have yet to get sokol validation to accept this. we keep getting VALIDATE_SHADERDESC_IMAGE_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS: shader stage: one or more images are note referenced by (sg_shader_desc.vs|fs.image_sampler_pairs[].image_slot)
  3. only use one sampler for all textures, and break up batches when we need to queue up a texture with a sampling type that doesn't match the textures that are already queued

Any input or advice would be appreciated!

floooh commented 9 months ago

Do you have the fix from 27-Oct which sets GL_TEXTURE_MAX_LEVEL in the GL backend (https://github.com/floooh/sokol/blob/master/CHANGELOG.md#27-oct-2023)? I was hoping that this relaxes the filtering requirements for textures with and without mipmaps. At least it didn't require anymore to set the mipmap-filter explicitly to NONE for textures without mipmaps.

danielchasehooper commented 9 months ago

Hard to tell since sokol_gfx.h doesn't seem to have a version number, release date, or commit id in it anywhere. I diffed it with the sokol_gfx.h on master and the changes I saw didn't seem to be related to filtering, so I think that means I have the changes you mentioned. Would be helpful if the headers and the tools were versioned. https://github.com/floooh/sokol-tools/issues/105

That info is helpful though - We inject all our gl textures when creating the sokol resources but we weren't setting GL_TEXTURE_MAX_LEVEL to 0 for non mipmapped textures. Setting that fixed the black rendering and we're now able to draw the whole batch with the mipmap sampler. Thanks for the insight!