diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index 40ad0b2d..2adf1a83 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -298,6 +298,12 @@ bool hlsl_type_is_patch_array(const struct hlsl_type *type) || type->e.array.array_type == HLSL_ARRAY_PATCH_OUTPUT); } +bool hlsl_type_is_primitive_array(const struct hlsl_type *type) +{ + return type->class == HLSL_CLASS_ARRAY && (type->e.array.array_type != HLSL_ARRAY_GENERIC + || (type->modifiers & HLSL_PRIMITIVE_MODIFIERS_MASK)); +} + bool hlsl_base_type_is_integer(enum hlsl_base_type type) { switch (type) @@ -4746,6 +4752,7 @@ static bool hlsl_ctx_init(struct hlsl_ctx *ctx, const struct vkd3d_shader_compil ctx->partitioning = 0; ctx->input_control_point_count = UINT_MAX; ctx->max_vertex_count = 0; + ctx->input_primitive_type = VKD3D_PT_UNDEFINED; return true; } diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index adc7921e..9541a2f7 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -1192,12 +1192,17 @@ struct hlsl_ctx unsigned int input_control_point_count; struct hlsl_type *input_control_point_type; + /* The first declared input primitive parameter in tessellation and geometry shaders. */ + struct hlsl_ir_var *input_primitive_param; + /* Whether the current function being processed during HLSL codegen is * the patch constant function in a hull shader. */ bool is_patch_constant_func; /* The maximum output vertex count of a geometry shader. */ unsigned int max_vertex_count; + /* The input primitive type of a geometry shader. */ + enum vkd3d_primitive_type input_primitive_type; /* In some cases we generate opcodes by parsing an HLSL function and then * invoking it. If not NULL, this field is the name of the function that we @@ -1617,6 +1622,7 @@ struct hlsl_type *hlsl_get_element_type_from_path_index(struct hlsl_ctx *ctx, co struct hlsl_ir_node *idx); const char *hlsl_jump_type_to_string(enum hlsl_ir_jump_type type); +const char *hlsl_array_type_to_string(enum hlsl_array_type type); struct hlsl_type *hlsl_new_array_type(struct hlsl_ctx *ctx, struct hlsl_type *basic_type, unsigned int array_size, enum hlsl_array_type array_type); @@ -1736,6 +1742,7 @@ bool hlsl_type_is_integer(const struct hlsl_type *type); bool hlsl_type_is_resource(const struct hlsl_type *type); bool hlsl_type_is_shader(const struct hlsl_type *type); bool hlsl_type_is_patch_array(const struct hlsl_type *type); +bool hlsl_type_is_primitive_array(const struct hlsl_type *type); unsigned int hlsl_type_get_sm4_offset(const struct hlsl_type *type, unsigned int offset); bool hlsl_types_are_equal(const struct hlsl_type *t1, const struct hlsl_type *t2); diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index 363e5980..d9770aa8 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -6773,7 +6773,71 @@ static void validate_hull_shader_attributes(struct hlsl_ctx *ctx, const struct h } } -static void validate_and_record_patch_type(struct hlsl_ctx *ctx, struct hlsl_ir_var *var) +static enum vkd3d_primitive_type get_primitive_type(struct hlsl_ctx *ctx, struct hlsl_ir_var *var) +{ + uint32_t prim_modifier = var->data_type->modifiers & HLSL_PRIMITIVE_MODIFIERS_MASK; + enum vkd3d_primitive_type prim_type = VKD3D_PT_UNDEFINED; + + if (prim_modifier) + { + unsigned int count = var->data_type->e.array.elements_count; + unsigned int expected_count; + + VKD3D_ASSERT(!(prim_modifier & (prim_modifier - 1))); + + switch (prim_modifier) + { + case HLSL_PRIMITIVE_POINT: + prim_type = VKD3D_PT_POINTLIST; + expected_count = 1; + break; + + case HLSL_PRIMITIVE_LINE: + prim_type = VKD3D_PT_LINELIST; + expected_count = 2; + break; + + case HLSL_PRIMITIVE_TRIANGLE: + prim_type = VKD3D_PT_TRIANGLELIST; + expected_count = 3; + break; + + case HLSL_PRIMITIVE_LINEADJ: + prim_type = VKD3D_PT_LINELIST_ADJ; + expected_count = 4; + break; + + case HLSL_PRIMITIVE_TRIANGLEADJ: + prim_type = VKD3D_PT_TRIANGLELIST_ADJ; + expected_count = 6; + break; + + default: + vkd3d_unreachable(); + } + + if (count != expected_count) + { + struct vkd3d_string_buffer *string; + + if ((string = hlsl_modifiers_to_string(ctx, prim_modifier))) + hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_CONTROL_POINT_COUNT, + "Control point count %u does not match the expect count %u for the %s input primitive type.", + count, expected_count, string->buffer); + hlsl_release_string_buffer(ctx, string); + } + } + + /* Patch types take precedence over primitive modifiers. */ + if (hlsl_type_is_patch_array(var->data_type)) + prim_type = VKD3D_PT_PATCH; + + VKD3D_ASSERT(prim_type != VKD3D_PT_UNDEFINED); + return prim_type; +} + + +static void validate_and_record_prim_type(struct hlsl_ctx *ctx, struct hlsl_ir_var *var) { unsigned int control_point_count = var->data_type->e.array.elements_count; enum hlsl_array_type array_type = var->data_type->e.array.array_type; @@ -6791,7 +6855,7 @@ static void validate_and_record_patch_type(struct hlsl_ctx *ctx, struct hlsl_ir_ return; } } - else + else if (array_type == HLSL_ARRAY_PATCH_OUTPUT) { if (!ctx->is_patch_constant_func && profile->type != VKD3D_SHADER_TYPE_DOMAIN) { @@ -6802,6 +6866,30 @@ static void validate_and_record_patch_type(struct hlsl_ctx *ctx, struct hlsl_ir_ } } + if ((var->data_type->modifiers & HLSL_PRIMITIVE_MODIFIERS_MASK) && profile->type != VKD3D_SHADER_TYPE_GEOMETRY) + { + hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INCOMPATIBLE_PROFILE, + "Input primitive parameters can only be used in geometry shaders."); + return; + } + + if (profile->type == VKD3D_SHADER_TYPE_GEOMETRY) + { + enum vkd3d_primitive_type prim_type = get_primitive_type(ctx, var); + + if (ctx->input_primitive_type == VKD3D_PT_UNDEFINED) + { + ctx->input_primitive_type = prim_type; + } + else if (ctx->input_primitive_type != prim_type) + { + hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, + "Input primitive type does not match the previously declared type."); + hlsl_note(ctx, &ctx->input_primitive_param->loc, VKD3D_SHADER_LOG_ERROR, + "The input primitive was previously declared here."); + } + } + if (control_point_count > 32) { hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_CONTROL_POINT_COUNT, @@ -6814,7 +6902,7 @@ static void validate_and_record_patch_type(struct hlsl_ctx *ctx, struct hlsl_ir_ { if (control_point_count != ctx->output_control_point_count) hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_CONTROL_POINT_COUNT, - "Output control point count %u does not match the count %u specified in the control point function.", + "Output control point count %u does not match the count %u declared in the control point function.", control_point_count, ctx->output_control_point_count); if (!hlsl_types_are_equal(control_point_type, ctx->output_control_point_type)) @@ -6826,22 +6914,32 @@ static void validate_and_record_patch_type(struct hlsl_ctx *ctx, struct hlsl_ir_ if (ctx->input_control_point_count != UINT_MAX) { - VKD3D_ASSERT(ctx->is_patch_constant_func); + VKD3D_ASSERT(profile->type == VKD3D_SHADER_TYPE_GEOMETRY || ctx->is_patch_constant_func); if (control_point_count != ctx->input_control_point_count) + { hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_CONTROL_POINT_COUNT, - "Input control point count %u does not match the count %u specified in the control point function.", + "Input control point count %u does not match the count %u declared previously.", control_point_count, ctx->input_control_point_count); + hlsl_note(ctx, &ctx->input_primitive_param->loc, VKD3D_SHADER_LOG_ERROR, + "The input primitive was previously declared here."); + } - if (!hlsl_types_are_equal(control_point_type, ctx->input_control_point_type)) + if (profile->type != VKD3D_SHADER_TYPE_GEOMETRY + && !hlsl_types_are_equal(control_point_type, ctx->input_control_point_type)) + { hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, - "Input control point type does not match the input type specified in the control point function."); + "Input control point type does not match the input type declared previously."); + hlsl_note(ctx, &ctx->input_primitive_param->loc, VKD3D_SHADER_LOG_ERROR, + "The input primitive was previously declared here."); + } return; } ctx->input_control_point_count = control_point_count; ctx->input_control_point_type = control_point_type; + ctx->input_primitive_param = var; } static void remove_unreachable_code(struct hlsl_ctx *ctx, struct hlsl_block *body) @@ -12418,37 +12516,46 @@ static void process_entry_function(struct hlsl_ctx *ctx, else prepend_uniform_copy(ctx, body, var); } - else if (hlsl_type_is_patch_array(var->data_type)) + else if (hlsl_type_is_primitive_array(var->data_type)) { - if (var->data_type->e.array.array_type == HLSL_ARRAY_PATCH_INPUT) + if (var->storage_modifiers & HLSL_STORAGE_OUT) + hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER, + "Input primitive parameter \"%s\" is declared as \"out\".", var->name); + + if (profile->type != VKD3D_SHADER_TYPE_GEOMETRY) { - if (input_patch) + enum hlsl_array_type array_type = var->data_type->e.array.array_type; + + if (array_type == HLSL_ARRAY_PATCH_INPUT) { - hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_DUPLICATE_PATCH, - "Found multiple InputPatch parameters."); - hlsl_note(ctx, &input_patch->loc, VKD3D_SHADER_LOG_ERROR, - "The InputPatch parameter was previously declared here."); - continue; + if (input_patch) + { + hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_DUPLICATE_PATCH, + "Found multiple InputPatch parameters."); + hlsl_note(ctx, &input_patch->loc, VKD3D_SHADER_LOG_ERROR, + "The InputPatch parameter was previously declared here."); + continue; + } + input_patch = var; } - input_patch = var; - } - else - { - if (output_patch) + else if (array_type == HLSL_ARRAY_PATCH_OUTPUT) { - hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_DUPLICATE_PATCH, - "Found multiple OutputPatch parameters."); - hlsl_note(ctx, &output_patch->loc, VKD3D_SHADER_LOG_ERROR, - "The OutputPatch parameter was previously declared here."); - continue; + if (output_patch) + { + hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_DUPLICATE_PATCH, + "Found multiple OutputPatch parameters."); + hlsl_note(ctx, &output_patch->loc, VKD3D_SHADER_LOG_ERROR, + "The OutputPatch parameter was previously declared here."); + continue; + } + output_patch = var; } - output_patch = var; } - validate_and_record_patch_type(ctx, var); + validate_and_record_prim_type(ctx, var); if (profile->type == VKD3D_SHADER_TYPE_GEOMETRY) { - hlsl_fixme(ctx, &var->loc, "InputPatch/OutputPatch parameters in geometry shaders."); + hlsl_fixme(ctx, &var->loc, "Input primitive parameters in geometry shaders."); continue; } @@ -12465,7 +12572,16 @@ static void process_entry_function(struct hlsl_ctx *ctx, } if (var->storage_modifiers & HLSL_STORAGE_IN) + { + if (profile->type == VKD3D_SHADER_TYPE_GEOMETRY) + { + hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_MISSING_PRIMITIVE_TYPE, + "Input parameter \"%s\" is missing a primitive type.", var->name); + continue; + } + prepend_input_var_copy(ctx, entry_func, var); + } if (var->storage_modifiers & HLSL_STORAGE_OUT) { if (profile->type == VKD3D_SHADER_TYPE_HULL && !ctx->is_patch_constant_func) @@ -12493,6 +12609,10 @@ static void process_entry_function(struct hlsl_ctx *ctx, hlsl_fixme(ctx, &entry_func->loc, "Passthrough hull shader control point function."); } + if (profile->type == VKD3D_SHADER_TYPE_GEOMETRY && ctx->input_primitive_type == VKD3D_PT_UNDEFINED) + hlsl_error(ctx, &entry_func->loc, VKD3D_SHADER_ERROR_HLSL_MISSING_PRIMITIVE_TYPE, + "Entry point \"%s\" is missing an input primitive parameter.", entry_func->func->name); + if (hlsl_version_ge(ctx, 4, 0)) { hlsl_transform_ir(ctx, lower_discard_neg, body, NULL); diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 9d9a02c1..1338b80d 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -170,6 +170,7 @@ enum vkd3d_shader_error VKD3D_SHADER_ERROR_HLSL_AMBIGUOUS_CALL = 5040, VKD3D_SHADER_ERROR_HLSL_DUPLICATE_PATCH = 5041, VKD3D_SHADER_ERROR_HLSL_INVALID_MAX_VERTEX_COUNT = 5042, + VKD3D_SHADER_ERROR_HLSL_MISSING_PRIMITIVE_TYPE = 5043, VKD3D_SHADER_WARNING_HLSL_IMPLICIT_TRUNCATION = 5300, VKD3D_SHADER_WARNING_HLSL_DIVISION_BY_ZERO = 5301,