From 73abbf1175d372e591f6b73d43434ba1824c2e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ol=C5=A1=C3=A1k?= Date: Sun, 31 Dec 2023 19:07:45 -0500 Subject: [PATCH] radeonsi: rewrite how small prim precision is passed to culling code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of passing 2 different 4-bit precision values via the SGPR, pass the quant mode enum + log_samples as 3 bits, and 2-bit log_samples separately. This saves 3 bits in the SGPR, which we'll need for culling states. This completely changes how the small prim precision is computed from the state bits. Reviewed-by: Timur Kristóf Part-of: --- .../drivers/radeonsi/si_nir_lower_abi.c | 32 ++++++---- src/gallium/drivers/radeonsi/si_pipe.h | 2 +- src/gallium/drivers/radeonsi/si_shader.h | 60 +++++++++++++++---- .../drivers/radeonsi/si_state_viewport.c | 34 ++--------- 4 files changed, 76 insertions(+), 52 deletions(-) diff --git a/src/gallium/drivers/radeonsi/si_nir_lower_abi.c b/src/gallium/drivers/radeonsi/si_nir_lower_abi.c index f8c687ff5e4..cb0cf01806d 100644 --- a/src/gallium/drivers/radeonsi/si_nir_lower_abi.c +++ b/src/gallium/drivers/radeonsi/si_nir_lower_abi.c @@ -262,6 +262,22 @@ static nir_def *get_num_vertices_per_prim(nir_builder *b, struct lower_abi_state return nir_iadd_imm(b, GET_FIELD_NIR(GS_STATE_OUTPRIM), 1); } +static nir_def *get_small_prim_precision(nir_builder *b, struct lower_abi_state *s, bool lines) +{ + /* Compute FP32 value "num_samples / quant_mode" using integer ops. + * See si_shader.h for how this works. + */ + struct si_shader_args *args = s->args; + nir_def *precision = GET_FIELD_NIR(GS_STATE_SMALL_PRIM_PRECISION); + nir_def *log_samples = GET_FIELD_NIR(GS_STATE_SMALL_PRIM_PRECISION_LOG_SAMPLES); + + if (lines) + precision = nir_iadd(b, precision, log_samples); + + /* The final FP32 value is: 1/2^(15 - precision) */ + return nir_ishl_imm(b, nir_ior_imm(b, precision, 0x70), 23); +} + static bool lower_intrinsic(nir_builder *b, nir_instr *instr, struct lower_abi_state *s) { nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr); @@ -409,20 +425,12 @@ static bool lower_intrinsic(nir_builder *b, nir_instr *instr, struct lower_abi_s case nir_intrinsic_load_cull_front_face_enabled_amd: replacement = nir_imm_bool(b, key->ge.opt.ngg_culling & SI_NGG_CULL_FRONT_FACE); break; - case nir_intrinsic_load_cull_small_triangle_precision_amd: { - nir_def *small_prim_precision = GET_FIELD_NIR(GS_STATE_SMALL_PRIM_PRECISION); - /* Extract the small prim precision. */ - small_prim_precision = nir_ior_imm(b, small_prim_precision, 0x70); - replacement = nir_ishl_imm(b, small_prim_precision, 23); + case nir_intrinsic_load_cull_small_triangle_precision_amd: + replacement = get_small_prim_precision(b, s, false); break; - } - case nir_intrinsic_load_cull_small_line_precision_amd: { - nir_def *small_prim_precision = GET_FIELD_NIR(GS_STATE_SMALL_PRIM_PRECISION_NO_AA); - /* Extract the small prim precision. */ - small_prim_precision = nir_ior_imm(b, small_prim_precision, 0x70); - replacement = nir_ishl_imm(b, small_prim_precision, 23); + case nir_intrinsic_load_cull_small_line_precision_amd: + replacement = get_small_prim_precision(b, s, true); break; - } case nir_intrinsic_load_cull_small_triangles_enabled_amd: /* Triangles always have small primitive culling enabled. */ replacement = nir_imm_bool(b, true); diff --git a/src/gallium/drivers/radeonsi/si_pipe.h b/src/gallium/drivers/radeonsi/si_pipe.h index 4b922aaeb50..978401ac519 100644 --- a/src/gallium/drivers/radeonsi/si_pipe.h +++ b/src/gallium/drivers/radeonsi/si_pipe.h @@ -749,7 +749,7 @@ struct si_framebuffer { enum si_quant_mode { - /* This is the list we want to support. */ + /* The small prim precision computation depends on the enum values to be like this. */ SI_QUANT_MODE_16_8_FIXED_POINT_1_256TH, SI_QUANT_MODE_14_10_FIXED_POINT_1_1024TH, SI_QUANT_MODE_12_12_FIXED_POINT_1_4096TH, diff --git a/src/gallium/drivers/radeonsi/si_shader.h b/src/gallium/drivers/radeonsi/si_shader.h index 1b0b99e5e23..64fc6b1da4b 100644 --- a/src/gallium/drivers/radeonsi/si_shader.h +++ b/src/gallium/drivers/radeonsi/si_shader.h @@ -244,17 +244,57 @@ enum */ #define GS_STATE_NUM_ES_OUTPUTS__SHIFT 13 #define GS_STATE_NUM_ES_OUTPUTS__MASK 0x3f -/* Small prim filter precision = num_samples / quant_mode, which can only be equal to 1/2^n - * where n is between 4 and 12. Knowing that, we only need to store 4 bits of the FP32 exponent. - * Set it like this: value = (fui(num_samples / quant_mode) >> 23) & 0xf; - * Expand to FP32 like this: ((0x70 | value) << 23); - * With 0x70 = 112, we get 2^(112 + value - 127) = 2^(value - 15), which is always a negative - * exponent and it's equal to 1/2^(15 - value). +/* Small prim filter precision = num_samples / quant_mode where num_samples is in {1, 2, 4, 8} and + * quant_mode is in {256, 1024, 4096}, which is equal to 1/2^n where n is between 5 and 12. + * + * Equation 1: Represent the value as 1/2^n. + * Assumption: log_samples <= 3 and log_quant_mode >= 8 + * num_samples / quant_mode = + * 2^log_samples / 2^log_quant_mode = + * 1 / 2^(log_quant_mode - log_samples) [because log_samples < log_quant_mode] + * + * Knowing that, we only need 4 bits to represent the FP32 exponent and thus the FP32 number. + * + * Equation 2: Encoding the exponent. + * 1/2^(15 - value) in FP32 = ((value | 0x70) << 23) in binary if value < 15 + * Proof: With 0x70 = 112, we get FP32 exponent 2^(112 + value - 127) according to the FP32 + * definition, which can be simplified to 2^(value - 15), which is a negative exponent + * for value < 15. Given that 2^-n = 1/2^n, the FP32 number is equal to 1/2^(15 - value). + * + * Equation 3: Convert quant_mode_enum to log_quant_mode. + * quant_mode_enum: + * 0 means 256 = 2^8 --> log2(256) = 8 + * 1 means 1024 = 2^10 --> log2(1024) = 10 + * 2 means 4096 = 2^12 --> log2(4096) = 12 + * + * Conversion to log_quant_mode: + * log_quant_mode = quant_mode_enum * 2 + 8. Proof: + * 0 * 2 + 8 = 8 + * 1 * 2 + 8 = 10 + * 2 * 2 + 8 = 12 + * + * Equation 4: Get the exponent value for Equation 2 from Equation 1. + * 15 - value = log_quant_mode - log_samples + * value = 15 - (log_quant_mode + log_samples) + * + * Combine equations 2, 3, and 4 to get the expression computing the FP32 number from log_samples + * and quant_mode_enum using integer ops: + * (value | 0x70) << 23 = + * ((15 - (log_quant_mode + log_samples)) | 0x70) << 23 = + * ((15 - (quant_mode_enum * 2 + 8 + log_samples)) | 0x70) << 23 = + * ((15 - quant_mode_enum * 2 - 8 - log_samples) | 0x70) << 23 = + * ((7 - quant_mode_enum * 2 - log_samples) | 0x70) << 23 = + * + * Since "log_samples <= 3" and "quant_mode_enum * 2 <= 4", we need a SGPR field that stores: + * triangle_precision = 7 - quant_mode_enum * 2 - log_samples + * + * Line precision ignores log_samples, so the shader should do: + * line_precision = triangle_precision + log_samples */ -#define GS_STATE_SMALL_PRIM_PRECISION_NO_AA__SHIFT 19 -#define GS_STATE_SMALL_PRIM_PRECISION_NO_AA__MASK 0xf -#define GS_STATE_SMALL_PRIM_PRECISION__SHIFT 23 -#define GS_STATE_SMALL_PRIM_PRECISION__MASK 0xf +#define GS_STATE_SMALL_PRIM_PRECISION__SHIFT 22 /* triangle_precision */ +#define GS_STATE_SMALL_PRIM_PRECISION__MASK 0x7 +#define GS_STATE_SMALL_PRIM_PRECISION_LOG_SAMPLES__SHIFT 25 +#define GS_STATE_SMALL_PRIM_PRECISION_LOG_SAMPLES__MASK 0x3 #define GS_STATE_STREAMOUT_QUERY_ENABLED__SHIFT 27 #define GS_STATE_STREAMOUT_QUERY_ENABLED__MASK 0x1 #define GS_STATE_PROVOKING_VTX_FIRST__SHIFT 28 diff --git a/src/gallium/drivers/radeonsi/si_state_viewport.c b/src/gallium/drivers/radeonsi/si_state_viewport.c index ba447961e89..1ce3960cb2f 100644 --- a/src/gallium/drivers/radeonsi/si_state_viewport.c +++ b/src/gallium/drivers/radeonsi/si_state_viewport.c @@ -94,7 +94,6 @@ static void si_emit_cull_state(struct si_context *sctx, unsigned index) sctx->last_small_prim_cull_info = info; } - /* This will end up in SGPR6 as (value << 8), shifted by the hw. */ radeon_add_to_buffer_list(sctx, &sctx->gfx_cs, sctx->small_prim_cull_info_buf, RADEON_USAGE_READ | RADEON_PRIO_CONST_BUFFER); @@ -117,35 +116,12 @@ static void si_emit_cull_state(struct si_context *sctx, unsigned index) * primitive culling. (more precision means a tighter bounding box * around primitives and more accurate elimination) */ - unsigned quant_mode = sctx->viewports.as_scissor[0].quant_mode; - float small_prim_precision_no_aa = 0; - unsigned num_samples = si_get_num_coverage_samples(sctx); + unsigned log_samples = util_logbase2(si_get_num_coverage_samples(sctx)); + unsigned precision = 7 - sctx->viewports.as_scissor[0].quant_mode * 2 - log_samples; + assert((precision & ~0x7) == 0); - if (quant_mode == SI_QUANT_MODE_12_12_FIXED_POINT_1_4096TH) - small_prim_precision_no_aa = 1.0 / 4096.0; - else if (quant_mode == SI_QUANT_MODE_14_10_FIXED_POINT_1_1024TH) - small_prim_precision_no_aa = 1.0 / 1024.0; - else - small_prim_precision_no_aa = 1.0 / 256.0; - - float small_prim_precision = num_samples * small_prim_precision_no_aa; - - /* Set VS_STATE.SMALL_PRIM_PRECISION for NGG culling. - * - * small_prim_precision is 1 / 2^n. We only need n between 5 (1/32) and 12 (1/4096). - * Such a floating point value can be packed into 4 bits as follows: - * If we pass the first 4 bits of the exponent to the shader and set the next 3 bits - * to 1, we'll get the number exactly because all other bits are always 0. See: - * 1 - * value = (0x70 | value.exponent[0:3]) << 23 = ------------------------------ - * 2 ^ (15 - value.exponent[0:3]) - * - * So pass only the first 4 bits of the float exponent to the shader. - */ - SET_FIELD(sctx->current_gs_state, GS_STATE_SMALL_PRIM_PRECISION_NO_AA, - (fui(small_prim_precision_no_aa) >> 23) & 0xf); - SET_FIELD(sctx->current_gs_state, GS_STATE_SMALL_PRIM_PRECISION, - (fui(small_prim_precision) >> 23) & 0xf); + SET_FIELD(sctx->current_gs_state, GS_STATE_SMALL_PRIM_PRECISION, precision); + SET_FIELD(sctx->current_gs_state, GS_STATE_SMALL_PRIM_PRECISION_LOG_SAMPLES, log_samples); } static void si_set_scissor_states(struct pipe_context *pctx, unsigned start_slot,