diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h
index c8e267d295b2c95253085649cf0e3ad4fbafb9de..38a7715e99cedf7a09f11038ffa17e475cb846aa 100644
--- a/libs/vkd3d-shader/hlsl.h
+++ b/libs/vkd3d-shader/hlsl.h
@@ -342,12 +342,17 @@ struct hlsl_attribute
 
 #define HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT 0
 
-/* Reservation of a specific register to a variable, field, or buffer, written in the HLSL source
- *   using the register(·) syntax */
+/* Reservation of a register and/or an offset for objects inside constant buffers, to be used as a
+ *   starting point of their allocation. They are available through the register(·) and the
+ *   packoffset(·) syntaxes, respectivelly.
+ * The costant buffer offset is measured register components. */
 struct hlsl_reg_reservation
 {
-    char type;
-    unsigned int index;
+    char reg_type;
+    unsigned int reg_index;
+
+    char offset_type;
+    unsigned int offset_index;
 };
 
 struct hlsl_ir_var
@@ -360,8 +365,7 @@ struct hlsl_ir_var
     struct hlsl_buffer *buffer;
     /* Bitfield for storage modifiers (type modifiers are stored in data_type->modifiers). */
     unsigned int storage_modifiers;
-    /* Optional register to be used as a starting point for the variable allocation, specified
-     *   by the user via the register(·) syntax. */
+    /* Optional reservations of registers and/or offsets for variables within constant buffers. */
     struct hlsl_reg_reservation reg_reservation;
 
     /* Item entry in hlsl_scope.vars. Specifically hlsl_ctx.globals.vars if the variable is global. */
@@ -674,6 +678,9 @@ struct hlsl_buffer
     unsigned size, used_size;
     /* Register of type 'b' on which the buffer is allocated. */
     struct hlsl_reg reg;
+
+    bool manually_packed_elements;
+    bool automatically_packed_elements;
 };
 
 struct hlsl_ctx
diff --git a/libs/vkd3d-shader/hlsl.l b/libs/vkd3d-shader/hlsl.l
index adff1da04d828f2e00e87282c8651096eda00737..10751bbe9ba0360bae4fe0dc08d29885eba8dd14 100644
--- a/libs/vkd3d-shader/hlsl.l
+++ b/libs/vkd3d-shader/hlsl.l
@@ -95,6 +95,7 @@ matrix                  {return KW_MATRIX;              }
 namespace               {return KW_NAMESPACE;           }
 nointerpolation         {return KW_NOINTERPOLATION;     }
 out                     {return KW_OUT;                 }
+packoffset              {return KW_PACKOFFSET;          }
 pass                    {return KW_PASS;                }
 PixelShader             {return KW_PIXELSHADER;         }
 precise                 {return KW_PRECISE;             }
diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y
index afcc4575370a86e1536079619888e6acc6681561..79ec970a0addcef03a4ef230892e2ee3fd3ec396 100644
--- a/libs/vkd3d-shader/hlsl.y
+++ b/libs/vkd3d-shader/hlsl.y
@@ -983,6 +983,9 @@ static bool gen_struct_fields(struct hlsl_ctx *ctx, struct parse_fields *fields,
             hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Illegal initializer on a struct field.");
             free_parse_initializer(&v->initializer);
         }
+        if (v->reg_reservation.offset_type)
+            hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
+                "packoffset() is not allowed inside struct definitions.");
         vkd3d_free(v);
     }
     vkd3d_free(defs);
@@ -1063,7 +1066,12 @@ static bool add_func_parameter(struct hlsl_ctx *ctx, struct hlsl_func_parameters
         hlsl_error(ctx, &loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
                 "Parameter '%s' is declared as both \"out\" and \"uniform\".", param->name);
 
-    if (!(var = hlsl_new_var(ctx, param->name, param->type, loc, &param->semantic, param->modifiers, &param->reg_reservation)))
+    if (param->reg_reservation.offset_type)
+        hlsl_error(ctx, &loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
+                "packoffset() is not allowed on function parameters.");
+
+    if (!(var = hlsl_new_var(ctx, param->name, param->type, loc, &param->semantic, param->modifiers,
+            &param->reg_reservation)))
         return false;
     var->is_param = 1;
 
@@ -1084,12 +1092,61 @@ static struct hlsl_reg_reservation parse_reg_reservation(const char *reg_string)
 {
     struct hlsl_reg_reservation reservation = {0};
 
-    if (!sscanf(reg_string + 1, "%u", &reservation.index))
+    if (!sscanf(reg_string + 1, "%u", &reservation.reg_index))
     {
         FIXME("Unsupported register reservation syntax.\n");
         return reservation;
     }
-    reservation.type = reg_string[0];
+    reservation.reg_type = reg_string[0];
+    return reservation;
+}
+
+static struct hlsl_reg_reservation parse_packoffset(struct hlsl_ctx *ctx, const char *reg_string,
+        const char *swizzle, const struct vkd3d_shader_location *loc)
+{
+    struct hlsl_reg_reservation reservation = {0};
+    char *endptr;
+
+    if (ctx->profile->major_version < 4)
+        return reservation;
+
+    reservation.offset_index = strtoul(reg_string + 1, &endptr, 10);
+    if (*endptr)
+    {
+        hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
+                "Invalid packoffset() syntax.");
+        return reservation;
+    }
+
+    reservation.offset_type = reg_string[0];
+    if (reservation.offset_type != 'c')
+    {
+        hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
+                "Only 'c' registers are allowed in packoffset().");
+        return reservation;
+    }
+
+    reservation.offset_index *= 4;
+
+    if (swizzle)
+    {
+        if (strlen(swizzle) != 1)
+            hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
+                    "Invalid packoffset() component \"%s\".", swizzle);
+
+        if (swizzle[0] == 'x' || swizzle[0] == 'r')
+            reservation.offset_index += 0;
+        else if (swizzle[0] == 'y' || swizzle[0] == 'g')
+            reservation.offset_index += 1;
+        else if (swizzle[0] == 'z' || swizzle[0] == 'b')
+            reservation.offset_index += 2;
+        else if (swizzle[0] == 'w' || swizzle[0] == 'a')
+            reservation.offset_index += 3;
+        else
+            hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
+                    "Invalid packoffset() component \"%s\".", swizzle);
+    }
+
     return reservation;
 }
 
@@ -2043,6 +2100,13 @@ static struct list *declare_vars(struct hlsl_ctx *ctx, struct hlsl_type *basic_t
 
         var->buffer = ctx->cur_buffer;
 
+        if (var->buffer == ctx->globals_buffer)
+        {
+            if (var->reg_reservation.offset_type)
+                hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
+                        "packoffset() is only allowed inside constant buffer declarations.");
+        }
+
         if (ctx->cur_scope == ctx->globals)
         {
             local = false;
@@ -3846,6 +3910,7 @@ static void validate_texture_format_type(struct hlsl_ctx *ctx, struct hlsl_type
 %token KW_NAMESPACE
 %token KW_NOINTERPOLATION
 %token KW_OUT
+%token KW_PACKOFFSET
 %token KW_PASS
 %token KW_PIXELSHADER
 %token KW_PRECISE
@@ -3999,6 +4064,7 @@ static void validate_texture_format_type(struct hlsl_ctx *ctx, struct hlsl_type
 %type <parameters> parameters
 
 %type <reg_reservation> register_opt
+%type <reg_reservation> packoffset_opt
 
 %type <sampler_dim> texture_type texture_ms_type uav_type
 
@@ -4349,8 +4415,11 @@ func_prototype_no_attrs:
                         "Semantics are not allowed on void functions.");
             }
 
-            if ($7.reg_reservation.type)
+            if ($7.reg_reservation.reg_type)
                 FIXME("Unexpected register reservation for a function.\n");
+            if ($7.reg_reservation.offset_type)
+                hlsl_error(ctx, &@5, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
+                        "packoffset() is not allowed on functions.");
 
             if (($$.decl = get_func_decl(&ctx->functions, $3, &$5)))
             {
@@ -4477,18 +4546,25 @@ colon_attribute:
       %empty
         {
             $$.semantic.name = NULL;
-            $$.reg_reservation.type = 0;
+            $$.reg_reservation.reg_type = 0;
+            $$.reg_reservation.offset_type = 0;
         }
     | semantic
         {
             $$.semantic = $1;
-            $$.reg_reservation.type = 0;
+            $$.reg_reservation.reg_type = 0;
+            $$.reg_reservation.offset_type = 0;
         }
     | register_opt
         {
             $$.semantic.name = NULL;
             $$.reg_reservation = $1;
         }
+    | packoffset_opt
+        {
+            $$.semantic.name = NULL;
+            $$.reg_reservation = $1;
+        }
 
 semantic:
       ':' any_identifier
@@ -4518,6 +4594,21 @@ register_opt:
             vkd3d_free($6);
         }
 
+packoffset_opt:
+      ':' KW_PACKOFFSET '(' any_identifier ')'
+        {
+            $$ = parse_packoffset(ctx, $4, NULL, &@$);
+
+            vkd3d_free($4);
+        }
+    | ':' KW_PACKOFFSET '(' any_identifier '.' any_identifier ')'
+        {
+            $$ = parse_packoffset(ctx, $4, $6, &@$);
+
+            vkd3d_free($4);
+            vkd3d_free($6);
+        }
+
 parameters:
       scope_start
         {
diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c
index 6c529f1913efa7cfbe8e8d19b2637d6548e1af81..5d7e426f152814ba9427c384bb4f3840c9a0641f 100644
--- a/libs/vkd3d-shader/hlsl_codegen.c
+++ b/libs/vkd3d-shader/hlsl_codegen.c
@@ -2307,9 +2307,9 @@ static void allocate_register_reservations(struct hlsl_ctx *ctx)
             continue;
         regset = hlsl_type_get_regset(var->data_type);
 
-        if (var->reg_reservation.type)
+        if (var->reg_reservation.reg_type)
         {
-            if (var->reg_reservation.type != get_regset_name(regset))
+            if (var->reg_reservation.reg_type != get_regset_name(regset))
             {
                 struct vkd3d_string_buffer *type_string;
 
@@ -2322,8 +2322,9 @@ static void allocate_register_reservations(struct hlsl_ctx *ctx)
             else
             {
                 var->regs[regset].allocated = true;
-                var->regs[regset].id = var->reg_reservation.index;
-                TRACE("Allocated reserved %s to %c%u.\n", var->name, var->reg_reservation.type, var->reg_reservation.index);
+                var->regs[regset].id = var->reg_reservation.reg_index;
+                TRACE("Allocated reserved %s to %c%u.\n", var->name, var->reg_reservation.reg_type,
+                        var->reg_reservation.reg_index);
             }
         }
     }
@@ -2898,23 +2899,117 @@ static const struct hlsl_buffer *get_reserved_buffer(struct hlsl_ctx *ctx, uint3
 
     LIST_FOR_EACH_ENTRY(buffer, &ctx->buffers, const struct hlsl_buffer, entry)
     {
-        if (buffer->used_size && buffer->reservation.type == 'b' && buffer->reservation.index == index)
+        if (buffer->used_size && buffer->reservation.reg_type == 'b' && buffer->reservation.reg_index == index)
             return buffer;
     }
     return NULL;
 }
 
-static void calculate_buffer_offset(struct hlsl_ir_var *var)
+static void calculate_buffer_offset(struct hlsl_ctx *ctx, struct hlsl_ir_var *var)
 {
+    unsigned int var_reg_size = var->data_type->reg_size[HLSL_REGSET_NUMERIC];
+    enum hlsl_type_class var_class = var->data_type->class;
     struct hlsl_buffer *buffer = var->buffer;
 
-    buffer->size = hlsl_type_get_sm4_offset(var->data_type, buffer->size);
+    if (var->reg_reservation.offset_type == 'c')
+    {
+        if (var->reg_reservation.offset_index % 4)
+        {
+            if (var_class == HLSL_CLASS_MATRIX)
+            {
+                hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
+                        "packoffset() reservations with matrix types must be aligned with the beginning of a register.");
+            }
+            else if (var_class == HLSL_CLASS_ARRAY)
+            {
+                hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
+                        "packoffset() reservations with array types must be aligned with the beginning of a register.");
+            }
+            else if (var_class == HLSL_CLASS_STRUCT)
+            {
+                hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
+                        "packoffset() reservations with struct types must be aligned with the beginning of a register.");
+            }
+            else if (var_class == HLSL_CLASS_VECTOR)
+            {
+                unsigned int aligned_offset = hlsl_type_get_sm4_offset(var->data_type, var->reg_reservation.offset_index);
+
+                if (var->reg_reservation.offset_index != aligned_offset)
+                    hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
+                            "packoffset() reservations with vector types cannot span multiple registers.");
+            }
+        }
+        var->buffer_offset = var->reg_reservation.offset_index;
+    }
+    else
+    {
+        var->buffer_offset = hlsl_type_get_sm4_offset(var->data_type, buffer->size);
+    }
 
-    var->buffer_offset = buffer->size;
     TRACE("Allocated buffer offset %u to %s.\n", var->buffer_offset, var->name);
-    buffer->size += var->data_type->reg_size[HLSL_REGSET_NUMERIC];
+    buffer->size = max(buffer->size, var->buffer_offset + var_reg_size);
     if (var->last_read)
-        buffer->used_size = buffer->size;
+        buffer->used_size = max(buffer->used_size, var->buffer_offset + var_reg_size);
+}
+
+static void validate_buffer_offsets(struct hlsl_ctx *ctx)
+{
+    struct hlsl_ir_var *var1, *var2;
+    struct hlsl_buffer *buffer;
+
+    LIST_FOR_EACH_ENTRY(var1, &ctx->extern_vars, struct hlsl_ir_var, extern_entry)
+    {
+        if (!var1->is_uniform || var1->data_type->class == HLSL_CLASS_OBJECT)
+            continue;
+
+        buffer = var1->buffer;
+        if (!buffer->used_size)
+            continue;
+
+        LIST_FOR_EACH_ENTRY(var2, &ctx->extern_vars, struct hlsl_ir_var, extern_entry)
+        {
+            unsigned int var1_reg_size, var2_reg_size;
+
+            if (!var2->is_uniform || var2->data_type->class == HLSL_CLASS_OBJECT)
+                continue;
+
+            if (var1 == var2 || var1->buffer != var2->buffer)
+                continue;
+
+            /* This is to avoid reporting the error twice for the same pair of overlapping variables. */
+            if (strcmp(var1->name, var2->name) >= 0)
+                continue;
+
+            var1_reg_size = var1->data_type->reg_size[HLSL_REGSET_NUMERIC];
+            var2_reg_size = var2->data_type->reg_size[HLSL_REGSET_NUMERIC];
+
+            if (var1->buffer_offset < var2->buffer_offset + var2_reg_size
+                    && var2->buffer_offset < var1->buffer_offset + var1_reg_size)
+                hlsl_error(ctx, &buffer->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
+                        "Invalid packoffset() reservation: Variables %s and %s overlap.",
+                        var1->name, var2->name);
+        }
+    }
+
+    LIST_FOR_EACH_ENTRY(var1, &ctx->extern_vars, struct hlsl_ir_var, extern_entry)
+    {
+        buffer = var1->buffer;
+        if (!buffer || buffer == ctx->globals_buffer)
+            continue;
+
+        if (var1->reg_reservation.offset_type
+                || (var1->data_type->class == HLSL_CLASS_OBJECT && var1->reg_reservation.reg_type))
+            buffer->manually_packed_elements = true;
+        else
+            buffer->automatically_packed_elements = true;
+
+        if (buffer->manually_packed_elements && buffer->automatically_packed_elements)
+        {
+            hlsl_error(ctx, &buffer->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
+                    "packoffset() must be specified for all the buffer elements, or none of them.");
+            break;
+        }
+    }
 }
 
 static void allocate_buffers(struct hlsl_ctx *ctx)
@@ -2930,10 +3025,12 @@ static void allocate_buffers(struct hlsl_ctx *ctx)
             if (var->is_param)
                 var->buffer = ctx->params_buffer;
 
-            calculate_buffer_offset(var);
+            calculate_buffer_offset(ctx, var);
         }
     }
 
+    validate_buffer_offsets(ctx);
+
     LIST_FOR_EACH_ENTRY(buffer, &ctx->buffers, struct hlsl_buffer, entry)
     {
         if (!buffer->used_size)
@@ -2941,23 +3038,23 @@ static void allocate_buffers(struct hlsl_ctx *ctx)
 
         if (buffer->type == HLSL_BUFFER_CONSTANT)
         {
-            if (buffer->reservation.type == 'b')
+            if (buffer->reservation.reg_type == 'b')
             {
-                const struct hlsl_buffer *reserved_buffer = get_reserved_buffer(ctx, buffer->reservation.index);
+                const struct hlsl_buffer *reserved_buffer = get_reserved_buffer(ctx, buffer->reservation.reg_index);
 
                 if (reserved_buffer && reserved_buffer != buffer)
                 {
                     hlsl_error(ctx, &buffer->loc, VKD3D_SHADER_ERROR_HLSL_OVERLAPPING_RESERVATIONS,
-                            "Multiple buffers bound to cb%u.", buffer->reservation.index);
+                            "Multiple buffers bound to cb%u.", buffer->reservation.reg_index);
                     hlsl_note(ctx, &reserved_buffer->loc, VKD3D_SHADER_LOG_ERROR,
-                            "Buffer %s is already bound to cb%u.", reserved_buffer->name, buffer->reservation.index);
+                            "Buffer %s is already bound to cb%u.", reserved_buffer->name, buffer->reservation.reg_index);
                 }
 
-                buffer->reg.id = buffer->reservation.index;
+                buffer->reg.id = buffer->reservation.reg_index;
                 buffer->reg.allocated = true;
                 TRACE("Allocated reserved %s to cb%u.\n", buffer->name, index);
             }
-            else if (!buffer->reservation.type)
+            else if (!buffer->reservation.reg_type)
             {
                 while (get_reserved_buffer(ctx, index))
                     ++index;
@@ -3043,7 +3140,7 @@ static void allocate_objects(struct hlsl_ctx *ctx, enum hlsl_regset regset)
                         regset_name, index);
             }
 
-            var->regs[regset].id = var->reg_reservation.index;
+            var->regs[regset].id = var->reg_reservation.reg_index;
             var->regs[regset].allocated = true;
             TRACE("Allocated reserved %s to %c%u.\n", var->name, regset_name, var->regs[regset].id);
         }
diff --git a/libs/vkd3d-shader/hlsl_sm4.c b/libs/vkd3d-shader/hlsl_sm4.c
index 3fc44d9c566ae84d7bfc945541b10b42b28f0902..6dd76d67faff892d5c394940690b0bc92b3548ed 100644
--- a/libs/vkd3d-shader/hlsl_sm4.c
+++ b/libs/vkd3d-shader/hlsl_sm4.c
@@ -594,7 +594,7 @@ static void write_sm4_rdef(struct hlsl_ctx *ctx, struct dxbc_writer *dxbc)
         var = extern_resources[i];
         regset = hlsl_type_get_regset(var->data_type);
 
-        if (var->reg_reservation.type)
+        if (var->reg_reservation.reg_type)
             flags |= D3D_SIF_USERPACKED;
 
         put_u32(&buffer, 0); /* name */
@@ -624,7 +624,7 @@ static void write_sm4_rdef(struct hlsl_ctx *ctx, struct dxbc_writer *dxbc)
         if (!cbuffer->reg.allocated)
             continue;
 
-        if (cbuffer->reservation.type)
+        if (cbuffer->reservation.reg_type)
             flags |= D3D_SIF_USERPACKED;
 
         put_u32(&buffer, 0); /* name */
diff --git a/tests/cbuffer.shader_test b/tests/cbuffer.shader_test
index 07ef18dba0b85ab498259c8da667dcba2127cfa4..9cf42d9d3de6fdb845bc0fe4ad07eae244357957 100644
--- a/tests/cbuffer.shader_test
+++ b/tests/cbuffer.shader_test
@@ -15,3 +15,691 @@ float4 main() : sv_target
 uniform 0 float4 1.0 2.0 3.0 4.0
 draw quad
 probe all rgba (1.0, 2.0, 3.0, 4.0)
+
+
+% SM1 buffer offset allocation follows different rules than SM4.
+% Those would have to be tested separately.
+[require]
+shader model >= 4.0
+
+
+[pixel shader fail]
+cbuffer buffer
+{
+    float4 a : packoffset(c1invalid_extra_chars);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+
+% Respect register boundaries
+[pixel shader]
+cbuffer buffer
+{
+    float2 a;
+    float2 b;
+    float2 c;
+    float3 d;
+}
+
+float4 main() : sv_target
+{
+    return float4(a.x, b.x, c.x, d.x);
+}
+
+[test]
+uniform 0 float4 0.0 1.0 2.0 3.0
+uniform 4 float4 4.0 5.0 6.0 7.0
+uniform 8 float4 8.0 9.0 10.0 11.0
+uniform 12 float4 12.0 13.0 14.0 15.0
+draw quad
+probe all rgba (0.0, 2.0, 4.0, 8.0)
+
+
+[pixel shader]
+cbuffer buffer
+{
+    float a;
+    float b[2];
+    float c;
+}
+
+float4 main() : sv_target
+{
+    return float4(a, b[0], b[1], c);
+}
+
+[test]
+uniform 0 float4 0.0 1.0 2.0 3.0
+uniform 4 float4 4.0 5.0 6.0 7.0
+uniform 8 float4 8.0 9.0 10.0 11.0
+draw quad
+probe all rgba (0.0, 4.0, 8.0, 9.0)
+
+
+[pixel shader]
+cbuffer buffer
+{
+    float a;
+    struct
+    {
+        float b;
+        float c;
+    } p;
+    float d;
+}
+
+float4 main() : sv_target
+{
+    return float4(a, p.b, p.c, d);
+}
+
+[test]
+uniform 0 float4 0.0 1.0 2.0 3.0
+uniform 4 float4 4.0 5.0 6.0 7.0
+uniform 8 float4 8.0 9.0 10.0 11.0
+uniform 12 float4 12.0 13.0 14.0 15.0
+draw quad
+probe all rgba (0.0, 4.0, 5.0, 6.0)
+
+
+[pixel shader fail]
+// Elements cannot overlap if buffer is used.
+cbuffer buffer
+{
+    float c : packoffset(c0);
+    float4 a[2] : packoffset(c1);
+    float4 b[2] : packoffset(c2);
+}
+
+float4 main() : sv_target
+{
+    return c;
+}
+
+
+[pixel shader]
+// Elements can overlap if buffer is not used.
+cbuffer buffer
+{
+    float4 a[2] : packoffset(c1);
+    float4 b[2] : packoffset(c2);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+
+[pixel shader]
+cbuffer buffer
+{
+    float4 a : packoffset(c1);
+    float4 b : packoffset(c2);
+}
+
+float4 main() : sv_target
+{
+    return 100 * a + b;
+}
+
+[test]
+uniform 0 float4 1.0 2.0 3.0 4.0
+uniform 4 float4 5.0 6.0 7.0 8.0
+uniform 8 float4 9.0 10.0 11.0 12.0
+draw quad
+probe all rgba (509, 610, 711, 812)
+
+
+[pixel shader]
+struct apple
+{
+    float2 a;
+    float b;
+    float4 c;
+};
+
+cbuffer buffer
+{
+    float4 foo : packoffset(c3);
+    struct apple bar : packoffset(c1);
+}
+
+float4 main() : sv_target
+{
+    return 1000 * foo + 100 * float4(bar.a, 0, 0) + 10 * float4(bar.b, 0, 0, 0) + bar.c;
+}
+
+[test]
+uniform 0 float4 0.0 1.0 2.0 3.0
+uniform 4 float4 4.0 5.0 6.0 7.0
+uniform 8 float4 8.0 9.0 10.0 11.0
+uniform 12 float4 12.0 13.0 14.0 15.0
+draw quad
+probe all rgba (12468.0, 13509.0, 14010.0, 15011.0)
+
+
+[pixel shader]
+cbuffer buffer
+{
+    float2 c : packoffset(c0.y);
+}
+
+float4 main() : sv_target
+{
+    return float4(c, c);
+}
+
+[test]
+uniform 0 float4 1.0 2.0 3.0 4.0
+draw quad
+probe all rgba (2.0, 3.0, 2.0, 3.0)
+
+
+[pixel shader fail]
+// Elements must respect register boundaries.
+cbuffer buffer
+{
+    float4 c : packoffset(c0.z);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+
+[pixel shader fail]
+// Matrices must be aligned.
+cbuffer buffer
+{
+    float1x1 m : packoffset(c0.y);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+
+[pixel shader fail]
+// Arrays must be aligned.
+cbuffer buffer
+{
+    float a[1] : packoffset(c0.y);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+
+[pixel shader fail]
+// Structs must be aligned.
+struct apple
+{
+    float p;
+};
+
+cbuffer buffer
+{
+    struct apple a : packoffset(c0.y);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+
+[pixel shader fail]
+// Invalid offset on unused buffer.
+cbuffer buffer
+{
+    float3 a : packoffset(c0.z);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+
+[pixel shader fail]
+// Invalid offset on unused variable.
+cbuffer buffer
+{
+    float a : packoffset(c0);
+    float3 b : packoffset(c0.z);
+}
+
+float4 main() : sv_target
+{
+    return a;
+}
+
+
+[pixel shader]
+cbuffer buffer
+{
+    float4 a : packoffset(c1);
+    float1 b : packoffset(c0);
+    float1 c : packoffset(c0.y);
+}
+
+float4 main() : sv_target
+{
+    return 100 * a + 10 * b + c;
+}
+
+[test]
+uniform 0 float 1.0
+uniform 1 float 2.0
+uniform 4 float4 5.0 6.0 7.0 8.0
+draw quad
+probe all rgba (512.0, 612.0, 712.0, 812.0)
+
+
+[pixel shader fail]
+// packoffset cannot be used unless all elements use it.
+cbuffer buffer
+{
+    float4 a : packoffset(c0);
+    float4 b;
+}
+
+float4 main() : sv_target
+{
+    return a + b;
+}
+
+
+[pixel shader]
+cbuffer buffer
+{
+    float2 c : packoffset(c0.b);
+}
+
+float4 main() : sv_target
+{
+    return float4(c, c);
+}
+
+[test]
+uniform 0 float4 1.0 2.0 3.0 4.0
+draw quad
+probe all rgba (3.0, 4.0, 3.0, 4.0)
+
+
+[pixel shader fail]
+cbuffer buffer
+{
+    float2 c : packoffset(c0.xy);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+
+[pixel shader fail]
+cbuffer buffer
+{
+    float4x4 mat : packoffset(c0._m00);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+
+[pixel shader fail]
+cbuffer buffer
+{
+    float4 a : packoffset(c0._m00);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+
+[pixel shader fail]
+cbuffer buffer
+{
+    float2 c : packoffset(c0.xz);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+
+% packoffset cannot be used outside a constant buffer.
+[pixel shader fail]
+float4 a : packoffset(c0);
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+[pixel shader fail]
+float4 foo(float4 a : packoffset(c0))
+{
+    return a;
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+[pixel shader fail]
+float4 foo(float4 a) : packoffset(c0)
+{
+    return a;
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+[pixel shader fail]
+struct apple
+{
+    float4 a : packoffset(c0);
+};
+
+cbuffer buffer
+{
+    struct apple a;
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+[pixel shader fail]
+cbuffer buffer
+{
+    struct
+    {
+        float4 a : packoffset(c0);
+    } s;
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+
+[texture 0]
+size (1, 1)
+0.0 0.0 0.0 4.0
+
+[sampler 0]
+filter linear linear linear
+address clamp clamp clamp
+
+
+[pixel shader]
+// Resources are allowed inside constant buffers but they behave as regular resources.
+cbuffer buffer
+{
+    float4 a;
+    Texture2D tex;
+    sampler sam;
+    float4 b;
+}
+
+float4 main() : sv_target
+{
+    return a + b + tex.Sample(sam, float2(0, 0));
+}
+
+[test]
+uniform 0 float4 1.0 0.0 0.0 0.0
+uniform 4 float4 0.0 2.0 0.0 0.0
+uniform 8 float4 0.0 0.0 3.0 0.0
+draw quad
+probe all rgba (1.0, 2.0, 0.0, 4.0)
+
+
+% packoffset() cannot be used to specify other types of registers
+[pixel shader fail]
+cbuffer buffer
+{
+    sampler sam : packoffset(s0);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+[pixel shader fail]
+cbuffer buffer
+{
+    Texture2D tex : packoffset(t0);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+
+[texture 0]
+size (1, 1)
+1.0 1.0 1.0 1.0
+
+[texture 1]
+size (1, 1)
+2.0 2.0 2.0 2.0
+
+
+[pixel shader]
+// packoffset() can be used in Textures, doesn't change the allocated t register.
+cbuffer buffer
+{
+    Texture2D tex : packoffset(c1);
+}
+
+float4 main() : sv_target
+{
+    return tex.Load(int3(0, 0, 0));
+}
+
+[test]
+draw quad
+probe all rgba (1.0, 1.0, 1.0, 1.0)
+
+
+% Samplers cannot have packoffset(), unless register() is also specified, or they are not used.
+% Note: In SM1 the rules are different: packoffset() is allowed for samplers, but they cannot be
+% used together with other numeric fields, which seems like a bug.
+[pixel shader fail todo]
+Texture2D tex;
+
+cbuffer buffer
+{
+    sampler sam : packoffset(c1);
+}
+
+float4 main() : sv_target
+{
+    return tex.Sample(sam, float2(0, 0));
+}
+
+[pixel shader todo]
+Texture2D tex;
+
+cbuffer buffer
+{
+    sampler sam : packoffset(c1) : register(s0);
+}
+
+float4 main() : sv_target
+{
+    return tex.Sample(sam, float2(0, 0));
+}
+
+[pixel shader]
+cbuffer buffer
+{
+    sampler sam : packoffset(c1);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+[pixel shader]
+cbuffer buffer
+{
+    sampler sam : packoffset(c1);
+    float4 a : packoffset(c0);
+}
+
+float4 main() : sv_target
+{
+    return a;
+}
+
+
+% When packoffset is used in one field, resources are also expected to have a reservation.
+[pixel shader fail]
+cbuffer buffer
+{
+    float4 foo : packoffset(c0);
+    Texture2D tex;
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+[pixel shader fail]
+cbuffer buffer
+{
+    float4 foo : packoffset(c0);
+    sampler sam;
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+% register() can be used instead of packoffset().
+[pixel shader]
+cbuffer buffer
+{
+    float4 foo : packoffset(c0);
+    Texture2D tex : register(t1);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+[pixel shader]
+cbuffer buffer
+{
+    float4 foo : packoffset(c0);
+    sampler sam : register(s1);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+% Using register() alone is considered manual packing for resources, so the other fields expect packoffset().
+[pixel shader fail]
+cbuffer buffer
+{
+    float4 foo;
+    Texture2D tex : register(t1);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+[pixel shader fail]
+cbuffer buffer
+{
+    float4 foo;
+    sampler sam : register(s1);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+% Using register() alone is not considered manual packing for non-resources.
+[pixel shader]
+cbuffer buffer
+{
+    float4 foo : register(c0);
+    Texture2D tex;
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+
+[require]
+shader model >= 5.0
+
+[texture 0]
+size (1, 1)
+0.0 0.0 0.0 0.5
+
+[pixel shader todo]
+struct apple
+{
+    float2 a;
+    Texture2D tex;
+    float b;
+};
+
+cbuffer buffer
+{
+    float4 foo : packoffset(c3);
+    struct apple bar : packoffset(c1);
+}
+
+float4 main() : sv_target
+{
+    return 10 * foo + float4(bar.a, 0, 0) + float4(0, 0, bar.b, 0) + bar.tex.Load(int3(0, 0, 0));
+}
+
+[test]
+uniform 0 float4 0.0 1.0 2.0 3.0
+uniform 4 float4 4.0 5.0 6.0 7.0
+uniform 8 float4 8.0 9.0 10.0 11.0
+uniform 12 float4 12.0 13.0 14.0 15.0
+todo draw quad
+todo probe all rgba (124.0, 135.0, 146.0, 150.5)