From cf59ad9c9f9f7b8fb5b78fca6e390ef12de0bf31 Mon Sep 17 00:00:00 2001
From: Francisco Casas <fcasas@codeweavers.com>
Date: Tue, 21 Feb 2023 20:16:23 -0300
Subject: [PATCH 01/10] tests: Test cbuffer element offsets.

---
 tests/cbuffer.shader_test | 77 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 77 insertions(+)

diff --git a/tests/cbuffer.shader_test b/tests/cbuffer.shader_test
index 07ef18dba0..d1d83b3bf3 100644
--- a/tests/cbuffer.shader_test
+++ b/tests/cbuffer.shader_test
@@ -15,3 +15,80 @@ 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
+
+
+% 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)
-- 
GitLab


From 496f9b42cb6c7f63dba82588c3270649678954e6 Mon Sep 17 00:00:00 2001
From: Francisco Casas <fcasas@codeweavers.com>
Date: Mon, 6 Feb 2023 19:17:24 -0300
Subject: [PATCH 02/10] tests: Test packoffset().

---
 tests/cbuffer.shader_test | 340 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 340 insertions(+)

diff --git a/tests/cbuffer.shader_test b/tests/cbuffer.shader_test
index d1d83b3bf3..7fa787d99e 100644
--- a/tests/cbuffer.shader_test
+++ b/tests/cbuffer.shader_test
@@ -92,3 +92,343 @@ 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 todo]
+// 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 todo]
+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
+todo draw quad
+todo probe all rgba (509, 610, 711, 812)
+
+
+[pixel shader todo]
+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
+todo draw quad
+todo probe all rgba (12468.0, 13509.0, 14010.0, 15011.0)
+
+
+[pixel shader todo]
+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
+todo draw quad
+todo 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 todo]
+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
+todo draw quad
+todo 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 todo]
+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
+todo draw quad
+todo 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;
+}
-- 
GitLab


From 9b7097169644af25d000bfbc3282253524d328fa Mon Sep 17 00:00:00 2001
From: Francisco Casas <fcasas@codeweavers.com>
Date: Mon, 6 Mar 2023 19:53:29 -0300
Subject: [PATCH 03/10] vkd3d-shader/hlsl: Rename struct hlsl_reg_reservation
 fields.

---
 libs/vkd3d-shader/hlsl.h         |  4 ++--
 libs/vkd3d-shader/hlsl.y         | 10 +++++-----
 libs/vkd3d-shader/hlsl_codegen.c | 25 +++++++++++++------------
 libs/vkd3d-shader/hlsl_sm4.c     |  4 ++--
 4 files changed, 22 insertions(+), 21 deletions(-)

diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h
index c8e267d295..e7061dbe16 100644
--- a/libs/vkd3d-shader/hlsl.h
+++ b/libs/vkd3d-shader/hlsl.h
@@ -346,8 +346,8 @@ struct hlsl_attribute
  *   using the register(·) syntax */
 struct hlsl_reg_reservation
 {
-    char type;
-    unsigned int index;
+    char reg_type;
+    unsigned int reg_index;
 };
 
 struct hlsl_ir_var
diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y
index afcc457537..da82cb376e 100644
--- a/libs/vkd3d-shader/hlsl.y
+++ b/libs/vkd3d-shader/hlsl.y
@@ -1084,12 +1084,12 @@ 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;
 }
 
@@ -4349,7 +4349,7 @@ 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 (($$.decl = get_func_decl(&ctx->functions, $3, &$5)))
@@ -4477,12 +4477,12 @@ colon_attribute:
       %empty
         {
             $$.semantic.name = NULL;
-            $$.reg_reservation.type = 0;
+            $$.reg_reservation.reg_type = 0;
         }
     | semantic
         {
             $$.semantic = $1;
-            $$.reg_reservation.type = 0;
+            $$.reg_reservation.reg_type = 0;
         }
     | register_opt
         {
diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c
index 6c529f1913..411aa79fdb 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,7 +2899,7 @@ 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;
@@ -2941,23 +2942,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 +3044,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 3fc44d9c56..6dd76d67fa 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 */
-- 
GitLab


From 4aca335f42b0db5c48500414cb006cd1967ab0be Mon Sep 17 00:00:00 2001
From: Francisco Casas <fcasas@codeweavers.com>
Date: Wed, 22 Feb 2023 14:53:17 -0300
Subject: [PATCH 04/10] vkd3d-shader/hlsl: Parse packoffset().

---
 libs/vkd3d-shader/hlsl.h  | 12 ++++--
 libs/vkd3d-shader/hlsl.l  |  1 +
 libs/vkd3d-shader/hlsl.y  | 90 ++++++++++++++++++++++++++++++++++++++-
 tests/cbuffer.shader_test | 50 +++++++++++++---------
 4 files changed, 129 insertions(+), 24 deletions(-)

diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h
index e7061dbe16..a19a65c550 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 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. */
diff --git a/libs/vkd3d-shader/hlsl.l b/libs/vkd3d-shader/hlsl.l
index adff1da04d..10751bbe9b 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 da82cb376e..f3196b5bda 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;
 
@@ -1093,6 +1101,52 @@ static struct hlsl_reg_reservation parse_reg_reservation(const char *reg_string)
     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;
+
+    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;
+}
+
 static struct hlsl_ir_function_decl *get_func_decl(struct rb_tree *funcs,
         const char *name, const struct hlsl_func_parameters *parameters)
 {
@@ -2043,6 +2097,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 +3907,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 +4061,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
 
@@ -4351,6 +4414,9 @@ func_prototype_no_attrs:
 
             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)))
             {
@@ -4478,17 +4544,24 @@ colon_attribute:
         {
             $$.semantic.name = NULL;
             $$.reg_reservation.reg_type = 0;
+            $$.reg_reservation.offset_type = 0;
         }
     | semantic
         {
             $$.semantic = $1;
             $$.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 +4591,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/tests/cbuffer.shader_test b/tests/cbuffer.shader_test
index 7fa787d99e..628a441a81 100644
--- a/tests/cbuffer.shader_test
+++ b/tests/cbuffer.shader_test
@@ -23,6 +23,18 @@ probe all rgba (1.0, 2.0, 3.0, 4.0)
 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
@@ -94,7 +106,7 @@ draw quad
 probe all rgba (0.0, 4.0, 5.0, 6.0)
 
 
-[pixel shader fail]
+[pixel shader fail todo]
 // Elements cannot overlap if buffer is used.
 cbuffer buffer
 {
@@ -109,7 +121,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader todo]
+[pixel shader]
 // Elements can overlap if buffer is not used.
 cbuffer buffer
 {
@@ -123,7 +135,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader todo]
+[pixel shader]
 cbuffer buffer
 {
     float4 a : packoffset(c1);
@@ -139,11 +151,11 @@ float4 main() : sv_target
 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
-todo draw quad
+draw quad
 todo probe all rgba (509, 610, 711, 812)
 
 
-[pixel shader todo]
+[pixel shader]
 struct apple
 {
     float2 a;
@@ -167,11 +179,11 @@ 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
+draw quad
 todo probe all rgba (12468.0, 13509.0, 14010.0, 15011.0)
 
 
-[pixel shader todo]
+[pixel shader]
 cbuffer buffer
 {
     float2 c : packoffset(c0.y);
@@ -184,11 +196,11 @@ float4 main() : sv_target
 
 [test]
 uniform 0 float4 1.0 2.0 3.0 4.0
-todo draw quad
+draw quad
 todo probe all rgba (2.0, 3.0, 2.0, 3.0)
 
 
-[pixel shader fail]
+[pixel shader fail todo]
 // Elements must respect register boundaries.
 cbuffer buffer
 {
@@ -201,7 +213,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader fail]
+[pixel shader fail todo]
 // Matrices must be aligned.
 cbuffer buffer
 {
@@ -214,7 +226,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader fail]
+[pixel shader fail todo]
 // Arrays must be aligned.
 cbuffer buffer
 {
@@ -227,7 +239,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader fail]
+[pixel shader fail todo]
 // Structs must be aligned.
 struct apple
 {
@@ -245,7 +257,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader fail]
+[pixel shader fail todo]
 // Invalid offset on unused buffer.
 cbuffer buffer
 {
@@ -258,7 +270,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader fail]
+[pixel shader fail todo]
 // Invalid offset on unused variable.
 cbuffer buffer
 {
@@ -272,7 +284,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader todo]
+[pixel shader]
 cbuffer buffer
 {
     float4 a : packoffset(c1);
@@ -289,11 +301,11 @@ float4 main() : sv_target
 uniform 0 float 1.0
 uniform 1 float 2.0
 uniform 4 float4 5.0 6.0 7.0 8.0
-todo draw quad
+draw quad
 todo probe all rgba (512.0, 612.0, 712.0, 812.0)
 
 
-[pixel shader fail]
+[pixel shader fail todo]
 // packoffset cannot be used unless all elements use it.
 cbuffer buffer
 {
@@ -307,7 +319,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader todo]
+[pixel shader]
 cbuffer buffer
 {
     float2 c : packoffset(c0.b);
@@ -320,7 +332,7 @@ float4 main() : sv_target
 
 [test]
 uniform 0 float4 1.0 2.0 3.0 4.0
-todo draw quad
+draw quad
 todo probe all rgba (3.0, 4.0, 3.0, 4.0)
 
 
-- 
GitLab


From 7777c32cac7e9a61e38a79c8efb97632f3b2a4dc Mon Sep 17 00:00:00 2001
From: Francisco Casas <fcasas@codeweavers.com>
Date: Tue, 21 Feb 2023 17:08:39 -0300
Subject: [PATCH 05/10] vkd3d-shader/hlsl: Support packoffset().

---
 libs/vkd3d-shader/hlsl_codegen.c | 46 +++++++++++++++++++++++++++-----
 tests/cbuffer.shader_test        | 22 +++++++--------
 2 files changed, 51 insertions(+), 17 deletions(-)

diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c
index 411aa79fdb..eafc06e2ea 100644
--- a/libs/vkd3d-shader/hlsl_codegen.c
+++ b/libs/vkd3d-shader/hlsl_codegen.c
@@ -2905,17 +2905,51 @@ static const struct hlsl_buffer *get_reserved_buffer(struct hlsl_ctx *ctx, uint3
     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 allocate_buffers(struct hlsl_ctx *ctx)
@@ -2931,7 +2965,7 @@ 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);
         }
     }
 
diff --git a/tests/cbuffer.shader_test b/tests/cbuffer.shader_test
index 628a441a81..bf71162900 100644
--- a/tests/cbuffer.shader_test
+++ b/tests/cbuffer.shader_test
@@ -152,7 +152,7 @@ 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
-todo probe all rgba (509, 610, 711, 812)
+probe all rgba (509, 610, 711, 812)
 
 
 [pixel shader]
@@ -180,7 +180,7 @@ 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
-todo probe all rgba (12468.0, 13509.0, 14010.0, 15011.0)
+probe all rgba (12468.0, 13509.0, 14010.0, 15011.0)
 
 
 [pixel shader]
@@ -197,10 +197,10 @@ float4 main() : sv_target
 [test]
 uniform 0 float4 1.0 2.0 3.0 4.0
 draw quad
-todo probe all rgba (2.0, 3.0, 2.0, 3.0)
+probe all rgba (2.0, 3.0, 2.0, 3.0)
 
 
-[pixel shader fail todo]
+[pixel shader fail]
 // Elements must respect register boundaries.
 cbuffer buffer
 {
@@ -213,7 +213,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader fail todo]
+[pixel shader fail]
 // Matrices must be aligned.
 cbuffer buffer
 {
@@ -226,7 +226,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader fail todo]
+[pixel shader fail]
 // Arrays must be aligned.
 cbuffer buffer
 {
@@ -239,7 +239,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader fail todo]
+[pixel shader fail]
 // Structs must be aligned.
 struct apple
 {
@@ -257,7 +257,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader fail todo]
+[pixel shader fail]
 // Invalid offset on unused buffer.
 cbuffer buffer
 {
@@ -270,7 +270,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader fail todo]
+[pixel shader fail]
 // Invalid offset on unused variable.
 cbuffer buffer
 {
@@ -302,7 +302,7 @@ uniform 0 float 1.0
 uniform 1 float 2.0
 uniform 4 float4 5.0 6.0 7.0 8.0
 draw quad
-todo probe all rgba (512.0, 612.0, 712.0, 812.0)
+probe all rgba (512.0, 612.0, 712.0, 812.0)
 
 
 [pixel shader fail todo]
@@ -333,7 +333,7 @@ float4 main() : sv_target
 [test]
 uniform 0 float4 1.0 2.0 3.0 4.0
 draw quad
-todo probe all rgba (3.0, 4.0, 3.0, 4.0)
+probe all rgba (3.0, 4.0, 3.0, 4.0)
 
 
 [pixel shader fail]
-- 
GitLab


From 4448d114ad6b279c8631aa9229fb04dda4d92561 Mon Sep 17 00:00:00 2001
From: Francisco Casas <fcasas@codeweavers.com>
Date: Tue, 21 Feb 2023 21:53:32 -0300
Subject: [PATCH 06/10] vkd3d-shader/hlsl: Detect overlaps in cbuffer offsets.

---
 libs/vkd3d-shader/hlsl_codegen.c | 42 ++++++++++++++++++++++++++++++++
 tests/cbuffer.shader_test        |  2 +-
 2 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c
index eafc06e2ea..2e10aa55e0 100644
--- a/libs/vkd3d-shader/hlsl_codegen.c
+++ b/libs/vkd3d-shader/hlsl_codegen.c
@@ -2952,6 +2952,46 @@ static void calculate_buffer_offset(struct hlsl_ctx *ctx, struct hlsl_ir_var *va
         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);
+        }
+    }
+}
+
 static void allocate_buffers(struct hlsl_ctx *ctx)
 {
     struct hlsl_buffer *buffer;
@@ -2969,6 +3009,8 @@ static void allocate_buffers(struct hlsl_ctx *ctx)
         }
     }
 
+    validate_buffer_offsets(ctx);
+
     LIST_FOR_EACH_ENTRY(buffer, &ctx->buffers, struct hlsl_buffer, entry)
     {
         if (!buffer->used_size)
diff --git a/tests/cbuffer.shader_test b/tests/cbuffer.shader_test
index bf71162900..d8cfc351ee 100644
--- a/tests/cbuffer.shader_test
+++ b/tests/cbuffer.shader_test
@@ -106,7 +106,7 @@ draw quad
 probe all rgba (0.0, 4.0, 5.0, 6.0)
 
 
-[pixel shader fail todo]
+[pixel shader fail]
 // Elements cannot overlap if buffer is used.
 cbuffer buffer
 {
-- 
GitLab


From 60237cb77324126dbce3ef0744880fa9fad8db8d Mon Sep 17 00:00:00 2001
From: Francisco Casas <fcasas@codeweavers.com>
Date: Tue, 21 Feb 2023 22:39:24 -0300
Subject: [PATCH 07/10] vkd3d-shader/hlsl: Don't allow manual and automatic
 cbuffer offset packing.

---
 libs/vkd3d-shader/hlsl.h         |  3 +++
 libs/vkd3d-shader/hlsl_codegen.c | 19 +++++++++++++++++++
 tests/cbuffer.shader_test        |  2 +-
 3 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h
index a19a65c550..38a7715e99 100644
--- a/libs/vkd3d-shader/hlsl.h
+++ b/libs/vkd3d-shader/hlsl.h
@@ -678,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_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c
index 2e10aa55e0..610b576cd1 100644
--- a/libs/vkd3d-shader/hlsl_codegen.c
+++ b/libs/vkd3d-shader/hlsl_codegen.c
@@ -2990,6 +2990,25 @@ static void validate_buffer_offsets(struct hlsl_ctx *ctx)
                         var1->name, var2->name);
         }
     }
+
+    LIST_FOR_EACH_ENTRY(var1, &ctx->extern_vars, struct hlsl_ir_var, extern_entry)
+    {
+        buffer = var1->buffer;
+        if (!buffer)
+            continue;
+
+        if (var1->reg_reservation.offset_type == 'c')
+            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)
diff --git a/tests/cbuffer.shader_test b/tests/cbuffer.shader_test
index d8cfc351ee..8c6e1724dd 100644
--- a/tests/cbuffer.shader_test
+++ b/tests/cbuffer.shader_test
@@ -305,7 +305,7 @@ draw quad
 probe all rgba (512.0, 612.0, 712.0, 812.0)
 
 
-[pixel shader fail todo]
+[pixel shader fail]
 // packoffset cannot be used unless all elements use it.
 cbuffer buffer
 {
-- 
GitLab


From bf4a125087c378ad9e1727bf28723c126545f3a6 Mon Sep 17 00:00:00 2001
From: Francisco Casas <fcasas@codeweavers.com>
Date: Tue, 28 Feb 2023 19:03:04 -0300
Subject: [PATCH 08/10] vkd3d-shader/hlsl: Ignore packoffset() contents for
 SM1.

---
 libs/vkd3d-shader/hlsl.y | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y
index f3196b5bda..79ec970a0a 100644
--- a/libs/vkd3d-shader/hlsl.y
+++ b/libs/vkd3d-shader/hlsl.y
@@ -1107,6 +1107,9 @@ static struct hlsl_reg_reservation parse_packoffset(struct hlsl_ctx *ctx, const
     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)
     {
-- 
GitLab


From 70ff7aadddb8b9787d21b9d044fade0482cb9bca Mon Sep 17 00:00:00 2001
From: Francisco Casas <fcasas@codeweavers.com>
Date: Wed, 1 Mar 2023 14:28:12 -0300
Subject: [PATCH 09/10] tests: Test packoffset() with resources inside
 cbuffers.

---
 tests/cbuffer.shader_test | 259 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 259 insertions(+)

diff --git a/tests/cbuffer.shader_test b/tests/cbuffer.shader_test
index 8c6e1724dd..e95a5235c3 100644
--- a/tests/cbuffer.shader_test
+++ b/tests/cbuffer.shader_test
@@ -444,3 +444,262 @@ 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 todo]
+cbuffer buffer
+{
+    float4 foo : packoffset(c0);
+    Texture2D tex : register(t1);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+[pixel shader todo]
+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 todo]
+cbuffer buffer
+{
+    float4 foo;
+    Texture2D tex : register(t1);
+}
+
+float4 main() : sv_target
+{
+    return 0;
+}
+
+[pixel shader fail todo]
+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)
-- 
GitLab


From f3e81327dc775e4e794618558696971ee1741fdc Mon Sep 17 00:00:00 2001
From: Francisco Casas <fcasas@codeweavers.com>
Date: Wed, 1 Mar 2023 15:57:31 -0300
Subject: [PATCH 10/10] vkd3d-shader/hlsl: Consider register() as manual
 packing for resource fields.

---
 libs/vkd3d-shader/hlsl_codegen.c | 5 +++--
 tests/cbuffer.shader_test        | 8 ++++----
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c
index 610b576cd1..5d7e426f15 100644
--- a/libs/vkd3d-shader/hlsl_codegen.c
+++ b/libs/vkd3d-shader/hlsl_codegen.c
@@ -2994,10 +2994,11 @@ static void validate_buffer_offsets(struct hlsl_ctx *ctx)
     LIST_FOR_EACH_ENTRY(var1, &ctx->extern_vars, struct hlsl_ir_var, extern_entry)
     {
         buffer = var1->buffer;
-        if (!buffer)
+        if (!buffer || buffer == ctx->globals_buffer)
             continue;
 
-        if (var1->reg_reservation.offset_type == 'c')
+        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;
diff --git a/tests/cbuffer.shader_test b/tests/cbuffer.shader_test
index e95a5235c3..9cf42d9d3d 100644
--- a/tests/cbuffer.shader_test
+++ b/tests/cbuffer.shader_test
@@ -607,7 +607,7 @@ float4 main() : sv_target
 }
 
 % register() can be used instead of packoffset().
-[pixel shader todo]
+[pixel shader]
 cbuffer buffer
 {
     float4 foo : packoffset(c0);
@@ -619,7 +619,7 @@ float4 main() : sv_target
     return 0;
 }
 
-[pixel shader todo]
+[pixel shader]
 cbuffer buffer
 {
     float4 foo : packoffset(c0);
@@ -632,7 +632,7 @@ float4 main() : sv_target
 }
 
 % Using register() alone is considered manual packing for resources, so the other fields expect packoffset().
-[pixel shader fail todo]
+[pixel shader fail]
 cbuffer buffer
 {
     float4 foo;
@@ -644,7 +644,7 @@ float4 main() : sv_target
     return 0;
 }
 
-[pixel shader fail todo]
+[pixel shader fail]
 cbuffer buffer
 {
     float4 foo;
-- 
GitLab