Unnormalized texture coordinate reads with VkSampler

There seems to be a lot of confusion around using VkSampler objects with unnormalized coordinates. I coded up a sample that does work, but am concerned that the approach I am using, while functional, is ultimately incorrect.

In C code I created an unnormalized sampler, a descriptor and give it a binding.
Then created a descriptor for the VkImageView with type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE. The ImageView is written to as part of a full screen render pass, then transitioned to shader read only for use by a shader in a later render pass.

But the same sampler when used as a VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER generates a validation error.

To use them in the shader I coded it up similar to this:

layout(binding=21) uniform sampler unorm_sampler;
layout(binding=22) uniform texture2D inputBuffer;

layout(binding=23) uniform sampler2D inputBufferSampler; // unnormalized combined image sampler

void main()
{
    vec4 value = texture( sampler2D( inputBuffer, unorm_sampler ), gl_FragCoord.xy ); 

   // Generates Validation layer error  VUID-vkCmdDraw-None-02703
   vec4 errorval = texture( inputBufferSampler, gl_FragCoord.xy ); 

}

The text of the error:
The Vulkan spec states: If the VkPipeline object bound to the pipeline bind point used by this command accesses a VkSampler object that uses unnormalized coordinates, that sampler must not be used with any of the SPIR-V OpImageSample* or OpImageSparseSample* instructions with ImplicitLod, Dref or Proj in their name, in any shader stage (https://vulkan.lunarg.com/doc/view/1.2.182.0/windows/1.2-extensions/vkspec.html#VUID-vkCmdDraw-None-02703)[Info] - key id:27

Both calls are using a sampler2D the combined sampler generates an error, the sampler2D created in the GLSL code does not. My guess was that I am sneaking one past the validation layers or I just plain misunderstand the difference between the two. Please help clear this up.

Just to be clear, I believe I am the confused party here and that Vulkan is working just fine and am sure that Vulkan’s unnormalized texture coordinate support is perfectly functional. I really just want to figure out the proper way to use unnormalized samplers in my glsl shaders.

Why do you have this “understanding”; what is it based on? Which “opcode that is disallowed under Vulkan” are you talking about? Also, there’s no “sampler2D function”.

I don’t understand what exactly your concerns are. It sounds like you’re asking if unnormalized coordinates work. Which they do. Because that’s what Vulkan says; it has a specific feature for this. Yes, there are rules to follow: the image view must be 1D or 2D, and it must not be arrayed or have mipmaps. And you can’t use offsets or projective texturing when doing this.

I edited my original question, thanks.
Here is the sampler setup:

{ 
    VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, 
    nullptr, 
    0, 
   VK_FILTER_LINEAR, VK_FILTER_LINEAR,
   VK_SAMPLER_MIPMAP_MODE_NEAREST,
   VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, 
   VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, 
   VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, 
   0, 
   VK_FALSE, 
   0, 
   VK_FALSE, 
   VK_COMPARE_OP_NEVER, 
   0, 
   0, 
   VK_BORDER_COLOR_INT_OPAQUE_BLACK, 
   VK_TRUE 
}

So it looks like texture(blablabla); calls all result in SPIR-V opcode 87 which is OpImageSampleImplicitLod and is an error per ‘VUID-vkCmdDraw-None-02703’. So now I am wondering what opcode is legal and how to coerce GLSL to spit it out.

After a little searching and more experimentation:

SPIR-V opcode 88 is the secret keys to the unnormalized kingdom. OpCode 88 instruction is OpImageSampleExplicitLod. Which is not listed (as near as I can tell) anywhere in the Vulkan spec as being an illegal operation with an unnormalized sampler.

And the secret GLSL instruction that maps to opcode 88 is textureLod. Here is a code sample using it.

// Set this up just like usual, expect use a unnormalized sampler
layout(binding = 23) uniform sampler2D unormSampler;

void main()
{
    vec4 val = textureLod( unormSampler, pixelCoord, 0); // 0 is the mipmap level to sample from
}

I didn’t try other mipmap levels ( I vaguely remember the Vulkan Spec saying something about no mipmaps with unnormalized coordinates.).

I did try VK_FILTER_LINEAR and VK_FILTER_NEAREST for the sample and they both worked as expected. (my test has artifacts that show up when using VK_FILTER_NEAREST)

Here are a list of things that don’t work with GLSL, Vulkan, and unnormalized samplers:

  1. Using sampler2Drect – Vulkan doesn’t support
  2. Using any version of texture(blablabla); – results in opcode 87 which is a no no per Vulkan spec
  3. Using samplerBuffer’s – again the Spec says no with unnormalized samplers

Hopefully this little post will help others having confusion around using unnormalized samplers with GLSL on Vulkan.

How did you determine Vulkan does not support sampler2DRect?

I can’t post a link but if you search google for “sampler2drect vulkan” the top hit will give you the link back to this forum, lower down in the post you pointed it out. Did I misinterpret your post?

I tried glslangValidator and it does not complain with -V100 and sampler2DRect. Though it does introduce SampledRect cap in the code, which might be a problem.

And yet, if you actually follow the links provided in that thread, nowhere in there does it say that sampler2DRect doesn’t exist in Vulkan GLSL. Indeed, it explicitly adds texture2DRect, which actually makes rectangle textures fully supported with Vulkan’s split sampler/texture system.

I get an error when declaring a sampler2DRect in GLSL:

UNASSIGNED-CoreValidation-Shader-InconsistentSpirv

with the text:

SPIR-V module not valid: Capability SampledRect is not allowed by Vulkan 1.2 specification (or requires extension)

2 other errors follow that (basically restating the same error).

Yea. I feel known good OpenGL code should largely be working. If it does not, then ping KhronosGroup/Vulkan-Docs and KhronosGroup/glslang as appropriate.

It might not be a well trodden path, as most of the usecases are either normalized, or storage image instead.