diff --git a/libs/vkd3d-shader/tpf.c b/libs/vkd3d-shader/tpf.c
index e2c8ca81cee09f51c4c3d4f7441775d11f9cf57f..10d404534f9f6584c6cbacbab9e0aa2edf792160 100644
--- a/libs/vkd3d-shader/tpf.c
+++ b/libs/vkd3d-shader/tpf.c
@@ -2970,27 +2970,48 @@ static D3D_SRV_DIMENSION sm4_rdef_resource_dimension(const struct hlsl_type *typ
     }
 }
 
+struct extern_resource
+{
+    /* var is only not NULL if this resource is a whole variable, so it may be responsible for more
+     * than one component. */
+    const struct hlsl_ir_var *var;
+
+    char *name;
+    struct hlsl_type *data_type;
+    bool is_user_packed;
+
+    enum hlsl_regset regset;
+    unsigned int id, bind_count;
+};
+
 static int sm4_compare_extern_resources(const void *a, const void *b)
 {
-    const struct hlsl_ir_var *aa = *(const struct hlsl_ir_var **)a;
-    const struct hlsl_ir_var *bb = *(const struct hlsl_ir_var **)b;
-    enum hlsl_regset aa_regset, bb_regset;
+    const struct extern_resource *aa = (const struct extern_resource *)a;
+    const struct extern_resource *bb = (const struct extern_resource *)b;
+    int r;
 
-    aa_regset = hlsl_type_get_regset(aa->data_type);
-    bb_regset = hlsl_type_get_regset(bb->data_type);
+    if ((r = vkd3d_u32_compare(aa->regset, bb->regset)))
+        return r;
 
-    if (aa_regset != bb_regset)
-        return aa_regset - bb_regset;
+    return vkd3d_u32_compare(aa->id, bb->id);
+}
 
-    return aa->regs[aa_regset].id - bb->regs[bb_regset].id;
+static void sm4_free_extern_resources(struct extern_resource *extern_resources, unsigned int count)
+{
+    unsigned int i;
+
+    for (i = 0; i < count; ++i)
+        vkd3d_free(extern_resources[i].name);
+    vkd3d_free(extern_resources);
 }
 
-static const struct hlsl_ir_var **sm4_get_extern_resources(struct hlsl_ctx *ctx, unsigned int *count)
+static struct extern_resource *sm4_get_extern_resources(struct hlsl_ctx *ctx, unsigned int *count)
 {
-    const struct hlsl_ir_var **extern_resources = NULL;
+    struct extern_resource *extern_resources = NULL;
     const struct hlsl_ir_var *var;
     enum hlsl_regset regset;
     size_t capacity = 0;
+    char *name;
 
     *count = 0;
 
@@ -3005,11 +3026,29 @@ static const struct hlsl_ir_var **sm4_get_extern_resources(struct hlsl_ctx *ctx,
         if (!(hlsl_array_reserve(ctx, (void **)&extern_resources, &capacity, *count + 1,
                 sizeof(*extern_resources))))
         {
+            sm4_free_extern_resources(extern_resources, *count);
             *count = 0;
             return NULL;
         }
 
-        extern_resources[*count] = var;
+        if (!(name = hlsl_strdup(ctx, var->name)))
+        {
+            sm4_free_extern_resources(extern_resources, *count);
+            *count = 0;
+            return NULL;
+        }
+
+        extern_resources[*count].var = var;
+
+        extern_resources[*count].name = name;
+        extern_resources[*count].data_type = var->data_type;
+        extern_resources[*count].is_user_packed = !!var->reg_reservation.reg_type;
+
+        extern_resources[*count].regset = regset;
+        extern_resources[*count].id = var->regs[regset].id;
+        extern_resources[*count].bind_count = var->regs[regset].bind_count;
+
+
         ++*count;
     }
 
@@ -3023,8 +3062,8 @@ static void write_sm4_rdef(struct hlsl_ctx *ctx, struct dxbc_writer *dxbc)
     size_t cbuffers_offset, resources_offset, creator_offset, string_offset;
     size_t cbuffer_position, resource_position, creator_position;
     const struct hlsl_profile_info *profile = ctx->profile;
-    const struct hlsl_ir_var **extern_resources;
     struct vkd3d_bytecode_buffer buffer = {0};
+    struct extern_resource *extern_resources;
     const struct hlsl_buffer *cbuffer;
     const struct hlsl_ir_var *var;
 
@@ -3078,18 +3117,15 @@ static void write_sm4_rdef(struct hlsl_ctx *ctx, struct dxbc_writer *dxbc)
 
     for (i = 0; i < extern_resources_count; ++i)
     {
-        enum hlsl_regset regset;
+        const struct extern_resource *resource = &extern_resources[i];
         uint32_t flags = 0;
 
-        var = extern_resources[i];
-        regset = hlsl_type_get_regset(var->data_type);
-
-        if (var->reg_reservation.reg_type)
+        if (resource->is_user_packed)
             flags |= D3D_SIF_USERPACKED;
 
         put_u32(&buffer, 0); /* name */
-        put_u32(&buffer, sm4_resource_type(var->data_type));
-        if (regset == HLSL_REGSET_SAMPLERS)
+        put_u32(&buffer, sm4_resource_type(resource->data_type));
+        if (resource->regset == HLSL_REGSET_SAMPLERS)
         {
             put_u32(&buffer, 0);
             put_u32(&buffer, 0);
@@ -3097,15 +3133,15 @@ static void write_sm4_rdef(struct hlsl_ctx *ctx, struct dxbc_writer *dxbc)
         }
         else
         {
-            unsigned int dimx = hlsl_type_get_component_type(ctx, var->data_type, 0)->e.resource_format->dimx;
+            unsigned int dimx = hlsl_type_get_component_type(ctx, resource->data_type, 0)->e.resource_format->dimx;
 
-            put_u32(&buffer, sm4_resource_format(var->data_type));
-            put_u32(&buffer, sm4_rdef_resource_dimension(var->data_type));
+            put_u32(&buffer, sm4_resource_format(resource->data_type));
+            put_u32(&buffer, sm4_rdef_resource_dimension(resource->data_type));
             put_u32(&buffer, ~0u); /* FIXME: multisample count */
             flags |= (dimx - 1) << VKD3D_SM4_SIF_TEXTURE_COMPONENTS_SHIFT;
         }
-        put_u32(&buffer, var->regs[regset].id);
-        put_u32(&buffer, var->regs[regset].bind_count);
+        put_u32(&buffer, resource->id);
+        put_u32(&buffer, resource->bind_count);
         put_u32(&buffer, flags);
     }
 
@@ -3131,9 +3167,9 @@ static void write_sm4_rdef(struct hlsl_ctx *ctx, struct dxbc_writer *dxbc)
 
     for (i = 0; i < extern_resources_count; ++i)
     {
-        var = extern_resources[i];
+        const struct extern_resource *resource = &extern_resources[i];
 
-        string_offset = put_string(&buffer, var->name);
+        string_offset = put_string(&buffer, resource->name);
         set_u32(&buffer, resources_offset + i * 8 * sizeof(uint32_t), string_offset);
     }
 
@@ -3239,7 +3275,7 @@ static void write_sm4_rdef(struct hlsl_ctx *ctx, struct dxbc_writer *dxbc)
 
     add_section(dxbc, TAG_RDEF, &buffer);
 
-    vkd3d_free(extern_resources);
+    sm4_free_extern_resources(extern_resources, extern_resources_count);
 }
 
 static enum vkd3d_sm4_resource_type sm4_resource_dimension(const struct hlsl_type *type)
@@ -3693,9 +3729,11 @@ static void write_sm4_dcl_constant_buffer(struct vkd3d_bytecode_buffer *buffer,
     write_sm4_instruction(buffer, &instr);
 }
 
-static void write_sm4_dcl_samplers(struct vkd3d_bytecode_buffer *buffer, const struct hlsl_ir_var *var)
+static void write_sm4_dcl_samplers(struct hlsl_ctx *ctx, struct vkd3d_bytecode_buffer *buffer,
+        const struct extern_resource *resource)
 {
-    unsigned int i, count = var->data_type->reg_size[HLSL_REGSET_SAMPLERS];
+    struct hlsl_type *component_type;
+    unsigned int i;
     struct sm4_instruction instr =
     {
         .opcode = VKD3D_SM4_OP_DCL_SAMPLER,
@@ -3705,38 +3743,44 @@ static void write_sm4_dcl_samplers(struct vkd3d_bytecode_buffer *buffer, const s
         .dst_count = 1,
     };
 
-    if (var->data_type->sampler_dim == HLSL_SAMPLER_DIM_COMPARISON)
+    component_type = hlsl_type_get_component_type(ctx, resource->data_type, 0);
+
+    if (component_type->sampler_dim == HLSL_SAMPLER_DIM_COMPARISON)
         instr.opcode |= VKD3D_SM4_SAMPLER_COMPARISON << VKD3D_SM4_SAMPLER_MODE_SHIFT;
 
-    for (i = 0; i < count; ++i)
+    assert(resource->regset == HLSL_REGSET_SAMPLERS);
+
+    for (i = 0; i < resource->bind_count; ++i)
     {
-        if (!var->objects_usage[HLSL_REGSET_SAMPLERS][i].used)
+        if (resource->var && !resource->var->objects_usage[HLSL_REGSET_SAMPLERS][i].used)
             continue;
 
-        instr.dsts[0].reg.idx[0] = var->regs[HLSL_REGSET_SAMPLERS].id + i;
+        instr.dsts[0].reg.idx[0] = resource->id + i;
         write_sm4_instruction(buffer, &instr);
     }
 }
 
 static void write_sm4_dcl_textures(struct hlsl_ctx *ctx, struct vkd3d_bytecode_buffer *buffer,
-        const struct hlsl_ir_var *var, bool uav)
+        const struct extern_resource *resource, bool uav)
 {
     enum hlsl_regset regset = uav ? HLSL_REGSET_UAVS : HLSL_REGSET_TEXTURES;
-    unsigned int i, count = var->data_type->reg_size[regset];
     struct hlsl_type *component_type;
     struct sm4_instruction instr;
+    unsigned int i;
 
-    component_type = hlsl_type_get_component_type(ctx, var->data_type, 0);
+    assert(resource->regset == regset);
 
-    for (i = 0; i < count; ++i)
+    component_type = hlsl_type_get_component_type(ctx, resource->data_type, 0);
+
+    for (i = 0; i < resource->bind_count; ++i)
     {
-        if (!var->objects_usage[regset][i].used)
+        if (resource->var && !resource->var->objects_usage[regset][i].used)
             continue;
 
         instr = (struct sm4_instruction)
         {
             .dsts[0].reg.type = uav ? VKD3D_SM5_RT_UAV : VKD3D_SM4_RT_RESOURCE,
-            .dsts[0].reg.idx = {var->regs[regset].id + i},
+            .dsts[0].reg.idx = {resource->id + i},
             .dsts[0].reg.idx_count = 1,
             .dst_count = 1,
 
@@ -3746,11 +3790,11 @@ static void write_sm4_dcl_textures(struct hlsl_ctx *ctx, struct vkd3d_bytecode_b
 
         if (uav)
         {
-            switch (var->data_type->sampler_dim)
+            switch (resource->data_type->sampler_dim)
             {
             case HLSL_SAMPLER_DIM_STRUCTURED_BUFFER:
                 instr.opcode = VKD3D_SM5_OP_DCL_UAV_STRUCTURED;
-                instr.byte_stride = var->data_type->e.resource_format->reg_size[HLSL_REGSET_NUMERIC] * 4;
+                instr.byte_stride = resource->data_type->e.resource_format->reg_size[HLSL_REGSET_NUMERIC] * 4;
                 break;
             default:
                 instr.opcode = VKD3D_SM5_OP_DCL_UAV_TYPED;
@@ -5160,8 +5204,8 @@ static void write_sm4_shdr(struct hlsl_ctx *ctx,
         const struct hlsl_ir_function_decl *entry_func, struct dxbc_writer *dxbc)
 {
     const struct hlsl_profile_info *profile = ctx->profile;
-    const struct hlsl_ir_var **extern_resources;
     struct vkd3d_bytecode_buffer buffer = {0};
+    struct extern_resource *extern_resources;
     unsigned int extern_resources_count, i;
     const struct hlsl_buffer *cbuffer;
     const struct hlsl_ir_var *var;
@@ -5193,17 +5237,14 @@ static void write_sm4_shdr(struct hlsl_ctx *ctx,
 
     for (i = 0; i < extern_resources_count; ++i)
     {
-        enum hlsl_regset regset;
+        const struct extern_resource *resource = &extern_resources[i];
 
-        var = extern_resources[i];
-        regset = hlsl_type_get_regset(var->data_type);
-
-        if (regset == HLSL_REGSET_SAMPLERS)
-            write_sm4_dcl_samplers(&buffer, var);
-        else if (regset == HLSL_REGSET_TEXTURES)
-            write_sm4_dcl_textures(ctx, &buffer, var, false);
-        else if (regset == HLSL_REGSET_UAVS)
-            write_sm4_dcl_textures(ctx, &buffer, var, true);
+        if (resource->regset == HLSL_REGSET_SAMPLERS)
+            write_sm4_dcl_samplers(ctx, &buffer, resource);
+        else if (resource->regset == HLSL_REGSET_TEXTURES)
+            write_sm4_dcl_textures(ctx, &buffer, resource, false);
+        else if (resource->regset == HLSL_REGSET_UAVS)
+            write_sm4_dcl_textures(ctx, &buffer, resource, true);
     }
 
     LIST_FOR_EACH_ENTRY(var, &ctx->extern_vars, struct hlsl_ir_var, extern_entry)
@@ -5226,7 +5267,7 @@ static void write_sm4_shdr(struct hlsl_ctx *ctx,
 
     add_section(dxbc, TAG_SHDR, &buffer);
 
-    vkd3d_free(extern_resources);
+    sm4_free_extern_resources(extern_resources, extern_resources_count);
 }
 
 int hlsl_sm4_write(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry_func, struct vkd3d_shader_code *out)