textureLod / OpImageSampleExplicitLod ignores explicit LOD with VK_EXT_descriptor_heap

Environment:

  • NVIDIA RTX A6000

  • Driver 596.46 (also reproduced on 595.97)

  • Windows 11, Vulkan 1.3

  • Extensions: VK_EXT_descriptor_heap, VK_EXT_shader_object, VK_KHR_maintenance5

Description:

textureLod (SPIR-V OpImageSampleExplicitLod with Lod operand) always samples from mip 0 regardless of the LOD argument when the sampled image is bound through VK_EXT_descriptor_heap with VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_PUSH_INDEX_EXT.

texelFetch (OpImageFetch with Lod) works correctly through the same descriptor heap path, returning the correct per-mip value for every level. This confirms the mip data is uploaded correctly and the descriptor heap binding is functional — the issue is specific to OpImageSampleExplicitLod.

The bug reproduces with both GLSL (glslc) and Slang (slangc) compiled SPIR-V, ruling out a compiler issue.

Reproducer:

A minimal standalone compute-only reproducer is available at: https://github.com/foijord/vk-textureLod-repro

It creates a 16x16 R8_UNORM image with 5 mip levels, each filled with a distinct constant value, then dispatches a compute shader that samples at LODs 0–4 using both textureLod and texelFetch.

Output (bug present):

Texture2D textureLod with VK_EXT_descriptor_heap:
  textureLod(sampler2D(tex, samp), uv, lod):
    LOD 0: expected=1.000  actual=1.000  OK
    LOD 1: expected=0.749  actual=1.000  FAIL
    LOD 2: expected=0.502  actual=1.000  FAIL
    LOD 3: expected=0.251  actual=1.000  FAIL
    LOD 4: expected=0.000  actual=1.000  FAIL
  texelFetch(sampler2D(tex, samp), texel, lod):
    LOD 0: expected=1.000  actual=1.000  OK
    LOD 1: expected=0.749  actual=0.749  OK
    LOD 2: expected=0.502  actual=0.502  OK
    LOD 3: expected=0.251  actual=0.251  OK
    LOD 4: expected=0.000  actual=0.000  OK

All textureLod calls return the mip 0 value (1.000). texelFetch returns the correct value at every level.

Hi there @frode.oijord and welcome back to the NVIDIA developer forums!

Thanks for bringing this to our attention, I am sure someone will check out your post soon.

Hi @frode.oijord.

The problem here is that the sampler read in the shader is incorrect. Confusingly, in VkDescriptorMappingSourcePushIndexEXT and similar structures, uniform sampler expressions use the non-sampler fields for their mapping.

In other words, this code:

    vk::DescriptorMappingSourcePushIndexEXT pushSamp{};
    pushSamp.samplerHeapOffset     = static_cast<uint32_t>(samplerSectionOffset);
    pushSamp.samplerPushOffset     = 4;
    pushSamp.samplerHeapIndexStride = static_cast<uint32_t>(hp.samplerDescriptorSize);

should be this instead:

    vk::DescriptorMappingSourcePushIndexEXT pushSamp{};
    pushSamp.heapOffset     = static_cast<uint32_t>(samplerSectionOffset);
    pushSamp.pushOffset     = 4;
    pushSamp.heapIndexStride = static_cast<uint32_t>(hp.samplerDescriptorSize);

The sampler fields are for combined image samplers (e.g. uniform sampler2D).

I haven’t built the repro with the fix, but changing that should fix it.

Thanks,

Rodrigo

Rodrigo, you’re the man… works like a charm now. Highly appreciated!