mesa/src/intel/compiler/intel_nir_tcs_workarounds.c
Rhys Perry 463e3643f2 nir: add and use block predecessor helpers
Signed-off-by: Rhys Perry <pendingchaos02@gmail.com>
Acked-by: Alyssa Rosenzweig <alyssa.rosenzweig@intel.com>
Reviewed-by: Georg Lehmann <dadschoorse@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/40242>
2026-04-08 15:06:32 +00:00

117 lines
3.9 KiB
C

/*
* Copyright © 2016 Intel Corporation
* SPDX-License-Identifier: MIT
*/
#include "compiler/nir/nir_builder.h"
#include "intel_nir.h"
/**
* Implements the WaPreventHSTessLevelsInterference workaround (for Gfx7-8).
*
* From the Broadwell PRM, Volume 7 (3D-Media-GPGPU), Page 494 (below the
* definition of the patch header layouts):
*
* "HW Bug: The Tessellation stage will incorrectly add domain points
* along patch edges under the following conditions, which may result
* in conformance failures and/or cracking artifacts:
*
* * QUAD domain
* * INTEGER partitioning
* * All three TessFactors in a given U or V direction (e.g., V
* direction: UEQ0, InsideV, UEQ1) are all exactly 1.0
* * All three TessFactors in the other direction are > 1.0 and all
* round up to the same integer value (e.g, U direction:
* VEQ0 = 3.1, InsideU = 3.7, VEQ1 = 3.4)
*
* The suggested workaround (to be implemented as part of the postamble
* to the HS shader in the HS kernel) is:
*
* if (
* (TF[UEQ0] > 1.0) ||
* (TF[VEQ0] > 1.0) ||
* (TF[UEQ1] > 1.0) ||
* (TF[VEQ1] > 1.0) ||
* (TF[INSIDE_U] > 1.0) ||
* (TF[INSIDE_V] > 1.0) )
* {
* TF[INSIDE_U] = (TF[INSIDE_U] == 1.0) ? 2.0 : TF[INSIDE_U];
* TF[INSIDE_V] = (TF[INSIDE_V] == 1.0) ? 2.0 : TF[INSIDE_V];
* }"
*
* There's a subtlety here. Intel internal HSD-ES bug 1208668495 notes
* that the above workaround fails to fix certain GL/ES CTS tests which
* have inside tessellation factors of -1.0. This can be explained by
* a quote from the ARB_tessellation_shader specification:
*
* "If "equal_spacing" is used, the floating-point tessellation level is
* first clamped to the range [1,<max>], where <max> is implementation-
* dependent maximum tessellation level (MAX_TESS_GEN_LEVEL)."
*
* In other words, the actual inner tessellation factor used is
* clamp(TF[INSIDE_*], 1.0, 64.0). So we want to compare the clamped
* value against 1.0. To accomplish this, we change the comparison from
* (TF[INSIDE_*] == 1.0) to (TF[INSIDE_*] <= 1.0).
*/
static inline nir_def *
load_output(nir_builder *b, int num_components, int offset, int component)
{
return nir_load_output(b, num_components, 32, nir_imm_int(b, 0),
.base = offset,
.component = component);
}
static void
emit_quads_workaround(nir_builder *b, nir_block *block)
{
b->cursor = nir_after_block_before_jump(block);
nir_def *inner = load_output(b, 2, 0, 2);
nir_def *outer = load_output(b, 4, 1, 0);
nir_def *any_greater_than_1 =
nir_ior(b, nir_bany(b, nir_fgt_imm(b, outer, 1.0f)),
nir_bany(b, nir_fgt_imm(b, inner, 1.0f)));
nir_push_if(b, any_greater_than_1);
inner = nir_bcsel(b, nir_fle_imm(b, inner, 1.0f),
nir_imm_float(b, 2.0f), inner);
nir_store_output(b, inner, nir_imm_int(b, 0),
.src_type = nir_type_uint32,
.component = 2,
.write_mask = WRITEMASK_XY);
nir_pop_if(b, NULL);
}
void
intel_nir_apply_tcs_quads_workaround(nir_shader *nir)
{
assert(nir->info.stage == MESA_SHADER_TESS_CTRL);
nir_function_impl *impl = nir_shader_get_entrypoint(nir);
nir_builder b = nir_builder_create(impl);
/* emit_quads_workaround() inserts an if statement into each block,
* which splits it in two. This changes the set of predecessors of
* the end block. We want to process the original set, so to be safe,
* save it off to an array first.
*/
const unsigned num_end_preds = nir_block_num_preds(impl->end_block);
nir_block *end_preds[num_end_preds];
unsigned i = 0;
nir_foreach_pred(pred, impl->end_block) {
end_preds[i++] = pred;
}
for (i = 0; i < num_end_preds; i++) {
emit_quads_workaround(&b, end_preds[i]);
}
nir_progress(true, impl, nir_metadata_none);
}