blob: 3aa4ca578fa06cacd3cdd9a34b3e63aa8521be5e [file] [log] [blame] [edit]
/*
* Copyright © 2023 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "nir_builder.h"
#include "nir_builtin_builder.h"
#include "st_nir.h"
static nir_def *
fog_result(nir_builder *b, nir_def *color, enum gl_fog_mode fog_mode, struct gl_program_parameter_list *paramList)
{
nir_shader *s = b->shader;
nir_variable *fogc_var =
nir_create_variable_with_location(s, nir_var_shader_in, VARYING_SLOT_FOGC, glsl_float_type());
nir_def *fogc = nir_load_var(b, fogc_var);
s->info.inputs_read |= VARYING_BIT_FOGC;
static const gl_state_index16 fog_params_tokens[STATE_LENGTH] = {STATE_FOG_PARAMS_OPTIMIZED};
static const gl_state_index16 fog_color_tokens[STATE_LENGTH] = {STATE_FOG_COLOR};
nir_variable *fog_params_var = st_nir_state_variable_create(s, glsl_vec4_type(), fog_params_tokens);
fog_params_var->data.driver_location = _mesa_add_state_reference(paramList, fog_params_tokens);
nir_def *params = nir_load_var(b, fog_params_var);
nir_variable *fog_color_var = st_nir_state_variable_create(s, glsl_vec4_type(), fog_color_tokens);
fog_color_var->data.driver_location = _mesa_add_state_reference(paramList, fog_color_tokens);
nir_def *fog_color = nir_load_var(b, fog_color_var);
/* compute the 1 component fog factor f */
nir_def *f = NULL;
switch (fog_mode) {
case FOG_LINEAR:
/* f = (end - z) / (end - start)
*
* gl_MesaFogParamsOptimized gives us (-1 / (end - start)) and
* (end / (end - start)) so we can generate a single MAD.
*/
f = nir_fmad(b, fogc,
nir_channel(b, params, 0),
nir_channel(b, params, 1));
break;
case FOG_EXP:
/* f = e^(-(density * fogcoord))
*
* gl_MesaFogParamsOptimized gives us density/ln(2) so we can
* use EXP2 which is generally the native instruction without
* having to do any further math on the fog density uniform.
*/
f = nir_fmul(b, fogc, nir_channel(b, params, 2));
f = nir_fexp2(b, nir_fneg(b, f));
break;
case FOG_EXP2:
/* f = e^(-(density * fogcoord)^2)
*
* gl_MesaFogParamsOptimized gives us density/sqrt(ln(2)) so we
* can do this like FOG_EXP but with a squaring after the
* multiply by density.
*/
f = nir_fmul(b, fogc, nir_channel(b, params, 3));
f = nir_fmul(b, f, f);
f = nir_fexp2(b, nir_fneg(b, f));
break;
default:
unreachable("unsupported fog mode");
}
f = nir_fsat(b, f);
/* Not using flrp because we may end up lowering fog after driver lowering
* that meant to remove all lrps.
*/
return nir_fmad(b, color, f, nir_fmul(b, fog_color, nir_fsub_imm(b, 1.0, f)));
}
struct lower_fog_state {
enum gl_fog_mode fog_mode;
struct gl_program_parameter_list *paramList;
};
static bool
st_nir_lower_fog_instr(nir_builder *b, nir_instr *instr, void *_state)
{
const struct lower_fog_state *state = _state;
if (instr->type != nir_instr_type_intrinsic)
return false;
nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
if (intr->intrinsic != nir_intrinsic_store_output)
return false;
int loc = nir_intrinsic_io_semantics(intr).location;
if (loc != FRAG_RESULT_COLOR && loc != FRAG_RESULT_DATA0)
return false;
b->cursor = nir_before_instr(instr);
nir_def *color = intr->src[0].ssa;
color = nir_resize_vector(b, color, 4);
nir_def *fog = fog_result(b, color, state->fog_mode, state->paramList);
/* retain the non-fog-blended alpha value for color */
color = nir_vector_insert_imm(b, fog, nir_channel(b, color, 3), 3);
nir_src_rewrite(&intr->src[0],
nir_resize_vector(b, color, intr->num_components));
return true;
}
bool
st_nir_lower_fog(nir_shader *s, enum gl_fog_mode fog_mode, struct gl_program_parameter_list *paramList)
{
if (s->info.io_lowered) {
struct lower_fog_state state = {
.fog_mode = fog_mode,
.paramList = paramList,
};
nir_shader_instructions_pass(s, st_nir_lower_fog_instr,
nir_metadata_control_flow,
&state);
} else {
nir_variable *color_var = nir_find_variable_with_location(s, nir_var_shader_out, FRAG_RESULT_COLOR);
if (!color_var) {
color_var = nir_find_variable_with_location(s, nir_var_shader_out, FRAG_RESULT_DATA0);
/* Fog result would be undefined if we had no output color (in ARB_fragment_program) */
if (!color_var)
return false;
}
nir_function_impl *impl = nir_shader_get_entrypoint(s);
nir_builder b = nir_builder_at(nir_after_impl(impl));
/* Note: while ARB_fragment_program plus ARB_draw_buffers allows an array
* of result colors, prog_to_nir generates separate vars per slot so we
* don't have to handle that. Fog only applies to the first color result.
*/
assert(!glsl_type_is_array(color_var->type));
nir_def *color = nir_load_var(&b, color_var);
color = fog_result(&b, color, fog_mode, paramList);
nir_store_var(&b, color_var, color, 0x7);
nir_metadata_preserve(b.impl, nir_metadata_control_flow); }
return true;
}