From: Francisco Casas fcasas@codeweavers.com
--- libs/vkd3d-shader/hlsl.c | 6 +- libs/vkd3d-shader/hlsl.h | 10 +++- libs/vkd3d-shader/hlsl.l | 1 + libs/vkd3d-shader/hlsl.y | 98 ++++++++++++++++++++++++++++++-- libs/vkd3d-shader/hlsl_codegen.c | 4 +- tests/cbuffer.shader_test | 26 ++++----- 6 files changed, 121 insertions(+), 24 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index 42a1d52f..7d47feec 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -833,7 +833,7 @@ struct hlsl_ir_expr *hlsl_new_copy(struct hlsl_ctx *ctx, struct hlsl_ir_node *no
struct hlsl_ir_var *hlsl_new_var(struct hlsl_ctx *ctx, const char *name, struct hlsl_type *type, const struct vkd3d_shader_location loc, const struct hlsl_semantic *semantic, unsigned int modifiers, - const struct hlsl_reg_reservation *reg_reservation) + const struct hlsl_reg_reservation *reg_reservation, const struct hlsl_reg_reservation *offset_reservation) { struct hlsl_ir_var *var;
@@ -848,6 +848,8 @@ struct hlsl_ir_var *hlsl_new_var(struct hlsl_ctx *ctx, const char *name, struct var->storage_modifiers = modifiers; if (reg_reservation) var->reg_reservation = *reg_reservation; + if (offset_reservation) + var->offset_reservation = *offset_reservation; return var; }
@@ -867,7 +869,7 @@ struct hlsl_ir_var *hlsl_new_synthetic_var(struct hlsl_ctx *ctx, const char *tem hlsl_release_string_buffer(ctx, string); return NULL; } - var = hlsl_new_var(ctx, name, type, *loc, NULL, 0, NULL); + var = hlsl_new_var(ctx, name, type, *loc, NULL, 0, NULL, NULL); hlsl_release_string_buffer(ctx, string); if (var) list_add_tail(&ctx->dummy_scope->vars, &var->scope_entry); diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 6fa627ed..e1037e9a 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -347,7 +347,10 @@ 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 */ + * using the register(·) syntax. + * Also used for offset reservations for objects inside constant buffers when manual packing is + * enabled through the packoffset(·) syntax. In that case, hlsl_reg_reservation.index is in + * register components. */ struct hlsl_reg_reservation { char type; @@ -367,6 +370,9 @@ struct hlsl_ir_var /* Optional register to be used as a starting point for the variable allocation, specified * by the user via the register(·) syntax. */ struct hlsl_reg_reservation reg_reservation; + /* Optional offset to be used as a starting point for variables inside constant buffers, + * specified by the user via the packoffset(·) syntax. */ + struct hlsl_reg_reservation offset_reservation;
/* Item entry in hlsl_scope.vars. Specifically hlsl_ctx.globals.vars if the variable is global. */ struct list scope_entry; @@ -1073,7 +1079,7 @@ struct hlsl_ir_node *hlsl_new_unary_expr(struct hlsl_ctx *ctx, enum hlsl_ir_expr struct vkd3d_shader_location loc); struct hlsl_ir_var *hlsl_new_var(struct hlsl_ctx *ctx, const char *name, struct hlsl_type *type, const struct vkd3d_shader_location loc, const struct hlsl_semantic *semantic, unsigned int modifiers, - const struct hlsl_reg_reservation *reg_reservation); + const struct hlsl_reg_reservation *reg_reservation, const struct hlsl_reg_reservation *offset_reservation);
void hlsl_error(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc, enum vkd3d_shader_error error, const char *fmt, ...) VKD3D_PRINTF_FUNC(4, 5); diff --git a/libs/vkd3d-shader/hlsl.l b/libs/vkd3d-shader/hlsl.l index 69ed9d9d..d27fd803 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 6c618912..4093d3a8 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -39,14 +39,14 @@ struct parse_parameter struct hlsl_type *type; const char *name; struct hlsl_semantic semantic; - struct hlsl_reg_reservation reg_reservation; + struct hlsl_reg_reservation reg_reservation, offset_reservation; unsigned int modifiers; };
struct parse_colon_attribute { struct hlsl_semantic semantic; - struct hlsl_reg_reservation reg_reservation; + struct hlsl_reg_reservation reg_reservation, offset_reservation; };
struct parse_initializer @@ -71,7 +71,7 @@ struct parse_variable_def char *name; struct parse_array_sizes arrays; struct hlsl_semantic semantic; - struct hlsl_reg_reservation reg_reservation; + struct hlsl_reg_reservation reg_reservation, offset_reservation; struct parse_initializer initializer; };
@@ -1079,7 +1079,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, ¶m->semantic, param->modifiers, ¶m->reg_reservation))) + if (param->offset_reservation.type) + hlsl_error(ctx, &loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, + "packoffset() is only allowed inside constant buffer declarations."); + + if (!(var = hlsl_new_var(ctx, param->name, param->type, loc, ¶m->semantic, param->modifiers, + ¶m->reg_reservation, NULL))) return false; var->is_param = 1;
@@ -1109,6 +1114,50 @@ 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}; + + if (!sscanf(reg_string + 1, "%u", &reservation.index)) + { + hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, + "Invalid packoffset() syntax."); + return reservation; + } + + reservation.type = reg_string[0]; + if (reservation.type != 'c') + { + hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, + "Only 'c' registers are allowed in packoffset()."); + return reservation; + } + + reservation.index *= 4; + + if (swizzle) + { + if (strlen(swizzle) != 1) + hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, + "Invalid packoffset() swizzle "%s".", swizzle); + + if (swizzle[0] == 'x' || swizzle[0] == 'r') + reservation.index += 0; + else if (swizzle[0] == 'y' || swizzle[0] == 'g') + reservation.index += 1; + else if (swizzle[0] == 'z' || swizzle[0] == 'b') + reservation.index += 2; + else if (swizzle[0] == 'w' || swizzle[0] == 'a') + reservation.index += 3; + else + hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, + "Invalid packoffset() swizzle "%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) { @@ -2051,7 +2100,8 @@ static struct list *declare_vars(struct hlsl_ctx *ctx, struct hlsl_type *basic_t } vkd3d_free(v->arrays.sizes);
- if (!(var = hlsl_new_var(ctx, v->name, type, v->loc, &v->semantic, modifiers, &v->reg_reservation))) + if (!(var = hlsl_new_var(ctx, v->name, type, v->loc, &v->semantic, modifiers, &v->reg_reservation, + &v->offset_reservation))) { free_parse_variable_def(v); continue; @@ -2059,6 +2109,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->offset_reservation.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; @@ -3811,6 +3868,7 @@ static bool add_method_call(struct hlsl_ctx *ctx, struct list *instrs, struct hl %token KW_NAMESPACE %token KW_NOINTERPOLATION %token KW_OUT +%token KW_PACKOFFSET %token KW_PASS %token KW_PIXELSHADER %token KW_PRECISE @@ -3964,6 +4022,7 @@ static bool add_method_call(struct hlsl_ctx *ctx, struct list *instrs, struct hl %type <parameters> parameters
%type <reg_reservation> register_opt +%type <reg_reservation> packoffset_opt
%type <sampler_dim> texture_type uav_type
@@ -4316,6 +4375,9 @@ func_prototype_no_attrs:
if ($7.reg_reservation.type) FIXME("Unexpected register reservation for a function.\n"); + if ($7.offset_reservation.type) + hlsl_error(ctx, &@5, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, + "packoffset() is only allowed inside constant buffer declarations.");
if (($$.decl = get_func_decl(&ctx->functions, $3, &$5))) { @@ -4443,16 +4505,25 @@ colon_attribute: { $$.semantic.name = NULL; $$.reg_reservation.type = 0; + $$.offset_reservation.type = 0; } | semantic { $$.semantic = $1; $$.reg_reservation.type = 0; + $$.offset_reservation.type = 0; } | register_opt { $$.semantic.name = NULL; $$.reg_reservation = $1; + $$.offset_reservation.type = 0; + } + | packoffset_opt + { + $$.semantic.name = NULL; + $$.reg_reservation.type = 0; + $$.offset_reservation = $1; }
semantic: @@ -4483,6 +4554,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 { @@ -4546,6 +4632,7 @@ parameter: $$.name = $3; $$.semantic = $5.semantic; $$.reg_reservation = $5.reg_reservation; + $$.offset_reservation = $5.offset_reservation; }
texture_type: @@ -4833,6 +4920,7 @@ variable_decl: $$->arrays = $2; $$->semantic = $3.semantic; $$->reg_reservation = $3.reg_reservation; + $$->offset_reservation = $3.offset_reservation; }
state: diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index 65bd9d4c..aa1d2d3f 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -198,7 +198,7 @@ static void prepend_uniform_copy(struct hlsl_ctx *ctx, struct list *instrs, stru * can write the uniform name into the shader reflection data. */
if (!(uniform = hlsl_new_var(ctx, temp->name, temp->data_type, - temp->loc, NULL, temp->storage_modifiers, &temp->reg_reservation))) + temp->loc, NULL, temp->storage_modifiers, &temp->reg_reservation, &temp->offset_reservation))) return; list_add_before(&temp->scope_entry, &uniform->scope_entry); list_add_tail(&ctx->extern_vars, &uniform->extern_entry); @@ -238,7 +238,7 @@ static struct hlsl_ir_var *add_semantic_var(struct hlsl_ctx *ctx, struct hlsl_ir } new_semantic.index = semantic->index; if (!(ext_var = hlsl_new_var(ctx, hlsl_strdup(ctx, name->buffer), - type, var->loc, &new_semantic, modifiers, NULL))) + type, var->loc, &new_semantic, modifiers, NULL, NULL))) { hlsl_release_string_buffer(ctx, name); hlsl_cleanup_semantic(&new_semantic); diff --git a/tests/cbuffer.shader_test b/tests/cbuffer.shader_test index d89ababd..613e8b34 100644 --- a/tests/cbuffer.shader_test +++ b/tests/cbuffer.shader_test @@ -94,7 +94,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 +109,7 @@ float4 main() : sv_target }
-[pixel shader todo] +[pixel shader] // Elements can overlap if buffer is not used. cbuffer buffer { @@ -123,7 +123,7 @@ float4 main() : sv_target }
-[pixel shader todo] +[pixel shader] cbuffer buffer { float4 a : packoffset(c1); @@ -139,11 +139,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] cbuffer buffer { float2 c : packoffset(c0.y); @@ -156,11 +156,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 { @@ -173,7 +173,7 @@ float4 main() : sv_target }
-[pixel shader todo] +[pixel shader] cbuffer buffer { float4 a : packoffset(c1); @@ -190,11 +190,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 { @@ -208,7 +208,7 @@ float4 main() : sv_target }
-[pixel shader todo] +[pixel shader] cbuffer buffer { float2 c : packoffset(c0.b); @@ -221,7 +221,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)
@@ -314,7 +314,7 @@ draw quad probe all rgba (1.0, 2.0, 0.0, 4.0)
-[pixel shader fail] +[pixel shader fail todo] cbuffer buffer { float4 a : packoffset(c0);