diff --git a/include/vkd3d_shader.h b/include/vkd3d_shader.h index cb561d7f..f71f5aec 100644 --- a/include/vkd3d_shader.h +++ b/include/vkd3d_shader.h @@ -476,6 +476,45 @@ enum vkd3d_shader_binding_flag VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_BINDING_FLAG), }; +/** + * The factor used to interpolate the fragment output colour with fog. + * + * See VKD3D_SHADER_PARAMETER_NAME_FOG_FRAGMENT_MODE for specification of the + * interpolation factor as defined here. + * + * The following variables may be used to determine the interpolation factor: + * + * c = The fog coordinate value output from the vertex shader. This is an + * inter-stage varying with the semantic name "FOG" and semantic index 0. + * E = The value of VKD3D_SHADER_PARAMETER_NAME_FOG_END. + * k = The value of VKD3D_SHADER_PARAMETER_NAME_FOG_SCALE. + * + * \since 1.15 + */ +enum vkd3d_shader_fog_fragment_mode +{ + /** + * No fog interpolation is applied; + * the output colour is passed through unmodified. + * Equivalently, the fog interpolation factor is 1. + */ + VKD3D_SHADER_FOG_FRAGMENT_NONE = 0x0, + /** + * The fog interpolation factor is (E - c) * k. + * + * In order to implement traditional linear fog, as present in Direct3D and + * OpenGL, i.e. + * + * (end - c) / (end - start) + * + * set + * + * E = end + * k = 1 / (end - start) + */ + VKD3D_SHADER_FOG_FRAGMENT_LINEAR = 0x3, +}; + /** * The manner in which a parameter value is provided to the shader, used in * struct vkd3d_shader_parameter and struct vkd3d_shader_parameter1. @@ -739,6 +778,73 @@ enum vkd3d_shader_parameter_name * \since 1.14 */ VKD3D_SHADER_PARAMETER_NAME_POINT_SPRITE, + /** + * Fog mode used in fragment shaders. + * + * The value specified by this parameter must be a member of + * enum vkd3d_shader_fog_fragment_mode. + * + * If not VKD3D_SHADER_FOG_FRAGMENT_NONE, the pixel shader colour output at + * location 0 is linearly interpolated with the fog colour defined by + * VKD3D_SHADER_PARAMETER_NAME_FOG_COLOUR. The interpolation factor is + * defined according to the enumerant selected by this parameter. + * The interpolated value is then outputted instead of the original value at + * location 0. + * + * An interpolation factor of 0 specifies to use the fog colour; a factor of + * 1 specifies to use the original colour output. The interpolation factor + * is clamped to the [0, 1] range before interpolating. + * + * The default value is VKD3D_SHADER_FOG_FRAGMENT_NONE. + * + * The data type for this parameter must be + * VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32. + * + * Only VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT is supported in this + * version of vkd3d-shader. + * + * \since 1.15 + */ + VKD3D_SHADER_PARAMETER_NAME_FOG_FRAGMENT_MODE, + /** + * Fog colour. + * See VKD3D_SHADER_PARAMETER_NAME_FOG_FRAGMENT_MODE for documentation of + * fog. + * + * The data type for this parameter must be + * VKD3D_SHADER_PARAMETER_DATA_TYPE_FLOAT32_VEC4. + * + * The default value is transparent black, i.e. the vector {0, 0, 0, 0}. + * + * \since 1.15 + */ + VKD3D_SHADER_PARAMETER_NAME_FOG_COLOUR, + /** + * End coordinate for linear fog. + * See VKD3D_SHADER_PARAMETER_NAME_FOG_FRAGMENT_MODE for documentation of + * fog. + * + * The data type for this parameter must be + * VKD3D_SHADER_PARAMETER_DATA_TYPE_FLOAT32. + * + * The default value is 1.0. + * + * \since 1.15 + */ + VKD3D_SHADER_PARAMETER_NAME_FOG_END, + /** + * Scale value for linear fog. + * See VKD3D_SHADER_PARAMETER_NAME_FOG_FRAGMENT_MODE for documentation of + * fog. + * + * The data type for this parameter must be + * VKD3D_SHADER_PARAMETER_DATA_TYPE_FLOAT32. + * + * The default value is 1.0. + * + * \since 1.15 + */ + VKD3D_SHADER_PARAMETER_NAME_FOG_SCALE, VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_PARAMETER_NAME), }; diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index 0c06db9f..871d558f 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -1,5 +1,6 @@ /* * Copyright 2023 Conor McCarthy for CodeWeavers + * Copyright 2023-2024 Elizabeth Figura for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -222,6 +223,14 @@ static void src_param_init_parameter(struct vkd3d_shader_src_param *src, uint32_ src->reg.idx[0].offset = idx; } +static void src_param_init_parameter_vec4(struct vkd3d_shader_src_param *src, uint32_t idx, enum vkd3d_data_type type) +{ + vsir_src_param_init(src, VKD3DSPR_PARAMETER, type, 1); + src->reg.idx[0].offset = idx; + src->reg.dimension = VSIR_DIMENSION_VEC4; + src->swizzle = VKD3D_SHADER_NO_SWIZZLE; +} + static void vsir_src_param_init_resource(struct vkd3d_shader_src_param *src, unsigned int id, unsigned int idx) { vsir_src_param_init(src, VKD3DSPR_RESOURCE, VKD3D_DATA_UNUSED, 2); @@ -251,6 +260,14 @@ static void src_param_init_ssa_float(struct vkd3d_shader_src_param *src, unsigne src->reg.idx[0].offset = idx; } +static void src_param_init_ssa_float4(struct vkd3d_shader_src_param *src, unsigned int idx) +{ + vsir_src_param_init(src, VKD3DSPR_SSA, VKD3D_DATA_FLOAT, 1); + src->reg.idx[0].offset = idx; + src->reg.dimension = VSIR_DIMENSION_VEC4; + src->swizzle = VKD3D_SHADER_NO_SWIZZLE; +} + static void src_param_init_temp_bool(struct vkd3d_shader_src_param *src, unsigned int idx) { vsir_src_param_init(src, VKD3DSPR_TEMP, VKD3D_DATA_BOOL, 1); @@ -306,6 +323,14 @@ static void dst_param_init_ssa_float(struct vkd3d_shader_dst_param *dst, unsigne dst->reg.idx[0].offset = idx; } +static void dst_param_init_ssa_float4(struct vkd3d_shader_dst_param *dst, unsigned int idx) +{ + vsir_dst_param_init(dst, VKD3DSPR_SSA, VKD3D_DATA_FLOAT, 1); + dst->reg.idx[0].offset = idx; + dst->reg.dimension = VSIR_DIMENSION_VEC4; + dst->write_mask = VKD3DSP_WRITEMASK_ALL; +} + static void dst_param_init_temp_bool(struct vkd3d_shader_dst_param *dst, unsigned int idx) { vsir_dst_param_init(dst, VKD3DSPR_TEMP, VKD3D_DATA_BOOL, 1); @@ -864,11 +889,36 @@ static enum vkd3d_result vsir_program_ensure_ret(struct vsir_program *program, return VKD3D_OK; } +static bool add_signature_element(struct shader_signature *signature, const char *semantic_name, + uint32_t semantic_index, uint32_t mask, uint32_t register_index, + enum vkd3d_shader_interpolation_mode interpolation_mode) +{ + struct signature_element *new_elements, *e; + + if (!(new_elements = vkd3d_realloc(signature->elements, + (signature->element_count + 1) * sizeof(*signature->elements)))) + return false; + signature->elements = new_elements; + e = &signature->elements[signature->element_count++]; + memset(e, 0, sizeof(*e)); + e->semantic_name = vkd3d_strdup(semantic_name); + e->semantic_index = semantic_index; + e->sysval_semantic = VKD3D_SHADER_SV_NONE; + e->component_type = VKD3D_SHADER_COMPONENT_FLOAT; + e->register_count = 1; + e->mask = mask; + e->used_mask = mask; + e->register_index = register_index; + e->target_location = register_index; + e->interpolation_mode = interpolation_mode; + return true; +} + static enum vkd3d_result vsir_program_add_diffuse_output(struct vsir_program *program, struct vsir_transformation_context *ctx) { struct shader_signature *signature = &program->output_signature; - struct signature_element *new_elements, *e; + struct signature_element *e; if (program->shader_version.type != VKD3D_SHADER_TYPE_VERTEX) return VKD3D_OK; @@ -881,22 +931,8 @@ static enum vkd3d_result vsir_program_add_diffuse_output(struct vsir_program *pr return VKD3D_OK; } - if (!(new_elements = vkd3d_realloc(signature->elements, - (signature->element_count + 1) * sizeof(*signature->elements)))) + if (!add_signature_element(signature, "COLOR", 0, VKD3DSP_WRITEMASK_ALL, SM1_COLOR_REGISTER_OFFSET, VKD3DSIM_NONE)) return VKD3D_ERROR_OUT_OF_MEMORY; - signature->elements = new_elements; - e = &signature->elements[signature->element_count++]; - memset(e, 0, sizeof(*e)); - e->semantic_name = vkd3d_strdup("COLOR"); - e->sysval_semantic = VKD3D_SHADER_SV_NONE; - e->component_type = VKD3D_SHADER_COMPONENT_FLOAT; - e->register_count = 1; - e->mask = VKD3DSP_WRITEMASK_ALL; - e->used_mask = VKD3DSP_WRITEMASK_ALL; - e->register_index = SM1_COLOR_REGISTER_OFFSET; - e->target_location = SM1_COLOR_REGISTER_OFFSET; - e->interpolation_mode = VKD3DSIM_NONE; - return VKD3D_OK; } @@ -6685,6 +6721,187 @@ static enum vkd3d_result vsir_program_insert_point_coord(struct vsir_program *pr return VKD3D_OK; } +static enum vkd3d_result vsir_program_add_fog_input(struct vsir_program *program, + struct vsir_transformation_context *ctx) +{ + struct shader_signature *signature = &program->input_signature; + uint32_t register_idx = 0; + + if (program->shader_version.type != VKD3D_SHADER_TYPE_PIXEL) + return VKD3D_OK; + + if (!vsir_program_get_parameter(program, VKD3D_SHADER_PARAMETER_NAME_FOG_FRAGMENT_MODE)) + return VKD3D_OK; + + /* We could check the value and skip this if NONE, but chances are if a + * user specifies the fog fragment mode as a parameter, they'll want to + * enable it dynamically. Always specifying it (and hence always outputting + * it from the VS) avoids an extra VS variant. */ + + if (vsir_signature_find_element_by_name(signature, "FOG", 0)) + return VKD3D_OK; + + for (unsigned int i = 0; i < signature->element_count; ++i) + register_idx = max(register_idx, signature->elements[i].register_index + 1); + + if (!add_signature_element(signature, "FOG", 0, VKD3DSP_WRITEMASK_0, register_idx, VKD3DSIM_LINEAR)) + return VKD3D_ERROR_OUT_OF_MEMORY; + return VKD3D_OK; +} + +static enum vkd3d_result insert_fragment_fog_before_ret(struct vsir_program *program, + const struct vkd3d_shader_instruction *ret, enum vkd3d_shader_fog_fragment_mode mode, + uint32_t fog_signature_idx, uint32_t colour_signature_idx, uint32_t colour_temp, + size_t *ret_pos, struct vkd3d_shader_message_context *message_context) +{ + struct vkd3d_shader_instruction_array *instructions = &program->instructions; + struct vkd3d_shader_location loc = ret->location; + uint32_t ssa_factor = program->ssa_count++; + size_t pos = ret - instructions->elements; + struct vkd3d_shader_instruction *ins; + uint32_t ssa_temp; + + switch (mode) + { + case VKD3D_SHADER_FOG_FRAGMENT_LINEAR: + /* We generate the following code: + * + * add sr0, FOG_END, -vFOG.x + * mul_sat srFACTOR, sr0, FOG_SCALE + */ + if (!shader_instruction_array_insert_at(&program->instructions, pos, 4)) + return VKD3D_ERROR_OUT_OF_MEMORY; + *ret_pos = pos + 4; + + ssa_temp = program->ssa_count++; + + ins = &program->instructions.elements[pos]; + + vsir_instruction_init_with_params(program, ins, &loc, VKD3DSIH_ADD, 1, 2); + dst_param_init_ssa_float(&ins->dst[0], ssa_temp); + src_param_init_parameter(&ins->src[0], VKD3D_SHADER_PARAMETER_NAME_FOG_END, VKD3D_DATA_FLOAT); + vsir_src_param_init(&ins->src[1], VKD3DSPR_INPUT, VKD3D_DATA_FLOAT, 1); + ins->src[1].reg.idx[0].offset = fog_signature_idx; + ins->src[1].reg.dimension = VSIR_DIMENSION_VEC4; + ins->src[1].swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X); + ins->src[1].modifiers = VKD3DSPSM_NEG; + + vsir_instruction_init_with_params(program, ++ins, &loc, VKD3DSIH_MUL, 1, 2); + dst_param_init_ssa_float(&ins->dst[0], ssa_factor); + ins->dst[0].modifiers = VKD3DSPDM_SATURATE; + src_param_init_ssa_float(&ins->src[0], ssa_temp); + src_param_init_parameter(&ins->src[1], VKD3D_SHADER_PARAMETER_NAME_FOG_SCALE, VKD3D_DATA_FLOAT); + break; + + default: + vkd3d_unreachable(); + } + + /* We generate the following code: + * + * add sr0, FRAG_COLOUR, -FOG_COLOUR + * mad oC0, sr0, srFACTOR, FOG_COLOUR + */ + + vsir_instruction_init_with_params(program, ++ins, &loc, VKD3DSIH_ADD, 1, 2); + dst_param_init_ssa_float4(&ins->dst[0], program->ssa_count++); + src_param_init_temp_float4(&ins->src[0], colour_temp); + src_param_init_parameter_vec4(&ins->src[1], VKD3D_SHADER_PARAMETER_NAME_FOG_COLOUR, VKD3D_DATA_FLOAT); + ins->src[1].modifiers = VKD3DSPSM_NEG; + + vsir_instruction_init_with_params(program, ++ins, &loc, VKD3DSIH_MAD, 1, 3); + dst_param_init_output(&ins->dst[0], VKD3D_DATA_FLOAT, colour_signature_idx, + program->output_signature.elements[colour_signature_idx].mask); + src_param_init_ssa_float4(&ins->src[0], program->ssa_count - 1); + src_param_init_ssa_float(&ins->src[1], ssa_factor); + src_param_init_parameter_vec4(&ins->src[2], VKD3D_SHADER_PARAMETER_NAME_FOG_COLOUR, VKD3D_DATA_FLOAT); + + return VKD3D_OK; +} + +static enum vkd3d_result vsir_program_insert_fragment_fog(struct vsir_program *program, + struct vsir_transformation_context *ctx) +{ + struct vkd3d_shader_message_context *message_context = ctx->message_context; + uint32_t colour_signature_idx, fog_signature_idx, colour_temp; + const struct vkd3d_shader_parameter1 *mode_parameter = NULL; + static const struct vkd3d_shader_location no_loc; + const struct signature_element *fog_element; + enum vkd3d_shader_fog_fragment_mode mode; + struct vkd3d_shader_instruction *ins; + size_t new_pos; + int ret; + + if (program->shader_version.type != VKD3D_SHADER_TYPE_PIXEL) + return VKD3D_OK; + + if (!vsir_signature_find_sysval(&program->output_signature, VKD3D_SHADER_SV_TARGET, 0, &colour_signature_idx)) + return VKD3D_OK; + + if (!(mode_parameter = vsir_program_get_parameter(program, VKD3D_SHADER_PARAMETER_NAME_FOG_FRAGMENT_MODE))) + return VKD3D_OK; + + if (mode_parameter->type != VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT) + { + vkd3d_shader_error(message_context, &no_loc, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED, + "Unsupported fog fragment mode parameter type %#x.", mode_parameter->type); + return VKD3D_ERROR_NOT_IMPLEMENTED; + } + if (mode_parameter->data_type != VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32) + { + vkd3d_shader_error(message_context, &no_loc, VKD3D_SHADER_ERROR_VSIR_INVALID_DATA_TYPE, + "Invalid fog fragment mode parameter data type %#x.", mode_parameter->data_type); + return VKD3D_ERROR_INVALID_ARGUMENT; + } + mode = mode_parameter->u.immediate_constant.u.u32; + + if (mode == VKD3D_SHADER_FOG_FRAGMENT_NONE) + return VKD3D_OK; + + /* Should have been added by vsir_program_add_fog_input(). */ + if (!(fog_element = vsir_signature_find_element_by_name(&program->input_signature, "FOG", 0))) + { + ERR("Fog input not found.\n"); + return VKD3D_ERROR_INVALID_SHADER; + } + fog_signature_idx = fog_element - program->input_signature.elements; + + /* We're going to be reading from the output, so we need to go + * through the whole shader and convert it to a temp. */ + colour_temp = program->temp_count++; + + for (size_t i = 0; i < program->instructions.count; ++i) + { + ins = &program->instructions.elements[i]; + + if (vsir_instruction_is_dcl(ins)) + continue; + + if (ins->opcode == VKD3DSIH_RET) + { + if ((ret = insert_fragment_fog_before_ret(program, ins, mode, fog_signature_idx, + colour_signature_idx, colour_temp, &new_pos, message_context)) < 0) + return ret; + i = new_pos; + continue; + } + + for (size_t j = 0; j < ins->dst_count; ++j) + { + struct vkd3d_shader_dst_param *dst = &ins->dst[j]; + + /* Note we run after I/O normalization. */ + if (dst->reg.type == VKD3DSPR_OUTPUT && dst->reg.idx[0].offset == colour_signature_idx) + { + dst->reg.type = VKD3DSPR_TEMP; + dst->reg.idx[0].offset = colour_temp; + } + } + } + + return VKD3D_OK; +} + struct validation_context { struct vkd3d_shader_message_context *message_context; @@ -8769,6 +8986,9 @@ enum vkd3d_result vsir_program_transform_early(struct vsir_program *program, uin if (program->shader_version.major <= 2) vsir_transform(&ctx, vsir_program_add_diffuse_output); + /* For vsir_program_insert_fragment_fog(). */ + vsir_transform(&ctx, vsir_program_add_fog_input); + return ctx.result; } @@ -8823,6 +9043,7 @@ enum vkd3d_result vsir_program_transform(struct vsir_program *program, uint64_t vsir_transform(&ctx, vsir_program_insert_point_size); vsir_transform(&ctx, vsir_program_insert_point_size_clamp); vsir_transform(&ctx, vsir_program_insert_point_coord); + vsir_transform(&ctx, vsir_program_insert_fragment_fog); if (TRACE_ON()) vsir_program_trace(program); diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index f75f080a..a93f1532 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -3316,13 +3316,19 @@ static uint32_t spirv_compiler_emit_variable(struct spirv_compiler *compiler, static const struct vkd3d_spec_constant_info { enum vkd3d_shader_parameter_name name; - uint32_t default_value; + union + { + uint32_t u; + float f; + } default_value; const char *debug_name; } vkd3d_shader_parameters[] = { - {VKD3D_SHADER_PARAMETER_NAME_RASTERIZER_SAMPLE_COUNT, 1, "sample_count"}, - {VKD3D_SHADER_PARAMETER_NAME_ALPHA_TEST_REF, 0, "alpha_test_ref"}, + {VKD3D_SHADER_PARAMETER_NAME_RASTERIZER_SAMPLE_COUNT, {.u = 1}, "sample_count"}, + {VKD3D_SHADER_PARAMETER_NAME_ALPHA_TEST_REF, {.f = 0.0f}, "alpha_test_ref"}, + {VKD3D_SHADER_PARAMETER_NAME_FOG_END, {.f = 1.0f}, "fog_end"}, + {VKD3D_SHADER_PARAMETER_NAME_FOG_SCALE, {.f = 1.0f}, "fog_scale"}, }; static const struct vkd3d_spec_constant_info *get_spec_constant_info(enum vkd3d_shader_parameter_name name) @@ -3383,7 +3389,7 @@ static uint32_t spirv_compiler_emit_spec_constant(struct spirv_compiler *compile const struct vkd3d_spec_constant_info *info; info = get_spec_constant_info(name); - default_value = info ? info->default_value : 0; + default_value = info ? info->default_value.u : 0; scalar_type_id = vkd3d_spirv_get_type_id(builder, vkd3d_component_type_from_data_type(type), 1); vector_type_id = vkd3d_spirv_get_type_id(builder, vkd3d_component_type_from_data_type(type), component_count); diff --git a/tests/hlsl/fog.shader_test b/tests/hlsl/fog.shader_test index 81462c4c..b9455dcb 100644 --- a/tests/hlsl/fog.shader_test +++ b/tests/hlsl/fog.shader_test @@ -27,3 +27,46 @@ float4 main(float2 fog : fog) : sv_target [test] todo(msl) draw quad probe (0, 0) rgba (0.1, 0.2, 0, 1) + + +[require] +fog + +[input layout] +0 r32g32b32a32-float position + +[vb 0] +-1.0 -1.0 0.2 1.0 +-1.0 1.0 0.2 1.0 + 1.0 -1.0 0.6 1.0 + 1.0 1.0 0.6 1.0 + +[vertex shader] +float4 main(float4 pos : position, out float fog : fog) : sv_position +{ + fog = 0.2; + return pos; +} + +[pixel shader] +float4 main() : sv_target +{ + return float4(0, 0, 1, 1); +} + +[test] +fog-colour 0.0 1.0 1.0 1.0 + +fog disable +draw triangle strip 4 +probe (160, 120) rgba (0.0, 0.0, 1.0, 1.0) +probe (480, 360) rgba (0.0, 0.0, 1.0, 1.0) +probe (160, 120) rgba (0.0, 0.0, 1.0, 1.0) +probe (480, 360) rgba (0.0, 0.0, 1.0, 1.0) + +fog none +draw triangle strip 4 +probe (160, 120) rgba (0.0, 0.8, 1.0, 1.0) +probe (480, 360) rgba (0.0, 0.8, 1.0, 1.0) +probe (160, 120) rgba (0.0, 0.8, 1.0, 1.0) +probe (480, 360) rgba (0.0, 0.8, 1.0, 1.0) diff --git a/tests/shader_runner.c b/tests/shader_runner.c index 4b178298..748d89be 100644 --- a/tests/shader_runner.c +++ b/tests/shader_runner.c @@ -335,6 +335,7 @@ static const char *const shader_cap_strings[] = [SHADER_CAP_CLIP_PLANES] = "clip-planes", [SHADER_CAP_DEPTH_BOUNDS] = "depth-bounds", [SHADER_CAP_FLOAT64] = "float64", + [SHADER_CAP_FOG] = "fog", [SHADER_CAP_GEOMETRY_SHADER] = "geometry-shader", [SHADER_CAP_INT64] = "int64", [SHADER_CAP_POINT_SIZE] = "point-size", @@ -1424,6 +1425,28 @@ static void parse_test_directive(struct shader_runner *runner, const char *line) else runner->point_sprite = false; } + else if (match_string(line, "fog", &line)) + { + if (match_string(line, "disable", &line)) + runner->fog_mode = FOG_MODE_DISABLE; + else if (match_string(line, "none", &line)) + runner->fog_mode = FOG_MODE_NONE; + else if (match_string(line, "linear", &line)) + runner->fog_mode = FOG_MODE_LINEAR; + else if (match_string(line, "exp", &line)) + runner->fog_mode = FOG_MODE_EXP; + else if (match_string(line, "exp2", &line)) + runner->fog_mode = FOG_MODE_EXP2; + else + fatal_error("Invalid fog mode '%s'.\n", line); + } + else if (match_string(line, "fog-colour", &line)) + { + struct vec4 *v = &runner->fog_colour; + + if (sscanf(line, "%f %f %f %f", &v->x, &v->y, &v->z, &v->w) < 4) + fatal_error("Malformed float4 constant '%s'.\n", line); + } else { fatal_error("Unknown test directive '%s'.\n", line); @@ -1872,6 +1895,7 @@ void run_shader_tests(struct shader_runner *runner, const struct shader_runner_c runner->point_size = 1.0f; runner->point_size_min = 1.0f; runner->point_size_max = FLT_MAX; + runner->fog_mode = FOG_MODE_DISABLE; runner->sample_mask = ~0u; runner->depth_bounds = false; diff --git a/tests/shader_runner.h b/tests/shader_runner.h index 88809cc5..3a7267f3 100644 --- a/tests/shader_runner.h +++ b/tests/shader_runner.h @@ -141,6 +141,7 @@ enum shader_cap SHADER_CAP_CLIP_PLANES, SHADER_CAP_DEPTH_BOUNDS, SHADER_CAP_FLOAT64, + SHADER_CAP_FOG, SHADER_CAP_GEOMETRY_SHADER, SHADER_CAP_INT64, SHADER_CAP_POINT_SIZE, @@ -173,6 +174,15 @@ static inline unsigned int shader_runner_caps_get_feature_flags(const struct sha return flags; } +enum fog_mode +{ + FOG_MODE_NONE = 0, + FOG_MODE_EXP = 1, + FOG_MODE_EXP2 = 2, + FOG_MODE_LINEAR = 3, + FOG_MODE_DISABLE, +}; + struct shader_runner { const struct shader_runner_ops *ops; @@ -222,6 +232,8 @@ struct shader_runner struct vec4 clip_planes[8]; float point_size, point_size_min, point_size_max; bool point_sprite; + struct vec4 fog_colour; + enum fog_mode fog_mode; }; struct shader_runner_ops diff --git a/tests/shader_runner_d3d9.c b/tests/shader_runner_d3d9.c index e4315959..a7d63fb6 100644 --- a/tests/shader_runner_d3d9.c +++ b/tests/shader_runner_d3d9.c @@ -1,5 +1,5 @@ /* - * Copyright 2021 Zebediah Figura for CodeWeavers + * Copyright 2021-2024 Elizabeth Figura for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -145,6 +145,7 @@ static bool init_test_context(struct d3d9_shader_runner *runner) runner->caps.minimum_shader_model = SHADER_MODEL_2_0; runner->caps.maximum_shader_model = SHADER_MODEL_3_0; runner->caps.shader_caps[SHADER_CAP_CLIP_PLANES] = true; + runner->caps.shader_caps[SHADER_CAP_FOG] = true; runner->caps.shader_caps[SHADER_CAP_POINT_SIZE] = true; return true; @@ -325,16 +326,19 @@ static bool d3d9_runner_dispatch(struct shader_runner *r, unsigned int x, unsign fatal_error("Compute shaders are not supported.\n"); } +static uint32_t d3d_color_from_vec4(const struct vec4 *v) +{ + return vkd3d_make_u32(vkd3d_make_u16(v->z * 255.0f, v->y * 255.0f), + vkd3d_make_u16(v->x * 255.0f, v->w * 255.0f)); +} + static void d3d9_runner_clear(struct shader_runner *r, struct resource *resource, const struct vec4 *clear_value) { struct d3d9_shader_runner *runner = d3d9_shader_runner(r); - unsigned int colour; HRESULT hr; - colour = vkd3d_make_u32(vkd3d_make_u16(clear_value->z * 255.0, clear_value->y * 255.0), - vkd3d_make_u16(clear_value->x * 255.0, clear_value->w * 255.0)); - - hr = IDirect3DDevice9_ColorFill(runner->device, d3d9_resource(resource)->surface, NULL, colour); + hr = IDirect3DDevice9_ColorFill(runner->device, d3d9_resource(resource)->surface, + NULL, d3d_color_from_vec4(clear_value)); ok(hr == S_OK, "Got hr %#lx.\n", hr); } @@ -498,6 +502,16 @@ static bool d3d9_runner_draw(struct shader_runner *r, hr = IDirect3DDevice9_SetRenderState(device, D3DRS_POINTSPRITEENABLE, runner->r.point_sprite); ok(hr == D3D_OK, "Failed to set render state, hr %#lx.\n", hr); + hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGENABLE, (runner->r.fog_mode != FOG_MODE_DISABLE)); + ok(hr == D3D_OK, "Failed to set render state, hr %#lx.\n", hr); + if (runner->r.fog_mode != FOG_MODE_DISABLE) + { + hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGTABLEMODE, runner->r.fog_mode); + ok(hr == D3D_OK, "Failed to set render state, hr %#lx.\n", hr); + } + hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGCOLOR, d3d_color_from_vec4(&runner->r.fog_colour)); + ok(hr == D3D_OK, "Failed to set render state, hr %#lx.\n", hr); + hr = IDirect3DDevice9_CreateVertexDeclaration(device, decl_elements, &vertex_declaration); ok(hr == D3D_OK, "Failed to create vertex declaration, hr %#lx.\n", hr); hr = IDirect3DDevice9_CreateVertexShader(device, ID3D10Blob_GetBufferPointer(vs_code), &vs); diff --git a/tests/shader_runner_vulkan.c b/tests/shader_runner_vulkan.c index b479eb01..60312d09 100644 --- a/tests/shader_runner_vulkan.c +++ b/tests/shader_runner_vulkan.c @@ -1,7 +1,7 @@ /* * Shader runner which uses libvkd3d-shader to compile HLSL -> D3D bytecode -> SPIR-V * - * Copyright 2020-2022 Zebediah Figura for CodeWeavers + * Copyright 2020-2024 Elizabeth Figura for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -267,15 +267,27 @@ static void vulkan_runner_destroy_resource(struct shader_runner *r, struct resou free(resource); } +static enum vkd3d_shader_fog_fragment_mode get_fog_fragment_mode(enum fog_mode mode) +{ + switch (mode) + { + case FOG_MODE_DISABLE: return VKD3D_SHADER_FOG_FRAGMENT_NONE; + case FOG_MODE_NONE: return VKD3D_SHADER_FOG_FRAGMENT_LINEAR; + default: fatal_error("Unhandled fog mode %#x.\n", mode); + } +} + static bool compile_hlsl_and_scan(struct vulkan_shader_runner *runner, enum shader_type type) { + struct vkd3d_shader_parameter_info parameter_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_PARAMETER_INFO}; struct vkd3d_shader_compile_info info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_COMPILE_INFO}; + struct vkd3d_shader_parameter1 parameters[1]; enum vkd3d_result ret; if (!(runner->d3d_blobs[type] = compile_hlsl(&runner->r, type))) return false; - info.next = &runner->signatures[type]; + info.next = ¶meter_info; info.source.code = ID3D10Blob_GetBufferPointer(runner->d3d_blobs[type]); info.source.size = ID3D10Blob_GetBufferSize(runner->d3d_blobs[type]); if (runner->r.minimum_shader_model < SHADER_MODEL_4_0) @@ -284,6 +296,15 @@ static bool compile_hlsl_and_scan(struct vulkan_shader_runner *runner, enum shad info.source_type = VKD3D_SHADER_SOURCE_DXBC_TPF; info.target_type = VKD3D_SHADER_TARGET_SPIRV_BINARY; + parameter_info.next = &runner->signatures[type]; + parameter_info.parameter_count = ARRAY_SIZE(parameters); + parameter_info.parameters = parameters; + + parameters[0].name = VKD3D_SHADER_PARAMETER_NAME_FOG_FRAGMENT_MODE; + parameters[0].type = VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT; + parameters[0].data_type = VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32; + parameters[0].u.immediate_constant.u.u32 = get_fog_fragment_mode(runner->r.fog_mode); + runner->signatures[type].type = VKD3D_SHADER_STRUCTURE_TYPE_SCAN_SIGNATURE_INFO; runner->signatures[type].next = NULL; ret = vkd3d_shader_scan(&info, NULL); @@ -306,7 +327,7 @@ static bool compile_d3d_code(struct vulkan_shader_runner *runner, struct vkd3d_shader_varying_map varying_map[12]; struct vkd3d_shader_resource_binding *binding; struct vkd3d_shader_compile_option options[2]; - struct vkd3d_shader_parameter1 parameters[17]; + struct vkd3d_shader_parameter1 parameters[21]; unsigned int i; char *messages; int ret; @@ -460,6 +481,30 @@ static bool compile_d3d_code(struct vulkan_shader_runner *runner, parameters[16].data_type = VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32; parameters[16].u.immediate_constant.u.u32 = runner->r.point_sprite; + parameters[17].name = VKD3D_SHADER_PARAMETER_NAME_FOG_COLOUR; + parameters[17].type = VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT; + parameters[17].data_type = VKD3D_SHADER_PARAMETER_DATA_TYPE_FLOAT32_VEC4; + memcpy(parameters[17].u.immediate_constant.u.f32_vec4, &runner->r.fog_colour, sizeof(struct vec4)); + + parameters[18].name = VKD3D_SHADER_PARAMETER_NAME_FOG_FRAGMENT_MODE; + parameters[18].type = VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT; + parameters[18].data_type = VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32; + parameters[18].u.immediate_constant.u.u32 = get_fog_fragment_mode(runner->r.fog_mode); + + parameters[19].name = VKD3D_SHADER_PARAMETER_NAME_FOG_END; + parameters[19].type = VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT; + parameters[19].data_type = VKD3D_SHADER_PARAMETER_DATA_TYPE_FLOAT32; + + parameters[20].name = VKD3D_SHADER_PARAMETER_NAME_FOG_SCALE; + parameters[20].type = VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT; + parameters[20].data_type = VKD3D_SHADER_PARAMETER_DATA_TYPE_FLOAT32; + + if (runner->r.fog_mode == FOG_MODE_NONE) + { + parameters[19].u.immediate_constant.u.f32 = 0.0f; + parameters[20].u.immediate_constant.u.f32 = -1.0f; + } + parameter_info.parameter_count = ARRAY_SIZE(parameters); parameter_info.parameters = parameters; @@ -1656,6 +1701,7 @@ static bool init_vulkan_runner(struct vulkan_shader_runner *runner) } runner->caps.shader_caps[SHADER_CAP_CLIP_PLANES] = true; + runner->caps.shader_caps[SHADER_CAP_FOG] = true; runner->caps.shader_caps[SHADER_CAP_POINT_SIZE] = true; device_desc.pEnabledFeatures = &features; @@ -1758,6 +1804,9 @@ void run_shader_tests_vulkan(void) runner.caps.maximum_shader_model = SHADER_MODEL_3_0; run_shader_tests(&runner.r, &runner.caps, &vulkan_runner_ops, NULL); + /* Fog requires remapping, which is only correct for sm1. */ + runner.caps.shader_caps[SHADER_CAP_FOG] = false; + runner.caps.minimum_shader_model = SHADER_MODEL_4_0; runner.caps.maximum_shader_model = SHADER_MODEL_5_1; run_shader_tests(&runner.r, &runner.caps, &vulkan_runner_ops, NULL);