diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index e7061dbe..a19a65c5 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 adff1da0..10751bbe 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 da82cb37..f3196b5b 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, ¶m->semantic, param->modifiers, ¶m->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, ¶m->semantic, param->modifiers, + ¶m->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 %type register_opt +%type packoffset_opt %type 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 7fa787d9..628a441a 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)