diff --git a/include/vkd3d_shader.h b/include/vkd3d_shader.h
index 227e8365..d92f73a3 100644
--- a/include/vkd3d_shader.h
+++ b/include/vkd3d_shader.h
@@ -88,6 +88,12 @@ struct vkd3d_shader_interface
 
     const struct vkd3d_shader_push_constant *push_constants;
     unsigned int push_constant_count;
+
+    /* A sampler used by OpImageFetches generated for SM4 ld instructions.
+     *
+     * In Vulkan OpImageFetch must be used with a sampled image.
+     */
+    struct vkd3d_shader_descriptor_binding default_sampler;
 };
 
 HRESULT vkd3d_shader_compile_dxbc(const struct vkd3d_shader_code *dxbc,
diff --git a/libs/vkd3d/state.c b/libs/vkd3d/state.c
index 7bc2f133..2234a860 100644
--- a/libs/vkd3d/state.c
+++ b/libs/vkd3d/state.c
@@ -18,7 +18,6 @@
  */
 
 #include "vkd3d_private.h"
-#include "vkd3d_shader.h"
 
 #include "spirv/1.0/spirv.h"
 
@@ -733,6 +732,33 @@ static HRESULT d3d12_root_signature_init_root_descriptors(struct d3d12_root_sign
     return S_OK;
 }
 
+static HRESULT d3d12_root_signature_create_default_sampler(struct d3d12_root_signature *root_signature,
+        struct d3d12_device *device, unsigned int index, struct vkd3d_descriptor_set_context *context,
+        VkDescriptorSetLayoutBinding *binding)
+{
+    D3D12_STATIC_SAMPLER_DESC sampler_desc;
+    HRESULT hr;
+
+    memset(&sampler_desc, 0, sizeof(sampler_desc));
+    sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
+    sampler_desc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+    sampler_desc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+    sampler_desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+    if (FAILED(hr = vkd3d_create_static_sampler(device, &sampler_desc,
+            &root_signature->static_samplers[index])))
+        return hr;
+
+    root_signature->default_sampler.set = context->set_index;
+    root_signature->default_sampler.binding = context->descriptor_binding++;
+
+    binding->binding = root_signature->default_sampler.binding;
+    binding->descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
+    binding->descriptorCount = 1;
+    binding->stageFlags = VK_SHADER_STAGE_ALL;
+    binding->pImmutableSamplers = &root_signature->static_samplers[index];
+    return S_OK;
+}
+
 static HRESULT d3d12_root_signature_init_static_samplers(struct d3d12_root_signature *root_signature,
         struct d3d12_device *device, const D3D12_ROOT_SIGNATURE_DESC *desc,
         struct vkd3d_descriptor_set_context *context)
@@ -761,6 +787,14 @@ static HRESULT d3d12_root_signature_init_static_samplers(struct d3d12_root_signa
         ++cur_binding;
     }
 
+    if (i < root_signature->static_sampler_count)
+    {
+        if (FAILED(hr = d3d12_root_signature_create_default_sampler(root_signature, device,
+                i, context, cur_binding)))
+            return hr;
+        ++cur_binding;
+    }
+
     context->current_binding = cur_binding;
     return S_OK;
 }
@@ -809,12 +843,22 @@ static HRESULT d3d12_root_signature_init(struct d3d12_root_signature *root_signa
      * each SRV and UAV. */
     info.descriptor_count += info.srv_count + info.uav_count;
 
+    root_signature->descriptor_count = info.descriptor_count;
+    root_signature->static_sampler_count = desc->NumStaticSamplers;
+
+    /* An additional sampler is created for SpvOpImageFetch. */
+    if (info.srv_count || info.buffer_srv_count)
+    {
+        ++info.sampler_count;
+        ++info.descriptor_count;
+        ++root_signature->static_sampler_count;
+    }
+
     hr = E_OUTOFMEMORY;
     root_signature->parameter_count = desc->NumParameters;
     if (!(root_signature->parameters = vkd3d_calloc(root_signature->parameter_count,
             sizeof(*root_signature->parameters))))
         goto fail;
-    root_signature->descriptor_count = info.descriptor_count;
     if (!(root_signature->descriptor_mapping = vkd3d_calloc(root_signature->descriptor_count,
             sizeof(*root_signature->descriptor_mapping))))
         goto fail;
@@ -822,7 +866,6 @@ static HRESULT d3d12_root_signature_init(struct d3d12_root_signature *root_signa
     if (!(root_signature->push_constants = vkd3d_calloc(root_signature->constant_count,
             sizeof(*root_signature->push_constants))))
         goto fail;
-    root_signature->static_sampler_count = desc->NumStaticSamplers;
     if (!(root_signature->static_samplers = vkd3d_calloc(root_signature->static_sampler_count,
             sizeof(*root_signature->static_samplers))))
         goto fail;
@@ -1121,6 +1164,7 @@ static HRESULT create_shader_stage(struct d3d12_device *device,
         shader_interface.binding_count = root_signature->descriptor_count;
         shader_interface.push_constants = root_signature->push_constants;
         shader_interface.push_constant_count = root_signature->constant_count;
+        shader_interface.default_sampler = root_signature->default_sampler;
         if (FAILED(hr = vkd3d_shader_compile_dxbc(&dxbc, &spirv, 0, &shader_interface)))
         {
             WARN("Failed to compile shader, hr %#x.\n", hr);
diff --git a/libs/vkd3d/vkd3d_main.c b/libs/vkd3d/vkd3d_main.c
index 7470c7bc..0b617ab3 100644
--- a/libs/vkd3d/vkd3d_main.c
+++ b/libs/vkd3d/vkd3d_main.c
@@ -18,7 +18,6 @@
 
 #define INITGUID
 #include "vkd3d_private.h"
-#include "vkd3d_shader.h"
 
 HRESULT vkd3d_create_device(const struct vkd3d_device_create_info *create_info,
         REFIID riid, void **device)
diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h
index 7591053a..d04144a1 100644
--- a/libs/vkd3d/vkd3d_private.h
+++ b/libs/vkd3d/vkd3d_private.h
@@ -26,6 +26,7 @@
 #define NONAMELESSUNION
 #include "vkd3d.h"
 #include "vkd3d_memory.h"
+#include "vkd3d_shader.h"
 
 #include <assert.h>
 #include <inttypes.h>
@@ -317,6 +318,7 @@ struct d3d12_root_signature
 
     unsigned int descriptor_count;
     struct vkd3d_shader_resource_binding *descriptor_mapping;
+    struct vkd3d_shader_descriptor_binding default_sampler;
 
     unsigned int constant_count;
     struct vkd3d_shader_push_constant *push_constants;