Combined Image Samplers vs Separate Images in Vulkan GLSL
Vulkan GLSL supports two distinct approaches for sampling textures: combined image samplers and decoupled (separate) sampled images paired with standalone samplers. While combined image samplers bundle the texture data and sampling configuration into a single descriptor, the separated approach decouples the image resource from the sampler state. This article explains the syntax used to declare both types in GLSL, how they map to Vulkan descriptor types, and how to differentiate their usage in shader code.
Combined Image Samplers
A combined image sampler packages an image resource and a sampler
configuration into a single entity. It corresponds to the Vulkan
descriptor type
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER.
Declaration Syntax
To declare a combined image sampler, use the traditional OpenGL-style
sampler types (such as sampler2D,
samplerCube, or sampler3D):
layout(set = 0, binding = 0) uniform sampler2D combinedImageSampler;Sampling in Shader Code
Because the sampling parameters (filtering, addressing modes) and the image data are coupled, you can pass the identifier directly to GLSL texture lookup functions:
vec4 color = texture(combinedImageSampler, uvCoordinates);Separate Sampled Images and Samplers
Vulkan allows the separation of image view state from sampler state.
This corresponds to two distinct descriptor types:
VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE for the texture data and
VK_DESCRIPTOR_TYPE_SAMPLER for the filtering and wrapping
configurations. Decoupling them allows a single sampler to be reused
across multiple textures, or multiple samplers (such as nearest and
linear) to sample the same texture.
Declaration Syntax
Separate resources are declared using the texture prefix
for image resources and the raw sampler keyword for sampler
objects:
layout(set = 0, binding = 0) uniform texture2D separateTexture;
layout(set = 0, binding = 1) uniform sampler separateSampler;texture2D(ortextureCube,texture3D, etc.) represents only the image data.samplerrepresents only the sampling rules (filtering, mipmap modes, wrapping).
Sampling in Shader Code
Because a texture2D cannot be read without specifying
sampling behavior, you must construct a temporary combined sampler at
the point of sampling using the constructor syntax:
vec4 color = texture(sampler2D(separateTexture, separateSampler), uvCoordinates);The expression
sampler2D(separateTexture, separateSampler) combines the
two separate components on the fly for the texture()
call.
Distinguishing Between the Two Approaches
You can easily distinguish combined image samplers from separate images by examining three primary areas:
- Type Names in GLSL Declarations
- Combined: Prefixed with
samplerfollowed by the dimensional type (e.g.,sampler2D,sampler2DArray,samplerCube). - Separate: Images are declared using
texturefollowed by dimension (e.g.,texture2D,textureCube), while samplers are declared using the pure keywordsampler.
- Combined: Prefixed with
- Descriptor Set Allocation
- Combined image samplers consume a single descriptor binding
(
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER). - Separate images and samplers require two separate bindings: one for
VK_DESCRIPTOR_TYPE_SAMPLED_IMAGEand another forVK_DESCRIPTOR_TYPE_SAMPLER.
- Combined image samplers consume a single descriptor binding
(
- Sampling Call Syntax
- Combined: Evaluated directly:
texture(mySampler2D, uv). - Separate: Evaluated via constructor syntax:
texture(sampler2D(myTexture2D, mySampler), uv).
- Combined: Evaluated directly: