vkd3d-shader/ir: Allow controlling fog through parameters.

Fog requires several parameters to even implement the most basic of
functionality correctly, so this commit is relatively large.
This commit is contained in:
Elizabeth Figura
2024-10-05 10:26:20 -05:00
committed by Henri Verbeet
parent a1de406de4
commit f86d1e72a4
Notes: Henri Verbeet 2024-12-02 17:19:05 +01:00
Approved-by: Henri Verbeet (@hverbeet)
Merge-Request: https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/1265
8 changed files with 504 additions and 29 deletions

View File

@@ -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);

View File

@@ -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);